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/platform_file.h"
9 #include "content/browser/renderer_host/render_view_host_impl.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "content/public/browser/render_process_host.h"
12 #include "content/public/browser/render_process_host_observer.h"
13 #include "content/public/browser/web_contents.h"
14 #include "content/common/view_messages.h"
18 class MHTMLGenerationManager::Job
: public RenderProcessHostObserver
{
23 void SetWebContents(WebContents
* web_contents
);
25 base::PlatformFile
browser_file() { return browser_file_
; }
26 void set_browser_file(base::PlatformFile file
) { browser_file_
= file
; }
28 int process_id() { return process_id_
; }
29 int routing_id() { return routing_id_
; }
31 GenerateMHTMLCallback
callback() { return callback_
; }
32 void set_callback(GenerateMHTMLCallback callback
) { callback_
= callback
; }
34 // RenderProcessHostObserver:
35 virtual void RenderProcessExited(RenderProcessHost
* host
,
36 base::ProcessHandle handle
,
37 base::TerminationStatus status
,
38 int exit_code
) OVERRIDE
;
39 virtual void RenderProcessHostDestroyed(RenderProcessHost
* host
) OVERRIDE
;
43 // The handle to the file the MHTML is saved to for the browser process.
44 base::PlatformFile browser_file_
;
46 // The IDs mapping to a specific contents.
50 // The callback to call once generation is complete.
51 GenerateMHTMLCallback callback_
;
53 // The RenderProcessHost being observed, or NULL if none is.
54 RenderProcessHost
* host_
;
57 MHTMLGenerationManager::Job::Job()
58 : browser_file_(base::kInvalidPlatformFileValue
),
64 MHTMLGenerationManager::Job::~Job() {
66 host_
->RemoveObserver(this);
69 void MHTMLGenerationManager::Job::SetWebContents(WebContents
* web_contents
) {
70 process_id_
= web_contents
->GetRenderProcessHost()->GetID();
71 routing_id_
= web_contents
->GetRenderViewHost()->GetRoutingID();
72 host_
= web_contents
->GetRenderProcessHost();
73 host_
->AddObserver(this);
76 void MHTMLGenerationManager::Job::RenderProcessExited(
77 RenderProcessHost
* host
,
78 base::ProcessHandle handle
,
79 base::TerminationStatus status
,
81 MHTMLGenerationManager::GetInstance()->RenderProcessExited(this);
84 void MHTMLGenerationManager::Job::RenderProcessHostDestroyed(
85 RenderProcessHost
* host
) {
89 MHTMLGenerationManager
* MHTMLGenerationManager::GetInstance() {
90 return Singleton
<MHTMLGenerationManager
>::get();
93 MHTMLGenerationManager::MHTMLGenerationManager() {
96 MHTMLGenerationManager::~MHTMLGenerationManager() {
99 void MHTMLGenerationManager::SaveMHTML(WebContents
* web_contents
,
100 const base::FilePath
& file
,
101 const GenerateMHTMLCallback
& callback
) {
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
104 int job_id
= NewJob(web_contents
, callback
);
106 base::ProcessHandle renderer_process
=
107 web_contents
->GetRenderProcessHost()->GetHandle();
108 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
,
109 base::Bind(&MHTMLGenerationManager::CreateFile
, base::Unretained(this),
110 job_id
, file
, renderer_process
));
113 void MHTMLGenerationManager::StreamMHTML(
114 WebContents
* web_contents
,
115 const base::PlatformFile browser_file
,
116 const GenerateMHTMLCallback
& callback
) {
117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
119 int job_id
= NewJob(web_contents
, callback
);
121 base::ProcessHandle renderer_process
=
122 web_contents
->GetRenderProcessHost()->GetHandle();
123 IPC::PlatformFileForTransit renderer_file
=
124 IPC::GetFileHandleForProcess(browser_file
, renderer_process
, false);
126 FileHandleAvailable(job_id
, browser_file
, 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(BrowserThread::CurrentlyOn(BrowserThread::FILE));
138 base::PlatformFile browser_file
= base::CreatePlatformFile(file_path
,
139 base::PLATFORM_FILE_CREATE_ALWAYS
| base::PLATFORM_FILE_WRITE
,
141 if (browser_file
== base::kInvalidPlatformFileValue
) {
142 LOG(ERROR
) << "Failed to create file to save MHTML at: " <<
146 IPC::PlatformFileForTransit renderer_file
=
147 IPC::GetFileHandleForProcess(browser_file
, renderer_process
, false);
149 BrowserThread::PostTask(
152 base::Bind(&MHTMLGenerationManager::FileHandleAvailable
,
153 base::Unretained(this),
159 void MHTMLGenerationManager::FileHandleAvailable(
161 base::PlatformFile browser_file
,
162 IPC::PlatformFileForTransit renderer_file
) {
163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
164 if (browser_file
== base::kInvalidPlatformFileValue
) {
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
);
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(BrowserThread::CurrentlyOn(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 job
.browser_file()));
206 id_to_job_
.erase(job_id
);
209 void MHTMLGenerationManager::CloseFile(base::PlatformFile file
) {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
211 base::ClosePlatformFile(file
);
214 int MHTMLGenerationManager::NewJob(WebContents
* web_contents
,
215 const GenerateMHTMLCallback
& callback
) {
216 static int id_counter
= 0;
217 int job_id
= id_counter
++;
218 Job
& job
= id_to_job_
[job_id
];
219 job
.SetWebContents(web_contents
);
220 job
.set_callback(callback
);
224 void MHTMLGenerationManager::RenderProcessExited(Job
* job
) {
225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
226 for (IDToJobMap::iterator it
= id_to_job_
.begin(); it
!= id_to_job_
.end();
228 if (&it
->second
== job
) {
229 JobFinished(it
->first
, -1);
236 } // namespace content