Bug 1155651 - Return all players below a node in getAnimationPlayersForNode; r=miker

Now when the front requests the list of AnimationPlayers for a node,
all players for this node + the nodes in its subtree are returned.
Also the mutationobserver starts observing all mutations in the node
and its subtree.

--HG--
extra : rebase_source : c419bd2f397617317fa9c9d0b7fd22f07abc12cf
This commit is contained in:
Patrick Brosset 2015-04-21 09:22:30 +02:00
parent b2a2f73211
commit b2a9538957
8 changed files with 106 additions and 35 deletions

View File

@ -11,9 +11,6 @@ add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {controller, inspector} = yield openAnimationInspector();
is(controller.animationPlayers.length, 0,
"There are no AnimationPlayerFront objects at first");
info("Selecting an animated node");
// selectNode waits for the inspector-updated event before resolving, which
// means the controller.PLAYERS_UPDATED_EVENT event has been emitted before

View File

@ -10,15 +10,17 @@ add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {inspector, panel, controller} = yield openAnimationInspector();
info("Apply 2 finite animations to the test node");
info("Select the test node");
yield selectNode(".still", inspector);
info("Apply 2 finite animations to the test node and wait for the widgets to appear");
let onUiUpdated = panel.once(panel.UI_UPDATED_EVENT);
yield executeInContent("devtools:test:setAttribute", {
selector: ".still",
attributeName: "class",
attributeValue: "ball still multi-finite"
});
info("Select the test node");
yield selectNode(".still", inspector);
yield onUiUpdated;
is(controller.animationPlayers.length, 2, "2 animation players exist");

View File

@ -14,15 +14,17 @@ add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {inspector, panel} = yield openAnimationInspector();
info("Start an animation on the test node");
info("Select the test node");
yield selectNode(".still", inspector);
info("Start an animation on the test node and wait for the widget to appear");
let onUiUpdated = panel.once(panel.UI_UPDATED_EVENT);
yield executeInContent("devtools:test:setAttribute", {
selector: ".still",
attributeName: "class",
attributeValue: "ball still short"
});
info("Select the node");
yield selectNode(".still", inspector);
yield onUiUpdated;
info("Wait until the animation ends");
let widget = panel.playerWidgets[0];

View File

@ -31,7 +31,8 @@ add_task(function*() {
info("Faking an older server version by setting " +
"AnimationsController.hasSetCurrentTime to false");
yield selectNode("body", inspector);
// Selecting <div.still> to make sure no widgets are displayed in the panel.
yield selectNode(".still", inspector);
controller.hasSetCurrentTime = false;
info("Selecting the animated node again");
@ -46,13 +47,12 @@ add_task(function*() {
ok(container.children[0].classList.contains("toggle"),
"The first button is the play/pause button");
yield selectNode("body", inspector);
yield selectNode(".still", inspector);
controller.hasSetCurrentTime = true;
info("Faking an older server version by setting " +
"AnimationsController.hasSetPlaybackRate to false");
yield selectNode("body", inspector);
controller.hasSetPlaybackRate = false;
info("Selecting the animated node again");
@ -65,6 +65,6 @@ add_task(function*() {
ok(!container.querySelector("select"),
"The playback rate select does not exist");
yield selectNode("body", inspector);
yield selectNode(".still", inspector);
controller.hasSetPlaybackRate = true;
});

View File

@ -10,6 +10,9 @@ add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {inspector, panel} = yield openAnimationInspector();
info("Select the non-animated test node");
yield selectNode(".still", inspector);
ok(!panel.toggleAllButtonEl.classList.contains("paused"),
"The toggle button is in its running state by default");

View File

@ -486,13 +486,16 @@ let AnimationsActor = exports.AnimationsActor = ActorClass({
},
/**
* Retrieve the list of AnimationPlayerActor actors corresponding to
* currently running animations for a given node.
* @param {NodeActor} nodeActor The NodeActor type is defined in
* Retrieve the list of AnimationPlayerActor actors for currently running
* animations on a node and its descendants.
* @param {NodeActor} nodeActor The NodeActor as defined in
* /toolkit/devtools/server/actors/inspector
*/
getAnimationPlayersForNode: method(function(nodeActor) {
let animations = nodeActor.rawNode.getAnimations();
let animations = [
...nodeActor.rawNode.getAnimations(),
...this.getAllAnimations(nodeActor.rawNode)
];
// No care is taken here to destroy the previously stored actors because it
// is assumed that the client is responsible for lifetimes of actors.
@ -511,7 +514,10 @@ let AnimationsActor = exports.AnimationsActor = ActorClass({
this.stopAnimationPlayerUpdates();
let win = nodeActor.rawNode.ownerDocument.defaultView;
this.observer = new win.MutationObserver(this.onAnimationMutation);
this.observer.observe(nodeActor.rawNode, {animations: true});
this.observer.observe(nodeActor.rawNode, {
animations: true,
subtree: true
});
return this.actors;
}, {
@ -596,23 +602,31 @@ let AnimationsActor = exports.AnimationsActor = ActorClass({
}),
/**
* Iterates through all nodes in all of the tabActor's window documents and
* finds all existing animation players.
* This is currently used to allow playing/pausing all animations at once
* until the WebAnimations API provides a way to play/pause via the document
* timeline (alternatively, when bug 1123524 is fixed, we will be able to
* only iterate once and then listen for changes).
* Iterates through all nodes below a given rootNode (optionally also in
* nested frames) and finds all existing animation players.
* @param {DOMNode} rootNode The root node to start iterating at. Animation
* players will *not* be reported for this node.
* @param {Boolean} traverseFrames Whether we should iterate through nested
* frames too.
* @return {Array} An array of AnimationPlayer objects.
*/
getAllAnimationPlayers: function() {
getAllAnimations: function(rootNode, traverseFrames) {
let animations = [];
// These loops shouldn't be as bad as they look.
// Typically, there will be very few windows, and getElementsByTagName is
// really fast even on large DOM trees.
for (let window of this.tabActor.windows) {
let root = window.document.body || window.document;
for (let element of root.getElementsByTagNameNS("*", "*")) {
animations = [...animations, ...element.getAnimations()];
// Typically, there will be very few nested frames, and getElementsByTagName
// is really fast even on large DOM trees.
for (let element of rootNode.getElementsByTagNameNS("*", "*")) {
if (traverseFrames && element.contentWindow) {
animations = [
...animations,
...this.getAllAnimations(element.contentWindow.document, traverseFrames)
];
} else {
animations = [
...animations,
...element.getAnimations()
];
}
}
@ -636,7 +650,10 @@ let AnimationsActor = exports.AnimationsActor = ActorClass({
*/
pauseAll: method(function() {
let readyPromises = [];
for (let player of this.getAllAnimationPlayers()) {
// Until the WebAnimations API provides a way to play/pause via the document
// timeline, we have to iterate through the whole DOM to find all players.
for (let player of
this.getAllAnimations(this.tabActor.window.document, true)) {
player.pause();
readyPromises.push(player.ready);
}
@ -653,7 +670,10 @@ let AnimationsActor = exports.AnimationsActor = ActorClass({
*/
playAll: method(function() {
let readyPromises = [];
for (let player of this.getAllAnimationPlayers()) {
// Until the WebAnimations API provides a way to play/pause via the document
// timeline, we have to iterate through the whole DOM to find all players.
for (let player of
this.getAllAnimations(this.tabActor.window.document, true)) {
player.play();
readyPromises.push(player.ready);
}

View File

@ -31,6 +31,7 @@ support-files =
[browser_animation_actors_13.js]
[browser_animation_actors_14.js]
[browser_animation_actors_15.js]
[browser_animation_actors_16.js]
[browser_canvasframe_helper_01.js]
[browser_canvasframe_helper_02.js]
[browser_canvasframe_helper_03.js]

View File

@ -0,0 +1,46 @@
/* 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 retrieve all animations inside a node's
// subtree (but not going into iframes).
const {AnimationsFront} = require("devtools/server/actors/animation");
const {InspectorFront} = require("devtools/server/actors/inspector");
const URL = MAIN_DOMAIN + "animation.html";
add_task(function*() {
info("Creating a test document with 2 iframes containing animated nodes");
let doc = yield addTab("data:text/html;charset=utf-8," +
"<iframe id='iframe' src='" + URL + "'></iframe>");
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("Try retrieving all animations from the root doc's <body> node");
let rootBody = yield walker.querySelector(walker.rootNode, "body");
let players = yield front.getAnimationPlayersForNode(rootBody);
is(players.length, 0, "The node has no animation players");
info("Retrieve all animations from the iframe's <body> node");
let iframe = yield walker.querySelector(walker.rootNode, "#iframe");
let {nodes} = yield walker.children(iframe);
let frameBody = yield walker.querySelector(nodes[0], "body");
players = yield front.getAnimationPlayersForNode(frameBody);
// Testing for a hard-coded number of animations here would intermittently
// fail depending on how fast or slow the test is (indeed, the test page
// contains short transitions, and delayed animations). So just make sure we
// at least have the infinitely running animations.
ok(players.length >= 4, "All subtree animations were retrieved");
yield closeDebuggerClient(client);
gBrowser.removeCurrentTab();
});