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 virtual void RenderProcessExited(RenderProcessHost
* host
,
37 base::ProcessHandle handle
,
38 base::TerminationStatus status
,
39 int exit_code
) OVERRIDE
;
40 virtual void RenderProcessHostDestroyed(RenderProcessHost
* host
) OVERRIDE
;
44 // The handle to the file the MHTML is saved to for the browser process.
45 base::File browser_file_
;
47 // The IDs mapping to a specific contents.
51 // The callback to call once generation is complete.
52 GenerateMHTMLCallback callback_
;
54 // The RenderProcessHost being observed, or NULL if none is.
55 RenderProcessHost
* host_
;
56 DISALLOW_COPY_AND_ASSIGN(Job
);
59 MHTMLGenerationManager::Job::Job()
65 MHTMLGenerationManager::Job::~Job() {
67 host_
->RemoveObserver(this);
70 void MHTMLGenerationManager::Job::SetWebContents(WebContents
* web_contents
) {
71 process_id_
= web_contents
->GetRenderProcessHost()->GetID();
72 routing_id_
= web_contents
->GetRenderViewHost()->GetRoutingID();
73 host_
= web_contents
->GetRenderProcessHost();
74 host_
->AddObserver(this);
77 void MHTMLGenerationManager::Job::RenderProcessExited(
78 RenderProcessHost
* host
,
79 base::ProcessHandle handle
,
80 base::TerminationStatus status
,
82 MHTMLGenerationManager::GetInstance()->RenderProcessExited(this);
85 void MHTMLGenerationManager::Job::RenderProcessHostDestroyed(
86 RenderProcessHost
* host
) {
90 MHTMLGenerationManager
* MHTMLGenerationManager::GetInstance() {
91 return Singleton
<MHTMLGenerationManager
>::get();
94 MHTMLGenerationManager::MHTMLGenerationManager() {
97 MHTMLGenerationManager::~MHTMLGenerationManager() {
98 STLDeleteValues(&id_to_job_
);
101 void MHTMLGenerationManager::SaveMHTML(WebContents
* web_contents
,
102 const base::FilePath
& file
,
103 const GenerateMHTMLCallback
& callback
) {
104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
106 int job_id
= NewJob(web_contents
, callback
);
108 base::ProcessHandle renderer_process
=
109 web_contents
->GetRenderProcessHost()->GetHandle();
110 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
,
111 base::Bind(&MHTMLGenerationManager::CreateFile
, base::Unretained(this),
112 job_id
, file
, renderer_process
));
115 void MHTMLGenerationManager::StreamMHTML(
116 WebContents
* web_contents
,
117 base::File browser_file
,
118 const GenerateMHTMLCallback
& callback
) {
119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
121 int job_id
= NewJob(web_contents
, callback
);
123 base::ProcessHandle renderer_process
=
124 web_contents
->GetRenderProcessHost()->GetHandle();
125 IPC::PlatformFileForTransit renderer_file
=
126 IPC::GetFileHandleForProcess(browser_file
.GetPlatformFile(),
127 renderer_process
, false);
129 FileAvailable(job_id
, browser_file
.Pass(), renderer_file
);
133 void MHTMLGenerationManager::MHTMLGenerated(int job_id
, int64 mhtml_data_size
) {
134 JobFinished(job_id
, mhtml_data_size
);
137 void MHTMLGenerationManager::CreateFile(
138 int job_id
, const base::FilePath
& file_path
,
139 base::ProcessHandle renderer_process
) {
140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
141 base::File
browser_file(
142 file_path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
);
143 if (!browser_file
.IsValid()) {
144 LOG(ERROR
) << "Failed to create file to save MHTML at: " <<
148 IPC::PlatformFileForTransit renderer_file
=
149 IPC::GetFileHandleForProcess(browser_file
.GetPlatformFile(),
150 renderer_process
, false);
152 BrowserThread::PostTask(
155 base::Bind(&MHTMLGenerationManager::FileAvailable
,
156 base::Unretained(this),
158 base::Passed(&browser_file
),
162 void MHTMLGenerationManager::FileAvailable(
164 base::File browser_file
,
165 IPC::PlatformFileForTransit renderer_file
) {
166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
167 if (!browser_file
.IsValid()) {
168 LOG(ERROR
) << "Failed to create file";
169 JobFinished(job_id
, -1);
173 IDToJobMap::iterator iter
= id_to_job_
.find(job_id
);
174 if (iter
== id_to_job_
.end()) {
179 Job
* job
= iter
->second
;
180 job
->set_browser_file(browser_file
.Pass());
182 RenderViewHost
* rvh
= RenderViewHost::FromID(
183 job
->process_id(), job
->routing_id());
185 // The contents went away.
186 JobFinished(job_id
, -1);
190 rvh
->Send(new ViewMsg_SavePageAsMHTML(rvh
->GetRoutingID(), job_id
,
194 void MHTMLGenerationManager::JobFinished(int job_id
, int64 file_size
) {
195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
196 IDToJobMap::iterator iter
= id_to_job_
.find(job_id
);
197 if (iter
== id_to_job_
.end()) {
202 Job
* job
= iter
->second
;
203 job
->callback().Run(file_size
);
205 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
,
206 base::Bind(&MHTMLGenerationManager::CloseFile
, base::Unretained(this),
207 base::Passed(job
->browser_file())));
209 id_to_job_
.erase(job_id
);
213 void MHTMLGenerationManager::CloseFile(base::File file
) {
214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
218 int MHTMLGenerationManager::NewJob(WebContents
* web_contents
,
219 const GenerateMHTMLCallback
& callback
) {
220 static int id_counter
= 0;
221 int job_id
= id_counter
++;
222 Job
* job
= new Job();
223 id_to_job_
[job_id
] = job
;
224 job
->SetWebContents(web_contents
);
225 job
->set_callback(callback
);
229 void MHTMLGenerationManager::RenderProcessExited(Job
* job
) {
230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
231 for (IDToJobMap::iterator it
= id_to_job_
.begin(); it
!= id_to_job_
.end();
233 if (it
->second
== job
) {
234 JobFinished(it
->first
, -1);
241 } // namespace content