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