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 23namespace extensions { 24 25namespace { 26 27//////////////////////////////////////////////////////////////////////////////// 28// SettingsApiBubbleDelegate 29 30class 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 77SettingsApiBubbleDelegate::SettingsApiBubbleDelegate( 78 ExtensionService* service, 79 Profile* profile, 80 SettingsApiOverrideType type) 81 : service_(service), profile_(profile), type_(type) {} 82 83SettingsApiBubbleDelegate::~SettingsApiBubbleDelegate() {} 84 85bool 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 117void 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 126void 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 132void SettingsApiBubbleDelegate::OnClose() { 133 ExtensionToolbarModel* toolbar_model = ExtensionToolbarModel::Get(profile_); 134 if (toolbar_model) 135 toolbar_model->StopHighlighting(); 136} 137 138base::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 154base::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 221base::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 228base::string16 SettingsApiBubbleDelegate::GetLearnMoreLabel() const { 229 return l10n_util::GetStringUTF16(IDS_LEARN_MORE); 230} 231 232GURL SettingsApiBubbleDelegate::GetLearnMoreUrl() const { 233 return GURL(chrome::kExtensionControlledSettingLearnMoreURL); 234} 235 236base::string16 SettingsApiBubbleDelegate::GetActionButtonLabel() const { 237 return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_RESTORE_SETTINGS); 238} 239 240base::string16 SettingsApiBubbleDelegate::GetDismissButtonLabel() const { 241 return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_KEEP_CHANGES); 242} 243 244bool SettingsApiBubbleDelegate::ShouldShowExtensionList() const { 245 return false; 246} 247 248void SettingsApiBubbleDelegate::LogExtensionCount(size_t count) { 249} 250 251void 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 280SettingsApiBubbleController::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 292SettingsApiBubbleController::~SettingsApiBubbleController() {} 293 294bool 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 310bool 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