| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 <algorithm> | |
| 6 | |
| 7 #include "base/basictypes.h" | |
| 8 #include "base/compiler_specific.h" | |
| 9 #include "base/logging.h" | |
| 10 #include "base/memory/scoped_ptr.h" | |
| 11 #include "base/metrics/histogram.h" | |
| 12 #include "chrome/browser/profiles/profile.h" | |
| 13 #include "chrome/browser/protector/base_setting_change.h" | |
| 14 #include "chrome/browser/protector/histograms.h" | |
| 15 #include "chrome/browser/protector/protector_service.h" | |
| 16 #include "chrome/browser/protector/protector_service_factory.h" | |
| 17 #include "chrome/browser/search_engines/template_url.h" | |
| 18 #include "chrome/browser/search_engines/template_url_prepopulate_data.h" | |
| 19 #include "chrome/browser/search_engines/template_url_service.h" | |
| 20 #include "chrome/browser/search_engines/template_url_service_factory.h" | |
| 21 #include "chrome/browser/search_engines/template_url_service_observer.h" | |
| 22 #include "chrome/browser/webdata/keyword_table.h" | |
| 23 #include "chrome/common/chrome_notification_types.h" | |
| 24 #include "chrome/common/url_constants.h" | |
| 25 #include "content/public/browser/notification_observer.h" | |
| 26 #include "content/public/browser/notification_registrar.h" | |
| 27 #include "content/public/browser/notification_service.h" | |
| 28 #include "googleurl/src/gurl.h" | |
| 29 #include "grit/chromium_strings.h" | |
| 30 #include "grit/generated_resources.h" | |
| 31 #include "grit/theme_resources.h" | |
| 32 #include "ui/base/l10n/l10n_util.h" | |
| 33 | |
| 34 namespace protector { | |
| 35 | |
| 36 namespace { | |
| 37 | |
| 38 // Maximum length of the search engine name to be displayed. | |
| 39 const size_t kMaxDisplayedNameLength = 16; | |
| 40 | |
| 41 // Matches TemplateURL with all fields set from the prepopulated data equal | |
| 42 // to fields in another TemplateURL. | |
| 43 class TemplateURLIsSame { | |
| 44 public: | |
| 45 // Creates a matcher based on |other|. | |
| 46 explicit TemplateURLIsSame(const TemplateURL* other) : other_(other) { | |
| 47 } | |
| 48 | |
| 49 // Returns true if both |other| and |url| are NULL or have same field values. | |
| 50 bool operator()(const TemplateURL* url) { | |
| 51 if (url == other_ ) | |
| 52 return true; | |
| 53 if (!url || !other_) | |
| 54 return false; | |
| 55 return url->short_name() == other_->short_name() && | |
| 56 url->HasSameKeywordAs(*other_) && | |
| 57 url->url() == other_->url() && | |
| 58 url->suggestions_url() == other_->suggestions_url() && | |
| 59 url->instant_url() == other_->instant_url() && | |
| 60 url->safe_for_autoreplace() == other_->safe_for_autoreplace() && | |
| 61 url->favicon_url() == other_->favicon_url() && | |
| 62 url->show_in_default_list() == other_->show_in_default_list() && | |
| 63 url->input_encodings() == other_->input_encodings() && | |
| 64 url->prepopulate_id() == other_->prepopulate_id(); | |
| 65 } | |
| 66 | |
| 67 private: | |
| 68 const TemplateURL* other_; | |
| 69 }; | |
| 70 | |
| 71 } // namespace | |
| 72 | |
| 73 // Default search engine change tracked by Protector. | |
| 74 class DefaultSearchProviderChange : public BaseSettingChange, | |
| 75 public TemplateURLServiceObserver, | |
| 76 public content::NotificationObserver { | |
| 77 public: | |
| 78 DefaultSearchProviderChange(TemplateURL* new_search_provider, | |
| 79 TemplateURL* backup_search_provider); | |
| 80 | |
| 81 // BaseSettingChange overrides: | |
| 82 virtual bool Init(Profile* profile) OVERRIDE; | |
| 83 virtual void InitWhenDisabled(Profile* profile) OVERRIDE; | |
| 84 virtual void Apply(Browser* browser) OVERRIDE; | |
| 85 virtual void Discard(Browser* browser) OVERRIDE; | |
| 86 virtual void Timeout() OVERRIDE; | |
| 87 virtual int GetBadgeIconID() const OVERRIDE; | |
| 88 virtual int GetMenuItemIconID() const OVERRIDE; | |
| 89 virtual int GetBubbleIconID() const OVERRIDE; | |
| 90 virtual string16 GetBubbleTitle() const OVERRIDE; | |
| 91 virtual string16 GetBubbleMessage() const OVERRIDE; | |
| 92 virtual string16 GetApplyButtonText() const OVERRIDE; | |
| 93 virtual string16 GetDiscardButtonText() const OVERRIDE; | |
| 94 virtual DisplayName GetApplyDisplayName() const OVERRIDE; | |
| 95 virtual GURL GetNewSettingURL() const OVERRIDE; | |
| 96 | |
| 97 private: | |
| 98 virtual ~DefaultSearchProviderChange(); | |
| 99 | |
| 100 // TemplateURLServiceObserver overrides: | |
| 101 virtual void OnTemplateURLServiceChanged() OVERRIDE; | |
| 102 | |
| 103 // content::NotificationObserver overrides: | |
| 104 virtual void Observe(int type, | |
| 105 const content::NotificationSource& source, | |
| 106 const content::NotificationDetails& details) OVERRIDE; | |
| 107 | |
| 108 // Sets the default search provider to |*search_provider|. If a matching | |
| 109 // TemplateURL already exists, it is reused and |*search_provider| is reset. | |
| 110 // Otherwise, adds |*search_provider| to keywords and releases it. | |
| 111 // In both cases, |*search_provider| is NULL after this call. | |
| 112 // Returns the new default search provider (either |*search_provider| or the | |
| 113 // reused TemplateURL). | |
| 114 const TemplateURL* SetDefaultSearchProvider( | |
| 115 scoped_ptr<TemplateURL>* search_provider); | |
| 116 | |
| 117 // Returns true if |new_search_provider_| can be used as the default search | |
| 118 // provider. | |
| 119 bool NewSearchProviderValid() const; | |
| 120 | |
| 121 // Opens the Search engine settings page in a new tab. | |
| 122 void OpenSearchEngineSettings(Browser* browser); | |
| 123 | |
| 124 // Returns the TemplateURLService instance for the Profile this change is | |
| 125 // related to. | |
| 126 TemplateURLService* GetTemplateURLService(); | |
| 127 | |
| 128 // Histogram ID of the new search provider. | |
| 129 int new_histogram_id_; | |
| 130 // Indicates that the default search was restored to the prepopulated default | |
| 131 // search engines. | |
| 132 bool is_fallback_; | |
| 133 // Indicates that the the prepopulated default search is the same as | |
| 134 // |new_search_provider_|. | |
| 135 bool fallback_is_new_; | |
| 136 // ID of |new_search_provider_|. | |
| 137 TemplateURLID new_id_; | |
| 138 // The default search at the moment the change was detected. Will be used to | |
| 139 // restore the new default search back if Apply is called. Will be set to | |
| 140 // |NULL| if removed from the TemplateURLService. | |
| 141 TemplateURL* new_search_provider_; | |
| 142 // Default search provider set by Init for the period until user makes a | |
| 143 // choice and either Apply or Discard is performed. Never is |NULL| during | |
| 144 // that period since the change will dismiss itself if this provider gets | |
| 145 // deleted or stops being the default. | |
| 146 const TemplateURL* default_search_provider_; | |
| 147 // Stores backup of the default search until it becomes owned by the | |
| 148 // TemplateURLService or deleted. | |
| 149 scoped_ptr<TemplateURL> backup_search_provider_; | |
| 150 content::NotificationRegistrar registrar_; | |
| 151 | |
| 152 DISALLOW_COPY_AND_ASSIGN(DefaultSearchProviderChange); | |
| 153 }; | |
| 154 | |
| 155 DefaultSearchProviderChange::DefaultSearchProviderChange( | |
| 156 TemplateURL* new_search_provider, | |
| 157 TemplateURL* backup_search_provider) | |
| 158 : new_histogram_id_(GetSearchProviderHistogramID(new_search_provider)), | |
| 159 is_fallback_(false), | |
| 160 fallback_is_new_(false), | |
| 161 new_search_provider_(new_search_provider), | |
| 162 default_search_provider_(NULL), | |
| 163 backup_search_provider_(backup_search_provider) { | |
| 164 if (backup_search_provider) { | |
| 165 UMA_HISTOGRAM_ENUMERATION( | |
| 166 kProtectorHistogramSearchProviderHijacked, | |
| 167 new_histogram_id_, | |
| 168 kProtectorMaxSearchProviderID); | |
| 169 } else { | |
| 170 UMA_HISTOGRAM_ENUMERATION( | |
| 171 kProtectorHistogramSearchProviderCorrupt, | |
| 172 new_histogram_id_, | |
| 173 kProtectorMaxSearchProviderID); | |
| 174 } | |
| 175 } | |
| 176 | |
| 177 DefaultSearchProviderChange::~DefaultSearchProviderChange() { | |
| 178 if (profile()) | |
| 179 GetTemplateURLService()->RemoveObserver(this); | |
| 180 } | |
| 181 | |
| 182 bool DefaultSearchProviderChange::Init(Profile* profile) { | |
| 183 if (!BaseSettingChange::Init(profile)) | |
| 184 return false; | |
| 185 | |
| 186 if (!backup_search_provider_.get() || | |
| 187 !backup_search_provider_->SupportsReplacement()) { | |
| 188 // Fallback to a prepopulated default search provider, ignoring any | |
| 189 // overrides in Prefs. | |
| 190 backup_search_provider_.reset( | |
| 191 TemplateURLPrepopulateData::GetPrepopulatedDefaultSearch(NULL)); | |
| 192 is_fallback_ = true; | |
| 193 VLOG(1) << "Fallback search provider: " | |
| 194 << backup_search_provider_->short_name(); | |
| 195 } | |
| 196 | |
| 197 default_search_provider_ = SetDefaultSearchProvider(&backup_search_provider_); | |
| 198 DCHECK(default_search_provider_); | |
| 199 // |backup_search_provider_| should be |NULL| since now. | |
| 200 DCHECK(!backup_search_provider_.get()); | |
| 201 if (is_fallback_ && default_search_provider_ == new_search_provider_) | |
| 202 fallback_is_new_ = true; | |
| 203 | |
| 204 int restored_histogram_id = | |
| 205 GetSearchProviderHistogramID(default_search_provider_); | |
| 206 UMA_HISTOGRAM_ENUMERATION( | |
| 207 kProtectorHistogramSearchProviderRestored, | |
| 208 restored_histogram_id, | |
| 209 kProtectorMaxSearchProviderID); | |
| 210 | |
| 211 if (is_fallback_) { | |
| 212 VLOG(1) << "Fallback to search provider: " | |
| 213 << default_search_provider_->short_name(); | |
| 214 UMA_HISTOGRAM_ENUMERATION( | |
| 215 kProtectorHistogramSearchProviderFallback, | |
| 216 restored_histogram_id, | |
| 217 kProtectorMaxSearchProviderID); | |
| 218 } | |
| 219 | |
| 220 // Listen for the default search provider changes. | |
| 221 GetTemplateURLService()->AddObserver(this); | |
| 222 | |
| 223 if (new_search_provider_) { | |
| 224 // Listen for removal of |new_search_provider_|. | |
| 225 new_id_ = new_search_provider_->id(); | |
| 226 registrar_.Add( | |
| 227 this, chrome::NOTIFICATION_TEMPLATE_URL_REMOVED, | |
| 228 content::Source<Profile>(profile->GetOriginalProfile())); | |
| 229 } | |
| 230 | |
| 231 return true; | |
| 232 } | |
| 233 | |
| 234 void DefaultSearchProviderChange::InitWhenDisabled(Profile* profile) { | |
| 235 // The --no-protector case is handled in TemplateURLService internals. | |
| 236 // TODO(ivankr): move it here. | |
| 237 NOTREACHED(); | |
| 238 } | |
| 239 | |
| 240 void DefaultSearchProviderChange::Apply(Browser* browser) { | |
| 241 UMA_HISTOGRAM_ENUMERATION( | |
| 242 kProtectorHistogramSearchProviderApplied, | |
| 243 new_histogram_id_, | |
| 244 kProtectorMaxSearchProviderID); | |
| 245 | |
| 246 GetTemplateURLService()->RemoveObserver(this); | |
| 247 if (NewSearchProviderValid()) { | |
| 248 GetTemplateURLService()->SetDefaultSearchProvider(new_search_provider_); | |
| 249 } else { | |
| 250 // Open settings page in case the new setting is invalid. | |
| 251 OpenSearchEngineSettings(browser); | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 void DefaultSearchProviderChange::Discard(Browser* browser) { | |
| 256 UMA_HISTOGRAM_ENUMERATION( | |
| 257 kProtectorHistogramSearchProviderDiscarded, | |
| 258 new_histogram_id_, | |
| 259 kProtectorMaxSearchProviderID); | |
| 260 | |
| 261 GetTemplateURLService()->RemoveObserver(this); | |
| 262 if (is_fallback_) { | |
| 263 // Open settings page in case the old setting is invalid. | |
| 264 OpenSearchEngineSettings(browser); | |
| 265 } | |
| 266 // Nothing to do otherwise since we have already set the search engine | |
| 267 // to |old_id_| in |Init|. | |
| 268 } | |
| 269 | |
| 270 void DefaultSearchProviderChange::Timeout() { | |
| 271 UMA_HISTOGRAM_ENUMERATION( | |
| 272 kProtectorHistogramSearchProviderTimeout, | |
| 273 new_histogram_id_, | |
| 274 kProtectorMaxSearchProviderID); | |
| 275 } | |
| 276 | |
| 277 int DefaultSearchProviderChange::GetBadgeIconID() const { | |
| 278 return IDR_SEARCH_ENGINE_CHANGE_BADGE; | |
| 279 } | |
| 280 | |
| 281 int DefaultSearchProviderChange::GetMenuItemIconID() const { | |
| 282 return IDR_SEARCH_ENGINE_CHANGE_MENU; | |
| 283 } | |
| 284 | |
| 285 int DefaultSearchProviderChange::GetBubbleIconID() const { | |
| 286 return IDR_SEARCH_ENGINE_CHANGE_ALERT; | |
| 287 } | |
| 288 | |
| 289 string16 DefaultSearchProviderChange::GetBubbleTitle() const { | |
| 290 return l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_CHANGE_TITLE); | |
| 291 } | |
| 292 | |
| 293 string16 DefaultSearchProviderChange::GetBubbleMessage() const { | |
| 294 if (is_fallback_) { | |
| 295 return l10n_util::GetStringFUTF16( | |
| 296 IDS_SEARCH_ENGINE_CHANGE_NO_BACKUP_MESSAGE, | |
| 297 default_search_provider_->short_name()); | |
| 298 } | |
| 299 return l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_CHANGE_MESSAGE); | |
| 300 } | |
| 301 | |
| 302 string16 DefaultSearchProviderChange::GetApplyButtonText() const { | |
| 303 if (NewSearchProviderValid()) { | |
| 304 // If backup search engine is lost and fallback was made to the current | |
| 305 // search provider then there is no need to show this button. | |
| 306 if (fallback_is_new_) | |
| 307 return string16(); | |
| 308 string16 name = new_search_provider_->short_name(); | |
| 309 return (name.length() > kMaxDisplayedNameLength) ? | |
| 310 l10n_util::GetStringUTF16(IDS_CHANGE_SEARCH_ENGINE_NO_NAME) : | |
| 311 l10n_util::GetStringFUTF16(IDS_CHANGE_SEARCH_ENGINE, name); | |
| 312 } | |
| 313 if (is_fallback_) { | |
| 314 // Both settings are lost: don't show this button. | |
| 315 return string16(); | |
| 316 } | |
| 317 // New setting is lost, offer to go to settings. | |
| 318 return l10n_util::GetStringUTF16(IDS_SELECT_SEARCH_ENGINE); | |
| 319 } | |
| 320 | |
| 321 string16 DefaultSearchProviderChange::GetDiscardButtonText() const { | |
| 322 if (!is_fallback_) { | |
| 323 string16 name = default_search_provider_->short_name(); | |
| 324 return (name.length() > kMaxDisplayedNameLength) ? | |
| 325 l10n_util::GetStringUTF16(IDS_KEEP_SETTING) : | |
| 326 l10n_util::GetStringFUTF16(IDS_KEEP_SEARCH_ENGINE, name); | |
| 327 } | |
| 328 // Old setting is lost, offer to go to settings. | |
| 329 return l10n_util::GetStringUTF16(IDS_SELECT_SEARCH_ENGINE); | |
| 330 } | |
| 331 | |
| 332 BaseSettingChange::DisplayName | |
| 333 DefaultSearchProviderChange::GetApplyDisplayName() const { | |
| 334 if (NewSearchProviderValid() && | |
| 335 new_search_provider_->short_name().length() <= kMaxDisplayedNameLength) { | |
| 336 return DisplayName(kDefaultSearchProviderChangeNamePriority, | |
| 337 new_search_provider_->short_name()); | |
| 338 } | |
| 339 // Return the default empty name. | |
| 340 return BaseSettingChange::GetApplyDisplayName(); | |
| 341 } | |
| 342 | |
| 343 GURL DefaultSearchProviderChange::GetNewSettingURL() const { | |
| 344 if (is_fallback_) { | |
| 345 // Do not collide this change when fallback was made, since the message | |
| 346 // and Discard behaviour is pretty different. | |
| 347 return GURL(); | |
| 348 } | |
| 349 if (!NewSearchProviderValid()) | |
| 350 return GURL(); | |
| 351 return TemplateURLService::GenerateSearchURL(new_search_provider_); | |
| 352 } | |
| 353 | |
| 354 void DefaultSearchProviderChange::OnTemplateURLServiceChanged() { | |
| 355 TemplateURLService* url_service = GetTemplateURLService(); | |
| 356 if (url_service->GetDefaultSearchProvider() != default_search_provider_) { | |
| 357 VLOG(1) << "Default search provider has been changed by user"; | |
| 358 default_search_provider_ = NULL; | |
| 359 url_service->RemoveObserver(this); | |
| 360 // Will delete this DefaultSearchProviderChange instance. | |
| 361 ProtectorServiceFactory::GetForProfile(profile())->DismissChange(this); | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 void DefaultSearchProviderChange::Observe( | |
| 366 int type, | |
| 367 const content::NotificationSource& source, | |
| 368 const content::NotificationDetails& details) { | |
| 369 DCHECK(type == chrome::NOTIFICATION_TEMPLATE_URL_REMOVED); | |
| 370 TemplateURLID id = *content::Details<TemplateURLID>(details).ptr(); | |
| 371 if (id == new_id_) | |
| 372 new_search_provider_ = NULL; | |
| 373 registrar_.Remove(this, type, source); | |
| 374 // TODO(ivankr): should the change be dismissed as well? Probably not, | |
| 375 // since this may happend due to Sync or whatever. In that case, if user | |
| 376 // clicks on 'Change to...', the Search engine settings page will be opened. | |
| 377 } | |
| 378 | |
| 379 const TemplateURL* DefaultSearchProviderChange::SetDefaultSearchProvider( | |
| 380 scoped_ptr<TemplateURL>* search_provider) { | |
| 381 TemplateURLService* url_service = GetTemplateURLService(); | |
| 382 TemplateURLService::TemplateURLVector urls = url_service->GetTemplateURLs(); | |
| 383 TemplateURL* new_default_provider = NULL; | |
| 384 | |
| 385 // Check if this provider already exists and add it otherwise. | |
| 386 TemplateURLService::TemplateURLVector::const_iterator i = | |
| 387 std::find_if(urls.begin(), urls.end(), | |
| 388 TemplateURLIsSame(search_provider->get())); | |
| 389 if (i != urls.end()) { | |
| 390 VLOG(1) << "Provider already exists"; | |
| 391 new_default_provider = *i; | |
| 392 search_provider->reset(); | |
| 393 } else { | |
| 394 VLOG(1) << "No match, adding new provider"; | |
| 395 new_default_provider = search_provider->get(); | |
| 396 url_service->Add(search_provider->release()); | |
| 397 UMA_HISTOGRAM_ENUMERATION( | |
| 398 kProtectorHistogramSearchProviderMissing, | |
| 399 GetSearchProviderHistogramID(new_default_provider), | |
| 400 kProtectorMaxSearchProviderID); | |
| 401 } | |
| 402 | |
| 403 // TODO(ivankr): handle keyword conflicts with existing providers. | |
| 404 DCHECK(new_default_provider); | |
| 405 VLOG(1) << "Default search provider set to: " | |
| 406 << new_default_provider->short_name(); | |
| 407 url_service->SetDefaultSearchProvider(new_default_provider); | |
| 408 return new_default_provider; | |
| 409 } | |
| 410 | |
| 411 bool DefaultSearchProviderChange::NewSearchProviderValid() const { | |
| 412 return new_search_provider_ && new_search_provider_->SupportsReplacement(); | |
| 413 } | |
| 414 | |
| 415 void DefaultSearchProviderChange::OpenSearchEngineSettings(Browser* browser) { | |
| 416 ProtectorServiceFactory::GetForProfile(profile())->OpenTab( | |
| 417 GURL(std::string(chrome::kChromeUISettingsURL) + | |
| 418 chrome::kSearchEnginesSubPage), browser); | |
| 419 } | |
| 420 | |
| 421 TemplateURLService* DefaultSearchProviderChange::GetTemplateURLService() { | |
| 422 TemplateURLService* url_service = | |
| 423 TemplateURLServiceFactory::GetForProfile(profile()); | |
| 424 DCHECK(url_service); | |
| 425 return url_service; | |
| 426 } | |
| 427 | |
| 428 BaseSettingChange* CreateDefaultSearchProviderChange(TemplateURL* actual, | |
| 429 TemplateURL* backup) { | |
| 430 return new DefaultSearchProviderChange(actual, backup); | |
| 431 } | |
| 432 | |
| 433 } // namespace protector | |
| OLD | NEW |