Home | History | Annotate | Download | only in download
      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