Bug 1278269 - Make a PrintTarget class and PrintTargetThebes subclass and convert all nsIDeviceContextSpec subclasses to use them. r=Bas
authorJonathan Watt <jwatt@jwatt.org>
Fri, 03 Jun 2016 11:27:31 +0100
changeset 326438 b70203799e112afb9d9db09ebfac7f0b0ae71e33
parent 326437 f26550274a458d195d836c18fc706c79d43c372f
child 326439 7ee4fbc79fba11b47c514704cda112e8c5df1dd3
push id9858
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 14:37:10 +0000
treeherdermozilla-aurora@203106ef6cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBas
bugs1278269
milestone50.0a1
Bug 1278269 - Make a PrintTarget class and PrintTargetThebes subclass and convert all nsIDeviceContextSpec subclasses to use them. r=Bas
gfx/src/nsDeviceContext.cpp
gfx/src/nsDeviceContext.h
gfx/thebes/PrintTarget.cpp
gfx/thebes/PrintTarget.h
gfx/thebes/PrintTargetThebes.cpp
gfx/thebes/PrintTargetThebes.h
gfx/thebes/moz.build
widget/android/nsDeviceContextAndroid.cpp
widget/android/nsDeviceContextAndroid.h
widget/cocoa/nsDeviceContextSpecX.h
widget/cocoa/nsDeviceContextSpecX.mm
widget/gtk/nsDeviceContextSpecG.cpp
widget/gtk/nsDeviceContextSpecG.h
widget/nsDeviceContextSpecProxy.cpp
widget/nsDeviceContextSpecProxy.h
widget/nsIDeviceContextSpec.h
widget/qt/nsDeviceContextSpecQt.cpp
widget/qt/nsDeviceContextSpecQt.h
widget/windows/nsDeviceContextSpecWin.cpp
widget/windows/nsDeviceContextSpecWin.h
--- a/gfx/src/nsDeviceContext.cpp
+++ b/gfx/src/nsDeviceContext.cpp
@@ -7,16 +7,17 @@
 #include <algorithm>                    // for max
 #include "gfxASurface.h"                // for gfxASurface, etc
 #include "gfxContext.h"
 #include "gfxFont.h"                    // for gfxFontGroup
 #include "gfxImageSurface.h"            // for gfxImageSurface
 #include "gfxPoint.h"                   // for gfxSize
 #include "mozilla/Attributes.h"         // for final
 #include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/gfx/PrintTarget.h"
 #include "mozilla/Preferences.h"        // for Preferences
 #include "mozilla/Services.h"           // for GetObserverService
 #include "mozilla/mozalloc.h"           // for operator new
 #include "nsCRT.h"                      // for nsCRT
 #include "nsDebug.h"                    // for NS_NOTREACHED, NS_ASSERTION, etc
 #include "nsFont.h"                     // for nsFont
 #include "nsFontMetrics.h"              // for nsFontMetrics
 #include "nsIAtom.h"                    // for nsIAtom, NS_Atomize
@@ -242,17 +243,17 @@ nsDeviceContext::FontMetricsDeleted(cons
         mFontCache->FontMetricsDeleted(aFontMetrics);
     }
     return NS_OK;
 }
 
 bool
 nsDeviceContext::IsPrinterSurface()
 {
-    return mPrintingSurface != nullptr;
+    return mPrintTarget != nullptr;
 }
 
 void
 nsDeviceContext::SetDPI(double* aScale)
 {
     float dpi = -1.0f;
 
     // Use the printing DC to determine DPI values, if we have one.
@@ -319,67 +320,62 @@ nsDeviceContext::Init(nsIWidget *aWidget
     if (mScreenManager)
         return rv;
 
     mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
 
     return rv;
 }
 
+// XXX This is only for printing. We should make that obvious in the name.
 already_AddRefed<gfxContext>
 nsDeviceContext::CreateRenderingContext()
 {
+    MOZ_ASSERT(IsPrinterSurface());
     MOZ_ASSERT(mWidth > 0 && mHeight > 0);
 
-    RefPtr<gfxASurface> printingSurface = mPrintingSurface;
+    RefPtr<PrintTarget> printingTarget = mPrintTarget;
 #ifdef XP_MACOSX
     // CreateRenderingContext() can be called (on reflow) after EndPage()
-    // but before BeginPage().  On OS X (and only there) mPrintingSurface
+    // but before BeginPage().  On OS X (and only there) mPrintTarget
     // will in this case be null, because OS X printing surfaces are
     // per-page, and therefore only truly valid between calls to BeginPage()
     // and EndPage().  But we can get away with fudging things here, if need
     // be, by using a cached copy.
-    if (!printingSurface) {
-      printingSurface = mCachedPrintingSurface;
+    if (!printingTarget) {
+      printingTarget = mCachedPrintTarget;
     }
 #endif
 
+    // This will usually be null, depending on the pref print.print_via_parent.
+    RefPtr<DrawEventRecorder> recorder;
+    mDeviceContextSpec->GetDrawEventRecorder(getter_AddRefs(recorder));
+
     RefPtr<gfx::DrawTarget> dt =
-      gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(printingSurface,
-                                                             gfx::IntSize(mWidth, mHeight));
-
-    // This can legitimately happen - CreateDrawTargetForSurface will fail
-    // to create a draw target if the size is too large, for instance.
+      printingTarget->MakeDrawTarget(gfx::IntSize(mWidth, mHeight), recorder);
     if (!dt || !dt->IsValid()) {
-        gfxCriticalNote << "Failed to create draw target in device context sized " << mWidth << "x" << mHeight << " and pointers " << hexa(mPrintingSurface) << " and " << hexa(printingSurface);
-        return nullptr;
-    }
-
-    RefPtr<DrawEventRecorder> recorder;
-    nsresult rv = mDeviceContextSpec->GetDrawEventRecorder(getter_AddRefs(recorder));
-    if (NS_SUCCEEDED(rv) && recorder) {
-      dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dt);
-      if (!dt || !dt->IsValid()) {
-          gfxCriticalNote << "Failed to create a recording draw target";
-          return nullptr;
-      }
+      gfxCriticalNote
+        << "Failed to create draw target in device context sized "
+        << mWidth << "x" << mHeight << " and pointers "
+        << hexa(mPrintTarget) << " and " << hexa(printingTarget);
+      return nullptr;
     }
 
 #ifdef XP_MACOSX
     dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr);
 #endif
     dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);
 
     RefPtr<gfxContext> pContext = gfxContext::CreateOrNull(dt);
     MOZ_ASSERT(pContext); // already checked draw target above
 
     gfxMatrix transform;
-    if (printingSurface->GetRotateForLandscape()) {
+    if (printingTarget->RotateNeededForLandscape()) {
       // Rotate page 90 degrees to draw landscape page on portrait paper
-      IntSize size = printingSurface->GetSize();
+      IntSize size = printingTarget->GetSize();
       transform.Translate(gfxPoint(0, size.width));
       gfxMatrix rotate(0, -1,
                        1,  0,
                        0,  0);
       transform = rotate * transform;
     }
     transform.Scale(mPrintingScale, mPrintingScale);
 
@@ -398,49 +394,49 @@ nsDeviceContext::GetDepth(uint32_t& aDep
 
     aDepth = mDepth;
     return NS_OK;
 }
 
 nsresult
 nsDeviceContext::GetDeviceSurfaceDimensions(nscoord &aWidth, nscoord &aHeight)
 {
-    if (mPrintingSurface) {
+    if (mPrintTarget) {
         // we have a printer device
         aWidth = mWidth;
         aHeight = mHeight;
     } else {
         nsRect area;
         ComputeFullAreaUsingScreen(&area);
         aWidth = area.width;
         aHeight = area.height;
     }
 
     return NS_OK;
 }
 
 nsresult
 nsDeviceContext::GetRect(nsRect &aRect)
 {
-    if (mPrintingSurface) {
+    if (mPrintTarget) {
         // we have a printer device
         aRect.x = 0;
         aRect.y = 0;
         aRect.width = mWidth;
         aRect.height = mHeight;
     } else
         ComputeFullAreaUsingScreen ( &aRect );
 
     return NS_OK;
 }
 
 nsresult
 nsDeviceContext::GetClientRect(nsRect &aRect)
 {
-    if (mPrintingSurface) {
+    if (mPrintTarget) {
         // we have a printer device
         aRect.x = 0;
         aRect.y = 0;
         aRect.width = mWidth;
         aRect.height = mHeight;
     }
     else
         ComputeClientRectUsingScreen(&aRect);
@@ -450,68 +446,69 @@ nsDeviceContext::GetClientRect(nsRect &a
 
 nsresult
 nsDeviceContext::InitForPrinting(nsIDeviceContextSpec *aDevice)
 {
     NS_ENSURE_ARG_POINTER(aDevice);
 
     mDeviceContextSpec = aDevice;
 
-    nsresult rv = aDevice->GetSurfaceForPrinter(getter_AddRefs(mPrintingSurface));
-    if (NS_FAILED(rv))
+    mPrintTarget = aDevice->MakePrintTarget();
+    if (!mPrintTarget) {
         return NS_ERROR_FAILURE;
+    }
 
     Init(nullptr);
 
     if (!CalcPrintingSize()) {
         return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
 }
 
 nsresult
 nsDeviceContext::BeginDocument(const nsAString& aTitle,
                                const nsAString& aPrintToFileName,
                                int32_t          aStartPage,
                                int32_t          aEndPage)
 {
-    nsresult rv = mPrintingSurface->BeginPrinting(aTitle, aPrintToFileName);
+    nsresult rv = mPrintTarget->BeginPrinting(aTitle, aPrintToFileName);
 
     if (NS_SUCCEEDED(rv) && mDeviceContextSpec) {
       rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName,
                                              aStartPage, aEndPage);
     }
 
     return rv;
 }
 
 
 nsresult
 nsDeviceContext::EndDocument(void)
 {
     nsresult rv = NS_OK;
 
-    if (mPrintingSurface) {
-        rv = mPrintingSurface->EndPrinting();
+    if (mPrintTarget) {
+        rv = mPrintTarget->EndPrinting();
         if (NS_SUCCEEDED(rv))
-            mPrintingSurface->Finish();
+            mPrintTarget->Finish();
     }
 
     if (mDeviceContextSpec)
         mDeviceContextSpec->EndDocument();
 
     return rv;
 }
 
 
 nsresult
 nsDeviceContext::AbortDocument(void)
 {
-    nsresult rv = mPrintingSurface->AbortPrinting();
+    nsresult rv = mPrintTarget->AbortPrinting();
 
     if (mDeviceContextSpec)
         mDeviceContextSpec->EndDocument();
 
     return rv;
 }
 
 
@@ -523,39 +520,39 @@ nsDeviceContext::BeginPage(void)
     if (mDeviceContextSpec)
         rv = mDeviceContextSpec->BeginPage();
 
     if (NS_FAILED(rv)) return rv;
 
 #ifdef XP_MACOSX
     // We need to get a new surface for each page on the Mac, as the
     // CGContextRefs are only good for one page.
-    mDeviceContextSpec->GetSurfaceForPrinter(getter_AddRefs(mPrintingSurface));
+    mPrintTarget = mDeviceContextSpec->MakePrintTarget();
 #endif
 
-    rv = mPrintingSurface->BeginPage();
+    rv = mPrintTarget->BeginPage();
 
     return rv;
 }
 
 nsresult
 nsDeviceContext::EndPage(void)
 {
-    nsresult rv = mPrintingSurface->EndPage();
+    nsresult rv = mPrintTarget->EndPage();
 
 #ifdef XP_MACOSX
     // We need to release the CGContextRef in the surface here, plus it's
     // not something you would want anyway, as these CGContextRefs are only
     // good for one page.  But we need to keep a cached reference to it, since
-    // CreateRenderingContext() may try to access it when mPrintingSurface
+    // CreateRenderingContext() may try to access it when mPrintTarget
     // would normally be null.  See bug 665218.  If we just stop nulling out
-    // mPrintingSurface here (and thereby make that our cached copy), we'll
-    // break all our null checks on mPrintingSurface.  See bug 684622.
-    mCachedPrintingSurface = mPrintingSurface;
-    mPrintingSurface = nullptr;
+    // mPrintTarget here (and thereby make that our cached copy), we'll
+    // break all our null checks on mPrintTarget.  See bug 684622.
+    mCachedPrintTarget = mPrintTarget;
+    mPrintTarget = nullptr;
 #endif
 
     if (mDeviceContextSpec)
         mDeviceContextSpec->EndPage();
 
     return rv;
 }
 
@@ -629,21 +626,21 @@ nsDeviceContext::FindScreen(nsIScreen** 
     else {
         mScreenManager->GetPrimaryScreen(outScreen);
     }
 }
 
 bool
 nsDeviceContext::CalcPrintingSize()
 {
-    if (!mPrintingSurface) {
+    if (!mPrintTarget) {
         return (mWidth > 0 && mHeight > 0);
     }
 
-    gfxSize size = mPrintingSurface->GetSize();
+    gfxSize size = mPrintTarget->GetSize();
     // For printing, CSS inches and physical inches are identical
     // so it doesn't matter which we use here
     mWidth = NSToCoordRound(size.width * AppUnitsPerPhysicalInch()
                             / POINTS_PER_INCH_FLOAT);
     mHeight = NSToCoordRound(size.height * AppUnitsPerPhysicalInch()
                              / POINTS_PER_INCH_FLOAT);
 
     return (mWidth > 0 && mHeight > 0);
--- a/gfx/src/nsDeviceContext.h
+++ b/gfx/src/nsDeviceContext.h
@@ -16,32 +16,39 @@
 #include "nsCoord.h"                    // for nscoord
 #include "nsError.h"                    // for nsresult
 #include "nsISupports.h"                // for NS_INLINE_DECL_REFCOUNTING
 #include "nsMathUtils.h"                // for NS_round
 #include "nscore.h"                     // for char16_t, nsAString
 #include "mozilla/AppUnits.h"           // for AppUnits
 #include "nsFontMetrics.h"              // for nsFontMetrics::Params
 
-class gfxASurface;
 class gfxContext;
 class gfxTextPerfMetrics;
 class gfxUserFontSet;
 struct nsFont;
 class nsFontCache;
 class nsIAtom;
 class nsIDeviceContextSpec;
 class nsIScreen;
 class nsIScreenManager;
 class nsIWidget;
 struct nsRect;
 
+namespace mozilla {
+namespace gfx {
+class PrintTarget;
+}
+}
+
 class nsDeviceContext final
 {
 public:
+    typedef mozilla::gfx::PrintTarget PrintTarget;
+
     nsDeviceContext();
 
     NS_INLINE_DECL_REFCOUNTING(nsDeviceContext)
 
     /**
      * Initialize the device context from a widget
      * @param aWidget a widget to initialize the device context from
      * @return error status
@@ -272,15 +279,15 @@ private:
     int32_t  mAppUnitsPerPhysicalInch;
     float    mFullZoom;
     float    mPrintingScale;
 
     RefPtr<nsFontCache>            mFontCache;
     nsCOMPtr<nsIWidget>            mWidget;
     nsCOMPtr<nsIScreenManager>     mScreenManager;
     nsCOMPtr<nsIDeviceContextSpec> mDeviceContextSpec;
-    RefPtr<gfxASurface>          mPrintingSurface;
+    RefPtr<PrintTarget>            mPrintTarget;
 #ifdef XP_MACOSX
-    RefPtr<gfxASurface>          mCachedPrintingSurface;
+    RefPtr<PrintTarget>            mCachedPrintTarget;
 #endif
 };
 
 #endif /* _NS_DEVICECONTEXT_H_ */
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/PrintTarget.cpp
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PrintTarget.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+PrintTarget::PrintTarget(const IntSize& aSize)
+  : mSize(aSize)
+  , mIsFinished(false)
+{
+}
+
+PrintTarget::~PrintTarget()
+{
+}
+
+already_AddRefed<DrawTarget>
+PrintTarget::CreateRecordingDrawTarget(DrawEventRecorder* aRecorder,
+                                       DrawTarget* aDrawTarget)
+{
+  MOZ_ASSERT(aRecorder);
+  MOZ_ASSERT(aDrawTarget);
+
+  RefPtr<DrawTarget> dt;
+
+  if (aRecorder) {
+    // It doesn't really matter what we pass as the DrawTarget here.
+    dt = gfx::Factory::CreateRecordingDrawTarget(aRecorder, aDrawTarget);
+  }
+
+  if (!dt || !dt->IsValid()) {
+    gfxCriticalNote
+      << "Failed to create a recording DrawTarget for PrintTarget";
+    return nullptr;
+  }
+
+  return dt.forget();
+}
+
+void
+PrintTarget::Finish()
+{
+  if (mIsFinished) {
+    return;
+  }
+  mIsFinished = true;
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/PrintTarget.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PRINTTARGET_H
+#define MOZILLA_GFX_PRINTTARGET_H
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/2D.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DrawEventRecorder;
+
+/**
+ * A class that is used to draw output that is to be sent to a printer or print
+ * preview.
+ *
+ * This class wraps a cairo_surface_t* and provides access to it via a
+ * DrawTarget.  The various checkpointing methods manage the state of the
+ * platform specific cairo_surface_t*.
+ */
+class PrintTarget {
+public:
+
+  NS_INLINE_DECL_REFCOUNTING(PrintTarget);
+
+  /// Must be matched 1:1 by an EndPrinting/AbortPrinting call.
+  virtual nsresult BeginPrinting(const nsAString& aTitle,
+                                 const nsAString& aPrintToFileName) {
+    return NS_OK;
+  }
+  virtual nsresult EndPrinting() {
+    return NS_OK;
+  }
+  virtual nsresult AbortPrinting() {
+    return NS_OK;
+  }
+  virtual nsresult BeginPage() {
+    return NS_OK;
+  }
+  virtual nsresult EndPage() {
+    return NS_OK;
+  }
+
+  /**
+   * Releases the resources used by this PrintTarget.  Typically this should be
+   * called after calling EndPrinting().  Calling this more than once is
+   * allowed, but subsequent calls are a no-op.
+   *
+   * Note that any DrawTarget obtained from this PrintTarget will no longer be
+   * useful after this method has been called.
+   */
+  virtual void Finish();
+
+  /**
+   * Returns true if to print landscape our consumers must apply a 90 degrees
+   * rotation to our DrawTarget.
+   */
+  virtual bool RotateNeededForLandscape() const {
+    return false;
+  }
+
+  const IntSize& GetSize() const {
+    return mSize;
+  }
+
+  /**
+   * Makes a DrawTarget to draw the printer output to, or returns null on
+   * failure.
+   *
+   * If aRecorder is passed a recording DrawTarget will be created instead of
+   * the type of DrawTarget that would normally be returned for a particular
+   * subclass of this class.  This argument is only intended to be used in
+   * the e10s content process if printing output can't otherwise be transfered
+   * over to the parent process using the normal DrawTarget type.
+   *
+   * XXX For consistency with the old code this takes a size parameter even
+   * though we already have the size passed to our subclass's CreateOrNull
+   * factory methods.  The size passed to the factory method comes from
+   * nsIDeviceContextSpec::MakePrintTarget overrides, whereas the size
+   * passed to us comes from nsDeviceContext::CreateRenderingContext.  In at
+   * least one case (nsDeviceContextSpecAndroid::MakePrintTarget) these are
+   * different.  At some point we should align the two sources and get rid of
+   * this method's size parameter.
+   *
+   * XXX For consistency with the old code this returns a new DrawTarget for
+   * each call.  Perhaps we can create and cache a DrawTarget in our subclass's
+   * CreateOrNull factory methods and return that on each call?  Currently that
+   * seems to cause Mochitest failures on Windows though, which coincidentally
+   * is the only platform where we get passed an aRecorder.  Probably the
+   * issue is that we get called more than once with a different aRecorder, so
+   * storing one recording DrawTarget for our lifetime doesn't currently work.
+   *
+   * XXX Could we pass aRecorder to our subclass's CreateOrNull factory methods?
+   * We'd need to check that our consumers always pass the same aRecorder for
+   * our entire lifetime.
+   *
+   * XXX Once PrintTargetThebes is removed this can become non-virtual.
+   *
+   * XXX In the long run, this class and its sub-classes should be converted to
+   * use STL classes and mozilla::RefCounted<> so the can be moved to Moz2D.
+   *
+   * TODO: Consider adding a SetDPI method that calls
+   * cairo_surface_set_fallback_resolution.
+   */
+  virtual already_AddRefed<DrawTarget>
+  MakeDrawTarget(const IntSize& aSize,
+                 DrawEventRecorder* aRecorder = nullptr) = 0;
+
+protected:
+
+  // Only created via subclass's constructors
+  explicit PrintTarget(const IntSize& aSize);
+
+  // Protected because we're refcounted
+  virtual ~PrintTarget();
+
+  already_AddRefed<DrawTarget>
+  CreateRecordingDrawTarget(DrawEventRecorder* aRecorder,
+                            DrawTarget* aDrawTarget);
+
+  IntSize mSize;
+  bool mIsFinished;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PRINTTARGET_H */
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/PrintTargetThebes.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PrintTargetThebes.h"
+
+#include "gfxASurface.h"
+#include "gfxPlatform.h"
+#include "mozilla/gfx/Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+/* static */ already_AddRefed<PrintTargetThebes>
+PrintTargetThebes::CreateOrNull(gfxASurface* aSurface)
+{
+  MOZ_ASSERT(aSurface);
+
+  if (!aSurface || aSurface->CairoStatus()) {
+    return nullptr;
+  }
+
+  RefPtr<PrintTargetThebes> target = new PrintTargetThebes(aSurface);
+
+  return target.forget();
+}
+
+PrintTargetThebes::PrintTargetThebes(gfxASurface* aSurface)
+  : PrintTarget(aSurface->GetSize())
+  , mGfxSurface(aSurface)
+{
+}
+
+already_AddRefed<DrawTarget>
+PrintTargetThebes::MakeDrawTarget(const IntSize& aSize,
+                                  DrawEventRecorder* aRecorder)
+{
+  RefPtr<gfx::DrawTarget> dt =
+    gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mGfxSurface, aSize);
+  if (!dt || !dt->IsValid()) {
+    return nullptr;
+  }
+
+  if (aRecorder) {
+    dt = CreateRecordingDrawTarget(aRecorder, dt);
+    if (!dt || !dt->IsValid()) {
+      return nullptr;
+    }
+  }
+
+  return dt.forget();
+}
+
+nsresult
+PrintTargetThebes::BeginPrinting(const nsAString& aTitle,
+                                 const nsAString& aPrintToFileName)
+{
+  return mGfxSurface->BeginPrinting(aTitle, aPrintToFileName);
+}
+
+nsresult
+PrintTargetThebes::EndPrinting()
+{
+  return mGfxSurface->EndPrinting();
+}
+
+nsresult
+PrintTargetThebes::AbortPrinting()
+{
+  return mGfxSurface->AbortPrinting();
+}
+
+nsresult
+PrintTargetThebes::BeginPage()
+{
+  return mGfxSurface->BeginPage();
+}
+
+nsresult
+PrintTargetThebes::EndPage()
+{
+  return mGfxSurface->EndPage();
+}
+
+void
+PrintTargetThebes::Finish()
+{
+  return mGfxSurface->Finish();
+}
+
+bool
+PrintTargetThebes::RotateNeededForLandscape() const
+{
+  return mGfxSurface->GetRotateForLandscape();
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/PrintTargetThebes.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PRINTTARGETTHEBES_H
+#define MOZILLA_GFX_PRINTTARGETTHEBES_H
+
+#include "mozilla/gfx/PrintTarget.h"
+
+class gfxASurface;
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * XXX Remove this class.
+ *
+ * This class should go away once all the logic from the gfxASurface subclasses
+ * has been moved to new PrintTarget subclasses and we no longer need to
+ * wrap a gfxASurface.
+ *
+ * When removing this class, be sure to make PrintTarget::MakeDrawTarget
+ * non-virtual!
+ */
+class PrintTargetThebes final : public PrintTarget {
+public:
+
+  static already_AddRefed<PrintTargetThebes>
+  CreateOrNull(gfxASurface* aSurface);
+
+  virtual nsresult BeginPrinting(const nsAString& aTitle,
+                                 const nsAString& aPrintToFileName) override;
+  virtual nsresult EndPrinting() override;
+  virtual nsresult AbortPrinting() override;
+  virtual nsresult BeginPage() override;
+  virtual nsresult EndPage() override;
+  virtual void Finish() override;
+
+  virtual bool RotateNeededForLandscape() const override;
+
+  virtual already_AddRefed<DrawTarget>
+  MakeDrawTarget(const IntSize& aSize,
+                 DrawEventRecorder* aRecorder = nullptr) override;
+
+private:
+
+  // Only created via CreateOrNull
+  explicit PrintTargetThebes(gfxASurface* aSurface);
+
+  RefPtr<gfxASurface> mGfxSurface;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PRINTTARGETTHEBES_H */
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -47,16 +47,21 @@ EXPORTS += [
     'gfxTypes.h',
     'gfxUserFontSet.h',
     'gfxUtils.h',
     'RoundedRect.h',
     'SoftwareVsyncSource.h',
     'VsyncSource.h',
 ]
 
+EXPORTS.mozilla.gfx += [
+    'PrintTarget.h',
+    'PrintTargetThebes.h',
+]
+
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     EXPORTS += [
         'gfxAndroidPlatform.h',
         'gfxFT2FontBase.h',
         'gfxFT2Fonts.h',
         'gfxPDFSurface.h',
     ]
     SOURCES += [
@@ -197,16 +202,18 @@ SOURCES += [
     'gfxASurface.cpp',
     # on X11, gfxDrawable.cpp includes X headers for an old workaround which
     # we could consider removing soon (affects Ubuntus older than 10.04 LTS)
     # which currently prevent it from joining UNIFIED_SOURCES.
     'gfxDrawable.cpp',
     # gfxPlatform.cpp includes mac system header conflicting with point/size
     'gfxPlatform.cpp',
     'gfxPrefs.cpp',
+    'PrintTarget.cpp',
+    'PrintTargetThebes.cpp',
 ]
 
 UNIFIED_SOURCES += [
     'CJKCompatSVS.cpp',
     'gfxAlphaRecovery.cpp',
     'gfxBaseSharedMemorySurface.cpp',
     'gfxBlur.cpp',
     'gfxContext.cpp',
--- a/widget/android/nsDeviceContextAndroid.cpp
+++ b/widget/android/nsDeviceContextAndroid.cpp
@@ -1,50 +1,50 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "nsDeviceContextAndroid.h"
+
+#include "mozilla/gfx/PrintTargetThebes.h"
 #include "mozilla/RefPtr.h"
-#include "nsDeviceContextAndroid.h"
 #include "nsString.h"
 #include "nsIFile.h"
 #include "nsIFileStreams.h"
 #include "gfxPDFSurface.h"
 #include "nsIPrintSettings.h"
 #include "nsDirectoryServiceDefs.h"
 
+using namespace mozilla;
+using namespace mozilla::gfx;
+
 NS_IMPL_ISUPPORTS(nsDeviceContextSpecAndroid, nsIDeviceContextSpec)
 
-NS_IMETHODIMP
-nsDeviceContextSpecAndroid::GetSurfaceForPrinter(gfxASurface** aSurface)
+already_AddRefed<PrintTarget>
+nsDeviceContextSpecAndroid::MakePrintTarget()
 {
   nsresult rv =
     NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mTempFile));
-  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_SUCCESS(rv, nullptr);
 
   nsAutoCString filename("tmp-printing.pdf");
   mTempFile->AppendNative(filename);
   rv = mTempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660);
-  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_SUCCESS(rv, nullptr);
 
   nsCOMPtr<nsIFileOutputStream> stream = do_CreateInstance("@mozilla.org/network/file-output-stream;1");
   rv = stream->Init(mTempFile, -1, -1, 0);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  RefPtr<gfxASurface> surface;
+  NS_ENSURE_SUCCESS(rv, nullptr);
 
   // XXX: what should we do here for size? screen size?
-  gfxSize surfaceSize(480, 800);
-
-  surface = new gfxPDFSurface(stream, surfaceSize);
+  gfxSize size(480, 800);
 
+  RefPtr<gfxASurface> surface = new gfxPDFSurface(stream, size);
 
-  MOZ_ASSERT(surface, "valid address expected");
-  surface.swap(*aSurface);
-  return NS_OK;
+  return PrintTargetThebes::CreateOrNull(surface);
 }
 
 NS_IMETHODIMP
 nsDeviceContextSpecAndroid::Init(nsIWidget* aWidget,
                              nsIPrintSettings* aPS,
                              bool aIsPrintPreview)
 {
   mPrintSettings = aPS;
--- a/widget/android/nsDeviceContextAndroid.h
+++ b/widget/android/nsDeviceContextAndroid.h
@@ -8,17 +8,17 @@
 class nsDeviceContextSpecAndroid final : public nsIDeviceContextSpec
 {
 private:
     ~nsDeviceContextSpecAndroid() {}
 
 public:
     NS_DECL_ISUPPORTS
 
-    NS_IMETHOD GetSurfaceForPrinter(gfxASurface** surface) override;
+    virtual already_AddRefed<PrintTarget> MakePrintTarget() final;
 
     NS_IMETHOD Init(nsIWidget* aWidget,
                     nsIPrintSettings* aPS,
                     bool aIsPrintPreview) override;
     NS_IMETHOD BeginDocument(const nsAString& aTitle,
                              const nsAString& aPrintToFileName,
                              int32_t aStartPage,
                              int32_t aEndPage) override;
--- a/widget/cocoa/nsDeviceContextSpecX.h
+++ b/widget/cocoa/nsDeviceContextSpecX.h
@@ -13,17 +13,17 @@
 class nsDeviceContextSpecX : public nsIDeviceContextSpec
 {
 public:
     NS_DECL_ISUPPORTS
 
     nsDeviceContextSpecX();
 
     NS_IMETHOD Init(nsIWidget *aWidget, nsIPrintSettings* aPS, bool aIsPrintPreview) override;
-    NS_IMETHOD GetSurfaceForPrinter(gfxASurface **surface) override;
+    virtual already_AddRefed<PrintTarget> MakePrintTarget() final;
     NS_IMETHOD BeginDocument(const nsAString& aTitle,
                              const nsAString& aPrintToFileName,
                              int32_t          aStartPage,
                              int32_t          aEndPage) override;
     NS_IMETHOD EndDocument() override;
     NS_IMETHOD BeginPage() override;
     NS_IMETHOD EndPage() override;
 
--- a/widget/cocoa/nsDeviceContextSpecX.mm
+++ b/widget/cocoa/nsDeviceContextSpecX.mm
@@ -1,29 +1,33 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDeviceContextSpecX.h"
 
+#include "mozilla/gfx/PrintTargetThebes.h"
 #include "mozilla/RefPtr.h"
 #include "nsCRT.h"
 #include <unistd.h>
 
 #include "nsQueryObject.h"
 #include "nsIServiceManager.h"
 #include "nsIPrintOptions.h"
 #include "nsPrintSettingsX.h"
 
 #include "gfxQuartzSurface.h"
 
 // This must be the last include:
 #include "nsObjCExceptions.h"
 
+using namespace mozilla;
+using namespace mozilla::gfx;
+
 nsDeviceContextSpecX::nsDeviceContextSpecX()
 : mPrintSession(NULL)
 , mPageFormat(kPMNoPageFormat)
 , mPrintSettings(kPMNoPrintSettings)
 {
 }
 
 nsDeviceContextSpecX::~nsDeviceContextSpecX()
@@ -131,17 +135,17 @@ void nsDeviceContextSpecX::GetPaperRect(
     ::PMGetAdjustedPaperRect(mPageFormat, &paperRect);
 
     *aTop = paperRect.top, *aLeft = paperRect.left;
     *aBottom = paperRect.bottom, *aRight = paperRect.right;
 
     NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
-NS_IMETHODIMP nsDeviceContextSpecX::GetSurfaceForPrinter(gfxASurface **surface)
+already_AddRefed<PrintTarget> nsDeviceContextSpecX::MakePrintTarget()
 {
     NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
     double top, left, bottom, right;
     GetPaperRect(&top, &left, &bottom, &right);
     const double width = right - left;
     const double height = bottom - top;
     IntSize size(floor(width), floor(height));
@@ -156,18 +160,16 @@ NS_IMETHODIMP nsDeviceContextSpecX::GetS
         // Here, we translate it to top-left corner of the paper.
         CGContextTranslateCTM(context, 0, height);
         CGContextScaleCTM(context, 1.0, -1.0);
         newSurface = new gfxQuartzSurface(context, size);
     } else {
         newSurface = new gfxQuartzSurface(size, SurfaceFormat::A8R8G8B8_UINT32);
     }
 
-    if (!newSurface)
-        return NS_ERROR_FAILURE;
+    if (!newSurface) {
+        return nullptr;
+    }
 
-    *surface = newSurface;
-    NS_ADDREF(*surface);
+    return PrintTargetThebes::CreateOrNull(newSurface);
 
-    return NS_OK;
-
-    NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+    NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
 }
--- a/widget/gtk/nsDeviceContextSpecG.cpp
+++ b/widget/gtk/nsDeviceContextSpecG.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/gfx/PrintTargetThebes.h"
 #include "mozilla/Logging.h"
 
 #include "plstr.h"
 
 #include "nsDeviceContextSpecG.h"
 
 #include "prenv.h" /* for PR_GetEnv */
 
@@ -28,16 +29,19 @@
 #include "mozilla/Preferences.h"
 
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
 using namespace mozilla;
 
+using mozilla::gfx::PrintTarget;
+using mozilla::gfx::PrintTargetThebes;
+
 static PRLogModuleInfo *
 GetDeviceContextSpecGTKLog()
 {
   static PRLogModuleInfo *sLog;
   if (!sLog)
     sLog = PR_NewLogModule("DeviceContextSpecGTK");
   return sLog;
 }
@@ -97,20 +101,18 @@ nsDeviceContextSpecGTK::~nsDeviceContext
   }
 }
 
 NS_IMPL_ISUPPORTS(nsDeviceContextSpecGTK,
                   nsIDeviceContextSpec)
 
 #include "gfxPDFSurface.h"
 #include "gfxPSSurface.h"
-NS_IMETHODIMP nsDeviceContextSpecGTK::GetSurfaceForPrinter(gfxASurface **aSurface)
+already_AddRefed<PrintTarget> nsDeviceContextSpecGTK::MakePrintTarget()
 {
-  *aSurface = nullptr;
-
   double width, height;
   mPrintSettings->GetEffectivePageSize(&width, &height);
 
   // convert twips to points
   width  /= TWIPS_PER_POINT_FLOAT;
   height /= TWIPS_PER_POINT_FLOAT;
 
   DO_PR_DEBUG_LOG(("\"%s\", %f, %f\n", mPath, width, height));
@@ -120,70 +122,69 @@ NS_IMETHODIMP nsDeviceContextSpecGTK::Ge
   // file.
   MOZ_ASSERT(!mSpoolFile);
 
   // Spool file. Use Glib's temporary file function since we're
   // already dependent on the gtk software stack.
   gchar *buf;
   gint fd = g_file_open_tmp("XXXXXX.tmp", &buf, nullptr);
   if (-1 == fd)
-    return NS_ERROR_GFX_PRINTER_COULD_NOT_OPEN_FILE;
+    return nullptr;
   close(fd);
 
   rv = NS_NewNativeLocalFile(nsDependentCString(buf), false,
                              getter_AddRefs(mSpoolFile));
   if (NS_FAILED(rv)) {
     unlink(buf);
-    return NS_ERROR_GFX_PRINTER_COULD_NOT_OPEN_FILE;
+    return nullptr;
   }
 
   mSpoolName = buf;
   g_free(buf);
 
   mSpoolFile->SetPermissions(0600);
 
   nsCOMPtr<nsIFileOutputStream> stream = do_CreateInstance("@mozilla.org/network/file-output-stream;1");
   rv = stream->Init(mSpoolFile, -1, -1, 0);
   if (NS_FAILED(rv))
-    return rv;
+    return nullptr;
 
   int16_t format;
   mPrintSettings->GetOutputFormat(&format);
 
   RefPtr<gfxASurface> surface;
   gfxSize surfaceSize(width, height);
 
   // Determine the real format with some GTK magic
   if (format == nsIPrintSettings::kOutputFormatNative) {
     if (mIsPPreview) {
       // There is nothing to detect on Print Preview, use PS.
       format = nsIPrintSettings::kOutputFormatPS;
     } else {
-      return NS_ERROR_FAILURE;
+      return nullptr;
     }
   }
 
   if (format == nsIPrintSettings::kOutputFormatPDF) {
     surface = new gfxPDFSurface(stream, surfaceSize);
   } else {
     int32_t orientation;
     mPrintSettings->GetOrientation(&orientation);
     if (nsIPrintSettings::kPortraitOrientation == orientation) {
       surface = new gfxPSSurface(stream, surfaceSize, gfxPSSurface::PORTRAIT);
     } else {
       surface = new gfxPSSurface(stream, surfaceSize, gfxPSSurface::LANDSCAPE);
     }
   }
 
-  if (!surface)
-    return NS_ERROR_OUT_OF_MEMORY;
+  if (!surface) {
+    return nullptr;
+  }
 
-  surface.swap(*aSurface);
-
-  return NS_OK;
+  return PrintTargetThebes::CreateOrNull(surface);
 }
 
 /** -------------------------------------------------------
  *  Initialize the nsDeviceContextSpecGTK
  *  @update   dc 2/15/98
  *  @update   syd 3/2/99
  */
 NS_IMETHODIMP nsDeviceContextSpecGTK::Init(nsIWidget *aWidget,
--- a/widget/gtk/nsDeviceContextSpecG.h
+++ b/widget/gtk/nsDeviceContextSpecG.h
@@ -25,17 +25,17 @@ class nsPrintSettingsGTK;
 
 class nsDeviceContextSpecGTK : public nsIDeviceContextSpec
 {
 public:
   nsDeviceContextSpecGTK();
 
   NS_DECL_ISUPPORTS
 
-  NS_IMETHOD GetSurfaceForPrinter(gfxASurface **surface) override;
+  virtual already_AddRefed<PrintTarget> MakePrintTarget() final;
 
   NS_IMETHOD Init(nsIWidget *aWidget, nsIPrintSettings* aPS,
                   bool aIsPrintPreview) override;
   NS_IMETHOD BeginDocument(const nsAString& aTitle,
                            const nsAString& aPrintToFileName,
                            int32_t aStartPage, int32_t aEndPage) override;
   NS_IMETHOD EndDocument() override;
   NS_IMETHOD BeginPage() override { return NS_OK; }
--- a/widget/nsDeviceContextSpecProxy.cpp
+++ b/widget/nsDeviceContextSpecProxy.cpp
@@ -4,25 +4,29 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDeviceContextSpecProxy.h"
 
 #include "gfxASurface.h"
 #include "gfxPlatform.h"
 #include "mozilla/gfx/DrawEventRecorder.h"
+#include "mozilla/gfx/PrintTargetThebes.h"
 #include "mozilla/layout/RemotePrintJobChild.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/unused.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIPrintSession.h"
 #include "nsIPrintSettings.h"
 
 using mozilla::Unused;
 
+using namespace mozilla;
+using namespace mozilla::gfx;
+
 NS_IMPL_ISUPPORTS(nsDeviceContextSpecProxy, nsIDeviceContextSpec)
 
 NS_IMETHODIMP
 nsDeviceContextSpecProxy::Init(nsIWidget* aWidget,
                                nsIPrintSettings* aPrintSettings,
                                bool aIsPrintPreview)
 {
   nsresult rv;
@@ -56,38 +60,50 @@ nsDeviceContextSpecProxy::Init(nsIWidget
   if (NS_FAILED(rv) || !mRemotePrintJob) {
     NS_WARNING("We can't print via the parent without a RemotePrintJobChild.");
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsDeviceContextSpecProxy::GetSurfaceForPrinter(gfxASurface** aSurface)
+already_AddRefed<PrintTarget>
+nsDeviceContextSpecProxy::MakePrintTarget()
 {
-  MOZ_ASSERT(aSurface);
   MOZ_ASSERT(mRealDeviceContextSpec);
 
   double width, height;
   nsresult rv = mPrintSettings->GetEffectivePageSize(&width, &height);
   if (NS_WARN_IF(NS_FAILED(rv)) || width <= 0 || height <= 0) {
-    return NS_ERROR_FAILURE;
+    return nullptr;
   }
 
   // convert twips to points
   width /= TWIPS_PER_POINT_FLOAT;
   height /= TWIPS_PER_POINT_FLOAT;
 
   RefPtr<gfxASurface> surface = gfxPlatform::GetPlatform()->
     CreateOffscreenSurface(mozilla::gfx::IntSize(width, height),
                            mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32);
+  if (!surface) {
+    return nullptr;
+  }
 
-  surface.forget(aSurface);
-  return NS_OK;
+  // The type of PrintTarget that we return here doesn't really matter since
+  // our implementation of GetDrawEventRecorder returns an object, which means
+  // the DrawTarget returned by the PrintTarget will be a DrawTargetRecording.
+  // The recording will be serialized and sent over to the parent process where
+  // PrintTranslator::TranslateRecording will call MakePrintTarget (indirectly
+  // via PrintTranslator::CreateDrawTarget) on whatever type of
+  // nsIDeviceContextSpecProxy is created for the platform that we are running
+  // on.  It is that DrawTarget that the recording will be replayed on to
+  // print.
+  RefPtr<PrintTarget> target = PrintTargetThebes::CreateOrNull(surface);
+
+  return target.forget();
 }
 
 NS_IMETHODIMP
 nsDeviceContextSpecProxy::GetDrawEventRecorder(mozilla::gfx::DrawEventRecorder** aDrawEventRecorder)
 {
   MOZ_ASSERT(aDrawEventRecorder);
   RefPtr<mozilla::gfx::DrawEventRecorder> result = mRecorder;
   result.forget(aDrawEventRecorder);
--- a/widget/nsDeviceContextSpecProxy.h
+++ b/widget/nsDeviceContextSpecProxy.h
@@ -25,17 +25,17 @@ class RemotePrintJobChild;
 class nsDeviceContextSpecProxy final : public nsIDeviceContextSpec
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD Init(nsIWidget* aWidget, nsIPrintSettings* aPrintSettings,
                  bool aIsPrintPreview) final;
 
-  NS_METHOD GetSurfaceForPrinter(gfxASurface** aSurface) final;
+  virtual already_AddRefed<PrintTarget> MakePrintTarget() final;
 
   NS_METHOD GetDrawEventRecorder(mozilla::gfx::DrawEventRecorder** aDrawEventRecorder) final;
 
   float GetDPI() final;
 
   float GetPrintingScale() final;
 
   NS_METHOD BeginDocument(const nsAString& aTitle,
--- a/widget/nsIDeviceContextSpec.h
+++ b/widget/nsIDeviceContextSpec.h
@@ -10,40 +10,43 @@
 
 class nsIWidget;
 class nsIPrintSettings;
 class gfxASurface;
 
 namespace mozilla {
 namespace gfx{
 class DrawEventRecorder;
+class PrintTarget;
 }
 }
 
 #define NS_IDEVICE_CONTEXT_SPEC_IID   \
 { 0xf407cfba, 0xbe28, 0x46c9, \
   { 0x8a, 0xba, 0x04, 0x2d, 0xae, 0xbb, 0x4f, 0x23 } }
 
 class nsIDeviceContextSpec : public nsISupports
 {
 public:
+  typedef mozilla::gfx::PrintTarget PrintTarget;
+
    NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDEVICE_CONTEXT_SPEC_IID)
 
    /**
     * Initialize the device context spec.
     * @param aWidget         A widget a dialog can be hosted in
     * @param aPrintSettings  Print settings for the print operation
     * @param aIsPrintPreview True if creating Spec for PrintPreview
     * @return NS_OK or a suitable error code.
     */
    NS_IMETHOD Init(nsIWidget *aWidget,
                    nsIPrintSettings* aPrintSettings,
                    bool aIsPrintPreview) = 0;
 
-   NS_IMETHOD GetSurfaceForPrinter(gfxASurface **nativeSurface) = 0;
+   virtual already_AddRefed<PrintTarget> MakePrintTarget() = 0;
 
    /**
     * If required override to return a recorder to record the print.
     *
     * @param aDrawEventRecorder out param for the recorder to use
     * @return NS_OK or a suitable error code
     */
    NS_IMETHOD GetDrawEventRecorder(mozilla::gfx::DrawEventRecorder** aDrawEventRecorder)
--- a/widget/qt/nsDeviceContextSpecQt.cpp
+++ b/widget/qt/nsDeviceContextSpecQt.cpp
@@ -5,16 +5,17 @@
 
 
 #include <QTemporaryFile>
 #include <QPrinterInfo>
 
 #define SET_PRINTER_FEATURES_VIA_PREFS 1
 #define PRINTERFEATURES_PREF "print.tmp.printerfeatures"
 
+#include "mozilla/gfx/PrintTargetThebes.h"
 #include "mozilla/Logging.h"
 
 #include "plstr.h"
 
 #include "nsDeviceContextSpecQt.h"
 
 #include "prenv.h" /* for PR_GetEnv */
 
@@ -27,16 +28,19 @@
 #include "nsTArray.h"
 
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
 #include "gfxPDFSurface.h"
 
+using namespace mozilla;
+using namespace mozilla::gfx;
+
 static PRLogModuleInfo* DeviceContextSpecQtLM =
     PR_NewLogModule("DeviceContextSpecQt");
 /* Macro to make lines shorter */
 #define DO_PR_DEBUG_LOG(x) MOZ_LOG(DeviceContextSpecQtLM, mozilla::LogLevel::Debug, x)
 
 nsDeviceContextSpecQt::nsDeviceContextSpecQt()
 {
     DO_PR_DEBUG_LOG(("nsDeviceContextSpecQt::nsDeviceContextSpecQt()\n"));
@@ -45,22 +49,18 @@ nsDeviceContextSpecQt::nsDeviceContextSp
 nsDeviceContextSpecQt::~nsDeviceContextSpecQt()
 {
     DO_PR_DEBUG_LOG(("nsDeviceContextSpecQt::~nsDeviceContextSpecQt()\n"));
 }
 
 NS_IMPL_ISUPPORTS(nsDeviceContextSpecQt,
         nsIDeviceContextSpec)
 
-NS_IMETHODIMP nsDeviceContextSpecQt::GetSurfaceForPrinter(
-        gfxASurface** aSurface)
+already_AddRefed<PrintTarget> nsDeviceContextSpecQt::MakePrintTarget()
 {
-    NS_ENSURE_ARG_POINTER(aSurface);
-    *aSurface = nullptr;
-
     double width, height;
     mPrintSettings->GetEffectivePageSize(&width, &height);
 
     // If we're in landscape mode, we'll be rotating the output --
     // need to swap width & height.
     int32_t orientation;
     mPrintSettings->GetOrientation(&orientation);
     if (nsIPrintSettings::kLandscapeOrientation == orientation) {
@@ -72,65 +72,62 @@ NS_IMETHODIMP nsDeviceContextSpecQt::Get
     // convert twips to points
     width  /= TWIPS_PER_POINT_FLOAT;
     height /= TWIPS_PER_POINT_FLOAT;
 
     DO_PR_DEBUG_LOG(("\"%s\", %f, %f\n", mPath, width, height));
 
     QTemporaryFile file;
     if(!file.open()) {
-        return NS_ERROR_GFX_PRINTER_COULD_NOT_OPEN_FILE;
+        return nullptr;
     }
     file.setAutoRemove(false);
 
     nsresult rv = NS_NewNativeLocalFile(
             nsDependentCString(file.fileName().toUtf8().constData()),
             false,
             getter_AddRefs(mSpoolFile));
     if (NS_FAILED(rv)) {
         file.remove();
-        return NS_ERROR_GFX_PRINTER_COULD_NOT_OPEN_FILE;
+        return nullptr;
     }
 
     mSpoolName = file.fileName().toUtf8().constData();
 
     mSpoolFile->SetPermissions(0600);
 
     nsCOMPtr<nsIFileOutputStream> stream =
         do_CreateInstance("@mozilla.org/network/file-output-stream;1");
 
     rv = stream->Init(mSpoolFile, -1, -1, 0);
     if (NS_FAILED(rv))
-        return rv;
+        return nullptr;
 
     int16_t format;
     mPrintSettings->GetOutputFormat(&format);
 
     RefPtr<gfxASurface> surface;
     gfxSize surfaceSize(width, height);
 
     if (format == nsIPrintSettings::kOutputFormatNative) {
         if (mIsPPreview) {
             // There is nothing to detect on Print Preview, use PS.
             // TODO: implement for Qt?
             //format = nsIPrintSettings::kOutputFormatPS;
-            return NS_ERROR_NOT_IMPLEMENTED;
+            return nullptr;
         }
         format = nsIPrintSettings::kOutputFormatPDF;
     }
     if (format == nsIPrintSettings::kOutputFormatPDF) {
         surface = new gfxPDFSurface(stream, surfaceSize);
     } else {
-        return NS_ERROR_NOT_IMPLEMENTED;
+        return nullptr;
     }
 
-    MOZ_ASSERT(surface, "valid address expected");
-
-    surface.swap(*aSurface);
-    return NS_OK;
+    return PrintTargetThebes::CreateOrNull(surface);
 }
 
 NS_IMETHODIMP nsDeviceContextSpecQt::Init(nsIWidget* aWidget,
         nsIPrintSettings* aPS,
         bool aIsPrintPreview)
 {
     DO_PR_DEBUG_LOG(("nsDeviceContextSpecQt::Init(aPS=%p)\n", aPS));
 
--- a/widget/qt/nsDeviceContextSpecQt.h
+++ b/widget/qt/nsDeviceContextSpecQt.h
@@ -17,17 +17,17 @@
 
 class nsDeviceContextSpecQt : public nsIDeviceContextSpec
 {
 public:
     nsDeviceContextSpecQt();
 
     NS_DECL_ISUPPORTS
 
-    NS_IMETHOD GetSurfaceForPrinter(gfxASurface** surface);
+    virtual already_AddRefed<PrintTarget> MakePrintTarget() final;
 
     NS_IMETHOD Init(nsIWidget* aWidget,
                     nsIPrintSettings* aPS,
                     bool aIsPrintPreview);
     NS_IMETHOD BeginDocument(const nsAString& aTitle,
                              const nsAString& aPrintToFileName,
                              int32_t aStartPage,
                              int32_t aEndPage);
--- a/widget/windows/nsDeviceContextSpecWin.cpp
+++ b/widget/windows/nsDeviceContextSpecWin.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/gfx/PrintTargetThebes.h"
 #include "mozilla/RefPtr.h"
 
 #include "nsDeviceContextSpecWin.h"
 #include "prmem.h"
 
 #include <winspool.h>
 
 #include <tchar.h>
@@ -44,16 +45,17 @@
 
 #include "mozilla/gfx/Logging.h"
 
 #include "mozilla/Logging.h"
 PRLogModuleInfo * kWidgetPrintingLogMod = PR_NewLogModule("printing-widget");
 #define PR_PL(_p1)  MOZ_LOG(kWidgetPrintingLogMod, mozilla::LogLevel::Debug, _p1)
 
 using namespace mozilla;
+using namespace mozilla::gfx;
 
 static const wchar_t kDriverName[] =  L"WINSPOOL";
 
 //----------------------------------------------------------------------------------
 // The printer data is shared between the PrinterEnumerator and the nsDeviceContextSpecWin
 // The PrinterEnumerator creates the printer info
 // but the nsDeviceContextSpecWin cleans it up
 // If it gets created (via the Page Setup Dialog) but the user never prints anything
@@ -215,68 +217,72 @@ static void CleanAndCopyString(wchar_t*&
   }
 
   if (nullptr != aNewStr) {
     aStr = (wchar_t *)PR_Malloc(sizeof(wchar_t)*(wcslen(aNewStr) + 1));
     wcscpy(aStr, aNewStr);
   }
 }
 
-NS_IMETHODIMP nsDeviceContextSpecWin::GetSurfaceForPrinter(gfxASurface **surface)
+already_AddRefed<PrintTarget> nsDeviceContextSpecWin::MakePrintTarget()
 {
   NS_ASSERTION(mDevMode, "DevMode can't be NULL here");
 
-  *surface = nullptr;
   RefPtr<gfxASurface> newSurface;
 
   if (mOutputFormat == nsIPrintSettings::kOutputFormatPDF) {
     nsXPIDLString filename;
     mPrintSettings->GetToFileName(getter_Copies(filename));
 
     double width, height;
     mPrintSettings->GetEffectivePageSize(&width, &height);
     if (width <= 0 || height <= 0) {
-      return NS_ERROR_FAILURE;
+      return nullptr;
     }
 
     // convert twips to points
     width  /= TWIPS_PER_POINT_FLOAT;
     height /= TWIPS_PER_POINT_FLOAT;
 
     nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
     nsresult rv = file->InitWithPath(filename);
-    if (NS_FAILED(rv))
-      return rv;
+    if (NS_FAILED(rv)) {
+      return nullptr;
+    }
 
     nsCOMPtr<nsIFileOutputStream> stream = do_CreateInstance("@mozilla.org/network/file-output-stream;1");
     rv = stream->Init(file, -1, -1, 0);
-    if (NS_FAILED(rv))
-      return rv;
+    if (NS_FAILED(rv)) {
+      return nullptr;
+    }
 
     newSurface = new gfxPDFSurface(stream, gfxSize(width, height));
   } else {
     if (mDevMode) {
       NS_WARN_IF_FALSE(mDriverName, "No driver!");
       HDC dc = ::CreateDCW(mDriverName, mDeviceName, nullptr, mDevMode);
       if (!dc) {
         gfxCriticalError(gfxCriticalError::DefaultOptions(false)) << "Failed to create device context in GetSurfaceForPrinter";
-        return NS_ERROR_GFX_PRINTER_NAME_NOT_FOUND;
+        return nullptr;
       }
 
       // have this surface take over ownership of this DC
       newSurface = new gfxWindowsSurface(dc, gfxWindowsSurface::FLAG_TAKE_DC | gfxWindowsSurface::FLAG_FOR_PRINTING);
       if (newSurface->GetType() == (gfxSurfaceType)-1) {
         gfxCriticalError() << "Invalid windows surface from " << gfx::hexa(dc);
-        return NS_ERROR_FAILURE;
+        return nullptr;
       }
     }
   }
 
-  newSurface.forget(surface);
-  return NS_OK;
+  if (!newSurface) {
+    return nullptr;
+  }
+
+  return PrintTargetThebes::CreateOrNull(newSurface);
 }
 
 float
 nsDeviceContextSpecWin::GetDPI()
 {
   // To match the previous printing code we need to return 72 when printing to
   // PDF and 144 when printing to a Windows surface.
   return mOutputFormat == nsIPrintSettings::kOutputFormatPDF ? 72.0f : 144.0f;
--- a/widget/windows/nsDeviceContextSpecWin.h
+++ b/widget/windows/nsDeviceContextSpecWin.h
@@ -19,17 +19,17 @@ class nsIWidget;
 
 class nsDeviceContextSpecWin : public nsIDeviceContextSpec
 {
 public:
   nsDeviceContextSpecWin();
 
   NS_DECL_ISUPPORTS
 
-  NS_IMETHOD GetSurfaceForPrinter(gfxASurface **surface) override;
+  virtual already_AddRefed<PrintTarget> MakePrintTarget() final;
   NS_IMETHOD BeginDocument(const nsAString& aTitle,
                            const nsAString& aPrintToFileName,
                            int32_t          aStartPage,
                            int32_t          aEndPage) override { return NS_OK; }
   NS_IMETHOD EndDocument() override { return NS_OK; }
   NS_IMETHOD BeginPage() override { return NS_OK; }
   NS_IMETHOD EndPage() override { return NS_OK; }