author | Patrick Brosset <pbrosset@mozilla.com> |
Wed, 14 Oct 2015 10:03:29 +0200 | |
changeset 267653 | ec2bb28663937305bdbd2792ca1481e8db7f50a4 |
parent 267652 | 738e0c64591824dc87e8cc670a43f34b282f9423 |
child 267654 | 46ef1400ca8ad9338714dd06f1c2b5a2fb93eaf2 |
push id | 66539 |
push user | cbook@mozilla.com |
push date | Wed, 14 Oct 2015 14:21:32 +0000 |
treeherder | mozilla-inbound@80a002a6244a [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bgrins |
bugs | 1199589 |
milestone | 44.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/devtools/client/animationinspector/animation-inspector.xhtml +++ b/devtools/client/animationinspector/animation-inspector.xhtml @@ -17,16 +17,17 @@ <body class="theme-sidebar devtools-monospace" role="application" empty="true"> <div id="global-toolbar" class="theme-toolbar"> <span class="label">&allAnimations;</span> <button id="toggle-all" standalone="true" class="devtools-button pause-button"></button> </div> <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-current-time" class="label"></span> </div> <div id="players"></div> <div id="error-message"> <p>&invalidElement;</p> <p>&selectElement;</p> <button id="element-picker" standalone="true" class="devtools-button"></button> </div> <script type="application/javascript;version=1.8" src="animation-controller.js"></script>
--- a/devtools/client/animationinspector/animation-panel.js +++ b/devtools/client/animationinspector/animation-panel.js @@ -4,16 +4,17 @@ * 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 */ "use strict"; const {AnimationsTimeline} = require("devtools/client/animationinspector/components"); +const {formatStopwatchTime} = require("devtools/client/animationinspector/utils"); var $ = (selector, target = document) => target.querySelector(selector); /** * The main animations panel UI. */ var AnimationsPanel = { UI_UPDATED_EVENT: "ui-updated", @@ -32,16 +33,17 @@ var AnimationsPanel = { this.initialized = promise.defer(); this.playersEl = $("#players"); this.errorMessageEl = $("#error-message"); this.pickerButtonEl = $("#element-picker"); this.toggleAllButtonEl = $("#toggle-all"); this.playTimelineButtonEl = $("#pause-resume-timeline"); this.rewindTimelineButtonEl = $("#rewind-timeline"); + this.timelineCurrentTimeEl = $("#timeline-current-time"); // If the server doesn't support toggling all animations at once, hide the // whole global toolbar. if (!AnimationsController.traits.hasToggleAll) { $("#global-toolbar").style.display = "none"; } // Binding functions that need to be called in scope. @@ -79,16 +81,17 @@ var AnimationsPanel = { this.stopListeners(); this.animationsTimelineComponent.destroy(); this.animationsTimelineComponent = null; this.playersEl = this.errorMessageEl = null; this.toggleAllButtonEl = this.pickerButtonEl = null; this.playTimelineButtonEl = this.rewindTimelineButtonEl = null; + this.timelineCurrentTimeEl = null; this.destroyed.resolve(); }), startListeners: function() { AnimationsController.on(AnimationsController.PLAYERS_UPDATED_EVENT, this.refreshAnimationsUI); @@ -185,16 +188,26 @@ var AnimationsPanel = { // when users drag the scrubber with the mouse, and we want the server-side // requests to be sequenced). if (isPaused && !this.setCurrentTimeAllPromise) { this.setCurrentTimeAllPromise = AnimationsController.setCurrentTimeAll(time, true) .catch(error => console.error(error)) .then(() => this.setCurrentTimeAllPromise = null); } + + this.displayTimelineCurrentTime(); + }, + + displayTimelineCurrentTime: function() { + let {isMoving, isPaused, time} = this.timelineData; + + if (isMoving || isPaused) { + this.timelineCurrentTimeEl.textContent = formatStopwatchTime(time); + } }, /** * Make sure all known animations have their states up to date (which is * useful after the playState or currentTime has been changed and in case the * animations aren't auto-refreshing), and then refresh the UI. */ refreshAnimationsStateAndUI: Task.async(function*() {
--- a/devtools/client/animationinspector/components.js +++ b/devtools/client/animationinspector/components.js @@ -669,17 +669,17 @@ AnimationsTimeline.prototype = { let x = TimeScale.startTimeToDistance(time, this.timeHeaderEl.offsetWidth); this.scrubberEl.style.left = x + "px"; if (time < TimeScale.minStartTime || time > TimeScale.maxEndTime || !this.isAtLeastOneAnimationPlaying()) { this.stopAnimatingScrubber(); this.emit("timeline-data-changed", { - isPaused: false, + isPaused: !this.isAtLeastOneAnimationPlaying(), isMoving: false, time: TimeScale.distanceToRelativeTime(x, this.timeHeaderEl.offsetWidth) }); return; } this.emit("timeline-data-changed", { isPaused: false,
--- a/devtools/client/animationinspector/test/browser.ini +++ b/devtools/client/animationinspector/test/browser.ini @@ -19,16 +19,17 @@ support-files = [browser_animation_playerWidgets_target_nodes.js] [browser_animation_refresh_on_added_animation.js] [browser_animation_refresh_on_removed_animation.js] [browser_animation_refresh_when_active.js] [browser_animation_same_nb_of_playerWidgets_and_playerFronts.js] [browser_animation_shows_player_on_valid_node.js] [browser_animation_target_highlight_select.js] [browser_animation_target_highlighter_lock.js] +[browser_animation_timeline_currentTime.js] [browser_animation_timeline_header.js] [browser_animation_timeline_pause_button.js] [browser_animation_timeline_rewind_button.js] [browser_animation_timeline_scrubber_exists.js] [browser_animation_timeline_scrubber_movable.js] [browser_animation_timeline_scrubber_moves.js] [browser_animation_timeline_shows_delay.js] [browser_animation_timeline_shows_iterations.js]
new file mode 100644 --- /dev/null +++ b/devtools/client/animationinspector/test/browser_animation_timeline_currentTime.js @@ -0,0 +1,46 @@ +/* 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 displays the current time, and that it +// changes when animations are playing, gets back to 0 when animations are +// rewound, and stops when animations are paused. + +add_task(function*() { + yield addTab(TEST_URL_ROOT + "doc_simple_animation.html"); + + let {panel} = yield openAnimationInspector(); + let label = panel.timelineCurrentTimeEl; + ok(label, "The current time label exists"); + + // On page load animations are playing so the time shoud change, although we + // don't want to test the exact value of the time displayed, just that it + // actually changes. + info("Make sure the time displayed actually changes"); + yield isCurrentTimeLabelChanging(panel, true); + + info("Pause the animations and check that the time stops changing"); + yield clickTimelinePlayPauseButton(panel); + yield isCurrentTimeLabelChanging(panel, false); + + info("Rewind the animations and check that the time stops changing"); + yield clickTimelineRewindButton(panel); + yield isCurrentTimeLabelChanging(panel, false); + is(label.textContent, "00:00.000"); +}); + +function* isCurrentTimeLabelChanging(panel, isChanging) { + let label = panel.timelineCurrentTimeEl; + + let time1 = label.textContent; + yield new Promise(r => setTimeout(r, 200)); + let time2 = label.textContent; + + if (isChanging) { + ok(time1 !== time2, "The text displayed in the label changes with time"); + } else { + is(time1, time2, "The text displayed in the label doesn't change"); + } +}
new file mode 100644 --- /dev/null +++ b/devtools/client/animationinspector/test/unit/test_formatStopwatchTime.js @@ -0,0 +1,63 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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"; + +var Cu = Components.utils; +const {require} = Cu.import("resource://gre/modules/devtools/shared/Loader.jsm", {}); +const {formatStopwatchTime} = require("devtools/client/animationinspector/utils"); + + +const TEST_DATA = [{ + desc: "Formatting 0", + time: 0, + expected: "00:00.000" +}, { + desc: "Formatting null", + time: null, + expected: "00:00.000" +}, { + desc: "Formatting undefined", + time: undefined, + expected: "00:00.000" +}, { + desc: "Formatting a small number of ms", + time: 13, + expected: "00:00.013" +}, { + desc: "Formatting a slightly larger number of ms", + time: 500, + expected: "00:00.500" +}, { + desc: "Formatting 1 second", + time: 1000, + expected: "00:01.000" +}, { + desc: "Formatting a number of seconds", + time: 1532, + expected: "00:01.532" +}, { + desc: "Formatting a big number of seconds", + time: 58450, + expected: "00:58.450" +}, { + desc: "Formatting 1 minute", + time: 60000, + expected: "01:00.000" +}, { + desc: "Formatting a number of minutes", + time: 263567, + expected: "04:23.567" +}, { + desc: "Formatting a large number of minutes", + time: 1000 * 60 * 60 * 3, + expected: "180:00.000" +}]; + +function run_test() { + for (let {desc, time, expected} of TEST_DATA) { + equal(formatStopwatchTime(time), expected, desc); + } +}
--- a/devtools/client/animationinspector/test/unit/xpcshell.ini +++ b/devtools/client/animationinspector/test/unit/xpcshell.ini @@ -1,9 +1,10 @@ [DEFAULT] tags = devtools head = tail = firefox-appdir = browser skip-if = toolkit == 'android' || toolkit == 'gonk' [test_findOptimalTimeInterval.js] +[test_formatStopwatchTime.js] [test_timeScale.js]
--- a/devtools/client/animationinspector/utils.js +++ b/devtools/client/animationinspector/utils.js @@ -172,8 +172,39 @@ var TargetNodeHighlighter = { yield this.highlighter.hide(); this.isShown = false; this.emit("unhighlighted"); }) }; EventEmitter.decorate(TargetNodeHighlighter); exports.TargetNodeHighlighter = TargetNodeHighlighter; + +/** + * Format a timestamp (in ms) as a mm:ss.mmm string. + * @param {Number} time + * @return {String} + */ +function formatStopwatchTime(time) { + // Format falsy values as 0 + if (!time) { + return "00:00.000"; + } + + let milliseconds = parseInt(time % 1000, 10); + let seconds = parseInt((time / 1000) % 60, 10); + let minutes = parseInt((time / (1000 * 60)), 10); + + let pad = (nb, max) => { + if (nb < max) { + return new Array((max+"").length - (nb+"").length + 1).join("0") + nb; + } + return nb; + } + + minutes = pad(minutes, 10); + seconds = pad(seconds, 10); + milliseconds = pad(milliseconds, 100); + + return `${minutes}:${seconds}.${milliseconds}`; +} + +exports.formatStopwatchTime = formatStopwatchTime;
--- a/devtools/client/themes/animationinspector.css +++ b/devtools/client/themes/animationinspector.css @@ -57,17 +57,18 @@ body { [timeline] #global-toolbar { display: none; } [timeline] #timeline-toolbar { display: flex; } -#global-toolbar .label { +#global-toolbar .label, +#timeline-toolbar .label { padding: 1px 4px; } /* The main animations container */ #players { height: calc(100% - var(--toolbar-height)); overflow: auto;