download_item_impl.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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// File method ordering: Methods in this file are in the same order as
6// in download_item_impl.h, with the following exception: The public
7// interface Start is placed in chronological order with the other
8// (private) routines that together define a DownloadItem's state
9// transitions as the download progresses.  See "Download progression
10// cascade" later in this file.
11
12// A regular DownloadItem (created for a download in this session of the
13// browser) normally goes through the following states:
14//      * Created (when download starts)
15//      * Destination filename determined
16//      * Entered into the history database.
17//      * Made visible in the download shelf.
18//      * All the data is saved.  Note that the actual data download occurs
19//        in parallel with the above steps, but until those steps are
20//        complete, the state of the data save will be ignored.
21//      * Download file is renamed to its final name, and possibly
22//        auto-opened.
23
24#include "content/browser/download/download_item_impl.h"
25
26#include <vector>
27
28#include "base/basictypes.h"
29#include "base/bind.h"
30#include "base/command_line.h"
31#include "base/file_util.h"
32#include "base/format_macros.h"
33#include "base/logging.h"
34#include "base/metrics/histogram.h"
35#include "base/stl_util.h"
36#include "base/strings/stringprintf.h"
37#include "base/strings/utf_string_conversions.h"
38#include "content/browser/download/download_create_info.h"
39#include "content/browser/download/download_file.h"
40#include "content/browser/download/download_interrupt_reasons_impl.h"
41#include "content/browser/download/download_item_impl_delegate.h"
42#include "content/browser/download/download_request_handle.h"
43#include "content/browser/download/download_stats.h"
44#include "content/browser/renderer_host/render_view_host_impl.h"
45#include "content/browser/web_contents/web_contents_impl.h"
46#include "content/public/browser/browser_context.h"
47#include "content/public/browser/browser_thread.h"
48#include "content/public/browser/content_browser_client.h"
49#include "content/public/browser/download_danger_type.h"
50#include "content/public/browser/download_interrupt_reasons.h"
51#include "content/public/browser/download_url_parameters.h"
52#include "content/public/common/content_switches.h"
53#include "content/public/common/referrer.h"
54#include "net/base/net_util.h"
55
56namespace content {
57
58namespace {
59
60void DeleteDownloadedFile(const base::FilePath& path) {
61  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
62
63  // Make sure we only delete files.
64  if (!file_util::DirectoryExists(path))
65    file_util::Delete(path, false);
66}
67
68// Wrapper around DownloadFile::Detach and DownloadFile::Cancel that
69// takes ownership of the DownloadFile and hence implicitly destroys it
70// at the end of the function.
71static base::FilePath DownloadFileDetach(
72    scoped_ptr<DownloadFile> download_file) {
73  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
74  base::FilePath full_path = download_file->FullPath();
75  download_file->Detach();
76  return full_path;
77}
78
79static void DownloadFileCancel(scoped_ptr<DownloadFile> download_file) {
80  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
81  download_file->Cancel();
82}
83
84bool IsDownloadResumptionEnabled() {
85  return CommandLine::ForCurrentProcess()->HasSwitch(
86      switches::kEnableDownloadResumption);
87}
88
89}  // namespace
90
91const char DownloadItem::kEmptyFileHash[] = "";
92
93// The maximum number of attempts we will make to resume automatically.
94const int DownloadItemImpl::kMaxAutoResumeAttempts = 5;
95
96// Constructor for reading from the history service.
97DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate,
98                                   DownloadId download_id,
99                                   const base::FilePath& current_path,
100                                   const base::FilePath& target_path,
101                                   const std::vector<GURL>& url_chain,
102                                   const GURL& referrer_url,
103                                   const base::Time& start_time,
104                                   const base::Time& end_time,
105                                   int64 received_bytes,
106                                   int64 total_bytes,
107                                   DownloadItem::DownloadState state,
108                                   DownloadDangerType danger_type,
109                                   DownloadInterruptReason interrupt_reason,
110                                   bool opened,
111                                   const net::BoundNetLog& bound_net_log)
112    : is_save_package_download_(false),
113      download_id_(download_id),
114      current_path_(current_path),
115      target_path_(target_path),
116      target_disposition_(TARGET_DISPOSITION_OVERWRITE),
117      url_chain_(url_chain),
118      referrer_url_(referrer_url),
119      transition_type_(PAGE_TRANSITION_LINK),
120      has_user_gesture_(false),
121      total_bytes_(total_bytes),
122      received_bytes_(received_bytes),
123      bytes_per_sec_(0),
124      last_reason_(interrupt_reason),
125      start_tick_(base::TimeTicks()),
126      state_(ExternalToInternalState(state)),
127      danger_type_(danger_type),
128      start_time_(start_time),
129      end_time_(end_time),
130      delegate_(delegate),
131      is_paused_(false),
132      auto_resume_count_(0),
133      open_when_complete_(false),
134      file_externally_removed_(false),
135      auto_opened_(false),
136      is_temporary_(false),
137      all_data_saved_(state == COMPLETE),
138      destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE),
139      opened_(opened),
140      delegate_delayed_complete_(false),
141      bound_net_log_(bound_net_log),
142      weak_ptr_factory_(this) {
143  delegate_->Attach();
144  DCHECK_NE(IN_PROGRESS_INTERNAL, state_);
145  Init(false /* not actively downloading */, SRC_HISTORY_IMPORT);
146}
147
148// Constructing for a regular download:
149DownloadItemImpl::DownloadItemImpl(
150    DownloadItemImplDelegate* delegate,
151    DownloadId download_id,
152    const DownloadCreateInfo& info,
153    const net::BoundNetLog& bound_net_log)
154    : is_save_package_download_(false),
155      download_id_(download_id),
156      target_disposition_(
157          (info.save_info->prompt_for_save_location) ?
158              TARGET_DISPOSITION_PROMPT : TARGET_DISPOSITION_OVERWRITE),
159      url_chain_(info.url_chain),
160      referrer_url_(info.referrer_url),
161      suggested_filename_(UTF16ToUTF8(info.save_info->suggested_name)),
162      forced_file_path_(info.save_info->file_path),
163      transition_type_(info.transition_type),
164      has_user_gesture_(info.has_user_gesture),
165      content_disposition_(info.content_disposition),
166      mime_type_(info.mime_type),
167      original_mime_type_(info.original_mime_type),
168      remote_address_(info.remote_address),
169      total_bytes_(info.total_bytes),
170      received_bytes_(0),
171      bytes_per_sec_(0),
172      last_modified_time_(info.last_modified),
173      etag_(info.etag),
174      last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE),
175      start_tick_(base::TimeTicks::Now()),
176      state_(IN_PROGRESS_INTERNAL),
177      danger_type_(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS),
178      start_time_(info.start_time),
179      delegate_(delegate),
180      is_paused_(false),
181      auto_resume_count_(0),
182      open_when_complete_(false),
183      file_externally_removed_(false),
184      auto_opened_(false),
185      is_temporary_(!info.save_info->file_path.empty()),
186      all_data_saved_(false),
187      destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE),
188      opened_(false),
189      delegate_delayed_complete_(false),
190      bound_net_log_(bound_net_log),
191      weak_ptr_factory_(this) {
192  delegate_->Attach();
193  Init(true /* actively downloading */, SRC_ACTIVE_DOWNLOAD);
194
195  // Link the event sources.
196  bound_net_log_.AddEvent(
197      net::NetLog::TYPE_DOWNLOAD_URL_REQUEST,
198      info.request_bound_net_log.source().ToEventParametersCallback());
199
200  info.request_bound_net_log.AddEvent(
201      net::NetLog::TYPE_DOWNLOAD_STARTED,
202      bound_net_log_.source().ToEventParametersCallback());
203}
204
205// Constructing for the "Save Page As..." feature:
206DownloadItemImpl::DownloadItemImpl(
207    DownloadItemImplDelegate* delegate,
208    DownloadId download_id,
209    const base::FilePath& path,
210    const GURL& url,
211    const std::string& mime_type,
212    scoped_ptr<DownloadRequestHandleInterface> request_handle,
213    const net::BoundNetLog& bound_net_log)
214    : is_save_package_download_(true),
215      request_handle_(request_handle.Pass()),
216      download_id_(download_id),
217      current_path_(path),
218      target_path_(path),
219      target_disposition_(TARGET_DISPOSITION_OVERWRITE),
220      url_chain_(1, url),
221      referrer_url_(GURL()),
222      transition_type_(PAGE_TRANSITION_LINK),
223      has_user_gesture_(false),
224      mime_type_(mime_type),
225      original_mime_type_(mime_type),
226      total_bytes_(0),
227      received_bytes_(0),
228      bytes_per_sec_(0),
229      last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE),
230      start_tick_(base::TimeTicks::Now()),
231      state_(IN_PROGRESS_INTERNAL),
232      danger_type_(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS),
233      start_time_(base::Time::Now()),
234      delegate_(delegate),
235      is_paused_(false),
236      auto_resume_count_(0),
237      open_when_complete_(false),
238      file_externally_removed_(false),
239      auto_opened_(false),
240      is_temporary_(false),
241      all_data_saved_(false),
242      destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE),
243      opened_(false),
244      delegate_delayed_complete_(false),
245      bound_net_log_(bound_net_log),
246      weak_ptr_factory_(this) {
247  delegate_->Attach();
248  Init(true /* actively downloading */, SRC_SAVE_PAGE_AS);
249}
250
251DownloadItemImpl::~DownloadItemImpl() {
252  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
253
254  // Should always have been nuked before now, at worst in
255  // DownloadManager shutdown.
256  DCHECK(!download_file_.get());
257
258  FOR_EACH_OBSERVER(Observer, observers_, OnDownloadDestroyed(this));
259  delegate_->AssertStateConsistent(this);
260  delegate_->Detach();
261}
262
263void DownloadItemImpl::AddObserver(Observer* observer) {
264  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
265
266  observers_.AddObserver(observer);
267}
268
269void DownloadItemImpl::RemoveObserver(Observer* observer) {
270  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271
272  observers_.RemoveObserver(observer);
273}
274
275void DownloadItemImpl::UpdateObservers() {
276  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
277
278  FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this));
279}
280
281void DownloadItemImpl::ValidateDangerousDownload() {
282  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
283  DCHECK(!IsDone());
284  DCHECK(IsDangerous());
285
286  VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
287
288  if (IsDone() || !IsDangerous())
289    return;
290
291  RecordDangerousDownloadAccept(GetDangerType());
292
293  danger_type_ = DOWNLOAD_DANGER_TYPE_USER_VALIDATED;
294
295  bound_net_log_.AddEvent(
296      net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED,
297      base::Bind(&ItemCheckedNetLogCallback, GetDangerType()));
298
299  UpdateObservers();
300
301  MaybeCompleteDownload();
302}
303
304void DownloadItemImpl::StealDangerousDownload(
305    const AcquireFileCallback& callback) {
306  VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true);
307  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
308  DCHECK(IsDangerous());
309  if (download_file_) {
310    BrowserThread::PostTaskAndReplyWithResult(
311        BrowserThread::FILE,
312        FROM_HERE,
313        base::Bind(&DownloadFileDetach, base::Passed(&download_file_)),
314        callback);
315  } else {
316    callback.Run(current_path_);
317  }
318  current_path_.clear();
319  Remove();
320  // We have now been deleted.
321}
322
323void DownloadItemImpl::Pause() {
324  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
325
326  // Ignore irrelevant states.
327  if (state_ != IN_PROGRESS_INTERNAL || is_paused_)
328    return;
329
330  request_handle_->PauseRequest();
331  is_paused_ = true;
332  UpdateObservers();
333}
334
335void DownloadItemImpl::Resume() {
336  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
337  switch (state_) {
338    case IN_PROGRESS_INTERNAL:
339      if (!is_paused_)
340        return;
341      request_handle_->ResumeRequest();
342      is_paused_ = false;
343      UpdateObservers();
344      return;
345
346    case COMPLETING_INTERNAL:
347    case COMPLETE_INTERNAL:
348    case CANCELLED_INTERNAL:
349    case RESUMING_INTERNAL:
350      return;
351
352    case INTERRUPTED_INTERNAL:
353      auto_resume_count_ = 0;  // User input resets the counter.
354      ResumeInterruptedDownload();
355      return;
356
357    case MAX_DOWNLOAD_INTERNAL_STATE:
358      NOTREACHED();
359  }
360}
361
362void DownloadItemImpl::Cancel(bool user_cancel) {
363  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
364
365  VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true);
366  if (state_ != IN_PROGRESS_INTERNAL &&
367      state_ != INTERRUPTED_INTERNAL &&
368      state_ != RESUMING_INTERNAL) {
369    // Small downloads might be complete before this method has a chance to run.
370    return;
371  }
372
373  if (IsDangerous()) {
374    RecordDangerousDownloadDiscard(
375        user_cancel ? DOWNLOAD_DISCARD_DUE_TO_USER_ACTION
376                    : DOWNLOAD_DISCARD_DUE_TO_SHUTDOWN,
377        GetDangerType());
378  }
379
380  last_reason_ = user_cancel ? DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
381                             : DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN;
382
383  RecordDownloadCount(CANCELLED_COUNT);
384
385  // TODO(rdsmith/benjhayden): Remove condition as part of
386  // |SavePackage| integration.
387  // |download_file_| can be NULL if Interrupt() is called after the
388  // download file has been released.
389  if (!is_save_package_download_ && download_file_)
390    ReleaseDownloadFile(true);
391
392  if (state_ == IN_PROGRESS_INTERNAL) {
393    // Cancel the originating URL request unless it's already been cancelled
394    // by interrupt.
395    request_handle_->CancelRequest();
396  }
397
398  // Remove the intermediate file if we are cancelling an interrupted download.
399  // Continuable interruptions leave the intermediate file around.
400  if ((state_ == INTERRUPTED_INTERNAL || state_ == RESUMING_INTERNAL) &&
401      !current_path_.empty()) {
402    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
403                            base::Bind(&DeleteDownloadedFile, current_path_));
404    current_path_.clear();
405  }
406
407  TransitionTo(CANCELLED_INTERNAL);
408}
409
410void DownloadItemImpl::Remove() {
411  VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true);
412  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
413
414  delegate_->AssertStateConsistent(this);
415  Cancel(true);
416  delegate_->AssertStateConsistent(this);
417
418  NotifyRemoved();
419  delegate_->DownloadRemoved(this);
420  // We have now been deleted.
421}
422
423void DownloadItemImpl::OpenDownload() {
424  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
425
426  if (!IsDone()) {
427    // We don't honor the open_when_complete_ flag for temporary
428    // downloads. Don't set it because it shows up in the UI.
429    if (!IsTemporary())
430      open_when_complete_ = !open_when_complete_;
431    return;
432  }
433
434  if (state_ != COMPLETE_INTERNAL || file_externally_removed_)
435    return;
436
437  // Ideally, we want to detect errors in opening and report them, but we
438  // don't generally have the proper interface for that to the external
439  // program that opens the file.  So instead we spawn a check to update
440  // the UI if the file has been deleted in parallel with the open.
441  delegate_->CheckForFileRemoval(this);
442  RecordOpen(GetEndTime(), !GetOpened());
443  opened_ = true;
444  FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this));
445  delegate_->OpenDownload(this);
446}
447
448void DownloadItemImpl::ShowDownloadInShell() {
449  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
450
451  delegate_->ShowDownloadInShell(this);
452}
453
454int32 DownloadItemImpl::GetId() const {
455  return download_id_.local();
456}
457
458DownloadId DownloadItemImpl::GetGlobalId() const {
459  return download_id_;
460}
461
462DownloadItem::DownloadState DownloadItemImpl::GetState() const {
463  return InternalToExternalState(state_);
464}
465
466DownloadInterruptReason DownloadItemImpl::GetLastReason() const {
467  return last_reason_;
468}
469
470bool DownloadItemImpl::IsPaused() const {
471  return is_paused_;
472}
473
474bool DownloadItemImpl::IsTemporary() const {
475  return is_temporary_;
476}
477
478bool DownloadItemImpl::CanResume() const {
479  if (IsInProgress() && IsPaused())
480    return true;
481
482  if (state_ != INTERRUPTED_INTERNAL)
483    return false;
484
485  // Downloads that don't have a WebContents should still be resumable, but this
486  // isn't currently the case. See ResumeInterruptedDownload().
487  if (!GetWebContents())
488    return false;
489
490  ResumeMode resume_mode = GetResumeMode();
491  return IsDownloadResumptionEnabled() &&
492      (resume_mode == RESUME_MODE_USER_RESTART ||
493       resume_mode == RESUME_MODE_USER_CONTINUE);
494}
495
496bool DownloadItemImpl::IsDone() const {
497  switch (state_) {
498    case IN_PROGRESS_INTERNAL:
499    case COMPLETING_INTERNAL:
500      return false;
501
502    case COMPLETE_INTERNAL:
503    case CANCELLED_INTERNAL:
504      return true;
505
506    case INTERRUPTED_INTERNAL:
507      return !CanResume();
508
509    case RESUMING_INTERNAL:
510      return false;
511
512    case MAX_DOWNLOAD_INTERNAL_STATE:
513      break;
514  }
515  NOTREACHED();
516  return true;
517}
518
519bool DownloadItemImpl::IsInProgress() const {
520  return InternalToExternalState(state_) == IN_PROGRESS;
521}
522
523bool DownloadItemImpl::IsCancelled() const {
524  return  InternalToExternalState(state_) == CANCELLED;
525}
526
527bool DownloadItemImpl::IsInterrupted() const {
528  return InternalToExternalState(state_) == INTERRUPTED;
529}
530
531bool DownloadItemImpl::IsComplete() const {
532  return InternalToExternalState(state_) == COMPLETE;
533}
534
535const GURL& DownloadItemImpl::GetURL() const {
536  return url_chain_.empty() ? GURL::EmptyGURL() : url_chain_.back();
537}
538
539const std::vector<GURL>& DownloadItemImpl::GetUrlChain() const {
540  return url_chain_;
541}
542
543const GURL& DownloadItemImpl::GetOriginalUrl() const {
544  // Be careful about taking the front() of possibly-empty vectors!
545  // http://crbug.com/190096
546  return url_chain_.empty() ? GURL::EmptyGURL() : url_chain_.front();
547}
548
549const GURL& DownloadItemImpl::GetReferrerUrl() const {
550  return referrer_url_;
551}
552
553std::string DownloadItemImpl::GetSuggestedFilename() const {
554  return suggested_filename_;
555}
556
557std::string DownloadItemImpl::GetContentDisposition() const {
558  return content_disposition_;
559}
560
561std::string DownloadItemImpl::GetMimeType() const {
562  return mime_type_;
563}
564
565std::string DownloadItemImpl::GetOriginalMimeType() const {
566  return original_mime_type_;
567}
568
569std::string DownloadItemImpl::GetRemoteAddress() const {
570  return remote_address_;
571}
572
573bool DownloadItemImpl::HasUserGesture() const {
574  return has_user_gesture_;
575};
576
577PageTransition DownloadItemImpl::GetTransitionType() const {
578  return transition_type_;
579};
580
581const std::string& DownloadItemImpl::GetLastModifiedTime() const {
582  return last_modified_time_;
583}
584
585const std::string& DownloadItemImpl::GetETag() const {
586  return etag_;
587}
588
589bool DownloadItemImpl::IsSavePackageDownload() const {
590  return is_save_package_download_;
591}
592
593const base::FilePath& DownloadItemImpl::GetFullPath() const {
594  return current_path_;
595}
596
597const base::FilePath& DownloadItemImpl::GetTargetFilePath() const {
598  return target_path_;
599}
600
601const base::FilePath& DownloadItemImpl::GetForcedFilePath() const {
602  // TODO(asanka): Get rid of GetForcedFilePath(). We should instead just
603  // require that clients respect GetTargetFilePath() if it is already set.
604  return forced_file_path_;
605}
606
607base::FilePath DownloadItemImpl::GetFileNameToReportUser() const {
608  if (!display_name_.empty())
609    return display_name_;
610  return target_path_.BaseName();
611}
612
613DownloadItem::TargetDisposition DownloadItemImpl::GetTargetDisposition() const {
614  return target_disposition_;
615}
616
617const std::string& DownloadItemImpl::GetHash() const {
618  return hash_;
619}
620
621const std::string& DownloadItemImpl::GetHashState() const {
622  return hash_state_;
623}
624
625bool DownloadItemImpl::GetFileExternallyRemoved() const {
626  return file_externally_removed_;
627}
628
629bool DownloadItemImpl::IsDangerous() const {
630#if defined(OS_WIN)
631  // TODO(noelutz): At this point only the windows views UI supports
632  // warnings based on dangerous content.
633  return (danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
634          danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
635          danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
636          danger_type_ == DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT ||
637          danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST);
638#else
639  return (danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
640          danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL);
641#endif
642}
643
644DownloadDangerType DownloadItemImpl::GetDangerType() const {
645  return danger_type_;
646}
647
648bool DownloadItemImpl::TimeRemaining(base::TimeDelta* remaining) const {
649  if (total_bytes_ <= 0)
650    return false;  // We never received the content_length for this download.
651
652  int64 speed = CurrentSpeed();
653  if (speed == 0)
654    return false;
655
656  *remaining = base::TimeDelta::FromSeconds(
657      (total_bytes_ - received_bytes_) / speed);
658  return true;
659}
660
661int64 DownloadItemImpl::CurrentSpeed() const {
662  if (is_paused_)
663    return 0;
664  return bytes_per_sec_;
665}
666
667int DownloadItemImpl::PercentComplete() const {
668  // If the delegate is delaying completion of the download, then we have no
669  // idea how long it will take.
670  if (delegate_delayed_complete_ || total_bytes_ <= 0)
671    return -1;
672
673  return static_cast<int>(received_bytes_ * 100.0 / total_bytes_);
674}
675
676bool DownloadItemImpl::AllDataSaved() const {
677  return all_data_saved_;
678}
679
680int64 DownloadItemImpl::GetTotalBytes() const {
681  return total_bytes_;
682}
683
684int64 DownloadItemImpl::GetReceivedBytes() const {
685  return received_bytes_;
686}
687
688base::Time DownloadItemImpl::GetStartTime() const {
689  return start_time_;
690}
691
692base::Time DownloadItemImpl::GetEndTime() const {
693  return end_time_;
694}
695
696bool DownloadItemImpl::CanShowInFolder() {
697  // A download can be shown in the folder if the downloaded file is in a known
698  // location.
699  return CanOpenDownload() && !GetFullPath().empty();
700}
701
702bool DownloadItemImpl::CanOpenDownload() {
703  // We can open the file or mark it for opening on completion if the download
704  // is expected to complete successfully. Exclude temporary downloads, since
705  // they aren't owned by the download system.
706  return (!IsDone() || IsComplete()) && !IsTemporary() &&
707         !file_externally_removed_;
708}
709
710bool DownloadItemImpl::ShouldOpenFileBasedOnExtension() {
711  return delegate_->ShouldOpenFileBasedOnExtension(GetTargetFilePath());
712}
713
714bool DownloadItemImpl::GetOpenWhenComplete() const {
715  return open_when_complete_;
716}
717
718bool DownloadItemImpl::GetAutoOpened() {
719  return auto_opened_;
720}
721
722bool DownloadItemImpl::GetOpened() const {
723  return opened_;
724}
725
726BrowserContext* DownloadItemImpl::GetBrowserContext() const {
727  return delegate_->GetBrowserContext();
728}
729
730WebContents* DownloadItemImpl::GetWebContents() const {
731  // TODO(rdsmith): Remove null check after removing GetWebContents() from
732  // paths that might be used by DownloadItems created from history import.
733  // Currently such items have null request_handle_s, where other items
734  // (regular and SavePackage downloads) have actual objects off the pointer.
735  if (request_handle_)
736    return request_handle_->GetWebContents();
737  return NULL;
738}
739
740void DownloadItemImpl::OnContentCheckCompleted(DownloadDangerType danger_type) {
741  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
742  DCHECK(AllDataSaved());
743  VLOG(20) << __FUNCTION__ << " danger_type=" << danger_type
744           << " download=" << DebugString(true);
745  SetDangerType(danger_type);
746  UpdateObservers();
747}
748
749void DownloadItemImpl::SetOpenWhenComplete(bool open) {
750  open_when_complete_ = open;
751}
752
753void DownloadItemImpl::SetIsTemporary(bool temporary) {
754  is_temporary_ = temporary;
755}
756
757void DownloadItemImpl::SetOpened(bool opened) {
758  opened_ = opened;
759}
760
761void DownloadItemImpl::SetDisplayName(const base::FilePath& name) {
762  display_name_ = name;
763}
764
765std::string DownloadItemImpl::DebugString(bool verbose) const {
766  std::string description =
767      base::StringPrintf("{ id = %d"
768                         " state = %s",
769                         download_id_.local(),
770                         DebugDownloadStateString(state_));
771
772  // Construct a string of the URL chain.
773  std::string url_list("<none>");
774  if (!url_chain_.empty()) {
775    std::vector<GURL>::const_iterator iter = url_chain_.begin();
776    std::vector<GURL>::const_iterator last = url_chain_.end();
777    url_list = (*iter).is_valid() ? (*iter).spec() : "<invalid>";
778    ++iter;
779    for ( ; verbose && (iter != last); ++iter) {
780      url_list += " ->\n\t";
781      const GURL& next_url = *iter;
782      url_list += next_url.is_valid() ? next_url.spec() : "<invalid>";
783    }
784  }
785
786  if (verbose) {
787    description += base::StringPrintf(
788        " total = %" PRId64
789        " received = %" PRId64
790        " reason = %s"
791        " paused = %c"
792        " resume_mode = %s"
793        " auto_resume_count = %d"
794        " danger = %d"
795        " all_data_saved = %c"
796        " last_modified = '%s'"
797        " etag = '%s'"
798        " has_download_file = %s"
799        " url_chain = \n\t\"%s\"\n\t"
800        " full_path = \"%" PRFilePath "\"\n\t"
801        " target_path = \"%" PRFilePath "\"",
802        GetTotalBytes(),
803        GetReceivedBytes(),
804        InterruptReasonDebugString(last_reason_).c_str(),
805        IsPaused() ? 'T' : 'F',
806        DebugResumeModeString(GetResumeMode()),
807        auto_resume_count_,
808        GetDangerType(),
809        AllDataSaved() ? 'T' : 'F',
810        GetLastModifiedTime().c_str(),
811        GetETag().c_str(),
812        download_file_.get() ? "true" : "false",
813        url_list.c_str(),
814        GetFullPath().value().c_str(),
815        GetTargetFilePath().value().c_str());
816  } else {
817    description += base::StringPrintf(" url = \"%s\"", url_list.c_str());
818  }
819
820  description += " }";
821
822  return description;
823}
824
825DownloadItemImpl::ResumeMode DownloadItemImpl::GetResumeMode() const {
826  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
827  // We can't continue without a handle on the intermediate file.
828  // We also can't continue if we don't have some verifier to make sure
829  // we're getting the same file.
830  const bool force_restart =
831      (current_path_.empty() || (etag_.empty() && last_modified_time_.empty()));
832
833  // We won't auto-restart if we've used up our attempts or the
834  // download has been paused by user action.
835  const bool force_user =
836      (auto_resume_count_ >= kMaxAutoResumeAttempts || is_paused_);
837
838  ResumeMode mode = RESUME_MODE_INVALID;
839
840  switch(last_reason_) {
841    case DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR:
842    case DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT:
843      if (force_restart && force_user)
844        mode = RESUME_MODE_USER_RESTART;
845      else if (force_restart)
846        mode = RESUME_MODE_IMMEDIATE_RESTART;
847      else if (force_user)
848        mode = RESUME_MODE_USER_CONTINUE;
849      else
850        mode = RESUME_MODE_IMMEDIATE_CONTINUE;
851      break;
852
853    case DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION:
854    case DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE:
855    case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT:
856      if (force_user)
857        mode = RESUME_MODE_USER_RESTART;
858      else
859        mode = RESUME_MODE_IMMEDIATE_RESTART;
860      break;
861
862    case DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED:
863    case DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED:
864    case DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN:
865    case DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED:
866    case DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN:
867    case DOWNLOAD_INTERRUPT_REASON_CRASH:
868      if (force_restart)
869        mode = RESUME_MODE_USER_RESTART;
870      else
871        mode = RESUME_MODE_USER_CONTINUE;
872      break;
873
874    case DOWNLOAD_INTERRUPT_REASON_FILE_FAILED:
875    case DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED:
876    case DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE:
877    case DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG:
878    case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE:
879      mode = RESUME_MODE_USER_RESTART;
880      break;
881
882    case DOWNLOAD_INTERRUPT_REASON_NONE:
883    case DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED:
884    case DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT:
885    case DOWNLOAD_INTERRUPT_REASON_USER_CANCELED:
886    case DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED:
887    case DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED:
888      mode = RESUME_MODE_INVALID;
889      break;
890  }
891
892  return mode;
893}
894
895void DownloadItemImpl::NotifyRemoved() {
896  FOR_EACH_OBSERVER(Observer, observers_, OnDownloadRemoved(this));
897}
898
899void DownloadItemImpl::OnDownloadedFileRemoved() {
900  file_externally_removed_ = true;
901  VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
902  UpdateObservers();
903}
904
905base::WeakPtr<DownloadDestinationObserver>
906DownloadItemImpl::DestinationObserverAsWeakPtr() {
907  return weak_ptr_factory_.GetWeakPtr();
908}
909
910const net::BoundNetLog& DownloadItemImpl::GetBoundNetLog() const {
911  return bound_net_log_;
912}
913
914void DownloadItemImpl::SetTotalBytes(int64 total_bytes) {
915  total_bytes_ = total_bytes;
916}
917
918void DownloadItemImpl::OnAllDataSaved(const std::string& final_hash) {
919  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
920
921  DCHECK_EQ(IN_PROGRESS_INTERNAL, state_);
922  DCHECK(!all_data_saved_);
923  all_data_saved_ = true;
924  VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
925
926  // Store final hash and null out intermediate serialized hash state.
927  hash_ = final_hash;
928  hash_state_ = "";
929
930  UpdateObservers();
931}
932
933void DownloadItemImpl::MarkAsComplete() {
934  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
935
936  DCHECK(all_data_saved_);
937  end_time_ = base::Time::Now();
938  TransitionTo(COMPLETE_INTERNAL);
939}
940void DownloadItemImpl::DestinationUpdate(int64 bytes_so_far,
941                                         int64 bytes_per_sec,
942                                         const std::string& hash_state) {
943  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
944  VLOG(20) << __FUNCTION__ << " so_far=" << bytes_so_far
945           << " per_sec=" << bytes_per_sec << " download=" << DebugString(true);
946
947  if (!IsInProgress()) {
948    // Ignore if we're no longer in-progress.  This can happen if we race a
949    // Cancel on the UI thread with an update on the FILE thread.
950    //
951    // TODO(rdsmith): Arguably we should let this go through, as this means
952    // the download really did get further than we know before it was
953    // cancelled.  But the gain isn't very large, and the code is more
954    // fragile if it has to support in progress updates in a non-in-progress
955    // state.  This issue should be readdressed when we revamp performance
956    // reporting.
957    return;
958  }
959  bytes_per_sec_ = bytes_per_sec;
960  hash_state_ = hash_state;
961  received_bytes_ = bytes_so_far;
962
963  // If we've received more data than we were expecting (bad server info?),
964  // revert to 'unknown size mode'.
965  if (received_bytes_ > total_bytes_)
966    total_bytes_ = 0;
967
968  if (bound_net_log_.IsLoggingAllEvents()) {
969    bound_net_log_.AddEvent(
970        net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED,
971        net::NetLog::Int64Callback("bytes_so_far", received_bytes_));
972  }
973
974  UpdateObservers();
975}
976
977void DownloadItemImpl::DestinationError(DownloadInterruptReason reason) {
978  // Postpone recognition of this error until after file name determination
979  // has completed and the intermediate file has been renamed to simplify
980  // resumption conditions.
981  if (current_path_.empty() || target_path_.empty())
982    destination_error_ = reason;
983  else
984    Interrupt(reason);
985}
986
987void DownloadItemImpl::DestinationCompleted(const std::string& final_hash) {
988  VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
989  if (!IsInProgress())
990    return;
991  OnAllDataSaved(final_hash);
992  MaybeCompleteDownload();
993}
994
995// **** Download progression cascade
996
997void DownloadItemImpl::Init(bool active,
998                            DownloadType download_type) {
999  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1000
1001  if (active)
1002    RecordDownloadCount(START_COUNT);
1003
1004  std::string file_name;
1005  if (download_type == SRC_HISTORY_IMPORT) {
1006    // target_path_ works for History and Save As versions.
1007    file_name = target_path_.AsUTF8Unsafe();
1008  } else {
1009    // See if it's set programmatically.
1010    file_name = forced_file_path_.AsUTF8Unsafe();
1011    // Possibly has a 'download' attribute for the anchor.
1012    if (file_name.empty())
1013      file_name = suggested_filename_;
1014    // From the URL file name.
1015    if (file_name.empty())
1016      file_name = GetURL().ExtractFileName();
1017  }
1018
1019  base::Callback<base::Value*(net::NetLog::LogLevel)> active_data = base::Bind(
1020      &ItemActivatedNetLogCallback, this, download_type, &file_name);
1021  if (active) {
1022    bound_net_log_.BeginEvent(
1023        net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, active_data);
1024  } else {
1025    bound_net_log_.AddEvent(
1026        net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, active_data);
1027  }
1028
1029  VLOG(20) << __FUNCTION__ << "() " << DebugString(true);
1030}
1031
1032// We're starting the download.
1033void DownloadItemImpl::Start(
1034    scoped_ptr<DownloadFile> file,
1035    scoped_ptr<DownloadRequestHandleInterface> req_handle) {
1036  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1037  DCHECK(!download_file_.get());
1038  DCHECK(file.get());
1039  DCHECK(req_handle.get());
1040
1041  download_file_ = file.Pass();
1042  request_handle_ = req_handle.Pass();
1043
1044  if (IsCancelled()) {
1045    // The download was in the process of resuming when it was cancelled. Don't
1046    // proceed.
1047    ReleaseDownloadFile(true);
1048    request_handle_->CancelRequest();
1049    return;
1050  }
1051
1052  TransitionTo(IN_PROGRESS_INTERNAL);
1053
1054  last_reason_ = DOWNLOAD_INTERRUPT_REASON_NONE;
1055
1056  BrowserThread::PostTask(
1057      BrowserThread::FILE, FROM_HERE,
1058      base::Bind(&DownloadFile::Initialize,
1059                 // Safe because we control download file lifetime.
1060                 base::Unretained(download_file_.get()),
1061                 base::Bind(&DownloadItemImpl::OnDownloadFileInitialized,
1062                            weak_ptr_factory_.GetWeakPtr())));
1063}
1064
1065void DownloadItemImpl::OnDownloadFileInitialized(
1066    DownloadInterruptReason result) {
1067  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1068  if (result != DOWNLOAD_INTERRUPT_REASON_NONE) {
1069    Interrupt(result);
1070    // TODO(rdsmith/asanka): Arguably we should show this in the UI, but
1071    // it's not at all clear what to show--we haven't done filename
1072    // determination, so we don't know what name to display.  OTOH,
1073    // the failure mode of not showing the DI if the file initialization
1074    // fails isn't a good one.  Can we hack up a name based on the
1075    // URLRequest?  We'll need to make sure that initialization happens
1076    // properly.  Possibly the right thing is to have the UI handle
1077    // this case specially.
1078    return;
1079  }
1080
1081  delegate_->DetermineDownloadTarget(
1082      this, base::Bind(&DownloadItemImpl::OnDownloadTargetDetermined,
1083                       weak_ptr_factory_.GetWeakPtr()));
1084}
1085
1086// Called by delegate_ when the download target path has been
1087// determined.
1088void DownloadItemImpl::OnDownloadTargetDetermined(
1089    const base::FilePath& target_path,
1090    TargetDisposition disposition,
1091    DownloadDangerType danger_type,
1092    const base::FilePath& intermediate_path) {
1093  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1094
1095  // If the |target_path| is empty, then we consider this download to be
1096  // canceled.
1097  if (target_path.empty()) {
1098    Cancel(true);
1099    return;
1100  }
1101
1102  // TODO(rdsmith,asanka): We are ignoring the possibility that the download
1103  // has been interrupted at this point until we finish the intermediate
1104  // rename and set the full path.  That's dangerous, because we might race
1105  // with resumption, either manual (because the interrupt is visible to the
1106  // UI) or automatic.  If we keep the "ignore an error on download until file
1107  // name determination complete" semantics, we need to make sure that the
1108  // error is kept completely invisible until that point.
1109
1110  VLOG(20) << __FUNCTION__ << " " << target_path.value() << " " << disposition
1111           << " " << danger_type << " " << DebugString(true);
1112
1113  target_path_ = target_path;
1114  target_disposition_ = disposition;
1115  SetDangerType(danger_type);
1116  // TODO(asanka): SetDangerType() doesn't need to send a notification here.
1117
1118  // We want the intermediate and target paths to refer to the same directory so
1119  // that they are both on the same device and subject to same
1120  // space/permission/availability constraints.
1121  DCHECK(intermediate_path.DirName() == target_path.DirName());
1122
1123  // During resumption, we may choose to proceed with the same intermediate
1124  // file. No rename is necessary if our intermediate file already has the
1125  // correct name.
1126  //
1127  // The intermediate name may change from its original value during filename
1128  // determination on resumption, for example if the reason for the interruption
1129  // was the download target running out space, resulting in a user prompt.
1130  if (intermediate_path == current_path_) {
1131    OnDownloadRenamedToIntermediateName(DOWNLOAD_INTERRUPT_REASON_NONE,
1132                                        intermediate_path);
1133    return;
1134  }
1135
1136  // Rename to intermediate name.
1137  // TODO(asanka): Skip this rename if AllDataSaved() is true. This avoids a
1138  //               spurious rename when we can just rename to the final
1139  //               filename. Unnecessary renames may cause bugs like
1140  //               http://crbug.com/74187.
1141  DCHECK(!is_save_package_download_);
1142  DCHECK(download_file_.get());
1143  DownloadFile::RenameCompletionCallback callback =
1144      base::Bind(&DownloadItemImpl::OnDownloadRenamedToIntermediateName,
1145                 weak_ptr_factory_.GetWeakPtr());
1146  BrowserThread::PostTask(
1147      BrowserThread::FILE, FROM_HERE,
1148      base::Bind(&DownloadFile::RenameAndUniquify,
1149                 // Safe because we control download file lifetime.
1150                 base::Unretained(download_file_.get()),
1151                 intermediate_path, callback));
1152}
1153
1154void DownloadItemImpl::OnDownloadRenamedToIntermediateName(
1155    DownloadInterruptReason reason,
1156    const base::FilePath& full_path) {
1157  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1158  VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
1159
1160  if (DOWNLOAD_INTERRUPT_REASON_NONE != destination_error_) {
1161    // Process destination error.  If both |reason| and |destination_error_|
1162    // refer to actual errors, we want to use the |destination_error_| as the
1163    // argument to the Interrupt() routine, as it happened first.
1164    Interrupt(destination_error_);
1165    destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE;
1166  } else if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) {
1167    Interrupt(reason);
1168    // All file errors result in file deletion above; no need to cleanup.  The
1169    // current_path_ should be empty. Resuming this download will force a
1170    // restart and a re-doing of filename determination.
1171    DCHECK(current_path_.empty());
1172  } else {
1173    SetFullPath(full_path);
1174    MaybeCompleteDownload();
1175  }
1176}
1177
1178// When SavePackage downloads MHTML to GData (see
1179// SavePackageFilePickerChromeOS), GData calls MaybeCompleteDownload() like it
1180// does for non-SavePackage downloads, but SavePackage downloads never satisfy
1181// IsDownloadReadyForCompletion(). GDataDownloadObserver manually calls
1182// DownloadItem::UpdateObservers() when the upload completes so that SavePackage
1183// notices that the upload has completed and runs its normal Finish() pathway.
1184// MaybeCompleteDownload() is never the mechanism by which SavePackage completes
1185// downloads. SavePackage always uses its own Finish() to mark downloads
1186// complete.
1187void DownloadItemImpl::MaybeCompleteDownload() {
1188  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1189  DCHECK(!is_save_package_download_);
1190
1191  if (!IsDownloadReadyForCompletion(
1192          base::Bind(&DownloadItemImpl::MaybeCompleteDownload,
1193                     weak_ptr_factory_.GetWeakPtr())))
1194    return;
1195
1196  // TODO(rdsmith): DCHECK that we only pass through this point
1197  // once per download.  The natural way to do this is by a state
1198  // transition on the DownloadItem.
1199
1200  // Confirm we're in the proper set of states to be here;
1201  // have all data, have a history handle, (validated or safe).
1202  DCHECK_EQ(IN_PROGRESS_INTERNAL, state_);
1203  DCHECK(!IsDangerous());
1204  DCHECK(all_data_saved_);
1205
1206  OnDownloadCompleting();
1207}
1208
1209// Called by MaybeCompleteDownload() when it has determined that the download
1210// is ready for completion.
1211void DownloadItemImpl::OnDownloadCompleting() {
1212  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1213
1214  if (state_ != IN_PROGRESS_INTERNAL)
1215    return;
1216
1217  VLOG(20) << __FUNCTION__ << "()"
1218           << " " << DebugString(true);
1219  DCHECK(!GetTargetFilePath().empty());
1220  DCHECK(!IsDangerous());
1221
1222  // TODO(rdsmith/benjhayden): Remove as part of SavePackage integration.
1223  if (is_save_package_download_) {
1224    // Avoid doing anything on the file thread; there's nothing we control
1225    // there.
1226    // Strictly speaking, this skips giving the embedder a chance to open
1227    // the download.  But on a save package download, there's no real
1228    // concept of opening.
1229    Completed();
1230    return;
1231  }
1232
1233  DCHECK(download_file_.get());
1234  // Unilaterally rename; even if it already has the right name,
1235  // we need theannotation.
1236  DownloadFile::RenameCompletionCallback callback =
1237      base::Bind(&DownloadItemImpl::OnDownloadRenamedToFinalName,
1238                 weak_ptr_factory_.GetWeakPtr());
1239  BrowserThread::PostTask(
1240      BrowserThread::FILE, FROM_HERE,
1241      base::Bind(&DownloadFile::RenameAndAnnotate,
1242                 base::Unretained(download_file_.get()),
1243                 GetTargetFilePath(), callback));
1244}
1245
1246void DownloadItemImpl::OnDownloadRenamedToFinalName(
1247    DownloadInterruptReason reason,
1248    const base::FilePath& full_path) {
1249  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1250  DCHECK(!is_save_package_download_);
1251
1252  // If a cancel or interrupt hit, we'll cancel the DownloadFile, which
1253  // will result in deleting the file on the file thread.  So we don't
1254  // care about the name having been changed.
1255  if (state_ != IN_PROGRESS_INTERNAL)
1256    return;
1257
1258  VLOG(20) << __FUNCTION__ << "()"
1259           << " full_path = \"" << full_path.value() << "\""
1260           << " " << DebugString(false);
1261
1262  if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) {
1263    Interrupt(reason);
1264
1265    // All file errors should have resulted in in file deletion above. On
1266    // resumption we will need to re-do filename determination.
1267    DCHECK(current_path_.empty());
1268    return;
1269  }
1270
1271  DCHECK(target_path_ == full_path);
1272
1273  if (full_path != current_path_) {
1274    // full_path is now the current and target file path.
1275    DCHECK(!full_path.empty());
1276    SetFullPath(full_path);
1277  }
1278
1279  // Complete the download and release the DownloadFile.
1280  DCHECK(download_file_.get());
1281  ReleaseDownloadFile(false);
1282
1283  // We're not completely done with the download item yet, but at this
1284  // point we're committed to complete the download.  Cancels (or Interrupts,
1285  // though it's not clear how they could happen) after this point will be
1286  // ignored.
1287  TransitionTo(COMPLETING_INTERNAL);
1288
1289  if (delegate_->ShouldOpenDownload(
1290          this, base::Bind(&DownloadItemImpl::DelayedDownloadOpened,
1291                           weak_ptr_factory_.GetWeakPtr()))) {
1292    Completed();
1293  } else {
1294    delegate_delayed_complete_ = true;
1295  }
1296}
1297
1298void DownloadItemImpl::DelayedDownloadOpened(bool auto_opened) {
1299  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1300
1301  auto_opened_ = auto_opened;
1302  Completed();
1303}
1304
1305void DownloadItemImpl::Completed() {
1306  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1307
1308  VLOG(20) << __FUNCTION__ << "() " << DebugString(false);
1309
1310  DCHECK(all_data_saved_);
1311  end_time_ = base::Time::Now();
1312  TransitionTo(COMPLETE_INTERNAL);
1313  RecordDownloadCompleted(start_tick_, received_bytes_);
1314
1315  if (auto_opened_) {
1316    // If it was already handled by the delegate, do nothing.
1317  } else if (GetOpenWhenComplete() ||
1318             ShouldOpenFileBasedOnExtension() ||
1319             IsTemporary()) {
1320    // If the download is temporary, like in drag-and-drop, do not open it but
1321    // we still need to set it auto-opened so that it can be removed from the
1322    // download shelf.
1323    if (!IsTemporary())
1324      OpenDownload();
1325
1326    auto_opened_ = true;
1327    UpdateObservers();
1328  }
1329}
1330
1331void DownloadItemImpl::OnResumeRequestStarted(DownloadItem* item,
1332                                              net::Error error) {
1333  // If |item| is not NULL, then Start() has been called already, and nothing
1334  // more needs to be done here.
1335  if (item) {
1336    DCHECK_EQ(net::OK, error);
1337    DCHECK_EQ(static_cast<DownloadItem*>(this), item);
1338    return;
1339  }
1340  // Otherwise, the request failed without passing through
1341  // DownloadResourceHandler::OnResponseStarted.
1342  if (error == net::OK)
1343    error = net::ERR_FAILED;
1344  DownloadInterruptReason reason =
1345      ConvertNetErrorToInterruptReason(error, DOWNLOAD_INTERRUPT_FROM_NETWORK);
1346  DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, reason);
1347  Interrupt(reason);
1348}
1349
1350// **** End of Download progression cascade
1351
1352// An error occurred somewhere.
1353void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) {
1354  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1355
1356  // Somewhat counter-intuitively, it is possible for us to receive an
1357  // interrupt after we've already been interrupted.  The generation of
1358  // interrupts from the file thread Renames and the generation of
1359  // interrupts from disk writes go through two different mechanisms (driven
1360  // by rename requests from UI thread and by write requests from IO thread,
1361  // respectively), and since we choose not to keep state on the File thread,
1362  // this is the place where the races collide.  It's also possible for
1363  // interrupts to race with cancels.
1364
1365  // Whatever happens, the first one to hit the UI thread wins.
1366  if (state_ != IN_PROGRESS_INTERNAL && state_ != RESUMING_INTERNAL)
1367    return;
1368
1369  last_reason_ = reason;
1370
1371  ResumeMode resume_mode = GetResumeMode();
1372  // Cancel (delete file) if we're going to restart; no point in leaving
1373  // data around we aren't going to use.  Also cancel if resumption isn't
1374  // enabled for the same reason.
1375  ReleaseDownloadFile(resume_mode == RESUME_MODE_IMMEDIATE_RESTART ||
1376                      resume_mode == RESUME_MODE_USER_RESTART ||
1377                      !IsDownloadResumptionEnabled());
1378
1379  // Reset all data saved, as even if we did save all the data we're going
1380  // to go through another round of downloading when we resume.
1381  // There's a potential problem here in the abstract, as if we did download
1382  // all the data and then run into a continuable error, on resumption we
1383  // won't download any more data.  However, a) there are currently no
1384  // continuable errors that can occur after we download all the data, and
1385  // b) if there were, that would probably simply result in a null range
1386  // request, which would generate a DestinationCompleted() notification
1387  // from the DownloadFile, which would behave properly with setting
1388  // all_data_saved_ to false here.
1389  all_data_saved_ = false;
1390
1391  // Cancel the originating URL request.
1392  request_handle_->CancelRequest();
1393
1394  TransitionTo(INTERRUPTED_INTERNAL);
1395  RecordDownloadInterrupted(reason, received_bytes_, total_bytes_);
1396
1397  AutoResumeIfValid();
1398}
1399
1400void DownloadItemImpl::ReleaseDownloadFile(bool destroy_file) {
1401  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1402
1403  if (destroy_file) {
1404    BrowserThread::PostTask(
1405        BrowserThread::FILE, FROM_HERE,
1406        // Will be deleted at end of task execution.
1407        base::Bind(&DownloadFileCancel, base::Passed(&download_file_)));
1408    // Avoid attempting to reuse the intermediate file by clearing out
1409    // current_path_.
1410    current_path_.clear();
1411  } else {
1412    BrowserThread::PostTask(
1413        BrowserThread::FILE,
1414        FROM_HERE,
1415        base::Bind(base::IgnoreResult(&DownloadFileDetach),
1416                   // Will be deleted at end of task execution.
1417                   base::Passed(&download_file_)));
1418  }
1419  // Don't accept any more messages from the DownloadFile, and null
1420  // out any previous "all data received".  This also breaks links to
1421  // other entities we've given out weak pointers to.
1422  weak_ptr_factory_.InvalidateWeakPtrs();
1423}
1424
1425bool DownloadItemImpl::IsDownloadReadyForCompletion(
1426    const base::Closure& state_change_notification) {
1427  // If we don't have all the data, the download is not ready for
1428  // completion.
1429  if (!AllDataSaved())
1430    return false;
1431
1432  // If the download is dangerous, but not yet validated, it's not ready for
1433  // completion.
1434  if (IsDangerous())
1435    return false;
1436
1437  // If the download isn't active (e.g. has been cancelled) it's not
1438  // ready for completion.
1439  if (state_ != IN_PROGRESS_INTERNAL)
1440    return false;
1441
1442  // If the target filename hasn't been determined, then it's not ready for
1443  // completion. This is checked in ReadyForDownloadCompletionDone().
1444  if (GetTargetFilePath().empty())
1445    return false;
1446
1447  // This is checked in NeedsRename(). Without this conditional,
1448  // browser_tests:DownloadTest.DownloadMimeType fails the DCHECK.
1449  if (target_path_.DirName() != current_path_.DirName())
1450    return false;
1451
1452  // Give the delegate a chance to hold up a stop sign.  It'll call
1453  // use back through the passed callback if it does and that state changes.
1454  if (!delegate_->ShouldCompleteDownload(this, state_change_notification))
1455    return false;
1456
1457  return true;
1458}
1459
1460void DownloadItemImpl::TransitionTo(DownloadInternalState new_state) {
1461  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1462
1463  if (state_ == new_state)
1464    return;
1465
1466  DownloadInternalState old_state = state_;
1467  state_ = new_state;
1468
1469  switch (state_) {
1470    case COMPLETING_INTERNAL:
1471      bound_net_log_.AddEvent(
1472          net::NetLog::TYPE_DOWNLOAD_ITEM_COMPLETING,
1473          base::Bind(&ItemCompletingNetLogCallback, received_bytes_, &hash_));
1474      break;
1475    case COMPLETE_INTERNAL:
1476      bound_net_log_.AddEvent(
1477          net::NetLog::TYPE_DOWNLOAD_ITEM_FINISHED,
1478          base::Bind(&ItemFinishedNetLogCallback, auto_opened_));
1479      break;
1480    case INTERRUPTED_INTERNAL:
1481      bound_net_log_.AddEvent(
1482          net::NetLog::TYPE_DOWNLOAD_ITEM_INTERRUPTED,
1483          base::Bind(&ItemInterruptedNetLogCallback, last_reason_,
1484                     received_bytes_, &hash_state_));
1485      break;
1486    case IN_PROGRESS_INTERNAL:
1487      if (old_state == INTERRUPTED_INTERNAL) {
1488        bound_net_log_.AddEvent(
1489            net::NetLog::TYPE_DOWNLOAD_ITEM_RESUMED,
1490            base::Bind(&ItemResumingNetLogCallback,
1491                       false, last_reason_, received_bytes_, &hash_state_));
1492      }
1493      break;
1494    case CANCELLED_INTERNAL:
1495      bound_net_log_.AddEvent(
1496          net::NetLog::TYPE_DOWNLOAD_ITEM_CANCELED,
1497          base::Bind(&ItemCanceledNetLogCallback, received_bytes_,
1498                     &hash_state_));
1499      break;
1500    default:
1501      break;
1502  }
1503
1504  VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true)
1505    << " " << InternalToExternalState(old_state)
1506    << " " << InternalToExternalState(state_);
1507
1508  // Only update observers on user visible state changes.
1509  if (InternalToExternalState(state_) != InternalToExternalState(old_state))
1510    UpdateObservers();
1511
1512  bool is_done = (state_ != IN_PROGRESS_INTERNAL &&
1513                  state_ != COMPLETING_INTERNAL);
1514  bool was_done = (old_state != IN_PROGRESS_INTERNAL &&
1515                   old_state != COMPLETING_INTERNAL);
1516  // Termination
1517  if (is_done && !was_done)
1518    bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE);
1519
1520  // Resumption
1521  if (was_done && !is_done) {
1522    std::string file_name(target_path_.BaseName().AsUTF8Unsafe());
1523    bound_net_log_.BeginEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE,
1524                              base::Bind(&ItemActivatedNetLogCallback,
1525                                         this, SRC_ACTIVE_DOWNLOAD,
1526                                         &file_name));
1527  }
1528}
1529
1530void DownloadItemImpl::SetDangerType(DownloadDangerType danger_type) {
1531  if (danger_type != danger_type_) {
1532    bound_net_log_.AddEvent(
1533        net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED,
1534        base::Bind(&ItemCheckedNetLogCallback, danger_type));
1535  }
1536  danger_type_ = danger_type;
1537}
1538
1539void DownloadItemImpl::SetFullPath(const base::FilePath& new_path) {
1540  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1541  VLOG(20) << __FUNCTION__ << "()"
1542           << " new_path = \"" << new_path.value() << "\""
1543           << " " << DebugString(true);
1544  DCHECK(!new_path.empty());
1545
1546  bound_net_log_.AddEvent(
1547      net::NetLog::TYPE_DOWNLOAD_ITEM_RENAMED,
1548      base::Bind(&ItemRenamedNetLogCallback, &current_path_, &new_path));
1549
1550  current_path_ = new_path;
1551  UpdateObservers();
1552}
1553
1554void DownloadItemImpl::AutoResumeIfValid() {
1555  DVLOG(20) << __FUNCTION__ << "() " << DebugString(true);
1556  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1557  ResumeMode mode = GetResumeMode();
1558
1559  if (mode != RESUME_MODE_IMMEDIATE_RESTART &&
1560      mode != RESUME_MODE_IMMEDIATE_CONTINUE) {
1561    return;
1562  }
1563
1564  auto_resume_count_++;
1565
1566  ResumeInterruptedDownload();
1567}
1568
1569void DownloadItemImpl::ResumeInterruptedDownload() {
1570  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1571
1572  // If the flag for downloads resumption isn't enabled, ignore
1573  // this request.
1574  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
1575  if (!command_line.HasSwitch(switches::kEnableDownloadResumption))
1576    return;
1577
1578  // If we're not interrupted, ignore the request; our caller is drunk.
1579  if (state_ != INTERRUPTED_INTERNAL)
1580    return;
1581
1582  // If we can't get a web contents, we can't resume the download.
1583  // TODO(rdsmith): Find some alternative web contents to use--this
1584  // means we can't restart a download if it's a download imported
1585  // from the history.
1586  if (!GetWebContents())
1587    return;
1588
1589  // Reset the appropriate state if restarting.
1590  ResumeMode mode = GetResumeMode();
1591  if (mode == RESUME_MODE_IMMEDIATE_RESTART ||
1592      mode == RESUME_MODE_USER_RESTART) {
1593    received_bytes_ = 0;
1594    hash_state_ = "";
1595    last_modified_time_ = "";
1596    etag_ = "";
1597  }
1598
1599  scoped_ptr<DownloadUrlParameters> download_params(
1600      DownloadUrlParameters::FromWebContents(GetWebContents(),
1601                                             GetOriginalUrl()));
1602
1603  download_params->set_file_path(GetFullPath());
1604  download_params->set_offset(GetReceivedBytes());
1605  download_params->set_hash_state(GetHashState());
1606  download_params->set_last_modified(GetLastModifiedTime());
1607  download_params->set_etag(GetETag());
1608  download_params->set_callback(
1609      base::Bind(&DownloadItemImpl::OnResumeRequestStarted,
1610                 weak_ptr_factory_.GetWeakPtr()));
1611
1612  delegate_->ResumeInterruptedDownload(download_params.Pass(), GetGlobalId());
1613  // Just in case we were interrupted while paused.
1614  is_paused_ = false;
1615
1616  TransitionTo(RESUMING_INTERNAL);
1617}
1618
1619// static
1620DownloadItem::DownloadState DownloadItemImpl::InternalToExternalState(
1621    DownloadInternalState internal_state) {
1622  switch (internal_state) {
1623    case IN_PROGRESS_INTERNAL:
1624      return IN_PROGRESS;
1625    case COMPLETING_INTERNAL:
1626      return IN_PROGRESS;
1627    case COMPLETE_INTERNAL:
1628      return COMPLETE;
1629    case CANCELLED_INTERNAL:
1630      return CANCELLED;
1631    case INTERRUPTED_INTERNAL:
1632      return INTERRUPTED;
1633    case RESUMING_INTERNAL:
1634      return INTERRUPTED;
1635    case MAX_DOWNLOAD_INTERNAL_STATE:
1636      break;
1637  }
1638  NOTREACHED();
1639  return MAX_DOWNLOAD_STATE;
1640}
1641
1642// static
1643DownloadItemImpl::DownloadInternalState
1644DownloadItemImpl::ExternalToInternalState(
1645    DownloadState external_state) {
1646  switch (external_state) {
1647    case IN_PROGRESS:
1648      return IN_PROGRESS_INTERNAL;
1649    case COMPLETE:
1650      return COMPLETE_INTERNAL;
1651    case CANCELLED:
1652      return CANCELLED_INTERNAL;
1653    case INTERRUPTED:
1654      return INTERRUPTED_INTERNAL;
1655    default:
1656      NOTREACHED();
1657  }
1658  return MAX_DOWNLOAD_INTERNAL_STATE;
1659}
1660
1661const char* DownloadItemImpl::DebugDownloadStateString(
1662    DownloadInternalState state) {
1663  switch (state) {
1664    case IN_PROGRESS_INTERNAL:
1665      return "IN_PROGRESS";
1666    case COMPLETING_INTERNAL:
1667      return "COMPLETING";
1668    case COMPLETE_INTERNAL:
1669      return "COMPLETE";
1670    case CANCELLED_INTERNAL:
1671      return "CANCELLED";
1672    case INTERRUPTED_INTERNAL:
1673      return "INTERRUPTED";
1674    case RESUMING_INTERNAL:
1675      return "RESUMING";
1676    case MAX_DOWNLOAD_INTERNAL_STATE:
1677      break;
1678  };
1679  NOTREACHED() << "Unknown download state " << state;
1680  return "unknown";
1681}
1682
1683const char* DownloadItemImpl::DebugResumeModeString(ResumeMode mode) {
1684  switch (mode) {
1685    case RESUME_MODE_INVALID:
1686      return "INVALID";
1687    case RESUME_MODE_IMMEDIATE_CONTINUE:
1688      return "IMMEDIATE_CONTINUE";
1689    case RESUME_MODE_IMMEDIATE_RESTART:
1690      return "IMMEDIATE_RESTART";
1691    case RESUME_MODE_USER_CONTINUE:
1692      return "USER_CONTINUE";
1693    case RESUME_MODE_USER_RESTART:
1694      return "USER_RESTART";
1695  }
1696  NOTREACHED() << "Unknown resume mode " << mode;
1697  return "unknown";
1698}
1699
1700}  // namespace content
1701