• 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/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