blob: 836da3353ba03511347bd13fc3dc72bf6055ea39 [file] [log] [blame]
// Copyright (c) 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/extensions/suspicious_extension_bubble_controller.h"
#include "base/bind.h"
#include "base/metrics/histogram.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/suspicious_extension_bubble.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/user_metrics.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
namespace {
// UMA histogram constants.
enum UmaWipeoutHistogramOptions {
ACTION_LEARN_MORE = 0,
ACTION_DISMISS,
ACTION_BOUNDARY, // Must be the last value.
};
static base::LazyInstance<extensions::ProfileKeyedAPIFactory<
extensions::SuspiciousExtensionBubbleController> >
g_factory = LAZY_INSTANCE_INITIALIZER;
} // namespace
namespace extensions {
////////////////////////////////////////////////////////////////////////////////
// SuspiciousExtensionBubbleController
SuspiciousExtensionBubbleController::SuspiciousExtensionBubbleController(
Profile* profile)
: service_(extensions::ExtensionSystem::Get(profile)->extension_service()),
profile_(profile),
has_notified_(false) {
}
SuspiciousExtensionBubbleController::~SuspiciousExtensionBubbleController() {
}
// static
ProfileKeyedAPIFactory<SuspiciousExtensionBubbleController>*
SuspiciousExtensionBubbleController::GetFactoryInstance() {
return &g_factory.Get();
}
// static
SuspiciousExtensionBubbleController* SuspiciousExtensionBubbleController::Get(
Profile* profile) {
return ProfileKeyedAPIFactory<
SuspiciousExtensionBubbleController>::GetForProfile(profile);
}
bool SuspiciousExtensionBubbleController::HasSuspiciousExtensions() {
if (has_notified_)
return false;
if (!service_)
return false; // Can happen during tests.
suspicious_extensions_.clear();
ExtensionPrefs* prefs = service_->extension_prefs();
scoped_ptr<const ExtensionSet> extension_set(
service_->GenerateInstalledExtensionsSet());
for (ExtensionSet::const_iterator it = extension_set->begin();
it != extension_set->end(); ++it) {
std::string id = (*it)->id();
if (!prefs->IsExtensionDisabled(id))
continue;
int disble_reasons = prefs->GetDisableReasons(id);
if (disble_reasons & Extension::DISABLE_NOT_VERIFIED) {
if (prefs->HasWipeoutBeenAcknowledged(id))
continue;
suspicious_extensions_.push_back(id);
}
}
UMA_HISTOGRAM_COUNTS_100(
"SuspiciousExtensionBubble.ExtensionWipeoutCount",
suspicious_extensions_.size());
has_notified_ = true;
return !suspicious_extensions_.empty();
}
void SuspiciousExtensionBubbleController::Show(
SuspiciousExtensionBubble* bubble) {
// Wire up all the callbacks, to get notified what actions the user took.
base::Closure button_callback =
base::Bind(&SuspiciousExtensionBubbleController::OnBubbleDismiss,
base::Unretained(this));
base::Closure link_callback =
base::Bind(&SuspiciousExtensionBubbleController::OnLinkClicked,
base::Unretained(this));
bubble->OnButtonClicked(button_callback);
bubble->OnLinkClicked(link_callback);
content::RecordAction(
content::UserMetricsAction("ExtensionWipeoutNotificationShown"));
bubble->Show();
}
string16 SuspiciousExtensionBubbleController::GetTitle() {
return l10n_util::GetStringUTF16(IDS_EXTENSIONS_SUSPICIOUS_DISABLED_TITLE);
}
string16 SuspiciousExtensionBubbleController::GetMessageBody() {
return l10n_util::GetStringFUTF16(IDS_EXTENSIONS_SUSPICIOUS_DISABLED_BODY,
l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE));
}
string16 SuspiciousExtensionBubbleController::GetOverflowText(
const base::string16& overflow_count) {
base::string16 overflow_string = l10n_util::GetStringUTF16(
IDS_EXTENSIONS_SUSPICIOUS_DISABLED_AND_N_MORE);
base::string16 new_string;
// Just before string freeze, we checked in # as a substitution value for
// this string, whereas we should have used $1. It was discovered too late,
// so we do the substitution by hand in that case.
if (overflow_string.find(ASCIIToUTF16("#")) != base::string16::npos) {
base::ReplaceChars(overflow_string, ASCIIToUTF16("#").c_str(),
overflow_count, &new_string);
} else {
new_string = l10n_util::GetStringFUTF16(
IDS_EXTENSIONS_SUSPICIOUS_DISABLED_AND_N_MORE,
overflow_count);
}
return new_string;
}
string16 SuspiciousExtensionBubbleController::GetLearnMoreLabel() {
return l10n_util::GetStringUTF16(IDS_LEARN_MORE);
}
string16 SuspiciousExtensionBubbleController::GetDismissButtonLabel() {
return l10n_util::GetStringUTF16(IDS_EXTENSIONS_SUSPICIOUS_DISABLED_BUTTON);
}
std::vector<string16>
SuspiciousExtensionBubbleController::GetSuspiciousExtensionNames() {
if (suspicious_extensions_.empty())
return std::vector<string16>();
std::vector<string16> return_value;
for (ExtensionIdList::const_iterator it = suspicious_extensions_.begin();
it != suspicious_extensions_.end(); ++it) {
const Extension* extension = service_->GetInstalledExtension(*it);
if (extension) {
return_value.push_back(UTF8ToUTF16(extension->name()));
} else {
return_value.push_back(
ASCIIToUTF16(std::string("(unknown name) ") + *it));
// TODO(finnur): Add this as a string to the grd, for next milestone.
}
}
return return_value;
}
void SuspiciousExtensionBubbleController::OnBubbleDismiss() {
content::RecordAction(
content::UserMetricsAction("SuspiciousExtensionBubbleDismissed"));
UMA_HISTOGRAM_ENUMERATION("SuspiciousExtensionBubble.UserSelection",
ACTION_DISMISS, ACTION_BOUNDARY);
AcknowledgeWipeout();
}
void SuspiciousExtensionBubbleController::OnLinkClicked() {
UMA_HISTOGRAM_ENUMERATION("SuspiciousExtensionBubble.UserSelection",
ACTION_LEARN_MORE, ACTION_BOUNDARY);
Browser* browser =
chrome::FindBrowserWithProfile(profile_, chrome::GetActiveDesktop());
if (browser) {
browser->OpenURL(
content::OpenURLParams(GURL(chrome::kChromeUIExtensionsURL),
content::Referrer(),
NEW_FOREGROUND_TAB,
content::PAGE_TRANSITION_LINK,
false));
}
AcknowledgeWipeout();
}
void SuspiciousExtensionBubbleController::AcknowledgeWipeout() {
ExtensionPrefs* prefs = service_->extension_prefs();
for (ExtensionIdList::const_iterator it = suspicious_extensions_.begin();
it != suspicious_extensions_.end(); ++it) {
prefs->SetWipeoutAcknowledged(*it, true);
}
}
template <>
void ProfileKeyedAPIFactory<
SuspiciousExtensionBubbleController>::DeclareFactoryDependencies() {
DependsOn(extensions::ExtensionSystemFactory::GetInstance());
}
} // namespace extensions