Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2013 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 #include "chrome/browser/download/download_target_determiner.h" | |
| 6 | |
| 7 #include "base/prefs/pref_service.h" | |
| 8 #include "base/rand_util.h" | |
| 9 #include "base/stringprintf.h" | |
| 10 #include "base/time.h" | |
| 11 #include "chrome/browser/download/chrome_download_manager_delegate.h" | |
| 12 #include "chrome/browser/download/download_crx_util.h" | |
| 13 #include "chrome/browser/download/download_extensions.h" | |
| 14 #include "chrome/browser/download/download_prefs.h" | |
| 15 #include "chrome/browser/download/download_util.h" | |
| 16 #include "chrome/browser/extensions/webstore_installer.h" | |
| 17 #include "chrome/browser/history/history_service.h" | |
| 18 #include "chrome/browser/history/history_service_factory.h" | |
| 19 #include "chrome/browser/profiles/profile.h" | |
| 20 #include "chrome/common/extensions/extension.h" | |
| 21 #include "chrome/common/extensions/feature_switch.h" | |
| 22 #include "chrome/common/pref_names.h" | |
| 23 #include "content/public/browser/browser_context.h" | |
| 24 #include "content/public/browser/browser_thread.h" | |
| 25 #include "grit/generated_resources.h" | |
| 26 #include "net/base/net_util.h" | |
| 27 #include "ui/base/l10n/l10n_util.h" | |
| 28 | |
| 29 #if defined(OS_CHROMEOS) | |
| 30 #include "chrome/browser/chromeos/drive/drive_download_handler.h" | |
| 31 #include "chrome/browser/chromeos/drive/drive_file_system_util.h" | |
| 32 #endif | |
| 33 | |
| 34 using content::BrowserThread; | |
| 35 using content::DownloadItem; | |
| 36 | |
| 37 namespace { | |
| 38 | |
| 39 // Condenses the results from HistoryService::GetVisibleVisitCountToHost() to a | |
| 40 // single bool. A host is considered visited before if prior visible vists were | |
|
benjhayden
2013/04/26 15:07:37
s/vists/visits/
asanka
2013/04/26 17:02:21
Done.
| |
| 41 // found in history and the first such visit was earlier than the most recent | |
| 42 // midnight. | |
| 43 void VisitCountsToVisitedBefore( | |
| 44 const base::Callback<void(bool)>& callback, | |
| 45 HistoryService::Handle unused_handle, | |
| 46 bool found_visits, | |
| 47 int count, | |
| 48 base::Time first_visit) { | |
| 49 callback.Run( | |
| 50 found_visits && count > 0 && | |
| 51 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight())); | |
| 52 } | |
| 53 | |
| 54 } // namespace | |
| 55 | |
| 56 DownloadTargetDeterminerDelegate::~DownloadTargetDeterminerDelegate() { | |
| 57 } | |
| 58 | |
| 59 DownloadTargetDeterminer::DownloadTargetDeterminer( | |
| 60 DownloadItem* download, | |
| 61 DownloadPrefs* download_prefs, | |
| 62 const base::FilePath& last_selected_directory, | |
| 63 DownloadTargetDeterminerDelegate* delegate, | |
| 64 const content::DownloadTargetCallback& callback) | |
| 65 : next_state_(STATE_GENERATE_TARGET_PATH), | |
| 66 should_prompt_(false), | |
| 67 conflict_action_(download->GetForcedFilePath().empty() ? | |
| 68 DownloadPathReservationTracker::UNIQUIFY : | |
| 69 DownloadPathReservationTracker::OVERWRITE), | |
| 70 danger_type_(download->GetDangerType()), | |
| 71 download_(download), | |
| 72 download_prefs_(download_prefs), | |
| 73 delegate_(delegate), | |
| 74 last_selected_directory_(last_selected_directory), | |
| 75 completion_callback_(callback), | |
| 76 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
| 77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 78 DCHECK(download_); | |
| 79 DCHECK(delegate); | |
| 80 download_->AddObserver(this); | |
| 81 | |
| 82 DoLoop(); | |
| 83 } | |
| 84 | |
| 85 DownloadTargetDeterminer::~DownloadTargetDeterminer() { | |
| 86 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 87 DCHECK(download_); | |
| 88 DCHECK(completion_callback_.is_null()); | |
| 89 download_->RemoveObserver(this); | |
| 90 } | |
| 91 | |
| 92 void DownloadTargetDeterminer::DoLoop() { | |
| 93 Result result = CONTINUE; | |
| 94 do { | |
| 95 State current_state = next_state_; | |
| 96 next_state_ = STATE_NONE; | |
| 97 | |
| 98 switch (current_state) { | |
| 99 case STATE_GENERATE_TARGET_PATH: | |
| 100 result = DoGenerateTargetPath(); | |
| 101 break; | |
| 102 case STATE_NOTIFY_EXTENSIONS: | |
| 103 result = DoNotifyExtensions(); | |
| 104 break; | |
| 105 case STATE_RESERVE_VIRTUAL_PATH: | |
| 106 result = DoReserveVirtualPath(); | |
| 107 break; | |
| 108 case STATE_PROMPT_USER_FOR_DOWNLOAD_PATH: | |
| 109 result = DoPromptUserForDownloadPath(); | |
| 110 break; | |
| 111 case STATE_DETERMINE_LOCAL_PATH: | |
| 112 result = DoDetermineLocalPath(); | |
| 113 break; | |
| 114 case STATE_CHECK_DOWNLOAD_URL: | |
| 115 result = DoCheckDownloadUrl(); | |
| 116 break; | |
| 117 case STATE_DETERMINE_INTERMEDIATE_PATH: | |
| 118 result = DoDetermineIntermediatePath(); | |
| 119 break; | |
| 120 case STATE_CHECK_VISITED_REFERRER_BEFORE: | |
| 121 result = DoCheckVisitedReferrerBefore(); | |
| 122 break; | |
| 123 case STATE_NONE: | |
| 124 NOTREACHED(); | |
| 125 return; | |
| 126 } | |
| 127 } while (result == CONTINUE); | |
| 128 // Note that if a callback completes synchronously, the handler will still | |
| 129 // return QUIT_DOLOOP. In this case, an inner DoLoop() may complete the target | |
| 130 // determination and delete |this|. | |
| 131 | |
| 132 if (result == COMPLETE) | |
| 133 ScheduleCallbackAndDeleteSelf(); | |
| 134 } | |
| 135 | |
| 136 DownloadTargetDeterminer::Result | |
| 137 DownloadTargetDeterminer::DoGenerateTargetPath() { | |
| 138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 139 DCHECK(virtual_path_.empty()); | |
| 140 DCHECK(local_path_.empty()); | |
| 141 bool is_forced_path = !download_->GetForcedFilePath().empty(); | |
| 142 | |
| 143 next_state_ = STATE_NOTIFY_EXTENSIONS; | |
| 144 | |
| 145 // If we don't have a forced path, we should construct a path for the | |
| 146 // download. Forced paths are only specified for programmatic downloads | |
| 147 // (WebStore, Drag&Drop). Treat the path as a virtual path. We will eventually | |
| 148 // determine whether this is a local path and if not, figure out a local path. | |
| 149 if (!is_forced_path) { | |
| 150 std::string default_filename( | |
| 151 l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME)); | |
| 152 base::FilePath generated_filename = net::GenerateFileName( | |
| 153 download_->GetURL(), | |
| 154 download_->GetContentDisposition(), | |
| 155 GetProfile()->GetPrefs()->GetString(prefs::kDefaultCharset), | |
| 156 download_->GetSuggestedFilename(), | |
| 157 download_->GetMimeType(), | |
| 158 default_filename); | |
| 159 should_prompt_ = ShouldPromptForDownload(generated_filename); | |
| 160 base::FilePath target_directory; | |
| 161 if (should_prompt_ && !last_selected_directory_.empty()) { | |
| 162 DCHECK(!download_prefs_->IsDownloadPathManaged()); | |
| 163 // If the user is going to be prompted and the user has been prompted | |
| 164 // before, then always prefer the last directory that the user selected. | |
| 165 target_directory = last_selected_directory_; | |
| 166 } else { | |
| 167 target_directory = download_prefs_->DownloadPath(); | |
| 168 } | |
| 169 virtual_path_ = target_directory.Append(generated_filename); | |
| 170 } else { | |
| 171 DCHECK(!should_prompt_); | |
| 172 virtual_path_ = download_->GetForcedFilePath(); | |
| 173 } | |
| 174 DCHECK(virtual_path_.IsAbsolute()); | |
| 175 DVLOG(20) << "Generated virtual path: " << virtual_path_.AsUTF8Unsafe(); | |
| 176 | |
| 177 // If the download is DOA, don't bother going any further. This would be the | |
| 178 // case for a download that failed to initialize (e.g. the initial temporary | |
| 179 // file couldn't be created because both the downloads directory and the | |
| 180 // temporary directory are unwriteable). | |
| 181 if (!download_->IsInProgress()) | |
|
benjhayden
2013/04/26 15:07:37
Why not do this before computing virtual_path_?
asanka
2013/04/26 17:02:21
The point is to be able to compute a suggested pat
benjhayden
2013/04/26 17:25:56
Can you comment that this check happens after comp
asanka
2013/04/29 18:43:19
Done.
| |
| 182 return COMPLETE; | |
| 183 return CONTINUE; | |
| 184 } | |
| 185 | |
| 186 DownloadTargetDeterminer::Result | |
| 187 DownloadTargetDeterminer::DoNotifyExtensions() { | |
| 188 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 189 DCHECK(!virtual_path_.empty()); | |
| 190 | |
| 191 next_state_ = STATE_RESERVE_VIRTUAL_PATH; | |
| 192 | |
| 193 // If the target path is forced or if we don't have an extensions event | |
| 194 // router, then proceed with the original path. | |
| 195 if (!download_->GetForcedFilePath().empty()) | |
| 196 return CONTINUE; | |
| 197 | |
| 198 delegate_->NotifyExtensions(download_, virtual_path_, | |
| 199 base::Bind(&DownloadTargetDeterminer::NotifyExtensionsDone, | |
| 200 weak_ptr_factory_.GetWeakPtr())); | |
| 201 return QUIT_DOLOOP; | |
| 202 } | |
| 203 | |
| 204 void DownloadTargetDeterminer::NotifyExtensionsDone( | |
| 205 const base::FilePath& suggested_path, | |
| 206 DownloadPathReservationTracker::FilenameConflictAction conflict_action) { | |
| 207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 208 DVLOG(20) << "Extension suggested path: " << suggested_path.AsUTF8Unsafe(); | |
| 209 | |
| 210 if (!suggested_path.empty()) { | |
| 211 // TODO(asanka, benjhayden): The suggested path may contain path fragments. | |
|
benjhayden
2013/04/26 15:07:37
Remove this TODO since I have a pending CL to hand
asanka
2013/04/26 17:02:21
Done.
| |
| 212 // We need to validate each component individually. http://crbug.com/181332. | |
| 213 | |
| 214 // If an extension overrides the filename, then the target directory will be | |
| 215 // forced to download_prefs_->DownloadPath() since extensions cannot place | |
| 216 // downloaded files anywhere except there. This prevents subdirectories from | |
| 217 // accumulating: if an extension is allowed to say that a file should go in | |
| 218 // last_download_path/music/foo.mp3, then last_download_path will accumulate | |
| 219 // the subdirectory /music/ so that the next download may end up in | |
| 220 // Downloads/music/music/music/bar.mp3. | |
| 221 base::FilePath new_path(download_prefs_->DownloadPath().Append( | |
| 222 suggested_path).NormalizePathSeparators()); | |
| 223 // Do not pass a mime type to GenerateSafeFileName so that it does not force | |
| 224 // the filename to have an extension if the (Chrome) extension does not | |
| 225 // suggest it. | |
| 226 net::GenerateSafeFileName(std::string(), false, &new_path); | |
| 227 virtual_path_ = new_path; | |
| 228 conflict_action_ = conflict_action; | |
| 229 } | |
| 230 | |
| 231 DoLoop(); | |
| 232 } | |
| 233 | |
| 234 DownloadTargetDeterminer::Result | |
| 235 DownloadTargetDeterminer::DoReserveVirtualPath() { | |
| 236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 237 DCHECK(!virtual_path_.empty()); | |
| 238 | |
| 239 next_state_ = STATE_PROMPT_USER_FOR_DOWNLOAD_PATH; | |
| 240 | |
| 241 delegate_->ReserveVirtualPath( | |
| 242 download_, virtual_path_, conflict_action_, | |
| 243 base::Bind(&DownloadTargetDeterminer::ReserveVirtualPathDone, | |
| 244 weak_ptr_factory_.GetWeakPtr())); | |
| 245 return QUIT_DOLOOP; | |
| 246 } | |
| 247 | |
| 248 void DownloadTargetDeterminer::ReserveVirtualPathDone( | |
| 249 const base::FilePath& path, bool verified) { | |
| 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 251 DVLOG(20) << "Reserved path: " << path.AsUTF8Unsafe() | |
| 252 << " Verified:" << verified; | |
| 253 should_prompt_ = (should_prompt_ || !verified); | |
| 254 if (verified) | |
| 255 virtual_path_ = path; | |
| 256 DoLoop(); | |
| 257 } | |
| 258 | |
| 259 DownloadTargetDeterminer::Result | |
| 260 DownloadTargetDeterminer::DoPromptUserForDownloadPath() { | |
| 261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 262 DCHECK(!virtual_path_.empty()); | |
| 263 | |
| 264 next_state_ = STATE_DETERMINE_LOCAL_PATH; | |
| 265 | |
| 266 if (should_prompt_) { | |
|
benjhayden
2013/04/26 15:07:37
Why doesn't ReserveVirtualPathDone() skip past STA
asanka
2013/04/26 17:02:21
This is roughly following the pattern elsewhere th
| |
| 267 delegate_->PromptUserForDownloadPath( | |
| 268 download_, | |
| 269 virtual_path_, | |
| 270 base::Bind(&DownloadTargetDeterminer::PromptUserForDownloadPathDone, | |
| 271 weak_ptr_factory_.GetWeakPtr())); | |
| 272 return QUIT_DOLOOP; | |
| 273 } | |
| 274 return CONTINUE; | |
| 275 } | |
| 276 | |
| 277 void DownloadTargetDeterminer::PromptUserForDownloadPathDone( | |
| 278 const base::FilePath& virtual_path) { | |
| 279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 280 DVLOG(20) << "User selected path:" << virtual_path.AsUTF8Unsafe(); | |
| 281 if (virtual_path.empty()) { | |
| 282 CancelOnFailureAndDeleteSelf(); | |
| 283 return; | |
| 284 } | |
| 285 virtual_path_ = virtual_path; | |
| 286 DoLoop(); | |
| 287 } | |
| 288 | |
| 289 DownloadTargetDeterminer::Result | |
| 290 DownloadTargetDeterminer::DoDetermineLocalPath() { | |
| 291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 292 DCHECK(!virtual_path_.empty()); | |
| 293 DCHECK(local_path_.empty()); | |
| 294 | |
| 295 next_state_ = STATE_CHECK_DOWNLOAD_URL; | |
| 296 | |
| 297 delegate_->DetermineLocalPath( | |
| 298 download_, | |
| 299 virtual_path_, | |
| 300 base::Bind(&DownloadTargetDeterminer::DetermineLocalPathDone, | |
| 301 weak_ptr_factory_.GetWeakPtr())); | |
| 302 return QUIT_DOLOOP; | |
| 303 } | |
| 304 | |
| 305 void DownloadTargetDeterminer::DetermineLocalPathDone( | |
| 306 const base::FilePath& local_path) { | |
| 307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 308 DVLOG(20) << "Local path: " << local_path.AsUTF8Unsafe(); | |
| 309 local_path_ = local_path; | |
|
benjhayden
2013/04/26 15:07:37
Why set virtual_path_ *after* maybe calling Cancel
asanka
2013/04/26 17:02:21
Done.
| |
| 310 if (local_path_.empty()) { | |
| 311 // Path subsitution failed. | |
| 312 CancelOnFailureAndDeleteSelf(); | |
| 313 return; | |
| 314 } | |
| 315 DoLoop(); | |
| 316 } | |
| 317 | |
| 318 DownloadTargetDeterminer::Result | |
| 319 DownloadTargetDeterminer::DoCheckDownloadUrl() { | |
| 320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 321 DCHECK(!virtual_path_.empty()); | |
| 322 next_state_ = STATE_CHECK_VISITED_REFERRER_BEFORE; | |
| 323 delegate_->CheckDownloadUrl( | |
| 324 download_, | |
| 325 virtual_path_, | |
| 326 base::Bind(&DownloadTargetDeterminer::CheckDownloadUrlDone, | |
| 327 weak_ptr_factory_.GetWeakPtr())); | |
| 328 return QUIT_DOLOOP; | |
| 329 } | |
| 330 | |
| 331 void DownloadTargetDeterminer::CheckDownloadUrlDone( | |
| 332 content::DownloadDangerType danger_type) { | |
| 333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 334 DVLOG(20) << "URL Check Result:" << danger_type; | |
| 335 danger_type_ = danger_type; | |
| 336 DoLoop(); | |
| 337 } | |
| 338 | |
| 339 DownloadTargetDeterminer::Result | |
| 340 DownloadTargetDeterminer::DoCheckVisitedReferrerBefore() { | |
| 341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 342 | |
| 343 next_state_ = STATE_DETERMINE_INTERMEDIATE_PATH; | |
| 344 | |
| 345 // Checking if there are prior visits to the referrer is only necessary if the | |
| 346 // danger level of the download depends on the file type. This excludes cases | |
| 347 // where the download has already been deemed dangerous, or where the user is | |
| 348 // going to be prompted or where this is a programmatic download. | |
| 349 if (danger_type_ != content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS || | |
|
benjhayden
2013/04/26 15:07:37
Again, why not move this into CheckDownloadUrlDone
asanka
2013/04/26 17:02:21
As above.
| |
| 350 should_prompt_ || | |
| 351 !download_->GetForcedFilePath().empty()) { | |
| 352 return CONTINUE; | |
| 353 } | |
| 354 | |
| 355 // Only ping the history DB if the download would be considered safe if there | |
| 356 // are prior visits and is considered dangerous otherwise. | |
| 357 if (!IsDangerousFile(VISITED_REFERRER) && | |
| 358 IsDangerousFile(NO_VISITS_TO_REFERRER)) { | |
| 359 // HistoryServiceFactory redirects incognito profiles to on-record profiles. | |
| 360 // There's no history for on-record profiles in unit_tests. | |
| 361 HistoryService* history_service = HistoryServiceFactory::GetForProfile( | |
| 362 GetProfile(), Profile::EXPLICIT_ACCESS); | |
| 363 | |
| 364 if (history_service && download_->GetReferrerUrl().is_valid()) { | |
| 365 history_service->GetVisibleVisitCountToHost( | |
|
benjhayden
2013/04/25 17:16:04
Would moving this out to the delegate be worth the
asanka
2013/04/25 19:08:26
The history service is currently testable. See the
benjhayden
2013/04/26 15:07:37
nm, this looks good.
| |
| 366 download_->GetReferrerUrl(), &history_consumer_, | |
| 367 base::Bind(&VisitCountsToVisitedBefore, base::Bind( | |
| 368 &DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone, | |
| 369 weak_ptr_factory_.GetWeakPtr()))); | |
| 370 return QUIT_DOLOOP; | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 // If the danger level doesn't depend on having visited the refererrer URL or | |
| 375 // if original profile doesn't have a HistoryService or the referrer url is | |
| 376 // invalid, then assume the referrer has not been visited before. | |
| 377 if (IsDangerousFile(NO_VISITS_TO_REFERRER)) | |
|
benjhayden
2013/04/26 15:07:37
Can you merge this with the previous if-block so t
asanka
2013/04/26 17:02:21
Done.
| |
| 378 danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE; | |
| 379 return CONTINUE; | |
| 380 } | |
| 381 | |
| 382 void DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone( | |
| 383 bool visited_referrer_before) { | |
| 384 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 385 if (IsDangerousFile( | |
| 386 visited_referrer_before ? VISITED_REFERRER : NO_VISITS_TO_REFERRER)) | |
| 387 danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE; | |
| 388 DoLoop(); | |
| 389 } | |
| 390 | |
| 391 DownloadTargetDeterminer::Result | |
| 392 DownloadTargetDeterminer::DoDetermineIntermediatePath() { | |
|
benjhayden
2013/04/26 15:07:37
Should this line be indented like the others like
asanka
2013/04/26 17:02:21
Done.
| |
| 393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 394 DCHECK(!virtual_path_.empty()); | |
| 395 DCHECK(!local_path_.empty()); | |
| 396 DCHECK(intermediate_path_.empty()); | |
| 397 | |
| 398 next_state_ = STATE_NONE; | |
| 399 | |
| 400 if (virtual_path_.BaseName() != local_path_.BaseName() || | |
| 401 (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS && | |
| 402 !download_->GetForcedFilePath().empty())) { | |
| 403 // If the actual target of the download is a virtual path, then the local | |
| 404 // path is considered to point to a temporary path. A separate intermediate | |
| 405 // path is unnecessary since the local path already serves that purpose. | |
| 406 // | |
| 407 // Also, if the download has a forced path and is safe, then just use the | |
| 408 // target path. | |
| 409 intermediate_path_ = local_path_; | |
| 410 } else if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) { | |
| 411 // If the download is not dangerous, just append .crdownload to the target | |
| 412 // path. | |
| 413 intermediate_path_ = download_util::GetCrDownloadPath(local_path_); | |
| 414 } else { | |
| 415 // If the download is potentially dangerous we create a filename of the form | |
| 416 // 'Unconfirmed <random>.crdownload'. | |
| 417 base::FilePath::StringType file_name; | |
| 418 base::FilePath dir = local_path_.DirName(); | |
| 419 #if defined(OS_WIN) | |
| 420 string16 unconfirmed_prefix = | |
| 421 l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX); | |
| 422 #else | |
| 423 std::string unconfirmed_prefix = | |
| 424 l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX); | |
| 425 #endif | |
| 426 base::SStringPrintf( | |
| 427 &file_name, | |
| 428 unconfirmed_prefix.append( | |
| 429 FILE_PATH_LITERAL(" %d.crdownload")).c_str(), | |
|
benjhayden
2013/04/26 15:07:37
Should that string be a kConstant?
asanka
2013/04/26 17:02:21
Done. I was just moving code around, but personall
| |
| 430 base::RandInt(0, 1000000)); | |
|
benjhayden
2013/04/26 15:07:37
kConstant = 1000000?
asanka
2013/04/26 17:02:21
Done.
| |
| 431 intermediate_path_ = dir.Append(file_name); | |
| 432 } | |
| 433 | |
| 434 return COMPLETE; | |
| 435 } | |
| 436 | |
| 437 void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf() { | |
| 438 DCHECK(download_); | |
| 439 DVLOG(20) << "Scheduling callback. Virtual:" << virtual_path_.AsUTF8Unsafe() | |
| 440 << " Local:" << local_path_.AsUTF8Unsafe() | |
| 441 << " Intermediate:" << intermediate_path_.AsUTF8Unsafe() | |
| 442 << " Should prompt:" << should_prompt_ | |
| 443 << " Danger type:" << danger_type_; | |
| 444 MessageLoop::current()->PostTask( | |
| 445 FROM_HERE, | |
| 446 base::Bind(completion_callback_, | |
| 447 local_path_, | |
| 448 (should_prompt_ ? DownloadItem::TARGET_DISPOSITION_PROMPT : | |
| 449 DownloadItem::TARGET_DISPOSITION_OVERWRITE), | |
| 450 danger_type_, | |
| 451 intermediate_path_)); | |
| 452 completion_callback_.Reset(); | |
| 453 delete this; | |
| 454 } | |
| 455 | |
| 456 void DownloadTargetDeterminer::CancelOnFailureAndDeleteSelf() { | |
| 457 // Path substitution failed. | |
| 458 virtual_path_.clear(); | |
| 459 local_path_.clear(); | |
| 460 intermediate_path_.clear(); | |
| 461 ScheduleCallbackAndDeleteSelf(); | |
| 462 } | |
| 463 | |
| 464 Profile* DownloadTargetDeterminer::GetProfile() { | |
| 465 DCHECK(download_->GetBrowserContext()); | |
| 466 return Profile::FromBrowserContext(download_->GetBrowserContext()); | |
| 467 } | |
| 468 | |
| 469 bool DownloadTargetDeterminer::ShouldPromptForDownload( | |
| 470 const base::FilePath& filename) { | |
| 471 // If the download path is forced, don't prompt. | |
| 472 if (!download_->GetForcedFilePath().empty()) { | |
| 473 // 'Save As' downloads shouldn't have a forced path. | |
| 474 DCHECK_NE(DownloadItem::TARGET_DISPOSITION_PROMPT, | |
| 475 download_->GetTargetDisposition()); | |
| 476 return false; | |
| 477 } | |
| 478 | |
| 479 // Don't ask where to save if the download path is managed. Even if the user | |
| 480 // wanted to be prompted for "all" downloads, or if this was a 'Save As' | |
| 481 // download. | |
| 482 if (download_prefs_->IsDownloadPathManaged()) | |
| 483 return false; | |
| 484 | |
| 485 // Prompt if this is a 'Save As' download. | |
| 486 if (download_->GetTargetDisposition() == | |
| 487 DownloadItem::TARGET_DISPOSITION_PROMPT) | |
| 488 return true; | |
| 489 | |
| 490 // Check if the user has the "Always prompt for download location" preference | |
| 491 // set. If so we prompt for most downloads except for the following scenarios: | |
| 492 // 1) Extension installation. Note that we only care here about the case where | |
| 493 // an extension is installed, not when one is downloaded with "save as...". | |
| 494 // 2) Filetypes marked "always open." If the user just wants this file opened, | |
| 495 // don't bother asking where to keep it. | |
| 496 if (download_prefs_->PromptForDownload() && | |
| 497 !download_crx_util::IsExtensionDownload(*download_) && | |
| 498 !extensions::Extension::IsExtension(filename) && | |
| 499 !download_prefs_->IsAutoOpenEnabledBasedOnExtension(filename)) | |
| 500 return true; | |
| 501 | |
| 502 // Otherwise, don't prompt. | |
|
benjhayden
2013/04/26 15:07:37
Can you comment that we still could decide to prom
asanka
2013/04/26 17:02:21
Done.
| |
| 503 return false; | |
| 504 } | |
| 505 | |
| 506 bool DownloadTargetDeterminer::IsDangerousFile(PriorVisitsToReferrer visits) { | |
| 507 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 508 const bool is_extension_download = | |
| 509 download_crx_util::IsExtensionDownload(*download_); | |
| 510 | |
| 511 // User-initiated extension downloads from pref-whitelisted sources are not | |
| 512 // considered dangerous. | |
| 513 if (download_->HasUserGesture() && | |
| 514 is_extension_download && | |
| 515 download_crx_util::OffStoreInstallAllowedByPrefs( | |
| 516 GetProfile(), *download_)) { | |
| 517 return false; | |
| 518 } | |
| 519 | |
| 520 // Extensions that are not from the gallery are considered dangerous. | |
| 521 // When off-store install is disabled we skip this, since in this case, we | |
| 522 // will not offer to install the extension. | |
| 523 if (extensions::FeatureSwitch::easy_off_store_install()->IsEnabled() && | |
| 524 is_extension_download && | |
| 525 !extensions::WebstoreInstaller::GetAssociatedApproval(*download_)) { | |
| 526 return true; | |
| 527 } | |
| 528 | |
| 529 // Anything the user has marked auto-open is OK if it's user-initiated. | |
| 530 if (download_prefs_->IsAutoOpenEnabledBasedOnExtension(virtual_path_) && | |
| 531 download_->HasUserGesture()) | |
| 532 return false; | |
| 533 | |
| 534 switch (download_util::GetFileDangerLevel(virtual_path_.BaseName())) { | |
| 535 case download_util::NotDangerous: | |
|
benjhayden
2013/04/26 15:07:37
These enums look funny. Is this the right CL to fi
asanka
2013/04/26 17:02:21
I'll do a separate CL to fix them.
| |
| 536 return false; | |
| 537 | |
| 538 case download_util::AllowOnUserGesture: | |
| 539 // "Allow on user gesture" is OK when we have a user gesture and the | |
| 540 // hosting page has been visited before today. | |
| 541 if (download_->GetTransitionType() & | |
| 542 content::PAGE_TRANSITION_FROM_ADDRESS_BAR) { | |
| 543 return false; | |
| 544 } | |
| 545 return !download_->HasUserGesture() || visits == NO_VISITS_TO_REFERRER; | |
| 546 | |
| 547 case download_util::Dangerous: | |
| 548 return true; | |
| 549 } | |
| 550 NOTREACHED(); | |
| 551 return false; | |
| 552 } | |
| 553 | |
| 554 void DownloadTargetDeterminer::OnDownloadDestroyed( | |
| 555 DownloadItem* download) { | |
| 556 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 557 DCHECK_EQ(download_, download); | |
| 558 CancelOnFailureAndDeleteSelf(); | |
| 559 } | |
| 560 | |
| 561 // static | |
| 562 void DownloadTargetDeterminer::Start( | |
| 563 content::DownloadItem* download, | |
| 564 DownloadPrefs* download_prefs, | |
| 565 const base::FilePath& last_selected_directory, | |
| 566 DownloadTargetDeterminerDelegate* delegate, | |
| 567 const content::DownloadTargetCallback& callback) { | |
| 568 // DownloadTargetDeterminer owns itself and will self destruct when the job is | |
| 569 // complete or the download item is destroyed. The callback is always invoked | |
| 570 // asynchronously. | |
| 571 new DownloadTargetDeterminer(download, download_prefs, | |
| 572 last_selected_directory, delegate, callback); | |
| 573 } | |
| OLD | NEW |