blob: a5f6b2df989c215f0a5e756b5301ceebec653a84 [file] [log] [blame]
// Copyright 2014 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/safe_browsing/srt_global_error_win.h"
#include "base/base_paths.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/safe_browsing/srt_client_info_win.h"
#include "chrome/browser/safe_browsing/srt_field_trial_win.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/global_error/global_error_service.h"
#include "chrome/common/channel_info.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/installer/util/install_util.h"
#include "components/component_updater/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_thread.h"
#include "ui/base/l10n/l10n_util.h"
using base::SingleThreadTaskRunner;
using base::ThreadTaskRunnerHandle;
using content::BrowserThread;
namespace safe_browsing {
namespace {
// Used as a backup plan in case the SRT executable was not successfully
// downloaded or run.
const char kSRTDownloadURL[] =
"https://www.google.com/chrome/srt/?chrome-prompt=1";
// The extension to use to replace the temporary one created when the SRT was
// downloaded.
const base::FilePath::CharType kExecutableExtension[] = L"exe";
// Switches to add to the command line when executing the SRT.
const char kChromePromptSwitch[] = "chrome-prompt";
const char kChromeExePathSwitch[] = "chrome-exe-path";
const char kChromeSystemInstallSwitch[] = "chrome-system-install";
const char kUmaUserSwitch[] = "uma-user";
// Values to be passed to the kChromePromptSwitch of the Chrome Cleanup Tool to
// indicate how the user interacted with the accept button.
enum class ChromePromptValue {
// The user accepted the prompt when the prompt was first shown.
kPrompted = 3,
// The user accepted the prompt after navigating to it from the menu.
kShownFromMenu = 4
};
void MaybeExecuteSRTFromBlockingPool(
const base::FilePath& downloaded_path,
bool metrics_enabled,
ChromePromptValue prompt_value,
const scoped_refptr<SingleThreadTaskRunner>& task_runner,
const base::Closure& success_callback,
const base::Closure& failure_callback) {
DCHECK(!downloaded_path.empty());
if (base::PathExists(downloaded_path)) {
base::FilePath executable_path(
downloaded_path.ReplaceExtension(kExecutableExtension));
if (base::ReplaceFile(downloaded_path, executable_path, nullptr)) {
base::CommandLine srt_command_line(executable_path);
srt_command_line.AppendSwitchASCII(
kChromePromptSwitch,
base::IntToString(static_cast<int>(prompt_value)));
srt_command_line.AppendSwitchASCII(kChromeVersionSwitch,
version_info::GetVersionNumber());
srt_command_line.AppendSwitchASCII(kChromeChannelSwitch,
base::IntToString(ChannelAsInt()));
base::FilePath chrome_exe_path;
PathService::Get(base::FILE_EXE, &chrome_exe_path);
srt_command_line.AppendSwitchPath(kChromeExePathSwitch, chrome_exe_path);
if (!InstallUtil::IsPerUserInstall(chrome_exe_path))
srt_command_line.AppendSwitch(kChromeSystemInstallSwitch);
if (metrics_enabled) {
srt_command_line.AppendSwitch(kUmaUserSwitch);
srt_command_line.AppendSwitch(kEnableCrashReporting);
}
base::Process srt_process(
base::LaunchProcess(srt_command_line, base::LaunchOptions()));
if (srt_process.IsValid()) {
task_runner->PostTask(FROM_HERE, success_callback);
return;
}
}
}
task_runner->PostTask(FROM_HERE, failure_callback);
}
void DeleteFilesFromBlockingPool(const base::FilePath& downloaded_path) {
base::DeleteFile(downloaded_path, false);
base::DeleteFile(downloaded_path.ReplaceExtension(kExecutableExtension),
false);
}
} // namespace
// SRTGlobalError ------------------------------------------------------------
SRTGlobalError::SRTGlobalError(GlobalErrorService* global_error_service,
const base::FilePath& downloaded_path)
: global_error_service_(global_error_service),
downloaded_path_(downloaded_path) {
DCHECK(global_error_service_);
}
SRTGlobalError::~SRTGlobalError() {
if (!interacted_) {
BrowserThread::PostBlockingPoolTask(
FROM_HERE, base::Bind(&DeleteFilesFromBlockingPool, downloaded_path_));
}
}
bool SRTGlobalError::HasMenuItem() {
return true;
}
int SRTGlobalError::MenuItemCommandID() {
return IDC_SHOW_SRT_BUBBLE;
}
base::string16 SRTGlobalError::MenuItemLabel() {
return l10n_util::GetStringUTF16(IDS_SRT_MENU_ITEM);
}
void SRTGlobalError::ExecuteMenuItem(Browser* browser) {
RecordSRTPromptHistogram(SRT_PROMPT_SHOWN_FROM_MENU);
bubble_shown_from_menu_ = true;
ShowBubbleView(browser);
}
void SRTGlobalError::ShowBubbleView(Browser* browser) {
RecordSRTPromptHistogram(SRT_PROMPT_SHOWN);
GlobalErrorWithStandardBubble::ShowBubbleView(browser);
}
bool SRTGlobalError::ShouldShowCloseButton() const {
return true;
}
base::string16 SRTGlobalError::GetBubbleViewTitle() {
return l10n_util::GetStringUTF16(IDS_SRT_BUBBLE_TITLE);
}
std::vector<base::string16> SRTGlobalError::GetBubbleViewMessages() {
std::vector<base::string16> messages;
messages.push_back(l10n_util::GetStringUTF16(IDS_SRT_BUBBLE_TEXT));
return messages;
}
base::string16 SRTGlobalError::GetBubbleViewAcceptButtonLabel() {
return downloaded_path_.empty()
? l10n_util::GetStringUTF16(IDS_SRT_BUBBLE_DOWNLOAD_BUTTON_TEXT)
: l10n_util::GetStringUTF16(IDS_SRT_BUBBLE_RUN_BUTTON_TEXT);
}
bool SRTGlobalError::ShouldAddElevationIconToAcceptButton() {
return !downloaded_path_.empty() && SRTPromptNeedsElevationIcon();
}
base::string16 SRTGlobalError::GetBubbleViewCancelButtonLabel() {
// Show the dismiss button only if the bubble was shown from the menu.
return bubble_shown_from_menu_
? l10n_util::GetStringUTF16(IDS_SRT_BUBBLE_DISMISS)
: base::string16();
}
void SRTGlobalError::OnBubbleViewDidClose(Browser* browser) {
if (!interacted_) {
// If user didn't interact with the bubble, it means they used the generic
// close bubble button.
RecordSRTPromptHistogram(SRT_PROMPT_CLOSED);
g_browser_process->local_state()->SetBoolean(
prefs::kSwReporterPendingPrompt, true);
}
}
void SRTGlobalError::BubbleViewAcceptButtonPressed(Browser* browser) {
OnUserinteractionStarted(SRT_PROMPT_ACCEPTED);
MaybeExecuteSRT();
}
void SRTGlobalError::BubbleViewCancelButtonPressed(Browser* browser) {
OnUserinteractionStarted(SRT_PROMPT_DENIED);
BrowserThread::PostBlockingPoolTask(
FROM_HERE, base::Bind(&DeleteFilesFromBlockingPool, downloaded_path_));
OnUserinteractionDone();
}
bool SRTGlobalError::ShouldCloseOnDeactivate() const {
return false;
}
void SRTGlobalError::MaybeExecuteSRT() {
if (downloaded_path_.empty()) {
FallbackToDownloadPage();
return;
}
// At this point, this object owns itself, since ownership has been taken back
// from the global_error_service_ in the call to RemoveGlobalError. This means
// that it is safe to use base::Unretained here.
BrowserThread::PostBlockingPoolTask(
FROM_HERE,
base::Bind(
&MaybeExecuteSRTFromBlockingPool, downloaded_path_,
ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled(),
bubble_shown_from_menu_ ? ChromePromptValue::kShownFromMenu
: ChromePromptValue::kPrompted,
base::ThreadTaskRunnerHandle::Get(),
base::Bind(&SRTGlobalError::OnUserinteractionDone,
base::Unretained(this)),
base::Bind(&SRTGlobalError::FallbackToDownloadPage,
base::Unretained(this))));
}
void SRTGlobalError::FallbackToDownloadPage() {
RecordSRTPromptHistogram(SRT_PROMPT_FALLBACK);
Browser* browser = chrome::FindLastActive();
if (browser) {
browser->OpenURL(
content::OpenURLParams(GURL(kSRTDownloadURL), content::Referrer(),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK, false));
}
BrowserThread::PostBlockingPoolTask(
FROM_HERE, base::Bind(&DeleteFilesFromBlockingPool, downloaded_path_));
OnUserinteractionDone();
}
void SRTGlobalError::OnUserinteractionStarted(
SRTPromptHistogramValue histogram_value) {
// This is for cases where the UI doesn't go away quickly enough and user
// might click on the button more than once, or more than one button.
if (interacted_)
return;
RecordSRTPromptHistogram(histogram_value);
interacted_ = true;
if (global_error_service_) {
global_error_service_->RemoveGlobalError(this);
global_error_service_ = nullptr;
}
}
void SRTGlobalError::OnUserinteractionDone() {
DCHECK(interacted_);
// Once the user interacted with the bubble, we can forget about any pending
// prompt.
g_browser_process->local_state()->SetBoolean(prefs::kSwReporterPendingPrompt,
false);
delete this;
}
} // namespace safe_browsing