download_item.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 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 "chrome/browser/download/download_item.h"
6
7#include "app/l10n_util.h"
8#include "base/file_util.h"
9#include "base/logging.h"
10#include "base/timer.h"
11#include "base/utf_string_conversions.h"
12#include "net/base/net_util.h"
13#include "chrome/browser/browser_thread.h"
14#include "chrome/browser/download/download_history.h"
15#include "chrome/browser/download/download_manager.h"
16#include "chrome/browser/download/download_prefs.h"
17#include "chrome/browser/download/download_util.h"
18#include "chrome/browser/history/download_create_info.h"
19#include "chrome/browser/platform_util.h"
20#include "chrome/browser/prefs/pref_service.h"
21#include "chrome/browser/profile.h"
22#include "chrome/common/extensions/extension.h"
23#include "chrome/common/pref_names.h"
24
25namespace {
26
27// Update frequency (milliseconds).
28const int kUpdateTimeMs = 1000;
29
30void DeleteDownloadedFile(const FilePath& path) {
31  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
32
33  // Make sure we only delete files.
34  if (!file_util::DirectoryExists(path))
35    file_util::Delete(path, false);
36}
37
38}  // namespace
39
40// Constructor for reading from the history service.
41DownloadItem::DownloadItem(DownloadManager* download_manager,
42                           const DownloadCreateInfo& info)
43    : id_(-1),
44      full_path_(info.path),
45      path_uniquifier_(0),
46      url_(info.url),
47      referrer_url_(info.referrer_url),
48      mime_type_(info.mime_type),
49      original_mime_type_(info.original_mime_type),
50      total_bytes_(info.total_bytes),
51      received_bytes_(info.received_bytes),
52      start_tick_(base::TimeTicks()),
53      state_(static_cast<DownloadState>(info.state)),
54      start_time_(info.start_time),
55      db_handle_(info.db_handle),
56      download_manager_(download_manager),
57      is_paused_(false),
58      open_when_complete_(false),
59      safety_state_(SAFE),
60      auto_opened_(false),
61      target_name_(info.original_name),
62      render_process_id_(-1),
63      request_id_(-1),
64      save_as_(false),
65      is_otr_(false),
66      is_extension_install_(info.is_extension_install),
67      name_finalized_(false),
68      is_temporary_(false),
69      opened_(false) {
70  if (state_ == IN_PROGRESS)
71    state_ = CANCELLED;
72  Init(false /* don't start progress timer */);
73}
74
75// Constructing for a regular download:
76DownloadItem::DownloadItem(DownloadManager* download_manager,
77                           const DownloadCreateInfo& info,
78                           bool is_otr)
79    : id_(info.download_id),
80      full_path_(info.path),
81      path_uniquifier_(info.path_uniquifier),
82      url_(info.url),
83      referrer_url_(info.referrer_url),
84      mime_type_(info.mime_type),
85      original_mime_type_(info.original_mime_type),
86      total_bytes_(info.total_bytes),
87      received_bytes_(0),
88      start_tick_(base::TimeTicks::Now()),
89      state_(IN_PROGRESS),
90      start_time_(info.start_time),
91      db_handle_(DownloadHistory::kUninitializedHandle),
92      download_manager_(download_manager),
93      is_paused_(false),
94      open_when_complete_(false),
95      safety_state_(info.is_dangerous ? DANGEROUS : SAFE),
96      auto_opened_(false),
97      target_name_(info.original_name),
98      render_process_id_(info.child_id),
99      request_id_(info.request_id),
100      save_as_(info.prompt_user_for_save_location),
101      is_otr_(is_otr),
102      is_extension_install_(info.is_extension_install),
103      name_finalized_(false),
104      is_temporary_(!info.save_info.file_path.empty()),
105      opened_(false) {
106  Init(true /* start progress timer */);
107}
108
109// Constructing for the "Save Page As..." feature:
110DownloadItem::DownloadItem(DownloadManager* download_manager,
111                           const FilePath& path,
112                           const GURL& url,
113                           bool is_otr)
114    : id_(1),
115      full_path_(path),
116      path_uniquifier_(0),
117      url_(url),
118      referrer_url_(GURL()),
119      mime_type_(std::string()),
120      original_mime_type_(std::string()),
121      total_bytes_(0),
122      received_bytes_(0),
123      start_tick_(base::TimeTicks::Now()),
124      state_(IN_PROGRESS),
125      start_time_(base::Time::Now()),
126      db_handle_(DownloadHistory::kUninitializedHandle),
127      download_manager_(download_manager),
128      is_paused_(false),
129      open_when_complete_(false),
130      safety_state_(SAFE),
131      auto_opened_(false),
132      render_process_id_(-1),
133      request_id_(-1),
134      save_as_(false),
135      is_otr_(is_otr),
136      is_extension_install_(false),
137      name_finalized_(false),
138      is_temporary_(false),
139      opened_(false) {
140  Init(true /* start progress timer */);
141}
142
143DownloadItem::~DownloadItem() {
144  state_ = REMOVING;
145  UpdateObservers();
146}
147
148void DownloadItem::AddObserver(Observer* observer) {
149  observers_.AddObserver(observer);
150}
151
152void DownloadItem::RemoveObserver(Observer* observer) {
153  observers_.RemoveObserver(observer);
154}
155
156void DownloadItem::UpdateObservers() {
157  FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this));
158}
159
160void DownloadItem::NotifyObserversDownloadFileCompleted() {
161  FOR_EACH_OBSERVER(Observer, observers_, OnDownloadFileCompleted(this));
162}
163
164bool DownloadItem::CanOpenDownload() {
165  return !Extension::IsExtension(target_name_) &&
166      !download_util::IsExecutableFile(target_name_);
167}
168
169bool DownloadItem::ShouldOpenFileBasedOnExtension() {
170  return download_manager_->ShouldOpenFileBasedOnExtension(
171      GetUserVerifiedFileName());
172}
173
174void DownloadItem::OpenFilesBasedOnExtension(bool open) {
175  DownloadPrefs* prefs = download_manager_->download_prefs();
176  if (open)
177    prefs->EnableAutoOpenBasedOnExtension(GetUserVerifiedFileName());
178  else
179    prefs->DisableAutoOpenBasedOnExtension(GetUserVerifiedFileName());
180}
181
182void DownloadItem::OpenDownload() {
183  if (state() == DownloadItem::IN_PROGRESS) {
184    open_when_complete_ = !open_when_complete_;
185  } else if (state() == DownloadItem::COMPLETE) {
186    opened_ = true;
187    FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this));
188    if (is_extension_install()) {
189      download_util::OpenChromeExtension(download_manager_->profile(),
190                                         download_manager_,
191                                         *this);
192      return;
193    }
194#if defined(OS_MACOSX)
195    // Mac OS X requires opening downloads on the UI thread.
196    platform_util::OpenItem(full_path());
197#else
198    BrowserThread::PostTask(
199        BrowserThread::FILE, FROM_HERE,
200        NewRunnableFunction(&platform_util::OpenItem, full_path()));
201#endif
202  }
203}
204
205void DownloadItem::ShowDownloadInShell() {
206#if defined(OS_MACOSX)
207  // Mac needs to run this operation on the UI thread.
208  platform_util::ShowItemInFolder(full_path());
209#else
210  BrowserThread::PostTask(
211      BrowserThread::FILE, FROM_HERE,
212      NewRunnableFunction(&platform_util::ShowItemInFolder,
213                          full_path()));
214#endif
215}
216
217void DownloadItem::DangerousDownloadValidated() {
218  download_manager_->DangerousDownloadValidated(this);
219}
220
221void DownloadItem::UpdateSize(int64 bytes_so_far) {
222  received_bytes_ = bytes_so_far;
223
224  // If we've received more data than we were expecting (bad server info?),
225  // revert to 'unknown size mode'.
226  if (received_bytes_ > total_bytes_)
227    total_bytes_ = 0;
228}
229
230void DownloadItem::StartProgressTimer() {
231  update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdateTimeMs), this,
232                      &DownloadItem::UpdateObservers);
233}
234
235void DownloadItem::StopProgressTimer() {
236  update_timer_.Stop();
237}
238
239// Updates from the download thread may have been posted while this download
240// was being cancelled in the UI thread, so we'll accept them unless we're
241// complete.
242void DownloadItem::Update(int64 bytes_so_far) {
243  if (state_ == COMPLETE) {
244    NOTREACHED();
245    return;
246  }
247  UpdateSize(bytes_so_far);
248  UpdateObservers();
249}
250
251// Triggered by a user action.
252void DownloadItem::Cancel(bool update_history) {
253  if (state_ != IN_PROGRESS) {
254    // Small downloads might be complete before this method has a chance to run.
255    return;
256  }
257  state_ = CANCELLED;
258  UpdateObservers();
259  StopProgressTimer();
260  if (update_history)
261    download_manager_->DownloadCancelled(id_);
262}
263
264void DownloadItem::OnAllDataSaved(int64 size) {
265  state_ = COMPLETE;
266  UpdateSize(size);
267  StopProgressTimer();
268}
269
270void DownloadItem::Finished() {
271  // Handle chrome extensions explicitly and skip the shell execute.
272  if (is_extension_install()) {
273    download_util::OpenChromeExtension(download_manager_->profile(),
274                                       download_manager_,
275                                       *this);
276    auto_opened_ = true;
277  } else if (open_when_complete() ||
278             download_manager_->ShouldOpenFileBasedOnExtension(
279                 GetUserVerifiedFileName()) ||
280             is_temporary()) {
281    // If the download is temporary, like in drag-and-drop, do not open it but
282    // we still need to set it auto-opened so that it can be removed from the
283    // download shelf.
284    if (!is_temporary())
285      OpenDownload();
286    auto_opened_ = true;
287  }
288
289  // Notify our observers that we are complete (the call to OnAllDataSaved()
290  // set the state to complete but did not notify).
291  UpdateObservers();
292
293  // The download file is meant to be completed if both the filename is
294  // finalized and the file data is downloaded. The ordering of these two
295  // actions is indeterministic. Thus, if the filename is not finalized yet,
296  // delay the notification.
297  if (name_finalized())
298    NotifyObserversDownloadFileCompleted();
299}
300
301void DownloadItem::Remove(bool delete_on_disk) {
302  Cancel(true);
303  state_ = REMOVING;
304  if (delete_on_disk) {
305    BrowserThread::PostTask(
306        BrowserThread::FILE, FROM_HERE,
307        NewRunnableFunction(&DeleteDownloadedFile, full_path_));
308  }
309  download_manager_->RemoveDownload(db_handle_);
310  // We have now been deleted.
311}
312
313bool DownloadItem::TimeRemaining(base::TimeDelta* remaining) const {
314  if (total_bytes_ <= 0)
315    return false;  // We never received the content_length for this download.
316
317  int64 speed = CurrentSpeed();
318  if (speed == 0)
319    return false;
320
321  *remaining =
322      base::TimeDelta::FromSeconds((total_bytes_ - received_bytes_) / speed);
323  return true;
324}
325
326int64 DownloadItem::CurrentSpeed() const {
327  base::TimeDelta diff = base::TimeTicks::Now() - start_tick_;
328  int64 diff_ms = diff.InMilliseconds();
329  return diff_ms == 0 ? 0 : received_bytes_ * 1000 / diff_ms;
330}
331
332int DownloadItem::PercentComplete() const {
333  int percent = -1;
334  if (total_bytes_ > 0)
335    percent = static_cast<int>(received_bytes_ * 100.0 / total_bytes_);
336  return percent;
337}
338
339void DownloadItem::Rename(const FilePath& full_path) {
340  DCHECK(!full_path.empty());
341  full_path_ = full_path;
342}
343
344void DownloadItem::TogglePause() {
345  DCHECK(state_ == IN_PROGRESS);
346  download_manager_->PauseDownload(id_, !is_paused_);
347  is_paused_ = !is_paused_;
348  UpdateObservers();
349}
350
351void DownloadItem::OnNameFinalized() {
352  name_finalized_ = true;
353
354  // The download file is meant to be completed if both the filename is
355  // finalized and the file data is downloaded. The ordering of these two
356  // actions is indeterministic. Thus, if we are still in downloading the
357  // file, delay the notification.
358  if (state() == DownloadItem::COMPLETE)
359    NotifyObserversDownloadFileCompleted();
360}
361
362bool DownloadItem::MatchesQuery(const string16& query) const {
363  if (query.empty())
364    return true;
365
366  DCHECK_EQ(query, l10n_util::ToLower(query));
367
368  string16 url_raw(l10n_util::ToLower(UTF8ToUTF16(url_.spec())));
369  if (url_raw.find(query) != string16::npos)
370    return true;
371
372  // TODO(phajdan.jr): write a test case for the following code.
373  // A good test case would be:
374  //   "/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd",
375  //   L"/\x4f60\x597d\x4f60\x597d",
376  //   "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD"
377  PrefService* prefs = download_manager_->profile()->GetPrefs();
378  std::string languages(prefs->GetString(prefs::kAcceptLanguages));
379  string16 url_formatted(l10n_util::ToLower(net::FormatUrl(url_, languages)));
380  if (url_formatted.find(query) != string16::npos)
381    return true;
382
383  string16 path(l10n_util::ToLower(WideToUTF16(full_path().ToWStringHack())));
384  if (path.find(query) != std::wstring::npos)
385    return true;
386
387  return false;
388}
389
390FilePath DownloadItem::GetTargetFilePath() const {
391  return full_path_.DirName().Append(target_name_);
392}
393
394FilePath DownloadItem::GetFileNameToReportUser() const {
395  if (path_uniquifier_ > 0) {
396    FilePath name(target_name_);
397    download_util::AppendNumberToPath(&name, path_uniquifier_);
398    return name;
399  }
400  return target_name_;
401}
402
403FilePath DownloadItem::GetUserVerifiedFileName() const {
404  if (safety_state_ == DownloadItem::SAFE)
405    return target_name_;
406  return full_path_.BaseName();
407}
408
409void DownloadItem::Init(bool start_timer) {
410  if (target_name_.value().empty())
411    target_name_ = full_path_.BaseName();
412  if (start_timer)
413    StartProgressTimer();
414}
415