Bug 602759 part 5 - Refactor nsSVGTransform into SVGTransform and DOMSVGTransform; r=jwatt
authorBrian Birtles <birtles@gmail.com>
Sun, 25 Sep 2011 22:04:27 +0100
changeset 77549 85e56f66704c1fbc64b4eeae28f1481cf9aa5ee4
parent 77548 e2afd7f5962d9db90a3918653137b971ef6e58e9
child 77550 caa562226631ef6d3cb0abf7398743730451a2a0
push idunknown
push userunknown
push dateunknown
reviewersjwatt
bugs602759
milestone9.0a1
Bug 602759 part 5 - Refactor nsSVGTransform into SVGTransform and DOMSVGTransform; r=jwatt
content/svg/content/src/DOMSVGTransform.cpp
content/svg/content/src/DOMSVGTransform.h
content/svg/content/src/Makefile.in
content/svg/content/src/SVGMotionSMILType.cpp
content/svg/content/src/SVGTransform.cpp
content/svg/content/src/SVGTransform.h
content/svg/content/src/nsSVGTransform.cpp
content/svg/content/src/nsSVGTransform.h
rename from content/svg/content/src/nsSVGTransform.cpp
rename to content/svg/content/src/DOMSVGTransform.cpp
--- a/content/svg/content/src/nsSVGTransform.cpp
+++ b/content/svg/content/src/DOMSVGTransform.cpp
@@ -1,10 +1,11 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
  * Software distributed under the License is distributed on an "AS IS" basis,
@@ -16,397 +17,340 @@
  *
  * The Initial Developer of the Original Code is
  * Crocodile Clips Ltd..
  * Portions created by the Initial Developer are Copyright (C) 2001
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
+ *   Brian Birtles <birtles@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "nsSVGTransform.h"
-#include "prdtoa.h"
-#include "nsSVGMatrix.h"
-#include "nsISVGValueUtils.h"
-#include "nsWeakReference.h"
-#include "nsSVGMatrix.h"
-#include "nsTextFormatter.h"
+#include "DOMSVGTransform.h"
+#include "DOMSVGMatrix.h"
+#include "SVGAnimatedTransformList.h"
+#include "nsDOMError.h"
+#include <math.h>
 #include "nsContentUtils.h"
-#include "nsDOMError.h"
-
-const double radPerDegree = 2.0*3.1415926535 / 360.0;
-
-//----------------------------------------------------------------------
-// Implementation
 
-nsresult
-nsSVGTransform::Create(nsIDOMSVGTransform** aResult)
-{
-  nsSVGTransform *pl = new nsSVGTransform();
-  NS_ENSURE_TRUE(pl, NS_ERROR_OUT_OF_MEMORY);
-  NS_ADDREF(pl);
-  if (NS_FAILED(pl->Init())) {
-    NS_RELEASE(pl);
-    *aResult = nsnull;
-    return NS_ERROR_FAILURE;
-  }
-  *aResult = pl;
-  return NS_OK;
-}
-
-
-nsSVGTransform::nsSVGTransform()
-    : mAngle(0.0f),
-      mOriginX(0.0f),
-      mOriginY(0.0f),
-      mType( SVG_TRANSFORM_MATRIX )
-{
-}
-
-nsSVGTransform::~nsSVGTransform()
-{
-  NS_REMOVE_SVGVALUE_OBSERVER(mMatrix);
-}
-
-nsresult nsSVGTransform::Init()
-{
-  nsresult rv = NS_NewSVGMatrix(getter_AddRefs(mMatrix));
-  NS_ADD_SVGVALUE_OBSERVER(mMatrix);
-  return rv;
-}
+namespace mozilla {
 
 //----------------------------------------------------------------------
 // nsISupports methods:
 
-NS_IMPL_ADDREF(nsSVGTransform)
-NS_IMPL_RELEASE(nsSVGTransform)
-
-DOMCI_DATA(SVGTransform, nsSVGTransform)
+// We could use NS_IMPL_CYCLE_COLLECTION_1, except that in Unlink() we need to
+// clear our list's weak ref to us to be safe. (The other option would be to
+// not unlink and rely on the breaking of the other edges in the cycle, as
+// NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
+NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGTransform)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGTransform)
+  // We may not belong to a list, so we must null check tmp->mList.
+  if (tmp->mList) {
+    tmp->mList->mItems[tmp->mListIndex] = nsnull;
+  }
+NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mList)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGTransform)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mList)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_INTERFACE_MAP_BEGIN(nsSVGTransform)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValue)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGTransform)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGTransform)
+
+} // namespace mozilla
+DOMCI_DATA(SVGTransform, mozilla::DOMSVGTransform)
+namespace mozilla {
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGTransform)
+  NS_INTERFACE_MAP_ENTRY(mozilla::DOMSVGTransform)
   NS_INTERFACE_MAP_ENTRY(nsIDOMSVGTransform)
-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMSVGTransform)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGTransform)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVGValue)
 NS_INTERFACE_MAP_END
 
 
 //----------------------------------------------------------------------
-// nsISVGValue methods:
+// Ctors:
 
-NS_IMETHODIMP
-nsSVGTransform::SetValueString(const nsAString& aValue)
+DOMSVGTransform::DOMSVGTransform(DOMSVGTransformList *aList,
+                                 PRUint32 aListIndex,
+                                 PRBool aIsAnimValItem)
+  : mList(aList)
+  , mListIndex(aListIndex)
+  , mIsAnimValItem(aIsAnimValItem)
+  , mTransform(nsnull)
+  , mMatrixTearoff(nsnull)
 {
-  NS_NOTYETIMPLEMENTED("nsSVGTransform::SetValueString");
-  return NS_ERROR_NOT_IMPLEMENTED;
+  // These shifts are in sync with the members in the header.
+  NS_ABORT_IF_FALSE(aList &&
+                    aListIndex <= MaxListIndex() &&
+                    aIsAnimValItem < (1 << 1), "bad arg");
+
+  NS_ABORT_IF_FALSE(IndexIsValid(), "Bad index for DOMSVGNumber!");
 }
 
-NS_IMETHODIMP
-nsSVGTransform::GetValueString(nsAString& aValue)
+DOMSVGTransform::DOMSVGTransform()
+  : mList(nsnull)
+  , mListIndex(0)
+  , mIsAnimValItem(PR_FALSE)
+  , mTransform(new SVGTransform()) // Default ctor for objects not in a list
+                                   // initialises to matrix type with identity
+                                   // matrix
+  , mMatrixTearoff(nsnull)
 {
-  PRUnichar buf[256];
-  
-  switch (mType) {
-    case nsIDOMSVGTransform::SVG_TRANSFORM_TRANSLATE:
-      {
-        float dx, dy;
-        mMatrix->GetE(&dx);
-        mMatrix->GetF(&dy);
-        if (dy != 0.0f)
-          nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar), NS_LITERAL_STRING("translate(%g, %g)").get(), dx, dy);
-        else
-          nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar), NS_LITERAL_STRING("translate(%g)").get(), dx);
-      }
-      break;
-    case nsIDOMSVGTransform::SVG_TRANSFORM_ROTATE:
-      {
-        if (mOriginX != 0.0f || mOriginY != 0.0f)
-          nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
-                                    NS_LITERAL_STRING("rotate(%g, %g, %g)").get(),
-                                    mAngle, mOriginX, mOriginY);
-        else
-          nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
-                                    NS_LITERAL_STRING("rotate(%g)").get(), mAngle);
-      }
-      break;        
-    case nsIDOMSVGTransform::SVG_TRANSFORM_SCALE:
-      {
-        float sx, sy;
-        mMatrix->GetA(&sx);
-        mMatrix->GetD(&sy);
-        if (sy != sx)
-          nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
-                                    NS_LITERAL_STRING("scale(%g, %g)").get(), sx, sy);
-        else
-          nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
-                                    NS_LITERAL_STRING("scale(%g)").get(), sx);
-      }
-      break;
-    case nsIDOMSVGTransform::SVG_TRANSFORM_SKEWX:
-      {
-        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
-                                  NS_LITERAL_STRING("skewX(%g)").get(), mAngle);
-      }
-      break;
-    case nsIDOMSVGTransform::SVG_TRANSFORM_SKEWY:
-      {
-        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
-                                  NS_LITERAL_STRING("skewY(%g)").get(), mAngle);
-      }
-      break;
-    case nsIDOMSVGTransform::SVG_TRANSFORM_MATRIX:
-      {
-        float a,b,c,d,e,f;
-        mMatrix->GetA(&a);
-        mMatrix->GetB(&b);
-        mMatrix->GetC(&c);
-        mMatrix->GetD(&d);
-        mMatrix->GetE(&e);
-        mMatrix->GetF(&f);
-        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
-                                  NS_LITERAL_STRING("matrix(%g, %g, %g, %g, %g, %g)").get(),
-                                  a, b, c, d, e, f);
-      }
-      break;
-    default:
-      buf[0] = '\0';
-      NS_ERROR("unknown transformation type");
-      break;
-  }
-
-  aValue.Assign(buf);
-  
-  return NS_OK;
 }
 
-
-//----------------------------------------------------------------------
-// nsISVGValueObserver methods:
-
-NS_IMETHODIMP nsSVGTransform::WillModifySVGObservable(nsISVGValue* observable,
-                                                      modificationType aModType)
+DOMSVGTransform::DOMSVGTransform(const gfxMatrix &aMatrix)
+  : mList(nsnull)
+  , mListIndex(0)
+  , mIsAnimValItem(PR_FALSE)
+  , mTransform(new SVGTransform(aMatrix))
+  , mMatrixTearoff(nsnull)
 {
-  WillModify();
-  return NS_OK;
 }
 
-NS_IMETHODIMP nsSVGTransform::DidModifySVGObservable (nsISVGValue* observable,
-                                                      modificationType aModType)
+DOMSVGTransform::DOMSVGTransform(const SVGTransform &aTransform)
+  : mList(nsnull)
+  , mListIndex(0)
+  , mIsAnimValItem(PR_FALSE)
+  , mTransform(new SVGTransform(aTransform))
+  , mMatrixTearoff(nsnull)
 {
-  // we become a general matrix transform if mMatrix changes
-  mType = SVG_TRANSFORM_MATRIX;
-  mAngle = 0.0f;
-  DidModify();
-  return NS_OK;
 }
 
 
 //----------------------------------------------------------------------
 // nsIDOMSVGTransform methods:
 
 /* readonly attribute unsigned short type; */
-NS_IMETHODIMP nsSVGTransform::GetType(PRUint16 *aType)
+NS_IMETHODIMP
+DOMSVGTransform::GetType(PRUint16 *aType)
 {
-  *aType = mType;
+  *aType = Transform().Type();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGMatrix matrix; */
-NS_IMETHODIMP nsSVGTransform::GetMatrix(nsIDOMSVGMatrix * *aMatrix)
+NS_IMETHODIMP
+DOMSVGTransform::GetMatrix(nsIDOMSVGMatrix * *aMatrix)
 {
-  *aMatrix = mMatrix;
-  NS_IF_ADDREF(*aMatrix);
+  if (!mMatrixTearoff) {
+    mMatrixTearoff = new DOMSVGMatrix(*this);
+  }
+
+  NS_ADDREF(*aMatrix = mMatrixTearoff);
   return NS_OK;
 }
 
 /* readonly attribute float angle; */
-NS_IMETHODIMP nsSVGTransform::GetAngle(float *aAngle)
+NS_IMETHODIMP
+DOMSVGTransform::GetAngle(float *aAngle)
 {
-  *aAngle = mAngle;
+  *aAngle = Transform().Angle();
   return NS_OK;
 }
 
 /* void setMatrix (in nsIDOMSVGMatrix matrix); */
-NS_IMETHODIMP nsSVGTransform::SetMatrix(nsIDOMSVGMatrix *matrix)
+NS_IMETHODIMP
+DOMSVGTransform::SetMatrix(nsIDOMSVGMatrix *matrix)
 {
-  float a, b, c, d, e, f;
+  if (mIsAnimValItem)
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
 
-  if (!matrix)
+  nsCOMPtr<DOMSVGMatrix> domMatrix = do_QueryInterface(matrix);
+  if (!domMatrix)
     return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
 
-  WillModify();
+  Transform().SetMatrix(domMatrix->Matrix());
+  NotifyElementOfChange();
 
-  mType = SVG_TRANSFORM_MATRIX;
-  mAngle = 0.0f;
-  
-  matrix->GetA(&a);
-  matrix->GetB(&b);
-  matrix->GetC(&c);
-  matrix->GetD(&d);
-  matrix->GetE(&e);
-  matrix->GetF(&f);
-
-  NS_REMOVE_SVGVALUE_OBSERVER(mMatrix);
-  mMatrix->SetA(a);
-  mMatrix->SetB(b);
-  mMatrix->SetC(c);
-  mMatrix->SetD(d);
-  mMatrix->SetE(e);
-  mMatrix->SetF(f);
-  NS_ADD_SVGVALUE_OBSERVER(mMatrix);
-
-  DidModify();
   return NS_OK;
 }
 
 /* void setTranslate (in float tx, in float ty); */
-NS_IMETHODIMP nsSVGTransform::SetTranslate(float tx, float ty)
+NS_IMETHODIMP
+DOMSVGTransform::SetTranslate(float tx, float ty)
 {
+  if (mIsAnimValItem) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
   NS_ENSURE_FINITE2(tx, ty, NS_ERROR_ILLEGAL_VALUE);
 
-  WillModify();
-  
-  mType = SVG_TRANSFORM_TRANSLATE;
-  mAngle = 0.0f;
-  NS_REMOVE_SVGVALUE_OBSERVER(mMatrix);
-  mMatrix->SetA(1.0f);
-  mMatrix->SetB(0.0f);
-  mMatrix->SetC(0.0f);
-  mMatrix->SetD(1.0f);
-  mMatrix->SetE(tx);
-  mMatrix->SetF(ty);
-  NS_ADD_SVGVALUE_OBSERVER(mMatrix);
+  Transform().SetTranslate(tx, ty);
+  NotifyElementOfChange();
 
-  DidModify();
   return NS_OK;
 }
 
 /* void setScale (in float sx, in float sy); */
-NS_IMETHODIMP nsSVGTransform::SetScale(float sx, float sy)
+NS_IMETHODIMP
+DOMSVGTransform::SetScale(float sx, float sy)
 {
+  if (mIsAnimValItem) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
   NS_ENSURE_FINITE2(sx, sy, NS_ERROR_ILLEGAL_VALUE);
 
-  WillModify();
-  
-  mType = SVG_TRANSFORM_SCALE;
-  mAngle = 0.0f;
-  NS_REMOVE_SVGVALUE_OBSERVER(mMatrix);
-  mMatrix->SetA(sx);
-  mMatrix->SetB(0.0f);
-  mMatrix->SetC(0.0f);
-  mMatrix->SetD(sy);
-  mMatrix->SetE(0.0f);
-  mMatrix->SetF(0.0f);
-  NS_ADD_SVGVALUE_OBSERVER(mMatrix);
+  Transform().SetScale(sx, sy);
+  NotifyElementOfChange();
 
-  DidModify();
   return NS_OK;
 }
 
 /* void setRotate (in float angle, in float cx, in float cy); */
-NS_IMETHODIMP nsSVGTransform::SetRotate(float angle, float cx, float cy)
+NS_IMETHODIMP
+DOMSVGTransform::SetRotate(float angle, float cx, float cy)
 {
+  if (mIsAnimValItem) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
   NS_ENSURE_FINITE3(angle, cx, cy, NS_ERROR_ILLEGAL_VALUE);
 
-  WillModify();
-  
-  mType = SVG_TRANSFORM_ROTATE;
-  mAngle = angle;
-  mOriginX = cx;
-  mOriginY = cy;
-
-  gfxMatrix matrix(1, 0, 0, 1, cx, cy);
-  matrix.Rotate(angle * radPerDegree);
-  matrix.Translate(gfxPoint(-cx, -cy));
+  Transform().SetRotate(angle, cx, cy);
+  NotifyElementOfChange();
 
-  NS_REMOVE_SVGVALUE_OBSERVER(mMatrix);
-  mMatrix->SetA(static_cast<float>(matrix.xx));
-  mMatrix->SetB(static_cast<float>(matrix.yx));
-  mMatrix->SetC(static_cast<float>(matrix.xy));
-  mMatrix->SetD(static_cast<float>(matrix.yy));
-  mMatrix->SetE(static_cast<float>(matrix.x0));
-  mMatrix->SetF(static_cast<float>(matrix.y0));
-  NS_ADD_SVGVALUE_OBSERVER(mMatrix);
-
-  DidModify();
   return NS_OK;
 }
 
 /* void setSkewX (in float angle); */
-NS_IMETHODIMP nsSVGTransform::SetSkewX(float angle)
+NS_IMETHODIMP
+DOMSVGTransform::SetSkewX(float angle)
 {
+  if (mIsAnimValItem) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
   NS_ENSURE_FINITE(angle, NS_ERROR_ILLEGAL_VALUE);
 
-  float ta = static_cast<float>(tan(angle * radPerDegree));
-
-  NS_ENSURE_FINITE(ta, NS_ERROR_DOM_SVG_INVALID_VALUE_ERR);
-
-  WillModify();
-  
-  mType = SVG_TRANSFORM_SKEWX;
-  mAngle = angle;
+  nsresult rv = Transform().SetSkewX(angle);
+  if (NS_FAILED(rv))
+    return rv;
+  NotifyElementOfChange();
 
-  NS_REMOVE_SVGVALUE_OBSERVER(mMatrix);
-  mMatrix->SetA(1.0f);
-  mMatrix->SetB(0.0f);
-  mMatrix->SetC(ta);
-  mMatrix->SetD(1.0f);
-  mMatrix->SetE(0.0f);
-  mMatrix->SetF(0.0f);
-  NS_ADD_SVGVALUE_OBSERVER(mMatrix);
-
-  DidModify();
   return NS_OK;
 }
 
 /* void setSkewY (in float angle); */
-NS_IMETHODIMP nsSVGTransform::SetSkewY(float angle)
+NS_IMETHODIMP
+DOMSVGTransform::SetSkewY(float angle)
 {
+  if (mIsAnimValItem) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
   NS_ENSURE_FINITE(angle, NS_ERROR_ILLEGAL_VALUE);
 
-  float ta = static_cast<float>(tan(angle * radPerDegree));
-
-  NS_ENSURE_FINITE(ta, NS_ERROR_DOM_SVG_INVALID_VALUE_ERR);
-
-  WillModify();
-  
-  mType = SVG_TRANSFORM_SKEWY;
-  mAngle = angle;
+  nsresult rv = Transform().SetSkewY(angle);
+  if (NS_FAILED(rv))
+    return rv;
+  NotifyElementOfChange();
 
-  NS_REMOVE_SVGVALUE_OBSERVER(mMatrix);
-  mMatrix->SetA(1.0f);
-  mMatrix->SetB(ta);
-  mMatrix->SetC(0.0f);
-  mMatrix->SetD(1.0f);
-  mMatrix->SetE(0.0f);
-  mMatrix->SetF(0.0f);
-  NS_ADD_SVGVALUE_OBSERVER(mMatrix);
-
-  DidModify();
   return NS_OK;
 }
 
 
+//----------------------------------------------------------------------
+// List management methods:
 
-////////////////////////////////////////////////////////////////////////
-// Exported creation functions:
+void
+DOMSVGTransform::InsertingIntoList(DOMSVGTransformList *aList,
+                                   PRUint32 aListIndex,
+                                   PRBool aIsAnimValItem)
+{
+  NS_ABORT_IF_FALSE(!HasOwner(), "Inserting item that is already in a list");
+
+  mList = aList;
+  mListIndex = aListIndex;
+  mIsAnimValItem = aIsAnimValItem;
+  mTransform = nsnull;
+
+  NS_ABORT_IF_FALSE(IndexIsValid(), "Bad index for DOMSVGLength!");
+}
+
+void
+DOMSVGTransform::RemovingFromList()
+{
+  NS_ABORT_IF_FALSE(!mTransform,
+      "Item in list also has another non-list value associated with it");
+
+  mTransform = new SVGTransform(InternalItem());
+  mList = nsnull;
+  mIsAnimValItem = PR_FALSE;
+}
+
+SVGTransform&
+DOMSVGTransform::InternalItem()
+{
+  SVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
+  return mIsAnimValItem && alist->mAnimVal ?
+    (*alist->mAnimVal)[mListIndex] :
+    alist->mBaseVal[mListIndex];
+}
+
+const SVGTransform&
+DOMSVGTransform::InternalItem() const
+{
+  return const_cast<DOMSVGTransform *>(this)->InternalItem();
+}
 
-nsresult
-NS_NewSVGTransform(nsIDOMSVGTransform** result)
+#ifdef DEBUG
+PRBool
+DOMSVGTransform::IndexIsValid()
+{
+  SVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
+  return (mIsAnimValItem &&
+          mListIndex < alist->GetAnimValue().Length()) ||
+         (!mIsAnimValItem &&
+          mListIndex < alist->GetBaseValue().Length());
+}
+#endif // DEBUG
+
+
+//----------------------------------------------------------------------
+// Interface for DOMSVGMatrix's use
+
+void
+DOMSVGTransform::SetMatrix(const gfxMatrix& aMatrix)
 {
-  return nsSVGTransform::Create(result);
+  NS_ABORT_IF_FALSE(!mIsAnimValItem,
+      "Attempting to modify read-only transform");
+  Transform().SetMatrix(aMatrix);
+  NotifyElementOfChange();
+}
+
+void
+DOMSVGTransform::ClearMatrixTearoff(DOMSVGMatrix* aMatrix)
+{
+  NS_ABORT_IF_FALSE(mMatrixTearoff == aMatrix,
+      "Unexpected matrix pointer to be cleared");
+  mMatrixTearoff = nsnull;
 }
+
+
+//----------------------------------------------------------------------
+// Implementation helpers
+
+void
+DOMSVGTransform::NotifyElementOfChange()
+{
+  if (HasOwner()) {
+    Element()->DidChangeTransformList(PR_TRUE);
+#ifdef MOZ_SMIL
+    if (mList->mAList->IsAnimating()) {
+      Element()->AnimationNeedsResample();
+    }
+#endif // MOZ_SMIL
+  }
+}
+
+} // namespace mozilla
rename from content/svg/content/src/nsSVGTransform.h
rename to content/svg/content/src/DOMSVGTransform.h
--- a/content/svg/content/src/nsSVGTransform.h
+++ b/content/svg/content/src/DOMSVGTransform.h
@@ -1,10 +1,11 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
  * Software distributed under the License is distributed on an "AS IS" basis,
@@ -16,85 +17,219 @@
  *
  * The Initial Developer of the Original Code is
  * Crocodile Clips Ltd..
  * Portions created by the Initial Developer are Copyright (C) 2001
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
+ *   Brian Birtles <birtles@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#ifndef __NS_SVGTRANSFORM_H__
-#define __NS_SVGTRANSFORM_H__
+#ifndef MOZILLA_DOMSVGTRANSFORM_H__
+#define MOZILLA_DOMSVGTRANSFORM_H__
 
 #include "nsIDOMSVGTransform.h"
-#include "nsSVGValue.h"
-#include "nsISVGValueObserver.h"
+#include "DOMSVGTransformList.h"
+#include "SVGTransform.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsAutoPtr.h"
 
-////////////////////////////////////////////////////////////////////////
-// nsSVGTransform
+// We make DOMSVGTransform a pseudo-interface to allow us to QI to it in order
+// to check that the objects that scripts pass in are our our *native* transform
+// objects.
+//
+// {0A799862-9469-41FE-B4CD-2019E65D8DA6}
+#define MOZILLA_DOMSVGTRANSFORM_IID \
+  { 0x0A799862, 0x9469, 0x41FE, \
+    { 0xB4, 0xCD, 0x20, 0x19, 0xE6, 0x5D, 0x8D, 0xA6 } }
 
-class nsSVGTransform : public nsIDOMSVGTransform,
-                       public nsSVGValue,
-                       public nsISVGValueObserver
+#define MOZ_SVG_LIST_INDEX_BIT_COUNT 22 // supports > 4 million list items
+
+namespace mozilla {
+
+class DOMSVGMatrix;
+
+/**
+ * DOM wrapper for an SVG transform. See DOMSVGLength.h.
+ */
+class DOMSVGTransform : public nsIDOMSVGTransform
 {
 public:
-  static nsresult Create(nsIDOMSVGTransform** aResult);
-  
-protected:
-  nsSVGTransform();
-  ~nsSVGTransform();
-  nsresult Init();
-public:
-  // nsISupports interface:
-  NS_DECL_ISUPPORTS
-
-  // nsIDOMSVGTransform interface:
+  NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOMSVGTRANSFORM_IID)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGTransform)
   NS_DECL_NSIDOMSVGTRANSFORM
 
-  // nsISVGValue interface:
-  NS_IMETHOD SetValueString(const nsAString& aValue);
-  NS_IMETHOD GetValueString(nsAString& aValue);
+  /**
+   * Generic ctor for DOMSVGTransform objects that are created for an attribute.
+   */
+  DOMSVGTransform(DOMSVGTransformList *aList,
+                  PRUint32 aListIndex,
+                  PRBool aIsAnimValItem);
+
+  /**
+   * Ctors for creating the objects returned by:
+   *   SVGSVGElement.createSVGTransform(),
+   *   SVGSVGElement.createSVGTransformFromMatrix(in SVGMatrix matrix),
+   *   SVGTransformList.createSVGTransformFromMatrix(in SVGMatrix matrix)
+   * which do not initially belong to an attribute.
+   */
+  DOMSVGTransform();
+  DOMSVGTransform(const gfxMatrix &aMatrix);
+
+  /**
+   * Ctor for creating an unowned copy. Used with Clone().
+   */
+  DOMSVGTransform(const SVGTransform &aMatrix);
+
+  ~DOMSVGTransform() {
+    // Our matrix tear-off pointer should be cleared before we are destroyed
+    // (since matrix tear-offs keep an owning reference to their transform, and
+    // clear the tear-off pointer themselves if unlinked).
+    NS_ABORT_IF_FALSE(!mMatrixTearoff, "Matrix tear-off pointer not cleared."
+        " Transform being destroyed before matrix?");
+    // Our mList's weak ref to us must be nulled out when we die. If GC has
+    // unlinked us using the cycle collector code, then that has already
+    // happened, and mList is null.
+    if (mList) {
+      mList->mItems[mListIndex] = nsnull;
+    }
+  };
+
+  /**
+   * Create an unowned copy of an owned transform. The caller is responsible for
+   * the first AddRef().
+   */
+  DOMSVGTransform* Clone() {
+    NS_ASSERTION(mList, "unexpected caller");
+    return new DOMSVGTransform(InternalItem());
+  }
 
-  // nsISVGValueObserver
-  NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
-                                     modificationType aModType);
-  NS_IMETHOD DidModifySVGObservable (nsISVGValue* observable,
-                                     modificationType aModType);
+  PRBool IsInList() const {
+    return !!mList;
+  }
+
+  /**
+   * In future, if this class is used for non-list transforms, this will be
+   * different to IsInList().
+   */
+  PRBool HasOwner() const {
+    return !!mList;
+  }
 
-#ifdef MOZ_SMIL
-  // Additional methods needed for animation
-  void GetRotationOrigin(float& aOriginX, float& aOriginY) const
-  {
-    aOriginX = mOriginX;
-    aOriginY = mOriginY;
+  /**
+   * This method is called to notify this DOM object that it is being inserted
+   * into a list, and give it the information it needs as a result.
+   *
+   * This object MUST NOT already belong to a list when this method is called.
+   * That's not to say that script can't move these DOM objects between
+   * lists - it can - it's just that the logic to handle that (and send out
+   * the necessary notifications) is located elsewhere (in
+   * DOMSVGTransformList).)
+   */
+  void InsertingIntoList(DOMSVGTransformList *aList,
+                         PRUint32 aListIndex,
+                         PRBool aIsAnimValItem);
+
+  static PRUint32 MaxListIndex() {
+    return (1U << MOZ_SVG_LIST_INDEX_BIT_COUNT) - 1;
   }
-#endif // MOZ_SMIL
+
+  /// This method is called to notify this object that its list index changed.
+  void UpdateListIndex(PRUint32 aListIndex) {
+    mListIndex = aListIndex;
+  }
+
+  /**
+   * This method is called to notify this DOM object that it is about to be
+   * removed from its current DOM list so that it can first make a copy of its
+   * internal counterpart's values. (If it didn't do this, then it would
+   * "lose" its value on being removed.)
+   */
+  void RemovingFromList();
+
+  SVGTransform ToSVGTransform() const {
+    return Transform();
+  }
 
 protected:
-  nsCOMPtr<nsIDOMSVGMatrix> mMatrix;
-  float mAngle, mOriginX, mOriginY;
-  PRUint16 mType;
+  // Interface for DOMSVGMatrix's use
+  friend class DOMSVGMatrix;
+  const PRBool IsAnimVal() const {
+    return mIsAnimValItem;
+  }
+  const gfxMatrix& Matrix() const {
+    return Transform().Matrix();
+  }
+  void SetMatrix(const gfxMatrix& aMatrix);
+  void ClearMatrixTearoff(DOMSVGMatrix* aMatrix);
+
+private:
+  nsSVGElement* Element() {
+    return mList->Element();
+  }
+
+  /**
+   * Get a reference to the internal SVGTransform list item that this DOM
+   * wrapper object currently wraps.
+   */
+  SVGTransform& InternalItem();
+  const SVGTransform& InternalItem() const;
+
+#ifdef DEBUG
+  PRBool IndexIsValid();
+#endif
+
+  const SVGTransform& Transform() const {
+    return HasOwner() ? InternalItem() : *mTransform;
+  }
+  SVGTransform& Transform() {
+    return HasOwner() ? InternalItem() : *mTransform;
+  }
+  void NotifyElementOfChange();
+
+  nsRefPtr<DOMSVGTransformList> mList;
+
+  // Bounds for the following are checked in the ctor, so be sure to update
+  // that if you change the capacity of any of the following.
+
+  PRUint32 mListIndex:MOZ_SVG_LIST_INDEX_BIT_COUNT;
+  PRPackedBool mIsAnimValItem:1;
+
+  // Usually this class acts as a wrapper for an SVGTransform object which is
+  // part of a list and is accessed by going via the owning Element.
+  //
+  // However, in some circumstances, objects of this class may not be associated
+  // with any particular list and thus, no internal SVGTransform object. In
+  // that case we allocate an SVGTransform object on the heap to store the data.
+  nsAutoPtr<SVGTransform> mTransform;
+
+  // Weak ref to DOMSVGMatrix tearoff. The DOMSVGMatrix object will take of
+  // clearing this pointer when it is destroyed (by calling ClearMatrixTearoff).
+  //
+  // If this extra pointer member proves undesirable, it can be replaced with
+  // a hashmap (nsSVGAttrTearoffTable) to map from DOMSVGTransform to
+  // DOMSVGMatrix.
+  DOMSVGMatrix* mMatrixTearoff;
 };
 
-nsresult
-NS_NewSVGTransform(nsIDOMSVGTransform** result);
+NS_DEFINE_STATIC_IID_ACCESSOR(DOMSVGTransform, MOZILLA_DOMSVGTRANSFORM_IID)
+
+} // namespace mozilla
 
-// XXX we'll need this prototype-based stuff to support unsetting:
-//nsresult NS_NewSVGTransform(nsIDOMSVGTransform** result,
-//                            nsIDOMSVGTransform*  prototype);
+#undef MOZ_SVG_LIST_INDEX_BIT_COUNT
 
-
-#endif //__NS_SVGTRANSFORM_H__
+#endif // MOZILLA_DOMSVGTRANSFORM_H__
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -55,16 +55,17 @@ CPPSRCS		= \
 		DOMSVGLengthList.cpp \
 		DOMSVGMatrix.cpp \
 		DOMSVGNumber.cpp \
 		DOMSVGNumberList.cpp \
 		DOMSVGPathSeg.cpp \
 		DOMSVGPathSegList.cpp \
 		DOMSVGPoint.cpp \
 		DOMSVGPointList.cpp \
+		DOMSVGTransform.cpp \
 		nsDOMSVGZoomEvent.cpp \
 		nsDOMSVGEvent.cpp \
 		nsSVGAElement.cpp \
 		nsSVGAltGlyphElement.cpp \
 		nsSVGAngle.cpp \
 		nsSVGAnimatedTransformList.cpp \
 		nsSVGBoolean.cpp \
 		nsSVGCircleElement.cpp \
@@ -113,33 +114,33 @@ CPPSRCS		= \
 		nsSVGSwitchElement.cpp \
 		nsSVGSymbolElement.cpp \
 		nsSVGTSpanElement.cpp \
 		nsSVGTextContentElement.cpp \
 		nsSVGTextElement.cpp \
 		nsSVGTextPathElement.cpp \
 		nsSVGTextPositioningElement.cpp \
 		nsSVGTitleElement.cpp \
-		nsSVGTransform.cpp \
 		nsSVGTransformList.cpp \
 		nsSVGTransformListParser.cpp \
 		nsSVGUseElement.cpp \
 		nsSVGValue.cpp \
 		nsSVGViewBox.cpp \
 		SVGAnimatedLengthList.cpp \
 		SVGAnimatedNumberList.cpp \
 		SVGAnimatedPathSegList.cpp \
 		SVGAnimatedPointList.cpp \
 		SVGAnimatedPreserveAspectRatio.cpp \
 		SVGLength.cpp \
 		SVGLengthList.cpp \
 		SVGNumberList.cpp \
 		SVGPathData.cpp \
 		SVGPathSegUtils.cpp \
 		SVGPointList.cpp \
+		SVGTransform.cpp \
 		$(NULL)
 
 ifdef MOZ_SMIL
 CPPSRCS += nsSVGAnimateElement.cpp \
            nsSVGAnimateTransformElement.cpp \
            nsSVGAnimateMotionElement.cpp \
            nsSVGAnimationElement.cpp \
            nsSVGMpathElement.cpp \
--- a/content/svg/content/src/SVGMotionSMILType.cpp
+++ b/content/svg/content/src/SVGMotionSMILType.cpp
@@ -35,17 +35,16 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /* implementation of nsISMILType for use by <animateMotion> element */
 
 #include "SVGMotionSMILType.h"
 #include "nsSMILValue.h"
 #include "nsDebug.h"
-#include "nsSVGTransform.h"
 #include "nsSVGAngle.h"
 #include "nsIDOMSVGAngle.h"
 #include "nsSVGPathElement.h"
 #include "nsIDOMSVGPathSeg.h"
 #include "nsIDOMSVGPathSegList.h"
 #include "nsMathUtils.h"
 #include <math.h>
 
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGTransform.cpp
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Birtles <birtles@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "SVGTransform.h"
+#include "nsContentUtils.h"
+#include "nsTextFormatter.h"
+
+namespace {
+  const double radPerDegree = 2.0*3.1415926535 / 360.0;
+}
+
+namespace mozilla {
+
+void
+SVGTransform::GetValueAsString(nsAString& aValue) const
+{
+  PRUnichar buf[256];
+
+  switch (mType) {
+    case nsIDOMSVGTransform::SVG_TRANSFORM_TRANSLATE:
+      if (mMatrix.x0 != 0.0f)
+        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
+            NS_LITERAL_STRING("translate(%g, %g)").get(),
+            mMatrix.x0, mMatrix.y0);
+      else
+        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
+            NS_LITERAL_STRING("translate(%g)").get(),
+            mMatrix.x0);
+      break;
+    case nsIDOMSVGTransform::SVG_TRANSFORM_ROTATE:
+      if (mOriginX != 0.0f || mOriginY != 0.0f)
+        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
+            NS_LITERAL_STRING("rotate(%g, %g, %g)").get(),
+            mAngle, mOriginX, mOriginY);
+      else
+        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
+            NS_LITERAL_STRING("rotate(%g)").get(), mAngle);
+      break;
+    case nsIDOMSVGTransform::SVG_TRANSFORM_SCALE:
+      if (mMatrix.xx != mMatrix.yy)
+        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
+            NS_LITERAL_STRING("scale(%g, %g)").get(), mMatrix.xx, mMatrix.yy);
+      else
+        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
+            NS_LITERAL_STRING("scale(%g)").get(), mMatrix.xx);
+      break;
+    case nsIDOMSVGTransform::SVG_TRANSFORM_SKEWX:
+      nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
+                                NS_LITERAL_STRING("skewX(%g)").get(), mAngle);
+      break;
+    case nsIDOMSVGTransform::SVG_TRANSFORM_SKEWY:
+      nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
+                                NS_LITERAL_STRING("skewY(%g)").get(), mAngle);
+      break;
+    case nsIDOMSVGTransform::SVG_TRANSFORM_MATRIX:
+      nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
+          NS_LITERAL_STRING("matrix(%g, %g, %g, %g, %g, %g)").get(),
+                            mMatrix.xx, mMatrix.yx,
+                            mMatrix.xy, mMatrix.yy,
+                            mMatrix.x0, mMatrix.y0);
+      break;
+    default:
+      buf[0] = '\0';
+      NS_ERROR("unknown transformation type");
+      break;
+  }
+
+  aValue.Assign(buf);
+}
+
+void
+SVGTransform::SetMatrix(const gfxMatrix& aMatrix)
+{
+  mType    = nsIDOMSVGTransform::SVG_TRANSFORM_MATRIX;
+  mMatrix  = aMatrix;
+  // We set the other members here too, since operator== requires it and
+  // the DOM requires it for mAngle.
+  mAngle   = 0.f;
+  mOriginX = 0.f;
+  mOriginY = 0.f;
+}
+
+void
+SVGTransform::SetTranslate(float aTx, float aTy)
+{
+  mType    = nsIDOMSVGTransform::SVG_TRANSFORM_TRANSLATE;
+  mMatrix.Reset();
+  mMatrix.x0 = aTx;
+  mMatrix.y0 = aTy;
+  mAngle   = 0.f;
+  mOriginX = 0.f;
+  mOriginY = 0.f;
+}
+
+void
+SVGTransform::SetScale(float aSx, float aSy)
+{
+  mType    = nsIDOMSVGTransform::SVG_TRANSFORM_SCALE;
+  mMatrix.Reset();
+  mMatrix.xx = aSx;
+  mMatrix.yy = aSy;
+  mAngle   = 0.f;
+  mOriginX = 0.f;
+  mOriginY = 0.f;
+}
+
+void
+SVGTransform::SetRotate(float aAngle, float aCx, float aCy)
+{
+  mType    = nsIDOMSVGTransform::SVG_TRANSFORM_ROTATE;
+  mMatrix.Reset();
+  mMatrix.Translate(gfxPoint(aCx, aCy));
+  mMatrix.Rotate(aAngle*radPerDegree);
+  mMatrix.Translate(gfxPoint(-aCx, -aCy));
+  mAngle   = aAngle;
+  mOriginX = aCx;
+  mOriginY = aCy;
+}
+
+nsresult
+SVGTransform::SetSkewX(float aAngle)
+{
+  double ta = tan(aAngle*radPerDegree);
+  NS_ENSURE_FINITE(ta, NS_ERROR_DOM_SVG_INVALID_VALUE_ERR);
+
+  mType    = nsIDOMSVGTransform::SVG_TRANSFORM_SKEWX;
+  mMatrix.Reset();
+  mMatrix.xy = ta;
+  mAngle   = aAngle;
+  mOriginX = 0.f;
+  mOriginY = 0.f;
+  return NS_OK;
+}
+
+nsresult
+SVGTransform::SetSkewY(float aAngle)
+{
+  double ta = tan(aAngle*radPerDegree);
+  NS_ENSURE_FINITE(ta, NS_ERROR_DOM_SVG_INVALID_VALUE_ERR);
+
+  mType    = nsIDOMSVGTransform::SVG_TRANSFORM_SKEWY;
+  mMatrix.Reset();
+  mMatrix.yx = ta;
+  mAngle   = aAngle;
+  mOriginX = 0.f;
+  mOriginY = 0.f;
+  return NS_OK;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGTransform.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Birtles <birtles@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef MOZILLA_SVGTRANSFORM_H__
+#define MOZILLA_SVGTRANSFORM_H__
+
+#include "gfxMatrix.h"
+#include "nsIDOMSVGTransform.h"
+
+namespace mozilla {
+
+/*
+ * The DOM wrapper class for this class is DOMSVGTransformMatrix.
+ */
+class SVGTransform
+{
+public:
+  // Default ctor initialises to matrix type with identity matrix
+  SVGTransform()
+    : mMatrix() // Initialises to identity
+    , mAngle(0.f)
+    , mOriginX(0.f)
+    , mOriginY(0.f)
+    , mType(nsIDOMSVGTransform::SVG_TRANSFORM_MATRIX)
+  { }
+
+  SVGTransform(const gfxMatrix& aMatrix)
+    : mMatrix(aMatrix)
+    , mAngle(0.f)
+    , mOriginX(0.f)
+    , mOriginY(0.f)
+    , mType(nsIDOMSVGTransform::SVG_TRANSFORM_MATRIX)
+  { }
+
+  PRBool operator==(const SVGTransform& rhs) const {
+    return mType == rhs.mType &&
+      MatricesEqual(mMatrix, rhs.mMatrix) &&
+      mAngle == rhs.mAngle &&
+      mOriginX == rhs.mOriginX &&
+      mOriginY == rhs.mOriginY;
+  }
+
+  void GetValueAsString(nsAString& aValue) const;
+
+  float Angle() const {
+    return mAngle;
+  }
+  void GetRotationOrigin(float& aOriginX, float& aOriginY) const {
+    aOriginX = mOriginX;
+    aOriginY = mOriginY;
+  }
+  PRUint16 Type() const {
+    return mType;
+  }
+
+  const gfxMatrix& Matrix() const { return mMatrix; }
+  void SetMatrix(const gfxMatrix& aMatrix);
+  void SetTranslate(float aTx, float aTy);
+  void SetScale(float aSx, float aSy);
+  void SetRotate(float aAngle, float aCx, float aCy);
+  nsresult SetSkewX(float aAngle);
+  nsresult SetSkewY(float aAngle);
+
+protected:
+  static PRBool MatricesEqual(const gfxMatrix& a, const gfxMatrix& b)
+  {
+    return a.xx == b.xx &&
+           a.yx == b.yx &&
+           a.xy == b.xy &&
+           a.yy == b.yy &&
+           a.x0 == b.x0 &&
+           a.y0 == b.y0;
+  }
+
+  gfxMatrix mMatrix;
+  float mAngle, mOriginX, mOriginY;
+  PRUint16 mType;
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SVGTRANSFORM_H__