download_util.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2010 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// Download utility implementation 6 7#include "chrome/browser/download/download_util.h" 8 9#if defined(OS_WIN) 10#include <shobjidl.h> 11#endif 12#include <string> 13 14#include "app/l10n_util.h" 15#include "app/resource_bundle.h" 16#include "base/file_util.h" 17#include "base/i18n/rtl.h" 18#include "base/i18n/time_formatting.h" 19#include "base/path_service.h" 20#include "base/singleton.h" 21#include "base/string16.h" 22#include "base/string_number_conversions.h" 23#include "base/stringprintf.h" 24#include "base/sys_string_conversions.h" 25#include "base/thread_restrictions.h" 26#include "base/utf_string_conversions.h" 27#include "base/values.h" 28#include "base/win/windows_version.h" 29#include "chrome/browser/browser_list.h" 30#include "chrome/browser/browser_process.h" 31#include "chrome/browser/browser_thread.h" 32#include "chrome/browser/download/download_item.h" 33#include "chrome/browser/download/download_item_model.h" 34#include "chrome/browser/download/download_manager.h" 35#include "chrome/browser/extensions/crx_installer.h" 36#include "chrome/browser/extensions/extension_install_ui.h" 37#include "chrome/browser/extensions/extensions_service.h" 38#include "chrome/browser/history/download_create_info.h" 39#include "chrome/browser/net/chrome_url_request_context.h" 40#include "chrome/browser/profile.h" 41#include "chrome/browser/renderer_host/resource_dispatcher_host.h" 42#include "chrome/browser/tab_contents/infobar_delegate.h" 43#include "chrome/browser/tab_contents/tab_contents.h" 44#include "chrome/browser/ui/browser.h" 45#include "chrome/common/chrome_paths.h" 46#include "chrome/common/notification_service.h" 47#include "chrome/common/time_format.h" 48#include "gfx/canvas_skia.h" 49#include "gfx/rect.h" 50#include "grit/generated_resources.h" 51#include "grit/locale_settings.h" 52#include "grit/theme_resources.h" 53#include "net/base/mime_util.h" 54#include "net/base/net_util.h" 55#include "skia/ext/image_operations.h" 56#include "third_party/skia/include/core/SkPath.h" 57#include "third_party/skia/include/core/SkShader.h" 58 59#if defined(TOOLKIT_VIEWS) 60#include "app/os_exchange_data.h" 61#include "views/drag_utils.h" 62#endif 63 64#if defined(TOOLKIT_USES_GTK) 65#if defined(TOOLKIT_VIEWS) 66#include "app/drag_drop_types.h" 67#include "views/widget/widget_gtk.h" 68#elif defined(TOOLKIT_GTK) 69#include "chrome/browser/gtk/custom_drag.h" 70#endif // defined(TOOLKIT_GTK) 71#endif // defined(TOOLKIT_USES_GTK) 72 73#if defined(OS_WIN) 74#include "app/os_exchange_data_provider_win.h" 75#include "app/win_util.h" 76#include "app/win/drag_source.h" 77#include "base/win/scoped_comptr.h" 78#include "base/win_util.h" 79#include "chrome/browser/browser_list.h" 80#include "chrome/browser/views/frame/browser_view.h" 81#endif 82 83namespace download_util { 84 85// How many times to cycle the complete animation. This should be an odd number 86// so that the animation ends faded out. 87static const int kCompleteAnimationCycles = 5; 88 89// The maximum number of 'uniquified' files we will try to create. 90// This is used when the filename we're trying to download is already in use, 91// so we create a new unique filename by appending " (nnn)" before the 92// extension, where 1 <= nnn <= kMaxUniqueFiles. 93// Also used by code that cleans up said files. 94static const int kMaxUniqueFiles = 100; 95 96// Download temporary file creation -------------------------------------------- 97 98class DefaultDownloadDirectory { 99 public: 100 const FilePath& path() const { return path_; } 101 private: 102 DefaultDownloadDirectory() { 103 if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &path_)) { 104 NOTREACHED(); 105 } 106 if (DownloadPathIsDangerous(path_)) { 107 if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS_SAFE, &path_)) { 108 NOTREACHED(); 109 } 110 } 111 } 112 friend struct DefaultSingletonTraits<DefaultDownloadDirectory>; 113 FilePath path_; 114}; 115 116const FilePath& GetDefaultDownloadDirectory() { 117 return Singleton<DefaultDownloadDirectory>::get()->path(); 118} 119 120bool CreateTemporaryFileForDownload(FilePath* temp_file) { 121 if (file_util::CreateTemporaryFileInDir(GetDefaultDownloadDirectory(), 122 temp_file)) 123 return true; 124 return file_util::CreateTemporaryFile(temp_file); 125} 126 127bool DownloadPathIsDangerous(const FilePath& download_path) { 128 FilePath desktop_dir; 129 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_dir)) { 130 NOTREACHED(); 131 return false; 132 } 133 return (download_path == desktop_dir); 134} 135 136void GenerateExtension(const FilePath& file_name, 137 const std::string& mime_type, 138 FilePath::StringType* generated_extension) { 139 // We're worried about two things here: 140 // 141 // 1) Usability. If the site fails to provide a file extension, we want to 142 // guess a reasonable file extension based on the content type. 143 // 144 // 2) Shell integration. Some file extensions automatically integrate with 145 // the shell. We block these extensions to prevent a malicious web site 146 // from integrating with the user's shell. 147 148 // See if our file name already contains an extension. 149 FilePath::StringType extension = file_name.Extension(); 150 if (!extension.empty()) 151 extension.erase(extension.begin()); // Erase preceding '.'. 152 153#if defined(OS_WIN) 154 static const FilePath::CharType default_extension[] = 155 FILE_PATH_LITERAL("download"); 156 157 // Rename shell-integrated extensions. 158 if (win_util::IsShellIntegratedExtension(extension)) 159 extension.assign(default_extension); 160#endif 161 162 if (extension.empty()) { 163 // The GetPreferredExtensionForMimeType call will end up going to disk. Do 164 // this on another thread to avoid slowing the IO thread. 165 // http://crbug.com/61827 166 base::ThreadRestrictions::ScopedAllowIO allow_io; 167 net::GetPreferredExtensionForMimeType(mime_type, &extension); 168 } 169 170 generated_extension->swap(extension); 171} 172 173void GenerateFileNameFromInfo(DownloadCreateInfo* info, 174 FilePath* generated_name) { 175 GenerateFileName(GURL(info->url), 176 info->content_disposition, 177 info->referrer_charset, 178 info->mime_type, 179 generated_name); 180} 181 182void GenerateFileName(const GURL& url, 183 const std::string& content_disposition, 184 const std::string& referrer_charset, 185 const std::string& mime_type, 186 FilePath* generated_name) { 187 std::wstring default_name = 188 l10n_util::GetString(IDS_DEFAULT_DOWNLOAD_FILENAME); 189#if defined(OS_WIN) 190 FilePath default_file_path(default_name); 191#elif defined(OS_POSIX) 192 FilePath default_file_path(base::SysWideToNativeMB(default_name)); 193#endif 194 195 *generated_name = net::GetSuggestedFilename(GURL(url), 196 content_disposition, 197 referrer_charset, 198 default_file_path); 199 200 DCHECK(!generated_name->empty()); 201 202 GenerateSafeFileName(mime_type, generated_name); 203} 204 205void GenerateSafeFileName(const std::string& mime_type, FilePath* file_name) { 206 // Make sure we get the right file extension 207 FilePath::StringType extension; 208 GenerateExtension(*file_name, mime_type, &extension); 209 *file_name = file_name->ReplaceExtension(extension); 210 211#if defined(OS_WIN) 212 // Prepend "_" to the file name if it's a reserved name 213 FilePath::StringType leaf_name = file_name->BaseName().value(); 214 DCHECK(!leaf_name.empty()); 215 if (win_util::IsReservedName(leaf_name)) { 216 leaf_name = FilePath::StringType(FILE_PATH_LITERAL("_")) + leaf_name; 217 *file_name = file_name->DirName(); 218 if (file_name->value() == FilePath::kCurrentDirectory) { 219 *file_name = FilePath(leaf_name); 220 } else { 221 *file_name = file_name->Append(leaf_name); 222 } 223 } 224#endif 225} 226 227void OpenChromeExtension(Profile* profile, 228 DownloadManager* download_manager, 229 const DownloadItem& download_item) { 230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 231 DCHECK(download_item.is_extension_install()); 232 233 ExtensionsService* service = profile->GetExtensionsService(); 234 CHECK(service); 235 NotificationService* nservice = NotificationService::current(); 236 GURL nonconst_download_url = download_item.url(); 237 nservice->Notify(NotificationType::EXTENSION_READY_FOR_INSTALL, 238 Source<DownloadManager>(download_manager), 239 Details<GURL>(&nonconst_download_url)); 240 241 scoped_refptr<CrxInstaller> installer( 242 new CrxInstaller(service->install_directory(), 243 service, 244 new ExtensionInstallUI(profile))); 245 installer->set_delete_source(true); 246 247 if (UserScript::HasUserScriptFileExtension(download_item.url())) { 248 installer->InstallUserScript(download_item.full_path(), 249 download_item.url()); 250 return; 251 } 252 253 bool is_gallery_download = service->IsDownloadFromGallery( 254 download_item.url(), download_item.referrer_url()); 255 installer->set_original_mime_type(download_item.original_mime_type()); 256 installer->set_apps_require_extension_mime_type(true); 257 installer->set_allow_privilege_increase(true); 258 installer->set_original_url(download_item.url()); 259 installer->set_is_gallery_install(is_gallery_download); 260 installer->InstallCrx(download_item.full_path()); 261 installer->set_allow_silent_install(is_gallery_download); 262} 263 264// Download progress painting -------------------------------------------------- 265 266// Common bitmaps used for download progress animations. We load them once the 267// first time we do a progress paint, then reuse them as they are always the 268// same. 269SkBitmap* g_foreground_16 = NULL; 270SkBitmap* g_background_16 = NULL; 271SkBitmap* g_foreground_32 = NULL; 272SkBitmap* g_background_32 = NULL; 273 274void PaintDownloadProgress(gfx::Canvas* canvas, 275#if defined(TOOLKIT_VIEWS) 276 views::View* containing_view, 277#endif 278 int origin_x, 279 int origin_y, 280 int start_angle, 281 int percent_done, 282 PaintDownloadProgressSize size) { 283 // Load up our common bitmaps 284 if (!g_background_16) { 285 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 286 g_foreground_16 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_16); 287 g_background_16 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_BACKGROUND_16); 288 g_foreground_32 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_32); 289 g_background_32 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_BACKGROUND_32); 290 } 291 292 SkBitmap* background = (size == BIG) ? g_background_32 : g_background_16; 293 SkBitmap* foreground = (size == BIG) ? g_foreground_32 : g_foreground_16; 294 295 const int kProgressIconSize = (size == BIG) ? kBigProgressIconSize : 296 kSmallProgressIconSize; 297 298 // We start by storing the bounds of the background and foreground bitmaps 299 // so that it is easy to mirror the bounds if the UI layout is RTL. 300 gfx::Rect background_bounds(origin_x, origin_y, 301 background->width(), background->height()); 302 gfx::Rect foreground_bounds(origin_x, origin_y, 303 foreground->width(), foreground->height()); 304 305#if defined(TOOLKIT_VIEWS) 306 // Mirror the positions if necessary. 307 int mirrored_x = containing_view->MirroredLeftPointForRect(background_bounds); 308 background_bounds.set_x(mirrored_x); 309 mirrored_x = containing_view->MirroredLeftPointForRect(foreground_bounds); 310 foreground_bounds.set_x(mirrored_x); 311#endif 312 313 // Draw the background progress image. 314 SkPaint background_paint; 315 canvas->DrawBitmapInt(*background, 316 background_bounds.x(), 317 background_bounds.y(), 318 background_paint); 319 320 // Layer the foreground progress image in an arc proportional to the download 321 // progress. The arc grows clockwise, starting in the midnight position, as 322 // the download progresses. However, if the download does not have known total 323 // size (the server didn't give us one), then we just spin an arc around until 324 // we're done. 325 float sweep_angle = 0.0; 326 float start_pos = static_cast<float>(kStartAngleDegrees); 327 if (percent_done < 0) { 328 sweep_angle = kUnknownAngleDegrees; 329 start_pos = static_cast<float>(start_angle); 330 } else if (percent_done > 0) { 331 sweep_angle = static_cast<float>(kMaxDegrees / 100.0 * percent_done); 332 } 333 334 // Set up an arc clipping region for the foreground image. Don't bother using 335 // a clipping region if it would round to 360 (really 0) degrees, since that 336 // would eliminate the foreground completely and be quite confusing (it would 337 // look like 0% complete when it should be almost 100%). 338 SkPaint foreground_paint; 339 if (sweep_angle < static_cast<float>(kMaxDegrees - 1)) { 340 SkRect oval; 341 oval.set(SkIntToScalar(foreground_bounds.x()), 342 SkIntToScalar(foreground_bounds.y()), 343 SkIntToScalar(foreground_bounds.x() + kProgressIconSize), 344 SkIntToScalar(foreground_bounds.y() + kProgressIconSize)); 345 SkPath path; 346 path.arcTo(oval, 347 SkFloatToScalar(start_pos), 348 SkFloatToScalar(sweep_angle), false); 349 path.lineTo(SkIntToScalar(foreground_bounds.x() + kProgressIconSize / 2), 350 SkIntToScalar(foreground_bounds.y() + kProgressIconSize / 2)); 351 352 SkShader* shader = 353 SkShader::CreateBitmapShader(*foreground, 354 SkShader::kClamp_TileMode, 355 SkShader::kClamp_TileMode); 356 SkMatrix shader_scale; 357 shader_scale.setTranslate(SkIntToScalar(foreground_bounds.x()), 358 SkIntToScalar(foreground_bounds.y())); 359 shader->setLocalMatrix(shader_scale); 360 foreground_paint.setShader(shader); 361 foreground_paint.setAntiAlias(true); 362 shader->unref(); 363 canvas->AsCanvasSkia()->drawPath(path, foreground_paint); 364 return; 365 } 366 367 canvas->DrawBitmapInt(*foreground, 368 foreground_bounds.x(), 369 foreground_bounds.y(), 370 foreground_paint); 371} 372 373void PaintDownloadComplete(gfx::Canvas* canvas, 374#if defined(TOOLKIT_VIEWS) 375 views::View* containing_view, 376#endif 377 int origin_x, 378 int origin_y, 379 double animation_progress, 380 PaintDownloadProgressSize size) { 381 // Load up our common bitmaps. 382 if (!g_foreground_16) { 383 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 384 g_foreground_16 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_16); 385 g_foreground_32 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_32); 386 } 387 388 SkBitmap* complete = (size == BIG) ? g_foreground_32 : g_foreground_16; 389 390 gfx::Rect complete_bounds(origin_x, origin_y, 391 complete->width(), complete->height()); 392#if defined(TOOLKIT_VIEWS) 393 // Mirror the positions if necessary. 394 complete_bounds.set_x( 395 containing_view->MirroredLeftPointForRect(complete_bounds)); 396#endif 397 398 // Start at full opacity, then loop back and forth five times before ending 399 // at zero opacity. 400 static const double PI = 3.141592653589793; 401 double opacity = sin(animation_progress * PI * kCompleteAnimationCycles + 402 PI/2) / 2 + 0.5; 403 404 canvas->SaveLayerAlpha(static_cast<int>(255.0 * opacity), complete_bounds); 405 canvas->AsCanvasSkia()->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode); 406 canvas->DrawBitmapInt(*complete, complete_bounds.x(), complete_bounds.y()); 407 canvas->Restore(); 408} 409 410// Load a language dependent height so that the dangerous download confirmation 411// message doesn't overlap with the download link label. 412int GetBigProgressIconSize() { 413 static int big_progress_icon_size = 0; 414 if (big_progress_icon_size == 0) { 415 string16 locale_size_str = 416 l10n_util::GetStringUTF16(IDS_DOWNLOAD_BIG_PROGRESS_SIZE); 417 bool rc = base::StringToInt(locale_size_str, &big_progress_icon_size); 418 if (!rc || big_progress_icon_size < kBigProgressIconSize) { 419 NOTREACHED(); 420 big_progress_icon_size = kBigProgressIconSize; 421 } 422 } 423 424 return big_progress_icon_size; 425} 426 427int GetBigProgressIconOffset() { 428 return (GetBigProgressIconSize() - kBigIconSize) / 2; 429} 430 431#if defined(TOOLKIT_VIEWS) 432// Download dragging 433void DragDownload(const DownloadItem* download, 434 SkBitmap* icon, 435 gfx::NativeView view) { 436 DCHECK(download); 437 438 // Set up our OLE machinery 439 OSExchangeData data; 440 441 if (icon) { 442 drag_utils::CreateDragImageForFile( 443 download->GetFileNameToReportUser().value(), icon, &data); 444 } 445 446 const FilePath full_path = download->full_path(); 447 data.SetFilename(full_path.ToWStringHack()); 448 449 std::string mime_type = download->mime_type(); 450 if (mime_type.empty()) 451 net::GetMimeTypeFromFile(full_path, &mime_type); 452 453 // Add URL so that we can load supported files when dragged to TabContents. 454 if (net::IsSupportedMimeType(mime_type)) { 455 data.SetURL(GURL(WideToUTF8(full_path.ToWStringHack())), 456 download->GetFileNameToReportUser().ToWStringHack()); 457 } 458 459#if defined(OS_WIN) 460 scoped_refptr<app::win::DragSource> drag_source(new app::win::DragSource); 461 462 // Run the drag and drop loop 463 DWORD effects; 464 DoDragDrop(OSExchangeDataProviderWin::GetIDataObject(data), drag_source.get(), 465 DROPEFFECT_COPY | DROPEFFECT_LINK, &effects); 466#elif defined(TOOLKIT_USES_GTK) 467 GtkWidget* root = gtk_widget_get_toplevel(view); 468 if (!root) 469 return; 470 views::WidgetGtk* widget = views::WidgetGtk::GetViewForNative(root); 471 if (!widget) 472 return; 473 474 widget->DoDrag(data, DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK); 475#endif // OS_WIN 476} 477#elif defined(USE_X11) 478void DragDownload(const DownloadItem* download, 479 SkBitmap* icon, 480 gfx::NativeView view) { 481 DownloadItemDrag::BeginDrag(download, icon); 482} 483#endif // USE_X11 484 485DictionaryValue* CreateDownloadItemValue(DownloadItem* download, int id) { 486 DictionaryValue* file_value = new DictionaryValue(); 487 488 file_value->SetInteger("started", 489 static_cast<int>(download->start_time().ToTimeT())); 490 file_value->SetString("since_string", 491 TimeFormat::RelativeDate(download->start_time(), NULL)); 492 file_value->SetString("date_string", 493 WideToUTF16Hack(base::TimeFormatShortDate(download->start_time()))); 494 file_value->SetInteger("id", id); 495 file_value->SetString("file_path", 496 WideToUTF16Hack(download->GetTargetFilePath().ToWStringHack())); 497 // Keep file names as LTR. 498 string16 file_name = WideToUTF16Hack( 499 download->GetFileNameToReportUser().ToWStringHack()); 500 file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name); 501 file_value->SetString("file_name", file_name); 502 file_value->SetString("url", download->url().spec()); 503 file_value->SetBoolean("otr", download->is_otr()); 504 505 if (download->state() == DownloadItem::IN_PROGRESS) { 506 if (download->safety_state() == DownloadItem::DANGEROUS) { 507 file_value->SetString("state", "DANGEROUS"); 508 } else if (download->is_paused()) { 509 file_value->SetString("state", "PAUSED"); 510 } else { 511 file_value->SetString("state", "IN_PROGRESS"); 512 } 513 514 file_value->SetString("progress_status_text", 515 WideToUTF16Hack(GetProgressStatusText(download))); 516 517 file_value->SetInteger("percent", 518 static_cast<int>(download->PercentComplete())); 519 file_value->SetInteger("received", 520 static_cast<int>(download->received_bytes())); 521 } else if (download->state() == DownloadItem::CANCELLED) { 522 file_value->SetString("state", "CANCELLED"); 523 } else if (download->state() == DownloadItem::COMPLETE) { 524 if (download->safety_state() == DownloadItem::DANGEROUS) { 525 file_value->SetString("state", "DANGEROUS"); 526 } else { 527 file_value->SetString("state", "COMPLETE"); 528 } 529 } 530 531 file_value->SetInteger("total", 532 static_cast<int>(download->total_bytes())); 533 534 return file_value; 535} 536 537std::wstring GetProgressStatusText(DownloadItem* download) { 538 int64 total = download->total_bytes(); 539 int64 size = download->received_bytes(); 540 DataUnits amount_units = GetByteDisplayUnits(size); 541 std::wstring received_size = UTF16ToWideHack(FormatBytes(size, amount_units, 542 true)); 543 std::wstring amount = received_size; 544 545 // Adjust both strings for the locale direction since we don't yet know which 546 // string we'll end up using for constructing the final progress string. 547 std::wstring amount_localized; 548 if (base::i18n::AdjustStringForLocaleDirection(amount, &amount_localized)) { 549 amount.assign(amount_localized); 550 received_size.assign(amount_localized); 551 } 552 553 if (total) { 554 amount_units = GetByteDisplayUnits(total); 555 std::wstring total_text = 556 UTF16ToWideHack(FormatBytes(total, amount_units, true)); 557 std::wstring total_text_localized; 558 if (base::i18n::AdjustStringForLocaleDirection(total_text, 559 &total_text_localized)) 560 total_text.assign(total_text_localized); 561 562 amount = l10n_util::GetStringF(IDS_DOWNLOAD_TAB_PROGRESS_SIZE, 563 received_size, 564 total_text); 565 } else { 566 amount.assign(received_size); 567 } 568 int64 current_speed = download->CurrentSpeed(); 569 amount_units = GetByteDisplayUnits(current_speed); 570 std::wstring speed_text = UTF16ToWideHack(FormatSpeed(current_speed, 571 amount_units, true)); 572 std::wstring speed_text_localized; 573 if (base::i18n::AdjustStringForLocaleDirection(speed_text, 574 &speed_text_localized)) 575 speed_text.assign(speed_text_localized); 576 577 base::TimeDelta remaining; 578 string16 time_remaining; 579 if (download->is_paused()) 580 time_remaining = l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED); 581 else if (download->TimeRemaining(&remaining)) 582 time_remaining = TimeFormat::TimeRemaining(remaining); 583 584 if (time_remaining.empty()) { 585 return l10n_util::GetStringF(IDS_DOWNLOAD_TAB_PROGRESS_STATUS_TIME_UNKNOWN, 586 speed_text, amount); 587 } 588 return l10n_util::GetStringF(IDS_DOWNLOAD_TAB_PROGRESS_STATUS, speed_text, 589 amount, UTF16ToWideHack(time_remaining)); 590} 591 592#if !defined(OS_MACOSX) 593void UpdateAppIconDownloadProgress(int download_count, 594 bool progress_known, 595 float progress) { 596#if defined(OS_WIN) 597 // Taskbar progress bar is only supported on Win7. 598 if (base::win::GetVersion() < base::win::VERSION_WIN7) 599 return; 600 601 base::win::ScopedComPtr<ITaskbarList3> taskbar; 602 HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL, 603 CLSCTX_INPROC_SERVER); 604 if (FAILED(result)) { 605 VLOG(1) << "Failed creating a TaskbarList object: " << result; 606 return; 607 } 608 609 result = taskbar->HrInit(); 610 if (FAILED(result)) { 611 LOG(ERROR) << "Failed initializing an ITaskbarList3 interface."; 612 return; 613 } 614 615 // Iterate through all the browser windows, and draw the progress bar. 616 for (BrowserList::const_iterator browser_iterator = BrowserList::begin(); 617 browser_iterator != BrowserList::end(); browser_iterator++) { 618 HWND frame = (*browser_iterator)->window()->GetNativeHandle(); 619 if (download_count == 0 || progress == 1.0f) 620 taskbar->SetProgressState(frame, TBPF_NOPROGRESS); 621 else if (!progress_known) 622 taskbar->SetProgressState(frame, TBPF_INDETERMINATE); 623 else 624 taskbar->SetProgressValue(frame, (int)(progress * 100), 100); 625 } 626#endif 627} 628#endif 629 630// Appends the passed the number between parenthesis the path before the 631// extension. 632void AppendNumberToPath(FilePath* path, int number) { 633 *path = path->InsertBeforeExtensionASCII(StringPrintf(" (%d)", number)); 634} 635 636// Attempts to find a number that can be appended to that path to make it 637// unique. If |path| does not exist, 0 is returned. If it fails to find such 638// a number, -1 is returned. 639int GetUniquePathNumber(const FilePath& path) { 640 if (!file_util::PathExists(path)) 641 return 0; 642 643 FilePath new_path; 644 for (int count = 1; count <= kMaxUniqueFiles; ++count) { 645 new_path = FilePath(path); 646 AppendNumberToPath(&new_path, count); 647 648 if (!file_util::PathExists(new_path)) 649 return count; 650 } 651 652 return -1; 653} 654 655void DownloadUrl( 656 const GURL& url, 657 const GURL& referrer, 658 const std::string& referrer_charset, 659 const DownloadSaveInfo& save_info, 660 ResourceDispatcherHost* rdh, 661 int render_process_host_id, 662 int render_view_id, 663 URLRequestContextGetter* request_context_getter) { 664 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 665 666 URLRequestContext* context = request_context_getter->GetURLRequestContext(); 667 context->set_referrer_charset(referrer_charset); 668 669 rdh->BeginDownload(url, 670 referrer, 671 save_info, 672 true, // Show "Save as" UI. 673 render_process_host_id, 674 render_view_id, 675 context); 676} 677 678void CancelDownloadRequest(ResourceDispatcherHost* rdh, 679 int render_process_id, 680 int request_id) { 681 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 682 rdh->CancelRequest(render_process_id, request_id, false); 683} 684 685int GetUniquePathNumberWithCrDownload(const FilePath& path) { 686 if (!file_util::PathExists(path) && 687 !file_util::PathExists(GetCrDownloadPath(path))) 688 return 0; 689 690 FilePath new_path; 691 for (int count = 1; count <= kMaxUniqueFiles; ++count) { 692 new_path = FilePath(path); 693 AppendNumberToPath(&new_path, count); 694 695 if (!file_util::PathExists(new_path) && 696 !file_util::PathExists(GetCrDownloadPath(new_path))) 697 return count; 698 } 699 700 return -1; 701} 702 703namespace { 704 705// NOTE: If index is 0, deletes files that do not have the " (nnn)" appended. 706void DeleteUniqueDownloadFile(const FilePath& path, int index) { 707 FilePath new_path(path); 708 if (index > 0) 709 AppendNumberToPath(&new_path, index); 710 file_util::Delete(new_path, false); 711} 712 713} 714 715void EraseUniqueDownloadFiles(const FilePath& path) { 716 FilePath cr_path = GetCrDownloadPath(path); 717 718 for (int index = 0; index <= kMaxUniqueFiles; ++index) { 719 DeleteUniqueDownloadFile(path, index); 720 DeleteUniqueDownloadFile(cr_path, index); 721 } 722} 723 724FilePath GetCrDownloadPath(const FilePath& suggested_path) { 725 FilePath::StringType file_name; 726 base::SStringPrintf( 727 &file_name, 728 PRFilePathLiteral FILE_PATH_LITERAL(".crdownload"), 729 suggested_path.value().c_str()); 730 return FilePath(file_name); 731} 732 733// TODO(erikkay,phajdan.jr): This is apparently not being exercised in tests. 734bool IsDangerous(DownloadCreateInfo* info, Profile* profile) { 735 // Downloads can be marked as dangerous for two reasons: 736 // a) They have a dangerous-looking filename 737 // b) They are an extension that is not from the gallery 738 if (IsExecutableFile(info->suggested_path.BaseName())) { 739 return true; 740 } else if (info->is_extension_install) { 741 ExtensionsService* service = profile->GetExtensionsService(); 742 if (!service || 743 !service->IsDownloadFromGallery(info->url, info->referrer_url)) { 744 return true; 745 } 746 } 747 return false; 748} 749 750} // namespace download_util 751