| 1 | /* |
| 2 | * Copyright (C) 2017-2018 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * 1. Redistributions of source code must retain the above copyright |
| 8 | * notice, this list of conditions and the following disclaimer. |
| 9 | * 2. Redistributions in binary form must reproduce the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer in the |
| 11 | * documentation and/or other materials provided with the distribution. |
| 12 | * |
| 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| 14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| 17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | #include "config.h" |
| 27 | #include "AnimationEffect.h" |
| 28 | |
| 29 | #include "FillMode.h" |
| 30 | #include "JSComputedEffectTiming.h" |
| 31 | #include "WebAnimationUtilities.h" |
| 32 | |
| 33 | namespace WebCore { |
| 34 | |
| 35 | AnimationEffect::AnimationEffect() |
| 36 | : m_timingFunction(LinearTimingFunction::create()) |
| 37 | { |
| 38 | } |
| 39 | |
| 40 | AnimationEffect::~AnimationEffect() |
| 41 | { |
| 42 | } |
| 43 | |
| 44 | EffectTiming AnimationEffect::getTiming() const |
| 45 | { |
| 46 | EffectTiming timing; |
| 47 | timing.delay = secondsToWebAnimationsAPITime(m_delay); |
| 48 | timing.endDelay = secondsToWebAnimationsAPITime(m_endDelay); |
| 49 | timing.fill = m_fill; |
| 50 | timing.iterationStart = m_iterationStart; |
| 51 | timing.iterations = m_iterations; |
| 52 | if (m_iterationDuration == 0_s) |
| 53 | timing.duration = "auto" ; |
| 54 | else |
| 55 | timing.duration = secondsToWebAnimationsAPITime(m_iterationDuration); |
| 56 | timing.direction = m_direction; |
| 57 | timing.easing = m_timingFunction->cssText(); |
| 58 | return timing; |
| 59 | } |
| 60 | |
| 61 | BasicEffectTiming AnimationEffect::getBasicTiming() const |
| 62 | { |
| 63 | // The Web Animations spec introduces a number of animation effect time-related definitions that refer |
| 64 | // to each other a fair bit, so rather than implementing them as individual methods, it's more efficient |
| 65 | // to return them all as a single BasicEffectTiming. |
| 66 | |
| 67 | auto activeDuration = [this]() -> Seconds { |
| 68 | // 3.8.2. Calculating the active duration |
| 69 | // https://drafts.csswg.org/web-animations-1/#calculating-the-active-duration |
| 70 | |
| 71 | // The active duration is calculated as follows: |
| 72 | // active duration = iteration duration × iteration count |
| 73 | // If either the iteration duration or iteration count are zero, the active duration is zero. |
| 74 | if (!m_iterationDuration || !m_iterations) |
| 75 | return 0_s; |
| 76 | return m_iterationDuration * m_iterations; |
| 77 | }(); |
| 78 | |
| 79 | auto endTime = [this, activeDuration]() -> Seconds { |
| 80 | // 3.5.3 The active interval |
| 81 | // https://drafts.csswg.org/web-animations-1/#end-time |
| 82 | |
| 83 | // The end time of an animation effect is the result of evaluating max(start delay + active duration + end delay, 0). |
| 84 | auto endTime = m_delay + activeDuration + m_endDelay; |
| 85 | return endTime > 0_s ? endTime : 0_s; |
| 86 | }(); |
| 87 | |
| 88 | auto localTime = [this]() -> Optional<Seconds> { |
| 89 | // 4.5.4. Local time |
| 90 | // https://drafts.csswg.org/web-animations-1/#local-time-section |
| 91 | |
| 92 | // The local time of an animation effect at a given moment is based on the first matching condition from the following: |
| 93 | // If the animation effect is associated with an animation, the local time is the current time of the animation. |
| 94 | // Otherwise, the local time is unresolved. |
| 95 | if (m_animation) |
| 96 | return m_animation->currentTime(); |
| 97 | return WTF::nullopt; |
| 98 | }(); |
| 99 | |
| 100 | auto phase = [this, endTime, localTime, activeDuration]() -> AnimationEffectPhase { |
| 101 | // 3.5.5. Animation effect phases and states |
| 102 | // https://drafts.csswg.org/web-animations-1/#animation-effect-phases-and-states |
| 103 | |
| 104 | bool animationIsBackwards = m_animation && m_animation->playbackRate() < 0; |
| 105 | auto beforeActiveBoundaryTime = std::max(std::min(m_delay, endTime), 0_s); |
| 106 | auto activeAfterBoundaryTime = std::max(std::min(m_delay + activeDuration, endTime), 0_s); |
| 107 | |
| 108 | // (This should be the last statement, but it's more efficient to cache the local time and return right away if it's not resolved.) |
| 109 | // Furthermore, it is often convenient to refer to the case when an animation effect is in none of the above phases |
| 110 | // as being in the idle phase. |
| 111 | if (!localTime) |
| 112 | return AnimationEffectPhase::Idle; |
| 113 | |
| 114 | // An animation effect is in the before phase if the animation effect’s local time is not unresolved and |
| 115 | // either of the following conditions are met: |
| 116 | // 1. the local time is less than the before-active boundary time, or |
| 117 | // 2. the animation direction is ‘backwards’ and the local time is equal to the before-active boundary time. |
| 118 | if ((*localTime + timeEpsilon) < beforeActiveBoundaryTime || (animationIsBackwards && std::abs(localTime->microseconds() - beforeActiveBoundaryTime.microseconds()) < timeEpsilon.microseconds())) |
| 119 | return AnimationEffectPhase::Before; |
| 120 | |
| 121 | // An animation effect is in the after phase if the animation effect’s local time is not unresolved and |
| 122 | // either of the following conditions are met: |
| 123 | // 1. the local time is greater than the active-after boundary time, or |
| 124 | // 2. the animation direction is ‘forwards’ and the local time is equal to the active-after boundary time. |
| 125 | if ((*localTime - timeEpsilon) > activeAfterBoundaryTime || (!animationIsBackwards && std::abs(localTime->microseconds() - activeAfterBoundaryTime.microseconds()) < timeEpsilon.microseconds())) |
| 126 | return AnimationEffectPhase::After; |
| 127 | |
| 128 | // An animation effect is in the active phase if the animation effect’s local time is not unresolved and it is not |
| 129 | // in either the before phase nor the after phase. |
| 130 | // (No need to check, we've already established that local time was resolved). |
| 131 | return AnimationEffectPhase::Active; |
| 132 | }(); |
| 133 | |
| 134 | auto activeTime = [this, localTime, phase, activeDuration]() -> Optional<Seconds> { |
| 135 | // 3.8.3.1. Calculating the active time |
| 136 | // https://drafts.csswg.org/web-animations-1/#calculating-the-active-time |
| 137 | |
| 138 | // The active time is based on the local time and start delay. However, it is only defined |
| 139 | // when the animation effect should produce an output and hence depends on its fill mode |
| 140 | // and phase as follows, |
| 141 | |
| 142 | // If the animation effect is in the before phase, the result depends on the first matching |
| 143 | // condition from the following, |
| 144 | if (phase == AnimationEffectPhase::Before) { |
| 145 | // If the fill mode is backwards or both, return the result of evaluating |
| 146 | // max(local time - start delay, 0). |
| 147 | if (m_fill == FillMode::Backwards || m_fill == FillMode::Both) |
| 148 | return std::max(*localTime - m_delay, 0_s); |
| 149 | // Otherwise, return an unresolved time value. |
| 150 | return WTF::nullopt; |
| 151 | } |
| 152 | |
| 153 | // If the animation effect is in the active phase, return the result of evaluating local time - start delay. |
| 154 | if (phase == AnimationEffectPhase::Active) |
| 155 | return *localTime - m_delay; |
| 156 | |
| 157 | // If the animation effect is in the after phase, the result depends on the first matching |
| 158 | // condition from the following, |
| 159 | if (phase == AnimationEffectPhase::After) { |
| 160 | // If the fill mode is forwards or both, return the result of evaluating |
| 161 | // max(min(local time - start delay, active duration), 0). |
| 162 | if (m_fill == FillMode::Forwards || m_fill == FillMode::Both) |
| 163 | return std::max(std::min(*localTime - m_delay, activeDuration), 0_s); |
| 164 | // Otherwise, return an unresolved time value. |
| 165 | return WTF::nullopt; |
| 166 | } |
| 167 | |
| 168 | // Otherwise (the local time is unresolved), return an unresolved time value. |
| 169 | return WTF::nullopt; |
| 170 | }(); |
| 171 | |
| 172 | return { localTime, activeTime, endTime, activeDuration, phase }; |
| 173 | } |
| 174 | |
| 175 | ComputedEffectTiming AnimationEffect::getComputedTiming() const |
| 176 | { |
| 177 | // The Web Animations spec introduces a number of animation effect time-related definitions that refer |
| 178 | // to each other a fair bit, so rather than implementing them as individual methods, it's more efficient |
| 179 | // to return them all as a single ComputedEffectTiming. |
| 180 | |
| 181 | auto basicEffectTiming = getBasicTiming(); |
| 182 | auto activeTime = basicEffectTiming.activeTime; |
| 183 | auto activeDuration = basicEffectTiming.activeDuration; |
| 184 | auto phase = basicEffectTiming.phase; |
| 185 | |
| 186 | auto overallProgress = [this, phase, activeTime]() -> Optional<double> { |
| 187 | // 3.8.3.2. Calculating the overall progress |
| 188 | // https://drafts.csswg.org/web-animations-1/#calculating-the-overall-progress |
| 189 | |
| 190 | // The overall progress describes the number of iterations that have completed (including partial iterations) and is defined as follows: |
| 191 | |
| 192 | // 1. If the active time is unresolved, return unresolved. |
| 193 | if (!activeTime) |
| 194 | return WTF::nullopt; |
| 195 | |
| 196 | // 2. Calculate an initial value for overall progress based on the first matching condition from below, |
| 197 | double overallProgress; |
| 198 | |
| 199 | if (!m_iterationDuration) { |
| 200 | // If the iteration duration is zero, if the animation effect is in the before phase, let overall progress be zero, |
| 201 | // otherwise, let it be equal to the iteration count. |
| 202 | overallProgress = phase == AnimationEffectPhase::Before ? 0 : m_iterations; |
| 203 | } else { |
| 204 | // Otherwise, let overall progress be the result of calculating active time / iteration duration. |
| 205 | overallProgress = secondsToWebAnimationsAPITime(*activeTime) / secondsToWebAnimationsAPITime(m_iterationDuration); |
| 206 | } |
| 207 | |
| 208 | // 3. Return the result of calculating overall progress + iteration start. |
| 209 | overallProgress += m_iterationStart; |
| 210 | return std::abs(overallProgress); |
| 211 | }(); |
| 212 | |
| 213 | auto simpleIterationProgress = [this, overallProgress, phase, activeTime, activeDuration]() -> Optional<double> { |
| 214 | // 3.8.3.3. Calculating the simple iteration progress |
| 215 | // https://drafts.csswg.org/web-animations-1/#calculating-the-simple-iteration-progress |
| 216 | |
| 217 | // The simple iteration progress is a fraction of the progress through the current iteration that |
| 218 | // ignores transformations to the time introduced by the playback direction or timing functions |
| 219 | // applied to the effect, and is calculated as follows: |
| 220 | |
| 221 | // 1. If the overall progress is unresolved, return unresolved. |
| 222 | if (!overallProgress) |
| 223 | return WTF::nullopt; |
| 224 | |
| 225 | // 2. If overall progress is infinity, let the simple iteration progress be iteration start % 1.0, |
| 226 | // otherwise, let the simple iteration progress be overall progress % 1.0. |
| 227 | double simpleIterationProgress = std::isinf(*overallProgress) ? fmod(m_iterationStart, 1) : fmod(*overallProgress, 1); |
| 228 | |
| 229 | // 3. If all of the following conditions are true, |
| 230 | // |
| 231 | // the simple iteration progress calculated above is zero, and |
| 232 | // the animation effect is in the active phase or the after phase, and |
| 233 | // the active time is equal to the active duration, and |
| 234 | // the iteration count is not equal to zero. |
| 235 | // let the simple iteration progress be 1.0. |
| 236 | if (!simpleIterationProgress && (phase == AnimationEffectPhase::Active || phase == AnimationEffectPhase::After) && std::abs(activeTime->microseconds() - activeDuration.microseconds()) < timeEpsilon.microseconds() && m_iterations) |
| 237 | return 1; |
| 238 | |
| 239 | return simpleIterationProgress; |
| 240 | }(); |
| 241 | |
| 242 | auto currentIteration = [this, activeTime, phase, simpleIterationProgress, overallProgress]() -> Optional<double> { |
| 243 | // 3.8.4. Calculating the current iteration |
| 244 | // https://drafts.csswg.org/web-animations-1/#calculating-the-current-iteration |
| 245 | |
| 246 | // The current iteration can be calculated using the following steps: |
| 247 | |
| 248 | // 1. If the active time is unresolved, return unresolved. |
| 249 | if (!activeTime) |
| 250 | return WTF::nullopt; |
| 251 | |
| 252 | // 2. If the animation effect is in the after phase and the iteration count is infinity, return infinity. |
| 253 | if (phase == AnimationEffectPhase::After && std::isinf(m_iterations)) |
| 254 | return std::numeric_limits<double>::infinity(); |
| 255 | |
| 256 | // 3. If the simple iteration progress is 1.0, return floor(overall progress) - 1. |
| 257 | if (*simpleIterationProgress == 1) |
| 258 | return floor(*overallProgress) - 1; |
| 259 | |
| 260 | // 4. Otherwise, return floor(overall progress). |
| 261 | return floor(*overallProgress); |
| 262 | }(); |
| 263 | |
| 264 | auto currentDirection = [this, currentIteration]() -> AnimationEffect::ComputedDirection { |
| 265 | // 3.9.1. Calculating the directed progress |
| 266 | // https://drafts.csswg.org/web-animations-1/#calculating-the-directed-progress |
| 267 | |
| 268 | // If playback direction is normal, let the current direction be forwards. |
| 269 | if (m_direction == PlaybackDirection::Normal) |
| 270 | return AnimationEffect::ComputedDirection::Forwards; |
| 271 | |
| 272 | // If playback direction is reverse, let the current direction be reverse. |
| 273 | if (m_direction == PlaybackDirection::Reverse) |
| 274 | return AnimationEffect::ComputedDirection::Reverse; |
| 275 | |
| 276 | if (!currentIteration) |
| 277 | return AnimationEffect::ComputedDirection::Forwards; |
| 278 | |
| 279 | // Otherwise, let d be the current iteration. |
| 280 | auto d = *currentIteration; |
| 281 | // If playback direction is alternate-reverse increment d by 1. |
| 282 | if (m_direction == PlaybackDirection::AlternateReverse) |
| 283 | d++; |
| 284 | // If d % 2 == 0, let the current direction be forwards, otherwise let the current direction be reverse. |
| 285 | // If d is infinity, let the current direction be forwards. |
| 286 | if (std::isinf(d) || !fmod(d, 2)) |
| 287 | return AnimationEffect::ComputedDirection::Forwards; |
| 288 | return AnimationEffect::ComputedDirection::Reverse; |
| 289 | }(); |
| 290 | |
| 291 | auto directedProgress = [simpleIterationProgress, currentDirection]() -> Optional<double> { |
| 292 | // 3.9.1. Calculating the directed progress |
| 293 | // https://drafts.csswg.org/web-animations-1/#calculating-the-directed-progress |
| 294 | |
| 295 | // The directed progress is calculated from the simple iteration progress using the following steps: |
| 296 | |
| 297 | // 1. If the simple iteration progress is unresolved, return unresolved. |
| 298 | if (!simpleIterationProgress) |
| 299 | return WTF::nullopt; |
| 300 | |
| 301 | // 2. Calculate the current direction (we implement this as a separate method). |
| 302 | |
| 303 | // 3. If the current direction is forwards then return the simple iteration progress. |
| 304 | if (currentDirection == AnimationEffect::ComputedDirection::Forwards) |
| 305 | return *simpleIterationProgress; |
| 306 | |
| 307 | // Otherwise, return 1.0 - simple iteration progress. |
| 308 | return 1 - *simpleIterationProgress; |
| 309 | }(); |
| 310 | |
| 311 | auto transformedProgress = [this, directedProgress, currentDirection, phase]() -> Optional<double> { |
| 312 | // 3.10.1. Calculating the transformed progress |
| 313 | // https://drafts.csswg.org/web-animations-1/#calculating-the-transformed-progress |
| 314 | |
| 315 | // The transformed progress is calculated from the directed progress using the following steps: |
| 316 | // |
| 317 | // 1. If the directed progress is unresolved, return unresolved. |
| 318 | if (!directedProgress) |
| 319 | return WTF::nullopt; |
| 320 | |
| 321 | if (auto iterationDuration = m_iterationDuration.seconds()) { |
| 322 | bool before = false; |
| 323 | // 2. Calculate the value of the before flag as follows: |
| 324 | if (is<StepsTimingFunction>(m_timingFunction)) { |
| 325 | // 1. Determine the current direction using the procedure defined in §3.9.1 Calculating the directed progress. |
| 326 | // 2. If the current direction is forwards, let going forwards be true, otherwise it is false. |
| 327 | bool goingForwards = currentDirection == AnimationEffect::ComputedDirection::Forwards; |
| 328 | // 3. The before flag is set if the animation effect is in the before phase and going forwards is true; |
| 329 | // or if the animation effect is in the after phase and going forwards is false. |
| 330 | before = (phase == AnimationEffectPhase::Before && goingForwards) || (phase == AnimationEffectPhase::After && !goingForwards); |
| 331 | } |
| 332 | |
| 333 | // 3. Return the result of evaluating the animation effect’s timing function passing directed progress as the |
| 334 | // input progress value and before flag as the before flag. |
| 335 | return m_timingFunction->transformTime(*directedProgress, iterationDuration, before); |
| 336 | } |
| 337 | |
| 338 | return *directedProgress; |
| 339 | }(); |
| 340 | |
| 341 | ComputedEffectTiming computedTiming; |
| 342 | computedTiming.delay = secondsToWebAnimationsAPITime(m_delay); |
| 343 | computedTiming.endDelay = secondsToWebAnimationsAPITime(m_endDelay); |
| 344 | computedTiming.fill = m_fill == FillMode::Auto ? FillMode::None : m_fill; |
| 345 | computedTiming.iterationStart = m_iterationStart; |
| 346 | computedTiming.iterations = m_iterations; |
| 347 | computedTiming.duration = secondsToWebAnimationsAPITime(m_iterationDuration); |
| 348 | computedTiming.direction = m_direction; |
| 349 | computedTiming.easing = m_timingFunction->cssText(); |
| 350 | computedTiming.endTime = secondsToWebAnimationsAPITime(basicEffectTiming.endTime); |
| 351 | computedTiming.activeDuration = secondsToWebAnimationsAPITime(activeDuration); |
| 352 | if (basicEffectTiming.localTime) |
| 353 | computedTiming.localTime = secondsToWebAnimationsAPITime(*basicEffectTiming.localTime); |
| 354 | computedTiming.simpleIterationProgress = simpleIterationProgress; |
| 355 | computedTiming.progress = transformedProgress; |
| 356 | computedTiming.currentIteration = currentIteration; |
| 357 | computedTiming.phase = phase; |
| 358 | return computedTiming; |
| 359 | } |
| 360 | |
| 361 | ExceptionOr<void> AnimationEffect::updateTiming(Optional<OptionalEffectTiming> timing) |
| 362 | { |
| 363 | // 6.5.4. Updating the timing of an AnimationEffect |
| 364 | // https://drafts.csswg.org/web-animations/#updating-animationeffect-timing |
| 365 | |
| 366 | // To update the timing properties of an animation effect, effect, from an EffectTiming or OptionalEffectTiming object, input, perform the following steps: |
| 367 | if (!timing) |
| 368 | return { }; |
| 369 | |
| 370 | // 1. If the iterationStart member of input is present and less than zero, throw a TypeError and abort this procedure. |
| 371 | if (timing->iterationStart) { |
| 372 | if (timing->iterationStart.value() < 0) |
| 373 | return Exception { TypeError }; |
| 374 | } |
| 375 | |
| 376 | // 2. If the iterations member of input is present, and less than zero or is the value NaN, throw a TypeError and abort this procedure. |
| 377 | if (timing->iterations) { |
| 378 | if (timing->iterations.value() < 0 || std::isnan(timing->iterations.value())) |
| 379 | return Exception { TypeError }; |
| 380 | } |
| 381 | |
| 382 | // 3. If the duration member of input is present, and less than zero or is the value NaN, throw a TypeError and abort this procedure. |
| 383 | // FIXME: should it not throw an exception on a string other than "auto"? |
| 384 | if (timing->duration) { |
| 385 | if (WTF::holds_alternative<double>(timing->duration.value())) { |
| 386 | auto durationAsDouble = WTF::get<double>(timing->duration.value()); |
| 387 | if (durationAsDouble < 0 || std::isnan(durationAsDouble)) |
| 388 | return Exception { TypeError }; |
| 389 | } else { |
| 390 | if (WTF::get<String>(timing->duration.value()) != "auto" ) |
| 391 | return Exception { TypeError }; |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | // 4. If the easing member of input is present but cannot be parsed using the <timing-function> production [CSS-EASING-1], throw a TypeError and abort this procedure. |
| 396 | if (!timing->easing.isNull()) { |
| 397 | auto timingFunctionResult = TimingFunction::createFromCSSText(timing->easing); |
| 398 | if (timingFunctionResult.hasException()) |
| 399 | return timingFunctionResult.releaseException(); |
| 400 | m_timingFunction = timingFunctionResult.returnValue(); |
| 401 | } |
| 402 | |
| 403 | // 5. Assign each member present in input to the corresponding timing property of effect as follows: |
| 404 | // |
| 405 | // delay → start delay |
| 406 | // endDelay → end delay |
| 407 | // fill → fill mode |
| 408 | // iterationStart → iteration start |
| 409 | // iterations → iteration count |
| 410 | // duration → iteration duration |
| 411 | // direction → playback direction |
| 412 | // easing → timing function |
| 413 | |
| 414 | if (timing->delay) |
| 415 | m_delay = Seconds::fromMilliseconds(timing->delay.value()); |
| 416 | |
| 417 | if (timing->endDelay) |
| 418 | m_endDelay = Seconds::fromMilliseconds(timing->endDelay.value()); |
| 419 | |
| 420 | if (timing->fill) |
| 421 | m_fill = timing->fill.value(); |
| 422 | |
| 423 | if (timing->iterationStart) |
| 424 | m_iterationStart = timing->iterationStart.value(); |
| 425 | |
| 426 | if (timing->iterations) |
| 427 | m_iterations = timing->iterations.value(); |
| 428 | |
| 429 | if (timing->duration) |
| 430 | m_iterationDuration = WTF::holds_alternative<double>(timing->duration.value()) ? Seconds::fromMilliseconds(WTF::get<double>(timing->duration.value())) : 0_s; |
| 431 | |
| 432 | if (timing->direction) |
| 433 | m_direction = timing->direction.value(); |
| 434 | |
| 435 | if (m_animation) |
| 436 | m_animation->effectTimingDidChange(); |
| 437 | |
| 438 | return { }; |
| 439 | } |
| 440 | |
| 441 | ExceptionOr<void> AnimationEffect::setIterationStart(double iterationStart) |
| 442 | { |
| 443 | // https://drafts.csswg.org/web-animations-1/#dom-animationeffecttiming-iterationstart |
| 444 | // If an attempt is made to set this attribute to a value less than zero, a TypeError must |
| 445 | // be thrown and the value of the iterationStart attribute left unchanged. |
| 446 | if (iterationStart < 0) |
| 447 | return Exception { TypeError }; |
| 448 | |
| 449 | if (m_iterationStart == iterationStart) |
| 450 | return { }; |
| 451 | |
| 452 | m_iterationStart = iterationStart; |
| 453 | |
| 454 | return { }; |
| 455 | } |
| 456 | |
| 457 | ExceptionOr<void> AnimationEffect::setIterations(double iterations) |
| 458 | { |
| 459 | // https://drafts.csswg.org/web-animations-1/#dom-animationeffecttiming-iterations |
| 460 | // If an attempt is made to set this attribute to a value less than zero or a NaN value, a |
| 461 | // TypeError must be thrown and the value of the iterations attribute left unchanged. |
| 462 | if (iterations < 0 || std::isnan(iterations)) |
| 463 | return Exception { TypeError }; |
| 464 | |
| 465 | if (m_iterations == iterations) |
| 466 | return { }; |
| 467 | |
| 468 | m_iterations = iterations; |
| 469 | |
| 470 | return { }; |
| 471 | } |
| 472 | |
| 473 | void AnimationEffect::setDelay(const Seconds& delay) |
| 474 | { |
| 475 | if (m_delay == delay) |
| 476 | return; |
| 477 | |
| 478 | m_delay = delay; |
| 479 | } |
| 480 | |
| 481 | void AnimationEffect::setEndDelay(const Seconds& endDelay) |
| 482 | { |
| 483 | if (m_endDelay == endDelay) |
| 484 | return; |
| 485 | |
| 486 | m_endDelay = endDelay; |
| 487 | } |
| 488 | |
| 489 | void AnimationEffect::setFill(FillMode fill) |
| 490 | { |
| 491 | if (m_fill == fill) |
| 492 | return; |
| 493 | |
| 494 | m_fill = fill; |
| 495 | } |
| 496 | |
| 497 | void AnimationEffect::setIterationDuration(const Seconds& duration) |
| 498 | { |
| 499 | if (m_iterationDuration == duration) |
| 500 | return; |
| 501 | |
| 502 | m_iterationDuration = duration; |
| 503 | } |
| 504 | |
| 505 | void AnimationEffect::setDirection(PlaybackDirection direction) |
| 506 | { |
| 507 | if (m_direction == direction) |
| 508 | return; |
| 509 | |
| 510 | m_direction = direction; |
| 511 | } |
| 512 | |
| 513 | void AnimationEffect::setTimingFunction(const RefPtr<TimingFunction>& timingFunction) |
| 514 | { |
| 515 | m_timingFunction = timingFunction; |
| 516 | } |
| 517 | |
| 518 | } // namespace WebCore |
| 519 | |