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