Bug 978648: Handle dynamic changes to @keyframes rules and keyframe rules better. r=heycam
authorL. David Baron <dbaron@dbaron.org>
Tue, 04 Mar 2014 20:13:20 -0800
changeset 171931 1a34a6a07d71
parent 171930 50462316925c
child 171932 acabb39782ef
push id26342
push usercbook@mozilla.com
push dateWed, 05 Mar 2014 12:04:59 +0000
treeherdermozilla-central@7f7d0399102a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs978648
milestone30.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 978648: Handle dynamic changes to @keyframes rules and keyframe rules better. r=heycam
layout/style/nsCSSRules.cpp
layout/style/test/test_animations.html
--- a/layout/style/nsCSSRules.cpp
+++ b/layout/style/nsCSSRules.cpp
@@ -32,16 +32,17 @@
 #include "mozilla/css/Declaration.h"
 #include "nsCSSParser.h"
 #include "nsPrintfCString.h"
 #include "nsDOMClassInfoID.h"
 #include "mozilla/dom/CSSStyleDeclarationBinding.h"
 #include "StyleRule.h"
 #include "nsFont.h"
 #include "nsIURI.h"
+#include "mozAutoDocUpdate.h"
 
 using namespace mozilla;
 
 #define IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(class_, super_) \
   /* virtual */ nsIDOMCSSRule* class_::GetDOMRule()               \
   { return this; }                                                \
   /* virtual */ nsIDOMCSSRule* class_::GetExistingDOMRule()       \
   { return this; }
@@ -2369,25 +2370,33 @@ nsCSSKeyframeRule::DoGetKeyText(nsAStrin
 
 NS_IMETHODIMP
 nsCSSKeyframeRule::SetKeyText(const nsAString& aKeyText)
 {
   nsCSSParser parser;
 
   InfallibleTArray<float> newSelectors;
   // FIXME: pass filename and line number
-  if (parser.ParseKeyframeSelectorString(aKeyText, nullptr, 0, newSelectors)) {
-    newSelectors.SwapElements(mKeys);
-  } else {
+  if (!parser.ParseKeyframeSelectorString(aKeyText, nullptr, 0, newSelectors)) {
     // for now, we don't do anything if the parse fails
+    return NS_OK;
   }
 
+  nsIDocument* doc = GetDocument();
+  MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
+
+  newSelectors.SwapElements(mKeys);
+
   nsCSSStyleSheet* sheet = GetStyleSheet();
   if (sheet) {
     sheet->SetModifiedByChildRule();
+
+    if (doc) {
+      doc->StyleRuleChanged(sheet, this, this);
+    }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSSKeyframeRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
 {
@@ -2396,25 +2405,35 @@ nsCSSKeyframeRule::GetStyle(nsIDOMCSSSty
   }
   NS_ADDREF(*aStyle = mDOMDeclaration);
   return NS_OK;
 }
 
 void
 nsCSSKeyframeRule::ChangeDeclaration(css::Declaration* aDeclaration)
 {
+  // Our caller already did a BeginUpdate/EndUpdate, but with
+  // UPDATE_CONTENT, and we need UPDATE_STYLE to trigger work in
+  // PresShell::EndUpdate.
+  nsIDocument* doc = GetDocument();
+  MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
+
   // Be careful to not assign to an nsAutoPtr if we would be assigning
   // the thing it already holds.
   if (aDeclaration != mDeclaration) {
     mDeclaration = aDeclaration;
   }
 
   nsCSSStyleSheet* sheet = GetStyleSheet();
   if (sheet) {
     sheet->SetModifiedByChildRule();
+
+    if (doc) {
+      doc->StyleRuleChanged(sheet, this, this);
+    }
   }
 }
 
 /* virtual */ size_t
 nsCSSKeyframeRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this);
 
@@ -2528,21 +2547,32 @@ nsCSSKeyframesRule::GetName(nsAString& a
 {
   aName = mName;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSSKeyframesRule::SetName(const nsAString& aName)
 {
+  if (mName == aName) {
+    return NS_OK;
+  }
+
+  nsIDocument* doc = GetDocument();
+  MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
+
   mName = aName;
 
   nsCSSStyleSheet* sheet = GetStyleSheet();
   if (sheet) {
     sheet->SetModifiedByChildRule();
+
+    if (doc) {
+      doc->StyleRuleChanged(sheet, this, this);
+    }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSSKeyframesRule::GetCssRules(nsIDOMCSSRuleList* *aRuleList)
 {
@@ -2556,17 +2586,29 @@ nsCSSKeyframesRule::AppendRule(const nsA
   // which also turns out to match WebKit:
   // http://lists.w3.org/Archives/Public/www-style/2011Apr/0034.html
   nsCSSParser parser;
 
   // FIXME: pass filename and line number
   nsRefPtr<nsCSSKeyframeRule> rule =
     parser.ParseKeyframeRule(aRule, nullptr, 0);
   if (rule) {
+    nsIDocument* doc = GetDocument();
+    MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
+
     AppendStyleRule(rule);
+
+    nsCSSStyleSheet* sheet = GetStyleSheet();
+    if (sheet) {
+      sheet->SetModifiedByChildRule();
+
+      if (doc) {
+        doc->StyleRuleChanged(sheet, this, this);
+      }
+    }
   }
 
   return NS_OK;
 }
 
 static const uint32_t RULE_NOT_FOUND = uint32_t(-1);
 
 uint32_t
@@ -2592,20 +2634,28 @@ nsCSSKeyframesRule::FindRuleIndexForKey(
   return RULE_NOT_FOUND;
 }
 
 NS_IMETHODIMP
 nsCSSKeyframesRule::DeleteRule(const nsAString& aKey)
 {
   uint32_t index = FindRuleIndexForKey(aKey);
   if (index != RULE_NOT_FOUND) {
+    nsIDocument* doc = GetDocument();
+    MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
+
     mRules.RemoveObjectAt(index);
+
     nsCSSStyleSheet* sheet = GetStyleSheet();
     if (sheet) {
       sheet->SetModifiedByChildRule();
+
+      if (doc) {
+        doc->StyleRuleChanged(sheet, this, this);
+      }
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSSKeyframesRule::FindRule(const nsAString& aKey,
                              nsIDOMMozCSSKeyframeRule** aResult)
--- a/layout/style/test/test_animations.html
+++ b/layout/style/test/test_animations.html
@@ -1492,14 +1492,45 @@ new_div("animation-name: anim2, anim3; a
 is(cs.marginRight, "25px", "animation-name list length is the length that matters");
 is(cs.marginTop, "25px", "animation-name list length is the length that matters");
 done_div();
 new_div("animation-name: anim2, anim3, anim2; animation-duration: 1s; animation-timing-function: linear; animation-delay: -250ms, -250ms, -750ms, -500ms;");
 is(cs.marginRight, "75px", "animation-name list length is the length that matters, and the last occurrence of a name wins");
 is(cs.marginTop, "25px", "animation-name list length is the length that matters");
 done_div();
 
+var dyn_sheet_elt = document.createElement("style");
+document.head.appendChild(dyn_sheet_elt);
+var dyn_sheet = dyn_sheet_elt.sheet;
+dyn_sheet.insertRule("@keyframes dyn1 { from { margin-left: 0 } 50% { margin-left: 50px } to { margin-left: 100px } }", 0);
+dyn_sheet.insertRule("@keyframes dyn2 { from { margin-left: 100px } to { margin-left: 200px } }", 1);
+var dyn1 = dyn_sheet.cssRules[0];
+var dyn2 = dyn_sheet.cssRules[1];
+new_div("animation: dyn1 1s linear");
+is(cs.marginLeft, "0px", "dynamic rule change test, initial state");
+advance_clock(250);
+is(cs.marginLeft, "25px", "dynamic rule change test, 250ms");
+dyn2.name = "dyn1";
+is(cs.marginLeft, "125px", "dynamic rule change test, change in @keyframes name applies");
+dyn2.appendRule("50% { margin-left: 0px }");
+is(cs.marginLeft, "50px", "dynamic rule change test, @keyframes appendRule");
+var dyn2_kf1 = dyn2.cssRules[0]; // currently 0% { margin-left: 100px }
+dyn2_kf1.style.marginLeft = "-100px";
+// FIXME: Bug 978833 (keyframe rules used as nsIStyleRule but doesn't follow immutability contract)
+todo_is(cs.marginLeft, "-50px", "dynamic rule change test, keyframe style set");
+dyn2.name = "dyn2";
+is(cs.marginLeft, "25px", "dynamic rule change test, change in @keyframes name applies (second time)");
+var dyn1_kf2 = dyn1.cssRules[1]; // currently 50% { margin-left: 50px }
+dyn1_kf2.keyText = "25%";
+is(cs.marginLeft, "50px", "dynamic rule change test, change in keyframe keyText");
+dyn1.deleteRule("25%");
+is(cs.marginLeft, "25px", "dynamic rule change test, @keyframes deleteRule");
+done_div();
+dyn_sheet_elt.parentNode.removeChild(dyn_sheet_elt);
+dyn_sheet_elt = null;
+dyn_sheet = null;
+
 SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
 
 </script>
 </pre>
 </body>
 </html>