mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1211801 - Add a playback rate selector to the animation panel. r=miker
This commit is contained in:
parent
6898bfcc30
commit
d29ab74560
@ -31,6 +31,11 @@ player.animationDurationLabel=Duration:
|
||||
# displayed before the animation delay.
|
||||
player.animationDelayLabel=Delay:
|
||||
|
||||
# LOCALIZATION NOTE (player.animationRateLabel):
|
||||
# This string is displayed in each animation player widget. It is the label
|
||||
# displayed before the animation playback rate.
|
||||
player.animationRateLabel=Playback rate:
|
||||
|
||||
# LOCALIZATION NOTE (player.animationIterationCountLabel):
|
||||
# This string is displayed in each animation player widget. It is the label
|
||||
# displayed before the number of times the animation is set to repeat.
|
||||
|
@ -86,6 +86,8 @@ var getServerTraits = Task.async(function*(target) {
|
||||
method: "stopAnimationPlayerUpdates" },
|
||||
{ name: "hasSetPlaybackRate", actor: "animationplayer",
|
||||
method: "setPlaybackRate" },
|
||||
{ name: "hasSetPlaybackRates", actor: "animations",
|
||||
method: "setPlaybackRates" },
|
||||
{ name: "hasTargetNode", actor: "domwalker",
|
||||
method: "getNodeFromActor" },
|
||||
{ name: "hasSetCurrentTimes", actor: "animations",
|
||||
@ -284,6 +286,23 @@ var AnimationsController = {
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Set all known animations' playback rates to the provided rate.
|
||||
* @param {Number} rate.
|
||||
* @return {Promise} Resolves when the rate has been set.
|
||||
*/
|
||||
setPlaybackRateAll: Task.async(function*(rate) {
|
||||
if (this.traits.hasSetPlaybackRates) {
|
||||
// If the backend can set all playback rates at the same time, use that.
|
||||
yield this.animationsFront.setPlaybackRates(this.animationPlayers, rate);
|
||||
} else if (this.traits.hasSetPlaybackRate) {
|
||||
// Otherwise, fall back to setting each rate individually.
|
||||
for (let animation of this.animationPlayers) {
|
||||
yield animation.setPlaybackRate(rate);
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
// AnimationPlayerFront objects are managed by this controller. They are
|
||||
// retrieved when refreshAnimationPlayers is called, stored in the
|
||||
// animationPlayers array, and destroyed when refreshAnimationPlayers is
|
||||
|
@ -22,6 +22,7 @@
|
||||
<div id="timeline-toolbar" class="theme-toolbar">
|
||||
<button id="rewind-timeline" standalone="true" class="devtools-button"></button>
|
||||
<button id="pause-resume-timeline" standalone="true" class="devtools-button pause-button paused"></button>
|
||||
<span id="timeline-rate"></span>
|
||||
<span id="timeline-current-time" class="label"></span>
|
||||
</div>
|
||||
<div id="players"></div>
|
||||
|
@ -3,12 +3,14 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
/* globals AnimationsController, document, performance, promise,
|
||||
gToolbox, gInspector, requestAnimationFrame, cancelAnimationFrame, L10N */
|
||||
/* globals AnimationsController, document, promise, gToolbox, gInspector */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {AnimationsTimeline} = require("devtools/client/animationinspector/components");
|
||||
const {
|
||||
AnimationsTimeline,
|
||||
RateSelector
|
||||
} = require("devtools/client/animationinspector/components");
|
||||
const {formatStopwatchTime} = require("devtools/client/animationinspector/utils");
|
||||
|
||||
var $ = (selector, target = document) => target.querySelector(selector);
|
||||
@ -39,6 +41,7 @@ var AnimationsPanel = {
|
||||
this.playTimelineButtonEl = $("#pause-resume-timeline");
|
||||
this.rewindTimelineButtonEl = $("#rewind-timeline");
|
||||
this.timelineCurrentTimeEl = $("#timeline-current-time");
|
||||
this.rateSelectorEl = $("#timeline-rate");
|
||||
|
||||
// If the server doesn't support toggling all animations at once, hide the
|
||||
// whole global toolbar.
|
||||
@ -49,7 +52,8 @@ var AnimationsPanel = {
|
||||
// Binding functions that need to be called in scope.
|
||||
for (let functionName of ["onPickerStarted", "onPickerStopped",
|
||||
"refreshAnimationsUI", "toggleAll", "onTabNavigated",
|
||||
"onTimelineDataChanged", "playPauseTimeline", "rewindTimeline"]) {
|
||||
"onTimelineDataChanged", "playPauseTimeline", "rewindTimeline",
|
||||
"onRateChanged"]) {
|
||||
this[functionName] = this[functionName].bind(this);
|
||||
}
|
||||
let hUtils = gToolbox.highlighterUtils;
|
||||
@ -58,12 +62,16 @@ var AnimationsPanel = {
|
||||
this.animationsTimelineComponent = new AnimationsTimeline(gInspector);
|
||||
this.animationsTimelineComponent.init(this.playersEl);
|
||||
|
||||
if (AnimationsController.traits.hasSetPlaybackRate) {
|
||||
this.rateSelectorComponent = new RateSelector();
|
||||
this.rateSelectorComponent.init(this.rateSelectorEl);
|
||||
}
|
||||
|
||||
this.startListeners();
|
||||
|
||||
yield this.refreshAnimationsUI();
|
||||
|
||||
this.initialized.resolve();
|
||||
|
||||
this.emit(this.PANEL_INITIALIZED);
|
||||
}),
|
||||
|
||||
@ -83,10 +91,15 @@ var AnimationsPanel = {
|
||||
this.animationsTimelineComponent.destroy();
|
||||
this.animationsTimelineComponent = null;
|
||||
|
||||
if (this.rateSelectorComponent) {
|
||||
this.rateSelectorComponent.destroy();
|
||||
this.rateSelectorComponent = null;
|
||||
}
|
||||
|
||||
this.playersEl = this.errorMessageEl = null;
|
||||
this.toggleAllButtonEl = this.pickerButtonEl = null;
|
||||
this.playTimelineButtonEl = this.rewindTimelineButtonEl = null;
|
||||
this.timelineCurrentTimeEl = null;
|
||||
this.timelineCurrentTimeEl = this.rateSelectorEl = null;
|
||||
|
||||
this.destroyed.resolve();
|
||||
}),
|
||||
@ -107,6 +120,10 @@ var AnimationsPanel = {
|
||||
|
||||
this.animationsTimelineComponent.on("timeline-data-changed",
|
||||
this.onTimelineDataChanged);
|
||||
|
||||
if (this.rateSelectorComponent) {
|
||||
this.rateSelectorComponent.on("rate-changed", this.onRateChanged);
|
||||
}
|
||||
},
|
||||
|
||||
stopListeners: function() {
|
||||
@ -125,6 +142,10 @@ var AnimationsPanel = {
|
||||
|
||||
this.animationsTimelineComponent.off("timeline-data-changed",
|
||||
this.onTimelineDataChanged);
|
||||
|
||||
if (this.rateSelectorComponent) {
|
||||
this.rateSelectorComponent.off("rate-changed", this.onRateChanged);
|
||||
}
|
||||
},
|
||||
|
||||
togglePlayers: function(isVisible) {
|
||||
@ -173,13 +194,23 @@ var AnimationsPanel = {
|
||||
.catch(e => console.error(e));
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the playback rate of all current animations shown in the timeline to
|
||||
* the value of this.rateSelectorEl.
|
||||
*/
|
||||
onRateChanged: function(e, rate) {
|
||||
AnimationsController.setPlaybackRateAll(rate)
|
||||
.then(() => this.refreshAnimationsStateAndUI())
|
||||
.catch(e => console.error(e));
|
||||
},
|
||||
|
||||
onTabNavigated: function() {
|
||||
this.toggleAllButtonEl.classList.remove("paused");
|
||||
},
|
||||
|
||||
onTimelineDataChanged: function(e, data) {
|
||||
this.timelineData = data;
|
||||
let {isMoving, isPaused, isUserDrag, time} = data;
|
||||
let {isMoving, isUserDrag, time} = data;
|
||||
|
||||
this.playTimelineButtonEl.classList.toggle("paused", !isMoving);
|
||||
|
||||
@ -232,6 +263,11 @@ var AnimationsPanel = {
|
||||
AnimationsController.animationPlayers,
|
||||
AnimationsController.documentCurrentTime);
|
||||
|
||||
// Re-render the rate selector component.
|
||||
if (this.rateSelectorComponent) {
|
||||
this.rateSelectorComponent.render(AnimationsController.animationPlayers);
|
||||
}
|
||||
|
||||
// If there are no players to show, show the error message instead and
|
||||
// return.
|
||||
if (!AnimationsController.animationPlayers.length) {
|
||||
|
@ -34,6 +34,8 @@ const L10N = new ViewHelpers.L10N(STRINGS_URI);
|
||||
const MILLIS_TIME_FORMAT_MAX_DURATION = 4000;
|
||||
// The minimum spacing between 2 time graduation headers in the timeline (px).
|
||||
const TIME_GRADUATION_MIN_SPACING = 40;
|
||||
// List of playback rate presets displayed in the timeline toolbar.
|
||||
const PLAYBACK_RATES = [.1, .25, .5, 1, 2, 5, 10];
|
||||
// The size of the fast-track icon (for compositor-running animations), this is
|
||||
// used to position the icon correctly.
|
||||
const FAST_TRACK_ICON_SIZE = 20;
|
||||
@ -187,6 +189,10 @@ AnimationTargetNode.prototype = {
|
||||
this.previewEl.appendChild(document.createTextNode(">"));
|
||||
}
|
||||
|
||||
this.startListeners();
|
||||
},
|
||||
|
||||
startListeners: function() {
|
||||
// Init events for highlighting and selecting the node.
|
||||
this.previewEl.addEventListener("mouseover", this.onPreviewMouseOver);
|
||||
this.previewEl.addEventListener("mouseout", this.onPreviewMouseOut);
|
||||
@ -200,15 +206,19 @@ AnimationTargetNode.prototype = {
|
||||
TargetNodeHighlighter.on("highlighted", this.onTargetHighlighterLocked);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
TargetNodeHighlighter.unhighlight().catch(e => console.error(e));
|
||||
|
||||
stopListeners: function() {
|
||||
TargetNodeHighlighter.off("highlighted", this.onTargetHighlighterLocked);
|
||||
this.inspector.off("markupmutation", this.onMarkupMutations);
|
||||
this.previewEl.removeEventListener("mouseover", this.onPreviewMouseOver);
|
||||
this.previewEl.removeEventListener("mouseout", this.onPreviewMouseOut);
|
||||
this.previewEl.removeEventListener("click", this.onSelectNodeClick);
|
||||
this.highlightNodeEl.removeEventListener("click", this.onHighlightNodeClick);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
TargetNodeHighlighter.unhighlight().catch(e => console.error(e));
|
||||
|
||||
this.stopListeners();
|
||||
|
||||
this.el.remove();
|
||||
this.el = this.tagNameEl = this.idEl = this.classEl = null;
|
||||
@ -217,21 +227,26 @@ AnimationTargetNode.prototype = {
|
||||
},
|
||||
|
||||
get highlighterUtils() {
|
||||
return this.inspector.toolbox.highlighterUtils;
|
||||
if (this.inspector && this.inspector.toolbox) {
|
||||
return this.inspector.toolbox.highlighterUtils;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
onPreviewMouseOver: function() {
|
||||
if (!this.nodeFront) {
|
||||
if (!this.nodeFront || !this.highlighterUtils) {
|
||||
return;
|
||||
}
|
||||
this.highlighterUtils.highlightNodeFront(this.nodeFront);
|
||||
this.highlighterUtils.highlightNodeFront(this.nodeFront)
|
||||
.catch(e => console.error(e));
|
||||
},
|
||||
|
||||
onPreviewMouseOut: function() {
|
||||
if (!this.nodeFront) {
|
||||
if (!this.nodeFront || !this.highlighterUtils) {
|
||||
return;
|
||||
}
|
||||
this.highlighterUtils.unhighlight();
|
||||
this.highlighterUtils.unhighlight()
|
||||
.catch(e => console.error(e));
|
||||
},
|
||||
|
||||
onSelectNodeClick: function() {
|
||||
@ -331,6 +346,90 @@ AnimationTargetNode.prototype = {
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* UI component responsible for displaying a playback rate selector UI.
|
||||
* The rendering logic is such that a predefined list of rates is generated.
|
||||
* If *all* animations passed to render share the same rate, then that rate is
|
||||
* selected in the <select> element, otherwise, the empty value is selected.
|
||||
* If the rate that all animations share isn't part of the list of predefined
|
||||
* rates, than that rate is added to the list.
|
||||
*/
|
||||
function RateSelector() {
|
||||
this.onRateChanged = this.onRateChanged.bind(this);
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
exports.RateSelector = RateSelector;
|
||||
|
||||
RateSelector.prototype = {
|
||||
init: function(containerEl) {
|
||||
this.selectEl = createNode({
|
||||
parent: containerEl,
|
||||
nodeType: "select",
|
||||
attributes: {"class": "devtools-button"}
|
||||
});
|
||||
|
||||
this.selectEl.addEventListener("change", this.onRateChanged);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.selectEl.removeEventListener("change", this.onRateChanged);
|
||||
this.selectEl.remove();
|
||||
this.selectEl = null;
|
||||
},
|
||||
|
||||
getAnimationsRates: function(animations) {
|
||||
return sortedUnique(animations.map(a => a.state.playbackRate));
|
||||
},
|
||||
|
||||
getAllRates: function(animations) {
|
||||
let animationsRates = this.getAnimationsRates(animations);
|
||||
if (animationsRates.length > 1) {
|
||||
return PLAYBACK_RATES;
|
||||
}
|
||||
|
||||
return sortedUnique(PLAYBACK_RATES.concat(animationsRates));
|
||||
},
|
||||
|
||||
render: function(animations) {
|
||||
let allRates = this.getAnimationsRates(animations);
|
||||
let hasOneRate = allRates.length === 1;
|
||||
|
||||
this.selectEl.innerHTML = "";
|
||||
|
||||
if (!hasOneRate) {
|
||||
// When the animations displayed have mixed playback rates, we can't
|
||||
// select any of the predefined ones, instead, insert an empty rate.
|
||||
createNode({
|
||||
parent: this.selectEl,
|
||||
nodeType: "option",
|
||||
attributes: {value: "", selector: "true"},
|
||||
textContent: "-"
|
||||
});
|
||||
}
|
||||
for (let rate of this.getAllRates(animations)) {
|
||||
let option = createNode({
|
||||
parent: this.selectEl,
|
||||
nodeType: "option",
|
||||
attributes: {value: rate},
|
||||
textContent: L10N.getFormatStr("player.playbackRateLabel", rate)
|
||||
});
|
||||
|
||||
// If there's only one rate and this is the option for it, select it.
|
||||
if (hasOneRate && rate === allRates[0]) {
|
||||
option.setAttribute("selected", "true");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onRateChanged: function() {
|
||||
let rate = parseFloat(this.selectEl.value);
|
||||
if (!isNaN(rate)) {
|
||||
this.emit("rate-changed", rate);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The TimeScale helper object is used to know which size should something be
|
||||
* displayed with in the animation panel, depending on the animations that are
|
||||
@ -858,21 +957,49 @@ AnimationTimeBlock.prototype = {
|
||||
getTooltipText: function(state) {
|
||||
let getTime = time => L10N.getFormatStr("player.timeLabel",
|
||||
L10N.numberWithDecimals(time / 1000, 2));
|
||||
// The type isn't always available, older servers don't send it.
|
||||
let title =
|
||||
|
||||
let text = "";
|
||||
|
||||
// Adding the name (the type isn't always available, older servers don't
|
||||
// send it).
|
||||
text +=
|
||||
state.type
|
||||
? L10N.getFormatStr("timeline." + state.type + ".nameLabel", state.name)
|
||||
: state.name;
|
||||
let delay = L10N.getStr("player.animationDelayLabel") + " " +
|
||||
getTime(state.delay);
|
||||
let duration = L10N.getStr("player.animationDurationLabel") + " " +
|
||||
getTime(state.duration);
|
||||
let iterations = L10N.getStr("player.animationIterationCountLabel") + " " +
|
||||
(state.iterationCount ||
|
||||
L10N.getStr("player.infiniteIterationCountText"));
|
||||
let compositor = state.isRunningOnCompositor
|
||||
? L10N.getStr("player.runningOnCompositorTooltip")
|
||||
: "";
|
||||
return [title, duration, iterations, delay, compositor].join("\n");
|
||||
text += "\n";
|
||||
|
||||
// Adding the delay.
|
||||
text += L10N.getStr("player.animationDelayLabel") + " ";
|
||||
text += getTime(state.delay);
|
||||
text += "\n";
|
||||
|
||||
// Adding the duration.
|
||||
text += L10N.getStr("player.animationDurationLabel") + " ";
|
||||
text += getTime(state.duration);
|
||||
text += "\n";
|
||||
|
||||
// Adding the iteration count (the infinite symbol, or an integer).
|
||||
// XXX: see bug 1219608 to remove this if the count is 1.
|
||||
text += L10N.getStr("player.animationIterationCountLabel") + " ";
|
||||
text += state.iterationCount ||
|
||||
L10N.getStr("player.infiniteIterationCountText");
|
||||
text += "\n";
|
||||
|
||||
// Adding the playback rate if it's different than 1.
|
||||
if (state.playbackRate !== 1) {
|
||||
text += L10N.getStr("player.animationRateLabel") + " ";
|
||||
text += state.playbackRate;
|
||||
text += "\n";
|
||||
}
|
||||
|
||||
// Adding a note that the animation is running on the compositor thread if
|
||||
// needed.
|
||||
if (state.isRunningOnCompositor) {
|
||||
text += L10N.getStr("player.runningOnCompositorTooltip");
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
let sortedUnique = arr => [...new Set(arr)].sort((a, b) => a > b);
|
||||
|
@ -28,6 +28,7 @@ support-files =
|
||||
[browser_animation_timeline_currentTime.js]
|
||||
[browser_animation_timeline_header.js]
|
||||
[browser_animation_timeline_pause_button.js]
|
||||
[browser_animation_timeline_rate_selector.js]
|
||||
[browser_animation_timeline_rewind_button.js]
|
||||
[browser_animation_timeline_scrubber_exists.js]
|
||||
[browser_animation_timeline_scrubber_movable.js]
|
||||
|
@ -0,0 +1,54 @@
|
||||
/* 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 that the timeline toolbar contains a playback rate selector UI and that
|
||||
// it can be used to change the playback rate of animations in the timeline.
|
||||
// Also check that it displays the rate of the current animations in case they
|
||||
// all have the same rate, or that it displays the empty value in case they
|
||||
// have mixed rates.
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
|
||||
|
||||
let {panel, controller, inspector, toolbox} = yield openAnimationInspector();
|
||||
|
||||
// In this test, we disable the highlighter on purpose because of the way
|
||||
// events are simulated to select an option in the playbackRate <select>.
|
||||
// Indeed, this may cause mousemove events to be triggered on the nodes that
|
||||
// are underneath the <select>, and these are AnimationTargetNode instances.
|
||||
// Simulating mouse events on them will cause the highlighter to emit requests
|
||||
// and this might cause the test to fail if they happen after it has ended.
|
||||
disableHighlighter(toolbox);
|
||||
|
||||
let select = panel.rateSelectorEl.firstChild;
|
||||
|
||||
ok(select, "The rate selector exists");
|
||||
|
||||
info("Change all of the current animations' rates to 0.5");
|
||||
yield changeTimelinePlaybackRate(panel, .5);
|
||||
checkAllAnimationsRatesChanged(controller, select, .5);
|
||||
|
||||
info("Select just one animated node and change its rate only");
|
||||
yield selectNode(".animated", inspector);
|
||||
|
||||
yield changeTimelinePlaybackRate(panel, 2);
|
||||
checkAllAnimationsRatesChanged(controller, select, 2);
|
||||
|
||||
info("Select the <body> again, it should now have mixed-rates animations");
|
||||
yield selectNode("body", inspector);
|
||||
|
||||
is(select.value, "", "The selected rate is empty");
|
||||
|
||||
info("Change the rate for these mixed-rate animations");
|
||||
yield changeTimelinePlaybackRate(panel, 1);
|
||||
checkAllAnimationsRatesChanged(controller, select, 1);
|
||||
});
|
||||
|
||||
function checkAllAnimationsRatesChanged({animationPlayers}, select, rate) {
|
||||
ok(animationPlayers.every(({state}) => state.playbackRate === rate),
|
||||
"All animations' rates have been set to " + rate);
|
||||
is(select.value, rate, "The right value is displayed in the select");
|
||||
}
|
@ -480,6 +480,11 @@ function* assertScrubberMoving(panel, isMoving) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the play/pause button in the timeline toolbar and wait for animations
|
||||
* to update.
|
||||
* @param {AnimationsPanel} panel
|
||||
*/
|
||||
function* clickTimelinePlayPauseButton(panel) {
|
||||
let onUiUpdated = panel.once(panel.UI_UPDATED_EVENT);
|
||||
|
||||
@ -491,6 +496,11 @@ function* clickTimelinePlayPauseButton(panel) {
|
||||
yield waitForAllAnimationTargets(panel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the rewind button in the timeline toolbar and wait for animations to
|
||||
* update.
|
||||
* @param {AnimationsPanel} panel
|
||||
*/
|
||||
function* clickTimelineRewindButton(panel) {
|
||||
let onUiUpdated = panel.once(panel.UI_UPDATED_EVENT);
|
||||
|
||||
@ -501,3 +511,52 @@ function* clickTimelineRewindButton(panel) {
|
||||
yield onUiUpdated;
|
||||
yield waitForAllAnimationTargets(panel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a rate inside the playback rate selector in the timeline toolbar and
|
||||
* wait for animations to update.
|
||||
* @param {AnimationsPanel} panel
|
||||
* @param {Number} rate The new rate value to be selected
|
||||
*/
|
||||
function* changeTimelinePlaybackRate(panel, rate) {
|
||||
let onUiUpdated = panel.once(panel.UI_UPDATED_EVENT);
|
||||
|
||||
let select = panel.rateSelectorEl.firstChild;
|
||||
let win = select.ownerDocument.defaultView;
|
||||
|
||||
// Get the right option.
|
||||
let option = [...select.options].filter(o => o.value === rate + "")[0];
|
||||
if (!option) {
|
||||
ok(false,
|
||||
"Could not find an option for rate " + rate + " in the rate selector. " +
|
||||
"Values are: " + [...select.options].map(o => o.value));
|
||||
return;
|
||||
}
|
||||
|
||||
// Simulate the right events to select the option in the drop-down.
|
||||
EventUtils.synthesizeMouseAtCenter(select, {type: "mousedown"}, win);
|
||||
EventUtils.synthesizeMouseAtCenter(option, {type: "mouseup"}, win);
|
||||
|
||||
yield onUiUpdated;
|
||||
yield waitForAllAnimationTargets(panel);
|
||||
|
||||
// Simulate a mousemove outside of the rate selector area to avoid subsequent
|
||||
// tests from failing because of unwanted mouseover events.
|
||||
EventUtils.synthesizeMouseAtCenter(win.document.querySelector("#timeline-toolbar"),
|
||||
{type: "mousemove"}, win);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()),
|
||||
cancelPick: () => new Promise(r => r()),
|
||||
destroy: () => {},
|
||||
traits: {}
|
||||
};
|
||||
}
|
||||
|
@ -161,6 +161,13 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
#timeline-rate select {
|
||||
-moz-appearance: none;
|
||||
text-align: center;
|
||||
color: inherit;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
/* Animation timeline component */
|
||||
|
||||
.animation-timeline {
|
||||
|
@ -410,7 +410,7 @@ var AnimationPlayerActor = ActorClass({
|
||||
* Set the current time of the animation player.
|
||||
*/
|
||||
setCurrentTime: method(function(currentTime) {
|
||||
this.player.currentTime = currentTime;
|
||||
this.player.currentTime = currentTime * this.player.playbackRate;
|
||||
}, {
|
||||
request: {
|
||||
currentTime: Arg(0, "number")
|
||||
@ -849,6 +849,23 @@ var AnimationsActor = exports.AnimationsActor = ActorClass({
|
||||
shouldPause: Arg(2, "boolean")
|
||||
},
|
||||
response: {}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Set the playback rate of several animations at the same time.
|
||||
* @param {Array} players A list of AnimationPlayerActor.
|
||||
* @param {Number} rate The new rate.
|
||||
*/
|
||||
setPlaybackRates: method(function(players, rate) {
|
||||
for (let player of players) {
|
||||
player.setPlaybackRate(rate);
|
||||
}
|
||||
}, {
|
||||
request: {
|
||||
players: Arg(0, "array:animationplayer"),
|
||||
rate: Arg(1, "number")
|
||||
},
|
||||
response: {}
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -4,7 +4,8 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
// Check that a player's playbackRate can be changed.
|
||||
// Check that a player's playbackRate can be changed, and that multiple players
|
||||
// can have their rates changed at the same time.
|
||||
|
||||
add_task(function*() {
|
||||
let {client, walker, animations} =
|
||||
@ -32,6 +33,19 @@ add_task(function*() {
|
||||
state = yield player.getCurrentState();
|
||||
is(state.playbackRate, 1, "The playbackRate was changed back");
|
||||
|
||||
info("Retrieve several animation players and set their rates");
|
||||
node = yield walker.querySelector(walker.rootNode, "body");
|
||||
let players = yield animations.getAnimationPlayersForNode(node);
|
||||
|
||||
info("Change all animations in <body> to .5 rate");
|
||||
yield animations.setPlaybackRates(players, .5);
|
||||
|
||||
info("Query their states and check they are correct");
|
||||
for (let player of players) {
|
||||
let state = yield player.getCurrentState();
|
||||
is(state.playbackRate, .5, "The playbackRate was updated");
|
||||
}
|
||||
|
||||
yield closeDebuggerClient(client);
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user