2008-08-04 Dean Jackson <dino@apple.com>
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Aug 2008 01:54:46 +0000 (01:54 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Aug 2008 01:54:46 +0000 (01:54 +0000)
        Reviewed by Dave Hyatt.

        Improve AnimationController
        https://bugs.webkit.org/show_bug.cgi?id=19938

        - Transition class is now called Animation
        - new state-based AnimationController that can support CSS Animations
        - add support for -webkit-transition-delay
        - remove -webkit-transition-repeat-count (since it never existed officially)
        - updates the -webkit-transition shorthand to reflect removing repeat count
        - updates the Transition class so that properties can be shared with animations
        - adds a "now" keyword for -webkit-transition-delay
        - adds a new change type for style (changed by animation)
        - adds new event names (although they are not dispatched yet)
        - makes text stroke and text fill colors returned by RenderStyle const

        Tests:
        - manual-tests/transition-left.html
        - manual-tests/transition-delay.html
        - manual-tests/transition-timing-functions.html

        * css/CSSComputedStyleDeclaration.cpp:
        * css/CSSParser.cpp:
        * css/CSSParser.h:
        * css/CSSPropertyNames.in:
        * css/CSSStyleSelector.cpp:
        * css/CSSStyleSelector.h:
        * css/CSSTimingFunctionValue.h:
        * css/CSSValue.h:
        * css/CSSValueKeywords.in:
        * css/WebKitCSSTransformValue.cpp:
        * dom/Document.cpp:
        * dom/Document.h:
        * dom/Element.cpp:
        * dom/EventNames.h:
        * dom/Node.cpp:
        * dom/Node.h:
        * history/CachedPage.cpp:
        * page/AnimationController.cpp:
        * page/AnimationController.h:
        * page/Frame.cpp:
        * rendering/RenderObject.cpp:
        * rendering/RenderWidget.cpp:
        * rendering/style/RenderStyle.cpp:
        * rendering/style/RenderStyle.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@35545 268f45cc-cd09-0410-ab3c-d52691b4dbfc

31 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css/computed-style-expected.txt
LayoutTests/fast/css/computed-style-without-renderer-expected.txt
LayoutTests/svg/css/getComputedStyle-basic-expected.txt
WebCore/ChangeLog
WebCore/css/CSSComputedStyleDeclaration.cpp
WebCore/css/CSSParser.cpp
WebCore/css/CSSParser.h
WebCore/css/CSSPropertyNames.in
WebCore/css/CSSStyleSelector.cpp
WebCore/css/CSSStyleSelector.h
WebCore/css/CSSTimingFunctionValue.h
WebCore/css/CSSValue.h
WebCore/css/CSSValueKeywords.in
WebCore/dom/Document.cpp
WebCore/dom/Document.h
WebCore/dom/Element.cpp
WebCore/dom/EventNames.h
WebCore/dom/Node.cpp
WebCore/dom/Node.h
WebCore/history/CachedPage.cpp
WebCore/manual-tests/transition-delay.html [new file with mode: 0644]
WebCore/manual-tests/transition-left.html [new file with mode: 0644]
WebCore/manual-tests/transition-timing-functions.html [new file with mode: 0644]
WebCore/page/AnimationController.cpp
WebCore/page/AnimationController.h
WebCore/page/Frame.cpp
WebCore/rendering/RenderObject.cpp
WebCore/rendering/RenderWidget.cpp
WebCore/rendering/style/RenderStyle.cpp
WebCore/rendering/style/RenderStyle.h

index 3f9c1e884e9202b7905bc032d2d63d74cb64d0e0..502b803ae5e8727a5a6b1eff3bd4ec125b12ae89 100644 (file)
@@ -1,3 +1,15 @@
+2008-08-04  Dean Jackson  <dino@apple.com>
+
+        Reviewed by Dave Hyatt
+
+        Update expected computed style results for the following bug
+        Improve AnimationController
+        https://bugs.webkit.org/show_bug.cgi?id=19938
+
+        * fast/css/computed-style-expected.txt:
+        * fast/css/computed-style-without-renderer-expected.txt:
+        * svg/css/getComputedStyle-basic-expected.txt:
+
 2008-08-04  Sam Weinig  <sam@webkit.org>
 
         Reviewed by Dan Bernstein.
index a77df294cb6bfde83dff5d45ea643c40ff8fb17e..bad2e928c00bbafb9d1937f5521b434dd4d2bbbd 100644 (file)
@@ -138,6 +138,10 @@ zoom: 1;
 -webkit-transform: none;
 -webkit-transform-origin-x: 392px;
 -webkit-transform-origin-y: 288px;
+-webkit-transition-delay: 0s;
+-webkit-transition-duration: 0s;
+-webkit-transition-property: all;
+-webkit-transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1);
 -webkit-user-drag: auto;
 -webkit-user-modify: read-only;
 -webkit-user-select: text;
index b75caf7dd92fb772e414bd92f9042447195ad996..b43b9756a4cbdcdbc8e47b1e3c840d77d9179077 100644 (file)
@@ -137,6 +137,10 @@ Computed style of an element whose parent's 'display' value is 'none':
     -webkit-transform: none
     -webkit-transform-origin-x: 50%
     -webkit-transform-origin-y: 50%
+    -webkit-transition-delay: 0s
+    -webkit-transition-duration: 0s
+    -webkit-transition-property: all
+    -webkit-transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1)
     -webkit-user-drag: auto
     -webkit-user-modify: read-only
     -webkit-user-select: text
index 6d93c4fae8cb0f06a96358d5b2c64cfe1a378604..32cf88308553a17d498428009446520ff7e19a79 100644 (file)
@@ -274,6 +274,14 @@ rect: style.getPropertyValue(-webkit-transform-origin-x) : 0px
 rect: style.getPropertyCSSValue(-webkit-transform-origin-x) : [object CSSPrimitiveValue]
 rect: style.getPropertyValue(-webkit-transform-origin-y) : 0px
 rect: style.getPropertyCSSValue(-webkit-transform-origin-y) : [object CSSPrimitiveValue]
+rect: style.getPropertyValue(-webkit-transition-delay) : 0s
+rect: style.getPropertyCSSValue(-webkit-transition-delay) : [object CSSValueList]
+rect: style.getPropertyValue(-webkit-transition-duration) : 0s
+rect: style.getPropertyCSSValue(-webkit-transition-duration) : [object CSSValueList]
+rect: style.getPropertyValue(-webkit-transition-property) : all
+rect: style.getPropertyCSSValue(-webkit-transition-property) : [object CSSValueList]
+rect: style.getPropertyValue(-webkit-transition-timing-function) : cubic-bezier(0.25, 0.1, 0.25, 1)
+rect: style.getPropertyCSSValue(-webkit-transition-timing-function) : [object CSSValueList]
 rect: style.getPropertyValue(-webkit-user-drag) : auto
 rect: style.getPropertyCSSValue(-webkit-user-drag) : [object CSSPrimitiveValue]
 rect: style.getPropertyValue(-webkit-user-modify) : read-only
@@ -642,6 +650,14 @@ g: style.getPropertyValue(-webkit-transform-origin-x) : 0px
 g: style.getPropertyCSSValue(-webkit-transform-origin-x) : [object CSSPrimitiveValue]
 g: style.getPropertyValue(-webkit-transform-origin-y) : 0px
 g: style.getPropertyCSSValue(-webkit-transform-origin-y) : [object CSSPrimitiveValue]
+g: style.getPropertyValue(-webkit-transition-delay) : 0s
+g: style.getPropertyCSSValue(-webkit-transition-delay) : [object CSSValueList]
+g: style.getPropertyValue(-webkit-transition-duration) : 0s
+g: style.getPropertyCSSValue(-webkit-transition-duration) : [object CSSValueList]
+g: style.getPropertyValue(-webkit-transition-property) : all
+g: style.getPropertyCSSValue(-webkit-transition-property) : [object CSSValueList]
+g: style.getPropertyValue(-webkit-transition-timing-function) : cubic-bezier(0.25, 0.1, 0.25, 1)
+g: style.getPropertyCSSValue(-webkit-transition-timing-function) : [object CSSValueList]
 g: style.getPropertyValue(-webkit-user-drag) : auto
 g: style.getPropertyCSSValue(-webkit-user-drag) : [object CSSPrimitiveValue]
 g: style.getPropertyValue(-webkit-user-modify) : read-only
index a10a1a2caf479ac96a5d515735eb0a94e180bafd..2b1c7d6bf6004e1d99af229f737c16e76a342cf2 100644 (file)
@@ -1,18 +1,50 @@
-2008-05-12  Mike Belshe  <mike@belshe.com>
+2008-08-04  Dean Jackson  <dino@apple.com>
 
-        Reviewed by Antti.
-        
-        Currently we create two copies of script in the HTMLTokenizer.
-        One copy gets passed into the javascript engine, the other gets
-        stored in the DOM.  Modify the HTMLParser so that it does not
-        chunk the string into it's normal 64K chunks for script code,
-        and modify the HTMLTokenizer to pass that same string into the
-        JS engine.  On some sites (e.g. GMail), which have hundreds of
-        KB of inline JS, this saves more than a MB of RAM to run the page.
-        (Strings use 16bit strings, so 600KB of JS code == 1.2MB of memory)
-
-        * html\HTMLParser.cpp
-        * html\HTMLTokenizer.cpp
+        Reviewed by Dave Hyatt.
+
+        Improve AnimationController
+        https://bugs.webkit.org/show_bug.cgi?id=19938
+
+        - Transition class is now called Animation
+        - new state-based AnimationController that can support CSS Animations
+        - add support for -webkit-transition-delay
+        - remove -webkit-transition-repeat-count (since it never existed officially)
+        - updates the -webkit-transition shorthand to reflect removing repeat count
+        - updates the Transition class so that properties can be shared with animations
+        - adds a "now" keyword for -webkit-transition-delay
+        - adds a new change type for style (changed by animation)
+        - adds new event names (although they are not dispatched yet)
+        - makes text stroke and text fill colors returned by RenderStyle const
+
+        Tests:
+        - manual-tests/transition-left.html
+        - manual-tests/transition-delay.html
+        - manual-tests/transition-timing-functions.html
+
+        * css/CSSComputedStyleDeclaration.cpp:
+        * css/CSSParser.cpp:
+        * css/CSSParser.h:
+        * css/CSSPropertyNames.in:
+        * css/CSSStyleSelector.cpp:
+        * css/CSSStyleSelector.h:
+        * css/CSSTimingFunctionValue.h:
+        * css/CSSValue.h:
+        * css/CSSValueKeywords.in:
+        * css/WebKitCSSTransformValue.cpp:
+        * dom/Document.cpp:
+        * dom/Document.h:
+        * dom/Element.cpp:
+        * dom/EventNames.h:
+        * dom/Node.cpp:
+        * dom/Node.h:
+        * history/CachedPage.cpp:
+        * page/AnimationController.cpp:
+        * page/AnimationController.h:
+        * page/Frame.cpp:
+        * rendering/RenderObject.cpp:
+        * rendering/RenderWidget.cpp:
+        * rendering/style/RenderStyle.cpp:
+        * rendering/style/RenderStyle.h:
 
 2008-08-04  Sam Weinig  <sam@webkit.org>
 
index 8592f212d843ef204cd26bd145846a052d24afc3..b236b1044da0d4b30efeddeb714557facb11bbb4 100644 (file)
@@ -29,6 +29,7 @@
 #include "CSSPrimitiveValueMappings.h"
 #include "CSSPropertyNames.h"
 #include "CSSReflectValue.h"
+#include "CSSTimingFunctionValue.h"
 #include "CSSValueList.h"
 #include "CachedImage.h"
 #include "Document.h"
@@ -187,6 +188,10 @@ static const int computedProperties[] = {
     CSSPropertyWebkitTransform,
     CSSPropertyWebkitTransformOriginX,
     CSSPropertyWebkitTransformOriginY,
+    CSSPropertyWebkitTransitionDelay,
+    CSSPropertyWebkitTransitionDuration,
+    CSSPropertyWebkitTransitionProperty,
+    CSSPropertyWebkitTransitionTimingFunction,
     CSSPropertyWebkitUserDrag,
     CSSPropertyWebkitUserModify,
     CSSPropertyWebkitUserSelect,
@@ -1055,6 +1060,63 @@ PassRefPtr<CSSValue> CSSComputedStyleDeclaration::getPropertyCSSValue(int proper
             }
             else
                 return CSSPrimitiveValue::create(style->transformOriginY());
+        case CSSPropertyWebkitTransitionDelay: {
+            RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
+            const AnimationList* t = style->transitions();
+            if (t) {
+                for (size_t i = 0; i < t->size(); ++i)
+                    list->append(CSSPrimitiveValue::create((*t)[i]->delay(), CSSPrimitiveValue::CSS_S));
+            }
+            else
+                list->append(CSSPrimitiveValue::create(RenderStyle::initialDelay(), CSSPrimitiveValue::CSS_S));
+            return list.release();
+        }
+        case CSSPropertyWebkitTransitionDuration: {
+            RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
+            const AnimationList* t = style->transitions();
+            if (t) {
+                for (size_t i = 0; i < t->size(); ++i)
+                    list->append(CSSPrimitiveValue::create((*t)[i]->duration(), CSSPrimitiveValue::CSS_S));
+            }
+            else
+                list->append(CSSPrimitiveValue::create(RenderStyle::initialDuration(), CSSPrimitiveValue::CSS_S));
+            return list.release();
+        }
+        case CSSPropertyWebkitTransitionTimingFunction: {
+            RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
+            const AnimationList* t = style->transitions();
+            if (t) {
+                for (size_t i = 0; i < t->size(); ++i) {
+                    const TimingFunction& tf = (*t)[i]->timingFunction();
+                    list->append(CSSTimingFunctionValue::create(tf.x1(), tf.y1(), tf.x2(), tf.y2()));
+                }
+            }
+            else {
+                const TimingFunction& tf = RenderStyle::initialTimingFunction();
+                list->append(CSSTimingFunctionValue::create(tf.x1(), tf.y1(), tf.x2(), tf.y2()));
+            }
+            return list.release();
+        }
+        case CSSPropertyWebkitTransitionProperty: {
+            RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
+            const AnimationList* t = style->transitions();
+            if (t) {
+                for (size_t i = 0; i < t->size(); ++i) {
+                    int prop = (*t)[i]->property();
+                    const char* name;
+                    if (prop == cAnimateNone)
+                        name = "none";
+                    else if (prop == cAnimateAll)
+                        name = "all";
+                    else
+                        name = getPropertyName(static_cast<CSSPropertyID>(prop));
+                    list->append(CSSPrimitiveValue::create(name, CSSPrimitiveValue::CSS_STRING));
+                }
+            }
+            else
+                list->append(CSSPrimitiveValue::create("all", CSSPrimitiveValue::CSS_STRING));
+            return list.release();
+        }
         case CSSPropertyBackground:
         case CSSPropertyBorder:
         case CSSPropertyBorderBottom:
@@ -1112,11 +1174,6 @@ PassRefPtr<CSSValue> CSSComputedStyleDeclaration::getPropertyCSSValue(int proper
         case CSSPropertyWebkitMask:
         case CSSPropertyWebkitPaddingStart:
         case CSSPropertyWebkitTextStroke:
-        case CSSPropertyWebkitTransition:
-        case CSSPropertyWebkitTransitionDuration:
-        case CSSPropertyWebkitTransitionProperty:
-        case CSSPropertyWebkitTransitionRepeatCount:
-        case CSSPropertyWebkitTransitionTimingFunction:
             // FIXME: The above are unimplemented.
             break;
 #if ENABLE(SVG)
index df9e6cfa45707b0b666672e46154bc02191d7f3d..740ed6ac45e453644f4353c0cb352e5b2e502b5b 100644 (file)
@@ -1294,8 +1294,8 @@ bool CSSParser::parseValue(int propId, bool important)
         }
         return false;
     }
+    case CSSPropertyWebkitTransitionDelay:
     case CSSPropertyWebkitTransitionDuration:
-    case CSSPropertyWebkitTransitionRepeatCount:
     case CSSPropertyWebkitTransitionTimingFunction:
     case CSSPropertyWebkitTransitionProperty: {
         RefPtr<CSSValue> val;
@@ -1709,8 +1709,9 @@ void CSSParser::addTransitionValue(RefPtr<CSSValue>& lval, PassRefPtr<CSSValue>
 
 bool CSSParser::parseTransitionShorthand(bool important)
 {
-    const int properties[] = { CSSPropertyWebkitTransitionProperty, CSSPropertyWebkitTransitionDuration, 
-                               CSSPropertyWebkitTransitionTimingFunction, CSSPropertyWebkitTransitionRepeatCount };
+    const int properties[] = { CSSPropertyWebkitTransitionProperty,
+                               CSSPropertyWebkitTransitionDuration,
+                               CSSPropertyWebkitTransitionTimingFunction };
     const int numProperties = sizeof(properties) / sizeof(properties[0]);
     
     ShorthandScope scope(this, CSSPropertyWebkitTransition);
@@ -2206,20 +2207,20 @@ bool CSSParser::parseFillProperty(int propId, int& propId1, int& propId2,
     return false;
 }
 
-PassRefPtr<CSSValue> CSSParser::parseTransitionDuration()
+PassRefPtr<CSSValue> CSSParser::parseDelay()
 {
     CSSParserValue* value = m_valueList->current();
+    if (value->id == CSSValueNow)
+        return CSSPrimitiveValue::createIdentifier(value->id);
     if (validUnit(value, FTime, m_strict))
         return CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit);
     return 0;
 }
 
-PassRefPtr<CSSValue> CSSParser::parseTransitionRepeatCount()
+PassRefPtr<CSSValue> CSSParser::parseDuration()
 {
     CSSParserValue* value = m_valueList->current();
-    if (value->id == CSSValueInfinite)
-        return CSSPrimitiveValue::createIdentifier(value->id);
-    if (validUnit(value, FInteger|FNonNeg, m_strict))
+    if (validUnit(value, FTime|FNonNeg, m_strict))
         return CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit);
     return 0;
 }
@@ -2242,7 +2243,7 @@ bool CSSParser::parseTimingFunctionValue(CSSParserValueList*& args, double& resu
     return true;
 }
 
-PassRefPtr<CSSValue> CSSParser::parseTransitionTimingFunction()
+PassRefPtr<CSSValue> CSSParser::parseTimingFunction()
 {
     CSSParserValue* value = m_valueList->current();
     if (value->id == CSSValueEase || value->id == CSSValueLinear || value->id == CSSValueEaseIn || value->id == CSSValueEaseOut || value->id == CSSValueEaseInOut)
@@ -2306,18 +2307,18 @@ bool CSSParser::parseTransitionProperty(int propId, RefPtr<CSSValue>& result)
         }
         else {
             switch (propId) {
-                case CSSPropertyWebkitTransitionDuration:
-                    currValue = parseTransitionDuration();
+                case CSSPropertyWebkitTransitionDelay:
+                    currValue = parseDelay();
                     if (currValue)
                         m_valueList->next();
                     break;
-                case CSSPropertyWebkitTransitionRepeatCount:
-                    currValue = parseTransitionRepeatCount();
+                case CSSPropertyWebkitTransitionDuration:
+                    currValue = parseDuration();
                     if (currValue)
                         m_valueList->next();
                     break;
                 case CSSPropertyWebkitTransitionTimingFunction:
-                    currValue = parseTransitionTimingFunction();
+                    currValue = parseTimingFunction();
                     if (currValue)
                         m_valueList->next();
                     break;
@@ -3907,7 +3908,7 @@ PassRefPtr<CSSValueList> CSSParser::parseTransform()
 
     // The transform is a list of functional primitives that specify transform operations.
     // We collect a list of WebKitCSSTransformValues, where each value specifies a single operation.
-    RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
+    RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
     for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) {
         if (value->unit != CSSParserValue::Function || !value->function)
             return 0;
index 5ded55c835dab7a19dffcbdaa1a3948a80c0b255..880471ac2abadcfd32ab5b22d902f69e88639372 100644 (file)
@@ -84,11 +84,13 @@ namespace WebCore {
         void addFillValue(RefPtr<CSSValue>& lval, PassRefPtr<CSSValue> rval);
 
         void addTransitionValue(RefPtr<CSSValue>& lval, PassRefPtr<CSSValue> rval);
-        PassRefPtr<CSSValue> parseTransitionDuration();
-        PassRefPtr<CSSValue> parseTransitionRepeatCount();
-        PassRefPtr<CSSValue> parseTransitionTimingFunction();
-        bool parseTimingFunctionValue(CSSParserValueList*& args, double& result);
+
+        PassRefPtr<CSSValue> parseDelay();
+        PassRefPtr<CSSValue> parseDuration();
+        PassRefPtr<CSSValue> parseTimingFunction();
         PassRefPtr<CSSValue> parseTransitionProperty();
+
+        bool parseTimingFunctionValue(CSSParserValueList*& args, double& result);
         bool parseTransitionProperty(int propId, RefPtr<CSSValue>&);
         bool parseTransitionShorthand(bool important);
         
index ec3925ae74e421c1a45742ec4855e26f8be30bde..43a7021c1693d3b8d6c41870acdf2a0b0acd114d 100644 (file)
@@ -142,11 +142,6 @@ word-spacing
 word-wrap
 z-index
 zoom
--webkit-transition
--webkit-transition-duration
--webkit-transition-property
--webkit-transition-repeat-count
--webkit-transition-timing-function
 -webkit-appearance
 -webkit-background-clip
 -webkit-background-composite
@@ -225,6 +220,11 @@ zoom
 -webkit-transform-origin
 -webkit-transform-origin-x
 -webkit-transform-origin-y
+-webkit-transition
+-webkit-transition-delay
+-webkit-transition-duration
+-webkit-transition-property
+-webkit-transition-timing-function
 -webkit-user-drag
 -webkit-user-modify
 -webkit-user-select
index b48ddd58eb0570813e333609dfe45dbaa9a22d37..24d75bd8b9dd56e29bb4b888d4fdde3ff7837a12 100644 (file)
@@ -184,61 +184,52 @@ HANDLE_FILL_LAYER_VALUE(mask, Mask, prop, Prop, value)
 
 #define HANDLE_TRANSITION_INHERIT_AND_INITIAL(prop, Prop) \
 if (isInherit) { \
-    Transition* currChild = m_style->accessTransitions(); \
-    Transition* prevChild = 0; \
-    const Transition* currParent = m_parentStyle->transitions(); \
-    while (currParent && currParent->is##Prop##Set()) { \
-        if (!currChild) { \
-            /* Need to make a new layer.*/ \
-            currChild = new Transition(); \
-            prevChild->setNext(currChild); \
-        } \
-        currChild->set##Prop(currParent->prop()); \
-        prevChild = currChild; \
-        currChild = prevChild->next(); \
-        currParent = currParent->next(); \
+    AnimationList* list = m_style->accessTransitions(); \
+    const AnimationList* parentList = m_parentStyle->transitions(); \
+    size_t i = 0; \
+    for ( ; i < parentList->size() && (*parentList)[i]->is##Prop##Set(); ++i) { \
+        if (list->size() <= i) \
+            list->append(Animation::create()); \
+        (*list)[i]->set##Prop((*parentList)[i]->prop()); \
     } \
     \
-    while (currChild) { \
-        /* Reset any remaining layers to not have the property set. */ \
-        currChild->clear##Prop(); \
-        currChild = currChild->next(); \
-    } \
-} else if (isInitial) { \
-    Transition* currChild = m_style->accessTransitions(); \
-    currChild->set##Prop(RenderStyle::initialTransition##Prop()); \
-    for (currChild = currChild->next(); currChild; currChild = currChild->next()) \
-        currChild->clear##Prop(); \
+    /* Reset any remaining layers to not have the property set. */ \
+    for ( ; i < list->size(); ++i) \
+        (*list)[i]->clear##Prop(); \
+} \
+if (isInitial) { \
+    AnimationList* list = m_style->accessTransitions(); \
+    (*list)[0]->set##Prop(RenderStyle::initial##Prop()); \
+    for (size_t i = 1; i < list->size(); ++i) \
+        (*list)[0]->clear##Prop(); \
 }
 
 #define HANDLE_TRANSITION_VALUE(prop, Prop, value) { \
 HANDLE_TRANSITION_INHERIT_AND_INITIAL(prop, Prop) \
 if (isInherit || isInitial) \
     return; \
-Transition* currChild = m_style->accessTransitions(); \
-Transition* prevChild = 0; \
+AnimationList* list = m_style->accessTransitions(); \
+size_t childIndex = 0; \
 if (value->isValueList()) { \
     /* Walk each value and put it into a layer, creating new layers as needed. */ \
     CSSValueList* valueList = static_cast<CSSValueList*>(value); \
     for (unsigned int i = 0; i < valueList->length(); i++) { \
-        if (!currChild) { \
-            /* Need to make a new layer to hold this value */ \
-            currChild = new Transition(); \
-            prevChild->setNext(currChild); \
-        } \
-        mapTransition##Prop(currChild, valueList->itemWithoutBoundsCheck(i)); \
-        prevChild = currChild; \
-        currChild = currChild->next(); \
+        if (childIndex <= list->size()) \
+            list->append(Animation::create()); \
+        map##Prop((*list)[childIndex].get(), valueList->itemWithoutBoundsCheck(i)); \
+        ++childIndex; \
     } \
 } else { \
-    mapTransition##Prop(currChild, value); \
-    currChild = currChild->next(); \
+    if (list->isEmpty()) \
+        list->append(Animation::create()); \
+    map##Prop((*list)[childIndex].get(), value); \
+    childIndex = 1; \
 } \
-while (currChild) { \
+for ( ; childIndex < list->size(); ++childIndex) { \
     /* Reset all remaining layers to not have the property set. */ \
-    currChild->clear##Prop(); \
-    currChild = currChild->next(); \
-} }
+    (*list)[childIndex]->clear##Prop(); \
+} \
+}
 
 #define HANDLE_INHERIT_COND(propID, prop, Prop) \
 if (id == propID) { \
@@ -4758,18 +4749,18 @@ void CSSStyleSelector::applyProperty(int id, CSSValue *value)
         else if (isInherit)
             m_style->inheritTransitions(m_parentStyle->transitions());
         return;
+    case CSSPropertyWebkitTransitionDelay:
+        HANDLE_TRANSITION_VALUE(delay, Delay, value)
+        return;
     case CSSPropertyWebkitTransitionDuration:
         HANDLE_TRANSITION_VALUE(duration, Duration, value)
         return;
-    case CSSPropertyWebkitTransitionRepeatCount:
-        HANDLE_TRANSITION_VALUE(repeatCount, RepeatCount, value)
+    case CSSPropertyWebkitTransitionProperty:
+        HANDLE_TRANSITION_VALUE(property, Property, value)
         return;
     case CSSPropertyWebkitTransitionTimingFunction:
         HANDLE_TRANSITION_VALUE(timingFunction, TimingFunction, value)
         return;
-    case CSSPropertyWebkitTransitionProperty:
-        HANDLE_TRANSITION_VALUE(property, Property, value)
-        return;
     case CSSPropertyInvalid:
         return;
     case CSSPropertyFontStretch:
@@ -5010,10 +5001,10 @@ void CSSStyleSelector::mapFillYPosition(FillLayer* layer, CSSValue* value)
     layer->setYPosition(l);
 }
 
-void CSSStyleSelector::mapTransitionDuration(Transition* transition, CSSValue* value)
+void CSSStyleSelector::mapDuration(Animation* transition, CSSValue* value)
 {
     if (value->cssValueType() == CSSValue::CSS_INITIAL) {
-        transition->setDuration(RenderStyle::initialTransitionDuration());
+        transition->setDuration(RenderStyle::initialDuration());
         return;
     }
 
@@ -5022,35 +5013,36 @@ void CSSStyleSelector::mapTransitionDuration(Transition* transition, CSSValue* v
 
     CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value);
     if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_S)
-        transition->setDuration(int(1000*primitiveValue->getFloatValue()));
+        transition->setDuration(primitiveValue->getFloatValue());
     else if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_MS)
-        transition->setDuration(int(primitiveValue->getFloatValue()));
+        transition->setDuration(primitiveValue->getFloatValue()/1000.0f);
 }
 
-void CSSStyleSelector::mapTransitionRepeatCount(Transition* transition, CSSValue* value)
+void CSSStyleSelector::mapDelay(Animation* transition, CSSValue* value)
 {
     if (value->cssValueType() == CSSValue::CSS_INITIAL) {
-        transition->setRepeatCount(RenderStyle::initialTransitionRepeatCount());
+        transition->setDelay(RenderStyle::initialDelay());
         return;
     }
 
-    if (!value->isPrimitiveValue())
-        return;
-
     CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value);
-    if (primitiveValue->getIdent() == CSSValueInfinite)
-        transition->setRepeatCount(-1);
-    else
-        transition->setRepeatCount(int(primitiveValue->getFloatValue()));
+    if (primitiveValue->getIdent() == CSSValueNow)
+        transition->setDelay(0);
+    else {
+        if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_S)
+            transition->setDelay(primitiveValue->getFloatValue());
+        else
+            transition->setDelay(primitiveValue->getFloatValue()/1000.0f);
+    }
 }
 
-void CSSStyleSelector::mapTransitionTimingFunction(Transition* transition, CSSValue* value)
+void CSSStyleSelector::mapTimingFunction(Animation* transition, CSSValue* value)
 {
     if (value->cssValueType() == CSSValue::CSS_INITIAL) {
-        transition->setTimingFunction(RenderStyle::initialTransitionTimingFunction());
+        transition->setTimingFunction(RenderStyle::initialTimingFunction());
         return;
     }
-
+    
     if (value->isPrimitiveValue()) {
         CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value);
         switch (primitiveValue->getIdent()) {
@@ -5072,17 +5064,17 @@ void CSSStyleSelector::mapTransitionTimingFunction(Transition* transition, CSSVa
         }
         return;
     }
-
-    if (value->isTransitionTimingFunctionValue()) {
+    
+    if (value->isTimingFunctionValue()) {
         CSSTimingFunctionValue* timingFunction = static_cast<CSSTimingFunctionValue*>(value);
         transition->setTimingFunction(TimingFunction(CubicBezierTimingFunction, timingFunction->x1(), timingFunction->y1(), timingFunction->x2(), timingFunction->y2()));
     }
 }
 
-void CSSStyleSelector::mapTransitionProperty(Transition* transition, CSSValue* value)
+void CSSStyleSelector::mapProperty(Animation* transition, CSSValue* value)
 {
     if (value->cssValueType() == CSSValue::CSS_INITIAL) {
-        transition->setProperty(RenderStyle::initialTransitionProperty());
+        transition->setProperty(RenderStyle::initialProperty());
         return;
     }
 
@@ -5090,7 +5082,7 @@ void CSSStyleSelector::mapTransitionProperty(Transition* transition, CSSValue* v
         return;
 
     CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value);
-    transition->setProperty(primitiveValue->getIdent());
+    transition->setProperty(static_cast<CSSPropertyID>(primitiveValue->getIdent()));
 }
 
 void CSSStyleSelector::mapNinePieceImage(CSSValue* value, NinePieceImage& image)
index e16f2e725b2d008edfdd2f83cb72703e9e49ab9f..b9167181a44552e3ded5f41160465f649de2389c 100644 (file)
@@ -205,10 +205,10 @@ public:
         void mapFillXPosition(FillLayer*, CSSValue*);
         void mapFillYPosition(FillLayer*, CSSValue*);
 
-        void mapTransitionDuration(Transition*, CSSValue*);
-        void mapTransitionRepeatCount(Transition*, CSSValue*);
-        void mapTransitionTimingFunction(Transition*, CSSValue*);
-        void mapTransitionProperty(Transition*, CSSValue*);
+        void mapDelay(Animation*, CSSValue*);
+        void mapDuration(Animation*, CSSValue*);
+        void mapTimingFunction(Animation*, CSSValue*);
+        void mapProperty(Animation*, CSSValue*);
 
         void mapNinePieceImage(CSSValue*, NinePieceImage&);
 
index 3cb3ebda0459e056a87e01f5667486f12daa5990..ffa455c5b6735ff9e63e5da7735193b49de56c39 100644 (file)
@@ -54,7 +54,7 @@ private:
     {
     }
 
-    virtual bool isTransitionTimingFunctionValue() { return true; }
+    virtual bool isTimingFunctionValue() { return true; }
     
     double m_x1;
     double m_y1;
index bf5c361f40bd68abe8313669ef20db4be784c875..ffdf42f32e94bb48103b6358fc48f80ee87f9fcf 100644 (file)
@@ -52,7 +52,7 @@ public:
     virtual bool isImageValue() const { return false; }
     virtual bool isImplicitInitialValue() const { return false; }
     virtual bool isPrimitiveValue() const { return false; }
-    virtual bool isTransitionTimingFunctionValue() { return false; }
+    virtual bool isTimingFunctionValue() { return false; }
     virtual bool isValueList() { return false; }
 
 #if ENABLE(SVG)
index 3cbfb85cdd57bb3ad8c16bc477165bbca9352ab2..b39e151de750659e25bfccfa6990eb179002f41e 100644 (file)
@@ -562,6 +562,11 @@ visual
 #
 lines
 
+#
+# CSS_PROP__WEBKIT_TRANSITION_DELAY
+#
+now
+
 #
 # CSS_PROP__WEBKIT_TRANSITION_TIMING_FUNCTION
 #
index 2ce6734bb1b499795e0d0c4cfc81cacf0121447e..d905591e5ed9197dae004c5756317b3c63c54bdf 100644 (file)
@@ -24,6 +24,7 @@
 #include "config.h"
 #include "Document.h"
 
+#include "AnimationController.h"
 #include "AXObjectCache.h"
 #include "CDATASection.h"
 #include "CSSHelper.h"
@@ -1162,6 +1163,10 @@ void Document::updateRendering()
 {
     if (hasChangedChild())
         recalcStyle(NoChange);
+    
+    // tell the animation controller that the style is available and it can start animations
+    if (m_frame)
+        m_frame->animation()->styleAvailable();
 }
 
 void Document::updateDocumentsRendering()
index 034c11afc709a94ff8cd0788ce4fd839dec06bee..63eb7767ef8ea5bda8ba74bee46b7bbf33379ae0 100644 (file)
@@ -750,7 +750,7 @@ public:
 
     void updateFocusAppearanceSoon();
     void cancelFocusAppearanceUpdate();
-    
+        
     // FF method for accessing the selection added for compatability.
     DOMSelection* getSelection() const;
     
@@ -1046,6 +1046,7 @@ private:
 #if USE(LOW_BANDWIDTH_DISPLAY)
     bool m_inLowBandwidthDisplay;
 #endif
+
 };
 
 inline bool Document::hasElementWithId(AtomicStringImpl* id) const
index 7d5f3f6171c96ad39f447b5b5c84989271c56260..970c3e10b81145db6e8edbf3c2645e11982e2553 100644 (file)
@@ -785,9 +785,9 @@ void Element::recalcStyle(StyleChange change)
                 newStyle->setChildrenAffectedByDirectAdjacentRules();
         }
 
-        if (ch != NoChange)
+        if (ch != NoChange) {
             setRenderStyle(newStyle);
-        else if (changed() && (document()->usesSiblingRules() || document()->usesDescendantRules())) {
+        } else if (changed() && (styleChangeType() != AnimationStyleChange) && (document()->usesSiblingRules() || document()->usesDescendantRules())) {
             // Although no change occurred, we use the new style so that the cousin style sharing code won't get
             // fooled into believing this style is the same.  This is only necessary if the document actually uses
             // sibling/descendant rules, since otherwise it isn't possible for ancestor styles to affect sharing of
@@ -796,12 +796,13 @@ void Element::recalcStyle(StyleChange change)
                 renderer()->setStyleInternal(newStyle);
             else
                 setRenderStyle(newStyle);
-        }
+        } else if (styleChangeType() == AnimationStyleChange)
+             setRenderStyle(newStyle);
 
         newStyle->deref(document()->renderArena());
 
         if (change != Force) {
-            if ((document()->usesDescendantRules() || hasPositionalRules) && styleChangeType() == FullStyleChange)
+            if ((document()->usesDescendantRules() || hasPositionalRules) && styleChangeType() >= FullStyleChange)
                 change = Force;
             else
                 change = ch;
index 872c6cf1e3eab7d30b3b28e13668a166e1dab450..d289829389000ef0f4061bd8d9a7c7f3078559a8 100644 (file)
@@ -119,6 +119,12 @@ namespace WebCore { namespace EventNames {
     macro(progress) \
     macro(stalled) \
     \
+    macro(webkitAnimationEnd) \
+    macro(webkitAnimationStart) \
+    macro(webkitAnimationIteration) \
+    \
+    macro(webkitTransitionEnd) \
+    \
 // end of DOM_EVENT_NAMES_FOR_EACH
 
 #ifndef DOM_EVENT_NAMES_HIDE_GLOBALS
index 1004cd9f7501ffefaa82b76187126971c15de7d3..14608c4fe988ef7f2e8777008bb7de97b5f03174 100644 (file)
@@ -412,7 +412,7 @@ void Node::setChanged(StyleChangeType changeType)
     if ((changeType != NoStyleChange) && !attached()) // changed compared to what?
         return;
 
-    if (!(changeType == InlineStyleChange && m_styleChange == FullStyleChange))
+    if (!(changeType == InlineStyleChange && (m_styleChange == FullStyleChange || m_styleChange == AnimationStyleChange)))
         m_styleChange = changeType;
 
     if (m_styleChange != NoStyleChange) {
index a5394837b8293660587564dbfef2aa134a88f17b..c4a11bbe006d69988f99e48bd5e8ed2c4bb7a7d5 100644 (file)
@@ -61,7 +61,7 @@ struct NodeListsNodeData;
 
 typedef int ExceptionCode;
 
-enum StyleChangeType { NoStyleChange, InlineStyleChange, FullStyleChange };
+enum StyleChangeType { NoStyleChange, InlineStyleChange, FullStyleChange, AnimationStyleChange };
 
 const unsigned short DOCUMENT_POSITION_EQUIVALENT = 0x00;
 const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01;
index 193189689f33ea7ca2c6d1bf2d0b55df652d7b62..cdbd96e2ba58117810896fec9903332d700f3912 100644 (file)
@@ -122,7 +122,7 @@ void CachedPage::restore(Page* page)
         m_document->accessSVGExtensions()->unpauseAnimations();
 #endif
 
-    mainFrame->animation()->resumeAnimations();
+    mainFrame->animation()->resumeAnimations(m_document.get());
 
     mainFrame->eventHandler()->setMousePressNode(mousePressNode());
         
diff --git a/WebCore/manual-tests/transition-delay.html b/WebCore/manual-tests/transition-delay.html
new file mode 100644 (file)
index 0000000..006c798
--- /dev/null
@@ -0,0 +1,59 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html lang="en">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <title>Transition Delay</title>
+    <style>
+      #container {
+        position: absolute;
+        width: 500px;
+        height: 340px;
+        border: 1px solid black;
+      }
+      
+      .box {
+        position: relative;
+        width: 100px;
+        height: 100px;
+        margin: 10px;
+        background-color: #66F;
+        z-index: 0;
+      }
+      
+      #container.slide > .box {
+        -webkit-transform: translateX(380px);
+        -webkit-transition-property: -webkit-transform;
+        -webkit-transition-duration: 2s;
+      }
+      
+      #box1 {
+        -webkit-transition-delay: 200ms;
+      }
+
+      #box2 {
+        -webkit-transition-delay: 2s;
+      }
+
+      #box3 {
+        -webkit-transition-delay: 4s;
+      }
+     </style>
+     
+</head>
+<body>
+<p>Click to animate</p>
+<div id="container" onclick="this.className = 'slide'">
+  <div class="box" id="box1">
+      Delay: 200ms
+  </div>
+  <div class="box" id="box2">
+      Delay: 2s
+  </div>
+  <div class="box" id="box3">
+      Delay: 4s
+  </div>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/WebCore/manual-tests/transition-left.html b/WebCore/manual-tests/transition-left.html
new file mode 100644 (file)
index 0000000..492e306
--- /dev/null
@@ -0,0 +1,46 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html lang="en">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <title>Transition of left property</title>
+    <style>
+         div {
+             position: relative;
+             left: 100px;
+             height: 200px;
+             width: 300px;
+             background-color: #9bb;
+             -webkit-transition-property: left;
+             -webkit-transition-duration: 1s;
+         }
+     </style>
+     <script type="text/javascript" charset="utf-8">
+      
+      var flag = true;
+      
+      function init() {
+        document.getElementById("target").addEventListener("click", function(evt) {
+          if (flag)
+            evt.target.style.left = "300px";
+          else 
+            evt.target.style.left = "100px";
+          flag = !flag;
+        }, false);
+      }
+      
+      window.addEventListener("load", init, false);
+     </script>
+</head>
+<body>
+  <h1>Transition of 'left' property</h1>
+
+  <p>The element below should move 200 pixels left or right when clicked</p>
+  
+  <div id="target">
+    This element should transition.
+  </div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/WebCore/manual-tests/transition-timing-functions.html b/WebCore/manual-tests/transition-timing-functions.html
new file mode 100644 (file)
index 0000000..ed1e4d1
--- /dev/null
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+  "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+  <head>
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+    <title>Timing Functions for Transitions</title>
+    <style type="text/css" media="screen">
+      
+      body {
+        margin: 10px;
+        padding: 0;
+      }
+      
+      .container div {
+        position: relative;
+        width: 250px;
+        height: 50px;
+        background: #99c;
+        -webkit-transition-property: left;
+        -webkit-transition-duration: 5s;
+        margin-bottom: 10px;
+      }
+      
+      .container:hover .default,
+      .container:hover .ease,
+      .container:hover .linear,
+      .container:hover .ease-in,
+      .container:hover .ease-out,
+      .container:hover .ease-in-out,
+      .container:hover .cubic,
+      .container:hover .error {
+        left: 400px;
+      }
+
+      .container .ease {
+        -webkit-transition-timing-function: ease;
+      }
+
+      .container .linear {
+        -webkit-transition-timing-function: linear;
+      }
+
+      .container .ease-in {
+        -webkit-transition-timing-function: ease-in;
+      }
+
+      .container .ease-out {
+        -webkit-transition-timing-function: ease-out;
+      }
+
+      .container .ease-in-out {
+        -webkit-transition-timing-function: ease-in-out;
+      }
+
+      .container .cubic {
+        -webkit-transition-timing-function: cubic-bezier(.42, .0, .58, 1.0)
+      }
+
+      .container .error {
+        -webkit-transition-timing-function: bananas;
+      }
+
+      #endmarker {
+        position: absolute;
+        width: 10px;
+        left: 400px;
+        top: 100px;
+        height: 500px;
+        background-color: red;
+      }
+
+      
+    </style>
+  </head>
+  <body>
+    <div id="endmarker">
+    </div>
+
+    <h1>Timing functions for transitions</h1>
+    <p>On hover, the elements below should transition using the
+      described timing functions</p>
+      
+    <div class="container">
+      
+      <div class="default">
+        Default (no timing function specified, should be the same as Ease)
+      </div>
+
+      <div class="ease">
+        Ease (the default ease function)
+      </div>
+
+      <div class="linear">
+        Linear
+      </div>
+
+      <div class="ease-in">
+        Ease In
+      </div>
+
+      <div class="ease-out">
+        Ease Out
+      </div>
+
+      <div class="ease-in-out">
+        Ease In Out
+      </div>
+
+      <div class="cubic">
+        Cubic bezier specified the same as Ease In Out
+      </div>
+
+      <div class="error">
+        Bogus definition, should become default of Ease
+      </div>
+      
+    </div>
+    
+    
+    
+  </body>
+</html>
\ No newline at end of file
index 1677fb2c5f05fb8235221412c50b3e3e6c4f9a0c..7e8e93b222b1ba1eb0a28fab9439259f67dcc8f0 100644 (file)
@@ -30,7 +30,9 @@
 #include "AnimationController.h"
 
 #include "CSSPropertyNames.h"
+#include "CString.h"
 #include "Document.h"
+#include "EventNames.h"
 #include "FloatConversion.h"
 #include "Frame.h"
 #include "RenderObject.h"
@@ -43,6 +45,12 @@ namespace WebCore {
 
 static const double cAnimationTimerDelay = 0.025;
 
+static void setChanged(Node* node)
+{
+    ASSERT(!node || (node->document() && !node->document()->inPageCache()));
+    node->setChanged(AnimationStyleChange);
+}
+
 // The epsilon value we pass to UnitBezier::solve given that the animation is going to run over |dur| seconds. The longer the
 // animation, the more precision we need in the timing function result to avoid ugly discontinuities.
 static inline double solveEpsilon(double duration) { return 1. / (200. * duration); }
@@ -55,135 +63,731 @@ static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x
     return bezier.solve(t, solveEpsilon(duration));
 }
 
-class CompositeImplicitAnimation;
+class CompositeAnimation;
+class AnimationBase;
 
-class ImplicitAnimation : public Noncopyable {
+class AnimationTimerBase {
 public:
-    ImplicitAnimation(const Transition*);
-    ~ImplicitAnimation();
-
-    void animate(CompositeImplicitAnimation*, RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle, RenderStyle*& animatedStyle);
-
-    void reset(RenderObject*, RenderStyle* from, RenderStyle* to);
+    AnimationTimerBase(AnimationBase* anim)
+    : m_timer(this, &AnimationTimerBase::timerFired)
+    , m_anim(anim)
+    {
+        m_timer.startOneShot(0);
+    }
+    virtual ~AnimationTimerBase()
+    {
+    }
+    
+    void startTimer(double timeout=0)
+    {
+        m_timer.startOneShot(timeout);
+    }
+    
+    void cancelTimer()
+    {
+        m_timer.stop();
+    }
     
-    double progress() const;
+    virtual void timerFired(Timer<AnimationTimerBase>*) = 0;
+    
+private:
+    Timer<AnimationTimerBase> m_timer;
+    
+protected:
+    AnimationBase* m_anim;
+};
 
-    int property() const { return m_property; }
+class AnimationEventDispatcher : public AnimationTimerBase {
+public:
+    AnimationEventDispatcher(AnimationBase* anim) 
+    : AnimationTimerBase(anim)
+    , m_property(CSSPropertyInvalid)
+    , m_reset(false)
+    , m_elapsedTime(-1)
+    {
+    }
+    
+    virtual ~AnimationEventDispatcher()
+    {
+    }
+    
+    void startTimer(Element* element, const AtomicString& name, int property, bool reset, 
+                    const AtomicString& eventType, double elapsedTime)
+    {
+        m_element = element;
+        m_name = name;
+        m_property = property;
+        m_reset = reset;
+        m_eventType = eventType;
+        m_elapsedTime = elapsedTime;
+        AnimationTimerBase::startTimer();
+    }
     
-    bool finished() const { return m_finished; }
-    void setFinished(bool f) { m_finished = f; }
+    virtual void timerFired(Timer<AnimationTimerBase>*);
     
-    bool stale() const { return m_stale; }
-    void setStale() { m_stale = true; }
+private:
+    RefPtr<Element> m_element;
+    AtomicString m_name;
+    int m_property;
+    bool m_reset;
+    AtomicString m_eventType;
+    double m_elapsedTime;
+};
 
+class AnimationTimerCallback : public AnimationTimerBase {
+public:
+    AnimationTimerCallback(AnimationBase* anim) 
+    : AnimationTimerBase(anim)
+    , m_elapsedTime(0)
+    { }
+    virtual ~AnimationTimerCallback() { }
+    
+    virtual void timerFired(Timer<AnimationTimerBase>*);
+    
+    void startTimer(double timeout, const AtomicString& eventType, double elapsedTime)
+    {
+        m_eventType = eventType;
+        m_elapsedTime = elapsedTime;
+        AnimationTimerBase::startTimer(timeout);
+    }
+    
 private:
-    // The two styles that we are blending.
-    RenderStyle* m_fromStyle;
-    RenderStyle* m_toStyle;
+    AtomicString m_eventType;
+    double m_elapsedTime;
+};
 
-    int m_property;
-    TimingFunction m_function;
-    double m_duration;
+class ImplicitAnimation;
+class AnimationControllerPrivate;
 
-    int m_repeatCount;
+// A CompositeAnimation represents a collection of animations that
+// are running, such as a number of properties transitioning at once.
+
+class CompositeAnimation : public Noncopyable {
+public:
+    CompositeAnimation(AnimationControllerPrivate* animationController)
+    : m_suspended(false)
+    , m_animationController(animationController)
+    , m_numStyleAvailableWaiters(0)
+    { }
+    
+    ~CompositeAnimation()
+    {
+        deleteAllValues(m_transitions);
+    }
+    
+    RenderStyle* animate(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle);
+    
+    void setAnimating(bool inAnimating);
+    bool animating();
+    
+    bool hasAnimationForProperty(int prop) const { return m_transitions.contains(prop); }
+    
+    void setTransitionStartTime(int property, double t);
+    
+    void resetTransitions(RenderObject*);
+    void resetAnimations(RenderObject*);
+    
+    void cleanupFinishedAnimations(RenderObject*);
+    
+    void suspendAnimations();
+    void resumeAnimations();
+    bool suspended() const      { return m_suspended; }
+    
+    void overrideImplicitAnimations(int property);
+    void resumeOverriddenImplicitAnimations(int property);
+    
+    void styleAvailable();
+    
+    bool isAnimatingProperty(int property) const;
+    
+    void setWaitingForStyleAvailable(bool waiting);
+protected:
+    void updateTransitions(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle);
+    
+private:
+    typedef HashMap<int, ImplicitAnimation*> CSSPropertyTransitionsMap;
+    
+    CSSPropertyTransitionsMap   m_transitions;
+    bool                        m_suspended;
+    AnimationControllerPrivate* m_animationController;
+    uint32_t                    m_numStyleAvailableWaiters;
+};
+
+class AnimationBase : public Noncopyable {
+public:
+    AnimationBase(const Animation* transition, RenderObject* renderer, CompositeAnimation* compAnim);    
+    virtual ~AnimationBase()
+    {
+        if (m_animState == STATE_START_WAIT_STYLE_AVAILABLE)
+            m_compAnim->setWaitingForStyleAvailable(false);
+    }
+    
+    RenderObject* renderer() const { return m_object; }
+    double startTime() const { return m_startTime; }
+    double duration() const  { return m_animation->duration(); }
+    
+    void cancelTimers()
+    {
+        m_animationTimerCallback.cancelTimer();
+        m_animationEventDispatcher.cancelTimer();
+    }
+    
+    // Animations and Transitions go through the states below. When entering the STARTED state
+    // the animation is started. This may or may not require deferred response from the animator.
+    // If so, we stay in this state until that response is received (and it returns the start time).
+    // Otherwise, we use the current time as the start time and go immediately to the LOOPING or
+    // ENDING state.
+    enum AnimState { 
+        STATE_NEW,                      // animation just created, animation not running yet
+        STATE_START_WAIT_TIMER,         // start timer running, waiting for fire
+        STATE_START_WAIT_STYLE_AVAILABLE,   // waiting for style setup so we can start animations
+        STATE_START_WAIT_RESPONSE,      // animation started, waiting for response
+        STATE_LOOPING,                  // response received, animation running, loop timer running, waiting for fire
+        STATE_ENDING,                   // response received, animation running, end timer running, waiting for fire
+        STATE_PAUSED_WAIT_TIMER,        // animation in pause mode when animation started
+        STATE_PAUSED_WAIT_RESPONSE,     // animation paused when in STARTING state
+        STATE_PAUSED_RUN,               // animation paused when in LOOPING or ENDING state
+        STATE_DONE                      // end timer fired, animation finished and removed
+    };
+    
+    enum AnimStateInput {
+        STATE_INPUT_MAKE_NEW,           // reset back to new from any state
+        STATE_INPUT_START_ANIMATION,    // animation requests a start
+        STATE_INPUT_RESTART_ANIMATION,  // force a restart from any state
+        STATE_INPUT_START_TIMER_FIRED,  // start timer fired
+        STATE_INPUT_STYLE_AVAILABLE,        // style is setup, ready to start animating
+        STATE_INPUT_START_TIME_SET,     // m_startTime was set
+        STATE_INPUT_LOOP_TIMER_FIRED,   // loop timer fired
+        STATE_INPUT_END_TIMER_FIRED,    // end timer fired
+        STATE_INPUT_PAUSE_OVERRIDE,     // pause an animation due to override
+        STATE_INPUT_RESUME_OVERRIDE,    // resume an overridden animation
+        STATE_INPUT_PLAY_STATE_RUNNING, // play state paused -> running
+        STATE_INPUT_PLAY_STATE_PAUSED,  // play state running -> paused
+        STATE_INPUT_END_ANIMATION       // force an end from any state
+    };
+    
+    // Called when animation is in NEW state to start animation
+    void updateStateMachine(AnimStateInput input, double param);
+    
+    // Animation has actually started, at passed time
+    // This is a callback and is only received when RenderObject::startAnimation() or RenderObject::startTransition() 
+    // returns true. If RenderObject::
+    void onAnimationStartResponse(double startTime);
+    
+    // Called to change to or from paused state
+    void updatePlayState(bool running);
+    bool playStatePlaying() const { return m_animation; }
+    
+    bool waitingToStart() const { return m_animState == STATE_NEW || m_animState == STATE_START_WAIT_TIMER; }
+    bool preactive() const
+    { return m_animState == STATE_NEW ||
+             m_animState == STATE_START_WAIT_TIMER ||
+             m_animState == STATE_START_WAIT_STYLE_AVAILABLE ||
+             m_animState == STATE_START_WAIT_RESPONSE;
+    }
+    bool postactive() const { return m_animState == STATE_DONE; }
+    bool active() const { return !postactive() && !preactive(); }
+    bool running() const { return !isnew() && !postactive(); }
+    bool paused() const { return m_pauseTime >= 0; }
+    bool isnew() const { return m_animState == STATE_NEW; }
+    bool waitingForStartTime() const { return m_animState == STATE_START_WAIT_RESPONSE; }
+    bool waitingForStyleAvailable() const { return m_animState == STATE_START_WAIT_STYLE_AVAILABLE; }
+    bool waitingForEndEvent() const  { return m_waitingForEndEvent; }
+    
+    // "animating" means that something is running that requires a timer to keep firing
+    // (e.g. a software animation)
+    void setAnimating(bool inAnimating = true) { m_animating = inAnimating; }
+    bool animating() const { return m_animating; }
+    
+    double progress(double scale, double offset) const;
+    
+    virtual void animate(CompositeAnimation*, RenderObject*, const RenderStyle* currentStyle, 
+                         const RenderStyle* targetStyle, RenderStyle*& ioAnimatedStyle) { }
+    virtual void reset(RenderObject* renderer, const RenderStyle* from = 0, const RenderStyle* to = 0) { }
+    
+    virtual bool shouldFireEvents() const { return false; }
+    
+    void animationTimerCallbackFired(const AtomicString& eventType, double elapsedTime);
+    void animationEventDispatcherFired(Element* element, const AtomicString& name, int property, bool reset, 
+                                       const AtomicString& eventType, double elapsedTime);
+    
+    bool animationsMatch(const Animation* anim) const { return m_animation->animationsMatch(anim); }
+    
+    void setAnimation(const Animation* anim) { m_animation = const_cast<Animation*>(anim); }
+    
+    // Return true if this animation is overridden. This will only be the case for
+    // ImplicitAnimations and is used to determine whether or not we should force
+    // set the start time. If an animation is overridden, it will probably not get
+    // back the START_TIME event.
+    virtual bool overridden() const { return false; }
+    
+    // Does this animation/transition involve the given property?
+    virtual bool affectsProperty(int property) const { return false; }
+    bool isAnimatingProperty(int property) const
+    {
+        return (!waitingToStart() && !postactive()) && affectsProperty(property);
+    }
+        
+protected:
+    Element* elementForEventDispatch()
+    {
+        if (m_object->node() && m_object->node()->isElementNode())
+            return static_cast<Element*>(m_object->node());
+        return 0;
+    }
+    
+    virtual void overrideAnimations() { }
+    virtual void resumeOverriddenAnimations() { }
+    
+    CompositeAnimation* compositeAnimation() { return m_compAnim; }
+    
+    // These are called when the corresponding timer fires so subclasses can do any extra work
+    virtual void onAnimationStart(double elapsedTime) { }
+    virtual void onAnimationIteration(double elapsedTime) { }
+    virtual void onAnimationEnd(double elapsedTime) { }
+    virtual bool startAnimation(double beginTime) { return false; }
+    virtual void endAnimation(bool reset) { }
+    
+    void primeEventTimers();
+    
+protected:
+    AnimState m_animState;
     int m_iteration;
     
-    bool m_finished;    
+    bool m_animating;       // transition/animation requires continual timer firing
+    bool m_waitedForResponse;
     double m_startTime;
-    bool m_paused;
     double m_pauseTime;
+    RenderObject*   m_object;
     
-    bool m_stale;
+    AnimationTimerCallback m_animationTimerCallback;
+    AnimationEventDispatcher m_animationEventDispatcher;
+    RefPtr<Animation> m_animation;
+    CompositeAnimation* m_compAnim;
+    bool m_waitingForEndEvent;
 };
 
-class CompositeImplicitAnimation : public Noncopyable {
+class ImplicitAnimation : public AnimationBase {
 public:
-    ~CompositeImplicitAnimation() { deleteAllValues(m_animations); }
-
-    RenderStyle* animate(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle);
-
-    bool animating() const;
-
-    void reset(RenderObject*);
+    ImplicitAnimation(const Animation* transition, RenderObject* renderer, CompositeAnimation* compAnim)
+    : AnimationBase(transition, renderer, compAnim)
+    , m_property(transition->property())
+    , m_overridden(false)
+    , m_fromStyle(0)
+    , m_toStyle(0)
+    {
+    }
+    
+    virtual ~ImplicitAnimation()
+    {
+        ASSERT(!m_fromStyle && !m_toStyle);
+        
+        // If we were waiting for an end event, we need to finish the animation to make sure no old
+        // animations stick around in the lower levels
+        if (waitingForEndEvent() && m_object)
+            ASSERT(0);
+        
+        // Do the cleanup here instead of in the base class so the specialized methods get called
+        if (!postactive())
+            updateStateMachine(STATE_INPUT_END_ANIMATION, -1);     
+    }
+    
+    int property() const { return m_property; }
+    
+    virtual void onAnimationEnd(double inElapsedTime);
+    virtual bool startAnimation(double beginTime);
+    virtual void endAnimation(bool reset);
+    
+    virtual void animate(CompositeAnimation*, RenderObject*, const RenderStyle* currentStyle, 
+                         const RenderStyle* targetStyle, RenderStyle*& ioAnimatedStyle);
+    virtual void reset(RenderObject* renderer, const RenderStyle* from = 0, const RenderStyle* to = 0);
+    
+    void setOverridden(bool b);
+    virtual bool overridden() const { return m_overridden; }
+    
+    virtual bool shouldFireEvents() const { return true; }
+    
+    virtual bool affectsProperty(int property) const;
+    
+    bool hasStyle() const { return m_fromStyle && m_toStyle; }
+    
+    bool isTargetPropertyEqual(int prop, RenderStyle* targetStyle);
 
+    void blendPropertyValueInStyle(int prop, RenderStyle* currentStyle);
+    
+protected:
+    bool shouldSendEventForListener(Document::ListenerType inListenerType)
+    {
+        return m_object->document()->hasListenerType(inListenerType);
+    }
+    
+    bool sendTransitionEvent(const AtomicString& inEventType, double inElapsedTime);
+    
 private:
-    HashMap<int, ImplicitAnimation*> m_animations;
+    int m_property;
+    bool m_overridden;
+    
+    // The two styles that we are blending.
+    RenderStyle* m_fromStyle;
+    RenderStyle* m_toStyle;
 };
 
-ImplicitAnimation::ImplicitAnimation(const Transition* transition)
-: m_fromStyle(0)
-, m_toStyle(0)
-, m_property(transition->property())
-, m_function(transition->timingFunction())
-, m_duration(transition->duration() / 1000.0)
-, m_repeatCount(transition->repeatCount())
-, m_iteration(0)
-, m_finished(false)
-, m_startTime(currentTime())
-, m_paused(false)
-, m_pauseTime(m_startTime)
-, m_stale(false)
+void AnimationTimerCallback::timerFired(Timer<AnimationTimerBase>*)
 {
+    m_anim->animationTimerCallbackFired(m_eventType, m_elapsedTime);
 }
 
-ImplicitAnimation::~ImplicitAnimation()
+void AnimationEventDispatcher::timerFired(Timer<AnimationTimerBase>*)
 {
-    ASSERT(!m_fromStyle && !m_toStyle);
+    m_anim->animationEventDispatcherFired(m_element.get(), m_name, m_property, m_reset, m_eventType, m_elapsedTime);
 }
-
-void ImplicitAnimation::reset(RenderObject* renderer, RenderStyle* from, RenderStyle* to)
+    
+AnimationBase::AnimationBase(const Animation* transition, RenderObject* renderer, CompositeAnimation* compAnim)
+: m_animState(STATE_NEW)
+, m_iteration(0)
+, m_animating(false)
+, m_waitedForResponse(false)
+, m_startTime(0)
+, m_pauseTime(-1)
+, m_object(renderer)
+, m_animationTimerCallback(const_cast<AnimationBase*>(this))
+, m_animationEventDispatcher(const_cast<AnimationBase*>(this))
+, m_animation(const_cast<Animation*>(transition))
+, m_compAnim(compAnim)
+, m_waitingForEndEvent(false)
 {
-    setFinished(false);
+}
 
-    if (m_fromStyle)
-        m_fromStyle->deref(renderer->renderArena());
-    if (m_toStyle)
-        m_toStyle->deref(renderer->renderArena());
-    m_fromStyle = from;
-    if (m_fromStyle)
-        m_fromStyle->ref();
-    m_toStyle = to;
-    if (m_toStyle)
-        m_toStyle->ref();
-    if (from || to)
-        m_startTime = currentTime();
+void AnimationBase::updateStateMachine(AnimStateInput input, double param)
+{
+    // if we get a RESTART then we force a new animation, regardless of state
+    if (input == STATE_INPUT_MAKE_NEW) {
+        if (m_animState == STATE_START_WAIT_STYLE_AVAILABLE)
+            m_compAnim->setWaitingForStyleAvailable(false);
+        m_animState = STATE_NEW;
+        m_startTime = 0;
+        m_pauseTime = -1;
+        m_waitedForResponse = false;
+        endAnimation(false);
+        return;
+    }
+    else if (input == STATE_INPUT_RESTART_ANIMATION) {
+        cancelTimers();
+        if (m_animState == STATE_START_WAIT_STYLE_AVAILABLE)
+            m_compAnim->setWaitingForStyleAvailable(false);
+        m_animState = STATE_NEW;
+        m_startTime = 0;
+        m_pauseTime = -1;
+        endAnimation(false);
         
-    // If we are stale, attempt to update to a new transition using the new |to| style.  If we are able to find a new transition,
-    // then we will unmark ourselves for death.
-    if (stale() && from && to) {
-         for (const Transition* transition = to->transitions(); transition; transition = transition->next()) {
-            if (m_property != transition->property())
-                continue;
-            int duration = transition->duration();
-            int repeatCount = transition->repeatCount();
-            if (duration && repeatCount) {
-                m_duration = duration / 1000.0;
-                m_repeatCount = repeatCount;
-                m_stale = false;
-                break;
-            }
+        if (!paused())
+            updateStateMachine(STATE_INPUT_START_ANIMATION, -1);
+        return;
+    }
+    else if (input == STATE_INPUT_END_ANIMATION) {
+        cancelTimers();
+        if (m_animState == STATE_START_WAIT_STYLE_AVAILABLE)
+            m_compAnim->setWaitingForStyleAvailable(false);
+        m_animState = STATE_DONE;
+        endAnimation(true);
+        return;
+    }
+    else if (input == STATE_INPUT_PAUSE_OVERRIDE) {
+        if (m_animState == STATE_START_WAIT_RESPONSE) {
+            // If we are in the WAIT_RESPONSE state, the animation will get canceled before 
+            // we get a response, so move to the next state
+            endAnimation(false);
+            updateStateMachine(STATE_INPUT_START_TIME_SET, currentTime());
+        }
+        return;
+    }
+    else if (input == STATE_INPUT_RESUME_OVERRIDE) {
+        if (m_animState == STATE_LOOPING || m_animState == STATE_ENDING) {
+            // Start the animation
+            startAnimation(m_startTime);
         }
+        return;
     }
     
-    // If we did not find a new transition, then mark ourselves as being finished so that we can be removed.
-    if (stale())
-        setFinished(true);
+    // execute state machine
+    switch(m_animState) {
+        case STATE_NEW:
+            ASSERT(input == STATE_INPUT_START_ANIMATION || input == STATE_INPUT_PLAY_STATE_RUNNING || input == STATE_INPUT_PLAY_STATE_PAUSED);
+            if (input == STATE_INPUT_START_ANIMATION || input == STATE_INPUT_PLAY_STATE_RUNNING) {
+                // Set the start timer to the initial delay (0 if no delay)
+                m_waitedForResponse = false;
+                m_animState = STATE_START_WAIT_TIMER;
+                m_animationTimerCallback.startTimer(m_animation->delay(), EventNames::webkitAnimationStartEvent, m_animation->delay());
+            }
+            break;
+        case STATE_START_WAIT_TIMER:
+            ASSERT(input == STATE_INPUT_START_TIMER_FIRED || input == STATE_INPUT_PLAY_STATE_PAUSED);
+            
+            if (input == STATE_INPUT_START_TIMER_FIRED) {
+                ASSERT(param >= 0);
+                // Start timer has fired, tell the animation to start and wait for it to respond with start time
+                m_animState = STATE_START_WAIT_STYLE_AVAILABLE;
+                m_compAnim->setWaitingForStyleAvailable(true);
+                
+                // Trigger a render so we can start the animation
+                setChanged(m_object->element());
+                m_object->animation()->startUpdateRenderingDispatcher();
+            }
+            else {
+                ASSERT(running());
+                // We're waiting for the start timer to fire and we got a pause. Cancel the timer, pause and wait
+                m_pauseTime = currentTime();
+                cancelTimers();
+                m_animState = STATE_PAUSED_WAIT_TIMER;
+            }
+            break;
+        case STATE_START_WAIT_STYLE_AVAILABLE:
+            ASSERT(input == STATE_INPUT_STYLE_AVAILABLE || input == STATE_INPUT_PLAY_STATE_PAUSED);
+            
+            m_compAnim->setWaitingForStyleAvailable(false);
+            
+            if (input == STATE_INPUT_STYLE_AVAILABLE) {
+                // Start timer has fired, tell the animation to start and wait for it to respond with start time
+                m_animState = STATE_START_WAIT_RESPONSE;
+                
+                overrideAnimations();
+                
+                // Send start event, if needed
+                onAnimationStart(0.0f); // the elapsedTime is always 0 here
+                
+                // Start the animation
+                if (overridden() || !startAnimation(0)) {
+                    // We're not going to get a startTime callback, so fire the start time here
+                    m_animState = STATE_START_WAIT_RESPONSE;
+                    updateStateMachine(STATE_INPUT_START_TIME_SET, currentTime());
+                }
+                else
+                    m_waitedForResponse = true;
+            }
+            else {
+                ASSERT(running());
+                // We're waiting for the a notification that the style has been setup. If we're asked to wait
+                // at this point, the style must have been processed, so we can deal with this like we would
+                // for WAIT_RESPONSE, except that we don't need to do an endAnimation().
+                m_pauseTime = 0;
+                m_animState = STATE_START_WAIT_RESPONSE;
+            }
+            break;
+        case STATE_START_WAIT_RESPONSE:
+            ASSERT(input == STATE_INPUT_START_TIME_SET || input == STATE_INPUT_PLAY_STATE_PAUSED);
+            
+            if (input == STATE_INPUT_START_TIME_SET) {
+                ASSERT(param >= 0);
+                // We have a start time, set it, unless the startTime is already set
+                if (m_startTime <= 0)
+                    m_startTime = param;
+                
+                // Decide when the end or loop event needs to fire
+                primeEventTimers();
+                
+                // Trigger a render so we can start the animation
+                setChanged(m_object->element());
+                m_object->animation()->startUpdateRenderingDispatcher();
+            }
+            else {
+                // We are pausing while waiting for a start response. Cancel the animation and wait. When 
+                // we unpause, we will act as though the start timer just fired
+                m_pauseTime = 0;
+                endAnimation(false);
+                m_animState = STATE_PAUSED_WAIT_RESPONSE;
+            }
+            break;
+        case STATE_LOOPING:
+            ASSERT(input == STATE_INPUT_LOOP_TIMER_FIRED || input == STATE_INPUT_PLAY_STATE_PAUSED);
+            
+            if (input == STATE_INPUT_LOOP_TIMER_FIRED) {
+                ASSERT(param >= 0);
+                // Loop timer fired, loop again or end.
+                onAnimationIteration(param);
+                primeEventTimers();
+            }
+            else {
+                // We are pausing while running. Cancel the animation and wait
+                m_pauseTime = currentTime();
+                cancelTimers();
+                endAnimation(false);
+                m_animState = STATE_PAUSED_RUN;
+            }
+            break;
+        case STATE_ENDING:
+            ASSERT(input == STATE_INPUT_END_TIMER_FIRED || input == STATE_INPUT_PLAY_STATE_PAUSED);
+            
+            if (input == STATE_INPUT_END_TIMER_FIRED) {
+                ASSERT(param >= 0);
+                // End timer fired, finish up
+                onAnimationEnd(param);
+                
+                resumeOverriddenAnimations();
+                
+                // Fire off another style change so we can set the final value
+                setChanged(m_object->element());
+                m_animState = STATE_DONE;
+                m_object->animation()->startUpdateRenderingDispatcher();
+                // |this| may be deleted here when we've been called from timerFired()
+            }
+            else {
+                // We are pausing while running. Cancel the animation and wait
+                m_pauseTime = currentTime();
+                cancelTimers();
+                endAnimation(false);
+                m_animState = STATE_PAUSED_RUN;
+            }
+            // |this| may be deleted here
+            break;
+        case STATE_PAUSED_WAIT_TIMER:
+            ASSERT(input == STATE_INPUT_PLAY_STATE_RUNNING);
+            ASSERT(!running());
+            // Update the times
+            m_startTime += currentTime() - m_pauseTime;
+            m_pauseTime = -1;
+            
+            // we were waiting for the start timer to fire, go back and wait again
+            m_animState = STATE_NEW;
+            updateStateMachine(STATE_INPUT_START_ANIMATION, 0);
+            break;
+        case STATE_PAUSED_WAIT_RESPONSE:
+        case STATE_PAUSED_RUN:
+            // We treat these two cases the same. The only difference is that, when we are in the WAIT_RESPONSE
+            // state, we don't yet have a valid startTime, so we send 0 to startAnimation. When the START_TIME
+            // event comes in qnd we were in the RUN state, we will notice that we have already set the 
+            // startTime and will ignore it.
+            ASSERT(input == STATE_INPUT_PLAY_STATE_RUNNING);
+            ASSERT(!running());
+            // Update the times
+            if (m_animState == STATE_PAUSED_RUN)
+                m_startTime += currentTime() - m_pauseTime;
+            else
+                m_startTime = 0;
+            m_pauseTime = -1;
+            
+            // We were waiting for a begin time response from the animation, go back and wait again
+            m_animState = STATE_START_WAIT_RESPONSE;
+            
+            // Start the animation
+            if (overridden() || !startAnimation(m_startTime)) {
+                // We're not going to get a startTime callback, so fire the start time here
+                updateStateMachine(STATE_INPUT_START_TIME_SET, currentTime());
+            }
+            else
+                m_waitedForResponse = true;
+            break;
+        case STATE_DONE:
+            // We're done. Stay in this state until we are deleted
+            break;
+    }
+    // |this| may be deleted here if we came out of STATE_ENDING when we've been called from timerFired()
+}
+    
+void AnimationBase::animationTimerCallbackFired(const AtomicString& eventType, double elapsedTime)
+{
+    ASSERT(m_object->document() && !m_object->document()->inPageCache());
+    
+    // FIXME: use an enum
+    if (eventType == EventNames::webkitAnimationStartEvent)
+        updateStateMachine(STATE_INPUT_START_TIMER_FIRED, elapsedTime);
+    else if (eventType == EventNames::webkitAnimationIterationEvent)
+        updateStateMachine(STATE_INPUT_LOOP_TIMER_FIRED, elapsedTime);
+    else if (eventType == EventNames::webkitAnimationEndEvent) {
+        updateStateMachine(STATE_INPUT_END_TIMER_FIRED, elapsedTime);
+        // |this| may be deleted here
+    }
 }
 
-double ImplicitAnimation::progress() const
+void AnimationBase::animationEventDispatcherFired(Element* element, const AtomicString& name, int property, 
+                                                  bool reset, const AtomicString& eventType, double elapsedTime)
 {
-    double elapsedTime = currentTime() - m_startTime;
+    m_waitingForEndEvent = false;
     
-    if (m_finished || !m_duration || elapsedTime >= m_duration)
-        return 1.0;
+    // Keep an atomic string on the stack to keep it alive until we exit this method
+    // (since dispatching the event may cause |this| to be deleted, therefore removing
+    // the last ref to the atomic string).
+    AtomicString animName(name);
+    AtomicString animEventType(eventType);
+    // Make sure the element sticks around too
+    RefPtr<Element> elementRetainer(element);
+    
+    ASSERT(!element || (element->document() && !element->document()->inPageCache()));
+    if (!element)
+        return;
+}
+
+void AnimationBase::updatePlayState(bool run)
+{
+    if (paused() == run || isnew())
+        updateStateMachine(run ? STATE_INPUT_PLAY_STATE_RUNNING : STATE_INPUT_PLAY_STATE_PAUSED, -1);
+}
 
-    if (m_function.type() == LinearTimingFunction)
-        return elapsedTime / m_duration;
+double AnimationBase::progress(double scale, double offset) const
+{
+    if (preactive())
+        return 0;
+    
+    double elapsedTime = running() ? (currentTime() - m_startTime) : (m_pauseTime - m_startTime);
+    if (running() && elapsedTime < 0)
+        return 0;
+    
+    double dur = m_animation->duration();
+    
+    if (postactive() || !m_animation->duration() || elapsedTime >= dur)
+        return 1.0;
+    
+    // Compute the fractional time, taking into account direction.
+    // There is no need to worry about iterations, we assume that we would have 
+    // short circuited above if we were done
+    double t = elapsedTime / m_animation->duration();
+    int i = (int) t;
+    t -= i;
+    
+    if (scale != 1 || offset != 0)
+        t = (t - offset) * scale;
+    
+    if (m_animation->timingFunction().type() == LinearTimingFunction)
+        return t;
     
     // Cubic bezier.
-    return solveCubicBezierFunction(m_function.x1(), m_function.y1(), 
-                                    m_function.x2(), m_function.y2(),
-                                    elapsedTime / m_duration, m_duration);
+    double tt = solveCubicBezierFunction(m_animation->timingFunction().x1(), 
+                                         m_animation->timingFunction().y1(), 
+                                         m_animation->timingFunction().x2(), 
+                                         m_animation->timingFunction().y2(),
+                                         t, m_animation->duration());
+    return tt;
+}
+
+void AnimationBase::primeEventTimers()
+{
+    // Decide when the end or loop event needs to fire
+    double ct = currentTime();
+    const double elapsedDuration = ct - m_startTime;
+    ASSERT(elapsedDuration >= 0);
+    
+    double totalDuration = m_animation->duration();
+    double durationLeft = 0;
+    double nextIterationTime = totalDuration;
+    if (totalDuration < 0 || elapsedDuration < totalDuration) {
+        durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration());
+        nextIterationTime = elapsedDuration + durationLeft;
+    }
+    
+    // At this point, we may have 0 durationLeft, if we've gotten the event late and we are already
+    // past totalDuration. In this case we still fire an end timer before processing the end. 
+    // This defers the call to sendAnimationEvents to avoid re-entrant calls that destroy
+    // the RenderObject, and therefore |this| before we're done with it.
+    if (totalDuration < 0 || nextIterationTime < totalDuration) {
+        // We are not at the end yet, send a loop event
+        ASSERT(nextIterationTime > 0);
+        m_animState = STATE_LOOPING;
+        m_animationTimerCallback.startTimer(durationLeft, EventNames::webkitAnimationIterationEvent, nextIterationTime);
+    }
+    else {
+        // We are at the end, send an end event
+        m_animState = STATE_ENDING;
+        m_animationTimerCallback.startTimer(durationLeft, EventNames::webkitAnimationEndEvent, nextIterationTime);
+    }
 }
 
 static inline int blendFunc(int from, int to, double progress)
@@ -223,7 +827,8 @@ static inline IntSize blendFunc(const IntSize& from, const IntSize& to, double p
 static inline ShadowData* blendFunc(const ShadowData* from, const ShadowData* to, double progress)
 {  
     ASSERT(from && to);
-    return new ShadowData(blendFunc(from->x, to->x, progress), blendFunc(from->y, to->y, progress), blendFunc(from->blur, to->blur, progress), blendFunc(from->color, to->color, progress));
+    return new ShadowData(blendFunc(from->x, to->x, progress), blendFunc(from->y, to->y, progress), 
+                          blendFunc(from->blur, to->blur, progress), blendFunc(from->color, to->color, progress));
 }
 
 static inline TransformOperations blendFunc(const TransformOperations& from, const TransformOperations& to, double progress)
@@ -237,7 +842,8 @@ static inline TransformOperations blendFunc(const TransformOperations& from, con
         TransformOperation* fromOp = i < fromSize ? from[i].get() : 0;
         TransformOperation* toOp = i < toSize ? to[i].get() : 0;
         TransformOperation* blendedOp = toOp ? toOp->blend(fromOp, progress) : fromOp->blend(0, progress, true);
-        result.append(blendedOp);
+        if (blendedOp)
+            result.append(blendedOp);
     }
     return result;
 }
@@ -254,311 +860,829 @@ static inline EVisibility blendFunc(EVisibility from, EVisibility to, double pro
     return result > 0. ? VISIBLE : (to != VISIBLE ? to : from);
 }
 
-#define BLEND(prop, getter, setter) \
-    if (m_property == prop && m_toStyle->getter() != targetStyle->getter()) {\
-        reset(renderer, currentStyle, targetStyle); \
-        if (stale()) \
-            return; \
-    } \
-    if (m_property == cAnimateAll || m_property == prop) { \
-        if (m_fromStyle->getter() != m_toStyle->getter()) {\
-            setFinished(false); \
-            if (!animatedStyle) \
-                animatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle); \
-            animatedStyle->setter(blendFunc(m_fromStyle->getter(), m_toStyle->getter(), progress()));\
-            if (m_property == prop) \
-                return; \
-        }\
-    }\
-
-#define BLEND_MAYBE_INVALID_COLOR(prop, getter, setter) \
-    if (m_property == prop && m_toStyle->getter() != targetStyle->getter()) { \
-        reset(renderer, currentStyle, targetStyle); \
-        if (stale()) \
-            return; \
-    } \
-    if (m_property == cAnimateAll || m_property == prop) { \
-        Color fromColor = m_fromStyle->getter(); \
-        Color toColor = m_toStyle->getter(); \
-        if (fromColor.isValid() || toColor.isValid()) { \
-            if (!fromColor.isValid()) \
-                fromColor = m_fromStyle->color(); \
-            if (!toColor.isValid()) \
-                toColor = m_toStyle->color(); \
-            if (fromColor != toColor) {\
-                setFinished(false); \
-                if (!animatedStyle) \
-                    animatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle); \
-                animatedStyle->setter(blendFunc(fromColor, toColor, progress()));\
-                if (m_property == prop) \
-                    return; \
-            }\
-        }\
-    }\
-
-#define BLEND_SHADOW(prop, getter, setter) \
-    if (m_property == prop && (!m_toStyle->getter() || !targetStyle->getter() || *m_toStyle->getter() != *targetStyle->getter())) { \
-        reset(renderer, currentStyle, targetStyle); \
-        if (stale()) \
-            return; \
-    } \
-    if (m_property == cAnimateAll || m_property == prop) { \
-        if (m_fromStyle->getter() && m_toStyle->getter() && *m_fromStyle->getter() != *m_toStyle->getter()) {\
-            setFinished(false); \
-            if (!animatedStyle) \
-                animatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle); \
-            animatedStyle->setter(blendFunc(m_fromStyle->getter(), m_toStyle->getter(), progress()));\
-            if (m_property == prop) \
-                return; \
-        }\
-    }
-
-void ImplicitAnimation::animate(CompositeImplicitAnimation* animation, RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle, RenderStyle*& animatedStyle)
-{
-    // FIXME: If we have no transition-property, then the only way to tell if our goal state changed is to check
-    // every single animatable property.  For now we'll just diff the styles to ask that question,
-    // but we should really exclude non-animatable properties.
-    if (!m_toStyle || (m_property == cAnimateAll && targetStyle->diff(m_toStyle))) {
-        reset(renderer, currentStyle, targetStyle);
-        if (stale())
-            return;
-    }
-
-    // FIXME: Blow up shorthands so that they can be honored.
-    setFinished(true);
-    BLEND(CSSPropertyLeft, left, setLeft);
-    BLEND(CSSPropertyRight, right, setRight);
-    BLEND(CSSPropertyTop, top, setTop);
-    BLEND(CSSPropertyBottom, bottom, setBottom);
-    BLEND(CSSPropertyWidth, width, setWidth);
-    BLEND(CSSPropertyHeight, height, setHeight);
-    BLEND(CSSPropertyBorderLeftWidth, borderLeftWidth, setBorderLeftWidth);
-    BLEND(CSSPropertyBorderRightWidth, borderRightWidth, setBorderRightWidth);
-    BLEND(CSSPropertyBorderTopWidth, borderTopWidth, setBorderTopWidth);
-    BLEND(CSSPropertyBorderBottomWidth, borderBottomWidth, setBorderBottomWidth);
-    BLEND(CSSPropertyMarginLeft, marginLeft, setMarginLeft);
-    BLEND(CSSPropertyMarginRight, marginRight, setMarginRight);
-    BLEND(CSSPropertyMarginTop, marginTop, setMarginTop);
-    BLEND(CSSPropertyMarginBottom, marginBottom, setMarginBottom);
-    BLEND(CSSPropertyPaddingLeft, paddingLeft, setPaddingLeft);
-    BLEND(CSSPropertyPaddingRight, paddingRight, setPaddingRight);
-    BLEND(CSSPropertyPaddingTop, paddingTop, setPaddingTop);
-    BLEND(CSSPropertyPaddingBottom, paddingBottom, setPaddingBottom);
-    BLEND(CSSPropertyOpacity, opacity, setOpacity);
-    BLEND(CSSPropertyColor, color, setColor);
-    BLEND(CSSPropertyBackgroundColor, backgroundColor, setBackgroundColor);
-    BLEND_MAYBE_INVALID_COLOR(CSSPropertyWebkitColumnRuleColor, columnRuleColor, setColumnRuleColor);
-    BLEND(CSSPropertyWebkitColumnRuleWidth, columnRuleWidth, setColumnRuleWidth);
-    BLEND(CSSPropertyWebkitColumnGap, columnGap, setColumnGap);
-    BLEND(CSSPropertyWebkitColumnCount, columnCount, setColumnCount);
-    BLEND(CSSPropertyWebkitColumnWidth, columnWidth, setColumnWidth);
-    BLEND_MAYBE_INVALID_COLOR(CSSPropertyWebkitTextStrokeColor, textStrokeColor, setTextStrokeColor);
-    BLEND_MAYBE_INVALID_COLOR(CSSPropertyWebkitTextFillColor, textFillColor, setTextFillColor);
-    BLEND(CSSPropertyWebkitBorderHorizontalSpacing, horizontalBorderSpacing, setHorizontalBorderSpacing);
-    BLEND(CSSPropertyWebkitBorderVerticalSpacing, verticalBorderSpacing, setVerticalBorderSpacing);
-    BLEND_MAYBE_INVALID_COLOR(CSSPropertyBorderLeftColor, borderLeftColor, setBorderLeftColor);
-    BLEND_MAYBE_INVALID_COLOR(CSSPropertyBorderRightColor, borderRightColor, setBorderRightColor);
-    BLEND_MAYBE_INVALID_COLOR(CSSPropertyBorderTopColor, borderTopColor, setBorderTopColor);
-    BLEND_MAYBE_INVALID_COLOR(CSSPropertyBorderBottomColor, borderBottomColor, setBorderBottomColor);
-    BLEND(CSSPropertyZIndex, zIndex, setZIndex);
-    BLEND(CSSPropertyLineHeight, lineHeight, setLineHeight);
-    BLEND_MAYBE_INVALID_COLOR(CSSPropertyOutlineColor, outlineColor, setOutlineColor);
-    BLEND(CSSPropertyOutlineOffset, outlineOffset, setOutlineOffset);
-    BLEND(CSSPropertyOutlineWidth, outlineWidth, setOutlineWidth);
-    BLEND(CSSPropertyLetterSpacing, letterSpacing, setLetterSpacing);
-    BLEND(CSSPropertyWordSpacing, wordSpacing, setWordSpacing);
-    BLEND_SHADOW(CSSPropertyWebkitBoxShadow, boxShadow, setBoxShadow);
-    BLEND_SHADOW(CSSPropertyTextShadow, textShadow, setTextShadow);
-    BLEND(CSSPropertyWebkitTransform, transform, setTransform);
-    BLEND(CSSPropertyWebkitTransformOriginX, transformOriginX, setTransformOriginX);
-    BLEND(CSSPropertyWebkitTransformOriginY, transformOriginY, setTransformOriginY);
-    BLEND(CSSPropertyWebkitBorderTopLeftRadius, borderTopLeftRadius, setBorderTopLeftRadius);
-    BLEND(CSSPropertyWebkitBorderTopRightRadius, borderTopRightRadius, setBorderTopRightRadius);
-    BLEND(CSSPropertyWebkitBorderBottomLeftRadius, borderBottomLeftRadius, setBorderBottomLeftRadius);
-    BLEND(CSSPropertyWebkitBorderBottomRightRadius, borderBottomRightRadius, setBorderBottomRightRadius);
-    BLEND(CSSPropertyVisibility, visibility, setVisibility);
-    BLEND(CSSPropertyZoom, zoom, setZoom);
-}
-
-RenderStyle* CompositeImplicitAnimation::animate(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle)
-{
-    const Transition* currentTransitions = currentStyle->transitions();
-    const Transition* targetTransitions = targetStyle->transitions();
-    bool transitionsChanged = m_animations.isEmpty() || (currentTransitions != targetTransitions && !(currentTransitions && targetTransitions && *currentTransitions == *targetTransitions));
-
-    if (transitionsChanged) {
-        HashMap<int, ImplicitAnimation*>::iterator end = m_animations.end();
-         
-        for (HashMap<int, ImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it)
-            // Mark any running animations as stale if the set of transitions changed.  Stale animations continue
-            // to blend on a timer, and then remove themselves when finished.
-            it->second->setStale();
-
-        // If our transitions changed we need to add new animations for any transitions that
-        // don't exist yet.  If a new transition conflicts with a currently running stale transition, then we will not add it.
-        // The stale transition is responsible for updating itself to the new transition if it ever gets reset or finishes.
-        for (const Transition* transition = targetTransitions; transition; transition = transition->next()) {
-            int property = transition->property();
-            int duration = transition->duration();
-            int repeatCount = transition->repeatCount();
-            if (property && duration && repeatCount && !m_animations.contains(property)) {
-                ImplicitAnimation* animation = new ImplicitAnimation(transition);
-                m_animations.set(property, animation);
-            }
-        }
+class PropertyWrapperBase {
+public:
+    PropertyWrapperBase(int prop)
+    : m_prop(prop)
+    { }
+    virtual ~PropertyWrapperBase() { }
+    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const=0;
+    virtual void blend(RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog) const=0;
+    
+    int property() const { return m_prop; }
+    
+private:
+    int m_prop;
+};
+
+template <typename T>
+class PropertyWrapperGetter : public PropertyWrapperBase {
+public:
+    PropertyWrapperGetter(int prop, T (RenderStyle::*getter)() const)
+    : PropertyWrapperBase(prop)
+    , m_getter(getter)
+    { }
+    
+    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
+    {
+        return (a->*m_getter)() == (b->*m_getter)();
     }
+    
+protected:
+    T (RenderStyle::*m_getter)() const;
+};
 
-    // Now that we have animation objects ready, let them know about the new goal state.  We want them
-    // to fill in a RenderStyle*& only if needed.
-    RenderStyle* result = 0;
-    HashMap<int, ImplicitAnimation*>::iterator end = m_animations.end();
-    Vector<int> obsoleteTransitions;
+template <typename T>
+class PropertyWrapper : public PropertyWrapperGetter<T> {
+public:
+    PropertyWrapper(int prop, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T))
+    : PropertyWrapperGetter<T>(prop, getter)
+    , m_setter(setter)
+    { }
     
-    // Look at the "all" animation first.
-    ImplicitAnimation* allAnimation = m_animations.get(cAnimateAll);
-    if (allAnimation) {
-        allAnimation->animate(this, renderer, currentStyle, targetStyle, result);
+    virtual void blend(RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog) const
+    {
+        (dst->*m_setter)(blendFunc((a->*PropertyWrapperGetter<T>::m_getter)(), (b->*PropertyWrapperGetter<T>::m_getter)(), prog));
+    }
+    
+protected:
+    void (RenderStyle::*m_setter)(T);
+};
 
-        // If the animation is done and we are marked as stale, then we can be removed after the
-        // iteration of the hashmap is finished.
-        if (allAnimation->finished() && allAnimation->stale())
-            obsoleteTransitions.append(cAnimateAll);
+class PropertyWrapperShadow : public PropertyWrapperGetter<ShadowData*> {
+public:
+    PropertyWrapperShadow(int prop, ShadowData* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(ShadowData*, bool))
+    : PropertyWrapperGetter<ShadowData*>(prop, getter)
+    , m_setter(setter)
+    { }
+    
+    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
+    {
+        ShadowData* shadowa = (a->*m_getter)();
+        ShadowData* shadowb = (b->*m_getter)();
+        
+        if (!shadowa && shadowb || shadowa && !shadowb)
+            return false;
+        if (shadowa && shadowb && (*shadowa != *shadowb))
+            return false;
+        return true;
     }
     
-    // Now look at the specialized animations.
-    for (HashMap<int, ImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it) {
-        // Skip over the "all" animation, since we animated it already.
-        if (it->second->property() == cAnimateAll)
-            continue;
+    virtual void blend(RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog) const
+    {
+        ShadowData* shadowa = (a->*m_getter)();
+        ShadowData* shadowb = (b->*m_getter)();
+        ShadowData defaultShadowData(0, 0, 0, Color::transparent);
         
-        it->second->animate(this, renderer, currentStyle, targetStyle, result);
-
-        // If the animation is done and we are marked as stale, then we can be removed after the
-        // iteration of the hashmap is finished.
-        if (it->second->finished() && it->second->stale())
-            obsoleteTransitions.append(it->second->property());
+        if (!shadowa)
+            shadowa = &defaultShadowData;
+        if (!shadowb)
+            shadowb = &defaultShadowData;
+        
+        (dst->*m_setter)(blendFunc(shadowa, shadowb, prog), false);
+    }
+    
+private:
+    void (RenderStyle::*m_setter)(ShadowData*, bool);
+};
 
+class PropertyWrapperIntSize : public PropertyWrapperGetter<IntSize> {
+public:
+    PropertyWrapperIntSize(int prop, IntSize (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const IntSize&))
+    : PropertyWrapperGetter<IntSize>(prop, getter)
+    , m_setter(setter)
+    { }
+    
+    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
+    {
+        IntSize sizea = (a->*m_getter)();
+        IntSize sizeb = (b->*m_getter)();
+        
+        return (sizea == sizeb);
     }
     
-    // Now cull out any stale animations that are finished.
-    for (unsigned i = 0; i < obsoleteTransitions.size(); ++i) {
-        ImplicitAnimation* animation = m_animations.take(obsoleteTransitions[i]);
-        animation->reset(renderer, 0, 0);
-        delete animation;
+    virtual void blend(RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog) const
+    {
+        IntSize sizea = (a->*m_getter)();
+        IntSize sizeb = (b->*m_getter)();
+                
+        (dst->*m_setter)(blendFunc(sizea, sizeb, prog));
     }
+    
+private:
+    void (RenderStyle::*m_setter)(const IntSize&);
+};
 
-    if (result)
-        return result;
-
-    return targetStyle;
-}
-
-bool CompositeImplicitAnimation::animating() const
-{
-    HashMap<int, ImplicitAnimation*>::const_iterator end = m_animations.end();
-    for (HashMap<int, ImplicitAnimation*>::const_iterator it = m_animations.begin(); it != end; ++it)
-        if (!it->second->finished())
-            return true;
-    return false;
-}
-
-void CompositeImplicitAnimation::reset(RenderObject* renderer)
-{
-    HashMap<int, ImplicitAnimation*>::const_iterator end = m_animations.end();
-    for (HashMap<int, ImplicitAnimation*>::const_iterator it = m_animations.begin(); it != end; ++it)
-        it->second->reset(renderer, 0, 0);
-}
+class PropertyWrapperMaybeInvalidColor : public PropertyWrapperBase {
+public:
+    PropertyWrapperMaybeInvalidColor(int prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&))
+    : PropertyWrapperBase(prop)
+    , m_getter(getter)
+    , m_setter(setter)
+    { }
+    
+    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
+    {
+        Color fromColor = (a->*m_getter)();
+        Color toColor = (b->*m_getter)();
+        if (!fromColor.isValid())
+            fromColor = a->color();
+        if (!toColor.isValid())
+            toColor = b->color();
+        
+        return fromColor == toColor;
+    }
+    
+    virtual void blend(RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog) const
+    {
+        Color fromColor = (a->*m_getter)();
+        Color toColor = (b->*m_getter)();
+        if (!fromColor.isValid())
+            fromColor = a->color();
+        if (!toColor.isValid())
+            toColor = b->color();
+        (dst->*m_setter)(blendFunc(fromColor, toColor, prog));
+    }
+    
+private:
+    const Color& (RenderStyle::*m_getter)() const;
+    void (RenderStyle::*m_setter)(const Color&);
+};
 
 class AnimationControllerPrivate {
 public:
     AnimationControllerPrivate(Frame*);
     ~AnimationControllerPrivate();
-
-    CompositeImplicitAnimation* get(RenderObject*, RenderStyle*);
+    
+    CompositeAnimation* accessCompositeAnimation(RenderObject*);
     bool clear(RenderObject*);
     
-    void timerFired(Timer<AnimationControllerPrivate>*);
-    void updateTimer();
-
-    bool hasImplicitAnimations() const { return !m_animations.isEmpty(); }
-
+    void animationTimerFired(Timer<AnimationControllerPrivate>*);
+    void updateAnimationTimer();
+    
+    void updateRenderingDispatcherFired(Timer<AnimationControllerPrivate>*);
+    void startUpdateRenderingDispatcher();
+    
+    bool hasAnimations() const { return !m_compositeAnimations.isEmpty(); }
+    
+    void suspendAnimations(Document* document);
+    void resumeAnimations(Document* document);
+    
+    void styleAvailable();
+    
+    bool isAnimatingPropertyOnRenderer(RenderObject* obj, int property) const;
+    
+    static bool propertiesEqual(int prop, const RenderStyle* a, const RenderStyle* b);
+    static int getPropertyAtIndex(int i);
+    static int getNumProperties() { return gPropertyWrappers->size(); }
+    
+    // Return true if we need to start software animation timers
+    static bool blendProperties(int prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog);
+    
+    void setWaitingForStyleAvailable(bool waiting) { if (waiting) m_numStyleAvailableWaiters++; else m_numStyleAvailableWaiters--; }
+    
 private:
-    HashMap<RenderObject*, CompositeImplicitAnimation*> m_animations;
-    Timer<AnimationControllerPrivate> m_timer;
+    static void ensurePropertyMap();
+    
+    typedef HashMap<RenderObject*, CompositeAnimation*> RenderObjectAnimationMap;
+    
+    RenderObjectAnimationMap m_compositeAnimations;
+    Timer<AnimationControllerPrivate> m_animationTimer;
+    Timer<AnimationControllerPrivate> m_updateRenderingDispatcher;
     Frame* m_frame;
+    uint32_t m_numStyleAvailableWaiters;
+    
+    static Vector<PropertyWrapperBase*>* gPropertyWrappers;
+    static int gPropertyWrapperMap[numCSSProperties];
 };
 
+Vector<PropertyWrapperBase*>* AnimationControllerPrivate::gPropertyWrappers = 0;
+int AnimationControllerPrivate::gPropertyWrapperMap[];
+
 AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame)
-    : m_timer(this, &AnimationControllerPrivate::timerFired)
-    , m_frame(frame)
+: m_animationTimer(this, &AnimationControllerPrivate::animationTimerFired)
+, m_updateRenderingDispatcher(this, &AnimationControllerPrivate::updateRenderingDispatcherFired)
+, m_frame(frame)
+, m_numStyleAvailableWaiters(0)
 {
+    ensurePropertyMap();
 }
 
 AnimationControllerPrivate::~AnimationControllerPrivate()
 {
-    deleteAllValues(m_animations);
+    deleteAllValues(m_compositeAnimations);
+}
+
+// static
+void AnimationControllerPrivate::ensurePropertyMap()
+{
+    // FIXME: This data is never destroyed. Maybe we should ref count it and toss it when the last AnimationController is destroyed?
+    if (gPropertyWrappers == 0) {
+        gPropertyWrappers = new Vector<PropertyWrapperBase*>();
+        
+        // build the list of property wrappers to do the comparisons and blends
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyLeft, &RenderStyle::left, &RenderStyle::setLeft));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyRight, &RenderStyle::right, &RenderStyle::setRight));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyTop, &RenderStyle::top, &RenderStyle::setTop));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyBottom, &RenderStyle::bottom, &RenderStyle::setBottom));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWidth, &RenderStyle::width, &RenderStyle::setWidth));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyHeight, &RenderStyle::height, &RenderStyle::setHeight));
+        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderLeftWidth, &RenderStyle::borderLeftWidth, &RenderStyle::setBorderLeftWidth));
+        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderRightWidth, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth));
+        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderTopWidth, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth));
+        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderBottomWidth, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginLeft, &RenderStyle::marginLeft, &RenderStyle::setMarginLeft));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginRight, &RenderStyle::marginRight, &RenderStyle::setMarginRight));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginTop, &RenderStyle::marginTop, &RenderStyle::setMarginTop));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginBottom, &RenderStyle::marginBottom, &RenderStyle::setMarginBottom));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingLeft, &RenderStyle::paddingLeft, &RenderStyle::setPaddingLeft));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingRight, &RenderStyle::paddingRight, &RenderStyle::setPaddingRight));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingTop, &RenderStyle::paddingTop, &RenderStyle::setPaddingTop));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingBottom, &RenderStyle::paddingBottom, &RenderStyle::setPaddingBottom));
+        gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity));
+        gPropertyWrappers->append(new PropertyWrapper<const Color&>(CSSPropertyColor, &RenderStyle::color, &RenderStyle::setColor));
+        gPropertyWrappers->append(new PropertyWrapper<const Color&>(CSSPropertyBackgroundColor, &RenderStyle::backgroundColor, &RenderStyle::setBackgroundColor));
+        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnRuleWidth, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth));
+        gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap));
+        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnCount, &RenderStyle::columnCount, &RenderStyle::setColumnCount));
+        gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitColumnWidth, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth));
+        gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyWebkitBorderHorizontalSpacing, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing));
+        gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyWebkitBorderVerticalSpacing, &RenderStyle::verticalBorderSpacing, &RenderStyle::setVerticalBorderSpacing));
+        gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyZIndex, &RenderStyle::zIndex, &RenderStyle::setZIndex));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyLineHeight, &RenderStyle::lineHeight, &RenderStyle::setLineHeight));
+        gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyOutlineOffset, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset));
+        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyOutlineWidth, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth));
+        gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyLetterSpacing, &RenderStyle::letterSpacing, &RenderStyle::setLetterSpacing));
+        gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyWordSpacing, &RenderStyle::wordSpacing, &RenderStyle::setWordSpacing));
+        gPropertyWrappers->append(new PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX));
+        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY));
+        gPropertyWrappers->append(new PropertyWrapperIntSize(CSSPropertyWebkitBorderTopLeftRadius, &RenderStyle::borderTopLeftRadius, &RenderStyle::setBorderTopLeftRadius));
+        gPropertyWrappers->append(new PropertyWrapperIntSize(CSSPropertyWebkitBorderTopRightRadius, &RenderStyle::borderTopRightRadius, &RenderStyle::setBorderTopRightRadius));
+        gPropertyWrappers->append(new PropertyWrapperIntSize(CSSPropertyWebkitBorderBottomLeftRadius, &RenderStyle::borderBottomLeftRadius, &RenderStyle::setBorderBottomLeftRadius));
+        gPropertyWrappers->append(new PropertyWrapperIntSize(CSSPropertyWebkitBorderBottomRightRadius, &RenderStyle::borderBottomRightRadius, &RenderStyle::setBorderBottomRightRadius));
+        gPropertyWrappers->append(new PropertyWrapper<EVisibility>(CSSPropertyVisibility, &RenderStyle::visibility, &RenderStyle::setVisibility));
+        gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyZoom, &RenderStyle::zoom, &RenderStyle::setZoom));
+        
+        // FIXME: these might be invalid colors, need to check for that
+        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitColumnRuleColor, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor));
+        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitTextStrokeColor, &RenderStyle::textStrokeColor, &RenderStyle::setTextStrokeColor));
+        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitTextFillColor, &RenderStyle::textFillColor, &RenderStyle::setTextFillColor));
+        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderLeftColor, &RenderStyle::borderLeftColor, &RenderStyle::setBorderLeftColor));
+        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderRightColor, &RenderStyle::borderRightColor, &RenderStyle::setBorderRightColor));
+        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderTopColor, &RenderStyle::borderTopColor, &RenderStyle::setBorderTopColor));
+        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderBottomColor, &RenderStyle::borderBottomColor, &RenderStyle::setBorderBottomColor));
+        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyOutlineColor, &RenderStyle::outlineColor, &RenderStyle::setOutlineColor));
+        
+        // These are for shadows
+        gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyWebkitBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow));
+        gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyTextShadow, &RenderStyle::textShadow, &RenderStyle::setTextShadow));
+        
+        // Make sure unused slots have a value
+        for (unsigned int i = 0; i < (unsigned int) numCSSProperties; ++i)
+            gPropertyWrapperMap[i] = CSSPropertyInvalid;
+        
+        size_t n = gPropertyWrappers->size();
+        for (unsigned int i = 0; i < n; ++i) {
+            ASSERT((*gPropertyWrappers)[i]->property() - firstCSSProperty < numCSSProperties);
+            gPropertyWrapperMap[(*gPropertyWrappers)[i]->property() - firstCSSProperty] = i;
+        }
+    }
 }
 
-CompositeImplicitAnimation* AnimationControllerPrivate::get(RenderObject* renderer, RenderStyle* targetStyle)
+// static
+bool AnimationControllerPrivate::propertiesEqual(int prop, const RenderStyle* a, const RenderStyle* b)
 {
-    CompositeImplicitAnimation* animation = m_animations.get(renderer);
-    if (!animation && targetStyle->transitions()) {
-        animation = new CompositeImplicitAnimation();
-        m_animations.set(renderer, animation);
+    if (prop == cAnimateAll) {
+        size_t n = gPropertyWrappers->size();
+        for (unsigned int i = 0; i < n; ++i) {
+            if (!(*gPropertyWrappers)[i]->equals(a, b))
+                return false;
+        }
+    }
+    else {
+        int propIndex = prop - firstCSSProperty;
+        
+        if (propIndex >= 0 && propIndex < numCSSProperties) {
+            int i = gPropertyWrapperMap[propIndex];
+            return (i >= 0) ? (*gPropertyWrappers)[i]->equals(a, b) : true;
+        }
+    }
+    return true;
+}
+
+// static
+int AnimationControllerPrivate::getPropertyAtIndex(int i)
+{
+    if (i < 0 || i >= (int) gPropertyWrappers->size())
+        return CSSPropertyInvalid;
+        
+    return (*gPropertyWrappers)[i]->property();
+}
+    
+// static - return true if we need to start software animation timers
+bool AnimationControllerPrivate::blendProperties(int prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog)
+{
+    if (prop == cAnimateAll) {
+        bool needsTimer = false;
+    
+        size_t n = gPropertyWrappers->size();
+        for (unsigned int i = 0; i < n; ++i) {
+            PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i];
+            if (!wrapper->equals(a, b)) {
+                wrapper->blend(dst, a, b, prog);
+                needsTimer = true;
+            }
+        }
+        return needsTimer;
+    }
+    
+    int propIndex = prop - firstCSSProperty;
+    if (propIndex >= 0 && propIndex < numCSSProperties) {
+        int i = gPropertyWrapperMap[propIndex];
+        if (i >= 0) {
+            PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i];
+            wrapper->blend(dst, a, b, prog);
+            return true;
+        }
+    }
+    
+    return false;
+}
+
+CompositeAnimation* AnimationControllerPrivate::accessCompositeAnimation(RenderObject* renderer)
+{
+    CompositeAnimation* animation = m_compositeAnimations.get(renderer);
+    if (!animation) {
+        animation = new CompositeAnimation(this);
+        m_compositeAnimations.set(renderer, animation);
     }
     return animation;
 }
 
 bool AnimationControllerPrivate::clear(RenderObject* renderer)
 {
-    CompositeImplicitAnimation* animation = m_animations.take(renderer);
+    // Return false if we didn't do anything OR we are suspended (so we don't try to
+    // do a setChanged() when suspended
+    CompositeAnimation* animation = m_compositeAnimations.take(renderer);
     if (!animation)
         return false;
-    animation->reset(renderer);
+    animation->resetTransitions(renderer);
+    bool wasSuspended = animation->suspended();
     delete animation;
-    return true;
+    return !wasSuspended;
 }
 
-void AnimationControllerPrivate::updateTimer()
+void AnimationControllerPrivate::styleAvailable()
+{
+    if (m_numStyleAvailableWaiters == 0)
+        return;
+    
+    RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
+    for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); 
+         it != animationsEnd; ++it) {
+        it->second->styleAvailable();
+    }
+}
+
+void AnimationControllerPrivate::updateAnimationTimer()
 {
     bool animating = false;
-    HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator end = m_animations.end();
-    for (HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it) {
-        if (it->second->animating()) {
+    
+    RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
+    for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); 
+         it != animationsEnd; ++it) {
+        CompositeAnimation* compAnim = it->second;
+        if (!compAnim->suspended() && compAnim->animating()) {
             animating = true;
             break;
         }
     }
     
     if (animating) {
-        if (!m_timer.isActive())
-            m_timer.startRepeating(cAnimationTimerDelay);
-    } else if (m_timer.isActive())
-        m_timer.stop();
+        if (!m_animationTimer.isActive())
+            m_animationTimer.startRepeating(cAnimationTimerDelay);
+    } else if (m_animationTimer.isActive())
+        m_animationTimer.stop();
 }
 
-void AnimationControllerPrivate::timerFired(Timer<AnimationControllerPrivate>* timer)
+void AnimationControllerPrivate::updateRenderingDispatcherFired(Timer<AnimationControllerPrivate>*)
+{
+    if (m_frame && m_frame->document()) {
+        m_frame->document()->updateRendering();
+    }
+}
+
+void AnimationControllerPrivate::startUpdateRenderingDispatcher()
+{
+    if (!m_updateRenderingDispatcher.isActive()) {
+        m_updateRenderingDispatcher.startOneShot(0);
+    }
+}
+
+void AnimationControllerPrivate::animationTimerFired(Timer<AnimationControllerPrivate>* timer)
 {
     // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate
     // updateRendering.  It will then call back to us with new information.
     bool animating = false;
-    HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator end = m_animations.end();
-    for (HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it) {
-        if (it->second->animating()) {
+    RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
+    for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); 
+         it != animationsEnd; ++it) {
+        RenderObject* renderer = it->first;
+        CompositeAnimation* compAnim = it->second;
+        if (!compAnim->suspended() && compAnim->animating()) {
             animating = true;
-            it->first->element()->setChanged();
+            compAnim->setAnimating(false);
+            setChanged(renderer->element());
         }
     }
     
     m_frame->document()->updateRendering();
     
-    updateTimer();
+    updateAnimationTimer();
 }
 
-AnimationController::AnimationController(Frame* frame)
-:m_data(new AnimationControllerPrivate(frame))
+bool AnimationControllerPrivate::isAnimatingPropertyOnRenderer(RenderObject* obj, int property) const
 {
+    CompositeAnimation* animation = m_compositeAnimations.get(obj);
+    if (!animation) return false;
+    
+    return animation->isAnimatingProperty(property);
+}
+
+void AnimationControllerPrivate::suspendAnimations(Document* document)
+{
+    RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
+    for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); 
+         it != animationsEnd; ++it) {
+        RenderObject* renderer = it->first;
+        CompositeAnimation* compAnim = it->second;
+        if (renderer->document() == document)
+            compAnim->suspendAnimations();
+    }
+    
+    updateAnimationTimer();
+}
+
+void AnimationControllerPrivate::resumeAnimations(Document* document)
+{
+    RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
+    for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); 
+         it != animationsEnd; ++it) {
+        RenderObject* renderer = it->first;
+        CompositeAnimation* compAnim = it->second;
+        if (renderer->document() == document)
+            compAnim->resumeAnimations();
+    }
+    
+    updateAnimationTimer();
+}
+
+void CompositeAnimation::updateTransitions(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle)
+{
+    // If currentStyle is null, we don't do transitions
+    if (!currentStyle || !targetStyle->transitions())
+        return;
+        
+    // Check to see if we need to update the active transitions
+    for (size_t i = 0; i < targetStyle->transitions()->size(); ++i) {
+        const Animation* anim = (*targetStyle->transitions())[i].get();
+        double duration = anim->duration();
+        double delay = anim->delay();
+
+        // If this is an empty transition, skip it
+        if (duration == 0 && delay <= 0)
+            continue;
+         
+        int prop = anim->property();
+        bool all = prop == cAnimateAll;
+        
+        // Handle both the 'all' and single property cases. For the single prop case, we make only one pass
+        // through the loop
+        for (int i = 0; ; ++i) {
+            if (all) {
+                if (i >= AnimationControllerPrivate::getNumProperties())
+                    break;
+                // get the next prop
+                prop = AnimationControllerPrivate::getPropertyAtIndex(i);
+            }
+
+            // See if there is a current transition for this prop
+            ImplicitAnimation* implAnim = m_transitions.get(prop);
+            bool equal = true;
+            
+            if (implAnim) {
+                // There is one, has our target changed?
+                if (!implAnim->isTargetPropertyEqual(prop, targetStyle)) {
+                    // It has changed - toss it and start over
+                    // Opacity is special since it can pop in and out of RenderLayers. We need to compute
+                    // the blended opacity value between the previous from and to styles and put that in the currentStyle, which
+                    // will become the new fromStyle. This is changing a const RenderStyle, but we know what we are doing, really :-)
+                    if (prop == CSSPropertyOpacity) {
+                        // get the blended value of opacity into the currentStyle (which will be the new fromStyle)
+                        implAnim->blendPropertyValueInStyle(CSSPropertyOpacity, currentStyle);
+                    }
+
+                    implAnim->reset(renderer);
+                    delete implAnim;
+                    m_transitions.remove(prop);
+                    equal = false;
+                }
+            }
+            else
+                // See if we need to start a new transition
+                equal = AnimationControllerPrivate::propertiesEqual(prop, currentStyle, targetStyle);
+            
+            if (!equal) {
+                // AAdd the new transition
+                ImplicitAnimation* animation = new ImplicitAnimation(const_cast<Animation*>(anim), renderer, this);
+                m_transitions.set(prop, animation);
+            }
+            
+            // We only need one pass for the single prop case
+            if (!all)
+                break;
+        }
+    }
+}
+
+RenderStyle* CompositeAnimation::animate(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle)
+{
+    RenderStyle* resultStyle = 0;
+    
+    // We don't do any transitions if we don't have a currentStyle (on startup)
+    updateTransitions(renderer, currentStyle, targetStyle);
+    
+    if (currentStyle) {
+        // Now that we have transition objects ready, let them know about the new goal state.  We want them
+        // to fill in a RenderStyle*& only if needed.
+        CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
+        for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
+            ImplicitAnimation*  anim = it->second;
+            if (anim) {
+                anim->animate(this, renderer, currentStyle, targetStyle, resultStyle);
+            }
+        }
+    }
+    
+    cleanupFinishedAnimations(renderer);
+    
+    return resultStyle ? resultStyle : targetStyle;
+}
+
+// "animating" means that something is running that requires the timer to keep firing
+// (e.g. a transition)
+void CompositeAnimation::setAnimating(bool inAnimating)
+{
+    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
+    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
+        ImplicitAnimation*  transition = it->second;
+        transition->setAnimating(inAnimating);
+    }
+}
 
+bool CompositeAnimation::animating()
+{
+    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
+    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
+        ImplicitAnimation*  transition = it->second;
+        if (transition && transition->animating() && transition->running())
+            return true;
+    }
+    return false;
+}
+
+void CompositeAnimation::resetTransitions(RenderObject* renderer)
+{
+    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
+    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
+        ImplicitAnimation*  transition = it->second;
+        transition->reset(renderer);
+        delete transition;
+    }
+    m_transitions.clear();
+}
+
+void CompositeAnimation::cleanupFinishedAnimations(RenderObject* renderer)
+{
+    if (suspended())
+        return;
+    
+    // Make a list of transitions to be deleted
+    Vector<int> finishedTransitions;
+    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
+
+    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
+        ImplicitAnimation* anim = it->second;
+        if (!anim)
+            continue;
+        if (anim->postactive() && !anim->waitingForEndEvent())
+            finishedTransitions.append(anim->property());
+    }
+    
+    // Delete them
+    for (Vector<int>::iterator it = finishedTransitions.begin(); it != finishedTransitions.end(); ++it) {
+        ImplicitAnimation* anim = m_transitions.get(*it);
+        if (anim) {
+            anim->reset(renderer);
+            delete anim;
+        }
+        m_transitions.remove(*it);
+    }
+}
+
+void CompositeAnimation::setTransitionStartTime(int property, double t)
+{
+    // Set the start time for given property transition
+    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
+    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
+        ImplicitAnimation* anim = it->second;
+        if (anim && anim->waitingForStartTime() && 
+                    (anim->property() == property || anim->property() == cAnimateAll))
+            anim->updateStateMachine(AnimationBase::STATE_INPUT_START_TIME_SET, t);
+    }
+}
+
+void CompositeAnimation::suspendAnimations()
+{
+    if (m_suspended)
+        return;
+    
+    m_suspended = true;
+
+    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
+    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
+        ImplicitAnimation* anim = it->second;
+        if (anim && anim->hasStyle()) {
+            anim->updatePlayState(false);
+        }
+    }
+}
+
+void CompositeAnimation::resumeAnimations()
+{
+    if (!m_suspended)
+        return;
+    
+    m_suspended = false;
+
+    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
+    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
+        ImplicitAnimation* anim = it->second;
+        if (anim && anim->hasStyle()) {
+            anim->updatePlayState(true);
+        }
+    }
+}
+
+void CompositeAnimation::overrideImplicitAnimations(int property)
+{
+    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
+    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
+        ImplicitAnimation* anim = it->second;
+        if (anim && (anim->property() == property || anim->property() == cAnimateAll))
+            anim->setOverridden(true);
+    }
+}
+
+void CompositeAnimation::resumeOverriddenImplicitAnimations(int property)
+{
+    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
+    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
+        ImplicitAnimation* anim = it->second;
+        if (anim && (anim->property() == property || anim->property() == cAnimateAll))
+            anim->setOverridden(false);
+    }
+}
+
+void CompositeAnimation::styleAvailable()
+{
+    if (m_numStyleAvailableWaiters == 0)
+        return;
+    
+    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
+    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
+        ImplicitAnimation* anim = it->second;
+        if (anim && anim->waitingForStyleAvailable())
+            anim->updateStateMachine(AnimationBase::STATE_INPUT_STYLE_AVAILABLE, -1);
+    }
+}
+
+bool CompositeAnimation::isAnimatingProperty(int property) const
+{
+    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
+    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
+        ImplicitAnimation* anim = it->second;
+        if (anim && anim->isAnimatingProperty(property))
+            return true;
+    }
+    return false;
+}
+
+void ImplicitAnimation::animate(CompositeAnimation* animation, RenderObject* renderer, const RenderStyle* currentStyle, 
+                                const RenderStyle* targetStyle, RenderStyle*& ioAnimatedStyle)
+{
+    if (paused())
+        return;
+    
+    // If we get this far and the animation is done, it means we are cleaning up a just finished animation.
+    // If so, send back the targetStyle (it will get tossed later)
+    if (postactive()) {
+        if (!ioAnimatedStyle)
+            ioAnimatedStyle = const_cast<RenderStyle*>(targetStyle);
+        return;
+    }
+
+    // Reset to start the transition if we are new
+    if (isnew())
+        reset(renderer, currentStyle, targetStyle);
+    
+    // Run a cycle of animation.
+    // We know we will need a new render style, so make one if needed
+    if (!ioAnimatedStyle)
+        ioAnimatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle);
+    
+    double prog = progress(1, 0);
+    bool needsAnim = AnimationControllerPrivate::blendProperties(m_property, ioAnimatedStyle, m_fromStyle, m_toStyle, prog);
+    if (needsAnim)
+        setAnimating();
+}
+
+bool ImplicitAnimation::startAnimation(double beginTime)
+{
+    return false;
+}
+
+void ImplicitAnimation::endAnimation(bool reset)
+{
+}
+
+void ImplicitAnimation::onAnimationEnd(double inElapsedTime)
+{
+    // we're converting the animation into a transition here
+    if (!sendTransitionEvent(EventNames::webkitTransitionEndEvent, inElapsedTime)) {
+        // we didn't dispatch an event, which would call endAnimation(), so we'll just end
+        // it here.
+        endAnimation(true);
+    }
+}
+
+bool ImplicitAnimation::sendTransitionEvent(const AtomicString& inEventType, double inElapsedTime)
+{
+    return false; // didn't dispatch an event
+}
+
+void ImplicitAnimation::reset(RenderObject* renderer, const RenderStyle* from /* = 0 */, const RenderStyle* to /* = 0 */)
+{
+    ASSERT((!m_toStyle && !to) || m_toStyle != to);
+    ASSERT((!m_fromStyle && !from) || m_fromStyle != from);
+    if (m_fromStyle)
+        m_fromStyle->deref(renderer->renderArena());
+    if (m_toStyle)
+        m_toStyle->deref(renderer->renderArena());
+    
+    m_fromStyle = const_cast<RenderStyle*>(from);   // it is read-only, other than the ref
+    if (m_fromStyle)
+        m_fromStyle->ref();
+    
+    m_toStyle = const_cast<RenderStyle*>(to);       // it is read-only, other than the ref
+    if (m_toStyle)
+        m_toStyle->ref();
+    
+    // restart the transition
+    if (from && to)
+        updateStateMachine(STATE_INPUT_RESTART_ANIMATION, -1);
+}
+
+void ImplicitAnimation::setOverridden(bool b)
+{
+    if (b != m_overridden) {
+        m_overridden = b;
+        updateStateMachine(m_overridden ? STATE_INPUT_PAUSE_OVERRIDE : STATE_INPUT_RESUME_OVERRIDE, -1);
+    }
+}
+
+bool ImplicitAnimation::affectsProperty(int property) const
+{
+    return m_property == property ||
+    (m_property == cAnimateAll && !AnimationControllerPrivate::propertiesEqual(property, m_fromStyle, m_toStyle));
+}
+
+bool ImplicitAnimation::isTargetPropertyEqual(int prop, RenderStyle* targetStyle)
+{
+    return AnimationControllerPrivate::propertiesEqual(prop, m_toStyle, targetStyle);
+}
+
+void ImplicitAnimation::blendPropertyValueInStyle(int prop, RenderStyle* currentStyle)
+{
+    double prog = progress(1, 0);
+    AnimationControllerPrivate::blendProperties(prop, currentStyle, m_fromStyle, m_toStyle, prog);
+}    
+
+AnimationController::AnimationController(Frame* frame)
+: m_data(new AnimationControllerPrivate(frame))
+{
+    
 }
 
 AnimationController::~AnimationController()
@@ -566,50 +1690,87 @@ AnimationController::~AnimationController()
     delete m_data;
 }
 
-void AnimationController::cancelImplicitAnimations(RenderObject* renderer)
+void AnimationController::cancelAnimations(RenderObject* renderer)
 {
-    if (!m_data->hasImplicitAnimations())
+    if (!m_data->hasAnimations())
         return;
-
+    
     if (m_data->clear(renderer))
-        renderer->element()->setChanged();
+        setChanged(renderer->element());
 }
 
-RenderStyle* AnimationController::updateImplicitAnimations(RenderObject* renderer, RenderStyle* newStyle)
-{
+RenderStyle* AnimationController::updateAnimations(RenderObject* renderer, RenderStyle* newStyle)
+{    
+    // don't do anything if we're in the cache
+    if (!renderer->document() || renderer->document()->inPageCache())
+        return newStyle;
+    
+    RenderStyle* oldStyle = renderer->style();
+    
+    if ((!oldStyle || !oldStyle->transitions()) && !newStyle->transitions())
+        return newStyle;
+    
+    RenderStyle* blendedStyle = newStyle;
+    
     // Fetch our current set of implicit animations from a hashtable.  We then compare them
     // against the animations in the style and make sure we're in sync.  If destination values
     // have changed, we reset the animation.  We then do a blend to get new values and we return
     // a new style.
     ASSERT(renderer->element()); // FIXME: We do not animate generated content yet.
-
-    CompositeImplicitAnimation* animation = m_data->get(renderer, newStyle);
-    if (!animation && !newStyle->transitions())
-        return newStyle;
-
-    RenderStyle* blendedStyle = animation->animate(renderer, renderer->style(), newStyle);
-    m_data->updateTimer();
-
+    
+    CompositeAnimation* rendererAnimations = m_data->accessCompositeAnimation(renderer);
+    blendedStyle = rendererAnimations->animate(renderer, oldStyle, newStyle);
+    
+    m_data->updateAnimationTimer();
+    
     if (blendedStyle != newStyle) {
-        // Do some of the work that CSSStyleSelector::adjustRenderStyle() does, to impose rules
-        // like opacity creating stacking context.
+        // If the animations/transitions change opacity or transform, we neeed to update
+        // the style to impose the stacking rules. Note that this is also
+        // done in CSSStyleSelector::adjustRenderStyle().
         if (blendedStyle->hasAutoZIndex() && (blendedStyle->opacity() < 1.0f || blendedStyle->hasTransform()))
             blendedStyle->setZIndex(0);
     }
-
     return blendedStyle;
 }
 
-void AnimationController::suspendAnimations()
+bool AnimationController::isAnimatingPropertyOnRenderer(RenderObject* obj, int property) const
+{
+    return m_data->isAnimatingPropertyOnRenderer(obj, property);
+}
+
+void AnimationController::suspendAnimations(Document* document)
 {
-    // FIXME: Walk the whole hashtable and call pause on each animation.
-    // Kill our timer.
+#ifdef DEBUG_STATE_MACHINE
+    fprintf(stderr, "AnimationController %p suspendAnimations for document %p\n", this, document);
+#endif
+    m_data->suspendAnimations(document);
 }
 
-void AnimationController::resumeAnimations()
+void AnimationController::resumeAnimations(Document* document)
 {
-    // FIXME: Walk the whole hashtable and call resume on each animation.
-    // Start our timer.
+#ifdef DEBUG_STATE_MACHINE
+    fprintf(stderr, "AnimationController %p resumeAnimations for document %p\n", this, document);
+#endif
+    m_data->resumeAnimations(document);
 }
 
+void AnimationController::startUpdateRenderingDispatcher()
+{
+    m_data->startUpdateRenderingDispatcher();
+}
+
+void AnimationController::styleAvailable()
+{
+    m_data->styleAvailable();
+}
+
+void CompositeAnimation::setWaitingForStyleAvailable(bool waiting)
+{
+    if (waiting)
+        m_numStyleAvailableWaiters++;
+    else
+        m_numStyleAvailableWaiters--;
+    m_animationController->setWaitingForStyleAvailable(waiting);
+}
+    
 }
index fda114372ea400c03c08eb1aef64e1c94746ebf3..90c49d96c196c7cb7dc4dedeffef6cb600741d75 100644 (file)
 #ifndef AnimationController_h
 #define AnimationController_h
 
+#include "CSSPropertyNames.h"
+
 namespace WebCore {
 
 class AnimationControllerPrivate;
+class Document;
 class Frame;
 class RenderObject;
 class RenderStyle;
@@ -42,14 +45,22 @@ public:
     AnimationController(Frame*);
     ~AnimationController();
     
-    void cancelImplicitAnimations(RenderObject*);
-    RenderStyle* updateImplicitAnimations(RenderObject*, RenderStyle* newStyle);
+    void cancelAnimations(RenderObject*);
+    RenderStyle* updateAnimations(RenderObject*, RenderStyle* newStyle);
+    
+    bool isAnimatingPropertyOnRenderer(RenderObject* obj, int property) const;
+    
+    void suspendAnimations(Document* document);
+    void resumeAnimations(Document* document);
+    void updateAnimationTimer();
     
-    void suspendAnimations();
-    void resumeAnimations();
+    void startUpdateRenderingDispatcher();
+    
+    void styleAvailable();
     
 private:
     AnimationControllerPrivate* m_data;
+    
 };
 
 }
index cd297df1a6cd1bf3f2902e397fde7f3f1a8e8538..e061e855c2944ce403bcce00795ff23e83b79f2d 100644 (file)
@@ -1482,7 +1482,7 @@ void Frame::clearTimers(FrameView *view, Document *document)
         if (view->frame()) {
             if (document && document->renderer() && document->renderer()->hasLayer())
                 document->renderer()->layer()->suspendMarquees();
-            view->frame()->animation()->suspendAnimations();
+            view->frame()->animation()->suspendAnimations(document);
             view->frame()->eventHandler()->stopAutoscrollTimer();
         }
     }
index 7bc502ac951fdf8c93f0a7ee6e2da3f6e28e4847..c37d13af8d0bf924e0b7a42073baa0933b03b000 100644 (file)
@@ -2156,7 +2156,7 @@ void RenderObject::handleDynamicFloatPositionChange()
 void RenderObject::setAnimatableStyle(RenderStyle* style)
 {
     if (!isText() && m_style && style)
-        style = animation()->updateImplicitAnimations(this, style);
+        style = animation()->updateAnimations(this, style);
 
     setStyle(style);
 }
@@ -2535,7 +2535,7 @@ void RenderObject::destroy()
     if (AXObjectCache::accessibilityEnabled())
         document()->axObjectCache()->remove(this);
 
-    animation()->cancelImplicitAnimations(this);
+    animation()->cancelAnimations(this);
 
     // By default no ref-counting. RenderWidget::destroy() doesn't call
     // this function because it needs to do ref-counting. If anything
index 87d8445efacf23c71211aa3e01080361fe713863..a377ad11a8043e25be42be30a0d7e0741adb291d 100644 (file)
@@ -72,7 +72,7 @@ void RenderWidget::destroy()
     // So the code below includes copied and pasted contents of
     // both RenderBox::destroy() and RenderObject::destroy().
     // Fix originally made for <rdar://problem/4228818>.
-    animation()->cancelImplicitAnimations(this);
+    animation()->cancelAnimations(this);
 
     if (RenderView* v = view())
         v->removeWidget(this);
index faff7e03011a0bac2dd25c950973aa714ea00d77..e8fcd2a93a5ca068411bd97c0f1d2ee8c185c27d 100644 (file)
@@ -664,111 +664,87 @@ TransformOperation* MatrixTransformOperation::blend(const TransformOperation* fr
                                         fromF + (m_f - fromF) * progress);
 }
 
-Transition::Transition()
-    : m_duration(RenderStyle::initialTransitionDuration())
-    , m_repeatCount(RenderStyle::initialTransitionRepeatCount())
-    , m_timingFunction(RenderStyle::initialTransitionTimingFunction())
-    , m_property(RenderStyle::initialTransitionProperty())
+Animation::Animation()
+    : m_duration(RenderStyle::initialDuration())
+    , m_timingFunction(RenderStyle::initialTimingFunction())
+    , m_delay(RenderStyle::initialDelay())
+    , m_property(RenderStyle::initialProperty())
     , m_durationSet(false)
-    , m_repeatCountSet(false)
     , m_timingFunctionSet(false)
+    , m_delaySet(false)
     , m_propertySet(false)
-    , m_next(0)
 {
 }
 
-Transition::Transition(const Transition& o)
-    : m_duration(o.m_duration)
-    , m_repeatCount(o.m_repeatCount)
+Animation::Animation(const Animation& o)
+    : RefCounted<Animation>()
+    , m_duration(o.m_duration)
     , m_timingFunction(o.m_timingFunction)
+    , m_delay(o.m_delay)
     , m_property(o.m_property)
     , m_durationSet(o.m_durationSet)
-    , m_repeatCountSet(o.m_repeatCountSet)
     , m_timingFunctionSet(o.m_timingFunctionSet)
+    , m_delaySet(o.m_delaySet)
     , m_propertySet(o.m_propertySet)
-    , m_next(o.m_next ? new Transition(*o.m_next) : 0)
-{
-}
-
-Transition::~Transition()
 {
-    delete m_next;
 }
 
-Transition& Transition::operator=(const Transition& o)
+Animation& Animation::operator=(const Animation& o)
 {
-    if (m_next != o.m_next) {
-        delete m_next;
-        m_next = o.m_next ? new Transition(*o.m_next) : 0;
-    }
-
+    m_delay = o.m_delay;
     m_duration = o.m_duration;
-    m_repeatCount = o.m_repeatCount;
     m_timingFunction = o.m_timingFunction;
     m_property = o.m_property;
 
+    m_delaySet = o.m_delaySet;
     m_durationSet = o.m_durationSet;
-    m_repeatCountSet = o.m_repeatCountSet;
     m_timingFunctionSet = o.m_timingFunctionSet;
     m_propertySet = o.m_propertySet;
 
     return *this;
 }
 
-bool Transition::operator==(const Transition& o) const
+bool Animation::animationsMatch(const Animation* o) const
 {
-    return m_duration == o.m_duration && m_repeatCount == o.m_repeatCount && m_timingFunction == o.m_timingFunction &&
-           m_property == o.m_property && m_durationSet == o.m_durationSet && m_repeatCountSet == o.m_repeatCountSet &&
-           m_timingFunctionSet == o.m_timingFunctionSet && m_propertySet == o.m_propertySet &&
-           ((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next);
+    if (!o)
+        return false;
+    
+    bool result = m_duration == o->m_duration &&
+                  m_timingFunction == o->m_timingFunction &&
+                  m_delay == o->m_delay &&
+                  m_property == o->m_property && 
+                  m_durationSet == o->m_durationSet && 
+                  m_timingFunctionSet == o->m_timingFunctionSet && 
+                  m_delaySet == o->m_delaySet &&
+                  m_propertySet == o->m_propertySet;
+    
+    return result;
+}
+
+#define FILL_UNSET_PROPERTY(test, propGet, propSet) \
+for (i = 0; i < size() && (*this)[i]->test(); ++i) { } \
+if (i < size() && i != 0) { \
+    for (size_t j = 0; i < size(); ++i, ++j) \
+        (*this)[i]->propSet((*this)[j]->propGet()); \
 }
 
-void Transition::fillUnsetProperties()
+void AnimationList::fillUnsetProperties()
 {
-    Transition* curr;
-    for (curr = this; curr && curr->isDurationSet(); curr = curr->next()) { }
-    if (curr && curr != this) {
-        // We need to fill in the remaining values with the pattern specified.
-        for (Transition* pattern = this; curr; curr = curr->next()) {
-            curr->m_duration = pattern->m_duration;
-            pattern = pattern->next();
-            if (pattern == curr || !pattern)
-                pattern = this;
-        }
-    }
-    
-    for (curr = this; curr && curr->isRepeatCountSet(); curr = curr->next()) { }
-    if (curr && curr != this) {
-        // We need to fill in the remaining values with the pattern specified.
-        for (Transition* pattern = this; curr; curr = curr->next()) {
-            curr->m_repeatCount = pattern->m_repeatCount;
-            pattern = pattern->next();
-            if (pattern == curr || !pattern)
-                pattern = this;
-        }
-    }
-    
-    for (curr = this; curr && curr->isTimingFunctionSet(); curr = curr->next()) { }
-    if (curr && curr != this) {
-        // We need to fill in the remaining values with the pattern specified.
-        for (Transition* pattern = this; curr; curr = curr->next()) {
-            curr->m_timingFunction = pattern->m_timingFunction;
-            pattern = pattern->next();
-            if (pattern == curr || !pattern)
-                pattern = this;
-        }
-    }
+    size_t i;
+    FILL_UNSET_PROPERTY(isDelaySet, delay, setDelay);
+    FILL_UNSET_PROPERTY(isDurationSet, duration, setDuration);
+    FILL_UNSET_PROPERTY(isTimingFunctionSet, timingFunction, setTimingFunction);
+    FILL_UNSET_PROPERTY(isPropertySet, property, setProperty);
+}
 
-    for (curr = this; curr && curr->isPropertySet(); curr = curr->next()) { }
-    if (curr && curr != this) {
-        // We need to fill in the remaining values with the pattern specified.
-        for (Transition* pattern = this; curr; curr = curr->next()) {
-            curr->m_property = pattern->m_property;
-            pattern = pattern->next();
-            if (pattern == curr || !pattern)
-                pattern = this;
-        }
-    }
+bool AnimationList::operator==(const AnimationList& o) const
+{
+    if (size() != o.size())
+        return false;
+    for (size_t i = 0; i < size(); ++i)
+        if (*at(i) != *o.at(i))
+            return false;
+    return true;
 }
 
 StyleRareNonInheritedData::StyleRareNonInheritedData()
@@ -784,7 +760,7 @@ StyleRareNonInheritedData::StyleRareNonInheritedData()
     , m_appearance(RenderStyle::initialAppearance())
     , m_borderFit(RenderStyle::initialBorderFit())
     , m_boxShadow(0)
-    , m_transition(0)
+    , m_transitions(0)
     , m_mask(FillLayer(MaskFillLayer))
 #if ENABLE(XBL)
     , bindingURI(0)
@@ -811,7 +787,7 @@ StyleRareNonInheritedData::StyleRareNonInheritedData(const StyleRareNonInherited
     , m_borderFit(o.m_borderFit)
     , m_boxShadow(o.m_boxShadow ? new ShadowData(*o.m_boxShadow) : 0)
     , m_boxReflect(o.m_boxReflect)
-    , m_transition(o.m_transition ? new Transition(*o.m_transition) : 0)
+    , m_transitions(o.m_transitions ? new AnimationList(*o.m_transitions) : 0)
     , m_mask(o.m_mask)
     , m_maskBoxImage(o.m_maskBoxImage)
 #if ENABLE(XBL)
@@ -825,7 +801,6 @@ StyleRareNonInheritedData::~StyleRareNonInheritedData()
     delete m_content;
     delete m_counterDirectives;
     delete m_boxShadow;
-    delete m_transition;
 #if ENABLE(XBL)
     delete bindingURI;
 #endif
@@ -896,9 +871,9 @@ bool StyleRareNonInheritedData::reflectionDataEquivalent(const StyleRareNonInher
 
 bool StyleRareNonInheritedData::transitionDataEquivalent(const StyleRareNonInheritedData& o) const
 {
-    if (!m_transition && o.m_transition || m_transition && !o.m_transition)
+    if (!m_transitions && o.m_transitions || m_transitions && !o.m_transitions)
         return false;
-    if (m_transition && o.m_transition && (*m_transition != *o.m_transition))
+    if (m_transitions && o.m_transitions && (*m_transitions != *o.m_transitions))
         return false;
     return true;
 }
@@ -1494,7 +1469,7 @@ RenderStyle::Diff RenderStyle::diff(const RenderStyle* other) const
     // Cursors are not checked, since they will be set appropriately in response to mouse events,
     // so they don't need to cause any repaint or layout.
 
-    // Transitions don't need to be checked either.  We always set the new style on the RenderObject, so we will get a chance to fire off
+    // Animations don't need to be checked either.  We always set the new style on the RenderObject, so we will get a chance to fire off
     // the resulting transition properly.
     return Equal;
 }
@@ -1870,33 +1845,50 @@ const Vector<StyleDashboardRegion>& RenderStyle::noneDashboardRegions()
 
 void RenderStyle::adjustTransitions()
 {
-    if (transitions()) {
-        if (transitions()->isEmpty()) {
-            clearTransitions();
-            return;
+    AnimationList* transitionList = rareNonInheritedData->m_transitions;
+    if (!transitionList)
+        return;
+    
+    if (transitionList->size() == 0) {
+        clearTransitions();
+        return;
+    }
+
+    // get rid of empty transitions and anything beyond them
+    for (size_t i = 0; i < transitionList->size(); ++i) {
+        if ((*transitionList)[i]->isEmpty()) {
+            transitionList->resize(i);
+            break;
         }
+    }
 
-        Transition* next;
-        for (Transition* p = accessTransitions(); p; p = next) {
-            next = p->m_next;
-            if (next && next->isEmpty()) {
-                delete next;
-                p->m_next = 0;
-                break;
+    if (transitionList->size() == 0) {
+        clearTransitions();
+        return;
+    }
+
+    // Repeat patterns into layers that don't have some properties set.
+    transitionList->fillUnsetProperties();
+        
+    // Make sure there are no duplicate properties. This is an O(n^2) algorithm
+    // but the lists tend to be very short, so it is probably ok
+    for (size_t i = 0; i < transitionList->size(); ++i) {
+        for (size_t j = i+1; j < transitionList->size(); ++j) {
+            if ((*transitionList)[i]->property() == (*transitionList)[j]->property()) {
+                // toss i
+                transitionList->remove(i);
+                j = i;
             }
         }
-    
-        // Repeat patterns into layers that don't have some properties set.
-        accessTransitions()->fillUnsetProperties();
     }
 }
 
-Transition* RenderStyle::accessTransitions()
+AnimationList* RenderStyle::accessTransitions()
 {
-    Transition* layer = rareNonInheritedData.access()->m_transition;
-    if (!layer)
-        rareNonInheritedData.access()->m_transition = new Transition();
-    return rareNonInheritedData->m_transition;
+    AnimationList* list = rareNonInheritedData.access()->m_transitions;
+    if (!list)
+        rareNonInheritedData.access()->m_transitions = new AnimationList();
+    return rareNonInheritedData->m_transitions;
 }
 
 }
index ebab8b6e7c74d791d64e3005c1e5a910de153677..e12127f53682f585d309f29a5e1d3a45c559af67 100644 (file)
@@ -1257,60 +1257,74 @@ private:
     double m_y2;
 };
 
-struct Transition {
+class Animation : public RefCounted<Animation> {
 public:
-    Transition();
-    ~Transition();
-
-    Transition* next() const { return m_next; }
-    Transition* next() { return m_next; }
 
+    static PassRefPtr<Animation> create() { return adoptRef(new Animation); };
+    
+    bool isDelaySet() const { return m_delaySet; }
     bool isDurationSet() const { return m_durationSet; }
-    bool isRepeatCountSet() const { return m_repeatCountSet; }
     bool isTimingFunctionSet() const { return m_timingFunctionSet; }
     bool isPropertySet() const { return m_propertySet; }
-    
-    bool isEmpty() const { return !m_durationSet && !m_repeatCountSet && !m_timingFunctionSet && !m_propertySet; }
+
+    bool isEmpty() const
+    {
+        return (!m_durationSet && !m_delaySet && !m_timingFunctionSet && !m_propertySet);
+    }
+
+    bool isEmptyOrZeroDuration() const
+    {
+        return isEmpty() || (m_duration == 0 && m_delay <= 0);
+    }
+
+    void clearDelay() { m_delaySet = false; }
     void clearDuration() { m_durationSet = false; }
-    void clearRepeatCount() { m_repeatCountSet = false; }
     void clearTimingFunction() { m_timingFunctionSet = false; }
+
     void clearProperty() { m_propertySet = false; }
 
-    int duration() const { return m_duration; }
-    int repeatCount() const { return m_repeatCount; }
+    double delay() const { return m_delay; }
+    double duration() const { return m_duration; }
     const TimingFunction& timingFunction() const { return m_timingFunction; }
     int property() const { return m_property; }
-    
-    void setDuration(int d) { m_duration = d; m_durationSet = true; }
-    void setRepeatCount(int c) { m_repeatCount = c; m_repeatCountSet = true; }
+
+    void setDelay(double c) { m_delay = c; m_delaySet = true; }
+    void setDuration(double d) { ASSERT(d >= 0); m_duration = d; m_durationSet = true; }
     void setTimingFunction(const TimingFunction& f) { m_timingFunction = f; m_timingFunctionSet = true; }
     void setProperty(int t) { m_property = t; m_propertySet = true; }
 
-    void setNext(Transition* n) { if (m_next != n) { delete m_next; m_next = n; } }
+    Animation& operator=(const Animation& o);
 
-    Transition& operator=(const Transition& o);    
-    Transition(const Transition& o);
+    // return true if all members of this class match (excluding m_next)
+    bool animationsMatch(const Animation* t) const;
 
-    bool operator==(const Transition& o) const;
-    bool operator!=(const Transition& o) const {
-        return !(*this == o);
-    }
+    // return true every Animation in the chain (defined by m_next) match 
+    bool operator==(const Animation& o) const { return animationsMatch(&o); }
+    bool operator!=(const Animation& o) const { return !(*this == o); }
 
-    void fillUnsetProperties();
-
-    int m_duration;
-    int m_repeatCount;
+private:
+    Animation();
+    Animation(const Animation& o);
+    
+    double m_duration;
     TimingFunction m_timingFunction;
+    double m_delay;
     int m_property;
 
-    bool m_durationSet;
-    bool m_repeatCountSet;
-    bool m_timingFunctionSet;
-    bool m_propertySet;
-
-    Transition* m_next;
+    bool m_durationSet       : 1;
+    bool m_timingFunctionSet : 1;
+    bool m_delaySet          : 1;
+    bool m_propertySet       : 1;
 };
 
+class AnimationList : public Vector<RefPtr<Animation> >
+{
+public:
+    void fillUnsetProperties();
+    bool operator==(const AnimationList& o) const;
+};    
+
+
 class StyleReflection : public RefCounted<StyleReflection> {
 public:
     static PassRefPtr<StyleReflection> create()
@@ -1389,7 +1403,7 @@ public:
     
     RefPtr<StyleReflection> m_boxReflect;
 
-    Transition* m_transition;
+    AnimationList* m_transitions;
 
     FillLayer m_mask;
     NinePieceImage m_maskBoxImage;
@@ -2030,9 +2044,9 @@ public:
         return background->m_outline._offset;
     }
     ShadowData* textShadow() const { return rareInheritedData->textShadow; }
-    Color textStrokeColor() const { return rareInheritedData->textStrokeColor; }
+    const Color& textStrokeColor() const { return rareInheritedData->textStrokeColor; }
     float textStrokeWidth() const { return rareInheritedData->textStrokeWidth; }
-    Color textFillColor() const { return rareInheritedData->textFillColor; }
+    const Color& textFillColor() const { return rareInheritedData->textFillColor; }
     float opacity() const { return rareNonInheritedData->opacity; }
     EAppearance appearance() const { return static_cast<EAppearance>(rareNonInheritedData->m_appearance); }
     EBoxAlignment boxAlign() const { return static_cast<EBoxAlignment>(rareNonInheritedData->flexibleBox->align); }
@@ -2087,8 +2101,10 @@ public:
     // End CSS3 Getters
 
     // Apple-specific property getter methods
-    Transition* accessTransitions();
-    const Transition* transitions() const { return rareNonInheritedData->m_transition; }
+    AnimationList* accessTransitions();
+    const AnimationList* transitions() const { return rareNonInheritedData->m_transitions; }
+    bool hasTransitions() const { return rareNonInheritedData->m_transitions && rareNonInheritedData->m_transitions->size() > 0; }
+
     int lineClamp() const { return rareNonInheritedData->lineClamp; }
     bool textSizeAdjust() const { return rareInheritedData->textSizeAdjust; }
     ETextSecurity textSecurity() const { return static_cast<ETextSecurity>(rareInheritedData->textSecurity); }
@@ -2350,10 +2366,17 @@ public:
     void setTransformOriginX(Length l) { SET_VAR(rareNonInheritedData.access()->m_transform, m_x, l); }
     void setTransformOriginY(Length l) { SET_VAR(rareNonInheritedData.access()->m_transform, m_y, l); }
     // End CSS3 Setters
-   
+
     // Apple-specific property setters
-    void clearTransitions() { delete rareNonInheritedData.access()->m_transition; rareNonInheritedData.access()->m_transition = 0; }
-    void inheritTransitions(const Transition* parent) { clearTransitions(); if (parent) rareNonInheritedData.access()->m_transition = new Transition(*parent); }
+    void clearTransitions()
+    {
+        if (rareNonInheritedData.access()->m_transitions) {
+            delete rareNonInheritedData.access()->m_transitions;
+            rareNonInheritedData.access()->m_transitions = 0;
+        }
+    }
+
+    void inheritTransitions(const AnimationList* parent) { clearTransitions(); if (parent) rareNonInheritedData.access()->m_transitions = new AnimationList(*parent); }
     void adjustTransitions();
     void setLineClamp(int c) { SET_VAR(rareNonInheritedData, lineClamp, c); }
     void setTextSizeAdjust(bool b) { SET_VAR(rareInheritedData, textSizeAdjust, b); }
@@ -2511,10 +2534,10 @@ public:
     static Length initialTransformOriginY() { return Length(50.0, Percent); }
     
     // Keep these at the end.
-    static int initialTransitionDuration() { return 0; }
-    static int initialTransitionRepeatCount() { return 1; }
-    static TimingFunction initialTransitionTimingFunction() { return TimingFunction(); }
-    static int initialTransitionProperty() { return cAnimateAll; }
+    static float initialDelay() { return 0; }
+    static double initialDuration() { return 0; }
+    static TimingFunction initialTimingFunction() { return TimingFunction(); }
+    static int initialProperty() { return cAnimateAll; }
     static int initialLineClamp() { return -1; }
     static bool initialTextSizeAdjust() { return true; }
     static ETextSecurity initialTextSecurity() { return TSNONE; }