blob: daa65fc15b4c0cf52fc543a5097484e47adda55e [file] [log] [blame]
// Copyright (c) 2012 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/chrome_download_manager_delegate.h"
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/file_util.h"
#include "base/rand_util.h"
#include "base/stringprintf.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/api/prefs/pref_member.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_completion_blocker.h"
#include "chrome/browser/download/download_crx_util.h"
#include "chrome/browser/download/download_extensions.h"
#include "chrome/browser/download/download_file_picker.h"
#include "chrome/browser/download/download_history.h"
#include "chrome/browser/download/download_path_reservation_tracker.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/download_status_updater.h"
#include "chrome/browser/download/download_util.h"
#include "chrome/browser/download/save_package_file_picker.h"
#include "chrome/browser/extensions/api/downloads/downloads_api.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/intents/web_intents_util.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/extensions/extension_switch_utils.h"
#include "chrome/common/extensions/user_script.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/download_item.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_intents_dispatcher.h"
#include "grit/generated_resources.h"
#include "net/base/net_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "webkit/glue/web_intent_data.h"
#include "webkit/glue/web_intent_reply_data.h"
#if !defined(OS_ANDROID)
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#endif
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/gdata/drive_download_observer.h"
#include "chrome/browser/chromeos/gdata/drive_file_system_util.h"
#include "chrome/browser/download/download_file_picker_chromeos.h"
#include "chrome/browser/download/save_package_file_picker_chromeos.h"
#endif
using content::BrowserContext;
using content::BrowserThread;
using content::DownloadId;
using content::DownloadItem;
using content::DownloadManager;
using content::WebContents;
using safe_browsing::DownloadProtectionService;
namespace {
// String pointer used for identifying safebrowing data associated with
// a download item.
static const char safe_browsing_id[] = "Safe Browsing ID";
// String pointer used to set the local file extension to be used when a
// download is going to be dispatched with web intents.
static const FilePath::CharType kWebIntentsFileExtension[] =
FILE_PATH_LITERAL(".webintents");
// The state of a safebrowsing check.
class SafeBrowsingState : public DownloadCompletionBlocker {
public:
SafeBrowsingState()
: verdict_(DownloadProtectionService::SAFE) {
}
virtual ~SafeBrowsingState();
// The verdict that we got from calling CheckClientDownload. Only valid to
// call if |is_complete()|.
DownloadProtectionService::DownloadCheckResult verdict() const {
return verdict_;
}
void SetVerdict(DownloadProtectionService::DownloadCheckResult result) {
verdict_ = result;
CompleteDownload();
}
private:
DownloadProtectionService::DownloadCheckResult verdict_;
DISALLOW_COPY_AND_ASSIGN(SafeBrowsingState);
};
SafeBrowsingState::~SafeBrowsingState() {}
// Generate a filename based on the response from the server. Similar
// in operation to net::GenerateFileName(), but uses a localized
// default name.
void GenerateFileNameFromRequest(const DownloadItem& download_item,
FilePath* generated_name) {
std::string default_file_name(
l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
*generated_name = net::GenerateFileName(download_item.GetURL(),
download_item.GetContentDisposition(),
download_item.GetReferrerCharset(),
download_item.GetSuggestedFilename(),
download_item.GetMimeType(),
default_file_name);
}
// Needed to give PostTask a void closure in OnWebIntentDispatchCompleted.
void DeleteFile(const FilePath& file_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
file_util::Delete(file_path, false);
}
// Called when the web intents dispatch has completed. Deletes the |file_path|.
void OnWebIntentDispatchCompleted(
const FilePath& file_path,
webkit_glue::WebIntentReplyType intent_reply) {
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&DeleteFile, file_path));
}
} // namespace
ChromeDownloadManagerDelegate::ChromeDownloadManagerDelegate(Profile* profile)
: profile_(profile),
next_download_id_(0),
download_prefs_(new DownloadPrefs(profile)) {
}
ChromeDownloadManagerDelegate::~ChromeDownloadManagerDelegate() {
}
void ChromeDownloadManagerDelegate::SetDownloadManager(DownloadManager* dm) {
download_manager_ = dm;
download_history_.reset(new DownloadHistory(profile_));
if (!profile_->IsOffTheRecord()) {
// DownloadManager should not be RefCountedThreadSafe.
// ChromeDownloadManagerDelegate outlives DownloadManager, and
// DownloadHistory uses a scoped canceller to cancel tasks when it is
// deleted. Almost all callbacks to DownloadManager should use weak pointers
// or bounce off a container object that uses ManagerGoingDown() to simulate
// a weak pointer.
download_history_->Load(
base::Bind(&DownloadManager::OnPersistentStoreQueryComplete,
download_manager_));
}
#if !defined(OS_ANDROID)
extension_event_router_.reset(new ExtensionDownloadsEventRouter(
profile_, download_manager_));
#endif
}
void ChromeDownloadManagerDelegate::Shutdown() {
download_history_.reset();
download_prefs_.reset();
#if !defined(OS_ANDROID)
extension_event_router_.reset();
#endif
}
DownloadId ChromeDownloadManagerDelegate::GetNextId() {
if (!profile_->IsOffTheRecord())
return DownloadId(this, next_download_id_++);
return BrowserContext::GetDownloadManager(profile_->GetOriginalProfile())->
GetDelegate()->GetNextId();
}
bool ChromeDownloadManagerDelegate::DetermineDownloadTarget(
DownloadItem* download,
const content::DownloadTargetCallback& callback) {
#if defined(ENABLE_SAFE_BROWSING)
DownloadProtectionService* service = GetDownloadProtectionService();
if (service) {
VLOG(2) << __FUNCTION__ << "() Start SB URL check for download = "
<< download->DebugString(false);
service->CheckDownloadUrl(
DownloadProtectionService::DownloadInfo::FromDownloadItem(*download),
base::Bind(
&ChromeDownloadManagerDelegate::CheckDownloadUrlDone,
this,
download->GetId(),
callback));
return true;
}
#endif
CheckDownloadUrlDone(download->GetId(), callback,
DownloadProtectionService::SAFE);
return true;
}
void ChromeDownloadManagerDelegate::ChooseDownloadPath(
DownloadItem* item,
const FilePath& suggested_path,
const FileSelectedCallback& file_selected_callback) {
// Deletes itself.
DownloadFilePicker* file_picker =
#if defined(OS_CHROMEOS)
new DownloadFilePickerChromeOS();
#else
new DownloadFilePicker();
#endif
file_picker->Init(download_manager_, item, suggested_path,
file_selected_callback);
}
FilePath ChromeDownloadManagerDelegate::GetIntermediatePath(
const FilePath& target_path,
content::DownloadDangerType danger_type) {
// If the download is not dangerous, just append .crdownload to the target
// path.
if (danger_type == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS)
return download_util::GetCrDownloadPath(target_path);
// If the download is potentially dangerous we create a filename of the form
// 'Unconfirmed <random>.crdownload'.
FilePath::StringType file_name;
FilePath dir = target_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));
return dir.Append(file_name);
}
WebContents* ChromeDownloadManagerDelegate::
GetAlternativeWebContentsToNotifyForDownload() {
#if defined(OS_ANDROID)
// Android does not implement BrowserList or any other way to get an
// alternate web contents.
return NULL;
#else
// Start the download in the last active browser. This is not ideal but better
// than fully hiding the download from the user.
Browser* last_active = browser::FindLastActiveWithProfile(profile_);
return last_active ? chrome::GetActiveWebContents(last_active) : NULL;
#endif
}
bool ChromeDownloadManagerDelegate::ShouldOpenFileBasedOnExtension(
const FilePath& path) {
FilePath::StringType extension = path.Extension();
if (extension.empty())
return false;
if (extensions::Extension::IsExtension(path))
return false;
DCHECK(extension[0] == FilePath::kExtensionSeparator);
extension.erase(0, 1);
return download_prefs_->IsAutoOpenEnabledForExtension(extension);
}
// static
void ChromeDownloadManagerDelegate::DisableSafeBrowsing(DownloadItem* item) {
#if defined(ENABLE_SAFE_BROWSING)
SafeBrowsingState* state = static_cast<SafeBrowsingState*>(
item->GetUserData(&safe_browsing_id));
DCHECK(!state);
if (!state)
state = new SafeBrowsingState();
state->SetVerdict(DownloadProtectionService::SAFE);
item->SetUserData(&safe_browsing_id, state);
#endif
}
bool ChromeDownloadManagerDelegate::IsDownloadReadyForCompletion(
DownloadItem* item,
const base::Closure& internal_complete_callback) {
#if defined(ENABLE_SAFE_BROWSING)
SafeBrowsingState* state = static_cast<SafeBrowsingState*>(
item->GetUserData(&safe_browsing_id));
if (!state) {
// Begin the safe browsing download protection check.
DownloadProtectionService* service = GetDownloadProtectionService();
if (service) {
VLOG(2) << __FUNCTION__ << "() Start SB download check for download = "
<< item->DebugString(false);
state = new SafeBrowsingState();
state->set_callback(internal_complete_callback);
item->SetUserData(&safe_browsing_id, state);
service->CheckClientDownload(
DownloadProtectionService::DownloadInfo::FromDownloadItem(*item),
base::Bind(
&ChromeDownloadManagerDelegate::CheckClientDownloadDone,
this,
item->GetId()));
return false;
}
} else if (!state->is_complete()) {
// Don't complete the download until we have an answer.
state->set_callback(internal_complete_callback);
return false;
}
#endif
#if defined(OS_CHROMEOS)
// If there's a Drive upload associated with this download, we wait until that
// is complete before allowing the download item to complete.
if (!gdata::DriveDownloadObserver::IsReadyToComplete(
item, internal_complete_callback))
return false;
#endif
return true;
}
// ShouldCompleteDownloadInternal() will never be called directly by a user, it
// will only be called asynchronously, so it should run
// |user_complete_callback|. ShouldCompleteDownload() will only be called
// directly by a user, so it does not need to run |user_complete_callback|
// because it can return true synchronously. The two methods look very similar,
// but their semantics are very different.
void ChromeDownloadManagerDelegate::ShouldCompleteDownloadInternal(
int download_id,
const base::Closure& user_complete_callback) {
DownloadItem* item = download_manager_->GetDownload(download_id);
if (!item)
return;
if (IsDownloadReadyForCompletion(item, base::Bind(
&ChromeDownloadManagerDelegate::ShouldCompleteDownloadInternal, this,
download_id, user_complete_callback)))
user_complete_callback.Run();
}
bool ChromeDownloadManagerDelegate::ShouldCompleteDownload(
DownloadItem* item,
const base::Closure& user_complete_callback) {
return IsDownloadReadyForCompletion(item, base::Bind(
&ChromeDownloadManagerDelegate::ShouldCompleteDownloadInternal, this,
item->GetId(), user_complete_callback));
}
bool ChromeDownloadManagerDelegate::ShouldOpenDownload(DownloadItem* item) {
if (download_crx_util::IsExtensionDownload(*item)) {
scoped_refptr<extensions::CrxInstaller> crx_installer =
download_crx_util::OpenChromeExtension(profile_, *item);
// CRX_INSTALLER_DONE will fire when the install completes. Observe()
// will call DelayedDownloadOpened() on this item. If this DownloadItem
// is not around when CRX_INSTALLER_DONE fires, Complete() will not be
// called.
registrar_.Add(
this,
chrome::NOTIFICATION_CRX_INSTALLER_DONE,
content::Source<extensions::CrxInstaller>(crx_installer.get()));
crx_installers_[crx_installer.get()] = item->GetId();
// The status text and percent complete indicator will change now
// that we are installing a CRX. Update observers so that they pick
// up the change.
item->UpdateObservers();
return false;
}
if (ShouldOpenWithWebIntents(item)) {
OpenWithWebIntent(item);
item->DelayedDownloadOpened(true /* did_open */);
return false;
}
return true;
}
bool ChromeDownloadManagerDelegate::ShouldOpenWithWebIntents(
const DownloadItem* item) {
if (!web_intents::IsWebIntentsEnabledForProfile(profile_))
return false;
if ((item->GetWebContents() && !item->GetWebContents()->GetDelegate()) &&
!web_intents::GetBrowserForBackgroundWebIntentDelivery(profile_)) {
return false;
}
if (!item->GetForcedFilePath().empty())
return false;
if (item->GetTargetDisposition() == DownloadItem::TARGET_DISPOSITION_PROMPT)
return false;
std::string mime_type = item->GetMimeType();
if (mime_type == "application/rss+xml" ||
mime_type == "application/atom+xml") {
return true;
}
#if defined(OS_CHROMEOS)
if (mime_type == "application/msword" ||
mime_type == "application/vnd.ms-powerpoint" ||
mime_type == "application/vnd.ms-excel" ||
mime_type == "application/vnd.openxmlformats-officedocument."
"wordprocessingml.document" ||
mime_type == "application/vnd.openxmlformats-officedocument."
"presentationml.presentation" ||
mime_type == "application/vnd.openxmlformats-officedocument."
"spreadsheetml.sheet") {
return true;
}
#endif // defined(OS_CHROMEOS)
// If QuickOffice extension is installed, use web intents to handle the
// downloaded file.
const char* kQuickOfficeExtensionId = "gbkeegbaiigmenfmjfclcdgdpimamgkj";
ExtensionServiceInterface* extension_service =
profile_->GetExtensionService();
if (extension_service &&
extension_service->GetInstalledExtension(kQuickOfficeExtensionId) &&
extension_service->IsExtensionEnabled(kQuickOfficeExtensionId)) {
if (mime_type == "application/msword" ||
mime_type == "application/vnd.ms-powerpoint" ||
mime_type == "application/vnd.ms-excel" ||
mime_type == "application/vnd.openxmlformats-officedocument."
"wordprocessingml.document" ||
mime_type == "application/vnd.openxmlformats-officedocument."
"presentationml.presentation" ||
mime_type == "application/vnd.openxmlformats-officedocument."
"spreadsheetml.sheet") {
return true;
}
}
return false;
}
void ChromeDownloadManagerDelegate::OpenWithWebIntent(
const DownloadItem* item) {
webkit_glue::WebIntentData intent_data(
ASCIIToUTF16("http://webintents.org/view"),
ASCIIToUTF16(item->GetMimeType()),
item->GetFullPath(),
item->GetReceivedBytes());
// RCH specifies that the receiver gets the url, but with Web Intents
// it isn't really needed.
intent_data.extra_data.insert(make_pair(
ASCIIToUTF16("url"), ASCIIToUTF16(item->GetURL().spec())));
// Pass the downloaded filename to the service app as the name hint.
intent_data.extra_data.insert(
make_pair(ASCIIToUTF16("filename"),
item->GetFileNameToReportUser().LossyDisplayName()));
content::WebIntentsDispatcher* dispatcher =
content::WebIntentsDispatcher::Create(intent_data);
dispatcher->RegisterReplyNotification(
base::Bind(&OnWebIntentDispatchCompleted, item->GetFullPath()));
content::WebContentsDelegate* delegate = NULL;
#if !defined(OS_ANDROID)
if (item->GetWebContents() && item->GetWebContents()->GetDelegate()) {
delegate = item->GetWebContents()->GetDelegate();
} else {
delegate = web_intents::GetBrowserForBackgroundWebIntentDelivery(profile_);
}
#endif // !defined(OS_ANDROID)
DCHECK(delegate);
delegate->WebIntentDispatch(NULL, dispatcher);
}
bool ChromeDownloadManagerDelegate::GenerateFileHash() {
#if defined(ENABLE_SAFE_BROWSING)
return profile_->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled) &&
g_browser_process->safe_browsing_service()->DownloadBinHashNeeded();
#else
return false;
#endif
}
void ChromeDownloadManagerDelegate::AddItemToPersistentStore(
DownloadItem* item) {
if (profile_->IsOffTheRecord()) {
OnItemAddedToPersistentStore(
item->GetId(), download_history_->GetNextFakeDbHandle());
return;
}
download_history_->AddEntry(item,
base::Bind(&ChromeDownloadManagerDelegate::OnItemAddedToPersistentStore,
this));
}
void ChromeDownloadManagerDelegate::UpdateItemInPersistentStore(
DownloadItem* item) {
download_history_->UpdateEntry(item);
}
void ChromeDownloadManagerDelegate::UpdatePathForItemInPersistentStore(
DownloadItem* item,
const FilePath& new_path) {
download_history_->UpdateDownloadPath(item, new_path);
}
void ChromeDownloadManagerDelegate::RemoveItemFromPersistentStore(
DownloadItem* item) {
download_history_->RemoveEntry(item);
}
void ChromeDownloadManagerDelegate::RemoveItemsFromPersistentStoreBetween(
base::Time remove_begin,
base::Time remove_end) {
if (profile_->IsOffTheRecord())
return;
download_history_->RemoveEntriesBetween(remove_begin, remove_end);
}
void ChromeDownloadManagerDelegate::GetSaveDir(BrowserContext* browser_context,
FilePath* website_save_dir,
FilePath* download_save_dir,
bool* skip_dir_check) {
Profile* profile = Profile::FromBrowserContext(browser_context);
PrefService* prefs = profile->GetPrefs();
// Check whether the preference has the preferred directory for saving file.
// If not, initialize it with default directory.
if (!prefs->FindPreference(prefs::kSaveFileDefaultDirectory)) {
DCHECK(prefs->FindPreference(prefs::kDownloadDefaultDirectory));
FilePath default_save_path = prefs->GetFilePath(
prefs::kDownloadDefaultDirectory);
prefs->RegisterFilePathPref(prefs::kSaveFileDefaultDirectory,
default_save_path,
PrefService::UNSYNCABLE_PREF);
}
// Get the directory from preference.
*website_save_dir = prefs->GetFilePath(prefs::kSaveFileDefaultDirectory);
DCHECK(!website_save_dir->empty());
*download_save_dir = prefs->GetFilePath(prefs::kDownloadDefaultDirectory);
*skip_dir_check = false;
#if defined(OS_CHROMEOS)
*skip_dir_check = gdata::util::IsUnderDriveMountPoint(*website_save_dir);
#endif
}
void ChromeDownloadManagerDelegate::ChooseSavePath(
WebContents* web_contents,
const FilePath& suggested_path,
const FilePath::StringType& default_extension,
bool can_save_as_complete,
const content::SavePackagePathPickedCallback& callback) {
// Deletes itself.
#if defined(OS_CHROMEOS)
new SavePackageFilePickerChromeOS(web_contents, suggested_path, callback);
#else
new SavePackageFilePicker(web_contents, suggested_path, default_extension,
can_save_as_complete, download_prefs_.get(), callback);
#endif
}
void ChromeDownloadManagerDelegate::ClearLastDownloadPath() {
last_download_path_.clear();
}
DownloadProtectionService*
ChromeDownloadManagerDelegate::GetDownloadProtectionService() {
#if defined(ENABLE_SAFE_BROWSING)
SafeBrowsingService* sb_service = g_browser_process->safe_browsing_service();
if (sb_service && sb_service->download_protection_service() &&
profile_->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) {
return sb_service->download_protection_service();
}
#endif
return NULL;
}
// TODO(phajdan.jr): This is apparently not being exercised in tests.
bool ChromeDownloadManagerDelegate::IsDangerousFile(
const DownloadItem& download,
const FilePath& suggested_path,
bool visited_referrer_before) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Anything loaded directly from the address bar is OK.
if (download.GetTransitionType() & content::PAGE_TRANSITION_FROM_ADDRESS_BAR)
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::switch_utils::IsEasyOffStoreInstallEnabled() &&
download_crx_util::IsExtensionDownload(download) &&
!extensions::WebstoreInstaller::GetAssociatedApproval(download)) {
return true;
}
// Anything the user has marked auto-open is OK if it's user-initiated.
if (ShouldOpenFileBasedOnExtension(suggested_path) &&
download.HasUserGesture())
return false;
// "Allow on user gesture" is OK when we have a user gesture and the hosting
// page has been visited before today.
download_util::DownloadDangerLevel danger_level =
download_util::GetFileDangerLevel(suggested_path.BaseName());
if (danger_level == download_util::AllowOnUserGesture)
return !download.HasUserGesture() || !visited_referrer_before;
return danger_level == download_util::Dangerous;
}
void ChromeDownloadManagerDelegate::GetReservedPath(
DownloadItem& download,
const FilePath& target_path,
const FilePath& default_download_path,
bool should_uniquify_path,
const DownloadPathReservationTracker::ReservedPathCallback& callback) {
DownloadPathReservationTracker::GetReservedPath(
download, target_path, default_download_path, should_uniquify_path,
callback);
}
void ChromeDownloadManagerDelegate::CheckDownloadUrlDone(
int32 download_id,
const content::DownloadTargetCallback& callback,
DownloadProtectionService::DownloadCheckResult result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DownloadItem* download = download_manager_->GetDownload(download_id);
if (!download || (download->GetState() != DownloadItem::IN_PROGRESS))
return;
VLOG(2) << __FUNCTION__ << "() download = " << download->DebugString(false)
<< " verdict = " << result;
content::DownloadDangerType danger_type = download->GetDangerType();
if (result != DownloadProtectionService::SAFE)
danger_type = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL;
download_history_->CheckVisitedReferrerBefore(
download_id, download->GetReferrerUrl(),
base::Bind(&ChromeDownloadManagerDelegate::CheckVisitedReferrerBeforeDone,
this, download_id, callback, danger_type));
}
void ChromeDownloadManagerDelegate::CheckClientDownloadDone(
int32 download_id,
DownloadProtectionService::DownloadCheckResult result) {
DownloadItem* item = download_manager_->GetDownload(download_id);
if (!item || (item->GetState() != DownloadItem::IN_PROGRESS))
return;
VLOG(2) << __FUNCTION__ << "() download = " << item->DebugString(false)
<< " verdict = " << result;
// We only mark the content as being dangerous if the download's safety state
// has not been set to DANGEROUS yet. We don't want to show two warnings.
if (item->GetSafetyState() == DownloadItem::SAFE) {
switch (result) {
case DownloadProtectionService::SAFE:
// Do nothing.
break;
case DownloadProtectionService::DANGEROUS:
item->OnContentCheckCompleted(
content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT);
break;
case DownloadProtectionService::UNCOMMON:
item->OnContentCheckCompleted(
content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT);
break;
}
}
SafeBrowsingState* state = static_cast<SafeBrowsingState*>(
item->GetUserData(&safe_browsing_id));
state->SetVerdict(result);
}
// content::NotificationObserver implementation.
void ChromeDownloadManagerDelegate::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK(type == chrome::NOTIFICATION_CRX_INSTALLER_DONE);
registrar_.Remove(this,
chrome::NOTIFICATION_CRX_INSTALLER_DONE,
source);
scoped_refptr<extensions::CrxInstaller> installer =
content::Source<extensions::CrxInstaller>(source).ptr();
int download_id = crx_installers_[installer];
crx_installers_.erase(installer.get());
DownloadItem* item = download_manager_->GetDownload(download_id);
if (item)
item->DelayedDownloadOpened(installer->did_handle_successfully());
}
void ChromeDownloadManagerDelegate::CheckVisitedReferrerBeforeDone(
int32 download_id,
const content::DownloadTargetCallback& callback,
content::DownloadDangerType danger_type,
bool visited_referrer_before) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DownloadItem* download =
download_manager_->GetDownload(download_id);
if (!download || (download->GetState() != DownloadItem::IN_PROGRESS))
return;
bool should_prompt = (download->GetTargetDisposition() ==
DownloadItem::TARGET_DISPOSITION_PROMPT);
bool is_forced_path = !download->GetForcedFilePath().empty();
FilePath suggested_path;
// Check whether this download is for an extension install or not.
// Allow extensions to be explicitly saved.
if (!is_forced_path) {
FilePath generated_name;
GenerateFileNameFromRequest(*download, &generated_name);
// Freeze the user's preference for showing a Save As dialog. We're going
// to bounce around a bunch of threads and we don't want to worry about race
// conditions where the user changes this pref out from under us.
if (download_prefs_->PromptForDownload()) {
// But ignore the user's preference 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...".
// 2) Filetypes marked "always open." If the user just wants this file
// opened, don't bother asking where to keep it.
if (!download_crx_util::IsExtensionDownload(*download) &&
!ShouldOpenFileBasedOnExtension(generated_name))
should_prompt = true;
}
if (download_prefs_->IsDownloadPathManaged())
should_prompt = false;
// Determine the proper path for a download, by either one of the following:
// 1) using the default download directory.
// 2) prompting the user.
FilePath target_directory;
if (should_prompt && !last_download_path_.empty())
target_directory = last_download_path_;
else
target_directory = download_prefs_->DownloadPath();
suggested_path = target_directory.Append(generated_name);
} else {
DCHECK(!should_prompt);
suggested_path = download->GetForcedFilePath();
}
// If we will open the file with a web intents dispatch,
// give it a name that will not allow the OS to open it using usual
// associated apps.
if (ShouldOpenWithWebIntents(download)) {
download->SetDisplayName(suggested_path.BaseName());
suggested_path = suggested_path.AddExtension(kWebIntentsFileExtension);
}
// 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 (!should_prompt && !is_forced_path &&
IsDangerousFile(*download, suggested_path, visited_referrer_before)) {
danger_type = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
}
#if defined(ENABLE_SAFE_BROWSING)
DownloadProtectionService* service = GetDownloadProtectionService();
// 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.
if (service && service->enabled()) {
DownloadProtectionService::DownloadInfo info =
DownloadProtectionService::DownloadInfo::FromDownloadItem(*download);
info.target_file = suggested_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.
if (service->IsSupportedDownload(info))
danger_type = content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT;
}
#endif
} else {
// Currently we only expect this case.
DCHECK_EQ(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, danger_type);
}
#if defined (OS_CHROMEOS)
gdata::DriveDownloadObserver::SubstituteDriveDownloadPath(
profile_, suggested_path, download,
base::Bind(
&ChromeDownloadManagerDelegate::SubstituteDriveDownloadPathCallback,
this, download->GetId(), callback, should_prompt, is_forced_path,
danger_type));
#else
GetReservedPath(
*download, suggested_path, download_prefs_->DownloadPath(),
!is_forced_path,
base::Bind(&ChromeDownloadManagerDelegate::OnPathReservationAvailable,
this, download->GetId(), callback, should_prompt,
danger_type));
#endif
}
#if defined (OS_CHROMEOS)
// TODO(asanka): Merge this logic with the logic in DownloadFilePickerChromeOS.
void ChromeDownloadManagerDelegate::SubstituteDriveDownloadPathCallback(
int32 download_id,
const content::DownloadTargetCallback& callback,
bool should_prompt,
bool is_forced_path,
content::DownloadDangerType danger_type,
const FilePath& suggested_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DownloadItem* download =
download_manager_->GetDownload(download_id);
if (!download || (download->GetState() != DownloadItem::IN_PROGRESS))
return;
GetReservedPath(
*download, suggested_path, download_prefs_->DownloadPath(),
!is_forced_path,
base::Bind(&ChromeDownloadManagerDelegate::OnPathReservationAvailable,
this, download->GetId(), callback, should_prompt,
danger_type));
}
#endif
void ChromeDownloadManagerDelegate::OnPathReservationAvailable(
int32 download_id,
const content::DownloadTargetCallback& callback,
bool should_prompt,
content::DownloadDangerType danger_type,
const FilePath& reserved_path,
bool reserved_path_verified) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DownloadItem* download =
download_manager_->GetDownload(download_id);
if (!download || (download->GetState() != DownloadItem::IN_PROGRESS))
return;
if (should_prompt || !reserved_path_verified) {
// If the target path could not be verified then the path was non-existant,
// non writeable or could not be uniquified. Prompt the user.
ChooseDownloadPath(
download, reserved_path,
base::Bind(&ChromeDownloadManagerDelegate::OnTargetPathDetermined,
this, download_id, callback,
DownloadItem::TARGET_DISPOSITION_PROMPT, danger_type));
} else {
OnTargetPathDetermined(download_id, callback,
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
danger_type, reserved_path);
}
}
void ChromeDownloadManagerDelegate::OnTargetPathDetermined(
int32 download_id,
const content::DownloadTargetCallback& callback,
DownloadItem::TargetDisposition disposition,
content::DownloadDangerType danger_type,
const FilePath& target_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
FilePath intermediate_path;
DownloadItem* download =
download_manager_->GetDownload(download_id);
if (!download || (download->GetState() != DownloadItem::IN_PROGRESS))
return;
// If |target_path| is empty, then that means that the user wants to cancel
// the download.
if (!target_path.empty()) {
intermediate_path = GetIntermediatePath(target_path, danger_type);
// Retain the last directory. Exclude temporary downloads since the path
// likely points at the location of a temporary file.
// TODO(asanka): This logic is a hack. DownloadFilePicker should give us a
// directory to persist. Or perhaps, if the Drive path
// substitution logic is moved here, then we would have a
// persistable path after the DownloadFilePicker is done.
if (disposition == DownloadItem::TARGET_DISPOSITION_PROMPT &&
!download->IsTemporary())
last_download_path_ = target_path.DirName();
}
callback.Run(target_path, disposition, danger_type, intermediate_path);
}
void ChromeDownloadManagerDelegate::OnItemAddedToPersistentStore(
int32 download_id, int64 db_handle) {
// It's not immediately obvious, but HistoryBackend::CreateDownload() can
// call this function with an invalid |db_handle|. For instance, this can
// happen when the history database is offline. We cannot have multiple
// DownloadItems with the same invalid db_handle, so we need to assign a
// unique |db_handle| here.
if (db_handle == DownloadItem::kUninitializedHandle)
db_handle = download_history_->GetNextFakeDbHandle();
download_manager_->OnItemAddedToPersistentStore(download_id, db_handle);
}