• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/extensions/settings_api_bubble_controller.h"
6 
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 "extensions/browser/extension_prefs.h"
16 #include "extensions/browser/extension_registry.h"
17 #include "extensions/browser/extension_system.h"
18 #include "grit/chromium_strings.h"
19 #include "grit/generated_resources.h"
20 #include "ui/base/l10n/l10n_util.h"
21 
22 namespace extensions {
23 
24 namespace {
25 
26 ////////////////////////////////////////////////////////////////////////////////
27 // SettingsApiBubbleDelegate
28 
29 class SettingsApiBubbleDelegate
30     : public ExtensionMessageBubbleController::Delegate {
31  public:
32   explicit SettingsApiBubbleDelegate(ExtensionService* service,
33                                      Profile* profile,
34                                      SettingsApiOverrideType type);
35   virtual ~SettingsApiBubbleDelegate();
36 
37   // ExtensionMessageBubbleController::Delegate methods.
38   virtual bool ShouldIncludeExtension(const std::string& extension_id) OVERRIDE;
39   virtual void AcknowledgeExtension(
40       const std::string& extension_id,
41       ExtensionMessageBubbleController::BubbleAction user_action) OVERRIDE;
42   virtual void PerformAction(const ExtensionIdList& list) OVERRIDE;
43   virtual void OnClose() OVERRIDE;
44   virtual base::string16 GetTitle() const OVERRIDE;
45   virtual base::string16 GetMessageBody(
46       bool anchored_to_browser_action) const OVERRIDE;
47   virtual base::string16 GetOverflowText(
48       const base::string16& overflow_count) const OVERRIDE;
49   virtual base::string16 GetLearnMoreLabel() const OVERRIDE;
50   virtual GURL GetLearnMoreUrl() const OVERRIDE;
51   virtual base::string16 GetActionButtonLabel() const OVERRIDE;
52   virtual base::string16 GetDismissButtonLabel() const OVERRIDE;
53   virtual bool ShouldShowExtensionList() const OVERRIDE;
54   virtual void LogExtensionCount(size_t count) OVERRIDE;
55   virtual void LogAction(
56       ExtensionMessageBubbleController::BubbleAction action) OVERRIDE;
57 
58  private:
59   // Our extension service. Weak, not owned by us.
60   ExtensionService* service_;
61 
62   // A weak pointer to the profile we are associated with. Not owned by us.
63   Profile* profile_;
64 
65   // The type of settings override this bubble will report on. This can be, for
66   // example, a bubble to notify the user that the search engine has been
67   // changed by an extension (or homepage/startup pages/etc).
68   SettingsApiOverrideType type_;
69 
70   // The ID of the extension we are showing the bubble for.
71   std::string extension_id_;
72 
73   DISALLOW_COPY_AND_ASSIGN(SettingsApiBubbleDelegate);
74 };
75 
SettingsApiBubbleDelegate(ExtensionService * service,Profile * profile,SettingsApiOverrideType type)76 SettingsApiBubbleDelegate::SettingsApiBubbleDelegate(
77     ExtensionService* service,
78     Profile* profile,
79     SettingsApiOverrideType type)
80     : service_(service), profile_(profile), type_(type) {}
81 
~SettingsApiBubbleDelegate()82 SettingsApiBubbleDelegate::~SettingsApiBubbleDelegate() {}
83 
ShouldIncludeExtension(const std::string & extension_id)84 bool SettingsApiBubbleDelegate::ShouldIncludeExtension(
85     const std::string& extension_id) {
86   ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
87   const Extension* extension =
88       registry->GetExtensionById(extension_id, ExtensionRegistry::ENABLED);
89   if (!extension)
90     return false;  // The extension provided is no longer enabled.
91 
92   ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
93   if (prefs->HasSettingsApiBubbleBeenAcknowledged(extension_id))
94     return false;
95 
96   const Extension* override = NULL;
97   switch (type_) {
98     case extensions::BUBBLE_TYPE_HOME_PAGE:
99       override = extensions::GetExtensionOverridingHomepage(profile_);
100       break;
101     case extensions::BUBBLE_TYPE_STARTUP_PAGES:
102       override = extensions::GetExtensionOverridingStartupPages(profile_);
103       break;
104     case extensions::BUBBLE_TYPE_SEARCH_ENGINE:
105       override = extensions::GetExtensionOverridingSearchEngine(profile_);
106       break;
107   }
108 
109   if (!override || override->id() != extension->id())
110     return false;
111 
112   extension_id_ = extension_id;
113   return true;
114 }
115 
AcknowledgeExtension(const std::string & extension_id,ExtensionMessageBubbleController::BubbleAction user_action)116 void SettingsApiBubbleDelegate::AcknowledgeExtension(
117     const std::string& extension_id,
118     ExtensionMessageBubbleController::BubbleAction user_action) {
119   if (user_action != ExtensionMessageBubbleController::ACTION_EXECUTE) {
120     ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
121     prefs->SetSettingsApiBubbleBeenAcknowledged(extension_id, true);
122   }
123 }
124 
PerformAction(const ExtensionIdList & list)125 void SettingsApiBubbleDelegate::PerformAction(const ExtensionIdList& list) {
126   for (size_t i = 0; i < list.size(); ++i) {
127     service_->DisableExtension(list[i], Extension::DISABLE_USER_ACTION);
128   }
129 }
130 
OnClose()131 void SettingsApiBubbleDelegate::OnClose() {
132   ExtensionToolbarModel* toolbar_model = ExtensionToolbarModel::Get(profile_);
133   if (toolbar_model)
134     toolbar_model->StopHighlighting();
135 }
136 
GetTitle() const137 base::string16 SettingsApiBubbleDelegate::GetTitle() const {
138   switch (type_) {
139     case BUBBLE_TYPE_HOME_PAGE:
140       return l10n_util::GetStringUTF16(
141           IDS_EXTENSIONS_SETTINGS_API_TITLE_HOME_PAGE_BUBBLE);
142     case BUBBLE_TYPE_STARTUP_PAGES:
143       return l10n_util::GetStringUTF16(
144           IDS_EXTENSIONS_SETTINGS_API_TITLE_STARTUP_PAGES_BUBBLE);
145     case BUBBLE_TYPE_SEARCH_ENGINE:
146       return l10n_util::GetStringUTF16(
147           IDS_EXTENSIONS_SETTINGS_API_TITLE_SEARCH_ENGINE_BUBBLE);
148   }
149   NOTREACHED();
150   return base::string16();
151 }
152 
GetMessageBody(bool anchored_to_browser_action) const153 base::string16 SettingsApiBubbleDelegate::GetMessageBody(
154     bool anchored_to_browser_action) 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();
163   }
164 
165   bool home_change = settings->homepage != NULL;
166   bool startup_change = !settings->startup_pages.empty();
167   bool search_change = settings->search_engine != NULL;
168 
169   base::string16 body;
170   switch (type_) {
171     case BUBBLE_TYPE_HOME_PAGE:
172       body = l10n_util::GetStringUTF16(
173           IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_HOME_PAGE);
174       if (startup_change && search_change) {
175         body += l10n_util::GetStringUTF16(
176             IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_AND_SEARCH);
177       } else if (startup_change) {
178         body += l10n_util::GetStringUTF16(
179             IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_PAGES);
180       } else if (search_change) {
181         body += l10n_util::GetStringUTF16(
182             IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_SEARCH_ENGINE);
183       }
184       break;
185     case BUBBLE_TYPE_STARTUP_PAGES:
186       body = l10n_util::GetStringUTF16(
187           IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_START_PAGES);
188       if (home_change && search_change) {
189         body += l10n_util::GetStringUTF16(
190             IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_HOME_AND_SEARCH);
191       } else if (home_change) {
192         body += l10n_util::GetStringUTF16(
193             IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_HOME_PAGE);
194       } else if (search_change) {
195         body += l10n_util::GetStringUTF16(
196             IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_SEARCH_ENGINE);
197       }
198       break;
199     case BUBBLE_TYPE_SEARCH_ENGINE:
200       body = l10n_util::GetStringUTF16(
201           IDS_EXTENSIONS_SETTINGS_API_FIRST_LINE_SEARCH_ENGINE);
202       if (startup_change && home_change) {
203         body += l10n_util::GetStringUTF16(
204             IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_AND_HOME);
205       } else if (startup_change) {
206         body += l10n_util::GetStringUTF16(
207             IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_PAGES);
208       } else if (home_change) {
209         body += l10n_util::GetStringUTF16(
210             IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_HOME_PAGE);
211       }
212       break;
213   }
214   if (!body.empty())
215     body += l10n_util::GetStringUTF16(
216             IDS_EXTENSIONS_SETTINGS_API_THIRD_LINE_CONFIRMATION);
217   return body;
218 }
219 
GetOverflowText(const base::string16 & overflow_count) const220 base::string16 SettingsApiBubbleDelegate::GetOverflowText(
221     const base::string16& overflow_count) const {
222   // Does not have more than one extension in the list at a time.
223   NOTREACHED();
224   return base::string16();
225 }
226 
GetLearnMoreLabel() const227 base::string16 SettingsApiBubbleDelegate::GetLearnMoreLabel() const {
228   return l10n_util::GetStringUTF16(IDS_LEARN_MORE);
229 }
230 
GetLearnMoreUrl() const231 GURL SettingsApiBubbleDelegate::GetLearnMoreUrl() const {
232   return GURL(chrome::kExtensionControlledSettingLearnMoreURL);
233 }
234 
GetActionButtonLabel() const235 base::string16 SettingsApiBubbleDelegate::GetActionButtonLabel() const {
236   return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_RESTORE_SETTINGS);
237 }
238 
GetDismissButtonLabel() const239 base::string16 SettingsApiBubbleDelegate::GetDismissButtonLabel() const {
240   return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_KEEP_CHANGES);
241 }
242 
ShouldShowExtensionList() const243 bool SettingsApiBubbleDelegate::ShouldShowExtensionList() const {
244   return false;
245 }
246 
LogExtensionCount(size_t count)247 void SettingsApiBubbleDelegate::LogExtensionCount(size_t count) {
248 }
249 
LogAction(ExtensionMessageBubbleController::BubbleAction action)250 void SettingsApiBubbleDelegate::LogAction(
251     ExtensionMessageBubbleController::BubbleAction action) {
252   switch (type_) {
253     case BUBBLE_TYPE_HOME_PAGE:
254       UMA_HISTOGRAM_ENUMERATION(
255           "ExtensionOverrideBubble.SettingsApiUserSelectionHomePage",
256           action,
257           ExtensionMessageBubbleController::ACTION_BOUNDARY);
258       break;
259     case BUBBLE_TYPE_STARTUP_PAGES:
260       UMA_HISTOGRAM_ENUMERATION(
261           "ExtensionOverrideBubble.SettingsApiUserSelectionStartupPage",
262           action,
263           ExtensionMessageBubbleController::ACTION_BOUNDARY);
264       break;
265     case BUBBLE_TYPE_SEARCH_ENGINE:
266       UMA_HISTOGRAM_ENUMERATION(
267           "ExtensionOverrideBubble.SettingsApiUserSelectionSearchEngine",
268           action,
269           ExtensionMessageBubbleController::ACTION_BOUNDARY);
270       break;
271   }
272 }
273 
274 }  // namespace
275 
276 ////////////////////////////////////////////////////////////////////////////////
277 // SettingsApiBubbleController
278 
SettingsApiBubbleController(Profile * profile,SettingsApiOverrideType type)279 SettingsApiBubbleController::SettingsApiBubbleController(
280     Profile* profile,
281     SettingsApiOverrideType type)
282     : ExtensionMessageBubbleController(
283           new SettingsApiBubbleDelegate(
284               ExtensionSystem::Get(profile)->extension_service(),
285               profile,
286               type),
287           profile),
288       profile_(profile),
289       type_(type) {}
290 
~SettingsApiBubbleController()291 SettingsApiBubbleController::~SettingsApiBubbleController() {}
292 
ShouldShow(const std::string & extension_id)293 bool SettingsApiBubbleController::ShouldShow(const std::string& extension_id) {
294   ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
295   if (prefs->HasSettingsApiBubbleBeenAcknowledged(extension_id))
296     return false;
297 
298   if (!delegate()->ShouldIncludeExtension(extension_id))
299     return false;
300 
301   // If the browser is showing the 'Chrome crashed' infobar, it won't be showing
302   // the startup pages, so there's no point in showing the bubble now.
303   if (type_ == BUBBLE_TYPE_STARTUP_PAGES)
304     return profile_->GetLastSessionExitType() != Profile::EXIT_CRASHED;
305 
306   return true;
307 }
308 
CloseOnDeactivate()309 bool SettingsApiBubbleController::CloseOnDeactivate() {
310   // Startup bubbles tend to get lost in the focus storm that happens on
311   // startup. Other types should dismiss on focus loss.
312   return type_ != BUBBLE_TYPE_STARTUP_PAGES;
313 }
314 
315 }  // namespace extensions
316