added support for CSS2 selector syntax

This commit is contained in:
peterl%netscape.com 1998-12-11 02:50:43 +00:00
Родитель 846697e4a9
Коммит e0ce171455
9 изменённых файлов: 2652 добавлений и 1488 удалений

Просмотреть файл

@ -31,6 +31,9 @@
#include "nsVoidArray.h" #include "nsVoidArray.h"
#include "nsColor.h" #include "nsColor.h"
#include "nsStyleConsts.h" #include "nsStyleConsts.h"
#include "nsCSSAtoms.h"
#include "nsINameSpaceManager.h"
#include "nsINameSpace.h"
// XXX TODO: // XXX TODO:
// - rework aErrorCode stuff: switch over to nsresult // - 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 #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, // e.g. "P B, H1 B { ... }" has a selector list with two elements,
// each of which has two selectors. // each of which has two selectors.
struct SelectorList { struct SelectorList {
SelectorList* mNext; SelectorList(void);
nsVoidArray mSelectors; ~SelectorList(void);
SelectorList(); void AddSelector(const nsCSSSelector& aSelector);
void Destroy();
void AddSelector(Selector* aSelector) {
mSelectors.AppendElement(aSelector);
}
#ifdef NS_DEBUG #ifdef NS_DEBUG
void Dump(); void Dump(void);
#endif #endif
private: nsCSSSelector* mSelectors;
~SelectorList(); SelectorList* mNext;
}; };
SelectorList::SelectorList() SelectorList::SelectorList(void)
: mSelectors(nsnull),
mNext(nsnull)
{ {
mNext = nsnull;
} }
SelectorList::~SelectorList() SelectorList::~SelectorList()
{ {
PRInt32 n = mSelectors.Count(); nsCSSSelector* sel = mSelectors;
for (PRInt32 i = 0; i < n; i++) { while (nsnull != sel) {
Selector* sel = (Selector*) mSelectors.ElementAt(i); nsCSSSelector* dead = sel;
delete sel; sel = sel->mNext;
delete dead;
}
if (nsnull != mNext) {
delete mNext;
} }
} }
void SelectorList::Destroy() void SelectorList::AddSelector(const nsCSSSelector& aSelector)
{ { // prepend to list
SelectorList* list = this; nsCSSSelector* newSel = new nsCSSSelector(aSelector);
while (nsnull != list) { newSel->mNext = mSelectors;
SelectorList* next = list->mNext; mSelectors = newSel;
delete list;
list = next;
}
} }
#ifdef NS_DEBUG #ifdef NS_DEBUG
void SelectorList::Dump() 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 #endif
@ -200,6 +113,8 @@ public:
NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet); NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet);
NS_IMETHOD SetCaseSensative(PRBool aCaseSensative);
NS_IMETHOD Parse(nsIUnicharInputStream* aInput, NS_IMETHOD Parse(nsIUnicharInputStream* aInput,
nsIURL* aInputURL, nsIURL* aInputURL,
nsICSSStyleSheet*& aResult); nsICSSStyleSheet*& aResult);
@ -231,9 +146,9 @@ protected:
PRBool GatherMedia(PRInt32& aErrorCode, nsString& aMedia); PRBool GatherMedia(PRInt32& aErrorCode, nsString& aMedia);
PRBool ProcessImport(PRInt32& aErrorCode, const nsString& aURLSpec, const 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 ParseSelectorList(PRInt32& aErrorCode, SelectorList* aListHead); PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList*& aListHead);
PRBool ParseSelector(PRInt32& aErrorCode, Selector* aSelectorResult); PRBool ParseSelector(PRInt32& aErrorCode, nsCSSSelector& aSelectorResult);
nsICSSDeclaration* ParseDeclarationBlock(PRInt32& aErrorCode, nsICSSDeclaration* ParseDeclarationBlock(PRInt32& aErrorCode,
PRBool aCheckForBraces); PRBool aCheckForBraces);
PRBool ParseDeclaration(PRInt32& aErrorCode, PRBool ParseDeclaration(PRInt32& aErrorCode,
@ -317,6 +232,7 @@ protected:
PRBool mInHead; PRBool mInHead;
PRBool mNavQuirkMode; PRBool mNavQuirkMode;
PRBool mCaseSensative;
}; };
NS_HTML nsresult NS_HTML nsresult
@ -334,19 +250,23 @@ NS_NewCSSParser(nsICSSParser** aInstancePtrResult)
CSSParserImpl::CSSParserImpl() CSSParserImpl::CSSParserImpl()
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
nsCSSAtoms::AddrefAtoms();
mScanner = nsnull; mScanner = nsnull;
mSheet = nsnull; mSheet = nsnull;
mHavePushBack = PR_FALSE; mHavePushBack = PR_FALSE;
mNavQuirkMode = PR_TRUE; mNavQuirkMode = PR_TRUE;
mCaseSensative = PR_FALSE;
} }
CSSParserImpl::CSSParserImpl(nsICSSStyleSheet* aSheet) CSSParserImpl::CSSParserImpl(nsICSSStyleSheet* aSheet)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
nsCSSAtoms::AddrefAtoms();
mScanner = nsnull; mScanner = nsnull;
mSheet = aSheet; NS_ADDREF(aSheet); mSheet = aSheet; NS_ADDREF(aSheet);
mHavePushBack = PR_FALSE; mHavePushBack = PR_FALSE;
mNavQuirkMode = PR_TRUE; mNavQuirkMode = PR_TRUE;
mCaseSensative = PR_FALSE;
} }
NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID) NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID)
@ -354,16 +274,17 @@ NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID)
CSSParserImpl::~CSSParserImpl() CSSParserImpl::~CSSParserImpl()
{ {
NS_IF_RELEASE(mSheet); NS_IF_RELEASE(mSheet);
nsCSSAtoms::ReleaseAtoms();
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::GetInfoMask(PRUint32& aResult) CSSParserImpl::GetInfoMask(PRUint32& aResult)
{ {
aResult = NS_CSS_GETINFO_CSS1 | NS_CSS_GETINFO_CSSP; aResult = NS_CSS_GETINFO_CSS1 | NS_CSS_GETINFO_CSSP;
return NS_OK; return NS_OK;
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet) CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet)
{ {
NS_PRECONDITION(nsnull != aSheet, "null ptr"); NS_PRECONDITION(nsnull != aSheet, "null ptr");
@ -381,7 +302,15 @@ CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet)
return NS_OK; return NS_OK;
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::SetCaseSensative(PRBool aCaseSensative)
{
mCaseSensative = aCaseSensative;
return NS_OK;
}
NS_IMETHODIMP
CSSParserImpl::Parse(nsIUnicharInputStream* aInput, CSSParserImpl::Parse(nsIUnicharInputStream* aInput,
nsIURL* aInputURL, nsIURL* aInputURL,
nsICSSStyleSheet*& aResult) nsICSSStyleSheet*& aResult)
@ -426,7 +355,7 @@ CSSParserImpl::Parse(nsIUnicharInputStream* aInput,
return NS_OK; return NS_OK;
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::ParseDeclarations(const nsString& aDeclaration, CSSParserImpl::ParseDeclarations(const nsString& aDeclaration,
nsIURL* aBaseURL, nsIURL* aBaseURL,
nsIStyleRule*& aResult) nsIStyleRule*& aResult)
@ -471,7 +400,7 @@ CSSParserImpl::ParseDeclarations(const nsString& aDeclaration,
return NS_OK; return NS_OK;
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::ParseAndAppendDeclaration(const nsString& aBuffer, CSSParserImpl::ParseAndAppendDeclaration(const nsString& aBuffer,
nsIURL* aBaseURL, nsIURL* aBaseURL,
nsICSSDeclaration* aDeclaration, nsICSSDeclaration* aDeclaration,
@ -925,18 +854,18 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode)
nsCSSToken* tk = &mToken; nsCSSToken* tk = &mToken;
// First get the list of selectors for the rule // First get the list of selectors for the rule
SelectorList *slist = new SelectorList(); SelectorList* slist = nsnull;
if (!ParseSelectorList(aErrorCode, slist)) { if (! ParseSelectorList(aErrorCode, slist)) {
SkipRuleSet(aErrorCode); SkipRuleSet(aErrorCode);
slist->Destroy();
return PR_FALSE; return PR_FALSE;
} }
NS_ASSERTION(nsnull != slist, "null selector list");
// Next parse the declaration block // Next parse the declaration block
nsICSSDeclaration* declaration = ParseDeclarationBlock(aErrorCode, PR_TRUE); nsICSSDeclaration* declaration = ParseDeclarationBlock(aErrorCode, PR_TRUE);
if (nsnull == declaration) { if (nsnull == declaration) {
// XXX skip something here // XXX skip something here
slist->Destroy(); delete slist;
return PR_FALSE; return PR_FALSE;
} }
@ -950,34 +879,16 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode)
// Translate the selector list and declaration block into style data // Translate the selector list and declaration block into style data
SelectorList* list = slist; SelectorList* list = slist;
nsCSSSelector selector;
nsICSSStyleRule* rule = nsnull;
while (nsnull != list) { while (nsnull != list) {
PRInt32 selIndex = list->mSelectors.Count(); PRInt32 weight = list->mSelectors->CalcWeight();
nsICSSStyleRule* rule = nsnull;
Selector* sel = (Selector*)list->mSelectors[--selIndex]; NS_NewCSSStyleRule(&rule, *(list->mSelectors));
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);
}
if (nsnull != rule) { if (nsnull != rule) {
while (--selIndex >= 0) { if (nsnull != list->mSelectors->mNext) { // hand off other selectors to new rule
Selector* sel = (Selector*)list->mSelectors[selIndex]; nsCSSSelector* ruleFirst = rule->FirstSelector();
selector.Set(sel->mTag, sel->mID, sel->mClass, sel->mPseudoClass); ruleFirst->mNext = list->mSelectors->mNext;
list->mSelectors->mNext = nsnull;
rule->AddSelector(selector);
weight += sel->Weight();
} }
rule->SetDeclaration(declaration); rule->SetDeclaration(declaration);
rule->SetWeight(weight); rule->SetWeight(weight);
@ -990,219 +901,342 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode)
} }
// Release temporary storage // Release temporary storage
slist->Destroy(); delete slist;
NS_RELEASE(declaration); NS_RELEASE(declaration);
return PR_TRUE; return PR_TRUE;
} }
PRBool CSSParserImpl::ParseSelectorList(PRInt32& aErrorCode, 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 // must have at least one selector group
aListHead = nsnull;
return PR_FALSE; return PR_FALSE;
} }
NS_ASSERTION(nsnull != list, "no selector list");
aListHead = list;
// After that there must either be a "," or a "{" // After that there must either be a "," or a "{"
nsCSSToken* tk = &mToken; nsCSSToken* tk = &mToken;
for (;;) { for (;;) {
if (!GetToken(aErrorCode, PR_TRUE)) { if (! GetToken(aErrorCode, PR_TRUE)) {
return PR_FALSE; break;
} }
if (eCSSToken_Symbol != tk->mType) { if (eCSSToken_Symbol != tk->mType) {
UngetToken(); UngetToken();
return PR_FALSE; break;
} }
if (',' == tk->mSymbol) { if (',' == tk->mSymbol) {
SelectorList* newList = nsnull;
// Another selector group must follow // Another selector group must follow
SelectorList* newList = new SelectorList(); if (! ParseSelectorGroup(aErrorCode, newList)) {
if (!ParseSelectorGroup(aErrorCode, newList)) { break;
newList->Destroy();
return PR_FALSE;
} }
// add new list to the end of the selector list // add new list to the end of the selector list
aListHead->mNext = newList; list->mNext = newList;
aListHead = newList; list = newList;
continue; continue;
} else if ('{' == tk->mSymbol) { } else if ('{' == tk->mSymbol) {
UngetToken(); UngetToken();
break; return PR_TRUE;
} else { } else {
UngetToken(); 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, PRBool CSSParserImpl::ParseSelectorGroup(PRInt32& aErrorCode,
SelectorList* aList) SelectorList*& aList)
{ {
SelectorList* list = nsnull;
for (;;) { for (;;) {
Selector* sel = new Selector(); nsCSSSelector selector;
if (!ParseSelector(aErrorCode, sel)) { if (! ParseSelector(aErrorCode, selector)) {
delete sel;
break; 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) #define SEL_MASK_NSPACE 0x01
{ #define SEL_MASK_ELEM 0x02
return (aBuffer.EqualsIgnoreCase("link") || #define SEL_MASK_ID 0x04
aBuffer.EqualsIgnoreCase("visited") || #define SEL_MASK_CLASS 0x08
aBuffer.EqualsIgnoreCase("hover") || #define SEL_MASK_ATTRIB 0x10
aBuffer.EqualsIgnoreCase("out-of-date") || // our extension #define SEL_MASK_PCLASS 0x20
aBuffer.EqualsIgnoreCase("active")); #define SEL_MASK_PELEM 0x40
}
/** /**
* These are the 31 possible kinds of CSS1 style selectors: * This is the format for selectors:
* (but there are 50 ways to leave your lover) * operator? [namespace \:]? element_name? [ ID | class | attrib | pseudo ]*
* [*] means it can repeat
* <UL>
* <LI>Tag
* <LI>Tag#Id
* <LI>Tag#Id.Class[*]
* <LI>Tag#Id.Class[*]:PseudoClass[*]
* <LI>Tag#Id.Class[*]:PseudoClass[*]:PseudoElement
* <LI>Tag#Id.Class[*]:PseudoElement
* <LI>Tag#Id:PseudoClass[*]
* <LI>Tag#Id:PseudoClass[*]:PseudoElement
* <LI>Tag#Id:PseudoElement
* <LI>Tag.Class[*]
* <LI>Tag.Class[*]:PseudoClass[*]
* <LI>Tag.Class[*]:PseudoClass[*]:PseudoElement
* <LI>Tag.Class[*]:PseudoElement
* <LI>Tag:PseudoClass[*]
* <LI>Tag:PseudoClass[*]:PseudoElement
* <LI>Tag:PseudoElement
* <LI>#Id
* <LI>#Id.Class[*]
* <LI>#Id.Class[*]:PseudoClass[*]
* <LI>#Id.Class[*]:PseudoClass[*]:PseudoElement
* <LI>#Id.Class[*]:PseudoElement
* <LI>#Id:PseudoClass[*]
* <LI>#Id:PseudoClass[*]:PseudoElement
* <LI>#Id:PseudoElement
* <LI>.Class[*]
* <LI>.Class[*]:PseudoClass[*]
* <LI>.Class[*]:PseudoClass[*]:PseudoElement
* <LI>.Class[*]:PseudoElement
* <LI>:PseudoClass[*]
* <LI>:PseudoClass[*]:PseudoElement
* <LI>:PseudoElement
* </UL>
*/ */
PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode,
Selector* aSelectorResult) nsCSSSelector& aSelector)
{ {
PRUint32 mask = 0; PRInt32 dataMask = 0;
aSelectorResult->mTag.Truncate(0); nsAutoString buffer;
aSelectorResult->mClass.Truncate(0);
aSelectorResult->mID.Truncate(0);
aSelectorResult->mPseudoClass.Truncate(0);
aSelectorResult->mPseudoElement.Truncate(0);
nsCSSToken* tk = &mToken; if (! GetToken(aErrorCode, PR_TRUE)) {
if (!GetToken(aErrorCode, PR_TRUE)) {
return PR_FALSE; return PR_FALSE;
} }
if (eCSSToken_Ident == tk->mType) { if ((eCSSToken_Symbol == mToken.mType) && ('*' == mToken.mSymbol)) { // universal element selector
// tag // don't set any tag in the selector
mask |= SELECTOR_TAG; dataMask |= SEL_MASK_ELEM;
aSelectorResult->mTag.Append(tk->mIdent); if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
if (!GetToken(aErrorCode, PR_FALSE)) {
// premature eof is ok (here!)
return PR_TRUE; return PR_TRUE;
} }
} }
if (eCSSToken_ID == tk->mType) { else if (eCSSToken_Ident == mToken.mType) { // element name
// #id PRInt32 colon = mToken.mIdent.Find(':');
if ((0 < tk->mIdent.Length()) && (nsString::IsAlpha(tk->mIdent.CharAt(0)))) { // verify is legal ID if (-1 == colon) { // no namespace
mask |= SELECTOR_ID; if (mCaseSensative) {
aSelectorResult->mID.Append(tk->mIdent); aSelector.SetTag(mToken.mIdent);
if (!GetToken(aErrorCode, PR_FALSE)) { }
// premature eof is ok (here!) else {
return PR_TRUE; mToken.mIdent.ToUpperCase(buffer);
aSelector.SetTag(buffer);
} }
} }
else { else { // pull out the namespace
return PR_FALSE; 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);
} }
} dataMask |= SEL_MASK_ELEM;
if ((eCSSToken_Symbol == tk->mType) && ('.' == tk->mSymbol)) { if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
// .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!)
return PR_TRUE; return PR_TRUE;
} }
} }
if ((eCSSToken_Symbol == tk->mType) && (':' == tk->mSymbol)) { for (;;) {
// :pseudo if (eCSSToken_ID == mToken.mType) { // #id
if (!GetToken(aErrorCode, PR_FALSE)) { if ((0 == (dataMask & SEL_MASK_ID)) && // only one ID
// premature eof (0 < mToken.mIdent.Length()) &&
return PR_FALSE; (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) { else if ((eCSSToken_Symbol == mToken.mType) && ('.' == mToken.mSymbol)) { // .class
// malformed selector if (! GetToken(aErrorCode, PR_FALSE)) { // get ident
UngetToken(); return PR_FALSE;
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)) { else if ((eCSSToken_Symbol == mToken.mType) && (':' == mToken.mSymbol)) { // :pseudo
mask |= SELECTOR_PSEUDO_CLASS; if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof
aSelectorResult->mPseudoClass.Append(tk->mIdent); 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 { else if ((eCSSToken_Symbol == mToken.mType) && ('[' == mToken.mSymbol)) { // attribute
mask |= SELECTOR_PSEUDO_ELEMENT; if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
aSelectorResult->mPseudoElement.Append(':'); // keep the colon return PR_FALSE;
aSelectorResult->mPseudoElement.Append(tk->mIdent); }
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)) { else { // not a selector token, we're done
// premature eof is ok (here!) break;
}
if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
return PR_TRUE; return PR_TRUE;
} }
} }
if ((eCSSToken_Symbol == tk->mType) && (':' == tk->mSymbol)) { UngetToken();
// :pseudo return PRBool(0 != dataMask);
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;
} }
nsICSSDeclaration* nsICSSDeclaration*

Просмотреть файл

@ -38,6 +38,7 @@
#include "nsIScriptGlobalObject.h" #include "nsIScriptGlobalObject.h"
#include "nsIScriptObjectOwner.h" #include "nsIScriptObjectOwner.h"
#include "nsDOMCSSDeclaration.h" #include "nsDOMCSSDeclaration.h"
#include "nsINameSpaceManager.h"
//#define DEBUG_REFS //#define DEBUG_REFS
@ -62,95 +63,330 @@ static NS_DEFINE_IID(kCSSTableSID, NS_CSS_TABLE_SID);
// -- nsCSSSelector ------------------------------- // -- nsCSSSelector -------------------------------
nsCSSSelector::nsCSSSelector() #define NS_IF_COPY(dest,source,type) \
: mTag(nsnull), mID(nsnull), mClass(nsnull), mPseudoClass(nsnull), 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) mNext(nsnull)
{ {
NS_IF_ADDREF(mAtom);
} }
nsCSSSelector::nsCSSSelector(nsIAtom* aTag, nsIAtom* aID, nsIAtom* aClass, nsIAtom* aPseudoClass) nsAtomList::nsAtomList(const nsString& aAtomValue)
: mTag(aTag), mID(aID), mClass(aClass), mPseudoClass(aPseudoClass), : 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) mNext(nsnull)
{ {
NS_IF_ADDREF(mTag);
NS_IF_ADDREF(mID);
NS_IF_ADDREF(mClass);
NS_IF_ADDREF(mPseudoClass);
} }
nsCSSSelector::nsCSSSelector(const nsCSSSelector& aCopy) 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) mNext(nsnull)
{ // implmented to support extension to CSS2 (when we have to copy the array) {
NS_IF_ADDREF(mTag); NS_IF_ADDREF(mTag);
NS_IF_ADDREF(mID); NS_IF_ADDREF(mID);
NS_IF_ADDREF(mClass); NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList);
NS_IF_ADDREF(mPseudoClass); NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList);
NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector);
} }
nsCSSSelector::~nsCSSSelector() nsCSSSelector::~nsCSSSelector(void)
{ {
NS_IF_RELEASE(mTag); Reset();
NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass);
NS_IF_RELEASE(mPseudoClass);
} }
nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy)
{ {
NS_IF_RELEASE(mTag); NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID); NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass); NS_IF_DELETE(mClassList);
NS_IF_RELEASE(mPseudoClass); NS_IF_DELETE(mPseudoClassList);
NS_IF_DELETE(mAttrList);
mNameSpace = aCopy.mNameSpace;
mTag = aCopy.mTag; mTag = aCopy.mTag;
mID = aCopy.mID; mID = aCopy.mID;
mClass = aCopy.mClass; NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList);
mPseudoClass = aCopy.mPseudoClass; NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList);
NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector);
mOperator = aCopy.mOperator;
NS_IF_ADDREF(mTag); NS_IF_ADDREF(mTag);
NS_IF_ADDREF(mID); NS_IF_ADDREF(mID);
NS_IF_ADDREF(mClass);
NS_IF_ADDREF(mPseudoClass);
return *this; return *this;
} }
PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const
{ {
if (this == aOther) {
return PR_TRUE;
}
if (nsnull != aOther) { if (nsnull != aOther) {
return (PRBool)((aOther->mTag == mTag) && (aOther->mID == mID) && if ((aOther->mNameSpace == mNameSpace) &&
(aOther->mClass == mClass) && (aOther->mPseudoClass == mPseudoClass)); (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; return PR_FALSE;
} }
void nsCSSSelector::Set(const nsString& aTag, const nsString& aID, void nsCSSSelector::Reset(void)
const nsString& aClass, const nsString& aPseudoClass)
{ {
nsAutoString buffer; mNameSpace = kNameSpaceID_Unknown;
NS_IF_RELEASE(mTag); NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID); NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass); NS_IF_DELETE(mClassList);
NS_IF_RELEASE(mPseudoClass); 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()) { if (0 < aTag.Length()) {
aTag.ToUpperCase(buffer); // XXX is this correct? what about class? mTag = NS_NewAtom(aTag);
mTag = NS_NewAtom(buffer);
} }
}
void nsCSSSelector::SetID(const nsString& aID)
{
NS_IF_RELEASE(mID);
if (0 < aID.Length()) { if (0 < aID.Length()) {
mID = NS_NewAtom(aID); mID = NS_NewAtom(aID);
} }
}
void nsCSSSelector::AddClass(const nsString& aClass)
{
if (0 < aClass.Length()) { if (0 < aClass.Length()) {
mClass = NS_NewAtom(aClass); nsAtomList** list = &mClassList;
} while (nsnull != *list) {
if (0 < aPseudoClass.Length()) { list = &((*list)->mNext);
aPseudoClass.ToUpperCase(buffer);
mPseudoClass = NS_NewAtom(buffer);
if (nsnull == mTag) {
mTag = nsHTMLAtoms::a;
NS_ADDREF(mTag);
} }
*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 ------------------------------- // -- CSSImportantRule -------------------------------
static nscoord CalcLength(const nsCSSValue& aValue, const nsStyleFont* aFont, static nscoord CalcLength(const nsCSSValue& aValue, const nsStyleFont* aFont,
@ -365,6 +601,8 @@ public:
virtual nsCSSSelector* FirstSelector(void); virtual nsCSSSelector* FirstSelector(void);
virtual void AddSelector(const nsCSSSelector& aSelector); virtual void AddSelector(const nsCSSSelector& aSelector);
virtual void DeleteSelector(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 nsICSSDeclaration* GetDeclaration(void) const;
virtual void SetDeclaration(nsICSSDeclaration* aDeclaration); virtual void SetDeclaration(nsICSSDeclaration* aDeclaration);
@ -410,6 +648,7 @@ protected:
PRUint32 mRefCnt : 31; PRUint32 mRefCnt : 31;
nsCSSSelector mSelector; nsCSSSelector mSelector;
nsString mSelectorText;
nsICSSDeclaration* mDeclaration; nsICSSDeclaration* mDeclaration;
PRInt32 mWeight; PRInt32 mWeight;
CSSImportantRule* mImportantRule; CSSImportantRule* mImportantRule;
@ -463,7 +702,7 @@ static const PRInt32 kInstrument = 1075;
#endif #endif
CSSStyleRuleImpl::CSSStyleRuleImpl(const nsCSSSelector& aSelector) CSSStyleRuleImpl::CSSStyleRuleImpl(const nsCSSSelector& aSelector)
: mSelector(aSelector), mDeclaration(nsnull), : mSelector(aSelector), mSelectorText(), mDeclaration(nsnull),
mWeight(0), mImportantRule(nsnull) mWeight(0), mImportantRule(nsnull)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
@ -628,16 +867,13 @@ nsCSSSelector* CSSStyleRuleImpl::FirstSelector(void)
void CSSStyleRuleImpl::AddSelector(const nsCSSSelector& aSelector) void CSSStyleRuleImpl::AddSelector(const nsCSSSelector& aSelector)
{ {
if ((nsnull != aSelector.mTag) || (nsnull != aSelector.mID) || nsCSSSelector* selector = new nsCSSSelector(aSelector);
(nsnull != aSelector.mClass) || (nsnull != aSelector.mPseudoClass)) { // skip empty selectors nsCSSSelector* last = &mSelector;
nsCSSSelector* selector = new nsCSSSelector(aSelector);
nsCSSSelector* last = &mSelector;
while (nsnull != last->mNext) { while (nsnull != last->mNext) {
last = last->mNext; last = last->mNext;
}
last->mNext = selector;
} }
last->mNext = selector;
} }
@ -645,9 +881,15 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector)
{ {
if (nsnull != aSelector) { if (nsnull != aSelector) {
if (&mSelector == aSelector) { // handle first selector if (&mSelector == aSelector) { // handle first selector
mSelector = *aSelector; // assign value if (nsnull != mSelector.mNext) {
mSelector.mNext = aSelector->mNext; nsCSSSelector* nextOne = mSelector.mNext;
delete aSelector; mSelector = *nextOne; // assign values
mSelector.mNext = nextOne->mNext;
delete nextOne;
}
else {
mSelector.Reset();
}
} }
else { else {
nsCSSSelector* selector = &mSelector; 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 nsICSSDeclaration* CSSStyleRuleImpl::GetDeclaration(void) const
{ {
NS_IF_ADDREF(mDeclaration); NS_IF_ADDREF(mDeclaration);
@ -1056,8 +1309,8 @@ void MapDeclarationInto(nsICSSDeclaration* aDeclaration,
text->mTextDecoration = td; text->mTextDecoration = td;
} }
else if (eCSSUnit_None == ourText->mDecoration.GetUnit()) { else if (eCSSUnit_None == ourText->mDecoration.GetUnit()) {
font->mFont.decorations = NS_STYLE_TEXT_DECORATION_NONE; font->mFont.decorations = parentFont->mFont.decorations;
font->mFixedFont.decorations = NS_STYLE_TEXT_DECORATION_NONE; font->mFixedFont.decorations = parentFont->mFixedFont.decorations;
text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE; text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE;
} }
else if (eCSSUnit_Inherit == ourText->mDecoration.GetUnit()) { else if (eCSSUnit_Inherit == ourText->mDecoration.GetUnit()) {
@ -1695,6 +1948,11 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector)
{ {
nsAutoString buffer; nsAutoString buffer;
if (kNameSpaceID_None < aSelector->mNameSpace) {
buffer.Append(aSelector->mNameSpace, 10);
fputs(buffer, out);
fputs("\\:", out);
}
if (nsnull != aSelector->mTag) { if (nsnull != aSelector->mTag) {
aSelector->mTag->ToString(buffer); aSelector->mTag->ToString(buffer);
fputs(buffer, out); fputs(buffer, out);
@ -1704,15 +1962,39 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector)
fputs("#", out); fputs("#", out);
fputs(buffer, out); fputs(buffer, out);
} }
if (nsnull != aSelector->mClass) { nsAtomList* list = aSelector->mClassList;
aSelector->mClass->ToString(buffer); while (nsnull != list) {
list->mAtom->ToString(buffer);
fputs(".", out); fputs(".", out);
fputs(buffer, out); fputs(buffer, out);
list = list->mNext;
} }
if (nsnull != aSelector->mPseudoClass) { list = aSelector->mPseudoClassList;
aSelector->mPseudoClass->ToString(buffer); while (nsnull != list) {
list->mAtom->ToString(buffer);
fputs(":", out); fputs(":", out);
fputs(buffer, 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 NS_IMETHODIMP
CSSStyleRuleImpl::GetSelectorText(nsString& aSelectorText) CSSStyleRuleImpl::GetSelectorText(nsString& aSelectorText)
{ {
nsAutoString buffer; aSelectorText = mSelectorText;
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;
}
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CSSStyleRuleImpl::SetSelectorText(const nsString& aSelectorText) 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; return NS_OK;
} }

Просмотреть файл

@ -42,10 +42,12 @@
#include "nsIScriptObjectOwner.h" #include "nsIScriptObjectOwner.h"
#include "nsIScriptGlobalObject.h" #include "nsIScriptGlobalObject.h"
#include "nsICSSParser.h" #include "nsICSSParser.h"
#include "nsCSSAtoms.h"
#include "nsINameSpaceManager.h"
#include "prlog.h" #include "prlog.h"
//#define DEBUG_REFS //#define DEBUG_REFS
//#define DEBUG_RULES #define DEBUG_RULES
static NS_DEFINE_IID(kICSSStyleSheetIID, NS_ICSS_STYLE_SHEET_IID); static NS_DEFINE_IID(kICSSStyleSheetIID, NS_ICSS_STYLE_SHEET_IID);
static NS_DEFINE_IID(kIStyleSheetIID, NS_ISTYLE_SHEET_IID); static NS_DEFINE_IID(kIStyleSheetIID, NS_ISTYLE_SHEET_IID);
@ -120,8 +122,8 @@ struct RuleValue {
} }
~RuleValue(void) ~RuleValue(void)
{ {
if (nsnull != mNext) { if ((nsnull != mNext) && (nsnull != mNext->mNext)) {
delete mNext; delete mNext; // don't delete that last in the chain, it's shared
} }
} }
@ -142,7 +144,7 @@ public:
RuleHash(void); RuleHash(void);
~RuleHash(void); ~RuleHash(void);
void AppendRule(nsICSSStyleRule* aRule); 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); RuleEnumFunc aFunc, void* aData);
void EnumerateTagRules(nsIAtom* aTag, void EnumerateTagRules(nsIAtom* aTag,
RuleEnumFunc aFunc, void* aData); RuleEnumFunc aFunc, void* aData);
@ -154,11 +156,17 @@ protected:
nsHashtable mTagTable; nsHashtable mTagTable;
nsHashtable mIdTable; nsHashtable mIdTable;
nsHashtable mClassTable; nsHashtable mClassTable;
RuleValue** mEnumList;
PRInt32 mEnumListSize;
RuleValue mEndValue;
}; };
RuleHash::RuleHash(void) RuleHash::RuleHash(void)
: mRuleCount(0), : 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); mTagTable.Enumerate(DeleteValue);
mIdTable.Enumerate(DeleteValue); mIdTable.Enumerate(DeleteValue);
mClassTable.Enumerate(DeleteValue); mClassTable.Enumerate(DeleteValue);
if (nsnull != mEnumList) {
delete [] mEnumList;
}
} }
void RuleHash::AppendRuleToTable(nsHashtable& aTable, nsIAtom* aAtom, nsICSSStyleRule* aRule) void RuleHash::AppendRuleToTable(nsHashtable& aTable, nsIAtom* aAtom, nsICSSStyleRule* aRule)
{ {
NS_ASSERTION(nsnull != aAtom, "null hash key");
RuleKey key(aAtom); RuleKey key(aAtom);
RuleValue* value = (RuleValue*)aTable.Get(&key); RuleValue* value = (RuleValue*)aTable.Get(&key);
if (nsnull == value) { if (nsnull == value) {
value = new RuleValue(aRule, mRuleCount++); value = new RuleValue(aRule, mRuleCount++);
aTable.Put(&key, value); aTable.Put(&key, value);
value->mNext = &mEndValue;
} }
else { else {
while (nsnull != value->mNext) { while (&mEndValue != value->mNext) {
value = value->mNext; value = value->mNext;
} }
value->mNext = new RuleValue(aRule, mRuleCount++); value->mNext = new RuleValue(aRule, mRuleCount++);
value->mNext->mNext = &mEndValue;
} }
mEndValue.mIndex = mRuleCount;
} }
void RuleHash::AppendRule(nsICSSStyleRule* aRule) void RuleHash::AppendRule(nsICSSStyleRule* aRule)
@ -198,65 +214,128 @@ void RuleHash::AppendRule(nsICSSStyleRule* aRule)
if (nsnull != selector->mID) { if (nsnull != selector->mID) {
AppendRuleToTable(mIdTable, selector->mID, aRule); AppendRuleToTable(mIdTable, selector->mID, aRule);
} }
else if (nsnull != selector->mClass) { else if (nsnull != selector->mClassList) {
AppendRuleToTable(mClassTable, selector->mClass, aRule); AppendRuleToTable(mClassTable, selector->mClassList->mAtom, aRule);
} }
else if (nsnull != selector->mTag) { else if (nsnull != selector->mTag) {
AppendRuleToTable(mTagTable, selector->mTag, aRule); 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) RuleEnumFunc aFunc, void* aData)
{ {
RuleValue* tagValue = nsnull; PRInt32 classCount = aClassList.Count();
RuleValue* idValue = nsnull; PRInt32 testCount = classCount + 1; // + 1 for universal selector
RuleValue* classValue = nsnull;
if (nsnull != aTag) { if (nsnull != aTag) {
RuleKey tagKey(aTag); testCount++;
tagValue = (RuleValue*)mTagTable.Get(&tagKey);
} }
if (nsnull != aID) { if (nsnull != aID) {
RuleKey idKey(aID); testCount++;
idValue = (RuleValue*)mIdTable.Get(&idKey);
}
if (nsnull != aClass) {
RuleKey classKey(aClass);
classValue = (RuleValue*)mClassTable.Get(&classKey);
} }
PRInt32 tagIndex = ((nsnull != tagValue) ? tagValue->mIndex : mRuleCount); if (mEnumListSize < testCount) {
PRInt32 idIndex = ((nsnull != idValue) ? idValue->mIndex : mRuleCount); delete [] mEnumList;
PRInt32 classIndex = ((nsnull != classValue) ? classValue->mIndex : mRuleCount); mEnumList = new RuleValue*[testCount];
mEnumListSize = testCount;
}
while ((nsnull != tagValue) || (nsnull != idValue) || (nsnull != classValue)) { PRInt32 index;
if ((tagIndex < idIndex) && (tagIndex < classIndex)) { PRInt32 valueCount = 0;
(*aFunc)(tagValue->mRule, aData);
tagValue = tagValue->mNext; { // universal tag rules
tagIndex = ((nsnull != tagValue) ? tagValue->mIndex : mRuleCount); RuleValue* value = (RuleValue*)mTagTable.Get(&RuleKey(nsCSSAtoms::universalSelector));
if (nsnull != value) {
mEnumList[valueCount++] = value;
} }
else if (idIndex < classIndex) { }
(*aFunc)(idValue->mRule, aData); if (nsnull != aTag) {
idValue = idValue->mNext; RuleValue* value = (RuleValue*)mTagTable.Get(&RuleKey(aTag));
idIndex = ((nsnull != idValue) ? idValue->mIndex : mRuleCount); if (nsnull != value) {
mEnumList[valueCount++] = value;
} }
else { }
(*aFunc)(classValue->mRule, aData); if (nsnull != aID) {
classValue = classValue->mNext; RuleValue* value = (RuleValue*)mIdTable.Get(&RuleKey(aID));
classIndex = ((nsnull != classValue) ? classValue->mIndex : mRuleCount); 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) void RuleHash::EnumerateTagRules(nsIAtom* aTag, RuleEnumFunc aFunc, void* aData)
{ {
RuleKey tagKey(aTag); RuleValue* tagValue = (RuleValue*)mTagTable.Get(&RuleKey(aTag));
RuleValue* value = (RuleValue*)mTagTable.Get(&tagKey); RuleValue* uniValue = (RuleValue*)mTagTable.Get(&RuleKey(nsCSSAtoms::universalSelector));
while (nsnull != value) { if (nsnull == tagValue) {
(*aFunc)(value->mRule, aData); if (nsnull != uniValue) {
value = value->mNext; 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) mRuleHash(nsnull)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
nsCSSAtoms::AddrefAtoms();
mParent = nsnull; mParent = nsnull;
mRuleCollection = nsnull; mRuleCollection = nsnull;
mImportsCollection = nsnull; mImportsCollection = nsnull;
@ -744,6 +825,7 @@ CSSStyleSheetImpl::~CSSStyleSheetImpl()
// XXX The document reference is not reference counted and should // XXX The document reference is not reference counted and should
// not be released. The document will let us know when it is going // not be released. The document will let us know when it is going
// away. // away.
nsCSSAtoms::ReleaseAtoms();
} }
NS_IMPL_ADDREF(CSSStyleSheetImpl) NS_IMPL_ADDREF(CSSStyleSheetImpl)
@ -801,76 +883,87 @@ static PRBool SelectorMatches(nsIPresContext* aPresContext,
PRBool result = PR_FALSE; PRBool result = PR_FALSE;
nsIAtom* contentTag; nsIAtom* contentTag;
aContent->GetTag(contentTag); aContent->GetTag(contentTag);
PRInt32 nameSpaceID;
aContent->GetNameSpaceID(nameSpaceID);
if ((nsnull == aSelector->mTag) || (aSelector->mTag == contentTag)) { if (((nsnull == aSelector->mTag) || (aSelector->mTag == contentTag)) &&
if ((nsnull != aSelector->mClass) || (nsnull != aSelector->mID) || ((kNameSpaceID_Unknown == aSelector->mNameSpace) || (nameSpaceID == aSelector->mNameSpace))) {
(nsnull != aSelector->mPseudoClass)) { 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; nsIHTMLContent* htmlContent;
if (NS_OK == aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent)) { if (NS_SUCCEEDED(aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent))) {
nsIAtom* contentClass;
htmlContent->GetClass(contentClass);
nsIAtom* contentID; nsIAtom* contentID;
htmlContent->GetID(contentID); htmlContent->GetID(contentID);
if ((nsnull == aSelector->mClass) || (aSelector->mClass == contentClass)) { if ((nsnull == aSelector->mID) || (aSelector->mID == contentID)) {
if ((nsnull == aSelector->mID) || (aSelector->mID == contentID)) { nsIAtom* contentClass;
if ((contentTag == nsHTMLAtoms::a) && (nsnull != aSelector->mPseudoClass)) { htmlContent->GetClass(contentClass);
// test link state // XXX only testing for frist class right now
nsILinkHandler* linkHandler; if ((nsnull == aSelector->mClassList) || (aSelector->mClassList->mAtom == contentClass)) {
result = PR_TRUE;
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;
}
} }
NS_IF_RELEASE(contentClass);
} }
NS_RELEASE(htmlContent); NS_RELEASE(htmlContent);
NS_IF_RELEASE(contentClass);
NS_IF_RELEASE(contentID); NS_IF_RELEASE(contentID);
} }
} }
else { if ((PR_TRUE == result) &&
result = PR_TRUE; (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); NS_IF_RELEASE(contentTag);
@ -899,6 +992,8 @@ static void ContentEnumFunc(nsICSSStyleRule* aRule, void* aData)
{ {
ContentEnumData* data = (ContentEnumData*)aData; ContentEnumData* data = (ContentEnumData*)aData;
// XXX not dealing with selector functions...
nsCSSSelector* selector = aRule->FirstSelector(); nsCSSSelector* selector = aRule->FirstSelector();
if (SelectorMatches(data->mPresContext, selector, data->mContent)) { if (SelectorMatches(data->mPresContext, selector, data->mContent)) {
selector = selector->mNext; selector = selector->mNext;
@ -995,7 +1090,13 @@ PRInt32 CSSStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext,
NS_RELEASE(htmlContent); 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; matchCount += data.mCount;
#ifdef DEBUG_RULES #ifdef DEBUG_RULES
@ -1005,7 +1106,7 @@ PRInt32 CSSStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext,
NS_NewISupportsArray(&list2); NS_NewISupportsArray(&list2);
data.mResults = list1; data.mResults = list1;
mRuleHash->EnumerateAllRules(tagAtom, idAtom, classAtom, ContentEnumFunc, &data); mRuleHash->EnumerateAllRules(tagAtom, idAtom, classArray, ContentEnumFunc, &data);
data.mResults = list2; data.mResults = list2;
mWeightedRules->EnumerateBackwards(ContentEnumWrap, &data); mWeightedRules->EnumerateBackwards(ContentEnumWrap, &data);
NS_ASSERTION(list1->Equals(list2), "lists not equal"); NS_ASSERTION(list1->Equals(list2), "lists not equal");

Просмотреть файл

@ -31,6 +31,9 @@
#include "nsVoidArray.h" #include "nsVoidArray.h"
#include "nsColor.h" #include "nsColor.h"
#include "nsStyleConsts.h" #include "nsStyleConsts.h"
#include "nsCSSAtoms.h"
#include "nsINameSpaceManager.h"
#include "nsINameSpace.h"
// XXX TODO: // XXX TODO:
// - rework aErrorCode stuff: switch over to nsresult // - 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 #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, // e.g. "P B, H1 B { ... }" has a selector list with two elements,
// each of which has two selectors. // each of which has two selectors.
struct SelectorList { struct SelectorList {
SelectorList* mNext; SelectorList(void);
nsVoidArray mSelectors; ~SelectorList(void);
SelectorList(); void AddSelector(const nsCSSSelector& aSelector);
void Destroy();
void AddSelector(Selector* aSelector) {
mSelectors.AppendElement(aSelector);
}
#ifdef NS_DEBUG #ifdef NS_DEBUG
void Dump(); void Dump(void);
#endif #endif
private: nsCSSSelector* mSelectors;
~SelectorList(); SelectorList* mNext;
}; };
SelectorList::SelectorList() SelectorList::SelectorList(void)
: mSelectors(nsnull),
mNext(nsnull)
{ {
mNext = nsnull;
} }
SelectorList::~SelectorList() SelectorList::~SelectorList()
{ {
PRInt32 n = mSelectors.Count(); nsCSSSelector* sel = mSelectors;
for (PRInt32 i = 0; i < n; i++) { while (nsnull != sel) {
Selector* sel = (Selector*) mSelectors.ElementAt(i); nsCSSSelector* dead = sel;
delete sel; sel = sel->mNext;
delete dead;
}
if (nsnull != mNext) {
delete mNext;
} }
} }
void SelectorList::Destroy() void SelectorList::AddSelector(const nsCSSSelector& aSelector)
{ { // prepend to list
SelectorList* list = this; nsCSSSelector* newSel = new nsCSSSelector(aSelector);
while (nsnull != list) { newSel->mNext = mSelectors;
SelectorList* next = list->mNext; mSelectors = newSel;
delete list;
list = next;
}
} }
#ifdef NS_DEBUG #ifdef NS_DEBUG
void SelectorList::Dump() 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 #endif
@ -200,6 +113,8 @@ public:
NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet); NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet);
NS_IMETHOD SetCaseSensative(PRBool aCaseSensative);
NS_IMETHOD Parse(nsIUnicharInputStream* aInput, NS_IMETHOD Parse(nsIUnicharInputStream* aInput,
nsIURL* aInputURL, nsIURL* aInputURL,
nsICSSStyleSheet*& aResult); nsICSSStyleSheet*& aResult);
@ -231,9 +146,9 @@ protected:
PRBool GatherMedia(PRInt32& aErrorCode, nsString& aMedia); PRBool GatherMedia(PRInt32& aErrorCode, nsString& aMedia);
PRBool ProcessImport(PRInt32& aErrorCode, const nsString& aURLSpec, const 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 ParseSelectorList(PRInt32& aErrorCode, SelectorList* aListHead); PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList*& aListHead);
PRBool ParseSelector(PRInt32& aErrorCode, Selector* aSelectorResult); PRBool ParseSelector(PRInt32& aErrorCode, nsCSSSelector& aSelectorResult);
nsICSSDeclaration* ParseDeclarationBlock(PRInt32& aErrorCode, nsICSSDeclaration* ParseDeclarationBlock(PRInt32& aErrorCode,
PRBool aCheckForBraces); PRBool aCheckForBraces);
PRBool ParseDeclaration(PRInt32& aErrorCode, PRBool ParseDeclaration(PRInt32& aErrorCode,
@ -317,6 +232,7 @@ protected:
PRBool mInHead; PRBool mInHead;
PRBool mNavQuirkMode; PRBool mNavQuirkMode;
PRBool mCaseSensative;
}; };
NS_HTML nsresult NS_HTML nsresult
@ -334,19 +250,23 @@ NS_NewCSSParser(nsICSSParser** aInstancePtrResult)
CSSParserImpl::CSSParserImpl() CSSParserImpl::CSSParserImpl()
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
nsCSSAtoms::AddrefAtoms();
mScanner = nsnull; mScanner = nsnull;
mSheet = nsnull; mSheet = nsnull;
mHavePushBack = PR_FALSE; mHavePushBack = PR_FALSE;
mNavQuirkMode = PR_TRUE; mNavQuirkMode = PR_TRUE;
mCaseSensative = PR_FALSE;
} }
CSSParserImpl::CSSParserImpl(nsICSSStyleSheet* aSheet) CSSParserImpl::CSSParserImpl(nsICSSStyleSheet* aSheet)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
nsCSSAtoms::AddrefAtoms();
mScanner = nsnull; mScanner = nsnull;
mSheet = aSheet; NS_ADDREF(aSheet); mSheet = aSheet; NS_ADDREF(aSheet);
mHavePushBack = PR_FALSE; mHavePushBack = PR_FALSE;
mNavQuirkMode = PR_TRUE; mNavQuirkMode = PR_TRUE;
mCaseSensative = PR_FALSE;
} }
NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID) NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID)
@ -354,16 +274,17 @@ NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID)
CSSParserImpl::~CSSParserImpl() CSSParserImpl::~CSSParserImpl()
{ {
NS_IF_RELEASE(mSheet); NS_IF_RELEASE(mSheet);
nsCSSAtoms::ReleaseAtoms();
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::GetInfoMask(PRUint32& aResult) CSSParserImpl::GetInfoMask(PRUint32& aResult)
{ {
aResult = NS_CSS_GETINFO_CSS1 | NS_CSS_GETINFO_CSSP; aResult = NS_CSS_GETINFO_CSS1 | NS_CSS_GETINFO_CSSP;
return NS_OK; return NS_OK;
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet) CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet)
{ {
NS_PRECONDITION(nsnull != aSheet, "null ptr"); NS_PRECONDITION(nsnull != aSheet, "null ptr");
@ -381,7 +302,15 @@ CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet)
return NS_OK; return NS_OK;
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::SetCaseSensative(PRBool aCaseSensative)
{
mCaseSensative = aCaseSensative;
return NS_OK;
}
NS_IMETHODIMP
CSSParserImpl::Parse(nsIUnicharInputStream* aInput, CSSParserImpl::Parse(nsIUnicharInputStream* aInput,
nsIURL* aInputURL, nsIURL* aInputURL,
nsICSSStyleSheet*& aResult) nsICSSStyleSheet*& aResult)
@ -426,7 +355,7 @@ CSSParserImpl::Parse(nsIUnicharInputStream* aInput,
return NS_OK; return NS_OK;
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::ParseDeclarations(const nsString& aDeclaration, CSSParserImpl::ParseDeclarations(const nsString& aDeclaration,
nsIURL* aBaseURL, nsIURL* aBaseURL,
nsIStyleRule*& aResult) nsIStyleRule*& aResult)
@ -471,7 +400,7 @@ CSSParserImpl::ParseDeclarations(const nsString& aDeclaration,
return NS_OK; return NS_OK;
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::ParseAndAppendDeclaration(const nsString& aBuffer, CSSParserImpl::ParseAndAppendDeclaration(const nsString& aBuffer,
nsIURL* aBaseURL, nsIURL* aBaseURL,
nsICSSDeclaration* aDeclaration, nsICSSDeclaration* aDeclaration,
@ -925,18 +854,18 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode)
nsCSSToken* tk = &mToken; nsCSSToken* tk = &mToken;
// First get the list of selectors for the rule // First get the list of selectors for the rule
SelectorList *slist = new SelectorList(); SelectorList* slist = nsnull;
if (!ParseSelectorList(aErrorCode, slist)) { if (! ParseSelectorList(aErrorCode, slist)) {
SkipRuleSet(aErrorCode); SkipRuleSet(aErrorCode);
slist->Destroy();
return PR_FALSE; return PR_FALSE;
} }
NS_ASSERTION(nsnull != slist, "null selector list");
// Next parse the declaration block // Next parse the declaration block
nsICSSDeclaration* declaration = ParseDeclarationBlock(aErrorCode, PR_TRUE); nsICSSDeclaration* declaration = ParseDeclarationBlock(aErrorCode, PR_TRUE);
if (nsnull == declaration) { if (nsnull == declaration) {
// XXX skip something here // XXX skip something here
slist->Destroy(); delete slist;
return PR_FALSE; return PR_FALSE;
} }
@ -950,34 +879,16 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode)
// Translate the selector list and declaration block into style data // Translate the selector list and declaration block into style data
SelectorList* list = slist; SelectorList* list = slist;
nsCSSSelector selector;
nsICSSStyleRule* rule = nsnull;
while (nsnull != list) { while (nsnull != list) {
PRInt32 selIndex = list->mSelectors.Count(); PRInt32 weight = list->mSelectors->CalcWeight();
nsICSSStyleRule* rule = nsnull;
Selector* sel = (Selector*)list->mSelectors[--selIndex]; NS_NewCSSStyleRule(&rule, *(list->mSelectors));
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);
}
if (nsnull != rule) { if (nsnull != rule) {
while (--selIndex >= 0) { if (nsnull != list->mSelectors->mNext) { // hand off other selectors to new rule
Selector* sel = (Selector*)list->mSelectors[selIndex]; nsCSSSelector* ruleFirst = rule->FirstSelector();
selector.Set(sel->mTag, sel->mID, sel->mClass, sel->mPseudoClass); ruleFirst->mNext = list->mSelectors->mNext;
list->mSelectors->mNext = nsnull;
rule->AddSelector(selector);
weight += sel->Weight();
} }
rule->SetDeclaration(declaration); rule->SetDeclaration(declaration);
rule->SetWeight(weight); rule->SetWeight(weight);
@ -990,219 +901,342 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode)
} }
// Release temporary storage // Release temporary storage
slist->Destroy(); delete slist;
NS_RELEASE(declaration); NS_RELEASE(declaration);
return PR_TRUE; return PR_TRUE;
} }
PRBool CSSParserImpl::ParseSelectorList(PRInt32& aErrorCode, 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 // must have at least one selector group
aListHead = nsnull;
return PR_FALSE; return PR_FALSE;
} }
NS_ASSERTION(nsnull != list, "no selector list");
aListHead = list;
// After that there must either be a "," or a "{" // After that there must either be a "," or a "{"
nsCSSToken* tk = &mToken; nsCSSToken* tk = &mToken;
for (;;) { for (;;) {
if (!GetToken(aErrorCode, PR_TRUE)) { if (! GetToken(aErrorCode, PR_TRUE)) {
return PR_FALSE; break;
} }
if (eCSSToken_Symbol != tk->mType) { if (eCSSToken_Symbol != tk->mType) {
UngetToken(); UngetToken();
return PR_FALSE; break;
} }
if (',' == tk->mSymbol) { if (',' == tk->mSymbol) {
SelectorList* newList = nsnull;
// Another selector group must follow // Another selector group must follow
SelectorList* newList = new SelectorList(); if (! ParseSelectorGroup(aErrorCode, newList)) {
if (!ParseSelectorGroup(aErrorCode, newList)) { break;
newList->Destroy();
return PR_FALSE;
} }
// add new list to the end of the selector list // add new list to the end of the selector list
aListHead->mNext = newList; list->mNext = newList;
aListHead = newList; list = newList;
continue; continue;
} else if ('{' == tk->mSymbol) { } else if ('{' == tk->mSymbol) {
UngetToken(); UngetToken();
break; return PR_TRUE;
} else { } else {
UngetToken(); 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, PRBool CSSParserImpl::ParseSelectorGroup(PRInt32& aErrorCode,
SelectorList* aList) SelectorList*& aList)
{ {
SelectorList* list = nsnull;
for (;;) { for (;;) {
Selector* sel = new Selector(); nsCSSSelector selector;
if (!ParseSelector(aErrorCode, sel)) { if (! ParseSelector(aErrorCode, selector)) {
delete sel;
break; 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) #define SEL_MASK_NSPACE 0x01
{ #define SEL_MASK_ELEM 0x02
return (aBuffer.EqualsIgnoreCase("link") || #define SEL_MASK_ID 0x04
aBuffer.EqualsIgnoreCase("visited") || #define SEL_MASK_CLASS 0x08
aBuffer.EqualsIgnoreCase("hover") || #define SEL_MASK_ATTRIB 0x10
aBuffer.EqualsIgnoreCase("out-of-date") || // our extension #define SEL_MASK_PCLASS 0x20
aBuffer.EqualsIgnoreCase("active")); #define SEL_MASK_PELEM 0x40
}
/** /**
* These are the 31 possible kinds of CSS1 style selectors: * This is the format for selectors:
* (but there are 50 ways to leave your lover) * operator? [namespace \:]? element_name? [ ID | class | attrib | pseudo ]*
* [*] means it can repeat
* <UL>
* <LI>Tag
* <LI>Tag#Id
* <LI>Tag#Id.Class[*]
* <LI>Tag#Id.Class[*]:PseudoClass[*]
* <LI>Tag#Id.Class[*]:PseudoClass[*]:PseudoElement
* <LI>Tag#Id.Class[*]:PseudoElement
* <LI>Tag#Id:PseudoClass[*]
* <LI>Tag#Id:PseudoClass[*]:PseudoElement
* <LI>Tag#Id:PseudoElement
* <LI>Tag.Class[*]
* <LI>Tag.Class[*]:PseudoClass[*]
* <LI>Tag.Class[*]:PseudoClass[*]:PseudoElement
* <LI>Tag.Class[*]:PseudoElement
* <LI>Tag:PseudoClass[*]
* <LI>Tag:PseudoClass[*]:PseudoElement
* <LI>Tag:PseudoElement
* <LI>#Id
* <LI>#Id.Class[*]
* <LI>#Id.Class[*]:PseudoClass[*]
* <LI>#Id.Class[*]:PseudoClass[*]:PseudoElement
* <LI>#Id.Class[*]:PseudoElement
* <LI>#Id:PseudoClass[*]
* <LI>#Id:PseudoClass[*]:PseudoElement
* <LI>#Id:PseudoElement
* <LI>.Class[*]
* <LI>.Class[*]:PseudoClass[*]
* <LI>.Class[*]:PseudoClass[*]:PseudoElement
* <LI>.Class[*]:PseudoElement
* <LI>:PseudoClass[*]
* <LI>:PseudoClass[*]:PseudoElement
* <LI>:PseudoElement
* </UL>
*/ */
PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode,
Selector* aSelectorResult) nsCSSSelector& aSelector)
{ {
PRUint32 mask = 0; PRInt32 dataMask = 0;
aSelectorResult->mTag.Truncate(0); nsAutoString buffer;
aSelectorResult->mClass.Truncate(0);
aSelectorResult->mID.Truncate(0);
aSelectorResult->mPseudoClass.Truncate(0);
aSelectorResult->mPseudoElement.Truncate(0);
nsCSSToken* tk = &mToken; if (! GetToken(aErrorCode, PR_TRUE)) {
if (!GetToken(aErrorCode, PR_TRUE)) {
return PR_FALSE; return PR_FALSE;
} }
if (eCSSToken_Ident == tk->mType) { if ((eCSSToken_Symbol == mToken.mType) && ('*' == mToken.mSymbol)) { // universal element selector
// tag // don't set any tag in the selector
mask |= SELECTOR_TAG; dataMask |= SEL_MASK_ELEM;
aSelectorResult->mTag.Append(tk->mIdent); if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
if (!GetToken(aErrorCode, PR_FALSE)) {
// premature eof is ok (here!)
return PR_TRUE; return PR_TRUE;
} }
} }
if (eCSSToken_ID == tk->mType) { else if (eCSSToken_Ident == mToken.mType) { // element name
// #id PRInt32 colon = mToken.mIdent.Find(':');
if ((0 < tk->mIdent.Length()) && (nsString::IsAlpha(tk->mIdent.CharAt(0)))) { // verify is legal ID if (-1 == colon) { // no namespace
mask |= SELECTOR_ID; if (mCaseSensative) {
aSelectorResult->mID.Append(tk->mIdent); aSelector.SetTag(mToken.mIdent);
if (!GetToken(aErrorCode, PR_FALSE)) { }
// premature eof is ok (here!) else {
return PR_TRUE; mToken.mIdent.ToUpperCase(buffer);
aSelector.SetTag(buffer);
} }
} }
else { else { // pull out the namespace
return PR_FALSE; 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);
} }
} dataMask |= SEL_MASK_ELEM;
if ((eCSSToken_Symbol == tk->mType) && ('.' == tk->mSymbol)) { if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
// .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!)
return PR_TRUE; return PR_TRUE;
} }
} }
if ((eCSSToken_Symbol == tk->mType) && (':' == tk->mSymbol)) { for (;;) {
// :pseudo if (eCSSToken_ID == mToken.mType) { // #id
if (!GetToken(aErrorCode, PR_FALSE)) { if ((0 == (dataMask & SEL_MASK_ID)) && // only one ID
// premature eof (0 < mToken.mIdent.Length()) &&
return PR_FALSE; (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) { else if ((eCSSToken_Symbol == mToken.mType) && ('.' == mToken.mSymbol)) { // .class
// malformed selector if (! GetToken(aErrorCode, PR_FALSE)) { // get ident
UngetToken(); return PR_FALSE;
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)) { else if ((eCSSToken_Symbol == mToken.mType) && (':' == mToken.mSymbol)) { // :pseudo
mask |= SELECTOR_PSEUDO_CLASS; if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof
aSelectorResult->mPseudoClass.Append(tk->mIdent); 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 { else if ((eCSSToken_Symbol == mToken.mType) && ('[' == mToken.mSymbol)) { // attribute
mask |= SELECTOR_PSEUDO_ELEMENT; if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
aSelectorResult->mPseudoElement.Append(':'); // keep the colon return PR_FALSE;
aSelectorResult->mPseudoElement.Append(tk->mIdent); }
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)) { else { // not a selector token, we're done
// premature eof is ok (here!) break;
}
if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
return PR_TRUE; return PR_TRUE;
} }
} }
if ((eCSSToken_Symbol == tk->mType) && (':' == tk->mSymbol)) { UngetToken();
// :pseudo return PRBool(0 != dataMask);
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;
} }
nsICSSDeclaration* nsICSSDeclaration*

Просмотреть файл

@ -38,6 +38,7 @@
#include "nsIScriptGlobalObject.h" #include "nsIScriptGlobalObject.h"
#include "nsIScriptObjectOwner.h" #include "nsIScriptObjectOwner.h"
#include "nsDOMCSSDeclaration.h" #include "nsDOMCSSDeclaration.h"
#include "nsINameSpaceManager.h"
//#define DEBUG_REFS //#define DEBUG_REFS
@ -62,95 +63,330 @@ static NS_DEFINE_IID(kCSSTableSID, NS_CSS_TABLE_SID);
// -- nsCSSSelector ------------------------------- // -- nsCSSSelector -------------------------------
nsCSSSelector::nsCSSSelector() #define NS_IF_COPY(dest,source,type) \
: mTag(nsnull), mID(nsnull), mClass(nsnull), mPseudoClass(nsnull), 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) mNext(nsnull)
{ {
NS_IF_ADDREF(mAtom);
} }
nsCSSSelector::nsCSSSelector(nsIAtom* aTag, nsIAtom* aID, nsIAtom* aClass, nsIAtom* aPseudoClass) nsAtomList::nsAtomList(const nsString& aAtomValue)
: mTag(aTag), mID(aID), mClass(aClass), mPseudoClass(aPseudoClass), : 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) mNext(nsnull)
{ {
NS_IF_ADDREF(mTag);
NS_IF_ADDREF(mID);
NS_IF_ADDREF(mClass);
NS_IF_ADDREF(mPseudoClass);
} }
nsCSSSelector::nsCSSSelector(const nsCSSSelector& aCopy) 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) mNext(nsnull)
{ // implmented to support extension to CSS2 (when we have to copy the array) {
NS_IF_ADDREF(mTag); NS_IF_ADDREF(mTag);
NS_IF_ADDREF(mID); NS_IF_ADDREF(mID);
NS_IF_ADDREF(mClass); NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList);
NS_IF_ADDREF(mPseudoClass); NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList);
NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector);
} }
nsCSSSelector::~nsCSSSelector() nsCSSSelector::~nsCSSSelector(void)
{ {
NS_IF_RELEASE(mTag); Reset();
NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass);
NS_IF_RELEASE(mPseudoClass);
} }
nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy)
{ {
NS_IF_RELEASE(mTag); NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID); NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass); NS_IF_DELETE(mClassList);
NS_IF_RELEASE(mPseudoClass); NS_IF_DELETE(mPseudoClassList);
NS_IF_DELETE(mAttrList);
mNameSpace = aCopy.mNameSpace;
mTag = aCopy.mTag; mTag = aCopy.mTag;
mID = aCopy.mID; mID = aCopy.mID;
mClass = aCopy.mClass; NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList);
mPseudoClass = aCopy.mPseudoClass; NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList);
NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector);
mOperator = aCopy.mOperator;
NS_IF_ADDREF(mTag); NS_IF_ADDREF(mTag);
NS_IF_ADDREF(mID); NS_IF_ADDREF(mID);
NS_IF_ADDREF(mClass);
NS_IF_ADDREF(mPseudoClass);
return *this; return *this;
} }
PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const
{ {
if (this == aOther) {
return PR_TRUE;
}
if (nsnull != aOther) { if (nsnull != aOther) {
return (PRBool)((aOther->mTag == mTag) && (aOther->mID == mID) && if ((aOther->mNameSpace == mNameSpace) &&
(aOther->mClass == mClass) && (aOther->mPseudoClass == mPseudoClass)); (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; return PR_FALSE;
} }
void nsCSSSelector::Set(const nsString& aTag, const nsString& aID, void nsCSSSelector::Reset(void)
const nsString& aClass, const nsString& aPseudoClass)
{ {
nsAutoString buffer; mNameSpace = kNameSpaceID_Unknown;
NS_IF_RELEASE(mTag); NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID); NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass); NS_IF_DELETE(mClassList);
NS_IF_RELEASE(mPseudoClass); 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()) { if (0 < aTag.Length()) {
aTag.ToUpperCase(buffer); // XXX is this correct? what about class? mTag = NS_NewAtom(aTag);
mTag = NS_NewAtom(buffer);
} }
}
void nsCSSSelector::SetID(const nsString& aID)
{
NS_IF_RELEASE(mID);
if (0 < aID.Length()) { if (0 < aID.Length()) {
mID = NS_NewAtom(aID); mID = NS_NewAtom(aID);
} }
}
void nsCSSSelector::AddClass(const nsString& aClass)
{
if (0 < aClass.Length()) { if (0 < aClass.Length()) {
mClass = NS_NewAtom(aClass); nsAtomList** list = &mClassList;
} while (nsnull != *list) {
if (0 < aPseudoClass.Length()) { list = &((*list)->mNext);
aPseudoClass.ToUpperCase(buffer);
mPseudoClass = NS_NewAtom(buffer);
if (nsnull == mTag) {
mTag = nsHTMLAtoms::a;
NS_ADDREF(mTag);
} }
*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 ------------------------------- // -- CSSImportantRule -------------------------------
static nscoord CalcLength(const nsCSSValue& aValue, const nsStyleFont* aFont, static nscoord CalcLength(const nsCSSValue& aValue, const nsStyleFont* aFont,
@ -365,6 +601,8 @@ public:
virtual nsCSSSelector* FirstSelector(void); virtual nsCSSSelector* FirstSelector(void);
virtual void AddSelector(const nsCSSSelector& aSelector); virtual void AddSelector(const nsCSSSelector& aSelector);
virtual void DeleteSelector(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 nsICSSDeclaration* GetDeclaration(void) const;
virtual void SetDeclaration(nsICSSDeclaration* aDeclaration); virtual void SetDeclaration(nsICSSDeclaration* aDeclaration);
@ -410,6 +648,7 @@ protected:
PRUint32 mRefCnt : 31; PRUint32 mRefCnt : 31;
nsCSSSelector mSelector; nsCSSSelector mSelector;
nsString mSelectorText;
nsICSSDeclaration* mDeclaration; nsICSSDeclaration* mDeclaration;
PRInt32 mWeight; PRInt32 mWeight;
CSSImportantRule* mImportantRule; CSSImportantRule* mImportantRule;
@ -463,7 +702,7 @@ static const PRInt32 kInstrument = 1075;
#endif #endif
CSSStyleRuleImpl::CSSStyleRuleImpl(const nsCSSSelector& aSelector) CSSStyleRuleImpl::CSSStyleRuleImpl(const nsCSSSelector& aSelector)
: mSelector(aSelector), mDeclaration(nsnull), : mSelector(aSelector), mSelectorText(), mDeclaration(nsnull),
mWeight(0), mImportantRule(nsnull) mWeight(0), mImportantRule(nsnull)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
@ -628,16 +867,13 @@ nsCSSSelector* CSSStyleRuleImpl::FirstSelector(void)
void CSSStyleRuleImpl::AddSelector(const nsCSSSelector& aSelector) void CSSStyleRuleImpl::AddSelector(const nsCSSSelector& aSelector)
{ {
if ((nsnull != aSelector.mTag) || (nsnull != aSelector.mID) || nsCSSSelector* selector = new nsCSSSelector(aSelector);
(nsnull != aSelector.mClass) || (nsnull != aSelector.mPseudoClass)) { // skip empty selectors nsCSSSelector* last = &mSelector;
nsCSSSelector* selector = new nsCSSSelector(aSelector);
nsCSSSelector* last = &mSelector;
while (nsnull != last->mNext) { while (nsnull != last->mNext) {
last = last->mNext; last = last->mNext;
}
last->mNext = selector;
} }
last->mNext = selector;
} }
@ -645,9 +881,15 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector)
{ {
if (nsnull != aSelector) { if (nsnull != aSelector) {
if (&mSelector == aSelector) { // handle first selector if (&mSelector == aSelector) { // handle first selector
mSelector = *aSelector; // assign value if (nsnull != mSelector.mNext) {
mSelector.mNext = aSelector->mNext; nsCSSSelector* nextOne = mSelector.mNext;
delete aSelector; mSelector = *nextOne; // assign values
mSelector.mNext = nextOne->mNext;
delete nextOne;
}
else {
mSelector.Reset();
}
} }
else { else {
nsCSSSelector* selector = &mSelector; 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 nsICSSDeclaration* CSSStyleRuleImpl::GetDeclaration(void) const
{ {
NS_IF_ADDREF(mDeclaration); NS_IF_ADDREF(mDeclaration);
@ -1056,8 +1309,8 @@ void MapDeclarationInto(nsICSSDeclaration* aDeclaration,
text->mTextDecoration = td; text->mTextDecoration = td;
} }
else if (eCSSUnit_None == ourText->mDecoration.GetUnit()) { else if (eCSSUnit_None == ourText->mDecoration.GetUnit()) {
font->mFont.decorations = NS_STYLE_TEXT_DECORATION_NONE; font->mFont.decorations = parentFont->mFont.decorations;
font->mFixedFont.decorations = NS_STYLE_TEXT_DECORATION_NONE; font->mFixedFont.decorations = parentFont->mFixedFont.decorations;
text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE; text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE;
} }
else if (eCSSUnit_Inherit == ourText->mDecoration.GetUnit()) { else if (eCSSUnit_Inherit == ourText->mDecoration.GetUnit()) {
@ -1695,6 +1948,11 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector)
{ {
nsAutoString buffer; nsAutoString buffer;
if (kNameSpaceID_None < aSelector->mNameSpace) {
buffer.Append(aSelector->mNameSpace, 10);
fputs(buffer, out);
fputs("\\:", out);
}
if (nsnull != aSelector->mTag) { if (nsnull != aSelector->mTag) {
aSelector->mTag->ToString(buffer); aSelector->mTag->ToString(buffer);
fputs(buffer, out); fputs(buffer, out);
@ -1704,15 +1962,39 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector)
fputs("#", out); fputs("#", out);
fputs(buffer, out); fputs(buffer, out);
} }
if (nsnull != aSelector->mClass) { nsAtomList* list = aSelector->mClassList;
aSelector->mClass->ToString(buffer); while (nsnull != list) {
list->mAtom->ToString(buffer);
fputs(".", out); fputs(".", out);
fputs(buffer, out); fputs(buffer, out);
list = list->mNext;
} }
if (nsnull != aSelector->mPseudoClass) { list = aSelector->mPseudoClassList;
aSelector->mPseudoClass->ToString(buffer); while (nsnull != list) {
list->mAtom->ToString(buffer);
fputs(":", out); fputs(":", out);
fputs(buffer, 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 NS_IMETHODIMP
CSSStyleRuleImpl::GetSelectorText(nsString& aSelectorText) CSSStyleRuleImpl::GetSelectorText(nsString& aSelectorText)
{ {
nsAutoString buffer; aSelectorText = mSelectorText;
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;
}
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CSSStyleRuleImpl::SetSelectorText(const nsString& aSelectorText) 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; return NS_OK;
} }

Просмотреть файл

@ -42,10 +42,12 @@
#include "nsIScriptObjectOwner.h" #include "nsIScriptObjectOwner.h"
#include "nsIScriptGlobalObject.h" #include "nsIScriptGlobalObject.h"
#include "nsICSSParser.h" #include "nsICSSParser.h"
#include "nsCSSAtoms.h"
#include "nsINameSpaceManager.h"
#include "prlog.h" #include "prlog.h"
//#define DEBUG_REFS //#define DEBUG_REFS
//#define DEBUG_RULES #define DEBUG_RULES
static NS_DEFINE_IID(kICSSStyleSheetIID, NS_ICSS_STYLE_SHEET_IID); static NS_DEFINE_IID(kICSSStyleSheetIID, NS_ICSS_STYLE_SHEET_IID);
static NS_DEFINE_IID(kIStyleSheetIID, NS_ISTYLE_SHEET_IID); static NS_DEFINE_IID(kIStyleSheetIID, NS_ISTYLE_SHEET_IID);
@ -120,8 +122,8 @@ struct RuleValue {
} }
~RuleValue(void) ~RuleValue(void)
{ {
if (nsnull != mNext) { if ((nsnull != mNext) && (nsnull != mNext->mNext)) {
delete mNext; delete mNext; // don't delete that last in the chain, it's shared
} }
} }
@ -142,7 +144,7 @@ public:
RuleHash(void); RuleHash(void);
~RuleHash(void); ~RuleHash(void);
void AppendRule(nsICSSStyleRule* aRule); 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); RuleEnumFunc aFunc, void* aData);
void EnumerateTagRules(nsIAtom* aTag, void EnumerateTagRules(nsIAtom* aTag,
RuleEnumFunc aFunc, void* aData); RuleEnumFunc aFunc, void* aData);
@ -154,11 +156,17 @@ protected:
nsHashtable mTagTable; nsHashtable mTagTable;
nsHashtable mIdTable; nsHashtable mIdTable;
nsHashtable mClassTable; nsHashtable mClassTable;
RuleValue** mEnumList;
PRInt32 mEnumListSize;
RuleValue mEndValue;
}; };
RuleHash::RuleHash(void) RuleHash::RuleHash(void)
: mRuleCount(0), : 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); mTagTable.Enumerate(DeleteValue);
mIdTable.Enumerate(DeleteValue); mIdTable.Enumerate(DeleteValue);
mClassTable.Enumerate(DeleteValue); mClassTable.Enumerate(DeleteValue);
if (nsnull != mEnumList) {
delete [] mEnumList;
}
} }
void RuleHash::AppendRuleToTable(nsHashtable& aTable, nsIAtom* aAtom, nsICSSStyleRule* aRule) void RuleHash::AppendRuleToTable(nsHashtable& aTable, nsIAtom* aAtom, nsICSSStyleRule* aRule)
{ {
NS_ASSERTION(nsnull != aAtom, "null hash key");
RuleKey key(aAtom); RuleKey key(aAtom);
RuleValue* value = (RuleValue*)aTable.Get(&key); RuleValue* value = (RuleValue*)aTable.Get(&key);
if (nsnull == value) { if (nsnull == value) {
value = new RuleValue(aRule, mRuleCount++); value = new RuleValue(aRule, mRuleCount++);
aTable.Put(&key, value); aTable.Put(&key, value);
value->mNext = &mEndValue;
} }
else { else {
while (nsnull != value->mNext) { while (&mEndValue != value->mNext) {
value = value->mNext; value = value->mNext;
} }
value->mNext = new RuleValue(aRule, mRuleCount++); value->mNext = new RuleValue(aRule, mRuleCount++);
value->mNext->mNext = &mEndValue;
} }
mEndValue.mIndex = mRuleCount;
} }
void RuleHash::AppendRule(nsICSSStyleRule* aRule) void RuleHash::AppendRule(nsICSSStyleRule* aRule)
@ -198,65 +214,128 @@ void RuleHash::AppendRule(nsICSSStyleRule* aRule)
if (nsnull != selector->mID) { if (nsnull != selector->mID) {
AppendRuleToTable(mIdTable, selector->mID, aRule); AppendRuleToTable(mIdTable, selector->mID, aRule);
} }
else if (nsnull != selector->mClass) { else if (nsnull != selector->mClassList) {
AppendRuleToTable(mClassTable, selector->mClass, aRule); AppendRuleToTable(mClassTable, selector->mClassList->mAtom, aRule);
} }
else if (nsnull != selector->mTag) { else if (nsnull != selector->mTag) {
AppendRuleToTable(mTagTable, selector->mTag, aRule); 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) RuleEnumFunc aFunc, void* aData)
{ {
RuleValue* tagValue = nsnull; PRInt32 classCount = aClassList.Count();
RuleValue* idValue = nsnull; PRInt32 testCount = classCount + 1; // + 1 for universal selector
RuleValue* classValue = nsnull;
if (nsnull != aTag) { if (nsnull != aTag) {
RuleKey tagKey(aTag); testCount++;
tagValue = (RuleValue*)mTagTable.Get(&tagKey);
} }
if (nsnull != aID) { if (nsnull != aID) {
RuleKey idKey(aID); testCount++;
idValue = (RuleValue*)mIdTable.Get(&idKey);
}
if (nsnull != aClass) {
RuleKey classKey(aClass);
classValue = (RuleValue*)mClassTable.Get(&classKey);
} }
PRInt32 tagIndex = ((nsnull != tagValue) ? tagValue->mIndex : mRuleCount); if (mEnumListSize < testCount) {
PRInt32 idIndex = ((nsnull != idValue) ? idValue->mIndex : mRuleCount); delete [] mEnumList;
PRInt32 classIndex = ((nsnull != classValue) ? classValue->mIndex : mRuleCount); mEnumList = new RuleValue*[testCount];
mEnumListSize = testCount;
}
while ((nsnull != tagValue) || (nsnull != idValue) || (nsnull != classValue)) { PRInt32 index;
if ((tagIndex < idIndex) && (tagIndex < classIndex)) { PRInt32 valueCount = 0;
(*aFunc)(tagValue->mRule, aData);
tagValue = tagValue->mNext; { // universal tag rules
tagIndex = ((nsnull != tagValue) ? tagValue->mIndex : mRuleCount); RuleValue* value = (RuleValue*)mTagTable.Get(&RuleKey(nsCSSAtoms::universalSelector));
if (nsnull != value) {
mEnumList[valueCount++] = value;
} }
else if (idIndex < classIndex) { }
(*aFunc)(idValue->mRule, aData); if (nsnull != aTag) {
idValue = idValue->mNext; RuleValue* value = (RuleValue*)mTagTable.Get(&RuleKey(aTag));
idIndex = ((nsnull != idValue) ? idValue->mIndex : mRuleCount); if (nsnull != value) {
mEnumList[valueCount++] = value;
} }
else { }
(*aFunc)(classValue->mRule, aData); if (nsnull != aID) {
classValue = classValue->mNext; RuleValue* value = (RuleValue*)mIdTable.Get(&RuleKey(aID));
classIndex = ((nsnull != classValue) ? classValue->mIndex : mRuleCount); 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) void RuleHash::EnumerateTagRules(nsIAtom* aTag, RuleEnumFunc aFunc, void* aData)
{ {
RuleKey tagKey(aTag); RuleValue* tagValue = (RuleValue*)mTagTable.Get(&RuleKey(aTag));
RuleValue* value = (RuleValue*)mTagTable.Get(&tagKey); RuleValue* uniValue = (RuleValue*)mTagTable.Get(&RuleKey(nsCSSAtoms::universalSelector));
while (nsnull != value) { if (nsnull == tagValue) {
(*aFunc)(value->mRule, aData); if (nsnull != uniValue) {
value = value->mNext; 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) mRuleHash(nsnull)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
nsCSSAtoms::AddrefAtoms();
mParent = nsnull; mParent = nsnull;
mRuleCollection = nsnull; mRuleCollection = nsnull;
mImportsCollection = nsnull; mImportsCollection = nsnull;
@ -744,6 +825,7 @@ CSSStyleSheetImpl::~CSSStyleSheetImpl()
// XXX The document reference is not reference counted and should // XXX The document reference is not reference counted and should
// not be released. The document will let us know when it is going // not be released. The document will let us know when it is going
// away. // away.
nsCSSAtoms::ReleaseAtoms();
} }
NS_IMPL_ADDREF(CSSStyleSheetImpl) NS_IMPL_ADDREF(CSSStyleSheetImpl)
@ -801,76 +883,87 @@ static PRBool SelectorMatches(nsIPresContext* aPresContext,
PRBool result = PR_FALSE; PRBool result = PR_FALSE;
nsIAtom* contentTag; nsIAtom* contentTag;
aContent->GetTag(contentTag); aContent->GetTag(contentTag);
PRInt32 nameSpaceID;
aContent->GetNameSpaceID(nameSpaceID);
if ((nsnull == aSelector->mTag) || (aSelector->mTag == contentTag)) { if (((nsnull == aSelector->mTag) || (aSelector->mTag == contentTag)) &&
if ((nsnull != aSelector->mClass) || (nsnull != aSelector->mID) || ((kNameSpaceID_Unknown == aSelector->mNameSpace) || (nameSpaceID == aSelector->mNameSpace))) {
(nsnull != aSelector->mPseudoClass)) { 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; nsIHTMLContent* htmlContent;
if (NS_OK == aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent)) { if (NS_SUCCEEDED(aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent))) {
nsIAtom* contentClass;
htmlContent->GetClass(contentClass);
nsIAtom* contentID; nsIAtom* contentID;
htmlContent->GetID(contentID); htmlContent->GetID(contentID);
if ((nsnull == aSelector->mClass) || (aSelector->mClass == contentClass)) { if ((nsnull == aSelector->mID) || (aSelector->mID == contentID)) {
if ((nsnull == aSelector->mID) || (aSelector->mID == contentID)) { nsIAtom* contentClass;
if ((contentTag == nsHTMLAtoms::a) && (nsnull != aSelector->mPseudoClass)) { htmlContent->GetClass(contentClass);
// test link state // XXX only testing for frist class right now
nsILinkHandler* linkHandler; if ((nsnull == aSelector->mClassList) || (aSelector->mClassList->mAtom == contentClass)) {
result = PR_TRUE;
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;
}
} }
NS_IF_RELEASE(contentClass);
} }
NS_RELEASE(htmlContent); NS_RELEASE(htmlContent);
NS_IF_RELEASE(contentClass);
NS_IF_RELEASE(contentID); NS_IF_RELEASE(contentID);
} }
} }
else { if ((PR_TRUE == result) &&
result = PR_TRUE; (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); NS_IF_RELEASE(contentTag);
@ -899,6 +992,8 @@ static void ContentEnumFunc(nsICSSStyleRule* aRule, void* aData)
{ {
ContentEnumData* data = (ContentEnumData*)aData; ContentEnumData* data = (ContentEnumData*)aData;
// XXX not dealing with selector functions...
nsCSSSelector* selector = aRule->FirstSelector(); nsCSSSelector* selector = aRule->FirstSelector();
if (SelectorMatches(data->mPresContext, selector, data->mContent)) { if (SelectorMatches(data->mPresContext, selector, data->mContent)) {
selector = selector->mNext; selector = selector->mNext;
@ -995,7 +1090,13 @@ PRInt32 CSSStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext,
NS_RELEASE(htmlContent); 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; matchCount += data.mCount;
#ifdef DEBUG_RULES #ifdef DEBUG_RULES
@ -1005,7 +1106,7 @@ PRInt32 CSSStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext,
NS_NewISupportsArray(&list2); NS_NewISupportsArray(&list2);
data.mResults = list1; data.mResults = list1;
mRuleHash->EnumerateAllRules(tagAtom, idAtom, classAtom, ContentEnumFunc, &data); mRuleHash->EnumerateAllRules(tagAtom, idAtom, classArray, ContentEnumFunc, &data);
data.mResults = list2; data.mResults = list2;
mWeightedRules->EnumerateBackwards(ContentEnumWrap, &data); mWeightedRules->EnumerateBackwards(ContentEnumWrap, &data);
NS_ASSERTION(list1->Equals(list2), "lists not equal"); NS_ASSERTION(list1->Equals(list2), "lists not equal");

Просмотреть файл

@ -31,6 +31,9 @@
#include "nsVoidArray.h" #include "nsVoidArray.h"
#include "nsColor.h" #include "nsColor.h"
#include "nsStyleConsts.h" #include "nsStyleConsts.h"
#include "nsCSSAtoms.h"
#include "nsINameSpaceManager.h"
#include "nsINameSpace.h"
// XXX TODO: // XXX TODO:
// - rework aErrorCode stuff: switch over to nsresult // - 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 #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, // e.g. "P B, H1 B { ... }" has a selector list with two elements,
// each of which has two selectors. // each of which has two selectors.
struct SelectorList { struct SelectorList {
SelectorList* mNext; SelectorList(void);
nsVoidArray mSelectors; ~SelectorList(void);
SelectorList(); void AddSelector(const nsCSSSelector& aSelector);
void Destroy();
void AddSelector(Selector* aSelector) {
mSelectors.AppendElement(aSelector);
}
#ifdef NS_DEBUG #ifdef NS_DEBUG
void Dump(); void Dump(void);
#endif #endif
private: nsCSSSelector* mSelectors;
~SelectorList(); SelectorList* mNext;
}; };
SelectorList::SelectorList() SelectorList::SelectorList(void)
: mSelectors(nsnull),
mNext(nsnull)
{ {
mNext = nsnull;
} }
SelectorList::~SelectorList() SelectorList::~SelectorList()
{ {
PRInt32 n = mSelectors.Count(); nsCSSSelector* sel = mSelectors;
for (PRInt32 i = 0; i < n; i++) { while (nsnull != sel) {
Selector* sel = (Selector*) mSelectors.ElementAt(i); nsCSSSelector* dead = sel;
delete sel; sel = sel->mNext;
delete dead;
}
if (nsnull != mNext) {
delete mNext;
} }
} }
void SelectorList::Destroy() void SelectorList::AddSelector(const nsCSSSelector& aSelector)
{ { // prepend to list
SelectorList* list = this; nsCSSSelector* newSel = new nsCSSSelector(aSelector);
while (nsnull != list) { newSel->mNext = mSelectors;
SelectorList* next = list->mNext; mSelectors = newSel;
delete list;
list = next;
}
} }
#ifdef NS_DEBUG #ifdef NS_DEBUG
void SelectorList::Dump() 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 #endif
@ -200,6 +113,8 @@ public:
NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet); NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet);
NS_IMETHOD SetCaseSensative(PRBool aCaseSensative);
NS_IMETHOD Parse(nsIUnicharInputStream* aInput, NS_IMETHOD Parse(nsIUnicharInputStream* aInput,
nsIURL* aInputURL, nsIURL* aInputURL,
nsICSSStyleSheet*& aResult); nsICSSStyleSheet*& aResult);
@ -231,9 +146,9 @@ protected:
PRBool GatherMedia(PRInt32& aErrorCode, nsString& aMedia); PRBool GatherMedia(PRInt32& aErrorCode, nsString& aMedia);
PRBool ProcessImport(PRInt32& aErrorCode, const nsString& aURLSpec, const 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 ParseSelectorList(PRInt32& aErrorCode, SelectorList* aListHead); PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList*& aListHead);
PRBool ParseSelector(PRInt32& aErrorCode, Selector* aSelectorResult); PRBool ParseSelector(PRInt32& aErrorCode, nsCSSSelector& aSelectorResult);
nsICSSDeclaration* ParseDeclarationBlock(PRInt32& aErrorCode, nsICSSDeclaration* ParseDeclarationBlock(PRInt32& aErrorCode,
PRBool aCheckForBraces); PRBool aCheckForBraces);
PRBool ParseDeclaration(PRInt32& aErrorCode, PRBool ParseDeclaration(PRInt32& aErrorCode,
@ -317,6 +232,7 @@ protected:
PRBool mInHead; PRBool mInHead;
PRBool mNavQuirkMode; PRBool mNavQuirkMode;
PRBool mCaseSensative;
}; };
NS_HTML nsresult NS_HTML nsresult
@ -334,19 +250,23 @@ NS_NewCSSParser(nsICSSParser** aInstancePtrResult)
CSSParserImpl::CSSParserImpl() CSSParserImpl::CSSParserImpl()
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
nsCSSAtoms::AddrefAtoms();
mScanner = nsnull; mScanner = nsnull;
mSheet = nsnull; mSheet = nsnull;
mHavePushBack = PR_FALSE; mHavePushBack = PR_FALSE;
mNavQuirkMode = PR_TRUE; mNavQuirkMode = PR_TRUE;
mCaseSensative = PR_FALSE;
} }
CSSParserImpl::CSSParserImpl(nsICSSStyleSheet* aSheet) CSSParserImpl::CSSParserImpl(nsICSSStyleSheet* aSheet)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
nsCSSAtoms::AddrefAtoms();
mScanner = nsnull; mScanner = nsnull;
mSheet = aSheet; NS_ADDREF(aSheet); mSheet = aSheet; NS_ADDREF(aSheet);
mHavePushBack = PR_FALSE; mHavePushBack = PR_FALSE;
mNavQuirkMode = PR_TRUE; mNavQuirkMode = PR_TRUE;
mCaseSensative = PR_FALSE;
} }
NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID) NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID)
@ -354,16 +274,17 @@ NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID)
CSSParserImpl::~CSSParserImpl() CSSParserImpl::~CSSParserImpl()
{ {
NS_IF_RELEASE(mSheet); NS_IF_RELEASE(mSheet);
nsCSSAtoms::ReleaseAtoms();
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::GetInfoMask(PRUint32& aResult) CSSParserImpl::GetInfoMask(PRUint32& aResult)
{ {
aResult = NS_CSS_GETINFO_CSS1 | NS_CSS_GETINFO_CSSP; aResult = NS_CSS_GETINFO_CSS1 | NS_CSS_GETINFO_CSSP;
return NS_OK; return NS_OK;
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet) CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet)
{ {
NS_PRECONDITION(nsnull != aSheet, "null ptr"); NS_PRECONDITION(nsnull != aSheet, "null ptr");
@ -381,7 +302,15 @@ CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet)
return NS_OK; return NS_OK;
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::SetCaseSensative(PRBool aCaseSensative)
{
mCaseSensative = aCaseSensative;
return NS_OK;
}
NS_IMETHODIMP
CSSParserImpl::Parse(nsIUnicharInputStream* aInput, CSSParserImpl::Parse(nsIUnicharInputStream* aInput,
nsIURL* aInputURL, nsIURL* aInputURL,
nsICSSStyleSheet*& aResult) nsICSSStyleSheet*& aResult)
@ -426,7 +355,7 @@ CSSParserImpl::Parse(nsIUnicharInputStream* aInput,
return NS_OK; return NS_OK;
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::ParseDeclarations(const nsString& aDeclaration, CSSParserImpl::ParseDeclarations(const nsString& aDeclaration,
nsIURL* aBaseURL, nsIURL* aBaseURL,
nsIStyleRule*& aResult) nsIStyleRule*& aResult)
@ -471,7 +400,7 @@ CSSParserImpl::ParseDeclarations(const nsString& aDeclaration,
return NS_OK; return NS_OK;
} }
NS_METHOD NS_IMETHODIMP
CSSParserImpl::ParseAndAppendDeclaration(const nsString& aBuffer, CSSParserImpl::ParseAndAppendDeclaration(const nsString& aBuffer,
nsIURL* aBaseURL, nsIURL* aBaseURL,
nsICSSDeclaration* aDeclaration, nsICSSDeclaration* aDeclaration,
@ -925,18 +854,18 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode)
nsCSSToken* tk = &mToken; nsCSSToken* tk = &mToken;
// First get the list of selectors for the rule // First get the list of selectors for the rule
SelectorList *slist = new SelectorList(); SelectorList* slist = nsnull;
if (!ParseSelectorList(aErrorCode, slist)) { if (! ParseSelectorList(aErrorCode, slist)) {
SkipRuleSet(aErrorCode); SkipRuleSet(aErrorCode);
slist->Destroy();
return PR_FALSE; return PR_FALSE;
} }
NS_ASSERTION(nsnull != slist, "null selector list");
// Next parse the declaration block // Next parse the declaration block
nsICSSDeclaration* declaration = ParseDeclarationBlock(aErrorCode, PR_TRUE); nsICSSDeclaration* declaration = ParseDeclarationBlock(aErrorCode, PR_TRUE);
if (nsnull == declaration) { if (nsnull == declaration) {
// XXX skip something here // XXX skip something here
slist->Destroy(); delete slist;
return PR_FALSE; return PR_FALSE;
} }
@ -950,34 +879,16 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode)
// Translate the selector list and declaration block into style data // Translate the selector list and declaration block into style data
SelectorList* list = slist; SelectorList* list = slist;
nsCSSSelector selector;
nsICSSStyleRule* rule = nsnull;
while (nsnull != list) { while (nsnull != list) {
PRInt32 selIndex = list->mSelectors.Count(); PRInt32 weight = list->mSelectors->CalcWeight();
nsICSSStyleRule* rule = nsnull;
Selector* sel = (Selector*)list->mSelectors[--selIndex]; NS_NewCSSStyleRule(&rule, *(list->mSelectors));
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);
}
if (nsnull != rule) { if (nsnull != rule) {
while (--selIndex >= 0) { if (nsnull != list->mSelectors->mNext) { // hand off other selectors to new rule
Selector* sel = (Selector*)list->mSelectors[selIndex]; nsCSSSelector* ruleFirst = rule->FirstSelector();
selector.Set(sel->mTag, sel->mID, sel->mClass, sel->mPseudoClass); ruleFirst->mNext = list->mSelectors->mNext;
list->mSelectors->mNext = nsnull;
rule->AddSelector(selector);
weight += sel->Weight();
} }
rule->SetDeclaration(declaration); rule->SetDeclaration(declaration);
rule->SetWeight(weight); rule->SetWeight(weight);
@ -990,219 +901,342 @@ PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode)
} }
// Release temporary storage // Release temporary storage
slist->Destroy(); delete slist;
NS_RELEASE(declaration); NS_RELEASE(declaration);
return PR_TRUE; return PR_TRUE;
} }
PRBool CSSParserImpl::ParseSelectorList(PRInt32& aErrorCode, 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 // must have at least one selector group
aListHead = nsnull;
return PR_FALSE; return PR_FALSE;
} }
NS_ASSERTION(nsnull != list, "no selector list");
aListHead = list;
// After that there must either be a "," or a "{" // After that there must either be a "," or a "{"
nsCSSToken* tk = &mToken; nsCSSToken* tk = &mToken;
for (;;) { for (;;) {
if (!GetToken(aErrorCode, PR_TRUE)) { if (! GetToken(aErrorCode, PR_TRUE)) {
return PR_FALSE; break;
} }
if (eCSSToken_Symbol != tk->mType) { if (eCSSToken_Symbol != tk->mType) {
UngetToken(); UngetToken();
return PR_FALSE; break;
} }
if (',' == tk->mSymbol) { if (',' == tk->mSymbol) {
SelectorList* newList = nsnull;
// Another selector group must follow // Another selector group must follow
SelectorList* newList = new SelectorList(); if (! ParseSelectorGroup(aErrorCode, newList)) {
if (!ParseSelectorGroup(aErrorCode, newList)) { break;
newList->Destroy();
return PR_FALSE;
} }
// add new list to the end of the selector list // add new list to the end of the selector list
aListHead->mNext = newList; list->mNext = newList;
aListHead = newList; list = newList;
continue; continue;
} else if ('{' == tk->mSymbol) { } else if ('{' == tk->mSymbol) {
UngetToken(); UngetToken();
break; return PR_TRUE;
} else { } else {
UngetToken(); 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, PRBool CSSParserImpl::ParseSelectorGroup(PRInt32& aErrorCode,
SelectorList* aList) SelectorList*& aList)
{ {
SelectorList* list = nsnull;
for (;;) { for (;;) {
Selector* sel = new Selector(); nsCSSSelector selector;
if (!ParseSelector(aErrorCode, sel)) { if (! ParseSelector(aErrorCode, selector)) {
delete sel;
break; 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) #define SEL_MASK_NSPACE 0x01
{ #define SEL_MASK_ELEM 0x02
return (aBuffer.EqualsIgnoreCase("link") || #define SEL_MASK_ID 0x04
aBuffer.EqualsIgnoreCase("visited") || #define SEL_MASK_CLASS 0x08
aBuffer.EqualsIgnoreCase("hover") || #define SEL_MASK_ATTRIB 0x10
aBuffer.EqualsIgnoreCase("out-of-date") || // our extension #define SEL_MASK_PCLASS 0x20
aBuffer.EqualsIgnoreCase("active")); #define SEL_MASK_PELEM 0x40
}
/** /**
* These are the 31 possible kinds of CSS1 style selectors: * This is the format for selectors:
* (but there are 50 ways to leave your lover) * operator? [namespace \:]? element_name? [ ID | class | attrib | pseudo ]*
* [*] means it can repeat
* <UL>
* <LI>Tag
* <LI>Tag#Id
* <LI>Tag#Id.Class[*]
* <LI>Tag#Id.Class[*]:PseudoClass[*]
* <LI>Tag#Id.Class[*]:PseudoClass[*]:PseudoElement
* <LI>Tag#Id.Class[*]:PseudoElement
* <LI>Tag#Id:PseudoClass[*]
* <LI>Tag#Id:PseudoClass[*]:PseudoElement
* <LI>Tag#Id:PseudoElement
* <LI>Tag.Class[*]
* <LI>Tag.Class[*]:PseudoClass[*]
* <LI>Tag.Class[*]:PseudoClass[*]:PseudoElement
* <LI>Tag.Class[*]:PseudoElement
* <LI>Tag:PseudoClass[*]
* <LI>Tag:PseudoClass[*]:PseudoElement
* <LI>Tag:PseudoElement
* <LI>#Id
* <LI>#Id.Class[*]
* <LI>#Id.Class[*]:PseudoClass[*]
* <LI>#Id.Class[*]:PseudoClass[*]:PseudoElement
* <LI>#Id.Class[*]:PseudoElement
* <LI>#Id:PseudoClass[*]
* <LI>#Id:PseudoClass[*]:PseudoElement
* <LI>#Id:PseudoElement
* <LI>.Class[*]
* <LI>.Class[*]:PseudoClass[*]
* <LI>.Class[*]:PseudoClass[*]:PseudoElement
* <LI>.Class[*]:PseudoElement
* <LI>:PseudoClass[*]
* <LI>:PseudoClass[*]:PseudoElement
* <LI>:PseudoElement
* </UL>
*/ */
PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode,
Selector* aSelectorResult) nsCSSSelector& aSelector)
{ {
PRUint32 mask = 0; PRInt32 dataMask = 0;
aSelectorResult->mTag.Truncate(0); nsAutoString buffer;
aSelectorResult->mClass.Truncate(0);
aSelectorResult->mID.Truncate(0);
aSelectorResult->mPseudoClass.Truncate(0);
aSelectorResult->mPseudoElement.Truncate(0);
nsCSSToken* tk = &mToken; if (! GetToken(aErrorCode, PR_TRUE)) {
if (!GetToken(aErrorCode, PR_TRUE)) {
return PR_FALSE; return PR_FALSE;
} }
if (eCSSToken_Ident == tk->mType) { if ((eCSSToken_Symbol == mToken.mType) && ('*' == mToken.mSymbol)) { // universal element selector
// tag // don't set any tag in the selector
mask |= SELECTOR_TAG; dataMask |= SEL_MASK_ELEM;
aSelectorResult->mTag.Append(tk->mIdent); if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
if (!GetToken(aErrorCode, PR_FALSE)) {
// premature eof is ok (here!)
return PR_TRUE; return PR_TRUE;
} }
} }
if (eCSSToken_ID == tk->mType) { else if (eCSSToken_Ident == mToken.mType) { // element name
// #id PRInt32 colon = mToken.mIdent.Find(':');
if ((0 < tk->mIdent.Length()) && (nsString::IsAlpha(tk->mIdent.CharAt(0)))) { // verify is legal ID if (-1 == colon) { // no namespace
mask |= SELECTOR_ID; if (mCaseSensative) {
aSelectorResult->mID.Append(tk->mIdent); aSelector.SetTag(mToken.mIdent);
if (!GetToken(aErrorCode, PR_FALSE)) { }
// premature eof is ok (here!) else {
return PR_TRUE; mToken.mIdent.ToUpperCase(buffer);
aSelector.SetTag(buffer);
} }
} }
else { else { // pull out the namespace
return PR_FALSE; 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);
} }
} dataMask |= SEL_MASK_ELEM;
if ((eCSSToken_Symbol == tk->mType) && ('.' == tk->mSymbol)) { if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
// .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!)
return PR_TRUE; return PR_TRUE;
} }
} }
if ((eCSSToken_Symbol == tk->mType) && (':' == tk->mSymbol)) { for (;;) {
// :pseudo if (eCSSToken_ID == mToken.mType) { // #id
if (!GetToken(aErrorCode, PR_FALSE)) { if ((0 == (dataMask & SEL_MASK_ID)) && // only one ID
// premature eof (0 < mToken.mIdent.Length()) &&
return PR_FALSE; (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) { else if ((eCSSToken_Symbol == mToken.mType) && ('.' == mToken.mSymbol)) { // .class
// malformed selector if (! GetToken(aErrorCode, PR_FALSE)) { // get ident
UngetToken(); return PR_FALSE;
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)) { else if ((eCSSToken_Symbol == mToken.mType) && (':' == mToken.mSymbol)) { // :pseudo
mask |= SELECTOR_PSEUDO_CLASS; if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof
aSelectorResult->mPseudoClass.Append(tk->mIdent); 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 { else if ((eCSSToken_Symbol == mToken.mType) && ('[' == mToken.mSymbol)) { // attribute
mask |= SELECTOR_PSEUDO_ELEMENT; if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
aSelectorResult->mPseudoElement.Append(':'); // keep the colon return PR_FALSE;
aSelectorResult->mPseudoElement.Append(tk->mIdent); }
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)) { else { // not a selector token, we're done
// premature eof is ok (here!) break;
}
if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
return PR_TRUE; return PR_TRUE;
} }
} }
if ((eCSSToken_Symbol == tk->mType) && (':' == tk->mSymbol)) { UngetToken();
// :pseudo return PRBool(0 != dataMask);
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;
} }
nsICSSDeclaration* nsICSSDeclaration*

Просмотреть файл

@ -38,6 +38,7 @@
#include "nsIScriptGlobalObject.h" #include "nsIScriptGlobalObject.h"
#include "nsIScriptObjectOwner.h" #include "nsIScriptObjectOwner.h"
#include "nsDOMCSSDeclaration.h" #include "nsDOMCSSDeclaration.h"
#include "nsINameSpaceManager.h"
//#define DEBUG_REFS //#define DEBUG_REFS
@ -62,95 +63,330 @@ static NS_DEFINE_IID(kCSSTableSID, NS_CSS_TABLE_SID);
// -- nsCSSSelector ------------------------------- // -- nsCSSSelector -------------------------------
nsCSSSelector::nsCSSSelector() #define NS_IF_COPY(dest,source,type) \
: mTag(nsnull), mID(nsnull), mClass(nsnull), mPseudoClass(nsnull), 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) mNext(nsnull)
{ {
NS_IF_ADDREF(mAtom);
} }
nsCSSSelector::nsCSSSelector(nsIAtom* aTag, nsIAtom* aID, nsIAtom* aClass, nsIAtom* aPseudoClass) nsAtomList::nsAtomList(const nsString& aAtomValue)
: mTag(aTag), mID(aID), mClass(aClass), mPseudoClass(aPseudoClass), : 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) mNext(nsnull)
{ {
NS_IF_ADDREF(mTag);
NS_IF_ADDREF(mID);
NS_IF_ADDREF(mClass);
NS_IF_ADDREF(mPseudoClass);
} }
nsCSSSelector::nsCSSSelector(const nsCSSSelector& aCopy) 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) mNext(nsnull)
{ // implmented to support extension to CSS2 (when we have to copy the array) {
NS_IF_ADDREF(mTag); NS_IF_ADDREF(mTag);
NS_IF_ADDREF(mID); NS_IF_ADDREF(mID);
NS_IF_ADDREF(mClass); NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList);
NS_IF_ADDREF(mPseudoClass); NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList);
NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector);
} }
nsCSSSelector::~nsCSSSelector() nsCSSSelector::~nsCSSSelector(void)
{ {
NS_IF_RELEASE(mTag); Reset();
NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass);
NS_IF_RELEASE(mPseudoClass);
} }
nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy)
{ {
NS_IF_RELEASE(mTag); NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID); NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass); NS_IF_DELETE(mClassList);
NS_IF_RELEASE(mPseudoClass); NS_IF_DELETE(mPseudoClassList);
NS_IF_DELETE(mAttrList);
mNameSpace = aCopy.mNameSpace;
mTag = aCopy.mTag; mTag = aCopy.mTag;
mID = aCopy.mID; mID = aCopy.mID;
mClass = aCopy.mClass; NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList);
mPseudoClass = aCopy.mPseudoClass; NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList);
NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector);
mOperator = aCopy.mOperator;
NS_IF_ADDREF(mTag); NS_IF_ADDREF(mTag);
NS_IF_ADDREF(mID); NS_IF_ADDREF(mID);
NS_IF_ADDREF(mClass);
NS_IF_ADDREF(mPseudoClass);
return *this; return *this;
} }
PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const
{ {
if (this == aOther) {
return PR_TRUE;
}
if (nsnull != aOther) { if (nsnull != aOther) {
return (PRBool)((aOther->mTag == mTag) && (aOther->mID == mID) && if ((aOther->mNameSpace == mNameSpace) &&
(aOther->mClass == mClass) && (aOther->mPseudoClass == mPseudoClass)); (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; return PR_FALSE;
} }
void nsCSSSelector::Set(const nsString& aTag, const nsString& aID, void nsCSSSelector::Reset(void)
const nsString& aClass, const nsString& aPseudoClass)
{ {
nsAutoString buffer; mNameSpace = kNameSpaceID_Unknown;
NS_IF_RELEASE(mTag); NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID); NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass); NS_IF_DELETE(mClassList);
NS_IF_RELEASE(mPseudoClass); 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()) { if (0 < aTag.Length()) {
aTag.ToUpperCase(buffer); // XXX is this correct? what about class? mTag = NS_NewAtom(aTag);
mTag = NS_NewAtom(buffer);
} }
}
void nsCSSSelector::SetID(const nsString& aID)
{
NS_IF_RELEASE(mID);
if (0 < aID.Length()) { if (0 < aID.Length()) {
mID = NS_NewAtom(aID); mID = NS_NewAtom(aID);
} }
}
void nsCSSSelector::AddClass(const nsString& aClass)
{
if (0 < aClass.Length()) { if (0 < aClass.Length()) {
mClass = NS_NewAtom(aClass); nsAtomList** list = &mClassList;
} while (nsnull != *list) {
if (0 < aPseudoClass.Length()) { list = &((*list)->mNext);
aPseudoClass.ToUpperCase(buffer);
mPseudoClass = NS_NewAtom(buffer);
if (nsnull == mTag) {
mTag = nsHTMLAtoms::a;
NS_ADDREF(mTag);
} }
*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 ------------------------------- // -- CSSImportantRule -------------------------------
static nscoord CalcLength(const nsCSSValue& aValue, const nsStyleFont* aFont, static nscoord CalcLength(const nsCSSValue& aValue, const nsStyleFont* aFont,
@ -365,6 +601,8 @@ public:
virtual nsCSSSelector* FirstSelector(void); virtual nsCSSSelector* FirstSelector(void);
virtual void AddSelector(const nsCSSSelector& aSelector); virtual void AddSelector(const nsCSSSelector& aSelector);
virtual void DeleteSelector(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 nsICSSDeclaration* GetDeclaration(void) const;
virtual void SetDeclaration(nsICSSDeclaration* aDeclaration); virtual void SetDeclaration(nsICSSDeclaration* aDeclaration);
@ -410,6 +648,7 @@ protected:
PRUint32 mRefCnt : 31; PRUint32 mRefCnt : 31;
nsCSSSelector mSelector; nsCSSSelector mSelector;
nsString mSelectorText;
nsICSSDeclaration* mDeclaration; nsICSSDeclaration* mDeclaration;
PRInt32 mWeight; PRInt32 mWeight;
CSSImportantRule* mImportantRule; CSSImportantRule* mImportantRule;
@ -463,7 +702,7 @@ static const PRInt32 kInstrument = 1075;
#endif #endif
CSSStyleRuleImpl::CSSStyleRuleImpl(const nsCSSSelector& aSelector) CSSStyleRuleImpl::CSSStyleRuleImpl(const nsCSSSelector& aSelector)
: mSelector(aSelector), mDeclaration(nsnull), : mSelector(aSelector), mSelectorText(), mDeclaration(nsnull),
mWeight(0), mImportantRule(nsnull) mWeight(0), mImportantRule(nsnull)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
@ -628,16 +867,13 @@ nsCSSSelector* CSSStyleRuleImpl::FirstSelector(void)
void CSSStyleRuleImpl::AddSelector(const nsCSSSelector& aSelector) void CSSStyleRuleImpl::AddSelector(const nsCSSSelector& aSelector)
{ {
if ((nsnull != aSelector.mTag) || (nsnull != aSelector.mID) || nsCSSSelector* selector = new nsCSSSelector(aSelector);
(nsnull != aSelector.mClass) || (nsnull != aSelector.mPseudoClass)) { // skip empty selectors nsCSSSelector* last = &mSelector;
nsCSSSelector* selector = new nsCSSSelector(aSelector);
nsCSSSelector* last = &mSelector;
while (nsnull != last->mNext) { while (nsnull != last->mNext) {
last = last->mNext; last = last->mNext;
}
last->mNext = selector;
} }
last->mNext = selector;
} }
@ -645,9 +881,15 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector)
{ {
if (nsnull != aSelector) { if (nsnull != aSelector) {
if (&mSelector == aSelector) { // handle first selector if (&mSelector == aSelector) { // handle first selector
mSelector = *aSelector; // assign value if (nsnull != mSelector.mNext) {
mSelector.mNext = aSelector->mNext; nsCSSSelector* nextOne = mSelector.mNext;
delete aSelector; mSelector = *nextOne; // assign values
mSelector.mNext = nextOne->mNext;
delete nextOne;
}
else {
mSelector.Reset();
}
} }
else { else {
nsCSSSelector* selector = &mSelector; 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 nsICSSDeclaration* CSSStyleRuleImpl::GetDeclaration(void) const
{ {
NS_IF_ADDREF(mDeclaration); NS_IF_ADDREF(mDeclaration);
@ -1056,8 +1309,8 @@ void MapDeclarationInto(nsICSSDeclaration* aDeclaration,
text->mTextDecoration = td; text->mTextDecoration = td;
} }
else if (eCSSUnit_None == ourText->mDecoration.GetUnit()) { else if (eCSSUnit_None == ourText->mDecoration.GetUnit()) {
font->mFont.decorations = NS_STYLE_TEXT_DECORATION_NONE; font->mFont.decorations = parentFont->mFont.decorations;
font->mFixedFont.decorations = NS_STYLE_TEXT_DECORATION_NONE; font->mFixedFont.decorations = parentFont->mFixedFont.decorations;
text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE; text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE;
} }
else if (eCSSUnit_Inherit == ourText->mDecoration.GetUnit()) { else if (eCSSUnit_Inherit == ourText->mDecoration.GetUnit()) {
@ -1695,6 +1948,11 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector)
{ {
nsAutoString buffer; nsAutoString buffer;
if (kNameSpaceID_None < aSelector->mNameSpace) {
buffer.Append(aSelector->mNameSpace, 10);
fputs(buffer, out);
fputs("\\:", out);
}
if (nsnull != aSelector->mTag) { if (nsnull != aSelector->mTag) {
aSelector->mTag->ToString(buffer); aSelector->mTag->ToString(buffer);
fputs(buffer, out); fputs(buffer, out);
@ -1704,15 +1962,39 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector)
fputs("#", out); fputs("#", out);
fputs(buffer, out); fputs(buffer, out);
} }
if (nsnull != aSelector->mClass) { nsAtomList* list = aSelector->mClassList;
aSelector->mClass->ToString(buffer); while (nsnull != list) {
list->mAtom->ToString(buffer);
fputs(".", out); fputs(".", out);
fputs(buffer, out); fputs(buffer, out);
list = list->mNext;
} }
if (nsnull != aSelector->mPseudoClass) { list = aSelector->mPseudoClassList;
aSelector->mPseudoClass->ToString(buffer); while (nsnull != list) {
list->mAtom->ToString(buffer);
fputs(":", out); fputs(":", out);
fputs(buffer, 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 NS_IMETHODIMP
CSSStyleRuleImpl::GetSelectorText(nsString& aSelectorText) CSSStyleRuleImpl::GetSelectorText(nsString& aSelectorText)
{ {
nsAutoString buffer; aSelectorText = mSelectorText;
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;
}
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CSSStyleRuleImpl::SetSelectorText(const nsString& aSelectorText) 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; return NS_OK;
} }

Просмотреть файл

@ -42,10 +42,12 @@
#include "nsIScriptObjectOwner.h" #include "nsIScriptObjectOwner.h"
#include "nsIScriptGlobalObject.h" #include "nsIScriptGlobalObject.h"
#include "nsICSSParser.h" #include "nsICSSParser.h"
#include "nsCSSAtoms.h"
#include "nsINameSpaceManager.h"
#include "prlog.h" #include "prlog.h"
//#define DEBUG_REFS //#define DEBUG_REFS
//#define DEBUG_RULES #define DEBUG_RULES
static NS_DEFINE_IID(kICSSStyleSheetIID, NS_ICSS_STYLE_SHEET_IID); static NS_DEFINE_IID(kICSSStyleSheetIID, NS_ICSS_STYLE_SHEET_IID);
static NS_DEFINE_IID(kIStyleSheetIID, NS_ISTYLE_SHEET_IID); static NS_DEFINE_IID(kIStyleSheetIID, NS_ISTYLE_SHEET_IID);
@ -120,8 +122,8 @@ struct RuleValue {
} }
~RuleValue(void) ~RuleValue(void)
{ {
if (nsnull != mNext) { if ((nsnull != mNext) && (nsnull != mNext->mNext)) {
delete mNext; delete mNext; // don't delete that last in the chain, it's shared
} }
} }
@ -142,7 +144,7 @@ public:
RuleHash(void); RuleHash(void);
~RuleHash(void); ~RuleHash(void);
void AppendRule(nsICSSStyleRule* aRule); 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); RuleEnumFunc aFunc, void* aData);
void EnumerateTagRules(nsIAtom* aTag, void EnumerateTagRules(nsIAtom* aTag,
RuleEnumFunc aFunc, void* aData); RuleEnumFunc aFunc, void* aData);
@ -154,11 +156,17 @@ protected:
nsHashtable mTagTable; nsHashtable mTagTable;
nsHashtable mIdTable; nsHashtable mIdTable;
nsHashtable mClassTable; nsHashtable mClassTable;
RuleValue** mEnumList;
PRInt32 mEnumListSize;
RuleValue mEndValue;
}; };
RuleHash::RuleHash(void) RuleHash::RuleHash(void)
: mRuleCount(0), : 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); mTagTable.Enumerate(DeleteValue);
mIdTable.Enumerate(DeleteValue); mIdTable.Enumerate(DeleteValue);
mClassTable.Enumerate(DeleteValue); mClassTable.Enumerate(DeleteValue);
if (nsnull != mEnumList) {
delete [] mEnumList;
}
} }
void RuleHash::AppendRuleToTable(nsHashtable& aTable, nsIAtom* aAtom, nsICSSStyleRule* aRule) void RuleHash::AppendRuleToTable(nsHashtable& aTable, nsIAtom* aAtom, nsICSSStyleRule* aRule)
{ {
NS_ASSERTION(nsnull != aAtom, "null hash key");
RuleKey key(aAtom); RuleKey key(aAtom);
RuleValue* value = (RuleValue*)aTable.Get(&key); RuleValue* value = (RuleValue*)aTable.Get(&key);
if (nsnull == value) { if (nsnull == value) {
value = new RuleValue(aRule, mRuleCount++); value = new RuleValue(aRule, mRuleCount++);
aTable.Put(&key, value); aTable.Put(&key, value);
value->mNext = &mEndValue;
} }
else { else {
while (nsnull != value->mNext) { while (&mEndValue != value->mNext) {
value = value->mNext; value = value->mNext;
} }
value->mNext = new RuleValue(aRule, mRuleCount++); value->mNext = new RuleValue(aRule, mRuleCount++);
value->mNext->mNext = &mEndValue;
} }
mEndValue.mIndex = mRuleCount;
} }
void RuleHash::AppendRule(nsICSSStyleRule* aRule) void RuleHash::AppendRule(nsICSSStyleRule* aRule)
@ -198,65 +214,128 @@ void RuleHash::AppendRule(nsICSSStyleRule* aRule)
if (nsnull != selector->mID) { if (nsnull != selector->mID) {
AppendRuleToTable(mIdTable, selector->mID, aRule); AppendRuleToTable(mIdTable, selector->mID, aRule);
} }
else if (nsnull != selector->mClass) { else if (nsnull != selector->mClassList) {
AppendRuleToTable(mClassTable, selector->mClass, aRule); AppendRuleToTable(mClassTable, selector->mClassList->mAtom, aRule);
} }
else if (nsnull != selector->mTag) { else if (nsnull != selector->mTag) {
AppendRuleToTable(mTagTable, selector->mTag, aRule); 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) RuleEnumFunc aFunc, void* aData)
{ {
RuleValue* tagValue = nsnull; PRInt32 classCount = aClassList.Count();
RuleValue* idValue = nsnull; PRInt32 testCount = classCount + 1; // + 1 for universal selector
RuleValue* classValue = nsnull;
if (nsnull != aTag) { if (nsnull != aTag) {
RuleKey tagKey(aTag); testCount++;
tagValue = (RuleValue*)mTagTable.Get(&tagKey);
} }
if (nsnull != aID) { if (nsnull != aID) {
RuleKey idKey(aID); testCount++;
idValue = (RuleValue*)mIdTable.Get(&idKey);
}
if (nsnull != aClass) {
RuleKey classKey(aClass);
classValue = (RuleValue*)mClassTable.Get(&classKey);
} }
PRInt32 tagIndex = ((nsnull != tagValue) ? tagValue->mIndex : mRuleCount); if (mEnumListSize < testCount) {
PRInt32 idIndex = ((nsnull != idValue) ? idValue->mIndex : mRuleCount); delete [] mEnumList;
PRInt32 classIndex = ((nsnull != classValue) ? classValue->mIndex : mRuleCount); mEnumList = new RuleValue*[testCount];
mEnumListSize = testCount;
}
while ((nsnull != tagValue) || (nsnull != idValue) || (nsnull != classValue)) { PRInt32 index;
if ((tagIndex < idIndex) && (tagIndex < classIndex)) { PRInt32 valueCount = 0;
(*aFunc)(tagValue->mRule, aData);
tagValue = tagValue->mNext; { // universal tag rules
tagIndex = ((nsnull != tagValue) ? tagValue->mIndex : mRuleCount); RuleValue* value = (RuleValue*)mTagTable.Get(&RuleKey(nsCSSAtoms::universalSelector));
if (nsnull != value) {
mEnumList[valueCount++] = value;
} }
else if (idIndex < classIndex) { }
(*aFunc)(idValue->mRule, aData); if (nsnull != aTag) {
idValue = idValue->mNext; RuleValue* value = (RuleValue*)mTagTable.Get(&RuleKey(aTag));
idIndex = ((nsnull != idValue) ? idValue->mIndex : mRuleCount); if (nsnull != value) {
mEnumList[valueCount++] = value;
} }
else { }
(*aFunc)(classValue->mRule, aData); if (nsnull != aID) {
classValue = classValue->mNext; RuleValue* value = (RuleValue*)mIdTable.Get(&RuleKey(aID));
classIndex = ((nsnull != classValue) ? classValue->mIndex : mRuleCount); 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) void RuleHash::EnumerateTagRules(nsIAtom* aTag, RuleEnumFunc aFunc, void* aData)
{ {
RuleKey tagKey(aTag); RuleValue* tagValue = (RuleValue*)mTagTable.Get(&RuleKey(aTag));
RuleValue* value = (RuleValue*)mTagTable.Get(&tagKey); RuleValue* uniValue = (RuleValue*)mTagTable.Get(&RuleKey(nsCSSAtoms::universalSelector));
while (nsnull != value) { if (nsnull == tagValue) {
(*aFunc)(value->mRule, aData); if (nsnull != uniValue) {
value = value->mNext; 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) mRuleHash(nsnull)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
nsCSSAtoms::AddrefAtoms();
mParent = nsnull; mParent = nsnull;
mRuleCollection = nsnull; mRuleCollection = nsnull;
mImportsCollection = nsnull; mImportsCollection = nsnull;
@ -744,6 +825,7 @@ CSSStyleSheetImpl::~CSSStyleSheetImpl()
// XXX The document reference is not reference counted and should // XXX The document reference is not reference counted and should
// not be released. The document will let us know when it is going // not be released. The document will let us know when it is going
// away. // away.
nsCSSAtoms::ReleaseAtoms();
} }
NS_IMPL_ADDREF(CSSStyleSheetImpl) NS_IMPL_ADDREF(CSSStyleSheetImpl)
@ -801,76 +883,87 @@ static PRBool SelectorMatches(nsIPresContext* aPresContext,
PRBool result = PR_FALSE; PRBool result = PR_FALSE;
nsIAtom* contentTag; nsIAtom* contentTag;
aContent->GetTag(contentTag); aContent->GetTag(contentTag);
PRInt32 nameSpaceID;
aContent->GetNameSpaceID(nameSpaceID);
if ((nsnull == aSelector->mTag) || (aSelector->mTag == contentTag)) { if (((nsnull == aSelector->mTag) || (aSelector->mTag == contentTag)) &&
if ((nsnull != aSelector->mClass) || (nsnull != aSelector->mID) || ((kNameSpaceID_Unknown == aSelector->mNameSpace) || (nameSpaceID == aSelector->mNameSpace))) {
(nsnull != aSelector->mPseudoClass)) { 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; nsIHTMLContent* htmlContent;
if (NS_OK == aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent)) { if (NS_SUCCEEDED(aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent))) {
nsIAtom* contentClass;
htmlContent->GetClass(contentClass);
nsIAtom* contentID; nsIAtom* contentID;
htmlContent->GetID(contentID); htmlContent->GetID(contentID);
if ((nsnull == aSelector->mClass) || (aSelector->mClass == contentClass)) { if ((nsnull == aSelector->mID) || (aSelector->mID == contentID)) {
if ((nsnull == aSelector->mID) || (aSelector->mID == contentID)) { nsIAtom* contentClass;
if ((contentTag == nsHTMLAtoms::a) && (nsnull != aSelector->mPseudoClass)) { htmlContent->GetClass(contentClass);
// test link state // XXX only testing for frist class right now
nsILinkHandler* linkHandler; if ((nsnull == aSelector->mClassList) || (aSelector->mClassList->mAtom == contentClass)) {
result = PR_TRUE;
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;
}
} }
NS_IF_RELEASE(contentClass);
} }
NS_RELEASE(htmlContent); NS_RELEASE(htmlContent);
NS_IF_RELEASE(contentClass);
NS_IF_RELEASE(contentID); NS_IF_RELEASE(contentID);
} }
} }
else { if ((PR_TRUE == result) &&
result = PR_TRUE; (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); NS_IF_RELEASE(contentTag);
@ -899,6 +992,8 @@ static void ContentEnumFunc(nsICSSStyleRule* aRule, void* aData)
{ {
ContentEnumData* data = (ContentEnumData*)aData; ContentEnumData* data = (ContentEnumData*)aData;
// XXX not dealing with selector functions...
nsCSSSelector* selector = aRule->FirstSelector(); nsCSSSelector* selector = aRule->FirstSelector();
if (SelectorMatches(data->mPresContext, selector, data->mContent)) { if (SelectorMatches(data->mPresContext, selector, data->mContent)) {
selector = selector->mNext; selector = selector->mNext;
@ -995,7 +1090,13 @@ PRInt32 CSSStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext,
NS_RELEASE(htmlContent); 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; matchCount += data.mCount;
#ifdef DEBUG_RULES #ifdef DEBUG_RULES
@ -1005,7 +1106,7 @@ PRInt32 CSSStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext,
NS_NewISupportsArray(&list2); NS_NewISupportsArray(&list2);
data.mResults = list1; data.mResults = list1;
mRuleHash->EnumerateAllRules(tagAtom, idAtom, classAtom, ContentEnumFunc, &data); mRuleHash->EnumerateAllRules(tagAtom, idAtom, classArray, ContentEnumFunc, &data);
data.mResults = list2; data.mResults = list2;
mWeightedRules->EnumerateBackwards(ContentEnumWrap, &data); mWeightedRules->EnumerateBackwards(ContentEnumWrap, &data);
NS_ASSERTION(list1->Equals(list2), "lists not equal"); NS_ASSERTION(list1->Equals(list2), "lists not equal");