Bug 1174060 - 3 - Place the scrubber correctly when new animations are added; r=miker
authorPatrick Brosset <pbrosset@mozilla.com>
Thu, 03 Sep 2015 09:34:08 +0200
changeset 261285 ba641c7faa1279afc43ec3bb62d1f68ccc6db7a6
parent 261284 688b108cdfe0cdf4efb333ee2ea0241a2a29bc32
child 261286 0ff170e2d376f505eb0a7926611707ec0d091fce
push id64705
push usercbook@mozilla.com
push dateTue, 08 Sep 2015 14:02:43 +0000
treeherdermozilla-inbound@7fa38a962661 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmiker
bugs1174060
milestone43.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 1174060 - 3 - Place the scrubber correctly when new animations are added; r=miker
browser/devtools/animationinspector/animation-controller.js
browser/devtools/animationinspector/animation-panel.js
browser/devtools/animationinspector/components.js
toolkit/devtools/server/actors/animation.js
--- a/browser/devtools/animationinspector/animation-controller.js
+++ b/browser/devtools/animationinspector/animation-controller.js
@@ -303,16 +303,35 @@ let AnimationsController = {
         this.animationPlayers.splice(index, 1);
       }
     }
 
     // Let the UI know the list has been updated.
     this.emit(this.PLAYERS_UPDATED_EVENT, this.animationPlayers);
   }),
 
+  /**
+   * Get the latest known current time of document.timeline.
+   * This value is sent along with all AnimationPlayerActors' states, but it
+   * isn't updated after that, so this function loops over all know animations
+   * to find the highest value.
+   * @return {Number|Boolean} False is returned if this server version doesn't
+   * provide document's current time.
+   */
+  get documentCurrentTime() {
+    let time = 0;
+    for (let {state} of this.animationPlayers) {
+      if (!state.documentCurrentTime) {
+        return false;
+      }
+      time = Math.max(time, state.documentCurrentTime);
+    }
+    return time;
+  },
+
   startAllAutoRefresh: function() {
     if (this.traits.isNewUI) {
       return;
     }
 
     for (let front of this.animationPlayers) {
       front.startAutoRefresh();
     }
--- a/browser/devtools/animationinspector/animation-panel.js
+++ b/browser/devtools/animationinspector/animation-panel.js
@@ -178,17 +178,18 @@ let AnimationsPanel = {
 
     // Empty the whole panel first.
     this.hideErrorMessage();
     yield this.destroyPlayerWidgets();
 
     // Re-render the timeline component.
     if (this.animationsTimelineComponent) {
       this.animationsTimelineComponent.render(
-        AnimationsController.animationPlayers);
+        AnimationsController.animationPlayers,
+        AnimationsController.documentCurrentTime);
     }
 
     // If there are no players to show, show the error message instead and
     // return.
     if (!AnimationsController.animationPlayers.length) {
       this.displayErrorMessage();
       this.emit(this.UI_UPDATED_EVENT);
       done();
--- a/browser/devtools/animationinspector/components.js
+++ b/browser/devtools/animationinspector/components.js
@@ -791,17 +791,17 @@ AnimationsTimeline.prototype = {
 
     this.scrubberEl.style.left = offset + "px";
 
     let time = TimeScale.distanceToRelativeTime(offset,
       this.timeHeaderEl.offsetWidth);
     this.emit("current-time-changed", time);
   },
 
-  render: function(animations) {
+  render: function(animations, documentCurrentTime) {
     this.unrender();
 
     this.animations = animations;
     if (!this.animations.length) {
       return;
     }
 
     // Loop first to set the time scale for all current animations.
@@ -849,22 +849,21 @@ AnimationsTimeline.prototype = {
       // Save the targetNode so it can be destroyed later.
       this.targetNodes.push(targetNode);
     }
 
     // Use the document's current time to position the scrubber (if the server
     // doesn't provide it, hide the scrubber entirely).
     // Note that because the currentTime was sent via the protocol, some time
     // may have gone by since then, and so the scrubber might be a bit late.
-    let time = this.animations[0].state.documentCurrentTime;
-    if (!time) {
+    if (!documentCurrentTime) {
       this.scrubberEl.style.display = "none";
     } else {
       this.scrubberEl.style.display = "block";
-      this.startAnimatingScrubber(time);
+      this.startAnimatingScrubber(documentCurrentTime);
     }
   },
 
   startAnimatingScrubber: function(time) {
     let x = TimeScale.startTimeToDistance(time, this.timeHeaderEl.offsetWidth);
     this.scrubberEl.style.left = x + "px";
 
     if (time < TimeScale.minStartTime ||
--- a/toolkit/devtools/server/actors/animation.js
+++ b/toolkit/devtools/server/actors/animation.js
@@ -319,17 +319,24 @@ let AnimationPlayerActor = ActorClass({
   }),
 
   /**
    * Executed when the current animation changes, used to emit the new state
    * the the front.
    */
   onAnimationMutation: function(mutations) {
     let hasChanged = false;
-    for (let {changedAnimations} of mutations) {
+    for (let {removedAnimations, changedAnimations} of mutations) {
+      if (removedAnimations.length) {
+        // Reset the local copy of the state on removal, since the animation can
+        // be kept on the client and re-added, its state needs to be sent in
+        // full.
+        this.currentState = null;
+      }
+
       if (!changedAnimations.length) {
         return;
       }
       if (changedAnimations.some(animation => animation === this.player)) {
         hasChanged = true;
         break;
       }
     }