Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / settings_api_bubble_controller.cc
blob7a4f65de773e92c9898d44ccace16aa66713cfbe
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 {
25 namespace {
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 {
36 public:
37 explicit SettingsApiBubbleDelegate(ExtensionService* service,
38 Profile* profile,
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;
59 void LogAction(
60 ExtensionMessageBubbleController::BubbleAction action) override;
62 private:
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.
67 Profile* profile_;
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,
82 Profile* profile,
83 SettingsApiOverrideType type)
84 : ExtensionMessageBubbleController::Delegate(profile),
85 service_(service),
86 type_(type) {
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);
97 if (!extension)
98 return false; // The extension provided is no longer enabled.
100 if (HasBubbleInfoBeenAcknowledged(extension_id))
101 return false;
103 const Extension* override = NULL;
104 switch (type_) {
105 case extensions::BUBBLE_TYPE_HOME_PAGE:
106 override = extensions::GetExtensionOverridingHomepage(profile());
107 break;
108 case extensions::BUBBLE_TYPE_STARTUP_PAGES:
109 override = extensions::GetExtensionOverridingStartupPages(profile());
110 break;
111 case extensions::BUBBLE_TYPE_SEARCH_ENGINE:
112 override = extensions::GetExtensionOverridingSearchEngine(profile());
113 break;
116 if (!override || override->id() != extension->id())
117 return false;
119 extension_id_ = extension_id;
120 return true;
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 {
137 switch (type_) {
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);
148 NOTREACHED();
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) {
161 NOTREACHED();
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;
172 base::string16 body;
173 switch (type_) {
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) {
179 second_line_id =
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;
186 break;
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) {
192 second_line_id =
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;
199 break;
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;
210 break;
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()));
217 if (second_line_id)
218 body += l10n_util::GetStringUTF16(second_line_id);
220 body += l10n_util::GetStringUTF16(
221 IDS_EXTENSIONS_SETTINGS_API_THIRD_LINE_CONFIRMATION);
223 return body;
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.
229 NOTREACHED();
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 {
246 return false;
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) {
258 switch (type_) {
259 case BUBBLE_TYPE_HOME_PAGE:
260 UMA_HISTOGRAM_ENUMERATION(
261 "ExtensionOverrideBubble.SettingsApiUserSelectionHomePage",
262 action,
263 ExtensionMessageBubbleController::ACTION_BOUNDARY);
264 break;
265 case BUBBLE_TYPE_STARTUP_PAGES:
266 UMA_HISTOGRAM_ENUMERATION(
267 "ExtensionOverrideBubble.SettingsApiUserSelectionStartupPage",
268 action,
269 ExtensionMessageBubbleController::ACTION_BOUNDARY);
270 break;
271 case BUBBLE_TYPE_SEARCH_ENGINE:
272 UMA_HISTOGRAM_ENUMERATION(
273 "ExtensionOverrideBubble.SettingsApiUserSelectionSearchEngine",
274 action,
275 ExtensionMessageBubbleController::ACTION_BOUNDARY);
276 break;
280 } // namespace
282 ////////////////////////////////////////////////////////////////////////////////
283 // SettingsApiBubbleController
285 SettingsApiBubbleController::SettingsApiBubbleController(
286 Profile* profile,
287 SettingsApiOverrideType type)
288 : ExtensionMessageBubbleController(
289 new SettingsApiBubbleDelegate(
290 ExtensionSystem::Get(profile)->extension_service(),
291 profile,
292 type),
293 profile),
294 profile_(profile),
295 type_(type) {}
297 SettingsApiBubbleController::~SettingsApiBubbleController() {}
299 bool SettingsApiBubbleController::ShouldShow() {
300 const Extension* extension = nullptr;
301 switch (type_) {
302 case BUBBLE_TYPE_HOME_PAGE:
303 extension = GetExtensionOverridingHomepage(profile_);
304 break;
305 case BUBBLE_TYPE_SEARCH_ENGINE:
306 extension = GetExtensionOverridingSearchEngine(profile_);
307 break;
308 case BUBBLE_TYPE_STARTUP_PAGES:
309 extension = GetExtensionOverridingStartupPages(profile_);
310 break;
313 if (!extension)
314 return false;
316 if (delegate()->HasBubbleInfoBeenAcknowledged(extension->id()))
317 return false;
319 if (!delegate()->ShouldIncludeExtension(extension->id()))
320 return false;
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;
327 return true;
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