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