1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 et sw=2 tw=78: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * emk <VYV03354@nifty.ne.jp>
25 : * Daniel Glazman <glazman@netscape.com>
26 : * L. David Baron <dbaron@dbaron.org>
27 : * Boris Zbarsky <bzbarsky@mit.edu>
28 : * Mats Palmgren <mats.palmgren@bredband.net>
29 : * Christian Biesinger <cbiesinger@web.de>
30 : * Jeff Walden <jwalden+code@mit.edu>
31 : * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>, Collabora Ltd.
32 : * Siraj Razick <siraj.razick@collabora.co.uk>, Collabora Ltd.
33 : *
34 : * Alternatively, the contents of this file may be used under the terms of
35 : * either of the GNU General Public License Version 2 or later (the "GPL"),
36 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
37 : * in which case the provisions of the GPL or the LGPL are applicable instead
38 : * of those above. If you wish to allow use of your version of this file only
39 : * under the terms of either the GPL or the LGPL, and not to allow others to
40 : * use your version of this file under the terms of the MPL, indicate your
41 : * decision by deleting the provisions above and replace them with the notice
42 : * and other provisions required by the GPL or the LGPL. If you do not delete
43 : * the provisions above, a recipient may use your version of this file under
44 : * the terms of any one of the MPL, the GPL or the LGPL.
45 : *
46 : * ***** END LICENSE BLOCK ***** */
47 :
48 : /* parsing of CSS stylesheets, based on a token stream from the CSS scanner */
49 :
50 : #include "nsCSSParser.h"
51 : #include "nsCSSProps.h"
52 : #include "nsCSSKeywords.h"
53 : #include "nsCSSScanner.h"
54 : #include "mozilla/css/Loader.h"
55 : #include "mozilla/css/StyleRule.h"
56 : #include "mozilla/css/ImportRule.h"
57 : #include "nsCSSRules.h"
58 : #include "mozilla/css/NameSpaceRule.h"
59 : #include "nsTArray.h"
60 : #include "nsCSSStyleSheet.h"
61 : #include "mozilla/css/Declaration.h"
62 : #include "nsStyleConsts.h"
63 : #include "nsIURL.h"
64 : #include "nsNetUtil.h"
65 : #include "nsCOMPtr.h"
66 : #include "nsString.h"
67 : #include "nsReadableUtils.h"
68 : #include "nsUnicharUtils.h"
69 : #include "nsIAtom.h"
70 : #include "nsCOMArray.h"
71 : #include "nsColor.h"
72 : #include "nsCSSPseudoClasses.h"
73 : #include "nsCSSPseudoElements.h"
74 : #include "nsCSSAnonBoxes.h"
75 : #include "nsINameSpaceManager.h"
76 : #include "nsXMLNameSpaceMap.h"
77 : #include "nsThemeConstants.h"
78 : #include "nsContentErrors.h"
79 : #include "nsIMediaList.h"
80 : #include "mozilla/LookAndFeel.h"
81 : #include "nsStyleUtil.h"
82 : #include "nsIPrincipal.h"
83 : #include "prprf.h"
84 : #include "math.h"
85 : #include "nsContentUtils.h"
86 : #include "nsDOMError.h"
87 : #include "nsAutoPtr.h"
88 : #include "prlog.h"
89 : #include "CSSCalc.h"
90 : #include "nsMediaFeatures.h"
91 : #include "nsLayoutUtils.h"
92 :
93 : #include "mozilla/Util.h"
94 :
95 : using namespace mozilla;
96 :
97 : // Flags for ParseVariant method
98 : #define VARIANT_KEYWORD 0x000001 // K
99 : #define VARIANT_LENGTH 0x000002 // L
100 : #define VARIANT_PERCENT 0x000004 // P
101 : #define VARIANT_COLOR 0x000008 // C eCSSUnit_Color, eCSSUnit_Ident (e.g. "red")
102 : #define VARIANT_URL 0x000010 // U
103 : #define VARIANT_NUMBER 0x000020 // N
104 : #define VARIANT_INTEGER 0x000040 // I
105 : #define VARIANT_ANGLE 0x000080 // G
106 : #define VARIANT_FREQUENCY 0x000100 // F
107 : #define VARIANT_TIME 0x000200 // T
108 : #define VARIANT_STRING 0x000400 // S
109 : #define VARIANT_COUNTER 0x000800 //
110 : #define VARIANT_ATTR 0x001000 //
111 : #define VARIANT_IDENTIFIER 0x002000 // D
112 : #define VARIANT_IDENTIFIER_NO_INHERIT 0x004000 // like above, but excluding
113 : // 'inherit' and 'initial'
114 : #define VARIANT_AUTO 0x010000 // A
115 : #define VARIANT_INHERIT 0x020000 // H eCSSUnit_Initial, eCSSUnit_Inherit
116 : #define VARIANT_NONE 0x040000 // O
117 : #define VARIANT_NORMAL 0x080000 // M
118 : #define VARIANT_SYSFONT 0x100000 // eCSSUnit_System_Font
119 : #define VARIANT_GRADIENT 0x200000 // eCSSUnit_Gradient
120 : #define VARIANT_TIMING_FUNCTION 0x400000 // cubic-bezier() and steps()
121 : #define VARIANT_ALL 0x800000 //
122 : #define VARIANT_IMAGE_RECT 0x01000000 // eCSSUnit_Function
123 : // This is an extra bit that says that a VARIANT_ANGLE allows unitless zero:
124 : #define VARIANT_ZERO_ANGLE 0x02000000 // unitless zero for angles
125 : #define VARIANT_CALC 0x04000000 // eCSSUnit_Calc
126 : #define VARIANT_ELEMENT 0x08000000 // eCSSUnit_Element
127 : #define VARIANT_POSITIVE_LENGTH 0x10000000 // Only lengths greater than 0.0
128 :
129 : // Common combinations of variants
130 : #define VARIANT_AL (VARIANT_AUTO | VARIANT_LENGTH)
131 : #define VARIANT_LP (VARIANT_LENGTH | VARIANT_PERCENT)
132 : #define VARIANT_LN (VARIANT_LENGTH | VARIANT_NUMBER)
133 : #define VARIANT_AH (VARIANT_AUTO | VARIANT_INHERIT)
134 : #define VARIANT_AHLP (VARIANT_AH | VARIANT_LP)
135 : #define VARIANT_AHI (VARIANT_AH | VARIANT_INTEGER)
136 : #define VARIANT_AHK (VARIANT_AH | VARIANT_KEYWORD)
137 : #define VARIANT_AHKLP (VARIANT_AHLP | VARIANT_KEYWORD)
138 : #define VARIANT_AHL (VARIANT_AH | VARIANT_LENGTH)
139 : #define VARIANT_AHKL (VARIANT_AHK | VARIANT_LENGTH)
140 : #define VARIANT_HK (VARIANT_INHERIT | VARIANT_KEYWORD)
141 : #define VARIANT_HKF (VARIANT_HK | VARIANT_FREQUENCY)
142 : #define VARIANT_HKI (VARIANT_HK | VARIANT_INTEGER)
143 : #define VARIANT_HKL (VARIANT_HK | VARIANT_LENGTH)
144 : #define VARIANT_HKLP (VARIANT_HK | VARIANT_LP)
145 : #define VARIANT_HKLPO (VARIANT_HKLP | VARIANT_NONE)
146 : #define VARIANT_HL (VARIANT_INHERIT | VARIANT_LENGTH)
147 : #define VARIANT_HI (VARIANT_INHERIT | VARIANT_INTEGER)
148 : #define VARIANT_HLP (VARIANT_HL | VARIANT_PERCENT)
149 : #define VARIANT_HLPN (VARIANT_HLP | VARIANT_NUMBER)
150 : #define VARIANT_HLPO (VARIANT_HLP | VARIANT_NONE)
151 : #define VARIANT_HTP (VARIANT_INHERIT | VARIANT_TIME | VARIANT_PERCENT)
152 : #define VARIANT_HMK (VARIANT_HK | VARIANT_NORMAL)
153 : #define VARIANT_HC (VARIANT_INHERIT | VARIANT_COLOR)
154 : #define VARIANT_HCK (VARIANT_HK | VARIANT_COLOR)
155 : #define VARIANT_HUK (VARIANT_HK | VARIANT_URL)
156 : #define VARIANT_HUO (VARIANT_INHERIT | VARIANT_URL | VARIANT_NONE)
157 : #define VARIANT_AHUO (VARIANT_AUTO | VARIANT_HUO)
158 : #define VARIANT_HPN (VARIANT_INHERIT | VARIANT_PERCENT | VARIANT_NUMBER)
159 : #define VARIANT_PN (VARIANT_PERCENT | VARIANT_NUMBER)
160 : #define VARIANT_ALPN (VARIANT_AL | VARIANT_PN)
161 : #define VARIANT_HN (VARIANT_INHERIT | VARIANT_NUMBER)
162 : #define VARIANT_HON (VARIANT_HN | VARIANT_NONE)
163 : #define VARIANT_HOS (VARIANT_INHERIT | VARIANT_NONE | VARIANT_STRING)
164 : #define VARIANT_LPN (VARIANT_LP | VARIANT_NUMBER)
165 : #define VARIANT_UK (VARIANT_URL | VARIANT_KEYWORD)
166 : #define VARIANT_UO (VARIANT_URL | VARIANT_NONE)
167 : #define VARIANT_ANGLE_OR_ZERO (VARIANT_ANGLE | VARIANT_ZERO_ANGLE)
168 : #define VARIANT_LPCALC (VARIANT_LENGTH | VARIANT_CALC | VARIANT_PERCENT)
169 : #define VARIANT_LNCALC (VARIANT_LENGTH | VARIANT_CALC | VARIANT_NUMBER)
170 : #define VARIANT_LPNCALC (VARIANT_LNCALC | VARIANT_PERCENT)
171 : #define VARIANT_IMAGE (VARIANT_URL | VARIANT_NONE | VARIANT_GRADIENT | \
172 : VARIANT_IMAGE_RECT | VARIANT_ELEMENT)
173 :
174 : // This lives here because it depends on the above macros.
175 : const PRUint32
176 : nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = {
177 : #define CSS_PROP(name_, id_, method_, flags_, parsevariant_, kwtable_, \
178 : stylestruct_, stylestructoffset_, animtype_) \
179 : parsevariant_,
180 : #include "nsCSSPropList.h"
181 : #undef CSS_PROP
182 : };
183 :
184 : //----------------------------------------------------------------------
185 :
186 : namespace {
187 :
188 : // Rule processing function
189 : typedef void (* RuleAppendFunc) (css::Rule* aRule, void* aData);
190 : static void AppendRuleToArray(css::Rule* aRule, void* aArray);
191 : static void AppendRuleToSheet(css::Rule* aRule, void* aParser);
192 :
193 : // Your basic top-down recursive descent style parser
194 : // The exposed methods and members of this class are precisely those
195 : // needed by nsCSSParser, far below.
196 : class CSSParserImpl {
197 : public:
198 : CSSParserImpl();
199 : ~CSSParserImpl();
200 :
201 : nsresult SetStyleSheet(nsCSSStyleSheet* aSheet);
202 :
203 : nsresult SetQuirkMode(bool aQuirkMode);
204 :
205 : nsresult SetSVGMode(bool aSVGMode);
206 :
207 : nsresult SetChildLoader(mozilla::css::Loader* aChildLoader);
208 :
209 : // Clears everything set by the above Set*() functions.
210 : void Reset();
211 :
212 : nsresult ParseSheet(const nsAString& aInput,
213 : nsIURI* aSheetURI,
214 : nsIURI* aBaseURI,
215 : nsIPrincipal* aSheetPrincipal,
216 : PRUint32 aLineNumber,
217 : bool aAllowUnsafeRules);
218 :
219 : nsresult ParseStyleAttribute(const nsAString& aAttributeValue,
220 : nsIURI* aDocURL,
221 : nsIURI* aBaseURL,
222 : nsIPrincipal* aNodePrincipal,
223 : css::StyleRule** aResult);
224 :
225 : nsresult ParseDeclarations(const nsAString& aBuffer,
226 : nsIURI* aSheetURL,
227 : nsIURI* aBaseURL,
228 : nsIPrincipal* aSheetPrincipal,
229 : css::Declaration* aDeclaration,
230 : bool* aChanged);
231 :
232 : nsresult ParseRule(const nsAString& aRule,
233 : nsIURI* aSheetURL,
234 : nsIURI* aBaseURL,
235 : nsIPrincipal* aSheetPrincipal,
236 : nsCOMArray<css::Rule>& aResult);
237 :
238 : nsresult ParseProperty(const nsCSSProperty aPropID,
239 : const nsAString& aPropValue,
240 : nsIURI* aSheetURL,
241 : nsIURI* aBaseURL,
242 : nsIPrincipal* aSheetPrincipal,
243 : css::Declaration* aDeclaration,
244 : bool* aChanged,
245 : bool aIsImportant);
246 :
247 : nsresult ParseMediaList(const nsSubstring& aBuffer,
248 : nsIURI* aURL, // for error reporting
249 : PRUint32 aLineNumber, // for error reporting
250 : nsMediaList* aMediaList,
251 : bool aHTMLMode);
252 :
253 : nsresult ParseColorString(const nsSubstring& aBuffer,
254 : nsIURI* aURL, // for error reporting
255 : PRUint32 aLineNumber, // for error reporting
256 : nscolor* aColor);
257 :
258 : nsresult ParseSelectorString(const nsSubstring& aSelectorString,
259 : nsIURI* aURL, // for error reporting
260 : PRUint32 aLineNumber, // for error reporting
261 : nsCSSSelectorList **aSelectorList);
262 :
263 : already_AddRefed<nsCSSKeyframeRule>
264 : ParseKeyframeRule(const nsSubstring& aBuffer,
265 : nsIURI* aURL,
266 : PRUint32 aLineNumber);
267 :
268 : bool ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
269 : nsIURI* aURL, // for error reporting
270 : PRUint32 aLineNumber, // for error reporting
271 : InfallibleTArray<float>& aSelectorList);
272 :
273 : protected:
274 : class nsAutoParseCompoundProperty;
275 : friend class nsAutoParseCompoundProperty;
276 :
277 : void AppendRule(css::Rule* aRule);
278 : friend void AppendRuleToSheet(css::Rule*, void*); // calls AppendRule
279 :
280 : /**
281 : * This helper class automatically calls SetParsingCompoundProperty in its
282 : * constructor and takes care of resetting it to false in its destructor.
283 : */
284 : class nsAutoParseCompoundProperty {
285 : public:
286 0 : nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
287 : {
288 0 : NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
289 : "already parsing compound property");
290 0 : NS_ASSERTION(aParser, "Null parser?");
291 0 : aParser->SetParsingCompoundProperty(true);
292 0 : }
293 :
294 0 : ~nsAutoParseCompoundProperty()
295 : {
296 0 : mParser->SetParsingCompoundProperty(false);
297 0 : }
298 : private:
299 : CSSParserImpl* mParser;
300 : };
301 :
302 : // the caller must hold on to aString until parsing is done
303 : void InitScanner(const nsSubstring& aString, nsIURI* aSheetURI,
304 : PRUint32 aLineNumber, nsIURI* aBaseURI,
305 : nsIPrincipal* aSheetPrincipal);
306 : void ReleaseScanner(void);
307 0 : bool IsSVGMode() const {
308 0 : return mScanner.IsSVGMode();
309 : }
310 :
311 : bool GetToken(bool aSkipWS);
312 : void UngetToken();
313 :
314 : // get the part in paretheses of the url() function, which is really a
315 : // part of a token in the CSS grammar, but we're using a combination
316 : // of the parser and the scanner to do it to handle the backtracking
317 : // required by the error handling of the tokenization (since if we
318 : // fail to scan the full token, we should fall back to tokenizing as
319 : // FUNCTION ... ')').
320 : // Note that this function WILL WRITE TO aURL IN SOME FAILURE CASES.
321 : bool GetURLInParens(nsString& aURL);
322 :
323 110 : void AssertInitialState() {
324 110 : NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state");
325 110 : NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
326 110 : }
327 :
328 : bool ExpectSymbol(PRUnichar aSymbol, bool aSkipWS);
329 : bool ExpectEndProperty();
330 : bool CheckEndProperty();
331 : nsSubstring* NextIdent();
332 : void SkipUntil(PRUnichar aStopSymbol);
333 : void SkipUntilOneOf(const PRUnichar* aStopSymbolChars);
334 : void SkipRuleSet(bool aInsideBraces);
335 : bool SkipAtRule(bool aInsideBlock);
336 : bool SkipDeclaration(bool aCheckForBraces);
337 :
338 : void PushGroup(css::GroupRule* aRule);
339 : void PopGroup();
340 :
341 : bool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData,
342 : bool aInsideBraces = false);
343 : bool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData,
344 : bool aInAtRule);
345 : bool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData);
346 : bool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData);
347 : bool ParseURLOrString(nsString& aURL);
348 : bool GatherMedia(nsMediaList* aMedia,
349 : bool aInAtRule);
350 : bool ParseMediaQuery(bool aInAtRule, nsMediaQuery **aQuery,
351 : bool *aHitStop);
352 : bool ParseMediaQueryExpression(nsMediaQuery* aQuery);
353 : void ProcessImport(const nsString& aURLSpec,
354 : nsMediaList* aMedia,
355 : RuleAppendFunc aAppendFunc,
356 : void* aProcessData);
357 : bool ParseGroupRule(css::GroupRule* aRule, RuleAppendFunc aAppendFunc,
358 : void* aProcessData);
359 : bool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData);
360 : bool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData);
361 : bool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
362 : void ProcessNameSpace(const nsString& aPrefix,
363 : const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
364 : void* aProcessData);
365 :
366 : bool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
367 : bool ParseFontDescriptor(nsCSSFontFaceRule* aRule);
368 : bool ParseFontDescriptorValue(nsCSSFontDesc aDescID,
369 : nsCSSValue& aValue);
370 :
371 : bool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData);
372 : bool ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aProcessData);
373 : already_AddRefed<nsCSSKeyframeRule> ParseKeyframeRule();
374 : bool ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList);
375 :
376 : enum nsSelectorParsingStatus {
377 : // we have parsed a selector and we saw a token that cannot be
378 : // part of a selector:
379 : eSelectorParsingStatus_Done,
380 : // we should continue parsing the selector:
381 : eSelectorParsingStatus_Continue,
382 : // we saw an unexpected token or token value,
383 : // or we saw end-of-file with an unfinished selector:
384 : eSelectorParsingStatus_Error
385 : };
386 : nsSelectorParsingStatus ParseIDSelector(PRInt32& aDataMask,
387 : nsCSSSelector& aSelector);
388 :
389 : nsSelectorParsingStatus ParseClassSelector(PRInt32& aDataMask,
390 : nsCSSSelector& aSelector);
391 :
392 : // aPseudoElement and aPseudoElementArgs are the location where
393 : // pseudo-elements (as opposed to pseudo-classes) are stored;
394 : // pseudo-classes are stored on aSelector. aPseudoElement and
395 : // aPseudoElementArgs must be non-null iff !aIsNegated.
396 : nsSelectorParsingStatus ParsePseudoSelector(PRInt32& aDataMask,
397 : nsCSSSelector& aSelector,
398 : bool aIsNegated,
399 : nsIAtom** aPseudoElement,
400 : nsAtomList** aPseudoElementArgs,
401 : nsCSSPseudoElements::Type* aPseudoElementType);
402 :
403 : nsSelectorParsingStatus ParseAttributeSelector(PRInt32& aDataMask,
404 : nsCSSSelector& aSelector);
405 :
406 : nsSelectorParsingStatus ParseTypeOrUniversalSelector(PRInt32& aDataMask,
407 : nsCSSSelector& aSelector,
408 : bool aIsNegated);
409 :
410 : nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
411 : nsCSSPseudoClasses::Type aType);
412 :
413 : nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
414 : nsCSSPseudoClasses::Type aType);
415 :
416 : nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
417 : nsCSSPseudoClasses::Type aType);
418 :
419 : nsSelectorParsingStatus ParseNegatedSimpleSelector(PRInt32& aDataMask,
420 : nsCSSSelector& aSelector);
421 :
422 : // If aStopChar is non-zero, the selector list is done when we hit
423 : // aStopChar. Otherwise, it's done when we hit EOF.
424 : bool ParseSelectorList(nsCSSSelectorList*& aListHead,
425 : PRUnichar aStopChar);
426 : bool ParseSelectorGroup(nsCSSSelectorList*& aListHead);
427 : bool ParseSelector(nsCSSSelectorList* aList, PRUnichar aPrevCombinator);
428 :
429 : css::Declaration* ParseDeclarationBlock(bool aCheckForBraces);
430 : bool ParseDeclaration(css::Declaration* aDeclaration,
431 : bool aCheckForBraces,
432 : bool aMustCallValueAppended,
433 : bool* aChanged);
434 :
435 : bool ParseProperty(nsCSSProperty aPropID);
436 : bool ParsePropertyByFunction(nsCSSProperty aPropID);
437 : bool ParseSingleValueProperty(nsCSSValue& aValue,
438 : nsCSSProperty aPropID);
439 :
440 : enum PriorityParsingStatus {
441 : ePriority_None,
442 : ePriority_Important,
443 : ePriority_Error
444 : };
445 : PriorityParsingStatus ParsePriority();
446 :
447 : #ifdef MOZ_XUL
448 : bool ParseTreePseudoElement(nsAtomList **aPseudoElementArgs);
449 : #endif
450 :
451 : void InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties);
452 :
453 : // Property specific parsing routines
454 : bool ParseBackground();
455 :
456 : struct BackgroundParseState {
457 : nsCSSValue& mColor;
458 : nsCSSValueList* mImage;
459 : nsCSSValuePairList* mRepeat;
460 : nsCSSValueList* mAttachment;
461 : nsCSSValueList* mClip;
462 : nsCSSValueList* mOrigin;
463 : nsCSSValueList* mPosition;
464 : nsCSSValuePairList* mSize;
465 0 : BackgroundParseState(
466 : nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat,
467 : nsCSSValueList* aAttachment, nsCSSValueList* aClip,
468 : nsCSSValueList* aOrigin, nsCSSValueList* aPosition,
469 : nsCSSValuePairList* aSize) :
470 : mColor(aColor), mImage(aImage), mRepeat(aRepeat),
471 : mAttachment(aAttachment), mClip(aClip), mOrigin(aOrigin),
472 0 : mPosition(aPosition), mSize(aSize) {};
473 : };
474 :
475 : bool ParseBackgroundItem(BackgroundParseState& aState);
476 :
477 : bool ParseValueList(nsCSSProperty aPropID); // a single value prop-id
478 : bool ParseBackgroundRepeat();
479 : bool ParseBackgroundRepeatValues(nsCSSValuePair& aValue);
480 : bool ParseBackgroundPosition();
481 :
482 : // ParseBoxPositionValues parses the CSS 2.1 background-position syntax,
483 : // which is still used by some properties. See ParseBackgroundPositionValues
484 : // for the css3-background syntax.
485 : bool ParseBoxPositionValues(nsCSSValuePair& aOut, bool aAcceptsInherit,
486 : bool aAllowExplicitCenter = true); // deprecated
487 : bool ParseBackgroundPositionValues(nsCSSValue& aOut, bool aAcceptsInherit);
488 :
489 : bool ParseBackgroundSize();
490 : bool ParseBackgroundSizeValues(nsCSSValuePair& aOut);
491 : bool ParseBorderColor();
492 : bool ParseBorderColors(nsCSSProperty aProperty);
493 : void SetBorderImageInitialValues();
494 : bool ParseBorderImageRepeat(bool aAcceptsInherit);
495 : // If ParseBorderImageSlice returns false, aConsumedTokens indicates
496 : // whether or not any tokens were consumed (in other words, was the property
497 : // in error or just not present). If ParseBorderImageSlice returns true
498 : // aConsumedTokens is always true.
499 : bool ParseBorderImageSlice(bool aAcceptsInherit, bool* aConsumedTokens);
500 : bool ParseBorderImageWidth(bool aAcceptsInherit);
501 : bool ParseBorderImageOutset(bool aAcceptsInherit);
502 : bool ParseBorderImage();
503 : bool ParseBorderSpacing();
504 : bool ParseBorderSide(const nsCSSProperty aPropIDs[],
505 : bool aSetAllSides);
506 : bool ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
507 : PRInt32 aSourceType);
508 : bool ParseBorderStyle();
509 : bool ParseBorderWidth();
510 :
511 : bool ParseCalc(nsCSSValue &aValue, PRInt32 aVariantMask);
512 : bool ParseCalcAdditiveExpression(nsCSSValue& aValue,
513 : PRInt32& aVariantMask);
514 : bool ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
515 : PRInt32& aVariantMask,
516 : bool *aHadFinalWS);
517 : bool ParseCalcTerm(nsCSSValue& aValue, PRInt32& aVariantMask);
518 : bool RequireWhitespace();
519 :
520 : // for 'clip' and '-moz-image-region'
521 : bool ParseRect(nsCSSProperty aPropID);
522 : bool ParseColumns();
523 : bool ParseContent();
524 : bool ParseCounterData(nsCSSProperty aPropID);
525 : bool ParseCursor();
526 : bool ParseFont();
527 : bool ParseFontWeight(nsCSSValue& aValue);
528 : bool ParseOneFamily(nsAString& aValue);
529 : bool ParseFamily(nsCSSValue& aValue);
530 : bool ParseFontSrc(nsCSSValue& aValue);
531 : bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
532 : bool ParseFontRanges(nsCSSValue& aValue);
533 : bool ParseListStyle();
534 : bool ParseMargin();
535 : bool ParseMarks(nsCSSValue& aValue);
536 : bool ParseMozTransform();
537 : bool ParseOutline();
538 : bool ParseOverflow();
539 : bool ParsePadding();
540 : bool ParseQuotes();
541 : bool ParseSize();
542 : bool ParseTextDecoration();
543 : bool ParseTextDecorationLine(nsCSSValue& aValue);
544 : bool ParseTextOverflow(nsCSSValue& aValue);
545 : bool ParseUnicodeBidi(nsCSSValue& aValue);
546 :
547 : bool ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow);
548 : bool ParseShadowList(nsCSSProperty aProperty);
549 : bool ParseTransitionProperty();
550 : bool ParseTransitionTimingFunctionValues(nsCSSValue& aValue);
551 : bool ParseTransitionTimingFunctionValueComponent(float& aComponent,
552 : char aStop,
553 : bool aCheckRange);
554 : bool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue);
555 : enum ParseAnimationOrTransitionShorthandResult {
556 : eParseAnimationOrTransitionShorthand_Values,
557 : eParseAnimationOrTransitionShorthand_Inherit,
558 : eParseAnimationOrTransitionShorthand_Error
559 : };
560 : ParseAnimationOrTransitionShorthandResult
561 : ParseAnimationOrTransitionShorthand(const nsCSSProperty* aProperties,
562 : const nsCSSValue* aInitialValues,
563 : nsCSSValue* aValues,
564 : size_t aNumProperties);
565 : bool ParseTransition();
566 : bool ParseAnimation();
567 :
568 : bool ParsePaint(nsCSSProperty aPropID);
569 : bool ParseDasharray();
570 : bool ParseMarker();
571 :
572 : // Reused utility parsing routines
573 : void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue);
574 : bool ParseBoxProperties(const nsCSSProperty aPropIDs[]);
575 : bool ParseGroupedBoxProperty(PRInt32 aVariantMask,
576 : nsCSSValue& aValue);
577 : bool ParseDirectionalBoxProperty(nsCSSProperty aProperty,
578 : PRInt32 aSourceType);
579 : bool ParseBoxCornerRadius(const nsCSSProperty aPropID);
580 : bool ParseBoxCornerRadii(const nsCSSProperty aPropIDs[]);
581 : PRInt32 ParseChoice(nsCSSValue aValues[],
582 : const nsCSSProperty aPropIDs[], PRInt32 aNumIDs);
583 : bool ParseColor(nsCSSValue& aValue);
584 : bool ParseColorComponent(PRUint8& aComponent,
585 : PRInt32& aType, char aStop);
586 : // ParseHSLColor parses everything starting with the opening '('
587 : // up through and including the aStop char.
588 : bool ParseHSLColor(nscolor& aColor, char aStop);
589 : // ParseColorOpacity will enforce that the color ends with a ')'
590 : // after the opacity
591 : bool ParseColorOpacity(PRUint8& aOpacity);
592 : bool ParseEnum(nsCSSValue& aValue, const PRInt32 aKeywordTable[]);
593 : bool ParseVariant(nsCSSValue& aValue,
594 : PRInt32 aVariantMask,
595 : const PRInt32 aKeywordTable[]);
596 : bool ParseNonNegativeVariant(nsCSSValue& aValue,
597 : PRInt32 aVariantMask,
598 : const PRInt32 aKeywordTable[]);
599 : bool ParseOneOrLargerVariant(nsCSSValue& aValue,
600 : PRInt32 aVariantMask,
601 : const PRInt32 aKeywordTable[]);
602 : bool ParseCounter(nsCSSValue& aValue);
603 : bool ParseAttr(nsCSSValue& aValue);
604 : bool SetValueToURL(nsCSSValue& aValue, const nsString& aURL);
605 : bool TranslateDimension(nsCSSValue& aValue, PRInt32 aVariantMask,
606 : float aNumber, const nsString& aUnit);
607 : bool ParseImageRect(nsCSSValue& aImage);
608 : bool ParseElement(nsCSSValue& aValue);
609 : bool ParseColorStop(nsCSSValueGradient* aGradient);
610 : bool ParseGradient(nsCSSValue& aValue, bool aIsRadial,
611 : bool aIsRepeating);
612 :
613 0 : void SetParsingCompoundProperty(bool aBool) {
614 0 : mParsingCompoundProperty = aBool;
615 0 : }
616 0 : bool IsParsingCompoundProperty(void) const {
617 0 : return mParsingCompoundProperty;
618 : }
619 :
620 : /* Functions for -moz-transform Parsing */
621 : bool ParseSingleTransform(nsCSSValue& aValue, bool& aIs3D);
622 : bool ParseFunction(const nsString &aFunction, const PRInt32 aAllowedTypes[],
623 : PRUint16 aMinElems, PRUint16 aMaxElems,
624 : nsCSSValue &aValue);
625 : bool ParseFunctionInternals(const PRInt32 aVariantMask[],
626 : PRUint16 aMinElems,
627 : PRUint16 aMaxElems,
628 : InfallibleTArray<nsCSSValue>& aOutput);
629 :
630 : /* Functions for -moz-transform-origin/-moz-perspective-origin Parsing */
631 : bool ParseMozTransformOrigin(bool aPerspective);
632 :
633 : /* Find and return the namespace ID associated with aPrefix.
634 : If aPrefix has not been declared in an @namespace rule, returns
635 : kNameSpaceID_Unknown and sets mFoundUnresolvablePrefix to true. */
636 : PRInt32 GetNamespaceIdForPrefix(const nsString& aPrefix);
637 :
638 : /* Find the correct default namespace, and set it on aSelector. */
639 : void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
640 :
641 : // Current token. The value is valid after calling GetToken and invalidated
642 : // by UngetToken.
643 : nsCSSToken mToken;
644 :
645 : // Our scanner.
646 : nsCSSScanner mScanner;
647 :
648 : // The URI to be used as a base for relative URIs.
649 : nsCOMPtr<nsIURI> mBaseURI;
650 :
651 : // The URI to be used as an HTTP "Referer" and for error reporting.
652 : nsCOMPtr<nsIURI> mSheetURI;
653 :
654 : // The principal of the sheet involved
655 : nsCOMPtr<nsIPrincipal> mSheetPrincipal;
656 :
657 : // The sheet we're parsing into
658 : nsRefPtr<nsCSSStyleSheet> mSheet;
659 :
660 : // Used for @import rules
661 : mozilla::css::Loader* mChildLoader; // not ref counted, it owns us
662 :
663 : // Sheet section we're in. This is used to enforce correct ordering of the
664 : // various rule types (eg the fact that a @charset rule must come before
665 : // anything else). Note that there are checks of similar things in various
666 : // places in nsCSSStyleSheet.cpp (e.g in insertRule, RebuildChildList).
667 : enum nsCSSSection {
668 : eCSSSection_Charset,
669 : eCSSSection_Import,
670 : eCSSSection_NameSpace,
671 : eCSSSection_General
672 : };
673 : nsCSSSection mSection;
674 :
675 : nsXMLNameSpaceMap *mNameSpaceMap; // weak, mSheet owns it
676 :
677 : // After an UngetToken is done this flag is true. The next call to
678 : // GetToken clears the flag.
679 : bool mHavePushBack : 1;
680 :
681 : // True if we are in quirks mode; false in standards or almost standards mode
682 : bool mNavQuirkMode : 1;
683 :
684 : // True if unsafe rules should be allowed
685 : bool mUnsafeRulesEnabled : 1;
686 :
687 : // True for parsing media lists for HTML attributes, where we have to
688 : // ignore CSS comments.
689 : bool mHTMLMediaMode : 1;
690 :
691 : // This flag is set when parsing a non-box shorthand; it's used to not apply
692 : // some quirks during shorthand parsing
693 : bool mParsingCompoundProperty : 1;
694 :
695 : // GetNamespaceIdForPrefix will set mFoundUnresolvablePrefix to true
696 : // when it encounters a prefix that is not mapped to a namespace.
697 : bool mFoundUnresolvablePrefix : 1;
698 :
699 : #ifdef DEBUG
700 : bool mScannerInited : 1;
701 : #endif
702 :
703 : // Stack of rule groups; used for @media and such.
704 : InfallibleTArray<nsRefPtr<css::GroupRule> > mGroupStack;
705 :
706 : // During the parsing of a property (which may be a shorthand), the data
707 : // are stored in |mTempData|. (It is needed to ensure that parser
708 : // errors cause the data to be ignored, and to ensure that a
709 : // non-'!important' declaration does not override an '!important'
710 : // one.)
711 : nsCSSExpandedDataBlock mTempData;
712 :
713 : // All data from successfully parsed properties are placed into |mData|.
714 : nsCSSExpandedDataBlock mData;
715 :
716 : public:
717 : // Used from nsCSSParser constructors and destructors
718 : CSSParserImpl* mNextFree;
719 : };
720 :
721 0 : static void AppendRuleToArray(css::Rule* aRule, void* aArray)
722 : {
723 0 : static_cast<nsCOMArray<css::Rule>*>(aArray)->AppendObject(aRule);
724 0 : }
725 :
726 0 : static void AppendRuleToSheet(css::Rule* aRule, void* aParser)
727 : {
728 0 : CSSParserImpl* parser = (CSSParserImpl*) aParser;
729 0 : parser->AppendRule(aRule);
730 0 : }
731 :
732 : #ifdef CSS_REPORT_PARSE_ERRORS
733 :
734 : #define REPORT_UNEXPECTED(msg_) \
735 : mScanner.ReportUnexpected(#msg_)
736 :
737 : #define REPORT_UNEXPECTED_P(msg_, params_) \
738 : mScanner.ReportUnexpectedParams(#msg_, params_)
739 :
740 : #define REPORT_UNEXPECTED_EOF(lf_) \
741 : mScanner.ReportUnexpectedEOF(#lf_)
742 :
743 : #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
744 : mScanner.ReportUnexpectedEOF(ch_)
745 :
746 : #define REPORT_UNEXPECTED_TOKEN(msg_) \
747 : mScanner.ReportUnexpectedToken(mToken, #msg_)
748 :
749 : #define REPORT_UNEXPECTED_TOKEN_P(msg_, params_) \
750 : mScanner.ReportUnexpectedTokenParams(mToken, #msg_, \
751 : params_, ArrayLength(params_))
752 :
753 :
754 : #define OUTPUT_ERROR() \
755 : mScanner.OutputError()
756 :
757 : #define CLEAR_ERROR() \
758 : mScanner.ClearError()
759 :
760 : #else
761 :
762 : #define REPORT_UNEXPECTED(msg_)
763 : #define REPORT_UNEXPECTED_P(msg_, params_)
764 : #define REPORT_UNEXPECTED_EOF(lf_)
765 : #define REPORT_UNEXPECTED_EOF_CHAR(ch_)
766 : #define REPORT_UNEXPECTED_TOKEN(msg_)
767 : #define REPORT_UNEXPECTED_TOKEN_P(msg_, params_)
768 : #define OUTPUT_ERROR()
769 : #define CLEAR_ERROR()
770 :
771 : #endif
772 :
773 12 : CSSParserImpl::CSSParserImpl()
774 : : mToken(),
775 : mScanner(),
776 : mChildLoader(nsnull),
777 : mSection(eCSSSection_Charset),
778 : mNameSpaceMap(nsnull),
779 : mHavePushBack(false),
780 : mNavQuirkMode(false),
781 : mUnsafeRulesEnabled(false),
782 : mHTMLMediaMode(false),
783 : mParsingCompoundProperty(false),
784 : mFoundUnresolvablePrefix(false)
785 : #ifdef DEBUG
786 : , mScannerInited(false)
787 : #endif
788 12 : , mNextFree(nsnull)
789 : {
790 12 : }
791 :
792 24 : CSSParserImpl::~CSSParserImpl()
793 : {
794 12 : mData.AssertInitialState();
795 12 : mTempData.AssertInitialState();
796 12 : }
797 :
798 : nsresult
799 110 : CSSParserImpl::SetStyleSheet(nsCSSStyleSheet* aSheet)
800 : {
801 110 : if (aSheet != mSheet) {
802 : // Switch to using the new sheet, if any
803 0 : mGroupStack.Clear();
804 0 : mSheet = aSheet;
805 0 : if (mSheet) {
806 0 : mNameSpaceMap = mSheet->GetNameSpaceMap();
807 : } else {
808 0 : mNameSpaceMap = nsnull;
809 : }
810 : }
811 :
812 110 : return NS_OK;
813 : }
814 :
815 : nsresult
816 220 : CSSParserImpl::SetQuirkMode(bool aQuirkMode)
817 : {
818 220 : mNavQuirkMode = aQuirkMode;
819 220 : return NS_OK;
820 : }
821 :
822 : nsresult
823 110 : CSSParserImpl::SetSVGMode(bool aSVGMode)
824 : {
825 110 : mScanner.SetSVGMode(aSVGMode);
826 110 : return NS_OK;
827 : }
828 :
829 : nsresult
830 220 : CSSParserImpl::SetChildLoader(mozilla::css::Loader* aChildLoader)
831 : {
832 220 : mChildLoader = aChildLoader; // not ref counted, it owns us
833 220 : return NS_OK;
834 : }
835 :
836 : void
837 110 : CSSParserImpl::Reset()
838 : {
839 110 : NS_ASSERTION(! mScannerInited, "resetting with scanner active");
840 110 : SetStyleSheet(nsnull);
841 110 : SetQuirkMode(false);
842 110 : SetSVGMode(false);
843 110 : SetChildLoader(nsnull);
844 110 : }
845 :
846 : void
847 110 : CSSParserImpl::InitScanner(const nsSubstring& aString, nsIURI* aSheetURI,
848 : PRUint32 aLineNumber, nsIURI* aBaseURI,
849 : nsIPrincipal* aSheetPrincipal)
850 : {
851 : // Having it not own the string is OK since the caller will hold on to
852 : // the stream until we're done parsing.
853 110 : NS_ASSERTION(! mScannerInited, "already have scanner");
854 :
855 110 : mScanner.Init(aString, aSheetURI, aLineNumber, mSheet, mChildLoader);
856 :
857 : #ifdef DEBUG
858 110 : mScannerInited = true;
859 : #endif
860 110 : mBaseURI = aBaseURI;
861 110 : mSheetURI = aSheetURI;
862 110 : mSheetPrincipal = aSheetPrincipal;
863 :
864 110 : mHavePushBack = false;
865 110 : }
866 :
867 : void
868 110 : CSSParserImpl::ReleaseScanner(void)
869 : {
870 110 : mScanner.Close();
871 : #ifdef DEBUG
872 110 : mScannerInited = false;
873 : #endif
874 110 : mBaseURI = nsnull;
875 110 : mSheetURI = nsnull;
876 110 : mSheetPrincipal = nsnull;
877 110 : }
878 :
879 : nsresult
880 0 : CSSParserImpl::ParseSheet(const nsAString& aInput,
881 : nsIURI* aSheetURI,
882 : nsIURI* aBaseURI,
883 : nsIPrincipal* aSheetPrincipal,
884 : PRUint32 aLineNumber,
885 : bool aAllowUnsafeRules)
886 : {
887 0 : NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
888 :
889 0 : NS_ASSERTION(nsnull != aBaseURI, "need base URI");
890 0 : NS_ASSERTION(nsnull != aSheetURI, "need sheet URI");
891 0 : AssertInitialState();
892 :
893 0 : NS_PRECONDITION(mSheet, "Must have sheet to parse into");
894 0 : NS_ENSURE_STATE(mSheet);
895 :
896 : #ifdef DEBUG
897 0 : nsIURI* uri = mSheet->GetSheetURI();
898 : bool equal;
899 0 : NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
900 : "Sheet URI does not match passed URI");
901 0 : NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
902 : &equal)) &&
903 : equal,
904 : "Sheet principal does not match passed principal");
905 : #endif
906 :
907 0 : InitScanner(aInput, aSheetURI, aLineNumber, aBaseURI, aSheetPrincipal);
908 :
909 0 : PRInt32 ruleCount = mSheet->StyleRuleCount();
910 0 : if (0 < ruleCount) {
911 0 : css::Rule* lastRule = nsnull;
912 0 : mSheet->GetStyleRuleAt(ruleCount - 1, lastRule);
913 0 : if (lastRule) {
914 0 : switch (lastRule->GetType()) {
915 : case css::Rule::CHARSET_RULE:
916 : case css::Rule::IMPORT_RULE:
917 0 : mSection = eCSSSection_Import;
918 0 : break;
919 : case css::Rule::NAMESPACE_RULE:
920 0 : mSection = eCSSSection_NameSpace;
921 0 : break;
922 : default:
923 0 : mSection = eCSSSection_General;
924 0 : break;
925 : }
926 0 : NS_RELEASE(lastRule);
927 : }
928 : }
929 : else {
930 0 : mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
931 : }
932 :
933 0 : mUnsafeRulesEnabled = aAllowUnsafeRules;
934 :
935 0 : nsCSSToken* tk = &mToken;
936 0 : for (;;) {
937 : // Get next non-whitespace token
938 0 : if (!GetToken(true)) {
939 0 : OUTPUT_ERROR();
940 : break;
941 : }
942 0 : if (eCSSToken_HTMLComment == tk->mType) {
943 0 : continue; // legal here only
944 : }
945 0 : if (eCSSToken_AtKeyword == tk->mType) {
946 0 : ParseAtRule(AppendRuleToSheet, this, false);
947 0 : continue;
948 : }
949 0 : UngetToken();
950 0 : if (ParseRuleSet(AppendRuleToSheet, this)) {
951 0 : mSection = eCSSSection_General;
952 : }
953 : }
954 0 : ReleaseScanner();
955 :
956 0 : mUnsafeRulesEnabled = false;
957 :
958 : // XXX check for low level errors
959 0 : return NS_OK;
960 : }
961 :
962 : /**
963 : * Determines whether the identifier contained in the given string is a
964 : * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1.
965 : */
966 : static bool
967 0 : NonMozillaVendorIdentifier(const nsAString& ident)
968 : {
969 0 : return (ident.First() == PRUnichar('-') &&
970 0 : !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) ||
971 0 : ident.First() == PRUnichar('_');
972 :
973 : }
974 :
975 : nsresult
976 0 : CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
977 : nsIURI* aDocURI,
978 : nsIURI* aBaseURI,
979 : nsIPrincipal* aNodePrincipal,
980 : css::StyleRule** aResult)
981 : {
982 0 : NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
983 0 : AssertInitialState();
984 :
985 0 : NS_ASSERTION(nsnull != aBaseURI, "need base URI");
986 :
987 : // XXX line number?
988 0 : InitScanner(aAttributeValue, aDocURI, 0, aBaseURI, aNodePrincipal);
989 :
990 0 : mSection = eCSSSection_General;
991 :
992 : // In quirks mode, allow style declarations to have braces or not
993 : // (bug 99554).
994 : bool haveBraces;
995 0 : if (mNavQuirkMode && GetToken(true)) {
996 : haveBraces = eCSSToken_Symbol == mToken.mType &&
997 0 : '{' == mToken.mSymbol;
998 0 : UngetToken();
999 : }
1000 : else {
1001 0 : haveBraces = false;
1002 : }
1003 :
1004 0 : css::Declaration* declaration = ParseDeclarationBlock(haveBraces);
1005 0 : if (declaration) {
1006 : // Create a style rule for the declaration
1007 0 : NS_ADDREF(*aResult = new css::StyleRule(nsnull, declaration));
1008 : } else {
1009 0 : *aResult = nsnull;
1010 : }
1011 :
1012 0 : ReleaseScanner();
1013 :
1014 : // XXX check for low level errors
1015 0 : return NS_OK;
1016 : }
1017 :
1018 : nsresult
1019 0 : CSSParserImpl::ParseDeclarations(const nsAString& aBuffer,
1020 : nsIURI* aSheetURI,
1021 : nsIURI* aBaseURI,
1022 : nsIPrincipal* aSheetPrincipal,
1023 : css::Declaration* aDeclaration,
1024 : bool* aChanged)
1025 : {
1026 0 : NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1027 0 : AssertInitialState();
1028 :
1029 0 : *aChanged = false;
1030 :
1031 0 : InitScanner(aBuffer, aSheetURI, 0, aBaseURI, aSheetPrincipal);
1032 :
1033 0 : mSection = eCSSSection_General;
1034 :
1035 0 : mData.AssertInitialState();
1036 0 : aDeclaration->ClearData();
1037 : // We could check if it was already empty, but...
1038 0 : *aChanged = true;
1039 :
1040 0 : for (;;) {
1041 : // If we cleared the old decl, then we want to be calling
1042 : // ValueAppended as we parse.
1043 0 : if (!ParseDeclaration(aDeclaration, false, true, aChanged)) {
1044 0 : if (!SkipDeclaration(false)) {
1045 : break;
1046 : }
1047 : }
1048 : }
1049 :
1050 0 : aDeclaration->CompressFrom(&mData);
1051 0 : ReleaseScanner();
1052 0 : return NS_OK;
1053 : }
1054 :
1055 : nsresult
1056 0 : CSSParserImpl::ParseRule(const nsAString& aRule,
1057 : nsIURI* aSheetURI,
1058 : nsIURI* aBaseURI,
1059 : nsIPrincipal* aSheetPrincipal,
1060 : nsCOMArray<css::Rule>& aResult)
1061 : {
1062 0 : NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1063 0 : AssertInitialState();
1064 :
1065 0 : NS_ASSERTION(nsnull != aBaseURI, "need base URI");
1066 :
1067 0 : InitScanner(aRule, aSheetURI, 0, aBaseURI, aSheetPrincipal);
1068 :
1069 0 : mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
1070 :
1071 0 : nsCSSToken* tk = &mToken;
1072 : // Get first non-whitespace token
1073 0 : if (!GetToken(true)) {
1074 0 : REPORT_UNEXPECTED(PEParseRuleWSOnly);
1075 0 : OUTPUT_ERROR();
1076 0 : } else if (eCSSToken_AtKeyword == tk->mType) {
1077 0 : ParseAtRule(AppendRuleToArray, &aResult, false);
1078 : }
1079 : else {
1080 0 : UngetToken();
1081 0 : ParseRuleSet(AppendRuleToArray, &aResult);
1082 : }
1083 0 : OUTPUT_ERROR();
1084 0 : ReleaseScanner();
1085 : // XXX check for low-level errors
1086 0 : return NS_OK;
1087 : }
1088 :
1089 : // See Bug 723197
1090 : #ifdef _MSC_VER
1091 : #pragma optimize( "", off )
1092 : #endif
1093 : nsresult
1094 0 : CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
1095 : const nsAString& aPropValue,
1096 : nsIURI* aSheetURI,
1097 : nsIURI* aBaseURI,
1098 : nsIPrincipal* aSheetPrincipal,
1099 : css::Declaration* aDeclaration,
1100 : bool* aChanged,
1101 : bool aIsImportant)
1102 : {
1103 0 : NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1104 0 : NS_PRECONDITION(aBaseURI, "need base URI");
1105 0 : NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
1106 0 : AssertInitialState();
1107 0 : mData.AssertInitialState();
1108 0 : mTempData.AssertInitialState();
1109 0 : aDeclaration->AssertMutable();
1110 :
1111 0 : InitScanner(aPropValue, aSheetURI, 0, aBaseURI, aSheetPrincipal);
1112 0 : mSection = eCSSSection_General;
1113 :
1114 0 : *aChanged = false;
1115 :
1116 0 : if (eCSSProperty_UNKNOWN == aPropID) { // unknown property
1117 0 : NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1118 : const PRUnichar *params[] = {
1119 0 : propName.get()
1120 0 : };
1121 0 : REPORT_UNEXPECTED_P(PEUnknownProperty, params);
1122 0 : REPORT_UNEXPECTED(PEDeclDropped);
1123 0 : OUTPUT_ERROR();
1124 0 : ReleaseScanner();
1125 0 : return NS_OK;
1126 : }
1127 :
1128 0 : bool parsedOK = ParseProperty(aPropID);
1129 : // We should now be at EOF
1130 0 : if (parsedOK && GetToken(true)) {
1131 0 : REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1132 0 : parsedOK = false;
1133 : }
1134 :
1135 0 : if (!parsedOK) {
1136 0 : NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1137 : const PRUnichar *params[] = {
1138 0 : propName.get()
1139 0 : };
1140 0 : REPORT_UNEXPECTED_P(PEValueParsingError, params);
1141 0 : REPORT_UNEXPECTED(PEDeclDropped);
1142 0 : OUTPUT_ERROR();
1143 0 : mTempData.ClearProperty(aPropID);
1144 : } else {
1145 :
1146 : // We know we don't need to force a ValueAppended call for the new
1147 : // value. So if we are not processing a shorthand, and there's
1148 : // already a value for this property in the declaration at the
1149 : // same importance level, then we can just copy our parsed value
1150 : // directly into the declaration without going through the whole
1151 : // expand/compress thing.
1152 0 : if (!aDeclaration->TryReplaceValue(aPropID, aIsImportant, mTempData,
1153 0 : aChanged)) {
1154 : // Do it the slow way
1155 0 : aDeclaration->ExpandTo(&mData);
1156 : *aChanged = mData.TransferFromBlock(mTempData, aPropID, aIsImportant,
1157 0 : true, false, aDeclaration);
1158 0 : aDeclaration->CompressFrom(&mData);
1159 : }
1160 0 : CLEAR_ERROR();
1161 : }
1162 :
1163 0 : mTempData.AssertInitialState();
1164 :
1165 0 : ReleaseScanner();
1166 0 : return NS_OK;
1167 : }
1168 : #ifdef _MSC_VER
1169 : #pragma optimize( "", on )
1170 : #endif
1171 :
1172 : nsresult
1173 0 : CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer,
1174 : nsIURI* aURI, // for error reporting
1175 : PRUint32 aLineNumber, // for error reporting
1176 : nsMediaList* aMediaList,
1177 : bool aHTMLMode)
1178 : {
1179 : // XXX Are there cases where the caller wants to keep what it already
1180 : // has in case of parser error?
1181 0 : aMediaList->Clear();
1182 :
1183 : // fake base URI since media lists don't have URIs in them
1184 0 : InitScanner(aBuffer, aURI, aLineNumber, aURI, nsnull);
1185 :
1186 0 : AssertInitialState();
1187 0 : mHTMLMediaMode = aHTMLMode;
1188 :
1189 : // XXXldb We need to make the scanner not skip CSS comments! (Or
1190 : // should we?)
1191 :
1192 : // For aHTMLMode, we used to follow the parsing rules in
1193 : // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors
1194 : // which wouldn't work for media queries since they remove all but the
1195 : // first word. However, they're changed in
1196 : // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-document.html#media2
1197 : // (as of 2008-05-29) which says that the media attribute just points
1198 : // to a media query. (The main substative difference is the relative
1199 : // precedence of commas and paretheses.)
1200 :
1201 0 : GatherMedia(aMediaList, false);
1202 :
1203 0 : CLEAR_ERROR();
1204 0 : ReleaseScanner();
1205 0 : mHTMLMediaMode = false;
1206 :
1207 0 : return NS_OK;
1208 : }
1209 :
1210 : nsresult
1211 0 : CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
1212 : nsIURI* aURI, // for error reporting
1213 : PRUint32 aLineNumber, // for error reporting
1214 : nscolor* aColor)
1215 : {
1216 0 : AssertInitialState();
1217 0 : InitScanner(aBuffer, aURI, aLineNumber, aURI, nsnull);
1218 :
1219 0 : nsCSSValue value;
1220 : // Parse a color, and check that there's nothing else after it.
1221 0 : bool colorParsed = ParseColor(value) && !GetToken(true);
1222 0 : OUTPUT_ERROR();
1223 0 : ReleaseScanner();
1224 :
1225 0 : if (!colorParsed) {
1226 0 : return NS_ERROR_FAILURE;
1227 : }
1228 :
1229 0 : switch (value.GetUnit()) {
1230 : case eCSSUnit_Color:
1231 0 : *aColor = value.GetColorValue();
1232 0 : return NS_OK;
1233 :
1234 : case eCSSUnit_Ident: {
1235 0 : nsDependentString id(value.GetStringBufferValue());
1236 0 : if (!NS_ColorNameToRGB(id, aColor)) {
1237 0 : return NS_ERROR_FAILURE;
1238 : }
1239 0 : return NS_OK;
1240 : }
1241 :
1242 : case eCSSUnit_EnumColor: {
1243 0 : PRInt32 val = value.GetIntValue();
1244 0 : if (val < 0) {
1245 : // XXX - negative numbers are NS_COLOR_CURRENTCOLOR,
1246 : // NS_COLOR_MOZ_HYPERLINKTEXT, etc. which we don't handle.
1247 : // Should remove this limitation at some point.
1248 0 : return NS_ERROR_FAILURE;
1249 : }
1250 : nscolor rgba;
1251 0 : nsresult rv = LookAndFeel::GetColor(LookAndFeel::ColorID(val), &rgba);
1252 0 : if (NS_FAILED(rv)) {
1253 0 : return rv;
1254 : }
1255 0 : *aColor = rgba;
1256 0 : return NS_OK;
1257 : }
1258 :
1259 : default:
1260 0 : return NS_ERROR_FAILURE;
1261 : }
1262 : }
1263 :
1264 : nsresult
1265 110 : CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
1266 : nsIURI* aURI, // for error reporting
1267 : PRUint32 aLineNumber, // for error reporting
1268 : nsCSSSelectorList **aSelectorList)
1269 : {
1270 110 : InitScanner(aSelectorString, aURI, aLineNumber, aURI, nsnull);
1271 :
1272 110 : AssertInitialState();
1273 :
1274 : // This is the only place that cares about mFoundUnresolvablePrefix,
1275 : // so this is the only place that bothers clearing it.
1276 110 : mFoundUnresolvablePrefix = false;
1277 :
1278 110 : bool success = ParseSelectorList(*aSelectorList, PRUnichar(0));
1279 110 : bool prefixErr = mFoundUnresolvablePrefix;
1280 :
1281 : // We deliberately do not call OUTPUT_ERROR here, because all our
1282 : // callers map a failure return to a JS exception, and if that JS
1283 : // exception is caught, people don't want to see parser diagnostics;
1284 : // see e.g. http://bugs.jquery.com/ticket/7535
1285 : // It would be nice to be able to save the parser diagnostics into
1286 : // the exception, so that if it _isn't_ caught we can report them
1287 : // along with the usual uncaught-exception message, but we don't
1288 : // have any way to do that at present; see bug 631621.
1289 110 : CLEAR_ERROR();
1290 110 : ReleaseScanner();
1291 :
1292 110 : if (success) {
1293 110 : NS_ASSERTION(*aSelectorList, "Should have list!");
1294 110 : return NS_OK;
1295 : }
1296 :
1297 0 : NS_ASSERTION(!*aSelectorList, "Shouldn't have list!");
1298 0 : if (prefixErr)
1299 0 : return NS_ERROR_DOM_NAMESPACE_ERR;
1300 :
1301 0 : return NS_ERROR_DOM_SYNTAX_ERR;
1302 : }
1303 :
1304 :
1305 : already_AddRefed<nsCSSKeyframeRule>
1306 0 : CSSParserImpl::ParseKeyframeRule(const nsSubstring& aBuffer,
1307 : nsIURI* aURI,
1308 : PRUint32 aLineNumber)
1309 : {
1310 0 : InitScanner(aBuffer, aURI, aLineNumber, aURI, nsnull);
1311 :
1312 0 : AssertInitialState();
1313 :
1314 0 : nsRefPtr<nsCSSKeyframeRule> result = ParseKeyframeRule();
1315 0 : if (GetToken(true)) {
1316 : // extra garbage at the end
1317 0 : result = nsnull;
1318 : }
1319 :
1320 0 : OUTPUT_ERROR();
1321 0 : ReleaseScanner();
1322 :
1323 0 : return result.forget();
1324 : }
1325 :
1326 : bool
1327 0 : CSSParserImpl::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
1328 : nsIURI* aURI, // for error reporting
1329 : PRUint32 aLineNumber, // for error reporting
1330 : InfallibleTArray<float>& aSelectorList)
1331 : {
1332 0 : NS_ABORT_IF_FALSE(aSelectorList.IsEmpty(), "given list should start empty");
1333 :
1334 0 : InitScanner(aSelectorString, aURI, aLineNumber, aURI, nsnull);
1335 :
1336 0 : AssertInitialState();
1337 :
1338 0 : bool success = ParseKeyframeSelectorList(aSelectorList) &&
1339 : // must consume entire input string
1340 0 : !GetToken(true);
1341 :
1342 0 : OUTPUT_ERROR();
1343 0 : ReleaseScanner();
1344 :
1345 0 : if (success) {
1346 0 : NS_ASSERTION(!aSelectorList.IsEmpty(), "should not be empty");
1347 : } else {
1348 0 : aSelectorList.Clear();
1349 : }
1350 :
1351 0 : return success;
1352 : }
1353 :
1354 : //----------------------------------------------------------------------
1355 :
1356 : bool
1357 1210 : CSSParserImpl::GetToken(bool aSkipWS)
1358 : {
1359 110 : for (;;) {
1360 1210 : if (!mHavePushBack) {
1361 990 : if (!mScanner.Next(mToken)) {
1362 : break;
1363 : }
1364 : }
1365 770 : mHavePushBack = false;
1366 770 : if (aSkipWS && (eCSSToken_WhiteSpace == mToken.mType)) {
1367 110 : continue;
1368 : }
1369 660 : return true;
1370 : }
1371 440 : return false;
1372 : }
1373 :
1374 : bool
1375 0 : CSSParserImpl::GetURLInParens(nsString& aURL)
1376 : {
1377 0 : NS_ASSERTION(!mHavePushBack, "mustn't have pushback at this point");
1378 0 : if (! mScanner.NextURL(mToken)) {
1379 : // EOF
1380 0 : return false;
1381 : }
1382 :
1383 0 : aURL = mToken.mIdent;
1384 :
1385 0 : if (eCSSToken_URL != mToken.mType) {
1386 : // In the failure case (which gives a token of type
1387 : // eCSSToken_Bad_URL), we do not have to match parentheses *inside*
1388 : // the Bad_URL token, since this is now an invalid URL token. But
1389 : // we do need to match the closing parenthesis to match the 'url('.
1390 0 : NS_ABORT_IF_FALSE(mToken.mType == eCSSToken_Bad_URL,
1391 : "unexpected token type");
1392 0 : SkipUntil(')');
1393 0 : return false;
1394 : }
1395 :
1396 0 : return true;
1397 : }
1398 :
1399 : void
1400 220 : CSSParserImpl::UngetToken()
1401 : {
1402 220 : NS_PRECONDITION(!mHavePushBack, "double pushback");
1403 220 : mHavePushBack = true;
1404 220 : }
1405 :
1406 : bool
1407 220 : CSSParserImpl::ExpectSymbol(PRUnichar aSymbol,
1408 : bool aSkipWS)
1409 : {
1410 220 : if (!GetToken(aSkipWS)) {
1411 : // CSS2.1 specifies that all "open constructs" are to be closed at
1412 : // EOF. It simplifies higher layers if we claim to have found an
1413 : // ), ], }, or ; if we encounter EOF while looking for one of them.
1414 : // Do still issue a diagnostic, to aid debugging.
1415 110 : if (aSymbol == ')' || aSymbol == ']' ||
1416 : aSymbol == '}' || aSymbol == ';') {
1417 0 : REPORT_UNEXPECTED_EOF_CHAR(aSymbol);
1418 0 : return true;
1419 : }
1420 : else
1421 110 : return false;
1422 : }
1423 110 : if (mToken.IsSymbol(aSymbol)) {
1424 0 : return true;
1425 : }
1426 110 : UngetToken();
1427 110 : return false;
1428 : }
1429 :
1430 : // Checks to see if we're at the end of a property. If an error occurs during
1431 : // the check, does not signal a parse error.
1432 : bool
1433 0 : CSSParserImpl::CheckEndProperty()
1434 : {
1435 0 : if (!GetToken(true)) {
1436 0 : return true; // properties may end with eof
1437 : }
1438 0 : if ((eCSSToken_Symbol == mToken.mType) &&
1439 : ((';' == mToken.mSymbol) ||
1440 : ('!' == mToken.mSymbol) ||
1441 : ('}' == mToken.mSymbol))) {
1442 : // XXX need to verify that ! is only followed by "important [;|}]
1443 : // XXX this requires a multi-token pushback buffer
1444 0 : UngetToken();
1445 0 : return true;
1446 : }
1447 0 : UngetToken();
1448 0 : return false;
1449 : }
1450 :
1451 : // Checks if we're at the end of a property, raising an error if we're not.
1452 : bool
1453 0 : CSSParserImpl::ExpectEndProperty()
1454 : {
1455 0 : if (CheckEndProperty())
1456 0 : return true;
1457 :
1458 : // If we're here, we read something incorrect, so we should report it.
1459 0 : REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1460 0 : return false;
1461 : }
1462 :
1463 : // Parses the priority suffix on a property, which at present may be
1464 : // either '!important' or nothing.
1465 : CSSParserImpl::PriorityParsingStatus
1466 0 : CSSParserImpl::ParsePriority()
1467 : {
1468 0 : if (!GetToken(true)) {
1469 0 : return ePriority_None; // properties may end with EOF
1470 : }
1471 0 : if (!mToken.IsSymbol('!')) {
1472 0 : UngetToken();
1473 0 : return ePriority_None; // dunno what it is, but it's not a priority
1474 : }
1475 :
1476 0 : if (!GetToken(true)) {
1477 : // EOF is not ok after !
1478 0 : REPORT_UNEXPECTED_EOF(PEImportantEOF);
1479 0 : return ePriority_Error;
1480 : }
1481 :
1482 0 : if (mToken.mType != eCSSToken_Ident ||
1483 0 : !mToken.mIdent.LowerCaseEqualsLiteral("important")) {
1484 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
1485 0 : UngetToken();
1486 0 : return ePriority_Error;
1487 : }
1488 :
1489 0 : return ePriority_Important;
1490 : }
1491 :
1492 : nsSubstring*
1493 0 : CSSParserImpl::NextIdent()
1494 : {
1495 : // XXX Error reporting?
1496 0 : if (!GetToken(true)) {
1497 0 : return nsnull;
1498 : }
1499 0 : if (eCSSToken_Ident != mToken.mType) {
1500 0 : UngetToken();
1501 0 : return nsnull;
1502 : }
1503 0 : return &mToken.mIdent;
1504 : }
1505 :
1506 : bool
1507 0 : CSSParserImpl::SkipAtRule(bool aInsideBlock)
1508 : {
1509 0 : for (;;) {
1510 0 : if (!GetToken(true)) {
1511 0 : REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF);
1512 0 : return false;
1513 : }
1514 0 : if (eCSSToken_Symbol == mToken.mType) {
1515 0 : PRUnichar symbol = mToken.mSymbol;
1516 0 : if (symbol == ';') {
1517 0 : break;
1518 : }
1519 0 : if (aInsideBlock && symbol == '}') {
1520 : // The closing } doesn't belong to us.
1521 0 : UngetToken();
1522 0 : break;
1523 : }
1524 0 : if (symbol == '{') {
1525 0 : SkipUntil('}');
1526 0 : break;
1527 0 : } else if (symbol == '(') {
1528 0 : SkipUntil(')');
1529 0 : } else if (symbol == '[') {
1530 0 : SkipUntil(']');
1531 : }
1532 0 : } else if (eCSSToken_Function == mToken.mType ||
1533 : eCSSToken_Bad_URL == mToken.mType) {
1534 0 : SkipUntil(')');
1535 : }
1536 : }
1537 0 : return true;
1538 : }
1539 :
1540 : bool
1541 0 : CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc,
1542 : void* aData,
1543 : bool aInAtRule)
1544 : {
1545 :
1546 : nsCSSSection newSection;
1547 : bool (CSSParserImpl::*parseFunc)(RuleAppendFunc, void*);
1548 :
1549 0 : if ((mSection <= eCSSSection_Charset) &&
1550 0 : (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
1551 0 : parseFunc = &CSSParserImpl::ParseCharsetRule;
1552 0 : newSection = eCSSSection_Import; // only one charset allowed
1553 :
1554 0 : } else if ((mSection <= eCSSSection_Import) &&
1555 0 : mToken.mIdent.LowerCaseEqualsLiteral("import")) {
1556 0 : parseFunc = &CSSParserImpl::ParseImportRule;
1557 0 : newSection = eCSSSection_Import;
1558 :
1559 0 : } else if ((mSection <= eCSSSection_NameSpace) &&
1560 0 : mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
1561 0 : parseFunc = &CSSParserImpl::ParseNameSpaceRule;
1562 0 : newSection = eCSSSection_NameSpace;
1563 :
1564 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
1565 0 : parseFunc = &CSSParserImpl::ParseMediaRule;
1566 0 : newSection = eCSSSection_General;
1567 :
1568 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
1569 0 : parseFunc = &CSSParserImpl::ParseMozDocumentRule;
1570 0 : newSection = eCSSSection_General;
1571 :
1572 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
1573 0 : parseFunc = &CSSParserImpl::ParseFontFaceRule;
1574 0 : newSection = eCSSSection_General;
1575 :
1576 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
1577 0 : parseFunc = &CSSParserImpl::ParsePageRule;
1578 0 : newSection = eCSSSection_General;
1579 :
1580 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-keyframes")) {
1581 0 : parseFunc = &CSSParserImpl::ParseKeyframesRule;
1582 0 : newSection = eCSSSection_General;
1583 :
1584 : } else {
1585 0 : if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
1586 0 : REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
1587 0 : OUTPUT_ERROR();
1588 : }
1589 : // Skip over unsupported at rule, don't advance section
1590 0 : return SkipAtRule(aInAtRule);
1591 : }
1592 :
1593 : // Inside of @-rules, only the rules that can occur anywhere
1594 : // are allowed.
1595 0 : bool unnestable = aInAtRule && newSection != eCSSSection_General;
1596 0 : if (unnestable) {
1597 0 : REPORT_UNEXPECTED_TOKEN(PEGroupRuleNestedAtRule);
1598 : }
1599 :
1600 0 : if (unnestable || !(this->*parseFunc)(aAppendFunc, aData)) {
1601 : // Skip over invalid at rule, don't advance section
1602 0 : OUTPUT_ERROR();
1603 0 : return SkipAtRule(aInAtRule);
1604 : }
1605 :
1606 : // Nested @-rules don't affect the top-level rule chain requirement
1607 0 : if (!aInAtRule) {
1608 0 : mSection = newSection;
1609 : }
1610 :
1611 0 : return true;
1612 : }
1613 :
1614 : bool
1615 0 : CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc,
1616 : void* aData)
1617 : {
1618 0 : if (!GetToken(true)) {
1619 0 : REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
1620 0 : return false;
1621 : }
1622 :
1623 0 : if (eCSSToken_String != mToken.mType) {
1624 0 : UngetToken();
1625 0 : REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
1626 0 : return false;
1627 : }
1628 :
1629 0 : nsAutoString charset = mToken.mIdent;
1630 :
1631 0 : if (!ExpectSymbol(';', true)) {
1632 0 : return false;
1633 : }
1634 :
1635 0 : nsRefPtr<css::CharsetRule> rule = new css::CharsetRule(charset);
1636 0 : (*aAppendFunc)(rule, aData);
1637 :
1638 0 : return true;
1639 : }
1640 :
1641 : bool
1642 0 : CSSParserImpl::ParseURLOrString(nsString& aURL)
1643 : {
1644 0 : if (!GetToken(true)) {
1645 0 : return false;
1646 : }
1647 0 : if (eCSSToken_String == mToken.mType || eCSSToken_URL == mToken.mType) {
1648 0 : aURL = mToken.mIdent;
1649 0 : return true;
1650 : }
1651 0 : UngetToken();
1652 0 : return false;
1653 : }
1654 :
1655 : bool
1656 0 : CSSParserImpl::ParseMediaQuery(bool aInAtRule,
1657 : nsMediaQuery **aQuery,
1658 : bool *aHitStop)
1659 : {
1660 0 : *aQuery = nsnull;
1661 0 : *aHitStop = false;
1662 :
1663 : // "If the comma-separated list is the empty list it is assumed to
1664 : // specify the media query 'all'." (css3-mediaqueries, section
1665 : // "Media Queries")
1666 0 : if (!GetToken(true)) {
1667 0 : *aHitStop = true;
1668 : // expected termination by EOF
1669 0 : if (!aInAtRule)
1670 0 : return true;
1671 :
1672 : // unexpected termination by EOF
1673 0 : REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1674 0 : return true;
1675 : }
1676 :
1677 0 : if (eCSSToken_Symbol == mToken.mType && aInAtRule &&
1678 : (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}' )) {
1679 0 : *aHitStop = true;
1680 0 : UngetToken();
1681 0 : return true;
1682 : }
1683 0 : UngetToken();
1684 :
1685 0 : nsMediaQuery* query = new nsMediaQuery;
1686 0 : *aQuery = query;
1687 :
1688 0 : if (ExpectSymbol('(', true)) {
1689 : // we got an expression without a media type
1690 0 : UngetToken(); // so ParseMediaQueryExpression can handle it
1691 0 : query->SetType(nsGkAtoms::all);
1692 0 : query->SetTypeOmitted();
1693 : // Just parse the first expression here.
1694 0 : if (!ParseMediaQueryExpression(query)) {
1695 0 : OUTPUT_ERROR();
1696 0 : query->SetHadUnknownExpression();
1697 : }
1698 : } else {
1699 0 : nsCOMPtr<nsIAtom> mediaType;
1700 0 : bool gotNotOrOnly = false;
1701 0 : for (;;) {
1702 0 : if (!GetToken(true)) {
1703 0 : REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1704 0 : return false;
1705 : }
1706 0 : if (eCSSToken_Ident != mToken.mType) {
1707 0 : REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
1708 0 : UngetToken();
1709 0 : return false;
1710 : }
1711 : // case insensitive from CSS - must be lower cased
1712 0 : nsContentUtils::ASCIIToLower(mToken.mIdent);
1713 0 : mediaType = do_GetAtom(mToken.mIdent);
1714 0 : if (!mediaType) {
1715 0 : NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
1716 : }
1717 0 : if (gotNotOrOnly ||
1718 0 : (mediaType != nsGkAtoms::_not && mediaType != nsGkAtoms::only))
1719 : break;
1720 0 : gotNotOrOnly = true;
1721 0 : if (mediaType == nsGkAtoms::_not)
1722 0 : query->SetNegated();
1723 : else
1724 0 : query->SetHasOnly();
1725 : }
1726 0 : query->SetType(mediaType);
1727 : }
1728 :
1729 0 : for (;;) {
1730 0 : if (!GetToken(true)) {
1731 0 : *aHitStop = true;
1732 : // expected termination by EOF
1733 0 : if (!aInAtRule)
1734 0 : break;
1735 :
1736 : // unexpected termination by EOF
1737 0 : REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1738 0 : break;
1739 : }
1740 :
1741 0 : if (eCSSToken_Symbol == mToken.mType && aInAtRule &&
1742 : (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}')) {
1743 0 : *aHitStop = true;
1744 0 : UngetToken();
1745 0 : break;
1746 : }
1747 0 : if (eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
1748 : // Done with the expressions for this query
1749 0 : break;
1750 : }
1751 0 : if (eCSSToken_Ident != mToken.mType ||
1752 0 : !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
1753 0 : REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
1754 0 : UngetToken();
1755 0 : return false;
1756 : }
1757 0 : if (!ParseMediaQueryExpression(query)) {
1758 0 : OUTPUT_ERROR();
1759 0 : query->SetHadUnknownExpression();
1760 : }
1761 : }
1762 0 : return true;
1763 : }
1764 :
1765 : // Returns false only when there is a low-level error in the scanner
1766 : // (out-of-memory).
1767 : bool
1768 0 : CSSParserImpl::GatherMedia(nsMediaList* aMedia,
1769 : bool aInAtRule)
1770 : {
1771 0 : for (;;) {
1772 0 : nsAutoPtr<nsMediaQuery> query;
1773 : bool hitStop;
1774 0 : if (!ParseMediaQuery(aInAtRule, getter_Transfers(query),
1775 0 : &hitStop)) {
1776 0 : NS_ASSERTION(!hitStop, "should return true when hit stop");
1777 0 : OUTPUT_ERROR();
1778 0 : if (query) {
1779 0 : query->SetHadUnknownExpression();
1780 : }
1781 0 : if (aInAtRule) {
1782 : const PRUnichar stopChars[] =
1783 0 : { PRUnichar(','), PRUnichar('{'), PRUnichar(';'), PRUnichar('}'), PRUnichar(0) };
1784 0 : SkipUntilOneOf(stopChars);
1785 : } else {
1786 0 : SkipUntil(',');
1787 : }
1788 : // Rely on SkipUntilOneOf leaving mToken around as the last token read.
1789 0 : if (mToken.mType == eCSSToken_Symbol && aInAtRule &&
1790 : (mToken.mSymbol == '{' || mToken.mSymbol == ';' || mToken.mSymbol == '}')) {
1791 0 : UngetToken();
1792 0 : hitStop = true;
1793 : }
1794 : }
1795 0 : if (query) {
1796 0 : aMedia->AppendQuery(query);
1797 : }
1798 0 : if (hitStop) {
1799 : break;
1800 : }
1801 : }
1802 0 : return true;
1803 : }
1804 :
1805 : bool
1806 0 : CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
1807 : {
1808 0 : if (!ExpectSymbol('(', true)) {
1809 0 : REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart);
1810 0 : return false;
1811 : }
1812 0 : if (! GetToken(true)) {
1813 0 : REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
1814 0 : return false;
1815 : }
1816 0 : if (eCSSToken_Ident != mToken.mType) {
1817 0 : REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
1818 0 : UngetToken();
1819 0 : SkipUntil(')');
1820 0 : return false;
1821 : }
1822 :
1823 0 : nsMediaExpression *expr = aQuery->NewExpression();
1824 :
1825 : // case insensitive from CSS - must be lower cased
1826 0 : nsContentUtils::ASCIIToLower(mToken.mIdent);
1827 : const PRUnichar *featureString;
1828 0 : if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("min-"))) {
1829 0 : expr->mRange = nsMediaExpression::eMin;
1830 0 : featureString = mToken.mIdent.get() + 4;
1831 0 : } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("max-"))) {
1832 0 : expr->mRange = nsMediaExpression::eMax;
1833 0 : featureString = mToken.mIdent.get() + 4;
1834 : } else {
1835 0 : expr->mRange = nsMediaExpression::eEqual;
1836 0 : featureString = mToken.mIdent.get();
1837 : }
1838 :
1839 0 : nsCOMPtr<nsIAtom> mediaFeatureAtom = do_GetAtom(featureString);
1840 0 : if (!mediaFeatureAtom) {
1841 0 : NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
1842 : }
1843 0 : const nsMediaFeature *feature = nsMediaFeatures::features;
1844 0 : for (; feature->mName; ++feature) {
1845 0 : if (*(feature->mName) == mediaFeatureAtom) {
1846 0 : break;
1847 : }
1848 : }
1849 0 : if (!feature->mName ||
1850 : (expr->mRange != nsMediaExpression::eEqual &&
1851 : feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) {
1852 0 : REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
1853 0 : SkipUntil(')');
1854 0 : return false;
1855 : }
1856 0 : expr->mFeature = feature;
1857 :
1858 0 : if (!GetToken(true) || mToken.IsSymbol(')')) {
1859 : // Query expressions for any feature can be given without a value.
1860 : // However, min/max prefixes are not allowed.
1861 0 : if (expr->mRange != nsMediaExpression::eEqual) {
1862 0 : REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue);
1863 0 : return false;
1864 : }
1865 0 : expr->mValue.Reset();
1866 0 : return true;
1867 : }
1868 :
1869 0 : if (!mToken.IsSymbol(':')) {
1870 0 : REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
1871 0 : UngetToken();
1872 0 : SkipUntil(')');
1873 0 : return false;
1874 : }
1875 :
1876 : bool rv;
1877 0 : switch (feature->mValueType) {
1878 : case nsMediaFeature::eLength:
1879 0 : rv = ParseNonNegativeVariant(expr->mValue, VARIANT_LENGTH, nsnull);
1880 0 : break;
1881 : case nsMediaFeature::eInteger:
1882 : case nsMediaFeature::eBoolInteger:
1883 0 : rv = ParseNonNegativeVariant(expr->mValue, VARIANT_INTEGER, nsnull);
1884 : // Enforce extra restrictions for eBoolInteger
1885 0 : if (rv &&
1886 : feature->mValueType == nsMediaFeature::eBoolInteger &&
1887 0 : expr->mValue.GetIntValue() > 1)
1888 0 : rv = false;
1889 0 : break;
1890 : case nsMediaFeature::eFloat:
1891 0 : rv = ParseNonNegativeVariant(expr->mValue, VARIANT_NUMBER, nsnull);
1892 0 : break;
1893 : case nsMediaFeature::eIntRatio:
1894 : {
1895 : // Two integers separated by '/', with optional whitespace on
1896 : // either side of the '/'.
1897 0 : nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
1898 0 : expr->mValue.SetArrayValue(a, eCSSUnit_Array);
1899 : // We don't bother with ParseNonNegativeVariant since we have to
1900 : // check for != 0 as well; no need to worry about the UngetToken
1901 : // since we're throwing out up to the next ')' anyway.
1902 0 : rv = ParseVariant(a->Item(0), VARIANT_INTEGER, nsnull) &&
1903 0 : a->Item(0).GetIntValue() > 0 &&
1904 0 : ExpectSymbol('/', true) &&
1905 0 : ParseVariant(a->Item(1), VARIANT_INTEGER, nsnull) &&
1906 0 : a->Item(1).GetIntValue() > 0;
1907 : }
1908 0 : break;
1909 : case nsMediaFeature::eResolution:
1910 0 : rv = GetToken(true);
1911 0 : if (!rv)
1912 0 : break;
1913 0 : rv = mToken.mType == eCSSToken_Dimension && mToken.mNumber > 0.0f;
1914 0 : if (!rv) {
1915 0 : UngetToken();
1916 0 : break;
1917 : }
1918 : // No worries about whether unitless zero is allowed, since the
1919 : // value must be positive (and we checked that above).
1920 0 : NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied");
1921 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
1922 0 : expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
1923 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
1924 0 : expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
1925 : } else {
1926 0 : rv = false;
1927 : }
1928 0 : break;
1929 : case nsMediaFeature::eEnumerated:
1930 : rv = ParseVariant(expr->mValue, VARIANT_KEYWORD,
1931 0 : feature->mData.mKeywordTable);
1932 0 : break;
1933 : case nsMediaFeature::eIdent:
1934 0 : rv = ParseVariant(expr->mValue, VARIANT_IDENTIFIER, nsnull);
1935 0 : break;
1936 : }
1937 0 : if (!rv || !ExpectSymbol(')', true)) {
1938 0 : REPORT_UNEXPECTED(PEMQExpectedFeatureValue);
1939 0 : SkipUntil(')');
1940 0 : return false;
1941 : }
1942 :
1943 0 : return true;
1944 : }
1945 :
1946 : // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
1947 : bool
1948 0 : CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData)
1949 : {
1950 0 : nsRefPtr<nsMediaList> media = new nsMediaList();
1951 :
1952 0 : nsAutoString url;
1953 0 : if (!ParseURLOrString(url)) {
1954 0 : REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
1955 0 : return false;
1956 : }
1957 :
1958 0 : if (!ExpectSymbol(';', true)) {
1959 0 : if (!GatherMedia(media, true) ||
1960 0 : !ExpectSymbol(';', true)) {
1961 0 : REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
1962 : // don't advance section, simply ignore invalid @import
1963 0 : return false;
1964 : }
1965 :
1966 : // Safe to assert this, since we ensured that there is something
1967 : // other than the ';' coming after the @import's url() token.
1968 0 : NS_ASSERTION(media->Count() != 0, "media list must be nonempty");
1969 : }
1970 :
1971 0 : ProcessImport(url, media, aAppendFunc, aData);
1972 0 : return true;
1973 : }
1974 :
1975 :
1976 : void
1977 0 : CSSParserImpl::ProcessImport(const nsString& aURLSpec,
1978 : nsMediaList* aMedia,
1979 : RuleAppendFunc aAppendFunc,
1980 : void* aData)
1981 : {
1982 0 : nsRefPtr<css::ImportRule> rule = new css::ImportRule(aMedia, aURLSpec);
1983 0 : (*aAppendFunc)(rule, aData);
1984 :
1985 : // Diagnose bad URIs even if we don't have a child loader.
1986 0 : nsCOMPtr<nsIURI> url;
1987 : // Charset will be deduced from mBaseURI, which is more or less correct.
1988 0 : nsresult rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nsnull, mBaseURI);
1989 :
1990 0 : if (NS_FAILED(rv)) {
1991 0 : if (rv == NS_ERROR_MALFORMED_URI) {
1992 : // import url is bad
1993 : const PRUnichar *params[] = {
1994 0 : aURLSpec.get()
1995 0 : };
1996 0 : REPORT_UNEXPECTED_P(PEImportBadURI, params);
1997 0 : OUTPUT_ERROR();
1998 : }
1999 : return;
2000 : }
2001 :
2002 0 : if (mChildLoader) {
2003 0 : mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule);
2004 : }
2005 : }
2006 :
2007 : // Parse the {} part of an @media or @-moz-document rule.
2008 : bool
2009 0 : CSSParserImpl::ParseGroupRule(css::GroupRule* aRule,
2010 : RuleAppendFunc aAppendFunc,
2011 : void* aData)
2012 : {
2013 : // XXXbz this could use better error reporting throughout the method
2014 0 : if (!ExpectSymbol('{', true)) {
2015 0 : return false;
2016 : }
2017 :
2018 : // push rule on stack, loop over children
2019 0 : PushGroup(aRule);
2020 0 : nsCSSSection holdSection = mSection;
2021 0 : mSection = eCSSSection_General;
2022 :
2023 0 : for (;;) {
2024 : // Get next non-whitespace token
2025 0 : if (! GetToken(true)) {
2026 0 : REPORT_UNEXPECTED_EOF(PEGroupRuleEOF);
2027 0 : break;
2028 : }
2029 0 : if (mToken.IsSymbol('}')) { // done!
2030 0 : UngetToken();
2031 0 : break;
2032 : }
2033 0 : if (eCSSToken_AtKeyword == mToken.mType) {
2034 : // Parse for nested rules
2035 0 : ParseAtRule(aAppendFunc, aData, true);
2036 0 : continue;
2037 : }
2038 0 : UngetToken();
2039 0 : ParseRuleSet(AppendRuleToSheet, this, true);
2040 : }
2041 0 : PopGroup();
2042 :
2043 0 : if (!ExpectSymbol('}', true)) {
2044 0 : mSection = holdSection;
2045 0 : return false;
2046 : }
2047 0 : (*aAppendFunc)(aRule, aData);
2048 0 : return true;
2049 : }
2050 :
2051 : // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
2052 : bool
2053 0 : CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData)
2054 : {
2055 0 : nsRefPtr<nsMediaList> media = new nsMediaList();
2056 :
2057 0 : if (GatherMedia(media, true)) {
2058 : // XXXbz this could use better error reporting throughout the method
2059 0 : nsRefPtr<css::MediaRule> rule = new css::MediaRule();
2060 : // Append first, so when we do SetMedia() the rule
2061 : // knows what its stylesheet is.
2062 0 : if (ParseGroupRule(rule, aAppendFunc, aData)) {
2063 0 : rule->SetMedia(media);
2064 0 : return true;
2065 : }
2066 : }
2067 :
2068 0 : return false;
2069 : }
2070 :
2071 : // Parse a @-moz-document rule. This is like an @media rule, but instead
2072 : // of a medium it has a nonempty list of items where each item is either
2073 : // url(), url-prefix(), or domain().
2074 : bool
2075 0 : CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData)
2076 : {
2077 0 : css::DocumentRule::URL *urls = nsnull;
2078 0 : css::DocumentRule::URL **next = &urls;
2079 0 : do {
2080 0 : if (!GetToken(true)) {
2081 0 : REPORT_UNEXPECTED_EOF(PEMozDocRuleEOF);
2082 0 : delete urls;
2083 0 : return false;
2084 : }
2085 :
2086 0 : if (!(eCSSToken_URL == mToken.mType ||
2087 : (eCSSToken_Function == mToken.mType &&
2088 0 : (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
2089 0 : mToken.mIdent.LowerCaseEqualsLiteral("domain") ||
2090 0 : mToken.mIdent.LowerCaseEqualsLiteral("regexp"))))) {
2091 0 : REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc);
2092 0 : UngetToken();
2093 0 : delete urls;
2094 0 : return false;
2095 : }
2096 0 : css::DocumentRule::URL *cur = *next = new css::DocumentRule::URL;
2097 0 : next = &cur->next;
2098 0 : if (mToken.mType == eCSSToken_URL) {
2099 0 : cur->func = css::DocumentRule::eURL;
2100 0 : CopyUTF16toUTF8(mToken.mIdent, cur->url);
2101 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("regexp")) {
2102 : // regexp() is different from url-prefix() and domain() (but
2103 : // probably the way they *should* have been* in that it requires a
2104 : // string argument, and doesn't try to behave like url().
2105 0 : cur->func = css::DocumentRule::eRegExp;
2106 0 : GetToken(true);
2107 : // copy before we know it's valid (but before ExpectSymbol changes
2108 : // mToken.mIdent)
2109 0 : CopyUTF16toUTF8(mToken.mIdent, cur->url);
2110 0 : if (eCSSToken_String != mToken.mType || !ExpectSymbol(')', true)) {
2111 0 : REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotString);
2112 0 : SkipUntil(')');
2113 0 : delete urls;
2114 0 : return false;
2115 : }
2116 : } else {
2117 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
2118 0 : cur->func = css::DocumentRule::eURLPrefix;
2119 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
2120 0 : cur->func = css::DocumentRule::eDomain;
2121 : }
2122 :
2123 0 : nsAutoString url;
2124 0 : if (!GetURLInParens(url)) {
2125 0 : REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
2126 0 : delete urls;
2127 0 : return false;
2128 : }
2129 :
2130 : // We could try to make the URL (as long as it's not domain())
2131 : // canonical and absolute with NS_NewURI and GetSpec, but I'm
2132 : // inclined to think we shouldn't.
2133 0 : CopyUTF16toUTF8(url, cur->url);
2134 : }
2135 : } while (ExpectSymbol(',', true));
2136 :
2137 0 : nsRefPtr<css::DocumentRule> rule = new css::DocumentRule();
2138 0 : rule->SetURLs(urls);
2139 :
2140 0 : return ParseGroupRule(rule, aAppendFunc, aData);
2141 : }
2142 :
2143 : // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
2144 : bool
2145 0 : CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData)
2146 : {
2147 0 : if (!GetToken(true)) {
2148 0 : REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
2149 0 : return false;
2150 : }
2151 :
2152 0 : nsAutoString prefix;
2153 0 : nsAutoString url;
2154 :
2155 0 : if (eCSSToken_Ident == mToken.mType) {
2156 0 : prefix = mToken.mIdent;
2157 : // user-specified identifiers are case-sensitive (bug 416106)
2158 : } else {
2159 0 : UngetToken();
2160 : }
2161 :
2162 0 : if (!ParseURLOrString(url) || !ExpectSymbol(';', true)) {
2163 0 : if (mHavePushBack) {
2164 0 : REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
2165 : } else {
2166 0 : REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
2167 : }
2168 0 : return false;
2169 : }
2170 :
2171 0 : ProcessNameSpace(prefix, url, aAppendFunc, aData);
2172 0 : return true;
2173 : }
2174 :
2175 : void
2176 0 : CSSParserImpl::ProcessNameSpace(const nsString& aPrefix,
2177 : const nsString& aURLSpec,
2178 : RuleAppendFunc aAppendFunc,
2179 : void* aData)
2180 : {
2181 0 : nsCOMPtr<nsIAtom> prefix;
2182 :
2183 0 : if (!aPrefix.IsEmpty()) {
2184 0 : prefix = do_GetAtom(aPrefix);
2185 0 : if (!prefix) {
2186 0 : NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
2187 : }
2188 : }
2189 :
2190 0 : nsRefPtr<css::NameSpaceRule> rule = new css::NameSpaceRule(prefix, aURLSpec);
2191 0 : (*aAppendFunc)(rule, aData);
2192 :
2193 : // If this was the first namespace rule encountered, it will trigger
2194 : // creation of a namespace map.
2195 0 : if (!mNameSpaceMap) {
2196 0 : mNameSpaceMap = mSheet->GetNameSpaceMap();
2197 : }
2198 0 : }
2199 :
2200 : // font-face-rule: '@font-face' '{' font-description '}'
2201 : // font-description: font-descriptor+
2202 : bool
2203 0 : CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData)
2204 : {
2205 0 : if (!ExpectSymbol('{', true)) {
2206 0 : REPORT_UNEXPECTED_TOKEN(PEBadFontBlockStart);
2207 0 : return false;
2208 : }
2209 :
2210 0 : nsRefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule());
2211 :
2212 0 : for (;;) {
2213 0 : if (!GetToken(true)) {
2214 0 : REPORT_UNEXPECTED_EOF(PEFontFaceEOF);
2215 0 : break;
2216 : }
2217 0 : if (mToken.IsSymbol('}')) { // done!
2218 0 : UngetToken();
2219 0 : break;
2220 : }
2221 :
2222 : // ignore extra semicolons
2223 0 : if (mToken.IsSymbol(';'))
2224 0 : continue;
2225 :
2226 0 : if (!ParseFontDescriptor(rule)) {
2227 0 : REPORT_UNEXPECTED(PEDeclSkipped);
2228 0 : OUTPUT_ERROR();
2229 0 : if (!SkipDeclaration(true))
2230 0 : break;
2231 : }
2232 : }
2233 0 : if (!ExpectSymbol('}', true)) {
2234 0 : REPORT_UNEXPECTED_TOKEN(PEBadFontBlockEnd);
2235 0 : return false;
2236 : }
2237 0 : (*aAppendFunc)(rule, aData);
2238 0 : return true;
2239 : }
2240 :
2241 : // font-descriptor: font-family-desc
2242 : // | font-style-desc
2243 : // | font-weight-desc
2244 : // | font-stretch-desc
2245 : // | font-src-desc
2246 : // | unicode-range-desc
2247 : //
2248 : // All font-*-desc productions follow the pattern
2249 : // IDENT ':' value ';'
2250 : //
2251 : // On entry to this function, mToken is the IDENT.
2252 :
2253 : bool
2254 0 : CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule)
2255 : {
2256 0 : if (eCSSToken_Ident != mToken.mType) {
2257 0 : REPORT_UNEXPECTED_TOKEN(PEFontDescExpected);
2258 0 : return false;
2259 : }
2260 :
2261 0 : nsString descName = mToken.mIdent;
2262 0 : if (!ExpectSymbol(':', true)) {
2263 0 : REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
2264 0 : OUTPUT_ERROR();
2265 0 : return false;
2266 : }
2267 :
2268 0 : nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName);
2269 0 : nsCSSValue value;
2270 :
2271 0 : if (descID == eCSSFontDesc_UNKNOWN) {
2272 0 : if (NonMozillaVendorIdentifier(descName)) {
2273 : // silently skip other vendors' extensions
2274 0 : SkipDeclaration(true);
2275 0 : return true;
2276 : } else {
2277 : const PRUnichar *params[] = {
2278 0 : descName.get()
2279 0 : };
2280 0 : REPORT_UNEXPECTED_P(PEUnknownFontDesc, params);
2281 0 : return false;
2282 : }
2283 : }
2284 :
2285 0 : if (!ParseFontDescriptorValue(descID, value)) {
2286 : const PRUnichar *params[] = {
2287 0 : descName.get()
2288 0 : };
2289 0 : REPORT_UNEXPECTED_P(PEValueParsingError, params);
2290 0 : return false;
2291 : }
2292 :
2293 0 : if (!ExpectEndProperty())
2294 0 : return false;
2295 :
2296 0 : aRule->SetDesc(descID, value);
2297 0 : return true;
2298 : }
2299 :
2300 :
2301 : bool
2302 0 : CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData)
2303 : {
2304 : // XXX not yet implemented
2305 0 : return false;
2306 : }
2307 :
2308 : bool
2309 0 : CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aData)
2310 : {
2311 0 : if (!GetToken(true)) {
2312 0 : REPORT_UNEXPECTED_EOF(PEKeyframeNameEOF);
2313 0 : return false;
2314 : }
2315 :
2316 0 : if (mToken.mType != eCSSToken_Ident) {
2317 0 : REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName);
2318 0 : UngetToken();
2319 0 : return false;
2320 : }
2321 0 : nsString name(mToken.mIdent);
2322 :
2323 0 : if (!ExpectSymbol('{', true)) {
2324 0 : REPORT_UNEXPECTED_TOKEN(PEKeyframeBrace);
2325 0 : return false;
2326 : }
2327 :
2328 0 : nsRefPtr<nsCSSKeyframesRule> rule = new nsCSSKeyframesRule(name);
2329 :
2330 0 : while (!ExpectSymbol('}', true)) {
2331 0 : nsRefPtr<nsCSSKeyframeRule> kid = ParseKeyframeRule();
2332 0 : if (kid) {
2333 0 : rule->AppendStyleRule(kid);
2334 : } else {
2335 0 : OUTPUT_ERROR();
2336 0 : SkipRuleSet(true);
2337 : }
2338 : }
2339 :
2340 0 : (*aAppendFunc)(rule, aData);
2341 0 : return true;
2342 : }
2343 :
2344 : already_AddRefed<nsCSSKeyframeRule>
2345 0 : CSSParserImpl::ParseKeyframeRule()
2346 : {
2347 0 : InfallibleTArray<float> selectorList;
2348 0 : if (!ParseKeyframeSelectorList(selectorList)) {
2349 0 : REPORT_UNEXPECTED(PEBadSelectorKeyframeRuleIgnored);
2350 0 : return nsnull;
2351 : }
2352 :
2353 0 : nsAutoPtr<css::Declaration> declaration(ParseDeclarationBlock(true));
2354 0 : if (!declaration) {
2355 0 : REPORT_UNEXPECTED(PEBadSelectorKeyframeRuleIgnored);
2356 0 : return nsnull;
2357 : }
2358 :
2359 : // Takes ownership of declaration, and steals contents of selectorList.
2360 : nsRefPtr<nsCSSKeyframeRule> rule =
2361 0 : new nsCSSKeyframeRule(selectorList, declaration);
2362 :
2363 0 : return rule.forget();
2364 : }
2365 :
2366 : bool
2367 0 : CSSParserImpl::ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList)
2368 : {
2369 0 : for (;;) {
2370 0 : if (!GetToken(true)) {
2371 : // The first time through the loop, this means we got an empty
2372 : // list. Otherwise, it means we have a trailing comma.
2373 0 : return false;
2374 : }
2375 : float value;
2376 0 : switch (mToken.mType) {
2377 : case eCSSToken_Percentage:
2378 0 : value = mToken.mNumber;
2379 0 : break;
2380 : case eCSSToken_Ident:
2381 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
2382 0 : value = 0.0f;
2383 0 : break;
2384 : }
2385 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
2386 0 : value = 1.0f;
2387 0 : break;
2388 : }
2389 : // fall through
2390 : default:
2391 0 : UngetToken();
2392 : // The first time through the loop, this means we got an empty
2393 : // list. Otherwise, it means we have a trailing comma.
2394 0 : return false;
2395 : }
2396 0 : aSelectorList.AppendElement(value);
2397 0 : if (!ExpectSymbol(',', true)) {
2398 0 : return true;
2399 : }
2400 : }
2401 : }
2402 :
2403 : void
2404 0 : CSSParserImpl::SkipUntil(PRUnichar aStopSymbol)
2405 : {
2406 0 : nsCSSToken* tk = &mToken;
2407 0 : nsAutoTArray<PRUnichar, 16> stack;
2408 0 : stack.AppendElement(aStopSymbol);
2409 0 : for (;;) {
2410 0 : if (!GetToken(true)) {
2411 0 : break;
2412 : }
2413 0 : if (eCSSToken_Symbol == tk->mType) {
2414 0 : PRUnichar symbol = tk->mSymbol;
2415 0 : PRUint32 stackTopIndex = stack.Length() - 1;
2416 0 : if (symbol == stack.ElementAt(stackTopIndex)) {
2417 0 : stack.RemoveElementAt(stackTopIndex);
2418 0 : if (stackTopIndex == 0) {
2419 0 : break;
2420 : }
2421 :
2422 : // Just handle out-of-memory by parsing incorrectly. It's
2423 : // highly unlikely we're dealing with a legitimate style sheet
2424 : // anyway.
2425 0 : } else if ('{' == symbol) {
2426 0 : stack.AppendElement('}');
2427 0 : } else if ('[' == symbol) {
2428 0 : stack.AppendElement(']');
2429 0 : } else if ('(' == symbol) {
2430 0 : stack.AppendElement(')');
2431 : }
2432 0 : } else if (eCSSToken_Function == tk->mType ||
2433 : eCSSToken_Bad_URL == tk->mType) {
2434 0 : stack.AppendElement(')');
2435 : }
2436 : }
2437 0 : }
2438 :
2439 : void
2440 0 : CSSParserImpl::SkipUntilOneOf(const PRUnichar* aStopSymbolChars)
2441 : {
2442 0 : nsCSSToken* tk = &mToken;
2443 0 : nsDependentString stopSymbolChars(aStopSymbolChars);
2444 0 : for (;;) {
2445 0 : if (!GetToken(true)) {
2446 0 : break;
2447 : }
2448 0 : if (eCSSToken_Symbol == tk->mType) {
2449 0 : PRUnichar symbol = tk->mSymbol;
2450 0 : if (stopSymbolChars.FindChar(symbol) != -1) {
2451 0 : break;
2452 0 : } else if ('{' == symbol) {
2453 0 : SkipUntil('}');
2454 0 : } else if ('[' == symbol) {
2455 0 : SkipUntil(']');
2456 0 : } else if ('(' == symbol) {
2457 0 : SkipUntil(')');
2458 : }
2459 0 : } else if (eCSSToken_Function == tk->mType ||
2460 : eCSSToken_Bad_URL == tk->mType) {
2461 0 : SkipUntil(')');
2462 : }
2463 : }
2464 0 : }
2465 :
2466 : bool
2467 0 : CSSParserImpl::SkipDeclaration(bool aCheckForBraces)
2468 : {
2469 0 : nsCSSToken* tk = &mToken;
2470 0 : for (;;) {
2471 0 : if (!GetToken(true)) {
2472 0 : if (aCheckForBraces) {
2473 0 : REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
2474 : }
2475 0 : return false;
2476 : }
2477 0 : if (eCSSToken_Symbol == tk->mType) {
2478 0 : PRUnichar symbol = tk->mSymbol;
2479 0 : if (';' == symbol) {
2480 0 : break;
2481 : }
2482 0 : if (aCheckForBraces) {
2483 0 : if ('}' == symbol) {
2484 0 : UngetToken();
2485 0 : break;
2486 : }
2487 : }
2488 0 : if ('{' == symbol) {
2489 0 : SkipUntil('}');
2490 0 : } else if ('(' == symbol) {
2491 0 : SkipUntil(')');
2492 0 : } else if ('[' == symbol) {
2493 0 : SkipUntil(']');
2494 : }
2495 0 : } else if (eCSSToken_Function == tk->mType ||
2496 : eCSSToken_Bad_URL == tk->mType) {
2497 0 : SkipUntil(')');
2498 : }
2499 : }
2500 0 : return true;
2501 : }
2502 :
2503 : void
2504 0 : CSSParserImpl::SkipRuleSet(bool aInsideBraces)
2505 : {
2506 0 : nsCSSToken* tk = &mToken;
2507 0 : for (;;) {
2508 0 : if (!GetToken(true)) {
2509 0 : REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
2510 0 : break;
2511 : }
2512 0 : if (eCSSToken_Symbol == tk->mType) {
2513 0 : PRUnichar symbol = tk->mSymbol;
2514 0 : if ('}' == symbol && aInsideBraces) {
2515 : // leave block closer for higher-level grammar to consume
2516 0 : UngetToken();
2517 0 : break;
2518 0 : } else if ('{' == symbol) {
2519 0 : SkipUntil('}');
2520 0 : break;
2521 0 : } else if ('(' == symbol) {
2522 0 : SkipUntil(')');
2523 0 : } else if ('[' == symbol) {
2524 0 : SkipUntil(']');
2525 : }
2526 0 : } else if (eCSSToken_Function == tk->mType ||
2527 : eCSSToken_Bad_URL == tk->mType) {
2528 0 : SkipUntil(')');
2529 : }
2530 : }
2531 0 : }
2532 :
2533 : void
2534 0 : CSSParserImpl::PushGroup(css::GroupRule* aRule)
2535 : {
2536 0 : mGroupStack.AppendElement(aRule);
2537 0 : }
2538 :
2539 : void
2540 0 : CSSParserImpl::PopGroup()
2541 : {
2542 0 : PRUint32 count = mGroupStack.Length();
2543 0 : if (0 < count) {
2544 0 : mGroupStack.RemoveElementAt(count - 1);
2545 : }
2546 0 : }
2547 :
2548 : void
2549 0 : CSSParserImpl::AppendRule(css::Rule* aRule)
2550 : {
2551 0 : PRUint32 count = mGroupStack.Length();
2552 0 : if (0 < count) {
2553 0 : mGroupStack[count - 1]->AppendStyleRule(aRule);
2554 : }
2555 : else {
2556 0 : mSheet->AppendStyleRule(aRule);
2557 : }
2558 0 : }
2559 :
2560 : bool
2561 0 : CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData,
2562 : bool aInsideBraces)
2563 : {
2564 : // First get the list of selectors for the rule
2565 0 : nsCSSSelectorList* slist = nsnull;
2566 0 : PRUint32 linenum = mScanner.GetLineNumber();
2567 0 : if (! ParseSelectorList(slist, PRUnichar('{'))) {
2568 0 : REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
2569 0 : OUTPUT_ERROR();
2570 0 : SkipRuleSet(aInsideBraces);
2571 0 : return false;
2572 : }
2573 0 : NS_ASSERTION(nsnull != slist, "null selector list");
2574 0 : CLEAR_ERROR();
2575 :
2576 : // Next parse the declaration block
2577 0 : css::Declaration* declaration = ParseDeclarationBlock(true);
2578 0 : if (nsnull == declaration) {
2579 0 : delete slist;
2580 0 : return false;
2581 : }
2582 :
2583 : #if 0
2584 : slist->Dump();
2585 : fputs("{\n", stdout);
2586 : declaration->List();
2587 : fputs("}\n", stdout);
2588 : #endif
2589 :
2590 : // Translate the selector list and declaration block into style data
2591 :
2592 0 : nsRefPtr<css::StyleRule> rule = new css::StyleRule(slist, declaration);
2593 0 : rule->SetLineNumber(linenum);
2594 0 : (*aAppendFunc)(rule, aData);
2595 :
2596 0 : return true;
2597 : }
2598 :
2599 : bool
2600 110 : CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
2601 : PRUnichar aStopChar)
2602 : {
2603 110 : nsCSSSelectorList* list = nsnull;
2604 110 : if (! ParseSelectorGroup(list)) {
2605 : // must have at least one selector group
2606 0 : aListHead = nsnull;
2607 0 : return false;
2608 : }
2609 110 : NS_ASSERTION(nsnull != list, "no selector list");
2610 110 : aListHead = list;
2611 :
2612 : // After that there must either be a "," or a "{" (the latter if
2613 : // StopChar is nonzero)
2614 110 : nsCSSToken* tk = &mToken;
2615 0 : for (;;) {
2616 110 : if (! GetToken(true)) {
2617 110 : if (aStopChar == PRUnichar(0)) {
2618 110 : return true;
2619 : }
2620 :
2621 0 : REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
2622 0 : break;
2623 : }
2624 :
2625 0 : if (eCSSToken_Symbol == tk->mType) {
2626 0 : if (',' == tk->mSymbol) {
2627 0 : nsCSSSelectorList* newList = nsnull;
2628 : // Another selector group must follow
2629 0 : if (! ParseSelectorGroup(newList)) {
2630 0 : break;
2631 : }
2632 : // add new list to the end of the selector list
2633 0 : list->mNext = newList;
2634 0 : list = newList;
2635 0 : continue;
2636 0 : } else if (aStopChar == tk->mSymbol && aStopChar != PRUnichar(0)) {
2637 0 : UngetToken();
2638 0 : return true;
2639 : }
2640 : }
2641 0 : REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
2642 0 : UngetToken();
2643 0 : break;
2644 : }
2645 :
2646 0 : delete aListHead;
2647 0 : aListHead = nsnull;
2648 0 : return false;
2649 : }
2650 :
2651 0 : static bool IsUniversalSelector(const nsCSSSelector& aSelector)
2652 : {
2653 : return bool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
2654 0 : (aSelector.mLowercaseTag == nsnull) &&
2655 : (aSelector.mIDList == nsnull) &&
2656 : (aSelector.mClassList == nsnull) &&
2657 : (aSelector.mAttrList == nsnull) &&
2658 : (aSelector.mNegations == nsnull) &&
2659 0 : (aSelector.mPseudoClassList == nsnull));
2660 : }
2661 :
2662 : bool
2663 110 : CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList)
2664 : {
2665 110 : PRUnichar combinator = 0;
2666 220 : nsAutoPtr<nsCSSSelectorList> list(new nsCSSSelectorList());
2667 :
2668 110 : for (;;) {
2669 220 : if (!ParseSelector(list, combinator)) {
2670 0 : return false;
2671 : }
2672 :
2673 : // Look for a combinator.
2674 220 : if (!GetToken(false)) {
2675 110 : break; // EOF ok here
2676 : }
2677 :
2678 110 : combinator = PRUnichar(0);
2679 110 : if (mToken.mType == eCSSToken_WhiteSpace) {
2680 110 : if (!GetToken(true)) {
2681 0 : break; // EOF ok here
2682 : }
2683 110 : combinator = PRUnichar(' ');
2684 : }
2685 :
2686 110 : if (mToken.mType != eCSSToken_Symbol) {
2687 0 : UngetToken(); // not a combinator
2688 : } else {
2689 110 : PRUnichar symbol = mToken.mSymbol;
2690 110 : if (symbol == '+' || symbol == '>' || symbol == '~') {
2691 110 : combinator = mToken.mSymbol;
2692 : } else {
2693 0 : UngetToken(); // not a combinator
2694 0 : if (symbol == ',' || symbol == '{' || symbol == ')') {
2695 0 : break; // end of selector group
2696 : }
2697 : }
2698 : }
2699 :
2700 110 : if (!combinator) {
2701 0 : REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
2702 0 : return false;
2703 : }
2704 : }
2705 :
2706 110 : aList = list.forget();
2707 110 : return true;
2708 : }
2709 :
2710 : #define SEL_MASK_NSPACE 0x01
2711 : #define SEL_MASK_ELEM 0x02
2712 : #define SEL_MASK_ID 0x04
2713 : #define SEL_MASK_CLASS 0x08
2714 : #define SEL_MASK_ATTRIB 0x10
2715 : #define SEL_MASK_PCLASS 0x20
2716 : #define SEL_MASK_PELEM 0x40
2717 :
2718 : //
2719 : // Parses an ID selector #name
2720 : //
2721 : CSSParserImpl::nsSelectorParsingStatus
2722 0 : CSSParserImpl::ParseIDSelector(PRInt32& aDataMask,
2723 : nsCSSSelector& aSelector)
2724 : {
2725 0 : NS_ASSERTION(!mToken.mIdent.IsEmpty(),
2726 : "Empty mIdent in eCSSToken_ID token?");
2727 0 : aDataMask |= SEL_MASK_ID;
2728 0 : aSelector.AddID(mToken.mIdent);
2729 0 : return eSelectorParsingStatus_Continue;
2730 : }
2731 :
2732 : //
2733 : // Parses a class selector .name
2734 : //
2735 : CSSParserImpl::nsSelectorParsingStatus
2736 0 : CSSParserImpl::ParseClassSelector(PRInt32& aDataMask,
2737 : nsCSSSelector& aSelector)
2738 : {
2739 0 : if (! GetToken(false)) { // get ident
2740 0 : REPORT_UNEXPECTED_EOF(PEClassSelEOF);
2741 0 : return eSelectorParsingStatus_Error;
2742 : }
2743 0 : if (eCSSToken_Ident != mToken.mType) { // malformed selector
2744 0 : REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
2745 0 : UngetToken();
2746 0 : return eSelectorParsingStatus_Error;
2747 : }
2748 0 : aDataMask |= SEL_MASK_CLASS;
2749 :
2750 0 : aSelector.AddClass(mToken.mIdent);
2751 :
2752 0 : return eSelectorParsingStatus_Continue;
2753 : }
2754 :
2755 : //
2756 : // Parse a type element selector or a universal selector
2757 : // namespace|type or namespace|* or *|* or *
2758 : //
2759 : CSSParserImpl::nsSelectorParsingStatus
2760 220 : CSSParserImpl::ParseTypeOrUniversalSelector(PRInt32& aDataMask,
2761 : nsCSSSelector& aSelector,
2762 : bool aIsNegated)
2763 : {
2764 440 : nsAutoString buffer;
2765 220 : if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace
2766 0 : if (ExpectSymbol('|', false)) { // was namespace
2767 0 : aDataMask |= SEL_MASK_NSPACE;
2768 0 : aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
2769 :
2770 0 : if (! GetToken(false)) {
2771 0 : REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2772 0 : return eSelectorParsingStatus_Error;
2773 : }
2774 0 : if (eCSSToken_Ident == mToken.mType) { // element name
2775 0 : aDataMask |= SEL_MASK_ELEM;
2776 :
2777 0 : aSelector.SetTag(mToken.mIdent);
2778 : }
2779 0 : else if (mToken.IsSymbol('*')) { // universal selector
2780 0 : aDataMask |= SEL_MASK_ELEM;
2781 : // don't set tag
2782 : }
2783 : else {
2784 0 : REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2785 0 : UngetToken();
2786 0 : return eSelectorParsingStatus_Error;
2787 : }
2788 : }
2789 : else { // was universal element selector
2790 0 : SetDefaultNamespaceOnSelector(aSelector);
2791 0 : aDataMask |= SEL_MASK_ELEM;
2792 : // don't set any tag in the selector
2793 : }
2794 0 : if (! GetToken(false)) { // premature eof is ok (here!)
2795 0 : return eSelectorParsingStatus_Done;
2796 : }
2797 : }
2798 220 : else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name
2799 220 : buffer = mToken.mIdent; // hang on to ident
2800 :
2801 220 : if (ExpectSymbol('|', false)) { // was namespace
2802 0 : aDataMask |= SEL_MASK_NSPACE;
2803 0 : PRInt32 nameSpaceID = GetNamespaceIdForPrefix(buffer);
2804 0 : if (nameSpaceID == kNameSpaceID_Unknown) {
2805 0 : return eSelectorParsingStatus_Error;
2806 : }
2807 0 : aSelector.SetNameSpace(nameSpaceID);
2808 :
2809 0 : if (! GetToken(false)) {
2810 0 : REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2811 0 : return eSelectorParsingStatus_Error;
2812 : }
2813 0 : if (eCSSToken_Ident == mToken.mType) { // element name
2814 0 : aDataMask |= SEL_MASK_ELEM;
2815 0 : aSelector.SetTag(mToken.mIdent);
2816 : }
2817 0 : else if (mToken.IsSymbol('*')) { // universal selector
2818 0 : aDataMask |= SEL_MASK_ELEM;
2819 : // don't set tag
2820 : }
2821 : else {
2822 0 : REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2823 0 : UngetToken();
2824 0 : return eSelectorParsingStatus_Error;
2825 : }
2826 : }
2827 : else { // was element name
2828 220 : SetDefaultNamespaceOnSelector(aSelector);
2829 220 : aSelector.SetTag(buffer);
2830 :
2831 220 : aDataMask |= SEL_MASK_ELEM;
2832 : }
2833 220 : if (! GetToken(false)) { // premature eof is ok (here!)
2834 110 : return eSelectorParsingStatus_Done;
2835 : }
2836 : }
2837 0 : else if (mToken.IsSymbol('|')) { // No namespace
2838 0 : aDataMask |= SEL_MASK_NSPACE;
2839 0 : aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace
2840 :
2841 : // get mandatory tag
2842 0 : if (! GetToken(false)) {
2843 0 : REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2844 0 : return eSelectorParsingStatus_Error;
2845 : }
2846 0 : if (eCSSToken_Ident == mToken.mType) { // element name
2847 0 : aDataMask |= SEL_MASK_ELEM;
2848 0 : aSelector.SetTag(mToken.mIdent);
2849 : }
2850 0 : else if (mToken.IsSymbol('*')) { // universal selector
2851 0 : aDataMask |= SEL_MASK_ELEM;
2852 : // don't set tag
2853 : }
2854 : else {
2855 0 : REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2856 0 : UngetToken();
2857 0 : return eSelectorParsingStatus_Error;
2858 : }
2859 0 : if (! GetToken(false)) { // premature eof is ok (here!)
2860 0 : return eSelectorParsingStatus_Done;
2861 : }
2862 : }
2863 : else {
2864 0 : SetDefaultNamespaceOnSelector(aSelector);
2865 : }
2866 :
2867 110 : if (aIsNegated) {
2868 : // restore last token read in case of a negated type selector
2869 0 : UngetToken();
2870 : }
2871 110 : return eSelectorParsingStatus_Continue;
2872 : }
2873 :
2874 : //
2875 : // Parse attribute selectors [attr], [attr=value], [attr|=value],
2876 : // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
2877 : //
2878 : CSSParserImpl::nsSelectorParsingStatus
2879 0 : CSSParserImpl::ParseAttributeSelector(PRInt32& aDataMask,
2880 : nsCSSSelector& aSelector)
2881 : {
2882 0 : if (! GetToken(true)) { // premature EOF
2883 0 : REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2884 0 : return eSelectorParsingStatus_Error;
2885 : }
2886 :
2887 0 : PRInt32 nameSpaceID = kNameSpaceID_None;
2888 0 : nsAutoString attr;
2889 0 : if (mToken.IsSymbol('*')) { // wildcard namespace
2890 0 : nameSpaceID = kNameSpaceID_Unknown;
2891 0 : if (ExpectSymbol('|', false)) {
2892 0 : if (! GetToken(false)) { // premature EOF
2893 0 : REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2894 0 : return eSelectorParsingStatus_Error;
2895 : }
2896 0 : if (eCSSToken_Ident == mToken.mType) { // attr name
2897 0 : attr = mToken.mIdent;
2898 : }
2899 : else {
2900 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2901 0 : UngetToken();
2902 0 : return eSelectorParsingStatus_Error;
2903 : }
2904 : }
2905 : else {
2906 0 : REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
2907 0 : return eSelectorParsingStatus_Error;
2908 : }
2909 : }
2910 0 : else if (mToken.IsSymbol('|')) { // NO namespace
2911 0 : if (! GetToken(false)) { // premature EOF
2912 0 : REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2913 0 : return eSelectorParsingStatus_Error;
2914 : }
2915 0 : if (eCSSToken_Ident == mToken.mType) { // attr name
2916 0 : attr = mToken.mIdent;
2917 : }
2918 : else {
2919 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2920 0 : UngetToken();
2921 0 : return eSelectorParsingStatus_Error;
2922 : }
2923 : }
2924 0 : else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
2925 0 : attr = mToken.mIdent; // hang on to it
2926 0 : if (ExpectSymbol('|', false)) { // was a namespace
2927 0 : nameSpaceID = GetNamespaceIdForPrefix(attr);
2928 0 : if (nameSpaceID == kNameSpaceID_Unknown) {
2929 0 : return eSelectorParsingStatus_Error;
2930 : }
2931 0 : if (! GetToken(false)) { // premature EOF
2932 0 : REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2933 0 : return eSelectorParsingStatus_Error;
2934 : }
2935 0 : if (eCSSToken_Ident == mToken.mType) { // attr name
2936 0 : attr = mToken.mIdent;
2937 : }
2938 : else {
2939 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2940 0 : UngetToken();
2941 0 : return eSelectorParsingStatus_Error;
2942 : }
2943 : }
2944 : }
2945 : else { // malformed
2946 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
2947 0 : UngetToken();
2948 0 : return eSelectorParsingStatus_Error;
2949 : }
2950 :
2951 0 : if (! GetToken(true)) { // premature EOF
2952 0 : REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
2953 0 : return eSelectorParsingStatus_Error;
2954 : }
2955 0 : if ((eCSSToken_Symbol == mToken.mType) ||
2956 : (eCSSToken_Includes == mToken.mType) ||
2957 : (eCSSToken_Dashmatch == mToken.mType) ||
2958 : (eCSSToken_Beginsmatch == mToken.mType) ||
2959 : (eCSSToken_Endsmatch == mToken.mType) ||
2960 : (eCSSToken_Containsmatch == mToken.mType)) {
2961 : PRUint8 func;
2962 0 : if (eCSSToken_Includes == mToken.mType) {
2963 0 : func = NS_ATTR_FUNC_INCLUDES;
2964 : }
2965 0 : else if (eCSSToken_Dashmatch == mToken.mType) {
2966 0 : func = NS_ATTR_FUNC_DASHMATCH;
2967 : }
2968 0 : else if (eCSSToken_Beginsmatch == mToken.mType) {
2969 0 : func = NS_ATTR_FUNC_BEGINSMATCH;
2970 : }
2971 0 : else if (eCSSToken_Endsmatch == mToken.mType) {
2972 0 : func = NS_ATTR_FUNC_ENDSMATCH;
2973 : }
2974 0 : else if (eCSSToken_Containsmatch == mToken.mType) {
2975 0 : func = NS_ATTR_FUNC_CONTAINSMATCH;
2976 : }
2977 0 : else if (']' == mToken.mSymbol) {
2978 0 : aDataMask |= SEL_MASK_ATTRIB;
2979 0 : aSelector.AddAttribute(nameSpaceID, attr);
2980 0 : func = NS_ATTR_FUNC_SET;
2981 : }
2982 0 : else if ('=' == mToken.mSymbol) {
2983 0 : func = NS_ATTR_FUNC_EQUALS;
2984 : }
2985 : else {
2986 0 : REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
2987 0 : UngetToken(); // bad function
2988 0 : return eSelectorParsingStatus_Error;
2989 : }
2990 0 : if (NS_ATTR_FUNC_SET != func) { // get value
2991 0 : if (! GetToken(true)) { // premature EOF
2992 0 : REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
2993 0 : return eSelectorParsingStatus_Error;
2994 : }
2995 0 : if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
2996 0 : nsAutoString value(mToken.mIdent);
2997 0 : if (! GetToken(true)) { // premature EOF
2998 0 : REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
2999 0 : return eSelectorParsingStatus_Error;
3000 : }
3001 0 : if (mToken.IsSymbol(']')) {
3002 0 : bool isCaseSensitive = true;
3003 :
3004 : // For cases when this style sheet is applied to an HTML
3005 : // element in an HTML document, and the attribute selector is
3006 : // for a non-namespaced attribute, then check to see if it's
3007 : // one of the known attributes whose VALUE is
3008 : // case-insensitive.
3009 0 : if (nameSpaceID == kNameSpaceID_None) {
3010 : static const char* caseInsensitiveHTMLAttribute[] = {
3011 : // list based on http://www.w3.org/TR/html4/
3012 : "lang",
3013 : "dir",
3014 : "http-equiv",
3015 : "text",
3016 : "link",
3017 : "vlink",
3018 : "alink",
3019 : "compact",
3020 : "align",
3021 : "frame",
3022 : "rules",
3023 : "valign",
3024 : "scope",
3025 : "axis",
3026 : "nowrap",
3027 : "hreflang",
3028 : "rel",
3029 : "rev",
3030 : "charset",
3031 : "codetype",
3032 : "declare",
3033 : "valuetype",
3034 : "shape",
3035 : "nohref",
3036 : "media",
3037 : "bgcolor",
3038 : "clear",
3039 : "color",
3040 : "face",
3041 : "noshade",
3042 : "noresize",
3043 : "scrolling",
3044 : "target",
3045 : "method",
3046 : "enctype",
3047 : "accept-charset",
3048 : "accept",
3049 : "checked",
3050 : "multiple",
3051 : "selected",
3052 : "disabled",
3053 : "readonly",
3054 : "language",
3055 : "defer",
3056 : "type",
3057 : // additional attributes not in HTML4
3058 : "direction", // marquee
3059 : nsnull
3060 : };
3061 0 : short i = 0;
3062 : const char* htmlAttr;
3063 0 : while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) {
3064 0 : if (attr.LowerCaseEqualsASCII(htmlAttr)) {
3065 0 : isCaseSensitive = false;
3066 0 : break;
3067 : }
3068 : }
3069 : }
3070 0 : aDataMask |= SEL_MASK_ATTRIB;
3071 0 : aSelector.AddAttribute(nameSpaceID, attr, func, value, isCaseSensitive);
3072 : }
3073 : else {
3074 0 : REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
3075 0 : UngetToken();
3076 0 : return eSelectorParsingStatus_Error;
3077 : }
3078 : }
3079 : else {
3080 0 : REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
3081 0 : UngetToken();
3082 0 : return eSelectorParsingStatus_Error;
3083 : }
3084 : }
3085 : }
3086 : else {
3087 0 : REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
3088 0 : UngetToken(); // bad dog, no biscut!
3089 0 : return eSelectorParsingStatus_Error;
3090 : }
3091 0 : return eSelectorParsingStatus_Continue;
3092 : }
3093 :
3094 : //
3095 : // Parse pseudo-classes and pseudo-elements
3096 : //
3097 : CSSParserImpl::nsSelectorParsingStatus
3098 0 : CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask,
3099 : nsCSSSelector& aSelector,
3100 : bool aIsNegated,
3101 : nsIAtom** aPseudoElement,
3102 : nsAtomList** aPseudoElementArgs,
3103 : nsCSSPseudoElements::Type* aPseudoElementType)
3104 : {
3105 0 : NS_ASSERTION(aIsNegated || (aPseudoElement && aPseudoElementArgs),
3106 : "expected location to store pseudo element");
3107 0 : NS_ASSERTION(!aIsNegated || (!aPseudoElement && !aPseudoElementArgs),
3108 : "negated selectors shouldn't have a place to store "
3109 : "pseudo elements");
3110 0 : if (! GetToken(false)) { // premature eof
3111 0 : REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
3112 0 : return eSelectorParsingStatus_Error;
3113 : }
3114 :
3115 : // First, find out whether we are parsing a CSS3 pseudo-element
3116 0 : bool parsingPseudoElement = false;
3117 0 : if (mToken.IsSymbol(':')) {
3118 0 : parsingPseudoElement = true;
3119 0 : if (! GetToken(false)) { // premature eof
3120 0 : REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
3121 0 : return eSelectorParsingStatus_Error;
3122 : }
3123 : }
3124 :
3125 : // Do some sanity-checking on the token
3126 0 : if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
3127 : // malformed selector
3128 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
3129 0 : UngetToken();
3130 0 : return eSelectorParsingStatus_Error;
3131 : }
3132 :
3133 : // OK, now we know we have an mIdent. Atomize it. All the atoms, for
3134 : // pseudo-classes as well as pseudo-elements, start with a single ':'.
3135 0 : nsAutoString buffer;
3136 0 : buffer.Append(PRUnichar(':'));
3137 0 : buffer.Append(mToken.mIdent);
3138 0 : nsContentUtils::ASCIIToLower(buffer);
3139 0 : nsCOMPtr<nsIAtom> pseudo = do_GetAtom(buffer);
3140 0 : if (!pseudo) {
3141 0 : NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
3142 : }
3143 :
3144 : // stash away some info about this pseudo so we only have to get it once.
3145 0 : bool isTreePseudo = false;
3146 : nsCSSPseudoElements::Type pseudoElementType =
3147 0 : nsCSSPseudoElements::GetPseudoType(pseudo);
3148 : #ifdef MOZ_XUL
3149 0 : isTreePseudo = (pseudoElementType == nsCSSPseudoElements::ePseudo_XULTree);
3150 : // If a tree pseudo-element is using the function syntax, it will
3151 : // get isTree set here and will pass the check below that only
3152 : // allows functions if they are in our list of things allowed to be
3153 : // functions. If it is _not_ using the function syntax, isTree will
3154 : // be false, and it will still pass that check. So the tree
3155 : // pseudo-elements are allowed to be either functions or not, as
3156 : // desired.
3157 0 : bool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
3158 : #endif
3159 : bool isPseudoElement =
3160 0 : (pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount);
3161 : // anonymous boxes are only allowed if they're the tree boxes or we have
3162 : // enabled unsafe rules
3163 : bool isAnonBox = isTreePseudo ||
3164 : (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox &&
3165 0 : mUnsafeRulesEnabled);
3166 : nsCSSPseudoClasses::Type pseudoClassType =
3167 0 : nsCSSPseudoClasses::GetPseudoType(pseudo);
3168 : bool isPseudoClass =
3169 0 : (pseudoClassType != nsCSSPseudoClasses::ePseudoClass_NotPseudoClass);
3170 :
3171 0 : NS_ASSERTION(!isPseudoClass ||
3172 : pseudoElementType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
3173 : "Why is this atom both a pseudo-class and a pseudo-element?");
3174 0 : NS_ASSERTION(isPseudoClass + isPseudoElement + isAnonBox <= 1,
3175 : "Shouldn't be more than one of these");
3176 :
3177 0 : if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
3178 : // Not a pseudo-class, not a pseudo-element.... forget it
3179 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
3180 0 : UngetToken();
3181 0 : return eSelectorParsingStatus_Error;
3182 : }
3183 :
3184 : // If it's a function token, it better be on our "ok" list, and if the name
3185 : // is that of a function pseudo it better be a function token
3186 0 : if ((eCSSToken_Function == mToken.mType) !=
3187 : (
3188 : #ifdef MOZ_XUL
3189 : isTree ||
3190 : #endif
3191 : nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType ||
3192 0 : nsCSSPseudoClasses::HasStringArg(pseudoClassType) ||
3193 0 : nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) ||
3194 0 : nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType))) {
3195 : // There are no other function pseudos
3196 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
3197 0 : UngetToken();
3198 0 : return eSelectorParsingStatus_Error;
3199 : }
3200 :
3201 : // If it starts with "::", it better be a pseudo-element
3202 0 : if (parsingPseudoElement &&
3203 0 : !isPseudoElement &&
3204 0 : !isAnonBox) {
3205 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
3206 0 : UngetToken();
3207 0 : return eSelectorParsingStatus_Error;
3208 : }
3209 :
3210 0 : if (!parsingPseudoElement &&
3211 : nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType) {
3212 0 : if (aIsNegated) { // :not() can't be itself negated
3213 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
3214 0 : UngetToken();
3215 0 : return eSelectorParsingStatus_Error;
3216 : }
3217 : // CSS 3 Negation pseudo-class takes one simple selector as argument
3218 : nsSelectorParsingStatus parsingStatus =
3219 0 : ParseNegatedSimpleSelector(aDataMask, aSelector);
3220 0 : if (eSelectorParsingStatus_Continue != parsingStatus) {
3221 0 : return parsingStatus;
3222 0 : }
3223 : }
3224 0 : else if (!parsingPseudoElement && isPseudoClass) {
3225 0 : aDataMask |= SEL_MASK_PCLASS;
3226 0 : if (eCSSToken_Function == mToken.mType) {
3227 : nsSelectorParsingStatus parsingStatus;
3228 0 : if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
3229 : parsingStatus =
3230 0 : ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
3231 : }
3232 0 : else if (nsCSSPseudoClasses::HasNthPairArg(pseudoClassType)) {
3233 : parsingStatus =
3234 0 : ParsePseudoClassWithNthPairArg(aSelector, pseudoClassType);
3235 : }
3236 : else {
3237 0 : NS_ABORT_IF_FALSE(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType),
3238 : "unexpected pseudo with function token");
3239 : parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
3240 0 : pseudoClassType);
3241 : }
3242 0 : if (eSelectorParsingStatus_Continue != parsingStatus) {
3243 0 : if (eSelectorParsingStatus_Error == parsingStatus) {
3244 0 : SkipUntil(')');
3245 : }
3246 0 : return parsingStatus;
3247 : }
3248 : }
3249 : else {
3250 0 : aSelector.AddPseudoClass(pseudoClassType);
3251 0 : }
3252 : }
3253 0 : else if (isPseudoElement || isAnonBox) {
3254 : // Pseudo-element. Make some more sanity checks.
3255 :
3256 0 : if (aIsNegated) { // pseudo-elements can't be negated
3257 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
3258 0 : UngetToken();
3259 0 : return eSelectorParsingStatus_Error;
3260 : }
3261 : // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
3262 : // to have a single ':' on them. Others (CSS3+ pseudo-elements and
3263 : // various -moz-* pseudo-elements) must have |parsingPseudoElement|
3264 : // set.
3265 0 : if (!parsingPseudoElement &&
3266 0 : !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
3267 : #ifdef MOZ_XUL
3268 0 : && !isTreePseudo
3269 : #endif
3270 : ) {
3271 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
3272 0 : UngetToken();
3273 0 : return eSelectorParsingStatus_Error;
3274 : }
3275 :
3276 0 : if (0 == (aDataMask & SEL_MASK_PELEM)) {
3277 0 : aDataMask |= SEL_MASK_PELEM;
3278 0 : NS_ADDREF(*aPseudoElement = pseudo);
3279 0 : *aPseudoElementType = pseudoElementType;
3280 :
3281 : #ifdef MOZ_XUL
3282 0 : if (isTree) {
3283 : // We have encountered a pseudoelement of the form
3284 : // -moz-tree-xxxx(a,b,c). We parse (a,b,c) and add each
3285 : // item in the list to the pseudoclass list. They will be pulled
3286 : // from the list later along with the pseudo-element.
3287 0 : if (!ParseTreePseudoElement(aPseudoElementArgs)) {
3288 0 : return eSelectorParsingStatus_Error;
3289 : }
3290 : }
3291 : #endif
3292 :
3293 : // the next *non*whitespace token must be '{' or ',' or EOF
3294 0 : if (!GetToken(true)) { // premature eof is ok (here!)
3295 0 : return eSelectorParsingStatus_Done;
3296 : }
3297 0 : if ((mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
3298 0 : UngetToken();
3299 0 : return eSelectorParsingStatus_Done;
3300 : }
3301 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelTrailing);
3302 0 : UngetToken();
3303 0 : return eSelectorParsingStatus_Error;
3304 : }
3305 : else { // multiple pseudo elements, not legal
3306 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
3307 0 : UngetToken();
3308 0 : return eSelectorParsingStatus_Error;
3309 : }
3310 : }
3311 : #ifdef DEBUG
3312 : else {
3313 : // We should never end up here. Indeed, if we ended up here, we know (from
3314 : // the current if/else cascade) that !isPseudoElement and !isAnonBox. But
3315 : // then due to our earlier check we know that isPseudoClass. Since we
3316 : // didn't fall into the isPseudoClass case in this cascade, we must have
3317 : // parsingPseudoElement. But we've already checked the
3318 : // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
3319 : // it's happened.
3320 0 : NS_NOTREACHED("How did this happen?");
3321 : }
3322 : #endif
3323 0 : return eSelectorParsingStatus_Continue;
3324 : }
3325 :
3326 : //
3327 : // Parse the argument of a negation pseudo-class :not()
3328 : //
3329 : CSSParserImpl::nsSelectorParsingStatus
3330 0 : CSSParserImpl::ParseNegatedSimpleSelector(PRInt32& aDataMask,
3331 : nsCSSSelector& aSelector)
3332 : {
3333 0 : if (! GetToken(true)) { // premature eof
3334 0 : REPORT_UNEXPECTED_EOF(PENegationEOF);
3335 0 : return eSelectorParsingStatus_Error;
3336 : }
3337 :
3338 0 : if (mToken.IsSymbol(')')) {
3339 0 : REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
3340 0 : return eSelectorParsingStatus_Error;
3341 : }
3342 :
3343 : // Create a new nsCSSSelector and add it to the end of
3344 : // aSelector.mNegations.
3345 : // Given the current parsing rules, every selector in mNegations
3346 : // contains only one simple selector (css3 definition) within it.
3347 : // This could easily change in future versions of CSS, and the only
3348 : // thing we need to change to support that is this parsing code and the
3349 : // serialization code for nsCSSSelector.
3350 0 : nsCSSSelector *newSel = new nsCSSSelector();
3351 0 : nsCSSSelector* negations = &aSelector;
3352 0 : while (negations->mNegations) {
3353 0 : negations = negations->mNegations;
3354 : }
3355 0 : negations->mNegations = newSel;
3356 :
3357 : nsSelectorParsingStatus parsingStatus;
3358 0 : if (eCSSToken_ID == mToken.mType) { // #id
3359 0 : parsingStatus = ParseIDSelector(aDataMask, *newSel);
3360 : }
3361 0 : else if (mToken.IsSymbol('.')) { // .class
3362 0 : parsingStatus = ParseClassSelector(aDataMask, *newSel);
3363 : }
3364 0 : else if (mToken.IsSymbol(':')) { // :pseudo
3365 : parsingStatus = ParsePseudoSelector(aDataMask, *newSel, true,
3366 0 : nsnull, nsnull, nsnull);
3367 : }
3368 0 : else if (mToken.IsSymbol('[')) { // [attribute
3369 0 : parsingStatus = ParseAttributeSelector(aDataMask, *newSel);
3370 0 : if (eSelectorParsingStatus_Error == parsingStatus) {
3371 : // Skip forward to the matching ']'
3372 0 : SkipUntil(']');
3373 : }
3374 : }
3375 : else {
3376 : // then it should be a type element or universal selector
3377 0 : parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, true);
3378 : }
3379 0 : if (eSelectorParsingStatus_Error == parsingStatus) {
3380 0 : REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
3381 0 : SkipUntil(')');
3382 0 : return parsingStatus;
3383 : }
3384 : // close the parenthesis
3385 0 : if (!ExpectSymbol(')', true)) {
3386 0 : REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
3387 0 : SkipUntil(')');
3388 0 : return eSelectorParsingStatus_Error;
3389 : }
3390 :
3391 0 : NS_ASSERTION(newSel->mNameSpace == kNameSpaceID_Unknown ||
3392 : (!newSel->mIDList && !newSel->mClassList &&
3393 : !newSel->mPseudoClassList && !newSel->mAttrList),
3394 : "Need to fix the serialization code to deal with this");
3395 :
3396 0 : return eSelectorParsingStatus_Continue;
3397 : }
3398 :
3399 : //
3400 : // Parse the argument of a pseudo-class that has an ident arg
3401 : //
3402 : CSSParserImpl::nsSelectorParsingStatus
3403 0 : CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
3404 : nsCSSPseudoClasses::Type aType)
3405 : {
3406 0 : if (! GetToken(true)) { // premature eof
3407 0 : REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3408 0 : return eSelectorParsingStatus_Error;
3409 : }
3410 : // We expect an identifier with a language abbreviation
3411 0 : if (eCSSToken_Ident != mToken.mType) {
3412 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent);
3413 0 : UngetToken();
3414 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
3415 : }
3416 :
3417 : // -moz-locale-dir can only have values of 'ltr' or 'rtl'.
3418 0 : if (aType == nsCSSPseudoClasses::ePseudoClass_mozLocaleDir) {
3419 0 : if (!mToken.mIdent.EqualsLiteral("ltr") &&
3420 0 : !mToken.mIdent.EqualsLiteral("rtl")) {
3421 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
3422 : }
3423 : }
3424 :
3425 : // Add the pseudo with the language parameter
3426 0 : aSelector.AddPseudoClass(aType, mToken.mIdent.get());
3427 :
3428 : // close the parenthesis
3429 0 : if (!ExpectSymbol(')', true)) {
3430 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
3431 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
3432 : }
3433 :
3434 0 : return eSelectorParsingStatus_Continue;
3435 : }
3436 :
3437 : CSSParserImpl::nsSelectorParsingStatus
3438 0 : CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
3439 : nsCSSPseudoClasses::Type aType)
3440 : {
3441 0 : PRInt32 numbers[2] = { 0, 0 };
3442 0 : bool lookForB = true;
3443 :
3444 : // Follow the whitespace rules as proposed in
3445 : // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
3446 :
3447 0 : if (! GetToken(true)) {
3448 0 : REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3449 0 : return eSelectorParsingStatus_Error;
3450 : }
3451 :
3452 0 : if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
3453 : // The CSS tokenization doesn't handle :nth-child() containing - well:
3454 : // 2n-1 is a dimension
3455 : // n-1 is an identifier
3456 : // The easiest way to deal with that is to push everything from the
3457 : // minus on back onto the scanner's pushback buffer.
3458 0 : PRUint32 truncAt = 0;
3459 0 : if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
3460 0 : truncAt = 1;
3461 0 : } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("-n-"))) {
3462 0 : truncAt = 2;
3463 : }
3464 0 : if (truncAt != 0) {
3465 0 : for (PRUint32 i = mToken.mIdent.Length() - 1; i >= truncAt; --i) {
3466 0 : mScanner.Pushback(mToken.mIdent[i]);
3467 : }
3468 0 : mToken.mIdent.Truncate(truncAt);
3469 : }
3470 : }
3471 :
3472 0 : if (eCSSToken_Ident == mToken.mType) {
3473 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("odd")) {
3474 0 : numbers[0] = 2;
3475 0 : numbers[1] = 1;
3476 0 : lookForB = false;
3477 : }
3478 0 : else if (mToken.mIdent.LowerCaseEqualsLiteral("even")) {
3479 0 : numbers[0] = 2;
3480 0 : numbers[1] = 0;
3481 0 : lookForB = false;
3482 : }
3483 0 : else if (mToken.mIdent.LowerCaseEqualsLiteral("n")) {
3484 0 : numbers[0] = 1;
3485 : }
3486 0 : else if (mToken.mIdent.LowerCaseEqualsLiteral("-n")) {
3487 0 : numbers[0] = -1;
3488 : }
3489 : else {
3490 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3491 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
3492 : }
3493 : }
3494 0 : else if (eCSSToken_Number == mToken.mType) {
3495 0 : if (!mToken.mIntegerValid) {
3496 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3497 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
3498 : }
3499 0 : numbers[1] = mToken.mInteger;
3500 0 : lookForB = false;
3501 : }
3502 0 : else if (eCSSToken_Dimension == mToken.mType) {
3503 0 : if (!mToken.mIntegerValid || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
3504 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3505 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
3506 : }
3507 0 : numbers[0] = mToken.mInteger;
3508 : }
3509 : // XXX If it's a ')', is that valid? (as 0n+0)
3510 : else {
3511 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3512 0 : UngetToken();
3513 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
3514 : }
3515 :
3516 0 : if (! GetToken(true)) {
3517 0 : REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3518 0 : return eSelectorParsingStatus_Error;
3519 : }
3520 0 : if (lookForB && !mToken.IsSymbol(')')) {
3521 : // The '+' or '-' sign can optionally be separated by whitespace.
3522 : // If it is separated by whitespace from what follows it, it appears
3523 : // as a separate token rather than part of the number token.
3524 0 : bool haveSign = false;
3525 0 : PRInt32 sign = 1;
3526 0 : if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
3527 0 : haveSign = true;
3528 0 : if (mToken.IsSymbol('-')) {
3529 0 : sign = -1;
3530 : }
3531 0 : if (! GetToken(true)) {
3532 0 : REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3533 0 : return eSelectorParsingStatus_Error;
3534 : }
3535 : }
3536 0 : if (eCSSToken_Number != mToken.mType ||
3537 0 : !mToken.mIntegerValid || mToken.mHasSign == haveSign) {
3538 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3539 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
3540 : }
3541 0 : numbers[1] = mToken.mInteger * sign;
3542 0 : if (! GetToken(true)) {
3543 0 : REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3544 0 : return eSelectorParsingStatus_Error;
3545 : }
3546 : }
3547 0 : if (!mToken.IsSymbol(')')) {
3548 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
3549 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
3550 : }
3551 0 : aSelector.AddPseudoClass(aType, numbers);
3552 0 : return eSelectorParsingStatus_Continue;
3553 : }
3554 :
3555 : //
3556 : // Parse the argument of a pseudo-class that has a selector list argument.
3557 : // Such selector lists cannot contain combinators, but can contain
3558 : // anything that goes between a pair of combinators.
3559 : //
3560 : CSSParserImpl::nsSelectorParsingStatus
3561 0 : CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
3562 : nsCSSPseudoClasses::Type aType)
3563 : {
3564 0 : nsAutoPtr<nsCSSSelectorList> slist;
3565 0 : if (! ParseSelectorList(*getter_Transfers(slist), PRUnichar(')'))) {
3566 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
3567 : }
3568 :
3569 : // Check that none of the selectors in the list have combinators or
3570 : // pseudo-elements.
3571 0 : for (nsCSSSelectorList *l = slist; l; l = l->mNext) {
3572 0 : nsCSSSelector *s = l->mSelectors;
3573 0 : if (s->mNext || s->IsPseudoElement()) {
3574 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
3575 : }
3576 : }
3577 :
3578 : // Add the pseudo with the selector list parameter
3579 0 : aSelector.AddPseudoClass(aType, slist.forget());
3580 :
3581 : // close the parenthesis
3582 0 : if (!ExpectSymbol(')', true)) {
3583 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
3584 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
3585 : }
3586 :
3587 0 : return eSelectorParsingStatus_Continue;
3588 : }
3589 :
3590 :
3591 : /**
3592 : * This is the format for selectors:
3593 : * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
3594 : */
3595 : bool
3596 220 : CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
3597 : PRUnichar aPrevCombinator)
3598 : {
3599 220 : if (! GetToken(true)) {
3600 0 : REPORT_UNEXPECTED_EOF(PESelectorEOF);
3601 0 : return false;
3602 : }
3603 :
3604 220 : nsCSSSelector* selector = aList->AddSelector(aPrevCombinator);
3605 440 : nsCOMPtr<nsIAtom> pseudoElement;
3606 440 : nsAutoPtr<nsAtomList> pseudoElementArgs;
3607 : nsCSSPseudoElements::Type pseudoElementType =
3608 220 : nsCSSPseudoElements::ePseudo_NotPseudoElement;
3609 :
3610 220 : PRInt32 dataMask = 0;
3611 : nsSelectorParsingStatus parsingStatus =
3612 220 : ParseTypeOrUniversalSelector(dataMask, *selector, false);
3613 :
3614 220 : while (parsingStatus == eSelectorParsingStatus_Continue) {
3615 110 : if (eCSSToken_ID == mToken.mType) { // #id
3616 0 : parsingStatus = ParseIDSelector(dataMask, *selector);
3617 : }
3618 110 : else if (mToken.IsSymbol('.')) { // .class
3619 0 : parsingStatus = ParseClassSelector(dataMask, *selector);
3620 : }
3621 110 : else if (mToken.IsSymbol(':')) { // :pseudo
3622 : parsingStatus = ParsePseudoSelector(dataMask, *selector, false,
3623 0 : getter_AddRefs(pseudoElement),
3624 : getter_Transfers(pseudoElementArgs),
3625 0 : &pseudoElementType);
3626 : }
3627 110 : else if (mToken.IsSymbol('[')) { // [attribute
3628 0 : parsingStatus = ParseAttributeSelector(dataMask, *selector);
3629 0 : if (eSelectorParsingStatus_Error == parsingStatus) {
3630 0 : SkipUntil(']');
3631 : }
3632 : }
3633 : else { // not a selector token, we're done
3634 110 : parsingStatus = eSelectorParsingStatus_Done;
3635 110 : UngetToken();
3636 110 : break;
3637 : }
3638 :
3639 0 : if (parsingStatus != eSelectorParsingStatus_Continue) {
3640 0 : break;
3641 : }
3642 :
3643 0 : if (! GetToken(false)) { // premature eof is ok (here!)
3644 0 : parsingStatus = eSelectorParsingStatus_Done;
3645 0 : break;
3646 : }
3647 : }
3648 :
3649 220 : if (parsingStatus == eSelectorParsingStatus_Error) {
3650 0 : return false;
3651 : }
3652 :
3653 220 : if (!dataMask) {
3654 0 : if (selector->mNext) {
3655 0 : REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
3656 : } else {
3657 0 : REPORT_UNEXPECTED(PESelectorGroupNoSelector);
3658 : }
3659 0 : return false;
3660 : }
3661 :
3662 220 : if (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox) {
3663 : // We got an anonymous box pseudo-element; it must be the only
3664 : // thing in this selector group.
3665 0 : if (selector->mNext || !IsUniversalSelector(*selector)) {
3666 0 : REPORT_UNEXPECTED(PEAnonBoxNotAlone);
3667 0 : return false;
3668 : }
3669 :
3670 : // Rewrite the current selector as this pseudo-element.
3671 : // It does not contribute to selector weight.
3672 0 : selector->mLowercaseTag.swap(pseudoElement);
3673 0 : selector->mClassList = pseudoElementArgs.forget();
3674 0 : selector->SetPseudoType(pseudoElementType);
3675 0 : return true;
3676 : }
3677 :
3678 220 : aList->mWeight += selector->CalcWeight();
3679 :
3680 : // Pseudo-elements other than anonymous boxes are represented as
3681 : // direct children ('>' combinator) of the rest of the selector.
3682 220 : if (pseudoElement) {
3683 0 : selector = aList->AddSelector('>');
3684 :
3685 0 : selector->mLowercaseTag.swap(pseudoElement);
3686 0 : selector->mClassList = pseudoElementArgs.forget();
3687 0 : selector->SetPseudoType(pseudoElementType);
3688 : }
3689 :
3690 220 : return true;
3691 : }
3692 :
3693 : css::Declaration*
3694 0 : CSSParserImpl::ParseDeclarationBlock(bool aCheckForBraces)
3695 : {
3696 0 : if (aCheckForBraces) {
3697 0 : if (!ExpectSymbol('{', true)) {
3698 0 : REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
3699 0 : OUTPUT_ERROR();
3700 0 : return nsnull;
3701 : }
3702 : }
3703 0 : css::Declaration* declaration = new css::Declaration();
3704 0 : mData.AssertInitialState();
3705 0 : if (declaration) {
3706 0 : for (;;) {
3707 : bool changed;
3708 0 : if (!ParseDeclaration(declaration, aCheckForBraces,
3709 0 : true, &changed)) {
3710 0 : if (!SkipDeclaration(aCheckForBraces)) {
3711 0 : break;
3712 : }
3713 0 : if (aCheckForBraces) {
3714 0 : if (ExpectSymbol('}', true)) {
3715 0 : break;
3716 : }
3717 : }
3718 : // Since the skipped declaration didn't end the block we parse
3719 : // the next declaration.
3720 : }
3721 : }
3722 0 : declaration->CompressFrom(&mData);
3723 : }
3724 0 : return declaration;
3725 : }
3726 :
3727 : // The types to pass to ParseColorComponent. These correspond to the
3728 : // various datatypes that can go within rgb().
3729 : #define COLOR_TYPE_UNKNOWN 0
3730 : #define COLOR_TYPE_INTEGERS 1
3731 : #define COLOR_TYPE_PERCENTAGES 2
3732 :
3733 : bool
3734 0 : CSSParserImpl::ParseColor(nsCSSValue& aValue)
3735 : {
3736 0 : if (!GetToken(true)) {
3737 0 : REPORT_UNEXPECTED_EOF(PEColorEOF);
3738 0 : return false;
3739 : }
3740 :
3741 0 : nsCSSToken* tk = &mToken;
3742 : nscolor rgba;
3743 0 : switch (tk->mType) {
3744 : case eCSSToken_ID:
3745 : case eCSSToken_Ref:
3746 : // #xxyyzz
3747 0 : if (NS_HexToRGB(tk->mIdent, &rgba)) {
3748 0 : aValue.SetColorValue(rgba);
3749 0 : return true;
3750 : }
3751 0 : break;
3752 :
3753 : case eCSSToken_Ident:
3754 0 : if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
3755 0 : aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
3756 0 : return true;
3757 : }
3758 : else {
3759 0 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
3760 0 : if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
3761 : PRInt32 value;
3762 0 : if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) {
3763 0 : aValue.SetIntValue(value, eCSSUnit_EnumColor);
3764 0 : return true;
3765 : }
3766 : }
3767 : }
3768 0 : break;
3769 : case eCSSToken_Function:
3770 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("rgb")) {
3771 : // rgb ( component , component , component )
3772 : PRUint8 r, g, b;
3773 0 : PRInt32 type = COLOR_TYPE_UNKNOWN;
3774 0 : if (ParseColorComponent(r, type, ',') &&
3775 0 : ParseColorComponent(g, type, ',') &&
3776 0 : ParseColorComponent(b, type, ')')) {
3777 0 : aValue.SetColorValue(NS_RGB(r,g,b));
3778 0 : return true;
3779 : }
3780 0 : SkipUntil(')');
3781 0 : return false;
3782 : }
3783 0 : else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-rgba") ||
3784 0 : mToken.mIdent.LowerCaseEqualsLiteral("rgba")) {
3785 : // rgba ( component , component , component , opacity )
3786 : PRUint8 r, g, b, a;
3787 0 : PRInt32 type = COLOR_TYPE_UNKNOWN;
3788 0 : if (ParseColorComponent(r, type, ',') &&
3789 0 : ParseColorComponent(g, type, ',') &&
3790 0 : ParseColorComponent(b, type, ',') &&
3791 0 : ParseColorOpacity(a)) {
3792 0 : aValue.SetColorValue(NS_RGBA(r, g, b, a));
3793 0 : return true;
3794 : }
3795 0 : SkipUntil(')');
3796 0 : return false;
3797 : }
3798 0 : else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
3799 : // hsl ( hue , saturation , lightness )
3800 : // "hue" is a number, "saturation" and "lightness" are percentages.
3801 0 : if (ParseHSLColor(rgba, ')')) {
3802 0 : aValue.SetColorValue(rgba);
3803 0 : return true;
3804 : }
3805 0 : SkipUntil(')');
3806 0 : return false;
3807 : }
3808 0 : else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-hsla") ||
3809 0 : mToken.mIdent.LowerCaseEqualsLiteral("hsla")) {
3810 : // hsla ( hue , saturation , lightness , opacity )
3811 : // "hue" is a number, "saturation" and "lightness" are percentages,
3812 : // "opacity" is a number.
3813 : PRUint8 a;
3814 0 : if (ParseHSLColor(rgba, ',') &&
3815 0 : ParseColorOpacity(a)) {
3816 : aValue.SetColorValue(NS_RGBA(NS_GET_R(rgba), NS_GET_G(rgba),
3817 0 : NS_GET_B(rgba), a));
3818 0 : return true;
3819 : }
3820 0 : SkipUntil(')');
3821 0 : return false;
3822 : }
3823 0 : break;
3824 : default:
3825 0 : break;
3826 : }
3827 :
3828 : // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
3829 0 : if (mNavQuirkMode && !IsParsingCompoundProperty()) {
3830 : // - If the string starts with 'a-f', the nsCSSScanner builds the
3831 : // token as a eCSSToken_Ident and we can parse the string as a
3832 : // 'xxyyzz' RGB color.
3833 : // - If it only contains '0-9' digits, the token is a
3834 : // eCSSToken_Number and it must be converted back to a 6
3835 : // characters string to be parsed as a RGB color.
3836 : // - If it starts with '0-9' and contains any 'a-f', the token is a
3837 : // eCSSToken_Dimension, the mNumber part must be converted back to
3838 : // a string and the mIdent part must be appended to that string so
3839 : // that the resulting string has 6 characters.
3840 : // Note: This is a hack for Nav compatibility. Do not attempt to
3841 : // simplify it by hacking into the ncCSSScanner. This would be very
3842 : // bad.
3843 0 : nsAutoString str;
3844 : char buffer[20];
3845 0 : switch (tk->mType) {
3846 : case eCSSToken_Ident:
3847 0 : str.Assign(tk->mIdent);
3848 0 : break;
3849 :
3850 : case eCSSToken_Number:
3851 0 : if (tk->mIntegerValid) {
3852 0 : PR_snprintf(buffer, sizeof(buffer), "%06d", tk->mInteger);
3853 0 : str.AssignWithConversion(buffer);
3854 : }
3855 0 : break;
3856 :
3857 : case eCSSToken_Dimension:
3858 0 : if (tk->mIdent.Length() <= 6) {
3859 0 : PR_snprintf(buffer, sizeof(buffer), "%06.0f", tk->mNumber);
3860 0 : nsAutoString temp;
3861 0 : temp.AssignWithConversion(buffer);
3862 0 : temp.Right(str, 6 - tk->mIdent.Length());
3863 0 : str.Append(tk->mIdent);
3864 : }
3865 0 : break;
3866 : default:
3867 : // There is a whole bunch of cases that are
3868 : // not handled by this switch. Ignore them.
3869 0 : break;
3870 : }
3871 0 : if (NS_HexToRGB(str, &rgba)) {
3872 0 : aValue.SetColorValue(rgba);
3873 0 : return true;
3874 : }
3875 : }
3876 :
3877 : // It's not a color
3878 0 : REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
3879 0 : UngetToken();
3880 0 : return false;
3881 : }
3882 :
3883 : // aType will be set if we have already parsed other color components
3884 : // in this color spec
3885 : bool
3886 0 : CSSParserImpl::ParseColorComponent(PRUint8& aComponent,
3887 : PRInt32& aType,
3888 : char aStop)
3889 : {
3890 0 : if (!GetToken(true)) {
3891 0 : REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
3892 0 : return false;
3893 : }
3894 : float value;
3895 0 : nsCSSToken* tk = &mToken;
3896 0 : switch (tk->mType) {
3897 : case eCSSToken_Number:
3898 0 : switch (aType) {
3899 : case COLOR_TYPE_UNKNOWN:
3900 0 : aType = COLOR_TYPE_INTEGERS;
3901 0 : break;
3902 : case COLOR_TYPE_INTEGERS:
3903 0 : break;
3904 : case COLOR_TYPE_PERCENTAGES:
3905 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3906 0 : UngetToken();
3907 0 : return false;
3908 : default:
3909 0 : NS_NOTREACHED("Someone forgot to add the new color component type in here");
3910 : }
3911 :
3912 0 : if (!mToken.mIntegerValid) {
3913 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
3914 0 : UngetToken();
3915 0 : return false;
3916 : }
3917 0 : value = tk->mNumber;
3918 0 : break;
3919 : case eCSSToken_Percentage:
3920 0 : switch (aType) {
3921 : case COLOR_TYPE_UNKNOWN:
3922 0 : aType = COLOR_TYPE_PERCENTAGES;
3923 0 : break;
3924 : case COLOR_TYPE_INTEGERS:
3925 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
3926 0 : UngetToken();
3927 0 : return false;
3928 : case COLOR_TYPE_PERCENTAGES:
3929 0 : break;
3930 : default:
3931 0 : NS_NOTREACHED("Someone forgot to add the new color component type in here");
3932 : }
3933 0 : value = tk->mNumber * 255.0f;
3934 0 : break;
3935 : default:
3936 0 : REPORT_UNEXPECTED_TOKEN(PEColorBadRGBContents);
3937 0 : UngetToken();
3938 0 : return false;
3939 : }
3940 0 : if (ExpectSymbol(aStop, true)) {
3941 0 : if (value < 0.0f) value = 0.0f;
3942 0 : if (value > 255.0f) value = 255.0f;
3943 0 : aComponent = NSToIntRound(value);
3944 0 : return true;
3945 : }
3946 0 : const PRUnichar stopString[] = { PRUnichar(aStop), PRUnichar(0) };
3947 : const PRUnichar *params[] = {
3948 : nsnull,
3949 : stopString
3950 0 : };
3951 0 : REPORT_UNEXPECTED_TOKEN_P(PEColorComponentBadTerm, params);
3952 0 : return false;
3953 : }
3954 :
3955 :
3956 : bool
3957 0 : CSSParserImpl::ParseHSLColor(nscolor& aColor,
3958 : char aStop)
3959 : {
3960 : float h, s, l;
3961 :
3962 : // Get the hue
3963 0 : if (!GetToken(true)) {
3964 0 : REPORT_UNEXPECTED_EOF(PEColorHueEOF);
3965 0 : return false;
3966 : }
3967 0 : if (mToken.mType != eCSSToken_Number) {
3968 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
3969 0 : UngetToken();
3970 0 : return false;
3971 : }
3972 0 : h = mToken.mNumber;
3973 0 : h /= 360.0f;
3974 : // hue values are wraparound
3975 0 : h = h - floor(h);
3976 :
3977 0 : if (!ExpectSymbol(',', true)) {
3978 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
3979 0 : return false;
3980 : }
3981 :
3982 : // Get the saturation
3983 0 : if (!GetToken(true)) {
3984 0 : REPORT_UNEXPECTED_EOF(PEColorSaturationEOF);
3985 0 : return false;
3986 : }
3987 0 : if (mToken.mType != eCSSToken_Percentage) {
3988 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3989 0 : UngetToken();
3990 0 : return false;
3991 : }
3992 0 : s = mToken.mNumber;
3993 0 : if (s < 0.0f) s = 0.0f;
3994 0 : if (s > 1.0f) s = 1.0f;
3995 :
3996 0 : if (!ExpectSymbol(',', true)) {
3997 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
3998 0 : return false;
3999 : }
4000 :
4001 : // Get the lightness
4002 0 : if (!GetToken(true)) {
4003 0 : REPORT_UNEXPECTED_EOF(PEColorLightnessEOF);
4004 0 : return false;
4005 : }
4006 0 : if (mToken.mType != eCSSToken_Percentage) {
4007 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
4008 0 : UngetToken();
4009 0 : return false;
4010 : }
4011 0 : l = mToken.mNumber;
4012 0 : if (l < 0.0f) l = 0.0f;
4013 0 : if (l > 1.0f) l = 1.0f;
4014 :
4015 0 : if (ExpectSymbol(aStop, true)) {
4016 0 : aColor = NS_HSL2RGB(h, s, l);
4017 0 : return true;
4018 : }
4019 :
4020 0 : const PRUnichar stopString[] = { PRUnichar(aStop), PRUnichar(0) };
4021 : const PRUnichar *params[] = {
4022 : nsnull,
4023 : stopString
4024 0 : };
4025 0 : REPORT_UNEXPECTED_TOKEN_P(PEColorComponentBadTerm, params);
4026 0 : return false;
4027 : }
4028 :
4029 :
4030 : bool
4031 0 : CSSParserImpl::ParseColorOpacity(PRUint8& aOpacity)
4032 : {
4033 0 : if (!GetToken(true)) {
4034 0 : REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
4035 0 : return false;
4036 : }
4037 :
4038 0 : if (mToken.mType != eCSSToken_Number) {
4039 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
4040 0 : UngetToken();
4041 0 : return false;
4042 : }
4043 :
4044 0 : if (mToken.mNumber < 0.0f) {
4045 0 : mToken.mNumber = 0.0f;
4046 0 : } else if (mToken.mNumber > 1.0f) {
4047 0 : mToken.mNumber = 1.0f;
4048 : }
4049 :
4050 0 : PRUint8 value = nsStyleUtil::FloatToColorComponent(mToken.mNumber);
4051 : // Need to compare to something slightly larger
4052 : // than 0.5 due to floating point inaccuracies.
4053 0 : NS_ASSERTION(fabs(255.0f*mToken.mNumber - value) <= 0.51f,
4054 : "FloatToColorComponent did something weird");
4055 :
4056 0 : if (!ExpectSymbol(')', true)) {
4057 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
4058 0 : return false;
4059 : }
4060 :
4061 0 : aOpacity = value;
4062 :
4063 0 : return true;
4064 : }
4065 :
4066 : #ifdef MOZ_XUL
4067 : bool
4068 0 : CSSParserImpl::ParseTreePseudoElement(nsAtomList **aPseudoElementArgs)
4069 : {
4070 : // The argument to a tree pseudo-element is a sequence of identifiers
4071 : // that are either space- or comma-separated. (Was the intent to
4072 : // allow only comma-separated? That's not what was done.)
4073 0 : nsCSSSelector fakeSelector; // so we can reuse AddPseudoClass
4074 :
4075 0 : while (!ExpectSymbol(')', true)) {
4076 0 : if (!GetToken(true)) {
4077 0 : return false;
4078 : }
4079 0 : if (eCSSToken_Ident == mToken.mType) {
4080 0 : fakeSelector.AddClass(mToken.mIdent);
4081 : }
4082 0 : else if (!mToken.IsSymbol(',')) {
4083 0 : UngetToken();
4084 0 : SkipUntil(')');
4085 0 : return false;
4086 : }
4087 : }
4088 0 : *aPseudoElementArgs = fakeSelector.mClassList;
4089 0 : fakeSelector.mClassList = nsnull;
4090 0 : return true;
4091 : }
4092 : #endif
4093 :
4094 : //----------------------------------------------------------------------
4095 :
4096 : bool
4097 0 : CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration,
4098 : bool aCheckForBraces,
4099 : bool aMustCallValueAppended,
4100 : bool* aChanged)
4101 : {
4102 0 : mTempData.AssertInitialState();
4103 :
4104 : // Get property name
4105 0 : nsCSSToken* tk = &mToken;
4106 0 : nsAutoString propertyName;
4107 0 : for (;;) {
4108 0 : if (!GetToken(true)) {
4109 0 : if (aCheckForBraces) {
4110 0 : REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
4111 : }
4112 0 : return false;
4113 : }
4114 0 : if (eCSSToken_Ident == tk->mType) {
4115 0 : propertyName = tk->mIdent;
4116 : // grab the ident before the ExpectSymbol trashes the token
4117 0 : if (!ExpectSymbol(':', true)) {
4118 0 : REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
4119 0 : REPORT_UNEXPECTED(PEDeclDropped);
4120 0 : OUTPUT_ERROR();
4121 0 : return false;
4122 : }
4123 : break;
4124 : }
4125 0 : if (tk->IsSymbol(';')) {
4126 : // dangling semicolons are skipped
4127 0 : continue;
4128 : }
4129 :
4130 0 : if (!tk->IsSymbol('}')) {
4131 0 : REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
4132 0 : REPORT_UNEXPECTED(PEDeclSkipped);
4133 0 : OUTPUT_ERROR();
4134 : }
4135 : // Not a declaration...
4136 0 : UngetToken();
4137 0 : return false;
4138 : }
4139 :
4140 : // Map property name to its ID and then parse the property
4141 0 : nsCSSProperty propID = nsCSSProps::LookupProperty(propertyName);
4142 0 : if (eCSSProperty_UNKNOWN == propID) { // unknown property
4143 0 : if (!NonMozillaVendorIdentifier(propertyName)) {
4144 : const PRUnichar *params[] = {
4145 0 : propertyName.get()
4146 0 : };
4147 0 : REPORT_UNEXPECTED_P(PEUnknownProperty, params);
4148 0 : REPORT_UNEXPECTED(PEDeclDropped);
4149 0 : OUTPUT_ERROR();
4150 : }
4151 :
4152 0 : return false;
4153 : }
4154 0 : if (! ParseProperty(propID)) {
4155 : // XXX Much better to put stuff in the value parsers instead...
4156 : const PRUnichar *params[] = {
4157 0 : propertyName.get()
4158 0 : };
4159 0 : REPORT_UNEXPECTED_P(PEValueParsingError, params);
4160 0 : REPORT_UNEXPECTED(PEDeclDropped);
4161 0 : OUTPUT_ERROR();
4162 0 : mTempData.ClearProperty(propID);
4163 0 : mTempData.AssertInitialState();
4164 0 : return false;
4165 : }
4166 0 : CLEAR_ERROR();
4167 :
4168 : // Look for "!important".
4169 0 : PriorityParsingStatus status = ParsePriority();
4170 :
4171 : // Look for a semicolon or close brace.
4172 0 : if (status != ePriority_Error) {
4173 0 : if (!GetToken(true)) {
4174 : // EOF is always ok
4175 0 : } else if (mToken.IsSymbol(';')) {
4176 : // semicolon is always ok
4177 0 : } else if (mToken.IsSymbol('}')) {
4178 : // brace is ok if aCheckForBraces, but don't eat it
4179 0 : UngetToken();
4180 0 : if (!aCheckForBraces) {
4181 0 : status = ePriority_Error;
4182 : }
4183 : } else {
4184 0 : UngetToken();
4185 0 : status = ePriority_Error;
4186 : }
4187 : }
4188 :
4189 0 : if (status == ePriority_Error) {
4190 0 : if (aCheckForBraces) {
4191 0 : REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2);
4192 : } else {
4193 0 : REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
4194 : }
4195 0 : REPORT_UNEXPECTED(PEDeclDropped);
4196 0 : OUTPUT_ERROR();
4197 0 : mTempData.ClearProperty(propID);
4198 0 : mTempData.AssertInitialState();
4199 0 : return false;
4200 : }
4201 :
4202 : *aChanged |= mData.TransferFromBlock(mTempData, propID,
4203 : status == ePriority_Important,
4204 : false, aMustCallValueAppended,
4205 0 : aDeclaration);
4206 0 : return true;
4207 : }
4208 :
4209 : static const nsCSSProperty kBorderTopIDs[] = {
4210 : eCSSProperty_border_top_width,
4211 : eCSSProperty_border_top_style,
4212 : eCSSProperty_border_top_color
4213 : };
4214 : static const nsCSSProperty kBorderRightIDs[] = {
4215 : eCSSProperty_border_right_width_value,
4216 : eCSSProperty_border_right_style_value,
4217 : eCSSProperty_border_right_color_value,
4218 : eCSSProperty_border_right_width,
4219 : eCSSProperty_border_right_style,
4220 : eCSSProperty_border_right_color
4221 : };
4222 : static const nsCSSProperty kBorderBottomIDs[] = {
4223 : eCSSProperty_border_bottom_width,
4224 : eCSSProperty_border_bottom_style,
4225 : eCSSProperty_border_bottom_color
4226 : };
4227 : static const nsCSSProperty kBorderLeftIDs[] = {
4228 : eCSSProperty_border_left_width_value,
4229 : eCSSProperty_border_left_style_value,
4230 : eCSSProperty_border_left_color_value,
4231 : eCSSProperty_border_left_width,
4232 : eCSSProperty_border_left_style,
4233 : eCSSProperty_border_left_color
4234 : };
4235 : static const nsCSSProperty kBorderStartIDs[] = {
4236 : eCSSProperty_border_start_width_value,
4237 : eCSSProperty_border_start_style_value,
4238 : eCSSProperty_border_start_color_value,
4239 : eCSSProperty_border_start_width,
4240 : eCSSProperty_border_start_style,
4241 : eCSSProperty_border_start_color
4242 : };
4243 : static const nsCSSProperty kBorderEndIDs[] = {
4244 : eCSSProperty_border_end_width_value,
4245 : eCSSProperty_border_end_style_value,
4246 : eCSSProperty_border_end_color_value,
4247 : eCSSProperty_border_end_width,
4248 : eCSSProperty_border_end_style,
4249 : eCSSProperty_border_end_color
4250 : };
4251 : static const nsCSSProperty kColumnRuleIDs[] = {
4252 : eCSSProperty__moz_column_rule_width,
4253 : eCSSProperty__moz_column_rule_style,
4254 : eCSSProperty__moz_column_rule_color
4255 : };
4256 :
4257 : bool
4258 0 : CSSParserImpl::ParseEnum(nsCSSValue& aValue,
4259 : const PRInt32 aKeywordTable[])
4260 : {
4261 0 : nsSubstring* ident = NextIdent();
4262 0 : if (nsnull == ident) {
4263 0 : return false;
4264 : }
4265 0 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
4266 0 : if (eCSSKeyword_UNKNOWN < keyword) {
4267 : PRInt32 value;
4268 0 : if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
4269 0 : aValue.SetIntValue(value, eCSSUnit_Enumerated);
4270 0 : return true;
4271 : }
4272 : }
4273 :
4274 : // Put the unknown identifier back and return
4275 0 : UngetToken();
4276 0 : return false;
4277 : }
4278 :
4279 :
4280 : struct UnitInfo {
4281 : char name[6]; // needs to be long enough for the longest unit, with
4282 : // terminating null.
4283 : PRUint32 length;
4284 : nsCSSUnit unit;
4285 : PRInt32 type;
4286 : };
4287 :
4288 : #define STR_WITH_LEN(_str) \
4289 : _str, sizeof(_str) - 1
4290 :
4291 : const UnitInfo UnitData[] = {
4292 : { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH },
4293 : { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
4294 : { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
4295 : { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
4296 : { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
4297 : { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
4298 : { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
4299 : { STR_WITH_LEN("rem"), eCSSUnit_RootEM, VARIANT_LENGTH },
4300 : { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
4301 : { STR_WITH_LEN("mozmm"), eCSSUnit_PhysicalMillimeter, VARIANT_LENGTH },
4302 : { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
4303 : { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
4304 : { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
4305 : { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE },
4306 : { STR_WITH_LEN("turn"), eCSSUnit_Turn, VARIANT_ANGLE },
4307 : { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY },
4308 : { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY },
4309 : { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME },
4310 : { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME }
4311 : };
4312 :
4313 : #undef STR_WITH_LEN
4314 :
4315 : bool
4316 0 : CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
4317 : PRInt32 aVariantMask,
4318 : float aNumber,
4319 : const nsString& aUnit)
4320 : {
4321 : nsCSSUnit units;
4322 0 : PRInt32 type = 0;
4323 0 : if (!aUnit.IsEmpty()) {
4324 : PRUint32 i;
4325 0 : for (i = 0; i < ArrayLength(UnitData); ++i) {
4326 0 : if (aUnit.LowerCaseEqualsASCII(UnitData[i].name,
4327 0 : UnitData[i].length)) {
4328 0 : units = UnitData[i].unit;
4329 0 : type = UnitData[i].type;
4330 0 : break;
4331 : }
4332 : }
4333 :
4334 0 : if (i == ArrayLength(UnitData)) {
4335 : // Unknown unit
4336 0 : return false;
4337 : }
4338 : } else {
4339 : // Must be a zero number...
4340 0 : NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
4341 0 : if ((VARIANT_LENGTH & aVariantMask) != 0) {
4342 0 : units = eCSSUnit_Pixel;
4343 0 : type = VARIANT_LENGTH;
4344 : }
4345 0 : else if ((VARIANT_ANGLE & aVariantMask) != 0) {
4346 0 : NS_ASSERTION(aVariantMask & VARIANT_ZERO_ANGLE,
4347 : "must have allowed zero angle");
4348 0 : units = eCSSUnit_Degree;
4349 0 : type = VARIANT_ANGLE;
4350 : }
4351 : else {
4352 0 : NS_ERROR("Variant mask does not include dimension; why were we called?");
4353 0 : return false;
4354 : }
4355 : }
4356 0 : if ((type & aVariantMask) != 0) {
4357 0 : aValue.SetFloatValue(aNumber, units);
4358 0 : return true;
4359 : }
4360 0 : return false;
4361 : }
4362 :
4363 : // Note that this does include VARIANT_CALC, which is numeric. This is
4364 : // because calc() parsing, as proposed, drops range restrictions inside
4365 : // the calc() expression and clamps the result of the calculation to the
4366 : // range.
4367 : #define VARIANT_ALL_NONNUMERIC \
4368 : VARIANT_KEYWORD | \
4369 : VARIANT_COLOR | \
4370 : VARIANT_URL | \
4371 : VARIANT_STRING | \
4372 : VARIANT_COUNTER | \
4373 : VARIANT_ATTR | \
4374 : VARIANT_IDENTIFIER | \
4375 : VARIANT_IDENTIFIER_NO_INHERIT | \
4376 : VARIANT_AUTO | \
4377 : VARIANT_INHERIT | \
4378 : VARIANT_NONE | \
4379 : VARIANT_NORMAL | \
4380 : VARIANT_SYSFONT | \
4381 : VARIANT_GRADIENT | \
4382 : VARIANT_TIMING_FUNCTION | \
4383 : VARIANT_ALL | \
4384 : VARIANT_CALC
4385 :
4386 : // Note that callers passing VARIANT_CALC in aVariantMask will get
4387 : // full-range parsing inside the calc() expression, and the code that
4388 : // computes the calc will be required to clamp the resulting value to an
4389 : // appropriate range.
4390 : bool
4391 0 : CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue,
4392 : PRInt32 aVariantMask,
4393 : const PRInt32 aKeywordTable[])
4394 : {
4395 : // The variant mask must only contain non-numeric variants or the ones
4396 : // that we specifically handle.
4397 0 : NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
4398 : VARIANT_NUMBER |
4399 : VARIANT_LENGTH |
4400 : VARIANT_PERCENT |
4401 : VARIANT_INTEGER)) == 0,
4402 : "need to update code below to handle additional variants");
4403 :
4404 0 : if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
4405 0 : if (eCSSUnit_Number == aValue.GetUnit() ||
4406 0 : aValue.IsLengthUnit()){
4407 0 : if (aValue.GetFloatValue() < 0) {
4408 0 : UngetToken();
4409 0 : return false;
4410 : }
4411 : }
4412 0 : else if (aValue.GetUnit() == eCSSUnit_Percent) {
4413 0 : if (aValue.GetPercentValue() < 0) {
4414 0 : UngetToken();
4415 0 : return false;
4416 : }
4417 0 : } else if (aValue.GetUnit() == eCSSUnit_Integer) {
4418 0 : if (aValue.GetIntValue() < 0) {
4419 0 : UngetToken();
4420 0 : return false;
4421 : }
4422 : }
4423 0 : return true;
4424 : }
4425 0 : return false;
4426 : }
4427 :
4428 : // Note that callers passing VARIANT_CALC in aVariantMask will get
4429 : // full-range parsing inside the calc() expression, and the code that
4430 : // computes the calc will be required to clamp the resulting value to an
4431 : // appropriate range.
4432 : bool
4433 0 : CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue,
4434 : PRInt32 aVariantMask,
4435 : const PRInt32 aKeywordTable[])
4436 : {
4437 : // The variant mask must only contain non-numeric variants or the ones
4438 : // that we specifically handle.
4439 0 : NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
4440 : VARIANT_NUMBER |
4441 : VARIANT_INTEGER)) == 0,
4442 : "need to update code below to handle additional variants");
4443 :
4444 0 : if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
4445 0 : if (aValue.GetUnit() == eCSSUnit_Integer) {
4446 0 : if (aValue.GetIntValue() < 1) {
4447 0 : UngetToken();
4448 0 : return false;
4449 : }
4450 0 : } else if (eCSSUnit_Number == aValue.GetUnit()) {
4451 0 : if (aValue.GetFloatValue() < 1.0f) {
4452 0 : UngetToken();
4453 0 : return false;
4454 : }
4455 : }
4456 0 : return true;
4457 : }
4458 0 : return false;
4459 : }
4460 :
4461 : // Assigns to aValue iff it returns true.
4462 : bool
4463 0 : CSSParserImpl::ParseVariant(nsCSSValue& aValue,
4464 : PRInt32 aVariantMask,
4465 : const PRInt32 aKeywordTable[])
4466 : {
4467 0 : NS_ASSERTION(IsParsingCompoundProperty() ||
4468 : ((~aVariantMask) & (VARIANT_LENGTH|VARIANT_COLOR)),
4469 : "cannot distinguish lengths and colors in quirks mode");
4470 0 : NS_ABORT_IF_FALSE(!(aVariantMask & VARIANT_IDENTIFIER) ||
4471 : !(aVariantMask & VARIANT_IDENTIFIER_NO_INHERIT),
4472 : "must not set both VARIANT_IDENTIFIER and "
4473 : "VARIANT_IDENTIFIER_NO_INHERIT");
4474 :
4475 0 : if (!GetToken(true)) {
4476 0 : return false;
4477 : }
4478 0 : nsCSSToken* tk = &mToken;
4479 0 : if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) &&
4480 : (eCSSToken_Ident == tk->mType)) {
4481 0 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
4482 0 : if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
4483 0 : if ((aVariantMask & VARIANT_AUTO) != 0) {
4484 0 : if (eCSSKeyword_auto == keyword) {
4485 0 : aValue.SetAutoValue();
4486 0 : return true;
4487 : }
4488 : }
4489 0 : if ((aVariantMask & VARIANT_INHERIT) != 0) {
4490 : // XXX Should we check IsParsingCompoundProperty, or do all
4491 : // callers handle it? (Not all callers set it, though, since
4492 : // they want the quirks that are disabled by setting it.)
4493 0 : if (eCSSKeyword_inherit == keyword) {
4494 0 : aValue.SetInheritValue();
4495 0 : return true;
4496 : }
4497 0 : else if (eCSSKeyword__moz_initial == keyword) { // anything that can inherit can also take an initial val.
4498 0 : aValue.SetInitialValue();
4499 0 : return true;
4500 : }
4501 : }
4502 0 : if ((aVariantMask & VARIANT_NONE) != 0) {
4503 0 : if (eCSSKeyword_none == keyword) {
4504 0 : aValue.SetNoneValue();
4505 0 : return true;
4506 : }
4507 : }
4508 0 : if ((aVariantMask & VARIANT_ALL) != 0) {
4509 0 : if (eCSSKeyword_all == keyword) {
4510 0 : aValue.SetAllValue();
4511 0 : return true;
4512 : }
4513 : }
4514 0 : if ((aVariantMask & VARIANT_NORMAL) != 0) {
4515 0 : if (eCSSKeyword_normal == keyword) {
4516 0 : aValue.SetNormalValue();
4517 0 : return true;
4518 : }
4519 : }
4520 0 : if ((aVariantMask & VARIANT_SYSFONT) != 0) {
4521 0 : if (eCSSKeyword__moz_use_system_font == keyword &&
4522 0 : !IsParsingCompoundProperty()) {
4523 0 : aValue.SetSystemFontValue();
4524 0 : return true;
4525 : }
4526 : }
4527 0 : if ((aVariantMask & VARIANT_KEYWORD) != 0) {
4528 : PRInt32 value;
4529 0 : if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
4530 0 : aValue.SetIntValue(value, eCSSUnit_Enumerated);
4531 0 : return true;
4532 : }
4533 : }
4534 : }
4535 : }
4536 : // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or
4537 : // VARIANT_ZERO_ANGLE.
4538 0 : if (((aVariantMask & VARIANT_NUMBER) != 0) &&
4539 : (eCSSToken_Number == tk->mType)) {
4540 0 : aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
4541 0 : return true;
4542 : }
4543 0 : if (((aVariantMask & VARIANT_INTEGER) != 0) &&
4544 : (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
4545 0 : aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
4546 0 : return true;
4547 : }
4548 0 : if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE |
4549 : VARIANT_FREQUENCY | VARIANT_TIME)) != 0 &&
4550 : eCSSToken_Dimension == tk->mType) ||
4551 : ((aVariantMask & (VARIANT_LENGTH | VARIANT_ZERO_ANGLE)) != 0 &&
4552 : eCSSToken_Number == tk->mType &&
4553 : tk->mNumber == 0.0f)) {
4554 0 : if ((aVariantMask & VARIANT_POSITIVE_LENGTH) != 0 &&
4555 : tk->mNumber <= 0.0) {
4556 0 : UngetToken();
4557 0 : return false;
4558 : }
4559 0 : if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
4560 0 : return true;
4561 : }
4562 : // Put the token back; we didn't parse it, so we shouldn't consume it
4563 0 : UngetToken();
4564 0 : return false;
4565 : }
4566 0 : if (((aVariantMask & VARIANT_PERCENT) != 0) &&
4567 : (eCSSToken_Percentage == tk->mType)) {
4568 0 : aValue.SetPercentValue(tk->mNumber);
4569 0 : return true;
4570 : }
4571 0 : if (mNavQuirkMode && !IsParsingCompoundProperty()) { // NONSTANDARD: Nav interprets unitless numbers as px
4572 0 : if (((aVariantMask & VARIANT_LENGTH) != 0) &&
4573 : (eCSSToken_Number == tk->mType)) {
4574 0 : aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
4575 0 : return true;
4576 : }
4577 : }
4578 :
4579 0 : if (IsSVGMode() && !IsParsingCompoundProperty()) {
4580 : // STANDARD: SVG Spec states that lengths and coordinates can be unitless
4581 : // in which case they default to user-units (1 px = 1 user unit)
4582 0 : if (((aVariantMask & VARIANT_LENGTH) != 0) &&
4583 : (eCSSToken_Number == tk->mType)) {
4584 0 : aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
4585 0 : return true;
4586 : }
4587 : }
4588 :
4589 0 : if (((aVariantMask & VARIANT_URL) != 0) &&
4590 : eCSSToken_URL == tk->mType) {
4591 0 : SetValueToURL(aValue, tk->mIdent);
4592 0 : return true;
4593 : }
4594 0 : if ((aVariantMask & VARIANT_GRADIENT) != 0 &&
4595 : eCSSToken_Function == tk->mType) {
4596 : // a generated gradient
4597 0 : if (tk->mIdent.LowerCaseEqualsLiteral("-moz-linear-gradient"))
4598 0 : return ParseGradient(aValue, false, false);
4599 :
4600 0 : if (tk->mIdent.LowerCaseEqualsLiteral("-moz-radial-gradient"))
4601 0 : return ParseGradient(aValue, true, false);
4602 :
4603 0 : if (tk->mIdent.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient"))
4604 0 : return ParseGradient(aValue, false, true);
4605 :
4606 0 : if (tk->mIdent.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient"))
4607 0 : return ParseGradient(aValue, true, true);
4608 : }
4609 0 : if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 &&
4610 : eCSSToken_Function == tk->mType &&
4611 0 : tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) {
4612 0 : return ParseImageRect(aValue);
4613 : }
4614 0 : if ((aVariantMask & VARIANT_ELEMENT) != 0 &&
4615 : eCSSToken_Function == tk->mType &&
4616 0 : tk->mIdent.LowerCaseEqualsLiteral("-moz-element")) {
4617 0 : return ParseElement(aValue);
4618 : }
4619 0 : if ((aVariantMask & VARIANT_COLOR) != 0) {
4620 0 : if ((mNavQuirkMode && !IsParsingCompoundProperty()) || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
4621 : (eCSSToken_ID == tk->mType) ||
4622 : (eCSSToken_Ref == tk->mType) ||
4623 : (eCSSToken_Ident == tk->mType) ||
4624 : ((eCSSToken_Function == tk->mType) &&
4625 0 : (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
4626 0 : tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
4627 0 : tk->mIdent.LowerCaseEqualsLiteral("-moz-rgba") ||
4628 0 : tk->mIdent.LowerCaseEqualsLiteral("-moz-hsla") ||
4629 0 : tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
4630 0 : tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
4631 : {
4632 : // Put token back so that parse color can get it
4633 0 : UngetToken();
4634 0 : if (ParseColor(aValue)) {
4635 0 : return true;
4636 : }
4637 0 : return false;
4638 : }
4639 : }
4640 0 : if (((aVariantMask & VARIANT_STRING) != 0) &&
4641 : (eCSSToken_String == tk->mType)) {
4642 0 : nsAutoString buffer;
4643 0 : buffer.Append(tk->mIdent);
4644 0 : aValue.SetStringValue(buffer, eCSSUnit_String);
4645 0 : return true;
4646 : }
4647 0 : if (((aVariantMask &
4648 : (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) &&
4649 : (eCSSToken_Ident == tk->mType) &&
4650 : ((aVariantMask & VARIANT_IDENTIFIER) != 0 ||
4651 0 : !(tk->mIdent.LowerCaseEqualsLiteral("inherit") ||
4652 0 : tk->mIdent.LowerCaseEqualsLiteral("initial")))) {
4653 0 : aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
4654 0 : return true;
4655 : }
4656 0 : if (((aVariantMask & VARIANT_COUNTER) != 0) &&
4657 : (eCSSToken_Function == tk->mType) &&
4658 0 : (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
4659 0 : tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
4660 0 : return ParseCounter(aValue);
4661 : }
4662 0 : if (((aVariantMask & VARIANT_ATTR) != 0) &&
4663 : (eCSSToken_Function == tk->mType) &&
4664 0 : tk->mIdent.LowerCaseEqualsLiteral("attr")) {
4665 0 : if (!ParseAttr(aValue)) {
4666 0 : SkipUntil(')');
4667 0 : return false;
4668 : }
4669 0 : return true;
4670 : }
4671 0 : if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) &&
4672 : (eCSSToken_Function == tk->mType)) {
4673 0 : if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) {
4674 0 : if (!ParseTransitionTimingFunctionValues(aValue)) {
4675 0 : SkipUntil(')');
4676 0 : return false;
4677 : }
4678 0 : return true;
4679 : }
4680 0 : if (tk->mIdent.LowerCaseEqualsLiteral("steps")) {
4681 0 : if (!ParseTransitionStepTimingFunctionValues(aValue)) {
4682 0 : SkipUntil(')');
4683 0 : return false;
4684 : }
4685 0 : return true;
4686 : }
4687 : }
4688 0 : if ((aVariantMask & VARIANT_CALC) &&
4689 : (eCSSToken_Function == tk->mType) &&
4690 0 : tk->mIdent.LowerCaseEqualsLiteral("-moz-calc")) {
4691 : // calc() currently allows only lengths and percents inside it.
4692 0 : return ParseCalc(aValue, aVariantMask & VARIANT_LP);
4693 : }
4694 :
4695 0 : UngetToken();
4696 0 : return false;
4697 : }
4698 :
4699 :
4700 : bool
4701 0 : CSSParserImpl::ParseCounter(nsCSSValue& aValue)
4702 : {
4703 0 : nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
4704 0 : eCSSUnit_Counter : eCSSUnit_Counters);
4705 :
4706 : // A non-iterative for loop to break out when an error occurs.
4707 : for (;;) {
4708 0 : if (!GetToken(true)) {
4709 0 : break;
4710 : }
4711 0 : if (eCSSToken_Ident != mToken.mType) {
4712 0 : UngetToken();
4713 0 : break;
4714 : }
4715 :
4716 : nsRefPtr<nsCSSValue::Array> val =
4717 0 : nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
4718 :
4719 0 : val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_Ident);
4720 :
4721 0 : if (eCSSUnit_Counters == unit) {
4722 : // must have a comma and then a separator string
4723 0 : if (!ExpectSymbol(',', true) || !GetToken(true)) {
4724 : break;
4725 : }
4726 0 : if (eCSSToken_String != mToken.mType) {
4727 0 : UngetToken();
4728 : break;
4729 : }
4730 0 : val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
4731 : }
4732 :
4733 : // get optional type
4734 0 : PRInt32 type = NS_STYLE_LIST_STYLE_DECIMAL;
4735 0 : if (ExpectSymbol(',', true)) {
4736 0 : if (!GetToken(true)) {
4737 : break;
4738 : }
4739 : nsCSSKeyword keyword;
4740 0 : if (eCSSToken_Ident != mToken.mType ||
4741 0 : (keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent)) ==
4742 : eCSSKeyword_UNKNOWN ||
4743 : !nsCSSProps::FindKeyword(keyword, nsCSSProps::kListStyleKTable,
4744 0 : type)) {
4745 0 : UngetToken();
4746 : break;
4747 : }
4748 : }
4749 :
4750 0 : PRInt32 typeItem = eCSSUnit_Counters == unit ? 2 : 1;
4751 0 : val->Item(typeItem).SetIntValue(type, eCSSUnit_Enumerated);
4752 :
4753 0 : if (!ExpectSymbol(')', true)) {
4754 : break;
4755 : }
4756 :
4757 0 : aValue.SetArrayValue(val, unit);
4758 0 : return true;
4759 : }
4760 :
4761 0 : SkipUntil(')');
4762 0 : return false;
4763 : }
4764 :
4765 : bool
4766 0 : CSSParserImpl::ParseAttr(nsCSSValue& aValue)
4767 : {
4768 0 : if (!GetToken(true)) {
4769 0 : return false;
4770 : }
4771 :
4772 0 : nsAutoString attr;
4773 0 : if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
4774 0 : nsAutoString holdIdent(mToken.mIdent);
4775 0 : if (ExpectSymbol('|', false)) { // namespace
4776 0 : PRInt32 nameSpaceID = GetNamespaceIdForPrefix(holdIdent);
4777 0 : if (nameSpaceID == kNameSpaceID_Unknown) {
4778 0 : return false;
4779 : }
4780 0 : attr.AppendInt(nameSpaceID, 10);
4781 0 : attr.Append(PRUnichar('|'));
4782 0 : if (! GetToken(false)) {
4783 0 : REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4784 0 : return false;
4785 : }
4786 0 : if (eCSSToken_Ident == mToken.mType) {
4787 0 : attr.Append(mToken.mIdent);
4788 : }
4789 : else {
4790 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4791 0 : UngetToken();
4792 0 : return false;
4793 : }
4794 : }
4795 : else { // no namespace
4796 0 : attr = holdIdent;
4797 : }
4798 : }
4799 0 : else if (mToken.IsSymbol('*')) { // namespace wildcard
4800 : // Wildcard namespace makes no sense here and is not allowed
4801 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4802 0 : UngetToken();
4803 0 : return false;
4804 : }
4805 0 : else if (mToken.IsSymbol('|')) { // explicit NO namespace
4806 0 : if (! GetToken(false)) {
4807 0 : REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4808 0 : return false;
4809 : }
4810 0 : if (eCSSToken_Ident == mToken.mType) {
4811 0 : attr.Append(mToken.mIdent);
4812 : }
4813 : else {
4814 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4815 0 : UngetToken();
4816 0 : return false;
4817 : }
4818 : }
4819 : else {
4820 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
4821 0 : UngetToken();
4822 0 : return false;
4823 : }
4824 0 : if (!ExpectSymbol(')', true)) {
4825 0 : return false;
4826 : }
4827 0 : aValue.SetStringValue(attr, eCSSUnit_Attr);
4828 0 : return true;
4829 : }
4830 :
4831 : bool
4832 0 : CSSParserImpl::SetValueToURL(nsCSSValue& aValue, const nsString& aURL)
4833 : {
4834 0 : if (!mSheetPrincipal) {
4835 : NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
4836 0 : "origin principal");
4837 0 : return false;
4838 : }
4839 :
4840 0 : nsRefPtr<nsStringBuffer> buffer(nsCSSValue::BufferFromString(aURL));
4841 :
4842 : // Note: urlVal retains its own reference to |buffer|.
4843 : nsCSSValue::URL *urlVal =
4844 0 : new nsCSSValue::URL(buffer, mBaseURI, mSheetURI, mSheetPrincipal);
4845 0 : aValue.SetURLValue(urlVal);
4846 0 : return true;
4847 : }
4848 :
4849 : /**
4850 : * Parse the arguments of -moz-image-rect() function.
4851 : * -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>)
4852 : */
4853 : bool
4854 0 : CSSParserImpl::ParseImageRect(nsCSSValue& aImage)
4855 : {
4856 : // A non-iterative for loop to break out when an error occurs.
4857 : for (;;) {
4858 0 : nsCSSValue newFunction;
4859 : static const PRUint32 kNumArgs = 5;
4860 : nsCSSValue::Array* func =
4861 0 : newFunction.InitFunction(eCSSKeyword__moz_image_rect, kNumArgs);
4862 :
4863 : // func->Item(0) is reserved for the function name.
4864 0 : nsCSSValue& url = func->Item(1);
4865 0 : nsCSSValue& top = func->Item(2);
4866 0 : nsCSSValue& right = func->Item(3);
4867 0 : nsCSSValue& bottom = func->Item(4);
4868 0 : nsCSSValue& left = func->Item(5);
4869 :
4870 0 : nsAutoString urlString;
4871 0 : if (!ParseURLOrString(urlString) ||
4872 0 : !SetValueToURL(url, urlString) ||
4873 0 : !ExpectSymbol(',', true)) {
4874 : break;
4875 : }
4876 :
4877 : static const PRInt32 VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT;
4878 0 : if (!ParseNonNegativeVariant(top, VARIANT_SIDE, nsnull) ||
4879 0 : !ExpectSymbol(',', true) ||
4880 0 : !ParseNonNegativeVariant(right, VARIANT_SIDE, nsnull) ||
4881 0 : !ExpectSymbol(',', true) ||
4882 0 : !ParseNonNegativeVariant(bottom, VARIANT_SIDE, nsnull) ||
4883 0 : !ExpectSymbol(',', true) ||
4884 0 : !ParseNonNegativeVariant(left, VARIANT_SIDE, nsnull) ||
4885 0 : !ExpectSymbol(')', true))
4886 : break;
4887 :
4888 0 : aImage = newFunction;
4889 0 : return true;
4890 : }
4891 :
4892 0 : SkipUntil(')');
4893 0 : return false;
4894 : }
4895 :
4896 : // <element>: -moz-element(# <element_id> )
4897 : bool
4898 0 : CSSParserImpl::ParseElement(nsCSSValue& aValue)
4899 : {
4900 : // A non-iterative for loop to break out when an error occurs.
4901 : for (;;) {
4902 0 : if (!GetToken(true))
4903 0 : break;
4904 :
4905 0 : if (mToken.mType == eCSSToken_ID) {
4906 0 : aValue.SetStringValue(mToken.mIdent, eCSSUnit_Element);
4907 : } else {
4908 0 : UngetToken();
4909 0 : break;
4910 : }
4911 :
4912 0 : if (!ExpectSymbol(')', true))
4913 0 : break;
4914 :
4915 0 : return true;
4916 : }
4917 :
4918 : // If we detect a syntax error, we must match the opening parenthesis of the
4919 : // function with the closing parenthesis and skip all the tokens in between.
4920 0 : SkipUntil(')');
4921 0 : return false;
4922 : }
4923 :
4924 : // <color-stop> : <color> [ <percentage> | <length> ]?
4925 : bool
4926 0 : CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient)
4927 : {
4928 0 : nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
4929 0 : if (!ParseVariant(stop->mColor, VARIANT_COLOR, nsnull)) {
4930 0 : return false;
4931 : }
4932 :
4933 : // Stop positions do not have to fall between the starting-point and
4934 : // ending-point, so we don't use ParseNonNegativeVariant.
4935 0 : if (!ParseVariant(stop->mLocation, VARIANT_LP, nsnull)) {
4936 0 : stop->mLocation.SetNoneValue();
4937 : }
4938 0 : return true;
4939 : }
4940 :
4941 : // <gradient>
4942 : // : linear-gradient( <gradient-line>? <color-stops> ')'
4943 : // : radial-gradient( <gradient-line>? <gradient-shape-size>?
4944 : // <color-stops> ')'
4945 : //
4946 : // <gradient-line> : [ to [left | right] || [top | bottom] ] ,
4947 : // | <legacy-gradient-line>
4948 : // <legacy-gradient-line> : [ <bg-position> || <angle>] ,
4949 : //
4950 : // <gradient-shape-size> : [<gradient-shape> || <gradient-size>] ,
4951 : // <gradient-shape> : circle | ellipse
4952 : // <gradient-size> : closest-side | closest-corner
4953 : // | farthest-side | farthest-corner
4954 : // | contain | cover
4955 : //
4956 : // <color-stops> : <color-stop> , <color-stop> [, <color-stop>]*
4957 : bool
4958 0 : CSSParserImpl::ParseGradient(nsCSSValue& aValue, bool aIsRadial,
4959 : bool aIsRepeating)
4960 : {
4961 : nsRefPtr<nsCSSValueGradient> cssGradient
4962 0 : = new nsCSSValueGradient(aIsRadial, aIsRepeating);
4963 :
4964 : // <gradient-line>
4965 : // N.B. ParseBoxPositionValues is not guaranteed to put back
4966 : // everything it scanned if it fails, so we must only call it
4967 : // if there is no alternative to consuming a <box-position>.
4968 : // ParseVariant, as used here, will either succeed and consume
4969 : // a single token, or fail and consume none, so we can be more
4970 : // cavalier about calling it.
4971 :
4972 0 : if (!GetToken(true)) {
4973 0 : return false;
4974 : }
4975 :
4976 0 : bool toCorner = false;
4977 0 : if (mToken.mType == eCSSToken_Ident &&
4978 0 : mToken.mIdent.LowerCaseEqualsLiteral("to")) {
4979 0 : toCorner = true;
4980 0 : if (!GetToken(true)) {
4981 0 : return false;
4982 : }
4983 : }
4984 :
4985 0 : nsCSSTokenType ty = mToken.mType;
4986 0 : nsString id = mToken.mIdent;
4987 0 : cssGradient->mIsToCorner = toCorner;
4988 0 : UngetToken();
4989 :
4990 0 : bool haveGradientLine = false;
4991 0 : switch (ty) {
4992 : case eCSSToken_Percentage:
4993 : case eCSSToken_Number:
4994 : case eCSSToken_Dimension:
4995 0 : haveGradientLine = true;
4996 0 : break;
4997 :
4998 : case eCSSToken_Function:
4999 0 : if (id.LowerCaseEqualsLiteral("-moz-calc")) {
5000 0 : haveGradientLine = true;
5001 0 : break;
5002 : }
5003 : // fall through
5004 : case eCSSToken_ID:
5005 : case eCSSToken_Ref:
5006 : // this is a color
5007 0 : break;
5008 :
5009 : case eCSSToken_Ident: {
5010 : // This is only a gradient line if it's a box position keyword.
5011 0 : nsCSSKeyword kw = nsCSSKeywords::LookupKeyword(id);
5012 : PRInt32 junk;
5013 0 : if (kw != eCSSKeyword_UNKNOWN &&
5014 : nsCSSProps::FindKeyword(kw, nsCSSProps::kBackgroundPositionKTable,
5015 0 : junk)) {
5016 0 : haveGradientLine = true;
5017 : }
5018 0 : break;
5019 : }
5020 :
5021 : default:
5022 : // error
5023 0 : SkipUntil(')');
5024 0 : return false;
5025 : }
5026 :
5027 0 : if (haveGradientLine) {
5028 0 : if (toCorner) {
5029 : // "to" syntax only allows box position keywords
5030 0 : if (ty != eCSSToken_Ident) {
5031 0 : SkipUntil(')');
5032 0 : return false;
5033 : }
5034 :
5035 : // "to" syntax doesn't allow explicit "center"
5036 0 : if (!ParseBoxPositionValues(cssGradient->mBgPos, false, false)) {
5037 0 : SkipUntil(')');
5038 0 : return false;
5039 : }
5040 :
5041 0 : const nsCSSValue& xValue = cssGradient->mBgPos.mXValue;
5042 0 : const nsCSSValue& yValue = cssGradient->mBgPos.mYValue;
5043 0 : if (xValue.GetUnit() != eCSSUnit_Enumerated ||
5044 0 : !(xValue.GetIntValue() & (NS_STYLE_BG_POSITION_LEFT |
5045 : NS_STYLE_BG_POSITION_CENTER |
5046 0 : NS_STYLE_BG_POSITION_RIGHT)) ||
5047 0 : yValue.GetUnit() != eCSSUnit_Enumerated ||
5048 0 : !(yValue.GetIntValue() & (NS_STYLE_BG_POSITION_TOP |
5049 : NS_STYLE_BG_POSITION_CENTER |
5050 0 : NS_STYLE_BG_POSITION_BOTTOM))) {
5051 0 : SkipUntil(')');
5052 0 : return false;
5053 : }
5054 :
5055 0 : if (!ExpectSymbol(',', true)) {
5056 0 : SkipUntil(')');
5057 0 : return false;
5058 : }
5059 : } else {
5060 : bool haveAngle =
5061 0 : ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nsnull);
5062 :
5063 : // if we got an angle, we might now have a comma, ending the gradient-line
5064 0 : if (!haveAngle || !ExpectSymbol(',', true)) {
5065 0 : if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
5066 0 : SkipUntil(')');
5067 0 : return false;
5068 : }
5069 :
5070 0 : if (!ExpectSymbol(',', true) &&
5071 : // if we didn't already get an angle, we might have one now,
5072 : // otherwise it's an error
5073 : (haveAngle ||
5074 0 : !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nsnull) ||
5075 : // now we better have a comma
5076 0 : !ExpectSymbol(',', true))) {
5077 0 : SkipUntil(')');
5078 0 : return false;
5079 : }
5080 : }
5081 : }
5082 : }
5083 :
5084 : // radial gradients might have a <gradient-shape-size> here
5085 0 : if (aIsRadial) {
5086 : bool haveShape =
5087 0 : ParseVariant(cssGradient->mRadialShape, VARIANT_KEYWORD,
5088 0 : nsCSSProps::kRadialGradientShapeKTable);
5089 : bool haveSize =
5090 0 : ParseVariant(cssGradient->mRadialSize, VARIANT_KEYWORD,
5091 0 : nsCSSProps::kRadialGradientSizeKTable);
5092 :
5093 : // could be in either order
5094 0 : if (!haveShape) {
5095 : haveShape =
5096 0 : ParseVariant(cssGradient->mRadialShape, VARIANT_KEYWORD,
5097 0 : nsCSSProps::kRadialGradientShapeKTable);
5098 : }
5099 0 : if ((haveShape || haveSize) && !ExpectSymbol(',', true)) {
5100 0 : SkipUntil(')');
5101 0 : return false;
5102 : }
5103 : }
5104 :
5105 : // At least two color stops are required
5106 0 : if (!ParseColorStop(cssGradient) ||
5107 0 : !ExpectSymbol(',', true) ||
5108 0 : !ParseColorStop(cssGradient)) {
5109 0 : SkipUntil(')');
5110 0 : return false;
5111 : }
5112 :
5113 : // Additional color stops
5114 0 : while (ExpectSymbol(',', true)) {
5115 0 : if (!ParseColorStop(cssGradient)) {
5116 0 : SkipUntil(')');
5117 0 : return false;
5118 : }
5119 : }
5120 :
5121 0 : if (!ExpectSymbol(')', true)) {
5122 0 : SkipUntil(')');
5123 0 : return false;
5124 : }
5125 :
5126 0 : aValue.SetGradientValue(cssGradient);
5127 0 : return true;
5128 : }
5129 :
5130 : PRInt32
5131 0 : CSSParserImpl::ParseChoice(nsCSSValue aValues[],
5132 : const nsCSSProperty aPropIDs[], PRInt32 aNumIDs)
5133 : {
5134 0 : PRInt32 found = 0;
5135 0 : nsAutoParseCompoundProperty compound(this);
5136 :
5137 : PRInt32 loop;
5138 0 : for (loop = 0; loop < aNumIDs; loop++) {
5139 : // Try each property parser in order
5140 0 : PRInt32 hadFound = found;
5141 : PRInt32 index;
5142 0 : for (index = 0; index < aNumIDs; index++) {
5143 0 : PRInt32 bit = 1 << index;
5144 0 : if ((found & bit) == 0) {
5145 0 : if (ParseSingleValueProperty(aValues[index], aPropIDs[index])) {
5146 0 : found |= bit;
5147 : // It's more efficient to break since it will reset |hadFound|
5148 : // to |found|. Furthermore, ParseListStyle depends on our going
5149 : // through the properties in order for each value..
5150 0 : break;
5151 : }
5152 : }
5153 : }
5154 0 : if (found == hadFound) { // found nothing new
5155 0 : break;
5156 : }
5157 : }
5158 0 : if (0 < found) {
5159 0 : if (1 == found) { // only first property
5160 0 : if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
5161 0 : for (loop = 1; loop < aNumIDs; loop++) {
5162 0 : aValues[loop].SetInheritValue();
5163 : }
5164 0 : found = ((1 << aNumIDs) - 1);
5165 : }
5166 0 : else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
5167 0 : for (loop = 1; loop < aNumIDs; loop++) {
5168 0 : aValues[loop].SetInitialValue();
5169 : }
5170 0 : found = ((1 << aNumIDs) - 1);
5171 : }
5172 : }
5173 : else { // more than one value, verify no inherits or initials
5174 0 : for (loop = 0; loop < aNumIDs; loop++) {
5175 0 : if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
5176 0 : found = -1;
5177 0 : break;
5178 : }
5179 0 : else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
5180 0 : found = -1;
5181 0 : break;
5182 : }
5183 : }
5184 : }
5185 : }
5186 0 : return found;
5187 : }
5188 :
5189 : void
5190 0 : CSSParserImpl::AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue)
5191 : {
5192 0 : mTempData.AddLonghandProperty(aPropID, aValue);
5193 0 : }
5194 :
5195 : /**
5196 : * Parse a "box" property. Box properties have 1 to 4 values. When less
5197 : * than 4 values are provided a standard mapping is used to replicate
5198 : * existing values.
5199 : */
5200 : bool
5201 0 : CSSParserImpl::ParseBoxProperties(const nsCSSProperty aPropIDs[])
5202 : {
5203 : // Get up to four values for the property
5204 0 : PRInt32 count = 0;
5205 0 : nsCSSRect result;
5206 0 : NS_FOR_CSS_SIDES (index) {
5207 0 : if (! ParseSingleValueProperty(result.*(nsCSSRect::sides[index]),
5208 0 : aPropIDs[index])) {
5209 0 : break;
5210 : }
5211 0 : count++;
5212 : }
5213 0 : if ((count == 0) || (false == ExpectEndProperty())) {
5214 0 : return false;
5215 : }
5216 :
5217 0 : if (1 < count) { // verify no more than single inherit or initial
5218 0 : NS_FOR_CSS_SIDES (index) {
5219 0 : nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
5220 0 : if (eCSSUnit_Inherit == unit || eCSSUnit_Initial == unit) {
5221 0 : return false;
5222 : }
5223 : }
5224 : }
5225 :
5226 : // Provide missing values by replicating some of the values found
5227 0 : switch (count) {
5228 : case 1: // Make right == top
5229 0 : result.mRight = result.mTop;
5230 : case 2: // Make bottom == top
5231 0 : result.mBottom = result.mTop;
5232 : case 3: // Make left == right
5233 0 : result.mLeft = result.mRight;
5234 : }
5235 :
5236 0 : NS_FOR_CSS_SIDES (index) {
5237 0 : AppendValue(aPropIDs[index], result.*(nsCSSRect::sides[index]));
5238 : }
5239 0 : return true;
5240 : }
5241 :
5242 : // Similar to ParseBoxProperties, except there is only one property
5243 : // with the result as its value, not four. Requires values be nonnegative.
5244 : bool
5245 0 : CSSParserImpl::ParseGroupedBoxProperty(PRInt32 aVariantMask,
5246 : /** outparam */ nsCSSValue& aValue)
5247 : {
5248 0 : nsCSSRect& result = aValue.SetRectValue();
5249 :
5250 0 : PRInt32 count = 0;
5251 0 : NS_FOR_CSS_SIDES (index) {
5252 0 : if (!ParseNonNegativeVariant(result.*(nsCSSRect::sides[index]),
5253 0 : aVariantMask, nsnull)) {
5254 0 : break;
5255 : }
5256 0 : count++;
5257 : }
5258 :
5259 0 : if (count == 0) {
5260 0 : return false;
5261 : }
5262 :
5263 : // Provide missing values by replicating some of the values found
5264 0 : switch (count) {
5265 : case 1: // Make right == top
5266 0 : result.mRight = result.mTop;
5267 : case 2: // Make bottom == top
5268 0 : result.mBottom = result.mTop;
5269 : case 3: // Make left == right
5270 0 : result.mLeft = result.mRight;
5271 : }
5272 :
5273 0 : return true;
5274 : }
5275 :
5276 : bool
5277 0 : CSSParserImpl::ParseDirectionalBoxProperty(nsCSSProperty aProperty,
5278 : PRInt32 aSourceType)
5279 : {
5280 0 : const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(aProperty);
5281 0 : NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
5282 : "not box property with physical vs. logical cascading");
5283 0 : nsCSSValue value;
5284 0 : if (!ParseSingleValueProperty(value, subprops[0]) ||
5285 0 : !ExpectEndProperty())
5286 0 : return false;
5287 :
5288 0 : AppendValue(subprops[0], value);
5289 0 : nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
5290 0 : AppendValue(subprops[1], typeVal);
5291 0 : AppendValue(subprops[2], typeVal);
5292 0 : return true;
5293 : }
5294 :
5295 : bool
5296 0 : CSSParserImpl::ParseBoxCornerRadius(nsCSSProperty aPropID)
5297 : {
5298 0 : nsCSSValue dimenX, dimenY;
5299 : // required first value
5300 0 : if (! ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nsnull))
5301 0 : return false;
5302 :
5303 : // optional second value (forbidden if first value is inherit/initial)
5304 0 : if (dimenX.GetUnit() != eCSSUnit_Inherit &&
5305 0 : dimenX.GetUnit() != eCSSUnit_Initial) {
5306 0 : ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nsnull);
5307 : }
5308 :
5309 0 : if (dimenX == dimenY || dimenY.GetUnit() == eCSSUnit_Null) {
5310 0 : AppendValue(aPropID, dimenX);
5311 : } else {
5312 0 : nsCSSValue value;
5313 0 : value.SetPairValue(dimenX, dimenY);
5314 0 : AppendValue(aPropID, value);
5315 : }
5316 0 : return true;
5317 : }
5318 :
5319 : bool
5320 0 : CSSParserImpl::ParseBoxCornerRadii(const nsCSSProperty aPropIDs[])
5321 : {
5322 : // Rectangles are used as scratch storage.
5323 : // top => top-left, right => top-right,
5324 : // bottom => bottom-right, left => bottom-left.
5325 0 : nsCSSRect dimenX, dimenY;
5326 0 : PRInt32 countX = 0, countY = 0;
5327 :
5328 0 : NS_FOR_CSS_SIDES (side) {
5329 0 : if (! ParseNonNegativeVariant(dimenX.*nsCSSRect::sides[side],
5330 : (side > 0 ? 0 : VARIANT_INHERIT) |
5331 : VARIANT_LP | VARIANT_CALC,
5332 0 : nsnull))
5333 0 : break;
5334 0 : countX++;
5335 : }
5336 0 : if (countX == 0)
5337 0 : return false;
5338 :
5339 0 : if (ExpectSymbol('/', true)) {
5340 0 : NS_FOR_CSS_SIDES (side) {
5341 0 : if (! ParseNonNegativeVariant(dimenY.*nsCSSRect::sides[side],
5342 0 : VARIANT_LP | VARIANT_CALC, nsnull))
5343 0 : break;
5344 0 : countY++;
5345 : }
5346 0 : if (countY == 0)
5347 0 : return false;
5348 : }
5349 0 : if (!ExpectEndProperty())
5350 0 : return false;
5351 :
5352 : // if 'initial' or 'inherit' was used, it must be the only value
5353 0 : if (countX > 1 || countY > 0) {
5354 0 : nsCSSUnit unit = dimenX.mTop.GetUnit();
5355 0 : if (eCSSUnit_Inherit == unit || eCSSUnit_Initial == unit)
5356 0 : return false;
5357 : }
5358 :
5359 : // if we have no Y-values, use the X-values
5360 0 : if (countY == 0) {
5361 0 : dimenY = dimenX;
5362 0 : countY = countX;
5363 : }
5364 :
5365 : // Provide missing values by replicating some of the values found
5366 0 : switch (countX) {
5367 0 : case 1: dimenX.mRight = dimenX.mTop; // top-right same as top-left, and
5368 0 : case 2: dimenX.mBottom = dimenX.mTop; // bottom-right same as top-left, and
5369 0 : case 3: dimenX.mLeft = dimenX.mRight; // bottom-left same as top-right
5370 : }
5371 :
5372 0 : switch (countY) {
5373 0 : case 1: dimenY.mRight = dimenY.mTop; // top-right same as top-left, and
5374 0 : case 2: dimenY.mBottom = dimenY.mTop; // bottom-right same as top-left, and
5375 0 : case 3: dimenY.mLeft = dimenY.mRight; // bottom-left same as top-right
5376 : }
5377 :
5378 0 : NS_FOR_CSS_SIDES(side) {
5379 0 : nsCSSValue& x = dimenX.*nsCSSRect::sides[side];
5380 0 : nsCSSValue& y = dimenY.*nsCSSRect::sides[side];
5381 :
5382 0 : if (x == y) {
5383 0 : AppendValue(aPropIDs[side], x);
5384 : } else {
5385 0 : nsCSSValue pair;
5386 0 : pair.SetPairValue(x, y);
5387 0 : AppendValue(aPropIDs[side], pair);
5388 : }
5389 : }
5390 0 : return true;
5391 : }
5392 :
5393 : // These must be in CSS order (top,right,bottom,left) for indexing to work
5394 : static const nsCSSProperty kBorderStyleIDs[] = {
5395 : eCSSProperty_border_top_style,
5396 : eCSSProperty_border_right_style_value,
5397 : eCSSProperty_border_bottom_style,
5398 : eCSSProperty_border_left_style_value
5399 : };
5400 : static const nsCSSProperty kBorderWidthIDs[] = {
5401 : eCSSProperty_border_top_width,
5402 : eCSSProperty_border_right_width_value,
5403 : eCSSProperty_border_bottom_width,
5404 : eCSSProperty_border_left_width_value
5405 : };
5406 : static const nsCSSProperty kBorderColorIDs[] = {
5407 : eCSSProperty_border_top_color,
5408 : eCSSProperty_border_right_color_value,
5409 : eCSSProperty_border_bottom_color,
5410 : eCSSProperty_border_left_color_value
5411 : };
5412 : static const nsCSSProperty kBorderRadiusIDs[] = {
5413 : eCSSProperty_border_top_left_radius,
5414 : eCSSProperty_border_top_right_radius,
5415 : eCSSProperty_border_bottom_right_radius,
5416 : eCSSProperty_border_bottom_left_radius
5417 : };
5418 : static const nsCSSProperty kOutlineRadiusIDs[] = {
5419 : eCSSProperty__moz_outline_radius_topLeft,
5420 : eCSSProperty__moz_outline_radius_topRight,
5421 : eCSSProperty__moz_outline_radius_bottomRight,
5422 : eCSSProperty__moz_outline_radius_bottomLeft
5423 : };
5424 :
5425 : bool
5426 0 : CSSParserImpl::ParseProperty(nsCSSProperty aPropID)
5427 : {
5428 0 : NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range");
5429 0 : switch (nsCSSProps::PropertyParseType(aPropID)) {
5430 : case CSS_PROPERTY_PARSE_INACCESSIBLE: {
5431 : // The user can't use these
5432 0 : REPORT_UNEXPECTED(PEInaccessibleProperty2);
5433 0 : return false;
5434 : }
5435 : case CSS_PROPERTY_PARSE_FUNCTION: {
5436 0 : return ParsePropertyByFunction(aPropID);
5437 : }
5438 : case CSS_PROPERTY_PARSE_VALUE: {
5439 0 : nsCSSValue value;
5440 0 : if (ParseSingleValueProperty(value, aPropID)) {
5441 0 : if (ExpectEndProperty()) {
5442 0 : AppendValue(aPropID, value);
5443 0 : return true;
5444 : }
5445 : // XXX Report errors?
5446 : }
5447 : // XXX Report errors?
5448 0 : return false;
5449 : }
5450 : case CSS_PROPERTY_PARSE_VALUE_LIST: {
5451 0 : return ParseValueList(aPropID);
5452 : }
5453 : }
5454 0 : NS_ABORT_IF_FALSE(false,
5455 : "Property's flags field in nsCSSPropList.h is missing "
5456 : "one of the CSS_PROPERTY_PARSE_* constants");
5457 0 : return false;
5458 : }
5459 :
5460 : bool
5461 0 : CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID)
5462 : {
5463 0 : switch (aPropID) { // handle shorthand or multiple properties
5464 : case eCSSProperty_background:
5465 0 : return ParseBackground();
5466 : case eCSSProperty_background_repeat:
5467 0 : return ParseBackgroundRepeat();
5468 : case eCSSProperty_background_position:
5469 0 : return ParseBackgroundPosition();
5470 : case eCSSProperty_background_size:
5471 0 : return ParseBackgroundSize();
5472 : case eCSSProperty_border:
5473 0 : return ParseBorderSide(kBorderTopIDs, true);
5474 : case eCSSProperty_border_color:
5475 0 : return ParseBorderColor();
5476 : case eCSSProperty_border_spacing:
5477 0 : return ParseBorderSpacing();
5478 : case eCSSProperty_border_style:
5479 0 : return ParseBorderStyle();
5480 : case eCSSProperty_border_bottom:
5481 0 : return ParseBorderSide(kBorderBottomIDs, false);
5482 : case eCSSProperty_border_end:
5483 : return ParseDirectionalBorderSide(kBorderEndIDs,
5484 0 : NS_BOXPROP_SOURCE_LOGICAL);
5485 : case eCSSProperty_border_left:
5486 : return ParseDirectionalBorderSide(kBorderLeftIDs,
5487 0 : NS_BOXPROP_SOURCE_PHYSICAL);
5488 : case eCSSProperty_border_right:
5489 : return ParseDirectionalBorderSide(kBorderRightIDs,
5490 0 : NS_BOXPROP_SOURCE_PHYSICAL);
5491 : case eCSSProperty_border_start:
5492 : return ParseDirectionalBorderSide(kBorderStartIDs,
5493 0 : NS_BOXPROP_SOURCE_LOGICAL);
5494 : case eCSSProperty_border_top:
5495 0 : return ParseBorderSide(kBorderTopIDs, false);
5496 : case eCSSProperty_border_bottom_colors:
5497 : case eCSSProperty_border_left_colors:
5498 : case eCSSProperty_border_right_colors:
5499 : case eCSSProperty_border_top_colors:
5500 0 : return ParseBorderColors(aPropID);
5501 : case eCSSProperty_border_image_slice:
5502 0 : return ParseBorderImageSlice(true, nsnull);
5503 : case eCSSProperty_border_image_width:
5504 0 : return ParseBorderImageWidth(true);
5505 : case eCSSProperty_border_image_outset:
5506 0 : return ParseBorderImageOutset(true);
5507 : case eCSSProperty_border_image_repeat:
5508 0 : return ParseBorderImageRepeat(true);
5509 : case eCSSProperty_border_image:
5510 0 : return ParseBorderImage();
5511 : case eCSSProperty_border_width:
5512 0 : return ParseBorderWidth();
5513 : case eCSSProperty_border_end_color:
5514 : return ParseDirectionalBoxProperty(eCSSProperty_border_end_color,
5515 0 : NS_BOXPROP_SOURCE_LOGICAL);
5516 : case eCSSProperty_border_left_color:
5517 : return ParseDirectionalBoxProperty(eCSSProperty_border_left_color,
5518 0 : NS_BOXPROP_SOURCE_PHYSICAL);
5519 : case eCSSProperty_border_right_color:
5520 : return ParseDirectionalBoxProperty(eCSSProperty_border_right_color,
5521 0 : NS_BOXPROP_SOURCE_PHYSICAL);
5522 : case eCSSProperty_border_start_color:
5523 : return ParseDirectionalBoxProperty(eCSSProperty_border_start_color,
5524 0 : NS_BOXPROP_SOURCE_LOGICAL);
5525 : case eCSSProperty_border_end_width:
5526 : return ParseDirectionalBoxProperty(eCSSProperty_border_end_width,
5527 0 : NS_BOXPROP_SOURCE_LOGICAL);
5528 : case eCSSProperty_border_left_width:
5529 : return ParseDirectionalBoxProperty(eCSSProperty_border_left_width,
5530 0 : NS_BOXPROP_SOURCE_PHYSICAL);
5531 : case eCSSProperty_border_right_width:
5532 : return ParseDirectionalBoxProperty(eCSSProperty_border_right_width,
5533 0 : NS_BOXPROP_SOURCE_PHYSICAL);
5534 : case eCSSProperty_border_start_width:
5535 : return ParseDirectionalBoxProperty(eCSSProperty_border_start_width,
5536 0 : NS_BOXPROP_SOURCE_LOGICAL);
5537 : case eCSSProperty_border_end_style:
5538 : return ParseDirectionalBoxProperty(eCSSProperty_border_end_style,
5539 0 : NS_BOXPROP_SOURCE_LOGICAL);
5540 : case eCSSProperty_border_left_style:
5541 : return ParseDirectionalBoxProperty(eCSSProperty_border_left_style,
5542 0 : NS_BOXPROP_SOURCE_PHYSICAL);
5543 : case eCSSProperty_border_right_style:
5544 : return ParseDirectionalBoxProperty(eCSSProperty_border_right_style,
5545 0 : NS_BOXPROP_SOURCE_PHYSICAL);
5546 : case eCSSProperty_border_start_style:
5547 : return ParseDirectionalBoxProperty(eCSSProperty_border_start_style,
5548 0 : NS_BOXPROP_SOURCE_LOGICAL);
5549 : case eCSSProperty_border_radius:
5550 0 : return ParseBoxCornerRadii(kBorderRadiusIDs);
5551 : case eCSSProperty__moz_outline_radius:
5552 0 : return ParseBoxCornerRadii(kOutlineRadiusIDs);
5553 :
5554 : case eCSSProperty_border_top_left_radius:
5555 : case eCSSProperty_border_top_right_radius:
5556 : case eCSSProperty_border_bottom_right_radius:
5557 : case eCSSProperty_border_bottom_left_radius:
5558 : case eCSSProperty__moz_outline_radius_topLeft:
5559 : case eCSSProperty__moz_outline_radius_topRight:
5560 : case eCSSProperty__moz_outline_radius_bottomRight:
5561 : case eCSSProperty__moz_outline_radius_bottomLeft:
5562 0 : return ParseBoxCornerRadius(aPropID);
5563 :
5564 : case eCSSProperty_box_shadow:
5565 : case eCSSProperty_text_shadow:
5566 0 : return ParseShadowList(aPropID);
5567 :
5568 : case eCSSProperty_clip:
5569 0 : return ParseRect(eCSSProperty_clip);
5570 : case eCSSProperty__moz_columns:
5571 0 : return ParseColumns();
5572 : case eCSSProperty__moz_column_rule:
5573 0 : return ParseBorderSide(kColumnRuleIDs, false);
5574 : case eCSSProperty_content:
5575 0 : return ParseContent();
5576 : case eCSSProperty_counter_increment:
5577 : case eCSSProperty_counter_reset:
5578 0 : return ParseCounterData(aPropID);
5579 : case eCSSProperty_cursor:
5580 0 : return ParseCursor();
5581 : case eCSSProperty_font:
5582 0 : return ParseFont();
5583 : case eCSSProperty_image_region:
5584 0 : return ParseRect(eCSSProperty_image_region);
5585 : case eCSSProperty_list_style:
5586 0 : return ParseListStyle();
5587 : case eCSSProperty_margin:
5588 0 : return ParseMargin();
5589 : case eCSSProperty_margin_end:
5590 : return ParseDirectionalBoxProperty(eCSSProperty_margin_end,
5591 0 : NS_BOXPROP_SOURCE_LOGICAL);
5592 : case eCSSProperty_margin_left:
5593 : return ParseDirectionalBoxProperty(eCSSProperty_margin_left,
5594 0 : NS_BOXPROP_SOURCE_PHYSICAL);
5595 : case eCSSProperty_margin_right:
5596 : return ParseDirectionalBoxProperty(eCSSProperty_margin_right,
5597 0 : NS_BOXPROP_SOURCE_PHYSICAL);
5598 : case eCSSProperty_margin_start:
5599 : return ParseDirectionalBoxProperty(eCSSProperty_margin_start,
5600 0 : NS_BOXPROP_SOURCE_LOGICAL);
5601 : case eCSSProperty_outline:
5602 0 : return ParseOutline();
5603 : case eCSSProperty_overflow:
5604 0 : return ParseOverflow();
5605 : case eCSSProperty_padding:
5606 0 : return ParsePadding();
5607 : case eCSSProperty_padding_end:
5608 : return ParseDirectionalBoxProperty(eCSSProperty_padding_end,
5609 0 : NS_BOXPROP_SOURCE_LOGICAL);
5610 : case eCSSProperty_padding_left:
5611 : return ParseDirectionalBoxProperty(eCSSProperty_padding_left,
5612 0 : NS_BOXPROP_SOURCE_PHYSICAL);
5613 : case eCSSProperty_padding_right:
5614 : return ParseDirectionalBoxProperty(eCSSProperty_padding_right,
5615 0 : NS_BOXPROP_SOURCE_PHYSICAL);
5616 : case eCSSProperty_padding_start:
5617 : return ParseDirectionalBoxProperty(eCSSProperty_padding_start,
5618 0 : NS_BOXPROP_SOURCE_LOGICAL);
5619 : case eCSSProperty_quotes:
5620 0 : return ParseQuotes();
5621 : case eCSSProperty_size:
5622 0 : return ParseSize();
5623 : case eCSSProperty_text_decoration:
5624 0 : return ParseTextDecoration();
5625 : case eCSSProperty__moz_transform:
5626 0 : return ParseMozTransform();
5627 : case eCSSProperty__moz_transform_origin:
5628 0 : return ParseMozTransformOrigin(false);
5629 : case eCSSProperty_perspective_origin:
5630 0 : return ParseMozTransformOrigin(true);
5631 : case eCSSProperty_transition:
5632 0 : return ParseTransition();
5633 : case eCSSProperty_animation:
5634 0 : return ParseAnimation();
5635 : case eCSSProperty_transition_property:
5636 0 : return ParseTransitionProperty();
5637 : case eCSSProperty_fill:
5638 : case eCSSProperty_stroke:
5639 0 : return ParsePaint(aPropID);
5640 : case eCSSProperty_stroke_dasharray:
5641 0 : return ParseDasharray();
5642 : case eCSSProperty_marker:
5643 0 : return ParseMarker();
5644 : default:
5645 0 : NS_ABORT_IF_FALSE(false, "should not be called");
5646 0 : return false;
5647 : }
5648 : }
5649 :
5650 : // Bits used in determining which background position info we have
5651 : #define BG_CENTER NS_STYLE_BG_POSITION_CENTER
5652 : #define BG_TOP NS_STYLE_BG_POSITION_TOP
5653 : #define BG_BOTTOM NS_STYLE_BG_POSITION_BOTTOM
5654 : #define BG_LEFT NS_STYLE_BG_POSITION_LEFT
5655 : #define BG_RIGHT NS_STYLE_BG_POSITION_RIGHT
5656 : #define BG_CTB (BG_CENTER | BG_TOP | BG_BOTTOM)
5657 : #define BG_TB (BG_TOP | BG_BOTTOM)
5658 : #define BG_CLR (BG_CENTER | BG_LEFT | BG_RIGHT)
5659 : #define BG_LR (BG_LEFT | BG_RIGHT)
5660 :
5661 : bool
5662 0 : CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
5663 : nsCSSProperty aPropID)
5664 : {
5665 0 : if (aPropID == eCSSPropertyExtra_x_none_value) {
5666 0 : return ParseVariant(aValue, VARIANT_NONE | VARIANT_INHERIT, nsnull);
5667 : }
5668 :
5669 0 : if (aPropID == eCSSPropertyExtra_x_auto_value) {
5670 0 : return ParseVariant(aValue, VARIANT_AUTO | VARIANT_INHERIT, nsnull);
5671 : }
5672 :
5673 0 : if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) {
5674 0 : NS_ABORT_IF_FALSE(false, "not a single value property");
5675 0 : return false;
5676 : }
5677 :
5678 0 : if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_VALUE_PARSER_FUNCTION)) {
5679 0 : switch (aPropID) {
5680 : case eCSSProperty_font_family:
5681 0 : return ParseFamily(aValue);
5682 : case eCSSProperty_font_weight:
5683 0 : return ParseFontWeight(aValue);
5684 : case eCSSProperty_marks:
5685 0 : return ParseMarks(aValue);
5686 : case eCSSProperty_text_decoration_line:
5687 0 : return ParseTextDecorationLine(aValue);
5688 : case eCSSProperty_text_overflow:
5689 0 : return ParseTextOverflow(aValue);
5690 : case eCSSProperty_unicode_bidi:
5691 0 : return ParseUnicodeBidi(aValue);
5692 : default:
5693 0 : NS_ABORT_IF_FALSE(false, "should not reach here");
5694 0 : return false;
5695 : }
5696 : }
5697 :
5698 0 : PRUint32 variant = nsCSSProps::ParserVariant(aPropID);
5699 0 : if (variant == 0) {
5700 0 : NS_ABORT_IF_FALSE(false, "not a single value property");
5701 0 : return false;
5702 : }
5703 :
5704 : // We only allow 'script-level' when unsafe rules are enabled, because
5705 : // otherwise it could interfere with rulenode optimizations if used in
5706 : // a non-MathML-enabled document.
5707 0 : if (aPropID == eCSSProperty_script_level && !mUnsafeRulesEnabled)
5708 0 : return false;
5709 :
5710 0 : const PRInt32 *kwtable = nsCSSProps::kKeywordTableTable[aPropID];
5711 0 : switch (nsCSSProps::ValueRestrictions(aPropID)) {
5712 : default:
5713 0 : NS_ABORT_IF_FALSE(false, "should not be reached");
5714 : case 0:
5715 0 : return ParseVariant(aValue, variant, kwtable);
5716 : case CSS_PROPERTY_VALUE_NONNEGATIVE:
5717 0 : return ParseNonNegativeVariant(aValue, variant, kwtable);
5718 : case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
5719 0 : return ParseOneOrLargerVariant(aValue, variant, kwtable);
5720 : }
5721 : }
5722 :
5723 : // nsFont::EnumerateFamilies callback for ParseFontDescriptorValue
5724 0 : struct NS_STACK_CLASS ExtractFirstFamilyData {
5725 : nsAutoString mFamilyName;
5726 : bool mGood;
5727 0 : ExtractFirstFamilyData() : mFamilyName(), mGood(false) {}
5728 : };
5729 :
5730 : static bool
5731 0 : ExtractFirstFamily(const nsString& aFamily,
5732 : bool aGeneric,
5733 : void* aData)
5734 : {
5735 0 : ExtractFirstFamilyData* realData = (ExtractFirstFamilyData*) aData;
5736 0 : if (aGeneric || realData->mFamilyName.Length() > 0) {
5737 0 : realData->mGood = false;
5738 0 : return false;
5739 : }
5740 0 : realData->mFamilyName.Assign(aFamily);
5741 0 : realData->mGood = true;
5742 0 : return true;
5743 : }
5744 :
5745 : // font-descriptor: descriptor ':' value ';'
5746 : // caller has advanced mToken to point at the descriptor
5747 : bool
5748 0 : CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID,
5749 : nsCSSValue& aValue)
5750 : {
5751 0 : switch (aDescID) {
5752 : // These four are similar to the properties of the same name,
5753 : // possibly with more restrictions on the values they can take.
5754 : case eCSSFontDesc_Family: {
5755 0 : if (!ParseFamily(aValue) ||
5756 0 : aValue.GetUnit() != eCSSUnit_Families)
5757 0 : return false;
5758 :
5759 : // the style parameters to the nsFont constructor are ignored,
5760 : // because it's only being used to call EnumerateFamilies
5761 0 : nsAutoString valueStr;
5762 0 : aValue.GetStringValue(valueStr);
5763 0 : nsFont font(valueStr, 0, 0, 0, 0, 0, 0);
5764 0 : ExtractFirstFamilyData dat;
5765 :
5766 0 : font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
5767 0 : if (!dat.mGood)
5768 0 : return false;
5769 :
5770 0 : aValue.SetStringValue(dat.mFamilyName, eCSSUnit_String);
5771 0 : return true;
5772 : }
5773 :
5774 : case eCSSFontDesc_Style:
5775 : // property is VARIANT_HMK|VARIANT_SYSFONT
5776 : return ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
5777 0 : nsCSSProps::kFontStyleKTable);
5778 :
5779 : case eCSSFontDesc_Weight:
5780 0 : return (ParseFontWeight(aValue) &&
5781 0 : aValue.GetUnit() != eCSSUnit_Inherit &&
5782 0 : aValue.GetUnit() != eCSSUnit_Initial &&
5783 0 : (aValue.GetUnit() != eCSSUnit_Enumerated ||
5784 0 : (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
5785 0 : aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
5786 :
5787 : case eCSSFontDesc_Stretch:
5788 : // property is VARIANT_HK|VARIANT_SYSFONT
5789 : return ParseVariant(aValue, VARIANT_KEYWORD,
5790 0 : nsCSSProps::kFontStretchKTable);
5791 :
5792 : // These two are unique to @font-face and have their own special grammar.
5793 : case eCSSFontDesc_Src:
5794 0 : return ParseFontSrc(aValue);
5795 :
5796 : case eCSSFontDesc_UnicodeRange:
5797 0 : return ParseFontRanges(aValue);
5798 :
5799 : case eCSSFontDesc_FontFeatureSettings:
5800 : case eCSSFontDesc_FontLanguageOverride:
5801 0 : return ParseVariant(aValue, VARIANT_NORMAL | VARIANT_STRING, nsnull);
5802 :
5803 : case eCSSFontDesc_UNKNOWN:
5804 : case eCSSFontDesc_COUNT:
5805 0 : NS_NOTREACHED("bad nsCSSFontDesc code");
5806 : }
5807 : // explicitly do NOT have a default case to let the compiler
5808 : // help find missing descriptors
5809 0 : return false;
5810 : }
5811 :
5812 : void
5813 0 : CSSParserImpl::InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties)
5814 : {
5815 0 : nsCSSValue physical(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
5816 0 : for (const nsCSSProperty *prop = aSourceProperties;
5817 : *prop != eCSSProperty_UNKNOWN; ++prop) {
5818 0 : AppendValue(*prop, physical);
5819 : }
5820 0 : }
5821 :
5822 : static nsCSSValue
5823 0 : BoxPositionMaskToCSSValue(PRInt32 aMask, bool isX)
5824 : {
5825 0 : PRInt32 val = NS_STYLE_BG_POSITION_CENTER;
5826 0 : if (isX) {
5827 0 : if (aMask & BG_LEFT) {
5828 0 : val = NS_STYLE_BG_POSITION_LEFT;
5829 : }
5830 0 : else if (aMask & BG_RIGHT) {
5831 0 : val = NS_STYLE_BG_POSITION_RIGHT;
5832 : }
5833 : }
5834 : else {
5835 0 : if (aMask & BG_TOP) {
5836 0 : val = NS_STYLE_BG_POSITION_TOP;
5837 : }
5838 0 : else if (aMask & BG_BOTTOM) {
5839 0 : val = NS_STYLE_BG_POSITION_BOTTOM;
5840 : }
5841 : }
5842 :
5843 0 : return nsCSSValue(val, eCSSUnit_Enumerated);
5844 : }
5845 :
5846 : bool
5847 0 : CSSParserImpl::ParseBackground()
5848 : {
5849 0 : nsAutoParseCompoundProperty compound(this);
5850 :
5851 : // background-color can only be set once, so it's not a list.
5852 0 : nsCSSValue color;
5853 :
5854 : // Check first for inherit/initial.
5855 0 : if (ParseVariant(color, VARIANT_INHERIT, nsnull)) {
5856 : // must be alone
5857 0 : if (!ExpectEndProperty()) {
5858 0 : return false;
5859 : }
5860 0 : for (const nsCSSProperty* subprops =
5861 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_background);
5862 : *subprops != eCSSProperty_UNKNOWN; ++subprops) {
5863 0 : AppendValue(*subprops, color);
5864 : }
5865 0 : return true;
5866 : }
5867 :
5868 0 : nsCSSValue image, repeat, attachment, clip, origin, position, size;
5869 : BackgroundParseState state(color, image.SetListValue(),
5870 : repeat.SetPairListValue(),
5871 : attachment.SetListValue(), clip.SetListValue(),
5872 : origin.SetListValue(), position.SetListValue(),
5873 0 : size.SetPairListValue());
5874 :
5875 0 : for (;;) {
5876 0 : if (!ParseBackgroundItem(state)) {
5877 0 : return false;
5878 : }
5879 0 : if (CheckEndProperty()) {
5880 : break;
5881 : }
5882 : // If we saw a color, this must be the last item.
5883 0 : if (color.GetUnit() != eCSSUnit_Null) {
5884 0 : REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
5885 0 : return false;
5886 : }
5887 : // Otherwise, a comma is mandatory.
5888 0 : if (!ExpectSymbol(',', true)) {
5889 0 : return false;
5890 : }
5891 : // Chain another entry on all the lists.
5892 0 : state.mImage->mNext = new nsCSSValueList;
5893 0 : state.mImage = state.mImage->mNext;
5894 0 : state.mRepeat->mNext = new nsCSSValuePairList;
5895 0 : state.mRepeat = state.mRepeat->mNext;
5896 0 : state.mAttachment->mNext = new nsCSSValueList;
5897 0 : state.mAttachment = state.mAttachment->mNext;
5898 0 : state.mClip->mNext = new nsCSSValueList;
5899 0 : state.mClip = state.mClip->mNext;
5900 0 : state.mOrigin->mNext = new nsCSSValueList;
5901 0 : state.mOrigin = state.mOrigin->mNext;
5902 0 : state.mPosition->mNext = new nsCSSValueList;
5903 0 : state.mPosition = state.mPosition->mNext;
5904 0 : state.mSize->mNext = new nsCSSValuePairList;
5905 0 : state.mSize = state.mSize->mNext;
5906 : }
5907 :
5908 : // If we get to this point without seeing a color, provide a default.
5909 0 : if (color.GetUnit() == eCSSUnit_Null) {
5910 0 : color.SetColorValue(NS_RGBA(0,0,0,0));
5911 : }
5912 :
5913 0 : AppendValue(eCSSProperty_background_image, image);
5914 0 : AppendValue(eCSSProperty_background_repeat, repeat);
5915 0 : AppendValue(eCSSProperty_background_attachment, attachment);
5916 0 : AppendValue(eCSSProperty_background_clip, clip);
5917 0 : AppendValue(eCSSProperty_background_origin, origin);
5918 0 : AppendValue(eCSSProperty_background_position, position);
5919 0 : AppendValue(eCSSProperty_background_size, size);
5920 0 : AppendValue(eCSSProperty_background_color, color);
5921 0 : return true;
5922 : }
5923 :
5924 : // Parse one item of the background shorthand property.
5925 : bool
5926 0 : CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState)
5927 :
5928 : {
5929 : // Fill in the values that the shorthand will set if we don't find
5930 : // other values.
5931 0 : aState.mImage->mValue.SetNoneValue();
5932 : aState.mRepeat->mXValue.SetIntValue(NS_STYLE_BG_REPEAT_REPEAT,
5933 0 : eCSSUnit_Enumerated);
5934 0 : aState.mRepeat->mYValue.Reset();
5935 : aState.mAttachment->mValue.SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL,
5936 0 : eCSSUnit_Enumerated);
5937 : aState.mClip->mValue.SetIntValue(NS_STYLE_BG_CLIP_BORDER,
5938 0 : eCSSUnit_Enumerated);
5939 : aState.mOrigin->mValue.SetIntValue(NS_STYLE_BG_ORIGIN_PADDING,
5940 0 : eCSSUnit_Enumerated);
5941 0 : nsRefPtr<nsCSSValue::Array> positionArr = nsCSSValue::Array::Create(4);
5942 0 : aState.mPosition->mValue.SetArrayValue(positionArr, eCSSUnit_Array);
5943 0 : positionArr->Item(1).SetPercentValue(0.0f);
5944 0 : positionArr->Item(3).SetPercentValue(0.0f);
5945 0 : aState.mSize->mXValue.SetAutoValue();
5946 0 : aState.mSize->mYValue.SetAutoValue();
5947 :
5948 0 : bool haveColor = false,
5949 0 : haveImage = false,
5950 0 : haveRepeat = false,
5951 0 : haveAttach = false,
5952 0 : havePosition = false,
5953 0 : haveOrigin = false,
5954 0 : haveSomething = false;
5955 :
5956 0 : while (GetToken(true)) {
5957 0 : nsCSSTokenType tt = mToken.mType;
5958 0 : UngetToken(); // ...but we'll still cheat and use mToken
5959 0 : if (tt == eCSSToken_Symbol) {
5960 : // ExpectEndProperty only looks for symbols, and nothing else will
5961 : // show up as one.
5962 0 : break;
5963 : }
5964 :
5965 0 : if (tt == eCSSToken_Ident) {
5966 0 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
5967 : PRInt32 dummy;
5968 0 : if (keyword == eCSSKeyword_inherit ||
5969 : keyword == eCSSKeyword__moz_initial) {
5970 0 : return false;
5971 0 : } else if (keyword == eCSSKeyword_none) {
5972 0 : if (haveImage)
5973 0 : return false;
5974 0 : haveImage = true;
5975 0 : if (!ParseSingleValueProperty(aState.mImage->mValue,
5976 0 : eCSSProperty_background_image)) {
5977 0 : NS_NOTREACHED("should be able to parse");
5978 0 : return false;
5979 : }
5980 0 : } else if (nsCSSProps::FindKeyword(keyword,
5981 : nsCSSProps::kBackgroundAttachmentKTable, dummy)) {
5982 0 : if (haveAttach)
5983 0 : return false;
5984 0 : haveAttach = true;
5985 0 : if (!ParseSingleValueProperty(aState.mAttachment->mValue,
5986 0 : eCSSProperty_background_attachment)) {
5987 0 : NS_NOTREACHED("should be able to parse");
5988 0 : return false;
5989 : }
5990 0 : } else if (nsCSSProps::FindKeyword(keyword,
5991 : nsCSSProps::kBackgroundRepeatKTable, dummy)) {
5992 0 : if (haveRepeat)
5993 0 : return false;
5994 0 : haveRepeat = true;
5995 0 : nsCSSValuePair scratch;
5996 0 : if (!ParseBackgroundRepeatValues(scratch)) {
5997 0 : NS_NOTREACHED("should be able to parse");
5998 0 : return false;
5999 : }
6000 0 : aState.mRepeat->mXValue = scratch.mXValue;
6001 0 : aState.mRepeat->mYValue = scratch.mYValue;
6002 0 : } else if (nsCSSProps::FindKeyword(keyword,
6003 : nsCSSProps::kBackgroundPositionKTable, dummy)) {
6004 0 : if (havePosition)
6005 0 : return false;
6006 0 : havePosition = true;
6007 0 : if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) {
6008 0 : return false;
6009 : }
6010 0 : } else if (nsCSSProps::FindKeyword(keyword,
6011 : nsCSSProps::kBackgroundOriginKTable, dummy)) {
6012 0 : if (haveOrigin)
6013 0 : return false;
6014 0 : haveOrigin = true;
6015 0 : if (!ParseSingleValueProperty(aState.mOrigin->mValue,
6016 0 : eCSSProperty_background_origin)) {
6017 0 : NS_NOTREACHED("should be able to parse");
6018 0 : return false;
6019 : }
6020 : MOZ_STATIC_ASSERT(NS_STYLE_BG_CLIP_BORDER ==
6021 : NS_STYLE_BG_ORIGIN_BORDER &&
6022 : NS_STYLE_BG_CLIP_PADDING ==
6023 : NS_STYLE_BG_ORIGIN_PADDING &&
6024 : NS_STYLE_BG_CLIP_CONTENT ==
6025 : NS_STYLE_BG_ORIGIN_CONTENT,
6026 : "bg-clip and bg-origin style constants must agree");
6027 :
6028 0 : aState.mClip->mValue = aState.mOrigin->mValue;
6029 : } else {
6030 0 : if (haveColor)
6031 0 : return false;
6032 0 : haveColor = true;
6033 0 : if (!ParseSingleValueProperty(aState.mColor,
6034 0 : eCSSProperty_background_color)) {
6035 0 : return false;
6036 : }
6037 : }
6038 0 : } else if (tt == eCSSToken_URL ||
6039 : (tt == eCSSToken_Function &&
6040 0 : (mToken.mIdent.LowerCaseEqualsLiteral("-moz-linear-gradient") ||
6041 0 : mToken.mIdent.LowerCaseEqualsLiteral("-moz-radial-gradient") ||
6042 0 : mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") ||
6043 0 : mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") ||
6044 0 : mToken.mIdent.LowerCaseEqualsLiteral("-moz-image-rect") ||
6045 0 : mToken.mIdent.LowerCaseEqualsLiteral("-moz-element")))) {
6046 0 : if (haveImage)
6047 0 : return false;
6048 0 : haveImage = true;
6049 0 : if (!ParseSingleValueProperty(aState.mImage->mValue,
6050 0 : eCSSProperty_background_image)) {
6051 0 : return false;
6052 : }
6053 0 : } else if (tt == eCSSToken_Dimension ||
6054 : tt == eCSSToken_Number ||
6055 : tt == eCSSToken_Percentage ||
6056 : (tt == eCSSToken_Function &&
6057 0 : mToken.mIdent.LowerCaseEqualsLiteral("-moz-calc"))) {
6058 0 : if (havePosition)
6059 0 : return false;
6060 0 : havePosition = true;
6061 0 : if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) {
6062 0 : return false;
6063 : }
6064 : } else {
6065 0 : if (haveColor)
6066 0 : return false;
6067 0 : haveColor = true;
6068 : // Note: This parses 'inherit' and 'initial', but
6069 : // we've already checked for them, so it's ok.
6070 0 : if (!ParseSingleValueProperty(aState.mColor,
6071 0 : eCSSProperty_background_color)) {
6072 0 : return false;
6073 : }
6074 : }
6075 0 : haveSomething = true;
6076 : }
6077 :
6078 0 : return haveSomething;
6079 : }
6080 :
6081 : // This function is very similar to ParseBackgroundPosition and
6082 : // ParseBackgroundSize.
6083 : bool
6084 0 : CSSParserImpl::ParseValueList(nsCSSProperty aPropID)
6085 : {
6086 : // aPropID is a single value prop-id
6087 0 : nsCSSValue value;
6088 0 : if (ParseVariant(value, VARIANT_INHERIT, nsnull)) {
6089 : // 'initial' and 'inherit' stand alone, no list permitted.
6090 0 : if (!ExpectEndProperty()) {
6091 0 : return false;
6092 : }
6093 : } else {
6094 0 : nsCSSValueList* item = value.SetListValue();
6095 0 : for (;;) {
6096 0 : if (!ParseSingleValueProperty(item->mValue, aPropID)) {
6097 0 : return false;
6098 : }
6099 0 : if (CheckEndProperty()) {
6100 0 : break;
6101 : }
6102 0 : if (!ExpectSymbol(',', true)) {
6103 0 : return false;
6104 : }
6105 0 : item->mNext = new nsCSSValueList;
6106 0 : item = item->mNext;
6107 : }
6108 : }
6109 0 : AppendValue(aPropID, value);
6110 0 : return true;
6111 : }
6112 :
6113 : bool
6114 0 : CSSParserImpl::ParseBackgroundRepeat()
6115 : {
6116 0 : nsCSSValue value;
6117 0 : if (ParseVariant(value, VARIANT_INHERIT, nsnull)) {
6118 : // 'initial' and 'inherit' stand alone, no list permitted.
6119 0 : if (!ExpectEndProperty()) {
6120 0 : return false;
6121 : }
6122 : } else {
6123 0 : nsCSSValuePair valuePair;
6124 0 : if (!ParseBackgroundRepeatValues(valuePair)) {
6125 0 : return false;
6126 : }
6127 0 : nsCSSValuePairList* item = value.SetPairListValue();
6128 0 : for (;;) {
6129 0 : item->mXValue = valuePair.mXValue;
6130 0 : item->mYValue = valuePair.mYValue;
6131 0 : if (CheckEndProperty()) {
6132 : break;
6133 : }
6134 0 : if (!ExpectSymbol(',', true)) {
6135 0 : return false;
6136 : }
6137 0 : if (!ParseBackgroundRepeatValues(valuePair)) {
6138 0 : return false;
6139 : }
6140 0 : item->mNext = new nsCSSValuePairList;
6141 0 : item = item->mNext;
6142 : }
6143 : }
6144 :
6145 0 : AppendValue(eCSSProperty_background_repeat, value);
6146 0 : return true;
6147 : }
6148 :
6149 : bool
6150 0 : CSSParserImpl::ParseBackgroundRepeatValues(nsCSSValuePair& aValue)
6151 : {
6152 0 : nsCSSValue& xValue = aValue.mXValue;
6153 0 : nsCSSValue& yValue = aValue.mYValue;
6154 :
6155 0 : if (ParseEnum(xValue, nsCSSProps::kBackgroundRepeatKTable)) {
6156 0 : PRInt32 value = xValue.GetIntValue();
6157 : // For single values set yValue as eCSSUnit_Null.
6158 0 : if (value == NS_STYLE_BG_REPEAT_REPEAT_X ||
6159 : value == NS_STYLE_BG_REPEAT_REPEAT_Y ||
6160 0 : !ParseEnum(yValue, nsCSSProps::kBackgroundRepeatPartKTable)) {
6161 : // the caller will fail cases like "repeat-x no-repeat"
6162 : // by expecting a list separator or an end property.
6163 0 : yValue.Reset();
6164 : }
6165 0 : return true;
6166 : }
6167 :
6168 0 : return false;
6169 : }
6170 :
6171 : // This function is very similar to ParseBackgroundList and ParseBackgroundSize.
6172 : bool
6173 0 : CSSParserImpl::ParseBackgroundPosition()
6174 : {
6175 0 : nsCSSValue value;
6176 0 : if (ParseVariant(value, VARIANT_INHERIT, nsnull)) {
6177 : // 'initial' and 'inherit' stand alone, no list permitted.
6178 0 : if (!ExpectEndProperty()) {
6179 0 : return false;
6180 : }
6181 : } else {
6182 0 : nsCSSValue itemValue;
6183 0 : if (!ParseBackgroundPositionValues(itemValue, false)) {
6184 0 : return false;
6185 : }
6186 0 : nsCSSValueList* item = value.SetListValue();
6187 0 : for (;;) {
6188 0 : item->mValue = itemValue;
6189 0 : if (CheckEndProperty()) {
6190 : break;
6191 : }
6192 0 : if (!ExpectSymbol(',', true)) {
6193 0 : return false;
6194 : }
6195 0 : if (!ParseBackgroundPositionValues(itemValue, false)) {
6196 0 : return false;
6197 : }
6198 0 : item->mNext = new nsCSSValueList;
6199 0 : item = item->mNext;
6200 : }
6201 : }
6202 0 : AppendValue(eCSSProperty_background_position, value);
6203 0 : return true;
6204 : }
6205 :
6206 : /**
6207 : * BoxPositionMaskToCSSValue and ParseBoxPositionValues are used
6208 : * for parsing the CSS 2.1 background-position syntax (which has at
6209 : * most two values). (Compare to the css3-background syntax which
6210 : * takes up to four values.) Some current CSS specifications that
6211 : * use background-position-like syntax still use this old syntax.
6212 : **
6213 : * Parses two values that correspond to positions in a box. These can be
6214 : * values corresponding to percentages of the box, raw offsets, or keywords
6215 : * like "top," "left center," etc.
6216 : *
6217 : * @param aOut The nsCSSValuePair in which to place the result.
6218 : * @param aAcceptsInherit If true, 'inherit' and 'initial' are legal values
6219 : * @param aAllowExplicitCenter If true, 'center' is a legal value
6220 : * @return Whether or not the operation succeeded.
6221 : */
6222 0 : bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut,
6223 : bool aAcceptsInherit,
6224 : bool aAllowExplicitCenter)
6225 : {
6226 : // First try a percentage or a length value
6227 0 : nsCSSValue &xValue = aOut.mXValue,
6228 0 : &yValue = aOut.mYValue;
6229 : PRInt32 variantMask =
6230 0 : (aAcceptsInherit ? VARIANT_INHERIT : 0) | VARIANT_LP | VARIANT_CALC;
6231 0 : if (ParseVariant(xValue, variantMask, nsnull)) {
6232 0 : if (eCSSUnit_Inherit == xValue.GetUnit() ||
6233 0 : eCSSUnit_Initial == xValue.GetUnit()) { // both are inherited or both are set to initial
6234 0 : yValue = xValue;
6235 0 : return true;
6236 : }
6237 : // We have one percentage/length/calc. Get the optional second
6238 : // percentage/length/calc/keyword.
6239 0 : if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nsnull)) {
6240 : // We have two numbers
6241 0 : return true;
6242 : }
6243 :
6244 0 : if (ParseEnum(yValue, nsCSSProps::kBackgroundPositionKTable)) {
6245 0 : PRInt32 yVal = yValue.GetIntValue();
6246 0 : if (!(yVal & BG_CTB)) {
6247 : // The second keyword can only be 'center', 'top', or 'bottom'
6248 0 : return false;
6249 : }
6250 0 : yValue = BoxPositionMaskToCSSValue(yVal, false);
6251 0 : return true;
6252 : }
6253 :
6254 : // If only one percentage or length value is given, it sets the
6255 : // horizontal position only, and the vertical position will be 50%.
6256 0 : yValue.SetPercentValue(0.5f);
6257 0 : return true;
6258 : }
6259 :
6260 : // Now try keywords. We do this manually to allow for the first
6261 : // appearance of "center" to apply to the either the x or y
6262 : // position (it's ambiguous so we have to disambiguate). Each
6263 : // allowed keyword value is assigned it's own bit. We don't allow
6264 : // any duplicate keywords other than center. We try to get two
6265 : // keywords but it's okay if there is only one.
6266 0 : PRInt32 mask = 0;
6267 0 : if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
6268 0 : PRInt32 bit = xValue.GetIntValue();
6269 0 : mask |= bit;
6270 0 : if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
6271 0 : bit = xValue.GetIntValue();
6272 0 : if (mask & (bit & ~BG_CENTER)) {
6273 : // Only the 'center' keyword can be duplicated.
6274 0 : return false;
6275 : }
6276 0 : mask |= bit;
6277 : }
6278 : else {
6279 : // Only one keyword. See if we have a length, percentage, or calc.
6280 0 : if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nsnull)) {
6281 0 : if (!(mask & BG_CLR)) {
6282 : // The first keyword can only be 'center', 'left', or 'right'
6283 0 : return false;
6284 : }
6285 :
6286 0 : xValue = BoxPositionMaskToCSSValue(mask, true);
6287 0 : return true;
6288 : }
6289 : }
6290 : }
6291 :
6292 : // Check for bad input. Bad input consists of no matching keywords,
6293 : // or pairs of x keywords or pairs of y keywords.
6294 0 : if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
6295 : (mask == (BG_LEFT | BG_RIGHT)) ||
6296 0 : (!aAllowExplicitCenter && (mask & BG_CENTER))) {
6297 0 : return false;
6298 : }
6299 :
6300 : // Create style values
6301 0 : xValue = BoxPositionMaskToCSSValue(mask, true);
6302 0 : yValue = BoxPositionMaskToCSSValue(mask, false);
6303 0 : return true;
6304 : }
6305 :
6306 0 : bool CSSParserImpl::ParseBackgroundPositionValues(nsCSSValue& aOut,
6307 : bool aAcceptsInherit)
6308 : {
6309 : // css3-background allows positions to be defined as offsets
6310 : // from an edge. There can be 2 keywords and 2 offsets given. These
6311 : // four 'values' are stored in an array in the following order:
6312 : // [keyword offset keyword offset]. If a keyword or offset isn't
6313 : // parsed the value of the corresponding array element is set
6314 : // to eCSSUnit_Null by a call to nsCSSValue::Reset().
6315 0 : if (aAcceptsInherit && ParseVariant(aOut, VARIANT_INHERIT, nsnull)) {
6316 0 : return true;
6317 : }
6318 :
6319 0 : nsRefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(4);
6320 0 : aOut.SetArrayValue(value, eCSSUnit_Array);
6321 :
6322 : // The following clarifies organisation of the array.
6323 0 : nsCSSValue &xEdge = value->Item(0),
6324 0 : &xOffset = value->Item(1),
6325 0 : &yEdge = value->Item(2),
6326 0 : &yOffset = value->Item(3);
6327 :
6328 : // Parse all the values into the array.
6329 0 : PRUint32 valueCount = 0;
6330 0 : for (PRInt32 i = 0; i < 4; i++) {
6331 0 : if (!ParseVariant(value->Item(i), VARIANT_LPCALC | VARIANT_KEYWORD,
6332 0 : nsCSSProps::kBackgroundPositionKTable)) {
6333 0 : break;
6334 : }
6335 0 : ++valueCount;
6336 : }
6337 :
6338 0 : switch (valueCount) {
6339 : case 4:
6340 : // "If three or four values are given, then each <percentage> or <length>
6341 : // represents an offset and must be preceded by a keyword, which specifies
6342 : // from which edge the offset is given."
6343 0 : if (eCSSUnit_Enumerated != xEdge.GetUnit() ||
6344 0 : BG_CENTER == xEdge.GetIntValue() ||
6345 0 : eCSSUnit_Enumerated == xOffset.GetUnit() ||
6346 0 : eCSSUnit_Enumerated != yEdge.GetUnit() ||
6347 0 : BG_CENTER == yEdge.GetIntValue() ||
6348 0 : eCSSUnit_Enumerated == yOffset.GetUnit()) {
6349 0 : return false;
6350 : }
6351 0 : break;
6352 : case 3:
6353 : // "If three or four values are given, then each <percentage> or<length>
6354 : // represents an offset and must be preceded by a keyword, which specifies
6355 : // from which edge the offset is given." ... "If three values are given,
6356 : // the missing offset is assumed to be zero."
6357 0 : if (eCSSUnit_Enumerated != value->Item(1).GetUnit()) {
6358 : // keyword offset keyword
6359 : // Second value is non-keyword, thus first value must be a non-center
6360 : // keyword.
6361 0 : if (eCSSUnit_Enumerated != value->Item(0).GetUnit() ||
6362 0 : BG_CENTER == value->Item(0).GetIntValue()) {
6363 0 : return false;
6364 : }
6365 :
6366 : // Remaining value must be a keyword.
6367 0 : if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
6368 0 : return false;
6369 : }
6370 :
6371 0 : yOffset.Reset(); // Everything else is in the correct position.
6372 0 : } else if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
6373 : // keyword keyword offset
6374 : // Third value is non-keyword, thus second value must be non-center
6375 : // keyword.
6376 0 : if (BG_CENTER == value->Item(1).GetIntValue()) {
6377 0 : return false;
6378 : }
6379 :
6380 : // Remaining value must be a keyword.
6381 0 : if (eCSSUnit_Enumerated != value->Item(0).GetUnit()) {
6382 0 : return false;
6383 : }
6384 :
6385 : // Move the values to the correct position in the array.
6386 0 : value->Item(3) = value->Item(2); // yOffset
6387 0 : value->Item(2) = value->Item(1); // yEdge
6388 0 : value->Item(1).Reset(); // xOffset
6389 : } else {
6390 0 : return false;
6391 : }
6392 0 : break;
6393 : case 2:
6394 : // "If two values are given and at least one value is not a keyword, then
6395 : // the first value represents the horizontal position (or offset) and the
6396 : // second represents the vertical position (or offset)"
6397 0 : if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
6398 0 : if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
6399 : // keyword keyword
6400 0 : value->Item(2) = value->Item(1); // move yEdge to correct position
6401 0 : xOffset.Reset();
6402 0 : yOffset.Reset();
6403 : } else {
6404 : // keyword offset
6405 : // First value must represent horizontal position.
6406 0 : if ((BG_TOP | BG_BOTTOM) & value->Item(0).GetIntValue()) {
6407 0 : return false;
6408 : }
6409 0 : value->Item(3) = value->Item(1); // move yOffset to correct position
6410 0 : xOffset.Reset();
6411 0 : yEdge.Reset();
6412 : }
6413 : } else {
6414 0 : if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
6415 : // offset keyword
6416 : // Second value must represent vertical position.
6417 0 : if ((BG_LEFT | BG_RIGHT) & value->Item(1).GetIntValue()) {
6418 0 : return false;
6419 : }
6420 0 : value->Item(2) = value->Item(1); // move yEdge to correct position
6421 0 : value->Item(1) = value->Item(0); // move xOffset to correct position
6422 0 : xEdge.Reset();
6423 0 : yOffset.Reset();
6424 : } else {
6425 : // offset offset
6426 0 : value->Item(3) = value->Item(1); // move yOffset to correct position
6427 0 : value->Item(1) = value->Item(0); // move xOffset to correct position
6428 0 : xEdge.Reset();
6429 0 : yEdge.Reset();
6430 : }
6431 : }
6432 0 : break;
6433 : case 1:
6434 : // "If only one value is specified, the second value is assumed to be
6435 : // center."
6436 0 : if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
6437 0 : xOffset.Reset();
6438 : } else {
6439 0 : value->Item(1) = value->Item(0); // move xOffset to correct position
6440 0 : xEdge.Reset();
6441 : }
6442 0 : yEdge.SetIntValue(NS_STYLE_BG_POSITION_CENTER, eCSSUnit_Enumerated);
6443 0 : yOffset.Reset();
6444 0 : break;
6445 : default:
6446 0 : return false;
6447 : }
6448 :
6449 : // For compatibility with CSS2.1 code the edges can be unspecified.
6450 : // Unspecified edges are recorded as NULL.
6451 0 : NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() ||
6452 : eCSSUnit_Null == xEdge.GetUnit()) &&
6453 : (eCSSUnit_Enumerated == yEdge.GetUnit() ||
6454 : eCSSUnit_Null == yEdge.GetUnit()) &&
6455 : eCSSUnit_Enumerated != xOffset.GetUnit() &&
6456 : eCSSUnit_Enumerated != yOffset.GetUnit(),
6457 : "Unexpected units");
6458 :
6459 : // Keywords in first and second pairs can not both be vertical or
6460 : // horizontal keywords. (eg. left right, bottom top). Additionally,
6461 : // non-center keyword can not be duplicated (eg. left left).
6462 : PRInt32 xEdgeEnum =
6463 0 : xEdge.GetUnit() == eCSSUnit_Enumerated ? xEdge.GetIntValue() : 0;
6464 : PRInt32 yEdgeEnum =
6465 0 : yEdge.GetUnit() == eCSSUnit_Enumerated ? yEdge.GetIntValue() : 0;
6466 0 : if ((xEdgeEnum | yEdgeEnum) == (BG_LEFT | BG_RIGHT) ||
6467 : (xEdgeEnum | yEdgeEnum) == (BG_TOP | BG_BOTTOM) ||
6468 : (xEdgeEnum & yEdgeEnum & ~BG_CENTER)) {
6469 0 : return false;
6470 : }
6471 :
6472 : // The values could be in an order that is different than expected.
6473 : // eg. x contains vertical information, y contains horizontal information.
6474 : // Swap if incorrect order.
6475 0 : if (xEdgeEnum & (BG_TOP | BG_BOTTOM) ||
6476 : yEdgeEnum & (BG_LEFT | BG_RIGHT)) {
6477 0 : nsCSSValue swapEdge = xEdge;
6478 0 : nsCSSValue swapOffset = xOffset;
6479 0 : xEdge = yEdge;
6480 0 : xOffset = yOffset;
6481 0 : yEdge = swapEdge;
6482 0 : yOffset = swapOffset;
6483 : }
6484 :
6485 0 : return true;
6486 : }
6487 :
6488 : // This function is very similar to ParseBackgroundList and
6489 : // ParseBackgroundPosition.
6490 : bool
6491 0 : CSSParserImpl::ParseBackgroundSize()
6492 : {
6493 0 : nsCSSValue value;
6494 0 : if (ParseVariant(value, VARIANT_INHERIT, nsnull)) {
6495 : // 'initial' and 'inherit' stand alone, no list permitted.
6496 0 : if (!ExpectEndProperty()) {
6497 0 : return false;
6498 : }
6499 : } else {
6500 0 : nsCSSValuePair valuePair;
6501 0 : if (!ParseBackgroundSizeValues(valuePair)) {
6502 0 : return false;
6503 : }
6504 0 : nsCSSValuePairList* item = value.SetPairListValue();
6505 0 : for (;;) {
6506 0 : item->mXValue = valuePair.mXValue;
6507 0 : item->mYValue = valuePair.mYValue;
6508 0 : if (CheckEndProperty()) {
6509 : break;
6510 : }
6511 0 : if (!ExpectSymbol(',', true)) {
6512 0 : return false;
6513 : }
6514 0 : if (!ParseBackgroundSizeValues(valuePair)) {
6515 0 : return false;
6516 : }
6517 0 : item->mNext = new nsCSSValuePairList;
6518 0 : item = item->mNext;
6519 : }
6520 : }
6521 0 : AppendValue(eCSSProperty_background_size, value);
6522 0 : return true;
6523 : }
6524 :
6525 : /**
6526 : * Parses two values that correspond to lengths for the background-size
6527 : * property. These can be one or two lengths (or the 'auto' keyword) or
6528 : * percentages corresponding to the element's dimensions or the single keywords
6529 : * 'contain' or 'cover'. 'initial' and 'inherit' must be handled by the caller
6530 : * if desired.
6531 : *
6532 : * @param aOut The nsCSSValuePair in which to place the result.
6533 : * @return Whether or not the operation succeeded.
6534 : */
6535 : #define BG_SIZE_VARIANT (VARIANT_LP | VARIANT_AUTO | VARIANT_CALC)
6536 0 : bool CSSParserImpl::ParseBackgroundSizeValues(nsCSSValuePair &aOut)
6537 : {
6538 : // First try a percentage or a length value
6539 0 : nsCSSValue &xValue = aOut.mXValue,
6540 0 : &yValue = aOut.mYValue;
6541 0 : if (ParseNonNegativeVariant(xValue, BG_SIZE_VARIANT, nsnull)) {
6542 : // We have one percentage/length/calc/auto. Get the optional second
6543 : // percentage/length/calc/keyword.
6544 0 : if (ParseNonNegativeVariant(yValue, BG_SIZE_VARIANT, nsnull)) {
6545 : // We have a second percentage/length/calc/auto.
6546 0 : return true;
6547 : }
6548 :
6549 : // If only one percentage or length value is given, it sets the
6550 : // horizontal size only, and the vertical size will be as if by 'auto'.
6551 0 : yValue.SetAutoValue();
6552 0 : return true;
6553 : }
6554 :
6555 : // Now address 'contain' and 'cover'.
6556 0 : if (!ParseEnum(xValue, nsCSSProps::kBackgroundSizeKTable))
6557 0 : return false;
6558 0 : yValue.Reset();
6559 0 : return true;
6560 : }
6561 : #undef BG_SIZE_VARIANT
6562 :
6563 : bool
6564 0 : CSSParserImpl::ParseBorderColor()
6565 : {
6566 : static const nsCSSProperty kBorderColorSources[] = {
6567 : eCSSProperty_border_left_color_ltr_source,
6568 : eCSSProperty_border_left_color_rtl_source,
6569 : eCSSProperty_border_right_color_ltr_source,
6570 : eCSSProperty_border_right_color_rtl_source,
6571 : eCSSProperty_UNKNOWN
6572 : };
6573 :
6574 : // do this now, in case 4 values weren't specified
6575 0 : InitBoxPropsAsPhysical(kBorderColorSources);
6576 0 : return ParseBoxProperties(kBorderColorIDs);
6577 : }
6578 :
6579 : void
6580 0 : CSSParserImpl::SetBorderImageInitialValues()
6581 : {
6582 : // border-image-source: none
6583 0 : nsCSSValue source;
6584 0 : source.SetNoneValue();
6585 0 : AppendValue(eCSSProperty_border_image_source, source);
6586 :
6587 : // border-image-slice: 100%
6588 0 : nsCSSValue sliceBoxValue;
6589 0 : nsCSSRect& sliceBox = sliceBoxValue.SetRectValue();
6590 0 : sliceBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Percent));
6591 0 : nsCSSValue slice;
6592 0 : nsCSSValueList* sliceList = slice.SetListValue();
6593 0 : sliceList->mValue = sliceBoxValue;
6594 0 : AppendValue(eCSSProperty_border_image_slice, slice);
6595 :
6596 : // border-image-width: 1
6597 0 : nsCSSValue width;
6598 0 : nsCSSRect& widthBox = width.SetRectValue();
6599 0 : widthBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Number));
6600 0 : AppendValue(eCSSProperty_border_image_width, width);
6601 :
6602 : // border-image-outset: 0
6603 0 : nsCSSValue outset;
6604 0 : nsCSSRect& outsetBox = outset.SetRectValue();
6605 0 : outsetBox.SetAllSidesTo(nsCSSValue(0.0f, eCSSUnit_Number));
6606 0 : AppendValue(eCSSProperty_border_image_outset, outset);
6607 :
6608 : // border-image-repeat: repeat
6609 0 : nsCSSValue repeat;
6610 0 : nsCSSValuePair repeatPair;
6611 : repeatPair.SetBothValuesTo(nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH,
6612 0 : eCSSUnit_Enumerated));
6613 0 : repeat.SetPairValue(&repeatPair);
6614 0 : AppendValue(eCSSProperty_border_image_repeat, repeat);
6615 0 : }
6616 :
6617 : bool
6618 0 : CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit,
6619 : bool* aConsumedTokens)
6620 : {
6621 : // border-image-slice: initial | [<number>|<percentage>]{1,4} && fill?
6622 0 : nsCSSValue value;
6623 :
6624 0 : if (aConsumedTokens) {
6625 0 : *aConsumedTokens = true;
6626 : }
6627 :
6628 0 : if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nsnull)) {
6629 : // Keyword "inherit" can not be mixed, so we are done.
6630 0 : AppendValue(eCSSProperty_border_image_slice, value);
6631 0 : return true;
6632 : }
6633 :
6634 : // Try parsing "fill" value.
6635 0 : nsCSSValue imageSliceFillValue;
6636 : bool hasFill = ParseEnum(imageSliceFillValue,
6637 0 : nsCSSProps::kBorderImageSliceKTable);
6638 :
6639 : // Parse the box dimensions.
6640 0 : nsCSSValue imageSliceBoxValue;
6641 0 : if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue)) {
6642 0 : if (!hasFill && aConsumedTokens) {
6643 0 : *aConsumedTokens = false;
6644 : }
6645 :
6646 0 : return false;
6647 : }
6648 :
6649 : // Try parsing "fill" keyword again if the first time failed because keyword
6650 : // and slice dimensions can be in any order.
6651 0 : if (!hasFill) {
6652 : hasFill = ParseEnum(imageSliceFillValue,
6653 0 : nsCSSProps::kBorderImageSliceKTable);
6654 : }
6655 :
6656 0 : nsCSSValueList* borderImageSlice = value.SetListValue();
6657 : // Put the box value into the list.
6658 0 : borderImageSlice->mValue = imageSliceBoxValue;
6659 :
6660 0 : if (hasFill) {
6661 : // Put the "fill" value into the list.
6662 0 : borderImageSlice->mNext = new nsCSSValueList;
6663 0 : borderImageSlice->mNext->mValue = imageSliceFillValue;
6664 : }
6665 :
6666 0 : AppendValue(eCSSProperty_border_image_slice, value);
6667 0 : return true;
6668 : }
6669 :
6670 : bool
6671 0 : CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit)
6672 : {
6673 : // border-image-width: initial | [<length>|<number>|<percentage>|auto]{1,4}
6674 0 : nsCSSValue value;
6675 :
6676 0 : if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nsnull)) {
6677 : // Keyword "inherit" can no be mixed, so we are done.
6678 0 : AppendValue(eCSSProperty_border_image_width, value);
6679 0 : return true;
6680 : }
6681 :
6682 : // Parse the box dimensions.
6683 0 : if (!ParseGroupedBoxProperty(VARIANT_ALPN, value)) {
6684 0 : return false;
6685 : }
6686 :
6687 0 : AppendValue(eCSSProperty_border_image_width, value);
6688 0 : return true;
6689 : }
6690 :
6691 : bool
6692 0 : CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit)
6693 : {
6694 : // border-image-outset: initial | [<length>|<number>]{1,4}
6695 0 : nsCSSValue value;
6696 :
6697 0 : if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nsnull)) {
6698 : // Keyword "inherit" can not be mixed, so we are done.
6699 0 : AppendValue(eCSSProperty_border_image_outset, value);
6700 0 : return true;
6701 : }
6702 :
6703 : // Parse the box dimensions.
6704 0 : if (!ParseGroupedBoxProperty(VARIANT_LN, value)) {
6705 0 : return false;
6706 : }
6707 :
6708 0 : AppendValue(eCSSProperty_border_image_outset, value);
6709 0 : return true;
6710 : }
6711 :
6712 : bool
6713 0 : CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit)
6714 : {
6715 0 : nsCSSValue value;
6716 0 : if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nsnull)) {
6717 : // Keyword "inherit" can not be mixed, so we are done.
6718 0 : AppendValue(eCSSProperty_border_image_repeat, value);
6719 0 : return true;
6720 : }
6721 :
6722 0 : nsCSSValuePair result;
6723 0 : if (!ParseEnum(result.mXValue, nsCSSProps::kBorderImageRepeatKTable)) {
6724 0 : return false;
6725 : }
6726 :
6727 : // optional second keyword, defaults to first
6728 0 : if (!ParseEnum(result.mYValue, nsCSSProps::kBorderImageRepeatKTable)) {
6729 0 : result.mYValue = result.mXValue;
6730 : }
6731 :
6732 0 : value.SetPairValue(&result);
6733 0 : AppendValue(eCSSProperty_border_image_repeat, value);
6734 0 : return true;
6735 : }
6736 :
6737 : bool
6738 0 : CSSParserImpl::ParseBorderImage()
6739 : {
6740 0 : nsAutoParseCompoundProperty compound(this);
6741 :
6742 : // border-image: inherit |
6743 : // <border-image-source> ||
6744 : // <border-image-slice>
6745 : // [ / <border-image-width>?
6746 : // [ / <border-image-outset>]?]? ||
6747 : // <border-image-repeat>
6748 :
6749 0 : nsCSSValue value;
6750 0 : if (ParseVariant(value, VARIANT_INHERIT, nsnull)) {
6751 0 : AppendValue(eCSSProperty_border_image_source, value);
6752 0 : AppendValue(eCSSProperty_border_image_slice, value);
6753 0 : AppendValue(eCSSProperty_border_image_width, value);
6754 0 : AppendValue(eCSSProperty_border_image_outset, value);
6755 0 : AppendValue(eCSSProperty_border_image_repeat, value);
6756 : // Keyword "inherit" can't be mixed, so we are done.
6757 0 : return true;
6758 : }
6759 :
6760 : // No empty property.
6761 0 : if (CheckEndProperty()) {
6762 0 : return false;
6763 : }
6764 :
6765 : // Shorthand properties are required to set everything they can.
6766 0 : SetBorderImageInitialValues();
6767 :
6768 0 : bool foundSource = false;
6769 0 : bool foundSliceWidthOutset = false;
6770 0 : bool foundRepeat = false;
6771 :
6772 : // This loop is used to handle the parsing of border-image properties which
6773 : // can appear in any order.
6774 0 : nsCSSValue imageSourceValue;
6775 0 : while (!CheckEndProperty()) {
6776 : // <border-image-source>
6777 0 : if (!foundSource && ParseVariant(imageSourceValue, VARIANT_UO, nsnull)) {
6778 0 : AppendValue(eCSSProperty_border_image_source, imageSourceValue);
6779 0 : foundSource = true;
6780 0 : continue;
6781 : }
6782 :
6783 : // <border-image-slice>
6784 : // ParseBorderImageSlice is weird. It may consume tokens and then return
6785 : // false, because it parses a property with two required components that
6786 : // can appear in either order. Since the tokens that were consumed cannot
6787 : // parse as anything else we care about, this isn't a problem.
6788 0 : if (!foundSliceWidthOutset) {
6789 0 : bool sliceConsumedTokens = false;
6790 0 : if (ParseBorderImageSlice(false, &sliceConsumedTokens)) {
6791 0 : foundSliceWidthOutset = true;
6792 :
6793 : // [ / <border-image-width>?
6794 0 : if (ExpectSymbol('/', true)) {
6795 0 : ParseBorderImageWidth(false);
6796 :
6797 : // [ / <border-image-outset>
6798 0 : if (ExpectSymbol('/', true)) {
6799 0 : if (!ParseBorderImageOutset(false)) {
6800 0 : return false;
6801 : }
6802 : }
6803 : }
6804 :
6805 0 : continue;
6806 : } else {
6807 : // If we consumed some tokens for <border-image-slice> but did not
6808 : // successfully parse it, we have an error.
6809 0 : if (sliceConsumedTokens) {
6810 0 : return false;
6811 : }
6812 : }
6813 : }
6814 :
6815 : // <border-image-repeat>
6816 0 : if (!foundRepeat && ParseBorderImageRepeat(false)) {
6817 0 : foundRepeat = true;
6818 0 : continue;
6819 : }
6820 :
6821 0 : return false;
6822 : }
6823 :
6824 0 : return true;
6825 : }
6826 :
6827 : bool
6828 0 : CSSParserImpl::ParseBorderSpacing()
6829 : {
6830 0 : nsCSSValue xValue, yValue;
6831 0 : if (!ParseNonNegativeVariant(xValue, VARIANT_HL | VARIANT_CALC, nsnull)) {
6832 0 : return false;
6833 : }
6834 :
6835 : // If we have one length, get the optional second length.
6836 : // set the second value equal to the first.
6837 0 : if (xValue.IsLengthUnit() || xValue.IsCalcUnit()) {
6838 0 : ParseNonNegativeVariant(yValue, VARIANT_LENGTH | VARIANT_CALC, nsnull);
6839 : }
6840 :
6841 0 : if (!ExpectEndProperty()) {
6842 0 : return false;
6843 : }
6844 :
6845 0 : if (yValue == xValue || yValue.GetUnit() == eCSSUnit_Null) {
6846 0 : AppendValue(eCSSProperty_border_spacing, xValue);
6847 : } else {
6848 0 : nsCSSValue pair;
6849 0 : pair.SetPairValue(xValue, yValue);
6850 0 : AppendValue(eCSSProperty_border_spacing, pair);
6851 : }
6852 0 : return true;
6853 : }
6854 :
6855 : bool
6856 0 : CSSParserImpl::ParseBorderSide(const nsCSSProperty aPropIDs[],
6857 : bool aSetAllSides)
6858 : {
6859 0 : const PRInt32 numProps = 3;
6860 0 : nsCSSValue values[numProps];
6861 :
6862 0 : PRInt32 found = ParseChoice(values, aPropIDs, numProps);
6863 0 : if ((found < 1) || (false == ExpectEndProperty())) {
6864 0 : return false;
6865 : }
6866 :
6867 0 : if ((found & 1) == 0) { // Provide default border-width
6868 0 : values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
6869 : }
6870 0 : if ((found & 2) == 0) { // Provide default border-style
6871 0 : values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
6872 : }
6873 0 : if ((found & 4) == 0) { // text color will be used
6874 0 : values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
6875 : }
6876 :
6877 0 : if (aSetAllSides) {
6878 : static const nsCSSProperty kBorderSources[] = {
6879 : eCSSProperty_border_left_color_ltr_source,
6880 : eCSSProperty_border_left_color_rtl_source,
6881 : eCSSProperty_border_right_color_ltr_source,
6882 : eCSSProperty_border_right_color_rtl_source,
6883 : eCSSProperty_border_left_style_ltr_source,
6884 : eCSSProperty_border_left_style_rtl_source,
6885 : eCSSProperty_border_right_style_ltr_source,
6886 : eCSSProperty_border_right_style_rtl_source,
6887 : eCSSProperty_border_left_width_ltr_source,
6888 : eCSSProperty_border_left_width_rtl_source,
6889 : eCSSProperty_border_right_width_ltr_source,
6890 : eCSSProperty_border_right_width_rtl_source,
6891 : eCSSProperty_UNKNOWN
6892 : };
6893 :
6894 0 : InitBoxPropsAsPhysical(kBorderSources);
6895 :
6896 : // Parsing "border" shorthand; set all four sides to the same thing
6897 0 : for (PRInt32 index = 0; index < 4; index++) {
6898 : NS_ASSERTION(numProps == 3, "This code needs updating");
6899 0 : AppendValue(kBorderWidthIDs[index], values[0]);
6900 0 : AppendValue(kBorderStyleIDs[index], values[1]);
6901 0 : AppendValue(kBorderColorIDs[index], values[2]);
6902 : }
6903 :
6904 : static const nsCSSProperty kBorderColorsProps[] = {
6905 : eCSSProperty_border_top_colors,
6906 : eCSSProperty_border_right_colors,
6907 : eCSSProperty_border_bottom_colors,
6908 : eCSSProperty_border_left_colors
6909 : };
6910 :
6911 : // Set the other properties that the border shorthand sets to their
6912 : // initial values.
6913 0 : nsCSSValue extraValue;
6914 0 : switch (values[0].GetUnit()) {
6915 : case eCSSUnit_Inherit:
6916 : case eCSSUnit_Initial:
6917 0 : extraValue = values[0];
6918 : // Set value of border-image properties to initial/inherit
6919 0 : AppendValue(eCSSProperty_border_image_source, extraValue);
6920 0 : AppendValue(eCSSProperty_border_image_slice, extraValue);
6921 0 : AppendValue(eCSSProperty_border_image_width, extraValue);
6922 0 : AppendValue(eCSSProperty_border_image_outset, extraValue);
6923 0 : AppendValue(eCSSProperty_border_image_repeat, extraValue);
6924 0 : break;
6925 : default:
6926 0 : extraValue.SetNoneValue();
6927 0 : SetBorderImageInitialValues();
6928 0 : break;
6929 : }
6930 0 : NS_FOR_CSS_SIDES(side) {
6931 0 : AppendValue(kBorderColorsProps[side], extraValue);
6932 : }
6933 : }
6934 : else {
6935 : // Just set our one side
6936 0 : for (PRInt32 index = 0; index < numProps; index++) {
6937 0 : AppendValue(aPropIDs[index], values[index]);
6938 : }
6939 : }
6940 0 : return true;
6941 : }
6942 :
6943 : bool
6944 0 : CSSParserImpl::ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
6945 : PRInt32 aSourceType)
6946 : {
6947 0 : const PRInt32 numProps = 3;
6948 0 : nsCSSValue values[numProps];
6949 :
6950 0 : PRInt32 found = ParseChoice(values, aPropIDs, numProps);
6951 0 : if ((found < 1) || (false == ExpectEndProperty())) {
6952 0 : return false;
6953 : }
6954 :
6955 0 : if ((found & 1) == 0) { // Provide default border-width
6956 0 : values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
6957 : }
6958 0 : if ((found & 2) == 0) { // Provide default border-style
6959 0 : values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
6960 : }
6961 0 : if ((found & 4) == 0) { // text color will be used
6962 0 : values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
6963 : }
6964 0 : for (PRInt32 index = 0; index < numProps; index++) {
6965 : const nsCSSProperty* subprops =
6966 0 : nsCSSProps::SubpropertyEntryFor(aPropIDs[index + numProps]);
6967 0 : NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
6968 : "not box property with physical vs. logical cascading");
6969 0 : AppendValue(subprops[0], values[index]);
6970 0 : nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
6971 0 : AppendValue(subprops[1], typeVal);
6972 0 : AppendValue(subprops[2], typeVal);
6973 : }
6974 0 : return true;
6975 : }
6976 :
6977 : bool
6978 0 : CSSParserImpl::ParseBorderStyle()
6979 : {
6980 : static const nsCSSProperty kBorderStyleSources[] = {
6981 : eCSSProperty_border_left_style_ltr_source,
6982 : eCSSProperty_border_left_style_rtl_source,
6983 : eCSSProperty_border_right_style_ltr_source,
6984 : eCSSProperty_border_right_style_rtl_source,
6985 : eCSSProperty_UNKNOWN
6986 : };
6987 :
6988 : // do this now, in case 4 values weren't specified
6989 0 : InitBoxPropsAsPhysical(kBorderStyleSources);
6990 0 : return ParseBoxProperties(kBorderStyleIDs);
6991 : }
6992 :
6993 : bool
6994 0 : CSSParserImpl::ParseBorderWidth()
6995 : {
6996 : static const nsCSSProperty kBorderWidthSources[] = {
6997 : eCSSProperty_border_left_width_ltr_source,
6998 : eCSSProperty_border_left_width_rtl_source,
6999 : eCSSProperty_border_right_width_ltr_source,
7000 : eCSSProperty_border_right_width_rtl_source,
7001 : eCSSProperty_UNKNOWN
7002 : };
7003 :
7004 : // do this now, in case 4 values weren't specified
7005 0 : InitBoxPropsAsPhysical(kBorderWidthSources);
7006 0 : return ParseBoxProperties(kBorderWidthIDs);
7007 : }
7008 :
7009 : bool
7010 0 : CSSParserImpl::ParseBorderColors(nsCSSProperty aProperty)
7011 : {
7012 0 : nsCSSValue value;
7013 0 : if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nsnull)) {
7014 : // 'inherit', 'initial', and 'none' are only allowed on their own
7015 0 : if (!ExpectEndProperty()) {
7016 0 : return false;
7017 : }
7018 : } else {
7019 0 : nsCSSValueList *cur = value.SetListValue();
7020 0 : for (;;) {
7021 0 : if (!ParseVariant(cur->mValue, VARIANT_COLOR | VARIANT_KEYWORD,
7022 0 : nsCSSProps::kBorderColorKTable)) {
7023 0 : return false;
7024 : }
7025 0 : if (CheckEndProperty()) {
7026 0 : break;
7027 : }
7028 0 : cur->mNext = new nsCSSValueList;
7029 0 : cur = cur->mNext;
7030 : }
7031 : }
7032 0 : AppendValue(aProperty, value);
7033 0 : return true;
7034 : }
7035 :
7036 : // Parse the top level of a calc() expression.
7037 : bool
7038 0 : CSSParserImpl::ParseCalc(nsCSSValue &aValue, PRInt32 aVariantMask)
7039 : {
7040 : // Parsing calc expressions requires, in a number of cases, looking
7041 : // for a token that is *either* a value of the property or a number.
7042 : // This can be done without lookahead when we assume that the property
7043 : // values cannot themselves be numbers.
7044 0 : NS_ASSERTION(!(aVariantMask & VARIANT_NUMBER), "unexpected variant mask");
7045 0 : NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
7046 :
7047 : // One-iteration loop so we can break to the error-handling case.
7048 : do {
7049 : // The toplevel of a calc() is always an nsCSSValue::Array of length 1.
7050 0 : nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
7051 :
7052 0 : if (!ParseCalcAdditiveExpression(arr->Item(0), aVariantMask))
7053 : break;
7054 :
7055 0 : if (!ExpectSymbol(')', true))
7056 : break;
7057 :
7058 0 : aValue.SetArrayValue(arr, eCSSUnit_Calc);
7059 0 : return true;
7060 : } while (false);
7061 :
7062 0 : SkipUntil(')');
7063 0 : return false;
7064 : }
7065 :
7066 : // We optimize away the <value-expression> production given that
7067 : // ParseVariant consumes initial whitespace and we call
7068 : // ExpectSymbol(')') with true for aSkipWS.
7069 : // * If aVariantMask is VARIANT_NUMBER, this function parses the
7070 : // <number-additive-expression> production.
7071 : // * If aVariantMask does not contain VARIANT_NUMBER, this function
7072 : // parses the <value-additive-expression> production.
7073 : // * Otherwise (VARIANT_NUMBER and other bits) this function parses
7074 : // whichever one of the productions matches ***and modifies
7075 : // aVariantMask*** to reflect which one it has parsed by either
7076 : // removing VARIANT_NUMBER or removing all other bits.
7077 : // It does so iteratively, but builds the correct recursive
7078 : // data structure.
7079 : bool
7080 0 : CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue,
7081 : PRInt32& aVariantMask)
7082 : {
7083 0 : NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
7084 0 : nsCSSValue *storage = &aValue;
7085 0 : for (;;) {
7086 : bool haveWS;
7087 0 : if (!ParseCalcMultiplicativeExpression(*storage, aVariantMask, &haveWS))
7088 0 : return false;
7089 :
7090 0 : if (!haveWS || !GetToken(false))
7091 0 : return true;
7092 : nsCSSUnit unit;
7093 0 : if (mToken.IsSymbol('+')) {
7094 0 : unit = eCSSUnit_Calc_Plus;
7095 0 : } else if (mToken.IsSymbol('-')) {
7096 0 : unit = eCSSUnit_Calc_Minus;
7097 : } else {
7098 0 : UngetToken();
7099 0 : return true;
7100 : }
7101 0 : if (!RequireWhitespace())
7102 0 : return false;
7103 :
7104 0 : nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
7105 0 : arr->Item(0) = aValue;
7106 0 : storage = &arr->Item(1);
7107 0 : aValue.SetArrayValue(arr, unit);
7108 : }
7109 : }
7110 :
7111 : struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps,
7112 : public mozilla::css::CSSValueInputCalcOps
7113 : {
7114 0 : result_type ComputeLeafValue(const nsCSSValue& aValue)
7115 : {
7116 0 : NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
7117 0 : return aValue.GetFloatValue();
7118 : }
7119 :
7120 0 : float ComputeNumber(const nsCSSValue& aValue)
7121 : {
7122 0 : return mozilla::css::ComputeCalc(aValue, *this);
7123 : }
7124 : };
7125 :
7126 : // * If aVariantMask is VARIANT_NUMBER, this function parses the
7127 : // <number-multiplicative-expression> production.
7128 : // * If aVariantMask does not contain VARIANT_NUMBER, this function
7129 : // parses the <value-multiplicative-expression> production.
7130 : // * Otherwise (VARIANT_NUMBER and other bits) this function parses
7131 : // whichever one of the productions matches ***and modifies
7132 : // aVariantMask*** to reflect which one it has parsed by either
7133 : // removing VARIANT_NUMBER or removing all other bits.
7134 : // It does so iteratively, but builds the correct recursive data
7135 : // structure.
7136 : // This function always consumes *trailing* whitespace when it returns
7137 : // true; whether there was any such whitespace is returned in the
7138 : // aHadFinalWS parameter.
7139 : bool
7140 0 : CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
7141 : PRInt32& aVariantMask,
7142 : bool *aHadFinalWS)
7143 : {
7144 0 : NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
7145 0 : bool gotValue = false; // already got the part with the unit
7146 0 : bool afterDivision = false;
7147 :
7148 0 : nsCSSValue *storage = &aValue;
7149 0 : for (;;) {
7150 : PRInt32 variantMask;
7151 0 : if (afterDivision || gotValue) {
7152 0 : variantMask = VARIANT_NUMBER;
7153 : } else {
7154 0 : variantMask = aVariantMask | VARIANT_NUMBER;
7155 : }
7156 0 : if (!ParseCalcTerm(*storage, variantMask))
7157 0 : return false;
7158 0 : NS_ABORT_IF_FALSE(variantMask != 0,
7159 : "ParseCalcTerm did not set variantMask appropriately");
7160 0 : NS_ABORT_IF_FALSE(!(variantMask & VARIANT_NUMBER) ||
7161 : !(variantMask & ~PRInt32(VARIANT_NUMBER)),
7162 : "ParseCalcTerm did not set variantMask appropriately");
7163 :
7164 0 : if (variantMask & VARIANT_NUMBER) {
7165 : // Simplify the value immediately so we can check for division by
7166 : // zero.
7167 : ReduceNumberCalcOps ops;
7168 0 : float number = mozilla::css::ComputeCalc(*storage, ops);
7169 0 : if (number == 0.0 && afterDivision)
7170 0 : return false;
7171 0 : storage->SetFloatValue(number, eCSSUnit_Number);
7172 : } else {
7173 0 : gotValue = true;
7174 :
7175 0 : if (storage != &aValue) {
7176 : // Simplify any numbers in the Times_L position (which are
7177 : // not simplified by the check above).
7178 0 : NS_ABORT_IF_FALSE(storage == &aValue.GetArrayValue()->Item(1),
7179 : "unexpected relationship to current storage");
7180 0 : nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0);
7181 : ReduceNumberCalcOps ops;
7182 0 : float number = mozilla::css::ComputeCalc(leftValue, ops);
7183 0 : leftValue.SetFloatValue(number, eCSSUnit_Number);
7184 : }
7185 : }
7186 :
7187 0 : bool hadWS = RequireWhitespace();
7188 0 : if (!GetToken(false)) {
7189 0 : *aHadFinalWS = hadWS;
7190 0 : break;
7191 : }
7192 : nsCSSUnit unit;
7193 0 : if (mToken.IsSymbol('*')) {
7194 0 : unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L;
7195 0 : afterDivision = false;
7196 0 : } else if (mToken.IsSymbol('/')) {
7197 0 : unit = eCSSUnit_Calc_Divided;
7198 0 : afterDivision = true;
7199 : } else {
7200 0 : UngetToken();
7201 0 : *aHadFinalWS = hadWS;
7202 0 : break;
7203 : }
7204 :
7205 0 : nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
7206 0 : arr->Item(0) = aValue;
7207 0 : storage = &arr->Item(1);
7208 0 : aValue.SetArrayValue(arr, unit);
7209 : }
7210 :
7211 : // Adjust aVariantMask (see comments above function) to reflect which
7212 : // option we took.
7213 0 : if (aVariantMask & VARIANT_NUMBER) {
7214 0 : if (gotValue) {
7215 0 : aVariantMask &= ~PRInt32(VARIANT_NUMBER);
7216 : } else {
7217 0 : aVariantMask = VARIANT_NUMBER;
7218 : }
7219 : } else {
7220 0 : if (!gotValue) {
7221 : // We had to find a value, but we didn't.
7222 0 : return false;
7223 : }
7224 : }
7225 :
7226 0 : return true;
7227 : }
7228 :
7229 : // * If aVariantMask is VARIANT_NUMBER, this function parses the
7230 : // <number-term> production.
7231 : // * If aVariantMask does not contain VARIANT_NUMBER, this function
7232 : // parses the <value-term> production.
7233 : // * Otherwise (VARIANT_NUMBER and other bits) this function parses
7234 : // whichever one of the productions matches ***and modifies
7235 : // aVariantMask*** to reflect which one it has parsed by either
7236 : // removing VARIANT_NUMBER or removing all other bits.
7237 : bool
7238 0 : CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, PRInt32& aVariantMask)
7239 : {
7240 0 : NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
7241 0 : if (!GetToken(true))
7242 0 : return false;
7243 : // Either an additive expression in parentheses...
7244 0 : if (mToken.IsSymbol('(')) {
7245 0 : if (!ParseCalcAdditiveExpression(aValue, aVariantMask) ||
7246 0 : !ExpectSymbol(')', true)) {
7247 0 : SkipUntil(')');
7248 0 : return false;
7249 : }
7250 0 : return true;
7251 : }
7252 : // ... or just a value
7253 0 : UngetToken();
7254 : // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
7255 : // always gets picked up
7256 0 : if (!ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nsnull)) {
7257 0 : return false;
7258 : }
7259 : // ...and do the VARIANT_NUMBER check ourselves.
7260 0 : if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) {
7261 0 : return false;
7262 : }
7263 : // If we did the value parsing, we need to adjust aVariantMask to
7264 : // reflect which option we took (see above).
7265 0 : if (aVariantMask & VARIANT_NUMBER) {
7266 0 : if (aValue.GetUnit() == eCSSUnit_Number) {
7267 0 : aVariantMask = VARIANT_NUMBER;
7268 : } else {
7269 0 : aVariantMask &= ~PRInt32(VARIANT_NUMBER);
7270 : }
7271 : }
7272 0 : return true;
7273 : }
7274 :
7275 : // This function consumes all consecutive whitespace and returns whether
7276 : // there was any.
7277 : bool
7278 0 : CSSParserImpl::RequireWhitespace()
7279 : {
7280 0 : if (!GetToken(false))
7281 0 : return false;
7282 0 : if (mToken.mType != eCSSToken_WhiteSpace) {
7283 0 : UngetToken();
7284 0 : return false;
7285 : }
7286 : // Skip any additional whitespace tokens.
7287 0 : if (GetToken(true)) {
7288 0 : UngetToken();
7289 : }
7290 0 : return true;
7291 : }
7292 :
7293 : bool
7294 0 : CSSParserImpl::ParseRect(nsCSSProperty aPropID)
7295 : {
7296 0 : if (! GetToken(true)) {
7297 0 : return false;
7298 : }
7299 :
7300 0 : nsCSSValue val;
7301 :
7302 0 : if (mToken.mType == eCSSToken_Ident) {
7303 0 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
7304 0 : switch (keyword) {
7305 : case eCSSKeyword_auto:
7306 0 : if (!ExpectEndProperty()) {
7307 0 : return false;
7308 : }
7309 0 : val.SetAutoValue();
7310 0 : break;
7311 : case eCSSKeyword_inherit:
7312 0 : if (!ExpectEndProperty()) {
7313 0 : return false;
7314 : }
7315 0 : val.SetInheritValue();
7316 0 : break;
7317 : case eCSSKeyword__moz_initial:
7318 0 : if (!ExpectEndProperty()) {
7319 0 : return false;
7320 : }
7321 0 : val.SetInitialValue();
7322 0 : break;
7323 : default:
7324 0 : UngetToken();
7325 0 : return false;
7326 : }
7327 0 : } else if (mToken.mType == eCSSToken_Function &&
7328 0 : mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
7329 0 : nsCSSRect& rect = val.SetRectValue();
7330 : bool useCommas;
7331 0 : NS_FOR_CSS_SIDES(side) {
7332 0 : if (! ParseVariant(rect.*(nsCSSRect::sides[side]),
7333 0 : VARIANT_AL, nsnull)) {
7334 0 : return false;
7335 : }
7336 0 : if (side == 0) {
7337 0 : useCommas = ExpectSymbol(',', true);
7338 0 : } else if (useCommas && side < 3) {
7339 : // Skip optional commas between elements, but only if the first
7340 : // separator was a comma.
7341 0 : if (!ExpectSymbol(',', true)) {
7342 0 : return false;
7343 : }
7344 : }
7345 : }
7346 0 : if (!ExpectSymbol(')', true)) {
7347 0 : return false;
7348 : }
7349 0 : if (!ExpectEndProperty()) {
7350 0 : return false;
7351 : }
7352 : } else {
7353 0 : UngetToken();
7354 0 : return false;
7355 : }
7356 :
7357 0 : AppendValue(aPropID, val);
7358 0 : return true;
7359 : }
7360 :
7361 : bool
7362 0 : CSSParserImpl::ParseColumns()
7363 : {
7364 : // We use a similar "fake value" hack to ParseListStyle, because
7365 : // "auto" is acceptable for both column-count and column-width.
7366 : // If the fake "auto" value is found, and one of the real values isn't,
7367 : // that means the fake auto value is meant for the real value we didn't
7368 : // find.
7369 : static const nsCSSProperty columnIDs[] = {
7370 : eCSSPropertyExtra_x_auto_value,
7371 : eCSSProperty__moz_column_count,
7372 : eCSSProperty__moz_column_width
7373 : };
7374 0 : const PRInt32 numProps = NS_ARRAY_LENGTH(columnIDs);
7375 :
7376 0 : nsCSSValue values[numProps];
7377 0 : PRInt32 found = ParseChoice(values, columnIDs, numProps);
7378 0 : if (found < 1 || !ExpectEndProperty()) {
7379 0 : return false;
7380 : }
7381 0 : if ((found & (1|2|4)) == (1|2|4) &&
7382 0 : values[0].GetUnit() == eCSSUnit_Auto) {
7383 : // We filled all 3 values, which is invalid
7384 0 : return false;
7385 : }
7386 :
7387 0 : if ((found & 2) == 0) {
7388 : // Provide auto column-count
7389 0 : values[1].SetAutoValue();
7390 : }
7391 0 : if ((found & 4) == 0) {
7392 : // Provide auto column-width
7393 0 : values[2].SetAutoValue();
7394 : }
7395 :
7396 : // Start at index 1 to skip the fake auto value.
7397 0 : for (PRInt32 index = 1; index < numProps; index++) {
7398 0 : AppendValue(columnIDs[index], values[index]);
7399 : }
7400 0 : return true;
7401 : }
7402 :
7403 : #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
7404 : VARIANT_KEYWORD)
7405 : bool
7406 0 : CSSParserImpl::ParseContent()
7407 : {
7408 : // We need to divide the 'content' keywords into two classes for
7409 : // ParseVariant's sake, so we can't just use nsCSSProps::kContentKTable.
7410 : static const PRInt32 kContentListKWs[] = {
7411 : eCSSKeyword_open_quote, NS_STYLE_CONTENT_OPEN_QUOTE,
7412 : eCSSKeyword_close_quote, NS_STYLE_CONTENT_CLOSE_QUOTE,
7413 : eCSSKeyword_no_open_quote, NS_STYLE_CONTENT_NO_OPEN_QUOTE,
7414 : eCSSKeyword_no_close_quote, NS_STYLE_CONTENT_NO_CLOSE_QUOTE,
7415 : eCSSKeyword_UNKNOWN,-1
7416 : };
7417 :
7418 : static const PRInt32 kContentSolitaryKWs[] = {
7419 : eCSSKeyword__moz_alt_content, NS_STYLE_CONTENT_ALT_CONTENT,
7420 : eCSSKeyword_UNKNOWN,-1
7421 : };
7422 :
7423 : // Verify that these two lists add up to the size of
7424 : // nsCSSProps::kContentKTable.
7425 0 : NS_ABORT_IF_FALSE(nsCSSProps::kContentKTable[
7426 : ArrayLength(kContentListKWs) +
7427 : ArrayLength(kContentSolitaryKWs) - 4] ==
7428 : eCSSKeyword_UNKNOWN &&
7429 : nsCSSProps::kContentKTable[
7430 : ArrayLength(kContentListKWs) +
7431 : ArrayLength(kContentSolitaryKWs) - 3] == -1,
7432 : "content keyword tables out of sync");
7433 :
7434 0 : nsCSSValue value;
7435 0 : if (ParseVariant(value, VARIANT_HMK | VARIANT_NONE,
7436 : kContentSolitaryKWs)) {
7437 : // 'inherit', 'initial', 'normal', 'none', and 'alt-content' must be alone
7438 0 : if (!ExpectEndProperty()) {
7439 0 : return false;
7440 : }
7441 : } else {
7442 0 : nsCSSValueList* cur = value.SetListValue();
7443 0 : for (;;) {
7444 0 : if (!ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs)) {
7445 0 : return false;
7446 : }
7447 0 : if (CheckEndProperty()) {
7448 0 : break;
7449 : }
7450 0 : cur->mNext = new nsCSSValueList;
7451 0 : cur = cur->mNext;
7452 : }
7453 : }
7454 0 : AppendValue(eCSSProperty_content, value);
7455 0 : return true;
7456 : }
7457 :
7458 : bool
7459 0 : CSSParserImpl::ParseCounterData(nsCSSProperty aPropID)
7460 : {
7461 0 : nsCSSValue value;
7462 0 : if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nsnull)) {
7463 0 : if (!GetToken(true) || mToken.mType != eCSSToken_Ident) {
7464 0 : return false;
7465 : }
7466 :
7467 0 : nsCSSValuePairList *cur = value.SetPairListValue();
7468 0 : for (;;) {
7469 0 : cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
7470 0 : if (!GetToken(true)) {
7471 0 : break;
7472 : }
7473 0 : if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
7474 0 : cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
7475 : } else {
7476 0 : UngetToken();
7477 : }
7478 0 : if (CheckEndProperty()) {
7479 0 : break;
7480 : }
7481 0 : if (!GetToken(true) || mToken.mType != eCSSToken_Ident) {
7482 0 : return false;
7483 : }
7484 0 : cur->mNext = new nsCSSValuePairList;
7485 0 : cur = cur->mNext;
7486 : }
7487 : }
7488 0 : AppendValue(aPropID, value);
7489 0 : return true;
7490 : }
7491 :
7492 : bool
7493 0 : CSSParserImpl::ParseCursor()
7494 : {
7495 0 : nsCSSValue value;
7496 0 : if (ParseVariant(value, VARIANT_INHERIT, nsnull)) {
7497 : // 'inherit' and 'initial' must be alone
7498 0 : if (!ExpectEndProperty()) {
7499 0 : return false;
7500 : }
7501 : } else {
7502 0 : nsCSSValueList* cur = value.SetListValue();
7503 0 : for (;;) {
7504 0 : if (!ParseVariant(cur->mValue, VARIANT_UK, nsCSSProps::kCursorKTable)) {
7505 0 : return false;
7506 : }
7507 0 : if (cur->mValue.GetUnit() != eCSSUnit_URL) { // keyword must be last
7508 0 : if (ExpectEndProperty()) {
7509 0 : break;
7510 : }
7511 0 : return false;
7512 : }
7513 :
7514 : // We have a URL, so make a value array with three values.
7515 0 : nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
7516 0 : val->Item(0) = cur->mValue;
7517 :
7518 : // Parse optional x and y position of cursor hotspot (css3-ui).
7519 0 : if (ParseVariant(val->Item(1), VARIANT_NUMBER, nsnull)) {
7520 : // If we have one number, we must have two.
7521 0 : if (!ParseVariant(val->Item(2), VARIANT_NUMBER, nsnull)) {
7522 0 : return false;
7523 : }
7524 : }
7525 0 : cur->mValue.SetArrayValue(val, eCSSUnit_Array);
7526 :
7527 0 : if (!ExpectSymbol(',', true)) { // url must not be last
7528 0 : return false;
7529 : }
7530 0 : cur->mNext = new nsCSSValueList;
7531 0 : cur = cur->mNext;
7532 : }
7533 : }
7534 0 : AppendValue(eCSSProperty_cursor, value);
7535 0 : return true;
7536 : }
7537 :
7538 :
7539 : bool
7540 0 : CSSParserImpl::ParseFont()
7541 : {
7542 : static const nsCSSProperty fontIDs[] = {
7543 : eCSSProperty_font_style,
7544 : eCSSProperty_font_variant,
7545 : eCSSProperty_font_weight
7546 : };
7547 :
7548 0 : nsCSSValue family;
7549 0 : if (ParseVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) {
7550 0 : if (ExpectEndProperty()) {
7551 0 : if (eCSSUnit_Inherit == family.GetUnit() ||
7552 0 : eCSSUnit_Initial == family.GetUnit()) {
7553 0 : AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
7554 0 : AppendValue(eCSSProperty_font_family, family);
7555 0 : AppendValue(eCSSProperty_font_style, family);
7556 0 : AppendValue(eCSSProperty_font_variant, family);
7557 0 : AppendValue(eCSSProperty_font_weight, family);
7558 0 : AppendValue(eCSSProperty_font_size, family);
7559 0 : AppendValue(eCSSProperty_line_height, family);
7560 0 : AppendValue(eCSSProperty_font_stretch, family);
7561 0 : AppendValue(eCSSProperty_font_size_adjust, family);
7562 0 : AppendValue(eCSSProperty_font_feature_settings, family);
7563 0 : AppendValue(eCSSProperty_font_language_override, family);
7564 : }
7565 : else {
7566 0 : AppendValue(eCSSProperty__x_system_font, family);
7567 0 : nsCSSValue systemFont(eCSSUnit_System_Font);
7568 0 : AppendValue(eCSSProperty_font_family, systemFont);
7569 0 : AppendValue(eCSSProperty_font_style, systemFont);
7570 0 : AppendValue(eCSSProperty_font_variant, systemFont);
7571 0 : AppendValue(eCSSProperty_font_weight, systemFont);
7572 0 : AppendValue(eCSSProperty_font_size, systemFont);
7573 0 : AppendValue(eCSSProperty_line_height, systemFont);
7574 0 : AppendValue(eCSSProperty_font_stretch, systemFont);
7575 0 : AppendValue(eCSSProperty_font_size_adjust, systemFont);
7576 0 : AppendValue(eCSSProperty_font_feature_settings, systemFont);
7577 0 : AppendValue(eCSSProperty_font_language_override, systemFont);
7578 : }
7579 0 : return true;
7580 : }
7581 0 : return false;
7582 : }
7583 :
7584 : // Get optional font-style, font-variant and font-weight (in any order)
7585 0 : const PRInt32 numProps = 3;
7586 0 : nsCSSValue values[numProps];
7587 0 : PRInt32 found = ParseChoice(values, fontIDs, numProps);
7588 0 : if ((found < 0) || (eCSSUnit_Inherit == values[0].GetUnit()) ||
7589 0 : (eCSSUnit_Initial == values[0].GetUnit())) { // illegal data
7590 0 : return false;
7591 : }
7592 0 : if ((found & 1) == 0) {
7593 : // Provide default font-style
7594 0 : values[0].SetIntValue(NS_FONT_STYLE_NORMAL, eCSSUnit_Enumerated);
7595 : }
7596 0 : if ((found & 2) == 0) {
7597 : // Provide default font-variant
7598 0 : values[1].SetIntValue(NS_FONT_VARIANT_NORMAL, eCSSUnit_Enumerated);
7599 : }
7600 0 : if ((found & 4) == 0) {
7601 : // Provide default font-weight
7602 0 : values[2].SetIntValue(NS_FONT_WEIGHT_NORMAL, eCSSUnit_Enumerated);
7603 : }
7604 :
7605 : // Get mandatory font-size
7606 0 : nsCSSValue size;
7607 0 : if (! ParseVariant(size, VARIANT_KEYWORD | VARIANT_LP, nsCSSProps::kFontSizeKTable)) {
7608 0 : return false;
7609 : }
7610 :
7611 : // Get optional "/" line-height
7612 0 : nsCSSValue lineHeight;
7613 0 : if (ExpectSymbol('/', true)) {
7614 0 : if (! ParseNonNegativeVariant(lineHeight,
7615 : VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL,
7616 0 : nsnull)) {
7617 0 : return false;
7618 : }
7619 : }
7620 : else {
7621 0 : lineHeight.SetNormalValue();
7622 : }
7623 :
7624 : // Get final mandatory font-family
7625 0 : nsAutoParseCompoundProperty compound(this);
7626 0 : if (ParseFamily(family)) {
7627 0 : if ((eCSSUnit_Inherit != family.GetUnit()) && (eCSSUnit_Initial != family.GetUnit()) &&
7628 0 : ExpectEndProperty()) {
7629 0 : AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
7630 0 : AppendValue(eCSSProperty_font_family, family);
7631 0 : AppendValue(eCSSProperty_font_style, values[0]);
7632 0 : AppendValue(eCSSProperty_font_variant, values[1]);
7633 0 : AppendValue(eCSSProperty_font_weight, values[2]);
7634 0 : AppendValue(eCSSProperty_font_size, size);
7635 0 : AppendValue(eCSSProperty_line_height, lineHeight);
7636 : AppendValue(eCSSProperty_font_stretch,
7637 0 : nsCSSValue(NS_FONT_STRETCH_NORMAL, eCSSUnit_Enumerated));
7638 0 : AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
7639 0 : AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal));
7640 0 : AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal));
7641 0 : return true;
7642 : }
7643 : }
7644 0 : return false;
7645 : }
7646 :
7647 : bool
7648 0 : CSSParserImpl::ParseFontWeight(nsCSSValue& aValue)
7649 : {
7650 0 : if (ParseVariant(aValue, VARIANT_HKI | VARIANT_SYSFONT,
7651 : nsCSSProps::kFontWeightKTable)) {
7652 0 : if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
7653 0 : PRInt32 intValue = aValue.GetIntValue();
7654 0 : if ((100 <= intValue) &&
7655 : (intValue <= 900) &&
7656 : (0 == (intValue % 100))) {
7657 0 : return true;
7658 : } else {
7659 0 : UngetToken();
7660 0 : return false;
7661 : }
7662 : }
7663 0 : return true;
7664 : }
7665 0 : return false;
7666 : }
7667 :
7668 : bool
7669 0 : CSSParserImpl::ParseOneFamily(nsAString& aFamily)
7670 : {
7671 0 : if (!GetToken(true))
7672 0 : return false;
7673 :
7674 0 : nsCSSToken* tk = &mToken;
7675 :
7676 0 : if (eCSSToken_Ident == tk->mType) {
7677 0 : aFamily.Append(tk->mIdent);
7678 0 : for (;;) {
7679 0 : if (!GetToken(false))
7680 0 : break;
7681 :
7682 0 : if (eCSSToken_Ident == tk->mType) {
7683 0 : aFamily.Append(tk->mIdent);
7684 0 : } else if (eCSSToken_WhiteSpace == tk->mType) {
7685 : // Lookahead one token and drop whitespace if we are ending the
7686 : // font name.
7687 0 : if (!GetToken(true))
7688 0 : break;
7689 :
7690 0 : UngetToken();
7691 0 : if (eCSSToken_Ident == tk->mType)
7692 0 : aFamily.Append(PRUnichar(' '));
7693 : else
7694 0 : break;
7695 : } else {
7696 0 : UngetToken();
7697 0 : break;
7698 : }
7699 : }
7700 0 : return true;
7701 :
7702 0 : } else if (eCSSToken_String == tk->mType) {
7703 0 : aFamily.Append(tk->mSymbol); // replace the quotes
7704 0 : aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
7705 0 : aFamily.Append(tk->mSymbol);
7706 0 : return true;
7707 :
7708 : } else {
7709 0 : UngetToken();
7710 0 : return false;
7711 : }
7712 : }
7713 :
7714 : ///////////////////////////////////////////////////////
7715 : // -moz-transform Parsing Implementation
7716 :
7717 : /* Reads a function list of arguments. Do not call this function
7718 : * directly; it's mean to be caled from ParseFunction.
7719 : */
7720 : bool
7721 0 : CSSParserImpl::ParseFunctionInternals(const PRInt32 aVariantMask[],
7722 : PRUint16 aMinElems,
7723 : PRUint16 aMaxElems,
7724 : InfallibleTArray<nsCSSValue> &aOutput)
7725 : {
7726 0 : for (PRUint16 index = 0; index < aMaxElems; ++index) {
7727 0 : nsCSSValue newValue;
7728 0 : if (!ParseVariant(newValue, aVariantMask[index], nsnull))
7729 0 : return false;
7730 :
7731 0 : aOutput.AppendElement(newValue);
7732 :
7733 : // See whether to continue or whether to look for end of function.
7734 0 : if (!ExpectSymbol(',', true)) {
7735 : // We need to read the closing parenthesis, and also must take care
7736 : // that we haven't read too few symbols.
7737 0 : return ExpectSymbol(')', true) && (index + 1) >= aMinElems;
7738 : }
7739 : }
7740 :
7741 : // If we're here, we finished looping without hitting the end, so we read too
7742 : // many elements.
7743 0 : return false;
7744 : }
7745 :
7746 : /* Parses a function [ input of the form (a [, b]*) ] and stores it
7747 : * as an nsCSSValue that holds a function of the form
7748 : * function-name arg1 arg2 ... argN
7749 : *
7750 : * On error, the return value is false.
7751 : *
7752 : * @param aFunction The name of the function that we're reading.
7753 : * @param aAllowedTypes An array of values corresponding to the legal
7754 : * types for each element in the function. The zeroth element in the
7755 : * array corresponds to the first function parameter, etc. The length
7756 : * of this array _must_ be greater than or equal to aMaxElems or the
7757 : * behavior is undefined.
7758 : * @param aMinElems Minimum number of elements to read. Reading fewer than
7759 : * this many elements will result in the function failing.
7760 : * @param aMaxElems Maximum number of elements to read. Reading more than
7761 : * this many elements will result in the function failing.
7762 : * @param aValue (out) The value that was parsed.
7763 : */
7764 : bool
7765 0 : CSSParserImpl::ParseFunction(const nsString &aFunction,
7766 : const PRInt32 aAllowedTypes[],
7767 : PRUint16 aMinElems, PRUint16 aMaxElems,
7768 : nsCSSValue &aValue)
7769 : {
7770 : typedef InfallibleTArray<nsCSSValue>::size_type arrlen_t;
7771 :
7772 : /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
7773 : * elements stored in the the nsCSSValue::Array.
7774 : */
7775 : static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
7776 :
7777 : /* Make a copy of the function name, since the reference is _probably_ to
7778 : * mToken.mIdent, which is going to get overwritten during the course of this
7779 : * function.
7780 : */
7781 0 : nsString functionName(aFunction);
7782 :
7783 : /* Read in a list of values as an array, failing if we can't or if
7784 : * it's out of bounds.
7785 : */
7786 0 : InfallibleTArray<nsCSSValue> foundValues;
7787 0 : if (!ParseFunctionInternals(aAllowedTypes, aMinElems, aMaxElems,
7788 0 : foundValues))
7789 0 : return false;
7790 :
7791 : /* Now, convert this array into an nsCSSValue::Array object.
7792 : * We'll need N + 1 spots, one for the function name and the rest for the
7793 : * arguments. In case the user has given us more than 2^16 - 2 arguments,
7794 : * we'll truncate them at 2^16 - 2 arguments.
7795 : */
7796 0 : PRUint16 numElements = (foundValues.Length() <= MAX_ALLOWED_ELEMS ?
7797 0 : foundValues.Length() + 1 : MAX_ALLOWED_ELEMS);
7798 : nsRefPtr<nsCSSValue::Array> convertedArray =
7799 0 : nsCSSValue::Array::Create(numElements);
7800 :
7801 : /* Copy things over. */
7802 0 : convertedArray->Item(0).SetStringValue(functionName, eCSSUnit_Ident);
7803 0 : for (PRUint16 index = 0; index + 1 < numElements; ++index)
7804 0 : convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
7805 :
7806 : /* Fill in the outparam value with the array. */
7807 0 : aValue.SetArrayValue(convertedArray, eCSSUnit_Function);
7808 :
7809 : /* Return it! */
7810 0 : return true;
7811 : }
7812 :
7813 : /**
7814 : * Given a token, determines the minimum and maximum number of function
7815 : * parameters to read, along with the mask that should be used to read
7816 : * those function parameters. If the token isn't a transform function,
7817 : * returns an error.
7818 : *
7819 : * @param aToken The token identifying the function.
7820 : * @param aMinElems [out] The minimum number of elements to read.
7821 : * @param aMaxElems [out] The maximum number of elements to read
7822 : * @param aVariantMask [out] The variant mask to use during parsing
7823 : * @return Whether the information was loaded successfully.
7824 : */
7825 0 : static bool GetFunctionParseInformation(nsCSSKeyword aToken,
7826 : PRUint16 &aMinElems,
7827 : PRUint16 &aMaxElems,
7828 : const PRInt32 *& aVariantMask,
7829 : bool &aIs3D)
7830 : {
7831 : /* These types represent the common variant masks that will be used to
7832 : * parse out the individual functions. The order in the enumeration
7833 : * must match the order in which the masks are declared.
7834 : */
7835 : enum { eLengthPercentCalc,
7836 : eLengthCalc,
7837 : eTwoLengthPercentCalcs,
7838 : eTwoLengthPercentCalcsOneLengthCalc,
7839 : eAngle,
7840 : eTwoAngles,
7841 : eNumber,
7842 : ePositiveLength,
7843 : eTwoNumbers,
7844 : eThreeNumbers,
7845 : eThreeNumbersOneAngle,
7846 : eMatrix,
7847 : eMatrix3d,
7848 : eNumVariantMasks };
7849 : static const PRInt32 kMaxElemsPerFunction = 16;
7850 : static const PRInt32 kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
7851 : {VARIANT_LPCALC},
7852 : {VARIANT_LENGTH|VARIANT_CALC},
7853 : {VARIANT_LPCALC, VARIANT_LPCALC},
7854 : {VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LENGTH|VARIANT_CALC},
7855 : {VARIANT_ANGLE_OR_ZERO},
7856 : {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO},
7857 : {VARIANT_NUMBER},
7858 : {VARIANT_LENGTH|VARIANT_POSITIVE_LENGTH},
7859 : {VARIANT_NUMBER, VARIANT_NUMBER},
7860 : {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
7861 : {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO},
7862 : {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
7863 : VARIANT_LPNCALC, VARIANT_LPNCALC},
7864 : {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
7865 : VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
7866 : VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
7867 : VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}};
7868 :
7869 : #ifdef DEBUG
7870 : static const PRUint8 kVariantMaskLengths[eNumVariantMasks] =
7871 : {1, 1, 2, 3, 1, 2, 1, 1, 2, 3, 4, 6, 16};
7872 : #endif
7873 :
7874 0 : PRInt32 variantIndex = eNumVariantMasks;
7875 :
7876 0 : aIs3D = false;
7877 :
7878 0 : switch (aToken) {
7879 : case eCSSKeyword_translatex:
7880 : case eCSSKeyword_translatey:
7881 : /* Exactly one length or percent. */
7882 0 : variantIndex = eLengthPercentCalc;
7883 0 : aMinElems = 1U;
7884 0 : aMaxElems = 1U;
7885 0 : break;
7886 : case eCSSKeyword_translatez:
7887 : /* Exactly one length */
7888 0 : variantIndex = eLengthCalc;
7889 0 : aMinElems = 1U;
7890 0 : aMaxElems = 1U;
7891 0 : aIs3D = true;
7892 0 : break;
7893 : case eCSSKeyword_translate3d:
7894 : /* Exactly two lengthds or percents and a number */
7895 0 : variantIndex = eTwoLengthPercentCalcsOneLengthCalc;
7896 0 : aMinElems = 3U;
7897 0 : aMaxElems = 3U;
7898 0 : aIs3D = true;
7899 0 : break;
7900 : case eCSSKeyword_scalez:
7901 0 : aIs3D = true;
7902 : case eCSSKeyword_scalex:
7903 : case eCSSKeyword_scaley:
7904 : /* Exactly one scale factor. */
7905 0 : variantIndex = eNumber;
7906 0 : aMinElems = 1U;
7907 0 : aMaxElems = 1U;
7908 0 : break;
7909 : case eCSSKeyword_scale3d:
7910 : /* Exactly three scale factors. */
7911 0 : variantIndex = eThreeNumbers;
7912 0 : aMinElems = 3U;
7913 0 : aMaxElems = 3U;
7914 0 : aIs3D = true;
7915 0 : break;
7916 : case eCSSKeyword_rotatex:
7917 : case eCSSKeyword_rotatey:
7918 0 : aIs3D = true;
7919 : case eCSSKeyword_rotate:
7920 : case eCSSKeyword_rotatez:
7921 : /* Exactly one angle. */
7922 0 : variantIndex = eAngle;
7923 0 : aMinElems = 1U;
7924 0 : aMaxElems = 1U;
7925 0 : break;
7926 : case eCSSKeyword_rotate3d:
7927 0 : variantIndex = eThreeNumbersOneAngle;
7928 0 : aMinElems = 4U;
7929 0 : aMaxElems = 4U;
7930 0 : aIs3D = true;
7931 0 : break;
7932 : case eCSSKeyword_translate:
7933 : /* One or two lengths or percents. */
7934 0 : variantIndex = eTwoLengthPercentCalcs;
7935 0 : aMinElems = 1U;
7936 0 : aMaxElems = 2U;
7937 0 : break;
7938 : case eCSSKeyword_skew:
7939 : /* Exactly one or two angles. */
7940 0 : variantIndex = eTwoAngles;
7941 0 : aMinElems = 1U;
7942 0 : aMaxElems = 2U;
7943 0 : break;
7944 : case eCSSKeyword_scale:
7945 : /* One or two scale factors. */
7946 0 : variantIndex = eTwoNumbers;
7947 0 : aMinElems = 1U;
7948 0 : aMaxElems = 2U;
7949 0 : break;
7950 : case eCSSKeyword_skewx:
7951 : /* Exactly one angle. */
7952 0 : variantIndex = eAngle;
7953 0 : aMinElems = 1U;
7954 0 : aMaxElems = 1U;
7955 0 : break;
7956 : case eCSSKeyword_skewy:
7957 : /* Exactly one angle. */
7958 0 : variantIndex = eAngle;
7959 0 : aMinElems = 1U;
7960 0 : aMaxElems = 1U;
7961 0 : break;
7962 : case eCSSKeyword_matrix:
7963 : /* Six values, which can be numbers, lengths, or percents. */
7964 0 : variantIndex = eMatrix;
7965 0 : aMinElems = 6U;
7966 0 : aMaxElems = 6U;
7967 0 : break;
7968 : case eCSSKeyword_matrix3d:
7969 : /* 16 matrix values, all numbers */
7970 0 : variantIndex = eMatrix3d;
7971 0 : aMinElems = 16U;
7972 0 : aMaxElems = 16U;
7973 0 : aIs3D = true;
7974 0 : break;
7975 : case eCSSKeyword_perspective:
7976 : /* Exactly one scale number. */
7977 0 : variantIndex = ePositiveLength;
7978 0 : aMinElems = 1U;
7979 0 : aMaxElems = 1U;
7980 0 : aIs3D = true;
7981 0 : break;
7982 : default:
7983 : /* Oh dear, we didn't match. Report an error. */
7984 0 : return false;
7985 : }
7986 :
7987 0 : NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
7988 0 : NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
7989 0 : NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
7990 0 : NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
7991 0 : NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
7992 : #ifdef DEBUG
7993 0 : NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
7994 : "Invalid aMaxElems for this variant mask.");
7995 : #endif
7996 :
7997 : // Convert the index into a mask.
7998 0 : aVariantMask = kVariantMasks[variantIndex];
7999 :
8000 0 : return true;
8001 : }
8002 :
8003 : /* Reads a single transform function from the tokenizer stream, reporting an
8004 : * error if something goes wrong.
8005 : */
8006 : bool
8007 0 : CSSParserImpl::ParseSingleTransform(nsCSSValue& aValue, bool& aIs3D)
8008 : {
8009 0 : if (!GetToken(true))
8010 0 : return false;
8011 :
8012 0 : if (mToken.mType != eCSSToken_Function) {
8013 0 : UngetToken();
8014 0 : return false;
8015 : }
8016 :
8017 : const PRInt32* variantMask;
8018 : PRUint16 minElems, maxElems;
8019 0 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
8020 :
8021 0 : if (!GetFunctionParseInformation(keyword,
8022 0 : minElems, maxElems, variantMask, aIs3D))
8023 0 : return false;
8024 :
8025 : // Bug 721136: Normalize the identifier to lowercase, except that things
8026 : // like scaleX should have the last character capitalized. This matches
8027 : // what other browsers do.
8028 0 : nsContentUtils::ASCIIToLower(mToken.mIdent);
8029 0 : switch (keyword) {
8030 : case eCSSKeyword_rotatex:
8031 : case eCSSKeyword_scalex:
8032 : case eCSSKeyword_skewx:
8033 : case eCSSKeyword_translatex:
8034 0 : mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('X'));
8035 0 : break;
8036 :
8037 : case eCSSKeyword_rotatey:
8038 : case eCSSKeyword_scaley:
8039 : case eCSSKeyword_skewy:
8040 : case eCSSKeyword_translatey:
8041 0 : mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('Y'));
8042 0 : break;
8043 :
8044 : case eCSSKeyword_rotatez:
8045 : case eCSSKeyword_scalez:
8046 : case eCSSKeyword_translatez:
8047 0 : mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('Z'));
8048 0 : break;
8049 :
8050 : default:
8051 0 : break;
8052 : }
8053 :
8054 0 : return ParseFunction(mToken.mIdent, variantMask, minElems, maxElems, aValue);
8055 : }
8056 :
8057 : /* Parses a -moz-transform property list by continuously reading in properties
8058 : * and constructing a matrix from it.
8059 : */
8060 0 : bool CSSParserImpl::ParseMozTransform()
8061 : {
8062 0 : nsCSSValue value;
8063 0 : if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nsnull)) {
8064 : // 'inherit', 'initial', and 'none' must be alone
8065 0 : if (!ExpectEndProperty()) {
8066 0 : return false;
8067 : }
8068 : } else {
8069 0 : nsCSSValueList* cur = value.SetListValue();
8070 0 : for (;;) {
8071 : bool is3D;
8072 0 : if (!ParseSingleTransform(cur->mValue, is3D)) {
8073 0 : return false;
8074 : }
8075 0 : if (is3D && !nsLayoutUtils::Are3DTransformsEnabled()) {
8076 0 : return false;
8077 : }
8078 0 : if (CheckEndProperty()) {
8079 0 : break;
8080 : }
8081 0 : cur->mNext = new nsCSSValueList;
8082 0 : cur = cur->mNext;
8083 : }
8084 : }
8085 0 : AppendValue(eCSSProperty__moz_transform, value);
8086 0 : return true;
8087 : }
8088 :
8089 0 : bool CSSParserImpl::ParseMozTransformOrigin(bool aPerspective)
8090 : {
8091 0 : nsCSSValuePair position;
8092 0 : if (!ParseBoxPositionValues(position, true))
8093 0 : return false;
8094 :
8095 0 : nsCSSProperty prop = eCSSProperty__moz_transform_origin;
8096 0 : if (aPerspective) {
8097 0 : if (!ExpectEndProperty()) {
8098 0 : return false;
8099 : }
8100 0 : prop = eCSSProperty_perspective_origin;
8101 : }
8102 :
8103 : // Unlike many other uses of pairs, this position should always be stored
8104 : // as a pair, even if the values are the same, so it always serializes as
8105 : // a pair, and to keep the computation code simple.
8106 0 : if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
8107 0 : position.mXValue.GetUnit() == eCSSUnit_Initial) {
8108 0 : NS_ABORT_IF_FALSE(position.mXValue == position.mYValue,
8109 : "inherit/initial only half?");
8110 0 : AppendValue(prop, position.mXValue);
8111 : } else {
8112 0 : nsCSSValue value;
8113 0 : if (aPerspective) {
8114 0 : value.SetPairValue(position.mXValue, position.mYValue);
8115 : } else {
8116 0 : nsCSSValue depth;
8117 0 : if (!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nsnull) ||
8118 0 : !nsLayoutUtils::Are3DTransformsEnabled()) {
8119 0 : depth.Reset();
8120 : }
8121 0 : value.SetTripletValue(position.mXValue, position.mYValue, depth);
8122 : }
8123 :
8124 0 : AppendValue(prop, value);
8125 : }
8126 0 : return true;
8127 : }
8128 :
8129 : bool
8130 0 : CSSParserImpl::ParseFamily(nsCSSValue& aValue)
8131 : {
8132 0 : if (!GetToken(true))
8133 0 : return false;
8134 :
8135 0 : if (eCSSToken_Ident == mToken.mType) {
8136 0 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
8137 0 : if (keyword == eCSSKeyword_inherit) {
8138 0 : aValue.SetInheritValue();
8139 0 : return true;
8140 : }
8141 0 : if (keyword == eCSSKeyword__moz_initial) {
8142 0 : aValue.SetInitialValue();
8143 0 : return true;
8144 : }
8145 0 : if (keyword == eCSSKeyword__moz_use_system_font &&
8146 0 : !IsParsingCompoundProperty()) {
8147 0 : aValue.SetSystemFontValue();
8148 0 : return true;
8149 : }
8150 : }
8151 :
8152 0 : UngetToken();
8153 :
8154 0 : nsAutoString family;
8155 0 : for (;;) {
8156 0 : if (!ParseOneFamily(family))
8157 0 : return false;
8158 :
8159 0 : if (!ExpectSymbol(',', true))
8160 : break;
8161 :
8162 0 : family.Append(PRUnichar(','));
8163 : }
8164 :
8165 0 : if (family.IsEmpty()) {
8166 0 : return false;
8167 : }
8168 0 : aValue.SetStringValue(family, eCSSUnit_Families);
8169 0 : return true;
8170 : }
8171 :
8172 : // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
8173 : // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
8174 : // local-src: 'local(' ( string | ident ) ')'
8175 :
8176 : bool
8177 0 : CSSParserImpl::ParseFontSrc(nsCSSValue& aValue)
8178 : {
8179 : // could we maybe turn nsCSSValue::Array into InfallibleTArray<nsCSSValue>?
8180 0 : InfallibleTArray<nsCSSValue> values;
8181 0 : nsCSSValue cur;
8182 0 : for (;;) {
8183 0 : if (!GetToken(true))
8184 0 : break;
8185 :
8186 0 : if (mToken.mType == eCSSToken_URL) {
8187 0 : SetValueToURL(cur, mToken.mIdent);
8188 0 : values.AppendElement(cur);
8189 0 : if (!ParseFontSrcFormat(values))
8190 0 : return false;
8191 :
8192 0 : } else if (mToken.mType == eCSSToken_Function &&
8193 0 : mToken.mIdent.LowerCaseEqualsLiteral("local")) {
8194 : // css3-fonts does not specify a formal grammar for local().
8195 : // The text permits both unquoted identifiers and quoted
8196 : // strings. We resolve this ambiguity in the spec by
8197 : // assuming that the appropriate production is a single
8198 : // <family-name>, possibly surrounded by whitespace.
8199 :
8200 0 : nsAutoString family;
8201 0 : if (!ParseOneFamily(family)) {
8202 0 : SkipUntil(')');
8203 0 : return false;
8204 : }
8205 0 : if (!ExpectSymbol(')', true)) {
8206 0 : SkipUntil(')');
8207 0 : return false;
8208 : }
8209 :
8210 : // the style parameters to the nsFont constructor are ignored,
8211 : // because it's only being used to call EnumerateFamilies
8212 0 : nsFont font(family, 0, 0, 0, 0, 0, 0);
8213 0 : ExtractFirstFamilyData dat;
8214 :
8215 0 : font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
8216 0 : if (!dat.mGood)
8217 0 : return false;
8218 :
8219 0 : cur.SetStringValue(dat.mFamilyName, eCSSUnit_Local_Font);
8220 0 : values.AppendElement(cur);
8221 : } else {
8222 0 : return false;
8223 : }
8224 :
8225 0 : if (!ExpectSymbol(',', true))
8226 0 : break;
8227 : }
8228 :
8229 0 : if (values.Length() == 0)
8230 0 : return false;
8231 :
8232 : nsRefPtr<nsCSSValue::Array> srcVals
8233 0 : = nsCSSValue::Array::Create(values.Length());
8234 :
8235 : PRUint32 i;
8236 0 : for (i = 0; i < values.Length(); i++)
8237 0 : srcVals->Item(i) = values[i];
8238 0 : aValue.SetArrayValue(srcVals, eCSSUnit_Array);
8239 0 : return true;
8240 : }
8241 :
8242 : bool
8243 0 : CSSParserImpl::ParseFontSrcFormat(InfallibleTArray<nsCSSValue> & values)
8244 : {
8245 0 : if (!GetToken(true))
8246 0 : return true; // EOF harmless here
8247 0 : if (mToken.mType != eCSSToken_Function ||
8248 0 : !mToken.mIdent.LowerCaseEqualsLiteral("format")) {
8249 0 : UngetToken();
8250 0 : return true;
8251 : }
8252 :
8253 0 : do {
8254 0 : if (!GetToken(true))
8255 0 : return false; // EOF - no need for SkipUntil
8256 :
8257 0 : if (mToken.mType != eCSSToken_String) {
8258 0 : UngetToken();
8259 0 : SkipUntil(')');
8260 0 : return false;
8261 : }
8262 :
8263 0 : nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format);
8264 0 : values.AppendElement(cur);
8265 : } while (ExpectSymbol(',', true));
8266 :
8267 0 : if (!ExpectSymbol(')', true)) {
8268 0 : SkipUntil(')');
8269 0 : return false;
8270 : }
8271 :
8272 0 : return true;
8273 : }
8274 :
8275 : // font-ranges: urange ( ',' urange )*
8276 : bool
8277 0 : CSSParserImpl::ParseFontRanges(nsCSSValue& aValue)
8278 : {
8279 0 : InfallibleTArray<PRUint32> ranges;
8280 0 : for (;;) {
8281 0 : if (!GetToken(true))
8282 0 : break;
8283 :
8284 0 : if (mToken.mType != eCSSToken_URange) {
8285 0 : UngetToken();
8286 0 : break;
8287 : }
8288 :
8289 : // An invalid range token is a parsing error, causing the entire
8290 : // descriptor to be ignored.
8291 0 : if (!mToken.mIntegerValid)
8292 0 : return false;
8293 :
8294 0 : PRUint32 low = mToken.mInteger;
8295 0 : PRUint32 high = mToken.mInteger2;
8296 :
8297 : // A range that descends, or a range that is entirely outside the
8298 : // current range of Unicode (U+0-10FFFF) is ignored, but does not
8299 : // invalidate the descriptor. A range that straddles the high end
8300 : // is clipped.
8301 0 : if (low <= 0x10FFFF && low <= high) {
8302 0 : if (high > 0x10FFFF)
8303 0 : high = 0x10FFFF;
8304 :
8305 0 : ranges.AppendElement(low);
8306 0 : ranges.AppendElement(high);
8307 : }
8308 0 : if (!ExpectSymbol(',', true))
8309 0 : break;
8310 : }
8311 :
8312 0 : if (ranges.Length() == 0)
8313 0 : return false;
8314 :
8315 : nsRefPtr<nsCSSValue::Array> srcVals
8316 0 : = nsCSSValue::Array::Create(ranges.Length());
8317 :
8318 0 : for (PRUint32 i = 0; i < ranges.Length(); i++)
8319 0 : srcVals->Item(i).SetIntValue(ranges[i], eCSSUnit_Integer);
8320 0 : aValue.SetArrayValue(srcVals, eCSSUnit_Array);
8321 0 : return true;
8322 : }
8323 :
8324 : bool
8325 0 : CSSParserImpl::ParseListStyle()
8326 : {
8327 : // 'list-style' can accept 'none' for two different subproperties,
8328 : // 'list-style-type' and 'list-style-position'. In order to accept
8329 : // 'none' as the value of either but still allow another value for
8330 : // either, we need to ensure that the first 'none' we find gets
8331 : // allocated to a dummy property instead.
8332 : static const nsCSSProperty listStyleIDs[] = {
8333 : eCSSPropertyExtra_x_none_value,
8334 : eCSSProperty_list_style_type,
8335 : eCSSProperty_list_style_position,
8336 : eCSSProperty_list_style_image
8337 : };
8338 :
8339 0 : nsCSSValue values[NS_ARRAY_LENGTH(listStyleIDs)];
8340 : PRInt32 found =
8341 0 : ParseChoice(values, listStyleIDs, ArrayLength(listStyleIDs));
8342 0 : if (found < 1 || !ExpectEndProperty()) {
8343 0 : return false;
8344 : }
8345 :
8346 0 : if ((found & (1|2|8)) == (1|2|8)) {
8347 0 : if (values[0].GetUnit() == eCSSUnit_None) {
8348 : // We found a 'none' plus another value for both of
8349 : // 'list-style-type' and 'list-style-image'. This is a parse
8350 : // error, since the 'none' has to count for at least one of them.
8351 0 : return false;
8352 : } else {
8353 0 : NS_ASSERTION(found == (1|2|4|8) && values[0] == values[1] &&
8354 : values[0] == values[2] && values[0] == values[3],
8355 : "should be a special value");
8356 : }
8357 : }
8358 :
8359 : // Provide default values
8360 0 : if ((found & 2) == 0) {
8361 0 : if (found & 1) {
8362 0 : values[1].SetIntValue(NS_STYLE_LIST_STYLE_NONE, eCSSUnit_Enumerated);
8363 : } else {
8364 0 : values[1].SetIntValue(NS_STYLE_LIST_STYLE_DISC, eCSSUnit_Enumerated);
8365 : }
8366 : }
8367 0 : if ((found & 4) == 0) {
8368 : values[2].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE,
8369 0 : eCSSUnit_Enumerated);
8370 : }
8371 0 : if ((found & 8) == 0) {
8372 0 : values[3].SetNoneValue();
8373 : }
8374 :
8375 : // Start at 1 to avoid appending fake value.
8376 0 : for (PRUint32 index = 1; index < ArrayLength(listStyleIDs); ++index) {
8377 0 : AppendValue(listStyleIDs[index], values[index]);
8378 : }
8379 0 : return true;
8380 : }
8381 :
8382 : bool
8383 0 : CSSParserImpl::ParseMargin()
8384 : {
8385 : static const nsCSSProperty kMarginSideIDs[] = {
8386 : eCSSProperty_margin_top,
8387 : eCSSProperty_margin_right_value,
8388 : eCSSProperty_margin_bottom,
8389 : eCSSProperty_margin_left_value
8390 : };
8391 : static const nsCSSProperty kMarginSources[] = {
8392 : eCSSProperty_margin_left_ltr_source,
8393 : eCSSProperty_margin_left_rtl_source,
8394 : eCSSProperty_margin_right_ltr_source,
8395 : eCSSProperty_margin_right_rtl_source,
8396 : eCSSProperty_UNKNOWN
8397 : };
8398 :
8399 : // do this now, in case 4 values weren't specified
8400 0 : InitBoxPropsAsPhysical(kMarginSources);
8401 0 : return ParseBoxProperties(kMarginSideIDs);
8402 : }
8403 :
8404 : bool
8405 0 : CSSParserImpl::ParseMarks(nsCSSValue& aValue)
8406 : {
8407 0 : if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kPageMarksKTable)) {
8408 0 : if (eCSSUnit_Enumerated == aValue.GetUnit()) {
8409 0 : if (NS_STYLE_PAGE_MARKS_NONE != aValue.GetIntValue() &&
8410 0 : false == CheckEndProperty()) {
8411 0 : nsCSSValue second;
8412 0 : if (ParseEnum(second, nsCSSProps::kPageMarksKTable)) {
8413 : // 'none' keyword in conjuction with others is not allowed
8414 0 : if (NS_STYLE_PAGE_MARKS_NONE != second.GetIntValue()) {
8415 0 : aValue.SetIntValue(aValue.GetIntValue() | second.GetIntValue(),
8416 0 : eCSSUnit_Enumerated);
8417 0 : return true;
8418 : }
8419 : }
8420 0 : return false;
8421 : }
8422 : }
8423 0 : return true;
8424 : }
8425 0 : return false;
8426 : }
8427 :
8428 : bool
8429 0 : CSSParserImpl::ParseOutline()
8430 : {
8431 0 : const PRInt32 numProps = 3;
8432 : static const nsCSSProperty kOutlineIDs[] = {
8433 : eCSSProperty_outline_color,
8434 : eCSSProperty_outline_style,
8435 : eCSSProperty_outline_width
8436 : };
8437 :
8438 0 : nsCSSValue values[numProps];
8439 0 : PRInt32 found = ParseChoice(values, kOutlineIDs, numProps);
8440 0 : if ((found < 1) || (false == ExpectEndProperty())) {
8441 0 : return false;
8442 : }
8443 :
8444 : // Provide default values
8445 0 : if ((found & 1) == 0) { // Provide default outline-color
8446 0 : values[0].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
8447 : }
8448 0 : if ((found & 2) == 0) { // Provide default outline-style
8449 0 : values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
8450 : }
8451 0 : if ((found & 4) == 0) { // Provide default outline-width
8452 0 : values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
8453 : }
8454 :
8455 : PRInt32 index;
8456 0 : for (index = 0; index < numProps; index++) {
8457 0 : AppendValue(kOutlineIDs[index], values[index]);
8458 : }
8459 0 : return true;
8460 : }
8461 :
8462 : bool
8463 0 : CSSParserImpl::ParseOverflow()
8464 : {
8465 0 : nsCSSValue overflow;
8466 0 : if (!ParseVariant(overflow, VARIANT_HK,
8467 0 : nsCSSProps::kOverflowKTable) ||
8468 0 : !ExpectEndProperty())
8469 0 : return false;
8470 :
8471 0 : nsCSSValue overflowX(overflow);
8472 0 : nsCSSValue overflowY(overflow);
8473 0 : if (eCSSUnit_Enumerated == overflow.GetUnit())
8474 0 : switch(overflow.GetIntValue()) {
8475 : case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
8476 0 : overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
8477 0 : overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
8478 0 : break;
8479 : case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
8480 0 : overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
8481 0 : overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
8482 0 : break;
8483 : }
8484 0 : AppendValue(eCSSProperty_overflow_x, overflowX);
8485 0 : AppendValue(eCSSProperty_overflow_y, overflowY);
8486 0 : return true;
8487 : }
8488 :
8489 : bool
8490 0 : CSSParserImpl::ParsePadding()
8491 : {
8492 : static const nsCSSProperty kPaddingSideIDs[] = {
8493 : eCSSProperty_padding_top,
8494 : eCSSProperty_padding_right_value,
8495 : eCSSProperty_padding_bottom,
8496 : eCSSProperty_padding_left_value
8497 : };
8498 : static const nsCSSProperty kPaddingSources[] = {
8499 : eCSSProperty_padding_left_ltr_source,
8500 : eCSSProperty_padding_left_rtl_source,
8501 : eCSSProperty_padding_right_ltr_source,
8502 : eCSSProperty_padding_right_rtl_source,
8503 : eCSSProperty_UNKNOWN
8504 : };
8505 :
8506 : // do this now, in case 4 values weren't specified
8507 0 : InitBoxPropsAsPhysical(kPaddingSources);
8508 0 : return ParseBoxProperties(kPaddingSideIDs);
8509 : }
8510 :
8511 : bool
8512 0 : CSSParserImpl::ParseQuotes()
8513 : {
8514 0 : nsCSSValue value;
8515 0 : if (!ParseVariant(value, VARIANT_HOS, nsnull)) {
8516 0 : return false;
8517 : }
8518 0 : if (value.GetUnit() != eCSSUnit_String) {
8519 0 : if (!ExpectEndProperty()) {
8520 0 : return false;
8521 : }
8522 : } else {
8523 0 : nsCSSValue open = value;
8524 0 : nsCSSValuePairList* quotes = value.SetPairListValue();
8525 0 : for (;;) {
8526 0 : quotes->mXValue = open;
8527 : // get mandatory close
8528 0 : if (!ParseVariant(quotes->mYValue, VARIANT_STRING, nsnull)) {
8529 0 : return false;
8530 : }
8531 0 : if (CheckEndProperty()) {
8532 : break;
8533 : }
8534 : // look for another open
8535 0 : if (!ParseVariant(open, VARIANT_STRING, nsnull)) {
8536 0 : return false;
8537 : }
8538 0 : quotes->mNext = new nsCSSValuePairList;
8539 0 : quotes = quotes->mNext;
8540 : }
8541 : }
8542 0 : AppendValue(eCSSProperty_quotes, value);
8543 0 : return true;
8544 : }
8545 :
8546 : bool
8547 0 : CSSParserImpl::ParseSize()
8548 : {
8549 0 : nsCSSValue width, height;
8550 0 : if (!ParseVariant(width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) {
8551 0 : return false;
8552 : }
8553 0 : if (width.IsLengthUnit()) {
8554 0 : ParseVariant(height, VARIANT_LENGTH, nsnull);
8555 : }
8556 0 : if (!ExpectEndProperty()) {
8557 0 : return false;
8558 : }
8559 :
8560 0 : if (width == height || height.GetUnit() == eCSSUnit_Null) {
8561 0 : AppendValue(eCSSProperty_size, width);
8562 : } else {
8563 0 : nsCSSValue pair;
8564 0 : pair.SetPairValue(width, height);
8565 0 : AppendValue(eCSSProperty_size, pair);
8566 : }
8567 0 : return true;
8568 : }
8569 :
8570 : bool
8571 0 : CSSParserImpl::ParseTextDecoration()
8572 : {
8573 : enum {
8574 : eDecorationNone = NS_STYLE_TEXT_DECORATION_LINE_NONE,
8575 : eDecorationUnderline = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
8576 : eDecorationOverline = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE,
8577 : eDecorationLineThrough = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
8578 : eDecorationBlink = NS_STYLE_TEXT_DECORATION_LINE_BLINK,
8579 : eDecorationPrefAnchors = NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS
8580 : };
8581 : MOZ_STATIC_ASSERT((eDecorationNone ^ eDecorationUnderline ^
8582 : eDecorationOverline ^ eDecorationLineThrough ^
8583 : eDecorationBlink ^ eDecorationPrefAnchors) ==
8584 : (eDecorationNone | eDecorationUnderline |
8585 : eDecorationOverline | eDecorationLineThrough |
8586 : eDecorationBlink | eDecorationPrefAnchors),
8587 : "text decoration constants need to be bitmasks");
8588 :
8589 : static const PRInt32 kTextDecorationKTable[] = {
8590 : eCSSKeyword_none, eDecorationNone,
8591 : eCSSKeyword_underline, eDecorationUnderline,
8592 : eCSSKeyword_overline, eDecorationOverline,
8593 : eCSSKeyword_line_through, eDecorationLineThrough,
8594 : eCSSKeyword_blink, eDecorationBlink,
8595 : eCSSKeyword__moz_anchor_decoration, eDecorationPrefAnchors,
8596 : eCSSKeyword_UNKNOWN,-1
8597 : };
8598 :
8599 0 : nsCSSValue value;
8600 0 : if (!ParseVariant(value, VARIANT_HK, kTextDecorationKTable)) {
8601 0 : return false;
8602 : }
8603 :
8604 0 : nsCSSValue blink, line, style, color;
8605 0 : switch (value.GetUnit()) {
8606 : case eCSSUnit_Enumerated: {
8607 : // We shouldn't accept decoration line style and color via
8608 : // text-decoration.
8609 : color.SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR,
8610 0 : eCSSUnit_Enumerated);
8611 : style.SetIntValue(NS_STYLE_TEXT_DECORATION_STYLE_SOLID,
8612 0 : eCSSUnit_Enumerated);
8613 :
8614 0 : PRInt32 intValue = value.GetIntValue();
8615 0 : if (intValue == eDecorationNone) {
8616 0 : blink.SetIntValue(NS_STYLE_TEXT_BLINK_NONE, eCSSUnit_Enumerated);
8617 : line.SetIntValue(NS_STYLE_TEXT_DECORATION_LINE_NONE,
8618 0 : eCSSUnit_Enumerated);
8619 0 : break;
8620 : }
8621 :
8622 : // look for more keywords
8623 0 : nsCSSValue keyword;
8624 : PRInt32 index;
8625 0 : for (index = 0; index < 3; index++) {
8626 0 : if (!ParseEnum(keyword, kTextDecorationKTable)) {
8627 0 : break;
8628 : }
8629 0 : PRInt32 newValue = keyword.GetIntValue();
8630 0 : if (newValue == eDecorationNone || newValue & intValue) {
8631 : // 'none' keyword in conjuction with others is not allowed, and
8632 : // duplicate keyword is not allowed.
8633 0 : return false;
8634 : }
8635 0 : intValue |= newValue;
8636 : }
8637 :
8638 : blink.SetIntValue((intValue & eDecorationBlink) != 0 ?
8639 : NS_STYLE_TEXT_BLINK_BLINK : NS_STYLE_TEXT_BLINK_NONE,
8640 0 : eCSSUnit_Enumerated);
8641 0 : line.SetIntValue((intValue & ~eDecorationBlink), eCSSUnit_Enumerated);
8642 0 : break;
8643 : }
8644 : default:
8645 0 : blink = line = color = style = value;
8646 0 : break;
8647 : }
8648 :
8649 0 : AppendValue(eCSSProperty_text_blink, blink);
8650 0 : AppendValue(eCSSProperty_text_decoration_line, line);
8651 0 : AppendValue(eCSSProperty_text_decoration_color, color);
8652 0 : AppendValue(eCSSProperty_text_decoration_style, style);
8653 :
8654 0 : return true;
8655 : }
8656 :
8657 : bool
8658 0 : CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue)
8659 : {
8660 0 : if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTextDecorationLineKTable)) {
8661 0 : if (eCSSUnit_Enumerated == aValue.GetUnit()) {
8662 0 : PRInt32 intValue = aValue.GetIntValue();
8663 0 : if (intValue != NS_STYLE_TEXT_DECORATION_LINE_NONE) {
8664 : // look for more keywords
8665 0 : nsCSSValue keyword;
8666 : PRInt32 index;
8667 0 : for (index = 0; index < 2; index++) {
8668 0 : if (ParseEnum(keyword, nsCSSProps::kTextDecorationLineKTable)) {
8669 0 : PRInt32 newValue = keyword.GetIntValue();
8670 0 : if (newValue == NS_STYLE_TEXT_DECORATION_LINE_NONE ||
8671 : newValue & intValue) {
8672 : // 'none' keyword in conjuction with others is not allowed, and
8673 : // duplicate keyword is not allowed.
8674 0 : return false;
8675 : }
8676 0 : intValue |= newValue;
8677 : }
8678 : else {
8679 0 : break;
8680 : }
8681 : }
8682 0 : aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
8683 : }
8684 : }
8685 0 : return true;
8686 : }
8687 0 : return false;
8688 : }
8689 :
8690 : bool
8691 0 : CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue)
8692 : {
8693 0 : if (ParseVariant(aValue, VARIANT_INHERIT, nsnull)) {
8694 : // 'inherit' and 'initial' must be alone
8695 0 : return true;
8696 : }
8697 :
8698 0 : nsCSSValue left;
8699 0 : if (!ParseVariant(left, VARIANT_KEYWORD | VARIANT_STRING,
8700 0 : nsCSSProps::kTextOverflowKTable))
8701 0 : return false;
8702 :
8703 0 : nsCSSValue right;
8704 0 : if (ParseVariant(right, VARIANT_KEYWORD | VARIANT_STRING,
8705 : nsCSSProps::kTextOverflowKTable))
8706 0 : aValue.SetPairValue(left, right);
8707 : else {
8708 0 : aValue = left;
8709 : }
8710 0 : return true;
8711 : }
8712 :
8713 : bool
8714 0 : CSSParserImpl::ParseUnicodeBidi(nsCSSValue& aValue)
8715 : {
8716 0 : if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kUnicodeBidiKTable)) {
8717 0 : if (eCSSUnit_Enumerated == aValue.GetUnit()) {
8718 0 : PRInt32 intValue = aValue.GetIntValue();
8719 : // unicode-bidi can have either one or two values, but the only legal
8720 : // combination of two values is 'isolate bidi-override'
8721 0 : if (intValue == NS_STYLE_UNICODE_BIDI_ISOLATE ||
8722 : intValue == NS_STYLE_UNICODE_BIDI_OVERRIDE) {
8723 : // look for more keywords
8724 0 : nsCSSValue second;
8725 0 : if (ParseEnum(second, nsCSSProps::kUnicodeBidiKTable)) {
8726 0 : intValue |= second.GetIntValue();
8727 0 : if (intValue != (NS_STYLE_UNICODE_BIDI_ISOLATE |
8728 : NS_STYLE_UNICODE_BIDI_OVERRIDE)) {
8729 0 : return false;
8730 : }
8731 : }
8732 0 : aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
8733 : }
8734 : }
8735 0 : return true;
8736 : }
8737 0 : return false;
8738 : }
8739 :
8740 : bool
8741 0 : CSSParserImpl::ParseTransitionProperty()
8742 : {
8743 0 : nsCSSValue value;
8744 0 : if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE | VARIANT_ALL,
8745 : nsnull)) {
8746 : // 'inherit', 'initial', 'none', and 'all' must be alone
8747 0 : if (!ExpectEndProperty()) {
8748 0 : return false;
8749 : }
8750 : } else {
8751 : // Accept a list of arbitrary identifiers. They should be
8752 : // CSS properties, but we want to accept any so that we
8753 : // accept properties that we don't know about yet, e.g.
8754 : // transition-property: invalid-property, left, opacity;
8755 0 : nsCSSValueList* cur = value.SetListValue();
8756 0 : for (;;) {
8757 0 : if (!ParseVariant(cur->mValue, VARIANT_IDENTIFIER, nsnull)) {
8758 0 : return false;
8759 : }
8760 0 : nsDependentString str(cur->mValue.GetStringBufferValue());
8761 : // Exclude 'none' and 'all' and 'inherit' and 'initial'
8762 : // according to the same rules as for 'counter-reset' in CSS 2.1
8763 : // (except 'counter-reset' doesn't exclude 'all' since it
8764 : // doesn't support 'all' as a special value).
8765 0 : if (str.LowerCaseEqualsLiteral("none") ||
8766 0 : str.LowerCaseEqualsLiteral("all") ||
8767 0 : str.LowerCaseEqualsLiteral("inherit") ||
8768 0 : str.LowerCaseEqualsLiteral("initial")) {
8769 0 : return false;
8770 : }
8771 0 : if (CheckEndProperty()) {
8772 : break;
8773 : }
8774 0 : if (!ExpectSymbol(',', true)) {
8775 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
8776 0 : return false;
8777 : }
8778 0 : cur->mNext = new nsCSSValueList;
8779 0 : cur = cur->mNext;
8780 : }
8781 : }
8782 0 : AppendValue(eCSSProperty_transition_property, value);
8783 0 : return true;
8784 : }
8785 :
8786 : bool
8787 0 : CSSParserImpl::ParseTransitionTimingFunctionValues(nsCSSValue& aValue)
8788 : {
8789 0 : NS_ASSERTION(!mHavePushBack &&
8790 : mToken.mType == eCSSToken_Function &&
8791 : mToken.mIdent.LowerCaseEqualsLiteral("cubic-bezier"),
8792 : "unexpected initial state");
8793 :
8794 0 : nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(4);
8795 :
8796 : float x1, x2, y1, y2;
8797 0 : if (!ParseTransitionTimingFunctionValueComponent(x1, ',', true) ||
8798 0 : !ParseTransitionTimingFunctionValueComponent(y1, ',', false) ||
8799 0 : !ParseTransitionTimingFunctionValueComponent(x2, ',', true) ||
8800 0 : !ParseTransitionTimingFunctionValueComponent(y2, ')', false)) {
8801 0 : return false;
8802 : }
8803 :
8804 0 : val->Item(0).SetFloatValue(x1, eCSSUnit_Number);
8805 0 : val->Item(1).SetFloatValue(y1, eCSSUnit_Number);
8806 0 : val->Item(2).SetFloatValue(x2, eCSSUnit_Number);
8807 0 : val->Item(3).SetFloatValue(y2, eCSSUnit_Number);
8808 :
8809 0 : aValue.SetArrayValue(val, eCSSUnit_Cubic_Bezier);
8810 :
8811 0 : return true;
8812 : }
8813 :
8814 : bool
8815 0 : CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent,
8816 : char aStop,
8817 : bool aCheckRange)
8818 : {
8819 0 : if (!GetToken(true)) {
8820 0 : return false;
8821 : }
8822 0 : nsCSSToken* tk = &mToken;
8823 0 : if (tk->mType == eCSSToken_Number) {
8824 0 : float num = tk->mNumber;
8825 0 : if (aCheckRange && (num < 0.0 || num > 1.0)) {
8826 0 : return false;
8827 : }
8828 0 : aComponent = num;
8829 0 : if (ExpectSymbol(aStop, true)) {
8830 0 : return true;
8831 : }
8832 : }
8833 0 : return false;
8834 : }
8835 :
8836 : bool
8837 0 : CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue)
8838 : {
8839 0 : NS_ASSERTION(!mHavePushBack &&
8840 : mToken.mType == eCSSToken_Function &&
8841 : mToken.mIdent.LowerCaseEqualsLiteral("steps"),
8842 : "unexpected initial state");
8843 :
8844 0 : nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
8845 :
8846 0 : if (!ParseOneOrLargerVariant(val->Item(0), VARIANT_INTEGER, nsnull)) {
8847 0 : return false;
8848 : }
8849 :
8850 0 : PRInt32 type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
8851 0 : if (ExpectSymbol(',', true)) {
8852 0 : if (!GetToken(true)) {
8853 0 : return false;
8854 : }
8855 0 : type = -1;
8856 0 : if (mToken.mType == eCSSToken_Ident) {
8857 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("start")) {
8858 0 : type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START;
8859 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("end")) {
8860 0 : type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
8861 : }
8862 : }
8863 0 : if (type == -1) {
8864 0 : UngetToken();
8865 0 : return false;
8866 : }
8867 : }
8868 0 : val->Item(1).SetIntValue(type, eCSSUnit_Enumerated);
8869 :
8870 0 : if (!ExpectSymbol(')', true)) {
8871 0 : return false;
8872 : }
8873 :
8874 0 : aValue.SetArrayValue(val, eCSSUnit_Steps);
8875 0 : return true;
8876 : }
8877 :
8878 : static nsCSSValueList*
8879 0 : AppendValueToList(nsCSSValue& aContainer,
8880 : nsCSSValueList* aTail,
8881 : const nsCSSValue& aValue)
8882 : {
8883 : nsCSSValueList* entry;
8884 0 : if (aContainer.GetUnit() == eCSSUnit_Null) {
8885 0 : NS_ABORT_IF_FALSE(!aTail, "should not have an entry");
8886 0 : entry = aContainer.SetListValue();
8887 : } else {
8888 0 : NS_ABORT_IF_FALSE(!aTail->mNext, "should not have a next entry");
8889 0 : NS_ABORT_IF_FALSE(aContainer.GetUnit() == eCSSUnit_List, "not a list");
8890 0 : entry = new nsCSSValueList;
8891 0 : aTail->mNext = entry;
8892 : }
8893 0 : entry->mValue = aValue;
8894 0 : return entry;
8895 : }
8896 :
8897 : CSSParserImpl::ParseAnimationOrTransitionShorthandResult
8898 0 : CSSParserImpl::ParseAnimationOrTransitionShorthand(
8899 : const nsCSSProperty* aProperties,
8900 : const nsCSSValue* aInitialValues,
8901 : nsCSSValue* aValues,
8902 : size_t aNumProperties)
8903 : {
8904 0 : nsCSSValue tempValue;
8905 : // first see if 'inherit' or '-moz-initial' is specified. If one is,
8906 : // it can be the only thing specified, so don't attempt to parse any
8907 : // additional properties
8908 0 : if (ParseVariant(tempValue, VARIANT_INHERIT, nsnull)) {
8909 0 : for (PRUint32 i = 0; i < aNumProperties; ++i) {
8910 0 : AppendValue(aProperties[i], tempValue);
8911 : }
8912 0 : return eParseAnimationOrTransitionShorthand_Inherit;
8913 : }
8914 :
8915 : static const size_t maxNumProperties = 7;
8916 0 : NS_ABORT_IF_FALSE(aNumProperties <= maxNumProperties,
8917 : "can't handle this many properties");
8918 : nsCSSValueList *cur[maxNumProperties];
8919 : bool parsedProperty[maxNumProperties];
8920 :
8921 0 : for (size_t i = 0; i < aNumProperties; ++i) {
8922 0 : cur[i] = nsnull;
8923 : }
8924 0 : bool atEOP = false; // at end of property?
8925 0 : for (;;) { // loop over comma-separated transitions or animations
8926 : // whether a particular subproperty was specified for this
8927 : // transition or animation
8928 0 : for (size_t i = 0; i < aNumProperties; ++i) {
8929 0 : parsedProperty[i] = false;
8930 : }
8931 0 : for (;;) { // loop over values within a transition or animation
8932 0 : bool foundProperty = false;
8933 : // check to see if we're at the end of one full transition or
8934 : // animation definition (either because we hit a comma or because
8935 : // we hit the end of the property definition)
8936 0 : if (ExpectSymbol(',', true))
8937 0 : break;
8938 0 : if (CheckEndProperty()) {
8939 0 : atEOP = true;
8940 0 : break;
8941 : }
8942 :
8943 : // else, try to parse the next transition or animation sub-property
8944 0 : for (PRUint32 i = 0; !foundProperty && i < aNumProperties; ++i) {
8945 0 : if (!parsedProperty[i]) {
8946 : // if we haven't found this property yet, try to parse it
8947 0 : if (ParseSingleValueProperty(tempValue, aProperties[i])) {
8948 0 : parsedProperty[i] = true;
8949 0 : cur[i] = AppendValueToList(aValues[i], cur[i], tempValue);
8950 0 : foundProperty = true;
8951 0 : break; // out of inner loop; continue looking for next sub-property
8952 : }
8953 : }
8954 : }
8955 0 : if (!foundProperty) {
8956 : // We're not at a ',' or at the end of the property, but we couldn't
8957 : // parse any of the sub-properties, so the declaration is invalid.
8958 0 : return eParseAnimationOrTransitionShorthand_Error;
8959 : }
8960 : }
8961 :
8962 : // We hit the end of the property or the end of one transition
8963 : // or animation definition, add its components to the list.
8964 0 : for (PRUint32 i = 0; i < aNumProperties; ++i) {
8965 : // If all of the subproperties were not explicitly specified, fill
8966 : // in the missing ones with initial values.
8967 0 : if (!parsedProperty[i]) {
8968 0 : cur[i] = AppendValueToList(aValues[i], cur[i], aInitialValues[i]);
8969 : }
8970 : }
8971 :
8972 0 : if (atEOP)
8973 : break;
8974 : // else we just hit a ',' so continue parsing the next compound transition
8975 : }
8976 :
8977 0 : return eParseAnimationOrTransitionShorthand_Values;
8978 : }
8979 :
8980 : bool
8981 0 : CSSParserImpl::ParseTransition()
8982 : {
8983 : static const nsCSSProperty kTransitionProperties[] = {
8984 : eCSSProperty_transition_duration,
8985 : eCSSProperty_transition_timing_function,
8986 : // Must check 'transition-delay' after 'transition-duration', since
8987 : // that's our assumption about what the spec means for the shorthand
8988 : // syntax (the first time given is the duration, and the second
8989 : // given is the delay).
8990 : eCSSProperty_transition_delay,
8991 : // Must check 'transition-property' after
8992 : // 'transition-timing-function' since 'transition-property' accepts
8993 : // any keyword.
8994 : eCSSProperty_transition_property
8995 : };
8996 : static const PRUint32 numProps = NS_ARRAY_LENGTH(kTransitionProperties);
8997 : // this is a shorthand property that accepts -property, -delay,
8998 : // -duration, and -timing-function with some components missing.
8999 : // there can be multiple transitions, separated with commas
9000 :
9001 0 : nsCSSValue initialValues[numProps];
9002 0 : initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
9003 : initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
9004 0 : eCSSUnit_Enumerated);
9005 0 : initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
9006 0 : initialValues[3].SetAllValue();
9007 :
9008 0 : nsCSSValue values[numProps];
9009 :
9010 : ParseAnimationOrTransitionShorthandResult spres =
9011 : ParseAnimationOrTransitionShorthand(kTransitionProperties,
9012 0 : initialValues, values, numProps);
9013 0 : if (spres != eParseAnimationOrTransitionShorthand_Values) {
9014 0 : return spres != eParseAnimationOrTransitionShorthand_Error;
9015 : }
9016 :
9017 : // Make two checks on the list for 'transition-property':
9018 : // + If there is more than one item, then none of the items can be
9019 : // 'none' or 'all'.
9020 : // + None of the items can be 'inherit' or 'initial' (this is the case,
9021 : // like with counter-reset &c., where CSS 2.1 specifies 'initial', so
9022 : // we should check it without the -moz- prefix).
9023 : {
9024 0 : NS_ABORT_IF_FALSE(kTransitionProperties[3] ==
9025 : eCSSProperty_transition_property,
9026 : "array index mismatch");
9027 0 : nsCSSValueList *l = values[3].GetListValue();
9028 0 : bool multipleItems = !!l->mNext;
9029 0 : do {
9030 0 : const nsCSSValue& val = l->mValue;
9031 0 : if (val.GetUnit() != eCSSUnit_Ident) {
9032 0 : NS_ABORT_IF_FALSE(val.GetUnit() == eCSSUnit_None ||
9033 : val.GetUnit() == eCSSUnit_All, "unexpected unit");
9034 0 : if (multipleItems) {
9035 : // This is a syntax error.
9036 0 : return false;
9037 : }
9038 :
9039 : // Unbox a solitary 'none' or 'all'.
9040 0 : if (val.GetUnit() == eCSSUnit_None) {
9041 0 : values[3].SetNoneValue();
9042 : } else {
9043 0 : values[3].SetAllValue();
9044 : }
9045 0 : break;
9046 : }
9047 0 : nsDependentString str(val.GetStringBufferValue());
9048 0 : if (str.EqualsLiteral("inherit") || str.EqualsLiteral("initial")) {
9049 0 : return false;
9050 : }
9051 : } while ((l = l->mNext));
9052 : }
9053 :
9054 : // Save all parsed transition sub-properties in mTempData
9055 0 : for (PRUint32 i = 0; i < numProps; ++i) {
9056 0 : AppendValue(kTransitionProperties[i], values[i]);
9057 : }
9058 0 : return true;
9059 : }
9060 :
9061 : bool
9062 0 : CSSParserImpl::ParseAnimation()
9063 : {
9064 : static const nsCSSProperty kAnimationProperties[] = {
9065 : eCSSProperty_animation_duration,
9066 : eCSSProperty_animation_timing_function,
9067 : // Must check 'animation-delay' after 'animation-duration', since
9068 : // that's our assumption about what the spec means for the shorthand
9069 : // syntax (the first time given is the duration, and the second
9070 : // given is the delay).
9071 : eCSSProperty_animation_delay,
9072 : eCSSProperty_animation_direction,
9073 : eCSSProperty_animation_fill_mode,
9074 : eCSSProperty_animation_iteration_count,
9075 : // Must check 'animation-name' after 'animation-timing-function',
9076 : // 'animation-direction', 'animation-fill-mode',
9077 : // 'animation-iteration-count', and 'animation-play-state' since
9078 : // 'animation-name' accepts any keyword.
9079 : eCSSProperty_animation_name
9080 : };
9081 : static const PRUint32 numProps = NS_ARRAY_LENGTH(kAnimationProperties);
9082 : // this is a shorthand property that accepts -property, -delay,
9083 : // -duration, and -timing-function with some components missing.
9084 : // there can be multiple animations, separated with commas
9085 :
9086 0 : nsCSSValue initialValues[numProps];
9087 0 : initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
9088 : initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
9089 0 : eCSSUnit_Enumerated);
9090 0 : initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
9091 0 : initialValues[3].SetIntValue(NS_STYLE_ANIMATION_DIRECTION_NORMAL, eCSSUnit_Enumerated);
9092 0 : initialValues[4].SetIntValue(NS_STYLE_ANIMATION_FILL_MODE_NONE, eCSSUnit_Enumerated);
9093 0 : initialValues[5].SetFloatValue(1.0f, eCSSUnit_Number);
9094 0 : initialValues[6].SetNoneValue();
9095 :
9096 0 : nsCSSValue values[numProps];
9097 :
9098 : ParseAnimationOrTransitionShorthandResult spres =
9099 : ParseAnimationOrTransitionShorthand(kAnimationProperties,
9100 0 : initialValues, values, numProps);
9101 0 : if (spres != eParseAnimationOrTransitionShorthand_Values) {
9102 0 : return spres != eParseAnimationOrTransitionShorthand_Error;
9103 : }
9104 :
9105 : // Save all parsed animation sub-properties in mTempData
9106 0 : for (PRUint32 i = 0; i < numProps; ++i) {
9107 0 : AppendValue(kAnimationProperties[i], values[i]);
9108 : }
9109 0 : return true;
9110 : }
9111 :
9112 : bool
9113 0 : CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow)
9114 : {
9115 : // A shadow list item is an array, with entries in this sequence:
9116 : enum {
9117 : IndexX,
9118 : IndexY,
9119 : IndexRadius,
9120 : IndexSpread, // only for box-shadow
9121 : IndexColor,
9122 : IndexInset // only for box-shadow
9123 : };
9124 :
9125 0 : nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(6);
9126 :
9127 0 : if (aIsBoxShadow) {
9128 : // Optional inset keyword (ignore errors)
9129 0 : ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD,
9130 0 : nsCSSProps::kBoxShadowTypeKTable);
9131 : }
9132 :
9133 0 : nsCSSValue xOrColor;
9134 0 : bool haveColor = false;
9135 0 : if (!ParseVariant(xOrColor, VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC,
9136 0 : nsnull)) {
9137 0 : return false;
9138 : }
9139 0 : if (xOrColor.IsLengthUnit() || xOrColor.IsCalcUnit()) {
9140 0 : val->Item(IndexX) = xOrColor;
9141 : } else {
9142 : // Must be a color (as string or color value)
9143 0 : NS_ASSERTION(xOrColor.GetUnit() == eCSSUnit_Ident ||
9144 : xOrColor.GetUnit() == eCSSUnit_Color ||
9145 : xOrColor.GetUnit() == eCSSUnit_EnumColor,
9146 : "Must be a color value");
9147 0 : val->Item(IndexColor) = xOrColor;
9148 0 : haveColor = true;
9149 :
9150 : // X coordinate mandatory after color
9151 0 : if (!ParseVariant(val->Item(IndexX), VARIANT_LENGTH | VARIANT_CALC,
9152 0 : nsnull)) {
9153 0 : return false;
9154 : }
9155 : }
9156 :
9157 : // Y coordinate; mandatory
9158 0 : if (!ParseVariant(val->Item(IndexY), VARIANT_LENGTH | VARIANT_CALC,
9159 0 : nsnull)) {
9160 0 : return false;
9161 : }
9162 :
9163 : // Optional radius. Ignore errors except if they pass a negative
9164 : // value which we must reject. If we use ParseNonNegativeVariant
9165 : // we can't tell the difference between an unspecified radius
9166 : // and a negative radius.
9167 0 : if (ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH | VARIANT_CALC,
9168 0 : nsnull) &&
9169 0 : val->Item(IndexRadius).IsLengthUnit() &&
9170 0 : val->Item(IndexRadius).GetFloatValue() < 0) {
9171 0 : return false;
9172 : }
9173 :
9174 0 : if (aIsBoxShadow) {
9175 : // Optional spread
9176 0 : ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH | VARIANT_CALC, nsnull);
9177 : }
9178 :
9179 0 : if (!haveColor) {
9180 : // Optional color
9181 0 : ParseVariant(val->Item(IndexColor), VARIANT_COLOR, nsnull);
9182 : }
9183 :
9184 0 : if (aIsBoxShadow && val->Item(IndexInset).GetUnit() == eCSSUnit_Null) {
9185 : // Optional inset keyword
9186 0 : ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD,
9187 0 : nsCSSProps::kBoxShadowTypeKTable);
9188 : }
9189 :
9190 0 : aValue.SetArrayValue(val, eCSSUnit_Array);
9191 0 : return true;
9192 : }
9193 :
9194 : bool
9195 0 : CSSParserImpl::ParseShadowList(nsCSSProperty aProperty)
9196 : {
9197 0 : nsAutoParseCompoundProperty compound(this);
9198 0 : bool isBoxShadow = aProperty == eCSSProperty_box_shadow;
9199 :
9200 0 : nsCSSValue value;
9201 0 : if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nsnull)) {
9202 : // 'inherit', 'initial', and 'none' must be alone
9203 0 : if (!ExpectEndProperty()) {
9204 0 : return false;
9205 : }
9206 : } else {
9207 0 : nsCSSValueList* cur = value.SetListValue();
9208 0 : for (;;) {
9209 0 : if (!ParseShadowItem(cur->mValue, isBoxShadow)) {
9210 0 : return false;
9211 : }
9212 0 : if (CheckEndProperty()) {
9213 0 : break;
9214 : }
9215 0 : if (!ExpectSymbol(',', true)) {
9216 0 : return false;
9217 : }
9218 0 : cur->mNext = new nsCSSValueList;
9219 0 : cur = cur->mNext;
9220 : }
9221 : }
9222 0 : AppendValue(aProperty, value);
9223 0 : return true;
9224 : }
9225 :
9226 : PRInt32
9227 0 : CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix)
9228 : {
9229 0 : NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here");
9230 :
9231 0 : PRInt32 nameSpaceID = kNameSpaceID_Unknown;
9232 0 : if (mNameSpaceMap) {
9233 : // user-specified identifiers are case-sensitive (bug 416106)
9234 0 : nsCOMPtr<nsIAtom> prefix = do_GetAtom(aPrefix);
9235 0 : if (!prefix) {
9236 0 : NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
9237 : }
9238 0 : nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
9239 : }
9240 : // else no declared namespaces
9241 :
9242 0 : if (nameSpaceID == kNameSpaceID_Unknown) { // unknown prefix, dump it
9243 : const PRUnichar *params[] = {
9244 0 : aPrefix.get()
9245 0 : };
9246 0 : REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, params);
9247 0 : mFoundUnresolvablePrefix = true;
9248 : }
9249 :
9250 0 : return nameSpaceID;
9251 : }
9252 :
9253 : void
9254 220 : CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector)
9255 : {
9256 220 : if (mNameSpaceMap) {
9257 0 : aSelector.SetNameSpace(mNameSpaceMap->FindNameSpaceID(nsnull));
9258 : } else {
9259 220 : aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
9260 : }
9261 220 : }
9262 :
9263 : bool
9264 0 : CSSParserImpl::ParsePaint(nsCSSProperty aPropID)
9265 : {
9266 0 : nsCSSValue x, y;
9267 0 : if (!ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL, nsnull))
9268 0 : return false;
9269 0 : if (x.GetUnit() == eCSSUnit_URL) {
9270 0 : if (!ParseVariant(y, VARIANT_COLOR | VARIANT_NONE, nsnull))
9271 0 : y.SetColorValue(NS_RGB(0, 0, 0));
9272 : }
9273 0 : if (!ExpectEndProperty())
9274 0 : return false;
9275 :
9276 0 : if (x.GetUnit() != eCSSUnit_URL) {
9277 0 : AppendValue(aPropID, x);
9278 : } else {
9279 0 : nsCSSValue val;
9280 0 : val.SetPairValue(x, y);
9281 0 : AppendValue(aPropID, val);
9282 : }
9283 0 : return true;
9284 : }
9285 :
9286 : bool
9287 0 : CSSParserImpl::ParseDasharray()
9288 : {
9289 0 : nsCSSValue value;
9290 0 : if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nsnull)) {
9291 : // 'inherit', 'initial', and 'none' are only allowed on their own
9292 0 : if (!ExpectEndProperty()) {
9293 0 : return false;
9294 : }
9295 : } else {
9296 0 : nsCSSValueList *cur = value.SetListValue();
9297 0 : for (;;) {
9298 0 : if (!ParseNonNegativeVariant(cur->mValue, VARIANT_LPN, nsnull)) {
9299 0 : return false;
9300 : }
9301 0 : if (CheckEndProperty()) {
9302 0 : break;
9303 : }
9304 : // skip optional commas between elements
9305 0 : (void)ExpectSymbol(',', true);
9306 :
9307 0 : cur->mNext = new nsCSSValueList;
9308 0 : cur = cur->mNext;
9309 : }
9310 : }
9311 0 : AppendValue(eCSSProperty_stroke_dasharray, value);
9312 0 : return true;
9313 : }
9314 :
9315 : bool
9316 0 : CSSParserImpl::ParseMarker()
9317 : {
9318 0 : nsCSSValue marker;
9319 0 : if (ParseSingleValueProperty(marker, eCSSProperty_marker_end)) {
9320 0 : if (ExpectEndProperty()) {
9321 0 : AppendValue(eCSSProperty_marker_end, marker);
9322 0 : AppendValue(eCSSProperty_marker_mid, marker);
9323 0 : AppendValue(eCSSProperty_marker_start, marker);
9324 0 : return true;
9325 : }
9326 : }
9327 0 : return false;
9328 : }
9329 :
9330 : } // anonymous namespace
9331 :
9332 : // Recycling of parser implementation objects
9333 :
9334 : static CSSParserImpl* gFreeList = nsnull;
9335 :
9336 110 : nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader,
9337 : nsCSSStyleSheet* aSheet)
9338 : {
9339 110 : CSSParserImpl *impl = gFreeList;
9340 110 : if (impl) {
9341 98 : gFreeList = impl->mNextFree;
9342 98 : impl->mNextFree = nsnull;
9343 : } else {
9344 12 : impl = new CSSParserImpl();
9345 : }
9346 :
9347 110 : if (aLoader) {
9348 110 : impl->SetChildLoader(aLoader);
9349 110 : impl->SetQuirkMode(aLoader->GetCompatibilityMode() ==
9350 110 : eCompatibility_NavQuirks);
9351 : }
9352 110 : if (aSheet) {
9353 0 : impl->SetStyleSheet(aSheet);
9354 : }
9355 :
9356 110 : mImpl = static_cast<void*>(impl);
9357 110 : }
9358 :
9359 110 : nsCSSParser::~nsCSSParser()
9360 : {
9361 110 : CSSParserImpl *impl = static_cast<CSSParserImpl*>(mImpl);
9362 110 : impl->Reset();
9363 110 : impl->mNextFree = gFreeList;
9364 110 : gFreeList = impl;
9365 110 : }
9366 :
9367 : /* static */ void
9368 1364 : nsCSSParser::Shutdown()
9369 : {
9370 1364 : CSSParserImpl *tofree = gFreeList;
9371 : CSSParserImpl *next;
9372 2740 : while (tofree)
9373 : {
9374 12 : next = tofree->mNextFree;
9375 12 : delete tofree;
9376 12 : tofree = next;
9377 : }
9378 1364 : }
9379 :
9380 : // Wrapper methods
9381 :
9382 : nsresult
9383 0 : nsCSSParser::SetStyleSheet(nsCSSStyleSheet* aSheet)
9384 : {
9385 : return static_cast<CSSParserImpl*>(mImpl)->
9386 0 : SetStyleSheet(aSheet);
9387 : }
9388 :
9389 : nsresult
9390 0 : nsCSSParser::SetQuirkMode(bool aQuirkMode)
9391 : {
9392 : return static_cast<CSSParserImpl*>(mImpl)->
9393 0 : SetQuirkMode(aQuirkMode);
9394 : }
9395 :
9396 : nsresult
9397 0 : nsCSSParser::SetSVGMode(bool aSVGMode)
9398 : {
9399 : return static_cast<CSSParserImpl*>(mImpl)->
9400 0 : SetSVGMode(aSVGMode);
9401 : }
9402 :
9403 : nsresult
9404 0 : nsCSSParser::SetChildLoader(mozilla::css::Loader* aChildLoader)
9405 : {
9406 : return static_cast<CSSParserImpl*>(mImpl)->
9407 0 : SetChildLoader(aChildLoader);
9408 : }
9409 :
9410 : nsresult
9411 0 : nsCSSParser::ParseSheet(const nsAString& aInput,
9412 : nsIURI* aSheetURI,
9413 : nsIURI* aBaseURI,
9414 : nsIPrincipal* aSheetPrincipal,
9415 : PRUint32 aLineNumber,
9416 : bool aAllowUnsafeRules)
9417 : {
9418 : return static_cast<CSSParserImpl*>(mImpl)->
9419 : ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber,
9420 0 : aAllowUnsafeRules);
9421 : }
9422 :
9423 : nsresult
9424 0 : nsCSSParser::ParseStyleAttribute(const nsAString& aAttributeValue,
9425 : nsIURI* aDocURI,
9426 : nsIURI* aBaseURI,
9427 : nsIPrincipal* aNodePrincipal,
9428 : css::StyleRule** aResult)
9429 : {
9430 : return static_cast<CSSParserImpl*>(mImpl)->
9431 : ParseStyleAttribute(aAttributeValue, aDocURI, aBaseURI,
9432 0 : aNodePrincipal, aResult);
9433 : }
9434 :
9435 : nsresult
9436 0 : nsCSSParser::ParseDeclarations(const nsAString& aBuffer,
9437 : nsIURI* aSheetURI,
9438 : nsIURI* aBaseURI,
9439 : nsIPrincipal* aSheetPrincipal,
9440 : css::Declaration* aDeclaration,
9441 : bool* aChanged)
9442 : {
9443 : return static_cast<CSSParserImpl*>(mImpl)->
9444 : ParseDeclarations(aBuffer, aSheetURI, aBaseURI, aSheetPrincipal,
9445 0 : aDeclaration, aChanged);
9446 : }
9447 :
9448 : nsresult
9449 0 : nsCSSParser::ParseRule(const nsAString& aRule,
9450 : nsIURI* aSheetURI,
9451 : nsIURI* aBaseURI,
9452 : nsIPrincipal* aSheetPrincipal,
9453 : nsCOMArray<css::Rule>& aResult)
9454 : {
9455 : return static_cast<CSSParserImpl*>(mImpl)->
9456 0 : ParseRule(aRule, aSheetURI, aBaseURI, aSheetPrincipal, aResult);
9457 : }
9458 :
9459 : nsresult
9460 0 : nsCSSParser::ParseProperty(const nsCSSProperty aPropID,
9461 : const nsAString& aPropValue,
9462 : nsIURI* aSheetURI,
9463 : nsIURI* aBaseURI,
9464 : nsIPrincipal* aSheetPrincipal,
9465 : css::Declaration* aDeclaration,
9466 : bool* aChanged,
9467 : bool aIsImportant)
9468 : {
9469 : return static_cast<CSSParserImpl*>(mImpl)->
9470 : ParseProperty(aPropID, aPropValue, aSheetURI, aBaseURI,
9471 0 : aSheetPrincipal, aDeclaration, aChanged, aIsImportant);
9472 : }
9473 :
9474 : nsresult
9475 0 : nsCSSParser::ParseMediaList(const nsSubstring& aBuffer,
9476 : nsIURI* aURI,
9477 : PRUint32 aLineNumber,
9478 : nsMediaList* aMediaList,
9479 : bool aHTMLMode)
9480 : {
9481 : return static_cast<CSSParserImpl*>(mImpl)->
9482 0 : ParseMediaList(aBuffer, aURI, aLineNumber, aMediaList, aHTMLMode);
9483 : }
9484 :
9485 : nsresult
9486 0 : nsCSSParser::ParseColorString(const nsSubstring& aBuffer,
9487 : nsIURI* aURI,
9488 : PRUint32 aLineNumber,
9489 : nscolor* aColor)
9490 : {
9491 : return static_cast<CSSParserImpl*>(mImpl)->
9492 0 : ParseColorString(aBuffer, aURI, aLineNumber, aColor);
9493 : }
9494 :
9495 : nsresult
9496 110 : nsCSSParser::ParseSelectorString(const nsSubstring& aSelectorString,
9497 : nsIURI* aURI,
9498 : PRUint32 aLineNumber,
9499 : nsCSSSelectorList** aSelectorList)
9500 : {
9501 : return static_cast<CSSParserImpl*>(mImpl)->
9502 110 : ParseSelectorString(aSelectorString, aURI, aLineNumber, aSelectorList);
9503 : }
9504 :
9505 : already_AddRefed<nsCSSKeyframeRule>
9506 0 : nsCSSParser::ParseKeyframeRule(const nsSubstring& aBuffer,
9507 : nsIURI* aURI,
9508 : PRUint32 aLineNumber)
9509 : {
9510 : return static_cast<CSSParserImpl*>(mImpl)->
9511 0 : ParseKeyframeRule(aBuffer, aURI, aLineNumber);
9512 : }
9513 :
9514 : bool
9515 0 : nsCSSParser::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
9516 : nsIURI* aURI,
9517 : PRUint32 aLineNumber,
9518 : InfallibleTArray<float>& aSelectorList)
9519 : {
9520 : return static_cast<CSSParserImpl*>(mImpl)->
9521 : ParseKeyframeSelectorString(aSelectorString, aURI, aLineNumber,
9522 0 : aSelectorList);
9523 : }
|