OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/download/chrome_download_manager_delegate.h" | 5 #include "chrome/browser/download/chrome_download_manager_delegate.h" |
6 | 6 |
| 7 #include "base/callback.h" |
| 8 #include "base/file_util.h" |
| 9 #include "base/path_service.h" |
| 10 #include "base/rand_util.h" |
| 11 #include "base/stringprintf.h" |
| 12 #include "chrome/browser/download/download_extensions.h" |
7 #include "chrome/browser/download/download_file_picker.h" | 13 #include "chrome/browser/download/download_file_picker.h" |
| 14 #include "chrome/browser/download/download_history.h" |
| 15 #include "chrome/browser/download/download_item.h" |
| 16 #include "chrome/browser/download/download_manager.h" |
| 17 #include "chrome/browser/download/download_prefs.h" |
| 18 #include "chrome/browser/download/download_safe_browsing_client.h" |
| 19 #include "chrome/browser/download/download_util.h" |
8 #include "chrome/browser/download/save_package_file_picker.h" | 20 #include "chrome/browser/download/save_package_file_picker.h" |
| 21 #include "chrome/browser/extensions/extension_service.h" |
9 #include "chrome/browser/prefs/pref_member.h" | 22 #include "chrome/browser/prefs/pref_member.h" |
10 #include "chrome/browser/prefs/pref_service.h" | 23 #include "chrome/browser/prefs/pref_service.h" |
11 #include "chrome/browser/profiles/profile.h" | 24 #include "chrome/browser/profiles/profile.h" |
12 #include "chrome/browser/ui/browser.h" | 25 #include "chrome/browser/ui/browser.h" |
13 #include "chrome/browser/ui/browser_list.h" | 26 #include "chrome/browser/ui/browser_list.h" |
| 27 #include "chrome/common/chrome_paths.h" |
| 28 #include "chrome/common/extensions/user_script.h" |
14 #include "chrome/common/pref_names.h" | 29 #include "chrome/common/pref_names.h" |
15 #include "content/browser/tab_contents/tab_contents.h" | 30 #include "content/browser/tab_contents/tab_contents.h" |
| 31 #include "grit/generated_resources.h" |
| 32 #include "ui/base/l10n/l10n_util.h" |
16 | 33 |
17 ChromeDownloadManagerDelegate::ChromeDownloadManagerDelegate() | 34 ChromeDownloadManagerDelegate::ChromeDownloadManagerDelegate() { |
18 : download_manager_(NULL) { | |
19 } | 35 } |
20 | 36 |
| 37 ChromeDownloadManagerDelegate::~ChromeDownloadManagerDelegate() { |
| 38 } |
| 39 |
| 40 bool ChromeDownloadManagerDelegate::ShouldStartDownload(int32 download_id) { |
| 41 // We create a download item and store it in our download map, and inform the |
| 42 // history system of a new download. Since this method can be called while the |
| 43 // history service thread is still reading the persistent state, we do not |
| 44 // insert the new DownloadItem into 'history_downloads_' or inform our |
| 45 // observers at this point. OnCreateDownloadEntryComplete() handles that |
| 46 // finalization of the the download creation as a callback from the history |
| 47 // thread. |
| 48 DownloadItem* download = |
| 49 download_manager_->GetActiveDownloadItem(download_id); |
| 50 if (!download) |
| 51 return false; |
| 52 |
| 53 #if defined(ENABLE_SAFE_BROWSING) |
| 54 // Create a client to verify download URL with safebrowsing. |
| 55 // It deletes itself after the callback. |
| 56 scoped_refptr<DownloadSBClient> sb_client = new DownloadSBClient( |
| 57 download_id, download->url_chain(), download->referrer_url(), |
| 58 download_manager_->profile()->GetPrefs()->GetBoolean( |
| 59 prefs::kSafeBrowsingEnabled)); |
| 60 sb_client->CheckDownloadUrl( |
| 61 NewCallback(this, &ChromeDownloadManagerDelegate::CheckDownloadUrlDone)); |
| 62 #else |
| 63 CheckDownloadUrlDone(download_id, false); |
| 64 #endif |
| 65 return false; |
| 66 } |
| 67 |
| 68 void ChromeDownloadManagerDelegate::ChooseDownloadPath( |
| 69 TabContents* tab_contents, |
| 70 const FilePath& suggested_path, |
| 71 void* data) { |
| 72 // Deletes itself. |
| 73 new DownloadFilePicker( |
| 74 download_manager_, tab_contents, suggested_path, data); |
| 75 } |
| 76 |
| 77 TabContents* ChromeDownloadManagerDelegate:: |
| 78 GetAlternativeTabContentsToNotifyForDownload() { |
| 79 // Start the download in the last active browser. This is not ideal but better |
| 80 // than fully hiding the download from the user. |
| 81 Browser* last_active = BrowserList::GetLastActiveWithProfile( |
| 82 download_manager_->profile()); |
| 83 return last_active ? last_active->GetSelectedTabContents() : NULL; |
| 84 } |
| 85 |
| 86 |
| 87 bool ChromeDownloadManagerDelegate::ShouldOpenFileBasedOnExtension( |
| 88 const FilePath& path) { |
| 89 FilePath::StringType extension = path.Extension(); |
| 90 if (extension.empty()) |
| 91 return false; |
| 92 if (Extension::IsExtension(path)) |
| 93 return false; |
| 94 DCHECK(extension[0] == FilePath::kExtensionSeparator); |
| 95 extension.erase(0, 1); |
| 96 return download_manager_->download_prefs()-> |
| 97 IsAutoOpenEnabledForExtension(extension); |
| 98 } |
| 99 |
| 100 |
21 void ChromeDownloadManagerDelegate::GetSaveDir(TabContents* tab_contents, | 101 void ChromeDownloadManagerDelegate::GetSaveDir(TabContents* tab_contents, |
22 FilePath* website_save_dir, | 102 FilePath* website_save_dir, |
23 FilePath* download_save_dir) { | 103 FilePath* download_save_dir) { |
24 Profile* profile = | 104 Profile* profile = |
25 Profile::FromBrowserContext(tab_contents->browser_context()); | 105 Profile::FromBrowserContext(tab_contents->browser_context()); |
26 PrefService* prefs = profile->GetPrefs(); | 106 PrefService* prefs = profile->GetPrefs(); |
27 | 107 |
28 // Check whether the preference has the preferred directory for saving file. | 108 // Check whether the preference has the preferred directory for saving file. |
29 // If not, initialize it with default directory. | 109 // If not, initialize it with default directory. |
30 if (!prefs->FindPreference(prefs::kSaveFileDefaultDirectory)) { | 110 if (!prefs->FindPreference(prefs::kSaveFileDefaultDirectory)) { |
(...skipping 14 matching lines...) Expand all Loading... |
45 | 125 |
46 void ChromeDownloadManagerDelegate::ChooseSavePath( | 126 void ChromeDownloadManagerDelegate::ChooseSavePath( |
47 const base::WeakPtr<SavePackage>& save_package, | 127 const base::WeakPtr<SavePackage>& save_package, |
48 const FilePath& suggested_path, | 128 const FilePath& suggested_path, |
49 bool can_save_as_complete) { | 129 bool can_save_as_complete) { |
50 // Deletes itself. | 130 // Deletes itself. |
51 new SavePackageFilePicker( | 131 new SavePackageFilePicker( |
52 save_package, suggested_path, can_save_as_complete); | 132 save_package, suggested_path, can_save_as_complete); |
53 } | 133 } |
54 | 134 |
55 void ChromeDownloadManagerDelegate::ChooseDownloadPath( | 135 void ChromeDownloadManagerDelegate::CheckDownloadUrlDone( |
56 DownloadManager* download_manager, | 136 int32 download_id, bool is_dangerous_url) { |
57 TabContents* tab_contents, | 137 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
58 const FilePath& suggested_path, | 138 |
59 void* data) { | 139 DownloadItem* download = |
60 // Deletes itself. | 140 download_manager_->GetActiveDownloadItem(download_id); |
61 new DownloadFilePicker( | 141 if (!download) |
62 download_manager, tab_contents, suggested_path, data); | 142 return; |
63 } | 143 |
64 | 144 if (is_dangerous_url) |
65 TabContents* | 145 download->MarkUrlDangerous(); |
66 ChromeDownloadManagerDelegate::GetAlternativeTabContentsToNotifyForDownload( | 146 |
67 DownloadManager* download_manager) { | 147 download_manager_->download_history()->CheckVisitedReferrerBefore( |
68 // Start the download in the last active browser. This is not ideal but better | 148 download_id, |
69 // than fully hiding the download from the user. | 149 download->referrer_url(), |
70 Browser* last_active = BrowserList::GetLastActiveWithProfile( | 150 NewCallback(this, |
71 download_manager->profile()); | 151 &ChromeDownloadManagerDelegate::CheckVisitedReferrerBeforeDone)); |
72 return last_active ? last_active->GetSelectedTabContents() : NULL; | 152 } |
73 } | 153 |
| 154 void ChromeDownloadManagerDelegate::CheckVisitedReferrerBeforeDone( |
| 155 int32 download_id, |
| 156 bool visited_referrer_before) { |
| 157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 158 |
| 159 DownloadItem* download = |
| 160 download_manager_->GetActiveDownloadItem(download_id); |
| 161 if (!download) |
| 162 return; |
| 163 |
| 164 // Check whether this download is for an extension install or not. |
| 165 // Allow extensions to be explicitly saved. |
| 166 DownloadStateInfo state = download->state_info(); |
| 167 if (!state.prompt_user_for_save_location) { |
| 168 if (UserScript::IsURLUserScript(download->GetURL(), |
| 169 download->mime_type()) || |
| 170 (download->mime_type() == Extension::kMimeType)) { |
| 171 state.is_extension_install = true; |
| 172 } |
| 173 } |
| 174 |
| 175 if (state.force_file_name.empty()) { |
| 176 FilePath generated_name; |
| 177 download_util::GenerateFileNameFromRequest(*download, |
| 178 &generated_name); |
| 179 |
| 180 // Freeze the user's preference for showing a Save As dialog. We're going |
| 181 // to bounce around a bunch of threads and we don't want to worry about race |
| 182 // conditions where the user changes this pref out from under us. |
| 183 if (download_manager_->download_prefs()->PromptForDownload()) { |
| 184 // But ignore the user's preference for the following scenarios: |
| 185 // 1) Extension installation. Note that we only care here about the case |
| 186 // where an extension is installed, not when one is downloaded with |
| 187 // "save as...". |
| 188 // 2) Filetypes marked "always open." If the user just wants this file |
| 189 // opened, don't bother asking where to keep it. |
| 190 if (!state.is_extension_install && |
| 191 !ShouldOpenFileBasedOnExtension(generated_name)) |
| 192 state.prompt_user_for_save_location = true; |
| 193 } |
| 194 if (download_manager_->download_prefs()->IsDownloadPathManaged()) { |
| 195 state.prompt_user_for_save_location = false; |
| 196 } |
| 197 |
| 198 // Determine the proper path for a download, by either one of the following: |
| 199 // 1) using the default download directory. |
| 200 // 2) prompting the user. |
| 201 if (state.prompt_user_for_save_location && |
| 202 !download_manager_->last_download_path().empty()) { |
| 203 state.suggested_path = download_manager_->last_download_path(); |
| 204 } else { |
| 205 state.suggested_path = |
| 206 download_manager_->download_prefs()->download_path(); |
| 207 } |
| 208 state.suggested_path = state.suggested_path.Append(generated_name); |
| 209 } else { |
| 210 state.suggested_path = state.force_file_name; |
| 211 } |
| 212 |
| 213 if (!state.prompt_user_for_save_location && state.force_file_name.empty()) { |
| 214 state.is_dangerous_file = |
| 215 IsDangerousFile(*download, state, visited_referrer_before); |
| 216 } |
| 217 |
| 218 // We need to move over to the download thread because we don't want to stat |
| 219 // the suggested path on the UI thread. |
| 220 // We can only access preferences on the UI thread, so check the download path |
| 221 // now and pass the value to the FILE thread. |
| 222 BrowserThread::PostTask( |
| 223 BrowserThread::FILE, FROM_HERE, |
| 224 NewRunnableMethod( |
| 225 this, |
| 226 &ChromeDownloadManagerDelegate::CheckIfSuggestedPathExists, |
| 227 download->id(), |
| 228 state, |
| 229 download_manager_->download_prefs()->download_path())); |
| 230 } |
| 231 |
| 232 void ChromeDownloadManagerDelegate::CheckIfSuggestedPathExists( |
| 233 int32 download_id, |
| 234 DownloadStateInfo state, |
| 235 const FilePath& default_path) { |
| 236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 237 |
| 238 // Make sure the default download directory exists. |
| 239 // TODO(phajdan.jr): only create the directory when we're sure the user |
| 240 // is going to save there and not to another directory of his choice. |
| 241 file_util::CreateDirectory(default_path); |
| 242 |
| 243 // Check writability of the suggested path. If we can't write to it, default |
| 244 // to the user's "My Documents" directory. We'll prompt them in this case. |
| 245 FilePath dir = state.suggested_path.DirName(); |
| 246 FilePath filename = state.suggested_path.BaseName(); |
| 247 if (!file_util::PathIsWritable(dir)) { |
| 248 VLOG(1) << "Unable to write to directory \"" << dir.value() << "\""; |
| 249 state.prompt_user_for_save_location = true; |
| 250 PathService::Get(chrome::DIR_USER_DOCUMENTS, &state.suggested_path); |
| 251 state.suggested_path = state.suggested_path.Append(filename); |
| 252 } |
| 253 |
| 254 // If the download is deemed dangerous, we'll use a temporary name for it. |
| 255 if (state.IsDangerous()) { |
| 256 state.target_name = FilePath(state.suggested_path).BaseName(); |
| 257 // Create a temporary file to hold the file until the user approves its |
| 258 // download. |
| 259 FilePath::StringType file_name; |
| 260 FilePath path; |
| 261 #if defined(OS_WIN) |
| 262 string16 unconfirmed_prefix = |
| 263 l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX); |
| 264 #else |
| 265 std::string unconfirmed_prefix = |
| 266 l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX); |
| 267 #endif |
| 268 |
| 269 while (path.empty()) { |
| 270 base::SStringPrintf( |
| 271 &file_name, |
| 272 unconfirmed_prefix.append( |
| 273 FILE_PATH_LITERAL(" %d.crdownload")).c_str(), |
| 274 base::RandInt(0, 100000)); |
| 275 path = dir.Append(file_name); |
| 276 if (file_util::PathExists(path)) |
| 277 path = FilePath(); |
| 278 } |
| 279 state.suggested_path = path; |
| 280 } else { |
| 281 // Do not add the path uniquifier if we are saving to a specific path as in |
| 282 // the drag-out case. |
| 283 if (state.force_file_name.empty()) { |
| 284 state.path_uniquifier = download_util::GetUniquePathNumberWithCrDownload( |
| 285 state.suggested_path); |
| 286 } |
| 287 // We know the final path, build it if necessary. |
| 288 if (state.path_uniquifier > 0) { |
| 289 download_util::AppendNumberToPath(&(state.suggested_path), |
| 290 state.path_uniquifier); |
| 291 // Setting path_uniquifier to 0 to make sure we don't try to unique it |
| 292 // later on. |
| 293 state.path_uniquifier = 0; |
| 294 } else if (state.path_uniquifier == -1) { |
| 295 // We failed to find a unique path. We have to prompt the user. |
| 296 VLOG(1) << "Unable to find a unique path for suggested path \"" |
| 297 << state.suggested_path.value() << "\""; |
| 298 state.prompt_user_for_save_location = true; |
| 299 } |
| 300 } |
| 301 |
| 302 // Create an empty file at the suggested path so that we don't allocate the |
| 303 // same "non-existant" path to multiple downloads. |
| 304 // See: http://code.google.com/p/chromium/issues/detail?id=3662 |
| 305 if (!state.prompt_user_for_save_location && |
| 306 state.force_file_name.empty()) { |
| 307 if (state.IsDangerous()) |
| 308 file_util::WriteFile(state.suggested_path, "", 0); |
| 309 else |
| 310 file_util::WriteFile(download_util::GetCrDownloadPath( |
| 311 state.suggested_path), "", 0); |
| 312 } |
| 313 |
| 314 BrowserThread::PostTask( |
| 315 BrowserThread::UI, FROM_HERE, |
| 316 NewRunnableMethod( |
| 317 this, |
| 318 &ChromeDownloadManagerDelegate::OnPathExistenceAvailable, |
| 319 download_id, |
| 320 state)); |
| 321 } |
| 322 |
| 323 void ChromeDownloadManagerDelegate::OnPathExistenceAvailable( |
| 324 int32 download_id, |
| 325 const DownloadStateInfo& new_state) { |
| 326 DownloadItem* download = |
| 327 download_manager_->GetActiveDownloadItem(download_id); |
| 328 if (!download) |
| 329 return; |
| 330 download->SetFileCheckResults(new_state); |
| 331 download_manager_->RestartDownload(download_id); |
| 332 } |
| 333 |
| 334 // TODO(phajdan.jr): This is apparently not being exercised in tests. |
| 335 bool ChromeDownloadManagerDelegate::IsDangerousFile( |
| 336 const DownloadItem& download, |
| 337 const DownloadStateInfo& state, |
| 338 bool visited_referrer_before) { |
| 339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 340 |
| 341 bool auto_open = ShouldOpenFileBasedOnExtension(state.suggested_path); |
| 342 download_util::DownloadDangerLevel danger_level = |
| 343 download_util::GetFileDangerLevel(state.suggested_path.BaseName()); |
| 344 |
| 345 if (danger_level == download_util::Dangerous) |
| 346 return !(auto_open && state.has_user_gesture); |
| 347 |
| 348 if (danger_level == download_util::AllowOnUserGesture && |
| 349 (!state.has_user_gesture || !visited_referrer_before)) |
| 350 return true; |
| 351 |
| 352 if (state.is_extension_install) { |
| 353 // Extensions that are not from the gallery are considered dangerous. |
| 354 ExtensionService* service = |
| 355 download_manager_->profile()->GetExtensionService(); |
| 356 if (!service || !service->IsDownloadFromGallery(download.GetURL(), |
| 357 download.referrer_url())) |
| 358 return true; |
| 359 } |
| 360 return false; |
| 361 } |
OLD | NEW |