download_item_impl.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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    base::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, UPDATE_OBSERVERS);
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 ((GetState() == IN_PROGRESS) && 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
519const GURL& DownloadItemImpl::GetURL() const {
520  return url_chain_.empty() ? GURL::EmptyGURL() : url_chain_.back();
521}
522
523const std::vector<GURL>& DownloadItemImpl::GetUrlChain() const {
524  return url_chain_;
525}
526
527const GURL& DownloadItemImpl::GetOriginalUrl() const {
528  // Be careful about taking the front() of possibly-empty vectors!
529  // http://crbug.com/190096
530  return url_chain_.empty() ? GURL::EmptyGURL() : url_chain_.front();
531}
532
533const GURL& DownloadItemImpl::GetReferrerUrl() const {
534  return referrer_url_;
535}
536
537std::string DownloadItemImpl::GetSuggestedFilename() const {
538  return suggested_filename_;
539}
540
541std::string DownloadItemImpl::GetContentDisposition() const {
542  return content_disposition_;
543}
544
545std::string DownloadItemImpl::GetMimeType() const {
546  return mime_type_;
547}
548
549std::string DownloadItemImpl::GetOriginalMimeType() const {
550  return original_mime_type_;
551}
552
553std::string DownloadItemImpl::GetRemoteAddress() const {
554  return remote_address_;
555}
556
557bool DownloadItemImpl::HasUserGesture() const {
558  return has_user_gesture_;
559};
560
561PageTransition DownloadItemImpl::GetTransitionType() const {
562  return transition_type_;
563};
564
565const std::string& DownloadItemImpl::GetLastModifiedTime() const {
566  return last_modified_time_;
567}
568
569const std::string& DownloadItemImpl::GetETag() const {
570  return etag_;
571}
572
573bool DownloadItemImpl::IsSavePackageDownload() const {
574  return is_save_package_download_;
575}
576
577const base::FilePath& DownloadItemImpl::GetFullPath() const {
578  return current_path_;
579}
580
581const base::FilePath& DownloadItemImpl::GetTargetFilePath() const {
582  return target_path_;
583}
584
585const base::FilePath& DownloadItemImpl::GetForcedFilePath() const {
586  // TODO(asanka): Get rid of GetForcedFilePath(). We should instead just
587  // require that clients respect GetTargetFilePath() if it is already set.
588  return forced_file_path_;
589}
590
591base::FilePath DownloadItemImpl::GetFileNameToReportUser() const {
592  if (!display_name_.empty())
593    return display_name_;
594  return target_path_.BaseName();
595}
596
597DownloadItem::TargetDisposition DownloadItemImpl::GetTargetDisposition() const {
598  return target_disposition_;
599}
600
601const std::string& DownloadItemImpl::GetHash() const {
602  return hash_;
603}
604
605const std::string& DownloadItemImpl::GetHashState() const {
606  return hash_state_;
607}
608
609bool DownloadItemImpl::GetFileExternallyRemoved() const {
610  return file_externally_removed_;
611}
612
613bool DownloadItemImpl::IsDangerous() const {
614#if defined(OS_WIN)
615  // TODO(noelutz): At this point only the windows views UI supports
616  // warnings based on dangerous content.
617  return (danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
618          danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
619          danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
620          danger_type_ == DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT ||
621          danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST);
622#else
623  return (danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
624          danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL);
625#endif
626}
627
628DownloadDangerType DownloadItemImpl::GetDangerType() const {
629  return danger_type_;
630}
631
632bool DownloadItemImpl::TimeRemaining(base::TimeDelta* remaining) const {
633  if (total_bytes_ <= 0)
634    return false;  // We never received the content_length for this download.
635
636  int64 speed = CurrentSpeed();
637  if (speed == 0)
638    return false;
639
640  *remaining = base::TimeDelta::FromSeconds(
641      (total_bytes_ - received_bytes_) / speed);
642  return true;
643}
644
645int64 DownloadItemImpl::CurrentSpeed() const {
646  if (is_paused_)
647    return 0;
648  return bytes_per_sec_;
649}
650
651int DownloadItemImpl::PercentComplete() const {
652  // If the delegate is delaying completion of the download, then we have no
653  // idea how long it will take.
654  if (delegate_delayed_complete_ || total_bytes_ <= 0)
655    return -1;
656
657  return static_cast<int>(received_bytes_ * 100.0 / total_bytes_);
658}
659
660bool DownloadItemImpl::AllDataSaved() const {
661  return all_data_saved_;
662}
663
664int64 DownloadItemImpl::GetTotalBytes() const {
665  return total_bytes_;
666}
667
668int64 DownloadItemImpl::GetReceivedBytes() const {
669  return received_bytes_;
670}
671
672base::Time DownloadItemImpl::GetStartTime() const {
673  return start_time_;
674}
675
676base::Time DownloadItemImpl::GetEndTime() const {
677  return end_time_;
678}
679
680bool DownloadItemImpl::CanShowInFolder() {
681  // A download can be shown in the folder if the downloaded file is in a known
682  // location.
683  return CanOpenDownload() && !GetFullPath().empty();
684}
685
686bool DownloadItemImpl::CanOpenDownload() {
687  // We can open the file or mark it for opening on completion if the download
688  // is expected to complete successfully. Exclude temporary downloads, since
689  // they aren't owned by the download system.
690  const bool is_complete = GetState() == DownloadItem::COMPLETE;
691  return (!IsDone() || is_complete) && !IsTemporary() &&
692         !file_externally_removed_;
693}
694
695bool DownloadItemImpl::ShouldOpenFileBasedOnExtension() {
696  return delegate_->ShouldOpenFileBasedOnExtension(GetTargetFilePath());
697}
698
699bool DownloadItemImpl::GetOpenWhenComplete() const {
700  return open_when_complete_;
701}
702
703bool DownloadItemImpl::GetAutoOpened() {
704  return auto_opened_;
705}
706
707bool DownloadItemImpl::GetOpened() const {
708  return opened_;
709}
710
711BrowserContext* DownloadItemImpl::GetBrowserContext() const {
712  return delegate_->GetBrowserContext();
713}
714
715WebContents* DownloadItemImpl::GetWebContents() const {
716  // TODO(rdsmith): Remove null check after removing GetWebContents() from
717  // paths that might be used by DownloadItems created from history import.
718  // Currently such items have null request_handle_s, where other items
719  // (regular and SavePackage downloads) have actual objects off the pointer.
720  if (request_handle_)
721    return request_handle_->GetWebContents();
722  return NULL;
723}
724
725void DownloadItemImpl::OnContentCheckCompleted(DownloadDangerType danger_type) {
726  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
727  DCHECK(AllDataSaved());
728  VLOG(20) << __FUNCTION__ << " danger_type=" << danger_type
729           << " download=" << DebugString(true);
730  SetDangerType(danger_type);
731  UpdateObservers();
732}
733
734void DownloadItemImpl::SetOpenWhenComplete(bool open) {
735  open_when_complete_ = open;
736}
737
738void DownloadItemImpl::SetIsTemporary(bool temporary) {
739  is_temporary_ = temporary;
740}
741
742void DownloadItemImpl::SetOpened(bool opened) {
743  opened_ = opened;
744}
745
746void DownloadItemImpl::SetDisplayName(const base::FilePath& name) {
747  display_name_ = name;
748}
749
750std::string DownloadItemImpl::DebugString(bool verbose) const {
751  std::string description =
752      base::StringPrintf("{ id = %d"
753                         " state = %s",
754                         download_id_.local(),
755                         DebugDownloadStateString(state_));
756
757  // Construct a string of the URL chain.
758  std::string url_list("<none>");
759  if (!url_chain_.empty()) {
760    std::vector<GURL>::const_iterator iter = url_chain_.begin();
761    std::vector<GURL>::const_iterator last = url_chain_.end();
762    url_list = (*iter).is_valid() ? (*iter).spec() : "<invalid>";
763    ++iter;
764    for ( ; verbose && (iter != last); ++iter) {
765      url_list += " ->\n\t";
766      const GURL& next_url = *iter;
767      url_list += next_url.is_valid() ? next_url.spec() : "<invalid>";
768    }
769  }
770
771  if (verbose) {
772    description += base::StringPrintf(
773        " total = %" PRId64
774        " received = %" PRId64
775        " reason = %s"
776        " paused = %c"
777        " resume_mode = %s"
778        " auto_resume_count = %d"
779        " danger = %d"
780        " all_data_saved = %c"
781        " last_modified = '%s'"
782        " etag = '%s'"
783        " has_download_file = %s"
784        " url_chain = \n\t\"%s\"\n\t"
785        " full_path = \"%" PRFilePath "\"\n\t"
786        " target_path = \"%" PRFilePath "\"",
787        GetTotalBytes(),
788        GetReceivedBytes(),
789        InterruptReasonDebugString(last_reason_).c_str(),
790        IsPaused() ? 'T' : 'F',
791        DebugResumeModeString(GetResumeMode()),
792        auto_resume_count_,
793        GetDangerType(),
794        AllDataSaved() ? 'T' : 'F',
795        GetLastModifiedTime().c_str(),
796        GetETag().c_str(),
797        download_file_.get() ? "true" : "false",
798        url_list.c_str(),
799        GetFullPath().value().c_str(),
800        GetTargetFilePath().value().c_str());
801  } else {
802    description += base::StringPrintf(" url = \"%s\"", url_list.c_str());
803  }
804
805  description += " }";
806
807  return description;
808}
809
810DownloadItemImpl::ResumeMode DownloadItemImpl::GetResumeMode() const {
811  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
812  // We can't continue without a handle on the intermediate file.
813  // We also can't continue if we don't have some verifier to make sure
814  // we're getting the same file.
815  const bool force_restart =
816      (current_path_.empty() || (etag_.empty() && last_modified_time_.empty()));
817
818  // We won't auto-restart if we've used up our attempts or the
819  // download has been paused by user action.
820  const bool force_user =
821      (auto_resume_count_ >= kMaxAutoResumeAttempts || is_paused_);
822
823  ResumeMode mode = RESUME_MODE_INVALID;
824
825  switch(last_reason_) {
826    case DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR:
827    case DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT:
828      if (force_restart && force_user)
829        mode = RESUME_MODE_USER_RESTART;
830      else if (force_restart)
831        mode = RESUME_MODE_IMMEDIATE_RESTART;
832      else if (force_user)
833        mode = RESUME_MODE_USER_CONTINUE;
834      else
835        mode = RESUME_MODE_IMMEDIATE_CONTINUE;
836      break;
837
838    case DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION:
839    case DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE:
840    case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT:
841      if (force_user)
842        mode = RESUME_MODE_USER_RESTART;
843      else
844        mode = RESUME_MODE_IMMEDIATE_RESTART;
845      break;
846
847    case DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED:
848    case DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED:
849    case DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN:
850    case DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED:
851    case DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN:
852    case DOWNLOAD_INTERRUPT_REASON_CRASH:
853      if (force_restart)
854        mode = RESUME_MODE_USER_RESTART;
855      else
856        mode = RESUME_MODE_USER_CONTINUE;
857      break;
858
859    case DOWNLOAD_INTERRUPT_REASON_FILE_FAILED:
860    case DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED:
861    case DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE:
862    case DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG:
863    case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE:
864      mode = RESUME_MODE_USER_RESTART;
865      break;
866
867    case DOWNLOAD_INTERRUPT_REASON_NONE:
868    case DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED:
869    case DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT:
870    case DOWNLOAD_INTERRUPT_REASON_USER_CANCELED:
871    case DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED:
872    case DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED:
873      mode = RESUME_MODE_INVALID;
874      break;
875  }
876
877  return mode;
878}
879
880void DownloadItemImpl::NotifyRemoved() {
881  FOR_EACH_OBSERVER(Observer, observers_, OnDownloadRemoved(this));
882}
883
884void DownloadItemImpl::OnDownloadedFileRemoved() {
885  file_externally_removed_ = true;
886  VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
887  UpdateObservers();
888}
889
890base::WeakPtr<DownloadDestinationObserver>
891DownloadItemImpl::DestinationObserverAsWeakPtr() {
892  return weak_ptr_factory_.GetWeakPtr();
893}
894
895const net::BoundNetLog& DownloadItemImpl::GetBoundNetLog() const {
896  return bound_net_log_;
897}
898
899void DownloadItemImpl::SetTotalBytes(int64 total_bytes) {
900  total_bytes_ = total_bytes;
901}
902
903void DownloadItemImpl::OnAllDataSaved(const std::string& final_hash) {
904  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
905
906  DCHECK_EQ(IN_PROGRESS_INTERNAL, state_);
907  DCHECK(!all_data_saved_);
908  all_data_saved_ = true;
909  VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
910
911  // Store final hash and null out intermediate serialized hash state.
912  hash_ = final_hash;
913  hash_state_ = "";
914
915  UpdateObservers();
916}
917
918void DownloadItemImpl::MarkAsComplete() {
919  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
920
921  DCHECK(all_data_saved_);
922  end_time_ = base::Time::Now();
923  TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS);
924}
925
926void DownloadItemImpl::DestinationUpdate(int64 bytes_so_far,
927                                         int64 bytes_per_sec,
928                                         const std::string& hash_state) {
929  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
930  VLOG(20) << __FUNCTION__ << " so_far=" << bytes_so_far
931           << " per_sec=" << bytes_per_sec << " download=" << DebugString(true);
932
933  if (GetState() != IN_PROGRESS) {
934    // Ignore if we're no longer in-progress.  This can happen if we race a
935    // Cancel on the UI thread with an update on the FILE thread.
936    //
937    // TODO(rdsmith): Arguably we should let this go through, as this means
938    // the download really did get further than we know before it was
939    // cancelled.  But the gain isn't very large, and the code is more
940    // fragile if it has to support in progress updates in a non-in-progress
941    // state.  This issue should be readdressed when we revamp performance
942    // reporting.
943    return;
944  }
945  bytes_per_sec_ = bytes_per_sec;
946  hash_state_ = hash_state;
947  received_bytes_ = bytes_so_far;
948
949  // If we've received more data than we were expecting (bad server info?),
950  // revert to 'unknown size mode'.
951  if (received_bytes_ > total_bytes_)
952    total_bytes_ = 0;
953
954  if (bound_net_log_.IsLoggingAllEvents()) {
955    bound_net_log_.AddEvent(
956        net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED,
957        net::NetLog::Int64Callback("bytes_so_far", received_bytes_));
958  }
959
960  UpdateObservers();
961}
962
963void DownloadItemImpl::DestinationError(DownloadInterruptReason reason) {
964  // Postpone recognition of this error until after file name determination
965  // has completed and the intermediate file has been renamed to simplify
966  // resumption conditions.
967  if (current_path_.empty() || target_path_.empty())
968    destination_error_ = reason;
969  else
970    Interrupt(reason);
971}
972
973void DownloadItemImpl::DestinationCompleted(const std::string& final_hash) {
974  VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
975  if (GetState() != IN_PROGRESS)
976    return;
977  OnAllDataSaved(final_hash);
978  MaybeCompleteDownload();
979}
980
981// **** Download progression cascade
982
983void DownloadItemImpl::Init(bool active,
984                            DownloadType download_type) {
985  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
986
987  if (active)
988    RecordDownloadCount(START_COUNT);
989
990  std::string file_name;
991  if (download_type == SRC_HISTORY_IMPORT) {
992    // target_path_ works for History and Save As versions.
993    file_name = target_path_.AsUTF8Unsafe();
994  } else {
995    // See if it's set programmatically.
996    file_name = forced_file_path_.AsUTF8Unsafe();
997    // Possibly has a 'download' attribute for the anchor.
998    if (file_name.empty())
999      file_name = suggested_filename_;
1000    // From the URL file name.
1001    if (file_name.empty())
1002      file_name = GetURL().ExtractFileName();
1003  }
1004
1005  base::Callback<base::Value*(net::NetLog::LogLevel)> active_data = base::Bind(
1006      &ItemActivatedNetLogCallback, this, download_type, &file_name);
1007  if (active) {
1008    bound_net_log_.BeginEvent(
1009        net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, active_data);
1010  } else {
1011    bound_net_log_.AddEvent(
1012        net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, active_data);
1013  }
1014
1015  VLOG(20) << __FUNCTION__ << "() " << DebugString(true);
1016}
1017
1018// We're starting the download.
1019void DownloadItemImpl::Start(
1020    scoped_ptr<DownloadFile> file,
1021    scoped_ptr<DownloadRequestHandleInterface> req_handle) {
1022  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1023  DCHECK(!download_file_.get());
1024  DCHECK(file.get());
1025  DCHECK(req_handle.get());
1026
1027  download_file_ = file.Pass();
1028  request_handle_ = req_handle.Pass();
1029
1030  if (GetState() == CANCELLED) {
1031    // The download was in the process of resuming when it was cancelled. Don't
1032    // proceed.
1033    ReleaseDownloadFile(true);
1034    request_handle_->CancelRequest();
1035    return;
1036  }
1037
1038  TransitionTo(IN_PROGRESS_INTERNAL, UPDATE_OBSERVERS);
1039
1040  BrowserThread::PostTask(
1041      BrowserThread::FILE, FROM_HERE,
1042      base::Bind(&DownloadFile::Initialize,
1043                 // Safe because we control download file lifetime.
1044                 base::Unretained(download_file_.get()),
1045                 base::Bind(&DownloadItemImpl::OnDownloadFileInitialized,
1046                            weak_ptr_factory_.GetWeakPtr())));
1047}
1048
1049void DownloadItemImpl::OnDownloadFileInitialized(
1050    DownloadInterruptReason result) {
1051  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1052  if (result != DOWNLOAD_INTERRUPT_REASON_NONE) {
1053    Interrupt(result);
1054    // TODO(rdsmith/asanka): Arguably we should show this in the UI, but
1055    // it's not at all clear what to show--we haven't done filename
1056    // determination, so we don't know what name to display.  OTOH,
1057    // the failure mode of not showing the DI if the file initialization
1058    // fails isn't a good one.  Can we hack up a name based on the
1059    // URLRequest?  We'll need to make sure that initialization happens
1060    // properly.  Possibly the right thing is to have the UI handle
1061    // this case specially.
1062    return;
1063  }
1064
1065  delegate_->DetermineDownloadTarget(
1066      this, base::Bind(&DownloadItemImpl::OnDownloadTargetDetermined,
1067                       weak_ptr_factory_.GetWeakPtr()));
1068}
1069
1070// Called by delegate_ when the download target path has been
1071// determined.
1072void DownloadItemImpl::OnDownloadTargetDetermined(
1073    const base::FilePath& target_path,
1074    TargetDisposition disposition,
1075    DownloadDangerType danger_type,
1076    const base::FilePath& intermediate_path) {
1077  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1078
1079  // If the |target_path| is empty, then we consider this download to be
1080  // canceled.
1081  if (target_path.empty()) {
1082    Cancel(true);
1083    return;
1084  }
1085
1086  // TODO(rdsmith,asanka): We are ignoring the possibility that the download
1087  // has been interrupted at this point until we finish the intermediate
1088  // rename and set the full path.  That's dangerous, because we might race
1089  // with resumption, either manual (because the interrupt is visible to the
1090  // UI) or automatic.  If we keep the "ignore an error on download until file
1091  // name determination complete" semantics, we need to make sure that the
1092  // error is kept completely invisible until that point.
1093
1094  VLOG(20) << __FUNCTION__ << " " << target_path.value() << " " << disposition
1095           << " " << danger_type << " " << DebugString(true);
1096
1097  target_path_ = target_path;
1098  target_disposition_ = disposition;
1099  SetDangerType(danger_type);
1100
1101  // We want the intermediate and target paths to refer to the same directory so
1102  // that they are both on the same device and subject to same
1103  // space/permission/availability constraints.
1104  DCHECK(intermediate_path.DirName() == target_path.DirName());
1105
1106  // During resumption, we may choose to proceed with the same intermediate
1107  // file. No rename is necessary if our intermediate file already has the
1108  // correct name.
1109  //
1110  // The intermediate name may change from its original value during filename
1111  // determination on resumption, for example if the reason for the interruption
1112  // was the download target running out space, resulting in a user prompt.
1113  if (intermediate_path == current_path_) {
1114    OnDownloadRenamedToIntermediateName(DOWNLOAD_INTERRUPT_REASON_NONE,
1115                                        intermediate_path);
1116    return;
1117  }
1118
1119  // Rename to intermediate name.
1120  // TODO(asanka): Skip this rename if AllDataSaved() is true. This avoids a
1121  //               spurious rename when we can just rename to the final
1122  //               filename. Unnecessary renames may cause bugs like
1123  //               http://crbug.com/74187.
1124  DCHECK(!is_save_package_download_);
1125  DCHECK(download_file_.get());
1126  DownloadFile::RenameCompletionCallback callback =
1127      base::Bind(&DownloadItemImpl::OnDownloadRenamedToIntermediateName,
1128                 weak_ptr_factory_.GetWeakPtr());
1129  BrowserThread::PostTask(
1130      BrowserThread::FILE, FROM_HERE,
1131      base::Bind(&DownloadFile::RenameAndUniquify,
1132                 // Safe because we control download file lifetime.
1133                 base::Unretained(download_file_.get()),
1134                 intermediate_path, callback));
1135}
1136
1137void DownloadItemImpl::OnDownloadRenamedToIntermediateName(
1138    DownloadInterruptReason reason,
1139    const base::FilePath& full_path) {
1140  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1141  VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
1142
1143  if (DOWNLOAD_INTERRUPT_REASON_NONE != destination_error_) {
1144    // Process destination error.  If both |reason| and |destination_error_|
1145    // refer to actual errors, we want to use the |destination_error_| as the
1146    // argument to the Interrupt() routine, as it happened first.
1147    if (reason == DOWNLOAD_INTERRUPT_REASON_NONE)
1148      SetFullPath(full_path);
1149    Interrupt(destination_error_);
1150    destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE;
1151  } else if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) {
1152    Interrupt(reason);
1153    // All file errors result in file deletion above; no need to cleanup.  The
1154    // current_path_ should be empty. Resuming this download will force a
1155    // restart and a re-doing of filename determination.
1156    DCHECK(current_path_.empty());
1157  } else {
1158    SetFullPath(full_path);
1159    UpdateObservers();
1160    MaybeCompleteDownload();
1161  }
1162}
1163
1164// When SavePackage downloads MHTML to GData (see
1165// SavePackageFilePickerChromeOS), GData calls MaybeCompleteDownload() like it
1166// does for non-SavePackage downloads, but SavePackage downloads never satisfy
1167// IsDownloadReadyForCompletion(). GDataDownloadObserver manually calls
1168// DownloadItem::UpdateObservers() when the upload completes so that SavePackage
1169// notices that the upload has completed and runs its normal Finish() pathway.
1170// MaybeCompleteDownload() is never the mechanism by which SavePackage completes
1171// downloads. SavePackage always uses its own Finish() to mark downloads
1172// complete.
1173void DownloadItemImpl::MaybeCompleteDownload() {
1174  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1175  DCHECK(!is_save_package_download_);
1176
1177  if (!IsDownloadReadyForCompletion(
1178          base::Bind(&DownloadItemImpl::MaybeCompleteDownload,
1179                     weak_ptr_factory_.GetWeakPtr())))
1180    return;
1181
1182  // TODO(rdsmith): DCHECK that we only pass through this point
1183  // once per download.  The natural way to do this is by a state
1184  // transition on the DownloadItem.
1185
1186  // Confirm we're in the proper set of states to be here;
1187  // have all data, have a history handle, (validated or safe).
1188  DCHECK_EQ(IN_PROGRESS_INTERNAL, state_);
1189  DCHECK(!IsDangerous());
1190  DCHECK(all_data_saved_);
1191
1192  OnDownloadCompleting();
1193}
1194
1195// Called by MaybeCompleteDownload() when it has determined that the download
1196// is ready for completion.
1197void DownloadItemImpl::OnDownloadCompleting() {
1198  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1199
1200  if (state_ != IN_PROGRESS_INTERNAL)
1201    return;
1202
1203  VLOG(20) << __FUNCTION__ << "()"
1204           << " " << DebugString(true);
1205  DCHECK(!GetTargetFilePath().empty());
1206  DCHECK(!IsDangerous());
1207
1208  // TODO(rdsmith/benjhayden): Remove as part of SavePackage integration.
1209  if (is_save_package_download_) {
1210    // Avoid doing anything on the file thread; there's nothing we control
1211    // there.
1212    // Strictly speaking, this skips giving the embedder a chance to open
1213    // the download.  But on a save package download, there's no real
1214    // concept of opening.
1215    Completed();
1216    return;
1217  }
1218
1219  DCHECK(download_file_.get());
1220  // Unilaterally rename; even if it already has the right name,
1221  // we need theannotation.
1222  DownloadFile::RenameCompletionCallback callback =
1223      base::Bind(&DownloadItemImpl::OnDownloadRenamedToFinalName,
1224                 weak_ptr_factory_.GetWeakPtr());
1225  BrowserThread::PostTask(
1226      BrowserThread::FILE, FROM_HERE,
1227      base::Bind(&DownloadFile::RenameAndAnnotate,
1228                 base::Unretained(download_file_.get()),
1229                 GetTargetFilePath(), callback));
1230}
1231
1232void DownloadItemImpl::OnDownloadRenamedToFinalName(
1233    DownloadInterruptReason reason,
1234    const base::FilePath& full_path) {
1235  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1236  DCHECK(!is_save_package_download_);
1237
1238  // If a cancel or interrupt hit, we'll cancel the DownloadFile, which
1239  // will result in deleting the file on the file thread.  So we don't
1240  // care about the name having been changed.
1241  if (state_ != IN_PROGRESS_INTERNAL)
1242    return;
1243
1244  VLOG(20) << __FUNCTION__ << "()"
1245           << " full_path = \"" << full_path.value() << "\""
1246           << " " << DebugString(false);
1247
1248  if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) {
1249    Interrupt(reason);
1250
1251    // All file errors should have resulted in in file deletion above. On
1252    // resumption we will need to re-do filename determination.
1253    DCHECK(current_path_.empty());
1254    return;
1255  }
1256
1257  DCHECK(target_path_ == full_path);
1258
1259  if (full_path != current_path_) {
1260    // full_path is now the current and target file path.
1261    DCHECK(!full_path.empty());
1262    SetFullPath(full_path);
1263  }
1264
1265  // Complete the download and release the DownloadFile.
1266  DCHECK(download_file_.get());
1267  ReleaseDownloadFile(false);
1268
1269  // We're not completely done with the download item yet, but at this
1270  // point we're committed to complete the download.  Cancels (or Interrupts,
1271  // though it's not clear how they could happen) after this point will be
1272  // ignored.
1273  TransitionTo(COMPLETING_INTERNAL, DONT_UPDATE_OBSERVERS);
1274
1275  if (delegate_->ShouldOpenDownload(
1276          this, base::Bind(&DownloadItemImpl::DelayedDownloadOpened,
1277                           weak_ptr_factory_.GetWeakPtr()))) {
1278    Completed();
1279  } else {
1280    delegate_delayed_complete_ = true;
1281    UpdateObservers();
1282  }
1283}
1284
1285void DownloadItemImpl::DelayedDownloadOpened(bool auto_opened) {
1286  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1287
1288  auto_opened_ = auto_opened;
1289  Completed();
1290}
1291
1292void DownloadItemImpl::Completed() {
1293  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1294
1295  VLOG(20) << __FUNCTION__ << "() " << DebugString(false);
1296
1297  DCHECK(all_data_saved_);
1298  end_time_ = base::Time::Now();
1299  TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS);
1300  RecordDownloadCompleted(start_tick_, received_bytes_);
1301
1302  if (auto_opened_) {
1303    // If it was already handled by the delegate, do nothing.
1304  } else if (GetOpenWhenComplete() ||
1305             ShouldOpenFileBasedOnExtension() ||
1306             IsTemporary()) {
1307    // If the download is temporary, like in drag-and-drop, do not open it but
1308    // we still need to set it auto-opened so that it can be removed from the
1309    // download shelf.
1310    if (!IsTemporary())
1311      OpenDownload();
1312
1313    auto_opened_ = true;
1314    UpdateObservers();
1315  }
1316}
1317
1318void DownloadItemImpl::OnResumeRequestStarted(DownloadItem* item,
1319                                              net::Error error) {
1320  // If |item| is not NULL, then Start() has been called already, and nothing
1321  // more needs to be done here.
1322  if (item) {
1323    DCHECK_EQ(net::OK, error);
1324    DCHECK_EQ(static_cast<DownloadItem*>(this), item);
1325    return;
1326  }
1327  // Otherwise, the request failed without passing through
1328  // DownloadResourceHandler::OnResponseStarted.
1329  if (error == net::OK)
1330    error = net::ERR_FAILED;
1331  DownloadInterruptReason reason =
1332      ConvertNetErrorToInterruptReason(error, DOWNLOAD_INTERRUPT_FROM_NETWORK);
1333  DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, reason);
1334  Interrupt(reason);
1335}
1336
1337// **** End of Download progression cascade
1338
1339// An error occurred somewhere.
1340void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) {
1341  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1342
1343  // Somewhat counter-intuitively, it is possible for us to receive an
1344  // interrupt after we've already been interrupted.  The generation of
1345  // interrupts from the file thread Renames and the generation of
1346  // interrupts from disk writes go through two different mechanisms (driven
1347  // by rename requests from UI thread and by write requests from IO thread,
1348  // respectively), and since we choose not to keep state on the File thread,
1349  // this is the place where the races collide.  It's also possible for
1350  // interrupts to race with cancels.
1351
1352  // Whatever happens, the first one to hit the UI thread wins.
1353  if (state_ != IN_PROGRESS_INTERNAL && state_ != RESUMING_INTERNAL)
1354    return;
1355
1356  last_reason_ = reason;
1357
1358  ResumeMode resume_mode = GetResumeMode();
1359
1360  if (state_ == IN_PROGRESS_INTERNAL) {
1361    // Cancel (delete file) if we're going to restart; no point in leaving
1362    // data around we aren't going to use.  Also cancel if resumption isn't
1363    // enabled for the same reason.
1364    ReleaseDownloadFile(resume_mode == RESUME_MODE_IMMEDIATE_RESTART ||
1365                        resume_mode == RESUME_MODE_USER_RESTART ||
1366                        !IsDownloadResumptionEnabled());
1367
1368    // Cancel the originating URL request.
1369    request_handle_->CancelRequest();
1370  } else {
1371    DCHECK(!download_file_.get());
1372  }
1373
1374  // Reset all data saved, as even if we did save all the data we're going
1375  // to go through another round of downloading when we resume.
1376  // There's a potential problem here in the abstract, as if we did download
1377  // all the data and then run into a continuable error, on resumption we
1378  // won't download any more data.  However, a) there are currently no
1379  // continuable errors that can occur after we download all the data, and
1380  // b) if there were, that would probably simply result in a null range
1381  // request, which would generate a DestinationCompleted() notification
1382  // from the DownloadFile, which would behave properly with setting
1383  // all_data_saved_ to false here.
1384  all_data_saved_ = false;
1385
1386  TransitionTo(INTERRUPTED_INTERNAL, DONT_UPDATE_OBSERVERS);
1387  RecordDownloadInterrupted(reason, received_bytes_, total_bytes_);
1388
1389  AutoResumeIfValid();
1390  UpdateObservers();
1391}
1392
1393void DownloadItemImpl::ReleaseDownloadFile(bool destroy_file) {
1394  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1395
1396  if (destroy_file) {
1397    BrowserThread::PostTask(
1398        BrowserThread::FILE, FROM_HERE,
1399        // Will be deleted at end of task execution.
1400        base::Bind(&DownloadFileCancel, base::Passed(&download_file_)));
1401    // Avoid attempting to reuse the intermediate file by clearing out
1402    // current_path_.
1403    current_path_.clear();
1404  } else {
1405    BrowserThread::PostTask(
1406        BrowserThread::FILE,
1407        FROM_HERE,
1408        base::Bind(base::IgnoreResult(&DownloadFileDetach),
1409                   // Will be deleted at end of task execution.
1410                   base::Passed(&download_file_)));
1411  }
1412  // Don't accept any more messages from the DownloadFile, and null
1413  // out any previous "all data received".  This also breaks links to
1414  // other entities we've given out weak pointers to.
1415  weak_ptr_factory_.InvalidateWeakPtrs();
1416}
1417
1418bool DownloadItemImpl::IsDownloadReadyForCompletion(
1419    const base::Closure& state_change_notification) {
1420  // If we don't have all the data, the download is not ready for
1421  // completion.
1422  if (!AllDataSaved())
1423    return false;
1424
1425  // If the download is dangerous, but not yet validated, it's not ready for
1426  // completion.
1427  if (IsDangerous())
1428    return false;
1429
1430  // If the download isn't active (e.g. has been cancelled) it's not
1431  // ready for completion.
1432  if (state_ != IN_PROGRESS_INTERNAL)
1433    return false;
1434
1435  // If the target filename hasn't been determined, then it's not ready for
1436  // completion. This is checked in ReadyForDownloadCompletionDone().
1437  if (GetTargetFilePath().empty())
1438    return false;
1439
1440  // This is checked in NeedsRename(). Without this conditional,
1441  // browser_tests:DownloadTest.DownloadMimeType fails the DCHECK.
1442  if (target_path_.DirName() != current_path_.DirName())
1443    return false;
1444
1445  // Give the delegate a chance to hold up a stop sign.  It'll call
1446  // use back through the passed callback if it does and that state changes.
1447  if (!delegate_->ShouldCompleteDownload(this, state_change_notification))
1448    return false;
1449
1450  return true;
1451}
1452
1453void DownloadItemImpl::TransitionTo(DownloadInternalState new_state,
1454                                    ShouldUpdateObservers notify_action) {
1455  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1456
1457  if (state_ == new_state)
1458    return;
1459
1460  DownloadInternalState old_state = state_;
1461  state_ = new_state;
1462
1463  switch (state_) {
1464    case COMPLETING_INTERNAL:
1465      bound_net_log_.AddEvent(
1466          net::NetLog::TYPE_DOWNLOAD_ITEM_COMPLETING,
1467          base::Bind(&ItemCompletingNetLogCallback, received_bytes_, &hash_));
1468      break;
1469    case COMPLETE_INTERNAL:
1470      bound_net_log_.AddEvent(
1471          net::NetLog::TYPE_DOWNLOAD_ITEM_FINISHED,
1472          base::Bind(&ItemFinishedNetLogCallback, auto_opened_));
1473      break;
1474    case INTERRUPTED_INTERNAL:
1475      bound_net_log_.AddEvent(
1476          net::NetLog::TYPE_DOWNLOAD_ITEM_INTERRUPTED,
1477          base::Bind(&ItemInterruptedNetLogCallback, last_reason_,
1478                     received_bytes_, &hash_state_));
1479      break;
1480    case IN_PROGRESS_INTERNAL:
1481      if (old_state == INTERRUPTED_INTERNAL) {
1482        bound_net_log_.AddEvent(
1483            net::NetLog::TYPE_DOWNLOAD_ITEM_RESUMED,
1484            base::Bind(&ItemResumingNetLogCallback,
1485                       false, last_reason_, received_bytes_, &hash_state_));
1486      }
1487      break;
1488    case CANCELLED_INTERNAL:
1489      bound_net_log_.AddEvent(
1490          net::NetLog::TYPE_DOWNLOAD_ITEM_CANCELED,
1491          base::Bind(&ItemCanceledNetLogCallback, received_bytes_,
1492                     &hash_state_));
1493      break;
1494    default:
1495      break;
1496  }
1497
1498  VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true)
1499    << " " << InternalToExternalState(old_state)
1500    << " " << InternalToExternalState(state_);
1501
1502  bool is_done = (state_ != IN_PROGRESS_INTERNAL &&
1503                  state_ != COMPLETING_INTERNAL);
1504  bool was_done = (old_state != IN_PROGRESS_INTERNAL &&
1505                   old_state != COMPLETING_INTERNAL);
1506  // Termination
1507  if (is_done && !was_done)
1508    bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE);
1509
1510  // Resumption
1511  if (was_done && !is_done) {
1512    std::string file_name(target_path_.BaseName().AsUTF8Unsafe());
1513    bound_net_log_.BeginEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE,
1514                              base::Bind(&ItemActivatedNetLogCallback,
1515                                         this, SRC_ACTIVE_DOWNLOAD,
1516                                         &file_name));
1517  }
1518
1519  if (notify_action == UPDATE_OBSERVERS)
1520    UpdateObservers();
1521}
1522
1523void DownloadItemImpl::SetDangerType(DownloadDangerType danger_type) {
1524  if (danger_type != danger_type_) {
1525    bound_net_log_.AddEvent(
1526        net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED,
1527        base::Bind(&ItemCheckedNetLogCallback, danger_type));
1528  }
1529  danger_type_ = danger_type;
1530}
1531
1532void DownloadItemImpl::SetFullPath(const base::FilePath& new_path) {
1533  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1534  VLOG(20) << __FUNCTION__ << "()"
1535           << " new_path = \"" << new_path.value() << "\""
1536           << " " << DebugString(true);
1537  DCHECK(!new_path.empty());
1538
1539  bound_net_log_.AddEvent(
1540      net::NetLog::TYPE_DOWNLOAD_ITEM_RENAMED,
1541      base::Bind(&ItemRenamedNetLogCallback, &current_path_, &new_path));
1542
1543  current_path_ = new_path;
1544}
1545
1546void DownloadItemImpl::AutoResumeIfValid() {
1547  DVLOG(20) << __FUNCTION__ << "() " << DebugString(true);
1548  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1549  ResumeMode mode = GetResumeMode();
1550
1551  if (mode != RESUME_MODE_IMMEDIATE_RESTART &&
1552      mode != RESUME_MODE_IMMEDIATE_CONTINUE) {
1553    return;
1554  }
1555
1556  auto_resume_count_++;
1557
1558  ResumeInterruptedDownload();
1559}
1560
1561void DownloadItemImpl::ResumeInterruptedDownload() {
1562  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1563
1564  // If the flag for downloads resumption isn't enabled, ignore
1565  // this request.
1566  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
1567  if (!command_line.HasSwitch(switches::kEnableDownloadResumption))
1568    return;
1569
1570  // If we're not interrupted, ignore the request; our caller is drunk.
1571  if (state_ != INTERRUPTED_INTERNAL)
1572    return;
1573
1574  // If we can't get a web contents, we can't resume the download.
1575  // TODO(rdsmith): Find some alternative web contents to use--this
1576  // means we can't restart a download if it's a download imported
1577  // from the history.
1578  if (!GetWebContents())
1579    return;
1580
1581  // Reset the appropriate state if restarting.
1582  ResumeMode mode = GetResumeMode();
1583  if (mode == RESUME_MODE_IMMEDIATE_RESTART ||
1584      mode == RESUME_MODE_USER_RESTART) {
1585    received_bytes_ = 0;
1586    hash_state_ = "";
1587    last_modified_time_ = "";
1588    etag_ = "";
1589  }
1590
1591  scoped_ptr<DownloadUrlParameters> download_params(
1592      DownloadUrlParameters::FromWebContents(GetWebContents(),
1593                                             GetOriginalUrl()));
1594
1595  download_params->set_file_path(GetFullPath());
1596  download_params->set_offset(GetReceivedBytes());
1597  download_params->set_hash_state(GetHashState());
1598  download_params->set_last_modified(GetLastModifiedTime());
1599  download_params->set_etag(GetETag());
1600  download_params->set_callback(
1601      base::Bind(&DownloadItemImpl::OnResumeRequestStarted,
1602                 weak_ptr_factory_.GetWeakPtr()));
1603
1604  delegate_->ResumeInterruptedDownload(download_params.Pass(), GetGlobalId());
1605  // Just in case we were interrupted while paused.
1606  is_paused_ = false;
1607
1608  TransitionTo(RESUMING_INTERNAL, DONT_UPDATE_OBSERVERS);
1609}
1610
1611// static
1612DownloadItem::DownloadState DownloadItemImpl::InternalToExternalState(
1613    DownloadInternalState internal_state) {
1614  switch (internal_state) {
1615    case IN_PROGRESS_INTERNAL:
1616      return IN_PROGRESS;
1617    case COMPLETING_INTERNAL:
1618      return IN_PROGRESS;
1619    case COMPLETE_INTERNAL:
1620      return COMPLETE;
1621    case CANCELLED_INTERNAL:
1622      return CANCELLED;
1623    case INTERRUPTED_INTERNAL:
1624      return INTERRUPTED;
1625    case RESUMING_INTERNAL:
1626      return INTERRUPTED;
1627    case MAX_DOWNLOAD_INTERNAL_STATE:
1628      break;
1629  }
1630  NOTREACHED();
1631  return MAX_DOWNLOAD_STATE;
1632}
1633
1634// static
1635DownloadItemImpl::DownloadInternalState
1636DownloadItemImpl::ExternalToInternalState(
1637    DownloadState external_state) {
1638  switch (external_state) {
1639    case IN_PROGRESS:
1640      return IN_PROGRESS_INTERNAL;
1641    case COMPLETE:
1642      return COMPLETE_INTERNAL;
1643    case CANCELLED:
1644      return CANCELLED_INTERNAL;
1645    case INTERRUPTED:
1646      return INTERRUPTED_INTERNAL;
1647    default:
1648      NOTREACHED();
1649  }
1650  return MAX_DOWNLOAD_INTERNAL_STATE;
1651}
1652
1653const char* DownloadItemImpl::DebugDownloadStateString(
1654    DownloadInternalState state) {
1655  switch (state) {
1656    case IN_PROGRESS_INTERNAL:
1657      return "IN_PROGRESS";
1658    case COMPLETING_INTERNAL:
1659      return "COMPLETING";
1660    case COMPLETE_INTERNAL:
1661      return "COMPLETE";
1662    case CANCELLED_INTERNAL:
1663      return "CANCELLED";
1664    case INTERRUPTED_INTERNAL:
1665      return "INTERRUPTED";
1666    case RESUMING_INTERNAL:
1667      return "RESUMING";
1668    case MAX_DOWNLOAD_INTERNAL_STATE:
1669      break;
1670  };
1671  NOTREACHED() << "Unknown download state " << state;
1672  return "unknown";
1673}
1674
1675const char* DownloadItemImpl::DebugResumeModeString(ResumeMode mode) {
1676  switch (mode) {
1677    case RESUME_MODE_INVALID:
1678      return "INVALID";
1679    case RESUME_MODE_IMMEDIATE_CONTINUE:
1680      return "IMMEDIATE_CONTINUE";
1681    case RESUME_MODE_IMMEDIATE_RESTART:
1682      return "IMMEDIATE_RESTART";
1683    case RESUME_MODE_USER_CONTINUE:
1684      return "USER_CONTINUE";
1685    case RESUME_MODE_USER_RESTART:
1686      return "USER_RESTART";
1687  }
1688  NOTREACHED() << "Unknown resume mode " << mode;
1689  return "unknown";
1690}
1691
1692}  // namespace content
1693