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"
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"
19 class MHTMLGenerationManager::Job
: public RenderProcessHostObserver
{
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
;
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.
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()
63 MHTMLGenerationManager::Job::~Job() {
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
,
79 MHTMLGenerationManager::GetInstance()->RenderProcessExited(this);
82 void MHTMLGenerationManager::Job::RenderProcessHostDestroyed(
83 RenderProcessHost
* host
) {
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: " <<
145 IPC::PlatformFileForTransit renderer_file
=
146 IPC::GetFileHandleForProcess(browser_file
.GetPlatformFile(),
147 renderer_process
, false);
149 BrowserThread::PostTask(
152 base::Bind(&MHTMLGenerationManager::FileAvailable
,
153 base::Unretained(this),
155 base::Passed(&browser_file
),
159 void MHTMLGenerationManager::FileAvailable(
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);
170 IDToJobMap::iterator iter
= id_to_job_
.find(job_id
);
171 if (iter
== id_to_job_
.end()) {
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());
182 // The contents went away.
183 JobFinished(job_id
, -1);
187 rvh
->Send(new ViewMsg_SavePageAsMHTML(rvh
->GetRoutingID(), job_id
,
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()) {
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
);
210 void MHTMLGenerationManager::CloseFile(base::File file
) {
211 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
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
);
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();
230 if (it
->second
== job
) {
231 JobFinished(it
->first
, -1);
238 } // namespace content