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