Bug 1155661 - 3 - Add a way to play/pause the current animations at the same time; r=miker
authorPatrick Brosset <pbrosset@mozilla.com>
Wed, 16 Sep 2015 14:42:32 +0200
changeset 295848 b8d10d58218eb962d2e545ad3d6f296ee4f21ed1
parent 295847 6c3532823c25db03dbae91614c71341622f898cc
child 295849 0c461fc0a8236160e7e6979d46b69452a36a268a
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmiker
bugs1155661
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 1155661 - 3 - Add a way to play/pause the current animations at the same time; r=miker This is a useful method to expose on the AnimationsActor so that the front-end can play or pause the list of animations currently displayed in the UI without introducing synchronization offsets. This way all animations are paused synchronously on the server instead of having to request pausing each animation one by one from the front-end.
browser/devtools/animationinspector/animation-controller.js
toolkit/devtools/server/actors/animation.js
toolkit/devtools/server/tests/browser/browser_animation_actors_08.js
--- a/browser/devtools/animationinspector/animation-controller.js
+++ b/browser/devtools/animationinspector/animation-controller.js
@@ -75,16 +75,18 @@ function destroy() {
  * features should be enabled/disabled.
  * @param {Target} target The current toolbox target.
  * @return {Object} An object with boolean properties.
  */
 var getServerTraits = Task.async(function*(target) {
   let config = [
     { name: "hasToggleAll", actor: "animations",
       method: "toggleAll" },
+    { name: "hasToggleSeveral", actor: "animations",
+      method: "toggleSeveral" },
     { name: "hasSetCurrentTime", actor: "animationplayer",
       method: "setCurrentTime" },
     { name: "hasMutationEvents", actor: "animations",
      method: "stopAnimationPlayerUpdates" },
     { name: "hasSetPlaybackRate", actor: "animationplayer",
       method: "setPlaybackRate" },
     { name: "hasTargetNode", actor: "domwalker",
       method: "getNodeFromActor" },
@@ -241,29 +243,52 @@ var AnimationsController = {
     if (!this.traits.hasToggleAll) {
       return promise.resolve();
     }
 
     return this.animationsFront.toggleAll().catch(e => console.error(e));
   },
 
   /**
+   * Similar to toggleAll except that it only plays/pauses the currently known
+   * animations (those listed in this.animationPlayers).
+   * @param {Boolean} shouldPause True if the animations should be paused, false
+   * if they should be played.
+   * @return {Promise} Resolves when the playState has been changed.
+   */
+  toggleCurrentAnimations: Task.async(function*(shouldPause) {
+    if (this.traits.hasToggleSeveral) {
+      yield this.animationsFront.toggleSeveral(this.animationPlayers,
+                                               shouldPause);
+    } else {
+      // Fall back to pausing/playing the players one by one, which is bound to
+      // introduce some de-synchronization.
+      for (let player of this.animationPlayers) {
+        if (shouldPause) {
+          yield player.pause();
+        } else {
+          yield player.play();
+        }
+      }
+    }
+  }),
+
+  /**
    * Set all known animations' currentTimes to the provided time.
-   * Note that depending on the server's capabilities, this might resolve in
-   * either one packet, or as many packets as there are animations. In the
-   * latter case, some time deltas might be introduced.
    * @param {Number} time.
    * @param {Boolean} shouldPause Should the animations be paused too.
    * @return {Promise} Resolves when the current time has been set.
    */
   setCurrentTimeAll: Task.async(function*(time, shouldPause) {
     if (this.traits.hasSetCurrentTimes) {
       yield this.animationsFront.setCurrentTimes(this.animationPlayers, time,
                                                  shouldPause);
     } else {
+      // Fall back to pausing and setting the current time on each player, one
+      // by one, which is bound to introduce some de-synchronization.
       for (let animation of this.animationPlayers) {
         if (shouldPause) {
           yield animation.pause();
         }
         yield animation.setCurrentTime(time);
       }
     }
   }),
--- a/toolkit/devtools/server/actors/animation.js
+++ b/toolkit/devtools/server/actors/animation.js
@@ -836,16 +836,34 @@ var AnimationsActor = exports.Animations
     }
     return this.pauseAll();
   }, {
     request: {},
     response: {}
   }),
 
   /**
+   * Toggle (play/pause) several animations at the same time.
+   * @param {Array} players A list of AnimationPlayerActor objects.
+   * @param {Boolean} shouldPause If set to true, the players will be paused,
+   * otherwise they will be played.
+   */
+  toggleSeveral: method(function(players, shouldPause) {
+    return promise.all(players.map(player => {
+      return shouldPause ? player.pause() : player.play();
+    }));
+  }, {
+    request: {
+      players: Arg(0, "array:animationplayer"),
+      shouldPause: Arg(1, "boolean")
+    },
+    response: {}
+  }),
+
+  /**
    * Set the current time of several animations at the same time.
    * @param {Array} players A list of AnimationPlayerActor.
    * @param {Number} time The new currentTime.
    * @param {Boolean} shouldPause Should the players be paused too.
    */
   setCurrentTimes: method(function(players, time, shouldPause) {
     return promise.all(players.map(player => {
       let pause = shouldPause ? player.pause() : promise.resolve();
--- a/toolkit/devtools/server/tests/browser/browser_animation_actors_08.js
+++ b/toolkit/devtools/server/tests/browser/browser_animation_actors_08.js
@@ -1,55 +1,88 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-// Check that the AnimationsActor can pause/play all animations at once.
+// Check that the AnimationsActor can pause/play all animations at once, and
+// check that it can also pause/play a given list of animations at once.
 
 const {AnimationsFront} = require("devtools/server/actors/animation");
 const {InspectorFront} = require("devtools/server/actors/inspector");
 
+// List of selectors that match "all" animated nodes in the test page.
+// This list misses a bunch of animated nodes on purpose. Only the ones that
+// have infinite animations are listed. This is done to avoid intermittents
+// caused when finite animations are already done playing by the time the test
+// runs.
+const ALL_ANIMATED_NODES = [".simple-animation", ".multiple-animations",
+                            ".delayed-animation"];
+// List of selectors that match some animated nodes in the test page only.
+const SOME_ANIMATED_NODES = [".simple-animation", ".delayed-animation"];
+
 add_task(function*() {
-  let doc = yield addTab(MAIN_DOMAIN + "animation.html");
+  yield addTab(MAIN_DOMAIN + "animation.html");
 
   initDebuggerServer();
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   let form = yield connectDebuggerClient(client);
   let inspector = InspectorFront(client, form);
   let walker = yield inspector.getWalker();
   let front = AnimationsFront(client, form);
 
   info("Pause all animations in the test document");
   yield front.pauseAll();
-  yield checkAllAnimationsStates(walker, front, "paused");
+  yield checkAnimationsStates(walker, front, ALL_ANIMATED_NODES, "paused");
 
   info("Play all animations in the test document");
   yield front.playAll();
-  yield checkAllAnimationsStates(walker, front, "running");
+  yield checkAnimationsStates(walker, front, ALL_ANIMATED_NODES, "running");
 
   info("Pause all animations in the test document using toggleAll");
   yield front.toggleAll();
-  yield checkAllAnimationsStates(walker, front, "paused");
+  yield checkAnimationsStates(walker, front, ALL_ANIMATED_NODES, "paused");
 
   info("Play all animations in the test document using toggleAll");
   yield front.toggleAll();
-  yield checkAllAnimationsStates(walker, front, "running");
+  yield checkAnimationsStates(walker, front, ALL_ANIMATED_NODES, "running");
+
+  info("Pause a given list of animations only");
+  let players = [];
+  for (let selector of SOME_ANIMATED_NODES) {
+    let [player] = yield getPlayersFor(walker, front, selector);
+    players.push(player);
+  }
+  yield front.toggleSeveral(players, true);
+  yield checkAnimationsStates(walker, front, SOME_ANIMATED_NODES, "paused");
+  yield checkAnimationsStates(walker, front, [".multiple-animations"], "running");
+
+  info("Play the same list of animations");
+  yield front.toggleSeveral(players, false);
+  yield checkAnimationsStates(walker, front, ALL_ANIMATED_NODES, "running");
 
   yield closeDebuggerClient(client);
   gBrowser.removeCurrentTab();
 });
 
-function* checkAllAnimationsStates(walker, front, playState) {
-  info("Checking the playState of all the nodes that have infinite running animations");
+function* checkAnimationsStates(walker, front, selectors, playState) {
+  info("Checking the playState of all the nodes that have infinite running " +
+       "animations");
 
-  let selectors = [".simple-animation", ".multiple-animations", ".delayed-animation"];
   for (let selector of selectors) {
     info("Getting the AnimationPlayerFront for node " + selector);
-    let node = yield walker.querySelector(walker.rootNode, selector);
-    let [player] = yield front.getAnimationPlayersForNode(node);
-    yield player.ready;
-    let state = yield player.getCurrentState();
-    is(state.playState, playState,
-      "The playState of node " + selector + " is " + playState);
+    let [player] = yield getPlayersFor(walker, front, selector);
+    yield player.ready();
+    yield checkPlayState(player, selector, playState);
   }
 }
+
+function* getPlayersFor(walker, front, selector) {
+  let node = yield walker.querySelector(walker.rootNode, selector);
+  return front.getAnimationPlayersForNode(node);
+}
+
+function* checkPlayState(player, selector, expectedState) {
+  let state = yield player.getCurrentState();
+  is(state.playState, expectedState,
+    "The playState of node " + selector + " is " + expectedState);
+}