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 "gfxContext.h"
12 #include "mozilla/Attributes.h"
13 #include "mozilla/Unused.h"
14 #include "nsAppDirectoryServiceDefs.h"
15 #include "nsComponentManagerUtils.h"
16 #include "nsDirectoryServiceUtils.h"
17 #include "nsDeviceContext.h"
18 #include "nsIDeviceContextSpec.h"
19 #include "nsIPrintSettings.h"
20 #include "nsIWebProgressListener.h"
21 #include "PrintTranslator.h"
22 #include "private/pprio.h"
23 #include "nsAnonymousTemporaryFile.h"
24
25 namespace mozilla {
26 namespace layout {
27
RemotePrintJobParent(nsIPrintSettings * aPrintSettings)28 RemotePrintJobParent::RemotePrintJobParent(nsIPrintSettings* aPrintSettings)
29 : mPrintSettings(aPrintSettings), mIsDoingPrinting(false) {
30 MOZ_COUNT_CTOR(RemotePrintJobParent);
31 }
32
RecvInitializePrint(const nsString & aDocumentTitle,const nsString & aPrintToFile,const int32_t & aStartPage,const int32_t & aEndPage)33 mozilla::ipc::IPCResult RemotePrintJobParent::RecvInitializePrint(
34 const nsString& aDocumentTitle, const nsString& aPrintToFile,
35 const int32_t& aStartPage, const int32_t& aEndPage) {
36 nsresult rv =
37 InitializePrintDevice(aDocumentTitle, aPrintToFile, aStartPage, aEndPage);
38 if (NS_FAILED(rv)) {
39 Unused << SendPrintInitializationResult(rv, FileDescriptor());
40 Unused << Send__delete__(this);
41 return IPC_OK();
42 }
43
44 mPrintTranslator.reset(new PrintTranslator(mPrintDeviceContext));
45 FileDescriptor fd;
46 rv = PrepareNextPageFD(&fd);
47 if (NS_FAILED(rv)) {
48 Unused << SendPrintInitializationResult(rv, FileDescriptor());
49 Unused << Send__delete__(this);
50 return IPC_OK();
51 }
52
53 Unused << SendPrintInitializationResult(NS_OK, fd);
54 return IPC_OK();
55 }
56
InitializePrintDevice(const nsString & aDocumentTitle,const nsString & aPrintToFile,const int32_t & aStartPage,const int32_t & aEndPage)57 nsresult RemotePrintJobParent::InitializePrintDevice(
58 const nsString& aDocumentTitle, const nsString& aPrintToFile,
59 const int32_t& aStartPage, const int32_t& aEndPage) {
60 nsresult rv;
61 nsCOMPtr<nsIDeviceContextSpec> deviceContextSpec =
62 do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv);
63 if (NS_WARN_IF(NS_FAILED(rv))) {
64 return rv;
65 }
66
67 rv = deviceContextSpec->Init(nullptr, mPrintSettings, false);
68 if (NS_WARN_IF(NS_FAILED(rv))) {
69 return rv;
70 }
71
72 mPrintDeviceContext = new nsDeviceContext();
73 rv = mPrintDeviceContext->InitForPrinting(deviceContextSpec);
74 if (NS_WARN_IF(NS_FAILED(rv))) {
75 return rv;
76 }
77
78 rv = mPrintDeviceContext->BeginDocument(aDocumentTitle, aPrintToFile,
79 aStartPage, aEndPage);
80 if (NS_FAILED(rv)) {
81 NS_WARNING_ASSERTION(rv == NS_ERROR_ABORT,
82 "Failed to initialize print device");
83 return rv;
84 }
85
86 if (!mPrintDeviceContext->IsSyncPagePrinting()) {
87 mPrintDeviceContext->RegisterPageDoneCallback(
88 [this](nsresult aResult) { PageDone(aResult); });
89 }
90
91 mIsDoingPrinting = true;
92
93 return NS_OK;
94 }
95
PrepareNextPageFD(FileDescriptor * aFd)96 nsresult RemotePrintJobParent::PrepareNextPageFD(FileDescriptor* aFd) {
97 PRFileDesc* prFd = nullptr;
98 nsresult rv = NS_OpenAnonymousTemporaryFile(&prFd);
99 if (NS_FAILED(rv)) {
100 return rv;
101 }
102 *aFd = FileDescriptor(
103 FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prFd)));
104 mCurrentPageStream.OpenFD(prFd);
105 return NS_OK;
106 }
107
RecvProcessPage()108 mozilla::ipc::IPCResult RemotePrintJobParent::RecvProcessPage() {
109 if (!mCurrentPageStream.IsOpen()) {
110 Unused << SendAbortPrint(NS_ERROR_FAILURE);
111 return IPC_OK();
112 }
113 mCurrentPageStream.Seek(0, PR_SEEK_SET);
114 nsresult rv = PrintPage(mCurrentPageStream);
115 mCurrentPageStream.Close();
116
117 if (mPrintDeviceContext->IsSyncPagePrinting()) {
118 PageDone(rv);
119 }
120
121 return IPC_OK();
122 }
123
PrintPage(PRFileDescStream & aRecording)124 nsresult RemotePrintJobParent::PrintPage(PRFileDescStream& aRecording) {
125 MOZ_ASSERT(mPrintDeviceContext);
126
127 nsresult rv = mPrintDeviceContext->BeginPage();
128 if (NS_WARN_IF(NS_FAILED(rv))) {
129 return rv;
130 }
131 if (!mPrintTranslator->TranslateRecording(aRecording)) {
132 return NS_ERROR_FAILURE;
133 }
134
135 rv = mPrintDeviceContext->EndPage();
136 if (NS_WARN_IF(NS_FAILED(rv))) {
137 return rv;
138 }
139
140 return NS_OK;
141 }
142
PageDone(nsresult aResult)143 void RemotePrintJobParent::PageDone(nsresult aResult) {
144 MOZ_ASSERT(mIsDoingPrinting);
145
146 if (NS_FAILED(aResult)) {
147 Unused << SendAbortPrint(aResult);
148 } else {
149 FileDescriptor fd;
150 aResult = PrepareNextPageFD(&fd);
151 if (NS_FAILED(aResult)) {
152 Unused << SendAbortPrint(aResult);
153 }
154
155 Unused << SendPageProcessed(fd);
156 }
157 }
158
RecvFinalizePrint()159 mozilla::ipc::IPCResult RemotePrintJobParent::RecvFinalizePrint() {
160 // EndDocument is sometimes called in the child even when BeginDocument has
161 // not been called. See bug 1223332.
162 if (mPrintDeviceContext) {
163 DebugOnly<nsresult> rv = mPrintDeviceContext->EndDocument();
164
165 // Too late to abort the child just log.
166 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EndDocument failed");
167
168 // Since RecvFinalizePrint is called after all page printed, there should
169 // be no more page-done callbacks after that, in theory. Unregistering
170 // page-done callback is not must have, but we still do this for safety.
171 mPrintDeviceContext->UnregisterPageDoneCallback();
172 }
173
174 mIsDoingPrinting = false;
175
176 Unused << Send__delete__(this);
177 return IPC_OK();
178 }
179
RecvAbortPrint(const nsresult & aRv)180 mozilla::ipc::IPCResult RemotePrintJobParent::RecvAbortPrint(
181 const nsresult& aRv) {
182 if (mPrintDeviceContext) {
183 Unused << mPrintDeviceContext->AbortDocument();
184 mPrintDeviceContext->UnregisterPageDoneCallback();
185 }
186
187 mIsDoingPrinting = false;
188
189 Unused << Send__delete__(this);
190 return IPC_OK();
191 }
192
RecvStateChange(const long & aStateFlags,const nsresult & aStatus)193 mozilla::ipc::IPCResult RemotePrintJobParent::RecvStateChange(
194 const long& aStateFlags, const nsresult& aStatus) {
195 uint32_t numberOfListeners = mPrintProgressListeners.Length();
196 for (uint32_t i = 0; i < numberOfListeners; ++i) {
197 nsIWebProgressListener* listener = mPrintProgressListeners.SafeElementAt(i);
198 listener->OnStateChange(nullptr, nullptr, aStateFlags, aStatus);
199 }
200
201 return IPC_OK();
202 }
203
RecvProgressChange(const long & aCurSelfProgress,const long & aMaxSelfProgress,const long & aCurTotalProgress,const long & aMaxTotalProgress)204 mozilla::ipc::IPCResult RemotePrintJobParent::RecvProgressChange(
205 const long& aCurSelfProgress, const long& aMaxSelfProgress,
206 const long& aCurTotalProgress, const long& aMaxTotalProgress) {
207 uint32_t numberOfListeners = mPrintProgressListeners.Length();
208 for (uint32_t i = 0; i < numberOfListeners; ++i) {
209 nsIWebProgressListener* listener = mPrintProgressListeners.SafeElementAt(i);
210 listener->OnProgressChange(nullptr, nullptr, aCurSelfProgress,
211 aMaxSelfProgress, aCurTotalProgress,
212 aMaxTotalProgress);
213 }
214
215 return IPC_OK();
216 }
217
RecvStatusChange(const nsresult & aStatus)218 mozilla::ipc::IPCResult RemotePrintJobParent::RecvStatusChange(
219 const nsresult& aStatus) {
220 uint32_t numberOfListeners = mPrintProgressListeners.Length();
221 for (uint32_t i = 0; i < numberOfListeners; ++i) {
222 nsIWebProgressListener* listener = mPrintProgressListeners.SafeElementAt(i);
223 listener->OnStatusChange(nullptr, nullptr, aStatus, nullptr);
224 }
225
226 return IPC_OK();
227 }
228
RegisterListener(nsIWebProgressListener * aListener)229 void RemotePrintJobParent::RegisterListener(nsIWebProgressListener* aListener) {
230 MOZ_ASSERT(aListener);
231
232 mPrintProgressListeners.AppendElement(aListener);
233 }
234
GetPrintSettings()235 already_AddRefed<nsIPrintSettings> RemotePrintJobParent::GetPrintSettings() {
236 nsCOMPtr<nsIPrintSettings> printSettings = mPrintSettings;
237 return printSettings.forget();
238 }
239
~RemotePrintJobParent()240 RemotePrintJobParent::~RemotePrintJobParent() {
241 MOZ_COUNT_DTOR(RemotePrintJobParent);
242 }
243
ActorDestroy(ActorDestroyReason aWhy)244 void RemotePrintJobParent::ActorDestroy(ActorDestroyReason aWhy) {
245 if (mPrintDeviceContext) {
246 mPrintDeviceContext->UnregisterPageDoneCallback();
247 }
248
249 mIsDoingPrinting = false;
250
251 // If progress dialog is opened, notify closing it.
252 for (auto listener : mPrintProgressListeners) {
253 listener->OnStateChange(nullptr, nullptr,
254 nsIWebProgressListener::STATE_STOP, NS_OK);
255 }
256 }
257
258 } // namespace layout
259 } // namespace mozilla
260