1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/download/download_item_model.h" 6 7#include "base/i18n/number_formatting.h" 8#include "base/i18n/rtl.h" 9#include "base/metrics/field_trial.h" 10#include "base/strings/string16.h" 11#include "base/strings/sys_string_conversions.h" 12#include "base/strings/utf_string_conversions.h" 13#include "base/supports_user_data.h" 14#include "base/time/time.h" 15#include "chrome/browser/download/download_crx_util.h" 16#include "chrome/browser/download/download_util.h" 17#include "chrome/browser/safe_browsing/download_feedback_service.h" 18#include "content/public/browser/download_danger_type.h" 19#include "content/public/browser/download_interrupt_reasons.h" 20#include "content/public/browser/download_item.h" 21#include "grit/chromium_strings.h" 22#include "grit/generated_resources.h" 23#include "ui/base/l10n/l10n_util.h" 24#include "ui/base/l10n/time_format.h" 25#include "ui/base/text/bytes_formatting.h" 26#include "ui/base/text/text_elider.h" 27 28using base::TimeDelta; 29using content::DownloadItem; 30 31namespace { 32 33// Per DownloadItem data used by DownloadItemModel. The model doesn't keep any 34// state since there could be multiple models associated with a single 35// DownloadItem, and the lifetime of the model is shorter than the DownloadItem. 36class DownloadItemModelData : public base::SupportsUserData::Data { 37 public: 38 // Get the DownloadItemModelData object for |download|. Returns NULL if 39 // there's no model data. 40 static const DownloadItemModelData* Get(const DownloadItem* download); 41 42 // Get the DownloadItemModelData object for |download|. Creates a model data 43 // object if not found. Always returns a non-NULL pointer, unless OOM. 44 static DownloadItemModelData* GetOrCreate(DownloadItem* download); 45 46 bool should_show_in_shelf() const { return should_show_in_shelf_; } 47 void set_should_show_in_shelf(bool should_show_in_shelf) { 48 should_show_in_shelf_ = should_show_in_shelf; 49 } 50 51 bool should_notify_ui() const { return should_notify_ui_; } 52 void set_should_notify_ui(bool should_notify_ui) { 53 should_notify_ui_ = should_notify_ui; 54 } 55 56 private: 57 DownloadItemModelData(); 58 virtual ~DownloadItemModelData() {} 59 60 static const char kKey[]; 61 62 // Whether the download should be displayed in the download shelf. True by 63 // default. 64 bool should_show_in_shelf_; 65 66 // Whether the UI should be notified when the download is ready to be 67 // presented. 68 bool should_notify_ui_; 69}; 70 71// static 72const char DownloadItemModelData::kKey[] = "DownloadItemModelData key"; 73 74// static 75const DownloadItemModelData* DownloadItemModelData::Get( 76 const DownloadItem* download) { 77 return static_cast<const DownloadItemModelData*>(download->GetUserData(kKey)); 78} 79 80// static 81DownloadItemModelData* DownloadItemModelData::GetOrCreate( 82 DownloadItem* download) { 83 DownloadItemModelData* data = 84 static_cast<DownloadItemModelData*>(download->GetUserData(kKey)); 85 if (data == NULL) { 86 data = new DownloadItemModelData(); 87 download->SetUserData(kKey, data); 88 } 89 return data; 90} 91 92DownloadItemModelData::DownloadItemModelData() 93 : should_show_in_shelf_(true), 94 should_notify_ui_(false) { 95} 96 97string16 InterruptReasonStatusMessage(int reason) { 98 int string_id = 0; 99 100 switch (reason) { 101 case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED: 102 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_ACCESS_DENIED; 103 break; 104 case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE: 105 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_DISK_FULL; 106 break; 107 case content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG: 108 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_PATH_TOO_LONG; 109 break; 110 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE: 111 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_FILE_TOO_LARGE; 112 break; 113 case content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED: 114 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_VIRUS; 115 break; 116 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR: 117 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_TEMPORARY_PROBLEM; 118 break; 119 case content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED: 120 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_BLOCKED; 121 break; 122 case content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED: 123 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SECURITY_CHECK_FAILED; 124 break; 125 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT: 126 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_FILE_TOO_SHORT; 127 break; 128 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED: 129 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_ERROR; 130 break; 131 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT: 132 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_TIMEOUT; 133 break; 134 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED: 135 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NETWORK_DISCONNECTED; 136 break; 137 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN: 138 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SERVER_DOWN; 139 break; 140 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED: 141 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SERVER_PROBLEM; 142 break; 143 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT: 144 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_NO_FILE; 145 break; 146 case content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED: 147 string_id = IDS_DOWNLOAD_STATUS_CANCELLED; 148 break; 149 case content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN: 150 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_SHUTDOWN; 151 break; 152 case content::DOWNLOAD_INTERRUPT_REASON_CRASH: 153 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_CRASH; 154 break; 155 default: 156 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS; 157 break; 158 } 159 160 return l10n_util::GetStringUTF16(string_id); 161} 162 163string16 InterruptReasonMessage(int reason) { 164 int string_id = 0; 165 string16 status_text; 166 167 switch (reason) { 168 case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED: 169 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_ACCESS_DENIED; 170 break; 171 case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE: 172 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_DISK_FULL; 173 break; 174 case content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG: 175 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_PATH_TOO_LONG; 176 break; 177 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE: 178 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FILE_TOO_LARGE; 179 break; 180 case content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED: 181 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_VIRUS; 182 break; 183 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR: 184 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_TEMPORARY_PROBLEM; 185 break; 186 case content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED: 187 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_BLOCKED; 188 break; 189 case content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED: 190 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SECURITY_CHECK_FAILED; 191 break; 192 case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT: 193 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FILE_TOO_SHORT; 194 break; 195 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED: 196 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_ERROR; 197 break; 198 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT: 199 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_TIMEOUT; 200 break; 201 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED: 202 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NETWORK_DISCONNECTED; 203 break; 204 case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN: 205 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_DOWN; 206 break; 207 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED: 208 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SERVER_PROBLEM; 209 break; 210 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT: 211 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_NO_FILE; 212 break; 213 case content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED: 214 string_id = IDS_DOWNLOAD_STATUS_CANCELLED; 215 break; 216 case content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN: 217 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_SHUTDOWN; 218 break; 219 case content::DOWNLOAD_INTERRUPT_REASON_CRASH: 220 string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_CRASH; 221 break; 222 default: 223 string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS; 224 break; 225 } 226 227 status_text = l10n_util::GetStringUTF16(string_id); 228 229 return status_text; 230} 231 232} // namespace 233 234// ----------------------------------------------------------------------------- 235// DownloadItemModel 236 237DownloadItemModel::DownloadItemModel(DownloadItem* download) 238 : download_(download) { 239} 240 241DownloadItemModel::~DownloadItemModel() { 242} 243 244string16 DownloadItemModel::GetInterruptReasonText() const { 245 if (download_->GetState() != DownloadItem::INTERRUPTED || 246 download_->GetLastReason() == 247 content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED) { 248 return string16(); 249 } 250 return InterruptReasonMessage(download_->GetLastReason()); 251} 252 253string16 DownloadItemModel::GetStatusText() const { 254 string16 status_text; 255 switch (download_->GetState()) { 256 case DownloadItem::IN_PROGRESS: 257 status_text = GetInProgressStatusString(); 258 break; 259 case DownloadItem::COMPLETE: 260 if (download_->GetFileExternallyRemoved()) { 261 status_text = l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_REMOVED); 262 } else { 263 status_text.clear(); 264 } 265 break; 266 case DownloadItem::CANCELLED: 267 status_text = l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELLED); 268 break; 269 case DownloadItem::INTERRUPTED: { 270 content::DownloadInterruptReason reason = download_->GetLastReason(); 271 if (reason != content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED) { 272 string16 interrupt_reason = InterruptReasonStatusMessage(reason); 273 status_text = l10n_util::GetStringFUTF16( 274 IDS_DOWNLOAD_STATUS_INTERRUPTED, interrupt_reason); 275 } else { 276 // Same as DownloadItem::CANCELLED. 277 status_text = l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELLED); 278 } 279 break; 280 } 281 default: 282 NOTREACHED(); 283 } 284 285 return status_text; 286} 287 288string16 DownloadItemModel::GetTooltipText(const gfx::Font& font, 289 int max_width) const { 290 string16 tooltip = ui::ElideFilename( 291 download_->GetFileNameToReportUser(), font, max_width); 292 content::DownloadInterruptReason reason = download_->GetLastReason(); 293 if (download_->GetState() == DownloadItem::INTERRUPTED && 294 reason != content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED) { 295 tooltip += ASCIIToUTF16("\n"); 296 tooltip += ui::ElideText(InterruptReasonStatusMessage(reason), 297 font, max_width, ui::ELIDE_AT_END); 298 } 299 return tooltip; 300} 301 302string16 DownloadItemModel::GetWarningText(const gfx::Font& font, 303 int base_width) const { 304 // Should only be called if IsDangerous(). 305 DCHECK(IsDangerous()); 306 string16 elided_filename = 307 ui::ElideFilename(download_->GetFileNameToReportUser(), font, base_width); 308 switch (download_->GetDangerType()) { 309 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: { 310 std::string trial_condition = 311 base::FieldTrialList::FindFullName(download_util::kFinchTrialName); 312 if (trial_condition.empty()) 313 return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL); 314 return download_util::AssembleMalwareFinchString(trial_condition, 315 elided_filename); 316 } 317 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: { 318 if (download_crx_util::IsExtensionDownload(*download_)) { 319 return l10n_util::GetStringUTF16( 320 IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION); 321 } else { 322 return l10n_util::GetStringFUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD, 323 elided_filename); 324 } 325 } 326 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: 327 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: { 328 std::string trial_condition = 329 base::FieldTrialList::FindFullName(download_util::kFinchTrialName); 330 if (trial_condition.empty()) { 331 return l10n_util::GetStringFUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT, 332 elided_filename); 333 } 334 return download_util::AssembleMalwareFinchString(trial_condition, 335 elided_filename); 336 } 337 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: { 338 return l10n_util::GetStringFUTF16(IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT, 339 elided_filename); 340 } 341 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: { 342 return l10n_util::GetStringFUTF16( 343 IDS_PROMPT_DOWNLOAD_CHANGES_SEARCH_SETTINGS, elided_filename); 344 } 345 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: 346 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: 347 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: 348 case content::DOWNLOAD_DANGER_TYPE_MAX: { 349 break; 350 } 351 } 352 NOTREACHED(); 353 return string16(); 354} 355 356string16 DownloadItemModel::GetWarningConfirmButtonText() const { 357 // Should only be called if IsDangerous() 358 DCHECK(IsDangerous()); 359 if (download_->GetDangerType() == 360 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE && 361 download_crx_util::IsExtensionDownload(*download_)) { 362 return l10n_util::GetStringUTF16(IDS_CONTINUE_EXTENSION_DOWNLOAD); 363 } else { 364 return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD); 365 } 366} 367 368int64 DownloadItemModel::GetCompletedBytes() const { 369 return download_->GetReceivedBytes(); 370} 371 372int64 DownloadItemModel::GetTotalBytes() const { 373 return download_->AllDataSaved() ? download_->GetReceivedBytes() : 374 download_->GetTotalBytes(); 375} 376 377// TODO(asanka,rdsmith): Once 'open' moves exclusively to the 378// ChromeDownloadManagerDelegate, we should calculate the percentage here 379// instead of calling into the DownloadItem. 380int DownloadItemModel::PercentComplete() const { 381 return download_->PercentComplete(); 382} 383 384bool DownloadItemModel::IsDangerous() const { 385 return download_->IsDangerous(); 386} 387 388bool DownloadItemModel::IsMalicious() const { 389 if (!IsDangerous()) 390 return false; 391 switch (download_->GetDangerType()) { 392 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: 393 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: 394 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: 395 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: 396 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: 397 return true; 398 399 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: 400 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: 401 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: 402 case content::DOWNLOAD_DANGER_TYPE_MAX: 403 // We shouldn't get any of these due to the IsDangerous() test above. 404 NOTREACHED(); 405 // Fallthrough. 406 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: 407 return false; 408 } 409 NOTREACHED(); 410 return false; 411} 412 413bool DownloadItemModel::ShouldAllowDownloadFeedback() const { 414 if (!IsDangerous()) 415 return false; 416 return safe_browsing::DownloadFeedbackService::IsEnabledForDownload( 417 *download_); 418} 419 420bool DownloadItemModel::ShouldRemoveFromShelfWhenComplete() const { 421 switch (download_->GetState()) { 422 case DownloadItem::IN_PROGRESS: 423 // If the download is dangerous or malicious, we should display a warning 424 // on the shelf until the user accepts the download. 425 if (IsDangerous()) 426 return false; 427 428 // If the download is an extension, temporary, or will be opened 429 // automatically, then it should be removed from the shelf on completion. 430 // TODO(asanka): The logic for deciding opening behavior should be in a 431 // central location. http://crbug.com/167702 432 return (download_crx_util::IsExtensionDownload(*download_) || 433 download_->IsTemporary() || 434 download_->GetOpenWhenComplete() || 435 download_->ShouldOpenFileBasedOnExtension()); 436 437 case DownloadItem::COMPLETE: 438 // If the download completed, then rely on GetAutoOpened() to check for 439 // opening behavior. This should accurately reflect whether the download 440 // was successfully opened. Extensions, for example, may fail to open. 441 return download_->GetAutoOpened() || download_->IsTemporary(); 442 443 case DownloadItem::CANCELLED: 444 case DownloadItem::INTERRUPTED: 445 // Interrupted or cancelled downloads should remain on the shelf. 446 return false; 447 448 case DownloadItem::MAX_DOWNLOAD_STATE: 449 NOTREACHED(); 450 } 451 452 NOTREACHED(); 453 return false; 454} 455 456bool DownloadItemModel::ShouldShowDownloadStartedAnimation() const { 457 return !download_->IsSavePackageDownload() && 458 !download_crx_util::IsExtensionDownload(*download_); 459} 460 461bool DownloadItemModel::ShouldShowInShelf() const { 462 const DownloadItemModelData* data = DownloadItemModelData::Get(download_); 463 return !data || data->should_show_in_shelf(); 464} 465 466void DownloadItemModel::SetShouldShowInShelf(bool should_show) { 467 DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_); 468 data->set_should_show_in_shelf(should_show); 469} 470 471bool DownloadItemModel::ShouldNotifyUI() const { 472 const DownloadItemModelData* data = DownloadItemModelData::Get(download_); 473 return data && data->should_notify_ui(); 474} 475 476void DownloadItemModel::SetShouldNotifyUI(bool should_notify) { 477 DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_); 478 data->set_should_notify_ui(should_notify); 479} 480 481string16 DownloadItemModel::GetProgressSizesString() const { 482 string16 size_ratio; 483 int64 size = GetCompletedBytes(); 484 int64 total = GetTotalBytes(); 485 if (total > 0) { 486 ui::DataUnits amount_units = ui::GetByteDisplayUnits(total); 487 string16 simple_size = ui::FormatBytesWithUnits(size, amount_units, false); 488 489 // In RTL locales, we render the text "size/total" in an RTL context. This 490 // is problematic since a string such as "123/456 MB" is displayed 491 // as "MB 123/456" because it ends with an LTR run. In order to solve this, 492 // we mark the total string as an LTR string if the UI layout is 493 // right-to-left so that the string "456 MB" is treated as an LTR run. 494 string16 simple_total = base::i18n::GetDisplayStringInLTRDirectionality( 495 ui::FormatBytesWithUnits(total, amount_units, true)); 496 size_ratio = l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_SIZES, 497 simple_size, simple_total); 498 } else { 499 size_ratio = ui::FormatBytes(size); 500 } 501 return size_ratio; 502} 503 504string16 DownloadItemModel::GetInProgressStatusString() const { 505 DCHECK_EQ(DownloadItem::IN_PROGRESS, download_->GetState()); 506 507 TimeDelta time_remaining; 508 // time_remaining is only known if the download isn't paused. 509 bool time_remaining_known = (!download_->IsPaused() && 510 download_->TimeRemaining(&time_remaining)); 511 512 // Indication of progress. (E.g.:"100/200 MB" or "100MB") 513 string16 size_ratio = GetProgressSizesString(); 514 515 // The download is a CRX (app, extension, theme, ...) and it is being unpacked 516 // and validated. 517 if (download_->AllDataSaved() && 518 download_crx_util::IsExtensionDownload(*download_)) { 519 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING); 520 } 521 522 // A paused download: "100/120 MB, Paused" 523 if (download_->IsPaused()) { 524 return l10n_util::GetStringFUTF16( 525 IDS_DOWNLOAD_STATUS_IN_PROGRESS, size_ratio, 526 l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED)); 527 } 528 529 // A download scheduled to be opened when complete: "Opening in 10 secs" 530 if (download_->GetOpenWhenComplete()) { 531 if (!time_remaining_known) 532 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE); 533 534 return l10n_util::GetStringFUTF16( 535 IDS_DOWNLOAD_STATUS_OPEN_IN, 536 ui::TimeFormat::TimeRemainingShort(time_remaining)); 537 } 538 539 // In progress download with known time left: "100/120 MB, 10 secs left" 540 if (time_remaining_known) { 541 return l10n_util::GetStringFUTF16( 542 IDS_DOWNLOAD_STATUS_IN_PROGRESS, size_ratio, 543 ui::TimeFormat::TimeRemaining(time_remaining)); 544 } 545 546 // In progress download with no known time left and non-zero completed bytes: 547 // "100/120 MB" or "100 MB" 548 if (GetCompletedBytes() > 0) 549 return size_ratio; 550 551 // Instead of displaying "0 B" we say "Starting..." 552 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_STARTING); 553} 554