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