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