Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(172)

Side by Side Diff: chrome/browser/download/chrome_download_manager_delegate.cc

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

Powered by Google App Engine
This is Rietveld 408576698