1 // Copyright (c) 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 "chrome/browser/extensions/extension_service.h"
9 #include "chrome/browser/extensions/extension_toolbar_model.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 void OnClose() override
;
49 base::string16
GetTitle() const override
;
50 base::string16
GetMessageBody(bool anchored_to_browser_action
) 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 void LogExtensionCount(size_t count
) override
;
59 ExtensionMessageBubbleController::BubbleAction action
) override
;
62 // Our extension service. Weak, not owned by us.
63 ExtensionService
* service_
;
65 // A weak pointer to the profile we are associated with. Not owned by us.
68 // The type of settings override this bubble will report on. This can be, for
69 // example, a bubble to notify the user that the search engine has been
70 // changed by an extension (or homepage/startup pages/etc).
71 SettingsApiOverrideType type_
;
73 // The ID of the extension we are showing the bubble for.
74 std::string extension_id_
;
76 DISALLOW_COPY_AND_ASSIGN(SettingsApiBubbleDelegate
);
79 SettingsApiBubbleDelegate::SettingsApiBubbleDelegate(
80 ExtensionService
* service
,
82 SettingsApiOverrideType type
)
83 : ExtensionMessageBubbleController::Delegate(profile
),
86 set_acknowledged_flag_pref_name(kSettingsBubbleAcknowledged
);
89 SettingsApiBubbleDelegate::~SettingsApiBubbleDelegate() {}
91 bool SettingsApiBubbleDelegate::ShouldIncludeExtension(
92 const std::string
& extension_id
) {
93 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile());
94 const Extension
* extension
=
95 registry
->GetExtensionById(extension_id
, ExtensionRegistry::ENABLED
);
97 return false; // The extension provided is no longer enabled.
99 if (HasBubbleInfoBeenAcknowledged(extension_id
))
102 const Extension
* override
= NULL
;
104 case extensions::BUBBLE_TYPE_HOME_PAGE
:
105 override
= extensions::GetExtensionOverridingHomepage(profile());
107 case extensions::BUBBLE_TYPE_STARTUP_PAGES
:
108 override
= extensions::GetExtensionOverridingStartupPages(profile());
110 case extensions::BUBBLE_TYPE_SEARCH_ENGINE
:
111 override
= extensions::GetExtensionOverridingSearchEngine(profile());
115 if (!override
|| override
->id() != extension
->id())
118 extension_id_
= extension_id
;
122 void SettingsApiBubbleDelegate::AcknowledgeExtension(
123 const std::string
& extension_id
,
124 ExtensionMessageBubbleController::BubbleAction user_action
) {
125 if (user_action
!= ExtensionMessageBubbleController::ACTION_EXECUTE
)
126 SetBubbleInfoBeenAcknowledged(extension_id
, true);
129 void SettingsApiBubbleDelegate::PerformAction(const ExtensionIdList
& list
) {
130 for (size_t i
= 0; i
< list
.size(); ++i
) {
131 service_
->DisableExtension(list
[i
], Extension::DISABLE_USER_ACTION
);
135 void SettingsApiBubbleDelegate::OnClose() {
136 ExtensionToolbarModel
* toolbar_model
= ExtensionToolbarModel::Get(profile());
138 toolbar_model
->StopHighlighting();
141 base::string16
SettingsApiBubbleDelegate::GetTitle() const {
143 case BUBBLE_TYPE_HOME_PAGE
:
144 return l10n_util::GetStringUTF16(
145 IDS_EXTENSIONS_SETTINGS_API_TITLE_HOME_PAGE_BUBBLE
);
146 case BUBBLE_TYPE_STARTUP_PAGES
:
147 return l10n_util::GetStringUTF16(
148 IDS_EXTENSIONS_SETTINGS_API_TITLE_STARTUP_PAGES_BUBBLE
);
149 case BUBBLE_TYPE_SEARCH_ENGINE
:
150 return l10n_util::GetStringUTF16(
151 IDS_EXTENSIONS_SETTINGS_API_TITLE_SEARCH_ENGINE_BUBBLE
);
154 return base::string16();
157 base::string16
SettingsApiBubbleDelegate::GetMessageBody(
158 bool anchored_to_browser_action
) const {
159 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile());
160 const Extension
* extension
=
161 registry
->GetExtensionById(extension_id_
, ExtensionRegistry::ENABLED
);
162 const SettingsOverrides
* settings
=
163 extension
? SettingsOverrides::Get(extension
) : NULL
;
164 if (!extension
|| !settings
) {
166 return base::string16();
169 bool home_change
= settings
->homepage
!= NULL
;
170 bool startup_change
= !settings
->startup_pages
.empty();
171 bool search_change
= settings
->search_engine
!= NULL
;
175 case BUBBLE_TYPE_HOME_PAGE
:
176 body
= l10n_util::GetStringUTF16(
177 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_HOME_PAGE
);
178 if (startup_change
&& search_change
) {
179 body
+= l10n_util::GetStringUTF16(
180 IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_AND_SEARCH
);
181 } else if (startup_change
) {
182 body
+= l10n_util::GetStringUTF16(
183 IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_PAGES
);
184 } else if (search_change
) {
185 body
+= l10n_util::GetStringUTF16(
186 IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_SEARCH_ENGINE
);
189 case BUBBLE_TYPE_STARTUP_PAGES
:
190 body
= l10n_util::GetStringUTF16(
191 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_START_PAGES
);
192 if (home_change
&& search_change
) {
193 body
+= l10n_util::GetStringUTF16(
194 IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_HOME_AND_SEARCH
);
195 } else if (home_change
) {
196 body
+= l10n_util::GetStringUTF16(
197 IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_HOME_PAGE
);
198 } else if (search_change
) {
199 body
+= l10n_util::GetStringUTF16(
200 IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_SEARCH_ENGINE
);
203 case BUBBLE_TYPE_SEARCH_ENGINE
:
204 body
= l10n_util::GetStringUTF16(
205 IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_SEARCH_ENGINE
);
206 if (startup_change
&& home_change
) {
207 body
+= l10n_util::GetStringUTF16(
208 IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_AND_HOME
);
209 } else if (startup_change
) {
210 body
+= l10n_util::GetStringUTF16(
211 IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_PAGES
);
212 } else if (home_change
) {
213 body
+= l10n_util::GetStringUTF16(
214 IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_HOME_PAGE
);
219 body
+= l10n_util::GetStringUTF16(
220 IDS_EXTENSIONS_SETTINGS_API_THIRD_LINE_CONFIRMATION
);
224 base::string16
SettingsApiBubbleDelegate::GetOverflowText(
225 const base::string16
& overflow_count
) const {
226 // Does not have more than one extension in the list at a time.
228 return base::string16();
231 GURL
SettingsApiBubbleDelegate::GetLearnMoreUrl() const {
232 return GURL(chrome::kExtensionControlledSettingLearnMoreURL
);
235 base::string16
SettingsApiBubbleDelegate::GetActionButtonLabel() const {
236 return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_RESTORE_SETTINGS
);
239 base::string16
SettingsApiBubbleDelegate::GetDismissButtonLabel() const {
240 return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_KEEP_CHANGES
);
243 bool SettingsApiBubbleDelegate::ShouldShowExtensionList() const {
247 void SettingsApiBubbleDelegate::LogExtensionCount(size_t count
) {
250 void SettingsApiBubbleDelegate::LogAction(
251 ExtensionMessageBubbleController::BubbleAction action
) {
253 case BUBBLE_TYPE_HOME_PAGE
:
254 UMA_HISTOGRAM_ENUMERATION(
255 "ExtensionOverrideBubble.SettingsApiUserSelectionHomePage",
257 ExtensionMessageBubbleController::ACTION_BOUNDARY
);
259 case BUBBLE_TYPE_STARTUP_PAGES
:
260 UMA_HISTOGRAM_ENUMERATION(
261 "ExtensionOverrideBubble.SettingsApiUserSelectionStartupPage",
263 ExtensionMessageBubbleController::ACTION_BOUNDARY
);
265 case BUBBLE_TYPE_SEARCH_ENGINE
:
266 UMA_HISTOGRAM_ENUMERATION(
267 "ExtensionOverrideBubble.SettingsApiUserSelectionSearchEngine",
269 ExtensionMessageBubbleController::ACTION_BOUNDARY
);
276 ////////////////////////////////////////////////////////////////////////////////
277 // SettingsApiBubbleController
279 SettingsApiBubbleController::SettingsApiBubbleController(
281 SettingsApiOverrideType type
)
282 : ExtensionMessageBubbleController(
283 new SettingsApiBubbleDelegate(
284 ExtensionSystem::Get(profile
)->extension_service(),
291 SettingsApiBubbleController::~SettingsApiBubbleController() {}
293 bool SettingsApiBubbleController::ShouldShow(const std::string
& extension_id
) {
294 if (delegate()->HasBubbleInfoBeenAcknowledged(extension_id
))
297 if (!delegate()->ShouldIncludeExtension(extension_id
))
300 // If the browser is showing the 'Chrome crashed' infobar, it won't be showing
301 // the startup pages, so there's no point in showing the bubble now.
302 if (type_
== BUBBLE_TYPE_STARTUP_PAGES
)
303 return profile_
->GetLastSessionExitType() != Profile::EXIT_CRASHED
;
308 bool SettingsApiBubbleController::CloseOnDeactivate() {
309 // Startup bubbles tend to get lost in the focus storm that happens on
310 // startup. Other types should dismiss on focus loss.
311 return type_
!= BUBBLE_TYPE_STARTUP_PAGES
;
314 } // namespace extensions