1 // Copyright 2014 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.
5 #include "chrome/browser/extensions/settings_api_bubble_controller.h"
7 #include "base/metrics/histogram.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/extensions/extension_service.h"
10 #include "chrome/browser/extensions/settings_api_helpers.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/startup/startup_browser_creator.h"
14 #include "chrome/common/extensions/manifest_handlers/settings_overrides_handler.h"
15 #include "chrome/common/url_constants.h"
16 #include "chrome/grit/chromium_strings.h"
17 #include "chrome/grit/generated_resources.h"
18 #include "extensions/browser/extension_prefs.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/browser/extension_system.h"
21 #include "grit/components_strings.h"
22 #include "ui/base/l10n/l10n_util.h"
24 namespace extensions
{
28 // Whether the user has been notified about extension taking over some aspect of
29 // the user's settings (homepage, startup pages, or search engine).
30 const char kSettingsBubbleAcknowledged
[] = "ack_settings_bubble";
32 ////////////////////////////////////////////////////////////////////////////////
33 // SettingsApiBubbleDelegate
35 class SettingsApiBubbleDelegate
36 : public ExtensionMessageBubbleController::Delegate
{
38 SettingsApiBubbleDelegate(Profile
* profile
, SettingsApiOverrideType type
);
39 ~SettingsApiBubbleDelegate() override
;
41 // ExtensionMessageBubbleController::Delegate methods.
42 bool ShouldIncludeExtension(const std::string
& extension_id
) override
;
43 void AcknowledgeExtension(
44 const std::string
& extension_id
,
45 ExtensionMessageBubbleController::BubbleAction user_action
) override
;
46 void PerformAction(const ExtensionIdList
& list
) override
;
47 base::string16
GetTitle() const override
;
48 base::string16
GetMessageBody(bool anchored_to_browser_action
,
49 int extension_count
) const override
;
50 base::string16
GetOverflowText(
51 const base::string16
& overflow_count
) const override
;
52 GURL
GetLearnMoreUrl() const override
;
53 base::string16
GetActionButtonLabel() const override
;
54 base::string16
GetDismissButtonLabel() const override
;
55 bool ShouldShowExtensionList() const override
;
56 bool ShouldHighlightExtensions() const override
;
57 void LogExtensionCount(size_t count
) override
;
59 ExtensionMessageBubbleController::BubbleAction action
) override
;
62 // The type of settings override this bubble will report on. This can be, for
63 // example, a bubble to notify the user that the search engine has been
64 // changed by an extension (or homepage/startup pages/etc).
65 SettingsApiOverrideType type_
;
67 // The ID of the extension we are showing the bubble for.
68 std::string extension_id_
;
70 DISALLOW_COPY_AND_ASSIGN(SettingsApiBubbleDelegate
);
73 SettingsApiBubbleDelegate::SettingsApiBubbleDelegate(
75 SettingsApiOverrideType type
)
76 : ExtensionMessageBubbleController::Delegate(profile
),
78 set_acknowledged_flag_pref_name(kSettingsBubbleAcknowledged
);
81 SettingsApiBubbleDelegate::~SettingsApiBubbleDelegate() {}
83 bool SettingsApiBubbleDelegate::ShouldIncludeExtension(
84 const std::string
& extension_id
) {
85 const Extension
* extension
=
86 registry()->GetExtensionById(extension_id
, ExtensionRegistry::ENABLED
);
88 return false; // The extension provided is no longer enabled.
90 if (HasBubbleInfoBeenAcknowledged(extension_id
))
93 const Extension
* override
= NULL
;
95 case extensions::BUBBLE_TYPE_HOME_PAGE
:
96 override
= extensions::GetExtensionOverridingHomepage(profile());
98 case extensions::BUBBLE_TYPE_STARTUP_PAGES
:
99 override
= extensions::GetExtensionOverridingStartupPages(profile());
101 case extensions::BUBBLE_TYPE_SEARCH_ENGINE
:
102 override
= extensions::GetExtensionOverridingSearchEngine(profile());
106 if (!override
|| override
->id() != extension
->id())
109 extension_id_
= extension_id
;
113 void SettingsApiBubbleDelegate::AcknowledgeExtension(
114 const std::string
& extension_id
,
115 ExtensionMessageBubbleController::BubbleAction user_action
) {
116 if (user_action
!= ExtensionMessageBubbleController::ACTION_EXECUTE
)
117 SetBubbleInfoBeenAcknowledged(extension_id
, true);
120 void SettingsApiBubbleDelegate::PerformAction(const ExtensionIdList
& list
) {
121 for (size_t i
= 0; i
< list
.size(); ++i
) {
122 service()->DisableExtension(list
[i
], Extension::DISABLE_USER_ACTION
);
126 base::string16
SettingsApiBubbleDelegate::GetTitle() const {
128 case BUBBLE_TYPE_HOME_PAGE
:
129 return l10n_util::GetStringUTF16(
130 IDS_EXTENSIONS_SETTINGS_API_TITLE_HOME_PAGE_BUBBLE
);
131 case BUBBLE_TYPE_STARTUP_PAGES
:
132 return l10n_util::GetStringUTF16(
133 IDS_EXTENSIONS_SETTINGS_API_TITLE_STARTUP_PAGES_BUBBLE
);
134 case BUBBLE_TYPE_SEARCH_ENGINE
:
135 return l10n_util::GetStringUTF16(
136 IDS_EXTENSIONS_SETTINGS_API_TITLE_SEARCH_ENGINE_BUBBLE
);
139 return base::string16();
142 base::string16
SettingsApiBubbleDelegate::GetMessageBody(
143 bool anchored_to_browser_action
,
144 int extension_count
) const {
145 const Extension
* extension
=
146 registry()->GetExtensionById(extension_id_
, ExtensionRegistry::ENABLED
);
147 const SettingsOverrides
* settings
=
148 extension
? SettingsOverrides::Get(extension
) : NULL
;
149 if (!extension
|| !settings
) {
151 return base::string16();
154 bool home_change
= settings
->homepage
!= NULL
;
155 bool startup_change
= !settings
->startup_pages
.empty();
156 bool search_change
= settings
->search_engine
!= NULL
;
158 int first_line_id
= 0;
159 int second_line_id
= 0;
163 case BUBBLE_TYPE_HOME_PAGE
:
164 first_line_id
= anchored_to_browser_action
?
165 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_HOME_PAGE_SPECIFIC
:
166 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_HOME_PAGE
;
167 if (startup_change
&& search_change
) {
169 IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_AND_SEARCH
;
170 } else if (startup_change
) {
171 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_PAGES
;
172 } else if (search_change
) {
173 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_SEARCH_ENGINE
;
176 case BUBBLE_TYPE_STARTUP_PAGES
:
177 first_line_id
= anchored_to_browser_action
?
178 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_START_PAGES_SPECIFIC
:
179 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_START_PAGES
;
180 if (home_change
&& search_change
) {
182 IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_HOME_AND_SEARCH
;
183 } else if (home_change
) {
184 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_HOME_PAGE
;
185 } else if (search_change
) {
186 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_SEARCH_ENGINE
;
189 case BUBBLE_TYPE_SEARCH_ENGINE
:
190 first_line_id
= anchored_to_browser_action
?
191 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_SEARCH_ENGINE_SPECIFIC
:
192 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_SEARCH_ENGINE
;
193 if (startup_change
&& home_change
)
194 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_AND_HOME
;
195 else if (startup_change
)
196 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_PAGES
;
197 else if (home_change
)
198 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_HOME_PAGE
;
201 DCHECK_NE(0, first_line_id
);
202 body
= anchored_to_browser_action
?
203 l10n_util::GetStringUTF16(first_line_id
) :
204 l10n_util::GetStringFUTF16(first_line_id
,
205 base::UTF8ToUTF16(extension
->name()));
207 body
+= l10n_util::GetStringUTF16(second_line_id
);
209 body
+= l10n_util::GetStringUTF16(
210 IDS_EXTENSIONS_SETTINGS_API_THIRD_LINE_CONFIRMATION
);
215 base::string16
SettingsApiBubbleDelegate::GetOverflowText(
216 const base::string16
& overflow_count
) const {
217 // Does not have more than one extension in the list at a time.
219 return base::string16();
222 GURL
SettingsApiBubbleDelegate::GetLearnMoreUrl() const {
223 return GURL(chrome::kExtensionControlledSettingLearnMoreURL
);
226 base::string16
SettingsApiBubbleDelegate::GetActionButtonLabel() const {
227 return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_RESTORE_SETTINGS
);
230 base::string16
SettingsApiBubbleDelegate::GetDismissButtonLabel() const {
231 return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_KEEP_CHANGES
);
234 bool SettingsApiBubbleDelegate::ShouldShowExtensionList() const {
238 bool SettingsApiBubbleDelegate::ShouldHighlightExtensions() const {
239 return type_
== BUBBLE_TYPE_STARTUP_PAGES
;
242 void SettingsApiBubbleDelegate::LogExtensionCount(size_t count
) {
245 void SettingsApiBubbleDelegate::LogAction(
246 ExtensionMessageBubbleController::BubbleAction action
) {
248 case BUBBLE_TYPE_HOME_PAGE
:
249 UMA_HISTOGRAM_ENUMERATION(
250 "ExtensionOverrideBubble.SettingsApiUserSelectionHomePage",
252 ExtensionMessageBubbleController::ACTION_BOUNDARY
);
254 case BUBBLE_TYPE_STARTUP_PAGES
:
255 UMA_HISTOGRAM_ENUMERATION(
256 "ExtensionOverrideBubble.SettingsApiUserSelectionStartupPage",
258 ExtensionMessageBubbleController::ACTION_BOUNDARY
);
260 case BUBBLE_TYPE_SEARCH_ENGINE
:
261 UMA_HISTOGRAM_ENUMERATION(
262 "ExtensionOverrideBubble.SettingsApiUserSelectionSearchEngine",
264 ExtensionMessageBubbleController::ACTION_BOUNDARY
);
271 ////////////////////////////////////////////////////////////////////////////////
272 // SettingsApiBubbleController
274 SettingsApiBubbleController::SettingsApiBubbleController(
276 SettingsApiOverrideType type
)
277 : ExtensionMessageBubbleController(
278 new SettingsApiBubbleDelegate(browser
->profile(), type
),
282 SettingsApiBubbleController::~SettingsApiBubbleController() {}
284 bool SettingsApiBubbleController::ShouldShow() {
285 const Extension
* extension
= nullptr;
287 case BUBBLE_TYPE_HOME_PAGE
:
288 extension
= GetExtensionOverridingHomepage(profile());
290 case BUBBLE_TYPE_SEARCH_ENGINE
:
291 extension
= GetExtensionOverridingSearchEngine(profile());
293 case BUBBLE_TYPE_STARTUP_PAGES
:
294 extension
= GetExtensionOverridingStartupPages(profile());
301 if (delegate()->HasBubbleInfoBeenAcknowledged(extension
->id()))
304 if (!delegate()->ShouldIncludeExtension(extension
->id()))
307 // If the browser is showing the 'Chrome crashed' infobar, it won't be showing
308 // the startup pages, so there's no point in showing the bubble now.
309 if (type_
== BUBBLE_TYPE_STARTUP_PAGES
)
310 return profile()->GetLastSessionExitType() != Profile::EXIT_CRASHED
;
315 bool SettingsApiBubbleController::CloseOnDeactivate() {
316 // Startup bubbles tend to get lost in the focus storm that happens on
317 // startup. Other types should dismiss on focus loss.
318 return type_
!= BUBBLE_TYPE_STARTUP_PAGES
;
321 } // namespace extensions