| 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 |