added support for CSS2 selector syntax

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

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

@ -31,6 +31,9 @@
#include "nsVoidArray.h"
#include "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()
{
NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass);
NS_IF_RELEASE(mPseudoClass);
nsCSSSelector::~nsCSSSelector(void)
{
Reset();
}
nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy)
{
NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass);
NS_IF_RELEASE(mPseudoClass);
NS_IF_DELETE(mClassList);
NS_IF_DELETE(mPseudoClassList);
NS_IF_DELETE(mAttrList);
mNameSpace = aCopy.mNameSpace;
mTag = aCopy.mTag;
mID = aCopy.mID;
mClass = aCopy.mClass;
mPseudoClass = aCopy.mPseudoClass;
NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList);
NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList);
NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector);
mOperator = aCopy.mOperator;
NS_IF_ADDREF(mTag);
NS_IF_ADDREF(mID);
NS_IF_ADDREF(mClass);
NS_IF_ADDREF(mPseudoClass);
return *this;
}
PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const
{
if (this == aOther) {
return PR_TRUE;
}
if (nsnull != aOther) {
return (PRBool)((aOther->mTag == mTag) && (aOther->mID == mID) &&
(aOther->mClass == mClass) && (aOther->mPseudoClass == mPseudoClass));
if ((aOther->mNameSpace == mNameSpace) &&
(aOther->mTag == mTag) &&
(aOther->mID == mID) &&
(aOther->mOperator == mOperator)) {
if (nsnull != mClassList) {
if (PR_FALSE == mClassList->Equals(aOther->mClassList)) {
return PR_FALSE;
}
}
else {
if (nsnull != aOther->mClassList) {
return PR_FALSE;
}
}
if (nsnull != mPseudoClassList) {
if (PR_FALSE == mPseudoClassList->Equals(aOther->mPseudoClassList)) {
return PR_FALSE;
}
}
else {
if (nsnull != aOther->mPseudoClassList) {
return PR_FALSE;
}
}
if (nsnull != mAttrList) {
if (PR_FALSE == mAttrList->Equals(aOther->mAttrList)) {
return PR_FALSE;
}
}
else {
if (nsnull != aOther->mAttrList) {
return PR_FALSE;
}
}
return PR_TRUE;
}
}
return PR_FALSE;
}
void nsCSSSelector::Set(const nsString& aTag, const nsString& aID,
const nsString& aClass, const nsString& aPseudoClass)
void nsCSSSelector::Reset(void)
{
nsAutoString buffer;
mNameSpace = kNameSpaceID_Unknown;
NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass);
NS_IF_RELEASE(mPseudoClass);
NS_IF_DELETE(mClassList);
NS_IF_DELETE(mPseudoClassList);
NS_IF_DELETE(mAttrList);
}
void nsCSSSelector::SetNameSpace(PRInt32 aNameSpace)
{
mNameSpace = aNameSpace;
}
void nsCSSSelector::SetTag(const nsString& aTag)
{
NS_IF_RELEASE(mTag);
if (0 < aTag.Length()) {
aTag.ToUpperCase(buffer); // XXX is this correct? what about class?
mTag = NS_NewAtom(buffer);
mTag = NS_NewAtom(aTag);
}
}
void nsCSSSelector::SetID(const nsString& aID)
{
NS_IF_RELEASE(mID);
if (0 < aID.Length()) {
mID = NS_NewAtom(aID);
}
}
void nsCSSSelector::AddClass(const nsString& aClass)
{
if (0 < aClass.Length()) {
mClass = NS_NewAtom(aClass);
}
if (0 < aPseudoClass.Length()) {
aPseudoClass.ToUpperCase(buffer);
mPseudoClass = NS_NewAtom(buffer);
if (nsnull == mTag) {
mTag = nsHTMLAtoms::a;
NS_ADDREF(mTag);
nsAtomList** list = &mClassList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAtomList(aClass);
}
}
void nsCSSSelector::AddPseudoClass(const nsString& aPseudoClass)
{
if (0 < aPseudoClass.Length()) {
nsAtomList** list = &mPseudoClassList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAtomList(aPseudoClass);
}
}
void nsCSSSelector::AddPseudoClass(nsIAtom* aPseudoClass)
{
if (nsnull != aPseudoClass) {
nsAtomList** list = &mPseudoClassList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAtomList(aPseudoClass);
}
}
void nsCSSSelector::AddAttribute(const nsString& aAttr)
{
if (0 < aAttr.Length()) {
nsAttrSelector** list = &mAttrList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAttrSelector(aAttr);
}
}
void nsCSSSelector::AddAttribute(const nsString& aAttr, PRUint8 aFunc, const nsString& aValue)
{
if (0 < aAttr.Length()) {
nsAttrSelector** list = &mAttrList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAttrSelector(aAttr, aFunc, aValue);
}
}
void nsCSSSelector::SetOperator(PRUnichar aOperator)
{
mOperator = aOperator;
}
PRInt32 nsCSSSelector::CalcWeight(void) const
{
PRInt32 weight = 0;
if (nsnull != mTag) {
weight += 0x000001;
}
if (nsnull != mID) {
weight += 0x010000;
}
nsAtomList* list = mClassList;
while (nsnull != list) {
weight += 0x000100;
list = list->mNext;
}
list = mPseudoClassList;
while (nsnull != list) {
weight += 0x000100;
list = list->mNext;
}
nsAttrSelector* attr = mAttrList;
while (nsnull != attr) {
weight += 0x000100;
attr = attr->mNext;
}
if (nsnull != mNext) {
weight += mNext->CalcWeight();
}
return weight;
}
// -- CSSImportantRule -------------------------------
static nscoord CalcLength(const nsCSSValue& aValue, const nsStyleFont* aFont,
@ -365,6 +601,8 @@ public:
virtual nsCSSSelector* FirstSelector(void);
virtual void AddSelector(const nsCSSSelector& aSelector);
virtual void DeleteSelector(nsCSSSelector* aSelector);
virtual void SetSourceSelectorText(const nsString& aSelectorText);
virtual void GetSourceSelectorText(nsString& aSelectorText) const;
virtual nsICSSDeclaration* GetDeclaration(void) const;
virtual void SetDeclaration(nsICSSDeclaration* aDeclaration);
@ -410,6 +648,7 @@ protected:
PRUint32 mRefCnt : 31;
nsCSSSelector mSelector;
nsString mSelectorText;
nsICSSDeclaration* mDeclaration;
PRInt32 mWeight;
CSSImportantRule* mImportantRule;
@ -463,7 +702,7 @@ static const PRInt32 kInstrument = 1075;
#endif
CSSStyleRuleImpl::CSSStyleRuleImpl(const nsCSSSelector& aSelector)
: mSelector(aSelector), mDeclaration(nsnull),
: mSelector(aSelector), mSelectorText(), mDeclaration(nsnull),
mWeight(0), mImportantRule(nsnull)
{
NS_INIT_REFCNT();
@ -628,16 +867,13 @@ nsCSSSelector* CSSStyleRuleImpl::FirstSelector(void)
void CSSStyleRuleImpl::AddSelector(const nsCSSSelector& aSelector)
{
if ((nsnull != aSelector.mTag) || (nsnull != aSelector.mID) ||
(nsnull != aSelector.mClass) || (nsnull != aSelector.mPseudoClass)) { // skip empty selectors
nsCSSSelector* selector = new nsCSSSelector(aSelector);
nsCSSSelector* last = &mSelector;
nsCSSSelector* selector = new nsCSSSelector(aSelector);
nsCSSSelector* last = &mSelector;
while (nsnull != last->mNext) {
last = last->mNext;
}
last->mNext = selector;
while (nsnull != last->mNext) {
last = last->mNext;
}
last->mNext = selector;
}
@ -645,9 +881,15 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector)
{
if (nsnull != aSelector) {
if (&mSelector == aSelector) { // handle first selector
mSelector = *aSelector; // assign value
mSelector.mNext = aSelector->mNext;
delete aSelector;
if (nsnull != mSelector.mNext) {
nsCSSSelector* nextOne = mSelector.mNext;
mSelector = *nextOne; // assign values
mSelector.mNext = nextOne->mNext;
delete nextOne;
}
else {
mSelector.Reset();
}
}
else {
nsCSSSelector* selector = &mSelector;
@ -664,6 +906,17 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector)
}
}
void CSSStyleRuleImpl::SetSourceSelectorText(const nsString& aSelectorText)
{
mSelectorText = aSelectorText;
}
void CSSStyleRuleImpl::GetSourceSelectorText(nsString& aSelectorText) const
{
aSelectorText = mSelectorText;
}
nsICSSDeclaration* CSSStyleRuleImpl::GetDeclaration(void) const
{
NS_IF_ADDREF(mDeclaration);
@ -1056,8 +1309,8 @@ void MapDeclarationInto(nsICSSDeclaration* aDeclaration,
text->mTextDecoration = td;
}
else if (eCSSUnit_None == ourText->mDecoration.GetUnit()) {
font->mFont.decorations = NS_STYLE_TEXT_DECORATION_NONE;
font->mFixedFont.decorations = NS_STYLE_TEXT_DECORATION_NONE;
font->mFont.decorations = parentFont->mFont.decorations;
font->mFixedFont.decorations = parentFont->mFixedFont.decorations;
text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE;
}
else if (eCSSUnit_Inherit == ourText->mDecoration.GetUnit()) {
@ -1695,6 +1948,11 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector)
{
nsAutoString buffer;
if (kNameSpaceID_None < aSelector->mNameSpace) {
buffer.Append(aSelector->mNameSpace, 10);
fputs(buffer, out);
fputs("\\:", out);
}
if (nsnull != aSelector->mTag) {
aSelector->mTag->ToString(buffer);
fputs(buffer, out);
@ -1704,15 +1962,39 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector)
fputs("#", out);
fputs(buffer, out);
}
if (nsnull != aSelector->mClass) {
aSelector->mClass->ToString(buffer);
nsAtomList* list = aSelector->mClassList;
while (nsnull != list) {
list->mAtom->ToString(buffer);
fputs(".", out);
fputs(buffer, out);
list = list->mNext;
}
if (nsnull != aSelector->mPseudoClass) {
aSelector->mPseudoClass->ToString(buffer);
list = aSelector->mPseudoClassList;
while (nsnull != list) {
list->mAtom->ToString(buffer);
fputs(":", out);
fputs(buffer, out);
list = list->mNext;
}
nsAttrSelector* attr = aSelector->mAttrList;
while (nsnull != attr) {
fputs("[", out);
attr->mAttr->ToString(buffer);
fputs(buffer, out);
if (NS_ATTR_FUNC_SET != attr->mFunction) {
switch (attr->mFunction) {
case NS_ATTR_FUNC_EQUALS: fputs("=", out); break;
case NS_ATTR_FUNC_INCLUDES: fputs("~=", out); break;
case NS_ATTR_FUNC_DASHMATCH: fputs("|=", out); break;
}
fputs(attr->mValue, out);
}
fputs("]", out);
}
if (0 != aSelector->mOperator) {
buffer = aSelector->mOperator;
buffer.Append(" ");
fputs(buffer, out);
}
}
@ -1782,45 +2064,16 @@ CSSStyleRuleImpl::GetSheet(nsIDOMCSSStyleSheet** aSheet)
NS_IMETHODIMP
CSSStyleRuleImpl::GetSelectorText(nsString& aSelectorText)
{
nsAutoString buffer;
nsAutoString thisSelector;
nsCSSSelector *selector = &mSelector;
// XXX Ugh...they're in reverse order from the source. Sorry
// for the ugliness.
aSelectorText.SetLength(0);
while (nsnull != selector) {
thisSelector.SetLength(0);
if (nsnull != selector->mTag) {
selector->mTag->ToString(buffer);
thisSelector.Append(buffer);
}
if (nsnull != selector->mID) {
selector->mID->ToString(buffer);
thisSelector.Append("#");
thisSelector.Append(buffer);
}
if (nsnull != selector->mClass) {
selector->mClass->ToString(buffer);
thisSelector.Append(".");
thisSelector.Append(buffer);
}
if (nsnull != selector->mPseudoClass) {
selector->mPseudoClass->ToString(buffer);
thisSelector.Append(":");
thisSelector.Append(buffer);
}
aSelectorText.Insert(thisSelector, 0, thisSelector.Length());
selector = selector->mNext;
}
aSelectorText = mSelectorText;
return NS_OK;
}
NS_IMETHODIMP
CSSStyleRuleImpl::SetSelectorText(const nsString& aSelectorText)
{
// XXX TBI
// XXX TBI - get a parser and re-parse the selectors,
// XXX then need to re-compute the cascade
mSelectorText = aSelectorText;
return NS_OK;
}

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

@ -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()
{
NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass);
NS_IF_RELEASE(mPseudoClass);
nsCSSSelector::~nsCSSSelector(void)
{
Reset();
}
nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy)
{
NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass);
NS_IF_RELEASE(mPseudoClass);
NS_IF_DELETE(mClassList);
NS_IF_DELETE(mPseudoClassList);
NS_IF_DELETE(mAttrList);
mNameSpace = aCopy.mNameSpace;
mTag = aCopy.mTag;
mID = aCopy.mID;
mClass = aCopy.mClass;
mPseudoClass = aCopy.mPseudoClass;
NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList);
NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList);
NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector);
mOperator = aCopy.mOperator;
NS_IF_ADDREF(mTag);
NS_IF_ADDREF(mID);
NS_IF_ADDREF(mClass);
NS_IF_ADDREF(mPseudoClass);
return *this;
}
PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const
{
if (this == aOther) {
return PR_TRUE;
}
if (nsnull != aOther) {
return (PRBool)((aOther->mTag == mTag) && (aOther->mID == mID) &&
(aOther->mClass == mClass) && (aOther->mPseudoClass == mPseudoClass));
if ((aOther->mNameSpace == mNameSpace) &&
(aOther->mTag == mTag) &&
(aOther->mID == mID) &&
(aOther->mOperator == mOperator)) {
if (nsnull != mClassList) {
if (PR_FALSE == mClassList->Equals(aOther->mClassList)) {
return PR_FALSE;
}
}
else {
if (nsnull != aOther->mClassList) {
return PR_FALSE;
}
}
if (nsnull != mPseudoClassList) {
if (PR_FALSE == mPseudoClassList->Equals(aOther->mPseudoClassList)) {
return PR_FALSE;
}
}
else {
if (nsnull != aOther->mPseudoClassList) {
return PR_FALSE;
}
}
if (nsnull != mAttrList) {
if (PR_FALSE == mAttrList->Equals(aOther->mAttrList)) {
return PR_FALSE;
}
}
else {
if (nsnull != aOther->mAttrList) {
return PR_FALSE;
}
}
return PR_TRUE;
}
}
return PR_FALSE;
}
void nsCSSSelector::Set(const nsString& aTag, const nsString& aID,
const nsString& aClass, const nsString& aPseudoClass)
void nsCSSSelector::Reset(void)
{
nsAutoString buffer;
mNameSpace = kNameSpaceID_Unknown;
NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass);
NS_IF_RELEASE(mPseudoClass);
NS_IF_DELETE(mClassList);
NS_IF_DELETE(mPseudoClassList);
NS_IF_DELETE(mAttrList);
}
void nsCSSSelector::SetNameSpace(PRInt32 aNameSpace)
{
mNameSpace = aNameSpace;
}
void nsCSSSelector::SetTag(const nsString& aTag)
{
NS_IF_RELEASE(mTag);
if (0 < aTag.Length()) {
aTag.ToUpperCase(buffer); // XXX is this correct? what about class?
mTag = NS_NewAtom(buffer);
mTag = NS_NewAtom(aTag);
}
}
void nsCSSSelector::SetID(const nsString& aID)
{
NS_IF_RELEASE(mID);
if (0 < aID.Length()) {
mID = NS_NewAtom(aID);
}
}
void nsCSSSelector::AddClass(const nsString& aClass)
{
if (0 < aClass.Length()) {
mClass = NS_NewAtom(aClass);
}
if (0 < aPseudoClass.Length()) {
aPseudoClass.ToUpperCase(buffer);
mPseudoClass = NS_NewAtom(buffer);
if (nsnull == mTag) {
mTag = nsHTMLAtoms::a;
NS_ADDREF(mTag);
nsAtomList** list = &mClassList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAtomList(aClass);
}
}
void nsCSSSelector::AddPseudoClass(const nsString& aPseudoClass)
{
if (0 < aPseudoClass.Length()) {
nsAtomList** list = &mPseudoClassList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAtomList(aPseudoClass);
}
}
void nsCSSSelector::AddPseudoClass(nsIAtom* aPseudoClass)
{
if (nsnull != aPseudoClass) {
nsAtomList** list = &mPseudoClassList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAtomList(aPseudoClass);
}
}
void nsCSSSelector::AddAttribute(const nsString& aAttr)
{
if (0 < aAttr.Length()) {
nsAttrSelector** list = &mAttrList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAttrSelector(aAttr);
}
}
void nsCSSSelector::AddAttribute(const nsString& aAttr, PRUint8 aFunc, const nsString& aValue)
{
if (0 < aAttr.Length()) {
nsAttrSelector** list = &mAttrList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAttrSelector(aAttr, aFunc, aValue);
}
}
void nsCSSSelector::SetOperator(PRUnichar aOperator)
{
mOperator = aOperator;
}
PRInt32 nsCSSSelector::CalcWeight(void) const
{
PRInt32 weight = 0;
if (nsnull != mTag) {
weight += 0x000001;
}
if (nsnull != mID) {
weight += 0x010000;
}
nsAtomList* list = mClassList;
while (nsnull != list) {
weight += 0x000100;
list = list->mNext;
}
list = mPseudoClassList;
while (nsnull != list) {
weight += 0x000100;
list = list->mNext;
}
nsAttrSelector* attr = mAttrList;
while (nsnull != attr) {
weight += 0x000100;
attr = attr->mNext;
}
if (nsnull != mNext) {
weight += mNext->CalcWeight();
}
return weight;
}
// -- CSSImportantRule -------------------------------
static nscoord CalcLength(const nsCSSValue& aValue, const nsStyleFont* aFont,
@ -365,6 +601,8 @@ public:
virtual nsCSSSelector* FirstSelector(void);
virtual void AddSelector(const nsCSSSelector& aSelector);
virtual void DeleteSelector(nsCSSSelector* aSelector);
virtual void SetSourceSelectorText(const nsString& aSelectorText);
virtual void GetSourceSelectorText(nsString& aSelectorText) const;
virtual nsICSSDeclaration* GetDeclaration(void) const;
virtual void SetDeclaration(nsICSSDeclaration* aDeclaration);
@ -410,6 +648,7 @@ protected:
PRUint32 mRefCnt : 31;
nsCSSSelector mSelector;
nsString mSelectorText;
nsICSSDeclaration* mDeclaration;
PRInt32 mWeight;
CSSImportantRule* mImportantRule;
@ -463,7 +702,7 @@ static const PRInt32 kInstrument = 1075;
#endif
CSSStyleRuleImpl::CSSStyleRuleImpl(const nsCSSSelector& aSelector)
: mSelector(aSelector), mDeclaration(nsnull),
: mSelector(aSelector), mSelectorText(), mDeclaration(nsnull),
mWeight(0), mImportantRule(nsnull)
{
NS_INIT_REFCNT();
@ -628,16 +867,13 @@ nsCSSSelector* CSSStyleRuleImpl::FirstSelector(void)
void CSSStyleRuleImpl::AddSelector(const nsCSSSelector& aSelector)
{
if ((nsnull != aSelector.mTag) || (nsnull != aSelector.mID) ||
(nsnull != aSelector.mClass) || (nsnull != aSelector.mPseudoClass)) { // skip empty selectors
nsCSSSelector* selector = new nsCSSSelector(aSelector);
nsCSSSelector* last = &mSelector;
nsCSSSelector* selector = new nsCSSSelector(aSelector);
nsCSSSelector* last = &mSelector;
while (nsnull != last->mNext) {
last = last->mNext;
}
last->mNext = selector;
while (nsnull != last->mNext) {
last = last->mNext;
}
last->mNext = selector;
}
@ -645,9 +881,15 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector)
{
if (nsnull != aSelector) {
if (&mSelector == aSelector) { // handle first selector
mSelector = *aSelector; // assign value
mSelector.mNext = aSelector->mNext;
delete aSelector;
if (nsnull != mSelector.mNext) {
nsCSSSelector* nextOne = mSelector.mNext;
mSelector = *nextOne; // assign values
mSelector.mNext = nextOne->mNext;
delete nextOne;
}
else {
mSelector.Reset();
}
}
else {
nsCSSSelector* selector = &mSelector;
@ -664,6 +906,17 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector)
}
}
void CSSStyleRuleImpl::SetSourceSelectorText(const nsString& aSelectorText)
{
mSelectorText = aSelectorText;
}
void CSSStyleRuleImpl::GetSourceSelectorText(nsString& aSelectorText) const
{
aSelectorText = mSelectorText;
}
nsICSSDeclaration* CSSStyleRuleImpl::GetDeclaration(void) const
{
NS_IF_ADDREF(mDeclaration);
@ -1056,8 +1309,8 @@ void MapDeclarationInto(nsICSSDeclaration* aDeclaration,
text->mTextDecoration = td;
}
else if (eCSSUnit_None == ourText->mDecoration.GetUnit()) {
font->mFont.decorations = NS_STYLE_TEXT_DECORATION_NONE;
font->mFixedFont.decorations = NS_STYLE_TEXT_DECORATION_NONE;
font->mFont.decorations = parentFont->mFont.decorations;
font->mFixedFont.decorations = parentFont->mFixedFont.decorations;
text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE;
}
else if (eCSSUnit_Inherit == ourText->mDecoration.GetUnit()) {
@ -1695,6 +1948,11 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector)
{
nsAutoString buffer;
if (kNameSpaceID_None < aSelector->mNameSpace) {
buffer.Append(aSelector->mNameSpace, 10);
fputs(buffer, out);
fputs("\\:", out);
}
if (nsnull != aSelector->mTag) {
aSelector->mTag->ToString(buffer);
fputs(buffer, out);
@ -1704,15 +1962,39 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector)
fputs("#", out);
fputs(buffer, out);
}
if (nsnull != aSelector->mClass) {
aSelector->mClass->ToString(buffer);
nsAtomList* list = aSelector->mClassList;
while (nsnull != list) {
list->mAtom->ToString(buffer);
fputs(".", out);
fputs(buffer, out);
list = list->mNext;
}
if (nsnull != aSelector->mPseudoClass) {
aSelector->mPseudoClass->ToString(buffer);
list = aSelector->mPseudoClassList;
while (nsnull != list) {
list->mAtom->ToString(buffer);
fputs(":", out);
fputs(buffer, out);
list = list->mNext;
}
nsAttrSelector* attr = aSelector->mAttrList;
while (nsnull != attr) {
fputs("[", out);
attr->mAttr->ToString(buffer);
fputs(buffer, out);
if (NS_ATTR_FUNC_SET != attr->mFunction) {
switch (attr->mFunction) {
case NS_ATTR_FUNC_EQUALS: fputs("=", out); break;
case NS_ATTR_FUNC_INCLUDES: fputs("~=", out); break;
case NS_ATTR_FUNC_DASHMATCH: fputs("|=", out); break;
}
fputs(attr->mValue, out);
}
fputs("]", out);
}
if (0 != aSelector->mOperator) {
buffer = aSelector->mOperator;
buffer.Append(" ");
fputs(buffer, out);
}
}
@ -1782,45 +2064,16 @@ CSSStyleRuleImpl::GetSheet(nsIDOMCSSStyleSheet** aSheet)
NS_IMETHODIMP
CSSStyleRuleImpl::GetSelectorText(nsString& aSelectorText)
{
nsAutoString buffer;
nsAutoString thisSelector;
nsCSSSelector *selector = &mSelector;
// XXX Ugh...they're in reverse order from the source. Sorry
// for the ugliness.
aSelectorText.SetLength(0);
while (nsnull != selector) {
thisSelector.SetLength(0);
if (nsnull != selector->mTag) {
selector->mTag->ToString(buffer);
thisSelector.Append(buffer);
}
if (nsnull != selector->mID) {
selector->mID->ToString(buffer);
thisSelector.Append("#");
thisSelector.Append(buffer);
}
if (nsnull != selector->mClass) {
selector->mClass->ToString(buffer);
thisSelector.Append(".");
thisSelector.Append(buffer);
}
if (nsnull != selector->mPseudoClass) {
selector->mPseudoClass->ToString(buffer);
thisSelector.Append(":");
thisSelector.Append(buffer);
}
aSelectorText.Insert(thisSelector, 0, thisSelector.Length());
selector = selector->mNext;
}
aSelectorText = mSelectorText;
return NS_OK;
}
NS_IMETHODIMP
CSSStyleRuleImpl::SetSelectorText(const nsString& aSelectorText)
{
// XXX TBI
// XXX TBI - get a parser and re-parse the selectors,
// XXX then need to re-compute the cascade
mSelectorText = aSelectorText;
return NS_OK;
}

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

@ -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()
{
NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass);
NS_IF_RELEASE(mPseudoClass);
nsCSSSelector::~nsCSSSelector(void)
{
Reset();
}
nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy)
{
NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass);
NS_IF_RELEASE(mPseudoClass);
NS_IF_DELETE(mClassList);
NS_IF_DELETE(mPseudoClassList);
NS_IF_DELETE(mAttrList);
mNameSpace = aCopy.mNameSpace;
mTag = aCopy.mTag;
mID = aCopy.mID;
mClass = aCopy.mClass;
mPseudoClass = aCopy.mPseudoClass;
NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList);
NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList);
NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector);
mOperator = aCopy.mOperator;
NS_IF_ADDREF(mTag);
NS_IF_ADDREF(mID);
NS_IF_ADDREF(mClass);
NS_IF_ADDREF(mPseudoClass);
return *this;
}
PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const
{
if (this == aOther) {
return PR_TRUE;
}
if (nsnull != aOther) {
return (PRBool)((aOther->mTag == mTag) && (aOther->mID == mID) &&
(aOther->mClass == mClass) && (aOther->mPseudoClass == mPseudoClass));
if ((aOther->mNameSpace == mNameSpace) &&
(aOther->mTag == mTag) &&
(aOther->mID == mID) &&
(aOther->mOperator == mOperator)) {
if (nsnull != mClassList) {
if (PR_FALSE == mClassList->Equals(aOther->mClassList)) {
return PR_FALSE;
}
}
else {
if (nsnull != aOther->mClassList) {
return PR_FALSE;
}
}
if (nsnull != mPseudoClassList) {
if (PR_FALSE == mPseudoClassList->Equals(aOther->mPseudoClassList)) {
return PR_FALSE;
}
}
else {
if (nsnull != aOther->mPseudoClassList) {
return PR_FALSE;
}
}
if (nsnull != mAttrList) {
if (PR_FALSE == mAttrList->Equals(aOther->mAttrList)) {
return PR_FALSE;
}
}
else {
if (nsnull != aOther->mAttrList) {
return PR_FALSE;
}
}
return PR_TRUE;
}
}
return PR_FALSE;
}
void nsCSSSelector::Set(const nsString& aTag, const nsString& aID,
const nsString& aClass, const nsString& aPseudoClass)
void nsCSSSelector::Reset(void)
{
nsAutoString buffer;
mNameSpace = kNameSpaceID_Unknown;
NS_IF_RELEASE(mTag);
NS_IF_RELEASE(mID);
NS_IF_RELEASE(mClass);
NS_IF_RELEASE(mPseudoClass);
NS_IF_DELETE(mClassList);
NS_IF_DELETE(mPseudoClassList);
NS_IF_DELETE(mAttrList);
}
void nsCSSSelector::SetNameSpace(PRInt32 aNameSpace)
{
mNameSpace = aNameSpace;
}
void nsCSSSelector::SetTag(const nsString& aTag)
{
NS_IF_RELEASE(mTag);
if (0 < aTag.Length()) {
aTag.ToUpperCase(buffer); // XXX is this correct? what about class?
mTag = NS_NewAtom(buffer);
mTag = NS_NewAtom(aTag);
}
}
void nsCSSSelector::SetID(const nsString& aID)
{
NS_IF_RELEASE(mID);
if (0 < aID.Length()) {
mID = NS_NewAtom(aID);
}
}
void nsCSSSelector::AddClass(const nsString& aClass)
{
if (0 < aClass.Length()) {
mClass = NS_NewAtom(aClass);
}
if (0 < aPseudoClass.Length()) {
aPseudoClass.ToUpperCase(buffer);
mPseudoClass = NS_NewAtom(buffer);
if (nsnull == mTag) {
mTag = nsHTMLAtoms::a;
NS_ADDREF(mTag);
nsAtomList** list = &mClassList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAtomList(aClass);
}
}
void nsCSSSelector::AddPseudoClass(const nsString& aPseudoClass)
{
if (0 < aPseudoClass.Length()) {
nsAtomList** list = &mPseudoClassList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAtomList(aPseudoClass);
}
}
void nsCSSSelector::AddPseudoClass(nsIAtom* aPseudoClass)
{
if (nsnull != aPseudoClass) {
nsAtomList** list = &mPseudoClassList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAtomList(aPseudoClass);
}
}
void nsCSSSelector::AddAttribute(const nsString& aAttr)
{
if (0 < aAttr.Length()) {
nsAttrSelector** list = &mAttrList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAttrSelector(aAttr);
}
}
void nsCSSSelector::AddAttribute(const nsString& aAttr, PRUint8 aFunc, const nsString& aValue)
{
if (0 < aAttr.Length()) {
nsAttrSelector** list = &mAttrList;
while (nsnull != *list) {
list = &((*list)->mNext);
}
*list = new nsAttrSelector(aAttr, aFunc, aValue);
}
}
void nsCSSSelector::SetOperator(PRUnichar aOperator)
{
mOperator = aOperator;
}
PRInt32 nsCSSSelector::CalcWeight(void) const
{
PRInt32 weight = 0;
if (nsnull != mTag) {
weight += 0x000001;
}
if (nsnull != mID) {
weight += 0x010000;
}
nsAtomList* list = mClassList;
while (nsnull != list) {
weight += 0x000100;
list = list->mNext;
}
list = mPseudoClassList;
while (nsnull != list) {
weight += 0x000100;
list = list->mNext;
}
nsAttrSelector* attr = mAttrList;
while (nsnull != attr) {
weight += 0x000100;
attr = attr->mNext;
}
if (nsnull != mNext) {
weight += mNext->CalcWeight();
}
return weight;
}
// -- CSSImportantRule -------------------------------
static nscoord CalcLength(const nsCSSValue& aValue, const nsStyleFont* aFont,
@ -365,6 +601,8 @@ public:
virtual nsCSSSelector* FirstSelector(void);
virtual void AddSelector(const nsCSSSelector& aSelector);
virtual void DeleteSelector(nsCSSSelector* aSelector);
virtual void SetSourceSelectorText(const nsString& aSelectorText);
virtual void GetSourceSelectorText(nsString& aSelectorText) const;
virtual nsICSSDeclaration* GetDeclaration(void) const;
virtual void SetDeclaration(nsICSSDeclaration* aDeclaration);
@ -410,6 +648,7 @@ protected:
PRUint32 mRefCnt : 31;
nsCSSSelector mSelector;
nsString mSelectorText;
nsICSSDeclaration* mDeclaration;
PRInt32 mWeight;
CSSImportantRule* mImportantRule;
@ -463,7 +702,7 @@ static const PRInt32 kInstrument = 1075;
#endif
CSSStyleRuleImpl::CSSStyleRuleImpl(const nsCSSSelector& aSelector)
: mSelector(aSelector), mDeclaration(nsnull),
: mSelector(aSelector), mSelectorText(), mDeclaration(nsnull),
mWeight(0), mImportantRule(nsnull)
{
NS_INIT_REFCNT();
@ -628,16 +867,13 @@ nsCSSSelector* CSSStyleRuleImpl::FirstSelector(void)
void CSSStyleRuleImpl::AddSelector(const nsCSSSelector& aSelector)
{
if ((nsnull != aSelector.mTag) || (nsnull != aSelector.mID) ||
(nsnull != aSelector.mClass) || (nsnull != aSelector.mPseudoClass)) { // skip empty selectors
nsCSSSelector* selector = new nsCSSSelector(aSelector);
nsCSSSelector* last = &mSelector;
nsCSSSelector* selector = new nsCSSSelector(aSelector);
nsCSSSelector* last = &mSelector;
while (nsnull != last->mNext) {
last = last->mNext;
}
last->mNext = selector;
while (nsnull != last->mNext) {
last = last->mNext;
}
last->mNext = selector;
}
@ -645,9 +881,15 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector)
{
if (nsnull != aSelector) {
if (&mSelector == aSelector) { // handle first selector
mSelector = *aSelector; // assign value
mSelector.mNext = aSelector->mNext;
delete aSelector;
if (nsnull != mSelector.mNext) {
nsCSSSelector* nextOne = mSelector.mNext;
mSelector = *nextOne; // assign values
mSelector.mNext = nextOne->mNext;
delete nextOne;
}
else {
mSelector.Reset();
}
}
else {
nsCSSSelector* selector = &mSelector;
@ -664,6 +906,17 @@ void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector)
}
}
void CSSStyleRuleImpl::SetSourceSelectorText(const nsString& aSelectorText)
{
mSelectorText = aSelectorText;
}
void CSSStyleRuleImpl::GetSourceSelectorText(nsString& aSelectorText) const
{
aSelectorText = mSelectorText;
}
nsICSSDeclaration* CSSStyleRuleImpl::GetDeclaration(void) const
{
NS_IF_ADDREF(mDeclaration);
@ -1056,8 +1309,8 @@ void MapDeclarationInto(nsICSSDeclaration* aDeclaration,
text->mTextDecoration = td;
}
else if (eCSSUnit_None == ourText->mDecoration.GetUnit()) {
font->mFont.decorations = NS_STYLE_TEXT_DECORATION_NONE;
font->mFixedFont.decorations = NS_STYLE_TEXT_DECORATION_NONE;
font->mFont.decorations = parentFont->mFont.decorations;
font->mFixedFont.decorations = parentFont->mFixedFont.decorations;
text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE;
}
else if (eCSSUnit_Inherit == ourText->mDecoration.GetUnit()) {
@ -1695,6 +1948,11 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector)
{
nsAutoString buffer;
if (kNameSpaceID_None < aSelector->mNameSpace) {
buffer.Append(aSelector->mNameSpace, 10);
fputs(buffer, out);
fputs("\\:", out);
}
if (nsnull != aSelector->mTag) {
aSelector->mTag->ToString(buffer);
fputs(buffer, out);
@ -1704,15 +1962,39 @@ static void ListSelector(FILE* out, const nsCSSSelector* aSelector)
fputs("#", out);
fputs(buffer, out);
}
if (nsnull != aSelector->mClass) {
aSelector->mClass->ToString(buffer);
nsAtomList* list = aSelector->mClassList;
while (nsnull != list) {
list->mAtom->ToString(buffer);
fputs(".", out);
fputs(buffer, out);
list = list->mNext;
}
if (nsnull != aSelector->mPseudoClass) {
aSelector->mPseudoClass->ToString(buffer);
list = aSelector->mPseudoClassList;
while (nsnull != list) {
list->mAtom->ToString(buffer);
fputs(":", out);
fputs(buffer, out);
list = list->mNext;
}
nsAttrSelector* attr = aSelector->mAttrList;
while (nsnull != attr) {
fputs("[", out);
attr->mAttr->ToString(buffer);
fputs(buffer, out);
if (NS_ATTR_FUNC_SET != attr->mFunction) {
switch (attr->mFunction) {
case NS_ATTR_FUNC_EQUALS: fputs("=", out); break;
case NS_ATTR_FUNC_INCLUDES: fputs("~=", out); break;
case NS_ATTR_FUNC_DASHMATCH: fputs("|=", out); break;
}
fputs(attr->mValue, out);
}
fputs("]", out);
}
if (0 != aSelector->mOperator) {
buffer = aSelector->mOperator;
buffer.Append(" ");
fputs(buffer, out);
}
}
@ -1782,45 +2064,16 @@ CSSStyleRuleImpl::GetSheet(nsIDOMCSSStyleSheet** aSheet)
NS_IMETHODIMP
CSSStyleRuleImpl::GetSelectorText(nsString& aSelectorText)
{
nsAutoString buffer;
nsAutoString thisSelector;
nsCSSSelector *selector = &mSelector;
// XXX Ugh...they're in reverse order from the source. Sorry
// for the ugliness.
aSelectorText.SetLength(0);
while (nsnull != selector) {
thisSelector.SetLength(0);
if (nsnull != selector->mTag) {
selector->mTag->ToString(buffer);
thisSelector.Append(buffer);
}
if (nsnull != selector->mID) {
selector->mID->ToString(buffer);
thisSelector.Append("#");
thisSelector.Append(buffer);
}
if (nsnull != selector->mClass) {
selector->mClass->ToString(buffer);
thisSelector.Append(".");
thisSelector.Append(buffer);
}
if (nsnull != selector->mPseudoClass) {
selector->mPseudoClass->ToString(buffer);
thisSelector.Append(":");
thisSelector.Append(buffer);
}
aSelectorText.Insert(thisSelector, 0, thisSelector.Length());
selector = selector->mNext;
}
aSelectorText = mSelectorText;
return NS_OK;
}
NS_IMETHODIMP
CSSStyleRuleImpl::SetSelectorText(const nsString& aSelectorText)
{
// XXX TBI
// XXX TBI - get a parser and re-parse the selectors,
// XXX then need to re-compute the cascade
mSelectorText = aSelectorText;
return NS_OK;
}

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

@ -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");