PlatformCAAnimationRemote.mm [plain text]
/*
* Copyright (C) 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "PlatformCAAnimationRemote.h"
#import "ArgumentCoders.h"
#import "RemoteLayerTreeHost.h"
#import "WKAnimationDelegate.h"
#import "WebCoreArgumentCoders.h"
#import <QuartzCore/QuartzCore.h>
#import <WebCore/GraphicsLayer.h>
#import <WebCore/PlatformCAAnimationCocoa.h>
#import <WebCore/PlatformCAFilters.h>
#import <WebCore/TimingFunction.h>
#import <pal/spi/cocoa/QuartzCoreSPI.h>
#import <wtf/BlockObjCExceptions.h>
#import <wtf/RetainPtr.h>
#import <wtf/cocoa/VectorCocoa.h>
#import <wtf/text/TextStream.h>
static MonotonicTime mediaTimeToCurrentTime(CFTimeInterval t)
{
return WTF::MonotonicTime::now() + Seconds(t - CACurrentMediaTime());
}
static NSString * const WKExplicitBeginTimeFlag = @"WKPlatformCAAnimationExplicitBeginTimeFlag";
@interface WKAnimationDelegate () <CAAnimationDelegate>
@end
@implementation WKAnimationDelegate
- (instancetype)initWithLayerID:(WebCore::GraphicsLayer::PlatformLayerID)layerID layerTreeHost:(WebKit::RemoteLayerTreeHost*)layerTreeHost
{
if ((self = [super init])) {
_layerID = layerID;
_layerTreeHost = layerTreeHost;
}
return self;
}
- (void)invalidate
{
_layerTreeHost = nullptr;
}
- (void)animationDidStart:(CAAnimation *)animation
{
if (!_layerTreeHost)
return;
bool hasExplicitBeginTime = [[animation valueForKey:WKExplicitBeginTimeFlag] boolValue];
MonotonicTime startTime;
if (hasExplicitBeginTime) {
// We don't know what time CA used to commit the animation, so just use the current time
// (even though this will be slightly off).
startTime = mediaTimeToCurrentTime(CACurrentMediaTime());
} else
startTime = mediaTimeToCurrentTime([animation beginTime]);
_layerTreeHost->animationDidStart(_layerID, animation, startTime);
}
- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished
{
if (!_layerTreeHost)
return;
_layerTreeHost->animationDidEnd(_layerID, animation);
}
@end
namespace WebKit {
using namespace WebCore;
void PlatformCAAnimationRemote::KeyframeValue::encode(IPC::Encoder& encoder) const
{
encoder << keyType;
switch (keyType) {
case NumberKeyType:
encoder << number;
break;
case ColorKeyType:
encoder << color;
break;
case PointKeyType:
encoder << point;
break;
case TransformKeyType:
encoder << transform;
break;
case FilterKeyType:
encoder << *filter.get();
break;
}
}
Optional<PlatformCAAnimationRemote::KeyframeValue> PlatformCAAnimationRemote::KeyframeValue::decode(IPC::Decoder& decoder)
{
PlatformCAAnimationRemote::KeyframeValue value;
if (!decoder.decode(value.keyType))
return WTF::nullopt;
switch (value.keyType) {
case NumberKeyType:
if (!decoder.decode(value.number))
return WTF::nullopt;
break;
case ColorKeyType:
if (!decoder.decode(value.color))
return WTF::nullopt;
break;
case PointKeyType:
if (!decoder.decode(value.point))
return WTF::nullopt;
break;
case TransformKeyType:
if (!decoder.decode(value.transform))
return WTF::nullopt;
break;
case FilterKeyType:
if (!decodeFilterOperation(decoder, value.filter))
return WTF::nullopt;
break;
}
return WTFMove(value);
}
static void encodeTimingFunction(IPC::Encoder& encoder, TimingFunction* timingFunction)
{
switch (timingFunction->type()) {
case TimingFunction::LinearFunction:
encoder << *static_cast<LinearTimingFunction*>(timingFunction);
break;
case TimingFunction::CubicBezierFunction:
encoder << *static_cast<CubicBezierTimingFunction*>(timingFunction);
break;
case TimingFunction::StepsFunction:
encoder << *static_cast<StepsTimingFunction*>(timingFunction);
break;
case TimingFunction::SpringFunction:
encoder << *static_cast<SpringTimingFunction*>(timingFunction);
break;
}
}
static Optional<RefPtr<TimingFunction>> decodeTimingFunction(IPC::Decoder& decoder)
{
TimingFunction::TimingFunctionType type;
if (!decoder.decode(type))
return WTF::nullopt;
RefPtr<TimingFunction> timingFunction;
switch (type) {
case TimingFunction::LinearFunction:
timingFunction = LinearTimingFunction::create();
if (!decoder.decode(*static_cast<LinearTimingFunction*>(timingFunction.get())))
return WTF::nullopt;
break;
case TimingFunction::CubicBezierFunction:
timingFunction = CubicBezierTimingFunction::create();
if (!decoder.decode(*static_cast<CubicBezierTimingFunction*>(timingFunction.get())))
return WTF::nullopt;
break;
case TimingFunction::StepsFunction:
timingFunction = StepsTimingFunction::create();
if (!decoder.decode(*static_cast<StepsTimingFunction*>(timingFunction.get())))
return WTF::nullopt;
break;
case TimingFunction::SpringFunction:
timingFunction = SpringTimingFunction::create();
if (!decoder.decode(*static_cast<SpringTimingFunction*>(timingFunction.get())))
return WTF::nullopt;
break;
}
return timingFunction;
}
void PlatformCAAnimationRemote::Properties::encode(IPC::Encoder& encoder) const
{
encoder << keyPath;
encoder << animationType;
encoder << beginTime;
encoder << duration;
encoder << timeOffset;
encoder << repeatCount;
encoder << speed;
encoder << fillMode;
encoder << valueFunction;
bool hasTimingFunction = !!timingFunction;
encoder << hasTimingFunction;
if (hasTimingFunction)
encodeTimingFunction(encoder, timingFunction.get());
encoder << autoReverses;
encoder << removedOnCompletion;
encoder << additive;
encoder << reverseTimingFunctions;
encoder << hasExplicitBeginTime;
encoder << keyValues;
encoder << keyTimes;
encoder << static_cast<uint64_t>(timingFunctions.size());
for (const auto& timingFunction : timingFunctions)
encodeTimingFunction(encoder, timingFunction.get());
encoder << animations;
}
Optional<PlatformCAAnimationRemote::Properties> PlatformCAAnimationRemote::Properties::decode(IPC::Decoder& decoder)
{
PlatformCAAnimationRemote::Properties properties;
if (!decoder.decode(properties.keyPath))
return WTF::nullopt;
if (!decoder.decode(properties.animationType))
return WTF::nullopt;
if (!decoder.decode(properties.beginTime))
return WTF::nullopt;
if (!decoder.decode(properties.duration))
return WTF::nullopt;
if (!decoder.decode(properties.timeOffset))
return WTF::nullopt;
if (!decoder.decode(properties.repeatCount))
return WTF::nullopt;
if (!decoder.decode(properties.speed))
return WTF::nullopt;
if (!decoder.decode(properties.fillMode))
return WTF::nullopt;
if (!decoder.decode(properties.valueFunction))
return WTF::nullopt;
bool hasTimingFunction;
if (!decoder.decode(hasTimingFunction))
return WTF::nullopt;
if (hasTimingFunction) {
if (auto timingFunction = decodeTimingFunction(decoder))
properties.timingFunction = WTFMove(*timingFunction);
else
return WTF::nullopt;
}
if (!decoder.decode(properties.autoReverses))
return WTF::nullopt;
if (!decoder.decode(properties.removedOnCompletion))
return WTF::nullopt;
if (!decoder.decode(properties.additive))
return WTF::nullopt;
if (!decoder.decode(properties.reverseTimingFunctions))
return WTF::nullopt;
if (!decoder.decode(properties.hasExplicitBeginTime))
return WTF::nullopt;
if (!decoder.decode(properties.keyValues))
return WTF::nullopt;
if (!decoder.decode(properties.keyTimes))
return WTF::nullopt;
uint64_t numTimingFunctions;
if (!decoder.decode(numTimingFunctions))
return WTF::nullopt;
if (numTimingFunctions) {
properties.timingFunctions.reserveInitialCapacity(numTimingFunctions);
for (size_t i = 0; i < numTimingFunctions; ++i) {
if (auto timingFunction = decodeTimingFunction(decoder))
properties.timingFunctions.uncheckedAppend(WTFMove(*timingFunction));
else
return WTF::nullopt;
}
}
if (!decoder.decode(properties.animations))
return WTF::nullopt;
return WTFMove(properties);
}
Ref<PlatformCAAnimation> PlatformCAAnimationRemote::create(PlatformCAAnimation::AnimationType type, const String& keyPath)
{
return adoptRef(*new PlatformCAAnimationRemote(type, keyPath));
}
Ref<PlatformCAAnimation> PlatformCAAnimationRemote::copy() const
{
auto animation = create(animationType(), keyPath());
animation->setBeginTime(beginTime());
animation->setDuration(duration());
animation->setSpeed(speed());
animation->setTimeOffset(timeOffset());
animation->setRepeatCount(repeatCount());
animation->setAutoreverses(autoreverses());
animation->setFillMode(fillMode());
animation->setRemovedOnCompletion(isRemovedOnCompletion());
animation->setAdditive(isAdditive());
animation->copyTimingFunctionFrom(*this);
animation->setValueFunction(valueFunction());
downcast<PlatformCAAnimationRemote>(animation.get()).setHasExplicitBeginTime(hasExplicitBeginTime());
// Copy the specific Basic or Keyframe values.
if (animationType() == Keyframe) {
animation->copyValuesFrom(*this);
animation->copyKeyTimesFrom(*this);
animation->copyTimingFunctionsFrom(*this);
} else {
animation->copyFromValueFrom(*this);
animation->copyToValueFrom(*this);
}
animation->copyAnimationsFrom(*this);
return animation;
}
PlatformCAAnimationRemote::PlatformCAAnimationRemote(AnimationType type, const String& keyPath)
: PlatformCAAnimation(type)
{
m_properties.keyPath = keyPath;
m_properties.animationType = type;
}
String PlatformCAAnimationRemote::keyPath() const
{
return m_properties.keyPath;
}
CFTimeInterval PlatformCAAnimationRemote::beginTime() const
{
return m_properties.beginTime;
}
void PlatformCAAnimationRemote::setBeginTime(CFTimeInterval value)
{
m_properties.beginTime = value;
// Also set a flag to tell us if we've passed in a 0 value.
// The flag is needed because later beginTime will get changed
// to the time at which it fired and we need to know whether
// or not it was 0 to begin with.
if (value)
m_properties.hasExplicitBeginTime = value;
}
CFTimeInterval PlatformCAAnimationRemote::duration() const
{
return m_properties.duration;
}
void PlatformCAAnimationRemote::setDuration(CFTimeInterval value)
{
m_properties.duration = value;
}
float PlatformCAAnimationRemote::speed() const
{
return m_properties.speed;
}
void PlatformCAAnimationRemote::setSpeed(float value)
{
m_properties.speed = value;
}
CFTimeInterval PlatformCAAnimationRemote::timeOffset() const
{
return m_properties.timeOffset;
}
void PlatformCAAnimationRemote::setTimeOffset(CFTimeInterval value)
{
m_properties.timeOffset = value;
}
float PlatformCAAnimationRemote::repeatCount() const
{
return m_properties.repeatCount;
}
void PlatformCAAnimationRemote::setRepeatCount(float value)
{
m_properties.repeatCount = value;
}
bool PlatformCAAnimationRemote::autoreverses() const
{
return m_properties.autoReverses;
}
void PlatformCAAnimationRemote::setAutoreverses(bool value)
{
m_properties.autoReverses = value;
}
PlatformCAAnimation::FillModeType PlatformCAAnimationRemote::fillMode() const
{
return m_properties.fillMode;
}
void PlatformCAAnimationRemote::setFillMode(FillModeType value)
{
m_properties.fillMode = value;
}
void PlatformCAAnimationRemote::setTimingFunction(const TimingFunction* value, bool)
{
RefPtr<TimingFunction> timingFunction = value->clone();
m_properties.timingFunction = WTFMove(timingFunction);
}
void PlatformCAAnimationRemote::copyTimingFunctionFrom(const PlatformCAAnimation& value)
{
const PlatformCAAnimationRemote& other = downcast<PlatformCAAnimationRemote>(value);
m_properties.timingFunction = other.m_properties.timingFunction;
}
bool PlatformCAAnimationRemote::isRemovedOnCompletion() const
{
return m_properties.removedOnCompletion;
}
void PlatformCAAnimationRemote::setRemovedOnCompletion(bool value)
{
m_properties.removedOnCompletion = value;
}
bool PlatformCAAnimationRemote::isAdditive() const
{
return m_properties.additive;
}
void PlatformCAAnimationRemote::setAdditive(bool value)
{
m_properties.additive = value;
}
PlatformCAAnimation::ValueFunctionType PlatformCAAnimationRemote::valueFunction() const
{
return m_properties.valueFunction;
}
void PlatformCAAnimationRemote::setValueFunction(ValueFunctionType value)
{
m_properties.valueFunction = value;
}
void PlatformCAAnimationRemote::setFromValue(float value)
{
if (animationType() != Basic)
return;
m_properties.keyValues.resize(2);
m_properties.keyValues[0] = KeyframeValue(value);
}
void PlatformCAAnimationRemote::setFromValue(const TransformationMatrix& value)
{
if (animationType() != Basic)
return;
m_properties.keyValues.resize(2);
m_properties.keyValues[0] = KeyframeValue(value);
}
void PlatformCAAnimationRemote::setFromValue(const FloatPoint3D& value)
{
if (animationType() != Basic)
return;
m_properties.keyValues.resize(2);
m_properties.keyValues[0] = KeyframeValue(value);
}
void PlatformCAAnimationRemote::setFromValue(const Color& value)
{
if (animationType() != Basic)
return;
m_properties.keyValues.resize(2);
m_properties.keyValues[0] = KeyframeValue(value);
}
void PlatformCAAnimationRemote::setFromValue(const FilterOperation* operation, int internalFilterPropertyIndex)
{
if (animationType() != Basic)
return;
m_properties.keyValues.resize(2);
m_properties.keyValues[0] = KeyframeValue(operation->clone());
}
void PlatformCAAnimationRemote::copyFromValueFrom(const PlatformCAAnimation& value)
{
const PlatformCAAnimationRemote& other = downcast<PlatformCAAnimationRemote>(value);
if (other.m_properties.keyValues.isEmpty())
return;
m_properties.keyValues.resize(2);
m_properties.keyValues[0] = other.m_properties.keyValues[0];
}
void PlatformCAAnimationRemote::setToValue(float value)
{
if (animationType() != Basic)
return;
m_properties.keyValues.resize(2);
m_properties.keyValues[1] = KeyframeValue(value);
}
void PlatformCAAnimationRemote::setToValue(const TransformationMatrix& value)
{
if (animationType() != Basic)
return;
m_properties.keyValues.resize(2);
m_properties.keyValues[1] = KeyframeValue(value);
}
void PlatformCAAnimationRemote::setToValue(const FloatPoint3D& value)
{
if (animationType() != Basic)
return;
m_properties.keyValues.resize(2);
m_properties.keyValues[1] = KeyframeValue(value);
}
void PlatformCAAnimationRemote::setToValue(const Color& value)
{
if (animationType() != Basic)
return;
m_properties.keyValues.resize(2);
m_properties.keyValues[1] = KeyframeValue(value);
}
void PlatformCAAnimationRemote::setToValue(const FilterOperation* operation, int internalFilterPropertyIndex)
{
if (animationType() != Basic)
return;
UNUSED_PARAM(internalFilterPropertyIndex);
ASSERT(operation);
m_properties.keyValues.resize(2);
m_properties.keyValues[1] = KeyframeValue(operation->clone());
}
void PlatformCAAnimationRemote::copyToValueFrom(const PlatformCAAnimation& value)
{
const PlatformCAAnimationRemote& other = downcast<PlatformCAAnimationRemote>(value);
if (other.m_properties.keyValues.size() < 2)
return;
m_properties.keyValues.resize(2);
m_properties.keyValues[1] = other.m_properties.keyValues[1];
}
// Keyframe-animation properties.
void PlatformCAAnimationRemote::setValues(const Vector<float>& values)
{
if (animationType() != Keyframe)
return;
Vector<KeyframeValue> keyframes;
keyframes.reserveInitialCapacity(values.size());
for (size_t i = 0; i < values.size(); ++i)
keyframes.uncheckedAppend(KeyframeValue(values[i]));
m_properties.keyValues = WTFMove(keyframes);
}
void PlatformCAAnimationRemote::setValues(const Vector<TransformationMatrix>& values)
{
if (animationType() != Keyframe)
return;
Vector<KeyframeValue> keyframes;
keyframes.reserveInitialCapacity(values.size());
for (size_t i = 0; i < values.size(); ++i)
keyframes.uncheckedAppend(KeyframeValue(values[i]));
m_properties.keyValues = WTFMove(keyframes);
}
void PlatformCAAnimationRemote::setValues(const Vector<FloatPoint3D>& values)
{
if (animationType() != Keyframe)
return;
Vector<KeyframeValue> keyframes;
keyframes.reserveInitialCapacity(values.size());
for (size_t i = 0; i < values.size(); ++i)
keyframes.uncheckedAppend(KeyframeValue(values[i]));
m_properties.keyValues = WTFMove(keyframes);
}
void PlatformCAAnimationRemote::setValues(const Vector<Color>& values)
{
if (animationType() != Keyframe)
return;
Vector<KeyframeValue> keyframes;
keyframes.reserveInitialCapacity(values.size());
for (size_t i = 0; i < values.size(); ++i)
keyframes.uncheckedAppend(KeyframeValue(values[i]));
m_properties.keyValues = WTFMove(keyframes);
}
void PlatformCAAnimationRemote::setValues(const Vector<RefPtr<FilterOperation>>& values, int internalFilterPropertyIndex)
{
UNUSED_PARAM(internalFilterPropertyIndex);
if (animationType() != Keyframe)
return;
Vector<KeyframeValue> keyframes;
keyframes.reserveInitialCapacity(values.size());
for (auto& value : values)
keyframes.uncheckedAppend(KeyframeValue { value.copyRef() });
m_properties.keyValues = WTFMove(keyframes);
}
void PlatformCAAnimationRemote::copyValuesFrom(const PlatformCAAnimation& value)
{
m_properties.keyValues = downcast<PlatformCAAnimationRemote>(value).m_properties.keyValues;
}
void PlatformCAAnimationRemote::setKeyTimes(const Vector<float>& keyTimes)
{
m_properties.keyTimes = keyTimes;
}
void PlatformCAAnimationRemote::copyKeyTimesFrom(const PlatformCAAnimation& value)
{
m_properties.keyTimes = downcast<PlatformCAAnimationRemote>(value).m_properties.keyTimes;
}
void PlatformCAAnimationRemote::setTimingFunctions(const Vector<const TimingFunction*>& values, bool reverse)
{
Vector<RefPtr<WebCore::TimingFunction>> timingFunctions;
timingFunctions.reserveInitialCapacity(values.size());
for (size_t i = 0; i < values.size(); ++i)
timingFunctions.uncheckedAppend(values[i]->clone());
m_properties.timingFunctions = WTFMove(timingFunctions);
m_properties.reverseTimingFunctions = reverse;
}
void PlatformCAAnimationRemote::copyTimingFunctionsFrom(const PlatformCAAnimation& value)
{
const PlatformCAAnimationRemote& other = downcast<PlatformCAAnimationRemote>(value);
m_properties.timingFunctions = other.m_properties.timingFunctions;
m_properties.reverseTimingFunctions = other.m_properties.reverseTimingFunctions;
}
void PlatformCAAnimationRemote::setAnimations(const Vector<RefPtr<PlatformCAAnimation>>& values)
{
m_properties.animations = values.map([](auto& value) {
return downcast<PlatformCAAnimationRemote>(value.get())->properties();
});
}
void PlatformCAAnimationRemote::copyAnimationsFrom(const PlatformCAAnimation& value)
{
const PlatformCAAnimationRemote& other = downcast<PlatformCAAnimationRemote>(value);
m_properties.animations = other.m_properties.animations;
}
static NSObject* animationValueFromKeyframeValue(const PlatformCAAnimationRemote::KeyframeValue& keyframeValue)
{
switch (keyframeValue.keyframeType()) {
case PlatformCAAnimationRemote::KeyframeValue::NumberKeyType:
return @(keyframeValue.numberValue());
case PlatformCAAnimationRemote::KeyframeValue::ColorKeyType: {
auto [r, g, b, a] = keyframeValue.colorValue().toSRGBALossy<uint8_t>();
return @[ @(r), @(g), @(b), @(a) ];
}
case PlatformCAAnimationRemote::KeyframeValue::PointKeyType: {
FloatPoint3D point = keyframeValue.pointValue();
return @[ @(point.x()), @(point.y()), @(point.z()) ];
}
case PlatformCAAnimationRemote::KeyframeValue::TransformKeyType:
return [NSValue valueWithCATransform3D:keyframeValue.transformValue()];
case PlatformCAAnimationRemote::KeyframeValue::FilterKeyType:
return PlatformCAFilters::filterValueForOperation(keyframeValue.filterValue(), 0 /* unused */).autorelease();
}
}
static RetainPtr<CAAnimation> createAnimation(CALayer *layer, RemoteLayerTreeHost* layerTreeHost, const PlatformCAAnimationRemote::Properties& properties)
{
RetainPtr<CAAnimation> caAnimation;
switch (properties.animationType) {
case PlatformCAAnimation::Basic: {
auto basicAnimation = [CABasicAnimation animationWithKeyPath:properties.keyPath];
if (properties.keyValues.size() > 1) {
[basicAnimation setFromValue:animationValueFromKeyframeValue(properties.keyValues[0])];
[basicAnimation setToValue:animationValueFromKeyframeValue(properties.keyValues[1])];
}
if (properties.timingFunctions.size())
[basicAnimation setTimingFunction:toCAMediaTimingFunction(properties.timingFunctions[0].get(), properties.reverseTimingFunctions)];
caAnimation = basicAnimation;
break;
}
case PlatformCAAnimation::Group: {
auto animationGroup = [CAAnimationGroup animation];
if (properties.animations.size()) {
[animationGroup setAnimations:createNSArray(properties.animations, [&] (auto& animationProperties) {
return createAnimation(layer, layerTreeHost, animationProperties).get();
}).get()];
}
caAnimation = animationGroup;
break;
}
case PlatformCAAnimation::Keyframe: {
auto keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:properties.keyPath];
if (properties.keyValues.size()) {
[keyframeAnimation setValues:createNSArray(properties.keyValues, [] (auto& value) {
return animationValueFromKeyframeValue(value);
}).get()];
}
if (properties.keyTimes.size()) {
[keyframeAnimation setKeyTimes:createNSArray(properties.keyTimes, [] (auto& time) {
return @(time);
}).get()];
}
if (properties.timingFunction)
[keyframeAnimation setTimingFunction:toCAMediaTimingFunction(properties.timingFunction.get(), false)]; // FIXME: handle reverse.
if (properties.timingFunctions.size()) {
[keyframeAnimation setTimingFunctions:createNSArray(properties.timingFunctions, [&] (auto& function) {
return toCAMediaTimingFunction(function.get(), properties.reverseTimingFunctions);
}).get()];
}
caAnimation = keyframeAnimation;
break;
}
case PlatformCAAnimation::Spring: {
auto springAnimation = [CASpringAnimation animationWithKeyPath:properties.keyPath];
if (properties.keyValues.size() > 1) {
[springAnimation setFromValue:animationValueFromKeyframeValue(properties.keyValues[0])];
[springAnimation setToValue:animationValueFromKeyframeValue(properties.keyValues[1])];
}
if (properties.timingFunctions.size()) {
auto& timingFunction = properties.timingFunctions[0];
if (timingFunction->isSpringTimingFunction()) {
auto& function = *static_cast<const SpringTimingFunction*>(timingFunction.get());
[springAnimation setMass:function.mass()];
[springAnimation setStiffness:function.stiffness()];
[springAnimation setDamping:function.damping()];
[springAnimation setInitialVelocity:function.initialVelocity()];
}
}
caAnimation = springAnimation;
break;
}
}
[caAnimation setBeginTime:properties.beginTime];
[caAnimation setDuration:properties.duration];
[caAnimation setTimeOffset:properties.timeOffset];
[caAnimation setRepeatCount:properties.repeatCount];
[caAnimation setSpeed:properties.speed];
[caAnimation setAutoreverses:properties.autoReverses];
[caAnimation setRemovedOnCompletion:properties.removedOnCompletion];
if ([caAnimation isKindOfClass:[CAPropertyAnimation class]]) {
[(CAPropertyAnimation *)caAnimation setAdditive:properties.additive];
if (properties.valueFunction != PlatformCAAnimation::NoValueFunction)
[(CAPropertyAnimation *)caAnimation setValueFunction:[CAValueFunction functionWithName:toCAValueFunctionType(properties.valueFunction)]];
}
if (properties.fillMode != PlatformCAAnimation::NoFillMode)
[caAnimation setFillMode:toCAFillModeType(properties.fillMode)];
if (properties.hasExplicitBeginTime)
[caAnimation setValue:@YES forKey:WKExplicitBeginTimeFlag];
if (layerTreeHost) {
GraphicsLayer::PlatformLayerID layerID = RemoteLayerTreeNode::layerID(layer);
RetainPtr<WKAnimationDelegate>& delegate = layerTreeHost->animationDelegates().add(layerID, nullptr).iterator->value;
if (!delegate)
delegate = adoptNS([[WKAnimationDelegate alloc] initWithLayerID:layerID layerTreeHost:layerTreeHost]);
[caAnimation setDelegate:delegate.get()];
}
return caAnimation;
}
static void addAnimationToLayer(CALayer *layer, RemoteLayerTreeHost* layerTreeHost, const String& key, const PlatformCAAnimationRemote::Properties& properties)
{
[layer addAnimation:createAnimation(layer, layerTreeHost, properties).get() forKey:key];
}
void PlatformCAAnimationRemote::updateLayerAnimations(CALayer *layer, RemoteLayerTreeHost* layerTreeHost, const AnimationsList& animationsToAdd, const HashSet<String>& animationsToRemove)
{
BEGIN_BLOCK_OBJC_EXCEPTIONS
for (const auto& value : animationsToRemove)
[layer removeAnimationForKey:value];
for (const auto& keyAnimationPair : animationsToAdd)
addAnimationToLayer(layer, layerTreeHost, keyAnimationPair.first, keyAnimationPair.second);
END_BLOCK_OBJC_EXCEPTIONS
}
TextStream& operator<<(TextStream&ts, const PlatformCAAnimationRemote::KeyframeValue& value)
{
switch (value.keyframeType()) {
case PlatformCAAnimationRemote::KeyframeValue::NumberKeyType:
ts << "number=" << value.numberValue();
break;
case PlatformCAAnimationRemote::KeyframeValue::ColorKeyType:
ts << "color=";
ts << value.colorValue();
break;
case PlatformCAAnimationRemote::KeyframeValue::PointKeyType:
ts << "point=";
ts << value.pointValue();
break;
case PlatformCAAnimationRemote::KeyframeValue::TransformKeyType:
ts << "transform=";
ts << value.transformValue();
break;
case PlatformCAAnimationRemote::KeyframeValue::FilterKeyType:
ts << "filter=";
if (value.filterValue())
ts << *value.filterValue();
else
ts << "null";
break;
}
return ts;
}
TextStream& operator<<(TextStream& ts, const PlatformCAAnimationRemote::Properties& animation)
{
ts << "type=";
ts << animation.animationType;
ts << " keyPath=";
ts << animation.keyPath;
if (animation.beginTime)
ts.dumpProperty("beginTime", animation.beginTime);
if (animation.duration)
ts.dumpProperty("duration", animation.duration);
if (animation.timeOffset)
ts.dumpProperty("timeOffset", animation.timeOffset);
ts.dumpProperty("repeatCount", animation.repeatCount);
if (animation.speed != 1)
ts.dumpProperty("speed", animation.speed);
ts.dumpProperty("fillMode", animation.fillMode);
ts.dumpProperty("valueFunction", animation.valueFunction);
ts.dumpProperty<const TimingFunction&>("timing function", *animation.timingFunction);
if (animation.autoReverses)
ts.dumpProperty("autoReverses", animation.autoReverses);
if (!animation.removedOnCompletion)
ts.dumpProperty("removedOnCompletion", animation.removedOnCompletion);
if (animation.additive)
ts.dumpProperty("additive", animation.additive);
if (animation.reverseTimingFunctions)
ts.dumpProperty("reverseTimingFunctions", animation.reverseTimingFunctions);
if (animation.hasExplicitBeginTime)
ts.dumpProperty("hasExplicitBeginTime", animation.hasExplicitBeginTime);
ts << "\n";
ts.increaseIndent();
ts.writeIndent();
ts << "(" << "keyframes";
ts.increaseIndent();
size_t maxFrames = std::max(animation.keyValues.size(), animation.keyTimes.size());
maxFrames = std::max(maxFrames, animation.timingFunctions.size());
for (size_t i = 0; i < maxFrames; ++i) {
ts << "\n";
ts.writeIndent();
ts << "(keyframe " << unsigned(i);
if (i < animation.keyTimes.size())
ts.dumpProperty("time", animation.keyTimes[i]);
if (i < animation.timingFunctions.size() && animation.timingFunctions[i])
ts.dumpProperty<const TimingFunction&>("timing function", *animation.timingFunctions[i]);
if (i < animation.keyValues.size())
ts.dumpProperty("value", animation.keyValues[i]);
ts << ")";
}
ts.decreaseIndent();
ts.decreaseIndent();
if (!animation.animations.isEmpty()) {
ts << "\n";
ts.increaseIndent();
ts.writeIndent();
ts << "(" << "animations";
ts.increaseIndent();
for (auto& childAnimation : animation.animations) {
ts << "\n";
ts.writeIndent();
ts << "(animation " << childAnimation;
ts << ")";
}
ts.decreaseIndent();
ts.decreaseIndent();
}
return ts;
}
} // namespace WebKit