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 "nsDeviceContextSpecProxy.h"
8 
9 #include "gfxASurface.h"
10 #include "gfxPlatform.h"
11 #include "mozilla/gfx/DrawEventRecorder.h"
12 #include "mozilla/gfx/PrintTargetThebes.h"
13 #include "mozilla/layout/RemotePrintJobChild.h"
14 #include "mozilla/RefPtr.h"
15 #include "mozilla/Unused.h"
16 #include "nsComponentManagerUtils.h"
17 #include "nsAppDirectoryServiceDefs.h"
18 #include "nsDirectoryServiceUtils.h"
19 #include "nsIPrintSession.h"
20 #include "nsIPrintSettings.h"
21 #include "private/pprio.h"
22 
23 using mozilla::Unused;
24 
25 using namespace mozilla;
26 using namespace mozilla::gfx;
27 
28 NS_IMPL_ISUPPORTS(nsDeviceContextSpecProxy, nsIDeviceContextSpec)
29 
30 nsDeviceContextSpecProxy::nsDeviceContextSpecProxy() = default;
31 nsDeviceContextSpecProxy::~nsDeviceContextSpecProxy() = default;
32 
33 NS_IMETHODIMP
Init(nsIWidget * aWidget,nsIPrintSettings * aPrintSettings,bool aIsPrintPreview)34 nsDeviceContextSpecProxy::Init(nsIWidget* aWidget,
35                                nsIPrintSettings* aPrintSettings,
36                                bool aIsPrintPreview) {
37   nsresult rv;
38   mRealDeviceContextSpec =
39       do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv);
40   if (NS_WARN_IF(NS_FAILED(rv))) {
41     return rv;
42   }
43 
44   mRealDeviceContextSpec->Init(nullptr, aPrintSettings, aIsPrintPreview);
45   if (NS_WARN_IF(NS_FAILED(rv))) {
46     mRealDeviceContextSpec = nullptr;
47     return rv;
48   }
49 
50   mPrintSettings = aPrintSettings;
51 
52   if (aIsPrintPreview) {
53     return NS_OK;
54   }
55 
56   // nsIPrintSettings only has a weak reference to nsIPrintSession, so we hold
57   // it to make sure it's available for the lifetime of the print.
58   rv = mPrintSettings->GetPrintSession(getter_AddRefs(mPrintSession));
59   if (NS_FAILED(rv) || !mPrintSession) {
60     NS_WARNING("We can't print via the parent without an nsIPrintSession.");
61     return NS_ERROR_FAILURE;
62   }
63 
64   mRemotePrintJob = mPrintSession->GetRemotePrintJob();
65   if (!mRemotePrintJob) {
66     NS_WARNING("We can't print via the parent without a RemotePrintJobChild.");
67     return NS_ERROR_FAILURE;
68   }
69 
70   return NS_OK;
71 }
72 
MakePrintTarget()73 already_AddRefed<PrintTarget> nsDeviceContextSpecProxy::MakePrintTarget() {
74   MOZ_ASSERT(mRealDeviceContextSpec);
75 
76   double width, height;
77   mPrintSettings->GetEffectiveSheetSize(&width, &height);
78   if (width <= 0 || height <= 0) {
79     return nullptr;
80   }
81 
82   // convert twips to points
83   width /= TWIPS_PER_POINT_FLOAT;
84   height /= TWIPS_PER_POINT_FLOAT;
85 
86   RefPtr<gfxASurface> surface =
87       gfxPlatform::GetPlatform()->CreateOffscreenSurface(
88           mozilla::gfx::IntSize::Ceil(width, height),
89           mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32);
90   if (!surface) {
91     return nullptr;
92   }
93 
94   // The type of PrintTarget that we return here shouldn't really matter since
95   // our implementation of GetDrawEventRecorder returns an object, which means
96   // the DrawTarget returned by the PrintTarget will be a
97   // DrawTargetWrapAndRecord. The recording will be serialized and sent over to
98   // the parent process where PrintTranslator::TranslateRecording will call
99   // MakePrintTarget (indirectly via PrintTranslator::CreateDrawTarget) on
100   // whatever type of nsIDeviceContextSpecProxy is created for the platform that
101   // we are running on.  It is that DrawTarget that the recording will be
102   // replayed on to print.
103   // XXX(jwatt): The above isn't quite true.  We do want to use a
104   // PrintTargetRecording here, but we can't until bug 1280324 is figured out
105   // and fixed otherwise we will cause bug 1280181 to happen again.
106   RefPtr<PrintTarget> target = PrintTargetThebes::CreateOrNull(surface);
107 
108   return target.forget();
109 }
110 
111 NS_IMETHODIMP
GetDrawEventRecorder(mozilla::gfx::DrawEventRecorder ** aDrawEventRecorder)112 nsDeviceContextSpecProxy::GetDrawEventRecorder(
113     mozilla::gfx::DrawEventRecorder** aDrawEventRecorder) {
114   MOZ_ASSERT(aDrawEventRecorder);
115   RefPtr<mozilla::gfx::DrawEventRecorder> result = mRecorder;
116   result.forget(aDrawEventRecorder);
117   return NS_OK;
118 }
119 
GetDPI()120 float nsDeviceContextSpecProxy::GetDPI() {
121   MOZ_ASSERT(mRealDeviceContextSpec);
122 
123   return mRealDeviceContextSpec->GetDPI();
124 }
125 
GetPrintingScale()126 float nsDeviceContextSpecProxy::GetPrintingScale() {
127   MOZ_ASSERT(mRealDeviceContextSpec);
128 
129   return mRealDeviceContextSpec->GetPrintingScale();
130 }
131 
GetPrintingTranslate()132 gfxPoint nsDeviceContextSpecProxy::GetPrintingTranslate() {
133   MOZ_ASSERT(mRealDeviceContextSpec);
134 
135   return mRealDeviceContextSpec->GetPrintingTranslate();
136 }
137 
138 NS_IMETHODIMP
BeginDocument(const nsAString & aTitle,const nsAString & aPrintToFileName,int32_t aStartPage,int32_t aEndPage)139 nsDeviceContextSpecProxy::BeginDocument(const nsAString& aTitle,
140                                         const nsAString& aPrintToFileName,
141                                         int32_t aStartPage, int32_t aEndPage) {
142   if (!mRemotePrintJob || mRemotePrintJob->IsDestroyed()) {
143     mRemotePrintJob = nullptr;
144     return NS_ERROR_NOT_AVAILABLE;
145   }
146 
147   mRecorder = new mozilla::layout::DrawEventRecorderPRFileDesc();
148   nsresult rv = mRemotePrintJob->InitializePrint(
149       nsString(aTitle), nsString(aPrintToFileName), aStartPage, aEndPage);
150   if (NS_FAILED(rv)) {
151     // The parent process will send a 'delete' message to tell this process to
152     // delete our RemotePrintJobChild.  As soon as we return to the event loop
153     // and evaluate that message we will crash if we try to access
154     // mRemotePrintJob.  We must not try to use it again.
155     mRemotePrintJob = nullptr;
156   }
157   return rv;
158 }
159 
160 NS_IMETHODIMP
EndDocument()161 nsDeviceContextSpecProxy::EndDocument() {
162   if (!mRemotePrintJob || mRemotePrintJob->IsDestroyed()) {
163     mRemotePrintJob = nullptr;
164     return NS_ERROR_NOT_AVAILABLE;
165   }
166 
167   Unused << mRemotePrintJob->SendFinalizePrint();
168 
169   return NS_OK;
170 }
171 
172 NS_IMETHODIMP
AbortDocument()173 nsDeviceContextSpecProxy::AbortDocument() {
174   if (!mRemotePrintJob || mRemotePrintJob->IsDestroyed()) {
175     mRemotePrintJob = nullptr;
176     return NS_ERROR_NOT_AVAILABLE;
177   }
178 
179   Unused << mRemotePrintJob->SendAbortPrint(NS_OK);
180 
181   return NS_OK;
182 }
183 
184 NS_IMETHODIMP
BeginPage()185 nsDeviceContextSpecProxy::BeginPage() {
186   if (!mRemotePrintJob || mRemotePrintJob->IsDestroyed()) {
187     mRemotePrintJob = nullptr;
188     return NS_ERROR_NOT_AVAILABLE;
189   }
190 
191   mRecorder->OpenFD(mRemotePrintJob->GetNextPageFD());
192 
193   return NS_OK;
194 }
195 
196 NS_IMETHODIMP
EndPage()197 nsDeviceContextSpecProxy::EndPage() {
198   if (!mRemotePrintJob || mRemotePrintJob->IsDestroyed()) {
199     mRemotePrintJob = nullptr;
200     return NS_ERROR_NOT_AVAILABLE;
201   }
202 
203   // Send the page recording to the parent.
204   mRecorder->Close();
205   mRemotePrintJob->ProcessPage(std::move(mRecorder->TakeDependentSurfaces()));
206 
207   return NS_OK;
208 }
209