diff --git a/mozilla/content/html/style/src/nsCSSParser.cpp b/mozilla/content/html/style/src/nsCSSParser.cpp index e0a4aebf2c7..2237f531f94 100644 --- a/mozilla/content/html/style/src/nsCSSParser.cpp +++ b/mozilla/content/html/style/src/nsCSSParser.cpp @@ -31,6 +31,9 @@ #include "nsVoidArray.h" #include "nsColor.h" #include "nsStyleConsts.h" +#include "nsCSSAtoms.h" +#include "nsINameSpaceManager.h" +#include "nsINameSpace.h" // XXX TODO: // - rework aErrorCode stuff: switch over to nsresult @@ -46,142 +49,52 @@ static NS_DEFINE_IID(kCSSTextSID, NS_CSS_TEXT_SID); #define KEYWORD_BUFFER_SIZE 50 // big enough for any keyword -struct Selector { - nsAutoString mTag; // weight 1 - nsAutoString mID; // weight 100 - nsAutoString mClass; // weight 10 - nsAutoString mPseudoClass; // weight 10 (== class) - nsAutoString mPseudoElement; // weight 10 (== class) ?? - - PRUint32 mMask; // which fields have values - - Selector(); - ~Selector(); - - PRInt32 Weight() const; -#ifdef NS_DEBUG - void Dump() const; -#endif -}; - -#define SELECTOR_TAG 0x01 -#define SELECTOR_ID 0x02 -#define SELECTOR_CLASS 0x04 -#define SELECTOR_PSEUDO_CLASS 0x08 -#define SELECTOR_PSEUDO_ELEMENT 0x10 - -#define SELECTOR_WEIGHT_BASE 10 - -Selector::Selector() -{ - mMask = 0; -} - -Selector::~Selector() -{ -} - -PRInt32 Selector::Weight() const -{ - return (((0 != (SELECTOR_TAG & mMask)) ? 1 : 0) + - ((0 != (SELECTOR_ID & mMask)) ? (SELECTOR_WEIGHT_BASE * SELECTOR_WEIGHT_BASE) : 0) + - ((0 != ((SELECTOR_CLASS | SELECTOR_PSEUDO_CLASS | SELECTOR_PSEUDO_ELEMENT) & mMask)) ? SELECTOR_WEIGHT_BASE : 0)); -} - -#ifdef NS_DEBUG -void Selector::Dump() const -{ - PRBool needSpace = PR_FALSE; - if (mTag.Length() > 0) { - fputs(mTag, stdout); - needSpace = PR_TRUE; - } - if (mID.Length() > 0) { - if (needSpace) fputs(" ", stdout); - fputs("#", stdout); - fputs(mID, stdout); - needSpace = PR_TRUE; - } - if (mClass.Length() > 0) { - if (needSpace) fputs(" ", stdout); - fputs(".", stdout); - fputs(mClass, stdout); - needSpace = PR_TRUE; - } - if (mPseudoClass.Length() > 0) { - if (needSpace) fputs(" ", stdout); - fputs(":", stdout); - fputs(mPseudoClass, stdout); - needSpace = PR_TRUE; - } - if (mPseudoElement.Length() > 0) { - if (needSpace) fputs(" ", stdout); - fputs(":", stdout); - fputs(mPseudoElement, stdout); - needSpace = PR_TRUE; - } -} -#endif - // e.g. "P B, H1 B { ... }" has a selector list with two elements, // each of which has two selectors. struct SelectorList { - SelectorList* mNext; - nsVoidArray mSelectors; + SelectorList(void); + ~SelectorList(void); - SelectorList(); - - void Destroy(); - - void AddSelector(Selector* aSelector) { - mSelectors.AppendElement(aSelector); - } + void AddSelector(const nsCSSSelector& aSelector); #ifdef NS_DEBUG - void Dump(); + void Dump(void); #endif -private: - ~SelectorList(); + nsCSSSelector* mSelectors; + SelectorList* mNext; }; -SelectorList::SelectorList() +SelectorList::SelectorList(void) + : mSelectors(nsnull), + mNext(nsnull) { - mNext = nsnull; } SelectorList::~SelectorList() { - PRInt32 n = mSelectors.Count(); - for (PRInt32 i = 0; i < n; i++) { - Selector* sel = (Selector*) mSelectors.ElementAt(i); - delete sel; + nsCSSSelector* sel = mSelectors; + while (nsnull != sel) { + nsCSSSelector* dead = sel; + sel = sel->mNext; + delete dead; + } + if (nsnull != mNext) { + delete mNext; } } -void SelectorList::Destroy() -{ - SelectorList* list = this; - while (nsnull != list) { - SelectorList* next = list->mNext; - delete list; - list = next; - } +void SelectorList::AddSelector(const nsCSSSelector& aSelector) +{ // prepend to list + nsCSSSelector* newSel = new nsCSSSelector(aSelector); + newSel->mNext = mSelectors; + mSelectors = newSel; } + #ifdef NS_DEBUG void SelectorList::Dump() { - PRInt32 n = mSelectors.Count(); - for (PRInt32 i = 0; i < n; i++) { - Selector* sel = (Selector*) mSelectors.ElementAt(i); - sel->Dump(); - fputs(" ", stdout); - } - if (mNext) { - fputs(", ", stdout); - mNext->Dump(); - } } #endif @@ -200,6 +113,8 @@ public: NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet); + NS_IMETHOD SetCaseSensative(PRBool aCaseSensative); + NS_IMETHOD Parse(nsIUnicharInputStream* aInput, nsIURL* aInputURL, nsICSSStyleSheet*& aResult); @@ -231,9 +146,9 @@ protected: PRBool GatherMedia(PRInt32& aErrorCode, nsString& aMedia); PRBool ProcessImport(PRInt32& aErrorCode, const nsString& aURLSpec, const nsString& aMedia); - PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList* aListHead); - PRBool ParseSelectorList(PRInt32& aErrorCode, SelectorList* aListHead); - PRBool ParseSelector(PRInt32& aErrorCode, Selector* aSelectorResult); + PRBool ParseSelectorList(PRInt32& aErrorCode, SelectorList*& aListHead); + PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList*& aListHead); + PRBool ParseSelector(PRInt32& aErrorCode, nsCSSSelector& aSelectorResult); nsICSSDeclaration* ParseDeclarationBlock(PRInt32& aErrorCode, PRBool aCheckForBraces); PRBool ParseDeclaration(PRInt32& aErrorCode, @@ -317,6 +232,7 @@ protected: PRBool mInHead; PRBool mNavQuirkMode; + PRBool mCaseSensative; }; NS_HTML nsresult @@ -334,19 +250,23 @@ NS_NewCSSParser(nsICSSParser** aInstancePtrResult) CSSParserImpl::CSSParserImpl() { NS_INIT_REFCNT(); + nsCSSAtoms::AddrefAtoms(); mScanner = nsnull; mSheet = nsnull; mHavePushBack = PR_FALSE; mNavQuirkMode = PR_TRUE; + mCaseSensative = PR_FALSE; } CSSParserImpl::CSSParserImpl(nsICSSStyleSheet* aSheet) { NS_INIT_REFCNT(); + nsCSSAtoms::AddrefAtoms(); mScanner = nsnull; mSheet = aSheet; NS_ADDREF(aSheet); mHavePushBack = PR_FALSE; mNavQuirkMode = PR_TRUE; + mCaseSensative = PR_FALSE; } NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID) @@ -354,16 +274,17 @@ NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID) CSSParserImpl::~CSSParserImpl() { NS_IF_RELEASE(mSheet); + nsCSSAtoms::ReleaseAtoms(); } -NS_METHOD +NS_IMETHODIMP CSSParserImpl::GetInfoMask(PRUint32& aResult) { aResult = NS_CSS_GETINFO_CSS1 | NS_CSS_GETINFO_CSSP; return NS_OK; } -NS_METHOD +NS_IMETHODIMP CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet) { NS_PRECONDITION(nsnull != aSheet, "null ptr"); @@ -381,7 +302,15 @@ CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet) return NS_OK; } -NS_METHOD +NS_IMETHODIMP +CSSParserImpl::SetCaseSensative(PRBool aCaseSensative) +{ + mCaseSensative = aCaseSensative; + return NS_OK; +} + + +NS_IMETHODIMP CSSParserImpl::Parse(nsIUnicharInputStream* aInput, nsIURL* aInputURL, nsICSSStyleSheet*& aResult) @@ -426,7 +355,7 @@ CSSParserImpl::Parse(nsIUnicharInputStream* aInput, return NS_OK; } -NS_METHOD +NS_IMETHODIMP CSSParserImpl::ParseDeclarations(const nsString& aDeclaration, nsIURL* aBaseURL, nsIStyleRule*& aResult) @@ -471,7 +400,7 @@ CSSParserImpl::ParseDeclarations(const nsString& aDeclaration, return NS_OK; } -NS_METHOD +NS_IMETHODIMP CSSParserImpl::ParseAndAppendDeclaration(const nsString& aBuffer, nsIURL* aBaseURL, nsICSSDeclaration* aDeclaration, @@ -925,18 +854,18 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode) nsCSSToken* tk = &mToken; // First get the list of selectors for the rule - SelectorList *slist = new SelectorList(); - if (!ParseSelectorList(aErrorCode, slist)) { + SelectorList* slist = nsnull; + if (! ParseSelectorList(aErrorCode, slist)) { SkipRuleSet(aErrorCode); - slist->Destroy(); return PR_FALSE; } + NS_ASSERTION(nsnull != slist, "null selector list"); // Next parse the declaration block nsICSSDeclaration* declaration = ParseDeclarationBlock(aErrorCode, PR_TRUE); if (nsnull == declaration) { // XXX skip something here - slist->Destroy(); + delete slist; return PR_FALSE; } @@ -950,34 +879,16 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode) // Translate the selector list and declaration block into style data SelectorList* list = slist; - nsCSSSelector selector; - nsICSSStyleRule* rule = nsnull; while (nsnull != list) { - PRInt32 selIndex = list->mSelectors.Count(); - - Selector* sel = (Selector*)list->mSelectors[--selIndex]; - if (0 < sel->mPseudoElement.Length()) { // only pseudo elements at end selector - nsString nullStr; - selector.Set(sel->mPseudoElement, nullStr, nullStr, nullStr); - NS_NewCSSStyleRule(&rule, selector); - } - selector.Set(sel->mTag, sel->mID, sel->mClass, sel->mPseudoClass); - PRInt32 weight = sel->Weight(); - - if (nsnull == rule) { - NS_NewCSSStyleRule(&rule, selector); - } - else { - rule->AddSelector(selector); - } + PRInt32 weight = list->mSelectors->CalcWeight(); + nsICSSStyleRule* rule = nsnull; + NS_NewCSSStyleRule(&rule, *(list->mSelectors)); if (nsnull != rule) { - while (--selIndex >= 0) { - Selector* sel = (Selector*)list->mSelectors[selIndex]; - selector.Set(sel->mTag, sel->mID, sel->mClass, sel->mPseudoClass); - - rule->AddSelector(selector); - weight += sel->Weight(); + if (nsnull != list->mSelectors->mNext) { // hand off other selectors to new rule + nsCSSSelector* ruleFirst = rule->FirstSelector(); + ruleFirst->mNext = list->mSelectors->mNext; + list->mSelectors->mNext = nsnull; } rule->SetDeclaration(declaration); rule->SetWeight(weight); @@ -990,219 +901,342 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode) } // Release temporary storage - slist->Destroy(); + delete slist; NS_RELEASE(declaration); return PR_TRUE; } PRBool CSSParserImpl::ParseSelectorList(PRInt32& aErrorCode, - SelectorList* aListHead) + SelectorList*& aListHead) { - if (!ParseSelectorGroup(aErrorCode, aListHead)) { + SelectorList* list = nsnull; + if (! ParseSelectorGroup(aErrorCode, list)) { // must have at least one selector group + aListHead = nsnull; return PR_FALSE; } + NS_ASSERTION(nsnull != list, "no selector list"); + aListHead = list; // After that there must either be a "," or a "{" nsCSSToken* tk = &mToken; for (;;) { - if (!GetToken(aErrorCode, PR_TRUE)) { - return PR_FALSE; + if (! GetToken(aErrorCode, PR_TRUE)) { + break; } if (eCSSToken_Symbol != tk->mType) { UngetToken(); - return PR_FALSE; + break; } if (',' == tk->mSymbol) { + SelectorList* newList = nsnull; // Another selector group must follow - SelectorList* newList = new SelectorList(); - if (!ParseSelectorGroup(aErrorCode, newList)) { - newList->Destroy(); - return PR_FALSE; + if (! ParseSelectorGroup(aErrorCode, newList)) { + break; } // add new list to the end of the selector list - aListHead->mNext = newList; - aListHead = newList; + list->mNext = newList; + list = newList; continue; } else if ('{' == tk->mSymbol) { UngetToken(); - break; + return PR_TRUE; } else { UngetToken(); - return PR_FALSE; + break; } } - return PR_TRUE; + delete aListHead; + aListHead = nsnull; + return PR_FALSE; +} + +static PRBool IsPseudoClass(const nsIAtom* aAtom) +{ + return PRBool((nsCSSAtoms::activePseudo == aAtom) || + (nsCSSAtoms::firstChildPseudo == aAtom) || + (nsCSSAtoms::focusPseudo == aAtom) || + (nsCSSAtoms::hoverPseudo == aAtom) || + (nsCSSAtoms::langPseudo == aAtom) || + (nsCSSAtoms::linkPseudo == aAtom) || + (nsCSSAtoms::outOfDatePseudo == aAtom) || + (nsCSSAtoms::visitedPseudo == aAtom)); +} + +static PRBool IsSinglePseudoClass(const nsCSSSelector& aSelector) +{ + return PRBool((aSelector.mNameSpace == kNameSpaceID_Unknown) && + (aSelector.mTag == nsnull) && + (aSelector.mID == nsnull) && + (aSelector.mClassList == nsnull) && + (aSelector.mAttrList == nsnull) && + (aSelector.mPseudoClassList != nsnull) && + (aSelector.mPseudoClassList->mNext == nsnull)); } PRBool CSSParserImpl::ParseSelectorGroup(PRInt32& aErrorCode, - SelectorList* aList) + SelectorList*& aList) { + SelectorList* list = nsnull; for (;;) { - Selector* sel = new Selector(); - if (!ParseSelector(aErrorCode, sel)) { - delete sel; + nsCSSSelector selector; + if (! ParseSelector(aErrorCode, selector)) { break; } - aList->AddSelector(sel); + if (nsnull == list) { + list = new SelectorList(); + } + list->AddSelector(selector); + nsCSSSelector* listSel = list->mSelectors; + // XXX parse combinator here + + // pull out pseudo elements here + nsAtomList* prevList = nsnull; + nsAtomList* pseudoClassList = listSel->mPseudoClassList; + while (nsnull != pseudoClassList) { + if (! IsPseudoClass(pseudoClassList->mAtom)) { + if (IsSinglePseudoClass(*listSel)) { // convert to pseudo element selector + nsIAtom* pseudoElement = pseudoClassList->mAtom; // steal ref count + pseudoClassList->mAtom = nsnull; + listSel->Reset(); + listSel->mTag = pseudoElement; + } + else { // append new pseudo element selector + selector.Reset(); + selector.mTag = pseudoClassList->mAtom; // steal ref count + list->AddSelector(selector); + pseudoClassList->mAtom = nsnull; + if (nsnull == prevList) { // delete list entry + listSel->mPseudoClassList = pseudoClassList->mNext; + } + else { + prevList->mNext = pseudoClassList->mNext; + } + pseudoClassList->mNext = nsnull; + delete pseudoClassList; + } + break; // only one pseudo element per selector + } + prevList = pseudoClassList; + pseudoClassList = pseudoClassList->mNext; + } } - return (PRBool) (aList->mSelectors.Count() > 0); + aList = list; + return PRBool(nsnull != aList); } -static PRBool IsPseudoClass(const nsString& aBuffer) -{ - return (aBuffer.EqualsIgnoreCase("link") || - aBuffer.EqualsIgnoreCase("visited") || - aBuffer.EqualsIgnoreCase("hover") || - aBuffer.EqualsIgnoreCase("out-of-date") || // our extension - aBuffer.EqualsIgnoreCase("active")); -} +#define SEL_MASK_NSPACE 0x01 +#define SEL_MASK_ELEM 0x02 +#define SEL_MASK_ID 0x04 +#define SEL_MASK_CLASS 0x08 +#define SEL_MASK_ATTRIB 0x10 +#define SEL_MASK_PCLASS 0x20 +#define SEL_MASK_PELEM 0x40 /** - * These are the 31 possible kinds of CSS1 style selectors: - * (but there are 50 ways to leave your lover) - * [*] means it can repeat - * + * This is the format for selectors: + * operator? [namespace \:]? element_name? [ ID | class | attrib | pseudo ]* */ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, - Selector* aSelectorResult) + nsCSSSelector& aSelector) { - PRUint32 mask = 0; - aSelectorResult->mTag.Truncate(0); - aSelectorResult->mClass.Truncate(0); - aSelectorResult->mID.Truncate(0); - aSelectorResult->mPseudoClass.Truncate(0); - aSelectorResult->mPseudoElement.Truncate(0); + PRInt32 dataMask = 0; + nsAutoString buffer; - nsCSSToken* tk = &mToken; - if (!GetToken(aErrorCode, PR_TRUE)) { + if (! GetToken(aErrorCode, PR_TRUE)) { return PR_FALSE; } - if (eCSSToken_Ident == tk->mType) { - // tag - mask |= SELECTOR_TAG; - aSelectorResult->mTag.Append(tk->mIdent); - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof is ok (here!) + if ((eCSSToken_Symbol == mToken.mType) && ('*' == mToken.mSymbol)) { // universal element selector + // don't set any tag in the selector + dataMask |= SEL_MASK_ELEM; + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) return PR_TRUE; } } - if (eCSSToken_ID == tk->mType) { - // #id - if ((0 < tk->mIdent.Length()) && (nsString::IsAlpha(tk->mIdent.CharAt(0)))) { // verify is legal ID - mask |= SELECTOR_ID; - aSelectorResult->mID.Append(tk->mIdent); - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof is ok (here!) - return PR_TRUE; + else if (eCSSToken_Ident == mToken.mType) { // element name + PRInt32 colon = mToken.mIdent.Find(':'); + if (-1 == colon) { // no namespace + if (mCaseSensative) { + aSelector.SetTag(mToken.mIdent); + } + else { + mToken.mIdent.ToUpperCase(buffer); + aSelector.SetTag(buffer); } } - else { - return PR_FALSE; + else { // pull out the namespace + nsAutoString nameSpace; + mToken.mIdent.Left(nameSpace, colon); + mToken.mIdent.Right(buffer, (mToken.mIdent.Length() - (colon + 1))); + if (! mCaseSensative) { + buffer.ToUpperCase(); + } + // XXX lookup namespace, set it + // deal with * namespace (== unknown) + aSelector.SetTag(buffer); } - } - if ((eCSSToken_Symbol == tk->mType) && ('.' == tk->mSymbol)) { - // .class - if (!GetToken(aErrorCode, PR_FALSE)) { - return PR_FALSE; - } - if (eCSSToken_Ident != tk->mType) { - // malformed selector - UngetToken(); - return PR_FALSE; - } - mask |= SELECTOR_CLASS; - aSelectorResult->mClass.Append(tk->mIdent); - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof is ok (here!) + dataMask |= SEL_MASK_ELEM; + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) return PR_TRUE; } } - if ((eCSSToken_Symbol == tk->mType) && (':' == tk->mSymbol)) { - // :pseudo - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof - return PR_FALSE; + for (;;) { + if (eCSSToken_ID == mToken.mType) { // #id + if ((0 == (dataMask & SEL_MASK_ID)) && // only one ID + (0 < mToken.mIdent.Length()) && + (nsString::IsAlpha(mToken.mIdent.CharAt(0)))) { // verify is legal ID + dataMask |= SEL_MASK_ID; + aSelector.SetID(mToken.mIdent); + } + else { + UngetToken(); + return PR_FALSE; + } } - if (eCSSToken_Ident != tk->mType) { - // malformed selector - UngetToken(); - return PR_FALSE; + else if ((eCSSToken_Symbol == mToken.mType) && ('.' == mToken.mSymbol)) { // .class + if (! GetToken(aErrorCode, PR_FALSE)) { // get ident + return PR_FALSE; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector (XXX what about leading digits?) + UngetToken(); + return PR_FALSE; + } + dataMask |= SEL_MASK_CLASS; + if (mCaseSensative) { + aSelector.AddClass(mToken.mIdent); + } + else { + mToken.mIdent.ToUpperCase(buffer); + aSelector.AddClass(buffer); + } } - if (IsPseudoClass(tk->mIdent)) { - mask |= SELECTOR_PSEUDO_CLASS; - aSelectorResult->mPseudoClass.Append(tk->mIdent); + else if ((eCSSToken_Symbol == mToken.mType) && (':' == mToken.mSymbol)) { // :pseudo + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof + return PR_FALSE; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector + UngetToken(); + return PR_FALSE; + } + buffer.Truncate(); + buffer.Append(':'); + buffer.Append(mToken.mIdent); + buffer.ToUpperCase(); + nsIAtom* pseudo = NS_NewAtom(buffer); + if (IsPseudoClass(pseudo)) { + // XXX parse lang pseudo class + dataMask |= SEL_MASK_PCLASS; + aSelector.AddPseudoClass(pseudo); + } + else { + if (0 == (dataMask & SEL_MASK_PELEM)) { + dataMask |= SEL_MASK_PELEM; + aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later + } + else { // multiple pseudo elements, not legal + UngetToken(); + NS_RELEASE(pseudo); + return PR_FALSE; + } + } + NS_RELEASE(pseudo); } - else { - mask |= SELECTOR_PSEUDO_ELEMENT; - aSelectorResult->mPseudoElement.Append(':'); // keep the colon - aSelectorResult->mPseudoElement.Append(tk->mIdent); + else if ((eCSSToken_Symbol == mToken.mType) && ('[' == mToken.mSymbol)) { // attribute + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector + UngetToken(); + return PR_FALSE; + } + nsAutoString attr(mToken.mType); + if (! mCaseSensative) { + attr.ToUpperCase(); + } + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if (eCSSToken_Symbol == mToken.mType) { + PRUint8 func; + if (']' == mToken.mSymbol) { + dataMask |= SEL_MASK_ATTRIB; + aSelector.AddAttribute(attr); + func = NS_ATTR_FUNC_SET; + } + else if ('=' == mToken.mSymbol) { + func = NS_ATTR_FUNC_EQUALS; + } + else if ('~' == mToken.mSymbol) { + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if ((eCSSToken_Symbol == mToken.mType) && ('=' == mToken.mSymbol)) { + func = NS_ATTR_FUNC_INCLUDES; + } + else { + UngetToken(); + return PR_FALSE; + } + } + else if ('|' == mToken.mSymbol) { + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if ((eCSSToken_Symbol == mToken.mType) && ('=' == mToken.mSymbol)) { + func = NS_ATTR_FUNC_DASHMATCH; + } + else { + UngetToken(); + return PR_FALSE; + } + } + else { + UngetToken(); // bad function + return PR_FALSE; + } + if (NS_ATTR_FUNC_SET != func) { // get value + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) { + nsAutoString value(mToken.mIdent); + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if ((eCSSToken_Symbol == mToken.mType) && (']' == mToken.mSymbol)) { + dataMask |= SEL_MASK_ATTRIB; + if (! mCaseSensative) { + value.ToUpperCase(); + } + aSelector.AddAttribute(attr, func, value); + } + else { + UngetToken(); + return PR_FALSE; + } + } + else { + UngetToken(); + return PR_FALSE; + } + } + } + else { + UngetToken(); // bad dog, no biscut! + return PR_FALSE; + } } - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof is ok (here!) + else { // not a selector token, we're done + break; + } + + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) return PR_TRUE; } } - if ((eCSSToken_Symbol == tk->mType) && (':' == tk->mSymbol)) { - // :pseudo - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof - return PR_FALSE; - } - if (eCSSToken_Ident != tk->mType) { - // malformed selector - UngetToken(); - return PR_FALSE; - } - if (! IsPseudoClass(tk->mIdent)) { - mask |= SELECTOR_PSEUDO_ELEMENT; - aSelectorResult->mPseudoElement.Truncate(0); - aSelectorResult->mPseudoElement.Append(':'); // keep the colon - aSelectorResult->mPseudoElement.Append(tk->mIdent); - } - tk = nsnull; - } - if (nsnull != tk) { - UngetToken(); - } - if (mask == 0) { - return PR_FALSE; - } - aSelectorResult->mMask = mask; - return PR_TRUE; + UngetToken(); + return PRBool(0 != dataMask); } nsICSSDeclaration* diff --git a/mozilla/content/html/style/src/nsCSSStyleRule.cpp b/mozilla/content/html/style/src/nsCSSStyleRule.cpp index e612fcfdce4..3678fa8e206 100644 --- a/mozilla/content/html/style/src/nsCSSStyleRule.cpp +++ b/mozilla/content/html/style/src/nsCSSStyleRule.cpp @@ -38,6 +38,7 @@ #include "nsIScriptGlobalObject.h" #include "nsIScriptObjectOwner.h" #include "nsDOMCSSDeclaration.h" +#include "nsINameSpaceManager.h" //#define DEBUG_REFS @@ -62,95 +63,330 @@ static NS_DEFINE_IID(kCSSTableSID, NS_CSS_TABLE_SID); // -- nsCSSSelector ------------------------------- -nsCSSSelector::nsCSSSelector() - : mTag(nsnull), mID(nsnull), mClass(nsnull), mPseudoClass(nsnull), +#define NS_IF_COPY(dest,source,type) \ + if (nsnull != source) dest = new type(*(source)) + +#define NS_IF_DELETE(ptr) \ + if (nsnull != ptr) { delete ptr; ptr = nsnull; } + +nsAtomList::nsAtomList(nsIAtom* aAtom) + : mAtom(aAtom), mNext(nsnull) { + NS_IF_ADDREF(mAtom); } -nsCSSSelector::nsCSSSelector(nsIAtom* aTag, nsIAtom* aID, nsIAtom* aClass, nsIAtom* aPseudoClass) - : mTag(aTag), mID(aID), mClass(aClass), mPseudoClass(aPseudoClass), +nsAtomList::nsAtomList(const nsString& aAtomValue) + : mAtom(nsnull), + mNext(nsnull) +{ + mAtom = NS_NewAtom(aAtomValue); +} + +nsAtomList::nsAtomList(const nsAtomList& aCopy) + : mAtom(aCopy.mAtom), + mNext(nsnull) +{ + NS_IF_ADDREF(mAtom); + NS_IF_COPY(mNext, aCopy.mNext, nsAtomList); +} + +nsAtomList::~nsAtomList(void) +{ + NS_IF_RELEASE(mAtom); + NS_IF_DELETE(mNext); +} + +PRBool nsAtomList::Equals(const nsAtomList* aOther) const +{ + if (this == aOther) { + return PR_TRUE; + } + if (nsnull != aOther) { + if (mAtom == aOther->mAtom) { + if (nsnull != mNext) { + return mNext->Equals(aOther->mNext); + } + return PRBool(nsnull == aOther->mNext); + } + } + return PR_FALSE; +} + +nsAttrSelector::nsAttrSelector(const nsString& aAttr) + : mAttr(nsnull), + mFunction(NS_ATTR_FUNC_SET), + mValue(), + mNext(nsnull) +{ + mAttr = NS_NewAtom(aAttr); +} + +nsAttrSelector::nsAttrSelector(const nsString& aAttr, PRUint8 aFunction, const nsString& aValue) + : mAttr(nsnull), + mFunction(aFunction), + mValue(aValue), + mNext(nsnull) +{ + mAttr = NS_NewAtom(aAttr); +} + +nsAttrSelector::nsAttrSelector(const nsAttrSelector& aCopy) + : mAttr(aCopy.mAttr), + mFunction(aCopy.mFunction), + mValue(aCopy.mValue), + mNext(nsnull) +{ + NS_IF_ADDREF(mAttr); + NS_IF_COPY(mNext, aCopy.mNext, nsAttrSelector); +} + +nsAttrSelector::~nsAttrSelector(void) +{ + NS_IF_RELEASE(mAttr); + NS_IF_DELETE(mNext); +} + +PRBool nsAttrSelector::Equals(const nsAttrSelector* aOther) const +{ + if (this == aOther) { + return PR_TRUE; + } + if (nsnull != aOther) { + if ((mAttr == aOther->mAttr) && + (mFunction == aOther->mFunction) && + mValue.Equals(aOther->mValue)) { + if (nsnull != mNext) { + return mNext->Equals(aOther->mNext); + } + return PRBool(nsnull == aOther->mNext); + } + } + return PR_FALSE; +} + +nsCSSSelector::nsCSSSelector(void) + : mNameSpace(kNameSpaceID_Unknown), mTag(nsnull), + mID(nsnull), + mClassList(nsnull), + mPseudoClassList(nsnull), + mAttrList(nsnull), + mOperator(0), mNext(nsnull) { - NS_IF_ADDREF(mTag); - NS_IF_ADDREF(mID); - NS_IF_ADDREF(mClass); - NS_IF_ADDREF(mPseudoClass); } nsCSSSelector::nsCSSSelector(const nsCSSSelector& aCopy) - : mTag(aCopy.mTag), mID(aCopy.mID), mClass(aCopy.mClass), mPseudoClass(aCopy.mPseudoClass), + : mNameSpace(aCopy.mNameSpace), mTag(aCopy.mTag), + mID(aCopy.mID), + mClassList(nsnull), + mPseudoClassList(nsnull), + mAttrList(nsnull), + mOperator(aCopy.mOperator), mNext(nsnull) -{ // implmented to support extension to CSS2 (when we have to copy the array) +{ NS_IF_ADDREF(mTag); NS_IF_ADDREF(mID); - NS_IF_ADDREF(mClass); - NS_IF_ADDREF(mPseudoClass); + NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList); + NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); + NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); } -nsCSSSelector::~nsCSSSelector() -{ - NS_IF_RELEASE(mTag); - NS_IF_RELEASE(mID); - NS_IF_RELEASE(mClass); - NS_IF_RELEASE(mPseudoClass); +nsCSSSelector::~nsCSSSelector(void) +{ + Reset(); } nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) { NS_IF_RELEASE(mTag); NS_IF_RELEASE(mID); - NS_IF_RELEASE(mClass); - NS_IF_RELEASE(mPseudoClass); + NS_IF_DELETE(mClassList); + NS_IF_DELETE(mPseudoClassList); + NS_IF_DELETE(mAttrList); + + mNameSpace = aCopy.mNameSpace; mTag = aCopy.mTag; mID = aCopy.mID; - mClass = aCopy.mClass; - mPseudoClass = aCopy.mPseudoClass; + NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList); + NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); + NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); + mOperator = aCopy.mOperator; + NS_IF_ADDREF(mTag); NS_IF_ADDREF(mID); - NS_IF_ADDREF(mClass); - NS_IF_ADDREF(mPseudoClass); return *this; } PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const { + if (this == aOther) { + return PR_TRUE; + } if (nsnull != aOther) { - return (PRBool)((aOther->mTag == mTag) && (aOther->mID == mID) && - (aOther->mClass == mClass) && (aOther->mPseudoClass == mPseudoClass)); + if ((aOther->mNameSpace == mNameSpace) && + (aOther->mTag == mTag) && + (aOther->mID == mID) && + (aOther->mOperator == mOperator)) { + if (nsnull != mClassList) { + if (PR_FALSE == mClassList->Equals(aOther->mClassList)) { + return PR_FALSE; + } + } + else { + if (nsnull != aOther->mClassList) { + return PR_FALSE; + } + } + if (nsnull != mPseudoClassList) { + if (PR_FALSE == mPseudoClassList->Equals(aOther->mPseudoClassList)) { + return PR_FALSE; + } + } + else { + if (nsnull != aOther->mPseudoClassList) { + return PR_FALSE; + } + } + if (nsnull != mAttrList) { + if (PR_FALSE == mAttrList->Equals(aOther->mAttrList)) { + return PR_FALSE; + } + } + else { + if (nsnull != aOther->mAttrList) { + return PR_FALSE; + } + } + return PR_TRUE; + } } return PR_FALSE; } -void nsCSSSelector::Set(const nsString& aTag, const nsString& aID, - const nsString& aClass, const nsString& aPseudoClass) +void nsCSSSelector::Reset(void) { - nsAutoString buffer; + mNameSpace = kNameSpaceID_Unknown; NS_IF_RELEASE(mTag); NS_IF_RELEASE(mID); - NS_IF_RELEASE(mClass); - NS_IF_RELEASE(mPseudoClass); + NS_IF_DELETE(mClassList); + NS_IF_DELETE(mPseudoClassList); + NS_IF_DELETE(mAttrList); +} + +void nsCSSSelector::SetNameSpace(PRInt32 aNameSpace) +{ + mNameSpace = aNameSpace; +} + +void nsCSSSelector::SetTag(const nsString& aTag) +{ + NS_IF_RELEASE(mTag); if (0 < aTag.Length()) { - aTag.ToUpperCase(buffer); // XXX is this correct? what about class? - mTag = NS_NewAtom(buffer); + mTag = NS_NewAtom(aTag); } +} + +void nsCSSSelector::SetID(const nsString& aID) +{ + NS_IF_RELEASE(mID); if (0 < aID.Length()) { mID = NS_NewAtom(aID); } +} + +void nsCSSSelector::AddClass(const nsString& aClass) +{ if (0 < aClass.Length()) { - mClass = NS_NewAtom(aClass); - } - if (0 < aPseudoClass.Length()) { - aPseudoClass.ToUpperCase(buffer); - mPseudoClass = NS_NewAtom(buffer); - if (nsnull == mTag) { - mTag = nsHTMLAtoms::a; - NS_ADDREF(mTag); + nsAtomList** list = &mClassList; + while (nsnull != *list) { + list = &((*list)->mNext); } + *list = new nsAtomList(aClass); } } +void nsCSSSelector::AddPseudoClass(const nsString& aPseudoClass) +{ + if (0 < aPseudoClass.Length()) { + nsAtomList** list = &mPseudoClassList; + while (nsnull != *list) { + list = &((*list)->mNext); + } + *list = new nsAtomList(aPseudoClass); + } +} + +void nsCSSSelector::AddPseudoClass(nsIAtom* aPseudoClass) +{ + if (nsnull != aPseudoClass) { + nsAtomList** list = &mPseudoClassList; + while (nsnull != *list) { + list = &((*list)->mNext); + } + *list = new nsAtomList(aPseudoClass); + } +} + +void nsCSSSelector::AddAttribute(const nsString& aAttr) +{ + if (0 < aAttr.Length()) { + nsAttrSelector** list = &mAttrList; + while (nsnull != *list) { + list = &((*list)->mNext); + } + *list = new nsAttrSelector(aAttr); + } +} + +void nsCSSSelector::AddAttribute(const nsString& aAttr, PRUint8 aFunc, const nsString& aValue) +{ + if (0 < aAttr.Length()) { + nsAttrSelector** list = &mAttrList; + while (nsnull != *list) { + list = &((*list)->mNext); + } + *list = new nsAttrSelector(aAttr, aFunc, aValue); + } +} + +void nsCSSSelector::SetOperator(PRUnichar aOperator) +{ + mOperator = aOperator; +} + +PRInt32 nsCSSSelector::CalcWeight(void) const +{ + PRInt32 weight = 0; + + if (nsnull != mTag) { + weight += 0x000001; + } + if (nsnull != mID) { + weight += 0x010000; + } + nsAtomList* list = mClassList; + while (nsnull != list) { + weight += 0x000100; + list = list->mNext; + } + list = mPseudoClassList; + while (nsnull != list) { + weight += 0x000100; + list = list->mNext; + } + nsAttrSelector* attr = mAttrList; + while (nsnull != attr) { + weight += 0x000100; + attr = attr->mNext; + } + if (nsnull != mNext) { + weight += mNext->CalcWeight(); + } + return weight; +} + // -- CSSImportantRule ------------------------------- static nscoord CalcLength(const nsCSSValue& aValue, const nsStyleFont* aFont, @@ -365,6 +601,8 @@ public: virtual nsCSSSelector* FirstSelector(void); virtual void AddSelector(const nsCSSSelector& aSelector); virtual void DeleteSelector(nsCSSSelector* aSelector); + virtual void SetSourceSelectorText(const nsString& aSelectorText); + virtual void GetSourceSelectorText(nsString& aSelectorText) const; virtual nsICSSDeclaration* GetDeclaration(void) const; virtual void SetDeclaration(nsICSSDeclaration* aDeclaration); @@ -410,6 +648,7 @@ protected: PRUint32 mRefCnt : 31; nsCSSSelector mSelector; + nsString mSelectorText; nsICSSDeclaration* mDeclaration; PRInt32 mWeight; CSSImportantRule* mImportantRule; @@ -463,7 +702,7 @@ static const PRInt32 kInstrument = 1075; #endif CSSStyleRuleImpl::CSSStyleRuleImpl(const nsCSSSelector& aSelector) - : mSelector(aSelector), mDeclaration(nsnull), + : mSelector(aSelector), mSelectorText(), mDeclaration(nsnull), mWeight(0), mImportantRule(nsnull) { NS_INIT_REFCNT(); @@ -628,16 +867,13 @@ nsCSSSelector* CSSStyleRuleImpl::FirstSelector(void) void CSSStyleRuleImpl::AddSelector(const nsCSSSelector& aSelector) { - if ((nsnull != aSelector.mTag) || (nsnull != aSelector.mID) || - (nsnull != aSelector.mClass) || (nsnull != aSelector.mPseudoClass)) { // skip empty selectors - nsCSSSelector* selector = new nsCSSSelector(aSelector); - nsCSSSelector* last = &mSelector; + nsCSSSelector* selector = new nsCSSSelector(aSelector); + nsCSSSelector* last = &mSelector; - while (nsnull != last->mNext) { - last = last->mNext; - } - last->mNext = selector; + while (nsnull != last->mNext) { + last = last->mNext; } + last->mNext = selector; } @@ -645,9 +881,15 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector) { if (nsnull != aSelector) { if (&mSelector == aSelector) { // handle first selector - mSelector = *aSelector; // assign value - mSelector.mNext = aSelector->mNext; - delete aSelector; + if (nsnull != mSelector.mNext) { + nsCSSSelector* nextOne = mSelector.mNext; + mSelector = *nextOne; // assign values + mSelector.mNext = nextOne->mNext; + delete nextOne; + } + else { + mSelector.Reset(); + } } else { nsCSSSelector* selector = &mSelector; @@ -664,6 +906,17 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector) } } +void CSSStyleRuleImpl::SetSourceSelectorText(const nsString& aSelectorText) +{ + mSelectorText = aSelectorText; +} + +void CSSStyleRuleImpl::GetSourceSelectorText(nsString& aSelectorText) const +{ + aSelectorText = mSelectorText; +} + + nsICSSDeclaration* CSSStyleRuleImpl::GetDeclaration(void) const { NS_IF_ADDREF(mDeclaration); @@ -1056,8 +1309,8 @@ void MapDeclarationInto(nsICSSDeclaration* aDeclaration, text->mTextDecoration = td; } else if (eCSSUnit_None == ourText->mDecoration.GetUnit()) { - font->mFont.decorations = NS_STYLE_TEXT_DECORATION_NONE; - font->mFixedFont.decorations = NS_STYLE_TEXT_DECORATION_NONE; + font->mFont.decorations = parentFont->mFont.decorations; + font->mFixedFont.decorations = parentFont->mFixedFont.decorations; text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE; } else if (eCSSUnit_Inherit == ourText->mDecoration.GetUnit()) { @@ -1695,6 +1948,11 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector) { nsAutoString buffer; + if (kNameSpaceID_None < aSelector->mNameSpace) { + buffer.Append(aSelector->mNameSpace, 10); + fputs(buffer, out); + fputs("\\:", out); + } if (nsnull != aSelector->mTag) { aSelector->mTag->ToString(buffer); fputs(buffer, out); @@ -1704,15 +1962,39 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector) fputs("#", out); fputs(buffer, out); } - if (nsnull != aSelector->mClass) { - aSelector->mClass->ToString(buffer); + nsAtomList* list = aSelector->mClassList; + while (nsnull != list) { + list->mAtom->ToString(buffer); fputs(".", out); fputs(buffer, out); + list = list->mNext; } - if (nsnull != aSelector->mPseudoClass) { - aSelector->mPseudoClass->ToString(buffer); + list = aSelector->mPseudoClassList; + while (nsnull != list) { + list->mAtom->ToString(buffer); fputs(":", out); fputs(buffer, out); + list = list->mNext; + } + nsAttrSelector* attr = aSelector->mAttrList; + while (nsnull != attr) { + fputs("[", out); + attr->mAttr->ToString(buffer); + fputs(buffer, out); + if (NS_ATTR_FUNC_SET != attr->mFunction) { + switch (attr->mFunction) { + case NS_ATTR_FUNC_EQUALS: fputs("=", out); break; + case NS_ATTR_FUNC_INCLUDES: fputs("~=", out); break; + case NS_ATTR_FUNC_DASHMATCH: fputs("|=", out); break; + } + fputs(attr->mValue, out); + } + fputs("]", out); + } + if (0 != aSelector->mOperator) { + buffer = aSelector->mOperator; + buffer.Append(" "); + fputs(buffer, out); } } @@ -1782,45 +2064,16 @@ CSSStyleRuleImpl::GetSheet(nsIDOMCSSStyleSheet** aSheet) NS_IMETHODIMP CSSStyleRuleImpl::GetSelectorText(nsString& aSelectorText) { - nsAutoString buffer; - nsAutoString thisSelector; - nsCSSSelector *selector = &mSelector; - - // XXX Ugh...they're in reverse order from the source. Sorry - // for the ugliness. - aSelectorText.SetLength(0); - while (nsnull != selector) { - thisSelector.SetLength(0); - if (nsnull != selector->mTag) { - selector->mTag->ToString(buffer); - thisSelector.Append(buffer); - } - if (nsnull != selector->mID) { - selector->mID->ToString(buffer); - thisSelector.Append("#"); - thisSelector.Append(buffer); - } - if (nsnull != selector->mClass) { - selector->mClass->ToString(buffer); - thisSelector.Append("."); - thisSelector.Append(buffer); - } - if (nsnull != selector->mPseudoClass) { - selector->mPseudoClass->ToString(buffer); - thisSelector.Append(":"); - thisSelector.Append(buffer); - } - aSelectorText.Insert(thisSelector, 0, thisSelector.Length()); - selector = selector->mNext; - } - + aSelectorText = mSelectorText; return NS_OK; } NS_IMETHODIMP CSSStyleRuleImpl::SetSelectorText(const nsString& aSelectorText) { - // XXX TBI + // XXX TBI - get a parser and re-parse the selectors, + // XXX then need to re-compute the cascade + mSelectorText = aSelectorText; return NS_OK; } diff --git a/mozilla/content/html/style/src/nsCSSStyleSheet.cpp b/mozilla/content/html/style/src/nsCSSStyleSheet.cpp index 2cc0bd32cb0..d3dbd968918 100644 --- a/mozilla/content/html/style/src/nsCSSStyleSheet.cpp +++ b/mozilla/content/html/style/src/nsCSSStyleSheet.cpp @@ -42,10 +42,12 @@ #include "nsIScriptObjectOwner.h" #include "nsIScriptGlobalObject.h" #include "nsICSSParser.h" +#include "nsCSSAtoms.h" +#include "nsINameSpaceManager.h" #include "prlog.h" //#define DEBUG_REFS -//#define DEBUG_RULES +#define DEBUG_RULES static NS_DEFINE_IID(kICSSStyleSheetIID, NS_ICSS_STYLE_SHEET_IID); static NS_DEFINE_IID(kIStyleSheetIID, NS_ISTYLE_SHEET_IID); @@ -120,8 +122,8 @@ struct RuleValue { } ~RuleValue(void) { - if (nsnull != mNext) { - delete mNext; + if ((nsnull != mNext) && (nsnull != mNext->mNext)) { + delete mNext; // don't delete that last in the chain, it's shared } } @@ -142,7 +144,7 @@ public: RuleHash(void); ~RuleHash(void); void AppendRule(nsICSSStyleRule* aRule); - void EnumerateAllRules(nsIAtom* aTag, nsIAtom* aID, nsIAtom* aClass, + void EnumerateAllRules(nsIAtom* aTag, nsIAtom* aID, const nsVoidArray& aClassList, RuleEnumFunc aFunc, void* aData); void EnumerateTagRules(nsIAtom* aTag, RuleEnumFunc aFunc, void* aData); @@ -154,11 +156,17 @@ protected: nsHashtable mTagTable; nsHashtable mIdTable; nsHashtable mClassTable; + + RuleValue** mEnumList; + PRInt32 mEnumListSize; + RuleValue mEndValue; }; RuleHash::RuleHash(void) : mRuleCount(0), - mTagTable(), mIdTable(), mClassTable() + mTagTable(), mIdTable(), mClassTable(), + mEnumList(nsnull), mEnumListSize(0), + mEndValue(nsnull, -1) { } @@ -173,23 +181,31 @@ RuleHash::~RuleHash(void) mTagTable.Enumerate(DeleteValue); mIdTable.Enumerate(DeleteValue); mClassTable.Enumerate(DeleteValue); + if (nsnull != mEnumList) { + delete [] mEnumList; + } } void RuleHash::AppendRuleToTable(nsHashtable& aTable, nsIAtom* aAtom, nsICSSStyleRule* aRule) { + NS_ASSERTION(nsnull != aAtom, "null hash key"); + RuleKey key(aAtom); RuleValue* value = (RuleValue*)aTable.Get(&key); if (nsnull == value) { value = new RuleValue(aRule, mRuleCount++); aTable.Put(&key, value); + value->mNext = &mEndValue; } else { - while (nsnull != value->mNext) { + while (&mEndValue != value->mNext) { value = value->mNext; } value->mNext = new RuleValue(aRule, mRuleCount++); + value->mNext->mNext = &mEndValue; } + mEndValue.mIndex = mRuleCount; } void RuleHash::AppendRule(nsICSSStyleRule* aRule) @@ -198,65 +214,128 @@ void RuleHash::AppendRule(nsICSSStyleRule* aRule) if (nsnull != selector->mID) { AppendRuleToTable(mIdTable, selector->mID, aRule); } - else if (nsnull != selector->mClass) { - AppendRuleToTable(mClassTable, selector->mClass, aRule); + else if (nsnull != selector->mClassList) { + AppendRuleToTable(mClassTable, selector->mClassList->mAtom, aRule); } else if (nsnull != selector->mTag) { AppendRuleToTable(mTagTable, selector->mTag, aRule); } + else { // universal tag selector + AppendRuleToTable(mTagTable, nsCSSAtoms::universalSelector, aRule); + } } -void RuleHash::EnumerateAllRules(nsIAtom* aTag, nsIAtom* aID, nsIAtom* aClass, +void RuleHash::EnumerateAllRules(nsIAtom* aTag, nsIAtom* aID, const nsVoidArray& aClassList, RuleEnumFunc aFunc, void* aData) { - RuleValue* tagValue = nsnull; - RuleValue* idValue = nsnull; - RuleValue* classValue = nsnull; - + PRInt32 classCount = aClassList.Count(); + PRInt32 testCount = classCount + 1; // + 1 for universal selector if (nsnull != aTag) { - RuleKey tagKey(aTag); - tagValue = (RuleValue*)mTagTable.Get(&tagKey); + testCount++; } if (nsnull != aID) { - RuleKey idKey(aID); - idValue = (RuleValue*)mIdTable.Get(&idKey); - } - if (nsnull != aClass) { - RuleKey classKey(aClass); - classValue = (RuleValue*)mClassTable.Get(&classKey); + testCount++; } - PRInt32 tagIndex = ((nsnull != tagValue) ? tagValue->mIndex : mRuleCount); - PRInt32 idIndex = ((nsnull != idValue) ? idValue->mIndex : mRuleCount); - PRInt32 classIndex = ((nsnull != classValue) ? classValue->mIndex : mRuleCount); + if (mEnumListSize < testCount) { + delete [] mEnumList; + mEnumList = new RuleValue*[testCount]; + mEnumListSize = testCount; + } - while ((nsnull != tagValue) || (nsnull != idValue) || (nsnull != classValue)) { - if ((tagIndex < idIndex) && (tagIndex < classIndex)) { - (*aFunc)(tagValue->mRule, aData); - tagValue = tagValue->mNext; - tagIndex = ((nsnull != tagValue) ? tagValue->mIndex : mRuleCount); + PRInt32 index; + PRInt32 valueCount = 0; + + { // universal tag rules + RuleValue* value = (RuleValue*)mTagTable.Get(&RuleKey(nsCSSAtoms::universalSelector)); + if (nsnull != value) { + mEnumList[valueCount++] = value; } - else if (idIndex < classIndex) { - (*aFunc)(idValue->mRule, aData); - idValue = idValue->mNext; - idIndex = ((nsnull != idValue) ? idValue->mIndex : mRuleCount); + } + if (nsnull != aTag) { + RuleValue* value = (RuleValue*)mTagTable.Get(&RuleKey(aTag)); + if (nsnull != value) { + mEnumList[valueCount++] = value; } - else { - (*aFunc)(classValue->mRule, aData); - classValue = classValue->mNext; - classIndex = ((nsnull != classValue) ? classValue->mIndex : mRuleCount); + } + if (nsnull != aID) { + RuleValue* value = (RuleValue*)mIdTable.Get(&RuleKey(aID)); + if (nsnull != value) { + mEnumList[valueCount++] = value; } } + for (index = 0; index < classCount; index++) { + nsIAtom* classAtom = (nsIAtom*)aClassList.ElementAt(index); + RuleValue* value = (RuleValue*)mClassTable.Get(&RuleKey(classAtom)); + if (nsnull != value) { + mEnumList[valueCount++] = value; + } + } + NS_ASSERTION(valueCount <= testCount, "values exceeded list size"); + + if (1 < valueCount) { + PRInt32 ruleIndex = mRuleCount; + PRInt32 valueIndex = -1; + + for (index = 0; index < valueCount; index++) { + if (mEnumList[index]->mIndex < ruleIndex) { + ruleIndex = mEnumList[index]->mIndex; + valueIndex = index; + } + } + do { + (*aFunc)(mEnumList[valueIndex]->mRule, aData); + mEnumList[valueIndex] = mEnumList[valueIndex]->mNext; + ruleIndex = mEnumList[valueIndex]->mIndex; + for (index = 0; index < valueCount; index++) { + if (mEnumList[index]->mIndex < ruleIndex) { + ruleIndex = mEnumList[index]->mIndex; + valueIndex = index; + } + } + } while (ruleIndex < mRuleCount); + } + else if (0 < valueCount) { // fast loop over single value + RuleValue* value = mEnumList[0]; + do { + (*aFunc)(value->mRule, aData); + value = value->mNext; + } while (&mEndValue != value); + } } void RuleHash::EnumerateTagRules(nsIAtom* aTag, RuleEnumFunc aFunc, void* aData) { - RuleKey tagKey(aTag); - RuleValue* value = (RuleValue*)mTagTable.Get(&tagKey); + RuleValue* tagValue = (RuleValue*)mTagTable.Get(&RuleKey(aTag)); + RuleValue* uniValue = (RuleValue*)mTagTable.Get(&RuleKey(nsCSSAtoms::universalSelector)); - while (nsnull != value) { - (*aFunc)(value->mRule, aData); - value = value->mNext; + if (nsnull == tagValue) { + if (nsnull != uniValue) { + do { + (*aFunc)(uniValue->mRule, aData); + uniValue = uniValue->mNext; + } while (&mEndValue != uniValue); + } + } + else { + if (nsnull == uniValue) { + do { + (*aFunc)(tagValue->mRule, aData); + tagValue = tagValue->mNext; + } while (&mEndValue != tagValue); + } + else { + do { + if (tagValue->mIndex < uniValue->mIndex) { + (*aFunc)(tagValue->mRule, aData); + tagValue = tagValue->mNext; + } + else { + (*aFunc)(uniValue->mRule, aData); + uniValue = uniValue->mNext; + } + } while ((&mEndValue != tagValue) || (&mEndValue != uniValue)); + } } } @@ -691,6 +770,8 @@ CSSStyleSheetImpl::CSSStyleSheetImpl() mRuleHash(nsnull) { NS_INIT_REFCNT(); + nsCSSAtoms::AddrefAtoms(); + mParent = nsnull; mRuleCollection = nsnull; mImportsCollection = nsnull; @@ -744,6 +825,7 @@ CSSStyleSheetImpl::~CSSStyleSheetImpl() // XXX The document reference is not reference counted and should // not be released. The document will let us know when it is going // away. + nsCSSAtoms::ReleaseAtoms(); } NS_IMPL_ADDREF(CSSStyleSheetImpl) @@ -801,76 +883,87 @@ static PRBool SelectorMatches(nsIPresContext* aPresContext, PRBool result = PR_FALSE; nsIAtom* contentTag; aContent->GetTag(contentTag); + PRInt32 nameSpaceID; + aContent->GetNameSpaceID(nameSpaceID); - if ((nsnull == aSelector->mTag) || (aSelector->mTag == contentTag)) { - if ((nsnull != aSelector->mClass) || (nsnull != aSelector->mID) || - (nsnull != aSelector->mPseudoClass)) { + if (((nsnull == aSelector->mTag) || (aSelector->mTag == contentTag)) && + ((kNameSpaceID_Unknown == aSelector->mNameSpace) || (nameSpaceID == aSelector->mNameSpace))) { + result = PR_TRUE; + // namespace/tag match + if (nsnull != aSelector->mAttrList) { // test for attribute match + } + if ((PR_TRUE == result) && + ((nsnull != aSelector->mID) || (nsnull != aSelector->mClassList))) { // test for ID & class match + result = PR_FALSE; nsIHTMLContent* htmlContent; - if (NS_OK == aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent)) { - nsIAtom* contentClass; - htmlContent->GetClass(contentClass); + if (NS_SUCCEEDED(aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent))) { nsIAtom* contentID; htmlContent->GetID(contentID); - if ((nsnull == aSelector->mClass) || (aSelector->mClass == contentClass)) { - if ((nsnull == aSelector->mID) || (aSelector->mID == contentID)) { - if ((contentTag == nsHTMLAtoms::a) && (nsnull != aSelector->mPseudoClass)) { - // test link state - nsILinkHandler* linkHandler; - - if ((NS_OK == aPresContext->GetLinkHandler(&linkHandler)) && - (nsnull != linkHandler)) { - nsAutoString base, href; // XXX base?? - nsresult attrState = htmlContent->GetAttribute("href", href); - - if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { - nsIURL* docURL = nsnull; - nsIDocument* doc = nsnull; - aContent->GetDocument(doc); - if (nsnull != doc) { - docURL = doc->GetDocumentURL(); - NS_RELEASE(doc); - } - - nsAutoString absURLSpec; - nsresult rv = NS_MakeAbsoluteURL(docURL, base, href, absURLSpec); - NS_IF_RELEASE(docURL); - - nsLinkState state; - if (NS_OK == linkHandler->GetLinkState(absURLSpec, state)) { - switch (state) { - case eLinkState_Unvisited: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::link); - break; - case eLinkState_Visited: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::visited); - break; - case eLinkState_OutOfDate: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::outOfDate); - break; - case eLinkState_Active: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::active); - break; - case eLinkState_Hover: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::hover); - break; - } - } - } - NS_RELEASE(linkHandler); - } - } - else { - result = PR_TRUE; - } + if ((nsnull == aSelector->mID) || (aSelector->mID == contentID)) { + nsIAtom* contentClass; + htmlContent->GetClass(contentClass); + // XXX only testing for frist class right now + if ((nsnull == aSelector->mClassList) || (aSelector->mClassList->mAtom == contentClass)) { + result = PR_TRUE; } + NS_IF_RELEASE(contentClass); } NS_RELEASE(htmlContent); - NS_IF_RELEASE(contentClass); NS_IF_RELEASE(contentID); } } - else { - result = PR_TRUE; + if ((PR_TRUE == result) && + (nsnull != aSelector->mPseudoClassList)) { // test for pseudo class match + result = PR_FALSE; + // XXX only testing for anchor pseudo classes right now + // XXX only testing first pseudo class right now + if ((contentTag == nsHTMLAtoms::a) && (nsnull != aSelector->mPseudoClassList)) { + // test link state + nsILinkHandler* linkHandler; + + if ((NS_OK == aPresContext->GetLinkHandler(&linkHandler)) && + (nsnull != linkHandler)) { + nsAutoString base, href; // XXX base?? + nsresult attrState = aContent->GetAttribute("href", href); + + if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { + nsIURL* docURL = nsnull; + nsIDocument* doc = nsnull; + aContent->GetDocument(doc); + if (nsnull != doc) { + docURL = doc->GetDocumentURL(); + NS_RELEASE(doc); + } + + nsAutoString absURLSpec; + nsresult rv = NS_MakeAbsoluteURL(docURL, base, href, absURLSpec); + NS_IF_RELEASE(docURL); + + nsIAtom* pseudo = aSelector->mPseudoClassList->mAtom; + nsLinkState state; + if (NS_OK == linkHandler->GetLinkState(absURLSpec, state)) { + switch (state) { + case eLinkState_Unvisited: + result = PRBool (pseudo == nsCSSAtoms::linkPseudo); + break; + case eLinkState_Visited: + result = PRBool (pseudo == nsCSSAtoms::visitedPseudo); + break; + case eLinkState_OutOfDate: + result = PRBool (pseudo == nsCSSAtoms::outOfDatePseudo); + break; + case eLinkState_Active: + result = PRBool (pseudo == nsCSSAtoms::activePseudo); + break; + case eLinkState_Hover: + result = PRBool (pseudo == nsCSSAtoms::hoverPseudo); + break; + } + } + } + NS_RELEASE(linkHandler); + } + } } } NS_IF_RELEASE(contentTag); @@ -899,6 +992,8 @@ static void ContentEnumFunc(nsICSSStyleRule* aRule, void* aData) { ContentEnumData* data = (ContentEnumData*)aData; + // XXX not dealing with selector functions... + nsCSSSelector* selector = aRule->FirstSelector(); if (SelectorMatches(data->mPresContext, selector, data->mContent)) { selector = selector->mNext; @@ -995,7 +1090,13 @@ PRInt32 CSSStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext, NS_RELEASE(htmlContent); } - mRuleHash->EnumerateAllRules(tagAtom, idAtom, classAtom, ContentEnumFunc, &data); + nsVoidArray classArray; // XXX need to recycle this guy + + if (nsnull != classAtom) { + classArray.AppendElement(classAtom); + } + // XXX need to handle multiple classes + mRuleHash->EnumerateAllRules(tagAtom, idAtom, classArray, ContentEnumFunc, &data); matchCount += data.mCount; #ifdef DEBUG_RULES @@ -1005,7 +1106,7 @@ PRInt32 CSSStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext, NS_NewISupportsArray(&list2); data.mResults = list1; - mRuleHash->EnumerateAllRules(tagAtom, idAtom, classAtom, ContentEnumFunc, &data); + mRuleHash->EnumerateAllRules(tagAtom, idAtom, classArray, ContentEnumFunc, &data); data.mResults = list2; mWeightedRules->EnumerateBackwards(ContentEnumWrap, &data); NS_ASSERTION(list1->Equals(list2), "lists not equal"); diff --git a/mozilla/layout/html/style/src/nsCSSParser.cpp b/mozilla/layout/html/style/src/nsCSSParser.cpp index e0a4aebf2c7..2237f531f94 100644 --- a/mozilla/layout/html/style/src/nsCSSParser.cpp +++ b/mozilla/layout/html/style/src/nsCSSParser.cpp @@ -31,6 +31,9 @@ #include "nsVoidArray.h" #include "nsColor.h" #include "nsStyleConsts.h" +#include "nsCSSAtoms.h" +#include "nsINameSpaceManager.h" +#include "nsINameSpace.h" // XXX TODO: // - rework aErrorCode stuff: switch over to nsresult @@ -46,142 +49,52 @@ static NS_DEFINE_IID(kCSSTextSID, NS_CSS_TEXT_SID); #define KEYWORD_BUFFER_SIZE 50 // big enough for any keyword -struct Selector { - nsAutoString mTag; // weight 1 - nsAutoString mID; // weight 100 - nsAutoString mClass; // weight 10 - nsAutoString mPseudoClass; // weight 10 (== class) - nsAutoString mPseudoElement; // weight 10 (== class) ?? - - PRUint32 mMask; // which fields have values - - Selector(); - ~Selector(); - - PRInt32 Weight() const; -#ifdef NS_DEBUG - void Dump() const; -#endif -}; - -#define SELECTOR_TAG 0x01 -#define SELECTOR_ID 0x02 -#define SELECTOR_CLASS 0x04 -#define SELECTOR_PSEUDO_CLASS 0x08 -#define SELECTOR_PSEUDO_ELEMENT 0x10 - -#define SELECTOR_WEIGHT_BASE 10 - -Selector::Selector() -{ - mMask = 0; -} - -Selector::~Selector() -{ -} - -PRInt32 Selector::Weight() const -{ - return (((0 != (SELECTOR_TAG & mMask)) ? 1 : 0) + - ((0 != (SELECTOR_ID & mMask)) ? (SELECTOR_WEIGHT_BASE * SELECTOR_WEIGHT_BASE) : 0) + - ((0 != ((SELECTOR_CLASS | SELECTOR_PSEUDO_CLASS | SELECTOR_PSEUDO_ELEMENT) & mMask)) ? SELECTOR_WEIGHT_BASE : 0)); -} - -#ifdef NS_DEBUG -void Selector::Dump() const -{ - PRBool needSpace = PR_FALSE; - if (mTag.Length() > 0) { - fputs(mTag, stdout); - needSpace = PR_TRUE; - } - if (mID.Length() > 0) { - if (needSpace) fputs(" ", stdout); - fputs("#", stdout); - fputs(mID, stdout); - needSpace = PR_TRUE; - } - if (mClass.Length() > 0) { - if (needSpace) fputs(" ", stdout); - fputs(".", stdout); - fputs(mClass, stdout); - needSpace = PR_TRUE; - } - if (mPseudoClass.Length() > 0) { - if (needSpace) fputs(" ", stdout); - fputs(":", stdout); - fputs(mPseudoClass, stdout); - needSpace = PR_TRUE; - } - if (mPseudoElement.Length() > 0) { - if (needSpace) fputs(" ", stdout); - fputs(":", stdout); - fputs(mPseudoElement, stdout); - needSpace = PR_TRUE; - } -} -#endif - // e.g. "P B, H1 B { ... }" has a selector list with two elements, // each of which has two selectors. struct SelectorList { - SelectorList* mNext; - nsVoidArray mSelectors; + SelectorList(void); + ~SelectorList(void); - SelectorList(); - - void Destroy(); - - void AddSelector(Selector* aSelector) { - mSelectors.AppendElement(aSelector); - } + void AddSelector(const nsCSSSelector& aSelector); #ifdef NS_DEBUG - void Dump(); + void Dump(void); #endif -private: - ~SelectorList(); + nsCSSSelector* mSelectors; + SelectorList* mNext; }; -SelectorList::SelectorList() +SelectorList::SelectorList(void) + : mSelectors(nsnull), + mNext(nsnull) { - mNext = nsnull; } SelectorList::~SelectorList() { - PRInt32 n = mSelectors.Count(); - for (PRInt32 i = 0; i < n; i++) { - Selector* sel = (Selector*) mSelectors.ElementAt(i); - delete sel; + nsCSSSelector* sel = mSelectors; + while (nsnull != sel) { + nsCSSSelector* dead = sel; + sel = sel->mNext; + delete dead; + } + if (nsnull != mNext) { + delete mNext; } } -void SelectorList::Destroy() -{ - SelectorList* list = this; - while (nsnull != list) { - SelectorList* next = list->mNext; - delete list; - list = next; - } +void SelectorList::AddSelector(const nsCSSSelector& aSelector) +{ // prepend to list + nsCSSSelector* newSel = new nsCSSSelector(aSelector); + newSel->mNext = mSelectors; + mSelectors = newSel; } + #ifdef NS_DEBUG void SelectorList::Dump() { - PRInt32 n = mSelectors.Count(); - for (PRInt32 i = 0; i < n; i++) { - Selector* sel = (Selector*) mSelectors.ElementAt(i); - sel->Dump(); - fputs(" ", stdout); - } - if (mNext) { - fputs(", ", stdout); - mNext->Dump(); - } } #endif @@ -200,6 +113,8 @@ public: NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet); + NS_IMETHOD SetCaseSensative(PRBool aCaseSensative); + NS_IMETHOD Parse(nsIUnicharInputStream* aInput, nsIURL* aInputURL, nsICSSStyleSheet*& aResult); @@ -231,9 +146,9 @@ protected: PRBool GatherMedia(PRInt32& aErrorCode, nsString& aMedia); PRBool ProcessImport(PRInt32& aErrorCode, const nsString& aURLSpec, const nsString& aMedia); - PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList* aListHead); - PRBool ParseSelectorList(PRInt32& aErrorCode, SelectorList* aListHead); - PRBool ParseSelector(PRInt32& aErrorCode, Selector* aSelectorResult); + PRBool ParseSelectorList(PRInt32& aErrorCode, SelectorList*& aListHead); + PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList*& aListHead); + PRBool ParseSelector(PRInt32& aErrorCode, nsCSSSelector& aSelectorResult); nsICSSDeclaration* ParseDeclarationBlock(PRInt32& aErrorCode, PRBool aCheckForBraces); PRBool ParseDeclaration(PRInt32& aErrorCode, @@ -317,6 +232,7 @@ protected: PRBool mInHead; PRBool mNavQuirkMode; + PRBool mCaseSensative; }; NS_HTML nsresult @@ -334,19 +250,23 @@ NS_NewCSSParser(nsICSSParser** aInstancePtrResult) CSSParserImpl::CSSParserImpl() { NS_INIT_REFCNT(); + nsCSSAtoms::AddrefAtoms(); mScanner = nsnull; mSheet = nsnull; mHavePushBack = PR_FALSE; mNavQuirkMode = PR_TRUE; + mCaseSensative = PR_FALSE; } CSSParserImpl::CSSParserImpl(nsICSSStyleSheet* aSheet) { NS_INIT_REFCNT(); + nsCSSAtoms::AddrefAtoms(); mScanner = nsnull; mSheet = aSheet; NS_ADDREF(aSheet); mHavePushBack = PR_FALSE; mNavQuirkMode = PR_TRUE; + mCaseSensative = PR_FALSE; } NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID) @@ -354,16 +274,17 @@ NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID) CSSParserImpl::~CSSParserImpl() { NS_IF_RELEASE(mSheet); + nsCSSAtoms::ReleaseAtoms(); } -NS_METHOD +NS_IMETHODIMP CSSParserImpl::GetInfoMask(PRUint32& aResult) { aResult = NS_CSS_GETINFO_CSS1 | NS_CSS_GETINFO_CSSP; return NS_OK; } -NS_METHOD +NS_IMETHODIMP CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet) { NS_PRECONDITION(nsnull != aSheet, "null ptr"); @@ -381,7 +302,15 @@ CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet) return NS_OK; } -NS_METHOD +NS_IMETHODIMP +CSSParserImpl::SetCaseSensative(PRBool aCaseSensative) +{ + mCaseSensative = aCaseSensative; + return NS_OK; +} + + +NS_IMETHODIMP CSSParserImpl::Parse(nsIUnicharInputStream* aInput, nsIURL* aInputURL, nsICSSStyleSheet*& aResult) @@ -426,7 +355,7 @@ CSSParserImpl::Parse(nsIUnicharInputStream* aInput, return NS_OK; } -NS_METHOD +NS_IMETHODIMP CSSParserImpl::ParseDeclarations(const nsString& aDeclaration, nsIURL* aBaseURL, nsIStyleRule*& aResult) @@ -471,7 +400,7 @@ CSSParserImpl::ParseDeclarations(const nsString& aDeclaration, return NS_OK; } -NS_METHOD +NS_IMETHODIMP CSSParserImpl::ParseAndAppendDeclaration(const nsString& aBuffer, nsIURL* aBaseURL, nsICSSDeclaration* aDeclaration, @@ -925,18 +854,18 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode) nsCSSToken* tk = &mToken; // First get the list of selectors for the rule - SelectorList *slist = new SelectorList(); - if (!ParseSelectorList(aErrorCode, slist)) { + SelectorList* slist = nsnull; + if (! ParseSelectorList(aErrorCode, slist)) { SkipRuleSet(aErrorCode); - slist->Destroy(); return PR_FALSE; } + NS_ASSERTION(nsnull != slist, "null selector list"); // Next parse the declaration block nsICSSDeclaration* declaration = ParseDeclarationBlock(aErrorCode, PR_TRUE); if (nsnull == declaration) { // XXX skip something here - slist->Destroy(); + delete slist; return PR_FALSE; } @@ -950,34 +879,16 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode) // Translate the selector list and declaration block into style data SelectorList* list = slist; - nsCSSSelector selector; - nsICSSStyleRule* rule = nsnull; while (nsnull != list) { - PRInt32 selIndex = list->mSelectors.Count(); - - Selector* sel = (Selector*)list->mSelectors[--selIndex]; - if (0 < sel->mPseudoElement.Length()) { // only pseudo elements at end selector - nsString nullStr; - selector.Set(sel->mPseudoElement, nullStr, nullStr, nullStr); - NS_NewCSSStyleRule(&rule, selector); - } - selector.Set(sel->mTag, sel->mID, sel->mClass, sel->mPseudoClass); - PRInt32 weight = sel->Weight(); - - if (nsnull == rule) { - NS_NewCSSStyleRule(&rule, selector); - } - else { - rule->AddSelector(selector); - } + PRInt32 weight = list->mSelectors->CalcWeight(); + nsICSSStyleRule* rule = nsnull; + NS_NewCSSStyleRule(&rule, *(list->mSelectors)); if (nsnull != rule) { - while (--selIndex >= 0) { - Selector* sel = (Selector*)list->mSelectors[selIndex]; - selector.Set(sel->mTag, sel->mID, sel->mClass, sel->mPseudoClass); - - rule->AddSelector(selector); - weight += sel->Weight(); + if (nsnull != list->mSelectors->mNext) { // hand off other selectors to new rule + nsCSSSelector* ruleFirst = rule->FirstSelector(); + ruleFirst->mNext = list->mSelectors->mNext; + list->mSelectors->mNext = nsnull; } rule->SetDeclaration(declaration); rule->SetWeight(weight); @@ -990,219 +901,342 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode) } // Release temporary storage - slist->Destroy(); + delete slist; NS_RELEASE(declaration); return PR_TRUE; } PRBool CSSParserImpl::ParseSelectorList(PRInt32& aErrorCode, - SelectorList* aListHead) + SelectorList*& aListHead) { - if (!ParseSelectorGroup(aErrorCode, aListHead)) { + SelectorList* list = nsnull; + if (! ParseSelectorGroup(aErrorCode, list)) { // must have at least one selector group + aListHead = nsnull; return PR_FALSE; } + NS_ASSERTION(nsnull != list, "no selector list"); + aListHead = list; // After that there must either be a "," or a "{" nsCSSToken* tk = &mToken; for (;;) { - if (!GetToken(aErrorCode, PR_TRUE)) { - return PR_FALSE; + if (! GetToken(aErrorCode, PR_TRUE)) { + break; } if (eCSSToken_Symbol != tk->mType) { UngetToken(); - return PR_FALSE; + break; } if (',' == tk->mSymbol) { + SelectorList* newList = nsnull; // Another selector group must follow - SelectorList* newList = new SelectorList(); - if (!ParseSelectorGroup(aErrorCode, newList)) { - newList->Destroy(); - return PR_FALSE; + if (! ParseSelectorGroup(aErrorCode, newList)) { + break; } // add new list to the end of the selector list - aListHead->mNext = newList; - aListHead = newList; + list->mNext = newList; + list = newList; continue; } else if ('{' == tk->mSymbol) { UngetToken(); - break; + return PR_TRUE; } else { UngetToken(); - return PR_FALSE; + break; } } - return PR_TRUE; + delete aListHead; + aListHead = nsnull; + return PR_FALSE; +} + +static PRBool IsPseudoClass(const nsIAtom* aAtom) +{ + return PRBool((nsCSSAtoms::activePseudo == aAtom) || + (nsCSSAtoms::firstChildPseudo == aAtom) || + (nsCSSAtoms::focusPseudo == aAtom) || + (nsCSSAtoms::hoverPseudo == aAtom) || + (nsCSSAtoms::langPseudo == aAtom) || + (nsCSSAtoms::linkPseudo == aAtom) || + (nsCSSAtoms::outOfDatePseudo == aAtom) || + (nsCSSAtoms::visitedPseudo == aAtom)); +} + +static PRBool IsSinglePseudoClass(const nsCSSSelector& aSelector) +{ + return PRBool((aSelector.mNameSpace == kNameSpaceID_Unknown) && + (aSelector.mTag == nsnull) && + (aSelector.mID == nsnull) && + (aSelector.mClassList == nsnull) && + (aSelector.mAttrList == nsnull) && + (aSelector.mPseudoClassList != nsnull) && + (aSelector.mPseudoClassList->mNext == nsnull)); } PRBool CSSParserImpl::ParseSelectorGroup(PRInt32& aErrorCode, - SelectorList* aList) + SelectorList*& aList) { + SelectorList* list = nsnull; for (;;) { - Selector* sel = new Selector(); - if (!ParseSelector(aErrorCode, sel)) { - delete sel; + nsCSSSelector selector; + if (! ParseSelector(aErrorCode, selector)) { break; } - aList->AddSelector(sel); + if (nsnull == list) { + list = new SelectorList(); + } + list->AddSelector(selector); + nsCSSSelector* listSel = list->mSelectors; + // XXX parse combinator here + + // pull out pseudo elements here + nsAtomList* prevList = nsnull; + nsAtomList* pseudoClassList = listSel->mPseudoClassList; + while (nsnull != pseudoClassList) { + if (! IsPseudoClass(pseudoClassList->mAtom)) { + if (IsSinglePseudoClass(*listSel)) { // convert to pseudo element selector + nsIAtom* pseudoElement = pseudoClassList->mAtom; // steal ref count + pseudoClassList->mAtom = nsnull; + listSel->Reset(); + listSel->mTag = pseudoElement; + } + else { // append new pseudo element selector + selector.Reset(); + selector.mTag = pseudoClassList->mAtom; // steal ref count + list->AddSelector(selector); + pseudoClassList->mAtom = nsnull; + if (nsnull == prevList) { // delete list entry + listSel->mPseudoClassList = pseudoClassList->mNext; + } + else { + prevList->mNext = pseudoClassList->mNext; + } + pseudoClassList->mNext = nsnull; + delete pseudoClassList; + } + break; // only one pseudo element per selector + } + prevList = pseudoClassList; + pseudoClassList = pseudoClassList->mNext; + } } - return (PRBool) (aList->mSelectors.Count() > 0); + aList = list; + return PRBool(nsnull != aList); } -static PRBool IsPseudoClass(const nsString& aBuffer) -{ - return (aBuffer.EqualsIgnoreCase("link") || - aBuffer.EqualsIgnoreCase("visited") || - aBuffer.EqualsIgnoreCase("hover") || - aBuffer.EqualsIgnoreCase("out-of-date") || // our extension - aBuffer.EqualsIgnoreCase("active")); -} +#define SEL_MASK_NSPACE 0x01 +#define SEL_MASK_ELEM 0x02 +#define SEL_MASK_ID 0x04 +#define SEL_MASK_CLASS 0x08 +#define SEL_MASK_ATTRIB 0x10 +#define SEL_MASK_PCLASS 0x20 +#define SEL_MASK_PELEM 0x40 /** - * These are the 31 possible kinds of CSS1 style selectors: - * (but there are 50 ways to leave your lover) - * [*] means it can repeat - * + * This is the format for selectors: + * operator? [namespace \:]? element_name? [ ID | class | attrib | pseudo ]* */ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, - Selector* aSelectorResult) + nsCSSSelector& aSelector) { - PRUint32 mask = 0; - aSelectorResult->mTag.Truncate(0); - aSelectorResult->mClass.Truncate(0); - aSelectorResult->mID.Truncate(0); - aSelectorResult->mPseudoClass.Truncate(0); - aSelectorResult->mPseudoElement.Truncate(0); + PRInt32 dataMask = 0; + nsAutoString buffer; - nsCSSToken* tk = &mToken; - if (!GetToken(aErrorCode, PR_TRUE)) { + if (! GetToken(aErrorCode, PR_TRUE)) { return PR_FALSE; } - if (eCSSToken_Ident == tk->mType) { - // tag - mask |= SELECTOR_TAG; - aSelectorResult->mTag.Append(tk->mIdent); - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof is ok (here!) + if ((eCSSToken_Symbol == mToken.mType) && ('*' == mToken.mSymbol)) { // universal element selector + // don't set any tag in the selector + dataMask |= SEL_MASK_ELEM; + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) return PR_TRUE; } } - if (eCSSToken_ID == tk->mType) { - // #id - if ((0 < tk->mIdent.Length()) && (nsString::IsAlpha(tk->mIdent.CharAt(0)))) { // verify is legal ID - mask |= SELECTOR_ID; - aSelectorResult->mID.Append(tk->mIdent); - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof is ok (here!) - return PR_TRUE; + else if (eCSSToken_Ident == mToken.mType) { // element name + PRInt32 colon = mToken.mIdent.Find(':'); + if (-1 == colon) { // no namespace + if (mCaseSensative) { + aSelector.SetTag(mToken.mIdent); + } + else { + mToken.mIdent.ToUpperCase(buffer); + aSelector.SetTag(buffer); } } - else { - return PR_FALSE; + else { // pull out the namespace + nsAutoString nameSpace; + mToken.mIdent.Left(nameSpace, colon); + mToken.mIdent.Right(buffer, (mToken.mIdent.Length() - (colon + 1))); + if (! mCaseSensative) { + buffer.ToUpperCase(); + } + // XXX lookup namespace, set it + // deal with * namespace (== unknown) + aSelector.SetTag(buffer); } - } - if ((eCSSToken_Symbol == tk->mType) && ('.' == tk->mSymbol)) { - // .class - if (!GetToken(aErrorCode, PR_FALSE)) { - return PR_FALSE; - } - if (eCSSToken_Ident != tk->mType) { - // malformed selector - UngetToken(); - return PR_FALSE; - } - mask |= SELECTOR_CLASS; - aSelectorResult->mClass.Append(tk->mIdent); - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof is ok (here!) + dataMask |= SEL_MASK_ELEM; + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) return PR_TRUE; } } - if ((eCSSToken_Symbol == tk->mType) && (':' == tk->mSymbol)) { - // :pseudo - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof - return PR_FALSE; + for (;;) { + if (eCSSToken_ID == mToken.mType) { // #id + if ((0 == (dataMask & SEL_MASK_ID)) && // only one ID + (0 < mToken.mIdent.Length()) && + (nsString::IsAlpha(mToken.mIdent.CharAt(0)))) { // verify is legal ID + dataMask |= SEL_MASK_ID; + aSelector.SetID(mToken.mIdent); + } + else { + UngetToken(); + return PR_FALSE; + } } - if (eCSSToken_Ident != tk->mType) { - // malformed selector - UngetToken(); - return PR_FALSE; + else if ((eCSSToken_Symbol == mToken.mType) && ('.' == mToken.mSymbol)) { // .class + if (! GetToken(aErrorCode, PR_FALSE)) { // get ident + return PR_FALSE; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector (XXX what about leading digits?) + UngetToken(); + return PR_FALSE; + } + dataMask |= SEL_MASK_CLASS; + if (mCaseSensative) { + aSelector.AddClass(mToken.mIdent); + } + else { + mToken.mIdent.ToUpperCase(buffer); + aSelector.AddClass(buffer); + } } - if (IsPseudoClass(tk->mIdent)) { - mask |= SELECTOR_PSEUDO_CLASS; - aSelectorResult->mPseudoClass.Append(tk->mIdent); + else if ((eCSSToken_Symbol == mToken.mType) && (':' == mToken.mSymbol)) { // :pseudo + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof + return PR_FALSE; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector + UngetToken(); + return PR_FALSE; + } + buffer.Truncate(); + buffer.Append(':'); + buffer.Append(mToken.mIdent); + buffer.ToUpperCase(); + nsIAtom* pseudo = NS_NewAtom(buffer); + if (IsPseudoClass(pseudo)) { + // XXX parse lang pseudo class + dataMask |= SEL_MASK_PCLASS; + aSelector.AddPseudoClass(pseudo); + } + else { + if (0 == (dataMask & SEL_MASK_PELEM)) { + dataMask |= SEL_MASK_PELEM; + aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later + } + else { // multiple pseudo elements, not legal + UngetToken(); + NS_RELEASE(pseudo); + return PR_FALSE; + } + } + NS_RELEASE(pseudo); } - else { - mask |= SELECTOR_PSEUDO_ELEMENT; - aSelectorResult->mPseudoElement.Append(':'); // keep the colon - aSelectorResult->mPseudoElement.Append(tk->mIdent); + else if ((eCSSToken_Symbol == mToken.mType) && ('[' == mToken.mSymbol)) { // attribute + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector + UngetToken(); + return PR_FALSE; + } + nsAutoString attr(mToken.mType); + if (! mCaseSensative) { + attr.ToUpperCase(); + } + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if (eCSSToken_Symbol == mToken.mType) { + PRUint8 func; + if (']' == mToken.mSymbol) { + dataMask |= SEL_MASK_ATTRIB; + aSelector.AddAttribute(attr); + func = NS_ATTR_FUNC_SET; + } + else if ('=' == mToken.mSymbol) { + func = NS_ATTR_FUNC_EQUALS; + } + else if ('~' == mToken.mSymbol) { + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if ((eCSSToken_Symbol == mToken.mType) && ('=' == mToken.mSymbol)) { + func = NS_ATTR_FUNC_INCLUDES; + } + else { + UngetToken(); + return PR_FALSE; + } + } + else if ('|' == mToken.mSymbol) { + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if ((eCSSToken_Symbol == mToken.mType) && ('=' == mToken.mSymbol)) { + func = NS_ATTR_FUNC_DASHMATCH; + } + else { + UngetToken(); + return PR_FALSE; + } + } + else { + UngetToken(); // bad function + return PR_FALSE; + } + if (NS_ATTR_FUNC_SET != func) { // get value + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) { + nsAutoString value(mToken.mIdent); + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if ((eCSSToken_Symbol == mToken.mType) && (']' == mToken.mSymbol)) { + dataMask |= SEL_MASK_ATTRIB; + if (! mCaseSensative) { + value.ToUpperCase(); + } + aSelector.AddAttribute(attr, func, value); + } + else { + UngetToken(); + return PR_FALSE; + } + } + else { + UngetToken(); + return PR_FALSE; + } + } + } + else { + UngetToken(); // bad dog, no biscut! + return PR_FALSE; + } } - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof is ok (here!) + else { // not a selector token, we're done + break; + } + + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) return PR_TRUE; } } - if ((eCSSToken_Symbol == tk->mType) && (':' == tk->mSymbol)) { - // :pseudo - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof - return PR_FALSE; - } - if (eCSSToken_Ident != tk->mType) { - // malformed selector - UngetToken(); - return PR_FALSE; - } - if (! IsPseudoClass(tk->mIdent)) { - mask |= SELECTOR_PSEUDO_ELEMENT; - aSelectorResult->mPseudoElement.Truncate(0); - aSelectorResult->mPseudoElement.Append(':'); // keep the colon - aSelectorResult->mPseudoElement.Append(tk->mIdent); - } - tk = nsnull; - } - if (nsnull != tk) { - UngetToken(); - } - if (mask == 0) { - return PR_FALSE; - } - aSelectorResult->mMask = mask; - return PR_TRUE; + UngetToken(); + return PRBool(0 != dataMask); } nsICSSDeclaration* diff --git a/mozilla/layout/html/style/src/nsCSSStyleRule.cpp b/mozilla/layout/html/style/src/nsCSSStyleRule.cpp index e612fcfdce4..3678fa8e206 100644 --- a/mozilla/layout/html/style/src/nsCSSStyleRule.cpp +++ b/mozilla/layout/html/style/src/nsCSSStyleRule.cpp @@ -38,6 +38,7 @@ #include "nsIScriptGlobalObject.h" #include "nsIScriptObjectOwner.h" #include "nsDOMCSSDeclaration.h" +#include "nsINameSpaceManager.h" //#define DEBUG_REFS @@ -62,95 +63,330 @@ static NS_DEFINE_IID(kCSSTableSID, NS_CSS_TABLE_SID); // -- nsCSSSelector ------------------------------- -nsCSSSelector::nsCSSSelector() - : mTag(nsnull), mID(nsnull), mClass(nsnull), mPseudoClass(nsnull), +#define NS_IF_COPY(dest,source,type) \ + if (nsnull != source) dest = new type(*(source)) + +#define NS_IF_DELETE(ptr) \ + if (nsnull != ptr) { delete ptr; ptr = nsnull; } + +nsAtomList::nsAtomList(nsIAtom* aAtom) + : mAtom(aAtom), mNext(nsnull) { + NS_IF_ADDREF(mAtom); } -nsCSSSelector::nsCSSSelector(nsIAtom* aTag, nsIAtom* aID, nsIAtom* aClass, nsIAtom* aPseudoClass) - : mTag(aTag), mID(aID), mClass(aClass), mPseudoClass(aPseudoClass), +nsAtomList::nsAtomList(const nsString& aAtomValue) + : mAtom(nsnull), + mNext(nsnull) +{ + mAtom = NS_NewAtom(aAtomValue); +} + +nsAtomList::nsAtomList(const nsAtomList& aCopy) + : mAtom(aCopy.mAtom), + mNext(nsnull) +{ + NS_IF_ADDREF(mAtom); + NS_IF_COPY(mNext, aCopy.mNext, nsAtomList); +} + +nsAtomList::~nsAtomList(void) +{ + NS_IF_RELEASE(mAtom); + NS_IF_DELETE(mNext); +} + +PRBool nsAtomList::Equals(const nsAtomList* aOther) const +{ + if (this == aOther) { + return PR_TRUE; + } + if (nsnull != aOther) { + if (mAtom == aOther->mAtom) { + if (nsnull != mNext) { + return mNext->Equals(aOther->mNext); + } + return PRBool(nsnull == aOther->mNext); + } + } + return PR_FALSE; +} + +nsAttrSelector::nsAttrSelector(const nsString& aAttr) + : mAttr(nsnull), + mFunction(NS_ATTR_FUNC_SET), + mValue(), + mNext(nsnull) +{ + mAttr = NS_NewAtom(aAttr); +} + +nsAttrSelector::nsAttrSelector(const nsString& aAttr, PRUint8 aFunction, const nsString& aValue) + : mAttr(nsnull), + mFunction(aFunction), + mValue(aValue), + mNext(nsnull) +{ + mAttr = NS_NewAtom(aAttr); +} + +nsAttrSelector::nsAttrSelector(const nsAttrSelector& aCopy) + : mAttr(aCopy.mAttr), + mFunction(aCopy.mFunction), + mValue(aCopy.mValue), + mNext(nsnull) +{ + NS_IF_ADDREF(mAttr); + NS_IF_COPY(mNext, aCopy.mNext, nsAttrSelector); +} + +nsAttrSelector::~nsAttrSelector(void) +{ + NS_IF_RELEASE(mAttr); + NS_IF_DELETE(mNext); +} + +PRBool nsAttrSelector::Equals(const nsAttrSelector* aOther) const +{ + if (this == aOther) { + return PR_TRUE; + } + if (nsnull != aOther) { + if ((mAttr == aOther->mAttr) && + (mFunction == aOther->mFunction) && + mValue.Equals(aOther->mValue)) { + if (nsnull != mNext) { + return mNext->Equals(aOther->mNext); + } + return PRBool(nsnull == aOther->mNext); + } + } + return PR_FALSE; +} + +nsCSSSelector::nsCSSSelector(void) + : mNameSpace(kNameSpaceID_Unknown), mTag(nsnull), + mID(nsnull), + mClassList(nsnull), + mPseudoClassList(nsnull), + mAttrList(nsnull), + mOperator(0), mNext(nsnull) { - NS_IF_ADDREF(mTag); - NS_IF_ADDREF(mID); - NS_IF_ADDREF(mClass); - NS_IF_ADDREF(mPseudoClass); } nsCSSSelector::nsCSSSelector(const nsCSSSelector& aCopy) - : mTag(aCopy.mTag), mID(aCopy.mID), mClass(aCopy.mClass), mPseudoClass(aCopy.mPseudoClass), + : mNameSpace(aCopy.mNameSpace), mTag(aCopy.mTag), + mID(aCopy.mID), + mClassList(nsnull), + mPseudoClassList(nsnull), + mAttrList(nsnull), + mOperator(aCopy.mOperator), mNext(nsnull) -{ // implmented to support extension to CSS2 (when we have to copy the array) +{ NS_IF_ADDREF(mTag); NS_IF_ADDREF(mID); - NS_IF_ADDREF(mClass); - NS_IF_ADDREF(mPseudoClass); + NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList); + NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); + NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); } -nsCSSSelector::~nsCSSSelector() -{ - NS_IF_RELEASE(mTag); - NS_IF_RELEASE(mID); - NS_IF_RELEASE(mClass); - NS_IF_RELEASE(mPseudoClass); +nsCSSSelector::~nsCSSSelector(void) +{ + Reset(); } nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) { NS_IF_RELEASE(mTag); NS_IF_RELEASE(mID); - NS_IF_RELEASE(mClass); - NS_IF_RELEASE(mPseudoClass); + NS_IF_DELETE(mClassList); + NS_IF_DELETE(mPseudoClassList); + NS_IF_DELETE(mAttrList); + + mNameSpace = aCopy.mNameSpace; mTag = aCopy.mTag; mID = aCopy.mID; - mClass = aCopy.mClass; - mPseudoClass = aCopy.mPseudoClass; + NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList); + NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); + NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); + mOperator = aCopy.mOperator; + NS_IF_ADDREF(mTag); NS_IF_ADDREF(mID); - NS_IF_ADDREF(mClass); - NS_IF_ADDREF(mPseudoClass); return *this; } PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const { + if (this == aOther) { + return PR_TRUE; + } if (nsnull != aOther) { - return (PRBool)((aOther->mTag == mTag) && (aOther->mID == mID) && - (aOther->mClass == mClass) && (aOther->mPseudoClass == mPseudoClass)); + if ((aOther->mNameSpace == mNameSpace) && + (aOther->mTag == mTag) && + (aOther->mID == mID) && + (aOther->mOperator == mOperator)) { + if (nsnull != mClassList) { + if (PR_FALSE == mClassList->Equals(aOther->mClassList)) { + return PR_FALSE; + } + } + else { + if (nsnull != aOther->mClassList) { + return PR_FALSE; + } + } + if (nsnull != mPseudoClassList) { + if (PR_FALSE == mPseudoClassList->Equals(aOther->mPseudoClassList)) { + return PR_FALSE; + } + } + else { + if (nsnull != aOther->mPseudoClassList) { + return PR_FALSE; + } + } + if (nsnull != mAttrList) { + if (PR_FALSE == mAttrList->Equals(aOther->mAttrList)) { + return PR_FALSE; + } + } + else { + if (nsnull != aOther->mAttrList) { + return PR_FALSE; + } + } + return PR_TRUE; + } } return PR_FALSE; } -void nsCSSSelector::Set(const nsString& aTag, const nsString& aID, - const nsString& aClass, const nsString& aPseudoClass) +void nsCSSSelector::Reset(void) { - nsAutoString buffer; + mNameSpace = kNameSpaceID_Unknown; NS_IF_RELEASE(mTag); NS_IF_RELEASE(mID); - NS_IF_RELEASE(mClass); - NS_IF_RELEASE(mPseudoClass); + NS_IF_DELETE(mClassList); + NS_IF_DELETE(mPseudoClassList); + NS_IF_DELETE(mAttrList); +} + +void nsCSSSelector::SetNameSpace(PRInt32 aNameSpace) +{ + mNameSpace = aNameSpace; +} + +void nsCSSSelector::SetTag(const nsString& aTag) +{ + NS_IF_RELEASE(mTag); if (0 < aTag.Length()) { - aTag.ToUpperCase(buffer); // XXX is this correct? what about class? - mTag = NS_NewAtom(buffer); + mTag = NS_NewAtom(aTag); } +} + +void nsCSSSelector::SetID(const nsString& aID) +{ + NS_IF_RELEASE(mID); if (0 < aID.Length()) { mID = NS_NewAtom(aID); } +} + +void nsCSSSelector::AddClass(const nsString& aClass) +{ if (0 < aClass.Length()) { - mClass = NS_NewAtom(aClass); - } - if (0 < aPseudoClass.Length()) { - aPseudoClass.ToUpperCase(buffer); - mPseudoClass = NS_NewAtom(buffer); - if (nsnull == mTag) { - mTag = nsHTMLAtoms::a; - NS_ADDREF(mTag); + nsAtomList** list = &mClassList; + while (nsnull != *list) { + list = &((*list)->mNext); } + *list = new nsAtomList(aClass); } } +void nsCSSSelector::AddPseudoClass(const nsString& aPseudoClass) +{ + if (0 < aPseudoClass.Length()) { + nsAtomList** list = &mPseudoClassList; + while (nsnull != *list) { + list = &((*list)->mNext); + } + *list = new nsAtomList(aPseudoClass); + } +} + +void nsCSSSelector::AddPseudoClass(nsIAtom* aPseudoClass) +{ + if (nsnull != aPseudoClass) { + nsAtomList** list = &mPseudoClassList; + while (nsnull != *list) { + list = &((*list)->mNext); + } + *list = new nsAtomList(aPseudoClass); + } +} + +void nsCSSSelector::AddAttribute(const nsString& aAttr) +{ + if (0 < aAttr.Length()) { + nsAttrSelector** list = &mAttrList; + while (nsnull != *list) { + list = &((*list)->mNext); + } + *list = new nsAttrSelector(aAttr); + } +} + +void nsCSSSelector::AddAttribute(const nsString& aAttr, PRUint8 aFunc, const nsString& aValue) +{ + if (0 < aAttr.Length()) { + nsAttrSelector** list = &mAttrList; + while (nsnull != *list) { + list = &((*list)->mNext); + } + *list = new nsAttrSelector(aAttr, aFunc, aValue); + } +} + +void nsCSSSelector::SetOperator(PRUnichar aOperator) +{ + mOperator = aOperator; +} + +PRInt32 nsCSSSelector::CalcWeight(void) const +{ + PRInt32 weight = 0; + + if (nsnull != mTag) { + weight += 0x000001; + } + if (nsnull != mID) { + weight += 0x010000; + } + nsAtomList* list = mClassList; + while (nsnull != list) { + weight += 0x000100; + list = list->mNext; + } + list = mPseudoClassList; + while (nsnull != list) { + weight += 0x000100; + list = list->mNext; + } + nsAttrSelector* attr = mAttrList; + while (nsnull != attr) { + weight += 0x000100; + attr = attr->mNext; + } + if (nsnull != mNext) { + weight += mNext->CalcWeight(); + } + return weight; +} + // -- CSSImportantRule ------------------------------- static nscoord CalcLength(const nsCSSValue& aValue, const nsStyleFont* aFont, @@ -365,6 +601,8 @@ public: virtual nsCSSSelector* FirstSelector(void); virtual void AddSelector(const nsCSSSelector& aSelector); virtual void DeleteSelector(nsCSSSelector* aSelector); + virtual void SetSourceSelectorText(const nsString& aSelectorText); + virtual void GetSourceSelectorText(nsString& aSelectorText) const; virtual nsICSSDeclaration* GetDeclaration(void) const; virtual void SetDeclaration(nsICSSDeclaration* aDeclaration); @@ -410,6 +648,7 @@ protected: PRUint32 mRefCnt : 31; nsCSSSelector mSelector; + nsString mSelectorText; nsICSSDeclaration* mDeclaration; PRInt32 mWeight; CSSImportantRule* mImportantRule; @@ -463,7 +702,7 @@ static const PRInt32 kInstrument = 1075; #endif CSSStyleRuleImpl::CSSStyleRuleImpl(const nsCSSSelector& aSelector) - : mSelector(aSelector), mDeclaration(nsnull), + : mSelector(aSelector), mSelectorText(), mDeclaration(nsnull), mWeight(0), mImportantRule(nsnull) { NS_INIT_REFCNT(); @@ -628,16 +867,13 @@ nsCSSSelector* CSSStyleRuleImpl::FirstSelector(void) void CSSStyleRuleImpl::AddSelector(const nsCSSSelector& aSelector) { - if ((nsnull != aSelector.mTag) || (nsnull != aSelector.mID) || - (nsnull != aSelector.mClass) || (nsnull != aSelector.mPseudoClass)) { // skip empty selectors - nsCSSSelector* selector = new nsCSSSelector(aSelector); - nsCSSSelector* last = &mSelector; + nsCSSSelector* selector = new nsCSSSelector(aSelector); + nsCSSSelector* last = &mSelector; - while (nsnull != last->mNext) { - last = last->mNext; - } - last->mNext = selector; + while (nsnull != last->mNext) { + last = last->mNext; } + last->mNext = selector; } @@ -645,9 +881,15 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector) { if (nsnull != aSelector) { if (&mSelector == aSelector) { // handle first selector - mSelector = *aSelector; // assign value - mSelector.mNext = aSelector->mNext; - delete aSelector; + if (nsnull != mSelector.mNext) { + nsCSSSelector* nextOne = mSelector.mNext; + mSelector = *nextOne; // assign values + mSelector.mNext = nextOne->mNext; + delete nextOne; + } + else { + mSelector.Reset(); + } } else { nsCSSSelector* selector = &mSelector; @@ -664,6 +906,17 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector) } } +void CSSStyleRuleImpl::SetSourceSelectorText(const nsString& aSelectorText) +{ + mSelectorText = aSelectorText; +} + +void CSSStyleRuleImpl::GetSourceSelectorText(nsString& aSelectorText) const +{ + aSelectorText = mSelectorText; +} + + nsICSSDeclaration* CSSStyleRuleImpl::GetDeclaration(void) const { NS_IF_ADDREF(mDeclaration); @@ -1056,8 +1309,8 @@ void MapDeclarationInto(nsICSSDeclaration* aDeclaration, text->mTextDecoration = td; } else if (eCSSUnit_None == ourText->mDecoration.GetUnit()) { - font->mFont.decorations = NS_STYLE_TEXT_DECORATION_NONE; - font->mFixedFont.decorations = NS_STYLE_TEXT_DECORATION_NONE; + font->mFont.decorations = parentFont->mFont.decorations; + font->mFixedFont.decorations = parentFont->mFixedFont.decorations; text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE; } else if (eCSSUnit_Inherit == ourText->mDecoration.GetUnit()) { @@ -1695,6 +1948,11 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector) { nsAutoString buffer; + if (kNameSpaceID_None < aSelector->mNameSpace) { + buffer.Append(aSelector->mNameSpace, 10); + fputs(buffer, out); + fputs("\\:", out); + } if (nsnull != aSelector->mTag) { aSelector->mTag->ToString(buffer); fputs(buffer, out); @@ -1704,15 +1962,39 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector) fputs("#", out); fputs(buffer, out); } - if (nsnull != aSelector->mClass) { - aSelector->mClass->ToString(buffer); + nsAtomList* list = aSelector->mClassList; + while (nsnull != list) { + list->mAtom->ToString(buffer); fputs(".", out); fputs(buffer, out); + list = list->mNext; } - if (nsnull != aSelector->mPseudoClass) { - aSelector->mPseudoClass->ToString(buffer); + list = aSelector->mPseudoClassList; + while (nsnull != list) { + list->mAtom->ToString(buffer); fputs(":", out); fputs(buffer, out); + list = list->mNext; + } + nsAttrSelector* attr = aSelector->mAttrList; + while (nsnull != attr) { + fputs("[", out); + attr->mAttr->ToString(buffer); + fputs(buffer, out); + if (NS_ATTR_FUNC_SET != attr->mFunction) { + switch (attr->mFunction) { + case NS_ATTR_FUNC_EQUALS: fputs("=", out); break; + case NS_ATTR_FUNC_INCLUDES: fputs("~=", out); break; + case NS_ATTR_FUNC_DASHMATCH: fputs("|=", out); break; + } + fputs(attr->mValue, out); + } + fputs("]", out); + } + if (0 != aSelector->mOperator) { + buffer = aSelector->mOperator; + buffer.Append(" "); + fputs(buffer, out); } } @@ -1782,45 +2064,16 @@ CSSStyleRuleImpl::GetSheet(nsIDOMCSSStyleSheet** aSheet) NS_IMETHODIMP CSSStyleRuleImpl::GetSelectorText(nsString& aSelectorText) { - nsAutoString buffer; - nsAutoString thisSelector; - nsCSSSelector *selector = &mSelector; - - // XXX Ugh...they're in reverse order from the source. Sorry - // for the ugliness. - aSelectorText.SetLength(0); - while (nsnull != selector) { - thisSelector.SetLength(0); - if (nsnull != selector->mTag) { - selector->mTag->ToString(buffer); - thisSelector.Append(buffer); - } - if (nsnull != selector->mID) { - selector->mID->ToString(buffer); - thisSelector.Append("#"); - thisSelector.Append(buffer); - } - if (nsnull != selector->mClass) { - selector->mClass->ToString(buffer); - thisSelector.Append("."); - thisSelector.Append(buffer); - } - if (nsnull != selector->mPseudoClass) { - selector->mPseudoClass->ToString(buffer); - thisSelector.Append(":"); - thisSelector.Append(buffer); - } - aSelectorText.Insert(thisSelector, 0, thisSelector.Length()); - selector = selector->mNext; - } - + aSelectorText = mSelectorText; return NS_OK; } NS_IMETHODIMP CSSStyleRuleImpl::SetSelectorText(const nsString& aSelectorText) { - // XXX TBI + // XXX TBI - get a parser and re-parse the selectors, + // XXX then need to re-compute the cascade + mSelectorText = aSelectorText; return NS_OK; } diff --git a/mozilla/layout/html/style/src/nsCSSStyleSheet.cpp b/mozilla/layout/html/style/src/nsCSSStyleSheet.cpp index 2cc0bd32cb0..d3dbd968918 100644 --- a/mozilla/layout/html/style/src/nsCSSStyleSheet.cpp +++ b/mozilla/layout/html/style/src/nsCSSStyleSheet.cpp @@ -42,10 +42,12 @@ #include "nsIScriptObjectOwner.h" #include "nsIScriptGlobalObject.h" #include "nsICSSParser.h" +#include "nsCSSAtoms.h" +#include "nsINameSpaceManager.h" #include "prlog.h" //#define DEBUG_REFS -//#define DEBUG_RULES +#define DEBUG_RULES static NS_DEFINE_IID(kICSSStyleSheetIID, NS_ICSS_STYLE_SHEET_IID); static NS_DEFINE_IID(kIStyleSheetIID, NS_ISTYLE_SHEET_IID); @@ -120,8 +122,8 @@ struct RuleValue { } ~RuleValue(void) { - if (nsnull != mNext) { - delete mNext; + if ((nsnull != mNext) && (nsnull != mNext->mNext)) { + delete mNext; // don't delete that last in the chain, it's shared } } @@ -142,7 +144,7 @@ public: RuleHash(void); ~RuleHash(void); void AppendRule(nsICSSStyleRule* aRule); - void EnumerateAllRules(nsIAtom* aTag, nsIAtom* aID, nsIAtom* aClass, + void EnumerateAllRules(nsIAtom* aTag, nsIAtom* aID, const nsVoidArray& aClassList, RuleEnumFunc aFunc, void* aData); void EnumerateTagRules(nsIAtom* aTag, RuleEnumFunc aFunc, void* aData); @@ -154,11 +156,17 @@ protected: nsHashtable mTagTable; nsHashtable mIdTable; nsHashtable mClassTable; + + RuleValue** mEnumList; + PRInt32 mEnumListSize; + RuleValue mEndValue; }; RuleHash::RuleHash(void) : mRuleCount(0), - mTagTable(), mIdTable(), mClassTable() + mTagTable(), mIdTable(), mClassTable(), + mEnumList(nsnull), mEnumListSize(0), + mEndValue(nsnull, -1) { } @@ -173,23 +181,31 @@ RuleHash::~RuleHash(void) mTagTable.Enumerate(DeleteValue); mIdTable.Enumerate(DeleteValue); mClassTable.Enumerate(DeleteValue); + if (nsnull != mEnumList) { + delete [] mEnumList; + } } void RuleHash::AppendRuleToTable(nsHashtable& aTable, nsIAtom* aAtom, nsICSSStyleRule* aRule) { + NS_ASSERTION(nsnull != aAtom, "null hash key"); + RuleKey key(aAtom); RuleValue* value = (RuleValue*)aTable.Get(&key); if (nsnull == value) { value = new RuleValue(aRule, mRuleCount++); aTable.Put(&key, value); + value->mNext = &mEndValue; } else { - while (nsnull != value->mNext) { + while (&mEndValue != value->mNext) { value = value->mNext; } value->mNext = new RuleValue(aRule, mRuleCount++); + value->mNext->mNext = &mEndValue; } + mEndValue.mIndex = mRuleCount; } void RuleHash::AppendRule(nsICSSStyleRule* aRule) @@ -198,65 +214,128 @@ void RuleHash::AppendRule(nsICSSStyleRule* aRule) if (nsnull != selector->mID) { AppendRuleToTable(mIdTable, selector->mID, aRule); } - else if (nsnull != selector->mClass) { - AppendRuleToTable(mClassTable, selector->mClass, aRule); + else if (nsnull != selector->mClassList) { + AppendRuleToTable(mClassTable, selector->mClassList->mAtom, aRule); } else if (nsnull != selector->mTag) { AppendRuleToTable(mTagTable, selector->mTag, aRule); } + else { // universal tag selector + AppendRuleToTable(mTagTable, nsCSSAtoms::universalSelector, aRule); + } } -void RuleHash::EnumerateAllRules(nsIAtom* aTag, nsIAtom* aID, nsIAtom* aClass, +void RuleHash::EnumerateAllRules(nsIAtom* aTag, nsIAtom* aID, const nsVoidArray& aClassList, RuleEnumFunc aFunc, void* aData) { - RuleValue* tagValue = nsnull; - RuleValue* idValue = nsnull; - RuleValue* classValue = nsnull; - + PRInt32 classCount = aClassList.Count(); + PRInt32 testCount = classCount + 1; // + 1 for universal selector if (nsnull != aTag) { - RuleKey tagKey(aTag); - tagValue = (RuleValue*)mTagTable.Get(&tagKey); + testCount++; } if (nsnull != aID) { - RuleKey idKey(aID); - idValue = (RuleValue*)mIdTable.Get(&idKey); - } - if (nsnull != aClass) { - RuleKey classKey(aClass); - classValue = (RuleValue*)mClassTable.Get(&classKey); + testCount++; } - PRInt32 tagIndex = ((nsnull != tagValue) ? tagValue->mIndex : mRuleCount); - PRInt32 idIndex = ((nsnull != idValue) ? idValue->mIndex : mRuleCount); - PRInt32 classIndex = ((nsnull != classValue) ? classValue->mIndex : mRuleCount); + if (mEnumListSize < testCount) { + delete [] mEnumList; + mEnumList = new RuleValue*[testCount]; + mEnumListSize = testCount; + } - while ((nsnull != tagValue) || (nsnull != idValue) || (nsnull != classValue)) { - if ((tagIndex < idIndex) && (tagIndex < classIndex)) { - (*aFunc)(tagValue->mRule, aData); - tagValue = tagValue->mNext; - tagIndex = ((nsnull != tagValue) ? tagValue->mIndex : mRuleCount); + PRInt32 index; + PRInt32 valueCount = 0; + + { // universal tag rules + RuleValue* value = (RuleValue*)mTagTable.Get(&RuleKey(nsCSSAtoms::universalSelector)); + if (nsnull != value) { + mEnumList[valueCount++] = value; } - else if (idIndex < classIndex) { - (*aFunc)(idValue->mRule, aData); - idValue = idValue->mNext; - idIndex = ((nsnull != idValue) ? idValue->mIndex : mRuleCount); + } + if (nsnull != aTag) { + RuleValue* value = (RuleValue*)mTagTable.Get(&RuleKey(aTag)); + if (nsnull != value) { + mEnumList[valueCount++] = value; } - else { - (*aFunc)(classValue->mRule, aData); - classValue = classValue->mNext; - classIndex = ((nsnull != classValue) ? classValue->mIndex : mRuleCount); + } + if (nsnull != aID) { + RuleValue* value = (RuleValue*)mIdTable.Get(&RuleKey(aID)); + if (nsnull != value) { + mEnumList[valueCount++] = value; } } + for (index = 0; index < classCount; index++) { + nsIAtom* classAtom = (nsIAtom*)aClassList.ElementAt(index); + RuleValue* value = (RuleValue*)mClassTable.Get(&RuleKey(classAtom)); + if (nsnull != value) { + mEnumList[valueCount++] = value; + } + } + NS_ASSERTION(valueCount <= testCount, "values exceeded list size"); + + if (1 < valueCount) { + PRInt32 ruleIndex = mRuleCount; + PRInt32 valueIndex = -1; + + for (index = 0; index < valueCount; index++) { + if (mEnumList[index]->mIndex < ruleIndex) { + ruleIndex = mEnumList[index]->mIndex; + valueIndex = index; + } + } + do { + (*aFunc)(mEnumList[valueIndex]->mRule, aData); + mEnumList[valueIndex] = mEnumList[valueIndex]->mNext; + ruleIndex = mEnumList[valueIndex]->mIndex; + for (index = 0; index < valueCount; index++) { + if (mEnumList[index]->mIndex < ruleIndex) { + ruleIndex = mEnumList[index]->mIndex; + valueIndex = index; + } + } + } while (ruleIndex < mRuleCount); + } + else if (0 < valueCount) { // fast loop over single value + RuleValue* value = mEnumList[0]; + do { + (*aFunc)(value->mRule, aData); + value = value->mNext; + } while (&mEndValue != value); + } } void RuleHash::EnumerateTagRules(nsIAtom* aTag, RuleEnumFunc aFunc, void* aData) { - RuleKey tagKey(aTag); - RuleValue* value = (RuleValue*)mTagTable.Get(&tagKey); + RuleValue* tagValue = (RuleValue*)mTagTable.Get(&RuleKey(aTag)); + RuleValue* uniValue = (RuleValue*)mTagTable.Get(&RuleKey(nsCSSAtoms::universalSelector)); - while (nsnull != value) { - (*aFunc)(value->mRule, aData); - value = value->mNext; + if (nsnull == tagValue) { + if (nsnull != uniValue) { + do { + (*aFunc)(uniValue->mRule, aData); + uniValue = uniValue->mNext; + } while (&mEndValue != uniValue); + } + } + else { + if (nsnull == uniValue) { + do { + (*aFunc)(tagValue->mRule, aData); + tagValue = tagValue->mNext; + } while (&mEndValue != tagValue); + } + else { + do { + if (tagValue->mIndex < uniValue->mIndex) { + (*aFunc)(tagValue->mRule, aData); + tagValue = tagValue->mNext; + } + else { + (*aFunc)(uniValue->mRule, aData); + uniValue = uniValue->mNext; + } + } while ((&mEndValue != tagValue) || (&mEndValue != uniValue)); + } } } @@ -691,6 +770,8 @@ CSSStyleSheetImpl::CSSStyleSheetImpl() mRuleHash(nsnull) { NS_INIT_REFCNT(); + nsCSSAtoms::AddrefAtoms(); + mParent = nsnull; mRuleCollection = nsnull; mImportsCollection = nsnull; @@ -744,6 +825,7 @@ CSSStyleSheetImpl::~CSSStyleSheetImpl() // XXX The document reference is not reference counted and should // not be released. The document will let us know when it is going // away. + nsCSSAtoms::ReleaseAtoms(); } NS_IMPL_ADDREF(CSSStyleSheetImpl) @@ -801,76 +883,87 @@ static PRBool SelectorMatches(nsIPresContext* aPresContext, PRBool result = PR_FALSE; nsIAtom* contentTag; aContent->GetTag(contentTag); + PRInt32 nameSpaceID; + aContent->GetNameSpaceID(nameSpaceID); - if ((nsnull == aSelector->mTag) || (aSelector->mTag == contentTag)) { - if ((nsnull != aSelector->mClass) || (nsnull != aSelector->mID) || - (nsnull != aSelector->mPseudoClass)) { + if (((nsnull == aSelector->mTag) || (aSelector->mTag == contentTag)) && + ((kNameSpaceID_Unknown == aSelector->mNameSpace) || (nameSpaceID == aSelector->mNameSpace))) { + result = PR_TRUE; + // namespace/tag match + if (nsnull != aSelector->mAttrList) { // test for attribute match + } + if ((PR_TRUE == result) && + ((nsnull != aSelector->mID) || (nsnull != aSelector->mClassList))) { // test for ID & class match + result = PR_FALSE; nsIHTMLContent* htmlContent; - if (NS_OK == aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent)) { - nsIAtom* contentClass; - htmlContent->GetClass(contentClass); + if (NS_SUCCEEDED(aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent))) { nsIAtom* contentID; htmlContent->GetID(contentID); - if ((nsnull == aSelector->mClass) || (aSelector->mClass == contentClass)) { - if ((nsnull == aSelector->mID) || (aSelector->mID == contentID)) { - if ((contentTag == nsHTMLAtoms::a) && (nsnull != aSelector->mPseudoClass)) { - // test link state - nsILinkHandler* linkHandler; - - if ((NS_OK == aPresContext->GetLinkHandler(&linkHandler)) && - (nsnull != linkHandler)) { - nsAutoString base, href; // XXX base?? - nsresult attrState = htmlContent->GetAttribute("href", href); - - if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { - nsIURL* docURL = nsnull; - nsIDocument* doc = nsnull; - aContent->GetDocument(doc); - if (nsnull != doc) { - docURL = doc->GetDocumentURL(); - NS_RELEASE(doc); - } - - nsAutoString absURLSpec; - nsresult rv = NS_MakeAbsoluteURL(docURL, base, href, absURLSpec); - NS_IF_RELEASE(docURL); - - nsLinkState state; - if (NS_OK == linkHandler->GetLinkState(absURLSpec, state)) { - switch (state) { - case eLinkState_Unvisited: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::link); - break; - case eLinkState_Visited: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::visited); - break; - case eLinkState_OutOfDate: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::outOfDate); - break; - case eLinkState_Active: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::active); - break; - case eLinkState_Hover: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::hover); - break; - } - } - } - NS_RELEASE(linkHandler); - } - } - else { - result = PR_TRUE; - } + if ((nsnull == aSelector->mID) || (aSelector->mID == contentID)) { + nsIAtom* contentClass; + htmlContent->GetClass(contentClass); + // XXX only testing for frist class right now + if ((nsnull == aSelector->mClassList) || (aSelector->mClassList->mAtom == contentClass)) { + result = PR_TRUE; } + NS_IF_RELEASE(contentClass); } NS_RELEASE(htmlContent); - NS_IF_RELEASE(contentClass); NS_IF_RELEASE(contentID); } } - else { - result = PR_TRUE; + if ((PR_TRUE == result) && + (nsnull != aSelector->mPseudoClassList)) { // test for pseudo class match + result = PR_FALSE; + // XXX only testing for anchor pseudo classes right now + // XXX only testing first pseudo class right now + if ((contentTag == nsHTMLAtoms::a) && (nsnull != aSelector->mPseudoClassList)) { + // test link state + nsILinkHandler* linkHandler; + + if ((NS_OK == aPresContext->GetLinkHandler(&linkHandler)) && + (nsnull != linkHandler)) { + nsAutoString base, href; // XXX base?? + nsresult attrState = aContent->GetAttribute("href", href); + + if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { + nsIURL* docURL = nsnull; + nsIDocument* doc = nsnull; + aContent->GetDocument(doc); + if (nsnull != doc) { + docURL = doc->GetDocumentURL(); + NS_RELEASE(doc); + } + + nsAutoString absURLSpec; + nsresult rv = NS_MakeAbsoluteURL(docURL, base, href, absURLSpec); + NS_IF_RELEASE(docURL); + + nsIAtom* pseudo = aSelector->mPseudoClassList->mAtom; + nsLinkState state; + if (NS_OK == linkHandler->GetLinkState(absURLSpec, state)) { + switch (state) { + case eLinkState_Unvisited: + result = PRBool (pseudo == nsCSSAtoms::linkPseudo); + break; + case eLinkState_Visited: + result = PRBool (pseudo == nsCSSAtoms::visitedPseudo); + break; + case eLinkState_OutOfDate: + result = PRBool (pseudo == nsCSSAtoms::outOfDatePseudo); + break; + case eLinkState_Active: + result = PRBool (pseudo == nsCSSAtoms::activePseudo); + break; + case eLinkState_Hover: + result = PRBool (pseudo == nsCSSAtoms::hoverPseudo); + break; + } + } + } + NS_RELEASE(linkHandler); + } + } } } NS_IF_RELEASE(contentTag); @@ -899,6 +992,8 @@ static void ContentEnumFunc(nsICSSStyleRule* aRule, void* aData) { ContentEnumData* data = (ContentEnumData*)aData; + // XXX not dealing with selector functions... + nsCSSSelector* selector = aRule->FirstSelector(); if (SelectorMatches(data->mPresContext, selector, data->mContent)) { selector = selector->mNext; @@ -995,7 +1090,13 @@ PRInt32 CSSStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext, NS_RELEASE(htmlContent); } - mRuleHash->EnumerateAllRules(tagAtom, idAtom, classAtom, ContentEnumFunc, &data); + nsVoidArray classArray; // XXX need to recycle this guy + + if (nsnull != classAtom) { + classArray.AppendElement(classAtom); + } + // XXX need to handle multiple classes + mRuleHash->EnumerateAllRules(tagAtom, idAtom, classArray, ContentEnumFunc, &data); matchCount += data.mCount; #ifdef DEBUG_RULES @@ -1005,7 +1106,7 @@ PRInt32 CSSStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext, NS_NewISupportsArray(&list2); data.mResults = list1; - mRuleHash->EnumerateAllRules(tagAtom, idAtom, classAtom, ContentEnumFunc, &data); + mRuleHash->EnumerateAllRules(tagAtom, idAtom, classArray, ContentEnumFunc, &data); data.mResults = list2; mWeightedRules->EnumerateBackwards(ContentEnumWrap, &data); NS_ASSERTION(list1->Equals(list2), "lists not equal"); diff --git a/mozilla/layout/style/nsCSSParser.cpp b/mozilla/layout/style/nsCSSParser.cpp index e0a4aebf2c7..2237f531f94 100644 --- a/mozilla/layout/style/nsCSSParser.cpp +++ b/mozilla/layout/style/nsCSSParser.cpp @@ -31,6 +31,9 @@ #include "nsVoidArray.h" #include "nsColor.h" #include "nsStyleConsts.h" +#include "nsCSSAtoms.h" +#include "nsINameSpaceManager.h" +#include "nsINameSpace.h" // XXX TODO: // - rework aErrorCode stuff: switch over to nsresult @@ -46,142 +49,52 @@ static NS_DEFINE_IID(kCSSTextSID, NS_CSS_TEXT_SID); #define KEYWORD_BUFFER_SIZE 50 // big enough for any keyword -struct Selector { - nsAutoString mTag; // weight 1 - nsAutoString mID; // weight 100 - nsAutoString mClass; // weight 10 - nsAutoString mPseudoClass; // weight 10 (== class) - nsAutoString mPseudoElement; // weight 10 (== class) ?? - - PRUint32 mMask; // which fields have values - - Selector(); - ~Selector(); - - PRInt32 Weight() const; -#ifdef NS_DEBUG - void Dump() const; -#endif -}; - -#define SELECTOR_TAG 0x01 -#define SELECTOR_ID 0x02 -#define SELECTOR_CLASS 0x04 -#define SELECTOR_PSEUDO_CLASS 0x08 -#define SELECTOR_PSEUDO_ELEMENT 0x10 - -#define SELECTOR_WEIGHT_BASE 10 - -Selector::Selector() -{ - mMask = 0; -} - -Selector::~Selector() -{ -} - -PRInt32 Selector::Weight() const -{ - return (((0 != (SELECTOR_TAG & mMask)) ? 1 : 0) + - ((0 != (SELECTOR_ID & mMask)) ? (SELECTOR_WEIGHT_BASE * SELECTOR_WEIGHT_BASE) : 0) + - ((0 != ((SELECTOR_CLASS | SELECTOR_PSEUDO_CLASS | SELECTOR_PSEUDO_ELEMENT) & mMask)) ? SELECTOR_WEIGHT_BASE : 0)); -} - -#ifdef NS_DEBUG -void Selector::Dump() const -{ - PRBool needSpace = PR_FALSE; - if (mTag.Length() > 0) { - fputs(mTag, stdout); - needSpace = PR_TRUE; - } - if (mID.Length() > 0) { - if (needSpace) fputs(" ", stdout); - fputs("#", stdout); - fputs(mID, stdout); - needSpace = PR_TRUE; - } - if (mClass.Length() > 0) { - if (needSpace) fputs(" ", stdout); - fputs(".", stdout); - fputs(mClass, stdout); - needSpace = PR_TRUE; - } - if (mPseudoClass.Length() > 0) { - if (needSpace) fputs(" ", stdout); - fputs(":", stdout); - fputs(mPseudoClass, stdout); - needSpace = PR_TRUE; - } - if (mPseudoElement.Length() > 0) { - if (needSpace) fputs(" ", stdout); - fputs(":", stdout); - fputs(mPseudoElement, stdout); - needSpace = PR_TRUE; - } -} -#endif - // e.g. "P B, H1 B { ... }" has a selector list with two elements, // each of which has two selectors. struct SelectorList { - SelectorList* mNext; - nsVoidArray mSelectors; + SelectorList(void); + ~SelectorList(void); - SelectorList(); - - void Destroy(); - - void AddSelector(Selector* aSelector) { - mSelectors.AppendElement(aSelector); - } + void AddSelector(const nsCSSSelector& aSelector); #ifdef NS_DEBUG - void Dump(); + void Dump(void); #endif -private: - ~SelectorList(); + nsCSSSelector* mSelectors; + SelectorList* mNext; }; -SelectorList::SelectorList() +SelectorList::SelectorList(void) + : mSelectors(nsnull), + mNext(nsnull) { - mNext = nsnull; } SelectorList::~SelectorList() { - PRInt32 n = mSelectors.Count(); - for (PRInt32 i = 0; i < n; i++) { - Selector* sel = (Selector*) mSelectors.ElementAt(i); - delete sel; + nsCSSSelector* sel = mSelectors; + while (nsnull != sel) { + nsCSSSelector* dead = sel; + sel = sel->mNext; + delete dead; + } + if (nsnull != mNext) { + delete mNext; } } -void SelectorList::Destroy() -{ - SelectorList* list = this; - while (nsnull != list) { - SelectorList* next = list->mNext; - delete list; - list = next; - } +void SelectorList::AddSelector(const nsCSSSelector& aSelector) +{ // prepend to list + nsCSSSelector* newSel = new nsCSSSelector(aSelector); + newSel->mNext = mSelectors; + mSelectors = newSel; } + #ifdef NS_DEBUG void SelectorList::Dump() { - PRInt32 n = mSelectors.Count(); - for (PRInt32 i = 0; i < n; i++) { - Selector* sel = (Selector*) mSelectors.ElementAt(i); - sel->Dump(); - fputs(" ", stdout); - } - if (mNext) { - fputs(", ", stdout); - mNext->Dump(); - } } #endif @@ -200,6 +113,8 @@ public: NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet); + NS_IMETHOD SetCaseSensative(PRBool aCaseSensative); + NS_IMETHOD Parse(nsIUnicharInputStream* aInput, nsIURL* aInputURL, nsICSSStyleSheet*& aResult); @@ -231,9 +146,9 @@ protected: PRBool GatherMedia(PRInt32& aErrorCode, nsString& aMedia); PRBool ProcessImport(PRInt32& aErrorCode, const nsString& aURLSpec, const nsString& aMedia); - PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList* aListHead); - PRBool ParseSelectorList(PRInt32& aErrorCode, SelectorList* aListHead); - PRBool ParseSelector(PRInt32& aErrorCode, Selector* aSelectorResult); + PRBool ParseSelectorList(PRInt32& aErrorCode, SelectorList*& aListHead); + PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList*& aListHead); + PRBool ParseSelector(PRInt32& aErrorCode, nsCSSSelector& aSelectorResult); nsICSSDeclaration* ParseDeclarationBlock(PRInt32& aErrorCode, PRBool aCheckForBraces); PRBool ParseDeclaration(PRInt32& aErrorCode, @@ -317,6 +232,7 @@ protected: PRBool mInHead; PRBool mNavQuirkMode; + PRBool mCaseSensative; }; NS_HTML nsresult @@ -334,19 +250,23 @@ NS_NewCSSParser(nsICSSParser** aInstancePtrResult) CSSParserImpl::CSSParserImpl() { NS_INIT_REFCNT(); + nsCSSAtoms::AddrefAtoms(); mScanner = nsnull; mSheet = nsnull; mHavePushBack = PR_FALSE; mNavQuirkMode = PR_TRUE; + mCaseSensative = PR_FALSE; } CSSParserImpl::CSSParserImpl(nsICSSStyleSheet* aSheet) { NS_INIT_REFCNT(); + nsCSSAtoms::AddrefAtoms(); mScanner = nsnull; mSheet = aSheet; NS_ADDREF(aSheet); mHavePushBack = PR_FALSE; mNavQuirkMode = PR_TRUE; + mCaseSensative = PR_FALSE; } NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID) @@ -354,16 +274,17 @@ NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID) CSSParserImpl::~CSSParserImpl() { NS_IF_RELEASE(mSheet); + nsCSSAtoms::ReleaseAtoms(); } -NS_METHOD +NS_IMETHODIMP CSSParserImpl::GetInfoMask(PRUint32& aResult) { aResult = NS_CSS_GETINFO_CSS1 | NS_CSS_GETINFO_CSSP; return NS_OK; } -NS_METHOD +NS_IMETHODIMP CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet) { NS_PRECONDITION(nsnull != aSheet, "null ptr"); @@ -381,7 +302,15 @@ CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet) return NS_OK; } -NS_METHOD +NS_IMETHODIMP +CSSParserImpl::SetCaseSensative(PRBool aCaseSensative) +{ + mCaseSensative = aCaseSensative; + return NS_OK; +} + + +NS_IMETHODIMP CSSParserImpl::Parse(nsIUnicharInputStream* aInput, nsIURL* aInputURL, nsICSSStyleSheet*& aResult) @@ -426,7 +355,7 @@ CSSParserImpl::Parse(nsIUnicharInputStream* aInput, return NS_OK; } -NS_METHOD +NS_IMETHODIMP CSSParserImpl::ParseDeclarations(const nsString& aDeclaration, nsIURL* aBaseURL, nsIStyleRule*& aResult) @@ -471,7 +400,7 @@ CSSParserImpl::ParseDeclarations(const nsString& aDeclaration, return NS_OK; } -NS_METHOD +NS_IMETHODIMP CSSParserImpl::ParseAndAppendDeclaration(const nsString& aBuffer, nsIURL* aBaseURL, nsICSSDeclaration* aDeclaration, @@ -925,18 +854,18 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode) nsCSSToken* tk = &mToken; // First get the list of selectors for the rule - SelectorList *slist = new SelectorList(); - if (!ParseSelectorList(aErrorCode, slist)) { + SelectorList* slist = nsnull; + if (! ParseSelectorList(aErrorCode, slist)) { SkipRuleSet(aErrorCode); - slist->Destroy(); return PR_FALSE; } + NS_ASSERTION(nsnull != slist, "null selector list"); // Next parse the declaration block nsICSSDeclaration* declaration = ParseDeclarationBlock(aErrorCode, PR_TRUE); if (nsnull == declaration) { // XXX skip something here - slist->Destroy(); + delete slist; return PR_FALSE; } @@ -950,34 +879,16 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode) // Translate the selector list and declaration block into style data SelectorList* list = slist; - nsCSSSelector selector; - nsICSSStyleRule* rule = nsnull; while (nsnull != list) { - PRInt32 selIndex = list->mSelectors.Count(); - - Selector* sel = (Selector*)list->mSelectors[--selIndex]; - if (0 < sel->mPseudoElement.Length()) { // only pseudo elements at end selector - nsString nullStr; - selector.Set(sel->mPseudoElement, nullStr, nullStr, nullStr); - NS_NewCSSStyleRule(&rule, selector); - } - selector.Set(sel->mTag, sel->mID, sel->mClass, sel->mPseudoClass); - PRInt32 weight = sel->Weight(); - - if (nsnull == rule) { - NS_NewCSSStyleRule(&rule, selector); - } - else { - rule->AddSelector(selector); - } + PRInt32 weight = list->mSelectors->CalcWeight(); + nsICSSStyleRule* rule = nsnull; + NS_NewCSSStyleRule(&rule, *(list->mSelectors)); if (nsnull != rule) { - while (--selIndex >= 0) { - Selector* sel = (Selector*)list->mSelectors[selIndex]; - selector.Set(sel->mTag, sel->mID, sel->mClass, sel->mPseudoClass); - - rule->AddSelector(selector); - weight += sel->Weight(); + if (nsnull != list->mSelectors->mNext) { // hand off other selectors to new rule + nsCSSSelector* ruleFirst = rule->FirstSelector(); + ruleFirst->mNext = list->mSelectors->mNext; + list->mSelectors->mNext = nsnull; } rule->SetDeclaration(declaration); rule->SetWeight(weight); @@ -990,219 +901,342 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode) } // Release temporary storage - slist->Destroy(); + delete slist; NS_RELEASE(declaration); return PR_TRUE; } PRBool CSSParserImpl::ParseSelectorList(PRInt32& aErrorCode, - SelectorList* aListHead) + SelectorList*& aListHead) { - if (!ParseSelectorGroup(aErrorCode, aListHead)) { + SelectorList* list = nsnull; + if (! ParseSelectorGroup(aErrorCode, list)) { // must have at least one selector group + aListHead = nsnull; return PR_FALSE; } + NS_ASSERTION(nsnull != list, "no selector list"); + aListHead = list; // After that there must either be a "," or a "{" nsCSSToken* tk = &mToken; for (;;) { - if (!GetToken(aErrorCode, PR_TRUE)) { - return PR_FALSE; + if (! GetToken(aErrorCode, PR_TRUE)) { + break; } if (eCSSToken_Symbol != tk->mType) { UngetToken(); - return PR_FALSE; + break; } if (',' == tk->mSymbol) { + SelectorList* newList = nsnull; // Another selector group must follow - SelectorList* newList = new SelectorList(); - if (!ParseSelectorGroup(aErrorCode, newList)) { - newList->Destroy(); - return PR_FALSE; + if (! ParseSelectorGroup(aErrorCode, newList)) { + break; } // add new list to the end of the selector list - aListHead->mNext = newList; - aListHead = newList; + list->mNext = newList; + list = newList; continue; } else if ('{' == tk->mSymbol) { UngetToken(); - break; + return PR_TRUE; } else { UngetToken(); - return PR_FALSE; + break; } } - return PR_TRUE; + delete aListHead; + aListHead = nsnull; + return PR_FALSE; +} + +static PRBool IsPseudoClass(const nsIAtom* aAtom) +{ + return PRBool((nsCSSAtoms::activePseudo == aAtom) || + (nsCSSAtoms::firstChildPseudo == aAtom) || + (nsCSSAtoms::focusPseudo == aAtom) || + (nsCSSAtoms::hoverPseudo == aAtom) || + (nsCSSAtoms::langPseudo == aAtom) || + (nsCSSAtoms::linkPseudo == aAtom) || + (nsCSSAtoms::outOfDatePseudo == aAtom) || + (nsCSSAtoms::visitedPseudo == aAtom)); +} + +static PRBool IsSinglePseudoClass(const nsCSSSelector& aSelector) +{ + return PRBool((aSelector.mNameSpace == kNameSpaceID_Unknown) && + (aSelector.mTag == nsnull) && + (aSelector.mID == nsnull) && + (aSelector.mClassList == nsnull) && + (aSelector.mAttrList == nsnull) && + (aSelector.mPseudoClassList != nsnull) && + (aSelector.mPseudoClassList->mNext == nsnull)); } PRBool CSSParserImpl::ParseSelectorGroup(PRInt32& aErrorCode, - SelectorList* aList) + SelectorList*& aList) { + SelectorList* list = nsnull; for (;;) { - Selector* sel = new Selector(); - if (!ParseSelector(aErrorCode, sel)) { - delete sel; + nsCSSSelector selector; + if (! ParseSelector(aErrorCode, selector)) { break; } - aList->AddSelector(sel); + if (nsnull == list) { + list = new SelectorList(); + } + list->AddSelector(selector); + nsCSSSelector* listSel = list->mSelectors; + // XXX parse combinator here + + // pull out pseudo elements here + nsAtomList* prevList = nsnull; + nsAtomList* pseudoClassList = listSel->mPseudoClassList; + while (nsnull != pseudoClassList) { + if (! IsPseudoClass(pseudoClassList->mAtom)) { + if (IsSinglePseudoClass(*listSel)) { // convert to pseudo element selector + nsIAtom* pseudoElement = pseudoClassList->mAtom; // steal ref count + pseudoClassList->mAtom = nsnull; + listSel->Reset(); + listSel->mTag = pseudoElement; + } + else { // append new pseudo element selector + selector.Reset(); + selector.mTag = pseudoClassList->mAtom; // steal ref count + list->AddSelector(selector); + pseudoClassList->mAtom = nsnull; + if (nsnull == prevList) { // delete list entry + listSel->mPseudoClassList = pseudoClassList->mNext; + } + else { + prevList->mNext = pseudoClassList->mNext; + } + pseudoClassList->mNext = nsnull; + delete pseudoClassList; + } + break; // only one pseudo element per selector + } + prevList = pseudoClassList; + pseudoClassList = pseudoClassList->mNext; + } } - return (PRBool) (aList->mSelectors.Count() > 0); + aList = list; + return PRBool(nsnull != aList); } -static PRBool IsPseudoClass(const nsString& aBuffer) -{ - return (aBuffer.EqualsIgnoreCase("link") || - aBuffer.EqualsIgnoreCase("visited") || - aBuffer.EqualsIgnoreCase("hover") || - aBuffer.EqualsIgnoreCase("out-of-date") || // our extension - aBuffer.EqualsIgnoreCase("active")); -} +#define SEL_MASK_NSPACE 0x01 +#define SEL_MASK_ELEM 0x02 +#define SEL_MASK_ID 0x04 +#define SEL_MASK_CLASS 0x08 +#define SEL_MASK_ATTRIB 0x10 +#define SEL_MASK_PCLASS 0x20 +#define SEL_MASK_PELEM 0x40 /** - * These are the 31 possible kinds of CSS1 style selectors: - * (but there are 50 ways to leave your lover) - * [*] means it can repeat - * + * This is the format for selectors: + * operator? [namespace \:]? element_name? [ ID | class | attrib | pseudo ]* */ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, - Selector* aSelectorResult) + nsCSSSelector& aSelector) { - PRUint32 mask = 0; - aSelectorResult->mTag.Truncate(0); - aSelectorResult->mClass.Truncate(0); - aSelectorResult->mID.Truncate(0); - aSelectorResult->mPseudoClass.Truncate(0); - aSelectorResult->mPseudoElement.Truncate(0); + PRInt32 dataMask = 0; + nsAutoString buffer; - nsCSSToken* tk = &mToken; - if (!GetToken(aErrorCode, PR_TRUE)) { + if (! GetToken(aErrorCode, PR_TRUE)) { return PR_FALSE; } - if (eCSSToken_Ident == tk->mType) { - // tag - mask |= SELECTOR_TAG; - aSelectorResult->mTag.Append(tk->mIdent); - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof is ok (here!) + if ((eCSSToken_Symbol == mToken.mType) && ('*' == mToken.mSymbol)) { // universal element selector + // don't set any tag in the selector + dataMask |= SEL_MASK_ELEM; + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) return PR_TRUE; } } - if (eCSSToken_ID == tk->mType) { - // #id - if ((0 < tk->mIdent.Length()) && (nsString::IsAlpha(tk->mIdent.CharAt(0)))) { // verify is legal ID - mask |= SELECTOR_ID; - aSelectorResult->mID.Append(tk->mIdent); - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof is ok (here!) - return PR_TRUE; + else if (eCSSToken_Ident == mToken.mType) { // element name + PRInt32 colon = mToken.mIdent.Find(':'); + if (-1 == colon) { // no namespace + if (mCaseSensative) { + aSelector.SetTag(mToken.mIdent); + } + else { + mToken.mIdent.ToUpperCase(buffer); + aSelector.SetTag(buffer); } } - else { - return PR_FALSE; + else { // pull out the namespace + nsAutoString nameSpace; + mToken.mIdent.Left(nameSpace, colon); + mToken.mIdent.Right(buffer, (mToken.mIdent.Length() - (colon + 1))); + if (! mCaseSensative) { + buffer.ToUpperCase(); + } + // XXX lookup namespace, set it + // deal with * namespace (== unknown) + aSelector.SetTag(buffer); } - } - if ((eCSSToken_Symbol == tk->mType) && ('.' == tk->mSymbol)) { - // .class - if (!GetToken(aErrorCode, PR_FALSE)) { - return PR_FALSE; - } - if (eCSSToken_Ident != tk->mType) { - // malformed selector - UngetToken(); - return PR_FALSE; - } - mask |= SELECTOR_CLASS; - aSelectorResult->mClass.Append(tk->mIdent); - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof is ok (here!) + dataMask |= SEL_MASK_ELEM; + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) return PR_TRUE; } } - if ((eCSSToken_Symbol == tk->mType) && (':' == tk->mSymbol)) { - // :pseudo - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof - return PR_FALSE; + for (;;) { + if (eCSSToken_ID == mToken.mType) { // #id + if ((0 == (dataMask & SEL_MASK_ID)) && // only one ID + (0 < mToken.mIdent.Length()) && + (nsString::IsAlpha(mToken.mIdent.CharAt(0)))) { // verify is legal ID + dataMask |= SEL_MASK_ID; + aSelector.SetID(mToken.mIdent); + } + else { + UngetToken(); + return PR_FALSE; + } } - if (eCSSToken_Ident != tk->mType) { - // malformed selector - UngetToken(); - return PR_FALSE; + else if ((eCSSToken_Symbol == mToken.mType) && ('.' == mToken.mSymbol)) { // .class + if (! GetToken(aErrorCode, PR_FALSE)) { // get ident + return PR_FALSE; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector (XXX what about leading digits?) + UngetToken(); + return PR_FALSE; + } + dataMask |= SEL_MASK_CLASS; + if (mCaseSensative) { + aSelector.AddClass(mToken.mIdent); + } + else { + mToken.mIdent.ToUpperCase(buffer); + aSelector.AddClass(buffer); + } } - if (IsPseudoClass(tk->mIdent)) { - mask |= SELECTOR_PSEUDO_CLASS; - aSelectorResult->mPseudoClass.Append(tk->mIdent); + else if ((eCSSToken_Symbol == mToken.mType) && (':' == mToken.mSymbol)) { // :pseudo + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof + return PR_FALSE; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector + UngetToken(); + return PR_FALSE; + } + buffer.Truncate(); + buffer.Append(':'); + buffer.Append(mToken.mIdent); + buffer.ToUpperCase(); + nsIAtom* pseudo = NS_NewAtom(buffer); + if (IsPseudoClass(pseudo)) { + // XXX parse lang pseudo class + dataMask |= SEL_MASK_PCLASS; + aSelector.AddPseudoClass(pseudo); + } + else { + if (0 == (dataMask & SEL_MASK_PELEM)) { + dataMask |= SEL_MASK_PELEM; + aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later + } + else { // multiple pseudo elements, not legal + UngetToken(); + NS_RELEASE(pseudo); + return PR_FALSE; + } + } + NS_RELEASE(pseudo); } - else { - mask |= SELECTOR_PSEUDO_ELEMENT; - aSelectorResult->mPseudoElement.Append(':'); // keep the colon - aSelectorResult->mPseudoElement.Append(tk->mIdent); + else if ((eCSSToken_Symbol == mToken.mType) && ('[' == mToken.mSymbol)) { // attribute + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector + UngetToken(); + return PR_FALSE; + } + nsAutoString attr(mToken.mType); + if (! mCaseSensative) { + attr.ToUpperCase(); + } + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if (eCSSToken_Symbol == mToken.mType) { + PRUint8 func; + if (']' == mToken.mSymbol) { + dataMask |= SEL_MASK_ATTRIB; + aSelector.AddAttribute(attr); + func = NS_ATTR_FUNC_SET; + } + else if ('=' == mToken.mSymbol) { + func = NS_ATTR_FUNC_EQUALS; + } + else if ('~' == mToken.mSymbol) { + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if ((eCSSToken_Symbol == mToken.mType) && ('=' == mToken.mSymbol)) { + func = NS_ATTR_FUNC_INCLUDES; + } + else { + UngetToken(); + return PR_FALSE; + } + } + else if ('|' == mToken.mSymbol) { + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if ((eCSSToken_Symbol == mToken.mType) && ('=' == mToken.mSymbol)) { + func = NS_ATTR_FUNC_DASHMATCH; + } + else { + UngetToken(); + return PR_FALSE; + } + } + else { + UngetToken(); // bad function + return PR_FALSE; + } + if (NS_ATTR_FUNC_SET != func) { // get value + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) { + nsAutoString value(mToken.mIdent); + if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF + return PR_FALSE; + } + if ((eCSSToken_Symbol == mToken.mType) && (']' == mToken.mSymbol)) { + dataMask |= SEL_MASK_ATTRIB; + if (! mCaseSensative) { + value.ToUpperCase(); + } + aSelector.AddAttribute(attr, func, value); + } + else { + UngetToken(); + return PR_FALSE; + } + } + else { + UngetToken(); + return PR_FALSE; + } + } + } + else { + UngetToken(); // bad dog, no biscut! + return PR_FALSE; + } } - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof is ok (here!) + else { // not a selector token, we're done + break; + } + + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!) return PR_TRUE; } } - if ((eCSSToken_Symbol == tk->mType) && (':' == tk->mSymbol)) { - // :pseudo - if (!GetToken(aErrorCode, PR_FALSE)) { - // premature eof - return PR_FALSE; - } - if (eCSSToken_Ident != tk->mType) { - // malformed selector - UngetToken(); - return PR_FALSE; - } - if (! IsPseudoClass(tk->mIdent)) { - mask |= SELECTOR_PSEUDO_ELEMENT; - aSelectorResult->mPseudoElement.Truncate(0); - aSelectorResult->mPseudoElement.Append(':'); // keep the colon - aSelectorResult->mPseudoElement.Append(tk->mIdent); - } - tk = nsnull; - } - if (nsnull != tk) { - UngetToken(); - } - if (mask == 0) { - return PR_FALSE; - } - aSelectorResult->mMask = mask; - return PR_TRUE; + UngetToken(); + return PRBool(0 != dataMask); } nsICSSDeclaration* diff --git a/mozilla/layout/style/nsCSSStyleRule.cpp b/mozilla/layout/style/nsCSSStyleRule.cpp index e612fcfdce4..3678fa8e206 100644 --- a/mozilla/layout/style/nsCSSStyleRule.cpp +++ b/mozilla/layout/style/nsCSSStyleRule.cpp @@ -38,6 +38,7 @@ #include "nsIScriptGlobalObject.h" #include "nsIScriptObjectOwner.h" #include "nsDOMCSSDeclaration.h" +#include "nsINameSpaceManager.h" //#define DEBUG_REFS @@ -62,95 +63,330 @@ static NS_DEFINE_IID(kCSSTableSID, NS_CSS_TABLE_SID); // -- nsCSSSelector ------------------------------- -nsCSSSelector::nsCSSSelector() - : mTag(nsnull), mID(nsnull), mClass(nsnull), mPseudoClass(nsnull), +#define NS_IF_COPY(dest,source,type) \ + if (nsnull != source) dest = new type(*(source)) + +#define NS_IF_DELETE(ptr) \ + if (nsnull != ptr) { delete ptr; ptr = nsnull; } + +nsAtomList::nsAtomList(nsIAtom* aAtom) + : mAtom(aAtom), mNext(nsnull) { + NS_IF_ADDREF(mAtom); } -nsCSSSelector::nsCSSSelector(nsIAtom* aTag, nsIAtom* aID, nsIAtom* aClass, nsIAtom* aPseudoClass) - : mTag(aTag), mID(aID), mClass(aClass), mPseudoClass(aPseudoClass), +nsAtomList::nsAtomList(const nsString& aAtomValue) + : mAtom(nsnull), + mNext(nsnull) +{ + mAtom = NS_NewAtom(aAtomValue); +} + +nsAtomList::nsAtomList(const nsAtomList& aCopy) + : mAtom(aCopy.mAtom), + mNext(nsnull) +{ + NS_IF_ADDREF(mAtom); + NS_IF_COPY(mNext, aCopy.mNext, nsAtomList); +} + +nsAtomList::~nsAtomList(void) +{ + NS_IF_RELEASE(mAtom); + NS_IF_DELETE(mNext); +} + +PRBool nsAtomList::Equals(const nsAtomList* aOther) const +{ + if (this == aOther) { + return PR_TRUE; + } + if (nsnull != aOther) { + if (mAtom == aOther->mAtom) { + if (nsnull != mNext) { + return mNext->Equals(aOther->mNext); + } + return PRBool(nsnull == aOther->mNext); + } + } + return PR_FALSE; +} + +nsAttrSelector::nsAttrSelector(const nsString& aAttr) + : mAttr(nsnull), + mFunction(NS_ATTR_FUNC_SET), + mValue(), + mNext(nsnull) +{ + mAttr = NS_NewAtom(aAttr); +} + +nsAttrSelector::nsAttrSelector(const nsString& aAttr, PRUint8 aFunction, const nsString& aValue) + : mAttr(nsnull), + mFunction(aFunction), + mValue(aValue), + mNext(nsnull) +{ + mAttr = NS_NewAtom(aAttr); +} + +nsAttrSelector::nsAttrSelector(const nsAttrSelector& aCopy) + : mAttr(aCopy.mAttr), + mFunction(aCopy.mFunction), + mValue(aCopy.mValue), + mNext(nsnull) +{ + NS_IF_ADDREF(mAttr); + NS_IF_COPY(mNext, aCopy.mNext, nsAttrSelector); +} + +nsAttrSelector::~nsAttrSelector(void) +{ + NS_IF_RELEASE(mAttr); + NS_IF_DELETE(mNext); +} + +PRBool nsAttrSelector::Equals(const nsAttrSelector* aOther) const +{ + if (this == aOther) { + return PR_TRUE; + } + if (nsnull != aOther) { + if ((mAttr == aOther->mAttr) && + (mFunction == aOther->mFunction) && + mValue.Equals(aOther->mValue)) { + if (nsnull != mNext) { + return mNext->Equals(aOther->mNext); + } + return PRBool(nsnull == aOther->mNext); + } + } + return PR_FALSE; +} + +nsCSSSelector::nsCSSSelector(void) + : mNameSpace(kNameSpaceID_Unknown), mTag(nsnull), + mID(nsnull), + mClassList(nsnull), + mPseudoClassList(nsnull), + mAttrList(nsnull), + mOperator(0), mNext(nsnull) { - NS_IF_ADDREF(mTag); - NS_IF_ADDREF(mID); - NS_IF_ADDREF(mClass); - NS_IF_ADDREF(mPseudoClass); } nsCSSSelector::nsCSSSelector(const nsCSSSelector& aCopy) - : mTag(aCopy.mTag), mID(aCopy.mID), mClass(aCopy.mClass), mPseudoClass(aCopy.mPseudoClass), + : mNameSpace(aCopy.mNameSpace), mTag(aCopy.mTag), + mID(aCopy.mID), + mClassList(nsnull), + mPseudoClassList(nsnull), + mAttrList(nsnull), + mOperator(aCopy.mOperator), mNext(nsnull) -{ // implmented to support extension to CSS2 (when we have to copy the array) +{ NS_IF_ADDREF(mTag); NS_IF_ADDREF(mID); - NS_IF_ADDREF(mClass); - NS_IF_ADDREF(mPseudoClass); + NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList); + NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); + NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); } -nsCSSSelector::~nsCSSSelector() -{ - NS_IF_RELEASE(mTag); - NS_IF_RELEASE(mID); - NS_IF_RELEASE(mClass); - NS_IF_RELEASE(mPseudoClass); +nsCSSSelector::~nsCSSSelector(void) +{ + Reset(); } nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) { NS_IF_RELEASE(mTag); NS_IF_RELEASE(mID); - NS_IF_RELEASE(mClass); - NS_IF_RELEASE(mPseudoClass); + NS_IF_DELETE(mClassList); + NS_IF_DELETE(mPseudoClassList); + NS_IF_DELETE(mAttrList); + + mNameSpace = aCopy.mNameSpace; mTag = aCopy.mTag; mID = aCopy.mID; - mClass = aCopy.mClass; - mPseudoClass = aCopy.mPseudoClass; + NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList); + NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); + NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); + mOperator = aCopy.mOperator; + NS_IF_ADDREF(mTag); NS_IF_ADDREF(mID); - NS_IF_ADDREF(mClass); - NS_IF_ADDREF(mPseudoClass); return *this; } PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const { + if (this == aOther) { + return PR_TRUE; + } if (nsnull != aOther) { - return (PRBool)((aOther->mTag == mTag) && (aOther->mID == mID) && - (aOther->mClass == mClass) && (aOther->mPseudoClass == mPseudoClass)); + if ((aOther->mNameSpace == mNameSpace) && + (aOther->mTag == mTag) && + (aOther->mID == mID) && + (aOther->mOperator == mOperator)) { + if (nsnull != mClassList) { + if (PR_FALSE == mClassList->Equals(aOther->mClassList)) { + return PR_FALSE; + } + } + else { + if (nsnull != aOther->mClassList) { + return PR_FALSE; + } + } + if (nsnull != mPseudoClassList) { + if (PR_FALSE == mPseudoClassList->Equals(aOther->mPseudoClassList)) { + return PR_FALSE; + } + } + else { + if (nsnull != aOther->mPseudoClassList) { + return PR_FALSE; + } + } + if (nsnull != mAttrList) { + if (PR_FALSE == mAttrList->Equals(aOther->mAttrList)) { + return PR_FALSE; + } + } + else { + if (nsnull != aOther->mAttrList) { + return PR_FALSE; + } + } + return PR_TRUE; + } } return PR_FALSE; } -void nsCSSSelector::Set(const nsString& aTag, const nsString& aID, - const nsString& aClass, const nsString& aPseudoClass) +void nsCSSSelector::Reset(void) { - nsAutoString buffer; + mNameSpace = kNameSpaceID_Unknown; NS_IF_RELEASE(mTag); NS_IF_RELEASE(mID); - NS_IF_RELEASE(mClass); - NS_IF_RELEASE(mPseudoClass); + NS_IF_DELETE(mClassList); + NS_IF_DELETE(mPseudoClassList); + NS_IF_DELETE(mAttrList); +} + +void nsCSSSelector::SetNameSpace(PRInt32 aNameSpace) +{ + mNameSpace = aNameSpace; +} + +void nsCSSSelector::SetTag(const nsString& aTag) +{ + NS_IF_RELEASE(mTag); if (0 < aTag.Length()) { - aTag.ToUpperCase(buffer); // XXX is this correct? what about class? - mTag = NS_NewAtom(buffer); + mTag = NS_NewAtom(aTag); } +} + +void nsCSSSelector::SetID(const nsString& aID) +{ + NS_IF_RELEASE(mID); if (0 < aID.Length()) { mID = NS_NewAtom(aID); } +} + +void nsCSSSelector::AddClass(const nsString& aClass) +{ if (0 < aClass.Length()) { - mClass = NS_NewAtom(aClass); - } - if (0 < aPseudoClass.Length()) { - aPseudoClass.ToUpperCase(buffer); - mPseudoClass = NS_NewAtom(buffer); - if (nsnull == mTag) { - mTag = nsHTMLAtoms::a; - NS_ADDREF(mTag); + nsAtomList** list = &mClassList; + while (nsnull != *list) { + list = &((*list)->mNext); } + *list = new nsAtomList(aClass); } } +void nsCSSSelector::AddPseudoClass(const nsString& aPseudoClass) +{ + if (0 < aPseudoClass.Length()) { + nsAtomList** list = &mPseudoClassList; + while (nsnull != *list) { + list = &((*list)->mNext); + } + *list = new nsAtomList(aPseudoClass); + } +} + +void nsCSSSelector::AddPseudoClass(nsIAtom* aPseudoClass) +{ + if (nsnull != aPseudoClass) { + nsAtomList** list = &mPseudoClassList; + while (nsnull != *list) { + list = &((*list)->mNext); + } + *list = new nsAtomList(aPseudoClass); + } +} + +void nsCSSSelector::AddAttribute(const nsString& aAttr) +{ + if (0 < aAttr.Length()) { + nsAttrSelector** list = &mAttrList; + while (nsnull != *list) { + list = &((*list)->mNext); + } + *list = new nsAttrSelector(aAttr); + } +} + +void nsCSSSelector::AddAttribute(const nsString& aAttr, PRUint8 aFunc, const nsString& aValue) +{ + if (0 < aAttr.Length()) { + nsAttrSelector** list = &mAttrList; + while (nsnull != *list) { + list = &((*list)->mNext); + } + *list = new nsAttrSelector(aAttr, aFunc, aValue); + } +} + +void nsCSSSelector::SetOperator(PRUnichar aOperator) +{ + mOperator = aOperator; +} + +PRInt32 nsCSSSelector::CalcWeight(void) const +{ + PRInt32 weight = 0; + + if (nsnull != mTag) { + weight += 0x000001; + } + if (nsnull != mID) { + weight += 0x010000; + } + nsAtomList* list = mClassList; + while (nsnull != list) { + weight += 0x000100; + list = list->mNext; + } + list = mPseudoClassList; + while (nsnull != list) { + weight += 0x000100; + list = list->mNext; + } + nsAttrSelector* attr = mAttrList; + while (nsnull != attr) { + weight += 0x000100; + attr = attr->mNext; + } + if (nsnull != mNext) { + weight += mNext->CalcWeight(); + } + return weight; +} + // -- CSSImportantRule ------------------------------- static nscoord CalcLength(const nsCSSValue& aValue, const nsStyleFont* aFont, @@ -365,6 +601,8 @@ public: virtual nsCSSSelector* FirstSelector(void); virtual void AddSelector(const nsCSSSelector& aSelector); virtual void DeleteSelector(nsCSSSelector* aSelector); + virtual void SetSourceSelectorText(const nsString& aSelectorText); + virtual void GetSourceSelectorText(nsString& aSelectorText) const; virtual nsICSSDeclaration* GetDeclaration(void) const; virtual void SetDeclaration(nsICSSDeclaration* aDeclaration); @@ -410,6 +648,7 @@ protected: PRUint32 mRefCnt : 31; nsCSSSelector mSelector; + nsString mSelectorText; nsICSSDeclaration* mDeclaration; PRInt32 mWeight; CSSImportantRule* mImportantRule; @@ -463,7 +702,7 @@ static const PRInt32 kInstrument = 1075; #endif CSSStyleRuleImpl::CSSStyleRuleImpl(const nsCSSSelector& aSelector) - : mSelector(aSelector), mDeclaration(nsnull), + : mSelector(aSelector), mSelectorText(), mDeclaration(nsnull), mWeight(0), mImportantRule(nsnull) { NS_INIT_REFCNT(); @@ -628,16 +867,13 @@ nsCSSSelector* CSSStyleRuleImpl::FirstSelector(void) void CSSStyleRuleImpl::AddSelector(const nsCSSSelector& aSelector) { - if ((nsnull != aSelector.mTag) || (nsnull != aSelector.mID) || - (nsnull != aSelector.mClass) || (nsnull != aSelector.mPseudoClass)) { // skip empty selectors - nsCSSSelector* selector = new nsCSSSelector(aSelector); - nsCSSSelector* last = &mSelector; + nsCSSSelector* selector = new nsCSSSelector(aSelector); + nsCSSSelector* last = &mSelector; - while (nsnull != last->mNext) { - last = last->mNext; - } - last->mNext = selector; + while (nsnull != last->mNext) { + last = last->mNext; } + last->mNext = selector; } @@ -645,9 +881,15 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector) { if (nsnull != aSelector) { if (&mSelector == aSelector) { // handle first selector - mSelector = *aSelector; // assign value - mSelector.mNext = aSelector->mNext; - delete aSelector; + if (nsnull != mSelector.mNext) { + nsCSSSelector* nextOne = mSelector.mNext; + mSelector = *nextOne; // assign values + mSelector.mNext = nextOne->mNext; + delete nextOne; + } + else { + mSelector.Reset(); + } } else { nsCSSSelector* selector = &mSelector; @@ -664,6 +906,17 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector) } } +void CSSStyleRuleImpl::SetSourceSelectorText(const nsString& aSelectorText) +{ + mSelectorText = aSelectorText; +} + +void CSSStyleRuleImpl::GetSourceSelectorText(nsString& aSelectorText) const +{ + aSelectorText = mSelectorText; +} + + nsICSSDeclaration* CSSStyleRuleImpl::GetDeclaration(void) const { NS_IF_ADDREF(mDeclaration); @@ -1056,8 +1309,8 @@ void MapDeclarationInto(nsICSSDeclaration* aDeclaration, text->mTextDecoration = td; } else if (eCSSUnit_None == ourText->mDecoration.GetUnit()) { - font->mFont.decorations = NS_STYLE_TEXT_DECORATION_NONE; - font->mFixedFont.decorations = NS_STYLE_TEXT_DECORATION_NONE; + font->mFont.decorations = parentFont->mFont.decorations; + font->mFixedFont.decorations = parentFont->mFixedFont.decorations; text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE; } else if (eCSSUnit_Inherit == ourText->mDecoration.GetUnit()) { @@ -1695,6 +1948,11 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector) { nsAutoString buffer; + if (kNameSpaceID_None < aSelector->mNameSpace) { + buffer.Append(aSelector->mNameSpace, 10); + fputs(buffer, out); + fputs("\\:", out); + } if (nsnull != aSelector->mTag) { aSelector->mTag->ToString(buffer); fputs(buffer, out); @@ -1704,15 +1962,39 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector) fputs("#", out); fputs(buffer, out); } - if (nsnull != aSelector->mClass) { - aSelector->mClass->ToString(buffer); + nsAtomList* list = aSelector->mClassList; + while (nsnull != list) { + list->mAtom->ToString(buffer); fputs(".", out); fputs(buffer, out); + list = list->mNext; } - if (nsnull != aSelector->mPseudoClass) { - aSelector->mPseudoClass->ToString(buffer); + list = aSelector->mPseudoClassList; + while (nsnull != list) { + list->mAtom->ToString(buffer); fputs(":", out); fputs(buffer, out); + list = list->mNext; + } + nsAttrSelector* attr = aSelector->mAttrList; + while (nsnull != attr) { + fputs("[", out); + attr->mAttr->ToString(buffer); + fputs(buffer, out); + if (NS_ATTR_FUNC_SET != attr->mFunction) { + switch (attr->mFunction) { + case NS_ATTR_FUNC_EQUALS: fputs("=", out); break; + case NS_ATTR_FUNC_INCLUDES: fputs("~=", out); break; + case NS_ATTR_FUNC_DASHMATCH: fputs("|=", out); break; + } + fputs(attr->mValue, out); + } + fputs("]", out); + } + if (0 != aSelector->mOperator) { + buffer = aSelector->mOperator; + buffer.Append(" "); + fputs(buffer, out); } } @@ -1782,45 +2064,16 @@ CSSStyleRuleImpl::GetSheet(nsIDOMCSSStyleSheet** aSheet) NS_IMETHODIMP CSSStyleRuleImpl::GetSelectorText(nsString& aSelectorText) { - nsAutoString buffer; - nsAutoString thisSelector; - nsCSSSelector *selector = &mSelector; - - // XXX Ugh...they're in reverse order from the source. Sorry - // for the ugliness. - aSelectorText.SetLength(0); - while (nsnull != selector) { - thisSelector.SetLength(0); - if (nsnull != selector->mTag) { - selector->mTag->ToString(buffer); - thisSelector.Append(buffer); - } - if (nsnull != selector->mID) { - selector->mID->ToString(buffer); - thisSelector.Append("#"); - thisSelector.Append(buffer); - } - if (nsnull != selector->mClass) { - selector->mClass->ToString(buffer); - thisSelector.Append("."); - thisSelector.Append(buffer); - } - if (nsnull != selector->mPseudoClass) { - selector->mPseudoClass->ToString(buffer); - thisSelector.Append(":"); - thisSelector.Append(buffer); - } - aSelectorText.Insert(thisSelector, 0, thisSelector.Length()); - selector = selector->mNext; - } - + aSelectorText = mSelectorText; return NS_OK; } NS_IMETHODIMP CSSStyleRuleImpl::SetSelectorText(const nsString& aSelectorText) { - // XXX TBI + // XXX TBI - get a parser and re-parse the selectors, + // XXX then need to re-compute the cascade + mSelectorText = aSelectorText; return NS_OK; } diff --git a/mozilla/layout/style/nsCSSStyleSheet.cpp b/mozilla/layout/style/nsCSSStyleSheet.cpp index 2cc0bd32cb0..d3dbd968918 100644 --- a/mozilla/layout/style/nsCSSStyleSheet.cpp +++ b/mozilla/layout/style/nsCSSStyleSheet.cpp @@ -42,10 +42,12 @@ #include "nsIScriptObjectOwner.h" #include "nsIScriptGlobalObject.h" #include "nsICSSParser.h" +#include "nsCSSAtoms.h" +#include "nsINameSpaceManager.h" #include "prlog.h" //#define DEBUG_REFS -//#define DEBUG_RULES +#define DEBUG_RULES static NS_DEFINE_IID(kICSSStyleSheetIID, NS_ICSS_STYLE_SHEET_IID); static NS_DEFINE_IID(kIStyleSheetIID, NS_ISTYLE_SHEET_IID); @@ -120,8 +122,8 @@ struct RuleValue { } ~RuleValue(void) { - if (nsnull != mNext) { - delete mNext; + if ((nsnull != mNext) && (nsnull != mNext->mNext)) { + delete mNext; // don't delete that last in the chain, it's shared } } @@ -142,7 +144,7 @@ public: RuleHash(void); ~RuleHash(void); void AppendRule(nsICSSStyleRule* aRule); - void EnumerateAllRules(nsIAtom* aTag, nsIAtom* aID, nsIAtom* aClass, + void EnumerateAllRules(nsIAtom* aTag, nsIAtom* aID, const nsVoidArray& aClassList, RuleEnumFunc aFunc, void* aData); void EnumerateTagRules(nsIAtom* aTag, RuleEnumFunc aFunc, void* aData); @@ -154,11 +156,17 @@ protected: nsHashtable mTagTable; nsHashtable mIdTable; nsHashtable mClassTable; + + RuleValue** mEnumList; + PRInt32 mEnumListSize; + RuleValue mEndValue; }; RuleHash::RuleHash(void) : mRuleCount(0), - mTagTable(), mIdTable(), mClassTable() + mTagTable(), mIdTable(), mClassTable(), + mEnumList(nsnull), mEnumListSize(0), + mEndValue(nsnull, -1) { } @@ -173,23 +181,31 @@ RuleHash::~RuleHash(void) mTagTable.Enumerate(DeleteValue); mIdTable.Enumerate(DeleteValue); mClassTable.Enumerate(DeleteValue); + if (nsnull != mEnumList) { + delete [] mEnumList; + } } void RuleHash::AppendRuleToTable(nsHashtable& aTable, nsIAtom* aAtom, nsICSSStyleRule* aRule) { + NS_ASSERTION(nsnull != aAtom, "null hash key"); + RuleKey key(aAtom); RuleValue* value = (RuleValue*)aTable.Get(&key); if (nsnull == value) { value = new RuleValue(aRule, mRuleCount++); aTable.Put(&key, value); + value->mNext = &mEndValue; } else { - while (nsnull != value->mNext) { + while (&mEndValue != value->mNext) { value = value->mNext; } value->mNext = new RuleValue(aRule, mRuleCount++); + value->mNext->mNext = &mEndValue; } + mEndValue.mIndex = mRuleCount; } void RuleHash::AppendRule(nsICSSStyleRule* aRule) @@ -198,65 +214,128 @@ void RuleHash::AppendRule(nsICSSStyleRule* aRule) if (nsnull != selector->mID) { AppendRuleToTable(mIdTable, selector->mID, aRule); } - else if (nsnull != selector->mClass) { - AppendRuleToTable(mClassTable, selector->mClass, aRule); + else if (nsnull != selector->mClassList) { + AppendRuleToTable(mClassTable, selector->mClassList->mAtom, aRule); } else if (nsnull != selector->mTag) { AppendRuleToTable(mTagTable, selector->mTag, aRule); } + else { // universal tag selector + AppendRuleToTable(mTagTable, nsCSSAtoms::universalSelector, aRule); + } } -void RuleHash::EnumerateAllRules(nsIAtom* aTag, nsIAtom* aID, nsIAtom* aClass, +void RuleHash::EnumerateAllRules(nsIAtom* aTag, nsIAtom* aID, const nsVoidArray& aClassList, RuleEnumFunc aFunc, void* aData) { - RuleValue* tagValue = nsnull; - RuleValue* idValue = nsnull; - RuleValue* classValue = nsnull; - + PRInt32 classCount = aClassList.Count(); + PRInt32 testCount = classCount + 1; // + 1 for universal selector if (nsnull != aTag) { - RuleKey tagKey(aTag); - tagValue = (RuleValue*)mTagTable.Get(&tagKey); + testCount++; } if (nsnull != aID) { - RuleKey idKey(aID); - idValue = (RuleValue*)mIdTable.Get(&idKey); - } - if (nsnull != aClass) { - RuleKey classKey(aClass); - classValue = (RuleValue*)mClassTable.Get(&classKey); + testCount++; } - PRInt32 tagIndex = ((nsnull != tagValue) ? tagValue->mIndex : mRuleCount); - PRInt32 idIndex = ((nsnull != idValue) ? idValue->mIndex : mRuleCount); - PRInt32 classIndex = ((nsnull != classValue) ? classValue->mIndex : mRuleCount); + if (mEnumListSize < testCount) { + delete [] mEnumList; + mEnumList = new RuleValue*[testCount]; + mEnumListSize = testCount; + } - while ((nsnull != tagValue) || (nsnull != idValue) || (nsnull != classValue)) { - if ((tagIndex < idIndex) && (tagIndex < classIndex)) { - (*aFunc)(tagValue->mRule, aData); - tagValue = tagValue->mNext; - tagIndex = ((nsnull != tagValue) ? tagValue->mIndex : mRuleCount); + PRInt32 index; + PRInt32 valueCount = 0; + + { // universal tag rules + RuleValue* value = (RuleValue*)mTagTable.Get(&RuleKey(nsCSSAtoms::universalSelector)); + if (nsnull != value) { + mEnumList[valueCount++] = value; } - else if (idIndex < classIndex) { - (*aFunc)(idValue->mRule, aData); - idValue = idValue->mNext; - idIndex = ((nsnull != idValue) ? idValue->mIndex : mRuleCount); + } + if (nsnull != aTag) { + RuleValue* value = (RuleValue*)mTagTable.Get(&RuleKey(aTag)); + if (nsnull != value) { + mEnumList[valueCount++] = value; } - else { - (*aFunc)(classValue->mRule, aData); - classValue = classValue->mNext; - classIndex = ((nsnull != classValue) ? classValue->mIndex : mRuleCount); + } + if (nsnull != aID) { + RuleValue* value = (RuleValue*)mIdTable.Get(&RuleKey(aID)); + if (nsnull != value) { + mEnumList[valueCount++] = value; } } + for (index = 0; index < classCount; index++) { + nsIAtom* classAtom = (nsIAtom*)aClassList.ElementAt(index); + RuleValue* value = (RuleValue*)mClassTable.Get(&RuleKey(classAtom)); + if (nsnull != value) { + mEnumList[valueCount++] = value; + } + } + NS_ASSERTION(valueCount <= testCount, "values exceeded list size"); + + if (1 < valueCount) { + PRInt32 ruleIndex = mRuleCount; + PRInt32 valueIndex = -1; + + for (index = 0; index < valueCount; index++) { + if (mEnumList[index]->mIndex < ruleIndex) { + ruleIndex = mEnumList[index]->mIndex; + valueIndex = index; + } + } + do { + (*aFunc)(mEnumList[valueIndex]->mRule, aData); + mEnumList[valueIndex] = mEnumList[valueIndex]->mNext; + ruleIndex = mEnumList[valueIndex]->mIndex; + for (index = 0; index < valueCount; index++) { + if (mEnumList[index]->mIndex < ruleIndex) { + ruleIndex = mEnumList[index]->mIndex; + valueIndex = index; + } + } + } while (ruleIndex < mRuleCount); + } + else if (0 < valueCount) { // fast loop over single value + RuleValue* value = mEnumList[0]; + do { + (*aFunc)(value->mRule, aData); + value = value->mNext; + } while (&mEndValue != value); + } } void RuleHash::EnumerateTagRules(nsIAtom* aTag, RuleEnumFunc aFunc, void* aData) { - RuleKey tagKey(aTag); - RuleValue* value = (RuleValue*)mTagTable.Get(&tagKey); + RuleValue* tagValue = (RuleValue*)mTagTable.Get(&RuleKey(aTag)); + RuleValue* uniValue = (RuleValue*)mTagTable.Get(&RuleKey(nsCSSAtoms::universalSelector)); - while (nsnull != value) { - (*aFunc)(value->mRule, aData); - value = value->mNext; + if (nsnull == tagValue) { + if (nsnull != uniValue) { + do { + (*aFunc)(uniValue->mRule, aData); + uniValue = uniValue->mNext; + } while (&mEndValue != uniValue); + } + } + else { + if (nsnull == uniValue) { + do { + (*aFunc)(tagValue->mRule, aData); + tagValue = tagValue->mNext; + } while (&mEndValue != tagValue); + } + else { + do { + if (tagValue->mIndex < uniValue->mIndex) { + (*aFunc)(tagValue->mRule, aData); + tagValue = tagValue->mNext; + } + else { + (*aFunc)(uniValue->mRule, aData); + uniValue = uniValue->mNext; + } + } while ((&mEndValue != tagValue) || (&mEndValue != uniValue)); + } } } @@ -691,6 +770,8 @@ CSSStyleSheetImpl::CSSStyleSheetImpl() mRuleHash(nsnull) { NS_INIT_REFCNT(); + nsCSSAtoms::AddrefAtoms(); + mParent = nsnull; mRuleCollection = nsnull; mImportsCollection = nsnull; @@ -744,6 +825,7 @@ CSSStyleSheetImpl::~CSSStyleSheetImpl() // XXX The document reference is not reference counted and should // not be released. The document will let us know when it is going // away. + nsCSSAtoms::ReleaseAtoms(); } NS_IMPL_ADDREF(CSSStyleSheetImpl) @@ -801,76 +883,87 @@ static PRBool SelectorMatches(nsIPresContext* aPresContext, PRBool result = PR_FALSE; nsIAtom* contentTag; aContent->GetTag(contentTag); + PRInt32 nameSpaceID; + aContent->GetNameSpaceID(nameSpaceID); - if ((nsnull == aSelector->mTag) || (aSelector->mTag == contentTag)) { - if ((nsnull != aSelector->mClass) || (nsnull != aSelector->mID) || - (nsnull != aSelector->mPseudoClass)) { + if (((nsnull == aSelector->mTag) || (aSelector->mTag == contentTag)) && + ((kNameSpaceID_Unknown == aSelector->mNameSpace) || (nameSpaceID == aSelector->mNameSpace))) { + result = PR_TRUE; + // namespace/tag match + if (nsnull != aSelector->mAttrList) { // test for attribute match + } + if ((PR_TRUE == result) && + ((nsnull != aSelector->mID) || (nsnull != aSelector->mClassList))) { // test for ID & class match + result = PR_FALSE; nsIHTMLContent* htmlContent; - if (NS_OK == aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent)) { - nsIAtom* contentClass; - htmlContent->GetClass(contentClass); + if (NS_SUCCEEDED(aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent))) { nsIAtom* contentID; htmlContent->GetID(contentID); - if ((nsnull == aSelector->mClass) || (aSelector->mClass == contentClass)) { - if ((nsnull == aSelector->mID) || (aSelector->mID == contentID)) { - if ((contentTag == nsHTMLAtoms::a) && (nsnull != aSelector->mPseudoClass)) { - // test link state - nsILinkHandler* linkHandler; - - if ((NS_OK == aPresContext->GetLinkHandler(&linkHandler)) && - (nsnull != linkHandler)) { - nsAutoString base, href; // XXX base?? - nsresult attrState = htmlContent->GetAttribute("href", href); - - if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { - nsIURL* docURL = nsnull; - nsIDocument* doc = nsnull; - aContent->GetDocument(doc); - if (nsnull != doc) { - docURL = doc->GetDocumentURL(); - NS_RELEASE(doc); - } - - nsAutoString absURLSpec; - nsresult rv = NS_MakeAbsoluteURL(docURL, base, href, absURLSpec); - NS_IF_RELEASE(docURL); - - nsLinkState state; - if (NS_OK == linkHandler->GetLinkState(absURLSpec, state)) { - switch (state) { - case eLinkState_Unvisited: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::link); - break; - case eLinkState_Visited: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::visited); - break; - case eLinkState_OutOfDate: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::outOfDate); - break; - case eLinkState_Active: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::active); - break; - case eLinkState_Hover: - result = PRBool (aSelector->mPseudoClass == nsHTMLAtoms::hover); - break; - } - } - } - NS_RELEASE(linkHandler); - } - } - else { - result = PR_TRUE; - } + if ((nsnull == aSelector->mID) || (aSelector->mID == contentID)) { + nsIAtom* contentClass; + htmlContent->GetClass(contentClass); + // XXX only testing for frist class right now + if ((nsnull == aSelector->mClassList) || (aSelector->mClassList->mAtom == contentClass)) { + result = PR_TRUE; } + NS_IF_RELEASE(contentClass); } NS_RELEASE(htmlContent); - NS_IF_RELEASE(contentClass); NS_IF_RELEASE(contentID); } } - else { - result = PR_TRUE; + if ((PR_TRUE == result) && + (nsnull != aSelector->mPseudoClassList)) { // test for pseudo class match + result = PR_FALSE; + // XXX only testing for anchor pseudo classes right now + // XXX only testing first pseudo class right now + if ((contentTag == nsHTMLAtoms::a) && (nsnull != aSelector->mPseudoClassList)) { + // test link state + nsILinkHandler* linkHandler; + + if ((NS_OK == aPresContext->GetLinkHandler(&linkHandler)) && + (nsnull != linkHandler)) { + nsAutoString base, href; // XXX base?? + nsresult attrState = aContent->GetAttribute("href", href); + + if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { + nsIURL* docURL = nsnull; + nsIDocument* doc = nsnull; + aContent->GetDocument(doc); + if (nsnull != doc) { + docURL = doc->GetDocumentURL(); + NS_RELEASE(doc); + } + + nsAutoString absURLSpec; + nsresult rv = NS_MakeAbsoluteURL(docURL, base, href, absURLSpec); + NS_IF_RELEASE(docURL); + + nsIAtom* pseudo = aSelector->mPseudoClassList->mAtom; + nsLinkState state; + if (NS_OK == linkHandler->GetLinkState(absURLSpec, state)) { + switch (state) { + case eLinkState_Unvisited: + result = PRBool (pseudo == nsCSSAtoms::linkPseudo); + break; + case eLinkState_Visited: + result = PRBool (pseudo == nsCSSAtoms::visitedPseudo); + break; + case eLinkState_OutOfDate: + result = PRBool (pseudo == nsCSSAtoms::outOfDatePseudo); + break; + case eLinkState_Active: + result = PRBool (pseudo == nsCSSAtoms::activePseudo); + break; + case eLinkState_Hover: + result = PRBool (pseudo == nsCSSAtoms::hoverPseudo); + break; + } + } + } + NS_RELEASE(linkHandler); + } + } } } NS_IF_RELEASE(contentTag); @@ -899,6 +992,8 @@ static void ContentEnumFunc(nsICSSStyleRule* aRule, void* aData) { ContentEnumData* data = (ContentEnumData*)aData; + // XXX not dealing with selector functions... + nsCSSSelector* selector = aRule->FirstSelector(); if (SelectorMatches(data->mPresContext, selector, data->mContent)) { selector = selector->mNext; @@ -995,7 +1090,13 @@ PRInt32 CSSStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext, NS_RELEASE(htmlContent); } - mRuleHash->EnumerateAllRules(tagAtom, idAtom, classAtom, ContentEnumFunc, &data); + nsVoidArray classArray; // XXX need to recycle this guy + + if (nsnull != classAtom) { + classArray.AppendElement(classAtom); + } + // XXX need to handle multiple classes + mRuleHash->EnumerateAllRules(tagAtom, idAtom, classArray, ContentEnumFunc, &data); matchCount += data.mCount; #ifdef DEBUG_RULES @@ -1005,7 +1106,7 @@ PRInt32 CSSStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext, NS_NewISupportsArray(&list2); data.mResults = list1; - mRuleHash->EnumerateAllRules(tagAtom, idAtom, classAtom, ContentEnumFunc, &data); + mRuleHash->EnumerateAllRules(tagAtom, idAtom, classArray, ContentEnumFunc, &data); data.mResults = list2; mWeightedRules->EnumerateBackwards(ContentEnumWrap, &data); NS_ASSERTION(list1->Equals(list2), "lists not equal");