Bug 458861. Validate TrueType headers before activating downloaded font. r=roc, sr...
[wine-gecko.git] / layout / style / nsCSSParser.cpp
bloba1074e567e54c584ecd67e3b362119e2817aa52e
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * emk <VYV03354@nifty.ne.jp>
24 * Daniel Glazman <glazman@netscape.com>
25 * L. David Baron <dbaron@dbaron.org>
26 * Boris Zbarsky <bzbarsky@mit.edu>
27 * Mats Palmgren <mats.palmgren@bredband.net>
28 * Christian Biesinger <cbiesinger@web.de>
30 * Alternatively, the contents of this file may be used under the terms of
31 * either of the GNU General Public License Version 2 or later (the "GPL"),
32 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 * in which case the provisions of the GPL or the LGPL are applicable instead
34 * of those above. If you wish to allow use of your version of this file only
35 * under the terms of either the GPL or the LGPL, and not to allow others to
36 * use your version of this file under the terms of the MPL, indicate your
37 * decision by deleting the provisions above and replace them with the notice
38 * and other provisions required by the GPL or the LGPL. If you do not delete
39 * the provisions above, a recipient may use your version of this file under
40 * the terms of any one of the MPL, the GPL or the LGPL.
42 * ***** END LICENSE BLOCK ***** */
44 /* parsing of CSS stylesheets, based on a token stream from the CSS scanner */
46 #include "nsICSSParser.h"
47 #include "nsCSSProps.h"
48 #include "nsCSSKeywords.h"
49 #include "nsCSSScanner.h"
50 #include "nsICSSLoader.h"
51 #include "nsICSSStyleRule.h"
52 #include "nsICSSImportRule.h"
53 #include "nsCSSRules.h"
54 #include "nsICSSNameSpaceRule.h"
55 #include "nsIUnicharInputStream.h"
56 #include "nsICSSStyleSheet.h"
57 #include "nsCSSDeclaration.h"
58 #include "nsStyleConsts.h"
59 #include "nsIURL.h"
60 #include "nsNetUtil.h"
61 #include "nsCOMPtr.h"
62 #include "nsString.h"
63 #include "nsReadableUtils.h"
64 #include "nsUnicharUtils.h"
65 #include "nsIAtom.h"
66 #include "nsVoidArray.h"
67 #include "nsCOMArray.h"
68 #include "nsColor.h"
69 #include "nsStyleConsts.h"
70 #include "nsCSSPseudoClasses.h"
71 #include "nsCSSPseudoElements.h"
72 #include "nsCSSAnonBoxes.h"
73 #include "nsINameSpaceManager.h"
74 #include "nsXMLNameSpaceMap.h"
75 #include "nsThemeConstants.h"
76 #include "nsContentErrors.h"
77 #include "nsPrintfCString.h"
78 #include "nsIMediaList.h"
79 #include "nsILookAndFeel.h"
80 #include "nsStyleUtil.h"
81 #include "nsIPrincipal.h"
82 #include "prprf.h"
83 #include "math.h"
84 #include "nsContentUtils.h"
85 #include "nsDOMError.h"
86 #include "nsAutoPtr.h"
87 #include "nsTArray.h"
89 // Flags for ParseVariant method
90 #define VARIANT_KEYWORD 0x000001 // K
91 #define VARIANT_LENGTH 0x000002 // L
92 #define VARIANT_PERCENT 0x000004 // P
93 #define VARIANT_COLOR 0x000008 // C eCSSUnit_Color, eCSSUnit_String (e.g. "red")
94 #define VARIANT_URL 0x000010 // U
95 #define VARIANT_NUMBER 0x000020 // N
96 #define VARIANT_INTEGER 0x000040 // I
97 #define VARIANT_ANGLE 0x000080 // G
98 #define VARIANT_FREQUENCY 0x000100 // F
99 #define VARIANT_TIME 0x000200 // T
100 #define VARIANT_STRING 0x000400 // S
101 #define VARIANT_COUNTER 0x000800 //
102 #define VARIANT_ATTR 0x001000 //
103 #define VARIANT_IDENTIFIER 0x002000 // D
104 #define VARIANT_AUTO 0x010000 // A
105 #define VARIANT_INHERIT 0x020000 // H eCSSUnit_Initial, eCSSUnit_Inherit
106 #define VARIANT_NONE 0x040000 // O
107 #define VARIANT_NORMAL 0x080000 // M
108 #define VARIANT_SYSFONT 0x100000 // eCSSUnit_System_Font
110 // Common combinations of variants
111 #define VARIANT_AL (VARIANT_AUTO | VARIANT_LENGTH)
112 #define VARIANT_LP (VARIANT_LENGTH | VARIANT_PERCENT)
113 #define VARIANT_AH (VARIANT_AUTO | VARIANT_INHERIT)
114 #define VARIANT_AHLP (VARIANT_AH | VARIANT_LP)
115 #define VARIANT_AHI (VARIANT_AH | VARIANT_INTEGER)
116 #define VARIANT_AHK (VARIANT_AH | VARIANT_KEYWORD)
117 #define VARIANT_AHKLP (VARIANT_AHLP | VARIANT_KEYWORD)
118 #define VARIANT_AUK (VARIANT_AUTO | VARIANT_URL | VARIANT_KEYWORD)
119 #define VARIANT_AHUK (VARIANT_AH | VARIANT_URL | VARIANT_KEYWORD)
120 #define VARIANT_AHL (VARIANT_AH | VARIANT_LENGTH)
121 #define VARIANT_AHKL (VARIANT_AHK | VARIANT_LENGTH)
122 #define VARIANT_HK (VARIANT_INHERIT | VARIANT_KEYWORD)
123 #define VARIANT_HKF (VARIANT_HK | VARIANT_FREQUENCY)
124 #define VARIANT_HKL (VARIANT_HK | VARIANT_LENGTH)
125 #define VARIANT_HKLP (VARIANT_HK | VARIANT_LP)
126 #define VARIANT_HKLPO (VARIANT_HKLP | VARIANT_NONE)
127 #define VARIANT_HL (VARIANT_INHERIT | VARIANT_LENGTH)
128 #define VARIANT_HI (VARIANT_INHERIT | VARIANT_INTEGER)
129 #define VARIANT_HLP (VARIANT_HL | VARIANT_PERCENT)
130 #define VARIANT_HLPN (VARIANT_HLP | VARIANT_NUMBER)
131 #define VARIANT_HLPO (VARIANT_HLP | VARIANT_NONE)
132 #define VARIANT_HTP (VARIANT_INHERIT | VARIANT_TIME | VARIANT_PERCENT)
133 #define VARIANT_HMK (VARIANT_HK | VARIANT_NORMAL)
134 #define VARIANT_HMKI (VARIANT_HMK | VARIANT_INTEGER)
135 #define VARIANT_HC (VARIANT_INHERIT | VARIANT_COLOR)
136 #define VARIANT_HCK (VARIANT_HK | VARIANT_COLOR)
137 #define VARIANT_HUO (VARIANT_INHERIT | VARIANT_URL | VARIANT_NONE)
138 #define VARIANT_AHUO (VARIANT_AUTO | VARIANT_HUO)
139 #define VARIANT_HPN (VARIANT_INHERIT | VARIANT_PERCENT | VARIANT_NUMBER)
140 #define VARIANT_HOK (VARIANT_HK | VARIANT_NONE)
141 #define VARIANT_HN (VARIANT_INHERIT | VARIANT_NUMBER)
142 #define VARIANT_HON (VARIANT_HN | VARIANT_NONE)
143 #define VARIANT_HOS (VARIANT_INHERIT | VARIANT_NONE | VARIANT_STRING)
145 //----------------------------------------------------------------------
147 // Your basic top-down recursive descent style parser
148 class CSSParserImpl : public nsICSSParser {
149 public:
150 CSSParserImpl();
151 virtual ~CSSParserImpl();
153 NS_DECL_ISUPPORTS
155 NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet);
157 NS_IMETHOD SetCaseSensitive(PRBool aCaseSensitive);
159 NS_IMETHOD SetQuirkMode(PRBool aQuirkMode);
161 #ifdef MOZ_SVG
162 NS_IMETHOD SetSVGMode(PRBool aSVGMode);
163 #endif
165 NS_IMETHOD SetChildLoader(nsICSSLoader* aChildLoader);
167 NS_IMETHOD Parse(nsIUnicharInputStream* aInput,
168 nsIURI* aSheetURI,
169 nsIURI* aBaseURI,
170 nsIPrincipal* aSheetPrincipal,
171 PRUint32 aLineNumber,
172 PRBool aAllowUnsafeRules);
174 NS_IMETHOD ParseStyleAttribute(const nsAString& aAttributeValue,
175 nsIURI* aDocURL,
176 nsIURI* aBaseURL,
177 nsIPrincipal* aNodePrincipal,
178 nsICSSStyleRule** aResult);
180 NS_IMETHOD ParseAndAppendDeclaration(const nsAString& aBuffer,
181 nsIURI* aSheetURL,
182 nsIURI* aBaseURL,
183 nsIPrincipal* aSheetPrincipal,
184 nsCSSDeclaration* aDeclaration,
185 PRBool aParseOnlyOneDecl,
186 PRBool* aChanged,
187 PRBool aClearOldDecl);
189 NS_IMETHOD ParseRule(const nsAString& aRule,
190 nsIURI* aSheetURL,
191 nsIURI* aBaseURL,
192 nsIPrincipal* aSheetPrincipal,
193 nsCOMArray<nsICSSRule>& aResult);
195 NS_IMETHOD ParseProperty(const nsCSSProperty aPropID,
196 const nsAString& aPropValue,
197 nsIURI* aSheetURL,
198 nsIURI* aBaseURL,
199 nsIPrincipal* aSheetPrincipal,
200 nsCSSDeclaration* aDeclaration,
201 PRBool* aChanged);
203 NS_IMETHOD ParseMediaList(const nsSubstring& aBuffer,
204 nsIURI* aURL, // for error reporting
205 PRUint32 aLineNumber, // for error reporting
206 nsMediaList* aMediaList,
207 PRBool aHTMLMode);
209 NS_IMETHOD ParseColorString(const nsSubstring& aBuffer,
210 nsIURI* aURL, // for error reporting
211 PRUint32 aLineNumber, // for error reporting
212 nscolor* aColor);
214 NS_IMETHOD ParseSelectorString(const nsSubstring& aSelectorString,
215 nsIURI* aURL, // for error reporting
216 PRUint32 aLineNumber, // for error reporting
217 nsCSSSelectorList **aSelectorList);
219 void AppendRule(nsICSSRule* aRule);
221 protected:
222 class nsAutoParseCompoundProperty;
223 friend class nsAutoParseCompoundProperty;
226 * This helper class automatically calls SetParsingCompoundProperty in its
227 * constructor and takes care of resetting it to false in its destructor.
229 class nsAutoParseCompoundProperty {
230 public:
231 nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
233 NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
234 "already parsing compound property");
235 NS_ASSERTION(aParser, "Null parser?");
236 aParser->SetParsingCompoundProperty(PR_TRUE);
239 ~nsAutoParseCompoundProperty()
241 mParser->SetParsingCompoundProperty(PR_FALSE);
243 private:
244 CSSParserImpl* mParser;
247 void InitScanner(nsIUnicharInputStream* aInput, nsIURI* aSheetURI,
248 PRUint32 aLineNumber, nsIURI* aBaseURI,
249 nsIPrincipal* aSheetPrincipal);
250 // the caller must hold on to aBuffer until parsing is done
251 void InitScanner(const nsSubstring& aString, nsIURI* aSheetURI,
252 PRUint32 aLineNumber, nsIURI* aBaseURI,
253 nsIPrincipal* aSheetPrincipal);
254 void ReleaseScanner(void);
255 #ifdef MOZ_SVG
256 PRBool IsSVGMode() const {
257 return mScanner.IsSVGMode();
259 #endif
261 PRBool GetToken(PRBool aSkipWS);
262 PRBool GetURLToken();
263 void UngetToken();
265 void AssertInitialState() {
266 NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state");
267 NS_PRECONDITION(!mUnresolvablePrefixException, "Bad initial state");
268 NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
271 PRBool ExpectSymbol(PRUnichar aSymbol, PRBool aSkipWS);
272 PRBool ExpectEndProperty();
273 PRBool CheckEndProperty();
274 nsSubstring* NextIdent();
275 void SkipUntil(PRUnichar aStopSymbol);
276 void SkipRuleSet();
277 PRBool SkipAtRule();
278 PRBool SkipDeclaration(PRBool aCheckForBraces);
279 PRBool GetNonCloseParenToken(PRBool aSkipWS);
281 PRBool PushGroup(nsICSSGroupRule* aRule);
282 void PopGroup(void);
284 PRBool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData);
285 PRBool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData);
286 PRBool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData);
287 PRBool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData);
288 PRBool GatherURL(nsString& aURL);
289 PRBool GatherMedia(nsMediaList* aMedia,
290 PRUnichar aStopSymbol);
291 PRBool ParseMediaQuery(PRUnichar aStopSymbol, nsMediaQuery **aQuery,
292 PRBool *aParsedSomething, PRBool *aHitStop);
293 PRBool ParseMediaQueryExpression(nsMediaQuery* aQuery);
294 PRBool ProcessImport(const nsString& aURLSpec,
295 nsMediaList* aMedia,
296 RuleAppendFunc aAppendFunc,
297 void* aProcessData);
298 PRBool ParseGroupRule(nsICSSGroupRule* aRule, RuleAppendFunc aAppendFunc,
299 void* aProcessData);
300 PRBool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData);
301 PRBool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData);
302 PRBool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
303 PRBool ProcessNameSpace(const nsString& aPrefix,
304 const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
305 void* aProcessData);
307 PRBool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
308 PRBool ParseFontDescriptor(nsCSSFontFaceRule* aRule);
309 PRBool ParseFontDescriptorValue(nsCSSFontDesc aDescID,
310 nsCSSValue& aValue);
312 PRBool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData);
314 enum nsSelectorParsingStatus {
315 // we have parsed a selector and we saw a token that cannot be
316 // part of a selector:
317 eSelectorParsingStatus_Done,
318 // we should continue parsing the selector:
319 eSelectorParsingStatus_Continue,
320 // same as "Done" but we did not find a selector:
321 eSelectorParsingStatus_Empty,
322 // we saw an unexpected token or token value,
323 // or we saw end-of-file with an unfinished selector:
324 eSelectorParsingStatus_Error
326 nsSelectorParsingStatus ParseIDSelector(PRInt32& aDataMask,
327 nsCSSSelector& aSelector);
329 nsSelectorParsingStatus ParseClassSelector(PRInt32& aDataMask,
330 nsCSSSelector& aSelector);
332 nsSelectorParsingStatus ParsePseudoSelector(PRInt32& aDataMask,
333 nsCSSSelector& aSelector,
334 PRBool aIsNegated);
336 nsSelectorParsingStatus ParseAttributeSelector(PRInt32& aDataMask,
337 nsCSSSelector& aSelector);
339 nsSelectorParsingStatus ParseTypeOrUniversalSelector(PRInt32& aDataMask,
340 nsCSSSelector& aSelector,
341 PRBool aIsNegated);
343 nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
344 nsIAtom* aPseudo);
346 nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
347 nsIAtom* aPseudo);
349 nsSelectorParsingStatus ParseNegatedSimpleSelector(PRInt32& aDataMask,
350 nsCSSSelector& aSelector);
352 nsSelectorParsingStatus ParseSelector(nsCSSSelector& aSelectorResult);
354 // If aTerminateAtBrace is true, the selector list is done when we
355 // hit a '{'. Otherwise, it's done when we hit EOF.
356 PRBool ParseSelectorList(nsCSSSelectorList*& aListHead,
357 PRBool aTerminateAtBrace);
358 PRBool ParseSelectorGroup(nsCSSSelectorList*& aListHead);
359 nsCSSDeclaration* ParseDeclarationBlock(PRBool aCheckForBraces);
360 PRBool ParseDeclaration(nsCSSDeclaration* aDeclaration,
361 PRBool aCheckForBraces,
362 PRBool aMustCallValueAppended,
363 PRBool* aChanged);
364 // After a parse error parsing |aPropID|, clear the data in
365 // |mTempData|.
366 void ClearTempData(nsCSSProperty aPropID);
367 // After a successful parse of |aPropID|, transfer data from
368 // |mTempData| to |mData|. Set |*aChanged| to true if something
369 // changed, but leave it unmodified otherwise. If aMustCallValueAppended
370 // is false, will not call ValueAppended on aDeclaration if the property
371 // is already set in it.
372 void TransferTempData(nsCSSDeclaration* aDeclaration,
373 nsCSSProperty aPropID, PRBool aIsImportant,
374 PRBool aMustCallValueAppended,
375 PRBool* aChanged);
376 void DoTransferTempData(nsCSSDeclaration* aDeclaration,
377 nsCSSProperty aPropID, PRBool aIsImportant,
378 PRBool aMustCallValueAppended,
379 PRBool* aChanged);
380 PRBool ParseProperty(nsCSSProperty aPropID);
381 PRBool ParseSingleValueProperty(nsCSSValue& aValue,
382 nsCSSProperty aPropID);
384 #ifdef MOZ_XUL
385 PRBool ParseTreePseudoElement(nsCSSSelector& aSelector);
386 #endif
388 void InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties);
390 // Property specific parsing routines
391 PRBool ParseAzimuth(nsCSSValue& aValue);
392 PRBool ParseBackground();
393 PRBool ParseBackgroundPosition();
394 PRBool ParseBackgroundPositionValues();
395 PRBool ParseBoxPosition(nsCSSValuePair& aOut);
396 PRBool ParseBoxPositionValues(nsCSSValuePair& aOut);
397 PRBool ParseBorderColor();
398 PRBool ParseBorderColors(nsCSSValueList** aResult,
399 nsCSSProperty aProperty);
400 PRBool ParseBorderImage();
401 PRBool ParseBorderSpacing();
402 PRBool ParseBorderSide(const nsCSSProperty aPropIDs[],
403 PRBool aSetAllSides);
404 PRBool ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
405 PRInt32 aSourceType);
406 PRBool ParseBorderStyle();
407 PRBool ParseBorderWidth();
408 // for 'clip' and '-moz-image-region'
409 PRBool ParseRect(nsCSSRect& aRect,
410 nsCSSProperty aPropID);
411 PRBool DoParseRect(nsCSSRect& aRect);
412 PRBool ParseContent();
413 PRBool ParseCounterData(nsCSSValuePairList** aResult,
414 nsCSSProperty aPropID);
415 PRBool ParseCue();
416 PRBool ParseCursor();
417 PRBool ParseFont();
418 PRBool ParseFontWeight(nsCSSValue& aValue);
419 PRBool ParseOneFamily(nsAString& aValue);
420 PRBool ParseFamily(nsCSSValue& aValue);
421 PRBool ParseFontSrc(nsCSSValue& aValue);
422 PRBool ParseFontSrcFormat(nsTArray<nsCSSValue>& values);
423 PRBool ParseFontRanges(nsCSSValue& aValue);
424 PRBool ParseListStyle();
425 PRBool ParseMargin();
426 PRBool ParseMarks(nsCSSValue& aValue);
427 PRBool ParseMozTransform();
428 PRBool ParseOutline();
429 PRBool ParseOverflow();
430 PRBool ParsePadding();
431 PRBool ParsePause();
432 PRBool ParseQuotes();
433 PRBool ParseSize();
434 PRBool ParseTextDecoration(nsCSSValue& aValue);
436 nsCSSValueList* ParseCSSShadowList(PRBool aUsesSpread);
437 PRBool ParseTextShadow();
438 PRBool ParseBoxShadow();
440 #ifdef MOZ_SVG
441 PRBool ParsePaint(nsCSSValuePair* aResult,
442 nsCSSProperty aPropID);
443 PRBool ParseDasharray();
444 PRBool ParseMarker();
445 #endif
447 // Reused utility parsing routines
448 void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue);
449 PRBool ParseBoxProperties(nsCSSRect& aResult,
450 const nsCSSProperty aPropIDs[]);
451 PRBool ParseDirectionalBoxProperty(nsCSSProperty aProperty,
452 PRInt32 aSourceType);
453 PRBool ParseBoxCornerRadius(const nsCSSProperty aPropID);
454 PRBool ParseBoxCornerRadii(nsCSSCornerSizes& aRadii,
455 const nsCSSProperty aPropIDs[]);
456 PRInt32 ParseChoice(nsCSSValue aValues[],
457 const nsCSSProperty aPropIDs[], PRInt32 aNumIDs);
458 PRBool ParseColor(nsCSSValue& aValue);
459 PRBool ParseColorComponent(PRUint8& aComponent,
460 PRInt32& aType, char aStop);
461 // ParseHSLColor parses everything starting with the opening '('
462 // up through and including the aStop char.
463 PRBool ParseHSLColor(nscolor& aColor, char aStop);
464 // ParseColorOpacity will enforce that the color ends with a ')'
465 // after the opacity
466 PRBool ParseColorOpacity(PRUint8& aOpacity);
467 PRBool ParseEnum(nsCSSValue& aValue, const PRInt32 aKeywordTable[]);
468 PRBool ParseVariant(nsCSSValue& aValue,
469 PRInt32 aVariantMask,
470 const PRInt32 aKeywordTable[]);
471 PRBool ParsePositiveVariant(nsCSSValue& aValue,
472 PRInt32 aVariantMask,
473 const PRInt32 aKeywordTable[]);
474 PRBool ParseCounter(nsCSSValue& aValue);
475 PRBool ParseAttr(nsCSSValue& aValue);
476 PRBool ParseURL(nsCSSValue& aValue);
477 PRBool TranslateDimension(nsCSSValue& aValue, PRInt32 aVariantMask,
478 float aNumber, const nsString& aUnit);
480 void SetParsingCompoundProperty(PRBool aBool) {
481 NS_ASSERTION(aBool == PR_TRUE || aBool == PR_FALSE, "bad PRBool value");
482 mParsingCompoundProperty = aBool;
484 PRBool IsParsingCompoundProperty(void) const {
485 return mParsingCompoundProperty;
488 /* Functions for -moz-transform Parsing */
489 PRBool ReadSingleTransform(nsCSSValueList**& aTail);
490 PRBool ParseFunction(const nsString &aFunction, const PRInt32 aAllowedTypes[],
491 PRUint16 aMinElems, PRUint16 aMaxElems,
492 nsCSSValue &aValue);
493 PRBool ParseFunctionInternals(const PRInt32 aVariantMask[],
494 PRUint16 aMinElems,
495 PRUint16 aMaxElems,
496 nsTArray<nsCSSValue>& aOutput);
498 /* Functions for -moz-transform-origin Parsing */
499 PRBool ParseMozTransformOrigin();
502 /* Find and return the correct namespace ID for the prefix aPrefix.
503 If the prefix cannot be resolved to a namespace, this method will
504 return false. Otherwise it will return true. When returning
505 false, it may set the low-level error code, depending on the
506 value of mUnresolvablePrefixException.
508 This method never returns kNameSpaceID_Unknown or
509 kNameSpaceID_None for aNameSpaceID while returning true.
511 PRBool GetNamespaceIdForPrefix(const nsString& aPrefix,
512 PRInt32* aNameSpaceID);
514 /* Find the correct default namespace, and set it on aSelector. */
515 void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
517 // Current token. The value is valid after calling GetToken and invalidated
518 // by UngetToken.
519 nsCSSToken mToken;
521 // Our scanner.
522 nsCSSScanner mScanner;
524 // The URI to be used as a base for relative URIs.
525 nsCOMPtr<nsIURI> mBaseURL;
527 // The URI to be used as an HTTP "Referer" and for error reporting.
528 nsCOMPtr<nsIURI> mSheetURL;
530 // The principal of the sheet involved
531 nsCOMPtr<nsIPrincipal> mSheetPrincipal;
533 // The sheet we're parsing into
534 nsCOMPtr<nsICSSStyleSheet> mSheet;
536 // Used for @import rules
537 nsICSSLoader* mChildLoader; // not ref counted, it owns us
539 // Sheet section we're in. This is used to enforce correct ordering of the
540 // various rule types (eg the fact that a @charset rule must come before
541 // anything else). Note that there are checks of similar things in various
542 // places in nsCSSStyleSheet.cpp (e.g in insertRule, RebuildChildList).
543 enum nsCSSSection {
544 eCSSSection_Charset,
545 eCSSSection_Import,
546 eCSSSection_NameSpace,
547 eCSSSection_General
549 nsCSSSection mSection;
551 nsXMLNameSpaceMap *mNameSpaceMap; // weak, mSheet owns it
553 // After an UngetToken is done this flag is true. The next call to
554 // GetToken clears the flag.
555 PRPackedBool mHavePushBack : 1;
557 // True if we are in quirks mode; false in standards or almost standards mode
558 PRPackedBool mNavQuirkMode : 1;
560 // True if unsafe rules should be allowed
561 PRPackedBool mUnsafeRulesEnabled : 1;
563 // True for parsing media lists for HTML attributes, where we have to
564 // ignore CSS comments.
565 PRPackedBool mHTMLMediaMode : 1;
567 // True if tagnames and attributes are case-sensitive
568 PRPackedBool mCaseSensitive : 1;
570 // This flag is set when parsing a non-box shorthand; it's used to not apply
571 // some quirks during shorthand parsing
572 PRPackedBool mParsingCompoundProperty : 1;
574 // If this flag is true, failure to resolve a namespace prefix
575 // should set the low-level error to NS_ERROR_DOM_NAMESPACE_ERR
576 PRPackedBool mUnresolvablePrefixException : 1;
578 // Stack of rule groups; used for @media and such.
579 nsCOMArray<nsICSSGroupRule> mGroupStack;
581 // During the parsing of a property (which may be a shorthand), the data
582 // are stored in |mTempData|. (It is needed to ensure that parser
583 // errors cause the data to be ignored, and to ensure that a
584 // non-'!important' declaration does not override an '!important'
585 // one.)
586 nsCSSExpandedDataBlock mTempData;
588 // All data from successfully parsed properties are placed into |mData|.
589 nsCSSExpandedDataBlock mData;
591 #ifdef DEBUG
592 PRPackedBool mScannerInited;
593 #endif
596 static void AppendRuleToArray(nsICSSRule* aRule, void* aArray)
598 static_cast<nsCOMArray<nsICSSRule>*>(aArray)->AppendObject(aRule);
601 static void AppendRuleToSheet(nsICSSRule* aRule, void* aParser)
603 CSSParserImpl* parser = (CSSParserImpl*) aParser;
604 parser->AppendRule(aRule);
607 nsresult
608 NS_NewCSSParser(nsICSSParser** aInstancePtrResult)
610 CSSParserImpl *it = new CSSParserImpl();
612 if (it == nsnull) {
613 return NS_ERROR_OUT_OF_MEMORY;
616 return it->QueryInterface(NS_GET_IID(nsICSSParser), (void **) aInstancePtrResult);
619 #ifdef CSS_REPORT_PARSE_ERRORS
621 #define REPORT_UNEXPECTED(msg_) \
622 mScanner.ReportUnexpected(#msg_)
624 #define REPORT_UNEXPECTED_P(msg_, params_) \
625 mScanner.ReportUnexpectedParams(#msg_, params_, NS_ARRAY_LENGTH(params_))
627 #define REPORT_UNEXPECTED_EOF(lf_) \
628 mScanner.ReportUnexpectedEOF(#lf_)
630 #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
631 mScanner.ReportUnexpectedEOF(ch_)
633 #define REPORT_UNEXPECTED_TOKEN(msg_) \
634 mScanner.ReportUnexpectedToken(mToken, #msg_)
636 #define REPORT_UNEXPECTED_TOKEN_P(msg_, params_) \
637 mScanner.ReportUnexpectedTokenParams(mToken, #msg_, \
638 params_, NS_ARRAY_LENGTH(params_))
641 #define OUTPUT_ERROR() \
642 mScanner.OutputError()
644 #define CLEAR_ERROR() \
645 mScanner.ClearError()
647 #else
649 #define REPORT_UNEXPECTED(msg_)
650 #define REPORT_UNEXPECTED_P(msg_, params_)
651 #define REPORT_UNEXPECTED_EOF(lf_)
652 #define REPORT_UNEXPECTED_EOF_CHAR(ch_)
653 #define REPORT_UNEXPECTED_TOKEN(msg_)
654 #define REPORT_UNEXPECTED_TOKEN_P(msg_, params_)
655 #define OUTPUT_ERROR()
656 #define CLEAR_ERROR()
658 #endif
660 CSSParserImpl::CSSParserImpl()
661 : mToken(),
662 mScanner(),
663 mChildLoader(nsnull),
664 mSection(eCSSSection_Charset),
665 mNameSpaceMap(nsnull),
666 mHavePushBack(PR_FALSE),
667 mNavQuirkMode(PR_FALSE),
668 mUnsafeRulesEnabled(PR_FALSE),
669 mHTMLMediaMode(PR_FALSE),
670 mCaseSensitive(PR_FALSE),
671 mParsingCompoundProperty(PR_FALSE),
672 mUnresolvablePrefixException(PR_FALSE)
673 #ifdef DEBUG
674 , mScannerInited(PR_FALSE)
675 #endif
679 NS_IMPL_ISUPPORTS1(CSSParserImpl, nsICSSParser)
681 CSSParserImpl::~CSSParserImpl()
683 mData.AssertInitialState();
684 mTempData.AssertInitialState();
687 NS_IMETHODIMP
688 CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet)
690 if (aSheet != mSheet) {
691 // Switch to using the new sheet, if any
692 mGroupStack.Clear();
693 mSheet = aSheet;
694 if (mSheet) {
695 mNameSpaceMap = mSheet->GetNameSpaceMap();
696 } else {
697 mNameSpaceMap = nsnull;
701 return NS_OK;
704 NS_IMETHODIMP
705 CSSParserImpl::SetCaseSensitive(PRBool aCaseSensitive)
707 NS_ASSERTION(aCaseSensitive == PR_TRUE || aCaseSensitive == PR_FALSE, "bad PRBool value");
708 mCaseSensitive = aCaseSensitive;
709 return NS_OK;
712 NS_IMETHODIMP
713 CSSParserImpl::SetQuirkMode(PRBool aQuirkMode)
715 NS_ASSERTION(aQuirkMode == PR_TRUE || aQuirkMode == PR_FALSE, "bad PRBool value");
716 mNavQuirkMode = aQuirkMode;
717 return NS_OK;
720 #ifdef MOZ_SVG
721 NS_IMETHODIMP
722 CSSParserImpl::SetSVGMode(PRBool aSVGMode)
724 NS_ASSERTION(aSVGMode == PR_TRUE || aSVGMode == PR_FALSE,
725 "bad PRBool value");
726 mScanner.SetSVGMode(aSVGMode);
727 return NS_OK;
729 #endif
731 NS_IMETHODIMP
732 CSSParserImpl::SetChildLoader(nsICSSLoader* aChildLoader)
734 mChildLoader = aChildLoader; // not ref counted, it owns us
735 return NS_OK;
738 void
739 CSSParserImpl::InitScanner(nsIUnicharInputStream* aInput, nsIURI* aSheetURI,
740 PRUint32 aLineNumber, nsIURI* aBaseURI,
741 nsIPrincipal* aSheetPrincipal)
743 NS_ASSERTION(! mScannerInited, "already have scanner");
745 mScanner.Init(aInput, nsnull, 0, aSheetURI, aLineNumber);
746 #ifdef DEBUG
747 mScannerInited = PR_TRUE;
748 #endif
749 mBaseURL = aBaseURI;
750 mSheetURL = aSheetURI;
751 mSheetPrincipal = aSheetPrincipal;
753 mHavePushBack = PR_FALSE;
756 void
757 CSSParserImpl::InitScanner(const nsSubstring& aString, nsIURI* aSheetURI,
758 PRUint32 aLineNumber, nsIURI* aBaseURI,
759 nsIPrincipal* aSheetPrincipal)
761 // Having it not own the string is OK since the caller will hold on to
762 // the stream until we're done parsing.
763 NS_ASSERTION(! mScannerInited, "already have scanner");
765 mScanner.Init(nsnull, aString.BeginReading(), aString.Length(), aSheetURI, aLineNumber);
767 #ifdef DEBUG
768 mScannerInited = PR_TRUE;
769 #endif
770 mBaseURL = aBaseURI;
771 mSheetURL = aSheetURI;
772 mSheetPrincipal = aSheetPrincipal;
774 mHavePushBack = PR_FALSE;
777 void
778 CSSParserImpl::ReleaseScanner(void)
780 mScanner.Close();
781 #ifdef DEBUG
782 mScannerInited = PR_FALSE;
783 #endif
784 mBaseURL = nsnull;
785 mSheetURL = nsnull;
786 mSheetPrincipal = nsnull;
790 NS_IMETHODIMP
791 CSSParserImpl::Parse(nsIUnicharInputStream* aInput,
792 nsIURI* aSheetURI,
793 nsIURI* aBaseURI,
794 nsIPrincipal* aSheetPrincipal,
795 PRUint32 aLineNumber,
796 PRBool aAllowUnsafeRules)
798 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
800 NS_ASSERTION(nsnull != aBaseURI, "need base URL");
801 NS_ASSERTION(nsnull != aSheetURI, "need sheet URL");
802 AssertInitialState();
804 NS_PRECONDITION(mSheet, "Must have sheet to parse into");
805 NS_ENSURE_STATE(mSheet);
807 #ifdef DEBUG
808 nsCOMPtr<nsIURI> uri;
809 mSheet->GetSheetURI(getter_AddRefs(uri));
810 PRBool equal;
811 NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
812 "Sheet URI does not match passed URI");
813 NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
814 &equal)) &&
815 equal,
816 "Sheet principal does not match passed principal");
817 #endif
819 InitScanner(aInput, aSheetURI, aLineNumber, aBaseURI, aSheetPrincipal);
821 PRInt32 ruleCount = 0;
822 mSheet->StyleRuleCount(ruleCount);
823 if (0 < ruleCount) {
824 nsICSSRule* lastRule = nsnull;
825 mSheet->GetStyleRuleAt(ruleCount - 1, lastRule);
826 if (lastRule) {
827 PRInt32 type;
828 lastRule->GetType(type);
829 switch (type) {
830 case nsICSSRule::CHARSET_RULE:
831 case nsICSSRule::IMPORT_RULE:
832 mSection = eCSSSection_Import;
833 break;
834 case nsICSSRule::NAMESPACE_RULE:
835 mSection = eCSSSection_NameSpace;
836 break;
837 default:
838 mSection = eCSSSection_General;
839 break;
841 NS_RELEASE(lastRule);
844 else {
845 mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
848 mUnsafeRulesEnabled = aAllowUnsafeRules;
850 nsCSSToken* tk = &mToken;
851 for (;;) {
852 // Get next non-whitespace token
853 if (!GetToken(PR_TRUE)) {
854 OUTPUT_ERROR();
855 break;
857 if (eCSSToken_HTMLComment == tk->mType) {
858 continue; // legal here only
860 if (eCSSToken_AtKeyword == tk->mType) {
861 ParseAtRule(AppendRuleToSheet, this);
862 continue;
864 UngetToken();
865 if (ParseRuleSet(AppendRuleToSheet, this)) {
866 mSection = eCSSSection_General;
869 ReleaseScanner();
871 mUnsafeRulesEnabled = PR_FALSE;
873 // XXX check for low level errors
874 return NS_OK;
878 * Determines whether the identifier contained in the given string is a
879 * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1.
881 static PRBool
882 NonMozillaVendorIdentifier(const nsAString& ident)
884 return (ident.First() == PRUnichar('-') &&
885 !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) ||
886 ident.First() == PRUnichar('_');
890 NS_IMETHODIMP
891 CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
892 nsIURI* aDocURL,
893 nsIURI* aBaseURL,
894 nsIPrincipal* aNodePrincipal,
895 nsICSSStyleRule** aResult)
897 NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
898 AssertInitialState();
900 NS_ASSERTION(nsnull != aBaseURL, "need base URL");
902 // XXX line number?
903 InitScanner(aAttributeValue, aDocURL, 0, aBaseURL, aNodePrincipal);
905 mSection = eCSSSection_General;
907 // In quirks mode, allow style declarations to have braces or not
908 // (bug 99554).
909 PRBool haveBraces;
910 if (mNavQuirkMode && GetToken(PR_TRUE)) {
911 haveBraces = eCSSToken_Symbol == mToken.mType &&
912 '{' == mToken.mSymbol;
913 UngetToken();
915 else {
916 haveBraces = PR_FALSE;
919 nsCSSDeclaration* declaration = ParseDeclarationBlock(haveBraces);
920 if (declaration) {
921 // Create a style rule for the declaration
922 nsICSSStyleRule* rule = nsnull;
923 nsresult rv = NS_NewCSSStyleRule(&rule, nsnull, declaration);
924 if (NS_FAILED(rv)) {
925 declaration->RuleAbort();
926 ReleaseScanner();
927 return rv;
929 *aResult = rule;
931 else {
932 *aResult = nsnull;
935 ReleaseScanner();
937 // XXX check for low level errors
938 return NS_OK;
941 NS_IMETHODIMP
942 CSSParserImpl::ParseAndAppendDeclaration(const nsAString& aBuffer,
943 nsIURI* aSheetURL,
944 nsIURI* aBaseURL,
945 nsIPrincipal* aSheetPrincipal,
946 nsCSSDeclaration* aDeclaration,
947 PRBool aParseOnlyOneDecl,
948 PRBool* aChanged,
949 PRBool aClearOldDecl)
951 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
952 AssertInitialState();
954 *aChanged = PR_FALSE;
956 InitScanner(aBuffer, aSheetURL, 0, aBaseURL, aSheetPrincipal);
958 mSection = eCSSSection_General;
960 if (aClearOldDecl) {
961 mData.AssertInitialState();
962 aDeclaration->ClearData();
963 // We could check if it was already empty, but...
964 *aChanged = PR_TRUE;
965 } else {
966 aDeclaration->ExpandTo(&mData);
969 nsresult rv = NS_OK;
970 do {
971 // If we cleared the old decl, then we want to be calling
972 // ValueAppended as we parse.
973 if (!ParseDeclaration(aDeclaration, PR_FALSE, aClearOldDecl, aChanged)) {
974 rv = mScanner.GetLowLevelError();
975 if (NS_FAILED(rv))
976 break;
978 if (!SkipDeclaration(PR_FALSE)) {
979 rv = mScanner.GetLowLevelError();
980 break;
983 } while (!aParseOnlyOneDecl);
984 aDeclaration->CompressFrom(&mData);
986 ReleaseScanner();
987 return rv;
990 NS_IMETHODIMP
991 CSSParserImpl::ParseRule(const nsAString& aRule,
992 nsIURI* aSheetURL,
993 nsIURI* aBaseURL,
994 nsIPrincipal* aSheetPrincipal,
995 nsCOMArray<nsICSSRule>& aResult)
997 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
998 AssertInitialState();
1000 NS_ASSERTION(nsnull != aBaseURL, "need base URL");
1002 InitScanner(aRule, aSheetURL, 0, aBaseURL, aSheetPrincipal);
1004 mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
1006 nsCSSToken* tk = &mToken;
1007 // Get first non-whitespace token
1008 if (!GetToken(PR_TRUE)) {
1009 REPORT_UNEXPECTED(PEParseRuleWSOnly);
1010 OUTPUT_ERROR();
1011 } else if (eCSSToken_AtKeyword == tk->mType) {
1012 ParseAtRule(AppendRuleToArray, &aResult);
1014 else {
1015 UngetToken();
1016 ParseRuleSet(AppendRuleToArray, &aResult);
1018 OUTPUT_ERROR();
1019 ReleaseScanner();
1020 // XXX check for low-level errors
1021 return NS_OK;
1024 NS_IMETHODIMP
1025 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
1026 const nsAString& aPropValue,
1027 nsIURI* aSheetURL,
1028 nsIURI* aBaseURL,
1029 nsIPrincipal* aSheetPrincipal,
1030 nsCSSDeclaration* aDeclaration,
1031 PRBool* aChanged)
1033 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1034 AssertInitialState();
1036 NS_ASSERTION(nsnull != aBaseURL, "need base URL");
1037 NS_ASSERTION(nsnull != aDeclaration, "Need declaration to parse into!");
1038 *aChanged = PR_FALSE;
1040 InitScanner(aPropValue, aSheetURL, 0, aBaseURL, aSheetPrincipal);
1042 mSection = eCSSSection_General;
1044 if (eCSSProperty_UNKNOWN == aPropID) { // unknown property
1045 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1046 const PRUnichar *params[] = {
1047 propName.get()
1049 REPORT_UNEXPECTED_P(PEUnknownProperty, params);
1050 REPORT_UNEXPECTED(PEDeclDropped);
1051 OUTPUT_ERROR();
1052 ReleaseScanner();
1053 return NS_OK;
1056 mData.AssertInitialState();
1057 mTempData.AssertInitialState();
1058 aDeclaration->ExpandTo(&mData);
1059 nsresult result = NS_OK;
1060 PRBool parsedOK = ParseProperty(aPropID);
1061 if (parsedOK && !GetToken(PR_TRUE)) {
1062 TransferTempData(aDeclaration, aPropID, PR_FALSE, PR_FALSE, aChanged);
1063 } else {
1064 if (parsedOK) {
1065 // Junk at end of property value.
1066 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1068 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1069 const PRUnichar *params[] = {
1070 propName.get()
1072 REPORT_UNEXPECTED_P(PEValueParsingError, params);
1073 REPORT_UNEXPECTED(PEDeclDropped);
1074 OUTPUT_ERROR();
1075 ClearTempData(aPropID);
1076 result = mScanner.GetLowLevelError();
1078 CLEAR_ERROR();
1080 aDeclaration->CompressFrom(&mData);
1082 ReleaseScanner();
1083 return result;
1086 NS_IMETHODIMP
1087 CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer,
1088 nsIURI* aURL, // for error reporting
1089 PRUint32 aLineNumber, // for error reporting
1090 nsMediaList* aMediaList,
1091 PRBool aHTMLMode)
1093 // XXX Are there cases where the caller wants to keep what it already
1094 // has in case of parser error?
1095 aMediaList->Clear();
1097 // fake base URL since media lists don't have URLs in them
1098 InitScanner(aBuffer, aURL, aLineNumber, aURL, nsnull);
1100 AssertInitialState();
1101 NS_ASSERTION(aHTMLMode == PR_TRUE || aHTMLMode == PR_FALSE,
1102 "invalid PRBool");
1103 mHTMLMediaMode = aHTMLMode;
1105 // XXXldb We need to make the scanner not skip CSS comments! (Or
1106 // should we?)
1108 // For aHTMLMode, we used to follow the parsing rules in
1109 // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors
1110 // which wouldn't work for media queries since they remove all but the
1111 // first word. However, they're changed in
1112 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-document.html#media2
1113 // (as of 2008-05-29) which says that the media attribute just points
1114 // to a media query. (The main substative difference is the relative
1115 // precedence of commas and paretheses.)
1117 if (!GatherMedia(aMediaList, PRUnichar(0))) {
1118 aMediaList->Clear();
1119 aMediaList->SetNonEmpty(); // don't match anything
1120 if (!mHTMLMediaMode) {
1121 OUTPUT_ERROR();
1124 nsresult rv = mScanner.GetLowLevelError();
1125 CLEAR_ERROR();
1126 ReleaseScanner();
1127 mHTMLMediaMode = PR_FALSE;
1129 return rv;
1132 NS_IMETHODIMP
1133 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
1134 nsIURI* aURL, // for error reporting
1135 PRUint32 aLineNumber, // for error reporting
1136 nscolor* aColor)
1138 AssertInitialState();
1139 InitScanner(aBuffer, aURL, aLineNumber, aURL, nsnull);
1141 nsCSSValue value;
1142 PRBool colorParsed = ParseColor(value);
1143 nsresult rv = mScanner.GetLowLevelError();
1144 OUTPUT_ERROR();
1145 ReleaseScanner();
1147 if (!colorParsed) {
1148 return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
1151 if (value.GetUnit() == eCSSUnit_String) {
1152 nscolor rgba;
1153 if (NS_ColorNameToRGB(nsDependentString(value.GetStringBufferValue()), &rgba)) {
1154 (*aColor) = rgba;
1155 rv = NS_OK;
1157 } else if (value.GetUnit() == eCSSUnit_Color) {
1158 (*aColor) = value.GetColorValue();
1159 rv = NS_OK;
1160 } else if (value.GetUnit() == eCSSUnit_EnumColor) {
1161 PRInt32 intValue = value.GetIntValue();
1162 if (intValue >= 0) {
1163 nsCOMPtr<nsILookAndFeel> lfSvc = do_GetService("@mozilla.org/widget/lookandfeel;1");
1164 if (lfSvc) {
1165 nscolor rgba;
1166 rv = lfSvc->GetColor((nsILookAndFeel::nsColorID) value.GetIntValue(), rgba);
1167 if (NS_SUCCEEDED(rv))
1168 (*aColor) = rgba;
1170 } else {
1171 // XXX - this is NS_COLOR_CURRENTCOLOR, NS_COLOR_MOZ_HYPERLINKTEXT, etc.
1172 // which we don't handle as per the ParseColorString definition. Should
1173 // remove this limitation at some point.
1174 rv = NS_ERROR_FAILURE;
1178 return rv;
1181 NS_IMETHODIMP
1182 CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
1183 nsIURI* aURL, // for error reporting
1184 PRUint32 aLineNumber, // for error reporting
1185 nsCSSSelectorList **aSelectorList)
1187 InitScanner(aSelectorString, aURL, aLineNumber, aURL, nsnull);
1189 AssertInitialState();
1191 mUnresolvablePrefixException = PR_TRUE;
1193 PRBool success = ParseSelectorList(*aSelectorList, PR_FALSE);
1194 nsresult rv = mScanner.GetLowLevelError();
1195 OUTPUT_ERROR();
1196 ReleaseScanner();
1198 mUnresolvablePrefixException = PR_FALSE;
1200 if (success) {
1201 NS_ASSERTION(*aSelectorList, "Should have list!");
1202 return NS_OK;
1205 NS_ASSERTION(!*aSelectorList, "Shouldn't have list!");
1206 if (NS_SUCCEEDED(rv)) {
1207 rv = NS_ERROR_DOM_SYNTAX_ERR;
1209 return rv;
1212 //----------------------------------------------------------------------
1214 PRBool
1215 CSSParserImpl::GetToken(PRBool aSkipWS)
1217 for (;;) {
1218 if (!mHavePushBack) {
1219 if (!mScanner.Next(mToken)) {
1220 break;
1223 mHavePushBack = PR_FALSE;
1224 if (aSkipWS && (eCSSToken_WhiteSpace == mToken.mType)) {
1225 continue;
1227 return PR_TRUE;
1229 return PR_FALSE;
1232 PRBool
1233 CSSParserImpl::GetURLToken()
1235 for (;;) {
1236 // XXXldb This pushback code doesn't make sense.
1237 if (! mHavePushBack) {
1238 if (! mScanner.NextURL(mToken)) {
1239 break;
1242 mHavePushBack = PR_FALSE;
1243 if (eCSSToken_WhiteSpace != mToken.mType) {
1244 return PR_TRUE;
1247 return PR_FALSE;
1250 void
1251 CSSParserImpl::UngetToken()
1253 NS_PRECONDITION(mHavePushBack == PR_FALSE, "double pushback");
1254 mHavePushBack = PR_TRUE;
1257 PRBool
1258 CSSParserImpl::ExpectSymbol(PRUnichar aSymbol,
1259 PRBool aSkipWS)
1261 if (!GetToken(aSkipWS)) {
1262 // CSS2.1 specifies that all "open constructs" are to be closed at
1263 // EOF. It simplifies higher layers if we claim to have found an
1264 // ), ], }, or ; if we encounter EOF while looking for one of them.
1265 // Do still issue a diagnostic, to aid debugging.
1266 if (aSymbol == ')' || aSymbol == ']' ||
1267 aSymbol == '}' || aSymbol == ';') {
1268 REPORT_UNEXPECTED_EOF_CHAR(aSymbol);
1269 return PR_TRUE;
1271 else
1272 return PR_FALSE;
1274 if (mToken.IsSymbol(aSymbol)) {
1275 return PR_TRUE;
1277 UngetToken();
1278 return PR_FALSE;
1281 // Checks to see if we're at the end of a property. If an error occurs during
1282 // the check, does not signal a parse error.
1283 PRBool
1284 CSSParserImpl::CheckEndProperty()
1286 if (!GetToken(PR_TRUE)) {
1287 return PR_TRUE; // properties may end with eof
1289 if ((eCSSToken_Symbol == mToken.mType) &&
1290 ((';' == mToken.mSymbol) ||
1291 ('!' == mToken.mSymbol) ||
1292 ('}' == mToken.mSymbol))) {
1293 // XXX need to verify that ! is only followed by "important [;|}]
1294 // XXX this requires a multi-token pushback buffer
1295 UngetToken();
1296 return PR_TRUE;
1298 UngetToken();
1299 return PR_FALSE;
1302 // Checks if we're at the end of a property, raising an error if we're not.
1303 PRBool
1304 CSSParserImpl::ExpectEndProperty()
1306 if (CheckEndProperty())
1307 return PR_TRUE;
1309 // If we're here, we read something incorrect, so we should report it.
1310 REPORT_UNEXPECTED_TOKEN(PRExpectEndValue);
1311 return PR_FALSE;
1315 nsSubstring*
1316 CSSParserImpl::NextIdent()
1318 // XXX Error reporting?
1319 if (!GetToken(PR_TRUE)) {
1320 return nsnull;
1322 if (eCSSToken_Ident != mToken.mType) {
1323 UngetToken();
1324 return nsnull;
1326 return &mToken.mIdent;
1329 PRBool
1330 CSSParserImpl::SkipAtRule()
1332 for (;;) {
1333 if (!GetToken(PR_TRUE)) {
1334 REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF);
1335 return PR_FALSE;
1337 if (eCSSToken_Symbol == mToken.mType) {
1338 PRUnichar symbol = mToken.mSymbol;
1339 if (symbol == ';') {
1340 break;
1342 if (symbol == '{') {
1343 SkipUntil('}');
1344 break;
1345 } else if (symbol == '(') {
1346 SkipUntil(')');
1347 } else if (symbol == '[') {
1348 SkipUntil(']');
1352 return PR_TRUE;
1355 PRBool
1356 CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc,
1357 void* aData)
1359 if ((mSection <= eCSSSection_Charset) &&
1360 (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
1361 if (ParseCharsetRule(aAppendFunc, aData)) {
1362 mSection = eCSSSection_Import; // only one charset allowed
1363 return PR_TRUE;
1366 if ((mSection <= eCSSSection_Import) &&
1367 mToken.mIdent.LowerCaseEqualsLiteral("import")) {
1368 if (ParseImportRule(aAppendFunc, aData)) {
1369 mSection = eCSSSection_Import;
1370 return PR_TRUE;
1373 if ((mSection <= eCSSSection_NameSpace) &&
1374 mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
1375 if (ParseNameSpaceRule(aAppendFunc, aData)) {
1376 mSection = eCSSSection_NameSpace;
1377 return PR_TRUE;
1380 if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
1381 if (ParseMediaRule(aAppendFunc, aData)) {
1382 mSection = eCSSSection_General;
1383 return PR_TRUE;
1386 if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
1387 if (ParseMozDocumentRule(aAppendFunc, aData)) {
1388 mSection = eCSSSection_General;
1389 return PR_TRUE;
1392 if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
1393 if (ParseFontFaceRule(aAppendFunc, aData)) {
1394 mSection = eCSSSection_General;
1395 return PR_TRUE;
1398 if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
1399 if (ParsePageRule(aAppendFunc, aData)) {
1400 mSection = eCSSSection_General;
1401 return PR_TRUE;
1405 if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
1406 REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
1407 OUTPUT_ERROR();
1410 // Skip over unsupported at rule, don't advance section
1411 return SkipAtRule();
1414 PRBool
1415 CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc,
1416 void* aData)
1418 if (!GetToken(PR_TRUE)) {
1419 REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
1420 return PR_FALSE;
1423 if (eCSSToken_String != mToken.mType) {
1424 REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
1425 return PR_FALSE;
1428 nsAutoString charset = mToken.mIdent;
1430 if (!ExpectSymbol(';', PR_TRUE)) {
1431 return PR_FALSE;
1434 nsCOMPtr<nsICSSRule> rule;
1435 NS_NewCSSCharsetRule(getter_AddRefs(rule), charset);
1437 if (rule) {
1438 (*aAppendFunc)(rule, aData);
1441 return PR_TRUE;
1444 PRBool
1445 CSSParserImpl::GatherURL(nsString& aURL)
1447 if (!GetToken(PR_TRUE)) {
1448 return PR_FALSE;
1450 if (eCSSToken_String == mToken.mType) {
1451 aURL = mToken.mIdent;
1452 return PR_TRUE;
1454 else if (eCSSToken_Function == mToken.mType &&
1455 mToken.mIdent.LowerCaseEqualsLiteral("url") &&
1456 ExpectSymbol('(', PR_FALSE) &&
1457 GetURLToken() &&
1458 (eCSSToken_String == mToken.mType ||
1459 eCSSToken_URL == mToken.mType)) {
1460 aURL = mToken.mIdent;
1461 if (ExpectSymbol(')', PR_TRUE)) {
1462 return PR_TRUE;
1465 return PR_FALSE;
1468 PRBool
1469 CSSParserImpl::ParseMediaQuery(PRUnichar aStopSymbol,
1470 nsMediaQuery **aQuery,
1471 PRBool *aParsedSomething,
1472 PRBool *aHitStop)
1474 *aQuery = nsnull;
1475 *aParsedSomething = PR_FALSE;
1476 *aHitStop = PR_FALSE;
1478 // "If the comma-separated list is the empty list it is assumed to
1479 // specify the media query 'all'." (css3-mediaqueries, section
1480 // "Media Queries")
1481 if (!GetToken(PR_TRUE)) {
1482 *aHitStop = PR_TRUE;
1483 // expected termination by EOF
1484 if (aStopSymbol == PRUnichar(0))
1485 return PR_TRUE;
1487 // unexpected termination by EOF
1488 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1489 return PR_TRUE;
1492 if (eCSSToken_Symbol == mToken.mType &&
1493 mToken.mSymbol == aStopSymbol) {
1494 *aHitStop = PR_TRUE;
1495 UngetToken();
1496 return PR_TRUE;
1498 UngetToken();
1500 *aParsedSomething = PR_TRUE;
1502 nsAutoPtr<nsMediaQuery> query(new nsMediaQuery);
1503 if (!query) {
1504 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1505 return PR_FALSE;
1508 if (ExpectSymbol('(', PR_TRUE)) {
1509 // we got an expression without a media type
1510 UngetToken(); // so ParseMediaQueryExpression can handle it
1511 query->SetType(nsGkAtoms::all);
1512 query->SetTypeOmitted();
1513 // Just parse the first expression here.
1514 if (!ParseMediaQueryExpression(query)) {
1515 OUTPUT_ERROR();
1516 query->SetHadUnknownExpression();
1518 } else {
1519 nsCOMPtr<nsIAtom> mediaType;
1520 PRBool gotNotOrOnly = PR_FALSE;
1521 for (;;) {
1522 if (!GetToken(PR_TRUE)) {
1523 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1524 return PR_FALSE;
1526 if (eCSSToken_Ident != mToken.mType) {
1527 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
1528 UngetToken();
1529 return PR_FALSE;
1531 // case insensitive from CSS - must be lower cased
1532 ToLowerCase(mToken.mIdent);
1533 mediaType = do_GetAtom(mToken.mIdent);
1534 if (gotNotOrOnly ||
1535 (mediaType != nsGkAtoms::_not && mediaType != nsGkAtoms::only))
1536 break;
1537 gotNotOrOnly = PR_TRUE;
1538 if (mediaType == nsGkAtoms::_not)
1539 query->SetNegated();
1540 else
1541 query->SetHasOnly();
1543 query->SetType(mediaType);
1546 for (;;) {
1547 if (!GetToken(PR_TRUE)) {
1548 *aHitStop = PR_TRUE;
1549 // expected termination by EOF
1550 if (aStopSymbol == PRUnichar(0))
1551 break;
1553 // unexpected termination by EOF
1554 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1555 break;
1558 if (eCSSToken_Symbol == mToken.mType &&
1559 mToken.mSymbol == aStopSymbol) {
1560 *aHitStop = PR_TRUE;
1561 UngetToken();
1562 break;
1564 if (eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
1565 // Done with the expressions for this query
1566 break;
1568 if (eCSSToken_Ident != mToken.mType ||
1569 !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
1570 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
1571 UngetToken();
1572 return PR_FALSE;
1574 if (!ParseMediaQueryExpression(query)) {
1575 OUTPUT_ERROR();
1576 query->SetHadUnknownExpression();
1579 *aQuery = query.forget();
1580 return PR_TRUE;
1583 // Returns false only when there is a low-level error in the scanner
1584 // (out-of-memory).
1585 PRBool
1586 CSSParserImpl::GatherMedia(nsMediaList* aMedia,
1587 PRUnichar aStopSymbol)
1589 for (;;) {
1590 nsAutoPtr<nsMediaQuery> query;
1591 PRBool parsedSomething, hitStop;
1592 if (!ParseMediaQuery(aStopSymbol, getter_Transfers(query),
1593 &parsedSomething, &hitStop)) {
1594 if (NS_FAILED(mScanner.GetLowLevelError())) {
1595 return PR_FALSE;
1597 SkipUntil(',');
1599 if (parsedSomething) {
1600 aMedia->SetNonEmpty();
1602 if (query) {
1603 nsresult rv = aMedia->AppendQuery(query);
1604 if (NS_FAILED(rv)) {
1605 mScanner.SetLowLevelError(rv);
1606 return PR_FALSE;
1609 if (hitStop) {
1610 break;
1613 return PR_TRUE;
1616 PRBool
1617 CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
1619 if (!ExpectSymbol('(', PR_TRUE)) {
1620 REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart);
1621 return PR_FALSE;
1623 if (! GetToken(PR_TRUE)) {
1624 REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
1625 return PR_FALSE;
1627 if (eCSSToken_Ident != mToken.mType) {
1628 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
1629 SkipUntil(')');
1630 return PR_FALSE;
1633 nsMediaExpression *expr = aQuery->NewExpression();
1634 if (!expr) {
1635 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1636 SkipUntil(')');
1637 return PR_FALSE;
1640 // case insensitive from CSS - must be lower cased
1641 ToLowerCase(mToken.mIdent);
1642 const PRUnichar *featureString;
1643 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("min-"))) {
1644 expr->mRange = nsMediaExpression::eMin;
1645 featureString = mToken.mIdent.get() + 4;
1646 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("max-"))) {
1647 expr->mRange = nsMediaExpression::eMax;
1648 featureString = mToken.mIdent.get() + 4;
1649 } else {
1650 expr->mRange = nsMediaExpression::eEqual;
1651 featureString = mToken.mIdent.get();
1654 nsCOMPtr<nsIAtom> mediaFeatureAtom = do_GetAtom(featureString);
1655 const nsMediaFeature *feature = nsMediaFeatures::features;
1656 for (; feature->mName; ++feature) {
1657 if (*(feature->mName) == mediaFeatureAtom) {
1658 break;
1661 if (!feature->mName ||
1662 (expr->mRange != nsMediaExpression::eEqual &&
1663 feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) {
1664 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
1665 SkipUntil(')');
1666 return PR_FALSE;
1668 expr->mFeature = feature;
1670 if (!GetToken(PR_TRUE) || mToken.IsSymbol(')')) {
1671 // Query expressions for any feature can be given without a value.
1672 // However, min/max prefixes are not allowed.
1673 if (expr->mRange != nsMediaExpression::eEqual) {
1674 REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue);
1675 return PR_FALSE;
1677 expr->mValue.Reset();
1678 return PR_TRUE;
1681 if (!mToken.IsSymbol(':')) {
1682 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
1683 SkipUntil(')');
1684 return PR_FALSE;
1687 PRBool rv;
1688 switch (feature->mValueType) {
1689 case nsMediaFeature::eLength:
1690 rv = ParsePositiveVariant(expr->mValue, VARIANT_LENGTH, nsnull);
1691 break;
1692 case nsMediaFeature::eInteger:
1693 case nsMediaFeature::eBoolInteger:
1694 rv = ParsePositiveVariant(expr->mValue, VARIANT_INTEGER, nsnull);
1695 // Enforce extra restrictions for eBoolInteger
1696 if (rv &&
1697 feature->mValueType == nsMediaFeature::eBoolInteger &&
1698 expr->mValue.GetIntValue() > 1)
1699 rv = PR_FALSE;
1700 break;
1701 case nsMediaFeature::eIntRatio:
1703 // Two integers separated by '/', with optional whitespace on
1704 // either side of the '/'.
1705 nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
1706 if (!a) {
1707 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1708 SkipUntil(')');
1709 return PR_FALSE;
1711 expr->mValue.SetArrayValue(a, eCSSUnit_Array);
1712 // We don't bother with ParsePositiveVariant since we have to
1713 // check for != 0 as well; no need to worry about the UngetToken
1714 // since we're throwing out up to the next ')' anyway.
1715 rv = ParseVariant(a->Item(0), VARIANT_INTEGER, nsnull) &&
1716 a->Item(0).GetIntValue() > 0 &&
1717 ExpectSymbol('/', PR_TRUE) &&
1718 ParseVariant(a->Item(1), VARIANT_INTEGER, nsnull) &&
1719 a->Item(1).GetIntValue() > 0;
1721 break;
1722 case nsMediaFeature::eResolution:
1723 rv = GetToken(PR_TRUE) && mToken.IsDimension() &&
1724 mToken.mIntegerValid && mToken.mNumber > 0.0f;
1725 if (rv) {
1726 // No worries about whether unitless zero is allowed, since the
1727 // value must be positive (and we checked that above).
1728 NS_ASSERTION(!mToken.mIdent.IsEmpty(), "IsDimension lied");
1729 if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
1730 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
1731 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
1732 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
1733 } else {
1734 rv = PR_FALSE;
1737 break;
1738 case nsMediaFeature::eEnumerated:
1739 rv = ParseVariant(expr->mValue, VARIANT_KEYWORD,
1740 feature->mKeywordTable);
1741 break;
1743 if (!rv || !ExpectSymbol(')', PR_TRUE)) {
1744 REPORT_UNEXPECTED(PEMQExpectedFeatureValue);
1745 SkipUntil(')');
1746 return PR_FALSE;
1749 return PR_TRUE;
1752 // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
1753 PRBool
1754 CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData)
1756 nsCOMPtr<nsMediaList> media = new nsMediaList();
1757 if (!media) {
1758 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1759 return PR_FALSE;
1762 nsAutoString url;
1763 if (!GatherURL(url)) {
1764 REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
1765 return PR_FALSE;
1768 if (!ExpectSymbol(';', PR_TRUE)) {
1769 if (!GatherMedia(media, ';') ||
1770 !ExpectSymbol(';', PR_TRUE)) {
1771 REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
1772 // don't advance section, simply ignore invalid @import
1773 return PR_FALSE;
1776 // Safe to assert this, since we ensured that there is something
1777 // other than the ';' coming after the @import's url() token.
1778 NS_ASSERTION(media->Count() != 0, "media list must be nonempty");
1781 ProcessImport(url, media, aAppendFunc, aData);
1782 return PR_TRUE;
1786 PRBool
1787 CSSParserImpl::ProcessImport(const nsString& aURLSpec,
1788 nsMediaList* aMedia,
1789 RuleAppendFunc aAppendFunc,
1790 void* aData)
1792 nsCOMPtr<nsICSSImportRule> rule;
1793 nsresult rv = NS_NewCSSImportRule(getter_AddRefs(rule), aURLSpec, aMedia);
1794 if (NS_FAILED(rv)) {
1795 mScanner.SetLowLevelError(rv);
1796 return PR_FALSE;
1798 (*aAppendFunc)(rule, aData);
1800 if (mChildLoader) {
1801 nsCOMPtr<nsIURI> url;
1802 // XXX should pass a charset!
1803 rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nsnull, mBaseURL);
1805 if (NS_FAILED(rv)) {
1806 // import url is bad
1807 // XXX log this somewhere for easier web page debugging
1808 mScanner.SetLowLevelError(rv);
1809 return PR_FALSE;
1812 mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule);
1815 return PR_TRUE;
1818 // Parse the {} part of an @media or @-moz-document rule.
1819 PRBool
1820 CSSParserImpl::ParseGroupRule(nsICSSGroupRule* aRule,
1821 RuleAppendFunc aAppendFunc,
1822 void* aData)
1824 // XXXbz this could use better error reporting throughout the method
1825 if (!ExpectSymbol('{', PR_TRUE)) {
1826 return PR_FALSE;
1829 // push rule on stack, loop over children
1830 if (!PushGroup(aRule)) {
1831 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1832 return PR_FALSE;
1834 nsCSSSection holdSection = mSection;
1835 mSection = eCSSSection_General;
1837 for (;;) {
1838 // Get next non-whitespace token
1839 if (! GetToken(PR_TRUE)) {
1840 REPORT_UNEXPECTED_EOF(PEGroupRuleEOF);
1841 break;
1843 if (mToken.IsSymbol('}')) { // done!
1844 UngetToken();
1845 break;
1847 if (eCSSToken_AtKeyword == mToken.mType) {
1848 SkipAtRule(); // group rules cannot contain @rules
1849 continue;
1851 UngetToken();
1852 ParseRuleSet(AppendRuleToSheet, this);
1854 PopGroup();
1856 if (!ExpectSymbol('}', PR_TRUE)) {
1857 mSection = holdSection;
1858 return PR_FALSE;
1860 (*aAppendFunc)(aRule, aData);
1861 return PR_TRUE;
1864 // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
1865 PRBool
1866 CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData)
1868 nsCOMPtr<nsMediaList> media = new nsMediaList();
1869 if (!media) {
1870 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1871 return PR_FALSE;
1874 if (GatherMedia(media, '{')) {
1875 // XXXbz this could use better error reporting throughout the method
1876 nsRefPtr<nsCSSMediaRule> rule(new nsCSSMediaRule());
1877 // Append first, so when we do SetMedia() the rule
1878 // knows what its stylesheet is.
1879 if (rule && ParseGroupRule(rule, aAppendFunc, aData)) {
1880 rule->SetMedia(media);
1881 return PR_TRUE;
1885 return PR_FALSE;
1888 // Parse a @-moz-document rule. This is like an @media rule, but instead
1889 // of a medium it has a nonempty list of items where each item is either
1890 // url(), url-prefix(), or domain().
1891 PRBool
1892 CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData)
1894 nsCSSDocumentRule::URL *urls = nsnull;
1895 nsCSSDocumentRule::URL **next = &urls;
1896 do {
1897 if (!GetToken(PR_TRUE) ||
1898 eCSSToken_Function != mToken.mType ||
1899 !(mToken.mIdent.LowerCaseEqualsLiteral("url") ||
1900 mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
1901 mToken.mIdent.LowerCaseEqualsLiteral("domain"))) {
1902 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc);
1903 delete urls;
1904 return PR_FALSE;
1906 nsCSSDocumentRule::URL *cur = *next = new nsCSSDocumentRule::URL;
1907 if (!cur) {
1908 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1909 delete urls;
1910 return PR_FALSE;
1912 next = &cur->next;
1913 if (mToken.mIdent.LowerCaseEqualsLiteral("url")) {
1914 cur->func = nsCSSDocumentRule::eURL;
1915 } else if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
1916 cur->func = nsCSSDocumentRule::eURLPrefix;
1917 } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
1918 cur->func = nsCSSDocumentRule::eDomain;
1921 if (!ExpectSymbol('(', PR_FALSE) ||
1922 !GetURLToken() ||
1923 (eCSSToken_String != mToken.mType &&
1924 eCSSToken_URL != mToken.mType)) {
1925 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
1926 delete urls;
1927 return PR_FALSE;
1929 if (!ExpectSymbol(')', PR_TRUE)) {
1930 delete urls;
1931 return PR_FALSE;
1934 // We could try to make the URL (as long as it's not domain())
1935 // canonical and absolute with NS_NewURI and GetSpec, but I'm
1936 // inclined to think we shouldn't.
1937 CopyUTF16toUTF8(mToken.mIdent, cur->url);
1938 } while (ExpectSymbol(',', PR_TRUE));
1940 nsRefPtr<nsCSSDocumentRule> rule(new nsCSSDocumentRule());
1941 if (!rule) {
1942 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1943 delete urls;
1944 return PR_FALSE;
1946 rule->SetURLs(urls);
1948 return ParseGroupRule(rule, aAppendFunc, aData);
1951 // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
1952 PRBool
1953 CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData)
1955 if (!GetToken(PR_TRUE)) {
1956 REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
1957 return PR_FALSE;
1960 nsAutoString prefix;
1961 nsAutoString url;
1963 if (eCSSToken_Ident == mToken.mType) {
1964 prefix = mToken.mIdent;
1965 // user-specified identifiers are case-sensitive (bug 416106)
1966 if (! GetToken(PR_TRUE)) {
1967 REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
1968 return PR_FALSE;
1972 if (eCSSToken_String == mToken.mType) {
1973 url = mToken.mIdent;
1974 if (ExpectSymbol(';', PR_TRUE)) {
1975 ProcessNameSpace(prefix, url, aAppendFunc, aData);
1976 return PR_TRUE;
1979 else if ((eCSSToken_Function == mToken.mType) &&
1980 (mToken.mIdent.LowerCaseEqualsLiteral("url"))) {
1981 if (ExpectSymbol('(', PR_FALSE)) {
1982 if (GetURLToken()) {
1983 if ((eCSSToken_String == mToken.mType) || (eCSSToken_URL == mToken.mType)) {
1984 url = mToken.mIdent;
1985 if (ExpectSymbol(')', PR_TRUE)) {
1986 if (ExpectSymbol(';', PR_TRUE)) {
1987 ProcessNameSpace(prefix, url, aAppendFunc, aData);
1988 return PR_TRUE;
1995 REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
1997 return PR_FALSE;
2000 PRBool
2001 CSSParserImpl::ProcessNameSpace(const nsString& aPrefix,
2002 const nsString& aURLSpec,
2003 RuleAppendFunc aAppendFunc,
2004 void* aData)
2006 PRBool result = PR_FALSE;
2008 nsCOMPtr<nsICSSNameSpaceRule> rule;
2009 nsCOMPtr<nsIAtom> prefix;
2011 if (!aPrefix.IsEmpty()) {
2012 prefix = do_GetAtom(aPrefix);
2015 NS_NewCSSNameSpaceRule(getter_AddRefs(rule), prefix, aURLSpec);
2016 if (rule) {
2017 (*aAppendFunc)(rule, aData);
2019 // If this was the first namespace rule encountered, it will trigger
2020 // creation of a namespace map.
2021 if (!mNameSpaceMap) {
2022 mNameSpaceMap = mSheet->GetNameSpaceMap();
2026 return result;
2029 // font-face-rule: '@font-face' '{' font-description '}'
2030 // font-description: font-descriptor+
2031 PRBool
2032 CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData)
2034 if (!ExpectSymbol('{', PR_TRUE))
2035 return PR_FALSE;
2037 nsRefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule());
2038 if (!rule) {
2039 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2040 return PR_FALSE;
2043 for (;;) {
2044 if (!GetToken(PR_TRUE)) {
2045 REPORT_UNEXPECTED_EOF(PEFontFaceEOF);
2046 break;
2048 if (mToken.IsSymbol('}')) { // done!
2049 UngetToken();
2050 break;
2053 // ignore extra semicolons
2054 if (mToken.IsSymbol(';'))
2055 continue;
2057 if (!ParseFontDescriptor(rule)) {
2058 REPORT_UNEXPECTED(PEDeclSkipped);
2059 OUTPUT_ERROR();
2060 if (!SkipDeclaration(PR_TRUE))
2061 break;
2064 if (!ExpectSymbol('}', PR_TRUE))
2065 return PR_FALSE;
2066 (*aAppendFunc)(rule, aData);
2067 return PR_TRUE;
2070 // font-descriptor: font-family-desc
2071 // | font-style-desc
2072 // | font-weight-desc
2073 // | font-stretch-desc
2074 // | font-src-desc
2075 // | unicode-range-desc
2077 // All font-*-desc productions follow the pattern
2078 // IDENT ':' value ';'
2080 // On entry to this function, mToken is the IDENT.
2082 PRBool
2083 CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule)
2085 if (eCSSToken_Ident != mToken.mType) {
2086 REPORT_UNEXPECTED_TOKEN(PEFontDescExpected);
2087 return PR_FALSE;
2090 nsString descName = mToken.mIdent;
2091 if (!ExpectSymbol(':', PR_TRUE)) {
2092 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
2093 OUTPUT_ERROR();
2094 return PR_FALSE;
2097 nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName);
2098 nsCSSValue value;
2100 if (descID == eCSSFontDesc_UNKNOWN) {
2101 if (NonMozillaVendorIdentifier(descName)) {
2102 // silently skip other vendors' extensions
2103 SkipDeclaration(PR_TRUE);
2104 return PR_TRUE;
2105 } else {
2106 const PRUnichar *params[] = {
2107 descName.get()
2109 REPORT_UNEXPECTED_P(PEUnknownFontDesc, params);
2110 return PR_FALSE;
2114 if (!ParseFontDescriptorValue(descID, value)) {
2115 const PRUnichar *params[] = {
2116 descName.get()
2118 REPORT_UNEXPECTED_P(PEValueParsingError, params);
2119 return PR_FALSE;
2122 if (!ExpectEndProperty())
2123 return PR_FALSE;
2125 aRule->SetDesc(descID, value);
2126 return PR_TRUE;
2130 PRBool
2131 CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData)
2133 // XXX not yet implemented
2134 return PR_FALSE;
2137 void
2138 CSSParserImpl::SkipUntil(PRUnichar aStopSymbol)
2140 nsCSSToken* tk = &mToken;
2141 for (;;) {
2142 if (!GetToken(PR_TRUE)) {
2143 break;
2145 if (eCSSToken_Symbol == tk->mType) {
2146 PRUnichar symbol = tk->mSymbol;
2147 if (symbol == aStopSymbol) {
2148 break;
2149 } else if ('{' == symbol) {
2150 SkipUntil('}');
2151 } else if ('[' == symbol) {
2152 SkipUntil(']');
2153 } else if ('(' == symbol) {
2154 SkipUntil(')');
2160 PRBool
2161 CSSParserImpl::GetNonCloseParenToken(PRBool aSkipWS)
2163 if (!GetToken(aSkipWS))
2164 return PR_FALSE;
2165 if (mToken.mType == eCSSToken_Symbol && mToken.mSymbol == ')') {
2166 UngetToken();
2167 return PR_FALSE;
2169 return PR_TRUE;
2172 PRBool
2173 CSSParserImpl::SkipDeclaration(PRBool aCheckForBraces)
2175 nsCSSToken* tk = &mToken;
2176 for (;;) {
2177 if (!GetToken(PR_TRUE)) {
2178 if (aCheckForBraces) {
2179 REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
2181 return PR_FALSE;
2183 if (eCSSToken_Symbol == tk->mType) {
2184 PRUnichar symbol = tk->mSymbol;
2185 if (';' == symbol) {
2186 break;
2188 if (aCheckForBraces) {
2189 if ('}' == symbol) {
2190 UngetToken();
2191 break;
2194 if ('{' == symbol) {
2195 SkipUntil('}');
2196 } else if ('(' == symbol) {
2197 SkipUntil(')');
2198 } else if ('[' == symbol) {
2199 SkipUntil(']');
2203 return PR_TRUE;
2206 void
2207 CSSParserImpl::SkipRuleSet()
2209 nsCSSToken* tk = &mToken;
2210 for (;;) {
2211 if (!GetToken(PR_TRUE)) {
2212 REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
2213 break;
2215 if (eCSSToken_Symbol == tk->mType) {
2216 PRUnichar symbol = tk->mSymbol;
2217 if ('{' == symbol) {
2218 SkipUntil('}');
2219 break;
2221 if ('(' == symbol) {
2222 SkipUntil(')');
2223 } else if ('[' == symbol) {
2224 SkipUntil(']');
2230 PRBool
2231 CSSParserImpl::PushGroup(nsICSSGroupRule* aRule)
2233 if (mGroupStack.AppendObject(aRule))
2234 return PR_TRUE;
2236 return PR_FALSE;
2239 void
2240 CSSParserImpl::PopGroup(void)
2242 PRInt32 count = mGroupStack.Count();
2243 if (0 < count) {
2244 mGroupStack.RemoveObjectAt(count - 1);
2248 void
2249 CSSParserImpl::AppendRule(nsICSSRule* aRule)
2251 PRInt32 count = mGroupStack.Count();
2252 if (0 < count) {
2253 mGroupStack[count - 1]->AppendStyleRule(aRule);
2255 else {
2256 mSheet->AppendStyleRule(aRule);
2260 PRBool
2261 CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData)
2263 // First get the list of selectors for the rule
2264 nsCSSSelectorList* slist = nsnull;
2265 PRUint32 linenum = mScanner.GetLineNumber();
2266 if (! ParseSelectorList(slist, PR_TRUE)) {
2267 REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
2268 OUTPUT_ERROR();
2269 SkipRuleSet();
2270 return PR_FALSE;
2272 NS_ASSERTION(nsnull != slist, "null selector list");
2273 CLEAR_ERROR();
2275 // Next parse the declaration block
2276 nsCSSDeclaration* declaration = ParseDeclarationBlock(PR_TRUE);
2277 if (nsnull == declaration) {
2278 // XXX skip something here
2279 delete slist;
2280 return PR_FALSE;
2283 #if 0
2284 slist->Dump();
2285 fputs("{\n", stdout);
2286 declaration->List();
2287 fputs("}\n", stdout);
2288 #endif
2290 // Translate the selector list and declaration block into style data
2292 nsCOMPtr<nsICSSStyleRule> rule;
2293 NS_NewCSSStyleRule(getter_AddRefs(rule), slist, declaration);
2294 if (!rule) {
2295 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2296 delete slist;
2297 return PR_FALSE;
2299 rule->SetLineNumber(linenum);
2300 (*aAppendFunc)(rule, aData);
2302 return PR_TRUE;
2305 PRBool
2306 CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
2307 PRBool aTerminateAtBrace)
2309 nsCSSSelectorList* list = nsnull;
2310 if (! ParseSelectorGroup(list)) {
2311 // must have at least one selector group
2312 aListHead = nsnull;
2313 return PR_FALSE;
2315 NS_ASSERTION(nsnull != list, "no selector list");
2316 aListHead = list;
2318 // After that there must either be a "," or a "{" (the latter if
2319 // aTerminateAtBrace is true)
2320 nsCSSToken* tk = &mToken;
2321 for (;;) {
2322 if (! GetToken(PR_TRUE)) {
2323 if (!aTerminateAtBrace) {
2324 return PR_TRUE;
2327 REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
2328 break;
2331 if (eCSSToken_Symbol == tk->mType) {
2332 if (',' == tk->mSymbol) {
2333 nsCSSSelectorList* newList = nsnull;
2334 // Another selector group must follow
2335 if (! ParseSelectorGroup(newList)) {
2336 break;
2338 // add new list to the end of the selector list
2339 list->mNext = newList;
2340 list = newList;
2341 continue;
2342 } else if ('{' == tk->mSymbol && aTerminateAtBrace) {
2343 UngetToken();
2344 return PR_TRUE;
2347 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
2348 UngetToken();
2349 break;
2352 delete aListHead;
2353 aListHead = nsnull;
2354 return PR_FALSE;
2357 static PRBool IsSinglePseudoClass(const nsCSSSelector& aSelector)
2359 return PRBool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
2360 (aSelector.mTag == nsnull) &&
2361 (aSelector.mIDList == nsnull) &&
2362 (aSelector.mClassList == nsnull) &&
2363 (aSelector.mAttrList == nsnull) &&
2364 (aSelector.mNegations == nsnull) &&
2365 (aSelector.mPseudoClassList != nsnull) &&
2366 (aSelector.mPseudoClassList->mNext == nsnull));
2369 #ifdef MOZ_XUL
2370 static PRBool IsTreePseudoElement(nsIAtom* aPseudo)
2372 const char* str;
2373 aPseudo->GetUTF8String(&str);
2374 static const char moz_tree[] = ":-moz-tree-";
2375 return nsCRT::strncmp(str, moz_tree, PRInt32(sizeof(moz_tree)-1)) == 0;
2377 #endif
2379 PRBool
2380 CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList)
2382 nsAutoPtr<nsCSSSelectorList> list;
2383 PRUnichar combinator = PRUnichar(0);
2384 PRInt32 weight = 0;
2385 PRBool havePseudoElement = PR_FALSE;
2386 PRBool done = PR_FALSE;
2387 while (!done) {
2388 nsAutoPtr<nsCSSSelector> newSelector(new nsCSSSelector());
2389 if (!newSelector) {
2390 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2391 return PR_FALSE;
2393 nsSelectorParsingStatus parsingStatus =
2394 ParseSelector(*newSelector);
2395 if (parsingStatus == eSelectorParsingStatus_Empty) {
2396 if (!list) {
2397 REPORT_UNEXPECTED(PESelectorGroupNoSelector);
2399 break;
2401 if (parsingStatus == eSelectorParsingStatus_Error) {
2402 list = nsnull;
2403 break;
2405 if (nsnull == list) {
2406 list = new nsCSSSelectorList();
2407 if (nsnull == list) {
2408 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2409 return PR_FALSE;
2412 list->AddSelector(newSelector);
2413 nsCSSSelector* listSel = list->mSelectors;
2415 // pull out pseudo elements here
2416 nsPseudoClassList* prevList = nsnull;
2417 nsPseudoClassList* pseudoClassList = listSel->mPseudoClassList;
2418 while (nsnull != pseudoClassList) {
2419 if (! nsCSSPseudoClasses::IsPseudoClass(pseudoClassList->mAtom)) {
2420 havePseudoElement = PR_TRUE;
2421 if (IsSinglePseudoClass(*listSel)) { // convert to pseudo element selector
2422 nsIAtom* pseudoElement = pseudoClassList->mAtom; // steal ref count
2423 pseudoClassList->mAtom = nsnull;
2424 listSel->Reset();
2425 if (listSel->mNext) {// more to the selector
2426 listSel->mOperator = PRUnichar('>');
2427 nsAutoPtr<nsCSSSelector> empty(new nsCSSSelector());
2428 if (!empty) {
2429 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2430 return PR_FALSE;
2432 list->AddSelector(empty); // leave a blank (universal) selector in the middle
2433 listSel = list->mSelectors; // use the new one for the pseudo
2435 listSel->mTag = pseudoElement;
2437 else { // append new pseudo element selector
2438 nsAutoPtr<nsCSSSelector> pseudoTagSelector(new nsCSSSelector());
2439 if (!pseudoTagSelector) {
2440 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2441 return PR_FALSE;
2443 pseudoTagSelector->mTag = pseudoClassList->mAtom; // steal ref count
2444 #ifdef MOZ_XUL
2445 if (IsTreePseudoElement(pseudoTagSelector->mTag)) {
2446 // Take the remaining "pseudoclasses" that we parsed
2447 // inside the tree pseudoelement's ()-list, and
2448 // make our new selector have these pseudoclasses
2449 // in its pseudoclass list.
2450 pseudoTagSelector->mPseudoClassList = pseudoClassList->mNext;
2451 pseudoClassList->mNext = nsnull;
2453 #endif
2454 list->AddSelector(pseudoTagSelector);
2455 pseudoClassList->mAtom = nsnull;
2456 listSel->mOperator = PRUnichar('>');
2457 if (nsnull == prevList) { // delete list entry
2458 listSel->mPseudoClassList = pseudoClassList->mNext;
2460 else {
2461 prevList->mNext = pseudoClassList->mNext;
2463 pseudoClassList->mNext = nsnull;
2464 delete pseudoClassList;
2465 weight += listSel->CalcWeight(); // capture weight from remainder
2467 break; // only one pseudo element per selector
2469 prevList = pseudoClassList;
2470 pseudoClassList = pseudoClassList->mNext;
2473 combinator = PRUnichar(0);
2474 if (!GetToken(PR_FALSE)) {
2475 break;
2478 // Assume we are done unless we find a combinator here.
2479 done = PR_TRUE;
2480 if (eCSSToken_WhiteSpace == mToken.mType) {
2481 if (!GetToken(PR_TRUE)) {
2482 break;
2484 done = PR_FALSE;
2487 if (eCSSToken_Symbol == mToken.mType &&
2488 ('+' == mToken.mSymbol ||
2489 '>' == mToken.mSymbol ||
2490 '~' == mToken.mSymbol)) {
2491 done = PR_FALSE;
2492 combinator = mToken.mSymbol;
2493 list->mSelectors->SetOperator(combinator);
2495 else {
2496 if (eCSSToken_Symbol == mToken.mType &&
2497 ('{' == mToken.mSymbol ||
2498 ',' == mToken.mSymbol)) {
2499 // End of this selector group
2500 done = PR_TRUE;
2502 UngetToken(); // give it back to selector if we're not done, or make sure
2503 // we see it as the end of the selector if we are.
2506 if (havePseudoElement) {
2507 break;
2509 else {
2510 weight += listSel->CalcWeight();
2514 if (PRUnichar(0) != combinator) { // no dangling combinators
2515 list = nsnull;
2516 // This should report the problematic combinator
2517 REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
2519 aList = list.forget();
2520 if (aList) {
2521 aList->mWeight = weight;
2523 return PRBool(nsnull != aList);
2526 #define SEL_MASK_NSPACE 0x01
2527 #define SEL_MASK_ELEM 0x02
2528 #define SEL_MASK_ID 0x04
2529 #define SEL_MASK_CLASS 0x08
2530 #define SEL_MASK_ATTRIB 0x10
2531 #define SEL_MASK_PCLASS 0x20
2532 #define SEL_MASK_PELEM 0x40
2535 // Parses an ID selector #name
2537 CSSParserImpl::nsSelectorParsingStatus
2538 CSSParserImpl::ParseIDSelector(PRInt32& aDataMask,
2539 nsCSSSelector& aSelector)
2541 NS_ASSERTION(!mToken.mIdent.IsEmpty(),
2542 "Empty mIdent in eCSSToken_ID token?");
2543 aDataMask |= SEL_MASK_ID;
2544 aSelector.AddID(mToken.mIdent);
2545 return eSelectorParsingStatus_Continue;
2549 // Parses a class selector .name
2551 CSSParserImpl::nsSelectorParsingStatus
2552 CSSParserImpl::ParseClassSelector(PRInt32& aDataMask,
2553 nsCSSSelector& aSelector)
2555 if (! GetToken(PR_FALSE)) { // get ident
2556 REPORT_UNEXPECTED_EOF(PEClassSelEOF);
2557 return eSelectorParsingStatus_Error;
2559 if (eCSSToken_Ident != mToken.mType) { // malformed selector
2560 REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
2561 UngetToken();
2562 return eSelectorParsingStatus_Error;
2564 aDataMask |= SEL_MASK_CLASS;
2566 aSelector.AddClass(mToken.mIdent);
2568 return eSelectorParsingStatus_Continue;
2572 // Parse a type element selector or a universal selector
2573 // namespace|type or namespace|* or *|* or *
2575 CSSParserImpl::nsSelectorParsingStatus
2576 CSSParserImpl::ParseTypeOrUniversalSelector(PRInt32& aDataMask,
2577 nsCSSSelector& aSelector,
2578 PRBool aIsNegated)
2580 nsAutoString buffer;
2581 if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace
2582 if (ExpectSymbol('|', PR_FALSE)) { // was namespace
2583 aDataMask |= SEL_MASK_NSPACE;
2584 aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
2586 if (! GetToken(PR_FALSE)) {
2587 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2588 return eSelectorParsingStatus_Error;
2590 if (eCSSToken_Ident == mToken.mType) { // element name
2591 aDataMask |= SEL_MASK_ELEM;
2592 if (mCaseSensitive) {
2593 aSelector.SetTag(mToken.mIdent);
2595 else {
2596 ToLowerCase(mToken.mIdent, buffer);
2597 aSelector.SetTag(buffer);
2600 else if (mToken.IsSymbol('*')) { // universal selector
2601 aDataMask |= SEL_MASK_ELEM;
2602 // don't set tag
2604 else {
2605 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2606 UngetToken();
2607 return eSelectorParsingStatus_Error;
2610 else { // was universal element selector
2611 SetDefaultNamespaceOnSelector(aSelector);
2612 aDataMask |= SEL_MASK_ELEM;
2613 // don't set any tag in the selector
2615 if (! GetToken(PR_FALSE)) { // premature eof is ok (here!)
2616 return eSelectorParsingStatus_Done;
2619 else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name
2620 buffer = mToken.mIdent; // hang on to ident
2622 if (ExpectSymbol('|', PR_FALSE)) { // was namespace
2623 aDataMask |= SEL_MASK_NSPACE;
2624 PRInt32 nameSpaceID;
2625 if (!GetNamespaceIdForPrefix(buffer, &nameSpaceID)) {
2626 return eSelectorParsingStatus_Error;
2628 aSelector.SetNameSpace(nameSpaceID);
2630 if (! GetToken(PR_FALSE)) {
2631 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2632 return eSelectorParsingStatus_Error;
2634 if (eCSSToken_Ident == mToken.mType) { // element name
2635 aDataMask |= SEL_MASK_ELEM;
2636 if (mCaseSensitive) {
2637 aSelector.SetTag(mToken.mIdent);
2639 else {
2640 ToLowerCase(mToken.mIdent, buffer);
2641 aSelector.SetTag(buffer);
2644 else if (mToken.IsSymbol('*')) { // universal selector
2645 aDataMask |= SEL_MASK_ELEM;
2646 // don't set tag
2648 else {
2649 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2650 UngetToken();
2651 return eSelectorParsingStatus_Error;
2654 else { // was element name
2655 SetDefaultNamespaceOnSelector(aSelector);
2656 if (mCaseSensitive) {
2657 aSelector.SetTag(buffer);
2659 else {
2660 ToLowerCase(buffer);
2661 aSelector.SetTag(buffer);
2663 aDataMask |= SEL_MASK_ELEM;
2665 if (! GetToken(PR_FALSE)) { // premature eof is ok (here!)
2666 return eSelectorParsingStatus_Done;
2669 else if (mToken.IsSymbol('|')) { // No namespace
2670 aDataMask |= SEL_MASK_NSPACE;
2671 aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace
2673 // get mandatory tag
2674 if (! GetToken(PR_FALSE)) {
2675 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2676 return eSelectorParsingStatus_Error;
2678 if (eCSSToken_Ident == mToken.mType) { // element name
2679 aDataMask |= SEL_MASK_ELEM;
2680 if (mCaseSensitive) {
2681 aSelector.SetTag(mToken.mIdent);
2683 else {
2684 ToLowerCase(mToken.mIdent, buffer);
2685 aSelector.SetTag(buffer);
2688 else if (mToken.IsSymbol('*')) { // universal selector
2689 aDataMask |= SEL_MASK_ELEM;
2690 // don't set tag
2692 else {
2693 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2694 UngetToken();
2695 return eSelectorParsingStatus_Error;
2697 if (! GetToken(PR_FALSE)) { // premature eof is ok (here!)
2698 return eSelectorParsingStatus_Done;
2701 else {
2702 SetDefaultNamespaceOnSelector(aSelector);
2705 if (aIsNegated) {
2706 // restore last token read in case of a negated type selector
2707 UngetToken();
2709 return eSelectorParsingStatus_Continue;
2713 // Parse attribute selectors [attr], [attr=value], [attr|=value],
2714 // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
2716 CSSParserImpl::nsSelectorParsingStatus
2717 CSSParserImpl::ParseAttributeSelector(PRInt32& aDataMask,
2718 nsCSSSelector& aSelector)
2720 if (! GetToken(PR_TRUE)) { // premature EOF
2721 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2722 return eSelectorParsingStatus_Error;
2725 PRInt32 nameSpaceID = kNameSpaceID_None;
2726 nsAutoString attr;
2727 if (mToken.IsSymbol('*')) { // wildcard namespace
2728 nameSpaceID = kNameSpaceID_Unknown;
2729 if (ExpectSymbol('|', PR_FALSE)) {
2730 if (! GetToken(PR_FALSE)) { // premature EOF
2731 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2732 return eSelectorParsingStatus_Error;
2734 if (eCSSToken_Ident == mToken.mType) { // attr name
2735 attr = mToken.mIdent;
2737 else {
2738 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2739 UngetToken();
2740 return eSelectorParsingStatus_Error;
2743 else {
2744 REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
2745 return eSelectorParsingStatus_Error;
2748 else if (mToken.IsSymbol('|')) { // NO namespace
2749 if (! GetToken(PR_FALSE)) { // premature EOF
2750 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2751 return eSelectorParsingStatus_Error;
2753 if (eCSSToken_Ident == mToken.mType) { // attr name
2754 attr = mToken.mIdent;
2756 else {
2757 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2758 UngetToken();
2759 return eSelectorParsingStatus_Error;
2762 else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
2763 attr = mToken.mIdent; // hang on to it
2764 if (ExpectSymbol('|', PR_FALSE)) { // was a namespace
2765 if (!GetNamespaceIdForPrefix(attr, &nameSpaceID)) {
2766 return eSelectorParsingStatus_Error;
2768 if (! GetToken(PR_FALSE)) { // premature EOF
2769 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2770 return eSelectorParsingStatus_Error;
2772 if (eCSSToken_Ident == mToken.mType) { // attr name
2773 attr = mToken.mIdent;
2775 else {
2776 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2777 UngetToken();
2778 return eSelectorParsingStatus_Error;
2782 else { // malformed
2783 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
2784 UngetToken();
2785 return eSelectorParsingStatus_Error;
2788 if (! mCaseSensitive) {
2789 ToLowerCase(attr);
2791 if (! GetToken(PR_TRUE)) { // premature EOF
2792 REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
2793 return eSelectorParsingStatus_Error;
2795 if ((eCSSToken_Symbol == mToken.mType) ||
2796 (eCSSToken_Includes == mToken.mType) ||
2797 (eCSSToken_Dashmatch == mToken.mType) ||
2798 (eCSSToken_Beginsmatch == mToken.mType) ||
2799 (eCSSToken_Endsmatch == mToken.mType) ||
2800 (eCSSToken_Containsmatch == mToken.mType)) {
2801 PRUint8 func;
2802 if (eCSSToken_Includes == mToken.mType) {
2803 func = NS_ATTR_FUNC_INCLUDES;
2805 else if (eCSSToken_Dashmatch == mToken.mType) {
2806 func = NS_ATTR_FUNC_DASHMATCH;
2808 else if (eCSSToken_Beginsmatch == mToken.mType) {
2809 func = NS_ATTR_FUNC_BEGINSMATCH;
2811 else if (eCSSToken_Endsmatch == mToken.mType) {
2812 func = NS_ATTR_FUNC_ENDSMATCH;
2814 else if (eCSSToken_Containsmatch == mToken.mType) {
2815 func = NS_ATTR_FUNC_CONTAINSMATCH;
2817 else if (']' == mToken.mSymbol) {
2818 aDataMask |= SEL_MASK_ATTRIB;
2819 aSelector.AddAttribute(nameSpaceID, attr);
2820 func = NS_ATTR_FUNC_SET;
2822 else if ('=' == mToken.mSymbol) {
2823 func = NS_ATTR_FUNC_EQUALS;
2825 else {
2826 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
2827 UngetToken(); // bad function
2828 return eSelectorParsingStatus_Error;
2830 if (NS_ATTR_FUNC_SET != func) { // get value
2831 if (! GetToken(PR_TRUE)) { // premature EOF
2832 REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
2833 return eSelectorParsingStatus_Error;
2835 if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
2836 nsAutoString value(mToken.mIdent);
2837 if (! GetToken(PR_TRUE)) { // premature EOF
2838 REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
2839 return eSelectorParsingStatus_Error;
2841 if (mToken.IsSymbol(']')) {
2842 PRBool isCaseSensitive = PR_TRUE;
2844 // If we're parsing a style sheet for an HTML document, and
2845 // the attribute selector is for a non-namespaced attribute,
2846 // then check to see if it's one of the known attributes whose
2847 // VALUE is case-insensitive.
2848 if (!mCaseSensitive && nameSpaceID == kNameSpaceID_None) {
2849 static const char* caseInsensitiveHTMLAttribute[] = {
2850 // list based on http://www.w3.org/TR/html4/
2851 "lang",
2852 "dir",
2853 "http-equiv",
2854 "text",
2855 "link",
2856 "vlink",
2857 "alink",
2858 "compact",
2859 "align",
2860 "frame",
2861 "rules",
2862 "valign",
2863 "scope",
2864 "axis",
2865 "nowrap",
2866 "hreflang",
2867 "rel",
2868 "rev",
2869 "charset",
2870 "codetype",
2871 "declare",
2872 "valuetype",
2873 "shape",
2874 "nohref",
2875 "media",
2876 "bgcolor",
2877 "clear",
2878 "color",
2879 "face",
2880 "noshade",
2881 "noresize",
2882 "scrolling",
2883 "target",
2884 "method",
2885 "enctype",
2886 "accept-charset",
2887 "accept",
2888 "checked",
2889 "multiple",
2890 "selected",
2891 "disabled",
2892 "readonly",
2893 "language",
2894 "defer",
2895 "type",
2896 // additional attributes not in HTML4
2897 "direction", // marquee
2898 nsnull
2900 short i = 0;
2901 const char* htmlAttr;
2902 while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) {
2903 if (attr.EqualsIgnoreCase(htmlAttr)) {
2904 isCaseSensitive = PR_FALSE;
2905 break;
2909 aDataMask |= SEL_MASK_ATTRIB;
2910 aSelector.AddAttribute(nameSpaceID, attr, func, value, isCaseSensitive);
2912 else {
2913 REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
2914 UngetToken();
2915 return eSelectorParsingStatus_Error;
2918 else {
2919 REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
2920 UngetToken();
2921 return eSelectorParsingStatus_Error;
2925 else {
2926 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
2927 UngetToken(); // bad dog, no biscut!
2928 return eSelectorParsingStatus_Error;
2930 return eSelectorParsingStatus_Continue;
2934 // Parse pseudo-classes and pseudo-elements
2936 CSSParserImpl::nsSelectorParsingStatus
2937 CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask,
2938 nsCSSSelector& aSelector,
2939 PRBool aIsNegated)
2941 if (! GetToken(PR_FALSE)) { // premature eof
2942 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
2943 return eSelectorParsingStatus_Error;
2946 // First, find out whether we are parsing a CSS3 pseudo-element
2947 PRBool parsingPseudoElement = PR_FALSE;
2948 if (mToken.IsSymbol(':')) {
2949 parsingPseudoElement = PR_TRUE;
2950 if (! GetToken(PR_FALSE)) { // premature eof
2951 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
2952 return eSelectorParsingStatus_Error;
2956 // Do some sanity-checking on the token
2957 if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
2958 // malformed selector
2959 REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
2960 UngetToken();
2961 return eSelectorParsingStatus_Error;
2964 // OK, now we know we have an mIdent. Atomize it. All the atoms, for
2965 // pseudo-classes as well as pseudo-elements, start with a single ':'.
2966 nsAutoString buffer;
2967 buffer.Append(PRUnichar(':'));
2968 buffer.Append(mToken.mIdent);
2969 ToLowerCase(buffer);
2970 nsCOMPtr<nsIAtom> pseudo = do_GetAtom(buffer);
2972 // stash away some info about this pseudo so we only have to get it once.
2973 PRBool isTreePseudo = PR_FALSE;
2974 #ifdef MOZ_XUL
2975 isTreePseudo = IsTreePseudoElement(pseudo);
2976 // If a tree pseudo-element is using the function syntax, it will
2977 // get isTree set here and will pass the check below that only
2978 // allows functions if they are in our list of things allowed to be
2979 // functions. If it is _not_ using the function syntax, isTree will
2980 // be false, and it will still pass that check. So the tree
2981 // pseudo-elements are allowed to be either functions or not, as
2982 // desired.
2983 PRBool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
2984 #endif
2985 PRBool isPseudoElement = nsCSSPseudoElements::IsPseudoElement(pseudo);
2986 // anonymous boxes are only allowed if they're the tree boxes or we have
2987 // enabled unsafe rules
2988 PRBool isAnonBox = nsCSSAnonBoxes::IsAnonBox(pseudo) &&
2989 (mUnsafeRulesEnabled || isTreePseudo);
2990 PRBool isPseudoClass = nsCSSPseudoClasses::IsPseudoClass(pseudo);
2992 if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
2993 // Not a pseudo-class, not a pseudo-element.... forget it
2994 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
2995 UngetToken();
2996 return eSelectorParsingStatus_Error;
2999 // If it's a function token, it better be on our "ok" list, and if the name
3000 // is that of a function pseudo it better be a function token
3001 if ((eCSSToken_Function == mToken.mType) !=
3003 #ifdef MOZ_XUL
3004 isTree ||
3005 #endif
3006 nsCSSPseudoClasses::notPseudo == pseudo ||
3007 nsCSSPseudoClasses::HasStringArg(pseudo) ||
3008 nsCSSPseudoClasses::HasNthPairArg(pseudo))) {
3009 // There are no other function pseudos
3010 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
3011 UngetToken();
3012 return eSelectorParsingStatus_Error;
3015 // If it starts with "::", it better be a pseudo-element
3016 if (parsingPseudoElement &&
3017 !isPseudoElement &&
3018 !isAnonBox) {
3019 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
3020 UngetToken();
3021 return eSelectorParsingStatus_Error;
3024 if (!parsingPseudoElement && nsCSSPseudoClasses::notPseudo == pseudo) {
3025 if (aIsNegated) { // :not() can't be itself negated
3026 REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
3027 UngetToken();
3028 return eSelectorParsingStatus_Error;
3030 // CSS 3 Negation pseudo-class takes one simple selector as argument
3031 nsSelectorParsingStatus parsingStatus =
3032 ParseNegatedSimpleSelector(aDataMask, aSelector);
3033 if (eSelectorParsingStatus_Continue != parsingStatus) {
3034 return parsingStatus;
3037 else if (!parsingPseudoElement && isPseudoClass) {
3038 aDataMask |= SEL_MASK_PCLASS;
3039 if (nsCSSPseudoClasses::HasStringArg(pseudo)) {
3040 nsSelectorParsingStatus parsingStatus =
3041 ParsePseudoClassWithIdentArg(aSelector, pseudo);
3042 if (eSelectorParsingStatus_Continue != parsingStatus) {
3043 return parsingStatus;
3046 else if (nsCSSPseudoClasses::HasNthPairArg(pseudo)) {
3047 nsSelectorParsingStatus parsingStatus =
3048 ParsePseudoClassWithNthPairArg(aSelector, pseudo);
3049 if (eSelectorParsingStatus_Continue != parsingStatus) {
3050 return parsingStatus;
3053 else {
3054 aSelector.AddPseudoClass(pseudo);
3057 else if (isPseudoElement || isAnonBox) {
3058 // Pseudo-element. Make some more sanity checks.
3060 if (aIsNegated) { // pseudo-elements can't be negated
3061 REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
3062 UngetToken();
3063 return eSelectorParsingStatus_Error;
3065 // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
3066 // to have a single ':' on them. Others (CSS3+ pseudo-elements and
3067 // various -moz-* pseudo-elements) must have |parsingPseudoElement|
3068 // set.
3069 if (!parsingPseudoElement &&
3070 !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
3071 #ifdef MOZ_XUL
3072 && !isTreePseudo
3073 #endif
3075 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
3076 UngetToken();
3077 return eSelectorParsingStatus_Error;
3080 if (0 == (aDataMask & SEL_MASK_PELEM)) {
3081 aDataMask |= SEL_MASK_PELEM;
3082 aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later
3084 #ifdef MOZ_XUL
3085 if (isTree) {
3086 // We have encountered a pseudoelement of the form
3087 // -moz-tree-xxxx(a,b,c). We parse (a,b,c) and add each
3088 // item in the list to the pseudoclass list. They will be pulled
3089 // from the list later along with the pseudo-element.
3090 if (!ParseTreePseudoElement(aSelector)) {
3091 return eSelectorParsingStatus_Error;
3094 #endif
3096 // ensure selector ends here, must be followed by EOF, space, '{' or ','
3097 if (GetToken(PR_FALSE)) { // premature eof is ok (here!)
3098 if ((eCSSToken_WhiteSpace == mToken.mType) ||
3099 (mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
3100 UngetToken();
3101 return eSelectorParsingStatus_Done;
3103 REPORT_UNEXPECTED_TOKEN(PEPseudoSelTrailing);
3104 UngetToken();
3105 return eSelectorParsingStatus_Error;
3108 else { // multiple pseudo elements, not legal
3109 REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
3110 UngetToken();
3111 return eSelectorParsingStatus_Error;
3114 #ifdef DEBUG
3115 else {
3116 // We should never end up here. Indeed, if we ended up here, we know (from
3117 // the current if/else cascade) that !isPseudoElement and !isAnonBox. But
3118 // then due to our earlier check we know that isPseudoClass. Since we
3119 // didn't fall into the isPseudoClass case in this cascade, we must have
3120 // parsingPseudoElement. But we've already checked the
3121 // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
3122 // it's happened.
3123 NS_NOTREACHED("How did this happen?");
3125 #endif
3126 return eSelectorParsingStatus_Continue;
3130 // Parse the argument of a negation pseudo-class :not()
3132 CSSParserImpl::nsSelectorParsingStatus
3133 CSSParserImpl::ParseNegatedSimpleSelector(PRInt32& aDataMask,
3134 nsCSSSelector& aSelector)
3136 // Check if we have the first parenthesis
3137 if (!ExpectSymbol('(', PR_FALSE)) {
3138 REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
3139 return eSelectorParsingStatus_Error;
3142 if (! GetToken(PR_TRUE)) { // premature eof
3143 REPORT_UNEXPECTED_EOF(PENegationEOF);
3144 return eSelectorParsingStatus_Error;
3147 // Create a new nsCSSSelector and add it to the end of
3148 // aSelector.mNegations.
3149 // Given the current parsing rules, every selector in mNegations
3150 // contains only one simple selector (css3 definition) within it.
3151 // This could easily change in future versions of CSS, and the only
3152 // thing we need to change to support that is this parsing code.
3153 nsCSSSelector *newSel = new nsCSSSelector();
3154 if (!newSel) {
3155 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
3156 return eSelectorParsingStatus_Error;
3158 nsCSSSelector* negations = &aSelector;
3159 while (negations->mNegations) {
3160 negations = negations->mNegations;
3162 negations->mNegations = newSel;
3164 nsSelectorParsingStatus parsingStatus;
3165 if (eCSSToken_ID == mToken.mType) { // #id
3166 parsingStatus = ParseIDSelector(aDataMask, *newSel);
3168 else if (mToken.IsSymbol('.')) { // .class
3169 parsingStatus = ParseClassSelector(aDataMask, *newSel);
3171 else if (mToken.IsSymbol(':')) { // :pseudo
3172 parsingStatus = ParsePseudoSelector(aDataMask, *newSel, PR_TRUE);
3174 else if (mToken.IsSymbol('[')) { // [attribute
3175 parsingStatus = ParseAttributeSelector(aDataMask, *newSel);
3177 else {
3178 // then it should be a type element or universal selector
3179 parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, PR_TRUE);
3181 if (eSelectorParsingStatus_Error == parsingStatus) {
3182 REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
3183 return parsingStatus;
3185 // close the parenthesis
3186 if (!ExpectSymbol(')', PR_TRUE)) {
3187 REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
3188 return eSelectorParsingStatus_Error;
3191 return eSelectorParsingStatus_Continue;
3195 // Parse the argument of a pseudo-class that has an ident arg
3197 CSSParserImpl::nsSelectorParsingStatus
3198 CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
3199 nsIAtom* aPseudo)
3201 // Check if we have the first parenthesis
3202 if (!ExpectSymbol('(', PR_FALSE)) {
3203 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoArg);
3204 return eSelectorParsingStatus_Error;
3207 if (! GetToken(PR_TRUE)) { // premature eof
3208 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3209 return eSelectorParsingStatus_Error;
3211 // We expect an identifier with a language abbreviation
3212 if (eCSSToken_Ident != mToken.mType) {
3213 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent);
3214 UngetToken();
3215 // XXX Call SkipUntil to the next ")"?
3216 return eSelectorParsingStatus_Error;
3219 // Add the pseudo with the language parameter
3220 aSelector.AddPseudoClass(aPseudo, mToken.mIdent.get());
3222 // close the parenthesis
3223 if (!ExpectSymbol(')', PR_TRUE)) {
3224 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
3225 // XXX Call SkipUntil to the next ")"?
3226 return eSelectorParsingStatus_Error;
3229 return eSelectorParsingStatus_Continue;
3232 CSSParserImpl::nsSelectorParsingStatus
3233 CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
3234 nsIAtom* aPseudo)
3236 PRInt32 numbers[2] = { 0, 0 };
3237 PRBool lookForB = PR_TRUE;
3239 // Check whether we have the first parenthesis
3240 if (!ExpectSymbol('(', PR_FALSE)) {
3241 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoArg);
3242 return eSelectorParsingStatus_Error;
3245 // Follow the whitespace rules as proposed in
3246 // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
3248 if (! GetToken(PR_TRUE)) {
3249 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3250 return eSelectorParsingStatus_Error;
3253 if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
3254 // The CSS tokenization doesn't handle :nth-child() containing - well:
3255 // 2n-1 is a dimension
3256 // n-1 is an identifier
3257 // The easiest way to deal with that is to push everything from the
3258 // minus on back onto the scanner's pushback buffer.
3259 PRUint32 truncAt = 0;
3260 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
3261 truncAt = 1;
3262 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("-n-"))) {
3263 truncAt = 2;
3265 if (truncAt != 0) {
3266 for (PRUint32 i = mToken.mIdent.Length() - 1; i >= truncAt; --i) {
3267 mScanner.Pushback(mToken.mIdent[i]);
3269 mToken.mIdent.Truncate(truncAt);
3273 if (eCSSToken_Ident == mToken.mType) {
3274 if (mToken.mIdent.EqualsIgnoreCase("odd")) {
3275 numbers[0] = 2;
3276 numbers[1] = 1;
3277 lookForB = PR_FALSE;
3279 else if (mToken.mIdent.EqualsIgnoreCase("even")) {
3280 numbers[0] = 2;
3281 numbers[1] = 0;
3282 lookForB = PR_FALSE;
3284 else if (mToken.mIdent.EqualsIgnoreCase("n")) {
3285 numbers[0] = 1;
3287 else if (mToken.mIdent.EqualsIgnoreCase("-n")) {
3288 numbers[0] = -1;
3290 else {
3291 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3292 // XXX Call SkipUntil to the next ")"?
3293 return eSelectorParsingStatus_Error;
3296 else if (eCSSToken_Number == mToken.mType) {
3297 if (!mToken.mIntegerValid) {
3298 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3299 // XXX Call SkipUntil to the next ")"?
3300 return eSelectorParsingStatus_Error;
3302 numbers[1] = mToken.mInteger;
3303 lookForB = PR_FALSE;
3305 else if (eCSSToken_Dimension == mToken.mType) {
3306 if (!mToken.mIntegerValid || !mToken.mIdent.EqualsIgnoreCase("n")) {
3307 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3308 // XXX Call SkipUntil to the next ")"?
3309 return eSelectorParsingStatus_Error;
3311 numbers[0] = mToken.mInteger;
3313 // XXX If it's a ')', is that valid? (as 0n+0)
3314 else {
3315 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3316 // XXX Call SkipUntil to the next ")" (unless this is one already)?
3317 return eSelectorParsingStatus_Error;
3320 if (! GetToken(PR_TRUE)) {
3321 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3322 return eSelectorParsingStatus_Error;
3324 if (lookForB && !mToken.IsSymbol(')')) {
3325 // The '+' or '-' sign can optionally be separated by whitespace.
3326 // If it is separated by whitespace from what follows it, it appears
3327 // as a separate token rather than part of the number token.
3328 PRBool haveSign = PR_FALSE;
3329 PRInt32 sign = 1;
3330 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
3331 haveSign = PR_TRUE;
3332 if (mToken.IsSymbol('-')) {
3333 sign = -1;
3335 if (! GetToken(PR_TRUE)) {
3336 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3337 return eSelectorParsingStatus_Error;
3340 if (eCSSToken_Number != mToken.mType ||
3341 !mToken.mIntegerValid || mToken.mHasSign == haveSign) {
3342 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3343 // XXX Call SkipUntil to the next ")"?
3344 return eSelectorParsingStatus_Error;
3346 numbers[1] = mToken.mInteger * sign;
3347 if (! GetToken(PR_TRUE)) {
3348 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3349 return eSelectorParsingStatus_Error;
3352 if (!mToken.IsSymbol(')')) {
3353 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
3354 // XXX Call SkipUntil to the next ")"?
3355 return eSelectorParsingStatus_Error;
3357 aSelector.AddPseudoClass(aPseudo, numbers);
3358 return eSelectorParsingStatus_Continue;
3363 * This is the format for selectors:
3364 * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
3366 CSSParserImpl::nsSelectorParsingStatus
3367 CSSParserImpl::ParseSelector(nsCSSSelector& aSelector)
3369 if (! GetToken(PR_TRUE)) {
3370 REPORT_UNEXPECTED_EOF(PESelectorEOF);
3371 return eSelectorParsingStatus_Error;
3374 PRInt32 dataMask = 0;
3375 nsSelectorParsingStatus parsingStatus =
3376 ParseTypeOrUniversalSelector(dataMask, aSelector, PR_FALSE);
3377 if (parsingStatus != eSelectorParsingStatus_Continue) {
3378 return parsingStatus;
3381 for (;;) {
3382 if (eCSSToken_ID == mToken.mType) { // #id
3383 parsingStatus = ParseIDSelector(dataMask, aSelector);
3385 else if (mToken.IsSymbol('.')) { // .class
3386 parsingStatus = ParseClassSelector(dataMask, aSelector);
3388 else if (mToken.IsSymbol(':')) { // :pseudo
3389 parsingStatus = ParsePseudoSelector(dataMask, aSelector, PR_FALSE);
3391 else if (mToken.IsSymbol('[')) { // [attribute
3392 parsingStatus = ParseAttributeSelector(dataMask, aSelector);
3394 else { // not a selector token, we're done
3395 parsingStatus = eSelectorParsingStatus_Done;
3396 break;
3399 if (parsingStatus != eSelectorParsingStatus_Continue) {
3400 return parsingStatus;
3403 if (! GetToken(PR_FALSE)) { // premature eof is ok (here!)
3404 return eSelectorParsingStatus_Done;
3407 UngetToken();
3408 return dataMask ? parsingStatus : eSelectorParsingStatus_Empty;
3411 nsCSSDeclaration*
3412 CSSParserImpl::ParseDeclarationBlock(PRBool aCheckForBraces)
3414 if (aCheckForBraces) {
3415 if (!ExpectSymbol('{', PR_TRUE)) {
3416 REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
3417 OUTPUT_ERROR();
3418 return nsnull;
3421 nsCSSDeclaration* declaration = new nsCSSDeclaration();
3422 mData.AssertInitialState();
3423 if (declaration) {
3424 for (;;) {
3425 PRBool changed;
3426 if (!ParseDeclaration(declaration, aCheckForBraces,
3427 PR_TRUE, &changed)) {
3428 if (!SkipDeclaration(aCheckForBraces)) {
3429 break;
3431 if (aCheckForBraces) {
3432 if (ExpectSymbol('}', PR_TRUE)) {
3433 break;
3436 // Since the skipped declaration didn't end the block we parse
3437 // the next declaration.
3440 declaration->CompressFrom(&mData);
3442 return declaration;
3445 // The types to pass to ParseColorComponent. These correspond to the
3446 // various datatypes that can go within rgb().
3447 #define COLOR_TYPE_UNKNOWN 0
3448 #define COLOR_TYPE_INTEGERS 1
3449 #define COLOR_TYPE_PERCENTAGES 2
3451 PRBool
3452 CSSParserImpl::ParseColor(nsCSSValue& aValue)
3454 if (!GetToken(PR_TRUE)) {
3455 REPORT_UNEXPECTED_EOF(PEColorEOF);
3456 return PR_FALSE;
3459 nsCSSToken* tk = &mToken;
3460 nscolor rgba;
3461 switch (tk->mType) {
3462 case eCSSToken_ID:
3463 case eCSSToken_Ref:
3464 // #xxyyzz
3465 if (NS_HexToRGB(tk->mIdent, &rgba)) {
3466 aValue.SetColorValue(rgba);
3467 return PR_TRUE;
3469 break;
3471 case eCSSToken_Ident:
3472 if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
3473 aValue.SetStringValue(tk->mIdent, eCSSUnit_String);
3474 return PR_TRUE;
3476 else {
3477 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
3478 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
3479 PRInt32 value;
3480 if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) {
3481 aValue.SetIntValue(value, eCSSUnit_EnumColor);
3482 return PR_TRUE;
3486 break;
3487 case eCSSToken_Function:
3488 if (mToken.mIdent.LowerCaseEqualsLiteral("rgb")) {
3489 // rgb ( component , component , component )
3490 PRUint8 r, g, b;
3491 PRInt32 type = COLOR_TYPE_UNKNOWN;
3492 if (ExpectSymbol('(', PR_FALSE) && // this won't fail
3493 ParseColorComponent(r, type, ',') &&
3494 ParseColorComponent(g, type, ',') &&
3495 ParseColorComponent(b, type, ')')) {
3496 aValue.SetColorValue(NS_RGB(r,g,b));
3497 return PR_TRUE;
3499 return PR_FALSE; // already pushed back
3501 else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-rgba") ||
3502 mToken.mIdent.LowerCaseEqualsLiteral("rgba")) {
3503 // rgba ( component , component , component , opacity )
3504 PRUint8 r, g, b, a;
3505 PRInt32 type = COLOR_TYPE_UNKNOWN;
3506 if (ExpectSymbol('(', PR_FALSE) && // this won't fail
3507 ParseColorComponent(r, type, ',') &&
3508 ParseColorComponent(g, type, ',') &&
3509 ParseColorComponent(b, type, ',') &&
3510 ParseColorOpacity(a)) {
3511 aValue.SetColorValue(NS_RGBA(r, g, b, a));
3512 return PR_TRUE;
3514 return PR_FALSE; // already pushed back
3516 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
3517 // hsl ( hue , saturation , lightness )
3518 // "hue" is a number, "saturation" and "lightness" are percentages.
3519 if (ParseHSLColor(rgba, ')')) {
3520 aValue.SetColorValue(rgba);
3521 return PR_TRUE;
3523 return PR_FALSE;
3525 else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-hsla") ||
3526 mToken.mIdent.LowerCaseEqualsLiteral("hsla")) {
3527 // hsla ( hue , saturation , lightness , opacity )
3528 // "hue" is a number, "saturation" and "lightness" are percentages,
3529 // "opacity" is a number.
3530 PRUint8 a;
3531 if (ParseHSLColor(rgba, ',') &&
3532 ParseColorOpacity(a)) {
3533 aValue.SetColorValue(NS_RGBA(NS_GET_R(rgba), NS_GET_G(rgba),
3534 NS_GET_B(rgba), a));
3535 return PR_TRUE;
3537 return PR_FALSE;
3539 break;
3540 default:
3541 break;
3544 // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
3545 if (mNavQuirkMode && !IsParsingCompoundProperty()) {
3546 // - If the string starts with 'a-f', the nsCSSScanner builds the
3547 // token as a eCSSToken_Ident and we can parse the string as a
3548 // 'xxyyzz' RGB color.
3549 // - If it only contains '0-9' digits, the token is a
3550 // eCSSToken_Number and it must be converted back to a 6
3551 // characters string to be parsed as a RGB color.
3552 // - If it starts with '0-9' and contains any 'a-f', the token is a
3553 // eCSSToken_Dimension, the mNumber part must be converted back to
3554 // a string and the mIdent part must be appended to that string so
3555 // that the resulting string has 6 characters.
3556 // Note: This is a hack for Nav compatibility. Do not attempt to
3557 // simplify it by hacking into the ncCSSScanner. This would be very
3558 // bad.
3559 nsAutoString str;
3560 char buffer[20];
3561 switch (tk->mType) {
3562 case eCSSToken_Ident:
3563 str.Assign(tk->mIdent);
3564 break;
3566 case eCSSToken_Number:
3567 if (tk->mIntegerValid) {
3568 PR_snprintf(buffer, sizeof(buffer), "%06d", tk->mInteger);
3569 str.AssignWithConversion(buffer);
3571 break;
3573 case eCSSToken_Dimension:
3574 if (tk->mIdent.Length() <= 6) {
3575 PR_snprintf(buffer, sizeof(buffer), "%06.0f", tk->mNumber);
3576 nsAutoString temp;
3577 temp.AssignWithConversion(buffer);
3578 temp.Right(str, 6 - tk->mIdent.Length());
3579 str.Append(tk->mIdent);
3581 break;
3582 default:
3583 // There is a whole bunch of cases that are
3584 // not handled by this switch. Ignore them.
3585 break;
3587 if (NS_HexToRGB(str, &rgba)) {
3588 aValue.SetColorValue(rgba);
3589 return PR_TRUE;
3593 // It's not a color
3594 REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
3595 UngetToken();
3596 return PR_FALSE;
3599 // aType will be set if we have already parsed other color components
3600 // in this color spec
3601 PRBool
3602 CSSParserImpl::ParseColorComponent(PRUint8& aComponent,
3603 PRInt32& aType,
3604 char aStop)
3606 if (!GetToken(PR_TRUE)) {
3607 REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
3608 return PR_FALSE;
3610 float value;
3611 nsCSSToken* tk = &mToken;
3612 switch (tk->mType) {
3613 case eCSSToken_Number:
3614 switch (aType) {
3615 case COLOR_TYPE_UNKNOWN:
3616 aType = COLOR_TYPE_INTEGERS;
3617 break;
3618 case COLOR_TYPE_INTEGERS:
3619 break;
3620 case COLOR_TYPE_PERCENTAGES:
3621 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3622 UngetToken();
3623 return PR_FALSE;
3624 default:
3625 NS_NOTREACHED("Someone forgot to add the new color component type in here");
3628 if (!mToken.mIntegerValid) {
3629 REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
3630 UngetToken();
3631 return PR_FALSE;
3633 value = tk->mNumber;
3634 break;
3635 case eCSSToken_Percentage:
3636 switch (aType) {
3637 case COLOR_TYPE_UNKNOWN:
3638 aType = COLOR_TYPE_PERCENTAGES;
3639 break;
3640 case COLOR_TYPE_INTEGERS:
3641 REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
3642 UngetToken();
3643 return PR_FALSE;
3644 case COLOR_TYPE_PERCENTAGES:
3645 break;
3646 default:
3647 NS_NOTREACHED("Someone forgot to add the new color component type in here");
3649 value = tk->mNumber * 255.0f;
3650 break;
3651 default:
3652 REPORT_UNEXPECTED_TOKEN(PEColorBadRGBContents);
3653 UngetToken();
3654 return PR_FALSE;
3656 if (ExpectSymbol(aStop, PR_TRUE)) {
3657 if (value < 0.0f) value = 0.0f;
3658 if (value > 255.0f) value = 255.0f;
3659 aComponent = NSToIntRound(value);
3660 return PR_TRUE;
3662 const PRUnichar stopString[] = { PRUnichar(aStop), PRUnichar(0) };
3663 const PRUnichar *params[] = {
3664 nsnull,
3665 stopString
3667 REPORT_UNEXPECTED_TOKEN_P(PEColorComponentBadTerm, params);
3668 return PR_FALSE;
3672 PRBool
3673 CSSParserImpl::ParseHSLColor(nscolor& aColor,
3674 char aStop)
3676 float h, s, l;
3677 if (!ExpectSymbol('(', PR_FALSE)) {
3678 NS_ERROR("How did this get to be a function token?");
3679 return PR_FALSE;
3682 // Get the hue
3683 if (!GetToken(PR_TRUE)) {
3684 REPORT_UNEXPECTED_EOF(PEColorHueEOF);
3685 return PR_FALSE;
3687 if (mToken.mType != eCSSToken_Number) {
3688 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
3689 UngetToken();
3690 return PR_FALSE;
3692 h = mToken.mNumber;
3693 h /= 360.0f;
3694 // hue values are wraparound
3695 h = h - floor(h);
3697 if (!ExpectSymbol(',', PR_TRUE)) {
3698 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
3699 return PR_FALSE;
3702 // Get the saturation
3703 if (!GetToken(PR_TRUE)) {
3704 REPORT_UNEXPECTED_EOF(PEColorSaturationEOF);
3705 return PR_FALSE;
3707 if (mToken.mType != eCSSToken_Percentage) {
3708 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3709 UngetToken();
3710 return PR_FALSE;
3712 s = mToken.mNumber;
3713 if (s < 0.0f) s = 0.0f;
3714 if (s > 1.0f) s = 1.0f;
3716 if (!ExpectSymbol(',', PR_TRUE)) {
3717 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
3718 return PR_FALSE;
3721 // Get the lightness
3722 if (!GetToken(PR_TRUE)) {
3723 REPORT_UNEXPECTED_EOF(PEColorLightnessEOF);
3724 return PR_FALSE;
3726 if (mToken.mType != eCSSToken_Percentage) {
3727 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3728 UngetToken();
3729 return PR_FALSE;
3731 l = mToken.mNumber;
3732 if (l < 0.0f) l = 0.0f;
3733 if (l > 1.0f) l = 1.0f;
3735 if (ExpectSymbol(aStop, PR_TRUE)) {
3736 aColor = NS_HSL2RGB(h, s, l);
3737 return PR_TRUE;
3740 const PRUnichar stopString[] = { PRUnichar(aStop), PRUnichar(0) };
3741 const PRUnichar *params[] = {
3742 nsnull,
3743 stopString
3745 REPORT_UNEXPECTED_TOKEN_P(PEColorComponentBadTerm, params);
3746 return PR_FALSE;
3750 PRBool
3751 CSSParserImpl::ParseColorOpacity(PRUint8& aOpacity)
3753 if (!GetToken(PR_TRUE)) {
3754 REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
3755 return PR_FALSE;
3758 if (mToken.mType != eCSSToken_Number) {
3759 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
3760 UngetToken();
3761 return PR_FALSE;
3764 if (mToken.mNumber < 0.0f) {
3765 mToken.mNumber = 0.0f;
3766 } else if (mToken.mNumber > 1.0f) {
3767 mToken.mNumber = 1.0f;
3770 PRUint8 value = nsStyleUtil::FloatToColorComponent(mToken.mNumber);
3771 NS_ASSERTION(fabs(mToken.mNumber - value/255.0f) <= 0.5f,
3772 "FloatToColorComponent did something weird");
3774 if (!ExpectSymbol(')', PR_TRUE)) {
3775 REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
3776 return PR_FALSE;
3779 aOpacity = value;
3781 return PR_TRUE;
3784 #ifdef MOZ_XUL
3785 PRBool
3786 CSSParserImpl::ParseTreePseudoElement(nsCSSSelector& aSelector)
3788 if (ExpectSymbol('(', PR_FALSE)) {
3789 while (!ExpectSymbol(')', PR_TRUE)) {
3790 if (!GetToken(PR_TRUE)) {
3791 return PR_FALSE;
3793 else if (eCSSToken_Ident == mToken.mType) {
3794 nsCOMPtr<nsIAtom> pseudo = do_GetAtom(mToken.mIdent);
3795 aSelector.AddPseudoClass(pseudo);
3797 else if (eCSSToken_Symbol == mToken.mType) {
3798 if (!mToken.IsSymbol(','))
3799 return PR_FALSE;
3801 else return PR_FALSE;
3803 return PR_TRUE;
3805 return PR_FALSE;
3807 #endif
3809 //----------------------------------------------------------------------
3811 PRBool
3812 CSSParserImpl::ParseDeclaration(nsCSSDeclaration* aDeclaration,
3813 PRBool aCheckForBraces,
3814 PRBool aMustCallValueAppended,
3815 PRBool* aChanged)
3817 mTempData.AssertInitialState();
3819 // Get property name
3820 nsCSSToken* tk = &mToken;
3821 nsAutoString propertyName;
3822 for (;;) {
3823 if (!GetToken(PR_TRUE)) {
3824 if (aCheckForBraces) {
3825 REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
3827 return PR_FALSE;
3829 if (eCSSToken_Ident == tk->mType) {
3830 propertyName = tk->mIdent;
3831 // grab the ident before the ExpectSymbol trashes the token
3832 if (!ExpectSymbol(':', PR_TRUE)) {
3833 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
3834 REPORT_UNEXPECTED(PEDeclDropped);
3835 OUTPUT_ERROR();
3836 return PR_FALSE;
3838 break;
3840 if (tk->IsSymbol(';')) {
3841 // dangling semicolons are skipped
3842 continue;
3845 if (!tk->IsSymbol('}')) {
3846 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
3847 REPORT_UNEXPECTED(PEDeclSkipped);
3848 OUTPUT_ERROR();
3850 // Not a declaration...
3851 UngetToken();
3852 return PR_FALSE;
3855 // Map property name to its ID and then parse the property
3856 nsCSSProperty propID = nsCSSProps::LookupProperty(propertyName);
3857 if (eCSSProperty_UNKNOWN == propID) { // unknown property
3858 if (!NonMozillaVendorIdentifier(propertyName)) {
3859 const PRUnichar *params[] = {
3860 propertyName.get()
3862 REPORT_UNEXPECTED_P(PEUnknownProperty, params);
3863 REPORT_UNEXPECTED(PEDeclDropped);
3864 OUTPUT_ERROR();
3867 return PR_FALSE;
3869 if (! ParseProperty(propID)) {
3870 // XXX Much better to put stuff in the value parsers instead...
3871 const PRUnichar *params[] = {
3872 propertyName.get()
3874 REPORT_UNEXPECTED_P(PEValueParsingError, params);
3875 REPORT_UNEXPECTED(PEDeclDropped);
3876 OUTPUT_ERROR();
3877 ClearTempData(propID);
3878 return PR_FALSE;
3880 CLEAR_ERROR();
3882 // See if the declaration is followed by a "!important" declaration
3883 PRBool isImportant = PR_FALSE;
3884 if (!GetToken(PR_TRUE)) {
3885 // EOF is a perfectly good way to end a declaration and declaration block
3886 TransferTempData(aDeclaration, propID, isImportant,
3887 aMustCallValueAppended, aChanged);
3888 return PR_TRUE;
3891 if (eCSSToken_Symbol == tk->mType && '!' == tk->mSymbol) {
3892 // Look for important ident
3893 if (!GetToken(PR_TRUE)) {
3894 // Premature eof is not ok
3895 REPORT_UNEXPECTED_EOF(PEImportantEOF);
3896 ClearTempData(propID);
3897 return PR_FALSE;
3899 if ((eCSSToken_Ident != tk->mType) ||
3900 !tk->mIdent.LowerCaseEqualsLiteral("important")) {
3901 REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
3902 OUTPUT_ERROR();
3903 UngetToken();
3904 ClearTempData(propID);
3905 return PR_FALSE;
3907 isImportant = PR_TRUE;
3909 else {
3910 // Not a !important declaration
3911 UngetToken();
3914 // Make sure valid property declaration is terminated with either a
3915 // semicolon, EOF or a right-curly-brace (this last only when
3916 // aCheckForBraces is true).
3917 if (!GetToken(PR_TRUE)) {
3918 // EOF is a perfectly good way to end a declaration and declaration block
3919 TransferTempData(aDeclaration, propID, isImportant,
3920 aMustCallValueAppended, aChanged);
3921 return PR_TRUE;
3923 if (eCSSToken_Symbol == tk->mType) {
3924 if (';' == tk->mSymbol) {
3925 TransferTempData(aDeclaration, propID, isImportant,
3926 aMustCallValueAppended, aChanged);
3927 return PR_TRUE;
3929 if (aCheckForBraces && '}' == tk->mSymbol) {
3930 // Unget the '}' so we'll be able to tell that this is the end
3931 // of the declaration block when we unwind from here.
3932 UngetToken();
3933 TransferTempData(aDeclaration, propID, isImportant,
3934 aMustCallValueAppended, aChanged);
3935 return PR_TRUE;
3938 if (aCheckForBraces)
3939 REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2);
3940 else
3941 REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
3942 REPORT_UNEXPECTED(PEDeclDropped);
3943 OUTPUT_ERROR();
3944 ClearTempData(propID);
3945 return PR_FALSE;
3948 void
3949 CSSParserImpl::ClearTempData(nsCSSProperty aPropID)
3951 if (nsCSSProps::IsShorthand(aPropID)) {
3952 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
3953 mTempData.ClearProperty(*p);
3955 } else {
3956 mTempData.ClearProperty(aPropID);
3958 mTempData.AssertInitialState();
3961 void
3962 CSSParserImpl::TransferTempData(nsCSSDeclaration* aDeclaration,
3963 nsCSSProperty aPropID, PRBool aIsImportant,
3964 PRBool aMustCallValueAppended,
3965 PRBool* aChanged)
3967 if (nsCSSProps::IsShorthand(aPropID)) {
3968 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
3969 DoTransferTempData(aDeclaration, *p, aIsImportant,
3970 aMustCallValueAppended, aChanged);
3972 } else {
3973 DoTransferTempData(aDeclaration, aPropID, aIsImportant,
3974 aMustCallValueAppended, aChanged);
3976 mTempData.AssertInitialState();
3979 // Perhaps the transferring code should be in nsCSSExpandedDataBlock, in
3980 // case some other caller wants to use it in the future (although I
3981 // can't think of why).
3982 void
3983 CSSParserImpl::DoTransferTempData(nsCSSDeclaration* aDeclaration,
3984 nsCSSProperty aPropID, PRBool aIsImportant,
3985 PRBool aMustCallValueAppended,
3986 PRBool* aChanged)
3988 NS_ASSERTION(mTempData.HasPropertyBit(aPropID), "oops");
3989 if (aIsImportant) {
3990 if (!mData.HasImportantBit(aPropID))
3991 *aChanged = PR_TRUE;
3992 mData.SetImportantBit(aPropID);
3993 } else {
3994 if (mData.HasImportantBit(aPropID)) {
3995 mTempData.ClearProperty(aPropID);
3996 return;
4000 if (aMustCallValueAppended || !mData.HasPropertyBit(aPropID)) {
4001 aDeclaration->ValueAppended(aPropID);
4004 mData.SetPropertyBit(aPropID);
4005 mTempData.ClearPropertyBit(aPropID);
4008 * Save needless copying and allocation by calling the destructor in
4009 * the destination, copying memory directly, and then using placement
4010 * new.
4012 void *v_source = mTempData.PropertyAt(aPropID);
4013 void *v_dest = mData.PropertyAt(aPropID);
4014 switch (nsCSSProps::kTypeTable[aPropID]) {
4015 case eCSSType_Value: {
4016 nsCSSValue *source = static_cast<nsCSSValue*>(v_source);
4017 nsCSSValue *dest = static_cast<nsCSSValue*>(v_dest);
4018 if (*source != *dest)
4019 *aChanged = PR_TRUE;
4020 dest->~nsCSSValue();
4021 memcpy(dest, source, sizeof(nsCSSValue));
4022 new (source) nsCSSValue();
4023 } break;
4025 case eCSSType_Rect: {
4026 nsCSSRect *source = static_cast<nsCSSRect*>(v_source);
4027 nsCSSRect *dest = static_cast<nsCSSRect*>(v_dest);
4028 if (*source != *dest)
4029 *aChanged = PR_TRUE;
4030 dest->~nsCSSRect();
4031 memcpy(dest, source, sizeof(nsCSSRect));
4032 new (source) nsCSSRect();
4033 } break;
4035 case eCSSType_ValuePair: {
4036 nsCSSValuePair *source = static_cast<nsCSSValuePair*>(v_source);
4037 nsCSSValuePair *dest = static_cast<nsCSSValuePair*>(v_dest);
4038 if (*source != *dest)
4039 *aChanged = PR_TRUE;
4040 dest->~nsCSSValuePair();
4041 memcpy(dest, source, sizeof(nsCSSValuePair));
4042 new (source) nsCSSValuePair();
4043 } break;
4045 case eCSSType_ValueList: {
4046 nsCSSValueList **source = static_cast<nsCSSValueList**>(v_source);
4047 nsCSSValueList **dest = static_cast<nsCSSValueList**>(v_dest);
4048 if (!nsCSSValueList::Equal(*source, *dest))
4049 *aChanged = PR_TRUE;
4050 delete *dest;
4051 *dest = *source;
4052 *source = nsnull;
4053 } break;
4055 case eCSSType_ValuePairList: {
4056 nsCSSValuePairList **source =
4057 static_cast<nsCSSValuePairList**>(v_source);
4058 nsCSSValuePairList **dest =
4059 static_cast<nsCSSValuePairList**>(v_dest);
4060 if (!nsCSSValuePairList::Equal(*source, *dest))
4061 *aChanged = PR_TRUE;
4062 delete *dest;
4063 *dest = *source;
4064 *source = nsnull;
4065 } break;
4069 static const nsCSSProperty kBorderTopIDs[] = {
4070 eCSSProperty_border_top_width,
4071 eCSSProperty_border_top_style,
4072 eCSSProperty_border_top_color
4074 static const nsCSSProperty kBorderRightIDs[] = {
4075 eCSSProperty_border_right_width_value,
4076 eCSSProperty_border_right_style_value,
4077 eCSSProperty_border_right_color_value,
4078 eCSSProperty_border_right_width,
4079 eCSSProperty_border_right_style,
4080 eCSSProperty_border_right_color
4082 static const nsCSSProperty kBorderBottomIDs[] = {
4083 eCSSProperty_border_bottom_width,
4084 eCSSProperty_border_bottom_style,
4085 eCSSProperty_border_bottom_color
4087 static const nsCSSProperty kBorderLeftIDs[] = {
4088 eCSSProperty_border_left_width_value,
4089 eCSSProperty_border_left_style_value,
4090 eCSSProperty_border_left_color_value,
4091 eCSSProperty_border_left_width,
4092 eCSSProperty_border_left_style,
4093 eCSSProperty_border_left_color
4095 static const nsCSSProperty kBorderStartIDs[] = {
4096 eCSSProperty_border_start_width_value,
4097 eCSSProperty_border_start_style_value,
4098 eCSSProperty_border_start_color_value,
4099 eCSSProperty_border_start_width,
4100 eCSSProperty_border_start_style,
4101 eCSSProperty_border_start_color
4103 static const nsCSSProperty kBorderEndIDs[] = {
4104 eCSSProperty_border_end_width_value,
4105 eCSSProperty_border_end_style_value,
4106 eCSSProperty_border_end_color_value,
4107 eCSSProperty_border_end_width,
4108 eCSSProperty_border_end_style,
4109 eCSSProperty_border_end_color
4111 static const nsCSSProperty kColumnRuleIDs[] = {
4112 eCSSProperty__moz_column_rule_width,
4113 eCSSProperty__moz_column_rule_style,
4114 eCSSProperty__moz_column_rule_color
4117 PRBool
4118 CSSParserImpl::ParseEnum(nsCSSValue& aValue,
4119 const PRInt32 aKeywordTable[])
4121 nsSubstring* ident = NextIdent();
4122 if (nsnull == ident) {
4123 return PR_FALSE;
4125 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
4126 if (eCSSKeyword_UNKNOWN < keyword) {
4127 PRInt32 value;
4128 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
4129 aValue.SetIntValue(value, eCSSUnit_Enumerated);
4130 return PR_TRUE;
4134 // Put the unknown identifier back and return
4135 UngetToken();
4136 return PR_FALSE;
4140 struct UnitInfo {
4141 char name[5]; // needs to be long enough for the longest unit, with
4142 // terminating null.
4143 PRUint32 length;
4144 nsCSSUnit unit;
4145 PRInt32 type;
4148 #define STR_WITH_LEN(_str) \
4149 _str, sizeof(_str) - 1
4151 const UnitInfo UnitData[] = {
4152 { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH },
4153 { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
4154 { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
4155 { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
4156 { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
4157 { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
4158 { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
4159 { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
4160 { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
4161 { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
4162 { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
4163 { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE },
4164 { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY },
4165 { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY },
4166 { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME },
4167 { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME }
4170 #undef STR_WITH_LEN
4172 PRBool
4173 CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
4174 PRInt32 aVariantMask,
4175 float aNumber,
4176 const nsString& aUnit)
4178 nsCSSUnit units;
4179 PRInt32 type = 0;
4180 if (!aUnit.IsEmpty()) {
4181 PRUint32 i;
4182 for (i = 0; i < NS_ARRAY_LENGTH(UnitData); ++i) {
4183 if (aUnit.LowerCaseEqualsASCII(UnitData[i].name,
4184 UnitData[i].length)) {
4185 units = UnitData[i].unit;
4186 type = UnitData[i].type;
4187 break;
4191 if (i == NS_ARRAY_LENGTH(UnitData)) {
4192 // Unknown unit
4193 return PR_FALSE;
4195 } else {
4196 // Must be a zero number...
4197 NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
4198 if ((VARIANT_LENGTH & aVariantMask) != 0) {
4199 units = eCSSUnit_Point;
4200 type = VARIANT_LENGTH;
4202 else if ((VARIANT_ANGLE & aVariantMask) != 0) {
4203 units = eCSSUnit_Degree;
4204 type = VARIANT_ANGLE;
4206 else if ((VARIANT_FREQUENCY & aVariantMask) != 0) {
4207 units = eCSSUnit_Hertz;
4208 type = VARIANT_FREQUENCY;
4210 else if ((VARIANT_TIME & aVariantMask) != 0) {
4211 units = eCSSUnit_Seconds;
4212 type = VARIANT_TIME;
4214 else {
4215 NS_ERROR("Variant mask does not include dimension; why were we called?");
4216 return PR_FALSE;
4219 if ((type & aVariantMask) != 0) {
4220 aValue.SetFloatValue(aNumber, units);
4221 return PR_TRUE;
4223 return PR_FALSE;
4226 PRBool
4227 CSSParserImpl::ParsePositiveVariant(nsCSSValue& aValue,
4228 PRInt32 aVariantMask,
4229 const PRInt32 aKeywordTable[])
4231 if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
4232 if (eCSSUnit_Number == aValue.GetUnit() ||
4233 aValue.IsLengthUnit()){
4234 if (aValue.GetFloatValue() < 0) {
4235 UngetToken();
4236 return PR_FALSE;
4239 else if (aValue.GetUnit() == eCSSUnit_Percent) {
4240 if (aValue.GetPercentValue() < 0) {
4241 UngetToken();
4242 return PR_FALSE;
4244 } else if (aValue.GetUnit() == eCSSUnit_Integer) {
4245 if (aValue.GetIntValue() < 0) {
4246 UngetToken();
4247 return PR_FALSE;
4250 return PR_TRUE;
4252 return PR_FALSE;
4255 // Assigns to aValue iff it returns PR_TRUE.
4256 PRBool
4257 CSSParserImpl::ParseVariant(nsCSSValue& aValue,
4258 PRInt32 aVariantMask,
4259 const PRInt32 aKeywordTable[])
4261 NS_ASSERTION(IsParsingCompoundProperty() ||
4262 ((~aVariantMask) & (VARIANT_LENGTH|VARIANT_COLOR)),
4263 "cannot distinguish lengths and colors in quirks mode");
4265 if (!GetToken(PR_TRUE)) {
4266 return PR_FALSE;
4268 nsCSSToken* tk = &mToken;
4269 if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE)) != 0) &&
4270 (eCSSToken_Ident == tk->mType)) {
4271 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
4272 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
4273 if ((aVariantMask & VARIANT_AUTO) != 0) {
4274 if (eCSSKeyword_auto == keyword) {
4275 aValue.SetAutoValue();
4276 return PR_TRUE;
4279 if ((aVariantMask & VARIANT_INHERIT) != 0) {
4280 // XXX Should we check IsParsingCompoundProperty, or do all
4281 // callers handle it? (Not all callers set it, though, since
4282 // they want the quirks that are disabled by setting it.)
4283 if (eCSSKeyword_inherit == keyword) {
4284 aValue.SetInheritValue();
4285 return PR_TRUE;
4287 else if (eCSSKeyword__moz_initial == keyword) { // anything that can inherit can also take an initial val.
4288 aValue.SetInitialValue();
4289 return PR_TRUE;
4292 if ((aVariantMask & VARIANT_NONE) != 0) {
4293 if (eCSSKeyword_none == keyword) {
4294 aValue.SetNoneValue();
4295 return PR_TRUE;
4298 if ((aVariantMask & VARIANT_NORMAL) != 0) {
4299 if (eCSSKeyword_normal == keyword) {
4300 aValue.SetNormalValue();
4301 return PR_TRUE;
4304 if ((aVariantMask & VARIANT_SYSFONT) != 0) {
4305 if (eCSSKeyword__moz_use_system_font == keyword &&
4306 !IsParsingCompoundProperty()) {
4307 aValue.SetSystemFontValue();
4308 return PR_TRUE;
4311 if ((aVariantMask & VARIANT_KEYWORD) != 0) {
4312 PRInt32 value;
4313 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
4314 aValue.SetIntValue(value, eCSSUnit_Enumerated);
4315 return PR_TRUE;
4320 if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE | VARIANT_FREQUENCY | VARIANT_TIME)) != 0) &&
4321 tk->IsDimension()) {
4322 if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
4323 return PR_TRUE;
4325 // Put the token back; we didn't parse it, so we shouldn't consume it
4326 UngetToken();
4327 return PR_FALSE;
4329 if (((aVariantMask & VARIANT_PERCENT) != 0) &&
4330 (eCSSToken_Percentage == tk->mType)) {
4331 aValue.SetPercentValue(tk->mNumber);
4332 return PR_TRUE;
4334 if (((aVariantMask & VARIANT_NUMBER) != 0) &&
4335 (eCSSToken_Number == tk->mType)) {
4336 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
4337 return PR_TRUE;
4339 if (((aVariantMask & VARIANT_INTEGER) != 0) &&
4340 (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
4341 aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
4342 return PR_TRUE;
4344 if (mNavQuirkMode && !IsParsingCompoundProperty()) { // NONSTANDARD: Nav interprets unitless numbers as px
4345 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
4346 (eCSSToken_Number == tk->mType)) {
4347 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
4348 return PR_TRUE;
4352 #ifdef MOZ_SVG
4353 if (IsSVGMode() && !IsParsingCompoundProperty()) {
4354 // STANDARD: SVG Spec states that lengths and coordinates can be unitless
4355 // in which case they default to user-units (1 px = 1 user unit)
4356 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
4357 (eCSSToken_Number == tk->mType)) {
4358 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
4359 return PR_TRUE;
4362 #endif
4364 if (((aVariantMask & VARIANT_URL) != 0) &&
4365 (eCSSToken_Function == tk->mType) &&
4366 tk->mIdent.LowerCaseEqualsLiteral("url")) {
4367 if (ParseURL(aValue)) {
4368 return PR_TRUE;
4370 return PR_FALSE;
4372 if ((aVariantMask & VARIANT_COLOR) != 0) {
4373 if ((mNavQuirkMode && !IsParsingCompoundProperty()) || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
4374 (eCSSToken_ID == tk->mType) ||
4375 (eCSSToken_Ref == tk->mType) ||
4376 (eCSSToken_Ident == tk->mType) ||
4377 ((eCSSToken_Function == tk->mType) &&
4378 (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
4379 tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
4380 tk->mIdent.LowerCaseEqualsLiteral("-moz-rgba") ||
4381 tk->mIdent.LowerCaseEqualsLiteral("-moz-hsla") ||
4382 tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
4383 tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
4385 // Put token back so that parse color can get it
4386 UngetToken();
4387 if (ParseColor(aValue)) {
4388 return PR_TRUE;
4390 return PR_FALSE;
4393 if (((aVariantMask & VARIANT_STRING) != 0) &&
4394 (eCSSToken_String == tk->mType)) {
4395 nsAutoString buffer;
4396 buffer.Append(tk->mSymbol);
4397 buffer.Append(tk->mIdent);
4398 buffer.Append(tk->mSymbol);
4399 aValue.SetStringValue(buffer, eCSSUnit_String);
4400 return PR_TRUE;
4402 if (((aVariantMask & VARIANT_IDENTIFIER) != 0) &&
4403 (eCSSToken_Ident == tk->mType)) {
4404 aValue.SetStringValue(tk->mIdent, eCSSUnit_String);
4405 return PR_TRUE;
4407 if (((aVariantMask & VARIANT_COUNTER) != 0) &&
4408 (eCSSToken_Function == tk->mType) &&
4409 (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
4410 tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
4411 return ParseCounter(aValue);
4413 if (((aVariantMask & VARIANT_ATTR) != 0) &&
4414 (eCSSToken_Function == tk->mType) &&
4415 tk->mIdent.LowerCaseEqualsLiteral("attr")) {
4416 return ParseAttr(aValue);
4419 UngetToken();
4420 return PR_FALSE;
4424 PRBool
4425 CSSParserImpl::ParseCounter(nsCSSValue& aValue)
4427 nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
4428 eCSSUnit_Counter : eCSSUnit_Counters);
4430 if (!ExpectSymbol('(', PR_FALSE))
4431 return PR_FALSE;
4433 if (!GetNonCloseParenToken(PR_TRUE) ||
4434 eCSSToken_Ident != mToken.mType) {
4435 SkipUntil(')');
4436 return PR_FALSE;
4439 nsRefPtr<nsCSSValue::Array> val =
4440 nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
4441 if (!val) {
4442 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
4443 return PR_FALSE;
4446 val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_String);
4448 if (eCSSUnit_Counters == unit) {
4449 // get mandatory separator string
4450 if (!ExpectSymbol(',', PR_TRUE) ||
4451 !(GetNonCloseParenToken(PR_TRUE) &&
4452 eCSSToken_String == mToken.mType)) {
4453 SkipUntil(')');
4454 return PR_FALSE;
4456 val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
4459 // get optional type
4460 PRInt32 type = NS_STYLE_LIST_STYLE_DECIMAL;
4461 if (ExpectSymbol(',', PR_TRUE)) {
4462 nsCSSKeyword keyword;
4463 PRBool success = GetNonCloseParenToken(PR_TRUE) &&
4464 eCSSToken_Ident == mToken.mType &&
4465 (keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent)) !=
4466 eCSSKeyword_UNKNOWN;
4467 if (success) {
4468 if (keyword == eCSSKeyword_none) {
4469 type = NS_STYLE_LIST_STYLE_NONE;
4470 } else {
4471 success = nsCSSProps::FindKeyword(keyword,
4472 nsCSSProps::kListStyleKTable, type);
4475 if (!success) {
4476 SkipUntil(')');
4477 return PR_FALSE;
4480 PRInt32 typeItem = eCSSUnit_Counters == unit ? 2 : 1;
4481 val->Item(typeItem).SetIntValue(type, eCSSUnit_Enumerated);
4483 if (!ExpectSymbol(')', PR_TRUE)) {
4484 SkipUntil(')');
4485 return PR_FALSE;
4488 aValue.SetArrayValue(val, unit);
4489 return PR_TRUE;
4492 PRBool
4493 CSSParserImpl::ParseAttr(nsCSSValue& aValue)
4495 if (ExpectSymbol('(', PR_FALSE)) {
4496 if (GetToken(PR_TRUE)) {
4497 nsAutoString attr;
4498 if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
4499 nsAutoString holdIdent(mToken.mIdent);
4500 if (ExpectSymbol('|', PR_FALSE)) { // namespace
4501 PRInt32 nameSpaceID;
4502 if (!GetNamespaceIdForPrefix(holdIdent, &nameSpaceID)) {
4503 return PR_FALSE;
4505 attr.AppendInt(nameSpaceID, 10);
4506 attr.Append(PRUnichar('|'));
4507 if (! GetToken(PR_FALSE)) {
4508 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4509 return PR_FALSE;
4511 if (eCSSToken_Ident == mToken.mType) {
4512 if (mCaseSensitive) {
4513 attr.Append(mToken.mIdent);
4514 } else {
4515 nsAutoString buffer;
4516 ToLowerCase(mToken.mIdent, buffer);
4517 attr.Append(buffer);
4520 else {
4521 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4522 UngetToken();
4523 return PR_FALSE;
4526 else { // no namespace
4527 if (mCaseSensitive) {
4528 attr = holdIdent;
4530 else {
4531 ToLowerCase(holdIdent, attr);
4535 else if (mToken.IsSymbol('*')) { // namespace wildcard
4536 // Wildcard namespace makes no sense here and is not allowed
4537 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4538 UngetToken();
4539 return PR_FALSE;
4541 else if (mToken.IsSymbol('|')) { // explicit NO namespace
4542 if (! GetToken(PR_FALSE)) {
4543 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4544 return PR_FALSE;
4546 if (eCSSToken_Ident == mToken.mType) {
4547 if (mCaseSensitive) {
4548 attr.Append(mToken.mIdent);
4549 } else {
4550 nsAutoString buffer;
4551 ToLowerCase(mToken.mIdent, buffer);
4552 attr.Append(buffer);
4555 else {
4556 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4557 UngetToken();
4558 return PR_FALSE;
4561 else {
4562 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
4563 UngetToken();
4564 return PR_FALSE;
4566 if (ExpectSymbol(')', PR_TRUE)) {
4567 aValue.SetStringValue(attr, eCSSUnit_Attr);
4568 return PR_TRUE;
4572 return PR_FALSE;
4575 PRBool
4576 CSSParserImpl::ParseURL(nsCSSValue& aValue)
4578 if (!mSheetPrincipal) {
4579 NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
4580 "origin principal");
4581 return PR_FALSE;
4584 if (!ExpectSymbol('(', PR_FALSE))
4585 return PR_FALSE;
4586 if (!GetURLToken())
4587 return PR_FALSE;
4589 nsCSSToken* tk = &mToken;
4590 if (eCSSToken_String != tk->mType && eCSSToken_URL != tk->mType)
4591 return PR_FALSE;
4593 nsString url = tk->mIdent;
4594 if (!ExpectSymbol(')', PR_TRUE))
4595 return PR_FALSE;
4597 // Translate url into an absolute url if the url is relative to the
4598 // style sheet.
4599 nsCOMPtr<nsIURI> uri;
4600 NS_NewURI(getter_AddRefs(uri), url, nsnull, mBaseURL);
4602 nsStringBuffer* buffer = nsCSSValue::BufferFromString(url);
4603 if (NS_UNLIKELY(!buffer)) {
4604 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
4605 return PR_FALSE;
4607 nsCSSValue::URL *urlVal =
4608 new nsCSSValue::URL(uri, buffer, mSheetURL, mSheetPrincipal);
4610 buffer->Release();
4611 if (NS_UNLIKELY(!urlVal)) {
4612 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
4613 return PR_FALSE;
4615 aValue.SetURLValue(urlVal);
4616 return PR_TRUE;
4619 PRInt32
4620 CSSParserImpl::ParseChoice(nsCSSValue aValues[],
4621 const nsCSSProperty aPropIDs[], PRInt32 aNumIDs)
4623 PRInt32 found = 0;
4624 nsAutoParseCompoundProperty compound(this);
4626 PRInt32 loop;
4627 for (loop = 0; loop < aNumIDs; loop++) {
4628 // Try each property parser in order
4629 PRInt32 hadFound = found;
4630 PRInt32 index;
4631 for (index = 0; index < aNumIDs; index++) {
4632 PRInt32 bit = 1 << index;
4633 if ((found & bit) == 0) {
4634 if (ParseSingleValueProperty(aValues[index], aPropIDs[index])) {
4635 found |= bit;
4639 if (found == hadFound) { // found nothing new
4640 break;
4643 if (0 < found) {
4644 if (1 == found) { // only first property
4645 if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
4646 for (loop = 1; loop < aNumIDs; loop++) {
4647 aValues[loop].SetInheritValue();
4649 found = ((1 << aNumIDs) - 1);
4651 else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
4652 for (loop = 1; loop < aNumIDs; loop++) {
4653 aValues[loop].SetInitialValue();
4655 found = ((1 << aNumIDs) - 1);
4658 else { // more than one value, verify no inherits or initials
4659 for (loop = 0; loop < aNumIDs; loop++) {
4660 if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
4661 found = -1;
4662 break;
4664 else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
4665 found = -1;
4666 break;
4671 return found;
4674 void
4675 CSSParserImpl::AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue)
4677 NS_ASSERTION(0 <= aPropID && aPropID < eCSSProperty_COUNT_no_shorthands,
4678 "property out of range");
4679 NS_ASSERTION(nsCSSProps::kTypeTable[aPropID] == eCSSType_Value,
4680 nsPrintfCString(64, "type error (property=\'%s\')",
4681 nsCSSProps::GetStringValue(aPropID).get()).get());
4682 nsCSSValue& storage =
4683 *static_cast<nsCSSValue*>(mTempData.PropertyAt(aPropID));
4684 storage = aValue;
4685 mTempData.SetPropertyBit(aPropID);
4689 * Parse a "box" property. Box properties have 1 to 4 values. When less
4690 * than 4 values are provided a standard mapping is used to replicate
4691 * existing values.
4693 PRBool
4694 CSSParserImpl::ParseBoxProperties(nsCSSRect& aResult,
4695 const nsCSSProperty aPropIDs[])
4697 // Get up to four values for the property
4698 PRInt32 count = 0;
4699 nsCSSRect result;
4700 NS_FOR_CSS_SIDES (index) {
4701 if (! ParseSingleValueProperty(result.*(nsCSSRect::sides[index]),
4702 aPropIDs[index])) {
4703 break;
4705 count++;
4707 if ((count == 0) || (PR_FALSE == ExpectEndProperty())) {
4708 return PR_FALSE;
4711 if (1 < count) { // verify no more than single inherit or initial
4712 NS_FOR_CSS_SIDES (index) {
4713 nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
4714 if (eCSSUnit_Inherit == unit || eCSSUnit_Initial == unit) {
4715 return PR_FALSE;
4720 // Provide missing values by replicating some of the values found
4721 switch (count) {
4722 case 1: // Make right == top
4723 result.mRight = result.mTop;
4724 case 2: // Make bottom == top
4725 result.mBottom = result.mTop;
4726 case 3: // Make left == right
4727 result.mLeft = result.mRight;
4730 NS_FOR_CSS_SIDES (index) {
4731 mTempData.SetPropertyBit(aPropIDs[index]);
4733 aResult = result;
4734 return PR_TRUE;
4737 PRBool
4738 CSSParserImpl::ParseDirectionalBoxProperty(nsCSSProperty aProperty,
4739 PRInt32 aSourceType)
4741 const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(aProperty);
4742 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
4743 "not box property with physical vs. logical cascading");
4744 nsCSSValue value;
4745 if (!ParseSingleValueProperty(value, subprops[0]) ||
4746 !ExpectEndProperty())
4747 return PR_FALSE;
4749 AppendValue(subprops[0], value);
4750 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
4751 AppendValue(subprops[1], typeVal);
4752 AppendValue(subprops[2], typeVal);
4753 return PR_TRUE;
4756 PRBool
4757 CSSParserImpl::ParseBoxCornerRadius(nsCSSProperty aPropID)
4759 nsCSSValue dimenX, dimenY;
4760 // required first value
4761 if (! ParsePositiveVariant(dimenX, VARIANT_HLP, nsnull))
4762 return PR_FALSE;
4763 // optional second value (forbidden if first value is inherit/initial)
4764 if (dimenX.GetUnit() == eCSSUnit_Inherit ||
4765 dimenX.GetUnit() == eCSSUnit_Initial ||
4766 ! ParsePositiveVariant(dimenY, VARIANT_LP, nsnull))
4767 dimenY = dimenX;
4769 NS_ASSERTION(nsCSSProps::kTypeTable[aPropID] == eCSSType_ValuePair,
4770 nsPrintfCString(64, "type error (property='%s')",
4771 nsCSSProps::GetStringValue(aPropID).get())
4772 .get());
4773 nsCSSValuePair& storage =
4774 *static_cast<nsCSSValuePair*>(mTempData.PropertyAt(aPropID));
4775 storage.mXValue = dimenX;
4776 storage.mYValue = dimenY;
4777 mTempData.SetPropertyBit(aPropID);
4778 return PR_TRUE;
4781 PRBool
4782 CSSParserImpl::ParseBoxCornerRadii(nsCSSCornerSizes& aRadii,
4783 const nsCSSProperty aPropIDs[])
4785 // Rectangles are used as scratch storage.
4786 // top => top-left, right => top-right,
4787 // bottom => bottom-right, left => bottom-left.
4788 nsCSSRect dimenX, dimenY;
4789 PRInt32 countX = 0, countY = 0;
4791 NS_FOR_CSS_SIDES (side) {
4792 if (! ParsePositiveVariant(dimenX.*nsCSSRect::sides[side],
4793 side > 0 ? VARIANT_LP : VARIANT_HLP, nsnull))
4794 break;
4795 countX++;
4797 if (countX == 0)
4798 return PR_FALSE;
4800 if (ExpectSymbol('/', PR_TRUE)) {
4801 NS_FOR_CSS_SIDES (side) {
4802 if (! ParsePositiveVariant(dimenY.*nsCSSRect::sides[side],
4803 VARIANT_LP, nsnull))
4804 break;
4805 countY++;
4807 if (countY == 0)
4808 return PR_FALSE;
4810 if (!ExpectEndProperty())
4811 return PR_FALSE;
4813 // if 'initial' or 'inherit' was used, it must be the only value
4814 if (countX > 1 || countY > 0) {
4815 nsCSSUnit unit = dimenX.mTop.GetUnit();
4816 if (eCSSUnit_Inherit == unit || eCSSUnit_Initial == unit)
4817 return PR_FALSE;
4820 // if we have no Y-values, use the X-values
4821 if (countY == 0) {
4822 dimenY = dimenX;
4823 countY = countX;
4826 // Provide missing values by replicating some of the values found
4827 switch (countX) {
4828 case 1: dimenX.mRight = dimenX.mTop; // top-right same as top-left, and
4829 case 2: dimenX.mBottom = dimenX.mTop; // bottom-right same as top-left, and
4830 case 3: dimenX.mLeft = dimenX.mRight; // bottom-left same as top-right
4833 switch (countY) {
4834 case 1: dimenY.mRight = dimenY.mTop; // top-right same as top-left, and
4835 case 2: dimenY.mBottom = dimenY.mTop; // bottom-right same as top-left, and
4836 case 3: dimenY.mLeft = dimenY.mRight; // bottom-left same as top-right
4839 NS_FOR_CSS_SIDES(side) {
4840 nsCSSValuePair& corner =
4841 aRadii.GetFullCorner(NS_SIDE_TO_FULL_CORNER(side, PR_FALSE));
4842 corner.mXValue = dimenX.*nsCSSRect::sides[side];
4843 corner.mYValue = dimenY.*nsCSSRect::sides[side];
4844 mTempData.SetPropertyBit(aPropIDs[side]);
4846 return PR_TRUE;
4849 // These must be in CSS order (top,right,bottom,left) for indexing to work
4850 static const nsCSSProperty kBorderStyleIDs[] = {
4851 eCSSProperty_border_top_style,
4852 eCSSProperty_border_right_style_value,
4853 eCSSProperty_border_bottom_style,
4854 eCSSProperty_border_left_style_value
4856 static const nsCSSProperty kBorderWidthIDs[] = {
4857 eCSSProperty_border_top_width,
4858 eCSSProperty_border_right_width_value,
4859 eCSSProperty_border_bottom_width,
4860 eCSSProperty_border_left_width_value
4862 static const nsCSSProperty kBorderColorIDs[] = {
4863 eCSSProperty_border_top_color,
4864 eCSSProperty_border_right_color_value,
4865 eCSSProperty_border_bottom_color,
4866 eCSSProperty_border_left_color_value
4868 static const nsCSSProperty kBorderRadiusIDs[] = {
4869 eCSSProperty__moz_border_radius_topLeft,
4870 eCSSProperty__moz_border_radius_topRight,
4871 eCSSProperty__moz_border_radius_bottomRight,
4872 eCSSProperty__moz_border_radius_bottomLeft
4874 static const nsCSSProperty kOutlineRadiusIDs[] = {
4875 eCSSProperty__moz_outline_radius_topLeft,
4876 eCSSProperty__moz_outline_radius_topRight,
4877 eCSSProperty__moz_outline_radius_bottomRight,
4878 eCSSProperty__moz_outline_radius_bottomLeft
4881 PRBool
4882 CSSParserImpl::ParseProperty(nsCSSProperty aPropID)
4884 NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range");
4886 switch (aPropID) { // handle shorthand or multiple properties
4887 case eCSSProperty_background:
4888 return ParseBackground();
4889 case eCSSProperty_background_position:
4890 return ParseBackgroundPosition();
4891 case eCSSProperty_border:
4892 return ParseBorderSide(kBorderTopIDs, PR_TRUE);
4893 case eCSSProperty_border_color:
4894 return ParseBorderColor();
4895 case eCSSProperty_border_spacing:
4896 return ParseBorderSpacing();
4897 case eCSSProperty_border_style:
4898 return ParseBorderStyle();
4899 case eCSSProperty_border_bottom:
4900 return ParseBorderSide(kBorderBottomIDs, PR_FALSE);
4901 case eCSSProperty_border_end:
4902 return ParseDirectionalBorderSide(kBorderEndIDs,
4903 NS_BOXPROP_SOURCE_LOGICAL);
4904 case eCSSProperty_border_left:
4905 return ParseDirectionalBorderSide(kBorderLeftIDs,
4906 NS_BOXPROP_SOURCE_PHYSICAL);
4907 case eCSSProperty_border_right:
4908 return ParseDirectionalBorderSide(kBorderRightIDs,
4909 NS_BOXPROP_SOURCE_PHYSICAL);
4910 case eCSSProperty_border_start:
4911 return ParseDirectionalBorderSide(kBorderStartIDs,
4912 NS_BOXPROP_SOURCE_LOGICAL);
4913 case eCSSProperty_border_top:
4914 return ParseBorderSide(kBorderTopIDs, PR_FALSE);
4915 case eCSSProperty_border_bottom_colors:
4916 return ParseBorderColors(&mTempData.mMargin.mBorderColors.mBottom,
4917 aPropID);
4918 case eCSSProperty_border_left_colors:
4919 return ParseBorderColors(&mTempData.mMargin.mBorderColors.mLeft,
4920 aPropID);
4921 case eCSSProperty_border_right_colors:
4922 return ParseBorderColors(&mTempData.mMargin.mBorderColors.mRight,
4923 aPropID);
4924 case eCSSProperty_border_top_colors:
4925 return ParseBorderColors(&mTempData.mMargin.mBorderColors.mTop,
4926 aPropID);
4927 case eCSSProperty_border_image:
4928 return ParseBorderImage();
4929 case eCSSProperty_border_width:
4930 return ParseBorderWidth();
4931 case eCSSProperty_border_end_color:
4932 return ParseDirectionalBoxProperty(eCSSProperty_border_end_color,
4933 NS_BOXPROP_SOURCE_LOGICAL);
4934 case eCSSProperty_border_left_color:
4935 return ParseDirectionalBoxProperty(eCSSProperty_border_left_color,
4936 NS_BOXPROP_SOURCE_PHYSICAL);
4937 case eCSSProperty_border_right_color:
4938 return ParseDirectionalBoxProperty(eCSSProperty_border_right_color,
4939 NS_BOXPROP_SOURCE_PHYSICAL);
4940 case eCSSProperty_border_start_color:
4941 return ParseDirectionalBoxProperty(eCSSProperty_border_start_color,
4942 NS_BOXPROP_SOURCE_LOGICAL);
4943 case eCSSProperty_border_end_width:
4944 return ParseDirectionalBoxProperty(eCSSProperty_border_end_width,
4945 NS_BOXPROP_SOURCE_LOGICAL);
4946 case eCSSProperty_border_left_width:
4947 return ParseDirectionalBoxProperty(eCSSProperty_border_left_width,
4948 NS_BOXPROP_SOURCE_PHYSICAL);
4949 case eCSSProperty_border_right_width:
4950 return ParseDirectionalBoxProperty(eCSSProperty_border_right_width,
4951 NS_BOXPROP_SOURCE_PHYSICAL);
4952 case eCSSProperty_border_start_width:
4953 return ParseDirectionalBoxProperty(eCSSProperty_border_start_width,
4954 NS_BOXPROP_SOURCE_LOGICAL);
4955 case eCSSProperty_border_end_style:
4956 return ParseDirectionalBoxProperty(eCSSProperty_border_end_style,
4957 NS_BOXPROP_SOURCE_LOGICAL);
4958 case eCSSProperty_border_left_style:
4959 return ParseDirectionalBoxProperty(eCSSProperty_border_left_style,
4960 NS_BOXPROP_SOURCE_PHYSICAL);
4961 case eCSSProperty_border_right_style:
4962 return ParseDirectionalBoxProperty(eCSSProperty_border_right_style,
4963 NS_BOXPROP_SOURCE_PHYSICAL);
4964 case eCSSProperty_border_start_style:
4965 return ParseDirectionalBoxProperty(eCSSProperty_border_start_style,
4966 NS_BOXPROP_SOURCE_LOGICAL);
4967 case eCSSProperty__moz_border_radius:
4968 return ParseBoxCornerRadii(mTempData.mMargin.mBorderRadius,
4969 kBorderRadiusIDs);
4970 case eCSSProperty__moz_outline_radius:
4971 return ParseBoxCornerRadii(mTempData.mMargin.mOutlineRadius,
4972 kOutlineRadiusIDs);
4974 case eCSSProperty__moz_border_radius_topLeft:
4975 case eCSSProperty__moz_border_radius_topRight:
4976 case eCSSProperty__moz_border_radius_bottomRight:
4977 case eCSSProperty__moz_border_radius_bottomLeft:
4978 case eCSSProperty__moz_outline_radius_topLeft:
4979 case eCSSProperty__moz_outline_radius_topRight:
4980 case eCSSProperty__moz_outline_radius_bottomRight:
4981 case eCSSProperty__moz_outline_radius_bottomLeft:
4982 return ParseBoxCornerRadius(aPropID);
4984 case eCSSProperty_box_shadow:
4985 return ParseBoxShadow();
4986 case eCSSProperty_clip:
4987 return ParseRect(mTempData.mDisplay.mClip, eCSSProperty_clip);
4988 case eCSSProperty__moz_column_rule:
4989 return ParseBorderSide(kColumnRuleIDs, PR_FALSE);
4990 case eCSSProperty_content:
4991 return ParseContent();
4992 case eCSSProperty_counter_increment:
4993 return ParseCounterData(&mTempData.mContent.mCounterIncrement,
4994 aPropID);
4995 case eCSSProperty_counter_reset:
4996 return ParseCounterData(&mTempData.mContent.mCounterReset,
4997 aPropID);
4998 case eCSSProperty_cue:
4999 return ParseCue();
5000 case eCSSProperty_cursor:
5001 return ParseCursor();
5002 case eCSSProperty_font:
5003 return ParseFont();
5004 case eCSSProperty_image_region:
5005 return ParseRect(mTempData.mList.mImageRegion,
5006 eCSSProperty_image_region);
5007 case eCSSProperty_list_style:
5008 return ParseListStyle();
5009 case eCSSProperty_margin:
5010 return ParseMargin();
5011 case eCSSProperty_margin_end:
5012 return ParseDirectionalBoxProperty(eCSSProperty_margin_end,
5013 NS_BOXPROP_SOURCE_LOGICAL);
5014 case eCSSProperty_margin_left:
5015 return ParseDirectionalBoxProperty(eCSSProperty_margin_left,
5016 NS_BOXPROP_SOURCE_PHYSICAL);
5017 case eCSSProperty_margin_right:
5018 return ParseDirectionalBoxProperty(eCSSProperty_margin_right,
5019 NS_BOXPROP_SOURCE_PHYSICAL);
5020 case eCSSProperty_margin_start:
5021 return ParseDirectionalBoxProperty(eCSSProperty_margin_start,
5022 NS_BOXPROP_SOURCE_LOGICAL);
5023 case eCSSProperty_outline:
5024 return ParseOutline();
5025 case eCSSProperty_overflow:
5026 return ParseOverflow();
5027 case eCSSProperty_padding:
5028 return ParsePadding();
5029 case eCSSProperty_padding_end:
5030 return ParseDirectionalBoxProperty(eCSSProperty_padding_end,
5031 NS_BOXPROP_SOURCE_LOGICAL);
5032 case eCSSProperty_padding_left:
5033 return ParseDirectionalBoxProperty(eCSSProperty_padding_left,
5034 NS_BOXPROP_SOURCE_PHYSICAL);
5035 case eCSSProperty_padding_right:
5036 return ParseDirectionalBoxProperty(eCSSProperty_padding_right,
5037 NS_BOXPROP_SOURCE_PHYSICAL);
5038 case eCSSProperty_padding_start:
5039 return ParseDirectionalBoxProperty(eCSSProperty_padding_start,
5040 NS_BOXPROP_SOURCE_LOGICAL);
5041 case eCSSProperty_pause:
5042 return ParsePause();
5043 case eCSSProperty_quotes:
5044 return ParseQuotes();
5045 case eCSSProperty_size:
5046 return ParseSize();
5047 case eCSSProperty_text_shadow:
5048 return ParseTextShadow();
5049 case eCSSProperty__moz_transform:
5050 return ParseMozTransform();
5051 case eCSSProperty__moz_transform_origin:
5052 return ParseMozTransformOrigin();
5054 #ifdef MOZ_SVG
5055 case eCSSProperty_fill:
5056 return ParsePaint(&mTempData.mSVG.mFill, eCSSProperty_fill);
5057 case eCSSProperty_stroke:
5058 return ParsePaint(&mTempData.mSVG.mStroke, eCSSProperty_stroke);
5059 case eCSSProperty_stroke_dasharray:
5060 return ParseDasharray();
5061 case eCSSProperty_marker:
5062 return ParseMarker();
5063 #endif
5065 // Strip out properties we use internally.
5066 case eCSSProperty__x_system_font:
5067 case eCSSProperty_margin_end_value:
5068 case eCSSProperty_margin_left_value:
5069 case eCSSProperty_margin_right_value:
5070 case eCSSProperty_margin_start_value:
5071 case eCSSProperty_margin_left_ltr_source:
5072 case eCSSProperty_margin_left_rtl_source:
5073 case eCSSProperty_margin_right_ltr_source:
5074 case eCSSProperty_margin_right_rtl_source:
5075 case eCSSProperty_padding_end_value:
5076 case eCSSProperty_padding_left_value:
5077 case eCSSProperty_padding_right_value:
5078 case eCSSProperty_padding_start_value:
5079 case eCSSProperty_padding_left_ltr_source:
5080 case eCSSProperty_padding_left_rtl_source:
5081 case eCSSProperty_padding_right_ltr_source:
5082 case eCSSProperty_padding_right_rtl_source:
5083 case eCSSProperty_border_end_color_value:
5084 case eCSSProperty_border_left_color_value:
5085 case eCSSProperty_border_right_color_value:
5086 case eCSSProperty_border_start_color_value:
5087 case eCSSProperty_border_left_color_ltr_source:
5088 case eCSSProperty_border_left_color_rtl_source:
5089 case eCSSProperty_border_right_color_ltr_source:
5090 case eCSSProperty_border_right_color_rtl_source:
5091 case eCSSProperty_border_end_style_value:
5092 case eCSSProperty_border_left_style_value:
5093 case eCSSProperty_border_right_style_value:
5094 case eCSSProperty_border_start_style_value:
5095 case eCSSProperty_border_left_style_ltr_source:
5096 case eCSSProperty_border_left_style_rtl_source:
5097 case eCSSProperty_border_right_style_ltr_source:
5098 case eCSSProperty_border_right_style_rtl_source:
5099 case eCSSProperty_border_end_width_value:
5100 case eCSSProperty_border_left_width_value:
5101 case eCSSProperty_border_right_width_value:
5102 case eCSSProperty_border_start_width_value:
5103 case eCSSProperty_border_left_width_ltr_source:
5104 case eCSSProperty_border_left_width_rtl_source:
5105 case eCSSProperty_border_right_width_ltr_source:
5106 case eCSSProperty_border_right_width_rtl_source:
5107 // The user can't use these
5108 REPORT_UNEXPECTED(PEInaccessibleProperty2);
5109 return PR_FALSE;
5110 default: // must be single property
5112 nsCSSValue value;
5113 if (ParseSingleValueProperty(value, aPropID)) {
5114 if (ExpectEndProperty()) {
5115 AppendValue(aPropID, value);
5116 return PR_TRUE;
5118 // XXX Report errors?
5120 // XXX Report errors?
5123 return PR_FALSE;
5126 // Bits used in determining which background position info we have
5127 #define BG_CENTER NS_STYLE_BG_POSITION_CENTER
5128 #define BG_TOP NS_STYLE_BG_POSITION_TOP
5129 #define BG_BOTTOM NS_STYLE_BG_POSITION_BOTTOM
5130 #define BG_LEFT NS_STYLE_BG_POSITION_LEFT
5131 #define BG_RIGHT NS_STYLE_BG_POSITION_RIGHT
5132 #define BG_CTB (BG_CENTER | BG_TOP | BG_BOTTOM)
5133 #define BG_CLR (BG_CENTER | BG_LEFT | BG_RIGHT)
5135 PRBool
5136 CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
5137 nsCSSProperty aPropID)
5139 switch (aPropID) {
5140 case eCSSProperty_UNKNOWN:
5141 case eCSSProperty_background:
5142 case eCSSProperty_background_position:
5143 case eCSSProperty_border:
5144 case eCSSProperty_border_color:
5145 case eCSSProperty_border_bottom_colors:
5146 case eCSSProperty_border_image:
5147 case eCSSProperty_border_left_colors:
5148 case eCSSProperty_border_right_colors:
5149 case eCSSProperty_border_end_color:
5150 case eCSSProperty_border_left_color:
5151 case eCSSProperty_border_right_color:
5152 case eCSSProperty_border_start_color:
5153 case eCSSProperty_border_end_style:
5154 case eCSSProperty_border_left_style:
5155 case eCSSProperty_border_right_style:
5156 case eCSSProperty_border_start_style:
5157 case eCSSProperty_border_end_width:
5158 case eCSSProperty_border_left_width:
5159 case eCSSProperty_border_right_width:
5160 case eCSSProperty_border_start_width:
5161 case eCSSProperty_border_top_colors:
5162 case eCSSProperty_border_spacing:
5163 case eCSSProperty_border_style:
5164 case eCSSProperty_border_bottom:
5165 case eCSSProperty_border_end:
5166 case eCSSProperty_border_left:
5167 case eCSSProperty_border_right:
5168 case eCSSProperty_border_start:
5169 case eCSSProperty_border_top:
5170 case eCSSProperty_border_width:
5171 case eCSSProperty__moz_border_radius:
5172 case eCSSProperty__moz_border_radius_topLeft:
5173 case eCSSProperty__moz_border_radius_topRight:
5174 case eCSSProperty__moz_border_radius_bottomRight:
5175 case eCSSProperty__moz_border_radius_bottomLeft:
5176 case eCSSProperty_box_shadow:
5177 case eCSSProperty_clip:
5178 case eCSSProperty__moz_column_rule:
5179 case eCSSProperty_content:
5180 case eCSSProperty_counter_increment:
5181 case eCSSProperty_counter_reset:
5182 case eCSSProperty_cue:
5183 case eCSSProperty_cursor:
5184 case eCSSProperty_font:
5185 case eCSSProperty_image_region:
5186 case eCSSProperty_list_style:
5187 case eCSSProperty_margin:
5188 case eCSSProperty_margin_end:
5189 case eCSSProperty_margin_left:
5190 case eCSSProperty_margin_right:
5191 case eCSSProperty_margin_start:
5192 case eCSSProperty_outline:
5193 case eCSSProperty__moz_outline_radius:
5194 case eCSSProperty__moz_outline_radius_topLeft:
5195 case eCSSProperty__moz_outline_radius_topRight:
5196 case eCSSProperty__moz_outline_radius_bottomRight:
5197 case eCSSProperty__moz_outline_radius_bottomLeft:
5198 case eCSSProperty_overflow:
5199 case eCSSProperty_padding:
5200 case eCSSProperty_padding_end:
5201 case eCSSProperty_padding_left:
5202 case eCSSProperty_padding_right:
5203 case eCSSProperty_padding_start:
5204 case eCSSProperty_pause:
5205 case eCSSProperty_quotes:
5206 case eCSSProperty_size:
5207 case eCSSProperty_text_shadow:
5208 case eCSSProperty__moz_transform:
5209 case eCSSProperty__moz_transform_origin:
5210 case eCSSProperty_COUNT:
5211 #ifdef MOZ_SVG
5212 case eCSSProperty_fill:
5213 case eCSSProperty_stroke:
5214 case eCSSProperty_stroke_dasharray:
5215 case eCSSProperty_marker:
5216 #endif
5217 NS_ERROR("not a single value property");
5218 return PR_FALSE;
5220 case eCSSProperty__x_system_font:
5221 case eCSSProperty_margin_left_ltr_source:
5222 case eCSSProperty_margin_left_rtl_source:
5223 case eCSSProperty_margin_right_ltr_source:
5224 case eCSSProperty_margin_right_rtl_source:
5225 case eCSSProperty_padding_left_ltr_source:
5226 case eCSSProperty_padding_left_rtl_source:
5227 case eCSSProperty_padding_right_ltr_source:
5228 case eCSSProperty_padding_right_rtl_source:
5229 case eCSSProperty_border_left_color_ltr_source:
5230 case eCSSProperty_border_left_color_rtl_source:
5231 case eCSSProperty_border_right_color_ltr_source:
5232 case eCSSProperty_border_right_color_rtl_source:
5233 case eCSSProperty_border_left_style_ltr_source:
5234 case eCSSProperty_border_left_style_rtl_source:
5235 case eCSSProperty_border_right_style_ltr_source:
5236 case eCSSProperty_border_right_style_rtl_source:
5237 case eCSSProperty_border_left_width_ltr_source:
5238 case eCSSProperty_border_left_width_rtl_source:
5239 case eCSSProperty_border_right_width_ltr_source:
5240 case eCSSProperty_border_right_width_rtl_source:
5241 #ifdef MOZ_MATHML
5242 case eCSSProperty_script_size_multiplier:
5243 case eCSSProperty_script_min_size:
5244 #endif
5245 NS_ERROR("not currently parsed here");
5246 return PR_FALSE;
5248 case eCSSProperty_appearance:
5249 return ParseVariant(aValue, VARIANT_HK,
5250 nsCSSProps::kAppearanceKTable);
5251 case eCSSProperty_azimuth:
5252 return ParseAzimuth(aValue);
5253 case eCSSProperty_background_attachment:
5254 return ParseVariant(aValue, VARIANT_HK,
5255 nsCSSProps::kBackgroundAttachmentKTable);
5256 case eCSSProperty__moz_background_clip:
5257 return ParseVariant(aValue, VARIANT_HK,
5258 nsCSSProps::kBackgroundClipKTable);
5259 case eCSSProperty_background_color:
5260 return ParseVariant(aValue, VARIANT_HC, nsnull);
5261 case eCSSProperty_background_image:
5262 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5263 case eCSSProperty__moz_background_inline_policy:
5264 return ParseVariant(aValue, VARIANT_HK,
5265 nsCSSProps::kBackgroundInlinePolicyKTable);
5266 case eCSSProperty__moz_background_origin:
5267 return ParseVariant(aValue, VARIANT_HK,
5268 nsCSSProps::kBackgroundOriginKTable);
5269 case eCSSProperty_background_repeat:
5270 return ParseVariant(aValue, VARIANT_HK,
5271 nsCSSProps::kBackgroundRepeatKTable);
5272 case eCSSProperty_binding:
5273 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5274 case eCSSProperty_border_collapse:
5275 return ParseVariant(aValue, VARIANT_HK,
5276 nsCSSProps::kBorderCollapseKTable);
5277 case eCSSProperty_border_bottom_color:
5278 case eCSSProperty_border_end_color_value: // for internal use
5279 case eCSSProperty_border_left_color_value: // for internal use
5280 case eCSSProperty_border_right_color_value: // for internal use
5281 case eCSSProperty_border_start_color_value: // for internal use
5282 case eCSSProperty_border_top_color:
5283 case eCSSProperty__moz_column_rule_color:
5284 return ParseVariant(aValue, VARIANT_HCK,
5285 nsCSSProps::kBorderColorKTable);
5286 case eCSSProperty_border_bottom_style:
5287 case eCSSProperty_border_end_style_value: // for internal use
5288 case eCSSProperty_border_left_style_value: // for internal use
5289 case eCSSProperty_border_right_style_value: // for internal use
5290 case eCSSProperty_border_start_style_value: // for internal use
5291 case eCSSProperty_border_top_style:
5292 case eCSSProperty__moz_column_rule_style:
5293 return ParseVariant(aValue, VARIANT_HOK,
5294 nsCSSProps::kBorderStyleKTable);
5295 case eCSSProperty_border_bottom_width:
5296 case eCSSProperty_border_end_width_value: // for internal use
5297 case eCSSProperty_border_left_width_value: // for internal use
5298 case eCSSProperty_border_right_width_value: // for internal use
5299 case eCSSProperty_border_start_width_value: // for internal use
5300 case eCSSProperty_border_top_width:
5301 case eCSSProperty__moz_column_rule_width:
5302 return ParsePositiveVariant(aValue, VARIANT_HKL,
5303 nsCSSProps::kBorderWidthKTable);
5304 case eCSSProperty__moz_column_count:
5305 return ParsePositiveVariant(aValue, VARIANT_AHI, nsnull);
5306 case eCSSProperty__moz_column_width:
5307 return ParsePositiveVariant(aValue, VARIANT_AHL, nsnull);
5308 case eCSSProperty__moz_column_gap:
5309 return ParsePositiveVariant(aValue, VARIANT_HL | VARIANT_NORMAL, nsnull);
5310 case eCSSProperty_bottom:
5311 case eCSSProperty_top:
5312 case eCSSProperty_left:
5313 case eCSSProperty_right:
5314 return ParseVariant(aValue, VARIANT_AHLP, nsnull);
5315 case eCSSProperty_box_align:
5316 return ParseVariant(aValue, VARIANT_HK,
5317 nsCSSProps::kBoxAlignKTable);
5318 case eCSSProperty_box_direction:
5319 return ParseVariant(aValue, VARIANT_HK,
5320 nsCSSProps::kBoxDirectionKTable);
5321 case eCSSProperty_box_flex:
5322 return ParsePositiveVariant(aValue, VARIANT_HN, nsnull);
5323 case eCSSProperty_box_orient:
5324 return ParseVariant(aValue, VARIANT_HK,
5325 nsCSSProps::kBoxOrientKTable);
5326 case eCSSProperty_box_pack:
5327 return ParseVariant(aValue, VARIANT_HK,
5328 nsCSSProps::kBoxPackKTable);
5329 case eCSSProperty_box_ordinal_group:
5330 return ParseVariant(aValue, VARIANT_HI, nsnull);
5331 #ifdef MOZ_SVG
5332 case eCSSProperty_clip_path:
5333 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5334 case eCSSProperty_clip_rule:
5335 return ParseVariant(aValue, VARIANT_HK,
5336 nsCSSProps::kFillRuleKTable);
5337 case eCSSProperty_color_interpolation:
5338 case eCSSProperty_color_interpolation_filters:
5339 return ParseVariant(aValue, VARIANT_AHK,
5340 nsCSSProps::kColorInterpolationKTable);
5341 case eCSSProperty_dominant_baseline:
5342 return ParseVariant(aValue, VARIANT_AHK,
5343 nsCSSProps::kDominantBaselineKTable);
5344 case eCSSProperty_fill_opacity:
5345 return ParseVariant(aValue, VARIANT_HN,
5346 nsnull);
5347 case eCSSProperty_fill_rule:
5348 return ParseVariant(aValue, VARIANT_HK,
5349 nsCSSProps::kFillRuleKTable);
5350 case eCSSProperty_filter:
5351 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5352 case eCSSProperty_flood_color:
5353 return ParseVariant(aValue, VARIANT_HC, nsnull);
5354 case eCSSProperty_flood_opacity:
5355 return ParseVariant(aValue, VARIANT_HN, nsnull);
5356 case eCSSProperty_lighting_color:
5357 return ParseVariant(aValue, VARIANT_HC, nsnull);
5358 case eCSSProperty_marker_end:
5359 case eCSSProperty_marker_mid:
5360 case eCSSProperty_marker_start:
5361 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5362 case eCSSProperty_mask:
5363 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5364 case eCSSProperty_pointer_events:
5365 return ParseVariant(aValue, VARIANT_HOK,
5366 nsCSSProps::kPointerEventsKTable);
5367 case eCSSProperty_shape_rendering:
5368 return ParseVariant(aValue, VARIANT_AHK,
5369 nsCSSProps::kShapeRenderingKTable);
5370 case eCSSProperty_stop_color:
5371 return ParseVariant(aValue, VARIANT_HC,
5372 nsnull);
5373 case eCSSProperty_stop_opacity:
5374 return ParseVariant(aValue, VARIANT_HN,
5375 nsnull);
5376 case eCSSProperty_stroke_dashoffset:
5377 return ParseVariant(aValue, VARIANT_HLPN,
5378 nsnull);
5379 case eCSSProperty_stroke_linecap:
5380 return ParseVariant(aValue, VARIANT_HK,
5381 nsCSSProps::kStrokeLinecapKTable);
5382 case eCSSProperty_stroke_linejoin:
5383 return ParseVariant(aValue, VARIANT_HK,
5384 nsCSSProps::kStrokeLinejoinKTable);
5385 case eCSSProperty_stroke_miterlimit:
5386 return ParsePositiveVariant(aValue, VARIANT_HN,
5387 nsnull);
5388 case eCSSProperty_stroke_opacity:
5389 return ParseVariant(aValue, VARIANT_HN,
5390 nsnull);
5391 case eCSSProperty_stroke_width:
5392 return ParsePositiveVariant(aValue, VARIANT_HLPN,
5393 nsnull);
5394 case eCSSProperty_text_anchor:
5395 return ParseVariant(aValue, VARIANT_HK,
5396 nsCSSProps::kTextAnchorKTable);
5397 case eCSSProperty_text_rendering:
5398 return ParseVariant(aValue, VARIANT_AHK,
5399 nsCSSProps::kTextRenderingKTable);
5400 #endif
5401 case eCSSProperty_box_sizing:
5402 return ParseVariant(aValue, VARIANT_HK,
5403 nsCSSProps::kBoxSizingKTable);
5404 case eCSSProperty_height:
5405 return ParsePositiveVariant(aValue, VARIANT_AHLP, nsnull);
5406 case eCSSProperty_width:
5407 return ParsePositiveVariant(aValue, VARIANT_AHKLP,
5408 nsCSSProps::kWidthKTable);
5409 case eCSSProperty_force_broken_image_icon:
5410 return ParsePositiveVariant(aValue, VARIANT_HI, nsnull);
5411 case eCSSProperty_caption_side:
5412 return ParseVariant(aValue, VARIANT_HK,
5413 nsCSSProps::kCaptionSideKTable);
5414 case eCSSProperty_clear:
5415 return ParseVariant(aValue, VARIANT_HOK,
5416 nsCSSProps::kClearKTable);
5417 case eCSSProperty_color:
5418 return ParseVariant(aValue, VARIANT_HC, nsnull);
5419 case eCSSProperty_cue_after:
5420 case eCSSProperty_cue_before:
5421 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5422 case eCSSProperty_direction:
5423 return ParseVariant(aValue, VARIANT_HK,
5424 nsCSSProps::kDirectionKTable);
5425 case eCSSProperty_display:
5426 if (ParseVariant(aValue, VARIANT_HOK, nsCSSProps::kDisplayKTable)) {
5427 if (aValue.GetUnit() == eCSSUnit_Enumerated) {
5428 switch (aValue.GetIntValue()) {
5429 case NS_STYLE_DISPLAY_MARKER: // bug 2055
5430 case NS_STYLE_DISPLAY_RUN_IN: // bug 2056
5431 case NS_STYLE_DISPLAY_COMPACT: // bug 14983
5432 return PR_FALSE;
5435 return PR_TRUE;
5437 return PR_FALSE;
5438 case eCSSProperty_elevation:
5439 return ParseVariant(aValue, VARIANT_HK | VARIANT_ANGLE,
5440 nsCSSProps::kElevationKTable);
5441 case eCSSProperty_empty_cells:
5442 return ParseVariant(aValue, VARIANT_HK,
5443 nsCSSProps::kEmptyCellsKTable);
5444 case eCSSProperty_float:
5445 return ParseVariant(aValue, VARIANT_HOK,
5446 nsCSSProps::kFloatKTable);
5447 case eCSSProperty_float_edge:
5448 return ParseVariant(aValue, VARIANT_HK,
5449 nsCSSProps::kFloatEdgeKTable);
5450 case eCSSProperty_font_family:
5451 return ParseFamily(aValue);
5452 case eCSSProperty_font_size:
5453 return ParsePositiveVariant(aValue,
5454 VARIANT_HKLP | VARIANT_SYSFONT,
5455 nsCSSProps::kFontSizeKTable);
5456 case eCSSProperty_font_size_adjust:
5457 return ParseVariant(aValue, VARIANT_HON | VARIANT_SYSFONT,
5458 nsnull);
5459 case eCSSProperty_font_stretch:
5460 return ParseVariant(aValue, VARIANT_HMK | VARIANT_SYSFONT,
5461 nsCSSProps::kFontStretchKTable);
5462 case eCSSProperty_font_style:
5463 return ParseVariant(aValue, VARIANT_HMK | VARIANT_SYSFONT,
5464 nsCSSProps::kFontStyleKTable);
5465 case eCSSProperty_font_variant:
5466 return ParseVariant(aValue, VARIANT_HMK | VARIANT_SYSFONT,
5467 nsCSSProps::kFontVariantKTable);
5468 case eCSSProperty_font_weight:
5469 return ParseFontWeight(aValue);
5470 case eCSSProperty_ime_mode:
5471 return ParseVariant(aValue, VARIANT_AHK | VARIANT_NORMAL,
5472 nsCSSProps::kIMEModeKTable);
5473 case eCSSProperty_letter_spacing:
5474 case eCSSProperty_word_spacing:
5475 return ParseVariant(aValue, VARIANT_HL | VARIANT_NORMAL, nsnull);
5476 case eCSSProperty_line_height:
5477 return ParsePositiveVariant(aValue, VARIANT_HLPN | VARIANT_NORMAL | VARIANT_SYSFONT, nsnull);
5478 case eCSSProperty_list_style_image:
5479 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5480 case eCSSProperty_list_style_position:
5481 return ParseVariant(aValue, VARIANT_HK, nsCSSProps::kListStylePositionKTable);
5482 case eCSSProperty_list_style_type:
5483 return ParseVariant(aValue, VARIANT_HOK, nsCSSProps::kListStyleKTable);
5484 case eCSSProperty_margin_bottom:
5485 case eCSSProperty_margin_end_value: // for internal use
5486 case eCSSProperty_margin_left_value: // for internal use
5487 case eCSSProperty_margin_right_value: // for internal use
5488 case eCSSProperty_margin_start_value: // for internal use
5489 case eCSSProperty_margin_top:
5490 return ParseVariant(aValue, VARIANT_AHLP, nsnull);
5491 case eCSSProperty_marker_offset:
5492 return ParseVariant(aValue, VARIANT_AHL, nsnull);
5493 case eCSSProperty_marks:
5494 return ParseMarks(aValue);
5495 case eCSSProperty_max_height:
5496 return ParsePositiveVariant(aValue, VARIANT_HLPO, nsnull);
5497 case eCSSProperty_max_width:
5498 return ParsePositiveVariant(aValue, VARIANT_HKLPO,
5499 nsCSSProps::kWidthKTable);
5500 case eCSSProperty_min_height:
5501 return ParsePositiveVariant(aValue, VARIANT_HLP, nsnull);
5502 case eCSSProperty_min_width:
5503 return ParsePositiveVariant(aValue, VARIANT_HKLP,
5504 nsCSSProps::kWidthKTable);
5505 case eCSSProperty_opacity:
5506 return ParseVariant(aValue, VARIANT_HN, nsnull);
5507 case eCSSProperty_orphans:
5508 case eCSSProperty_widows:
5509 return ParseVariant(aValue, VARIANT_HI, nsnull);
5510 case eCSSProperty_outline_color:
5511 return ParseVariant(aValue, VARIANT_HCK,
5512 nsCSSProps::kOutlineColorKTable);
5513 case eCSSProperty_outline_style:
5514 return ParseVariant(aValue, VARIANT_HOK | VARIANT_AUTO,
5515 nsCSSProps::kOutlineStyleKTable);
5516 case eCSSProperty_outline_width:
5517 return ParsePositiveVariant(aValue, VARIANT_HKL,
5518 nsCSSProps::kBorderWidthKTable);
5519 case eCSSProperty_outline_offset:
5520 return ParseVariant(aValue, VARIANT_HL, nsnull);
5521 case eCSSProperty_overflow_x:
5522 case eCSSProperty_overflow_y:
5523 return ParseVariant(aValue, VARIANT_AHK,
5524 nsCSSProps::kOverflowSubKTable);
5525 case eCSSProperty_padding_bottom:
5526 case eCSSProperty_padding_end_value: // for internal use
5527 case eCSSProperty_padding_left_value: // for internal use
5528 case eCSSProperty_padding_right_value: // for internal use
5529 case eCSSProperty_padding_start_value: // for internal use
5530 case eCSSProperty_padding_top:
5531 return ParsePositiveVariant(aValue, VARIANT_HLP, nsnull);
5532 case eCSSProperty_page:
5533 return ParseVariant(aValue, VARIANT_AUTO | VARIANT_IDENTIFIER, nsnull);
5534 case eCSSProperty_page_break_after:
5535 case eCSSProperty_page_break_before:
5536 return ParseVariant(aValue, VARIANT_AHK,
5537 nsCSSProps::kPageBreakKTable);
5538 case eCSSProperty_page_break_inside:
5539 return ParseVariant(aValue, VARIANT_AHK,
5540 nsCSSProps::kPageBreakInsideKTable);
5541 case eCSSProperty_pause_after:
5542 case eCSSProperty_pause_before:
5543 return ParseVariant(aValue, VARIANT_HTP, nsnull);
5544 case eCSSProperty_pitch:
5545 return ParseVariant(aValue, VARIANT_HKF, nsCSSProps::kPitchKTable);
5546 case eCSSProperty_pitch_range:
5547 return ParseVariant(aValue, VARIANT_HN, nsnull);
5548 case eCSSProperty_position:
5549 return ParseVariant(aValue, VARIANT_HK, nsCSSProps::kPositionKTable);
5550 case eCSSProperty_richness:
5551 return ParseVariant(aValue, VARIANT_HN, nsnull);
5552 #ifdef MOZ_MATHML
5553 // script-level can take Integer or Number values, but only Integer ("relative")
5554 // values can be specified in a style sheet. Also we only allow this property
5555 // when unsafe rules are enabled, because otherwise it could interfere
5556 // with rulenode optimizations if used in a non-MathML-enabled document.
5557 case eCSSProperty_script_level:
5558 if (!mUnsafeRulesEnabled)
5559 return PR_FALSE;
5560 return ParseVariant(aValue, VARIANT_HI, nsnull);
5561 #endif
5562 case eCSSProperty_speak:
5563 return ParseVariant(aValue, VARIANT_HMK | VARIANT_NONE,
5564 nsCSSProps::kSpeakKTable);
5565 case eCSSProperty_speak_header:
5566 return ParseVariant(aValue, VARIANT_HK,
5567 nsCSSProps::kSpeakHeaderKTable);
5568 case eCSSProperty_speak_numeral:
5569 return ParseVariant(aValue, VARIANT_HK,
5570 nsCSSProps::kSpeakNumeralKTable);
5571 case eCSSProperty_speak_punctuation:
5572 return ParseVariant(aValue, VARIANT_HOK,
5573 nsCSSProps::kSpeakPunctuationKTable);
5574 case eCSSProperty_speech_rate:
5575 return ParseVariant(aValue, VARIANT_HN | VARIANT_KEYWORD,
5576 nsCSSProps::kSpeechRateKTable);
5577 case eCSSProperty_stack_sizing:
5578 return ParseVariant(aValue, VARIANT_HK,
5579 nsCSSProps::kStackSizingKTable);
5580 case eCSSProperty_stress:
5581 return ParseVariant(aValue, VARIANT_HN, nsnull);
5582 case eCSSProperty_table_layout:
5583 return ParseVariant(aValue, VARIANT_AHK,
5584 nsCSSProps::kTableLayoutKTable);
5585 case eCSSProperty_text_align:
5586 // When we support aligning on a string, we can parse text-align
5587 // as a string....
5588 return ParseVariant(aValue, VARIANT_HK /* | VARIANT_STRING */,
5589 nsCSSProps::kTextAlignKTable);
5590 case eCSSProperty_text_decoration:
5591 return ParseTextDecoration(aValue);
5592 case eCSSProperty_text_indent:
5593 return ParseVariant(aValue, VARIANT_HLP, nsnull);
5594 case eCSSProperty_text_transform:
5595 return ParseVariant(aValue, VARIANT_HOK,
5596 nsCSSProps::kTextTransformKTable);
5597 case eCSSProperty_unicode_bidi:
5598 return ParseVariant(aValue, VARIANT_HMK,
5599 nsCSSProps::kUnicodeBidiKTable);
5600 case eCSSProperty_user_focus:
5601 return ParseVariant(aValue, VARIANT_HMK | VARIANT_NONE,
5602 nsCSSProps::kUserFocusKTable);
5603 case eCSSProperty_user_input:
5604 return ParseVariant(aValue, VARIANT_AHK | VARIANT_NONE,
5605 nsCSSProps::kUserInputKTable);
5606 case eCSSProperty_user_modify:
5607 return ParseVariant(aValue, VARIANT_HK,
5608 nsCSSProps::kUserModifyKTable);
5609 case eCSSProperty_user_select:
5610 return ParseVariant(aValue, VARIANT_AHK | VARIANT_NONE,
5611 nsCSSProps::kUserSelectKTable);
5612 case eCSSProperty_vertical_align:
5613 return ParseVariant(aValue, VARIANT_HKLP,
5614 nsCSSProps::kVerticalAlignKTable);
5615 case eCSSProperty_visibility:
5616 return ParseVariant(aValue, VARIANT_HK,
5617 nsCSSProps::kVisibilityKTable);
5618 case eCSSProperty_voice_family:
5619 return ParseFamily(aValue);
5620 case eCSSProperty_volume:
5621 return ParseVariant(aValue, VARIANT_HPN | VARIANT_KEYWORD,
5622 nsCSSProps::kVolumeKTable);
5623 case eCSSProperty_white_space:
5624 return ParseVariant(aValue, VARIANT_HMK,
5625 nsCSSProps::kWhitespaceKTable);
5626 case eCSSProperty__moz_window_shadow:
5627 return ParseVariant(aValue, VARIANT_HOK,
5628 nsCSSProps::kWindowShadowKTable);
5629 case eCSSProperty_word_wrap:
5630 return ParseVariant(aValue, VARIANT_HMK,
5631 nsCSSProps::kWordwrapKTable);
5632 case eCSSProperty_z_index:
5633 return ParseVariant(aValue, VARIANT_AHI, nsnull);
5635 // explicitly do NOT have a default case to let the compiler
5636 // help find missing properties
5637 return PR_FALSE;
5640 // nsFont::EnumerateFamilies callback for ParseFontDescriptorValue
5641 struct NS_STACK_CLASS ExtractFirstFamilyData {
5642 nsAutoString mFamilyName;
5643 PRBool mGood;
5644 ExtractFirstFamilyData() : mFamilyName(), mGood(PR_FALSE) {}
5647 static PRBool
5648 ExtractFirstFamily(const nsString& aFamily,
5649 PRBool aGeneric,
5650 void* aData)
5652 ExtractFirstFamilyData* realData = (ExtractFirstFamilyData*) aData;
5653 if (aGeneric || realData->mFamilyName.Length() > 0) {
5654 realData->mGood = PR_FALSE;
5655 return PR_FALSE;
5657 realData->mFamilyName.Assign(aFamily);
5658 realData->mGood = PR_TRUE;
5659 return PR_TRUE;
5662 // font-descriptor: descriptor ':' value ';'
5663 // caller has advanced mToken to point at the descriptor
5664 PRBool
5665 CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID,
5666 nsCSSValue& aValue)
5668 switch (aDescID) {
5669 // These four are similar to the properties of the same name,
5670 // possibly with more restrictions on the values they can take.
5671 case eCSSFontDesc_Family: {
5672 if (!ParseFamily(aValue) ||
5673 aValue.GetUnit() != eCSSUnit_String)
5674 return PR_FALSE;
5676 // the style parameters to the nsFont constructor are ignored,
5677 // because it's only being used to call EnumerateFamilies
5678 nsAutoString valueStr;
5679 aValue.GetStringValue(valueStr);
5680 nsFont font(valueStr, 0, 0, 0, 0, 0);
5681 ExtractFirstFamilyData dat;
5683 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
5684 if (!dat.mGood)
5685 return PR_FALSE;
5687 aValue.SetStringValue(dat.mFamilyName, eCSSUnit_String);
5688 return PR_TRUE;
5691 case eCSSFontDesc_Style:
5692 // property is VARIANT_HMK|VARIANT_SYSFONT
5693 return ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
5694 nsCSSProps::kFontStyleKTable);
5696 case eCSSFontDesc_Weight:
5697 return (ParseFontWeight(aValue) &&
5698 aValue.GetUnit() != eCSSUnit_Inherit &&
5699 aValue.GetUnit() != eCSSUnit_Initial &&
5700 (aValue.GetUnit() != eCSSUnit_Enumerated ||
5701 (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
5702 aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
5704 case eCSSFontDesc_Stretch:
5705 // property is VARIANT_HMK|VARIANT_SYSFONT
5706 return (ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
5707 nsCSSProps::kFontStretchKTable) &&
5708 (aValue.GetUnit() != eCSSUnit_Enumerated ||
5709 (aValue.GetIntValue() != NS_STYLE_FONT_STRETCH_WIDER &&
5710 aValue.GetIntValue() != NS_STYLE_FONT_STRETCH_NARROWER)));
5712 // These two are unique to @font-face and have their own special grammar.
5713 case eCSSFontDesc_Src:
5714 return ParseFontSrc(aValue);
5716 case eCSSFontDesc_UnicodeRange:
5717 return ParseFontRanges(aValue);
5719 case eCSSFontDesc_UNKNOWN:
5720 case eCSSFontDesc_COUNT:
5721 NS_NOTREACHED("bad nsCSSFontDesc code");
5723 // explicitly do NOT have a default case to let the compiler
5724 // help find missing descriptors
5725 return PR_FALSE;
5728 void
5729 CSSParserImpl::InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties)
5731 nsCSSValue physical(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
5732 for (const nsCSSProperty *prop = aSourceProperties;
5733 *prop != eCSSProperty_UNKNOWN; ++prop) {
5734 AppendValue(*prop, physical);
5738 PRBool
5739 CSSParserImpl::ParseAzimuth(nsCSSValue& aValue)
5741 if (ParseVariant(aValue, VARIANT_HK | VARIANT_ANGLE,
5742 nsCSSProps::kAzimuthKTable)) {
5743 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
5744 PRInt32 intValue = aValue.GetIntValue();
5745 if ((NS_STYLE_AZIMUTH_LEFT_SIDE <= intValue) &&
5746 (intValue <= NS_STYLE_AZIMUTH_BEHIND)) { // look for optional modifier
5747 nsCSSValue modifier;
5748 if (ParseEnum(modifier, nsCSSProps::kAzimuthKTable)) {
5749 PRInt32 enumValue = modifier.GetIntValue();
5750 if (((intValue == NS_STYLE_AZIMUTH_BEHIND) &&
5751 (NS_STYLE_AZIMUTH_LEFT_SIDE <= enumValue) && (enumValue <= NS_STYLE_AZIMUTH_RIGHT_SIDE)) ||
5752 ((enumValue == NS_STYLE_AZIMUTH_BEHIND) &&
5753 (NS_STYLE_AZIMUTH_LEFT_SIDE <= intValue) && (intValue <= NS_STYLE_AZIMUTH_RIGHT_SIDE))) {
5754 aValue.SetIntValue(intValue | enumValue, eCSSUnit_Enumerated);
5755 return PR_TRUE;
5757 // Put the unknown identifier back and return
5758 UngetToken();
5759 return PR_FALSE;
5763 return PR_TRUE;
5765 return PR_FALSE;
5768 static nsCSSValue
5769 BoxPositionMaskToCSSValue(PRInt32 aMask, PRBool isX)
5771 PRInt32 val = NS_STYLE_BG_POSITION_CENTER;
5772 if (isX) {
5773 if (aMask & BG_LEFT) {
5774 val = NS_STYLE_BG_POSITION_LEFT;
5776 else if (aMask & BG_RIGHT) {
5777 val = NS_STYLE_BG_POSITION_RIGHT;
5780 else {
5781 if (aMask & BG_TOP) {
5782 val = NS_STYLE_BG_POSITION_TOP;
5784 else if (aMask & BG_BOTTOM) {
5785 val = NS_STYLE_BG_POSITION_BOTTOM;
5789 return nsCSSValue(val, eCSSUnit_Enumerated);
5792 PRBool
5793 CSSParserImpl::ParseBackground()
5795 nsAutoParseCompoundProperty compound(this);
5797 // Fill in the values that the shorthand will set if we don't find
5798 // other values.
5799 mTempData.mColor.mBackColor.SetColorValue(NS_RGBA(0, 0, 0, 0));
5800 mTempData.SetPropertyBit(eCSSProperty_background_color);
5801 mTempData.mColor.mBackImage.SetNoneValue();
5802 mTempData.SetPropertyBit(eCSSProperty_background_image);
5803 mTempData.mColor.mBackRepeat.SetIntValue(NS_STYLE_BG_REPEAT_XY,
5804 eCSSUnit_Enumerated);
5805 mTempData.SetPropertyBit(eCSSProperty_background_repeat);
5806 mTempData.mColor.mBackAttachment.SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL,
5807 eCSSUnit_Enumerated);
5808 mTempData.SetPropertyBit(eCSSProperty_background_attachment);
5809 mTempData.mColor.mBackPosition.mXValue.SetPercentValue(0.0f);
5810 mTempData.mColor.mBackPosition.mYValue.SetPercentValue(0.0f);
5811 mTempData.SetPropertyBit(eCSSProperty_background_position);
5812 // including the ones that we can't set from the shorthand.
5813 mTempData.mColor.mBackClip.SetIntValue(NS_STYLE_BG_CLIP_BORDER,
5814 eCSSUnit_Enumerated);
5815 mTempData.SetPropertyBit(eCSSProperty__moz_background_clip);
5816 mTempData.mColor.mBackOrigin.SetIntValue(NS_STYLE_BG_ORIGIN_PADDING,
5817 eCSSUnit_Enumerated);
5818 mTempData.SetPropertyBit(eCSSProperty__moz_background_origin);
5819 mTempData.mColor.mBackInlinePolicy.SetIntValue(
5820 NS_STYLE_BG_INLINE_POLICY_CONTINUOUS, eCSSUnit_Enumerated);
5821 mTempData.SetPropertyBit(eCSSProperty__moz_background_inline_policy);
5823 // XXX If ParseSingleValueProperty were table-driven (bug 376079) and
5824 // automatically filled in the right field of mTempData, we could move
5825 // ParseBackgroundPosition to it (as a special case) and switch back
5826 // to using ParseChoice here.
5828 PRBool haveColor = PR_FALSE,
5829 haveImage = PR_FALSE,
5830 haveRepeat = PR_FALSE,
5831 haveAttach = PR_FALSE,
5832 havePosition = PR_FALSE;
5833 while (GetToken(PR_TRUE)) {
5834 nsCSSTokenType tt = mToken.mType;
5835 UngetToken(); // ...but we'll still cheat and use mToken
5836 if (tt == eCSSToken_Symbol) {
5837 // ExpectEndProperty only looks for symbols, and nothing else will
5838 // show up as one.
5839 break;
5842 if (tt == eCSSToken_Ident) {
5843 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
5844 PRInt32 dummy;
5845 if (keyword == eCSSKeyword_inherit ||
5846 keyword == eCSSKeyword__moz_initial) {
5847 if (haveColor || haveImage || haveRepeat || haveAttach || havePosition)
5848 return PR_FALSE;
5849 haveColor = haveImage = haveRepeat = haveAttach = havePosition =
5850 PR_TRUE;
5851 GetToken(PR_TRUE); // undo the UngetToken above
5852 nsCSSValue val;
5853 if (keyword == eCSSKeyword_inherit) {
5854 val.SetInheritValue();
5855 } else {
5856 val.SetInitialValue();
5858 mTempData.mColor.mBackColor = val;
5859 mTempData.mColor.mBackImage = val;
5860 mTempData.mColor.mBackRepeat = val;
5861 mTempData.mColor.mBackAttachment = val;
5862 mTempData.mColor.mBackPosition.mXValue = val;
5863 mTempData.mColor.mBackPosition.mYValue = val;
5864 // Reset (for 'inherit') the 3 properties that can't be
5865 // specified, although it's not entirely clear in the spec:
5866 // http://lists.w3.org/Archives/Public/www-style/2007Mar/0110
5867 mTempData.mColor.mBackClip = val;
5868 mTempData.mColor.mBackOrigin = val;
5869 mTempData.mColor.mBackInlinePolicy = val;
5870 break;
5871 } else if (keyword == eCSSKeyword_none) {
5872 if (haveImage)
5873 return PR_FALSE;
5874 haveImage = PR_TRUE;
5875 if (!ParseSingleValueProperty(mTempData.mColor.mBackImage,
5876 eCSSProperty_background_image)) {
5877 NS_NOTREACHED("should be able to parse");
5878 return PR_FALSE;
5880 } else if (nsCSSProps::FindKeyword(keyword,
5881 nsCSSProps::kBackgroundAttachmentKTable, dummy)) {
5882 if (haveAttach)
5883 return PR_FALSE;
5884 haveAttach = PR_TRUE;
5885 if (!ParseSingleValueProperty(mTempData.mColor.mBackAttachment,
5886 eCSSProperty_background_attachment)) {
5887 NS_NOTREACHED("should be able to parse");
5888 return PR_FALSE;
5890 } else if (nsCSSProps::FindKeyword(keyword,
5891 nsCSSProps::kBackgroundRepeatKTable, dummy)) {
5892 if (haveRepeat)
5893 return PR_FALSE;
5894 haveRepeat = PR_TRUE;
5895 if (!ParseSingleValueProperty(mTempData.mColor.mBackRepeat,
5896 eCSSProperty_background_repeat)) {
5897 NS_NOTREACHED("should be able to parse");
5898 return PR_FALSE;
5900 } else if (nsCSSProps::FindKeyword(keyword,
5901 nsCSSProps::kBackgroundPositionKTable, dummy)) {
5902 if (havePosition)
5903 return PR_FALSE;
5904 havePosition = PR_TRUE;
5905 if (!ParseBackgroundPositionValues()) {
5906 return PR_FALSE;
5908 } else {
5909 if (haveColor)
5910 return PR_FALSE;
5911 haveColor = PR_TRUE;
5912 if (!ParseSingleValueProperty(mTempData.mColor.mBackColor,
5913 eCSSProperty_background_color)) {
5914 return PR_FALSE;
5917 } else if (eCSSToken_Function == tt &&
5918 mToken.mIdent.LowerCaseEqualsLiteral("url")) {
5919 if (haveImage)
5920 return PR_FALSE;
5921 haveImage = PR_TRUE;
5922 if (!ParseSingleValueProperty(mTempData.mColor.mBackImage,
5923 eCSSProperty_background_image)) {
5924 return PR_FALSE;
5926 } else if (mToken.IsDimension() || tt == eCSSToken_Percentage) {
5927 if (havePosition)
5928 return PR_FALSE;
5929 havePosition = PR_TRUE;
5930 if (!ParseBackgroundPositionValues()) {
5931 return PR_FALSE;
5933 } else {
5934 if (haveColor)
5935 return PR_FALSE;
5936 haveColor = PR_TRUE;
5937 if (!ParseSingleValueProperty(mTempData.mColor.mBackColor,
5938 eCSSProperty_background_color)) {
5939 return PR_FALSE;
5944 return ExpectEndProperty() &&
5945 (haveColor || haveImage || haveRepeat || haveAttach || havePosition);
5948 PRBool
5949 CSSParserImpl::ParseBackgroundPosition()
5951 if (!ParseBoxPosition(mTempData.mColor.mBackPosition))
5952 return PR_FALSE;
5953 mTempData.SetPropertyBit(eCSSProperty_background_position);
5954 return PR_TRUE;
5957 PRBool
5958 CSSParserImpl::ParseBackgroundPositionValues()
5960 return ParseBoxPositionValues(mTempData.mColor.mBackPosition);
5964 * Parses two values that correspond to positions in a box. These can be
5965 * values corresponding to percentages of the box, raw offsets, or keywords
5966 * like "top," "left center," etc.
5968 * @param aOut The nsCSSValuePair where to place the result.
5969 * @return Whether or not the operation succeeded.
5971 PRBool CSSParserImpl::ParseBoxPosition(nsCSSValuePair &aOut)
5973 // Need to read the box positions and the end of the property.
5974 return ParseBoxPositionValues(aOut) && ExpectEndProperty();
5977 PRBool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut)
5979 // First try a percentage or a length value
5980 nsCSSValue &xValue = aOut.mXValue,
5981 &yValue = aOut.mYValue;
5982 if (ParseVariant(xValue, VARIANT_HLP, nsnull)) {
5983 if (eCSSUnit_Inherit == xValue.GetUnit() ||
5984 eCSSUnit_Initial == xValue.GetUnit()) { // both are inherited or both are set to initial
5985 yValue = xValue;
5986 return PR_TRUE;
5988 // We have one percentage/length. Get the optional second
5989 // percentage/length/keyword.
5990 if (ParseVariant(yValue, VARIANT_LP, nsnull)) {
5991 // We have two numbers
5992 return PR_TRUE;
5995 if (ParseEnum(yValue, nsCSSProps::kBackgroundPositionKTable)) {
5996 PRInt32 yVal = yValue.GetIntValue();
5997 if (!(yVal & BG_CTB)) {
5998 // The second keyword can only be 'center', 'top', or 'bottom'
5999 return PR_FALSE;
6001 yValue = BoxPositionMaskToCSSValue(yVal, PR_FALSE);
6002 return PR_TRUE;
6005 // If only one percentage or length value is given, it sets the
6006 // horizontal position only, and the vertical position will be 50%.
6007 yValue.SetPercentValue(0.5f);
6008 return PR_TRUE;
6011 // Now try keywords. We do this manually to allow for the first
6012 // appearance of "center" to apply to the either the x or y
6013 // position (it's ambiguous so we have to disambiguate). Each
6014 // allowed keyword value is assigned it's own bit. We don't allow
6015 // any duplicate keywords other than center. We try to get two
6016 // keywords but it's okay if there is only one.
6017 PRInt32 mask = 0;
6018 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
6019 PRInt32 bit = xValue.GetIntValue();
6020 mask |= bit;
6021 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
6022 bit = xValue.GetIntValue();
6023 if (mask & (bit & ~BG_CENTER)) {
6024 // Only the 'center' keyword can be duplicated.
6025 return PR_FALSE;
6027 mask |= bit;
6029 else {
6030 // Only one keyword. See if we have a length or percentage.
6031 if (ParseVariant(yValue, VARIANT_LP, nsnull)) {
6032 if (!(mask & BG_CLR)) {
6033 // The first keyword can only be 'center', 'left', or 'right'
6034 return PR_FALSE;
6037 xValue = BoxPositionMaskToCSSValue(mask, PR_TRUE);
6038 return PR_TRUE;
6043 // Check for bad input. Bad input consists of no matching keywords,
6044 // or pairs of x keywords or pairs of y keywords.
6045 if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
6046 (mask == (BG_LEFT | BG_RIGHT))) {
6047 return PR_FALSE;
6050 // Create style values
6051 xValue = BoxPositionMaskToCSSValue(mask, PR_TRUE);
6052 yValue = BoxPositionMaskToCSSValue(mask, PR_FALSE);
6053 return PR_TRUE;
6056 PRBool
6057 CSSParserImpl::ParseBorderColor()
6059 static const nsCSSProperty kBorderColorSources[] = {
6060 eCSSProperty_border_left_color_ltr_source,
6061 eCSSProperty_border_left_color_rtl_source,
6062 eCSSProperty_border_right_color_ltr_source,
6063 eCSSProperty_border_right_color_rtl_source,
6064 eCSSProperty_UNKNOWN
6067 // do this now, in case 4 values weren't specified
6068 InitBoxPropsAsPhysical(kBorderColorSources);
6069 return ParseBoxProperties(mTempData.mMargin.mBorderColor,
6070 kBorderColorIDs);
6073 PRBool
6074 CSSParserImpl::ParseBorderImage()
6076 if (ParseVariant(mTempData.mMargin.mBorderImage,
6077 VARIANT_INHERIT | VARIANT_NONE, nsnull)) {
6078 mTempData.SetPropertyBit(eCSSProperty_border_image);
6079 return PR_TRUE;
6082 // <uri> [<number> | <percentage>]{1,4} [ / <border-width>{1,4} ]? [stretch | repeat | round]{0,2}
6083 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(11);
6084 if (!arr) {
6085 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6086 return PR_FALSE;
6089 nsCSSValue& url = arr->Item(0);
6090 nsCSSValue& splitTop = arr->Item(1);
6091 nsCSSValue& splitRight = arr->Item(2);
6092 nsCSSValue& splitBottom = arr->Item(3);
6093 nsCSSValue& splitLeft = arr->Item(4);
6094 nsCSSValue& borderWidthTop = arr->Item(5);
6095 nsCSSValue& borderWidthRight = arr->Item(6);
6096 nsCSSValue& borderWidthBottom = arr->Item(7);
6097 nsCSSValue& borderWidthLeft = arr->Item(8);
6098 nsCSSValue& horizontalKeyword = arr->Item(9);
6099 nsCSSValue& verticalKeyword = arr->Item(10);
6101 // <uri>
6102 if (!ParseVariant(url, VARIANT_URL, nsnull)) {
6103 return PR_FALSE;
6106 // [<number> | <percentage>]{1,4}
6107 if (!ParsePositiveVariant(splitTop,
6108 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
6109 return PR_FALSE;
6111 if (!ParsePositiveVariant(splitRight,
6112 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
6113 splitRight = splitTop;
6115 if (!ParsePositiveVariant(splitBottom,
6116 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
6117 splitBottom = splitTop;
6119 if (!ParsePositiveVariant(splitLeft,
6120 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
6121 splitLeft = splitRight;
6124 // [ / <border-width>{1,4} ]?
6125 if (ExpectSymbol('/', PR_TRUE)) {
6126 // if have '/', at least one value is required
6127 if (!ParsePositiveVariant(borderWidthTop, VARIANT_LENGTH, nsnull)) {
6128 return PR_FALSE;
6130 if (!ParsePositiveVariant(borderWidthRight, VARIANT_LENGTH, nsnull)) {
6131 borderWidthRight = borderWidthTop;
6133 if (!ParsePositiveVariant(borderWidthBottom, VARIANT_LENGTH, nsnull)) {
6134 borderWidthBottom = borderWidthTop;
6136 if (!ParsePositiveVariant(borderWidthLeft, VARIANT_LENGTH, nsnull)) {
6137 borderWidthLeft = borderWidthRight;
6141 // [stretch | repeat | round]{0,2}
6142 // missing keywords are handled in nsRuleNode::ComputeBorderData()
6143 if (ParseEnum(horizontalKeyword, nsCSSProps::kBorderImageKTable)) {
6144 ParseEnum(verticalKeyword, nsCSSProps::kBorderImageKTable);
6147 if (!ExpectEndProperty()) {
6148 return PR_FALSE;
6151 mTempData.mMargin.mBorderImage.SetArrayValue(arr, eCSSUnit_Array);
6152 mTempData.SetPropertyBit(eCSSProperty_border_image);
6154 return PR_TRUE;
6157 PRBool
6158 CSSParserImpl::ParseBorderSpacing()
6160 nsCSSValue xValue;
6161 if (ParsePositiveVariant(xValue, VARIANT_HL, nsnull)) {
6162 if (xValue.IsLengthUnit()) {
6163 // We have one length. Get the optional second length.
6164 nsCSSValue yValue;
6165 if (ParsePositiveVariant(yValue, VARIANT_LENGTH, nsnull)) {
6166 // We have two numbers
6167 if (ExpectEndProperty()) {
6168 mTempData.mTable.mBorderSpacing.mXValue = xValue;
6169 mTempData.mTable.mBorderSpacing.mYValue = yValue;
6170 mTempData.SetPropertyBit(eCSSProperty_border_spacing);
6171 return PR_TRUE;
6173 return PR_FALSE;
6177 // We have one length which is the horizontal spacing. Create a value for
6178 // the vertical spacing which is equal
6179 if (ExpectEndProperty()) {
6180 mTempData.mTable.mBorderSpacing.SetBothValuesTo(xValue);
6181 mTempData.SetPropertyBit(eCSSProperty_border_spacing);
6182 return PR_TRUE;
6185 return PR_FALSE;
6188 PRBool
6189 CSSParserImpl::ParseBorderSide(const nsCSSProperty aPropIDs[],
6190 PRBool aSetAllSides)
6192 const PRInt32 numProps = 3;
6193 nsCSSValue values[numProps];
6195 PRInt32 found = ParseChoice(values, aPropIDs, numProps);
6196 if ((found < 1) || (PR_FALSE == ExpectEndProperty())) {
6197 return PR_FALSE;
6200 if ((found & 1) == 0) { // Provide default border-width
6201 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
6203 if ((found & 2) == 0) { // Provide default border-style
6204 values[1].SetNoneValue();
6206 if ((found & 4) == 0) { // text color will be used
6207 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
6210 if (aSetAllSides) {
6211 static const nsCSSProperty kBorderSources[] = {
6212 eCSSProperty_border_left_color_ltr_source,
6213 eCSSProperty_border_left_color_rtl_source,
6214 eCSSProperty_border_right_color_ltr_source,
6215 eCSSProperty_border_right_color_rtl_source,
6216 eCSSProperty_border_left_style_ltr_source,
6217 eCSSProperty_border_left_style_rtl_source,
6218 eCSSProperty_border_right_style_ltr_source,
6219 eCSSProperty_border_right_style_rtl_source,
6220 eCSSProperty_border_left_width_ltr_source,
6221 eCSSProperty_border_left_width_rtl_source,
6222 eCSSProperty_border_right_width_ltr_source,
6223 eCSSProperty_border_right_width_rtl_source,
6224 eCSSProperty_UNKNOWN
6227 InitBoxPropsAsPhysical(kBorderSources);
6229 // Parsing "border" shorthand; set all four sides to the same thing
6230 for (PRInt32 index = 0; index < 4; index++) {
6231 NS_ASSERTION(numProps == 3, "This code needs updating");
6232 AppendValue(kBorderWidthIDs[index], values[0]);
6233 AppendValue(kBorderStyleIDs[index], values[1]);
6234 AppendValue(kBorderColorIDs[index], values[2]);
6237 else {
6238 // Just set our one side
6239 for (PRInt32 index = 0; index < numProps; index++) {
6240 AppendValue(aPropIDs[index], values[index]);
6243 return PR_TRUE;
6246 PRBool
6247 CSSParserImpl::ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
6248 PRInt32 aSourceType)
6250 const PRInt32 numProps = 3;
6251 nsCSSValue values[numProps];
6253 PRInt32 found = ParseChoice(values, aPropIDs, numProps);
6254 if ((found < 1) || (PR_FALSE == ExpectEndProperty())) {
6255 return PR_FALSE;
6258 if ((found & 1) == 0) { // Provide default border-width
6259 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
6261 if ((found & 2) == 0) { // Provide default border-style
6262 values[1].SetNoneValue();
6264 if ((found & 4) == 0) { // text color will be used
6265 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
6267 for (PRInt32 index = 0; index < numProps; index++) {
6268 const nsCSSProperty* subprops =
6269 nsCSSProps::SubpropertyEntryFor(aPropIDs[index + numProps]);
6270 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
6271 "not box property with physical vs. logical cascading");
6272 AppendValue(subprops[0], values[index]);
6273 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
6274 AppendValue(subprops[1], typeVal);
6275 AppendValue(subprops[2], typeVal);
6277 return PR_TRUE;
6280 PRBool
6281 CSSParserImpl::ParseBorderStyle()
6283 static const nsCSSProperty kBorderStyleSources[] = {
6284 eCSSProperty_border_left_style_ltr_source,
6285 eCSSProperty_border_left_style_rtl_source,
6286 eCSSProperty_border_right_style_ltr_source,
6287 eCSSProperty_border_right_style_rtl_source,
6288 eCSSProperty_UNKNOWN
6291 // do this now, in case 4 values weren't specified
6292 InitBoxPropsAsPhysical(kBorderStyleSources);
6293 return ParseBoxProperties(mTempData.mMargin.mBorderStyle,
6294 kBorderStyleIDs);
6297 PRBool
6298 CSSParserImpl::ParseBorderWidth()
6300 static const nsCSSProperty kBorderWidthSources[] = {
6301 eCSSProperty_border_left_width_ltr_source,
6302 eCSSProperty_border_left_width_rtl_source,
6303 eCSSProperty_border_right_width_ltr_source,
6304 eCSSProperty_border_right_width_rtl_source,
6305 eCSSProperty_UNKNOWN
6308 // do this now, in case 4 values weren't specified
6309 InitBoxPropsAsPhysical(kBorderWidthSources);
6310 return ParseBoxProperties(mTempData.mMargin.mBorderWidth,
6311 kBorderWidthIDs);
6314 PRBool
6315 CSSParserImpl::ParseBorderColors(nsCSSValueList** aResult,
6316 nsCSSProperty aProperty)
6318 nsCSSValue value;
6319 if (ParseVariant(value, VARIANT_HCK|VARIANT_NONE, nsCSSProps::kBorderColorKTable)) {
6320 nsCSSValueList* listHead = new nsCSSValueList();
6321 nsCSSValueList* list = listHead;
6322 if (!list) {
6323 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6324 return PR_FALSE;
6326 list->mValue = value;
6328 while (list) {
6329 if (ExpectEndProperty()) {
6330 mTempData.SetPropertyBit(aProperty);
6331 *aResult = listHead;
6332 return PR_TRUE;
6334 // FIXME Bug 389404: We should not accept inherit, -moz-initial,
6335 // or none as anything other than the first value.
6336 if (ParseVariant(value, VARIANT_HCK|VARIANT_NONE, nsCSSProps::kBorderColorKTable)) {
6337 list->mNext = new nsCSSValueList();
6338 list = list->mNext;
6339 if (list)
6340 list->mValue = value;
6341 else
6342 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6344 else
6345 break;
6347 delete listHead;
6349 return PR_FALSE;
6352 PRBool
6353 CSSParserImpl::ParseRect(nsCSSRect& aRect, nsCSSProperty aPropID)
6355 nsCSSRect rect;
6356 PRBool result;
6357 if ((result = DoParseRect(rect)) &&
6358 rect != aRect) {
6359 aRect = rect;
6360 mTempData.SetPropertyBit(aPropID);
6362 return result;
6365 PRBool
6366 CSSParserImpl::DoParseRect(nsCSSRect& aRect)
6368 if (! GetToken(PR_TRUE)) {
6369 return PR_FALSE;
6371 if (eCSSToken_Ident == mToken.mType) {
6372 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
6373 switch (keyword) {
6374 case eCSSKeyword_auto:
6375 if (ExpectEndProperty()) {
6376 aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Auto));
6377 return PR_TRUE;
6379 break;
6380 case eCSSKeyword_inherit:
6381 if (ExpectEndProperty()) {
6382 aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Inherit));
6383 return PR_TRUE;
6385 break;
6386 case eCSSKeyword__moz_initial:
6387 if (ExpectEndProperty()) {
6388 aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Initial));
6389 return PR_TRUE;
6391 break;
6392 default:
6393 UngetToken();
6394 break;
6396 } else if ((eCSSToken_Function == mToken.mType) &&
6397 mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
6398 if (!ExpectSymbol('(', PR_TRUE)) {
6399 return PR_FALSE;
6401 NS_FOR_CSS_SIDES(side) {
6402 if (! ParseVariant(aRect.*(nsCSSRect::sides[side]),
6403 VARIANT_AL, nsnull)) {
6404 return PR_FALSE;
6406 if (3 != side) {
6407 // skip optional commas between elements
6408 ExpectSymbol(',', PR_TRUE);
6411 if (!ExpectSymbol(')', PR_TRUE)) {
6412 return PR_FALSE;
6414 if (ExpectEndProperty()) {
6415 return PR_TRUE;
6417 } else {
6418 UngetToken();
6420 return PR_FALSE;
6423 #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
6424 VARIANT_KEYWORD)
6425 PRBool
6426 CSSParserImpl::ParseContent()
6428 // XXX Rewrite to make it look more like ParseCursor or ParseCounterData?
6429 nsCSSValue value;
6430 if (ParseVariant(value,
6431 VARIANT_CONTENT | VARIANT_INHERIT | VARIANT_NORMAL |
6432 VARIANT_NONE,
6433 nsCSSProps::kContentKTable)) {
6434 nsCSSValueList* listHead = new nsCSSValueList();
6435 nsCSSValueList* list = listHead;
6436 if (nsnull == list) {
6437 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6438 return PR_FALSE;
6440 list->mValue = value;
6442 while (nsnull != list) {
6443 if (ExpectEndProperty()) {
6444 mTempData.SetPropertyBit(eCSSProperty_content);
6445 mTempData.mContent.mContent = listHead;
6446 return PR_TRUE;
6448 if (eCSSUnit_Inherit == value.GetUnit() ||
6449 eCSSUnit_Initial == value.GetUnit() ||
6450 eCSSUnit_Normal == value.GetUnit() ||
6451 eCSSUnit_None == value.GetUnit() ||
6452 (eCSSUnit_Enumerated == value.GetUnit() &&
6453 NS_STYLE_CONTENT_ALT_CONTENT == value.GetIntValue())) {
6454 // This only matters the first time through the loop.
6455 delete listHead;
6456 return PR_FALSE;
6458 if (ParseVariant(value, VARIANT_CONTENT, nsCSSProps::kContentKTable) &&
6459 // Make sure we didn't end up with NS_STYLE_CONTENT_ALT_CONTENT here
6460 (value.GetUnit() != eCSSUnit_Enumerated ||
6461 value.GetIntValue() != NS_STYLE_CONTENT_ALT_CONTENT)) {
6462 list->mNext = new nsCSSValueList();
6463 list = list->mNext;
6464 if (nsnull != list) {
6465 list->mValue = value;
6467 else {
6468 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6471 else {
6472 break;
6475 delete listHead;
6477 return PR_FALSE;
6480 struct SingleCounterPropValue {
6481 char str[13];
6482 nsCSSUnit unit;
6485 PRBool
6486 CSSParserImpl::ParseCounterData(nsCSSValuePairList** aResult,
6487 nsCSSProperty aPropID)
6489 nsSubstring* ident = NextIdent();
6490 if (nsnull == ident) {
6491 return PR_FALSE;
6493 static const SingleCounterPropValue singleValues[] = {
6494 { "none", eCSSUnit_None },
6495 { "inherit", eCSSUnit_Inherit },
6496 { "-moz-initial", eCSSUnit_Initial }
6498 for (const SingleCounterPropValue *sv = singleValues,
6499 *sv_end = singleValues + NS_ARRAY_LENGTH(singleValues);
6500 sv != sv_end; ++sv) {
6501 if (ident->LowerCaseEqualsASCII(sv->str)) {
6502 if (CheckEndProperty()) {
6503 nsCSSValuePairList* dataHead = new nsCSSValuePairList();
6504 if (!dataHead) {
6505 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6506 return PR_FALSE;
6508 dataHead->mXValue = nsCSSValue(sv->unit);
6509 *aResult = dataHead;
6510 mTempData.SetPropertyBit(aPropID);
6511 return PR_TRUE;
6513 return PR_FALSE;
6516 UngetToken(); // undo NextIdent
6518 nsCSSValuePairList* dataHead = nsnull;
6519 nsCSSValuePairList **next = &dataHead;
6520 for (;;) {
6521 if (!GetToken(PR_TRUE) || mToken.mType != eCSSToken_Ident) {
6522 break;
6524 nsCSSValuePairList *data = *next = new nsCSSValuePairList();
6525 if (!data) {
6526 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6527 break;
6529 next = &data->mNext;
6530 data->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
6531 if (GetToken(PR_TRUE)) {
6532 if (eCSSToken_Number == mToken.mType && mToken.mIntegerValid) {
6533 data->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
6534 } else {
6535 UngetToken();
6538 if (ExpectEndProperty()) {
6539 mTempData.SetPropertyBit(aPropID);
6540 *aResult = dataHead;
6541 return PR_TRUE;
6544 delete dataHead;
6545 return PR_FALSE;
6548 PRBool
6549 CSSParserImpl::ParseCue()
6551 nsCSSValue before;
6552 if (ParseSingleValueProperty(before, eCSSProperty_cue_before)) {
6553 if (eCSSUnit_Inherit != before.GetUnit() &&
6554 eCSSUnit_Initial != before.GetUnit()) {
6555 nsCSSValue after;
6556 if (ParseSingleValueProperty(after, eCSSProperty_cue_after)) {
6557 if (ExpectEndProperty()) {
6558 AppendValue(eCSSProperty_cue_before, before);
6559 AppendValue(eCSSProperty_cue_after, after);
6560 return PR_TRUE;
6562 return PR_FALSE;
6565 if (ExpectEndProperty()) {
6566 AppendValue(eCSSProperty_cue_before, before);
6567 AppendValue(eCSSProperty_cue_after, before);
6568 return PR_TRUE;
6571 return PR_FALSE;
6574 PRBool
6575 CSSParserImpl::ParseCursor()
6577 nsCSSValueList *list = nsnull;
6578 for (nsCSSValueList **curp = &list, *cur; ; curp = &cur->mNext) {
6579 cur = *curp = new nsCSSValueList();
6580 if (!cur) {
6581 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6582 break;
6584 if (!ParseVariant(cur->mValue,
6585 (cur == list) ? VARIANT_AHUK : VARIANT_AUK,
6586 nsCSSProps::kCursorKTable)) {
6587 break;
6589 if (cur->mValue.GetUnit() != eCSSUnit_URL) {
6590 if (!ExpectEndProperty()) {
6591 break;
6593 // Only success case here, since having the failure case at the
6594 // end allows more sharing of code.
6595 mTempData.SetPropertyBit(eCSSProperty_cursor);
6596 mTempData.mUserInterface.mCursor = list;
6597 return PR_TRUE;
6599 // We have a URL, so make a value array with three values.
6600 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
6601 if (!val) {
6602 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6603 break;
6605 val->Item(0) = cur->mValue;
6606 cur->mValue.SetArrayValue(val, eCSSUnit_Array);
6608 // Parse optional x and y position of cursor hotspot (css3-ui).
6609 if (ParseVariant(val->Item(1), VARIANT_NUMBER, nsnull)) {
6610 // If we have one number, we must have two.
6611 if (!ParseVariant(val->Item(2), VARIANT_NUMBER, nsnull)) {
6612 break;
6616 if (!ExpectSymbol(',', PR_TRUE)) {
6617 break;
6620 // Have failure case at the end so we can |break| to get to it.
6621 delete list;
6622 return PR_FALSE;
6626 PRBool
6627 CSSParserImpl::ParseFont()
6629 static const nsCSSProperty fontIDs[] = {
6630 eCSSProperty_font_style,
6631 eCSSProperty_font_variant,
6632 eCSSProperty_font_weight
6635 nsCSSValue family;
6636 if (ParseVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) {
6637 if (ExpectEndProperty()) {
6638 if (eCSSUnit_Inherit == family.GetUnit() ||
6639 eCSSUnit_Initial == family.GetUnit()) {
6640 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
6641 AppendValue(eCSSProperty_font_family, family);
6642 AppendValue(eCSSProperty_font_style, family);
6643 AppendValue(eCSSProperty_font_variant, family);
6644 AppendValue(eCSSProperty_font_weight, family);
6645 AppendValue(eCSSProperty_font_size, family);
6646 AppendValue(eCSSProperty_line_height, family);
6647 AppendValue(eCSSProperty_font_stretch, family);
6648 AppendValue(eCSSProperty_font_size_adjust, family);
6650 else {
6651 AppendValue(eCSSProperty__x_system_font, family);
6652 nsCSSValue systemFont(eCSSUnit_System_Font);
6653 AppendValue(eCSSProperty_font_family, systemFont);
6654 AppendValue(eCSSProperty_font_style, systemFont);
6655 AppendValue(eCSSProperty_font_variant, systemFont);
6656 AppendValue(eCSSProperty_font_weight, systemFont);
6657 AppendValue(eCSSProperty_font_size, systemFont);
6658 AppendValue(eCSSProperty_line_height, systemFont);
6659 AppendValue(eCSSProperty_font_stretch, systemFont);
6660 AppendValue(eCSSProperty_font_size_adjust, systemFont);
6662 return PR_TRUE;
6664 return PR_FALSE;
6667 // Get optional font-style, font-variant and font-weight (in any order)
6668 const PRInt32 numProps = 3;
6669 nsCSSValue values[numProps];
6670 PRInt32 found = ParseChoice(values, fontIDs, numProps);
6671 if ((found < 0) || (eCSSUnit_Inherit == values[0].GetUnit()) ||
6672 (eCSSUnit_Initial == values[0].GetUnit())) { // illegal data
6673 return PR_FALSE;
6675 if ((found & 1) == 0) {
6676 // Provide default font-style
6677 values[0].SetNormalValue();
6679 if ((found & 2) == 0) {
6680 // Provide default font-variant
6681 values[1].SetNormalValue();
6683 if ((found & 4) == 0) {
6684 // Provide default font-weight
6685 values[2].SetNormalValue();
6688 // Get mandatory font-size
6689 nsCSSValue size;
6690 if (! ParseVariant(size, VARIANT_KEYWORD | VARIANT_LP, nsCSSProps::kFontSizeKTable)) {
6691 return PR_FALSE;
6694 // Get optional "/" line-height
6695 nsCSSValue lineHeight;
6696 if (ExpectSymbol('/', PR_TRUE)) {
6697 if (! ParsePositiveVariant(lineHeight,
6698 VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL,
6699 nsnull)) {
6700 return PR_FALSE;
6703 else {
6704 lineHeight.SetNormalValue();
6707 // Get final mandatory font-family
6708 nsAutoParseCompoundProperty compound(this);
6709 if (ParseFamily(family)) {
6710 if ((eCSSUnit_Inherit != family.GetUnit()) && (eCSSUnit_Initial != family.GetUnit()) &&
6711 ExpectEndProperty()) {
6712 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
6713 AppendValue(eCSSProperty_font_family, family);
6714 AppendValue(eCSSProperty_font_style, values[0]);
6715 AppendValue(eCSSProperty_font_variant, values[1]);
6716 AppendValue(eCSSProperty_font_weight, values[2]);
6717 AppendValue(eCSSProperty_font_size, size);
6718 AppendValue(eCSSProperty_line_height, lineHeight);
6719 AppendValue(eCSSProperty_font_stretch, nsCSSValue(eCSSUnit_Normal));
6720 AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
6721 return PR_TRUE;
6724 return PR_FALSE;
6727 PRBool
6728 CSSParserImpl::ParseFontWeight(nsCSSValue& aValue)
6730 if (ParseVariant(aValue, VARIANT_HMKI | VARIANT_SYSFONT, nsCSSProps::kFontWeightKTable)) {
6731 if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
6732 PRInt32 intValue = aValue.GetIntValue();
6733 if ((100 <= intValue) &&
6734 (intValue <= 900) &&
6735 (0 == (intValue % 100))) {
6736 return PR_TRUE;
6737 } else {
6738 UngetToken();
6739 return PR_FALSE;
6742 return PR_TRUE;
6744 return PR_FALSE;
6747 PRBool
6748 CSSParserImpl::ParseOneFamily(nsAString& aFamily)
6750 if (!GetToken(PR_TRUE))
6751 return PR_FALSE;
6753 nsCSSToken* tk = &mToken;
6755 if (eCSSToken_Ident == tk->mType) {
6756 aFamily.Append(tk->mIdent);
6757 for (;;) {
6758 if (!GetToken(PR_FALSE))
6759 break;
6761 if (eCSSToken_Ident == tk->mType) {
6762 aFamily.Append(tk->mIdent);
6763 } else if (eCSSToken_WhiteSpace == tk->mType) {
6764 // Lookahead one token and drop whitespace if we are ending the
6765 // font name.
6766 if (!GetToken(PR_TRUE))
6767 break;
6769 UngetToken();
6770 if (eCSSToken_Ident == tk->mType)
6771 aFamily.Append(PRUnichar(' '));
6772 else
6773 break;
6774 } else {
6775 UngetToken();
6776 break;
6779 return PR_TRUE;
6781 } else if (eCSSToken_String == tk->mType) {
6782 aFamily.Append(tk->mSymbol); // replace the quotes
6783 aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
6784 aFamily.Append(tk->mSymbol);
6785 return PR_TRUE;
6787 } else {
6788 UngetToken();
6789 return PR_FALSE;
6793 ///////////////////////////////////////////////////////
6794 // -moz-transform Parsing Implementation
6796 /* Reads a function list of arguments. Do not call this function
6797 * directly; it's mean to be caled from ParseFunction.
6799 PRBool
6800 CSSParserImpl::ParseFunctionInternals(const PRInt32 aVariantMask[],
6801 PRUint16 aMinElems,
6802 PRUint16 aMaxElems,
6803 nsTArray<nsCSSValue> &aOutput)
6805 for (PRUint16 index = 0; index < aMaxElems; ++index) {
6806 nsCSSValue newValue;
6807 if (!ParseVariant(newValue, aVariantMask[index], nsnull))
6808 return PR_FALSE;
6810 if (!aOutput.AppendElement(newValue)) {
6811 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6812 return PR_FALSE;
6815 // See whether to continue or whether to look for end of function.
6816 if (!ExpectSymbol(',', PR_TRUE)) {
6817 // We need to read the closing parenthesis, and also must take care
6818 // that we haven't read too few symbols.
6819 return ExpectSymbol(')', PR_TRUE) && (index + 1) >= aMinElems;
6823 // If we're here, we finished looping without hitting the end, so we read too
6824 // many elements.
6825 return PR_FALSE;
6828 /* Parses a function [ input of the form (a [, b]*) ] and stores it
6829 * as an nsCSSValue that holds a function of the form
6830 * function-name arg1 arg2 ... argN
6832 * On error, the return value is PR_FALSE.
6834 * @param aFunction The name of the function that we're reading.
6835 * @param aAllowedTypes An array of values corresponding to the legal
6836 * types for each element in the function. The zeroth element in the
6837 * array corresponds to the first function parameter, etc. The length
6838 * of this array _must_ be greater than or equal to aMaxElems or the
6839 * behavior is undefined.
6840 * @param aMinElems Minimum number of elements to read. Reading fewer than
6841 * this many elements will result in the function failing.
6842 * @param aMaxElems Maximum number of elements to read. Reading more than
6843 * this many elements will result in the function failing.
6844 * @param aValue (out) The value that was parsed.
6846 PRBool
6847 CSSParserImpl::ParseFunction(const nsString &aFunction,
6848 const PRInt32 aAllowedTypes[],
6849 PRUint16 aMinElems, PRUint16 aMaxElems,
6850 nsCSSValue &aValue)
6852 typedef nsTArray<nsCSSValue>::size_type arrlen_t;
6854 /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
6855 * elements stored in the the nsCSSValue::Array.
6857 static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
6859 /* Make a copy of the function name, since the reference is _probably_ to
6860 * mToken.mIdent, which is going to get overwritten during the course of this
6861 * function.
6863 nsString functionName(aFunction);
6865 /* First things first... read the parenthesis. If it fails, abort. */
6866 if (!ExpectSymbol('(', PR_TRUE))
6867 return PR_FALSE;
6869 /* Read in a list of values as an nsTArray, failing if we can't or if
6870 * it's out of bounds.
6872 nsTArray<nsCSSValue> foundValues;
6873 if (!ParseFunctionInternals(aAllowedTypes, aMinElems, aMaxElems,
6874 foundValues))
6875 return PR_FALSE;
6877 /* Now, convert this nsTArray into an nsCSSValue::Array object.
6878 * We'll need N + 1 spots, one for the function name and the rest for the
6879 * arguments. In case the user has given us more than 2^16 - 2 arguments,
6880 * we'll truncate them at 2^16 - 2 arguments.
6882 PRUint16 numElements = (foundValues.Length() <= MAX_ALLOWED_ELEMS ?
6883 foundValues.Length() + 1 : MAX_ALLOWED_ELEMS);
6884 nsRefPtr<nsCSSValue::Array> convertedArray =
6885 nsCSSValue::Array::Create(numElements);
6886 if (!convertedArray) {
6887 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6888 return PR_FALSE;
6891 /* Copy things over. */
6892 convertedArray->Item(0).SetStringValue(functionName, eCSSUnit_String);
6893 for (PRUint16 index = 0; index + 1 < numElements; ++index)
6894 convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
6896 /* Fill in the outparam value with the array. */
6897 aValue.SetArrayValue(convertedArray, eCSSUnit_Function);
6899 /* Return it! */
6900 return PR_TRUE;
6904 * Given a token, determines the minimum and maximum number of function
6905 * parameters to read, along with the mask that should be used to read
6906 * those function parameters. If the token isn't a transform function,
6907 * returns an error.
6909 * @param aToken The token identifying the function.
6910 * @param aMinElems [out] The minimum number of elements to read.
6911 * @param aMaxElems [out] The maximum number of elements to read
6912 * @param aVariantMask [out] The variant mask to use during parsing
6913 * @return Whether the information was loaded successfully.
6915 static PRBool GetFunctionParseInformation(nsCSSKeyword aToken,
6916 PRUint16 &aMinElems,
6917 PRUint16 &aMaxElems,
6918 const PRInt32 *& aVariantMask)
6920 /* These types represent the common variant masks that will be used to
6921 * parse out the individual functions. The order in the enumeration
6922 * must match the order in which the masks are declared.
6924 enum { eLengthPercent,
6925 eTwoLengthPercents,
6926 eAngle,
6927 eTwoAngles,
6928 eNumber,
6929 eTwoNumbers,
6930 eMatrix,
6931 eNumVariantMasks };
6932 static const PRInt32 kMaxElemsPerFunction = 6;
6933 static const PRInt32 kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
6934 {VARIANT_LENGTH | VARIANT_PERCENT},
6935 {VARIANT_LENGTH | VARIANT_PERCENT, VARIANT_LENGTH | VARIANT_PERCENT},
6936 {VARIANT_ANGLE},
6937 {VARIANT_ANGLE, VARIANT_ANGLE},
6938 {VARIANT_NUMBER},
6939 {VARIANT_NUMBER, VARIANT_NUMBER},
6940 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
6941 VARIANT_LENGTH | VARIANT_PERCENT, VARIANT_LENGTH | VARIANT_PERCENT}};
6943 #ifdef DEBUG
6944 static const PRUint8 kVariantMaskLengths[eNumVariantMasks] =
6945 {1, 2, 1, 2, 1, 2, 6};
6946 #endif
6948 PRInt32 variantIndex = eNumVariantMasks;
6950 switch (aToken) {
6951 case eCSSKeyword_translatex:
6952 /* Exactly one length or percent. */
6953 variantIndex = eLengthPercent;
6954 aMinElems = 1U;
6955 aMaxElems = 1U;
6956 break;
6957 case eCSSKeyword_translatey:
6958 /* Exactly one length or percent. */
6959 variantIndex = eLengthPercent;
6960 aMinElems = 1U;
6961 aMaxElems = 1U;
6962 break;
6963 case eCSSKeyword_scalex:
6964 /* Exactly one scale factor. */
6965 variantIndex = eNumber;
6966 aMinElems = 1U;
6967 aMaxElems = 1U;
6968 break;
6969 case eCSSKeyword_scaley:
6970 /* Exactly one scale factor. */
6971 variantIndex = eNumber;
6972 aMinElems = 1U;
6973 aMaxElems = 1U;
6974 break;
6975 case eCSSKeyword_rotate:
6976 /* Exactly one angle. */
6977 variantIndex = eAngle;
6978 aMinElems = 1U;
6979 aMaxElems = 1U;
6980 break;
6981 case eCSSKeyword_translate:
6982 /* One or two lengths or percents. */
6983 variantIndex = eTwoLengthPercents;
6984 aMinElems = 1U;
6985 aMaxElems = 2U;
6986 break;
6987 case eCSSKeyword_skew:
6988 /* Exactly one or two angles. */
6989 variantIndex = eTwoAngles;
6990 aMinElems = 1U;
6991 aMaxElems = 2U;
6992 break;
6993 case eCSSKeyword_scale:
6994 /* One or two scale factors. */
6995 variantIndex = eTwoNumbers;
6996 aMinElems = 1U;
6997 aMaxElems = 2U;
6998 break;
6999 case eCSSKeyword_skewx:
7000 /* Exactly one angle. */
7001 variantIndex = eAngle;
7002 aMinElems = 1U;
7003 aMaxElems = 1U;
7004 break;
7005 case eCSSKeyword_skewy:
7006 /* Exactly one angle. */
7007 variantIndex = eAngle;
7008 aMinElems = 1U;
7009 aMaxElems = 1U;
7010 break;
7011 case eCSSKeyword_matrix:
7012 /* Six values, which can be numbers, lengths, or percents. */
7013 variantIndex = eMatrix;
7014 aMinElems = 6U;
7015 aMaxElems = 6U;
7016 break;
7017 default:
7018 /* Oh dear, we didn't match. Report an error. */
7019 return PR_FALSE;
7022 NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
7023 NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
7024 NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
7025 NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
7026 NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
7027 #ifdef DEBUG
7028 NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
7029 "Invalid aMaxElems for this variant mask.");
7030 #endif
7032 // Convert the index into a mask.
7033 aVariantMask = kVariantMasks[variantIndex];
7035 return PR_TRUE;
7039 /* Reads a single transform function from the tokenizer stream, reporting an
7040 * error if something goes wrong.
7042 PRBool CSSParserImpl::ReadSingleTransform(nsCSSValueList **& aTail)
7044 typedef nsTArray<nsCSSValue>::size_type arrlen_t;
7046 if (!GetToken(PR_TRUE))
7047 return PR_FALSE;
7049 /* Check to make sure that we've read a function. */
7050 if (mToken.mType != eCSSToken_Function) {
7051 UngetToken();
7052 return PR_FALSE;
7055 /* Load up the variant mask information for ParseFunction. If we can't,
7056 * abort.
7058 const PRInt32* variantMask;
7059 PRUint16 minElems, maxElems;
7060 if (!GetFunctionParseInformation(nsCSSKeywords::LookupKeyword(mToken.mIdent),
7061 minElems, maxElems, variantMask))
7062 return PR_FALSE;
7064 /* Create a cell to populate, fail if we're out of memory. */
7065 nsAutoPtr<nsCSSValue> newCell(new nsCSSValue);
7066 if (!newCell) {
7067 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7068 return PR_FALSE;
7071 /* Try reading things in, failing if we can't */
7072 if (!ParseFunction(mToken.mIdent, variantMask, minElems, maxElems, *newCell))
7073 return PR_FALSE;
7075 /* Wrap up our result in an nsCSSValueList cell. */
7076 nsAutoPtr<nsCSSValueList> toAppend(new nsCSSValueList);
7077 if (!toAppend)
7078 return PR_FALSE;
7080 toAppend->mValue = *newCell;
7082 /* Chain the element to the end of the transform list, then update the
7083 * list.
7085 *aTail = toAppend.forget();
7086 aTail = &(*aTail)->mNext;
7088 /* It worked! Return true. */
7089 return PR_TRUE;
7092 /* Parses a -moz-transform property list by continuously reading in properties
7093 * and constructing a matrix from it.
7095 PRBool CSSParserImpl::ParseMozTransform()
7097 mTempData.mDisplay.mTransform = nsnull;
7099 /* First, check to see if this is some sort of keyword - none, inherit,
7100 * or initial.
7102 nsCSSValue keywordValue;
7103 if (ParseVariant(keywordValue, VARIANT_INHERIT | VARIANT_NONE, nsnull)) {
7104 /* Looks like it is. Make a new value list and fill it in, failing if
7105 * we can't.
7107 mTempData.mDisplay.mTransform = new nsCSSValueList;
7108 if (!mTempData.mDisplay.mTransform) {
7109 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7110 return PR_FALSE;
7113 /* Inform the parser that everything worked. */
7114 mTempData.mDisplay.mTransform->mValue = keywordValue;
7115 mTempData.SetPropertyBit(eCSSProperty__moz_transform);
7116 return PR_TRUE;
7119 /* We will read a nonempty list of transforms. Thus we'll read in a
7120 * transform, then continuously read transforms until we're no longer
7121 * able to do so.
7123 nsCSSValueList *transformList = nsnull;
7124 nsCSSValueList **tail = &transformList;
7125 do {
7126 /* Try reading a transform. If we fail to do so, abort. */
7127 if (!ReadSingleTransform(tail)) {
7128 delete transformList;
7129 return PR_FALSE;
7132 while (!CheckEndProperty());
7134 /* Confirm that this is the end of the property and set error code
7135 * appropriately otherwise.
7137 if (!ExpectEndProperty()) {
7138 delete transformList;
7139 return PR_FALSE;
7142 /* Validate our data. */
7143 NS_ASSERTION(transformList, "Didn't read any transforms!");
7145 mTempData.SetPropertyBit(eCSSProperty__moz_transform);
7146 mTempData.mDisplay.mTransform = transformList;
7148 return PR_TRUE;
7151 PRBool CSSParserImpl::ParseMozTransformOrigin()
7153 /* Read in a box position, fail if we can't. */
7154 if (!ParseBoxPosition(mTempData.mDisplay.mTransformOrigin))
7155 return PR_FALSE;
7157 /* Set the property bit and return. */
7158 mTempData.SetPropertyBit(eCSSProperty__moz_transform_origin);
7159 return PR_TRUE;
7162 PRBool
7163 CSSParserImpl::ParseFamily(nsCSSValue& aValue)
7165 if (!GetToken(PR_TRUE))
7166 return PR_FALSE;
7168 if (eCSSToken_Ident == mToken.mType) {
7169 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
7170 if (keyword == eCSSKeyword_inherit) {
7171 aValue.SetInheritValue();
7172 return PR_TRUE;
7174 if (keyword == eCSSKeyword__moz_initial) {
7175 aValue.SetInitialValue();
7176 return PR_TRUE;
7178 if (keyword == eCSSKeyword__moz_use_system_font &&
7179 !IsParsingCompoundProperty()) {
7180 aValue.SetSystemFontValue();
7181 return PR_TRUE;
7185 UngetToken();
7187 nsAutoString family;
7188 for (;;) {
7189 if (!ParseOneFamily(family))
7190 return PR_FALSE;
7192 if (!ExpectSymbol(',', PR_TRUE))
7193 break;
7195 family.Append(PRUnichar(','));
7198 if (family.IsEmpty()) {
7199 return PR_FALSE;
7201 aValue.SetStringValue(family, eCSSUnit_String);
7202 return PR_TRUE;
7205 // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
7206 // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
7207 // local-src: 'local(' ( string | ident ) ')'
7209 PRBool
7210 CSSParserImpl::ParseFontSrc(nsCSSValue& aValue)
7212 // could we maybe turn nsCSSValue::Array into nsTArray<nsCSSValue>?
7213 nsTArray<nsCSSValue> values;
7214 nsCSSValue cur;
7215 for (;;) {
7216 if (!GetToken(PR_TRUE))
7217 break;
7219 if (mToken.mType == eCSSToken_Function &&
7220 mToken.mIdent.LowerCaseEqualsLiteral("url")) {
7221 if (!ParseURL(cur))
7222 return PR_FALSE;
7223 values.AppendElement(cur);
7224 if (!ParseFontSrcFormat(values))
7225 return PR_FALSE;
7227 } else if (mToken.mType == eCSSToken_Function &&
7228 mToken.mIdent.LowerCaseEqualsLiteral("local")) {
7229 // css3-fonts does not specify a formal grammar for local().
7230 // The text permits both unquoted identifiers and quoted
7231 // strings. We resolve this ambiguity in the spec by
7232 // assuming that the appropriate production is a single
7233 // <family-name>, possibly surrounded by whitespace.
7235 nsAutoString family;
7236 if (!ExpectSymbol('(', PR_FALSE))
7237 return PR_FALSE;
7238 if (!ParseOneFamily(family))
7239 return PR_FALSE;
7240 if (!ExpectSymbol(')', PR_TRUE))
7241 return PR_FALSE;
7243 // the style parameters to the nsFont constructor are ignored,
7244 // because it's only being used to call EnumerateFamilies
7245 nsFont font(family, 0, 0, 0, 0, 0);
7246 ExtractFirstFamilyData dat;
7248 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
7249 if (!dat.mGood)
7250 return PR_FALSE;
7252 cur.SetStringValue(dat.mFamilyName, eCSSUnit_Local_Font);
7253 values.AppendElement(cur);
7254 } else {
7255 return PR_FALSE;
7258 if (!ExpectSymbol(',', PR_TRUE))
7259 break;
7262 nsRefPtr<nsCSSValue::Array> srcVals
7263 = nsCSSValue::Array::Create(values.Length());
7264 if (!srcVals)
7265 return PR_FALSE;
7267 PRUint32 i;
7268 for (i = 0; i < values.Length(); i++)
7269 srcVals->Item(i) = values[i];
7270 aValue.SetArrayValue(srcVals, eCSSUnit_Array);
7271 return PR_TRUE;
7274 PRBool
7275 CSSParserImpl::ParseFontSrcFormat(nsTArray<nsCSSValue> & values)
7277 if (!GetToken(PR_TRUE))
7278 return PR_TRUE; // EOF harmless here
7279 if (mToken.mType != eCSSToken_Function ||
7280 !mToken.mIdent.LowerCaseEqualsLiteral("format")) {
7281 UngetToken();
7282 return PR_TRUE;
7284 if (!ExpectSymbol('(', PR_FALSE))
7285 return PR_FALSE;
7287 do {
7288 if (!GetToken(PR_TRUE))
7289 return PR_FALSE;
7291 if (mToken.mType != eCSSToken_String)
7292 return PR_FALSE;
7294 nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format);
7295 values.AppendElement(cur);
7296 } while (ExpectSymbol(',', PR_TRUE));
7298 return ExpectSymbol(')', PR_TRUE);
7301 // font-ranges: urange ( ',' urange )*
7302 PRBool
7303 CSSParserImpl::ParseFontRanges(nsCSSValue& aValue)
7305 // not currently implemented (bug 443976)
7306 return PR_FALSE;
7309 PRBool
7310 CSSParserImpl::ParseListStyle()
7312 const PRInt32 numProps = 3;
7313 static const nsCSSProperty listStyleIDs[] = {
7314 eCSSProperty_list_style_type,
7315 eCSSProperty_list_style_position,
7316 eCSSProperty_list_style_image
7319 nsCSSValue values[numProps];
7320 PRInt32 index;
7321 PRInt32 found = ParseChoice(values, listStyleIDs, numProps);
7322 if ((found < 1) || (PR_FALSE == ExpectEndProperty())) {
7323 return PR_FALSE;
7326 // Provide default values
7327 if ((found & 1) == 0) {
7328 values[0].SetIntValue(NS_STYLE_LIST_STYLE_DISC, eCSSUnit_Enumerated);
7330 if ((found & 2) == 0) {
7331 values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE, eCSSUnit_Enumerated);
7333 if ((found & 4) == 0) {
7334 values[2].SetNoneValue();
7337 for (index = 0; index < numProps; index++) {
7338 AppendValue(listStyleIDs[index], values[index]);
7340 return PR_TRUE;
7343 PRBool
7344 CSSParserImpl::ParseMargin()
7346 static const nsCSSProperty kMarginSideIDs[] = {
7347 eCSSProperty_margin_top,
7348 eCSSProperty_margin_right_value,
7349 eCSSProperty_margin_bottom,
7350 eCSSProperty_margin_left_value
7352 static const nsCSSProperty kMarginSources[] = {
7353 eCSSProperty_margin_left_ltr_source,
7354 eCSSProperty_margin_left_rtl_source,
7355 eCSSProperty_margin_right_ltr_source,
7356 eCSSProperty_margin_right_rtl_source,
7357 eCSSProperty_UNKNOWN
7360 // do this now, in case 4 values weren't specified
7361 InitBoxPropsAsPhysical(kMarginSources);
7362 return ParseBoxProperties(mTempData.mMargin.mMargin,
7363 kMarginSideIDs);
7366 PRBool
7367 CSSParserImpl::ParseMarks(nsCSSValue& aValue)
7369 if (ParseVariant(aValue, VARIANT_HOK, nsCSSProps::kPageMarksKTable)) {
7370 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
7371 if (PR_FALSE == CheckEndProperty()) {
7372 nsCSSValue second;
7373 if (ParseEnum(second, nsCSSProps::kPageMarksKTable)) {
7374 aValue.SetIntValue(aValue.GetIntValue() | second.GetIntValue(), eCSSUnit_Enumerated);
7375 return PR_TRUE;
7377 return PR_FALSE;
7380 return PR_TRUE;
7382 return PR_FALSE;
7385 PRBool
7386 CSSParserImpl::ParseOutline()
7388 const PRInt32 numProps = 3;
7389 static const nsCSSProperty kOutlineIDs[] = {
7390 eCSSProperty_outline_color,
7391 eCSSProperty_outline_style,
7392 eCSSProperty_outline_width
7395 nsCSSValue values[numProps];
7396 PRInt32 found = ParseChoice(values, kOutlineIDs, numProps);
7397 if ((found < 1) || (PR_FALSE == ExpectEndProperty())) {
7398 return PR_FALSE;
7401 // Provide default values
7402 if ((found & 1) == 0) {
7403 #ifdef GFX_HAS_INVERT
7404 values[0].SetIntValue(NS_STYLE_COLOR_INVERT, eCSSUnit_Enumerated);
7405 #else
7406 values[0].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
7407 #endif
7409 if ((found & 2) == 0) {
7410 values[1].SetNoneValue();
7412 if ((found & 4) == 0) {
7413 values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
7416 PRInt32 index;
7417 for (index = 0; index < numProps; index++) {
7418 AppendValue(kOutlineIDs[index], values[index]);
7420 return PR_TRUE;
7423 PRBool
7424 CSSParserImpl::ParseOverflow()
7426 nsCSSValue overflow;
7427 if (!ParseVariant(overflow, VARIANT_AHK,
7428 nsCSSProps::kOverflowKTable) ||
7429 !ExpectEndProperty())
7430 return PR_FALSE;
7432 nsCSSValue overflowX(overflow);
7433 nsCSSValue overflowY(overflow);
7434 if (eCSSUnit_Enumerated == overflow.GetUnit())
7435 switch(overflow.GetIntValue()) {
7436 case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
7437 overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
7438 overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
7439 break;
7440 case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
7441 overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
7442 overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
7443 break;
7445 AppendValue(eCSSProperty_overflow_x, overflowX);
7446 AppendValue(eCSSProperty_overflow_y, overflowY);
7447 return PR_TRUE;
7450 PRBool
7451 CSSParserImpl::ParsePadding()
7453 static const nsCSSProperty kPaddingSideIDs[] = {
7454 eCSSProperty_padding_top,
7455 eCSSProperty_padding_right_value,
7456 eCSSProperty_padding_bottom,
7457 eCSSProperty_padding_left_value
7459 static const nsCSSProperty kPaddingSources[] = {
7460 eCSSProperty_padding_left_ltr_source,
7461 eCSSProperty_padding_left_rtl_source,
7462 eCSSProperty_padding_right_ltr_source,
7463 eCSSProperty_padding_right_rtl_source,
7464 eCSSProperty_UNKNOWN
7467 // do this now, in case 4 values weren't specified
7468 InitBoxPropsAsPhysical(kPaddingSources);
7469 return ParseBoxProperties(mTempData.mMargin.mPadding,
7470 kPaddingSideIDs);
7473 PRBool
7474 CSSParserImpl::ParsePause()
7476 nsCSSValue before;
7477 if (ParseSingleValueProperty(before, eCSSProperty_pause_before)) {
7478 if (eCSSUnit_Inherit != before.GetUnit() && eCSSUnit_Initial != before.GetUnit()) {
7479 nsCSSValue after;
7480 if (ParseSingleValueProperty(after, eCSSProperty_pause_after)) {
7481 if (ExpectEndProperty()) {
7482 AppendValue(eCSSProperty_pause_before, before);
7483 AppendValue(eCSSProperty_pause_after, after);
7484 return PR_TRUE;
7486 return PR_FALSE;
7489 if (ExpectEndProperty()) {
7490 AppendValue(eCSSProperty_pause_before, before);
7491 AppendValue(eCSSProperty_pause_after, before);
7492 return PR_TRUE;
7495 return PR_FALSE;
7498 PRBool
7499 CSSParserImpl::ParseQuotes()
7501 nsCSSValue open;
7502 if (ParseVariant(open, VARIANT_HOS, nsnull)) {
7503 if (eCSSUnit_String == open.GetUnit()) {
7504 nsCSSValuePairList* quotesHead = new nsCSSValuePairList();
7505 nsCSSValuePairList* quotes = quotesHead;
7506 if (nsnull == quotes) {
7507 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7508 return PR_FALSE;
7510 quotes->mXValue = open;
7511 while (nsnull != quotes) {
7512 // get mandatory close
7513 if (ParseVariant(quotes->mYValue, VARIANT_STRING,
7514 nsnull)) {
7515 if (CheckEndProperty()) {
7516 mTempData.SetPropertyBit(eCSSProperty_quotes);
7517 mTempData.mContent.mQuotes = quotesHead;
7518 return PR_TRUE;
7520 // look for another open
7521 if (ParseVariant(open, VARIANT_STRING, nsnull)) {
7522 quotes->mNext = new nsCSSValuePairList();
7523 quotes = quotes->mNext;
7524 if (nsnull != quotes) {
7525 quotes->mXValue = open;
7526 continue;
7528 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7531 break;
7533 delete quotesHead;
7534 return PR_FALSE;
7536 if (ExpectEndProperty()) {
7537 nsCSSValuePairList* quotesHead = new nsCSSValuePairList();
7538 quotesHead->mXValue = open;
7539 mTempData.mContent.mQuotes = quotesHead;
7540 mTempData.SetPropertyBit(eCSSProperty_quotes);
7541 return PR_TRUE;
7544 return PR_FALSE;
7547 PRBool
7548 CSSParserImpl::ParseSize()
7550 nsCSSValue width;
7551 if (ParseVariant(width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) {
7552 if (width.IsLengthUnit()) {
7553 nsCSSValue height;
7554 if (ParseVariant(height, VARIANT_LENGTH, nsnull)) {
7555 if (ExpectEndProperty()) {
7556 mTempData.mPage.mSize.mXValue = width;
7557 mTempData.mPage.mSize.mYValue = height;
7558 mTempData.SetPropertyBit(eCSSProperty_size);
7559 return PR_TRUE;
7561 return PR_FALSE;
7564 if (ExpectEndProperty()) {
7565 mTempData.mPage.mSize.SetBothValuesTo(width);
7566 mTempData.SetPropertyBit(eCSSProperty_size);
7567 return PR_TRUE;
7570 return PR_FALSE;
7573 PRBool
7574 CSSParserImpl::ParseTextDecoration(nsCSSValue& aValue)
7576 if (ParseVariant(aValue, VARIANT_HOK, nsCSSProps::kTextDecorationKTable)) {
7577 if (eCSSUnit_Enumerated == aValue.GetUnit()) { // look for more keywords
7578 PRInt32 intValue = aValue.GetIntValue();
7579 nsCSSValue keyword;
7580 PRInt32 index;
7581 for (index = 0; index < 3; index++) {
7582 if (ParseEnum(keyword, nsCSSProps::kTextDecorationKTable)) {
7583 intValue |= keyword.GetIntValue();
7585 else {
7586 break;
7589 aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
7591 return PR_TRUE;
7593 return PR_FALSE;
7596 nsCSSValueList*
7597 CSSParserImpl::ParseCSSShadowList(PRBool aUsesSpread)
7599 nsAutoParseCompoundProperty compound(this);
7601 // Parses x, y, radius, color (in two possible orders)
7602 // This parses the input into a list. Either it contains just a "none" or
7603 // "inherit" value, or a list of arrays.
7604 // The resulting arrays will always contain the above order, with color and
7605 // radius as null values as needed
7606 enum {
7607 IndexX,
7608 IndexY,
7609 IndexRadius,
7610 IndexSpread,
7611 IndexColor
7614 nsCSSValueList *list = nsnull;
7615 for (nsCSSValueList **curp = &list, *cur; ; curp = &cur->mNext) {
7616 cur = *curp = new nsCSSValueList();
7617 if (!cur) {
7618 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7619 break;
7621 if (!ParseVariant(cur->mValue,
7622 (cur == list) ? VARIANT_HC | VARIANT_LENGTH | VARIANT_NONE
7623 : VARIANT_COLOR | VARIANT_LENGTH,
7624 nsnull)) {
7625 break;
7628 nsCSSUnit unit = cur->mValue.GetUnit();
7629 if (unit != eCSSUnit_None && unit != eCSSUnit_Inherit &&
7630 unit != eCSSUnit_Initial) {
7631 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(5);
7632 if (!val) {
7633 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7634 break;
7636 PRBool haveColor = PR_FALSE;
7637 if (cur->mValue.IsLengthUnit()) {
7638 val->Item(IndexX) = cur->mValue;
7639 } else {
7640 // Must be a color (as string or color value)
7641 NS_ASSERTION(unit == eCSSUnit_String || unit == eCSSUnit_Color ||
7642 unit == eCSSUnit_EnumColor,
7643 "Must be a color value (named color, numeric color, "
7644 "or system color)");
7645 haveColor = PR_TRUE;
7646 val->Item(IndexColor) = cur->mValue;
7648 // Parse the X coordinate
7649 if (!ParseVariant(val->Item(IndexX), VARIANT_LENGTH,
7650 nsnull)) {
7651 break;
7654 cur->mValue.SetArrayValue(val, eCSSUnit_Array);
7656 // Y coordinate; this one is not optional
7657 if (!ParseVariant(val->Item(IndexY), VARIANT_LENGTH, nsnull)) {
7658 break;
7661 // Optional radius. Ignore errors except if they pass a negative
7662 // value which we must reject. If we use ParsePositiveVariant we can't
7663 // tell the difference between an unspecified radius and a negative
7664 // radius, so that's why we don't use it.
7665 if (ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH, nsnull) &&
7666 val->Item(IndexRadius).GetFloatValue() < 0) {
7667 break;
7670 if (aUsesSpread) {
7671 // Optional spread (ignore errors)
7672 ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH,
7673 nsnull);
7676 if (!haveColor) {
7677 // Optional color (ignore errors)
7678 ParseVariant(val->Item(IndexColor), VARIANT_COLOR,
7679 nsnull);
7682 // Might be at a comma now
7683 if (ExpectSymbol(',', PR_TRUE)) {
7684 // Go to next value
7685 continue;
7689 if (!ExpectEndProperty()) {
7690 // If we don't have a comma to delimit the next value, we
7691 // must be at the end of the property. Otherwise we've hit
7692 // something else, which is an error.
7693 break;
7696 // Only success case here, since having the failure case at the
7697 // end allows more sharing of code.
7698 return list;
7700 // Have failure case at the end so we can |break| to get to it.
7701 delete list;
7702 return nsnull;
7705 PRBool
7706 CSSParserImpl::ParseTextShadow()
7708 nsCSSValueList* list = ParseCSSShadowList(PR_FALSE);
7709 if (!list)
7710 return PR_FALSE;
7712 mTempData.SetPropertyBit(eCSSProperty_text_shadow);
7713 mTempData.mText.mTextShadow = list;
7714 return PR_TRUE;
7717 PRBool
7718 CSSParserImpl::ParseBoxShadow()
7720 nsCSSValueList* list = ParseCSSShadowList(PR_TRUE);
7721 if (!list)
7722 return PR_FALSE;
7724 mTempData.SetPropertyBit(eCSSProperty_box_shadow);
7725 mTempData.mMargin.mBoxShadow = list;
7726 return PR_TRUE;
7729 PRBool
7730 CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix,
7731 PRInt32* aNameSpaceID)
7733 NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here");
7735 PRInt32 nameSpaceID = kNameSpaceID_Unknown;
7736 if (mNameSpaceMap) {
7737 // user-specified identifiers are case-sensitive (bug 416106)
7738 nsCOMPtr<nsIAtom> prefix = do_GetAtom(aPrefix);
7739 nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
7741 // else no declared namespaces
7743 if (kNameSpaceID_Unknown == nameSpaceID) { // unknown prefix, dump it
7744 const PRUnichar *params[] = {
7745 aPrefix.get()
7747 REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, params);
7748 if (mUnresolvablePrefixException)
7749 mScanner.SetLowLevelError(NS_ERROR_DOM_NAMESPACE_ERR);
7750 return PR_FALSE;
7753 *aNameSpaceID = nameSpaceID;
7754 return PR_TRUE;
7757 void
7758 CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector)
7760 if (mNameSpaceMap) {
7761 aSelector.SetNameSpace(mNameSpaceMap->FindNameSpaceID(nsnull));
7762 } else {
7763 aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
7767 #ifdef MOZ_SVG
7768 PRBool
7769 CSSParserImpl::ParsePaint(nsCSSValuePair* aResult,
7770 nsCSSProperty aPropID)
7772 if (!ParseVariant(aResult->mXValue,
7773 VARIANT_HC | VARIANT_NONE | VARIANT_URL,
7774 nsnull))
7775 return PR_FALSE;
7777 if (aResult->mXValue.GetUnit() == eCSSUnit_URL) {
7778 if (!ParseVariant(aResult->mYValue, VARIANT_COLOR | VARIANT_NONE,
7779 nsnull))
7780 aResult->mYValue.SetColorValue(NS_RGB(0, 0, 0));
7781 } else {
7782 aResult->mYValue = aResult->mXValue;
7785 if (!ExpectEndProperty())
7786 return PR_FALSE;
7788 mTempData.SetPropertyBit(aPropID);
7789 return PR_TRUE;
7792 PRBool
7793 CSSParserImpl::ParseDasharray()
7795 nsCSSValue value;
7796 if (ParseVariant(value, VARIANT_HLPN | VARIANT_NONE, nsnull)) {
7797 nsCSSValueList *listHead = new nsCSSValueList;
7798 nsCSSValueList *list = listHead;
7799 if (!list) {
7800 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7801 return PR_FALSE;
7804 list->mValue = value;
7806 for (;;) {
7807 if (CheckEndProperty()) {
7808 mTempData.SetPropertyBit(eCSSProperty_stroke_dasharray);
7809 mTempData.mSVG.mStrokeDasharray = listHead;
7810 return PR_TRUE;
7813 if (eCSSUnit_Inherit == value.GetUnit() ||
7814 eCSSUnit_Initial == value.GetUnit() ||
7815 eCSSUnit_None == value.GetUnit())
7816 break;
7818 if (!ExpectSymbol(',', PR_TRUE))
7819 break;
7821 if (!ParseVariant(value,
7822 VARIANT_LENGTH | VARIANT_PERCENT | VARIANT_NUMBER,
7823 nsnull))
7824 break;
7826 list->mNext = new nsCSSValueList;
7827 list = list->mNext;
7828 if (list)
7829 list->mValue = value;
7830 else {
7831 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7832 break;
7835 delete listHead;
7837 return PR_FALSE;
7840 PRBool
7841 CSSParserImpl::ParseMarker()
7843 nsCSSValue marker;
7844 if (ParseSingleValueProperty(marker, eCSSProperty_marker_end)) {
7845 if (ExpectEndProperty()) {
7846 AppendValue(eCSSProperty_marker_end, marker);
7847 AppendValue(eCSSProperty_marker_mid, marker);
7848 AppendValue(eCSSProperty_marker_start, marker);
7849 return PR_TRUE;
7852 return PR_FALSE;
7854 #endif