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