1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/local_discovery/pwg_raster_converter.h"
6
7 #include "base/bind_helpers.h"
8 #include "base/cancelable_callback.h"
9 #include "base/files/file.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/logging.h"
13 #include "chrome/common/chrome_utility_messages.h"
14 #include "chrome/common/chrome_utility_printing_messages.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/child_process_data.h"
17 #include "content/public/browser/utility_process_host.h"
18 #include "content/public/browser/utility_process_host_client.h"
19 #include "printing/pdf_render_settings.h"
20 #include "printing/pwg_raster_settings.h"
21
22 namespace local_discovery {
23
24 namespace {
25
26 using content::BrowserThread;
27
28 class FileHandlers {
29 public:
FileHandlers()30 FileHandlers() {}
31
~FileHandlers()32 ~FileHandlers() {
33 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
34 }
35
36 void Init(base::RefCountedMemory* data);
37 bool IsValid();
38
GetPwgPath() const39 base::FilePath GetPwgPath() const {
40 return temp_dir_.path().AppendASCII("output.pwg");
41 }
42
GetPdfPath() const43 base::FilePath GetPdfPath() const {
44 return temp_dir_.path().AppendASCII("input.pdf");
45 }
46
GetPdfForProcess(base::ProcessHandle process)47 IPC::PlatformFileForTransit GetPdfForProcess(base::ProcessHandle process) {
48 DCHECK(pdf_file_.IsValid());
49 IPC::PlatformFileForTransit transit =
50 IPC::TakeFileHandleForProcess(pdf_file_.Pass(), process);
51 return transit;
52 }
53
GetPwgForProcess(base::ProcessHandle process)54 IPC::PlatformFileForTransit GetPwgForProcess(base::ProcessHandle process) {
55 DCHECK(pwg_file_.IsValid());
56 IPC::PlatformFileForTransit transit =
57 IPC::TakeFileHandleForProcess(pwg_file_.Pass(), process);
58 return transit;
59 }
60
61 private:
62 base::ScopedTempDir temp_dir_;
63 base::File pdf_file_;
64 base::File pwg_file_;
65 };
66
Init(base::RefCountedMemory * data)67 void FileHandlers::Init(base::RefCountedMemory* data) {
68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
69
70 if (!temp_dir_.CreateUniqueTempDir()) {
71 return;
72 }
73
74 if (static_cast<int>(data->size()) !=
75 base::WriteFile(GetPdfPath(), data->front_as<char>(), data->size())) {
76 return;
77 }
78
79 // Reopen in read only mode.
80 pdf_file_.Initialize(GetPdfPath(),
81 base::File::FLAG_OPEN | base::File::FLAG_READ);
82 pwg_file_.Initialize(GetPwgPath(),
83 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
84 }
85
IsValid()86 bool FileHandlers::IsValid() {
87 return pdf_file_.IsValid() && pwg_file_.IsValid();
88 }
89
90 // Converts PDF into PWG raster.
91 // Class uses 3 threads: UI, IO and FILE.
92 // Internal workflow is following:
93 // 1. Create instance on the UI thread. (files_, settings_,)
94 // 2. Create file on the FILE thread.
95 // 3. Start utility process and start conversion on the IO thread.
96 // 4. Run result callback on the UI thread.
97 // 5. Instance is destroyed from any thread that has the last reference.
98 // 6. FileHandlers destroyed on the FILE thread.
99 // This step posts |FileHandlers| to be destroyed on the FILE thread.
100 // All these steps work sequentially, so no data should be accessed
101 // simultaneously by several threads.
102 class PwgUtilityProcessHostClient : public content::UtilityProcessHostClient {
103 public:
104 explicit PwgUtilityProcessHostClient(
105 const printing::PdfRenderSettings& settings,
106 const printing::PwgRasterSettings& bitmap_settings);
107
108 void Convert(base::RefCountedMemory* data,
109 const PWGRasterConverter::ResultCallback& callback);
110
111 // UtilityProcessHostClient implementation.
112 virtual void OnProcessCrashed(int exit_code) OVERRIDE;
113 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
114
115 private:
116 virtual ~PwgUtilityProcessHostClient();
117
118 // Message handlers.
119 void OnProcessStarted();
120 void OnSucceeded();
121 void OnFailed();
122
123 void RunCallback(bool success);
124
125 void StartProcessOnIOThread();
126
127 void RunCallbackOnUIThread(bool success);
128 void OnFilesReadyOnUIThread();
129
130 scoped_ptr<FileHandlers, BrowserThread::DeleteOnFileThread> files_;
131 printing::PdfRenderSettings settings_;
132 printing::PwgRasterSettings bitmap_settings_;
133 PWGRasterConverter::ResultCallback callback_;
134 base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
135
136 DISALLOW_COPY_AND_ASSIGN(PwgUtilityProcessHostClient);
137 };
138
PwgUtilityProcessHostClient(const printing::PdfRenderSettings & settings,const printing::PwgRasterSettings & bitmap_settings)139 PwgUtilityProcessHostClient::PwgUtilityProcessHostClient(
140 const printing::PdfRenderSettings& settings,
141 const printing::PwgRasterSettings& bitmap_settings)
142 : settings_(settings), bitmap_settings_(bitmap_settings) {}
143
~PwgUtilityProcessHostClient()144 PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() {
145 }
146
Convert(base::RefCountedMemory * data,const PWGRasterConverter::ResultCallback & callback)147 void PwgUtilityProcessHostClient::Convert(
148 base::RefCountedMemory* data,
149 const PWGRasterConverter::ResultCallback& callback) {
150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
151 callback_ = callback;
152 CHECK(!files_);
153 files_.reset(new FileHandlers());
154 BrowserThread::PostTaskAndReply(
155 BrowserThread::FILE, FROM_HERE,
156 base::Bind(&FileHandlers::Init, base::Unretained(files_.get()),
157 make_scoped_refptr(data)),
158 base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread, this));
159 }
160
OnProcessCrashed(int exit_code)161 void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
162 OnFailed();
163 }
164
OnMessageReceived(const IPC::Message & message)165 bool PwgUtilityProcessHostClient::OnMessageReceived(
166 const IPC::Message& message) {
167 bool handled = true;
168 IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient, message)
169 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
170 IPC_MESSAGE_HANDLER(
171 ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded, OnSucceeded)
172 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed,
173 OnFailed)
174 IPC_MESSAGE_UNHANDLED(handled = false)
175 IPC_END_MESSAGE_MAP()
176 return handled;
177 }
178
OnProcessStarted()179 void PwgUtilityProcessHostClient::OnProcessStarted() {
180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
181 if (!utility_process_host_) {
182 RunCallbackOnUIThread(false);
183 return;
184 }
185
186 base::ProcessHandle process = utility_process_host_->GetData().handle;
187 utility_process_host_->Send(new ChromeUtilityMsg_RenderPDFPagesToPWGRaster(
188 files_->GetPdfForProcess(process),
189 settings_,
190 bitmap_settings_,
191 files_->GetPwgForProcess(process)));
192 utility_process_host_.reset();
193 }
194
OnSucceeded()195 void PwgUtilityProcessHostClient::OnSucceeded() {
196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
197 RunCallback(true);
198 }
199
OnFailed()200 void PwgUtilityProcessHostClient::OnFailed() {
201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
202 RunCallback(false);
203 }
204
OnFilesReadyOnUIThread()205 void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() {
206 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
207 if (!files_->IsValid()) {
208 RunCallbackOnUIThread(false);
209 return;
210 }
211 BrowserThread::PostTask(
212 BrowserThread::IO, FROM_HERE,
213 base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread, this));
214 }
215
StartProcessOnIOThread()216 void PwgUtilityProcessHostClient::StartProcessOnIOThread() {
217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
218 utility_process_host_ =
219 content::UtilityProcessHost::Create(
220 this,
221 base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
222 utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
223 }
224
RunCallback(bool success)225 void PwgUtilityProcessHostClient::RunCallback(bool success) {
226 BrowserThread::PostTask(
227 BrowserThread::UI, FROM_HERE,
228 base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this,
229 success));
230 }
231
RunCallbackOnUIThread(bool success)232 void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success) {
233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
234 if (!callback_.is_null()) {
235 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
236 base::Bind(callback_, success,
237 files_->GetPwgPath()));
238 callback_.Reset();
239 }
240 }
241
242 class PWGRasterConverterImpl : public PWGRasterConverter {
243 public:
244 PWGRasterConverterImpl();
245
246 virtual ~PWGRasterConverterImpl();
247
248 virtual void Start(base::RefCountedMemory* data,
249 const printing::PdfRenderSettings& conversion_settings,
250 const printing::PwgRasterSettings& bitmap_settings,
251 const ResultCallback& callback) OVERRIDE;
252
253 private:
254 scoped_refptr<PwgUtilityProcessHostClient> utility_client_;
255 base::CancelableCallback<ResultCallback::RunType> callback_;
256
257 DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl);
258 };
259
PWGRasterConverterImpl()260 PWGRasterConverterImpl::PWGRasterConverterImpl() {
261 }
262
~PWGRasterConverterImpl()263 PWGRasterConverterImpl::~PWGRasterConverterImpl() {
264 }
265
Start(base::RefCountedMemory * data,const printing::PdfRenderSettings & conversion_settings,const printing::PwgRasterSettings & bitmap_settings,const ResultCallback & callback)266 void PWGRasterConverterImpl::Start(
267 base::RefCountedMemory* data,
268 const printing::PdfRenderSettings& conversion_settings,
269 const printing::PwgRasterSettings& bitmap_settings,
270 const ResultCallback& callback) {
271 // Rebind cancelable callback to avoid calling callback if
272 // PWGRasterConverterImpl is destroyed.
273 callback_.Reset(callback);
274 utility_client_ =
275 new PwgUtilityProcessHostClient(conversion_settings, bitmap_settings);
276 utility_client_->Convert(data, callback_.callback());
277 }
278
279 } // namespace
280
281 // static
CreateDefault()282 scoped_ptr<PWGRasterConverter> PWGRasterConverter::CreateDefault() {
283 return scoped_ptr<PWGRasterConverter>(new PWGRasterConverterImpl());
284 }
285
286 } // namespace local_discovery
287