mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1198708 - Part 6: Implement KeyframeEffectReadOnly.getFrames(). r=birtles,bzbarsky
Since getFrames() must gather all properties set at a given keyframe offset time for a given easing function, we need to provide a total ordering for ComputedTimingFunction objects. Until the spec defines how to do this, we sort first by NS_STYLE_TRANSITION_TIMING_FUNCTION_* value, then second by the four values in a cubic-bezier() function (in order) or the integer and optional keyword in a steps() function. Because we don't support automatic spacing of keyframes yet, ComputedKeyFrame.computedOffset is always the same as Keyframe.offset. Another assumption made is that the value of easing for a Keyframe object at 100% should be the same as the value from the previous Keyframe for the same property. An alternative would be to leave off easing from that Keyframe, which would need the default value for that IDL dictionary member removed (otherwise it would always be set to "linear").
This commit is contained in:
parent
6e3ca80812
commit
b13da366d2
@ -54,6 +54,31 @@ ComputedTimingFunction::GetValue(double aPortion) const
|
||||
return StepEnd(mSteps, aPortion);
|
||||
}
|
||||
|
||||
int32_t
|
||||
ComputedTimingFunction::Compare(const ComputedTimingFunction& aRhs) const
|
||||
{
|
||||
if (mType != aRhs.mType) {
|
||||
return int32_t(mType) - int32_t(aRhs.mType);
|
||||
}
|
||||
|
||||
if (mType == nsTimingFunction::Type::CubicBezier) {
|
||||
int32_t order = mTimingFunction.Compare(aRhs.mTimingFunction);
|
||||
if (order != 0) {
|
||||
return order;
|
||||
}
|
||||
} else if (mType == nsTimingFunction::Type::StepStart ||
|
||||
mType == nsTimingFunction::Type::StepEnd) {
|
||||
if (mSteps != aRhs.mSteps) {
|
||||
return int32_t(mSteps) - int32_t(aRhs.mSteps);
|
||||
}
|
||||
if (mStepSyntax != aRhs.mStepSyntax) {
|
||||
return int32_t(mStepSyntax) - int32_t(aRhs.mStepSyntax);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ComputedTimingFunction::AppendToString(nsAString& aResult) const
|
||||
{
|
||||
@ -477,5 +502,116 @@ KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
|
||||
}
|
||||
}
|
||||
|
||||
struct KeyframeValueEntry
|
||||
{
|
||||
float mOffset;
|
||||
nsCSSProperty mProperty;
|
||||
nsString mValue;
|
||||
const ComputedTimingFunction* mTimingFunction;
|
||||
|
||||
bool operator==(const KeyframeValueEntry& aRhs) const
|
||||
{
|
||||
NS_ASSERTION(mOffset != aRhs.mOffset || mProperty != aRhs.mProperty,
|
||||
"shouldn't have duplicate (offset, property) pairs");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator<(const KeyframeValueEntry& aRhs) const
|
||||
{
|
||||
NS_ASSERTION(mOffset != aRhs.mOffset || mProperty != aRhs.mProperty,
|
||||
"shouldn't have duplicate (offset, property) pairs");
|
||||
|
||||
// First, sort by offset.
|
||||
if (mOffset != aRhs.mOffset) {
|
||||
return mOffset < aRhs.mOffset;
|
||||
}
|
||||
|
||||
// Second, by timing function.
|
||||
int32_t order = mTimingFunction->Compare(*aRhs.mTimingFunction);
|
||||
if (order != 0) {
|
||||
return order < 0;
|
||||
}
|
||||
|
||||
// Last, by property IDL name.
|
||||
return nsCSSProps::PropertyIDLNameSortPosition(mProperty) <
|
||||
nsCSSProps::PropertyIDLNameSortPosition(aRhs.mProperty);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::GetFrames(JSContext*& aCx,
|
||||
nsTArray<JSObject*>& aResult,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// Collect tuples of the form (offset, property, value, easing) from
|
||||
// mProperties, then sort them so we can generate one ComputedKeyframe per
|
||||
// offset/easing pair. We sort secondarily by property IDL name so that we
|
||||
// have a uniform order that we set properties on the ComputedKeyframe
|
||||
// object.
|
||||
nsAutoTArray<KeyframeValueEntry,4> entries;
|
||||
for (const AnimationProperty& property : mProperties) {
|
||||
if (property.mSegments.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (size_t i = 0, n = property.mSegments.Length(); i < n; i++) {
|
||||
const AnimationPropertySegment& segment = property.mSegments[i];
|
||||
KeyframeValueEntry* entry = entries.AppendElement();
|
||||
entry->mOffset = segment.mFromKey;
|
||||
entry->mProperty = property.mProperty;
|
||||
entry->mTimingFunction = &segment.mTimingFunction;
|
||||
StyleAnimationValue::UncomputeValue(property.mProperty,
|
||||
segment.mFromValue,
|
||||
entry->mValue);
|
||||
}
|
||||
const AnimationPropertySegment& segment = property.mSegments.LastElement();
|
||||
KeyframeValueEntry* entry = entries.AppendElement();
|
||||
entry->mOffset = segment.mToKey;
|
||||
entry->mProperty = property.mProperty;
|
||||
// We don't have the an appropriate animation-timing-function value to use,
|
||||
// either from the element or from the 100% keyframe, so we just set it to
|
||||
// the animation-timing-value value used on the previous segment.
|
||||
entry->mTimingFunction = &segment.mTimingFunction;
|
||||
StyleAnimationValue::UncomputeValue(property.mProperty,
|
||||
segment.mToValue,
|
||||
entry->mValue);
|
||||
}
|
||||
entries.Sort();
|
||||
|
||||
for (size_t i = 0, n = entries.Length(); i < n; ) {
|
||||
// Create a JS object with the explicit ComputedKeyframe dictionary members.
|
||||
ComputedKeyframe keyframeDict;
|
||||
keyframeDict.mOffset.SetValue(entries[i].mOffset);
|
||||
keyframeDict.mComputedOffset.Construct(entries[i].mOffset);
|
||||
keyframeDict.mEasing.Truncate();
|
||||
entries[i].mTimingFunction->AppendToString(keyframeDict.mEasing);
|
||||
keyframeDict.mComposite.SetValue(CompositeOperation::Replace);
|
||||
|
||||
JS::Rooted<JS::Value> keyframeValue(aCx);
|
||||
if (!ToJSValue(aCx, keyframeDict, &keyframeValue)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> keyframe(aCx, &keyframeValue.toObject());
|
||||
|
||||
// Set the property name/value pairs on the JS object.
|
||||
do {
|
||||
const KeyframeValueEntry& entry = entries[i];
|
||||
const char* name = nsCSSProps::PropertyIDLName(entry.mProperty);
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!ToJSValue(aCx, entry.mValue, &value) ||
|
||||
!JS_DefineProperty(aCx, keyframe, name, value, JSPROP_ENUMERATE)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
++i;
|
||||
} while (i < n &&
|
||||
entries[i].mOffset == entries[i - 1].mOffset &&
|
||||
*entries[i].mTimingFunction == *entries[i - 1].mTimingFunction);
|
||||
|
||||
aResult.AppendElement(keyframe);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/AnimationEffectReadOnly.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/KeyframeBinding.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "nsSMILKeySpline.h"
|
||||
#include "nsStyleStruct.h" // for nsTimingFunction
|
||||
@ -130,6 +131,7 @@ public:
|
||||
bool operator!=(const ComputedTimingFunction& aOther) const {
|
||||
return !(*this == aOther);
|
||||
}
|
||||
int32_t Compare(const ComputedTimingFunction& aRhs) const;
|
||||
void AppendToString(nsAString& aResult) const;
|
||||
|
||||
private:
|
||||
@ -231,6 +233,9 @@ public:
|
||||
" pseudo-element is not yet supported.");
|
||||
return mTarget;
|
||||
}
|
||||
void GetFrames(JSContext*& aCx,
|
||||
nsTArray<JSObject*>& aResult,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// Temporary workaround to return both the target element and pseudo-type
|
||||
// until we implement PseudoElement (bug 1174575).
|
||||
|
@ -57,6 +57,13 @@ public:
|
||||
bool operator!=(const nsSMILKeySpline& aOther) const {
|
||||
return !(*this == aOther);
|
||||
}
|
||||
int32_t Compare(const nsSMILKeySpline& aRhs) const {
|
||||
if (mX1 != aRhs.mX1) return mX1 < aRhs.mX1 ? -1 : 1;
|
||||
if (mY1 != aRhs.mY1) return mY1 < aRhs.mY1 ? -1 : 1;
|
||||
if (mX2 != aRhs.mX2) return mX2 < aRhs.mX2 ? -1 : 1;
|
||||
if (mY2 != aRhs.mY2) return mY2 < aRhs.mY2 ? -1 : 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
|
25
dom/webidl/Keyframe.webidl
Normal file
25
dom/webidl/Keyframe.webidl
Normal file
@ -0,0 +1,25 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* https://w3c.github.io/web-animations/#the-compositeoperation-enumeration
|
||||
* https://w3c.github.io/web-animations/#the-keyframe-dictionary
|
||||
* https://w3c.github.io/web-animations/#the-computedkeyframe-dictionary
|
||||
*
|
||||
* Copyright © 2015 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
enum CompositeOperation { "replace", "add", "accumulate" };
|
||||
|
||||
dictionary Keyframe {
|
||||
double? offset = null;
|
||||
DOMString easing = "linear";
|
||||
CompositeOperation? composite = null;
|
||||
};
|
||||
|
||||
dictionary ComputedKeyframe : Keyframe {
|
||||
double computedOffset;
|
||||
};
|
@ -19,5 +19,8 @@ interface KeyframeEffectReadOnly : AnimationEffectReadOnly {
|
||||
// readonly attribute CompositeOperation composite;
|
||||
// readonly attribute DOMString spacing;
|
||||
// KeyframeEffect clone();
|
||||
// sequence<ComputedKeyframe> getFrames ();
|
||||
|
||||
// We use object instead of ComputedKeyframe so that we can put the
|
||||
// property-value pairs on the object.
|
||||
[Throws] sequence<object> getFrames();
|
||||
};
|
||||
|
@ -274,6 +274,7 @@ WEBIDL_FILES = [
|
||||
'KeyAlgorithm.webidl',
|
||||
'KeyboardEvent.webidl',
|
||||
'KeyEvent.webidl',
|
||||
'Keyframe.webidl',
|
||||
'KeyframeEffect.webidl',
|
||||
'KillSwitch.webidl',
|
||||
'LegacyQueryInterface.webidl',
|
||||
|
Loading…
Reference in New Issue
Block a user