| 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 |