[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / printing / pdf_to_emf_converter.cc
blobbce69a6b5bf0cf0cb7ae3d7caedcd52f989a0784
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 "content/public/browser/browser_thread.h"
15 #include "content/public/browser/child_process_data.h"
16 #include "content/public/browser/utility_process_host.h"
17 #include "content/public/browser/utility_process_host_client.h"
19 namespace printing {
21 namespace {
23 using content::BrowserThread;
25 class FileHandlers {
26 public:
27 FileHandlers() {}
29 ~FileHandlers() {
30 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
33 void Init(base::RefCountedMemory* data);
34 bool IsValid();
36 base::FilePath GetEmfPath() const {
37 return temp_dir_.path().AppendASCII("output.emf");
40 base::FilePath GetPdfPath() const {
41 return temp_dir_.path().AppendASCII("input.pdf");
44 IPC::PlatformFileForTransit GetPdfForProcess(base::ProcessHandle process) {
45 DCHECK(pdf_file_.IsValid());
46 IPC::PlatformFileForTransit transit =
47 IPC::TakeFileHandleForProcess(pdf_file_.Pass(), process);
48 return transit;
51 const base::FilePath& GetBasePath() const {
52 return temp_dir_.path();
55 private:
56 base::ScopedTempDir temp_dir_;
57 base::File pdf_file_;
60 void FileHandlers::Init(base::RefCountedMemory* data) {
61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
63 if (!temp_dir_.CreateUniqueTempDir()) {
64 return;
67 if (static_cast<int>(data->size()) !=
68 base::WriteFile(GetPdfPath(), data->front_as<char>(), data->size())) {
69 return;
72 // Reopen in read only mode.
73 pdf_file_.Initialize(GetPdfPath(),
74 base::File::FLAG_OPEN | base::File::FLAG_READ);
77 bool FileHandlers::IsValid() {
78 return pdf_file_.IsValid();
81 // Converts PDF into EMF.
82 // Class uses 3 threads: UI, IO and FILE.
83 // Internal workflow is following:
84 // 1. Create instance on the UI thread. (files_, settings_,)
85 // 2. Create file on the FILE thread.
86 // 3. Start utility process and start conversion on the IO thread.
87 // 4. Run result callback on the UI thread.
88 // 5. Instance is destroyed from any thread that has the last reference.
89 // 6. FileHandlers destroyed on the FILE thread.
90 // This step posts |FileHandlers| to be destroyed on the FILE thread.
91 // All these steps work sequentially, so no data should be accessed
92 // simultaneously by several threads.
93 class PdfToEmfUtilityProcessHostClient
94 : public content::UtilityProcessHostClient {
95 public:
96 explicit PdfToEmfUtilityProcessHostClient(
97 const printing::PdfRenderSettings& settings);
99 void Convert(base::RefCountedMemory* data,
100 const PdfToEmfConverter::ResultCallback& callback);
102 // UtilityProcessHostClient implementation.
103 virtual void OnProcessCrashed(int exit_code) OVERRIDE;
104 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
106 private:
107 virtual ~PdfToEmfUtilityProcessHostClient();
109 // Message handlers.
110 void OnProcessStarted();
111 void OnSucceeded(const std::vector<printing::PageRange>& page_ranges,
112 double scale_factor);
113 void OnFailed();
115 void RunCallback(const std::vector<printing::PageRange>& page_ranges,
116 double scale_factor);
118 void StartProcessOnIOThread();
120 void RunCallbackOnUIThread(
121 const std::vector<printing::PageRange>& page_ranges,
122 double scale_factor);
123 void OnFilesReadyOnUIThread();
125 scoped_ptr<FileHandlers> files_;
126 printing::PdfRenderSettings settings_;
127 PdfToEmfConverter::ResultCallback callback_;
128 base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
130 DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient);
133 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient(
134 const printing::PdfRenderSettings& settings)
135 : settings_(settings) {}
137 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() {
138 // Delete temp directory.
139 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, files_.release());
142 void PdfToEmfUtilityProcessHostClient::Convert(
143 base::RefCountedMemory* data,
144 const PdfToEmfConverter::ResultCallback& callback) {
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
146 callback_ = callback;
147 CHECK(!files_);
148 files_.reset(new FileHandlers());
149 BrowserThread::PostTaskAndReply(
150 BrowserThread::FILE,
151 FROM_HERE,
152 base::Bind(&FileHandlers::Init,
153 base::Unretained(files_.get()),
154 make_scoped_refptr(data)),
155 base::Bind(&PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread,
156 this));
159 void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
160 OnFailed();
163 bool PdfToEmfUtilityProcessHostClient::OnMessageReceived(
164 const IPC::Message& message) {
165 bool handled = true;
166 IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message)
167 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
168 IPC_MESSAGE_HANDLER(
169 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded, OnSucceeded)
170 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed,
171 OnFailed)
172 IPC_MESSAGE_UNHANDLED(handled = false)
173 IPC_END_MESSAGE_MAP()
174 return handled;
177 void PdfToEmfUtilityProcessHostClient::OnProcessStarted() {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
179 if (!utility_process_host_) {
180 RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0);
181 return;
184 base::ProcessHandle process = utility_process_host_->GetData().handle;
185 utility_process_host_->Send(
186 new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
187 files_->GetPdfForProcess(process),
188 files_->GetEmfPath(),
189 settings_,
190 std::vector<printing::PageRange>()));
191 utility_process_host_.reset();
194 void PdfToEmfUtilityProcessHostClient::OnSucceeded(
195 const std::vector<printing::PageRange>& page_ranges,
196 double scale_factor) {
197 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
198 RunCallback(page_ranges, scale_factor);
201 void PdfToEmfUtilityProcessHostClient::OnFailed() {
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
203 RunCallback(std::vector<printing::PageRange>(), 0.0);
206 void PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread() {
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
208 if (!files_->IsValid()) {
209 RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0);
210 return;
212 BrowserThread::PostTask(
213 BrowserThread::IO,
214 FROM_HERE,
215 base::Bind(&PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread,
216 this));
219 void PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread() {
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
221 utility_process_host_ =
222 content::UtilityProcessHost::Create(
223 this,
224 base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
225 // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load
226 // gdiplus.dll, change how rendering happens, and not be able to correctly
227 // generate when sent to a metafile DC.
228 utility_process_host_->SetExposedDir(files_->GetBasePath());
229 utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
232 void PdfToEmfUtilityProcessHostClient::RunCallback(
233 const std::vector<printing::PageRange>& page_ranges,
234 double scale_factor) {
235 BrowserThread::PostTask(
236 BrowserThread::UI,
237 FROM_HERE,
238 base::Bind(&PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread,
239 this,
240 page_ranges,
241 scale_factor));
244 void PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread(
245 const std::vector<printing::PageRange>& page_ranges,
246 double scale_factor) {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
248 std::vector<base::FilePath> page_filenames;
249 std::vector<printing::PageRange>::const_iterator iter;
250 for (iter = page_ranges.begin(); iter != page_ranges.end(); ++iter) {
251 for (int page_number = iter->from; page_number <= iter->to; ++page_number) {
252 page_filenames.push_back(files_->GetEmfPath().InsertBeforeExtensionASCII(
253 base::StringPrintf(".%d", page_number)));
256 if (!callback_.is_null()) {
257 BrowserThread::PostTask(
258 BrowserThread::UI,
259 FROM_HERE,
260 base::Bind(callback_, scale_factor, page_filenames));
261 callback_.Reset();
265 class PdfToEmfConverterImpl : public PdfToEmfConverter {
266 public:
267 PdfToEmfConverterImpl();
269 virtual ~PdfToEmfConverterImpl();
271 virtual void Start(base::RefCountedMemory* data,
272 const printing::PdfRenderSettings& conversion_settings,
273 const ResultCallback& callback) OVERRIDE;
275 private:
276 scoped_refptr<PdfToEmfUtilityProcessHostClient> utility_client_;
277 base::CancelableCallback<ResultCallback::RunType> callback_;
279 DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl);
282 PdfToEmfConverterImpl::PdfToEmfConverterImpl() {
285 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() {
288 void PdfToEmfConverterImpl::Start(
289 base::RefCountedMemory* data,
290 const printing::PdfRenderSettings& conversion_settings,
291 const ResultCallback& callback) {
292 // Rebind cancelable callback to avoid calling callback if
293 // PdfToEmfConverterImpl is destroyed.
294 callback_.Reset(callback);
295 utility_client_ = new PdfToEmfUtilityProcessHostClient(conversion_settings);
296 utility_client_->Convert(data, callback_.callback());
299 } // namespace
301 // static
302 scoped_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() {
303 return scoped_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl());
306 } // namespace printing