Back to index

lightning-sunbird  0.9+nobinonly
nsCSSParser.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   emk <VYV03354@nifty.ne.jp>
00024  *   Daniel Glazman <glazman@netscape.com>
00025  *   L. David Baron <dbaron@dbaron.org>
00026  *   Boris Zbarsky <bzbarsky@mit.edu>
00027  *   Mats Palmgren <mats.palmgren@bredband.net>
00028  *
00029  * Alternatively, the contents of this file may be used under the terms of
00030  * either of the GNU General Public License Version 2 or later (the "GPL"),
00031  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00032  * in which case the provisions of the GPL or the LGPL are applicable instead
00033  * of those above. If you wish to allow use of your version of this file only
00034  * under the terms of either the GPL or the LGPL, and not to allow others to
00035  * use your version of this file under the terms of the MPL, indicate your
00036  * decision by deleting the provisions above and replace them with the notice
00037  * and other provisions required by the GPL or the LGPL. If you do not delete
00038  * the provisions above, a recipient may use your version of this file under
00039  * the terms of any one of the MPL, the GPL or the LGPL.
00040  *
00041  * ***** END LICENSE BLOCK ***** */
00042 #include "nsICSSParser.h"
00043 #include "nsCSSProps.h"
00044 #include "nsCSSKeywords.h"
00045 #include "nsCSSScanner.h"
00046 #include "nsICSSLoader.h"
00047 #include "nsICSSStyleRule.h"
00048 #include "nsICSSImportRule.h"
00049 #include "nsCSSRules.h"
00050 #include "nsICSSNameSpaceRule.h"
00051 #include "nsIUnicharInputStream.h"
00052 #include "nsICSSStyleSheet.h"
00053 #include "nsCSSDeclaration.h"
00054 #include "nsStyleConsts.h"
00055 #include "nsIURL.h"
00056 #include "nsNetUtil.h"
00057 #include "nsCOMPtr.h"
00058 #include "nsString.h"
00059 #include "nsReadableUtils.h"
00060 #include "nsUnicharUtils.h"
00061 #include "nsIAtom.h"
00062 #include "nsVoidArray.h"
00063 #include "nsISupportsArray.h"
00064 #include "nsCOMArray.h"
00065 #include "nsColor.h"
00066 #include "nsStyleConsts.h"
00067 #include "nsLayoutAtoms.h"
00068 #include "nsCSSPseudoClasses.h"
00069 #include "nsCSSPseudoElements.h"
00070 #include "nsCSSAnonBoxes.h"
00071 #include "nsINameSpaceManager.h"
00072 #include "nsXMLNameSpaceMap.h"
00073 #include "nsThemeConstants.h"
00074 #include "nsContentErrors.h"
00075 #include "nsUnitConversion.h"
00076 #include "nsPrintfCString.h"
00077 #include "nsIMediaList.h"
00078 #include "nsILookAndFeel.h"
00079 
00080 #include "prprf.h"
00081 #include "math.h"
00082 
00083 //----------------------------------------------------------------------
00084 
00085 // Your basic top-down recursive descent style parser
00086 class CSSParserImpl : public nsICSSParser,
00087                       public nsICSSParser_MOZILLA_1_8_BRANCH {
00088 public:
00089   CSSParserImpl();
00090   virtual ~CSSParserImpl();
00091 
00092   NS_DECL_ISUPPORTS
00093 
00094   NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet);
00095 
00096   NS_IMETHOD SetCaseSensitive(PRBool aCaseSensitive);
00097 
00098   NS_IMETHOD SetQuirkMode(PRBool aQuirkMode);
00099 
00100 #ifdef  MOZ_SVG
00101   NS_IMETHOD SetSVGMode(PRBool aSVGMode);
00102 #endif
00103 
00104   NS_IMETHOD SetChildLoader(nsICSSLoader* aChildLoader);
00105 
00106   // nsICSSParser_MOZILLA_1_8_BRANCH
00107   NS_IMETHOD Parse(nsIUnicharInputStream* aInput,
00108                    nsIURI*                aSheetURI,
00109                    nsIURI*                aBaseURI,
00110                    PRUint32               aLineNumber,
00111                    PRBool                 aAllowUnsafeRules,
00112                    nsICSSStyleSheet*&     aResult);
00113 
00114   NS_IMETHOD Parse(nsIUnicharInputStream* aInput,
00115                    nsIURI*                aSheetURI,
00116                    nsIURI*                aBaseURI,
00117                    PRUint32               aLineNumber,
00118                    nsICSSStyleSheet*&     aResult);
00119 
00120   NS_IMETHOD ParseStyleAttribute(const nsAString&  aAttributeValue,
00121                                  nsIURI*           aDocURL,
00122                                  nsIURI*           aBaseURL,
00123                                  nsICSSStyleRule** aResult);
00124   
00125   NS_IMETHOD ParseAndAppendDeclaration(const nsAString&  aBuffer,
00126                                        nsIURI*           aSheetURL,
00127                                        nsIURI*           aBaseURL,
00128                                        nsCSSDeclaration* aDeclaration,
00129                                        PRBool            aParseOnlyOneDecl,
00130                                        PRBool*           aChanged,
00131                                        PRBool            aClearOldDecl);
00132 
00133   NS_IMETHOD ParseRule(const nsAString&   aRule,
00134                        nsIURI*            aSheetURL,
00135                        nsIURI*            aBaseURL,
00136                        nsISupportsArray** aResult);
00137 
00138   NS_IMETHOD ParseProperty(const nsCSSProperty aPropID,
00139                            const nsAString& aPropValue,
00140                            nsIURI* aSheetURL,
00141                            nsIURI* aBaseURL,
00142                            nsCSSDeclaration* aDeclaration,
00143                            PRBool* aChanged);
00144 
00145   NS_IMETHOD ParseMediaList(const nsSubstring& aBuffer,
00146                             nsIURI* aURL, // for error reporting
00147                             PRUint32 aLineNumber, // for error reporting
00148                             nsMediaList* aMediaList,
00149                             PRBool aHTMLMode);
00150 
00151   NS_IMETHOD ParseColorString(const nsSubstring& aBuffer,
00152                               nsIURI* aURL, // for error reporting
00153                               PRUint32 aLineNumber, // for error reporting
00154                               PRBool aHandleAlphaColors,
00155                               nscolor* aColor);
00156 
00157   void AppendRule(nsICSSRule* aRule);
00158 
00159 protected:
00160   nsresult InitScanner(nsIUnicharInputStream* aInput, nsIURI* aSheetURI,
00161                        PRUint32 aLineNumber, nsIURI* aBaseURI);
00162   // the caller must hold on to aBuffer until parsing is done
00163   nsresult InitScanner(const nsAString& aBuffer, nsIURI* aSheetURI,
00164                        PRUint32 aLineNumber, nsIURI* aBaseURI);
00165   nsresult ReleaseScanner(void);
00166 
00167   nsresult DoParseMediaList(const nsSubstring& aBuffer,
00168                             nsIURI* aURL, // for error reporting
00169                             PRUint32 aLineNumber, // for error reporting
00170                             nsMediaList* aMediaList);
00171 
00172   PRBool GetToken(nsresult& aErrorCode, PRBool aSkipWS);
00173   PRBool GetURLToken(nsresult& aErrorCode, PRBool aSkipWS);
00174   void UngetToken();
00175 
00176   PRBool ExpectSymbol(nsresult& aErrorCode, PRUnichar aSymbol, PRBool aSkipWS);
00177   PRBool ExpectEndProperty(nsresult& aErrorCode, PRBool aSkipWS);
00178   nsString* NextIdent(nsresult& aErrorCode);
00179   void SkipUntil(nsresult& aErrorCode, PRUnichar aStopSymbol);
00180   void SkipRuleSet(nsresult& aErrorCode);
00181   PRBool SkipAtRule(nsresult& aErrorCode);
00182   PRBool SkipDeclaration(nsresult& aErrorCode, PRBool aCheckForBraces);
00183   PRBool GetNonCloseParenToken(nsresult& aErrorCode, PRBool aSkipWS);
00184 
00185   PRBool PushGroup(nsICSSGroupRule* aRule);
00186   void PopGroup(void);
00187 
00188   PRBool ParseRuleSet(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
00189   PRBool ParseAtRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
00190   PRBool ParseCharsetRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
00191   PRBool ParseImportRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
00192   PRBool GatherURL(nsresult& aErrorCode, nsString& aURL);
00193   PRBool GatherMedia(nsresult& aErrorCode, nsMediaList* aMedia,
00194                      PRUnichar aStopSymbol);
00195   PRBool ProcessImport(nsresult& aErrorCode,
00196                        const nsString& aURLSpec,
00197                        nsMediaList* aMedia,
00198                        RuleAppendFunc aAppendFunc,
00199                        void* aProcessData);
00200   PRBool ParseGroupRule(nsresult& aErrorCode, nsICSSGroupRule* aRule, RuleAppendFunc aAppendFunc, void* aProcessData);
00201   PRBool ParseMediaRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
00202   PRBool ParseMozDocumentRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
00203   PRBool ParseNameSpaceRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
00204   PRBool ProcessNameSpace(nsresult& aErrorCode, const nsString& aPrefix, 
00205                           const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
00206                           void* aProcessData);
00207   PRBool ParseFontFaceRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
00208   PRBool ParsePageRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
00209 
00210   enum nsSelectorParsingStatus {
00211     // we have parsed a selector and we saw a token that cannot be part of a selector:
00212     eSelectorParsingStatus_Done,
00213     // we should continue parsing the selector:
00214     eSelectorParsingStatus_Continue,
00215     // same as "Done" but we did not find a selector:
00216     eSelectorParsingStatus_Empty,
00217     // we saw an unexpected token or token value,
00218     // or we saw end-of-file with an unfinished selector:
00219     eSelectorParsingStatus_Error 
00220   };
00221   nsSelectorParsingStatus ParseIDSelector(PRInt32&       aDataMask,
00222                                           nsCSSSelector& aSelector,
00223                                           nsresult&      aErrorCode);
00224 
00225   nsSelectorParsingStatus ParseClassSelector(PRInt32&       aDataMask,
00226                                              nsCSSSelector& aSelector,
00227                                              nsresult&      aErrorCode);
00228 
00229   nsSelectorParsingStatus ParsePseudoSelector(PRInt32&       aDataMask,
00230                                               nsCSSSelector& aSelector,
00231                                               nsresult&      aErrorCode,
00232                                               PRBool         aIsNegated);
00233 
00234   nsSelectorParsingStatus ParseAttributeSelector(PRInt32&       aDataMask,
00235                                                  nsCSSSelector& aSelector,
00236                                                  nsresult&      aErrorCode);
00237 
00238   nsSelectorParsingStatus ParseTypeOrUniversalSelector(PRInt32&       aDataMask,
00239                                                        nsCSSSelector& aSelector,
00240                                                        nsresult&      aErrorCode,
00241                                                        PRBool         aIsNegated);
00242 
00243   nsSelectorParsingStatus ParseLangSelector(nsCSSSelector& aSelector,
00244                                             nsresult&      aErrorCode);
00245 
00246   nsSelectorParsingStatus ParseNegatedSimpleSelector(PRInt32&       aDataMask,
00247                                                      nsCSSSelector& aSelector,
00248                                                      nsresult&      aErrorCode);
00249 
00250   nsSelectorParsingStatus ParseSelector(nsresult&      aErrorCode,
00251                                         nsCSSSelector& aSelectorResult);
00252 
00253   PRBool ParseSelectorList(nsresult& aErrorCode, nsCSSSelectorList*& aListHead);
00254   PRBool ParseSelectorGroup(nsresult& aErrorCode, nsCSSSelectorList*& aListHead);
00255   nsCSSDeclaration* ParseDeclarationBlock(nsresult& aErrorCode,
00256                                            PRBool aCheckForBraces);
00257   PRBool ParseDeclaration(nsresult& aErrorCode,
00258                           nsCSSDeclaration* aDeclaration,
00259                           PRBool aCheckForBraces,
00260                           PRBool aMustCallValueAppended,
00261                           PRBool* aChanged);
00262   // After a parse error parsing |aPropID|, clear the data in
00263   // |mTempData|.
00264   void ClearTempData(nsCSSProperty aPropID);
00265   // After a successful parse of |aPropID|, transfer data from
00266   // |mTempData| to |mData|.  Set |*aChanged| to true if something
00267   // changed, but leave it unmodified otherwise.  If aMustCallValueAppended
00268   // is false, will not call ValueAppended on aDeclaration if the property
00269   // is already set in it.
00270   void TransferTempData(nsCSSDeclaration* aDeclaration,
00271                         nsCSSProperty aPropID, PRBool aIsImportant,
00272                         PRBool aMustCallValueAppended,
00273                         PRBool* aChanged);
00274   void DoTransferTempData(nsCSSDeclaration* aDeclaration,
00275                           nsCSSProperty aPropID, PRBool aIsImportant,
00276                           PRBool aMustCallValueAppended,
00277                           PRBool* aChanged);
00278   PRBool ParseProperty(nsresult& aErrorCode, nsCSSProperty aPropID);
00279   PRBool ParseSingleValueProperty(nsresult& aErrorCode, nsCSSValue& aValue, 
00280                                   nsCSSProperty aPropID);
00281 
00282 #ifdef MOZ_XUL
00283   PRBool ParseTreePseudoElement(nsresult& aErrorCode, nsCSSSelector& aSelector);
00284 #endif
00285 
00286   // Property specific parsing routines
00287   PRBool ParseAzimuth(nsresult& aErrorCode, nsCSSValue& aValue);
00288   PRBool ParseBackground(nsresult& aErrorCode);
00289   PRBool ParseBackgroundPosition(nsresult& aErrorCode);
00290   PRBool ParseBorderColor(nsresult& aErrorCode);
00291   PRBool ParseBorderColors(nsresult& aErrorCode,
00292                            nsCSSValueList** aResult,
00293                            nsCSSProperty aProperty);
00294   PRBool ParseBorderSpacing(nsresult& aErrorCode);
00295   PRBool ParseBorderSide(nsresult& aErrorCode,
00296                          const nsCSSProperty aPropIDs[],
00297                          PRBool aSetAllSides);
00298   PRBool ParseBorderStyle(nsresult& aErrorCode);
00299   PRBool ParseBorderWidth(nsresult& aErrorCode);
00300   PRBool ParseBorderRadius(nsresult& aErrorCode);
00301   PRBool ParseOutlineRadius(nsresult& aErrorCode);
00302   // for 'clip' and '-moz-image-region'
00303   PRBool ParseRect(nsCSSRect& aRect, nsresult& aErrorCode,
00304                    nsCSSProperty aPropID);
00305   PRBool DoParseRect(nsCSSRect& aRect, nsresult& aErrorCode);
00306   PRBool ParseContent(nsresult& aErrorCode);
00307   PRBool ParseCounterData(nsresult& aErrorCode,
00308                           nsCSSCounterData** aResult,
00309                           nsCSSProperty aPropID);
00310   PRBool ParseCue(nsresult& aErrorCode);
00311   PRBool ParseCursor(nsresult& aErrorCode);
00312   PRBool ParseFont(nsresult& aErrorCode);
00313   PRBool ParseFontWeight(nsresult& aErrorCode, nsCSSValue& aValue);
00314   PRBool ParseFamily(nsresult& aErrorCode, nsCSSValue& aValue);
00315   PRBool ParseListStyle(nsresult& aErrorCode);
00316   PRBool ParseMargin(nsresult& aErrorCode);
00317   PRBool ParseMarks(nsresult& aErrorCode, nsCSSValue& aValue);
00318   PRBool ParseOutline(nsresult& aErrorCode);
00319   PRBool ParseOverflow(nsresult& aErrorCode);
00320   PRBool ParsePadding(nsresult& aErrorCode);
00321   PRBool ParsePause(nsresult& aErrorCode);
00322   PRBool ParseQuotes(nsresult& aErrorCode);
00323   PRBool ParseSize(nsresult& aErrorCode);
00324   PRBool ParseTextDecoration(nsresult& aErrorCode, nsCSSValue& aValue);
00325   PRBool ParseTextShadow(nsresult& aErrorCode);
00326 
00327 #ifdef MOZ_SVG
00328   PRBool ParseDasharray(nsresult& aErrorCode);
00329   PRBool ParseMarker(nsresult& aErrorCode);
00330 #endif
00331 
00332   // Reused utility parsing routines
00333   void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue);
00334   PRBool ParseBoxProperties(nsresult& aErrorCode, nsCSSRect& aResult,
00335                             const nsCSSProperty aPropIDs[]);
00336   PRBool ParseDirectionalBoxProperty(nsresult& aErrorCode,
00337                                      nsCSSProperty aProperty,
00338                                      PRInt32 aSourceType);
00339   PRInt32 ParseChoice(nsresult& aErrorCode, nsCSSValue aValues[],
00340                       const nsCSSProperty aPropIDs[], PRInt32 aNumIDs);
00341   PRBool ParseColor(nsresult& aErrorCode, nsCSSValue& aValue);
00342   PRBool ParseColorComponent(nsresult& aErrorCode, PRUint8& aComponent,
00343                              PRInt32& aType, char aStop);
00344   // ParseHSLColor parses everything starting with the opening '(' up through
00345   // and including the aStop char.
00346   PRBool ParseHSLColor(nsresult& aErrorCode, nscolor& aColor, char aStop);
00347   // ParseColorOpacity will enforce that the color ends with a ')' after the opacity
00348   PRBool ParseColorOpacity(nsresult& aErrorCode, PRUint8& aOpacity);
00349   PRBool ParseEnum(nsresult& aErrorCode, nsCSSValue& aValue, const PRInt32 aKeywordTable[]);
00350   PRBool ParseVariant(nsresult& aErrorCode, nsCSSValue& aValue,
00351                       PRInt32 aVariantMask,
00352                       const PRInt32 aKeywordTable[]);
00353   PRBool ParsePositiveVariant(nsresult& aErrorCode, nsCSSValue& aValue, 
00354                               PRInt32 aVariantMask, 
00355                               const PRInt32 aKeywordTable[]); 
00356   PRBool ParseCounter(nsresult& aErrorCode, nsCSSValue& aValue);
00357   PRBool ParseAttr(nsresult& aErrorCode, nsCSSValue& aValue);
00358   PRBool ParseURL(nsresult& aErrorCode, nsCSSValue& aValue);
00359   PRBool TranslateDimension(nsresult& aErrorCode, nsCSSValue& aValue, PRInt32 aVariantMask,
00360                             float aNumber, const nsString& aUnit);
00361 
00362   void SetParsingCompoundProperty(PRBool aBool) {
00363     NS_ASSERTION(aBool == PR_TRUE || aBool == PR_FALSE, "bad PRBool value");
00364     mParsingCompoundProperty = aBool;
00365   }
00366   PRBool IsParsingCompoundProperty(void) {
00367     return mParsingCompoundProperty;
00368   }
00369 
00370   // Current token. The value is valid after calling GetToken
00371   nsCSSToken mToken;
00372 
00373   // Our scanner.
00374   nsCSSScanner mScanner;
00375 
00376   // The URI to be used as a base for relative URIs.
00377   nsCOMPtr<nsIURI> mBaseURL;
00378 
00379   // The URI to be used as an HTTP "Referer" and for error reporting.
00380   nsCOMPtr<nsIURI> mSheetURL;
00381 
00382   // The sheet we're parsing into
00383   nsCOMPtr<nsICSSStyleSheet> mSheet;
00384 
00385   // Used for @import rules
00386   nsICSSLoader* mChildLoader; // not ref counted, it owns us
00387 
00388   // Sheet section we're in.  This is used to enforce correct ordering of the
00389   // various rule types (eg the fact that a @charset rule must come before
00390   // anything else).
00391   enum nsCSSSection { 
00392     eCSSSection_Charset, 
00393     eCSSSection_Import, 
00394     eCSSSection_NameSpace,
00395     eCSSSection_General 
00396   };
00397   nsCSSSection  mSection;
00398 
00399   nsXMLNameSpaceMap *mNameSpaceMap;  // weak, mSheet owns it
00400 
00401   // After an UngetToken is done this flag is true. The next call to
00402   // GetToken clears the flag.
00403   PRPackedBool mHavePushBack : 1;
00404 
00405   // True if we are in quirks mode; false in standards or almost standards mode
00406   PRPackedBool  mNavQuirkMode : 1;
00407   
00408   // True if unsafe rules should be allowed
00409   PRPackedBool mUnsafeRulesEnabled : 1;
00410 
00411 #ifdef MOZ_SVG
00412   // True if we are in SVG mode; false in "normal" CSS
00413   PRPackedBool  mSVGMode : 1;
00414 #endif
00415 
00416   // True for parsing media lists for HTML attributes, where we have to
00417   // ignore CSS comments.
00418   PRPackedBool mHTMLMediaMode : 1;
00419 
00420   // True if ParseColor should handle rgba() and hsla(), which most of
00421   // Gecko currently doesn't understand.
00422   PRPackedBool mHandleAlphaColors : 1;
00423 
00424   // True if tagnames and attributes are case-sensitive
00425   PRPackedBool  mCaseSensitive : 1;
00426 
00427   // This flag is set when parsing a non-box shorthand; it's used to not apply
00428   // some quirks during shorthand parsing
00429   PRPackedBool  mParsingCompoundProperty : 1;
00430 
00431   // Stack of rule groups; used for @media and such.
00432   nsCOMArray<nsICSSGroupRule> mGroupStack;
00433 
00434   // During the parsing of a property (which may be a shorthand), the data
00435   // are stored in |mTempData|.  (It is needed to ensure that parser
00436   // errors cause the data to be ignored, and to ensure that a
00437   // non-'!important' declaration does not override an '!important'
00438   // one.)
00439   nsCSSExpandedDataBlock mTempData;
00440 
00441   // All data from successfully parsed properties are placed into |mData|.
00442   nsCSSExpandedDataBlock mData;
00443 
00444 #ifdef DEBUG
00445   PRPackedBool mScannerInited;
00446 #endif
00447 };
00448 
00449 PR_STATIC_CALLBACK(void) AppendRuleToArray(nsICSSRule* aRule, void* aArray)
00450 {
00451   nsISupportsArray* arr = (nsISupportsArray*) aArray;
00452   arr->AppendElement(aRule);
00453 }
00454 
00455 PR_STATIC_CALLBACK(void) AppendRuleToSheet(nsICSSRule* aRule, void* aParser)
00456 {
00457   CSSParserImpl* parser = (CSSParserImpl*) aParser;
00458   parser->AppendRule(aRule);
00459 }
00460 
00461 nsresult
00462 NS_NewCSSParser(nsICSSParser** aInstancePtrResult)
00463 {
00464   CSSParserImpl *it = new CSSParserImpl();
00465 
00466   if (it == nsnull) {
00467     return NS_ERROR_OUT_OF_MEMORY;
00468   }
00469 
00470   return it->QueryInterface(NS_GET_IID(nsICSSParser), (void **) aInstancePtrResult);
00471 }
00472 
00473 #ifdef CSS_REPORT_PARSE_ERRORS
00474 
00475 #define REPORT_UNEXPECTED(msg_) \
00476   mScanner.ReportUnexpected(#msg_)
00477 
00478 #define REPORT_UNEXPECTED_P(msg_, params_) \
00479   mScanner.ReportUnexpectedParams(#msg_, params_, NS_ARRAY_LENGTH(params_))
00480 
00481 #define REPORT_UNEXPECTED_EOF(lf_) \
00482   mScanner.ReportUnexpectedEOF(#lf_)
00483 
00484 #define REPORT_UNEXPECTED_TOKEN(msg_) \
00485   mScanner.ReportUnexpectedToken(mToken, #msg_)
00486 
00487 #define REPORT_UNEXPECTED_TOKEN_P(msg_, params_) \
00488   mScanner.ReportUnexpectedTokenParams(mToken, #msg_, \
00489                                        params_, NS_ARRAY_LENGTH(params_))
00490 
00491 
00492 #define OUTPUT_ERROR() \
00493   mScanner.OutputError()
00494 
00495 #define CLEAR_ERROR() \
00496   mScanner.ClearError()
00497 
00498 #else
00499 
00500 #define REPORT_UNEXPECTED(msg_)
00501 #define REPORT_UNEXPECTED_P(msg_, params_)
00502 #define REPORT_UNEXPECTED_EOF(lf_)
00503 #define REPORT_UNEXPECTED_TOKEN(msg_)
00504 #define REPORT_UNEXPECTED_TOKEN_P(msg_, params_)
00505 #define OUTPUT_ERROR()
00506 #define CLEAR_ERROR()
00507 
00508 #endif
00509 
00510 CSSParserImpl::CSSParserImpl()
00511   : mToken(),
00512     mScanner(),
00513     mChildLoader(nsnull),
00514     mSection(eCSSSection_Charset),
00515     mNameSpaceMap(nsnull),
00516     mHavePushBack(PR_FALSE),
00517     mNavQuirkMode(PR_FALSE),
00518     mUnsafeRulesEnabled(PR_FALSE),
00519 #ifdef MOZ_SVG
00520     mSVGMode(PR_FALSE),
00521 #endif
00522     mHTMLMediaMode(PR_FALSE),
00523     mHandleAlphaColors(PR_FALSE),
00524     mCaseSensitive(PR_FALSE),
00525     mParsingCompoundProperty(PR_FALSE)
00526 #ifdef DEBUG
00527     , mScannerInited(PR_FALSE)
00528 #endif
00529 {
00530 }
00531 
00532 NS_IMPL_ISUPPORTS2(CSSParserImpl, nsICSSParser, nsICSSParser_MOZILLA_1_8_BRANCH)
00533 
00534 CSSParserImpl::~CSSParserImpl()
00535 {
00536   mData.AssertInitialState();
00537   mTempData.AssertInitialState();
00538 }
00539 
00540 NS_IMETHODIMP
00541 CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet)
00542 {
00543   NS_PRECONDITION(nsnull != aSheet, "null ptr");
00544   if (nsnull == aSheet) {
00545     return NS_ERROR_NULL_POINTER;
00546   }
00547 
00548   if (aSheet != mSheet) {
00549     // Switch to using the new sheet
00550     mGroupStack.Clear();
00551     mSheet = aSheet;
00552     mNameSpaceMap = mSheet->GetNameSpaceMap();
00553   }
00554 
00555   return NS_OK;
00556 }
00557 
00558 NS_IMETHODIMP
00559 CSSParserImpl::SetCaseSensitive(PRBool aCaseSensitive)
00560 {
00561   NS_ASSERTION(aCaseSensitive == PR_TRUE || aCaseSensitive == PR_FALSE, "bad PRBool value");
00562   mCaseSensitive = aCaseSensitive;
00563   return NS_OK;
00564 }
00565 
00566 NS_IMETHODIMP
00567 CSSParserImpl::SetQuirkMode(PRBool aQuirkMode)
00568 {
00569   NS_ASSERTION(aQuirkMode == PR_TRUE || aQuirkMode == PR_FALSE, "bad PRBool value");
00570   mNavQuirkMode = aQuirkMode;
00571   return NS_OK;
00572 }
00573 
00574 #ifdef MOZ_SVG
00575 NS_IMETHODIMP
00576 CSSParserImpl::SetSVGMode(PRBool aSVGMode)
00577 {
00578   NS_ASSERTION(aSVGMode == PR_TRUE || aSVGMode == PR_FALSE, "bad PRBool value");
00579   mSVGMode = aSVGMode;
00580   return NS_OK;
00581 }
00582 #endif
00583 
00584 NS_IMETHODIMP
00585 CSSParserImpl::SetChildLoader(nsICSSLoader* aChildLoader)
00586 {
00587   mChildLoader = aChildLoader;  // not ref counted, it owns us
00588   return NS_OK;
00589 }
00590 
00591 nsresult
00592 CSSParserImpl::InitScanner(nsIUnicharInputStream* aInput, nsIURI* aSheetURI,
00593                            PRUint32 aLineNumber, nsIURI* aBaseURI)
00594 {
00595   NS_ASSERTION(! mScannerInited, "already have scanner");
00596 
00597   mScanner.Init(aInput, aSheetURI, aLineNumber);
00598 #ifdef DEBUG
00599   mScannerInited = PR_TRUE;
00600 #endif
00601   mBaseURL = aBaseURI;
00602   mSheetURL = aSheetURI;
00603 
00604   mHavePushBack = PR_FALSE;
00605 
00606   return NS_OK;
00607 }
00608 
00609 nsresult
00610 CSSParserImpl::InitScanner(const nsAString& aBuffer, nsIURI* aSheetURI,
00611                            PRUint32 aLineNumber, nsIURI* aBaseURI)
00612 {
00613   // Having it not own the string is OK since the caller will hold on to
00614   // the stream until we're done parsing.
00615   nsCOMPtr<nsIUnicharInputStream> input;
00616   nsresult rv = NS_NewStringUnicharInputStream(getter_AddRefs(input),
00617                                                &aBuffer, PR_FALSE);
00618   if (NS_FAILED(rv)) {
00619     return rv;
00620   }
00621 
00622   return InitScanner(input, aSheetURI, aLineNumber, aBaseURI);
00623 }
00624 
00625 nsresult
00626 CSSParserImpl::ReleaseScanner(void)
00627 {
00628   mScanner.Close();
00629 #ifdef DEBUG
00630   mScannerInited = PR_FALSE;
00631 #endif
00632   mBaseURL = nsnull;
00633   mSheetURL = nsnull;
00634   return NS_OK;
00635 }
00636 
00637 
00638 NS_IMETHODIMP
00639 CSSParserImpl::Parse(nsIUnicharInputStream* aInput,
00640                      nsIURI*                aSheetURI,
00641                      nsIURI*                aBaseURI,
00642                      PRUint32               aLineNumber,
00643                      nsICSSStyleSheet*&     aResult)
00644 {
00645   return Parse(aInput, aSheetURI, aBaseURI, aLineNumber, PR_FALSE, aResult);
00646 }
00647 
00648 NS_IMETHODIMP
00649 CSSParserImpl::Parse(nsIUnicharInputStream* aInput,
00650                      nsIURI*                aSheetURI,
00651                      nsIURI*                aBaseURI,
00652                      PRUint32               aLineNumber,
00653                      PRBool                 aAllowUnsafeRules,
00654                      nsICSSStyleSheet*&     aResult)
00655 {
00656   NS_ASSERTION(nsnull != aBaseURI, "need base URL");
00657   NS_ASSERTION(nsnull != aSheetURI, "need sheet URL");
00658 
00659   if (! mSheet) {
00660     NS_NewCSSStyleSheet(getter_AddRefs(mSheet));
00661     NS_ENSURE_TRUE(mSheet, NS_ERROR_OUT_OF_MEMORY);
00662 
00663     nsCOMPtr<nsICSSStyleSheet_MOZILLA_1_8_BRANCH> sheet =
00664       do_QueryInterface(mSheet);
00665     sheet->SetURIs18(aSheetURI, aSheetURI, aBaseURI);
00666     mNameSpaceMap = nsnull;
00667   }
00668 #ifdef DEBUG
00669   else {
00670     nsCOMPtr<nsIURI> uri;
00671     mSheet->GetSheetURI(getter_AddRefs(uri));
00672     PRBool equal;
00673     aSheetURI->Equals(uri, &equal);
00674     NS_ASSERTION(equal, "Sheet URI does not match passed URI");
00675   }
00676 #endif
00677   
00678   nsresult errorCode = NS_OK;
00679 
00680   nsresult result = InitScanner(aInput, aSheetURI, aLineNumber, aBaseURI);
00681   if (! NS_SUCCEEDED(result)) {
00682     return result;
00683   }
00684 
00685   PRInt32 ruleCount = 0;
00686   mSheet->StyleRuleCount(ruleCount);
00687   if (0 < ruleCount) {
00688     nsICSSRule* lastRule = nsnull;
00689     mSheet->GetStyleRuleAt(ruleCount - 1, lastRule);
00690     if (lastRule) {
00691       PRInt32 type;
00692       lastRule->GetType(type);
00693       switch (type) {
00694         case nsICSSRule::CHARSET_RULE:
00695         case nsICSSRule::IMPORT_RULE:     
00696           mSection = eCSSSection_Import;    
00697           break;
00698         case nsICSSRule::NAMESPACE_RULE:  
00699           mSection = eCSSSection_NameSpace; 
00700           break;
00701         default:  
00702           mSection = eCSSSection_General; 
00703           break;
00704       }
00705       NS_RELEASE(lastRule);
00706     }
00707   }
00708   else {
00709     mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
00710   }
00711 
00712   mUnsafeRulesEnabled = aAllowUnsafeRules;
00713 
00714   nsCSSToken* tk = &mToken;
00715   for (;;) {
00716     // Get next non-whitespace token
00717     if (!GetToken(errorCode, PR_TRUE)) {
00718       OUTPUT_ERROR();
00719       break;
00720     }
00721     if (eCSSToken_HTMLComment == tk->mType) {
00722       continue; // legal here only
00723     }
00724     if (eCSSToken_AtKeyword == tk->mType) {
00725       ParseAtRule(errorCode, AppendRuleToSheet, this);
00726       continue;
00727     }
00728     UngetToken();
00729     if (ParseRuleSet(errorCode, AppendRuleToSheet, this)) {
00730       mSection = eCSSSection_General;
00731     }
00732   }
00733   ReleaseScanner();
00734 
00735   mUnsafeRulesEnabled = PR_FALSE;
00736 
00737   aResult = mSheet;
00738   NS_ADDREF(aResult);
00739 
00740   return NS_OK;
00741 }
00742 
00743 NS_IMETHODIMP
00744 CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
00745                                    nsIURI*                  aDocURL,
00746                                    nsIURI*                  aBaseURL,
00747                                    nsICSSStyleRule**        aResult)
00748 {
00749   NS_ASSERTION(nsnull != aBaseURL, "need base URL");
00750 
00751   nsresult rv =
00752     InitScanner(aAttributeValue, aDocURL, 0, aBaseURL); // XXX line number
00753   if (! NS_SUCCEEDED(rv)) {
00754     return rv;
00755   }
00756 
00757   mSection = eCSSSection_General;
00758   nsresult errorCode = NS_OK;
00759 
00760   // In quirks mode, allow style declarations to have braces or not
00761   // (bug 99554).
00762   PRBool haveBraces;
00763   if (mNavQuirkMode) {
00764     GetToken(errorCode, PR_TRUE);
00765     haveBraces = eCSSToken_Symbol == mToken.mType &&
00766                  '{' == mToken.mSymbol;
00767     UngetToken();
00768   }
00769   else {
00770     haveBraces = PR_FALSE;
00771   }
00772 
00773   nsCSSDeclaration* declaration = ParseDeclarationBlock(errorCode, haveBraces);
00774   if (declaration) {
00775     // Create a style rule for the delcaration
00776     nsICSSStyleRule* rule = nsnull;
00777     rv = NS_NewCSSStyleRule(&rule, nsnull, declaration);
00778     if (NS_FAILED(rv)) {
00779       declaration->RuleAbort();
00780       ReleaseScanner();
00781       return rv;
00782     }
00783     *aResult = rule;
00784   }
00785   else {
00786     *aResult = nsnull;
00787   }
00788 
00789   ReleaseScanner();
00790 
00791   return NS_OK;
00792 }
00793 
00794 NS_IMETHODIMP
00795 CSSParserImpl::ParseAndAppendDeclaration(const nsAString&  aBuffer,
00796                                          nsIURI*           aSheetURL,
00797                                          nsIURI*           aBaseURL,
00798                                          nsCSSDeclaration* aDeclaration,
00799                                          PRBool            aParseOnlyOneDecl,
00800                                          PRBool*           aChanged,
00801                                          PRBool            aClearOldDecl)
00802 {
00803 //  NS_ASSERTION(nsnull != aBaseURL, "need base URL");
00804   *aChanged = PR_FALSE;
00805 
00806   nsresult rv = InitScanner(aBuffer, aSheetURL, 0, aBaseURL);
00807   if (! NS_SUCCEEDED(rv)) {
00808     return rv;
00809   }
00810 
00811   mSection = eCSSSection_General;
00812   nsresult errorCode = NS_OK;
00813 
00814   if (aClearOldDecl) {
00815     mData.AssertInitialState();
00816     aDeclaration->ClearData();
00817     // We could check if it was already empty, but...
00818     *aChanged = PR_TRUE;
00819   } else {
00820     aDeclaration->ExpandTo(&mData);
00821   }
00822 
00823   do {
00824     // If we cleared the old decl, then we want to be calling
00825     // ValueAppended as we parse.
00826     if (!ParseDeclaration(errorCode, aDeclaration, PR_FALSE,
00827                           aClearOldDecl, aChanged)) {
00828       NS_ASSERTION(errorCode != nsresult(-1), "-1 is no longer used for EOF");
00829       rv = errorCode;
00830 
00831       if (NS_FAILED(errorCode))
00832         break;
00833 
00834       if (!SkipDeclaration(errorCode, PR_FALSE)) {
00835         NS_ASSERTION(errorCode != nsresult(-1), "-1 is no longer used for EOF");
00836         rv = errorCode;
00837         break;
00838       }
00839     }
00840   } while (!aParseOnlyOneDecl);
00841   aDeclaration->CompressFrom(&mData);
00842 
00843   ReleaseScanner();
00844   return rv;
00845 }
00846 
00847 NS_IMETHODIMP
00848 CSSParserImpl::ParseRule(const nsAString& aRule,
00849                          nsIURI*            aSheetURL,
00850                          nsIURI*            aBaseURL,
00851                          nsISupportsArray** aResult)
00852 {
00853   NS_ASSERTION(nsnull != aBaseURL, "need base URL");
00854   NS_ENSURE_ARG_POINTER(aResult);
00855 
00856   nsresult rv = InitScanner(aRule, aSheetURL, 0, aBaseURL);
00857   if (NS_FAILED(rv)) {
00858     return rv;
00859   }
00860 
00861   rv = NS_NewISupportsArray(aResult);
00862   if (NS_FAILED(rv)) {
00863     ReleaseScanner();
00864     return rv;
00865   }
00866   
00867   mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
00868   nsresult errorCode = NS_OK;
00869 
00870   nsCSSToken* tk = &mToken;
00871   // Get first non-whitespace token
00872   if (!GetToken(errorCode, PR_TRUE)) {
00873     REPORT_UNEXPECTED(PEParseRuleWSOnly);
00874     OUTPUT_ERROR();
00875   } else if (eCSSToken_AtKeyword == tk->mType) {
00876     ParseAtRule(errorCode, AppendRuleToArray, *aResult);    
00877   }
00878   else {
00879     UngetToken();
00880     ParseRuleSet(errorCode, AppendRuleToArray, *aResult);
00881   }
00882   OUTPUT_ERROR();
00883   ReleaseScanner();
00884   return NS_OK;
00885 }
00886 
00887 //XXXbz this function does not deal well with something like "foo
00889 //in the decl, then ignore the !important.  It should either fail to
00890 //parse this or do !important correctly....
00891 NS_IMETHODIMP
00892 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
00893                              const nsAString& aPropValue,
00894                              nsIURI* aSheetURL,
00895                              nsIURI* aBaseURL,
00896                              nsCSSDeclaration* aDeclaration,
00897                              PRBool* aChanged)
00898 {
00899   NS_ASSERTION(nsnull != aBaseURL, "need base URL");
00900   NS_ASSERTION(nsnull != aDeclaration, "Need declaration to parse into!");
00901   *aChanged = PR_FALSE;
00902 
00903   nsresult rv = InitScanner(aPropValue, aSheetURL, 0, aBaseURL);
00904   if (NS_FAILED(rv)) {
00905     return rv;
00906   }
00907 
00908   mSection = eCSSSection_General;
00909   nsresult errorCode = NS_OK;
00910 
00911   if (eCSSProperty_UNKNOWN == aPropID) { // unknown property
00912     NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
00913     const PRUnichar *params[] = {
00914       propName.get()
00915     };
00916     REPORT_UNEXPECTED_P(PEUnknownProperty, params);
00917     REPORT_UNEXPECTED(PEDeclDropped);
00918     OUTPUT_ERROR();
00919     ReleaseScanner();
00920     return NS_OK;
00921   }
00922   
00923   mData.AssertInitialState();
00924   mTempData.AssertInitialState();
00925   aDeclaration->ExpandTo(&mData);
00926   nsresult result = NS_OK;
00927   if (ParseProperty(errorCode, aPropID)) {
00928     TransferTempData(aDeclaration, aPropID, PR_FALSE, PR_FALSE, aChanged);
00929   } else {
00930     NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
00931     const PRUnichar *params[] = {
00932       propName.get()
00933     };
00934     REPORT_UNEXPECTED_P(PEPropertyParsingError, params);
00935     REPORT_UNEXPECTED(PEDeclDropped);
00936     OUTPUT_ERROR();
00937     ClearTempData(aPropID);
00938     NS_ASSERTION(errorCode != nsresult(-1), "-1 is no longer used for EOF");
00939     result = errorCode;
00940   }
00941   CLEAR_ERROR();
00942   
00943   aDeclaration->CompressFrom(&mData);
00944   
00945   ReleaseScanner();
00946   return result;
00947 }
00948 
00949 NS_IMETHODIMP
00950 CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer,
00951                               nsIURI* aURL, // for error reporting
00952                               PRUint32 aLineNumber, // for error reporting
00953                               nsMediaList* aMediaList,
00954                               PRBool aHTMLMode)
00955 {
00956   aMediaList->Clear();
00957   nsresult rv = NS_OK;
00958 
00959   if (aHTMLMode) {
00960     mHTMLMediaMode = PR_TRUE;
00961 
00962     // XXXldb We need to make the scanner not skip CSS comments!  (Or
00963     // should we?)
00964 
00965     // Follow the parsing rules in 
00966     // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors
00967 
00968     for (PRUint32 sub = 0, sub_end; sub < aBuffer.Length(); sub = sub_end + 1) {
00969       sub_end = aBuffer.FindChar(PRUnichar(','), sub);
00970       if (sub_end == PRUint32(kNotFound))
00971         sub_end = aBuffer.Length();
00972 
00973       PRUint32 parse_start, parse_end;
00974       for (parse_start = sub;
00975            parse_start < sub_end && nsCRT::IsAsciiSpace(aBuffer[parse_start]);
00976            ++parse_start)
00977         ;
00978 
00979       for (parse_end = parse_start;
00980            parse_end < sub_end &&
00981            (nsCRT::IsAsciiAlpha(aBuffer[parse_end]) ||
00982             nsCRT::IsAsciiDigit(aBuffer[parse_end]) ||
00983             aBuffer[parse_end] == PRUnichar('-'));
00984            ++parse_end)
00985         ;
00986 
00987       DoParseMediaList(Substring(aBuffer, parse_start, parse_end - parse_start),
00988                        aURL, aLineNumber, aMediaList);
00989     }
00990 
00991     mHTMLMediaMode = PR_FALSE;
00992   } else {
00993     rv = DoParseMediaList(aBuffer, aURL, aLineNumber, aMediaList);
00994   }
00995 
00996   return rv;
00997 }
00998 
00999 // All parameters but the first are the same as for |ParseMediaList|,
01000 // but for HTML we get the buffer in chunks according to the HTML spec's
01001 // parsing rules instead of in one piece.
01002 nsresult
01003 CSSParserImpl::DoParseMediaList(const nsSubstring& aBuffer,
01004                                 nsIURI* aURL, // for error reporting
01005                                 PRUint32 aLineNumber, // for error reporting
01006                                 nsMediaList* aMediaList)
01007 {
01008   // fake base URL since media lists don't have URLs in them
01009   nsresult rv = InitScanner(aBuffer, aURL, aLineNumber, aURL);
01010   if (NS_FAILED(rv)) {
01011     return rv;
01012   }
01013 
01014   if (!GatherMedia(rv, aMediaList, PRUnichar(0)) && !mHTMLMediaMode) {
01015     OUTPUT_ERROR();
01016   }
01017   CLEAR_ERROR();
01018   ReleaseScanner();
01019   return rv;
01020 }
01021 
01022 NS_IMETHODIMP
01023 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
01024                                 nsIURI* aURL, // for error reporting
01025                                 PRUint32 aLineNumber, // for error reporting
01026                                 PRBool aHandleAlphaColors,
01027                                 nscolor* aColor)
01028 {
01029   NS_ASSERTION(aHandleAlphaColors == PR_TRUE || aHandleAlphaColors == PR_FALSE, "bad PRBool value");
01030 
01031   nsresult rv = NS_ERROR_FAILURE;
01032 
01033   rv = InitScanner(aBuffer, aURL, aLineNumber, aURL);
01034   if (NS_FAILED(rv))
01035     return rv;
01036 
01037   mHandleAlphaColors = aHandleAlphaColors;
01038 
01039   nsCSSValue value;
01040   PRBool colorParsed = ParseColor(rv, value);
01041 
01042   CLEAR_ERROR();
01043   ReleaseScanner();
01044 
01045   mHandleAlphaColors = PR_FALSE;
01046 
01047   if (!colorParsed) {
01048     return NS_ERROR_FAILURE;
01049   }
01050 
01051   if (value.GetUnit() == eCSSUnit_String) {
01052     nsAutoString s;
01053     nscolor rgba;
01054     if (NS_ColorNameToRGB(value.GetStringValue(s), &rgba)) {
01055       (*aColor) = rgba;
01056       rv = NS_OK;
01057     }
01058   } else if (value.GetUnit() == eCSSUnit_Color) {
01059     (*aColor) = value.GetColorValue();
01060     rv = NS_OK;
01061   } else if (value.GetUnit() == eCSSUnit_Integer) {
01062     PRInt32 intValue = value.GetIntValue();
01063     if (intValue >= 0) {
01064       nsCOMPtr<nsILookAndFeel> lfSvc = do_GetService("@mozilla.org/widget/lookandfeel;1");
01065       if (lfSvc) {
01066         nscolor rgba;
01067         rv = lfSvc->GetColor((nsILookAndFeel::nsColorID) value.GetIntValue(), rgba);
01068         if (NS_SUCCEEDED(rv))
01069           (*aColor) = rgba;
01070       }
01071     } else {
01072       // XXX - this is NS_COLOR_CURRENTCOLOR, NS_COLOR_MOZ_HYPERLINKTEXT, etc.
01073       // which we don't handle as per the ParseColorString definition.  Should
01074       // remove this limitation at some point.
01075       rv = NS_ERROR_FAILURE;
01076     }
01077   }
01078 
01079   return rv;
01080 }
01081 
01082 //----------------------------------------------------------------------
01083 
01084 PRBool CSSParserImpl::GetToken(nsresult& aErrorCode, PRBool aSkipWS)
01085 {
01086   for (;;) {
01087     if (!mHavePushBack) {
01088       if (!mScanner.Next(aErrorCode, mToken)) {
01089         break;
01090       }
01091     }
01092     mHavePushBack = PR_FALSE;
01093     if (aSkipWS && (eCSSToken_WhiteSpace == mToken.mType)) {
01094       continue;
01095     }
01096     return PR_TRUE;
01097   }
01098   return PR_FALSE;
01099 }
01100 
01101 PRBool CSSParserImpl::GetURLToken(nsresult& aErrorCode, PRBool aSkipWS)
01102 {
01103   for (;;) {
01104     // XXXldb This pushback code doesn't make sense.
01105     if (! mHavePushBack) {
01106       if (! mScanner.NextURL(aErrorCode, mToken)) {
01107         break;
01108       }
01109     }
01110     mHavePushBack = PR_FALSE;
01111     if (aSkipWS && (eCSSToken_WhiteSpace == mToken.mType)) {
01112       continue;
01113     }
01114     return PR_TRUE;
01115   }
01116   return PR_FALSE;
01117 }
01118 
01119 void CSSParserImpl::UngetToken()
01120 {
01121   NS_PRECONDITION(mHavePushBack == PR_FALSE, "double pushback");
01122   mHavePushBack = PR_TRUE;
01123 }
01124 
01125 PRBool CSSParserImpl::ExpectSymbol(nsresult& aErrorCode,
01126                                    PRUnichar aSymbol,
01127                                    PRBool aSkipWS)
01128 {
01129   if (!GetToken(aErrorCode, aSkipWS)) {
01130     return PR_FALSE;
01131   }
01132   if (mToken.IsSymbol(aSymbol)) {
01133     return PR_TRUE;
01134   }
01135   UngetToken();
01136   return PR_FALSE;
01137 }
01138 
01139 PRBool CSSParserImpl::ExpectEndProperty(nsresult& aErrorCode, PRBool aSkipWS)
01140 {
01141   if (!GetToken(aErrorCode, aSkipWS)) {
01142     return PR_TRUE; // properties may end with eof
01143   }
01144   if ((eCSSToken_Symbol == mToken.mType) &&
01145       ((';' == mToken.mSymbol) || ('!' == mToken.mSymbol) || ('}' == mToken.mSymbol))) {
01146     // XXX need to verify that ! is only followed by "important [;|}]
01147     // XXX this requires a multi-token pushback buffer
01148     UngetToken();
01149     return PR_TRUE;
01150   }
01151   REPORT_UNEXPECTED_TOKEN(PEExpectEndProperty);
01152   UngetToken();
01153   return PR_FALSE;
01154 }
01155 
01156 
01157 nsString* CSSParserImpl::NextIdent(nsresult& aErrorCode)
01158 {
01159   // XXX Error reporting?
01160   if (!GetToken(aErrorCode, PR_TRUE)) {
01161     return nsnull;
01162   }
01163   if (eCSSToken_Ident != mToken.mType) {
01164     UngetToken();
01165     return nsnull;
01166   }
01167   return &mToken.mIdent;
01168 }
01169 
01170 PRBool CSSParserImpl::SkipAtRule(nsresult& aErrorCode)
01171 {
01172   for (;;) {
01173     if (!GetToken(aErrorCode, PR_TRUE)) {
01174       REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF);
01175       return PR_FALSE;
01176     }
01177     if (eCSSToken_Symbol == mToken.mType) {
01178       PRUnichar symbol = mToken.mSymbol;
01179       if (symbol == ';') {
01180         break;
01181       }
01182       if (symbol == '{') {
01183         SkipUntil(aErrorCode, '}');
01184         break;
01185       } else if (symbol == '(') {
01186         SkipUntil(aErrorCode, ')');
01187       } else if (symbol == '[') {
01188         SkipUntil(aErrorCode, ']');
01189       }
01190     }
01191   }
01192   return PR_TRUE;
01193 }
01194 
01195 PRBool CSSParserImpl::ParseAtRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc,
01196                                   void* aData)
01197 {
01198   if ((mSection <= eCSSSection_Charset) && 
01199       (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
01200     if (ParseCharsetRule(aErrorCode, aAppendFunc, aData)) {
01201       mSection = eCSSSection_Import;  // only one charset allowed
01202       return PR_TRUE;
01203     }
01204   }
01205   if ((mSection <= eCSSSection_Import) && 
01206       mToken.mIdent.LowerCaseEqualsLiteral("import")) {
01207     if (ParseImportRule(aErrorCode, aAppendFunc, aData)) {
01208       mSection = eCSSSection_Import;
01209       return PR_TRUE;
01210     }
01211   }
01212   if ((mSection <= eCSSSection_NameSpace) && 
01213       mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
01214     if (ParseNameSpaceRule(aErrorCode, aAppendFunc, aData)) {
01215       mSection = eCSSSection_NameSpace;
01216       return PR_TRUE;
01217     }
01218   }
01219   if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
01220     if (ParseMediaRule(aErrorCode, aAppendFunc, aData)) {
01221       mSection = eCSSSection_General;
01222       return PR_TRUE;
01223     }
01224   }
01225   if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
01226     if (ParseMozDocumentRule(aErrorCode, aAppendFunc, aData)) {
01227       mSection = eCSSSection_General;
01228       return PR_TRUE;
01229     }
01230   }
01231   if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
01232     if (ParseFontFaceRule(aErrorCode, aAppendFunc, aData)) {
01233       mSection = eCSSSection_General;
01234       return PR_TRUE;
01235     }
01236   }
01237   if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
01238     if (ParsePageRule(aErrorCode, aAppendFunc, aData)) {
01239       mSection = eCSSSection_General;
01240       return PR_TRUE;
01241     }
01242   }
01243   REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
01244   OUTPUT_ERROR();
01245 
01246   // Skip over unsupported at rule, don't advance section
01247   return SkipAtRule(aErrorCode);
01248 }
01249 
01250 PRBool CSSParserImpl::ParseCharsetRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc,
01251                                        void* aData)
01252 {
01253   if (!GetToken(aErrorCode, PR_TRUE)) {
01254     REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
01255     return PR_FALSE;
01256   }
01257 
01258   if (eCSSToken_String != mToken.mType) {
01259     REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
01260     return PR_FALSE;
01261   }
01262 
01263   nsAutoString charset = mToken.mIdent;
01264   
01265   if (!ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
01266     return PR_FALSE;
01267   }
01268 
01269   nsCOMPtr<nsICSSRule> rule;
01270   NS_NewCSSCharsetRule(getter_AddRefs(rule), charset);
01271 
01272   if (rule) {
01273     (*aAppendFunc)(rule, aData);
01274   }
01275 
01276   return PR_TRUE;
01277 }
01278 
01279 PRBool CSSParserImpl::GatherURL(nsresult& aErrorCode, nsString& aURL)
01280 {
01281   if (!GetToken(aErrorCode, PR_TRUE)) {
01282     return PR_FALSE;
01283   }
01284   if (eCSSToken_String == mToken.mType) {
01285     aURL = mToken.mIdent;
01286     return PR_TRUE;
01287   }
01288   else if (eCSSToken_Function == mToken.mType && 
01289            mToken.mIdent.LowerCaseEqualsLiteral("url") &&
01290            ExpectSymbol(aErrorCode, '(', PR_FALSE) &&
01291            GetURLToken(aErrorCode, PR_TRUE) &&
01292            (eCSSToken_String == mToken.mType ||
01293             eCSSToken_URL == mToken.mType)) {
01294     aURL = mToken.mIdent;
01295     if (ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
01296       return PR_TRUE;
01297     }
01298   }
01299   return PR_FALSE;
01300 }
01301 
01302 PRBool CSSParserImpl::GatherMedia(nsresult& aErrorCode,
01303                                   nsMediaList* aMedia,
01304                                   PRUnichar aStopSymbol)
01305 {
01306   for (;;) {
01307     if (!GetToken(aErrorCode, PR_TRUE)) {
01308       REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
01309       break;
01310     }
01311     if (eCSSToken_Ident != mToken.mType) {
01312       REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
01313       UngetToken();
01314       break;
01315     }
01316     ToLowerCase(mToken.mIdent);  // case insensitive from CSS - must be lower cased
01317     nsCOMPtr<nsIAtom> medium = do_GetAtom(mToken.mIdent);
01318     aMedia->AppendAtom(medium);
01319 
01320     if (!GetToken(aErrorCode, PR_TRUE)) {
01321       if (aStopSymbol == PRUnichar(0))
01322         return PR_TRUE;
01323       REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
01324       break;
01325     }
01326 
01327     if (eCSSToken_Symbol == mToken.mType &&
01328         mToken.mSymbol == aStopSymbol) {
01329       UngetToken();
01330       return PR_TRUE;
01331     } else if (eCSSToken_Symbol != mToken.mType ||
01332                mToken.mSymbol != ',') {
01333       REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
01334       UngetToken();
01335       break;
01336     }
01337   }
01338   return PR_FALSE;
01339 }
01340 
01341 // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
01342 PRBool CSSParserImpl::ParseImportRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aData)
01343 {
01344   nsCOMPtr<nsMediaList> media = new nsMediaList();
01345   if (!media) {
01346     aErrorCode = NS_ERROR_OUT_OF_MEMORY;
01347     return PR_FALSE;
01348   }
01349 
01350   nsAutoString url;
01351   if (!GatherURL(aErrorCode, url)) {
01352     REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
01353     return PR_FALSE;
01354   }
01355   
01356   if (!ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
01357     if (!GatherMedia(aErrorCode, media, ';') ||
01358         !ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
01359       REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
01360       // don't advance section, simply ignore invalid @import
01361       return PR_FALSE;
01362     }
01363     NS_ASSERTION(media->Count() != 0, "media list must be nonempty");
01364   }
01365 
01366   ProcessImport(aErrorCode, url, media, aAppendFunc, aData);
01367   return PR_TRUE;
01368 }
01369 
01370 
01371 PRBool CSSParserImpl::ProcessImport(nsresult& aErrorCode,
01372                                     const nsString& aURLSpec,
01373                                     nsMediaList* aMedia,
01374                                     RuleAppendFunc aAppendFunc,
01375                                     void* aData)
01376 {
01377   nsCOMPtr<nsICSSImportRule> rule;
01378   aErrorCode = NS_NewCSSImportRule(getter_AddRefs(rule), aURLSpec, aMedia);
01379   if (NS_FAILED(aErrorCode)) {
01380     return PR_FALSE;
01381   }
01382   (*aAppendFunc)(rule, aData);
01383 
01384   if (mChildLoader) {
01385     nsCOMPtr<nsIURI> url;
01386     // XXX should pass a charset!
01387     aErrorCode = NS_NewURI(getter_AddRefs(url), aURLSpec, nsnull, mBaseURL);
01388 
01389     if (NS_FAILED(aErrorCode)) {
01390       // import url is bad
01391       // XXX log this somewhere for easier web page debugging
01392       return PR_FALSE;
01393     }
01394 
01395     mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule);
01396   }
01397   
01398   return PR_TRUE;
01399 }
01400 
01401 // Parse the {} part of an @media or @-moz-document rule.
01402 PRBool CSSParserImpl::ParseGroupRule(nsresult& aErrorCode,
01403                                      nsICSSGroupRule* aRule,
01404                                      RuleAppendFunc aAppendFunc,
01405                                      void* aData)
01406 {
01407   // XXXbz this could use better error reporting throughout the method
01408   if (!ExpectSymbol(aErrorCode, '{', PR_TRUE)) {
01409     return PR_FALSE;
01410   }
01411 
01412   // push rule on stack, loop over children
01413   if (!PushGroup(aRule)) {
01414     aErrorCode = NS_ERROR_OUT_OF_MEMORY;
01415     return PR_FALSE;
01416   }
01417   nsCSSSection holdSection = mSection;
01418   mSection = eCSSSection_General;
01419 
01420   for (;;) {
01421     // Get next non-whitespace token
01422     if (! GetToken(aErrorCode, PR_TRUE)) {
01423       REPORT_UNEXPECTED_EOF(PEGroupRuleEOF);
01424       break;
01425     }
01426     if (mToken.IsSymbol('}')) { // done!
01427       UngetToken();
01428       break;
01429     }
01430     if (eCSSToken_AtKeyword == mToken.mType) {
01431       SkipAtRule(aErrorCode); // group rules cannot contain @rules
01432       continue;
01433     }
01434     UngetToken();
01435     ParseRuleSet(aErrorCode, AppendRuleToSheet, this);
01436   }
01437   PopGroup();
01438 
01439   if (!ExpectSymbol(aErrorCode, '}', PR_TRUE)) {
01440     mSection = holdSection;
01441     return PR_FALSE;
01442   }
01443   (*aAppendFunc)(aRule, aData);
01444   return PR_TRUE;
01445 }
01446 
01447 // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
01448 PRBool CSSParserImpl::ParseMediaRule(nsresult& aErrorCode,
01449                                      RuleAppendFunc aAppendFunc,
01450                                      void* aData)
01451 {
01452   nsCOMPtr<nsMediaList> media = new nsMediaList();
01453   if (!media) {
01454     aErrorCode = NS_ERROR_OUT_OF_MEMORY;
01455     return PR_FALSE;
01456   }
01457 
01458   if (GatherMedia(aErrorCode, media, '{')) {
01459     NS_ASSERTION(media->Count() != 0, "media list must be nonempty");
01460     // XXXbz this could use better error reporting throughout the method
01461     nsRefPtr<nsCSSMediaRule> rule(new nsCSSMediaRule());
01462     // Append first, so when we do SetMedia() the rule
01463     // knows what its stylesheet is.
01464     if (rule && ParseGroupRule(aErrorCode, rule, aAppendFunc, aData)) {
01465       rule->SetMedia(media);
01466       return PR_TRUE;
01467     }
01468   }
01469 
01470   return PR_FALSE;
01471 }
01472 
01473 // Parse a @-moz-document rule.  This is like an @media rule, but instead
01474 // of a medium it has a nonempty list of items where each item is either
01475 // url(), url-prefix(), or domain().
01476 PRBool CSSParserImpl::ParseMozDocumentRule(nsresult& aErrorCode,
01477                                            RuleAppendFunc aAppendFunc,
01478                                            void* aData)
01479 {
01480   nsCSSDocumentRule::URL *urls = nsnull;
01481   nsCSSDocumentRule::URL **next = &urls;
01482   do {
01483     if (!GetToken(aErrorCode, PR_TRUE) ||
01484         eCSSToken_Function != mToken.mType ||
01485         !(mToken.mIdent.LowerCaseEqualsLiteral("url") ||
01486           mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
01487           mToken.mIdent.LowerCaseEqualsLiteral("domain"))) {
01488       REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc);
01489       delete urls;
01490       return PR_FALSE;
01491     }
01492     nsCSSDocumentRule::URL *cur = *next = new nsCSSDocumentRule::URL;
01493     if (!cur) {
01494       aErrorCode = NS_ERROR_OUT_OF_MEMORY;
01495       delete urls;
01496       return PR_FALSE;
01497     }
01498     next = &cur->next;
01499     if (mToken.mIdent.LowerCaseEqualsLiteral("url")) {
01500       cur->func = nsCSSDocumentRule::eURL;
01501     } else if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
01502       cur->func = nsCSSDocumentRule::eURLPrefix;
01503     } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
01504       cur->func = nsCSSDocumentRule::eDomain;
01505     }
01506 
01507     if (!ExpectSymbol(aErrorCode, '(', PR_FALSE) ||
01508         !GetURLToken(aErrorCode, PR_TRUE) ||
01509         (eCSSToken_String != mToken.mType &&
01510          eCSSToken_URL != mToken.mType)) {
01511       REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
01512       delete urls;
01513       return PR_FALSE;
01514     }
01515     if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
01516       delete urls;
01517       return PR_FALSE;
01518     }
01519 
01520     // We could try to make the URL (as long as it's not domain())
01521     // canonical and absolute with NS_NewURI and GetSpec, but I'm
01522     // inclined to think we shouldn't.
01523     CopyUTF16toUTF8(mToken.mIdent, cur->url);
01524   } while (ExpectSymbol(aErrorCode, ',', PR_TRUE));
01525 
01526   nsRefPtr<nsCSSDocumentRule> rule(new nsCSSDocumentRule());
01527   if (!rule) {
01528     aErrorCode = NS_ERROR_OUT_OF_MEMORY;
01529     delete urls;
01530     return PR_FALSE;
01531   }
01532   rule->SetURLs(urls);
01533 
01534   return ParseGroupRule(aErrorCode, rule, aAppendFunc, aData);
01535 }
01536 
01537 // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
01538 PRBool CSSParserImpl::ParseNameSpaceRule(nsresult& aErrorCode,
01539                                          RuleAppendFunc aAppendFunc,
01540                                          void* aData)
01541 {
01542   if (!GetToken(aErrorCode, PR_TRUE)) {
01543     REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
01544     return PR_FALSE;
01545   }
01546 
01547   nsAutoString  prefix;
01548   nsAutoString  url;
01549 
01550   if (eCSSToken_Ident == mToken.mType) {
01551     prefix = mToken.mIdent;
01552     ToLowerCase(prefix); // always case insensitive, since stays within CSS
01553     if (! GetToken(aErrorCode, PR_TRUE)) {
01554       REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
01555       return PR_FALSE;
01556     }
01557   }
01558 
01559   if (eCSSToken_String == mToken.mType) {
01560     url = mToken.mIdent;
01561     if (ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
01562       ProcessNameSpace(aErrorCode, prefix, url, aAppendFunc, aData);
01563       return PR_TRUE;
01564     }
01565   }
01566   else if ((eCSSToken_Function == mToken.mType) && 
01567            (mToken.mIdent.LowerCaseEqualsLiteral("url"))) {
01568     if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
01569       if (GetURLToken(aErrorCode, PR_TRUE)) {
01570         if ((eCSSToken_String == mToken.mType) || (eCSSToken_URL == mToken.mType)) {
01571           url = mToken.mIdent;
01572           if (ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
01573             if (ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
01574               ProcessNameSpace(aErrorCode, prefix, url, aAppendFunc, aData);
01575               return PR_TRUE;
01576             }
01577           }
01578         }
01579       }
01580     }
01581   }
01582   REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
01583 
01584   return PR_FALSE;
01585 }
01586 
01587 PRBool CSSParserImpl::ProcessNameSpace(nsresult& aErrorCode, const nsString& aPrefix, 
01588                                        const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
01589                                        void* aData)
01590 {
01591   PRBool result = PR_FALSE;
01592 
01593   nsCOMPtr<nsICSSNameSpaceRule> rule;
01594   nsCOMPtr<nsIAtom> prefix;
01595 
01596   if (!aPrefix.IsEmpty()) {
01597     prefix = do_GetAtom(aPrefix);
01598   }
01599 
01600   NS_NewCSSNameSpaceRule(getter_AddRefs(rule), prefix, aURLSpec);
01601   if (rule) {
01602     (*aAppendFunc)(rule, aData);
01603 
01604     // If this was the first namespace rule encountered, it will trigger
01605     // creation of a namespace map.
01606     if (!mNameSpaceMap) {
01607       mNameSpaceMap = mSheet->GetNameSpaceMap();
01608     }
01609   }
01610 
01611   return result;
01612 }
01613 
01614 PRBool CSSParserImpl::ParseFontFaceRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aData)
01615 {
01616   // XXX not yet implemented
01617   return PR_FALSE;
01618 }
01619 
01620 PRBool CSSParserImpl::ParsePageRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aData)
01621 {
01622   // XXX not yet implemented
01623   return PR_FALSE;
01624 }
01625 
01626 void CSSParserImpl::SkipUntil(nsresult& aErrorCode, PRUnichar aStopSymbol)
01627 {
01628   nsCSSToken* tk = &mToken;
01629   for (;;) {
01630     if (!GetToken(aErrorCode, PR_TRUE)) {
01631       break;
01632     }
01633     if (eCSSToken_Symbol == tk->mType) {
01634       PRUnichar symbol = tk->mSymbol;
01635       if (symbol == aStopSymbol) {
01636         break;
01637       } else if ('{' == symbol) {
01638         SkipUntil(aErrorCode, '}');
01639       } else if ('[' == symbol) {
01640         SkipUntil(aErrorCode, ']');
01641       } else if ('(' == symbol) {
01642         SkipUntil(aErrorCode, ')');
01643       }
01644     }
01645   }
01646 }
01647 
01648 PRBool CSSParserImpl::GetNonCloseParenToken(nsresult& aErrorCode, PRBool aSkipWS)
01649 {
01650   if (!GetToken(aErrorCode, aSkipWS))
01651     return PR_FALSE;
01652   if (mToken.mType == eCSSToken_Symbol && mToken.mSymbol == ')') {
01653     UngetToken();
01654     return PR_FALSE;
01655   }
01656   return PR_TRUE;
01657 }
01658 
01659 PRBool
01660 CSSParserImpl::SkipDeclaration(nsresult& aErrorCode, PRBool aCheckForBraces)
01661 {
01662   nsCSSToken* tk = &mToken;
01663   for (;;) {
01664     if (!GetToken(aErrorCode, PR_TRUE)) {
01665       if (aCheckForBraces) {
01666         REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
01667       }
01668       return PR_FALSE;
01669     }
01670     if (eCSSToken_Symbol == tk->mType) {
01671       PRUnichar symbol = tk->mSymbol;
01672       if (';' == symbol) {
01673         break;
01674       }
01675       if (aCheckForBraces) {
01676         if ('}' == symbol) {
01677           UngetToken();
01678           break;
01679         }
01680       }
01681       if ('{' == symbol) {
01682         SkipUntil(aErrorCode, '}');
01683       } else if ('(' == symbol) {
01684         SkipUntil(aErrorCode, ')');
01685       } else if ('[' == symbol) {
01686         SkipUntil(aErrorCode, ']');
01687       }
01688     }
01689   }
01690   return PR_TRUE;
01691 }
01692 
01693 void CSSParserImpl::SkipRuleSet(nsresult& aErrorCode)
01694 {
01695   nsCSSToken* tk = &mToken;
01696   for (;;) {
01697     if (!GetToken(aErrorCode, PR_TRUE)) {
01698       REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
01699       break;
01700     }
01701     if (eCSSToken_Symbol == tk->mType) {
01702       PRUnichar symbol = tk->mSymbol;
01703       if ('{' == symbol) {
01704         SkipUntil(aErrorCode, '}');
01705         break;
01706       }
01707       if ('(' == symbol) {
01708         SkipUntil(aErrorCode, ')');
01709       } else if ('[' == symbol) {
01710         SkipUntil(aErrorCode, ']');
01711       }
01712     }
01713   }
01714 }
01715 
01716 PRBool CSSParserImpl::PushGroup(nsICSSGroupRule* aRule)
01717 {
01718   if (mGroupStack.AppendObject(aRule))
01719     return PR_TRUE;
01720 
01721   return PR_FALSE;
01722 }
01723 
01724 void CSSParserImpl::PopGroup(void)
01725 {
01726   PRInt32 count = mGroupStack.Count();
01727   if (0 < count) {
01728     mGroupStack.RemoveObjectAt(count - 1);
01729   }
01730 }
01731 
01732 void CSSParserImpl::AppendRule(nsICSSRule* aRule)
01733 {
01734   PRInt32 count = mGroupStack.Count();
01735   if (0 < count) {
01736     mGroupStack[count - 1]->AppendStyleRule(aRule);
01737   }
01738   else {
01739     mSheet->AppendStyleRule(aRule);
01740   }
01741 }
01742 
01743 PRBool CSSParserImpl::ParseRuleSet(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aData)
01744 {
01745   // First get the list of selectors for the rule
01746   nsCSSSelectorList* slist = nsnull;
01747   PRUint32 linenum = mScanner.GetLineNumber();
01748   if (! ParseSelectorList(aErrorCode, slist)) {
01749     REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
01750     OUTPUT_ERROR();
01751     SkipRuleSet(aErrorCode);
01752     return PR_FALSE;
01753   }
01754   NS_ASSERTION(nsnull != slist, "null selector list");
01755   CLEAR_ERROR();
01756 
01757   // Next parse the declaration block
01758   nsCSSDeclaration* declaration = ParseDeclarationBlock(aErrorCode, PR_TRUE);
01759   if (nsnull == declaration) {
01760     // XXX skip something here
01761     delete slist;
01762     return PR_FALSE;
01763   }
01764 
01765 #if 0
01766   slist->Dump();
01767   fputs("{\n", stdout);
01768   declaration->List();
01769   fputs("}\n", stdout);
01770 #endif
01771 
01772   // Translate the selector list and declaration block into style data
01773 
01774   nsCOMPtr<nsICSSStyleRule> rule;
01775   NS_NewCSSStyleRule(getter_AddRefs(rule), slist, declaration);
01776   if (!rule) {
01777     aErrorCode = NS_ERROR_OUT_OF_MEMORY;
01778     delete slist;
01779     return PR_FALSE;
01780   }
01781   rule->SetLineNumber(linenum);
01782   (*aAppendFunc)(rule, aData);
01783 
01784   return PR_TRUE;
01785 }
01786 
01787 PRBool CSSParserImpl::ParseSelectorList(nsresult& aErrorCode,
01788                                         nsCSSSelectorList*& aListHead)
01789 {
01790   nsCSSSelectorList* list = nsnull;
01791   if (! ParseSelectorGroup(aErrorCode, list)) {
01792     // must have at least one selector group
01793     aListHead = nsnull;
01794     return PR_FALSE;
01795   }
01796   NS_ASSERTION(nsnull != list, "no selector list");
01797   aListHead = list;
01798 
01799   // After that there must either be a "," or a "{"
01800   nsCSSToken* tk = &mToken;
01801   for (;;) {
01802     if (! GetToken(aErrorCode, PR_TRUE)) {
01803       REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
01804       break;
01805     }
01806 
01807     if (eCSSToken_Symbol == tk->mType) {
01808       if (',' == tk->mSymbol) {
01809         nsCSSSelectorList* newList = nsnull;
01810         // Another selector group must follow
01811         if (! ParseSelectorGroup(aErrorCode, newList)) {
01812           break;
01813         }
01814         // add new list to the end of the selector list
01815         list->mNext = newList;
01816         list = newList;
01817         continue;
01818       } else if ('{' == tk->mSymbol) {
01819         UngetToken();
01820         return PR_TRUE;
01821       }
01822     }
01823     REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
01824     UngetToken();
01825     break;
01826   }
01827 
01828   delete aListHead;
01829   aListHead = nsnull;
01830   return PR_FALSE;
01831 }
01832 
01833 static PRBool IsSinglePseudoClass(const nsCSSSelector& aSelector)
01834 {
01835   return PRBool((aSelector.mNameSpace == kNameSpaceID_Unknown) && 
01836                 (aSelector.mTag == nsnull) && 
01837                 (aSelector.mIDList == nsnull) &&
01838                 (aSelector.mClassList == nsnull) &&
01839                 (aSelector.mAttrList == nsnull) &&
01840                 (aSelector.mNegations == nsnull) &&
01841                 (aSelector.mPseudoClassList != nsnull) &&
01842                 (aSelector.mPseudoClassList->mNext == nsnull));
01843 }
01844 
01845 #ifdef MOZ_XUL
01846 static PRBool IsTreePseudoElement(nsIAtom* aPseudo)
01847 {
01848   const char* str;
01849   aPseudo->GetUTF8String(&str);
01850   static const char moz_tree[] = ":-moz-tree-";
01851   return nsCRT::strncmp(str, moz_tree, PRInt32(sizeof(moz_tree)-1)) == 0;
01852 }
01853 #endif
01854 
01855 PRBool CSSParserImpl::ParseSelectorGroup(nsresult& aErrorCode,
01856                                          nsCSSSelectorList*& aList)
01857 {
01858   nsCSSSelectorList* list = nsnull;
01859   PRUnichar     combinator = PRUnichar(0);
01860   PRInt32       weight = 0;
01861   PRBool        havePseudoElement = PR_FALSE;
01862   PRBool        done = PR_FALSE;
01863   while (!done) {
01864     nsCSSSelector selector;
01865     nsSelectorParsingStatus parsingStatus = ParseSelector(aErrorCode, selector);
01866     if (parsingStatus == eSelectorParsingStatus_Empty) {
01867       if (!list) {
01868         REPORT_UNEXPECTED(PESelectorGroupNoSelector);
01869       }
01870       break;
01871     }
01872     if (parsingStatus == eSelectorParsingStatus_Error) {
01873       if (list) {
01874         delete list;
01875         list = nsnull;
01876       }
01877       break;
01878     }
01879     if (nsnull == list) {
01880       list = new nsCSSSelectorList();
01881       if (nsnull == list) {
01882         aErrorCode = NS_ERROR_OUT_OF_MEMORY;
01883         return PR_FALSE;
01884       }
01885     }
01886     list->AddSelector(selector);
01887     nsCSSSelector* listSel = list->mSelectors;
01888 
01889     // pull out pseudo elements here
01890     nsAtomStringList* prevList = nsnull;
01891     nsAtomStringList* pseudoClassList = listSel->mPseudoClassList;
01892     while (nsnull != pseudoClassList) {
01893       if (! nsCSSPseudoClasses::IsPseudoClass(pseudoClassList->mAtom)) {
01894         havePseudoElement = PR_TRUE;
01895         if (IsSinglePseudoClass(*listSel)) {  // convert to pseudo element selector
01896           nsIAtom* pseudoElement = pseudoClassList->mAtom;  // steal ref count
01897           pseudoClassList->mAtom = nsnull;
01898           listSel->Reset();
01899           if (listSel->mNext) {// more to the selector
01900             listSel->mOperator = PRUnichar('>');
01901             nsCSSSelector empty;
01902             list->AddSelector(empty); // leave a blank (universal) selector in the middle
01903             listSel = list->mSelectors; // use the new one for the pseudo
01904           }
01905           listSel->mTag = pseudoElement;
01906         }
01907         else {  // append new pseudo element selector
01908           selector.Reset();
01909           selector.mTag = pseudoClassList->mAtom; // steal ref count
01910 #ifdef MOZ_XUL
01911           if (IsTreePseudoElement(selector.mTag)) {
01912             // Take the remaining "pseudoclasses" that we parsed
01913             // inside the tree pseudoelement's ()-list, and
01914             // make our new selector have these pseudoclasses
01915             // in its pseudoclass list.
01916             selector.mPseudoClassList = pseudoClassList->mNext;
01917             pseudoClassList->mNext = nsnull;
01918           }
01919 #endif
01920           list->AddSelector(selector);
01921           pseudoClassList->mAtom = nsnull;
01922           listSel->mOperator = PRUnichar('>');
01923           if (nsnull == prevList) { // delete list entry
01924             listSel->mPseudoClassList = pseudoClassList->mNext;
01925           }
01926           else {
01927             prevList->mNext = pseudoClassList->mNext;
01928           }
01929           pseudoClassList->mNext = nsnull;
01930           delete pseudoClassList;
01931           weight += listSel->CalcWeight(); // capture weight from remainder
01932         }
01933         break;  // only one pseudo element per selector
01934       }
01935       prevList = pseudoClassList;
01936       pseudoClassList = pseudoClassList->mNext;
01937     }
01938 
01939     combinator = PRUnichar(0);
01940     if (!GetToken(aErrorCode, PR_FALSE)) {
01941       break;
01942     }
01943 
01944     // Assume we are done unless we find a combinator here.
01945     done = PR_TRUE;
01946     if (eCSSToken_WhiteSpace == mToken.mType) {
01947       if (!GetToken(aErrorCode, PR_TRUE)) {
01948         break;
01949       }
01950       done = PR_FALSE;
01951     }
01952 
01953     if (eCSSToken_Symbol == mToken.mType && 
01954         ('+' == mToken.mSymbol ||
01955          '>' == mToken.mSymbol ||
01956          '~' == mToken.mSymbol)) {
01957       done = PR_FALSE;
01958       combinator = mToken.mSymbol;
01959       list->mSelectors->SetOperator(combinator);
01960     }
01961     else {
01962       UngetToken(); // give it back to selector
01963     }
01964 
01965     if (havePseudoElement) {
01966       break;
01967     }
01968     else {
01969       weight += selector.CalcWeight();
01970     }
01971   }
01972 
01973   if (PRUnichar(0) != combinator) { // no dangling combinators
01974     if (list) {
01975       delete list;
01976       list = nsnull;
01977     }
01978     // This should report the problematic combinator
01979     REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
01980   }
01981   aList = list;
01982   if (nsnull != list) {
01983     list->mWeight = weight;
01984   }
01985   return PRBool(nsnull != aList);
01986 }
01987 
01988 #define SEL_MASK_NSPACE   0x01
01989 #define SEL_MASK_ELEM     0x02
01990 #define SEL_MASK_ID       0x04
01991 #define SEL_MASK_CLASS    0x08
01992 #define SEL_MASK_ATTRIB   0x10
01993 #define SEL_MASK_PCLASS   0x20
01994 #define SEL_MASK_PELEM    0x40
01995 
01996 //
01997 // Parses an ID selector #name
01998 //
01999 CSSParserImpl::nsSelectorParsingStatus
02000 CSSParserImpl::ParseIDSelector(PRInt32&       aDataMask,
02001                                nsCSSSelector& aSelector,
02002                                nsresult&      aErrorCode)
02003 {
02004   NS_ASSERTION(!mToken.mIdent.IsEmpty(),
02005                "Empty mIdent in eCSSToken_ID token?");
02006   aDataMask |= SEL_MASK_ID;
02007   aSelector.AddID(mToken.mIdent);
02008   return eSelectorParsingStatus_Continue;
02009 }
02010 
02011 //
02012 // Parses a class selector .name
02013 //
02014 CSSParserImpl::nsSelectorParsingStatus
02015 CSSParserImpl::ParseClassSelector(PRInt32&       aDataMask,
02016                                   nsCSSSelector& aSelector,
02017                                   nsresult&      aErrorCode)
02018 {
02019   if (! GetToken(aErrorCode, PR_FALSE)) { // get ident
02020     REPORT_UNEXPECTED_EOF(PEClassSelEOF);
02021     return eSelectorParsingStatus_Error;
02022   }
02023   if (eCSSToken_Ident != mToken.mType) {  // malformed selector
02024     REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
02025     UngetToken();
02026     return eSelectorParsingStatus_Error;
02027   }
02028   aDataMask |= SEL_MASK_CLASS;
02029 
02030   aSelector.AddClass(mToken.mIdent);
02031 
02032   return eSelectorParsingStatus_Continue;
02033 }
02034 
02035 //
02036 // Parse a type element selector or a universal selector
02037 // namespace|type or namespace|* or *|* or *
02038 //
02039 CSSParserImpl::nsSelectorParsingStatus
02040 CSSParserImpl::ParseTypeOrUniversalSelector(PRInt32&       aDataMask,
02041                                             nsCSSSelector& aSelector,
02042                                             nsresult&      aErrorCode,
02043                                             PRBool         aIsNegated)
02044 {
02045   nsAutoString buffer;
02046   if (mToken.IsSymbol('*')) {  // universal element selector, or universal namespace
02047     if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) {  // was namespace
02048       aDataMask |= SEL_MASK_NSPACE;
02049       aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
02050 
02051       if (! GetToken(aErrorCode, PR_FALSE)) {
02052         REPORT_UNEXPECTED_EOF(PETypeSelEOF);
02053         return eSelectorParsingStatus_Error;
02054       }
02055       if (eCSSToken_Ident == mToken.mType) {  // element name
02056         aDataMask |= SEL_MASK_ELEM;
02057         if (mCaseSensitive) {
02058           aSelector.SetTag(mToken.mIdent);
02059         }
02060         else {
02061           ToLowerCase(mToken.mIdent, buffer);
02062           aSelector.SetTag(buffer);
02063         }
02064       }
02065       else if (mToken.IsSymbol('*')) {  // universal selector
02066         aDataMask |= SEL_MASK_ELEM;
02067         // don't set tag
02068       }
02069       else {
02070         REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
02071         UngetToken();
02072         return eSelectorParsingStatus_Error;
02073       }
02074     }
02075     else {  // was universal element selector
02076       aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
02077       if (mNameSpaceMap) { // look for default namespace
02078         PRInt32 defaultID = mNameSpaceMap->FindNameSpaceID(nsnull);
02079         if (defaultID != kNameSpaceID_None) {
02080           aSelector.SetNameSpace(defaultID);
02081         }
02082       }
02083       aDataMask |= SEL_MASK_ELEM;
02084       // don't set any tag in the selector
02085     }
02086     if (! GetToken(aErrorCode, PR_FALSE)) {   // premature eof is ok (here!)
02087       return eSelectorParsingStatus_Done;
02088     }
02089   }
02090   else if (eCSSToken_Ident == mToken.mType) {    // element name or namespace name
02091     buffer = mToken.mIdent; // hang on to ident
02092 
02093     if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) {  // was namespace
02094       aDataMask |= SEL_MASK_NSPACE;
02095       PRInt32 nameSpaceID = kNameSpaceID_Unknown;
02096       if (mNameSpaceMap) {
02097         ToLowerCase(buffer); // always case insensitive, since stays within CSS
02098         nsCOMPtr<nsIAtom> prefix = do_GetAtom(buffer);
02099         nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
02100       } // else, no declared namespaces
02101       if (kNameSpaceID_Unknown == nameSpaceID) {  // unknown prefix, dump it
02102         const PRUnichar *params[] = {
02103           buffer.get()
02104         };
02105         REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, params);
02106         return eSelectorParsingStatus_Error;
02107       }
02108       aSelector.SetNameSpace(nameSpaceID);
02109 
02110       if (! GetToken(aErrorCode, PR_FALSE)) {
02111         REPORT_UNEXPECTED_EOF(PETypeSelEOF);
02112         return eSelectorParsingStatus_Error;
02113       }
02114       if (eCSSToken_Ident == mToken.mType) {  // element name
02115         aDataMask |= SEL_MASK_ELEM;
02116         if (mCaseSensitive) {
02117           aSelector.SetTag(mToken.mIdent);
02118         }
02119         else {
02120           ToLowerCase(mToken.mIdent, buffer);
02121           aSelector.SetTag(buffer);
02122         }
02123       }
02124       else if (mToken.IsSymbol('*')) {  // universal selector
02125         aDataMask |= SEL_MASK_ELEM;
02126         // don't set tag
02127       }
02128       else {
02129         REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
02130         UngetToken();
02131         return eSelectorParsingStatus_Error;
02132       }
02133     }
02134     else {  // was element name
02135       aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
02136       if (mNameSpaceMap) { // look for default namespace
02137         PRInt32 defaultID = mNameSpaceMap->FindNameSpaceID(nsnull);
02138         if (defaultID != kNameSpaceID_None) {
02139           aSelector.SetNameSpace(defaultID);
02140         }
02141       }
02142       if (mCaseSensitive) {
02143         aSelector.SetTag(buffer);
02144       }
02145       else {
02146         ToLowerCase(buffer);
02147         aSelector.SetTag(buffer);
02148       }
02149       aDataMask |= SEL_MASK_ELEM;
02150     }
02151     if (! GetToken(aErrorCode, PR_FALSE)) {   // premature eof is ok (here!)
02152       return eSelectorParsingStatus_Done;
02153     }
02154   }
02155   else if (mToken.IsSymbol('|')) {  // No namespace
02156     aDataMask |= SEL_MASK_NSPACE;
02157     aSelector.SetNameSpace(kNameSpaceID_None);  // explicit NO namespace
02158 
02159     // get mandatory tag
02160     if (! GetToken(aErrorCode, PR_FALSE)) {
02161       REPORT_UNEXPECTED_EOF(PETypeSelEOF);
02162       return eSelectorParsingStatus_Error;
02163     }
02164     if (eCSSToken_Ident == mToken.mType) {  // element name
02165       aDataMask |= SEL_MASK_ELEM;
02166       if (mCaseSensitive) {
02167         aSelector.SetTag(mToken.mIdent);
02168       }
02169       else {
02170         ToLowerCase(mToken.mIdent, buffer);
02171         aSelector.SetTag(buffer);
02172       }
02173     }
02174     else if (mToken.IsSymbol('*')) {  // universal selector
02175       aDataMask |= SEL_MASK_ELEM;
02176       // don't set tag
02177     }
02178     else {
02179       REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
02180       UngetToken();
02181       return eSelectorParsingStatus_Error;
02182     }
02183     if (! GetToken(aErrorCode, PR_FALSE)) {   // premature eof is ok (here!)
02184       return eSelectorParsingStatus_Done;
02185     }
02186   }
02187   else {
02188     // no tag or namespace: implied universal selector
02189     // set namespace to unknown since it is not specified
02190     aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
02191     if (mNameSpaceMap) { // look for default namespace
02192       PRInt32 defaultID = mNameSpaceMap->FindNameSpaceID(nsnull);
02193       if (defaultID != kNameSpaceID_None) {
02194         aSelector.SetNameSpace(defaultID);
02195       }
02196     }
02197   }
02198 
02199   if (aIsNegated) {
02200     // restore last token read in case of a negated type selector
02201     UngetToken();
02202   }
02203   return eSelectorParsingStatus_Continue;
02204 }
02205 
02206 //
02207 // Parse attribute selectors [attr], [attr=value], [attr|=value],
02208 // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
02209 //
02210 CSSParserImpl::nsSelectorParsingStatus
02211 CSSParserImpl::ParseAttributeSelector(PRInt32&       aDataMask,
02212                                       nsCSSSelector& aSelector,
02213                                       nsresult&      aErrorCode)
02214 {
02215   if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF
02216     REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
02217     return eSelectorParsingStatus_Error;
02218   }
02219 
02220   PRInt32 nameSpaceID = kNameSpaceID_None;
02221   nsAutoString  attr;
02222   if (mToken.IsSymbol('*')) { // wildcard namespace
02223     nameSpaceID = kNameSpaceID_Unknown;
02224     if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) {
02225       if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
02226         REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
02227         return eSelectorParsingStatus_Error;
02228       }
02229       if (eCSSToken_Ident == mToken.mType) { // attr name
02230         attr = mToken.mIdent;
02231       }
02232       else {
02233         REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
02234         UngetToken();
02235         return eSelectorParsingStatus_Error;
02236        }
02237     }
02238     else {
02239       REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
02240       return eSelectorParsingStatus_Error;
02241     }
02242   }
02243   else if (mToken.IsSymbol('|')) { // NO namespace
02244     if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
02245       REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
02246       return eSelectorParsingStatus_Error;
02247     }
02248     if (eCSSToken_Ident == mToken.mType) { // attr name
02249       attr = mToken.mIdent;
02250     }
02251     else {
02252       REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
02253       UngetToken();
02254       return eSelectorParsingStatus_Error;
02255     }
02256   }
02257   else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
02258     attr = mToken.mIdent; // hang on to it
02259     if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) {  // was a namespace
02260       nameSpaceID = kNameSpaceID_Unknown;
02261       if (mNameSpaceMap) {
02262         ToLowerCase(attr); // always case insensitive, since stays within CSS
02263         nsCOMPtr<nsIAtom> prefix = do_GetAtom(attr);
02264         nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
02265       } // else, no declared namespaces
02266       if (kNameSpaceID_Unknown == nameSpaceID) {  // unknown prefix, dump it
02267         const PRUnichar *params[] = {
02268           attr.get()
02269         };
02270         REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, params);
02271         return eSelectorParsingStatus_Error;
02272       }
02273       if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
02274         REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
02275         return eSelectorParsingStatus_Error;
02276       }
02277       if (eCSSToken_Ident == mToken.mType) { // attr name
02278         attr = mToken.mIdent;
02279       }
02280       else {
02281         REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
02282         UngetToken();
02283         return eSelectorParsingStatus_Error;
02284       }
02285     }
02286   }
02287   else {  // malformed
02288     REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
02289     UngetToken();
02290     return eSelectorParsingStatus_Error;
02291   }
02292 
02293   if (! mCaseSensitive) {
02294     ToLowerCase(attr);
02295   }
02296   if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF
02297     REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
02298     return eSelectorParsingStatus_Error;
02299   }
02300   if ((eCSSToken_Symbol == mToken.mType) ||
02301       (eCSSToken_Includes == mToken.mType) ||
02302       (eCSSToken_Dashmatch == mToken.mType) ||
02303       (eCSSToken_Beginsmatch == mToken.mType) ||
02304       (eCSSToken_Endsmatch == mToken.mType) ||
02305       (eCSSToken_Containsmatch == mToken.mType)) {
02306     PRUint8 func;
02307     if (eCSSToken_Includes == mToken.mType) {
02308       func = NS_ATTR_FUNC_INCLUDES;
02309     }
02310     else if (eCSSToken_Dashmatch == mToken.mType) {
02311       func = NS_ATTR_FUNC_DASHMATCH;
02312     }
02313     else if (eCSSToken_Beginsmatch == mToken.mType) {
02314       func = NS_ATTR_FUNC_BEGINSMATCH;
02315     }
02316     else if (eCSSToken_Endsmatch == mToken.mType) {
02317       func = NS_ATTR_FUNC_ENDSMATCH;
02318     }
02319     else if (eCSSToken_Containsmatch == mToken.mType) {
02320       func = NS_ATTR_FUNC_CONTAINSMATCH;
02321     }
02322     else if (']' == mToken.mSymbol) {
02323       aDataMask |= SEL_MASK_ATTRIB;
02324       aSelector.AddAttribute(nameSpaceID, attr);
02325       func = NS_ATTR_FUNC_SET;
02326     }
02327     else if ('=' == mToken.mSymbol) {
02328       func = NS_ATTR_FUNC_EQUALS;
02329     }
02330     else {
02331       REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
02332       UngetToken(); // bad function
02333       return eSelectorParsingStatus_Error;
02334     }
02335     if (NS_ATTR_FUNC_SET != func) { // get value
02336       if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF
02337         REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
02338         return eSelectorParsingStatus_Error;
02339       }
02340       if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
02341         nsAutoString  value(mToken.mIdent);
02342         if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF
02343           REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
02344           return eSelectorParsingStatus_Error;
02345         }
02346         if (mToken.IsSymbol(']')) {
02347           PRBool isCaseSensitive = mCaseSensitive;
02348           if (nameSpaceID == kNameSpaceID_None ||
02349               nameSpaceID == kNameSpaceID_XHTML) {
02350             static const char* caseSensitiveHTMLAttribute[] = {
02351               // list based on http://www.w3.org/TR/REC-html40/index/attributes.html
02352               "abbr",          "alt",        "label",
02353               "prompt",        "standby",    "summary",
02354               "title",         "class",      "archive",
02355               "cite",          "datetime",   "href",
02356               "name",          nsnull
02357             };
02358             short i = 0;
02359             const char* htmlAttr;
02360             while ((htmlAttr = caseSensitiveHTMLAttribute[i++])) {
02361               if (attr.EqualsIgnoreCase(htmlAttr)) {
02362                 isCaseSensitive = PR_TRUE;
02363                 break;
02364               }
02365             }
02366           }
02367           aDataMask |= SEL_MASK_ATTRIB;
02368           aSelector.AddAttribute(nameSpaceID, attr, func, value, isCaseSensitive);
02369         }
02370         else {
02371           REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
02372           UngetToken();
02373           return eSelectorParsingStatus_Error;
02374         }
02375       }
02376       else {
02377         REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
02378         UngetToken();
02379         return eSelectorParsingStatus_Error;
02380       }
02381     }
02382   }
02383   else {
02384     REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
02385     UngetToken(); // bad dog, no biscut!
02386     return eSelectorParsingStatus_Error;
02387    }
02388    return eSelectorParsingStatus_Continue;
02389 }
02390 
02391 //
02392 // Parse pseudo-classes and pseudo-elements
02393 //
02394 CSSParserImpl::nsSelectorParsingStatus
02395 CSSParserImpl::ParsePseudoSelector(PRInt32&       aDataMask,
02396                                    nsCSSSelector& aSelector,
02397                                    nsresult&      aErrorCode,
02398                                    PRBool         aIsNegated)
02399 {
02400   if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof
02401     REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
02402     return eSelectorParsingStatus_Error;
02403   }
02404 
02405   // First, find out whether we are parsing a CSS3 pseudo-element
02406   PRBool parsingPseudoElement = PR_FALSE;
02407   if (mToken.IsSymbol(':')) {
02408     parsingPseudoElement = PR_TRUE;
02409     if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof
02410       REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
02411       return eSelectorParsingStatus_Error;
02412     }
02413   }
02414 
02415   // Do some sanity-checking on the token
02416   if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
02417     // malformed selector
02418     REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
02419     UngetToken();
02420     return eSelectorParsingStatus_Error;
02421   }
02422 
02423   // OK, now we know we have an mIdent.  Atomize it.  All the atoms, for
02424   // pseudo-classes as well as pseudo-elements, start with a single ':'.
02425   nsAutoString buffer;
02426   buffer.Append(PRUnichar(':'));
02427   buffer.Append(mToken.mIdent);
02428   ToLowerCase(buffer);
02429   nsCOMPtr<nsIAtom> pseudo = do_GetAtom(buffer);
02430 
02431   // stash away some info about this pseudo so we only have to get it once.
02432   PRBool isTreePseudo = PR_FALSE;
02433 #ifdef MOZ_XUL
02434   isTreePseudo = IsTreePseudoElement(pseudo);
02435   // If a tree pseudo-element is using the function syntax, it will
02436   // get isTree set here and will pass the check below that only
02437   // allows functions if they are in our list of things allowed to be
02438   // functions.  If it is _not_ using the function syntax, isTree will
02439   // be false, and it will still pass that check.  So the tree
02440   // pseudo-elements are allowed to be either functions or not, as
02441   // desired.
02442   PRBool isTree = (eCSSToken_Function == mToken.mType) &&
02443                   IsTreePseudoElement(pseudo);
02444 #endif
02445   PRBool isPseudoElement = nsCSSPseudoElements::IsPseudoElement(pseudo);
02446   // anonymous boxes are only allowed if they're the tree boxes or we have
02447   // enabled unsafe rules
02448   PRBool isAnonBox = nsCSSAnonBoxes::IsAnonBox(pseudo) &&
02449     (mUnsafeRulesEnabled || isTreePseudo);
02450 
02451   // If it's a function token, it better be on our "ok" list, and if the name
02452   // is that of a function pseudo it better be a function token
02453   if ((eCSSToken_Function == mToken.mType) !=
02454       (
02455 #ifdef MOZ_XUL
02456        isTree ||
02457 #endif
02458        nsCSSPseudoClasses::notPseudo == pseudo ||
02459        nsCSSPseudoClasses::lang == pseudo)) { // There are no other function pseudos
02460     REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
02461     UngetToken();
02462     return eSelectorParsingStatus_Error;
02463   }
02464   
02465   // If it starts with "::", it better be a pseudo-element
02466   if (parsingPseudoElement &&
02467       !isPseudoElement &&
02468       !isAnonBox) {
02469     REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
02470     UngetToken();
02471     return eSelectorParsingStatus_Error;
02472   }
02473 
02474   if (nsCSSPseudoClasses::notPseudo == pseudo) {
02475     if (aIsNegated) { // :not() can't be itself negated
02476       REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
02477       UngetToken();
02478       return eSelectorParsingStatus_Error;
02479     }
02480     // CSS 3 Negation pseudo-class takes one simple selector as argument
02481     nsSelectorParsingStatus parsingStatus =
02482       ParseNegatedSimpleSelector(aDataMask, aSelector, aErrorCode);
02483     if (eSelectorParsingStatus_Continue != parsingStatus) {
02484       return parsingStatus;
02485     }
02486   }    
02487   else if (!parsingPseudoElement &&
02488            nsCSSPseudoClasses::IsPseudoClass(pseudo)) {
02489     aDataMask |= SEL_MASK_PCLASS;
02490     if (nsCSSPseudoClasses::lang == pseudo) {
02491       nsSelectorParsingStatus parsingStatus = ParseLangSelector(aSelector, aErrorCode);
02492       if (eSelectorParsingStatus_Continue != parsingStatus) {
02493         return parsingStatus;
02494       }
02495     }
02496     // XXX are there more pseudo classes which accept arguments ?
02497     else {
02498       aSelector.AddPseudoClass(pseudo);
02499     }
02500   }
02501   else if (isPseudoElement || isAnonBox) {
02502     // Pseudo-element.  Make some more sanity checks.
02503     
02504     if (aIsNegated) { // pseudo-elements can't be negated
02505       REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
02506       UngetToken();
02507       return eSelectorParsingStatus_Error;
02508     }
02509     // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
02510     // to have a single ':' on them.  Others (CSS3+ pseudo-elements and
02511     // various -moz-* pseudo-elements) must have |parsingPseudoElement|
02512     // set.
02513     if (!parsingPseudoElement &&
02514         !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
02515 #ifdef MOZ_XUL
02516         && !isTreePseudo
02517 #endif
02518         ) {
02519       REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
02520       UngetToken();
02521       return eSelectorParsingStatus_Error;
02522     }
02523 
02524     if (0 == (aDataMask & SEL_MASK_PELEM)) {
02525       aDataMask |= SEL_MASK_PELEM;
02526       aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later
02527 
02528 #ifdef MOZ_XUL
02529       if (isTree) {
02530         // We have encountered a pseudoelement of the form
02531         // -moz-tree-xxxx(a,b,c).  We parse (a,b,c) and add each
02532         // item in the list to the pseudoclass list.  They will be pulled
02533         // from the list later along with the pseudo-element.
02534         if (!ParseTreePseudoElement(aErrorCode, aSelector)) {
02535           return eSelectorParsingStatus_Error;
02536         }
02537       }
02538 #endif
02539 
02540       // ensure selector ends here, must be followed by EOF, space, '{' or ','
02541       if (GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
02542         if ((eCSSToken_WhiteSpace == mToken.mType) || 
02543             (mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
02544           UngetToken();
02545           return eSelectorParsingStatus_Done;
02546         }
02547         REPORT_UNEXPECTED_TOKEN(PEPseudoSelTrailing);
02548         UngetToken();
02549         return eSelectorParsingStatus_Error;
02550       }
02551     }
02552     else {  // multiple pseudo elements, not legal
02553       REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
02554       UngetToken();
02555       return eSelectorParsingStatus_Error;
02556     }
02557   } else {
02558     // Not a pseudo-class, not a pseudo-element.... forget it
02559     REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
02560     UngetToken();
02561     return eSelectorParsingStatus_Error;
02562   }
02563   return eSelectorParsingStatus_Continue;
02564 }
02565 
02566 //
02567 // Parse the argument of a negation pseudo-class :not()
02568 //
02569 CSSParserImpl::nsSelectorParsingStatus
02570 CSSParserImpl::ParseNegatedSimpleSelector(PRInt32&       aDataMask,
02571                                           nsCSSSelector& aSelector,
02572                                           nsresult&      aErrorCode)
02573 {
02574   // Check if we have the first parenthesis
02575   if (!ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
02576     REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
02577     return eSelectorParsingStatus_Error;
02578   }
02579 
02580   if (! GetToken(aErrorCode, PR_TRUE)) { // premature eof
02581     REPORT_UNEXPECTED_EOF(PENegationEOF);
02582     return eSelectorParsingStatus_Error;
02583   }
02584   if (!aSelector.mNegations) {
02585     aSelector.mNegations = new nsCSSSelector();
02586     if (!aSelector.mNegations) {
02587       aErrorCode = NS_ERROR_OUT_OF_MEMORY;
02588       return eSelectorParsingStatus_Error;
02589     }
02590   }
02591   // ID, class and attribute selectors and pseudo-classes are stored in
02592   // the first mNegations attached to a selector
02593   nsSelectorParsingStatus parsingStatus;
02594   if (eCSSToken_ID == mToken.mType) { // #id
02595     parsingStatus = ParseIDSelector(aDataMask, *aSelector.mNegations, aErrorCode);
02596   }
02597   else if (mToken.IsSymbol('.')) {    // .class
02598     parsingStatus = ParseClassSelector(aDataMask, *aSelector.mNegations, aErrorCode);
02599   }
02600   else if (mToken.IsSymbol(':')) {    // :pseudo
02601     parsingStatus = ParsePseudoSelector(aDataMask, *aSelector.mNegations, aErrorCode, PR_TRUE);
02602   }
02603   else if (mToken.IsSymbol('[')) {    // [attribute
02604     parsingStatus = ParseAttributeSelector(aDataMask, *aSelector.mNegations, aErrorCode);
02605   }
02606   else {
02607     // then it should be a type element or universal selector
02608     nsCSSSelector *newSel = new nsCSSSelector();
02609     if (!newSel) {
02610       aErrorCode = NS_ERROR_OUT_OF_MEMORY;
02611       return eSelectorParsingStatus_Error;
02612     }
02613     nsCSSSelector* negations = aSelector.mNegations;
02614     while (nsnull != negations->mNegations) {
02615       negations = negations->mNegations;
02616     }
02617     // negated type element selectors and universal selectors are stored after the first
02618     // mNegations containing only negated IDs, classes, attributes and pseudo-classes
02619     negations->mNegations = newSel;
02620     parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, aErrorCode, PR_TRUE);
02621   }
02622   if (eSelectorParsingStatus_Error == parsingStatus) {
02623     REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
02624     return parsingStatus;
02625   }
02626   // close the parenthesis
02627   if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
02628     REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
02629     return eSelectorParsingStatus_Error;
02630   }
02631 
02632   return eSelectorParsingStatus_Continue;
02633 }
02634 
02635 //
02636 // Parse the argument of a pseudo-class :lang()
02637 //
02638 CSSParserImpl::nsSelectorParsingStatus
02639 CSSParserImpl::ParseLangSelector(nsCSSSelector& aSelector, nsresult& aErrorCode)
02640 {
02641   // Check if we have the first parenthesis
02642   if (!ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
02643     REPORT_UNEXPECTED_TOKEN(PELangNoArg);
02644     return eSelectorParsingStatus_Error;
02645   }
02646 
02647   if (! GetToken(aErrorCode, PR_TRUE)) { // premature eof
02648     REPORT_UNEXPECTED_EOF(PELangArgEOF);
02649     return eSelectorParsingStatus_Error;
02650   }
02651   // We expect an identifier with a language abbreviation
02652   if (eCSSToken_Ident != mToken.mType) {
02653     REPORT_UNEXPECTED_TOKEN(PELangArgNotIdent);
02654     UngetToken();
02655     return eSelectorParsingStatus_Error;
02656   }
02657 
02658   // Add the pseudo with the language parameter
02659   aSelector.AddPseudoClass(nsCSSPseudoClasses::lang, mToken.mIdent.get());
02660 
02661   // close the parenthesis
02662   if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
02663     REPORT_UNEXPECTED_TOKEN(PELangNoClose);
02664     return eSelectorParsingStatus_Error;
02665   }
02666 
02667   return eSelectorParsingStatus_Continue;
02668 }
02669 
02674 CSSParserImpl::nsSelectorParsingStatus
02675 CSSParserImpl::ParseSelector(nsresult& aErrorCode, nsCSSSelector& aSelector)
02676 {
02677   if (! GetToken(aErrorCode, PR_TRUE)) {
02678     REPORT_UNEXPECTED_EOF(PESelectorEOF);
02679     return eSelectorParsingStatus_Error;
02680   }
02681 
02682   PRInt32 dataMask = 0;
02683   nsSelectorParsingStatus parsingStatus =
02684     ParseTypeOrUniversalSelector(dataMask, aSelector, aErrorCode, PR_FALSE);
02685   if (parsingStatus != eSelectorParsingStatus_Continue) {
02686     return parsingStatus;
02687   }
02688 
02689   for (;;) {
02690     if (eCSSToken_ID == mToken.mType) { // #id
02691       parsingStatus = ParseIDSelector(dataMask, aSelector, aErrorCode);
02692     }
02693     else if (mToken.IsSymbol('.')) {    // .class
02694       parsingStatus = ParseClassSelector(dataMask, aSelector, aErrorCode);
02695     }
02696     else if (mToken.IsSymbol(':')) {    // :pseudo
02697       parsingStatus = ParsePseudoSelector(dataMask, aSelector, aErrorCode, PR_FALSE);
02698     }
02699     else if (mToken.IsSymbol('[')) {    // [attribute
02700       parsingStatus = ParseAttributeSelector(dataMask, aSelector, aErrorCode);
02701     }
02702     else {  // not a selector token, we're done
02703       parsingStatus = eSelectorParsingStatus_Done;
02704       break;
02705     }
02706 
02707     if (parsingStatus != eSelectorParsingStatus_Continue) {
02708       return parsingStatus;
02709     }
02710 
02711     if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
02712       return eSelectorParsingStatus_Done;
02713     }
02714   }
02715   UngetToken();
02716   return dataMask ? parsingStatus : eSelectorParsingStatus_Empty;
02717 }
02718 
02719 nsCSSDeclaration*
02720 CSSParserImpl::ParseDeclarationBlock(nsresult& aErrorCode,
02721                                      PRBool aCheckForBraces)
02722 {
02723   if (aCheckForBraces) {
02724     if (!ExpectSymbol(aErrorCode, '{', PR_TRUE)) {
02725       REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
02726       OUTPUT_ERROR();
02727       return nsnull;
02728     }
02729   }
02730   nsCSSDeclaration* declaration = new nsCSSDeclaration();
02731   mData.AssertInitialState();
02732   if (declaration) {
02733     for (;;) {
02734       PRBool changed;
02735       if (!ParseDeclaration(aErrorCode, declaration, aCheckForBraces,
02736                             PR_TRUE, &changed)) {
02737         if (!SkipDeclaration(aErrorCode, aCheckForBraces)) {
02738           break;
02739         }
02740         if (aCheckForBraces) {
02741           if (ExpectSymbol(aErrorCode, '}', PR_TRUE)) {
02742             break;
02743           }
02744         }
02745         // Since the skipped declaration didn't end the block we parse
02746         // the next declaration.
02747       }
02748     }
02749     declaration->CompressFrom(&mData);
02750   }
02751   return declaration;
02752 }
02753 
02754 // The types to pass to ParseColorComponent.  These correspond to the
02755 // various datatypes that can go within rgb().
02756 #define COLOR_TYPE_UNKNOWN 0
02757 #define COLOR_TYPE_INTEGERS 1
02758 #define COLOR_TYPE_PERCENTAGES 2
02759 
02760 PRBool CSSParserImpl::ParseColor(nsresult& aErrorCode, nsCSSValue& aValue)
02761 {
02762   if (!GetToken(aErrorCode, PR_TRUE)) {
02763     REPORT_UNEXPECTED_EOF(PEColorEOF);
02764     return PR_FALSE;
02765   }
02766 
02767   nsCSSToken* tk = &mToken;
02768   nscolor rgba;
02769   switch (tk->mType) {
02770     case eCSSToken_ID:
02771     case eCSSToken_Ref:
02772       // #xxyyzz
02773       if (NS_HexToRGB(tk->mIdent, &rgba)) {
02774         aValue.SetColorValue(rgba);
02775         return PR_TRUE;
02776       }
02777       break;
02778 
02779     case eCSSToken_Ident:
02780       if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
02781         aValue.SetStringValue(tk->mIdent, eCSSUnit_String);
02782         return PR_TRUE;
02783       }
02784       else {
02785         nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
02786         if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
02787           PRInt32 value;
02788           if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) {
02789             aValue.SetIntValue(value, eCSSUnit_Integer);
02790             return PR_TRUE;
02791           }
02792         }
02793       }
02794       break;
02795     case eCSSToken_Function:
02796       if (mToken.mIdent.LowerCaseEqualsLiteral("rgb")) {
02797         // rgb ( component , component , component )
02798         PRUint8 r, g, b;
02799         PRInt32 type = COLOR_TYPE_UNKNOWN;
02800         if (ExpectSymbol(aErrorCode, '(', PR_FALSE) && // this won't fail
02801             ParseColorComponent(aErrorCode, r, type, ',') &&
02802             ParseColorComponent(aErrorCode, g, type, ',') &&
02803             ParseColorComponent(aErrorCode, b, type, ')')) {
02804           aValue.SetColorValue(NS_RGB(r,g,b));
02805           return PR_TRUE;
02806         }
02807         return PR_FALSE;  // already pushed back
02808       }
02809       else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-rgba") ||
02810                (mHandleAlphaColors && mToken.mIdent.LowerCaseEqualsLiteral("rgba"))) {
02811         // rgba ( component , component , component , opacity )
02812         PRUint8 r, g, b, a;
02813         PRInt32 type = COLOR_TYPE_UNKNOWN;
02814         if (ExpectSymbol(aErrorCode, '(', PR_FALSE) && // this won't fail
02815             ParseColorComponent(aErrorCode, r, type, ',') &&
02816             ParseColorComponent(aErrorCode, g, type, ',') &&
02817             ParseColorComponent(aErrorCode, b, type, ',') &&
02818             ParseColorOpacity(aErrorCode, a)) {
02819           aValue.SetColorValue(NS_RGBA(r, g, b, a));
02820           return PR_TRUE;
02821         }
02822         return PR_FALSE;  // already pushed back
02823       }
02824       else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
02825         // hsl ( hue , saturation , lightness )
02826         // "hue" is a number, "saturation" and "lightness" are percentages.
02827         if (ParseHSLColor(aErrorCode, rgba, ')')) {
02828           aValue.SetColorValue(rgba);
02829           return PR_TRUE;
02830         }
02831         return PR_FALSE;
02832       }
02833       else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-hsla") ||
02834                (mHandleAlphaColors && mToken.mIdent.LowerCaseEqualsLiteral("hsla"))) {
02835         // hsla ( hue , saturation , lightness , opacity )
02836         // "hue" is a number, "saturation" and "lightness" are percentages,
02837         // "opacity" is a number.
02838         PRUint8 a;
02839         if (ParseHSLColor(aErrorCode, rgba, ',') &&
02840             ParseColorOpacity(aErrorCode, a)) {
02841           aValue.SetColorValue(NS_RGBA(NS_GET_R(rgba), NS_GET_G(rgba),
02842                                        NS_GET_B(rgba), a));
02843           return PR_TRUE;
02844         }
02845         return PR_FALSE;
02846       }
02847       break;
02848     default:
02849       break;
02850   }
02851 
02852   // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
02853   if (mNavQuirkMode && !IsParsingCompoundProperty()) {
02854     // - If the string starts with 'a-f', the nsCSSScanner builds the
02855     //   token as a eCSSToken_Ident and we can parse the string as a
02856     //   'xxyyzz' RGB color.
02857     // - If it only contains '0-9' digits, the token is a
02858     //   eCSSToken_Number and it must be converted back to a 6
02859     //   characters string to be parsed as a RGB color.
02860     // - If it starts with '0-9' and contains any 'a-f', the token is a
02861     //   eCSSToken_Dimension, the mNumber part must be converted back to
02862     //   a string and the mIdent part must be appended to that string so
02863     //   that the resulting string has 6 characters.
02864     // Note: This is a hack for Nav compatibility.  Do not attempt to
02865     // simplify it by hacking into the ncCSSScanner.  This would be very
02866     // bad.
02867     nsAutoString str;
02868     char buffer[20];
02869     switch (tk->mType) {
02870       case eCSSToken_Ident:
02871         str.Assign(tk->mIdent);
02872         break;
02873 
02874       case eCSSToken_Number:
02875         if (tk->mIntegerValid) {
02876           PR_snprintf(buffer, sizeof(buffer), "%06d", tk->mInteger);
02877           str.AssignWithConversion(buffer);
02878         }
02879         break;
02880 
02881       case eCSSToken_Dimension:
02882         if (tk->mIdent.Length() <= 6) {
02883           PR_snprintf(buffer, sizeof(buffer), "%06.0f", tk->mNumber);
02884           nsAutoString temp;
02885           temp.AssignWithConversion(buffer);
02886           temp.Right(str, 6 - tk->mIdent.Length());
02887           str.Append(tk->mIdent);
02888         }
02889         break;
02890       default:
02891         // There is a whole bunch of cases that are
02892         // not handled by this switch.  Ignore them.
02893         break;
02894     }
02895     if (NS_HexToRGB(str, &rgba)) {
02896       aValue.SetColorValue(rgba);
02897       return PR_TRUE;
02898     }
02899   }
02900 
02901   // It's not a color
02902   REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
02903   UngetToken();
02904   return PR_FALSE;
02905 }
02906 
02907 // aType will be set if we have already parsed other color components
02908 // in this color spec
02909 PRBool CSSParserImpl::ParseColorComponent(nsresult& aErrorCode,
02910                                           PRUint8& aComponent,
02911                                           PRInt32& aType,
02912                                           char aStop)
02913 {
02914   if (!GetToken(aErrorCode, PR_TRUE)) {
02915     REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
02916     return PR_FALSE;
02917   }
02918   float value;
02919   nsCSSToken* tk = &mToken;
02920   switch (tk->mType) {
02921   case eCSSToken_Number:
02922     switch (aType) {
02923       case COLOR_TYPE_UNKNOWN:
02924         aType = COLOR_TYPE_INTEGERS;
02925         break;
02926       case COLOR_TYPE_INTEGERS:
02927         break;
02928       case COLOR_TYPE_PERCENTAGES:
02929         REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
02930         UngetToken();
02931         return PR_FALSE;
02932       default:
02933         NS_NOTREACHED("Someone forgot to add the new color component type in here");
02934     }
02935 
02936     if (!mToken.mIntegerValid) {
02937       REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
02938       UngetToken();
02939       return PR_FALSE;
02940     }
02941     value = tk->mNumber;
02942     break;
02943   case eCSSToken_Percentage:
02944     switch (aType) {
02945       case COLOR_TYPE_UNKNOWN:
02946         aType = COLOR_TYPE_PERCENTAGES;
02947         break;
02948       case COLOR_TYPE_INTEGERS:
02949         REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
02950         UngetToken();
02951         return PR_FALSE;
02952       case COLOR_TYPE_PERCENTAGES:
02953         break;
02954       default:
02955         NS_NOTREACHED("Someone forgot to add the new color component type in here");
02956     }
02957     value = tk->mNumber * 255.0f;
02958     break;
02959   default:
02960     REPORT_UNEXPECTED_TOKEN(PEColorBadRGBContents);
02961     UngetToken();
02962     return PR_FALSE;
02963   }
02964   if (ExpectSymbol(aErrorCode, aStop, PR_TRUE)) {
02965     if (value < 0.0f) value = 0.0f;
02966     if (value > 255.0f) value = 255.0f;
02967     aComponent = (PRUint8) value;
02968     return PR_TRUE;
02969   }
02970   const PRUnichar stopString[] = { PRUnichar(aStop), PRUnichar(0) };
02971   const PRUnichar *params[] = {
02972     nsnull,
02973     stopString
02974   };
02975   REPORT_UNEXPECTED_TOKEN_P(PEColorComponentBadTerm, params);
02976   return PR_FALSE;
02977 }
02978 
02979 
02980 PRBool CSSParserImpl::ParseHSLColor(nsresult& aErrorCode, nscolor& aColor,
02981                                     char aStop)
02982 {
02983   float h, s, l;
02984   if (!ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
02985     NS_ERROR("How did this get to be a function token?");
02986     return PR_FALSE;
02987   }
02988 
02989   // Get the hue
02990   if (!GetToken(aErrorCode, PR_TRUE)) {
02991     REPORT_UNEXPECTED_EOF(PEColorHueEOF);
02992     return PR_FALSE;
02993   }
02994   if (mToken.mType != eCSSToken_Number) {
02995     REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
02996     UngetToken();
02997     return PR_FALSE;
02998   }
02999   h = mToken.mNumber;
03000   h /= 360.0f;
03001   // hue values are wraparound
03002   h = h - floor(h);
03003   
03004   if (!ExpectSymbol(aErrorCode, ',', PR_TRUE)) {
03005     REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
03006     return PR_FALSE;
03007   }
03008   
03009   // Get the saturation
03010   if (!GetToken(aErrorCode, PR_TRUE)) {
03011     REPORT_UNEXPECTED_EOF(PEColorSaturationEOF);
03012     return PR_FALSE;
03013   }
03014   if (mToken.mType != eCSSToken_Percentage) {
03015     REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
03016     UngetToken();
03017     return PR_FALSE;
03018   }
03019   s = mToken.mNumber;
03020   if (s < 0.0f) s = 0.0f;
03021   if (s > 1.0f) s = 1.0f;
03022   
03023   if (!ExpectSymbol(aErrorCode, ',', PR_TRUE)) {
03024     REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
03025     return PR_FALSE;
03026   }
03027 
03028   // Get the lightness
03029   if (!GetToken(aErrorCode, PR_TRUE)) {
03030     REPORT_UNEXPECTED_EOF(PEColorLightnessEOF);
03031     return PR_FALSE;
03032   }
03033   if (mToken.mType != eCSSToken_Percentage) {
03034     REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
03035     UngetToken();
03036     return PR_FALSE;
03037   }
03038   l = mToken.mNumber;
03039   if (l < 0.0f) l = 0.0f;
03040   if (l > 1.0f) l = 1.0f;
03041         
03042   if (ExpectSymbol(aErrorCode, aStop, PR_TRUE)) {
03043     aColor = NS_HSL2RGB(h, s, l);
03044     return PR_TRUE;
03045   }
03046   
03047   const PRUnichar stopString[] = { PRUnichar(aStop), PRUnichar(0) };
03048   const PRUnichar *params[] = {
03049     nsnull,
03050     stopString
03051   };
03052   REPORT_UNEXPECTED_TOKEN_P(PEColorComponentBadTerm, params);
03053   return PR_FALSE;
03054 }
03055  
03056  
03057 PRBool CSSParserImpl::ParseColorOpacity(nsresult& aErrorCode, PRUint8& aOpacity)
03058 {
03059   if (!GetToken(aErrorCode, PR_TRUE)) {
03060     REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
03061     return PR_FALSE;
03062   }
03063 
03064   if (mToken.mType != eCSSToken_Number) {
03065     REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
03066     UngetToken();
03067     return PR_FALSE;
03068   }
03069 
03070   PRInt32 value = NSToIntRound(mToken.mNumber*255);
03071 
03072   if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
03073     REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
03074     return PR_FALSE;
03075   }
03076   
03077   if (value < 0) value = 0;
03078   if (value > 255) value = 255;
03079   aOpacity = (PRUint8)value;
03080 
03081   return PR_TRUE;
03082 }
03083 
03084 #ifdef MOZ_XUL
03085 PRBool CSSParserImpl::ParseTreePseudoElement(nsresult& aErrorCode,
03086                                                  nsCSSSelector& aSelector)
03087 {
03088   if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
03089     while (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
03090       if (!GetToken(aErrorCode, PR_TRUE)) {
03091         return PR_FALSE;
03092       }
03093       else if (eCSSToken_Ident == mToken.mType) {
03094         nsCOMPtr<nsIAtom> pseudo = do_GetAtom(mToken.mIdent);
03095         aSelector.AddPseudoClass(pseudo);
03096       }
03097       else if (eCSSToken_Symbol == mToken.mType) {
03098         if (!mToken.IsSymbol(','))
03099           return PR_FALSE;
03100       }
03101       else return PR_FALSE;
03102     }
03103     return PR_TRUE;
03104   }
03105   return PR_FALSE; 
03106 }
03107 #endif
03108 
03109 //----------------------------------------------------------------------
03110 
03111 PRBool
03112 CSSParserImpl::ParseDeclaration(nsresult& aErrorCode,
03113                                 nsCSSDeclaration* aDeclaration,
03114                                 PRBool aCheckForBraces,
03115                                 PRBool aMustCallValueAppended,
03116                                 PRBool* aChanged)
03117 {
03118   mTempData.AssertInitialState();
03119 
03120   // Get property name
03121   nsCSSToken* tk = &mToken;
03122   nsAutoString propertyName;
03123   for (;;) {
03124     if (!GetToken(aErrorCode, PR_TRUE)) {
03125       if (aCheckForBraces) {
03126         REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
03127       }
03128       return PR_FALSE;
03129     }
03130     if (eCSSToken_Ident == tk->mType) {
03131       propertyName = tk->mIdent;
03132       // grab the ident before the ExpectSymbol trashes the token
03133       if (!ExpectSymbol(aErrorCode, ':', PR_TRUE)) {
03134         REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
03135         REPORT_UNEXPECTED(PEDeclDropped);
03136         OUTPUT_ERROR();
03137         return PR_FALSE;
03138       }
03139       break;
03140     }
03141     if (tk->IsSymbol(';')) {
03142       // dangling semicolons are skipped
03143       continue;
03144     }
03145 
03146     if (!tk->IsSymbol('}')) {
03147       REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
03148       REPORT_UNEXPECTED(PEDeclSkipped);
03149       OUTPUT_ERROR();
03150     }
03151     // Not a declaration...
03152     UngetToken();
03153     return PR_FALSE;
03154   }
03155 
03156   // Map property name to it's ID and then parse the property
03157   nsCSSProperty propID = nsCSSProps::LookupProperty(propertyName);
03158   if (eCSSProperty_UNKNOWN == propID) { // unknown property
03159     const PRUnichar *params[] = {
03160       propertyName.get()
03161     };
03162     REPORT_UNEXPECTED_P(PEUnknownProperty, params);
03163     REPORT_UNEXPECTED(PEDeclDropped);
03164     OUTPUT_ERROR();
03165     return PR_FALSE;
03166   }
03167   if (! ParseProperty(aErrorCode, propID)) {
03168     // XXX Much better to put stuff in the value parsers instead...
03169     const PRUnichar *params[] = {
03170       propertyName.get()
03171     };
03172     REPORT_UNEXPECTED_P(PEPropertyParsingError, params);
03173     REPORT_UNEXPECTED(PEDeclDropped);
03174     OUTPUT_ERROR();
03175     ClearTempData(propID);
03176     return PR_FALSE;
03177   }
03178   CLEAR_ERROR();
03179 
03180   // See if the declaration is followed by a "!important" declaration
03181   PRBool isImportant = PR_FALSE;
03182   if (!GetToken(aErrorCode, PR_TRUE)) {
03183     if (aCheckForBraces) {
03184       // Premature eof is not ok when proper termination is mandated
03185       REPORT_UNEXPECTED_EOF(PEEndOfDeclEOF);
03186       ClearTempData(propID);
03187       return PR_FALSE;
03188     }
03189     TransferTempData(aDeclaration, propID, isImportant,
03190                      aMustCallValueAppended, aChanged);
03191     return PR_TRUE;
03192   }
03193   else {
03194     if (eCSSToken_Symbol == tk->mType) {
03195       if ('!' == tk->mSymbol) {
03196         // Look for important ident
03197         if (!GetToken(aErrorCode, PR_TRUE)) {
03198           // Premature eof is not ok
03199           REPORT_UNEXPECTED_EOF(PEImportantEOF);
03200           ClearTempData(propID);
03201           return PR_FALSE;
03202         }
03203         if ((eCSSToken_Ident != tk->mType) ||
03204             !tk->mIdent.LowerCaseEqualsLiteral("important")) {
03205           REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
03206           OUTPUT_ERROR();
03207           UngetToken();
03208           ClearTempData(propID);
03209           return PR_FALSE;
03210         }
03211         isImportant = PR_TRUE;
03212       }
03213       else {
03214         // Not a !important declaration
03215         UngetToken();
03216       }
03217     }
03218     else {
03219       // Not a !important declaration
03220       UngetToken();
03221     }
03222   }
03223 
03224   // Make sure valid property declaration is terminated with either a
03225   // semicolon or a right-curly-brace (when aCheckForBraces is true).
03226   // When aCheckForBraces is false, proper termination is either
03227   // semicolon or EOF.
03228   if (!GetToken(aErrorCode, PR_TRUE)) {
03229     if (aCheckForBraces) {
03230       // Premature eof is not ok
03231       REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
03232       ClearTempData(propID);
03233       return PR_FALSE;
03234     }
03235     TransferTempData(aDeclaration, propID, isImportant,
03236                      aMustCallValueAppended, aChanged);
03237     return PR_TRUE;
03238   } 
03239   if (eCSSToken_Symbol == tk->mType) {
03240     if (';' == tk->mSymbol) {
03241       TransferTempData(aDeclaration, propID, isImportant,
03242                        aMustCallValueAppended, aChanged);
03243       return PR_TRUE;
03244     }
03245     if (!aCheckForBraces) {
03246       // If we didn't hit eof and we didn't see a semicolon then the
03247       // declaration is not properly terminated.
03248       REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
03249       REPORT_UNEXPECTED(PEDeclDropped);
03250       OUTPUT_ERROR();
03251       ClearTempData(propID);
03252       return PR_FALSE;
03253     }
03254     if ('}' == tk->mSymbol) {
03255       UngetToken();
03256       TransferTempData(aDeclaration, propID, isImportant,
03257                        aMustCallValueAppended, aChanged);
03258       return PR_TRUE;
03259     }
03260   }
03261   if (aCheckForBraces)
03262     REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd);
03263   else
03264     REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
03265   REPORT_UNEXPECTED(PEDeclDropped);
03266   OUTPUT_ERROR();
03267   ClearTempData(propID);
03268   return PR_FALSE;
03269 }
03270 
03271 void
03272 CSSParserImpl::ClearTempData(nsCSSProperty aPropID)
03273 {
03274   if (nsCSSProps::IsShorthand(aPropID)) {
03275     CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
03276       mTempData.ClearProperty(*p);
03277     }
03278   } else {
03279     mTempData.ClearProperty(aPropID);
03280   }
03281   mTempData.AssertInitialState();
03282 }
03283 
03284 void
03285 CSSParserImpl::TransferTempData(nsCSSDeclaration* aDeclaration,
03286                                 nsCSSProperty aPropID, PRBool aIsImportant,
03287                                 PRBool aMustCallValueAppended,
03288                                 PRBool* aChanged)
03289 {
03290   if (nsCSSProps::IsShorthand(aPropID)) {
03291     CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
03292       DoTransferTempData(aDeclaration, *p, aIsImportant,
03293                          aMustCallValueAppended, aChanged);
03294     }
03295   } else {
03296     DoTransferTempData(aDeclaration, aPropID, aIsImportant,
03297                        aMustCallValueAppended, aChanged);
03298   }
03299   mTempData.AssertInitialState();
03300 }
03301 
03302 // Perhaps the transferring code should be in nsCSSExpandedDataBlock, in
03303 // case some other caller wants to use it in the future (although I
03304 // can't think of why).
03305 void
03306 CSSParserImpl::DoTransferTempData(nsCSSDeclaration* aDeclaration,
03307                                   nsCSSProperty aPropID, PRBool aIsImportant,
03308                                   PRBool aMustCallValueAppended,
03309                                   PRBool* aChanged)
03310 {
03311   NS_ASSERTION(mTempData.HasPropertyBit(aPropID), "oops");
03312   if (aIsImportant) {
03313     if (!mData.HasImportantBit(aPropID))
03314       *aChanged = PR_TRUE;
03315     mData.SetImportantBit(aPropID);
03316   } else {
03317     if (mData.HasImportantBit(aPropID)) {
03318       mTempData.ClearProperty(aPropID);
03319       return;
03320     }
03321   }
03322 
03323   if (aMustCallValueAppended || !mData.HasPropertyBit(aPropID)) {
03324     aDeclaration->ValueAppended(aPropID);
03325   }
03326 
03327   mData.SetPropertyBit(aPropID);
03328   mTempData.ClearPropertyBit(aPropID);
03329 
03330   /*
03331    * Save needless copying and allocation by calling the destructor in
03332    * the destination, copying memory directly, and then using placement
03333    * new.
03334    */
03335   void *v_source = mTempData.PropertyAt(aPropID);
03336   void *v_dest = mData.PropertyAt(aPropID);
03337   switch (nsCSSProps::kTypeTable[aPropID]) {
03338     case eCSSType_Value: {
03339       nsCSSValue *source = NS_STATIC_CAST(nsCSSValue*, v_source);
03340       nsCSSValue *dest = NS_STATIC_CAST(nsCSSValue*, v_dest);
03341       if (*source != *dest)
03342         *aChanged = PR_TRUE;
03343       dest->~nsCSSValue();
03344       memcpy(dest, source, sizeof(nsCSSValue));
03345       new (source) nsCSSValue();
03346       if (dest->GetUnit() == eCSSUnit_Null) {
03347         // Some of our property parsers actually want to _clear_ properties in
03348         // mData (eg the "font" shorthand parser does for system fonts).  We've
03349         // cleared the data; now clear the bit too.
03350         mData.ClearPropertyBit(aPropID);
03351       }
03352     } break;
03353 
03354     case eCSSType_Rect: {
03355       nsCSSRect *source = NS_STATIC_CAST(nsCSSRect*, v_source);
03356       nsCSSRect *dest = NS_STATIC_CAST(nsCSSRect*, v_dest);
03357       if (*source != *dest)
03358         *aChanged = PR_TRUE;
03359       dest->~nsCSSRect();
03360       memcpy(dest, source, sizeof(nsCSSRect));
03361       new (source) nsCSSRect();
03362     } break;
03363 
03364     case eCSSType_ValuePair: {
03365       nsCSSValuePair *source = NS_STATIC_CAST(nsCSSValuePair*, v_source);
03366       nsCSSValuePair *dest = NS_STATIC_CAST(nsCSSValuePair*, v_dest);
03367       if (*source != *dest)
03368         *aChanged = PR_TRUE;
03369       dest->~nsCSSValuePair();
03370       memcpy(dest, source, sizeof(nsCSSValuePair));
03371       new (source) nsCSSValuePair();
03372     } break;
03373 
03374     case eCSSType_ValueList: {
03375       nsCSSValueList **source = NS_STATIC_CAST(nsCSSValueList**, v_source);
03376       nsCSSValueList **dest = NS_STATIC_CAST(nsCSSValueList**, v_dest);
03377       if (!nsCSSValueList::Equal(*source, *dest))
03378         *aChanged = PR_TRUE;
03379       delete *dest;
03380       *dest = *source;
03381       *source = nsnull;
03382     } break;
03383 
03384     case eCSSType_CounterData: {
03385       nsCSSCounterData **source = NS_STATIC_CAST(nsCSSCounterData**, v_source);
03386       nsCSSCounterData **dest = NS_STATIC_CAST(nsCSSCounterData**, v_dest);
03387       if (!nsCSSCounterData::Equal(*source, *dest))
03388         *aChanged = PR_TRUE;
03389       delete *dest;
03390       *dest = *source;
03391       *source = nsnull;
03392     } break;
03393 
03394     case eCSSType_Quotes: {
03395       nsCSSQuotes **source = NS_STATIC_CAST(nsCSSQuotes**, v_source);
03396       nsCSSQuotes **dest = NS_STATIC_CAST(nsCSSQuotes**, v_dest);
03397       if (!nsCSSQuotes::Equal(*source, *dest))
03398         *aChanged = PR_TRUE;
03399       delete *dest;
03400       *dest = *source;
03401       *source = nsnull;
03402     } break;
03403 
03404     case eCSSType_Shadow: {
03405       nsCSSShadow **source = NS_STATIC_CAST(nsCSSShadow**, v_source);
03406       nsCSSShadow **dest = NS_STATIC_CAST(nsCSSShadow**, v_dest);
03407       if (!nsCSSShadow::Equal(*source, *dest))
03408         *aChanged = PR_TRUE;
03409       delete *dest;
03410       *dest = *source;
03411       *source = nsnull;
03412     } break;
03413   }
03414 }
03415 
03416 // Flags for ParseVariant method
03417 #define VARIANT_KEYWORD         0x000001  // K
03418 #define VARIANT_LENGTH          0x000002  // L
03419 #define VARIANT_PERCENT         0x000004  // P
03420 #define VARIANT_COLOR           0x000008  // C
03421 #define VARIANT_URL             0x000010  // U
03422 #define VARIANT_NUMBER          0x000020  // N
03423 #define VARIANT_INTEGER         0x000040  // I
03424 #define VARIANT_ANGLE           0x000080  // G
03425 #define VARIANT_FREQUENCY       0x000100  // F
03426 #define VARIANT_TIME            0x000200  // T
03427 #define VARIANT_STRING          0x000400  // S
03428 #define VARIANT_COUNTER         0x000800  // 
03429 #define VARIANT_ATTR            0x001000  //
03430 #define VARIANT_IDENTIFIER      0x002000  // D
03431 #define VARIANT_AUTO            0x010000  // A
03432 #define VARIANT_INHERIT         0x020000  // H
03433 #define VARIANT_NONE            0x040000  // O
03434 #define VARIANT_NORMAL          0x080000  // M
03435 
03436 // Common combinations of variants
03437 #define VARIANT_AL   (VARIANT_AUTO | VARIANT_LENGTH)
03438 #define VARIANT_LP   (VARIANT_LENGTH | VARIANT_PERCENT)
03439 #define VARIANT_AH   (VARIANT_AUTO | VARIANT_INHERIT)
03440 #define VARIANT_AHLP (VARIANT_AH | VARIANT_LP)
03441 #define VARIANT_AHI  (VARIANT_AH | VARIANT_INTEGER)
03442 #define VARIANT_AHK  (VARIANT_AH | VARIANT_KEYWORD)
03443 #define VARIANT_AUK  (VARIANT_AUTO | VARIANT_URL | VARIANT_KEYWORD)
03444 #define VARIANT_AHUK (VARIANT_AH | VARIANT_URL | VARIANT_KEYWORD)
03445 #define VARIANT_AHL  (VARIANT_AH | VARIANT_LENGTH)
03446 #define VARIANT_AHKL (VARIANT_AHK | VARIANT_LENGTH)
03447 #define VARIANT_HK   (VARIANT_INHERIT | VARIANT_KEYWORD)
03448 #define VARIANT_HKF  (VARIANT_HK | VARIANT_FREQUENCY)
03449 #define VARIANT_HKL  (VARIANT_HK | VARIANT_LENGTH)
03450 #define VARIANT_HKLP (VARIANT_HK | VARIANT_LP)
03451 #define VARIANT_HL   (VARIANT_INHERIT | VARIANT_LENGTH)
03452 #define VARIANT_HI   (VARIANT_INHERIT | VARIANT_INTEGER)
03453 #define VARIANT_HLP  (VARIANT_HL | VARIANT_PERCENT)
03454 #define VARIANT_HLPN (VARIANT_HLP | VARIANT_NUMBER)
03455 #define VARIANT_HLPO (VARIANT_HLP | VARIANT_NONE)
03456 #define VARIANT_HTP  (VARIANT_INHERIT | VARIANT_TIME | VARIANT_PERCENT)
03457 #define VARIANT_HMK  (VARIANT_HK | VARIANT_NORMAL)
03458 #define VARIANT_HMKI (VARIANT_HMK | VARIANT_INTEGER)
03459 #define VARIANT_HC   (VARIANT_INHERIT | VARIANT_COLOR)
03460 #define VARIANT_HCK  (VARIANT_HK | VARIANT_COLOR)
03461 #define VARIANT_HUO  (VARIANT_INHERIT | VARIANT_URL | VARIANT_NONE)
03462 #define VARIANT_AHUO (VARIANT_AUTO | VARIANT_HUO)
03463 #define VARIANT_HPN  (VARIANT_INHERIT | VARIANT_PERCENT | VARIANT_NUMBER)
03464 #define VARIANT_HOK  (VARIANT_HK | VARIANT_NONE)
03465 #define VARIANT_HN   (VARIANT_INHERIT | VARIANT_NUMBER)
03466 #define VARIANT_HON  (VARIANT_HN | VARIANT_NONE)
03467 #define VARIANT_HOS  (VARIANT_INHERIT | VARIANT_NONE | VARIANT_STRING)
03468 
03469 static const nsCSSProperty kBorderTopIDs[] = {
03470   eCSSProperty_border_top_width,
03471   eCSSProperty_border_top_style,
03472   eCSSProperty_border_top_color
03473 };
03474 static const nsCSSProperty kBorderRightIDs[] = {
03475   eCSSProperty_border_right_width,
03476   eCSSProperty_border_right_style,
03477   eCSSProperty_border_right_color
03478 };
03479 static const nsCSSProperty kBorderBottomIDs[] = {
03480   eCSSProperty_border_bottom_width,
03481   eCSSProperty_border_bottom_style,
03482   eCSSProperty_border_bottom_color
03483 };
03484 static const nsCSSProperty kBorderLeftIDs[] = {
03485   eCSSProperty_border_left_width,
03486   eCSSProperty_border_left_style,
03487   eCSSProperty_border_left_color
03488 };
03489 
03490 PRBool CSSParserImpl::ParseEnum(nsresult& aErrorCode, nsCSSValue& aValue,
03491                                 const PRInt32 aKeywordTable[])
03492 {
03493   nsString* ident = NextIdent(aErrorCode);
03494   if (nsnull == ident) {
03495     return PR_FALSE;
03496   }
03497   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
03498   if (eCSSKeyword_UNKNOWN < keyword) {
03499     PRInt32 value;
03500     if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
03501       aValue.SetIntValue(value, eCSSUnit_Enumerated);
03502       return PR_TRUE;
03503     }
03504   }
03505 
03506   // Put the unknown identifier back and return
03507   UngetToken();
03508   return PR_FALSE;
03509 }
03510 
03511 PRBool CSSParserImpl::TranslateDimension(nsresult& aErrorCode,
03512                                          nsCSSValue& aValue,
03513                                          PRInt32 aVariantMask,
03514                                          float aNumber,
03515                                          const nsString& aUnit)
03516 {
03517   nsCSSUnit units;
03518   PRInt32   type = 0;
03519   if (!aUnit.IsEmpty()) {
03520     nsCSSKeyword id = nsCSSKeywords::LookupKeyword(aUnit);
03521     switch (id) {
03522       case eCSSKeyword_em:    units = eCSSUnit_EM;          type = VARIANT_LENGTH;  break;
03523       case eCSSKeyword_ex:    units = eCSSUnit_XHeight;     type = VARIANT_LENGTH;  break;
03524       case eCSSKeyword_ch:    units = eCSSUnit_Char;        type = VARIANT_LENGTH;  break;
03525       case eCSSKeyword_px:    units = eCSSUnit_Pixel;       type = VARIANT_LENGTH;  break;
03526       case eCSSKeyword_in:    units = eCSSUnit_Inch;        type = VARIANT_LENGTH;  break;
03527       case eCSSKeyword_cm:    units = eCSSUnit_Centimeter;  type = VARIANT_LENGTH;  break;
03528       case eCSSKeyword_mm:    units = eCSSUnit_Millimeter;  type = VARIANT_LENGTH;  break;
03529       case eCSSKeyword_pt:    units = eCSSUnit_Point;       type = VARIANT_LENGTH;  break;
03530       case eCSSKeyword_pc:    units = eCSSUnit_Pica;        type = VARIANT_LENGTH;  break;
03531 
03532       case eCSSKeyword_deg:   units = eCSSUnit_Degree;      type = VARIANT_ANGLE;   break;
03533       case eCSSKeyword_grad:  units = eCSSUnit_Grad;        type = VARIANT_ANGLE;   break;
03534       case eCSSKeyword_rad:   units = eCSSUnit_Radian;      type = VARIANT_ANGLE;   break;
03535 
03536       case eCSSKeyword_hz:    units = eCSSUnit_Hertz;       type = VARIANT_FREQUENCY; break;
03537       case eCSSKeyword_khz:   units = eCSSUnit_Kilohertz;   type = VARIANT_FREQUENCY; break;
03538 
03539       case eCSSKeyword_s:     units = eCSSUnit_Seconds;       type = VARIANT_TIME;  break;
03540       case eCSSKeyword_ms:    units = eCSSUnit_Milliseconds;  type = VARIANT_TIME;  break;
03541       default:
03542         // unknown unit
03543         return PR_FALSE;
03544     }
03545   } else {
03546     // Must be a zero number...
03547     NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
03548     if ((VARIANT_LENGTH & aVariantMask) != 0) {
03549       units = eCSSUnit_Point;
03550       type = VARIANT_LENGTH;
03551     }
03552     else if ((VARIANT_ANGLE & aVariantMask) != 0) {
03553       units = eCSSUnit_Degree;
03554       type = VARIANT_ANGLE;
03555     }
03556     else if ((VARIANT_FREQUENCY & aVariantMask) != 0) {
03557       units = eCSSUnit_Hertz;
03558       type = VARIANT_FREQUENCY;
03559     }
03560     else if ((VARIANT_TIME & aVariantMask) != 0) {
03561       units = eCSSUnit_Seconds;
03562       type = VARIANT_TIME;
03563     }
03564     else {
03565       NS_ERROR("Variant mask does not include dimension; why were we called?");
03566       return PR_FALSE;
03567     }
03568   }
03569   if ((type & aVariantMask) != 0) {
03570     aValue.SetFloatValue(aNumber, units);
03571     return PR_TRUE;
03572   }
03573   return PR_FALSE;
03574 }
03575 
03576 PRBool CSSParserImpl::ParsePositiveVariant(nsresult& aErrorCode, 
03577                                            nsCSSValue& aValue, 
03578                                            PRInt32 aVariantMask, 
03579                                            const PRInt32 aKeywordTable[]) 
03580 { 
03581   if (ParseVariant(aErrorCode, aValue, aVariantMask, aKeywordTable)) { 
03582     if (eCSSUnit_Number == aValue.GetUnit() || 
03583         aValue.IsLengthUnit()){ 
03584       if (aValue.GetFloatValue() < 0) { 
03585         UngetToken();
03586         return PR_FALSE; 
03587       } 
03588     } 
03589     else if(aValue.GetUnit() == eCSSUnit_Percent) { 
03590       if (aValue.GetPercentValue() < 0) { 
03591         UngetToken();
03592         return PR_FALSE; 
03593       } 
03594     } 
03595     return PR_TRUE; 
03596   } 
03597   return PR_FALSE; 
03598 } 
03599 
03600 // Assigns to aValue iff it returns PR_TRUE.
03601 PRBool CSSParserImpl::ParseVariant(nsresult& aErrorCode, nsCSSValue& aValue,
03602                                    PRInt32 aVariantMask,
03603                                    const PRInt32 aKeywordTable[])
03604 {
03605   if (!GetToken(aErrorCode, PR_TRUE)) {
03606     return PR_FALSE;
03607   }
03608   nsCSSToken* tk = &mToken;
03609   if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE)) != 0) &&
03610       (eCSSToken_Ident == tk->mType)) {
03611     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
03612     if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
03613       if ((aVariantMask & VARIANT_AUTO) != 0) {
03614         if (eCSSKeyword_auto == keyword) {
03615           aValue.SetAutoValue();
03616           return PR_TRUE;
03617         }
03618       }
03619       if ((aVariantMask & VARIANT_INHERIT) != 0) {
03620         if (eCSSKeyword_inherit == keyword) {
03621           aValue.SetInheritValue();
03622           return PR_TRUE;
03623         }
03624         else if (eCSSKeyword__moz_initial == keyword) { // anything that can inherit can also take an initial val.
03625           aValue.SetInitialValue();
03626           return PR_TRUE;
03627         }
03628       }
03629       if ((aVariantMask & VARIANT_NONE) != 0) {
03630         if (eCSSKeyword_none == keyword) {
03631           aValue.SetNoneValue();
03632           return PR_TRUE;
03633         }
03634       }
03635       if ((aVariantMask & VARIANT_NORMAL) != 0) {
03636         if (eCSSKeyword_normal == keyword) {
03637           aValue.SetNormalValue();
03638           return PR_TRUE;
03639         }
03640       }
03641       if ((aVariantMask & VARIANT_KEYWORD) != 0) {
03642         PRInt32 value;
03643         if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
03644           aValue.SetIntValue(value, eCSSUnit_Enumerated);
03645           return PR_TRUE;
03646         }
03647       }
03648     }
03649   }
03650   if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE | VARIANT_FREQUENCY | VARIANT_TIME)) != 0) && 
03651       tk->IsDimension()) {
03652     if (TranslateDimension(aErrorCode, aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
03653       return PR_TRUE;
03654     }
03655     // Put the token back; we didn't parse it, so we shouldn't consume it
03656     UngetToken();
03657     return PR_FALSE;
03658   }
03659   if (((aVariantMask & VARIANT_PERCENT) != 0) &&
03660       (eCSSToken_Percentage == tk->mType)) {
03661     aValue.SetPercentValue(tk->mNumber);
03662     return PR_TRUE;
03663   }
03664   if (((aVariantMask & VARIANT_NUMBER) != 0) &&
03665       (eCSSToken_Number == tk->mType)) {
03666     aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
03667     return PR_TRUE;
03668   }
03669   if (((aVariantMask & VARIANT_INTEGER) != 0) &&
03670       (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
03671     aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
03672     return PR_TRUE;
03673   }
03674   if (mNavQuirkMode && !IsParsingCompoundProperty()) { // NONSTANDARD: Nav interprets unitless numbers as px
03675     if (((aVariantMask & VARIANT_LENGTH) != 0) &&
03676         (eCSSToken_Number == tk->mType)) {
03677       aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
03678       return PR_TRUE;
03679     }
03680   }
03681 
03682 #ifdef  MOZ_SVG
03683   if (mSVGMode && !IsParsingCompoundProperty()) {
03684     // STANDARD: SVG Spec states that lengths and coordinates can be unitless
03685     // in which case they default to user-units (1 px = 1 user unit)
03686     if (((aVariantMask & VARIANT_LENGTH) != 0) &&
03687         (eCSSToken_Number == tk->mType)) {
03688       aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
03689       return PR_TRUE;
03690     }
03691   }
03692 #endif
03693 
03694   if (((aVariantMask & VARIANT_URL) != 0) &&
03695       (eCSSToken_Function == tk->mType) && 
03696       tk->mIdent.LowerCaseEqualsLiteral("url")) {
03697     if (ParseURL(aErrorCode, aValue)) {
03698       return PR_TRUE;
03699     }
03700     return PR_FALSE;
03701   }
03702   if ((aVariantMask & VARIANT_COLOR) != 0) {
03703     if ((mNavQuirkMode && !IsParsingCompoundProperty()) || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
03704               (eCSSToken_ID == tk->mType) || 
03705               (eCSSToken_Ref == tk->mType) || 
03706         (eCSSToken_Ident == tk->mType) ||
03707         ((eCSSToken_Function == tk->mType) && 
03708          (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
03709           tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
03710           tk->mIdent.LowerCaseEqualsLiteral("-moz-rgba") ||
03711           tk->mIdent.LowerCaseEqualsLiteral("-moz-hsla") ||
03712           (mHandleAlphaColors && (tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
03713                                   tk->mIdent.LowerCaseEqualsLiteral("hsla"))))))
03714     {
03715       // Put token back so that parse color can get it
03716       UngetToken();
03717       if (ParseColor(aErrorCode, aValue)) {
03718         return PR_TRUE;
03719       }
03720       return PR_FALSE;
03721     }
03722   }
03723   if (((aVariantMask & VARIANT_STRING) != 0) && 
03724       (eCSSToken_String == tk->mType)) {
03725     nsAutoString  buffer;
03726     buffer.Append(tk->mSymbol);
03727     buffer.Append(tk->mIdent);
03728     buffer.Append(tk->mSymbol);
03729     aValue.SetStringValue(buffer, eCSSUnit_String);
03730     return PR_TRUE;
03731   }
03732   if (((aVariantMask & VARIANT_IDENTIFIER) != 0) && 
03733       (eCSSToken_Ident == tk->mType)) {
03734     aValue.SetStringValue(tk->mIdent, eCSSUnit_String);
03735     return PR_TRUE;
03736   }
03737   if (((aVariantMask & VARIANT_COUNTER) != 0) &&
03738       (eCSSToken_Function == tk->mType) &&
03739       (tk->mIdent.LowerCaseEqualsLiteral("counter") || 
03740        tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
03741     return ParseCounter(aErrorCode, aValue);
03742   }
03743   if (((aVariantMask & VARIANT_ATTR) != 0) &&
03744       (eCSSToken_Function == tk->mType) &&
03745       tk->mIdent.LowerCaseEqualsLiteral("attr")) {
03746     return ParseAttr(aErrorCode, aValue);
03747   }
03748 
03749   UngetToken();
03750   return PR_FALSE;
03751 }
03752 
03753 
03754 PRBool CSSParserImpl::ParseCounter(nsresult& aErrorCode, nsCSSValue& aValue)
03755 {
03756   nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ? 
03757                     eCSSUnit_Counter : eCSSUnit_Counters);
03758 
03759   if (!ExpectSymbol(aErrorCode, '(', PR_FALSE))
03760     return PR_FALSE;
03761 
03762   if (!GetNonCloseParenToken(aErrorCode, PR_TRUE) ||
03763       eCSSToken_Ident != mToken.mType) {
03764     SkipUntil(aErrorCode, ')');
03765     return PR_FALSE;
03766   }
03767 
03768   nsRefPtr<nsCSSValue::Array> val =
03769     nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
03770   if (!val) {
03771     aErrorCode = NS_ERROR_OUT_OF_MEMORY;
03772     return PR_FALSE;
03773   }
03774 
03775   val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_String);
03776 
03777   if (eCSSUnit_Counters == unit) {
03778     // get mandatory separator string
03779     if (!ExpectSymbol(aErrorCode, ',', PR_TRUE) ||
03780         !(GetNonCloseParenToken(aErrorCode, PR_TRUE) &&
03781           eCSSToken_String == mToken.mType)) {
03782       SkipUntil(aErrorCode, ')');
03783       return PR_FALSE;
03784     }
03785     val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
03786   }
03787 
03788   // get optional type
03789   PRInt32 type = NS_STYLE_LIST_STYLE_DECIMAL;
03790   if (ExpectSymbol(aErrorCode, ',', PR_TRUE)) {
03791     nsCSSKeyword keyword;
03792     PRBool success = GetNonCloseParenToken(aErrorCode, PR_TRUE) &&
03793                      eCSSToken_Ident == mToken.mType &&
03794                      (keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent)) !=
03795                       eCSSKeyword_UNKNOWN;
03796     if (success) {
03797       if (keyword == eCSSKeyword_none) {
03798         type = NS_STYLE_LIST_STYLE_NONE;
03799       } else {
03800         success = nsCSSProps::FindKeyword(keyword,
03801                                           nsCSSProps::kListStyleKTable, type);
03802       }
03803     }
03804     if (!success) {
03805       SkipUntil(aErrorCode, ')');
03806       return PR_FALSE;
03807     }
03808   }
03809   PRInt32 typeItem = eCSSUnit_Counters == unit ? 2 : 1;
03810   val->Item(typeItem).SetIntValue(type, eCSSUnit_Enumerated);
03811 
03812   if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
03813     SkipUntil(aErrorCode, ')');
03814     return PR_FALSE;
03815   }
03816 
03817   aValue.SetArrayValue(val, unit);
03818   return PR_TRUE;
03819 }
03820 
03821 PRBool CSSParserImpl::ParseAttr(nsresult& aErrorCode, nsCSSValue& aValue)
03822 {
03823   if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
03824     if (GetToken(aErrorCode, PR_TRUE)) {
03825       nsAutoString attr;
03826       if (eCSSToken_Ident == mToken.mType) {  // attr name or namespace
03827         nsAutoString  holdIdent(mToken.mIdent);
03828         if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) {  // namespace
03829           PRInt32 nameSpaceID = kNameSpaceID_Unknown;
03830           if (mNameSpaceMap) {
03831             ToLowerCase(holdIdent); // always case insensitive, since stays within CSS
03832             nsCOMPtr<nsIAtom> prefix = do_GetAtom(holdIdent);
03833             nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
03834           } // else, no declared namespaces
03835           if (kNameSpaceID_Unknown == nameSpaceID) {  // unknown prefix, dump it
03836             const PRUnichar *params[] = {
03837               holdIdent.get()
03838             };
03839             REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, params);
03840             return PR_FALSE;
03841           }
03842           attr.AppendInt(nameSpaceID, 10);
03843           attr.Append(PRUnichar('|'));
03844           if (! GetToken(aErrorCode, PR_FALSE)) {
03845             REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
03846             return PR_FALSE;
03847           }
03848           if (eCSSToken_Ident == mToken.mType) {
03849             if (mCaseSensitive) {
03850               attr.Append(mToken.mIdent);
03851             } else {
03852               nsAutoString buffer;
03853               ToLowerCase(mToken.mIdent, buffer);
03854               attr.Append(buffer);
03855             }
03856           }
03857           else {
03858             REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
03859             UngetToken();
03860             return PR_FALSE;
03861           }
03862         }
03863         else {  // no namespace
03864           if (mCaseSensitive) {
03865             attr = holdIdent;
03866           }
03867           else {
03868             ToLowerCase(holdIdent, attr);
03869           }
03870         }
03871       }
03872       else if (mToken.IsSymbol('*')) {  // namespace wildcard
03873         // Wildcard namespace makes no sense here and is not allowed
03874         REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
03875         UngetToken();
03876         return PR_FALSE;
03877       }
03878       else if (mToken.IsSymbol('|')) {  // explicit NO namespace
03879         if (! GetToken(aErrorCode, PR_FALSE)) {
03880           REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
03881           return PR_FALSE;
03882         }
03883         if (eCSSToken_Ident == mToken.mType) {
03884           if (mCaseSensitive) {
03885             attr.Append(mToken.mIdent);
03886           } else {
03887             nsAutoString buffer;
03888             ToLowerCase(mToken.mIdent, buffer);
03889             attr.Append(buffer);
03890           }
03891         }
03892         else {
03893           REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
03894           UngetToken();
03895           return PR_FALSE;
03896         }
03897       }
03898       else {
03899         REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
03900         UngetToken();
03901         return PR_FALSE;
03902       }
03903       if (ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
03904         aValue.SetStringValue(attr, eCSSUnit_Attr);
03905         return PR_TRUE;
03906       }
03907     }
03908   }
03909   return PR_FALSE;
03910 }
03911 
03912 PRBool CSSParserImpl::ParseURL(nsresult& aErrorCode, nsCSSValue& aValue)
03913 {
03914   if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
03915     if (! GetURLToken(aErrorCode, PR_TRUE)) {
03916       return PR_FALSE;
03917     }
03918     nsCSSToken* tk = &mToken;
03919     if ((eCSSToken_String == tk->mType) || (eCSSToken_URL == tk->mType)) {
03920       // Translate url into an absolute url if the url is relative to
03921       // the style sheet.
03922       nsCOMPtr<nsIURI> uri;
03923       NS_NewURI(getter_AddRefs(uri), tk->mIdent, nsnull, mBaseURL);
03924       if (ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
03925         // Set a null value on failure.  Most failure cases should be
03926         // NS_ERROR_MALFORMED_URI.
03927         nsCSSValue::URL *url =
03928           new nsCSSValue::URL(uri, tk->mIdent.get(), mSheetURL);
03929         if (!url || !url->mString) {
03930           aErrorCode = NS_ERROR_OUT_OF_MEMORY;
03931           delete url;
03932           return PR_FALSE;
03933         }
03934         aValue.SetURLValue(url);
03935         return PR_TRUE;
03936       }
03937     }
03938   }
03939   return PR_FALSE;
03940 }
03941 
03942 PRInt32 CSSParserImpl::ParseChoice(nsresult& aErrorCode, nsCSSValue aValues[],
03943                                    const nsCSSProperty aPropIDs[], PRInt32 aNumIDs)
03944 {
03945   PRInt32 found = 0;
03946   SetParsingCompoundProperty(PR_TRUE);
03947 
03948   PRInt32 loop;
03949   for (loop = 0; loop < aNumIDs; loop++) {
03950     // Try each property parser in order
03951     PRInt32 hadFound = found;
03952     PRInt32 index;
03953     for (index = 0; index < aNumIDs; index++) {
03954       PRInt32 bit = 1 << index;
03955       if ((found & bit) == 0) {
03956         if (ParseSingleValueProperty(aErrorCode, aValues[index], aPropIDs[index])) {
03957           found |= bit;
03958         }
03959       }
03960     }
03961     if (found == hadFound) {  // found nothing new
03962       break;
03963     }
03964   }
03965   if (0 < found) {
03966     if (1 == found) { // only first property
03967       if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
03968         for (loop = 1; loop < aNumIDs; loop++) {
03969           aValues[loop].SetInheritValue();
03970         }
03971         found = ((1 << aNumIDs) - 1);
03972       }
03973       else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
03974         for (loop = 1; loop < aNumIDs; loop++) {
03975           aValues[loop].SetInitialValue();
03976         }
03977         found = ((1 << aNumIDs) - 1);
03978       }
03979     }
03980     else {  // more than one value, verify no inherits or initials
03981       for (loop = 0; loop < aNumIDs; loop++) {
03982         if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
03983           found = -1;
03984           break;
03985         }
03986         else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
03987           found = -1;
03988           break;
03989         }
03990       }
03991     }
03992   }
03993   SetParsingCompoundProperty(PR_FALSE);
03994   return found;
03995 }
03996 
03997 void
03998 CSSParserImpl::AppendValue(nsCSSProperty aPropID,
03999                            const nsCSSValue& aValue)
04000 {
04001   NS_ASSERTION(0 <= aPropID && aPropID < eCSSProperty_COUNT_no_shorthands,
04002                "property out of range");
04003   NS_ASSERTION(nsCSSProps::kTypeTable[aPropID] == eCSSType_Value,
04004                nsPrintfCString(64, "type error (property=\'%s\')",
04005                              nsCSSProps::GetStringValue(aPropID).get()).get());
04006   nsCSSValue& storage =
04007       *NS_STATIC_CAST(nsCSSValue*, mTempData.PropertyAt(aPropID));
04008   storage = aValue;
04009   mTempData.SetPropertyBit(aPropID);
04010 }
04011 
04017 PRBool CSSParserImpl::ParseBoxProperties(nsresult& aErrorCode,
04018                                          nsCSSRect& aResult,
04019                                          const nsCSSProperty aPropIDs[])
04020 {
04021   // Get up to four values for the property
04022   PRInt32 count = 0;
04023   PRInt32 index;
04024   nsCSSRect result;
04025   for (index = 0; index < 4; index++) {
04026     if (! ParseSingleValueProperty(aErrorCode,
04027                                    result.*(nsCSSRect::sides[index]),
04028                                    aPropIDs[index])) {
04029       break;
04030     }
04031     count++;
04032   }
04033   if ((count == 0) || (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE))) {
04034     return PR_FALSE;
04035   }
04036 
04037   if (1 < count) { // verify no more than single inherit or initial
04038     for (index = 0; index < 4; index++) {
04039       nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
04040       if (eCSSUnit_Inherit == unit || eCSSUnit_Initial == unit) {
04041         return PR_FALSE;
04042       }
04043     }
04044   }
04045 
04046   // Provide missing values by replicating some of the values found
04047   switch (count) {
04048     case 1: // Make right == top
04049       result.mRight = result.mTop;
04050     case 2: // Make bottom == top
04051       result.mBottom = result.mTop;
04052     case 3: // Make left == right
04053       result.mLeft = result.mRight;
04054   }
04055 
04056   for (index = 0; index < 4; index++) {
04057     mTempData.SetPropertyBit(aPropIDs[index]);
04058   }
04059   aResult = result;
04060   return PR_TRUE;
04061 }
04062 
04063 PRBool CSSParserImpl::ParseDirectionalBoxProperty(nsresult& aErrorCode,
04064                                                   nsCSSProperty aProperty,
04065                                                   PRInt32 aSourceType)
04066 {
04067   const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(aProperty);
04068   NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
04069                "not box property with physical vs. logical cascading");
04070   nsCSSValue value;
04071   if (!ParseSingleValueProperty(aErrorCode, value, subprops[0]) ||
04072       !ExpectEndProperty(aErrorCode, PR_TRUE))
04073     return PR_FALSE;
04074 
04075   AppendValue(subprops[0], value);
04076   nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
04077   AppendValue(subprops[1], typeVal);
04078   AppendValue(subprops[2], typeVal);
04079   aErrorCode = NS_OK;
04080   return PR_TRUE;
04081 }
04082 
04083 PRBool CSSParserImpl::ParseProperty(nsresult& aErrorCode,
04084                                     nsCSSProperty aPropID)
04085 {
04086   switch (aPropID) {  // handle shorthand or multiple properties
04087   case eCSSProperty_background:
04088     return ParseBackground(aErrorCode);
04089   case eCSSProperty_background_position:
04090     return ParseBackgroundPosition(aErrorCode);
04091   case eCSSProperty_border:
04092     return ParseBorderSide(aErrorCode, kBorderTopIDs, PR_TRUE);
04093   case eCSSProperty_border_color:
04094     return ParseBorderColor(aErrorCode);
04095   case eCSSProperty_border_spacing:
04096     return ParseBorderSpacing(aErrorCode);
04097   case eCSSProperty_border_style:
04098     return ParseBorderStyle(aErrorCode);
04099   case eCSSProperty_border_bottom:
04100     return ParseBorderSide(aErrorCode, kBorderBottomIDs, PR_FALSE);
04101   case eCSSProperty_border_left:
04102     return ParseBorderSide(aErrorCode, kBorderLeftIDs, PR_FALSE);
04103   case eCSSProperty_border_right:
04104     return ParseBorderSide(aErrorCode, kBorderRightIDs, PR_FALSE);
04105   case eCSSProperty_border_top:
04106     return ParseBorderSide(aErrorCode, kBorderTopIDs, PR_FALSE);
04107   case eCSSProperty_border_bottom_colors:
04108     return ParseBorderColors(aErrorCode,
04109                              &mTempData.mMargin.mBorderColors.mBottom,
04110                              aPropID);
04111   case eCSSProperty_border_left_colors:
04112     return ParseBorderColors(aErrorCode,
04113                              &mTempData.mMargin.mBorderColors.mLeft,
04114                              aPropID);
04115   case eCSSProperty_border_right_colors:
04116     return ParseBorderColors(aErrorCode,
04117                              &mTempData.mMargin.mBorderColors.mRight,
04118                              aPropID);
04119   case eCSSProperty_border_top_colors:
04120     return ParseBorderColors(aErrorCode,
04121                              &mTempData.mMargin.mBorderColors.mTop,
04122                              aPropID);
04123   case eCSSProperty_border_width:
04124     return ParseBorderWidth(aErrorCode);
04125   case eCSSProperty__moz_border_radius:
04126     return ParseBorderRadius(aErrorCode);
04127   case eCSSProperty__moz_outline_radius:
04128     return ParseOutlineRadius(aErrorCode);
04129   case eCSSProperty_clip:
04130     return ParseRect(mTempData.mDisplay.mClip, aErrorCode,
04131                      eCSSProperty_clip);
04132   case eCSSProperty_content:
04133     return ParseContent(aErrorCode);
04134   case eCSSProperty_counter_increment:
04135     return ParseCounterData(aErrorCode, &mTempData.mContent.mCounterIncrement,
04136                             aPropID);
04137   case eCSSProperty_counter_reset:
04138     return ParseCounterData(aErrorCode, &mTempData.mContent.mCounterReset,
04139                             aPropID);
04140   case eCSSProperty_cue:
04141     return ParseCue(aErrorCode);
04142   case eCSSProperty_cursor:
04143     return ParseCursor(aErrorCode);
04144   case eCSSProperty_font:
04145     return ParseFont(aErrorCode);
04146   case eCSSProperty_image_region:
04147     return ParseRect(mTempData.mList.mImageRegion, aErrorCode,
04148                      eCSSProperty_image_region);
04149   case eCSSProperty_list_style:
04150     return ParseListStyle(aErrorCode);
04151   case eCSSProperty_margin:
04152     return ParseMargin(aErrorCode);
04153   case eCSSProperty_margin_end:
04154     return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_margin_end,
04155                                        NS_BOXPROP_SOURCE_LOGICAL);
04156   case eCSSProperty_margin_left:
04157     return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_margin_left,
04158                                        NS_BOXPROP_SOURCE_PHYSICAL);
04159   case eCSSProperty_margin_right:
04160     return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_margin_right,
04161                                        NS_BOXPROP_SOURCE_PHYSICAL);
04162   case eCSSProperty_margin_start:
04163     return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_margin_start,
04164                                        NS_BOXPROP_SOURCE_LOGICAL);
04165   case eCSSProperty_outline:
04166     return ParseOutline(aErrorCode);
04167   case eCSSProperty_overflow:
04168     return ParseOverflow(aErrorCode);
04169   case eCSSProperty_padding:
04170     return ParsePadding(aErrorCode);
04171   case eCSSProperty_padding_end:
04172     return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_padding_end,
04173                                        NS_BOXPROP_SOURCE_LOGICAL);
04174   case eCSSProperty_padding_left:
04175     return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_padding_left,
04176                                        NS_BOXPROP_SOURCE_PHYSICAL);
04177   case eCSSProperty_padding_right:
04178     return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_padding_right,
04179                                        NS_BOXPROP_SOURCE_PHYSICAL);
04180   case eCSSProperty_padding_start:
04181     return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_padding_start,
04182                                        NS_BOXPROP_SOURCE_LOGICAL);
04183   case eCSSProperty_pause:
04184     return ParsePause(aErrorCode);
04185   case eCSSProperty_quotes:
04186     return ParseQuotes(aErrorCode);
04187   case eCSSProperty_size:
04188     return ParseSize(aErrorCode);
04189   case eCSSProperty_text_shadow:
04190     return ParseTextShadow(aErrorCode);
04191 
04192 #ifdef MOZ_SVG
04193   case eCSSProperty_stroke_dasharray:
04194     return ParseDasharray(aErrorCode);
04195   case eCSSProperty_marker:
04196     return ParseMarker(aErrorCode);
04197 #endif
04198 
04199   // Strip out properties we use internally. These properties are used
04200   // by compound property parsing routines (e.g. "background-position").
04201   case eCSSProperty_background_x_position:
04202   case eCSSProperty_background_y_position:
04203   case eCSSProperty_margin_end_value:
04204   case eCSSProperty_margin_left_value:
04205   case eCSSProperty_margin_right_value:
04206   case eCSSProperty_margin_start_value:
04207   case eCSSProperty_margin_left_ltr_source:
04208   case eCSSProperty_margin_left_rtl_source:
04209   case eCSSProperty_margin_right_ltr_source:
04210   case eCSSProperty_margin_right_rtl_source:
04211   case eCSSProperty_padding_end_value:
04212   case eCSSProperty_padding_left_value:
04213   case eCSSProperty_padding_right_value:
04214   case eCSSProperty_padding_start_value:
04215   case eCSSProperty_padding_left_ltr_source:
04216   case eCSSProperty_padding_left_rtl_source:
04217   case eCSSProperty_padding_right_ltr_source:
04218   case eCSSProperty_padding_right_rtl_source:
04219     // The user can't use these
04220     REPORT_UNEXPECTED(PEInaccessibleProperty);
04221     return PR_FALSE;
04222 
04223   default:  // must be single property
04224     {
04225       nsCSSValue value;
04226       if (ParseSingleValueProperty(aErrorCode, value, aPropID)) {
04227         if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
04228           AppendValue(aPropID, value);
04229           aErrorCode = NS_OK;
04230           return PR_TRUE;
04231         }
04232         // XXX Report errors?
04233       }
04234       // XXX Report errors?
04235     }
04236   }
04237   return PR_FALSE;
04238 }
04239 
04240 // Bits used in determining which background position info we have
04241 #define BG_CENTER  0x01
04242 #define BG_TOP     0x02
04243 #define BG_BOTTOM  0x04
04244 #define BG_LEFT    0x08
04245 #define BG_RIGHT   0x10
04246 #define BG_CTB    (BG_CENTER | BG_TOP | BG_BOTTOM)
04247 #define BG_CLR    (BG_CENTER | BG_LEFT | BG_RIGHT)
04248 
04249 // Note: Don't change this table unless you update
04250 // parseBackgroundPosition!
04251 
04252 static const PRInt32 kBackgroundXYPositionKTable[] = {
04253   eCSSKeyword_center, BG_CENTER,
04254   eCSSKeyword_top, BG_TOP,
04255   eCSSKeyword_bottom, BG_BOTTOM,
04256   eCSSKeyword_left, BG_LEFT,
04257   eCSSKeyword_right, BG_RIGHT,
04258   -1,
04259 };
04260 
04261 PRBool CSSParserImpl::ParseSingleValueProperty(nsresult& aErrorCode,
04262                                                nsCSSValue& aValue,
04263                                                nsCSSProperty aPropID)
04264 {
04265   switch (aPropID) {
04266   case eCSSProperty_UNKNOWN:
04267   case eCSSProperty_background:
04268   case eCSSProperty_background_position:
04269   case eCSSProperty_border:
04270   case eCSSProperty_border_color:
04271   case eCSSProperty_border_bottom_colors:
04272   case eCSSProperty_border_left_colors:
04273   case eCSSProperty_border_right_colors:
04274   case eCSSProperty_border_top_colors:
04275   case eCSSProperty_border_spacing:
04276   case eCSSProperty_border_style:
04277   case eCSSProperty_border_bottom:
04278   case eCSSProperty_border_left:
04279   case eCSSProperty_border_right:
04280   case eCSSProperty_border_top:
04281   case eCSSProperty_border_width:
04282   case eCSSProperty__moz_border_radius:
04283   case eCSSProperty_clip:
04284   case eCSSProperty_content:
04285   case eCSSProperty_counter_increment:
04286   case eCSSProperty_counter_reset:
04287   case eCSSProperty_cue:
04288   case eCSSProperty_cursor:
04289   case eCSSProperty_font:
04290   case eCSSProperty_image_region:
04291   case eCSSProperty_list_style:
04292   case eCSSProperty_margin:
04293   case eCSSProperty_margin_end:
04294   case eCSSProperty_margin_left:
04295   case eCSSProperty_margin_right:
04296   case eCSSProperty_margin_start:
04297   case eCSSProperty_outline:
04298   case eCSSProperty__moz_outline_radius:
04299   case eCSSProperty_overflow:
04300   case eCSSProperty_padding:
04301   case eCSSProperty_padding_end:
04302   case eCSSProperty_padding_left:
04303   case eCSSProperty_padding_right:
04304   case eCSSProperty_padding_start:
04305   case eCSSProperty_pause:
04306   case eCSSProperty_quotes:
04307   case eCSSProperty_size:
04308   case eCSSProperty_text_shadow:
04309   case eCSSProperty_COUNT:
04310 #ifdef MOZ_SVG
04311   case eCSSProperty_stroke_dasharray:
04312   case eCSSProperty_marker:
04313 #endif
04314     NS_ERROR("not a single value property");
04315     return PR_FALSE;
04316 
04317   case eCSSProperty_margin_left_ltr_source:
04318   case eCSSProperty_margin_left_rtl_source:
04319   case eCSSProperty_margin_right_ltr_source:
04320   case eCSSProperty_margin_right_rtl_source:
04321   case eCSSProperty_padding_left_ltr_source:
04322   case eCSSProperty_padding_left_rtl_source:
04323   case eCSSProperty_padding_right_ltr_source:
04324   case eCSSProperty_padding_right_rtl_source:
04325     NS_ERROR("not currently parsed here");
04326     return PR_FALSE;
04327 
04328   case eCSSProperty_appearance:
04329     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04330                         nsCSSProps::kAppearanceKTable);
04331   case eCSSProperty_azimuth:
04332     return ParseAzimuth(aErrorCode, aValue);
04333   case eCSSProperty_background_attachment:
04334     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04335                         nsCSSProps::kBackgroundAttachmentKTable);
04336   case eCSSProperty__moz_background_clip:
04337     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04338                         nsCSSProps::kBackgroundClipKTable);
04339   case eCSSProperty_background_color:
04340     return ParseVariant(aErrorCode, aValue, VARIANT_HCK,
04341                         nsCSSProps::kBackgroundColorKTable);
04342   case eCSSProperty_background_image:
04343     return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
04344   case eCSSProperty__moz_background_inline_policy:
04345     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04346                         nsCSSProps::kBackgroundInlinePolicyKTable);
04347   case eCSSProperty__moz_background_origin:
04348     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04349                         nsCSSProps::kBackgroundOriginKTable);
04350   case eCSSProperty_background_repeat:
04351     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04352                         nsCSSProps::kBackgroundRepeatKTable);
04353   case eCSSProperty_background_x_position: // for internal use
04354   case eCSSProperty_background_y_position: // for internal use
04355     return ParseVariant(aErrorCode, aValue, VARIANT_HKLP,
04356                         kBackgroundXYPositionKTable);
04357   case eCSSProperty_binding:
04358     return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
04359   case eCSSProperty_border_collapse:
04360     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04361                         nsCSSProps::kBorderCollapseKTable);
04362   case eCSSProperty_border_bottom_color:
04363   case eCSSProperty_border_left_color:
04364   case eCSSProperty_border_right_color:
04365   case eCSSProperty_border_top_color:
04366     return ParseVariant(aErrorCode, aValue, VARIANT_HCK, 
04367                         nsCSSProps::kBorderColorKTable);
04368   case eCSSProperty_border_bottom_style:
04369   case eCSSProperty_border_left_style:
04370   case eCSSProperty_border_right_style:
04371   case eCSSProperty_border_top_style:
04372     return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
04373                         nsCSSProps::kBorderStyleKTable);
04374   case eCSSProperty_border_bottom_width:
04375   case eCSSProperty_border_left_width:
04376   case eCSSProperty_border_right_width:
04377   case eCSSProperty_border_top_width:
04378     return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HKL,
04379                                 nsCSSProps::kBorderWidthKTable);
04380   case eCSSProperty__moz_border_radius_topLeft:
04381   case eCSSProperty__moz_border_radius_topRight:
04382   case eCSSProperty__moz_border_radius_bottomRight:
04383   case eCSSProperty__moz_border_radius_bottomLeft:
04384     return ParseVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
04385   case eCSSProperty__moz_column_count:
04386     return ParsePositiveVariant(aErrorCode, aValue, VARIANT_AHI, nsnull);
04387   case eCSSProperty__moz_column_width:
04388     return ParseVariant(aErrorCode, aValue, VARIANT_AHL, nsnull);
04389   case eCSSProperty__moz_column_gap:
04390     return ParseVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
04391   case eCSSProperty__moz_outline_radius_topLeft:
04392   case eCSSProperty__moz_outline_radius_topRight:
04393   case eCSSProperty__moz_outline_radius_bottomRight:
04394   case eCSSProperty__moz_outline_radius_bottomLeft:
04395     return ParseVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
04396   case eCSSProperty_bottom:
04397   case eCSSProperty_top:
04398   case eCSSProperty_left:
04399   case eCSSProperty_right:
04400          return ParseVariant(aErrorCode, aValue, VARIANT_AHLP, nsnull);
04401   case eCSSProperty_box_align:
04402     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04403                         nsCSSProps::kBoxAlignKTable);
04404   case eCSSProperty_box_direction:
04405     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04406                         nsCSSProps::kBoxDirectionKTable);
04407   case eCSSProperty_box_flex:
04408     return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
04409   case eCSSProperty_box_orient:
04410     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04411                         nsCSSProps::kBoxOrientKTable);
04412   case eCSSProperty_box_pack:
04413     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04414                         nsCSSProps::kBoxPackKTable);
04415   case eCSSProperty_box_ordinal_group:
04416     return ParseVariant(aErrorCode, aValue, VARIANT_INTEGER, nsnull);
04417 #ifdef MOZ_SVG
04418   case eCSSProperty_clip_path:
04419     return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
04420   case eCSSProperty_clip_rule:
04421     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04422                         nsCSSProps::kFillRuleKTable);
04423   case eCSSProperty_dominant_baseline:
04424     return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
04425                         nsCSSProps::kDominantBaselineKTable);
04426   case eCSSProperty_fill:
04427     return ParseVariant(aErrorCode, aValue, VARIANT_HC | VARIANT_NONE | VARIANT_URL,
04428                         nsnull);
04429   case eCSSProperty_fill_opacity:
04430     return ParseVariant(aErrorCode, aValue, VARIANT_HN,
04431                         nsnull);
04432   case eCSSProperty_fill_rule:
04433     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04434                         nsCSSProps::kFillRuleKTable);
04435   case eCSSProperty_marker_end:
04436   case eCSSProperty_marker_mid:
04437   case eCSSProperty_marker_start:
04438     return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
04439   case eCSSProperty_pointer_events:
04440     return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
04441                         nsCSSProps::kPointerEventsKTable);
04442   case eCSSProperty_shape_rendering:
04443     return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
04444                         nsCSSProps::kShapeRenderingKTable);
04445   case eCSSProperty_stop_color:
04446     return ParseVariant(aErrorCode, aValue, VARIANT_HC | VARIANT_NONE,
04447                         nsnull);
04448   case eCSSProperty_stop_opacity:
04449     return ParseVariant(aErrorCode, aValue, VARIANT_HN,
04450                         nsnull);
04451   case eCSSProperty_stroke:
04452     return ParseVariant(aErrorCode, aValue, VARIANT_HC | VARIANT_NONE | VARIANT_URL,
04453                         nsnull);
04454   case eCSSProperty_stroke_dashoffset:
04455     return ParseVariant(aErrorCode, aValue, VARIANT_HLPN,
04456                         nsnull);
04457   case eCSSProperty_stroke_linecap:
04458     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04459                         nsCSSProps::kStrokeLinecapKTable);
04460   case eCSSProperty_stroke_linejoin:
04461     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04462                         nsCSSProps::kStrokeLinejoinKTable);
04463   case eCSSProperty_stroke_miterlimit:
04464     return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HN,
04465                                 nsnull);
04466   case eCSSProperty_stroke_opacity:
04467     return ParseVariant(aErrorCode, aValue, VARIANT_HN,
04468                         nsnull);
04469   case eCSSProperty_stroke_width:
04470     return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLPN,
04471                         nsnull);
04472   case eCSSProperty_text_anchor:
04473     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04474                         nsCSSProps::kTextAnchorKTable);
04475   case eCSSProperty_text_rendering:
04476     return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
04477                         nsCSSProps::kTextRenderingKTable);
04478 #endif
04479   case eCSSProperty_box_sizing:
04480     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04481                         nsCSSProps::kBoxSizingKTable);
04482   case eCSSProperty_height:
04483   case eCSSProperty_width:
04484     return ParsePositiveVariant(aErrorCode, aValue, VARIANT_AHLP, nsnull);
04485   case eCSSProperty_force_broken_image_icon:
04486     return ParsePositiveVariant(aErrorCode, aValue, VARIANT_INTEGER, nsnull);
04487   case eCSSProperty_caption_side:
04488     return ParseVariant(aErrorCode, aValue, VARIANT_HK, 
04489                         nsCSSProps::kCaptionSideKTable);
04490   case eCSSProperty_clear:
04491     return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
04492                         nsCSSProps::kClearKTable);
04493   case eCSSProperty_color:
04494     return ParseVariant(aErrorCode, aValue, VARIANT_HC, nsnull);
04495   case eCSSProperty_cue_after:
04496   case eCSSProperty_cue_before:
04497     return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
04498   case eCSSProperty_direction:
04499     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04500                         nsCSSProps::kDirectionKTable);
04501   case eCSSProperty_display:
04502     if (ParseVariant(aErrorCode, aValue, VARIANT_HOK, nsCSSProps::kDisplayKTable)) {
04503                      if (aValue.GetUnit() == eCSSUnit_Enumerated) {
04504                             switch (aValue.GetIntValue()) {
04505                                    case NS_STYLE_DISPLAY_MARKER:        // bug 2055
04506                                    case NS_STYLE_DISPLAY_RUN_IN:              // bug 2056
04507                                    case NS_STYLE_DISPLAY_COMPACT:       // bug 14983
04508                                    case NS_STYLE_DISPLAY_INLINE_TABLE:  // bug 18218
04509                                           return PR_FALSE;
04510                             }
04511                      }
04512       return PR_TRUE;
04513               }
04514     return PR_FALSE;
04515   case eCSSProperty_elevation:
04516     return ParseVariant(aErrorCode, aValue, VARIANT_HK | VARIANT_ANGLE,
04517                         nsCSSProps::kElevationKTable);
04518   case eCSSProperty_empty_cells:
04519     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04520                         nsCSSProps::kEmptyCellsKTable);
04521   case eCSSProperty_float:
04522     return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
04523                         nsCSSProps::kFloatKTable);
04524   case eCSSProperty_float_edge:
04525     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04526                         nsCSSProps::kFloatEdgeKTable);
04527   case eCSSProperty_font_family:
04528     return ParseFamily(aErrorCode, aValue);
04529   case eCSSProperty_font_size: 
04530     return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HKLP,
04531                                 nsCSSProps::kFontSizeKTable);
04532   case eCSSProperty_font_size_adjust:
04533     return ParseVariant(aErrorCode, aValue, VARIANT_HON,
04534                         nsnull);
04535   case eCSSProperty_font_stretch:
04536     return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
04537                         nsCSSProps::kFontStretchKTable);
04538   case eCSSProperty_font_style:
04539     return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
04540                         nsCSSProps::kFontStyleKTable);
04541   case eCSSProperty_font_variant:
04542     return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
04543                         nsCSSProps::kFontVariantKTable);
04544   case eCSSProperty_font_weight:
04545     return ParseFontWeight(aErrorCode, aValue);
04546   case eCSSProperty_letter_spacing:
04547   case eCSSProperty_word_spacing:
04548     return ParseVariant(aErrorCode, aValue, VARIANT_HL | VARIANT_NORMAL, nsnull);
04549   case eCSSProperty_line_height:
04550     return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLPN | VARIANT_NORMAL, nsnull);
04551   case eCSSProperty_list_style_image:
04552     return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
04553   case eCSSProperty_list_style_position:
04554     return ParseVariant(aErrorCode, aValue, VARIANT_HK, nsCSSProps::kListStylePositionKTable);
04555   case eCSSProperty_list_style_type:
04556     return ParseVariant(aErrorCode, aValue, VARIANT_HOK, nsCSSProps::kListStyleKTable);
04557   case eCSSProperty_margin_bottom:
04558   case eCSSProperty_margin_end_value: // for internal use
04559   case eCSSProperty_margin_left_value: // for internal use
04560   case eCSSProperty_margin_right_value: // for internal use
04561   case eCSSProperty_margin_start_value: // for internal use
04562   case eCSSProperty_margin_top:
04563     return ParseVariant(aErrorCode, aValue, VARIANT_AHLP, nsnull);
04564   case eCSSProperty_marker_offset:
04565     return ParseVariant(aErrorCode, aValue, VARIANT_AHL, nsnull);
04566   case eCSSProperty_marks:
04567     return ParseMarks(aErrorCode, aValue);
04568   case eCSSProperty_max_height:
04569   case eCSSProperty_max_width:
04570     return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLPO, nsnull);
04571   case eCSSProperty_min_height:
04572   case eCSSProperty_min_width:
04573     return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
04574   case eCSSProperty_opacity:
04575     return ParseVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
04576   case eCSSProperty_orphans:
04577   case eCSSProperty_widows:
04578     return ParseVariant(aErrorCode, aValue, VARIANT_HI, nsnull);
04579   case eCSSProperty_outline_color:
04580     return ParseVariant(aErrorCode, aValue, VARIANT_HCK, 
04581                         nsCSSProps::kOutlineColorKTable);
04582   case eCSSProperty_outline_style:
04583     return ParseVariant(aErrorCode, aValue, VARIANT_HOK | VARIANT_AUTO, 
04584                         nsCSSProps::kOutlineStyleKTable);
04585   case eCSSProperty_outline_width:
04586     return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HKL,
04587                                 nsCSSProps::kBorderWidthKTable);
04588   case eCSSProperty_outline_offset:
04589     return ParseVariant(aErrorCode, aValue, VARIANT_HL, nsnull);
04590   case eCSSProperty_overflow_x:
04591   case eCSSProperty_overflow_y:
04592     return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
04593                         nsCSSProps::kOverflowSubKTable);
04594   case eCSSProperty_padding_bottom:
04595   case eCSSProperty_padding_end_value: // for internal use
04596   case eCSSProperty_padding_left_value: // for internal use
04597   case eCSSProperty_padding_right_value: // for internal use
04598   case eCSSProperty_padding_start_value: // for internal use
04599   case eCSSProperty_padding_top:
04600     return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
04601   case eCSSProperty_page:
04602     return ParseVariant(aErrorCode, aValue, VARIANT_AUTO | VARIANT_IDENTIFIER, nsnull);
04603   case eCSSProperty_page_break_after:
04604   case eCSSProperty_page_break_before:
04605     return ParseVariant(aErrorCode, aValue, VARIANT_AHK, 
04606                         nsCSSProps::kPageBreakKTable);
04607   case eCSSProperty_page_break_inside:
04608     return ParseVariant(aErrorCode, aValue, VARIANT_AHK, 
04609                         nsCSSProps::kPageBreakInsideKTable);
04610   case eCSSProperty_pause_after:
04611   case eCSSProperty_pause_before:
04612     return ParseVariant(aErrorCode, aValue, VARIANT_HTP, nsnull);
04613   case eCSSProperty_pitch:
04614     return ParseVariant(aErrorCode, aValue, VARIANT_HKF, nsCSSProps::kPitchKTable);
04615   case eCSSProperty_pitch_range:
04616     return ParseVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
04617   case eCSSProperty_position:
04618     return ParseVariant(aErrorCode, aValue, VARIANT_HK, nsCSSProps::kPositionKTable);
04619   case eCSSProperty_richness:
04620     return ParseVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
04621   case eCSSProperty_speak:
04622     return ParseVariant(aErrorCode, aValue, VARIANT_HMK | VARIANT_NONE,
04623                         nsCSSProps::kSpeakKTable);
04624   case eCSSProperty_speak_header:
04625     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04626                         nsCSSProps::kSpeakHeaderKTable);
04627   case eCSSProperty_speak_numeral:
04628     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04629                         nsCSSProps::kSpeakNumeralKTable);
04630   case eCSSProperty_speak_punctuation:
04631     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04632                         nsCSSProps::kSpeakPunctuationKTable);
04633   case eCSSProperty_speech_rate:
04634     return ParseVariant(aErrorCode, aValue, VARIANT_HN | VARIANT_KEYWORD,
04635                         nsCSSProps::kSpeechRateKTable);
04636   case eCSSProperty_stress:
04637     return ParseVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
04638   case eCSSProperty_table_layout:
04639     return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
04640                         nsCSSProps::kTableLayoutKTable);
04641   case eCSSProperty_text_align:
04642     // When we support aligning on a string, we can parse text-align
04643     // as a string....
04644     return ParseVariant(aErrorCode, aValue, VARIANT_HK /* | VARIANT_STRING */,
04645                         nsCSSProps::kTextAlignKTable);
04646   case eCSSProperty_text_decoration:
04647     return ParseTextDecoration(aErrorCode, aValue);
04648   case eCSSProperty_text_indent:
04649     return ParseVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
04650   case eCSSProperty_text_transform:
04651     return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
04652                         nsCSSProps::kTextTransformKTable);
04653   case eCSSProperty_unicode_bidi:
04654     return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
04655                         nsCSSProps::kUnicodeBidiKTable);
04656   case eCSSProperty_user_focus:
04657     return ParseVariant(aErrorCode, aValue, VARIANT_HMK | VARIANT_NONE,
04658                         nsCSSProps::kUserFocusKTable);
04659   case eCSSProperty_user_input:
04660     return ParseVariant(aErrorCode, aValue, VARIANT_AHK | VARIANT_NONE,
04661                         nsCSSProps::kUserInputKTable);
04662   case eCSSProperty_user_modify:
04663     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
04664                         nsCSSProps::kUserModifyKTable);
04665   case eCSSProperty_user_select:
04666     return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
04667                         nsCSSProps::kUserSelectKTable);
04668   case eCSSProperty_vertical_align:
04669     return ParseVariant(aErrorCode, aValue, VARIANT_HKLP,
04670                         nsCSSProps::kVerticalAlignKTable);
04671   case eCSSProperty_visibility:
04672     return ParseVariant(aErrorCode, aValue, VARIANT_HK, 
04673                         nsCSSProps::kVisibilityKTable);
04674   case eCSSProperty_voice_family:
04675     return ParseFamily(aErrorCode, aValue);
04676   case eCSSProperty_volume:
04677     return ParseVariant(aErrorCode, aValue, VARIANT_HPN | VARIANT_KEYWORD,
04678                         nsCSSProps::kVolumeKTable);
04679   case eCSSProperty_white_space:
04680     return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
04681                         nsCSSProps::kWhitespaceKTable);
04682   case eCSSProperty_z_index:
04683     return ParseVariant(aErrorCode, aValue, VARIANT_AHI, nsnull);
04684   }
04685   // explicitly do NOT have a default case to let the compiler
04686   // help find missing properties
04687   return PR_FALSE;
04688 }
04689 
04690 PRBool CSSParserImpl::ParseAzimuth(nsresult& aErrorCode, nsCSSValue& aValue)
04691 {
04692   if (ParseVariant(aErrorCode, aValue, VARIANT_HK | VARIANT_ANGLE, 
04693                    nsCSSProps::kAzimuthKTable)) {
04694     if (eCSSUnit_Enumerated == aValue.GetUnit()) {
04695       PRInt32 intValue = aValue.GetIntValue();
04696       if ((NS_STYLE_AZIMUTH_LEFT_SIDE <= intValue) && 
04697           (intValue <= NS_STYLE_AZIMUTH_BEHIND)) {  // look for optional modifier
04698         nsCSSValue  modifier;
04699         if (ParseEnum(aErrorCode, modifier, nsCSSProps::kAzimuthKTable)) {
04700           PRInt32 enumValue = modifier.GetIntValue();
04701           if (((intValue == NS_STYLE_AZIMUTH_BEHIND) && 
04702                (NS_STYLE_AZIMUTH_LEFT_SIDE <= enumValue) && (enumValue <= NS_STYLE_AZIMUTH_RIGHT_SIDE)) ||
04703               ((enumValue == NS_STYLE_AZIMUTH_BEHIND) && 
04704                (NS_STYLE_AZIMUTH_LEFT_SIDE <= intValue) && (intValue <= NS_STYLE_AZIMUTH_RIGHT_SIDE))) {
04705             aValue.SetIntValue(intValue | enumValue, eCSSUnit_Enumerated);
04706             return PR_TRUE;
04707           }
04708           // Put the unknown identifier back and return
04709           UngetToken();
04710           return PR_FALSE;
04711         }
04712       }
04713     }
04714     return PR_TRUE;
04715   }
04716   return PR_FALSE;
04717 }
04718 
04719 static nsCSSValue
04720 BackgroundPositionMaskToCSSValue(PRInt32 aMask, PRBool isX)
04721 {
04722   PRInt32 pct = 50;
04723   if (isX) {
04724     if (aMask & BG_LEFT) {
04725       pct = 0;
04726     }
04727     else if (aMask & BG_RIGHT) {
04728       pct = 100;
04729     }
04730   }
04731   else {
04732     if (aMask & BG_TOP) {
04733       pct = 0;
04734     }
04735     else if (aMask & BG_BOTTOM) {
04736       pct = 100;
04737     }
04738   }
04739 
04740   return nsCSSValue(pct, eCSSUnit_Enumerated);
04741 }
04742 
04743 PRBool CSSParserImpl::ParseBackground(nsresult& aErrorCode)
04744 {
04745   const PRInt32 numProps = 6;
04746   static const nsCSSProperty kBackgroundIDs[numProps] = {
04747     eCSSProperty_background_color,
04748     eCSSProperty_background_image,
04749     eCSSProperty_background_repeat,
04750     eCSSProperty_background_attachment,
04751     eCSSProperty_background_x_position,
04752     eCSSProperty_background_y_position
04753   };
04754 
04755   nsCSSValue  values[numProps];
04756   PRInt32 found = ParseChoice(aErrorCode, values, kBackgroundIDs, numProps);
04757   if ((found < 1) || (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE))) {
04758     return PR_FALSE;
04759   }
04760 
04761   if (0 != (found & 0x30)) {  // found one or more position values, validate them
04762     if (0 == (found & 0x20)) {
04763       if (eCSSUnit_Enumerated == values[4].GetUnit()) {
04764         PRInt32 mask = values[4].GetIntValue();
04765         values[4] = BackgroundPositionMaskToCSSValue(mask, PR_TRUE);
04766         values[5] = BackgroundPositionMaskToCSSValue(mask, PR_FALSE);
04767       }
04768       else {
04769         values[5].SetPercentValue(0.5f);
04770       }
04771     }
04772     else { // both x & y values
04773       nsCSSUnit xUnit = values[4].GetUnit();
04774       nsCSSUnit yUnit = values[5].GetUnit();
04775       if (eCSSUnit_Enumerated == xUnit) {
04776         PRInt32 xValue = values[4].GetIntValue();
04777         if (eCSSUnit_Enumerated == yUnit) {
04778           PRInt32 yValue = values[5].GetIntValue();
04779           if (0 != (xValue & (BG_LEFT | BG_RIGHT)) &&  // x is really an x value
04780               0 != (yValue & (BG_LEFT | BG_RIGHT))) {  // y is also an x value
04781             return PR_FALSE;
04782           }
04783           if (0 != (xValue & (BG_TOP | BG_BOTTOM)) &&  // x is really an y value
04784               0 != (yValue & (BG_TOP | BG_BOTTOM))) {  // y is also an y value
04785             return PR_FALSE;
04786           }
04787           if (0 != (xValue & (BG_TOP | BG_BOTTOM)) ||  // x is really a y value
04788               0 != (yValue & (BG_LEFT | BG_RIGHT))) {  // or y is really an x value
04789             PRInt32 holdXValue = xValue;
04790             xValue = yValue;
04791             yValue = holdXValue;
04792           }
04793           NS_ASSERTION(xValue & BG_CLR, "bad x value");
04794           NS_ASSERTION(yValue & BG_CTB, "bad y value");
04795           values[4] = BackgroundPositionMaskToCSSValue(xValue, PR_TRUE);
04796           values[5] = BackgroundPositionMaskToCSSValue(yValue, PR_FALSE);
04797         }
04798         else {
04799           if (!(xValue & BG_CLR)) {
04800             // The first keyword can only be 'center', 'left', or 'right'
04801             return PR_FALSE;
04802           }
04803           values[4] = BackgroundPositionMaskToCSSValue(xValue, PR_TRUE);
04804         }
04805       }
04806       else {
04807         if (eCSSUnit_Enumerated == yUnit) {
04808           PRInt32 yValue = values[5].GetIntValue();
04809           if (!(yValue & BG_CTB)) {
04810             // The second keyword can only be 'center', 'top', or 'bottom'
04811             return PR_FALSE;
04812           }
04813           values[5] = BackgroundPositionMaskToCSSValue(yValue, PR_FALSE);
04814         }
04815       }
04816     }
04817   }
04818 
04819   // Provide missing values
04820   if ((found & 0x01) == 0) {
04821     values[0].SetIntValue(NS_STYLE_BG_COLOR_TRANSPARENT, eCSSUnit_Enumerated);
04822   }
04823   if ((found & 0x02) == 0) {
04824     values[1].SetNoneValue();
04825   }
04826   if ((found & 0x04) == 0) {
04827     values[2].SetIntValue(NS_STYLE_BG_REPEAT_XY, eCSSUnit_Enumerated);
04828   }
04829   if ((found & 0x08) == 0) {
04830     values[3].SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL, eCSSUnit_Enumerated);
04831   }
04832   if ((found & 0x30) == 0) {
04833     values[4].SetPercentValue(0.0f);
04834     values[5].SetPercentValue(0.0f);
04835   }
04836 
04837   PRInt32 index;
04838   for (index = 0; index < numProps; ++index) {
04839     AppendValue(kBackgroundIDs[index], values[index]);
04840   }
04841 
04842   // Background properties not settable from the shorthand get reset to their initial value
04843   static const PRInt32 numResetProps = 3;
04844   static const nsCSSProperty kBackgroundResetIDs[numResetProps] = {
04845     eCSSProperty__moz_background_clip,
04846     eCSSProperty__moz_background_inline_policy,
04847     eCSSProperty__moz_background_origin
04848   };
04849 
04850   nsCSSValue initial;
04851   initial.SetInitialValue();
04852   for (index = 0; index < numResetProps; ++index) {
04853     AppendValue(kBackgroundResetIDs[index], initial);
04854   }
04855 
04856   return PR_TRUE;
04857 }
04858 
04859 PRBool CSSParserImpl::ParseBackgroundPosition(nsresult& aErrorCode)
04860 {
04861   // First try a percentage or a length value
04862   nsCSSValue xValue, yValue;
04863   if (ParseVariant(aErrorCode, xValue, VARIANT_HLP, nsnull)) {
04864     if (eCSSUnit_Inherit == xValue.GetUnit() ||
04865         eCSSUnit_Initial == xValue.GetUnit()) {  // both are inherited or both are set to initial
04866       if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
04867         AppendValue(eCSSProperty_background_x_position, xValue);
04868         AppendValue(eCSSProperty_background_y_position, xValue);
04869         return PR_TRUE;
04870       }
04871       return PR_FALSE;
04872     }
04873     // We have one percentage/length. Get the optional second
04874     // percentage/length/keyword.
04875     if (ParseVariant(aErrorCode, yValue, VARIANT_LP, nsnull)) {
04876       // We have two numbers
04877       if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
04878         AppendValue(eCSSProperty_background_x_position, xValue);
04879         AppendValue(eCSSProperty_background_y_position, yValue);
04880         return PR_TRUE;
04881       }
04882       return PR_FALSE;
04883     }
04884 
04885     if (ParseEnum(aErrorCode, yValue, kBackgroundXYPositionKTable)) {
04886       PRInt32 yVal = yValue.GetIntValue();
04887       if (!(yVal & BG_CTB)) {
04888         // The second keyword can only be 'center', 'top', or 'bottom'
04889         return PR_FALSE;
04890       }
04891       if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
04892         yValue = BackgroundPositionMaskToCSSValue(yVal, PR_FALSE);
04893         AppendValue(eCSSProperty_background_x_position, xValue);
04894         AppendValue(eCSSProperty_background_y_position, yValue);
04895         return PR_TRUE;
04896       }
04897       return PR_FALSE;
04898     }
04899 
04900     // If only one percentage or length value is given, it sets the
04901     // horizontal position only, and the vertical position will be 50%.
04902     if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
04903       AppendValue(eCSSProperty_background_x_position, xValue);
04904       AppendValue(eCSSProperty_background_y_position, nsCSSValue(0.5f, eCSSUnit_Percent));
04905       return PR_TRUE;
04906     }
04907     return PR_FALSE;
04908   }
04909 
04910   // Now try keywords. We do this manually to allow for the first
04911   // appearance of "center" to apply to the either the x or y
04912   // position (it's ambiguous so we have to disambiguate). Each
04913   // allowed keyword value is assigned it's own bit. We don't allow
04914   // any duplicate keywords other than center. We try to get two
04915   // keywords but it's okay if there is only one.
04916   PRInt32 mask = 0;
04917   if (ParseEnum(aErrorCode, xValue, kBackgroundXYPositionKTable)) {
04918     PRInt32 bit = xValue.GetIntValue();
04919     mask |= bit;
04920     if (ParseEnum(aErrorCode, xValue, kBackgroundXYPositionKTable)) {
04921       bit = xValue.GetIntValue();
04922       if (mask & (bit & ~BG_CENTER)) {
04923         // Only the 'center' keyword can be duplicated.
04924         return PR_FALSE;
04925       }
04926       mask |= bit;
04927     }
04928     else {
04929       // Only one keyword.  See if we have a length or percentage.
04930       if (ParseVariant(aErrorCode, yValue, VARIANT_LP, nsnull)) {
04931         if (!(mask & BG_CLR)) {
04932           // The first keyword can only be 'center', 'left', or 'right'
04933           return PR_FALSE;
04934         }
04935 
04936         if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
04937           xValue = BackgroundPositionMaskToCSSValue(mask, PR_TRUE);
04938           AppendValue(eCSSProperty_background_x_position, xValue);
04939           AppendValue(eCSSProperty_background_y_position, yValue);
04940           return PR_TRUE;
04941         }
04942         return PR_FALSE;
04943       }
04944     }
04945   }
04946 
04947   // Check for bad input. Bad input consists of no matching keywords,
04948   // or pairs of x keywords or pairs of y keywords.
04949   if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
04950       (mask == (BG_LEFT | BG_RIGHT))) {
04951     return PR_FALSE;
04952   }
04953 
04954   if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
04955     // Create style values
04956     xValue = BackgroundPositionMaskToCSSValue(mask, PR_TRUE);
04957     yValue = BackgroundPositionMaskToCSSValue(mask, PR_FALSE);
04958     AppendValue(eCSSProperty_background_x_position, xValue);
04959     AppendValue(eCSSProperty_background_y_position, yValue);
04960     return PR_TRUE;
04961   }
04962   return PR_FALSE;
04963 }
04964 
04965 // These must be in CSS order (top,right,bottom,left) for indexing to work
04966 static const nsCSSProperty kBorderStyleIDs[] = {
04967   eCSSProperty_border_top_style,
04968   eCSSProperty_border_right_style,
04969   eCSSProperty_border_bottom_style,
04970   eCSSProperty_border_left_style
04971 };
04972 static const nsCSSProperty kBorderWidthIDs[] = {
04973   eCSSProperty_border_top_width,
04974   eCSSProperty_border_right_width,
04975   eCSSProperty_border_bottom_width,
04976   eCSSProperty_border_left_width
04977 };
04978 static const nsCSSProperty kBorderColorIDs[] = {
04979   eCSSProperty_border_top_color,
04980   eCSSProperty_border_right_color,
04981   eCSSProperty_border_bottom_color,
04982   eCSSProperty_border_left_color
04983 };
04984 static const nsCSSProperty kBorderRadiusIDs[] = {
04985   eCSSProperty__moz_border_radius_topLeft,
04986   eCSSProperty__moz_border_radius_topRight,
04987   eCSSProperty__moz_border_radius_bottomRight,
04988   eCSSProperty__moz_border_radius_bottomLeft
04989 };
04990 static const nsCSSProperty kOutlineRadiusIDs[] = {
04991   eCSSProperty__moz_outline_radius_topLeft,
04992   eCSSProperty__moz_outline_radius_topRight,
04993   eCSSProperty__moz_outline_radius_bottomRight,
04994   eCSSProperty__moz_outline_radius_bottomLeft
04995 };
04996 
04997 PRBool CSSParserImpl::ParseBorderColor(nsresult& aErrorCode)
04998 {
04999   return ParseBoxProperties(aErrorCode, mTempData.mMargin.mBorderColor,
05000                             kBorderColorIDs);
05001 }
05002 
05003 PRBool CSSParserImpl::ParseBorderSpacing(nsresult& aErrorCode)
05004 {
05005   nsCSSValue  xValue;
05006   if (ParsePositiveVariant(aErrorCode, xValue, VARIANT_HL, nsnull)) {
05007     if (xValue.IsLengthUnit()) {
05008       // We have one length. Get the optional second length.
05009       nsCSSValue yValue;
05010       if (ParsePositiveVariant(aErrorCode, yValue, VARIANT_LENGTH, nsnull)) {
05011         // We have two numbers
05012         if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05013           mTempData.mTable.mBorderSpacing.mXValue = xValue;
05014           mTempData.mTable.mBorderSpacing.mYValue = yValue;
05015           mTempData.SetPropertyBit(eCSSProperty_border_spacing);
05016           return PR_TRUE;
05017         }
05018         return PR_FALSE;
05019       }
05020     }
05021 
05022     // We have one length which is the horizontal spacing. Create a value for
05023     // the vertical spacing which is equal
05024     if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05025       mTempData.mTable.mBorderSpacing.SetBothValuesTo(xValue);
05026       mTempData.SetPropertyBit(eCSSProperty_border_spacing);
05027       return PR_TRUE;
05028     }
05029   }
05030   return PR_FALSE;
05031 }
05032 
05033 PRBool CSSParserImpl::ParseBorderSide(nsresult& aErrorCode,
05034                                       const nsCSSProperty aPropIDs[],
05035                                       PRBool aSetAllSides)
05036 {
05037   const PRInt32 numProps = 3;
05038   nsCSSValue  values[numProps];
05039 
05040   PRInt32 found = ParseChoice(aErrorCode, values, aPropIDs, numProps);
05041   if ((found < 1) || (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE))) {
05042     return PR_FALSE;
05043   }
05044 
05045   if ((found & 1) == 0) { // Provide default border-width
05046     values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
05047   }
05048   if ((found & 2) == 0) { // Provide default border-style
05049     values[1].SetNoneValue();
05050   }
05051   if ((found & 4) == 0) { // text color will be used
05052     values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
05053   }
05054 
05055   if (aSetAllSides) {
05056     // Parsing "border" shorthand; set all four sides to the same thing
05057     for (PRInt32 index = 0; index < 4; index++) {
05058       NS_ASSERTION(numProps == 3, "This code needs updating");
05059       AppendValue(kBorderWidthIDs[index], values[0]);
05060       AppendValue(kBorderStyleIDs[index], values[1]);
05061       AppendValue(kBorderColorIDs[index], values[2]);
05062     }   
05063   }
05064   else {
05065     // Just set our one side
05066     for (PRInt32 index = 0; index < numProps; index++) {
05067       AppendValue(aPropIDs[index], values[index]);
05068     }
05069   }
05070   return PR_TRUE;
05071 }
05072 
05073 PRBool CSSParserImpl::ParseBorderStyle(nsresult& aErrorCode)
05074 {
05075   return ParseBoxProperties(aErrorCode, mTempData.mMargin.mBorderStyle,
05076                             kBorderStyleIDs);
05077 }
05078 
05079 PRBool CSSParserImpl::ParseBorderWidth(nsresult& aErrorCode)
05080 {
05081   return ParseBoxProperties(aErrorCode, mTempData.mMargin.mBorderWidth,
05082                             kBorderWidthIDs);
05083 }
05084 
05085 PRBool CSSParserImpl::ParseBorderRadius(nsresult& aErrorCode)
05086 {
05087   return ParseBoxProperties(aErrorCode, mTempData.mMargin.mBorderRadius,
05088                             kBorderRadiusIDs);
05089 }
05090 
05091 PRBool CSSParserImpl::ParseOutlineRadius(nsresult& aErrorCode)
05092 {
05093   return ParseBoxProperties(aErrorCode, mTempData.mMargin.mOutlineRadius,
05094                             kOutlineRadiusIDs);
05095 }
05096 
05097 PRBool CSSParserImpl::ParseBorderColors(nsresult& aErrorCode,
05098                                         nsCSSValueList** aResult,
05099                                         nsCSSProperty aProperty)
05100 {
05101   nsCSSValue value;
05102   if (ParseVariant(aErrorCode, value, VARIANT_HCK|VARIANT_NONE, nsCSSProps::kBorderColorKTable)) {
05103     nsCSSValueList* listHead = new nsCSSValueList();
05104     nsCSSValueList* list = listHead;
05105     if (!list) {
05106       aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05107       return PR_FALSE;
05108     }
05109     list->mValue = value;
05110 
05111     while (list) {
05112       if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05113         mTempData.SetPropertyBit(aProperty);
05114         *aResult = listHead;
05115         aErrorCode = NS_OK;
05116         return PR_TRUE;
05117       }
05118       if (ParseVariant(aErrorCode, value, VARIANT_HCK|VARIANT_NONE, nsCSSProps::kBorderColorKTable)) {
05119         list->mNext = new nsCSSValueList();
05120         list = list->mNext;
05121         if (list)
05122           list->mValue = value;
05123         else
05124           aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05125       }
05126       else
05127         break;
05128     }
05129     delete listHead;
05130   }
05131   return PR_FALSE;
05132 }
05133 
05134 PRBool
05135 CSSParserImpl::ParseRect(nsCSSRect& aRect, nsresult& aErrorCode,
05136                          nsCSSProperty aPropID)
05137 {
05138   nsCSSRect rect;
05139   PRBool result;
05140   if ((result = DoParseRect(rect, aErrorCode)) &&
05141       rect != aRect) {
05142     aRect = rect;
05143     mTempData.SetPropertyBit(aPropID);
05144   }
05145   return result;
05146 }
05147 
05148 PRBool
05149 CSSParserImpl::DoParseRect(nsCSSRect& aRect, nsresult& aErrorCode)
05150 {
05151   if (! GetToken(aErrorCode, PR_TRUE)) {
05152     return PR_FALSE;
05153   }
05154   if (eCSSToken_Ident == mToken.mType) {
05155     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
05156     switch (keyword) {
05157       case eCSSKeyword_auto:
05158         if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05159           aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Auto));
05160           return PR_TRUE;
05161         }
05162         break;
05163       case eCSSKeyword_inherit:
05164         if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05165           aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Inherit));
05166           return PR_TRUE;
05167         }
05168         break;
05169       case eCSSKeyword__moz_initial:
05170         if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05171           aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Initial));
05172           return PR_TRUE;
05173         }
05174         break;
05175       default:
05176         UngetToken();
05177         break;
05178     }
05179   } else if ((eCSSToken_Function == mToken.mType) && 
05180              mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
05181     if (!ExpectSymbol(aErrorCode, '(', PR_TRUE)) {
05182       return PR_FALSE;
05183     }
05184     NS_FOR_CSS_SIDES(side) {
05185       if (! ParseVariant(aErrorCode, aRect.*(nsCSSRect::sides[side]),
05186                          VARIANT_AL, nsnull)) {
05187         return PR_FALSE;
05188       }
05189       if (3 != side) {
05190         // skip optional commas between elements
05191         ExpectSymbol(aErrorCode, ',', PR_TRUE);
05192       }
05193     }
05194     if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
05195       return PR_FALSE;
05196     }
05197     if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05198       return PR_TRUE;
05199     }
05200   } else {
05201     UngetToken();
05202   }
05203   return PR_FALSE;
05204 }
05205 
05206 #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
05207                          VARIANT_KEYWORD)
05208 PRBool CSSParserImpl::ParseContent(nsresult& aErrorCode)
05209 {
05210   // XXX Rewrite to make it look more like ParseCursor or ParseCounterData?
05211   nsCSSValue  value;
05212   if (ParseVariant(aErrorCode, value,
05213                    VARIANT_CONTENT | VARIANT_INHERIT | VARIANT_NORMAL, 
05214                    nsCSSProps::kContentKTable)) {
05215     nsCSSValueList* listHead = new nsCSSValueList();
05216     nsCSSValueList* list = listHead;
05217     if (nsnull == list) {
05218       aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05219       return PR_FALSE;
05220     }
05221     list->mValue = value;
05222 
05223     while (nsnull != list) {
05224       if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05225         mTempData.SetPropertyBit(eCSSProperty_content);
05226         mTempData.mContent.mContent = listHead;
05227         aErrorCode = NS_OK;
05228         return PR_TRUE;
05229       }
05230       if (eCSSUnit_Inherit == value.GetUnit() ||
05231           eCSSUnit_Initial == value.GetUnit() ||
05232           eCSSUnit_Normal == value.GetUnit()) {
05233         // This only matters the first time through the loop.
05234         return PR_FALSE;
05235       }
05236       if (ParseVariant(aErrorCode, value, VARIANT_CONTENT, nsCSSProps::kContentKTable)) {
05237         list->mNext = new nsCSSValueList();
05238         list = list->mNext;
05239         if (nsnull != list) {
05240           list->mValue = value;
05241         }
05242         else {
05243           aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05244         }
05245       }
05246       else {
05247         break;
05248       }
05249     }
05250     delete listHead;
05251   }
05252   return PR_FALSE;
05253 }
05254 
05255 struct SingleCounterPropValue {
05256   char str[13];
05257   nsCSSUnit unit;
05258 };
05259 
05260 PRBool CSSParserImpl::ParseCounterData(nsresult& aErrorCode,
05261                                        nsCSSCounterData** aResult,
05262                                        nsCSSProperty aPropID)
05263 {
05264   nsString* ident = NextIdent(aErrorCode);
05265   if (nsnull == ident) {
05266     return PR_FALSE;
05267   }
05268   static const SingleCounterPropValue singleValues[] = {
05269     { "none", eCSSUnit_None },
05270     { "inherit", eCSSUnit_Inherit },
05271     { "-moz-initial", eCSSUnit_Initial }
05272   };
05273   for (const SingleCounterPropValue *sv = singleValues,
05274            *sv_end = singleValues + NS_ARRAY_LENGTH(singleValues);
05275        sv != sv_end; ++sv) {
05276     if (ident->LowerCaseEqualsLiteral(sv->str)) {
05277       if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05278         nsCSSCounterData* dataHead = new nsCSSCounterData();
05279         if (!dataHead) {
05280           aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05281           return PR_FALSE;
05282         }
05283         dataHead->mCounter = nsCSSValue(sv->unit);
05284         *aResult = dataHead;
05285         mTempData.SetPropertyBit(aPropID);
05286         return PR_TRUE;
05287       }
05288       return PR_FALSE;
05289     }
05290   }
05291   UngetToken(); // undo NextIdent
05292 
05293   nsCSSCounterData* dataHead = nsnull;
05294   nsCSSCounterData **next = &dataHead;
05295   for (;;) {
05296     if (!GetToken(aErrorCode, PR_TRUE) || mToken.mType != eCSSToken_Ident) {
05297       break;
05298     }
05299     nsCSSCounterData *data = *next = new nsCSSCounterData();
05300     if (!data) {
05301       aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05302       break;
05303     }
05304     next = &data->mNext;
05305     data->mCounter.SetStringValue(mToken.mIdent, eCSSUnit_String);
05306     if (GetToken(aErrorCode, PR_TRUE)) {
05307       if (eCSSToken_Number == mToken.mType && mToken.mIntegerValid) {
05308         data->mValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
05309       } else {
05310         UngetToken();
05311       }
05312     }
05313     if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05314       mTempData.SetPropertyBit(aPropID);
05315       *aResult = dataHead;
05316       aErrorCode = NS_OK;
05317       return PR_TRUE;
05318     }
05319   }
05320   delete dataHead;
05321   return PR_FALSE;
05322 }
05323 
05324 PRBool CSSParserImpl::ParseCue(nsresult& aErrorCode)
05325 {
05326   nsCSSValue before;
05327   if (ParseSingleValueProperty(aErrorCode, before, eCSSProperty_cue_before)) {
05328     if (eCSSUnit_URL == before.GetUnit()) {
05329       nsCSSValue after;
05330       if (ParseSingleValueProperty(aErrorCode, after, eCSSProperty_cue_after)) {
05331         if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05332           AppendValue(eCSSProperty_cue_before, before);
05333           AppendValue(eCSSProperty_cue_after, after);
05334           return PR_TRUE;
05335         }
05336         return PR_FALSE;
05337       }
05338     }
05339     if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05340       AppendValue(eCSSProperty_cue_before, before);
05341       AppendValue(eCSSProperty_cue_after, before);
05342       return PR_TRUE;
05343     }
05344   }
05345   return PR_FALSE;
05346 }
05347 
05348 PRBool CSSParserImpl::ParseCursor(nsresult& aErrorCode)
05349 {
05350   nsCSSValueList *list = nsnull;
05351   for (nsCSSValueList **curp = &list, *cur; ; curp = &cur->mNext) {
05352     cur = *curp = new nsCSSValueList();
05353     if (!cur) {
05354       aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05355       break;
05356     }
05357     if (!ParseVariant(aErrorCode, cur->mValue,
05358                       (cur == list) ? VARIANT_AHUK : VARIANT_AUK,
05359                       nsCSSProps::kCursorKTable)) {
05360       break;
05361     }
05362     if (cur->mValue.GetUnit() != eCSSUnit_URL) {
05363       if (!ExpectEndProperty(aErrorCode, PR_TRUE)) {
05364         break;
05365       }
05366       // Only success case here, since having the failure case at the
05367       // end allows more sharing of code.
05368       mTempData.SetPropertyBit(eCSSProperty_cursor);
05369       mTempData.mUserInterface.mCursor = list;
05370       aErrorCode = NS_OK;
05371       return PR_TRUE;
05372     } 
05373     // We have a URL, so make a value array with three values.
05374     nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
05375     if (!val) {
05376       aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05377       break;
05378     }
05379     val->Item(0) = cur->mValue;
05380     cur->mValue.SetArrayValue(val, eCSSUnit_Array);
05381 
05382     // Parse optional x and y position of cursor hotspot (css3-ui).
05383     if (ParseVariant(aErrorCode, val->Item(1), VARIANT_NUMBER, nsnull)) {
05384       // If we have one number, we must have two.
05385       if (!ParseVariant(aErrorCode, val->Item(2), VARIANT_NUMBER, nsnull)) {
05386         break;
05387       }
05388     }
05389 
05390     if (!ExpectSymbol(aErrorCode, ',', PR_TRUE)) {
05391       break;
05392     }
05393   }
05394   // Have failure case at the end so we can |break| to get to it.
05395   delete list;
05396   return PR_FALSE;
05397 }
05398 
05399 
05400 PRBool CSSParserImpl::ParseFont(nsresult& aErrorCode)
05401 {
05402   static const nsCSSProperty fontIDs[] = {
05403     eCSSProperty_font_style,
05404     eCSSProperty_font_variant,
05405     eCSSProperty_font_weight
05406   };
05407 
05408   nsCSSValue  family;
05409   if (ParseVariant(aErrorCode, family, VARIANT_HK, nsCSSProps::kFontKTable)) {
05410     if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05411       if (eCSSUnit_Inherit == family.GetUnit()) {
05412         AppendValue(eCSSProperty_font_family, family);
05413         AppendValue(eCSSProperty_font_style, family);
05414         AppendValue(eCSSProperty_font_variant, family);
05415         AppendValue(eCSSProperty_font_weight, family);
05416         AppendValue(eCSSProperty_font_size, family);
05417         AppendValue(eCSSProperty_line_height, family);
05418         AppendValue(eCSSProperty_font_stretch, family);
05419         AppendValue(eCSSProperty_font_size_adjust, family);
05420       }
05421       else {
05422         AppendValue(eCSSProperty_font_family, family);  // keyword value overrides everything else
05423         nsCSSValue empty;
05424         // XXXbz this is actually _clearing_ the values for the following
05425         // properties in mTempData, but setting the bit for them.  We need that
05426         // because we want to clear out the values in mData when all is said
05427         // and done.  See the code in TransferTempData that handles this.  The
05428         // end result is that mData always has its property bits set like it
05429         // should, but mTempData can, in fact, have bits set for properties
05430         // that are not set...
05431         AppendValue(eCSSProperty_font_style, empty);
05432         AppendValue(eCSSProperty_font_variant, empty);
05433         AppendValue(eCSSProperty_font_weight, empty);
05434         AppendValue(eCSSProperty_font_size, empty);
05435         AppendValue(eCSSProperty_line_height, empty);
05436         AppendValue(eCSSProperty_font_stretch, empty);
05437         AppendValue(eCSSProperty_font_size_adjust, empty);
05438       }
05439       return PR_TRUE;
05440     }
05441     return PR_FALSE;
05442   }
05443 
05444   // Get optional font-style, font-variant and font-weight (in any order)
05445   const PRInt32 numProps = 3;
05446   nsCSSValue  values[numProps];
05447   PRInt32 found = ParseChoice(aErrorCode, values, fontIDs, numProps);
05448   if ((found < 0) || (eCSSUnit_Inherit == values[0].GetUnit()) || 
05449       (eCSSUnit_Initial == values[0].GetUnit())) { // illegal data
05450     return PR_FALSE;
05451   }
05452   if ((found & 1) == 0) {
05453     // Provide default font-style
05454     values[0].SetNormalValue();
05455   }
05456   if ((found & 2) == 0) {
05457     // Provide default font-variant
05458     values[1].SetNormalValue();
05459   }
05460   if ((found & 4) == 0) {
05461     // Provide default font-weight
05462     values[2].SetNormalValue();
05463   }
05464 
05465   // Get mandatory font-size
05466   nsCSSValue  size;
05467   if (! ParseVariant(aErrorCode, size, VARIANT_KEYWORD | VARIANT_LP, nsCSSProps::kFontSizeKTable)) {
05468     return PR_FALSE;
05469   }
05470 
05471   // Get optional "/" line-height
05472   nsCSSValue  lineHeight;
05473   if (ExpectSymbol(aErrorCode, '/', PR_TRUE)) {
05474     if (! ParsePositiveVariant(aErrorCode, lineHeight,
05475                                VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL,
05476                                nsnull)) {
05477       return PR_FALSE;
05478     }
05479   }
05480   else {
05481     lineHeight.SetNormalValue();
05482   }
05483 
05484   // Get final mandatory font-family
05485   if (ParseFamily(aErrorCode, family)) {
05486     if ((eCSSUnit_Inherit != family.GetUnit()) && (eCSSUnit_Initial != family.GetUnit()) &&
05487         ExpectEndProperty(aErrorCode, PR_TRUE)) {
05488       AppendValue(eCSSProperty_font_family, family);
05489       AppendValue(eCSSProperty_font_style, values[0]);
05490       AppendValue(eCSSProperty_font_variant, values[1]);
05491       AppendValue(eCSSProperty_font_weight, values[2]);
05492       AppendValue(eCSSProperty_font_size, size);
05493       AppendValue(eCSSProperty_line_height, lineHeight);
05494       AppendValue(eCSSProperty_font_stretch, nsCSSValue(eCSSUnit_Normal));
05495       AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
05496       return PR_TRUE;
05497     }
05498   }
05499   return PR_FALSE;
05500 }
05501 
05502 PRBool CSSParserImpl::ParseFontWeight(nsresult& aErrorCode, nsCSSValue& aValue)
05503 {
05504   if (ParseVariant(aErrorCode, aValue, VARIANT_HMKI, nsCSSProps::kFontWeightKTable)) {
05505     if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
05506       PRInt32 intValue = aValue.GetIntValue();
05507       if ((100 <= intValue) &&
05508           (intValue <= 900) &&
05509           (0 == (intValue % 100))) {
05510         return PR_TRUE;
05511       } else {
05512         UngetToken();
05513         return PR_FALSE;
05514       }
05515     }
05516     return PR_TRUE;
05517   }
05518   return PR_FALSE;
05519 }
05520 
05521 PRBool CSSParserImpl::ParseFamily(nsresult& aErrorCode, nsCSSValue& aValue)
05522 {
05523   nsCSSToken* tk = &mToken;
05524   nsAutoString family;
05525   PRBool firstOne = PR_TRUE;
05526   for (;;) {
05527     if (!GetToken(aErrorCode, PR_TRUE)) {
05528       break;
05529     }
05530     if (eCSSToken_Ident == tk->mType) {
05531       if (firstOne) {
05532         nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
05533         if (keyword == eCSSKeyword_inherit) {
05534           aValue.SetInheritValue();
05535           return PR_TRUE;
05536         }
05537         else if (keyword == eCSSKeyword__moz_initial) {
05538           aValue.SetInitialValue();
05539           return PR_TRUE;
05540         }
05541       }
05542       else {
05543         family.Append(PRUnichar(','));
05544       }
05545       family.Append(tk->mIdent);
05546       for (;;) {
05547         if (!GetToken(aErrorCode, PR_FALSE)) {
05548           break;
05549         }
05550         if (eCSSToken_Ident == tk->mType) {
05551           family.Append(tk->mIdent);
05552         } else if (eCSSToken_WhiteSpace == tk->mType) {
05553           // Lookahead one token and drop whitespace if we ending the
05554           // font name.
05555           if (!GetToken(aErrorCode, PR_TRUE)) {
05556             break;
05557           }
05558           if (eCSSToken_Ident != tk->mType) {
05559             UngetToken();
05560             break;
05561           }
05562           UngetToken();
05563           family.Append(PRUnichar(' '));
05564         } else {
05565           UngetToken();
05566           break;
05567         }
05568       }
05569       firstOne = PR_FALSE;
05570     } else if (eCSSToken_String == tk->mType) {
05571       if (!firstOne) {
05572         family.Append(PRUnichar(','));
05573       }
05574       family.Append(tk->mSymbol); // replace the quotes
05575       family.Append(tk->mIdent); // XXX What if it had escaped quotes?
05576       family.Append(tk->mSymbol);
05577       firstOne = PR_FALSE;
05578     } else if (eCSSToken_Symbol == tk->mType) {
05579       if (',' != tk->mSymbol) {
05580         UngetToken();
05581         break;
05582       }
05583     } else {
05584       UngetToken();
05585       break;
05586     }
05587   }
05588   if (family.IsEmpty()) {
05589     return PR_FALSE;
05590   }
05591   aValue.SetStringValue(family, eCSSUnit_String);
05592   return PR_TRUE;
05593 }
05594 
05595 PRBool CSSParserImpl::ParseListStyle(nsresult& aErrorCode)
05596 {
05597   const PRInt32 numProps = 3;
05598   static const nsCSSProperty listStyleIDs[] = {
05599     eCSSProperty_list_style_type,
05600     eCSSProperty_list_style_position,
05601     eCSSProperty_list_style_image
05602   };
05603 
05604   nsCSSValue  values[numProps];
05605   PRInt32 index;
05606   PRInt32 found = ParseChoice(aErrorCode, values, listStyleIDs, numProps);
05607   if ((found < 1) || (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE))) {
05608     return PR_FALSE;
05609   }
05610 
05611   // Provide default values
05612   if ((found & 1) == 0) {
05613     values[0].SetIntValue(NS_STYLE_LIST_STYLE_DISC, eCSSUnit_Enumerated);
05614   }
05615   if ((found & 2) == 0) {
05616     values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE, eCSSUnit_Enumerated);
05617   }
05618   if ((found & 4) == 0) {
05619     values[2].SetNoneValue();
05620   }
05621 
05622   for (index = 0; index < numProps; index++) {
05623     AppendValue(listStyleIDs[index], values[index]);
05624   }
05625   return PR_TRUE;
05626 }
05627 
05628 PRBool CSSParserImpl::ParseMargin(nsresult& aErrorCode)
05629 {
05630   static const nsCSSProperty kMarginSideIDs[] = {
05631     eCSSProperty_margin_top,
05632     eCSSProperty_margin_right_value,
05633     eCSSProperty_margin_bottom,
05634     eCSSProperty_margin_left_value
05635   };
05636   // do this now, in case 4 values weren't specified
05637   mTempData.SetPropertyBit(eCSSProperty_margin_left_ltr_source);
05638   mTempData.SetPropertyBit(eCSSProperty_margin_left_rtl_source);
05639   mTempData.SetPropertyBit(eCSSProperty_margin_right_ltr_source);
05640   mTempData.SetPropertyBit(eCSSProperty_margin_right_rtl_source);
05641   mTempData.mMargin.mMarginLeftLTRSource.SetIntValue(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
05642   mTempData.mMargin.mMarginLeftRTLSource.SetIntValue(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
05643   mTempData.mMargin.mMarginRightLTRSource.SetIntValue(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
05644   mTempData.mMargin.mMarginRightRTLSource.SetIntValue(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
05645   return ParseBoxProperties(aErrorCode, mTempData.mMargin.mMargin,
05646                             kMarginSideIDs);
05647 }
05648 
05649 PRBool CSSParserImpl::ParseMarks(nsresult& aErrorCode, nsCSSValue& aValue)
05650 {
05651   if (ParseVariant(aErrorCode, aValue, VARIANT_HOK, nsCSSProps::kPageMarksKTable)) {
05652     if (eCSSUnit_Enumerated == aValue.GetUnit()) {
05653       if (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE)) {
05654         nsCSSValue  second;
05655         if (ParseEnum(aErrorCode, second, nsCSSProps::kPageMarksKTable)) {
05656           aValue.SetIntValue(aValue.GetIntValue() | second.GetIntValue(), eCSSUnit_Enumerated);
05657           return PR_TRUE;
05658         }
05659         return PR_FALSE;
05660       }
05661     }
05662     return PR_TRUE;
05663   }
05664   return PR_FALSE;
05665 }
05666 
05667 PRBool CSSParserImpl::ParseOutline(nsresult& aErrorCode)
05668 {
05669   const PRInt32 numProps = 3;
05670   static const nsCSSProperty kOutlineIDs[] = {
05671     eCSSProperty_outline_color,
05672     eCSSProperty_outline_style,
05673     eCSSProperty_outline_width
05674   };
05675 
05676   nsCSSValue  values[numProps];
05677   PRInt32 found = ParseChoice(aErrorCode, values, kOutlineIDs, numProps);
05678   if ((found < 1) || (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE))) {
05679     return PR_FALSE;
05680   }
05681 
05682   // Provide default values
05683   if ((found & 1) == 0) {
05684     values[0].SetIntValue(NS_STYLE_COLOR_INVERT, eCSSUnit_Enumerated);
05685   }
05686   if ((found & 2) == 0) {
05687     values[1].SetNoneValue();
05688   }
05689   if ((found & 4) == 0) {
05690     values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
05691   }
05692 
05693   PRInt32 index;
05694   for (index = 0; index < numProps; index++) {
05695     AppendValue(kOutlineIDs[index], values[index]);
05696   }
05697   return PR_TRUE;
05698 }
05699 
05700 PRBool CSSParserImpl::ParseOverflow(nsresult& aErrorCode)
05701 {
05702   nsCSSValue overflow;
05703   if (!ParseVariant(aErrorCode, overflow, VARIANT_AHK,
05704                    nsCSSProps::kOverflowKTable) ||
05705       !ExpectEndProperty(aErrorCode, PR_TRUE))
05706     return PR_FALSE;
05707 
05708   nsCSSValue overflowX(overflow);
05709   nsCSSValue overflowY(overflow);
05710   if (eCSSUnit_Enumerated == overflow.GetUnit())
05711     switch(overflow.GetIntValue()) {
05712       case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
05713         overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
05714         overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
05715         break;
05716       case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
05717         overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
05718         overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
05719         break;
05720     }
05721   AppendValue(eCSSProperty_overflow_x, overflowX);
05722   AppendValue(eCSSProperty_overflow_y, overflowY);
05723   aErrorCode = NS_OK;
05724   return PR_TRUE;
05725 }
05726 
05727 PRBool CSSParserImpl::ParsePadding(nsresult& aErrorCode)
05728 {
05729   static const nsCSSProperty kPaddingSideIDs[] = {
05730     eCSSProperty_padding_top,
05731     eCSSProperty_padding_right_value,
05732     eCSSProperty_padding_bottom,
05733     eCSSProperty_padding_left_value
05734   };
05735   // do this now, in case 4 values weren't specified
05736   mTempData.SetPropertyBit(eCSSProperty_padding_left_ltr_source);
05737   mTempData.SetPropertyBit(eCSSProperty_padding_left_rtl_source);
05738   mTempData.SetPropertyBit(eCSSProperty_padding_right_ltr_source);
05739   mTempData.SetPropertyBit(eCSSProperty_padding_right_rtl_source);
05740   mTempData.mMargin.mPaddingLeftLTRSource.SetIntValue(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
05741   mTempData.mMargin.mPaddingLeftRTLSource.SetIntValue(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
05742   mTempData.mMargin.mPaddingRightLTRSource.SetIntValue(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
05743   mTempData.mMargin.mPaddingRightRTLSource.SetIntValue(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
05744   return ParseBoxProperties(aErrorCode, mTempData.mMargin.mPadding,
05745                             kPaddingSideIDs);
05746 }
05747 
05748 PRBool CSSParserImpl::ParsePause(nsresult& aErrorCode)
05749 {
05750   nsCSSValue  before;
05751   if (ParseSingleValueProperty(aErrorCode, before, eCSSProperty_pause_before)) {
05752     if (eCSSUnit_Inherit != before.GetUnit() && eCSSUnit_Initial != before.GetUnit()) {
05753       nsCSSValue after;
05754       if (ParseSingleValueProperty(aErrorCode, after, eCSSProperty_pause_after)) {
05755         if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05756           AppendValue(eCSSProperty_pause_before, before);
05757           AppendValue(eCSSProperty_pause_after, after);
05758           return PR_TRUE;
05759         }
05760         return PR_FALSE;
05761       }
05762     }
05763     if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05764       AppendValue(eCSSProperty_pause_before, before);
05765       AppendValue(eCSSProperty_pause_after, before);
05766       return PR_TRUE;
05767     }
05768   }
05769   return PR_FALSE;
05770 }
05771 
05772 PRBool CSSParserImpl::ParseQuotes(nsresult& aErrorCode)
05773 {
05774   nsCSSValue  open;
05775   if (ParseVariant(aErrorCode, open, VARIANT_HOS, nsnull)) {
05776     if (eCSSUnit_String == open.GetUnit()) {
05777       nsCSSQuotes* quotesHead = new nsCSSQuotes();
05778       nsCSSQuotes* quotes = quotesHead;
05779       if (nsnull == quotes) {
05780         aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05781         return PR_FALSE;
05782       }
05783       quotes->mOpen = open;
05784       while (nsnull != quotes) {
05785         // get mandatory close
05786         if (ParseVariant(aErrorCode, quotes->mClose, VARIANT_STRING, nsnull)) {
05787           if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05788             mTempData.SetPropertyBit(eCSSProperty_quotes);
05789             mTempData.mContent.mQuotes = quotesHead;
05790             aErrorCode = NS_OK;
05791             return PR_TRUE;
05792           }
05793           // look for another open
05794           if (ParseVariant(aErrorCode, open, VARIANT_STRING, nsnull)) {
05795             quotes->mNext = new nsCSSQuotes();
05796             quotes = quotes->mNext;
05797             if (nsnull != quotes) {
05798               quotes->mOpen = open;
05799               continue;
05800             }
05801             aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05802           }
05803         }
05804         break;
05805       }
05806       delete quotesHead;
05807       return PR_FALSE;
05808     }
05809     if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05810       nsCSSQuotes* quotesHead = new nsCSSQuotes();
05811       quotesHead->mOpen = open;
05812       mTempData.mContent.mQuotes = quotesHead;
05813       mTempData.SetPropertyBit(eCSSProperty_quotes);
05814       return PR_TRUE;
05815     }
05816   }
05817   return PR_FALSE;
05818 }
05819 
05820 PRBool CSSParserImpl::ParseSize(nsresult& aErrorCode)
05821 {
05822   nsCSSValue width;
05823   if (ParseVariant(aErrorCode, width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) {
05824     if (width.IsLengthUnit()) {
05825       nsCSSValue  height;
05826       if (ParseVariant(aErrorCode, height, VARIANT_LENGTH, nsnull)) {
05827         if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05828           mTempData.mPage.mSize.mXValue = width;
05829           mTempData.mPage.mSize.mYValue = height;
05830           mTempData.SetPropertyBit(eCSSProperty_size);
05831           return PR_TRUE;
05832         }
05833         return PR_FALSE;
05834       }
05835     }
05836     if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05837       mTempData.mPage.mSize.SetBothValuesTo(width);
05838       mTempData.SetPropertyBit(eCSSProperty_size);
05839       return PR_TRUE;
05840     }
05841   }
05842   return PR_FALSE;
05843 }
05844 
05845 PRBool CSSParserImpl::ParseTextDecoration(nsresult& aErrorCode, nsCSSValue& aValue)
05846 {
05847   if (ParseVariant(aErrorCode, aValue, VARIANT_HOK, nsCSSProps::kTextDecorationKTable)) {
05848     if (eCSSUnit_Enumerated == aValue.GetUnit()) {  // look for more keywords
05849       PRInt32 intValue = aValue.GetIntValue();
05850       nsCSSValue  keyword;
05851       PRInt32 index;
05852       for (index = 0; index < 3; index++) {
05853         if (ParseEnum(aErrorCode, keyword, nsCSSProps::kTextDecorationKTable)) {
05854           intValue |= keyword.GetIntValue();
05855         }
05856         else {
05857           break;
05858         }
05859       }
05860       aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
05861     }
05862     return PR_TRUE;
05863   }
05864   return PR_FALSE;
05865 }
05866 
05867 PRBool CSSParserImpl::ParseTextShadow(nsresult& aErrorCode)
05868 {
05869   nsCSSValue  value;
05870   if (ParseVariant(aErrorCode, value, VARIANT_HC | VARIANT_LENGTH | VARIANT_NONE, nsnull)) {
05871     nsCSSUnit unit = value.GetUnit();
05872     if ((eCSSUnit_Color == unit) || (eCSSUnit_String == unit) || value.IsLengthUnit()) {
05873       nsCSSShadow*  shadowHead = new nsCSSShadow();
05874       nsCSSShadow*  shadow = shadowHead;
05875       if (nsnull == shadow) {
05876         aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05877         return PR_FALSE;
05878       }
05879       while (nsnull != shadow) {
05880         PRBool  haveColor = PR_FALSE;
05881         if (value.IsLengthUnit()) {
05882           shadow->mXOffset = value;
05883         }
05884         else {
05885           haveColor = PR_TRUE;
05886           shadow->mColor = value;
05887           if (ParseVariant(aErrorCode, value, VARIANT_LENGTH, nsnull)) {
05888             shadow->mXOffset = value;
05889           }
05890           else {
05891             break;
05892           }
05893         }
05894         if (ParseVariant(aErrorCode, value, VARIANT_LENGTH, nsnull)) {
05895           shadow->mYOffset = value;
05896         }
05897         else {
05898           break;
05899         }
05900         if (ParseVariant(aErrorCode, value, VARIANT_LENGTH, nsnull)) {
05901           shadow->mRadius = value;
05902         } // optional
05903         if (PR_FALSE == haveColor) {
05904           if (ParseVariant(aErrorCode, value, VARIANT_COLOR, nsnull)) {
05905             shadow->mColor = value;
05906           }
05907         }
05908         if (ExpectSymbol(aErrorCode, ',', PR_TRUE)) {
05909           shadow->mNext = new nsCSSShadow();
05910           shadow = shadow->mNext;
05911           if (nsnull == shadow) {
05912             aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05913             break;
05914           }
05915           if (PR_FALSE == ParseVariant(aErrorCode, value, VARIANT_COLOR | VARIANT_LENGTH, nsnull)) {
05916             break;
05917           }
05918         }
05919         else {
05920           if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05921             mTempData.SetPropertyBit(eCSSProperty_text_shadow);
05922             mTempData.mText.mTextShadow = shadowHead;
05923             aErrorCode = NS_OK;
05924             return PR_TRUE;
05925           }
05926           break;
05927         }
05928       }
05929       delete shadowHead;
05930       return PR_FALSE;
05931     }
05932     // value is inherit or none
05933     if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05934       nsCSSShadow* shadowHead = new nsCSSShadow();
05935       shadowHead->mXOffset = value;
05936       mTempData.SetPropertyBit(eCSSProperty_text_shadow);
05937       mTempData.mText.mTextShadow = shadowHead;
05938       return PR_TRUE;
05939     }
05940   }
05941   return PR_FALSE;
05942 }
05943 
05944 #ifdef MOZ_SVG
05945 PRBool CSSParserImpl::ParseDasharray(nsresult& aErrorCode)
05946 {
05947   nsCSSValue value;
05948   if (ParseVariant(aErrorCode, value, VARIANT_HLPN | VARIANT_NONE, nsnull)) {
05949     nsCSSValueList *listHead = new nsCSSValueList;
05950     nsCSSValueList *list = listHead;
05951     if (!list) {
05952       aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05953       return PR_FALSE;
05954     }
05955 
05956     list->mValue = value;
05957 
05958     for (;;) {
05959       if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05960         mTempData.SetPropertyBit(eCSSProperty_stroke_dasharray);
05961         mTempData.mSVG.mStrokeDasharray = listHead;
05962         aErrorCode = NS_OK;
05963         return PR_TRUE;
05964       }
05965 
05966       if (eCSSUnit_Inherit == value.GetUnit() ||
05967           eCSSUnit_Initial == value.GetUnit() ||
05968           eCSSUnit_None    == value.GetUnit())
05969         break;
05970 
05971       if (!ExpectSymbol(aErrorCode, ',', PR_TRUE))
05972         break;
05973 
05974       if (!ParseVariant(aErrorCode, value,
05975                         VARIANT_LENGTH | VARIANT_PERCENT | VARIANT_NUMBER,
05976                         nsnull))
05977         break;
05978 
05979       list->mNext = new nsCSSValueList;
05980       list = list->mNext;
05981       if (list)
05982         list->mValue = value;
05983       else {
05984         aErrorCode = NS_ERROR_OUT_OF_MEMORY;
05985         break;
05986       }
05987     }
05988     delete listHead;
05989   }
05990   return PR_FALSE;
05991 }
05992 
05993 PRBool CSSParserImpl::ParseMarker(nsresult& aErrorCode)
05994 {
05995   nsCSSValue marker;
05996   if (ParseSingleValueProperty(aErrorCode, marker, eCSSProperty_marker_end)) {
05997     if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
05998       AppendValue(eCSSProperty_marker_end, marker);
05999       AppendValue(eCSSProperty_marker_mid, marker);
06000       AppendValue(eCSSProperty_marker_start, marker);
06001       aErrorCode = NS_OK;
06002       return PR_TRUE;
06003     }
06004   }
06005   return PR_FALSE;
06006 }
06007 #endif