blob: 8010f97301aaa887eaec16263d701d85170f2102 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// 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// Download utility implementation
6
Torne (Richard Coles)58218062012-11-14 11:43:16 +00007#include "chrome/browser/download/download_util.h"
8
9#include <cmath>
10#include <string>
11
12#include "base/file_util.h"
13#include "base/i18n/rtl.h"
14#include "base/i18n/time_formatting.h"
15#include "base/lazy_instance.h"
16#include "base/metrics/histogram.h"
17#include "base/path_service.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010018#include "base/strings/string16.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000019#include "base/strings/string_number_conversions.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010020#include "base/strings/sys_string_conversions.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010021#include "base/strings/utf_string_conversions.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000022#include "base/threading/thread_restrictions.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000023#include "base/value_conversions.h"
24#include "base/values.h"
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010025#include "chrome/browser/chrome_notification_types.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000026#include "chrome/browser/download/download_extensions.h"
27#include "chrome/browser/download/download_item_model.h"
28#include "chrome/browser/profiles/profile.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000029#include "chrome/common/chrome_paths.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000030#include "content/public/browser/download_item.h"
31#include "content/public/browser/download_manager.h"
32#include "content/public/browser/render_view_host.h"
33#include "content/public/common/url_constants.h"
34#include "grit/generated_resources.h"
35#include "grit/locale_settings.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000036#include "net/base/mime_util.h"
37#include "net/base/net_util.h"
38#include "skia/ext/image_operations.h"
39#include "third_party/skia/include/core/SkPath.h"
40#include "third_party/skia/include/core/SkShader.h"
41#include "ui/base/l10n/l10n_util.h"
Ben Murdochbb1529c2013-08-08 10:24:53 +010042#include "ui/base/l10n/time_format.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000043#include "ui/base/text/bytes_formatting.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000044#include "ui/gfx/image/image.h"
45#include "ui/gfx/rect.h"
46
47#if defined(TOOLKIT_VIEWS)
48#include "ui/base/dragdrop/drag_drop_types.h"
49#include "ui/base/dragdrop/drag_utils.h"
50#include "ui/base/dragdrop/os_exchange_data.h"
51#include "ui/gfx/screen.h"
52#include "ui/views/widget/widget.h"
53#endif
54
55#if defined(TOOLKIT_GTK)
56#include "chrome/browser/ui/gtk/custom_drag.h"
57#endif // defined(TOOLKIT_GTK)
58
59#if defined(OS_WIN) && !defined(USE_AURA)
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000060#include "ui/base/dragdrop/drag_source_win.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000061#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
62#endif
63
64#if defined(USE_AURA)
65#include "ui/aura/client/drag_drop_client.h"
66#include "ui/aura/root_window.h"
67#include "ui/aura/window.h"
68#endif
69
Torne (Richard Coles)58218062012-11-14 11:43:16 +000070namespace download_util {
71
72using content::DownloadItem;
73
74// Download temporary file creation --------------------------------------------
75
76class DefaultDownloadDirectory {
77 public:
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000078 const base::FilePath& path() const { return path_; }
Torne (Richard Coles)58218062012-11-14 11:43:16 +000079 private:
80 DefaultDownloadDirectory() {
81 if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &path_)) {
82 NOTREACHED();
83 }
84 if (DownloadPathIsDangerous(path_)) {
85 // This is only useful on platforms that support
86 // DIR_DEFAULT_DOWNLOADS_SAFE.
87 if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS_SAFE, &path_)) {
88 NOTREACHED();
89 }
90 }
91 }
92 friend struct base::DefaultLazyInstanceTraits<DefaultDownloadDirectory>;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000093 base::FilePath path_;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000094};
95
96static base::LazyInstance<DefaultDownloadDirectory>
97 g_default_download_directory = LAZY_INSTANCE_INITIALIZER;
98
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000099const base::FilePath& GetDefaultDownloadDirectory() {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000100 return g_default_download_directory.Get().path();
101}
102
103// Consider downloads 'dangerous' if they go to the home directory on Linux and
104// to the desktop on any platform.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000105bool DownloadPathIsDangerous(const base::FilePath& download_path) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000106#if defined(OS_LINUX)
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000107 base::FilePath home_dir = file_util::GetHomeDir();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000108 if (download_path == home_dir) {
109 return true;
110 }
111#endif
112
113#if defined(OS_ANDROID)
114 // Android does not have a desktop dir.
115 return false;
116#else
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000117 base::FilePath desktop_dir;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000118 if (!PathService::Get(base::DIR_USER_DESKTOP, &desktop_dir)) {
119 NOTREACHED();
120 return false;
121 }
122 return (download_path == desktop_dir);
123#endif
124}
125
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000126#if defined(TOOLKIT_VIEWS)
127// Download dragging
128void DragDownload(const DownloadItem* download,
129 gfx::Image* icon,
130 gfx::NativeView view) {
131 DCHECK(download);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100132 DCHECK_EQ(DownloadItem::COMPLETE, download->GetState());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000133
134 // Set up our OLE machinery
135 ui::OSExchangeData data;
136
137 if (icon) {
138 drag_utils::CreateDragImageForFile(
139 download->GetFileNameToReportUser(), icon->ToImageSkia(), &data);
140 }
141
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000142 const base::FilePath full_path = download->GetTargetFilePath();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000143 data.SetFilename(full_path);
144
145 std::string mime_type = download->GetMimeType();
146 if (mime_type.empty())
147 net::GetMimeTypeFromFile(full_path, &mime_type);
148
149 // Add URL so that we can load supported files when dragged to WebContents.
150 if (net::IsSupportedMimeType(mime_type)) {
151 data.SetURL(net::FilePathToFileURL(full_path),
152 download->GetFileNameToReportUser().LossyDisplayName());
153 }
154
155#if !defined(TOOLKIT_GTK)
156#if defined(USE_AURA)
157 aura::RootWindow* root_window = view->GetRootWindow();
158 if (!root_window || !aura::client::GetDragDropClient(root_window))
159 return;
160
161 gfx::Point location = gfx::Screen::GetScreenFor(view)->GetCursorScreenPoint();
162 // TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below.
163 aura::client::GetDragDropClient(root_window)->StartDragAndDrop(
164 data,
165 root_window,
166 view,
167 location,
168 ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK,
169 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
170#else // We are on WIN without AURA
171 // We cannot use Widget::RunShellDrag on WIN since the |view| is backed by a
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000172 // WebContentsViewWin, not a NativeWidgetWin.
173 scoped_refptr<ui::DragSourceWin> drag_source(new ui::DragSourceWin);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000174 // Run the drag and drop loop
175 DWORD effects;
176 DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data),
177 drag_source.get(), DROPEFFECT_COPY | DROPEFFECT_LINK, &effects);
178#endif
179
180#else
181 GtkWidget* root = gtk_widget_get_toplevel(view);
182 if (!root)
183 return;
184
185 views::NativeWidgetGtk* widget = static_cast<views::NativeWidgetGtk*>(
186 views::Widget::GetWidgetForNativeView(root)->native_widget());
187 if (!widget)
188 return;
189
190 widget->DoDrag(data,
191 ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK);
192#endif // TOOLKIT_GTK
193}
194#elif defined(USE_X11)
195void DragDownload(const DownloadItem* download,
196 gfx::Image* icon,
197 gfx::NativeView view) {
198 DownloadItemDrag::BeginDrag(download, icon);
199}
200#endif // USE_X11
201
202string16 GetProgressStatusText(DownloadItem* download) {
203 int64 total = download->GetTotalBytes();
204 int64 size = download->GetReceivedBytes();
205 string16 received_size = ui::FormatBytes(size);
206 string16 amount = received_size;
207
208 // Adjust both strings for the locale direction since we don't yet know which
209 // string we'll end up using for constructing the final progress string.
210 base::i18n::AdjustStringForLocaleDirection(&amount);
211
212 if (total) {
213 string16 total_text = ui::FormatBytes(total);
214 base::i18n::AdjustStringForLocaleDirection(&total_text);
215
216 base::i18n::AdjustStringForLocaleDirection(&received_size);
217 amount = l10n_util::GetStringFUTF16(IDS_DOWNLOAD_TAB_PROGRESS_SIZE,
218 received_size,
219 total_text);
220 } else {
221 amount.assign(received_size);
222 }
223 int64 current_speed = download->CurrentSpeed();
224 string16 speed_text = ui::FormatSpeed(current_speed);
225 base::i18n::AdjustStringForLocaleDirection(&speed_text);
226
227 base::TimeDelta remaining;
228 string16 time_remaining;
229 if (download->IsPaused())
230 time_remaining = l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED);
231 else if (download->TimeRemaining(&remaining))
Ben Murdochbb1529c2013-08-08 10:24:53 +0100232 time_remaining = ui::TimeFormat::TimeRemaining(remaining);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000233
234 if (time_remaining.empty()) {
235 base::i18n::AdjustStringForLocaleDirection(&amount);
236 return l10n_util::GetStringFUTF16(
237 IDS_DOWNLOAD_TAB_PROGRESS_STATUS_TIME_UNKNOWN, speed_text, amount);
238 }
239 return l10n_util::GetStringFUTF16(IDS_DOWNLOAD_TAB_PROGRESS_STATUS,
240 speed_text, amount, time_remaining);
241}
242
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000243void RecordShelfClose(int size, int in_progress, bool autoclose) {
244 static const int kMaxShelfSize = 16;
245 if (autoclose) {
246 UMA_HISTOGRAM_ENUMERATION("Download.ShelfSizeOnAutoClose",
247 size,
248 kMaxShelfSize);
249 UMA_HISTOGRAM_ENUMERATION("Download.ShelfInProgressSizeOnAutoClose",
250 in_progress,
251 kMaxShelfSize);
252 } else {
253 UMA_HISTOGRAM_ENUMERATION("Download.ShelfSizeOnUserClose",
254 size,
255 kMaxShelfSize);
256 UMA_HISTOGRAM_ENUMERATION("Download.ShelfInProgressSizeOnUserClose",
257 in_progress,
258 kMaxShelfSize);
259 }
260}
261
262void RecordDownloadCount(ChromeDownloadCountTypes type) {
263 UMA_HISTOGRAM_ENUMERATION(
264 "Download.CountsChrome", type, CHROME_DOWNLOAD_COUNT_TYPES_LAST_ENTRY);
265}
266
267void RecordDownloadSource(ChromeDownloadSource source) {
268 UMA_HISTOGRAM_ENUMERATION(
269 "Download.SourcesChrome", source, CHROME_DOWNLOAD_SOURCE_LAST_ENTRY);
270}
271
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000272} // namespace download_util