Bug 1691109 [wpt PR 27513] - Increase timeout duration for wpt/fetch/api/basic/keepal...
[gecko.git] / dom / animation / ComputedTimingFunction.cpp
blob17b8de323d8b6f4a89fc409322ed8c31298e7606
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ComputedTimingFunction.h"
8 #include "mozilla/ServoBindings.h"
9 #include "nsAlgorithm.h" // For clamped()
11 namespace mozilla {
13 void ComputedTimingFunction::Init(const nsTimingFunction& aFunction) {
14 const StyleComputedTimingFunction& timing = aFunction.mTiming;
15 switch (timing.tag) {
16 case StyleComputedTimingFunction::Tag::Keyword: {
17 mType = static_cast<Type>(static_cast<uint8_t>(timing.keyword._0));
19 static_assert(
20 static_cast<uint8_t>(StyleTimingKeyword::Linear) == 0 &&
21 static_cast<uint8_t>(StyleTimingKeyword::Ease) == 1 &&
22 static_cast<uint8_t>(StyleTimingKeyword::EaseIn) == 2 &&
23 static_cast<uint8_t>(StyleTimingKeyword::EaseOut) == 3 &&
24 static_cast<uint8_t>(StyleTimingKeyword::EaseInOut) == 4,
25 "transition timing function constants not as expected");
27 static const float timingFunctionValues[5][4] = {
28 {0.00f, 0.00f, 1.00f, 1.00f}, // linear
29 {0.25f, 0.10f, 0.25f, 1.00f}, // ease
30 {0.42f, 0.00f, 1.00f, 1.00f}, // ease-in
31 {0.00f, 0.00f, 0.58f, 1.00f}, // ease-out
32 {0.42f, 0.00f, 0.58f, 1.00f} // ease-in-out
34 const float(&values)[4] = timingFunctionValues[uint8_t(mType)];
35 mTimingFunction.Init(values[0], values[1], values[2], values[3]);
36 break;
38 case StyleComputedTimingFunction::Tag::CubicBezier:
39 mType = Type::CubicBezier;
40 mTimingFunction.Init(timing.cubic_bezier.x1, timing.cubic_bezier.y1,
41 timing.cubic_bezier.x2, timing.cubic_bezier.y2);
42 break;
43 case StyleComputedTimingFunction::Tag::Steps:
44 mType = Type::Step;
45 mSteps.mSteps = static_cast<uint32_t>(timing.steps._0);
46 mSteps.mPos = timing.steps._1;
47 break;
51 static inline double StepTiming(
52 const ComputedTimingFunction::StepFunc& aStepFunc, double aPortion,
53 ComputedTimingFunction::BeforeFlag aBeforeFlag) {
54 // Use the algorithm defined in the spec:
55 // https://drafts.csswg.org/css-easing-1/#step-timing-function-algo
57 // Calculate current step.
58 int32_t currentStep = floor(aPortion * aStepFunc.mSteps);
60 // Increment current step if it is jump-start or start.
61 if (aStepFunc.mPos == StyleStepPosition::Start ||
62 aStepFunc.mPos == StyleStepPosition::JumpStart ||
63 aStepFunc.mPos == StyleStepPosition::JumpBoth) {
64 ++currentStep;
67 // If the "before flag" is set and we are at a transition point,
68 // drop back a step
69 if (aBeforeFlag == ComputedTimingFunction::BeforeFlag::Set &&
70 fmod(aPortion * aStepFunc.mSteps, 1) == 0) {
71 --currentStep;
74 // We should not produce a result outside [0, 1] unless we have an
75 // input outside that range. This takes care of steps that would otherwise
76 // occur at boundaries.
77 if (aPortion >= 0.0 && currentStep < 0) {
78 currentStep = 0;
81 int32_t jumps = aStepFunc.mSteps;
82 if (aStepFunc.mPos == StyleStepPosition::JumpBoth) {
83 ++jumps;
84 } else if (aStepFunc.mPos == StyleStepPosition::JumpNone) {
85 --jumps;
88 if (aPortion <= 1.0 && currentStep > jumps) {
89 currentStep = jumps;
92 // Convert to the output progress value.
93 MOZ_ASSERT(jumps > 0, "`jumps` should be a positive integer");
94 return double(currentStep) / double(jumps);
97 double ComputedTimingFunction::GetValue(
98 double aPortion, ComputedTimingFunction::BeforeFlag aBeforeFlag) const {
99 if (HasSpline()) {
100 // Check for a linear curve.
101 // (GetSplineValue(), below, also checks this but doesn't work when
102 // aPortion is outside the range [0.0, 1.0]).
103 if (mTimingFunction.X1() == mTimingFunction.Y1() &&
104 mTimingFunction.X2() == mTimingFunction.Y2()) {
105 return aPortion;
108 // Ensure that we return 0 or 1 on both edges.
109 if (aPortion == 0.0) {
110 return 0.0;
112 if (aPortion == 1.0) {
113 return 1.0;
116 // For negative values, try to extrapolate with tangent (p1 - p0) or,
117 // if p1 is coincident with p0, with (p2 - p0).
118 if (aPortion < 0.0) {
119 if (mTimingFunction.X1() > 0.0) {
120 return aPortion * mTimingFunction.Y1() / mTimingFunction.X1();
121 } else if (mTimingFunction.Y1() == 0 && mTimingFunction.X2() > 0.0) {
122 return aPortion * mTimingFunction.Y2() / mTimingFunction.X2();
124 // If we can't calculate a sensible tangent, don't extrapolate at all.
125 return 0.0;
128 // For values greater than 1, try to extrapolate with tangent (p2 - p3) or,
129 // if p2 is coincident with p3, with (p1 - p3).
130 if (aPortion > 1.0) {
131 if (mTimingFunction.X2() < 1.0) {
132 return 1.0 + (aPortion - 1.0) * (mTimingFunction.Y2() - 1) /
133 (mTimingFunction.X2() - 1);
134 } else if (mTimingFunction.Y2() == 1 && mTimingFunction.X1() < 1.0) {
135 return 1.0 + (aPortion - 1.0) * (mTimingFunction.Y1() - 1) /
136 (mTimingFunction.X1() - 1);
138 // If we can't calculate a sensible tangent, don't extrapolate at all.
139 return 1.0;
142 return mTimingFunction.GetSplineValue(aPortion);
145 return StepTiming(mSteps, aPortion, aBeforeFlag);
148 int32_t ComputedTimingFunction::Compare(
149 const ComputedTimingFunction& aRhs) const {
150 if (mType != aRhs.mType) {
151 return int32_t(mType) - int32_t(aRhs.mType);
154 if (mType == Type::CubicBezier) {
155 int32_t order = mTimingFunction.Compare(aRhs.mTimingFunction);
156 if (order != 0) {
157 return order;
159 } else if (mType == Type::Step) {
160 if (mSteps.mPos != aRhs.mSteps.mPos) {
161 return int32_t(mSteps.mPos) - int32_t(aRhs.mSteps.mPos);
162 } else if (mSteps.mSteps != aRhs.mSteps.mSteps) {
163 return int32_t(mSteps.mSteps) - int32_t(aRhs.mSteps.mSteps);
167 return 0;
170 void ComputedTimingFunction::AppendToString(nsACString& aResult) const {
171 nsTimingFunction timing;
172 switch (mType) {
173 case Type::CubicBezier:
174 timing.mTiming = StyleComputedTimingFunction::CubicBezier(
175 mTimingFunction.X1(), mTimingFunction.Y1(), mTimingFunction.X2(),
176 mTimingFunction.Y2());
177 break;
178 case Type::Step:
179 timing.mTiming =
180 StyleComputedTimingFunction::Steps(mSteps.mSteps, mSteps.mPos);
181 break;
182 case Type::Linear:
183 case Type::Ease:
184 case Type::EaseIn:
185 case Type::EaseOut:
186 case Type::EaseInOut:
187 timing.mTiming = StyleComputedTimingFunction::Keyword(
188 static_cast<StyleTimingKeyword>(mType));
189 break;
190 default:
191 MOZ_ASSERT_UNREACHABLE("Unsupported timing type");
193 Servo_SerializeEasing(&timing, &aResult);
196 /* static */
197 int32_t ComputedTimingFunction::Compare(
198 const Maybe<ComputedTimingFunction>& aLhs,
199 const Maybe<ComputedTimingFunction>& aRhs) {
200 // We can't use |operator<| for const Maybe<>& here because
201 // 'ease' is prior to 'linear' which is represented by Nothing().
202 // So we have to convert Nothing() as 'linear' and check it first.
203 Type lhsType = aLhs.isNothing() ? Type::Linear : aLhs->GetType();
204 Type rhsType = aRhs.isNothing() ? Type::Linear : aRhs->GetType();
206 if (lhsType != rhsType) {
207 return int32_t(lhsType) - int32_t(rhsType);
210 // Both of them are Nothing().
211 if (lhsType == Type::Linear) {
212 return 0;
215 // Other types.
216 return aLhs->Compare(aRhs.value());
219 } // namespace mozilla