Home | History | Annotate | Download | only in extensions
      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 
     76 SettingsApiBubbleDelegate::SettingsApiBubbleDelegate(
     77     ExtensionService* service,
     78     Profile* profile,
     79     SettingsApiOverrideType type)
     80     : service_(service), profile_(profile), type_(type) {}
     81 
     82 SettingsApiBubbleDelegate::~SettingsApiBubbleDelegate() {}
     83 
     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 
    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 
    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 
    131 void SettingsApiBubbleDelegate::OnClose() {
    132   ExtensionToolbarModel* toolbar_model = ExtensionToolbarModel::Get(profile_);
    133   if (toolbar_model)
    134     toolbar_model->StopHighlighting();
    135 }
    136 
    137 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 
    153 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 
    220 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 
    227 base::string16 SettingsApiBubbleDelegate::GetLearnMoreLabel() const {
    228   return l10n_util::GetStringUTF16(IDS_LEARN_MORE);
    229 }
    230 
    231 GURL SettingsApiBubbleDelegate::GetLearnMoreUrl() const {
    232   return GURL(chrome::kExtensionControlledSettingLearnMoreURL);
    233 }
    234 
    235 base::string16 SettingsApiBubbleDelegate::GetActionButtonLabel() const {
    236   return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_RESTORE_SETTINGS);
    237 }
    238 
    239 base::string16 SettingsApiBubbleDelegate::GetDismissButtonLabel() const {
    240   return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_KEEP_CHANGES);
    241 }
    242 
    243 bool SettingsApiBubbleDelegate::ShouldShowExtensionList() const {
    244   return false;
    245 }
    246 
    247 void SettingsApiBubbleDelegate::LogExtensionCount(size_t count) {
    248 }
    249 
    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 
    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 
    291 SettingsApiBubbleController::~SettingsApiBubbleController() {}
    292 
    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 
    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