Add mechanism for separate style data for visited style. (Bug 147777) r=bzbarsky

This commit is contained in:
L. David Baron
2010-04-02 18:58:25 -07:00
parent 814fd53014
commit afab956b9a
4 changed files with 163 additions and 1 deletions

View File

@@ -7652,6 +7652,13 @@ nsCSSFrameConstructor::DoContentStateChanged(nsIContent* aContent,
++mHoverGeneration;
}
if (aStateMask & NS_EVENT_STATE_VISITED) {
// Exposing information to the page about whether the link is
// visited or not isn't really something we can worry about here.
// FIXME: We could probably do this a bit better.
NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
}
PostRestyleEvent(aContent, rshint, hint);
}
}

View File

@@ -463,12 +463,118 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther)
#undef DO_STRUCT_DIFFERENCE
// Note that we do not check whether this->RelevantLinkVisited() !=
// aOther->RelevantLinkVisited(); we don't need to since
// nsCSSFrameConstructor::DoContentStateChanged always adds
// nsChangeHint_RepaintFrame for NS_EVENT_STATE_VISITED changes (and
// needs to, since HasStateDependentStyle probably doesn't work right
// for NS_EVENT_STATE_VISITED). Hopefully this doesn't actually
// expose whether links are visited to performance tests since all
// link coloring happens asynchronously at a time when it's hard for
// the page to measure.
// However, we do need to compute the larger of the changes that can
// happen depending on whether the link is visited or unvisited, since
// doing only the one that's currently appropriate would expose which
// links are in history to easy performance measurement. Therefore,
// here, we add nsChangeHint_RepaintFrame hints (the maximum for
// things that can depend on :visited) for the properties on which we
// call GetVisitedDependentColor.
nsStyleContext *thisVis = GetStyleIfVisited(),
*otherVis = aOther->GetStyleIfVisited();
if (!thisVis != !otherVis) {
// One style context has a style-if-visited and the other doesn't.
// Presume a difference.
NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
} else if (thisVis && !NS_IsHintSubset(nsChangeHint_RepaintFrame, hint)) {
// Both style contexts have a style-if-visited.
PRBool change = PR_FALSE;
// NB: Calling Peek on |this|, not |thisVis|, since callers may look
// at a struct on |this| without looking at the same struct on
// |thisVis| (including this function if we skip one of these checks
// due to change being true already or due to the old style context
// not having a style-if-visited), but not the other way around.
if (PeekStyleColor()) {
if (thisVis->GetStyleColor()->mColor !=
otherVis->GetStyleColor()->mColor) {
change = PR_TRUE;
}
}
// NB: Calling Peek on |this|, not |thisVis| (see above).
if (!change && PeekStyleBackground()) {
if (thisVis->GetStyleBackground()->mBackgroundColor !=
otherVis->GetStyleBackground()->mBackgroundColor) {
change = PR_TRUE;
}
}
// NB: Calling Peek on |this|, not |thisVis| (see above).
if (!change && PeekStyleBorder()) {
const nsStyleBorder *thisVisBorder = thisVis->GetStyleBorder();
const nsStyleBorder *otherVisBorder = otherVis->GetStyleBorder();
NS_FOR_CSS_SIDES(side) {
PRBool thisFG, otherFG;
nscolor thisColor, otherColor;
thisVisBorder->GetBorderColor(side, thisColor, thisFG);
otherVisBorder->GetBorderColor(side, otherColor, otherFG);
if (thisFG != otherFG || (!thisFG && thisColor != otherColor)) {
change = PR_TRUE;
break;
}
}
}
// NB: Calling Peek on |this|, not |thisVis| (see above).
if (!change && PeekStyleOutline()) {
const nsStyleOutline *thisVisOutline = thisVis->GetStyleOutline();
const nsStyleOutline *otherVisOutline = otherVis->GetStyleOutline();
PRBool haveColor;
nscolor thisColor, otherColor;
if (thisVisOutline->GetOutlineInitialColor() !=
otherVisOutline->GetOutlineInitialColor() ||
(haveColor = thisVisOutline->GetOutlineColor(thisColor)) !=
otherVisOutline->GetOutlineColor(otherColor) ||
(haveColor && thisColor != otherColor)) {
change = PR_TRUE;
}
}
// NB: Calling Peek on |this|, not |thisVis| (see above).
if (!change && PeekStyleColumn()) {
const nsStyleColumn *thisVisColumn = thisVis->GetStyleColumn();
const nsStyleColumn *otherVisColumn = otherVis->GetStyleColumn();
if (thisVisColumn->mColumnRuleColor != otherVisColumn->mColumnRuleColor ||
thisVisColumn->mColumnRuleColorIsForeground !=
otherVisColumn->mColumnRuleColorIsForeground) {
change = PR_TRUE;
}
}
// NB: Calling Peek on |this|, not |thisVis| (see above).
if (!change && PeekStyleSVG()) {
const nsStyleSVG *thisVisSVG = thisVis->GetStyleSVG();
const nsStyleSVG *otherVisSVG = otherVis->GetStyleSVG();
if (thisVisSVG->mFill != otherVisSVG->mFill ||
thisVisSVG->mStroke != otherVisSVG->mStroke) {
change = PR_TRUE;
}
}
if (change) {
NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
}
}
return hint;
}
void
nsStyleContext::Mark()
{
if (mStyleIfVisited)
mStyleIfVisited->Mark();
// Mark our rule node.
mRuleNode->Mark();

View File

@@ -130,6 +130,48 @@ public:
PRBool HasPseudoElementData() const
{ return !!(mBits & NS_STYLE_HAS_PSEUDO_ELEMENT_DATA); }
// Is the only link whose visitedness is allowed to influence the
// style of the node this style context is for (which is that element
// or its nearest ancestor that is a link) visited?
PRBool RelevantLinkVisited() const
{ return !!(mBits & NS_STYLE_RELEVANT_LINK_VISITED); }
// Return the style context whose style data should be used for the R,
// G, and B components of color, background-color, and border-*-color
// if RelevantLinkIsVisited().
//
// GetPseudo() and GetPseudoType() on this style context return the
// same as on |this|, and its depth in the tree (number of GetParent()
// calls until null is returned) is the same as |this|, since its
// parent is either |this|'s parent or |this|'s parent's
// style-if-visited.
//
// Structs on this context should never be examined without also
// examining the corresponding struct on |this|. Doing so will likely
// both (1) lead to a privacy leak and (2) lead to dynamic change bugs
// related to the Peek code in nsStyleContext::CalcStyleDifference.
nsStyleContext* GetStyleIfVisited()
{ return mStyleIfVisited; }
// To be called only from nsStyleSet.
void SetStyleIfVisited(already_AddRefed<nsStyleContext> aStyleIfVisited)
{
NS_ASSERTION(!mStyleIfVisited, "should only be set once");
mStyleIfVisited = aStyleIfVisited;
NS_ASSERTION(GetStyleIfVisited()->GetPseudo() == GetPseudo(),
"pseudo tag mismatch");
if (GetParent() && GetParent()->GetStyleIfVisited()) {
NS_ASSERTION(GetStyleIfVisited()->GetParent() ==
GetParent()->GetStyleIfVisited() ||
GetStyleIfVisited()->GetParent() == GetParent(),
"parent mismatch");
} else {
NS_ASSERTION(GetStyleIfVisited()->GetParent() == GetParent(),
"parent mismatch");
}
}
// Tell this style context to cache aStruct as the struct for aSID
NS_HIDDEN_(void) SetStyle(nsStyleStructID aSID, void* aStruct);
@@ -243,7 +285,7 @@ protected:
#undef STYLE_STRUCT_RESET
#undef STYLE_STRUCT_INHERITED
nsStyleContext* const mParent;
nsStyleContext* const mParent; // STRONG
// Children are kept in two circularly-linked lists. The list anchor
// is not part of the list (null for empty), and we point to the first
@@ -256,6 +298,11 @@ protected:
nsStyleContext* mPrevSibling;
nsStyleContext* mNextSibling;
// Style to be used instead for the R, G, and B components of color,
// background-color, and border-*-color if the nearest ancestor link
// element is visited (see RelevantLinkVisited()).
nsRefPtr<nsStyleContext> mStyleIfVisited;
// If this style context is for a pseudo-element or anonymous box,
// the relevant atom.
nsCOMPtr<nsIAtom> mPseudoTag;

View File

@@ -84,6 +84,8 @@ class imgIContainer;
#define NS_STYLE_HAS_TEXT_DECORATIONS 0x01000000
// See nsStyleContext::HasPseudoElementData.
#define NS_STYLE_HAS_PSEUDO_ELEMENT_DATA 0x02000000
// See nsStyleContext::RelevantLinkIsVisited
#define NS_STYLE_RELEVANT_LINK_VISITED 0x04000000
// See nsStyleContext::GetPseudoEnum
#define NS_STYLE_CONTEXT_TYPE_MASK 0xf0000000
#define NS_STYLE_CONTEXT_TYPE_SHIFT 28