blob: 4f0bf9ff0c30ca2d9596d422fc9c52e830b77b7a [file] [log] [blame]
[email protected]c72ebfe2013-12-13 21:57:531// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/extension_message_bubble_controller.h"
6
7#include "base/bind.h"
8#include "base/metrics/histogram.h"
rdevlin.cronina28846d2015-04-30 23:12:199#include "base/strings/string_number_conversions.h"
10#include "base/strings/string_util.h"
[email protected]c72ebfe2013-12-13 21:57:5311#include "base/strings/utf_string_conversions.h"
12#include "chrome/browser/extensions/extension_message_bubble.h"
[email protected]f8454484652014-02-27 04:20:4213#include "chrome/browser/profiles/profile.h"
[email protected]c72ebfe2013-12-13 21:57:5314#include "chrome/browser/ui/browser.h"
15#include "chrome/browser/ui/browser_finder.h"
apaciblef9cfc4d2015-08-18 05:14:1416#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
[email protected]c72ebfe2013-12-13 21:57:5317#include "chrome/common/url_constants.h"
18#include "content/public/browser/user_metrics.h"
merkulova9be182e2014-10-07 14:57:5019#include "extensions/browser/extension_prefs.h"
[email protected]f47f7172014-03-19 19:27:1020#include "extensions/browser/extension_registry.h"
rdevlin.cronin0ba2a4c2015-08-06 18:40:1921#include "extensions/browser/extension_system.h"
merkulova9be182e2014-10-07 14:57:5022#include "grit/components_strings.h"
23#include "ui/base/l10n/l10n_util.h"
[email protected]c72ebfe2013-12-13 21:57:5324
25namespace extensions {
26
rdevlin.cronina28846d2015-04-30 23:12:1927namespace {
28// How many extensions to show in the bubble (max).
29const int kMaxExtensionsToShow = 7;
rdevlin.cronin0ba2a4c2015-08-06 18:40:1930
31// Whether or not to ignore the learn more link navigation for testing.
32bool g_should_ignore_learn_more_for_testing = false;
rdevlin.cronina28846d2015-04-30 23:12:1933}
34
[email protected]c72ebfe2013-12-13 21:57:5335////////////////////////////////////////////////////////////////////////////////
[email protected]4f2f353d2014-01-14 11:21:0936// ExtensionMessageBubbleController::Delegate
37
merkulova9be182e2014-10-07 14:57:5038ExtensionMessageBubbleController::Delegate::Delegate(Profile* profile)
rdevlin.cronin0ba2a4c2015-08-06 18:40:1939 : profile_(profile),
40 service_(ExtensionSystem::Get(profile)->extension_service()),
41 registry_(ExtensionRegistry::Get(profile)) {
[email protected]4f2f353d2014-01-14 11:21:0942}
43
44ExtensionMessageBubbleController::Delegate::~Delegate() {
45}
46
merkulova9be182e2014-10-07 14:57:5047base::string16 ExtensionMessageBubbleController::Delegate::GetLearnMoreLabel()
48 const {
49 return l10n_util::GetStringUTF16(IDS_LEARN_MORE);
50}
51
52bool ExtensionMessageBubbleController::Delegate::HasBubbleInfoBeenAcknowledged(
53 const std::string& extension_id) {
54 std::string pref_name = get_acknowledged_flag_pref_name();
55 if (pref_name.empty())
56 return false;
57 bool pref_state = false;
58 extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(profile_);
59 prefs->ReadPrefAsBoolean(extension_id, pref_name, &pref_state);
60 return pref_state;
61}
62
63void ExtensionMessageBubbleController::Delegate::SetBubbleInfoBeenAcknowledged(
64 const std::string& extension_id,
65 bool value) {
66 std::string pref_name = get_acknowledged_flag_pref_name();
67 if (pref_name.empty())
68 return;
69 extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(profile_);
70 prefs->UpdateExtensionPref(extension_id,
71 pref_name,
72 value ? new base::FundamentalValue(value) : NULL);
73}
74
rdevlin.cronincce78d02015-09-24 19:50:5575std::set<Profile*>*
76ExtensionMessageBubbleController::Delegate::GetProfileSet() {
77 return nullptr;
78}
79
merkulova9be182e2014-10-07 14:57:5080std::string
81ExtensionMessageBubbleController::Delegate::get_acknowledged_flag_pref_name()
82 const {
83 return acknowledged_pref_name_;
84}
85
86void
87ExtensionMessageBubbleController::Delegate::set_acknowledged_flag_pref_name(
88 std::string pref_name) {
89 acknowledged_pref_name_ = pref_name;
90}
91
[email protected]4f2f353d2014-01-14 11:21:0992////////////////////////////////////////////////////////////////////////////////
[email protected]c72ebfe2013-12-13 21:57:5393// ExtensionMessageBubbleController
94
95ExtensionMessageBubbleController::ExtensionMessageBubbleController(
rdevlin.cronin0ba2a4c2015-08-06 18:40:1996 Delegate* delegate,
97 Browser* browser)
98 : browser_(browser),
[email protected]c72ebfe2013-12-13 21:57:5399 user_action_(ACTION_BOUNDARY),
100 delegate_(delegate),
rdevlin.cronin9bd430a2015-05-08 18:06:18101 initialized_(false),
102 did_highlight_(false) {
[email protected]c72ebfe2013-12-13 21:57:53103}
104
105ExtensionMessageBubbleController::~ExtensionMessageBubbleController() {
106}
107
rdevlin.cronin0ba2a4c2015-08-06 18:40:19108Profile* ExtensionMessageBubbleController::profile() {
109 return browser_->profile();
110}
111
rdevlin.cronincce78d02015-09-24 19:50:55112bool ExtensionMessageBubbleController::ShouldShow() {
113 std::set<Profile*>* profiles = delegate_->GetProfileSet();
114 return (!profiles || !profiles->count(profile()->GetOriginalProfile())) &&
115 !GetExtensionList().empty();
116}
117
[email protected]4f58ac52013-12-17 12:00:42118std::vector<base::string16>
[email protected]c72ebfe2013-12-13 21:57:53119ExtensionMessageBubbleController::GetExtensionList() {
120 ExtensionIdList* list = GetOrCreateExtensionList();
121 if (list->empty())
[email protected]4f58ac52013-12-17 12:00:42122 return std::vector<base::string16>();
[email protected]c72ebfe2013-12-13 21:57:53123
rdevlin.cronin0ba2a4c2015-08-06 18:40:19124 ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
[email protected]4f58ac52013-12-17 12:00:42125 std::vector<base::string16> return_value;
rdevlin.cronincce78d02015-09-24 19:50:55126 for (const std::string& id : *list) {
[email protected]f47f7172014-03-19 19:27:10127 const Extension* extension =
rdevlin.cronincce78d02015-09-24 19:50:55128 registry->GetExtensionById(id, ExtensionRegistry::EVERYTHING);
129 // The extension may have been removed, since showing the bubble is an
130 // asynchronous process.
131 if (extension)
[email protected]04338722013-12-24 23:18:05132 return_value.push_back(base::UTF8ToUTF16(extension->name()));
[email protected]c72ebfe2013-12-13 21:57:53133 }
134 return return_value;
135}
136
rdevlin.cronina28846d2015-04-30 23:12:19137base::string16 ExtensionMessageBubbleController::GetExtensionListForDisplay() {
138 if (!delegate_->ShouldShowExtensionList())
139 return base::string16();
140
141 std::vector<base::string16> extension_list = GetExtensionList();
142 if (extension_list.size() > kMaxExtensionsToShow) {
143 int old_size = extension_list.size();
144 extension_list.erase(extension_list.begin() + kMaxExtensionsToShow,
145 extension_list.end());
146 extension_list.push_back(delegate_->GetOverflowText(base::IntToString16(
147 old_size - kMaxExtensionsToShow)));
148 }
149 const base::char16 bullet_point = 0x2022;
150 base::string16 prefix = bullet_point + base::ASCIIToUTF16(" ");
151 for (base::string16& str : extension_list)
152 str.insert(0, prefix);
brettwd94a22142015-07-15 05:19:26153 return base::JoinString(extension_list, base::ASCIIToUTF16("\n"));
rdevlin.cronina28846d2015-04-30 23:12:19154}
155
[email protected]c72ebfe2013-12-13 21:57:53156const ExtensionIdList& ExtensionMessageBubbleController::GetExtensionIdList() {
157 return *GetOrCreateExtensionList();
158}
159
[email protected]94b8a51a2014-03-26 20:57:55160bool ExtensionMessageBubbleController::CloseOnDeactivate() { return false; }
161
rdevlin.cronin70fc6052015-04-15 17:49:06162void ExtensionMessageBubbleController::HighlightExtensionsIfNecessary() {
rdevlin.cronin9bd430a2015-05-08 18:06:18163 if (delegate_->ShouldHighlightExtensions() && !did_highlight_) {
164 did_highlight_ = true;
rdevlin.cronin70fc6052015-04-15 17:49:06165 const ExtensionIdList& extension_ids = GetExtensionIdList();
166 DCHECK(!extension_ids.empty());
apaciblef9cfc4d2015-08-18 05:14:14167 ToolbarActionsModel::Get(profile())->HighlightActions(
168 extension_ids, ToolbarActionsModel::HIGHLIGHT_WARNING);
rdevlin.cronin70fc6052015-04-15 17:49:06169 }
170}
rdevlin.cronin86261efe2015-04-13 17:42:30171
rdevlin.cronin70fc6052015-04-15 17:49:06172void ExtensionMessageBubbleController::Show(ExtensionMessageBubble* bubble) {
[email protected]c72ebfe2013-12-13 21:57:53173 bubble->Show();
174}
175
176void ExtensionMessageBubbleController::OnBubbleAction() {
177 DCHECK_EQ(ACTION_BOUNDARY, user_action_);
178 user_action_ = ACTION_EXECUTE;
179
180 delegate_->LogAction(ACTION_EXECUTE);
181 delegate_->PerformAction(*GetOrCreateExtensionList());
rdevlin.cronin9bd430a2015-05-08 18:06:18182
183 OnClose();
[email protected]c72ebfe2013-12-13 21:57:53184}
185
186void ExtensionMessageBubbleController::OnBubbleDismiss() {
[email protected]a74569b2014-03-25 02:56:30187 // OnBubbleDismiss() can be called twice when we receive multiple
188 // "OnWidgetDestroying" notifications (this can at least happen when we close
189 // a window with a notification open). Handle this gracefully.
190 if (user_action_ != ACTION_BOUNDARY) {
191 DCHECK(user_action_ == ACTION_DISMISS);
192 return;
193 }
194
[email protected]c72ebfe2013-12-13 21:57:53195 user_action_ = ACTION_DISMISS;
196
197 delegate_->LogAction(ACTION_DISMISS);
rdevlin.cronin9bd430a2015-05-08 18:06:18198
199 OnClose();
[email protected]c72ebfe2013-12-13 21:57:53200}
201
202void ExtensionMessageBubbleController::OnLinkClicked() {
203 DCHECK_EQ(ACTION_BOUNDARY, user_action_);
204 user_action_ = ACTION_LEARN_MORE;
205
206 delegate_->LogAction(ACTION_LEARN_MORE);
rdevlin.cronin0ba2a4c2015-08-06 18:40:19207 if (!g_should_ignore_learn_more_for_testing) {
208 browser_->OpenURL(
[email protected]c72ebfe2013-12-13 21:57:53209 content::OpenURLParams(delegate_->GetLearnMoreUrl(),
210 content::Referrer(),
211 NEW_FOREGROUND_TAB,
Sylvain Defresnec6ccc77d2014-09-19 10:19:35212 ui::PAGE_TRANSITION_LINK,
[email protected]c72ebfe2013-12-13 21:57:53213 false));
214 }
rdevlin.cronin9bd430a2015-05-08 18:06:18215 OnClose();
[email protected]c72ebfe2013-12-13 21:57:53216}
217
rdevlin.cronin0ba2a4c2015-08-06 18:40:19218// static
219void ExtensionMessageBubbleController::set_should_ignore_learn_more_for_testing(
220 bool should_ignore) {
221 g_should_ignore_learn_more_for_testing = should_ignore;
222}
223
[email protected]c72ebfe2013-12-13 21:57:53224void ExtensionMessageBubbleController::AcknowledgeExtensions() {
225 ExtensionIdList* list = GetOrCreateExtensionList();
226 for (ExtensionIdList::const_iterator it = list->begin();
227 it != list->end(); ++it)
228 delegate_->AcknowledgeExtension(*it, user_action_);
229}
230
231ExtensionIdList* ExtensionMessageBubbleController::GetOrCreateExtensionList() {
[email protected]c72ebfe2013-12-13 21:57:53232 if (!initialized_) {
rdevlin.cronincce78d02015-09-24 19:50:55233 ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
234 scoped_ptr<const ExtensionSet> all_extensions;
235 if (!delegate_->ShouldLimitToEnabledExtensions())
236 all_extensions = registry->GenerateInstalledExtensionsSet();
237 const ExtensionSet& extensions_to_check =
238 all_extensions ? *all_extensions : registry->enabled_extensions();
239 for (const scoped_refptr<const Extension>& extension :
240 extensions_to_check) {
241 if (delegate_->ShouldIncludeExtension(extension.get()))
242 extension_list_.push_back(extension->id());
[email protected]c72ebfe2013-12-13 21:57:53243 }
244
245 delegate_->LogExtensionCount(extension_list_.size());
246 initialized_ = true;
247 }
248
249 return &extension_list_;
250}
251
rdevlin.cronin9bd430a2015-05-08 18:06:18252void ExtensionMessageBubbleController::OnClose() {
253 AcknowledgeExtensions();
254 if (did_highlight_)
apaciblef9cfc4d2015-08-18 05:14:14255 ToolbarActionsModel::Get(profile())->StopHighlighting();
rdevlin.cronin9bd430a2015-05-08 18:06:18256}
257
[email protected]c72ebfe2013-12-13 21:57:53258} // namespace extensions