Bug 523288 part 2. Get link state and content state lazily. r=dbaron
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 10 Dec 2009 14:36:03 -0800
changeset 35531 b3636bfdc6d938963b31a682aae2608011bfd137
parent 35530 1e9e3cd957f2160f66ae9df56ecaf37ea972c47d
child 35532 a6508c37eb412b22e4e3b53a376788f54c423b1f
push id10631
push userbzbarsky@mozilla.com
push dateThu, 10 Dec 2009 22:48:24 +0000
treeherdermozilla-central@08b48be6951b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs523288
milestone1.9.3a1pre
Bug 523288 part 2. Get link state and content state lazily. r=dbaron
layout/style/nsCSSRuleProcessor.cpp
layout/style/nsHTMLStyleSheet.cpp
layout/style/nsRuleProcessorData.h
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -909,18 +909,20 @@ RuleProcessorData::RuleProcessorData(nsP
   mRuleWalker = aRuleWalker;
   mScopedRoot = nsnull;
 
   mContentTag = nsnull;
   mContentID = nsnull;
   mHasAttributes = PR_FALSE;
   mIsHTMLContent = PR_FALSE;
   mIsLink = PR_FALSE;
+  mGotLinkInfo = PR_FALSE;
   mLinkState = eLinkState_Unknown;
-  mEventState = 0;
+  mContentState = 0;
+  mGotContentState = PR_FALSE;
   mNameSpaceID = kNameSpaceID_Unknown;
   mPreviousSiblingData = nsnull;
   mParentData = nsnull;
   mLanguage = nsnull;
   mClasses = nsnull;
   mNthIndices[0][0] = -2;
   mNthIndices[0][1] = -2;
   mNthIndices[1][0] = -2;
@@ -940,59 +942,28 @@ RuleProcessorData::RuleProcessorData(nsP
 
   if (aContent) {
     NS_ASSERTION(aContent->GetOwnerDoc(), "Document-less node here?");
     
     // get the tag and parent
     mContentTag = aContent->Tag();
     mParentContent = aContent->GetParent();
 
-    // get the event state
-    if (mPresContext) {
-      mPresContext->EventStateManager()->GetContentState(aContent, mEventState);
-    } else {
-      mEventState = aContent->IntrinsicState();
-    }
-
     // get the ID and classes for the content
     mContentID = aContent->GetID();
     mClasses = aContent->GetClasses();
 
     // see if there are attributes for the content
     mHasAttributes = aContent->GetAttrCount() > 0;
 
     // get the namespace
     mNameSpaceID = aContent->GetNameSpaceID();
 
     // check for HTMLContent and Link status
     mIsHTMLContent = (mNameSpaceID == kNameSpaceID_XHTML);
-
-    // if HTML content and it has some attributes, check for an HTML link
-    // NOTE: optimization: cannot be a link if no attributes (since it needs an href)
-    nsILinkHandler* linkHandler =
-      mPresContext ? mPresContext->GetLinkHandler() : nsnull;
-    if (mIsHTMLContent && mHasAttributes) {
-      // check if it is an HTML Link
-      if(nsStyleUtil::IsHTMLLink(aContent, linkHandler, &mLinkState)) {
-        mIsLink = PR_TRUE;
-      }
-    } 
-
-    // if not an HTML link, check for a simple xlink (cannot be both HTML link and xlink)
-    // NOTE: optimization: cannot be an XLink if no attributes (since it needs an 
-    if(!mIsLink &&
-       mHasAttributes && 
-       !(mIsHTMLContent || aContent->IsXUL()) && 
-       nsStyleUtil::IsLink(aContent, linkHandler, &mLinkState)) {
-      mIsLink = PR_TRUE;
-    } 
-  }
-
-  if (mLinkState == eLinkState_Visited && !gSupportVisitedPseudo) {
-    mLinkState = eLinkState_Unvisited;
   }
 }
 
 RuleProcessorData::~RuleProcessorData()
 {
   MOZ_COUNT_DTOR(RuleProcessorData);
 
   // Destroy potentially long chains of previous sibling and parent data
@@ -1046,16 +1017,69 @@ const nsString* RuleProcessorData::GetLa
           break;
         }
       }
     }
   }
   return mLanguage;
 }
 
+PRUint32
+RuleProcessorData::ContentState()
+{
+  if (!mGotContentState) {
+    mGotContentState = PR_TRUE;
+    if (mContent) {
+      if (mPresContext) {
+        mPresContext->EventStateManager()->GetContentState(mContent,
+                                                           mContentState);
+      } else {
+        mContentState = mContent->IntrinsicState();
+      }
+    }
+  }
+  return mContentState;
+}
+
+PRBool
+RuleProcessorData::IsLink()
+{
+  if (!mGotLinkInfo) {
+    mGotLinkInfo = PR_TRUE;
+    if (mContent) {
+      // if HTML content and it has some attributes, check for an HTML link
+      // NOTE: optimization: cannot be a link if no attributes (since it needs
+      // an href)
+      nsILinkHandler* linkHandler =
+        mPresContext ? mPresContext->GetLinkHandler() : nsnull;
+      if (mIsHTMLContent && mHasAttributes) {
+        // check if it is an HTML Link
+        if (nsStyleUtil::IsHTMLLink(mContent, linkHandler, &mLinkState)) {
+          mIsLink = PR_TRUE;
+        }
+      }
+
+      // if not an HTML link, check for a simple xlink (cannot be both HTML
+      // link and xlink) NOTE: optimization: cannot be an XLink if no
+      // attributes (since it needs an href)
+      if(!mIsLink &&
+         mHasAttributes && 
+         !(mIsHTMLContent || mContent->IsXUL()) && 
+         nsStyleUtil::IsLink(mContent, linkHandler, &mLinkState)) {
+        mIsLink = PR_TRUE;
+      }
+
+      if (mLinkState == eLinkState_Visited && !gSupportVisitedPseudo) {
+        mLinkState = eLinkState_Unvisited;
+      }
+    }
+  }
+  return mIsLink;
+}
+
 PRInt32
 RuleProcessorData::GetNthIndex(PRBool aIsOfType, PRBool aIsFromEnd,
                                PRBool aCheckEdgeOnly)
 {
   NS_ASSERTION(mParentContent, "caller should check mParentContent");
   NS_ASSERTION(!mPreviousSiblingData ||
                mPreviousSiblingData->mContent->IsNodeOfType(nsINode::eELEMENT),
                "Unexpected previous sibling data");
@@ -1161,17 +1185,17 @@ inline PRBool IsLinkPseudo(nsIAtom* aAto
 {
   return PRBool ((nsCSSPseudoClasses::link == aAtom) || 
                  (nsCSSPseudoClasses::visited == aAtom) ||
                  (nsCSSPseudoClasses::mozAnyLink == aAtom));
 }
 
 // Return whether we should apply a "global" (i.e., universal-tag)
 // selector for event states in quirks mode.  Note that
-// |data.mIsLink| is checked separately by the caller, so we return
+// |data.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)
 {
   return PRBool ((nsGkAtoms::button == aContentTag) ||
                  (nsGkAtoms::img == aContentTag)    ||
                  (nsGkAtoms::input == aContentTag)  ||
                  (nsGkAtoms::label == aContentTag)  ||
                  (nsGkAtoms::select == aContentTag) ||
@@ -1595,33 +1619,33 @@ static PRBool SelectorMatches(RuleProces
     }
     else if (nsCSSPseudoClasses::mozDragOver == pseudoClass->mAtom) {
       stateToCheck = NS_EVENT_STATE_DRAGOVER;
     }
     else if (nsCSSPseudoClasses::target == pseudoClass->mAtom) {
       stateToCheck = NS_EVENT_STATE_URLTARGET;
     }
     else if (IsLinkPseudo(pseudoClass->mAtom)) {
-      if (data.mIsLink) {
+      if (data.IsLink()) {
         if (nsCSSPseudoClasses::mozAnyLink == pseudoClass->mAtom) {
           result = PR_TRUE;
         }
         else {
           NS_ASSERTION(nsCSSPseudoClasses::link == pseudoClass->mAtom ||
                        nsCSSPseudoClasses::visited == pseudoClass->mAtom,
                        "somebody changed IsLinkPseudo");
-          NS_ASSERTION(data.mLinkState == eLinkState_Unvisited ||
-                       data.mLinkState == eLinkState_Visited,
+          NS_ASSERTION(data.LinkState() == eLinkState_Unvisited ||
+                       data.LinkState() == eLinkState_Visited,
                        "unexpected link state for mIsLink");
           if (aStateMask & NS_EVENT_STATE_VISITED) {
             result = PR_TRUE;
             if (aDependence)
               *aDependence = PR_TRUE;
           } else {
-            result = ((eLinkState_Unvisited == data.mLinkState) ==
+            result = ((eLinkState_Unvisited == data.LinkState()) ==
                       (nsCSSPseudoClasses::link == pseudoClass->mAtom));
           }
         }
       }
       else {
         result = PR_FALSE;  // not a link
       }
     }
@@ -1760,28 +1784,28 @@ static PRBool SelectorMatches(RuleProces
           !aSelector->HasTagSelector() && !aSelector->mIDList && 
           !aSelector->mAttrList &&
           // This (or the other way around) both make :not() asymmetric
           // in quirks mode (and it's hard to work around since we're
           // testing the current mNegations, not the first
           // (unnegated)). This at least makes it closer to the spec.
           !isNegated &&
           // important for |IsQuirkEventSensitive|:
-          data.mIsHTMLContent && !data.mIsLink &&
+          data.mIsHTMLContent && !data.IsLink() &&
           !IsQuirkEventSensitive(data.mContentTag)) {
         // In quirks mode, only make certain elements sensitive to
         // selectors ":hover" and ":active".
         result = PR_FALSE;
       } else {
         if (aStateMask & stateToCheck) {
           result = PR_TRUE;
           if (aDependence)
             *aDependence = PR_TRUE;
         } else {
-          result = (0 != (data.mEventState & stateToCheck));
+          result = (0 != (data.ContentState() & stateToCheck));
         }
       }
     }
   }
 
   if (result && aSelector->mAttrList) {
     // test for attribute match
     if (!data.mHasAttributes && !aAttribute) {
--- a/layout/style/nsHTMLStyleSheet.cpp
+++ b/layout/style/nsHTMLStyleSheet.cpp
@@ -425,32 +425,32 @@ nsHTMLStyleSheet::RulesMatching(ElementR
   if (content) {
     nsRuleWalker *ruleWalker = aData->mRuleWalker;
     if (aData->mIsHTMLContent) {
       nsIAtom* tag = aData->mContentTag;
 
       // if we have anchor colors, check if this is an anchor with an href
       if (tag == nsGkAtoms::a) {
         if (mLinkRule || mVisitedRule || mActiveRule) {
-          if (aData->mIsLink) {
-            switch (aData->mLinkState) {
+          if (aData->IsLink()) {
+            switch (aData->LinkState()) {
               case eLinkState_Unvisited:
                 if (mLinkRule)
                   ruleWalker->Forward(mLinkRule);
                 break;
               case eLinkState_Visited:
                 if (mVisitedRule)
                   ruleWalker->Forward(mVisitedRule);
                 break;
               default:
                 break;
             }
 
             // No need to add to the active rule if it's not a link
-            if (mActiveRule && (aData->mEventState & NS_EVENT_STATE_ACTIVE))
+            if (mActiveRule && (aData->ContentState() & NS_EVENT_STATE_ACTIVE))
               ruleWalker->Forward(mActiveRule);
           }
         } // end link/visited/active rules
       } // end A tag
       // add the rule to handle text-align for a <th>
       else if (tag == nsGkAtoms::th) {
         ruleWalker->Forward(mTableTHRule);
       }
@@ -502,18 +502,18 @@ nsHTMLStyleSheet::RulesMatching(ElementR
 
 // Test if style is dependent on content state
 NS_IMETHODIMP
 nsHTMLStyleSheet::HasStateDependentStyle(StateRuleProcessorData* aData,
                                          nsReStyleHint* aResult)
 {
   if (aData->mContent &&
       aData->mIsHTMLContent &&
-      aData->mIsLink &&
       aData->mContentTag == nsGkAtoms::a &&
+      aData->IsLink() &&
       ((mActiveRule && (aData->mStateMask & NS_EVENT_STATE_ACTIVE)) ||
        (mLinkRule && (aData->mStateMask & NS_EVENT_STATE_VISITED)) ||
        (mVisitedRule && (aData->mStateMask & NS_EVENT_STATE_VISITED)))) {
     *aResult = eReStyle_Self;
   }
   else
     *aResult = nsReStyleHint(0);
 
--- a/layout/style/nsRuleProcessorData.h
+++ b/layout/style/nsRuleProcessorData.h
@@ -101,16 +101,22 @@ private:
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
     return aContext->AllocateFromShell(sz);
   }
   void* operator new(size_t sz) CPP_THROW_NEW {
     return ::operator new(sz);
   }
 public:
   const nsString* GetLang();
+  PRUint32 ContentState();
+  PRBool IsLink();
+  nsLinkState LinkState() {
+    NS_ASSERTION(mGotLinkInfo && mIsLink, "Why am I being called?");
+    return mLinkState;
+  }
 
   // Returns a 1-based index of the child in its parent.  If the child
   // is not in its parent's child list (i.e., it is anonymous content),
   // returns 0.
   // If aCheckEdgeOnly is true, the function will return 1 if the result
   // is 1, and something other than 1 (maybe or maybe not a valid
   // result) otherwise.
   PRInt32 GetNthIndex(PRBool aIsOfType, PRBool aIsFromEnd,
@@ -120,39 +126,44 @@ public:
   nsIContent*       mContent;       // weak ref
   nsIContent*       mParentContent; // if content, content->GetParent(); weak ref
   nsRuleWalker*     mRuleWalker; // Used to add rules to our results.
   nsIContent*       mScopedRoot;    // Root of scoped stylesheet (set and unset by the supplier of the scoped stylesheet
   
   nsIAtom*          mContentTag;    // if content, then content->GetTag()
   nsIAtom*          mContentID;     // if styled content, then weak reference to styledcontent->GetID()
   PRPackedBool      mIsHTMLContent; // if content, then does QI on HTMLContent, true or false
-  PRPackedBool      mIsLink;        // if content, calls nsStyleUtil::IsHTMLLink or nsStyleUtil::IsLink
   PRPackedBool      mHasAttributes; // if content, content->GetAttrCount() > 0
   nsCompatibility   mCompatMode;    // Possibly remove use of this in SelectorMatches?
-  nsLinkState       mLinkState;     // if a link, this is the state, otherwise unknown
-  PRInt32           mEventState;    // if content, eventStateMgr->GetContentState()
   PRInt32           mNameSpaceID;   // if content, content->GetNameSapce()
   const nsAttrValue* mClasses;      // if styled content, styledcontent->GetClasses()
   // mPreviousSiblingData and mParentData are always RuleProcessorData
   // and never a derived class.  They are allocated lazily, when
   // selectors require matching of prior siblings or ancestors.
   RuleProcessorData* mPreviousSiblingData;
   RuleProcessorData* mParentData;
 
-protected:
+private:
   nsString *mLanguage; // NULL means we haven't found out the language yet
 
   // This node's index for :nth-child(), :nth-last-child(),
   // :nth-of-type(), :nth-last-of-type().  If -2, needs to be computed.
   // If -1, needs to be computed but known not to be 1.
   // If 0, the node is not at any index in its parent.
   // The first subscript is 0 for -child and 1 for -of-type, the second
   // subscript is 0 for nth- and 1 for nth-last-.
   PRInt32 mNthIndices[2][2];
+
+  PRInt32 mContentState;  // if content, eventStateMgr->GetContentState()
+  nsLinkState mLinkState; // if a link, this is the state, otherwise unknown
+  PRPackedBool mIsLink;   // if content, calls nsStyleUtil::IsHTMLLink
+                          // or nsStyleUtil::IsLink
+  PRPackedBool mGotContentState;
+  PRPackedBool mGotLinkInfo; // Whether we've gotten the right values
+                             // for mLinkState and mIsLink.
 };
 
 struct ElementRuleProcessorData : public RuleProcessorData {
   ElementRuleProcessorData(nsPresContext* aPresContext,
                            nsIContent* aContent, 
                            nsRuleWalker* aRuleWalker)
   : RuleProcessorData(aPresContext,aContent,aRuleWalker)
   {