Merge last fully green rev of mozilla-inbound to m-c.
authorJoe Drew <joe@drew.ca>
Tue, 17 Jan 2012 16:55:54 -0500
changeset 84699 f4049f65efc6e367c54643ad7c66b3e81e24bac5
parent 84665 83fa8893f553a214684313a5fe7708de4a8b63b3 (current diff)
parent 84698 8b4aae361875a4bc96c48c9b85425433b3249d63 (diff)
child 84779 7538f4d4697c67c34ce17cb567a35133eb4cb1bc
push id21872
push userjdrew@mozilla.com
push dateTue, 17 Jan 2012 21:57:32 +0000
treeherdermozilla-central@f4049f65efc6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone12.0a1
first release with
nightly linux32
f4049f65efc6 / 12.0a1 / 20120118031059 / files
nightly linux64
f4049f65efc6 / 12.0a1 / 20120118031059 / files
nightly mac
f4049f65efc6 / 12.0a1 / 20120118031059 / files
nightly win32
f4049f65efc6 / 12.0a1 / 20120118031059 / files
nightly win64
f4049f65efc6 / 12.0a1 / 20120118031059 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge last fully green rev of mozilla-inbound to m-c.
gfx/2d/ScaledFontSkia.cpp
gfx/2d/ScaledFontSkia.h
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -518,19 +518,17 @@
 @BINPATH@/res/entityTables/*
 #ifdef XP_MACOSX
 @BINPATH@/res/MainMenu.nib/
 #endif
 
 ; svg
 @BINPATH@/res/svg.css
 @BINPATH@/components/dom_svg.xpt
-#ifdef MOZ_SMIL
 @BINPATH@/components/dom_smil.xpt
-#endif
 
 ; [Personal Security Manager]
 ;
 @BINPATH@/@DLL_PREFIX@nssckbi@DLL_SUFFIX@
 @BINPATH@/components/pipboot.xpt
 @BINPATH@/components/pipnss.xpt
 @BINPATH@/components/pippki.xpt
 @BINPATH@/@DLL_PREFIX@nss3@DLL_SUFFIX@
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -40,17 +40,17 @@ tabbrowser {
 }
 
 .tabbrowser-tab:not([pinned]):not([fadein]) {
   max-width: 0.1px;
   min-width: 0.1px;
   opacity: 0 !important;
   -moz-transition: min-width 200ms ease-out,
                    max-width 250ms ease-out,
-                   opacity 50ms ease-out 100ms /* hide the tab for the last 100ms of the max-width transition */;
+                   opacity 50ms ease-out 180ms /* hide the tab for the last 20ms of the max-width transition */;
 }
 
 .tab-throbber:not([fadein]):not([pinned]),
 .tab-label:not([fadein]):not([pinned]),
 .tab-icon-image:not([fadein]):not([pinned]),
 .tab-close-button:not([fadein]):not([pinned]) {
   display: none;
 }
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -1697,22 +1697,20 @@ toolbarbutton.chevron > .toolbarbutton-m
 .tab-throbber[progress] {
   list-style-image: url("chrome://browser/skin/tabbrowser/loading.png");
 }
 
 .tabbrowser-tab:not(:hover) > .tab-stack > .tab-content > .tab-icon-image:not([selected="true"]) {
   opacity: .8;
 }
 
-/* Prevent overlapping of tabs during the close animation */
-.tab-label:not([fadein]):not([pinned]) {
-  margin-left: -16px;
-  margin-right: -16px;
-  -moz-transition: opacity 100ms ease-out,
-                   margin 30ms ease-out 80ms;
+.tabbrowser-tab:not([pinned]):not([fadein]) {
+  -moz-transition: min-width 200ms ease-out /* copied from browser/base/content/browser.css */,
+                   max-width 250ms ease-out /* copied from browser/base/content/browser.css */,
+                   opacity 50ms ease-out 100ms /* hide the tab for the last 100ms of the max-width transition */;
 }
 
 .tab-stack {
   /* ensure stable tab height with and without toolbarbuttons on the tab bar */
   height: 26px;
 }
 
 .tabbrowser-tab,
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -1160,18 +1160,20 @@ nsCanvasRenderingContext2DAzure::Redraw(
 
   mIsEntireFrameInvalid = true;
 
   if (!mCanvasElement) {
     NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
     return NS_OK;
   }
 
-  if (mThebesSurface)
-      mThebesSurface->MarkDirty();
+  if (!mThebesSurface)
+    mThebesSurface =
+      gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
+  mThebesSurface->MarkDirty();
 
   nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
 
   HTMLCanvasElement()->InvalidateCanvasContent(nsnull);
 
   return NS_OK;
 }
 
@@ -1190,18 +1192,20 @@ nsCanvasRenderingContext2DAzure::Redraw(
     return;
   }
 
   if (!mCanvasElement) {
     NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
     return;
   }
 
-  if (mThebesSurface)
-      mThebesSurface->MarkDirty();
+  if (!mThebesSurface)
+    mThebesSurface =
+      gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
+  mThebesSurface->MarkDirty();
 
   nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
 
   gfxRect tmpR = ThebesRect(r);
   HTMLCanvasElement()->InvalidateCanvasContent(&tmpR);
 
   return;
 }
--- a/content/canvas/test/test_canvas.html
+++ b/content/canvas/test/test_canvas.html
@@ -13,16 +13,33 @@ function IsD2DEnabled() {
     try {
         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
         enabled = Components.classes["@mozilla.org/gfx/info;1"].getService(Components.interfaces.nsIGfxInfo).D2DEnabled;
     } catch(e) {}
     
     return enabled;
 }
 
+function IsMacOSX10_5orOlder() {
+    var is105orOlder = false;
+
+    if (navigator.platform.indexOf("Mac") == 0) {
+        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+        var version = Components.classes["@mozilla.org/system-info;1"]
+                            .getService(Components.interfaces.nsIPropertyBag2)
+                            .getProperty("version");
+        // the next line is correct: Mac OS 10.6 corresponds to Darwin version 10 !
+        // Mac OS 10.5 would be Darwin version 9. the |version| string we've got here
+        // is the Darwin version.
+        is105orOlder = (parseFloat(version) < 10.0);
+    }
+    return is105orOlder;
+}
+
+
 function IsAzureEnabled() {
   var enabled = false;
 
   try {
     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
     enabled = Components.classes["@mozilla.org/gfx/info;1"].getService(Components.interfaces.nsIGfxInfo).AzureEnabled;
   } catch (e) { }
 
@@ -5773,20 +5790,22 @@ isPixel(ctx, 80,25, 0,255,0,255, 2);
 <script>
 
 
 function test_2d_gradient_interpolate_overlap() {
 
 var canvas = document.getElementById('c215');
 var ctx = canvas.getContext('2d');
 
-if (!IsD2DEnabled()) {
-    // Only run this on non-D2D. On D2D the different nature of how gradients
+if (!IsD2DEnabled() && !IsMacOSX10_5orOlder()) {
+    // On D2D the different nature of how gradients
     // are drawn makes it so we cannot guarantee these stops are completely
     // hard.
+
+    // On OS X 10.5 quartz is confused by the overlapping stops: Bug #715235
     canvas.width = 200;
     var g = ctx.createLinearGradient(0, 0, 200, 0);
     g.addColorStop(0, '#f00');
     g.addColorStop(0, '#ff0');
     g.addColorStop(0.25, '#00f');
     g.addColorStop(0.25, '#0f0');
     g.addColorStop(0.25, '#0f0');
     g.addColorStop(0.25, '#0f0');
@@ -5830,22 +5849,25 @@ for (var p = 0; p < ps.length; ++p)
 {
         g.addColorStop(ps[p], '#0f0');
         for (var i = 0; i < 15; ++i)
                 g.addColorStop(ps[p], '#f00');
         g.addColorStop(ps[p], '#0f0');
 }
 ctx.fillStyle = g;
 ctx.fillRect(0, 0, 100, 50);
-isPixel(ctx, 1,25, 0,255,0,255, 0);
-isPixel(ctx, 30,25, 0,255,0,255, 0);
-isPixel(ctx, 40,25, 0,255,0,255, 0);
-isPixel(ctx, 60,25, 0,255,0,255, 0);
-isPixel(ctx, 80,25, 0,255,0,255, 0);
-
+
+if (!IsMacOSX10_5orOlder()) {
+    // On OS X 10.5 quartz is confused by the overlapping stops: Bug #715235
+    isPixel(ctx, 1,25, 0,255,0,255, 0);
+    isPixel(ctx, 30,25, 0,255,0,255, 0);
+    isPixel(ctx, 40,25, 0,255,0,255, 0);
+    isPixel(ctx, 60,25, 0,255,0,255, 0);
+    isPixel(ctx, 80,25, 0,255,0,255, 0);
+}
 
 }
 </script>
 
 <!-- [[[ test_2d.gradient.interpolate.solid.html ]]] -->
 
 <p>Canvas test: 2d.gradient.interpolate.solid</p>
 <canvas id="c217" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
--- a/content/html/content/src/nsHTMLTableElement.cpp
+++ b/content/html/content/src/nsHTMLTableElement.cpp
@@ -731,22 +731,24 @@ nsHTMLTableElement::InsertRow(PRInt32 aI
     if (newRow) {
       nsCOMPtr<nsIDOMNode> newRowNode(do_QueryInterface(newRow));
       nsCOMPtr<nsIDOMNode> retChild;
 
       // If index is -1 or equal to the number of rows, the new row
       // is appended.
       if (aIndex == -1 || PRUint32(aIndex) == rowCount) {
         rv = parent->AppendChild(newRowNode, getter_AddRefs(retChild));
+        NS_ENSURE_SUCCESS(rv, rv);
       }
       else
       {
         // insert the new row before the reference row we found above
         rv = parent->InsertBefore(newRowNode, refRow,
                                   getter_AddRefs(retChild));
+        NS_ENSURE_SUCCESS(rv, rv);
       }
 
       if (retChild) {
         CallQueryInterface(retChild, aValue);
       }
     }
   } else {
     // the row count was 0, so 
@@ -773,16 +775,17 @@ nsHTMLTableElement::InsertRow(PRInt32 aI
       nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tbody,
                                   getter_AddRefs(nodeInfo));
 
       nsCOMPtr<nsIContent> newRowGroup =
         NS_NewHTMLTableSectionElement(nodeInfo.forget());
 
       if (newRowGroup) {
         rv = AppendChildTo(newRowGroup, true);
+        NS_ENSURE_SUCCESS(rv, rv);
 
         rowGroup = do_QueryInterface(newRowGroup);
       }
     }
 
     if (rowGroup) {
       nsCOMPtr<nsINodeInfo> nodeInfo;
       nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tr,
--- a/content/html/content/src/nsHTMLTableSectionElement.cpp
+++ b/content/html/content/src/nsHTMLTableSectionElement.cpp
@@ -186,18 +186,20 @@ nsHTMLTableSectionElement::InsertRow(PRI
   nsCOMPtr<nsIDOMNode> retChild;
 
   nsresult rv;
   if (doInsert) {
     nsCOMPtr<nsIDOMNode> refRow;
     rows->Item(aIndex, getter_AddRefs(refRow));
 
     rv = InsertBefore(rowNode, refRow, getter_AddRefs(retChild));
+    NS_ENSURE_SUCCESS(rv, rv);
   } else {
     rv = AppendChild(rowNode, getter_AddRefs(retChild));
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (retChild) {
     CallQueryInterface(retChild, aValue);
   }
 
   return NS_OK;
 }
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -36,17 +36,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _MOZILLA_GFX_2D_H
 #define _MOZILLA_GFX_2D_H
 
 #include "Point.h"
 #include "Rect.h"
 #include "Matrix.h"
-
+#include "UserData.h"
 // This RefPtr class isn't ideal for usage in Azure, as it doesn't allow T**
 // outparams using the &-operator. But it will have to do as there's no easy
 // solution.
 #include "mozilla/RefPtr.h"
 
 struct _cairo_surface;
 typedef _cairo_surface cairo_surface_t;
 
@@ -739,17 +739,24 @@ public:
 
   SurfaceFormat GetFormat() { return mFormat; }
 
   /* Tries to get a native surface for a DrawTarget, this may fail if the
    * draw target cannot convert to this surface type.
    */
   virtual void *GetNativeSurface(NativeSurfaceType aType) { return NULL; }
 
+  void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
+    mUserData.Add(key, userData, destroy);
+  }
+  void *GetUserData(UserDataKey *key) {
+    return mUserData.Get(key);
+  }
 protected:
+  UserData mUserData;
   Matrix mTransform;
   bool mTransformDirty : 1;
 
   SurfaceFormat mFormat;
 };
 
 class Factory
 {
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -14,17 +14,17 @@
  *
  * The Original Code is Mozilla Corporation code.
  *
  * The Initial Developer of the Original Code is Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Bas Schouten <bschouten@mozilla.com>
+ *   Jeff Muizelaar <jmuizelaar@mozilla.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
@@ -32,153 +32,934 @@
  * 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 "DrawTargetCG.h"
 #include "SourceSurfaceCG.h"
 #include "Rect.h"
+#include "ScaledFontMac.h"
+#include "Tools.h"
+#include <vector>
 
 //CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
 
 namespace mozilla {
 namespace gfx {
 
 static CGRect RectToCGRect(Rect r)
 {
   return CGRectMake(r.x, r.y, r.width, r.height);
 }
 
+static CGRect IntRectToCGRect(IntRect r)
+{
+  return CGRectMake(r.x, r.y, r.width, r.height);
+}
+
 CGBlendMode ToBlendMode(CompositionOp op)
 {
   CGBlendMode mode;
   switch (op) {
     case OP_OVER:
       mode = kCGBlendModeNormal;
       break;
-    case OP_SOURCE:
-      mode = kCGBlendModeCopy;
-      break;
-    case OP_CLEAR:
-      mode = kCGBlendModeClear;
-      break;
     case OP_ADD:
       mode = kCGBlendModePlusLighter;
       break;
     case OP_ATOP:
       mode = kCGBlendModeSourceAtop;
       break;
+    case OP_OUT:
+      mode = kCGBlendModeSourceOut;
+      break;
+    case OP_IN:
+      mode = kCGBlendModeSourceIn;
+      break;
+    case OP_SOURCE:
+      mode = kCGBlendModeCopy;
+      break;
+    case OP_DEST_IN:
+      mode = kCGBlendModeDestinationIn;
+      break;
+    case OP_DEST_OUT:
+      mode = kCGBlendModeDestinationOut;
+      break;
+    case OP_DEST_OVER:
+      mode = kCGBlendModeDestinationOver;
+      break;
+    case OP_DEST_ATOP:
+      mode = kCGBlendModeDestinationAtop;
+      break;
+    case OP_XOR:
+      mode = kCGBlendModeXOR;
+      break;
+      /*
+    case OP_CLEAR:
+      mode = kCGBlendModeClear;
+      break;*/
     default:
       mode = kCGBlendModeNormal;
   }
   return mode;
 }
 
 
 
 DrawTargetCG::DrawTargetCG()
 {
 }
 
 DrawTargetCG::~DrawTargetCG()
 {
+  // We need to conditionally release these because Init can fail without initializing these.
+  if (mColorSpace)
+    CGColorSpaceRelease(mColorSpace);
+  if (mCg)
+    CGContextRelease(mCg);
 }
 
 TemporaryRef<SourceSurface>
 DrawTargetCG::Snapshot()
 {
-  return NULL;
+  RefPtr<SourceSurfaceCG> newSurf = new SourceSurfaceCG(CGBitmapContextCreateImage(mCg));
+  return newSurf;
+}
+
+TemporaryRef<DrawTarget>
+DrawTargetCG::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+  // XXX: in thebes we use CGLayers to do this kind of thing. It probably makes sense
+  // to add that in somehow, but at a higher level
+  RefPtr<DrawTargetCG> newTarget = new DrawTargetCG();
+  if (newTarget->Init(aSize, aFormat)) {
+    return newTarget;
+  } else {
+    return NULL;
+  }
 }
 
 TemporaryRef<SourceSurface>
 DrawTargetCG::CreateSourceSurfaceFromData(unsigned char *aData,
                                            const IntSize &aSize,
                                            int32_t aStride,
                                            SurfaceFormat aFormat) const
 {
   RefPtr<SourceSurfaceCG> newSurf = new SourceSurfaceCG();
 
-  if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
+ if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
     return NULL;
   }
 
   return newSurf;
 }
 
 TemporaryRef<SourceSurface>
 DrawTargetCG::OptimizeSourceSurface(SourceSurface *aSurface) const
 {
   return NULL;
 }
 
+class UnboundnessFixer
+{
+    CGRect mClipBounds;
+    CGLayerRef mLayer;
+    CGContextRef mCg;
+  public:
+    UnboundnessFixer() : mCg(NULL) {}
+
+    CGContextRef Check(CGContextRef baseCg, CompositionOp blend)
+    {
+      if (!IsOperatorBoundByMask(blend)) {
+        mClipBounds = CGContextGetClipBoundingBox(baseCg);
+        // TransparencyLayers aren't blended using the blend mode so
+        // we are forced to use CGLayers
+
+        //XXX: The size here is in default user space units, of the layer relative to the graphics context.
+        // is the clip bounds still correct if, for example, we have a scale applied to the context?
+        mLayer = CGLayerCreateWithContext(baseCg, mClipBounds.size, NULL);
+        mCg = CGLayerGetContext(mLayer);
+        // CGContext's default to have the origin at the bottom left
+        // so flip it to the top left and adjust for the origin
+        // of the layer
+        CGContextTranslateCTM(mCg, -mClipBounds.origin.x, mClipBounds.origin.y + mClipBounds.size.height);
+        CGContextScaleCTM(mCg, 1, -1);
+
+        return mCg;
+      } else {
+        return baseCg;
+      }
+    }
+
+    void Fix(CGContextRef baseCg)
+    {
+        if (mCg) {
+            CGContextTranslateCTM(baseCg, 0, mClipBounds.size.height);
+            CGContextScaleCTM(baseCg, 1, -1);
+            mClipBounds.origin.y *= -1;
+            CGContextDrawLayerAtPoint(baseCg, mClipBounds.origin, mLayer);
+            CGContextRelease(mCg);
+        }
+    }
+};
+
 void
 DrawTargetCG::DrawSurface(SourceSurface *aSurface,
                            const Rect &aDest,
                            const Rect &aSource,
-                           const DrawOptions &aOptions,
-                           const DrawSurfaceOptions &aSurfOptions)
+                           const DrawSurfaceOptions &aSurfOptions,
+                           const DrawOptions &aDrawOptions)
 {
   CGImageRef image;
   CGImageRef subimage = NULL;
-  if (aSurface->GetType() == COREGRAPHICS_IMAGE) {
+  if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) {
+    CGContextSaveGState(mCg);
+
+    CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
+    UnboundnessFixer fixer;
+    CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+    CGContextSetAlpha(cg, aDrawOptions.mAlpha);
+
+    CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
     image = static_cast<SourceSurfaceCG*>(aSurface)->GetImage();
     /* we have two options here:
      *  - create a subimage -- this is slower
      *  - fancy things with clip and different dest rects */
     {
       subimage = CGImageCreateWithImageInRect(image, RectToCGRect(aSource));
       image = subimage;
     }
 
-    CGContextDrawImage(mCg, RectToCGRect(aDest), image);
+    CGContextScaleCTM(cg, 1, -1);
+
+    CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + aDest.height),
+                                    aDest.width, aDest.height);
+
+    //XXX: we should implement this for patterns too
+    if (aSurfOptions.mFilter == FILTER_POINT)
+      CGContextSetInterpolationQuality(cg, kCGInterpolationNone);
+
+    CGContextDrawImage(cg, flippedRect, image);
+
+    fixer.Fix(mCg);
+
+    CGContextRestoreGState(mCg);
+
+    CGImageRelease(subimage);
+  }
+}
+
+static CGColorRef ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColor)
+{
+  CGFloat components[4] = {aColor.r, aColor.g, aColor.b, aColor.a};
+  return CGColorCreate(aColorSpace, components);
+}
+
+class GradientStopsCG : public GradientStops
+{
+  public:
+  //XXX: The skia backend uses a vector and passes in aNumStops. It should do better
+  GradientStopsCG(GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode)
+  {
+    //XXX: do the stops need to be in any particular order?
+    // what should we do about the color space here? we certainly shouldn't be
+    // recreating it all the time
+    std::vector<CGFloat> colors;
+    std::vector<CGFloat> offsets;
+    colors.reserve(aNumStops*4);
+    offsets.reserve(aNumStops);
+
+    for (uint32_t i = 0; i < aNumStops; i++) {
+      colors.push_back(aStops[i].color.r);
+      colors.push_back(aStops[i].color.g);
+      colors.push_back(aStops[i].color.b);
+      colors.push_back(aStops[i].color.a);
+
+      offsets.push_back(aStops[i].offset);
+    }
+
+    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+    mGradient = CGGradientCreateWithColorComponents(colorSpace,
+                                                    &colors.front(),
+                                                    &offsets.front(),
+                                                    aNumStops);
+    CGColorSpaceRelease(colorSpace);
+  }
+  virtual ~GradientStopsCG() {
+    CGGradientRelease(mGradient);
+  }
+  BackendType GetBackendType() const { return BACKEND_COREGRAPHICS; }
+  CGGradientRef mGradient;
+};
+
+TemporaryRef<GradientStops>
+DrawTargetCG::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops,
+                                  ExtendMode aExtendMode) const
+{
+  return new GradientStopsCG(aStops, aNumStops, aExtendMode);
+}
+
+static void
+DrawGradient(CGContextRef cg, const Pattern &aPattern)
+{
+  if (aPattern.GetType() == PATTERN_LINEAR_GRADIENT) {
+    const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern);
+    GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get());
+    // XXX: we should take the m out of the properties of LinearGradientPatterns
+    CGPoint startPoint = { pat.mBegin.x, pat.mBegin.y };
+    CGPoint endPoint   = { pat.mEnd.x,   pat.mEnd.y };
+
+    // Canvas spec states that we should avoid drawing degenerate gradients (XXX: should this be in common code?)
+    //if (startPoint.x == endPoint.x && startPoint.y == endPoint.y)
+    //  return;
+
+    CGContextDrawLinearGradient(cg, stops->mGradient, startPoint, endPoint,
+                                kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
+  } else if (aPattern.GetType() == PATTERN_RADIAL_GRADIENT) {
+    const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern);
+    GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get());
+
+    // XXX: we should take the m out of the properties of RadialGradientPatterns
+    CGPoint startCenter = { pat.mCenter1.x, pat.mCenter1.y };
+    CGFloat startRadius = pat.mRadius1;
+    CGPoint endCenter   = { pat.mCenter2.x, pat.mCenter2.y };
+    CGFloat endRadius   = pat.mRadius2;
+
+    //XXX: are there degenerate radial gradients that we should avoid drawing?
+    CGContextDrawRadialGradient(cg, stops->mGradient, startCenter, startRadius, endCenter, endRadius,
+                                kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
+  } else {
+    assert(0);
+  }
+
+}
+
+static void
+drawPattern(void *info, CGContextRef context)
+{
+  CGImageRef image = static_cast<CGImageRef>(info);
+  CGRect rect = {{0, 0},
+    {static_cast<CGFloat>(CGImageGetWidth(image)),
+     static_cast<CGFloat>(CGImageGetHeight(image))}};
+  CGContextDrawImage(context, rect, image);
+}
+
+static void
+releaseInfo(void *info)
+{
+  CGImageRef image = static_cast<CGImageRef>(info);
+  CGImageRelease(image);
+}
+
+CGPatternCallbacks patternCallbacks = {
+  0,
+  drawPattern,
+  releaseInfo
+};
+
+static bool
+isGradient(const Pattern &aPattern)
+{
+  return aPattern.GetType() == PATTERN_LINEAR_GRADIENT || aPattern.GetType() == PATTERN_RADIAL_GRADIENT;
+}
+
+/* CoreGraphics patterns ignore the userspace transform so
+ * we need to multiply it in */
+static CGPatternRef
+CreateCGPattern(const Pattern &aPattern, CGAffineTransform aUserSpace)
+{
+  const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
+  // XXX: is .get correct here?
+  CGImageRef image = static_cast<SourceSurfaceCG*>(pat.mSurface.get())->GetImage();
+  CGFloat xStep, yStep;
+  switch (pat.mExtendMode) {
+    case EXTEND_CLAMP:
+      // The 1 << 22 comes from Webkit see Pattern::createPlatformPattern() in PatternCG.cpp for more info
+      xStep = static_cast<CGFloat>(1 << 22);
+      yStep = static_cast<CGFloat>(1 << 22);
+      break;
+    case EXTEND_REFLECT:
+      assert(0);
+    case EXTEND_REPEAT:
+      xStep = static_cast<CGFloat>(CGImageGetWidth(image));
+      yStep = static_cast<CGFloat>(CGImageGetHeight(image));
+      // webkit uses wkCGPatternCreateWithImageAndTransform a wrapper around CGPatternCreateWithImage2
+      // this is done to avoid pixel-cracking along pattern boundaries
+      // (see https://bugs.webkit.org/show_bug.cgi?id=53055)
+      // typedef enum {
+      //    wkPatternTilingNoDistortion,
+      //    wkPatternTilingConstantSpacingMinimalDistortion,
+      //    wkPatternTilingConstantSpacing
+      // } wkPatternTiling;
+      // extern CGPatternRef (*wkCGPatternCreateWithImageAndTransform)(CGImageRef, CGAffineTransform, int);
+  }
+
+  //XXX: We should be using CGContextDrawTiledImage when we can. Even though it
+  // creates a pattern, it seems to go down a faster path than using a delegate
+  // like we do below
+  CGRect bounds = {
+    {0, 0,},
+    {static_cast<CGFloat>(CGImageGetWidth(image)), static_cast<CGFloat>(CGImageGetHeight(image))}
+  };
+  CGAffineTransform transform = CGAffineTransformConcat(CGAffineTransformMakeScale(1, -1), aUserSpace);
+  transform = CGAffineTransformTranslate(transform, 0, -static_cast<float>(CGImageGetHeight(image)));
+  return CGPatternCreate(CGImageRetain(image), bounds, transform, xStep, yStep, kCGPatternTilingConstantSpacing,
+                         true, &patternCallbacks);
+}
+
+static void
+SetFillFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern &aPattern)
+{
+  assert(!isGradient(aPattern));
+  if (aPattern.GetType() == PATTERN_COLOR) {
+
+    const Color& color = static_cast<const ColorPattern&>(aPattern).mColor;
+    //XXX: we should cache colors
+    CGColorRef cgcolor = ColorToCGColor(aColorSpace, color);
+    CGContextSetFillColorWithColor(cg, cgcolor);
+    CGColorRelease(cgcolor);
+  } else if (aPattern.GetType() == PATTERN_SURFACE) {
+
+    CGColorSpaceRef patternSpace;
+    patternSpace = CGColorSpaceCreatePattern (NULL);
+    CGContextSetFillColorSpace(cg, patternSpace);
+    CGColorSpaceRelease(patternSpace);
+
+    CGPatternRef pattern = CreateCGPattern(aPattern, CGContextGetCTM(cg));
+    CGFloat alpha = 1.;
+    CGContextSetFillPattern(cg, pattern, &alpha);
+    CGPatternRelease(pattern);
+  }
+}
+
+static void
+SetStrokeFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern &aPattern)
+{
+  assert(!isGradient(aPattern));
+  if (aPattern.GetType() == PATTERN_COLOR) {
+    const Color& color = static_cast<const ColorPattern&>(aPattern).mColor;
+    //XXX: we should cache colors
+    CGColorRef cgcolor = ColorToCGColor(aColorSpace, color);
+    CGContextSetStrokeColorWithColor(cg, cgcolor);
+    CGColorRelease(cgcolor);
+  } else if (aPattern.GetType() == PATTERN_SURFACE) {
+    CGColorSpaceRef patternSpace;
+    patternSpace = CGColorSpaceCreatePattern (NULL);
+    CGContextSetStrokeColorSpace(cg, patternSpace);
+    CGColorSpaceRelease(patternSpace);
+
+    CGPatternRef pattern = CreateCGPattern(aPattern, CGContextGetCTM(cg));
+    CGFloat alpha = 1.;
+    CGContextSetStrokePattern(cg, pattern, &alpha);
+    CGPatternRelease(pattern);
+  }
+
+}
+
+
+void
+DrawTargetCG::FillRect(const Rect &aRect,
+                        const Pattern &aPattern,
+                        const DrawOptions &aDrawOptions)
+{
+  CGContextSaveGState(mCg);
+
+  UnboundnessFixer fixer;
+  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+  CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
+  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
+
+  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
+
+  if (isGradient(aPattern)) {
+    CGContextClipToRect(cg, RectToCGRect(aRect));
+    DrawGradient(cg, aPattern);
+  } else {
+    SetFillFromPattern(cg, mColorSpace, aPattern);
+    CGContextFillRect(cg, RectToCGRect(aRect));
+  }
+
+  fixer.Fix(mCg);
+  CGContextRestoreGState(mCg);
+}
+
+void
+DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions)
+{
+  CGContextSaveGState(mCg);
+
+  UnboundnessFixer fixer;
+  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+  CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
+  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
+
+  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
+
+  CGContextBeginPath(cg);
+  CGContextMoveToPoint(cg, p1.x, p1.y);
+  CGContextAddLineToPoint(cg, p2.x, p2.y);
+
+  SetStrokeOptions(cg, aStrokeOptions);
+
+  if (isGradient(aPattern)) {
+    CGContextReplacePathWithStrokedPath(cg);
+    //XXX: should we use EO clip here?
+    CGContextClip(cg);
+    DrawGradient(cg, aPattern);
+  } else {
+    SetStrokeFromPattern(cg, mColorSpace, aPattern);
+    CGContextStrokePath(cg);
+  }
+
+  fixer.Fix(mCg);
+  CGContextRestoreGState(mCg);
+}
+
+void
+DrawTargetCG::StrokeRect(const Rect &aRect,
+                         const Pattern &aPattern,
+                         const StrokeOptions &aStrokeOptions,
+                         const DrawOptions &aDrawOptions)
+{
+  CGContextSaveGState(mCg);
+
+  UnboundnessFixer fixer;
+  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+  CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
+  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
+
+  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
+
+  // we don't need to set all of the stroke state because
+  // it doesn't apply when stroking rects
+  switch (aStrokeOptions.mLineJoin)
+  {
+    case JOIN_BEVEL:
+      CGContextSetLineJoin(cg, kCGLineJoinBevel);
+      break;
+    case JOIN_ROUND:
+      CGContextSetLineJoin(cg, kCGLineJoinRound);
+      break;
+    case JOIN_MITER:
+    case JOIN_MITER_OR_BEVEL:
+      CGContextSetLineJoin(cg, kCGLineJoinMiter);
+      break;
+  }
+  CGContextSetLineWidth(cg, aStrokeOptions.mLineWidth);
+
+  if (isGradient(aPattern)) {
+    // There's no CGContextClipStrokeRect so we do it by hand
+    CGContextBeginPath(cg);
+    CGContextAddRect(cg, RectToCGRect(aRect));
+    CGContextReplacePathWithStrokedPath(cg);
+    //XXX: should we use EO clip here?
+    CGContextClip(cg);
+    DrawGradient(cg, aPattern);
+  } else {
+    SetStrokeFromPattern(cg, mColorSpace, aPattern);
+    CGContextStrokeRect(cg, RectToCGRect(aRect));
+  }
+
+  fixer.Fix(mCg);
+  CGContextRestoreGState(mCg);
+}
+
+
+void
+DrawTargetCG::ClearRect(const Rect &aRect)
+{
+  CGContextSaveGState(mCg);
+  CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(mTransform));
+
+  CGContextClearRect(mCg, RectToCGRect(aRect));
+
+  CGContextRestoreGState(mCg);
+}
+
+void
+DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions)
+{
+  CGContextSaveGState(mCg);
+
+  UnboundnessFixer fixer;
+  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+  CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
+  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
+
+  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
+
+
+  CGContextBeginPath(cg);
+
+  assert(aPath->GetBackendType() == BACKEND_COREGRAPHICS);
+  const PathCG *cgPath = static_cast<const PathCG*>(aPath);
+  CGContextAddPath(cg, cgPath->GetPath());
+
+  SetStrokeOptions(cg, aStrokeOptions);
+
+  if (isGradient(aPattern)) {
+    CGContextReplacePathWithStrokedPath(cg);
+    //XXX: should we use EO clip here?
+    CGContextClip(cg);
+    DrawGradient(cg, aPattern);
+  } else {
+    CGContextBeginPath(cg);
+    // XXX: we could put fill mode into the path fill rule if we wanted
+    const PathCG *cgPath = static_cast<const PathCG*>(aPath);
+    CGContextAddPath(cg, cgPath->GetPath());
+
+    SetStrokeFromPattern(cg, mColorSpace, aPattern);
+    CGContextStrokePath(cg);
+  }
+
+  fixer.Fix(mCg);
+  CGContextRestoreGState(mCg);
+}
+
+void
+DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aDrawOptions)
+{
+  assert(aPath->GetBackendType() == BACKEND_COREGRAPHICS);
+
+  CGContextSaveGState(mCg);
+
+  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
+  UnboundnessFixer fixer;
+  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+  CGContextSetAlpha(cg, aDrawOptions.mAlpha);
+
+  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
+
+  if (isGradient(aPattern)) {
+    // XXX: we should be able to avoid the extra SaveState that PushClip does
+    PushClip(aPath);
+    DrawGradient(cg, aPattern);
+    PopClip();
+  } else {
+    CGContextBeginPath(cg);
+    // XXX: we could put fill mode into the path fill rule if we wanted
+    const PathCG *cgPath = static_cast<const PathCG*>(aPath);
+    CGContextAddPath(cg, cgPath->GetPath());
+
+    SetFillFromPattern(cg, mColorSpace, aPattern);
+
+    if (cgPath->GetFillRule() == FILL_EVEN_ODD)
+      CGContextEOFillPath(cg);
+    else
+      CGContextFillPath(cg);
+  }
+
+  fixer.Fix(mCg);
+  CGContextRestoreGState(mCg);
+}
+
+
+void
+DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aDrawOptions)
+{
+  assert(aBuffer.mNumGlyphs);
+  CGContextSaveGState(mCg);
+
+  CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
+  UnboundnessFixer fixer;
+  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+  CGContextSetAlpha(cg, aDrawOptions.mAlpha);
+
+  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
+
+  ScaledFontMac* cgFont = static_cast<ScaledFontMac*>(aFont);
+  CGContextSetFont(cg, cgFont->mFont);
+  CGContextSetFontSize(cg, cgFont->mSize);
+
+  //XXX: we should use a stack vector here when we have a class like that
+  std::vector<CGGlyph> glyphs;
+  std::vector<CGPoint> positions;
+  glyphs.resize(aBuffer.mNumGlyphs);
+  positions.resize(aBuffer.mNumGlyphs);
+
+  CGFloat xprev = aBuffer.mGlyphs[0].mPosition.x;
+  CGFloat yprev = aBuffer.mGlyphs[0].mPosition.y;
+  CGContextSetTextPosition(cg, xprev, yprev);
+
+  // Handle the flip
+  CGAffineTransform matrix = CGAffineTransformMakeScale(1, -1);//CGAffineTransformMake(1, 0, 0, -1, 0, -mSize.height);
+  // "Note that the text matrix is not a part of the graphics state"
+  CGContextSetTextMatrix(cg, matrix);
+
+  for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+    glyphs[i] = aBuffer.mGlyphs[i].mIndex;
+    // XXX: CGPointMake might not be inlined
+    positions[i] = CGPointMake(aBuffer.mGlyphs[i].mPosition.x,
+                              -aBuffer.mGlyphs[i].mPosition.y);
+  }
+
+  //XXX: CGContextShowGlyphsAtPositions is 10.5+ for older versions use CGContextShowGlyphsWithAdvances
+  if (isGradient(aPattern)) {
+    CGContextSetTextDrawingMode(cg, kCGTextClip);
+    CGContextShowGlyphsAtPositions(cg, &glyphs.front(), &positions.front(), aBuffer.mNumGlyphs);
+    DrawGradient(cg, aPattern);
+  } else {
+    //XXX: with CoreGraphics we can stroke text directly instead of going
+    // through GetPath. It would be nice to add support for using that
+    CGContextSetTextDrawingMode(cg, kCGTextFill);
+    SetFillFromPattern(cg, mColorSpace, aPattern);
+    CGContextShowGlyphsAtPositions(cg, &glyphs.front(), &positions.front(), aBuffer.mNumGlyphs);
+  }
+
+  fixer.Fix(mCg);
+  CGContextRestoreGState(cg);
+}
+
+extern "C" {
+void
+CGContextResetClip(CGContextRef);
+};
+
+void
+DrawTargetCG::CopySurface(SourceSurface *aSurface,
+                          const IntRect& aSourceRect,
+                          const IntPoint &aDestination)
+{
+  CGImageRef image;
+  CGImageRef subimage = NULL;
+  if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) {
+    image = static_cast<SourceSurfaceCG*>(aSurface)->GetImage();
+    /* we have two options here:
+     *  - create a subimage -- this is slower
+     *  - fancy things with clip and different dest rects */
+    {
+      subimage = CGImageCreateWithImageInRect(image, IntRectToCGRect(aSourceRect));
+      image = subimage;
+    }
+    // XXX: it might be more efficient for us to do the copy directly if we have access to the bits
+
+    CGContextSaveGState(mCg);
+
+    // CopySurface ignores the clip, so we need to use private API to temporarily reset it
+    CGContextResetClip(mCg);
+    CGContextSetBlendMode(mCg, kCGBlendModeCopy);
+
+    CGContextScaleCTM(mCg, 1, -1);
+
+    CGRect flippedRect = CGRectMake(aDestination.x, -(aDestination.y + aSourceRect.height),
+                                    aSourceRect.width, aSourceRect.height);
+
+    CGContextDrawImage(mCg, flippedRect, image);
+
+    CGContextRestoreGState(mCg);
 
     CGImageRelease(subimage);
   }
 }
 
 void
-DrawTargetCG::FillRect(const Rect &aRect,
-                        const Pattern &aPattern,
-                        const DrawOptions &aOptions)
+DrawTargetCG::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, Float aSigma, CompositionOp aOperator)
 {
-  //XXX: it would be nice to hang a CGColor off of the pattern here
-  if (aPattern.GetType() == COLOR) {
-    Color color = static_cast<const ColorPattern*>(&aPattern)->mColor;
-    //XXX: the m prefixes are painful here
-    CGContextSetRGBFillColor(mCg, color.mR, color.mG, color.mB, color.mA);
+  CGImageRef image;
+  CGImageRef subimage = NULL;
+  if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) {
+    image = static_cast<SourceSurfaceCG*>(aSurface)->GetImage();
+
+    IntSize size = aSurface->GetSize();
+    CGContextSaveGState(mCg);
+    //XXX do we need to do the fixup here?
+    CGContextSetBlendMode(mCg, ToBlendMode(aOperator));
+
+    CGContextScaleCTM(mCg, 1, -1);
+
+    CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + size.height),
+                                    size.width, size.height);
+
+    CGColorRef color = ColorToCGColor(mColorSpace, aColor);
+    CGSize offset = {aOffset.x, -aOffset.y};
+    // CoreGraphics needs twice sigma as it's amount of blur
+    CGContextSetShadowWithColor(mCg, offset, 2*aSigma, color);
+    CGColorRelease(color);
+
+    CGContextDrawImage(mCg, flippedRect, image);
+
+    CGContextRestoreGState(mCg);
+
+    CGImageRelease(subimage);
+  }
+}
+
+bool
+DrawTargetCG::Init(CGContextRef cgContext, const IntSize &aSize)
+{
+  // XXX: we should come up with some consistent semantics for dealing
+  // with zero area drawtargets
+  if (aSize.width == 0 || aSize.height == 0) {
+    mColorSpace = NULL;
+    mCg = NULL;
+    return false;
   }
 
-  CGContextSetBlendMode(mCg, ToBlendMode(aOptions.mCompositionOp));
-  CGContextFillRect(mCg, RectToCGRect(aRect));
+  //XXX: handle SurfaceFormat
+
+  //XXX: we'd be better off reusing the Colorspace across draw targets
+  mColorSpace = CGColorSpaceCreateDeviceRGB();
+
+  mSize = aSize;
+
+  mCg = cgContext;
+
+  mData = NULL;
+
+  assert(mCg);
+  // CGContext's default to have the origin at the bottom left
+  // so flip it to the top left
+  CGContextTranslateCTM(mCg, 0, mSize.height);
+  CGContextScaleCTM(mCg, 1, -1);
+
+  //XXX: set correct format
+  mFormat = FORMAT_B8G8R8A8;
+
+  return true;
 }
 
+bool
+DrawTargetCG::Init(const IntSize &aSize, SurfaceFormat &)
+{
+  // XXX: we should come up with some consistent semantics for dealing
+  // with zero area drawtargets
+  if (aSize.width == 0 || aSize.height == 0) {
+    mColorSpace = NULL;
+    mCg = NULL;
+    return false;
+  }
 
-bool
-DrawTargetCG::Init(const IntSize &aSize)
-{
-  CGColorSpaceRef cgColorspace;
-  cgColorspace = CGColorSpaceCreateDeviceRGB();
+  //XXX: handle SurfaceFormat
+
+  //XXX: we'd be better off reusing the Colorspace across draw targets
+  mColorSpace = CGColorSpaceCreateDeviceRGB();
 
   mSize = aSize;
 
   int bitsPerComponent = 8;
-  int stride = mSize.width;
+  int stride = mSize.width*4;
 
   CGBitmapInfo bitinfo;
 
   bitinfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
 
-  // XXX: mWidth is ugly
-  mCg = CGBitmapContextCreate (NULL,
-                         mSize.width,
-			 mSize.height,
-			 bitsPerComponent,
-			 stride,
-			 cgColorspace,
-			 bitinfo);
+  // XXX: currently we allocate ourselves so that we can easily return a gfxImageSurface
+  // we might not need to later if once we don't need to support gfxImageSurface
+  //XXX: currently Init implicitly clears, that can often be a waste of time
+  // XXX: leaked
+  mData = calloc(mSize.height * stride, 1);
+  // XXX: what should we do if this fails?
+  mCg = CGBitmapContextCreate (mData,
+                               mSize.width,
+                               mSize.height,
+                               bitsPerComponent,
+                               stride,
+                               mColorSpace,
+                               bitinfo);
 
-  CGColorSpaceRelease (cgColorspace);
+
+  assert(mCg);
+  // CGContext's default to have the origin at the bottom left
+  // so flip it to the top left
+  CGContextTranslateCTM(mCg, 0, mSize.height);
+  CGContextScaleCTM(mCg, 1, -1);
+
+  //XXX: set correct format
+  mFormat = FORMAT_B8G8R8A8;
 
   return true;
 }
+
+TemporaryRef<PathBuilder>
+DrawTargetCG::CreatePathBuilder(FillRule aFillRule) const
+{
+  RefPtr<PathBuilderCG> pb = new PathBuilderCG(aFillRule);
+  return pb;
+}
+
+void*
+DrawTargetCG::GetNativeSurface(NativeSurfaceType aType)
+{
+  if (aType == NATIVE_SURFACE_CGCONTEXT) {
+    return mCg;
+  } else {
+    return NULL;
+  }
+}
+
+void
+DrawTargetCG::Mask(const Pattern &aSource,
+                   const Pattern &aMask,
+                   const DrawOptions &aDrawOptions)
+{
+
+  CGContextSaveGState(mCg);
+
+  if (isGradient(aMask)) {
+    assert(0);
+  } else {
+    if (aMask.GetType() == PATTERN_COLOR) {
+      DrawOptions drawOptions(aDrawOptions);
+      const Color& color = static_cast<const ColorPattern&>(aMask).mColor;
+      drawOptions.mAlpha *= color.a;
+      assert(0);
+      // XXX: we need to get a rect that when transformed covers the entire surface
+      //Rect
+      //FillRect(rect, aSource, drawOptions);
+    } else if (aMask.GetType() == PATTERN_SURFACE) {
+      const SurfacePattern& pat = static_cast<const SurfacePattern&>(aMask);
+      CGImageRef mask = static_cast<SourceSurfaceCG*>(pat.mSurface.get())->GetImage();
+      Rect rect(0,0, CGImageGetWidth(mask), CGImageGetHeight(mask));
+      // XXX: probably we need to do some flipping of the image or something
+      CGContextClipToMask(mCg, RectToCGRect(rect), mask);
+      FillRect(rect, aSource, aDrawOptions);
+    }
+  }
+
+  CGContextRestoreGState(mCg);
+}
+
+void
+DrawTargetCG::PushClipRect(const Rect &aRect)
+{
+  CGContextSaveGState(mCg);
+
+  CGContextClipToRect(mCg, RectToCGRect(aRect));
+}
+
+
+void
+DrawTargetCG::PushClip(const Path *aPath)
+{
+  CGContextSaveGState(mCg);
+
+  CGContextBeginPath(mCg);
+  assert(aPath->GetBackendType() == BACKEND_COREGRAPHICS);
+
+  const PathCG *cgPath = static_cast<const PathCG*>(aPath);
+
+  // Weirdly, CoreGraphics clips empty paths as all shown
+  // but emtpy rects as all clipped.  We detect this situation and
+  // workaround it appropriately
+  if (CGPathIsEmpty(cgPath->GetPath())) {
+    // XXX: should we return here?
+    CGContextClipToRect(mCg, CGRectZero);
+  }
+
+
+  CGContextAddPath(mCg, cgPath->GetPath());
+  if (cgPath->GetFillRule() == FILL_EVEN_ODD)
+    CGContextEOClip(mCg);
+  else
+    CGContextClip(mCg);
+}
+
+void
+DrawTargetCG::PopClip()
+{
+  CGContextRestoreGState(mCg);
+}
+
+
+
 }
 }
--- a/gfx/2d/DrawTargetCG.h
+++ b/gfx/2d/DrawTargetCG.h
@@ -30,56 +30,161 @@
  * 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 ***** */
 
-#pragma once
-
 #include <ApplicationServices/ApplicationServices.h>
 
 #include "2D.h"
 #include "Rect.h"
+#include "PathCG.h"
+
 namespace mozilla {
 namespace gfx {
 
+static inline CGAffineTransform
+GfxMatrixToCGAffineTransform(Matrix m)
+{
+  CGAffineTransform t;
+  t.a = m._11;
+  t.b = m._12;
+  t.c = m._21;
+  t.d = m._22;
+  t.tx = m._31;
+  t.ty = m._32;
+  return t;
+}
+
+static inline Rect
+CGRectToRect(CGRect rect)
+{
+  return Rect(rect.origin.x,
+              rect.origin.y,
+              rect.size.width,
+              rect.size.height);
+}
+
+static inline void
+SetStrokeOptions(CGContextRef cg, const StrokeOptions &aStrokeOptions)
+{
+  switch (aStrokeOptions.mLineCap)
+  {
+    case CAP_BUTT:
+      CGContextSetLineCap(cg, kCGLineCapButt);
+      break;
+    case CAP_ROUND:
+      CGContextSetLineCap(cg, kCGLineCapRound);
+      break;
+    case CAP_SQUARE:
+      CGContextSetLineCap(cg, kCGLineCapSquare);
+      break;
+  }
+
+  switch (aStrokeOptions.mLineJoin)
+  {
+    case JOIN_BEVEL:
+      CGContextSetLineJoin(cg, kCGLineJoinBevel);
+      break;
+    case JOIN_ROUND:
+      CGContextSetLineJoin(cg, kCGLineJoinRound);
+      break;
+    case JOIN_MITER:
+    case JOIN_MITER_OR_BEVEL:
+      CGContextSetLineJoin(cg, kCGLineJoinMiter);
+      break;
+  }
+
+  CGContextSetLineWidth(cg, aStrokeOptions.mLineWidth);
+  CGContextSetMiterLimit(cg, aStrokeOptions.mMiterLimit);
+
+  // XXX: rename mDashLength to dashLength
+  if (aStrokeOptions.mDashLength > 1) {
+    // we use a regular array instead of a std::vector here because we don't want to leak the <vector> include
+    CGFloat *dashes = new CGFloat[aStrokeOptions.mDashLength];
+    for (size_t i=0; i<aStrokeOptions.mDashLength; i++) {
+      dashes[i] = aStrokeOptions.mDashPattern[i];
+    }
+    CGContextSetLineDash(cg, aStrokeOptions.mDashOffset, dashes, aStrokeOptions.mDashLength);
+    delete[] dashes;
+  }
+}
+
+
 class DrawTargetCG : public DrawTarget
 {
 public:
   DrawTargetCG();
   virtual ~DrawTargetCG();
 
-  virtual BackendType GetType() const { return COREGRAPHICS; }
+  virtual BackendType GetType() const { return BACKEND_COREGRAPHICS; }
   virtual TemporaryRef<SourceSurface> Snapshot();
 
   virtual void DrawSurface(SourceSurface *aSurface,
                            const Rect &aDest,
                            const Rect &aSource,
-                           const DrawOptions &aOptions = DrawOptions(),
-                           const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions());
+                           const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
+                           const DrawOptions &aOptions = DrawOptions());
 
   virtual void FillRect(const Rect &aRect,
                         const Pattern &aPattern,
                         const DrawOptions &aOptions = DrawOptions());
 
 
-  bool Init(const IntSize &aSize);
+  //XXX: why do we take a reference to SurfaceFormat?
+  bool Init(const IntSize &aSize, SurfaceFormat&);
   bool Init(CGContextRef cgContext, const IntSize &aSize);
 
+
+  virtual void Flush() {}
+
+  virtual void DrawSurfaceWithShadow(SourceSurface *, const Point &, const Color &, const Point &, Float, CompositionOp);
+  virtual void ClearRect(const Rect &);
+  virtual void CopySurface(SourceSurface *, const IntRect&, const IntPoint&);
+  virtual void StrokeRect(const Rect &, const Pattern &, const StrokeOptions&, const DrawOptions&);
+  virtual void StrokeLine(const Point &, const Point &, const Pattern &, const StrokeOptions &, const DrawOptions &);
+  virtual void Stroke(const Path *, const Pattern &, const StrokeOptions &, const DrawOptions &);
+  virtual void Fill(const Path *, const Pattern &, const DrawOptions &);
+  virtual void FillGlyphs(ScaledFont *, const GlyphBuffer&, const Pattern &, const DrawOptions &);
+  virtual void Mask(const Pattern &aSource,
+                    const Pattern &aMask,
+                    const DrawOptions &aOptions = DrawOptions());
+  virtual void PushClip(const Path *);
+  virtual void PushClipRect(const Rect &aRect);
+  virtual void PopClip();
+  virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromNativeSurface(const NativeSurface&) const { return NULL;}
+  virtual TemporaryRef<DrawTarget> CreateSimilarDrawTarget(const IntSize &, SurfaceFormat) const;
+  virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule) const;
+  virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *, uint32_t,
+                                                          ExtendMode aExtendMode = EXTEND_CLAMP) const;
+
+  virtual void *GetNativeSurface(NativeSurfaceType);
+
+  virtual IntSize GetSize() { return mSize; }
+
+
   /* This is for creating good compatible surfaces */
   virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
                                                             const IntSize &aSize,
                                                             int32_t aStride,
                                                             SurfaceFormat aFormat) const;
   virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const;
+  CGContextRef GetCGContext() {
+      return mCg;
+  }
 private:
   bool InitCGRenderTarget();
 
   IntSize mSize;
+  CGColorSpaceRef mColorSpace;
   CGContextRef mCg;
 
+  void *mData;
+
+  SurfaceFormat mFormat;
+
 };
 
 }
 }
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -32,17 +32,17 @@
  * 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 "DrawTargetSkia.h"
 #include "SourceSurfaceSkia.h"
-#include "ScaledFontSkia.h"
+#include "ScaledFontBase.h"
 #include "skia/SkDevice.h"
 #include "skia/SkTypeface.h"
 #include "skia/SkGradientShader.h"
 #include "skia/SkBlurDrawLooper.h"
 #include "skia/SkBlurMaskFilter.h"
 #include "skia/SkColorFilter.h"
 #include "skia/SkLayerRasterizer.h"
 #include "skia/SkLayerDrawLooper.h"
@@ -53,16 +53,17 @@
 #include "Tools.h"
 #include <algorithm>
 
 namespace mozilla {
 namespace gfx {
 
 SkColor ColorToSkColor(const Color &color, Float aAlpha)
 {
+  //XXX: do a better job converting to int
   return SkColorSetARGB(color.a*aAlpha*255.0, color.r*255.0, color.g*255.0, color.b*255.0);
 }
 
 class GradientStopsSkia : public GradientStops
 {
 public:
   GradientStopsSkia(const std::vector<GradientStop>& aStops, uint32_t aNumStops)
     : mCount(aNumStops)
@@ -515,20 +516,20 @@ DrawTargetSkia::FillGlyphs(ScaledFont *a
                            const DrawOptions &aOptions)
 {
   if (aFont->GetType() != FONT_MAC && aFont->GetType() != FONT_SKIA) {
     return;
   }
 
   MarkChanged();
 
-  ScaledFontSkia* skiaFont = static_cast<ScaledFontSkia*>(aFont);
+  ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
 
   AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
-  paint.mPaint.setTypeface(skiaFont->mTypeface);
+  paint.mPaint.setTypeface(skiaFont->GetSkTypeface());
   paint.mPaint.setTextSize(SkFloatToScalar(skiaFont->mSize));
   paint.mPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
   
   std::vector<uint16_t> indices;
   std::vector<SkPoint> offsets;
   indices.resize(aBuffer.mNumGlyphs);
   offsets.resize(aBuffer.mNumGlyphs);
 
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -39,23 +39,30 @@
 
 #ifdef USE_CAIRO
 #include "DrawTargetCairo.h"
 #include "ScaledFontCairo.h"
 #endif
 
 #ifdef USE_SKIA
 #include "DrawTargetSkia.h"
+#include "ScaledFontBase.h"
+#endif
+
+#ifdef WIN32
+#include "ScaledFontWin.h"
+#endif
+
 #ifdef XP_MACOSX
 #include "ScaledFontMac.h"
 #endif
-#ifdef WIN32
-#include "ScaledFontWin.h"
-#endif
-#include "ScaledFontSkia.h"
+
+
+#ifdef XP_MACOSX
+#include "DrawTargetCG.h"
 #endif
 
 #ifdef WIN32
 #include "DrawTargetD2D.h"
 #include "ScaledFontDWrite.h"
 #include <d3d10_1.h>
 #endif
 
@@ -85,28 +92,40 @@ Factory::CreateDrawTarget(BackendType aB
     {
       RefPtr<DrawTargetD2D> newTarget;
       newTarget = new DrawTargetD2D();
       if (newTarget->Init(aSize, aFormat)) {
         return newTarget;
       }
       break;
     }
-#endif
+#elif defined XP_MACOSX || defined ANDROID
 #ifdef USE_SKIA
   case BACKEND_SKIA:
     {
       RefPtr<DrawTargetSkia> newTarget;
       newTarget = new DrawTargetSkia();
       if (newTarget->Init(aSize, aFormat)) {
         return newTarget;
       }
       break;
     }
 #endif
+#ifdef XP_MACOSX
+  case BACKEND_COREGRAPHICS:
+    {
+      RefPtr<DrawTargetCG> newTarget;
+      newTarget = new DrawTargetCG();
+      if (newTarget->Init(aSize, aFormat)) {
+        return newTarget;
+      }
+      break;
+    }
+#endif
+#endif
   default:
     gfxDebug() << "Invalid draw target type specified.";
     return NULL;
   }
 
   gfxDebug() << "Failed to create DrawTarget, Type: " << aBackend << " Size: " << aSize;
   // Failed
   return NULL;
@@ -117,32 +136,32 @@ Factory::CreateScaledFontForNativeFont(c
 {
   switch (aNativeFont.mType) {
 #ifdef WIN32
   case NATIVE_FONT_DWRITE_FONT_FACE:
     {
       return new ScaledFontDWrite(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aSize);
     }
 #endif
-#ifdef USE_SKIA
 #ifdef XP_MACOSX
   case NATIVE_FONT_MAC_FONT_FACE:
     {
       return new ScaledFontMac(static_cast<CGFontRef>(aNativeFont.mFont), aSize);
     }
 #endif
+#ifdef USE_SKIA
 #ifdef WIN32
   case NATIVE_FONT_GDI_FONT_FACE:
     {
       return new ScaledFontWin(static_cast<gfxGDIFont*>(aNativeFont.mFont), aSize);
     }
 #endif
   case NATIVE_FONT_SKIA_FONT_FACE:
     {
-      return new ScaledFontSkia(static_cast<gfxFont*>(aNativeFont.mFont), aSize);
+      return new ScaledFontBase(static_cast<gfxFont*>(aNativeFont.mFont), aSize);
     }
 #endif
   case NATIVE_FONT_CAIRO_FONT_FACE:
     {
       return new ScaledFontCairo(static_cast<gfxFont*>(aNativeFont.mFont));
     }
   default:
     gfxWarning() << "Invalid native font type specified.";
--- a/gfx/2d/Makefile.in
+++ b/gfx/2d/Makefile.in
@@ -57,37 +57,45 @@ EXPORTS_mozilla/gfx	= \
         BaseRect.h \
         BaseSize.h \
         Blur.h \
         PathHelpers.h \
         Point.h \
         Matrix.h \
         Rect.h \
         Types.h \
+        UserData.h \
 	$(NULL)
 
 CPPSRCS	= \
 	Factory.cpp \
         Matrix.cpp \
         DrawTargetCairo.cpp \
         ScaledFontCairo.cpp \
         SourceSurfaceCairo.cpp \
         PathCairo.cpp \
         Blur.cpp \
+        ScaledFontBase.cpp \
         $(NULL)
 
+ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
+CPPSRCS	+= \
+	   SourceSurfaceCG.cpp \
+	   DrawTargetCG.cpp \
+	   PathCG.cpp \
+	   $(NULL)
+endif
 
 DEFINES += -DMOZ_GFX -DUSE_CAIRO
 
 ifdef MOZ_ENABLE_SKIA
 CPPSRCS	+= \
         SourceSurfaceSkia.cpp \
         DrawTargetSkia.cpp \
         PathSkia.cpp \
-        ScaledFontSkia.cpp \
         $(NULL)
 
 DEFINES += -DUSE_SKIA
 
 endif
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 ifdef MOZ_ENABLE_SKIA
new file mode 100644
--- /dev/null
+++ b/gfx/2d/PathCG.cpp
@@ -0,0 +1,273 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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 Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bas Schouten <bschouten@mozilla.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 "PathCG.h"
+#include <math.h>
+#include "DrawTargetCG.h"
+#include "Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+PathBuilderCG::~PathBuilderCG()
+{
+  CGPathRelease(mCGPath);
+}
+
+void
+PathBuilderCG::MoveTo(const Point &aPoint)
+{
+  CGPathMoveToPoint(mCGPath, NULL, aPoint.x, aPoint.y);
+}
+
+void
+PathBuilderCG::LineTo(const Point &aPoint)
+{
+  if (CGPathIsEmpty(mCGPath))
+    MoveTo(aPoint);
+  else
+    CGPathAddLineToPoint(mCGPath, NULL, aPoint.x, aPoint.y);
+}
+
+void
+PathBuilderCG::BezierTo(const Point &aCP1,
+                         const Point &aCP2,
+                         const Point &aCP3)
+{
+
+  if (CGPathIsEmpty(mCGPath))
+    MoveTo(aCP1);
+  else
+    CGPathAddCurveToPoint(mCGPath, NULL,
+                          aCP1.x, aCP1.y,
+                          aCP2.x, aCP2.y,
+                          aCP3.x, aCP3.y);
+
+}
+
+void
+PathBuilderCG::QuadraticBezierTo(const Point &aCP1,
+                                  const Point &aCP2)
+{
+  if (CGPathIsEmpty(mCGPath))
+    MoveTo(aCP1);
+  else
+    CGPathAddQuadCurveToPoint(mCGPath, NULL,
+                              aCP1.x, aCP1.y,
+                              aCP2.x, aCP2.y);
+}
+
+void
+PathBuilderCG::Close()
+{
+  if (!CGPathIsEmpty(mCGPath))
+    CGPathCloseSubpath(mCGPath);
+}
+
+void
+PathBuilderCG::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
+                 Float aEndAngle, bool aAntiClockwise)
+{
+}
+
+Point
+PathBuilderCG::CurrentPoint() const
+{
+  CGPoint pt = CGPathGetCurrentPoint(mCGPath);
+  Point ret(pt.x, pt.y);
+  return ret;
+}
+
+void
+PathBuilderCG::EnsureActive(const Point &aPoint)
+{
+}
+
+TemporaryRef<Path>
+PathBuilderCG::Finish()
+{
+  RefPtr<PathCG> path = new PathCG(mCGPath, mFillRule);
+  return path;
+}
+
+TemporaryRef<PathBuilder>
+PathCG::CopyToBuilder(FillRule aFillRule) const
+{
+  CGMutablePathRef path = CGPathCreateMutableCopy(mPath);
+  RefPtr<PathBuilderCG> builder = new PathBuilderCG(path, aFillRule);
+  return builder;
+}
+
+
+
+TemporaryRef<PathBuilder>
+PathCG::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
+{
+  // 10.7 adds CGPathCreateMutableCopyByTransformingPath it might be faster than doing
+  // this by hand
+
+  struct TransformApplier {
+    CGMutablePathRef path;
+    CGAffineTransform transform;
+    static void
+    TranformCGPathApplierFunc(void *vinfo, const CGPathElement *element)
+    {
+      TransformApplier *info = reinterpret_cast<TransformApplier*>(vinfo);
+      switch (element->type) {
+        case kCGPathElementMoveToPoint:
+          {
+            CGPoint pt = element->points[0];
+            CGPathMoveToPoint(info->path, &info->transform, pt.x, pt.y);
+            break;
+          }
+        case kCGPathElementAddLineToPoint:
+          {
+            CGPoint pt = element->points[0];
+            CGPathAddLineToPoint(info->path, &info->transform, pt.x, pt.y);
+            break;
+          }
+        case kCGPathElementAddQuadCurveToPoint:
+          {
+            CGPoint pt  = element->points[0];
+            CGPoint cpt = element->points[1];
+            CGPathAddQuadCurveToPoint(info->path, &info->transform, cpt.x, cpt.y, pt.x, pt.y);
+            break;
+          }
+        case kCGPathElementAddCurveToPoint:
+          {
+            CGPoint pt   = element->points[0];
+            CGPoint cpt1 = element->points[1];
+            CGPoint cpt2 = element->points[2];
+            CGPathAddCurveToPoint(info->path, &info->transform, cpt1.x, cpt1.y, cpt2.x, cpt2.y, pt.x, pt.y);
+            break;
+          }
+        case kCGPathElementCloseSubpath:
+          {
+            CGPathCloseSubpath(info->path);
+            break;
+          }
+      }
+    }
+  };
+
+  TransformApplier ta;
+  ta.path = CGPathCreateMutable();
+  ta.transform = GfxMatrixToCGAffineTransform(aTransform);
+
+  CGPathApply(mPath, &ta, TransformApplier::TranformCGPathApplierFunc);
+  RefPtr<PathBuilderCG> builder = new PathBuilderCG(ta.path, aFillRule);
+  return builder;
+}
+
+
+bool
+PathCG::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
+{
+  Matrix inverse = aTransform;
+  inverse.Invert();
+  Point transformedPoint = inverse*aPoint;
+  // We could probably drop the input transform and just transform the point at the caller?
+  CGPoint point = {transformedPoint.x, transformedPoint.y};
+
+  // The transform parameter of CGPathContainsPoint doesn't seem to work properly on OS X 10.5
+  // so we transform aPoint ourselves.
+  return CGPathContainsPoint(mPath, NULL, point, mFillRule == FILL_EVEN_ODD);
+}
+
+static size_t
+PutBytesNull(void *info, const void *buffer, size_t count)
+{
+  return count;
+}
+
+/* The idea of a scratch context comes from WebKit */
+static CGContextRef
+CreateScratchContext()
+{
+  CGDataConsumerCallbacks callbacks = {PutBytesNull, NULL};
+  CGDataConsumerRef consumer = CGDataConsumerCreate(NULL, &callbacks);
+  CGContextRef cg = CGPDFContextCreate(consumer, NULL, NULL);
+  CGDataConsumerRelease(consumer);
+  return cg;
+}
+
+static CGContextRef
+ScratchContext()
+{
+  static CGContextRef cg = CreateScratchContext();
+  return cg;
+}
+
+//XXX: what should these functions return for an empty path?
+// currently they return CGRectNull {inf,inf, 0, 0}
+Rect
+PathCG::GetBounds(const Matrix &aTransform) const
+{
+  //XXX: are these bounds tight enough
+  Rect bounds = CGRectToRect(CGPathGetBoundingBox(mPath));
+  //XXX: curretnly this returns the bounds of the transformed bounds
+  // this is strictly looser than the bounds of the transformed path
+  return aTransform.TransformBounds(bounds);
+}
+
+Rect
+PathCG::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+                         const Matrix &aTransform) const
+{
+  // 10.7 has CGPathCreateCopyByStrokingPath which we could use
+  // instead of this scratch context business
+  CGContextRef cg = ScratchContext();
+
+  CGContextSaveGState(cg);
+
+  CGContextBeginPath(cg);
+  CGContextAddPath(cg, mPath);
+
+  SetStrokeOptions(cg, aStrokeOptions);
+
+  CGContextReplacePathWithStrokedPath(cg);
+  Rect bounds = CGRectToRect(CGContextGetPathBoundingBox(cg));
+
+  CGContextRestoreGState(cg);
+
+  return aTransform.TransformBounds(bounds);
+}
+
+
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/gfx/2d/PathCG.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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 Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bas Schouten <bschouten@mozilla.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_GFX_PATHCG_H_
+#define MOZILLA_GFX_PATHCG_H_
+
+#include <ApplicationServices/ApplicationServices.h>
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+class PathCG;
+
+class PathBuilderCG : public PathBuilder
+{
+public:
+  // absorbs a reference of aPath
+  PathBuilderCG(CGMutablePathRef aPath, FillRule aFillRule)
+    : mFigureActive(false)
+    , mFillRule(aFillRule)
+  {
+      mCGPath = aPath;
+  }
+
+  PathBuilderCG(FillRule aFillRule)
+    : mFigureActive(false)
+    , mFillRule(aFillRule)
+  {
+      mCGPath = CGPathCreateMutable();
+  }
+
+  virtual ~PathBuilderCG();
+
+  virtual void MoveTo(const Point &aPoint);
+  virtual void LineTo(const Point &aPoint);
+  virtual void BezierTo(const Point &aCP1,
+                        const Point &aCP2,
+                        const Point &aCP3);
+  virtual void QuadraticBezierTo(const Point &aCP1,
+                                 const Point &aCP2);
+  virtual void Close();
+  virtual void Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
+                   Float aEndAngle, bool aAntiClockwise = false);
+  virtual Point CurrentPoint() const;
+
+  virtual TemporaryRef<Path> Finish();
+
+private:
+  friend class PathCG;
+
+  void EnsureActive(const Point &aPoint);
+
+  CGMutablePathRef mCGPath;
+  bool mFigureActive;
+  Point mCurrentPoint;
+  Point mBeginPoint;
+  FillRule mFillRule;
+};
+
+class PathCG : public Path
+{
+public:
+  PathCG(CGMutablePathRef aPath, FillRule aFillRule)
+    : mPath(aPath)
+    , mFillRule(aFillRule)
+  {
+    CGPathRetain(mPath);
+  }
+  virtual ~PathCG() { CGPathRelease(mPath); }
+
+  virtual BackendType GetBackendType() const { return BACKEND_COREGRAPHICS; }
+
+  virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const;
+  virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
+                                                             FillRule aFillRule = FILL_WINDING) const;
+
+  virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
+  virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
+  virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+                                const Matrix &aTransform = Matrix()) const;
+
+  virtual FillRule GetFillRule() const { return mFillRule; }
+
+  CGMutablePathRef GetPath() const { return mPath; }
+
+private:
+  friend class DrawTargetCG;
+
+  CGMutablePathRef mPath;
+  bool mEndedActive;
+  Point mEndPoint;
+  FillRule mFillRule;
+};
+
+}
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/2d/ScaledFontBase.cpp
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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 Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@mozilla.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 "ScaledFontBase.h"
+#ifdef USE_SKIA
+#include "PathSkia.h"
+#include "skia/SkPaint.h"
+#include "skia/SkPath.h"
+#endif
+#include <vector>
+#include <cmath>
+using namespace std;
+#include "gfxFont.h"
+
+namespace mozilla {
+namespace gfx {
+#ifdef USE_SKIA
+static SkTypeface::Style gfxFontStyleToSkia(const gfxFontStyle* aStyle)
+{
+  if (aStyle->style == NS_FONT_STYLE_ITALIC) {
+    if (aStyle->weight == NS_FONT_WEIGHT_BOLD) {
+      return SkTypeface::kBoldItalic;
+    }
+    return SkTypeface::kItalic;
+  }
+  if (aStyle->weight == NS_FONT_WEIGHT_BOLD) {
+    return SkTypeface::kBold;
+  }
+  return SkTypeface::kNormal;
+}
+
+ScaledFontBase::ScaledFontBase(gfxFont* aFont, Float aSize)
+  : mSize(aSize)
+{
+  NS_LossyConvertUTF16toASCII name(aFont->GetName());
+  mTypeface = SkTypeface::CreateFromName(name.get(), gfxFontStyleToSkia(aFont->GetStyle()));
+}
+#endif
+
+ScaledFontBase::~ScaledFontBase()
+{
+#ifdef USE_SKIA
+  SkSafeUnref(mTypeface);
+#endif
+}
+
+ScaledFontBase::ScaledFontBase(Float aSize)
+  : mSize(aSize)
+{
+#ifdef USE_SKIA
+  mTypeface = NULL;
+#endif
+}
+
+
+TemporaryRef<Path>
+ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
+{
+#ifdef USE_SKIA
+  if (aTarget->GetType() == BACKEND_SKIA) {
+    SkPaint paint;
+    paint.setTypeface(GetSkTypeface());
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    paint.setTextSize(SkFloatToScalar(mSize));
+
+    std::vector<uint16_t> indices;
+    std::vector<SkPoint> offsets;
+    indices.resize(aBuffer.mNumGlyphs);
+    offsets.resize(aBuffer.mNumGlyphs);
+
+    for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+      indices[i] = aBuffer.mGlyphs[i].mIndex;
+      offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x);
+      offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y);
+    }
+
+    SkPath path;
+    paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path);
+    return new PathSkia(path, FILL_WINDING);
+  }
+#endif
+  return NULL;
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/2d/ScaledFontBase.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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 Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@mozilla.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_GFX_SCALEDFONTBASE_H_
+#define MOZILLA_GFX_SCALEDFONTBASE_H_
+
+#include "2D.h"
+#ifdef USE_SKIA
+#include "skia/SkTypeface.h"
+#endif
+
+class gfxFont;
+
+namespace mozilla {
+namespace gfx {
+
+class ScaledFontBase : public ScaledFont
+{
+public:
+  ScaledFontBase(Float aSize);
+  virtual ~ScaledFontBase();
+
+  virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
+#ifdef USE_SKIA
+  ScaledFontBase(gfxFont* aFont, Float aSize);
+  virtual SkTypeface* GetSkTypeface() { return mTypeface; }
+  virtual FontType GetType() const { return FONT_SKIA; }
+#endif
+
+protected:
+  friend class DrawTargetSkia;
+#ifdef USE_SKIA
+  SkTypeface* mTypeface;
+#endif
+  Float mSize;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_SCALEDFONTBASE_H_ */
--- a/gfx/2d/ScaledFontMac.cpp
+++ b/gfx/2d/ScaledFontMac.cpp
@@ -31,31 +31,82 @@
  * 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 "ScaledFontMac.h"
+#ifdef USE_SKIA
 #include "PathSkia.h"
 #include "skia/SkPaint.h"
 #include "skia/SkPath.h"
 #include "skia/SkTypeface_mac.h"
+#endif
+#include "DrawTargetCG.h"
 #include <vector>
 
+// prototype for private API
+extern "C" {
+CGPathRef CGFontGetGlyphPath(CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph);
+};
+
+
 namespace mozilla {
 namespace gfx {
 
 ScaledFontMac::ScaledFontMac(CGFontRef aFont, Float aSize)
-  : ScaledFontSkia(aSize)
+  : ScaledFontBase(aSize)
 {
-  mFontFace = CTFontCreateWithGraphicsFont(aFont, aSize, NULL, NULL);
-  mTypeface = SkCreateTypefaceFromCTFont(mFontFace);
+  // XXX: should we be taking a reference
+  mFont = CGFontRetain(aFont);
 }
 
 ScaledFontMac::~ScaledFontMac()
 {
-  CFRelease(mFontFace);
+  CGFontRelease(mFont);
+}
+
+#ifdef USE_SKIA
+SkTypeface* ScaledFontMac::GetSkTypeface()
+{
+  if (!mTypeface) {
+    CTFontRef fontFace = CTFontCreateWithGraphicsFont(mFont, mSize, NULL, NULL);
+    mTypeface = SkCreateTypefaceFromCTFont(fontFace);
+    CFRelease(fontFace);
+  }
+  return mTypeface;
+}
+#endif
+
+// private API here are the public options on OS X
+// CTFontCreatePathForGlyph
+// ATSUGlyphGetCubicPaths
+// we've used this in cairo sucessfully for some time.
+// Note: cairo dlsyms it. We could do that but maybe it's
+// safe just to use?
+
+TemporaryRef<Path>
+ScaledFontMac::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
+{
+  if (aTarget->GetType() == BACKEND_COREGRAPHICS) {
+      CGMutablePathRef path = CGPathCreateMutable();
+
+      for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+          // XXX: we could probably fold both of these transforms together to avoid extra work
+          CGAffineTransform flip = CGAffineTransformMakeScale(1, -1);
+          CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex);
+
+          CGAffineTransform matrix = CGAffineTransformMake(mSize, 0, 0, mSize,
+                                                           aBuffer.mGlyphs[i].mPosition.x,
+                                                           aBuffer.mGlyphs[i].mPosition.y);
+          CGPathAddPath(path, &matrix, glyphPath);
+          CGPathRelease(glyphPath);
+      }
+      return new PathCG(path, FILL_WINDING);
+  } else {
+      return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
+  }
 }
 
 }
 }
--- a/gfx/2d/ScaledFontMac.h
+++ b/gfx/2d/ScaledFontMac.h
@@ -33,33 +33,36 @@
  * 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_GFX_SCALEDFONTMAC_H_
 #define MOZILLA_GFX_SCALEDFONTMAC_H_
 
-#include "ScaledFontSkia.h"
 #import <ApplicationServices/ApplicationServices.h>
+#include "2D.h"
 
+#include "ScaledFontBase.h"
 
 namespace mozilla {
 namespace gfx {
 
-class ScaledFontMac : public ScaledFontSkia
+class ScaledFontMac : public ScaledFontBase
 {
 public:
   ScaledFontMac(CGFontRef aFont, Float aSize);
   virtual ~ScaledFontMac();
 
   virtual FontType GetType() const { return FONT_MAC; }
-
+#ifdef USE_SKIA
+  virtual SkTypeface* GetSkTypeface();
+#endif
+  virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
 private:
-  friend class DrawTargetSkia;
-
-  CTFontRef mFontFace;
+  friend class DrawTargetCG;
+  CGFontRef mFont;
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_SCALEDFONTMAC_H_ */
deleted file mode 100644
--- a/gfx/2d/ScaledFontSkia.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * ***** 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 Corporation code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Matt Woodrow <mwoodrow@mozilla.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 "ScaledFontSkia.h"
-#include "PathSkia.h"
-#include "skia/SkPaint.h"
-#include "skia/SkPath.h"
-#include <vector>
-#include <cmath>
-using namespace std;
-#include "gfxFont.h"
-
-namespace mozilla {
-namespace gfx {
-
-static SkTypeface::Style gfxFontStyleToSkia(const gfxFontStyle* aStyle)
-{
-  if (aStyle->style == NS_FONT_STYLE_ITALIC) {
-    if (aStyle->weight == NS_FONT_WEIGHT_BOLD) {
-      return SkTypeface::kBoldItalic;
-    }
-    return SkTypeface::kItalic;
-  }
-  if (aStyle->weight == NS_FONT_WEIGHT_BOLD) {
-    return SkTypeface::kBold;
-  }
-  return SkTypeface::kNormal;
-}
-
-ScaledFontSkia::ScaledFontSkia(gfxFont* aFont, Float aSize)
-  : mSize(aSize)
-{
-  NS_LossyConvertUTF16toASCII name(aFont->GetName());
-  mTypeface = SkTypeface::CreateFromName(name.get(), gfxFontStyleToSkia(aFont->GetStyle()));
-}
-
-ScaledFontSkia::ScaledFontSkia(Float aSize)
-  : mSize(aSize)
-{
-}
-
-ScaledFontSkia::~ScaledFontSkia()
-{
-  SkSafeUnref(mTypeface);
-}
-
-TemporaryRef<Path>
-ScaledFontSkia::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
-{
-  if (aTarget->GetType() != BACKEND_SKIA) {
-    return NULL;
-  }
-
-  SkPaint paint;
-  paint.setTypeface(mTypeface);
-  paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-  paint.setTextSize(SkFloatToScalar(mSize));
-  
-  std::vector<uint16_t> indices;
-  std::vector<SkPoint> offsets;
-  indices.resize(aBuffer.mNumGlyphs);
-  offsets.resize(aBuffer.mNumGlyphs);
-
-  for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
-    indices[i] = aBuffer.mGlyphs[i].mIndex;
-    offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x);
-    offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y);
-  }
-
-  SkPath path;
-  paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path);
-  return new PathSkia(path, FILL_WINDING);
-}
-
-}
-}
deleted file mode 100644
--- a/gfx/2d/ScaledFontSkia.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * ***** 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 Corporation code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Matt Woodrow <mwoodrow@mozilla.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_GFX_SCALEDFONTSKIA_H_
-#define MOZILLA_GFX_SCALEDFONTSKIA_H_
-
-#include "2D.h"
-#include "skia/SkTypeface.h"
-
-class gfxFont;
-
-namespace mozilla {
-namespace gfx {
-
-class ScaledFontSkia : public ScaledFont
-{
-public:
-  ScaledFontSkia(gfxFont* aFont, Float aSize);
-  ScaledFontSkia(Float aSize);
-  virtual ~ScaledFontSkia();
-
-  virtual FontType GetType() const { return FONT_SKIA; }
-
-  virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
-
-protected:
-  friend class DrawTargetSkia;
-
-  SkTypeface* mTypeface;
-  Float mSize;
-};
-
-}
-}
-
-#endif /* MOZILLA_GFX_SCALEDFONTSKIA_H_ */
--- a/gfx/2d/ScaledFontWin.cpp
+++ b/gfx/2d/ScaledFontWin.cpp
@@ -31,23 +31,37 @@
  * 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 "ScaledFontWin.h"
+#include "ScaeldFontBase.h"
+
+#ifdef USE_SKIA
 #include "skia/SkTypeface_win.h"
+#endif
 
 namespace mozilla {
 namespace gfx {
 
 ScaledFontWin::ScaledFontWin(gfxGDIFont* aFont, Float aSize)
-  : ScaledFontSkia(aSize)
+  : ScaledFontBase(aSize)
 {
   LOGFONT lf;
   GetObject(aFont->GetHFONT(), sizeof(LOGFONT), &lf);
-  mTypeface = SkCreateTypefaceFromLOGFONT(lf);
 }
 
+#ifdef USE_SKIA
+SkTypeface* ScaledFontWin::GetSkTypeface()
+{
+  if (!mTypeface) {
+    mTypeface = SkCreateTypefaceFromLOGFONT(lf);
+  }
+  return mTypeface;
+}
+#endif
+
+
 }
 }
--- a/gfx/2d/ScaledFontWin.h
+++ b/gfx/2d/ScaledFontWin.h
@@ -33,29 +33,33 @@
  * 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_GFX_SCALEDFONTWIN_H_
 #define MOZILLA_GFX_SCALEDFONTWIN_H_
 
-#include "ScaledFontSkia.h"
+#include "ScaledFontBase.h"
 #include "gfxGDIFont.h"
 
 namespace mozilla {
 namespace gfx {
 
-class ScaledFontWin : public ScaledFontSkia
+class ScaledFontWin : public ScaledFontBase
 {
 public:
   ScaledFontWin(gfxGDIFont* aFont, Float aSize);
 
   virtual FontType GetType() const { return FONT_GDI; }
-
+#ifdef USE_SKIA
+  virtual SkTypeface* GetSkTypeface();
+#endif
 private:
+#ifdef USE_SKIA
   friend class DrawTargetSkia;
+#endif
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_SCALEDFONTWIN_H_ */
--- a/gfx/2d/SourceSurfaceCG.cpp
+++ b/gfx/2d/SourceSurfaceCG.cpp
@@ -35,47 +35,47 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "SourceSurfaceCG.h"
 
 namespace mozilla {
 namespace gfx {
 
-SourceSurfaceCG::SourceSurfaceCG()
-{
-}
 
 SourceSurfaceCG::~SourceSurfaceCG()
 {
   CGImageRelease(mImage);
 }
 
 IntSize
 SourceSurfaceCG::GetSize() const
 {
   IntSize size;
-  size.width = CGImageGetHeight(mImage);
-  size.height = CGImageGetWidth(mImage);
+  size.width = CGImageGetWidth(mImage);
+  size.height = CGImageGetHeight(mImage);
   return size;
 }
 
 SurfaceFormat
 SourceSurfaceCG::GetFormat() const
 {
   return mFormat;
 }
 
 TemporaryRef<DataSourceSurface>
 SourceSurfaceCG::GetDataSurface()
 {
-  return NULL;
+  //XXX: we should be more disciplined about who takes a reference and where
+  CGImageRetain(mImage);
+  RefPtr<DataSourceSurfaceCG> dataSurf =
+    new DataSourceSurfaceCG(mImage);
+  return dataSurf;
 }
 
-
 static void releaseCallback(void *info, const void *data, size_t size) {
   free(info);
 }
 
 bool
 SourceSurfaceCG::InitFromData(unsigned char *aData,
                                const IntSize &aSize,
                                int32_t aStride,
@@ -83,48 +83,50 @@ SourceSurfaceCG::InitFromData(unsigned c
 {
   //XXX: we should avoid creating this colorspace everytime
   CGColorSpaceRef colorSpace = NULL;
   CGBitmapInfo bitinfo = 0;
   CGDataProviderRef dataProvider = NULL;
   int bitsPerComponent = 0;
   int bitsPerPixel = 0;
 
+  assert(aSize.width >= 0 && aSize.height >= 0);
+
   switch (aFormat) {
-    case B8G8R8A8:
+    case FORMAT_B8G8R8A8:
       colorSpace = CGColorSpaceCreateDeviceRGB();
       bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
       bitsPerComponent = 8;
       bitsPerPixel = 32;
       break;
 
-    case B8G8R8X8:
+    case FORMAT_B8G8R8X8:
       colorSpace = CGColorSpaceCreateDeviceRGB();
       bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
       bitsPerComponent = 8;
       bitsPerPixel = 32;
       break;
 
-    case A8:
+    case FORMAT_A8:
       // XXX: why don't we set a colorspace here?
       bitsPerComponent = 8;
       bitsPerPixel = 8;
   };
 
   void *data = malloc(aStride * aSize.height);
   memcpy(data, aData, aStride * aSize.height);
 
   mFormat = aFormat;
 
   dataProvider = CGDataProviderCreateWithData (data,
                                                data,
 					       aSize.height * aStride,
 					       releaseCallback);
 
-  if (aFormat == A8) {
+  if (aFormat == FORMAT_A8) {
     CGFloat decode[] = {1.0, 0.0};
     mImage = CGImageMaskCreate (aSize.width, aSize.height,
 				bitsPerComponent,
 				bitsPerPixel,
 				aStride,
 				dataProvider,
 				decode,
 				true);
@@ -140,17 +142,178 @@ SourceSurfaceCG::InitFromData(unsigned c
 			    NULL,
 			    true,
 			    kCGRenderingIntentDefault);
   }
 
   CGDataProviderRelease(dataProvider);
   CGColorSpaceRelease (colorSpace);
 
-  if (mImage) {
-    return false;
+  return mImage != NULL;
+}
+
+DataSourceSurfaceCG::~DataSourceSurfaceCG()
+{
+  CGImageRelease(mImage);
+  free(CGBitmapContextGetData(mCg));
+  CGContextRelease(mCg);
+}
+
+IntSize
+DataSourceSurfaceCG::GetSize() const
+{
+  IntSize size;
+  size.width = CGImageGetWidth(mImage);
+  size.height = CGImageGetHeight(mImage);
+  return size;
+}
+
+bool
+DataSourceSurfaceCG::InitFromData(unsigned char *aData,
+                               const IntSize &aSize,
+                               int32_t aStride,
+                               SurfaceFormat aFormat)
+{
+  //XXX: we should avoid creating this colorspace everytime
+  CGColorSpaceRef colorSpace = NULL;
+  CGBitmapInfo bitinfo = 0;
+  CGDataProviderRef dataProvider = NULL;
+  int bitsPerComponent = 0;
+  int bitsPerPixel = 0;
+
+  switch (aFormat) {
+    case FORMAT_B8G8R8A8:
+      colorSpace = CGColorSpaceCreateDeviceRGB();
+      bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
+      bitsPerComponent = 8;
+      bitsPerPixel = 32;
+      break;
+
+    case FORMAT_B8G8R8X8:
+      colorSpace = CGColorSpaceCreateDeviceRGB();
+      bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
+      bitsPerComponent = 8;
+      bitsPerPixel = 32;
+      break;
+
+    case FORMAT_A8:
+      // XXX: why don't we set a colorspace here?
+      bitsPerComponent = 8;
+      bitsPerPixel = 8;
+  };
+
+  void *data = malloc(aStride * aSize.height);
+  memcpy(data, aData, aStride * aSize.height);
+
+  //mFormat = aFormat;
+
+  dataProvider = CGDataProviderCreateWithData (data,
+                                               data,
+					       aSize.height * aStride,
+					       releaseCallback);
+
+  if (aFormat == FORMAT_A8) {
+    CGFloat decode[] = {1.0, 0.0};
+    mImage = CGImageMaskCreate (aSize.width, aSize.height,
+				bitsPerComponent,
+				bitsPerPixel,
+				aStride,
+				dataProvider,
+				decode,
+				true);
+
+  } else {
+    mImage = CGImageCreate (aSize.width, aSize.height,
+			    bitsPerComponent,
+			    bitsPerPixel,
+			    aStride,
+			    colorSpace,
+			    bitinfo,
+			    dataProvider,
+			    NULL,
+			    true,
+			    kCGRenderingIntentDefault);
   }
 
-  return true;
+  CGDataProviderRelease(dataProvider);
+  CGColorSpaceRelease (colorSpace);
+
+  return mImage;
+}
+
+CGContextRef CreateBitmapContextForImage(CGImageRef image)
+{
+  CGColorSpaceRef colorSpace;
+
+  size_t width  = CGImageGetWidth(image);
+  size_t height = CGImageGetHeight(image);
+
+  int bitmapBytesPerRow = (width * 4);
+  int bitmapByteCount   = (bitmapBytesPerRow * height);
+
+  void *data = calloc(bitmapByteCount, 1);
+  //XXX: which color space should we be using here?
+  colorSpace = CGColorSpaceCreateDeviceRGB();
+  assert(colorSpace);
+
+  // we'd like to pass NULL as the first parameter
+  // to let Quartz manage this memory for us. However,
+  // on 10.5 and older CGBitmapContextGetData will return
+  // NULL instead of the associated buffer so we need
+  // to manage it ourselves.
+  CGContextRef cg = CGBitmapContextCreate(data,
+                                          width,
+                                          height,
+                                          8,
+                                          bitmapBytesPerRow,
+                                          colorSpace,
+                                          kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
+  assert(cg);
+
+  CGColorSpaceRelease(colorSpace);
+
+  return cg;
 }
 
+DataSourceSurfaceCG::DataSourceSurfaceCG(CGImageRef aImage)
+{
+  mImage = aImage;
+  mCg = CreateBitmapContextForImage(aImage);
+  if (mCg == NULL) {
+    // error creating context
+    return;
+  }
+
+  // Get image width, height. We'll use the entire image.
+  CGFloat w = CGImageGetWidth(aImage);
+  CGFloat h = CGImageGetHeight(aImage);
+  CGRect rect = {{0,0},{w,h}};
+
+  // Draw the image to the bitmap context. Once we draw, the memory
+  // allocated for the context for rendering will then contain the
+  // raw image data in the specified color space.
+  CGContextDrawImage(mCg, rect, aImage);
+
+  // Now we can get a pointer to the image data associated with the bitmap
+  // context.
+  mData = CGBitmapContextGetData(mCg);
+  assert(mData);
+}
+
+unsigned char *
+DataSourceSurfaceCG::GetData()
+{
+  // See http://developer.apple.com/library/mac/#qa/qa1509/_index.html
+  // the following only works on 10.5+, the Q&A above suggests a method
+  // that can be used for earlier versions
+  //CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
+  //unsigned char *dataPtr = CFDataGetBytePtr(data);
+  //CFDataRelease(data);
+  // unfortunately the the method above only works for read-only access and
+  // we need read-write for DataSourceSurfaces
+
+  return (unsigned char*)mData;
+}
+
+
+
 }
 }
--- a/gfx/2d/SourceSurfaceCG.h
+++ b/gfx/2d/SourceSurfaceCG.h
@@ -42,20 +42,21 @@
 #include "2D.h"
 
 namespace mozilla {
 namespace gfx {
 
 class SourceSurfaceCG : public SourceSurface
 {
 public:
-  SourceSurfaceCG();
+  SourceSurfaceCG() {}
+  SourceSurfaceCG(CGImageRef aImage) : mImage(aImage) {}
   ~SourceSurfaceCG();
 
-  virtual SurfaceType GetType() const { return COREGRAPHICS_IMAGE; }
+  virtual SurfaceType GetType() const { return SURFACE_COREGRAPHICS_IMAGE; }
   virtual IntSize GetSize() const;
   virtual SurfaceFormat GetFormat() const;
   virtual TemporaryRef<DataSourceSurface> GetDataSurface();
 
   CGImageRef GetImage() { return mImage; }
 
   bool InitFromData(unsigned char *aData,
                     const IntSize &aSize,
@@ -66,10 +67,43 @@ private:
   CGImageRef mImage;
 
   /* It might be better to just use the bitmap info from the CGImageRef to
    * deduce the format to save space in SourceSurfaceCG,
    * for now we just store it in mFormat */
   SurfaceFormat mFormat;
 };
 
+class DataSourceSurfaceCG : public DataSourceSurface
+{
+public:
+  DataSourceSurfaceCG() {}
+  DataSourceSurfaceCG(CGImageRef aImage);
+  ~DataSourceSurfaceCG();
+
+  virtual SurfaceType GetType() const { return SURFACE_DATA; }
+  virtual IntSize GetSize() const;
+  virtual SurfaceFormat GetFormat() const { return FORMAT_B8G8R8A8; }
+
+  CGImageRef GetImage() { return mImage; }
+
+  bool InitFromData(unsigned char *aData,
+                    const IntSize &aSize,
+                    int32_t aStride,
+                    SurfaceFormat aFormat);
+
+  virtual unsigned char *GetData();
+
+  virtual int32_t Stride() { return CGImageGetBytesPerRow(mImage); }
+
+
+private:
+  CGContextRef mCg;
+  CGImageRef mImage;
+  //XXX: we don't need to store mData we can just get it from the CGContext
+  void *mData;
+  /* It might be better to just use the bitmap info from the CGImageRef to
+   * deduce the format to save space in SourceSurfaceCG,
+   * for now we just store it in mFormat */
+};
+
 }
 }
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -75,23 +75,25 @@ enum BackendType
 };
 
 enum FontType
 {
   FONT_DWRITE,
   FONT_GDI,
   FONT_MAC,
   FONT_SKIA,
-  FONT_CAIRO
+  FONT_CAIRO,
+  FONT_COREGRAPHICS
 };
 
 enum NativeSurfaceType
 {
   NATIVE_SURFACE_D3D10_TEXTURE,
-  NATIVE_SURFACE_CAIRO_SURFACE
+  NATIVE_SURFACE_CAIRO_SURFACE,
+  NATIVE_SURFACE_CGCONTEXT
 };
 
 enum NativeFontType
 {
   NATIVE_FONT_DWRITE_FONT_FACE,
   NATIVE_FONT_GDI_FONT_FACE,
   NATIVE_FONT_MAC_FONT_FACE,
   NATIVE_FONT_SKIA_FONT_FACE,
new file mode 100644
--- /dev/null
+++ b/gfx/2d/UserData.h
@@ -0,0 +1,108 @@
+/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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 Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (Sub) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jeff Muizelaar <jmuizelaar@mozilla.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 <stdlib.h>
+#include "mozilla/mozalloc.h"
+
+namespace mozilla {
+namespace gfx {
+
+struct UserDataKey {
+  int unused;
+};
+
+/* this class is basically a clone of the user data concept from cairo */
+class UserData
+{
+  typedef void (*destroyFunc)(void *data);
+public:
+  UserData() : count(0), entries(NULL) {}
+
+  /* Attaches untyped userData associated with key. destroy is called on destruction */
+  void Add(UserDataKey *key, void *userData, destroyFunc destroy)
+  {
+    // We could keep entries in a std::vector instead of managing it by hand
+    // but that would propagate an stl dependency out which we'd rather not
+    // do (see bug 666609). Plus, the entries array is expect to stay small
+    // so doing a realloc everytime we add a new entry shouldn't be too costly
+    entries = static_cast<Entry*>(moz_xrealloc(entries, sizeof(Entry)*(count+1)));
+
+    entries[count].key      = key;
+    entries[count].userData = userData;
+    entries[count].destroy  = destroy;
+
+    count++;
+  }
+
+  //XXX: we probably want to add a way to remove Keys
+
+  /* Retrives the userData for the associated key */
+  void *Get(UserDataKey *key)
+  {
+    for (int i=0; i<count; i++) {
+      if (key == entries[i].key) {
+        return entries[i].userData;
+      }
+    }
+    return NULL;
+  }
+
+  ~UserData()
+  {
+    for (int i=0; i<count; i++) {
+      entries[i].destroy(entries[i].userData);
+    }
+    free(entries);
+  }
+
+private:
+  struct Entry {
+    const UserDataKey *key;
+    void *userData;
+    destroyFunc destroy;
+  };
+
+  int count;
+  Entry *entries;
+
+};
+
+}
+}
+
+
--- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp
@@ -85,24 +85,27 @@ CanvasLayerD3D9::Initialize(const Data& 
   mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
 
   CreateTexture();
 }
 
 void
 CanvasLayerD3D9::UpdateSurface()
 {
-  if (!mDirty)
+  if (!mDirty && mTexture)
     return;
   mDirty = false;
 
   if (!mTexture) {
     CreateTexture();
-    NS_WARNING("CanvasLayerD3D9::Updated called but no texture present!");
-    return;
+
+    if (!mTexture) {
+      NS_WARNING("CanvasLayerD3D9::Updated called but no texture present and creation failed!");
+      return;
+    }
   }
 
   if (mGLContext) {
     // WebGL reads entire surface.
     LockTextureRectD3D9 textureLock(mTexture);
     if (!textureLock.HasLock()) {
       NS_WARNING("Failed to lock CanvasLayer texture.");
       return;
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -678,17 +678,17 @@ struct gfxTextRange {
  * by the fonts if they get aged three times without being re-used in the
  * meantime.
  *
  * Note that the ShapedWord timeout is much larger than the font timeout,
  * so that in the case of a short-lived font, we'll discard the gfxFont
  * completely, with all its words, and avoid the cost of aging the words
  * individually. That only happens with longer-lived fonts.
  */
-class THEBES_API gfxFontCache : public nsExpirationTracker<gfxFont,3> {
+class THEBES_API gfxFontCache MOZ_FINAL : public nsExpirationTracker<gfxFont,3> {
 public:
     enum {
         FONT_TIMEOUT_SECONDS = 10,
         SHAPED_WORD_TIMEOUT_SECONDS = 60
     };
 
     gfxFontCache();
     ~gfxFontCache();
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -527,35 +527,58 @@ gfxPlatform::GetScaledFontForFont(gfxFon
 
 cairo_user_data_key_t kDrawSourceSurface;
 static void
 DataSourceSurfaceDestroy(void *dataSourceSurface)
 {
       static_cast<DataSourceSurface*>(dataSourceSurface)->Release();
 }
 
+void DestroyThebesSurface(void *data)
+{
+  gfxASurface *surface = static_cast<gfxASurface*>(data);
+  surface->Release();
+}
+
+UserDataKey ThebesSurfaceKey;
+
+// The semantics of this function are sort of weird. We snapshot the first
+// time and then return the snapshotted surface for the lifetime of the
+// draw target
 already_AddRefed<gfxASurface>
 gfxPlatform::GetThebesSurfaceForDrawTarget(DrawTarget *aTarget)
 {
+  void *surface = aTarget->GetUserData(&ThebesSurfaceKey);
+  if (surface) {
+    nsRefPtr<gfxASurface> surf = static_cast<gfxASurface*>(surface);
+    return surf.forget();
+  }
+
   RefPtr<SourceSurface> source = aTarget->Snapshot();
   RefPtr<DataSourceSurface> data = source->GetDataSurface();
 
   if (!data) {
     return NULL;
   }
 
   IntSize size = data->GetSize();
   gfxASurface::gfxImageFormat format = gfxASurface::FormatFromContent(ContentForFormat(data->GetFormat()));
   
-  nsRefPtr<gfxImageSurface> image =
+  nsRefPtr<gfxImageSurface> surf =
     new gfxImageSurface(data->GetData(), gfxIntSize(size.width, size.height),
                         data->Stride(), format);
 
-  image->SetData(&kDrawSourceSurface, data.forget().drop(), DataSourceSurfaceDestroy);
-  return image.forget();
+  surf->SetData(&kDrawSourceSurface, data.forget().drop(), DataSourceSurfaceDestroy);
+
+  // add a reference to be held by the drawTarget
+  // careful, the reference graph is getting complicated here
+  surf->AddRef();
+  aTarget->AddUserData(&ThebesSurfaceKey, surf.get(), DestroyThebesSurface);
+
+  return surf.forget();
 }
 
 RefPtr<DrawTarget>
 gfxPlatform::CreateOffscreenDrawTarget(const IntSize& aSize, SurfaceFormat aFormat)
 {
   BackendType backend;
   if (!SupportsAzure(backend)) {
     return NULL;
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -66,16 +66,19 @@ struct gfxFontStyle;
 class gfxUserFontSet;
 class gfxFontEntry;
 class gfxProxyFontEntry;
 class gfxPlatformFontList;
 class gfxTextRun;
 class nsIURI;
 class nsIAtom;
 
+extern mozilla::gfx::UserDataKey ThebesSurfaceKey;
+void DestroyThebesSurface(void *data);
+
 extern cairo_user_data_key_t kDrawTarget;
 
 // pref lang id's for font prefs
 // !!! needs to match the list of pref font.default.xx entries listed in all.js !!!
 // !!! don't use as bit mask, this may grow larger !!!
 
 enum eFontPrefLang {
     eFontPrefLang_Western     =  0,
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -52,16 +52,17 @@
 #include "nsTArray.h"
 #include "nsUnicodeRange.h"
 
 #include "mozilla/Preferences.h"
 
 #include "qcms.h"
 
 #include <dlfcn.h>
+#include "mozilla/gfx/2D.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 // cribbed from CTFontManager.h
 enum {
    kAutoActivationDisabled = 1
 };
@@ -126,17 +127,17 @@ gfxPlatformMac::CreateOffscreenSurface(c
 {
     gfxASurface *newSurface = nsnull;
 
     newSurface = new gfxQuartzSurface(size, gfxASurface::FormatFromContent(contentType));
 
     NS_IF_ADDREF(newSurface);
     return newSurface;
 }
-    
+
 already_AddRefed<gfxASurface>
 gfxPlatformMac::OptimizeImage(gfxImageSurface *aSurface,
                               gfxASurface::gfxImageFormat format)
 {
     const gfxIntSize& surfaceSize = aSurface->GetSize();
     nsRefPtr<gfxImageSurface> isurf = aSurface;
 
     if (format != aSurface->Format()) {
@@ -157,17 +158,17 @@ gfxPlatformMac::GetScaledFontForFont(gfx
 {
     gfxMacFont *font = static_cast<gfxMacFont*>(aFont);
     return font->GetScaledFont();
 }
 
 bool
 gfxPlatformMac::SupportsAzure(BackendType& aBackend)
 {
-  aBackend = BACKEND_SKIA;
+  aBackend = BACKEND_COREGRAPHICS;
   return true;
 }
 
 nsresult
 gfxPlatformMac::ResolveFontName(const nsAString& aFontName,
                                 FontResolverCallback aCallback,
                                 void *aClosure, bool& aAborted)
 {
@@ -293,16 +294,46 @@ gfxPlatformMac::ReadAntiAliasingThreshol
             threshold = 0;
         }
         CFRelease(prefValue);
     }
 
     return threshold;
 }
 
+already_AddRefed<gfxASurface>
+gfxPlatformMac::GetThebesSurfaceForDrawTarget(DrawTarget *aTarget)
+{
+  if (aTarget->GetType() == BACKEND_COREGRAPHICS) {
+    void *surface = aTarget->GetUserData(&ThebesSurfaceKey);
+    if (surface) {
+      nsRefPtr<gfxASurface> surf = static_cast<gfxQuartzSurface*>(surface);
+      return surf.forget();
+    } else {
+      CGContextRef cg = static_cast<CGContextRef>(aTarget->GetNativeSurface(NATIVE_SURFACE_CGCONTEXT));
+
+      //XXX: it would be nice to have an implicit conversion from IntSize to gfxIntSize
+      IntSize intSize = aTarget->GetSize();
+      gfxIntSize size(intSize.width, intSize.height);
+
+      nsRefPtr<gfxASurface> surf =
+        new gfxQuartzSurface(cg, size);
+
+      // add a reference to be held by the drawTarget
+      surf->AddRef();
+      aTarget->AddUserData(&ThebesSurfaceKey, surf.get(), DestroyThebesSurface);
+
+      return surf.forget();
+    }
+  }
+
+  return gfxPlatform::GetThebesSurfaceForDrawTarget(aTarget);
+}
+
+
 qcms_profile *
 gfxPlatformMac::GetPlatformCMSOutputProfile()
 {
     qcms_profile *profile = nsnull;
     CMProfileRef cmProfile;
     CMProfileLocation *location;
     UInt32 locationSize;
 
--- a/gfx/thebes/gfxPlatformMac.h
+++ b/gfx/thebes/gfxPlatformMac.h
@@ -45,16 +45,17 @@
 #define MAC_OS_X_VERSION_10_4_HEX 0x00001040
 #define MAC_OS_X_VERSION_10_5_HEX 0x00001050
 #define MAC_OS_X_VERSION_10_6_HEX 0x00001060
 #define MAC_OS_X_VERSION_10_7_HEX 0x00001070
 
 #define MAC_OS_X_MAJOR_VERSION_MASK 0xFFFFFFF0U
 
 class gfxTextRun;
+class mozilla::gfx::DrawTarget;
 
 class THEBES_API gfxPlatformMac : public gfxPlatform {
 public:
     gfxPlatformMac();
     virtual ~gfxPlatformMac();
 
     static gfxPlatformMac *GetPlatform() {
         return (gfxPlatformMac*) gfxPlatform::GetPlatform();
@@ -99,16 +100,18 @@ public:
 
     // Returns the OS X version as returned from Gestalt(gestaltSystemVersion, ...)
     // Ex: Mac OS X 10.4.x ==> 0x104x 
     PRInt32 OSXVersion();
 
     // lower threshold on font anti-aliasing
     PRUint32 GetAntiAliasingThreshold() { return mFontAntiAliasingThreshold; }
 
+    virtual already_AddRefed<gfxASurface>
+    GetThebesSurfaceForDrawTarget(mozilla::gfx::DrawTarget *aTarget);
 private:
     virtual qcms_profile* GetPlatformCMSOutputProfile();
     
     // read in the pref value for the lower threshold on font anti-aliasing
     static PRUint32 ReadAntiAliasingThreshold();    
     
     PRInt32 mOSXVersion;
     PRUint32 mFontAntiAliasingThreshold;
--- a/gfx/thebes/gfxQuartzSurface.cpp
+++ b/gfx/thebes/gfxQuartzSurface.cpp
@@ -76,16 +76,36 @@ gfxQuartzSurface::gfxQuartzSurface(CGCon
     gfxIntSize size((unsigned int) floor(desiredSize.width),
                     (unsigned int) floor(desiredSize.height));
     if (!CheckSurfaceSize(size))
         MakeInvalid();
 
     unsigned int width = static_cast<unsigned int>(mSize.width);
     unsigned int height = static_cast<unsigned int>(mSize.height);
 
+    cairo_surface_t *surf =
+        cairo_quartz_surface_create_for_cg_context(context,
+                                                   width, height);
+
+    CGContextRetain(mCGContext);
+
+    Init(surf);
+}
+
+gfxQuartzSurface::gfxQuartzSurface(CGContextRef context,
+                                   const gfxIntSize& size,
+                                   bool aForPrinting)
+    : mCGContext(context), mSize(size), mForPrinting(aForPrinting)
+{
+    if (!CheckSurfaceSize(size))
+        MakeInvalid();
+
+    unsigned int width = static_cast<unsigned int>(mSize.width);
+    unsigned int height = static_cast<unsigned int>(mSize.height);
+
     cairo_surface_t *surf = 
         cairo_quartz_surface_create_for_cg_context(context,
                                                    width, height);
 
     CGContextRetain(mCGContext);
 
     Init(surf);
 }
--- a/gfx/thebes/gfxQuartzSurface.h
+++ b/gfx/thebes/gfxQuartzSurface.h
@@ -45,16 +45,17 @@
 #include <Carbon/Carbon.h>
 
 class gfxContext;
 
 class THEBES_API gfxQuartzSurface : public gfxASurface {
 public:
     gfxQuartzSurface(const gfxSize& size, gfxImageFormat format, bool aForPrinting = false);
     gfxQuartzSurface(CGContextRef context, const gfxSize& size, bool aForPrinting = false);
+    gfxQuartzSurface(CGContextRef context, const gfxIntSize& size, bool aForPrinting = false);
     gfxQuartzSurface(cairo_surface_t *csurf, bool aForPrinting = false);
     gfxQuartzSurface(unsigned char *data, const gfxSize& size, long stride, gfxImageFormat format, bool aForPrinting = false);
 
     virtual ~gfxQuartzSurface();
 
     virtual already_AddRefed<gfxASurface> CreateSimilarSurface(gfxContentType aType,
                                                                const gfxIntSize& aSize);
 
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -502,31 +502,45 @@ gfxWindowsPlatform::GetScaledFontForFont
   return scaledFont;
 }
 
 already_AddRefed<gfxASurface>
 gfxWindowsPlatform::GetThebesSurfaceForDrawTarget(DrawTarget *aTarget)
 {
 #ifdef XP_WIN
   if (aTarget->GetType() == BACKEND_DIRECT2D) {
-    RefPtr<ID3D10Texture2D> texture =
-      static_cast<ID3D10Texture2D*>(aTarget->GetNativeSurface(NATIVE_SURFACE_D3D10_TEXTURE));
+    void *surface = aTarget->GetUserData(&ThebesSurfaceKey);
+    if (surface) {
+      nsRefPtr<gfxASurface> surf = static_cast<gfxASurface*>(surface);
+      return surf.forget();
+    } else {
+      RefPtr<ID3D10Texture2D> texture =
+        static_cast<ID3D10Texture2D*>(aTarget->GetNativeSurface(NATIVE_SURFACE_D3D10_TEXTURE));
 
-    if (!texture) {
-      return gfxPlatform::GetThebesSurfaceForDrawTarget(aTarget);
-    }
+      if (!texture) {
+        return gfxPlatform::GetThebesSurfaceForDrawTarget(aTarget);
+      }
+
+      aTarget->Flush();
 
-    aTarget->Flush();
+      nsRefPtr<gfxASurface> surf =
+        new gfxD2DSurface(texture, ContentForFormat(aTarget->GetFormat()));
 
-    nsRefPtr<gfxASurface> surf =
-      new gfxD2DSurface(texture, ContentForFormat(aTarget->GetFormat()));
+      // add a reference to be held by the drawTarget
+      surf->AddRef();
+      aTarget->AddUserData(&ThebesSurfaceKey, surf.get(), DestroyThebesSurface);
+      /* "It might be worth it to clear cairo surfaces associated with a drawtarget.
+	  The strong reference means for example for D2D that cairo's scratch surface
+	  will be kept alive (well after a user being done) and consume extra VRAM.
+	  We can deal with this in a follow-up though." */
 
-    surf->SetData(&kDrawTarget, aTarget, NULL);
-
-    return surf.forget();
+      // shouldn't this hold a reference?
+      surf->SetData(&kDrawTarget, aTarget, NULL);
+      return surf.forget();
+    }
   }
 #endif
 
   return gfxPlatform::GetThebesSurfaceForDrawTarget(aTarget);
 }
 
 bool
 gfxWindowsPlatform::SupportsAzure(BackendType& aBackend)
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2576,29 +2576,16 @@ CanOptimizeForDenseStorage(JSObject *arr
     if (JS_UNLIKELY(arr->getType(cx)->hasAllFlags(OBJECT_FLAG_ITERATED)))
         return false;
 
     /* Now just watch out for getters and setters along the prototype chain. */
     return !js_PrototypeHasIndexedProperties(cx, arr) &&
            startingIndex + count <= arr->getDenseArrayInitializedLength();
 }
 
-static inline bool
-CopyArrayElement(JSContext *cx, JSObject *source, uint32_t sourceIndex,
-                 JSObject *target, uint32_t targetIndex)
-{
-    if (!JS_CHECK_OPERATION_LIMIT(cx))
-        return false;
-
-    JSBool hole;
-    Value fromValue;
-    return GetElement(cx, source, sourceIndex, &hole, &fromValue) &&
-           SetOrDeleteArrayElement(cx, target, targetIndex, hole, fromValue);
-}
-
 /* ES5 15.4.4.12. */
 static JSBool
 array_splice(JSContext *cx, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     JSObject *obj = ToObject(cx, &args.thisv());
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -560,22 +560,16 @@ XPCJSRuntime::AddXPConnectRoots(JSContex
     // hold them so that the JS objects are colored correctly in the cycle
     // collector. This includes JSContexts that don't have outstanding requests,
     // because their global object wasn't marked by the JS GC. All other JS
     // roots were marked by the JS GC and will be colored correctly in the cycle
     // collector.
 
     JSContext *iter = nsnull, *acx;
     while ((acx = JS_ContextIterator(GetJSRuntime(), &iter))) {
-        // Only skip JSContexts with outstanding requests if the
-        // callback does not want all traces (a debug feature).
-        // Otherwise, we do want to know about all JSContexts to get
-        // better graphs and explanations.
-        if (!cb.WantAllTraces() && nsXPConnect::GetXPConnect()->GetOutstandingRequests(acx))
-            continue;
         cb.NoteRoot(nsIProgrammingLanguage::CPLUSPLUS, acx,
                     nsXPConnect::JSContextParticipant());
     }
 
     XPCAutoLock lock(mMapLock);
 
     XPCWrappedNativeScope::SuspectAllWrappers(this, cx, cb);
 
--- a/layout/reftests/canvas/reftest.list
+++ b/layout/reftests/canvas/reftest.list
@@ -42,19 +42,23 @@ asserts-if(cocoaWidget,0-2) == size-chan
 == text-bidi-rtl-test.html text-bidi-rtl-ref.html
 
 fails-if(Android) != text-font-lang.html text-font-lang-notref.html
 
 == text-measure.html text-measure-ref.html
 
 == strokeText-path.html strokeText-path-ref.html
 
-# gradient off-by-one, fails on windows and linux
-== linear-gradient-1a.html linear-gradient-1-ref.html
-fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) == linear-gradient-1b.html linear-gradient-1-ref.html
+# azure quartz uses CGDrawLinearGradient instead of DrawShading
+# so we have less control over degenerate behaviour as tested by this
+# test
+fails-if(azureQuartz) == linear-gradient-1a.html linear-gradient-1-ref.html
+
+# this passes with cairo on 10.7 but not with azure for reasons unknown
+fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)||(azureQuartz&&/Mac\x20OS\x20X\x2010\.7/.test(http.oscpu))) == linear-gradient-1b.html linear-gradient-1-ref.html
 
 == zero-dimensions.html zero-dimensions-ref.html
 
 == evenodd-fill-sanity.html data:text/html,<body>Pass
 != evenodd-fill-1.html nonzero-fill-1.html
 == evenodd-fill-1.html evenodd-fill-ref.html
 == evenodd-fill-2.html evenodd-fill-ref.html
 == evenodd-fill-3.html nonzero-fill-2.html
--- a/layout/reftests/css-gradients/reftest.list
+++ b/layout/reftests/css-gradients/reftest.list
@@ -1,14 +1,14 @@
-fails-if(Android) == linear-1a.html linear-1-ref.html
-fails-if(Android) == linear-1b.html linear-1-ref.html
-fails-if(Android) == linear-keywords-1a.html linear-keywords-1-ref.html
-fails-if(Android) == linear-keywords-1b.html linear-keywords-1-ref.html
-fails-if(Android) == linear-percent.html linear-percent-ref.html
-fails-if(Android) == linear-mix.html linear-mix-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == linear-1a.html linear-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == linear-1b.html linear-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == linear-keywords-1a.html linear-keywords-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == linear-keywords-1b.html linear-keywords-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == linear-percent.html linear-percent-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == linear-mix.html linear-mix-ref.html
 == linear-diagonal-1a.html linear-diagonal-1-ref.html
 == linear-diagonal-1b.html linear-diagonal-1-ref.html
 == linear-diagonal-1c.html linear-diagonal-1-ref.html
 == linear-diagonal-2a.html linear-diagonal-2-ref.html
 == linear-diagonal-2b.html linear-diagonal-2-ref.html
 == linear-diagonal-2c.html linear-diagonal-2-ref.html
 == linear-diagonal-3a.html linear-diagonal-3-ref.html
 == linear-diagonal-3b.html linear-diagonal-3-ref.html
@@ -31,30 +31,30 @@ fails-if(d2d) == linear-repeat-1f.html l
 fails-if(d2d) == linear-repeat-1g.html linear-repeat-1-ref.html # bug 582236
 == linear-size-1a.html linear-size-1-ref.html
 == linear-stops-1a.html linear-stops-1-ref.html
 == linear-stops-1b.html linear-stops-1-ref.html
 == linear-stops-1c.html linear-stops-1-ref.html
 == linear-stops-1d.html linear-stops-1-ref.html
 == linear-stops-1e.html linear-stops-1-ref.html
 == linear-stops-1f.html linear-stops-1-ref.html
-fails-if(Android) == linear-vertical-1a.html linear-vertical-1-ref.html
-fails-if(Android) == linear-vertical-1b.html linear-vertical-1-ref.html
-fails-if(Android) == linear-vertical-1c.html linear-vertical-1-ref.html
-fails-if(Android) == linear-vertical-1d.html linear-vertical-1-ref.html
-fails-if(Android) == linear-vertical-1e.html linear-vertical-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1a.html linear-vertical-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1b.html linear-vertical-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1c.html linear-vertical-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1d.html linear-vertical-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1e.html linear-vertical-1-ref.html
 == linear-viewport.html linear-viewport-ref.html
 == linear-zero-length-1a.html linear-zero-length-1-ref.html
 == linear-zero-length-1b.html linear-zero-length-1-ref.html
 == linear-zero-length-1c.html linear-zero-length-1-ref.html
 == nostops.html about:blank
 == onestop.html about:blank
-fails-if(Android) random-if(d2d) == radial-1a.html radial-1-ref.html
-fails-if(Android) == radial-2a.html radial-2-ref.html
-fails-if(Android) == radial-2b.html radial-2-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) random-if(d2d) == radial-1a.html radial-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == radial-2a.html radial-2-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == radial-2b.html radial-2-ref.html
 == radial-position-1a.html radial-position-1-ref.html
 == radial-shape-closest-corner-1a.html radial-shape-closest-corner-1-ref.html
 == radial-shape-closest-side-1a.html radial-shape-closest-side-1-ref.html
 == radial-shape-farthest-corner-1a.html radial-shape-farthest-corner-1-ref.html
 == radial-shape-farthest-side-1a.html radial-shape-farthest-side-1-ref.html
 == radial-size-1a.html radial-size-1-ref.html
 == radial-zero-length-1a.html radial-zero-length-1-ref.html
 == radial-zero-length-1b.html radial-zero-length-1-ref.html
@@ -72,33 +72,33 @@ fails-if(d2d) == repeating-linear-1b.htm
 == twostops-1b.html twostops-1-ref.html
 fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) == twostops-1c.html twostops-1-ref.html # bug 524173
 == twostops-1d.html twostops-1-ref.html
 == twostops-1e.html twostops-1-ref.html
 == twostops-1f.html twostops-1-ref.html
 == twostops-1g.html twostops-1-ref.html
 
 # from http://www.xanthir.com/:4bhipd by way of http://a-ja.net/newgrad.html
-fails-if(Android) == aja-linear-1a.html aja-linear-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1a.html aja-linear-1-ref.html
 fails-if(!d2d) == aja-linear-1b.html aja-linear-1-ref.html # bug 526694
-fails-if(Android) == aja-linear-1c.html aja-linear-1-ref.html
-fails-if(Android) == aja-linear-1d.html aja-linear-1-ref.html
-fails-if(Android) == aja-linear-1e.html aja-linear-1-ref.html
-fails-if(Android) == aja-linear-1f.html aja-linear-1-ref.html
-fails-if(Android) == aja-linear-1g.html aja-linear-1-ref.html
-fails-if(Android) == aja-linear-2a.html aja-linear-2-ref.html
-fails-if(Android) == aja-linear-2b.html aja-linear-2-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1c.html aja-linear-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1d.html aja-linear-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1e.html aja-linear-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1f.html aja-linear-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1g.html aja-linear-1-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-2a.html aja-linear-2-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-2b.html aja-linear-2-ref.html
 fails == aja-linear-2c.html aja-linear-2-ref.html # bug 522607
 fails-if(!d2d) == aja-linear-2d.html aja-linear-2-ref.html # bug 526694
-fails-if(Android) == aja-linear-3a.html aja-linear-3-ref.html
-fails-if(Android) == aja-linear-3b.html aja-linear-3-ref.html
-fails-if(Android) == aja-linear-4a.html aja-linear-4-ref.html
-fails-if(Android) == aja-linear-4b.html aja-linear-4-ref.html
-fails-if(Android) == aja-linear-5a.html aja-linear-5-ref.html
-fails-if(Android) fails-if(/Mac\x20OS\x20X\x2010\.5/.test(http.oscpu)) == aja-linear-6a.html aja-linear-6-ref.html # bug 526708
+fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-3a.html aja-linear-3-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-3b.html aja-linear-3-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-4a.html aja-linear-4-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-4b.html aja-linear-4-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-5a.html aja-linear-5-ref.html
+fuzzy-if(azureQuartz) fails-if(Android) fails-if(/Mac\x20OS\x20X\x2010\.5/.test(http.oscpu)) == aja-linear-6a.html aja-linear-6-ref.html # bug 526708
 fails == aja-linear-6b.html aja-linear-6-ref.html # bug 522607
 == height-dependence-1.html height-dependence-1-ref.html
 fails-if(cocoaWidget) == height-dependence-2.html height-dependence-2-ref.html # bug 535007
 == height-dependence-3.html height-dependence-3-ref.html
 
 fails-if(d2d) == linear-onestopposition-1.html linear-onestopposition-1-ref.html # bug 638664
 == linear-onestopposition-1.html linear-onestopposition-1-ref2.html
 fails-if(d2d) fails-if(cocoaWidget) == radial-onestopposition-1.html radial-onestopposition-1-ref.html # bug 638664
new file mode 100644
--- /dev/null
+++ b/layout/reftests/reftest-sanity/fuzzy-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="background: #ff00ff; width: 500px; height: 500px;"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/reftest-sanity/fuzzy.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="background: #ff01ff; width: 500px; height: 500px;"></div>
+  </body>
+</html>
--- a/layout/reftests/reftest-sanity/reftest.list
+++ b/layout/reftests/reftest-sanity/reftest.list
@@ -124,8 +124,15 @@ fails pref(font.size.variable.x-western,
 fails pref(font.size.variable.x-western,"foo") == font-size-16.html font-default.html
 # a string pref
 pref(font.default.x-western,"serif") == font-serif.html font-default.html
 pref(font.default.x-western,"serif") != font-sans-serif.html font-default.html
 pref(font.default.x-western,"sans-serif") == font-sans-serif.html font-default.html
 pref(font.default.x-western,"sans-serif") != font-serif.html font-default.html
 fails pref(font.default.x-western,true) == font-serif.html font-default.html
 fails pref(font.default.x-western,0) == font-serif.html font-default.html
+# reftest syntax: fuzzy
+fuzzy == fuzzy.html fuzzy-ref.html
+fuzzy != too-fuzzy.html fuzzy-ref.html
+fuzzy-if(true) == fuzzy.html fuzzy-ref.html
+fuzzy-if(false) == fuzzy-ref.html fuzzy-ref.html
+# When using 565 fuzzy.html and fuzzy-ref.html will compare as equal
+fails fuzzy-if(false) random-if(Android) == fuzzy.html fuzzy-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/reftest-sanity/too-fuzzy.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="background: #ff04ff; width: 500px; height: 500px;"></div>
+  </body>
+</html>
--- a/layout/reftests/svg/as-image/reftest.list
+++ b/layout/reftests/svg/as-image/reftest.list
@@ -34,17 +34,17 @@ include zoom/reftest.list
 == canvas-drawImage-scale-1a.html lime100x100-ref.html
 == canvas-drawImage-scale-1b.html lime100x100-ref.html
 == canvas-drawImage-scale-1c.html lime100x100-ref.html
 
 fails == canvas-drawImage-scale-2a.html canvas-drawImage-scale-2-ref.html # XXX all edges fuzzy
 fails == canvas-drawImage-scale-2b.html canvas-drawImage-scale-2-ref.html # XXX all edges fuzzy
 
 == canvas-drawImage-slice-1a.html lime100x100-ref.html
-fails == canvas-drawImage-slice-1b.html lime100x100-ref.html # XXX all edges fuzzy
+fails-if(!azureQuartz) == canvas-drawImage-slice-1b.html lime100x100-ref.html # XXX all edges fuzzy
 
 == canvas-drawImage-origin-clean-1.html lime100x100-ref.html
 
 # Simple <img> tests
 == img-simple-1.html  lime100x100-ref.html
 == img-simple-2.html  lime100x100-ref.html
 == img-simple-3.html  img-simple-3-ref.html
 == img-simple-4.html  lime100x100-ref.html
--- a/layout/tools/reftest/README.txt
+++ b/layout/tools/reftest/README.txt
@@ -101,16 +101,24 @@ 2. A test item
 
       slow-if(condition) If the condition is met, the test is treated as if
                          'slow' had been specified.  This is useful for tests
                          which are slow only on particular platforms (e.g. a
                          test which exercised out-of-memory behavior might be
                          fast on a 32-bit system but inordinately slow on a
                          64-bit system).
 
+      fuzzy  This allows a test to pass if the pixel value differences
+             are <= 2. It can also be used with '!=' to ensure that the
+             difference is greater than 2.
+
+      fuzzy-if(condition) If the condition is met, the test is treated as if
+                          'fuzzy' had been specified. This is useful if there
+                          are differences on particular platforms.
+
       require-or(cond1&&cond2&&...,fallback)
           Require some particular setup be performed or environmental
           condition(s) made true (eg setting debug mode) before the test
           is run. If any condition is unknown, unimplemented, or fails,
           revert to the fallback failure-type.
           Example: require-or(debugMode,skip)
 
       asserts(count)
--- a/layout/tools/reftest/print-manifest-dirs.py
+++ b/layout/tools/reftest/print-manifest-dirs.py
@@ -34,17 +34,17 @@
 # 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 *****
 
 import sys, os.path, re
 
 commentRE = re.compile(r"\s+#")
-conditionsRE = re.compile(r"^(fails|needs-focus|random|skip|asserts)")
+conditionsRE = re.compile(r"^(fails|needs-focus|random|skip|asserts|fuzzy)")
 httpRE = re.compile(r"HTTP\((\.\.(\/\.\.)*)\)")
 protocolRE = re.compile(r"^\w+:")
 
 def parseManifest(manifest, dirs):
   """Parse the reftest manifest |manifest|, adding all directories containing
   tests (and the dirs containing the manifests themselves) to the set |dirs|."""
   manifestdir = os.path.dirname(os.path.abspath(manifest))
   dirs.add(manifestdir)
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -144,16 +144,17 @@ const TYPE_SCRIPT = 'script'; // test co
 // the test by using the *larger*.  
 // FIXME: In the future, we may also want to use this rule for combining
 // statuses that are on the same line (rather than making the last one
 // win).
 const EXPECTED_PASS = 0;
 const EXPECTED_FAIL = 1;
 const EXPECTED_RANDOM = 2;
 const EXPECTED_DEATH = 3;  // test must be skipped to avoid e.g. crash/hang
+const EXPECTED_FUZZY = 4;
 
 // types of preference value we might want to set for a specific test
 const PREF_BOOLEAN = 0;
 const PREF_STRING  = 1;
 const PREF_INTEGER = 2;
 
 var gPrefsToRestore = [];
 
@@ -340,23 +341,19 @@ function InitAndStartRefTests()
         gTotalChunks = prefs.getIntPref("reftest.totalChunks");
         gThisChunk = prefs.getIntPref("reftest.thisChunk");
     }
     catch(e) {
         gTotalChunks = 0;
         gThisChunk = 0;
     }
 
-    try {
-        gWindowUtils = gContainingWindow.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIDOMWindowUtils);
-        if (gWindowUtils && !gWindowUtils.compareCanvases)
-            gWindowUtils = null;
-    } catch (e) {
-        gWindowUtils = null;
-    }
+    gWindowUtils = gContainingWindow.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIDOMWindowUtils);
+    if (!gWindowUtils || !gWindowUtils.compareCanvases)
+        throw "nsIDOMWindowUtils inteface missing";
 
     gIOService = CC[IO_SERVICE_CONTRACTID].getService(CI.nsIIOService);
     gDebug = CC[DEBUG_CONTRACTID].getService(CI.nsIDebug2);
 
     RegisterProcessCrashObservers();
 
     if (gRemote) {
         gServer = null;
@@ -507,28 +504,30 @@ function BuildConditionSandbox(aURL) {
 
     // xr.XPCOMABI throws exception for configurations without full ABI
     // support (mobile builds on ARM)
     try {
         sandbox.xulRuntime.XPCOMABI = xr.XPCOMABI;
     } catch(e) {
         sandbox.xulRuntime.XPCOMABI = "";
     }
-  
+ 
+    
+    var gfxInfo = (NS_GFXINFO_CONTRACTID in CC) && CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo);
     try {
-        // nsIGfxInfo is currently only implemented on Windows
-        sandbox.d2d = (NS_GFXINFO_CONTRACTID in CC) && CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo).D2DEnabled;
-    } catch(e) {
-        sandbox.d2d = false;
+      sandbox.d2d = gfxInfo.D2DEnabled;
+    } catch (e) {
+      sandbox.d2d = false;
     }
+    sandbox.azureQuartz = gfxInfo.getInfo().AzureBackend == "quartz";
 
     sandbox.layersGPUAccelerated =
-      gWindowUtils && gWindowUtils.layerManagerType != "Basic";
+      gWindowUtils.layerManagerType != "Basic";
     sandbox.layersOpenGL =
-      gWindowUtils && gWindowUtils.layerManagerType == "OpenGL";
+      gWindowUtils.layerManagerType == "OpenGL";
 
     // Shortcuts for widget toolkits.
     sandbox.Android = xr.OS == "Android";
     sandbox.cocoaWidget = xr.widgetToolkit == "cocoa";
     sandbox.gtk2Widget = xr.widgetToolkit == "gtk2";
     sandbox.qtWidget = xr.widgetToolkit == "qt";
     sandbox.winWidget = xr.widgetToolkit == "windows";
 
@@ -685,26 +684,26 @@ function ReadManifest(aURL, inherited_st
         var expected_status = EXPECTED_PASS;
         var allow_silent_fail = false;
         var minAsserts = 0;
         var maxAsserts = 0;
         var needs_focus = false;
         var slow = false;
         var prefSettings = [];
         
-        while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref)/)) {
+        while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|fuzzy)/)) {
             var item = items.shift();
             var stat;
             var cond;
-            var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/);
+            var m = item.match(/^(fails|random|skip|silentfail|fuzzy)-if(\(.*\))$/);
             if (m) {
                 stat = m[1];
                 // Note: m[2] contains the parentheses, and we want them.
                 cond = Components.utils.evalInSandbox(m[2], sandbox);
-            } else if (item.match(/^(fails|random|skip)$/)) {
+            } else if (item.match(/^(fails|random|skip|fuzzy)$/)) {
                 stat = item;
                 cond = true;
             } else if (item == "needs-focus") {
                 needs_focus = true;
                 cond = false;
             } else if ((m = item.match(/^asserts\((\d+)(-\d+)?\)$/))) {
                 cond = false;
                 minAsserts = Number(m[1]);
@@ -776,16 +775,18 @@ function ReadManifest(aURL, inherited_st
 
             if (cond) {
                 if (stat == "fails") {
                     expected_status = EXPECTED_FAIL;
                 } else if (stat == "random") {
                     expected_status = EXPECTED_RANDOM;
                 } else if (stat == "skip") {
                     expected_status = EXPECTED_DEATH;
+                } else if (stat == "fuzzy") {
+                    expected_status = EXPECTED_FUZZY;
                 } else if (stat == "silentfail") {
                     allow_silent_fail = true;
                 }
             }
         }
 
         expected_status = Math.max(expected_status, inherited_status);
 
@@ -1295,16 +1296,18 @@ function RecordResult(testRunTime, error
     outputs[EXPECTED_FAIL] = {
         true:  {s: "TEST-UNEXPECTED-PASS"       , n: "UnexpectedPass"},
         false: {s: "TEST-KNOWN-FAIL"            , n: "KnownFail"}
     };
     outputs[EXPECTED_RANDOM] = {
         true:  {s: "TEST-PASS" + randomMsg      , n: "Random"},
         false: {s: "TEST-KNOWN-FAIL" + randomMsg, n: "Random"}
     };
+    outputs[EXPECTED_FUZZY] = outputs[EXPECTED_PASS];
+
     var output;
 
     if (gURLs[0].type == TYPE_LOAD) {
         ++gTestResults.LoadOnly;
         gDumpLog("REFTEST TEST-PASS | " + gURLs[0].prettyPath + " | (LOAD ONLY)\n");
         gCurrentCanvas = null;
         FinishTestItem();
         return;
@@ -1397,31 +1400,35 @@ function RecordResult(testRunTime, error
             // Both documents have been loaded. Compare the renderings and see
             // if the comparison result matches the expected result specified
             // in the manifest.
 
             // number of different pixels
             var differences;
             // whether the two renderings match:
             var equal;
+            var maxDifference = {};
 
-            if (gWindowUtils) {
-                differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, {});
-                equal = (differences == 0);
-            } else {
-                differences = -1;
-                var k1 = gCanvas1.toDataURL();
-                var k2 = gCanvas2.toDataURL();
-                equal = (k1 == k2);
+            differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, maxDifference);
+            equal = (differences == 0);
+
+            // what is expected on this platform (PASS, FAIL, or RANDOM)
+            var expected = gURLs[0].expected;
+
+            if (maxDifference.value > 0 && maxDifference.value <= 2) {
+                if (equal) {
+                    throw "Inconsistent result from compareCanvases.";
+                }
+                equal = expected == EXPECTED_FUZZY;
+                gDumpLog("REFTEST fuzzy match\n");
             }
 
             // whether the comparison result matches what is in the manifest
             var test_passed = (equal == (gURLs[0].type == TYPE_REFTEST_EQUAL));
-            // what is expected on this platform (PASS, FAIL, or RANDOM)
-            var expected = gURLs[0].expected;
+
             output = outputs[expected][test_passed];
 
             ++gTestResults[output.n];
 
             var result = "REFTEST " + output.s + " | " +
                          gURLs[0].prettyPath + " | "; // the URL being tested
             switch (gURLs[0].type) {
                 case TYPE_REFTEST_NOTEQUAL:
@@ -1429,21 +1436,22 @@ function RecordResult(testRunTime, error
                     break;
                 case TYPE_REFTEST_EQUAL:
                     result += "image comparison (==) ";
                     break;
             }
             gDumpLog(result + "\n");
 
             if (!test_passed && expected == EXPECTED_PASS ||
+                !test_passed && expected == EXPECTED_FUZZY ||
                 test_passed && expected == EXPECTED_FAIL) {
                 if (!equal) {
                     gDumpLog("REFTEST   IMAGE 1 (TEST): " + gCanvas1.toDataURL() + "\n");
                     gDumpLog("REFTEST   IMAGE 2 (REFERENCE): " + gCanvas2.toDataURL() + "\n");
-                    gDumpLog("REFTEST number of differing pixels: " + differences + "\n");
+                    gDumpLog("REFTEST number of differing pixels: " + differences + " max difference: " + maxDifference.value + "\n");
                 } else {
                     gDumpLog("REFTEST   IMAGE: " + gCanvas1.toDataURL() + "\n");
                 }
             }
 
             if (!test_passed && expected == EXPECTED_PASS) {
                 FlushTestLog();
             }
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -103,16 +103,19 @@ pref("image.cache.size", 1048576); // by
 
 /* offline cache prefs */
 pref("browser.offline-apps.notify", true);
 pref("browser.cache.offline.enable", true);
 pref("browser.cache.offline.capacity", 5120); // kilobytes
 pref("offline-apps.quota.max", 2048); // kilobytes
 pref("offline-apps.quota.warn", 1024); // kilobytes
 
+// cache compression turned off for now - see bug #715198
+pref("browser.cache.compression_level", 0);
+
 /* protocol warning prefs */
 pref("network.protocol-handler.warn-external.tel", false);
 pref("network.protocol-handler.warn-external.mailto", false);
 pref("network.protocol-handler.warn-external.vnd.youtube", false);
 
 /* http prefs */
 pref("network.http.pipelining", true);
 pref("network.http.pipelining.ssl", true);
--- a/mobile/xul/app/mobile.js
+++ b/mobile/xul/app/mobile.js
@@ -101,18 +101,18 @@ pref("image.cache.size", 1048576); // by
 
 /* offline cache prefs */
 pref("browser.offline-apps.notify", true);
 pref("browser.cache.offline.enable", true);
 pref("browser.cache.offline.capacity", 5120); // kilobytes
 pref("offline-apps.quota.max", 2048); // kilobytes
 pref("offline-apps.quota.warn", 1024); // kilobytes
 
-/* zlib compression level used for cache compression */
-pref("browser.cache.compression_level", 1);
+// cache compression turned off for now - see bug #715198
+pref("browser.cache.compression_level", 0);
 
 /* protocol warning prefs */
 pref("network.protocol-handler.warn-external.tel", false);
 pref("network.protocol-handler.warn-external.mailto", false);
 pref("network.protocol-handler.warn-external.vnd.youtube", false);
 
 /* http prefs */
 pref("network.http.pipelining", true);
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -91,17 +91,18 @@ pref("offline-apps.quota.max",        20
 // the user should be warned if offline app disk usage exceeds this amount
 // (in kilobytes)
 pref("offline-apps.quota.warn",        51200);
 
 // zlib compression level used for cache compression:
 // 0 => disable compression
 // 1 => best speed
 // 9 => best compression
-pref("browser.cache.compression_level", 5);
+// cache compression turned off for now - see bug #715198
+pref("browser.cache.compression_level", 0);
 
 // Whether or not indexedDB is enabled.
 pref("dom.indexedDB.enabled", true);
 // Space to allow indexedDB databases before prompting (in MB).
 pref("dom.indexedDB.warningQuota", 50);
 
 // Whether or not Web Workers are enabled.
 pref("dom.workers.enabled", true);
@@ -225,16 +226,20 @@ pref("gfx.font_rendering.harfbuzz.script
 
 #ifdef XP_WIN
 pref("gfx.font_rendering.directwrite.enabled", false);
 pref("gfx.font_rendering.directwrite.use_gdi_table_loading", true);
 #endif
 
 #ifdef XP_WIN
 pref("gfx.canvas.azure.enabled", true);
+#else
+#ifdef XP_MACOSX
+pref("gfx.canvas.azure.enabled", true);
+#endif
 #endif
 
 pref("accessibility.browsewithcaret", false);
 pref("accessibility.warn_on_browsewithcaret", true);
 
 pref("accessibility.browsewithcaret_shortcut.enabled", true);
 
 #ifndef XP_MACOSX
--- a/netwerk/cache/nsCacheEntryDescriptor.cpp
+++ b/netwerk/cache/nsCacheEntryDescriptor.cpp
@@ -328,16 +328,20 @@ nsCacheEntryDescriptor::OpenOutputStream
 
     nsOutputStreamWrapper* cacheOutput = nsnull;
     PRInt32 compressionLevel = nsCacheService::CacheCompressionLevel();
     const char *val;
     val = mCacheEntry->GetMetaDataElement("uncompressed-len");
     if ((compressionLevel > 0) && val) {
         cacheOutput = new nsCompressOutputStreamWrapper(this, offset);
     } else {
+        // clear compression flag when compression disabled - see bug #715198
+        if (val) {
+            mCacheEntry->SetMetaDataElement("uncompressed-len", nsnull);
+        }
         cacheOutput = new nsOutputStreamWrapper(this, offset);
     }
     if (!cacheOutput) return NS_ERROR_OUT_OF_MEMORY;
 
     NS_ADDREF(*result = cacheOutput);
     return NS_OK;
 }
 
--- a/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.js
@@ -4,16 +4,17 @@
 
 // Tests that compatibility overrides are refreshed when showing the addon
 // selection UI.
 
 const PREF_GETADDONS_BYIDS            = "extensions.getAddons.get.url";
 const PREF_MIN_PLATFORM_COMPAT        = "extensions.minCompatiblePlatformVersion";
 
 var gTestAddon = null;
+var gWin;
 
 function waitForView(aView, aCallback) {
   var view = gWin.document.getElementById(aView);
   if (view.parentNode.selectedPanel == view) {
     aCallback();
     return;
   }
 
--- a/tools/profiler/sps/TableTicker.cpp
+++ b/tools/profiler/sps/TableTicker.cpp
@@ -42,25 +42,32 @@
 #include "platform.h"
 #include "nsXULAppAPI.h"
 #include "nsThreadUtils.h"
 #include "prenv.h"
 #include "shared-libraries.h"
 #include "mozilla/StringBuilder.h"
 
 // we eventually want to make this runtime switchable
-#if defined(XP_MACOSX) || defined(XP_UNIX)
+#if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_UNIX))
  #ifndef ANDROID
   #define USE_BACKTRACE
  #endif
 #endif
 #ifdef USE_BACKTRACE
  #include <execinfo.h>
 #endif
 
+#if defined(MOZ_PROFILING) && defined(XP_WIN)
+ #define USE_NS_STACKWALK
+#endif
+#ifdef USE_NS_STACKWALK
+ #include "nsStackWalk.h"
+#endif
+
 using std::string;
 using namespace mozilla;
 
 #ifdef XP_WIN
 #include <windows.h>
 #define getpid GetCurrentProcessId
 #else
 #include <unistd.h>
@@ -267,17 +274,22 @@ class TableTicker: public Sampler {
   {
     return mStack;
   }
 
   Profile* GetProfile()
   {
     return &mProfile;
   }
- private:
+
+private:
+  // Not implemented on platforms which do not support backtracing
+  void doBacktrace(Profile &aProfile);
+
+private:
   Profile mProfile;
   Stack *mStack;
   bool mSaveRequested;
   bool mUseStackWalk;
   bool mJankOnly;
 };
 
 /**
@@ -332,31 +344,69 @@ void TableTicker::HandleSaveRequest()
 
   // TODO: Use use the ipc/chromium Tasks here to support processes
   // without XPCOM.
   nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
   NS_DispatchToMainThread(runnable);
 }
 
 #ifdef USE_BACKTRACE
-static
-void doBacktrace(Profile &aProfile)
+void TableTicker::doBacktrace(Profile &aProfile)
 {
   void *array[100];
   int count = backtrace (array, 100);
 
   aProfile.addTag(ProfileEntry('s', "XRE_Main", 0));
 
   for (int i = 0; i < count; i++) {
     if( (intptr_t)array[i] == -1 ) break;
     aProfile.addTag(ProfileEntry('l', (const char*)array[i]));
   }
 }
 #endif
 
+#ifdef USE_NS_STACKWALK
+typedef struct {
+  void** array;
+  size_t size;
+  size_t count;
+} PCArray;
+
+static
+void StackWalkCallback(void* aPC, void* aClosure)
+{
+  PCArray* array = static_cast<PCArray*>(aClosure);
+  if (array->count >= array->size) {
+    // too many frames, ignore
+    return;
+  }
+  array->array[array->count++] = aPC;
+}
+
+void TableTicker::doBacktrace(Profile &aProfile)
+{
+  uintptr_t thread = GetThreadHandle(platform_data());
+  MOZ_ASSERT(thread);
+  void* pc_array[1000];
+  PCArray array = {
+    pc_array,
+    mozilla::ArrayLength(pc_array),
+    0
+  };
+  nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread);
+  if (NS_SUCCEEDED(rv)) {
+    aProfile.addTag(ProfileEntry('s', "XRE_Main", 0));
+
+    for (size_t i = array.count; i > 0; --i) {
+      aProfile.addTag(ProfileEntry('l', (const char*)array.array[i - 1]));
+    }
+  }
+}
+#endif
+
 static
 void doSampleStackTrace(Stack *aStack, Profile &aProfile, TickSample *sample)
 {
   // Sample
   // 's' tag denotes the start of a sample block
   // followed by 0 or more 'c' tags.
   for (int i = 0; i < aStack->mStackPointer; i++) {
     if (i == 0) {
@@ -390,17 +440,17 @@ void TableTicker::Tick(TickSample* sampl
       TimeDuration delta = sample->timestamp - sLastTracerEvent;
       if (delta.ToMilliseconds() > 100.0) {
           recordSample = true;
       }
     }
   }
 
   if (recordSample) {
-#ifdef USE_BACKTRACE
+#if defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK)
     if (mUseStackWalk) {
       doBacktrace(mProfile);
     } else {
       doSampleStackTrace(mStack, mProfile, sample);
     }
 #else
     doSampleStackTrace(mStack, mProfile, sample);
 #endif
@@ -537,17 +587,17 @@ char* mozilla_sampler_get_profile()
   char *rtn = (char*)malloc( (profile.Length()+1) * sizeof(char) );
   strcpy(rtn, profile.Buffer());
   return rtn;
 }
 
 const char** mozilla_sampler_get_features()
 {
   static const char* features[] = {
-#ifdef MOZ_PROFILING && USE_BACKTRACE
+#if defined(MOZ_PROFILING) && defined(USE_BACKTRACE)
     "stackwalk",
 #endif
     NULL
   };
 
   return features;
 }
 
--- a/tools/profiler/sps/platform-win32.cc
+++ b/tools/profiler/sps/platform-win32.cc
@@ -28,16 +28,21 @@ class Sampler::PlatformData : public Mal
   }
 
   HANDLE profiled_thread() { return profiled_thread_; }
 
  private:
   HANDLE profiled_thread_;
 };
 
+uintptr_t
+Sampler::GetThreadHandle(Sampler::PlatformData* aData)
+{
+  return (uintptr_t) aData->profiled_thread();
+}
 
 class SamplerThread : public Thread {
  public:
   SamplerThread(int interval, Sampler* sampler)
       : Thread("SamplerThread"),
         interval_(interval),
         sampler_(sampler) {}
 
--- a/tools/profiler/sps/platform.h
+++ b/tools/profiler/sps/platform.h
@@ -198,16 +198,21 @@ class Sampler {
 
   // Whether the sampler is running (that is, consumes resources).
   bool IsActive() const { return active_; }
 
   class PlatformData;
 
   PlatformData* platform_data() { return data_; }
 
+#ifdef XP_WIN
+  // xxxehsan sucky hack :(
+  static uintptr_t GetThreadHandle(PlatformData*);
+#endif
+
  private:
   void SetActive(bool value) { NoBarrier_Store(&active_, value); }
 
   const int interval_;
   const bool profiling_;
   Atomic32 active_;
   PlatformData* data_;  // Platform specific data.
 };