зеркало из https://github.com/mozilla/pjs.git
added support for CSS2 selector syntax
This commit is contained in:
Родитель
846697e4a9
Коммит
e0ce171455
|
@ -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");
|
||||||
|
|
Загрузка…
Ссылка в новой задаче