Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 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 | // Download utility implementation |
| 6 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 7 | #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) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 18 | #include "base/strings/string16.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 19 | #include "base/strings/string_number_conversions.h" |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 20 | #include "base/strings/sys_string_conversions.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 21 | #include "base/strings/utf_string_conversions.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 22 | #include "base/threading/thread_restrictions.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 23 | #include "base/value_conversions.h" |
| 24 | #include "base/values.h" |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 25 | #include "chrome/browser/chrome_notification_types.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 26 | #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) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 29 | #include "chrome/common/chrome_paths.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 30 | #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) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 36 | #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 Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 42 | #include "ui/base/l10n/time_format.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 43 | #include "ui/base/text/bytes_formatting.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 44 | #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) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 60 | #include "ui/base/dragdrop/drag_source_win.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 61 | #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) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 70 | namespace download_util { |
| 71 | |
| 72 | using content::DownloadItem; |
| 73 | |
| 74 | // Download temporary file creation -------------------------------------------- |
| 75 | |
| 76 | class DefaultDownloadDirectory { |
| 77 | public: |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 78 | const base::FilePath& path() const { return path_; } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 79 | 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) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 93 | base::FilePath path_; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 94 | }; |
| 95 | |
| 96 | static base::LazyInstance<DefaultDownloadDirectory> |
| 97 | g_default_download_directory = LAZY_INSTANCE_INITIALIZER; |
| 98 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 99 | const base::FilePath& GetDefaultDownloadDirectory() { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 100 | 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) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 105 | bool DownloadPathIsDangerous(const base::FilePath& download_path) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 106 | #if defined(OS_LINUX) |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 107 | base::FilePath home_dir = file_util::GetHomeDir(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 108 | 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) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 117 | base::FilePath desktop_dir; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 118 | 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) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 126 | #if defined(TOOLKIT_VIEWS) |
| 127 | // Download dragging |
| 128 | void DragDownload(const DownloadItem* download, |
| 129 | gfx::Image* icon, |
| 130 | gfx::NativeView view) { |
| 131 | DCHECK(download); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 132 | DCHECK_EQ(DownloadItem::COMPLETE, download->GetState()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 133 | |
| 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) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 142 | const base::FilePath full_path = download->GetTargetFilePath(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 143 | 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) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 172 | // WebContentsViewWin, not a NativeWidgetWin. |
| 173 | scoped_refptr<ui::DragSourceWin> drag_source(new ui::DragSourceWin); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 174 | // 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) |
| 195 | void DragDownload(const DownloadItem* download, |
| 196 | gfx::Image* icon, |
| 197 | gfx::NativeView view) { |
| 198 | DownloadItemDrag::BeginDrag(download, icon); |
| 199 | } |
| 200 | #endif // USE_X11 |
| 201 | |
| 202 | string16 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 Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 232 | time_remaining = ui::TimeFormat::TimeRemaining(remaining); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 233 | |
| 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) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 243 | void 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 | |
| 262 | void RecordDownloadCount(ChromeDownloadCountTypes type) { |
| 263 | UMA_HISTOGRAM_ENUMERATION( |
| 264 | "Download.CountsChrome", type, CHROME_DOWNLOAD_COUNT_TYPES_LAST_ENTRY); |
| 265 | } |
| 266 | |
| 267 | void RecordDownloadSource(ChromeDownloadSource source) { |
| 268 | UMA_HISTOGRAM_ENUMERATION( |
| 269 | "Download.SourcesChrome", source, CHROME_DOWNLOAD_SOURCE_LAST_ENTRY); |
| 270 | } |
| 271 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 272 | } // namespace download_util |