Propagate whether we have a relevant link out of selector matching. (Bug 147777) r=bzbarsky

This commit is contained in:
L. David Baron 2010-04-02 18:58:26 -07:00
parent 179665d6ce
commit 12aeda8a30

View file

@ -1226,8 +1226,13 @@ struct TreeMatchContext {
// that certain restyling needs to happen.) // that certain restyling needs to happen.)
const PRBool mForStyling; const PRBool mForStyling;
// Did this matching operation find a relevant link? (If so, we'll
// need to construct a StyleIfVisited.)
PRBool mHaveRelevantLink;
TreeMatchContext(PRBool aForStyling) TreeMatchContext(PRBool aForStyling)
: mForStyling(aForStyling) : mForStyling(aForStyling)
, mHaveRelevantLink(PR_FALSE)
{ {
} }
}; };
@ -1256,8 +1261,20 @@ struct NodeMatchContext {
// :hover:active rule no longer matches when both states are unset). // :hover:active rule no longer matches when both states are unset).
const PRInt32 mStateMask; const PRInt32 mStateMask;
NodeMatchContext(PRInt32 aStateMask) // Is this link the unique link whose visitedness can affect the style
// of the node being matched? (That link is the nearest link to the
// node being matched that is itself or an ancestor.)
//
// Always false when TreeMatchContext::mForStyling is false. (We
// could figure it out for SelectorListMatches, but we're starting
// from the middle of the selector list when doing
// Has{Attribute,State}DependentStyle, so we can't tell. So when
// mForStyling is false, we have to assume we don't know.)
const PRBool mIsRelevantLink;
NodeMatchContext(PRInt32 aStateMask, PRBool aIsRelevantLink)
: mStateMask(aStateMask) : mStateMask(aStateMask)
, mIsRelevantLink(aIsRelevantLink)
{ {
} }
}; };
@ -1874,6 +1891,12 @@ static PRBool SelectorMatches(RuleProcessorData &data,
{ {
NS_PRECONDITION(!aSelector->IsPseudoElement(), NS_PRECONDITION(!aSelector->IsPseudoElement(),
"Pseudo-element snuck into SelectorMatches?"); "Pseudo-element snuck into SelectorMatches?");
NS_ABORT_IF_FALSE(aTreeMatchContext.mForStyling ||
!aNodeMatchContext.mIsRelevantLink,
"mIsRelevantLink should be set to false when mForStyling "
"is false since we don't know how to set it correctly in "
"Has(Attribute|State)DependentStyle");
// namespace/tag match // namespace/tag match
// optimization : bail out early if we can // optimization : bail out early if we can
if ((kNameSpaceID_Unknown != aSelector->mNameSpace && if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
@ -2099,7 +2122,8 @@ static PRBool SelectorMatches(RuleProcessorData &data,
static PRBool SelectorMatchesTree(RuleProcessorData& aPrevData, static PRBool SelectorMatchesTree(RuleProcessorData& aPrevData,
nsCSSSelector* aSelector, nsCSSSelector* aSelector,
TreeMatchContext& aTreeMatchContext) TreeMatchContext& aTreeMatchContext,
PRBool aLookForRelevantLink)
{ {
nsCSSSelector* selector = aSelector; nsCSSSelector* selector = aSelector;
RuleProcessorData* prevdata = &aPrevData; RuleProcessorData* prevdata = &aPrevData;
@ -2117,6 +2141,8 @@ static PRBool SelectorMatchesTree(RuleProcessorData& aPrevData,
RuleProcessorData* data; RuleProcessorData* data;
if (PRUnichar('+') == selector->mOperator || if (PRUnichar('+') == selector->mOperator ||
PRUnichar('~') == selector->mOperator) { PRUnichar('~') == selector->mOperator) {
// The relevant link must be an ancestor of the node being matched.
aLookForRelevantLink = PR_FALSE;
data = prevdata->mPreviousSiblingData; data = prevdata->mPreviousSiblingData;
if (!data) { if (!data) {
nsIContent* content = prevdata->mContent; nsIContent* content = prevdata->mContent;
@ -2158,7 +2184,18 @@ static PRBool SelectorMatchesTree(RuleProcessorData& aPrevData,
if (! data) { if (! data) {
return PR_FALSE; return PR_FALSE;
} }
NodeMatchContext nodeContext(0); NodeMatchContext nodeContext(0, aLookForRelevantLink && data->IsLink());
if (nodeContext.mIsRelevantLink) {
// If we find an ancestor of the matched node that is a link
// during the matching process, then it's the relevant link (see
// constructor call above).
// Since we are still matching against selectors that contain
// :visited (they'll just fail), we will always find such a node
// during the selector matching process if there is a relevant
// link that can influence selector matching.
aLookForRelevantLink = PR_FALSE;
aTreeMatchContext.mHaveRelevantLink = PR_TRUE;
}
if (SelectorMatches(*data, selector, nodeContext, aTreeMatchContext)) { if (SelectorMatches(*data, selector, nodeContext, aTreeMatchContext)) {
// to avoid greedy matching, we need to recur if this is a // to avoid greedy matching, we need to recur if this is a
// descendant or general sibling combinator and the next // descendant or general sibling combinator and the next
@ -2179,7 +2216,8 @@ static PRBool SelectorMatchesTree(RuleProcessorData& aPrevData,
// it tests from the top of the content tree, down. This // it tests from the top of the content tree, down. This
// doesn't matter much for performance since most selectors // doesn't matter much for performance since most selectors
// don't match. (If most did, it might be faster...) // don't match. (If most did, it might be faster...)
if (SelectorMatchesTree(*data, selector, aTreeMatchContext)) { if (SelectorMatchesTree(*data, selector, aTreeMatchContext,
aLookForRelevantLink)) {
return PR_TRUE; return PR_TRUE;
} }
} }
@ -2203,10 +2241,14 @@ static void ContentEnumFunc(nsICSSStyleRule* aRule, nsCSSSelector* aSelector,
RuleProcessorData* data = (RuleProcessorData*)aData; RuleProcessorData* data = (RuleProcessorData*)aData;
TreeMatchContext treeContext(PR_TRUE); TreeMatchContext treeContext(PR_TRUE);
NodeMatchContext nodeContext(0); NodeMatchContext nodeContext(0, data->IsLink());
if (nodeContext.mIsRelevantLink) {
treeContext.mHaveRelevantLink = PR_TRUE;
}
if (SelectorMatches(*data, aSelector, nodeContext, treeContext)) { if (SelectorMatches(*data, aSelector, nodeContext, treeContext)) {
nsCSSSelector *next = aSelector->mNext; nsCSSSelector *next = aSelector->mNext;
if (!next || SelectorMatchesTree(*data, next, treeContext)) { if (!next || SelectorMatchesTree(*data, next, treeContext,
!nodeContext.mIsRelevantLink)) {
// for performance, require that every implementation of // for performance, require that every implementation of
// nsICSSStyleRule return the same pointer for nsIStyleRule (why // nsICSSStyleRule return the same pointer for nsIStyleRule (why
// would anything multiply inherit nsIStyleRule anyway?) // would anything multiply inherit nsIStyleRule anyway?)
@ -2355,10 +2397,11 @@ nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData)
// don't bother calling SelectorMatches, since even if it returns false // don't bother calling SelectorMatches, since even if it returns false
// hint won't change. // hint won't change.
TreeMatchContext treeContext(PR_FALSE); TreeMatchContext treeContext(PR_FALSE);
NodeMatchContext nodeContext(aData->mStateMask); NodeMatchContext nodeContext(aData->mStateMask, PR_FALSE);
if ((possibleChange & ~hint) && if ((possibleChange & ~hint) &&
SelectorMatches(*aData, selector, nodeContext, treeContext) && SelectorMatches(*aData, selector, nodeContext, treeContext) &&
SelectorMatchesTree(*aData, selector->mNext, treeContext)) { SelectorMatchesTree(*aData, selector->mNext, treeContext, PR_FALSE))
{
hint = nsRestyleHint(hint | possibleChange); hint = nsRestyleHint(hint | possibleChange);
} }
} }
@ -2395,10 +2438,10 @@ AttributeEnumFunc(nsCSSSelector* aSelector, AttributeEnumData* aData)
// bother calling SelectorMatches, since even if it returns false // bother calling SelectorMatches, since even if it returns false
// enumData->change won't change. // enumData->change won't change.
TreeMatchContext treeContext(PR_FALSE); TreeMatchContext treeContext(PR_FALSE);
NodeMatchContext nodeContext(0); NodeMatchContext nodeContext(0, PR_FALSE);
if ((possibleChange & ~(aData->change)) && if ((possibleChange & ~(aData->change)) &&
SelectorMatches(*data, aSelector, nodeContext, treeContext) && SelectorMatches(*data, aSelector, nodeContext, treeContext) &&
SelectorMatchesTree(*data, aSelector->mNext, treeContext)) { SelectorMatchesTree(*data, aSelector->mNext, treeContext, PR_FALSE)) {
aData->change = nsRestyleHint(aData->change | possibleChange); aData->change = nsRestyleHint(aData->change | possibleChange);
} }
} }
@ -2940,10 +2983,10 @@ nsCSSRuleProcessor::SelectorListMatches(RuleProcessorData& aData,
NS_ASSERTION(sel, "Should have *some* selectors"); NS_ASSERTION(sel, "Should have *some* selectors");
NS_ASSERTION(!sel->IsPseudoElement(), "Shouldn't have been called"); NS_ASSERTION(!sel->IsPseudoElement(), "Shouldn't have been called");
TreeMatchContext treeContext(PR_FALSE); TreeMatchContext treeContext(PR_FALSE);
NodeMatchContext nodeContext(0); NodeMatchContext nodeContext(0, PR_FALSE);
if (SelectorMatches(aData, sel, nodeContext, treeContext)) { if (SelectorMatches(aData, sel, nodeContext, treeContext)) {
nsCSSSelector* next = sel->mNext; nsCSSSelector* next = sel->mNext;
if (!next || SelectorMatchesTree(aData, next, treeContext)) { if (!next || SelectorMatchesTree(aData, next, treeContext, PR_FALSE)) {
return PR_TRUE; return PR_TRUE;
} }
} }