Bug 1458268 - Avoid empty inspector when inspecting css transition;r=daisuke
MozReview-Commit-ID: 2uUkAmYrr4V
--- a/devtools/client/inspector/animation/components/KeyframesProgressBar.js
+++ b/devtools/client/inspector/animation/components/KeyframesProgressBar.js
@@ -36,42 +36,50 @@ class KeyframesProgressBar extends PureC
const { addAnimationsCurrentTimeListener } = this.props;
this.element = ReactDOM.findDOMNode(this);
this.setupAnimation(this.props);
addAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
}
componentWillReceiveProps(nextProps) {
- const { getAnimationsCurrentTime } = nextProps;
+ const { animation, getAnimationsCurrentTime, timeScale } = nextProps;
this.setupAnimation(nextProps);
- this.onCurrentTimeUpdated(getAnimationsCurrentTime());
+ this.updateOffset(getAnimationsCurrentTime(), animation, timeScale);
}
componentWillUnmount() {
const { removeAnimationsCurrentTimeListener } = this.props;
removeAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
this.element = null;
this.simulatedAnimation = null;
}
onCurrentTimeUpdated(currentTime) {
- const {
- animation,
- timeScale,
- } = this.props;
+ const { animation, timeScale } = this.props;
+ this.updateOffset(currentTime, animation, timeScale);
+ }
+
+ updateOffset(currentTime, animation, timeScale) {
const {
playbackRate,
previousStartTime = 0,
} = animation.state;
- this.simulatedAnimation.currentTime =
+ const time =
(timeScale.minStartTime + currentTime - previousStartTime) * playbackRate;
+ if (isNaN(time)) {
+ // Setting an invalid currentTime will throw so bail out if time is not a number for
+ // any reason.
+ return;
+ }
+
+ this.simulatedAnimation.currentTime = time;
const offset = this.element.offsetWidth *
this.simulatedAnimation.effect.getComputedTiming().progress;
this.setState({ offset });
}
setupAnimation(props) {
const {
--- a/devtools/client/inspector/animation/test/browser.ini
+++ b/devtools/client/inspector/animation/test/browser.ini
@@ -22,16 +22,17 @@ support-files =
[browser_animation_animation-detail_close-button.js]
[browser_animation_animation-detail_title.js]
[browser_animation_animation-detail_visibility.js]
[browser_animation_animation-list.js]
[browser_animation_animation-target.js]
[browser_animation_animation-target_highlight.js]
[browser_animation_animation-target_select.js]
[browser_animation_animation-timeline-tick.js]
+[browser_animation_css-transition-with-playstate-idle.js]
[browser_animation_current-time-label.js]
[browser_animation_current-time-scrubber.js]
[browser_animation_empty_on_invalid_nodes.js]
[browser_animation_inspector_exists.js]
[browser_animation_keyframes-graph_computed-value-path.js]
[browser_animation_keyframes-graph_computed-value-path_easing-hint.js]
[browser_animation_keyframes-graph_keyframe-marker.js]
[browser_animation_keyframes-progress-bar.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_css-transition-with-playstate-idle.js
@@ -0,0 +1,85 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that animation inspector does not fail when rendering an animation that
+// transitions from the playState "idle".
+
+const PAGE_URL = `data:text/html;charset=utf-8,
+<!DOCTYPE html>
+<html>
+<head>
+ <style type="text/css">
+ div {
+ opacity: 0;
+ transition-duration: 5000ms;
+ transition-property: opacity;
+ }
+
+ div.visible {
+ opacity: 1;
+ }
+ </style>
+</head>
+<body>
+ <div>test</div>
+</body>
+</html>`;
+
+add_task(async function() {
+ const tab = await addTab(PAGE_URL);
+ const { animationInspector, panel } = await openAnimationInspector();
+
+ info("Toggle the visible class to start the animation");
+ await toggleVisibleClass(tab);
+
+ info("Wait until the scrubber is displayed");
+ await waitUntil(() => panel.querySelector(".current-time-scrubber"));
+
+ const scrubberEl = panel.querySelector(".current-time-scrubber");
+
+ info("Wait until animations are paused");
+ await waitUntilAnimationsPaused(animationInspector);
+
+ // Check the initial position of the scrubber to detect the animation.
+ const scrubberX = scrubberEl.getBoundingClientRect().x;
+
+ info("Toggle the visible class to start the animation");
+ await toggleVisibleClass(tab);
+
+ info("scrubberX after: " + scrubberEl.getBoundingClientRect().x);
+
+ info("Wait until the scrubber starts moving");
+ await waitUntil(() => scrubberEl.getBoundingClientRect().x != scrubberX);
+
+ info("Wait until animations are paused");
+ await waitUntilAnimationsPaused(animationInspector);
+
+ // Query the scrubber element again to check that the UI is still rendered.
+ ok(!!panel.querySelector(".current-time-scrubber"),
+ "The scrubber element is still rendered in the animation inspector panel");
+
+ info("Wait for the keyframes graph to be updated before ending the test.");
+ await waitForAnimationDetail(animationInspector);
+});
+
+/**
+ * Local helper to toggle the "visible" class on the element with a transition defined.
+ */
+async function toggleVisibleClass(tab) {
+ await ContentTask.spawn(tab.linkedBrowser, {}, async function() {
+ let win = content.wrappedJSObject;
+ win.document.querySelector("div").classList.toggle("visible");
+ });
+}
+
+async function waitUntilAnimationsPaused(animationInspector) {
+ await waitUntil(() => {
+ const animations = animationInspector.state.animations;
+ return animations.every(animation => {
+ const state = animation.state.playState;
+ return state === "paused" || state === "finished";
+ });
+ });
+}