author Sandor Molnar <>
Fri, 24 Sep 2021 00:43:42 +0300
changeset 593102 4eda9eb8926bdd50f4b80128ce3475eb7c6d9a4d
parent 553870 b50ec2e6a0a9f30972a51a9ece9639f2de79f015
permissions -rw-r--r--
Merge autoland to mozilla-central. a=merge

/* -*- Mode: C++; tab-width: 20; 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 */


#include <functional>

#include "mozilla/RefPtr.h"
#include "mozilla/gfx/2D.h"
#include "nsISupportsImpl.h"
#include "nsStringFwd.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 {
  typedef std::function<void(nsresult)> PageDoneCallback;


  /// Must be matched 1:1 by an EndPrinting/AbortPrinting call.
  virtual nsresult BeginPrinting(const nsAString& aTitle,
                                 const nsAString& aPrintToFileName,
                                 int32_t aStartPage, int32_t aEndPage) {
    return NS_OK;
  virtual nsresult EndPrinting() { return NS_OK; }
  virtual nsresult AbortPrinting() {
#ifdef DEBUG
    mHasActivePage = false;
    return NS_OK;
  virtual nsresult BeginPage() {
#ifdef DEBUG
    MOZ_ASSERT(!mHasActivePage, "Missing EndPage() call");
    mHasActivePage = true;
    return NS_OK;
  virtual nsresult EndPage() {
#ifdef DEBUG
    mHasActivePage = false;
    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.
   * NOTE: this should only be called between BeginPage()/EndPage() calls, and
   * the returned DrawTarget should not be drawn to after EndPage() has been
   * called.
   * 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);

   * Returns a reference DrawTarget. Unlike MakeDrawTarget, this method is not
   * restricted to being called between BeginPage()/EndPage() calls, and the
   * returned DrawTarget is still valid to use after EndPage() has been called.
  virtual already_AddRefed<DrawTarget> GetReferenceDrawTarget();

   * If IsSyncPagePrinting returns true, then a user can assume the content of
   * a page was already printed after EndPage().
   * If IsSyncPagePrinting returns false, then a user should register a
   * callback function using RegisterPageDoneCallback to receive page print
   * done notifications.
  virtual bool IsSyncPagePrinting() const { return true; }
  void RegisterPageDoneCallback(PageDoneCallback&& aCallback);
  void UnregisterPageDoneCallback();

  static void AdjustPrintJobNameForIPP(const nsAString& aJobName,
                                       nsCString& aAdjustedJobName);
  static void AdjustPrintJobNameForIPP(const nsAString& aJobName,
                                       nsString& aAdjustedJobName);

  // Only created via subclass's constructors
  explicit PrintTarget(cairo_surface_t* aCairoSurface, const IntSize& aSize);

  // Protected because we're refcounted
  virtual ~PrintTarget();

  static already_AddRefed<DrawTarget> CreateRecordingDrawTarget(
      DrawEventRecorder* aRecorder, DrawTarget* aDrawTarget);

  cairo_surface_t* mCairoSurface;
  RefPtr<DrawTarget> mRefDT;  // reference DT

  IntSize mSize;
  bool mIsFinished;
#ifdef DEBUG
  bool mHasActivePage;

  PageDoneCallback mPageDoneCallback;

}  // namespace gfx
}  // namespace mozilla