Bug 706179: Add support for animations to the Layers API r=roc, dbaron, cjones
☠☠ backed out by 9d2a7a8ca1c7 ☠ ☠
authorDavid Zbarsky <dzbarsky@gmail.com>
Fri, 27 Jul 2012 13:23:44 -0700
changeset 100764 169ff207ed19ed2761784511ef568ee2764609a6
parent 100763 efb7823db78a1abda36af7fbc9a29ecf461739e4
child 100765 a34baed70c1bf5e4167eff7680912a0e234566c2
push idunknown
push userunknown
push dateunknown
reviewersroc, dbaron, cjones
bugs706179
milestone17.0a1
Bug 706179: Add support for animations to the Layers API r=roc, dbaron, cjones
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/PLayer.ipdl
gfx/layers/ipc/PLayers.ipdl
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayers.h
gfx/layers/ipc/ShadowLayersParent.cpp
gfx/layers/opengl/CanvasLayerOGL.cpp
gfx/src/nsRect.h
ipc/glue/IPCMessageUtils.h
xpcom/ds/TimeStamp.h
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -12,16 +12,17 @@
 #include "ImageLayers.h"
 #include "Layers.h"
 #include "gfxPlatform.h"
 #include "ReadbackLayer.h"
 #include "gfxUtils.h"
 #include "nsPrintfCString.h"
 #include "mozilla/Util.h"
 #include "LayerSorter.h"
+#include "AnimationCommon.h"
 
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
 
 typedef FrameMetrics::ViewID ViewID;
 const ViewID FrameMetrics::NULL_SCROLL_ID = 0;
 const ViewID FrameMetrics::ROOT_SCROLL_ID = 1;
 const ViewID FrameMetrics::START_SCROLL_ID = 2;
@@ -215,16 +216,255 @@ LayerManager::CreateAsynchronousImageCon
 {
   nsRefPtr<ImageContainer> container = new ImageContainer(ImageContainer::ENABLE_ASYNC);
   return container.forget();
 }
 
 //--------------------------------------------------
 // Layer
 
+Layer::Layer(LayerManager* aManager, void* aImplData) :
+  mManager(aManager),
+  mParent(nsnull),
+  mNextSibling(nsnull),
+  mPrevSibling(nsnull),
+  mImplData(aImplData),
+  mMaskLayer(nsnull),
+  mXScale(1.0f),
+  mYScale(1.0f),
+  mOpacity(1.0),
+  mContentFlags(0),
+  mUseClipRect(false),
+  mUseTileSourceRect(false),
+  mIsFixedPosition(false),
+  mDebugColorIndex(0)
+{}
+
+Layer::~Layer()
+{}
+
+void
+Layer::AddAnimation(const Animation& aAnimation)
+{
+  if (!AsShadowableLayer() || !AsShadowableLayer()->HasShadow())
+    return;
+
+  MOZ_ASSERT(aAnimation.segments().Length() >= 1);
+
+  mAnimations.AppendElement(aAnimation);
+  Mutated();
+}
+
+void
+Layer::ClearAnimations()
+{
+  mAnimations.Clear();
+  mAnimationData.Clear();
+  Mutated();
+}
+
+static nsCSSValueList*
+CreateCSSValueList(const InfallibleTArray<TransformFunction>& aFunctions)
+{
+  nsAutoPtr<nsCSSValueList> result;
+  nsCSSValueList** resultTail = getter_Transfers(result);
+  for (PRUint32 i = 0; i < aFunctions.Length(); i++) {
+    nsRefPtr<nsCSSValue::Array> arr;
+    switch (aFunctions[i].type()) {
+      case TransformFunction::TRotationX:
+      {
+        float theta = aFunctions[i].get_RotationX().radians();
+        arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotatex, resultTail);
+        arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian);
+        break;
+      }
+      case TransformFunction::TRotationY:
+      {
+        float theta = aFunctions[i].get_RotationY().radians();
+        arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotatey, resultTail);
+        arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian);
+        break;
+      }
+      case TransformFunction::TRotationZ:
+      {
+        float theta = aFunctions[i].get_RotationZ().radians();
+        arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotatez, resultTail);
+        arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian);
+        break;
+      }
+      case TransformFunction::TRotation:
+      {
+        float theta = aFunctions[i].get_Rotation().radians();
+        arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotate, resultTail);
+        arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian);
+        break;
+      }
+      case TransformFunction::TRotation3D:
+      {
+        float x = aFunctions[i].get_Rotation3D().x();
+        float y = aFunctions[i].get_Rotation3D().y();
+        float z = aFunctions[i].get_Rotation3D().z();
+        float theta = aFunctions[i].get_Rotation3D().radians();
+        arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotate3d, resultTail);
+        arr->Item(1).SetFloatValue(x, eCSSUnit_Number);
+        arr->Item(2).SetFloatValue(y, eCSSUnit_Number);
+        arr->Item(3).SetFloatValue(z, eCSSUnit_Number);
+        arr->Item(4).SetFloatValue(theta, eCSSUnit_Radian);
+        break;
+      }
+      case TransformFunction::TScale:
+      {
+        arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_scale3d, resultTail);
+        arr->Item(1).SetFloatValue(aFunctions[i].get_Scale().x(), eCSSUnit_Number);
+        arr->Item(2).SetFloatValue(aFunctions[i].get_Scale().y(), eCSSUnit_Number);
+        arr->Item(3).SetFloatValue(aFunctions[i].get_Scale().z(), eCSSUnit_Number);
+        break;
+      }
+      case TransformFunction::TTranslation:
+      {
+        arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_translate3d, resultTail);
+        arr->Item(1).SetFloatValue(aFunctions[i].get_Translation().x(), eCSSUnit_Pixel);
+        arr->Item(2).SetFloatValue(aFunctions[i].get_Translation().y(), eCSSUnit_Pixel);
+        arr->Item(3).SetFloatValue(aFunctions[i].get_Translation().z(), eCSSUnit_Pixel);
+        break;
+      }
+      case TransformFunction::TSkewX:
+      {
+        float x = aFunctions[i].get_SkewX().x();
+        arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_skewx, resultTail);
+        arr->Item(1).SetFloatValue(x, eCSSUnit_Number);
+        break;
+      }
+      case TransformFunction::TSkewY:
+      {
+        float y = aFunctions[i].get_SkewY().y();
+        arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_skewy, resultTail);
+        arr->Item(1).SetFloatValue(y, eCSSUnit_Number);
+        break;
+      }
+      case TransformFunction::TTransformMatrix:
+      {
+        arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_matrix3d, resultTail);
+        const gfx3DMatrix& matrix = aFunctions[i].get_TransformMatrix().value();
+        arr->Item(1).SetFloatValue(matrix._11, eCSSUnit_Number);
+        arr->Item(2).SetFloatValue(matrix._12, eCSSUnit_Number);
+        arr->Item(3).SetFloatValue(matrix._13, eCSSUnit_Number);
+        arr->Item(4).SetFloatValue(matrix._14, eCSSUnit_Number);
+        arr->Item(5).SetFloatValue(matrix._21, eCSSUnit_Number);
+        arr->Item(6).SetFloatValue(matrix._22, eCSSUnit_Number);
+        arr->Item(7).SetFloatValue(matrix._23, eCSSUnit_Number);
+        arr->Item(8).SetFloatValue(matrix._24, eCSSUnit_Number);
+        arr->Item(9).SetFloatValue(matrix._31, eCSSUnit_Number);
+        arr->Item(10).SetFloatValue(matrix._32, eCSSUnit_Number);
+        arr->Item(11).SetFloatValue(matrix._33, eCSSUnit_Number);
+        arr->Item(12).SetFloatValue(matrix._34, eCSSUnit_Number);
+        arr->Item(13).SetFloatValue(matrix._41, eCSSUnit_Number);
+        arr->Item(14).SetFloatValue(matrix._42, eCSSUnit_Number);
+        arr->Item(15).SetFloatValue(matrix._43, eCSSUnit_Number);
+        arr->Item(16).SetFloatValue(matrix._44, eCSSUnit_Number);
+        break;
+      }
+      case TransformFunction::TPerspective:
+      {
+        float perspective = aFunctions[i].get_Perspective().value();
+        arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_perspective, resultTail);
+        arr->Item(1).SetFloatValue(perspective, eCSSUnit_Pixel);
+        break;
+      }
+      default:
+        NS_ASSERTION(false, "All functions should be implemented?");
+    }
+  }
+  return result.forget();
+}
+
+void
+Layer::SetAnimations(const AnimationArray& aAnimations)
+{
+  mAnimations = aAnimations;
+  mAnimationData.Clear();
+  for (PRUint32 i = 0; i < mAnimations.Length(); i++) {
+    AnimData data;
+    InfallibleTArray<css::ComputedTimingFunction*>* functions =
+      &data.mFunctions;
+    nsTArray<AnimationSegment> segments = mAnimations.ElementAt(i).segments();
+    for (PRUint32 j = 0; j < segments.Length(); j++) {
+      TimingFunction tf = segments.ElementAt(j).sampleFn();
+      css::ComputedTimingFunction* ctf = new css::ComputedTimingFunction();
+      switch (tf.type()) {
+        case TimingFunction::TCubicBezierFunction: {
+          CubicBezierFunction cbf = tf.get_CubicBezierFunction();
+          ctf->Init(nsTimingFunction(cbf.x1(), cbf.y1(), cbf.x2(), cbf.y2()));
+          break;
+        }
+        default: {
+          NS_ASSERTION(tf.type() == TimingFunction::TStepFunction,
+                       "Function must be bezier or step");
+          StepFunction sf = tf.get_StepFunction();
+          nsTimingFunction::Type type = sf.type() == 1 ? nsTimingFunction::StepStart
+                                                       : nsTimingFunction::StepEnd;
+          ctf->Init(nsTimingFunction(type, sf.steps()));
+          break;
+        }
+      }
+      functions->AppendElement(ctf);
+    }
+
+    // Precompute the nsStyleAnimation::Values that we need if this is a transform
+    // animation.
+    InfallibleTArray<nsStyleAnimation::Value>* startValues =
+      &data.mStartValues;
+    InfallibleTArray<nsStyleAnimation::Value>* endValues =
+      &data.mEndValues;
+    for (PRUint32 j = 0; j < mAnimations[i].segments().Length(); j++) {
+      const AnimationSegment& segment = mAnimations[i].segments()[j];
+      if (segment.endState().type() == Animatable::TArrayOfTransformFunction) {
+        const InfallibleTArray<TransformFunction>& startFunctions =
+          segment.startState().get_ArrayOfTransformFunction();
+        nsStyleAnimation::Value startValue;
+        nsCSSValueList* startList;
+        if (startFunctions.Length() > 0) {
+          startList = CreateCSSValueList(startFunctions);
+        } else {
+          startList = new nsCSSValueList();
+          startList->mValue.SetNoneValue();
+        }
+        startValue.SetAndAdoptCSSValueListValue(startList, nsStyleAnimation::eUnit_Transform);
+        startValues->AppendElement(startValue);
+
+        const InfallibleTArray<TransformFunction>& endFunctions =
+          segment.endState().get_ArrayOfTransformFunction();
+        nsStyleAnimation::Value endValue;
+        nsCSSValueList* endList;
+        if (endFunctions.Length() > 0) {
+          endList = CreateCSSValueList(endFunctions);
+        } else {
+          endList = new nsCSSValueList();
+          endList->mValue.SetNoneValue();
+        }
+        endValue.SetAndAdoptCSSValueListValue(endList, nsStyleAnimation::eUnit_Transform);
+        endValues->AppendElement(endValue);
+      } else {
+        NS_ASSERTION(segment.endState().type() == Animatable::TOpacity,
+                     "Unknown Animatable type");
+        nsStyleAnimation::Value startValue;
+        startValue.SetFloatValue(segment.startState().get_Opacity().value());
+        startValues->AppendElement(startValue);
+
+        nsStyleAnimation::Value endValue;
+        endValue.SetFloatValue(segment.endState().get_Opacity().value());
+        endValues->AppendElement(endValue);
+      }
+    }
+    mAnimationData.AppendElement(data);
+  }
+
+  Mutated();
+}
+
 bool
 Layer::CanUseOpaqueSurface()
 {
   // If the visible content in the layer is opaque, there is no need
   // for an alpha channel.
   if (GetContentFlags() & CONTENT_OPAQUE)
     return true;
   // Also, if this layer is the bottommost layer in a container which
@@ -299,17 +539,17 @@ Layer::SnapTransform(const gfx3DMatrix& 
       *aResidualTransform = matrix2D * snappedMatrixInverse;
     }
   } else {
     result = aTransform;
   }
   return result;
 }
 
-nsIntRect 
+nsIntRect
 Layer::CalculateScissorRect(const nsIntRect& aCurrentScissorRect,
                             const gfxMatrix* aWorldTransform)
 {
   ContainerLayer* container = GetParent();
   NS_ASSERTION(container, "This can't be called on the root!");
 
   // Establish initial clip rect: it's either the one passed in, or
   // if the parent has an intermediate surface, it's the extents of that surface.
@@ -356,31 +596,44 @@ Layer::CalculateScissorRect(const nsIntR
     gfxRect trScissor = aWorldTransform->TransformBounds(r);
     trScissor.Round();
     if (!gfxUtils::GfxRectToIntRect(trScissor, &scissor))
       return nsIntRect(currentClip.TopLeft(), nsIntSize(0, 0));
   }
   return currentClip.Intersect(scissor);
 }
 
-const gfx3DMatrix&
+const gfx3DMatrix
 Layer::GetLocalTransform()
 {
+  gfx3DMatrix transform;
+  if (ShadowLayer* shadow = AsShadowLayer()) {
+    transform = shadow->GetShadowTransform();
+  } else {
+    transform = mTransform;
+  }
+  transform.Scale(mXScale, mYScale, 1);
+  return transform;
+}
+
+const float
+Layer::GetLocalOpacity()
+{
   if (ShadowLayer* shadow = AsShadowLayer())
-    return shadow->GetShadowTransform();
-  return mTransform;
+    return shadow->GetShadowOpacity();
+  return mOpacity;
 }
 
 float
 Layer::GetEffectiveOpacity()
 {
-  float opacity = GetOpacity();
+  float opacity = GetLocalOpacity();
   for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface();
        c = c->GetParent()) {
-    opacity *= c->GetOpacity();
+    opacity *= c->GetLocalOpacity();
   }
   return opacity;
 }
 
 void
 Layer::ComputeEffectiveTransformForMaskLayer(const gfx3DMatrix& aTransformToSurface)
 {
   if (mMaskLayer) {
@@ -785,34 +1038,34 @@ ReadbackLayer::PrintInfo(nsACString& aTo
 
 //--------------------------------------------------
 // LayerManager
 
 void
 LayerManager::Dump(FILE* aFile, const char* aPrefix)
 {
   FILE* file = FILEOrDefault(aFile);
- 
+
   fprintf(file, "<ul><li><a ");
 #ifdef MOZ_DUMP_PAINTING
   WriteSnapshotLinkToDumpFile(this, file);
 #endif
   fprintf(file, ">");
   DumpSelf(file, aPrefix);
 #ifdef MOZ_DUMP_PAINTING
   fprintf(file, "</a>");
 #endif
 
   nsCAutoString pfx(aPrefix);
   pfx += "  ";
   if (!GetRoot()) {
     fprintf(file, "%s(null)</li></ul>", pfx.get());
     return;
   }
- 
+
   fprintf(file, "<ul>");
   GetRoot()->Dump(file, pfx.get());
   fprintf(file, "</ul></li></ul>");
 }
 
 void
 LayerManager::DumpSelf(FILE* aFile, const char* aPrefix)
 {
@@ -936,10 +1189,10 @@ LayerManager::PrintInfo(nsACString& aTo,
 
 /*static*/ void LayerManager::InitLog() {}
 /*static*/ bool LayerManager::IsLogEnabled() { return false; }
 
 #endif // MOZ_LAYERS_HAVE_LOG
 
 PRLogModuleInfo* LayerManager::sLog;
 
-} // namespace layers 
+} // namespace layers
 } // namespace mozilla
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -13,16 +13,17 @@
 #include "nsRect.h"
 #include "nsISupportsImpl.h"
 #include "nsAutoPtr.h"
 #include "gfx3DMatrix.h"
 #include "gfxColor.h"
 #include "gfxPattern.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
+#include "nsStyleAnimation.h"
 #include "LayersBackend.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/TimeStamp.h"
 
 #if defined(DEBUG) || defined(PR_LOGGING)
 #  include <stdio.h>            // FILE
 #  include "prlog.h"
 #  ifndef MOZ_LAYERS_HAVE_LOG
@@ -38,18 +39,24 @@ struct PRLogModuleInfo;
 class gfxContext;
 class nsPaintEvent;
 
 namespace mozilla {
 namespace gl {
 class GLContext;
 }
 
+namespace css {
+class ComputedTimingFunction;
+}
+
 namespace layers {
 
+class Animation;
+class CommonLayerAttributes;
 class Layer;
 class ThebesLayer;
 class ContainerLayer;
 class ImageLayer;
 class ColorLayer;
 class ImageContainer;
 class CanvasLayer;
 class ReadbackLayer;
@@ -525,16 +532,23 @@ protected:
   static PRLogModuleInfo* sLog;
   uint64_t mId;
 private:
   TimeStamp mLastFrameTime;
   nsTArray<float> mFrameTimes;
 };
 
 class ThebesLayer;
+typedef InfallibleTArray<Animation> AnimationArray;
+
+struct AnimData {
+  InfallibleTArray<nsStyleAnimation::Value> mStartValues;
+  InfallibleTArray<nsStyleAnimation::Value> mEndValues;
+  InfallibleTArray<mozilla::css::ComputedTimingFunction*> mFunctions;
+};
 
 /**
  * A Layer represents anything that can be rendered onto a destination
  * surface.
  */
 class THEBES_API Layer {
   NS_INLINE_DECL_REFCOUNTING(Layer)  
 
@@ -546,17 +560,17 @@ public:
     TYPE_CONTAINER,
     TYPE_IMAGE,
     TYPE_READBACK,
     TYPE_REF,
     TYPE_SHADOW,
     TYPE_THEBES
   };
 
-  virtual ~Layer() {}
+  virtual ~Layer();
 
   /**
    * Returns the LayerManager this Layer belongs to. Note that the layer
    * manager might be in a destroyed state, at which point it's only
    * valid to set/get user data from it.
    */
   LayerManager* Manager() { return mManager; }
 
@@ -703,24 +717,39 @@ public:
    * are supported.
    */
   void SetTransform(const gfx3DMatrix& aMatrix)
   {
     mTransform = aMatrix;
     Mutated();
   }
 
+  void SetScale(float aXScale, float aYScale)
+  {
+    mXScale = aXScale;
+    mYScale = aYScale;
+    Mutated();
+  }
+
   /**
    * CONSTRUCTION PHASE ONLY
    * A layer is "fixed position" when it draws content from a content
    * (not chrome) document, the topmost content document has a root scrollframe
    * with a displayport, but the layer does not move when that displayport scrolls.
    */
   void SetIsFixedPosition(bool aFixedPosition) { mIsFixedPosition = aFixedPosition; }
 
+  // Call AddAnimation to add an animation to this layer from layout code.
+  void AddAnimation(const Animation& aAnimation);
+  // ClearAnimations clears animations on this layer.
+  void ClearAnimations();
+  // This is only called when the layer tree is updated. Do not call this from
+  // layout code.  To add an animation to this layer, use AddAnimation.
+  void SetAnimations(const AnimationArray& aAnimations);
+
   /**
    * CONSTRUCTION PHASE ONLY
    * If a layer is "fixed position", this determines which point on the layer
    * is considered the "anchor" point, that is, the point which remains in the
    * same position when compositing the layer tree with a transformation
    * (such as when asynchronously scrolling and zooming).
    */
   void SetFixedPositionAnchor(const gfxPoint& aAnchor) { mAnchor = aAnchor; }
@@ -731,20 +760,23 @@ public:
   PRUint32 GetContentFlags() { return mContentFlags; }
   const nsIntRegion& GetVisibleRegion() { return mVisibleRegion; }
   ContainerLayer* GetParent() { return mParent; }
   Layer* GetNextSibling() { return mNextSibling; }
   Layer* GetPrevSibling() { return mPrevSibling; }
   virtual Layer* GetFirstChild() { return nsnull; }
   virtual Layer* GetLastChild() { return nsnull; }
   const gfx3DMatrix& GetTransform() { return mTransform; }
+  float GetXScale() { return mXScale; }
+  float GetYScale() { return mYScale; }
   bool GetIsFixedPosition() { return mIsFixedPosition; }
   gfxPoint GetFixedPositionAnchor() { return mAnchor; }
   Layer* GetMaskLayer() { return mMaskLayer; }
-
+  AnimationArray& GetAnimations() { return mAnimations; }
+  InfallibleTArray<AnimData>& GetAnimationData() { return mAnimationData; }
   /**
    * DRAWING PHASE ONLY
    *
    * Write layer-subtype-specific attributes into aAttrs.  Used to
    * synchronize layer attributes to their shadows'.
    */
   virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { }
 
@@ -935,45 +967,38 @@ public:
   static bool IsLogEnabled() { return LayerManager::IsLogEnabled(); }
 
 #ifdef DEBUG
   void SetDebugColorIndex(PRUint32 aIndex) { mDebugColorIndex = aIndex; }
   PRUint32 GetDebugColorIndex() { return mDebugColorIndex; }
 #endif
 
 protected:
-  Layer(LayerManager* aManager, void* aImplData) :
-    mManager(aManager),
-    mParent(nsnull),
-    mNextSibling(nsnull),
-    mPrevSibling(nsnull),
-    mImplData(aImplData),
-    mMaskLayer(nsnull),
-    mOpacity(1.0),
-    mContentFlags(0),
-    mUseClipRect(false),
-    mUseTileSourceRect(false),
-    mIsFixedPosition(false),
-    mDebugColorIndex(0)
-    {}
+  Layer(LayerManager* aManager, void* aImplData);
 
   void Mutated() { mManager->Mutated(this); }
 
   // Print interesting information about this into aTo.  Internally
   // used to implement Dump*() and Log*().  If subclasses have
   // additional interesting properties, they should override this with
   // an implementation that first calls the base implementation then
   // appends additional info to aTo.
   virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix);
 
   /**
    * Returns the local transform for this layer: either mTransform or,
    * for shadow layers, GetShadowTransform()
    */
-  const gfx3DMatrix& GetLocalTransform();
+  const gfx3DMatrix GetLocalTransform();
+
+  /**
+   * Returns the local opacity for this layer: either mOpacity or,
+   * for shadow layers, GetShadowOpacity()
+   */
+  const float GetLocalOpacity();
 
   /**
    * Computes a tweaked version of aTransform that snaps a point or a rectangle
    * to pixel boundaries. Snapping is only performed if this layer's
    * layer manager has enabled snapping (which is the default).
    * @param aSnapRect a rectangle whose edges should be snapped to pixel
    * boundaries in the destination surface. If the rectangle is empty,
    * then the snapping process should preserve the scale factors of the
@@ -989,17 +1014,21 @@ protected:
   ContainerLayer* mParent;
   Layer* mNextSibling;
   Layer* mPrevSibling;
   void* mImplData;
   nsRefPtr<Layer> mMaskLayer;
   gfx::UserData mUserData;
   nsIntRegion mVisibleRegion;
   gfx3DMatrix mTransform;
+  float mXScale;
+  float mYScale;
   gfx3DMatrix mEffectiveTransform;
+  AnimationArray mAnimations;
+  InfallibleTArray<AnimData> mAnimationData;
   float mOpacity;
   nsIntRect mClipRect;
   nsIntRect mTileSourceRect;
   PRUint32 mContentFlags;
   bool mUseClipRect;
   bool mUseTileSourceRect;
   bool mIsFixedPosition;
   gfxPoint mAnchor;
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -16,16 +16,25 @@
 #include "AsyncPanZoomController.h"
 #include "BasicLayers.h"
 #include "CompositorParent.h"
 #include "LayerManagerOGL.h"
 #include "nsGkAtoms.h"
 #include "nsIWidget.h"
 #include "RenderTrace.h"
 #include "ShadowLayersParent.h"
+#include "BasicLayers.h"
+#include "LayerManagerOGL.h"
+#include "nsIWidget.h"
+#include "nsGkAtoms.h"
+#include "RenderTrace.h"
+#include "nsStyleAnimation.h"
+#include "nsDisplayList.h"
+#include "AnimationCommon.h"
+#include "nsAnimationManager.h"
 
 using namespace base;
 using namespace mozilla::ipc;
 using namespace std;
 
 namespace mozilla {
 namespace layers {
 
@@ -426,25 +435,25 @@ CompositorParent::Composite()
   mCurrentCompositeTask = NULL;
 
   mLastCompose = TimeStamp::Now();
 
   if (mPaused || !mLayerManager || !mLayerManager->GetRoot()) {
     return;
   }
 
-  Layer* aLayer = mLayerManager->GetRoot();
-  AutoResolveRefLayers resolve(aLayer);
+  Layer* layer = mLayerManager->GetRoot();
+  AutoResolveRefLayers resolve(layer);
 
   bool requestNextFrame = TransformShadowTree(mLastCompose);
   if (requestNextFrame) {
     ScheduleComposition();
   }
 
-  RenderTraceLayers(aLayer, "0000");
+  RenderTraceLayers(layer, "0000");
 
   if (LAYERS_OPENGL == mLayerManager->GetBackendType() &&
       !mTargetConfig.naturalBounds().IsEmpty()) {
     LayerManagerOGL* lm = static_cast<LayerManagerOGL*>(mLayerManager.get());
     lm->SetWorldTransform(
       ComputeGLTransformForRotation(mTargetConfig.naturalBounds(),
                                     mTargetConfig.rotation()));
   }
@@ -534,23 +543,132 @@ CompositorParent::TransformFixedLayers(L
 static void
 SetShadowProperties(Layer* aLayer)
 {
   // FIXME: Bug 717688 -- Do these updates in ShadowLayersParent::RecvUpdate.
   ShadowLayer* shadow = aLayer->AsShadowLayer();
   shadow->SetShadowTransform(aLayer->GetTransform());
   shadow->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
   shadow->SetShadowClipRect(aLayer->GetClipRect());
+  shadow->SetShadowOpacity(aLayer->GetOpacity());
 
   for (Layer* child = aLayer->GetFirstChild();
       child; child = child->GetNextSibling()) {
     SetShadowProperties(child);
   }
 }
 
+
+// SampleValue should eventually take the CSS property as an argument.  This
+// will be needed if we ever animate two values with the same type but different
+// interpolation rules.
+static void
+SampleValue(float aPortion, Animation& aAnimation, nsStyleAnimation::Value& aStart,
+            nsStyleAnimation::Value& aEnd, Animatable* aValue)
+{
+  nsStyleAnimation::Value interpolatedValue;
+  NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
+               aStart.GetUnit() == nsStyleAnimation::eUnit_None ||
+               aEnd.GetUnit() == nsStyleAnimation::eUnit_None, "Must have same unit");
+  if (aStart.GetUnit() == nsStyleAnimation::eUnit_Transform ||
+      aEnd.GetUnit() == nsStyleAnimation::eUnit_Transform) {
+    nsStyleAnimation::Interpolate(eCSSProperty_transform, aStart, aEnd,
+                                  aPortion, interpolatedValue);
+    nsCSSValueList* interpolatedList = interpolatedValue.GetCSSValueListValue();
+
+    TransformData& data = aAnimation.data().get_TransformData();
+    gfx3DMatrix transform =
+      nsDisplayTransform::GetResultingTransformMatrix(nsnull, data.origin(), nsDeviceContext::AppUnitsPerCSSPixel(),
+                                                      &data.bounds(), interpolatedList, &data.mozOrigin(),
+                                                      &data.perspectiveOrigin(), &data.perspective());
+
+    InfallibleTArray<TransformFunction>* functions = new InfallibleTArray<TransformFunction>();
+    functions->AppendElement(TransformMatrix(transform));
+    *aValue = *functions;
+    return;
+  }
+
+  NS_ASSERTION(aStart.GetUnit() == nsStyleAnimation::eUnit_Float, "Should be opacity");
+  nsStyleAnimation::Interpolate(eCSSProperty_opacity, aStart, aEnd,
+                                aPortion, interpolatedValue);
+  *aValue = interpolatedValue.GetFloatValue();
+}
+
+static bool
+SampleAnimations(Layer* aLayer, TimeStamp aPoint)
+{
+  AnimationArray& animations = aLayer->GetAnimations();
+  InfallibleTArray<AnimData>& animationData = aLayer->GetAnimationData();
+
+  bool activeAnimations = false;
+
+  for (PRUint32 i = animations.Length(); i-- !=0; ) {
+    Animation& animation = animations[i];
+    AnimData& animData = animationData[i];
+
+    double numIterations = animation.numIterations() != -1 ?
+      animation.numIterations() : NS_IEEEPositiveInfinity();
+    double positionInIteration =
+      ElementAnimations::GetPositionInIteration(animation.startTime(),
+                                                aPoint,
+                                                animation.duration(),
+                                                numIterations,
+                                                animation.direction());
+
+    if (positionInIteration == -1) {
+        animations.RemoveElementAt(i);
+        animationData.RemoveElementAt(i);
+        continue;
+    }
+
+    NS_ABORT_IF_FALSE(0.0 <= positionInIteration &&
+                          positionInIteration <= 1.0,
+                        "position should be in [0-1]");
+
+    int segmentIndex = 0;
+    AnimationSegment* segment = animation.segments().Elements();
+    while (segment->endPortion() < positionInIteration) {
+      ++segment;
+      ++segmentIndex;
+    }
+
+    double positionInSegment = (positionInIteration - segment->startPortion()) /
+                                 (segment->endPortion() - segment->startPortion());
+
+    double portion = animData.mFunctions[segmentIndex]->GetValue(positionInSegment);
+
+    activeAnimations = true;
+
+    // interpolate the property
+    Animatable interpolatedValue;
+    SampleValue(portion, animation, animData.mStartValues[segmentIndex],
+                animData.mEndValues[segmentIndex], &interpolatedValue);
+    ShadowLayer* shadow = aLayer->AsShadowLayer();
+    switch (interpolatedValue.type()) {
+    case Animatable::TOpacity:
+      shadow->SetShadowOpacity(interpolatedValue.get_Opacity().value());
+      break;
+   case Animatable::TArrayOfTransformFunction: {
+      gfx3DMatrix matrix = interpolatedValue.get_ArrayOfTransformFunction()[0].get_TransformMatrix().value();
+      shadow->SetShadowTransform(matrix);
+      break;
+    }
+    default:
+      NS_WARNING("Unhandled animated property");
+    }
+  }
+
+  for (Layer* child = aLayer->GetFirstChild(); child;
+       child = child->GetNextSibling()) {
+    activeAnimations |= SampleAnimations(child, aPoint);
+  }
+
+  return activeAnimations;
+}
+
 bool
 CompositorParent::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame,
                                                    Layer *aLayer,
                                                    bool* aWantNextFrame)
 {
   bool appliedTransform = false;
   for (Layer* child = aLayer->GetFirstChild();
       child; child = child->GetNextSibling()) {
@@ -586,16 +704,20 @@ bool
 CompositorParent::TransformShadowTree(TimeStamp aCurrentFrame)
 {
   bool wantNextFrame = false;
   Layer* layer = GetPrimaryScrollableLayer();
   ShadowLayer* shadow = layer->AsShadowLayer();
   ContainerLayer* container = layer->AsContainerLayer();
   Layer* root = mLayerManager->GetRoot();
 
+  // NB: we must sample animations *before* sampling pan/zoom
+  // transforms.
+  wantNextFrame |= SampleAnimations(layer, mLastCompose);
+
   const FrameMetrics& metrics = container->GetFrameMetrics();
   const gfx3DMatrix& rootTransform = root->GetTransform();
   const gfx3DMatrix& currentTransform = layer->GetTransform();
 
   // FIXME/bug 775437: unify this interface with the ~native-fennec
   // derived code
   // 
   // Attempt to apply an async content transform to any layer that has
@@ -681,17 +803,16 @@ CompositorParent::TransformShadowTree(Ti
                          mContentRect.YMost() - mWidgetSize.height / tempScaleDiffY) -
                  metricsScrollOffset.y;
       scaleDiff.y = tempScaleDiffY;
     }
 
     shadow->SetShadowTransform(treeTransform * currentTransform);
     TransformFixedLayers(layer, offset, scaleDiff);
   }
-
   return wantNextFrame;
 }
 
 void
 CompositorParent::SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom,
                                         const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect)
 {
 #ifdef MOZ_WIDGET_ANDROID
--- a/gfx/layers/ipc/PLayer.ipdl
+++ b/gfx/layers/ipc/PLayer.ipdl
@@ -9,17 +9,17 @@ include protocol PLayers;
 
 namespace mozilla {
 namespace layers {
 
 /**
  * PLayer represents a layer shared across thread contexts.
  */
 
-sync protocol PLayer {
+async protocol PLayer {
     manager PLayers;
 
     /**
      * OWNERSHIP MODEL
      *
      * Roughly speaking, the child side "actually owns" a Layer.  This
      * is because the parent side is the "shadow"; when the child
      * releases a Layer, the parent's shadow is no longer meaningful.
@@ -27,13 +27,13 @@ sync protocol PLayer {
      * To implement this model, the concrete PLayerParent keeps a
      * strong ref to its Layer, so the Layer's lifetime is bound to
      * the PLayerParent's.  Then, when the Layer's refcount hits 0 on
      * the child side, we send __delete__() from the child to parent.
      * The parent then releases its Layer, which results in the Layer
      * being deleted "soon" (usually immediately).
      */
 parent:
-    __delete__();
+    async __delete__();
 };
 
 } // layers
 } // mozilla
--- a/gfx/layers/ipc/PLayers.ipdl
+++ b/gfx/layers/ipc/PLayers.ipdl
@@ -9,16 +9,24 @@ include LayersSurfaces;
 using mozilla::ScreenRotation;
 include protocol PCompositor;
 include protocol PGrallocBuffer;
 include protocol PLayer;
 include protocol PRenderFrame;
 
 include "gfxipc/ShadowLayerUtils.h";
 include "mozilla/WidgetUtils.h";
+include "mozilla/TimeStamp.h";
+
+using gfxPoint3D;
+using nscoord;
+using nsRect;
+using nsPoint;
+using mozilla::TimeDuration;
+using mozilla::TimeStamp;
 
 /**
  * The layers protocol is spoken between thread contexts that manage
  * layer (sub)trees.  The protocol comprises atomically publishing
  * layer subtrees to a "shadow" thread context (which grafts the
  * subtree into its own tree), and atomically updating a published
  * subtree.  ("Atomic" in this sense is wrt painting.)
  */
@@ -37,43 +45,148 @@ struct OpCreateContainerLayer  { PLayer 
 struct OpCreateImageLayer      { PLayer layer; };
 struct OpCreateColorLayer      { PLayer layer; };
 struct OpCreateCanvasLayer     { PLayer layer; };
 struct OpCreateRefLayer        { PLayer layer; };
 
 struct ThebesBuffer {
   SurfaceDescriptor buffer;
   nsIntRect rect;
-  nsIntPoint rotation; 
+  nsIntPoint rotation;
 };
 union OptionalThebesBuffer { ThebesBuffer; null_t; };
 
 union CanvasSurface {
   SurfaceDescriptor;
   null_t;
 };
 
-// Change a layer's attributes
+struct CubicBezierFunction {
+  float x1;
+  float y1;
+  float x2;
+  float y2;
+};
+
+struct StepFunction {
+  int steps;
+  // 1 = nsTimingFunction::StepStart, 2 = nsTimingFunction::StepEnd
+  int type;
+};
+
+union TimingFunction {
+  CubicBezierFunction;
+  StepFunction;
+};
+
+struct Color { gfxRGBA value; };
+struct Opacity { float value; };
+struct Perspective { float value; };
+struct RotationX { float radians; };
+struct RotationY { float radians; };
+struct RotationZ { float radians; };
+struct Rotation { float radians; };
+struct Rotation3D {
+  float x;
+  float y;
+  float z;
+  float radians;
+};
+struct Scale {
+  float x;
+  float y;
+  float z;
+};
+struct SkewX { float x; };
+struct SkewY { float y; };
+struct TransformMatrix { gfx3DMatrix value; };
+struct Translation {
+  float x;
+  float y;
+  float z;
+};
+
+union TransformFunction {
+  Perspective;
+  RotationX;
+  RotationY;
+  RotationZ;
+  Rotation;
+  Rotation3D;
+  Scale;
+  SkewX;
+  SkewY;
+  Translation;
+  TransformMatrix;
+};
+
+union Animatable {
+  Color;
+  Opacity;
+  TransformFunction[];
+};
+
+struct AnimationSegment {
+  Animatable startState;
+  Animatable endState;
+  float startPortion;
+  float endPortion;
+  TimingFunction sampleFn;
+};
+
+// Transforms need extra information to correctly convert the list of transform
+// functions to a gfx3DMatrix that can be applied directly to the layer.
+struct TransformData {
+  nsPoint origin;
+  gfxPoint3D mozOrigin;
+  gfxPoint3D perspectiveOrigin;
+  nsRect bounds;
+  nscoord perspective;
+};
+
+union AnimationData {
+  null_t;
+  TransformData;
+};
+
+struct Animation {
+  TimeStamp startTime;
+  TimeDuration duration;
+  // For each frame, the interpolation point is computed based on the
+  // startTime, the direction, the duration, and the current time.
+  // The segments must uniquely cover the portion from 0.0 to 1.0
+  AnimationSegment[] segments;
+  // How many times to repeat the animation.  < 0 means "forever".
+  float numIterations;
+  // This uses the NS_STYLE_ANIMATION_DIRECTION_* constants.
+  int32_t direction;
+  AnimationData data;
+};
+
 struct CommonLayerAttributes {
   nsIntRegion visibleRegion;
-  gfx3DMatrix transform;
+  TransformMatrix transform;
+  float xScale;
+  float yScale;
   PRUint32 contentFlags;
-  float opacity;
+  Opacity opacity;
   bool useClipRect;
   nsIntRect clipRect;
   bool isFixedPosition;
   gfxPoint fixedPositionAnchor;
   nullable PLayer maskLayer;
+  // Animated colors will only honored for ColorLayers.
+  Animation[] animations;
 };
 
 struct ThebesLayerAttributes {
   nsIntRegion validRegion;
 };
 struct ContainerLayerAttributes{ FrameMetrics metrics; };
-struct ColorLayerAttributes    { gfxRGBA color; };
+struct ColorLayerAttributes    { Color color; };
 struct CanvasLayerAttributes   { GraphicsFilterType filter; };
 struct RefLayerAttributes      { int64_t id; };
 struct ImageLayerAttributes    {
   GraphicsFilterType filter;
   bool forceSingleTile;
 };
 
 union SpecificLayerAttributes {
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -295,29 +295,32 @@ ShadowLayerForwarder::EndTransaction(Inf
     ShadowableLayer* shadow = *it;
     Layer* mutant = shadow->AsLayer();
     NS_ABORT_IF_FALSE(!!mutant, "unshadowable layer?");
 
     LayerAttributes attrs;
     CommonLayerAttributes& common = attrs.common();
     common.visibleRegion() = mutant->GetVisibleRegion();
     common.transform() = mutant->GetTransform();
+    common.xScale() = mutant->GetXScale();
+    common.yScale() = mutant->GetYScale();
     common.contentFlags() = mutant->GetContentFlags();
     common.opacity() = mutant->GetOpacity();
     common.useClipRect() = !!mutant->GetClipRect();
     common.clipRect() = (common.useClipRect() ?
                          *mutant->GetClipRect() : nsIntRect());
     common.isFixedPosition() = mutant->GetIsFixedPosition();
     common.fixedPositionAnchor() = mutant->GetFixedPositionAnchor();
     if (Layer* maskLayer = mutant->GetMaskLayer()) {
       common.maskLayerChild() = Shadow(maskLayer->AsShadowableLayer());
     } else {
       common.maskLayerChild() = NULL;
     }
     common.maskLayerParent() = NULL;
+    common.animations() = mutant->GetAnimations();
     attrs.specific() = null_t();
     mutant->FillSpecificAttributes(attrs.specific());
 
     mTxn->AddEdit(OpSetLayerAttributes(NULL, Shadow(shadow), attrs));
   }
 
   AutoInfallibleTArray<Edit, 10> cset;
   size_t nCsets = mTxn->mCset.size() + mTxn->mPaints.size();
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -519,46 +519,54 @@ public:
    *
    * They are analogous to the Layer interface.
    */
   void SetShadowVisibleRegion(const nsIntRegion& aRegion)
   {
     mShadowVisibleRegion = aRegion;
   }
 
+  void SetShadowOpacity(float aOpacity)
+  {
+    mShadowOpacity = aOpacity;
+  }
+
   void SetShadowClipRect(const nsIntRect* aRect)
   {
     mUseShadowClipRect = aRect != nsnull;
     if (aRect) {
       mShadowClipRect = *aRect;
     }
   }
 
   void SetShadowTransform(const gfx3DMatrix& aMatrix)
   {
     mShadowTransform = aMatrix;
   }
 
   // These getters can be used anytime.
+  float GetShadowOpacity() { return mShadowOpacity; }
   const nsIntRect* GetShadowClipRect() { return mUseShadowClipRect ? &mShadowClipRect : nsnull; }
   const nsIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; }
   const gfx3DMatrix& GetShadowTransform() { return mShadowTransform; }
 
   virtual TiledLayerComposer* AsTiledLayerComposer() { return NULL; }
 
 protected:
   ShadowLayer()
     : mAllocator(nsnull)
+    , mShadowOpacity(1.0)
     , mUseShadowClipRect(false)
   {}
 
   ISurfaceDeAllocator* mAllocator;
   nsIntRegion mShadowVisibleRegion;
   gfx3DMatrix mShadowTransform;
   nsIntRect mShadowClipRect;
+  float mShadowOpacity;
   bool mUseShadowClipRect;
 };
 
 
 class ShadowThebesLayer : public ShadowLayer,
                           public ThebesLayer
 {
 public:
--- a/gfx/layers/ipc/ShadowLayersParent.cpp
+++ b/gfx/layers/ipc/ShadowLayersParent.cpp
@@ -209,29 +209,31 @@ ShadowLayersParent::RecvUpdate(const Inf
 
       const OpSetLayerAttributes& osla = edit.get_OpSetLayerAttributes();
       Layer* layer = AsShadowLayer(osla)->AsLayer();
       const LayerAttributes& attrs = osla.attrs();
 
       const CommonLayerAttributes& common = attrs.common();
       layer->SetVisibleRegion(common.visibleRegion());
       layer->SetContentFlags(common.contentFlags());
-      layer->SetOpacity(common.opacity());
+      layer->SetOpacity(common.opacity().value());
       layer->SetClipRect(common.useClipRect() ? &common.clipRect() : NULL);
-      layer->SetTransform(common.transform());
+      layer->SetTransform(common.transform().value());
+      layer->SetScale(common.xScale(), common.yScale());
       static bool fixedPositionLayersEnabled = getenv("MOZ_ENABLE_FIXED_POSITION_LAYERS") != 0;
       if (fixedPositionLayersEnabled) {
         layer->SetIsFixedPosition(common.isFixedPosition());
         layer->SetFixedPositionAnchor(common.fixedPositionAnchor());
       }
       if (PLayerParent* maskLayer = common.maskLayerParent()) {
         layer->SetMaskLayer(cast(maskLayer)->AsLayer());
       } else {
         layer->SetMaskLayer(NULL);
       }
+      layer->SetAnimations(common.animations());
 
       typedef SpecificLayerAttributes Specific;
       const SpecificLayerAttributes& specific = attrs.specific();
       switch (specific.type()) {
       case Specific::Tnull_t:
         break;
 
       case Specific::TThebesLayerAttributes: {
@@ -252,17 +254,17 @@ ShadowLayersParent::RecvUpdate(const Inf
         static_cast<ContainerLayer*>(layer)->SetFrameMetrics(
           specific.get_ContainerLayerAttributes().metrics());
         break;
 
       case Specific::TColorLayerAttributes:
         MOZ_LAYERS_LOG(("[ParentSide]   color layer"));
 
         static_cast<ColorLayer*>(layer)->SetColor(
-          specific.get_ColorLayerAttributes().color());
+          specific.get_ColorLayerAttributes().color().value());
         break;
 
       case Specific::TCanvasLayerAttributes:
         MOZ_LAYERS_LOG(("[ParentSide]   canvas layer"));
 
         static_cast<CanvasLayer*>(layer)->SetFilter(
           specific.get_CanvasLayerAttributes().filter());
         break;
--- a/gfx/layers/opengl/CanvasLayerOGL.cpp
+++ b/gfx/layers/opengl/CanvasLayerOGL.cpp
@@ -105,26 +105,26 @@ CanvasLayerOGL::Initialize(const Data& a
 
     mNeedsYFlip = mCanvasGLContext->GetOffscreenTexture() != 0;
   } else {
     NS_WARNING("CanvasLayerOGL::Initialize called without surface or GL context!");
     return;
   }
 
   mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
-      
+
   // Check the maximum texture size supported by GL. glTexImage2D supports
   // images of up to 2 + GL_MAX_TEXTURE_SIZE
   GLint texSize = gl()->GetMaxTextureSize();
   if (mBounds.width > (2 + texSize) || mBounds.height > (2 + texSize)) {
     mDelayedUpdates = true;
     MakeTextureIfNeeded(gl(), mTexture);
     // This should only ever occur with 2d canvas, WebGL can't already have a texture
     // of this size can it?
-    NS_ABORT_IF_FALSE(mCanvasSurface || mDrawTarget, 
+    NS_ABORT_IF_FALSE(mCanvasSurface || mDrawTarget,
                       "Invalid texture size when WebGL surface already exists at that size?");
   }
 }
 
 /**
  * Following UpdateSurface(), mTexture on context this->gl() should contain the data we want,
  * unless mDelayedUpdates is true because of a too-large surface.
  */
@@ -286,17 +286,17 @@ IsValidSharedTexDescriptor(const Surface
 ShadowCanvasLayerOGL::ShadowCanvasLayerOGL(LayerManagerOGL* aManager)
   : ShadowCanvasLayer(aManager, nsnull)
   , LayerOGL(aManager)
   , mNeedsYFlip(false)
   , mTexture(0)
 {
   mImplData = static_cast<LayerOGL*>(this);
 }
- 
+
 ShadowCanvasLayerOGL::~ShadowCanvasLayerOGL()
 {}
 
 void
 ShadowCanvasLayerOGL::Initialize(const Data& aData)
 {
   NS_RUNTIMEABORT("Incompatibe surface type");
 }
--- a/gfx/src/nsRect.h
+++ b/gfx/src/nsRect.h
@@ -194,16 +194,22 @@ struct NS_GFX nsRect :
   // Note: this can turn an empty rectangle into a non-empty rectangle
   inline nsIntRect ScaleToOutsidePixels(float aXScale, float aYScale,
                                         nscoord aAppUnitsPerPixel) const;
   // Note: this can turn an empty rectangle into a non-empty rectangle
   inline nsIntRect ToOutsidePixels(nscoord aAppUnitsPerPixel) const;
   inline nsIntRect ScaleToInsidePixels(float aXScale, float aYScale,
                                        nscoord aAppUnitsPerPixel) const;
   inline nsIntRect ToInsidePixels(nscoord aAppUnitsPerPixel) const;
+
+  // This is here only to keep IPDL-generated code happy. DO NOT USE.
+  bool operator==(const nsRect& aRect) const
+  {
+    return IsEqualEdges(aRect);
+  }
 };
 
 struct NS_GFX nsIntRect :
   public mozilla::gfx::BaseRect<PRInt32, nsIntRect, nsIntPoint, nsIntSize, nsIntMargin> {
   typedef mozilla::gfx::BaseRect<PRInt32, nsIntRect, nsIntPoint, nsIntSize, nsIntMargin> Super;
 
   // Constructors
   nsIntRect() : Super()
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -4,16 +4,17 @@
  * 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/. */
 
 #ifndef __IPC_GLUE_IPCMESSAGEUTILS_H__
 #define __IPC_GLUE_IPCMESSAGEUTILS_H__
 
 #include "chrome/common/ipc_message_utils.h"
 
+#include "mozilla/TimeStamp.h"
 #include "mozilla/Util.h"
 #include "mozilla/gfx/2D.h"
 
 #include "prtypes.h"
 #include "nsID.h"
 #include "nsMemory.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
@@ -434,21 +435,38 @@ struct ParamTraits<gfxPoint>
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.x);
     WriteParam(aMsg, aParam.y);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
-    if (ReadParam(aMsg, aIter, &aResult->x) &&
-        ReadParam(aMsg, aIter, &aResult->y))
-      return true;
+    return (ReadParam(aMsg, aIter, &aResult->x) &&
+            ReadParam(aMsg, aIter, &aResult->y));
+ }
+};
+
+template<>
+struct ParamTraits<gfxPoint3D>
+{
+  typedef gfxPoint3D paramType;
 
-    return false;
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.x);
+    WriteParam(aMsg, aParam.y);
+    WriteParam(aMsg, aParam.z);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    return (ReadParam(aMsg, aIter, &aResult->x) &&
+            ReadParam(aMsg, aIter, &aResult->y) &&
+            ReadParam(aMsg, aIter, &aResult->z));
   }
 };
 
 template<>
 struct ParamTraits<gfxSize>
 {
   typedef gfxSize paramType;
 
@@ -573,20 +591,38 @@ struct ParamTraits<mozilla::null_t>
   Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     *aResult = paramType();
     return true;
   }
 };
 
 template<>
+struct ParamTraits<nsPoint>
+{
+  typedef nsPoint paramType;
+
+  static void Write(Message* msg, const paramType& param)
+  {
+    WriteParam(msg, param.x);
+    WriteParam(msg, param.y);
+  }
+
+  static bool Read(const Message* msg, void** iter, paramType* result)
+  {
+    return (ReadParam(msg, iter, &result->x) &&
+            ReadParam(msg, iter, &result->y));
+  }
+};
+
+template<>
 struct ParamTraits<nsIntPoint>
 {
   typedef nsIntPoint paramType;
-  
+
   static void Write(Message* msg, const paramType& param)
   {
     WriteParam(msg, param.x);
     WriteParam(msg, param.y);
   }
 
   static bool Read(const Message* msg, void** iter, paramType* result)
   {
@@ -594,17 +630,17 @@ struct ParamTraits<nsIntPoint>
             ReadParam(msg, iter, &result->y));
   }
 };
 
 template<>
 struct ParamTraits<nsIntRect>
 {
   typedef nsIntRect paramType;
-  
+
   static void Write(Message* msg, const paramType& param)
   {
     WriteParam(msg, param.x);
     WriteParam(msg, param.y);
     WriteParam(msg, param.width);
     WriteParam(msg, param.height);
   }
 
@@ -643,39 +679,39 @@ struct ParamTraits<nsIntRegion>
     return false;
   }
 };
 
 template<>
 struct ParamTraits<nsIntSize>
 {
   typedef nsIntSize paramType;
-  
+
   static void Write(Message* msg, const paramType& param)
   {
     WriteParam(msg, param.width);
-    WriteParam(msg, param.height); 
+    WriteParam(msg, param.height);
   }
 
   static bool Read(const Message* msg, void** iter, paramType* result)
   {
     return (ReadParam(msg, iter, &result->width) &&
             ReadParam(msg, iter, &result->height));
   }
 };
 
 template<>
 struct ParamTraits<mozilla::gfx::Size>
 {
   typedef mozilla::gfx::Size paramType;
-  
+
   static void Write(Message* msg, const paramType& param)
   {
     WriteParam(msg, param.width);
-    WriteParam(msg, param.height); 
+    WriteParam(msg, param.height);
   }
 
   static bool Read(const Message* msg, void** iter, paramType* result)
   {
     return (ReadParam(msg, iter, &result->width) &&
             ReadParam(msg, iter, &result->height));
   }
 };
@@ -701,17 +737,17 @@ struct ParamTraits<mozilla::gfx::Rect>
             ReadParam(msg, iter, &result->height));
   }
 };
 
 template<>
 struct ParamTraits<nsRect>
 {
   typedef nsRect paramType;
-  
+
   static void Write(Message* msg, const paramType& param)
   {
     WriteParam(msg, param.x);
     WriteParam(msg, param.y);
     WriteParam(msg, param.width);
     WriteParam(msg, param.height);
   }
 
@@ -761,11 +797,39 @@ struct ParamTraits<nsID>
                               aParam.m1,
                               aParam.m2));
     for (unsigned int i = 0; i < mozilla::ArrayLength(aParam.m3); i++)
       aLog->append(StringPrintf(L"%2.2X", aParam.m3[i]));
     aLog->append(L"}");
   }
 };
 
+template<>
+struct ParamTraits<mozilla::TimeDuration>
+{
+  typedef mozilla::TimeDuration paramType;
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mValue);
+  }
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->mValue);
+  };
+};
+
+template<>
+struct ParamTraits<mozilla::TimeStamp>
+{
+  typedef mozilla::TimeStamp paramType;
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mValue);
+  }
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->mValue);
+  };
+};
+
 } /* namespace IPC */
 
 #endif /* __IPC_GLUE_IPCMESSAGEUTILS_H__ */
--- a/xpcom/ds/TimeStamp.h
+++ b/xpcom/ds/TimeStamp.h
@@ -8,16 +8,20 @@
 #define mozilla_TimeStamp_h
 
 #include "mozilla/Assertions.h"
 
 #include "prinrval.h"
 #include "nsDebug.h"
 #include "prlong.h"
 
+namespace IPC {
+template <typename T> struct ParamTraits;
+}
+
 namespace mozilla {
 
 class TimeStamp;
 
 /**
  * Instances of this class represent the length of an interval of time.
  * Negative durations are allowed, meaning the end is before the start.
  * 
@@ -90,32 +94,36 @@ public:
     return mValue <= aOther.mValue;
   }
   bool operator>=(const TimeDuration& aOther) const {
     return mValue >= aOther.mValue;
   }
   bool operator>(const TimeDuration& aOther) const {
     return mValue > aOther.mValue;
   }
+  bool operator==(const TimeDuration& aOther) const {
+    return mValue == aOther.mValue;
+  }
 
   // Return a best guess at the system's current timing resolution,
   // which might be variable.  TimeDurations below this order of
   // magnitude are meaningless, and those at the same order of
   // magnitude or just above are suspect.
   static TimeDuration Resolution();
 
   // We could define additional operators here:
   // -- convert to/from other time units
   // -- scale duration by a float
   // but let's do that on demand.
   // Comparing durations for equality will only lead to bugs on
   // platforms with high-resolution timers.
 
 private:
   friend class TimeStamp;
+  friend struct IPC::ParamTraits<mozilla::TimeDuration>;
 
   static TimeDuration FromTicks(PRInt64 aTicks) {
     TimeDuration t;
     t.mValue = aTicks;
     return t;
   }
 
   static TimeDuration FromTicks(double aTicks) {
@@ -260,16 +268,18 @@ public:
   // Comparing TimeStamps for equality should be discouraged. Adding
   // two TimeStamps, or scaling TimeStamps, is nonsense and must never
   // be allowed.
 
   static NS_HIDDEN_(nsresult) Startup();
   static NS_HIDDEN_(void) Shutdown();
 
 private:
+  friend struct IPC::ParamTraits<mozilla::TimeStamp>;
+
   TimeStamp(PRUint64 aValue) : mValue(aValue) {}
 
   /**
    * When built with PRIntervalTime, a value of 0 means this instance
    * is "null". Otherwise, the low 32 bits represent a PRIntervalTime,
    * and the high 32 bits represent a counter of the number of
    * rollovers of PRIntervalTime that we've seen. This counter starts
    * at 1 to avoid a real time colliding with the "null" value.