Bug 755084 Part 2: Perform CSS animations of transform and opacity on the compositor r=roc, dbaron
authorDavid Zbarsky <dzbarsky@gmail.com>
Wed, 25 Jul 2012 01:48:09 -0700
changeset 100385 808fc2bd4e8cc50b05a89be9e2a642b7f7b5f791
parent 100384 54b1484cd125c8d667e9a139d0d42461a053339b
child 100386 697f5b87eae9f9b2e1a5fcadce03decf721e1bc3
push id12474
push userdzbarsky@gmail.com
push dateWed, 25 Jul 2012 08:48:14 +0000
treeherdermozilla-inbound@32d16d0f87c9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, dbaron
bugs755084
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 755084 Part 2: Perform CSS animations of transform and opacity on the compositor r=roc, dbaron
content/base/public/nsINode.h
content/smil/nsSMILKeySpline.h
layout/base/FrameLayerBuilder.cpp
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/ipc/RenderFrameParent.cpp
layout/style/AnimationCommon.cpp
layout/style/AnimationCommon.h
layout/style/Makefile.in
layout/style/nsAnimationManager.cpp
layout/style/nsAnimationManager.h
layout/style/nsStyleAnimation.cpp
layout/style/nsStyleAnimation.h
layout/style/nsStyleTransformMatrix.cpp
layout/style/nsStyleTransformMatrix.h
modules/libpref/src/init/all.js
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -1286,16 +1286,18 @@ private:
     // Set if the element has some style states locked
     ElementHasLockedStyleStates,
     // Set if element has pointer locked
     ElementHasPointerLock,
     // Set if the node may have DOMMutationObserver attached to it.
     NodeMayHaveDOMMutationObserver,
     // Set if node is Content
     NodeIsContent,
+    // Set if the node has animations or transitions
+    ElementHasAnimations,
     // Guard value
     BooleanFlagCount
   };
 
   void SetBoolFlag(BooleanFlag name, bool value) {
     PR_STATIC_ASSERT(BooleanFlagCount <= 8*sizeof(mBoolFlags));
     mBoolFlags = (mBoolFlags & ~(1 << name)) | (value << name);
   }
@@ -1351,16 +1353,18 @@ public:
   bool MayHaveDOMMutationObserver()
     { return GetBoolFlag(NodeMayHaveDOMMutationObserver); }
   void SetMayHaveDOMMutationObserver()
     { SetBoolFlag(NodeMayHaveDOMMutationObserver, true); }
   bool HasListenerManager() { return HasFlag(NODE_HAS_LISTENERMANAGER); }
   bool HasPointerLock() const { return GetBoolFlag(ElementHasPointerLock); }
   void SetPointerLock() { SetBoolFlag(ElementHasPointerLock); }
   void ClearPointerLock() { ClearBoolFlag(ElementHasPointerLock); }
+  bool MayHaveAnimations() { return GetBoolFlag(ElementHasAnimations); }
+  void SetMayHaveAnimations() { SetBoolFlag(ElementHasAnimations); }
 protected:
   void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
   void SetInDocument() { SetBoolFlag(IsInDocument); }
   void SetNodeIsContent() { SetBoolFlag(NodeIsContent); }
   void ClearInDocument() { ClearBoolFlag(IsInDocument); }
   void SetIsElement() { SetBoolFlag(NodeIsElement); }
   void ClearIsElement() { ClearBoolFlag(NodeIsElement); }
   void SetHasID() { SetBoolFlag(ElementHasID); }
--- a/content/smil/nsSMILKeySpline.h
+++ b/content/smil/nsSMILKeySpline.h
@@ -21,16 +21,21 @@ public:
    * SMILANIM 3.2.3. They must each be in the range 0.0 <= x <= 1.0
    */
   nsSMILKeySpline(double aX1, double aY1,
                   double aX2, double aY2)
   {
     Init(aX1, aY1, aX2, aY2);
   }
 
+  double X1() const { return mX1; }
+  double Y1() const { return mY1; }
+  double X2() const { return mX2; }
+  double Y2() const { return mY2; }
+
   void Init(double aX1, double aY1,
             double aX2, double aY2);
 
   /**
    * Gets the output (y) value for an input (x).
    *
    * @param aX  The input x value. A floating-point number between 0 and
    *            1 (inclusive).
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -16,16 +16,19 @@
 #include "gfxUtils.h"
 #include "nsImageFrame.h"
 #include "nsRenderingContext.h"
 #include "MaskLayerImageCache.h"
 
 #include "mozilla/Preferences.h"
 #include "sampler.h"
 
+#include "nsAnimationManager.h"
+#include "nsTransitionManager.h"
+
 #ifdef DEBUG
 #include <stdio.h>
 #endif
 
 using namespace mozilla::layers;
 
 namespace mozilla {
 
@@ -623,17 +626,17 @@ FrameLayerBuilder::DisplayItemDataEntry:
 void
 FrameLayerBuilder::FlashPaint(gfxContext *aContext)
 {
   static bool sPaintFlashingEnabled;
   static bool sPaintFlashingPrefCached = false;
 
   if (!sPaintFlashingPrefCached) {
     sPaintFlashingPrefCached = true;
-    mozilla::Preferences::AddBoolVarCache(&sPaintFlashingEnabled, 
+    mozilla::Preferences::AddBoolVarCache(&sPaintFlashingEnabled,
                                           "nglayout.debug.paint_flashing");
   }
 
   if (sPaintFlashingEnabled) {
     float r = float(rand()) / RAND_MAX;
     float g = float(rand()) / RAND_MAX;
     float b = float(rand()) / RAND_MAX;
     aContext->SetColor(gfxRGBA(r, g, b, 0.2));
@@ -988,17 +991,17 @@ ContainerState::CreateOrRecycleMaskImage
   } else {
     // Create a new layer
     result = mManager->CreateImageLayer();
     if (!result)
       return nsnull;
     result->SetUserData(&gMaskLayerUserData, new MaskLayerUserData());
     result->SetForceSingleTile(true);
   }
-  
+
   return result.forget();
 }
 
 static nsIntPoint
 GetTranslationForThebesLayer(ThebesLayer* aLayer)
 {
   gfxMatrix transform;
   if (!aLayer->GetTransform().Is2D(&transform) ||
@@ -1226,17 +1229,17 @@ ContainerState::ThebesLayerData::UpdateC
       }
     }
     mCommonClipCount = clipCount;
     NS_ASSERTION(mItemClip.mRoundedClipRects.Length() >= PRUint32(mCommonClipCount),
                  "Inconsistent common clip count.");
   } else {
     // first item in the layer
     mCommonClipCount = aCurrentClip.mRoundedClipRects.Length();
-  } 
+  }
 }
 
 already_AddRefed<ImageContainer>
 ContainerState::ThebesLayerData::CanOptimizeImageLayer()
 {
   if (!mImage) {
     return nsnull;
   }
@@ -1248,17 +1251,17 @@ void
 ContainerState::PopThebesLayerData()
 {
   NS_ASSERTION(!mThebesLayerDataStack.IsEmpty(), "Can't pop");
 
   PRInt32 lastIndex = mThebesLayerDataStack.Length() - 1;
   ThebesLayerData* data = mThebesLayerDataStack[lastIndex];
 
   nsRefPtr<Layer> layer;
-  nsRefPtr<ImageContainer> imageContainer = data->CanOptimizeImageLayer(); 
+  nsRefPtr<ImageContainer> imageContainer = data->CanOptimizeImageLayer();
 
   if ((data->mIsSolidColorInVisibleRegion || imageContainer) &&
       data->mLayer->GetValidRegion().IsEmpty()) {
     NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && imageContainer),
                  "Can't be a solid color as well as an image!");
     if (imageContainer) {
       nsRefPtr<ImageLayer> imageLayer = CreateOrRecycleImageLayer();
       imageLayer->SetContainer(imageContainer);
@@ -1274,17 +1277,17 @@ ContainerState::PopThebesLayerData()
       layer = imageLayer;
     } else {
       nsRefPtr<ColorLayer> colorLayer = CreateOrRecycleColorLayer();
       colorLayer->SetIsFixedPosition(data->mLayer->GetIsFixedPosition());
       colorLayer->SetColor(data->mSolidColor);
 
       // Copy transform
       colorLayer->SetTransform(data->mLayer->GetTransform());
-      
+
       // Clip colorLayer to its visible region, since ColorLayers are
       // allowed to paint outside the visible region. Here we rely on the
       // fact that uniform display items fill rectangles; obviously the
       // area to fill must contain the visible region, and because it's
       // a rectangle, it must therefore contain the visible region's GetBounds.
       // Note that the visible region is already clipped appropriately.
       nsIntRect visibleRect = data->mVisibleRegion.GetBounds();
       colorLayer->SetClipRect(&visibleRect);
@@ -1305,17 +1308,17 @@ ContainerState::PopThebesLayerData()
     layer = data->mLayer;
     imageContainer = nsnull;
   }
 
   gfxMatrix transform;
   if (!layer->GetTransform().Is2D(&transform)) {
     NS_ERROR("Only 2D transformations currently supported");
   }
-  
+
   // ImageLayers are already configured with a visible region
   if (!imageContainer) {
     NS_ASSERTION(!transform.HasNonIntegerTranslation(),
                  "Matrix not just an integer translation?");
     // Convert from relative to the container to relative to the
     // ThebesLayer itself.
     nsIntRegion rgn = data->mVisibleRegion;
     rgn.MoveBy(-nsIntPoint(PRInt32(transform.x0), PRInt32(transform.y0)));
@@ -1635,19 +1638,19 @@ PaintInactiveLayer(nsDisplayListBuilder*
   // This item has an inactive layer. Render it to a ThebesLayer
   // using a temporary BasicLayerManager.
   PRInt32 appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
   nsIntRect itemVisibleRect =
     aItem->GetVisibleRect().ToOutsidePixels(appUnitsPerDevPixel);
 
   nsRefPtr<gfxContext> context = aContext;
 #ifdef MOZ_DUMP_PAINTING
-  nsRefPtr<gfxASurface> surf; 
+  nsRefPtr<gfxASurface> surf;
   if (gfxUtils::sDumpPainting) {
-    surf = gfxPlatform::GetPlatform()->CreateOffscreenSurface(itemVisibleRect.Size(), 
+    surf = gfxPlatform::GetPlatform()->CreateOffscreenSurface(itemVisibleRect.Size(),
                                                               gfxASurface::CONTENT_COLOR_ALPHA);
     surf->SetDeviceOffset(-itemVisibleRect.TopLeft());
     context = new gfxContext(surf);
   }
 #endif
 
   nsRefPtr<BasicLayerManager> tempManager = new BasicLayerManager();
   tempManager->SetUserData(&gLayerManagerLayerBuilder, new LayerManagerLayerBuilder(aLayerBuilder, false));
@@ -1670,17 +1673,17 @@ PaintInactiveLayer(nsDisplayListBuilder*
   } else {
     tempManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder);
   }
   aLayerBuilder->DidEndTransaction(tempManager);
  
 #ifdef MOZ_DUMP_PAINTING
   if (gfxUtils::sDumpPainting) {
     DumpPaintedImage(aItem, surf);
-  
+
     surf->SetDeviceOffset(gfxPoint(0, 0));
     aContext->SetSource(surf, itemVisibleRect.TopLeft());
     aContext->Rectangle(itemVisibleRect);
     aContext->Fill();
     aItem->SetPainted();
   }
 #endif
 }
@@ -2116,19 +2119,19 @@ ChooseScaleAndSetTransform(FrameLayerBui
     // scaled out of sight anyway.
     if (fabs(scale.width) < 1e-8 || fabs(scale.height) < 1e-8) {
       scale = gfxSize(1.0, 1.0);
     }
   } else {
     scale = gfxSize(1.0, 1.0);
   }
 
-  // Apply the inverse of our resolution-scale before the rest of our transform
-  transform = gfx3DMatrix::ScalingMatrix(1.0/scale.width, 1.0/scale.height, 1.0)*transform;
   aLayer->SetTransform(transform);
+  // Store the inverse of our resolution-scale on the layer
+  aLayer->SetScale(1.0f/float(scale.width), 1.0f/float(scale.height));
 
   FrameLayerBuilder::ContainerParameters
     result(scale.width, scale.height, aIncomingScale);
   if (aTransform) {
     result.mInTransformedSubtree = true;
     if (aContainerFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer)) {
       result.mInActiveTransformedSubtree = true;
     }
@@ -2547,28 +2550,28 @@ FrameLayerBuilder::GetThebesLayerResolut
 #ifdef MOZ_DUMP_PAINTING
 static void DebugPaintItem(nsRenderingContext* aDest, nsDisplayItem *aItem, nsDisplayListBuilder* aBuilder)
 {
   bool snap;
   nsRect appUnitBounds = aItem->GetBounds(aBuilder, &snap);
   gfxRect bounds(appUnitBounds.x, appUnitBounds.y, appUnitBounds.width, appUnitBounds.height);
   bounds.ScaleInverse(aDest->AppUnitsPerDevPixel());
 
-  nsRefPtr<gfxASurface> surf = 
-    gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(bounds.width, bounds.height), 
+  nsRefPtr<gfxASurface> surf =
+    gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(bounds.width, bounds.height),
                                                        gfxASurface::CONTENT_COLOR_ALPHA);
   surf->SetDeviceOffset(-bounds.TopLeft());
   nsRefPtr<gfxContext> context = new gfxContext(surf);
   nsRefPtr<nsRenderingContext> ctx = new nsRenderingContext();
   ctx->Init(aDest->DeviceContext(), context);
 
   aItem->Paint(aBuilder, ctx);
   DumpPaintedImage(aItem, surf);
   aItem->SetPainted();
-    
+
   surf->SetDeviceOffset(gfxPoint(0, 0));
   aDest->ThebesContext()->SetSource(surf, bounds.TopLeft());
   aDest->ThebesContext()->Rectangle(bounds);
   aDest->ThebesContext()->Fill();
 }
 #endif
 
 /*
@@ -3020,17 +3023,17 @@ CalculateBounds(nsTArray<FrameLayerBuild
     bounds.UnionRect(bounds, aRects[i].mRect);
    }
  
   return nsLayoutUtils::RectToGfxRect(bounds, A2D);
 }
  
 void
 ContainerState::SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aClip,
-                               PRUint32 aRoundedRectClipCount) 
+                               PRUint32 aRoundedRectClipCount)
 {
   // don't build an unnecessary mask
   nsIntRect layerBounds = aLayer->GetVisibleRegion().GetBounds();
   if (aClip.mRoundedClipRects.IsEmpty() ||
       aRoundedRectClipCount <= 0 ||
       layerBounds.IsEmpty()) {
     return;
   }
@@ -3046,17 +3049,17 @@ ContainerState::SetupMaskLayer(Layer *aL
   }
   newData.mScaleX = mParameters.mXScale;
   newData.mScaleY = mParameters.mYScale;
 
   if (*userData == newData) {
     aLayer->SetMaskLayer(maskLayer);
     return;
   }
- 
+
   // calculate a more precise bounding rect
   const PRInt32 A2D = mContainerFrame->PresContext()->AppUnitsPerDevPixel();
   gfxRect boundingRect = CalculateBounds(newData.mRoundedClipRects, A2D);
   boundingRect.Scale(mParameters.mXScale, mParameters.mYScale);
 
   PRUint32 maxSize = mManager->GetMaxTextureSize();
   NS_ASSERTION(maxSize > 0, "Invalid max texture size");
   nsIntSize surfaceSize(NS_MIN<PRInt32>(boundingRect.Width(), maxSize),
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -5,16 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /*
  * structures that represent things to be painted (ordered in z-order),
  * used during painting and hit testing
  */
 
+#include "mozilla/layers/PLayers.h"
+
 #include "nsDisplayList.h"
 
 #include "nsCSSRendering.h"
 #include "nsRenderingContext.h"
 #include "nsISelectionController.h"
 #include "nsIPresShell.h"
 #include "nsRegion.h"
 #include "nsFrameManager.h"
@@ -31,23 +33,355 @@
 #include "nsIInterfaceRequestorUtils.h"
 #include "BasicLayers.h"
 #include "nsBoxFrame.h"
 #include "nsViewportFrame.h"
 #include "nsSVGEffects.h"
 #include "nsSVGElement.h"
 #include "nsSVGClipPathFrame.h"
 #include "sampler.h"
+#include "nsAnimationManager.h"
+#include "nsIViewManager.h"
 
 #include "mozilla/StandardInteger.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 typedef FrameMetrics::ViewID ViewID;
 
+static void AddTransformFunctions(nsCSSValueList* aList,
+                                  nsStyleContext* aContext,
+                                  nsPresContext* aPresContext,
+                                  nsRect& aBounds,
+                                  float aAppUnitsPerPixel,
+                                  InfallibleTArray<TransformFunction>& aFunctions)
+{
+  if (aList->mValue.GetUnit() == eCSSUnit_None) {
+    return;
+  }
+
+  for (const nsCSSValueList* curr = aList; curr; curr = curr->mNext) {
+    const nsCSSValue& currElem = curr->mValue;
+    NS_ASSERTION(currElem.GetUnit() == eCSSUnit_Function,
+                 "Stream should consist solely of functions!");
+    nsCSSValue::Array* array = currElem.GetArrayValue();
+    bool canStoreInRuleTree = true;
+    switch (nsStyleTransformMatrix::TransformFunctionOf(array)) {
+      case eCSSKeyword_rotatex:
+      {
+        double theta = array->Item(1).GetAngleValueInRadians();
+        aFunctions.AppendElement(RotationX(theta));
+        break;
+      }
+      case eCSSKeyword_rotatey:
+      {
+        double theta = array->Item(1).GetAngleValueInRadians();
+        aFunctions.AppendElement(RotationY(theta));
+        break;
+      }
+      case eCSSKeyword_rotatez:
+      {
+        double theta = array->Item(1).GetAngleValueInRadians();
+        aFunctions.AppendElement(RotationZ(theta));
+        break;
+      }
+      case eCSSKeyword_rotate:
+      {
+        double theta = array->Item(1).GetAngleValueInRadians();
+        aFunctions.AppendElement(Rotation(theta));
+        break;
+      }
+      case eCSSKeyword_rotate3d:
+      {
+        double x = array->Item(1).GetFloatValue();
+        double y = array->Item(2).GetFloatValue();
+        double z = array->Item(3).GetFloatValue();
+        double theta = array->Item(4).GetAngleValueInRadians();
+        aFunctions.AppendElement(Rotation3D(x, y, z, theta));
+        break;
+      }
+      case eCSSKeyword_scalex:
+      {
+        double x = array->Item(1).GetFloatValue();
+        aFunctions.AppendElement(Scale(x, 1, 1));
+        break;
+      }
+      case eCSSKeyword_scaley:
+      {
+        double y = array->Item(1).GetFloatValue();
+        aFunctions.AppendElement(Scale(1, y, 1));
+        break;
+      }
+      case eCSSKeyword_scalez:
+      {
+        double z = array->Item(1).GetFloatValue();
+        aFunctions.AppendElement(Scale(1, 1, z));
+        break;
+      }
+      case eCSSKeyword_scale:
+      {
+        double x = array->Item(1).GetFloatValue();
+        // scale(x) is shorthand for scale(x, x);
+        double y = array->Count() == 2 ? x : array->Item(2).GetFloatValue();
+        aFunctions.AppendElement(Scale(x, y, 1));
+        break;
+      }
+      case eCSSKeyword_scale3d:
+      {
+        double x = array->Item(1).GetFloatValue();
+        double y = array->Item(2).GetFloatValue();
+        double z = array->Item(3).GetFloatValue();
+        aFunctions.AppendElement(Scale(x, y, z));
+        break;
+      }
+      case eCSSKeyword_translatex:
+      {
+        double x = nsStyleTransformMatrix::ProcessTranslatePart(
+          array->Item(1), aContext, aPresContext, canStoreInRuleTree,
+          aBounds.Width(), aAppUnitsPerPixel);
+        aFunctions.AppendElement(Translation(x, 0, 0));
+        break;
+      }
+      case eCSSKeyword_translatey:
+      {
+        double y = nsStyleTransformMatrix::ProcessTranslatePart(
+          array->Item(1), aContext, aPresContext, canStoreInRuleTree,
+          aBounds.Height(), aAppUnitsPerPixel);
+        aFunctions.AppendElement(Translation(0, y, 0));
+        break;
+      }
+      case eCSSKeyword_translatez:
+      {
+        double z = nsStyleTransformMatrix::ProcessTranslatePart(
+          array->Item(1), aContext, aPresContext, canStoreInRuleTree,
+          0, aAppUnitsPerPixel);
+        aFunctions.AppendElement(Translation(0, 0, z));
+        break;
+      }
+      case eCSSKeyword_translate:
+      {
+        double x = nsStyleTransformMatrix::ProcessTranslatePart(
+          array->Item(1), aContext, aPresContext, canStoreInRuleTree,
+          aBounds.Width(), aAppUnitsPerPixel);
+        // translate(x) is shorthand for translate(x, 0)
+        double y = 0;
+        if (array->Count() == 3) {
+           y = nsStyleTransformMatrix::ProcessTranslatePart(
+            array->Item(2), aContext, aPresContext, canStoreInRuleTree,
+            aBounds.Height(), aAppUnitsPerPixel);
+        }
+        aFunctions.AppendElement(Translation(x, y, 0));
+        break;
+      }
+      case eCSSKeyword_translate3d:
+      {
+        double x = nsStyleTransformMatrix::ProcessTranslatePart(
+          array->Item(1), aContext, aPresContext, canStoreInRuleTree,
+          aBounds.Width(), aAppUnitsPerPixel);
+        double y = nsStyleTransformMatrix::ProcessTranslatePart(
+          array->Item(2), aContext, aPresContext, canStoreInRuleTree,
+          aBounds.Height(), aAppUnitsPerPixel);
+        double z = nsStyleTransformMatrix::ProcessTranslatePart(
+          array->Item(3), aContext, aPresContext, canStoreInRuleTree,
+          0, aAppUnitsPerPixel);
+
+        aFunctions.AppendElement(Translation(x, y, z));
+        break;
+      }
+      case eCSSKeyword_skewx:
+      {
+        double x = array->Item(1).GetFloatValue();
+        aFunctions.AppendElement(SkewX(x));
+        break;
+      }
+      case eCSSKeyword_skewy:
+      {
+        double y = array->Item(1).GetFloatValue();
+        aFunctions.AppendElement(SkewY(y));
+        break;
+      }
+      case eCSSKeyword_matrix:
+      {
+        gfx3DMatrix matrix;
+        matrix._11 = array->Item(1).GetFloatValue();
+        matrix._12 = array->Item(2).GetFloatValue();
+        matrix._13 = 0;
+        matrix._14 = array->Item(3).GetFloatValue();
+        matrix._21 = array->Item(4).GetFloatValue();
+        matrix._22 = array->Item(5).GetFloatValue();
+        matrix._23 = 0;
+        matrix._24 = array->Item(6).GetFloatValue();
+        matrix._31 = 0;
+        matrix._32 = 0;
+        matrix._33 = 1;
+        matrix._34 = 0;
+        matrix._41 = 0;
+        matrix._42 = 0;
+        matrix._43 = 0;
+        matrix._44 = 1;
+        aFunctions.AppendElement(TransformMatrix(matrix));
+        break;
+      }
+      case eCSSKeyword_matrix3d:
+      {
+        gfx3DMatrix matrix;
+        matrix._11 = array->Item(1).GetFloatValue();
+        matrix._12 = array->Item(2).GetFloatValue();
+        matrix._13 = array->Item(3).GetFloatValue();
+        matrix._14 = array->Item(4).GetFloatValue();
+        matrix._21 = array->Item(5).GetFloatValue();
+        matrix._22 = array->Item(6).GetFloatValue();
+        matrix._23 = array->Item(7).GetFloatValue();
+        matrix._24 = array->Item(8).GetFloatValue();
+        matrix._31 = array->Item(9).GetFloatValue();
+        matrix._32 = array->Item(10).GetFloatValue();
+        matrix._33 = array->Item(11).GetFloatValue();
+        matrix._34 = array->Item(12).GetFloatValue();
+        matrix._41 = array->Item(13).GetFloatValue();
+        matrix._42 = array->Item(14).GetFloatValue();
+        matrix._43 = array->Item(15).GetFloatValue();
+        matrix._44 = array->Item(16).GetFloatValue();
+        aFunctions.AppendElement(TransformMatrix(matrix));
+        break;
+      }
+      case eCSSKeyword_perspective:
+      {
+        aFunctions.AppendElement(Perspective(array->Item(1).GetFloatValue()));
+        break;
+      }
+      default:
+        NS_ERROR("Function not handled yet!");
+    }
+  }
+}
+
+static TimingFunction
+ToTimingFunction(css::ComputedTimingFunction& aCTF)
+{
+  if (aCTF.GetType() == nsTimingFunction::Function) {
+    const nsSMILKeySpline* spline = aCTF.GetFunction();
+    return TimingFunction(CubicBezierFunction(spline->X1(), spline->Y1(),
+                                              spline->X2(), spline->Y2()));
+  }
+
+  PRUint32 type = aCTF.GetType() == nsTimingFunction::StepStart ? 1 : 2;
+  return TimingFunction(StepFunction(aCTF.GetSteps(), type));
+}
+
+static void
+AddTransformAnimations(ElementAnimations* ea, Layer* aLayer,
+                       const nsPoint& aOrigin)
+{
+  if (!ea)
+    return;
+  NS_ASSERTION(aLayer->AsContainerLayer(), "Should only animate ContainerLayer");
+  nsIFrame* frame = ea->mElement->GetPrimaryFrame();
+  nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(frame);
+  float scale = nsDeviceContext::AppUnitsPerCSSPixel();
+  gfxPoint3D offsetToTransformOrigin =
+    nsDisplayTransform::GetDeltaToMozTransformOrigin(frame, scale, &bounds);
+  gfxPoint3D offsetToPerspectiveOrigin =
+    nsDisplayTransform::GetDeltaToMozPerspectiveOrigin(frame, scale);
+  nscoord perspective = 0.0;
+  nsStyleContext* parentStyleContext = frame->GetStyleContext()->GetParent();
+  if (parentStyleContext) {
+    const nsStyleDisplay* disp = parentStyleContext->GetStyleDisplay();
+    if (disp && disp->mChildPerspective.GetUnit() == eStyleUnit_Coord) {
+      perspective = disp->mChildPerspective.GetCoordValue();
+    }
+  }
+
+  for (PRUint32 animIdx = 0; animIdx < ea->mAnimations.Length(); animIdx++) {
+    ElementAnimation* anim = &ea->mAnimations[animIdx];
+    if (!anim->CanPerformOnCompositor(ea->mElement, TimeStamp::Now())) {
+      continue;
+    }
+    float iterations = anim->mIterationCount != NS_IEEEPositiveInfinity()
+                         ? anim->mIterationCount : -1;
+    for (PRUint32 propIdx = 0; propIdx < anim->mProperties.Length(); propIdx++) {
+      AnimationProperty* property = &anim->mProperties[propIdx];
+      InfallibleTArray<AnimationSegment> segments;
+
+      if (property->mProperty != eCSSProperty_transform) {
+        continue;
+      }
+
+      for (PRUint32 segIdx = 0; segIdx < property->mSegments.Length(); segIdx++) {
+        AnimationPropertySegment* segment = &property->mSegments[segIdx];
+        nsCSSValueList* list = segment->mFromValue.GetCSSValueListValue();
+        InfallibleTArray<TransformFunction> fromFunctions;
+        AddTransformFunctions(list, frame->GetStyleContext(),
+                              frame->PresContext(), bounds,
+                              scale, fromFunctions);
+
+        list = segment->mToValue.GetCSSValueListValue();
+        InfallibleTArray<TransformFunction> toFunctions;
+        AddTransformFunctions(list, frame->GetStyleContext(),
+                              frame->PresContext(), bounds,
+                              scale, toFunctions);
+
+        segments.AppendElement(AnimationSegment(fromFunctions, toFunctions,
+                                                segment->mFromKey, segment->mToKey,
+                                                ToTimingFunction(segment->mTimingFunction)));
+      }
+
+      if (segments.Length() == 0) {
+        continue;
+      }
+      aLayer->AddAnimation(Animation(anim->mStartTime,
+                                     anim->mIterationDuration,
+                                     segments,
+                                     iterations,
+                                     anim->mDirection,
+                                     TransformData(aOrigin, offsetToTransformOrigin,
+                                                   offsetToPerspectiveOrigin,
+                                                   bounds, perspective)));
+     }
+  }
+}
+
+static void
+AddOpacityAnimations(ElementAnimations* ea, Layer* aLayer)
+{
+  if (!ea)
+    return;
+  NS_ASSERTION(aLayer->AsContainerLayer(), "Should only animate ContainerLayer");
+  for (PRUint32 animIdx = 0; animIdx < ea->mAnimations.Length(); animIdx++) {
+    ElementAnimation* anim = &ea->mAnimations[animIdx];
+    float iterations = anim->mIterationCount != NS_IEEEPositiveInfinity()
+                         ? anim->mIterationCount : -1;
+    if (!anim->CanPerformOnCompositor(ea->mElement, TimeStamp::Now())) {
+      continue;
+    }
+    for (PRUint32 propIdx = 0; propIdx < anim->mProperties.Length(); propIdx++) {
+      AnimationProperty* property = &anim->mProperties[propIdx];
+      InfallibleTArray<AnimationSegment> segments;
+      if (property->mProperty != eCSSProperty_opacity) {
+        continue;
+      }
+
+      for (PRUint32 segIdx = 0; segIdx < property->mSegments.Length(); segIdx++) {
+        AnimationPropertySegment* segment = &property->mSegments[segIdx];
+        segments.AppendElement(AnimationSegment(Opacity(segment->mFromValue.GetFloatValue()),
+                                                Opacity(segment->mToValue.GetFloatValue()),
+                                                segment->mFromKey,
+                                                segment->mToKey,
+                                                ToTimingFunction(segment->mTimingFunction)));
+      }
+
+      aLayer->AddAnimation(Animation(anim->mStartTime,
+                                     anim->mIterationDuration,
+                                     segments,
+                                     iterations,
+                                     anim->mDirection,
+                                     null_t()));
+     }
+  }
+}
+
 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
     Mode aMode, bool aBuildCaret)
     : mReferenceFrame(aReferenceFrame),
       mIgnoreScrollFrame(nsnull),
       mCurrentTableItem(nsnull),
       mFinalTransparentRegion(nsnull),
       mCachedOffsetFrame(aReferenceFrame),
       mCachedOffset(0, 0),
@@ -194,31 +528,33 @@ static void RecordFrameMetrics(nsIFrame*
   nsIScrollableFrame* scrollableFrame = nsnull;
   if (aScrollFrame)
     scrollableFrame = aScrollFrame->GetScrollTargetFrame();
 
   if (scrollableFrame) {
     nsRect contentBounds = scrollableFrame->GetScrollRange();
     contentBounds.width += scrollableFrame->GetScrollPortRect().width;
     contentBounds.height += scrollableFrame->GetScrollPortRect().height;
-    metrics.mCSSContentRect = gfx::Rect(nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.x),
-                                        nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.y),
-                                        nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.width),
-                                        nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.height));
+    metrics.mCSSContentRect =
+      mozilla::gfx::Rect(nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.x),
+                         nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.y),
+                         nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.width),
+                         nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.height));
     metrics.mContentRect = contentBounds.ScaleToNearestPixels(
       aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
     metrics.mViewportScrollOffset = scrollableFrame->GetScrollPosition().ScaleToNearestPixels(
       aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
   }
   else {
     nsRect contentBounds = aForFrame->GetRect();
-    metrics.mCSSContentRect = gfx::Rect(nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.x),
-                                        nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.y),
-                                        nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.width),
-                                        nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.height));
+    metrics.mCSSContentRect =
+      mozilla::gfx::Rect(nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.x),
+                         nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.y),
+                         nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.width),
+                         nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.height));
     metrics.mContentRect = contentBounds.ScaleToNearestPixels(
       aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
   }
 
   metrics.mScrollId = aScrollId;
 
   nsIPresShell* presShell = presContext->GetPresShell();
   metrics.mResolution = gfxSize(presShell->GetXResolution(), presShell->GetYResolution());
@@ -295,17 +631,17 @@ nsDisplayListBuilder::EnterPresShell(nsI
   state->mPresShell = aReferenceFrame->PresContext()->PresShell();
   state->mCaretFrame = nsnull;
   state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
 
   state->mPresShell->UpdateCanvasBackground();
 
   if (mIsPaintingToWindow) {
     mReferenceFrame->AddPaintedPresShell(state->mPresShell);
-    
+
     state->mPresShell->IncrementPaintCount();
   }
 
   bool buildCaret = mBuildCaret;
   if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
     if (state->mPresShell->IsPaintingSuppressed()) {
       mHadToIgnoreSuppression = true;
     }
@@ -358,17 +694,17 @@ nsDisplayListBuilder::MarkFramesForDispl
                                                const nsRect& aDirtyRect) {
   for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) {
     mFramesMarkedForDisplay.AppendElement(e.get());
     MarkOutOfFlowFrameForDisplay(aDirtyFrame, e.get(), aDirtyRect);
   }
 }
 
 void
-nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame, const nsRect& aDirtyRect) 
+nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame, const nsRect& aDirtyRect)
 {
   nsAutoTArray<nsIFrame::ChildList,4> childListArray;
   aDirtyFrame->GetChildLists(&childListArray);
   nsIFrame::ChildListArrayIterator lists(childListArray);
   for (; !lists.IsDone(); lists.Next()) {
     nsFrameList::Enumerator childFrames(lists.CurrentList());
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       nsIFrame *child = childFrames.get();
@@ -761,34 +1097,34 @@ void nsDisplayList::HitTest(nsDisplayLis
     // so that recursive calls to HitTest have more buffer space.
     item = aState->mItemBuffer[i];
     aState->mItemBuffer.SetLength(i);
 
     bool snap;
     if (aRect.Intersects(item->GetBounds(aBuilder, &snap))) {
       nsAutoTArray<nsIFrame*, 16> outFrames;
       item->HitTest(aBuilder, aRect, aState, &outFrames);
-      
-      // For 3d transforms with preserve-3d we add hit frames into the temp list 
+
+      // For 3d transforms with preserve-3d we add hit frames into the temp list
       // so we can sort them later, otherwise we add them directly to the output list.
       nsTArray<nsIFrame*> *writeFrames = aOutFrames;
       if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
           item->GetUnderlyingFrame()->Preserves3D()) {
         if (outFrames.Length()) {
           nsDisplayTransform *transform = static_cast<nsDisplayTransform*>(item);
           nsPoint point = aRect.TopLeft();
           // A 1x1 rect means a point, otherwise use the center of the rect
           if (aRect.width != 1 || aRect.height != 1) {
             point = aRect.Center();
           }
           temp.AppendElement(FramesWithDepth(transform->GetHitDepthAtPoint(point)));
           writeFrames = &temp[temp.Length() - 1].mFrames;
         }
       } else {
-        // We may have just finished a run of consecutive preserve-3d transforms, 
+        // We may have just finished a run of consecutive preserve-3d transforms,
         // so flush these into the destination array before processing our frame list.
         FlushFramesArray(temp, aOutFrames);
       }
 
       for (PRUint32 j = 0; j < outFrames.Length(); j++) {
         nsIFrame *f = outFrames.ElementAt(j);
         // Handle the XUL 'mousethrough' feature and 'pointer-events'.
         if (!GetMouseThrough(f) &&
@@ -823,17 +1159,17 @@ static void Sort(nsDisplayList* aList, P
     }
     prev = item;
   }
   if (sorted) {
     aList->AppendToTop(&list1);
     aList->AppendToTop(&list2);
     return;
   }
-  
+
   Sort(&list1, half, aCmp, aClosure);
   Sort(&list2, aCount - half, aCmp, aClosure);
 
   for (i = 0; i < aCount; ++i) {
     if (list1.GetBottom() &&
         (!list2.GetBottom() ||
          aCmp(list1.GetBottom(), list2.GetBottom(), aClosure))) {
       aList->AppendToTop(list1.RemoveBottom());
@@ -887,17 +1223,17 @@ void nsDisplayList::ExplodeAnonymousChil
       nsDisplayItem* j;
       while ((j = list->RemoveBottom()) != nsnull) {
         tmp.AppendToTop(static_cast<nsDisplayWrapList*>(i)->
             WrapWithClone(aBuilder, j));
       }
       i->~nsDisplayItem();
     }
   }
-  
+
   AppendToTop(&tmp);
 }
 
 void nsDisplayList::SortByZOrder(nsDisplayListBuilder* aBuilder,
                                  nsIContent* aCommonAncestor) {
   Sort(aBuilder, IsZOrderLEQ, aCommonAncestor);
 }
 
@@ -1974,24 +2310,31 @@ nsRegion nsDisplayOpacity::GetOpaqueRegi
   return nsRegion();
 }
 
 // nsDisplayOpacity uses layers for rendering
 already_AddRefed<Layer>
 nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder,
                              LayerManager* aManager,
                              const ContainerParameters& aContainerParameters) {
-  nsRefPtr<Layer> layer = GetLayerBuilderForManager(aManager)->
+  nsRefPtr<Layer> container = GetLayerBuilderForManager(aManager)->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
                            aContainerParameters, nsnull);
-  if (!layer)
+  if (!container)
     return nsnull;
 
-  layer->SetOpacity(mFrame->GetStyleDisplay()->mOpacity);
-  return layer.forget();
+  container->SetOpacity(mFrame->GetStyleDisplay()->mOpacity);
+
+  container->ClearAnimations();
+  ElementAnimations* ea =
+    nsAnimationManager::GetAnimationsForCompositor(mFrame->GetContent(),
+                                                   eCSSProperty_opacity);
+  AddOpacityAnimations(ea, container);
+
+  return container.forget();
 }
 
 /**
  * This doesn't take into account layer scaling --- the layer may be
  * rendered at a higher (or lower) resolution, affecting the retained layer
  * size --- but this should be good enough.
  */
 static bool
@@ -2006,16 +2349,22 @@ IsItemTooSmallForActiveLayer(nsDisplayIt
 
 nsDisplayItem::LayerState
 nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder,
                                 LayerManager* aManager,
                                 const ContainerParameters& aParameters) {
   if (mFrame->AreLayersMarkedActive(nsChangeHint_UpdateOpacityLayer) &&
       !IsItemTooSmallForActiveLayer(this))
     return LAYER_ACTIVE;
+  if (mFrame->GetContent()) {
+    if (nsAnimationManager::GetAnimationsForCompositor(mFrame->GetContent(),
+                                                       eCSSProperty_opacity)) {
+      return LAYER_ACTIVE;
+    }
+  }
   nsIFrame* activeScrolledRoot =
     nsLayoutUtils::GetActiveScrolledRootFor(mFrame, nsnull);
   return !ChildrenCanBeInactive(aBuilder, aManager, aParameters, mList, activeScrolledRoot)
       ? LAYER_ACTIVE : LAYER_INACTIVE;
 }
 
 bool
 nsDisplayOpacity::ComputeVisibility(nsDisplayListBuilder* aBuilder,
@@ -2357,17 +2706,17 @@ nsDisplayScrollInfoLayer::TryMerge(nsDis
 }
 
 bool
 nsDisplayScrollInfoLayer::ShouldFlattenAway(nsDisplayListBuilder* aBuilder)
 {
   // Layer metadata for a particular scroll frame needs to be unique. Only
   // one nsDisplayScrollLayer (with rendered content) or one
   // nsDisplayScrollInfoLayer (with only the metadata) should survive the
-  // visibility computation. 
+  // visibility computation.
   return RemoveScrollLayerCount() == 1;
 }
 
 nsDisplayClip::nsDisplayClip(nsDisplayListBuilder* aBuilder,
                              nsIFrame* aFrame, nsDisplayItem* aItem,
                              const nsRect& aRect)
    : nsDisplayWrapList(aBuilder, aFrame, aItem,
        aFrame == aItem->GetUnderlyingFrame() ? aItem->ToReferenceFrame() : aBuilder->ToReferenceFrame(aFrame)),
@@ -2640,17 +2989,17 @@ nsDisplayTransform::GetFrameBoundsForTra
 #else
 
 nsRect
 nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
 {
   NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!");
 
   nsRect result;
-  
+
   if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
     // TODO: SVG needs to define what percentage translations resolve against.
     return result;
   }
 
   /* Iterate through the continuation list, unioning together all the
    * bounding rects.
    */
@@ -2667,92 +3016,93 @@ nsDisplayTransform::GetFrameBoundsForTra
 
   return result;
 }
 
 #endif
 
 /* Returns the delta specified by the -moz-transform-origin property.
  * This is a positive delta, meaning that it indicates the direction to move
- * to get from (0, 0) of the frame to the transform origin.
+ * to get from (0, 0) of the frame to the transform origin.  This function is
+ * called off the main thread.
  */
-static
-gfxPoint3D GetDeltaToMozTransformOrigin(const nsIFrame* aFrame,
-                                        float aAppUnitsPerPixel,
-                                        const nsRect* aBoundsOverride)
+/* static */ gfxPoint3D
+nsDisplayTransform::GetDeltaToMozTransformOrigin(const nsIFrame* aFrame,
+                                                 float aAppUnitsPerPixel,
+                                                 const nsRect* aBoundsOverride)
 {
   NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
   NS_PRECONDITION(aFrame->IsTransformed(),
                   "Shouldn't get a delta for an untransformed frame!");
 
   /* For both of the coordinates, if the value of -moz-transform is a
    * percentage, it's relative to the size of the frame.  Otherwise, if it's
    * a distance, it's already computed for us!
    */
   const nsStyleDisplay* display = aFrame->GetStyleDisplay();
   nsRect boundingRect = (aBoundsOverride ? *aBoundsOverride :
                          nsDisplayTransform::GetFrameBoundsForTransform(aFrame));
 
   /* Allows us to access named variables by index. */
-  gfxPoint3D result;
-  gfxFloat* coords[3] = {&result.x, &result.y, &result.z};
+  float coords[3];
   const nscoord* dimensions[2] =
     {&boundingRect.width, &boundingRect.height};
 
   for (PRUint8 index = 0; index < 2; ++index) {
     /* If the -moz-transform-origin specifies a percentage, take the percentage
      * of the size of the box.
      */
     const nsStyleCoord &coord = display->mTransformOrigin[index];
     if (coord.GetUnit() == eStyleUnit_Calc) {
       const nsStyleCoord::Calc *calc = coord.GetCalcValue();
-      *coords[index] =
+      coords[index] =
         NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) *
           calc->mPercent +
         NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
     } else if (coord.GetUnit() == eStyleUnit_Percent) {
-      *coords[index] =
+      coords[index] =
         NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) *
         coord.GetPercentValue();
     } else {
       NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
-      *coords[index] =
+      coords[index] =
         NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel);
     }
     if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
         coord.GetUnit() != eStyleUnit_Percent) {
       // <length> values represent offsets from the origin of the SVG element's
       // user space, not the top left of its bounds, so we must adjust for that:
       nscoord offset =
         (index == 0) ? aFrame->GetPosition().x : aFrame->GetPosition().y;
-      *coords[index] -= NSAppUnitsToFloatPixels(offset, aAppUnitsPerPixel);
+      coords[index] -= NSAppUnitsToFloatPixels(offset, aAppUnitsPerPixel);
     }
   }
 
-  *coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(),
-                                       aAppUnitsPerPixel);
+  coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(),
+                                      aAppUnitsPerPixel);
   /* Adjust based on the origin of the rectangle. */
-  result.x += NSAppUnitsToFloatPixels(boundingRect.x, aAppUnitsPerPixel);
-  result.y += NSAppUnitsToFloatPixels(boundingRect.y, aAppUnitsPerPixel);
-
-  return result;
+  coords[0] += NSAppUnitsToFloatPixels(boundingRect.x, aAppUnitsPerPixel);
+  coords[1] += NSAppUnitsToFloatPixels(boundingRect.y, aAppUnitsPerPixel);
+
+  return gfxPoint3D(coords[0], coords[1], coords[2]);
 }
 
 /* Returns the delta specified by the -moz-perspective-origin property.
  * This is a positive delta, meaning that it indicates the direction to move
- * to get from (0, 0) of the frame to the perspective origin.
+ * to get from (0, 0) of the frame to the perspective origin. This function is
+ * called off the main thread.
  */
-static
-gfxPoint3D GetDeltaToMozPerspectiveOrigin(const nsIFrame* aFrame,
-                                          float aAppUnitsPerPixel)
+/* static */ gfxPoint3D
+nsDisplayTransform::GetDeltaToMozPerspectiveOrigin(const nsIFrame* aFrame,
+                                                   float aAppUnitsPerPixel)
 {
   NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
   NS_PRECONDITION(aFrame->IsTransformed(),
                   "Shouldn't get a delta for an untransformed frame!");
-  NS_PRECONDITION(aFrame->GetParentStyleContextFrame(), 
+  NS_PRECONDITION(aFrame->GetParentStyleContextFrame(),
                   "Can't get delta without a style parent!");
 
   /* For both of the coordinates, if the value of -moz-perspective-origin is a
    * percentage, it's relative to the size of the frame.  Otherwise, if it's
    * a distance, it's already computed for us!
    */
 
   //TODO: Should this be using our bounds or the parent's bounds?
@@ -2800,107 +3150,124 @@ gfxPoint3D GetDeltaToMozPerspectiveOrigi
 }
 
 /* Wraps up the -moz-transform matrix in a change-of-basis matrix pair that
  * translates from local coordinate space to transform coordinate space, then
  * hands it back.
  */
 gfx3DMatrix
 nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame,
-                                                const nsPoint &aOrigin,
+                                                const nsPoint& aOrigin,
                                                 float aAppUnitsPerPixel,
                                                 const nsRect* aBoundsOverride,
+                                                const nsCSSValueList* aTransformOverride,
+                                                gfxPoint3D* aToMozOrigin,
+                                                gfxPoint3D* aToPerspectiveOrigin,
+                                                nscoord* aChildPerspective,
                                                 nsIFrame** aOutAncestor)
 {
-  NS_PRECONDITION(aFrame, "Cannot get transform matrix for a null frame!");
+  NS_PRECONDITION(aFrame || (aToMozOrigin && aBoundsOverride && aToPerspectiveOrigin &&
+                             aTransformOverride && aChildPerspective),
+                  "Should have frame or necessary infromation to construct matrix");
+
+  NS_PRECONDITION(!(aFrame && (aToMozOrigin || aToPerspectiveOrigin ||
+                             aTransformOverride || aChildPerspective)),
+                  "Should not have both frame and necessary infromation to construct matrix");
 
   if (aOutAncestor) {
       *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
   }
 
   /* Account for the -moz-transform-origin property by translating the
    * coordinate space to the new origin.
    */
   gfxPoint3D toMozOrigin =
-    GetDeltaToMozTransformOrigin(aFrame, aAppUnitsPerPixel, aBoundsOverride);
+    aFrame ? GetDeltaToMozTransformOrigin(aFrame, aAppUnitsPerPixel, aBoundsOverride) : *aToMozOrigin;
   gfxPoint3D newOrigin =
     gfxPoint3D(NSAppUnitsToFloatPixels(aOrigin.x, aAppUnitsPerPixel),
                NSAppUnitsToFloatPixels(aOrigin.y, aAppUnitsPerPixel),
                0.0f);
 
   /* Get the underlying transform matrix.  This requires us to get the
    * bounds of the frame.
    */
-  const nsStyleDisplay* disp = aFrame->GetStyleDisplay();
+  const nsStyleDisplay* disp = aFrame ? aFrame->GetStyleDisplay() : nsnull;
   nsRect bounds = (aBoundsOverride ? *aBoundsOverride :
                    nsDisplayTransform::GetFrameBoundsForTransform(aFrame));
 
   /* Get the matrix, then change its basis to factor in the origin. */
   bool dummy;
   gfx3DMatrix result;
   // Call IsSVGTransformed() regardless of the value of
   // disp->mSpecifiedTransform, since we still need any transformFromSVGParent.
   gfxMatrix svgTransform, transformFromSVGParent;
   bool hasSVGTransforms =
-    aFrame->IsSVGTransformed(&svgTransform, &transformFromSVGParent);
+    aFrame && aFrame->IsSVGTransformed(&svgTransform, &transformFromSVGParent);
   /* Transformed frames always have a transform, or are preserving 3d (and might still have perspective!) */
-  if (disp->mSpecifiedTransform) {
+  if (aTransformOverride) {
+    result = nsStyleTransformMatrix::ReadTransforms(aTransformOverride, nsnull, nsnull,
+                                                    dummy, bounds, aAppUnitsPerPixel);
+  } else if (disp->mSpecifiedTransform) {
     result = nsStyleTransformMatrix::ReadTransforms(disp->mSpecifiedTransform,
                                                     aFrame->GetStyleContext(),
                                                     aFrame->PresContext(),
                                                     dummy, bounds, aAppUnitsPerPixel);
   } else if (hasSVGTransforms) {
     // Correct the translation components for zoom:
     float pixelsPerCSSPx = aFrame->PresContext()->AppUnitsPerCSSPixel() /
                              aAppUnitsPerPixel;
     svgTransform.x0 *= pixelsPerCSSPx;
     svgTransform.y0 *= pixelsPerCSSPx;
     result = gfx3DMatrix::From2D(svgTransform);
-  } else {
-     NS_ASSERTION(aFrame->GetStyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D ||
-                  aFrame->GetStyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN,
-                  "If we don't have a transform, then we must have another reason to have an nsDisplayTransform created");
   }
 
   if (hasSVGTransforms && !transformFromSVGParent.IsIdentity()) {
     // Correct the translation components for zoom:
     float pixelsPerCSSPx = aFrame->PresContext()->AppUnitsPerCSSPixel() /
                              aAppUnitsPerPixel;
     transformFromSVGParent.x0 *= pixelsPerCSSPx;
     transformFromSVGParent.y0 *= pixelsPerCSSPx;
     result = result * gfx3DMatrix::From2D(transformFromSVGParent);
   }
 
   const nsStyleDisplay* parentDisp = nsnull;
-  nsStyleContext* parentStyleContext = aFrame->GetStyleContext()->GetParent();
+  nsStyleContext* parentStyleContext = aFrame ? aFrame->GetStyleContext()->GetParent(): nsnull;
   if (parentStyleContext) {
     parentDisp = parentStyleContext->GetStyleDisplay();
   }
-  if (nsLayoutUtils::Are3DTransformsEnabled() &&
-      parentDisp && parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord &&
-      parentDisp->mChildPerspective.GetCoordValue() > 0.0) {
+  nscoord perspectiveCoord = 0;
+  if (parentDisp && parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord) {
+    perspectiveCoord = parentDisp->mChildPerspective.GetCoordValue();
+  }
+  if (aChildPerspective) {
+    perspectiveCoord = *aChildPerspective;
+  }
+
+  if (nsLayoutUtils::Are3DTransformsEnabled() && perspectiveCoord > 0.0) {
     gfx3DMatrix perspective;
     perspective._34 =
-      -1.0 / NSAppUnitsToFloatPixels(parentDisp->mChildPerspective.GetCoordValue(),
-                                     aAppUnitsPerPixel);
+      -1.0 / NSAppUnitsToFloatPixels(perspectiveCoord, aAppUnitsPerPixel);
     /* At the point when perspective is applied, we have been translated to the transform origin.
      * The translation to the perspective origin is the difference between these values.
      */
-    gfxPoint3D toPerspectiveOrigin = GetDeltaToMozPerspectiveOrigin(aFrame, aAppUnitsPerPixel);
+    gfxPoint3D toPerspectiveOrigin = aFrame ? GetDeltaToMozPerspectiveOrigin(aFrame, aAppUnitsPerPixel) : *aToPerspectiveOrigin;
     result = result * nsLayoutUtils::ChangeMatrixBasis(toPerspectiveOrigin - toMozOrigin, perspective);
   }
 
-  if (aFrame->Preserves3D() && nsLayoutUtils::Are3DTransformsEnabled()) {
+  if (aFrame && aFrame->Preserves3D() && nsLayoutUtils::Are3DTransformsEnabled()) {
       // Include the transform set on our parent
       NS_ASSERTION(aFrame->GetParent() &&
                    aFrame->GetParent()->IsTransformed() &&
                    aFrame->GetParent()->Preserves3DChildren(),
                    "Preserve3D mismatch!");
-      gfx3DMatrix parent = GetResultingTransformMatrix(aFrame->GetParent(), aOrigin - aFrame->GetPosition(),
-                                                       aAppUnitsPerPixel, nsnull, aOutAncestor);
+      gfx3DMatrix parent =
+        GetResultingTransformMatrix(aFrame->GetParent(),
+                                    aOrigin - aFrame->GetPosition(),
+                                    aAppUnitsPerPixel, nsnull, nsnull, nsnull,
+                                    nsnull, nsnull, aOutAncestor);
       return nsLayoutUtils::ChangeMatrixBasis(newOrigin + toMozOrigin, result) * parent;
   }
 
   return nsLayoutUtils::ChangeMatrixBasis
     (newOrigin + toMozOrigin, result);
 }
 
 bool
@@ -2921,17 +3288,17 @@ nsDisplayTransform::ShouldPrerenderTrans
         return true;
       }
     }
   }
   return false;
 }
 
 /* If the matrix is singular, or a hidden backface is shown, the frame won't be visible or hit. */
-static bool IsFrameVisible(nsIFrame* aFrame, const gfx3DMatrix& aMatrix) 
+static bool IsFrameVisible(nsIFrame* aFrame, const gfx3DMatrix& aMatrix)
 {
   if (aMatrix.IsSingular()) {
     return false;
   }
   if (aFrame->GetStyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
       aMatrix.IsBackfaceVisible()) {
     return false;
   }
@@ -2939,63 +3306,75 @@ static bool IsFrameVisible(nsIFrame* aFr
 }
 
 const gfx3DMatrix&
 nsDisplayTransform::GetTransform(float aAppUnitsPerPixel)
 {
   if (mTransform.IsIdentity() || mCachedAppUnitsPerPixel != aAppUnitsPerPixel) {
     mTransform =
       GetResultingTransformMatrix(mFrame, ToReferenceFrame(),
-                                  aAppUnitsPerPixel,
-                                  nsnull);
+                                  aAppUnitsPerPixel);
     mCachedAppUnitsPerPixel = aAppUnitsPerPixel;
   }
   return mTransform;
 }
 
 already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder,
                                                        LayerManager *aManager,
                                                        const ContainerParameters& aContainerParameters)
 {
-  const gfx3DMatrix& newTransformMatrix = 
+  const gfx3DMatrix& newTransformMatrix =
     GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel());
 
   if (!IsFrameVisible(mFrame, newTransformMatrix)) {
     return nsnull;
   }
 
   nsRefPtr<ContainerLayer> container = GetLayerBuilderForManager(aManager)->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, *mStoredList.GetList(),
                            aContainerParameters, &newTransformMatrix);
 
   // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all flags,
   // so we never need to explicitely unset this flag.
   if (mFrame->Preserves3D() || mFrame->Preserves3DChildren()) {
     container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_PRESERVE_3D);
   }
+
+  container->ClearAnimations();
+  ElementAnimations* ea =
+    nsAnimationManager::GetAnimationsForCompositor(mFrame->GetContent(),
+                                                   eCSSProperty_transform);
+  AddTransformAnimations(ea, container, ToReferenceFrame());
+
   return container.forget();
 }
 
 nsDisplayItem::LayerState
 nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerParameters& aParameters) {
   // Here we check if the *post-transform* bounds of this item are big enough
   // to justify an active layer.
   if (mFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer) &&
       !IsItemTooSmallForActiveLayer(this))
     return LAYER_ACTIVE;
   if (!GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel()).Is2D() || mFrame->Preserves3D())
     return LAYER_ACTIVE;
+  if (mFrame->GetContent()) {
+    if (nsAnimationManager::GetAnimationsForCompositor(mFrame->GetContent(),
+                                                       eCSSProperty_transform)) {
+      return LAYER_ACTIVE;
+    }
+  }
   nsIFrame* activeScrolledRoot =
     nsLayoutUtils::GetActiveScrolledRootFor(mFrame, nsnull);
-  return !mStoredList.ChildrenCanBeInactive(aBuilder, 
-                                            aManager, 
+  return !mStoredList.ChildrenCanBeInactive(aBuilder,
+                                            aManager,
                                             aParameters,
-                                            *mStoredList.GetList(), 
+                                            *mStoredList.GetList(),
                                             activeScrolledRoot)
       ? LAYER_ACTIVE : LAYER_INACTIVE;
 }
 
 bool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder *aBuilder,
                                              nsRegion *aVisibleRegion,
                                              const nsRect& aAllowVisibleRegionExpansion)
 {
@@ -3004,19 +3383,19 @@ bool nsDisplayTransform::ComputeVisibili
    * think that it's painting in its original rectangular coordinate space.
    * If we can't untransform, take the entire overflow rect */
   nsRect untransformedVisibleRect;
   float factor = nsPresContext::AppUnitsPerCSSPixel();
   if (ShouldPrerenderTransformedContent(aBuilder, mFrame) ||
       !UntransformRectMatrix(mVisibleRect,
                              GetTransform(factor),
                              factor,
-                             &untransformedVisibleRect)) 
+                             &untransformedVisibleRect))
   {
-    untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf() +  
+    untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf() +
                                aBuilder->ToReferenceFrame(mFrame);
   }
   nsRegion untransformedVisible = untransformedVisibleRect;
   // Call RecomputeVisiblity instead of ComputeVisibility since
   // nsDisplayItem::ComputeVisibility should only be called from
   // nsDisplayList::ComputeVisibility (which sets mVisibleRect on the item)
   mStoredList.RecomputeVisibility(aBuilder, &untransformedVisible);
   return true;
@@ -3071,17 +3450,17 @@ void nsDisplayTransform::HitTest(nsDispl
 
     gfxRect rect = matrix.Inverse().ProjectRectBounds(originalRect);;
 
     resultingRect = nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
                            NSFloatPixelsToAppUnits(float(rect.Y()), factor),
                            NSFloatPixelsToAppUnits(float(rect.Width()), factor),
                            NSFloatPixelsToAppUnits(float(rect.Height()), factor));
   }
-  
+
 
 #ifdef DEBUG_HIT
   printf("Frame: %p\n", dynamic_cast<void *>(mFrame));
   printf("  Untransformed point: (%f, %f)\n", resultingRect.X(), resultingRect.Y());
   PRUint32 originalFrameCount = aOutFrames.Length();
 #endif
 
   mStoredList.HitTest(aBuilder, resultingRect, aState, aOutFrames);
@@ -3097,18 +3476,18 @@ void nsDisplayTransform::HitTest(nsDispl
 
 float
 nsDisplayTransform::GetHitDepthAtPoint(const nsPoint& aPoint)
 {
   float factor = nsPresContext::AppUnitsPerCSSPixel();
   gfx3DMatrix matrix = GetTransform(factor);
 
   NS_ASSERTION(IsFrameVisible(mFrame, matrix), "We can't have hit a frame that isn't visible!");
-    
-  gfxPoint point = 
+
+  gfxPoint point =
     matrix.Inverse().ProjectPoint(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, factor),
                                            NSAppUnitsToFloatPixels(aPoint.y, factor)));
 
   gfxPoint3D transformed = matrix.Transform3D(gfxPoint3D(point.x, point.y, 0));
   return transformed.z;
 }
 
 /* The bounding rectangle for the object is the overflow rectangle translated
@@ -3147,17 +3526,17 @@ nsRegion nsDisplayTransform::GetOpaqueRe
                                              bool* aSnap)
 {
   *aSnap = false;
   nsRect untransformedVisible;
   float factor = nsPresContext::AppUnitsPerCSSPixel();
   if (!UntransformRectMatrix(mVisibleRect, GetTransform(factor), factor, &untransformedVisible)) {
       return nsRegion();
   }
-  
+
   const gfx3DMatrix& matrix = GetTransform(nsPresContext::AppUnitsPerCSSPixel());
 
   nsRegion result;
   gfxMatrix matrix2d;
   bool tmpSnap;
   if (matrix.Is2D(&matrix2d) &&
       matrix2d.PreservesAxisAlignedRectangles() &&
       mStoredList.GetOpaqueRegion(aBuilder, &tmpSnap).Contains(untransformedVisible)) {
@@ -3294,17 +3673,17 @@ bool nsDisplayTransform::UntransformRect
                                            nsRect* aOutRect)
 {
   NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!");
 
   /* Grab the matrix.  If the transform is degenerate, just hand back the
    * empty rect.
    */
   float factor = nsPresContext::AppUnitsPerCSSPixel();
-  gfx3DMatrix matrix = GetResultingTransformMatrix(aFrame, aOrigin, factor, nsnull);
+  gfx3DMatrix matrix = GetResultingTransformMatrix(aFrame, aOrigin, factor);
 
   return UntransformRectMatrix(aUntransformedBounds, matrix, factor, aOutRect);
 }
 
 nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
                                          nsIFrame* aFrame, nsDisplayList* aList)
     : nsDisplayWrapList(aBuilder, aFrame, aList),
       mEffectsBounds(aFrame->GetVisualOverflowRectRelativeToSelf())
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -2336,16 +2336,23 @@ public:
                                 const nsPoint &aOrigin,
                                 nsRect* aOutRect);
   
   static bool UntransformRectMatrix(const nsRect &aUntransformedBounds, 
                                     const gfx3DMatrix& aMatrix,
                                     float aAppUnitsPerPixel,
                                     nsRect* aOutRect);
 
+  static gfxPoint3D GetDeltaToMozTransformOrigin(const nsIFrame* aFrame,
+                                                 float aAppUnitsPerPixel,
+                                                 const nsRect* aBoundsOverride);
+
+  static gfxPoint3D GetDeltaToMozPerspectiveOrigin(const nsIFrame* aFrame,
+                                                   float aAppUnitsPerPixel);
+
   /**
    * Returns the bounds of a frame as defined for resolving percentage
    * <translation-value>s in CSS transforms.  If
    * UNIFIED_CONTINUATIONS is not defined, this is simply the frame's bounding
    * rectangle, translated to the origin.  Otherwise, returns the smallest
    * rectangle containing a frame and all of its continuations.  For example,
    * if there is a <span> element with several continuations split over
    * several lines, this function will return the rectangle containing all of
@@ -2369,16 +2376,20 @@ public:
    *        for the frame's bounding rectangle. Otherwise, it will use the
    *        value of aBoundsOverride.  This is mostly for internal use and in
    *        most cases you will not need to specify a value.
    */
   static gfx3DMatrix GetResultingTransformMatrix(const nsIFrame* aFrame,
                                                  const nsPoint& aOrigin,
                                                  float aAppUnitsPerPixel,
                                                  const nsRect* aBoundsOverride = nsnull,
+                                                 const nsCSSValueList* aTransformOverride = nsnull,
+                                                 gfxPoint3D* aToMozOrigin = nsnull,
+                                                 gfxPoint3D* aToPerspectiveOrigin = nsnull,
+                                                 nscoord* aChildPerspective = nsnull, 
                                                  nsIFrame** aOutAncestor = nsnull);
   /**
    * Return true when we should try to prerender the entire contents of the
    * transformed frame even when it's not completely visible (yet).
    */
   static bool ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder,
                                                 nsIFrame* aFrame);
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -87,16 +87,17 @@
 #include "nsDeckFrame.h"
 #include "nsTableFrame.h"
 
 #include "gfxContext.h"
 #include "nsRenderingContext.h"
 #include "CSSCalc.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "nsFontInflationData.h"
+#include "nsAnimationManager.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 
@@ -930,19 +931,28 @@ nsRect
 nsIFrame::GetPaddingRect() const
 {
   return GetPaddingRectRelativeToSelf() + GetPosition();
 }
 
 bool
 nsIFrame::IsTransformed() const
 {
-  return (mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
+  return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
           (GetStyleDisplay()->HasTransform() ||
-           IsSVGTransformed());
+           IsSVGTransformed() ||
+           (mContent &&
+            nsAnimationManager::GetAnimationsForCompositor(mContent, eCSSProperty_transform))));
+}
+
+bool
+nsIFrame::HasOpacity() const
+{
+  return GetStyleDisplay()->mOpacity < 1.0f || (mContent &&
+           nsAnimationManager::GetAnimationsForCompositor(mContent, eCSSProperty_opacity));
 }
 
 bool
 nsIFrame::IsSVGTransformed(gfxMatrix *aOwnTransforms,
                            gfxMatrix *aFromParentTransforms) const
 {
   return false;
 }
@@ -1931,17 +1941,17 @@ nsIFrame::BuildDisplayListForStackingCon
     rv = resultList.AppendNewToTop(
         new (aBuilder) nsDisplaySVGEffects(aBuilder, this, &resultList));
     if (NS_FAILED(rv))
       return rv;
   }
   /* Else, if the list is non-empty and there is CSS group opacity without SVG
    * effects, wrap it up in an opacity item.
    */
-  else if (disp->mOpacity < 1.0f &&
+  else if (HasOpacity() &&
            !nsSVGUtils::CanOptimizeOpacity(this) &&
            !resultList.IsEmpty()) {
     rv = resultList.AppendNewToTop(
         new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList));
     if (NS_FAILED(rv))
       return rv;
   }
 
@@ -2085,17 +2095,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
   // Don't paint our children if the theme object is a leaf.
   if (IsThemed(ourDisp) &&
       !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance))
     return NS_OK;
 
   // Child is composited if it's transformed, partially transparent, or has
   // SVG effects.
   const nsStyleDisplay* disp = child->GetStyleDisplay();
-  bool isVisuallyAtomic = disp->mOpacity != 1.0f
+  bool isVisuallyAtomic = child->HasOpacity()
     || child->IsTransformed()
     || nsSVGIntegrationUtils::UsingEffectsForFrame(child);
 
   bool isPositioned = !isSVG && disp->IsPositioned();
   if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating()) ||
       ((disp->mClipFlags & NS_STYLE_CLIP_RECT) &&
        IsSVGContentWithCSSClip(this)) ||
       (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
@@ -4753,18 +4763,18 @@ nsIFrame::GetTransformMatrix(nsIFrame* a
     /* Compute the delta to the parent, which we need because we are converting
      * coordinates to our parent.
      */
     NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this),
 	             "Cannot transform the viewport frame!");
     PRInt32 scaleFactor = PresContext()->AppUnitsPerDevPixel();
 
     gfx3DMatrix result =
-      nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0),
-                                                      scaleFactor, nsnull, aOutAncestor);
+      nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0), scaleFactor, nsnull, 
+                                                      nsnull, nsnull, nsnull, nsnull, aOutAncestor);
     // XXXjwatt: seems like this will double count offsets in the face of preserve-3d:
     nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
     /* Combine the raw transform with a translation to our parent. */
     result *= gfx3DMatrix::Translation
       (NSAppUnitsToFloatPixels(delta.x, scaleFactor),
        NSAppUnitsToFloatPixels(delta.y, scaleFactor),
        0.0f);
     return result;
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1222,16 +1222,18 @@ public:
   virtual bool NeedsView() { return false; }
 
   /**
    * Returns true if this frame is transformed (e.g. has CSS or SVG transforms)
    * or if its parent is an SVG frame that has children-only transforms (e.g.
    * an SVG viewBox attribute).
    */
   bool IsTransformed() const;
+  
+  bool HasOpacity() const;
 
   /**
    * Returns true if this frame is an SVG frame that has SVG transforms applied
    * to it, or if its parent frame is an SVG frame that has children-only
    * transforms (e.g. an SVG viewBox attribute).
    * If aOwnTransforms is non-null and the frame has its own SVG transforms,
    * aOwnTransforms will be set to these transforms. If aFromParentTransforms
    * is non-null and the frame has an SVG parent with children-only transforms,
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -241,16 +241,17 @@ TransformShadowTree(nsDisplayListBuilder
                     nsIFrame* aFrame, Layer* aLayer,
                     const ViewTransform& aTransform,
                     float aTempScaleDiffX = 1.0,
                     float aTempScaleDiffY = 1.0)
 {
   ShadowLayer* shadow = aLayer->AsShadowLayer();
   shadow->SetShadowClipRect(aLayer->GetClipRect());
   shadow->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
+  shadow->SetShadowOpacity(aLayer->GetOpacity());
 
   const FrameMetrics* metrics = GetFrameMetrics(aLayer);
 
   gfx3DMatrix shadowTransform = aLayer->GetTransform();
   ViewTransform layerTransform = aTransform;
 
   if (metrics && metrics->IsScrollable()) {
     const ViewID scrollId = metrics->mScrollId;
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -2,16 +2,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AnimationCommon.h"
 #include "nsRuleData.h"
 #include "nsCSSValue.h"
 #include "nsStyleContext.h"
+#include "nsIFrame.h"
+#include "nsAnimationManager.h"
 
 namespace mozilla {
 namespace css {
 
 CommonAnimationManager::CommonAnimationManager(nsPresContext *aPresContext)
   : mPresContext(aPresContext)
 {
   PR_INIT_CLIST(&mElementData);
@@ -34,17 +36,17 @@ CommonAnimationManager::Disconnect()
 void
 CommonAnimationManager::AddElementData(CommonElementAnimationData* aData)
 {
   if (PR_CLIST_IS_EMPTY(&mElementData)) {
     // We need to observe the refresh driver.
     nsRefreshDriver *rd = mPresContext->RefreshDriver();
     rd->AddRefreshObserver(this, Flush_Style);
   }
-
+    
   PR_INSERT_BEFORE(aData, &mElementData);
 }
 
 void
 CommonAnimationManager::ElementDataRemoved()
 {
   // If we have no transitions or animations left, remove ourselves from
   // the refresh driver.
@@ -208,10 +210,31 @@ ComputedTimingFunction::GetValue(double 
     default:
       NS_ABORT_IF_FALSE(false, "bad type");
       // fall through
     case nsTimingFunction::StepEnd:
       return StepEnd(mSteps, aPortion);
   }
 }
 
+bool
+CommonElementAnimationData::CanAnimatePropertyOnCompositor(const dom::Element *aElement,
+                                                           nsCSSProperty aProperty)
+{
+  nsIFrame* frame = aElement->GetPrimaryFrame();
+  if (aProperty == eCSSProperty_opacity) {
+    return nsAnimationManager::CanAnimateOpacity();
+  }
+  if (aProperty == eCSSProperty_transform && !(frame &&
+      frame->Preserves3D() &&
+      frame->Preserves3DChildren())) {
+    if (frame && frame->IsSVGTransformed()) {
+      return false;
+    }
+    return nsAnimationManager::CanAnimateTransform();
+  }
+  return false;
+}
+
+
+
 }
 }
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -102,16 +102,22 @@ private:
   InfallibleTArray<PropertyValuePair> mPropertyValuePairs;
 };
 
 class ComputedTimingFunction {
 public:
   typedef nsTimingFunction::Type Type;
   void Init(const nsTimingFunction &aFunction);
   double GetValue(double aPortion) const;
+  const nsSMILKeySpline* GetFunction() const {
+    NS_ASSERTION(mType == nsTimingFunction::Function, "Type mismatch");
+    return &mTimingFunction;
+  }
+  Type GetType() const { return mType; }
+  PRUint32 GetSteps() const { return mSteps; }
 private:
   Type mType;
   nsSMILKeySpline mTimingFunction;
   PRUint32 mSteps;
 };
 
 struct CommonElementAnimationData : public PRCList
 {
@@ -137,16 +143,20 @@ struct CommonElementAnimationData : publ
   }
 
   void Destroy()
   {
     // This will call our destructor.
     mElement->DeleteProperty(mElementProperty);
   }
 
+  static bool
+  CanAnimatePropertyOnCompositor(const dom::Element *aElement,
+                                 nsCSSProperty aProperty);
+
   dom::Element *mElement;
 
   // the atom we use in mElement's prop table (must be a static atom,
   // i.e., in an atom list)
   nsIAtom *mElementProperty;
 
   CommonAnimationManager *mManager;
 
--- a/layout/style/Makefile.in
+++ b/layout/style/Makefile.in
@@ -17,16 +17,18 @@ endif
 
 MODULE		= layout
 LIBRARY_NAME	= gkstyle_s
 LIBXUL_LIBRARY	= 1
 
 EXPORTS_NAMESPACES = mozilla/css
 
 EXPORTS		= \
+		AnimationCommon.h \
+		nsAnimationManager.h \
 		nsCSSAnonBoxList.h \
 		nsCSSAnonBoxes.h \
 		nsCSSFontDescList.h \
 		nsCSSKeywordList.h \
 		nsCSSKeywords.h \
 		nsCSSParser.h \
 		nsCSSPropAliasList.h \
 		nsCSSPropList.h \
@@ -50,16 +52,17 @@ EXPORTS		= \
 		nsIStyleRule.h \
 		nsIStyleRuleProcessor.h \
 		nsIStyleSheet.h \
 		nsLayoutStylesheetCache.h \
 		nsRuleData.h \
 		nsRuleNode.h \
 		nsRuleProcessorData.h \
 		nsRuleWalker.h \
+		nsStyleAnimation.h \
 		nsStyleContext.h \
 		nsStyleCoord.h \
 		nsStyleSet.h \
 		nsStyleStruct.h \
 		nsStyleStructFwd.h \
 		nsStyleStructInlines.h \
 		nsStyleStructList.h \
 		nsStyleTransformMatrix.h \
@@ -119,17 +122,19 @@ CPPSRCS		= \
 		nsTransitionManager.cpp \
 		StyleRule.cpp \
 		$(NULL)
 
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
-LOCAL_INCLUDES	= \
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+
+LOCAL_INCLUDES	+= \
 		-I$(srcdir)/../base \
 		-I$(srcdir)/../generic \
 		-I$(srcdir)/../xul/base/src \
 		-I$(srcdir)/../../content/base/src \
 		-I$(srcdir)/../../content/html/content/src \
 		-I$(srcdir)/../../content/xbl/src \
 		-I$(srcdir)/../../content/xul/document/src \
 		$(NULL)
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -6,16 +6,18 @@
 #include "nsAnimationManager.h"
 #include "nsPresContext.h"
 #include "nsRuleProcessorData.h"
 #include "nsStyleSet.h"
 #include "nsCSSRules.h"
 #include "nsStyleAnimation.h"
 #include "nsSMILKeySpline.h"
 #include "nsEventDispatcher.h"
+#include "nsDisplayList.h"
+#include "nsCSSFrameConstructor.h"
 
 using namespace mozilla;
 
 ElementAnimations::ElementAnimations(mozilla::dom::Element *aElement, nsIAtom *aElementProperty,
                                      nsAnimationManager *aAnimationManager)
   : CommonElementAnimationData(aElement, aElementProperty,
                                aAnimationManager),
     mNeedsRefreshes(true)
@@ -31,16 +33,124 @@ ElementAnimationsPropertyDtor(void      
   ElementAnimations *ea = static_cast<ElementAnimations*>(aPropertyValue);
 #ifdef DEBUG
   NS_ABORT_IF_FALSE(!ea->mCalledPropertyDtor, "can't call dtor twice");
   ea->mCalledPropertyDtor = true;
 #endif
   delete ea;
 }
 
+double
+ElementAnimations::GetPositionInIteration(TimeStamp aStartTime, TimeStamp aCurrentTime,
+                                          TimeDuration aDuration, double aIterationCount,
+                                          PRUint32 aDirection, bool aIsForElement,
+                                          ElementAnimation* aAnimation,
+                                          ElementAnimations* aEa,
+                                          EventArray* aEventsToDispatch)
+{
+  // Set |currentIterationCount| to the (fractional) number of
+  // iterations we've completed up to the current position.
+  TimeDuration currentTimeDuration = aCurrentTime - aStartTime;
+  double currentIterationCount =
+    currentTimeDuration / aDuration;
+  bool dispatchStartOrIteration = false;
+  if (currentIterationCount >= aIterationCount) {
+    if (!aAnimation) {
+      // We are on the compositor, so send a signal that the animation is over.
+      // The main thread will fire the animationend event.
+      return -1;
+    }
+    // Dispatch 'animationend' when needed.
+    if (aIsForElement &&
+        aAnimation->mLastNotification !=
+          ElementAnimation::LAST_NOTIFICATION_END) {
+      aAnimation->mLastNotification = ElementAnimation::LAST_NOTIFICATION_END;
+      // XXXdz: if this animation was done on the compositor, we should
+      // invalidate the frame and update style once we start throttling style
+      // updates.
+      AnimationEventInfo ei(aEa->mElement, aAnimation->mName, NS_ANIMATION_END,
+                            currentTimeDuration);
+      aEventsToDispatch->AppendElement(ei);
+    }
+
+    if (!aAnimation->FillsForwards()) {
+      // No animation data.
+      return -1;
+    }
+    currentIterationCount = double(aAnimation->mIterationCount);
+  } else {
+    if (aAnimation && !aAnimation->IsPaused()) {
+      aEa->mNeedsRefreshes = true;
+    }
+    if (currentIterationCount < 0.0) {
+      NS_ASSERTION(aAnimation, "Should not run animation that hasn't started yet on the compositor");
+      if (!aAnimation->FillsBackwards()) {
+        // No animation data.
+        return -1;
+      }
+      currentIterationCount = 0.0;
+    } else {
+      dispatchStartOrIteration = aAnimation && !aAnimation->IsPaused();
+    }
+  }
+
+  // Set |positionInIteration| to the position from 0% to 100% along
+  // the keyframes.
+  NS_ABORT_IF_FALSE(currentIterationCount >= 0.0, "must be positive");
+  PRUint32 whichIteration = int(currentIterationCount);
+  if (whichIteration == aIterationCount && whichIteration != 0) {
+    // When the animation's iteration count is an integer (as it
+    // normally is), we need to end at 100% of its last iteration
+    // rather than 0% of the next one (unless it's zero).
+    --whichIteration;
+  }
+  double positionInIteration =
+    currentIterationCount - double(whichIteration);
+
+  bool thisIterationReverse = false;
+  switch (aDirection) {
+    case NS_STYLE_ANIMATION_DIRECTION_NORMAL:
+      thisIterationReverse = false;
+      break;
+    case NS_STYLE_ANIMATION_DIRECTION_REVERSE:
+      thisIterationReverse = true;
+      break;
+    case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE:
+      thisIterationReverse = (whichIteration & 1) == 1;
+      break;
+    case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE_REVERSE:
+      thisIterationReverse = (whichIteration & 1) == 0;
+      break;
+  }
+  if (thisIterationReverse) {
+    positionInIteration = 1.0 - positionInIteration;
+  }
+
+  // Dispatch 'animationstart' or 'animationiteration' when needed.
+  if (aAnimation && aIsForElement && dispatchStartOrIteration &&
+      whichIteration != aAnimation->mLastNotification) {
+    // Notify 'animationstart' even if a negative delay puts us
+    // past the first iteration.
+    // Note that when somebody changes the animation-duration
+    // dynamically, this will fire an extra iteration event
+    // immediately in many cases.  It's not clear to me if that's the
+    // right thing to do.
+    PRUint32 message =
+      aAnimation->mLastNotification == ElementAnimation::LAST_NOTIFICATION_NONE
+        ? NS_ANIMATION_START : NS_ANIMATION_ITERATION;
+    // XXXdz: If this is a start, invalidate the frame here once we throttle animations.
+    aAnimation->mLastNotification = whichIteration;
+    AnimationEventInfo ei(aEa->mElement, aAnimation->mName, message,
+                          currentTimeDuration);
+    aEventsToDispatch->AppendElement(ei);
+  }
+
+  return positionInIteration;
+}
+
 void
 ElementAnimations::EnsureStyleRuleFor(TimeStamp aRefreshTime,
                                       EventArray& aEventsToDispatch)
 {
   if (!mNeedsRefreshes) {
     // All of our animations are paused or completed.
     mStyleRuleRefreshTime = aRefreshTime;
     return;
@@ -63,108 +173,34 @@ ElementAnimations::EnsureStyleRuleFor(Ti
       ElementAnimation &anim = mAnimations[animIdx];
 
       if (anim.mProperties.Length() == 0 ||
           anim.mIterationDuration.ToMilliseconds() <= 0.0) {
         // No animation data.
         continue;
       }
 
-      TimeDuration currentTimeDuration;
+      TimeStamp currentTime;
       if (anim.IsPaused()) {
         // FIXME: avoid recalculating every time
-        currentTimeDuration = anim.mPauseStart - anim.mStartTime;
+        currentTime = anim.mPauseStart;
       } else {
-        currentTimeDuration = aRefreshTime - anim.mStartTime;
-      }
-
-      // Set |currentIterationCount| to the (fractional) number of
-      // iterations we've completed up to the current position.
-      double currentIterationCount =
-        currentTimeDuration / anim.mIterationDuration;
-      bool dispatchStartOrIteration = false;
-      if (currentIterationCount >= double(anim.mIterationCount)) {
-        // Dispatch 'animationend' when needed.
-        if (IsForElement() && 
-            anim.mLastNotification !=
-              ElementAnimation::LAST_NOTIFICATION_END) {
-          anim.mLastNotification = ElementAnimation::LAST_NOTIFICATION_END;
-          AnimationEventInfo ei(mElement, anim.mName, NS_ANIMATION_END,
-                                currentTimeDuration);
-          aEventsToDispatch.AppendElement(ei);
-        }
-
-        if (!anim.FillsForwards()) {
-          // No animation data.
-          continue;
-        }
-        currentIterationCount = double(anim.mIterationCount);
-      } else {
-        if (!anim.IsPaused()) {
-          mNeedsRefreshes = true;
-        }
-        if (currentIterationCount < 0.0) {
-          if (!anim.FillsBackwards()) {
-            // No animation data.
-            continue;
-          }
-          currentIterationCount = 0.0;
-        } else {
-          dispatchStartOrIteration = !anim.IsPaused();
-        }
+        currentTime = aRefreshTime;
       }
 
-      // Set |positionInIteration| to the position from 0% to 100% along
-      // the keyframes.
-      NS_ABORT_IF_FALSE(currentIterationCount >= 0.0, "must be positive");
-      PRUint32 whichIteration = int(currentIterationCount);
-      if (whichIteration == anim.mIterationCount && whichIteration != 0) {
-        // When the animation's iteration count is an integer (as it
-        // normally is), we need to end at 100% of its last iteration
-        // rather than 0% of the next one (unless it's zero).
-        --whichIteration;
-      }
       double positionInIteration =
-        currentIterationCount - double(whichIteration);
-      bool thisIterationReverse = false;
-      switch (anim.mDirection) {
-        case NS_STYLE_ANIMATION_DIRECTION_NORMAL:
-          thisIterationReverse = false;
-          break;
-        case NS_STYLE_ANIMATION_DIRECTION_REVERSE:
-          thisIterationReverse = true;
-          break;
-        case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE:
-          thisIterationReverse = (whichIteration & 1) == 1;
-          break;
-        case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE_REVERSE:
-          thisIterationReverse = (whichIteration & 1) == 0;
-          break;
-      }
-      if (thisIterationReverse) {
-        positionInIteration = 1.0 - positionInIteration;
-      }
+        GetPositionInIteration(anim.mStartTime, currentTime,
+                               anim.mIterationDuration, anim.mIterationCount,
+                               anim.mDirection, IsForElement(),
+                               &anim, this, &aEventsToDispatch);
 
-      // Dispatch 'animationstart' or 'animationiteration' when needed.
-      if (IsForElement() && dispatchStartOrIteration &&
-          whichIteration != anim.mLastNotification) {
-        // Notify 'animationstart' even if a negative delay puts us
-        // past the first iteration.
-        // Note that when somebody changes the animation-duration
-        // dynamically, this will fire an extra iteration event
-        // immediately in many cases.  It's not clear to me if that's the
-        // right thing to do.
-        PRUint32 message =
-          anim.mLastNotification == ElementAnimation::LAST_NOTIFICATION_NONE
-            ? NS_ANIMATION_START : NS_ANIMATION_ITERATION;
-        anim.mLastNotification = whichIteration;
-        AnimationEventInfo ei(mElement, anim.mName, message,
-                              currentTimeDuration);
-        aEventsToDispatch.AppendElement(ei);
-      }
+      // The position is -1 when we don't have fill data for the current time,
+      // so we shouldn't animate.
+      if (positionInIteration == -1)
+        continue;
 
       NS_ABORT_IF_FALSE(0.0 <= positionInIteration &&
                           positionInIteration <= 1.0,
                         "position should be in [0-1]");
 
       for (PRUint32 propIdx = 0, propEnd = anim.mProperties.Length();
            propIdx != propEnd; ++propIdx)
       {
@@ -221,16 +257,76 @@ ElementAnimations::EnsureStyleRuleFor(Ti
                                         segment->mFromValue, segment->mToValue,
                                         valuePosition, *val);
         NS_ABORT_IF_FALSE(result, "interpolate must succeed now");
       }
     }
   }
 }
 
+static bool
+CanPerformAnimationOnCompositor(const ElementAnimation* aAnim,
+                                mozilla::dom::Element* aElement)
+{
+  for (PRUint32 propIdx = 0, propEnd = aAnim->mProperties.Length();
+       propIdx != propEnd; ++propIdx) {
+    const AnimationProperty &prop = aAnim->mProperties[propIdx];
+    if (!mozilla::css::CommonElementAnimationData::
+           CanAnimatePropertyOnCompositor(aElement,
+                                          prop.mProperty)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool
+ElementAnimation::CanPerformOnCompositor(mozilla::dom::Element* aElement,
+                                         TimeStamp aTime) const
+{
+  return CanPerformAnimationOnCompositor(this, aElement) &&
+    !IsPaused() && aTime > mStartTime &&
+    (aTime - mStartTime)  / mIterationDuration < mIterationCount;
+}
+
+bool
+ElementAnimations::HasAnimationOfProperty(nsCSSProperty aProperty) const
+{
+  for (PRUint32 animIdx = mAnimations.Length(); animIdx-- != 0; ) {
+    const ElementAnimation &anim = mAnimations[animIdx];
+    for (PRUint32 propIdx = 0, propEnd = anim.mProperties.Length();
+         propIdx != propEnd; ++propIdx) {
+      const AnimationProperty &prop = anim.mProperties[propIdx];
+      if (aProperty == prop.mProperty) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool
+ElementAnimations::CanPerformOnCompositorThread() const
+{
+  if (mElementProperty != nsGkAtoms::animationsProperty)
+    return false;
+  for (PRUint32 animIdx = mAnimations.Length(); animIdx-- != 0; ) {
+    const ElementAnimation &anim = mAnimations[animIdx];
+    if (anim.mIterationDuration.ToMilliseconds() <= 0.0) {
+      // No animation data
+      continue;
+    }
+
+    if (!CanPerformAnimationOnCompositor(&anim, mElement))
+      return false;
+ }
+
+  return true;
+}
+
 ElementAnimations*
 nsAnimationManager::GetElementAnimations(dom::Element *aElement,
                                          nsCSSPseudoElements::Type aPseudoType,
                                          bool aCreateIfNeeded)
 {
   if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementData)) {
     // Early return for the most common case.
     return nsnull;
@@ -249,27 +345,26 @@ nsAnimationManager::GetElementAnimations
                  "other than :before or :after");
     return nsnull;
   }
   ElementAnimations *ea = static_cast<ElementAnimations*>(
                              aElement->GetProperty(propName));
   if (!ea && aCreateIfNeeded) {
     // FIXME: Consider arena-allocating?
     ea = new ElementAnimations(aElement, propName, this);
-    if (!ea) {
-      NS_WARNING("out of memory");
-      return nsnull;
-    }
     nsresult rv = aElement->SetProperty(propName, ea,
                                         ElementAnimationsPropertyDtor, nsnull);
     if (NS_FAILED(rv)) {
       NS_WARNING("SetProperty failed");
       delete ea;
       return nsnull;
     }
+    if (propName == nsGkAtoms::animationsProperty) {
+      aElement->SetMayHaveAnimations();
+    }
 
     AddElementData(ea);
   }
 
   return ea;
 }
 
 /* virtual */ void
@@ -361,16 +456,18 @@ nsAnimationManager::CheckAnimationRule(n
         ea->Destroy();
       }
       return nsnull;
     }
 
     TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh();
 
     if (ea) {
+
+      // XXXdz: Invalidate the frame since the animation changed.
       // The cached style rule is invalid.
       ea->mStyleRule = nsnull;
       ea->mStyleRuleRefreshTime = TimeStamp();
 
       // Copy over the start times and (if still paused) pause starts
       // for each animation (matching on name only) that was also in the
       // old list of animations.
       // This means that we honor dynamic changes, which isn't what the
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -6,16 +6,17 @@
 #define nsAnimationManager_h_
 
 #include "AnimationCommon.h"
 #include "nsCSSPseudoElements.h"
 #include "nsStyleContext.h"
 #include "nsDataHashtable.h"
 #include "nsGUIEvent.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/Preferences.h"
 #include "nsThreadUtils.h"
 
 class nsCSSKeyframesRule;
 
 namespace mozilla {
 namespace css {
 class Declaration;
 }
@@ -83,16 +84,19 @@ struct ElementAnimation
     return mFillMode == NS_STYLE_ANIMATION_FILL_MODE_BOTH ||
            mFillMode == NS_STYLE_ANIMATION_FILL_MODE_BACKWARDS;
   }
 
   bool IsPaused() const {
     return mPlayState == NS_STYLE_ANIMATION_PLAY_STATE_PAUSED;
   }
 
+  bool CanPerformOnCompositor(mozilla::dom::Element* aElement,
+                              mozilla::TimeStamp aTime) const;
+
   mozilla::TimeStamp mStartTime; // with delay taken into account
   mozilla::TimeStamp mPauseStart;
   mozilla::TimeDuration mIterationDuration;
 
   enum {
     LAST_NOTIFICATION_NONE = PRUint32(-1),
     LAST_NOTIFICATION_END = PRUint32(-2)
   };
@@ -109,28 +113,51 @@ struct ElementAnimation
 struct ElementAnimations : public mozilla::css::CommonElementAnimationData
 {
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::TimeDuration TimeDuration;
 
   ElementAnimations(mozilla::dom::Element *aElement, nsIAtom *aElementProperty,
                     nsAnimationManager *aAnimationManager);
 
+  // This function takes as input the start time, duration, and direction of an
+  // animation and returns the position in the current iteration.  Note that
+  // this only works when we know that the animation is currently running.
+  // This way of calling the function can be used from the compositor.  Note
+  // that if the animation has not started yet, has already ended, or is paused,
+  // it should not be run from the compositor.  When this function is called 
+  // from the main thread, we need the actual ElementAnimation* in order to 
+  // get correct animation-fill behavior and to fire animation events.
+  // This function returns -1 for the position if the animation should not be
+  // run (because it is not currently active and has no fill behavior.)
+  static double GetPositionInIteration(TimeStamp aStartTime,
+                                       TimeStamp aCurrentTime,
+                                       TimeDuration aDuration,
+                                       double aIterationCount,
+                                       PRUint32 aDirection,
+                                       bool IsForElement = true,
+                                       ElementAnimation* aAnimation = nsnull,
+                                       ElementAnimations* aEa = nsnull,
+                                       EventArray* aEventsToDispatch = nsnull);
+
   void EnsureStyleRuleFor(TimeStamp aRefreshTime,
                           EventArray &aEventsToDispatch);
 
   bool IsForElement() const { // rather than for a pseudo-element
     return mElementProperty == nsGkAtoms::animationsProperty;
   }
 
   void PostRestyleForAnimation(nsPresContext *aPresContext) {
     nsRestyleHint styleHint = IsForElement() ? eRestyle_Self : eRestyle_Subtree;
     aPresContext->PresShell()->RestyleForAnimation(mElement, styleHint);
   }
 
+  // True if this animation can be performed on the compositor thread.
+  bool CanPerformOnCompositorThread() const;
+  bool HasAnimationOfProperty(nsCSSProperty aProperty) const;
   // This style rule contains the style data for currently animating
   // values.  It only matches when styling with animation.  When we
   // style without animation, we need to not use it so that we can
   // detect any new changes; if necessary we restyle immediately
   // afterwards with animation.
   // NOTE: If we don't need to apply any styles, mStyleRule will be
   // null, but mStyleRuleRefreshTime will still be valid.
   nsRefPtr<mozilla::css::AnimValuesStyleRule> mStyleRule;
@@ -144,24 +171,49 @@ struct ElementAnimations : public mozill
 
   InfallibleTArray<ElementAnimation> mAnimations;
 };
 
 class nsAnimationManager : public mozilla::css::CommonAnimationManager
 {
 public:
   nsAnimationManager(nsPresContext *aPresContext)
-    : mozilla::css::CommonAnimationManager(aPresContext),
-      mKeyframesListIsDirty(true)
+    : mozilla::css::CommonAnimationManager(aPresContext)
+    , mKeyframesListIsDirty(true)
   {
     mKeyframesRules.Init(16); // FIXME: make infallible!
   }
 
+  static bool CanAnimateOpacity() {
+    static bool canAnimateOpacity =
+      mozilla::Preferences::GetBool("layers.offmainthreadcomposition.animate-opacity", false) &&
+      mozilla::Preferences::GetBool("layers.offmainthreadcomposition.enabled", false);
+    return canAnimateOpacity;
+  }
 
+  static bool CanAnimateTransform() {
+    static bool canAnimateTransform =
+      mozilla::Preferences::GetBool("layers.offmainthreadcomposition.animate-transform", false) &&
+      mozilla::Preferences::GetBool("layers.offmainthreadcomposition.enabled", false);
+    return canAnimateTransform;
+  }
 
+  static ElementAnimations* GetAnimationsForCompositor(nsIContent* aContent,
+                                                       nsCSSProperty aProperty)
+  {
+    if (!aContent->MayHaveAnimations())
+      return nsnull;
+    ElementAnimations* animations = static_cast<ElementAnimations*>(
+      aContent->GetProperty(nsGkAtoms::animationsProperty));
+    if (!animations)
+      return nsnull;
+    bool propertyMatches = animations->HasAnimationOfProperty(aProperty);
+    return (propertyMatches && animations->CanPerformOnCompositorThread()) ?
+      animations : nsnull;
+  }
 
   // nsIStyleRuleProcessor (parts)
   virtual void RulesMatching(ElementRuleProcessorData* aData);
   virtual void RulesMatching(PseudoElementRuleProcessorData* aData);
   virtual void RulesMatching(AnonBoxRuleProcessorData* aData);
 #ifdef MOZ_XUL
   virtual void RulesMatching(XULTreeRuleProcessorData* aData);
 #endif
--- a/layout/style/nsStyleAnimation.cpp
+++ b/layout/style/nsStyleAnimation.cpp
@@ -1117,19 +1117,19 @@ AddTransformScale(const nsCSSValue &aVal
   NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit");
 
   float v1 = aValue1.GetFloatValue() - 1.0f,
         v2 = aValue2.GetFloatValue() - 1.0f;
   float result = v1 * aCoeff1 + v2 * aCoeff2;
   aResult.SetFloatValue(result + 1.0f, eCSSUnit_Number);
 }
 
-static already_AddRefed<nsCSSValue::Array>
-AppendTransformFunction(nsCSSKeyword aTransformFunction,
-                        nsCSSValueList**& aListTail)
+/* static */ already_AddRefed<nsCSSValue::Array>
+nsStyleAnimation::AppendTransformFunction(nsCSSKeyword aTransformFunction,
+                                          nsCSSValueList**& aListTail)
 {
   nsRefPtr<nsCSSValue::Array> arr = AppendFunction(aTransformFunction);
   nsCSSValueList *item = new nsCSSValueList;
   item->mValue.SetArrayValue(arr, eCSSUnit_Function);
 
   *aListTail = item;
   aListTail = &item->mNext;
 
@@ -1279,17 +1279,17 @@ Decompose3DMatrix(const gfx3DMatrix &aMa
   gfx3DMatrix local = aMatrix;
 
   if (local[3][3] == 0) {
     return false;
   }
   /* Normalize the matrix */
   local.Normalize();
 
-  /** 
+  /**
    * perspective is used to solve for perspective, but it also provides
    * an easy way to test for singularity of the upper 3x3 component.
    */
   gfx3DMatrix perspective = local;
   gfxPointH3D empty(0, 0, 0, 1);
   perspective.SetTransposedVector(3, empty);
 
   if (perspective.Determinant() == 0.0) {
@@ -1297,23 +1297,23 @@ Decompose3DMatrix(const gfx3DMatrix &aMa
   }
 
   /* First, isolate perspective. */
   if (local[0][3] != 0 || local[1][3] != 0 ||
       local[2][3] != 0) {
     /* aPerspective is the right hand side of the equation. */
     aPerspective = local.TransposedVector(3);
 
-    /** 
+    /**
      * Solve the equation by inverting perspective and multiplying
      * aPerspective by the inverse.
      */
     perspective.Invert();
     aPerspective = perspective.TransposeTransform4D(aPerspective);
-    
+
     /* Clear the perspective partition */
     local.SetTransposedVector(3, empty);
   } else {
     aPerspective = gfxPointH3D(0, 0, 0, 1);
   }
 
   /* Next take care of translation */
   for (int i = 0; i < 3; i++) {
@@ -1321,21 +1321,21 @@ Decompose3DMatrix(const gfx3DMatrix &aMa
     local[3][i] = 0;
   }
 
   /* Now get scale and shear. */
 
   /* Compute X scale factor and normalize first row. */
   aScale.x = local[0].Length();
   local[0] /= aScale.x;
-    
+
   /* Compute XY shear factor and make 2nd local orthogonal to 1st. */
   aShear[XYSHEAR] = local[0].DotProduct(local[1]);
   local[1] -= local[0] * aShear[XYSHEAR];
-  
+
   /* Now, compute Y scale and normalize 2nd local. */
   aScale.y = local[1].Length();
   local[1] /= aScale.y;
   aShear[XYSHEAR] /= aScale.y;
 
   /* Compute XZ and YZ shears, make 3rd local orthogonal */
   aShear[XZSHEAR] = local[0].DotProduct(local[2]);
   local[2] -= local[0] * aShear[XZSHEAR];
@@ -1402,21 +1402,21 @@ nsStyleAnimation::InterpolateTransformMa
                       rotate1, translate1, perspective1);
     Decompose3DMatrix(aMatrix2, scale2, shear2,
                       rotate2, translate2, perspective2);
   }
 
   // Interpolate each of the pieces
   gfx3DMatrix result;
 
-  gfxPointH3D perspective = 
+  gfxPointH3D perspective =
     InterpolateNumerically(perspective1, perspective2, aProgress);
   result.SetTransposedVector(3, perspective);
- 
-  gfxPoint3D translate = 
+
+  gfxPoint3D translate =
     InterpolateNumerically(translate1, translate2, aProgress);
   result.Translate(translate);
 
   gfxQuaternion q3 = rotate1.Slerp(rotate2, aProgress);
   gfx3DMatrix rotate = q3.ToMatrix();
   if (!rotate.IsIdentity()) {
       result = rotate * result;
   }
@@ -1435,35 +1435,35 @@ nsStyleAnimation::InterpolateTransformMa
   }
 
   float xyshear =
     InterpolateNumerically(shear1[XYSHEAR], shear2[XYSHEAR], aProgress);
   if (xyshear != 0.0) {
     result.SkewXY(xyshear);
   }
 
-  gfxPoint3D scale = 
+  gfxPoint3D scale =
     InterpolateNumerically(scale1, scale2, aProgress);
   if (scale != gfxPoint3D(1.0, 1.0, 1.0)) {
     result.Scale(scale.x, scale.y, scale.z);
   }
 
   return result;
 }
 
 static nsCSSValueList*
 AddDifferentTransformLists(const nsCSSValueList* aList1, double aCoeff1,
                            const nsCSSValueList* aList2, double aCoeff2)
 {
   nsAutoPtr<nsCSSValueList> result;
   nsCSSValueList **resultTail = getter_Transfers(result);
 
   nsRefPtr<nsCSSValue::Array> arr;
-  arr = AppendTransformFunction(eCSSKeyword_interpolatematrix, resultTail);
-  
+  arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_interpolatematrix, resultTail);
+
   // FIXME: We should change the other transform code to also only
   // take a single progress value, as having values that don't
   // sum to 1 doesn't make sense for these.
   if (aList1 == aList2) {
     arr->Item(1).Reset();
   } else {
     aList1->CloneInto(arr->Item(1).SetListValue());
   }
@@ -1498,17 +1498,17 @@ AddTransformLists(const nsCSSValueList* 
 
     nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(a1);
     nsRefPtr<nsCSSValue::Array> arr;
     if (tfunc != eCSSKeyword_matrix &&
         tfunc != eCSSKeyword_matrix3d &&
         tfunc != eCSSKeyword_interpolatematrix &&
         tfunc != eCSSKeyword_rotate3d &&
         tfunc != eCSSKeyword_perspective) {
-      arr = AppendTransformFunction(tfunc, resultTail);
+      arr = nsStyleAnimation::AppendTransformFunction(tfunc, resultTail);
     }
 
     switch (tfunc) {
       case eCSSKeyword_translate3d: {
           NS_ABORT_IF_FALSE(a1->Count() == 4, "unexpected count");
           NS_ABORT_IF_FALSE(a2->Count() == 4, "unexpected count");
           AddTransformTranslate(a1->Item(1), aCoeff1, a2->Item(1), aCoeff2,
                                 arr->Item(1));
--- a/layout/style/nsStyleAnimation.h
+++ b/layout/style/nsStyleAnimation.h
@@ -10,24 +10,20 @@
 
 #include "prtypes.h"
 #include "nsAString.h"
 #include "nsCRTGlue.h"
 #include "nsStringBuffer.h"
 #include "nsCSSProperty.h"
 #include "nsCoord.h"
 #include "nsColor.h"
+#include "nsCSSValue.h"
 
 class nsPresContext;
 class nsStyleContext;
-class nsCSSValue;
-struct nsCSSValueList;
-struct nsCSSValuePair;
-struct nsCSSValueTriplet;
-struct nsCSSValuePairList;
 struct nsCSSRect;
 class gfx3DMatrix;
 
 namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
@@ -127,17 +123,17 @@ public:
 
   // Type-conversion methods
   // -----------------------
   /**
    * Creates a computed value for the given specified value
    * (property ID + string).  A style context is needed in case the
    * specified value depends on inherited style or on the values of other
    * properties.
-   * 
+   *
    * @param aProperty       The property whose value we're computing.
    * @param aTargetElement  The content node to which our computed value is
    *                        applicable.
    * @param aSpecifiedValue The specified value, from which we'll build our
    *                        computed value.
    * @param aUseSVGMode     A flag to indicate whether we should parse
    *                        |aSpecifiedValue| in SVG mode.
    * @param [out] aComputedValue The resulting computed value.
@@ -198,19 +194,23 @@ public:
    /**
     * Interpolates between 2 matrices by decomposing them.
     *
     * @param aMatrix1   First matrix, using CSS pixel units.
     * @param aMatrix2   Second matrix, using CSS pixel units.
     * @param aProgress  Interpolation value in the range [0.0, 1.0]
     */
    static gfx3DMatrix InterpolateTransformMatrix(const gfx3DMatrix &aMatrix1,
-                                                 const gfx3DMatrix &aMatrix2, 
+                                                 const gfx3DMatrix &aMatrix2,
                                                  double aProgress);
 
+   static already_AddRefed<nsCSSValue::Array>
+     AppendTransformFunction(nsCSSKeyword aTransformFunction,
+                             nsCSSValueList**& aListTail);
+
   /**
    * The types and values for the values that we extract and animate.
    */
   enum Unit {
     eUnit_Null, // not initialized
     eUnit_Normal,
     eUnit_Auto,
     eUnit_None,
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -55,22 +55,23 @@ static nscoord CalcLength(const nsCSSVal
     //
     // Raw numbers are treated as being pixels.
     return nsPresContext::CSSPixelsToAppUnits(aValue.GetFloatValue());
   }
   return nsRuleNode::CalcLength(aValue, aContext, aPresContext,
                                 aCanStoreInRuleTree);
 }
 
-static float
+float
 ProcessTranslatePart(const nsCSSValue& aValue,
                      nsStyleContext* aContext,
                      nsPresContext* aPresContext,
                      bool& aCanStoreInRuleTree,
-                     nscoord aSize, float aAppUnitsPerMatrixUnit)
+                     nscoord aSize,
+                     float aAppUnitsPerMatrixUnit)
 {
   nscoord offset = 0;
   float percent = 0.0f;
 
   if (aValue.GetUnit() == eCSSUnit_Percent) {
     percent = aValue.GetPercentValue();
   } else if (aValue.IsCalcUnit()) {
     nsRuleNode::ComputedCalc result =
--- a/layout/style/nsStyleTransformMatrix.h
+++ b/layout/style/nsStyleTransformMatrix.h
@@ -25,16 +25,23 @@ class nsPresContext;
 namespace nsStyleTransformMatrix {
   
   /**
    * Return the transform function, as an nsCSSKeyword, for the given
    * nsCSSValue::Array from a transform list.
    */
   nsCSSKeyword TransformFunctionOf(const nsCSSValue::Array* aData);
 
+  float ProcessTranslatePart(const nsCSSValue& aValue,
+                             nsStyleContext* aContext,
+                             nsPresContext* aPresContext,
+                             bool& aCanStoreInRuleTree,
+                             nscoord aSize,
+                             float aAppUnitsPerMatrixUnit);
+
   /**
    * Given an nsCSSValueList containing -moz-transform functions,
    * returns a matrix containing the value of those functions.
    *
    * @param aData The nsCSSValueList containing the transform functions
    * @param aContext The style context, used for unit conversion.
    * @param aPresContext The presentation context, used for unit conversion.
    * @param aCanStoreInRuleTree Set to false if the result cannot be cached
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3498,16 +3498,20 @@ pref("layers.acceleration.disabled", tru
 pref("layers.acceleration.disabled", false);
 #endif
 
 // Whether to force acceleration on, ignoring blacklists.
 pref("layers.acceleration.force-enabled", false);
 
 pref("layers.acceleration.draw-fps", false);
 
+// Whether to animate simple opacity and transforms on the compositor
+pref("layers.offmainthreadcomposition.animate-opacity", false);
+pref("layers.offmainthreadcomposition.animate-transform", false);
+
 #ifdef MOZ_X11
 #ifdef MOZ_WIDGET_GTK2
 pref("gfx.xrender.enabled",true);
 #endif
 #endif
 
 #ifdef XP_WIN
 // Whether to disable the automatic detection and use of direct2d.