Home | History | Annotate | Download | only in download
      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/download_file_impl.h"
      6 
      7 #include <string>
      8 
      9 #include "base/bind.h"
     10 #include "base/file_util.h"
     11 #include "base/message_loop/message_loop_proxy.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/time/time.h"
     14 #include "content/browser/byte_stream.h"
     15 #include "content/browser/download/download_create_info.h"
     16 #include "content/browser/download/download_interrupt_reasons_impl.h"
     17 #include "content/browser/download/download_net_log_parameters.h"
     18 #include "content/browser/download/download_stats.h"
     19 #include "content/public/browser/browser_thread.h"
     20 #include "content/public/browser/download_destination_observer.h"
     21 #include "net/base/io_buffer.h"
     22 
     23 namespace content {
     24 
     25 const int kUpdatePeriodMs = 500;
     26 const int kMaxTimeBlockingFileThreadMs = 1000;
     27 
     28 int DownloadFile::number_active_objects_ = 0;
     29 
     30 DownloadFileImpl::DownloadFileImpl(
     31     scoped_ptr<DownloadSaveInfo> save_info,
     32     const base::FilePath& default_download_directory,
     33     const GURL& url,
     34     const GURL& referrer_url,
     35     bool calculate_hash,
     36     scoped_ptr<ByteStreamReader> stream,
     37     const net::BoundNetLog& bound_net_log,
     38     base::WeakPtr<DownloadDestinationObserver> observer)
     39         : file_(save_info->file_path,
     40                 url,
     41                 referrer_url,
     42                 save_info->offset,
     43                 calculate_hash,
     44                 save_info->hash_state,
     45                 save_info->file.Pass(),
     46                 bound_net_log),
     47           default_download_directory_(default_download_directory),
     48           stream_reader_(stream.Pass()),
     49           bytes_seen_(0),
     50           bound_net_log_(bound_net_log),
     51           observer_(observer),
     52           weak_factory_(this) {
     53 }
     54 
     55 DownloadFileImpl::~DownloadFileImpl() {
     56   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     57   --number_active_objects_;
     58 }
     59 
     60 void DownloadFileImpl::Initialize(const InitializeCallback& callback) {
     61   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     62 
     63   update_timer_.reset(new base::RepeatingTimer<DownloadFileImpl>());
     64   DownloadInterruptReason result =
     65       file_.Initialize(default_download_directory_);
     66   if (result != DOWNLOAD_INTERRUPT_REASON_NONE) {
     67     BrowserThread::PostTask(
     68         BrowserThread::UI, FROM_HERE, base::Bind(callback, result));
     69     return;
     70   }
     71 
     72   stream_reader_->RegisterCallback(
     73       base::Bind(&DownloadFileImpl::StreamActive, weak_factory_.GetWeakPtr()));
     74 
     75   download_start_ = base::TimeTicks::Now();
     76 
     77   // Primarily to make reset to zero in restart visible to owner.
     78   SendUpdate();
     79 
     80   // Initial pull from the straw.
     81   StreamActive();
     82 
     83   BrowserThread::PostTask(
     84       BrowserThread::UI, FROM_HERE, base::Bind(
     85           callback, DOWNLOAD_INTERRUPT_REASON_NONE));
     86 
     87   ++number_active_objects_;
     88 }
     89 
     90 DownloadInterruptReason DownloadFileImpl::AppendDataToFile(
     91     const char* data, size_t data_len) {
     92   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     93 
     94   if (!update_timer_->IsRunning()) {
     95     update_timer_->Start(FROM_HERE,
     96                          base::TimeDelta::FromMilliseconds(kUpdatePeriodMs),
     97                          this, &DownloadFileImpl::SendUpdate);
     98   }
     99   rate_estimator_.Increment(data_len);
    100   return file_.AppendDataToFile(data, data_len);
    101 }
    102 
    103 void DownloadFileImpl::RenameAndUniquify(
    104     const base::FilePath& full_path,
    105     const RenameCompletionCallback& callback) {
    106   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    107 
    108   base::FilePath new_path(full_path);
    109 
    110   int uniquifier = base::GetUniquePathNumber(
    111       new_path, base::FilePath::StringType());
    112   if (uniquifier > 0) {
    113     new_path = new_path.InsertBeforeExtensionASCII(
    114         base::StringPrintf(" (%d)", uniquifier));
    115   }
    116 
    117   DownloadInterruptReason reason = file_.Rename(new_path);
    118   if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
    119     // Make sure our information is updated, since we're about to
    120     // error out.
    121     SendUpdate();
    122 
    123     // Null out callback so that we don't do any more stream processing.
    124     stream_reader_->RegisterCallback(base::Closure());
    125 
    126     new_path.clear();
    127   }
    128 
    129   BrowserThread::PostTask(
    130       BrowserThread::UI, FROM_HERE,
    131       base::Bind(callback, reason, new_path));
    132 }
    133 
    134 void DownloadFileImpl::RenameAndAnnotate(
    135     const base::FilePath& full_path,
    136     const RenameCompletionCallback& callback) {
    137   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    138 
    139   base::FilePath new_path(full_path);
    140 
    141   DownloadInterruptReason reason = DOWNLOAD_INTERRUPT_REASON_NONE;
    142   // Short circuit null rename.
    143   if (full_path != file_.full_path())
    144     reason = file_.Rename(new_path);
    145 
    146   if (reason == DOWNLOAD_INTERRUPT_REASON_NONE) {
    147     // Doing the annotation after the rename rather than before leaves
    148     // a very small window during which the file has the final name but
    149     // hasn't been marked with the Mark Of The Web.  However, it allows
    150     // anti-virus scanners on Windows to actually see the data
    151     // (http://crbug.com/127999) under the correct name (which is information
    152     // it uses).
    153     reason = file_.AnnotateWithSourceInformation();
    154   }
    155 
    156   if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
    157     // Make sure our information is updated, since we're about to
    158     // error out.
    159     SendUpdate();
    160 
    161     // Null out callback so that we don't do any more stream processing.
    162     stream_reader_->RegisterCallback(base::Closure());
    163 
    164     new_path.clear();
    165   }
    166 
    167   BrowserThread::PostTask(
    168       BrowserThread::UI, FROM_HERE,
    169       base::Bind(callback, reason, new_path));
    170 }
    171 
    172 void DownloadFileImpl::Detach() {
    173   file_.Detach();
    174 }
    175 
    176 void DownloadFileImpl::Cancel() {
    177   file_.Cancel();
    178 }
    179 
    180 base::FilePath DownloadFileImpl::FullPath() const {
    181   return file_.full_path();
    182 }
    183 
    184 bool DownloadFileImpl::InProgress() const {
    185   return file_.in_progress();
    186 }
    187 
    188 int64 DownloadFileImpl::CurrentSpeed() const {
    189   return rate_estimator_.GetCountPerSecond();
    190 }
    191 
    192 bool DownloadFileImpl::GetHash(std::string* hash) {
    193   return file_.GetHash(hash);
    194 }
    195 
    196 std::string DownloadFileImpl::GetHashState() {
    197   return file_.GetHashState();
    198 }
    199 
    200 void DownloadFileImpl::SetClientGuid(const std::string& guid) {
    201   file_.SetClientGuid(guid);
    202 }
    203 
    204 void DownloadFileImpl::StreamActive() {
    205   base::TimeTicks start(base::TimeTicks::Now());
    206   base::TimeTicks now;
    207   scoped_refptr<net::IOBuffer> incoming_data;
    208   size_t incoming_data_size = 0;
    209   size_t total_incoming_data_size = 0;
    210   size_t num_buffers = 0;
    211   ByteStreamReader::StreamState state(ByteStreamReader::STREAM_EMPTY);
    212   DownloadInterruptReason reason = DOWNLOAD_INTERRUPT_REASON_NONE;
    213   base::TimeDelta delta(
    214       base::TimeDelta::FromMilliseconds(kMaxTimeBlockingFileThreadMs));
    215 
    216   // Take care of any file local activity required.
    217   do {
    218     state = stream_reader_->Read(&incoming_data, &incoming_data_size);
    219 
    220     switch (state) {
    221       case ByteStreamReader::STREAM_EMPTY:
    222         break;
    223       case ByteStreamReader::STREAM_HAS_DATA:
    224         {
    225           ++num_buffers;
    226           base::TimeTicks write_start(base::TimeTicks::Now());
    227           reason = AppendDataToFile(
    228               incoming_data.get()->data(), incoming_data_size);
    229           disk_writes_time_ += (base::TimeTicks::Now() - write_start);
    230           bytes_seen_ += incoming_data_size;
    231           total_incoming_data_size += incoming_data_size;
    232         }
    233         break;
    234       case ByteStreamReader::STREAM_COMPLETE:
    235         {
    236           reason = static_cast<DownloadInterruptReason>(
    237               stream_reader_->GetStatus());
    238           SendUpdate();
    239           base::TimeTicks close_start(base::TimeTicks::Now());
    240           file_.Finish();
    241           base::TimeTicks now(base::TimeTicks::Now());
    242           disk_writes_time_ += (now - close_start);
    243           RecordFileBandwidth(
    244               bytes_seen_, disk_writes_time_, now - download_start_);
    245           update_timer_.reset();
    246         }
    247         break;
    248       default:
    249         NOTREACHED();
    250         break;
    251     }
    252     now = base::TimeTicks::Now();
    253   } while (state == ByteStreamReader::STREAM_HAS_DATA &&
    254            reason == DOWNLOAD_INTERRUPT_REASON_NONE &&
    255            now - start <= delta);
    256 
    257   // If we're stopping to yield the thread, post a task so we come back.
    258   if (state == ByteStreamReader::STREAM_HAS_DATA &&
    259       now - start > delta) {
    260     BrowserThread::PostTask(
    261         BrowserThread::FILE, FROM_HERE,
    262         base::Bind(&DownloadFileImpl::StreamActive,
    263                    weak_factory_.GetWeakPtr()));
    264   }
    265 
    266   if (total_incoming_data_size)
    267     RecordFileThreadReceiveBuffers(num_buffers);
    268 
    269   RecordContiguousWriteTime(now - start);
    270 
    271   // Take care of communication with our observer.
    272   if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
    273     // Error case for both upstream source and file write.
    274     // Shut down processing and signal an error to our observer.
    275     // Our observer will clean us up.
    276     stream_reader_->RegisterCallback(base::Closure());
    277     weak_factory_.InvalidateWeakPtrs();
    278     SendUpdate();                       // Make info up to date before error.
    279     BrowserThread::PostTask(
    280         BrowserThread::UI, FROM_HERE,
    281         base::Bind(&DownloadDestinationObserver::DestinationError,
    282                    observer_, reason));
    283   } else if (state == ByteStreamReader::STREAM_COMPLETE) {
    284     // Signal successful completion and shut down processing.
    285     stream_reader_->RegisterCallback(base::Closure());
    286     weak_factory_.InvalidateWeakPtrs();
    287     std::string hash;
    288     if (!GetHash(&hash) || file_.IsEmptyHash(hash))
    289       hash.clear();
    290     SendUpdate();
    291     BrowserThread::PostTask(
    292         BrowserThread::UI, FROM_HERE,
    293         base::Bind(
    294             &DownloadDestinationObserver::DestinationCompleted,
    295             observer_, hash));
    296   }
    297   if (bound_net_log_.IsLogging()) {
    298     bound_net_log_.AddEvent(
    299         net::NetLog::TYPE_DOWNLOAD_STREAM_DRAINED,
    300         base::Bind(&FileStreamDrainedNetLogCallback, total_incoming_data_size,
    301                    num_buffers));
    302   }
    303 }
    304 
    305 void DownloadFileImpl::SendUpdate() {
    306   BrowserThread::PostTask(
    307       BrowserThread::UI, FROM_HERE,
    308       base::Bind(&DownloadDestinationObserver::DestinationUpdate,
    309                  observer_, file_.bytes_so_far(), CurrentSpeed(),
    310                  GetHashState()));
    311 }
    312 
    313 // static
    314 int DownloadFile::GetNumberOfDownloadFiles() {
    315   return number_active_objects_;
    316 }
    317 
    318 }  // namespace content
    319