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

Unified Diff: chrome/browser/download/download_target_determiner.cc

Issue 12850002: Move download filename determintion into a separate class. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 8 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/download/download_target_determiner.cc
diff --git a/chrome/browser/download/download_target_determiner.cc b/chrome/browser/download/download_target_determiner.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3df4e43151473900361f36d87d174e37c8a6cd23
--- /dev/null
+++ b/chrome/browser/download/download_target_determiner.cc
@@ -0,0 +1,627 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/download/download_target_determiner.h"
+
+#include "base/prefs/pref_service.h"
+#include "base/rand_util.h"
+#include "base/stringprintf.h"
+#include "base/time.h"
+#include "chrome/browser/download/chrome_download_manager_delegate.h"
+#include "chrome/browser/download/download_crx_util.h"
+#include "chrome/browser/download/download_extensions.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/download/download_util.h"
+#include "chrome/browser/extensions/api/downloads/downloads_api.h"
+#include "chrome/browser/extensions/webstore_installer.h"
+#include "chrome/browser/history/history_service.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/safe_browsing/download_protection_service.h"
+#include "chrome/common/extensions/feature_switch.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "grit/generated_resources.h"
+#include "net/base/net_util.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/drive/drive_download_handler.h"
+#include "chrome/browser/chromeos/drive/drive_file_system_util.h"
+#endif
+
+using content::BrowserThread;
+using content::DownloadItem;
+using safe_browsing::DownloadProtectionService;
+
+namespace {
+
+// Condenses the results from HistoryService::GetVisibleVisitCountToHost() to a
+// single bool. A host is considered visited before if prior visible vists were
+// found in history and the first such visit was earlier than the most recent
+// midnight.
+void VisitCountsToVisitedBefore(
+ const base::Callback<void(bool)>& callback,
+ HistoryService::Handle unused_handle,
+ bool found_visits,
+ int count,
+ base::Time first_visit) {
+ callback.Run(
+ found_visits && count > 0 &&
+ (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight()));
+}
+
+} // namespace
+
+DownloadTargetDeterminerDelegate::~DownloadTargetDeterminerDelegate() {
+}
+
+DownloadTargetDeterminer::DownloadTargetDeterminer(
+ DownloadItem* download,
+ DownloadPrefs* download_prefs,
+ const base::FilePath& last_selected_directory,
+ DownloadTargetDeterminerDelegate* delegate,
+ const CompletionCallback& callback)
+ : next_state_(STATE_GENERATE_TARGET_PATH),
+ should_prompt_(false),
+ should_overwrite_(!download->GetForcedFilePath().empty()),
+ danger_type_(download->GetDangerType()),
+ download_(download),
+ download_prefs_(download_prefs),
+ delegate_(delegate),
+ last_selected_directory_(last_selected_directory),
+ completion_callback_(callback),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(download_);
+ DCHECK(delegate);
+ download_->AddObserver(this);
+
+ DoLoop();
+}
+
+DownloadTargetDeterminer::~DownloadTargetDeterminer() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(download_);
+ DCHECK(completion_callback_.is_null());
+ download_->RemoveObserver(this);
+}
+
+void DownloadTargetDeterminer::DoLoop() {
+ Result result = CONTINUE;
+
+ do {
+ switch (next_state_) {
+ case STATE_GENERATE_TARGET_PATH:
+ result = DoGenerateTargetPath();
+ break;
+ case STATE_NOTIFY_EXTENSIONS:
+ result = DoNotifyExtensions();
+ break;
+ case STATE_RESERVE_VIRTUAL_PATH:
+ result = DoReserveVirtualPath();
+ break;
+ case STATE_PROMPT_USER_FOR_DOWNLOAD_PATH:
+ result = DoPromptUserForDownloadPath();
+ break;
+ case STATE_DETERMINE_LOCAL_PATH:
+ result = DoDetermineLocalPath();
+ break;
+ case STATE_CHECK_DOWNLOAD_URL:
+ result = DoCheckDownloadUrl();
+ break;
+ case STATE_DETERMINE_DANGER_TYPE:
+ result = DoDetermineDangerType();
+ break;
+ case STATE_DETERMINE_INTERMEDIATE_PATH:
+ result = DoDetermineIntermediatePath();
+ break;
+ case STATE_CHECK_VISITED_REFERRER_BEFORE:
+ result = DoCheckVisitedReferrerBefore();
+ break;
+ case STATE_NONE:
+ NOTREACHED();
+ break;
+ }
+ } while (result == CONTINUE && next_state_ != STATE_NONE);
+
+ if (result == COMPLETE)
+ ScheduleCallbackAndDeleteSelf();
+}
+
+DownloadTargetDeterminer::Result
+ DownloadTargetDeterminer::DoGenerateTargetPath() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(virtual_path_.empty());
+ DCHECK(local_path_.empty());
+ bool is_forced_path = !download_->GetForcedFilePath().empty();
+ base::FilePath suggested_path;
+
+ next_state_ = STATE_NOTIFY_EXTENSIONS;
+
+ // If we don't have a forced path, we should construct a path for the
+ // download. Forced paths are only specified for programmatic downloads
+ // (WebStore, Drag&Drop).
+ if (!is_forced_path) {
+ std::string default_filename(
+ l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
+ base::FilePath generated_filename = net::GenerateFileName(
+ download_->GetURL(),
+ download_->GetContentDisposition(),
+ GetProfile()->GetPrefs()->GetString(prefs::kDefaultCharset),
+ download_->GetSuggestedFilename(),
+ download_->GetMimeType(),
+ default_filename);
+ should_prompt_ = ShouldPromptForDownload(generated_filename);
+ base::FilePath target_directory;
+ if (should_prompt_ && !last_selected_directory_.empty()) {
+ DCHECK(!download_prefs_->IsDownloadPathManaged());
+ // If the user is going to be prompted and the user has been prompted
+ // before, then always prefer the last directory that the user selected.
+ target_directory = last_selected_directory_;
+ } else {
+ target_directory = download_prefs_->DownloadPath();
+ }
+ suggested_path = target_directory.Append(generated_filename);
+ } else {
+ DCHECK(!should_prompt_);
+ suggested_path = download_->GetForcedFilePath();
+ }
+ DCHECK(suggested_path.IsAbsolute());
+
+ // Treat the suggested_path as a virtual path. We will eventually determine
+ // whether this is a local path and if not, figure out a local path.
+ virtual_path_ = suggested_path;
benjhayden 2013/04/09 15:46:32 Why not assign directly to virtual_path_ above ins
asanka 2013/04/16 20:34:01 Done.
+ DVLOG(20) << "Generated virtual path: " << virtual_path_.AsUTF8Unsafe();
+
+ // If the download is DOA, don't bother going any further. This would be the
+ // case for a download that failed to initialize (e.g. the initial temporary
+ // file couldn't be created because both the downloads directory and the
+ // temporary directory are unwriteable).
+ if (!download_->IsInProgress())
+ return COMPLETE;
+ return CONTINUE;
+}
+
+DownloadTargetDeterminer::Result
+ DownloadTargetDeterminer::DoNotifyExtensions() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!virtual_path_.empty());
+ ExtensionDownloadsEventRouter* router = delegate_->GetExtensionEventRouter();
+
+ next_state_ = STATE_RESERVE_VIRTUAL_PATH;
+
+ // If the target path is forced or if we don't have an extensions event
+ // router, then proceed with the original path.
+ if (!download_->GetForcedFilePath().empty() || !router)
+ return CONTINUE;
+
+ base::Closure original_path_callback =
+ base::Bind(&DownloadTargetDeterminer::NotifyExtensionsDone,
+ weak_ptr_factory_.GetWeakPtr(), base::FilePath(), false);
+ ExtensionDownloadsEventRouter::FilenameChangedCallback override_callback =
+ base::Bind(&DownloadTargetDeterminer::NotifyExtensionsDone,
+ weak_ptr_factory_.GetWeakPtr());
+ router->OnDeterminingFilename(download_, virtual_path_.BaseName(),
+ original_path_callback,
+ override_callback);
+ return PENDING;
+}
+
+void DownloadTargetDeterminer::NotifyExtensionsDone(
+ const base::FilePath& suggested_path,
+ bool should_overwrite) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DVLOG(20) << "Extension suggested path: " << suggested_path.AsUTF8Unsafe();
+
+ if (!suggested_path.empty()) {
+ // TODO(asanka, benjhayden): The suggested path may contain path fragments.
+ // We need to validate each component individually. http://crbug.com/181332.
+
+ // If an extension overrides the filename, then the target directory will be
+ // forced to download_prefs_->DownloadPath() since extensions cannot place
+ // downloaded files anywhere except there. This prevents subdirectories from
+ // accumulating: if an extension is allowed to say that a file should go in
+ // last_download_path/music/foo.mp3, then last_download_path will accumulate
+ // the subdirectory /music/ so that the next download may end up in
+ // Downloads/music/music/music/bar.mp3.
+ base::FilePath new_path(download_prefs_->DownloadPath().Append(
+ suggested_path).NormalizePathSeparators());
+ // Do not pass a mime type to GenerateSafeFileName so that it does not force
+ // the filename to have an extension if the (Chrome) extension does not
+ // suggest it.
+ net::GenerateSafeFileName(std::string(), false, &new_path);
+ virtual_path_ = new_path;
+
+ // If |is_forced_path| were true, then extensions would not have been
+ // consulted, so use |overwrite| instead of |is_forced_path|. This does NOT
+ // set DownloadItem::GetForcedFilePath()!
+ should_overwrite_ = should_overwrite;
+ }
+
+ DoLoop();
+}
+
+DownloadTargetDeterminer::Result
+ DownloadTargetDeterminer::DoReserveVirtualPath() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!virtual_path_.empty());
+
+ next_state_ = STATE_PROMPT_USER_FOR_DOWNLOAD_PATH;
+
+ delegate_->ReserveVirtualPath(
+ download_, virtual_path_, !should_overwrite_,
+ base::Bind(&DownloadTargetDeterminer::ReserveVirtualPathDone,
+ weak_ptr_factory_.GetWeakPtr()));
+ return PENDING;
+}
+
+void DownloadTargetDeterminer::ReserveVirtualPathDone(
+ const base::FilePath& path, bool verified) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DVLOG(20) << "Reserved path: " << path.AsUTF8Unsafe()
+ << " Verified:" << verified;
+ should_prompt_ = (should_prompt_ || !verified);
+ if (verified)
+ virtual_path_ = path;
+ DoLoop();
+}
+
+DownloadTargetDeterminer::Result
+ DownloadTargetDeterminer::DoPromptUserForDownloadPath() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!virtual_path_.empty());
+
+ // We also prompt if the download path couldn't be verified.
+ if (should_prompt_) {
+ // If we are prompting, then delegate_->PromptUserForDownloadPathDone()
+ // returns both a local and virtual path, so we don't need to determine a
+ // local path in that case.
+ next_state_ = STATE_CHECK_DOWNLOAD_URL;
+ delegate_->PromptUserForDownloadPath(
+ download_,
+ virtual_path_,
+ base::Bind(&DownloadTargetDeterminer::PromptUserForDownloadPathDone,
+ weak_ptr_factory_.GetWeakPtr()));
+ return PENDING;
+ }
+
+ next_state_ = STATE_DETERMINE_LOCAL_PATH;
+ return CONTINUE;
+}
+
+void DownloadTargetDeterminer::PromptUserForDownloadPathDone(
+ const base::FilePath& virtual_path,
+ const base::FilePath& local_path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DVLOG(20) << "User selected paths (virtual):" << virtual_path.AsUTF8Unsafe()
+ << " (local):" << local_path.AsUTF8Unsafe();
+ if (virtual_path.empty()) {
+ CancelOnFailureAndDeleteSelf();
+ return;
+ }
+ virtual_path_ = virtual_path;
+ local_path_ = local_path;
+
+ DCHECK_EQ(STATE_CHECK_DOWNLOAD_URL, next_state_);
+ DoLoop();
+}
+
+DownloadTargetDeterminer::Result
+ DownloadTargetDeterminer::DoDetermineLocalPath() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!virtual_path_.empty());
+ DCHECK(local_path_.empty());
+
+ next_state_ = STATE_CHECK_DOWNLOAD_URL;
+
+ delegate_->DetermineLocalPath(
+ download_,
+ virtual_path_,
+ base::Bind(&DownloadTargetDeterminer::DetermineLocalPathDone,
+ weak_ptr_factory_.GetWeakPtr()));
+ return PENDING;
+}
+
+void DownloadTargetDeterminer::DetermineLocalPathDone(
+ const base::FilePath& local_path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DVLOG(20) << "Local path: " << local_path.AsUTF8Unsafe();
+ local_path_ = local_path;
+ if (local_path_.empty()) {
+ // Path subsitution failed.
+ CancelOnFailureAndDeleteSelf();
+ return;
+ }
+ DoLoop();
+}
+
+DownloadTargetDeterminer::Result
+ DownloadTargetDeterminer::DoCheckDownloadUrl() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ next_state_ = STATE_DETERMINE_DANGER_TYPE;
+
+#if defined(FULL_SAFE_BROWSING)
+ safe_browsing::DownloadProtectionService* service =
+ delegate_->GetDownloadProtectionService();
+ if (service) {
+ VLOG(2) << __FUNCTION__ << "() Start SB URL check for download = "
+ << download_->DebugString(false);
+ service->CheckDownloadUrl(
+ *download_,
+ base::Bind(
+ &DownloadTargetDeterminer::CheckDownloadUrlDone,
+ weak_ptr_factory_.GetWeakPtr()));
+ return PENDING;
+ }
+#endif
+ return CONTINUE;
+}
+
+void DownloadTargetDeterminer::CheckDownloadUrlDone(
+ DownloadProtectionService::DownloadCheckResult result) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DVLOG(20) << "URL Check Result:" << result;
+ if (result != DownloadProtectionService::SAFE)
+ danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL;
+ DoLoop();
+}
+
+DownloadTargetDeterminer::Result
+ DownloadTargetDeterminer::DoDetermineDangerType() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!virtual_path_.empty());
+
+ next_state_ = STATE_DETERMINE_INTERMEDIATE_PATH;
+
+#if defined(FULL_SAFE_BROWSING)
+ // If the download hasn't already been marked dangerous (could be
+ // DANGEROUS_URL), check if it is a dangerous file.
+ if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) {
+ // If this type of files is handled by the enhanced SafeBrowsing download
+ // protection, mark it as potentially dangerous content until we are done
+ // with scanning it.
+ safe_browsing::DownloadProtectionService* service =
+ delegate_->GetDownloadProtectionService();
+ if (service && service->IsSupportedDownload(*download_, virtual_path_)) {
+ // TODO(noelutz): if the user changes the extension name in the UI to
+ // something like .exe SafeBrowsing will currently *not* check if the
+ // download is malicious.
+ danger_type_ = content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT;
+ }
+ } else {
+ DCHECK_EQ(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, danger_type_);
+ }
+#endif
+
+ if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS &&
+ !should_prompt_ &&
+ download_->GetForcedFilePath().empty()) {
+ next_state_ = STATE_CHECK_VISITED_REFERRER_BEFORE;
+ }
+ return CONTINUE;
+}
+
+DownloadTargetDeterminer::Result
+ DownloadTargetDeterminer::DoCheckVisitedReferrerBefore() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ next_state_ = STATE_DETERMINE_INTERMEDIATE_PATH;
+
+ // HistoryServiceFactory redirects incognito profiles to on-record profiles.
+ HistoryService* history_service = HistoryServiceFactory::GetForProfile(
+ GetProfile(), Profile::EXPLICIT_ACCESS);
+
+ if (history_service && download_->GetReferrerUrl().is_valid()) {
+ history_service->GetVisibleVisitCountToHost(
+ download_->GetReferrerUrl(), &history_consumer_,
+ base::Bind(&VisitCountsToVisitedBefore, base::Bind(
+ &DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone,
+ weak_ptr_factory_.GetWeakPtr())));
+ return PENDING;
+ }
+
+ // If the original profile doesn't have a HistoryService or the referrer url
+ // is invalid, then give up and assume the referrer has not been visited
+ // before. There's no history for on-record profiles in unit_tests, for
+ // example.
+ if (IsDangerousFile(false))
+ danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
+ return CONTINUE;
+}
+
+void DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone(
+ bool visited_referrer_before) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (IsDangerousFile(visited_referrer_before))
+ danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
+ DoLoop();
+}
+
+DownloadTargetDeterminer::Result
+ DownloadTargetDeterminer::DoDetermineIntermediatePath() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!virtual_path_.empty());
+ DCHECK(!local_path_.empty());
+ DCHECK(intermediate_path_.empty());
+
+ next_state_ = STATE_NONE;
+
+ if (virtual_path_.BaseName() != local_path_.BaseName() ||
+ (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS &&
+ !download_->GetForcedFilePath().empty())) {
+ // If the actual target of the download is a virtual path, then the local
+ // path is considered to point to a temporary path. A separate intermediate
+ // path is unnecessary since the local path already serves that purpose.
+ //
+ // Also, if the download has a forced path and is safe, then just use the
+ // target path.
+ intermediate_path_ = local_path_;
+ } else if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) {
+ // If the download is not dangerous, just append .crdownload to the target
+ // path.
+ intermediate_path_ = download_util::GetCrDownloadPath(local_path_);
+ } else {
+ // If the download is potentially dangerous we create a filename of the form
+ // 'Unconfirmed <random>.crdownload'.
+ base::FilePath::StringType file_name;
+ base::FilePath dir = local_path_.DirName();
+#if defined(OS_WIN)
+ string16 unconfirmed_prefix =
+ l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
+#else
+ std::string unconfirmed_prefix =
+ l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
+#endif
+ base::SStringPrintf(
+ &file_name,
+ unconfirmed_prefix.append(
+ FILE_PATH_LITERAL(" %d.crdownload")).c_str(),
+ base::RandInt(0, 1000000));
+ intermediate_path_ = dir.Append(file_name);
+ }
+
+ return COMPLETE;
+}
+
+void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf() {
+ DCHECK(download_);
+ DVLOG(20) << "Scheduling callback. Virtual:" << virtual_path_.AsUTF8Unsafe()
+ << " Local:" << local_path_.AsUTF8Unsafe()
+ << " Intermediate:" << intermediate_path_.AsUTF8Unsafe()
+ << " Should prompt:" << should_prompt_
+ << " Danger type:" << danger_type_;
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(completion_callback_,
+ virtual_path_,
+ local_path_,
+ intermediate_path_,
+ (should_prompt_ ? DownloadItem::TARGET_DISPOSITION_PROMPT :
+ DownloadItem::TARGET_DISPOSITION_OVERWRITE),
+ danger_type_));
+ completion_callback_.Reset();
+ delete this;
+}
+
+void DownloadTargetDeterminer::CancelOnFailureAndDeleteSelf() {
+ // Path substitution failed.
+ virtual_path_.clear();
+ local_path_.clear();
+ intermediate_path_.clear();
+ ScheduleCallbackAndDeleteSelf();
+}
+
+Profile* DownloadTargetDeterminer::GetProfile() {
+ DCHECK(download_->GetBrowserContext());
+ return Profile::FromBrowserContext(download_->GetBrowserContext());
+}
+
+bool DownloadTargetDeterminer::ShouldPromptForDownload(
+ const base::FilePath& filename) {
+ // If the download path is forced, don't prompt.
+ if (!download_->GetForcedFilePath().empty()) {
+ // 'Save As' downloads shouldn't have a forced path.
+ DCHECK_NE(DownloadItem::TARGET_DISPOSITION_PROMPT,
+ download_->GetTargetDisposition());
+ return false;
+ }
+
+ // Don't ask where to save if the download path is managed. Even if the user
+ // wanted to be prompted for "all" downloads, or if this was a 'Save As'
benjhayden 2013/04/09 15:46:32 What if the user wants to change the basename?
asanka 2013/04/16 20:34:01 I don't intend to change the semantics of managed
+ // download.
+ if (download_prefs_->IsDownloadPathManaged())
+ return false;
+
+ // Prompt if this is a 'Save As' download.
+ if (download_->GetTargetDisposition() ==
+ DownloadItem::TARGET_DISPOSITION_PROMPT)
+ return true;
+
+ // Check if the user has the "Always prompt for download location" preference
+ // set. If so we prompt for most downloads except for the following scenarios:
+ // 1) Extension installation. Note that we only care here about the case where
+ // an extension is installed, not when one is downloaded with "save as...".
benjhayden 2013/04/09 15:46:32 Won't L552 prevent prompting if a user Saves As a
asanka 2013/04/16 20:34:01 Save As downloads have TARGET_DISPOSITION_PROMPT s
+ // 2) Filetypes marked "always open." If the user just wants this file opened,
+ // don't bother asking where to keep it.
+ if (download_prefs_->PromptForDownload() &&
+ !download_crx_util::IsExtensionDownload(*download_) &&
+ !extensions::Extension::IsExtension(filename) &&
+ !download_prefs_->IsAutoOpenEnabledBasedOnExtension(filename))
+ return true;
+
+ // Otherwise, don't prompt.
+ return false;
+}
+
+bool DownloadTargetDeterminer::IsDangerousFile(bool visited_referrer_before) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ const bool is_extension_download =
+ download_crx_util::IsExtensionDownload(*download_);
+
+ // User-initiated extension downloads from pref-whitelisted sources are not
+ // considered dangerous.
+ if (download_->HasUserGesture() &&
+ is_extension_download &&
+ download_crx_util::OffStoreInstallAllowedByPrefs(
+ GetProfile(), *download_)) {
+ return false;
+ }
+
+ // Extensions that are not from the gallery are considered dangerous.
+ // When off-store install is disabled we skip this, since in this case, we
+ // will not offer to install the extension.
+ if (extensions::FeatureSwitch::easy_off_store_install()->IsEnabled() &&
+ is_extension_download &&
+ !extensions::WebstoreInstaller::GetAssociatedApproval(*download_)) {
+ return true;
+ }
+
+ // Anything the user has marked auto-open is OK if it's user-initiated.
+ if (download_prefs_->IsAutoOpenEnabledBasedOnExtension(virtual_path_) &&
+ download_->HasUserGesture())
+ return false;
+
+ switch (download_util::GetFileDangerLevel(virtual_path_.BaseName())) {
+ case download_util::NotDangerous:
+ return false;
+
+ case download_util::AllowOnUserGesture:
+ // "Allow on user gesture" is OK when we have a user gesture and the
+ // hosting page has been visited before today.
+ if (download_->GetTransitionType() &
+ content::PAGE_TRANSITION_FROM_ADDRESS_BAR) {
+ return false;
+ }
+ return !download_->HasUserGesture() || !visited_referrer_before;
+
+ case download_util::Dangerous:
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+void DownloadTargetDeterminer::OnDownloadDestroyed(
+ DownloadItem* download) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_EQ(download_, download);
+ CancelOnFailureAndDeleteSelf();
+}
+
+// static
+void DownloadTargetDeterminer::Start(
+ content::DownloadItem* download,
+ DownloadPrefs* download_prefs,
+ const base::FilePath& last_selected_directory,
+ DownloadTargetDeterminerDelegate* delegate,
+ const CompletionCallback& callback) {
+ // DownloadTargetDeterminer owns itself and will self destruct when the job is
+ // complete or the download item is destroyed. The callback is always invoked
+ // asynchronously.
+ new DownloadTargetDeterminer(download, download_prefs,
+ last_selected_directory, delegate, callback);
+}

Powered by Google App Engine
This is Rietveld 408576698