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 file_util::WriteFile(GetPdfPath(),
79 reinterpret_cast<const char*>(data
->front()),
84 // Reopen in read only mode.
85 pdf_file_
= base::CreatePlatformFile(GetPdfPath(), base::PLATFORM_FILE_OPEN
|
86 base::PLATFORM_FILE_READ
,
88 pwg_file_
= base::CreatePlatformFile(GetPwgPath(),
89 base::PLATFORM_FILE_CREATE_ALWAYS
|
90 base::PLATFORM_FILE_APPEND
, NULL
, NULL
);
93 bool FileHandlers::IsValid() {
94 return pdf_file_
!= base::kInvalidPlatformFileValue
&&
95 pwg_file_
!= base::kInvalidPlatformFileValue
;
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.
110 class PwgUtilityProcessHostClient
: public content::UtilityProcessHostClient
{
112 explicit PwgUtilityProcessHostClient(
113 const printing::PdfRenderSettings
& settings
);
115 void Convert(base::RefCountedMemory
* data
,
116 const PWGRasterConverter::ResultCallback
& callback
);
118 // UtilityProcessHostClient implementation.
119 virtual void OnProcessCrashed(int exit_code
) OVERRIDE
;
120 virtual bool OnMessageReceived(const IPC::Message
& message
) OVERRIDE
;
123 virtual ~PwgUtilityProcessHostClient();
126 void OnProcessStarted();
130 void RunCallback(bool success
);
132 void StartProcessOnIOThread();
134 void RunCallbackOnUIThread(bool success
);
135 void OnFilesReadyOnUIThread();
137 scoped_ptr
<FileHandlers
> files_
;
138 printing::PdfRenderSettings 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
) : settings_(settings
) {
149 PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() {
150 // Delete temp directory.
151 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE
, files_
.release());
154 void PwgUtilityProcessHostClient::Convert(
155 base::RefCountedMemory
* data
,
156 const PWGRasterConverter::ResultCallback
& callback
) {
157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
158 callback_
= callback
;
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));
168 void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code
) {
172 bool PwgUtilityProcessHostClient::OnMessageReceived(
173 const IPC::Message
& message
) {
175 IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient
, message
)
176 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted
, OnProcessStarted
)
178 ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded
, OnSucceeded
)
179 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed
,
181 IPC_MESSAGE_UNHANDLED(handled
= false)
182 IPC_END_MESSAGE_MAP()
186 void PwgUtilityProcessHostClient::OnProcessStarted() {
187 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
188 if (!utility_process_host_
) {
189 RunCallbackOnUIThread(false);
193 base::ProcessHandle process
= utility_process_host_
->GetData().handle
;
194 utility_process_host_
->Send(
195 new ChromeUtilityMsg_RenderPDFPagesToPWGRaster(
196 files_
->GetPdfForProcess(process
),
198 files_
->GetPwgForProcess(process
)));
199 utility_process_host_
.reset();
202 void PwgUtilityProcessHostClient::OnSucceeded() {
203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
207 void PwgUtilityProcessHostClient::OnFailed() {
208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
212 void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() {
213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
214 if (!files_
->IsValid()) {
215 RunCallbackOnUIThread(false);
218 BrowserThread::PostTask(
219 BrowserThread::IO
, FROM_HERE
,
220 base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread
, this));
223 void PwgUtilityProcessHostClient::StartProcessOnIOThread() {
224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
225 utility_process_host_
=
226 content::UtilityProcessHost::Create(
228 base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
229 utility_process_host_
->Send(new ChromeUtilityMsg_StartupPing
);
232 void PwgUtilityProcessHostClient::RunCallback(bool success
) {
233 BrowserThread::PostTask(
234 BrowserThread::UI
, FROM_HERE
,
235 base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread
, this,
239 void 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()));
249 class PWGRasterConverterImpl
: public PWGRasterConverter
{
251 PWGRasterConverterImpl();
253 virtual ~PWGRasterConverterImpl();
255 virtual void Start(base::RefCountedMemory
* data
,
256 const printing::PdfRenderSettings
& conversion_settings
,
257 const ResultCallback
& callback
) OVERRIDE
;
259 scoped_refptr
<PwgUtilityProcessHostClient
> utility_client_
;
260 base::CancelableCallback
<ResultCallback::RunType
> callback_
;
262 DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl
);
265 PWGRasterConverterImpl::PWGRasterConverterImpl() {
268 PWGRasterConverterImpl::~PWGRasterConverterImpl() {
271 void 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());
285 scoped_ptr
<PWGRasterConverter
> PWGRasterConverter::CreateDefault() {
286 return scoped_ptr
<PWGRasterConverter
>(new PWGRasterConverterImpl());
289 } // namespace local_discovery