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.
4
5 #include "content/browser/download/mhtml_generation_manager.h"
6
7 #include "base/bind.h"
8 #include "base/platform_file.h"
9 #include "content/browser/renderer_host/render_process_host_impl.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/notification_service.h"
13 #include "content/public/browser/web_contents.h"
14 #include "content/common/view_messages.h"
15 #include "content/public/browser/notification_types.h"
16
17 namespace content {
18
Job()19 MHTMLGenerationManager::Job::Job()
20 : browser_file(base::kInvalidPlatformFileValue),
21 renderer_file(IPC::InvalidPlatformFileForTransit()),
22 process_id(-1),
23 routing_id(-1) {
24 }
25
~Job()26 MHTMLGenerationManager::Job::~Job() {
27 }
28
GetInstance()29 MHTMLGenerationManager* MHTMLGenerationManager::GetInstance() {
30 return Singleton<MHTMLGenerationManager>::get();
31 }
32
MHTMLGenerationManager()33 MHTMLGenerationManager::MHTMLGenerationManager() {
34 }
35
~MHTMLGenerationManager()36 MHTMLGenerationManager::~MHTMLGenerationManager() {
37 }
38
SaveMHTML(WebContents * web_contents,const base::FilePath & file,const GenerateMHTMLCallback & callback)39 void MHTMLGenerationManager::SaveMHTML(WebContents* web_contents,
40 const base::FilePath& file,
41 const GenerateMHTMLCallback& callback) {
42 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
43
44 int job_id = NewJob(web_contents, callback);
45
46 base::ProcessHandle renderer_process =
47 web_contents->GetRenderProcessHost()->GetHandle();
48 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
49 base::Bind(&MHTMLGenerationManager::CreateFile, base::Unretained(this),
50 job_id, file, renderer_process));
51 }
52
StreamMHTML(WebContents * web_contents,const base::PlatformFile browser_file,const GenerateMHTMLCallback & callback)53 void MHTMLGenerationManager::StreamMHTML(
54 WebContents* web_contents,
55 const base::PlatformFile browser_file,
56 const GenerateMHTMLCallback& callback) {
57 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
58
59 int job_id = NewJob(web_contents, callback);
60
61 base::ProcessHandle renderer_process =
62 web_contents->GetRenderProcessHost()->GetHandle();
63 IPC::PlatformFileForTransit renderer_file =
64 IPC::GetFileHandleForProcess(browser_file, renderer_process, false);
65
66 FileHandleAvailable(job_id, browser_file, renderer_file);
67 }
68
69
MHTMLGenerated(int job_id,int64 mhtml_data_size)70 void MHTMLGenerationManager::MHTMLGenerated(int job_id, int64 mhtml_data_size) {
71 JobFinished(job_id, mhtml_data_size);
72 }
73
CreateFile(int job_id,const base::FilePath & file_path,base::ProcessHandle renderer_process)74 void MHTMLGenerationManager::CreateFile(
75 int job_id, const base::FilePath& file_path,
76 base::ProcessHandle renderer_process) {
77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
78 base::PlatformFile browser_file = base::CreatePlatformFile(file_path,
79 base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE,
80 NULL, NULL);
81 if (browser_file == base::kInvalidPlatformFileValue) {
82 LOG(ERROR) << "Failed to create file to save MHTML at: " <<
83 file_path.value();
84 }
85
86 IPC::PlatformFileForTransit renderer_file =
87 IPC::GetFileHandleForProcess(browser_file, renderer_process, false);
88
89 BrowserThread::PostTask(
90 BrowserThread::UI,
91 FROM_HERE,
92 base::Bind(&MHTMLGenerationManager::FileHandleAvailable,
93 base::Unretained(this),
94 job_id,
95 browser_file,
96 renderer_file));
97 }
98
FileHandleAvailable(int job_id,base::PlatformFile browser_file,IPC::PlatformFileForTransit renderer_file)99 void MHTMLGenerationManager::FileHandleAvailable(int job_id,
100 base::PlatformFile browser_file,
101 IPC::PlatformFileForTransit renderer_file) {
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
103 if (browser_file == base::kInvalidPlatformFileValue) {
104 LOG(ERROR) << "Failed to create file";
105 JobFinished(job_id, -1);
106 return;
107 }
108
109 IDToJobMap::iterator iter = id_to_job_.find(job_id);
110 if (iter == id_to_job_.end()) {
111 NOTREACHED();
112 return;
113 }
114
115 Job& job = iter->second;
116 job.browser_file = browser_file;
117 job.renderer_file = renderer_file;
118
119 RenderViewHostImpl* rvh = RenderViewHostImpl::FromID(
120 job.process_id, job.routing_id);
121 if (!rvh) {
122 // The contents went away.
123 JobFinished(job_id, -1);
124 return;
125 }
126
127 rvh->Send(new ViewMsg_SavePageAsMHTML(rvh->GetRoutingID(), job_id,
128 renderer_file));
129 }
130
JobFinished(int job_id,int64 file_size)131 void MHTMLGenerationManager::JobFinished(int job_id, int64 file_size) {
132 IDToJobMap::iterator iter = id_to_job_.find(job_id);
133 if (iter == id_to_job_.end()) {
134 NOTREACHED();
135 return;
136 }
137
138 Job& job = iter->second;
139 job.callback.Run(file_size);
140
141 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
142 base::Bind(&MHTMLGenerationManager::CloseFile, base::Unretained(this),
143 job.browser_file));
144
145 id_to_job_.erase(job_id);
146 }
147
CloseFile(base::PlatformFile file)148 void MHTMLGenerationManager::CloseFile(base::PlatformFile file) {
149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
150 base::ClosePlatformFile(file);
151 }
152
NewJob(WebContents * web_contents,const GenerateMHTMLCallback & callback)153 int MHTMLGenerationManager::NewJob(WebContents* web_contents,
154 const GenerateMHTMLCallback& callback) {
155 static int id_counter = 0;
156 int job_id = id_counter++;
157 Job& job = id_to_job_[job_id];
158 job.process_id = web_contents->GetRenderProcessHost()->GetID();
159 job.routing_id = web_contents->GetRenderViewHost()->GetRoutingID();
160 job.callback = callback;
161 if (!registrar_.IsRegistered(
162 this,
163 NOTIFICATION_RENDERER_PROCESS_TERMINATED,
164 Source<RenderProcessHost>(web_contents->GetRenderProcessHost()))) {
165 registrar_.Add(
166 this,
167 NOTIFICATION_RENDERER_PROCESS_TERMINATED,
168 Source<RenderProcessHost>(web_contents->GetRenderProcessHost()));
169 }
170 return job_id;
171 }
172
Observe(int type,const NotificationSource & source,const NotificationDetails & details)173 void MHTMLGenerationManager::Observe(int type,
174 const NotificationSource& source,
175 const NotificationDetails& details) {
176 DCHECK(type == NOTIFICATION_RENDERER_PROCESS_TERMINATED);
177 RenderProcessHost* host = Source<RenderProcessHost>(source).ptr();
178 registrar_.Remove(
179 this,
180 NOTIFICATION_RENDERER_PROCESS_TERMINATED,
181 source);
182 std::set<int> job_to_delete;
183 for (IDToJobMap::iterator it = id_to_job_.begin(); it != id_to_job_.end();
184 ++it) {
185 if (it->second.process_id == host->GetID())
186 job_to_delete.insert(it->first);
187 }
188 for (std::set<int>::iterator it = job_to_delete.begin();
189 it != job_to_delete.end();
190 ++it) {
191 JobFinished(*it, -1);
192 }
193 }
194
195 } // namespace content
196