pwg_raster_converter.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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
113  void Convert(base::RefCountedMemory* data,
114               const PWGRasterConverter::ResultCallback& callback);
115
116  // UtilityProcessHostClient implementation.
117  virtual void OnProcessCrashed(int exit_code) OVERRIDE;
118  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
119
120 private:
121  virtual ~PwgUtilityProcessHostClient();
122
123  // Message handlers.
124  void OnProcessStarted();
125  void OnSucceeded();
126  void OnFailed();
127
128  void RunCallback(bool success);
129
130  void StartProcessOnIOThread();
131
132  void RunCallbackOnUIThread(bool success);
133  void OnFilesReadyOnUIThread();
134
135  scoped_ptr<FileHandlers> files_;
136  printing::PdfRenderSettings settings_;
137  PWGRasterConverter::ResultCallback callback_;
138  base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
139
140  DISALLOW_COPY_AND_ASSIGN(PwgUtilityProcessHostClient);
141};
142
143PwgUtilityProcessHostClient::PwgUtilityProcessHostClient(
144    const printing::PdfRenderSettings& settings) : settings_(settings) {
145}
146
147PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() {
148  // Delete temp directory.
149  BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, files_.release());
150}
151
152void PwgUtilityProcessHostClient::Convert(
153    base::RefCountedMemory* data,
154    const PWGRasterConverter::ResultCallback& callback) {
155  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
156  callback_ = callback;
157  CHECK(!files_);
158  files_.reset(new FileHandlers());
159  BrowserThread::PostTaskAndReply(
160      BrowserThread::FILE, FROM_HERE,
161      base::Bind(&FileHandlers::Init, base::Unretained(files_.get()),
162                 make_scoped_refptr(data)),
163      base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread, this));
164}
165
166void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
167  OnFailed();
168}
169
170bool PwgUtilityProcessHostClient::OnMessageReceived(
171  const IPC::Message& message) {
172  bool handled = true;
173  IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient, message)
174    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
175    IPC_MESSAGE_HANDLER(
176        ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded, OnSucceeded)
177    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed,
178                        OnFailed)
179    IPC_MESSAGE_UNHANDLED(handled = false)
180  IPC_END_MESSAGE_MAP()
181  return handled;
182}
183
184void PwgUtilityProcessHostClient::OnProcessStarted() {
185  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
186  if (!utility_process_host_) {
187    RunCallbackOnUIThread(false);
188    return;
189  }
190
191  base::ProcessHandle process = utility_process_host_->GetData().handle;
192  utility_process_host_->Send(
193      new ChromeUtilityMsg_RenderPDFPagesToPWGRaster(
194          files_->GetPdfForProcess(process),
195          settings_,
196          files_->GetPwgForProcess(process)));
197  utility_process_host_.reset();
198}
199
200void PwgUtilityProcessHostClient::OnSucceeded() {
201  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
202  RunCallback(true);
203}
204
205void PwgUtilityProcessHostClient::OnFailed() {
206  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
207  RunCallback(false);
208}
209
210void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() {
211  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
212  if (!files_->IsValid()) {
213    RunCallbackOnUIThread(false);
214    return;
215  }
216  BrowserThread::PostTask(
217      BrowserThread::IO, FROM_HERE,
218      base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread, this));
219}
220
221void PwgUtilityProcessHostClient::StartProcessOnIOThread() {
222  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
223  utility_process_host_ =
224      content::UtilityProcessHost::Create(
225          this,
226          base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
227  utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
228}
229
230void PwgUtilityProcessHostClient::RunCallback(bool success) {
231  BrowserThread::PostTask(
232      BrowserThread::UI, FROM_HERE,
233      base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this,
234                 success));
235}
236
237void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success) {
238  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
239  if (!callback_.is_null()) {
240    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
241                            base::Bind(callback_, success,
242                                       files_->GetPwgPath()));
243    callback_.Reset();
244  }
245}
246
247class PWGRasterConverterImpl : public PWGRasterConverter {
248 public:
249  PWGRasterConverterImpl();
250
251  virtual ~PWGRasterConverterImpl();
252
253  virtual void Start(base::RefCountedMemory* data,
254                     const printing::PdfRenderSettings& conversion_settings,
255                     const ResultCallback& callback) OVERRIDE;
256 private:
257  scoped_refptr<PwgUtilityProcessHostClient> utility_client_;
258  base::CancelableCallback<ResultCallback::RunType> callback_;
259
260  DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl);
261};
262
263PWGRasterConverterImpl::PWGRasterConverterImpl() {
264}
265
266PWGRasterConverterImpl::~PWGRasterConverterImpl() {
267}
268
269void PWGRasterConverterImpl::Start(
270    base::RefCountedMemory* data,
271    const printing::PdfRenderSettings& conversion_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_ = new PwgUtilityProcessHostClient(conversion_settings);
277  utility_client_->Convert(data, callback_.callback());
278}
279
280}  // namespace
281
282// static
283scoped_ptr<PWGRasterConverter> PWGRasterConverter::CreateDefault() {
284  return scoped_ptr<PWGRasterConverter>(new PWGRasterConverterImpl());
285}
286
287}  // namespace local_discovery
288