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 343474 b70203799e112afb9d9db09ebfac7f0b0ae71e33
parent 343473 f26550274a458d195d836c18fc706c79d43c372f
child 343475 7ee4fbc79fba11b47c514704cda112e8c5df1dd3
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBas
bugs1278269
milestone50.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 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; }