diff --git a/content/html/style/src/nsCSSStyleSheet.cpp b/content/html/style/src/nsCSSStyleSheet.cpp index 84eb70724225..c2bea3685b02 100644 --- a/content/html/style/src/nsCSSStyleSheet.cpp +++ b/content/html/style/src/nsCSSStyleSheet.cpp @@ -28,6 +28,7 @@ #include "nsIDocument.h" #include "nsIPresContext.h" #include "nsILinkHandler.h" +#include "nsIEventStateManager.h" #include "nsHTMLAtoms.h" #include "nsLayoutAtoms.h" #include "nsIFrame.h" @@ -964,6 +965,20 @@ static PRBool ValueDashMatch(const nsString& aValueList, const nsString& aValue, return PR_FALSE; } +static PRBool IsEventPseudo(nsIAtom* aAtom) +{ + return PRBool ((nsCSSAtoms::activePseudo == aAtom) || + (nsCSSAtoms::focusPseudo == aAtom) || + (nsCSSAtoms::hoverPseudo == aAtom)); + // XXX selected, enabled, disabled, selection? +} + +static PRBool IsLinkPseudo(nsIAtom* aAtom) +{ + return PRBool ((nsCSSAtoms::linkPseudo == aAtom) || + (nsCSSAtoms::outOfDatePseudo == aAtom) || + (nsCSSAtoms::visitedPseudo == aAtom)); +} static PRBool SelectorMatches(nsIPresContext* aPresContext, nsCSSSelector* aSelector, nsIContent* aContent) @@ -1032,65 +1047,120 @@ static PRBool SelectorMatches(nsIPresContext* aPresContext, } 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; + // first-child, lang, active, focus, hover, link, outOfDate, visited + // XXX disabled, enabled, selected, selection + nsAtomList* pseudoClass = aSelector->mPseudoClassList; + nsLinkEventState eventState = eLinkState_Unspecified; + nsLinkState linkState = nsLinkState(-1); // not a link + nsILinkHandler* linkHandler = nsnull; + nsIEventStateManager* eventStateManager = nsnull; - if ((NS_OK == aPresContext->GetLinkHandler(&linkHandler)) && - (nsnull != linkHandler)) { - nsAutoString base, href; - nsresult attrState = aContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::href, href); - - if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { - nsIURL* docURL = nsnull; - nsIHTMLContent* htmlContent; - if (NS_SUCCEEDED(aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent))) { - htmlContent->GetBaseURL(docURL); - NS_RELEASE(htmlContent); - } - else { - nsIDocument* doc = nsnull; - aContent->GetDocument(doc); - if (nsnull != doc) { - doc->GetBaseURL(docURL); - NS_RELEASE(doc); + while ((PR_TRUE == result) && (nsnull != pseudoClass)) { + if (nsCSSAtoms::firstChildPseudo == pseudoClass->mAtom) { + nsIContent* firstChild = nsnull; + nsIContent* parent; + aContent->GetParent(parent); + if (parent) { + PRInt32 index = -1; + do { + parent->ChildAt(++index, firstChild); + if (firstChild) { // skip text & comments + nsIAtom* tag; + firstChild->GetTag(tag); + if ((tag != nsLayoutAtoms::textTagName) && + (tag != nsLayoutAtoms::commentTagName)) { + NS_IF_RELEASE(tag); + break; + } + NS_IF_RELEASE(tag); + NS_RELEASE(firstChild); } - } - - nsAutoString absURLSpec; - 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; - //These cases have been moved from nsILinkHandler to to nsIEventStateManager. - //Code needs to be adjusted to get these state items from their new location. - /*case eLinkState_Active: - result = PRBool (pseudo == nsCSSAtoms::activePseudo); - break; - case eLinkState_Hover: - result = PRBool (pseudo == nsCSSAtoms::hoverPseudo); - break;*/ + else { + break; } + } while (1 == 1); + NS_RELEASE(parent); + } + result = PRBool(aContent == firstChild); + NS_IF_RELEASE(firstChild); + } + else if (nsCSSAtoms::langPseudo == pseudoClass->mAtom) { + // XXX not yet implemented + result = PR_FALSE; + } + else if (IsEventPseudo(pseudoClass->mAtom)) { + if (! eventStateManager) { + aPresContext->GetEventStateManager(&eventStateManager); + if (eventStateManager) { + eventStateManager->GetLinkState(aContent, eventState); } } - NS_RELEASE(linkHandler); + if (nsCSSAtoms::activePseudo == pseudoClass->mAtom) { + result = PRBool(0 != (eventState & eLinkState_Active)); + } + else if (nsCSSAtoms::focusPseudo == pseudoClass->mAtom) { +// result = PRBool(0 != (eventState & eLinkState_Focus)); + result = PR_FALSE; + } + else if (nsCSSAtoms::hoverPseudo == pseudoClass->mAtom) { + result = PRBool(0 != (eventState & eLinkState_Hover)); + } } + else if (IsLinkPseudo(pseudoClass->mAtom)) { + // XXX xml link too + if (nsHTMLAtoms::a == contentTag) { + if (! linkHandler) { + aPresContext->GetLinkHandler(&linkHandler); + if (linkHandler) { + nsAutoString base, href; + nsresult attrState = aContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::href, href); + + if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { + nsIURL* docURL = nsnull; + nsIHTMLContent* htmlContent; + if (NS_SUCCEEDED(aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent))) { + htmlContent->GetBaseURL(docURL); + NS_RELEASE(htmlContent); + } + else { + nsIDocument* doc = nsnull; + aContent->GetDocument(doc); + if (nsnull != doc) { + doc->GetBaseURL(docURL); + NS_RELEASE(doc); + } + } + + nsAutoString absURLSpec; + NS_MakeAbsoluteURL(docURL, base, href, absURLSpec); + NS_IF_RELEASE(docURL); + + linkHandler->GetLinkState(absURLSpec, linkState); + } + } + } + if (nsCSSAtoms::linkPseudo == pseudoClass->mAtom) { + result = PRBool(eLinkState_Unvisited == linkState); + } + else if (nsCSSAtoms::outOfDatePseudo == pseudoClass->mAtom) { + result = PRBool(eLinkState_OutOfDate == linkState); + } + else if (nsCSSAtoms::visitedPseudo == pseudoClass->mAtom) { + result = PRBool(eLinkState_Visited == linkState); + } + } + else { + result = PR_FALSE; // not a link + } + } + else { + result = PR_FALSE; // unknown pseudo class + } + pseudoClass = pseudoClass->mNext; } + + NS_IF_RELEASE(linkHandler); + NS_IF_RELEASE(eventStateManager); } } NS_IF_RELEASE(contentTag); @@ -1115,29 +1185,70 @@ struct ContentEnumData { PRInt32 mCount; }; +static PRBool SelectorMatchesTree(nsIPresContext* aPresContext, + nsIContent* aLastContent, + nsCSSSelector* aSelector) +{ + nsCSSSelector* selector = aSelector; + + if (selector) { + nsIContent* content = nsnull; + nsIContent* lastContent = aLastContent; + NS_ADDREF(lastContent); + while (nsnull != selector) { // check compound selectors + if (PRUnichar('+') == selector->mOperator) { // fetch previous sibling + nsIContent* parent; + PRInt32 index; + lastContent->GetParent(parent); + if (parent) { + parent->IndexOf(lastContent, index); + while (0 <= --index) { // skip text & comment nodes + parent->ChildAt(index, content); + nsIAtom* tag; + content->GetTag(tag); + if ((tag != nsLayoutAtoms::textTagName) && + (tag != nsLayoutAtoms::commentTagName)) { + NS_IF_RELEASE(tag); + break; + } + NS_RELEASE(content); + NS_IF_RELEASE(tag); + } + NS_RELEASE(parent); + } + } + else { // fetch parent + lastContent->GetParent(content); + } + if (! content) { + break; + } + if (SelectorMatches(aPresContext, selector, content)) { + selector = selector->mNext; + } + else { + if (PRUnichar(0) != selector->mOperator) { + NS_RELEASE(content); + break; // parent was required to match + } + } + NS_IF_RELEASE(lastContent); + lastContent = content; // take refcount + content = nsnull; + } + NS_IF_RELEASE(lastContent); + } + return PRBool(nsnull == selector); // matches if ran out of selectors +} + 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; - - nsIContent* content = nsnull; - nsIContent* lastContent = nsnull; - data->mContent->GetParent(content); - while ((nsnull != selector) && (nsnull != content)) { // check compound selectors - if (SelectorMatches(data->mPresContext, selector, content)) { - selector = selector->mNext; - } - lastContent = content; - content->GetParent(content); - NS_IF_RELEASE(lastContent); - } - NS_IF_RELEASE(content); - if (nsnull == selector) { // ran out, it matched + if (SelectorMatchesTree(data->mPresContext, data->mContent, selector)) { nsIStyleRule* iRule; if (NS_OK == aRule->QueryInterface(kIStyleRuleIID, (void**)&iRule)) { data->mResults->AppendElement(iRule); @@ -1270,30 +1381,36 @@ static void PseudoEnumFunc(nsICSSStyleRule* aRule, void* aData) nsCSSSelector* selector = aRule->FirstSelector(); if (selector->mTag == data->mPseudoTag) { selector = selector->mNext; - nsIContent* content = data->mParentContent; - nsIContent* lastContent = nsnull; - NS_IF_ADDREF(content); - while ((nsnull != selector) && (nsnull != content)) { // check compound selectors - if (SelectorMatches(data->mPresContext, selector, content)) { + + if (selector) { // test next selector specially + if (PRUnichar('+') == selector->mOperator) { + return; // not valid here, can't match + } + if (SelectorMatches(data->mPresContext, selector, data->mParentContent)) { selector = selector->mNext; } - lastContent = content; - content->GetParent(content); - NS_IF_RELEASE(lastContent); + else { + if (PRUnichar('>') == selector->mOperator) { + return; // immediate parent didn't match + } + } } - NS_IF_RELEASE(content); - if (nsnull == selector) { // ran out, it matched - nsIStyleRule* iRule; - if (NS_OK == aRule->QueryInterface(kIStyleRuleIID, (void**)&iRule)) { + + if (selector && + (! SelectorMatchesTree(data->mPresContext, data->mParentContent, selector))) { + return; // remaining selectors didn't match + } + + nsIStyleRule* iRule; + if (NS_OK == aRule->QueryInterface(kIStyleRuleIID, (void**)&iRule)) { + data->mResults->AppendElement(iRule); + data->mCount++; + NS_RELEASE(iRule); + iRule = aRule->GetImportantRule(); + if (nsnull != iRule) { data->mResults->AppendElement(iRule); data->mCount++; NS_RELEASE(iRule); - iRule = aRule->GetImportantRule(); - if (nsnull != iRule) { - data->mResults->AppendElement(iRule); - data->mCount++; - NS_RELEASE(iRule); - } } } } diff --git a/layout/html/style/src/nsCSSStyleSheet.cpp b/layout/html/style/src/nsCSSStyleSheet.cpp index 84eb70724225..c2bea3685b02 100644 --- a/layout/html/style/src/nsCSSStyleSheet.cpp +++ b/layout/html/style/src/nsCSSStyleSheet.cpp @@ -28,6 +28,7 @@ #include "nsIDocument.h" #include "nsIPresContext.h" #include "nsILinkHandler.h" +#include "nsIEventStateManager.h" #include "nsHTMLAtoms.h" #include "nsLayoutAtoms.h" #include "nsIFrame.h" @@ -964,6 +965,20 @@ static PRBool ValueDashMatch(const nsString& aValueList, const nsString& aValue, return PR_FALSE; } +static PRBool IsEventPseudo(nsIAtom* aAtom) +{ + return PRBool ((nsCSSAtoms::activePseudo == aAtom) || + (nsCSSAtoms::focusPseudo == aAtom) || + (nsCSSAtoms::hoverPseudo == aAtom)); + // XXX selected, enabled, disabled, selection? +} + +static PRBool IsLinkPseudo(nsIAtom* aAtom) +{ + return PRBool ((nsCSSAtoms::linkPseudo == aAtom) || + (nsCSSAtoms::outOfDatePseudo == aAtom) || + (nsCSSAtoms::visitedPseudo == aAtom)); +} static PRBool SelectorMatches(nsIPresContext* aPresContext, nsCSSSelector* aSelector, nsIContent* aContent) @@ -1032,65 +1047,120 @@ static PRBool SelectorMatches(nsIPresContext* aPresContext, } 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; + // first-child, lang, active, focus, hover, link, outOfDate, visited + // XXX disabled, enabled, selected, selection + nsAtomList* pseudoClass = aSelector->mPseudoClassList; + nsLinkEventState eventState = eLinkState_Unspecified; + nsLinkState linkState = nsLinkState(-1); // not a link + nsILinkHandler* linkHandler = nsnull; + nsIEventStateManager* eventStateManager = nsnull; - if ((NS_OK == aPresContext->GetLinkHandler(&linkHandler)) && - (nsnull != linkHandler)) { - nsAutoString base, href; - nsresult attrState = aContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::href, href); - - if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { - nsIURL* docURL = nsnull; - nsIHTMLContent* htmlContent; - if (NS_SUCCEEDED(aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent))) { - htmlContent->GetBaseURL(docURL); - NS_RELEASE(htmlContent); - } - else { - nsIDocument* doc = nsnull; - aContent->GetDocument(doc); - if (nsnull != doc) { - doc->GetBaseURL(docURL); - NS_RELEASE(doc); + while ((PR_TRUE == result) && (nsnull != pseudoClass)) { + if (nsCSSAtoms::firstChildPseudo == pseudoClass->mAtom) { + nsIContent* firstChild = nsnull; + nsIContent* parent; + aContent->GetParent(parent); + if (parent) { + PRInt32 index = -1; + do { + parent->ChildAt(++index, firstChild); + if (firstChild) { // skip text & comments + nsIAtom* tag; + firstChild->GetTag(tag); + if ((tag != nsLayoutAtoms::textTagName) && + (tag != nsLayoutAtoms::commentTagName)) { + NS_IF_RELEASE(tag); + break; + } + NS_IF_RELEASE(tag); + NS_RELEASE(firstChild); } - } - - nsAutoString absURLSpec; - 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; - //These cases have been moved from nsILinkHandler to to nsIEventStateManager. - //Code needs to be adjusted to get these state items from their new location. - /*case eLinkState_Active: - result = PRBool (pseudo == nsCSSAtoms::activePseudo); - break; - case eLinkState_Hover: - result = PRBool (pseudo == nsCSSAtoms::hoverPseudo); - break;*/ + else { + break; } + } while (1 == 1); + NS_RELEASE(parent); + } + result = PRBool(aContent == firstChild); + NS_IF_RELEASE(firstChild); + } + else if (nsCSSAtoms::langPseudo == pseudoClass->mAtom) { + // XXX not yet implemented + result = PR_FALSE; + } + else if (IsEventPseudo(pseudoClass->mAtom)) { + if (! eventStateManager) { + aPresContext->GetEventStateManager(&eventStateManager); + if (eventStateManager) { + eventStateManager->GetLinkState(aContent, eventState); } } - NS_RELEASE(linkHandler); + if (nsCSSAtoms::activePseudo == pseudoClass->mAtom) { + result = PRBool(0 != (eventState & eLinkState_Active)); + } + else if (nsCSSAtoms::focusPseudo == pseudoClass->mAtom) { +// result = PRBool(0 != (eventState & eLinkState_Focus)); + result = PR_FALSE; + } + else if (nsCSSAtoms::hoverPseudo == pseudoClass->mAtom) { + result = PRBool(0 != (eventState & eLinkState_Hover)); + } } + else if (IsLinkPseudo(pseudoClass->mAtom)) { + // XXX xml link too + if (nsHTMLAtoms::a == contentTag) { + if (! linkHandler) { + aPresContext->GetLinkHandler(&linkHandler); + if (linkHandler) { + nsAutoString base, href; + nsresult attrState = aContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::href, href); + + if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { + nsIURL* docURL = nsnull; + nsIHTMLContent* htmlContent; + if (NS_SUCCEEDED(aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent))) { + htmlContent->GetBaseURL(docURL); + NS_RELEASE(htmlContent); + } + else { + nsIDocument* doc = nsnull; + aContent->GetDocument(doc); + if (nsnull != doc) { + doc->GetBaseURL(docURL); + NS_RELEASE(doc); + } + } + + nsAutoString absURLSpec; + NS_MakeAbsoluteURL(docURL, base, href, absURLSpec); + NS_IF_RELEASE(docURL); + + linkHandler->GetLinkState(absURLSpec, linkState); + } + } + } + if (nsCSSAtoms::linkPseudo == pseudoClass->mAtom) { + result = PRBool(eLinkState_Unvisited == linkState); + } + else if (nsCSSAtoms::outOfDatePseudo == pseudoClass->mAtom) { + result = PRBool(eLinkState_OutOfDate == linkState); + } + else if (nsCSSAtoms::visitedPseudo == pseudoClass->mAtom) { + result = PRBool(eLinkState_Visited == linkState); + } + } + else { + result = PR_FALSE; // not a link + } + } + else { + result = PR_FALSE; // unknown pseudo class + } + pseudoClass = pseudoClass->mNext; } + + NS_IF_RELEASE(linkHandler); + NS_IF_RELEASE(eventStateManager); } } NS_IF_RELEASE(contentTag); @@ -1115,29 +1185,70 @@ struct ContentEnumData { PRInt32 mCount; }; +static PRBool SelectorMatchesTree(nsIPresContext* aPresContext, + nsIContent* aLastContent, + nsCSSSelector* aSelector) +{ + nsCSSSelector* selector = aSelector; + + if (selector) { + nsIContent* content = nsnull; + nsIContent* lastContent = aLastContent; + NS_ADDREF(lastContent); + while (nsnull != selector) { // check compound selectors + if (PRUnichar('+') == selector->mOperator) { // fetch previous sibling + nsIContent* parent; + PRInt32 index; + lastContent->GetParent(parent); + if (parent) { + parent->IndexOf(lastContent, index); + while (0 <= --index) { // skip text & comment nodes + parent->ChildAt(index, content); + nsIAtom* tag; + content->GetTag(tag); + if ((tag != nsLayoutAtoms::textTagName) && + (tag != nsLayoutAtoms::commentTagName)) { + NS_IF_RELEASE(tag); + break; + } + NS_RELEASE(content); + NS_IF_RELEASE(tag); + } + NS_RELEASE(parent); + } + } + else { // fetch parent + lastContent->GetParent(content); + } + if (! content) { + break; + } + if (SelectorMatches(aPresContext, selector, content)) { + selector = selector->mNext; + } + else { + if (PRUnichar(0) != selector->mOperator) { + NS_RELEASE(content); + break; // parent was required to match + } + } + NS_IF_RELEASE(lastContent); + lastContent = content; // take refcount + content = nsnull; + } + NS_IF_RELEASE(lastContent); + } + return PRBool(nsnull == selector); // matches if ran out of selectors +} + 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; - - nsIContent* content = nsnull; - nsIContent* lastContent = nsnull; - data->mContent->GetParent(content); - while ((nsnull != selector) && (nsnull != content)) { // check compound selectors - if (SelectorMatches(data->mPresContext, selector, content)) { - selector = selector->mNext; - } - lastContent = content; - content->GetParent(content); - NS_IF_RELEASE(lastContent); - } - NS_IF_RELEASE(content); - if (nsnull == selector) { // ran out, it matched + if (SelectorMatchesTree(data->mPresContext, data->mContent, selector)) { nsIStyleRule* iRule; if (NS_OK == aRule->QueryInterface(kIStyleRuleIID, (void**)&iRule)) { data->mResults->AppendElement(iRule); @@ -1270,30 +1381,36 @@ static void PseudoEnumFunc(nsICSSStyleRule* aRule, void* aData) nsCSSSelector* selector = aRule->FirstSelector(); if (selector->mTag == data->mPseudoTag) { selector = selector->mNext; - nsIContent* content = data->mParentContent; - nsIContent* lastContent = nsnull; - NS_IF_ADDREF(content); - while ((nsnull != selector) && (nsnull != content)) { // check compound selectors - if (SelectorMatches(data->mPresContext, selector, content)) { + + if (selector) { // test next selector specially + if (PRUnichar('+') == selector->mOperator) { + return; // not valid here, can't match + } + if (SelectorMatches(data->mPresContext, selector, data->mParentContent)) { selector = selector->mNext; } - lastContent = content; - content->GetParent(content); - NS_IF_RELEASE(lastContent); + else { + if (PRUnichar('>') == selector->mOperator) { + return; // immediate parent didn't match + } + } } - NS_IF_RELEASE(content); - if (nsnull == selector) { // ran out, it matched - nsIStyleRule* iRule; - if (NS_OK == aRule->QueryInterface(kIStyleRuleIID, (void**)&iRule)) { + + if (selector && + (! SelectorMatchesTree(data->mPresContext, data->mParentContent, selector))) { + return; // remaining selectors didn't match + } + + nsIStyleRule* iRule; + if (NS_OK == aRule->QueryInterface(kIStyleRuleIID, (void**)&iRule)) { + data->mResults->AppendElement(iRule); + data->mCount++; + NS_RELEASE(iRule); + iRule = aRule->GetImportantRule(); + if (nsnull != iRule) { data->mResults->AppendElement(iRule); data->mCount++; NS_RELEASE(iRule); - iRule = aRule->GetImportantRule(); - if (nsnull != iRule) { - data->mResults->AppendElement(iRule); - data->mCount++; - NS_RELEASE(iRule); - } } } } diff --git a/layout/style/nsCSSStyleSheet.cpp b/layout/style/nsCSSStyleSheet.cpp index 84eb70724225..c2bea3685b02 100644 --- a/layout/style/nsCSSStyleSheet.cpp +++ b/layout/style/nsCSSStyleSheet.cpp @@ -28,6 +28,7 @@ #include "nsIDocument.h" #include "nsIPresContext.h" #include "nsILinkHandler.h" +#include "nsIEventStateManager.h" #include "nsHTMLAtoms.h" #include "nsLayoutAtoms.h" #include "nsIFrame.h" @@ -964,6 +965,20 @@ static PRBool ValueDashMatch(const nsString& aValueList, const nsString& aValue, return PR_FALSE; } +static PRBool IsEventPseudo(nsIAtom* aAtom) +{ + return PRBool ((nsCSSAtoms::activePseudo == aAtom) || + (nsCSSAtoms::focusPseudo == aAtom) || + (nsCSSAtoms::hoverPseudo == aAtom)); + // XXX selected, enabled, disabled, selection? +} + +static PRBool IsLinkPseudo(nsIAtom* aAtom) +{ + return PRBool ((nsCSSAtoms::linkPseudo == aAtom) || + (nsCSSAtoms::outOfDatePseudo == aAtom) || + (nsCSSAtoms::visitedPseudo == aAtom)); +} static PRBool SelectorMatches(nsIPresContext* aPresContext, nsCSSSelector* aSelector, nsIContent* aContent) @@ -1032,65 +1047,120 @@ static PRBool SelectorMatches(nsIPresContext* aPresContext, } 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; + // first-child, lang, active, focus, hover, link, outOfDate, visited + // XXX disabled, enabled, selected, selection + nsAtomList* pseudoClass = aSelector->mPseudoClassList; + nsLinkEventState eventState = eLinkState_Unspecified; + nsLinkState linkState = nsLinkState(-1); // not a link + nsILinkHandler* linkHandler = nsnull; + nsIEventStateManager* eventStateManager = nsnull; - if ((NS_OK == aPresContext->GetLinkHandler(&linkHandler)) && - (nsnull != linkHandler)) { - nsAutoString base, href; - nsresult attrState = aContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::href, href); - - if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { - nsIURL* docURL = nsnull; - nsIHTMLContent* htmlContent; - if (NS_SUCCEEDED(aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent))) { - htmlContent->GetBaseURL(docURL); - NS_RELEASE(htmlContent); - } - else { - nsIDocument* doc = nsnull; - aContent->GetDocument(doc); - if (nsnull != doc) { - doc->GetBaseURL(docURL); - NS_RELEASE(doc); + while ((PR_TRUE == result) && (nsnull != pseudoClass)) { + if (nsCSSAtoms::firstChildPseudo == pseudoClass->mAtom) { + nsIContent* firstChild = nsnull; + nsIContent* parent; + aContent->GetParent(parent); + if (parent) { + PRInt32 index = -1; + do { + parent->ChildAt(++index, firstChild); + if (firstChild) { // skip text & comments + nsIAtom* tag; + firstChild->GetTag(tag); + if ((tag != nsLayoutAtoms::textTagName) && + (tag != nsLayoutAtoms::commentTagName)) { + NS_IF_RELEASE(tag); + break; + } + NS_IF_RELEASE(tag); + NS_RELEASE(firstChild); } - } - - nsAutoString absURLSpec; - 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; - //These cases have been moved from nsILinkHandler to to nsIEventStateManager. - //Code needs to be adjusted to get these state items from their new location. - /*case eLinkState_Active: - result = PRBool (pseudo == nsCSSAtoms::activePseudo); - break; - case eLinkState_Hover: - result = PRBool (pseudo == nsCSSAtoms::hoverPseudo); - break;*/ + else { + break; } + } while (1 == 1); + NS_RELEASE(parent); + } + result = PRBool(aContent == firstChild); + NS_IF_RELEASE(firstChild); + } + else if (nsCSSAtoms::langPseudo == pseudoClass->mAtom) { + // XXX not yet implemented + result = PR_FALSE; + } + else if (IsEventPseudo(pseudoClass->mAtom)) { + if (! eventStateManager) { + aPresContext->GetEventStateManager(&eventStateManager); + if (eventStateManager) { + eventStateManager->GetLinkState(aContent, eventState); } } - NS_RELEASE(linkHandler); + if (nsCSSAtoms::activePseudo == pseudoClass->mAtom) { + result = PRBool(0 != (eventState & eLinkState_Active)); + } + else if (nsCSSAtoms::focusPseudo == pseudoClass->mAtom) { +// result = PRBool(0 != (eventState & eLinkState_Focus)); + result = PR_FALSE; + } + else if (nsCSSAtoms::hoverPseudo == pseudoClass->mAtom) { + result = PRBool(0 != (eventState & eLinkState_Hover)); + } } + else if (IsLinkPseudo(pseudoClass->mAtom)) { + // XXX xml link too + if (nsHTMLAtoms::a == contentTag) { + if (! linkHandler) { + aPresContext->GetLinkHandler(&linkHandler); + if (linkHandler) { + nsAutoString base, href; + nsresult attrState = aContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::href, href); + + if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { + nsIURL* docURL = nsnull; + nsIHTMLContent* htmlContent; + if (NS_SUCCEEDED(aContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent))) { + htmlContent->GetBaseURL(docURL); + NS_RELEASE(htmlContent); + } + else { + nsIDocument* doc = nsnull; + aContent->GetDocument(doc); + if (nsnull != doc) { + doc->GetBaseURL(docURL); + NS_RELEASE(doc); + } + } + + nsAutoString absURLSpec; + NS_MakeAbsoluteURL(docURL, base, href, absURLSpec); + NS_IF_RELEASE(docURL); + + linkHandler->GetLinkState(absURLSpec, linkState); + } + } + } + if (nsCSSAtoms::linkPseudo == pseudoClass->mAtom) { + result = PRBool(eLinkState_Unvisited == linkState); + } + else if (nsCSSAtoms::outOfDatePseudo == pseudoClass->mAtom) { + result = PRBool(eLinkState_OutOfDate == linkState); + } + else if (nsCSSAtoms::visitedPseudo == pseudoClass->mAtom) { + result = PRBool(eLinkState_Visited == linkState); + } + } + else { + result = PR_FALSE; // not a link + } + } + else { + result = PR_FALSE; // unknown pseudo class + } + pseudoClass = pseudoClass->mNext; } + + NS_IF_RELEASE(linkHandler); + NS_IF_RELEASE(eventStateManager); } } NS_IF_RELEASE(contentTag); @@ -1115,29 +1185,70 @@ struct ContentEnumData { PRInt32 mCount; }; +static PRBool SelectorMatchesTree(nsIPresContext* aPresContext, + nsIContent* aLastContent, + nsCSSSelector* aSelector) +{ + nsCSSSelector* selector = aSelector; + + if (selector) { + nsIContent* content = nsnull; + nsIContent* lastContent = aLastContent; + NS_ADDREF(lastContent); + while (nsnull != selector) { // check compound selectors + if (PRUnichar('+') == selector->mOperator) { // fetch previous sibling + nsIContent* parent; + PRInt32 index; + lastContent->GetParent(parent); + if (parent) { + parent->IndexOf(lastContent, index); + while (0 <= --index) { // skip text & comment nodes + parent->ChildAt(index, content); + nsIAtom* tag; + content->GetTag(tag); + if ((tag != nsLayoutAtoms::textTagName) && + (tag != nsLayoutAtoms::commentTagName)) { + NS_IF_RELEASE(tag); + break; + } + NS_RELEASE(content); + NS_IF_RELEASE(tag); + } + NS_RELEASE(parent); + } + } + else { // fetch parent + lastContent->GetParent(content); + } + if (! content) { + break; + } + if (SelectorMatches(aPresContext, selector, content)) { + selector = selector->mNext; + } + else { + if (PRUnichar(0) != selector->mOperator) { + NS_RELEASE(content); + break; // parent was required to match + } + } + NS_IF_RELEASE(lastContent); + lastContent = content; // take refcount + content = nsnull; + } + NS_IF_RELEASE(lastContent); + } + return PRBool(nsnull == selector); // matches if ran out of selectors +} + 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; - - nsIContent* content = nsnull; - nsIContent* lastContent = nsnull; - data->mContent->GetParent(content); - while ((nsnull != selector) && (nsnull != content)) { // check compound selectors - if (SelectorMatches(data->mPresContext, selector, content)) { - selector = selector->mNext; - } - lastContent = content; - content->GetParent(content); - NS_IF_RELEASE(lastContent); - } - NS_IF_RELEASE(content); - if (nsnull == selector) { // ran out, it matched + if (SelectorMatchesTree(data->mPresContext, data->mContent, selector)) { nsIStyleRule* iRule; if (NS_OK == aRule->QueryInterface(kIStyleRuleIID, (void**)&iRule)) { data->mResults->AppendElement(iRule); @@ -1270,30 +1381,36 @@ static void PseudoEnumFunc(nsICSSStyleRule* aRule, void* aData) nsCSSSelector* selector = aRule->FirstSelector(); if (selector->mTag == data->mPseudoTag) { selector = selector->mNext; - nsIContent* content = data->mParentContent; - nsIContent* lastContent = nsnull; - NS_IF_ADDREF(content); - while ((nsnull != selector) && (nsnull != content)) { // check compound selectors - if (SelectorMatches(data->mPresContext, selector, content)) { + + if (selector) { // test next selector specially + if (PRUnichar('+') == selector->mOperator) { + return; // not valid here, can't match + } + if (SelectorMatches(data->mPresContext, selector, data->mParentContent)) { selector = selector->mNext; } - lastContent = content; - content->GetParent(content); - NS_IF_RELEASE(lastContent); + else { + if (PRUnichar('>') == selector->mOperator) { + return; // immediate parent didn't match + } + } } - NS_IF_RELEASE(content); - if (nsnull == selector) { // ran out, it matched - nsIStyleRule* iRule; - if (NS_OK == aRule->QueryInterface(kIStyleRuleIID, (void**)&iRule)) { + + if (selector && + (! SelectorMatchesTree(data->mPresContext, data->mParentContent, selector))) { + return; // remaining selectors didn't match + } + + nsIStyleRule* iRule; + if (NS_OK == aRule->QueryInterface(kIStyleRuleIID, (void**)&iRule)) { + data->mResults->AppendElement(iRule); + data->mCount++; + NS_RELEASE(iRule); + iRule = aRule->GetImportantRule(); + if (nsnull != iRule) { data->mResults->AppendElement(iRule); data->mCount++; NS_RELEASE(iRule); - iRule = aRule->GetImportantRule(); - if (nsnull != iRule) { - data->mResults->AppendElement(iRule); - data->mCount++; - NS_RELEASE(iRule); - } } } }