pwg_raster_converter.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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/file_util.h"
10#include "base/files/file.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
22namespace local_discovery {
23
24namespace {
25
26using content::BrowserThread;
27
28class FileHandlers {
29 public:
30  FileHandlers() {}
31
32  ~FileHandlers() {
33    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
34  }
35
36  void Init(base::RefCountedMemory* data);
37  bool IsValid();
38
39  base::FilePath GetPwgPath() const {
40    return temp_dir_.path().AppendASCII("output.pwg");
41  }
42
43  base::FilePath GetPdfPath() const {
44    return temp_dir_.path().AppendASCII("input.pdf");
45  }
46
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
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
67void 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
86bool 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.
102class 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> 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
139PwgUtilityProcessHostClient::PwgUtilityProcessHostClient(
140    const printing::PdfRenderSettings& settings,
141    const printing::PwgRasterSettings& bitmap_settings)
142    : settings_(settings), bitmap_settings_(bitmap_settings) {}
143
144PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() {
145  // Delete temp directory.
146  BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, files_.release());
147}
148
149void PwgUtilityProcessHostClient::Convert(
150    base::RefCountedMemory* data,
151    const PWGRasterConverter::ResultCallback& callback) {
152  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
153  callback_ = callback;
154  CHECK(!files_);
155  files_.reset(new FileHandlers());
156  BrowserThread::PostTaskAndReply(
157      BrowserThread::FILE, FROM_HERE,
158      base::Bind(&FileHandlers::Init, base::Unretained(files_.get()),
159                 make_scoped_refptr(data)),
160      base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread, this));
161}
162
163void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
164  OnFailed();
165}
166
167bool PwgUtilityProcessHostClient::OnMessageReceived(
168  const IPC::Message& message) {
169  bool handled = true;
170  IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient, message)
171    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
172    IPC_MESSAGE_HANDLER(
173        ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded, OnSucceeded)
174    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed,
175                        OnFailed)
176    IPC_MESSAGE_UNHANDLED(handled = false)
177  IPC_END_MESSAGE_MAP()
178  return handled;
179}
180
181void PwgUtilityProcessHostClient::OnProcessStarted() {
182  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
183  if (!utility_process_host_) {
184    RunCallbackOnUIThread(false);
185    return;
186  }
187
188  base::ProcessHandle process = utility_process_host_->GetData().handle;
189  utility_process_host_->Send(new ChromeUtilityMsg_RenderPDFPagesToPWGRaster(
190      files_->GetPdfForProcess(process),
191      settings_,
192      bitmap_settings_,
193      files_->GetPwgForProcess(process)));
194  utility_process_host_.reset();
195}
196
197void PwgUtilityProcessHostClient::OnSucceeded() {
198  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
199  RunCallback(true);
200}
201
202void PwgUtilityProcessHostClient::OnFailed() {
203  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
204  RunCallback(false);
205}
206
207void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() {
208  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
209  if (!files_->IsValid()) {
210    RunCallbackOnUIThread(false);
211    return;
212  }
213  BrowserThread::PostTask(
214      BrowserThread::IO, FROM_HERE,
215      base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread, this));
216}
217
218void PwgUtilityProcessHostClient::StartProcessOnIOThread() {
219  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
220  utility_process_host_ =
221      content::UtilityProcessHost::Create(
222          this,
223          base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
224  utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
225}
226
227void PwgUtilityProcessHostClient::RunCallback(bool success) {
228  BrowserThread::PostTask(
229      BrowserThread::UI, FROM_HERE,
230      base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this,
231                 success));
232}
233
234void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success) {
235  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
236  if (!callback_.is_null()) {
237    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
238                            base::Bind(callback_, success,
239                                       files_->GetPwgPath()));
240    callback_.Reset();
241  }
242}
243
244class PWGRasterConverterImpl : public PWGRasterConverter {
245 public:
246  PWGRasterConverterImpl();
247
248  virtual ~PWGRasterConverterImpl();
249
250  virtual void Start(base::RefCountedMemory* data,
251                     const printing::PdfRenderSettings& conversion_settings,
252                     const printing::PwgRasterSettings& bitmap_settings,
253                     const ResultCallback& callback) OVERRIDE;
254
255 private:
256  scoped_refptr<PwgUtilityProcessHostClient> utility_client_;
257  base::CancelableCallback<ResultCallback::RunType> callback_;
258
259  DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl);
260};
261
262PWGRasterConverterImpl::PWGRasterConverterImpl() {
263}
264
265PWGRasterConverterImpl::~PWGRasterConverterImpl() {
266}
267
268void PWGRasterConverterImpl::Start(
269    base::RefCountedMemory* data,
270    const printing::PdfRenderSettings& conversion_settings,
271    const printing::PwgRasterSettings& bitmap_settings,
272    const ResultCallback& callback) {
273  // Rebind cancelable callback to avoid calling callback if
274  // PWGRasterConverterImpl is destroyed.
275  callback_.Reset(callback);
276  utility_client_ =
277      new PwgUtilityProcessHostClient(conversion_settings, bitmap_settings);
278  utility_client_->Convert(data, callback_.callback());
279}
280
281}  // namespace
282
283// static
284scoped_ptr<PWGRasterConverter> PWGRasterConverter::CreateDefault() {
285  return scoped_ptr<PWGRasterConverter>(new PWGRasterConverterImpl());
286}
287
288}  // namespace local_discovery
289