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