Bug 704911. Don't restyle based on state selectors that match our node but don't depend on the state that's changing. r=dbaron
authorBoris Zbarsky <bzbarsky@mit.edu>
Mon, 05 Dec 2011 23:58:15 -0500
changeset 81476 5c0c7af66ec01e965d68cb42c670160bdfa64462
parent 81475 85358854403fbd5c6b1731e2eda2722deea51d98
child 81477 b30a2262608de91f296adfefd12cbfffb33cb785
push id3836
push userbzbarsky@mozilla.com
push dateTue, 06 Dec 2011 04:59:05 +0000
treeherdermozilla-inbound@5c0c7af66ec0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs704911
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 704911. Don't restyle based on state selectors that match our node but don't depend on the state that's changing. r=dbaron
layout/reftests/css-selectors/reftest.list
layout/reftests/css-selectors/state-dependent-in-any-ref.html
layout/reftests/css-selectors/state-dependent-in-any.html
layout/reftests/reftest.list
layout/style/nsCSSRuleProcessor.cpp
layout/style/nsCSSRuleProcessor.h
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-selectors/reftest.list
@@ -0,0 +1,1 @@
+== state-dependent-in-any.html state-dependent-in-any-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-selectors/state-dependent-in-any-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      span { color: green; }
+    </style>
+  </head>
+  <body>
+    <input value="Test"><span>This should be green</span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-selectors/state-dependent-in-any.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      span { color: red; }
+      :-moz-any(:valid) + span { color: green; }
+    </style>
+  </head>
+  <body>
+    <input required><span>This should be green</span>
+    <script>
+      document.body.offsetWidth;
+      document.getElementsByTagName("input")[0].value = "Test"
+    </script>
+  </body>
+</html>
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -87,16 +87,19 @@ skip-if(Android) include css-valid/refte
 skip-if(Android) include css-invalid/reftest.list
 
 # css-submit-invalid
 include css-submit-invalid/reftest.list
 
 # css text-overflow
 include text-overflow/reftest.list
 
+# css selectors
+include css-selectors/reftest.list
+
 # css transitions
 include css-transitions/reftest.list
 
 # css :-moz-ui-invalid
 skip-if(Android) include css-ui-invalid/reftest.list
 
 # css :-moz-ui-valid
 skip-if(Android) include css-ui-valid/reftest.list
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -894,17 +894,17 @@ struct RuleCascadeData {
     }
   }
 
   PRInt64 SizeOf() const;
 
   RuleHash                 mRuleHash;
   RuleHash*
     mPseudoElementRuleHashes[nsCSSPseudoElements::ePseudo_PseudoElementCount];
-  nsTArray<nsCSSSelector*> mStateSelectors;
+  nsTArray<nsCSSRuleProcessor::StateSelector>  mStateSelectors;
   nsEventStates            mSelectorDocumentStates;
   PLDHashTable             mClassSelectors;
   PLDHashTable             mIdSelectors;
   nsTArray<nsCSSSelector*> mPossiblyNegatedClassSelectors;
   nsTArray<nsCSSSelector*> mPossiblyNegatedIDSelectors;
   PLDHashTable             mAttributeSelectors;
   PLDHashTable             mAnonBoxRules;
 #ifdef MOZ_XUL
@@ -2362,32 +2362,36 @@ nsCSSRuleProcessor::HasStateDependentSty
   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
 
   // Look up the content node in the state rule list, which points to
   // any (CSS2 definition) simple selector (whether or not it is the
   // subject) that has a state pseudo-class on it.  This means that this
   // code will be matching selectors that aren't real selectors in any
   // stylesheet (e.g., if there is a selector "body > p:hover > a", then
   // "body > p:hover" will be in |cascade->mStateSelectors|).  Note that
-  // |IsStateSelector| below determines which selectors are in
+  // |ComputeSelectorStateDependence| below determines which selectors are in
   // |cascade->mStateSelectors|.
   nsRestyleHint hint = nsRestyleHint(0);
   if (cascade) {
-    nsCSSSelector **iter = cascade->mStateSelectors.Elements(),
-                  **end = iter + cascade->mStateSelectors.Length();
+    StateSelector *iter = cascade->mStateSelectors.Elements(),
+                  *end = iter + cascade->mStateSelectors.Length();
     NodeMatchContext nodeContext(aData->mStateMask, false);
     for(; iter != end; ++iter) {
-      nsCSSSelector* selector = *iter;
+      nsCSSSelector* selector = iter->mSelector;
+      nsEventStates states = iter->mStates;
 
       nsRestyleHint possibleChange = RestyleHintForOp(selector->mOperator);
 
       // If hint already includes all the bits of possibleChange,
       // don't bother calling SelectorMatches, since even if it returns false
       // hint won't change.
+      // Also don't bother calling SelectorMatches if none of the
+      // states passed in are relevant here.
       if ((possibleChange & ~hint) &&
+          states.HasAtLeastOneOfStates(aData->mStateMask) &&
           SelectorMatches(aData->mElement, selector, nodeContext,
                           aData->mTreeMatchContext) &&
           SelectorMatchesTree(aData->mElement, selector->mNext,
                               aData->mTreeMatchContext,
                               false))
       {
         hint = nsRestyleHint(hint | possibleChange);
       }
@@ -2615,33 +2619,33 @@ nsCSSRuleProcessor::ClearRuleCascades()
     RuleCascadeData *next = data->mNext;
     delete data;
     data = next;
   }
   return NS_OK;
 }
 
 
-// This function should return true only for selectors that need to be
-// checked by |HasStateDependentStyle|.
+// This function should return the set of states that this selector
+// depends on; this is used to implement HasStateDependentStyle.  It
+// does NOT recur down into things like :not and :-moz-any.
 inline
-bool IsStateSelector(nsCSSSelector& aSelector)
+nsEventStates ComputeSelectorStateDependence(nsCSSSelector& aSelector)
 {
+  nsEventStates states;
   for (nsPseudoClassList* pseudoClass = aSelector.mPseudoClassList;
        pseudoClass; pseudoClass = pseudoClass->mNext) {
     // Tree pseudo-elements overload mPseudoClassList for things that
     // aren't pseudo-classes.
     if (pseudoClass->mType >= nsCSSPseudoClasses::ePseudoClass_Count) {
       continue;
     }
-    if (!sPseudoClassStates[pseudoClass->mType].IsEmpty()) {
-      return true;
-    }
+    states |= sPseudoClassStates[pseudoClass->mType];
   }
-  return false;
+  return states;
 }
 
 static bool
 AddSelector(RuleCascadeData* aCascade,
             // The part between combinators at the top level of the selector
             nsCSSSelector* aSelectorInTopLevel,
             // The part we should look through (might be in :not or :-moz-any())
             nsCSSSelector* aSelectorPart)
@@ -2679,18 +2683,22 @@ AddSelector(RuleCascadeData* aCascade,
         }
         default: {
           break;
         }
       }
     }
 
     // Build mStateSelectors.
-    if (IsStateSelector(*negation))
-      aCascade->mStateSelectors.AppendElement(aSelectorInTopLevel);
+    nsEventStates dependentStates = ComputeSelectorStateDependence(*negation);
+    if (!dependentStates.IsEmpty()) {
+      aCascade->mStateSelectors.AppendElement(
+        nsCSSRuleProcessor::StateSelector(dependentStates,
+                                          aSelectorInTopLevel));
+    }
 
     // Build mIDSelectors
     if (negation == aSelectorInTopLevel) {
       for (nsAtomList* curID = negation->mIDList; curID;
            curID = curID->mNext) {
         AtomSelectorEntry *entry =
           static_cast<AtomSelectorEntry*>(PL_DHashTableOperate(&aCascade->mIdSelectors,
                                                                curID->mAtom,
--- a/layout/style/nsCSSRuleProcessor.h
+++ b/layout/style/nsCSSRuleProcessor.h
@@ -46,22 +46,24 @@
 #define nsCSSRuleProcessor_h_
 
 #include "nsIStyleRuleProcessor.h"
 #include "nsCSSStyleSheet.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsCSSRules.h"
 #include "nsRuleWalker.h"
+#include "nsEventStates.h"
 
 struct RuleCascadeData;
 struct nsCSSSelectorList;
 struct CascadeEnumData;
 struct TreeMatchContext;
 class nsCSSKeyframesRule;
+class nsCSSSelector;
 
 /**
  * The CSS style rule processor provides a mechanism for sibling style
  * sheets to combine their rule processing in order to allow proper
  * cascading to happen.
  *
  * CSS style rule processors keep a live reference on all style sheets
  * bound to them.  The CSS style sheets keep a weak reference to all the
@@ -156,16 +158,26 @@ public:
 #ifdef XP_WIN
   // Cached theme identifier for the moz-windows-theme media query.
   static PRUint8 GetWindowsThemeIdentifier();
   static void SetWindowsThemeIdentifier(PRUint8 aId) { 
     sWinThemeId = aId;
   }
 #endif
 
+  struct StateSelector {
+    StateSelector(nsEventStates aStates, nsCSSSelector* aSelector)
+      : mStates(aStates),
+        mSelector(aSelector)
+    {}
+
+    nsEventStates mStates;
+    nsCSSSelector* mSelector;
+  };
+
 private:
   static bool CascadeSheet(nsCSSStyleSheet* aSheet, CascadeEnumData* aData);
 
   RuleCascadeData* GetRuleCascade(nsPresContext* aPresContext);
   void RefreshRuleCascade(nsPresContext* aPresContext);
 
   // The sheet order here is the same as in nsStyleSet::mSheets
   sheet_array_type mSheets;