OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/download/download_item.h" | 5 #include "content/browser/download/download_item.h" |
6 | 6 |
7 #include <vector> | |
8 | |
9 #include "base/basictypes.h" | |
10 #include "base/bind.h" | |
11 #include "base/file_util.h" | |
12 #include "base/format_macros.h" | |
13 #include "base/i18n/case_conversion.h" | |
14 #include "base/i18n/string_search.h" | |
15 #include "base/logging.h" | |
16 #include "base/metrics/histogram.h" | |
17 #include "base/stringprintf.h" | |
18 #include "base/utf_string_conversions.h" | |
19 #include "content/browser/download/download_create_info.h" | |
20 #include "content/browser/download/download_file.h" | |
21 #include "content/browser/download/download_file_manager.h" | |
22 #include "content/browser/download/download_id.h" | |
23 #include "content/browser/download/download_manager.h" | |
24 #include "content/browser/download/download_persistent_store_info.h" | |
25 #include "content/browser/download/download_request_handle.h" | |
26 #include "content/browser/download/download_stats.h" | |
27 #include "content/browser/download/interrupt_reasons.h" | |
28 #include "content/browser/tab_contents/tab_contents.h" | |
29 #include "content/public/browser/browser_thread.h" | |
30 #include "content/public/browser/content_browser_client.h" | |
31 #include "content/public/browser/download_manager_delegate.h" | |
32 #include "net/base/net_util.h" | |
33 | |
34 using content::BrowserThread; | |
35 | |
36 // A DownloadItem normally goes through the following states: | |
37 // * Created (when download starts) | |
38 // * Made visible to consumers (e.g. Javascript) after the | |
39 // destination file has been determined. | |
40 // * Entered into the history database. | |
41 // * Made visible in the download shelf. | |
42 // * All data is saved. Note that the actual data download occurs | |
43 // in parallel with the above steps, but until those steps are | |
44 // complete, completion of the data download will be ignored. | |
45 // * Download file is renamed to its final name, and possibly | |
46 // auto-opened. | |
47 // TODO(rdsmith): This progress should be reflected in | |
48 // DownloadItem::DownloadState and a state transition table/state diagram. | |
49 // | |
50 // TODO(rdsmith): This description should be updated to reflect the cancel | |
51 // pathways. | |
52 | |
53 namespace { | |
54 | |
55 static void DeleteDownloadedFile(const FilePath& path) { | |
56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
57 | |
58 // Make sure we only delete files. | |
59 if (!file_util::DirectoryExists(path)) | |
60 file_util::Delete(path, false); | |
61 } | |
62 | |
63 const char* DebugSafetyStateString(DownloadItem::SafetyState state) { | |
64 switch (state) { | |
65 case DownloadItem::SAFE: | |
66 return "SAFE"; | |
67 case DownloadItem::DANGEROUS: | |
68 return "DANGEROUS"; | |
69 case DownloadItem::DANGEROUS_BUT_VALIDATED: | |
70 return "DANGEROUS_BUT_VALIDATED"; | |
71 default: | |
72 NOTREACHED() << "Unknown safety state " << state; | |
73 return "unknown"; | |
74 }; | |
75 } | |
76 | |
77 const char* DebugDownloadStateString(DownloadItem::DownloadState state) { | |
78 switch (state) { | |
79 case DownloadItem::IN_PROGRESS: | |
80 return "IN_PROGRESS"; | |
81 case DownloadItem::COMPLETE: | |
82 return "COMPLETE"; | |
83 case DownloadItem::CANCELLED: | |
84 return "CANCELLED"; | |
85 case DownloadItem::REMOVING: | |
86 return "REMOVING"; | |
87 case DownloadItem::INTERRUPTED: | |
88 return "INTERRUPTED"; | |
89 default: | |
90 NOTREACHED() << "Unknown download state " << state; | |
91 return "unknown"; | |
92 }; | |
93 } | |
94 | |
95 // Classes to null out request handle calls (for SavePage DownloadItems, which | |
96 // may have, e.g., Cancel() called on them without it doing anything) | |
97 // and to DCHECK on them (for history DownloadItems, which should never have | |
98 // any operation that implies an off-thread component, since they don't | |
99 // have any). | |
100 class NullDownloadRequestHandle : public DownloadRequestHandleInterface { | |
101 public: | |
102 NullDownloadRequestHandle() {} | |
103 | |
104 // DownloadRequestHandleInterface calls | |
105 virtual TabContents* GetTabContents() const OVERRIDE { | |
106 return NULL; | |
107 } | |
108 virtual DownloadManager* GetDownloadManager() const OVERRIDE { | |
109 return NULL; | |
110 } | |
111 virtual void PauseRequest() const OVERRIDE {} | |
112 virtual void ResumeRequest() const OVERRIDE {} | |
113 virtual void CancelRequest() const OVERRIDE {} | |
114 virtual std::string DebugString() const OVERRIDE { | |
115 return "Null DownloadRequestHandle"; | |
116 } | |
117 }; | |
118 | |
119 | |
120 } // namespace | |
121 | |
122 // Our download table ID starts at 1, so we use 0 to represent a download that | 7 // Our download table ID starts at 1, so we use 0 to represent a download that |
123 // has started, but has not yet had its data persisted in the table. We use fake | 8 // has started, but has not yet had its data persisted in the table. We use fake |
124 // database handles in incognito mode starting at -1 and progressively getting | 9 // database handles in incognito mode starting at -1 and progressively getting |
125 // more negative. | 10 // more negative. |
126 // static | 11 // static |
127 const int DownloadItem::kUninitializedHandle = 0; | 12 const int DownloadItem::kUninitializedHandle = 0; |
128 | 13 |
129 const char DownloadItem::kEmptyFileHash[] = ""; | 14 const char DownloadItem::kEmptyFileHash[] = ""; |
130 | 15 |
131 // Constructor for reading from the history service. | 16 DownloadItem::~DownloadItem() { |
132 DownloadItem::DownloadItem(DownloadManager* download_manager, | |
133 const DownloadPersistentStoreInfo& info) | |
134 : download_id_(download_manager->GetNextId()), | |
135 full_path_(info.path), | |
136 url_chain_(1, info.url), | |
137 referrer_url_(info.referrer_url), | |
138 total_bytes_(info.total_bytes), | |
139 received_bytes_(info.received_bytes), | |
140 start_tick_(base::TimeTicks()), | |
141 state_(static_cast<DownloadState>(info.state)), | |
142 start_time_(info.start_time), | |
143 end_time_(info.end_time), | |
144 db_handle_(info.db_handle), | |
145 download_manager_(download_manager), | |
146 is_paused_(false), | |
147 open_when_complete_(false), | |
148 file_externally_removed_(false), | |
149 safety_state_(SAFE), | |
150 auto_opened_(false), | |
151 is_otr_(false), | |
152 is_temporary_(false), | |
153 all_data_saved_(false), | |
154 opened_(info.opened), | |
155 open_enabled_(true), | |
156 delegate_delayed_complete_(false) { | |
157 if (IsInProgress()) | |
158 state_ = CANCELLED; | |
159 if (IsComplete()) | |
160 all_data_saved_ = true; | |
161 Init(false /* not actively downloading */); | |
162 } | 17 } |
163 | |
164 // Constructing for a regular download: | |
165 DownloadItem::DownloadItem(DownloadManager* download_manager, | |
166 const DownloadCreateInfo& info, | |
167 DownloadRequestHandleInterface* request_handle, | |
168 bool is_otr) | |
169 : state_info_(info.original_name, info.save_info.file_path, | |
170 info.has_user_gesture, info.transition_type, | |
171 info.prompt_user_for_save_location, info.path_uniquifier, | |
172 DownloadStateInfo::NOT_DANGEROUS), | |
173 request_handle_(request_handle), | |
174 download_id_(info.download_id), | |
175 full_path_(info.path), | |
176 url_chain_(info.url_chain), | |
177 referrer_url_(info.referrer_url), | |
178 suggested_filename_(UTF16ToUTF8(info.save_info.suggested_name)), | |
179 content_disposition_(info.content_disposition), | |
180 mime_type_(info.mime_type), | |
181 original_mime_type_(info.original_mime_type), | |
182 referrer_charset_(info.referrer_charset), | |
183 total_bytes_(info.total_bytes), | |
184 received_bytes_(0), | |
185 last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE), | |
186 start_tick_(base::TimeTicks::Now()), | |
187 state_(IN_PROGRESS), | |
188 start_time_(info.start_time), | |
189 db_handle_(DownloadItem::kUninitializedHandle), | |
190 download_manager_(download_manager), | |
191 is_paused_(false), | |
192 open_when_complete_(false), | |
193 file_externally_removed_(false), | |
194 safety_state_(SAFE), | |
195 auto_opened_(false), | |
196 is_otr_(is_otr), | |
197 is_temporary_(!info.save_info.file_path.empty()), | |
198 all_data_saved_(false), | |
199 opened_(false), | |
200 open_enabled_(true), | |
201 delegate_delayed_complete_(false) { | |
202 Init(true /* actively downloading */); | |
203 } | |
204 | |
205 // Constructing for the "Save Page As..." feature: | |
206 DownloadItem::DownloadItem(DownloadManager* download_manager, | |
207 const FilePath& path, | |
208 const GURL& url, | |
209 bool is_otr, | |
210 DownloadId download_id) | |
211 : request_handle_(new NullDownloadRequestHandle()), | |
212 download_id_(download_id), | |
213 full_path_(path), | |
214 url_chain_(1, url), | |
215 referrer_url_(GURL()), | |
216 total_bytes_(0), | |
217 received_bytes_(0), | |
218 last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE), | |
219 start_tick_(base::TimeTicks::Now()), | |
220 state_(IN_PROGRESS), | |
221 start_time_(base::Time::Now()), | |
222 db_handle_(DownloadItem::kUninitializedHandle), | |
223 download_manager_(download_manager), | |
224 is_paused_(false), | |
225 open_when_complete_(false), | |
226 file_externally_removed_(false), | |
227 safety_state_(SAFE), | |
228 auto_opened_(false), | |
229 is_otr_(is_otr), | |
230 is_temporary_(false), | |
231 all_data_saved_(false), | |
232 opened_(false), | |
233 open_enabled_(true), | |
234 delegate_delayed_complete_(false) { | |
235 Init(true /* actively downloading */); | |
236 } | |
237 | |
238 DownloadItem::~DownloadItem() { | |
239 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
240 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
241 | |
242 TransitionTo(REMOVING); | |
243 download_manager_->AssertQueueStateConsistent(this); | |
244 } | |
245 | |
246 void DownloadItem::AddObserver(Observer* observer) { | |
247 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
248 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
249 | |
250 observers_.AddObserver(observer); | |
251 } | |
252 | |
253 void DownloadItem::RemoveObserver(Observer* observer) { | |
254 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
255 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
256 | |
257 observers_.RemoveObserver(observer); | |
258 } | |
259 | |
260 void DownloadItem::UpdateObservers() { | |
261 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
262 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
263 | |
264 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this)); | |
265 } | |
266 | |
267 bool DownloadItem::CanShowInFolder() { | |
268 return !IsCancelled() && !file_externally_removed_; | |
269 } | |
270 | |
271 bool DownloadItem::CanOpenDownload() { | |
272 return !file_externally_removed_; | |
273 } | |
274 | |
275 bool DownloadItem::ShouldOpenFileBasedOnExtension() { | |
276 return download_manager_->delegate()->ShouldOpenFileBasedOnExtension( | |
277 GetUserVerifiedFilePath()); | |
278 } | |
279 | |
280 void DownloadItem::OpenDownload() { | |
281 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
282 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
283 | |
284 if (IsPartialDownload()) { | |
285 open_when_complete_ = !open_when_complete_; | |
286 return; | |
287 } | |
288 | |
289 if (!IsComplete() || file_externally_removed_) | |
290 return; | |
291 | |
292 // Ideally, we want to detect errors in opening and report them, but we | |
293 // don't generally have the proper interface for that to the external | |
294 // program that opens the file. So instead we spawn a check to update | |
295 // the UI if the file has been deleted in parallel with the open. | |
296 download_manager_->CheckForFileRemoval(this); | |
297 download_stats::RecordOpen(end_time(), !opened()); | |
298 opened_ = true; | |
299 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this)); | |
300 download_manager_->MarkDownloadOpened(this); | |
301 | |
302 // For testing: If download opening is disabled on this item, | |
303 // make the rest of the routine a no-op. | |
304 if (!open_enabled_) | |
305 return; | |
306 | |
307 content::GetContentClient()->browser()->OpenItem(full_path()); | |
308 } | |
309 | |
310 void DownloadItem::ShowDownloadInShell() { | |
311 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
312 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
313 | |
314 content::GetContentClient()->browser()->ShowItemInFolder(full_path()); | |
315 } | |
316 | |
317 void DownloadItem::DangerousDownloadValidated() { | |
318 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
319 DCHECK_EQ(DANGEROUS, safety_state()); | |
320 | |
321 UMA_HISTOGRAM_ENUMERATION("Download.DangerousDownloadValidated", | |
322 GetDangerType(), | |
323 DownloadStateInfo::DANGEROUS_TYPE_MAX); | |
324 | |
325 safety_state_ = DANGEROUS_BUT_VALIDATED; | |
326 UpdateObservers(); | |
327 | |
328 download_manager_->MaybeCompleteDownload(this); | |
329 } | |
330 | |
331 void DownloadItem::UpdateSize(int64 bytes_so_far) { | |
332 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
333 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
334 | |
335 received_bytes_ = bytes_so_far; | |
336 | |
337 // If we've received more data than we were expecting (bad server info?), | |
338 // revert to 'unknown size mode'. | |
339 if (received_bytes_ > total_bytes_) | |
340 total_bytes_ = 0; | |
341 } | |
342 | |
343 // Updates from the download thread may have been posted while this download | |
344 // was being cancelled in the UI thread, so we'll accept them unless we're | |
345 // complete. | |
346 void DownloadItem::Update(int64 bytes_so_far) { | |
347 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
348 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
349 | |
350 if (!IsInProgress()) { | |
351 NOTREACHED(); | |
352 return; | |
353 } | |
354 UpdateSize(bytes_so_far); | |
355 UpdateObservers(); | |
356 } | |
357 | |
358 // Triggered by a user action. | |
359 void DownloadItem::Cancel(bool user_cancel) { | |
360 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
361 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
362 | |
363 last_reason_ = user_cancel ? | |
364 DOWNLOAD_INTERRUPT_REASON_USER_CANCELED : | |
365 DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN; | |
366 | |
367 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); | |
368 if (!IsPartialDownload()) { | |
369 // Small downloads might be complete before this method has | |
370 // a chance to run. | |
371 return; | |
372 } | |
373 | |
374 download_stats::RecordDownloadCount(download_stats::CANCELLED_COUNT); | |
375 | |
376 TransitionTo(CANCELLED); | |
377 if (user_cancel) | |
378 download_manager_->DownloadCancelledInternal(this); | |
379 } | |
380 | |
381 void DownloadItem::MarkAsComplete() { | |
382 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
383 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
384 | |
385 DCHECK(all_data_saved_); | |
386 end_time_ = base::Time::Now(); | |
387 TransitionTo(COMPLETE); | |
388 } | |
389 | |
390 void DownloadItem::DelayedDownloadOpened() { | |
391 auto_opened_ = true; | |
392 Completed(); | |
393 } | |
394 | |
395 void DownloadItem::OnAllDataSaved(int64 size, const std::string& final_hash) { | |
396 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
397 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
398 | |
399 DCHECK(!all_data_saved_); | |
400 all_data_saved_ = true; | |
401 UpdateSize(size); | |
402 hash_ = final_hash; | |
403 } | |
404 | |
405 void DownloadItem::OnDownloadedFileRemoved() { | |
406 file_externally_removed_ = true; | |
407 UpdateObservers(); | |
408 } | |
409 | |
410 void DownloadItem::Completed() { | |
411 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
412 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
413 | |
414 VLOG(20) << __FUNCTION__ << "() " << DebugString(false); | |
415 | |
416 DCHECK(all_data_saved_); | |
417 end_time_ = base::Time::Now(); | |
418 TransitionTo(COMPLETE); | |
419 download_manager_->DownloadCompleted(id()); | |
420 download_stats::RecordDownloadCompleted(start_tick_, received_bytes_); | |
421 | |
422 if (auto_opened_) { | |
423 // If it was already handled by the delegate, do nothing. | |
424 } else if (open_when_complete() || | |
425 ShouldOpenFileBasedOnExtension() || | |
426 is_temporary()) { | |
427 // If the download is temporary, like in drag-and-drop, do not open it but | |
428 // we still need to set it auto-opened so that it can be removed from the | |
429 // download shelf. | |
430 if (!is_temporary()) | |
431 OpenDownload(); | |
432 | |
433 auto_opened_ = true; | |
434 UpdateObservers(); | |
435 } | |
436 } | |
437 | |
438 void DownloadItem::TransitionTo(DownloadState new_state) { | |
439 if (state_ == new_state) | |
440 return; | |
441 | |
442 state_ = new_state; | |
443 UpdateObservers(); | |
444 } | |
445 | |
446 void DownloadItem::UpdateSafetyState() { | |
447 SafetyState updated_value = state_info_.IsDangerous() ? | |
448 DownloadItem::DANGEROUS : DownloadItem::SAFE; | |
449 if (updated_value != safety_state_) { | |
450 safety_state_ = updated_value; | |
451 UpdateObservers(); | |
452 } | |
453 } | |
454 | |
455 void DownloadItem::UpdateTarget() { | |
456 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
457 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
458 | |
459 if (state_info_.target_name.value().empty()) | |
460 state_info_.target_name = full_path_.BaseName(); | |
461 } | |
462 | |
463 void DownloadItem::Interrupted(int64 size, InterruptReason reason) { | |
464 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
465 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
466 | |
467 if (!IsInProgress()) | |
468 return; | |
469 | |
470 last_reason_ = reason; | |
471 UpdateSize(size); | |
472 download_stats::RecordDownloadInterrupted(reason, | |
473 received_bytes_, | |
474 total_bytes_); | |
475 TransitionTo(INTERRUPTED); | |
476 } | |
477 | |
478 void DownloadItem::Delete(DeleteReason reason) { | |
479 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
480 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
481 | |
482 switch (reason) { | |
483 case DELETE_DUE_TO_USER_DISCARD: | |
484 UMA_HISTOGRAM_ENUMERATION("Download.UserDiscard", GetDangerType(), | |
485 DownloadStateInfo::DANGEROUS_TYPE_MAX); | |
486 break; | |
487 case DELETE_DUE_TO_BROWSER_SHUTDOWN: | |
488 UMA_HISTOGRAM_ENUMERATION("Download.Discard", GetDangerType(), | |
489 DownloadStateInfo::DANGEROUS_TYPE_MAX); | |
490 break; | |
491 default: | |
492 NOTREACHED(); | |
493 } | |
494 | |
495 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
496 base::Bind(&DeleteDownloadedFile, full_path_)); | |
497 Remove(); | |
498 // We have now been deleted. | |
499 } | |
500 | |
501 void DownloadItem::Remove() { | |
502 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
503 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
504 | |
505 download_manager_->AssertQueueStateConsistent(this); | |
506 Cancel(true); | |
507 download_manager_->AssertQueueStateConsistent(this); | |
508 | |
509 TransitionTo(REMOVING); | |
510 download_manager_->RemoveDownload(db_handle_); | |
511 // We have now been deleted. | |
512 } | |
513 | |
514 bool DownloadItem::TimeRemaining(base::TimeDelta* remaining) const { | |
515 if (total_bytes_ <= 0) | |
516 return false; // We never received the content_length for this download. | |
517 | |
518 int64 speed = CurrentSpeed(); | |
519 if (speed == 0) | |
520 return false; | |
521 | |
522 *remaining = base::TimeDelta::FromSeconds( | |
523 (total_bytes_ - received_bytes_) / speed); | |
524 return true; | |
525 } | |
526 | |
527 int64 DownloadItem::CurrentSpeed() const { | |
528 if (is_paused_) | |
529 return 0; | |
530 base::TimeDelta diff = base::TimeTicks::Now() - start_tick_; | |
531 int64 diff_ms = diff.InMilliseconds(); | |
532 return diff_ms == 0 ? 0 : received_bytes_ * 1000 / diff_ms; | |
533 } | |
534 | |
535 int DownloadItem::PercentComplete() const { | |
536 // If the delegate is delaying completion of the download, then we have no | |
537 // idea how long it will take. | |
538 if (delegate_delayed_complete_ || total_bytes_ <= 0) | |
539 return -1; | |
540 | |
541 return static_cast<int>(received_bytes_ * 100.0 / total_bytes_); | |
542 } | |
543 | |
544 void DownloadItem::OnPathDetermined(const FilePath& path) { | |
545 full_path_ = path; | |
546 UpdateTarget(); | |
547 } | |
548 | |
549 void DownloadItem::Rename(const FilePath& full_path) { | |
550 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
551 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
552 | |
553 VLOG(20) << __FUNCTION__ << "()" | |
554 << " full_path = \"" << full_path.value() << "\"" | |
555 << " " << DebugString(true); | |
556 DCHECK(!full_path.empty()); | |
557 full_path_ = full_path; | |
558 } | |
559 | |
560 void DownloadItem::TogglePause() { | |
561 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
562 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
563 | |
564 DCHECK(IsInProgress()); | |
565 if (is_paused_) | |
566 request_handle_->ResumeRequest(); | |
567 else | |
568 request_handle_->PauseRequest(); | |
569 is_paused_ = !is_paused_; | |
570 UpdateObservers(); | |
571 } | |
572 | |
573 void DownloadItem::OnDownloadCompleting(DownloadFileManager* file_manager) { | |
574 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
575 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
576 | |
577 VLOG(20) << __FUNCTION__ << "()" | |
578 << " needs rename = " << NeedsRename() | |
579 << " " << DebugString(true); | |
580 DCHECK_NE(DANGEROUS, safety_state()); | |
581 DCHECK(file_manager); | |
582 | |
583 if (NeedsRename()) { | |
584 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
585 base::Bind(&DownloadFileManager::RenameCompletingDownloadFile, | |
586 file_manager, global_id(), | |
587 GetTargetFilePath(), safety_state() == SAFE)); | |
588 return; | |
589 } | |
590 | |
591 Completed(); | |
592 | |
593 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
594 base::Bind(&DownloadFileManager::CompleteDownload, | |
595 file_manager, global_id())); | |
596 } | |
597 | |
598 void DownloadItem::OnDownloadRenamedToFinalName(const FilePath& full_path) { | |
599 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
600 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
601 | |
602 VLOG(20) << __FUNCTION__ << "()" | |
603 << " full_path = \"" << full_path.value() << "\"" | |
604 << " needed rename = " << NeedsRename() | |
605 << " " << DebugString(false); | |
606 DCHECK(NeedsRename()); | |
607 | |
608 Rename(full_path); | |
609 | |
610 if (download_manager_->delegate()->ShouldOpenDownload(this)) { | |
611 Completed(); | |
612 } else { | |
613 delegate_delayed_complete_ = true; | |
614 } | |
615 } | |
616 | |
617 bool DownloadItem::MatchesQuery(const string16& query) const { | |
618 if (query.empty()) | |
619 return true; | |
620 | |
621 DCHECK_EQ(query, base::i18n::ToLower(query)); | |
622 | |
623 string16 url_raw(UTF8ToUTF16(GetURL().spec())); | |
624 if (base::i18n::StringSearchIgnoringCaseAndAccents(query, url_raw)) | |
625 return true; | |
626 | |
627 // TODO(phajdan.jr): write a test case for the following code. | |
628 // A good test case would be: | |
629 // "/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", | |
630 // L"/\x4f60\x597d\x4f60\x597d", | |
631 // "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD" | |
632 std::string languages; | |
633 TabContents* tab = GetTabContents(); | |
634 if (tab) { | |
635 languages = content::GetContentClient()->browser()->GetAcceptLangs( | |
636 tab->browser_context()); | |
637 } | |
638 string16 url_formatted(net::FormatUrl(GetURL(), languages)); | |
639 if (base::i18n::StringSearchIgnoringCaseAndAccents(query, url_formatted)) | |
640 return true; | |
641 | |
642 string16 path(full_path().LossyDisplayName()); | |
643 return base::i18n::StringSearchIgnoringCaseAndAccents(query, path); | |
644 } | |
645 | |
646 void DownloadItem::SetFileCheckResults(const DownloadStateInfo& state) { | |
647 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
648 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
649 | |
650 VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true); | |
651 state_info_ = state; | |
652 VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true); | |
653 | |
654 UpdateSafetyState(); | |
655 } | |
656 | |
657 DownloadStateInfo::DangerType DownloadItem::GetDangerType() const { | |
658 return state_info_.danger; | |
659 } | |
660 | |
661 bool DownloadItem::IsDangerous() const { | |
662 return state_info_.IsDangerous(); | |
663 } | |
664 | |
665 void DownloadItem::MarkFileDangerous() { | |
666 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
667 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
668 state_info_.danger = DownloadStateInfo::DANGEROUS_FILE; | |
669 UpdateSafetyState(); | |
670 } | |
671 | |
672 void DownloadItem::MarkUrlDangerous() { | |
673 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
674 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
675 state_info_.danger = DownloadStateInfo::DANGEROUS_URL; | |
676 UpdateSafetyState(); | |
677 } | |
678 | |
679 void DownloadItem::MarkContentDangerous() { | |
680 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
681 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
682 state_info_.danger = DownloadStateInfo::DANGEROUS_CONTENT; | |
683 UpdateSafetyState(); | |
684 } | |
685 | |
686 DownloadPersistentStoreInfo DownloadItem::GetPersistentStoreInfo() const { | |
687 return DownloadPersistentStoreInfo(full_path(), | |
688 GetURL(), | |
689 referrer_url(), | |
690 start_time(), | |
691 end_time(), | |
692 received_bytes(), | |
693 total_bytes(), | |
694 state(), | |
695 db_handle(), | |
696 opened()); | |
697 } | |
698 | |
699 TabContents* DownloadItem::GetTabContents() const { | |
700 // TODO(rdsmith): Remove null check after removing GetTabContents() from | |
701 // paths that might be used by DownloadItems created from history import. | |
702 // Currently such items have null request_handle_s, where other items | |
703 // (regular and SavePackage downloads) have actual objects off the pointer. | |
704 if (request_handle_.get()) | |
705 return request_handle_->GetTabContents(); | |
706 return NULL; | |
707 } | |
708 | |
709 FilePath DownloadItem::GetTargetFilePath() const { | |
710 return full_path_.DirName().Append(state_info_.target_name); | |
711 } | |
712 | |
713 FilePath DownloadItem::GetFileNameToReportUser() const { | |
714 if (state_info_.path_uniquifier > 0) { | |
715 FilePath name(state_info_.target_name); | |
716 DownloadFile::AppendNumberToPath(&name, state_info_.path_uniquifier); | |
717 return name; | |
718 } | |
719 return state_info_.target_name; | |
720 } | |
721 | |
722 FilePath DownloadItem::GetUserVerifiedFilePath() const { | |
723 return (safety_state_ == DownloadItem::SAFE) ? | |
724 GetTargetFilePath() : full_path_; | |
725 } | |
726 | |
727 void DownloadItem::OffThreadCancel(DownloadFileManager* file_manager) { | |
728 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
729 request_handle_->CancelRequest(); | |
730 | |
731 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
732 base::Bind(&DownloadFileManager::CancelDownload, | |
733 file_manager, global_id())); | |
734 } | |
735 | |
736 void DownloadItem::Init(bool active) { | |
737 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
738 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
739 | |
740 UpdateTarget(); | |
741 if (active) { | |
742 download_stats::RecordDownloadCount(download_stats::START_COUNT); | |
743 } | |
744 VLOG(20) << __FUNCTION__ << "() " << DebugString(true); | |
745 } | |
746 | |
747 // TODO(ahendrickson) -- Move |INTERRUPTED| from |IsCancelled()| to | |
748 // |IsPartialDownload()|, when resuming interrupted downloads is implemented. | |
749 bool DownloadItem::IsPartialDownload() const { | |
750 return (state_ == IN_PROGRESS); | |
751 } | |
752 | |
753 bool DownloadItem::IsInProgress() const { | |
754 return (state_ == IN_PROGRESS); | |
755 } | |
756 | |
757 bool DownloadItem::IsCancelled() const { | |
758 return (state_ == CANCELLED) || | |
759 (state_ == INTERRUPTED); | |
760 } | |
761 | |
762 bool DownloadItem::IsInterrupted() const { | |
763 return (state_ == INTERRUPTED); | |
764 } | |
765 | |
766 bool DownloadItem::IsComplete() const { | |
767 return (state_ == COMPLETE); | |
768 } | |
769 | |
770 const GURL& DownloadItem::GetURL() const { | |
771 return url_chain_.empty() ? | |
772 GURL::EmptyGURL() : url_chain_.back(); | |
773 } | |
774 | |
775 std::string DownloadItem::DebugString(bool verbose) const { | |
776 std::string description = | |
777 base::StringPrintf("{ id = %d" | |
778 " state = %s", | |
779 download_id_.local(), | |
780 DebugDownloadStateString(state())); | |
781 | |
782 // Construct a string of the URL chain. | |
783 std::string url_list("<none>"); | |
784 if (!url_chain_.empty()) { | |
785 std::vector<GURL>::const_iterator iter = url_chain_.begin(); | |
786 std::vector<GURL>::const_iterator last = url_chain_.end(); | |
787 url_list = (*iter).spec(); | |
788 ++iter; | |
789 for ( ; verbose && (iter != last); ++iter) { | |
790 url_list += " ->\n\t"; | |
791 const GURL& next_url = *iter; | |
792 url_list += next_url.spec(); | |
793 } | |
794 } | |
795 | |
796 if (verbose) { | |
797 description += base::StringPrintf( | |
798 " db_handle = %" PRId64 | |
799 " total_bytes = %" PRId64 | |
800 " received_bytes = %" PRId64 | |
801 " is_paused = %c" | |
802 " is_otr = %c" | |
803 " safety_state = %s" | |
804 " url_chain = \n\t\"%s\"\n\t" | |
805 " target_name = \"%" PRFilePath "\"" | |
806 " full_path = \"%" PRFilePath "\"", | |
807 db_handle(), | |
808 total_bytes(), | |
809 received_bytes(), | |
810 is_paused() ? 'T' : 'F', | |
811 is_otr() ? 'T' : 'F', | |
812 DebugSafetyStateString(safety_state()), | |
813 url_list.c_str(), | |
814 state_info_.target_name.value().c_str(), | |
815 full_path().value().c_str()); | |
816 } else { | |
817 description += base::StringPrintf(" url = \"%s\"", url_list.c_str()); | |
818 } | |
819 | |
820 description += " }"; | |
821 | |
822 return description; | |
823 } | |
OLD | NEW |