Bug 692879. Implement CoreGraphics Azure backend. r=mwoodrow
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Mon, 09 Jan 2012 13:54:44 -0500
changeset 84666 c2fe0bb0e677a627af31b3c0f23070183f109435
parent 84665 9d8d71829f22ceb4e3b1156fa99045d57d0e9325
child 84667 7bfdbd721e0bd1f11eba7bd3175a0f8da7d4a575
push id4968
push userjmuizelaar@mozilla.com
push dateTue, 17 Jan 2012 17:15:25 +0000
treeherdermozilla-inbound@46c1346414bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmwoodrow
bugs692879
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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 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,
--- 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);
 }