Pass through desired link-visitedness on the rule walker and construct if-visited style contexts in the style set when visited styles are present. (Bug 147777) r=bzbarsky

This commit is contained in:
L. David Baron 2010-04-02 18:58:27 -07:00
parent 3dbb11b9a9
commit 124bb33c2e
3 changed files with 157 additions and 15 deletions

View File

@ -41,7 +41,11 @@
* rules are matched
*/
#ifndef nsRuleWalker_h_
#define nsRuleWalker_h_
#include "nsRuleNode.h"
#include "nsIStyleRule.h"
class nsRuleWalker {
public:
@ -75,6 +79,28 @@ public:
PRBool GetImportance() const { return mImportance; }
PRBool GetCheckForImportantRules() const { return mCheckForImportantRules; }
// We define the visited-relevant link to be the link that is the
// nearest self-or-ancestor to the node being matched.
enum VisitedHandlingType {
// Do rule matching as though all links are unvisited.
eRelevantLinkUnvisited,
// Do rule matching as though the relevant link is visited and all
// other links are unvisited.
eRelevantLinkVisited,
// Do rule matching as though a rule should match if it would match
// given any set of visitedness states. (used by users other than
// nsRuleWalker)
eLinksVisitedOrUnvisited
};
void ResetForVisitedMatching() {
Reset();
mVisitedHandling = eRelevantLinkVisited;
}
VisitedHandlingType VisitedHandling() const { return mVisitedHandling; }
void SetHaveRelevantLink() { mHaveRelevantLink = PR_TRUE; }
PRBool HaveRelevantLink() const { return mHaveRelevantLink; }
private:
nsRuleNode* mCurrent; // Our current position. Never null.
nsRuleNode* mRoot; // The root of the tree we're walking.
@ -84,10 +110,28 @@ private:
// we walk and set to false if we find
// one.
// When mVisitedHandling is eRelevantLinkUnvisited, this is set to
// true on the RuleProcessorData *for the node being matched* if a
// relevant link (see explanation in definition of VisitedHandling
// enum) was encountered during the matching process, which means that
// matching needs to be rerun with eRelevantLinkVisited. Otherwise,
// its behavior is undefined (it might get set appropriately, or might
// not).
PRBool mHaveRelevantLink;
VisitedHandlingType mVisitedHandling;
public:
nsRuleWalker(nsRuleNode* aRoot) :mCurrent(aRoot), mRoot(aRoot) {
nsRuleWalker(nsRuleNode* aRoot)
: mCurrent(aRoot)
, mRoot(aRoot)
, mHaveRelevantLink(PR_FALSE)
, mVisitedHandling(eRelevantLinkUnvisited)
{
NS_ASSERTION(mCurrent, "Caller screwed up and gave us null node");
MOZ_COUNT_CTOR(nsRuleWalker);
}
~nsRuleWalker() { MOZ_COUNT_DTOR(nsRuleWalker); }
};
#endif /* !defined(nsRuleWalker_h_) */

View File

@ -429,6 +429,12 @@ EnumRulesMatching(nsIStyleRuleProcessor* aProcessor, void* aData)
already_AddRefed<nsStyleContext>
nsStyleSet::GetContext(nsStyleContext* aParentContext,
nsRuleNode* aRuleNode,
// aVisitedRuleNode may be null; if it is null
// it means that we don't need to force creation
// of a StyleIfVisited. (But if we make one
// because aParentContext has one, then aRuleNode
// should be used.)
nsRuleNode* aVisitedRuleNode,
nsIAtom* aPseudoTag,
nsCSSPseudoElements::Type aPseudoType)
{
@ -440,11 +446,33 @@ nsStyleSet::GetContext(nsStyleContext* aParentContext,
aPseudoType),
"Pseudo mismatch");
nsStyleContext* result = nsnull;
// Ensure |aVisitedRuleNode != nsnull| corresponds to the need to
// create an if-visited style context, and that in that case, we have
// parentIfVisited set correctly.
nsStyleContext *parentIfVisited =
aParentContext ? aParentContext->GetStyleIfVisited() : nsnull;
if (parentIfVisited) {
if (!aVisitedRuleNode) {
aVisitedRuleNode = aRuleNode;
}
} else {
if (aVisitedRuleNode) {
parentIfVisited = aParentContext;
}
}
if (aIsLink) {
// If this node is a link, we want its visited's style context's
// parent to be the regular style context of its parent, because
// only the visitedness of the relevant link should influence style.
parentIfVisited = aParentContext;
}
nsRefPtr<nsStyleContext> result;
if (aParentContext)
result = aParentContext->FindChildWithRules(aPseudoTag, aRuleNode,
nsnull, PR_FALSE).get();
aVisitedRuleNode,
PR_FALSE);
#ifdef NOISY_DEBUG
if (result)
@ -455,8 +483,19 @@ nsStyleSet::GetContext(nsStyleContext* aParentContext,
if (!result) {
result = NS_NewStyleContext(aParentContext, aPseudoTag, aPseudoType,
aRuleNode, PresContext()).get();
if (!aParentContext && result)
aRuleNode, PresContext());
if (!result)
return nsnull;
if (aVisitedRuleNode) {
nsRefPtr<nsStyleContext> resultIfVisited =
NS_NewStyleContext(parentIfVisited, aPseudoTag, aPseudoType,
aVisitedRuleNode, PresContext());
if (!resultIfVisited) {
return nsnull;
}
result->SetStyleIfVisited(resultIfVisited.forget());
}
if (!aParentContext)
mRoots.AppendElement(result);
}
else {
@ -464,7 +503,7 @@ nsStyleSet::GetContext(nsStyleContext* aParentContext,
NS_ASSERTION(result->GetPseudo() == aPseudoTag, "Unexpected pseudo");
}
return result;
return result.forget();
}
void
@ -748,7 +787,17 @@ nsStyleSet::ResolveStyleFor(nsIContent* aContent,
FileRules(EnumRulesMatching<ElementRuleProcessorData>, &data, aContent,
&ruleWalker);
return GetContext(aParentContext, ruleWalker.CurrentNode(),
nsRuleNode *ruleNode = ruleWalker.CurrentNode();
nsRuleNode *visitedRuleNode = nsnull;
if (ruleWalker.HaveRelevantLink()) {
ruleWalker.ResetForVisitedMatching();
FileRules(EnumRulesMatching<ElementRuleProcessorData>, &data, aContent,
&ruleWalker);
visitedRuleNode = ruleWalker.CurrentNode();
}
return GetContext(aParentContext, ruleNode, visitedRuleNode,
nsnull, nsCSSPseudoElements::ePseudo_NotPseudoElement);
}
@ -766,7 +815,7 @@ nsStyleSet::ResolveStyleForRules(nsStyleContext* aParentContext,
ruleWalker.Forward(aRules.ObjectAt(i));
}
return GetContext(aParentContext, ruleWalker.CurrentNode(),
return GetContext(aParentContext, ruleWalker.CurrentNode(), nsnull,
nsnull, nsCSSPseudoElements::ePseudo_NotPseudoElement);
}
@ -784,7 +833,19 @@ nsStyleSet::ResolveStyleByAddingRules(nsStyleContext* aBaseContext,
for (PRInt32 i = 0; i < aRules.Count(); i++) {
ruleWalker.Forward(aRules.ObjectAt(i));
}
return GetContext(aBaseContext->GetParent(), ruleWalker.CurrentNode(),
nsRuleNode *ruleNode = ruleWalker.CurrentNode();
nsRuleNode *visitedRuleNode = nsnull;
if (aBaseContext->GetStyleIfVisited()) {
ruleWalker.SetCurrentNode(aBaseContext->GetStyleIfVisited()->GetRuleNode());
for (PRInt32 i = 0; i < aRules.Count(); i++) {
ruleWalker.Forward(aRules.ObjectAt(i));
}
visitedRuleNode = ruleWalker.CurrentNode();
}
return GetContext(aBaseContext->GetParent(), ruleNode, visitedRuleNode,
aBaseContext->GetPseudo(),
aBaseContext->GetPseudoType());
}
@ -792,7 +853,7 @@ nsStyleSet::ResolveStyleByAddingRules(nsStyleContext* aBaseContext,
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveStyleForNonElement(nsStyleContext* aParentContext)
{
return GetContext(aParentContext, mRuleTree,
return GetContext(aParentContext, mRuleTree, nsnull,
nsCSSAnonBoxes::mozNonElement,
nsCSSPseudoElements::ePseudo_AnonBox);
}
@ -829,7 +890,17 @@ nsStyleSet::ResolvePseudoElementStyle(nsIContent* aParentContent,
FileRules(EnumRulesMatching<PseudoElementRuleProcessorData>, &data,
aParentContent, &ruleWalker);
return GetContext(aParentContext, ruleWalker.CurrentNode(),
nsRuleNode *ruleNode = ruleWalker.CurrentNode();
nsRuleNode *visitedRuleNode = nsnull;
if (ruleWalker.HaveRelevantLink()) {
ruleWalker.ResetForVisitedMatching();
FileRules(EnumRulesMatching<PseudoElementRuleProcessorData>, &data,
aParentContent, &ruleWalker);
visitedRuleNode = ruleWalker.CurrentNode();
}
return GetContext(aParentContext, ruleNode, visitedRuleNode,
nsCSSPseudoElements::GetPseudoAtom(aType), aType);
}
@ -861,8 +932,18 @@ nsStyleSet::ProbePseudoElementStyle(nsIContent* aParentContent,
return nsnull;
}
nsRuleNode *visitedRuleNode = nsnull;
if (ruleWalker.HaveRelevantLink()) {
ruleWalker.ResetForVisitedMatching();
FileRules(EnumRulesMatching<PseudoElementRuleProcessorData>, &data,
aParentContent, &ruleWalker);
visitedRuleNode = ruleWalker.CurrentNode();
}
nsRefPtr<nsStyleContext> result =
GetContext(aParentContext, ruleNode, pseudoTag, aType);
GetContext(aParentContext, ruleNode, visitedRuleNode,
pseudoTag, aType);
// For :before and :after pseudo-elements, having display: none or no
// 'content' property is equivalent to not having the pseudo-element
@ -902,7 +983,7 @@ nsStyleSet::ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag,
FileRules(EnumRulesMatching<AnonBoxRuleProcessorData>, &data, nsnull,
&ruleWalker);
return GetContext(aParentContext, ruleWalker.CurrentNode(),
return GetContext(aParentContext, ruleWalker.CurrentNode(), nsnull,
aPseudoTag, nsCSSPseudoElements::ePseudo_AnonBox);
}
@ -927,7 +1008,17 @@ nsStyleSet::ResolveXULTreePseudoStyle(nsIContent* aParentContent,
FileRules(EnumRulesMatching<XULTreeRuleProcessorData>, &data, aParentContent,
&ruleWalker);
return GetContext(aParentContext, ruleWalker.CurrentNode(),
nsRuleNode *ruleNode = ruleWalker.CurrentNode();
nsRuleNode *visitedRuleNode = nsnull;
if (ruleWalker.HaveRelevantLink()) {
ruleWalker.ResetForVisitedMatching();
FileRules(EnumRulesMatching<XULTreeRuleProcessorData>, &data,
aParentContent, &ruleWalker);
visitedRuleNode = ruleWalker.CurrentNode();
}
return GetContext(aParentContext, ruleNode, visitedRuleNode,
aPseudoTag, nsCSSPseudoElements::ePseudo_XULTree);
}
#endif
@ -1044,8 +1135,14 @@ nsStyleSet::ReparentStyleContext(nsStyleContext* aStyleContext,
nsIAtom* pseudoTag = aStyleContext->GetPseudo();
nsCSSPseudoElements::Type pseudoType = aStyleContext->GetPseudoType();
nsRuleNode* ruleNode = aStyleContext->GetRuleNode();
nsRuleNode* visitedRuleNode = nsnull;
nsStyleContext* visitedContext = aStyleContext->GetStyleIfVisited();
if (visitedContext) {
visitedRuleNode = visitedContext->GetRuleNode();
}
return GetContext(aNewParentContext, ruleNode, pseudoTag, pseudoType);
return GetContext(aNewParentContext, ruleNode, visitedRuleNode,
pseudoTag, pseudoType);
}
struct StatefulData : public StateRuleProcessorData {

View File

@ -339,6 +339,7 @@ class nsStyleSet
already_AddRefed<nsStyleContext>
GetContext(nsStyleContext* aParentContext,
nsRuleNode* aRuleNode,
nsRuleNode* aVisitedRuleNode,
nsIAtom* aPseudoTag,
nsCSSPseudoElements::Type aPseudoType);