Bug 692879. Implement CoreGraphics Azure backend. r=mwoodrow
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Mon, 09 Jan 2012 13:54:44 -0500
changeset 85863 e6e3821a4b95b3599d93ffd37e1c08abd0b4dc35
parent 85862 1a2a2395cece68435b4e47dad294bf9753935a34
child 85864 fe11808a4109f60910d6b4b58d5ac1067243f87b
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmwoodrow
bugs692879
milestone12.0a1
Bug 692879. Implement CoreGraphics Azure backend. r=mwoodrow
gfx/2d/DrawTargetCG.cpp
gfx/2d/DrawTargetCG.h
gfx/2d/DrawTargetSkia.cpp
gfx/2d/Factory.cpp
gfx/2d/Makefile.in
gfx/2d/PathCG.cpp
gfx/2d/PathCG.h
gfx/2d/ScaledFontBase.cpp
gfx/2d/ScaledFontBase.h
gfx/2d/ScaledFontMac.cpp
gfx/2d/ScaledFontMac.h
gfx/2d/ScaledFontSkia.cpp
gfx/2d/ScaledFontSkia.h
gfx/2d/ScaledFontWin.cpp
gfx/2d/ScaledFontWin.h
gfx/2d/SourceSurfaceCG.cpp
gfx/2d/SourceSurfaceCG.h
gfx/2d/Types.h
gfx/thebes/gfxFont.h
gfx/thebes/gfxPlatformMac.cpp
gfx/thebes/gfxPlatformMac.h
gfx/thebes/gfxWindowsPlatform.cpp
--- 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
@@ -67,27 +67,34 @@ EXPORTS_mozilla/gfx	= \
 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 100755
--- 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 100755
--- 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,
--- 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/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,36 @@ gfxPlatformMac::ReadAntiAliasingThreshol
             threshold = 0;
         }
         CFRelease(prefValue);
     }
 
     return threshold;
 }
 
+already_AddRefed<gfxASurface>
+gfxPlatformMac::GetThebesSurfaceForDrawTarget(DrawTarget *aTarget)
+{
+  if (aTarget->GetType() == BACKEND_COREGRAPHICS) {
+    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);
+
+    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/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -514,16 +514,17 @@ gfxWindowsPlatform::GetThebesSurfaceForD
       return gfxPlatform::GetThebesSurfaceForDrawTarget(aTarget);
     }
 
     aTarget->Flush();
 
     nsRefPtr<gfxASurface> surf =
       new gfxD2DSurface(texture, ContentForFormat(aTarget->GetFormat()));
 
+    // shouldn't this hold a reference?
     surf->SetData(&kDrawTarget, aTarget, NULL);
 
     return surf.forget();
   }
 #endif
 
   return gfxPlatform::GetThebesSurfaceForDrawTarget(aTarget);
 }