Commit 6ee0600d authored by samli@chromium.org's avatar samli@chromium.org

Devtools Animations: Display keyframes for CSS Animations in inspector

This change displays CSS animation keyframes alongside the associated
animation player in the Animations sidebar pane. No visible changes are
introduced except with the hidden 'Animation Inspection' experiment enabled.

BUG=419269

Review URL: https://codereview.chromium.org/664993002

git-svn-id: svn://svn.chromium.org/blink/trunk@184328 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 8765f3f1
......@@ -218,6 +218,15 @@ CSSAnimations::CSSAnimations()
{
}
const AtomicString CSSAnimations::getAnimationNameForInspector(const AnimationPlayer& player)
{
for (const auto& it : m_animations) {
if (it.value->sequenceNumber() == player.sequenceNumber())
return it.key;
}
return nullAtom;
}
PassOwnPtrWillBeRawPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(const Element* animatingElement, Element& element, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
{
OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = adoptPtrWillBeNoop(new CSSAnimationUpdate());
......
......@@ -167,6 +167,8 @@ class CSSAnimations final {
public:
CSSAnimations();
const AtomicString getAnimationNameForInspector(const AnimationPlayer&);
// FIXME: This method is only used here and in the legacy animations
// implementation. It should be made private or file-scope when the legacy
// engine is removed.
......
......@@ -9,7 +9,10 @@
#include "core/animation/AnimationNode.h"
#include "core/animation/AnimationPlayer.h"
#include "core/animation/ElementAnimation.h"
#include "core/css/CSSKeyframeRule.h"
#include "core/css/CSSKeyframesRule.h"
#include "core/inspector/InspectorDOMAgent.h"
#include "core/inspector/InspectorStyleSheet.h"
namespace blink {
......@@ -35,6 +38,77 @@ void InspectorAnimationAgent::reset()
m_idToAnimationPlayer.clear();
}
static PassRefPtr<TypeBuilder::Animation::AnimationNode> buildObjectForAnimationNode(AnimationNode* animationNode)
{
RefPtr<TypeBuilder::Animation::AnimationNode> animationObject = TypeBuilder::Animation::AnimationNode::create()
.setStartDelay(animationNode->specifiedTiming().startDelay)
.setPlaybackRate(animationNode->specifiedTiming().playbackRate)
.setIterationStart(animationNode->specifiedTiming().iterationStart)
.setIterationCount(animationNode->specifiedTiming().iterationCount)
.setDuration(animationNode->duration())
.setDirection(animationNode->specifiedTiming().direction)
.setFillMode(animationNode->specifiedTiming().fillMode)
.setTimeFraction(animationNode->timeFraction())
.setName(animationNode->name());
return animationObject.release();
}
static String playerId(AnimationPlayer& player)
{
return String::number(player.sequenceNumber());
}
static PassRefPtr<TypeBuilder::Animation::AnimationPlayer> buildObjectForAnimationPlayer(AnimationPlayer& animationPlayer, PassRefPtr<TypeBuilder::Animation::KeyframesRule> keyframeRule = nullptr)
{
RefPtr<TypeBuilder::Animation::AnimationNode> animationObject = buildObjectForAnimationNode(animationPlayer.source());
if (keyframeRule)
animationObject->setKeyframesRule(keyframeRule);
RefPtr<TypeBuilder::Animation::AnimationPlayer> playerObject = TypeBuilder::Animation::AnimationPlayer::create()
.setId(playerId(animationPlayer))
.setPausedState(animationPlayer.paused())
.setPlayState(animationPlayer.playState())
.setPlaybackRate(animationPlayer.playbackRate())
.setStartTime(animationPlayer.startTime())
.setCurrentTime(animationPlayer.currentTime())
.setSource(animationObject.release());
return playerObject.release();
}
static PassRefPtr<TypeBuilder::Animation::KeyframeStyle> buildObjectForStyleKeyframe(StyleKeyframe* keyframe)
{
RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), keyframe->mutableProperties().ensureCSSStyleDeclaration(), 0);
RefPtr<TypeBuilder::Animation::KeyframeStyle> keyframeObject = TypeBuilder::Animation::KeyframeStyle::create()
.setOffset(keyframe->keyText())
.setStyle(inspectorStyle->buildObjectForStyle());
return keyframeObject.release();
}
static PassRefPtr<TypeBuilder::Animation::KeyframesRule> buildObjectForStyleRuleKeyframes(const StyleRuleKeyframes* keyframesRule)
{
RefPtr<TypeBuilder::Array<TypeBuilder::Animation::KeyframeStyle> > keyframes = TypeBuilder::Array<TypeBuilder::Animation::KeyframeStyle>::create();
const WillBeHeapVector<RefPtrWillBeMember<StyleKeyframe> >& styleKeyframes = keyframesRule->keyframes();
for (const auto& styleKeyframe : styleKeyframes)
keyframes->addItem(buildObjectForStyleKeyframe(styleKeyframe.get()));
RefPtr<TypeBuilder::Animation::KeyframesRule> keyframesObject = TypeBuilder::Animation::KeyframesRule::create()
.setKeyframes(keyframes);
keyframesObject->setName(keyframesRule->name());
return keyframesObject.release();
}
static PassRefPtr<TypeBuilder::Animation::KeyframesRule> buildObjectForKeyframesRule(const Element& element, const AnimationPlayer& player)
{
StyleResolver& styleResolver = element.ownerDocument()->ensureStyleResolver();
// FIXME: Add support for web anim
CSSAnimations& cssAnimations = element.activeAnimations()->cssAnimations();
const AtomicString animationName = cssAnimations.getAnimationNameForInspector(player);
ASSERT(animationName);
const StyleRuleKeyframes* keyframes = cssAnimations.matchScopedKeyframesRule(&styleResolver, &element, animationName.impl());
return buildObjectForStyleRuleKeyframes(keyframes);
}
void InspectorAnimationAgent::getAnimationPlayersForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<TypeBuilder::Animation::AnimationPlayer> >& animationPlayersArray)
{
animationPlayersArray = TypeBuilder::Array<TypeBuilder::Animation::AnimationPlayer>::create();
......@@ -46,7 +120,14 @@ void InspectorAnimationAgent::getAnimationPlayersForNode(ErrorString* errorStrin
for (WillBeHeapVector<RefPtrWillBeMember<AnimationPlayer> >::iterator it = players.begin(); it != players.end(); ++it) {
AnimationPlayer& player = *(it->get());
m_idToAnimationPlayer.set(playerId(player), &player);
RefPtr<TypeBuilder::Animation::AnimationPlayer> animationPlayerObject = buildObjectForAnimationPlayer(player);
RefPtr<TypeBuilder::Animation::AnimationPlayer> animationPlayerObject;
// FIXME: Add support for web animations
if (!element->activeAnimations()->cssAnimations().getAnimationNameForInspector(player).isNull()) {
RefPtr<TypeBuilder::Animation::KeyframesRule> keyframeRule = buildObjectForKeyframesRule(*element, player);
animationPlayerObject = buildObjectForAnimationPlayer(player, keyframeRule.release());
} else {
animationPlayerObject = buildObjectForAnimationPlayer(player);
}
animationPlayersArray->addItem(animationPlayerObject);
}
}
......@@ -97,39 +178,6 @@ AnimationPlayer* InspectorAnimationAgent::assertAnimationPlayer(ErrorString* err
return player;
}
String InspectorAnimationAgent::playerId(AnimationPlayer& player)
{
return String::number(player.sequenceNumber());
}
PassRefPtr<TypeBuilder::Animation::AnimationPlayer> InspectorAnimationAgent::buildObjectForAnimationPlayer(AnimationPlayer& animationPlayer)
{
RefPtr<TypeBuilder::Animation::AnimationPlayer> playerObject = TypeBuilder::Animation::AnimationPlayer::create()
.setId(playerId(animationPlayer))
.setPausedState(animationPlayer.paused())
.setPlayState(animationPlayer.playState())
.setPlaybackRate(animationPlayer.playbackRate())
.setStartTime(animationPlayer.startTime())
.setCurrentTime(animationPlayer.currentTime())
.setSource(buildObjectForAnimationNode(*(animationPlayer.source())));
return playerObject.release();
}
PassRefPtr<TypeBuilder::Animation::AnimationNode> InspectorAnimationAgent::buildObjectForAnimationNode(AnimationNode& animationNode)
{
RefPtr<TypeBuilder::Animation::AnimationNode> animationObject = TypeBuilder::Animation::AnimationNode::create()
.setStartDelay(animationNode.specifiedTiming().startDelay)
.setPlaybackRate(animationNode.specifiedTiming().playbackRate)
.setIterationStart(animationNode.specifiedTiming().iterationStart)
.setIterationCount(animationNode.specifiedTiming().iterationCount)
.setDuration(animationNode.duration())
.setDirection(animationNode.specifiedTiming().direction)
.setFillMode(animationNode.specifiedTiming().fillMode)
.setTimeFraction(animationNode.timeFraction())
.setName(animationNode.name());
return animationObject.release();
}
void InspectorAnimationAgent::trace(Visitor* visitor)
{
#if ENABLE(OILPAN)
......
......@@ -6,6 +6,7 @@
#define InspectorAnimationAgent_h
#include "core/InspectorFrontend.h"
#include "core/css/CSSKeyframesRule.h"
#include "core/inspector/InspectorBaseAgent.h"
#include "wtf/PassOwnPtr.h"
#include "wtf/text/WTFString.h"
......@@ -14,6 +15,7 @@ namespace blink {
class AnimationNode;
class AnimationPlayer;
class Element;
class InspectorDOMAgent;
class InspectorAnimationAgent final : public InspectorBaseAgent<InspectorAnimationAgent>, public InspectorBackendDispatcher::AnimationCommandHandler {
......@@ -44,11 +46,6 @@ public:
private:
InspectorAnimationAgent(InspectorDOMAgent*);
String playerId(AnimationPlayer&);
PassRefPtr<TypeBuilder::Animation::AnimationPlayer> buildObjectForAnimationPlayer(AnimationPlayer&);
PassRefPtr<TypeBuilder::Animation::AnimationNode> buildObjectForAnimationNode(AnimationNode&);
RawPtrWillBeMember<InspectorDOMAgent> m_domAgent;
InspectorFrontend::Animation* m_frontend;
WillBeHeapHashMap<String, RefPtrWillBeMember<AnimationPlayer> > m_idToAnimationPlayer;
......
......@@ -44,9 +44,22 @@ WebInspector.AnimationsSidebarPane.prototype = {
var id = player.source().name() ? player.source().name() : player.id();
separatorElement.createTextChild(WebInspector.UIString("Animation") + " " + id);
this.bodyElement.appendChild(this._animationSections[i].element);
if (player.source().keyframesRule()) {
var keyframes = player.source().keyframesRule().keyframes();
for (var j = 0; j < keyframes.length; j++) {
var inlineStyle = { selectorText: keyframes[j].offset(), style: keyframes[j].style(), isAttribute: true };
var section = new WebInspector.StylePropertiesSection(this._stylesPane, inlineStyle, true, false);
section.expanded = true;
this.bodyElement.appendChild(section.element);
}
}
}
}
if (!node)
return;
if (this._selectedNode === node) {
for (var i = 0; i < this._animationSections.length; ++i)
this._animationSections[i].updateCurrentTime();
......
......@@ -183,6 +183,8 @@ WebInspector.AnimationModel.AnimationNode = function(target, payload)
{
WebInspector.SDKObject.call(this, target);
this._payload = payload;
if (payload.keyframesRule)
this._keyframesRule = new WebInspector.AnimationModel.KeyframesRule(target, payload.keyframesRule);
}
WebInspector.AnimationModel.AnimationNode.prototype = {
......@@ -258,5 +260,91 @@ WebInspector.AnimationModel.AnimationNode.prototype = {
return this._payload.name;
},
/**
* @return {?WebInspector.AnimationModel.KeyframesRule}
*/
keyframesRule: function()
{
return this._keyframesRule;
},
__proto__: WebInspector.SDKObject.prototype
}
/**
* @constructor
* @extends {WebInspector.SDKObject}
* @param {!WebInspector.Target} target
* @param {!AnimationAgent.KeyframesRule} payload
*/
WebInspector.AnimationModel.KeyframesRule = function(target, payload)
{
WebInspector.SDKObject.call(this, target);
this._payload = payload;
this._keyframes = this._payload.keyframes.map(function (keyframeStyle) {
return new WebInspector.AnimationModel.KeyframeStyle(target, keyframeStyle);
});
}
WebInspector.AnimationModel.KeyframesRule.prototype = {
/**
* @param {!Array.<!AnimationAgent.KeyframeStyle>} payload
*/
_setKeyframesPayload: function(payload)
{
this._keyframes = payload.map(function (keyframeStyle) {
return new WebInspector.AnimationModel.KeyframeStyle(this._target, keyframeStyle);
});
},
/**
* @return {string|undefined}
*/
name: function()
{
return this._payload.name;
},
/**
* @return {!Array.<!WebInspector.AnimationModel.KeyframeStyle>}
*/
keyframes: function()
{
return this._keyframes;
},
__proto__: WebInspector.SDKObject.prototype
}
/**
* @constructor
* @extends {WebInspector.SDKObject}
* @param {!WebInspector.Target} target
* @param {!AnimationAgent.KeyframeStyle} payload
*/
WebInspector.AnimationModel.KeyframeStyle = function(target, payload)
{
WebInspector.SDKObject.call(this, target);
this._payload = payload;
this._style = WebInspector.CSSStyleDeclaration.parsePayload(this.target().cssModel, payload.style);
}
WebInspector.AnimationModel.KeyframeStyle.prototype = {
/**
* @return {string}
*/
offset: function()
{
return this._payload.offset;
},
/**
* @return {!WebInspector.CSSStyleDeclaration}
*/
style: function()
{
return this._style;
},
__proto__: WebInspector.SDKObject.prototype
}
\ No newline at end of file
......@@ -4452,9 +4452,28 @@
{ "name": "direction", "type": "number", "description": "<code>AnimationNode</code>'s playback direction." },
{ "name": "fillMode", "type": "number", "description": "<code>AnimationNode</code>'s fill mode." },
{ "name": "timeFraction", "type": "number", "description": "<code>AnimationNode</code>'s time fraction." },
{ "name": "name", "type": "string", "description": "<code>AnimationNode</code> name."}
{ "name": "name", "type": "string", "description": "<code>AnimationNode</code>'s name." },
{ "name": "keyframesRule", "$ref": "KeyframesRule", "optional": true, "description": "<code>AnimationNode</code>'s keyframes." }
],
"description": "AnimationNode instance"
},
{
"id": "KeyframesRule",
"type": "object",
"properties": [
{ "name": "name", "type": "string", "optional": true, "description": "CSS keyframed animation's name." },
{ "name": "keyframes", "type": "array", "items": { "$ref": "KeyframeStyle" }, "description": "List of animation keyframes." }
],
"description": "Keyframes Rule"
},
{
"id": "KeyframeStyle",
"type": "object",
"properties": [
{ "name": "offset", "type": "string", "description": "Keyframe's time offset." },
{ "name": "style", "$ref": "CSS.CSSStyle", "description": "Keyframe's associated CSS style declaration." }
],
"description": "Keyframe Style"
}
],
"commands": [
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment