1 // Copyright (c) 2011 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 "chrome/browser/download/download_file_manager.h" 6 7 #include "base/file_util.h" 8 #include "base/logging.h" 9 #include "base/stl_util-inl.h" 10 #include "base/task.h" 11 #include "base/utf_string_conversions.h" 12 #include "build/build_config.h" 13 #include "chrome/browser/download/download_manager.h" 14 #include "chrome/browser/download/download_util.h" 15 #include "chrome/browser/history/download_create_info.h" 16 #include "chrome/browser/net/chrome_url_request_context.h" 17 #include "chrome/browser/platform_util.h" 18 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 20 #include "chrome/browser/tab_contents/tab_util.h" 21 #include "content/browser/browser_thread.h" 22 #include "content/browser/renderer_host/resource_dispatcher_host.h" 23 #include "content/browser/tab_contents/tab_contents.h" 24 #include "googleurl/src/gurl.h" 25 #include "net/base/io_buffer.h" 26 27 namespace { 28 29 // Throttle updates to the UI thread so that a fast moving download doesn't 30 // cause it to become unresponsive (in milliseconds). 31 const int kUpdatePeriodMs = 500; 32 33 DownloadManager* DownloadManagerForRenderViewHost(int render_process_id, 34 int render_view_id) { 35 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 36 37 TabContents* contents = tab_util::GetTabContentsByID(render_process_id, 38 render_view_id); 39 if (contents) { 40 Profile* profile = contents->profile(); 41 if (profile) 42 return profile->GetDownloadManager(); 43 } 44 45 return NULL; 46 } 47 48 } // namespace 49 50 DownloadFileManager::DownloadFileManager(ResourceDispatcherHost* rdh) 51 : next_id_(0), 52 resource_dispatcher_host_(rdh) { 53 } 54 55 DownloadFileManager::~DownloadFileManager() { 56 DCHECK(downloads_.empty()); 57 } 58 59 void DownloadFileManager::Shutdown() { 60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 61 BrowserThread::PostTask( 62 BrowserThread::FILE, FROM_HERE, 63 NewRunnableMethod(this, &DownloadFileManager::OnShutdown)); 64 } 65 66 void DownloadFileManager::OnShutdown() { 67 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 68 StopUpdateTimer(); 69 STLDeleteValues(&downloads_); 70 } 71 72 void DownloadFileManager::CreateDownloadFile(DownloadCreateInfo* info, 73 DownloadManager* download_manager, 74 bool get_hash) { 75 VLOG(20) << __FUNCTION__ << "()" << " info = " << info->DebugString(); 76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 77 78 scoped_ptr<DownloadFile> 79 download_file(new DownloadFile(info, download_manager)); 80 if (!download_file->Initialize(get_hash)) { 81 BrowserThread::PostTask( 82 BrowserThread::IO, FROM_HERE, 83 NewRunnableFunction(&download_util::CancelDownloadRequest, 84 resource_dispatcher_host_, 85 info->child_id, 86 info->request_id)); 87 delete info; 88 return; 89 } 90 91 DCHECK(GetDownloadFile(info->download_id) == NULL); 92 downloads_[info->download_id] = download_file.release(); 93 // TODO(phajdan.jr): fix the duplication of path info below. 94 info->path = info->save_info.file_path; 95 96 // The file is now ready, we can un-pause the request and start saving data. 97 BrowserThread::PostTask( 98 BrowserThread::IO, FROM_HERE, 99 NewRunnableMethod(this, &DownloadFileManager::ResumeDownloadRequest, 100 info->child_id, info->request_id)); 101 102 StartUpdateTimer(); 103 104 BrowserThread::PostTask( 105 BrowserThread::UI, FROM_HERE, 106 NewRunnableMethod(download_manager, 107 &DownloadManager::StartDownload, info)); 108 } 109 110 void DownloadFileManager::ResumeDownloadRequest(int child_id, int request_id) { 111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 112 113 // This balances the pause in DownloadResourceHandler::OnResponseStarted. 114 resource_dispatcher_host_->PauseRequest(child_id, request_id, false); 115 } 116 117 DownloadFile* DownloadFileManager::GetDownloadFile(int id) { 118 DownloadFileMap::iterator it = downloads_.find(id); 119 return it == downloads_.end() ? NULL : it->second; 120 } 121 122 void DownloadFileManager::StartUpdateTimer() { 123 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 124 if (!update_timer_.IsRunning()) { 125 update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdatePeriodMs), 126 this, &DownloadFileManager::UpdateInProgressDownloads); 127 } 128 } 129 130 void DownloadFileManager::StopUpdateTimer() { 131 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 132 update_timer_.Stop(); 133 } 134 135 void DownloadFileManager::UpdateInProgressDownloads() { 136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 137 for (DownloadFileMap::iterator i = downloads_.begin(); 138 i != downloads_.end(); ++i) { 139 int id = i->first; 140 DownloadFile* download_file = i->second; 141 DownloadManager* manager = download_file->GetDownloadManager(); 142 if (manager) { 143 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 144 NewRunnableMethod(manager, &DownloadManager::UpdateDownload, 145 id, download_file->bytes_so_far())); 146 } 147 } 148 } 149 150 // Called on the IO thread once the ResourceDispatcherHost has decided that a 151 // request is a download. 152 int DownloadFileManager::GetNextId() { 153 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 154 return next_id_++; 155 } 156 157 void DownloadFileManager::StartDownload(DownloadCreateInfo* info) { 158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 159 DCHECK(info); 160 161 DownloadManager* manager = DownloadManagerForRenderViewHost( 162 info->child_id, info->render_view_id); 163 if (!manager) { 164 BrowserThread::PostTask( 165 BrowserThread::IO, FROM_HERE, 166 NewRunnableFunction(&download_util::CancelDownloadRequest, 167 resource_dispatcher_host_, 168 info->child_id, 169 info->request_id)); 170 delete info; 171 return; 172 } 173 174 manager->CreateDownloadItem(info); 175 176 bool hash_needed = resource_dispatcher_host_->safe_browsing_service()-> 177 DownloadBinHashNeeded(); 178 179 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 180 NewRunnableMethod(this, &DownloadFileManager::CreateDownloadFile, 181 info, make_scoped_refptr(manager), hash_needed)); 182 } 183 184 // We don't forward an update to the UI thread here, since we want to throttle 185 // the UI update rate via a periodic timer. If the user has cancelled the 186 // download (in the UI thread), we may receive a few more updates before the IO 187 // thread gets the cancel message: we just delete the data since the 188 // DownloadFile has been deleted. 189 void DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) { 190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 191 std::vector<DownloadBuffer::Contents> contents; 192 { 193 base::AutoLock auto_lock(buffer->lock); 194 contents.swap(buffer->contents); 195 } 196 197 DownloadFile* download = GetDownloadFile(id); 198 for (size_t i = 0; i < contents.size(); ++i) { 199 net::IOBuffer* data = contents[i].first; 200 const int data_len = contents[i].second; 201 if (download) 202 download->AppendDataToFile(data->data(), data_len); 203 data->Release(); 204 } 205 } 206 207 void DownloadFileManager::OnResponseCompleted( 208 int id, 209 DownloadBuffer* buffer, 210 int os_error, 211 const std::string& security_info) { 212 VLOG(20) << __FUNCTION__ << "()" << " id = " << id 213 << " os_error = " << os_error 214 << " security_info = \"" << security_info << "\""; 215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 216 delete buffer; 217 DownloadFile* download = GetDownloadFile(id); 218 if (!download) 219 return; 220 221 download->Finish(); 222 223 DownloadManager* download_manager = download->GetDownloadManager(); 224 if (!download_manager) { 225 CancelDownload(id); 226 return; 227 } 228 229 std::string hash; 230 if (!download->GetSha256Hash(&hash)) 231 hash.clear(); 232 233 BrowserThread::PostTask( 234 BrowserThread::UI, FROM_HERE, 235 NewRunnableMethod( 236 download_manager, &DownloadManager::OnResponseCompleted, 237 id, download->bytes_so_far(), os_error, hash)); 238 // We need to keep the download around until the UI thread has finalized 239 // the name. 240 } 241 242 // This method will be sent via a user action, or shutdown on the UI thread, and 243 // run on the download thread. Since this message has been sent from the UI 244 // thread, the download may have already completed and won't exist in our map. 245 void DownloadFileManager::CancelDownload(int id) { 246 VLOG(20) << __FUNCTION__ << "()" << " id = " << id; 247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 248 DownloadFileMap::iterator it = downloads_.find(id); 249 if (it == downloads_.end()) 250 return; 251 252 DownloadFile* download = it->second; 253 VLOG(20) << __FUNCTION__ << "()" 254 << " download = " << download->DebugString(); 255 download->Cancel(); 256 257 EraseDownload(id); 258 } 259 260 void DownloadFileManager::CompleteDownload(int id) { 261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 262 263 if (!ContainsKey(downloads_, id)) 264 return; 265 266 DownloadFile* download_file = downloads_[id]; 267 268 VLOG(20) << " " << __FUNCTION__ << "()" 269 << " id = " << id 270 << " download_file = " << download_file->DebugString(); 271 272 download_file->Detach(); 273 274 EraseDownload(id); 275 } 276 277 void DownloadFileManager::OnDownloadManagerShutdown(DownloadManager* manager) { 278 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 279 DCHECK(manager); 280 281 std::set<DownloadFile*> to_remove; 282 283 for (DownloadFileMap::iterator i = downloads_.begin(); 284 i != downloads_.end(); ++i) { 285 DownloadFile* download_file = i->second; 286 if (download_file->GetDownloadManager() == manager) { 287 download_file->CancelDownloadRequest(resource_dispatcher_host_); 288 to_remove.insert(download_file); 289 } 290 } 291 292 for (std::set<DownloadFile*>::iterator i = to_remove.begin(); 293 i != to_remove.end(); ++i) { 294 downloads_.erase((*i)->id()); 295 delete *i; 296 } 297 } 298 299 // Actions from the UI thread and run on the download thread 300 301 // The DownloadManager in the UI thread has provided an intermediate .crdownload 302 // name for the download specified by 'id'. Rename the in progress download. 303 // 304 // There are 2 possible rename cases where this method can be called: 305 // 1. tmp -> foo.crdownload (not final, safe) 306 // 2. tmp-> Unconfirmed.xxx.crdownload (not final, dangerous) 307 void DownloadFileManager::RenameInProgressDownloadFile( 308 int id, const FilePath& full_path) { 309 VLOG(20) << __FUNCTION__ << "()" << " id = " << id 310 << " full_path = \"" << full_path.value() << "\""; 311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 312 313 DownloadFile* download = GetDownloadFile(id); 314 if (!download) 315 return; 316 317 VLOG(20) << __FUNCTION__ << "()" << " download = " << download->DebugString(); 318 319 if (!download->Rename(full_path)) { 320 // Error. Between the time the UI thread generated 'full_path' to the time 321 // this code runs, something happened that prevents us from renaming. 322 CancelDownloadOnRename(id); 323 } 324 } 325 326 // The DownloadManager in the UI thread has provided a final name for the 327 // download specified by 'id'. Rename the download that's in the process 328 // of completing. 329 // 330 // There are 2 possible rename cases where this method can be called: 331 // 1. foo.crdownload -> foo (final, safe) 332 // 2. Unconfirmed.xxx.crdownload -> xxx (final, validated) 333 void DownloadFileManager::RenameCompletingDownloadFile( 334 int id, const FilePath& full_path, bool overwrite_existing_file) { 335 VLOG(20) << __FUNCTION__ << "()" << " id = " << id 336 << " overwrite_existing_file = " << overwrite_existing_file 337 << " full_path = \"" << full_path.value() << "\""; 338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 339 340 DownloadFile* download = GetDownloadFile(id); 341 if (!download) 342 return; 343 344 DCHECK(download->GetDownloadManager()); 345 DownloadManager* download_manager = download->GetDownloadManager(); 346 347 VLOG(20) << __FUNCTION__ << "()" << " download = " << download->DebugString(); 348 349 int uniquifier = 0; 350 FilePath new_path = full_path; 351 if (!overwrite_existing_file) { 352 // Make our name unique at this point, as if a dangerous file is 353 // downloading and a 2nd download is started for a file with the same 354 // name, they would have the same path. This is because we uniquify 355 // the name on download start, and at that time the first file does 356 // not exists yet, so the second file gets the same name. 357 // This should not happen in the SAFE case, and we check for that in the UI 358 // thread. 359 uniquifier = download_util::GetUniquePathNumber(new_path); 360 if (uniquifier > 0) { 361 download_util::AppendNumberToPath(&new_path, uniquifier); 362 } 363 } 364 365 // Rename the file, overwriting if necessary. 366 if (!download->Rename(new_path)) { 367 // Error. Between the time the UI thread generated 'full_path' to the time 368 // this code runs, something happened that prevents us from renaming. 369 CancelDownloadOnRename(id); 370 return; 371 } 372 373 #if defined(OS_MACOSX) 374 // Done here because we only want to do this once; see 375 // http://crbug.com/13120 for details. 376 download->AnnotateWithSourceInformation(); 377 #endif 378 379 BrowserThread::PostTask( 380 BrowserThread::UI, FROM_HERE, 381 NewRunnableMethod( 382 download_manager, &DownloadManager::OnDownloadRenamedToFinalName, id, 383 new_path, uniquifier)); 384 } 385 386 // Called only from RenameInProgressDownloadFile and 387 // RenameCompletingDownloadFile on the FILE thread. 388 void DownloadFileManager::CancelDownloadOnRename(int id) { 389 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 390 391 DownloadFile* download = GetDownloadFile(id); 392 if (!download) 393 return; 394 395 DownloadManager* download_manager = download->GetDownloadManager(); 396 if (!download_manager) { 397 // Without a download manager, we can't cancel the request normally, so we 398 // need to do it here. The normal path will also update the download 399 // history before cancelling the request. 400 download->CancelDownloadRequest(resource_dispatcher_host_); 401 return; 402 } 403 404 BrowserThread::PostTask( 405 BrowserThread::UI, FROM_HERE, 406 NewRunnableMethod(download_manager, 407 &DownloadManager::DownloadCancelled, id)); 408 } 409 410 void DownloadFileManager::EraseDownload(int id) { 411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 412 413 if (!ContainsKey(downloads_, id)) 414 return; 415 416 DownloadFile* download_file = downloads_[id]; 417 418 VLOG(20) << " " << __FUNCTION__ << "()" 419 << " id = " << id 420 << " download_file = " << download_file->DebugString(); 421 422 downloads_.erase(id); 423 424 delete download_file; 425 426 if (downloads_.empty()) 427 StopUpdateTimer(); 428 } 429