• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
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 "base/file_util.h"
15 #include "base/i18n/rtl.h"
16 #include "base/i18n/time_formatting.h"
17 #include "base/lazy_instance.h"
18 #include "base/metrics/histogram.h"
19 #include "base/path_service.h"
20 #include "base/string16.h"
21 #include "base/string_number_conversions.h"
22 #include "base/stringprintf.h"
23 #include "base/sys_string_conversions.h"
24 #include "base/threading/thread_restrictions.h"
25 #include "base/utf_string_conversions.h"
26 #include "base/value_conversions.h"
27 #include "base/values.h"
28 #include "base/win/windows_version.h"
29 #include "chrome/browser/download/download_extensions.h"
30 #include "chrome/browser/download/download_item.h"
31 #include "chrome/browser/download/download_item_model.h"
32 #include "chrome/browser/download/download_manager.h"
33 #include "chrome/browser/download/download_types.h"
34 #include "chrome/browser/extensions/crx_installer.h"
35 #include "chrome/browser/extensions/extension_install_ui.h"
36 #include "chrome/browser/extensions/extension_service.h"
37 #include "chrome/browser/history/download_create_info.h"
38 #include "chrome/browser/net/chrome_url_request_context.h"
39 #include "chrome/browser/profiles/profile.h"
40 #include "chrome/browser/ui/browser.h"
41 #include "chrome/common/chrome_paths.h"
42 #include "chrome/common/time_format.h"
43 #include "content/browser/browser_thread.h"
44 #include "content/browser/renderer_host/render_view_host.h"
45 #include "content/browser/renderer_host/resource_dispatcher_host.h"
46 #include "content/browser/tab_contents/tab_contents.h"
47 #include "content/common/notification_service.h"
48 #include "grit/generated_resources.h"
49 #include "grit/locale_settings.h"
50 #include "grit/theme_resources.h"
51 #include "net/base/mime_util.h"
52 #include "net/base/net_util.h"
53 #include "skia/ext/image_operations.h"
54 #include "third_party/skia/include/core/SkPath.h"
55 #include "third_party/skia/include/core/SkShader.h"
56 #include "ui/base/l10n/l10n_util.h"
57 #include "ui/base/resource/resource_bundle.h"
58 #include "ui/gfx/canvas_skia.h"
59 #include "ui/gfx/image.h"
60 #include "ui/gfx/rect.h"
61 
62 #if defined(TOOLKIT_VIEWS)
63 #include "ui/base/dragdrop/os_exchange_data.h"
64 #include "views/drag_utils.h"
65 #endif
66 
67 #if defined(TOOLKIT_USES_GTK)
68 #if defined(TOOLKIT_VIEWS)
69 #include "ui/base/dragdrop/drag_drop_types.h"
70 #include "views/widget/widget_gtk.h"
71 #elif defined(TOOLKIT_GTK)
72 #include "chrome/browser/ui/gtk/custom_drag.h"
73 #endif  // defined(TOOLKIT_GTK)
74 #endif  // defined(TOOLKIT_USES_GTK)
75 
76 #if defined(OS_WIN)
77 #include "base/win/scoped_comptr.h"
78 #include "chrome/browser/ui/browser_list.h"
79 #include "chrome/browser/ui/views/frame/browser_view.h"
80 #include "ui/base/dragdrop/drag_source.h"
81 #include "ui/base/dragdrop/os_exchange_data_provider_win.h"
82 #endif
83 
84 // TODO(phajdan.jr): Find some standard location for this, maintaining
85 // the same value on all platforms.
86 static const double PI = 3.141592653589793;
87 
88 namespace download_util {
89 
90 // How many times to cycle the complete animation. This should be an odd number
91 // so that the animation ends faded out.
92 static const int kCompleteAnimationCycles = 5;
93 
94 // The maximum number of 'uniquified' files we will try to create.
95 // This is used when the filename we're trying to download is already in use,
96 // so we create a new unique filename by appending " (nnn)" before the
97 // extension, where 1 <= nnn <= kMaxUniqueFiles.
98 // Also used by code that cleans up said files.
99 static const int kMaxUniqueFiles = 100;
100 
101 namespace {
102 
103 #if defined(OS_WIN)
104 // Returns whether the specified extension is automatically integrated into the
105 // windows shell.
IsShellIntegratedExtension(const string16 & extension)106 bool IsShellIntegratedExtension(const string16& extension) {
107   string16 extension_lower = StringToLowerASCII(extension);
108 
109   static const wchar_t* const integrated_extensions[] = {
110     // See <http://msdn.microsoft.com/en-us/library/ms811694.aspx>.
111     L"local",
112     // Right-clicking on shortcuts can be magical.
113     L"lnk",
114   };
115 
116   for (int i = 0; i < arraysize(integrated_extensions); ++i) {
117     if (extension_lower == integrated_extensions[i])
118       return true;
119   }
120 
121   // See <http://www.juniper.net/security/auto/vulnerabilities/vuln2612.html>.
122   // That vulnerability report is not exactly on point, but files become magical
123   // if their end in a CLSID.  Here we block extensions that look like CLSIDs.
124   if (!extension_lower.empty() && extension_lower[0] == L'{' &&
125       extension_lower[extension_lower.length() - 1] == L'}')
126     return true;
127 
128   return false;
129 }
130 
131 // Returns whether the specified file name is a reserved name on windows.
132 // This includes names like "com2.zip" (which correspond to devices) and
133 // desktop.ini and thumbs.db which have special meaning to the windows shell.
IsReservedName(const string16 & filename)134 bool IsReservedName(const string16& filename) {
135   // This list is taken from the MSDN article "Naming a file"
136   // http://msdn2.microsoft.com/en-us/library/aa365247(VS.85).aspx
137   // I also added clock$ because GetSaveFileName seems to consider it as a
138   // reserved name too.
139   static const wchar_t* const known_devices[] = {
140     L"con", L"prn", L"aux", L"nul", L"com1", L"com2", L"com3", L"com4", L"com5",
141     L"com6", L"com7", L"com8", L"com9", L"lpt1", L"lpt2", L"lpt3", L"lpt4",
142     L"lpt5", L"lpt6", L"lpt7", L"lpt8", L"lpt9", L"clock$"
143   };
144   string16 filename_lower = StringToLowerASCII(filename);
145 
146   for (int i = 0; i < arraysize(known_devices); ++i) {
147     // Exact match.
148     if (filename_lower == known_devices[i])
149       return true;
150     // Starts with "DEVICE.".
151     if (filename_lower.find(string16(known_devices[i]) + L".") == 0)
152       return true;
153   }
154 
155   static const wchar_t* const magic_names[] = {
156     // These file names are used by the "Customize folder" feature of the shell.
157     L"desktop.ini",
158     L"thumbs.db",
159   };
160 
161   for (int i = 0; i < arraysize(magic_names); ++i) {
162     if (filename_lower == magic_names[i])
163       return true;
164   }
165 
166   return false;
167 }
168 #endif  // OS_WIN
169 
170 }  // namespace
171 
172 // Download temporary file creation --------------------------------------------
173 
174 class DefaultDownloadDirectory {
175  public:
path() const176   const FilePath& path() const { return path_; }
177  private:
DefaultDownloadDirectory()178   DefaultDownloadDirectory() {
179     if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &path_)) {
180       NOTREACHED();
181     }
182     if (DownloadPathIsDangerous(path_)) {
183       if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS_SAFE, &path_)) {
184         NOTREACHED();
185       }
186     }
187   }
188   friend struct base::DefaultLazyInstanceTraits<DefaultDownloadDirectory>;
189   FilePath path_;
190 };
191 
192 static base::LazyInstance<DefaultDownloadDirectory>
193     g_default_download_directory(base::LINKER_INITIALIZED);
194 
GetDefaultDownloadDirectory()195 const FilePath& GetDefaultDownloadDirectory() {
196   return g_default_download_directory.Get().path();
197 }
198 
CreateTemporaryFileForDownload(FilePath * temp_file)199 bool CreateTemporaryFileForDownload(FilePath* temp_file) {
200   if (file_util::CreateTemporaryFileInDir(GetDefaultDownloadDirectory(),
201                                           temp_file))
202     return true;
203   return file_util::CreateTemporaryFile(temp_file);
204 }
205 
DownloadPathIsDangerous(const FilePath & download_path)206 bool DownloadPathIsDangerous(const FilePath& download_path) {
207   FilePath desktop_dir;
208   if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_dir)) {
209     NOTREACHED();
210     return false;
211   }
212   return (download_path == desktop_dir);
213 }
214 
GenerateExtension(const FilePath & file_name,const std::string & mime_type,FilePath::StringType * generated_extension)215 void GenerateExtension(const FilePath& file_name,
216                        const std::string& mime_type,
217                        FilePath::StringType* generated_extension) {
218   // We're worried about two things here:
219   //
220   // 1) Usability.  If the site fails to provide a file extension, we want to
221   //    guess a reasonable file extension based on the content type.
222   //
223   // 2) Shell integration.  Some file extensions automatically integrate with
224   //    the shell.  We block these extensions to prevent a malicious web site
225   //    from integrating with the user's shell.
226 
227   // See if our file name already contains an extension.
228   FilePath::StringType extension = file_name.Extension();
229   if (!extension.empty())
230     extension.erase(extension.begin());  // Erase preceding '.'.
231 
232 #if defined(OS_WIN)
233   static const FilePath::CharType default_extension[] =
234       FILE_PATH_LITERAL("download");
235 
236   // Rename shell-integrated extensions.
237   if (IsShellIntegratedExtension(extension))
238     extension.assign(default_extension);
239 #endif
240 
241   if (extension.empty()) {
242     // The GetPreferredExtensionForMimeType call will end up going to disk.  Do
243     // this on another thread to avoid slowing the IO thread.
244     // http://crbug.com/61827
245     base::ThreadRestrictions::ScopedAllowIO allow_io;
246     net::GetPreferredExtensionForMimeType(mime_type, &extension);
247   }
248 
249   generated_extension->swap(extension);
250 }
251 
GenerateFileNameFromInfo(DownloadCreateInfo * info,FilePath * generated_name)252 void GenerateFileNameFromInfo(DownloadCreateInfo* info,
253                               FilePath* generated_name) {
254   GenerateFileName(GURL(info->url()),
255                    info->content_disposition,
256                    info->referrer_charset,
257                    info->mime_type,
258                    generated_name);
259 }
260 
GenerateFileName(const GURL & url,const std::string & content_disposition,const std::string & referrer_charset,const std::string & mime_type,FilePath * generated_name)261 void GenerateFileName(const GURL& url,
262                       const std::string& content_disposition,
263                       const std::string& referrer_charset,
264                       const std::string& mime_type,
265                       FilePath* generated_name) {
266   string16 default_file_name(
267       l10n_util::GetStringUTF16(IDS_DEFAULT_DOWNLOAD_FILENAME));
268 
269   string16 new_name = net::GetSuggestedFilename(GURL(url),
270                                                 content_disposition,
271                                                 referrer_charset,
272                                                 default_file_name);
273 
274   // TODO(evan): this code is totally wrong -- we should just generate
275   // Unicode filenames and do all this encoding switching at the end.
276   // However, I'm just shuffling wrong code around, at least not adding
277   // to it.
278 #if defined(OS_WIN)
279   *generated_name = FilePath(new_name);
280 #else
281   *generated_name = FilePath(
282       base::SysWideToNativeMB(UTF16ToWide(new_name)));
283 #endif
284 
285   DCHECK(!generated_name->empty());
286 
287   GenerateSafeFileName(mime_type, generated_name);
288 }
289 
GenerateSafeFileName(const std::string & mime_type,FilePath * file_name)290 void GenerateSafeFileName(const std::string& mime_type, FilePath* file_name) {
291   // Make sure we get the right file extension
292   FilePath::StringType extension;
293   GenerateExtension(*file_name, mime_type, &extension);
294   *file_name = file_name->ReplaceExtension(extension);
295 
296 #if defined(OS_WIN)
297   // Prepend "_" to the file name if it's a reserved name
298   FilePath::StringType leaf_name = file_name->BaseName().value();
299   DCHECK(!leaf_name.empty());
300   if (IsReservedName(leaf_name)) {
301     leaf_name = FilePath::StringType(FILE_PATH_LITERAL("_")) + leaf_name;
302     *file_name = file_name->DirName();
303     if (file_name->value() == FilePath::kCurrentDirectory) {
304       *file_name = FilePath(leaf_name);
305     } else {
306       *file_name = file_name->Append(leaf_name);
307     }
308   }
309 #endif
310 }
311 
OpenChromeExtension(Profile * profile,DownloadManager * download_manager,const DownloadItem & download_item)312 void OpenChromeExtension(Profile* profile,
313                          DownloadManager* download_manager,
314                          const DownloadItem& download_item) {
315   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
316   DCHECK(download_item.is_extension_install());
317 
318   ExtensionService* service = profile->GetExtensionService();
319   CHECK(service);
320   NotificationService* nservice = NotificationService::current();
321   GURL nonconst_download_url = download_item.url();
322   nservice->Notify(NotificationType::EXTENSION_READY_FOR_INSTALL,
323                    Source<DownloadManager>(download_manager),
324                    Details<GURL>(&nonconst_download_url));
325 
326   scoped_refptr<CrxInstaller> installer(
327       new CrxInstaller(service, new ExtensionInstallUI(profile)));
328   installer->set_delete_source(true);
329 
330   if (UserScript::IsURLUserScript(download_item.url(),
331                                   download_item.mime_type())) {
332     installer->InstallUserScript(download_item.full_path(),
333                                  download_item.url());
334     return;
335   }
336 
337   bool is_gallery_download = service->IsDownloadFromGallery(
338       download_item.url(), download_item.referrer_url());
339   installer->set_original_mime_type(download_item.original_mime_type());
340   installer->set_apps_require_extension_mime_type(true);
341   installer->set_original_url(download_item.url());
342   installer->set_is_gallery_install(is_gallery_download);
343   installer->InstallCrx(download_item.full_path());
344   installer->set_allow_silent_install(is_gallery_download);
345 }
346 
RecordDownloadCount(DownloadCountTypes type)347 void RecordDownloadCount(DownloadCountTypes type) {
348   UMA_HISTOGRAM_ENUMERATION(
349       "Download.Counts", type, DOWNLOAD_COUNT_TYPES_LAST_ENTRY);
350 }
351 
352 // Download progress painting --------------------------------------------------
353 
354 // Common bitmaps used for download progress animations. We load them once the
355 // first time we do a progress paint, then reuse them as they are always the
356 // same.
357 SkBitmap* g_foreground_16 = NULL;
358 SkBitmap* g_background_16 = NULL;
359 SkBitmap* g_foreground_32 = NULL;
360 SkBitmap* g_background_32 = NULL;
361 
PaintDownloadProgress(gfx::Canvas * canvas,views::View * containing_view,int origin_x,int origin_y,int start_angle,int percent_done,PaintDownloadProgressSize size)362 void PaintDownloadProgress(gfx::Canvas* canvas,
363 #if defined(TOOLKIT_VIEWS)
364                            views::View* containing_view,
365 #endif
366                            int origin_x,
367                            int origin_y,
368                            int start_angle,
369                            int percent_done,
370                            PaintDownloadProgressSize size) {
371   // Load up our common bitmaps
372   if (!g_background_16) {
373     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
374     g_foreground_16 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_16);
375     g_background_16 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_BACKGROUND_16);
376     g_foreground_32 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_32);
377     g_background_32 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_BACKGROUND_32);
378   }
379 
380   SkBitmap* background = (size == BIG) ? g_background_32 : g_background_16;
381   SkBitmap* foreground = (size == BIG) ? g_foreground_32 : g_foreground_16;
382 
383   const int kProgressIconSize = (size == BIG) ? kBigProgressIconSize :
384                                                 kSmallProgressIconSize;
385 
386   // We start by storing the bounds of the background and foreground bitmaps
387   // so that it is easy to mirror the bounds if the UI layout is RTL.
388   gfx::Rect background_bounds(origin_x, origin_y,
389                               background->width(), background->height());
390   gfx::Rect foreground_bounds(origin_x, origin_y,
391                               foreground->width(), foreground->height());
392 
393 #if defined(TOOLKIT_VIEWS)
394   // Mirror the positions if necessary.
395   int mirrored_x = containing_view->GetMirroredXForRect(background_bounds);
396   background_bounds.set_x(mirrored_x);
397   mirrored_x = containing_view->GetMirroredXForRect(foreground_bounds);
398   foreground_bounds.set_x(mirrored_x);
399 #endif
400 
401   // Draw the background progress image.
402   SkPaint background_paint;
403   canvas->DrawBitmapInt(*background,
404                         background_bounds.x(),
405                         background_bounds.y(),
406                         background_paint);
407 
408   // Layer the foreground progress image in an arc proportional to the download
409   // progress. The arc grows clockwise, starting in the midnight position, as
410   // the download progresses. However, if the download does not have known total
411   // size (the server didn't give us one), then we just spin an arc around until
412   // we're done.
413   float sweep_angle = 0.0;
414   float start_pos = static_cast<float>(kStartAngleDegrees);
415   if (percent_done < 0) {
416     sweep_angle = kUnknownAngleDegrees;
417     start_pos = static_cast<float>(start_angle);
418   } else if (percent_done > 0) {
419     sweep_angle = static_cast<float>(kMaxDegrees / 100.0 * percent_done);
420   }
421 
422   // Set up an arc clipping region for the foreground image. Don't bother using
423   // a clipping region if it would round to 360 (really 0) degrees, since that
424   // would eliminate the foreground completely and be quite confusing (it would
425   // look like 0% complete when it should be almost 100%).
426   SkPaint foreground_paint;
427   if (sweep_angle < static_cast<float>(kMaxDegrees - 1)) {
428     SkRect oval;
429     oval.set(SkIntToScalar(foreground_bounds.x()),
430              SkIntToScalar(foreground_bounds.y()),
431              SkIntToScalar(foreground_bounds.x() + kProgressIconSize),
432              SkIntToScalar(foreground_bounds.y() + kProgressIconSize));
433     SkPath path;
434     path.arcTo(oval,
435                SkFloatToScalar(start_pos),
436                SkFloatToScalar(sweep_angle), false);
437     path.lineTo(SkIntToScalar(foreground_bounds.x() + kProgressIconSize / 2),
438                 SkIntToScalar(foreground_bounds.y() + kProgressIconSize / 2));
439 
440     SkShader* shader =
441         SkShader::CreateBitmapShader(*foreground,
442                                      SkShader::kClamp_TileMode,
443                                      SkShader::kClamp_TileMode);
444     SkMatrix shader_scale;
445     shader_scale.setTranslate(SkIntToScalar(foreground_bounds.x()),
446                               SkIntToScalar(foreground_bounds.y()));
447     shader->setLocalMatrix(shader_scale);
448     foreground_paint.setShader(shader);
449     foreground_paint.setAntiAlias(true);
450     shader->unref();
451     canvas->AsCanvasSkia()->drawPath(path, foreground_paint);
452     return;
453   }
454 
455   canvas->DrawBitmapInt(*foreground,
456                         foreground_bounds.x(),
457                         foreground_bounds.y(),
458                         foreground_paint);
459 }
460 
PaintDownloadComplete(gfx::Canvas * canvas,views::View * containing_view,int origin_x,int origin_y,double animation_progress,PaintDownloadProgressSize size)461 void PaintDownloadComplete(gfx::Canvas* canvas,
462 #if defined(TOOLKIT_VIEWS)
463                            views::View* containing_view,
464 #endif
465                            int origin_x,
466                            int origin_y,
467                            double animation_progress,
468                            PaintDownloadProgressSize size) {
469   // Load up our common bitmaps.
470   if (!g_foreground_16) {
471     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
472     g_foreground_16 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_16);
473     g_foreground_32 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_32);
474   }
475 
476   SkBitmap* complete = (size == BIG) ? g_foreground_32 : g_foreground_16;
477 
478   gfx::Rect complete_bounds(origin_x, origin_y,
479                             complete->width(), complete->height());
480 #if defined(TOOLKIT_VIEWS)
481   // Mirror the positions if necessary.
482   complete_bounds.set_x(containing_view->GetMirroredXForRect(complete_bounds));
483 #endif
484 
485   // Start at full opacity, then loop back and forth five times before ending
486   // at zero opacity.
487   double opacity = sin(animation_progress * PI * kCompleteAnimationCycles +
488                    PI/2) / 2 + 0.5;
489 
490   canvas->SaveLayerAlpha(static_cast<int>(255.0 * opacity), complete_bounds);
491   canvas->AsCanvasSkia()->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode);
492   canvas->DrawBitmapInt(*complete, complete_bounds.x(), complete_bounds.y());
493   canvas->Restore();
494 }
495 
PaintDownloadInterrupted(gfx::Canvas * canvas,views::View * containing_view,int origin_x,int origin_y,double animation_progress,PaintDownloadProgressSize size)496 void PaintDownloadInterrupted(gfx::Canvas* canvas,
497 #if defined(TOOLKIT_VIEWS)
498                               views::View* containing_view,
499 #endif
500                               int origin_x,
501                               int origin_y,
502                               double animation_progress,
503                               PaintDownloadProgressSize size) {
504   // Load up our common bitmaps.
505   if (!g_foreground_16) {
506     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
507     g_foreground_16 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_16);
508     g_foreground_32 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_32);
509   }
510 
511   SkBitmap* complete = (size == BIG) ? g_foreground_32 : g_foreground_16;
512 
513   gfx::Rect complete_bounds(origin_x, origin_y,
514                             complete->width(), complete->height());
515 #if defined(TOOLKIT_VIEWS)
516   // Mirror the positions if necessary.
517   complete_bounds.set_x(containing_view->GetMirroredXForRect(complete_bounds));
518 #endif
519 
520   // Start at zero opacity, then loop back and forth five times before ending
521   // at full opacity.
522   double opacity = sin(
523       (1.0 - animation_progress) * PI * kCompleteAnimationCycles + PI/2) / 2 +
524           0.5;
525 
526   canvas->SaveLayerAlpha(static_cast<int>(255.0 * opacity), complete_bounds);
527   canvas->AsCanvasSkia()->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode);
528   canvas->DrawBitmapInt(*complete, complete_bounds.x(), complete_bounds.y());
529   canvas->Restore();
530 }
531 
532 // Load a language dependent height so that the dangerous download confirmation
533 // message doesn't overlap with the download link label.
GetBigProgressIconSize()534 int GetBigProgressIconSize() {
535   static int big_progress_icon_size = 0;
536   if (big_progress_icon_size == 0) {
537     string16 locale_size_str =
538         l10n_util::GetStringUTF16(IDS_DOWNLOAD_BIG_PROGRESS_SIZE);
539     bool rc = base::StringToInt(locale_size_str, &big_progress_icon_size);
540     if (!rc || big_progress_icon_size < kBigProgressIconSize) {
541       NOTREACHED();
542       big_progress_icon_size = kBigProgressIconSize;
543     }
544   }
545 
546   return big_progress_icon_size;
547 }
548 
GetBigProgressIconOffset()549 int GetBigProgressIconOffset() {
550   return (GetBigProgressIconSize() - kBigIconSize) / 2;
551 }
552 
553 #if defined(TOOLKIT_VIEWS)
554 // Download dragging
DragDownload(const DownloadItem * download,gfx::Image * icon,gfx::NativeView view)555 void DragDownload(const DownloadItem* download,
556                   gfx::Image* icon,
557                   gfx::NativeView view) {
558   DCHECK(download);
559 
560   // Set up our OLE machinery
561   ui::OSExchangeData data;
562 
563   if (icon) {
564     drag_utils::CreateDragImageForFile(
565         download->GetFileNameToReportUser(), *icon, &data);
566   }
567 
568   const FilePath full_path = download->full_path();
569   data.SetFilename(full_path);
570 
571   std::string mime_type = download->mime_type();
572   if (mime_type.empty())
573     net::GetMimeTypeFromFile(full_path, &mime_type);
574 
575   // Add URL so that we can load supported files when dragged to TabContents.
576   if (net::IsSupportedMimeType(mime_type)) {
577     data.SetURL(net::FilePathToFileURL(full_path),
578                 download->GetFileNameToReportUser().LossyDisplayName());
579   }
580 
581 #if defined(OS_WIN)
582   scoped_refptr<ui::DragSource> drag_source(new ui::DragSource);
583 
584   // Run the drag and drop loop
585   DWORD effects;
586   DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data),
587              drag_source.get(), DROPEFFECT_COPY | DROPEFFECT_LINK, &effects);
588 #elif defined(TOOLKIT_USES_GTK)
589   GtkWidget* root = gtk_widget_get_toplevel(view);
590   if (!root)
591     return;
592 
593   views::WidgetGtk* widget = static_cast<views::WidgetGtk*>(
594       views::NativeWidget::GetNativeWidgetForNativeView(root));
595   if (!widget)
596     return;
597 
598   widget->DoDrag(data,
599                  ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK);
600 #endif  // OS_WIN
601 }
602 #elif defined(USE_X11)
DragDownload(const DownloadItem * download,gfx::Image * icon,gfx::NativeView view)603 void DragDownload(const DownloadItem* download,
604                   gfx::Image* icon,
605                   gfx::NativeView view) {
606   DownloadItemDrag::BeginDrag(download, icon);
607 }
608 #endif  // USE_X11
609 
CreateDownloadItemValue(DownloadItem * download,int id)610 DictionaryValue* CreateDownloadItemValue(DownloadItem* download, int id) {
611   DictionaryValue* file_value = new DictionaryValue();
612 
613   file_value->SetInteger("started",
614       static_cast<int>(download->start_time().ToTimeT()));
615   file_value->SetString("since_string",
616       TimeFormat::RelativeDate(download->start_time(), NULL));
617   file_value->SetString("date_string",
618       base::TimeFormatShortDate(download->start_time()));
619   file_value->SetInteger("id", id);
620   file_value->Set("file_path",
621                   base::CreateFilePathValue(download->GetTargetFilePath()));
622   // Keep file names as LTR.
623   string16 file_name = download->GetFileNameToReportUser().LossyDisplayName();
624   file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name);
625   file_value->SetString("file_name", file_name);
626   file_value->SetString("url", download->url().spec());
627   file_value->SetBoolean("otr", download->is_otr());
628 
629   if (download->IsInProgress()) {
630     if (download->safety_state() == DownloadItem::DANGEROUS) {
631       file_value->SetString("state", "DANGEROUS");
632       DCHECK(download->danger_type() == DownloadItem::DANGEROUS_FILE ||
633              download->danger_type() == DownloadItem::DANGEROUS_URL);
634       const char* danger_type_value =
635           download->danger_type() == DownloadItem::DANGEROUS_FILE ?
636           "DANGEROUS_FILE" : "DANGEROUS_URL";
637       file_value->SetString("danger_type", danger_type_value);
638     } else if (download->is_paused()) {
639       file_value->SetString("state", "PAUSED");
640     } else {
641       file_value->SetString("state", "IN_PROGRESS");
642     }
643 
644     file_value->SetString("progress_status_text",
645         GetProgressStatusText(download));
646 
647     file_value->SetInteger("percent",
648         static_cast<int>(download->PercentComplete()));
649     file_value->SetInteger("received",
650         static_cast<int>(download->received_bytes()));
651   } else if (download->IsInterrupted()) {
652     file_value->SetString("state", "INTERRUPTED");
653 
654     file_value->SetString("progress_status_text",
655         GetProgressStatusText(download));
656 
657     file_value->SetInteger("percent",
658         static_cast<int>(download->PercentComplete()));
659     file_value->SetInteger("received",
660         static_cast<int>(download->received_bytes()));
661   } else if (download->IsCancelled()) {
662     file_value->SetString("state", "CANCELLED");
663   } else if (download->IsComplete()) {
664     if (download->safety_state() == DownloadItem::DANGEROUS) {
665       file_value->SetString("state", "DANGEROUS");
666     } else {
667       file_value->SetString("state", "COMPLETE");
668     }
669   }
670 
671   file_value->SetInteger("total",
672       static_cast<int>(download->total_bytes()));
673 
674   return file_value;
675 }
676 
GetProgressStatusText(DownloadItem * download)677 string16 GetProgressStatusText(DownloadItem* download) {
678   int64 total = download->total_bytes();
679   int64 size = download->received_bytes();
680   DataUnits amount_units = GetByteDisplayUnits(size);
681   string16 received_size = FormatBytes(size, amount_units, true);
682   string16 amount = received_size;
683 
684   // Adjust both strings for the locale direction since we don't yet know which
685   // string we'll end up using for constructing the final progress string.
686   base::i18n::AdjustStringForLocaleDirection(&amount);
687 
688   if (total) {
689     amount_units = GetByteDisplayUnits(total);
690     string16 total_text = FormatBytes(total, amount_units, true);
691     base::i18n::AdjustStringForLocaleDirection(&total_text);
692 
693     base::i18n::AdjustStringForLocaleDirection(&received_size);
694     amount = l10n_util::GetStringFUTF16(IDS_DOWNLOAD_TAB_PROGRESS_SIZE,
695                                         received_size,
696                                         total_text);
697   } else {
698     amount.assign(received_size);
699   }
700   int64 current_speed = download->CurrentSpeed();
701   amount_units = GetByteDisplayUnits(current_speed);
702   string16 speed_text = FormatSpeed(current_speed, amount_units, true);
703   base::i18n::AdjustStringForLocaleDirection(&speed_text);
704 
705   base::TimeDelta remaining;
706   string16 time_remaining;
707   if (download->is_paused())
708     time_remaining = l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED);
709   else if (download->TimeRemaining(&remaining))
710     time_remaining = TimeFormat::TimeRemaining(remaining);
711 
712   if (time_remaining.empty()) {
713     base::i18n::AdjustStringForLocaleDirection(&amount);
714     return l10n_util::GetStringFUTF16(
715         IDS_DOWNLOAD_TAB_PROGRESS_STATUS_TIME_UNKNOWN, speed_text, amount);
716   }
717   return l10n_util::GetStringFUTF16(IDS_DOWNLOAD_TAB_PROGRESS_STATUS,
718                                     speed_text, amount, time_remaining);
719 }
720 
721 #if !defined(OS_MACOSX)
UpdateAppIconDownloadProgress(int download_count,bool progress_known,float progress)722 void UpdateAppIconDownloadProgress(int download_count,
723                                    bool progress_known,
724                                    float progress) {
725 #if defined(OS_WIN)
726   // Taskbar progress bar is only supported on Win7.
727   if (base::win::GetVersion() < base::win::VERSION_WIN7)
728     return;
729 
730   base::win::ScopedComPtr<ITaskbarList3> taskbar;
731   HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL,
732                                           CLSCTX_INPROC_SERVER);
733   if (FAILED(result)) {
734     VLOG(1) << "Failed creating a TaskbarList object: " << result;
735     return;
736   }
737 
738   result = taskbar->HrInit();
739   if (FAILED(result)) {
740     LOG(ERROR) << "Failed initializing an ITaskbarList3 interface.";
741     return;
742   }
743 
744   // Iterate through all the browser windows, and draw the progress bar.
745   for (BrowserList::const_iterator browser_iterator = BrowserList::begin();
746       browser_iterator != BrowserList::end(); browser_iterator++) {
747     Browser* browser = *browser_iterator;
748     BrowserWindow* window = browser->window();
749     if (!window)
750       continue;
751     HWND frame = window->GetNativeHandle();
752     if (download_count == 0 || progress == 1.0f)
753       taskbar->SetProgressState(frame, TBPF_NOPROGRESS);
754     else if (!progress_known)
755       taskbar->SetProgressState(frame, TBPF_INDETERMINATE);
756     else
757       taskbar->SetProgressValue(frame, static_cast<int>(progress * 100), 100);
758   }
759 #endif
760 }
761 #endif
762 
763 // Appends the passed the number between parenthesis the path before the
764 // extension.
AppendNumberToPath(FilePath * path,int number)765 void AppendNumberToPath(FilePath* path, int number) {
766   *path = path->InsertBeforeExtensionASCII(StringPrintf(" (%d)", number));
767 }
768 
769 // Attempts to find a number that can be appended to that path to make it
770 // unique. If |path| does not exist, 0 is returned.  If it fails to find such
771 // a number, -1 is returned.
GetUniquePathNumber(const FilePath & path)772 int GetUniquePathNumber(const FilePath& path) {
773   if (!file_util::PathExists(path))
774     return 0;
775 
776   FilePath new_path;
777   for (int count = 1; count <= kMaxUniqueFiles; ++count) {
778     new_path = FilePath(path);
779     AppendNumberToPath(&new_path, count);
780 
781     if (!file_util::PathExists(new_path))
782       return count;
783   }
784 
785   return -1;
786 }
787 
DownloadUrl(const GURL & url,const GURL & referrer,const std::string & referrer_charset,const DownloadSaveInfo & save_info,ResourceDispatcherHost * rdh,int render_process_host_id,int render_view_id,net::URLRequestContextGetter * request_context_getter)788 void DownloadUrl(
789     const GURL& url,
790     const GURL& referrer,
791     const std::string& referrer_charset,
792     const DownloadSaveInfo& save_info,
793     ResourceDispatcherHost* rdh,
794     int render_process_host_id,
795     int render_view_id,
796     net::URLRequestContextGetter* request_context_getter) {
797   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
798 
799   net::URLRequestContext* context =
800       request_context_getter->GetURLRequestContext();
801   context->set_referrer_charset(referrer_charset);
802 
803   rdh->BeginDownload(url,
804                      referrer,
805                      save_info,
806                      true,  // Show "Save as" UI.
807                      render_process_host_id,
808                      render_view_id,
809                      context);
810 }
811 
CancelDownloadRequest(ResourceDispatcherHost * rdh,int render_process_id,int request_id)812 void CancelDownloadRequest(ResourceDispatcherHost* rdh,
813                            int render_process_id,
814                            int request_id) {
815   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
816   // |rdh| may be NULL in unit tests.
817   if (rdh)
818     rdh->CancelRequest(render_process_id, request_id, false);
819 }
820 
NotifyDownloadInitiated(int render_process_id,int render_view_id)821 void NotifyDownloadInitiated(int render_process_id, int render_view_id) {
822   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
823   RenderViewHost* rvh = RenderViewHost::FromID(render_process_id,
824                                                render_view_id);
825   if (!rvh)
826     return;
827 
828   NotificationService::current()->Notify(NotificationType::DOWNLOAD_INITIATED,
829                                          Source<RenderViewHost>(rvh),
830                                          NotificationService::NoDetails());
831 }
832 
GetUniquePathNumberWithCrDownload(const FilePath & path)833 int GetUniquePathNumberWithCrDownload(const FilePath& path) {
834   if (!file_util::PathExists(path) &&
835       !file_util::PathExists(GetCrDownloadPath(path)))
836     return 0;
837 
838   FilePath new_path;
839   for (int count = 1; count <= kMaxUniqueFiles; ++count) {
840     new_path = FilePath(path);
841     AppendNumberToPath(&new_path, count);
842 
843     if (!file_util::PathExists(new_path) &&
844         !file_util::PathExists(GetCrDownloadPath(new_path)))
845       return count;
846   }
847 
848   return -1;
849 }
850 
851 namespace {
852 
853 // NOTE: If index is 0, deletes files that do not have the " (nnn)" appended.
DeleteUniqueDownloadFile(const FilePath & path,int index)854 void DeleteUniqueDownloadFile(const FilePath& path, int index) {
855   FilePath new_path(path);
856   if (index > 0)
857     AppendNumberToPath(&new_path, index);
858   file_util::Delete(new_path, false);
859 }
860 
861 }  // namespace
862 
EraseUniqueDownloadFiles(const FilePath & path)863 void EraseUniqueDownloadFiles(const FilePath& path) {
864   FilePath cr_path = GetCrDownloadPath(path);
865 
866   for (int index = 0; index <= kMaxUniqueFiles; ++index) {
867     DeleteUniqueDownloadFile(path, index);
868     DeleteUniqueDownloadFile(cr_path, index);
869   }
870 }
871 
GetCrDownloadPath(const FilePath & suggested_path)872 FilePath GetCrDownloadPath(const FilePath& suggested_path) {
873   FilePath::StringType file_name;
874   base::SStringPrintf(
875       &file_name,
876       PRFilePathLiteral FILE_PATH_LITERAL(".crdownload"),
877       suggested_path.value().c_str());
878   return FilePath(file_name);
879 }
880 
881 // TODO(erikkay,phajdan.jr): This is apparently not being exercised in tests.
IsDangerous(DownloadCreateInfo * info,Profile * profile,bool auto_open)882 bool IsDangerous(DownloadCreateInfo* info, Profile* profile, bool auto_open) {
883   DownloadDangerLevel danger_level = GetFileDangerLevel(
884       info->suggested_path.BaseName());
885   if (danger_level == Dangerous)
886     return !(auto_open && info->has_user_gesture);
887   if (danger_level == AllowOnUserGesture && !info->has_user_gesture)
888     return true;
889   if (info->is_extension_install) {
890     // Extensions that are not from the gallery are considered dangerous.
891     ExtensionService* service = profile->GetExtensionService();
892     if (!service ||
893         !service->IsDownloadFromGallery(info->url(), info->referrer_url))
894       return true;
895   }
896   return false;
897 }
898 
899 }  // namespace download_util
900