Bug 1232681 - Display script-generated animations correctly. r?pbro
MozReview-Commit-ID: 2pk7sxVTHTk
--- a/devtools/client/animationinspector/components/animation-time-block.js
+++ b/devtools/client/animationinspector/components/animation-time-block.js
@@ -143,18 +143,29 @@ AnimationTimeBlock.prototype = {
onClick: function(e) {
e.stopPropagation();
this.emit("selected", this.animation);
}
};
/**
* Get a formatted title for this animation. This will be either:
- * "some-name", "some-name : CSS Transition", or "some-name : CSS Animation",
- * depending if the server provides the type, and what type it is.
+ * "some-name", "some-name : CSS Transition", "some-name : CSS Animation",
+ * "some-name : Script Animation", or "Script Animation", depending
+ * if the server provides the type, what type it is and if the animation
+ * has a name
* @param {AnimationPlayerFront} animation
*/
function getFormattedAnimationTitle({state}) {
- // Older servers don't send the type.
- return state.type
- ? L10N.getFormatStr("timeline." + state.type + ".nameLabel", state.name)
- : state.name;
+ // Older servers don't send a type, and only know about
+ // CSSAnimations and CSSTransitions, so it's safe to use
+ // just the name.
+ if (!state.type) {
+ return state.name;
+ }
+
+ // Script-generated animations may not have a name.
+ if (state.type === "scriptanimation" && !state.name) {
+ return L10N.getStr("timeline.scriptanimation.unnamedLabel");
+ }
+
+ return L10N.getFormatStr(`timeline.${state.type}.nameLabel`, state.name);
}
--- a/devtools/client/animationinspector/test/browser.ini
+++ b/devtools/client/animationinspector/test/browser.ini
@@ -3,16 +3,17 @@ tags = devtools
subsuite = devtools
support-files =
doc_body_animation.html
doc_frame_script.js
doc_keyframes.html
doc_modify_playbackRate.html
doc_negative_animation.html
doc_simple_animation.html
+ doc_multiple_animation_types.html
head.js
[browser_animation_animated_properties_displayed.js]
[browser_animation_click_selects_animation.js]
[browser_animation_controller_exposes_document_currentTime.js]
skip-if = os == "linux" && !debug # Bug 1234567
[browser_animation_empty_on_invalid_nodes.js]
[browser_animation_keyframe_click_to_set_time.js]
--- a/devtools/client/animationinspector/test/browser_animation_playerWidgets_appear_on_panel_init.js
+++ b/devtools/client/animationinspector/test/browser_animation_playerWidgets_appear_on_panel_init.js
@@ -2,16 +2,46 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that player widgets are displayed right when the animation panel is
// initialized, if the selected node (<body> by default) is animated.
+const { ANIMATION_TYPES } = require("devtools/server/actors/animation");
+
add_task(function*() {
- yield addTab(TEST_URL_ROOT + "doc_body_animation.html");
+ yield new Promise(resolve => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.animations-api.core.enabled", true]
+ ]}, resolve);
+ });
+
+ yield addTab(TEST_URL_ROOT + "doc_multiple_animation_types.html");
let {panel} = yield openAnimationInspector();
- is(panel.animationsTimelineComponent.animations.length, 1,
- "One animation is handled by the timeline after init");
- assertAnimationsDisplayed(panel, 1, "One animation is displayed after init");
+ is(panel.animationsTimelineComponent.animations.length, 3,
+ "Three animations are handled by the timeline after init");
+ assertAnimationsDisplayed(panel, 3,
+ "Three animations are displayed after init");
+ is(
+ panel.animationsTimelineComponent
+ .animationsEl
+ .querySelectorAll(`.animation.${ANIMATION_TYPES.SCRIPT_ANIMATION}`)
+ .length,
+ 1,
+ "One script-generated animation is displayed");
+ is(
+ panel.animationsTimelineComponent
+ .animationsEl
+ .querySelectorAll(`.animation.${ANIMATION_TYPES.CSS_ANIMATION}`)
+ .length,
+ 1,
+ "One CSS animation is displayed");
+ is(
+ panel.animationsTimelineComponent
+ .animationsEl
+ .querySelectorAll(`.animation.${ANIMATION_TYPES.CSS_TRANSITION}`)
+ .length,
+ 1,
+ "One CSS transition is displayed");
});
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/doc_multiple_animation_types.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <style>
+ .ball {
+ width: 80px;
+ height: 80px;
+ border-radius: 50%;
+ }
+
+ .script-animation {
+ background: #f06;
+ }
+
+ .css-transition {
+ background: #006;
+ transition: background-color 10s;
+ }
+
+ .css-animation {
+ background: #a06;
+ animation: flash 10s forwards;
+ }
+
+ @keyframes flash {
+ 0% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+ }
+ </style>
+</head>
+<body>
+ <div class="ball script-animation"></div>
+ <div class="ball css-animation"></div>
+ <div class="ball css-transition"></div>
+
+ <script>
+ setTimeout(function(){
+ document.querySelector(".css-transition").style.backgroundColor = "yellow";
+ }, 0);
+
+ document.querySelector(".script-animation").animate([
+ { opacity: 1, offset: 0 },
+ { opacity: .1, offset: 1 }
+ ], {
+ duration: 10000,
+ fill: "forwards"
+ });
+ </script>
+</body>
+</html>
--- a/devtools/client/locales/en-US/animationinspector.properties
+++ b/devtools/client/locales/en-US/animationinspector.properties
@@ -103,14 +103,25 @@ timeline.timeGraduationLabel=%Sms
timeline.cssanimation.nameLabel=%S - CSS Animation
# LOCALIZATION NOTE (timeline.csstransition.nameLabel):
# This string is displayed in a tooltip of the animation panel that is shown
# when hovering over the name of a CSS Transition in the timeline UI.
# %S will be replaced by the name of the transition at run-time.
timeline.csstransition.nameLabel=%S - CSS Transition
+# LOCALIZATION NOTE (timeline.scriptanimation.nameLabel):
+# This string is displayed in a tooltip of the animation panel that is shown
+# when hovering over the name of a script-generated animation in the timeline UI.
+# %S will be replaced by the name of the animation at run-time.
+timeline.scriptanimation.nameLabel=%S - Script Animation
+
+# LOCALIZATION NOTE (timeline.scriptanimation.unnamedLabel):
+# This string is displayed in a tooltip of the animation panel that is shown
+# when hovering over an unnamed script-generated animation in the timeline UI.
+timeline.scriptanimation.unnamedLabel=Script Animation
+
# LOCALIZATION NOTE (timeline.unknown.nameLabel):
# This string is displayed in a tooltip of the animation panel that is shown
# when hovering over the name of an unknown animation type in the timeline UI.
# This can happen if devtools couldn't figure out the type of the animation.
# %S will be replaced by the name of the transition at run-time.
timeline.unknown.nameLabel=%S
--- a/devtools/client/themes/animationinspector.css
+++ b/devtools/client/themes/animationinspector.css
@@ -37,16 +37,21 @@
--timeline-background-color: var(--theme-contrast-background);
}
.animation.csstransition {
--timeline-border-color: var(--theme-highlight-bluegrey);
--timeline-background-color: var(--theme-highlight-blue);
}
+.animation.scriptanimation {
+ --timeline-border-color: var(--theme-highlight-green);
+ --timeline-background-color: var(--theme-graphs-green);
+}
+
html {
height: 100%;
}
body {
margin: 0;
padding: 0;
display : flex;
@@ -524,16 +529,20 @@ body {
.keyframes.cssanimation {
background-color: var(--theme-contrast-background);
}
.keyframes.csstransition {
background-color: var(--theme-highlight-blue);
}
+.keyframes.scriptanimation {
+ background-color: var(--theme-graphs-green);
+}
+
.keyframes .frame {
position: absolute;
top: 0;
width: 0;
height: 0;
background-color: inherit;
cursor: pointer;
}
--- a/devtools/server/actors/animation.js
+++ b/devtools/server/actors/animation.js
@@ -34,16 +34,17 @@ const {ActorClass, Actor, FrontClass, Fr
// Make sure the nodeActor type is know here.
const {NodeActor} = require("devtools/server/actors/inspector");
const events = require("sdk/event/core");
// Types of animations.
const ANIMATION_TYPES = {
CSS_ANIMATION: "cssanimation",
CSS_TRANSITION: "csstransition",
+ SCRIPT_ANIMATION: "scriptanimation",
UNKNOWN: "unknown"
};
exports.ANIMATION_TYPES = ANIMATION_TYPES;
/**
* The AnimationPlayerActor provides information about a given animation: its
* startTime, currentTime, current state, etc.
*
@@ -114,46 +115,55 @@ var AnimationPlayerActor = ActorClass({
// return its corresponding NodeActor ID too.
if (this.walker && this.walker.hasNode(this.node)) {
data.animationTargetNodeActorID = this.walker.getNode(this.node).actorID;
}
return data;
},
- isAnimation: function(player = this.player) {
+ isCssAnimation: function(player = this.player) {
return player instanceof this.window.CSSAnimation;
},
- isTransition: function(player = this.player) {
+ isCssTransition: function(player = this.player) {
return player instanceof this.window.CSSTransition;
},
+ isScriptAnimation: function(player = this.player) {
+ return player instanceof this.window.Animation && !(
+ player instanceof this.window.CSSAnimation ||
+ player instanceof this.window.CSSTransition
+ );
+ },
+
getType: function() {
- if (this.isAnimation()) {
+ if (this.isCssAnimation()) {
return ANIMATION_TYPES.CSS_ANIMATION;
- } else if (this.isTransition()) {
+ } else if (this.isCssTransition()) {
return ANIMATION_TYPES.CSS_TRANSITION;
+ } else if (this.isScriptAnimation()) {
+ return ANIMATION_TYPES.SCRIPT_ANIMATION;
}
return ANIMATION_TYPES.UNKNOWN;
},
/**
* Get the name of this animation. This can be either the animation.id
* property if it was set, or the keyframe rule name or the transition
* property.
* @return {String}
*/
getName: function() {
if (this.player.id) {
return this.player.id;
- } else if (this.isAnimation()) {
+ } else if (this.isCssAnimation()) {
return this.player.animationName;
- } else if (this.isTransition()) {
+ } else if (this.isCssTransition()) {
return this.player.transitionProperty;
}
return "";
},
/**
* Get the animation duration from this player, in milliseconds.
@@ -621,19 +631,19 @@ var AnimationsActor = exports.Animations
if (this.actors.find(a => a.player === player)) {
continue;
}
// If the added player has the same name and target node as a player we
// already have, it means it's a transition that's re-starting. So send
// a "removed" event for the one we already have.
let index = this.actors.findIndex(a => {
let isSameType = a.player.constructor === player.constructor;
- let isSameName = (a.isAnimation() &&
+ let isSameName = (a.isCssAnimation() &&
a.player.animationName === player.animationName) ||
- (a.isTransition() &&
+ (a.isCssTransition() &&
a.player.transitionProperty === player.transitionProperty);
let isSameNode = a.player.effect.target === player.effect.target;
return isSameType && isSameNode && isSameName;
});
if (index !== -1) {
eventData.push({
type: "removed",
--- a/devtools/server/tests/unit/test_animation_name.js
+++ b/devtools/server/tests/unit/test_animation_name.js
@@ -42,16 +42,21 @@ function run_test() {
// - expectedName {String} The expected name returned by
// AnimationPlayerActor.getName.
const TEST_DATA = [{
desc: "Animation with an id",
animation: new window.Animation(),
props: { id: "animation-id" },
expectedName: "animation-id"
}, {
+ desc: "Animation without an id",
+ animation: new window.Animation(),
+ props: {},
+ expectedName: ""
+ }, {
desc: "CSSTransition with an id",
animation: new window.CSSTransition(),
props: { id: "transition-with-id", transitionProperty: "width" },
expectedName: "transition-with-id"
}, {
desc: "CSSAnimation with an id",
animation: new window.CSSAnimation(),
props: { id: "animation-with-id", animationName: "move" },
--- a/devtools/server/tests/unit/test_animation_type.js
+++ b/devtools/server/tests/unit/test_animation_type.js
@@ -43,16 +43,20 @@ function run_test() {
desc: "Test CSSAnimation type",
animation: new window.CSSAnimation(),
expectedType: ANIMATION_TYPES.CSS_ANIMATION
}, {
desc: "Test CSSTransition type",
animation: new window.CSSTransition(),
expectedType: ANIMATION_TYPES.CSS_TRANSITION
}, {
+ desc: "Test ScriptAnimation type",
+ animation: new window.Animation(),
+ expectedType: ANIMATION_TYPES.SCRIPT_ANIMATION
+ }, {
desc: "Test unknown type",
animation: {effect: {target: getMockNode()}},
expectedType: ANIMATION_TYPES.UNKNOWN
}];
for (let { desc, animation, expectedType } of TEST_DATA) {
do_print(desc);
let actor = AnimationPlayerActor({}, animation);