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