| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/download/download_file_manager.h" | 5 #include "chrome/browser/download/download_file_manager.h" |
| 6 | 6 |
| 7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
| 8 #include "base/task.h" | 8 #include "base/task.h" |
| 9 #include "base/utf_string_conversions.h" | 9 #include "base/utf_string_conversions.h" |
| 10 #include "build/build_config.h" | 10 #include "build/build_config.h" |
| 11 #include "chrome/browser/chrome_thread.h" | 11 #include "chrome/browser/chrome_thread.h" |
| 12 #include "chrome/browser/download/download_manager.h" | 12 #include "chrome/browser/download/download_manager.h" |
| 13 #include "chrome/browser/download/download_util.h" |
| 13 #include "chrome/browser/history/download_types.h" | 14 #include "chrome/browser/history/download_types.h" |
| 14 #include "chrome/browser/net/chrome_url_request_context.h" | 15 #include "chrome/browser/net/chrome_url_request_context.h" |
| 15 #include "chrome/browser/platform_util.h" | 16 #include "chrome/browser/platform_util.h" |
| 16 #include "chrome/browser/profile.h" | 17 #include "chrome/browser/profile.h" |
| 17 #include "chrome/browser/renderer_host/resource_dispatcher_host.h" | 18 #include "chrome/browser/renderer_host/resource_dispatcher_host.h" |
| 18 #include "chrome/browser/tab_contents/tab_util.h" | 19 #include "chrome/browser/tab_contents/tab_util.h" |
| 19 #include "chrome/browser/tab_contents/tab_contents.h" | 20 #include "chrome/browser/tab_contents/tab_contents.h" |
| 20 #include "googleurl/src/gurl.h" | 21 #include "googleurl/src/gurl.h" |
| 21 | 22 |
| 22 #if defined(OS_WIN) | 23 #if defined(OS_WIN) |
| (...skipping 12 matching lines...) Expand all Loading... |
| 35 } // namespace | 36 } // namespace |
| 36 | 37 |
| 37 DownloadFileManager::DownloadFileManager(ResourceDispatcherHost* rdh) | 38 DownloadFileManager::DownloadFileManager(ResourceDispatcherHost* rdh) |
| 38 : next_id_(0), | 39 : next_id_(0), |
| 39 resource_dispatcher_host_(rdh) { | 40 resource_dispatcher_host_(rdh) { |
| 40 } | 41 } |
| 41 | 42 |
| 42 DownloadFileManager::~DownloadFileManager() { | 43 DownloadFileManager::~DownloadFileManager() { |
| 43 // Check for clean shutdown. | 44 // Check for clean shutdown. |
| 44 DCHECK(downloads_.empty()); | 45 DCHECK(downloads_.empty()); |
| 45 ui_progress_.clear(); | |
| 46 } | 46 } |
| 47 | 47 |
| 48 // Called during the browser shutdown process to clean up any state (open files, | 48 // Called during the browser shutdown process to clean up any state (open files, |
| 49 // timers) that live on the download_thread_. | 49 // timers) that live on the download_thread_. |
| 50 void DownloadFileManager::Shutdown() { | 50 void DownloadFileManager::Shutdown() { |
| 51 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); | 51 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 52 StopUpdateTimer(); | 52 StopUpdateTimer(); |
| 53 ChromeThread::PostTask( | 53 ChromeThread::PostTask( |
| 54 ChromeThread::FILE, FROM_HERE, | 54 ChromeThread::FILE, FROM_HERE, |
| 55 NewRunnableMethod(this, &DownloadFileManager::OnShutdown)); | 55 NewRunnableMethod(this, &DownloadFileManager::OnShutdown)); |
| 56 } | 56 } |
| 57 | 57 |
| 58 // Cease download thread operations. | 58 // Cease download thread operations. |
| 59 void DownloadFileManager::OnShutdown() { | 59 void DownloadFileManager::OnShutdown() { |
| 60 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); | 60 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 61 // Delete any partial downloads during shutdown. | 61 // Delete any partial downloads during shutdown. |
| 62 for (DownloadFileMap::iterator it = downloads_.begin(); | 62 for (DownloadFileMap::iterator it = downloads_.begin(); |
| 63 it != downloads_.end(); ++it) { | 63 it != downloads_.end(); ++it) { |
| 64 DownloadFile* download = it->second; | 64 DownloadFile* download = it->second; |
| 65 if (download->in_progress()) | 65 if (download->in_progress()) |
| 66 download->Cancel(); | 66 download->Cancel(); |
| 67 delete download; | 67 delete download; |
| 68 } | 68 } |
| 69 downloads_.clear(); | 69 downloads_.clear(); |
| 70 } | 70 } |
| 71 | 71 |
| 72 // Initiate a request for URL to be downloaded. Called from UI thread, | |
| 73 // runs on IO thread. | |
| 74 void DownloadFileManager::OnDownloadUrl( | |
| 75 const GURL& url, | |
| 76 const GURL& referrer, | |
| 77 const std::string& referrer_charset, | |
| 78 const DownloadSaveInfo& save_info, | |
| 79 int render_process_host_id, | |
| 80 int render_view_id, | |
| 81 URLRequestContextGetter* request_context_getter) { | |
| 82 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); | |
| 83 | |
| 84 URLRequestContext* context = request_context_getter->GetURLRequestContext(); | |
| 85 context->set_referrer_charset(referrer_charset); | |
| 86 | |
| 87 // Show "Save As" UI. | |
| 88 bool prompt_for_save_location = true; | |
| 89 resource_dispatcher_host_->BeginDownload(url, | |
| 90 referrer, | |
| 91 save_info, | |
| 92 prompt_for_save_location, | |
| 93 render_process_host_id, | |
| 94 render_view_id, | |
| 95 context); | |
| 96 } | |
| 97 | |
| 98 // Notifications sent from the download thread and run on the UI thread. | 72 // Notifications sent from the download thread and run on the UI thread. |
| 99 | 73 |
| 100 // Lookup the DownloadManager for this TabContents' profile and inform it of | 74 // Lookup the DownloadManager for this TabContents' profile and inform it of |
| 101 // a new download. | 75 // a new download. |
| 102 // TODO(paulg): When implementing download restart via the Downloads tab, | 76 // TODO(paulg): When implementing download restart via the Downloads tab, |
| 103 // there will be no 'render_process_id' or 'render_view_id'. | 77 // there will be no 'render_process_id' or 'render_view_id'. |
| 104 void DownloadFileManager::OnStartDownload(DownloadCreateInfo* info) { | 78 void DownloadFileManager::OnStartDownload(DownloadCreateInfo* info) { |
| 105 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); | 79 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 106 DownloadManager* manager = DownloadManagerFromRenderIds(info->child_id, | 80 DownloadManager* manager = DownloadManagerFromRenderIds(info->child_id, |
| 107 info->render_view_id); | 81 info->render_view_id); |
| 108 if (!manager) { | 82 if (!manager) { |
| 109 ChromeThread::PostTask( | 83 ChromeThread::PostTask( |
| 110 ChromeThread::IO, FROM_HERE, | 84 ChromeThread::IO, FROM_HERE, |
| 111 NewRunnableFunction(&DownloadManager::OnCancelDownloadRequest, | 85 NewRunnableFunction(&download_util::CancelDownloadRequest, |
| 112 resource_dispatcher_host_, | 86 resource_dispatcher_host_, |
| 113 info->child_id, | 87 info->child_id, |
| 114 info->request_id)); | 88 info->request_id)); |
| 115 delete info; | 89 delete info; |
| 116 return; | 90 return; |
| 117 } | 91 } |
| 118 | 92 |
| 119 StartUpdateTimer(); | 93 StartUpdateTimer(); |
| 120 | 94 |
| 121 // Add the download manager to our request maps for future updates. We want to | 95 // Add the download manager to our request maps for future updates. We want to |
| (...skipping 14 matching lines...) Expand all Loading... |
| 136 | 110 |
| 137 // StartDownload will clean up |info|. | 111 // StartDownload will clean up |info|. |
| 138 manager->StartDownload(info); | 112 manager->StartDownload(info); |
| 139 } | 113 } |
| 140 | 114 |
| 141 // Update the Download Manager with the finish state, and remove the request | 115 // Update the Download Manager with the finish state, and remove the request |
| 142 // tracking entries. | 116 // tracking entries. |
| 143 void DownloadFileManager::OnDownloadFinished(int id, | 117 void DownloadFileManager::OnDownloadFinished(int id, |
| 144 int64 bytes_so_far) { | 118 int64 bytes_so_far) { |
| 145 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); | 119 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 146 DownloadManager* manager = LookupManager(id); | 120 DownloadManager* manager = GetDownloadManager(id); |
| 147 if (manager) | 121 if (manager) |
| 148 manager->DownloadFinished(id, bytes_so_far); | 122 manager->DownloadFinished(id, bytes_so_far); |
| 149 RemoveDownload(id, manager); | 123 RemoveDownload(id, manager); |
| 150 RemoveDownloadFromUIProgress(id); | 124 RemoveDownloadFromUIProgress(id); |
| 151 } | 125 } |
| 152 | 126 |
| 153 // Lookup one in-progress download. | 127 // Lookup one in-progress download. |
| 154 DownloadFile* DownloadFileManager::LookupDownload(int id) { | 128 DownloadFile* DownloadFileManager::GetDownloadFile(int id) { |
| 155 DownloadFileMap::iterator it = downloads_.find(id); | 129 DownloadFileMap::iterator it = downloads_.find(id); |
| 156 return it == downloads_.end() ? NULL : it->second; | 130 return it == downloads_.end() ? NULL : it->second; |
| 157 } | 131 } |
| 158 | 132 |
| 159 // The UI progress is updated on the file thread and removed on the UI thread. | 133 // The UI progress is updated on the file thread and removed on the UI thread. |
| 160 void DownloadFileManager::RemoveDownloadFromUIProgress(int id) { | 134 void DownloadFileManager::RemoveDownloadFromUIProgress(int id) { |
| 161 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); | 135 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 162 AutoLock lock(progress_lock_); | 136 AutoLock lock(progress_lock_); |
| 163 if (ui_progress_.find(id) != ui_progress_.end()) | 137 if (ui_progress_.find(id) != ui_progress_.end()) |
| 164 ui_progress_.erase(id); | 138 ui_progress_.erase(id); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 180 } | 154 } |
| 181 | 155 |
| 182 // Our periodic timer has fired so send the UI thread updates on all in progress | 156 // Our periodic timer has fired so send the UI thread updates on all in progress |
| 183 // downloads. | 157 // downloads. |
| 184 void DownloadFileManager::UpdateInProgressDownloads() { | 158 void DownloadFileManager::UpdateInProgressDownloads() { |
| 185 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); | 159 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 186 AutoLock lock(progress_lock_); | 160 AutoLock lock(progress_lock_); |
| 187 ProgressMap::iterator it = ui_progress_.begin(); | 161 ProgressMap::iterator it = ui_progress_.begin(); |
| 188 for (; it != ui_progress_.end(); ++it) { | 162 for (; it != ui_progress_.end(); ++it) { |
| 189 const int id = it->first; | 163 const int id = it->first; |
| 190 DownloadManager* manager = LookupManager(id); | 164 DownloadManager* manager = GetDownloadManager(id); |
| 191 if (manager) | 165 if (manager) |
| 192 manager->UpdateDownload(id, it->second); | 166 manager->UpdateDownload(id, it->second); |
| 193 } | 167 } |
| 194 } | 168 } |
| 195 | 169 |
| 196 // Called on the IO thread once the ResourceDispatcherHost has decided that a | 170 // Called on the IO thread once the ResourceDispatcherHost has decided that a |
| 197 // request is a download. | 171 // request is a download. |
| 198 int DownloadFileManager::GetNextId() { | 172 int DownloadFileManager::GetNextId() { |
| 199 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); | 173 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| 200 return next_id_++; | 174 return next_id_++; |
| 201 } | 175 } |
| 202 | 176 |
| 203 // Notifications sent from the IO thread and run on the download thread: | 177 // Notifications sent from the IO thread and run on the download thread: |
| 204 | 178 |
| 205 // The IO thread created 'info', but the download thread (this method) uses it | 179 // The IO thread created 'info', but the download thread (this method) uses it |
| 206 // to create a DownloadFile, then passes 'info' to the UI thread where it is | 180 // to create a DownloadFile, then passes 'info' to the UI thread where it is |
| 207 // finally consumed and deleted. | 181 // finally consumed and deleted. |
| 208 void DownloadFileManager::StartDownload(DownloadCreateInfo* info) { | 182 void DownloadFileManager::StartDownload(DownloadCreateInfo* info) { |
| 209 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); | 183 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 210 DCHECK(info); | 184 DCHECK(info); |
| 211 | 185 |
| 212 DownloadFile* download = new DownloadFile(info); | 186 DownloadFile* download = new DownloadFile(info); |
| 213 if (!download->Initialize()) { | 187 if (!download->Initialize()) { |
| 214 // Couldn't open, cancel the operation. The UI thread does not yet know | 188 // Couldn't open, cancel the operation. The UI thread does not yet know |
| 215 // about this download so we have to clean up 'info'. We need to get back | 189 // about this download so we have to clean up 'info'. We need to get back |
| 216 // to the IO thread to cancel the network request and CancelDownloadRequest | 190 // to the IO thread to cancel the network request and CancelDownloadRequest |
| 217 // on the UI thread is the safe way to do that. | 191 // on the UI thread is the safe way to do that. |
| 218 ChromeThread::PostTask( | 192 ChromeThread::PostTask( |
| 219 ChromeThread::IO, FROM_HERE, | 193 ChromeThread::IO, FROM_HERE, |
| 220 NewRunnableFunction(&DownloadManager::OnCancelDownloadRequest, | 194 NewRunnableFunction(&download_util::CancelDownloadRequest, |
| 221 resource_dispatcher_host_, | 195 resource_dispatcher_host_, |
| 222 info->child_id, | 196 info->child_id, |
| 223 info->request_id)); | 197 info->request_id)); |
| 224 delete info; | 198 delete info; |
| 225 delete download; | 199 delete download; |
| 226 return; | 200 return; |
| 227 } | 201 } |
| 228 | 202 |
| 229 DCHECK(LookupDownload(info->download_id) == NULL); | 203 DCHECK(GetDownloadFile(info->download_id) == NULL); |
| 230 downloads_[info->download_id] = download; | 204 downloads_[info->download_id] = download; |
| 231 info->path = download->full_path(); | 205 info->path = download->full_path(); |
| 232 { | 206 { |
| 233 AutoLock lock(progress_lock_); | 207 AutoLock lock(progress_lock_); |
| 234 ui_progress_[info->download_id] = info->received_bytes; | 208 ui_progress_[info->download_id] = info->received_bytes; |
| 235 } | 209 } |
| 236 | 210 |
| 237 ChromeThread::PostTask( | 211 ChromeThread::PostTask( |
| 238 ChromeThread::UI, FROM_HERE, | 212 ChromeThread::UI, FROM_HERE, |
| 239 NewRunnableMethod(this, &DownloadFileManager::OnStartDownload, info)); | 213 NewRunnableMethod(this, &DownloadFileManager::OnStartDownload, info)); |
| 240 } | 214 } |
| 241 | 215 |
| 242 // We don't forward an update to the UI thread here, since we want to throttle | 216 // We don't forward an update to the UI thread here, since we want to throttle |
| 243 // the UI update rate via a periodic timer. If the user has cancelled the | 217 // the UI update rate via a periodic timer. If the user has cancelled the |
| 244 // download (in the UI thread), we may receive a few more updates before the IO | 218 // download (in the UI thread), we may receive a few more updates before the IO |
| 245 // thread gets the cancel message: we just delete the data since the | 219 // thread gets the cancel message: we just delete the data since the |
| 246 // DownloadFile has been deleted. | 220 // DownloadFile has been deleted. |
| 247 void DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) { | 221 void DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) { |
| 248 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); | 222 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 249 std::vector<DownloadBuffer::Contents> contents; | 223 std::vector<DownloadBuffer::Contents> contents; |
| 250 { | 224 { |
| 251 AutoLock auto_lock(buffer->lock); | 225 AutoLock auto_lock(buffer->lock); |
| 252 contents.swap(buffer->contents); | 226 contents.swap(buffer->contents); |
| 253 } | 227 } |
| 254 | 228 |
| 255 DownloadFile* download = LookupDownload(id); | 229 DownloadFile* download = GetDownloadFile(id); |
| 256 for (size_t i = 0; i < contents.size(); ++i) { | 230 for (size_t i = 0; i < contents.size(); ++i) { |
| 257 net::IOBuffer* data = contents[i].first; | 231 net::IOBuffer* data = contents[i].first; |
| 258 const int data_len = contents[i].second; | 232 const int data_len = contents[i].second; |
| 259 if (download) | 233 if (download) |
| 260 download->AppendDataToFile(data->data(), data_len); | 234 download->AppendDataToFile(data->data(), data_len); |
| 261 data->Release(); | 235 data->Release(); |
| 262 } | 236 } |
| 263 | 237 |
| 264 if (download) { | 238 if (download) { |
| 265 AutoLock lock(progress_lock_); | 239 AutoLock lock(progress_lock_); |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 319 } | 293 } |
| 320 } | 294 } |
| 321 | 295 |
| 322 if (downloads_.empty()) { | 296 if (downloads_.empty()) { |
| 323 ChromeThread::PostTask( | 297 ChromeThread::PostTask( |
| 324 ChromeThread::UI, FROM_HERE, | 298 ChromeThread::UI, FROM_HERE, |
| 325 NewRunnableMethod(this, &DownloadFileManager::StopUpdateTimer)); | 299 NewRunnableMethod(this, &DownloadFileManager::StopUpdateTimer)); |
| 326 } | 300 } |
| 327 } | 301 } |
| 328 | 302 |
| 329 void DownloadFileManager::DownloadUrl( | |
| 330 const GURL& url, | |
| 331 const GURL& referrer, | |
| 332 const std::string& referrer_charset, | |
| 333 const DownloadSaveInfo& save_info, | |
| 334 int render_process_host_id, | |
| 335 int render_view_id, | |
| 336 URLRequestContextGetter* request_context_getter) { | |
| 337 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); | |
| 338 ChromeThread::PostTask( | |
| 339 ChromeThread::IO, FROM_HERE, | |
| 340 NewRunnableMethod(this, | |
| 341 &DownloadFileManager::OnDownloadUrl, | |
| 342 url, | |
| 343 referrer, | |
| 344 referrer_charset, | |
| 345 save_info, | |
| 346 render_process_host_id, | |
| 347 render_view_id, | |
| 348 request_context_getter)); | |
| 349 } | |
| 350 | |
| 351 // Relate a download ID to its owning DownloadManager. | 303 // Relate a download ID to its owning DownloadManager. |
| 352 DownloadManager* DownloadFileManager::LookupManager(int download_id) { | 304 DownloadManager* DownloadFileManager::GetDownloadManager(int download_id) { |
| 353 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); | 305 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 354 DownloadManagerMap::iterator it = managers_.find(download_id); | 306 DownloadManagerMap::iterator it = managers_.find(download_id); |
| 355 if (it != managers_.end()) | 307 if (it != managers_.end()) |
| 356 return it->second; | 308 return it->second; |
| 357 return NULL; | 309 return NULL; |
| 358 } | 310 } |
| 359 | 311 |
| 360 // Utility function for look up table maintenance, called on the UI thread. | 312 // Utility function for look up table maintenance, called on the UI thread. |
| 361 // A manager may have multiple downloads in progress, so we just look up the | 313 // A manager may have multiple downloads in progress, so we just look up the |
| 362 // one download (id) and remove it from the set, and remove the set if it | 314 // one download (id) and remove it from the set, and remove the set if it |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 480 // this code runs, something happened that prevents us from renaming. | 432 // this code runs, something happened that prevents us from renaming. |
| 481 DownloadManagerMap::iterator dmit = managers_.find(download->id()); | 433 DownloadManagerMap::iterator dmit = managers_.find(download->id()); |
| 482 if (dmit != managers_.end()) { | 434 if (dmit != managers_.end()) { |
| 483 DownloadManager* dlm = dmit->second; | 435 DownloadManager* dlm = dmit->second; |
| 484 ChromeThread::PostTask( | 436 ChromeThread::PostTask( |
| 485 ChromeThread::UI, FROM_HERE, | 437 ChromeThread::UI, FROM_HERE, |
| 486 NewRunnableMethod(dlm, &DownloadManager::DownloadCancelled, id)); | 438 NewRunnableMethod(dlm, &DownloadManager::DownloadCancelled, id)); |
| 487 } else { | 439 } else { |
| 488 ChromeThread::PostTask( | 440 ChromeThread::PostTask( |
| 489 ChromeThread::IO, FROM_HERE, | 441 ChromeThread::IO, FROM_HERE, |
| 490 NewRunnableFunction(&DownloadManager::OnCancelDownloadRequest, | 442 NewRunnableFunction(&download_util::CancelDownloadRequest, |
| 491 resource_dispatcher_host_, | 443 resource_dispatcher_host_, |
| 492 download->child_id(), | 444 download->child_id(), |
| 493 download->request_id())); | 445 download->request_id())); |
| 494 } | 446 } |
| 495 } | 447 } |
| 496 | 448 |
| 497 // If the download has completed before we got this final name, we remove it | 449 // If the download has completed before we got this final name, we remove it |
| 498 // from our in progress map. | 450 // from our in progress map. |
| 499 if (!download->in_progress()) { | 451 if (!download->in_progress()) { |
| 500 downloads_.erase(it); | 452 downloads_.erase(it); |
| 501 delete download; | 453 delete download; |
| 502 } | 454 } |
| 503 | 455 |
| 504 if (downloads_.empty()) { | 456 if (downloads_.empty()) { |
| 505 ChromeThread::PostTask( | 457 ChromeThread::PostTask( |
| 506 ChromeThread::UI, FROM_HERE, | 458 ChromeThread::UI, FROM_HERE, |
| 507 NewRunnableMethod(this, &DownloadFileManager::StopUpdateTimer)); | 459 NewRunnableMethod(this, &DownloadFileManager::StopUpdateTimer)); |
| 508 } | 460 } |
| 509 } | 461 } |
| 510 | 462 |
| OLD | NEW |