• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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"
16 
17 namespace content {
18 
19 class MHTMLGenerationManager::Job : public RenderProcessHostObserver {
20  public:
21   Job();
22   virtual ~Job();
23 
24   void SetWebContents(WebContents* web_contents);
25 
browser_file()26   base::File browser_file() { return browser_file_.Pass(); }
set_browser_file(base::File file)27   void set_browser_file(base::File file) { browser_file_ = file.Pass(); }
28 
process_id()29   int process_id() { return process_id_; }
routing_id()30   int routing_id() { return routing_id_; }
31 
callback()32   GenerateMHTMLCallback callback() { return callback_; }
set_callback(GenerateMHTMLCallback callback)33   void set_callback(GenerateMHTMLCallback callback) { callback_ = callback; }
34 
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;
41 
42 
43  private:
44   // The handle to the file the MHTML is saved to for the browser process.
45   base::File browser_file_;
46 
47   // The IDs mapping to a specific contents.
48   int process_id_;
49   int routing_id_;
50 
51   // The callback to call once generation is complete.
52   GenerateMHTMLCallback callback_;
53 
54   // The RenderProcessHost being observed, or NULL if none is.
55   RenderProcessHost* host_;
56   DISALLOW_COPY_AND_ASSIGN(Job);
57 };
58 
Job()59 MHTMLGenerationManager::Job::Job()
60     : process_id_(-1),
61       routing_id_(-1),
62       host_(NULL) {
63 }
64 
~Job()65 MHTMLGenerationManager::Job::~Job() {
66   if (host_)
67     host_->RemoveObserver(this);
68 }
69 
SetWebContents(WebContents * web_contents)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);
75 }
76 
RenderProcessExited(RenderProcessHost * host,base::ProcessHandle handle,base::TerminationStatus status,int exit_code)77 void MHTMLGenerationManager::Job::RenderProcessExited(
78     RenderProcessHost* host,
79     base::ProcessHandle handle,
80     base::TerminationStatus status,
81     int exit_code) {
82   MHTMLGenerationManager::GetInstance()->RenderProcessExited(this);
83 }
84 
RenderProcessHostDestroyed(RenderProcessHost * host)85 void MHTMLGenerationManager::Job::RenderProcessHostDestroyed(
86     RenderProcessHost* host) {
87   host_ = NULL;
88 }
89 
GetInstance()90 MHTMLGenerationManager* MHTMLGenerationManager::GetInstance() {
91   return Singleton<MHTMLGenerationManager>::get();
92 }
93 
MHTMLGenerationManager()94 MHTMLGenerationManager::MHTMLGenerationManager() {
95 }
96 
~MHTMLGenerationManager()97 MHTMLGenerationManager::~MHTMLGenerationManager() {
98   STLDeleteValues(&id_to_job_);
99 }
100 
SaveMHTML(WebContents * web_contents,const base::FilePath & file,const GenerateMHTMLCallback & callback)101 void MHTMLGenerationManager::SaveMHTML(WebContents* web_contents,
102                                        const base::FilePath& file,
103                                        const GenerateMHTMLCallback& callback) {
104   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
105 
106   int job_id = NewJob(web_contents, callback);
107 
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));
113 }
114 
StreamMHTML(WebContents * web_contents,base::File browser_file,const GenerateMHTMLCallback & callback)115 void MHTMLGenerationManager::StreamMHTML(
116     WebContents* web_contents,
117     base::File browser_file,
118     const GenerateMHTMLCallback& callback) {
119   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
120 
121   int job_id = NewJob(web_contents, callback);
122 
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);
128 
129   FileAvailable(job_id, browser_file.Pass(), renderer_file);
130 }
131 
132 
MHTMLGenerated(int job_id,int64 mhtml_data_size)133 void MHTMLGenerationManager::MHTMLGenerated(int job_id, int64 mhtml_data_size) {
134   JobFinished(job_id, mhtml_data_size);
135 }
136 
CreateFile(int job_id,const base::FilePath & file_path,base::ProcessHandle renderer_process)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: " <<
145         file_path.value();
146   }
147 
148   IPC::PlatformFileForTransit renderer_file =
149       IPC::GetFileHandleForProcess(browser_file.GetPlatformFile(),
150                                    renderer_process, false);
151 
152   BrowserThread::PostTask(
153       BrowserThread::UI,
154       FROM_HERE,
155       base::Bind(&MHTMLGenerationManager::FileAvailable,
156                  base::Unretained(this),
157                  job_id,
158                  base::Passed(&browser_file),
159                  renderer_file));
160 }
161 
FileAvailable(int job_id,base::File browser_file,IPC::PlatformFileForTransit renderer_file)162 void MHTMLGenerationManager::FileAvailable(
163     int job_id,
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);
170     return;
171   }
172 
173   IDToJobMap::iterator iter = id_to_job_.find(job_id);
174   if (iter == id_to_job_.end()) {
175     NOTREACHED();
176     return;
177   }
178 
179   Job* job = iter->second;
180   job->set_browser_file(browser_file.Pass());
181 
182   RenderViewHost* rvh = RenderViewHost::FromID(
183       job->process_id(), job->routing_id());
184   if (!rvh) {
185     // The contents went away.
186     JobFinished(job_id, -1);
187     return;
188   }
189 
190   rvh->Send(new ViewMsg_SavePageAsMHTML(rvh->GetRoutingID(), job_id,
191                                         renderer_file));
192 }
193 
JobFinished(int job_id,int64 file_size)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()) {
198     NOTREACHED();
199     return;
200   }
201 
202   Job* job = iter->second;
203   job->callback().Run(file_size);
204 
205   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
206       base::Bind(&MHTMLGenerationManager::CloseFile, base::Unretained(this),
207                  base::Passed(job->browser_file())));
208 
209   id_to_job_.erase(job_id);
210   delete job;
211 }
212 
CloseFile(base::File file)213 void MHTMLGenerationManager::CloseFile(base::File file) {
214   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
215   file.Close();
216 }
217 
NewJob(WebContents * web_contents,const GenerateMHTMLCallback & callback)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);
226   return job_id;
227 }
228 
RenderProcessExited(Job * job)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();
232        ++it) {
233     if (it->second == job) {
234       JobFinished(it->first, -1);
235       return;
236     }
237   }
238   NOTREACHED();
239 }
240 
241 }  // namespace content
242