| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 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 <Windows.h> | |
| 6 #include <objbase.h> | |
| 7 | |
| 8 #include "chrome/browser/download_file.h" | |
| 9 | |
| 10 #include "base/file_util.h" | |
| 11 #include "base/path_service.h" | |
| 12 #include "base/scoped_ptr.h" | |
| 13 #include "base/string_util.h" | |
| 14 #include "base/task.h" | |
| 15 #include "chrome/browser/browser_process.h" | |
| 16 #include "chrome/browser/download_manager.h" | |
| 17 #include "chrome/browser/profile.h" | |
| 18 #include "chrome/browser/resource_dispatcher_host.h" | |
| 19 #include "chrome/browser/tab_contents.h" | |
| 20 #include "chrome/browser/tab_util.h" | |
| 21 #include "chrome/common/chrome_paths.h" | |
| 22 #include "chrome/common/stl_util-inl.h" | |
| 23 #include "chrome/common/win_util.h" | |
| 24 #include "chrome/common/win_safe_util.h" | |
| 25 #include "googleurl/src/gurl.h" | |
| 26 #include "net/base/net_util.h" | |
| 27 #include "net/url_request/url_request_context.h" | |
| 28 | |
| 29 // Throttle updates to the UI thread so that a fast moving download doesn't | |
| 30 // cause it to become unresponsive (ins milliseconds). | |
| 31 static const int kUpdatePeriodMs = 500; | |
| 32 | |
| 33 // Timer task for posting UI updates. This task is created and maintained by | |
| 34 // the DownloadFileManager long as there is an in progress download. The task | |
| 35 // is cancelled when all active downloads have completed, or in the destructor | |
| 36 // of the DownloadFileManager. | |
| 37 class DownloadFileUpdateTask : public Task { | |
| 38 public: | |
| 39 explicit DownloadFileUpdateTask(DownloadFileManager* manager) | |
| 40 : manager_(manager) {} | |
| 41 virtual void Run() { | |
| 42 manager_->UpdateInProgressDownloads(); | |
| 43 } | |
| 44 | |
| 45 private: | |
| 46 DownloadFileManager* manager_; | |
| 47 | |
| 48 DISALLOW_EVIL_CONSTRUCTORS(DownloadFileUpdateTask); | |
| 49 }; | |
| 50 | |
| 51 // DownloadFile implementation ------------------------------------------------- | |
| 52 | |
| 53 DownloadFile::DownloadFile(const DownloadCreateInfo* info) | |
| 54 : file_(NULL), | |
| 55 id_(info->download_id), | |
| 56 render_process_id_(info->render_process_id), | |
| 57 render_view_id_(info->render_view_id), | |
| 58 request_id_(info->request_id), | |
| 59 bytes_so_far_(0), | |
| 60 path_renamed_(false), | |
| 61 in_progress_(true) { | |
| 62 } | |
| 63 | |
| 64 DownloadFile::~DownloadFile() { | |
| 65 Close(); | |
| 66 } | |
| 67 | |
| 68 bool DownloadFile::Initialize() { | |
| 69 if (file_util::CreateTemporaryFileName(&full_path_)) | |
| 70 return Open(L"wb"); | |
| 71 return false; | |
| 72 } | |
| 73 | |
| 74 // FIXME bug 595247: handle errors on file writes. | |
| 75 bool DownloadFile::AppendDataToFile(const char* data, int data_len) { | |
| 76 if (file_) { | |
| 77 fwrite(data, 1, data_len, file_); | |
| 78 bytes_so_far_ += data_len; | |
| 79 return true; | |
| 80 } | |
| 81 return false; | |
| 82 } | |
| 83 | |
| 84 void DownloadFile::Cancel() { | |
| 85 Close(); | |
| 86 DeleteFile(full_path_.c_str()); | |
| 87 } | |
| 88 | |
| 89 // The UI has provided us with our finalized name. | |
| 90 bool DownloadFile::Rename(const std::wstring& new_path) { | |
| 91 Close(); | |
| 92 | |
| 93 // We cannot rename because rename will keep the same security descriptor | |
| 94 // on the destination file. We want to recreate the security descriptor | |
| 95 // with the security that makes sense in the new path. | |
| 96 if (!file_util::RenameFileAndResetSecurityDescriptor(full_path_.c_str(), | |
| 97 new_path.c_str())) { | |
| 98 return false; | |
| 99 } | |
| 100 | |
| 101 DeleteFile(full_path_.c_str()); | |
| 102 | |
| 103 full_path_ = new_path; | |
| 104 path_renamed_ = true; | |
| 105 | |
| 106 // We don't need to re-open the file if we're done (finished or canceled). | |
| 107 if (!in_progress_) | |
| 108 return true; | |
| 109 | |
| 110 if (!Open(L"a+b")) | |
| 111 return false; | |
| 112 return true; | |
| 113 } | |
| 114 | |
| 115 void DownloadFile::Close() { | |
| 116 if (file_) { | |
| 117 fclose(file_); | |
| 118 file_ = NULL; | |
| 119 } | |
| 120 } | |
| 121 | |
| 122 bool DownloadFile::Open(const wchar_t* open_mode) { | |
| 123 DCHECK(!full_path_.empty()); | |
| 124 if (_wfopen_s(&file_, full_path_.c_str(), open_mode)) { | |
| 125 file_ = NULL; | |
| 126 return false; | |
| 127 } | |
| 128 // Sets the Zone to tell Windows that this file comes from the internet. | |
| 129 // We ignore the return value because a failure is not fatal. | |
| 130 win_util::SetInternetZoneIdentifier(full_path_); | |
| 131 return true; | |
| 132 } | |
| 133 | |
| 134 // DownloadFileManager implementation ------------------------------------------ | |
| 135 | |
| 136 DownloadFileManager::DownloadFileManager(MessageLoop* ui_loop, | |
| 137 ResourceDispatcherHost* rdh) | |
| 138 : next_id_(0), | |
| 139 ui_loop_(ui_loop), | |
| 140 resource_dispatcher_host_(rdh) { | |
| 141 } | |
| 142 | |
| 143 DownloadFileManager::~DownloadFileManager() { | |
| 144 // Check for clean shutdown. | |
| 145 DCHECK(downloads_.empty()); | |
| 146 ui_progress_.clear(); | |
| 147 } | |
| 148 | |
| 149 void DownloadFileManager::Initialize() { | |
| 150 io_loop_ = g_browser_process->io_thread()->message_loop(); | |
| 151 file_loop_ = g_browser_process->file_thread()->message_loop(); | |
| 152 } | |
| 153 | |
| 154 // Called during the browser shutdown process to clean up any state (open files, | |
| 155 // timers) that live on the download_thread_. | |
| 156 void DownloadFileManager::Shutdown() { | |
| 157 DCHECK(MessageLoop::current() == ui_loop_); | |
| 158 StopUpdateTimer(); | |
| 159 file_loop_->PostTask(FROM_HERE, | |
| 160 NewRunnableMethod(this, | |
| 161 &DownloadFileManager::OnShutdown)); | |
| 162 } | |
| 163 | |
| 164 // Cease download thread operations. | |
| 165 void DownloadFileManager::OnShutdown() { | |
| 166 DCHECK(MessageLoop::current() == file_loop_); | |
| 167 // Delete any partial downloads during shutdown. | |
| 168 for (DownloadFileMap::iterator it = downloads_.begin(); | |
| 169 it != downloads_.end(); ++it) { | |
| 170 DownloadFile* download = it->second; | |
| 171 if (download->in_progress()) | |
| 172 download->Cancel(); | |
| 173 delete download; | |
| 174 } | |
| 175 downloads_.clear(); | |
| 176 } | |
| 177 | |
| 178 // Lookup one in-progress download. | |
| 179 DownloadFile* DownloadFileManager::LookupDownload(int id) { | |
| 180 DownloadFileMap::iterator it = downloads_.find(id); | |
| 181 return it == downloads_.end() ? NULL : it->second; | |
| 182 } | |
| 183 | |
| 184 // The UI progress is updated on the file thread and removed on the UI thread. | |
| 185 void DownloadFileManager::RemoveDownloadFromUIProgress(int id) { | |
| 186 DCHECK(MessageLoop::current() == ui_loop_); | |
| 187 AutoLock lock(progress_lock_); | |
| 188 if (ui_progress_.find(id) != ui_progress_.end()) | |
| 189 ui_progress_.erase(id); | |
| 190 } | |
| 191 | |
| 192 // Throttle updates to the UI thread by only posting update notifications at a | |
| 193 // regularly controlled interval. | |
| 194 void DownloadFileManager::StartUpdateTimer() { | |
| 195 DCHECK(MessageLoop::current() == ui_loop_); | |
| 196 if (!update_timer_.IsRunning()) { | |
| 197 update_timer_.Start(TimeDelta::FromMilliseconds(kUpdatePeriodMs), this, | |
| 198 &DownloadFileManager::UpdateInProgressDownloads); | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 void DownloadFileManager::StopUpdateTimer() { | |
| 203 DCHECK(MessageLoop::current() == ui_loop_); | |
| 204 update_timer_.Stop(); | |
| 205 } | |
| 206 | |
| 207 // Called on the IO thread once the ResourceDispatcherHost has decided that a | |
| 208 // request is a download. | |
| 209 int DownloadFileManager::GetNextId() { | |
| 210 DCHECK(MessageLoop::current() == io_loop_); | |
| 211 return next_id_++; | |
| 212 } | |
| 213 | |
| 214 // Notifications sent from the IO thread and run on the download thread: | |
| 215 | |
| 216 // The IO thread created 'info', but the download thread (this method) uses it | |
| 217 // to create a DownloadFile, then passes 'info' to the UI thread where it is | |
| 218 // finally consumed and deleted. | |
| 219 void DownloadFileManager::StartDownload(DownloadCreateInfo* info) { | |
| 220 DCHECK(MessageLoop::current() == file_loop_); | |
| 221 DCHECK(info); | |
| 222 | |
| 223 DownloadFile* download = new DownloadFile(info); | |
| 224 if (!download->Initialize()) { | |
| 225 // Couldn't open, cancel the operation. The UI thread does not yet know | |
| 226 // about this download so we have to clean up 'info'. We need to get back | |
| 227 // to the IO thread to cancel the network request and CancelDownloadRequest | |
| 228 // on the UI thread is the safe way to do that. | |
| 229 ui_loop_->PostTask(FROM_HERE, | |
| 230 NewRunnableFunction(&DownloadManager::CancelDownloadRequest, | |
| 231 info->render_process_id, | |
| 232 info->request_id)); | |
| 233 delete info; | |
| 234 delete download; | |
| 235 return; | |
| 236 } | |
| 237 | |
| 238 DCHECK(LookupDownload(info->download_id) == NULL); | |
| 239 downloads_[info->download_id] = download; | |
| 240 info->path = download->full_path(); | |
| 241 { | |
| 242 AutoLock lock(progress_lock_); | |
| 243 ui_progress_[info->download_id] = info->received_bytes; | |
| 244 } | |
| 245 | |
| 246 ui_loop_->PostTask(FROM_HERE, | |
| 247 NewRunnableMethod(this, | |
| 248 &DownloadFileManager::OnStartDownload, | |
| 249 info)); | |
| 250 } | |
| 251 | |
| 252 // We don't forward an update to the UI thread here, since we want to throttle | |
| 253 // the UI update rate via a periodic timer. If the user has cancelled the | |
| 254 // download (in the UI thread), we may receive a few more updates before the IO | |
| 255 // thread gets the cancel message: we just delete the data since the | |
| 256 // DownloadFile has been deleted. | |
| 257 void DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) { | |
| 258 DCHECK(MessageLoop::current() == file_loop_); | |
| 259 std::vector<DownloadBuffer::Contents> contents; | |
| 260 { | |
| 261 AutoLock auto_lock(buffer->lock); | |
| 262 contents.swap(buffer->contents); | |
| 263 } | |
| 264 | |
| 265 DownloadFile* download = LookupDownload(id); | |
| 266 for (size_t i = 0; i < contents.size(); ++i) { | |
| 267 char* data = contents[i].first; | |
| 268 const int data_len = contents[i].second; | |
| 269 if (download) | |
| 270 download->AppendDataToFile(data, data_len); | |
| 271 delete [] data; | |
| 272 } | |
| 273 | |
| 274 if (download) { | |
| 275 AutoLock lock(progress_lock_); | |
| 276 ui_progress_[download->id()] = download->bytes_so_far(); | |
| 277 } | |
| 278 } | |
| 279 | |
| 280 void DownloadFileManager::DownloadFinished(int id, DownloadBuffer* buffer) { | |
| 281 DCHECK(MessageLoop::current() == file_loop_); | |
| 282 delete buffer; | |
| 283 DownloadFileMap::iterator it = downloads_.find(id); | |
| 284 if (it != downloads_.end()) { | |
| 285 DownloadFile* download = it->second; | |
| 286 download->set_in_progress(false); | |
| 287 | |
| 288 ui_loop_->PostTask(FROM_HERE, | |
| 289 NewRunnableMethod(this, | |
| 290 &DownloadFileManager::OnDownloadFinished, | |
| 291 id, | |
| 292 download->bytes_so_far())); | |
| 293 | |
| 294 // We need to keep the download around until the UI thread has finalized | |
| 295 // the name. | |
| 296 if (download->path_renamed()) { | |
| 297 downloads_.erase(it); | |
| 298 delete download; | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 if (downloads_.empty()) | |
| 303 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( | |
| 304 this, &DownloadFileManager::StopUpdateTimer)); | |
| 305 } | |
| 306 | |
| 307 // This method will be sent via a user action, or shutdown on the UI thread, and | |
| 308 // run on the download thread. Since this message has been sent from the UI | |
| 309 // thread, the download may have already completed and won't exist in our map. | |
| 310 void DownloadFileManager::CancelDownload(int id) { | |
| 311 DCHECK(MessageLoop::current() == file_loop_); | |
| 312 DownloadFileMap::iterator it = downloads_.find(id); | |
| 313 if (it != downloads_.end()) { | |
| 314 DownloadFile* download = it->second; | |
| 315 download->set_in_progress(false); | |
| 316 | |
| 317 download->Cancel(); | |
| 318 | |
| 319 ui_loop_->PostTask(FROM_HERE, | |
| 320 NewRunnableMethod(this, | |
| 321 &DownloadFileManager::RemoveDownloadFromUIProgress, | |
| 322 download->id())); | |
| 323 | |
| 324 if (download->path_renamed()) { | |
| 325 downloads_.erase(it); | |
| 326 delete download; | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 if (downloads_.empty()) | |
| 331 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( | |
| 332 this, &DownloadFileManager::StopUpdateTimer)); | |
| 333 } | |
| 334 | |
| 335 // Our periodic timer has fired so send the UI thread updates on all in progress | |
| 336 // downloads. | |
| 337 void DownloadFileManager::UpdateInProgressDownloads() { | |
| 338 DCHECK(MessageLoop::current() == ui_loop_); | |
| 339 AutoLock lock(progress_lock_); | |
| 340 ProgressMap::iterator it = ui_progress_.begin(); | |
| 341 for (; it != ui_progress_.end(); ++it) { | |
| 342 const int id = it->first; | |
| 343 DownloadManager* manager = LookupManager(id); | |
| 344 if (manager) | |
| 345 manager->UpdateDownload(id, it->second); | |
| 346 } | |
| 347 } | |
| 348 | |
| 349 // Notifications sent from the download thread and run on the UI thread. | |
| 350 | |
| 351 // Lookup the DownloadManager for this WebContents' profile and inform it of | |
| 352 // a new download. | |
| 353 // TODO(paulg): When implementing download restart via the Downloads tab, | |
| 354 // there will be no 'render_process_id' or 'render_view_id'. | |
| 355 void DownloadFileManager::OnStartDownload(DownloadCreateInfo* info) { | |
| 356 DCHECK(MessageLoop::current() == ui_loop_); | |
| 357 DownloadManager* manager = | |
| 358 DownloadManagerFromRenderIds(info->render_process_id, | |
| 359 info->render_view_id); | |
| 360 if (!manager) { | |
| 361 DownloadManager::CancelDownloadRequest(info->render_process_id, | |
| 362 info->request_id); | |
| 363 delete info; | |
| 364 return; | |
| 365 } | |
| 366 | |
| 367 StartUpdateTimer(); | |
| 368 | |
| 369 // Add the download manager to our request maps for future updates. We want to | |
| 370 // be able to cancel all in progress downloads when a DownloadManager is | |
| 371 // deleted, such as when a profile is closed. We also want to be able to look | |
| 372 // up the DownloadManager associated with a given request without having to | |
| 373 // rely on using tab information, since a tab may be closed while a download | |
| 374 // initiated from that tab is still in progress. | |
| 375 DownloadRequests& downloads = requests_[manager]; | |
| 376 downloads.insert(info->download_id); | |
| 377 | |
| 378 // TODO(paulg): The manager will exist when restarts are implemented. | |
| 379 DownloadManagerMap::iterator dit = managers_.find(info->download_id); | |
| 380 if (dit == managers_.end()) | |
| 381 managers_[info->download_id] = manager; | |
| 382 else | |
| 383 NOTREACHED(); | |
| 384 | |
| 385 // StartDownload will clean up |info|. | |
| 386 manager->StartDownload(info); | |
| 387 } | |
| 388 | |
| 389 // Update the Download Manager with the finish state, and remove the request | |
| 390 // tracking entries. | |
| 391 void DownloadFileManager::OnDownloadFinished(int id, | |
| 392 int64 bytes_so_far) { | |
| 393 DCHECK(MessageLoop::current() == ui_loop_); | |
| 394 DownloadManager* manager = LookupManager(id); | |
| 395 if (manager) | |
| 396 manager->DownloadFinished(id, bytes_so_far); | |
| 397 RemoveDownload(id, manager); | |
| 398 RemoveDownloadFromUIProgress(id); | |
| 399 } | |
| 400 | |
| 401 void DownloadFileManager::DownloadUrl(const GURL& url, | |
| 402 const GURL& referrer, | |
| 403 int render_process_host_id, | |
| 404 int render_view_id, | |
| 405 URLRequestContext* request_context) { | |
| 406 DCHECK(MessageLoop::current() == ui_loop_); | |
| 407 base::Thread* thread = g_browser_process->io_thread(); | |
| 408 if (thread) { | |
| 409 thread->message_loop()->PostTask(FROM_HERE, | |
| 410 NewRunnableMethod(this, | |
| 411 &DownloadFileManager::OnDownloadUrl, | |
| 412 url, | |
| 413 referrer, | |
| 414 render_process_host_id, | |
| 415 render_view_id, | |
| 416 request_context)); | |
| 417 } | |
| 418 } | |
| 419 | |
| 420 // Relate a download ID to its owning DownloadManager. | |
| 421 DownloadManager* DownloadFileManager::LookupManager(int download_id) { | |
| 422 DCHECK(MessageLoop::current() == ui_loop_); | |
| 423 DownloadManagerMap::iterator it = managers_.find(download_id); | |
| 424 if (it != managers_.end()) | |
| 425 return it->second; | |
| 426 return NULL; | |
| 427 } | |
| 428 | |
| 429 // Utility function for look up table maintenance, called on the UI thread. | |
| 430 // A manager may have multiple downloads in progress, so we just look up the | |
| 431 // one download (id) and remove it from the set, and remove the set if it | |
| 432 // becomes empty. | |
| 433 void DownloadFileManager::RemoveDownload(int id, DownloadManager* manager) { | |
| 434 DCHECK(MessageLoop::current() == ui_loop_); | |
| 435 if (manager) { | |
| 436 RequestMap::iterator it = requests_.find(manager); | |
| 437 if (it != requests_.end()) { | |
| 438 DownloadRequests& downloads = it->second; | |
| 439 DownloadRequests::iterator rit = downloads.find(id); | |
| 440 if (rit != downloads.end()) | |
| 441 downloads.erase(rit); | |
| 442 if (downloads.empty()) | |
| 443 requests_.erase(it); | |
| 444 } | |
| 445 } | |
| 446 | |
| 447 // A download can only have one manager, so remove it if it exists. | |
| 448 DownloadManagerMap::iterator dit = managers_.find(id); | |
| 449 if (dit != managers_.end()) | |
| 450 managers_.erase(dit); | |
| 451 } | |
| 452 | |
| 453 // Utility function for converting request IDs to a TabContents. Must be called | |
| 454 // only on the UI thread since Profile operations may create UI objects, such as | |
| 455 // the first call to profile->GetDownloadManager(). | |
| 456 // static | |
| 457 DownloadManager* DownloadFileManager::DownloadManagerFromRenderIds( | |
| 458 int render_process_id, int render_view_id) { | |
| 459 TabContents* contents = tab_util::GetTabContentsByID(render_process_id, | |
| 460 render_view_id); | |
| 461 if (contents && contents->type() == TAB_CONTENTS_WEB) { | |
| 462 Profile* profile = contents->profile(); | |
| 463 if (profile) | |
| 464 return profile->GetDownloadManager(); | |
| 465 } | |
| 466 | |
| 467 return NULL; | |
| 468 } | |
| 469 | |
| 470 // Called by DownloadManagers in their destructor, and only on the UI thread. | |
| 471 void DownloadFileManager::RemoveDownloadManager(DownloadManager* manager) { | |
| 472 DCHECK(MessageLoop::current() == ui_loop_); | |
| 473 DCHECK(manager); | |
| 474 RequestMap::iterator it = requests_.find(manager); | |
| 475 if (it == requests_.end()) | |
| 476 return; | |
| 477 | |
| 478 const DownloadRequests& requests = it->second; | |
| 479 DownloadRequests::const_iterator i = requests.begin(); | |
| 480 for (; i != requests.end(); ++i) { | |
| 481 DownloadManagerMap::iterator dit = managers_.find(*i); | |
| 482 if (dit != managers_.end()) { | |
| 483 DCHECK(dit->second == manager); | |
| 484 managers_.erase(dit); | |
| 485 } | |
| 486 } | |
| 487 | |
| 488 requests_.erase(it); | |
| 489 } | |
| 490 | |
| 491 | |
| 492 // Notifications from the UI thread and run on the IO thread | |
| 493 | |
| 494 // Initiate a request for URL to be downloaded. | |
| 495 void DownloadFileManager::OnDownloadUrl(const GURL& url, | |
| 496 const GURL& referrer, | |
| 497 int render_process_host_id, | |
| 498 int render_view_id, | |
| 499 URLRequestContext* request_context) { | |
| 500 DCHECK(MessageLoop::current() == io_loop_); | |
| 501 resource_dispatcher_host_->BeginDownload(url, | |
| 502 referrer, | |
| 503 render_process_host_id, | |
| 504 render_view_id, | |
| 505 request_context); | |
| 506 } | |
| 507 | |
| 508 // Actions from the UI thread and run on the download thread | |
| 509 | |
| 510 // Open a download, or show it in a Windows Explorer window. We run on this | |
| 511 // thread to avoid blocking the UI with (potentially) slow Shell operations. | |
| 512 // TODO(paulg): File 'stat' operations. | |
| 513 void DownloadFileManager::OnShowDownloadInShell(const std::wstring full_path) { | |
| 514 DCHECK(MessageLoop::current() == file_loop_); | |
| 515 win_util::ShowItemInFolder(full_path); | |
| 516 } | |
| 517 | |
| 518 // Launches the selected download using ShellExecute 'open' verb. If there is | |
| 519 // a valid parent window, the 'safer' version will be used which can | |
| 520 // display a modal dialog asking for user consent on dangerous files. | |
| 521 void DownloadFileManager::OnOpenDownloadInShell(const std::wstring full_path, | |
| 522 const std::wstring& url, | |
| 523 HWND parent_window) { | |
| 524 DCHECK(MessageLoop::current() == file_loop_); | |
| 525 if (NULL != parent_window) { | |
| 526 win_util::SaferOpenItemViaShell(parent_window, L"", full_path, url, true); | |
| 527 } else { | |
| 528 win_util::OpenItemViaShell(full_path, true); | |
| 529 } | |
| 530 } | |
| 531 | |
| 532 // The DownloadManager in the UI thread has provided a final name for the | |
| 533 // download specified by 'id'. Rename the in progress download, and remove it | |
| 534 // from our table if it has been completed or cancelled already. | |
| 535 void DownloadFileManager::OnFinalDownloadName(int id, | |
| 536 const std::wstring& full_path) { | |
| 537 DCHECK(MessageLoop::current() == file_loop_); | |
| 538 DownloadFileMap::iterator it = downloads_.find(id); | |
| 539 if (it == downloads_.end()) | |
| 540 return; | |
| 541 | |
| 542 std::wstring download_dir = file_util::GetDirectoryFromPath(full_path); | |
| 543 if (!file_util::PathExists(download_dir)) | |
| 544 file_util::CreateDirectory(download_dir); | |
| 545 | |
| 546 DownloadFile* download = it->second; | |
| 547 if (!download->Rename(full_path)) { | |
| 548 // Error. Between the time the UI thread generated 'full_path' to the time | |
| 549 // this code runs, something happened that prevents us from renaming. | |
| 550 DownloadManagerMap::iterator dmit = managers_.find(download->id()); | |
| 551 if (dmit != managers_.end()) { | |
| 552 DownloadManager* dlm = dmit->second; | |
| 553 ui_loop_->PostTask(FROM_HERE, | |
| 554 NewRunnableMethod(dlm, | |
| 555 &DownloadManager::DownloadCancelled, | |
| 556 id)); | |
| 557 } else { | |
| 558 ui_loop_->PostTask(FROM_HERE, | |
| 559 NewRunnableFunction(&DownloadManager::CancelDownloadRequest, | |
| 560 download->render_process_id(), | |
| 561 download->request_id())); | |
| 562 } | |
| 563 } | |
| 564 | |
| 565 // If the download has completed before we got this final name, we remove it | |
| 566 // from our in progress map. | |
| 567 if (!download->in_progress()) { | |
| 568 downloads_.erase(it); | |
| 569 delete download; | |
| 570 } | |
| 571 | |
| 572 if (downloads_.empty()) | |
| 573 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( | |
| 574 this, &DownloadFileManager::StopUpdateTimer)); | |
| 575 } | |
| 576 | |
| 577 void DownloadFileManager::CreateDirectory(const std::wstring& directory) { | |
| 578 if (!file_util::PathExists(directory)) | |
| 579 file_util::CreateDirectory(directory); | |
| 580 } | |
| OLD | NEW |