From a96dbca975a09b2805c8ef3cbbbfbe2300c279fe Mon Sep 17 00:00:00 2001 From: "glazman%netscape.com" Date: Thu, 8 Mar 2001 23:27:39 +0000 Subject: [PATCH] Cleanup of ParseSelector() ; b=71100, r=peterl, sr=attinasi git-svn-id: svn://10.0.0.236/trunk@89008 18797224-902f-48f8-a5cc-f745e15eee43 --- .../content/html/style/src/nsCSSParser.cpp | 670 ++++++++++-------- mozilla/layout/style/nsCSSParser.cpp | 670 ++++++++++-------- 2 files changed, 786 insertions(+), 554 deletions(-) diff --git a/mozilla/content/html/style/src/nsCSSParser.cpp b/mozilla/content/html/style/src/nsCSSParser.cpp index de7481341d2..88699560a1f 100644 --- a/mozilla/content/html/style/src/nsCSSParser.cpp +++ b/mozilla/content/html/style/src/nsCSSParser.cpp @@ -192,6 +192,19 @@ protected: PRBool ParseFontFaceRule(PRInt32& aErrorCode); PRBool ParsePageRule(PRInt32& aErrorCode); + void ParseIDSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, + PRInt32& aParsingStatus, PRInt32& aErrorCode); + void ParseClassSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, + PRInt32& aParsingStatus, PRInt32& aErrorCode); + void ParsePseudoSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, + PRInt32& aParsingStatus, PRInt32& aErrorCode); + void ParseAttributeSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, + PRInt32& aParsingStatus, PRInt32& aErrorCode); + + void ParseTypeOrUniversalSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, PRInt32& aErrorCode); + PRBool ParseSelectorList(PRInt32& aErrorCode, SelectorList*& aListHead); PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList*& aListHead); PRBool ParseSelector(PRInt32& aErrorCode, nsCSSSelector& aSelectorResult); @@ -1502,31 +1515,69 @@ PRBool CSSParserImpl::ParseSelectorGroup(PRInt32& aErrorCode, #define SEL_MASK_PCLASS 0x20 #define SEL_MASK_PELEM 0x40 -/** - * This is the format for selectors: - * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]* - */ -PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, - nsCSSSelector& aSelector) -{ - PRInt32 dataMask = 0; - nsAutoString buffer; +#define SELECTOR_PARSING_ENDED_OK 1 +#define SELECTOR_PARSING_STOPPED_OK 2 +#define SELECTOR_PARSING_STOPPED_ERROR 3 - if (! GetToken(aErrorCode, PR_TRUE)) { - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; +void CSSParserImpl::ParseIDSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + if (0 < mToken.mIdent.Length()) { // verify is legal ID + aDataMask |= SEL_MASK_ID; + aSelector.AddID(mToken.mIdent); } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING( + "Expected non-empty name for ID selector, but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + aParsingStatus = SELECTOR_PARSING_ENDED_OK; +} + +void CSSParserImpl::ParseClassSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + if (! GetToken(aErrorCode, PR_FALSE)) { // get ident + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for class selector but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + aDataMask |= SEL_MASK_CLASS; + aSelector.AddClass(mToken.mIdent); // class always case sensitive + aParsingStatus = SELECTOR_PARSING_ENDED_OK; +} + + +void CSSParserImpl::ParseTypeOrUniversalSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + nsAutoString buffer; if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was namespace - dataMask |= SEL_MASK_NSPACE; + aDataMask |= SEL_MASK_NSPACE; aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard if (! GetToken(aErrorCode, PR_FALSE)) { REPORT_UNEXPECTED_EOF(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } if (eCSSToken_Ident == mToken.mType) { // element name - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; if (mCaseSensitive) { aSelector.SetTag(mToken.mIdent); } @@ -1536,14 +1587,15 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, } } else if (mToken.IsSymbol('*')) { // universal selector - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; // don't set tag } else { REPORT_UNEXPECTED_TOKEN( NS_LITERAL_STRING("Expected element name or '*' but found")); UngetToken(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } } else { // was universal element selector @@ -1558,18 +1610,19 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, NS_RELEASE(defaultNameSpace); } } - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; // don't set any tag in the selector } if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) - return PR_TRUE; + aParsingStatus = SELECTOR_PARSING_STOPPED_OK; + return; } } else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name buffer = mToken.mIdent; // hang on to ident if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was namespace - dataMask |= SEL_MASK_NSPACE; + aDataMask |= SEL_MASK_NSPACE; PRInt32 nameSpaceID = kNameSpaceID_Unknown; if (mNameSpace) { nsIAtom* prefix; @@ -1579,18 +1632,19 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, NS_IF_RELEASE(prefix); } // else, no delcared namespaces if (kNameSpaceID_Unknown == nameSpaceID) { // unknown prefix, dump it - REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Unknown namespace prefix")); - return PR_FALSE; + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Unknown namespace prefix")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } aSelector.SetNameSpace(nameSpaceID); if (! GetToken(aErrorCode, PR_FALSE)) { REPORT_UNEXPECTED_EOF(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } if (eCSSToken_Ident == mToken.mType) { // element name - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; if (mCaseSensitive) { aSelector.SetTag(mToken.mIdent); } @@ -1600,14 +1654,15 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, } } else if (mToken.IsSymbol('*')) { // universal selector - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; // don't set tag } else { REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Expected element name or '*' but found")); + NS_LITERAL_STRING("Expected element name or '*' but found")); UngetToken(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } } else { // was element name @@ -1629,23 +1684,25 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, buffer.ToLowerCase(); aSelector.SetTag(buffer); } - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; } if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) - return PR_TRUE; + aParsingStatus = SELECTOR_PARSING_STOPPED_OK; + return; } } else if (mToken.IsSymbol('|')) { // No namespace - dataMask |= SEL_MASK_NSPACE; + aDataMask |= SEL_MASK_NSPACE; aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace // get mandatory tag if (! GetToken(aErrorCode, PR_FALSE)) { REPORT_UNEXPECTED_EOF(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } if (eCSSToken_Ident == mToken.mType) { // element name - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; if (mCaseSensitive) { aSelector.SetTag(mToken.mIdent); } @@ -1655,274 +1712,333 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, } } else if (mToken.IsSymbol('*')) { // universal selector - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; // don't set tag } else { REPORT_UNEXPECTED_TOKEN( NS_LITERAL_STRING("Expected element name or '*' but found")); UngetToken(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) - return PR_TRUE; + aParsingStatus = SELECTOR_PARSING_STOPPED_OK; + return; } } - for (;;) { - if (eCSSToken_ID == mToken.mType) { // #id - if (0 < mToken.mIdent.Length()) { // verify is legal ID - dataMask |= SEL_MASK_ID; - aSelector.AddID(mToken.mIdent); + aParsingStatus = SELECTOR_PARSING_ENDED_OK; +} + +void CSSParserImpl::ParseAttributeSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + + PRInt32 nameSpaceID = kNameSpaceID_None; + nsAutoString attr; + if (mToken.IsSymbol('*')) { // wildcard namespace + nameSpaceID = kNameSpaceID_Unknown; + if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (eCSSToken_Ident == mToken.mType) { // attr name + attr = mToken.mIdent; } else { - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING( - "Expected non-empty name for ID selector, but found")); + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for attribute name but found")); UngetToken(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + else { + REPORT_UNEXPECTED_TOKEN( + NS_LITERAL_STRING("Expected '|' but found")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + else if (mToken.IsSymbol('|')) { // NO namespace + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (eCSSToken_Ident == mToken.mType) { // attr name + attr = mToken.mIdent; + } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for attribute name but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace + attr = mToken.mIdent; // hang on to it + if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was a namespace + nameSpaceID = kNameSpaceID_Unknown; + if (mNameSpace) { + nsIAtom* prefix; + attr.ToLowerCase(); // always case insensitive, since stays within CSS + prefix = NS_NewAtom(attr); + mNameSpace->FindNameSpaceID(prefix, nameSpaceID); + NS_IF_RELEASE(prefix); + } // else, no delcared namespaces + if (kNameSpaceID_Unknown == nameSpaceID) { // unknown prefix, dump it + REPORT_UNEXPECTED_TOKEN( + NS_LITERAL_STRING("Unknown namespace prefix")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (eCSSToken_Ident == mToken.mType) { // attr name + attr = mToken.mIdent; + } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for attribute name but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + } + else { // malformed + REPORT_UNEXPECTED_TOKEN( + NS_LITERAL_STRING("Expected attribute name or namespace but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + + if (! mCaseSensitive) { + attr.ToLowerCase(); + } + if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if ((eCSSToken_Symbol == mToken.mType) || + (eCSSToken_Includes == mToken.mType) || + (eCSSToken_Dashmatch == mToken.mType) || + (eCSSToken_Beginsmatch == mToken.mType) || + (eCSSToken_Endsmatch == mToken.mType) || + (eCSSToken_Containsmatch == mToken.mType)) { + PRUint8 func; + if (eCSSToken_Includes == mToken.mType) { + func = NS_ATTR_FUNC_INCLUDES; + } + else if (eCSSToken_Dashmatch == mToken.mType) { + func = NS_ATTR_FUNC_DASHMATCH; + } + else if (eCSSToken_Beginsmatch == mToken.mType) { + func = NS_ATTR_FUNC_BEGINSMATCH; + } + else if (eCSSToken_Endsmatch == mToken.mType) { + func = NS_ATTR_FUNC_ENDSMATCH; + } + else if (eCSSToken_Containsmatch == mToken.mType) { + func = NS_ATTR_FUNC_CONTAINSMATCH; + } + else if (']' == mToken.mSymbol) { + aDataMask |= SEL_MASK_ATTRIB; + aSelector.AddAttribute(nameSpaceID, attr); + func = NS_ATTR_FUNC_SET; + } + else if ('=' == mToken.mSymbol) { + func = NS_ATTR_FUNC_EQUALS; + } + else { + REPORT_UNEXPECTED_TOKEN( + NS_LITERAL_STRING("Unexpected token in attribute selector: ")); + UngetToken(); // bad function + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (NS_ATTR_FUNC_SET != func) { // get value + if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) { + nsAutoString value(mToken.mIdent); + if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (mToken.IsSymbol(']')) { + aDataMask |= SEL_MASK_ATTRIB; + aSelector.AddAttribute(nameSpaceID, attr, func, value, mCaseSensitive); + } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected ']' to terminate attribute selector but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier or string for value in attribute selector but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + } + else { + REPORT_UNEXPECTED_TOKEN( + NS_LITERAL_STRING("Unexpected token in attribute selector: ")); + UngetToken(); // bad dog, no biscut! + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + aParsingStatus = SELECTOR_PARSING_ENDED_OK; +} + +void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + nsAutoString buffer; + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector +#ifdef INCLUDE_XUL + if (eCSSToken_Function != mToken.mType || + !IsOutlinerPseudoElement(mToken.mIdent)) { +#endif + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for pseudo-class selector but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; +#ifdef INCLUDE_XUL + } +#endif + } + buffer.Truncate(); + buffer.AppendWithConversion(':'); + buffer.Append(mToken.mIdent); + buffer.ToLowerCase(); + nsIAtom* pseudo = NS_NewAtom(buffer); + if (IsPseudoClass(pseudo)) { + // XXX parse lang pseudo class + aDataMask |= SEL_MASK_PCLASS; + aSelector.AddPseudoClass(pseudo); + NS_RELEASE(pseudo); + } + else { + if (0 == (aDataMask & SEL_MASK_PELEM)) { + aDataMask |= SEL_MASK_PELEM; + aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later + NS_RELEASE(pseudo); + +#ifdef INCLUDE_XUL + if (eCSSToken_Function == mToken.mType && + IsOutlinerPseudoElement(mToken.mIdent)) { + // We have encountered a pseudoelement of the form + // -moz-outliner-xxxx(a,b,c). We parse (a,b,c) and add each + // item in the list to the pseudoclass list. They will be pulled + // from the list later along with the pseudoelement. + if (!ParseOutlinerPseudoElement(aErrorCode, aSelector)) + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } +#endif + + // ensure selector ends here, must be followed by EOF, space, '{' or ',' + if (GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) + if ((eCSSToken_WhiteSpace == mToken.mType) || + (mToken.IsSymbol('{') || mToken.IsSymbol(','))) { + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_OK; + return; + } + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Found trailing token after pseudo-element, which must be the last part of a selector: ")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + else { // multiple pseudo elements, not legal + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Extra pseudo-element")); + UngetToken(); + NS_RELEASE(pseudo); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + aParsingStatus = SELECTOR_PARSING_ENDED_OK; +} + +/** + * This is the format for selectors: + * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]* + */ +PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, + nsCSSSelector& aSelector) +{ + PRInt32 dataMask = 0; + PRInt32 parsingStatus = SELECTOR_PARSING_ENDED_OK; + + if (! GetToken(aErrorCode, PR_TRUE)) { + REPORT_UNEXPECTED_EOF(); + return PR_FALSE; + } + + ParseTypeOrUniversalSelector(dataMask, aSelector, parsingStatus, aErrorCode); + if (SELECTOR_PARSING_STOPPED_OK == parsingStatus) { + return PR_TRUE; + } + else if (SELECTOR_PARSING_STOPPED_ERROR == parsingStatus) { + return PR_FALSE; + } + + for (;;) { + parsingStatus = SELECTOR_PARSING_ENDED_OK; + if (eCSSToken_ID == mToken.mType) { // #id + ParseIDSelector(dataMask, aSelector, parsingStatus, aErrorCode); } else if (mToken.IsSymbol('.')) { // .class - if (! GetToken(aErrorCode, PR_FALSE)) { // get ident - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if (eCSSToken_Ident != mToken.mType) { // malformed selector - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for class selector but found")); - UngetToken(); - return PR_FALSE; - } - dataMask |= SEL_MASK_CLASS; - aSelector.AddClass(mToken.mIdent); // class always case sensitive + ParseClassSelector(dataMask, aSelector, parsingStatus, aErrorCode); } else if (mToken.IsSymbol(':')) { // :pseudo - if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if (eCSSToken_Ident != mToken.mType) { // malformed selector -#ifdef INCLUDE_XUL - if (eCSSToken_Function != mToken.mType || - !IsOutlinerPseudoElement(mToken.mIdent)) { -#endif - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for pseudo-class selector but found")); - UngetToken(); - return PR_FALSE; -#ifdef INCLUDE_XUL - } -#endif - } - buffer.Truncate(); - buffer.AppendWithConversion(':'); - buffer.Append(mToken.mIdent); - buffer.ToLowerCase(); - nsIAtom* pseudo = NS_NewAtom(buffer); - if (IsPseudoClass(pseudo)) { - // XXX parse lang pseudo class - dataMask |= SEL_MASK_PCLASS; - aSelector.AddPseudoClass(pseudo); - NS_RELEASE(pseudo); - } - else { - if (0 == (dataMask & SEL_MASK_PELEM)) { - dataMask |= SEL_MASK_PELEM; - aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later - NS_RELEASE(pseudo); - -#ifdef INCLUDE_XUL - if (eCSSToken_Function == mToken.mType && - IsOutlinerPseudoElement(mToken.mIdent)) { - // We have encountered a pseudoelement of the form - // -moz-outliner-xxxx(a,b,c). We parse (a,b,c) and add each - // item in the list to the pseudoclass list. They will be pulled - // from the list later along with the pseudoelement. - if (!ParseOutlinerPseudoElement(aErrorCode, aSelector)) - return PR_FALSE; - } -#endif - - // ensure selector ends here, must be followed by EOF, space, '{' or ',' - if (GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) - if ((eCSSToken_WhiteSpace == mToken.mType) || - (mToken.IsSymbol('{') || mToken.IsSymbol(','))) { - UngetToken(); - return PR_TRUE; - } - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Found trailing token after pseudo-element, which must be the last part of a selector: ")); - UngetToken(); - return PR_FALSE; - } - } - else { // multiple pseudo elements, not legal - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Extra pseudo-element")); - UngetToken(); - NS_RELEASE(pseudo); - return PR_FALSE; - } - } + ParsePseudoSelector(dataMask, aSelector, parsingStatus, aErrorCode); } else if (mToken.IsSymbol('[')) { // attribute - if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - - PRInt32 nameSpaceID = kNameSpaceID_None; - nsAutoString attr; - if (mToken.IsSymbol('*')) { // wildcard namespace - nameSpaceID = kNameSpaceID_Unknown; - if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { - if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if (eCSSToken_Ident == mToken.mType) { // attr name - attr = mToken.mIdent; - } - else { - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for attribute name but found")); - UngetToken(); - return PR_FALSE; - } - } - else { - REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Expected '|' but found")); - return PR_FALSE; - } - } - else if (mToken.IsSymbol('|')) { // NO namespace - if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if (eCSSToken_Ident == mToken.mType) { // attr name - attr = mToken.mIdent; - } - else { - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for attribute name but found")); - UngetToken(); - return PR_FALSE; - } - } - else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace - attr = mToken.mIdent; // hang on to it - if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was a namespace - nameSpaceID = kNameSpaceID_Unknown; - if (mNameSpace) { - nsIAtom* prefix; - attr.ToLowerCase(); // always case insensitive, since stays within CSS - prefix = NS_NewAtom(attr); - mNameSpace->FindNameSpaceID(prefix, nameSpaceID); - NS_IF_RELEASE(prefix); - } // else, no delcared namespaces - if (kNameSpaceID_Unknown == nameSpaceID) { // unknown prefix, dump it - REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Unknown namespace prefix")); - return PR_FALSE; - } - if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if (eCSSToken_Ident == mToken.mType) { // attr name - attr = mToken.mIdent; - } - else { - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for attribute name but found")); - UngetToken(); - return PR_FALSE; - } - } - } - else { // malformed - REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Expected attribute name or namespace but found")); - UngetToken(); - return PR_FALSE; - } - - if (! mCaseSensitive) { - attr.ToLowerCase(); - } - if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if ((eCSSToken_Symbol == mToken.mType) || - (eCSSToken_Includes == mToken.mType) || - (eCSSToken_Dashmatch == mToken.mType) || - (eCSSToken_Beginsmatch == mToken.mType) || - (eCSSToken_Endsmatch == mToken.mType) || - (eCSSToken_Containsmatch == mToken.mType)) { - PRUint8 func; - if (eCSSToken_Includes == mToken.mType) { - func = NS_ATTR_FUNC_INCLUDES; - } - else if (eCSSToken_Dashmatch == mToken.mType) { - func = NS_ATTR_FUNC_DASHMATCH; - } - else if (eCSSToken_Beginsmatch == mToken.mType) { - func = NS_ATTR_FUNC_BEGINSMATCH; - } - else if (eCSSToken_Endsmatch == mToken.mType) { - func = NS_ATTR_FUNC_ENDSMATCH; - } - else if (eCSSToken_Containsmatch == mToken.mType) { - func = NS_ATTR_FUNC_CONTAINSMATCH; - } - else if (']' == mToken.mSymbol) { - dataMask |= SEL_MASK_ATTRIB; - aSelector.AddAttribute(nameSpaceID, attr); - func = NS_ATTR_FUNC_SET; - } - else if ('=' == mToken.mSymbol) { - func = NS_ATTR_FUNC_EQUALS; - } - else { - REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Unexpected token in attribute selector: ")); - UngetToken(); // bad function - return PR_FALSE; - } - if (NS_ATTR_FUNC_SET != func) { // get value - if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) { - nsAutoString value(mToken.mIdent); - if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if (mToken.IsSymbol(']')) { - dataMask |= SEL_MASK_ATTRIB; - aSelector.AddAttribute(nameSpaceID, attr, func, value, mCaseSensitive); - } - else { - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected ']' to terminate attribute selector but found")); - UngetToken(); - return PR_FALSE; - } - } - else { - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier or string for value in attribute selector but found")); - UngetToken(); - return PR_FALSE; - } - } - } - else { - REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Unexpected token in attribute selector: ")); - UngetToken(); // bad dog, no biscut! - return PR_FALSE; - } + ParseAttributeSelector(dataMask, aSelector, parsingStatus, aErrorCode); } else { // not a selector token, we're done break; } - + + if (SELECTOR_PARSING_STOPPED_OK == parsingStatus) { + return PR_TRUE; + } + else if (SELECTOR_PARSING_STOPPED_ERROR == parsingStatus) { + return PR_FALSE; + } + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) return PR_TRUE; } diff --git a/mozilla/layout/style/nsCSSParser.cpp b/mozilla/layout/style/nsCSSParser.cpp index de7481341d2..88699560a1f 100644 --- a/mozilla/layout/style/nsCSSParser.cpp +++ b/mozilla/layout/style/nsCSSParser.cpp @@ -192,6 +192,19 @@ protected: PRBool ParseFontFaceRule(PRInt32& aErrorCode); PRBool ParsePageRule(PRInt32& aErrorCode); + void ParseIDSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, + PRInt32& aParsingStatus, PRInt32& aErrorCode); + void ParseClassSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, + PRInt32& aParsingStatus, PRInt32& aErrorCode); + void ParsePseudoSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, + PRInt32& aParsingStatus, PRInt32& aErrorCode); + void ParseAttributeSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, + PRInt32& aParsingStatus, PRInt32& aErrorCode); + + void ParseTypeOrUniversalSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, PRInt32& aErrorCode); + PRBool ParseSelectorList(PRInt32& aErrorCode, SelectorList*& aListHead); PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList*& aListHead); PRBool ParseSelector(PRInt32& aErrorCode, nsCSSSelector& aSelectorResult); @@ -1502,31 +1515,69 @@ PRBool CSSParserImpl::ParseSelectorGroup(PRInt32& aErrorCode, #define SEL_MASK_PCLASS 0x20 #define SEL_MASK_PELEM 0x40 -/** - * This is the format for selectors: - * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]* - */ -PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, - nsCSSSelector& aSelector) -{ - PRInt32 dataMask = 0; - nsAutoString buffer; +#define SELECTOR_PARSING_ENDED_OK 1 +#define SELECTOR_PARSING_STOPPED_OK 2 +#define SELECTOR_PARSING_STOPPED_ERROR 3 - if (! GetToken(aErrorCode, PR_TRUE)) { - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; +void CSSParserImpl::ParseIDSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + if (0 < mToken.mIdent.Length()) { // verify is legal ID + aDataMask |= SEL_MASK_ID; + aSelector.AddID(mToken.mIdent); } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING( + "Expected non-empty name for ID selector, but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + aParsingStatus = SELECTOR_PARSING_ENDED_OK; +} + +void CSSParserImpl::ParseClassSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + if (! GetToken(aErrorCode, PR_FALSE)) { // get ident + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for class selector but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + aDataMask |= SEL_MASK_CLASS; + aSelector.AddClass(mToken.mIdent); // class always case sensitive + aParsingStatus = SELECTOR_PARSING_ENDED_OK; +} + + +void CSSParserImpl::ParseTypeOrUniversalSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + nsAutoString buffer; if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was namespace - dataMask |= SEL_MASK_NSPACE; + aDataMask |= SEL_MASK_NSPACE; aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard if (! GetToken(aErrorCode, PR_FALSE)) { REPORT_UNEXPECTED_EOF(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } if (eCSSToken_Ident == mToken.mType) { // element name - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; if (mCaseSensitive) { aSelector.SetTag(mToken.mIdent); } @@ -1536,14 +1587,15 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, } } else if (mToken.IsSymbol('*')) { // universal selector - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; // don't set tag } else { REPORT_UNEXPECTED_TOKEN( NS_LITERAL_STRING("Expected element name or '*' but found")); UngetToken(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } } else { // was universal element selector @@ -1558,18 +1610,19 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, NS_RELEASE(defaultNameSpace); } } - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; // don't set any tag in the selector } if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) - return PR_TRUE; + aParsingStatus = SELECTOR_PARSING_STOPPED_OK; + return; } } else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name buffer = mToken.mIdent; // hang on to ident if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was namespace - dataMask |= SEL_MASK_NSPACE; + aDataMask |= SEL_MASK_NSPACE; PRInt32 nameSpaceID = kNameSpaceID_Unknown; if (mNameSpace) { nsIAtom* prefix; @@ -1579,18 +1632,19 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, NS_IF_RELEASE(prefix); } // else, no delcared namespaces if (kNameSpaceID_Unknown == nameSpaceID) { // unknown prefix, dump it - REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Unknown namespace prefix")); - return PR_FALSE; + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Unknown namespace prefix")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } aSelector.SetNameSpace(nameSpaceID); if (! GetToken(aErrorCode, PR_FALSE)) { REPORT_UNEXPECTED_EOF(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } if (eCSSToken_Ident == mToken.mType) { // element name - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; if (mCaseSensitive) { aSelector.SetTag(mToken.mIdent); } @@ -1600,14 +1654,15 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, } } else if (mToken.IsSymbol('*')) { // universal selector - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; // don't set tag } else { REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Expected element name or '*' but found")); + NS_LITERAL_STRING("Expected element name or '*' but found")); UngetToken(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } } else { // was element name @@ -1629,23 +1684,25 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, buffer.ToLowerCase(); aSelector.SetTag(buffer); } - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; } if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) - return PR_TRUE; + aParsingStatus = SELECTOR_PARSING_STOPPED_OK; + return; } } else if (mToken.IsSymbol('|')) { // No namespace - dataMask |= SEL_MASK_NSPACE; + aDataMask |= SEL_MASK_NSPACE; aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace // get mandatory tag if (! GetToken(aErrorCode, PR_FALSE)) { REPORT_UNEXPECTED_EOF(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } if (eCSSToken_Ident == mToken.mType) { // element name - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; if (mCaseSensitive) { aSelector.SetTag(mToken.mIdent); } @@ -1655,274 +1712,333 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, } } else if (mToken.IsSymbol('*')) { // universal selector - dataMask |= SEL_MASK_ELEM; + aDataMask |= SEL_MASK_ELEM; // don't set tag } else { REPORT_UNEXPECTED_TOKEN( NS_LITERAL_STRING("Expected element name or '*' but found")); UngetToken(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) - return PR_TRUE; + aParsingStatus = SELECTOR_PARSING_STOPPED_OK; + return; } } - for (;;) { - if (eCSSToken_ID == mToken.mType) { // #id - if (0 < mToken.mIdent.Length()) { // verify is legal ID - dataMask |= SEL_MASK_ID; - aSelector.AddID(mToken.mIdent); + aParsingStatus = SELECTOR_PARSING_ENDED_OK; +} + +void CSSParserImpl::ParseAttributeSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + + PRInt32 nameSpaceID = kNameSpaceID_None; + nsAutoString attr; + if (mToken.IsSymbol('*')) { // wildcard namespace + nameSpaceID = kNameSpaceID_Unknown; + if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (eCSSToken_Ident == mToken.mType) { // attr name + attr = mToken.mIdent; } else { - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING( - "Expected non-empty name for ID selector, but found")); + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for attribute name but found")); UngetToken(); - return PR_FALSE; + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + else { + REPORT_UNEXPECTED_TOKEN( + NS_LITERAL_STRING("Expected '|' but found")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + else if (mToken.IsSymbol('|')) { // NO namespace + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (eCSSToken_Ident == mToken.mType) { // attr name + attr = mToken.mIdent; + } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for attribute name but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace + attr = mToken.mIdent; // hang on to it + if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was a namespace + nameSpaceID = kNameSpaceID_Unknown; + if (mNameSpace) { + nsIAtom* prefix; + attr.ToLowerCase(); // always case insensitive, since stays within CSS + prefix = NS_NewAtom(attr); + mNameSpace->FindNameSpaceID(prefix, nameSpaceID); + NS_IF_RELEASE(prefix); + } // else, no delcared namespaces + if (kNameSpaceID_Unknown == nameSpaceID) { // unknown prefix, dump it + REPORT_UNEXPECTED_TOKEN( + NS_LITERAL_STRING("Unknown namespace prefix")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; } + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (eCSSToken_Ident == mToken.mType) { // attr name + attr = mToken.mIdent; + } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for attribute name but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + } + else { // malformed + REPORT_UNEXPECTED_TOKEN( + NS_LITERAL_STRING("Expected attribute name or namespace but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + + if (! mCaseSensitive) { + attr.ToLowerCase(); + } + if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if ((eCSSToken_Symbol == mToken.mType) || + (eCSSToken_Includes == mToken.mType) || + (eCSSToken_Dashmatch == mToken.mType) || + (eCSSToken_Beginsmatch == mToken.mType) || + (eCSSToken_Endsmatch == mToken.mType) || + (eCSSToken_Containsmatch == mToken.mType)) { + PRUint8 func; + if (eCSSToken_Includes == mToken.mType) { + func = NS_ATTR_FUNC_INCLUDES; + } + else if (eCSSToken_Dashmatch == mToken.mType) { + func = NS_ATTR_FUNC_DASHMATCH; + } + else if (eCSSToken_Beginsmatch == mToken.mType) { + func = NS_ATTR_FUNC_BEGINSMATCH; + } + else if (eCSSToken_Endsmatch == mToken.mType) { + func = NS_ATTR_FUNC_ENDSMATCH; + } + else if (eCSSToken_Containsmatch == mToken.mType) { + func = NS_ATTR_FUNC_CONTAINSMATCH; + } + else if (']' == mToken.mSymbol) { + aDataMask |= SEL_MASK_ATTRIB; + aSelector.AddAttribute(nameSpaceID, attr); + func = NS_ATTR_FUNC_SET; + } + else if ('=' == mToken.mSymbol) { + func = NS_ATTR_FUNC_EQUALS; + } + else { + REPORT_UNEXPECTED_TOKEN( + NS_LITERAL_STRING("Unexpected token in attribute selector: ")); + UngetToken(); // bad function + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (NS_ATTR_FUNC_SET != func) { // get value + if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) { + nsAutoString value(mToken.mIdent); + if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (mToken.IsSymbol(']')) { + aDataMask |= SEL_MASK_ATTRIB; + aSelector.AddAttribute(nameSpaceID, attr, func, value, mCaseSensitive); + } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected ']' to terminate attribute selector but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier or string for value in attribute selector but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + } + else { + REPORT_UNEXPECTED_TOKEN( + NS_LITERAL_STRING("Unexpected token in attribute selector: ")); + UngetToken(); // bad dog, no biscut! + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + aParsingStatus = SELECTOR_PARSING_ENDED_OK; +} + +void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + nsAutoString buffer; + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector +#ifdef INCLUDE_XUL + if (eCSSToken_Function != mToken.mType || + !IsOutlinerPseudoElement(mToken.mIdent)) { +#endif + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for pseudo-class selector but found")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; +#ifdef INCLUDE_XUL + } +#endif + } + buffer.Truncate(); + buffer.AppendWithConversion(':'); + buffer.Append(mToken.mIdent); + buffer.ToLowerCase(); + nsIAtom* pseudo = NS_NewAtom(buffer); + if (IsPseudoClass(pseudo)) { + // XXX parse lang pseudo class + aDataMask |= SEL_MASK_PCLASS; + aSelector.AddPseudoClass(pseudo); + NS_RELEASE(pseudo); + } + else { + if (0 == (aDataMask & SEL_MASK_PELEM)) { + aDataMask |= SEL_MASK_PELEM; + aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later + NS_RELEASE(pseudo); + +#ifdef INCLUDE_XUL + if (eCSSToken_Function == mToken.mType && + IsOutlinerPseudoElement(mToken.mIdent)) { + // We have encountered a pseudoelement of the form + // -moz-outliner-xxxx(a,b,c). We parse (a,b,c) and add each + // item in the list to the pseudoclass list. They will be pulled + // from the list later along with the pseudoelement. + if (!ParseOutlinerPseudoElement(aErrorCode, aSelector)) + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } +#endif + + // ensure selector ends here, must be followed by EOF, space, '{' or ',' + if (GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) + if ((eCSSToken_WhiteSpace == mToken.mType) || + (mToken.IsSymbol('{') || mToken.IsSymbol(','))) { + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_OK; + return; + } + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Found trailing token after pseudo-element, which must be the last part of a selector: ")); + UngetToken(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + else { // multiple pseudo elements, not legal + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Extra pseudo-element")); + UngetToken(); + NS_RELEASE(pseudo); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + } + aParsingStatus = SELECTOR_PARSING_ENDED_OK; +} + +/** + * This is the format for selectors: + * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]* + */ +PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, + nsCSSSelector& aSelector) +{ + PRInt32 dataMask = 0; + PRInt32 parsingStatus = SELECTOR_PARSING_ENDED_OK; + + if (! GetToken(aErrorCode, PR_TRUE)) { + REPORT_UNEXPECTED_EOF(); + return PR_FALSE; + } + + ParseTypeOrUniversalSelector(dataMask, aSelector, parsingStatus, aErrorCode); + if (SELECTOR_PARSING_STOPPED_OK == parsingStatus) { + return PR_TRUE; + } + else if (SELECTOR_PARSING_STOPPED_ERROR == parsingStatus) { + return PR_FALSE; + } + + for (;;) { + parsingStatus = SELECTOR_PARSING_ENDED_OK; + if (eCSSToken_ID == mToken.mType) { // #id + ParseIDSelector(dataMask, aSelector, parsingStatus, aErrorCode); } else if (mToken.IsSymbol('.')) { // .class - if (! GetToken(aErrorCode, PR_FALSE)) { // get ident - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if (eCSSToken_Ident != mToken.mType) { // malformed selector - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for class selector but found")); - UngetToken(); - return PR_FALSE; - } - dataMask |= SEL_MASK_CLASS; - aSelector.AddClass(mToken.mIdent); // class always case sensitive + ParseClassSelector(dataMask, aSelector, parsingStatus, aErrorCode); } else if (mToken.IsSymbol(':')) { // :pseudo - if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if (eCSSToken_Ident != mToken.mType) { // malformed selector -#ifdef INCLUDE_XUL - if (eCSSToken_Function != mToken.mType || - !IsOutlinerPseudoElement(mToken.mIdent)) { -#endif - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for pseudo-class selector but found")); - UngetToken(); - return PR_FALSE; -#ifdef INCLUDE_XUL - } -#endif - } - buffer.Truncate(); - buffer.AppendWithConversion(':'); - buffer.Append(mToken.mIdent); - buffer.ToLowerCase(); - nsIAtom* pseudo = NS_NewAtom(buffer); - if (IsPseudoClass(pseudo)) { - // XXX parse lang pseudo class - dataMask |= SEL_MASK_PCLASS; - aSelector.AddPseudoClass(pseudo); - NS_RELEASE(pseudo); - } - else { - if (0 == (dataMask & SEL_MASK_PELEM)) { - dataMask |= SEL_MASK_PELEM; - aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later - NS_RELEASE(pseudo); - -#ifdef INCLUDE_XUL - if (eCSSToken_Function == mToken.mType && - IsOutlinerPseudoElement(mToken.mIdent)) { - // We have encountered a pseudoelement of the form - // -moz-outliner-xxxx(a,b,c). We parse (a,b,c) and add each - // item in the list to the pseudoclass list. They will be pulled - // from the list later along with the pseudoelement. - if (!ParseOutlinerPseudoElement(aErrorCode, aSelector)) - return PR_FALSE; - } -#endif - - // ensure selector ends here, must be followed by EOF, space, '{' or ',' - if (GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) - if ((eCSSToken_WhiteSpace == mToken.mType) || - (mToken.IsSymbol('{') || mToken.IsSymbol(','))) { - UngetToken(); - return PR_TRUE; - } - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Found trailing token after pseudo-element, which must be the last part of a selector: ")); - UngetToken(); - return PR_FALSE; - } - } - else { // multiple pseudo elements, not legal - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Extra pseudo-element")); - UngetToken(); - NS_RELEASE(pseudo); - return PR_FALSE; - } - } + ParsePseudoSelector(dataMask, aSelector, parsingStatus, aErrorCode); } else if (mToken.IsSymbol('[')) { // attribute - if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - - PRInt32 nameSpaceID = kNameSpaceID_None; - nsAutoString attr; - if (mToken.IsSymbol('*')) { // wildcard namespace - nameSpaceID = kNameSpaceID_Unknown; - if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { - if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if (eCSSToken_Ident == mToken.mType) { // attr name - attr = mToken.mIdent; - } - else { - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for attribute name but found")); - UngetToken(); - return PR_FALSE; - } - } - else { - REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Expected '|' but found")); - return PR_FALSE; - } - } - else if (mToken.IsSymbol('|')) { // NO namespace - if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if (eCSSToken_Ident == mToken.mType) { // attr name - attr = mToken.mIdent; - } - else { - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for attribute name but found")); - UngetToken(); - return PR_FALSE; - } - } - else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace - attr = mToken.mIdent; // hang on to it - if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was a namespace - nameSpaceID = kNameSpaceID_Unknown; - if (mNameSpace) { - nsIAtom* prefix; - attr.ToLowerCase(); // always case insensitive, since stays within CSS - prefix = NS_NewAtom(attr); - mNameSpace->FindNameSpaceID(prefix, nameSpaceID); - NS_IF_RELEASE(prefix); - } // else, no delcared namespaces - if (kNameSpaceID_Unknown == nameSpaceID) { // unknown prefix, dump it - REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Unknown namespace prefix")); - return PR_FALSE; - } - if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if (eCSSToken_Ident == mToken.mType) { // attr name - attr = mToken.mIdent; - } - else { - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for attribute name but found")); - UngetToken(); - return PR_FALSE; - } - } - } - else { // malformed - REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Expected attribute name or namespace but found")); - UngetToken(); - return PR_FALSE; - } - - if (! mCaseSensitive) { - attr.ToLowerCase(); - } - if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if ((eCSSToken_Symbol == mToken.mType) || - (eCSSToken_Includes == mToken.mType) || - (eCSSToken_Dashmatch == mToken.mType) || - (eCSSToken_Beginsmatch == mToken.mType) || - (eCSSToken_Endsmatch == mToken.mType) || - (eCSSToken_Containsmatch == mToken.mType)) { - PRUint8 func; - if (eCSSToken_Includes == mToken.mType) { - func = NS_ATTR_FUNC_INCLUDES; - } - else if (eCSSToken_Dashmatch == mToken.mType) { - func = NS_ATTR_FUNC_DASHMATCH; - } - else if (eCSSToken_Beginsmatch == mToken.mType) { - func = NS_ATTR_FUNC_BEGINSMATCH; - } - else if (eCSSToken_Endsmatch == mToken.mType) { - func = NS_ATTR_FUNC_ENDSMATCH; - } - else if (eCSSToken_Containsmatch == mToken.mType) { - func = NS_ATTR_FUNC_CONTAINSMATCH; - } - else if (']' == mToken.mSymbol) { - dataMask |= SEL_MASK_ATTRIB; - aSelector.AddAttribute(nameSpaceID, attr); - func = NS_ATTR_FUNC_SET; - } - else if ('=' == mToken.mSymbol) { - func = NS_ATTR_FUNC_EQUALS; - } - else { - REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Unexpected token in attribute selector: ")); - UngetToken(); // bad function - return PR_FALSE; - } - if (NS_ATTR_FUNC_SET != func) { // get value - if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) { - nsAutoString value(mToken.mIdent); - if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF - REPORT_UNEXPECTED_EOF(); - return PR_FALSE; - } - if (mToken.IsSymbol(']')) { - dataMask |= SEL_MASK_ATTRIB; - aSelector.AddAttribute(nameSpaceID, attr, func, value, mCaseSensitive); - } - else { - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected ']' to terminate attribute selector but found")); - UngetToken(); - return PR_FALSE; - } - } - else { - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier or string for value in attribute selector but found")); - UngetToken(); - return PR_FALSE; - } - } - } - else { - REPORT_UNEXPECTED_TOKEN( - NS_LITERAL_STRING("Unexpected token in attribute selector: ")); - UngetToken(); // bad dog, no biscut! - return PR_FALSE; - } + ParseAttributeSelector(dataMask, aSelector, parsingStatus, aErrorCode); } else { // not a selector token, we're done break; } - + + if (SELECTOR_PARSING_STOPPED_OK == parsingStatus) { + return PR_TRUE; + } + else if (SELECTOR_PARSING_STOPPED_ERROR == parsingStatus) { + return PR_FALSE; + } + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) return PR_TRUE; }