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
- *
- * - Tag
- *
- Tag#Id
- *
- Tag#Id.Class[*]
- *
- Tag#Id.Class[*]:PseudoClass[*]
- *
- Tag#Id.Class[*]:PseudoClass[*]:PseudoElement
- *
- Tag#Id.Class[*]:PseudoElement
- *
- Tag#Id:PseudoClass[*]
- *
- Tag#Id:PseudoClass[*]:PseudoElement
- *
- Tag#Id:PseudoElement
- *
- Tag.Class[*]
- *
- Tag.Class[*]:PseudoClass[*]
- *
- Tag.Class[*]:PseudoClass[*]:PseudoElement
- *
- Tag.Class[*]:PseudoElement
- *
- Tag:PseudoClass[*]
- *
- Tag:PseudoClass[*]:PseudoElement
- *
- Tag:PseudoElement
- *
- #Id
- *
- #Id.Class[*]
- *
- #Id.Class[*]:PseudoClass[*]
- *
- #Id.Class[*]:PseudoClass[*]:PseudoElement
- *
- #Id.Class[*]:PseudoElement
- *
- #Id:PseudoClass[*]
- *
- #Id:PseudoClass[*]:PseudoElement
- *
- #Id:PseudoElement
- *
- .Class[*]
- *
- .Class[*]:PseudoClass[*]
- *
- .Class[*]:PseudoClass[*]:PseudoElement
- *
- .Class[*]:PseudoElement
- *
- :PseudoClass[*]
- *
- :PseudoClass[*]:PseudoElement
- *
- :PseudoElement
- *
+ * 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
- *
- * - Tag
- *
- Tag#Id
- *
- Tag#Id.Class[*]
- *
- Tag#Id.Class[*]:PseudoClass[*]
- *
- Tag#Id.Class[*]:PseudoClass[*]:PseudoElement
- *
- Tag#Id.Class[*]:PseudoElement
- *
- Tag#Id:PseudoClass[*]
- *
- Tag#Id:PseudoClass[*]:PseudoElement
- *
- Tag#Id:PseudoElement
- *
- Tag.Class[*]
- *
- Tag.Class[*]:PseudoClass[*]
- *
- Tag.Class[*]:PseudoClass[*]:PseudoElement
- *
- Tag.Class[*]:PseudoElement
- *
- Tag:PseudoClass[*]
- *
- Tag:PseudoClass[*]:PseudoElement
- *
- Tag:PseudoElement
- *
- #Id
- *
- #Id.Class[*]
- *
- #Id.Class[*]:PseudoClass[*]
- *
- #Id.Class[*]:PseudoClass[*]:PseudoElement
- *
- #Id.Class[*]:PseudoElement
- *
- #Id:PseudoClass[*]
- *
- #Id:PseudoClass[*]:PseudoElement
- *
- #Id:PseudoElement
- *
- .Class[*]
- *
- .Class[*]:PseudoClass[*]
- *
- .Class[*]:PseudoClass[*]:PseudoElement
- *
- .Class[*]:PseudoElement
- *
- :PseudoClass[*]
- *
- :PseudoClass[*]:PseudoElement
- *
- :PseudoElement
- *
+ * 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
- *
- * - Tag
- *
- Tag#Id
- *
- Tag#Id.Class[*]
- *
- Tag#Id.Class[*]:PseudoClass[*]
- *
- Tag#Id.Class[*]:PseudoClass[*]:PseudoElement
- *
- Tag#Id.Class[*]:PseudoElement
- *
- Tag#Id:PseudoClass[*]
- *
- Tag#Id:PseudoClass[*]:PseudoElement
- *
- Tag#Id:PseudoElement
- *
- Tag.Class[*]
- *
- Tag.Class[*]:PseudoClass[*]
- *
- Tag.Class[*]:PseudoClass[*]:PseudoElement
- *
- Tag.Class[*]:PseudoElement
- *
- Tag:PseudoClass[*]
- *
- Tag:PseudoClass[*]:PseudoElement
- *
- Tag:PseudoElement
- *
- #Id
- *
- #Id.Class[*]
- *
- #Id.Class[*]:PseudoClass[*]
- *
- #Id.Class[*]:PseudoClass[*]:PseudoElement
- *
- #Id.Class[*]:PseudoElement
- *
- #Id:PseudoClass[*]
- *
- #Id:PseudoClass[*]:PseudoElement
- *
- #Id:PseudoElement
- *
- .Class[*]
- *
- .Class[*]:PseudoClass[*]
- *
- .Class[*]:PseudoClass[*]:PseudoElement
- *
- .Class[*]:PseudoElement
- *
- :PseudoClass[*]
- *
- :PseudoClass[*]:PseudoElement
- *
- :PseudoElement
- *
+ * 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");