pwg_raster_converter.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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      file_util::WriteFile(GetPdfPath(),
79                           data->front_as<char>(),
80                           data->size())) {
81    return;
82  }
83
84  // Reopen in read only mode.
85  pdf_file_ = base::CreatePlatformFile(GetPdfPath(), base::PLATFORM_FILE_OPEN |
86                                                     base::PLATFORM_FILE_READ,
87                                       NULL, NULL);
88  pwg_file_ = base::CreatePlatformFile(GetPwgPath(),
89                                       base::PLATFORM_FILE_CREATE_ALWAYS |
90                                       base::PLATFORM_FILE_APPEND, NULL, NULL);
91}
92
93bool FileHandlers::IsValid() {
94  return pdf_file_ != base::kInvalidPlatformFileValue &&
95         pwg_file_ != base::kInvalidPlatformFileValue;
96}
97
98// Converts PDF into PWG raster.
99// Class uses 3 threads: UI, IO and FILE.
100// Internal workflow is following:
101// 1. Create instance on the UI thread. (files_, settings_,)
102// 2. Create file on the FILE thread.
103// 3. Start utility process and start conversion on the IO thread.
104// 4. Run result callback on the UI thread.
105// 5. Instance is destroyed from any thread that has the last reference.
106// 6. FileHandlers destroyed on the FILE thread.
107//    This step posts |FileHandlers| to be destroyed on the FILE thread.
108// All these steps work sequentially, so no data should be accessed
109// simultaneously by several threads.
110class PwgUtilityProcessHostClient : public content::UtilityProcessHostClient {
111 public:
112  explicit PwgUtilityProcessHostClient(
113      const printing::PdfRenderSettings& settings);
114
115  void Convert(base::RefCountedMemory* data,
116               const PWGRasterConverter::ResultCallback& callback);
117
118  // UtilityProcessHostClient implementation.
119  virtual void OnProcessCrashed(int exit_code) OVERRIDE;
120  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
121
122 private:
123  virtual ~PwgUtilityProcessHostClient();
124
125  // Message handlers.
126  void OnProcessStarted();
127  void OnSucceeded();
128  void OnFailed();
129
130  void RunCallback(bool success);
131
132  void StartProcessOnIOThread();
133
134  void RunCallbackOnUIThread(bool success);
135  void OnFilesReadyOnUIThread();
136
137  scoped_ptr<FileHandlers> files_;
138  printing::PdfRenderSettings 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) : settings_(settings) {
147}
148
149PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() {
150  // Delete temp directory.
151  BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, files_.release());
152}
153
154void PwgUtilityProcessHostClient::Convert(
155    base::RefCountedMemory* data,
156    const PWGRasterConverter::ResultCallback& callback) {
157  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
158  callback_ = callback;
159  CHECK(!files_);
160  files_.reset(new FileHandlers());
161  BrowserThread::PostTaskAndReply(
162      BrowserThread::FILE, FROM_HERE,
163      base::Bind(&FileHandlers::Init, base::Unretained(files_.get()),
164                 make_scoped_refptr(data)),
165      base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread, this));
166}
167
168void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
169  OnFailed();
170}
171
172bool PwgUtilityProcessHostClient::OnMessageReceived(
173  const IPC::Message& message) {
174  bool handled = true;
175  IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient, message)
176    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
177    IPC_MESSAGE_HANDLER(
178        ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded, OnSucceeded)
179    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed,
180                        OnFailed)
181    IPC_MESSAGE_UNHANDLED(handled = false)
182  IPC_END_MESSAGE_MAP()
183  return handled;
184}
185
186void PwgUtilityProcessHostClient::OnProcessStarted() {
187  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
188  if (!utility_process_host_) {
189    RunCallbackOnUIThread(false);
190    return;
191  }
192
193  base::ProcessHandle process = utility_process_host_->GetData().handle;
194  utility_process_host_->Send(
195      new ChromeUtilityMsg_RenderPDFPagesToPWGRaster(
196          files_->GetPdfForProcess(process),
197          settings_,
198          files_->GetPwgForProcess(process)));
199  utility_process_host_.reset();
200}
201
202void PwgUtilityProcessHostClient::OnSucceeded() {
203  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
204  RunCallback(true);
205}
206
207void PwgUtilityProcessHostClient::OnFailed() {
208  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
209  RunCallback(false);
210}
211
212void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() {
213  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
214  if (!files_->IsValid()) {
215    RunCallbackOnUIThread(false);
216    return;
217  }
218  BrowserThread::PostTask(
219      BrowserThread::IO, FROM_HERE,
220      base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread, this));
221}
222
223void PwgUtilityProcessHostClient::StartProcessOnIOThread() {
224  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
225  utility_process_host_ =
226      content::UtilityProcessHost::Create(
227          this,
228          base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
229  utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
230}
231
232void PwgUtilityProcessHostClient::RunCallback(bool success) {
233  BrowserThread::PostTask(
234      BrowserThread::UI, FROM_HERE,
235      base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this,
236                 success));
237}
238
239void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success) {
240  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
241  if (!callback_.is_null()) {
242    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
243                            base::Bind(callback_, success,
244                                       files_->GetPwgPath()));
245    callback_.Reset();
246  }
247}
248
249class PWGRasterConverterImpl : public PWGRasterConverter {
250 public:
251  PWGRasterConverterImpl();
252
253  virtual ~PWGRasterConverterImpl();
254
255  virtual void Start(base::RefCountedMemory* data,
256                     const printing::PdfRenderSettings& conversion_settings,
257                     const ResultCallback& callback) OVERRIDE;
258 private:
259  scoped_refptr<PwgUtilityProcessHostClient> utility_client_;
260  base::CancelableCallback<ResultCallback::RunType> callback_;
261
262  DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl);
263};
264
265PWGRasterConverterImpl::PWGRasterConverterImpl() {
266}
267
268PWGRasterConverterImpl::~PWGRasterConverterImpl() {
269}
270
271void PWGRasterConverterImpl::Start(
272    base::RefCountedMemory* data,
273    const printing::PdfRenderSettings& conversion_settings,
274    const ResultCallback& callback) {
275  // Rebind cancelable callback to avoid calling callback if
276  // PWGRasterConverterImpl is destroyed.
277  callback_.Reset(callback);
278  utility_client_ = new PwgUtilityProcessHostClient(conversion_settings);
279  utility_client_->Convert(data, callback_.callback());
280}
281
282}  // namespace
283
284// static
285scoped_ptr<PWGRasterConverter> PWGRasterConverter::CreateDefault() {
286  return scoped_ptr<PWGRasterConverter>(new PWGRasterConverterImpl());
287}
288
289}  // namespace local_discovery
290