Bug 562815 part 6 - Check if property is cached in rule tree; r=dbaron
authorBrian Birtles <birtles@gmail.com>
Tue, 23 Aug 2011 08:34:12 +0900
changeset 75694 621f3dd51ed24c5a5ed986ca4c2b228ac9f27f08
parent 75693 d571c0e5cf180acf8506895bf7c9916a8a204bb4
child 75695 f385c77d177ef1e3dcdb30a180e0ce92e1b48204
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersdbaron
bugs562815
milestone9.0a1
Bug 562815 part 6 - Check if property is cached in rule tree; r=dbaron
content/smil/nsSMILCSSValueType.cpp
layout/style/nsRuleNode.h
layout/style/nsStyleAnimation.cpp
layout/style/nsStyleAnimation.h
layout/style/nsStyleSet.cpp
layout/style/nsStyleSet.h
--- a/content/smil/nsSMILCSSValueType.cpp
+++ b/content/smil/nsSMILCSSValueType.cpp
@@ -382,23 +382,20 @@ ValueFromStringHelper(nsCSSProperty aPro
   PRUint32 subStringBegin = 0;
   PRInt32 absValuePos = nsSMILParserUtils::CheckForNegativeNumber(aString);
   if (absValuePos > 0) {
     isNegative = PR_TRUE;
     subStringBegin = (PRUint32)absValuePos; // Start parsing after '-' sign
   }
   nsDependentSubstring subString(aString, subStringBegin);
   if (!nsStyleAnimation::ComputeValue(aPropID, aTargetElement, subString,
-                                      PR_TRUE, aStyleAnimValue)) {
+                                      PR_TRUE, aStyleAnimValue,
+                                      aIsContextSensitive)) {
     return PR_FALSE;
   }
-  if (aIsContextSensitive) {
-    // XXX
-    *aIsContextSensitive = PR_TRUE;
-  }
   if (isNegative) {
     InvertSign(aStyleAnimValue);
   }
   
   if (aPropID == eCSSProperty_font_size) {
     // Divide out text-zoom, since SVG is supposed to ignore it
     NS_ABORT_IF_FALSE(aStyleAnimValue.GetUnit() ==
                         nsStyleAnimation::eUnit_Coord,
--- a/layout/style/nsRuleNode.h
+++ b/layout/style/nsRuleNode.h
@@ -739,11 +739,15 @@ public:
   // Return whether the rule tree for which this node is the root has
   // cached data such that we need to do dynamic change handling for
   // changes that change the results of media queries or require
   // rebuilding all style data.
   PRBool TreeHasCachedData() const {
     NS_ASSERTION(IsRoot(), "should only be called on root of rule tree");
     return HaveChildren() || mStyleData.mInheritedData || mStyleData.mResetData;
   }
+
+  PRBool NodeHasCachedData(const nsStyleStructID aSID) {
+    return !!mStyleData.GetStyleData(aSID);
+  }
 };
 
 #endif
--- a/layout/style/nsStyleAnimation.cpp
+++ b/layout/style/nsStyleAnimation.cpp
@@ -1921,86 +1921,99 @@ LookupStyleContext(dom::Element* aElemen
   nsIDocument* doc = aElement->GetCurrentDoc();
   nsIPresShell* shell = doc->GetShell();
   if (!shell) {
     return nsnull;
   }
   return nsComputedDOMStyle::GetStyleContextForElement(aElement, nsnull, shell);
 }
 
-
-/**
- * Helper function: StyleWithRuleAdded
- * Creates a custom style context as a sibling of the given element's
- * nsStyleContext with the given css::StyleRule added.
- *
- * @param aTargetElement  The element whose style context we'll use as a
- *                        sibling for our custom style context.
- * @param aStyleRule      The style rule to add to the style context.
- * @return The generated custom nsStyleContext, or nsnull on failure.
- */
-already_AddRefed<nsStyleContext>
-StyleWithRuleAdded(dom::Element* aTargetElement,
-                   css::StyleRule* aStyleRule)
-{
-  NS_ABORT_IF_FALSE(aTargetElement, "null target element");
-  NS_ABORT_IF_FALSE(aTargetElement->GetCurrentDoc(),
-                    "element needs to be in a document "
-                    "if we're going to look up its style context");
-  NS_ABORT_IF_FALSE(aStyleRule, "null style rule");
-
-  // Look up style context for our target element
-  nsRefPtr<nsStyleContext> styleContext = LookupStyleContext(aTargetElement);
-  if (!styleContext) {
-    return nsnull;
-  }
-
-  aStyleRule->RuleMatched();
-
-  // Create a temporary nsStyleContext for the style rule
-  nsCOMArray<nsIStyleRule> ruleArray;
-  ruleArray.AppendObject(aStyleRule);
-  nsStyleSet* styleSet = styleContext->PresContext()->StyleSet();
-  return styleSet->ResolveStyleByAddingRules(styleContext, ruleArray);
-}
-
 PRBool
 nsStyleAnimation::ComputeValue(nsCSSProperty aProperty,
                                dom::Element* aTargetElement,
                                const nsAString& aSpecifiedValue,
                                PRBool aUseSVGMode,
-                               Value& aComputedValue)
+                               Value& aComputedValue,
+                               PRBool* aIsContextSensitive)
 {
   NS_ABORT_IF_FALSE(aTargetElement, "null target element");
   NS_ABORT_IF_FALSE(aTargetElement->GetCurrentDoc(),
                     "we should only be able to actively animate nodes that "
                     "are in a document");
 
   nsCSSProperty propToParse =
     nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_REPORT_OTHER_NAME)
       ? nsCSSProps::OtherNameFor(aProperty) : aProperty;
 
   // Parse specified value into a temporary css::StyleRule
   nsRefPtr<css::StyleRule> styleRule =
     BuildStyleRule(propToParse, aTargetElement, aSpecifiedValue, aUseSVGMode);
   if (!styleRule) {
     return PR_FALSE;
   }
-  nsRefPtr<nsStyleContext> tmpStyleContext =
-    StyleWithRuleAdded(aTargetElement, styleRule);
-  if (!tmpStyleContext) {
-    return PR_FALSE;
+
+  if (nsCSSProps::IsShorthand(aProperty) ||
+      nsCSSProps::kAnimTypeTable[aProperty] == eStyleAnimType_None) {
+    // Just capture the specified value
+    aComputedValue.SetUnparsedStringValue(nsString(aSpecifiedValue));
+    if (aIsContextSensitive) {
+      // Since we're just returning the string as-is, aComputedValue isn't going
+      // to change depending on the context
+      *aIsContextSensitive = PR_FALSE;
+    }
+    return PR_TRUE;
   }
 
- if (nsCSSProps::IsShorthand(aProperty) ||
-     nsCSSProps::kAnimTypeTable[aProperty] == eStyleAnimType_None) {
-    // Just capture the specified value
-    aComputedValue.SetUnparsedStringValue(nsString(aSpecifiedValue));
-    return PR_TRUE;
+  // Look up style context for our target element
+  nsRefPtr<nsStyleContext> styleContext = LookupStyleContext(aTargetElement);
+  if (!styleContext) {
+    return PR_FALSE;
   }
+  nsStyleSet* styleSet = styleContext->PresContext()->StyleSet();
+
+  nsRefPtr<nsStyleContext> tmpStyleContext;
+  if (aIsContextSensitive) {
+    nsCOMArray<nsIStyleRule> ruleArray;
+    ruleArray.AppendObject(styleSet->InitialStyleRule());
+    ruleArray.AppendObject(styleRule);
+    styleRule->RuleMatched();
+    tmpStyleContext =
+      styleSet->ResolveStyleByAddingRules(styleContext, ruleArray);
+    if (!tmpStyleContext) {
+      return PR_FALSE;
+    }
+
+    // Force walk of rule tree
+    nsStyleStructID sid = nsCSSProps::kSIDTable[aProperty];
+    tmpStyleContext->GetStyleData(sid);
+
+    // If the rule node will have cached style data if the value is not
+    // context-sensitive. So if there's nothing cached, it's not context
+    // sensitive.
+    *aIsContextSensitive =
+      !tmpStyleContext->GetRuleNode()->NodeHasCachedData(sid);
+  }
+
+  // If we're not concerned whether the property is context sensitive then just
+  // add the rule to a new temporary style context alongside the target
+  // element's style context.
+  // Also, if we previously discovered that this property IS context-sensitive
+  // then we need to throw the temporary style context out since the property's
+  // value may have been biased by the 'initial' values supplied.
+  if (!aIsContextSensitive || *aIsContextSensitive) {
+    nsCOMArray<nsIStyleRule> ruleArray;
+    ruleArray.AppendObject(styleRule);
+    styleRule->RuleMatched();
+    tmpStyleContext =
+      styleSet->ResolveStyleByAddingRules(styleContext, ruleArray);
+    if (!tmpStyleContext) {
+      return PR_FALSE;
+    }
+  }
+
   // Extract computed value of our property from the temporary style rule
   return ExtractComputedValue(aProperty, tmpStyleContext, aComputedValue);
 }
 
 PRBool
 nsStyleAnimation::UncomputeValue(nsCSSProperty aProperty,
                                  nsPresContext* aPresContext,
                                  const Value& aComputedValue,
--- a/layout/style/nsStyleAnimation.h
+++ b/layout/style/nsStyleAnimation.h
@@ -170,23 +170,32 @@ public:
    * @param aProperty       The property whose value we're computing.
    * @param aTargetElement  The content node to which our computed value is
    *                        applicable.
    * @param aSpecifiedValue The specified value, from which we'll build our
    *                        computed value.
    * @param aUseSVGMode     A flag to indicate whether we should parse
    *                        |aSpecifiedValue| in SVG mode.
    * @param [out] aComputedValue The resulting computed value.
+   * @param [out] aIsContextSensitive
+   *                        Set to PR_TRUE if |aSpecifiedValue| may produce
+   *                        a different |aComputedValue| depending on other CSS
+   *                        properties on |aTargetElement| or its ancestors.
+   *                        PR_FALSE otherwise.
+   *                        Note that the operation of this method is
+   *                        significantly faster when |aIsContextSensitive| is
+   *                        nsnull.
    * @return PR_TRUE on success, PR_FALSE on failure.
    */
   static PRBool ComputeValue(nsCSSProperty aProperty,
-                             mozilla::dom::Element* aElement,
+                             mozilla::dom::Element* aTargetElement,
                              const nsAString& aSpecifiedValue,
                              PRBool aUseSVGMode,
-                             Value& aComputedValue);
+                             Value& aComputedValue,
+                             PRBool* aIsContextSensitive = nsnull);
 
   /**
    * Creates a specified value for the given computed value.
    *
    * The first overload fills in an nsCSSValue object; the second
    * produces a string.  The nsCSSValue result may depend on objects
    * owned by the |aComputedValue| object, so users of that variant
    * must keep |aComputedValue| alive longer than |aSpecifiedValue|.
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -52,16 +52,17 @@
 #include "nsStyleContext.h"
 #include "mozilla/css/StyleRule.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsCSSPseudoElements.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsIContent.h"
 #include "nsIFrame.h"
 #include "nsContentUtils.h"
+#include "nsRuleData.h"
 #include "nsRuleProcessorData.h"
 #include "nsTransitionManager.h"
 #include "nsAnimationManager.h"
 #include "nsEventStates.h"
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla::dom;
 namespace css = mozilla::css;
@@ -75,16 +76,60 @@ nsEmptyStyleRule::MapRuleInfoInto(nsRule
 
 #ifdef DEBUG
 /* virtual */ void
 nsEmptyStyleRule::List(FILE* out, PRInt32 aIndent) const
 {
 }
 #endif
 
+NS_IMPL_ISUPPORTS1(nsInitialStyleRule, nsIStyleRule)
+
+/* virtual */ void
+nsInitialStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
+{
+  // Iterate over the property groups
+  for (nsStyleStructID sid = nsStyleStructID(0);
+       sid < nsStyleStructID_Length; sid = nsStyleStructID(sid + 1)) {
+    if (aRuleData->mSIDs & (1 << sid)) {
+      // Iterate over nsCSSValues within the property group
+      nsCSSValue * const value_start =
+        aRuleData->mValueStorage + aRuleData->mValueOffsets[sid];
+      for (nsCSSValue *value = value_start,
+           *value_end = value + nsCSSProps::PropertyCountInStruct(sid);
+           value != value_end; ++value) {
+        // If MathML is disabled take care not to set MathML properties (or we
+        // will trigger assertions in nsRuleNode)
+        if (sid == eStyleStruct_Font &&
+            !aRuleData->mPresContext->Document()->GetMathMLEnabled()) {
+          size_t index = value - value_start;
+          if (index == nsCSSProps::PropertyIndexInStruct(
+                          eCSSProperty_script_level) ||
+              index == nsCSSProps::PropertyIndexInStruct(
+                          eCSSProperty_script_size_multiplier) ||
+              index == nsCSSProps::PropertyIndexInStruct(
+                          eCSSProperty_script_min_size)) {
+            continue;
+          }
+        }
+        if (value->GetUnit() == eCSSUnit_Null) {
+          value->SetInitialValue();
+        }
+      }
+    }
+  }
+}
+
+#ifdef DEBUG
+/* virtual */ void
+nsInitialStyleRule::List(FILE* out, PRInt32 aIndent) const
+{
+}
+#endif
+
 static const nsStyleSet::sheetType gCSSSheetTypes[] = {
   nsStyleSet::eAgentSheet,
   nsStyleSet::eUserSheet,
   nsStyleSet::eDocSheet,
   nsStyleSet::eOverrideSheet
 };
 
 nsStyleSet::nsStyleSet()
@@ -1560,8 +1605,17 @@ nsStyleSet::EnsureUniqueInnerOnCSSSheets
 
     // Enqueue all the sheet's children.
     if (!sheet->AppendAllChildSheets(queue)) {
       return nsCSSStyleSheet::eUniqueInner_CloneFailed;
     }
   }
   return res;
 }
+
+nsIStyleRule*
+nsStyleSet::InitialStyleRule()
+{
+  if (!mInitialStyleRule) {
+    mInitialStyleRule = new nsInitialStyleRule;
+  }
+  return mInitialStyleRule;
+}
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -69,16 +69,25 @@ class nsEmptyStyleRule : public nsIStyle
 {
   NS_DECL_ISUPPORTS
   virtual void MapRuleInfoInto(nsRuleData* aRuleData);
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const;
 #endif
 };
 
+class nsInitialStyleRule : public nsIStyleRule
+{
+  NS_DECL_ISUPPORTS
+  virtual void MapRuleInfoInto(nsRuleData* aRuleData);
+#ifdef DEBUG
+  virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const;
+#endif
+};
+
 // The style set object is created by the document viewer and ownership is
 // then handed off to the PresShell.  Only the PresShell should delete a
 // style set.
 
 class nsStyleSet
 {
  public:
   nsStyleSet();
@@ -304,16 +313,18 @@ class nsStyleSet
 
   // Notify the style set that a rulenode that wasn't in use now is
   void RuleNodeInUse() {
     --mUnusedRuleNodeCount;
   }
 
   nsCSSStyleSheet::EnsureUniqueInnerResult EnsureUniqueInnerOnCSSSheets();
 
+  nsIStyleRule* InitialStyleRule();
+
  private:
   // Not to be implemented
   nsStyleSet(const nsStyleSet& aCopy);
   nsStyleSet& operator=(const nsStyleSet& aCopy);
 
   // Returns false on out-of-memory.
   PRBool BuildDefaultStyleData(nsPresContext* aPresContext);
 
@@ -395,16 +406,20 @@ class nsStyleSet
 
   PRUint32 mUnusedRuleNodeCount; // used to batch rule node GC
   nsTArray<nsStyleContext*> mRoots; // style contexts with no parent
 
   // Empty style rules to force things that restrict which properties
   // apply into different branches of the rule tree.
   nsRefPtr<nsEmptyStyleRule> mFirstLineRule, mFirstLetterRule;
 
+  // Style rule which sets all properties to their initial values for
+  // determining when context-sensitive values are in use.
+  nsRefPtr<nsInitialStyleRule> mInitialStyleRule;
+
   PRUint16 mBatching;
 
   // Old rule trees, which should only be non-empty between
   // BeginReconstruct and EndReconstruct, but in case of bugs that cause
   // style contexts to exist too long, may last longer.
   nsTArray<nsRuleNode*> mOldRuleTrees;
 
   unsigned mInShutdown : 1;