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/startup/startup_browser_creator.h"
13 #include "chrome/common/extensions/manifest_handlers/settings_overrides_handler.h"
14 #include "chrome/common/url_constants.h"
15 #include "chrome/grit/chromium_strings.h"
16 #include "chrome/grit/generated_resources.h"
17 #include "extensions/browser/extension_prefs.h"
18 #include "extensions/browser/extension_registry.h"
19 #include "extensions/browser/extension_system.h"
20 #include "grit/components_strings.h"
21 #include "ui/base/l10n/l10n_util.h"
23 namespace extensions
{
27 // Whether the user has been notified about extension taking over some aspect of
28 // the user's settings (homepage, startup pages, or search engine).
29 const char kSettingsBubbleAcknowledged
[] = "ack_settings_bubble";
31 ////////////////////////////////////////////////////////////////////////////////
32 // SettingsApiBubbleDelegate
34 class SettingsApiBubbleDelegate
35 : public ExtensionMessageBubbleController::Delegate
{
37 explicit SettingsApiBubbleDelegate(ExtensionService
* service
,
39 SettingsApiOverrideType type
);
40 ~SettingsApiBubbleDelegate() override
;
42 // ExtensionMessageBubbleController::Delegate methods.
43 bool ShouldIncludeExtension(const std::string
& extension_id
) override
;
44 void AcknowledgeExtension(
45 const std::string
& extension_id
,
46 ExtensionMessageBubbleController::BubbleAction user_action
) override
;
47 void PerformAction(const ExtensionIdList
& list
) override
;
48 base::string16
GetTitle() const override
;
49 base::string16
GetMessageBody(bool anchored_to_browser_action
,
50 int extension_count
) const override
;
51 base::string16
GetOverflowText(
52 const base::string16
& overflow_count
) const override
;
53 GURL
GetLearnMoreUrl() const override
;
54 base::string16
GetActionButtonLabel() const override
;
55 base::string16
GetDismissButtonLabel() const override
;
56 bool ShouldShowExtensionList() const override
;
57 bool ShouldHighlightExtensions() const override
;
58 void LogExtensionCount(size_t count
) override
;
60 ExtensionMessageBubbleController::BubbleAction action
) override
;
63 // Our extension service. Weak, not owned by us.
64 ExtensionService
* service_
;
66 // A weak pointer to the profile we are associated with. Not owned by us.
69 // The type of settings override this bubble will report on. This can be, for
70 // example, a bubble to notify the user that the search engine has been
71 // changed by an extension (or homepage/startup pages/etc).
72 SettingsApiOverrideType type_
;
74 // The ID of the extension we are showing the bubble for.
75 std::string extension_id_
;
77 DISALLOW_COPY_AND_ASSIGN(SettingsApiBubbleDelegate
);
80 SettingsApiBubbleDelegate::SettingsApiBubbleDelegate(
81 ExtensionService
* service
,
83 SettingsApiOverrideType type
)
84 : ExtensionMessageBubbleController::Delegate(profile
),
87 set_acknowledged_flag_pref_name(kSettingsBubbleAcknowledged
);
90 SettingsApiBubbleDelegate::~SettingsApiBubbleDelegate() {}
92 bool SettingsApiBubbleDelegate::ShouldIncludeExtension(
93 const std::string
& extension_id
) {
94 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile());
95 const Extension
* extension
=
96 registry
->GetExtensionById(extension_id
, ExtensionRegistry::ENABLED
);
98 return false; // The extension provided is no longer enabled.
100 if (HasBubbleInfoBeenAcknowledged(extension_id
))
103 const Extension
* override
= NULL
;
105 case extensions::BUBBLE_TYPE_HOME_PAGE
:
106 override
= extensions::GetExtensionOverridingHomepage(profile());
108 case extensions::BUBBLE_TYPE_STARTUP_PAGES
:
109 override
= extensions::GetExtensionOverridingStartupPages(profile());
111 case extensions::BUBBLE_TYPE_SEARCH_ENGINE
:
112 override
= extensions::GetExtensionOverridingSearchEngine(profile());
116 if (!override
|| override
->id() != extension
->id())
119 extension_id_
= extension_id
;
123 void SettingsApiBubbleDelegate::AcknowledgeExtension(
124 const std::string
& extension_id
,
125 ExtensionMessageBubbleController::BubbleAction user_action
) {
126 if (user_action
!= ExtensionMessageBubbleController::ACTION_EXECUTE
)
127 SetBubbleInfoBeenAcknowledged(extension_id
, true);
130 void SettingsApiBubbleDelegate::PerformAction(const ExtensionIdList
& list
) {
131 for (size_t i
= 0; i
< list
.size(); ++i
) {
132 service_
->DisableExtension(list
[i
], Extension::DISABLE_USER_ACTION
);
136 base::string16
SettingsApiBubbleDelegate::GetTitle() const {
138 case BUBBLE_TYPE_HOME_PAGE
:
139 return l10n_util::GetStringUTF16(
140 IDS_EXTENSIONS_SETTINGS_API_TITLE_HOME_PAGE_BUBBLE
);
141 case BUBBLE_TYPE_STARTUP_PAGES
:
142 return l10n_util::GetStringUTF16(
143 IDS_EXTENSIONS_SETTINGS_API_TITLE_STARTUP_PAGES_BUBBLE
);
144 case BUBBLE_TYPE_SEARCH_ENGINE
:
145 return l10n_util::GetStringUTF16(
146 IDS_EXTENSIONS_SETTINGS_API_TITLE_SEARCH_ENGINE_BUBBLE
);
149 return base::string16();
152 base::string16
SettingsApiBubbleDelegate::GetMessageBody(
153 bool anchored_to_browser_action
,
154 int extension_count
) const {
155 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile());
156 const Extension
* extension
=
157 registry
->GetExtensionById(extension_id_
, ExtensionRegistry::ENABLED
);
158 const SettingsOverrides
* settings
=
159 extension
? SettingsOverrides::Get(extension
) : NULL
;
160 if (!extension
|| !settings
) {
162 return base::string16();
165 bool home_change
= settings
->homepage
!= NULL
;
166 bool startup_change
= !settings
->startup_pages
.empty();
167 bool search_change
= settings
->search_engine
!= NULL
;
169 int first_line_id
= 0;
170 int second_line_id
= 0;
174 case BUBBLE_TYPE_HOME_PAGE
:
175 first_line_id
= anchored_to_browser_action
?
176 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_HOME_PAGE_SPECIFIC
:
177 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_HOME_PAGE
;
178 if (startup_change
&& search_change
) {
180 IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_AND_SEARCH
;
181 } else if (startup_change
) {
182 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_PAGES
;
183 } else if (search_change
) {
184 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_SEARCH_ENGINE
;
187 case BUBBLE_TYPE_STARTUP_PAGES
:
188 first_line_id
= anchored_to_browser_action
?
189 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_START_PAGES_SPECIFIC
:
190 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_START_PAGES
;
191 if (home_change
&& search_change
) {
193 IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_HOME_AND_SEARCH
;
194 } else if (home_change
) {
195 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_HOME_PAGE
;
196 } else if (search_change
) {
197 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_SEARCH_ENGINE
;
200 case BUBBLE_TYPE_SEARCH_ENGINE
:
201 first_line_id
= anchored_to_browser_action
?
202 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_SEARCH_ENGINE_SPECIFIC
:
203 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_SEARCH_ENGINE
;
204 if (startup_change
&& home_change
)
205 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_AND_HOME
;
206 else if (startup_change
)
207 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_PAGES
;
208 else if (home_change
)
209 second_line_id
= IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_HOME_PAGE
;
212 DCHECK_NE(0, first_line_id
);
213 body
= anchored_to_browser_action
?
214 l10n_util::GetStringUTF16(first_line_id
) :
215 l10n_util::GetStringFUTF16(first_line_id
,
216 base::UTF8ToUTF16(extension
->name()));
218 body
+= l10n_util::GetStringUTF16(second_line_id
);
220 body
+= l10n_util::GetStringUTF16(
221 IDS_EXTENSIONS_SETTINGS_API_THIRD_LINE_CONFIRMATION
);
226 base::string16
SettingsApiBubbleDelegate::GetOverflowText(
227 const base::string16
& overflow_count
) const {
228 // Does not have more than one extension in the list at a time.
230 return base::string16();
233 GURL
SettingsApiBubbleDelegate::GetLearnMoreUrl() const {
234 return GURL(chrome::kExtensionControlledSettingLearnMoreURL
);
237 base::string16
SettingsApiBubbleDelegate::GetActionButtonLabel() const {
238 return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_RESTORE_SETTINGS
);
241 base::string16
SettingsApiBubbleDelegate::GetDismissButtonLabel() const {
242 return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_KEEP_CHANGES
);
245 bool SettingsApiBubbleDelegate::ShouldShowExtensionList() const {
249 bool SettingsApiBubbleDelegate::ShouldHighlightExtensions() const {
250 return type_
== BUBBLE_TYPE_STARTUP_PAGES
;
253 void SettingsApiBubbleDelegate::LogExtensionCount(size_t count
) {
256 void SettingsApiBubbleDelegate::LogAction(
257 ExtensionMessageBubbleController::BubbleAction action
) {
259 case BUBBLE_TYPE_HOME_PAGE
:
260 UMA_HISTOGRAM_ENUMERATION(
261 "ExtensionOverrideBubble.SettingsApiUserSelectionHomePage",
263 ExtensionMessageBubbleController::ACTION_BOUNDARY
);
265 case BUBBLE_TYPE_STARTUP_PAGES
:
266 UMA_HISTOGRAM_ENUMERATION(
267 "ExtensionOverrideBubble.SettingsApiUserSelectionStartupPage",
269 ExtensionMessageBubbleController::ACTION_BOUNDARY
);
271 case BUBBLE_TYPE_SEARCH_ENGINE
:
272 UMA_HISTOGRAM_ENUMERATION(
273 "ExtensionOverrideBubble.SettingsApiUserSelectionSearchEngine",
275 ExtensionMessageBubbleController::ACTION_BOUNDARY
);
282 ////////////////////////////////////////////////////////////////////////////////
283 // SettingsApiBubbleController
285 SettingsApiBubbleController::SettingsApiBubbleController(
287 SettingsApiOverrideType type
)
288 : ExtensionMessageBubbleController(
289 new SettingsApiBubbleDelegate(
290 ExtensionSystem::Get(profile
)->extension_service(),
297 SettingsApiBubbleController::~SettingsApiBubbleController() {}
299 bool SettingsApiBubbleController::ShouldShow() {
300 const Extension
* extension
= nullptr;
302 case BUBBLE_TYPE_HOME_PAGE
:
303 extension
= GetExtensionOverridingHomepage(profile_
);
305 case BUBBLE_TYPE_SEARCH_ENGINE
:
306 extension
= GetExtensionOverridingSearchEngine(profile_
);
308 case BUBBLE_TYPE_STARTUP_PAGES
:
309 extension
= GetExtensionOverridingStartupPages(profile_
);
316 if (delegate()->HasBubbleInfoBeenAcknowledged(extension
->id()))
319 if (!delegate()->ShouldIncludeExtension(extension
->id()))
322 // If the browser is showing the 'Chrome crashed' infobar, it won't be showing
323 // the startup pages, so there's no point in showing the bubble now.
324 if (type_
== BUBBLE_TYPE_STARTUP_PAGES
)
325 return profile_
->GetLastSessionExitType() != Profile::EXIT_CRASHED
;
330 bool SettingsApiBubbleController::CloseOnDeactivate() {
331 // Startup bubbles tend to get lost in the focus storm that happens on
332 // startup. Other types should dismiss on focus loss.
333 return type_
!= BUBBLE_TYPE_STARTUP_PAGES
;
336 } // namespace extensions