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