1 /* 2 * Copyright (C) 2007, 2008, 2009 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 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "config.h" 30 #include "AnimationBase.h" 31 32 #include "AnimationControllerPrivate.h" 33 #include "CSSMutableStyleDeclaration.h" 34 #include "CSSPropertyLonghand.h" 35 #include "CSSPropertyNames.h" 36 #include "CompositeAnimation.h" 37 #include "Document.h" 38 #include "EventNames.h" 39 #include "FloatConversion.h" 40 #include "Frame.h" 41 #include "IdentityTransformOperation.h" 42 #include "ImplicitAnimation.h" 43 #include "KeyframeAnimation.h" 44 #include "MatrixTransformOperation.h" 45 #include "Matrix3DTransformOperation.h" 46 #include "RenderBox.h" 47 #include "RenderLayer.h" 48 #include "RenderLayerBacking.h" 49 #include "RenderStyle.h" 50 #include "UnitBezier.h" 51 #include <algorithm> 52 #include <wtf/CurrentTime.h> 53 54 using namespace std; 55 56 namespace WebCore { 57 58 // The epsilon value we pass to UnitBezier::solve given that the animation is going to run over |dur| seconds. The longer the 59 // animation, the more precision we need in the timing function result to avoid ugly discontinuities. 60 static inline double solveEpsilon(double duration) 61 { 62 return 1.0 / (200.0 * duration); 63 } 64 65 static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x, double p2y, double t, double duration) 66 { 67 // Convert from input time to parametric value in curve, then from 68 // that to output time. 69 UnitBezier bezier(p1x, p1y, p2x, p2y); 70 return bezier.solve(t, solveEpsilon(duration)); 71 } 72 73 static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t) 74 { 75 if (stepAtStart) 76 return min(1.0, (floor(numSteps * t) + 1) / numSteps); 77 return floor(numSteps * t) / numSteps; 78 } 79 80 static inline int blendFunc(const AnimationBase*, int from, int to, double progress) 81 { 82 return int(from + (to - from) * progress); 83 } 84 85 static inline double blendFunc(const AnimationBase*, double from, double to, double progress) 86 { 87 return from + (to - from) * progress; 88 } 89 90 static inline float blendFunc(const AnimationBase*, float from, float to, double progress) 91 { 92 return narrowPrecisionToFloat(from + (to - from) * progress); 93 } 94 95 static inline Color blendFunc(const AnimationBase* anim, const Color& from, const Color& to, double progress) 96 { 97 // We need to preserve the state of the valid flag at the end of the animation 98 if (progress == 1 && !to.isValid()) 99 return Color(); 100 101 // Contrary to the name, RGBA32 actually stores ARGB, so we can initialize Color directly from premultipliedARGBFromColor(). 102 // Also, premultipliedARGBFromColor() bails on zero alpha, so special-case that. 103 Color premultFrom = from.alpha() ? premultipliedARGBFromColor(from) : 0; 104 Color premultTo = to.alpha() ? premultipliedARGBFromColor(to) : 0; 105 106 Color premultBlended(blendFunc(anim, premultFrom.red(), premultTo.red(), progress), 107 blendFunc(anim, premultFrom.green(), premultTo.green(), progress), 108 blendFunc(anim, premultFrom.blue(), premultTo.blue(), progress), 109 blendFunc(anim, premultFrom.alpha(), premultTo.alpha(), progress)); 110 111 return Color(colorFromPremultipliedARGB(premultBlended.rgb())); 112 } 113 114 static inline Length blendFunc(const AnimationBase*, const Length& from, const Length& to, double progress) 115 { 116 return to.blend(from, narrowPrecisionToFloat(progress)); 117 } 118 119 static inline LengthSize blendFunc(const AnimationBase* anim, const LengthSize& from, const LengthSize& to, double progress) 120 { 121 return LengthSize(blendFunc(anim, from.width(), to.width(), progress), 122 blendFunc(anim, from.height(), to.height(), progress)); 123 } 124 125 static inline IntSize blendFunc(const AnimationBase* anim, const IntSize& from, const IntSize& to, double progress) 126 { 127 return IntSize(blendFunc(anim, from.width(), to.width(), progress), 128 blendFunc(anim, from.height(), to.height(), progress)); 129 } 130 131 static inline ShadowStyle blendFunc(const AnimationBase* anim, ShadowStyle from, ShadowStyle to, double progress) 132 { 133 if (from == to) 134 return to; 135 136 double fromVal = from == Normal ? 1 : 0; 137 double toVal = to == Normal ? 1 : 0; 138 double result = blendFunc(anim, fromVal, toVal, progress); 139 return result > 0 ? Normal : Inset; 140 } 141 142 static inline ShadowData* blendFunc(const AnimationBase* anim, const ShadowData* from, const ShadowData* to, double progress) 143 { 144 ASSERT(from && to); 145 if (from->style() != to->style()) 146 return new ShadowData(*to); 147 148 return new ShadowData(blendFunc(anim, from->x(), to->x(), progress), 149 blendFunc(anim, from->y(), to->y(), progress), 150 blendFunc(anim, from->blur(), to->blur(), progress), 151 blendFunc(anim, from->spread(), to->spread(), progress), 152 blendFunc(anim, from->style(), to->style(), progress), 153 from->isWebkitBoxShadow(), 154 blendFunc(anim, from->color(), to->color(), progress)); 155 } 156 157 static inline TransformOperations blendFunc(const AnimationBase* anim, const TransformOperations& from, const TransformOperations& to, double progress) 158 { 159 TransformOperations result; 160 161 // If we have a transform function list, use that to do a per-function animation. Otherwise do a Matrix animation 162 if (anim->isTransformFunctionListValid()) { 163 unsigned fromSize = from.operations().size(); 164 unsigned toSize = to.operations().size(); 165 unsigned size = max(fromSize, toSize); 166 for (unsigned i = 0; i < size; i++) { 167 RefPtr<TransformOperation> fromOp = (i < fromSize) ? from.operations()[i].get() : 0; 168 RefPtr<TransformOperation> toOp = (i < toSize) ? to.operations()[i].get() : 0; 169 RefPtr<TransformOperation> blendedOp = toOp ? toOp->blend(fromOp.get(), progress) : (fromOp ? fromOp->blend(0, progress, true) : 0); 170 if (blendedOp) 171 result.operations().append(blendedOp); 172 else { 173 RefPtr<TransformOperation> identityOp = IdentityTransformOperation::create(); 174 if (progress > 0.5) 175 result.operations().append(toOp ? toOp : identityOp); 176 else 177 result.operations().append(fromOp ? fromOp : identityOp); 178 } 179 } 180 } else { 181 // Convert the TransformOperations into matrices 182 IntSize size = anim->renderer()->isBox() ? toRenderBox(anim->renderer())->borderBoxRect().size() : IntSize(); 183 TransformationMatrix fromT; 184 TransformationMatrix toT; 185 from.apply(size, fromT); 186 to.apply(size, toT); 187 188 toT.blend(fromT, progress); 189 190 // Append the result 191 result.operations().append(Matrix3DTransformOperation::create(toT)); 192 } 193 return result; 194 } 195 196 static inline EVisibility blendFunc(const AnimationBase* anim, EVisibility from, EVisibility to, double progress) 197 { 198 // Any non-zero result means we consider the object to be visible. Only at 0 do we consider the object to be 199 // invisible. The invisible value we use (HIDDEN vs. COLLAPSE) depends on the specified from/to values. 200 double fromVal = from == VISIBLE ? 1. : 0.; 201 double toVal = to == VISIBLE ? 1. : 0.; 202 if (fromVal == toVal) 203 return to; 204 double result = blendFunc(anim, fromVal, toVal, progress); 205 return result > 0. ? VISIBLE : (to != VISIBLE ? to : from); 206 } 207 208 static inline LengthBox blendFunc(const AnimationBase* anim, const LengthBox& from, const LengthBox& to, double progress) 209 { 210 // Length types have to match to animate 211 if (from.top().type() != to.top().type() 212 || from.right().type() != to.right().type() 213 || from.bottom().type() != to.bottom().type() 214 || from.left().type() != to.left().type()) 215 return to; 216 217 LengthBox result(blendFunc(anim, from.top(), to.top(), progress), 218 blendFunc(anim, from.right(), to.right(), progress), 219 blendFunc(anim, from.bottom(), to.bottom(), progress), 220 blendFunc(anim, from.left(), to.left(), progress)); 221 return result; 222 } 223 224 class PropertyWrapperBase; 225 226 static void addShorthandProperties(); 227 static PropertyWrapperBase* wrapperForProperty(int propertyID); 228 229 class PropertyWrapperBase { 230 WTF_MAKE_NONCOPYABLE(PropertyWrapperBase); WTF_MAKE_FAST_ALLOCATED; 231 public: 232 PropertyWrapperBase(int prop) 233 : m_prop(prop) 234 { 235 } 236 237 virtual ~PropertyWrapperBase() { } 238 239 virtual bool isShorthandWrapper() const { return false; } 240 virtual bool equals(const RenderStyle* a, const RenderStyle* b) const = 0; 241 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const = 0; 242 243 int property() const { return m_prop; } 244 245 #if USE(ACCELERATED_COMPOSITING) 246 virtual bool animationIsAccelerated() const { return false; } 247 #endif 248 249 private: 250 int m_prop; 251 }; 252 253 template <typename T> 254 class PropertyWrapperGetter : public PropertyWrapperBase { 255 public: 256 PropertyWrapperGetter(int prop, T (RenderStyle::*getter)() const) 257 : PropertyWrapperBase(prop) 258 , m_getter(getter) 259 { 260 } 261 262 virtual bool equals(const RenderStyle* a, const RenderStyle* b) const 263 { 264 // If the style pointers are the same, don't bother doing the test. 265 // If either is null, return false. If both are null, return true. 266 if ((!a && !b) || a == b) 267 return true; 268 if (!a || !b) 269 return false; 270 return (a->*m_getter)() == (b->*m_getter)(); 271 } 272 273 protected: 274 T (RenderStyle::*m_getter)() const; 275 }; 276 277 template <typename T> 278 class PropertyWrapper : public PropertyWrapperGetter<T> { 279 public: 280 PropertyWrapper(int prop, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T)) 281 : PropertyWrapperGetter<T>(prop, getter) 282 , m_setter(setter) 283 { 284 } 285 286 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const 287 { 288 (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<T>::m_getter)(), (b->*PropertyWrapperGetter<T>::m_getter)(), progress)); 289 } 290 291 protected: 292 void (RenderStyle::*m_setter)(T); 293 }; 294 295 #if USE(ACCELERATED_COMPOSITING) 296 class PropertyWrapperAcceleratedOpacity : public PropertyWrapper<float> { 297 public: 298 PropertyWrapperAcceleratedOpacity() 299 : PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity) 300 { 301 } 302 303 virtual bool animationIsAccelerated() const { return true; } 304 305 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const 306 { 307 float fromOpacity = a->opacity(); 308 309 // This makes sure we put the object being animated into a RenderLayer during the animation 310 dst->setOpacity(blendFunc(anim, (fromOpacity == 1) ? 0.999999f : fromOpacity, b->opacity(), progress)); 311 } 312 }; 313 314 class PropertyWrapperAcceleratedTransform : public PropertyWrapper<const TransformOperations&> { 315 public: 316 PropertyWrapperAcceleratedTransform() 317 : PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform) 318 { 319 } 320 321 virtual bool animationIsAccelerated() const { return true; } 322 323 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const 324 { 325 dst->setTransform(blendFunc(anim, a->transform(), b->transform(), progress)); 326 } 327 }; 328 #endif // USE(ACCELERATED_COMPOSITING) 329 330 class PropertyWrapperShadow : public PropertyWrapperBase { 331 public: 332 PropertyWrapperShadow(int prop, const ShadowData* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(ShadowData*, bool)) 333 : PropertyWrapperBase(prop) 334 , m_getter(getter) 335 , m_setter(setter) 336 { 337 } 338 339 virtual bool equals(const RenderStyle* a, const RenderStyle* b) const 340 { 341 const ShadowData* shadowA = (a->*m_getter)(); 342 const ShadowData* shadowB = (b->*m_getter)(); 343 344 while (true) { 345 if (!shadowA && !shadowB) // end of both lists 346 return true; 347 348 if (!shadowA || !shadowB) // end of just one of the lists 349 return false; 350 351 if (*shadowA != *shadowB) 352 return false; 353 354 shadowA = shadowA->next(); 355 shadowB = shadowB->next(); 356 } 357 358 return true; 359 } 360 361 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const 362 { 363 const ShadowData* shadowA = (a->*m_getter)(); 364 const ShadowData* shadowB = (b->*m_getter)(); 365 ShadowData defaultShadowData(0, 0, 0, 0, Normal, property() == CSSPropertyWebkitBoxShadow, Color::transparent); 366 ShadowData defaultInsetShadowData(0, 0, 0, 0, Inset, property() == CSSPropertyWebkitBoxShadow, Color::transparent); 367 368 ShadowData* newShadowData = 0; 369 ShadowData* lastShadow = 0; 370 371 while (shadowA || shadowB) { 372 const ShadowData* srcShadow = shadowA ? shadowA : (shadowB->style() == Inset ? &defaultInsetShadowData : &defaultShadowData); 373 const ShadowData* dstShadow = shadowB ? shadowB : (shadowA->style() == Inset ? &defaultInsetShadowData : &defaultShadowData); 374 375 ShadowData* blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress); 376 if (!lastShadow) 377 newShadowData = blendedShadow; 378 else 379 lastShadow->setNext(blendedShadow); 380 381 lastShadow = blendedShadow; 382 383 shadowA = shadowA ? shadowA->next() : 0; 384 shadowB = shadowB ? shadowB->next() : 0; 385 } 386 387 (dst->*m_setter)(newShadowData, false); 388 } 389 390 private: 391 const ShadowData* (RenderStyle::*m_getter)() const; 392 void (RenderStyle::*m_setter)(ShadowData*, bool); 393 }; 394 395 class PropertyWrapperMaybeInvalidColor : public PropertyWrapperBase { 396 public: 397 PropertyWrapperMaybeInvalidColor(int prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) 398 : PropertyWrapperBase(prop) 399 , m_getter(getter) 400 , m_setter(setter) 401 { 402 } 403 404 virtual bool equals(const RenderStyle* a, const RenderStyle* b) const 405 { 406 Color fromColor = (a->*m_getter)(); 407 Color toColor = (b->*m_getter)(); 408 409 if (!fromColor.isValid() && !toColor.isValid()) 410 return true; 411 412 if (!fromColor.isValid()) 413 fromColor = a->color(); 414 if (!toColor.isValid()) 415 toColor = b->color(); 416 417 return fromColor == toColor; 418 } 419 420 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const 421 { 422 Color fromColor = (a->*m_getter)(); 423 Color toColor = (b->*m_getter)(); 424 425 if (!fromColor.isValid() && !toColor.isValid()) 426 return; 427 428 if (!fromColor.isValid()) 429 fromColor = a->color(); 430 if (!toColor.isValid()) 431 toColor = b->color(); 432 (dst->*m_setter)(blendFunc(anim, fromColor, toColor, progress)); 433 } 434 435 private: 436 const Color& (RenderStyle::*m_getter)() const; 437 void (RenderStyle::*m_setter)(const Color&); 438 }; 439 440 // Wrapper base class for an animatable property in a FillLayer 441 class FillLayerPropertyWrapperBase { 442 public: 443 FillLayerPropertyWrapperBase() 444 { 445 } 446 447 virtual ~FillLayerPropertyWrapperBase() { } 448 449 virtual bool equals(const FillLayer* a, const FillLayer* b) const = 0; 450 virtual void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const = 0; 451 }; 452 453 template <typename T> 454 class FillLayerPropertyWrapperGetter : public FillLayerPropertyWrapperBase { 455 WTF_MAKE_NONCOPYABLE(FillLayerPropertyWrapperGetter); 456 public: 457 FillLayerPropertyWrapperGetter(T (FillLayer::*getter)() const) 458 : m_getter(getter) 459 { 460 } 461 462 virtual bool equals(const FillLayer* a, const FillLayer* b) const 463 { 464 // If the style pointers are the same, don't bother doing the test. 465 // If either is null, return false. If both are null, return true. 466 if ((!a && !b) || a == b) 467 return true; 468 if (!a || !b) 469 return false; 470 return (a->*m_getter)() == (b->*m_getter)(); 471 } 472 473 protected: 474 T (FillLayer::*m_getter)() const; 475 }; 476 477 template <typename T> 478 class FillLayerPropertyWrapper : public FillLayerPropertyWrapperGetter<T> { 479 public: 480 FillLayerPropertyWrapper(T (FillLayer::*getter)() const, void (FillLayer::*setter)(T)) 481 : FillLayerPropertyWrapperGetter<T>(getter) 482 , m_setter(setter) 483 { 484 } 485 486 virtual void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const 487 { 488 (dst->*m_setter)(blendFunc(anim, (a->*FillLayerPropertyWrapperGetter<T>::m_getter)(), (b->*FillLayerPropertyWrapperGetter<T>::m_getter)(), progress)); 489 } 490 491 protected: 492 void (FillLayer::*m_setter)(T); 493 }; 494 495 496 class FillLayersPropertyWrapper : public PropertyWrapperBase { 497 public: 498 typedef const FillLayer* (RenderStyle::*LayersGetter)() const; 499 typedef FillLayer* (RenderStyle::*LayersAccessor)(); 500 501 FillLayersPropertyWrapper(int prop, LayersGetter getter, LayersAccessor accessor) 502 : PropertyWrapperBase(prop) 503 , m_layersGetter(getter) 504 , m_layersAccessor(accessor) 505 { 506 switch (prop) { 507 case CSSPropertyBackgroundPositionX: 508 case CSSPropertyWebkitMaskPositionX: 509 m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<Length>(&FillLayer::xPosition, &FillLayer::setXPosition); 510 break; 511 case CSSPropertyBackgroundPositionY: 512 case CSSPropertyWebkitMaskPositionY: 513 m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<Length>(&FillLayer::yPosition, &FillLayer::setYPosition); 514 break; 515 case CSSPropertyBackgroundSize: 516 case CSSPropertyWebkitBackgroundSize: 517 case CSSPropertyWebkitMaskSize: 518 m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<LengthSize>(&FillLayer::sizeLength, &FillLayer::setSizeLength); 519 break; 520 } 521 } 522 523 virtual bool equals(const RenderStyle* a, const RenderStyle* b) const 524 { 525 const FillLayer* fromLayer = (a->*m_layersGetter)(); 526 const FillLayer* toLayer = (b->*m_layersGetter)(); 527 528 while (fromLayer && toLayer) { 529 if (!m_fillLayerPropertyWrapper->equals(fromLayer, toLayer)) 530 return false; 531 532 fromLayer = fromLayer->next(); 533 toLayer = toLayer->next(); 534 } 535 536 return true; 537 } 538 539 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const 540 { 541 const FillLayer* aLayer = (a->*m_layersGetter)(); 542 const FillLayer* bLayer = (b->*m_layersGetter)(); 543 FillLayer* dstLayer = (dst->*m_layersAccessor)(); 544 545 while (aLayer && bLayer && dstLayer) { 546 m_fillLayerPropertyWrapper->blend(anim, dstLayer, aLayer, bLayer, progress); 547 aLayer = aLayer->next(); 548 bLayer = bLayer->next(); 549 dstLayer = dstLayer->next(); 550 } 551 } 552 553 private: 554 FillLayerPropertyWrapperBase* m_fillLayerPropertyWrapper; 555 556 LayersGetter m_layersGetter; 557 LayersAccessor m_layersAccessor; 558 }; 559 560 class ShorthandPropertyWrapper : public PropertyWrapperBase { 561 public: 562 ShorthandPropertyWrapper(int property, const CSSPropertyLonghand& longhand) 563 : PropertyWrapperBase(property) 564 { 565 for (unsigned i = 0; i < longhand.length(); ++i) { 566 PropertyWrapperBase* wrapper = wrapperForProperty(longhand.properties()[i]); 567 if (wrapper) 568 m_propertyWrappers.append(wrapper); 569 } 570 } 571 572 virtual bool isShorthandWrapper() const { return true; } 573 574 virtual bool equals(const RenderStyle* a, const RenderStyle* b) const 575 { 576 Vector<PropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end(); 577 for (Vector<PropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it) { 578 if (!(*it)->equals(a, b)) 579 return false; 580 } 581 return true; 582 } 583 584 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const 585 { 586 Vector<PropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end(); 587 for (Vector<PropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it) 588 (*it)->blend(anim, dst, a, b, progress); 589 } 590 591 const Vector<PropertyWrapperBase*> propertyWrappers() const { return m_propertyWrappers; } 592 593 private: 594 Vector<PropertyWrapperBase*> m_propertyWrappers; 595 }; 596 597 598 static Vector<PropertyWrapperBase*>* gPropertyWrappers = 0; 599 static int gPropertyWrapperMap[numCSSProperties]; 600 601 static const int cInvalidPropertyWrapperIndex = -1; 602 603 604 void AnimationBase::ensurePropertyMap() 605 { 606 // FIXME: This data is never destroyed. Maybe we should ref count it and toss it when the last AnimationController is destroyed? 607 if (gPropertyWrappers == 0) { 608 gPropertyWrappers = new Vector<PropertyWrapperBase*>(); 609 610 // build the list of property wrappers to do the comparisons and blends 611 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyLeft, &RenderStyle::left, &RenderStyle::setLeft)); 612 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyRight, &RenderStyle::right, &RenderStyle::setRight)); 613 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyTop, &RenderStyle::top, &RenderStyle::setTop)); 614 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyBottom, &RenderStyle::bottom, &RenderStyle::setBottom)); 615 616 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWidth, &RenderStyle::width, &RenderStyle::setWidth)); 617 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMinWidth, &RenderStyle::minWidth, &RenderStyle::setMinWidth)); 618 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMaxWidth, &RenderStyle::maxWidth, &RenderStyle::setMaxWidth)); 619 620 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyHeight, &RenderStyle::height, &RenderStyle::setHeight)); 621 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMinHeight, &RenderStyle::minHeight, &RenderStyle::setMinHeight)); 622 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMaxHeight, &RenderStyle::maxHeight, &RenderStyle::setMaxHeight)); 623 624 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderLeftWidth, &RenderStyle::borderLeftWidth, &RenderStyle::setBorderLeftWidth)); 625 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderRightWidth, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth)); 626 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderTopWidth, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth)); 627 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderBottomWidth, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth)); 628 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginLeft, &RenderStyle::marginLeft, &RenderStyle::setMarginLeft)); 629 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginRight, &RenderStyle::marginRight, &RenderStyle::setMarginRight)); 630 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginTop, &RenderStyle::marginTop, &RenderStyle::setMarginTop)); 631 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginBottom, &RenderStyle::marginBottom, &RenderStyle::setMarginBottom)); 632 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingLeft, &RenderStyle::paddingLeft, &RenderStyle::setPaddingLeft)); 633 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingRight, &RenderStyle::paddingRight, &RenderStyle::setPaddingRight)); 634 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingTop, &RenderStyle::paddingTop, &RenderStyle::setPaddingTop)); 635 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingBottom, &RenderStyle::paddingBottom, &RenderStyle::setPaddingBottom)); 636 gPropertyWrappers->append(new PropertyWrapper<const Color&>(CSSPropertyColor, &RenderStyle::color, &RenderStyle::setColor)); 637 638 gPropertyWrappers->append(new PropertyWrapper<const Color&>(CSSPropertyBackgroundColor, &RenderStyle::backgroundColor, &RenderStyle::setBackgroundColor)); 639 640 gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionX, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers)); 641 gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionY, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers)); 642 gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers)); 643 gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyWebkitBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers)); 644 645 gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionX, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers)); 646 gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionY, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers)); 647 gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyWebkitMaskSize, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers)); 648 649 gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyFontSize, &RenderStyle::fontSize, &RenderStyle::setBlendedFontSize)); 650 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnRuleWidth, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth)); 651 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap)); 652 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnCount, &RenderStyle::columnCount, &RenderStyle::setColumnCount)); 653 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitColumnWidth, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth)); 654 gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyWebkitBorderHorizontalSpacing, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing)); 655 gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyWebkitBorderVerticalSpacing, &RenderStyle::verticalBorderSpacing, &RenderStyle::setVerticalBorderSpacing)); 656 gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyZIndex, &RenderStyle::zIndex, &RenderStyle::setZIndex)); 657 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyLineHeight, &RenderStyle::lineHeight, &RenderStyle::setLineHeight)); 658 gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyOutlineOffset, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset)); 659 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyOutlineWidth, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth)); 660 gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyLetterSpacing, &RenderStyle::letterSpacing, &RenderStyle::setLetterSpacing)); 661 gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyWordSpacing, &RenderStyle::wordSpacing, &RenderStyle::setWordSpacing)); 662 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyTextIndent, &RenderStyle::textIndent, &RenderStyle::setTextIndent)); 663 664 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitPerspective, &RenderStyle::perspective, &RenderStyle::setPerspective)); 665 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginX, &RenderStyle::perspectiveOriginX, &RenderStyle::setPerspectiveOriginX)); 666 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginY, &RenderStyle::perspectiveOriginY, &RenderStyle::setPerspectiveOriginY)); 667 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX)); 668 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY)); 669 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitTransformOriginZ, &RenderStyle::transformOriginZ, &RenderStyle::setTransformOriginZ)); 670 gPropertyWrappers->append(new PropertyWrapper<const LengthSize&>(CSSPropertyBorderTopLeftRadius, &RenderStyle::borderTopLeftRadius, &RenderStyle::setBorderTopLeftRadius)); 671 gPropertyWrappers->append(new PropertyWrapper<const LengthSize&>(CSSPropertyBorderTopRightRadius, &RenderStyle::borderTopRightRadius, &RenderStyle::setBorderTopRightRadius)); 672 gPropertyWrappers->append(new PropertyWrapper<const LengthSize&>(CSSPropertyBorderBottomLeftRadius, &RenderStyle::borderBottomLeftRadius, &RenderStyle::setBorderBottomLeftRadius)); 673 gPropertyWrappers->append(new PropertyWrapper<const LengthSize&>(CSSPropertyBorderBottomRightRadius, &RenderStyle::borderBottomRightRadius, &RenderStyle::setBorderBottomRightRadius)); 674 gPropertyWrappers->append(new PropertyWrapper<EVisibility>(CSSPropertyVisibility, &RenderStyle::visibility, &RenderStyle::setVisibility)); 675 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyZoom, &RenderStyle::zoom, &RenderStyle::setZoom)); 676 677 gPropertyWrappers->append(new PropertyWrapper<LengthBox>(CSSPropertyClip, &RenderStyle::clip, &RenderStyle::setClip)); 678 679 #if USE(ACCELERATED_COMPOSITING) 680 gPropertyWrappers->append(new PropertyWrapperAcceleratedOpacity()); 681 gPropertyWrappers->append(new PropertyWrapperAcceleratedTransform()); 682 #else 683 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity)); 684 gPropertyWrappers->append(new PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform)); 685 #endif 686 687 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitColumnRuleColor, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor)); 688 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitTextStrokeColor, &RenderStyle::textStrokeColor, &RenderStyle::setTextStrokeColor)); 689 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitTextFillColor, &RenderStyle::textFillColor, &RenderStyle::setTextFillColor)); 690 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderLeftColor, &RenderStyle::borderLeftColor, &RenderStyle::setBorderLeftColor)); 691 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderRightColor, &RenderStyle::borderRightColor, &RenderStyle::setBorderRightColor)); 692 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderTopColor, &RenderStyle::borderTopColor, &RenderStyle::setBorderTopColor)); 693 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderBottomColor, &RenderStyle::borderBottomColor, &RenderStyle::setBorderBottomColor)); 694 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyOutlineColor, &RenderStyle::outlineColor, &RenderStyle::setOutlineColor)); 695 696 gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow)); 697 gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyWebkitBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow)); 698 gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyTextShadow, &RenderStyle::textShadow, &RenderStyle::setTextShadow)); 699 700 #if ENABLE(SVG) 701 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyFillOpacity, &RenderStyle::fillOpacity, &RenderStyle::setFillOpacity)); 702 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyFloodOpacity, &RenderStyle::floodOpacity, &RenderStyle::setFloodOpacity)); 703 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyStrokeOpacity, &RenderStyle::strokeOpacity, &RenderStyle::setStrokeOpacity)); 704 #endif 705 706 // TODO: 707 // 708 // CSSPropertyVerticalAlign 709 // 710 // Compound properties that have components that should be animatable: 711 // 712 // CSSPropertyWebkitColumns 713 // CSSPropertyWebkitBoxReflect 714 715 // Make sure unused slots have a value 716 for (unsigned int i = 0; i < static_cast<unsigned int>(numCSSProperties); ++i) 717 gPropertyWrapperMap[i] = cInvalidPropertyWrapperIndex; 718 719 // First we put the non-shorthand property wrappers into the map, so the shorthand-building 720 // code can find them. 721 size_t n = gPropertyWrappers->size(); 722 for (unsigned int i = 0; i < n; ++i) { 723 ASSERT((*gPropertyWrappers)[i]->property() - firstCSSProperty < numCSSProperties); 724 gPropertyWrapperMap[(*gPropertyWrappers)[i]->property() - firstCSSProperty] = i; 725 } 726 727 // Now add the shorthand wrappers. 728 addShorthandProperties(); 729 } 730 } 731 732 static void addPropertyWrapper(int propertyID, PropertyWrapperBase* wrapper) 733 { 734 int propIndex = propertyID - firstCSSProperty; 735 736 ASSERT(gPropertyWrapperMap[propIndex] == cInvalidPropertyWrapperIndex); 737 738 unsigned wrapperIndex = gPropertyWrappers->size(); 739 gPropertyWrappers->append(wrapper); 740 gPropertyWrapperMap[propIndex] = wrapperIndex; 741 } 742 743 static void addShorthandProperties() 744 { 745 static const int animatableShorthandProperties[] = { 746 CSSPropertyBackground, // for background-color, background-position 747 CSSPropertyBackgroundPosition, 748 CSSPropertyWebkitMask, // for mask-position 749 CSSPropertyWebkitMaskPosition, 750 CSSPropertyBorderTop, CSSPropertyBorderRight, CSSPropertyBorderBottom, CSSPropertyBorderLeft, 751 CSSPropertyBorderColor, 752 CSSPropertyBorderRadius, 753 CSSPropertyBorderWidth, 754 CSSPropertyBorder, 755 CSSPropertyBorderSpacing, 756 CSSPropertyMargin, 757 CSSPropertyOutline, 758 CSSPropertyPadding, 759 CSSPropertyWebkitTextStroke, 760 CSSPropertyWebkitColumnRule, 761 CSSPropertyWebkitBorderRadius, 762 CSSPropertyWebkitTransformOrigin 763 }; 764 765 for (size_t i = 0; i < WTF_ARRAY_LENGTH(animatableShorthandProperties); ++i) { 766 int propertyID = animatableShorthandProperties[i]; 767 CSSPropertyLonghand longhand = longhandForProperty(propertyID); 768 if (longhand.length() > 0) 769 addPropertyWrapper(propertyID, new ShorthandPropertyWrapper(propertyID, longhand)); 770 } 771 772 // 'font' is not in the shorthand map. 773 static const int animatableFontProperties[] = { 774 CSSPropertyFontSize, 775 CSSPropertyFontWeight 776 }; 777 778 CSSPropertyLonghand fontLonghand(animatableFontProperties, WTF_ARRAY_LENGTH(animatableFontProperties)); 779 addPropertyWrapper(CSSPropertyFont, new ShorthandPropertyWrapper(CSSPropertyFont, fontLonghand)); 780 } 781 782 static PropertyWrapperBase* wrapperForProperty(int propertyID) 783 { 784 int propIndex = propertyID - firstCSSProperty; 785 if (propIndex >= 0 && propIndex < numCSSProperties) { 786 int wrapperIndex = gPropertyWrapperMap[propIndex]; 787 if (wrapperIndex >= 0) 788 return (*gPropertyWrappers)[wrapperIndex]; 789 } 790 return 0; 791 } 792 793 AnimationBase::AnimationBase(const Animation* transition, RenderObject* renderer, CompositeAnimation* compAnim) 794 : m_animState(AnimationStateNew) 795 , m_isAnimating(false) 796 , m_startTime(0) 797 , m_pauseTime(-1) 798 , m_requestedStartTime(0) 799 , m_object(renderer) 800 , m_animation(const_cast<Animation*>(transition)) 801 , m_compAnim(compAnim) 802 , m_isAccelerated(false) 803 , m_transformFunctionListValid(false) 804 , m_nextIterationDuration(-1) 805 { 806 // Compute the total duration 807 m_totalDuration = -1; 808 if (m_animation->iterationCount() > 0) 809 m_totalDuration = m_animation->duration() * m_animation->iterationCount(); 810 } 811 812 bool AnimationBase::propertiesEqual(int prop, const RenderStyle* a, const RenderStyle* b) 813 { 814 ensurePropertyMap(); 815 if (prop == cAnimateAll) { 816 size_t n = gPropertyWrappers->size(); 817 for (unsigned int i = 0; i < n; ++i) { 818 PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i]; 819 // No point comparing shorthand wrappers for 'all'. 820 if (!wrapper->isShorthandWrapper() && !wrapper->equals(a, b)) 821 return false; 822 } 823 } else { 824 PropertyWrapperBase* wrapper = wrapperForProperty(prop); 825 if (wrapper) 826 return wrapper->equals(a, b); 827 } 828 return true; 829 } 830 831 int AnimationBase::getPropertyAtIndex(int i, bool& isShorthand) 832 { 833 ensurePropertyMap(); 834 if (i < 0 || i >= static_cast<int>(gPropertyWrappers->size())) 835 return CSSPropertyInvalid; 836 837 PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i]; 838 isShorthand = wrapper->isShorthandWrapper(); 839 return wrapper->property(); 840 } 841 842 int AnimationBase::getNumProperties() 843 { 844 ensurePropertyMap(); 845 return gPropertyWrappers->size(); 846 } 847 848 // Returns true if we need to start animation timers 849 bool AnimationBase::blendProperties(const AnimationBase* anim, int prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) 850 { 851 ASSERT(prop != cAnimateAll); 852 853 ensurePropertyMap(); 854 PropertyWrapperBase* wrapper = wrapperForProperty(prop); 855 if (wrapper) { 856 wrapper->blend(anim, dst, a, b, progress); 857 #if USE(ACCELERATED_COMPOSITING) 858 return !wrapper->animationIsAccelerated() || !anim->isAccelerated(); 859 #else 860 return true; 861 #endif 862 } 863 864 return false; 865 } 866 867 #if USE(ACCELERATED_COMPOSITING) 868 bool AnimationBase::animationOfPropertyIsAccelerated(int prop) 869 { 870 ensurePropertyMap(); 871 PropertyWrapperBase* wrapper = wrapperForProperty(prop); 872 return wrapper ? wrapper->animationIsAccelerated() : false; 873 } 874 #endif 875 876 static bool gatherEnclosingShorthandProperties(int property, PropertyWrapperBase* wrapper, HashSet<int>& propertySet) 877 { 878 if (!wrapper->isShorthandWrapper()) 879 return false; 880 881 ShorthandPropertyWrapper* shorthandWrapper = static_cast<ShorthandPropertyWrapper*>(wrapper); 882 883 bool contained = false; 884 for (size_t i = 0; i < shorthandWrapper->propertyWrappers().size(); ++i) { 885 PropertyWrapperBase* currWrapper = shorthandWrapper->propertyWrappers()[i]; 886 887 if (gatherEnclosingShorthandProperties(property, currWrapper, propertySet) || currWrapper->property() == property) 888 contained = true; 889 } 890 891 if (contained) 892 propertySet.add(wrapper->property()); 893 894 return contained; 895 } 896 897 // Note: this is inefficient. It's only called from pauseTransitionAtTime(). 898 HashSet<int> AnimationBase::animatableShorthandsAffectingProperty(int property) 899 { 900 ensurePropertyMap(); 901 902 HashSet<int> foundProperties; 903 for (int i = 0; i < getNumProperties(); ++i) 904 gatherEnclosingShorthandProperties(property, (*gPropertyWrappers)[i], foundProperties); 905 906 return foundProperties; 907 } 908 909 void AnimationBase::setNeedsStyleRecalc(Node* node) 910 { 911 ASSERT(!node || (node->document() && !node->document()->inPageCache())); 912 if (node) 913 node->setNeedsStyleRecalc(SyntheticStyleChange); 914 } 915 916 double AnimationBase::duration() const 917 { 918 return m_animation->duration(); 919 } 920 921 bool AnimationBase::playStatePlaying() const 922 { 923 return m_animation->playState() == AnimPlayStatePlaying; 924 } 925 926 bool AnimationBase::animationsMatch(const Animation* anim) const 927 { 928 return m_animation->animationsMatch(anim); 929 } 930 931 void AnimationBase::updateStateMachine(AnimStateInput input, double param) 932 { 933 if (!m_compAnim) 934 return; 935 936 // If we get AnimationStateInputRestartAnimation then we force a new animation, regardless of state. 937 if (input == AnimationStateInputMakeNew) { 938 if (m_animState == AnimationStateStartWaitStyleAvailable) 939 m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this); 940 m_animState = AnimationStateNew; 941 m_startTime = 0; 942 m_pauseTime = -1; 943 m_requestedStartTime = 0; 944 m_nextIterationDuration = -1; 945 endAnimation(); 946 return; 947 } 948 949 if (input == AnimationStateInputRestartAnimation) { 950 if (m_animState == AnimationStateStartWaitStyleAvailable) 951 m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this); 952 m_animState = AnimationStateNew; 953 m_startTime = 0; 954 m_pauseTime = -1; 955 m_requestedStartTime = 0; 956 m_nextIterationDuration = -1; 957 endAnimation(); 958 959 if (!paused()) 960 updateStateMachine(AnimationStateInputStartAnimation, -1); 961 return; 962 } 963 964 if (input == AnimationStateInputEndAnimation) { 965 if (m_animState == AnimationStateStartWaitStyleAvailable) 966 m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this); 967 m_animState = AnimationStateDone; 968 endAnimation(); 969 return; 970 } 971 972 if (input == AnimationStateInputPauseOverride) { 973 if (m_animState == AnimationStateStartWaitResponse) { 974 // If we are in AnimationStateStartWaitResponse, the animation will get canceled before 975 // we get a response, so move to the next state. 976 endAnimation(); 977 updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); 978 } 979 return; 980 } 981 982 if (input == AnimationStateInputResumeOverride) { 983 if (m_animState == AnimationStateLooping || m_animState == AnimationStateEnding) { 984 // Start the animation 985 startAnimation(beginAnimationUpdateTime() - m_startTime); 986 } 987 return; 988 } 989 990 // Execute state machine 991 switch (m_animState) { 992 case AnimationStateNew: 993 ASSERT(input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning || input == AnimationStateInputPlayStatePaused); 994 if (input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning) { 995 m_requestedStartTime = beginAnimationUpdateTime(); 996 m_animState = AnimationStateStartWaitTimer; 997 } 998 break; 999 case AnimationStateStartWaitTimer: 1000 ASSERT(input == AnimationStateInputStartTimerFired || input == AnimationStateInputPlayStatePaused); 1001 1002 if (input == AnimationStateInputStartTimerFired) { 1003 ASSERT(param >= 0); 1004 // Start timer has fired, tell the animation to start and wait for it to respond with start time 1005 m_animState = AnimationStateStartWaitStyleAvailable; 1006 m_compAnim->animationController()->addToAnimationsWaitingForStyle(this); 1007 1008 // Trigger a render so we can start the animation 1009 if (m_object) 1010 m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node()); 1011 } else { 1012 ASSERT(!paused()); 1013 // We're waiting for the start timer to fire and we got a pause. Cancel the timer, pause and wait 1014 m_pauseTime = beginAnimationUpdateTime(); 1015 m_animState = AnimationStatePausedWaitTimer; 1016 } 1017 break; 1018 case AnimationStateStartWaitStyleAvailable: 1019 ASSERT(input == AnimationStateInputStyleAvailable || input == AnimationStateInputPlayStatePaused); 1020 1021 if (input == AnimationStateInputStyleAvailable) { 1022 // Start timer has fired, tell the animation to start and wait for it to respond with start time 1023 m_animState = AnimationStateStartWaitResponse; 1024 1025 overrideAnimations(); 1026 1027 // Start the animation 1028 if (overridden()) { 1029 // We won't try to start accelerated animations if we are overridden and 1030 // just move on to the next state. 1031 m_animState = AnimationStateStartWaitResponse; 1032 m_isAccelerated = false; 1033 updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); 1034 } else { 1035 double timeOffset = 0; 1036 // If the value for 'animation-delay' is negative then the animation appears to have started in the past. 1037 if (m_animation->delay() < 0) 1038 timeOffset = -m_animation->delay(); 1039 bool started = startAnimation(timeOffset); 1040 1041 m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started); 1042 m_isAccelerated = started; 1043 } 1044 } else { 1045 // We're waiting for the style to be available and we got a pause. Pause and wait 1046 m_pauseTime = beginAnimationUpdateTime(); 1047 m_animState = AnimationStatePausedWaitStyleAvailable; 1048 } 1049 break; 1050 case AnimationStateStartWaitResponse: 1051 ASSERT(input == AnimationStateInputStartTimeSet || input == AnimationStateInputPlayStatePaused); 1052 1053 if (input == AnimationStateInputStartTimeSet) { 1054 ASSERT(param >= 0); 1055 // We have a start time, set it, unless the startTime is already set 1056 if (m_startTime <= 0) { 1057 m_startTime = param; 1058 // If the value for 'animation-delay' is negative then the animation appears to have started in the past. 1059 if (m_animation->delay() < 0) 1060 m_startTime += m_animation->delay(); 1061 } 1062 1063 // Now that we know the start time, fire the start event. 1064 onAnimationStart(0); // The elapsedTime is 0. 1065 1066 // Decide whether to go into looping or ending state 1067 goIntoEndingOrLoopingState(); 1068 1069 // Dispatch updateStyleIfNeeded so we can start the animation 1070 if (m_object) 1071 m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node()); 1072 } else { 1073 // We are pausing while waiting for a start response. Cancel the animation and wait. When 1074 // we unpause, we will act as though the start timer just fired 1075 m_pauseTime = beginAnimationUpdateTime(); 1076 pauseAnimation(beginAnimationUpdateTime() - m_startTime); 1077 m_animState = AnimationStatePausedWaitResponse; 1078 } 1079 break; 1080 case AnimationStateLooping: 1081 ASSERT(input == AnimationStateInputLoopTimerFired || input == AnimationStateInputPlayStatePaused); 1082 1083 if (input == AnimationStateInputLoopTimerFired) { 1084 ASSERT(param >= 0); 1085 // Loop timer fired, loop again or end. 1086 onAnimationIteration(param); 1087 1088 // Decide whether to go into looping or ending state 1089 goIntoEndingOrLoopingState(); 1090 } else { 1091 // We are pausing while running. Cancel the animation and wait 1092 m_pauseTime = beginAnimationUpdateTime(); 1093 pauseAnimation(beginAnimationUpdateTime() - m_startTime); 1094 m_animState = AnimationStatePausedRun; 1095 } 1096 break; 1097 case AnimationStateEnding: 1098 ASSERT(input == AnimationStateInputEndTimerFired || input == AnimationStateInputPlayStatePaused); 1099 1100 if (input == AnimationStateInputEndTimerFired) { 1101 1102 ASSERT(param >= 0); 1103 // End timer fired, finish up 1104 onAnimationEnd(param); 1105 1106 m_animState = AnimationStateDone; 1107 1108 if (m_object) { 1109 if (m_animation->fillsForwards()) 1110 m_animState = AnimationStateFillingForwards; 1111 else 1112 resumeOverriddenAnimations(); 1113 1114 // Fire off another style change so we can set the final value 1115 m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node()); 1116 } 1117 } else { 1118 // We are pausing while running. Cancel the animation and wait 1119 m_pauseTime = beginAnimationUpdateTime(); 1120 pauseAnimation(beginAnimationUpdateTime() - m_startTime); 1121 m_animState = AnimationStatePausedRun; 1122 } 1123 // |this| may be deleted here 1124 break; 1125 case AnimationStatePausedWaitTimer: 1126 ASSERT(input == AnimationStateInputPlayStateRunning); 1127 ASSERT(paused()); 1128 // Update the times 1129 m_startTime += beginAnimationUpdateTime() - m_pauseTime; 1130 m_pauseTime = -1; 1131 1132 // we were waiting for the start timer to fire, go back and wait again 1133 m_animState = AnimationStateNew; 1134 updateStateMachine(AnimationStateInputStartAnimation, 0); 1135 break; 1136 case AnimationStatePausedWaitResponse: 1137 case AnimationStatePausedWaitStyleAvailable: 1138 case AnimationStatePausedRun: 1139 // We treat these two cases the same. The only difference is that, when we are in 1140 // AnimationStatePausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation. 1141 // When the AnimationStateInputStartTimeSet comes in and we were in AnimationStatePausedRun, we will notice 1142 // that we have already set the startTime and will ignore it. 1143 ASSERT(input == AnimationStateInputPlayStateRunning || input == AnimationStateInputStartTimeSet || input == AnimationStateInputStyleAvailable); 1144 ASSERT(paused()); 1145 1146 if (input == AnimationStateInputPlayStateRunning) { 1147 // Update the times 1148 if (m_animState == AnimationStatePausedRun) 1149 m_startTime += beginAnimationUpdateTime() - m_pauseTime; 1150 else 1151 m_startTime = 0; 1152 m_pauseTime = -1; 1153 1154 if (m_animState == AnimationStatePausedWaitStyleAvailable) 1155 m_animState = AnimationStateStartWaitStyleAvailable; 1156 else { 1157 // We were either running or waiting for a begin time response from the animation. 1158 // Either way we need to restart the animation (possibly with an offset if we 1159 // had already been running) and wait for it to start. 1160 m_animState = AnimationStateStartWaitResponse; 1161 1162 // Start the animation 1163 if (overridden()) { 1164 // We won't try to start accelerated animations if we are overridden and 1165 // just move on to the next state. 1166 updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); 1167 m_isAccelerated = true; 1168 } else { 1169 bool started = startAnimation(beginAnimationUpdateTime() - m_startTime); 1170 m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started); 1171 m_isAccelerated = started; 1172 } 1173 } 1174 break; 1175 } 1176 1177 if (input == AnimationStateInputStartTimeSet) { 1178 ASSERT(m_animState == AnimationStatePausedWaitResponse); 1179 1180 // We are paused but we got the callback that notifies us that an accelerated animation started. 1181 // We ignore the start time and just move into the paused-run state. 1182 m_animState = AnimationStatePausedRun; 1183 ASSERT(m_startTime == 0); 1184 m_startTime = param; 1185 m_pauseTime += m_startTime; 1186 break; 1187 } 1188 1189 ASSERT(m_animState == AnimationStatePausedWaitStyleAvailable); 1190 // We are paused but we got the callback that notifies us that style has been updated. 1191 // We move to the AnimationStatePausedWaitResponse state 1192 m_animState = AnimationStatePausedWaitResponse; 1193 overrideAnimations(); 1194 break; 1195 case AnimationStateFillingForwards: 1196 case AnimationStateDone: 1197 // We're done. Stay in this state until we are deleted 1198 break; 1199 } 1200 } 1201 1202 void AnimationBase::fireAnimationEventsIfNeeded() 1203 { 1204 if (!m_compAnim) 1205 return; 1206 1207 // If we are waiting for the delay time to expire and it has, go to the next state 1208 if (m_animState != AnimationStateStartWaitTimer && m_animState != AnimationStateLooping && m_animState != AnimationStateEnding) 1209 return; 1210 1211 // We have to make sure to keep a ref to the this pointer, because it could get destroyed 1212 // during an animation callback that might get called. Since the owner is a CompositeAnimation 1213 // and it ref counts this object, we will keep a ref to that instead. That way the AnimationBase 1214 // can still access the resources of its CompositeAnimation as needed. 1215 RefPtr<AnimationBase> protector(this); 1216 RefPtr<CompositeAnimation> compProtector(m_compAnim); 1217 1218 // Check for start timeout 1219 if (m_animState == AnimationStateStartWaitTimer) { 1220 if (beginAnimationUpdateTime() - m_requestedStartTime >= m_animation->delay()) 1221 updateStateMachine(AnimationStateInputStartTimerFired, 0); 1222 return; 1223 } 1224 1225 double elapsedDuration = beginAnimationUpdateTime() - m_startTime; 1226 // FIXME: we need to ensure that elapsedDuration is never < 0. If it is, this suggests that 1227 // we had a recalcStyle() outside of beginAnimationUpdate()/endAnimationUpdate(). 1228 // Also check in getTimeToNextEvent(). 1229 elapsedDuration = max(elapsedDuration, 0.0); 1230 1231 // Check for end timeout 1232 if (m_totalDuration >= 0 && elapsedDuration >= m_totalDuration) { 1233 // We may still be in AnimationStateLooping if we've managed to skip a 1234 // whole iteration, in which case we should jump to the end state. 1235 m_animState = AnimationStateEnding; 1236 1237 // Fire an end event 1238 updateStateMachine(AnimationStateInputEndTimerFired, m_totalDuration); 1239 } else { 1240 // Check for iteration timeout 1241 if (m_nextIterationDuration < 0) { 1242 // Hasn't been set yet, set it 1243 double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration()); 1244 m_nextIterationDuration = elapsedDuration + durationLeft; 1245 } 1246 1247 if (elapsedDuration >= m_nextIterationDuration) { 1248 // Set to the next iteration 1249 double previous = m_nextIterationDuration; 1250 double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration()); 1251 m_nextIterationDuration = elapsedDuration + durationLeft; 1252 1253 // Send the event 1254 updateStateMachine(AnimationStateInputLoopTimerFired, previous); 1255 } 1256 } 1257 } 1258 1259 void AnimationBase::updatePlayState(EAnimPlayState playState) 1260 { 1261 if (!m_compAnim) 1262 return; 1263 1264 // When we get here, we can have one of 4 desired states: running, paused, suspended, paused & suspended. 1265 // The state machine can be in one of two states: running, paused. 1266 // Set the state machine to the desired state. 1267 bool pause = playState == AnimPlayStatePaused || m_compAnim->suspended(); 1268 1269 if (pause == paused() && !isNew()) 1270 return; 1271 1272 updateStateMachine(pause ? AnimationStateInputPlayStatePaused : AnimationStateInputPlayStateRunning, -1); 1273 } 1274 1275 double AnimationBase::timeToNextService() 1276 { 1277 // Returns the time at which next service is required. -1 means no service is required. 0 means 1278 // service is required now, and > 0 means service is required that many seconds in the future. 1279 if (paused() || isNew() || m_animState == AnimationStateFillingForwards) 1280 return -1; 1281 1282 if (m_animState == AnimationStateStartWaitTimer) { 1283 double timeFromNow = m_animation->delay() - (beginAnimationUpdateTime() - m_requestedStartTime); 1284 return max(timeFromNow, 0.0); 1285 } 1286 1287 fireAnimationEventsIfNeeded(); 1288 1289 // In all other cases, we need service right away. 1290 return 0; 1291 } 1292 1293 double AnimationBase::progress(double scale, double offset, const TimingFunction* tf) const 1294 { 1295 if (preActive()) 1296 return 0; 1297 1298 double elapsedTime = getElapsedTime(); 1299 1300 double dur = m_animation->duration(); 1301 if (m_animation->iterationCount() > 0) 1302 dur *= m_animation->iterationCount(); 1303 1304 if (postActive() || !m_animation->duration()) 1305 return 1.0; 1306 if (m_animation->iterationCount() > 0 && elapsedTime >= dur) 1307 return (m_animation->iterationCount() % 2) ? 1.0 : 0.0; 1308 1309 // Compute the fractional time, taking into account direction. 1310 // There is no need to worry about iterations, we assume that we would have 1311 // short circuited above if we were done. 1312 double fractionalTime = elapsedTime / m_animation->duration(); 1313 int integralTime = static_cast<int>(fractionalTime); 1314 fractionalTime -= integralTime; 1315 1316 if ((m_animation->direction() == Animation::AnimationDirectionAlternate) && (integralTime & 1)) 1317 fractionalTime = 1 - fractionalTime; 1318 1319 if (scale != 1 || offset) 1320 fractionalTime = (fractionalTime - offset) * scale; 1321 1322 if (!tf) 1323 tf = m_animation->timingFunction().get(); 1324 1325 if (tf->isCubicBezierTimingFunction()) { 1326 const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(tf); 1327 return solveCubicBezierFunction(ctf->x1(), 1328 ctf->y1(), 1329 ctf->x2(), 1330 ctf->y2(), 1331 fractionalTime, m_animation->duration()); 1332 } else if (tf->isStepsTimingFunction()) { 1333 const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(tf); 1334 return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), fractionalTime); 1335 } else 1336 return fractionalTime; 1337 } 1338 1339 void AnimationBase::getTimeToNextEvent(double& time, bool& isLooping) const 1340 { 1341 // Decide when the end or loop event needs to fire 1342 const double elapsedDuration = max(beginAnimationUpdateTime() - m_startTime, 0.0); 1343 double durationLeft = 0; 1344 double nextIterationTime = m_totalDuration; 1345 1346 if (m_totalDuration < 0 || elapsedDuration < m_totalDuration) { 1347 durationLeft = m_animation->duration() > 0 ? (m_animation->duration() - fmod(elapsedDuration, m_animation->duration())) : 0; 1348 nextIterationTime = elapsedDuration + durationLeft; 1349 } 1350 1351 if (m_totalDuration < 0 || nextIterationTime < m_totalDuration) { 1352 // We are not at the end yet 1353 ASSERT(nextIterationTime > 0); 1354 isLooping = true; 1355 } else { 1356 // We are at the end 1357 isLooping = false; 1358 } 1359 1360 time = durationLeft; 1361 } 1362 1363 void AnimationBase::goIntoEndingOrLoopingState() 1364 { 1365 double t; 1366 bool isLooping; 1367 getTimeToNextEvent(t, isLooping); 1368 m_animState = isLooping ? AnimationStateLooping : AnimationStateEnding; 1369 } 1370 1371 void AnimationBase::freezeAtTime(double t) 1372 { 1373 if (!m_compAnim) 1374 return; 1375 1376 if (!m_startTime) { 1377 // If we haven't started yet, just generate the start event now 1378 m_compAnim->animationController()->receivedStartTimeResponse(currentTime()); 1379 } 1380 1381 ASSERT(m_startTime); // if m_startTime is zero, we haven't started yet, so we'll get a bad pause time. 1382 m_pauseTime = m_startTime + t - m_animation->delay(); 1383 1384 #if USE(ACCELERATED_COMPOSITING) 1385 if (m_object && m_object->hasLayer()) { 1386 RenderLayer* layer = toRenderBoxModelObject(m_object)->layer(); 1387 if (layer->isComposited()) 1388 layer->backing()->suspendAnimations(m_pauseTime); 1389 } 1390 #endif 1391 } 1392 1393 double AnimationBase::beginAnimationUpdateTime() const 1394 { 1395 if (!m_compAnim) 1396 return 0; 1397 1398 return m_compAnim->animationController()->beginAnimationUpdateTime(); 1399 } 1400 1401 double AnimationBase::getElapsedTime() const 1402 { 1403 if (paused()) 1404 return m_pauseTime - m_startTime; 1405 if (m_startTime <= 0) 1406 return 0; 1407 if (postActive()) 1408 return 1; 1409 1410 return beginAnimationUpdateTime() - m_startTime; 1411 } 1412 1413 void AnimationBase::setElapsedTime(double time) 1414 { 1415 // FIXME: implement this method 1416 UNUSED_PARAM(time); 1417 } 1418 1419 void AnimationBase::play() 1420 { 1421 // FIXME: implement this method 1422 } 1423 1424 void AnimationBase::pause() 1425 { 1426 // FIXME: implement this method 1427 } 1428 1429 } // namespace WebCore 1430