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