Revert 285173 "Removed InProcessBrowserTest::CleanUpOnMainThread()"
[chromium-blink-merge.git] / chrome / browser / printing / pdf_to_emf_converter.cc
blob21800ee6311e370862a549cded7790af4d4f50bf
1 // Copyright 2014 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/printing/pdf_to_emf_converter.h"
7 #include "base/bind_helpers.h"
8 #include "base/cancelable_callback.h"
9 #include "base/file_util.h"
10 #include "base/files/file.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/logging.h"
13 #include "chrome/common/chrome_utility_messages.h"
14 #include "chrome/common/chrome_utility_printing_messages.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/child_process_data.h"
17 #include "content/public/browser/utility_process_host.h"
18 #include "content/public/browser/utility_process_host_client.h"
19 #include "printing/page_range.h"
20 #include "printing/pdf_render_settings.h"
22 namespace printing {
24 namespace {
26 using content::BrowserThread;
28 class FileHandlers {
29 public:
30 FileHandlers() {}
32 ~FileHandlers() {
33 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
36 void Init(base::RefCountedMemory* data);
37 bool IsValid();
39 base::FilePath GetEmfPath() const {
40 return temp_dir_.path().AppendASCII("output.emf");
43 base::FilePath GetPdfPath() const {
44 return temp_dir_.path().AppendASCII("input.pdf");
47 IPC::PlatformFileForTransit GetPdfForProcess(base::ProcessHandle process) {
48 DCHECK(pdf_file_.IsValid());
49 IPC::PlatformFileForTransit transit =
50 IPC::TakeFileHandleForProcess(pdf_file_.Pass(), process);
51 return transit;
54 const base::FilePath& GetBasePath() const {
55 return temp_dir_.path();
58 private:
59 base::ScopedTempDir temp_dir_;
60 base::File pdf_file_;
63 void FileHandlers::Init(base::RefCountedMemory* data) {
64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
66 if (!temp_dir_.CreateUniqueTempDir()) {
67 return;
70 if (static_cast<int>(data->size()) !=
71 base::WriteFile(GetPdfPath(), data->front_as<char>(), data->size())) {
72 return;
75 // Reopen in read only mode.
76 pdf_file_.Initialize(GetPdfPath(),
77 base::File::FLAG_OPEN | base::File::FLAG_READ);
80 bool FileHandlers::IsValid() {
81 return pdf_file_.IsValid();
84 // Converts PDF into EMF.
85 // Class uses 3 threads: UI, IO and FILE.
86 // Internal workflow is following:
87 // 1. Create instance on the UI thread. (files_, settings_,)
88 // 2. Create file on the FILE thread.
89 // 3. Start utility process and start conversion on the IO thread.
90 // 4. Run result callback on the UI thread.
91 // 5. Instance is destroyed from any thread that has the last reference.
92 // 6. FileHandlers destroyed on the FILE thread.
93 // This step posts |FileHandlers| to be destroyed on the FILE thread.
94 // All these steps work sequentially, so no data should be accessed
95 // simultaneously by several threads.
96 class PdfToEmfUtilityProcessHostClient
97 : public content::UtilityProcessHostClient {
98 public:
99 explicit PdfToEmfUtilityProcessHostClient(
100 const printing::PdfRenderSettings& settings);
102 void Convert(base::RefCountedMemory* data,
103 const PdfToEmfConverter::ResultCallback& callback);
105 // UtilityProcessHostClient implementation.
106 virtual void OnProcessCrashed(int exit_code) OVERRIDE;
107 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
109 private:
110 virtual ~PdfToEmfUtilityProcessHostClient();
112 // Message handlers.
113 void OnProcessStarted();
114 void OnSucceeded(const std::vector<printing::PageRange>& page_ranges,
115 double scale_factor);
116 void OnFailed();
118 void RunCallback(const std::vector<printing::PageRange>& page_ranges,
119 double scale_factor);
121 void StartProcessOnIOThread();
123 void RunCallbackOnUIThread(
124 const std::vector<printing::PageRange>& page_ranges,
125 double scale_factor);
126 void OnFilesReadyOnUIThread();
128 scoped_ptr<FileHandlers> files_;
129 printing::PdfRenderSettings settings_;
130 PdfToEmfConverter::ResultCallback callback_;
131 base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
133 DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient);
136 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient(
137 const printing::PdfRenderSettings& settings)
138 : settings_(settings) {}
140 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() {
141 // Delete temp directory.
142 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, files_.release());
145 void PdfToEmfUtilityProcessHostClient::Convert(
146 base::RefCountedMemory* data,
147 const PdfToEmfConverter::ResultCallback& callback) {
148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
149 callback_ = callback;
150 CHECK(!files_);
151 files_.reset(new FileHandlers());
152 BrowserThread::PostTaskAndReply(
153 BrowserThread::FILE,
154 FROM_HERE,
155 base::Bind(&FileHandlers::Init,
156 base::Unretained(files_.get()),
157 make_scoped_refptr(data)),
158 base::Bind(&PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread,
159 this));
162 void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
163 OnFailed();
166 bool PdfToEmfUtilityProcessHostClient::OnMessageReceived(
167 const IPC::Message& message) {
168 bool handled = true;
169 IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message)
170 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
171 IPC_MESSAGE_HANDLER(
172 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded, OnSucceeded)
173 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed,
174 OnFailed)
175 IPC_MESSAGE_UNHANDLED(handled = false)
176 IPC_END_MESSAGE_MAP()
177 return handled;
180 void PdfToEmfUtilityProcessHostClient::OnProcessStarted() {
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
182 if (!utility_process_host_) {
183 RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0);
184 return;
187 base::ProcessHandle process = utility_process_host_->GetData().handle;
188 utility_process_host_->Send(
189 new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
190 files_->GetPdfForProcess(process),
191 files_->GetEmfPath(),
192 settings_,
193 std::vector<printing::PageRange>()));
194 utility_process_host_.reset();
197 void PdfToEmfUtilityProcessHostClient::OnSucceeded(
198 const std::vector<printing::PageRange>& page_ranges,
199 double scale_factor) {
200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
201 RunCallback(page_ranges, scale_factor);
204 void PdfToEmfUtilityProcessHostClient::OnFailed() {
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
206 RunCallback(std::vector<printing::PageRange>(), 0.0);
209 void PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread() {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
211 if (!files_->IsValid()) {
212 RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0);
213 return;
215 BrowserThread::PostTask(
216 BrowserThread::IO,
217 FROM_HERE,
218 base::Bind(&PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread,
219 this));
222 void PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread() {
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
224 utility_process_host_ =
225 content::UtilityProcessHost::Create(
226 this,
227 base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
228 // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load
229 // gdiplus.dll, change how rendering happens, and not be able to correctly
230 // generate when sent to a metafile DC.
231 utility_process_host_->SetExposedDir(files_->GetBasePath());
232 utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
235 void PdfToEmfUtilityProcessHostClient::RunCallback(
236 const std::vector<printing::PageRange>& page_ranges,
237 double scale_factor) {
238 BrowserThread::PostTask(
239 BrowserThread::UI,
240 FROM_HERE,
241 base::Bind(&PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread,
242 this,
243 page_ranges,
244 scale_factor));
247 void PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread(
248 const std::vector<printing::PageRange>& page_ranges,
249 double scale_factor) {
250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
251 std::vector<base::FilePath> page_filenames;
252 std::vector<printing::PageRange>::const_iterator iter;
253 for (iter = page_ranges.begin(); iter != page_ranges.end(); ++iter) {
254 for (int page_number = iter->from; page_number <= iter->to; ++page_number) {
255 page_filenames.push_back(files_->GetEmfPath().InsertBeforeExtensionASCII(
256 base::StringPrintf(".%d", page_number)));
259 if (!callback_.is_null()) {
260 BrowserThread::PostTask(
261 BrowserThread::UI,
262 FROM_HERE,
263 base::Bind(callback_, scale_factor, page_filenames));
264 callback_.Reset();
268 class PdfToEmfConverterImpl : public PdfToEmfConverter {
269 public:
270 PdfToEmfConverterImpl();
272 virtual ~PdfToEmfConverterImpl();
274 virtual void Start(base::RefCountedMemory* data,
275 const printing::PdfRenderSettings& conversion_settings,
276 const ResultCallback& callback) OVERRIDE;
278 private:
279 scoped_refptr<PdfToEmfUtilityProcessHostClient> utility_client_;
280 base::CancelableCallback<ResultCallback::RunType> callback_;
282 DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl);
285 PdfToEmfConverterImpl::PdfToEmfConverterImpl() {
288 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() {
291 void PdfToEmfConverterImpl::Start(
292 base::RefCountedMemory* data,
293 const printing::PdfRenderSettings& conversion_settings,
294 const ResultCallback& callback) {
295 // Rebind cancelable callback to avoid calling callback if
296 // PdfToEmfConverterImpl is destroyed.
297 callback_.Reset(callback);
298 utility_client_ = new PdfToEmfUtilityProcessHostClient(conversion_settings);
299 utility_client_->Convert(data, callback_.callback());
302 } // namespace
304 // static
305 scoped_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() {
306 return scoped_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl());
309 } // namespace printing