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.
5 #include "chrome/browser/local_discovery/pwg_raster_converter.h"
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"
18 namespace local_discovery {
22 using content::BrowserThread;
26 FileHandlers() : pdf_file_(base::kInvalidPlatformFileValue),
27 pwg_file_(base::kInvalidPlatformFileValue) { }
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_);
37 void Init(base::RefCountedMemory* data);
40 base::FilePath GetPwgPath() const {
41 return temp_dir_.path().AppendASCII("output.pwg");
44 base::FilePath GetPdfPath() const {
45 return temp_dir_.path().AppendASCII("input.pdf");
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;
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;
65 base::ScopedTempDir temp_dir_;
66 base::PlatformFile pdf_file_;
67 base::PlatformFile pwg_file_;
70 void FileHandlers::Init(base::RefCountedMemory* data) {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
73 if (!temp_dir_.CreateUniqueTempDir()) {
77 if (static_cast<int>(data->size()) !=
78 base::WriteFile(GetPdfPath(), data->front_as<char>(), data->size())) {
82 // Reopen in read only mode.
83 pdf_file_ = base::CreatePlatformFile(GetPdfPath(), base::PLATFORM_FILE_OPEN |
84 base::PLATFORM_FILE_READ,
86 pwg_file_ = base::CreatePlatformFile(GetPwgPath(),
87 base::PLATFORM_FILE_CREATE_ALWAYS |
88 base::PLATFORM_FILE_APPEND, NULL, NULL);
91 bool FileHandlers::IsValid() {
92 return pdf_file_ != base::kInvalidPlatformFileValue &&
93 pwg_file_ != base::kInvalidPlatformFileValue;
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.
108 class PwgUtilityProcessHostClient : public content::UtilityProcessHostClient {
110 explicit PwgUtilityProcessHostClient(
111 const printing::PdfRenderSettings& settings,
112 const printing::PwgRasterSettings& bitmap_settings);
114 void Convert(base::RefCountedMemory* data,
115 const PWGRasterConverter::ResultCallback& callback);
117 // UtilityProcessHostClient implementation.
118 virtual void OnProcessCrashed(int exit_code) OVERRIDE;
119 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
122 virtual ~PwgUtilityProcessHostClient();
125 void OnProcessStarted();
129 void RunCallback(bool success);
131 void StartProcessOnIOThread();
133 void RunCallbackOnUIThread(bool success);
134 void OnFilesReadyOnUIThread();
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_;
142 DISALLOW_COPY_AND_ASSIGN(PwgUtilityProcessHostClient);
145 PwgUtilityProcessHostClient::PwgUtilityProcessHostClient(
146 const printing::PdfRenderSettings& settings,
147 const printing::PwgRasterSettings& bitmap_settings)
148 : settings_(settings), bitmap_settings_(bitmap_settings) {}
150 PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() {
151 // Delete temp directory.
152 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, files_.release());
155 void PwgUtilityProcessHostClient::Convert(
156 base::RefCountedMemory* data,
157 const PWGRasterConverter::ResultCallback& callback) {
158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
159 callback_ = callback;
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));
169 void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
173 bool PwgUtilityProcessHostClient::OnMessageReceived(
174 const IPC::Message& message) {
176 IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient, message)
177 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
179 ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded, OnSucceeded)
180 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed,
182 IPC_MESSAGE_UNHANDLED(handled = false)
183 IPC_END_MESSAGE_MAP()
187 void PwgUtilityProcessHostClient::OnProcessStarted() {
188 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
189 if (!utility_process_host_) {
190 RunCallbackOnUIThread(false);
194 base::ProcessHandle process = utility_process_host_->GetData().handle;
195 utility_process_host_->Send(new ChromeUtilityMsg_RenderPDFPagesToPWGRaster(
196 files_->GetPdfForProcess(process),
199 files_->GetPwgForProcess(process)));
200 utility_process_host_.reset();
203 void PwgUtilityProcessHostClient::OnSucceeded() {
204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
208 void PwgUtilityProcessHostClient::OnFailed() {
209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
213 void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() {
214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
215 if (!files_->IsValid()) {
216 RunCallbackOnUIThread(false);
219 BrowserThread::PostTask(
220 BrowserThread::IO, FROM_HERE,
221 base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread, this));
224 void PwgUtilityProcessHostClient::StartProcessOnIOThread() {
225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
226 utility_process_host_ =
227 content::UtilityProcessHost::Create(
229 base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
230 utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
233 void PwgUtilityProcessHostClient::RunCallback(bool success) {
234 BrowserThread::PostTask(
235 BrowserThread::UI, FROM_HERE,
236 base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this,
240 void 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()));
250 class PWGRasterConverterImpl : public PWGRasterConverter {
252 PWGRasterConverterImpl();
254 virtual ~PWGRasterConverterImpl();
256 virtual void Start(base::RefCountedMemory* data,
257 const printing::PdfRenderSettings& conversion_settings,
258 const printing::PwgRasterSettings& bitmap_settings,
259 const ResultCallback& callback) OVERRIDE;
262 scoped_refptr<PwgUtilityProcessHostClient> utility_client_;
263 base::CancelableCallback<ResultCallback::RunType> callback_;
265 DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl);
268 PWGRasterConverterImpl::PWGRasterConverterImpl() {
271 PWGRasterConverterImpl::~PWGRasterConverterImpl() {
274 void 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);
283 new PwgUtilityProcessHostClient(conversion_settings, bitmap_settings);
284 utility_client_->Convert(data, callback_.callback());
290 scoped_ptr<PWGRasterConverter> PWGRasterConverter::CreateDefault() {
291 return scoped_ptr<PWGRasterConverter>(new PWGRasterConverterImpl());
294 } // namespace local_discovery