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