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 24namespace content { 25 26const int kUpdatePeriodMs = 500; 27const int kMaxTimeBlockingFileThreadMs = 1000; 28 29int DownloadFile::number_active_objects_ = 0; 30 31DownloadFileImpl::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 58DownloadFileImpl::~DownloadFileImpl() { 59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 60 --number_active_objects_; 61} 62 63void 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 93DownloadInterruptReason 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 106void 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 137void 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 175void DownloadFileImpl::Detach() { 176 file_.Detach(); 177} 178 179void DownloadFileImpl::Cancel() { 180 file_.Cancel(); 181} 182 183base::FilePath DownloadFileImpl::FullPath() const { 184 return file_.full_path(); 185} 186 187bool DownloadFileImpl::InProgress() const { 188 return file_.in_progress(); 189} 190 191int64 DownloadFileImpl::CurrentSpeed() const { 192 return rate_estimator_.GetCountPerSecond(); 193} 194 195bool DownloadFileImpl::GetHash(std::string* hash) { 196 return file_.GetHash(hash); 197} 198 199std::string DownloadFileImpl::GetHashState() { 200 return file_.GetHashState(); 201} 202 203void DownloadFileImpl::SetClientGuid(const std::string& guid) { 204 file_.SetClientGuid(guid); 205} 206 207void 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 308void 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 317int DownloadFile::GetNumberOfDownloadFiles() { 318 return number_active_objects_; 319} 320 321} // namespace content 322