• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • 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 
DownloadFileImpl(scoped_ptr<DownloadSaveInfo> save_info,const base::FilePath & default_download_directory,const GURL & url,const GURL & referrer_url,bool calculate_hash,scoped_ptr<ByteStreamReader> stream,const net::BoundNetLog & bound_net_log,base::WeakPtr<DownloadDestinationObserver> observer)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 
~DownloadFileImpl()55 DownloadFileImpl::~DownloadFileImpl() {
56   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
57   --number_active_objects_;
58 }
59 
Initialize(const InitializeCallback & callback)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 
AppendDataToFile(const char * data,size_t data_len)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 
RenameAndUniquify(const base::FilePath & full_path,const RenameCompletionCallback & callback)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 
RenameAndAnnotate(const base::FilePath & full_path,const RenameCompletionCallback & callback)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 
Detach()172 void DownloadFileImpl::Detach() {
173   file_.Detach();
174 }
175 
Cancel()176 void DownloadFileImpl::Cancel() {
177   file_.Cancel();
178 }
179 
FullPath() const180 base::FilePath DownloadFileImpl::FullPath() const {
181   return file_.full_path();
182 }
183 
InProgress() const184 bool DownloadFileImpl::InProgress() const {
185   return file_.in_progress();
186 }
187 
CurrentSpeed() const188 int64 DownloadFileImpl::CurrentSpeed() const {
189   return rate_estimator_.GetCountPerSecond();
190 }
191 
GetHash(std::string * hash)192 bool DownloadFileImpl::GetHash(std::string* hash) {
193   return file_.GetHash(hash);
194 }
195 
GetHashState()196 std::string DownloadFileImpl::GetHashState() {
197   return file_.GetHashState();
198 }
199 
SetClientGuid(const std::string & guid)200 void DownloadFileImpl::SetClientGuid(const std::string& guid) {
201   file_.SetClientGuid(guid);
202 }
203 
StreamActive()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 
SendUpdate()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
GetNumberOfDownloadFiles()314 int DownloadFile::GetNumberOfDownloadFiles() {
315   return number_active_objects_;
316 }
317 
318 }  // namespace content
319