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/. */
7 #include "RemotePrintJobParent.h"
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"
25 namespace mozilla::layout
{
27 RemotePrintJobParent::RemotePrintJobParent(nsIPrintSettings
* aPrintSettings
)
28 : mPrintSettings(aPrintSettings
),
29 mIsDoingPrinting(false),
30 mStatus(NS_ERROR_UNEXPECTED
) {
31 MOZ_COUNT_CTOR(RemotePrintJobParent
);
34 mozilla::ipc::IPCResult
RemotePrintJobParent::RecvInitializePrint(
35 const nsAString
& aDocumentTitle
, const int32_t& aStartPage
,
36 const int32_t& aEndPage
) {
37 nsresult rv
= InitializePrintDevice(aDocumentTitle
, aStartPage
, aEndPage
);
39 Unused
<< SendPrintInitializationResult(rv
, FileDescriptor());
41 Unused
<< Send__delete__(this);
45 mPrintTranslator
.reset(new PrintTranslator(mPrintDeviceContext
));
47 rv
= PrepareNextPageFD(&fd
);
49 Unused
<< SendPrintInitializationResult(rv
, FileDescriptor());
51 Unused
<< Send__delete__(this);
55 Unused
<< SendPrintInitializationResult(NS_OK
, fd
);
59 nsresult
RemotePrintJobParent::InitializePrintDevice(
60 const nsAString
& aDocumentTitle
, const int32_t& aStartPage
,
61 const int32_t& aEndPage
) {
63 nsCOMPtr
<nsIDeviceContextSpec
> deviceContextSpec
=
64 do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv
);
65 if (NS_WARN_IF(NS_FAILED(rv
))) {
69 rv
= deviceContextSpec
->Init(mPrintSettings
, false);
70 if (NS_WARN_IF(NS_FAILED(rv
))) {
74 mPrintDeviceContext
= new nsDeviceContext();
75 rv
= mPrintDeviceContext
->InitForPrinting(deviceContextSpec
);
76 if (NS_WARN_IF(NS_FAILED(rv
))) {
80 nsAutoString fileName
;
81 mPrintSettings
->GetToFileName(fileName
);
83 rv
= mPrintDeviceContext
->BeginDocument(aDocumentTitle
, fileName
, aStartPage
,
86 NS_WARNING_ASSERTION(rv
== NS_ERROR_ABORT
,
87 "Failed to initialize print device");
91 mIsDoingPrinting
= true;
96 nsresult
RemotePrintJobParent::PrepareNextPageFD(FileDescriptor
* aFd
) {
97 PRFileDesc
* prFd
= nullptr;
98 nsresult rv
= NS_OpenAnonymousTemporaryFile(&prFd
);
102 *aFd
= FileDescriptor(
103 FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prFd
)));
104 mCurrentPageStream
.OpenFD(prFd
);
108 mozilla::ipc::IPCResult
RemotePrintJobParent::RecvProcessPage(
109 nsTArray
<uint64_t>&& aDeps
) {
110 if (!mCurrentPageStream
.IsOpen()) {
111 Unused
<< SendAbortPrint(NS_ERROR_FAILURE
);
114 mCurrentPageStream
.Seek(0, PR_SEEK_SET
);
116 if (aDeps
.IsEmpty()) {
117 FinishProcessingPage();
121 nsTHashSet
<uint64_t> deps
;
122 for (auto i
: aDeps
) {
126 gfx::CrossProcessPaint::Start(std::move(deps
))
128 GetCurrentSerialEventTarget(), __func__
,
129 [self
= RefPtr
{this}](
130 gfx::CrossProcessPaint::ResolvedFragmentMap
&& aFragments
) {
131 self
->FinishProcessingPage(&aFragments
);
133 [self
= RefPtr
{this}](const nsresult
& aRv
) {
134 self
->FinishProcessingPage();
140 void RemotePrintJobParent::FinishProcessingPage(
141 gfx::CrossProcessPaint::ResolvedFragmentMap
* aFragments
) {
142 nsresult rv
= PrintPage(mCurrentPageStream
, aFragments
);
144 mCurrentPageStream
.Close();
149 nsresult
RemotePrintJobParent::PrintPage(
150 PRFileDescStream
& aRecording
,
151 gfx::CrossProcessPaint::ResolvedFragmentMap
* aFragments
) {
152 MOZ_ASSERT(mPrintDeviceContext
);
154 nsresult rv
= mPrintDeviceContext
->BeginPage();
155 if (NS_WARN_IF(NS_FAILED(rv
))) {
159 mPrintTranslator
->SetDependentSurfaces(aFragments
);
161 if (!mPrintTranslator
->TranslateRecording(aRecording
)) {
162 mPrintTranslator
->SetDependentSurfaces(nullptr);
163 return NS_ERROR_FAILURE
;
165 mPrintTranslator
->SetDependentSurfaces(nullptr);
167 rv
= mPrintDeviceContext
->EndPage();
168 if (NS_WARN_IF(NS_FAILED(rv
))) {
175 void RemotePrintJobParent::PageDone(nsresult aResult
) {
176 MOZ_ASSERT(mIsDoingPrinting
);
178 if (NS_FAILED(aResult
)) {
179 Unused
<< SendAbortPrint(aResult
);
182 aResult
= PrepareNextPageFD(&fd
);
183 if (NS_FAILED(aResult
)) {
184 Unused
<< SendAbortPrint(aResult
);
187 Unused
<< SendPageProcessed(fd
);
191 static void NotifyStatusChange(
192 const nsCOMArray
<nsIWebProgressListener
>& aListeners
, nsresult aStatus
) {
193 uint32_t numberOfListeners
= aListeners
.Length();
194 for (uint32_t i
= 0; i
< numberOfListeners
; ++i
) {
195 nsIWebProgressListener
* listener
= aListeners
[static_cast<int32_t>(i
)];
196 listener
->OnStatusChange(nullptr, nullptr, aStatus
, nullptr);
200 static void NotifyStateChange(
201 const nsCOMArray
<nsIWebProgressListener
>& aListeners
, long aStateFlags
,
203 uint32_t numberOfListeners
= aListeners
.Length();
204 for (uint32_t i
= 0; i
< numberOfListeners
; ++i
) {
205 nsIWebProgressListener
* listener
= aListeners
[static_cast<int32_t>(i
)];
206 listener
->OnStateChange(nullptr, nullptr, aStateFlags
, aStatus
);
210 static void Cleanup(const nsCOMArray
<nsIWebProgressListener
>& aListeners
,
211 RefPtr
<nsDeviceContext
>& aAbortContext
,
212 const bool aPrintingInterrupted
, const nsresult aResult
) {
213 auto result
= aResult
;
214 if (MOZ_UNLIKELY(aPrintingInterrupted
&& NS_SUCCEEDED(result
))) {
215 result
= NS_ERROR_UNEXPECTED
;
217 if (NS_FAILED(result
)) {
218 NotifyStatusChange(aListeners
, result
);
220 if (aPrintingInterrupted
&& aAbortContext
) {
221 // Abort any started print.
222 Unused
<< aAbortContext
->AbortDocument();
224 // However the print went, let the listeners know that we're done.
225 NotifyStateChange(aListeners
,
226 nsIWebProgressListener::STATE_STOP
|
227 nsIWebProgressListener::STATE_IS_DOCUMENT
,
231 mozilla::ipc::IPCResult
RemotePrintJobParent::RecvFinalizePrint() {
232 // EndDocument is sometimes called in the child even when BeginDocument has
233 // not been called. See bug 1223332.
234 if (mPrintDeviceContext
) {
235 mPrintDeviceContext
->EndDocument()->Then(
236 GetMainThreadSerialEventTarget(), __func__
,
237 [listeners
= std::move(mPrintProgressListeners
)](
238 const mozilla::gfx::PrintEndDocumentPromise::ResolveOrRejectValue
&
240 // Printing isn't interrupted, so we don't need the device context
242 RefPtr
<nsDeviceContext
> empty
;
243 if (aResult
.IsResolve()) {
244 Cleanup(listeners
, empty
, /* aPrintingInterrupted = */ false,
247 Cleanup(listeners
, empty
, /* aPrintingInterrupted = */ false,
248 aResult
.RejectValue());
254 mIsDoingPrinting
= false;
256 Unused
<< Send__delete__(this);
260 mozilla::ipc::IPCResult
RemotePrintJobParent::RecvAbortPrint(
261 const nsresult
& aRv
) {
262 // Leave the cleanup to `ActorDestroy()`.
263 Unused
<< Send__delete__(this);
267 mozilla::ipc::IPCResult
RemotePrintJobParent::RecvProgressChange(
268 const long& aCurSelfProgress
, const long& aMaxSelfProgress
,
269 const long& aCurTotalProgress
, const long& aMaxTotalProgress
) {
270 // Our progress follows that of `RemotePrintJobChild` closely enough - forward
271 // it instead of keeping more state variables here.
272 for (auto* listener
: mPrintProgressListeners
) {
273 listener
->OnProgressChange(nullptr, nullptr, aCurSelfProgress
,
274 aMaxSelfProgress
, aCurTotalProgress
,
280 mozilla::ipc::IPCResult
RemotePrintJobParent::RecvStatusChange(
281 const nsresult
& aStatus
) {
282 if (NS_FAILED(aStatus
)) {
283 // Remember the failure status for cleanup to forward to listeners.
290 void RemotePrintJobParent::RegisterListener(nsIWebProgressListener
* aListener
) {
291 MOZ_ASSERT(aListener
);
293 // Our listener is a Promise created by CanonicalBrowsingContext::Print
294 mPrintProgressListeners
.AppendElement(aListener
);
297 already_AddRefed
<nsIPrintSettings
> RemotePrintJobParent::GetPrintSettings() {
298 nsCOMPtr
<nsIPrintSettings
> printSettings
= mPrintSettings
;
299 return printSettings
.forget();
302 RemotePrintJobParent::~RemotePrintJobParent() {
303 MOZ_COUNT_DTOR(RemotePrintJobParent
);
306 void RemotePrintJobParent::ActorDestroy(ActorDestroyReason aWhy
) {
307 if (MOZ_UNLIKELY(mIsDoingPrinting
&& NS_SUCCEEDED(mStatus
))) {
308 mStatus
= NS_ERROR_UNEXPECTED
;
310 Cleanup(mPrintProgressListeners
, mPrintDeviceContext
, mIsDoingPrinting
,
312 // At any rate, this actor is done and cleaned up.
313 mIsDoingPrinting
= false;
316 } // namespace mozilla::layout