Commit f85a34b7 authored by cthomp@chromium.org's avatar cthomp@chromium.org

Experience sampling instrumentation for dangerous downloads warnings

BUG=384635

Review URL: https://codereview.chromium.org/402293002

Cr-Commit-Position: refs/heads/master@{#290076}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@290076 0039d316-1c4b-4281-b951-d872f2087c98
parent fc103daf
......@@ -9,6 +9,7 @@
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_stats.h"
#include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h"
#include "chrome/browser/ui/tab_modal_confirm_dialog.h"
#include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
#include "content/public/browser/download_danger_type.h"
......@@ -17,6 +18,8 @@
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
using extensions::ExperienceSamplingEvent;
namespace {
// TODO(wittman): Create a native web contents modal dialog implementation of
......@@ -56,6 +59,8 @@ class DownloadDangerPromptImpl : public DownloadDangerPrompt,
bool show_context_;
OnDone done_;
scoped_ptr<ExperienceSamplingEvent> sampling_event_;
DISALLOW_COPY_AND_ASSIGN(DownloadDangerPromptImpl);
};
......@@ -71,6 +76,14 @@ DownloadDangerPromptImpl::DownloadDangerPromptImpl(
DCHECK(!done_.is_null());
download_->AddObserver(this);
RecordOpenedDangerousConfirmDialog(download_->GetDangerType());
// ExperienceSampling: A malicious download warning is being shown to the
// user, so we start a new SamplingEvent and track it.
sampling_event_.reset(new ExperienceSamplingEvent(
ExperienceSamplingEvent::kDownloadDangerPrompt,
download->GetURL(),
download->GetReferrerUrl(),
download->GetBrowserContext()));
}
DownloadDangerPromptImpl::~DownloadDangerPromptImpl() {
......@@ -203,14 +216,20 @@ base::string16 DownloadDangerPromptImpl::GetCancelButtonTitle() {
}
void DownloadDangerPromptImpl::OnAccepted() {
// ExperienceSampling: User proceeded through the warning.
sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kProceed);
RunDone(ACCEPT);
}
void DownloadDangerPromptImpl::OnCanceled() {
// ExperienceSampling: User canceled the warning.
sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kDeny);
RunDone(CANCEL);
}
void DownloadDangerPromptImpl::OnClosed() {
// ExperienceSampling: User canceled the warning.
sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kDeny);
RunDone(DISMISS);
}
......
......@@ -5,6 +5,7 @@
#include "base/bind.h"
#include "base/files/file_path.h"
#include "chrome/browser/download/download_danger_prompt.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_tabstrip.h"
......@@ -14,11 +15,13 @@
#include "content/public/test/mock_download_item.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using ::testing::_;
using ::testing::ByRef;
using ::testing::Eq;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::SaveArg;
class DownloadDangerPromptTest : public InProcessBrowserTest {
......@@ -100,6 +103,13 @@ class DownloadDangerPromptTest : public InProcessBrowserTest {
};
IN_PROC_BROWSER_TEST_F(DownloadDangerPromptTest, TestAll) {
// ExperienceSampling: Set default actions for DownloadItem methods we need.
ON_CALL(download(), GetURL()).WillByDefault(ReturnRef(GURL::EmptyGURL()));
ON_CALL(download(), GetReferrerUrl())
.WillByDefault(ReturnRef(GURL::EmptyGURL()));
ON_CALL(download(), GetBrowserContext())
.WillByDefault(Return(browser()->profile()));
OpenNewTab();
// Clicking the Accept button should invoke the ACCEPT action.
......
......@@ -17,10 +17,17 @@ namespace extensions {
// static
const char ExperienceSamplingEvent::kProceed[] = "proceed";
const char ExperienceSamplingEvent::kDeny[] = "deny";
const char ExperienceSamplingEvent::kIgnore[] = "ignore";
const char ExperienceSamplingEvent::kCancel[] = "cancel";
const char ExperienceSamplingEvent::kReload[] = "reload";
// static
const char ExperienceSamplingEvent::kMaliciousDownload[] =
"download_warning_malicious";
const char ExperienceSamplingEvent::kDangerousDownload[] =
"download_warning_dangerous";
const char ExperienceSamplingEvent::kDownloadDangerPrompt[] =
"download_danger_prompt";
const char ExperienceSamplingEvent::kExtensionInstallDialog[] =
"extension_install_dialog_";
......
......@@ -22,10 +22,14 @@ class ExperienceSamplingEvent {
// String constants for user decision events.
static const char kProceed[];
static const char kDeny[];
static const char kIgnore[];
static const char kCancel[];
static const char kReload[];
// String constants for event names.
static const char kMaliciousDownload[];
static const char kDangerousDownload[];
static const char kDownloadDangerPrompt[];
static const char kExtensionInstallDialog[];
// The Create() functions can return an empty scoped_ptr if they cannot find
......
......@@ -21,6 +21,10 @@ class DownloadItem;
class PageNavigator;
}
namespace extensions {
class ExperienceSamplingEvent;
}
namespace gfx {
class FontList;
}
......@@ -71,6 +75,10 @@ class MenuModel;
kNormal,
kDangerous
} state_;
// ExperienceSampling: This tracks dangerous/malicious downloads warning UI
// and the user's decisions about it.
scoped_ptr<extensions::ExperienceSamplingEvent> sampling_event_;
};
// Initialize controller for |downloadItem|.
......
......@@ -14,6 +14,7 @@
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/download/download_shelf_context_menu.h"
#include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h"
#import "chrome/browser/themes/theme_properties.h"
#import "chrome/browser/themes/theme_service.h"
#import "chrome/browser/ui/cocoa/download/download_item_button.h"
......@@ -35,6 +36,7 @@
#include "ui/gfx/image/image.h"
using content::DownloadItem;
using extensions::ExperienceSamplingEvent;
namespace {
......@@ -117,6 +119,8 @@ class DownloadShelfContextMenuMac : public DownloadShelfContextMenu {
}
- (void)dealloc {
if (sampling_event_.get())
sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kIgnore);
[[NSNotificationCenter defaultCenter] removeObserver:self];
[progressView_ setController:nil];
[[self view] removeFromSuperview];
......@@ -144,6 +148,17 @@ class DownloadShelfContextMenuMac : public DownloadShelfContextMenu {
[self setState:kDangerous];
// ExperienceSampling: Dangerous or malicious download warning is being shown
// to the user, so we start a new SamplingEvent and track it.
std::string event_name = downloadModel->MightBeMalicious()
? ExperienceSamplingEvent::kMaliciousDownload
: ExperienceSamplingEvent::kDangerousDownload;
sampling_event_.reset(new ExperienceSamplingEvent(
event_name,
downloadModel->download()->GetURL(),
downloadModel->download()->GetReferrerUrl(),
downloadModel->download()->GetBrowserContext()));
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
NSImage* alertIcon;
......@@ -320,6 +335,11 @@ class DownloadShelfContextMenuMac : public DownloadShelfContextMenu {
// user did this to detect whether we're being clickjacked.
UMA_HISTOGRAM_LONG_TIMES("clickjacking.save_download",
base::Time::Now() - creationTime_);
// ExperienceSampling: User chose to proceed with dangerous download.
if (sampling_event_.get()) {
sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kProceed);
sampling_event_.reset(NULL);
}
// This will change the state and notify us.
bridge_->download_model()->download()->ValidateDangerousDownload();
}
......@@ -333,6 +353,11 @@ class DownloadShelfContextMenuMac : public DownloadShelfContextMenu {
}
- (IBAction)dismissMaliciousDownload:(id)sender {
// ExperienceSampling: User dismissed the dangerous download.
if (sampling_event_.get()) {
sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kDeny);
sampling_event_.reset(NULL);
}
[self remove];
// WARNING: we are deleted at this point.
}
......
......@@ -5,6 +5,7 @@
#include "base/compiler_specific.h"
#include "chrome/browser/download/download_danger_prompt.h"
#include "chrome/browser/download/download_stats.h"
#include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h"
#include "chrome/browser/ui/views/constrained_window_views.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
......@@ -21,6 +22,8 @@
#include "ui/views/window/dialog_client_view.h"
#include "ui/views/window/dialog_delegate.h"
using extensions::ExperienceSamplingEvent;
namespace {
const int kMessageWidth = 320;
......@@ -69,6 +72,8 @@ class DownloadDangerPromptViews : public DownloadDangerPrompt,
bool show_context_;
OnDone done_;
scoped_ptr<ExperienceSamplingEvent> sampling_event_;
views::View* contents_view_;
};
......@@ -117,6 +122,14 @@ DownloadDangerPromptViews::DownloadDangerPromptViews(
layout->AddView(message_body_label);
RecordOpenedDangerousConfirmDialog(download_->GetDangerType());
// ExperienceSampling: A malicious download warning is being shown to the
// user, so we start a new SamplingEvent and track it.
sampling_event_.reset(new ExperienceSamplingEvent(
ExperienceSamplingEvent::kDownloadDangerPrompt,
item->GetURL(),
item->GetReferrerUrl(),
item->GetBrowserContext()));
}
// DownloadDangerPrompt methods:
......@@ -170,18 +183,24 @@ ui::ModalType DownloadDangerPromptViews::GetModalType() const {
bool DownloadDangerPromptViews::Cancel() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// ExperienceSampling: User canceled the warning.
sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kDeny);
RunDone(CANCEL);
return true;
}
bool DownloadDangerPromptViews::Accept() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// ExperienceSampling: User proceeded through the warning.
sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kProceed);
RunDone(ACCEPT);
return true;
}
bool DownloadDangerPromptViews::Close() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// ExperienceSampling: User canceled the warning.
sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kDeny);
RunDone(DISMISS);
return true;
}
......
......@@ -23,6 +23,7 @@
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/download/download_stats.h"
#include "chrome/browser/download/drag_download_item.h"
#include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/download_feedback_service.h"
#include "chrome/browser/safe_browsing/download_protection_service.h"
......@@ -54,6 +55,7 @@
#include "ui/views/widget/widget.h"
using content::DownloadItem;
using extensions::ExperienceSamplingEvent;
// TODO(paulg): These may need to be adjusted when download progress
// animation is added, and also possibly to take into account
......@@ -230,6 +232,12 @@ DownloadItemView::DownloadItemView(DownloadItem* download_item,
DownloadItemView::~DownloadItemView() {
StopDownloadProgress();
download()->RemoveObserver(this);
// ExperienceSampling: If the user took no action to remove the warning
// before it disappeared, then the user effectively dismissed the download
// without keeping it.
if (sampling_event_.get())
sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kIgnore);
}
// Progress animation handlers.
......@@ -550,6 +558,12 @@ void DownloadItemView::ButtonPressed(views::Button* sender,
// The user has confirmed a dangerous download. We'd record how quickly the
// user did this to detect whether we're being clickjacked.
UMA_HISTOGRAM_LONG_TIMES("clickjacking.save_download", warning_duration);
// ExperienceSampling: User chose to proceed with a dangerous download.
if (sampling_event_.get()) {
sampling_event_->CreateUserDecisionEvent(
ExperienceSamplingEvent::kProceed);
sampling_event_.reset(NULL);
}
// This will change the state and notify us.
download()->ValidateDangerousDownload();
return;
......@@ -559,6 +573,11 @@ void DownloadItemView::ButtonPressed(views::Button* sender,
DCHECK_EQ(discard_button_, sender);
if (model_.IsMalicious()) {
UMA_HISTOGRAM_LONG_TIMES("clickjacking.dismiss_download", warning_duration);
// ExperienceSampling: User chose to dismiss the dangerous download.
if (sampling_event_.get()) {
sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kDeny);
sampling_event_.reset(NULL);
}
shelf_->RemoveDownloadView(this);
return;
}
......@@ -1127,6 +1146,11 @@ void DownloadItemView::ClearWarningDialog() {
body_state_ = NORMAL;
drop_down_state_ = NORMAL;
// ExperienceSampling: User proceeded through the warning.
if (sampling_event_.get()) {
sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kProceed);
sampling_event_.reset(NULL);
}
// Remove the views used by the warning dialog.
if (save_button_) {
RemoveChildView(save_button_);
......@@ -1170,6 +1194,17 @@ void DownloadItemView::ShowWarningDialog() {
#endif
mode_ = model_.MightBeMalicious() ? MALICIOUS_MODE : DANGEROUS_MODE;
// ExperienceSampling: Dangerous or malicious download warning is being shown
// to the user, so we start a new SamplingEvent and track it.
std::string event_name = model_.MightBeMalicious()
? ExperienceSamplingEvent::kMaliciousDownload
: ExperienceSamplingEvent::kDangerousDownload;
sampling_event_.reset(
new ExperienceSamplingEvent(event_name,
download()->GetURL(),
download()->GetReferrerUrl(),
download()->GetBrowserContext()));
body_state_ = NORMAL;
drop_down_state_ = NORMAL;
if (mode_ == DANGEROUS_MODE) {
......
......@@ -38,6 +38,10 @@
class DownloadShelfView;
class DownloadShelfContextMenuView;
namespace extensions {
class ExperienceSamplingEvent;
}
namespace gfx {
class Image;
class ImageSkia;
......@@ -339,6 +343,10 @@ class DownloadItemView : public views::ButtonListener,
// and reload the icon.
base::FilePath last_download_item_path_;
// ExperienceSampling: This tracks dangerous/malicious downloads warning UI
// and the user's decisions about it.
scoped_ptr<extensions::ExperienceSamplingEvent> sampling_event_;
DISALLOW_COPY_AND_ASSIGN(DownloadItemView);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment