--- a/devtools/client/animationinspector/test/browser.ini
+++ b/devtools/client/animationinspector/test/browser.ini
@@ -1,35 +1,41 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
doc_body_animation.html
+ doc_delayed_starttime_animations.html
doc_end_delay.html
doc_frame_script.js
doc_keyframes.html
doc_modify_playbackRate.html
doc_negative_animation.html
doc_pseudo_elements.html
doc_script_animation.html
doc_short_duration_animation.html
doc_simple_animation.html
doc_multiple_animation_types.html
+ doc_multiple_property_types.html
doc_timing_combination_animation.html
head.js
!/devtools/client/commandline/test/helpers.js
!/devtools/client/framework/test/shared-head.js
!/devtools/client/inspector/test/head.js
!/devtools/client/inspector/test/shared-head.js
!/devtools/client/shared/test/test-actor-registry.js
!/devtools/client/shared/test/test-actor.js
[browser_animation_animated_properties_displayed.js]
+[browser_animation_animated_properties_for_delayed_starttime_animations.js]
+[browser_animation_animated_properties_path.js]
+[browser_animation_animated_properties_progress_indicator.js]
[browser_animation_click_selects_animation.js]
[browser_animation_controller_exposes_document_currentTime.js]
+[browser_animation_detail_displayed.js]
skip-if = os == "linux" && !debug # Bug 1234567
[browser_animation_empty_on_invalid_nodes.js]
[browser_animation_keyframe_markers.js]
[browser_animation_mutations_with_same_names.js]
[browser_animation_panel_exists.js]
[browser_animation_participate_in_inspector_update.js]
[browser_animation_playerFronts_are_refreshed.js]
[browser_animation_playerWidgets_appear_on_panel_init.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/browser_animation_animated_properties_for_delayed_starttime_animations.js
@@ -0,0 +1,38 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for animations that have different starting time.
+// We should check progress indicator working well even if the start time is not zero.
+
+add_task(function* () {
+ yield addTab(URL_ROOT + "doc_delayed_starttime_animations.html");
+ const { panel } = yield openAnimationInspector();
+ yield setStyle(null, panel, "animation", "anim 100s", "#target2");
+ yield setStyle(null, panel, "animation", "anim 100s", "#target3");
+ yield setStyle(null, panel, "animation", "anim 100s", "#target4");
+ yield setStyle(null, panel, "animation", "anim 100s", "#target5");
+
+ yield clickOnAnimation(panel, 1);
+ const timelineComponent = panel.animationsTimelineComponent;
+ const detailsComponent = timelineComponent.details;
+ const progressIndicatorEl = detailsComponent.progressIndicatorEl;
+ const startTime = detailsComponent.animation.state.previousStartTime;
+ detailsComponent.indicateProgress(0);
+ is(progressIndicatorEl.style.left, "0%",
+ "The progress indicator position should be 0% at 0ms");
+ detailsComponent.indicateProgress(startTime);
+ is(progressIndicatorEl.style.left, "0%",
+ "The progress indicator position should be 0% at start time");
+ detailsComponent.indicateProgress(startTime + 50 * 1000);
+ is(progressIndicatorEl.style.left, "50%",
+ "The progress indicator position should be 50% at half time of animation");
+ detailsComponent.indicateProgress(startTime + 99 * 1000);
+ is(progressIndicatorEl.style.left, "99%",
+ "The progress indicator position should be 99% at 99s");
+ detailsComponent.indicateProgress(startTime + 100 * 1000);
+ is(progressIndicatorEl.style.left, "0%",
+ "The progress indicator position should be 0% at end of animation");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/browser_animation_animated_properties_path.js
@@ -0,0 +1,306 @@
+/* vim: set 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 animated properties's graph.
+// The graph constructs from SVG, also uses path (for shape), linearGradient,
+// stop (for color) element and so on.
+// We test followings.
+// 1. class name - which represents the animation type.
+// 2. coordinates of the path - x is time, y is graph y value which should be 0 - 1.
+// The path of animation types 'color', 'coord', 'opacity' or 'discrete' are created by
+// createPathSegments. Other types are created by createKeyframesPathSegments.
+// 3. color - animation type 'color' has linearGradient element.
+
+requestLongerTimeout(5);
+
+const TEST_CASES = [
+ {
+ "background-color": {
+ expectedClass: "color",
+ expectedValues: [
+ { x: 0, y: 1, color: "rgb(255, 0, 0)" },
+ { x: 1000, y: 1, color: "rgb(0, 255, 0)" }
+ ]
+ },
+ "font-size": {
+ expectedClass: "length",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 1000, y: 1 },
+ ]
+ },
+ "margin-left": {
+ expectedClass: "coord",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 1000, y: 1 },
+ ]
+ },
+ "opacity": {
+ expectedClass: "opacity",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 1000, y: 1 },
+ ]
+ },
+ "text-align": {
+ expectedClass: "discrete",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 499.999, y: 0 },
+ { x: 500, y: 1 },
+ { x: 1000, y: 1 },
+ ]
+ },
+ "transform": {
+ expectedClass: "transform",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 1000, y: 1 },
+ ]
+ }
+ },
+
+ {
+ "background-color": {
+ expectedClass: "color",
+ expectedValues: [
+ { x: 0, y: 1, color: "rgb(0, 255, 0)" },
+ { x: 1000, y: 1, color: "rgb(255, 0, 0)" }
+ ]
+ },
+ "font-size": {
+ expectedClass: "length",
+ expectedValues: [
+ { x: 0, y: 1 },
+ { x: 1000, y: 0 },
+ ]
+ },
+ "margin-left": {
+ expectedClass: "coord",
+ expectedValues: [
+ { x: 0, y: 1 },
+ { x: 1000, y: 0 },
+ ]
+ },
+ "opacity": {
+ expectedClass: "opacity",
+ expectedValues: [
+ { x: 0, y: 1 },
+ { x: 1000, y: 0 },
+ ]
+ },
+ "text-align": {
+ expectedClass: "discrete",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 499.999, y: 0 },
+ { x: 500, y: 1 },
+ { x: 1000, y: 1 },
+ ]
+ },
+ "transform": {
+ expectedClass: "transform",
+ expectedValues: [
+ { x: 0, y: 1 },
+ { x: 1000, y: 0 },
+ ]
+ }
+ },
+
+ {
+ "background-color": {
+ expectedClass: "color",
+ expectedValues: [
+ { x: 0, y: 1, color: "rgb(255, 0, 0)" },
+ { x: 500, y: 1, color: "rgb(0, 0, 255)" },
+ { x: 1000, y: 1, color: "rgb(0, 255, 0)" }
+ ]
+ },
+ "font-size": {
+ expectedClass: "length",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 500, y: 1 },
+ { x: 1000, y: 0 },
+ ]
+ },
+ "margin-left": {
+ expectedClass: "coord",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 500, y: 1 },
+ { x: 1000, y: 0 },
+ ]
+ },
+ "opacity": {
+ expectedClass: "opacity",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 500, y: 1 },
+ { x: 1000, y: 0 },
+ ]
+ },
+ "text-align": {
+ expectedClass: "discrete",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 249.999, y: 0 },
+ { x: 250, y: 1 },
+ { x: 749.999, y: 1 },
+ { x: 750, y: 0 },
+ { x: 1000, y: 0 },
+ ]
+ },
+ "transform": {
+ expectedClass: "transform",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 500, y: 1 },
+ { x: 1000, y: 0 },
+ ]
+ }
+ },
+ {
+ "background-color": {
+ expectedClass: "color",
+ expectedValues: [
+ { x: 0, y: 1, color: "rgb(255, 0, 0)" },
+ { x: 499.999, y: 1, color: "rgb(255, 0, 0)" },
+ { x: 500, y: 1, color: "rgb(128, 128, 0)" },
+ { x: 999.999, y: 1, color: "rgb(128, 128, 0)" },
+ { x: 1000, y: 1, color: "rgb(0, 255, 0)" }
+ ]
+ },
+ "font-size": {
+ expectedClass: "length",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 500, y: 0 },
+ { x: 500, y: 0.5 },
+ { x: 1000, y: 0.5 },
+ { x: 1000, y: 1 },
+ ]
+ },
+ "margin-left": {
+ expectedClass: "coord",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 499.999, y: 0 },
+ { x: 500, y: 0.5 },
+ { x: 999.999, y: 0.5 },
+ { x: 1000, y: 1 },
+ ]
+ },
+ "opacity": {
+ expectedClass: "opacity",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 499.999, y: 0 },
+ { x: 500, y: 0.5 },
+ { x: 999.999, y: 0.5 },
+ { x: 1000, y: 1 },
+ ]
+ },
+ "text-align": {
+ expectedClass: "discrete",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 499.999, y: 0 },
+ { x: 500, y: 1 },
+ { x: 1000, y: 1 },
+ ]
+ },
+ "transform": {
+ expectedClass: "transform",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 500, y: 0 },
+ { x: 500, y: 0.5 },
+ { x: 1000, y: 0.5 },
+ { x: 1000, y: 1 },
+ ]
+ }
+ },
+ {
+ "opacity": {
+ expectedClass: "opacity",
+ expectedValues: [
+ { x: 0, y: 0 },
+ { x: 500, y: 0.5 },
+ { x: 1000, y: 1 },
+ ]
+ }
+ }
+];
+
+add_task(function* () {
+ yield addTab(URL_ROOT + "doc_multiple_property_types.html");
+ const {panel} = yield openAnimationInspector();
+ const timelineComponent = panel.animationsTimelineComponent;
+ const detailEl = timelineComponent.details.containerEl;
+
+ for (let i = 0; i < TEST_CASES.length; i++) {
+ info(`Click to select the animation[${ i }]`);
+ yield clickOnAnimation(panel, i);
+ const timeBlock = timelineComponent.timeBlocks[0];
+ const state = timeBlock.animation.state;
+ const properties = TEST_CASES[i];
+ for (let property in properties) {
+ const testcase = properties[property];
+ info(`Test path of ${ property }`);
+ const className = testcase.expectedClass;
+ const pathEl = detailEl.querySelector(`path.${ className }`);
+ ok(pathEl, `Path element with class '${ className }' should exis`);
+ checkPathSegments(pathEl, state, testcase.expectedValues);
+ }
+ }
+});
+
+function checkPathSegments(pathEl, { duration }, expectedValues) {
+ const pathSegList = pathEl.pathSegList;
+
+ const firstPathSeg = pathSegList.getItem(0);
+ is(firstPathSeg.x, 0, "The x of first segment should be 0");
+ is(firstPathSeg.y, 0, "The y of first segment should be 0");
+
+ expectedValues.forEach(expectedValue => {
+ ok(hasSegment(pathSegList, expectedValue.x, expectedValue.y),
+ `The path segment of x ${ expectedValue.x }, y ${ expectedValue.y } should exist`);
+
+ if (expectedValue.color) {
+ checkColor(pathEl.closest("svg"), expectedValue.x / duration, expectedValue.color);
+ }
+ });
+
+ const closePathSeg = pathSegList.getItem(pathSegList.numberOfItems - 1);
+ is(closePathSeg.pathSegType, closePathSeg.PATHSEG_CLOSEPATH,
+ "The actual last segment should be close path");
+}
+
+function checkColor(svgEl, offset, color) {
+ const stopEl = findStopElement(svgEl, offset);
+ ok(stopEl, `stop element at offset ${ offset } should exist`);
+ is(stopEl.getAttribute("stop-color"), color,
+ `stop-color of stop element at offset ${ offset } should be ${ color }`);
+}
+
+function hasSegment(pathSegList, x, y) {
+ for (let i = 1; i < pathSegList.numberOfItems - 1; i++) {
+ const pathSeg = pathSegList.getItem(i);
+ if (parseFloat(pathSeg.x.toFixed(3)) === x &&
+ parseFloat(pathSeg.y.toFixed(6)) === y) {
+ return true;
+ }
+ }
+ return false;
+}
+
+function findStopElement(svgEl, offset) {
+ return [...svgEl.querySelectorAll("stop")].find(stopEl => {
+ return stopEl.getAttribute("offset") == offset;
+ });
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/browser_animation_animated_properties_progress_indicator.js
@@ -0,0 +1,86 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test progress indicator in animated properties.
+// Since this indicator works with the timeline, after selecting each animation,
+// click the timeline header to change the current time and check the change.
+
+add_task(function* () {
+ yield addTab(URL_ROOT + "doc_multiple_property_types.html");
+ const { panel } = yield openAnimationInspector();
+ const timelineComponent = panel.animationsTimelineComponent;
+ const detailsComponent = timelineComponent.details;
+
+ info("Click to select the animation");
+ yield clickOnAnimation(panel, 0);
+ let progressIndicatorEl = detailsComponent.progressIndicatorEl;
+ ok(progressIndicatorEl, "The progress indicator should be exist");
+ yield clickOnTimelineHeader(panel, 0);
+ is(progressIndicatorEl.style.left, "0%",
+ "The left style of progress indicator element should be 0% at 0ms");
+ yield clickOnTimelineHeader(panel, 0.5);
+ approximate(progressIndicatorEl.style.left, "50%",
+ "The left style of progress indicator element should be "
+ + "approximately 50% at 500ms");
+ yield clickOnTimelineHeader(panel, 1);
+ is(progressIndicatorEl.style.left, "100%",
+ "The left style of progress indicator element should be 100% at 1000ms");
+
+ info("Click to select the steps animation");
+ yield clickOnAnimation(panel, 4);
+ // Re-get progressIndicatorEl since this element re-create
+ // in case of select the animation.
+ progressIndicatorEl = detailsComponent.progressIndicatorEl;
+ // Use indicateProgess directly from here since
+ // MouseEvent.clientX may not be able to indicate finely
+ // in case of the width of header element * xPositionRate has a fraction.
+ detailsComponent.indicateProgress(499);
+ is(progressIndicatorEl.style.left, "0%",
+ "The left style of progress indicator element should be 0% at 0ms");
+ detailsComponent.indicateProgress(499);
+ is(progressIndicatorEl.style.left, "0%",
+ "The left style of progress indicator element should be 0% at 499ms");
+ detailsComponent.indicateProgress(500);
+ is(progressIndicatorEl.style.left, "50%",
+ "The left style of progress indicator element should be 50% at 500ms");
+ detailsComponent.indicateProgress(999);
+ is(progressIndicatorEl.style.left, "50%",
+ "The left style of progress indicator element should be 50% at 999ms");
+ yield clickOnTimelineHeader(panel, 1);
+ is(progressIndicatorEl.style.left, "100%",
+ "The left style of progress indicator element should be 100% at 1000ms");
+
+ info("Change the playback rate");
+ yield changeTimelinePlaybackRate(panel, 2);
+ yield clickOnAnimation(panel, 0);
+ progressIndicatorEl = detailsComponent.progressIndicatorEl;
+ yield clickOnTimelineHeader(panel, 0);
+ is(progressIndicatorEl.style.left, "0%",
+ "The left style of progress indicator element should be 0% "
+ + "at 0ms and playback rate 2");
+ detailsComponent.indicateProgress(250);
+ is(progressIndicatorEl.style.left, "50%",
+ "The left style of progress indicator element should be 50% "
+ + "at 250ms and playback rate 2");
+ detailsComponent.indicateProgress(500);
+ is(progressIndicatorEl.style.left, "100%",
+ "The left style of progress indicator element should be 100% "
+ + "at 500ms and playback rate 2");
+
+ info("Check the progress indicator position after select another animation");
+ yield changeTimelinePlaybackRate(panel, 1);
+ yield clickOnTimelineHeader(panel, 0.5);
+ const originalIndicatorPosition = progressIndicatorEl.style.left;
+ yield clickOnAnimation(panel, 1);
+ is(progressIndicatorEl.style.left, originalIndicatorPosition,
+ "The animation time should be continued even if another animation selects");
+});
+
+function approximate(percentageString1, percentageString2, message) {
+ const val1 = Math.round(parseFloat(percentageString1));
+ const val2 = Math.round(parseFloat(percentageString2));
+ is(val1, val2, message);
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/browser_animation_detail_displayed.js
@@ -0,0 +1,41 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the behavior of animation-detail container.
+// We test following cases.
+// 1. Existance of animation-detail element.
+// 2. Hidden at first if multiple animations were displayed.
+// 3. Display after click on an animation.
+// 4. Display from first time if displayed animation is only one.
+
+requestLongerTimeout(5);
+
+add_task(function* () {
+ yield addTab(URL_ROOT + "doc_multiple_property_types.html");
+ const { panel, inspector } = yield openAnimationInspector();
+ const timelineComponent = panel.animationsTimelineComponent;
+ const animationDetailEl =
+ timelineComponent.rootWrapperEl.querySelector(".animation-detail");
+
+ // 1. Existance of animation-detail element.
+ ok(animationDetailEl, "The animation-detail element should exist");
+
+ // 2. Hidden at first if multiple animations were displayed.
+ const win = animationDetailEl.ownerDocument.defaultView;
+ is(win.getComputedStyle(animationDetailEl).display, "none",
+ "The animation-detail element should be hidden at first "
+ + "if multiple animations were displayed");
+
+ // 3. Display after click on an animation.
+ yield clickOnAnimation(panel, 0);
+ isnot(win.getComputedStyle(animationDetailEl).display, "none",
+ "The animation-detail element should be displayed after clicked on an animation");
+
+ // 4. Display from first time if displayed animation is only one.
+ yield selectNodeAndWaitForAnimations("#target1", inspector);
+ ok(animationDetailEl.querySelector(".property"),
+ "The property in animation-detail element should be displayed");
+});
--- a/devtools/client/animationinspector/test/browser_animation_timeline_ui.js
+++ b/devtools/client/animationinspector/test/browser_animation_timeline_ui.js
@@ -34,10 +34,17 @@ add_task(function* () {
"The animated node target element is in the DOM");
ok(animationEl.querySelector(".time-block"),
"The timeline element is in the DOM");
is(animationEl.querySelector(".name").textContent,
animation.state.name,
"The name on the timeline is correct");
ok(animationEl.querySelector("svg path"),
"The timeline has svg and path element as summary graph");
+
+ const expectedBackgroundColor =
+ i % 2 === 0 ? "rgba(128, 128, 128, 0.03)" : "rgba(0, 0, 0, 0)";
+ const backgroundColor =
+ animationEl.ownerDocument.defaultView.getComputedStyle(animationEl).backgroundColor;
+ is(backgroundColor, expectedBackgroundColor,
+ "The background-color shoud be changed to alternate");
}
});
--- a/devtools/client/animationinspector/test/browser_animation_ui_updates_when_animation_data_changes.js
+++ b/devtools/client/animationinspector/test/browser_animation_ui_updates_when_animation_data_changes.js
@@ -12,43 +12,25 @@ requestLongerTimeout(2);
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel, controller, inspector} = yield openAnimationInspector();
info("Select the test node");
yield selectNodeAndWaitForAnimations(".animated", inspector);
let animation = controller.animationPlayers[0];
- yield setStyle(animation, panel, "animationDuration", "5.5s");
- yield setStyle(animation, panel, "animationIterationCount", "300");
- yield setStyle(animation, panel, "animationDelay", "45s");
+ yield setStyle(animation, panel, "animationDuration", "5.5s", ".animated");
+ yield setStyle(animation, panel, "animationIterationCount", "300", ".animated");
+ yield setStyle(animation, panel, "animationDelay", "45s", ".animated");
let animationsEl = panel.animationsTimelineComponent.animationsEl;
let timeBlockEl = animationsEl.querySelector(".time-block");
// 45s delay + (300 * 5.5)s duration
let expectedTotalDuration = 1695 * 1000;
// XXX: the nb and size of each iteration cannot be tested easily (displayed
// using a linear-gradient background and capped at 2px wide). They should
// be tested in bug 1173761.
let delayWidth = parseFloat(timeBlockEl.querySelector(".delay").style.width);
is(Math.round(delayWidth * expectedTotalDuration / 100), 45 * 1000,
"The timeline has the right delay");
});
-
-function* setStyle(animation, panel, name, value) {
- info("Change the animation style via the content DOM. Setting " +
- name + " to " + value);
-
- let onAnimationChanged = once(animation, "changed");
- yield executeInContent("devtools:test:setStyle", {
- selector: ".animated",
- propertyName: name,
- propertyValue: value
- });
- yield onAnimationChanged;
- yield waitForAnimationSelecting(panel);
-
- // Also wait for the target node previews to be loaded if the panel got
- // refreshed as a result of this animation mutation.
- yield waitForAllAnimationTargets(panel);
-}
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/doc_delayed_starttime_animations.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <style>
+ div {
+ width: 100px;
+ height: 100px;
+ border: 1px solid gray;
+ }
+
+ #target1 {
+ animation: anim 100s;
+ }
+
+ @keyframes anim {
+ from {
+ transform: translate(-50%, 100%);
+ }
+ to {
+ transform: translateX(-50%);
+ }
+ }
+ </style>
+ </head>
+ <body>
+ <div id="target1"></div>
+ <div id="target2"></div>
+ <div id="target3"></div>
+ <div id="target4"></div>
+ <div id="target5"></div>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/doc_multiple_property_types.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <style>
+ div {
+ width: 50px;
+ height: 50px;
+ }
+ </style>
+</head>
+<body>
+ <div id=target1>1</div>
+ <div id=target2>2</div>
+ <div id=target3>3</div>
+ <div id=target4>4</div>
+ <div id=target5>5</div>
+
+ <script>
+ "use strict";
+
+ const timing = {
+ duration: 1000,
+ fill: "forwards"
+ };
+
+ document.querySelector("#target1").animate(
+ [{ backgroundColor: "red",
+ fontSize: "10px",
+ marginLeft: "0px",
+ opacity: 0,
+ textAlign: "right",
+ transform: "translate(0px)" },
+ { backgroundColor: "lime",
+ fontSize: "20px",
+ marginLeft: "100px",
+ opacity: 1,
+ textAlign: "center",
+ transform: "translate(100px)" }], timing).pause();
+
+ document.querySelector("#target2").animate(
+ [{ backgroundColor: "lime",
+ fontSize: "20px",
+ marginLeft: "100px",
+ opacity: 1,
+ textAlign: "center",
+ transform: "translate(100px)" },
+ { backgroundColor: "red",
+ fontSize: "10px",
+ marginLeft: "0px",
+ opacity: 0,
+ textAlign: "right",
+ transform: "translate(0px)" }], timing).pause();
+
+ document.querySelector("#target3").animate(
+ [{ backgroundColor: "red",
+ fontSize: "10px",
+ marginLeft: "0px",
+ opacity: 0,
+ textAlign: "right",
+ transform: "translate(0px)" },
+ { backgroundColor: "blue",
+ fontSize: "20px",
+ marginLeft: "100px",
+ opacity: 1,
+ textAlign: "center",
+ transform: "translate(100px)" },
+ { backgroundColor: "lime",
+ fontSize: "10px",
+ marginLeft: "0px",
+ opacity: 0,
+ textAlign: "right",
+ transform: "translate(0px)" }], timing).pause();
+
+ document.querySelector("#target4").animate(
+ [{ backgroundColor: "red",
+ fontSize: "10px",
+ marginLeft: "0px",
+ opacity: 0,
+ textAlign: "right",
+ transform: "translate(0px)",
+ easing: "steps(2)" },
+ { backgroundColor: "lime",
+ fontSize: "20px",
+ marginLeft: "100px",
+ opacity: 1,
+ textAlign: "center",
+ transform: "translate(100px)" }], timing).pause();
+
+ timing.easing = "steps(2)";
+ document.querySelector("#target5").animate(
+ [{ opacity: 0 }, { opacity: 1 }], timing).pause();
+ </script>
+</body>
+</html>
--- a/devtools/client/animationinspector/test/head.js
+++ b/devtools/client/animationinspector/test/head.js
@@ -364,16 +364,39 @@ function* changeTimelinePlaybackRate(pan
* Wait for animation selecting.
* @param {AnimationsPanel} panel
*/
function* waitForAnimationSelecting(panel) {
yield panel.animationsTimelineComponent.once("animation-selected");
}
/**
+ + * Click the timeline header to update the animation current time.
+ + * @param {AnimationsPanel} panel
+ + * @param {Number} x position rate on timeline header.
+ + * This method calculates
+ + * `position * offsetWidth + offsetLeft of timeline header`
+ + * as the clientX of MouseEvent.
+ + * This parameter should be from 0.0 to 1.0.
+ + */
+function* clickOnTimelineHeader(panel, position) {
+ const timeline = panel.animationsTimelineComponent;
+ const onTimelineDataChanged = timeline.once("timeline-data-changed");
+
+ const header = timeline.timeHeaderEl;
+ const clientX = header.offsetLeft + header.offsetWidth * position;
+ EventUtils.sendMouseEvent({ type: "mousedown", clientX: clientX },
+ header, header.ownerDocument.defaultView);
+ info(`Click at (${ clientX }, 0) on timeline header`);
+ EventUtils.sendMouseEvent({ type: "mouseup", clientX: clientX }, header,
+ header.ownerDocument.defaultView);
+ return yield onTimelineDataChanged;
+}
+
+/**
* Prevent the toolbox common highlighter from making backend requests.
* @param {Toolbox} toolbox
*/
function disableHighlighter(toolbox) {
toolbox._highlighter = {
showBoxModel: () => new Promise(r => r()),
hideBoxModel: () => new Promise(r => r()),
pick: () => new Promise(r => r()),
@@ -427,8 +450,35 @@ function getKeyframeComponent(panel, pro
* @param {Index} keyframeIndex The index of the keyframe.
* @return {DOMNode} The keyframe element.
*/
function getKeyframeEl(panel, propertyName, keyframeIndex) {
let keyframeComponent = getKeyframeComponent(panel, propertyName);
return keyframeComponent.keyframesEl
.querySelectorAll(".frame")[keyframeIndex];
}
+
+/**
+ * Set style to test document.
+ * @param {Animation} animation - animation object.
+ * @param {AnimationsPanel} panel - The panel instance.
+ * @param {String} name - property name.
+ * @param {String} value - property value.
+ * @param {String} selector - selector for test document.
+ */
+function* setStyle(animation, panel, name, value, selector) {
+ info("Change the animation style via the content DOM. Setting " +
+ name + " to " + value + " of " + selector);
+
+ const onAnimationChanged = animation ? once(animation, "changed") : Promise.resolve();
+ yield executeInContent("devtools:test:setStyle", {
+ selector: selector,
+ propertyName: name,
+ propertyValue: value
+ });
+ yield onAnimationChanged;
+ const onSelected = animation ? waitForAnimationSelecting(panel) : Promise.resolve();
+ yield onSelected;
+
+ // Also wait for the target node previews to be loaded if the panel got
+ // refreshed as a result of this animation mutation.
+ yield waitForAllAnimationTargets(panel);
+}