summaryrefslogtreecommitdiffstats
path: root/layout/printing/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'layout/printing/ipc')
-rw-r--r--layout/printing/ipc/PRemotePrintJob.ipdl57
-rw-r--r--layout/printing/ipc/RemotePrintJobChild.cpp171
-rw-r--r--layout/printing/ipc/RemotePrintJobChild.h69
-rw-r--r--layout/printing/ipc/RemotePrintJobParent.cpp344
-rw-r--r--layout/printing/ipc/RemotePrintJobParent.h102
5 files changed, 743 insertions, 0 deletions
diff --git a/layout/printing/ipc/PRemotePrintJob.ipdl b/layout/printing/ipc/PRemotePrintJob.ipdl
new file mode 100644
index 0000000000..50b978cfc7
--- /dev/null
+++ b/layout/printing/ipc/PRemotePrintJob.ipdl
@@ -0,0 +1,57 @@
+/* -*- 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 protocol PContent;
+
+namespace mozilla {
+namespace layout {
+
+[ChildImpl=virtual, ParentImpl=virtual]
+async protocol PRemotePrintJob
+{
+ manager PContent;
+
+both:
+ // Tell either side to abort printing and clean up.
+ async AbortPrint(nsresult aRv);
+
+parent:
+ // Initialize the real print device with the given information.
+ async InitializePrint(nsString aDocumentTitle,
+ int32_t aStartPage, int32_t aEndPage);
+
+ // Translate the page recording writen into |fd| and play back the events to
+ // the real print device.
+ async ProcessPage(int32_t aWidthInPoints, int32_t aHeightInPoints, uint64_t[] aDeps);
+
+ // This informs the real print device that we've finished, so it can trigger
+ // the actual print.
+ async FinalizePrint();
+
+ // Report a progress change to listeners in the parent process.
+ async ProgressChange(long aCurSelfProgress,
+ long aMaxSelfProgress,
+ long aCurTotalProgress,
+ long aMaxTotalProgress);
+
+ // Report a status change to listeners in the parent process.
+ async StatusChange(nsresult aStatus);
+
+child:
+ // Inform the child that the print has been initialized in the parent or has
+ // failed with result aRv. Includes a file descriptor which the first page
+ // can be written to.
+ async PrintInitializationResult(nsresult aRv, FileDescriptor aFd);
+
+ // Inform the child that the latest page has been processed remotely. Includes
+ // a file descriptor which the next page can be written to.
+ async PageProcessed(FileDescriptor aFd);
+
+ async __delete__();
+};
+
+} // namespace layout
+} // namespace mozilla
diff --git a/layout/printing/ipc/RemotePrintJobChild.cpp b/layout/printing/ipc/RemotePrintJobChild.cpp
new file mode 100644
index 0000000000..bc92272451
--- /dev/null
+++ b/layout/printing/ipc/RemotePrintJobChild.cpp
@@ -0,0 +1,171 @@
+/* -*- 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 "RemotePrintJobChild.h"
+
+#include "mozilla/SpinEventLoopUntil.h"
+#include "mozilla/Unused.h"
+#include "nsPagePrintTimer.h"
+#include "nsPrintJob.h"
+#include "private/pprio.h"
+
+namespace mozilla {
+namespace layout {
+
+NS_IMPL_ISUPPORTS(RemotePrintJobChild, nsIWebProgressListener)
+
+RemotePrintJobChild::RemotePrintJobChild() = default;
+
+nsresult RemotePrintJobChild::InitializePrint(const nsString& aDocumentTitle,
+ const int32_t& aStartPage,
+ const int32_t& aEndPage) {
+ // Print initialization can sometimes display a dialog in the parent, so we
+ // need to spin a nested event loop until initialization completes.
+ Unused << SendInitializePrint(aDocumentTitle, aStartPage, aEndPage);
+ mozilla::SpinEventLoopUntil("RemotePrintJobChild::InitializePrint"_ns,
+ [&]() { return mPrintInitialized; });
+
+ return mInitializationResult;
+}
+
+mozilla::ipc::IPCResult RemotePrintJobChild::RecvPrintInitializationResult(
+ const nsresult& aRv, const mozilla::ipc::FileDescriptor& aFd) {
+ mPrintInitialized = true;
+ mInitializationResult = aRv;
+ if (NS_SUCCEEDED(aRv)) {
+ SetNextPageFD(aFd);
+ }
+ return IPC_OK();
+}
+
+PRFileDesc* RemotePrintJobChild::GetNextPageFD() {
+ MOZ_ASSERT(!mDestroyed);
+ MOZ_ASSERT(mNextPageFD);
+ PRFileDesc* fd = mNextPageFD;
+ mNextPageFD = nullptr;
+ return fd;
+}
+
+void RemotePrintJobChild::SetNextPageFD(
+ const mozilla::ipc::FileDescriptor& aFd) {
+ MOZ_ASSERT(!mDestroyed);
+ auto handle = aFd.ClonePlatformHandle();
+ mNextPageFD = PR_ImportFile(PROsfd(handle.release()));
+}
+
+void RemotePrintJobChild::ProcessPage(const IntSize& aSizeInPoints,
+ nsTArray<uint64_t>&& aDeps) {
+ MOZ_ASSERT(mPagePrintTimer);
+
+ mPagePrintTimer->WaitForRemotePrint();
+ if (!mDestroyed) {
+ Unused << SendProcessPage(aSizeInPoints.width, aSizeInPoints.height,
+ std::move(aDeps));
+ }
+}
+
+mozilla::ipc::IPCResult RemotePrintJobChild::RecvPageProcessed(
+ const mozilla::ipc::FileDescriptor& aFd) {
+ MOZ_ASSERT(mPagePrintTimer);
+ SetNextPageFD(aFd);
+
+ mPagePrintTimer->RemotePrintFinished();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RemotePrintJobChild::RecvAbortPrint(
+ const nsresult& aRv) {
+ MOZ_ASSERT(mPrintJob);
+
+ mPrintJob->CleanupOnFailure(aRv, true);
+ return IPC_OK();
+}
+
+void RemotePrintJobChild::SetPagePrintTimer(nsPagePrintTimer* aPagePrintTimer) {
+ MOZ_ASSERT(!mDestroyed);
+ MOZ_ASSERT(aPagePrintTimer);
+
+ mPagePrintTimer = aPagePrintTimer;
+}
+
+void RemotePrintJobChild::SetPrintJob(nsPrintJob* aPrintJob) {
+ MOZ_ASSERT(!mDestroyed);
+ MOZ_ASSERT(aPrintJob);
+
+ mPrintJob = aPrintJob;
+}
+
+// nsIWebProgressListener
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnStateChange(nsIWebProgress* aProgress,
+ nsIRequest* aRequest, uint32_t aStateFlags,
+ nsresult aStatus) {
+ // `RemotePrintJobParent` emits its own state change events based on its
+ // own progress & the actor lifecycle, so any forwarded event here would get
+ // ignored.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnProgressChange(nsIWebProgress* aProgress,
+ nsIRequest* aRequest,
+ int32_t aCurSelfProgress,
+ int32_t aMaxSelfProgress,
+ int32_t aCurTotalProgress,
+ int32_t aMaxTotalProgress) {
+ if (!mDestroyed) {
+ Unused << SendProgressChange(aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnLocationChange(nsIWebProgress* aProgress,
+ nsIRequest* aRequest, nsIURI* aURI,
+ uint32_t aFlags) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnStatusChange(nsIWebProgress* aProgress,
+ nsIRequest* aRequest, nsresult aStatus,
+ const char16_t* aMessage) {
+ if (NS_SUCCEEDED(mInitializationResult) && !mDestroyed) {
+ Unused << SendStatusChange(aStatus);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnSecurityChange(nsIWebProgress* aProgress,
+ nsIRequest* aRequest, uint32_t aState) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnContentBlockingEvent(nsIWebProgress* aProgress,
+ nsIRequest* aRequest,
+ uint32_t aEvent) {
+ return NS_OK;
+}
+
+// End of nsIWebProgressListener
+
+RemotePrintJobChild::~RemotePrintJobChild() = default;
+
+void RemotePrintJobChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mPagePrintTimer = nullptr;
+ mPrintJob = nullptr;
+
+ mDestroyed = true;
+}
+
+} // namespace layout
+} // namespace mozilla
diff --git a/layout/printing/ipc/RemotePrintJobChild.h b/layout/printing/ipc/RemotePrintJobChild.h
new file mode 100644
index 0000000000..73f2b5ef9c
--- /dev/null
+++ b/layout/printing/ipc/RemotePrintJobChild.h
@@ -0,0 +1,69 @@
+/* -*- 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/. */
+
+#ifndef mozilla_layout_RemotePrintJobChild_h
+#define mozilla_layout_RemotePrintJobChild_h
+
+#include "mozilla/layout/PRemotePrintJobChild.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/Point.h"
+#include "nsIWebProgressListener.h"
+
+class nsPagePrintTimer;
+class nsPrintJob;
+
+namespace mozilla {
+namespace layout {
+
+class RemotePrintJobChild final : public PRemotePrintJobChild,
+ public nsIWebProgressListener {
+ public:
+ using IntSize = mozilla::gfx::IntSize;
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIWEBPROGRESSLISTENER
+
+ RemotePrintJobChild();
+
+ void ActorDestroy(ActorDestroyReason aWhy) final;
+
+ nsresult InitializePrint(const nsString& aDocumentTitle,
+ const int32_t& aStartPage, const int32_t& aEndPage);
+
+ mozilla::ipc::IPCResult RecvPrintInitializationResult(
+ const nsresult& aRv, const FileDescriptor& aFd) final;
+
+ void ProcessPage(const IntSize& aSizeInPoints, nsTArray<uint64_t>&& aDeps);
+
+ mozilla::ipc::IPCResult RecvPageProcessed(const FileDescriptor& aFd) final;
+
+ mozilla::ipc::IPCResult RecvAbortPrint(const nsresult& aRv) final;
+
+ void SetPagePrintTimer(nsPagePrintTimer* aPagePrintTimer);
+
+ void SetPrintJob(nsPrintJob* aPrintJob);
+
+ PRFileDesc* GetNextPageFD();
+
+ [[nodiscard]] bool IsDestroyed() const { return mDestroyed; }
+
+ private:
+ ~RemotePrintJobChild() final;
+ void SetNextPageFD(const mozilla::ipc::FileDescriptor& aFd);
+
+ bool mPrintInitialized = false;
+ bool mDestroyed = false;
+ nsresult mInitializationResult = NS_OK;
+ RefPtr<nsPagePrintTimer> mPagePrintTimer;
+ RefPtr<nsPrintJob> mPrintJob;
+ PRFileDesc* mNextPageFD = nullptr;
+};
+
+} // namespace layout
+} // namespace mozilla
+
+#endif // mozilla_layout_RemotePrintJobChild_h
diff --git a/layout/printing/ipc/RemotePrintJobParent.cpp b/layout/printing/ipc/RemotePrintJobParent.cpp
new file mode 100644
index 0000000000..4e4e363a8a
--- /dev/null
+++ b/layout/printing/ipc/RemotePrintJobParent.cpp
@@ -0,0 +1,344 @@
+/* -*- 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 "RemotePrintJobParent.h"
+
+#include <fstream>
+
+#include "gfxContext.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ProfilerMarkers.h"
+#include "mozilla/Unused.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsComponentManagerUtils.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDeviceContext.h"
+#include "nsIDeviceContextSpec.h"
+#include "nsIPrintSettings.h"
+#include "nsIWebProgressListener.h"
+#include "PrintTranslator.h"
+#include "private/pprio.h"
+#include "nsAnonymousTemporaryFile.h"
+
+namespace mozilla::layout {
+
+RemotePrintJobParent::RemotePrintJobParent(nsIPrintSettings* aPrintSettings)
+ : mPrintSettings(aPrintSettings),
+ mIsDoingPrinting(false),
+ mStatus(NS_ERROR_UNEXPECTED) {
+ MOZ_COUNT_CTOR(RemotePrintJobParent);
+}
+
+mozilla::ipc::IPCResult RemotePrintJobParent::RecvInitializePrint(
+ const nsAString& aDocumentTitle, const int32_t& aStartPage,
+ const int32_t& aEndPage) {
+ PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
+ "RemotePrintJobParent::RecvInitializePrint"_ns);
+
+ nsresult rv = InitializePrintDevice(aDocumentTitle, aStartPage, aEndPage);
+ if (NS_FAILED(rv)) {
+ Unused << SendPrintInitializationResult(rv, FileDescriptor());
+ mStatus = rv;
+ Unused << Send__delete__(this);
+ return IPC_OK();
+ }
+
+ mPrintTranslator.reset(new PrintTranslator(mPrintDeviceContext));
+ FileDescriptor fd;
+ rv = PrepareNextPageFD(&fd);
+ if (NS_FAILED(rv)) {
+ Unused << SendPrintInitializationResult(rv, FileDescriptor());
+ mStatus = rv;
+ Unused << Send__delete__(this);
+ return IPC_OK();
+ }
+
+ Unused << SendPrintInitializationResult(NS_OK, fd);
+ return IPC_OK();
+}
+
+nsresult RemotePrintJobParent::InitializePrintDevice(
+ const nsAString& aDocumentTitle, const int32_t& aStartPage,
+ const int32_t& aEndPage) {
+ AUTO_PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
+ "RemotePrintJobParent::InitializePrintDevice"_ns);
+
+ nsresult rv;
+ nsCOMPtr<nsIDeviceContextSpec> deviceContextSpec =
+ do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = deviceContextSpec->Init(mPrintSettings, false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mPrintDeviceContext = new nsDeviceContext();
+ rv = mPrintDeviceContext->InitForPrinting(deviceContextSpec);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsAutoString fileName;
+ mPrintSettings->GetToFileName(fileName);
+
+ rv = mPrintDeviceContext->BeginDocument(aDocumentTitle, fileName, aStartPage,
+ aEndPage);
+ if (NS_FAILED(rv)) {
+ NS_WARNING_ASSERTION(rv == NS_ERROR_ABORT,
+ "Failed to initialize print device");
+ return rv;
+ }
+
+ mIsDoingPrinting = true;
+
+ return NS_OK;
+}
+
+nsresult RemotePrintJobParent::PrepareNextPageFD(FileDescriptor* aFd) {
+ AUTO_PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
+ "RemotePrintJobParent::PrepareNextPageFD"_ns);
+
+ PRFileDesc* prFd = nullptr;
+ nsresult rv = NS_OpenAnonymousTemporaryFile(&prFd);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aFd = FileDescriptor(
+ FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prFd)));
+ mCurrentPageStream.OpenFD(prFd);
+ return NS_OK;
+}
+
+mozilla::ipc::IPCResult RemotePrintJobParent::RecvProcessPage(
+ const int32_t& aWidthInPoints, const int32_t& aHeightInPoints,
+ nsTArray<uint64_t>&& aDeps) {
+ PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
+ "RemotePrintJobParent::RecvProcessPage"_ns);
+ if (!mCurrentPageStream.IsOpen()) {
+ Unused << SendAbortPrint(NS_ERROR_FAILURE);
+ return IPC_OK();
+ }
+ mCurrentPageStream.Seek(0, PR_SEEK_SET);
+
+ gfx::IntSize pageSizeInPoints(aWidthInPoints, aHeightInPoints);
+
+ if (aDeps.IsEmpty()) {
+ FinishProcessingPage(pageSizeInPoints);
+ return IPC_OK();
+ }
+
+ nsTHashSet<uint64_t> deps;
+ for (auto i : aDeps) {
+ deps.Insert(i);
+ }
+
+ gfx::CrossProcessPaint::Start(std::move(deps))
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self = RefPtr{this}, pageSizeInPoints](
+ gfx::CrossProcessPaint::ResolvedFragmentMap&& aFragments) {
+ self->FinishProcessingPage(pageSizeInPoints, &aFragments);
+ },
+ [self = RefPtr{this}, pageSizeInPoints](const nsresult& aRv) {
+ self->FinishProcessingPage(pageSizeInPoints);
+ });
+
+ return IPC_OK();
+}
+
+void RemotePrintJobParent::FinishProcessingPage(
+ const gfx::IntSize& aSizeInPoints,
+ gfx::CrossProcessPaint::ResolvedFragmentMap* aFragments) {
+ nsresult rv = PrintPage(aSizeInPoints, mCurrentPageStream, aFragments);
+
+ mCurrentPageStream.Close();
+
+ PageDone(rv);
+}
+
+nsresult RemotePrintJobParent::PrintPage(
+ const gfx::IntSize& aSizeInPoints, PRFileDescStream& aRecording,
+ gfx::CrossProcessPaint::ResolvedFragmentMap* aFragments) {
+ MOZ_ASSERT(mPrintDeviceContext);
+ AUTO_PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
+ "RemotePrintJobParent::PrintPage"_ns);
+
+ nsresult rv = mPrintDeviceContext->BeginPage(aSizeInPoints);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ if (aFragments) {
+ mPrintTranslator->SetDependentSurfaces(aFragments);
+ }
+ if (!mPrintTranslator->TranslateRecording(aRecording)) {
+ mPrintTranslator->SetDependentSurfaces(nullptr);
+ return NS_ERROR_FAILURE;
+ }
+ mPrintTranslator->SetDependentSurfaces(nullptr);
+
+ rv = mPrintDeviceContext->EndPage();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void RemotePrintJobParent::PageDone(nsresult aResult) {
+ MOZ_ASSERT(mIsDoingPrinting);
+
+ if (NS_FAILED(aResult)) {
+ Unused << SendAbortPrint(aResult);
+ } else {
+ FileDescriptor fd;
+ aResult = PrepareNextPageFD(&fd);
+ if (NS_FAILED(aResult)) {
+ Unused << SendAbortPrint(aResult);
+ }
+
+ Unused << SendPageProcessed(fd);
+ }
+}
+
+static void NotifyStatusChange(
+ const nsCOMArray<nsIWebProgressListener>& aListeners, nsresult aStatus) {
+ uint32_t numberOfListeners = aListeners.Length();
+ for (uint32_t i = 0; i < numberOfListeners; ++i) {
+ nsIWebProgressListener* listener = aListeners[static_cast<int32_t>(i)];
+ listener->OnStatusChange(nullptr, nullptr, aStatus, nullptr);
+ }
+}
+
+static void NotifyStateChange(
+ const nsCOMArray<nsIWebProgressListener>& aListeners, long aStateFlags,
+ nsresult aStatus) {
+ uint32_t numberOfListeners = aListeners.Length();
+ for (uint32_t i = 0; i < numberOfListeners; ++i) {
+ nsIWebProgressListener* listener = aListeners[static_cast<int32_t>(i)];
+ listener->OnStateChange(nullptr, nullptr, aStateFlags, aStatus);
+ }
+}
+
+static void Cleanup(const nsCOMArray<nsIWebProgressListener>& aListeners,
+ RefPtr<nsDeviceContext>& aAbortContext,
+ const bool aPrintingInterrupted, const nsresult aResult) {
+ auto result = aResult;
+ if (MOZ_UNLIKELY(aPrintingInterrupted && NS_SUCCEEDED(result))) {
+ result = NS_ERROR_UNEXPECTED;
+ }
+ if (NS_FAILED(result)) {
+ NotifyStatusChange(aListeners, result);
+ }
+ if (aPrintingInterrupted && aAbortContext) {
+ // Abort any started print.
+ Unused << aAbortContext->AbortDocument();
+ }
+ // However the print went, let the listeners know that we're done.
+ NotifyStateChange(aListeners,
+ nsIWebProgressListener::STATE_STOP |
+ nsIWebProgressListener::STATE_IS_DOCUMENT,
+ result);
+}
+
+mozilla::ipc::IPCResult RemotePrintJobParent::RecvFinalizePrint() {
+ PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
+ "RemotePrintJobParent::RecvFinalizePrint"_ns);
+
+ // EndDocument is sometimes called in the child even when BeginDocument has
+ // not been called. See bug 1223332.
+ if (mPrintDeviceContext) {
+ mPrintDeviceContext->EndDocument()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [listeners = std::move(mPrintProgressListeners)](
+ const mozilla::gfx::PrintEndDocumentPromise::ResolveOrRejectValue&
+ aResult) {
+ // Printing isn't interrupted, so we don't need the device context
+ // here.
+ RefPtr<nsDeviceContext> empty;
+ if (aResult.IsResolve()) {
+ Cleanup(listeners, empty, /* aPrintingInterrupted = */ false,
+ NS_OK);
+ } else {
+ Cleanup(listeners, empty, /* aPrintingInterrupted = */ false,
+ aResult.RejectValue());
+ }
+ });
+ mStatus = NS_OK;
+ }
+
+ mIsDoingPrinting = false;
+
+ Unused << Send__delete__(this);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RemotePrintJobParent::RecvAbortPrint(
+ const nsresult& aRv) {
+ PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
+ "RemotePrintJobParent::RecvAbortPrint"_ns);
+
+ // Leave the cleanup to `ActorDestroy()`.
+ Unused << Send__delete__(this);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RemotePrintJobParent::RecvProgressChange(
+ const long& aCurSelfProgress, const long& aMaxSelfProgress,
+ const long& aCurTotalProgress, const long& aMaxTotalProgress) {
+ PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
+ "RemotePrintJobParent::RecvProgressChange"_ns);
+ // Our progress follows that of `RemotePrintJobChild` closely enough - forward
+ // it instead of keeping more state variables here.
+ for (auto* listener : mPrintProgressListeners) {
+ listener->OnProgressChange(nullptr, nullptr, aCurSelfProgress,
+ aMaxSelfProgress, aCurTotalProgress,
+ aMaxTotalProgress);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RemotePrintJobParent::RecvStatusChange(
+ const nsresult& aStatus) {
+ PROFILER_MARKER_TEXT("RemotePrintJobParent", LAYOUT_Printing, {},
+ "RemotePrintJobParent::RecvProgressChange"_ns);
+ if (NS_FAILED(aStatus)) {
+ // Remember the failure status for cleanup to forward to listeners.
+ mStatus = aStatus;
+ }
+
+ return IPC_OK();
+}
+
+void RemotePrintJobParent::RegisterListener(nsIWebProgressListener* aListener) {
+ MOZ_ASSERT(aListener);
+
+ // Our listener is a Promise created by CanonicalBrowsingContext::Print
+ mPrintProgressListeners.AppendElement(aListener);
+}
+
+already_AddRefed<nsIPrintSettings> RemotePrintJobParent::GetPrintSettings() {
+ nsCOMPtr<nsIPrintSettings> printSettings = mPrintSettings;
+ return printSettings.forget();
+}
+
+RemotePrintJobParent::~RemotePrintJobParent() {
+ MOZ_COUNT_DTOR(RemotePrintJobParent);
+}
+
+void RemotePrintJobParent::ActorDestroy(ActorDestroyReason aWhy) {
+ if (MOZ_UNLIKELY(mIsDoingPrinting && NS_SUCCEEDED(mStatus))) {
+ mStatus = NS_ERROR_UNEXPECTED;
+ }
+ Cleanup(mPrintProgressListeners, mPrintDeviceContext, mIsDoingPrinting,
+ mStatus);
+ // At any rate, this actor is done and cleaned up.
+ mIsDoingPrinting = false;
+}
+
+} // namespace mozilla::layout
diff --git a/layout/printing/ipc/RemotePrintJobParent.h b/layout/printing/ipc/RemotePrintJobParent.h
new file mode 100644
index 0000000000..2f80553183
--- /dev/null
+++ b/layout/printing/ipc/RemotePrintJobParent.h
@@ -0,0 +1,102 @@
+/* -*- 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/. */
+
+#ifndef mozilla_layout_RemotePrintJobParent_h
+#define mozilla_layout_RemotePrintJobParent_h
+
+#include "mozilla/layout/PRemotePrintJobParent.h"
+#include "mozilla/layout/printing/DrawEventRecorder.h"
+
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/gfx/RecordedEvent.h"
+#include "mozilla/gfx/CrossProcessPaint.h"
+
+class nsDeviceContext;
+class nsIPrintSettings;
+class nsIWebProgressListener;
+
+namespace mozilla {
+namespace layout {
+
+class PrintTranslator;
+
+class RemotePrintJobParent final : public PRemotePrintJobParent {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(RemotePrintJobParent);
+
+ explicit RemotePrintJobParent(nsIPrintSettings* aPrintSettings);
+
+ void ActorDestroy(ActorDestroyReason aWhy) final;
+
+ mozilla::ipc::IPCResult RecvInitializePrint(const nsAString& aDocumentTitle,
+ const int32_t& aStartPage,
+ const int32_t& aEndPage) final;
+
+ mozilla::ipc::IPCResult RecvProcessPage(const int32_t& aWidthInPoints,
+ const int32_t& aHeightInPoints,
+ nsTArray<uint64_t>&& aDeps) final;
+
+ mozilla::ipc::IPCResult RecvFinalizePrint() final;
+
+ mozilla::ipc::IPCResult RecvAbortPrint(const nsresult& aRv) final;
+
+ mozilla::ipc::IPCResult RecvProgressChange(
+ const long& aCurSelfProgress, const long& aMaxSelfProgress,
+ const long& aCurTotalProgress, const long& aMaxTotalProgress) final;
+
+ mozilla::ipc::IPCResult RecvStatusChange(const nsresult& aStatus) final;
+
+ /**
+ * Register a progress listener to receive print progress updates.
+ *
+ * @param aListener the progress listener to register. Must not be null.
+ */
+ void RegisterListener(nsIWebProgressListener* aListener);
+
+ /**
+ * @return the print settings for this remote print job.
+ */
+ already_AddRefed<nsIPrintSettings> GetPrintSettings();
+
+ private:
+ ~RemotePrintJobParent() final;
+
+ nsresult InitializePrintDevice(const nsAString& aDocumentTitle,
+ const int32_t& aStartPage,
+ const int32_t& aEndPage);
+
+ nsresult PrepareNextPageFD(FileDescriptor* aFd);
+
+ nsresult PrintPage(
+ const gfx::IntSize& aSizeInPoints, PRFileDescStream& aRecording,
+ gfx::CrossProcessPaint::ResolvedFragmentMap* aFragments = nullptr);
+ void FinishProcessingPage(
+ const gfx::IntSize& aSizeInPoints,
+ gfx::CrossProcessPaint::ResolvedFragmentMap* aFragments = nullptr);
+
+ /**
+ * Called to notify our corresponding RemotePrintJobChild once we've
+ * finished printing a page.
+ */
+ void PageDone(nsresult aResult);
+
+ nsCOMPtr<nsIPrintSettings> mPrintSettings;
+ RefPtr<nsDeviceContext> mPrintDeviceContext;
+ UniquePtr<PrintTranslator> mPrintTranslator;
+ nsCOMArray<nsIWebProgressListener> mPrintProgressListeners;
+ PRFileDescStream mCurrentPageStream;
+ bool mIsDoingPrinting;
+ nsresult mStatus;
+};
+
+} // namespace layout
+} // namespace mozilla
+
+#endif // mozilla_layout_RemotePrintJobParent_h