Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / download / mhtml_generation_manager.cc
blob4ace9875816bf5a4bad847e9b399b5e3074ca341
1 // Copyright (c) 2012 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 "content/browser/download/mhtml_generation_manager.h"
7 #include "base/bind.h"
8 #include "base/files/file.h"
9 #include "base/stl_util.h"
10 #include "content/browser/renderer_host/render_view_host_impl.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/render_process_host.h"
13 #include "content/public/browser/render_process_host_observer.h"
14 #include "content/public/browser/web_contents.h"
15 #include "content/common/view_messages.h"
17 namespace content {
19 class MHTMLGenerationManager::Job : public RenderProcessHostObserver {
20 public:
21 Job();
22 ~Job() override;
24 void SetWebContents(WebContents* web_contents);
26 base::File browser_file() { return browser_file_.Pass(); }
27 void set_browser_file(base::File file) { browser_file_ = file.Pass(); }
29 int process_id() { return process_id_; }
30 int routing_id() { return routing_id_; }
32 GenerateMHTMLCallback callback() { return callback_; }
33 void set_callback(GenerateMHTMLCallback callback) { callback_ = callback; }
35 // RenderProcessHostObserver:
36 void RenderProcessExited(RenderProcessHost* host,
37 base::TerminationStatus status,
38 int exit_code) override;
39 void RenderProcessHostDestroyed(RenderProcessHost* host) override;
41 private:
42 // The handle to the file the MHTML is saved to for the browser process.
43 base::File browser_file_;
45 // The IDs mapping to a specific contents.
46 int process_id_;
47 int routing_id_;
49 // The callback to call once generation is complete.
50 GenerateMHTMLCallback callback_;
52 // The RenderProcessHost being observed, or NULL if none is.
53 RenderProcessHost* host_;
54 DISALLOW_COPY_AND_ASSIGN(Job);
57 MHTMLGenerationManager::Job::Job()
58 : process_id_(-1),
59 routing_id_(-1),
60 host_(NULL) {
63 MHTMLGenerationManager::Job::~Job() {
64 if (host_)
65 host_->RemoveObserver(this);
68 void MHTMLGenerationManager::Job::SetWebContents(WebContents* web_contents) {
69 process_id_ = web_contents->GetRenderProcessHost()->GetID();
70 routing_id_ = web_contents->GetRenderViewHost()->GetRoutingID();
71 host_ = web_contents->GetRenderProcessHost();
72 host_->AddObserver(this);
75 void MHTMLGenerationManager::Job::RenderProcessExited(
76 RenderProcessHost* host,
77 base::TerminationStatus status,
78 int exit_code) {
79 MHTMLGenerationManager::GetInstance()->RenderProcessExited(this);
82 void MHTMLGenerationManager::Job::RenderProcessHostDestroyed(
83 RenderProcessHost* host) {
84 host_ = NULL;
87 MHTMLGenerationManager* MHTMLGenerationManager::GetInstance() {
88 return Singleton<MHTMLGenerationManager>::get();
91 MHTMLGenerationManager::MHTMLGenerationManager() {
94 MHTMLGenerationManager::~MHTMLGenerationManager() {
95 STLDeleteValues(&id_to_job_);
98 void MHTMLGenerationManager::SaveMHTML(WebContents* web_contents,
99 const base::FilePath& file,
100 const GenerateMHTMLCallback& callback) {
101 DCHECK_CURRENTLY_ON(BrowserThread::UI);
103 int job_id = NewJob(web_contents, callback);
105 base::ProcessHandle renderer_process =
106 web_contents->GetRenderProcessHost()->GetHandle();
107 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
108 base::Bind(&MHTMLGenerationManager::CreateFile, base::Unretained(this),
109 job_id, file, renderer_process));
112 void MHTMLGenerationManager::StreamMHTML(
113 WebContents* web_contents,
114 base::File browser_file,
115 const GenerateMHTMLCallback& callback) {
116 DCHECK_CURRENTLY_ON(BrowserThread::UI);
118 int job_id = NewJob(web_contents, callback);
120 base::ProcessHandle renderer_process =
121 web_contents->GetRenderProcessHost()->GetHandle();
122 IPC::PlatformFileForTransit renderer_file =
123 IPC::GetFileHandleForProcess(browser_file.GetPlatformFile(),
124 renderer_process, false);
126 FileAvailable(job_id, browser_file.Pass(), renderer_file);
130 void MHTMLGenerationManager::MHTMLGenerated(int job_id, int64 mhtml_data_size) {
131 JobFinished(job_id, mhtml_data_size);
134 void MHTMLGenerationManager::CreateFile(
135 int job_id, const base::FilePath& file_path,
136 base::ProcessHandle renderer_process) {
137 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
138 base::File browser_file(
139 file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
140 if (!browser_file.IsValid()) {
141 LOG(ERROR) << "Failed to create file to save MHTML at: " <<
142 file_path.value();
145 IPC::PlatformFileForTransit renderer_file =
146 IPC::GetFileHandleForProcess(browser_file.GetPlatformFile(),
147 renderer_process, false);
149 BrowserThread::PostTask(
150 BrowserThread::UI,
151 FROM_HERE,
152 base::Bind(&MHTMLGenerationManager::FileAvailable,
153 base::Unretained(this),
154 job_id,
155 base::Passed(&browser_file),
156 renderer_file));
159 void MHTMLGenerationManager::FileAvailable(
160 int job_id,
161 base::File browser_file,
162 IPC::PlatformFileForTransit renderer_file) {
163 DCHECK_CURRENTLY_ON(BrowserThread::UI);
164 if (!browser_file.IsValid()) {
165 LOG(ERROR) << "Failed to create file";
166 JobFinished(job_id, -1);
167 return;
170 IDToJobMap::iterator iter = id_to_job_.find(job_id);
171 if (iter == id_to_job_.end()) {
172 NOTREACHED();
173 return;
176 Job* job = iter->second;
177 job->set_browser_file(browser_file.Pass());
179 RenderViewHost* rvh = RenderViewHost::FromID(
180 job->process_id(), job->routing_id());
181 if (!rvh) {
182 // The contents went away.
183 JobFinished(job_id, -1);
184 return;
187 rvh->Send(new ViewMsg_SavePageAsMHTML(rvh->GetRoutingID(), job_id,
188 renderer_file));
191 void MHTMLGenerationManager::JobFinished(int job_id, int64 file_size) {
192 DCHECK_CURRENTLY_ON(BrowserThread::UI);
193 IDToJobMap::iterator iter = id_to_job_.find(job_id);
194 if (iter == id_to_job_.end()) {
195 NOTREACHED();
196 return;
199 Job* job = iter->second;
200 job->callback().Run(file_size);
202 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
203 base::Bind(&MHTMLGenerationManager::CloseFile, base::Unretained(this),
204 base::Passed(job->browser_file())));
206 id_to_job_.erase(job_id);
207 delete job;
210 void MHTMLGenerationManager::CloseFile(base::File file) {
211 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
212 file.Close();
215 int MHTMLGenerationManager::NewJob(WebContents* web_contents,
216 const GenerateMHTMLCallback& callback) {
217 static int id_counter = 0;
218 int job_id = id_counter++;
219 Job* job = new Job();
220 id_to_job_[job_id] = job;
221 job->SetWebContents(web_contents);
222 job->set_callback(callback);
223 return job_id;
226 void MHTMLGenerationManager::RenderProcessExited(Job* job) {
227 DCHECK_CURRENTLY_ON(BrowserThread::UI);
228 for (IDToJobMap::iterator it = id_to_job_.begin(); it != id_to_job_.end();
229 ++it) {
230 if (it->second == job) {
231 JobFinished(it->first, -1);
232 return;
235 NOTREACHED();
238 } // namespace content