Bug 1821144 - remove old windows worker definitions. r=aryx
[gecko.git] / layout / printing / ipc / RemotePrintJobParent.cpp
blob8b3d5d5bd94932c0d80ad097767376112591f367
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"
9 #include <fstream>
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);
38 if (NS_FAILED(rv)) {
39 Unused << SendPrintInitializationResult(rv, FileDescriptor());
40 mStatus = rv;
41 Unused << Send__delete__(this);
42 return IPC_OK();
45 mPrintTranslator.reset(new PrintTranslator(mPrintDeviceContext));
46 FileDescriptor fd;
47 rv = PrepareNextPageFD(&fd);
48 if (NS_FAILED(rv)) {
49 Unused << SendPrintInitializationResult(rv, FileDescriptor());
50 mStatus = rv;
51 Unused << Send__delete__(this);
52 return IPC_OK();
55 Unused << SendPrintInitializationResult(NS_OK, fd);
56 return IPC_OK();
59 nsresult RemotePrintJobParent::InitializePrintDevice(
60 const nsAString& aDocumentTitle, const int32_t& aStartPage,
61 const int32_t& aEndPage) {
62 nsresult rv;
63 nsCOMPtr<nsIDeviceContextSpec> deviceContextSpec =
64 do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv);
65 if (NS_WARN_IF(NS_FAILED(rv))) {
66 return rv;
69 rv = deviceContextSpec->Init(mPrintSettings, false);
70 if (NS_WARN_IF(NS_FAILED(rv))) {
71 return rv;
74 mPrintDeviceContext = new nsDeviceContext();
75 rv = mPrintDeviceContext->InitForPrinting(deviceContextSpec);
76 if (NS_WARN_IF(NS_FAILED(rv))) {
77 return rv;
80 nsAutoString fileName;
81 mPrintSettings->GetToFileName(fileName);
83 rv = mPrintDeviceContext->BeginDocument(aDocumentTitle, fileName, aStartPage,
84 aEndPage);
85 if (NS_FAILED(rv)) {
86 NS_WARNING_ASSERTION(rv == NS_ERROR_ABORT,
87 "Failed to initialize print device");
88 return rv;
91 mIsDoingPrinting = true;
93 return NS_OK;
96 nsresult RemotePrintJobParent::PrepareNextPageFD(FileDescriptor* aFd) {
97 PRFileDesc* prFd = nullptr;
98 nsresult rv = NS_OpenAnonymousTemporaryFile(&prFd);
99 if (NS_FAILED(rv)) {
100 return rv;
102 *aFd = FileDescriptor(
103 FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prFd)));
104 mCurrentPageStream.OpenFD(prFd);
105 return NS_OK;
108 mozilla::ipc::IPCResult RemotePrintJobParent::RecvProcessPage(
109 nsTArray<uint64_t>&& aDeps) {
110 if (!mCurrentPageStream.IsOpen()) {
111 Unused << SendAbortPrint(NS_ERROR_FAILURE);
112 return IPC_OK();
114 mCurrentPageStream.Seek(0, PR_SEEK_SET);
116 if (aDeps.IsEmpty()) {
117 FinishProcessingPage();
118 return IPC_OK();
121 nsTHashSet<uint64_t> deps;
122 for (auto i : aDeps) {
123 deps.Insert(i);
126 gfx::CrossProcessPaint::Start(std::move(deps))
127 ->Then(
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();
137 return IPC_OK();
140 void RemotePrintJobParent::FinishProcessingPage(
141 gfx::CrossProcessPaint::ResolvedFragmentMap* aFragments) {
142 nsresult rv = PrintPage(mCurrentPageStream, aFragments);
144 mCurrentPageStream.Close();
146 PageDone(rv);
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))) {
156 return rv;
158 if (aFragments) {
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))) {
169 return rv;
172 return NS_OK;
175 void RemotePrintJobParent::PageDone(nsresult aResult) {
176 MOZ_ASSERT(mIsDoingPrinting);
178 if (NS_FAILED(aResult)) {
179 Unused << SendAbortPrint(aResult);
180 } else {
181 FileDescriptor fd;
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,
202 nsresult aStatus) {
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,
228 result);
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&
239 aResult) {
240 // Printing isn't interrupted, so we don't need the device context
241 // here.
242 RefPtr<nsDeviceContext> empty;
243 if (aResult.IsResolve()) {
244 Cleanup(listeners, empty, /* aPrintingInterrupted = */ false,
245 NS_OK);
246 } else {
247 Cleanup(listeners, empty, /* aPrintingInterrupted = */ false,
248 aResult.RejectValue());
251 mStatus = NS_OK;
254 mIsDoingPrinting = false;
256 Unused << Send__delete__(this);
257 return IPC_OK();
260 mozilla::ipc::IPCResult RemotePrintJobParent::RecvAbortPrint(
261 const nsresult& aRv) {
262 // Leave the cleanup to `ActorDestroy()`.
263 Unused << Send__delete__(this);
264 return IPC_OK();
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,
275 aMaxTotalProgress);
277 return IPC_OK();
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.
284 mStatus = aStatus;
287 return IPC_OK();
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,
311 mStatus);
312 // At any rate, this actor is done and cleaned up.
313 mIsDoingPrinting = false;
316 } // namespace mozilla::layout