Bug 598832 part 7. Stop caching the content state of elements; just reget it from the DOM as needed. r=dbaron

This commit is contained in:
Boris Zbarsky 2011-03-29 13:29:21 -04:00
parent 06fc5fd9e4
commit c2d78db208
5 changed files with 69 additions and 55 deletions

View File

@ -1121,8 +1121,7 @@ RuleProcessorData::RuleProcessorData(nsPresContext* aPresContext,
mElement(aElement),
mRuleWalker(aRuleWalker),
mPreviousSiblingData(nsnull),
mParentData(nsnull),
mGotContentState(PR_FALSE)
mParentData(nsnull)
{
MOZ_COUNT_CTOR(RuleProcessorData);
@ -1147,9 +1146,6 @@ RuleProcessorData::RuleProcessorData(nsPresContext* aPresContext,
// get the namespace
mNameSpaceID = aElement->GetNameSpaceID();
// No need to initialize mContentState; the ContentState() accessor will handle
// that.
}
RuleProcessorData::~RuleProcessorData()
@ -1205,27 +1201,30 @@ static void GetLang(nsIContent* aContent, nsString& aLang)
}
}
/* static */
nsEventStates
RuleProcessorData::ContentState()
nsCSSRuleProcessor::GetContentState(Element* aElement)
{
if (!mGotContentState) {
mGotContentState = PR_TRUE;
mContentState = mPresContext ?
mPresContext->EventStateManager()->GetContentState(mElement) :
mElement->IntrinsicState();
// If we are not supposed to mark visited links as such, be sure to
// flip the bits appropriately. We want to do this here, rather
// than in GetContentStateForVisitedHandling, so that we don't
// expose that :visited support is disabled to the Web page.
if ((!gSupportVisitedPseudo ||
gPrivateBrowsingObserver->InPrivateBrowsing()) &&
mContentState.HasState(NS_EVENT_STATE_VISITED)) {
mContentState &= ~NS_EVENT_STATE_VISITED;
mContentState |= NS_EVENT_STATE_UNVISITED;
}
nsIPresShell* shell = aElement->GetOwnerDoc()->GetShell();
nsPresContext* presContext;
nsEventStates state;
if (shell && (presContext = shell->GetPresContext())) {
state = presContext->EventStateManager()->GetContentState(aElement);
} else {
state = aElement->IntrinsicState();
}
return mContentState;
// If we are not supposed to mark visited links as such, be sure to
// flip the bits appropriately. We want to do this here, rather
// than in GetContentStateForVisitedHandling, so that we don't
// expose that :visited support is disabled to the Web page.
if ((!gSupportVisitedPseudo ||
gPrivateBrowsingObserver->InPrivateBrowsing()) &&
state.HasState(NS_EVENT_STATE_VISITED)) {
state &= ~NS_EVENT_STATE_VISITED;
state |= NS_EVENT_STATE_UNVISITED;
}
return state;
}
nsEventStates
@ -1234,21 +1233,24 @@ RuleProcessorData::DocumentState()
return mElement->GetOwnerDoc()->GetDocumentState();
}
/* static */
PRBool
RuleProcessorData::IsLink()
nsCSSRuleProcessor::IsLink(Element* aElement)
{
nsEventStates state = ContentState();
nsEventStates state = aElement->IntrinsicState();
return state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
}
/* static */
nsEventStates
RuleProcessorData::GetContentStateForVisitedHandling(
nsCSSRuleProcessor::GetContentStateForVisitedHandling(
Element* aElement,
nsRuleWalker::VisitedHandlingType aVisitedHandling,
PRBool aIsRelevantLink)
{
nsEventStates contentState = ContentState();
nsEventStates contentState = GetContentState(aElement);
if (contentState.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) {
NS_ABORT_IF_FALSE(IsLink(), "IsLink() should match state");
NS_ABORT_IF_FALSE(IsLink(aElement), "IsLink() should match state");
contentState &= ~(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
if (aIsRelevantLink) {
switch (aVisitedHandling) {
@ -1342,7 +1344,7 @@ static PRBool ValueIncludes(const nsSubstring& aValueList,
// Return whether we should apply a "global" (i.e., universal-tag)
// selector for event states in quirks mode. Note that
// |data.IsLink()| is checked separately by the caller, so we return
// |IsLink()| is checked separately by the caller, so we return
// false for |nsGkAtoms::a|, which here means a named anchor.
inline PRBool IsQuirkEventSensitive(nsIAtom *aContentTag)
{
@ -1992,7 +1994,7 @@ static PRBool SelectorMatches(Element* aElement,
// (unnegated)). This at least makes it closer to the spec.
!isNegated &&
// important for |IsQuirkEventSensitive|:
aElement->IsHTML() && !data.IsLink() &&
aElement->IsHTML() && !nsCSSRuleProcessor::IsLink(aElement) &&
!IsQuirkEventSensitive(data.mContentTag)) {
// In quirks mode, only make certain elements sensitive to
// selectors ":hover" and ":active".
@ -2002,7 +2004,9 @@ static PRBool SelectorMatches(Element* aElement,
if (aDependence)
*aDependence = PR_TRUE;
} else {
nsEventStates contentState = data.GetContentStateForVisitedHandling(
nsEventStates contentState =
nsCSSRuleProcessor::GetContentStateForVisitedHandling(
aElement,
aTreeMatchContext.mVisitedHandling,
aNodeMatchContext.mIsRelevantLink);
if (!contentState.HasAtLeastOneOfStates(statesToCheck)) {
@ -2183,8 +2187,9 @@ static PRBool SelectorMatchesTree(Element* aPrevElement,
if (! data) {
return PR_FALSE;
}
NodeMatchContext nodeContext(nsEventStates(), aLookForRelevantLink &&
data->IsLink());
NodeMatchContext nodeContext(nsEventStates(),
aLookForRelevantLink &&
nsCSSRuleProcessor::IsLink(data->mElement));
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
@ -2240,7 +2245,8 @@ static inline
void ContentEnumFunc(css::StyleRule* aRule, nsCSSSelector* aSelector,
RuleProcessorData* data)
{
NodeMatchContext nodeContext(nsEventStates(), data->IsLink());
NodeMatchContext nodeContext(nsEventStates(),
nsCSSRuleProcessor::IsLink(data->mElement));
if (nodeContext.mIsRelevantLink) {
data->mHaveRelevantLink = PR_TRUE;
}

View File

@ -50,6 +50,7 @@
#include "nsTArray.h"
#include "nsAutoPtr.h"
#include "nsCSSRules.h"
#include "nsRuleWalker.h"
struct RuleCascadeData;
struct nsCSSSelectorList;
@ -94,6 +95,25 @@ public:
RuleProcessorData& aData,
nsCSSSelectorList* aSelectorList);
/*
* Helper to get the content state for a content node. This may be
* slightly adjusted from IntrinsicState().
*/
static nsEventStates GetContentState(mozilla::dom::Element* aElement);
/*
* Helper to get the content state for :visited handling for an element
*/
static nsEventStates GetContentStateForVisitedHandling(
mozilla::dom::Element* aElement,
nsRuleWalker::VisitedHandlingType aVisitedHandling,
PRBool aIsRelevantLink);
/*
* Helper to test whether a node is a link
*/
static PRBool IsLink(mozilla::dom::Element* aElement);
// nsIStyleRuleProcessor
virtual void RulesMatching(ElementRuleProcessorData* aData);

View File

@ -70,6 +70,7 @@
#include "nsRuleData.h"
#include "nsContentErrors.h"
#include "nsRuleProcessorData.h"
#include "nsCSSRuleProcessor.h"
#include "mozilla/dom/Element.h"
#include "nsCSSFrameConstructor.h"
@ -221,11 +222,12 @@ nsHTMLStyleSheet::RulesMatching(ElementRuleProcessorData* aData)
// if we have anchor colors, check if this is an anchor with an href
if (tag == nsGkAtoms::a) {
if (mLinkRule || mVisitedRule || mActiveRule) {
nsEventStates state = aData->GetContentStateForVisitedHandling(
nsEventStates state = nsCSSRuleProcessor::GetContentStateForVisitedHandling(
aData->mElement,
ruleWalker->VisitedHandling(),
// If the node being matched is a link,
// it's the relevant link.
aData->IsLink());
nsCSSRuleProcessor::IsLink(aData->mElement));
if (mLinkRule && state.HasState(NS_EVENT_STATE_UNVISITED)) {
ruleWalker->Forward(mLinkRule);
ruleWalker->SetHaveRelevantLink();
@ -236,7 +238,7 @@ nsHTMLStyleSheet::RulesMatching(ElementRuleProcessorData* aData)
}
// No need to add to the active rule if it's not a link
if (mActiveRule && aData->IsLink() &&
if (mActiveRule && nsCSSRuleProcessor::IsLink(aData->mElement) &&
state.HasState(NS_EVENT_STATE_ACTIVE)) {
ruleWalker->Forward(mActiveRule);
}
@ -275,7 +277,7 @@ nsHTMLStyleSheet::HasStateDependentStyle(StateRuleProcessorData* aData)
{
if (aData->mElement->IsHTML() &&
aData->mContentTag == nsGkAtoms::a &&
aData->IsLink() &&
nsCSSRuleProcessor::IsLink(aData->mElement) &&
((mActiveRule && aData->mStateMask.HasState(NS_EVENT_STATE_ACTIVE)) ||
(mLinkRule && aData->mStateMask.HasState(NS_EVENT_STATE_VISITED)) ||
(mVisitedRule && aData->mStateMask.HasState(NS_EVENT_STATE_VISITED)))) {

View File

@ -172,13 +172,7 @@ public:
mVisitedHandling = mRuleWalker->VisitedHandling();
}
nsEventStates ContentState();
nsEventStates DocumentState();
PRBool IsLink();
nsEventStates GetContentStateForVisitedHandling(
nsRuleWalker::VisitedHandlingType aVisitedHandling,
PRBool aIsRelevantLink);
nsPresContext* mPresContext;
mozilla::dom::Element* mElement; // weak ref, must not be null
@ -195,15 +189,6 @@ public:
// selectors require matching of prior siblings or ancestors.
RuleProcessorData* mPreviousSiblingData;
RuleProcessorData* mParentData;
private:
// mContentState is initialized lazily.
nsEventStates mContentState; // eventStateMgr->GetContentState() or
// mElement->IntrinsicState() if we have no ESM
// adjusted for not supporting :visited (but
// with visitedness information when we support
// it).
PRPackedBool mGotContentState;
};
struct ElementRuleProcessorData : public RuleProcessorData {

View File

@ -781,8 +781,9 @@ nsStyleSet::ResolveStyleFor(Element* aElement,
}
return GetContext(aParentContext, ruleNode, visitedRuleNode,
data.IsLink(),
data.ContentState().HasState(NS_EVENT_STATE_VISITED),
nsCSSRuleProcessor::IsLink(aElement),
nsCSSRuleProcessor::GetContentState(aElement).
HasState(NS_EVENT_STATE_VISITED),
nsnull, nsCSSPseudoElements::ePseudo_NotPseudoElement);
}