tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RemotePrintJobParent.cpp (11297B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "RemotePrintJobParent.h"
      8 
      9 #include <fstream>
     10 
     11 #include "PrintTranslator.h"
     12 #include "gfxContext.h"
     13 #include "mozilla/ProfilerMarkers.h"
     14 #include "nsAnonymousTemporaryFile.h"
     15 #include "nsAppDirectoryServiceDefs.h"
     16 #include "nsComponentManagerUtils.h"
     17 #include "nsDeviceContext.h"
     18 #include "nsDirectoryServiceUtils.h"
     19 #include "nsIDeviceContextSpec.h"
     20 #include "nsIPrintSettings.h"
     21 #include "nsIWebProgressListener.h"
     22 #include "private/pprio.h"
     23 
     24 namespace mozilla::layout {
     25 
     26 RemotePrintJobParent::RemotePrintJobParent(nsIPrintSettings* aPrintSettings)
     27    : mPrintSettings(aPrintSettings),
     28      mIsDoingPrinting(false),
     29      mStatus(NS_ERROR_UNEXPECTED) {
     30  MOZ_COUNT_CTOR(RemotePrintJobParent);
     31 }
     32 
     33 mozilla::ipc::IPCResult RemotePrintJobParent::RecvInitializePrint(
     34    const nsAString& aDocumentTitle, const int32_t& aStartPage,
     35    const int32_t& aEndPage) {
     36  PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
     37                       "RemotePrintJobParent::RecvInitializePrint"_ns);
     38 
     39  nsresult rv = InitializePrintDevice(aDocumentTitle, aStartPage, aEndPage);
     40  if (NS_FAILED(rv)) {
     41    (void)SendPrintInitializationResult(rv, FileDescriptor());
     42    mStatus = rv;
     43    (void)Send__delete__(this);
     44    return IPC_OK();
     45  }
     46 
     47  mPrintTranslator = MakeUnique<PrintTranslator>(mPrintDeviceContext);
     48  FileDescriptor fd;
     49  rv = PrepareNextPageFD(&fd);
     50  if (NS_FAILED(rv)) {
     51    (void)SendPrintInitializationResult(rv, FileDescriptor());
     52    mStatus = rv;
     53    (void)Send__delete__(this);
     54    return IPC_OK();
     55  }
     56 
     57  (void)SendPrintInitializationResult(NS_OK, fd);
     58  return IPC_OK();
     59 }
     60 
     61 nsresult RemotePrintJobParent::InitializePrintDevice(
     62    const nsAString& aDocumentTitle, const int32_t& aStartPage,
     63    const int32_t& aEndPage) {
     64  AUTO_PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
     65                            "RemotePrintJobParent::InitializePrintDevice"_ns);
     66 
     67  nsresult rv;
     68  nsCOMPtr<nsIDeviceContextSpec> deviceContextSpec =
     69      do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv);
     70  if (NS_WARN_IF(NS_FAILED(rv))) {
     71    return rv;
     72  }
     73 
     74  rv = deviceContextSpec->Init(mPrintSettings, false);
     75  if (NS_WARN_IF(NS_FAILED(rv))) {
     76    return rv;
     77  }
     78 
     79  mPrintDeviceContext = new nsDeviceContext();
     80  rv = mPrintDeviceContext->InitForPrinting(deviceContextSpec);
     81  if (NS_WARN_IF(NS_FAILED(rv))) {
     82    return rv;
     83  }
     84 
     85  nsAutoString fileName;
     86  mPrintSettings->GetToFileName(fileName);
     87 
     88  rv = mPrintDeviceContext->BeginDocument(aDocumentTitle, fileName, aStartPage,
     89                                          aEndPage);
     90  if (NS_FAILED(rv)) {
     91    NS_WARNING_ASSERTION(rv == NS_ERROR_ABORT,
     92                         "Failed to initialize print device");
     93    return rv;
     94  }
     95 
     96  mIsDoingPrinting = true;
     97 
     98  return NS_OK;
     99 }
    100 
    101 nsresult RemotePrintJobParent::PrepareNextPageFD(FileDescriptor* aFd) {
    102  AUTO_PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
    103                            "RemotePrintJobParent::PrepareNextPageFD"_ns);
    104 
    105  PRFileDesc* prFd = nullptr;
    106  nsresult rv = NS_OpenAnonymousTemporaryFile(&prFd);
    107  if (NS_FAILED(rv)) {
    108    return rv;
    109  }
    110  *aFd = FileDescriptor(
    111      FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prFd)));
    112  mCurrentPageStream.OpenFD(prFd);
    113  return NS_OK;
    114 }
    115 
    116 mozilla::ipc::IPCResult RemotePrintJobParent::RecvProcessPage(
    117    const int32_t& aWidthInPoints, const int32_t& aHeightInPoints,
    118    nsTArray<uint64_t>&& aDeps) {
    119  PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
    120                       "RemotePrintJobParent::RecvProcessPage"_ns);
    121  if (!mCurrentPageStream.IsOpen()) {
    122    (void)SendAbortPrint(NS_ERROR_FAILURE);
    123    return IPC_OK();
    124  }
    125  mCurrentPageStream.Seek(0, PR_SEEK_SET);
    126 
    127  gfx::IntSize pageSizeInPoints(aWidthInPoints, aHeightInPoints);
    128 
    129  if (aDeps.IsEmpty()) {
    130    FinishProcessingPage(pageSizeInPoints);
    131    return IPC_OK();
    132  }
    133 
    134  nsTHashSet<uint64_t> deps;
    135  for (auto i : aDeps) {
    136    deps.Insert(i);
    137  }
    138 
    139  gfx::CrossProcessPaint::Start(std::move(deps))
    140      ->Then(
    141          GetCurrentSerialEventTarget(), __func__,
    142          [self = RefPtr{this}, pageSizeInPoints](
    143              gfx::CrossProcessPaint::ResolvedFragmentMap&& aFragments) {
    144            self->FinishProcessingPage(pageSizeInPoints, &aFragments);
    145          },
    146          [self = RefPtr{this}, pageSizeInPoints](const nsresult& aRv) {
    147            self->FinishProcessingPage(pageSizeInPoints);
    148          });
    149 
    150  return IPC_OK();
    151 }
    152 
    153 void RemotePrintJobParent::FinishProcessingPage(
    154    const gfx::IntSize& aSizeInPoints,
    155    gfx::CrossProcessPaint::ResolvedFragmentMap* aFragments) {
    156  nsresult rv = PrintPage(aSizeInPoints, mCurrentPageStream, aFragments);
    157 
    158  mCurrentPageStream.Close();
    159 
    160  PageDone(rv);
    161 }
    162 
    163 nsresult RemotePrintJobParent::PrintPage(
    164    const gfx::IntSize& aSizeInPoints, PRFileDescStream& aRecording,
    165    gfx::CrossProcessPaint::ResolvedFragmentMap* aFragments) {
    166  MOZ_ASSERT(mPrintDeviceContext);
    167  AUTO_PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
    168                            "RemotePrintJobParent::PrintPage"_ns);
    169 
    170  nsresult rv = mPrintDeviceContext->BeginPage(aSizeInPoints);
    171  if (NS_WARN_IF(NS_FAILED(rv))) {
    172    return rv;
    173  }
    174  if (aFragments) {
    175    mPrintTranslator->SetDependentSurfaces(aFragments);
    176  }
    177  if (!mPrintTranslator->TranslateRecording(aRecording)) {
    178    mPrintTranslator->SetDependentSurfaces(nullptr);
    179    return NS_ERROR_FAILURE;
    180  }
    181  mPrintTranslator->SetDependentSurfaces(nullptr);
    182 
    183  rv = mPrintDeviceContext->EndPage();
    184  if (NS_WARN_IF(NS_FAILED(rv))) {
    185    return rv;
    186  }
    187 
    188  return NS_OK;
    189 }
    190 
    191 void RemotePrintJobParent::PageDone(nsresult aResult) {
    192  MOZ_ASSERT(mIsDoingPrinting);
    193 
    194  if (NS_FAILED(aResult)) {
    195    (void)SendAbortPrint(aResult);
    196  } else {
    197    FileDescriptor fd;
    198    aResult = PrepareNextPageFD(&fd);
    199    if (NS_FAILED(aResult)) {
    200      (void)SendAbortPrint(aResult);
    201    }
    202 
    203    (void)SendPageProcessed(fd);
    204  }
    205 }
    206 
    207 static void NotifyStatusChange(
    208    const nsCOMArray<nsIWebProgressListener>& aListeners, nsresult aStatus) {
    209  uint32_t numberOfListeners = aListeners.Length();
    210  for (uint32_t i = 0; i < numberOfListeners; ++i) {
    211    nsIWebProgressListener* listener = aListeners[static_cast<int32_t>(i)];
    212    listener->OnStatusChange(nullptr, nullptr, aStatus, nullptr);
    213  }
    214 }
    215 
    216 static void NotifyStateChange(
    217    const nsCOMArray<nsIWebProgressListener>& aListeners, long aStateFlags,
    218    nsresult aStatus) {
    219  uint32_t numberOfListeners = aListeners.Length();
    220  for (uint32_t i = 0; i < numberOfListeners; ++i) {
    221    nsIWebProgressListener* listener = aListeners[static_cast<int32_t>(i)];
    222    listener->OnStateChange(nullptr, nullptr, aStateFlags, aStatus);
    223  }
    224 }
    225 
    226 static void Cleanup(const nsCOMArray<nsIWebProgressListener>& aListeners,
    227                    RefPtr<nsDeviceContext>& aAbortContext,
    228                    const bool aPrintingInterrupted, const nsresult aResult) {
    229  auto result = aResult;
    230  if (MOZ_UNLIKELY(aPrintingInterrupted && NS_SUCCEEDED(result))) {
    231    result = NS_ERROR_UNEXPECTED;
    232  }
    233  if (NS_FAILED(result)) {
    234    NotifyStatusChange(aListeners, result);
    235  }
    236  if (aPrintingInterrupted && aAbortContext) {
    237    // Abort any started print.
    238    (void)aAbortContext->AbortDocument();
    239  }
    240  // However the print went, let the listeners know that we're done.
    241  NotifyStateChange(aListeners,
    242                    nsIWebProgressListener::STATE_STOP |
    243                        nsIWebProgressListener::STATE_IS_DOCUMENT,
    244                    result);
    245 }
    246 
    247 mozilla::ipc::IPCResult RemotePrintJobParent::RecvFinalizePrint() {
    248  PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
    249                       "RemotePrintJobParent::RecvFinalizePrint"_ns);
    250 
    251  // EndDocument is sometimes called in the child even when BeginDocument has
    252  // not been called. See bug 1223332.
    253  if (mPrintDeviceContext) {
    254    mPrintDeviceContext->EndDocument()->Then(
    255        GetMainThreadSerialEventTarget(), __func__,
    256        [listeners = std::move(mPrintProgressListeners)](
    257            const mozilla::gfx::PrintEndDocumentPromise::ResolveOrRejectValue&
    258                aResult) {
    259          // Printing isn't interrupted, so we don't need the device context
    260          // here.
    261          RefPtr<nsDeviceContext> empty;
    262          if (aResult.IsResolve()) {
    263            Cleanup(listeners, empty, /* aPrintingInterrupted = */ false,
    264                    NS_OK);
    265          } else {
    266            Cleanup(listeners, empty, /* aPrintingInterrupted = */ false,
    267                    aResult.RejectValue());
    268          }
    269        });
    270    mStatus = NS_OK;
    271  }
    272 
    273  mIsDoingPrinting = false;
    274 
    275  (void)Send__delete__(this);
    276  return IPC_OK();
    277 }
    278 
    279 mozilla::ipc::IPCResult RemotePrintJobParent::RecvAbortPrint(
    280    const nsresult& aRv) {
    281  PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
    282                       "RemotePrintJobParent::RecvAbortPrint"_ns);
    283 
    284  // Leave the cleanup to `ActorDestroy()`.
    285  (void)Send__delete__(this);
    286  return IPC_OK();
    287 }
    288 
    289 mozilla::ipc::IPCResult RemotePrintJobParent::RecvProgressChange(
    290    const long& aCurSelfProgress, const long& aMaxSelfProgress,
    291    const long& aCurTotalProgress, const long& aMaxTotalProgress) {
    292  PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
    293                       "RemotePrintJobParent::RecvProgressChange"_ns);
    294  // Our progress follows that of `RemotePrintJobChild` closely enough - forward
    295  // it instead of keeping more state variables here.
    296  for (auto* listener : mPrintProgressListeners) {
    297    listener->OnProgressChange(nullptr, nullptr, aCurSelfProgress,
    298                               aMaxSelfProgress, aCurTotalProgress,
    299                               aMaxTotalProgress);
    300  }
    301  return IPC_OK();
    302 }
    303 
    304 mozilla::ipc::IPCResult RemotePrintJobParent::RecvStatusChange(
    305    const nsresult& aStatus) {
    306  PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
    307                       "RemotePrintJobParent::RecvProgressChange"_ns);
    308  if (NS_FAILED(aStatus)) {
    309    // Remember the failure status for cleanup to forward to listeners.
    310    mStatus = aStatus;
    311  }
    312 
    313  return IPC_OK();
    314 }
    315 
    316 void RemotePrintJobParent::RegisterListener(nsIWebProgressListener* aListener) {
    317  MOZ_ASSERT(aListener);
    318 
    319  // Our listener is a Promise created by CanonicalBrowsingContext::Print
    320  mPrintProgressListeners.AppendElement(aListener);
    321 }
    322 
    323 already_AddRefed<nsIPrintSettings> RemotePrintJobParent::GetPrintSettings() {
    324  nsCOMPtr<nsIPrintSettings> printSettings = mPrintSettings;
    325  return printSettings.forget();
    326 }
    327 
    328 RemotePrintJobParent::~RemotePrintJobParent() {
    329  MOZ_COUNT_DTOR(RemotePrintJobParent);
    330 }
    331 
    332 void RemotePrintJobParent::ActorDestroy(ActorDestroyReason aWhy) {
    333  if (MOZ_UNLIKELY(mIsDoingPrinting && NS_SUCCEEDED(mStatus))) {
    334    mStatus = NS_ERROR_UNEXPECTED;
    335  }
    336  Cleanup(mPrintProgressListeners, mPrintDeviceContext, mIsDoingPrinting,
    337          mStatus);
    338  // At any rate, this actor is done and cleaned up.
    339  mIsDoingPrinting = false;
    340 }
    341 
    342 }  // namespace mozilla::layout