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::GenerateMHTML( 40 WebContents* web_contents, 41 const base::FilePath& file, 42 const GenerateMHTMLCallback& callback) { 43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 44 static int id_counter = 0; 45 46 int job_id = id_counter++; 47 Job job; 48 job.file_path = file; 49 job.process_id = web_contents->GetRenderProcessHost()->GetID(); 50 job.routing_id = web_contents->GetRenderViewHost()->GetRoutingID(); 51 job.callback = callback; 52 id_to_job_[job_id] = job; 53 if (!registrar_.IsRegistered( 54 this, 55 NOTIFICATION_RENDERER_PROCESS_TERMINATED, 56 Source<RenderProcessHost>(web_contents->GetRenderProcessHost()))) { 57 registrar_.Add( 58 this, 59 NOTIFICATION_RENDERER_PROCESS_TERMINATED, 60 Source<RenderProcessHost>(web_contents->GetRenderProcessHost())); 61 } 62 63 base::ProcessHandle renderer_process = 64 web_contents->GetRenderProcessHost()->GetHandle(); 65 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 66 base::Bind(&MHTMLGenerationManager::CreateFile, base::Unretained(this), 67 job_id, file, renderer_process)); 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(BrowserThread::UI, FROM_HERE, 90 base::Bind(&MHTMLGenerationManager::FileCreated, base::Unretained(this), 91 job_id, browser_file, renderer_file)); 92 } 93 94 void MHTMLGenerationManager::FileCreated(int job_id, 95 base::PlatformFile browser_file, 96 IPC::PlatformFileForTransit renderer_file) { 97 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 98 if (browser_file == base::kInvalidPlatformFileValue) { 99 LOG(ERROR) << "Failed to create file"; 100 JobFinished(job_id, -1); 101 return; 102 } 103 104 IDToJobMap::iterator iter = id_to_job_.find(job_id); 105 if (iter == id_to_job_.end()) { 106 NOTREACHED(); 107 return; 108 } 109 110 Job& job = iter->second; 111 job.browser_file = browser_file; 112 job.renderer_file = renderer_file; 113 114 RenderViewHostImpl* rvh = RenderViewHostImpl::FromID( 115 job.process_id, job.routing_id); 116 if (!rvh) { 117 // The contents went away. 118 JobFinished(job_id, -1); 119 return; 120 } 121 122 rvh->Send(new ViewMsg_SavePageAsMHTML(rvh->GetRoutingID(), job_id, 123 renderer_file)); 124 } 125 126 void MHTMLGenerationManager::JobFinished(int job_id, int64 file_size) { 127 IDToJobMap::iterator iter = id_to_job_.find(job_id); 128 if (iter == id_to_job_.end()) { 129 NOTREACHED(); 130 return; 131 } 132 133 Job& job = iter->second; 134 job.callback.Run(job.file_path, file_size); 135 136 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 137 base::Bind(&MHTMLGenerationManager::CloseFile, base::Unretained(this), 138 job.browser_file)); 139 140 id_to_job_.erase(job_id); 141 } 142 143 void MHTMLGenerationManager::CloseFile(base::PlatformFile file) { 144 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 145 base::ClosePlatformFile(file); 146 } 147 148 void MHTMLGenerationManager::Observe(int type, 149 const NotificationSource& source, 150 const NotificationDetails& details) { 151 DCHECK(type == NOTIFICATION_RENDERER_PROCESS_TERMINATED); 152 RenderProcessHost* host = Source<RenderProcessHost>(source).ptr(); 153 registrar_.Remove( 154 this, 155 NOTIFICATION_RENDERER_PROCESS_TERMINATED, 156 source); 157 std::set<int> job_to_delete; 158 for (IDToJobMap::iterator it = id_to_job_.begin(); it != id_to_job_.end(); 159 ++it) { 160 if (it->second.process_id == host->GetID()) 161 job_to_delete.insert(it->first); 162 } 163 for (std::set<int>::iterator it = job_to_delete.begin(); 164 it != job_to_delete.end(); 165 ++it) { 166 JobFinished(*it, -1); 167 } 168 } 169 170 } // namespace content 171