Bug 454376 add -lCrun -lCstd for Solaris OS_LIBS, r=bsmedberg
[wine-gecko.git] / layout / style / nsCSSParser.cpp
blob0fdcb978337c1e5962b39e4d8141c597ee55ac24
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * emk <VYV03354@nifty.ne.jp>
24 * Daniel Glazman <glazman@netscape.com>
25 * L. David Baron <dbaron@dbaron.org>
26 * Boris Zbarsky <bzbarsky@mit.edu>
27 * Mats Palmgren <mats.palmgren@bredband.net>
28 * Christian Biesinger <cbiesinger@web.de>
30 * Alternatively, the contents of this file may be used under the terms of
31 * either of the GNU General Public License Version 2 or later (the "GPL"),
32 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 * in which case the provisions of the GPL or the LGPL are applicable instead
34 * of those above. If you wish to allow use of your version of this file only
35 * under the terms of either the GPL or the LGPL, and not to allow others to
36 * use your version of this file under the terms of the MPL, indicate your
37 * decision by deleting the provisions above and replace them with the notice
38 * and other provisions required by the GPL or the LGPL. If you do not delete
39 * the provisions above, a recipient may use your version of this file under
40 * the terms of any one of the MPL, the GPL or the LGPL.
42 * ***** END LICENSE BLOCK ***** */
44 /* parsing of CSS stylesheets, based on a token stream from the CSS scanner */
46 #include "nsICSSParser.h"
47 #include "nsCSSProps.h"
48 #include "nsCSSKeywords.h"
49 #include "nsCSSScanner.h"
50 #include "nsICSSLoader.h"
51 #include "nsICSSStyleRule.h"
52 #include "nsICSSImportRule.h"
53 #include "nsCSSRules.h"
54 #include "nsICSSNameSpaceRule.h"
55 #include "nsIUnicharInputStream.h"
56 #include "nsICSSStyleSheet.h"
57 #include "nsCSSDeclaration.h"
58 #include "nsStyleConsts.h"
59 #include "nsIURL.h"
60 #include "nsNetUtil.h"
61 #include "nsCOMPtr.h"
62 #include "nsString.h"
63 #include "nsReadableUtils.h"
64 #include "nsUnicharUtils.h"
65 #include "nsIAtom.h"
66 #include "nsVoidArray.h"
67 #include "nsCOMArray.h"
68 #include "nsColor.h"
69 #include "nsStyleConsts.h"
70 #include "nsCSSPseudoClasses.h"
71 #include "nsCSSPseudoElements.h"
72 #include "nsCSSAnonBoxes.h"
73 #include "nsINameSpaceManager.h"
74 #include "nsXMLNameSpaceMap.h"
75 #include "nsThemeConstants.h"
76 #include "nsContentErrors.h"
77 #include "nsPrintfCString.h"
78 #include "nsIMediaList.h"
79 #include "nsILookAndFeel.h"
80 #include "nsStyleUtil.h"
81 #include "nsIPrincipal.h"
82 #include "prprf.h"
83 #include "math.h"
84 #include "nsContentUtils.h"
85 #include "nsDOMError.h"
86 #include "nsAutoPtr.h"
87 #include "nsTArray.h"
89 // Flags for ParseVariant method
90 #define VARIANT_KEYWORD 0x000001 // K
91 #define VARIANT_LENGTH 0x000002 // L
92 #define VARIANT_PERCENT 0x000004 // P
93 #define VARIANT_COLOR 0x000008 // C eCSSUnit_Color, eCSSUnit_String (e.g. "red")
94 #define VARIANT_URL 0x000010 // U
95 #define VARIANT_NUMBER 0x000020 // N
96 #define VARIANT_INTEGER 0x000040 // I
97 #define VARIANT_ANGLE 0x000080 // G
98 #define VARIANT_FREQUENCY 0x000100 // F
99 #define VARIANT_TIME 0x000200 // T
100 #define VARIANT_STRING 0x000400 // S
101 #define VARIANT_COUNTER 0x000800 //
102 #define VARIANT_ATTR 0x001000 //
103 #define VARIANT_IDENTIFIER 0x002000 // D
104 #define VARIANT_AUTO 0x010000 // A
105 #define VARIANT_INHERIT 0x020000 // H eCSSUnit_Initial, eCSSUnit_Inherit
106 #define VARIANT_NONE 0x040000 // O
107 #define VARIANT_NORMAL 0x080000 // M
108 #define VARIANT_SYSFONT 0x100000 // eCSSUnit_System_Font
110 // Common combinations of variants
111 #define VARIANT_AL (VARIANT_AUTO | VARIANT_LENGTH)
112 #define VARIANT_LP (VARIANT_LENGTH | VARIANT_PERCENT)
113 #define VARIANT_AH (VARIANT_AUTO | VARIANT_INHERIT)
114 #define VARIANT_AHLP (VARIANT_AH | VARIANT_LP)
115 #define VARIANT_AHI (VARIANT_AH | VARIANT_INTEGER)
116 #define VARIANT_AHK (VARIANT_AH | VARIANT_KEYWORD)
117 #define VARIANT_AHKLP (VARIANT_AHLP | VARIANT_KEYWORD)
118 #define VARIANT_AUK (VARIANT_AUTO | VARIANT_URL | VARIANT_KEYWORD)
119 #define VARIANT_AHUK (VARIANT_AH | VARIANT_URL | VARIANT_KEYWORD)
120 #define VARIANT_AHL (VARIANT_AH | VARIANT_LENGTH)
121 #define VARIANT_AHKL (VARIANT_AHK | VARIANT_LENGTH)
122 #define VARIANT_HK (VARIANT_INHERIT | VARIANT_KEYWORD)
123 #define VARIANT_HKF (VARIANT_HK | VARIANT_FREQUENCY)
124 #define VARIANT_HKL (VARIANT_HK | VARIANT_LENGTH)
125 #define VARIANT_HKLP (VARIANT_HK | VARIANT_LP)
126 #define VARIANT_HKLPO (VARIANT_HKLP | VARIANT_NONE)
127 #define VARIANT_HL (VARIANT_INHERIT | VARIANT_LENGTH)
128 #define VARIANT_HI (VARIANT_INHERIT | VARIANT_INTEGER)
129 #define VARIANT_HLP (VARIANT_HL | VARIANT_PERCENT)
130 #define VARIANT_HLPN (VARIANT_HLP | VARIANT_NUMBER)
131 #define VARIANT_HLPO (VARIANT_HLP | VARIANT_NONE)
132 #define VARIANT_HTP (VARIANT_INHERIT | VARIANT_TIME | VARIANT_PERCENT)
133 #define VARIANT_HMK (VARIANT_HK | VARIANT_NORMAL)
134 #define VARIANT_HMKI (VARIANT_HMK | VARIANT_INTEGER)
135 #define VARIANT_HC (VARIANT_INHERIT | VARIANT_COLOR)
136 #define VARIANT_HCK (VARIANT_HK | VARIANT_COLOR)
137 #define VARIANT_HUO (VARIANT_INHERIT | VARIANT_URL | VARIANT_NONE)
138 #define VARIANT_AHUO (VARIANT_AUTO | VARIANT_HUO)
139 #define VARIANT_HPN (VARIANT_INHERIT | VARIANT_PERCENT | VARIANT_NUMBER)
140 #define VARIANT_HOK (VARIANT_HK | VARIANT_NONE)
141 #define VARIANT_HN (VARIANT_INHERIT | VARIANT_NUMBER)
142 #define VARIANT_HON (VARIANT_HN | VARIANT_NONE)
143 #define VARIANT_HOS (VARIANT_INHERIT | VARIANT_NONE | VARIANT_STRING)
145 //----------------------------------------------------------------------
147 // Your basic top-down recursive descent style parser
148 class CSSParserImpl : public nsICSSParser {
149 public:
150 CSSParserImpl();
151 virtual ~CSSParserImpl();
153 NS_DECL_ISUPPORTS
155 NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet);
157 NS_IMETHOD SetCaseSensitive(PRBool aCaseSensitive);
159 NS_IMETHOD SetQuirkMode(PRBool aQuirkMode);
161 #ifdef MOZ_SVG
162 NS_IMETHOD SetSVGMode(PRBool aSVGMode);
163 #endif
165 NS_IMETHOD SetChildLoader(nsICSSLoader* aChildLoader);
167 NS_IMETHOD Parse(nsIUnicharInputStream* aInput,
168 nsIURI* aSheetURI,
169 nsIURI* aBaseURI,
170 nsIPrincipal* aSheetPrincipal,
171 PRUint32 aLineNumber,
172 PRBool aAllowUnsafeRules);
174 NS_IMETHOD ParseStyleAttribute(const nsAString& aAttributeValue,
175 nsIURI* aDocURL,
176 nsIURI* aBaseURL,
177 nsIPrincipal* aNodePrincipal,
178 nsICSSStyleRule** aResult);
180 NS_IMETHOD ParseAndAppendDeclaration(const nsAString& aBuffer,
181 nsIURI* aSheetURL,
182 nsIURI* aBaseURL,
183 nsIPrincipal* aSheetPrincipal,
184 nsCSSDeclaration* aDeclaration,
185 PRBool aParseOnlyOneDecl,
186 PRBool* aChanged,
187 PRBool aClearOldDecl);
189 NS_IMETHOD ParseRule(const nsAString& aRule,
190 nsIURI* aSheetURL,
191 nsIURI* aBaseURL,
192 nsIPrincipal* aSheetPrincipal,
193 nsCOMArray<nsICSSRule>& aResult);
195 NS_IMETHOD ParseProperty(const nsCSSProperty aPropID,
196 const nsAString& aPropValue,
197 nsIURI* aSheetURL,
198 nsIURI* aBaseURL,
199 nsIPrincipal* aSheetPrincipal,
200 nsCSSDeclaration* aDeclaration,
201 PRBool* aChanged);
203 NS_IMETHOD ParseMediaList(const nsSubstring& aBuffer,
204 nsIURI* aURL, // for error reporting
205 PRUint32 aLineNumber, // for error reporting
206 nsMediaList* aMediaList,
207 PRBool aHTMLMode);
209 NS_IMETHOD ParseColorString(const nsSubstring& aBuffer,
210 nsIURI* aURL, // for error reporting
211 PRUint32 aLineNumber, // for error reporting
212 nscolor* aColor);
214 NS_IMETHOD ParseSelectorString(const nsSubstring& aSelectorString,
215 nsIURI* aURL, // for error reporting
216 PRUint32 aLineNumber, // for error reporting
217 nsCSSSelectorList **aSelectorList);
219 void AppendRule(nsICSSRule* aRule);
221 protected:
222 class nsAutoParseCompoundProperty;
223 friend class nsAutoParseCompoundProperty;
226 * This helper class automatically calls SetParsingCompoundProperty in its
227 * constructor and takes care of resetting it to false in its destructor.
229 class nsAutoParseCompoundProperty {
230 public:
231 nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
233 NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
234 "already parsing compound property");
235 NS_ASSERTION(aParser, "Null parser?");
236 aParser->SetParsingCompoundProperty(PR_TRUE);
239 ~nsAutoParseCompoundProperty()
241 mParser->SetParsingCompoundProperty(PR_FALSE);
243 private:
244 CSSParserImpl* mParser;
247 void InitScanner(nsIUnicharInputStream* aInput, nsIURI* aSheetURI,
248 PRUint32 aLineNumber, nsIURI* aBaseURI,
249 nsIPrincipal* aSheetPrincipal);
250 // the caller must hold on to aBuffer until parsing is done
251 void InitScanner(const nsSubstring& aString, nsIURI* aSheetURI,
252 PRUint32 aLineNumber, nsIURI* aBaseURI,
253 nsIPrincipal* aSheetPrincipal);
254 void ReleaseScanner(void);
255 #ifdef MOZ_SVG
256 PRBool IsSVGMode() const {
257 return mScanner.IsSVGMode();
259 #endif
261 PRBool GetToken(PRBool aSkipWS);
262 PRBool GetURLToken();
263 void UngetToken();
265 void AssertInitialState() {
266 NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state");
267 NS_PRECONDITION(!mUnresolvablePrefixException, "Bad initial state");
268 NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
271 PRBool ExpectSymbol(PRUnichar aSymbol, PRBool aSkipWS);
272 PRBool ExpectEndProperty();
273 PRBool CheckEndProperty();
274 nsSubstring* NextIdent();
275 void SkipUntil(PRUnichar aStopSymbol);
276 void SkipRuleSet();
277 PRBool SkipAtRule();
278 PRBool SkipDeclaration(PRBool aCheckForBraces);
279 PRBool GetNonCloseParenToken(PRBool aSkipWS);
281 PRBool PushGroup(nsICSSGroupRule* aRule);
282 void PopGroup(void);
284 PRBool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData);
285 PRBool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData);
286 PRBool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData);
287 PRBool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData);
288 PRBool GatherURL(nsString& aURL);
289 // Callers must clear or throw out aMedia if GatherMedia returns false.
290 PRBool GatherMedia(nsMediaList* aMedia,
291 PRUnichar aStopSymbol);
292 PRBool ParseMediaQueryExpression(nsMediaQuery* aQuery);
293 PRBool ProcessImport(const nsString& aURLSpec,
294 nsMediaList* aMedia,
295 RuleAppendFunc aAppendFunc,
296 void* aProcessData);
297 PRBool ParseGroupRule(nsICSSGroupRule* aRule, RuleAppendFunc aAppendFunc,
298 void* aProcessData);
299 PRBool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData);
300 PRBool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData);
301 PRBool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
302 PRBool ProcessNameSpace(const nsString& aPrefix,
303 const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
304 void* aProcessData);
306 PRBool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
307 PRBool ParseFontDescriptor(nsCSSFontFaceRule* aRule);
308 PRBool ParseFontDescriptorValue(nsCSSFontDesc aDescID,
309 nsCSSValue& aValue);
311 PRBool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData);
313 enum nsSelectorParsingStatus {
314 // we have parsed a selector and we saw a token that cannot be
315 // part of a selector:
316 eSelectorParsingStatus_Done,
317 // we should continue parsing the selector:
318 eSelectorParsingStatus_Continue,
319 // same as "Done" but we did not find a selector:
320 eSelectorParsingStatus_Empty,
321 // we saw an unexpected token or token value,
322 // or we saw end-of-file with an unfinished selector:
323 eSelectorParsingStatus_Error
325 nsSelectorParsingStatus ParseIDSelector(PRInt32& aDataMask,
326 nsCSSSelector& aSelector);
328 nsSelectorParsingStatus ParseClassSelector(PRInt32& aDataMask,
329 nsCSSSelector& aSelector);
331 nsSelectorParsingStatus ParsePseudoSelector(PRInt32& aDataMask,
332 nsCSSSelector& aSelector,
333 PRBool aIsNegated);
335 nsSelectorParsingStatus ParseAttributeSelector(PRInt32& aDataMask,
336 nsCSSSelector& aSelector);
338 nsSelectorParsingStatus ParseTypeOrUniversalSelector(PRInt32& aDataMask,
339 nsCSSSelector& aSelector,
340 PRBool aIsNegated);
342 nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
343 nsIAtom* aPseudo);
345 nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
346 nsIAtom* aPseudo);
348 nsSelectorParsingStatus ParseNegatedSimpleSelector(PRInt32& aDataMask,
349 nsCSSSelector& aSelector);
351 nsSelectorParsingStatus ParseSelector(nsCSSSelector& aSelectorResult);
353 // If aTerminateAtBrace is true, the selector list is done when we
354 // hit a '{'. Otherwise, it's done when we hit EOF.
355 PRBool ParseSelectorList(nsCSSSelectorList*& aListHead,
356 PRBool aTerminateAtBrace);
357 PRBool ParseSelectorGroup(nsCSSSelectorList*& aListHead);
358 nsCSSDeclaration* ParseDeclarationBlock(PRBool aCheckForBraces);
359 PRBool ParseDeclaration(nsCSSDeclaration* aDeclaration,
360 PRBool aCheckForBraces,
361 PRBool aMustCallValueAppended,
362 PRBool* aChanged);
363 // After a parse error parsing |aPropID|, clear the data in
364 // |mTempData|.
365 void ClearTempData(nsCSSProperty aPropID);
366 // After a successful parse of |aPropID|, transfer data from
367 // |mTempData| to |mData|. Set |*aChanged| to true if something
368 // changed, but leave it unmodified otherwise. If aMustCallValueAppended
369 // is false, will not call ValueAppended on aDeclaration if the property
370 // is already set in it.
371 void TransferTempData(nsCSSDeclaration* aDeclaration,
372 nsCSSProperty aPropID, PRBool aIsImportant,
373 PRBool aMustCallValueAppended,
374 PRBool* aChanged);
375 void DoTransferTempData(nsCSSDeclaration* aDeclaration,
376 nsCSSProperty aPropID, PRBool aIsImportant,
377 PRBool aMustCallValueAppended,
378 PRBool* aChanged);
379 PRBool ParseProperty(nsCSSProperty aPropID);
380 PRBool ParseSingleValueProperty(nsCSSValue& aValue,
381 nsCSSProperty aPropID);
383 #ifdef MOZ_XUL
384 PRBool ParseTreePseudoElement(nsCSSSelector& aSelector);
385 #endif
387 void InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties);
389 // Property specific parsing routines
390 PRBool ParseAzimuth(nsCSSValue& aValue);
391 PRBool ParseBackground();
392 PRBool ParseBackgroundPosition();
393 PRBool ParseBackgroundPositionValues();
394 PRBool ParseBoxPosition(nsCSSValuePair& aOut);
395 PRBool ParseBoxPositionValues(nsCSSValuePair& aOut);
396 PRBool ParseBorderColor();
397 PRBool ParseBorderColors(nsCSSValueList** aResult,
398 nsCSSProperty aProperty);
399 PRBool ParseBorderImage();
400 PRBool ParseBorderSpacing();
401 PRBool ParseBorderSide(const nsCSSProperty aPropIDs[],
402 PRBool aSetAllSides);
403 PRBool ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
404 PRInt32 aSourceType);
405 PRBool ParseBorderStyle();
406 PRBool ParseBorderWidth();
407 PRBool ParseBorderRadius();
408 PRBool ParseOutlineRadius();
409 // for 'clip' and '-moz-image-region'
410 PRBool ParseRect(nsCSSRect& aRect,
411 nsCSSProperty aPropID);
412 PRBool DoParseRect(nsCSSRect& aRect);
413 PRBool ParseContent();
414 PRBool ParseCounterData(nsCSSValuePairList** aResult,
415 nsCSSProperty aPropID);
416 PRBool ParseCue();
417 PRBool ParseCursor();
418 PRBool ParseFont();
419 PRBool ParseFontWeight(nsCSSValue& aValue);
420 PRBool ParseOneFamily(nsAString& aValue);
421 PRBool ParseFamily(nsCSSValue& aValue);
422 PRBool ParseFontSrc(nsCSSValue& aValue);
423 PRBool ParseFontSrcFormat(nsTArray<nsCSSValue>& values);
424 PRBool ParseFontRanges(nsCSSValue& aValue);
425 PRBool ParseListStyle();
426 PRBool ParseMargin();
427 PRBool ParseMarks(nsCSSValue& aValue);
428 PRBool ParseMozTransform();
429 PRBool ParseOutline();
430 PRBool ParseOverflow();
431 PRBool ParsePadding();
432 PRBool ParsePause();
433 PRBool ParseQuotes();
434 PRBool ParseSize();
435 PRBool ParseTextDecoration(nsCSSValue& aValue);
437 nsCSSValueList* ParseCSSShadowList(PRBool aUsesSpread);
438 PRBool ParseTextShadow();
439 PRBool ParseBoxShadow();
441 #ifdef MOZ_SVG
442 PRBool ParsePaint(nsCSSValuePair* aResult,
443 nsCSSProperty aPropID);
444 PRBool ParseDasharray();
445 PRBool ParseMarker();
446 #endif
448 // Reused utility parsing routines
449 void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue);
450 PRBool ParseBoxProperties(nsCSSRect& aResult,
451 const nsCSSProperty aPropIDs[]);
452 PRBool ParseDirectionalBoxProperty(nsCSSProperty aProperty,
453 PRInt32 aSourceType);
454 PRInt32 ParseChoice(nsCSSValue aValues[],
455 const nsCSSProperty aPropIDs[], PRInt32 aNumIDs);
456 PRBool ParseColor(nsCSSValue& aValue);
457 PRBool ParseColorComponent(PRUint8& aComponent,
458 PRInt32& aType, char aStop);
459 // ParseHSLColor parses everything starting with the opening '('
460 // up through and including the aStop char.
461 PRBool ParseHSLColor(nscolor& aColor, char aStop);
462 // ParseColorOpacity will enforce that the color ends with a ')'
463 // after the opacity
464 PRBool ParseColorOpacity(PRUint8& aOpacity);
465 PRBool ParseEnum(nsCSSValue& aValue, const PRInt32 aKeywordTable[]);
466 PRBool ParseVariant(nsCSSValue& aValue,
467 PRInt32 aVariantMask,
468 const PRInt32 aKeywordTable[]);
469 PRBool ParsePositiveVariant(nsCSSValue& aValue,
470 PRInt32 aVariantMask,
471 const PRInt32 aKeywordTable[]);
472 PRBool ParseCounter(nsCSSValue& aValue);
473 PRBool ParseAttr(nsCSSValue& aValue);
474 PRBool ParseURL(nsCSSValue& aValue);
475 PRBool TranslateDimension(nsCSSValue& aValue, PRInt32 aVariantMask,
476 float aNumber, const nsString& aUnit);
478 void SetParsingCompoundProperty(PRBool aBool) {
479 NS_ASSERTION(aBool == PR_TRUE || aBool == PR_FALSE, "bad PRBool value");
480 mParsingCompoundProperty = aBool;
482 PRBool IsParsingCompoundProperty(void) const {
483 return mParsingCompoundProperty;
486 /* Functions for -moz-transform Parsing */
487 PRBool ReadSingleTransform(nsCSSValueList**& aTail);
488 PRBool ParseFunction(const nsString &aFunction, const PRInt32 aAllowedTypes[],
489 PRUint16 aMinElems, PRUint16 aMaxElems,
490 nsCSSValue &aValue);
491 PRBool ParseFunctionInternals(const PRInt32 aVariantMask[],
492 PRUint16 aMinElems,
493 PRUint16 aMaxElems,
494 nsTArray<nsCSSValue>& aOutput);
496 /* Functions for -moz-transform-origin Parsing */
497 PRBool ParseMozTransformOrigin();
500 /* Find and return the correct namespace ID for the prefix aPrefix.
501 If the prefix cannot be resolved to a namespace, this method will
502 return false. Otherwise it will return true. When returning
503 false, it may set the low-level error code, depending on the
504 value of mUnresolvablePrefixException.
506 This method never returns kNameSpaceID_Unknown or
507 kNameSpaceID_None for aNameSpaceID while returning true.
509 PRBool GetNamespaceIdForPrefix(const nsString& aPrefix,
510 PRInt32* aNameSpaceID);
512 /* Find the correct default namespace, and set it on aSelector. */
513 void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
515 // Current token. The value is valid after calling GetToken and invalidated
516 // by UngetToken.
517 nsCSSToken mToken;
519 // Our scanner.
520 nsCSSScanner mScanner;
522 // The URI to be used as a base for relative URIs.
523 nsCOMPtr<nsIURI> mBaseURL;
525 // The URI to be used as an HTTP "Referer" and for error reporting.
526 nsCOMPtr<nsIURI> mSheetURL;
528 // The principal of the sheet involved
529 nsCOMPtr<nsIPrincipal> mSheetPrincipal;
531 // The sheet we're parsing into
532 nsCOMPtr<nsICSSStyleSheet> mSheet;
534 // Used for @import rules
535 nsICSSLoader* mChildLoader; // not ref counted, it owns us
537 // Sheet section we're in. This is used to enforce correct ordering of the
538 // various rule types (eg the fact that a @charset rule must come before
539 // anything else). Note that there are checks of similar things in various
540 // places in nsCSSStyleSheet.cpp (e.g in insertRule, RebuildChildList).
541 enum nsCSSSection {
542 eCSSSection_Charset,
543 eCSSSection_Import,
544 eCSSSection_NameSpace,
545 eCSSSection_General
547 nsCSSSection mSection;
549 nsXMLNameSpaceMap *mNameSpaceMap; // weak, mSheet owns it
551 // After an UngetToken is done this flag is true. The next call to
552 // GetToken clears the flag.
553 PRPackedBool mHavePushBack : 1;
555 // True if we are in quirks mode; false in standards or almost standards mode
556 PRPackedBool mNavQuirkMode : 1;
558 // True if unsafe rules should be allowed
559 PRPackedBool mUnsafeRulesEnabled : 1;
561 // True for parsing media lists for HTML attributes, where we have to
562 // ignore CSS comments.
563 PRPackedBool mHTMLMediaMode : 1;
565 // True if tagnames and attributes are case-sensitive
566 PRPackedBool mCaseSensitive : 1;
568 // This flag is set when parsing a non-box shorthand; it's used to not apply
569 // some quirks during shorthand parsing
570 PRPackedBool mParsingCompoundProperty : 1;
572 // If this flag is true, failure to resolve a namespace prefix
573 // should set the low-level error to NS_ERROR_DOM_NAMESPACE_ERR
574 PRPackedBool mUnresolvablePrefixException : 1;
576 // Stack of rule groups; used for @media and such.
577 nsCOMArray<nsICSSGroupRule> mGroupStack;
579 // During the parsing of a property (which may be a shorthand), the data
580 // are stored in |mTempData|. (It is needed to ensure that parser
581 // errors cause the data to be ignored, and to ensure that a
582 // non-'!important' declaration does not override an '!important'
583 // one.)
584 nsCSSExpandedDataBlock mTempData;
586 // All data from successfully parsed properties are placed into |mData|.
587 nsCSSExpandedDataBlock mData;
589 #ifdef DEBUG
590 PRPackedBool mScannerInited;
591 #endif
594 PR_STATIC_CALLBACK(void) AppendRuleToArray(nsICSSRule* aRule, void* aArray)
596 static_cast<nsCOMArray<nsICSSRule>*>(aArray)->AppendObject(aRule);
599 PR_STATIC_CALLBACK(void) AppendRuleToSheet(nsICSSRule* aRule, void* aParser)
601 CSSParserImpl* parser = (CSSParserImpl*) aParser;
602 parser->AppendRule(aRule);
605 nsresult
606 NS_NewCSSParser(nsICSSParser** aInstancePtrResult)
608 CSSParserImpl *it = new CSSParserImpl();
610 if (it == nsnull) {
611 return NS_ERROR_OUT_OF_MEMORY;
614 return it->QueryInterface(NS_GET_IID(nsICSSParser), (void **) aInstancePtrResult);
617 #ifdef CSS_REPORT_PARSE_ERRORS
619 #define REPORT_UNEXPECTED(msg_) \
620 mScanner.ReportUnexpected(#msg_)
622 #define REPORT_UNEXPECTED_P(msg_, params_) \
623 mScanner.ReportUnexpectedParams(#msg_, params_, NS_ARRAY_LENGTH(params_))
625 #define REPORT_UNEXPECTED_EOF(lf_) \
626 mScanner.ReportUnexpectedEOF(#lf_)
628 #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
629 mScanner.ReportUnexpectedEOF(ch_)
631 #define REPORT_UNEXPECTED_TOKEN(msg_) \
632 mScanner.ReportUnexpectedToken(mToken, #msg_)
634 #define REPORT_UNEXPECTED_TOKEN_P(msg_, params_) \
635 mScanner.ReportUnexpectedTokenParams(mToken, #msg_, \
636 params_, NS_ARRAY_LENGTH(params_))
639 #define OUTPUT_ERROR() \
640 mScanner.OutputError()
642 #define CLEAR_ERROR() \
643 mScanner.ClearError()
645 #else
647 #define REPORT_UNEXPECTED(msg_)
648 #define REPORT_UNEXPECTED_P(msg_, params_)
649 #define REPORT_UNEXPECTED_EOF(lf_)
650 #define REPORT_UNEXPECTED_EOF_CHAR(ch_)
651 #define REPORT_UNEXPECTED_TOKEN(msg_)
652 #define REPORT_UNEXPECTED_TOKEN_P(msg_, params_)
653 #define OUTPUT_ERROR()
654 #define CLEAR_ERROR()
656 #endif
658 CSSParserImpl::CSSParserImpl()
659 : mToken(),
660 mScanner(),
661 mChildLoader(nsnull),
662 mSection(eCSSSection_Charset),
663 mNameSpaceMap(nsnull),
664 mHavePushBack(PR_FALSE),
665 mNavQuirkMode(PR_FALSE),
666 mUnsafeRulesEnabled(PR_FALSE),
667 mHTMLMediaMode(PR_FALSE),
668 mCaseSensitive(PR_FALSE),
669 mParsingCompoundProperty(PR_FALSE),
670 mUnresolvablePrefixException(PR_FALSE)
671 #ifdef DEBUG
672 , mScannerInited(PR_FALSE)
673 #endif
677 NS_IMPL_ISUPPORTS1(CSSParserImpl, nsICSSParser)
679 CSSParserImpl::~CSSParserImpl()
681 mData.AssertInitialState();
682 mTempData.AssertInitialState();
685 NS_IMETHODIMP
686 CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet)
688 if (aSheet != mSheet) {
689 // Switch to using the new sheet, if any
690 mGroupStack.Clear();
691 mSheet = aSheet;
692 if (mSheet) {
693 mNameSpaceMap = mSheet->GetNameSpaceMap();
694 } else {
695 mNameSpaceMap = nsnull;
699 return NS_OK;
702 NS_IMETHODIMP
703 CSSParserImpl::SetCaseSensitive(PRBool aCaseSensitive)
705 NS_ASSERTION(aCaseSensitive == PR_TRUE || aCaseSensitive == PR_FALSE, "bad PRBool value");
706 mCaseSensitive = aCaseSensitive;
707 return NS_OK;
710 NS_IMETHODIMP
711 CSSParserImpl::SetQuirkMode(PRBool aQuirkMode)
713 NS_ASSERTION(aQuirkMode == PR_TRUE || aQuirkMode == PR_FALSE, "bad PRBool value");
714 mNavQuirkMode = aQuirkMode;
715 return NS_OK;
718 #ifdef MOZ_SVG
719 NS_IMETHODIMP
720 CSSParserImpl::SetSVGMode(PRBool aSVGMode)
722 NS_ASSERTION(aSVGMode == PR_TRUE || aSVGMode == PR_FALSE,
723 "bad PRBool value");
724 mScanner.SetSVGMode(aSVGMode);
725 return NS_OK;
727 #endif
729 NS_IMETHODIMP
730 CSSParserImpl::SetChildLoader(nsICSSLoader* aChildLoader)
732 mChildLoader = aChildLoader; // not ref counted, it owns us
733 return NS_OK;
736 void
737 CSSParserImpl::InitScanner(nsIUnicharInputStream* aInput, nsIURI* aSheetURI,
738 PRUint32 aLineNumber, nsIURI* aBaseURI,
739 nsIPrincipal* aSheetPrincipal)
741 NS_ASSERTION(! mScannerInited, "already have scanner");
743 mScanner.Init(aInput, nsnull, 0, aSheetURI, aLineNumber);
744 #ifdef DEBUG
745 mScannerInited = PR_TRUE;
746 #endif
747 mBaseURL = aBaseURI;
748 mSheetURL = aSheetURI;
749 mSheetPrincipal = aSheetPrincipal;
751 mHavePushBack = PR_FALSE;
754 void
755 CSSParserImpl::InitScanner(const nsSubstring& aString, nsIURI* aSheetURI,
756 PRUint32 aLineNumber, nsIURI* aBaseURI,
757 nsIPrincipal* aSheetPrincipal)
759 // Having it not own the string is OK since the caller will hold on to
760 // the stream until we're done parsing.
761 NS_ASSERTION(! mScannerInited, "already have scanner");
763 mScanner.Init(nsnull, aString.BeginReading(), aString.Length(), aSheetURI, aLineNumber);
765 #ifdef DEBUG
766 mScannerInited = PR_TRUE;
767 #endif
768 mBaseURL = aBaseURI;
769 mSheetURL = aSheetURI;
770 mSheetPrincipal = aSheetPrincipal;
772 mHavePushBack = PR_FALSE;
775 void
776 CSSParserImpl::ReleaseScanner(void)
778 mScanner.Close();
779 #ifdef DEBUG
780 mScannerInited = PR_FALSE;
781 #endif
782 mBaseURL = nsnull;
783 mSheetURL = nsnull;
784 mSheetPrincipal = nsnull;
788 NS_IMETHODIMP
789 CSSParserImpl::Parse(nsIUnicharInputStream* aInput,
790 nsIURI* aSheetURI,
791 nsIURI* aBaseURI,
792 nsIPrincipal* aSheetPrincipal,
793 PRUint32 aLineNumber,
794 PRBool aAllowUnsafeRules)
796 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
798 NS_ASSERTION(nsnull != aBaseURI, "need base URL");
799 NS_ASSERTION(nsnull != aSheetURI, "need sheet URL");
800 AssertInitialState();
802 NS_PRECONDITION(mSheet, "Must have sheet to parse into");
803 NS_ENSURE_STATE(mSheet);
805 #ifdef DEBUG
806 nsCOMPtr<nsIURI> uri;
807 mSheet->GetSheetURI(getter_AddRefs(uri));
808 PRBool equal;
809 NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
810 "Sheet URI does not match passed URI");
811 NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
812 &equal)) &&
813 equal,
814 "Sheet principal does not match passed principal");
815 #endif
817 InitScanner(aInput, aSheetURI, aLineNumber, aBaseURI, aSheetPrincipal);
819 PRInt32 ruleCount = 0;
820 mSheet->StyleRuleCount(ruleCount);
821 if (0 < ruleCount) {
822 nsICSSRule* lastRule = nsnull;
823 mSheet->GetStyleRuleAt(ruleCount - 1, lastRule);
824 if (lastRule) {
825 PRInt32 type;
826 lastRule->GetType(type);
827 switch (type) {
828 case nsICSSRule::CHARSET_RULE:
829 case nsICSSRule::IMPORT_RULE:
830 mSection = eCSSSection_Import;
831 break;
832 case nsICSSRule::NAMESPACE_RULE:
833 mSection = eCSSSection_NameSpace;
834 break;
835 default:
836 mSection = eCSSSection_General;
837 break;
839 NS_RELEASE(lastRule);
842 else {
843 mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
846 mUnsafeRulesEnabled = aAllowUnsafeRules;
848 nsCSSToken* tk = &mToken;
849 for (;;) {
850 // Get next non-whitespace token
851 if (!GetToken(PR_TRUE)) {
852 OUTPUT_ERROR();
853 break;
855 if (eCSSToken_HTMLComment == tk->mType) {
856 continue; // legal here only
858 if (eCSSToken_AtKeyword == tk->mType) {
859 ParseAtRule(AppendRuleToSheet, this);
860 continue;
862 UngetToken();
863 if (ParseRuleSet(AppendRuleToSheet, this)) {
864 mSection = eCSSSection_General;
867 ReleaseScanner();
869 mUnsafeRulesEnabled = PR_FALSE;
871 // XXX check for low level errors
872 return NS_OK;
876 * Determines whether the identifier contained in the given string is a
877 * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1.
879 static PRBool
880 NonMozillaVendorIdentifier(const nsAString& ident)
882 return (ident.First() == PRUnichar('-') &&
883 !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) ||
884 ident.First() == PRUnichar('_');
888 NS_IMETHODIMP
889 CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
890 nsIURI* aDocURL,
891 nsIURI* aBaseURL,
892 nsIPrincipal* aNodePrincipal,
893 nsICSSStyleRule** aResult)
895 NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
896 AssertInitialState();
898 NS_ASSERTION(nsnull != aBaseURL, "need base URL");
900 // XXX line number?
901 InitScanner(aAttributeValue, aDocURL, 0, aBaseURL, aNodePrincipal);
903 mSection = eCSSSection_General;
905 // In quirks mode, allow style declarations to have braces or not
906 // (bug 99554).
907 PRBool haveBraces;
908 if (mNavQuirkMode && GetToken(PR_TRUE)) {
909 haveBraces = eCSSToken_Symbol == mToken.mType &&
910 '{' == mToken.mSymbol;
911 UngetToken();
913 else {
914 haveBraces = PR_FALSE;
917 nsCSSDeclaration* declaration = ParseDeclarationBlock(haveBraces);
918 if (declaration) {
919 // Create a style rule for the declaration
920 nsICSSStyleRule* rule = nsnull;
921 nsresult rv = NS_NewCSSStyleRule(&rule, nsnull, declaration);
922 if (NS_FAILED(rv)) {
923 declaration->RuleAbort();
924 ReleaseScanner();
925 return rv;
927 *aResult = rule;
929 else {
930 *aResult = nsnull;
933 ReleaseScanner();
935 // XXX check for low level errors
936 return NS_OK;
939 NS_IMETHODIMP
940 CSSParserImpl::ParseAndAppendDeclaration(const nsAString& aBuffer,
941 nsIURI* aSheetURL,
942 nsIURI* aBaseURL,
943 nsIPrincipal* aSheetPrincipal,
944 nsCSSDeclaration* aDeclaration,
945 PRBool aParseOnlyOneDecl,
946 PRBool* aChanged,
947 PRBool aClearOldDecl)
949 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
950 AssertInitialState();
952 *aChanged = PR_FALSE;
954 InitScanner(aBuffer, aSheetURL, 0, aBaseURL, aSheetPrincipal);
956 mSection = eCSSSection_General;
958 if (aClearOldDecl) {
959 mData.AssertInitialState();
960 aDeclaration->ClearData();
961 // We could check if it was already empty, but...
962 *aChanged = PR_TRUE;
963 } else {
964 aDeclaration->ExpandTo(&mData);
967 nsresult rv = NS_OK;
968 do {
969 // If we cleared the old decl, then we want to be calling
970 // ValueAppended as we parse.
971 if (!ParseDeclaration(aDeclaration, PR_FALSE, aClearOldDecl, aChanged)) {
972 rv = mScanner.GetLowLevelError();
973 if (NS_FAILED(rv))
974 break;
976 if (!SkipDeclaration(PR_FALSE)) {
977 rv = mScanner.GetLowLevelError();
978 break;
981 } while (!aParseOnlyOneDecl);
982 aDeclaration->CompressFrom(&mData);
984 ReleaseScanner();
985 return rv;
988 NS_IMETHODIMP
989 CSSParserImpl::ParseRule(const nsAString& aRule,
990 nsIURI* aSheetURL,
991 nsIURI* aBaseURL,
992 nsIPrincipal* aSheetPrincipal,
993 nsCOMArray<nsICSSRule>& aResult)
995 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
996 AssertInitialState();
998 NS_ASSERTION(nsnull != aBaseURL, "need base URL");
1000 InitScanner(aRule, aSheetURL, 0, aBaseURL, aSheetPrincipal);
1002 mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
1004 nsCSSToken* tk = &mToken;
1005 // Get first non-whitespace token
1006 if (!GetToken(PR_TRUE)) {
1007 REPORT_UNEXPECTED(PEParseRuleWSOnly);
1008 OUTPUT_ERROR();
1009 } else if (eCSSToken_AtKeyword == tk->mType) {
1010 ParseAtRule(AppendRuleToArray, &aResult);
1012 else {
1013 UngetToken();
1014 ParseRuleSet(AppendRuleToArray, &aResult);
1016 OUTPUT_ERROR();
1017 ReleaseScanner();
1018 // XXX check for low-level errors
1019 return NS_OK;
1022 NS_IMETHODIMP
1023 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
1024 const nsAString& aPropValue,
1025 nsIURI* aSheetURL,
1026 nsIURI* aBaseURL,
1027 nsIPrincipal* aSheetPrincipal,
1028 nsCSSDeclaration* aDeclaration,
1029 PRBool* aChanged)
1031 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1032 AssertInitialState();
1034 NS_ASSERTION(nsnull != aBaseURL, "need base URL");
1035 NS_ASSERTION(nsnull != aDeclaration, "Need declaration to parse into!");
1036 *aChanged = PR_FALSE;
1038 InitScanner(aPropValue, aSheetURL, 0, aBaseURL, aSheetPrincipal);
1040 mSection = eCSSSection_General;
1042 if (eCSSProperty_UNKNOWN == aPropID) { // unknown property
1043 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1044 const PRUnichar *params[] = {
1045 propName.get()
1047 REPORT_UNEXPECTED_P(PEUnknownProperty, params);
1048 REPORT_UNEXPECTED(PEDeclDropped);
1049 OUTPUT_ERROR();
1050 ReleaseScanner();
1051 return NS_OK;
1054 mData.AssertInitialState();
1055 mTempData.AssertInitialState();
1056 aDeclaration->ExpandTo(&mData);
1057 nsresult result = NS_OK;
1058 PRBool parsedOK = ParseProperty(aPropID);
1059 if (parsedOK && !GetToken(PR_TRUE)) {
1060 TransferTempData(aDeclaration, aPropID, PR_FALSE, PR_FALSE, aChanged);
1061 } else {
1062 if (parsedOK) {
1063 // Junk at end of property value.
1064 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1066 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1067 const PRUnichar *params[] = {
1068 propName.get()
1070 REPORT_UNEXPECTED_P(PEValueParsingError, params);
1071 REPORT_UNEXPECTED(PEDeclDropped);
1072 OUTPUT_ERROR();
1073 ClearTempData(aPropID);
1074 result = mScanner.GetLowLevelError();
1076 CLEAR_ERROR();
1078 aDeclaration->CompressFrom(&mData);
1080 ReleaseScanner();
1081 return result;
1084 NS_IMETHODIMP
1085 CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer,
1086 nsIURI* aURL, // for error reporting
1087 PRUint32 aLineNumber, // for error reporting
1088 nsMediaList* aMediaList,
1089 PRBool aHTMLMode)
1091 // XXX Are there cases where the caller wants to keep what it already
1092 // has in case of parser error?
1093 aMediaList->Clear();
1095 // fake base URL since media lists don't have URLs in them
1096 InitScanner(aBuffer, aURL, aLineNumber, aURL, nsnull);
1098 AssertInitialState();
1099 NS_ASSERTION(aHTMLMode == PR_TRUE || aHTMLMode == PR_FALSE,
1100 "invalid PRBool");
1101 mHTMLMediaMode = aHTMLMode;
1103 // XXXldb We need to make the scanner not skip CSS comments! (Or
1104 // should we?)
1106 // For aHTMLMode, we used to follow the parsing rules in
1107 // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors
1108 // which wouldn't work for media queries since they remove all but the
1109 // first word. However, they're changed in
1110 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-document.html#media2
1111 // (as of 2008-05-29) which says that the media attribute just points
1112 // to a media query. (The main substative difference is the relative
1113 // precedence of commas and paretheses.)
1115 if (!GatherMedia(aMediaList, PRUnichar(0))) {
1116 aMediaList->Clear();
1117 aMediaList->SetNonEmpty(); // don't match anything
1118 if (!mHTMLMediaMode) {
1119 OUTPUT_ERROR();
1122 nsresult rv = mScanner.GetLowLevelError();
1123 CLEAR_ERROR();
1124 ReleaseScanner();
1125 mHTMLMediaMode = PR_FALSE;
1127 return rv;
1130 NS_IMETHODIMP
1131 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
1132 nsIURI* aURL, // for error reporting
1133 PRUint32 aLineNumber, // for error reporting
1134 nscolor* aColor)
1136 AssertInitialState();
1137 InitScanner(aBuffer, aURL, aLineNumber, aURL, nsnull);
1139 nsCSSValue value;
1140 PRBool colorParsed = ParseColor(value);
1141 nsresult rv = mScanner.GetLowLevelError();
1142 OUTPUT_ERROR();
1143 ReleaseScanner();
1145 if (!colorParsed) {
1146 return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
1149 if (value.GetUnit() == eCSSUnit_String) {
1150 nscolor rgba;
1151 if (NS_ColorNameToRGB(nsDependentString(value.GetStringBufferValue()), &rgba)) {
1152 (*aColor) = rgba;
1153 rv = NS_OK;
1155 } else if (value.GetUnit() == eCSSUnit_Color) {
1156 (*aColor) = value.GetColorValue();
1157 rv = NS_OK;
1158 } else if (value.GetUnit() == eCSSUnit_EnumColor) {
1159 PRInt32 intValue = value.GetIntValue();
1160 if (intValue >= 0) {
1161 nsCOMPtr<nsILookAndFeel> lfSvc = do_GetService("@mozilla.org/widget/lookandfeel;1");
1162 if (lfSvc) {
1163 nscolor rgba;
1164 rv = lfSvc->GetColor((nsILookAndFeel::nsColorID) value.GetIntValue(), rgba);
1165 if (NS_SUCCEEDED(rv))
1166 (*aColor) = rgba;
1168 } else {
1169 // XXX - this is NS_COLOR_CURRENTCOLOR, NS_COLOR_MOZ_HYPERLINKTEXT, etc.
1170 // which we don't handle as per the ParseColorString definition. Should
1171 // remove this limitation at some point.
1172 rv = NS_ERROR_FAILURE;
1176 return rv;
1179 NS_IMETHODIMP
1180 CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
1181 nsIURI* aURL, // for error reporting
1182 PRUint32 aLineNumber, // for error reporting
1183 nsCSSSelectorList **aSelectorList)
1185 InitScanner(aSelectorString, aURL, aLineNumber, aURL, nsnull);
1187 AssertInitialState();
1189 mUnresolvablePrefixException = PR_TRUE;
1191 PRBool success = ParseSelectorList(*aSelectorList, PR_FALSE);
1192 nsresult rv = mScanner.GetLowLevelError();
1193 OUTPUT_ERROR();
1194 ReleaseScanner();
1196 mUnresolvablePrefixException = PR_FALSE;
1198 if (success) {
1199 NS_ASSERTION(*aSelectorList, "Should have list!");
1200 return NS_OK;
1203 NS_ASSERTION(!*aSelectorList, "Shouldn't have list!");
1204 if (NS_SUCCEEDED(rv)) {
1205 rv = NS_ERROR_DOM_SYNTAX_ERR;
1207 return rv;
1210 //----------------------------------------------------------------------
1212 PRBool
1213 CSSParserImpl::GetToken(PRBool aSkipWS)
1215 for (;;) {
1216 if (!mHavePushBack) {
1217 if (!mScanner.Next(mToken)) {
1218 break;
1221 mHavePushBack = PR_FALSE;
1222 if (aSkipWS && (eCSSToken_WhiteSpace == mToken.mType)) {
1223 continue;
1225 return PR_TRUE;
1227 return PR_FALSE;
1230 PRBool
1231 CSSParserImpl::GetURLToken()
1233 for (;;) {
1234 // XXXldb This pushback code doesn't make sense.
1235 if (! mHavePushBack) {
1236 if (! mScanner.NextURL(mToken)) {
1237 break;
1240 mHavePushBack = PR_FALSE;
1241 if (eCSSToken_WhiteSpace != mToken.mType) {
1242 return PR_TRUE;
1245 return PR_FALSE;
1248 void
1249 CSSParserImpl::UngetToken()
1251 NS_PRECONDITION(mHavePushBack == PR_FALSE, "double pushback");
1252 mHavePushBack = PR_TRUE;
1255 PRBool
1256 CSSParserImpl::ExpectSymbol(PRUnichar aSymbol,
1257 PRBool aSkipWS)
1259 if (!GetToken(aSkipWS)) {
1260 // CSS2.1 specifies that all "open constructs" are to be closed at
1261 // EOF. It simplifies higher layers if we claim to have found an
1262 // ), ], }, or ; if we encounter EOF while looking for one of them.
1263 // Do still issue a diagnostic, to aid debugging.
1264 if (aSymbol == ')' || aSymbol == ']' ||
1265 aSymbol == '}' || aSymbol == ';') {
1266 REPORT_UNEXPECTED_EOF_CHAR(aSymbol);
1267 return PR_TRUE;
1269 else
1270 return PR_FALSE;
1272 if (mToken.IsSymbol(aSymbol)) {
1273 return PR_TRUE;
1275 UngetToken();
1276 return PR_FALSE;
1279 // Checks to see if we're at the end of a property. If an error occurs during
1280 // the check, does not signal a parse error.
1281 PRBool
1282 CSSParserImpl::CheckEndProperty()
1284 if (!GetToken(PR_TRUE)) {
1285 return PR_TRUE; // properties may end with eof
1287 if ((eCSSToken_Symbol == mToken.mType) &&
1288 ((';' == mToken.mSymbol) ||
1289 ('!' == mToken.mSymbol) ||
1290 ('}' == mToken.mSymbol))) {
1291 // XXX need to verify that ! is only followed by "important [;|}]
1292 // XXX this requires a multi-token pushback buffer
1293 UngetToken();
1294 return PR_TRUE;
1296 UngetToken();
1297 return PR_FALSE;
1300 // Checks if we're at the end of a property, raising an error if we're not.
1301 PRBool
1302 CSSParserImpl::ExpectEndProperty()
1304 if (CheckEndProperty())
1305 return PR_TRUE;
1307 // If we're here, we read something incorrect, so we should report it.
1308 REPORT_UNEXPECTED_TOKEN(PRExpectEndValue);
1309 return PR_FALSE;
1313 nsSubstring*
1314 CSSParserImpl::NextIdent()
1316 // XXX Error reporting?
1317 if (!GetToken(PR_TRUE)) {
1318 return nsnull;
1320 if (eCSSToken_Ident != mToken.mType) {
1321 UngetToken();
1322 return nsnull;
1324 return &mToken.mIdent;
1327 PRBool
1328 CSSParserImpl::SkipAtRule()
1330 for (;;) {
1331 if (!GetToken(PR_TRUE)) {
1332 REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF);
1333 return PR_FALSE;
1335 if (eCSSToken_Symbol == mToken.mType) {
1336 PRUnichar symbol = mToken.mSymbol;
1337 if (symbol == ';') {
1338 break;
1340 if (symbol == '{') {
1341 SkipUntil('}');
1342 break;
1343 } else if (symbol == '(') {
1344 SkipUntil(')');
1345 } else if (symbol == '[') {
1346 SkipUntil(']');
1350 return PR_TRUE;
1353 PRBool
1354 CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc,
1355 void* aData)
1357 if ((mSection <= eCSSSection_Charset) &&
1358 (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
1359 if (ParseCharsetRule(aAppendFunc, aData)) {
1360 mSection = eCSSSection_Import; // only one charset allowed
1361 return PR_TRUE;
1364 if ((mSection <= eCSSSection_Import) &&
1365 mToken.mIdent.LowerCaseEqualsLiteral("import")) {
1366 if (ParseImportRule(aAppendFunc, aData)) {
1367 mSection = eCSSSection_Import;
1368 return PR_TRUE;
1371 if ((mSection <= eCSSSection_NameSpace) &&
1372 mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
1373 if (ParseNameSpaceRule(aAppendFunc, aData)) {
1374 mSection = eCSSSection_NameSpace;
1375 return PR_TRUE;
1378 if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
1379 if (ParseMediaRule(aAppendFunc, aData)) {
1380 mSection = eCSSSection_General;
1381 return PR_TRUE;
1384 if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
1385 if (ParseMozDocumentRule(aAppendFunc, aData)) {
1386 mSection = eCSSSection_General;
1387 return PR_TRUE;
1390 if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
1391 if (ParseFontFaceRule(aAppendFunc, aData)) {
1392 mSection = eCSSSection_General;
1393 return PR_TRUE;
1396 if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
1397 if (ParsePageRule(aAppendFunc, aData)) {
1398 mSection = eCSSSection_General;
1399 return PR_TRUE;
1403 if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
1404 REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
1405 OUTPUT_ERROR();
1408 // Skip over unsupported at rule, don't advance section
1409 return SkipAtRule();
1412 PRBool
1413 CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc,
1414 void* aData)
1416 if (!GetToken(PR_TRUE)) {
1417 REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
1418 return PR_FALSE;
1421 if (eCSSToken_String != mToken.mType) {
1422 REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
1423 return PR_FALSE;
1426 nsAutoString charset = mToken.mIdent;
1428 if (!ExpectSymbol(';', PR_TRUE)) {
1429 return PR_FALSE;
1432 nsCOMPtr<nsICSSRule> rule;
1433 NS_NewCSSCharsetRule(getter_AddRefs(rule), charset);
1435 if (rule) {
1436 (*aAppendFunc)(rule, aData);
1439 return PR_TRUE;
1442 PRBool
1443 CSSParserImpl::GatherURL(nsString& aURL)
1445 if (!GetToken(PR_TRUE)) {
1446 return PR_FALSE;
1448 if (eCSSToken_String == mToken.mType) {
1449 aURL = mToken.mIdent;
1450 return PR_TRUE;
1452 else if (eCSSToken_Function == mToken.mType &&
1453 mToken.mIdent.LowerCaseEqualsLiteral("url") &&
1454 ExpectSymbol('(', PR_FALSE) &&
1455 GetURLToken() &&
1456 (eCSSToken_String == mToken.mType ||
1457 eCSSToken_URL == mToken.mType)) {
1458 aURL = mToken.mIdent;
1459 if (ExpectSymbol(')', PR_TRUE)) {
1460 return PR_TRUE;
1463 return PR_FALSE;
1466 // Callers must clear or throw out aMedia if GatherMedia returns false.
1467 PRBool
1468 CSSParserImpl::GatherMedia(nsMediaList* aMedia,
1469 PRUnichar aStopSymbol)
1471 // "If the comma-separated list is the empty list it is assumed to
1472 // specify the media query 'all'." (css3-mediaqueries, section
1473 // "Media Queries")
1474 if (!GetToken(PR_TRUE)) {
1475 // expected termination by EOF
1476 if (aStopSymbol == PRUnichar(0))
1477 return PR_TRUE;
1479 // unexpected termination by EOF; if we were looking for a
1480 // semicolon, return true anyway, for the same reason this is
1481 // done by ExpectSymbol().
1482 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1483 return aStopSymbol == PRUnichar(';');
1486 if (eCSSToken_Symbol == mToken.mType &&
1487 mToken.mSymbol == aStopSymbol) {
1488 UngetToken();
1489 return PR_TRUE;
1491 UngetToken();
1492 aMedia->SetNonEmpty();
1494 for (;;) {
1495 // We want to still have |query| after we transfer ownership from
1496 // |queryHolder| to |aMedia|.
1497 nsMediaQuery *query;
1499 nsAutoPtr<nsMediaQuery> queryHolder(new nsMediaQuery);
1500 if (!queryHolder) {
1501 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1502 return PR_FALSE;
1504 query = queryHolder;
1506 // In terms of error handling, it doesn't really matter when we
1507 // append this, since aMedia's contents get dropped entirely
1508 // whenever there is an error.
1509 nsresult rv = aMedia->AppendQuery(queryHolder);
1510 if (NS_FAILED(rv)) {
1511 mScanner.SetLowLevelError(rv);
1512 return PR_FALSE;
1514 NS_ASSERTION(!queryHolder, "ownership should have been transferred");
1517 if (ExpectSymbol('(', PR_TRUE)) {
1518 // we got an expression without a media type
1519 UngetToken(); // so ParseMediaQueryExpression can handle it
1520 query->SetType(nsGkAtoms::all);
1521 query->SetTypeOmitted();
1522 // Just parse the first expression here.
1523 if (!ParseMediaQueryExpression(query)) {
1524 OUTPUT_ERROR();
1525 query->SetHadUnknownExpression();
1527 } else {
1528 nsCOMPtr<nsIAtom> mediaType;
1529 PRBool gotNotOrOnly = PR_FALSE;
1530 for (;;) {
1531 if (!GetToken(PR_TRUE)) {
1532 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1533 return PR_FALSE;
1535 if (eCSSToken_Ident != mToken.mType) {
1536 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
1537 UngetToken();
1538 return PR_FALSE;
1540 // case insensitive from CSS - must be lower cased
1541 ToLowerCase(mToken.mIdent);
1542 mediaType = do_GetAtom(mToken.mIdent);
1543 if (gotNotOrOnly ||
1544 (mediaType != nsGkAtoms::_not && mediaType != nsGkAtoms::only))
1545 break;
1546 gotNotOrOnly = PR_TRUE;
1547 if (mediaType == nsGkAtoms::_not)
1548 query->SetNegated();
1549 else
1550 query->SetHasOnly();
1552 query->SetType(mediaType);
1555 for (;;) {
1556 if (!GetToken(PR_TRUE)) {
1557 // expected termination by EOF
1558 if (aStopSymbol == PRUnichar(0))
1559 return PR_TRUE;
1561 // unexpected termination by EOF; if we were looking for a
1562 // semicolon, return true anyway, for the same reason this is
1563 // done by ExpectSymbol().
1564 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1565 return aStopSymbol == PRUnichar(';');
1568 if (eCSSToken_Symbol == mToken.mType &&
1569 mToken.mSymbol == aStopSymbol) {
1570 UngetToken();
1571 return PR_TRUE;
1573 if (eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
1574 // Done with the expressions for this query
1575 break;
1577 if (eCSSToken_Ident != mToken.mType ||
1578 !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
1579 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
1580 UngetToken();
1581 return PR_FALSE;
1583 if (!ParseMediaQueryExpression(query)) {
1584 OUTPUT_ERROR();
1585 query->SetHadUnknownExpression();
1589 NS_NOTREACHED("unreachable code");
1590 return PR_FALSE; // keep the compiler happy
1593 PRBool
1594 CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
1596 if (!ExpectSymbol('(', PR_TRUE)) {
1597 REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart);
1598 return PR_FALSE;
1600 if (! GetToken(PR_TRUE)) {
1601 REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
1602 return PR_FALSE;
1604 if (eCSSToken_Ident != mToken.mType) {
1605 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
1606 SkipUntil(')');
1607 return PR_FALSE;
1610 nsMediaExpression *expr = aQuery->NewExpression();
1611 if (!expr) {
1612 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1613 SkipUntil(')');
1614 return PR_FALSE;
1617 // case insensitive from CSS - must be lower cased
1618 ToLowerCase(mToken.mIdent);
1619 const PRUnichar *featureString;
1620 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("min-"))) {
1621 expr->mRange = nsMediaExpression::eMin;
1622 featureString = mToken.mIdent.get() + 4;
1623 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("max-"))) {
1624 expr->mRange = nsMediaExpression::eMax;
1625 featureString = mToken.mIdent.get() + 4;
1626 } else {
1627 expr->mRange = nsMediaExpression::eEqual;
1628 featureString = mToken.mIdent.get();
1631 nsCOMPtr<nsIAtom> mediaFeatureAtom = do_GetAtom(featureString);
1632 const nsMediaFeature *feature = nsMediaFeatures::features;
1633 for (; feature->mName; ++feature) {
1634 if (*(feature->mName) == mediaFeatureAtom) {
1635 break;
1638 if (!feature->mName ||
1639 (expr->mRange != nsMediaExpression::eEqual &&
1640 feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) {
1641 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
1642 SkipUntil(')');
1643 return PR_FALSE;
1645 expr->mFeature = feature;
1647 if (!GetToken(PR_TRUE) || mToken.IsSymbol(')')) {
1648 // Query expressions for any feature can be given without a value.
1649 // However, min/max prefixes are not allowed.
1650 if (expr->mRange != nsMediaExpression::eEqual) {
1651 REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue);
1652 return PR_FALSE;
1654 expr->mValue.Reset();
1655 return PR_TRUE;
1658 if (!mToken.IsSymbol(':')) {
1659 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
1660 SkipUntil(')');
1661 return PR_FALSE;
1664 PRBool rv;
1665 switch (feature->mValueType) {
1666 case nsMediaFeature::eLength:
1667 rv = ParsePositiveVariant(expr->mValue, VARIANT_LENGTH, nsnull);
1668 break;
1669 case nsMediaFeature::eInteger:
1670 case nsMediaFeature::eBoolInteger:
1671 rv = ParsePositiveVariant(expr->mValue, VARIANT_INTEGER, nsnull);
1672 // Enforce extra restrictions for eBoolInteger
1673 if (rv &&
1674 feature->mValueType == nsMediaFeature::eBoolInteger &&
1675 expr->mValue.GetIntValue() > 1)
1676 rv = PR_FALSE;
1677 break;
1678 case nsMediaFeature::eIntRatio:
1680 // Two integers separated by '/', with optional whitespace on
1681 // either side of the '/'.
1682 nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
1683 if (!a) {
1684 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1685 SkipUntil(')');
1686 return PR_FALSE;
1688 expr->mValue.SetArrayValue(a, eCSSUnit_Array);
1689 // We don't bother with ParsePositiveVariant since we have to
1690 // check for != 0 as well; no need to worry about the UngetToken
1691 // since we're throwing out up to the next ')' anyway.
1692 rv = ParseVariant(a->Item(0), VARIANT_INTEGER, nsnull) &&
1693 a->Item(0).GetIntValue() > 0 &&
1694 ExpectSymbol('/', PR_TRUE) &&
1695 ParseVariant(a->Item(1), VARIANT_INTEGER, nsnull) &&
1696 a->Item(1).GetIntValue() > 0;
1698 break;
1699 case nsMediaFeature::eResolution:
1700 rv = GetToken(PR_TRUE) && mToken.IsDimension() &&
1701 mToken.mIntegerValid && mToken.mNumber > 0.0f;
1702 if (rv) {
1703 // No worries about whether unitless zero is allowed, since the
1704 // value must be positive (and we checked that above).
1705 NS_ASSERTION(!mToken.mIdent.IsEmpty(), "IsDimension lied");
1706 if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
1707 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
1708 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
1709 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
1710 } else {
1711 rv = PR_FALSE;
1714 break;
1715 case nsMediaFeature::eEnumerated:
1716 rv = ParseVariant(expr->mValue, VARIANT_KEYWORD,
1717 feature->mKeywordTable);
1718 break;
1720 if (!rv || !ExpectSymbol(')', PR_TRUE)) {
1721 REPORT_UNEXPECTED(PEMQExpectedFeatureValue);
1722 SkipUntil(')');
1723 return PR_FALSE;
1726 return PR_TRUE;
1729 // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
1730 PRBool
1731 CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData)
1733 nsCOMPtr<nsMediaList> media = new nsMediaList();
1734 if (!media) {
1735 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1736 return PR_FALSE;
1739 nsAutoString url;
1740 if (!GatherURL(url)) {
1741 REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
1742 return PR_FALSE;
1745 if (!ExpectSymbol(';', PR_TRUE)) {
1746 if (!GatherMedia(media, ';') ||
1747 !ExpectSymbol(';', PR_TRUE)) {
1748 REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
1749 // don't advance section, simply ignore invalid @import
1750 return PR_FALSE;
1753 // Safe to assert this, since we ensured that there is something
1754 // other than the ';' coming after the @import's url() token.
1755 NS_ASSERTION(media->Count() != 0, "media list must be nonempty");
1758 ProcessImport(url, media, aAppendFunc, aData);
1759 return PR_TRUE;
1763 PRBool
1764 CSSParserImpl::ProcessImport(const nsString& aURLSpec,
1765 nsMediaList* aMedia,
1766 RuleAppendFunc aAppendFunc,
1767 void* aData)
1769 nsCOMPtr<nsICSSImportRule> rule;
1770 nsresult rv = NS_NewCSSImportRule(getter_AddRefs(rule), aURLSpec, aMedia);
1771 if (NS_FAILED(rv)) {
1772 mScanner.SetLowLevelError(rv);
1773 return PR_FALSE;
1775 (*aAppendFunc)(rule, aData);
1777 if (mChildLoader) {
1778 nsCOMPtr<nsIURI> url;
1779 // XXX should pass a charset!
1780 rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nsnull, mBaseURL);
1782 if (NS_FAILED(rv)) {
1783 // import url is bad
1784 // XXX log this somewhere for easier web page debugging
1785 mScanner.SetLowLevelError(rv);
1786 return PR_FALSE;
1789 mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule);
1792 return PR_TRUE;
1795 // Parse the {} part of an @media or @-moz-document rule.
1796 PRBool
1797 CSSParserImpl::ParseGroupRule(nsICSSGroupRule* aRule,
1798 RuleAppendFunc aAppendFunc,
1799 void* aData)
1801 // XXXbz this could use better error reporting throughout the method
1802 if (!ExpectSymbol('{', PR_TRUE)) {
1803 return PR_FALSE;
1806 // push rule on stack, loop over children
1807 if (!PushGroup(aRule)) {
1808 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1809 return PR_FALSE;
1811 nsCSSSection holdSection = mSection;
1812 mSection = eCSSSection_General;
1814 for (;;) {
1815 // Get next non-whitespace token
1816 if (! GetToken(PR_TRUE)) {
1817 REPORT_UNEXPECTED_EOF(PEGroupRuleEOF);
1818 break;
1820 if (mToken.IsSymbol('}')) { // done!
1821 UngetToken();
1822 break;
1824 if (eCSSToken_AtKeyword == mToken.mType) {
1825 SkipAtRule(); // group rules cannot contain @rules
1826 continue;
1828 UngetToken();
1829 ParseRuleSet(AppendRuleToSheet, this);
1831 PopGroup();
1833 if (!ExpectSymbol('}', PR_TRUE)) {
1834 mSection = holdSection;
1835 return PR_FALSE;
1837 (*aAppendFunc)(aRule, aData);
1838 return PR_TRUE;
1841 // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
1842 PRBool
1843 CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData)
1845 nsCOMPtr<nsMediaList> media = new nsMediaList();
1846 if (!media) {
1847 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1848 return PR_FALSE;
1851 if (GatherMedia(media, '{')) {
1852 // XXXbz this could use better error reporting throughout the method
1853 nsRefPtr<nsCSSMediaRule> rule(new nsCSSMediaRule());
1854 // Append first, so when we do SetMedia() the rule
1855 // knows what its stylesheet is.
1856 if (rule && ParseGroupRule(rule, aAppendFunc, aData)) {
1857 rule->SetMedia(media);
1858 return PR_TRUE;
1862 return PR_FALSE;
1865 // Parse a @-moz-document rule. This is like an @media rule, but instead
1866 // of a medium it has a nonempty list of items where each item is either
1867 // url(), url-prefix(), or domain().
1868 PRBool
1869 CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData)
1871 nsCSSDocumentRule::URL *urls = nsnull;
1872 nsCSSDocumentRule::URL **next = &urls;
1873 do {
1874 if (!GetToken(PR_TRUE) ||
1875 eCSSToken_Function != mToken.mType ||
1876 !(mToken.mIdent.LowerCaseEqualsLiteral("url") ||
1877 mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
1878 mToken.mIdent.LowerCaseEqualsLiteral("domain"))) {
1879 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc);
1880 delete urls;
1881 return PR_FALSE;
1883 nsCSSDocumentRule::URL *cur = *next = new nsCSSDocumentRule::URL;
1884 if (!cur) {
1885 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1886 delete urls;
1887 return PR_FALSE;
1889 next = &cur->next;
1890 if (mToken.mIdent.LowerCaseEqualsLiteral("url")) {
1891 cur->func = nsCSSDocumentRule::eURL;
1892 } else if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
1893 cur->func = nsCSSDocumentRule::eURLPrefix;
1894 } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
1895 cur->func = nsCSSDocumentRule::eDomain;
1898 if (!ExpectSymbol('(', PR_FALSE) ||
1899 !GetURLToken() ||
1900 (eCSSToken_String != mToken.mType &&
1901 eCSSToken_URL != mToken.mType)) {
1902 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
1903 delete urls;
1904 return PR_FALSE;
1906 if (!ExpectSymbol(')', PR_TRUE)) {
1907 delete urls;
1908 return PR_FALSE;
1911 // We could try to make the URL (as long as it's not domain())
1912 // canonical and absolute with NS_NewURI and GetSpec, but I'm
1913 // inclined to think we shouldn't.
1914 CopyUTF16toUTF8(mToken.mIdent, cur->url);
1915 } while (ExpectSymbol(',', PR_TRUE));
1917 nsRefPtr<nsCSSDocumentRule> rule(new nsCSSDocumentRule());
1918 if (!rule) {
1919 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1920 delete urls;
1921 return PR_FALSE;
1923 rule->SetURLs(urls);
1925 return ParseGroupRule(rule, aAppendFunc, aData);
1928 // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
1929 PRBool
1930 CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData)
1932 if (!GetToken(PR_TRUE)) {
1933 REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
1934 return PR_FALSE;
1937 nsAutoString prefix;
1938 nsAutoString url;
1940 if (eCSSToken_Ident == mToken.mType) {
1941 prefix = mToken.mIdent;
1942 // user-specified identifiers are case-sensitive (bug 416106)
1943 if (! GetToken(PR_TRUE)) {
1944 REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
1945 return PR_FALSE;
1949 if (eCSSToken_String == mToken.mType) {
1950 url = mToken.mIdent;
1951 if (ExpectSymbol(';', PR_TRUE)) {
1952 ProcessNameSpace(prefix, url, aAppendFunc, aData);
1953 return PR_TRUE;
1956 else if ((eCSSToken_Function == mToken.mType) &&
1957 (mToken.mIdent.LowerCaseEqualsLiteral("url"))) {
1958 if (ExpectSymbol('(', PR_FALSE)) {
1959 if (GetURLToken()) {
1960 if ((eCSSToken_String == mToken.mType) || (eCSSToken_URL == mToken.mType)) {
1961 url = mToken.mIdent;
1962 if (ExpectSymbol(')', PR_TRUE)) {
1963 if (ExpectSymbol(';', PR_TRUE)) {
1964 ProcessNameSpace(prefix, url, aAppendFunc, aData);
1965 return PR_TRUE;
1972 REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
1974 return PR_FALSE;
1977 PRBool
1978 CSSParserImpl::ProcessNameSpace(const nsString& aPrefix,
1979 const nsString& aURLSpec,
1980 RuleAppendFunc aAppendFunc,
1981 void* aData)
1983 PRBool result = PR_FALSE;
1985 nsCOMPtr<nsICSSNameSpaceRule> rule;
1986 nsCOMPtr<nsIAtom> prefix;
1988 if (!aPrefix.IsEmpty()) {
1989 prefix = do_GetAtom(aPrefix);
1992 NS_NewCSSNameSpaceRule(getter_AddRefs(rule), prefix, aURLSpec);
1993 if (rule) {
1994 (*aAppendFunc)(rule, aData);
1996 // If this was the first namespace rule encountered, it will trigger
1997 // creation of a namespace map.
1998 if (!mNameSpaceMap) {
1999 mNameSpaceMap = mSheet->GetNameSpaceMap();
2003 return result;
2006 // font-face-rule: '@font-face' '{' font-description '}'
2007 // font-description: font-descriptor+
2008 PRBool
2009 CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData)
2011 if (!ExpectSymbol('{', PR_TRUE))
2012 return PR_FALSE;
2014 nsRefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule());
2015 if (!rule) {
2016 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2017 return PR_FALSE;
2020 for (;;) {
2021 if (!GetToken(PR_TRUE)) {
2022 REPORT_UNEXPECTED_EOF(PEFontFaceEOF);
2023 break;
2025 if (mToken.IsSymbol('}')) { // done!
2026 UngetToken();
2027 break;
2030 // ignore extra semicolons
2031 if (mToken.IsSymbol(';'))
2032 continue;
2034 if (!ParseFontDescriptor(rule)) {
2035 REPORT_UNEXPECTED(PEDeclSkipped);
2036 OUTPUT_ERROR();
2037 if (!SkipDeclaration(PR_TRUE))
2038 break;
2041 if (!ExpectSymbol('}', PR_TRUE))
2042 return PR_FALSE;
2043 (*aAppendFunc)(rule, aData);
2044 return PR_TRUE;
2047 // font-descriptor: font-family-desc
2048 // | font-style-desc
2049 // | font-weight-desc
2050 // | font-stretch-desc
2051 // | font-src-desc
2052 // | unicode-range-desc
2054 // All font-*-desc productions follow the pattern
2055 // IDENT ':' value ';'
2057 // On entry to this function, mToken is the IDENT.
2059 PRBool
2060 CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule)
2062 if (eCSSToken_Ident != mToken.mType) {
2063 REPORT_UNEXPECTED_TOKEN(PEFontDescExpected);
2064 return PR_FALSE;
2067 nsString descName = mToken.mIdent;
2068 if (!ExpectSymbol(':', PR_TRUE)) {
2069 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
2070 OUTPUT_ERROR();
2071 return PR_FALSE;
2074 nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName);
2075 nsCSSValue value;
2077 if (descID == eCSSFontDesc_UNKNOWN) {
2078 if (NonMozillaVendorIdentifier(descName)) {
2079 // silently skip other vendors' extensions
2080 SkipDeclaration(PR_TRUE);
2081 return PR_TRUE;
2082 } else {
2083 const PRUnichar *params[] = {
2084 descName.get()
2086 REPORT_UNEXPECTED_P(PEUnknownFontDesc, params);
2087 return PR_FALSE;
2091 if (!ParseFontDescriptorValue(descID, value)) {
2092 const PRUnichar *params[] = {
2093 descName.get()
2095 REPORT_UNEXPECTED_P(PEValueParsingError, params);
2096 return PR_FALSE;
2099 if (!ExpectEndProperty())
2100 return PR_FALSE;
2102 aRule->SetDesc(descID, value);
2103 return PR_TRUE;
2107 PRBool
2108 CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData)
2110 // XXX not yet implemented
2111 return PR_FALSE;
2114 void
2115 CSSParserImpl::SkipUntil(PRUnichar aStopSymbol)
2117 nsCSSToken* tk = &mToken;
2118 for (;;) {
2119 if (!GetToken(PR_TRUE)) {
2120 break;
2122 if (eCSSToken_Symbol == tk->mType) {
2123 PRUnichar symbol = tk->mSymbol;
2124 if (symbol == aStopSymbol) {
2125 break;
2126 } else if ('{' == symbol) {
2127 SkipUntil('}');
2128 } else if ('[' == symbol) {
2129 SkipUntil(']');
2130 } else if ('(' == symbol) {
2131 SkipUntil(')');
2137 PRBool
2138 CSSParserImpl::GetNonCloseParenToken(PRBool aSkipWS)
2140 if (!GetToken(aSkipWS))
2141 return PR_FALSE;
2142 if (mToken.mType == eCSSToken_Symbol && mToken.mSymbol == ')') {
2143 UngetToken();
2144 return PR_FALSE;
2146 return PR_TRUE;
2149 PRBool
2150 CSSParserImpl::SkipDeclaration(PRBool aCheckForBraces)
2152 nsCSSToken* tk = &mToken;
2153 for (;;) {
2154 if (!GetToken(PR_TRUE)) {
2155 if (aCheckForBraces) {
2156 REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
2158 return PR_FALSE;
2160 if (eCSSToken_Symbol == tk->mType) {
2161 PRUnichar symbol = tk->mSymbol;
2162 if (';' == symbol) {
2163 break;
2165 if (aCheckForBraces) {
2166 if ('}' == symbol) {
2167 UngetToken();
2168 break;
2171 if ('{' == symbol) {
2172 SkipUntil('}');
2173 } else if ('(' == symbol) {
2174 SkipUntil(')');
2175 } else if ('[' == symbol) {
2176 SkipUntil(']');
2180 return PR_TRUE;
2183 void
2184 CSSParserImpl::SkipRuleSet()
2186 nsCSSToken* tk = &mToken;
2187 for (;;) {
2188 if (!GetToken(PR_TRUE)) {
2189 REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
2190 break;
2192 if (eCSSToken_Symbol == tk->mType) {
2193 PRUnichar symbol = tk->mSymbol;
2194 if ('{' == symbol) {
2195 SkipUntil('}');
2196 break;
2198 if ('(' == symbol) {
2199 SkipUntil(')');
2200 } else if ('[' == symbol) {
2201 SkipUntil(']');
2207 PRBool
2208 CSSParserImpl::PushGroup(nsICSSGroupRule* aRule)
2210 if (mGroupStack.AppendObject(aRule))
2211 return PR_TRUE;
2213 return PR_FALSE;
2216 void
2217 CSSParserImpl::PopGroup(void)
2219 PRInt32 count = mGroupStack.Count();
2220 if (0 < count) {
2221 mGroupStack.RemoveObjectAt(count - 1);
2225 void
2226 CSSParserImpl::AppendRule(nsICSSRule* aRule)
2228 PRInt32 count = mGroupStack.Count();
2229 if (0 < count) {
2230 mGroupStack[count - 1]->AppendStyleRule(aRule);
2232 else {
2233 mSheet->AppendStyleRule(aRule);
2237 PRBool
2238 CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData)
2240 // First get the list of selectors for the rule
2241 nsCSSSelectorList* slist = nsnull;
2242 PRUint32 linenum = mScanner.GetLineNumber();
2243 if (! ParseSelectorList(slist, PR_TRUE)) {
2244 REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
2245 OUTPUT_ERROR();
2246 SkipRuleSet();
2247 return PR_FALSE;
2249 NS_ASSERTION(nsnull != slist, "null selector list");
2250 CLEAR_ERROR();
2252 // Next parse the declaration block
2253 nsCSSDeclaration* declaration = ParseDeclarationBlock(PR_TRUE);
2254 if (nsnull == declaration) {
2255 // XXX skip something here
2256 delete slist;
2257 return PR_FALSE;
2260 #if 0
2261 slist->Dump();
2262 fputs("{\n", stdout);
2263 declaration->List();
2264 fputs("}\n", stdout);
2265 #endif
2267 // Translate the selector list and declaration block into style data
2269 nsCOMPtr<nsICSSStyleRule> rule;
2270 NS_NewCSSStyleRule(getter_AddRefs(rule), slist, declaration);
2271 if (!rule) {
2272 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2273 delete slist;
2274 return PR_FALSE;
2276 rule->SetLineNumber(linenum);
2277 (*aAppendFunc)(rule, aData);
2279 return PR_TRUE;
2282 PRBool
2283 CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
2284 PRBool aTerminateAtBrace)
2286 nsCSSSelectorList* list = nsnull;
2287 if (! ParseSelectorGroup(list)) {
2288 // must have at least one selector group
2289 aListHead = nsnull;
2290 return PR_FALSE;
2292 NS_ASSERTION(nsnull != list, "no selector list");
2293 aListHead = list;
2295 // After that there must either be a "," or a "{" (the latter if
2296 // aTerminateAtBrace is true)
2297 nsCSSToken* tk = &mToken;
2298 for (;;) {
2299 if (! GetToken(PR_TRUE)) {
2300 if (!aTerminateAtBrace) {
2301 return PR_TRUE;
2304 REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
2305 break;
2308 if (eCSSToken_Symbol == tk->mType) {
2309 if (',' == tk->mSymbol) {
2310 nsCSSSelectorList* newList = nsnull;
2311 // Another selector group must follow
2312 if (! ParseSelectorGroup(newList)) {
2313 break;
2315 // add new list to the end of the selector list
2316 list->mNext = newList;
2317 list = newList;
2318 continue;
2319 } else if ('{' == tk->mSymbol && aTerminateAtBrace) {
2320 UngetToken();
2321 return PR_TRUE;
2324 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
2325 UngetToken();
2326 break;
2329 delete aListHead;
2330 aListHead = nsnull;
2331 return PR_FALSE;
2334 static PRBool IsSinglePseudoClass(const nsCSSSelector& aSelector)
2336 return PRBool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
2337 (aSelector.mTag == nsnull) &&
2338 (aSelector.mIDList == nsnull) &&
2339 (aSelector.mClassList == nsnull) &&
2340 (aSelector.mAttrList == nsnull) &&
2341 (aSelector.mNegations == nsnull) &&
2342 (aSelector.mPseudoClassList != nsnull) &&
2343 (aSelector.mPseudoClassList->mNext == nsnull));
2346 #ifdef MOZ_XUL
2347 static PRBool IsTreePseudoElement(nsIAtom* aPseudo)
2349 const char* str;
2350 aPseudo->GetUTF8String(&str);
2351 static const char moz_tree[] = ":-moz-tree-";
2352 return nsCRT::strncmp(str, moz_tree, PRInt32(sizeof(moz_tree)-1)) == 0;
2354 #endif
2356 PRBool
2357 CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList)
2359 nsAutoPtr<nsCSSSelectorList> list;
2360 PRUnichar combinator = PRUnichar(0);
2361 PRInt32 weight = 0;
2362 PRBool havePseudoElement = PR_FALSE;
2363 PRBool done = PR_FALSE;
2364 while (!done) {
2365 nsAutoPtr<nsCSSSelector> newSelector(new nsCSSSelector());
2366 if (!newSelector) {
2367 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2368 return PR_FALSE;
2370 nsSelectorParsingStatus parsingStatus =
2371 ParseSelector(*newSelector);
2372 if (parsingStatus == eSelectorParsingStatus_Empty) {
2373 if (!list) {
2374 REPORT_UNEXPECTED(PESelectorGroupNoSelector);
2376 break;
2378 if (parsingStatus == eSelectorParsingStatus_Error) {
2379 list = nsnull;
2380 break;
2382 if (nsnull == list) {
2383 list = new nsCSSSelectorList();
2384 if (nsnull == list) {
2385 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2386 return PR_FALSE;
2389 list->AddSelector(newSelector);
2390 nsCSSSelector* listSel = list->mSelectors;
2392 // pull out pseudo elements here
2393 nsPseudoClassList* prevList = nsnull;
2394 nsPseudoClassList* pseudoClassList = listSel->mPseudoClassList;
2395 while (nsnull != pseudoClassList) {
2396 if (! nsCSSPseudoClasses::IsPseudoClass(pseudoClassList->mAtom)) {
2397 havePseudoElement = PR_TRUE;
2398 if (IsSinglePseudoClass(*listSel)) { // convert to pseudo element selector
2399 nsIAtom* pseudoElement = pseudoClassList->mAtom; // steal ref count
2400 pseudoClassList->mAtom = nsnull;
2401 listSel->Reset();
2402 if (listSel->mNext) {// more to the selector
2403 listSel->mOperator = PRUnichar('>');
2404 nsAutoPtr<nsCSSSelector> empty(new nsCSSSelector());
2405 if (!empty) {
2406 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2407 return PR_FALSE;
2409 list->AddSelector(empty); // leave a blank (universal) selector in the middle
2410 listSel = list->mSelectors; // use the new one for the pseudo
2412 listSel->mTag = pseudoElement;
2414 else { // append new pseudo element selector
2415 nsAutoPtr<nsCSSSelector> pseudoTagSelector(new nsCSSSelector());
2416 if (!pseudoTagSelector) {
2417 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2418 return PR_FALSE;
2420 pseudoTagSelector->mTag = pseudoClassList->mAtom; // steal ref count
2421 #ifdef MOZ_XUL
2422 if (IsTreePseudoElement(pseudoTagSelector->mTag)) {
2423 // Take the remaining "pseudoclasses" that we parsed
2424 // inside the tree pseudoelement's ()-list, and
2425 // make our new selector have these pseudoclasses
2426 // in its pseudoclass list.
2427 pseudoTagSelector->mPseudoClassList = pseudoClassList->mNext;
2428 pseudoClassList->mNext = nsnull;
2430 #endif
2431 list->AddSelector(pseudoTagSelector);
2432 pseudoClassList->mAtom = nsnull;
2433 listSel->mOperator = PRUnichar('>');
2434 if (nsnull == prevList) { // delete list entry
2435 listSel->mPseudoClassList = pseudoClassList->mNext;
2437 else {
2438 prevList->mNext = pseudoClassList->mNext;
2440 pseudoClassList->mNext = nsnull;
2441 delete pseudoClassList;
2442 weight += listSel->CalcWeight(); // capture weight from remainder
2444 break; // only one pseudo element per selector
2446 prevList = pseudoClassList;
2447 pseudoClassList = pseudoClassList->mNext;
2450 combinator = PRUnichar(0);
2451 if (!GetToken(PR_FALSE)) {
2452 break;
2455 // Assume we are done unless we find a combinator here.
2456 done = PR_TRUE;
2457 if (eCSSToken_WhiteSpace == mToken.mType) {
2458 if (!GetToken(PR_TRUE)) {
2459 break;
2461 done = PR_FALSE;
2464 if (eCSSToken_Symbol == mToken.mType &&
2465 ('+' == mToken.mSymbol ||
2466 '>' == mToken.mSymbol ||
2467 '~' == mToken.mSymbol)) {
2468 done = PR_FALSE;
2469 combinator = mToken.mSymbol;
2470 list->mSelectors->SetOperator(combinator);
2472 else {
2473 if (eCSSToken_Symbol == mToken.mType &&
2474 ('{' == mToken.mSymbol ||
2475 ',' == mToken.mSymbol)) {
2476 // End of this selector group
2477 done = PR_TRUE;
2479 UngetToken(); // give it back to selector if we're not done, or make sure
2480 // we see it as the end of the selector if we are.
2483 if (havePseudoElement) {
2484 break;
2486 else {
2487 weight += listSel->CalcWeight();
2491 if (PRUnichar(0) != combinator) { // no dangling combinators
2492 list = nsnull;
2493 // This should report the problematic combinator
2494 REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
2496 aList = list.forget();
2497 if (aList) {
2498 aList->mWeight = weight;
2500 return PRBool(nsnull != aList);
2503 #define SEL_MASK_NSPACE 0x01
2504 #define SEL_MASK_ELEM 0x02
2505 #define SEL_MASK_ID 0x04
2506 #define SEL_MASK_CLASS 0x08
2507 #define SEL_MASK_ATTRIB 0x10
2508 #define SEL_MASK_PCLASS 0x20
2509 #define SEL_MASK_PELEM 0x40
2512 // Parses an ID selector #name
2514 CSSParserImpl::nsSelectorParsingStatus
2515 CSSParserImpl::ParseIDSelector(PRInt32& aDataMask,
2516 nsCSSSelector& aSelector)
2518 NS_ASSERTION(!mToken.mIdent.IsEmpty(),
2519 "Empty mIdent in eCSSToken_ID token?");
2520 aDataMask |= SEL_MASK_ID;
2521 aSelector.AddID(mToken.mIdent);
2522 return eSelectorParsingStatus_Continue;
2526 // Parses a class selector .name
2528 CSSParserImpl::nsSelectorParsingStatus
2529 CSSParserImpl::ParseClassSelector(PRInt32& aDataMask,
2530 nsCSSSelector& aSelector)
2532 if (! GetToken(PR_FALSE)) { // get ident
2533 REPORT_UNEXPECTED_EOF(PEClassSelEOF);
2534 return eSelectorParsingStatus_Error;
2536 if (eCSSToken_Ident != mToken.mType) { // malformed selector
2537 REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
2538 UngetToken();
2539 return eSelectorParsingStatus_Error;
2541 aDataMask |= SEL_MASK_CLASS;
2543 aSelector.AddClass(mToken.mIdent);
2545 return eSelectorParsingStatus_Continue;
2549 // Parse a type element selector or a universal selector
2550 // namespace|type or namespace|* or *|* or *
2552 CSSParserImpl::nsSelectorParsingStatus
2553 CSSParserImpl::ParseTypeOrUniversalSelector(PRInt32& aDataMask,
2554 nsCSSSelector& aSelector,
2555 PRBool aIsNegated)
2557 nsAutoString buffer;
2558 if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace
2559 if (ExpectSymbol('|', PR_FALSE)) { // was namespace
2560 aDataMask |= SEL_MASK_NSPACE;
2561 aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
2563 if (! GetToken(PR_FALSE)) {
2564 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2565 return eSelectorParsingStatus_Error;
2567 if (eCSSToken_Ident == mToken.mType) { // element name
2568 aDataMask |= SEL_MASK_ELEM;
2569 if (mCaseSensitive) {
2570 aSelector.SetTag(mToken.mIdent);
2572 else {
2573 ToLowerCase(mToken.mIdent, buffer);
2574 aSelector.SetTag(buffer);
2577 else if (mToken.IsSymbol('*')) { // universal selector
2578 aDataMask |= SEL_MASK_ELEM;
2579 // don't set tag
2581 else {
2582 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2583 UngetToken();
2584 return eSelectorParsingStatus_Error;
2587 else { // was universal element selector
2588 SetDefaultNamespaceOnSelector(aSelector);
2589 aDataMask |= SEL_MASK_ELEM;
2590 // don't set any tag in the selector
2592 if (! GetToken(PR_FALSE)) { // premature eof is ok (here!)
2593 return eSelectorParsingStatus_Done;
2596 else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name
2597 buffer = mToken.mIdent; // hang on to ident
2599 if (ExpectSymbol('|', PR_FALSE)) { // was namespace
2600 aDataMask |= SEL_MASK_NSPACE;
2601 PRInt32 nameSpaceID;
2602 if (!GetNamespaceIdForPrefix(buffer, &nameSpaceID)) {
2603 return eSelectorParsingStatus_Error;
2605 aSelector.SetNameSpace(nameSpaceID);
2607 if (! GetToken(PR_FALSE)) {
2608 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2609 return eSelectorParsingStatus_Error;
2611 if (eCSSToken_Ident == mToken.mType) { // element name
2612 aDataMask |= SEL_MASK_ELEM;
2613 if (mCaseSensitive) {
2614 aSelector.SetTag(mToken.mIdent);
2616 else {
2617 ToLowerCase(mToken.mIdent, buffer);
2618 aSelector.SetTag(buffer);
2621 else if (mToken.IsSymbol('*')) { // universal selector
2622 aDataMask |= SEL_MASK_ELEM;
2623 // don't set tag
2625 else {
2626 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2627 UngetToken();
2628 return eSelectorParsingStatus_Error;
2631 else { // was element name
2632 SetDefaultNamespaceOnSelector(aSelector);
2633 if (mCaseSensitive) {
2634 aSelector.SetTag(buffer);
2636 else {
2637 ToLowerCase(buffer);
2638 aSelector.SetTag(buffer);
2640 aDataMask |= SEL_MASK_ELEM;
2642 if (! GetToken(PR_FALSE)) { // premature eof is ok (here!)
2643 return eSelectorParsingStatus_Done;
2646 else if (mToken.IsSymbol('|')) { // No namespace
2647 aDataMask |= SEL_MASK_NSPACE;
2648 aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace
2650 // get mandatory tag
2651 if (! GetToken(PR_FALSE)) {
2652 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2653 return eSelectorParsingStatus_Error;
2655 if (eCSSToken_Ident == mToken.mType) { // element name
2656 aDataMask |= SEL_MASK_ELEM;
2657 if (mCaseSensitive) {
2658 aSelector.SetTag(mToken.mIdent);
2660 else {
2661 ToLowerCase(mToken.mIdent, buffer);
2662 aSelector.SetTag(buffer);
2665 else if (mToken.IsSymbol('*')) { // universal selector
2666 aDataMask |= SEL_MASK_ELEM;
2667 // don't set tag
2669 else {
2670 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2671 UngetToken();
2672 return eSelectorParsingStatus_Error;
2674 if (! GetToken(PR_FALSE)) { // premature eof is ok (here!)
2675 return eSelectorParsingStatus_Done;
2678 else {
2679 SetDefaultNamespaceOnSelector(aSelector);
2682 if (aIsNegated) {
2683 // restore last token read in case of a negated type selector
2684 UngetToken();
2686 return eSelectorParsingStatus_Continue;
2690 // Parse attribute selectors [attr], [attr=value], [attr|=value],
2691 // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
2693 CSSParserImpl::nsSelectorParsingStatus
2694 CSSParserImpl::ParseAttributeSelector(PRInt32& aDataMask,
2695 nsCSSSelector& aSelector)
2697 if (! GetToken(PR_TRUE)) { // premature EOF
2698 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2699 return eSelectorParsingStatus_Error;
2702 PRInt32 nameSpaceID = kNameSpaceID_None;
2703 nsAutoString attr;
2704 if (mToken.IsSymbol('*')) { // wildcard namespace
2705 nameSpaceID = kNameSpaceID_Unknown;
2706 if (ExpectSymbol('|', PR_FALSE)) {
2707 if (! GetToken(PR_FALSE)) { // premature EOF
2708 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2709 return eSelectorParsingStatus_Error;
2711 if (eCSSToken_Ident == mToken.mType) { // attr name
2712 attr = mToken.mIdent;
2714 else {
2715 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2716 UngetToken();
2717 return eSelectorParsingStatus_Error;
2720 else {
2721 REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
2722 return eSelectorParsingStatus_Error;
2725 else if (mToken.IsSymbol('|')) { // NO namespace
2726 if (! GetToken(PR_FALSE)) { // premature EOF
2727 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2728 return eSelectorParsingStatus_Error;
2730 if (eCSSToken_Ident == mToken.mType) { // attr name
2731 attr = mToken.mIdent;
2733 else {
2734 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2735 UngetToken();
2736 return eSelectorParsingStatus_Error;
2739 else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
2740 attr = mToken.mIdent; // hang on to it
2741 if (ExpectSymbol('|', PR_FALSE)) { // was a namespace
2742 if (!GetNamespaceIdForPrefix(attr, &nameSpaceID)) {
2743 return eSelectorParsingStatus_Error;
2745 if (! GetToken(PR_FALSE)) { // premature EOF
2746 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2747 return eSelectorParsingStatus_Error;
2749 if (eCSSToken_Ident == mToken.mType) { // attr name
2750 attr = mToken.mIdent;
2752 else {
2753 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2754 UngetToken();
2755 return eSelectorParsingStatus_Error;
2759 else { // malformed
2760 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
2761 UngetToken();
2762 return eSelectorParsingStatus_Error;
2765 if (! mCaseSensitive) {
2766 ToLowerCase(attr);
2768 if (! GetToken(PR_TRUE)) { // premature EOF
2769 REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
2770 return eSelectorParsingStatus_Error;
2772 if ((eCSSToken_Symbol == mToken.mType) ||
2773 (eCSSToken_Includes == mToken.mType) ||
2774 (eCSSToken_Dashmatch == mToken.mType) ||
2775 (eCSSToken_Beginsmatch == mToken.mType) ||
2776 (eCSSToken_Endsmatch == mToken.mType) ||
2777 (eCSSToken_Containsmatch == mToken.mType)) {
2778 PRUint8 func;
2779 if (eCSSToken_Includes == mToken.mType) {
2780 func = NS_ATTR_FUNC_INCLUDES;
2782 else if (eCSSToken_Dashmatch == mToken.mType) {
2783 func = NS_ATTR_FUNC_DASHMATCH;
2785 else if (eCSSToken_Beginsmatch == mToken.mType) {
2786 func = NS_ATTR_FUNC_BEGINSMATCH;
2788 else if (eCSSToken_Endsmatch == mToken.mType) {
2789 func = NS_ATTR_FUNC_ENDSMATCH;
2791 else if (eCSSToken_Containsmatch == mToken.mType) {
2792 func = NS_ATTR_FUNC_CONTAINSMATCH;
2794 else if (']' == mToken.mSymbol) {
2795 aDataMask |= SEL_MASK_ATTRIB;
2796 aSelector.AddAttribute(nameSpaceID, attr);
2797 func = NS_ATTR_FUNC_SET;
2799 else if ('=' == mToken.mSymbol) {
2800 func = NS_ATTR_FUNC_EQUALS;
2802 else {
2803 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
2804 UngetToken(); // bad function
2805 return eSelectorParsingStatus_Error;
2807 if (NS_ATTR_FUNC_SET != func) { // get value
2808 if (! GetToken(PR_TRUE)) { // premature EOF
2809 REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
2810 return eSelectorParsingStatus_Error;
2812 if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
2813 nsAutoString value(mToken.mIdent);
2814 if (! GetToken(PR_TRUE)) { // premature EOF
2815 REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
2816 return eSelectorParsingStatus_Error;
2818 if (mToken.IsSymbol(']')) {
2819 PRBool isCaseSensitive = PR_TRUE;
2821 // If we're parsing a style sheet for an HTML document, and
2822 // the attribute selector is for a non-namespaced attribute,
2823 // then check to see if it's one of the known attributes whose
2824 // VALUE is case-insensitive.
2825 if (!mCaseSensitive && nameSpaceID == kNameSpaceID_None) {
2826 static const char* caseInsensitiveHTMLAttribute[] = {
2827 // list based on http://www.w3.org/TR/html4/
2828 "lang",
2829 "dir",
2830 "http-equiv",
2831 "text",
2832 "link",
2833 "vlink",
2834 "alink",
2835 "compact",
2836 "align",
2837 "frame",
2838 "rules",
2839 "valign",
2840 "scope",
2841 "axis",
2842 "nowrap",
2843 "hreflang",
2844 "rel",
2845 "rev",
2846 "charset",
2847 "codetype",
2848 "declare",
2849 "valuetype",
2850 "shape",
2851 "nohref",
2852 "media",
2853 "bgcolor",
2854 "clear",
2855 "color",
2856 "face",
2857 "noshade",
2858 "noresize",
2859 "scrolling",
2860 "target",
2861 "method",
2862 "enctype",
2863 "accept-charset",
2864 "accept",
2865 "checked",
2866 "multiple",
2867 "selected",
2868 "disabled",
2869 "readonly",
2870 "language",
2871 "defer",
2872 "type",
2873 // additional attributes not in HTML4
2874 "direction", // marquee
2875 nsnull
2877 short i = 0;
2878 const char* htmlAttr;
2879 while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) {
2880 if (attr.EqualsIgnoreCase(htmlAttr)) {
2881 isCaseSensitive = PR_FALSE;
2882 break;
2886 aDataMask |= SEL_MASK_ATTRIB;
2887 aSelector.AddAttribute(nameSpaceID, attr, func, value, isCaseSensitive);
2889 else {
2890 REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
2891 UngetToken();
2892 return eSelectorParsingStatus_Error;
2895 else {
2896 REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
2897 UngetToken();
2898 return eSelectorParsingStatus_Error;
2902 else {
2903 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
2904 UngetToken(); // bad dog, no biscut!
2905 return eSelectorParsingStatus_Error;
2907 return eSelectorParsingStatus_Continue;
2911 // Parse pseudo-classes and pseudo-elements
2913 CSSParserImpl::nsSelectorParsingStatus
2914 CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask,
2915 nsCSSSelector& aSelector,
2916 PRBool aIsNegated)
2918 if (! GetToken(PR_FALSE)) { // premature eof
2919 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
2920 return eSelectorParsingStatus_Error;
2923 // First, find out whether we are parsing a CSS3 pseudo-element
2924 PRBool parsingPseudoElement = PR_FALSE;
2925 if (mToken.IsSymbol(':')) {
2926 parsingPseudoElement = PR_TRUE;
2927 if (! GetToken(PR_FALSE)) { // premature eof
2928 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
2929 return eSelectorParsingStatus_Error;
2933 // Do some sanity-checking on the token
2934 if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
2935 // malformed selector
2936 REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
2937 UngetToken();
2938 return eSelectorParsingStatus_Error;
2941 // OK, now we know we have an mIdent. Atomize it. All the atoms, for
2942 // pseudo-classes as well as pseudo-elements, start with a single ':'.
2943 nsAutoString buffer;
2944 buffer.Append(PRUnichar(':'));
2945 buffer.Append(mToken.mIdent);
2946 ToLowerCase(buffer);
2947 nsCOMPtr<nsIAtom> pseudo = do_GetAtom(buffer);
2949 // stash away some info about this pseudo so we only have to get it once.
2950 PRBool isTreePseudo = PR_FALSE;
2951 #ifdef MOZ_XUL
2952 isTreePseudo = IsTreePseudoElement(pseudo);
2953 // If a tree pseudo-element is using the function syntax, it will
2954 // get isTree set here and will pass the check below that only
2955 // allows functions if they are in our list of things allowed to be
2956 // functions. If it is _not_ using the function syntax, isTree will
2957 // be false, and it will still pass that check. So the tree
2958 // pseudo-elements are allowed to be either functions or not, as
2959 // desired.
2960 PRBool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
2961 #endif
2962 PRBool isPseudoElement = nsCSSPseudoElements::IsPseudoElement(pseudo);
2963 // anonymous boxes are only allowed if they're the tree boxes or we have
2964 // enabled unsafe rules
2965 PRBool isAnonBox = nsCSSAnonBoxes::IsAnonBox(pseudo) &&
2966 (mUnsafeRulesEnabled || isTreePseudo);
2967 PRBool isPseudoClass = nsCSSPseudoClasses::IsPseudoClass(pseudo);
2969 if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
2970 // Not a pseudo-class, not a pseudo-element.... forget it
2971 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
2972 UngetToken();
2973 return eSelectorParsingStatus_Error;
2976 // If it's a function token, it better be on our "ok" list, and if the name
2977 // is that of a function pseudo it better be a function token
2978 if ((eCSSToken_Function == mToken.mType) !=
2980 #ifdef MOZ_XUL
2981 isTree ||
2982 #endif
2983 nsCSSPseudoClasses::notPseudo == pseudo ||
2984 nsCSSPseudoClasses::HasStringArg(pseudo) ||
2985 nsCSSPseudoClasses::HasNthPairArg(pseudo))) {
2986 // There are no other function pseudos
2987 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
2988 UngetToken();
2989 return eSelectorParsingStatus_Error;
2992 // If it starts with "::", it better be a pseudo-element
2993 if (parsingPseudoElement &&
2994 !isPseudoElement &&
2995 !isAnonBox) {
2996 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
2997 UngetToken();
2998 return eSelectorParsingStatus_Error;
3001 if (!parsingPseudoElement && nsCSSPseudoClasses::notPseudo == pseudo) {
3002 if (aIsNegated) { // :not() can't be itself negated
3003 REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
3004 UngetToken();
3005 return eSelectorParsingStatus_Error;
3007 // CSS 3 Negation pseudo-class takes one simple selector as argument
3008 nsSelectorParsingStatus parsingStatus =
3009 ParseNegatedSimpleSelector(aDataMask, aSelector);
3010 if (eSelectorParsingStatus_Continue != parsingStatus) {
3011 return parsingStatus;
3014 else if (!parsingPseudoElement && isPseudoClass) {
3015 aDataMask |= SEL_MASK_PCLASS;
3016 if (nsCSSPseudoClasses::HasStringArg(pseudo)) {
3017 nsSelectorParsingStatus parsingStatus =
3018 ParsePseudoClassWithIdentArg(aSelector, pseudo);
3019 if (eSelectorParsingStatus_Continue != parsingStatus) {
3020 return parsingStatus;
3023 else if (nsCSSPseudoClasses::HasNthPairArg(pseudo)) {
3024 nsSelectorParsingStatus parsingStatus =
3025 ParsePseudoClassWithNthPairArg(aSelector, pseudo);
3026 if (eSelectorParsingStatus_Continue != parsingStatus) {
3027 return parsingStatus;
3030 else {
3031 aSelector.AddPseudoClass(pseudo);
3034 else if (isPseudoElement || isAnonBox) {
3035 // Pseudo-element. Make some more sanity checks.
3037 if (aIsNegated) { // pseudo-elements can't be negated
3038 REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
3039 UngetToken();
3040 return eSelectorParsingStatus_Error;
3042 // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
3043 // to have a single ':' on them. Others (CSS3+ pseudo-elements and
3044 // various -moz-* pseudo-elements) must have |parsingPseudoElement|
3045 // set.
3046 if (!parsingPseudoElement &&
3047 !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
3048 #ifdef MOZ_XUL
3049 && !isTreePseudo
3050 #endif
3052 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
3053 UngetToken();
3054 return eSelectorParsingStatus_Error;
3057 if (0 == (aDataMask & SEL_MASK_PELEM)) {
3058 aDataMask |= SEL_MASK_PELEM;
3059 aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later
3061 #ifdef MOZ_XUL
3062 if (isTree) {
3063 // We have encountered a pseudoelement of the form
3064 // -moz-tree-xxxx(a,b,c). We parse (a,b,c) and add each
3065 // item in the list to the pseudoclass list. They will be pulled
3066 // from the list later along with the pseudo-element.
3067 if (!ParseTreePseudoElement(aSelector)) {
3068 return eSelectorParsingStatus_Error;
3071 #endif
3073 // ensure selector ends here, must be followed by EOF, space, '{' or ','
3074 if (GetToken(PR_FALSE)) { // premature eof is ok (here!)
3075 if ((eCSSToken_WhiteSpace == mToken.mType) ||
3076 (mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
3077 UngetToken();
3078 return eSelectorParsingStatus_Done;
3080 REPORT_UNEXPECTED_TOKEN(PEPseudoSelTrailing);
3081 UngetToken();
3082 return eSelectorParsingStatus_Error;
3085 else { // multiple pseudo elements, not legal
3086 REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
3087 UngetToken();
3088 return eSelectorParsingStatus_Error;
3091 #ifdef DEBUG
3092 else {
3093 // We should never end up here. Indeed, if we ended up here, we know (from
3094 // the current if/else cascade) that !isPseudoElement and !isAnonBox. But
3095 // then due to our earlier check we know that isPseudoClass. Since we
3096 // didn't fall into the isPseudoClass case in this cascade, we must have
3097 // parsingPseudoElement. But we've already checked the
3098 // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
3099 // it's happened.
3100 NS_NOTREACHED("How did this happen?");
3102 #endif
3103 return eSelectorParsingStatus_Continue;
3107 // Parse the argument of a negation pseudo-class :not()
3109 CSSParserImpl::nsSelectorParsingStatus
3110 CSSParserImpl::ParseNegatedSimpleSelector(PRInt32& aDataMask,
3111 nsCSSSelector& aSelector)
3113 // Check if we have the first parenthesis
3114 if (!ExpectSymbol('(', PR_FALSE)) {
3115 REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
3116 return eSelectorParsingStatus_Error;
3119 if (! GetToken(PR_TRUE)) { // premature eof
3120 REPORT_UNEXPECTED_EOF(PENegationEOF);
3121 return eSelectorParsingStatus_Error;
3124 // Create a new nsCSSSelector and add it to the end of
3125 // aSelector.mNegations.
3126 // Given the current parsing rules, every selector in mNegations
3127 // contains only one simple selector (css3 definition) within it.
3128 // This could easily change in future versions of CSS, and the only
3129 // thing we need to change to support that is this parsing code.
3130 nsCSSSelector *newSel = new nsCSSSelector();
3131 if (!newSel) {
3132 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
3133 return eSelectorParsingStatus_Error;
3135 nsCSSSelector* negations = &aSelector;
3136 while (negations->mNegations) {
3137 negations = negations->mNegations;
3139 negations->mNegations = newSel;
3141 nsSelectorParsingStatus parsingStatus;
3142 if (eCSSToken_ID == mToken.mType) { // #id
3143 parsingStatus = ParseIDSelector(aDataMask, *newSel);
3145 else if (mToken.IsSymbol('.')) { // .class
3146 parsingStatus = ParseClassSelector(aDataMask, *newSel);
3148 else if (mToken.IsSymbol(':')) { // :pseudo
3149 parsingStatus = ParsePseudoSelector(aDataMask, *newSel, PR_TRUE);
3151 else if (mToken.IsSymbol('[')) { // [attribute
3152 parsingStatus = ParseAttributeSelector(aDataMask, *newSel);
3154 else {
3155 // then it should be a type element or universal selector
3156 parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, PR_TRUE);
3158 if (eSelectorParsingStatus_Error == parsingStatus) {
3159 REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
3160 return parsingStatus;
3162 // close the parenthesis
3163 if (!ExpectSymbol(')', PR_TRUE)) {
3164 REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
3165 return eSelectorParsingStatus_Error;
3168 return eSelectorParsingStatus_Continue;
3172 // Parse the argument of a pseudo-class that has an ident arg
3174 CSSParserImpl::nsSelectorParsingStatus
3175 CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
3176 nsIAtom* aPseudo)
3178 // Check if we have the first parenthesis
3179 if (!ExpectSymbol('(', PR_FALSE)) {
3180 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoArg);
3181 return eSelectorParsingStatus_Error;
3184 if (! GetToken(PR_TRUE)) { // premature eof
3185 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3186 return eSelectorParsingStatus_Error;
3188 // We expect an identifier with a language abbreviation
3189 if (eCSSToken_Ident != mToken.mType) {
3190 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent);
3191 UngetToken();
3192 // XXX Call SkipUntil to the next ")"?
3193 return eSelectorParsingStatus_Error;
3196 // Add the pseudo with the language parameter
3197 aSelector.AddPseudoClass(aPseudo, mToken.mIdent.get());
3199 // close the parenthesis
3200 if (!ExpectSymbol(')', PR_TRUE)) {
3201 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
3202 // XXX Call SkipUntil to the next ")"?
3203 return eSelectorParsingStatus_Error;
3206 return eSelectorParsingStatus_Continue;
3209 CSSParserImpl::nsSelectorParsingStatus
3210 CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
3211 nsIAtom* aPseudo)
3213 PRInt32 numbers[2] = { 0, 0 };
3214 PRBool lookForB = PR_TRUE;
3216 // Check whether we have the first parenthesis
3217 if (!ExpectSymbol('(', PR_FALSE)) {
3218 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoArg);
3219 return eSelectorParsingStatus_Error;
3222 // Follow the whitespace rules as proposed in
3223 // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
3225 if (! GetToken(PR_TRUE)) {
3226 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3227 return eSelectorParsingStatus_Error;
3230 if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
3231 // The CSS tokenization doesn't handle :nth-child() containing - well:
3232 // 2n-1 is a dimension
3233 // n-1 is an identifier
3234 // The easiest way to deal with that is to push everything from the
3235 // minus on back onto the scanner's pushback buffer.
3236 PRUint32 truncAt = 0;
3237 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
3238 truncAt = 1;
3239 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("-n-"))) {
3240 truncAt = 2;
3242 if (truncAt != 0) {
3243 for (PRUint32 i = mToken.mIdent.Length() - 1; i >= truncAt; --i) {
3244 mScanner.Pushback(mToken.mIdent[i]);
3246 mToken.mIdent.Truncate(truncAt);
3250 if (eCSSToken_Ident == mToken.mType) {
3251 if (mToken.mIdent.EqualsIgnoreCase("odd")) {
3252 numbers[0] = 2;
3253 numbers[1] = 1;
3254 lookForB = PR_FALSE;
3256 else if (mToken.mIdent.EqualsIgnoreCase("even")) {
3257 numbers[0] = 2;
3258 numbers[1] = 0;
3259 lookForB = PR_FALSE;
3261 else if (mToken.mIdent.EqualsIgnoreCase("n")) {
3262 numbers[0] = 1;
3264 else if (mToken.mIdent.EqualsIgnoreCase("-n")) {
3265 numbers[0] = -1;
3267 else {
3268 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3269 // XXX Call SkipUntil to the next ")"?
3270 return eSelectorParsingStatus_Error;
3273 else if (eCSSToken_Number == mToken.mType) {
3274 if (!mToken.mIntegerValid) {
3275 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3276 // XXX Call SkipUntil to the next ")"?
3277 return eSelectorParsingStatus_Error;
3279 numbers[1] = mToken.mInteger;
3280 lookForB = PR_FALSE;
3282 else if (eCSSToken_Dimension == mToken.mType) {
3283 if (!mToken.mIntegerValid || !mToken.mIdent.EqualsIgnoreCase("n")) {
3284 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3285 // XXX Call SkipUntil to the next ")"?
3286 return eSelectorParsingStatus_Error;
3288 numbers[0] = mToken.mInteger;
3290 // XXX If it's a ')', is that valid? (as 0n+0)
3291 else {
3292 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3293 // XXX Call SkipUntil to the next ")" (unless this is one already)?
3294 return eSelectorParsingStatus_Error;
3297 if (! GetToken(PR_TRUE)) {
3298 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3299 return eSelectorParsingStatus_Error;
3301 if (lookForB && !mToken.IsSymbol(')')) {
3302 // The '+' or '-' sign can optionally be separated by whitespace.
3303 // If it is separated by whitespace from what follows it, it appears
3304 // as a separate token rather than part of the number token.
3305 PRBool haveSign = PR_FALSE;
3306 PRInt32 sign = 1;
3307 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
3308 haveSign = PR_TRUE;
3309 if (mToken.IsSymbol('-')) {
3310 sign = -1;
3312 if (! GetToken(PR_TRUE)) {
3313 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3314 return eSelectorParsingStatus_Error;
3317 if (eCSSToken_Number != mToken.mType ||
3318 !mToken.mIntegerValid || mToken.mHasSign == haveSign) {
3319 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3320 // XXX Call SkipUntil to the next ")"?
3321 return eSelectorParsingStatus_Error;
3323 numbers[1] = mToken.mInteger * sign;
3324 if (! GetToken(PR_TRUE)) {
3325 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3326 return eSelectorParsingStatus_Error;
3329 if (!mToken.IsSymbol(')')) {
3330 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
3331 // XXX Call SkipUntil to the next ")"?
3332 return eSelectorParsingStatus_Error;
3334 aSelector.AddPseudoClass(aPseudo, numbers);
3335 return eSelectorParsingStatus_Continue;
3340 * This is the format for selectors:
3341 * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
3343 CSSParserImpl::nsSelectorParsingStatus
3344 CSSParserImpl::ParseSelector(nsCSSSelector& aSelector)
3346 if (! GetToken(PR_TRUE)) {
3347 REPORT_UNEXPECTED_EOF(PESelectorEOF);
3348 return eSelectorParsingStatus_Error;
3351 PRInt32 dataMask = 0;
3352 nsSelectorParsingStatus parsingStatus =
3353 ParseTypeOrUniversalSelector(dataMask, aSelector, PR_FALSE);
3354 if (parsingStatus != eSelectorParsingStatus_Continue) {
3355 return parsingStatus;
3358 for (;;) {
3359 if (eCSSToken_ID == mToken.mType) { // #id
3360 parsingStatus = ParseIDSelector(dataMask, aSelector);
3362 else if (mToken.IsSymbol('.')) { // .class
3363 parsingStatus = ParseClassSelector(dataMask, aSelector);
3365 else if (mToken.IsSymbol(':')) { // :pseudo
3366 parsingStatus = ParsePseudoSelector(dataMask, aSelector, PR_FALSE);
3368 else if (mToken.IsSymbol('[')) { // [attribute
3369 parsingStatus = ParseAttributeSelector(dataMask, aSelector);
3371 else { // not a selector token, we're done
3372 parsingStatus = eSelectorParsingStatus_Done;
3373 break;
3376 if (parsingStatus != eSelectorParsingStatus_Continue) {
3377 return parsingStatus;
3380 if (! GetToken(PR_FALSE)) { // premature eof is ok (here!)
3381 return eSelectorParsingStatus_Done;
3384 UngetToken();
3385 return dataMask ? parsingStatus : eSelectorParsingStatus_Empty;
3388 nsCSSDeclaration*
3389 CSSParserImpl::ParseDeclarationBlock(PRBool aCheckForBraces)
3391 if (aCheckForBraces) {
3392 if (!ExpectSymbol('{', PR_TRUE)) {
3393 REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
3394 OUTPUT_ERROR();
3395 return nsnull;
3398 nsCSSDeclaration* declaration = new nsCSSDeclaration();
3399 mData.AssertInitialState();
3400 if (declaration) {
3401 for (;;) {
3402 PRBool changed;
3403 if (!ParseDeclaration(declaration, aCheckForBraces,
3404 PR_TRUE, &changed)) {
3405 if (!SkipDeclaration(aCheckForBraces)) {
3406 break;
3408 if (aCheckForBraces) {
3409 if (ExpectSymbol('}', PR_TRUE)) {
3410 break;
3413 // Since the skipped declaration didn't end the block we parse
3414 // the next declaration.
3417 declaration->CompressFrom(&mData);
3419 return declaration;
3422 // The types to pass to ParseColorComponent. These correspond to the
3423 // various datatypes that can go within rgb().
3424 #define COLOR_TYPE_UNKNOWN 0
3425 #define COLOR_TYPE_INTEGERS 1
3426 #define COLOR_TYPE_PERCENTAGES 2
3428 PRBool
3429 CSSParserImpl::ParseColor(nsCSSValue& aValue)
3431 if (!GetToken(PR_TRUE)) {
3432 REPORT_UNEXPECTED_EOF(PEColorEOF);
3433 return PR_FALSE;
3436 nsCSSToken* tk = &mToken;
3437 nscolor rgba;
3438 switch (tk->mType) {
3439 case eCSSToken_ID:
3440 case eCSSToken_Ref:
3441 // #xxyyzz
3442 if (NS_HexToRGB(tk->mIdent, &rgba)) {
3443 aValue.SetColorValue(rgba);
3444 return PR_TRUE;
3446 break;
3448 case eCSSToken_Ident:
3449 if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
3450 aValue.SetStringValue(tk->mIdent, eCSSUnit_String);
3451 return PR_TRUE;
3453 else {
3454 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
3455 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
3456 PRInt32 value;
3457 if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) {
3458 aValue.SetIntValue(value, eCSSUnit_EnumColor);
3459 return PR_TRUE;
3463 break;
3464 case eCSSToken_Function:
3465 if (mToken.mIdent.LowerCaseEqualsLiteral("rgb")) {
3466 // rgb ( component , component , component )
3467 PRUint8 r, g, b;
3468 PRInt32 type = COLOR_TYPE_UNKNOWN;
3469 if (ExpectSymbol('(', PR_FALSE) && // this won't fail
3470 ParseColorComponent(r, type, ',') &&
3471 ParseColorComponent(g, type, ',') &&
3472 ParseColorComponent(b, type, ')')) {
3473 aValue.SetColorValue(NS_RGB(r,g,b));
3474 return PR_TRUE;
3476 return PR_FALSE; // already pushed back
3478 else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-rgba") ||
3479 mToken.mIdent.LowerCaseEqualsLiteral("rgba")) {
3480 // rgba ( component , component , component , opacity )
3481 PRUint8 r, g, b, a;
3482 PRInt32 type = COLOR_TYPE_UNKNOWN;
3483 if (ExpectSymbol('(', PR_FALSE) && // this won't fail
3484 ParseColorComponent(r, type, ',') &&
3485 ParseColorComponent(g, type, ',') &&
3486 ParseColorComponent(b, type, ',') &&
3487 ParseColorOpacity(a)) {
3488 aValue.SetColorValue(NS_RGBA(r, g, b, a));
3489 return PR_TRUE;
3491 return PR_FALSE; // already pushed back
3493 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
3494 // hsl ( hue , saturation , lightness )
3495 // "hue" is a number, "saturation" and "lightness" are percentages.
3496 if (ParseHSLColor(rgba, ')')) {
3497 aValue.SetColorValue(rgba);
3498 return PR_TRUE;
3500 return PR_FALSE;
3502 else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-hsla") ||
3503 mToken.mIdent.LowerCaseEqualsLiteral("hsla")) {
3504 // hsla ( hue , saturation , lightness , opacity )
3505 // "hue" is a number, "saturation" and "lightness" are percentages,
3506 // "opacity" is a number.
3507 PRUint8 a;
3508 if (ParseHSLColor(rgba, ',') &&
3509 ParseColorOpacity(a)) {
3510 aValue.SetColorValue(NS_RGBA(NS_GET_R(rgba), NS_GET_G(rgba),
3511 NS_GET_B(rgba), a));
3512 return PR_TRUE;
3514 return PR_FALSE;
3516 break;
3517 default:
3518 break;
3521 // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
3522 if (mNavQuirkMode && !IsParsingCompoundProperty()) {
3523 // - If the string starts with 'a-f', the nsCSSScanner builds the
3524 // token as a eCSSToken_Ident and we can parse the string as a
3525 // 'xxyyzz' RGB color.
3526 // - If it only contains '0-9' digits, the token is a
3527 // eCSSToken_Number and it must be converted back to a 6
3528 // characters string to be parsed as a RGB color.
3529 // - If it starts with '0-9' and contains any 'a-f', the token is a
3530 // eCSSToken_Dimension, the mNumber part must be converted back to
3531 // a string and the mIdent part must be appended to that string so
3532 // that the resulting string has 6 characters.
3533 // Note: This is a hack for Nav compatibility. Do not attempt to
3534 // simplify it by hacking into the ncCSSScanner. This would be very
3535 // bad.
3536 nsAutoString str;
3537 char buffer[20];
3538 switch (tk->mType) {
3539 case eCSSToken_Ident:
3540 str.Assign(tk->mIdent);
3541 break;
3543 case eCSSToken_Number:
3544 if (tk->mIntegerValid) {
3545 PR_snprintf(buffer, sizeof(buffer), "%06d", tk->mInteger);
3546 str.AssignWithConversion(buffer);
3548 break;
3550 case eCSSToken_Dimension:
3551 if (tk->mIdent.Length() <= 6) {
3552 PR_snprintf(buffer, sizeof(buffer), "%06.0f", tk->mNumber);
3553 nsAutoString temp;
3554 temp.AssignWithConversion(buffer);
3555 temp.Right(str, 6 - tk->mIdent.Length());
3556 str.Append(tk->mIdent);
3558 break;
3559 default:
3560 // There is a whole bunch of cases that are
3561 // not handled by this switch. Ignore them.
3562 break;
3564 if (NS_HexToRGB(str, &rgba)) {
3565 aValue.SetColorValue(rgba);
3566 return PR_TRUE;
3570 // It's not a color
3571 REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
3572 UngetToken();
3573 return PR_FALSE;
3576 // aType will be set if we have already parsed other color components
3577 // in this color spec
3578 PRBool
3579 CSSParserImpl::ParseColorComponent(PRUint8& aComponent,
3580 PRInt32& aType,
3581 char aStop)
3583 if (!GetToken(PR_TRUE)) {
3584 REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
3585 return PR_FALSE;
3587 float value;
3588 nsCSSToken* tk = &mToken;
3589 switch (tk->mType) {
3590 case eCSSToken_Number:
3591 switch (aType) {
3592 case COLOR_TYPE_UNKNOWN:
3593 aType = COLOR_TYPE_INTEGERS;
3594 break;
3595 case COLOR_TYPE_INTEGERS:
3596 break;
3597 case COLOR_TYPE_PERCENTAGES:
3598 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3599 UngetToken();
3600 return PR_FALSE;
3601 default:
3602 NS_NOTREACHED("Someone forgot to add the new color component type in here");
3605 if (!mToken.mIntegerValid) {
3606 REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
3607 UngetToken();
3608 return PR_FALSE;
3610 value = tk->mNumber;
3611 break;
3612 case eCSSToken_Percentage:
3613 switch (aType) {
3614 case COLOR_TYPE_UNKNOWN:
3615 aType = COLOR_TYPE_PERCENTAGES;
3616 break;
3617 case COLOR_TYPE_INTEGERS:
3618 REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
3619 UngetToken();
3620 return PR_FALSE;
3621 case COLOR_TYPE_PERCENTAGES:
3622 break;
3623 default:
3624 NS_NOTREACHED("Someone forgot to add the new color component type in here");
3626 value = tk->mNumber * 255.0f;
3627 break;
3628 default:
3629 REPORT_UNEXPECTED_TOKEN(PEColorBadRGBContents);
3630 UngetToken();
3631 return PR_FALSE;
3633 if (ExpectSymbol(aStop, PR_TRUE)) {
3634 if (value < 0.0f) value = 0.0f;
3635 if (value > 255.0f) value = 255.0f;
3636 aComponent = NSToIntRound(value);
3637 return PR_TRUE;
3639 const PRUnichar stopString[] = { PRUnichar(aStop), PRUnichar(0) };
3640 const PRUnichar *params[] = {
3641 nsnull,
3642 stopString
3644 REPORT_UNEXPECTED_TOKEN_P(PEColorComponentBadTerm, params);
3645 return PR_FALSE;
3649 PRBool
3650 CSSParserImpl::ParseHSLColor(nscolor& aColor,
3651 char aStop)
3653 float h, s, l;
3654 if (!ExpectSymbol('(', PR_FALSE)) {
3655 NS_ERROR("How did this get to be a function token?");
3656 return PR_FALSE;
3659 // Get the hue
3660 if (!GetToken(PR_TRUE)) {
3661 REPORT_UNEXPECTED_EOF(PEColorHueEOF);
3662 return PR_FALSE;
3664 if (mToken.mType != eCSSToken_Number) {
3665 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
3666 UngetToken();
3667 return PR_FALSE;
3669 h = mToken.mNumber;
3670 h /= 360.0f;
3671 // hue values are wraparound
3672 h = h - floor(h);
3674 if (!ExpectSymbol(',', PR_TRUE)) {
3675 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
3676 return PR_FALSE;
3679 // Get the saturation
3680 if (!GetToken(PR_TRUE)) {
3681 REPORT_UNEXPECTED_EOF(PEColorSaturationEOF);
3682 return PR_FALSE;
3684 if (mToken.mType != eCSSToken_Percentage) {
3685 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3686 UngetToken();
3687 return PR_FALSE;
3689 s = mToken.mNumber;
3690 if (s < 0.0f) s = 0.0f;
3691 if (s > 1.0f) s = 1.0f;
3693 if (!ExpectSymbol(',', PR_TRUE)) {
3694 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
3695 return PR_FALSE;
3698 // Get the lightness
3699 if (!GetToken(PR_TRUE)) {
3700 REPORT_UNEXPECTED_EOF(PEColorLightnessEOF);
3701 return PR_FALSE;
3703 if (mToken.mType != eCSSToken_Percentage) {
3704 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3705 UngetToken();
3706 return PR_FALSE;
3708 l = mToken.mNumber;
3709 if (l < 0.0f) l = 0.0f;
3710 if (l > 1.0f) l = 1.0f;
3712 if (ExpectSymbol(aStop, PR_TRUE)) {
3713 aColor = NS_HSL2RGB(h, s, l);
3714 return PR_TRUE;
3717 const PRUnichar stopString[] = { PRUnichar(aStop), PRUnichar(0) };
3718 const PRUnichar *params[] = {
3719 nsnull,
3720 stopString
3722 REPORT_UNEXPECTED_TOKEN_P(PEColorComponentBadTerm, params);
3723 return PR_FALSE;
3727 PRBool
3728 CSSParserImpl::ParseColorOpacity(PRUint8& aOpacity)
3730 if (!GetToken(PR_TRUE)) {
3731 REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
3732 return PR_FALSE;
3735 if (mToken.mType != eCSSToken_Number) {
3736 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
3737 UngetToken();
3738 return PR_FALSE;
3741 if (mToken.mNumber < 0.0f) {
3742 mToken.mNumber = 0.0f;
3743 } else if (mToken.mNumber > 1.0f) {
3744 mToken.mNumber = 1.0f;
3747 PRUint8 value = nsStyleUtil::FloatToColorComponent(mToken.mNumber);
3748 NS_ASSERTION(fabs(mToken.mNumber - value/255.0f) <= 0.5f,
3749 "FloatToColorComponent did something weird");
3751 if (!ExpectSymbol(')', PR_TRUE)) {
3752 REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
3753 return PR_FALSE;
3756 aOpacity = value;
3758 return PR_TRUE;
3761 #ifdef MOZ_XUL
3762 PRBool
3763 CSSParserImpl::ParseTreePseudoElement(nsCSSSelector& aSelector)
3765 if (ExpectSymbol('(', PR_FALSE)) {
3766 while (!ExpectSymbol(')', PR_TRUE)) {
3767 if (!GetToken(PR_TRUE)) {
3768 return PR_FALSE;
3770 else if (eCSSToken_Ident == mToken.mType) {
3771 nsCOMPtr<nsIAtom> pseudo = do_GetAtom(mToken.mIdent);
3772 aSelector.AddPseudoClass(pseudo);
3774 else if (eCSSToken_Symbol == mToken.mType) {
3775 if (!mToken.IsSymbol(','))
3776 return PR_FALSE;
3778 else return PR_FALSE;
3780 return PR_TRUE;
3782 return PR_FALSE;
3784 #endif
3786 //----------------------------------------------------------------------
3788 PRBool
3789 CSSParserImpl::ParseDeclaration(nsCSSDeclaration* aDeclaration,
3790 PRBool aCheckForBraces,
3791 PRBool aMustCallValueAppended,
3792 PRBool* aChanged)
3794 mTempData.AssertInitialState();
3796 // Get property name
3797 nsCSSToken* tk = &mToken;
3798 nsAutoString propertyName;
3799 for (;;) {
3800 if (!GetToken(PR_TRUE)) {
3801 if (aCheckForBraces) {
3802 REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
3804 return PR_FALSE;
3806 if (eCSSToken_Ident == tk->mType) {
3807 propertyName = tk->mIdent;
3808 // grab the ident before the ExpectSymbol trashes the token
3809 if (!ExpectSymbol(':', PR_TRUE)) {
3810 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
3811 REPORT_UNEXPECTED(PEDeclDropped);
3812 OUTPUT_ERROR();
3813 return PR_FALSE;
3815 break;
3817 if (tk->IsSymbol(';')) {
3818 // dangling semicolons are skipped
3819 continue;
3822 if (!tk->IsSymbol('}')) {
3823 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
3824 REPORT_UNEXPECTED(PEDeclSkipped);
3825 OUTPUT_ERROR();
3827 // Not a declaration...
3828 UngetToken();
3829 return PR_FALSE;
3832 // Map property name to its ID and then parse the property
3833 nsCSSProperty propID = nsCSSProps::LookupProperty(propertyName);
3834 if (eCSSProperty_UNKNOWN == propID) { // unknown property
3835 if (!NonMozillaVendorIdentifier(propertyName)) {
3836 const PRUnichar *params[] = {
3837 propertyName.get()
3839 REPORT_UNEXPECTED_P(PEUnknownProperty, params);
3840 REPORT_UNEXPECTED(PEDeclDropped);
3841 OUTPUT_ERROR();
3844 return PR_FALSE;
3846 if (! ParseProperty(propID)) {
3847 // XXX Much better to put stuff in the value parsers instead...
3848 const PRUnichar *params[] = {
3849 propertyName.get()
3851 REPORT_UNEXPECTED_P(PEValueParsingError, params);
3852 REPORT_UNEXPECTED(PEDeclDropped);
3853 OUTPUT_ERROR();
3854 ClearTempData(propID);
3855 return PR_FALSE;
3857 CLEAR_ERROR();
3859 // See if the declaration is followed by a "!important" declaration
3860 PRBool isImportant = PR_FALSE;
3861 if (!GetToken(PR_TRUE)) {
3862 // EOF is a perfectly good way to end a declaration and declaration block
3863 TransferTempData(aDeclaration, propID, isImportant,
3864 aMustCallValueAppended, aChanged);
3865 return PR_TRUE;
3868 if (eCSSToken_Symbol == tk->mType && '!' == tk->mSymbol) {
3869 // Look for important ident
3870 if (!GetToken(PR_TRUE)) {
3871 // Premature eof is not ok
3872 REPORT_UNEXPECTED_EOF(PEImportantEOF);
3873 ClearTempData(propID);
3874 return PR_FALSE;
3876 if ((eCSSToken_Ident != tk->mType) ||
3877 !tk->mIdent.LowerCaseEqualsLiteral("important")) {
3878 REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
3879 OUTPUT_ERROR();
3880 UngetToken();
3881 ClearTempData(propID);
3882 return PR_FALSE;
3884 isImportant = PR_TRUE;
3886 else {
3887 // Not a !important declaration
3888 UngetToken();
3891 // Make sure valid property declaration is terminated with either a
3892 // semicolon, EOF or a right-curly-brace (this last only when
3893 // aCheckForBraces is true).
3894 if (!GetToken(PR_TRUE)) {
3895 // EOF is a perfectly good way to end a declaration and declaration block
3896 TransferTempData(aDeclaration, propID, isImportant,
3897 aMustCallValueAppended, aChanged);
3898 return PR_TRUE;
3900 if (eCSSToken_Symbol == tk->mType) {
3901 if (';' == tk->mSymbol) {
3902 TransferTempData(aDeclaration, propID, isImportant,
3903 aMustCallValueAppended, aChanged);
3904 return PR_TRUE;
3906 if (aCheckForBraces && '}' == tk->mSymbol) {
3907 // Unget the '}' so we'll be able to tell that this is the end
3908 // of the declaration block when we unwind from here.
3909 UngetToken();
3910 TransferTempData(aDeclaration, propID, isImportant,
3911 aMustCallValueAppended, aChanged);
3912 return PR_TRUE;
3915 if (aCheckForBraces)
3916 REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2);
3917 else
3918 REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
3919 REPORT_UNEXPECTED(PEDeclDropped);
3920 OUTPUT_ERROR();
3921 ClearTempData(propID);
3922 return PR_FALSE;
3925 void
3926 CSSParserImpl::ClearTempData(nsCSSProperty aPropID)
3928 if (nsCSSProps::IsShorthand(aPropID)) {
3929 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
3930 mTempData.ClearProperty(*p);
3932 } else {
3933 mTempData.ClearProperty(aPropID);
3935 mTempData.AssertInitialState();
3938 void
3939 CSSParserImpl::TransferTempData(nsCSSDeclaration* aDeclaration,
3940 nsCSSProperty aPropID, PRBool aIsImportant,
3941 PRBool aMustCallValueAppended,
3942 PRBool* aChanged)
3944 if (nsCSSProps::IsShorthand(aPropID)) {
3945 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
3946 DoTransferTempData(aDeclaration, *p, aIsImportant,
3947 aMustCallValueAppended, aChanged);
3949 } else {
3950 DoTransferTempData(aDeclaration, aPropID, aIsImportant,
3951 aMustCallValueAppended, aChanged);
3953 mTempData.AssertInitialState();
3956 // Perhaps the transferring code should be in nsCSSExpandedDataBlock, in
3957 // case some other caller wants to use it in the future (although I
3958 // can't think of why).
3959 void
3960 CSSParserImpl::DoTransferTempData(nsCSSDeclaration* aDeclaration,
3961 nsCSSProperty aPropID, PRBool aIsImportant,
3962 PRBool aMustCallValueAppended,
3963 PRBool* aChanged)
3965 NS_ASSERTION(mTempData.HasPropertyBit(aPropID), "oops");
3966 if (aIsImportant) {
3967 if (!mData.HasImportantBit(aPropID))
3968 *aChanged = PR_TRUE;
3969 mData.SetImportantBit(aPropID);
3970 } else {
3971 if (mData.HasImportantBit(aPropID)) {
3972 mTempData.ClearProperty(aPropID);
3973 return;
3977 if (aMustCallValueAppended || !mData.HasPropertyBit(aPropID)) {
3978 aDeclaration->ValueAppended(aPropID);
3981 mData.SetPropertyBit(aPropID);
3982 mTempData.ClearPropertyBit(aPropID);
3985 * Save needless copying and allocation by calling the destructor in
3986 * the destination, copying memory directly, and then using placement
3987 * new.
3989 void *v_source = mTempData.PropertyAt(aPropID);
3990 void *v_dest = mData.PropertyAt(aPropID);
3991 switch (nsCSSProps::kTypeTable[aPropID]) {
3992 case eCSSType_Value: {
3993 nsCSSValue *source = static_cast<nsCSSValue*>(v_source);
3994 nsCSSValue *dest = static_cast<nsCSSValue*>(v_dest);
3995 if (*source != *dest)
3996 *aChanged = PR_TRUE;
3997 dest->~nsCSSValue();
3998 memcpy(dest, source, sizeof(nsCSSValue));
3999 new (source) nsCSSValue();
4000 } break;
4002 case eCSSType_Rect: {
4003 nsCSSRect *source = static_cast<nsCSSRect*>(v_source);
4004 nsCSSRect *dest = static_cast<nsCSSRect*>(v_dest);
4005 if (*source != *dest)
4006 *aChanged = PR_TRUE;
4007 dest->~nsCSSRect();
4008 memcpy(dest, source, sizeof(nsCSSRect));
4009 new (source) nsCSSRect();
4010 } break;
4012 case eCSSType_ValuePair: {
4013 nsCSSValuePair *source = static_cast<nsCSSValuePair*>(v_source);
4014 nsCSSValuePair *dest = static_cast<nsCSSValuePair*>(v_dest);
4015 if (*source != *dest)
4016 *aChanged = PR_TRUE;
4017 dest->~nsCSSValuePair();
4018 memcpy(dest, source, sizeof(nsCSSValuePair));
4019 new (source) nsCSSValuePair();
4020 } break;
4022 case eCSSType_ValueList: {
4023 nsCSSValueList **source = static_cast<nsCSSValueList**>(v_source);
4024 nsCSSValueList **dest = static_cast<nsCSSValueList**>(v_dest);
4025 if (!nsCSSValueList::Equal(*source, *dest))
4026 *aChanged = PR_TRUE;
4027 delete *dest;
4028 *dest = *source;
4029 *source = nsnull;
4030 } break;
4032 case eCSSType_ValuePairList: {
4033 nsCSSValuePairList **source =
4034 static_cast<nsCSSValuePairList**>(v_source);
4035 nsCSSValuePairList **dest =
4036 static_cast<nsCSSValuePairList**>(v_dest);
4037 if (!nsCSSValuePairList::Equal(*source, *dest))
4038 *aChanged = PR_TRUE;
4039 delete *dest;
4040 *dest = *source;
4041 *source = nsnull;
4042 } break;
4046 static const nsCSSProperty kBorderTopIDs[] = {
4047 eCSSProperty_border_top_width,
4048 eCSSProperty_border_top_style,
4049 eCSSProperty_border_top_color
4051 static const nsCSSProperty kBorderRightIDs[] = {
4052 eCSSProperty_border_right_width_value,
4053 eCSSProperty_border_right_style_value,
4054 eCSSProperty_border_right_color_value,
4055 eCSSProperty_border_right_width,
4056 eCSSProperty_border_right_style,
4057 eCSSProperty_border_right_color
4059 static const nsCSSProperty kBorderBottomIDs[] = {
4060 eCSSProperty_border_bottom_width,
4061 eCSSProperty_border_bottom_style,
4062 eCSSProperty_border_bottom_color
4064 static const nsCSSProperty kBorderLeftIDs[] = {
4065 eCSSProperty_border_left_width_value,
4066 eCSSProperty_border_left_style_value,
4067 eCSSProperty_border_left_color_value,
4068 eCSSProperty_border_left_width,
4069 eCSSProperty_border_left_style,
4070 eCSSProperty_border_left_color
4072 static const nsCSSProperty kBorderStartIDs[] = {
4073 eCSSProperty_border_start_width_value,
4074 eCSSProperty_border_start_style_value,
4075 eCSSProperty_border_start_color_value,
4076 eCSSProperty_border_start_width,
4077 eCSSProperty_border_start_style,
4078 eCSSProperty_border_start_color
4080 static const nsCSSProperty kBorderEndIDs[] = {
4081 eCSSProperty_border_end_width_value,
4082 eCSSProperty_border_end_style_value,
4083 eCSSProperty_border_end_color_value,
4084 eCSSProperty_border_end_width,
4085 eCSSProperty_border_end_style,
4086 eCSSProperty_border_end_color
4088 static const nsCSSProperty kColumnRuleIDs[] = {
4089 eCSSProperty__moz_column_rule_width,
4090 eCSSProperty__moz_column_rule_style,
4091 eCSSProperty__moz_column_rule_color
4094 PRBool
4095 CSSParserImpl::ParseEnum(nsCSSValue& aValue,
4096 const PRInt32 aKeywordTable[])
4098 nsSubstring* ident = NextIdent();
4099 if (nsnull == ident) {
4100 return PR_FALSE;
4102 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
4103 if (eCSSKeyword_UNKNOWN < keyword) {
4104 PRInt32 value;
4105 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
4106 aValue.SetIntValue(value, eCSSUnit_Enumerated);
4107 return PR_TRUE;
4111 // Put the unknown identifier back and return
4112 UngetToken();
4113 return PR_FALSE;
4117 struct UnitInfo {
4118 char name[5]; // needs to be long enough for the longest unit, with
4119 // terminating null.
4120 PRUint32 length;
4121 nsCSSUnit unit;
4122 PRInt32 type;
4125 #define STR_WITH_LEN(_str) \
4126 _str, sizeof(_str) - 1
4128 const UnitInfo UnitData[] = {
4129 { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH },
4130 { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
4131 { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
4132 { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
4133 { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
4134 { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
4135 { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
4136 { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
4137 { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
4138 { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
4139 { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
4140 { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE },
4141 { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY },
4142 { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY },
4143 { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME },
4144 { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME }
4147 #undef STR_WITH_LEN
4149 PRBool
4150 CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
4151 PRInt32 aVariantMask,
4152 float aNumber,
4153 const nsString& aUnit)
4155 nsCSSUnit units;
4156 PRInt32 type = 0;
4157 if (!aUnit.IsEmpty()) {
4158 PRUint32 i;
4159 for (i = 0; i < NS_ARRAY_LENGTH(UnitData); ++i) {
4160 if (aUnit.LowerCaseEqualsASCII(UnitData[i].name,
4161 UnitData[i].length)) {
4162 units = UnitData[i].unit;
4163 type = UnitData[i].type;
4164 break;
4168 if (i == NS_ARRAY_LENGTH(UnitData)) {
4169 // Unknown unit
4170 return PR_FALSE;
4172 } else {
4173 // Must be a zero number...
4174 NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
4175 if ((VARIANT_LENGTH & aVariantMask) != 0) {
4176 units = eCSSUnit_Point;
4177 type = VARIANT_LENGTH;
4179 else if ((VARIANT_ANGLE & aVariantMask) != 0) {
4180 units = eCSSUnit_Degree;
4181 type = VARIANT_ANGLE;
4183 else if ((VARIANT_FREQUENCY & aVariantMask) != 0) {
4184 units = eCSSUnit_Hertz;
4185 type = VARIANT_FREQUENCY;
4187 else if ((VARIANT_TIME & aVariantMask) != 0) {
4188 units = eCSSUnit_Seconds;
4189 type = VARIANT_TIME;
4191 else {
4192 NS_ERROR("Variant mask does not include dimension; why were we called?");
4193 return PR_FALSE;
4196 if ((type & aVariantMask) != 0) {
4197 aValue.SetFloatValue(aNumber, units);
4198 return PR_TRUE;
4200 return PR_FALSE;
4203 PRBool
4204 CSSParserImpl::ParsePositiveVariant(nsCSSValue& aValue,
4205 PRInt32 aVariantMask,
4206 const PRInt32 aKeywordTable[])
4208 if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
4209 if (eCSSUnit_Number == aValue.GetUnit() ||
4210 aValue.IsLengthUnit()){
4211 if (aValue.GetFloatValue() < 0) {
4212 UngetToken();
4213 return PR_FALSE;
4216 else if (aValue.GetUnit() == eCSSUnit_Percent) {
4217 if (aValue.GetPercentValue() < 0) {
4218 UngetToken();
4219 return PR_FALSE;
4221 } else if (aValue.GetUnit() == eCSSUnit_Integer) {
4222 if (aValue.GetIntValue() < 0) {
4223 UngetToken();
4224 return PR_FALSE;
4227 return PR_TRUE;
4229 return PR_FALSE;
4232 // Assigns to aValue iff it returns PR_TRUE.
4233 PRBool
4234 CSSParserImpl::ParseVariant(nsCSSValue& aValue,
4235 PRInt32 aVariantMask,
4236 const PRInt32 aKeywordTable[])
4238 NS_ASSERTION(IsParsingCompoundProperty() ||
4239 ((~aVariantMask) & (VARIANT_LENGTH|VARIANT_COLOR)),
4240 "cannot distinguish lengths and colors in quirks mode");
4242 if (!GetToken(PR_TRUE)) {
4243 return PR_FALSE;
4245 nsCSSToken* tk = &mToken;
4246 if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE)) != 0) &&
4247 (eCSSToken_Ident == tk->mType)) {
4248 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
4249 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
4250 if ((aVariantMask & VARIANT_AUTO) != 0) {
4251 if (eCSSKeyword_auto == keyword) {
4252 aValue.SetAutoValue();
4253 return PR_TRUE;
4256 if ((aVariantMask & VARIANT_INHERIT) != 0) {
4257 // XXX Should we check IsParsingCompoundProperty, or do all
4258 // callers handle it? (Not all callers set it, though, since
4259 // they want the quirks that are disabled by setting it.)
4260 if (eCSSKeyword_inherit == keyword) {
4261 aValue.SetInheritValue();
4262 return PR_TRUE;
4264 else if (eCSSKeyword__moz_initial == keyword) { // anything that can inherit can also take an initial val.
4265 aValue.SetInitialValue();
4266 return PR_TRUE;
4269 if ((aVariantMask & VARIANT_NONE) != 0) {
4270 if (eCSSKeyword_none == keyword) {
4271 aValue.SetNoneValue();
4272 return PR_TRUE;
4275 if ((aVariantMask & VARIANT_NORMAL) != 0) {
4276 if (eCSSKeyword_normal == keyword) {
4277 aValue.SetNormalValue();
4278 return PR_TRUE;
4281 if ((aVariantMask & VARIANT_SYSFONT) != 0) {
4282 if (eCSSKeyword__moz_use_system_font == keyword &&
4283 !IsParsingCompoundProperty()) {
4284 aValue.SetSystemFontValue();
4285 return PR_TRUE;
4288 if ((aVariantMask & VARIANT_KEYWORD) != 0) {
4289 PRInt32 value;
4290 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
4291 aValue.SetIntValue(value, eCSSUnit_Enumerated);
4292 return PR_TRUE;
4297 if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE | VARIANT_FREQUENCY | VARIANT_TIME)) != 0) &&
4298 tk->IsDimension()) {
4299 if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
4300 return PR_TRUE;
4302 // Put the token back; we didn't parse it, so we shouldn't consume it
4303 UngetToken();
4304 return PR_FALSE;
4306 if (((aVariantMask & VARIANT_PERCENT) != 0) &&
4307 (eCSSToken_Percentage == tk->mType)) {
4308 aValue.SetPercentValue(tk->mNumber);
4309 return PR_TRUE;
4311 if (((aVariantMask & VARIANT_NUMBER) != 0) &&
4312 (eCSSToken_Number == tk->mType)) {
4313 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
4314 return PR_TRUE;
4316 if (((aVariantMask & VARIANT_INTEGER) != 0) &&
4317 (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
4318 aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
4319 return PR_TRUE;
4321 if (mNavQuirkMode && !IsParsingCompoundProperty()) { // NONSTANDARD: Nav interprets unitless numbers as px
4322 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
4323 (eCSSToken_Number == tk->mType)) {
4324 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
4325 return PR_TRUE;
4329 #ifdef MOZ_SVG
4330 if (IsSVGMode() && !IsParsingCompoundProperty()) {
4331 // STANDARD: SVG Spec states that lengths and coordinates can be unitless
4332 // in which case they default to user-units (1 px = 1 user unit)
4333 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
4334 (eCSSToken_Number == tk->mType)) {
4335 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
4336 return PR_TRUE;
4339 #endif
4341 if (((aVariantMask & VARIANT_URL) != 0) &&
4342 (eCSSToken_Function == tk->mType) &&
4343 tk->mIdent.LowerCaseEqualsLiteral("url")) {
4344 if (ParseURL(aValue)) {
4345 return PR_TRUE;
4347 return PR_FALSE;
4349 if ((aVariantMask & VARIANT_COLOR) != 0) {
4350 if ((mNavQuirkMode && !IsParsingCompoundProperty()) || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
4351 (eCSSToken_ID == tk->mType) ||
4352 (eCSSToken_Ref == tk->mType) ||
4353 (eCSSToken_Ident == tk->mType) ||
4354 ((eCSSToken_Function == tk->mType) &&
4355 (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
4356 tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
4357 tk->mIdent.LowerCaseEqualsLiteral("-moz-rgba") ||
4358 tk->mIdent.LowerCaseEqualsLiteral("-moz-hsla") ||
4359 tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
4360 tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
4362 // Put token back so that parse color can get it
4363 UngetToken();
4364 if (ParseColor(aValue)) {
4365 return PR_TRUE;
4367 return PR_FALSE;
4370 if (((aVariantMask & VARIANT_STRING) != 0) &&
4371 (eCSSToken_String == tk->mType)) {
4372 nsAutoString buffer;
4373 buffer.Append(tk->mSymbol);
4374 buffer.Append(tk->mIdent);
4375 buffer.Append(tk->mSymbol);
4376 aValue.SetStringValue(buffer, eCSSUnit_String);
4377 return PR_TRUE;
4379 if (((aVariantMask & VARIANT_IDENTIFIER) != 0) &&
4380 (eCSSToken_Ident == tk->mType)) {
4381 aValue.SetStringValue(tk->mIdent, eCSSUnit_String);
4382 return PR_TRUE;
4384 if (((aVariantMask & VARIANT_COUNTER) != 0) &&
4385 (eCSSToken_Function == tk->mType) &&
4386 (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
4387 tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
4388 return ParseCounter(aValue);
4390 if (((aVariantMask & VARIANT_ATTR) != 0) &&
4391 (eCSSToken_Function == tk->mType) &&
4392 tk->mIdent.LowerCaseEqualsLiteral("attr")) {
4393 return ParseAttr(aValue);
4396 UngetToken();
4397 return PR_FALSE;
4401 PRBool
4402 CSSParserImpl::ParseCounter(nsCSSValue& aValue)
4404 nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
4405 eCSSUnit_Counter : eCSSUnit_Counters);
4407 if (!ExpectSymbol('(', PR_FALSE))
4408 return PR_FALSE;
4410 if (!GetNonCloseParenToken(PR_TRUE) ||
4411 eCSSToken_Ident != mToken.mType) {
4412 SkipUntil(')');
4413 return PR_FALSE;
4416 nsRefPtr<nsCSSValue::Array> val =
4417 nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
4418 if (!val) {
4419 mScanner.SetLowLevelError(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(',', PR_TRUE) ||
4428 !(GetNonCloseParenToken(PR_TRUE) &&
4429 eCSSToken_String == mToken.mType)) {
4430 SkipUntil(')');
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(',', PR_TRUE)) {
4439 nsCSSKeyword keyword;
4440 PRBool success = GetNonCloseParenToken(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(')');
4454 return PR_FALSE;
4457 PRInt32 typeItem = eCSSUnit_Counters == unit ? 2 : 1;
4458 val->Item(typeItem).SetIntValue(type, eCSSUnit_Enumerated);
4460 if (!ExpectSymbol(')', PR_TRUE)) {
4461 SkipUntil(')');
4462 return PR_FALSE;
4465 aValue.SetArrayValue(val, unit);
4466 return PR_TRUE;
4469 PRBool
4470 CSSParserImpl::ParseAttr(nsCSSValue& aValue)
4472 if (ExpectSymbol('(', PR_FALSE)) {
4473 if (GetToken(PR_TRUE)) {
4474 nsAutoString attr;
4475 if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
4476 nsAutoString holdIdent(mToken.mIdent);
4477 if (ExpectSymbol('|', PR_FALSE)) { // namespace
4478 PRInt32 nameSpaceID;
4479 if (!GetNamespaceIdForPrefix(holdIdent, &nameSpaceID)) {
4480 return PR_FALSE;
4482 attr.AppendInt(nameSpaceID, 10);
4483 attr.Append(PRUnichar('|'));
4484 if (! GetToken(PR_FALSE)) {
4485 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4486 return PR_FALSE;
4488 if (eCSSToken_Ident == mToken.mType) {
4489 if (mCaseSensitive) {
4490 attr.Append(mToken.mIdent);
4491 } else {
4492 nsAutoString buffer;
4493 ToLowerCase(mToken.mIdent, buffer);
4494 attr.Append(buffer);
4497 else {
4498 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4499 UngetToken();
4500 return PR_FALSE;
4503 else { // no namespace
4504 if (mCaseSensitive) {
4505 attr = holdIdent;
4507 else {
4508 ToLowerCase(holdIdent, attr);
4512 else if (mToken.IsSymbol('*')) { // namespace wildcard
4513 // Wildcard namespace makes no sense here and is not allowed
4514 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4515 UngetToken();
4516 return PR_FALSE;
4518 else if (mToken.IsSymbol('|')) { // explicit NO namespace
4519 if (! GetToken(PR_FALSE)) {
4520 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4521 return PR_FALSE;
4523 if (eCSSToken_Ident == mToken.mType) {
4524 if (mCaseSensitive) {
4525 attr.Append(mToken.mIdent);
4526 } else {
4527 nsAutoString buffer;
4528 ToLowerCase(mToken.mIdent, buffer);
4529 attr.Append(buffer);
4532 else {
4533 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4534 UngetToken();
4535 return PR_FALSE;
4538 else {
4539 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
4540 UngetToken();
4541 return PR_FALSE;
4543 if (ExpectSymbol(')', PR_TRUE)) {
4544 aValue.SetStringValue(attr, eCSSUnit_Attr);
4545 return PR_TRUE;
4549 return PR_FALSE;
4552 PRBool
4553 CSSParserImpl::ParseURL(nsCSSValue& aValue)
4555 if (!mSheetPrincipal) {
4556 NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
4557 "origin principal");
4558 return PR_FALSE;
4561 if (!ExpectSymbol('(', PR_FALSE))
4562 return PR_FALSE;
4563 if (!GetURLToken())
4564 return PR_FALSE;
4566 nsCSSToken* tk = &mToken;
4567 if (eCSSToken_String != tk->mType && eCSSToken_URL != tk->mType)
4568 return PR_FALSE;
4570 nsString url = tk->mIdent;
4571 if (!ExpectSymbol(')', PR_TRUE))
4572 return PR_FALSE;
4574 // Translate url into an absolute url if the url is relative to the
4575 // style sheet.
4576 nsCOMPtr<nsIURI> uri;
4577 NS_NewURI(getter_AddRefs(uri), url, nsnull, mBaseURL);
4579 nsStringBuffer* buffer = nsCSSValue::BufferFromString(url);
4580 if (NS_UNLIKELY(!buffer)) {
4581 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
4582 return PR_FALSE;
4584 nsCSSValue::URL *urlVal =
4585 new nsCSSValue::URL(uri, buffer, mSheetURL, mSheetPrincipal);
4587 buffer->Release();
4588 if (NS_UNLIKELY(!urlVal)) {
4589 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
4590 return PR_FALSE;
4592 aValue.SetURLValue(urlVal);
4593 return PR_TRUE;
4596 PRInt32
4597 CSSParserImpl::ParseChoice(nsCSSValue aValues[],
4598 const nsCSSProperty aPropIDs[], PRInt32 aNumIDs)
4600 PRInt32 found = 0;
4601 nsAutoParseCompoundProperty compound(this);
4603 PRInt32 loop;
4604 for (loop = 0; loop < aNumIDs; loop++) {
4605 // Try each property parser in order
4606 PRInt32 hadFound = found;
4607 PRInt32 index;
4608 for (index = 0; index < aNumIDs; index++) {
4609 PRInt32 bit = 1 << index;
4610 if ((found & bit) == 0) {
4611 if (ParseSingleValueProperty(aValues[index], aPropIDs[index])) {
4612 found |= bit;
4616 if (found == hadFound) { // found nothing new
4617 break;
4620 if (0 < found) {
4621 if (1 == found) { // only first property
4622 if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
4623 for (loop = 1; loop < aNumIDs; loop++) {
4624 aValues[loop].SetInheritValue();
4626 found = ((1 << aNumIDs) - 1);
4628 else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
4629 for (loop = 1; loop < aNumIDs; loop++) {
4630 aValues[loop].SetInitialValue();
4632 found = ((1 << aNumIDs) - 1);
4635 else { // more than one value, verify no inherits or initials
4636 for (loop = 0; loop < aNumIDs; loop++) {
4637 if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
4638 found = -1;
4639 break;
4641 else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
4642 found = -1;
4643 break;
4648 return found;
4651 void
4652 CSSParserImpl::AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue)
4654 NS_ASSERTION(0 <= aPropID && aPropID < eCSSProperty_COUNT_no_shorthands,
4655 "property out of range");
4656 NS_ASSERTION(nsCSSProps::kTypeTable[aPropID] == eCSSType_Value,
4657 nsPrintfCString(64, "type error (property=\'%s\')",
4658 nsCSSProps::GetStringValue(aPropID).get()).get());
4659 nsCSSValue& storage =
4660 *static_cast<nsCSSValue*>(mTempData.PropertyAt(aPropID));
4661 storage = aValue;
4662 mTempData.SetPropertyBit(aPropID);
4666 * Parse a "box" property. Box properties have 1 to 4 values. When less
4667 * than 4 values are provided a standard mapping is used to replicate
4668 * existing values.
4670 PRBool
4671 CSSParserImpl::ParseBoxProperties(nsCSSRect& aResult,
4672 const nsCSSProperty aPropIDs[])
4674 // Get up to four values for the property
4675 PRInt32 count = 0;
4676 PRInt32 index;
4677 nsCSSRect result;
4678 for (index = 0; index < 4; index++) {
4679 if (! ParseSingleValueProperty(result.*(nsCSSRect::sides[index]),
4680 aPropIDs[index])) {
4681 break;
4683 count++;
4685 if ((count == 0) || (PR_FALSE == ExpectEndProperty())) {
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
4716 CSSParserImpl::ParseDirectionalBoxProperty(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(value, subprops[0]) ||
4724 !ExpectEndProperty())
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 return PR_TRUE;
4734 PRBool
4735 CSSParserImpl::ParseProperty(nsCSSProperty aPropID)
4737 NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range");
4739 switch (aPropID) { // handle shorthand or multiple properties
4740 case eCSSProperty_background:
4741 return ParseBackground();
4742 case eCSSProperty_background_position:
4743 return ParseBackgroundPosition();
4744 case eCSSProperty_border:
4745 return ParseBorderSide(kBorderTopIDs, PR_TRUE);
4746 case eCSSProperty_border_color:
4747 return ParseBorderColor();
4748 case eCSSProperty_border_spacing:
4749 return ParseBorderSpacing();
4750 case eCSSProperty_border_style:
4751 return ParseBorderStyle();
4752 case eCSSProperty_border_bottom:
4753 return ParseBorderSide(kBorderBottomIDs, PR_FALSE);
4754 case eCSSProperty_border_end:
4755 return ParseDirectionalBorderSide(kBorderEndIDs,
4756 NS_BOXPROP_SOURCE_LOGICAL);
4757 case eCSSProperty_border_left:
4758 return ParseDirectionalBorderSide(kBorderLeftIDs,
4759 NS_BOXPROP_SOURCE_PHYSICAL);
4760 case eCSSProperty_border_right:
4761 return ParseDirectionalBorderSide(kBorderRightIDs,
4762 NS_BOXPROP_SOURCE_PHYSICAL);
4763 case eCSSProperty_border_start:
4764 return ParseDirectionalBorderSide(kBorderStartIDs,
4765 NS_BOXPROP_SOURCE_LOGICAL);
4766 case eCSSProperty_border_top:
4767 return ParseBorderSide(kBorderTopIDs, PR_FALSE);
4768 case eCSSProperty_border_bottom_colors:
4769 return ParseBorderColors(&mTempData.mMargin.mBorderColors.mBottom,
4770 aPropID);
4771 case eCSSProperty_border_left_colors:
4772 return ParseBorderColors(&mTempData.mMargin.mBorderColors.mLeft,
4773 aPropID);
4774 case eCSSProperty_border_right_colors:
4775 return ParseBorderColors(&mTempData.mMargin.mBorderColors.mRight,
4776 aPropID);
4777 case eCSSProperty_border_top_colors:
4778 return ParseBorderColors(&mTempData.mMargin.mBorderColors.mTop,
4779 aPropID);
4780 case eCSSProperty_border_image:
4781 return ParseBorderImage();
4782 case eCSSProperty_border_width:
4783 return ParseBorderWidth();
4784 case eCSSProperty_border_end_color:
4785 return ParseDirectionalBoxProperty(eCSSProperty_border_end_color,
4786 NS_BOXPROP_SOURCE_LOGICAL);
4787 case eCSSProperty_border_left_color:
4788 return ParseDirectionalBoxProperty(eCSSProperty_border_left_color,
4789 NS_BOXPROP_SOURCE_PHYSICAL);
4790 case eCSSProperty_border_right_color:
4791 return ParseDirectionalBoxProperty(eCSSProperty_border_right_color,
4792 NS_BOXPROP_SOURCE_PHYSICAL);
4793 case eCSSProperty_border_start_color:
4794 return ParseDirectionalBoxProperty(eCSSProperty_border_start_color,
4795 NS_BOXPROP_SOURCE_LOGICAL);
4796 case eCSSProperty_border_end_width:
4797 return ParseDirectionalBoxProperty(eCSSProperty_border_end_width,
4798 NS_BOXPROP_SOURCE_LOGICAL);
4799 case eCSSProperty_border_left_width:
4800 return ParseDirectionalBoxProperty(eCSSProperty_border_left_width,
4801 NS_BOXPROP_SOURCE_PHYSICAL);
4802 case eCSSProperty_border_right_width:
4803 return ParseDirectionalBoxProperty(eCSSProperty_border_right_width,
4804 NS_BOXPROP_SOURCE_PHYSICAL);
4805 case eCSSProperty_border_start_width:
4806 return ParseDirectionalBoxProperty(eCSSProperty_border_start_width,
4807 NS_BOXPROP_SOURCE_LOGICAL);
4808 case eCSSProperty_border_end_style:
4809 return ParseDirectionalBoxProperty(eCSSProperty_border_end_style,
4810 NS_BOXPROP_SOURCE_LOGICAL);
4811 case eCSSProperty_border_left_style:
4812 return ParseDirectionalBoxProperty(eCSSProperty_border_left_style,
4813 NS_BOXPROP_SOURCE_PHYSICAL);
4814 case eCSSProperty_border_right_style:
4815 return ParseDirectionalBoxProperty(eCSSProperty_border_right_style,
4816 NS_BOXPROP_SOURCE_PHYSICAL);
4817 case eCSSProperty_border_start_style:
4818 return ParseDirectionalBoxProperty(eCSSProperty_border_start_style,
4819 NS_BOXPROP_SOURCE_LOGICAL);
4820 case eCSSProperty__moz_border_radius:
4821 return ParseBorderRadius();
4822 case eCSSProperty__moz_outline_radius:
4823 return ParseOutlineRadius();
4824 case eCSSProperty_box_shadow:
4825 return ParseBoxShadow();
4826 case eCSSProperty_clip:
4827 return ParseRect(mTempData.mDisplay.mClip, eCSSProperty_clip);
4828 case eCSSProperty__moz_column_rule:
4829 return ParseBorderSide(kColumnRuleIDs, PR_FALSE);
4830 case eCSSProperty_content:
4831 return ParseContent();
4832 case eCSSProperty_counter_increment:
4833 return ParseCounterData(&mTempData.mContent.mCounterIncrement,
4834 aPropID);
4835 case eCSSProperty_counter_reset:
4836 return ParseCounterData(&mTempData.mContent.mCounterReset,
4837 aPropID);
4838 case eCSSProperty_cue:
4839 return ParseCue();
4840 case eCSSProperty_cursor:
4841 return ParseCursor();
4842 case eCSSProperty_font:
4843 return ParseFont();
4844 case eCSSProperty_image_region:
4845 return ParseRect(mTempData.mList.mImageRegion,
4846 eCSSProperty_image_region);
4847 case eCSSProperty_list_style:
4848 return ParseListStyle();
4849 case eCSSProperty_margin:
4850 return ParseMargin();
4851 case eCSSProperty_margin_end:
4852 return ParseDirectionalBoxProperty(eCSSProperty_margin_end,
4853 NS_BOXPROP_SOURCE_LOGICAL);
4854 case eCSSProperty_margin_left:
4855 return ParseDirectionalBoxProperty(eCSSProperty_margin_left,
4856 NS_BOXPROP_SOURCE_PHYSICAL);
4857 case eCSSProperty_margin_right:
4858 return ParseDirectionalBoxProperty(eCSSProperty_margin_right,
4859 NS_BOXPROP_SOURCE_PHYSICAL);
4860 case eCSSProperty_margin_start:
4861 return ParseDirectionalBoxProperty(eCSSProperty_margin_start,
4862 NS_BOXPROP_SOURCE_LOGICAL);
4863 case eCSSProperty_outline:
4864 return ParseOutline();
4865 case eCSSProperty_overflow:
4866 return ParseOverflow();
4867 case eCSSProperty_padding:
4868 return ParsePadding();
4869 case eCSSProperty_padding_end:
4870 return ParseDirectionalBoxProperty(eCSSProperty_padding_end,
4871 NS_BOXPROP_SOURCE_LOGICAL);
4872 case eCSSProperty_padding_left:
4873 return ParseDirectionalBoxProperty(eCSSProperty_padding_left,
4874 NS_BOXPROP_SOURCE_PHYSICAL);
4875 case eCSSProperty_padding_right:
4876 return ParseDirectionalBoxProperty(eCSSProperty_padding_right,
4877 NS_BOXPROP_SOURCE_PHYSICAL);
4878 case eCSSProperty_padding_start:
4879 return ParseDirectionalBoxProperty(eCSSProperty_padding_start,
4880 NS_BOXPROP_SOURCE_LOGICAL);
4881 case eCSSProperty_pause:
4882 return ParsePause();
4883 case eCSSProperty_quotes:
4884 return ParseQuotes();
4885 case eCSSProperty_size:
4886 return ParseSize();
4887 case eCSSProperty_text_shadow:
4888 return ParseTextShadow();
4889 case eCSSProperty__moz_transform:
4890 return ParseMozTransform();
4891 case eCSSProperty__moz_transform_origin:
4892 return ParseMozTransformOrigin();
4894 #ifdef MOZ_SVG
4895 case eCSSProperty_fill:
4896 return ParsePaint(&mTempData.mSVG.mFill, eCSSProperty_fill);
4897 case eCSSProperty_stroke:
4898 return ParsePaint(&mTempData.mSVG.mStroke, eCSSProperty_stroke);
4899 case eCSSProperty_stroke_dasharray:
4900 return ParseDasharray();
4901 case eCSSProperty_marker:
4902 return ParseMarker();
4903 #endif
4905 // Strip out properties we use internally.
4906 case eCSSProperty__x_system_font:
4907 case eCSSProperty_margin_end_value:
4908 case eCSSProperty_margin_left_value:
4909 case eCSSProperty_margin_right_value:
4910 case eCSSProperty_margin_start_value:
4911 case eCSSProperty_margin_left_ltr_source:
4912 case eCSSProperty_margin_left_rtl_source:
4913 case eCSSProperty_margin_right_ltr_source:
4914 case eCSSProperty_margin_right_rtl_source:
4915 case eCSSProperty_padding_end_value:
4916 case eCSSProperty_padding_left_value:
4917 case eCSSProperty_padding_right_value:
4918 case eCSSProperty_padding_start_value:
4919 case eCSSProperty_padding_left_ltr_source:
4920 case eCSSProperty_padding_left_rtl_source:
4921 case eCSSProperty_padding_right_ltr_source:
4922 case eCSSProperty_padding_right_rtl_source:
4923 case eCSSProperty_border_end_color_value:
4924 case eCSSProperty_border_left_color_value:
4925 case eCSSProperty_border_right_color_value:
4926 case eCSSProperty_border_start_color_value:
4927 case eCSSProperty_border_left_color_ltr_source:
4928 case eCSSProperty_border_left_color_rtl_source:
4929 case eCSSProperty_border_right_color_ltr_source:
4930 case eCSSProperty_border_right_color_rtl_source:
4931 case eCSSProperty_border_end_style_value:
4932 case eCSSProperty_border_left_style_value:
4933 case eCSSProperty_border_right_style_value:
4934 case eCSSProperty_border_start_style_value:
4935 case eCSSProperty_border_left_style_ltr_source:
4936 case eCSSProperty_border_left_style_rtl_source:
4937 case eCSSProperty_border_right_style_ltr_source:
4938 case eCSSProperty_border_right_style_rtl_source:
4939 case eCSSProperty_border_end_width_value:
4940 case eCSSProperty_border_left_width_value:
4941 case eCSSProperty_border_right_width_value:
4942 case eCSSProperty_border_start_width_value:
4943 case eCSSProperty_border_left_width_ltr_source:
4944 case eCSSProperty_border_left_width_rtl_source:
4945 case eCSSProperty_border_right_width_ltr_source:
4946 case eCSSProperty_border_right_width_rtl_source:
4947 // The user can't use these
4948 REPORT_UNEXPECTED(PEInaccessibleProperty2);
4949 return PR_FALSE;
4950 default: // must be single property
4952 nsCSSValue value;
4953 if (ParseSingleValueProperty(value, aPropID)) {
4954 if (ExpectEndProperty()) {
4955 AppendValue(aPropID, value);
4956 return PR_TRUE;
4958 // XXX Report errors?
4960 // XXX Report errors?
4963 return PR_FALSE;
4966 // Bits used in determining which background position info we have
4967 #define BG_CENTER NS_STYLE_BG_POSITION_CENTER
4968 #define BG_TOP NS_STYLE_BG_POSITION_TOP
4969 #define BG_BOTTOM NS_STYLE_BG_POSITION_BOTTOM
4970 #define BG_LEFT NS_STYLE_BG_POSITION_LEFT
4971 #define BG_RIGHT NS_STYLE_BG_POSITION_RIGHT
4972 #define BG_CTB (BG_CENTER | BG_TOP | BG_BOTTOM)
4973 #define BG_CLR (BG_CENTER | BG_LEFT | BG_RIGHT)
4975 PRBool
4976 CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
4977 nsCSSProperty aPropID)
4979 switch (aPropID) {
4980 case eCSSProperty_UNKNOWN:
4981 case eCSSProperty_background:
4982 case eCSSProperty_background_position:
4983 case eCSSProperty_border:
4984 case eCSSProperty_border_color:
4985 case eCSSProperty_border_bottom_colors:
4986 case eCSSProperty_border_image:
4987 case eCSSProperty_border_left_colors:
4988 case eCSSProperty_border_right_colors:
4989 case eCSSProperty_border_end_color:
4990 case eCSSProperty_border_left_color:
4991 case eCSSProperty_border_right_color:
4992 case eCSSProperty_border_start_color:
4993 case eCSSProperty_border_end_style:
4994 case eCSSProperty_border_left_style:
4995 case eCSSProperty_border_right_style:
4996 case eCSSProperty_border_start_style:
4997 case eCSSProperty_border_end_width:
4998 case eCSSProperty_border_left_width:
4999 case eCSSProperty_border_right_width:
5000 case eCSSProperty_border_start_width:
5001 case eCSSProperty_border_top_colors:
5002 case eCSSProperty_border_spacing:
5003 case eCSSProperty_border_style:
5004 case eCSSProperty_border_bottom:
5005 case eCSSProperty_border_end:
5006 case eCSSProperty_border_left:
5007 case eCSSProperty_border_right:
5008 case eCSSProperty_border_start:
5009 case eCSSProperty_border_top:
5010 case eCSSProperty_border_width:
5011 case eCSSProperty__moz_border_radius:
5012 case eCSSProperty_box_shadow:
5013 case eCSSProperty_clip:
5014 case eCSSProperty__moz_column_rule:
5015 case eCSSProperty_content:
5016 case eCSSProperty_counter_increment:
5017 case eCSSProperty_counter_reset:
5018 case eCSSProperty_cue:
5019 case eCSSProperty_cursor:
5020 case eCSSProperty_font:
5021 case eCSSProperty_image_region:
5022 case eCSSProperty_list_style:
5023 case eCSSProperty_margin:
5024 case eCSSProperty_margin_end:
5025 case eCSSProperty_margin_left:
5026 case eCSSProperty_margin_right:
5027 case eCSSProperty_margin_start:
5028 case eCSSProperty_outline:
5029 case eCSSProperty__moz_outline_radius:
5030 case eCSSProperty_overflow:
5031 case eCSSProperty_padding:
5032 case eCSSProperty_padding_end:
5033 case eCSSProperty_padding_left:
5034 case eCSSProperty_padding_right:
5035 case eCSSProperty_padding_start:
5036 case eCSSProperty_pause:
5037 case eCSSProperty_quotes:
5038 case eCSSProperty_size:
5039 case eCSSProperty_text_shadow:
5040 case eCSSProperty__moz_transform:
5041 case eCSSProperty__moz_transform_origin:
5042 case eCSSProperty_COUNT:
5043 #ifdef MOZ_SVG
5044 case eCSSProperty_fill:
5045 case eCSSProperty_stroke:
5046 case eCSSProperty_stroke_dasharray:
5047 case eCSSProperty_marker:
5048 #endif
5049 NS_ERROR("not a single value property");
5050 return PR_FALSE;
5052 case eCSSProperty__x_system_font:
5053 case eCSSProperty_margin_left_ltr_source:
5054 case eCSSProperty_margin_left_rtl_source:
5055 case eCSSProperty_margin_right_ltr_source:
5056 case eCSSProperty_margin_right_rtl_source:
5057 case eCSSProperty_padding_left_ltr_source:
5058 case eCSSProperty_padding_left_rtl_source:
5059 case eCSSProperty_padding_right_ltr_source:
5060 case eCSSProperty_padding_right_rtl_source:
5061 case eCSSProperty_border_left_color_ltr_source:
5062 case eCSSProperty_border_left_color_rtl_source:
5063 case eCSSProperty_border_right_color_ltr_source:
5064 case eCSSProperty_border_right_color_rtl_source:
5065 case eCSSProperty_border_left_style_ltr_source:
5066 case eCSSProperty_border_left_style_rtl_source:
5067 case eCSSProperty_border_right_style_ltr_source:
5068 case eCSSProperty_border_right_style_rtl_source:
5069 case eCSSProperty_border_left_width_ltr_source:
5070 case eCSSProperty_border_left_width_rtl_source:
5071 case eCSSProperty_border_right_width_ltr_source:
5072 case eCSSProperty_border_right_width_rtl_source:
5073 #ifdef MOZ_MATHML
5074 case eCSSProperty_script_size_multiplier:
5075 case eCSSProperty_script_min_size:
5076 #endif
5077 NS_ERROR("not currently parsed here");
5078 return PR_FALSE;
5080 case eCSSProperty_appearance:
5081 return ParseVariant(aValue, VARIANT_HK,
5082 nsCSSProps::kAppearanceKTable);
5083 case eCSSProperty_azimuth:
5084 return ParseAzimuth(aValue);
5085 case eCSSProperty_background_attachment:
5086 return ParseVariant(aValue, VARIANT_HK,
5087 nsCSSProps::kBackgroundAttachmentKTable);
5088 case eCSSProperty__moz_background_clip:
5089 return ParseVariant(aValue, VARIANT_HK,
5090 nsCSSProps::kBackgroundClipKTable);
5091 case eCSSProperty_background_color:
5092 return ParseVariant(aValue, VARIANT_HC, nsnull);
5093 case eCSSProperty_background_image:
5094 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5095 case eCSSProperty__moz_background_inline_policy:
5096 return ParseVariant(aValue, VARIANT_HK,
5097 nsCSSProps::kBackgroundInlinePolicyKTable);
5098 case eCSSProperty__moz_background_origin:
5099 return ParseVariant(aValue, VARIANT_HK,
5100 nsCSSProps::kBackgroundOriginKTable);
5101 case eCSSProperty_background_repeat:
5102 return ParseVariant(aValue, VARIANT_HK,
5103 nsCSSProps::kBackgroundRepeatKTable);
5104 case eCSSProperty_binding:
5105 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5106 case eCSSProperty_border_collapse:
5107 return ParseVariant(aValue, VARIANT_HK,
5108 nsCSSProps::kBorderCollapseKTable);
5109 case eCSSProperty_border_bottom_color:
5110 case eCSSProperty_border_end_color_value: // for internal use
5111 case eCSSProperty_border_left_color_value: // for internal use
5112 case eCSSProperty_border_right_color_value: // for internal use
5113 case eCSSProperty_border_start_color_value: // for internal use
5114 case eCSSProperty_border_top_color:
5115 case eCSSProperty__moz_column_rule_color:
5116 return ParseVariant(aValue, VARIANT_HCK,
5117 nsCSSProps::kBorderColorKTable);
5118 case eCSSProperty_border_bottom_style:
5119 case eCSSProperty_border_end_style_value: // for internal use
5120 case eCSSProperty_border_left_style_value: // for internal use
5121 case eCSSProperty_border_right_style_value: // for internal use
5122 case eCSSProperty_border_start_style_value: // for internal use
5123 case eCSSProperty_border_top_style:
5124 case eCSSProperty__moz_column_rule_style:
5125 return ParseVariant(aValue, VARIANT_HOK,
5126 nsCSSProps::kBorderStyleKTable);
5127 case eCSSProperty_border_bottom_width:
5128 case eCSSProperty_border_end_width_value: // for internal use
5129 case eCSSProperty_border_left_width_value: // for internal use
5130 case eCSSProperty_border_right_width_value: // for internal use
5131 case eCSSProperty_border_start_width_value: // for internal use
5132 case eCSSProperty_border_top_width:
5133 case eCSSProperty__moz_column_rule_width:
5134 return ParsePositiveVariant(aValue, VARIANT_HKL,
5135 nsCSSProps::kBorderWidthKTable);
5136 case eCSSProperty__moz_border_radius_topLeft:
5137 case eCSSProperty__moz_border_radius_topRight:
5138 case eCSSProperty__moz_border_radius_bottomRight:
5139 case eCSSProperty__moz_border_radius_bottomLeft:
5140 return ParsePositiveVariant(aValue, VARIANT_HLP, nsnull);
5141 case eCSSProperty__moz_column_count:
5142 return ParsePositiveVariant(aValue, VARIANT_AHI, nsnull);
5143 case eCSSProperty__moz_column_width:
5144 return ParsePositiveVariant(aValue, VARIANT_AHL, nsnull);
5145 case eCSSProperty__moz_column_gap:
5146 return ParsePositiveVariant(aValue, VARIANT_HL | VARIANT_NORMAL, nsnull);
5147 case eCSSProperty__moz_outline_radius_topLeft:
5148 case eCSSProperty__moz_outline_radius_topRight:
5149 case eCSSProperty__moz_outline_radius_bottomRight:
5150 case eCSSProperty__moz_outline_radius_bottomLeft:
5151 return ParsePositiveVariant(aValue, VARIANT_HLP, nsnull);
5152 case eCSSProperty_bottom:
5153 case eCSSProperty_top:
5154 case eCSSProperty_left:
5155 case eCSSProperty_right:
5156 return ParseVariant(aValue, VARIANT_AHLP, nsnull);
5157 case eCSSProperty_box_align:
5158 return ParseVariant(aValue, VARIANT_HK,
5159 nsCSSProps::kBoxAlignKTable);
5160 case eCSSProperty_box_direction:
5161 return ParseVariant(aValue, VARIANT_HK,
5162 nsCSSProps::kBoxDirectionKTable);
5163 case eCSSProperty_box_flex:
5164 return ParsePositiveVariant(aValue, VARIANT_HN, nsnull);
5165 case eCSSProperty_box_orient:
5166 return ParseVariant(aValue, VARIANT_HK,
5167 nsCSSProps::kBoxOrientKTable);
5168 case eCSSProperty_box_pack:
5169 return ParseVariant(aValue, VARIANT_HK,
5170 nsCSSProps::kBoxPackKTable);
5171 case eCSSProperty_box_ordinal_group:
5172 return ParseVariant(aValue, VARIANT_HI, nsnull);
5173 #ifdef MOZ_SVG
5174 case eCSSProperty_clip_path:
5175 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5176 case eCSSProperty_clip_rule:
5177 return ParseVariant(aValue, VARIANT_HK,
5178 nsCSSProps::kFillRuleKTable);
5179 case eCSSProperty_color_interpolation:
5180 case eCSSProperty_color_interpolation_filters:
5181 return ParseVariant(aValue, VARIANT_AHK,
5182 nsCSSProps::kColorInterpolationKTable);
5183 case eCSSProperty_dominant_baseline:
5184 return ParseVariant(aValue, VARIANT_AHK,
5185 nsCSSProps::kDominantBaselineKTable);
5186 case eCSSProperty_fill_opacity:
5187 return ParseVariant(aValue, VARIANT_HN,
5188 nsnull);
5189 case eCSSProperty_fill_rule:
5190 return ParseVariant(aValue, VARIANT_HK,
5191 nsCSSProps::kFillRuleKTable);
5192 case eCSSProperty_filter:
5193 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5194 case eCSSProperty_flood_color:
5195 return ParseVariant(aValue, VARIANT_HC, nsnull);
5196 case eCSSProperty_flood_opacity:
5197 return ParseVariant(aValue, VARIANT_HN, nsnull);
5198 case eCSSProperty_lighting_color:
5199 return ParseVariant(aValue, VARIANT_HC, nsnull);
5200 case eCSSProperty_marker_end:
5201 case eCSSProperty_marker_mid:
5202 case eCSSProperty_marker_start:
5203 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5204 case eCSSProperty_mask:
5205 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5206 case eCSSProperty_pointer_events:
5207 return ParseVariant(aValue, VARIANT_HOK,
5208 nsCSSProps::kPointerEventsKTable);
5209 case eCSSProperty_shape_rendering:
5210 return ParseVariant(aValue, VARIANT_AHK,
5211 nsCSSProps::kShapeRenderingKTable);
5212 case eCSSProperty_stop_color:
5213 return ParseVariant(aValue, VARIANT_HC,
5214 nsnull);
5215 case eCSSProperty_stop_opacity:
5216 return ParseVariant(aValue, VARIANT_HN,
5217 nsnull);
5218 case eCSSProperty_stroke_dashoffset:
5219 return ParseVariant(aValue, VARIANT_HLPN,
5220 nsnull);
5221 case eCSSProperty_stroke_linecap:
5222 return ParseVariant(aValue, VARIANT_HK,
5223 nsCSSProps::kStrokeLinecapKTable);
5224 case eCSSProperty_stroke_linejoin:
5225 return ParseVariant(aValue, VARIANT_HK,
5226 nsCSSProps::kStrokeLinejoinKTable);
5227 case eCSSProperty_stroke_miterlimit:
5228 return ParsePositiveVariant(aValue, VARIANT_HN,
5229 nsnull);
5230 case eCSSProperty_stroke_opacity:
5231 return ParseVariant(aValue, VARIANT_HN,
5232 nsnull);
5233 case eCSSProperty_stroke_width:
5234 return ParsePositiveVariant(aValue, VARIANT_HLPN,
5235 nsnull);
5236 case eCSSProperty_text_anchor:
5237 return ParseVariant(aValue, VARIANT_HK,
5238 nsCSSProps::kTextAnchorKTable);
5239 case eCSSProperty_text_rendering:
5240 return ParseVariant(aValue, VARIANT_AHK,
5241 nsCSSProps::kTextRenderingKTable);
5242 #endif
5243 case eCSSProperty_box_sizing:
5244 return ParseVariant(aValue, VARIANT_HK,
5245 nsCSSProps::kBoxSizingKTable);
5246 case eCSSProperty_height:
5247 return ParsePositiveVariant(aValue, VARIANT_AHLP, nsnull);
5248 case eCSSProperty_width:
5249 return ParsePositiveVariant(aValue, VARIANT_AHKLP,
5250 nsCSSProps::kWidthKTable);
5251 case eCSSProperty_force_broken_image_icon:
5252 return ParsePositiveVariant(aValue, VARIANT_HI, nsnull);
5253 case eCSSProperty_caption_side:
5254 return ParseVariant(aValue, VARIANT_HK,
5255 nsCSSProps::kCaptionSideKTable);
5256 case eCSSProperty_clear:
5257 return ParseVariant(aValue, VARIANT_HOK,
5258 nsCSSProps::kClearKTable);
5259 case eCSSProperty_color:
5260 return ParseVariant(aValue, VARIANT_HC, nsnull);
5261 case eCSSProperty_cue_after:
5262 case eCSSProperty_cue_before:
5263 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5264 case eCSSProperty_direction:
5265 return ParseVariant(aValue, VARIANT_HK,
5266 nsCSSProps::kDirectionKTable);
5267 case eCSSProperty_display:
5268 if (ParseVariant(aValue, VARIANT_HOK, nsCSSProps::kDisplayKTable)) {
5269 if (aValue.GetUnit() == eCSSUnit_Enumerated) {
5270 switch (aValue.GetIntValue()) {
5271 case NS_STYLE_DISPLAY_MARKER: // bug 2055
5272 case NS_STYLE_DISPLAY_RUN_IN: // bug 2056
5273 case NS_STYLE_DISPLAY_COMPACT: // bug 14983
5274 return PR_FALSE;
5277 return PR_TRUE;
5279 return PR_FALSE;
5280 case eCSSProperty_elevation:
5281 return ParseVariant(aValue, VARIANT_HK | VARIANT_ANGLE,
5282 nsCSSProps::kElevationKTable);
5283 case eCSSProperty_empty_cells:
5284 return ParseVariant(aValue, VARIANT_HK,
5285 nsCSSProps::kEmptyCellsKTable);
5286 case eCSSProperty_float:
5287 return ParseVariant(aValue, VARIANT_HOK,
5288 nsCSSProps::kFloatKTable);
5289 case eCSSProperty_float_edge:
5290 return ParseVariant(aValue, VARIANT_HK,
5291 nsCSSProps::kFloatEdgeKTable);
5292 case eCSSProperty_font_family:
5293 return ParseFamily(aValue);
5294 case eCSSProperty_font_size:
5295 return ParsePositiveVariant(aValue,
5296 VARIANT_HKLP | VARIANT_SYSFONT,
5297 nsCSSProps::kFontSizeKTable);
5298 case eCSSProperty_font_size_adjust:
5299 return ParseVariant(aValue, VARIANT_HON | VARIANT_SYSFONT,
5300 nsnull);
5301 case eCSSProperty_font_stretch:
5302 return ParseVariant(aValue, VARIANT_HMK | VARIANT_SYSFONT,
5303 nsCSSProps::kFontStretchKTable);
5304 case eCSSProperty_font_style:
5305 return ParseVariant(aValue, VARIANT_HMK | VARIANT_SYSFONT,
5306 nsCSSProps::kFontStyleKTable);
5307 case eCSSProperty_font_variant:
5308 return ParseVariant(aValue, VARIANT_HMK | VARIANT_SYSFONT,
5309 nsCSSProps::kFontVariantKTable);
5310 case eCSSProperty_font_weight:
5311 return ParseFontWeight(aValue);
5312 case eCSSProperty_ime_mode:
5313 return ParseVariant(aValue, VARIANT_AHK | VARIANT_NORMAL,
5314 nsCSSProps::kIMEModeKTable);
5315 case eCSSProperty_letter_spacing:
5316 case eCSSProperty_word_spacing:
5317 return ParseVariant(aValue, VARIANT_HL | VARIANT_NORMAL, nsnull);
5318 case eCSSProperty_line_height:
5319 return ParsePositiveVariant(aValue, VARIANT_HLPN | VARIANT_NORMAL | VARIANT_SYSFONT, nsnull);
5320 case eCSSProperty_list_style_image:
5321 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5322 case eCSSProperty_list_style_position:
5323 return ParseVariant(aValue, VARIANT_HK, nsCSSProps::kListStylePositionKTable);
5324 case eCSSProperty_list_style_type:
5325 return ParseVariant(aValue, VARIANT_HOK, nsCSSProps::kListStyleKTable);
5326 case eCSSProperty_margin_bottom:
5327 case eCSSProperty_margin_end_value: // for internal use
5328 case eCSSProperty_margin_left_value: // for internal use
5329 case eCSSProperty_margin_right_value: // for internal use
5330 case eCSSProperty_margin_start_value: // for internal use
5331 case eCSSProperty_margin_top:
5332 return ParseVariant(aValue, VARIANT_AHLP, nsnull);
5333 case eCSSProperty_marker_offset:
5334 return ParseVariant(aValue, VARIANT_AHL, nsnull);
5335 case eCSSProperty_marks:
5336 return ParseMarks(aValue);
5337 case eCSSProperty_max_height:
5338 return ParsePositiveVariant(aValue, VARIANT_HLPO, nsnull);
5339 case eCSSProperty_max_width:
5340 return ParsePositiveVariant(aValue, VARIANT_HKLPO,
5341 nsCSSProps::kWidthKTable);
5342 case eCSSProperty_min_height:
5343 return ParsePositiveVariant(aValue, VARIANT_HLP, nsnull);
5344 case eCSSProperty_min_width:
5345 return ParsePositiveVariant(aValue, VARIANT_HKLP,
5346 nsCSSProps::kWidthKTable);
5347 case eCSSProperty_opacity:
5348 return ParseVariant(aValue, VARIANT_HN, nsnull);
5349 case eCSSProperty_orphans:
5350 case eCSSProperty_widows:
5351 return ParseVariant(aValue, VARIANT_HI, nsnull);
5352 case eCSSProperty_outline_color:
5353 return ParseVariant(aValue, VARIANT_HCK,
5354 nsCSSProps::kOutlineColorKTable);
5355 case eCSSProperty_outline_style:
5356 return ParseVariant(aValue, VARIANT_HOK | VARIANT_AUTO,
5357 nsCSSProps::kOutlineStyleKTable);
5358 case eCSSProperty_outline_width:
5359 return ParsePositiveVariant(aValue, VARIANT_HKL,
5360 nsCSSProps::kBorderWidthKTable);
5361 case eCSSProperty_outline_offset:
5362 return ParseVariant(aValue, VARIANT_HL, nsnull);
5363 case eCSSProperty_overflow_x:
5364 case eCSSProperty_overflow_y:
5365 return ParseVariant(aValue, VARIANT_AHK,
5366 nsCSSProps::kOverflowSubKTable);
5367 case eCSSProperty_padding_bottom:
5368 case eCSSProperty_padding_end_value: // for internal use
5369 case eCSSProperty_padding_left_value: // for internal use
5370 case eCSSProperty_padding_right_value: // for internal use
5371 case eCSSProperty_padding_start_value: // for internal use
5372 case eCSSProperty_padding_top:
5373 return ParsePositiveVariant(aValue, VARIANT_HLP, nsnull);
5374 case eCSSProperty_page:
5375 return ParseVariant(aValue, VARIANT_AUTO | VARIANT_IDENTIFIER, nsnull);
5376 case eCSSProperty_page_break_after:
5377 case eCSSProperty_page_break_before:
5378 return ParseVariant(aValue, VARIANT_AHK,
5379 nsCSSProps::kPageBreakKTable);
5380 case eCSSProperty_page_break_inside:
5381 return ParseVariant(aValue, VARIANT_AHK,
5382 nsCSSProps::kPageBreakInsideKTable);
5383 case eCSSProperty_pause_after:
5384 case eCSSProperty_pause_before:
5385 return ParseVariant(aValue, VARIANT_HTP, nsnull);
5386 case eCSSProperty_pitch:
5387 return ParseVariant(aValue, VARIANT_HKF, nsCSSProps::kPitchKTable);
5388 case eCSSProperty_pitch_range:
5389 return ParseVariant(aValue, VARIANT_HN, nsnull);
5390 case eCSSProperty_position:
5391 return ParseVariant(aValue, VARIANT_HK, nsCSSProps::kPositionKTable);
5392 case eCSSProperty_richness:
5393 return ParseVariant(aValue, VARIANT_HN, nsnull);
5394 #ifdef MOZ_MATHML
5395 // script-level can take Integer or Number values, but only Integer ("relative")
5396 // values can be specified in a style sheet. Also we only allow this property
5397 // when unsafe rules are enabled, because otherwise it could interfere
5398 // with rulenode optimizations if used in a non-MathML-enabled document.
5399 case eCSSProperty_script_level:
5400 if (!mUnsafeRulesEnabled)
5401 return PR_FALSE;
5402 return ParseVariant(aValue, VARIANT_HI, nsnull);
5403 #endif
5404 case eCSSProperty_speak:
5405 return ParseVariant(aValue, VARIANT_HMK | VARIANT_NONE,
5406 nsCSSProps::kSpeakKTable);
5407 case eCSSProperty_speak_header:
5408 return ParseVariant(aValue, VARIANT_HK,
5409 nsCSSProps::kSpeakHeaderKTable);
5410 case eCSSProperty_speak_numeral:
5411 return ParseVariant(aValue, VARIANT_HK,
5412 nsCSSProps::kSpeakNumeralKTable);
5413 case eCSSProperty_speak_punctuation:
5414 return ParseVariant(aValue, VARIANT_HOK,
5415 nsCSSProps::kSpeakPunctuationKTable);
5416 case eCSSProperty_speech_rate:
5417 return ParseVariant(aValue, VARIANT_HN | VARIANT_KEYWORD,
5418 nsCSSProps::kSpeechRateKTable);
5419 case eCSSProperty_stack_sizing:
5420 return ParseVariant(aValue, VARIANT_HK,
5421 nsCSSProps::kStackSizingKTable);
5422 case eCSSProperty_stress:
5423 return ParseVariant(aValue, VARIANT_HN, nsnull);
5424 case eCSSProperty_table_layout:
5425 return ParseVariant(aValue, VARIANT_AHK,
5426 nsCSSProps::kTableLayoutKTable);
5427 case eCSSProperty_text_align:
5428 // When we support aligning on a string, we can parse text-align
5429 // as a string....
5430 return ParseVariant(aValue, VARIANT_HK /* | VARIANT_STRING */,
5431 nsCSSProps::kTextAlignKTable);
5432 case eCSSProperty_text_decoration:
5433 return ParseTextDecoration(aValue);
5434 case eCSSProperty_text_indent:
5435 return ParseVariant(aValue, VARIANT_HLP, nsnull);
5436 case eCSSProperty_text_transform:
5437 return ParseVariant(aValue, VARIANT_HOK,
5438 nsCSSProps::kTextTransformKTable);
5439 case eCSSProperty_unicode_bidi:
5440 return ParseVariant(aValue, VARIANT_HMK,
5441 nsCSSProps::kUnicodeBidiKTable);
5442 case eCSSProperty_user_focus:
5443 return ParseVariant(aValue, VARIANT_HMK | VARIANT_NONE,
5444 nsCSSProps::kUserFocusKTable);
5445 case eCSSProperty_user_input:
5446 return ParseVariant(aValue, VARIANT_AHK | VARIANT_NONE,
5447 nsCSSProps::kUserInputKTable);
5448 case eCSSProperty_user_modify:
5449 return ParseVariant(aValue, VARIANT_HK,
5450 nsCSSProps::kUserModifyKTable);
5451 case eCSSProperty_user_select:
5452 return ParseVariant(aValue, VARIANT_AHK | VARIANT_NONE,
5453 nsCSSProps::kUserSelectKTable);
5454 case eCSSProperty_vertical_align:
5455 return ParseVariant(aValue, VARIANT_HKLP,
5456 nsCSSProps::kVerticalAlignKTable);
5457 case eCSSProperty_visibility:
5458 return ParseVariant(aValue, VARIANT_HK,
5459 nsCSSProps::kVisibilityKTable);
5460 case eCSSProperty_voice_family:
5461 return ParseFamily(aValue);
5462 case eCSSProperty_volume:
5463 return ParseVariant(aValue, VARIANT_HPN | VARIANT_KEYWORD,
5464 nsCSSProps::kVolumeKTable);
5465 case eCSSProperty_white_space:
5466 return ParseVariant(aValue, VARIANT_HMK,
5467 nsCSSProps::kWhitespaceKTable);
5468 case eCSSProperty_word_wrap:
5469 return ParseVariant(aValue, VARIANT_HMK,
5470 nsCSSProps::kWordwrapKTable);
5471 case eCSSProperty_z_index:
5472 return ParseVariant(aValue, VARIANT_AHI, nsnull);
5474 // explicitly do NOT have a default case to let the compiler
5475 // help find missing properties
5476 return PR_FALSE;
5479 // nsFont::EnumerateFamilies callback for ParseFontDescriptorValue
5480 struct NS_STACK_CLASS ExtractFirstFamilyData {
5481 nsAutoString mFamilyName;
5482 PRBool mGood;
5483 ExtractFirstFamilyData() : mFamilyName(), mGood(PR_FALSE) {}
5486 static PRBool
5487 ExtractFirstFamily(const nsString& aFamily,
5488 PRBool aGeneric,
5489 void* aData)
5491 ExtractFirstFamilyData* realData = (ExtractFirstFamilyData*) aData;
5492 if (aGeneric || realData->mFamilyName.Length() > 0) {
5493 realData->mGood = PR_FALSE;
5494 return PR_FALSE;
5496 realData->mFamilyName.Assign(aFamily);
5497 realData->mGood = PR_TRUE;
5498 return PR_TRUE;
5501 // font-descriptor: descriptor ':' value ';'
5502 // caller has advanced mToken to point at the descriptor
5503 PRBool
5504 CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID,
5505 nsCSSValue& aValue)
5507 switch (aDescID) {
5508 // These four are similar to the properties of the same name,
5509 // possibly with more restrictions on the values they can take.
5510 case eCSSFontDesc_Family: {
5511 if (!ParseFamily(aValue) ||
5512 aValue.GetUnit() != eCSSUnit_String)
5513 return PR_FALSE;
5515 // the style parameters to the nsFont constructor are ignored,
5516 // because it's only being used to call EnumerateFamilies
5517 nsAutoString valueStr;
5518 aValue.GetStringValue(valueStr);
5519 nsFont font(valueStr, 0, 0, 0, 0, 0);
5520 ExtractFirstFamilyData dat;
5522 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
5523 if (!dat.mGood)
5524 return PR_FALSE;
5526 aValue.SetStringValue(dat.mFamilyName, eCSSUnit_String);
5527 return PR_TRUE;
5530 case eCSSFontDesc_Style:
5531 // property is VARIANT_HMK|VARIANT_SYSFONT
5532 return ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
5533 nsCSSProps::kFontStyleKTable);
5535 case eCSSFontDesc_Weight:
5536 return (ParseFontWeight(aValue) &&
5537 aValue.GetUnit() != eCSSUnit_Inherit &&
5538 aValue.GetUnit() != eCSSUnit_Initial &&
5539 (aValue.GetUnit() != eCSSUnit_Enumerated ||
5540 (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
5541 aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
5543 case eCSSFontDesc_Stretch:
5544 // property is VARIANT_HMK|VARIANT_SYSFONT
5545 return (ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
5546 nsCSSProps::kFontStretchKTable) &&
5547 (aValue.GetUnit() != eCSSUnit_Enumerated ||
5548 (aValue.GetIntValue() != NS_STYLE_FONT_STRETCH_WIDER &&
5549 aValue.GetIntValue() != NS_STYLE_FONT_STRETCH_NARROWER)));
5551 // These two are unique to @font-face and have their own special grammar.
5552 case eCSSFontDesc_Src:
5553 return ParseFontSrc(aValue);
5555 case eCSSFontDesc_UnicodeRange:
5556 return ParseFontRanges(aValue);
5558 case eCSSFontDesc_UNKNOWN:
5559 case eCSSFontDesc_COUNT:
5560 NS_NOTREACHED("bad nsCSSFontDesc code");
5562 // explicitly do NOT have a default case to let the compiler
5563 // help find missing descriptors
5564 return PR_FALSE;
5567 void
5568 CSSParserImpl::InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties)
5570 nsCSSValue physical(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
5571 for (const nsCSSProperty *prop = aSourceProperties;
5572 *prop != eCSSProperty_UNKNOWN; ++prop) {
5573 AppendValue(*prop, physical);
5577 PRBool
5578 CSSParserImpl::ParseAzimuth(nsCSSValue& aValue)
5580 if (ParseVariant(aValue, VARIANT_HK | VARIANT_ANGLE,
5581 nsCSSProps::kAzimuthKTable)) {
5582 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
5583 PRInt32 intValue = aValue.GetIntValue();
5584 if ((NS_STYLE_AZIMUTH_LEFT_SIDE <= intValue) &&
5585 (intValue <= NS_STYLE_AZIMUTH_BEHIND)) { // look for optional modifier
5586 nsCSSValue modifier;
5587 if (ParseEnum(modifier, nsCSSProps::kAzimuthKTable)) {
5588 PRInt32 enumValue = modifier.GetIntValue();
5589 if (((intValue == NS_STYLE_AZIMUTH_BEHIND) &&
5590 (NS_STYLE_AZIMUTH_LEFT_SIDE <= enumValue) && (enumValue <= NS_STYLE_AZIMUTH_RIGHT_SIDE)) ||
5591 ((enumValue == NS_STYLE_AZIMUTH_BEHIND) &&
5592 (NS_STYLE_AZIMUTH_LEFT_SIDE <= intValue) && (intValue <= NS_STYLE_AZIMUTH_RIGHT_SIDE))) {
5593 aValue.SetIntValue(intValue | enumValue, eCSSUnit_Enumerated);
5594 return PR_TRUE;
5596 // Put the unknown identifier back and return
5597 UngetToken();
5598 return PR_FALSE;
5602 return PR_TRUE;
5604 return PR_FALSE;
5607 static nsCSSValue
5608 BoxPositionMaskToCSSValue(PRInt32 aMask, PRBool isX)
5610 PRInt32 val = NS_STYLE_BG_POSITION_CENTER;
5611 if (isX) {
5612 if (aMask & BG_LEFT) {
5613 val = NS_STYLE_BG_POSITION_LEFT;
5615 else if (aMask & BG_RIGHT) {
5616 val = NS_STYLE_BG_POSITION_RIGHT;
5619 else {
5620 if (aMask & BG_TOP) {
5621 val = NS_STYLE_BG_POSITION_TOP;
5623 else if (aMask & BG_BOTTOM) {
5624 val = NS_STYLE_BG_POSITION_BOTTOM;
5628 return nsCSSValue(val, eCSSUnit_Enumerated);
5631 PRBool
5632 CSSParserImpl::ParseBackground()
5634 nsAutoParseCompoundProperty compound(this);
5636 // Fill in the values that the shorthand will set if we don't find
5637 // other values.
5638 mTempData.mColor.mBackColor.SetColorValue(NS_RGBA(0, 0, 0, 0));
5639 mTempData.SetPropertyBit(eCSSProperty_background_color);
5640 mTempData.mColor.mBackImage.SetNoneValue();
5641 mTempData.SetPropertyBit(eCSSProperty_background_image);
5642 mTempData.mColor.mBackRepeat.SetIntValue(NS_STYLE_BG_REPEAT_XY,
5643 eCSSUnit_Enumerated);
5644 mTempData.SetPropertyBit(eCSSProperty_background_repeat);
5645 mTempData.mColor.mBackAttachment.SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL,
5646 eCSSUnit_Enumerated);
5647 mTempData.SetPropertyBit(eCSSProperty_background_attachment);
5648 mTempData.mColor.mBackPosition.mXValue.SetPercentValue(0.0f);
5649 mTempData.mColor.mBackPosition.mYValue.SetPercentValue(0.0f);
5650 mTempData.SetPropertyBit(eCSSProperty_background_position);
5651 // including the ones that we can't set from the shorthand.
5652 mTempData.mColor.mBackClip.SetInitialValue();
5653 mTempData.SetPropertyBit(eCSSProperty__moz_background_clip);
5654 mTempData.mColor.mBackOrigin.SetInitialValue();
5655 mTempData.SetPropertyBit(eCSSProperty__moz_background_origin);
5656 mTempData.mColor.mBackInlinePolicy.SetInitialValue();
5657 mTempData.SetPropertyBit(eCSSProperty__moz_background_inline_policy);
5659 // XXX If ParseSingleValueProperty were table-driven (bug 376079) and
5660 // automatically filled in the right field of mTempData, we could move
5661 // ParseBackgroundPosition to it (as a special case) and switch back
5662 // to using ParseChoice here.
5664 PRBool haveColor = PR_FALSE,
5665 haveImage = PR_FALSE,
5666 haveRepeat = PR_FALSE,
5667 haveAttach = PR_FALSE,
5668 havePosition = PR_FALSE;
5669 while (GetToken(PR_TRUE)) {
5670 nsCSSTokenType tt = mToken.mType;
5671 UngetToken(); // ...but we'll still cheat and use mToken
5672 if (tt == eCSSToken_Symbol) {
5673 // ExpectEndProperty only looks for symbols, and nothing else will
5674 // show up as one.
5675 break;
5678 if (tt == eCSSToken_Ident) {
5679 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
5680 PRInt32 dummy;
5681 if (keyword == eCSSKeyword_inherit ||
5682 keyword == eCSSKeyword__moz_initial) {
5683 if (haveColor || haveImage || haveRepeat || haveAttach || havePosition)
5684 return PR_FALSE;
5685 haveColor = haveImage = haveRepeat = haveAttach = havePosition =
5686 PR_TRUE;
5687 GetToken(PR_TRUE); // undo the UngetToken above
5688 nsCSSValue val;
5689 if (keyword == eCSSKeyword_inherit) {
5690 val.SetInheritValue();
5691 } else {
5692 val.SetInitialValue();
5694 mTempData.mColor.mBackColor = val;
5695 mTempData.mColor.mBackImage = val;
5696 mTempData.mColor.mBackRepeat = val;
5697 mTempData.mColor.mBackAttachment = val;
5698 mTempData.mColor.mBackPosition.mXValue = val;
5699 mTempData.mColor.mBackPosition.mYValue = val;
5700 // Reset (for 'inherit') the 3 properties that can't be
5701 // specified, although it's not entirely clear in the spec:
5702 // http://lists.w3.org/Archives/Public/www-style/2007Mar/0110
5703 mTempData.mColor.mBackClip = val;
5704 mTempData.mColor.mBackOrigin = val;
5705 mTempData.mColor.mBackInlinePolicy = val;
5706 break;
5707 } else if (keyword == eCSSKeyword_none) {
5708 if (haveImage)
5709 return PR_FALSE;
5710 haveImage = PR_TRUE;
5711 if (!ParseSingleValueProperty(mTempData.mColor.mBackImage,
5712 eCSSProperty_background_image)) {
5713 NS_NOTREACHED("should be able to parse");
5714 return PR_FALSE;
5716 } else if (nsCSSProps::FindKeyword(keyword,
5717 nsCSSProps::kBackgroundAttachmentKTable, dummy)) {
5718 if (haveAttach)
5719 return PR_FALSE;
5720 haveAttach = PR_TRUE;
5721 if (!ParseSingleValueProperty(mTempData.mColor.mBackAttachment,
5722 eCSSProperty_background_attachment)) {
5723 NS_NOTREACHED("should be able to parse");
5724 return PR_FALSE;
5726 } else if (nsCSSProps::FindKeyword(keyword,
5727 nsCSSProps::kBackgroundRepeatKTable, dummy)) {
5728 if (haveRepeat)
5729 return PR_FALSE;
5730 haveRepeat = PR_TRUE;
5731 if (!ParseSingleValueProperty(mTempData.mColor.mBackRepeat,
5732 eCSSProperty_background_repeat)) {
5733 NS_NOTREACHED("should be able to parse");
5734 return PR_FALSE;
5736 } else if (nsCSSProps::FindKeyword(keyword,
5737 nsCSSProps::kBackgroundPositionKTable, dummy)) {
5738 if (havePosition)
5739 return PR_FALSE;
5740 havePosition = PR_TRUE;
5741 if (!ParseBackgroundPositionValues()) {
5742 return PR_FALSE;
5744 } else {
5745 if (haveColor)
5746 return PR_FALSE;
5747 haveColor = PR_TRUE;
5748 if (!ParseSingleValueProperty(mTempData.mColor.mBackColor,
5749 eCSSProperty_background_color)) {
5750 return PR_FALSE;
5753 } else if (eCSSToken_Function == tt &&
5754 mToken.mIdent.LowerCaseEqualsLiteral("url")) {
5755 if (haveImage)
5756 return PR_FALSE;
5757 haveImage = PR_TRUE;
5758 if (!ParseSingleValueProperty(mTempData.mColor.mBackImage,
5759 eCSSProperty_background_image)) {
5760 return PR_FALSE;
5762 } else if (mToken.IsDimension() || tt == eCSSToken_Percentage) {
5763 if (havePosition)
5764 return PR_FALSE;
5765 havePosition = PR_TRUE;
5766 if (!ParseBackgroundPositionValues()) {
5767 return PR_FALSE;
5769 } else {
5770 if (haveColor)
5771 return PR_FALSE;
5772 haveColor = PR_TRUE;
5773 if (!ParseSingleValueProperty(mTempData.mColor.mBackColor,
5774 eCSSProperty_background_color)) {
5775 return PR_FALSE;
5780 return ExpectEndProperty() &&
5781 (haveColor || haveImage || haveRepeat || haveAttach || havePosition);
5784 PRBool
5785 CSSParserImpl::ParseBackgroundPosition()
5787 if (!ParseBoxPosition(mTempData.mColor.mBackPosition))
5788 return PR_FALSE;
5789 mTempData.SetPropertyBit(eCSSProperty_background_position);
5790 return PR_TRUE;
5793 PRBool
5794 CSSParserImpl::ParseBackgroundPositionValues()
5796 return ParseBoxPositionValues(mTempData.mColor.mBackPosition);
5800 * Parses two values that correspond to positions in a box. These can be
5801 * values corresponding to percentages of the box, raw offsets, or keywords
5802 * like "top," "left center," etc.
5804 * @param aOut The nsCSSValuePair where to place the result.
5805 * @return Whether or not the operation succeeded.
5807 PRBool CSSParserImpl::ParseBoxPosition(nsCSSValuePair &aOut)
5809 // Need to read the box positions and the end of the property.
5810 return ParseBoxPositionValues(aOut) && ExpectEndProperty();
5813 PRBool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut)
5815 // First try a percentage or a length value
5816 nsCSSValue &xValue = aOut.mXValue,
5817 &yValue = aOut.mYValue;
5818 if (ParseVariant(xValue, VARIANT_HLP, nsnull)) {
5819 if (eCSSUnit_Inherit == xValue.GetUnit() ||
5820 eCSSUnit_Initial == xValue.GetUnit()) { // both are inherited or both are set to initial
5821 yValue = xValue;
5822 return PR_TRUE;
5824 // We have one percentage/length. Get the optional second
5825 // percentage/length/keyword.
5826 if (ParseVariant(yValue, VARIANT_LP, nsnull)) {
5827 // We have two numbers
5828 return PR_TRUE;
5831 if (ParseEnum(yValue, nsCSSProps::kBackgroundPositionKTable)) {
5832 PRInt32 yVal = yValue.GetIntValue();
5833 if (!(yVal & BG_CTB)) {
5834 // The second keyword can only be 'center', 'top', or 'bottom'
5835 return PR_FALSE;
5837 yValue = BoxPositionMaskToCSSValue(yVal, PR_FALSE);
5838 return PR_TRUE;
5841 // If only one percentage or length value is given, it sets the
5842 // horizontal position only, and the vertical position will be 50%.
5843 yValue.SetPercentValue(0.5f);
5844 return PR_TRUE;
5847 // Now try keywords. We do this manually to allow for the first
5848 // appearance of "center" to apply to the either the x or y
5849 // position (it's ambiguous so we have to disambiguate). Each
5850 // allowed keyword value is assigned it's own bit. We don't allow
5851 // any duplicate keywords other than center. We try to get two
5852 // keywords but it's okay if there is only one.
5853 PRInt32 mask = 0;
5854 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
5855 PRInt32 bit = xValue.GetIntValue();
5856 mask |= bit;
5857 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
5858 bit = xValue.GetIntValue();
5859 if (mask & (bit & ~BG_CENTER)) {
5860 // Only the 'center' keyword can be duplicated.
5861 return PR_FALSE;
5863 mask |= bit;
5865 else {
5866 // Only one keyword. See if we have a length or percentage.
5867 if (ParseVariant(yValue, VARIANT_LP, nsnull)) {
5868 if (!(mask & BG_CLR)) {
5869 // The first keyword can only be 'center', 'left', or 'right'
5870 return PR_FALSE;
5873 xValue = BoxPositionMaskToCSSValue(mask, PR_TRUE);
5874 return PR_TRUE;
5879 // Check for bad input. Bad input consists of no matching keywords,
5880 // or pairs of x keywords or pairs of y keywords.
5881 if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
5882 (mask == (BG_LEFT | BG_RIGHT))) {
5883 return PR_FALSE;
5886 // Create style values
5887 xValue = BoxPositionMaskToCSSValue(mask, PR_TRUE);
5888 yValue = BoxPositionMaskToCSSValue(mask, PR_FALSE);
5889 return PR_TRUE;
5892 // These must be in CSS order (top,right,bottom,left) for indexing to work
5893 static const nsCSSProperty kBorderStyleIDs[] = {
5894 eCSSProperty_border_top_style,
5895 eCSSProperty_border_right_style_value,
5896 eCSSProperty_border_bottom_style,
5897 eCSSProperty_border_left_style_value
5899 static const nsCSSProperty kBorderWidthIDs[] = {
5900 eCSSProperty_border_top_width,
5901 eCSSProperty_border_right_width_value,
5902 eCSSProperty_border_bottom_width,
5903 eCSSProperty_border_left_width_value
5905 static const nsCSSProperty kBorderColorIDs[] = {
5906 eCSSProperty_border_top_color,
5907 eCSSProperty_border_right_color_value,
5908 eCSSProperty_border_bottom_color,
5909 eCSSProperty_border_left_color_value
5911 static const nsCSSProperty kBorderRadiusIDs[] = {
5912 eCSSProperty__moz_border_radius_topLeft,
5913 eCSSProperty__moz_border_radius_topRight,
5914 eCSSProperty__moz_border_radius_bottomRight,
5915 eCSSProperty__moz_border_radius_bottomLeft
5917 static const nsCSSProperty kOutlineRadiusIDs[] = {
5918 eCSSProperty__moz_outline_radius_topLeft,
5919 eCSSProperty__moz_outline_radius_topRight,
5920 eCSSProperty__moz_outline_radius_bottomRight,
5921 eCSSProperty__moz_outline_radius_bottomLeft
5924 PRBool
5925 CSSParserImpl::ParseBorderColor()
5927 static const nsCSSProperty kBorderColorSources[] = {
5928 eCSSProperty_border_left_color_ltr_source,
5929 eCSSProperty_border_left_color_rtl_source,
5930 eCSSProperty_border_right_color_ltr_source,
5931 eCSSProperty_border_right_color_rtl_source,
5932 eCSSProperty_UNKNOWN
5935 // do this now, in case 4 values weren't specified
5936 InitBoxPropsAsPhysical(kBorderColorSources);
5937 return ParseBoxProperties(mTempData.mMargin.mBorderColor,
5938 kBorderColorIDs);
5941 PRBool
5942 CSSParserImpl::ParseBorderImage()
5944 if (ParseVariant(mTempData.mMargin.mBorderImage,
5945 VARIANT_INHERIT | VARIANT_NONE, nsnull)) {
5946 mTempData.SetPropertyBit(eCSSProperty_border_image);
5947 return PR_TRUE;
5950 // <uri> [<number> | <percentage>]{1,4} [ / <border-width>{1,4} ]? [stretch | repeat | round]{0,2}
5951 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(11);
5952 if (!arr) {
5953 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
5954 return PR_FALSE;
5957 nsCSSValue& url = arr->Item(0);
5958 nsCSSValue& splitTop = arr->Item(1);
5959 nsCSSValue& splitRight = arr->Item(2);
5960 nsCSSValue& splitBottom = arr->Item(3);
5961 nsCSSValue& splitLeft = arr->Item(4);
5962 nsCSSValue& borderWidthTop = arr->Item(5);
5963 nsCSSValue& borderWidthRight = arr->Item(6);
5964 nsCSSValue& borderWidthBottom = arr->Item(7);
5965 nsCSSValue& borderWidthLeft = arr->Item(8);
5966 nsCSSValue& horizontalKeyword = arr->Item(9);
5967 nsCSSValue& verticalKeyword = arr->Item(10);
5969 // <uri>
5970 if (!ParseVariant(url, VARIANT_URL, nsnull)) {
5971 return PR_FALSE;
5974 // [<number> | <percentage>]{1,4}
5975 if (!ParsePositiveVariant(splitTop,
5976 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
5977 return PR_FALSE;
5979 if (!ParsePositiveVariant(splitRight,
5980 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
5981 splitRight = splitTop;
5983 if (!ParsePositiveVariant(splitBottom,
5984 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
5985 splitBottom = splitTop;
5987 if (!ParsePositiveVariant(splitLeft,
5988 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
5989 splitLeft = splitRight;
5992 // [ / <border-width>{1,4} ]?
5993 if (ExpectSymbol('/', PR_TRUE)) {
5994 // if have '/', at least one value is required
5995 if (!ParsePositiveVariant(borderWidthTop, VARIANT_LENGTH, nsnull)) {
5996 return PR_FALSE;
5998 if (!ParsePositiveVariant(borderWidthRight, VARIANT_LENGTH, nsnull)) {
5999 borderWidthRight = borderWidthTop;
6001 if (!ParsePositiveVariant(borderWidthBottom, VARIANT_LENGTH, nsnull)) {
6002 borderWidthBottom = borderWidthTop;
6004 if (!ParsePositiveVariant(borderWidthLeft, VARIANT_LENGTH, nsnull)) {
6005 borderWidthLeft = borderWidthRight;
6009 // [stretch | repeat | round]{0,2}
6010 // missing keywords are handled in nsRuleNode::ComputeBorderData()
6011 if (ParseEnum(horizontalKeyword, nsCSSProps::kBorderImageKTable)) {
6012 ParseEnum(verticalKeyword, nsCSSProps::kBorderImageKTable);
6015 if (!ExpectEndProperty()) {
6016 return PR_FALSE;
6019 mTempData.mMargin.mBorderImage.SetArrayValue(arr, eCSSUnit_Array);
6020 mTempData.SetPropertyBit(eCSSProperty_border_image);
6022 return PR_TRUE;
6025 PRBool
6026 CSSParserImpl::ParseBorderSpacing()
6028 nsCSSValue xValue;
6029 if (ParsePositiveVariant(xValue, VARIANT_HL, nsnull)) {
6030 if (xValue.IsLengthUnit()) {
6031 // We have one length. Get the optional second length.
6032 nsCSSValue yValue;
6033 if (ParsePositiveVariant(yValue, VARIANT_LENGTH, nsnull)) {
6034 // We have two numbers
6035 if (ExpectEndProperty()) {
6036 mTempData.mTable.mBorderSpacing.mXValue = xValue;
6037 mTempData.mTable.mBorderSpacing.mYValue = yValue;
6038 mTempData.SetPropertyBit(eCSSProperty_border_spacing);
6039 return PR_TRUE;
6041 return PR_FALSE;
6045 // We have one length which is the horizontal spacing. Create a value for
6046 // the vertical spacing which is equal
6047 if (ExpectEndProperty()) {
6048 mTempData.mTable.mBorderSpacing.SetBothValuesTo(xValue);
6049 mTempData.SetPropertyBit(eCSSProperty_border_spacing);
6050 return PR_TRUE;
6053 return PR_FALSE;
6056 PRBool
6057 CSSParserImpl::ParseBorderSide(const nsCSSProperty aPropIDs[],
6058 PRBool aSetAllSides)
6060 const PRInt32 numProps = 3;
6061 nsCSSValue values[numProps];
6063 PRInt32 found = ParseChoice(values, aPropIDs, numProps);
6064 if ((found < 1) || (PR_FALSE == ExpectEndProperty())) {
6065 return PR_FALSE;
6068 if ((found & 1) == 0) { // Provide default border-width
6069 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
6071 if ((found & 2) == 0) { // Provide default border-style
6072 values[1].SetNoneValue();
6074 if ((found & 4) == 0) { // text color will be used
6075 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
6078 if (aSetAllSides) {
6079 static const nsCSSProperty kBorderSources[] = {
6080 eCSSProperty_border_left_color_ltr_source,
6081 eCSSProperty_border_left_color_rtl_source,
6082 eCSSProperty_border_right_color_ltr_source,
6083 eCSSProperty_border_right_color_rtl_source,
6084 eCSSProperty_border_left_style_ltr_source,
6085 eCSSProperty_border_left_style_rtl_source,
6086 eCSSProperty_border_right_style_ltr_source,
6087 eCSSProperty_border_right_style_rtl_source,
6088 eCSSProperty_border_left_width_ltr_source,
6089 eCSSProperty_border_left_width_rtl_source,
6090 eCSSProperty_border_right_width_ltr_source,
6091 eCSSProperty_border_right_width_rtl_source,
6092 eCSSProperty_UNKNOWN
6095 InitBoxPropsAsPhysical(kBorderSources);
6097 // Parsing "border" shorthand; set all four sides to the same thing
6098 for (PRInt32 index = 0; index < 4; index++) {
6099 NS_ASSERTION(numProps == 3, "This code needs updating");
6100 AppendValue(kBorderWidthIDs[index], values[0]);
6101 AppendValue(kBorderStyleIDs[index], values[1]);
6102 AppendValue(kBorderColorIDs[index], values[2]);
6105 else {
6106 // Just set our one side
6107 for (PRInt32 index = 0; index < numProps; index++) {
6108 AppendValue(aPropIDs[index], values[index]);
6111 return PR_TRUE;
6114 PRBool
6115 CSSParserImpl::ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
6116 PRInt32 aSourceType)
6118 const PRInt32 numProps = 3;
6119 nsCSSValue values[numProps];
6121 PRInt32 found = ParseChoice(values, aPropIDs, numProps);
6122 if ((found < 1) || (PR_FALSE == ExpectEndProperty())) {
6123 return PR_FALSE;
6126 if ((found & 1) == 0) { // Provide default border-width
6127 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
6129 if ((found & 2) == 0) { // Provide default border-style
6130 values[1].SetNoneValue();
6132 if ((found & 4) == 0) { // text color will be used
6133 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
6135 for (PRInt32 index = 0; index < numProps; index++) {
6136 const nsCSSProperty* subprops =
6137 nsCSSProps::SubpropertyEntryFor(aPropIDs[index + numProps]);
6138 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
6139 "not box property with physical vs. logical cascading");
6140 AppendValue(subprops[0], values[index]);
6141 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
6142 AppendValue(subprops[1], typeVal);
6143 AppendValue(subprops[2], typeVal);
6145 return PR_TRUE;
6148 PRBool
6149 CSSParserImpl::ParseBorderStyle()
6151 static const nsCSSProperty kBorderStyleSources[] = {
6152 eCSSProperty_border_left_style_ltr_source,
6153 eCSSProperty_border_left_style_rtl_source,
6154 eCSSProperty_border_right_style_ltr_source,
6155 eCSSProperty_border_right_style_rtl_source,
6156 eCSSProperty_UNKNOWN
6159 // do this now, in case 4 values weren't specified
6160 InitBoxPropsAsPhysical(kBorderStyleSources);
6161 return ParseBoxProperties(mTempData.mMargin.mBorderStyle,
6162 kBorderStyleIDs);
6165 PRBool
6166 CSSParserImpl::ParseBorderWidth()
6168 static const nsCSSProperty kBorderWidthSources[] = {
6169 eCSSProperty_border_left_width_ltr_source,
6170 eCSSProperty_border_left_width_rtl_source,
6171 eCSSProperty_border_right_width_ltr_source,
6172 eCSSProperty_border_right_width_rtl_source,
6173 eCSSProperty_UNKNOWN
6176 // do this now, in case 4 values weren't specified
6177 InitBoxPropsAsPhysical(kBorderWidthSources);
6178 return ParseBoxProperties(mTempData.mMargin.mBorderWidth,
6179 kBorderWidthIDs);
6182 PRBool
6183 CSSParserImpl::ParseBorderRadius()
6185 return ParseBoxProperties(mTempData.mMargin.mBorderRadius,
6186 kBorderRadiusIDs);
6189 PRBool
6190 CSSParserImpl::ParseOutlineRadius()
6192 return ParseBoxProperties(mTempData.mMargin.mOutlineRadius,
6193 kOutlineRadiusIDs);
6196 PRBool
6197 CSSParserImpl::ParseBorderColors(nsCSSValueList** aResult,
6198 nsCSSProperty aProperty)
6200 nsCSSValue value;
6201 if (ParseVariant(value, VARIANT_HCK|VARIANT_NONE, nsCSSProps::kBorderColorKTable)) {
6202 nsCSSValueList* listHead = new nsCSSValueList();
6203 nsCSSValueList* list = listHead;
6204 if (!list) {
6205 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6206 return PR_FALSE;
6208 list->mValue = value;
6210 while (list) {
6211 if (ExpectEndProperty()) {
6212 mTempData.SetPropertyBit(aProperty);
6213 *aResult = listHead;
6214 return PR_TRUE;
6216 // FIXME Bug 389404: We should not accept inherit, -moz-initial,
6217 // or none as anything other than the first value.
6218 if (ParseVariant(value, VARIANT_HCK|VARIANT_NONE, nsCSSProps::kBorderColorKTable)) {
6219 list->mNext = new nsCSSValueList();
6220 list = list->mNext;
6221 if (list)
6222 list->mValue = value;
6223 else
6224 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6226 else
6227 break;
6229 delete listHead;
6231 return PR_FALSE;
6234 PRBool
6235 CSSParserImpl::ParseRect(nsCSSRect& aRect, nsCSSProperty aPropID)
6237 nsCSSRect rect;
6238 PRBool result;
6239 if ((result = DoParseRect(rect)) &&
6240 rect != aRect) {
6241 aRect = rect;
6242 mTempData.SetPropertyBit(aPropID);
6244 return result;
6247 PRBool
6248 CSSParserImpl::DoParseRect(nsCSSRect& aRect)
6250 if (! GetToken(PR_TRUE)) {
6251 return PR_FALSE;
6253 if (eCSSToken_Ident == mToken.mType) {
6254 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
6255 switch (keyword) {
6256 case eCSSKeyword_auto:
6257 if (ExpectEndProperty()) {
6258 aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Auto));
6259 return PR_TRUE;
6261 break;
6262 case eCSSKeyword_inherit:
6263 if (ExpectEndProperty()) {
6264 aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Inherit));
6265 return PR_TRUE;
6267 break;
6268 case eCSSKeyword__moz_initial:
6269 if (ExpectEndProperty()) {
6270 aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Initial));
6271 return PR_TRUE;
6273 break;
6274 default:
6275 UngetToken();
6276 break;
6278 } else if ((eCSSToken_Function == mToken.mType) &&
6279 mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
6280 if (!ExpectSymbol('(', PR_TRUE)) {
6281 return PR_FALSE;
6283 NS_FOR_CSS_SIDES(side) {
6284 if (! ParseVariant(aRect.*(nsCSSRect::sides[side]),
6285 VARIANT_AL, nsnull)) {
6286 return PR_FALSE;
6288 if (3 != side) {
6289 // skip optional commas between elements
6290 ExpectSymbol(',', PR_TRUE);
6293 if (!ExpectSymbol(')', PR_TRUE)) {
6294 return PR_FALSE;
6296 if (ExpectEndProperty()) {
6297 return PR_TRUE;
6299 } else {
6300 UngetToken();
6302 return PR_FALSE;
6305 #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
6306 VARIANT_KEYWORD)
6307 PRBool
6308 CSSParserImpl::ParseContent()
6310 // XXX Rewrite to make it look more like ParseCursor or ParseCounterData?
6311 nsCSSValue value;
6312 if (ParseVariant(value,
6313 VARIANT_CONTENT | VARIANT_INHERIT | VARIANT_NORMAL |
6314 VARIANT_NONE,
6315 nsCSSProps::kContentKTable)) {
6316 nsCSSValueList* listHead = new nsCSSValueList();
6317 nsCSSValueList* list = listHead;
6318 if (nsnull == list) {
6319 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6320 return PR_FALSE;
6322 list->mValue = value;
6324 while (nsnull != list) {
6325 if (ExpectEndProperty()) {
6326 mTempData.SetPropertyBit(eCSSProperty_content);
6327 mTempData.mContent.mContent = listHead;
6328 return PR_TRUE;
6330 if (eCSSUnit_Inherit == value.GetUnit() ||
6331 eCSSUnit_Initial == value.GetUnit() ||
6332 eCSSUnit_Normal == value.GetUnit() ||
6333 eCSSUnit_None == value.GetUnit() ||
6334 (eCSSUnit_Enumerated == value.GetUnit() &&
6335 NS_STYLE_CONTENT_ALT_CONTENT == value.GetIntValue())) {
6336 // This only matters the first time through the loop.
6337 delete listHead;
6338 return PR_FALSE;
6340 if (ParseVariant(value, VARIANT_CONTENT, nsCSSProps::kContentKTable) &&
6341 // Make sure we didn't end up with NS_STYLE_CONTENT_ALT_CONTENT here
6342 (value.GetUnit() != eCSSUnit_Enumerated ||
6343 value.GetIntValue() != NS_STYLE_CONTENT_ALT_CONTENT)) {
6344 list->mNext = new nsCSSValueList();
6345 list = list->mNext;
6346 if (nsnull != list) {
6347 list->mValue = value;
6349 else {
6350 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6353 else {
6354 break;
6357 delete listHead;
6359 return PR_FALSE;
6362 struct SingleCounterPropValue {
6363 char str[13];
6364 nsCSSUnit unit;
6367 PRBool
6368 CSSParserImpl::ParseCounterData(nsCSSValuePairList** aResult,
6369 nsCSSProperty aPropID)
6371 nsSubstring* ident = NextIdent();
6372 if (nsnull == ident) {
6373 return PR_FALSE;
6375 static const SingleCounterPropValue singleValues[] = {
6376 { "none", eCSSUnit_None },
6377 { "inherit", eCSSUnit_Inherit },
6378 { "-moz-initial", eCSSUnit_Initial }
6380 for (const SingleCounterPropValue *sv = singleValues,
6381 *sv_end = singleValues + NS_ARRAY_LENGTH(singleValues);
6382 sv != sv_end; ++sv) {
6383 if (ident->LowerCaseEqualsASCII(sv->str)) {
6384 if (CheckEndProperty()) {
6385 nsCSSValuePairList* dataHead = new nsCSSValuePairList();
6386 if (!dataHead) {
6387 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6388 return PR_FALSE;
6390 dataHead->mXValue = nsCSSValue(sv->unit);
6391 *aResult = dataHead;
6392 mTempData.SetPropertyBit(aPropID);
6393 return PR_TRUE;
6395 return PR_FALSE;
6398 UngetToken(); // undo NextIdent
6400 nsCSSValuePairList* dataHead = nsnull;
6401 nsCSSValuePairList **next = &dataHead;
6402 for (;;) {
6403 if (!GetToken(PR_TRUE) || mToken.mType != eCSSToken_Ident) {
6404 break;
6406 nsCSSValuePairList *data = *next = new nsCSSValuePairList();
6407 if (!data) {
6408 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6409 break;
6411 next = &data->mNext;
6412 data->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
6413 if (GetToken(PR_TRUE)) {
6414 if (eCSSToken_Number == mToken.mType && mToken.mIntegerValid) {
6415 data->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
6416 } else {
6417 UngetToken();
6420 if (ExpectEndProperty()) {
6421 mTempData.SetPropertyBit(aPropID);
6422 *aResult = dataHead;
6423 return PR_TRUE;
6426 delete dataHead;
6427 return PR_FALSE;
6430 PRBool
6431 CSSParserImpl::ParseCue()
6433 nsCSSValue before;
6434 if (ParseSingleValueProperty(before, eCSSProperty_cue_before)) {
6435 if (eCSSUnit_Inherit != before.GetUnit() &&
6436 eCSSUnit_Initial != before.GetUnit()) {
6437 nsCSSValue after;
6438 if (ParseSingleValueProperty(after, eCSSProperty_cue_after)) {
6439 if (ExpectEndProperty()) {
6440 AppendValue(eCSSProperty_cue_before, before);
6441 AppendValue(eCSSProperty_cue_after, after);
6442 return PR_TRUE;
6444 return PR_FALSE;
6447 if (ExpectEndProperty()) {
6448 AppendValue(eCSSProperty_cue_before, before);
6449 AppendValue(eCSSProperty_cue_after, before);
6450 return PR_TRUE;
6453 return PR_FALSE;
6456 PRBool
6457 CSSParserImpl::ParseCursor()
6459 nsCSSValueList *list = nsnull;
6460 for (nsCSSValueList **curp = &list, *cur; ; curp = &cur->mNext) {
6461 cur = *curp = new nsCSSValueList();
6462 if (!cur) {
6463 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6464 break;
6466 if (!ParseVariant(cur->mValue,
6467 (cur == list) ? VARIANT_AHUK : VARIANT_AUK,
6468 nsCSSProps::kCursorKTable)) {
6469 break;
6471 if (cur->mValue.GetUnit() != eCSSUnit_URL) {
6472 if (!ExpectEndProperty()) {
6473 break;
6475 // Only success case here, since having the failure case at the
6476 // end allows more sharing of code.
6477 mTempData.SetPropertyBit(eCSSProperty_cursor);
6478 mTempData.mUserInterface.mCursor = list;
6479 return PR_TRUE;
6481 // We have a URL, so make a value array with three values.
6482 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
6483 if (!val) {
6484 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6485 break;
6487 val->Item(0) = cur->mValue;
6488 cur->mValue.SetArrayValue(val, eCSSUnit_Array);
6490 // Parse optional x and y position of cursor hotspot (css3-ui).
6491 if (ParseVariant(val->Item(1), VARIANT_NUMBER, nsnull)) {
6492 // If we have one number, we must have two.
6493 if (!ParseVariant(val->Item(2), VARIANT_NUMBER, nsnull)) {
6494 break;
6498 if (!ExpectSymbol(',', PR_TRUE)) {
6499 break;
6502 // Have failure case at the end so we can |break| to get to it.
6503 delete list;
6504 return PR_FALSE;
6508 PRBool
6509 CSSParserImpl::ParseFont()
6511 static const nsCSSProperty fontIDs[] = {
6512 eCSSProperty_font_style,
6513 eCSSProperty_font_variant,
6514 eCSSProperty_font_weight
6517 nsCSSValue family;
6518 if (ParseVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) {
6519 if (ExpectEndProperty()) {
6520 if (eCSSUnit_Inherit == family.GetUnit() ||
6521 eCSSUnit_Initial == family.GetUnit()) {
6522 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
6523 AppendValue(eCSSProperty_font_family, family);
6524 AppendValue(eCSSProperty_font_style, family);
6525 AppendValue(eCSSProperty_font_variant, family);
6526 AppendValue(eCSSProperty_font_weight, family);
6527 AppendValue(eCSSProperty_font_size, family);
6528 AppendValue(eCSSProperty_line_height, family);
6529 AppendValue(eCSSProperty_font_stretch, family);
6530 AppendValue(eCSSProperty_font_size_adjust, family);
6532 else {
6533 AppendValue(eCSSProperty__x_system_font, family);
6534 nsCSSValue systemFont(eCSSUnit_System_Font);
6535 AppendValue(eCSSProperty_font_family, systemFont);
6536 AppendValue(eCSSProperty_font_style, systemFont);
6537 AppendValue(eCSSProperty_font_variant, systemFont);
6538 AppendValue(eCSSProperty_font_weight, systemFont);
6539 AppendValue(eCSSProperty_font_size, systemFont);
6540 AppendValue(eCSSProperty_line_height, systemFont);
6541 AppendValue(eCSSProperty_font_stretch, systemFont);
6542 AppendValue(eCSSProperty_font_size_adjust, systemFont);
6544 return PR_TRUE;
6546 return PR_FALSE;
6549 // Get optional font-style, font-variant and font-weight (in any order)
6550 const PRInt32 numProps = 3;
6551 nsCSSValue values[numProps];
6552 PRInt32 found = ParseChoice(values, fontIDs, numProps);
6553 if ((found < 0) || (eCSSUnit_Inherit == values[0].GetUnit()) ||
6554 (eCSSUnit_Initial == values[0].GetUnit())) { // illegal data
6555 return PR_FALSE;
6557 if ((found & 1) == 0) {
6558 // Provide default font-style
6559 values[0].SetNormalValue();
6561 if ((found & 2) == 0) {
6562 // Provide default font-variant
6563 values[1].SetNormalValue();
6565 if ((found & 4) == 0) {
6566 // Provide default font-weight
6567 values[2].SetNormalValue();
6570 // Get mandatory font-size
6571 nsCSSValue size;
6572 if (! ParseVariant(size, VARIANT_KEYWORD | VARIANT_LP, nsCSSProps::kFontSizeKTable)) {
6573 return PR_FALSE;
6576 // Get optional "/" line-height
6577 nsCSSValue lineHeight;
6578 if (ExpectSymbol('/', PR_TRUE)) {
6579 if (! ParsePositiveVariant(lineHeight,
6580 VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL,
6581 nsnull)) {
6582 return PR_FALSE;
6585 else {
6586 lineHeight.SetNormalValue();
6589 // Get final mandatory font-family
6590 nsAutoParseCompoundProperty compound(this);
6591 if (ParseFamily(family)) {
6592 if ((eCSSUnit_Inherit != family.GetUnit()) && (eCSSUnit_Initial != family.GetUnit()) &&
6593 ExpectEndProperty()) {
6594 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
6595 AppendValue(eCSSProperty_font_family, family);
6596 AppendValue(eCSSProperty_font_style, values[0]);
6597 AppendValue(eCSSProperty_font_variant, values[1]);
6598 AppendValue(eCSSProperty_font_weight, values[2]);
6599 AppendValue(eCSSProperty_font_size, size);
6600 AppendValue(eCSSProperty_line_height, lineHeight);
6601 AppendValue(eCSSProperty_font_stretch, nsCSSValue(eCSSUnit_Normal));
6602 AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
6603 return PR_TRUE;
6606 return PR_FALSE;
6609 PRBool
6610 CSSParserImpl::ParseFontWeight(nsCSSValue& aValue)
6612 if (ParseVariant(aValue, VARIANT_HMKI | VARIANT_SYSFONT, nsCSSProps::kFontWeightKTable)) {
6613 if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
6614 PRInt32 intValue = aValue.GetIntValue();
6615 if ((100 <= intValue) &&
6616 (intValue <= 900) &&
6617 (0 == (intValue % 100))) {
6618 return PR_TRUE;
6619 } else {
6620 UngetToken();
6621 return PR_FALSE;
6624 return PR_TRUE;
6626 return PR_FALSE;
6629 PRBool
6630 CSSParserImpl::ParseOneFamily(nsAString& aFamily)
6632 if (!GetToken(PR_TRUE))
6633 return PR_FALSE;
6635 nsCSSToken* tk = &mToken;
6637 if (eCSSToken_Ident == tk->mType) {
6638 aFamily.Append(tk->mIdent);
6639 for (;;) {
6640 if (!GetToken(PR_FALSE))
6641 break;
6643 if (eCSSToken_Ident == tk->mType) {
6644 aFamily.Append(tk->mIdent);
6645 } else if (eCSSToken_WhiteSpace == tk->mType) {
6646 // Lookahead one token and drop whitespace if we are ending the
6647 // font name.
6648 if (!GetToken(PR_TRUE))
6649 break;
6651 UngetToken();
6652 if (eCSSToken_Ident == tk->mType)
6653 aFamily.Append(PRUnichar(' '));
6654 else
6655 break;
6656 } else {
6657 UngetToken();
6658 break;
6661 return PR_TRUE;
6663 } else if (eCSSToken_String == tk->mType) {
6664 aFamily.Append(tk->mSymbol); // replace the quotes
6665 aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
6666 aFamily.Append(tk->mSymbol);
6667 return PR_TRUE;
6669 } else {
6670 UngetToken();
6671 return PR_FALSE;
6675 ///////////////////////////////////////////////////////
6676 // -moz-transform Parsing Implementation
6678 /* Reads a function list of arguments. Do not call this function
6679 * directly; it's mean to be caled from ParseFunction.
6681 PRBool
6682 CSSParserImpl::ParseFunctionInternals(const PRInt32 aVariantMask[],
6683 PRUint16 aMinElems,
6684 PRUint16 aMaxElems,
6685 nsTArray<nsCSSValue> &aOutput)
6687 for (PRUint16 index = 0; index < aMaxElems; ++index) {
6688 nsCSSValue newValue;
6689 if (!ParseVariant(newValue, aVariantMask[index], nsnull))
6690 return PR_FALSE;
6692 if (!aOutput.AppendElement(newValue)) {
6693 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6694 return PR_FALSE;
6697 // See whether to continue or whether to look for end of function.
6698 if (!ExpectSymbol(',', PR_TRUE)) {
6699 // We need to read the closing parenthesis, and also must take care
6700 // that we haven't read too few symbols.
6701 return ExpectSymbol(')', PR_TRUE) && (index + 1) >= aMinElems;
6705 // If we're here, we finished looping without hitting the end, so we read too
6706 // many elements.
6707 return PR_FALSE;
6710 /* Parses a function [ input of the form (a [, b]*) ] and stores it
6711 * as an nsCSSValue that holds a function of the form
6712 * function-name arg1 arg2 ... argN
6714 * On error, the return value is PR_FALSE.
6716 * @param aFunction The name of the function that we're reading.
6717 * @param aAllowedTypes An array of values corresponding to the legal
6718 * types for each element in the function. The zeroth element in the
6719 * array corresponds to the first function parameter, etc. The length
6720 * of this array _must_ be greater than or equal to aMaxElems or the
6721 * behavior is undefined.
6722 * @param aMinElems Minimum number of elements to read. Reading fewer than
6723 * this many elements will result in the function failing.
6724 * @param aMaxElems Maximum number of elements to read. Reading more than
6725 * this many elements will result in the function failing.
6726 * @param aValue (out) The value that was parsed.
6728 PRBool
6729 CSSParserImpl::ParseFunction(const nsString &aFunction,
6730 const PRInt32 aAllowedTypes[],
6731 PRUint16 aMinElems, PRUint16 aMaxElems,
6732 nsCSSValue &aValue)
6734 typedef nsTArray<nsCSSValue>::size_type arrlen_t;
6736 /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
6737 * elements stored in the the nsCSSValue::Array.
6739 static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
6741 /* Make a copy of the function name, since the reference is _probably_ to
6742 * mToken.mIdent, which is going to get overwritten during the course of this
6743 * function.
6745 nsString functionName(aFunction);
6747 /* First things first... read the parenthesis. If it fails, abort. */
6748 if (!ExpectSymbol('(', PR_TRUE))
6749 return PR_FALSE;
6751 /* Read in a list of values as an nsTArray, failing if we can't or if
6752 * it's out of bounds.
6754 nsTArray<nsCSSValue> foundValues;
6755 if (!ParseFunctionInternals(aAllowedTypes, aMinElems, aMaxElems,
6756 foundValues))
6757 return PR_FALSE;
6759 /* Now, convert this nsTArray into an nsCSSValue::Array object.
6760 * We'll need N + 1 spots, one for the function name and the rest for the
6761 * arguments. In case the user has given us more than 2^16 - 2 arguments,
6762 * we'll truncate them at 2^16 - 2 arguments.
6764 PRUint16 numElements = (foundValues.Length() <= MAX_ALLOWED_ELEMS ?
6765 foundValues.Length() + 1 : MAX_ALLOWED_ELEMS);
6766 nsRefPtr<nsCSSValue::Array> convertedArray =
6767 nsCSSValue::Array::Create(numElements);
6768 if (!convertedArray) {
6769 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6770 return PR_FALSE;
6773 /* Copy things over. */
6774 convertedArray->Item(0).SetStringValue(functionName, eCSSUnit_String);
6775 for (PRUint16 index = 0; index + 1 < numElements; ++index)
6776 convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
6778 /* Fill in the outparam value with the array. */
6779 aValue.SetArrayValue(convertedArray, eCSSUnit_Function);
6781 /* Return it! */
6782 return PR_TRUE;
6786 * Given a token, determines the minimum and maximum number of function
6787 * parameters to read, along with the mask that should be used to read
6788 * those function parameters. If the token isn't a transform function,
6789 * returns an error.
6791 * @param aToken The token identifying the function.
6792 * @param aMinElems [out] The minimum number of elements to read.
6793 * @param aMaxElems [out] The maximum number of elements to read
6794 * @param aVariantMask [out] The variant mask to use during parsing
6795 * @return Whether the information was loaded successfully.
6797 static PRBool GetFunctionParseInformation(nsCSSKeyword aToken,
6798 PRUint16 &aMinElems,
6799 PRUint16 &aMaxElems,
6800 const PRInt32 *& aVariantMask)
6802 /* These types represent the common variant masks that will be used to
6803 * parse out the individual functions. The order in the enumeration
6804 * must match the order in which the masks are declared.
6806 enum { eLengthPercent,
6807 eTwoLengthPercents,
6808 eAngle,
6809 eTwoAngles,
6810 eNumber,
6811 eTwoNumbers,
6812 eMatrix,
6813 eNumVariantMasks };
6814 static const PRInt32 kMaxElemsPerFunction = 6;
6815 static const PRInt32 kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
6816 {VARIANT_LENGTH | VARIANT_PERCENT},
6817 {VARIANT_LENGTH | VARIANT_PERCENT, VARIANT_LENGTH | VARIANT_PERCENT},
6818 {VARIANT_ANGLE},
6819 {VARIANT_ANGLE, VARIANT_ANGLE},
6820 {VARIANT_NUMBER},
6821 {VARIANT_NUMBER, VARIANT_NUMBER},
6822 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
6823 VARIANT_LENGTH | VARIANT_PERCENT, VARIANT_LENGTH | VARIANT_PERCENT}};
6825 #ifdef DEBUG
6826 static const PRUint8 kVariantMaskLengths[eNumVariantMasks] =
6827 {1, 2, 1, 2, 1, 2, 6};
6828 #endif
6830 PRInt32 variantIndex = eNumVariantMasks;
6832 switch (aToken) {
6833 case eCSSKeyword_translatex:
6834 /* Exactly one length or percent. */
6835 variantIndex = eLengthPercent;
6836 aMinElems = 1U;
6837 aMaxElems = 1U;
6838 break;
6839 case eCSSKeyword_translatey:
6840 /* Exactly one length or percent. */
6841 variantIndex = eLengthPercent;
6842 aMinElems = 1U;
6843 aMaxElems = 1U;
6844 break;
6845 case eCSSKeyword_scalex:
6846 /* Exactly one scale factor. */
6847 variantIndex = eNumber;
6848 aMinElems = 1U;
6849 aMaxElems = 1U;
6850 break;
6851 case eCSSKeyword_scaley:
6852 /* Exactly one scale factor. */
6853 variantIndex = eNumber;
6854 aMinElems = 1U;
6855 aMaxElems = 1U;
6856 break;
6857 case eCSSKeyword_rotate:
6858 /* Exactly one angle. */
6859 variantIndex = eAngle;
6860 aMinElems = 1U;
6861 aMaxElems = 1U;
6862 break;
6863 case eCSSKeyword_translate:
6864 /* One or two lengths or percents. */
6865 variantIndex = eTwoLengthPercents;
6866 aMinElems = 1U;
6867 aMaxElems = 2U;
6868 break;
6869 case eCSSKeyword_skew:
6870 /* Exactly one or two angles. */
6871 variantIndex = eTwoAngles;
6872 aMinElems = 1U;
6873 aMaxElems = 2U;
6874 break;
6875 case eCSSKeyword_scale:
6876 /* One or two scale factors. */
6877 variantIndex = eTwoNumbers;
6878 aMinElems = 1U;
6879 aMaxElems = 2U;
6880 break;
6881 case eCSSKeyword_skewx:
6882 /* Exactly one angle. */
6883 variantIndex = eAngle;
6884 aMinElems = 1U;
6885 aMaxElems = 1U;
6886 break;
6887 case eCSSKeyword_skewy:
6888 /* Exactly one angle. */
6889 variantIndex = eAngle;
6890 aMinElems = 1U;
6891 aMaxElems = 1U;
6892 break;
6893 case eCSSKeyword_matrix:
6894 /* Six values, which can be numbers, lengths, or percents. */
6895 variantIndex = eMatrix;
6896 aMinElems = 6U;
6897 aMaxElems = 6U;
6898 break;
6899 default:
6900 /* Oh dear, we didn't match. Report an error. */
6901 return PR_FALSE;
6904 NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
6905 NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
6906 NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
6907 NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
6908 NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
6909 #ifdef DEBUG
6910 NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
6911 "Invalid aMaxElems for this variant mask.");
6912 #endif
6914 // Convert the index into a mask.
6915 aVariantMask = kVariantMasks[variantIndex];
6917 return PR_TRUE;
6921 /* Reads a single transform function from the tokenizer stream, reporting an
6922 * error if something goes wrong.
6924 PRBool CSSParserImpl::ReadSingleTransform(nsCSSValueList **& aTail)
6926 typedef nsTArray<nsCSSValue>::size_type arrlen_t;
6928 if (!GetToken(PR_TRUE))
6929 return PR_FALSE;
6931 /* Check to make sure that we've read a function. */
6932 if (mToken.mType != eCSSToken_Function) {
6933 UngetToken();
6934 return PR_FALSE;
6937 /* Load up the variant mask information for ParseFunction. If we can't,
6938 * abort.
6940 const PRInt32* variantMask;
6941 PRUint16 minElems, maxElems;
6942 if (!GetFunctionParseInformation(nsCSSKeywords::LookupKeyword(mToken.mIdent),
6943 minElems, maxElems, variantMask))
6944 return PR_FALSE;
6946 /* Create a cell to populate, fail if we're out of memory. */
6947 nsAutoPtr<nsCSSValue> newCell(new nsCSSValue);
6948 if (!newCell) {
6949 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6950 return PR_FALSE;
6953 /* Try reading things in, failing if we can't */
6954 if (!ParseFunction(mToken.mIdent, variantMask, minElems, maxElems, *newCell))
6955 return PR_FALSE;
6957 /* Wrap up our result in an nsCSSValueList cell. */
6958 nsAutoPtr<nsCSSValueList> toAppend(new nsCSSValueList);
6959 if (!toAppend)
6960 return PR_FALSE;
6962 toAppend->mValue = *newCell;
6964 /* Chain the element to the end of the transform list, then update the
6965 * list.
6967 *aTail = toAppend.forget();
6968 aTail = &(*aTail)->mNext;
6970 /* It worked! Return true. */
6971 return PR_TRUE;
6974 /* Parses a -moz-transform property list by continuously reading in properties
6975 * and constructing a matrix from it.
6977 PRBool CSSParserImpl::ParseMozTransform()
6979 mTempData.mDisplay.mTransform = nsnull;
6981 /* First, check to see if this is some sort of keyword - none, inherit,
6982 * or initial.
6984 nsCSSValue keywordValue;
6985 if (ParseVariant(keywordValue, VARIANT_INHERIT | VARIANT_NONE, nsnull)) {
6986 /* Looks like it is. Make a new value list and fill it in, failing if
6987 * we can't.
6989 mTempData.mDisplay.mTransform = new nsCSSValueList;
6990 if (!mTempData.mDisplay.mTransform) {
6991 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6992 return PR_FALSE;
6995 /* Inform the parser that everything worked. */
6996 mTempData.mDisplay.mTransform->mValue = keywordValue;
6997 mTempData.SetPropertyBit(eCSSProperty__moz_transform);
6998 return PR_TRUE;
7001 /* We will read a nonempty list of transforms. Thus we'll read in a
7002 * transform, then continuously read transforms until we're no longer
7003 * able to do so.
7005 nsCSSValueList *transformList = nsnull;
7006 nsCSSValueList **tail = &transformList;
7007 do {
7008 /* Try reading a transform. If we fail to do so, abort. */
7009 if (!ReadSingleTransform(tail)) {
7010 delete transformList;
7011 return PR_FALSE;
7014 while (!CheckEndProperty());
7016 /* Confirm that this is the end of the property and set error code
7017 * appropriately otherwise.
7019 if (!ExpectEndProperty()) {
7020 delete transformList;
7021 return PR_FALSE;
7024 /* Validate our data. */
7025 NS_ASSERTION(transformList, "Didn't read any transforms!");
7027 mTempData.SetPropertyBit(eCSSProperty__moz_transform);
7028 mTempData.mDisplay.mTransform = transformList;
7030 return PR_TRUE;
7033 PRBool CSSParserImpl::ParseMozTransformOrigin()
7035 /* Read in a box position, fail if we can't. */
7036 if (!ParseBoxPosition(mTempData.mDisplay.mTransformOrigin))
7037 return PR_FALSE;
7039 /* Set the property bit and return. */
7040 mTempData.SetPropertyBit(eCSSProperty__moz_transform_origin);
7041 return PR_TRUE;
7044 PRBool
7045 CSSParserImpl::ParseFamily(nsCSSValue& aValue)
7047 if (!GetToken(PR_TRUE))
7048 return PR_FALSE;
7050 if (eCSSToken_Ident == mToken.mType) {
7051 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
7052 if (keyword == eCSSKeyword_inherit) {
7053 aValue.SetInheritValue();
7054 return PR_TRUE;
7056 if (keyword == eCSSKeyword__moz_initial) {
7057 aValue.SetInitialValue();
7058 return PR_TRUE;
7060 if (keyword == eCSSKeyword__moz_use_system_font &&
7061 !IsParsingCompoundProperty()) {
7062 aValue.SetSystemFontValue();
7063 return PR_TRUE;
7067 UngetToken();
7069 nsAutoString family;
7070 for (;;) {
7071 if (!ParseOneFamily(family))
7072 return PR_FALSE;
7074 if (!ExpectSymbol(',', PR_TRUE))
7075 break;
7077 family.Append(PRUnichar(','));
7080 if (family.IsEmpty()) {
7081 return PR_FALSE;
7083 aValue.SetStringValue(family, eCSSUnit_String);
7084 return PR_TRUE;
7087 // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
7088 // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
7089 // local-src: 'local(' ( string | ident ) ')'
7091 PRBool
7092 CSSParserImpl::ParseFontSrc(nsCSSValue& aValue)
7094 // could we maybe turn nsCSSValue::Array into nsTArray<nsCSSValue>?
7095 nsTArray<nsCSSValue> values;
7096 nsCSSValue cur;
7097 for (;;) {
7098 if (!GetToken(PR_TRUE))
7099 break;
7101 if (mToken.mType == eCSSToken_Function &&
7102 mToken.mIdent.LowerCaseEqualsLiteral("url")) {
7103 if (!ParseURL(cur))
7104 return PR_FALSE;
7105 values.AppendElement(cur);
7106 if (!ParseFontSrcFormat(values))
7107 return PR_FALSE;
7109 } else if (mToken.mType == eCSSToken_Function &&
7110 mToken.mIdent.LowerCaseEqualsLiteral("local")) {
7111 // css3-fonts does not specify a formal grammar for local().
7112 // The text permits both unquoted identifiers and quoted
7113 // strings. We resolve this ambiguity in the spec by
7114 // assuming that the appropriate production is a single
7115 // <family-name>, possibly surrounded by whitespace.
7117 nsAutoString family;
7118 if (!ExpectSymbol('(', PR_FALSE))
7119 return PR_FALSE;
7120 if (!ParseOneFamily(family))
7121 return PR_FALSE;
7122 if (!ExpectSymbol(')', PR_TRUE))
7123 return PR_FALSE;
7125 // the style parameters to the nsFont constructor are ignored,
7126 // because it's only being used to call EnumerateFamilies
7127 nsFont font(family, 0, 0, 0, 0, 0);
7128 ExtractFirstFamilyData dat;
7130 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
7131 if (!dat.mGood)
7132 return PR_FALSE;
7134 cur.SetStringValue(dat.mFamilyName, eCSSUnit_Local_Font);
7135 values.AppendElement(cur);
7136 } else {
7137 return PR_FALSE;
7140 if (!ExpectSymbol(',', PR_TRUE))
7141 break;
7144 nsRefPtr<nsCSSValue::Array> srcVals
7145 = nsCSSValue::Array::Create(values.Length());
7146 if (!srcVals)
7147 return PR_FALSE;
7149 PRUint32 i;
7150 for (i = 0; i < values.Length(); i++)
7151 srcVals->Item(i) = values[i];
7152 aValue.SetArrayValue(srcVals, eCSSUnit_Array);
7153 return PR_TRUE;
7156 PRBool
7157 CSSParserImpl::ParseFontSrcFormat(nsTArray<nsCSSValue> & values)
7159 if (!GetToken(PR_TRUE))
7160 return PR_TRUE; // EOF harmless here
7161 if (mToken.mType != eCSSToken_Function ||
7162 !mToken.mIdent.LowerCaseEqualsLiteral("format")) {
7163 UngetToken();
7164 return PR_TRUE;
7166 if (!ExpectSymbol('(', PR_FALSE))
7167 return PR_FALSE;
7169 do {
7170 if (!GetToken(PR_TRUE))
7171 return PR_FALSE;
7173 if (mToken.mType != eCSSToken_String)
7174 return PR_FALSE;
7176 nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format);
7177 values.AppendElement(cur);
7178 } while (ExpectSymbol(',', PR_TRUE));
7180 return ExpectSymbol(')', PR_TRUE);
7183 // font-ranges: urange ( ',' urange )*
7184 PRBool
7185 CSSParserImpl::ParseFontRanges(nsCSSValue& aValue)
7187 // not currently implemented (bug 443976)
7188 return PR_FALSE;
7191 PRBool
7192 CSSParserImpl::ParseListStyle()
7194 const PRInt32 numProps = 3;
7195 static const nsCSSProperty listStyleIDs[] = {
7196 eCSSProperty_list_style_type,
7197 eCSSProperty_list_style_position,
7198 eCSSProperty_list_style_image
7201 nsCSSValue values[numProps];
7202 PRInt32 index;
7203 PRInt32 found = ParseChoice(values, listStyleIDs, numProps);
7204 if ((found < 1) || (PR_FALSE == ExpectEndProperty())) {
7205 return PR_FALSE;
7208 // Provide default values
7209 if ((found & 1) == 0) {
7210 values[0].SetIntValue(NS_STYLE_LIST_STYLE_DISC, eCSSUnit_Enumerated);
7212 if ((found & 2) == 0) {
7213 values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE, eCSSUnit_Enumerated);
7215 if ((found & 4) == 0) {
7216 values[2].SetNoneValue();
7219 for (index = 0; index < numProps; index++) {
7220 AppendValue(listStyleIDs[index], values[index]);
7222 return PR_TRUE;
7225 PRBool
7226 CSSParserImpl::ParseMargin()
7228 static const nsCSSProperty kMarginSideIDs[] = {
7229 eCSSProperty_margin_top,
7230 eCSSProperty_margin_right_value,
7231 eCSSProperty_margin_bottom,
7232 eCSSProperty_margin_left_value
7234 static const nsCSSProperty kMarginSources[] = {
7235 eCSSProperty_margin_left_ltr_source,
7236 eCSSProperty_margin_left_rtl_source,
7237 eCSSProperty_margin_right_ltr_source,
7238 eCSSProperty_margin_right_rtl_source,
7239 eCSSProperty_UNKNOWN
7242 // do this now, in case 4 values weren't specified
7243 InitBoxPropsAsPhysical(kMarginSources);
7244 return ParseBoxProperties(mTempData.mMargin.mMargin,
7245 kMarginSideIDs);
7248 PRBool
7249 CSSParserImpl::ParseMarks(nsCSSValue& aValue)
7251 if (ParseVariant(aValue, VARIANT_HOK, nsCSSProps::kPageMarksKTable)) {
7252 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
7253 if (PR_FALSE == CheckEndProperty()) {
7254 nsCSSValue second;
7255 if (ParseEnum(second, nsCSSProps::kPageMarksKTable)) {
7256 aValue.SetIntValue(aValue.GetIntValue() | second.GetIntValue(), eCSSUnit_Enumerated);
7257 return PR_TRUE;
7259 return PR_FALSE;
7262 return PR_TRUE;
7264 return PR_FALSE;
7267 PRBool
7268 CSSParserImpl::ParseOutline()
7270 const PRInt32 numProps = 3;
7271 static const nsCSSProperty kOutlineIDs[] = {
7272 eCSSProperty_outline_color,
7273 eCSSProperty_outline_style,
7274 eCSSProperty_outline_width
7277 nsCSSValue values[numProps];
7278 PRInt32 found = ParseChoice(values, kOutlineIDs, numProps);
7279 if ((found < 1) || (PR_FALSE == ExpectEndProperty())) {
7280 return PR_FALSE;
7283 // Provide default values
7284 if ((found & 1) == 0) {
7285 #ifdef GFX_HAS_INVERT
7286 values[0].SetIntValue(NS_STYLE_COLOR_INVERT, eCSSUnit_Enumerated);
7287 #else
7288 values[0].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
7289 #endif
7291 if ((found & 2) == 0) {
7292 values[1].SetNoneValue();
7294 if ((found & 4) == 0) {
7295 values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
7298 PRInt32 index;
7299 for (index = 0; index < numProps; index++) {
7300 AppendValue(kOutlineIDs[index], values[index]);
7302 return PR_TRUE;
7305 PRBool
7306 CSSParserImpl::ParseOverflow()
7308 nsCSSValue overflow;
7309 if (!ParseVariant(overflow, VARIANT_AHK,
7310 nsCSSProps::kOverflowKTable) ||
7311 !ExpectEndProperty())
7312 return PR_FALSE;
7314 nsCSSValue overflowX(overflow);
7315 nsCSSValue overflowY(overflow);
7316 if (eCSSUnit_Enumerated == overflow.GetUnit())
7317 switch(overflow.GetIntValue()) {
7318 case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
7319 overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
7320 overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
7321 break;
7322 case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
7323 overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
7324 overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
7325 break;
7327 AppendValue(eCSSProperty_overflow_x, overflowX);
7328 AppendValue(eCSSProperty_overflow_y, overflowY);
7329 return PR_TRUE;
7332 PRBool
7333 CSSParserImpl::ParsePadding()
7335 static const nsCSSProperty kPaddingSideIDs[] = {
7336 eCSSProperty_padding_top,
7337 eCSSProperty_padding_right_value,
7338 eCSSProperty_padding_bottom,
7339 eCSSProperty_padding_left_value
7341 static const nsCSSProperty kPaddingSources[] = {
7342 eCSSProperty_padding_left_ltr_source,
7343 eCSSProperty_padding_left_rtl_source,
7344 eCSSProperty_padding_right_ltr_source,
7345 eCSSProperty_padding_right_rtl_source,
7346 eCSSProperty_UNKNOWN
7349 // do this now, in case 4 values weren't specified
7350 InitBoxPropsAsPhysical(kPaddingSources);
7351 return ParseBoxProperties(mTempData.mMargin.mPadding,
7352 kPaddingSideIDs);
7355 PRBool
7356 CSSParserImpl::ParsePause()
7358 nsCSSValue before;
7359 if (ParseSingleValueProperty(before, eCSSProperty_pause_before)) {
7360 if (eCSSUnit_Inherit != before.GetUnit() && eCSSUnit_Initial != before.GetUnit()) {
7361 nsCSSValue after;
7362 if (ParseSingleValueProperty(after, eCSSProperty_pause_after)) {
7363 if (ExpectEndProperty()) {
7364 AppendValue(eCSSProperty_pause_before, before);
7365 AppendValue(eCSSProperty_pause_after, after);
7366 return PR_TRUE;
7368 return PR_FALSE;
7371 if (ExpectEndProperty()) {
7372 AppendValue(eCSSProperty_pause_before, before);
7373 AppendValue(eCSSProperty_pause_after, before);
7374 return PR_TRUE;
7377 return PR_FALSE;
7380 PRBool
7381 CSSParserImpl::ParseQuotes()
7383 nsCSSValue open;
7384 if (ParseVariant(open, VARIANT_HOS, nsnull)) {
7385 if (eCSSUnit_String == open.GetUnit()) {
7386 nsCSSValuePairList* quotesHead = new nsCSSValuePairList();
7387 nsCSSValuePairList* quotes = quotesHead;
7388 if (nsnull == quotes) {
7389 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7390 return PR_FALSE;
7392 quotes->mXValue = open;
7393 while (nsnull != quotes) {
7394 // get mandatory close
7395 if (ParseVariant(quotes->mYValue, VARIANT_STRING,
7396 nsnull)) {
7397 if (CheckEndProperty()) {
7398 mTempData.SetPropertyBit(eCSSProperty_quotes);
7399 mTempData.mContent.mQuotes = quotesHead;
7400 return PR_TRUE;
7402 // look for another open
7403 if (ParseVariant(open, VARIANT_STRING, nsnull)) {
7404 quotes->mNext = new nsCSSValuePairList();
7405 quotes = quotes->mNext;
7406 if (nsnull != quotes) {
7407 quotes->mXValue = open;
7408 continue;
7410 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7413 break;
7415 delete quotesHead;
7416 return PR_FALSE;
7418 if (ExpectEndProperty()) {
7419 nsCSSValuePairList* quotesHead = new nsCSSValuePairList();
7420 quotesHead->mXValue = open;
7421 mTempData.mContent.mQuotes = quotesHead;
7422 mTempData.SetPropertyBit(eCSSProperty_quotes);
7423 return PR_TRUE;
7426 return PR_FALSE;
7429 PRBool
7430 CSSParserImpl::ParseSize()
7432 nsCSSValue width;
7433 if (ParseVariant(width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) {
7434 if (width.IsLengthUnit()) {
7435 nsCSSValue height;
7436 if (ParseVariant(height, VARIANT_LENGTH, nsnull)) {
7437 if (ExpectEndProperty()) {
7438 mTempData.mPage.mSize.mXValue = width;
7439 mTempData.mPage.mSize.mYValue = height;
7440 mTempData.SetPropertyBit(eCSSProperty_size);
7441 return PR_TRUE;
7443 return PR_FALSE;
7446 if (ExpectEndProperty()) {
7447 mTempData.mPage.mSize.SetBothValuesTo(width);
7448 mTempData.SetPropertyBit(eCSSProperty_size);
7449 return PR_TRUE;
7452 return PR_FALSE;
7455 PRBool
7456 CSSParserImpl::ParseTextDecoration(nsCSSValue& aValue)
7458 if (ParseVariant(aValue, VARIANT_HOK, nsCSSProps::kTextDecorationKTable)) {
7459 if (eCSSUnit_Enumerated == aValue.GetUnit()) { // look for more keywords
7460 PRInt32 intValue = aValue.GetIntValue();
7461 nsCSSValue keyword;
7462 PRInt32 index;
7463 for (index = 0; index < 3; index++) {
7464 if (ParseEnum(keyword, nsCSSProps::kTextDecorationKTable)) {
7465 intValue |= keyword.GetIntValue();
7467 else {
7468 break;
7471 aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
7473 return PR_TRUE;
7475 return PR_FALSE;
7478 nsCSSValueList*
7479 CSSParserImpl::ParseCSSShadowList(PRBool aUsesSpread)
7481 nsAutoParseCompoundProperty compound(this);
7483 // Parses x, y, radius, color (in two possible orders)
7484 // This parses the input into a list. Either it contains just a "none" or
7485 // "inherit" value, or a list of arrays.
7486 // The resulting arrays will always contain the above order, with color and
7487 // radius as null values as needed
7488 enum {
7489 IndexX,
7490 IndexY,
7491 IndexRadius,
7492 IndexSpread,
7493 IndexColor
7496 nsCSSValueList *list = nsnull;
7497 for (nsCSSValueList **curp = &list, *cur; ; curp = &cur->mNext) {
7498 cur = *curp = new nsCSSValueList();
7499 if (!cur) {
7500 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7501 break;
7503 if (!ParseVariant(cur->mValue,
7504 (cur == list) ? VARIANT_HC | VARIANT_LENGTH | VARIANT_NONE
7505 : VARIANT_COLOR | VARIANT_LENGTH,
7506 nsnull)) {
7507 break;
7510 nsCSSUnit unit = cur->mValue.GetUnit();
7511 if (unit != eCSSUnit_None && unit != eCSSUnit_Inherit &&
7512 unit != eCSSUnit_Initial) {
7513 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(5);
7514 if (!val) {
7515 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7516 break;
7518 PRBool haveColor = PR_FALSE;
7519 if (cur->mValue.IsLengthUnit()) {
7520 val->Item(IndexX) = cur->mValue;
7521 } else {
7522 // Must be a color (as string or color value)
7523 NS_ASSERTION(unit == eCSSUnit_String || unit == eCSSUnit_Color ||
7524 unit == eCSSUnit_EnumColor,
7525 "Must be a color value (named color, numeric color, "
7526 "or system color)");
7527 haveColor = PR_TRUE;
7528 val->Item(IndexColor) = cur->mValue;
7530 // Parse the X coordinate
7531 if (!ParseVariant(val->Item(IndexX), VARIANT_LENGTH,
7532 nsnull)) {
7533 break;
7536 cur->mValue.SetArrayValue(val, eCSSUnit_Array);
7538 // Y coordinate; this one is not optional
7539 if (!ParseVariant(val->Item(IndexY), VARIANT_LENGTH, nsnull)) {
7540 break;
7543 // Optional radius. Ignore errors except if they pass a negative
7544 // value which we must reject. If we use ParsePositiveVariant we can't
7545 // tell the difference between an unspecified radius and a negative
7546 // radius, so that's why we don't use it.
7547 if (ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH, nsnull) &&
7548 val->Item(IndexRadius).GetFloatValue() < 0) {
7549 break;
7552 if (aUsesSpread) {
7553 // Optional spread (ignore errors)
7554 ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH,
7555 nsnull);
7558 if (!haveColor) {
7559 // Optional color (ignore errors)
7560 ParseVariant(val->Item(IndexColor), VARIANT_COLOR,
7561 nsnull);
7564 // Might be at a comma now
7565 if (ExpectSymbol(',', PR_TRUE)) {
7566 // Go to next value
7567 continue;
7571 if (!ExpectEndProperty()) {
7572 // If we don't have a comma to delimit the next value, we
7573 // must be at the end of the property. Otherwise we've hit
7574 // something else, which is an error.
7575 break;
7578 // Only success case here, since having the failure case at the
7579 // end allows more sharing of code.
7580 return list;
7582 // Have failure case at the end so we can |break| to get to it.
7583 delete list;
7584 return nsnull;
7587 PRBool
7588 CSSParserImpl::ParseTextShadow()
7590 nsCSSValueList* list = ParseCSSShadowList(PR_FALSE);
7591 if (!list)
7592 return PR_FALSE;
7594 mTempData.SetPropertyBit(eCSSProperty_text_shadow);
7595 mTempData.mText.mTextShadow = list;
7596 return PR_TRUE;
7599 PRBool
7600 CSSParserImpl::ParseBoxShadow()
7602 nsCSSValueList* list = ParseCSSShadowList(PR_TRUE);
7603 if (!list)
7604 return PR_FALSE;
7606 mTempData.SetPropertyBit(eCSSProperty_box_shadow);
7607 mTempData.mMargin.mBoxShadow = list;
7608 return PR_TRUE;
7611 PRBool
7612 CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix,
7613 PRInt32* aNameSpaceID)
7615 NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here");
7617 PRInt32 nameSpaceID = kNameSpaceID_Unknown;
7618 if (mNameSpaceMap) {
7619 // user-specified identifiers are case-sensitive (bug 416106)
7620 nsCOMPtr<nsIAtom> prefix = do_GetAtom(aPrefix);
7621 nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
7623 // else no declared namespaces
7625 NS_ASSERTION(nameSpaceID != kNameSpaceID_None, "Shouldn't happen!");
7627 if (kNameSpaceID_Unknown == nameSpaceID) { // unknown prefix, dump it
7628 const PRUnichar *params[] = {
7629 aPrefix.get()
7631 REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, params);
7632 if (mUnresolvablePrefixException)
7633 mScanner.SetLowLevelError(NS_ERROR_DOM_NAMESPACE_ERR);
7634 return PR_FALSE;
7637 *aNameSpaceID = nameSpaceID;
7638 return PR_TRUE;
7641 void
7642 CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector)
7644 aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
7645 if (mNameSpaceMap) { // look for default namespace
7646 PRInt32 defaultID = mNameSpaceMap->FindNameSpaceID(nsnull);
7647 if (defaultID != kNameSpaceID_None) {
7648 aSelector.SetNameSpace(defaultID);
7653 #ifdef MOZ_SVG
7654 PRBool
7655 CSSParserImpl::ParsePaint(nsCSSValuePair* aResult,
7656 nsCSSProperty aPropID)
7658 if (!ParseVariant(aResult->mXValue,
7659 VARIANT_HC | VARIANT_NONE | VARIANT_URL,
7660 nsnull))
7661 return PR_FALSE;
7663 if (aResult->mXValue.GetUnit() == eCSSUnit_URL) {
7664 if (!ParseVariant(aResult->mYValue, VARIANT_COLOR | VARIANT_NONE,
7665 nsnull))
7666 aResult->mYValue.SetColorValue(NS_RGB(0, 0, 0));
7667 } else {
7668 aResult->mYValue = aResult->mXValue;
7671 if (!ExpectEndProperty())
7672 return PR_FALSE;
7674 mTempData.SetPropertyBit(aPropID);
7675 return PR_TRUE;
7678 PRBool
7679 CSSParserImpl::ParseDasharray()
7681 nsCSSValue value;
7682 if (ParseVariant(value, VARIANT_HLPN | VARIANT_NONE, nsnull)) {
7683 nsCSSValueList *listHead = new nsCSSValueList;
7684 nsCSSValueList *list = listHead;
7685 if (!list) {
7686 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7687 return PR_FALSE;
7690 list->mValue = value;
7692 for (;;) {
7693 if (CheckEndProperty()) {
7694 mTempData.SetPropertyBit(eCSSProperty_stroke_dasharray);
7695 mTempData.mSVG.mStrokeDasharray = listHead;
7696 return PR_TRUE;
7699 if (eCSSUnit_Inherit == value.GetUnit() ||
7700 eCSSUnit_Initial == value.GetUnit() ||
7701 eCSSUnit_None == value.GetUnit())
7702 break;
7704 if (!ExpectSymbol(',', PR_TRUE))
7705 break;
7707 if (!ParseVariant(value,
7708 VARIANT_LENGTH | VARIANT_PERCENT | VARIANT_NUMBER,
7709 nsnull))
7710 break;
7712 list->mNext = new nsCSSValueList;
7713 list = list->mNext;
7714 if (list)
7715 list->mValue = value;
7716 else {
7717 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7718 break;
7721 delete listHead;
7723 return PR_FALSE;
7726 PRBool
7727 CSSParserImpl::ParseMarker()
7729 nsCSSValue marker;
7730 if (ParseSingleValueProperty(marker, eCSSProperty_marker_end)) {
7731 if (ExpectEndProperty()) {
7732 AppendValue(eCSSProperty_marker_end, marker);
7733 AppendValue(eCSSProperty_marker_mid, marker);
7734 AppendValue(eCSSProperty_marker_start, marker);
7735 return PR_TRUE;
7738 return PR_FALSE;
7740 #endif