Bug 1279654 - Create a PrintTargetRecording subclass of PrintTarget. r=mstange
authorJonathan Watt <jwatt@jwatt.org>
Mon, 13 Jun 2016 17:19:08 +0100
changeset 301707 1bd6da31483db28d8fd65a0ef69d8dfe42cb0f0f
parent 301706 c89b8cc657b20d8f854585f10020afcaa001d1bf
child 301708 bf789b359fcd281a8532469a006be63e06705932
push id30339
push usercbook@mozilla.com
push dateMon, 13 Jun 2016 23:16:11 +0000
treeherdermozilla-central@ddb6b3014922 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1279654
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 1279654 - Create a PrintTargetRecording subclass of PrintTarget. r=mstange
gfx/thebes/PrintTarget.cpp
gfx/thebes/PrintTarget.h
gfx/thebes/PrintTargetRecording.cpp
gfx/thebes/PrintTargetRecording.h
gfx/thebes/PrintTargetThebes.cpp
gfx/thebes/PrintTargetThebes.h
gfx/thebes/moz.build
widget/nsDeviceContextSpecProxy.cpp
--- a/gfx/thebes/PrintTarget.cpp
+++ b/gfx/thebes/PrintTarget.cpp
@@ -47,59 +47,33 @@ PrintTarget::~PrintTarget()
 
 already_AddRefed<DrawTarget>
 PrintTarget::MakeDrawTarget(const IntSize& aSize,
                             DrawEventRecorder* aRecorder)
 {
   MOZ_ASSERT(mCairoSurface,
              "We shouldn't have been constructed without a cairo surface");
 
+  MOZ_ASSERT(!aRecorder,
+             "aRecorder should only be passed to an instance of "
+             "PrintTargetRecording");
+
   if (cairo_surface_status(mCairoSurface)) {
     return nullptr;
   }
 
   // Note than aSize may not be the same as mSize (the size of mCairoSurface).
   // See the comments in our header.  If the sizes are different a clip will
   // be applied to mCairoSurface.
   RefPtr<DrawTarget> dt =
     Factory::CreateDrawTargetForCairoSurface(mCairoSurface, aSize);
   if (!dt || !dt->IsValid()) {
     return nullptr;
   }
 
-  if (aRecorder) {
-    dt = CreateRecordingDrawTarget(aRecorder, dt);
-    if (!dt || !dt->IsValid()) {
-      return nullptr;
-    }
-  }
-
-  return dt.forget();
-}
-
-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;
--- a/gfx/thebes/PrintTarget.h
+++ b/gfx/thebes/PrintTarget.h
@@ -94,18 +94,16 @@ public:
    * 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,
@@ -114,20 +112,16 @@ public:
 protected:
 
   // Only created via subclass's constructors
   explicit PrintTarget(cairo_surface_t* aCairoSurface, const IntSize& aSize);
 
   // Protected because we're refcounted
   virtual ~PrintTarget();
 
-  already_AddRefed<DrawTarget>
-  CreateRecordingDrawTarget(DrawEventRecorder* aRecorder,
-                            DrawTarget* aDrawTarget);
-
   cairo_surface_t* mCairoSurface;
   IntSize mSize;
   bool mIsFinished;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/PrintTargetRecording.cpp
@@ -0,0 +1,116 @@
+/* -*- 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 "PrintTargetRecording.h"
+
+#include "cairo.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+PrintTargetRecording::PrintTargetRecording(cairo_surface_t* aCairoSurface,
+                                           const IntSize& aSize)
+  : PrintTarget(aCairoSurface, aSize)
+{
+}
+
+/* static */ already_AddRefed<PrintTargetRecording>
+PrintTargetRecording::CreateOrNull(const IntSize& aSize)
+{
+  if (!Factory::CheckSurfaceSize(aSize)) {
+    return nullptr;
+  }
+
+  // Perhaps surprisingly, this surface is never actually drawn to.  This class
+  // creates a DrawTargetRecording using CreateRecordingDrawTarget, and that
+  // needs another DrawTarget to be passed to it.  You might expect the type of
+  // the DrawTarget that is passed to matter because it would seem logical to
+  // encoded its type in the recording, and on replaying the recording a
+  // DrawTarget of the same type would be created.  However, the passed
+  // DrawTarget's type doesn't seem to be encoded any more accurately than just
+  // "BackendType::CAIRO".  Even if it were, the code that replays the
+  // recording is PrintTranslator::TranslateRecording which (indirectly) calls
+  // MakePrintTarget on the type of nsIDeviceContextSpecProxy that is created
+  // for the platform that we're running on, and the type of DrawTarget that
+  // that returns is hardcoded.
+  //
+  // The only reason that we use cairo_recording_surface_create here is:
+  //
+  //   * It's pretty much the only cairo_*_surface_create methods that's both
+  //     available on all platforms and doesn't require allocating a
+  //     potentially large surface.
+  //
+  //   * Since we need a DrawTarget to pass to CreateRecordingDrawTarget we
+  //     might as well leverage our base class's machinery to create a
+  //     DrawTarget (it's as good a way as any other that will work), and to do
+  //     that we need a cairo_surface_t.
+  //
+  // So the fact that this is a "recording" PrintTarget and the function that
+  // we call here is cairo_recording_surface_create is simply a coincidence. We
+  // could use any cairo_*_surface_create method and this class would still
+  // work.
+  //
+  cairo_surface_t* surface =
+    cairo_recording_surface_create(CAIRO_CONTENT_COLOR_ALPHA, nullptr);
+
+  if (cairo_surface_status(surface)) {
+    return nullptr;
+  }
+
+  // The new object takes ownership of our surface reference.
+  RefPtr<PrintTargetRecording> target =
+    new PrintTargetRecording(surface, aSize);
+
+  return target.forget();
+}
+
+already_AddRefed<DrawTarget>
+PrintTargetRecording::MakeDrawTarget(const IntSize& aSize,
+                                     DrawEventRecorder* aRecorder)
+{
+  MOZ_ASSERT(aRecorder, "A DrawEventRecorder is required");
+
+  if (!aRecorder) {
+    return nullptr;
+  }
+
+  RefPtr<DrawTarget> dt = PrintTarget::MakeDrawTarget(aSize, nullptr);
+  if (dt) {
+    dt = CreateRecordingDrawTarget(aRecorder, dt);
+    if (!dt || !dt->IsValid()) {
+      return nullptr;
+    }
+  }
+
+  return dt.forget();
+}
+
+already_AddRefed<DrawTarget>
+PrintTargetRecording::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();
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/PrintTargetRecording.h
@@ -0,0 +1,43 @@
+/* -*- 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_PRINTTARGETRECORDING_H
+#define MOZILLA_GFX_PRINTTARGETRECORDING_H
+
+#include "PrintTarget.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Recording printing target.
+ *
+ * This exists for use on e10s's content process in order to record print
+ * output, send it over to the parent process, and replay it on a DrawTarget
+ * there for printing.
+ */
+class PrintTargetRecording final : public PrintTarget
+{
+public:
+  static already_AddRefed<PrintTargetRecording>
+  CreateOrNull(const IntSize& aSize);
+
+  virtual already_AddRefed<DrawTarget>
+  MakeDrawTarget(const IntSize& aSize,
+                 DrawEventRecorder* aRecorder = nullptr) override;
+
+private:
+  PrintTargetRecording(cairo_surface_t* aCairoSurface,
+                       const IntSize& aSize);
+
+  already_AddRefed<DrawTarget>
+  CreateRecordingDrawTarget(DrawEventRecorder* aRecorder,
+                            DrawTarget* aDrawTarget);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PRINTTARGETRECORDING_H */
--- a/gfx/thebes/PrintTargetThebes.cpp
+++ b/gfx/thebes/PrintTargetThebes.cpp
@@ -31,29 +31,26 @@ PrintTargetThebes::PrintTargetThebes(gfx
   , mGfxSurface(aSurface)
 {
 }
 
 already_AddRefed<DrawTarget>
 PrintTargetThebes::MakeDrawTarget(const IntSize& aSize,
                                   DrawEventRecorder* aRecorder)
 {
+  MOZ_ASSERT(!aRecorder,
+             "aRecorder should only be passed to an instance of "
+             "PrintTargetRecording");
+
   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);
--- a/gfx/thebes/PrintTargetThebes.h
+++ b/gfx/thebes/PrintTargetThebes.h
@@ -14,19 +14,16 @@ 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,
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -47,16 +47,17 @@ EXPORTS += [
     'gfxUtils.h',
     'RoundedRect.h',
     'SoftwareVsyncSource.h',
     'VsyncSource.h',
 ]
 
 EXPORTS.mozilla.gfx += [
     'PrintTarget.h',
+    'PrintTargetRecording.h',
     'PrintTargetThebes.h',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     EXPORTS += [
         'gfxAndroidPlatform.h',
         'gfxFT2FontBase.h',
         'gfxFT2Fonts.h',
@@ -212,16 +213,17 @@ SOURCES += [
     # 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',
+    'PrintTargetRecording.cpp',
     'PrintTargetThebes.cpp',
 ]
 
 UNIFIED_SOURCES += [
     'CJKCompatSVS.cpp',
     'gfxAlphaRecovery.cpp',
     'gfxBaseSharedMemorySurface.cpp',
     'gfxBlur.cpp',
--- a/widget/nsDeviceContextSpecProxy.cpp
+++ b/widget/nsDeviceContextSpecProxy.cpp
@@ -1,20 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "nsDeviceContextSpecProxy.h"
 
-#include "gfxASurface.h"
 #include "gfxPlatform.h"
 #include "mozilla/gfx/DrawEventRecorder.h"
-#include "mozilla/gfx/PrintTargetThebes.h"
+#include "mozilla/gfx/PrintTargetRecording.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;
@@ -75,35 +74,17 @@ nsDeviceContextSpecProxy::MakePrintTarge
   if (NS_WARN_IF(NS_FAILED(rv)) || width <= 0 || height <= 0) {
     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;
-  }
-
-  // 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();
+  return PrintTargetRecording::CreateOrNull(IntSize(width, height));
 }
 
 NS_IMETHODIMP
 nsDeviceContextSpecProxy::GetDrawEventRecorder(mozilla::gfx::DrawEventRecorder** aDrawEventRecorder)
 {
   MOZ_ASSERT(aDrawEventRecorder);
   RefPtr<mozilla::gfx::DrawEventRecorder> result = mRecorder;
   result.forget(aDrawEventRecorder);