summaryrefslogtreecommitdiffstats
path: root/layout/printing/ipc/RemotePrintJobParent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/printing/ipc/RemotePrintJobParent.cpp')
-rw-r--r--layout/printing/ipc/RemotePrintJobParent.cpp295
1 files changed, 295 insertions, 0 deletions
diff --git a/layout/printing/ipc/RemotePrintJobParent.cpp b/layout/printing/ipc/RemotePrintJobParent.cpp
new file mode 100644
index 0000000000..4e9ca0ed0d
--- /dev/null
+++ b/layout/printing/ipc/RemotePrintJobParent.cpp
@@ -0,0 +1,295 @@
+/* -*- 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/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 {
+namespace layout {
+
+RemotePrintJobParent::RemotePrintJobParent(nsIPrintSettings* aPrintSettings)
+ : mPrintSettings(aPrintSettings), mIsDoingPrinting(false) {
+ MOZ_COUNT_CTOR(RemotePrintJobParent);
+}
+
+mozilla::ipc::IPCResult RemotePrintJobParent::RecvInitializePrint(
+ const nsString& aDocumentTitle, const nsString& aPrintToFile,
+ const int32_t& aStartPage, const int32_t& aEndPage) {
+ nsresult rv =
+ InitializePrintDevice(aDocumentTitle, aPrintToFile, aStartPage, aEndPage);
+ if (NS_FAILED(rv)) {
+ Unused << SendPrintInitializationResult(rv, FileDescriptor());
+ // Let any listeners know about the failure before we delete.
+ Unused << RecvStatusChange(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());
+ // Let any listeners know about the failure before we delete.
+ Unused << RecvStatusChange(rv);
+ Unused << Send__delete__(this);
+ return IPC_OK();
+ }
+
+ Unused << SendPrintInitializationResult(NS_OK, fd);
+ return IPC_OK();
+}
+
+nsresult RemotePrintJobParent::InitializePrintDevice(
+ const nsString& aDocumentTitle, const nsString& aPrintToFile,
+ const int32_t& aStartPage, const int32_t& aEndPage) {
+ 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(nullptr, 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;
+ }
+
+ rv = mPrintDeviceContext->BeginDocument(aDocumentTitle, aPrintToFile,
+ aStartPage, aEndPage);
+ if (NS_FAILED(rv)) {
+ NS_WARNING_ASSERTION(rv == NS_ERROR_ABORT,
+ "Failed to initialize print device");
+ return rv;
+ }
+
+ if (!mPrintDeviceContext->IsSyncPagePrinting()) {
+ mPrintDeviceContext->RegisterPageDoneCallback(
+ [self = RefPtr{this}](nsresult aResult) { self->PageDone(aResult); });
+ }
+
+ mIsDoingPrinting = true;
+
+ return NS_OK;
+}
+
+nsresult RemotePrintJobParent::PrepareNextPageFD(FileDescriptor* aFd) {
+ 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(
+ nsTArray<uint64_t>&& aDeps) {
+ if (!mCurrentPageStream.IsOpen()) {
+ Unused << SendAbortPrint(NS_ERROR_FAILURE);
+ return IPC_OK();
+ }
+ mCurrentPageStream.Seek(0, PR_SEEK_SET);
+
+ if (aDeps.IsEmpty()) {
+ FinishProcessingPage();
+ return IPC_OK();
+ }
+
+ nsTHashtable<nsUint64HashKey> deps;
+ for (auto i : aDeps) {
+ deps.PutEntry(i);
+ }
+
+ gfx::CrossProcessPaint::Start(std::move(deps))
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self = RefPtr{this}](
+ gfx::CrossProcessPaint::ResolvedFragmentMap&& aFragments) {
+ self->FinishProcessingPage(&aFragments);
+ },
+ [self = RefPtr{this}](const nsresult& aRv) {
+ self->FinishProcessingPage();
+ });
+
+ return IPC_OK();
+}
+
+void RemotePrintJobParent::FinishProcessingPage(
+ gfx::CrossProcessPaint::ResolvedFragmentMap* aFragments) {
+ nsresult rv = PrintPage(mCurrentPageStream, aFragments);
+
+ mCurrentPageStream.Close();
+
+ if (mPrintDeviceContext->IsSyncPagePrinting()) {
+ PageDone(rv);
+ }
+}
+
+nsresult RemotePrintJobParent::PrintPage(
+ PRFileDescStream& aRecording,
+ gfx::CrossProcessPaint::ResolvedFragmentMap* aFragments) {
+ MOZ_ASSERT(mPrintDeviceContext);
+
+ nsresult rv = mPrintDeviceContext->BeginPage();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ if (aFragments) {
+ mPrintTranslator->SetDependentSurfaces(std::move(*aFragments));
+ }
+ if (!mPrintTranslator->TranslateRecording(aRecording)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ 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);
+ }
+}
+
+mozilla::ipc::IPCResult RemotePrintJobParent::RecvFinalizePrint() {
+ // EndDocument is sometimes called in the child even when BeginDocument has
+ // not been called. See bug 1223332.
+ if (mPrintDeviceContext) {
+ DebugOnly<nsresult> rv = mPrintDeviceContext->EndDocument();
+
+ // Too late to abort the child just log.
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EndDocument failed");
+
+ // Since RecvFinalizePrint is called after all page printed, there should
+ // be no more page-done callbacks after that, in theory. Unregistering
+ // page-done callback is not must have, but we still do this for safety.
+ mPrintDeviceContext->UnregisterPageDoneCallback();
+ }
+
+ mIsDoingPrinting = false;
+
+ Unused << Send__delete__(this);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RemotePrintJobParent::RecvAbortPrint(
+ const nsresult& aRv) {
+ if (mPrintDeviceContext) {
+ Unused << mPrintDeviceContext->AbortDocument();
+ mPrintDeviceContext->UnregisterPageDoneCallback();
+ }
+
+ mIsDoingPrinting = false;
+
+ Unused << Send__delete__(this);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RemotePrintJobParent::RecvStateChange(
+ const long& aStateFlags, const nsresult& aStatus) {
+ uint32_t numberOfListeners = mPrintProgressListeners.Length();
+ for (uint32_t i = 0; i < numberOfListeners; ++i) {
+ nsIWebProgressListener* listener = mPrintProgressListeners.SafeElementAt(i);
+ listener->OnStateChange(nullptr, nullptr, aStateFlags, aStatus);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RemotePrintJobParent::RecvProgressChange(
+ const long& aCurSelfProgress, const long& aMaxSelfProgress,
+ const long& aCurTotalProgress, const long& aMaxTotalProgress) {
+ uint32_t numberOfListeners = mPrintProgressListeners.Length();
+ for (uint32_t i = 0; i < numberOfListeners; ++i) {
+ nsIWebProgressListener* listener = mPrintProgressListeners.SafeElementAt(i);
+ listener->OnProgressChange(nullptr, nullptr, aCurSelfProgress,
+ aMaxSelfProgress, aCurTotalProgress,
+ aMaxTotalProgress);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RemotePrintJobParent::RecvStatusChange(
+ const nsresult& aStatus) {
+ uint32_t numberOfListeners = mPrintProgressListeners.Length();
+ for (uint32_t i = 0; i < numberOfListeners; ++i) {
+ nsIWebProgressListener* listener = mPrintProgressListeners.SafeElementAt(i);
+ listener->OnStatusChange(nullptr, nullptr, aStatus, nullptr);
+ }
+
+ return IPC_OK();
+}
+
+void RemotePrintJobParent::RegisterListener(nsIWebProgressListener* aListener) {
+ MOZ_ASSERT(aListener);
+
+ 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 (mPrintDeviceContext) {
+ mPrintDeviceContext->UnregisterPageDoneCallback();
+ }
+
+ mIsDoingPrinting = false;
+
+ // If progress dialog is opened, notify closing it.
+ for (auto listener : mPrintProgressListeners) {
+ listener->OnStateChange(nullptr, nullptr,
+ nsIWebProgressListener::STATE_STOP, NS_OK);
+ }
+}
+
+} // namespace layout
+} // namespace mozilla