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 19 MHTMLGenerationManager::Job::Job() 20 : browser_file(base::kInvalidPlatformFileValue), 21 renderer_file(IPC::InvalidPlatformFileForTransit()), 22 process_id(-1), 23 routing_id(-1) { 24 } 25 26 MHTMLGenerationManager::Job::~Job() { 27 } 28 29 MHTMLGenerationManager* MHTMLGenerationManager::GetInstance() { 30 return Singleton<MHTMLGenerationManager>::get(); 31 } 32 33 MHTMLGenerationManager::MHTMLGenerationManager() { 34 } 35 36 MHTMLGenerationManager::~MHTMLGenerationManager() { 37 } 38 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 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 70 void MHTMLGenerationManager::MHTMLGenerated(int job_id, int64 mhtml_data_size) { 71 JobFinished(job_id, mhtml_data_size); 72 } 73 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 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 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 148 void MHTMLGenerationManager::CloseFile(base::PlatformFile file) { 149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 150 base::ClosePlatformFile(file); 151 } 152 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 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