mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-21 20:03:08 +09:00
222 lines
6.8 KiB
JavaScript
222 lines
6.8 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
|
/* 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/. */
|
|
|
|
"use strict";
|
|
|
|
const {Task} = require("devtools/shared/task");
|
|
const EventEmitter = require("devtools/shared/event-emitter");
|
|
const {createNode, TimeScale} = require("devtools/client/animationinspector/utils");
|
|
const {Keyframes} = require("devtools/client/animationinspector/components/keyframes");
|
|
|
|
/**
|
|
* UI component responsible for displaying detailed information for a given
|
|
* animation.
|
|
* This includes information about timing, easing, keyframes, animated
|
|
* properties.
|
|
*
|
|
* @param {Object} serverTraits The list of server-side capabilities.
|
|
*/
|
|
function AnimationDetails(serverTraits) {
|
|
EventEmitter.decorate(this);
|
|
|
|
this.onFrameSelected = this.onFrameSelected.bind(this);
|
|
|
|
this.keyframeComponents = [];
|
|
this.serverTraits = serverTraits;
|
|
}
|
|
|
|
exports.AnimationDetails = AnimationDetails;
|
|
|
|
AnimationDetails.prototype = {
|
|
// These are part of frame objects but are not animated properties. This
|
|
// array is used to skip them.
|
|
NON_PROPERTIES: ["easing", "composite", "computedOffset", "offset"],
|
|
|
|
init: function (containerEl) {
|
|
this.containerEl = containerEl;
|
|
},
|
|
|
|
destroy: function () {
|
|
this.unrender();
|
|
this.containerEl = null;
|
|
this.serverTraits = null;
|
|
},
|
|
|
|
unrender: function () {
|
|
for (let component of this.keyframeComponents) {
|
|
component.off("frame-selected", this.onFrameSelected);
|
|
component.destroy();
|
|
}
|
|
this.keyframeComponents = [];
|
|
|
|
while (this.containerEl.firstChild) {
|
|
this.containerEl.firstChild.remove();
|
|
}
|
|
},
|
|
|
|
getPerfDataForProperty: function (animation, propertyName) {
|
|
let warning = "";
|
|
let className = "";
|
|
if (animation.state.propertyState) {
|
|
let isRunningOnCompositor;
|
|
for (let propState of animation.state.propertyState) {
|
|
if (propState.property == propertyName) {
|
|
isRunningOnCompositor = propState.runningOnCompositor;
|
|
if (typeof propState.warning != "undefined") {
|
|
warning = propState.warning;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (isRunningOnCompositor && warning == "") {
|
|
className = "oncompositor";
|
|
} else if (!isRunningOnCompositor && warning != "") {
|
|
className = "warning";
|
|
}
|
|
}
|
|
return {className, warning};
|
|
},
|
|
|
|
/**
|
|
* Get a list of the tracks of the animation actor
|
|
* @return {Object} A list of tracks, one per animated property, each
|
|
* with a list of keyframes
|
|
*/
|
|
getTracks: Task.async(function* () {
|
|
let tracks = {};
|
|
|
|
/*
|
|
* getFrames is a AnimationPlayorActor method that returns data about the
|
|
* keyframes of the animation.
|
|
* In FF48, the data it returns change, and will hold only longhand
|
|
* properties ( e.g. borderLeftWidth ), which does not match what we
|
|
* want to display in the animation detail.
|
|
* A new AnimationPlayerActor function, getProperties, is introduced,
|
|
* that returns the animated css properties of the animation and their
|
|
* keyframes values.
|
|
* If the animation actor has the getProperties function, we use it, and if
|
|
* not, we fall back to getFrames, which then returns values we used to
|
|
* handle.
|
|
*/
|
|
if (this.serverTraits.hasGetProperties) {
|
|
let properties = yield this.animation.getProperties();
|
|
for (let {name, values} of properties) {
|
|
if (!tracks[name]) {
|
|
tracks[name] = [];
|
|
}
|
|
|
|
for (let {value, offset} of values) {
|
|
tracks[name].push({value, offset});
|
|
}
|
|
}
|
|
} else {
|
|
let frames = yield this.animation.getFrames();
|
|
for (let frame of frames) {
|
|
for (let name in frame) {
|
|
if (this.NON_PROPERTIES.indexOf(name) != -1) {
|
|
continue;
|
|
}
|
|
|
|
if (!tracks[name]) {
|
|
tracks[name] = [];
|
|
}
|
|
|
|
tracks[name].push({
|
|
value: frame[name],
|
|
offset: frame.computedOffset
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return tracks;
|
|
}),
|
|
|
|
render: Task.async(function* (animation) {
|
|
this.unrender();
|
|
|
|
if (!animation) {
|
|
return;
|
|
}
|
|
this.animation = animation;
|
|
|
|
// We might have been destroyed in the meantime, or the component might
|
|
// have been re-rendered.
|
|
if (!this.containerEl || this.animation !== animation) {
|
|
return;
|
|
}
|
|
|
|
// Build an element for each animated property track.
|
|
this.tracks = yield this.getTracks(animation, this.serverTraits);
|
|
|
|
// Useful for tests to know when the keyframes have been retrieved.
|
|
this.emit("keyframes-retrieved");
|
|
|
|
for (let propertyName in this.tracks) {
|
|
let line = createNode({
|
|
parent: this.containerEl,
|
|
attributes: {"class": "property"}
|
|
});
|
|
let {warning, className} =
|
|
this.getPerfDataForProperty(animation, propertyName);
|
|
createNode({
|
|
// text-overflow doesn't work in flex items, so we need a second level
|
|
// of container to actually have an ellipsis on the name.
|
|
// See bug 972664.
|
|
parent: createNode({
|
|
parent: line,
|
|
attributes: {"class": "name"}
|
|
}),
|
|
textContent: getCssPropertyName(propertyName),
|
|
attributes: {"title": warning,
|
|
"class": className}
|
|
});
|
|
|
|
// Add the keyframes diagram for this property.
|
|
let framesWrapperEl = createNode({
|
|
parent: line,
|
|
attributes: {"class": "track-container"}
|
|
});
|
|
|
|
let framesEl = createNode({
|
|
parent: framesWrapperEl,
|
|
attributes: {"class": "frames"}
|
|
});
|
|
|
|
// Scale the list of keyframes according to the current time scale.
|
|
let {x, w} = TimeScale.getAnimationDimensions(animation);
|
|
framesEl.style.left = `${x}%`;
|
|
framesEl.style.width = `${w}%`;
|
|
|
|
let keyframesComponent = new Keyframes();
|
|
keyframesComponent.init(framesEl);
|
|
keyframesComponent.render({
|
|
keyframes: this.tracks[propertyName],
|
|
propertyName: propertyName,
|
|
animation: animation
|
|
});
|
|
keyframesComponent.on("frame-selected", this.onFrameSelected);
|
|
|
|
this.keyframeComponents.push(keyframesComponent);
|
|
}
|
|
}),
|
|
|
|
onFrameSelected: function (e, args) {
|
|
// Relay the event up, it's needed in parents too.
|
|
this.emit(e, args);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Turn propertyName into property-name.
|
|
* @param {String} jsPropertyName A camelcased CSS property name. Typically
|
|
* something that comes out of computed styles. E.g. borderBottomColor
|
|
* @return {String} The corresponding CSS property name: border-bottom-color
|
|
*/
|
|
function getCssPropertyName(jsPropertyName) {
|
|
return jsPropertyName.replace(/[A-Z]/g, "-$&").toLowerCase();
|
|
}
|
|
exports.getCssPropertyName = getCssPropertyName;
|