diff options
author | jeanluc@google.com <jeanluc@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-05 23:49:31 +0000 |
---|---|---|
committer | jeanluc@google.com <jeanluc@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-05 23:49:31 +0000 |
commit | b6fd1fa73e3905cb343a78b84a88aef3cb53ab40 (patch) | |
tree | 61b04a98b99aaca3138b868402b2005b59594119 /chrome/browser/search_engines | |
parent | 2858bbf9099f835325422009c5945e9b26a97d42 (diff) | |
download | chromium_src-b6fd1fa73e3905cb343a78b84a88aef3cb53ab40.zip chromium_src-b6fd1fa73e3905cb343a78b84a88aef3cb53ab40.tar.gz chromium_src-b6fd1fa73e3905cb343a78b84a88aef3cb53ab40.tar.bz2 |
Allow the default search provider to be specified via group policy. Also allow group policy to specify that there is no default. Respond to at run time to changes of policy. Handle the default search becoming managed or un-managed, or having the default change while staying managed. Optimize notification of observers so that we don't repeatedly notify observers for what is one change.
Change in Preferences.xib: Add defaultSearchEngineEnabled keypath to enable/disable the default search engine combobox in the Basic preference pane.
BUG=49306
TEST=ConfigurationPolicyPrefStoreDefaultSearchTest.*, TemplateURLModelTest.*, KeywordEditorControllerTest*, SearchProviderInstall*, ConfigDirPolicyProviderValueTestInstance*, ConfigurationPolicyProvider*
Review URL: http://codereview.chromium.org/3402023
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61588 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/search_engines')
11 files changed, 831 insertions, 282 deletions
diff --git a/chrome/browser/search_engines/keyword_editor_controller.cc b/chrome/browser/search_engines/keyword_editor_controller.cc index f40fdca..ac7d636 100644 --- a/chrome/browser/search_engines/keyword_editor_controller.cc +++ b/chrome/browser/search_engines/keyword_editor_controller.cc @@ -79,7 +79,7 @@ void KeywordEditorController::ModifyTemplateURL(const TemplateURL* template_url, } bool KeywordEditorController::CanEdit(const TemplateURL* url) const { - return !url_model()->IsDefaultSearchManaged() || + return !url_model()->is_default_search_managed() || url != url_model()->GetDefaultSearchProvider(); } @@ -87,7 +87,7 @@ bool KeywordEditorController::CanMakeDefault(const TemplateURL* url) const { return (url != url_model()->GetDefaultSearchProvider() && url->url() && url->url()->SupportsReplacement() && - !url_model()->IsDefaultSearchManaged()); + !url_model()->is_default_search_managed()); } bool KeywordEditorController::CanRemove(const TemplateURL* url) const { diff --git a/chrome/browser/search_engines/keyword_editor_controller_unittest.cc b/chrome/browser/search_engines/keyword_editor_controller_unittest.cc index ca817ec..8103ee0 100644 --- a/chrome/browser/search_engines/keyword_editor_controller_unittest.cc +++ b/chrome/browser/search_engines/keyword_editor_controller_unittest.cc @@ -10,6 +10,9 @@ #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/browser/search_engines/template_url_table_model.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_source.h" #include "chrome/common/pref_names.h" #include "chrome/test/testing_pref_service.h" #include "chrome/test/testing_profile.h" @@ -62,12 +65,27 @@ class KeywordEditorControllerTest : public testing::Test, removed_count_ = 0; } - void SimulateDefaultSearchIsManaged(const TemplateURL* turl) { - ASSERT_TRUE(turl->url() != NULL); - model_->SetDefaultSearchProvider(turl); + void SimulateDefaultSearchIsManaged(const std::string& url) { + ASSERT_FALSE(url.empty()); TestingPrefService* service = profile_->GetTestingPrefService(); - service->SetManagedPref(prefs::kDefaultSearchProviderSearchURL, - Value::CreateStringValue(turl->url()->url())); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderEnabled, + Value::CreateBooleanValue(true)); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderSearchURL, + Value::CreateStringValue(url)); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderName, + Value::CreateStringValue("managed")); + // Clear the IDs that are not specified via policy. + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderID, new StringValue("")); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderPrepopulateID, new StringValue("")); + model_->Observe( + NotificationType::PREF_CHANGED, + Source<PrefService>(profile_->GetTestingPrefService()), + Details<std::string>(NULL)); } TemplateURLTableModel* table_model() const { @@ -177,8 +195,8 @@ TEST_F(KeywordEditorControllerTest, CannotSetDefaultWhileManaged) { EXPECT_TRUE(controller_->CanMakeDefault(turl1)); EXPECT_TRUE(controller_->CanMakeDefault(turl2)); - SimulateDefaultSearchIsManaged(turl2); - EXPECT_TRUE(model_->IsDefaultSearchManaged()); + SimulateDefaultSearchIsManaged(turl2->url()->url()); + EXPECT_TRUE(model_->is_default_search_managed()); EXPECT_FALSE(controller_->CanMakeDefault(turl1)); EXPECT_FALSE(controller_->CanMakeDefault(turl2)); @@ -199,11 +217,13 @@ TEST_F(KeywordEditorControllerTest, EditManagedDefault) { EXPECT_TRUE(controller_->CanEdit(turl1)); EXPECT_TRUE(controller_->CanEdit(turl2)); - SimulateDefaultSearchIsManaged(turl2); - EXPECT_TRUE(model_->IsDefaultSearchManaged()); - + // Simulate setting a managed default. This will add another template URL to + // the model. + SimulateDefaultSearchIsManaged(turl2->url()->url()); + EXPECT_TRUE(model_->is_default_search_managed()); EXPECT_TRUE(controller_->CanEdit(turl1)); - EXPECT_FALSE(controller_->CanEdit(turl2)); + EXPECT_TRUE(controller_->CanEdit(turl2)); + EXPECT_FALSE(controller_->CanEdit(model_->GetDefaultSearchProvider())); } TEST_F(KeywordEditorControllerTest, MakeDefaultNoWebData) { diff --git a/chrome/browser/search_engines/search_provider_install_data_unittest.cc b/chrome/browser/search_engines/search_provider_install_data_unittest.cc index 9cdfa82..50b4a5a 100644 --- a/chrome/browser/search_engines/search_provider_install_data_unittest.cc +++ b/chrome/browser/search_engines/search_provider_install_data_unittest.cc @@ -16,6 +16,9 @@ #include "chrome/common/notification_service.h" #include "chrome/common/notification_source.h" #include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/testing_pref_service.h" +#include "chrome/test/testing_profile.h" #include "testing/gtest/include/gtest/gtest.h" // Create a TemplateURL. The caller owns the returned TemplateURL*. @@ -188,6 +191,29 @@ class SearchProviderInstallDataTest : public testing::Test { testing::Test::TearDown(); } + void SimulateDefaultSearchIsManaged(const std::string& url) { + ASSERT_FALSE(url.empty()); + TestingPrefService* service = util_.profile()->GetTestingPrefService(); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderEnabled, + Value::CreateBooleanValue(true)); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderSearchURL, + Value::CreateStringValue(url)); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderName, + Value::CreateStringValue("managed")); + // Clear the IDs that are not specified via policy. + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderID, new StringValue("")); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderPrepopulateID, new StringValue("")); + util_.model()->Observe( + NotificationType::PREF_CHANGED, + Source<PrefService>(util_.profile()->GetTestingPrefService()), + Details<std::string>(NULL)); + } + protected: TemplateURLModelTestUtil util_; scoped_ptr<ChromeThread> io_thread_; @@ -227,6 +253,33 @@ TEST_F(SearchProviderInstallDataTest, GetInstallState) { } +TEST_F(SearchProviderInstallDataTest, ManagedDefaultSearch) { + // Set up the database. + util_.ChangeModelToLoadState(); + std::string host = "www.unittest.com"; + TemplateURL* t_url = CreateTemplateURL("http://" + host + "/path", + L"unittest"); + util_.model()->Add(t_url); + + // Set a managed preference that establishes a default search provider. + std::string host2 = "www.managedtest.com"; + std::string url2 = "http://" + host2 + "/p{searchTerms}"; + SimulateDefaultSearchIsManaged(url2); + EXPECT_TRUE(util_.model()->is_default_search_managed()); + + // Wait for the changes to be saved. + util_.BlockTillServiceProcessesRequests(); + + // Verify the search providers install state. The default search should be + // the managed one we previously set. + scoped_refptr<TestGetInstallState> test_get_install_state( + new TestGetInstallState(install_data_)); + test_get_install_state->set_search_provider_host(host); + test_get_install_state->set_default_search_provider_host(host2); + EXPECT_TRUE(test_get_install_state->RunTests(*io_thread_.get())); +} + + TEST_F(SearchProviderInstallDataTest, GoogleBaseUrlChange) { scoped_refptr<TestGetInstallState> test_get_install_state( new TestGetInstallState(install_data_)); diff --git a/chrome/browser/search_engines/template_url.cc b/chrome/browser/search_engines/template_url.cc index 923b647..3a2aa2f 100644 --- a/chrome/browser/search_engines/template_url.cc +++ b/chrome/browser/search_engines/template_url.cc @@ -513,6 +513,12 @@ bool TemplateURLRef::HasGoogleBaseURLs() const { return false; } +// static +bool TemplateURLRef::SameUrlRefs(const TemplateURLRef* ref1, + const TemplateURLRef* ref2) { + return ref1 == ref2 || (ref1 && ref2 && ref1->url() == ref2->url()); +} + void TemplateURLRef::InvalidateCachedValues() const { supports_replacements_ = valid_ = parsed_ = false; host_.clear(); diff --git a/chrome/browser/search_engines/template_url.h b/chrome/browser/search_engines/template_url.h index b48da25..f90b372 100644 --- a/chrome/browser/search_engines/template_url.h +++ b/chrome/browser/search_engines/template_url.h @@ -127,6 +127,10 @@ class TemplateURLRef { // {google:baseURL} or {google:baseSuggestURL}. bool HasGoogleBaseURLs() const; + // Returns true if both refs are NULL or have the same values. + static bool SameUrlRefs(const TemplateURLRef* ref1, + const TemplateURLRef* ref2); + private: friend class SearchHostToURLsMapTest; friend class TemplateURL; diff --git a/chrome/browser/search_engines/template_url_model.cc b/chrome/browser/search_engines/template_url_model.cc index e4df284..49be40e 100644 --- a/chrome/browser/search_engines/template_url_model.cc +++ b/chrome/browser/search_engines/template_url_model.cc @@ -9,6 +9,7 @@ #include "base/environment.h" #include "base/stl_util-inl.h" #include "base/string_number_conversions.h" +#include "base/string_split.h" #include "base/utf_string_conversions.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/google/google_url_tracker.h" @@ -16,6 +17,7 @@ #include "chrome/browser/history/history_notifications.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/prefs/pref_set_observer.h" #include "chrome/browser/profile.h" #include "chrome/browser/rlz/rlz.h" #include "chrome/browser/search_engines/search_host_to_urls_map.h" @@ -46,6 +48,30 @@ static const char kTemplateParameter[] = "%s"; // have the same url. static const wchar_t kReplacementTerm[] = L"blah.blah.blah.blah.blah"; + +// Removes from the vector any template URL that was created because of +// policy. These TemplateURLs are freed. +// Sets default_search_provider to NULL if it was one of them. +static void RemoveProvidersCreatedByPolicy( + std::vector<TemplateURL*>* template_urls, + const TemplateURL** default_search_provider) { + DCHECK(template_urls); + DCHECK(default_search_provider); + for (std::vector<TemplateURL*>::iterator i = template_urls->begin(); + i != template_urls->end(); ) { + TemplateURL* template_url = *i; + if (template_url->created_by_policy()) { + if (*default_search_provider && + (*default_search_provider)->id() == template_url->id()) + *default_search_provider = NULL; + i = template_urls->erase(i); + delete template_url; + } else { + ++i; + } + } +} + class TemplateURLModel::LessWithPrefix { public: // We want to find the set of keywords that begin with a prefix. The STL @@ -74,6 +100,7 @@ TemplateURLModel::TemplateURLModel(Profile* profile) load_failed_(false), load_handle_(0), default_search_provider_(NULL), + is_default_search_managed_(false), next_id_(1) { DCHECK(profile_); Init(NULL, 0); @@ -87,6 +114,7 @@ TemplateURLModel::TemplateURLModel(const Initializer* initializers, load_handle_(0), service_(NULL), default_search_provider_(NULL), + is_default_search_managed_(false), next_id_(1) { Init(initializers, count); } @@ -186,9 +214,9 @@ bool TemplateURLModel::CanReplaceKeyword( const std::wstring& keyword, const GURL& url, const TemplateURL** template_url_to_replace) { - DCHECK(!keyword.empty()); // This should only be called for non-empty - // keywords. If we need to support empty kewords - // the code needs to change slightly. + DCHECK(!keyword.empty()); // This should only be called for non-empty + // keywords. If we need to support empty kewords + // the code needs to change slightly. const TemplateURL* existing_url = GetTemplateURLForKeyword(keyword); if (existing_url) { // We already have a TemplateURL for this keyword. Only allow it to be @@ -249,72 +277,31 @@ const TemplateURL* TemplateURLModel::GetTemplateURLForHost( } void TemplateURLModel::Add(TemplateURL* template_url) { - DCHECK(template_url); - DCHECK(template_url->id() == 0); - DCHECK(find(template_urls_.begin(), template_urls_.end(), template_url) == - template_urls_.end()); - template_url->set_id(++next_id_); - template_urls_.push_back(template_url); - AddToMaps(template_url); - - if (service_.get()) - service_->AddKeyword(*template_url); - - if (loaded_) { - FOR_EACH_OBSERVER(TemplateURLModelObserver, model_observers_, - OnTemplateURLModelChanged()); - } + AddNoNotify(template_url); + NotifyObservers(); } void TemplateURLModel::Remove(const TemplateURL* template_url) { - TemplateURLVector::iterator i = find(template_urls_.begin(), - template_urls_.end(), - template_url); - if (i == template_urls_.end()) - return; - - if (template_url == default_search_provider_) { - // Should never delete the default search provider. - NOTREACHED(); - return; - } - - RemoveFromMaps(template_url); - - // Remove it from the vector containing all TemplateURLs. - template_urls_.erase(i); - - if (loaded_) { - FOR_EACH_OBSERVER(TemplateURLModelObserver, model_observers_, - OnTemplateURLModelChanged()); - } - - if (service_.get()) - service_->RemoveKeyword(*template_url); - - if (profile_) { - HistoryService* history = - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - if (history) - history->DeleteAllSearchTermsForKeyword(template_url->id()); - } - - // We own the TemplateURL and need to delete it. - delete template_url; + RemoveNoNotify(template_url); + NotifyObservers(); } void TemplateURLModel::RemoveAutoGeneratedBetween(Time created_after, Time created_before) { + bool should_notify = false; for (size_t i = 0; i < template_urls_.size();) { if (template_urls_[i]->date_created() >= created_after && (created_before.is_null() || template_urls_[i]->date_created() < created_before) && CanReplace(template_urls_[i])) { - Remove(template_urls_[i]); + RemoveNoNotify(template_urls_[i]); + should_notify = true; } else { ++i; } } + if (should_notify) + NotifyObservers(); } void TemplateURLModel::RemoveAutoGeneratedSince(Time created_after) { @@ -348,10 +335,11 @@ void TemplateURLModel::RegisterExtensionKeyword(Extension* extension) { if (existing_url) { // TODO(mpcomplete): only replace if the user hasn't changed the keyword. // (We don't have UI for that yet). - Update(existing_url, *template_url); + UpdateNoNotify(existing_url, *template_url); } else { - Add(template_url.release()); + AddNoNotify(template_url.release()); } + NotifyObservers(); } void TemplateURLModel::UnregisterExtensionKeyword(Extension* extension) { @@ -398,79 +386,27 @@ void TemplateURLModel::ResetTemplateURL(const TemplateURL* url, new_url.SetURL(search_url, 0, 0); } new_url.set_safe_for_autoreplace(false); - Update(url, new_url); + UpdateNoNotify(url, new_url); + NotifyObservers(); } void TemplateURLModel::SetDefaultSearchProvider(const TemplateURL* url) { - if (default_search_provider_ == url) + if (is_default_search_managed_) { + NOTREACHED(); return; - - DCHECK(!url || find(template_urls_.begin(), template_urls_.end(), url) != - template_urls_.end()); - default_search_provider_ = url; - - if (url) { - TemplateURL* modifiable_url = const_cast<TemplateURL*>(url); - // Don't mark the url as edited, otherwise we won't be able to rev the - // templateurls we ship with. - modifiable_url->set_show_in_default_list(true); - if (service_.get()) - service_.get()->UpdateKeyword(*url); - - const TemplateURLRef* url_ref = url->url(); - if (url_ref && url_ref->HasGoogleBaseURLs()) { - GoogleURLTracker::RequestServerCheck(); -#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD) - RLZTracker::RecordProductEvent(rlz_lib::CHROME, - rlz_lib::CHROME_OMNIBOX, - rlz_lib::SET_TO_GOOGLE); -#endif - } - } - - SaveDefaultSearchProviderToPrefs(url); - - if (service_.get()) - service_->SetDefaultSearchProvider(url); - - if (loaded_) { - FOR_EACH_OBSERVER(TemplateURLModelObserver, model_observers_, - OnTemplateURLModelChanged()); } + if (default_search_provider_ == url) + return; + SetDefaultSearchProviderNoNotify(url); + NotifyObservers(); } const TemplateURL* TemplateURLModel::GetDefaultSearchProvider() { if (loaded_ && !load_failed_) return default_search_provider_; - if (!prefs_default_search_provider_.get()) { - TemplateURL* default_from_prefs; - if (LoadDefaultSearchProviderFromPrefs(&default_from_prefs)) { - prefs_default_search_provider_.reset(default_from_prefs); - } else { - std::vector<TemplateURL*> loaded_urls; - size_t default_search_index; - TemplateURLPrepopulateData::GetPrepopulatedEngines(GetPrefs(), - &loaded_urls, - &default_search_index); - if (default_search_index < loaded_urls.size()) { - prefs_default_search_provider_.reset(loaded_urls[default_search_index]); - loaded_urls.erase(loaded_urls.begin() + default_search_index); - } - STLDeleteElements(&loaded_urls); - } - } - - return prefs_default_search_provider_.get(); -} - -bool TemplateURLModel::IsDefaultSearchManaged() { - PrefService* prefs = GetPrefs(); - if (!prefs) - return false; - const PrefService::Preference* pref = - prefs->FindPreference(prefs::kDefaultSearchProviderSearchURL); - return pref && pref->IsManaged(); + // We're not loaded, rely on the default search provider stored in prefs. + return initial_default_search_provider_.get(); } void TemplateURLModel::AddObserver(TemplateURLModelObserver* observer) { @@ -512,9 +448,10 @@ void TemplateURLModel::OnWebDataServiceRequestDone( return; } - // prefs_default_search_provider_ is only needed before we've finished + // initial_default_search_provider_ is only needed before we've finished // loading. Now that we've loaded we can nuke it. - prefs_default_search_provider_.reset(); + initial_default_search_provider_.reset(); + is_default_search_managed_ = false; std::vector<TemplateURL*> template_urls; const TemplateURL* default_search_provider = NULL; @@ -526,23 +463,56 @@ void TemplateURLModel::OnWebDataServiceRequestDone( &default_search_provider, &new_resource_keyword_version); - // If the default search provider existed previously, then just - // set the member variable. Otherwise, we'll set it using the method - // to ensure that it is saved properly after its id is set. - if (default_search_provider && default_search_provider->id() != 0) { - default_search_provider_ = default_search_provider; - default_search_provider = NULL; - } - SetTemplateURLs(template_urls); - - if (default_search_provider) { + bool database_specified_a_default = NULL != default_search_provider; + + // Remove entries that were created because of policy as they may have + // changed since the database was saved. + RemoveProvidersCreatedByPolicy(&template_urls, &default_search_provider); + + // Check if default search provider is now managed. + scoped_ptr<TemplateURL> default_from_prefs; + LoadDefaultSearchProviderFromPrefs(&default_from_prefs, + &is_default_search_managed_); + + if (is_default_search_managed_) { + SetTemplateURLs(template_urls); + // Set the default. AddNoNotify will take ownership of default_from_prefs + // so it is safe to release. If it's null, there's no ownership to worry + // about :-) + TemplateURL* managed_default = default_from_prefs.release(); + if (managed_default) { + managed_default->set_created_by_policy(true); + managed_default->set_id(0); + AddNoNotify(managed_default); + } // Note that this saves the default search provider to prefs. - SetDefaultSearchProvider(default_search_provider); + SetDefaultSearchProviderNoNotify(managed_default); } else { - // Always save the default search provider to prefs. That way we don't - // have to worry about it being out of sync. - if (default_search_provider_) - SaveDefaultSearchProviderToPrefs(default_search_provider_); + // If we had a managed default, replace it with the first provider of + // the list. + if (database_specified_a_default && + NULL == default_search_provider && + !template_urls.empty()) + default_search_provider = template_urls[0]; + + // If the default search provider existed previously, then just + // set the member variable. Otherwise, we'll set it using the method + // to ensure that it is saved properly after its id is set. + if (default_search_provider && default_search_provider->id() != 0) { + default_search_provider_ = default_search_provider; + default_search_provider = NULL; + } + SetTemplateURLs(template_urls); + + if (default_search_provider) { + // Note that this saves the default search provider to prefs. + SetDefaultSearchProvider(default_search_provider); + } else { + // Always save the default search provider to prefs. That way we don't + // have to worry about it being out of sync. + if (default_search_provider_) + SaveDefaultSearchProviderToPrefs(default_search_provider_); + } } // This initializes provider_map_ which should be done before @@ -557,9 +527,7 @@ void TemplateURLModel::OnWebDataServiceRequestDone( if (new_resource_keyword_version && service_.get()) service_->SetBuiltinKeywordVersion(new_resource_keyword_version); - FOR_EACH_OBSERVER(TemplateURLModelObserver, model_observers_, - OnTemplateURLModelChanged()); - + NotifyObservers(); NotifyLoaded(); } @@ -582,7 +550,6 @@ void TemplateURLModel::Observe(NotificationType type, const NotificationDetails& details) { if (type == NotificationType::HISTORY_URL_VISITED) { Details<history::URLVisitedDetails> visit_details(details); - if (!loaded()) visits_to_add_.push_back(*visit_details.ptr()); else @@ -590,11 +557,39 @@ void TemplateURLModel::Observe(NotificationType type, } else if (type == NotificationType::GOOGLE_URL_UPDATED) { if (loaded_) GoogleBaseURLChanged(); + } else if (type == NotificationType::PREF_CHANGED) { + const std::string* pref_name = Details<std::string>(details).ptr(); + if (!pref_name || default_search_prefs_->IsObserved(*pref_name)) { + // A preference related to default search engine has changed. + // Update the model if needed. + UpdateDefaultSearch(); + } } else { NOTREACHED(); } } +void TemplateURLModel::RegisterUserPrefs(PrefService* prefs) { + prefs->RegisterBooleanPref( + prefs::kDefaultSearchProviderEnabled, true); + prefs->RegisterStringPref( + prefs::kDefaultSearchProviderName, std::string()); + prefs->RegisterStringPref( + prefs::kDefaultSearchProviderID, std::string()); + prefs->RegisterStringPref( + prefs::kDefaultSearchProviderPrepopulateID, std::string()); + prefs->RegisterStringPref( + prefs::kDefaultSearchProviderSuggestURL, std::string()); + prefs->RegisterStringPref( + prefs::kDefaultSearchProviderSearchURL, std::string()); + prefs->RegisterStringPref( + prefs::kDefaultSearchProviderKeyword, std::string()); + prefs->RegisterStringPref( + prefs::kDefaultSearchProviderIconURL, std::string()); + prefs->RegisterStringPref( + prefs::kDefaultSearchProviderEncodings, std::string()); +} + void TemplateURLModel::SetKeywordSearchTermsForURL(const TemplateURL* t_url, const GURL& url, const std::wstring& term) { @@ -616,6 +611,9 @@ void TemplateURLModel::Init(const Initializer* initializers, // navigates. registrar_.Add(this, NotificationType::HISTORY_URL_VISITED, Source<Profile>(profile_->GetOriginalProfile())); + PrefService* prefs = GetPrefs(); + default_search_prefs_.reset( + PrefSetObserver::CreateDefaultSearchPrefSetObserver(prefs, this)); } registrar_.Add(this, NotificationType::GOOGLE_URL_UPDATED, NotificationService::AllSources()); @@ -643,19 +641,24 @@ void TemplateURLModel::Init(const Initializer* initializers, template_url->set_keyword(initializers[i].keyword); template_url->set_short_name(initializers[i].content); template_url->SetURL(osd_url, 0, 0); - Add(template_url); + AddNoNotify(template_url); } } - // Request a server check for the correct Google URL if Google is the default - // search engine, not in headless mode and not in Chrome Frame. - const TemplateURL* default_provider = GetDefaultSearchProvider(); - scoped_ptr<base::Environment> env(base::Environment::Create()); - if (default_provider && !env->HasVar(env_vars::kHeadless) && - !CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeFrame)) { - const TemplateURLRef* default_provider_ref = default_provider->url(); - if (default_provider_ref && default_provider_ref->HasGoogleBaseURLs()) - GoogleURLTracker::RequestServerCheck(); + // Initialize default search. + UpdateDefaultSearch(); + + // Request a server check for the correct Google URL if Google is the + // default search engine, not in headless mode and not in Chrome Frame. + if (initial_default_search_provider_.get()) { + const TemplateURLRef* default_provider_ref = + initial_default_search_provider_->url(); + if (default_provider_ref && default_provider_ref->HasGoogleBaseURLs()) { + scoped_ptr<base::Environment> env(base::Environment::Create()); + if (!env->HasVar(env_vars::kHeadless) && + !CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeFrame)) + GoogleURLTracker::RequestServerCheck(); + } } } @@ -710,7 +713,7 @@ void TemplateURLModel::SetTemplateURLs(const std::vector<TemplateURL*>& urls) { ++i) { if ((*i)->id() != 0) continue; - Add(*i); + AddNoNotify(*i); } } @@ -743,66 +746,90 @@ void TemplateURLModel::SaveDefaultSearchProviderToPrefs( if (!prefs) return; - RegisterPrefs(prefs); - - const std::string search_url = - (t_url && t_url->url()) ? t_url->url()->url() : std::string(); + bool enabled = false; + std::string search_url; + std::string suggest_url; + std::string icon_url; + std::string encodings; + std::string short_name; + std::string keyword; + std::string id_string; + std::string prepopulate_id; + if (t_url) { + enabled = true; + if (t_url->url()) + search_url = t_url->url()->url(); + if (t_url->suggestions_url()) + suggest_url = t_url->suggestions_url()->url(); + GURL icon_gurl = t_url->GetFavIconURL(); + if (!icon_gurl.is_empty()) + icon_url = icon_gurl.spec(); + encodings = JoinString(t_url->input_encodings(), ';'); + short_name = WideToUTF8(t_url->short_name()); + keyword = WideToUTF8(t_url->keyword()); + id_string = base::Int64ToString(t_url->id()); + prepopulate_id = base::Int64ToString(t_url->prepopulate_id()); + } + prefs->SetBoolean(prefs::kDefaultSearchProviderEnabled, enabled); prefs->SetString(prefs::kDefaultSearchProviderSearchURL, search_url); - - const std::string suggest_url = - (t_url && t_url->suggestions_url()) ? t_url->suggestions_url()->url() : - std::string(); prefs->SetString(prefs::kDefaultSearchProviderSuggestURL, suggest_url); - - const std::string name = - t_url ? WideToUTF8(t_url->short_name()) : std::string(); - prefs->SetString(prefs::kDefaultSearchProviderName, name); - - const std::string id_string = - t_url ? base::Int64ToString(t_url->id()) : std::string(); + prefs->SetString(prefs::kDefaultSearchProviderIconURL, icon_url); + prefs->SetString(prefs::kDefaultSearchProviderEncodings, encodings); + prefs->SetString(prefs::kDefaultSearchProviderName, short_name); + prefs->SetString(prefs::kDefaultSearchProviderKeyword, keyword); prefs->SetString(prefs::kDefaultSearchProviderID, id_string); - - const std::string prepopulate_id = - t_url ? base::Int64ToString(t_url->prepopulate_id()) : std::string(); prefs->SetString(prefs::kDefaultSearchProviderPrepopulateID, prepopulate_id); prefs->ScheduleSavePersistentPrefs(); } bool TemplateURLModel::LoadDefaultSearchProviderFromPrefs( - TemplateURL** default_provider) { + scoped_ptr<TemplateURL>* default_provider, + bool* is_managed) { PrefService* prefs = GetPrefs(); - if (!prefs || !prefs->HasPrefPath(prefs::kDefaultSearchProviderSearchURL) || - !prefs->HasPrefPath(prefs::kDefaultSearchProviderSuggestURL) || - !prefs->HasPrefPath(prefs::kDefaultSearchProviderName) || - !prefs->HasPrefPath(prefs::kDefaultSearchProviderID)) { + if (!prefs || !prefs->HasPrefPath(prefs::kDefaultSearchProviderSearchURL)) return false; - } - RegisterPrefs(prefs); + // By default, kDefaultSearchProviderEnabled is true. Users of previous + // versions will transition correctly. + const PrefService::Preference* pref = + prefs->FindPreference(prefs::kDefaultSearchProviderEnabled); + *is_managed = pref && pref->IsManaged(); + + bool enabled = + prefs->GetBoolean(prefs::kDefaultSearchProviderEnabled); std::string suggest_url = prefs->GetString(prefs::kDefaultSearchProviderSuggestURL); std::string search_url = prefs->GetString(prefs::kDefaultSearchProviderSearchURL); - if (suggest_url.empty() && search_url.empty()) { + if (!enabled || (suggest_url.empty() && search_url.empty())) { // The user doesn't want a default search provider. - *default_provider = NULL; + default_provider->reset(NULL); return true; } std::wstring name = UTF8ToWide(prefs->GetString(prefs::kDefaultSearchProviderName)); - + std::wstring keyword = + UTF8ToWide(prefs->GetString(prefs::kDefaultSearchProviderKeyword)); + std::string icon_url = + prefs->GetString(prefs::kDefaultSearchProviderIconURL); + std::string encodings = + prefs->GetString(prefs::kDefaultSearchProviderEncodings); std::string id_string = prefs->GetString(prefs::kDefaultSearchProviderID); - std::string prepopulate_id = prefs->GetString(prefs::kDefaultSearchProviderPrepopulateID); - *default_provider = new TemplateURL(); + default_provider->reset(new TemplateURL()); (*default_provider)->set_short_name(name); (*default_provider)->SetURL(search_url, 0, 0); (*default_provider)->SetSuggestionsURL(suggest_url, 0, 0); + (*default_provider)->set_keyword(keyword); + (*default_provider)->SetFavIconURL(GURL(icon_url)); + std::vector<std::string> encodings_vector; + SplitString(encodings, ';', &encodings_vector); + (*default_provider)->set_input_encodings(encodings_vector); if (!id_string.empty()) { int64 value; base::StringToInt64(id_string, &value); @@ -813,24 +840,28 @@ bool TemplateURLModel::LoadDefaultSearchProviderFromPrefs( base::StringToInt(prepopulate_id, &value); (*default_provider)->set_prepopulate_id(value); } + (*default_provider)->set_show_in_default_list(true); return true; } -void TemplateURLModel::RegisterPrefs(PrefService* prefs) { - if (prefs->FindPreference(prefs::kDefaultSearchProviderName)) - return; - prefs->RegisterStringPref( - prefs::kDefaultSearchProviderName, std::string()); - prefs->RegisterStringPref( - prefs::kDefaultSearchProviderID, std::string()); - prefs->RegisterStringPref( - prefs::kDefaultSearchProviderPrepopulateID, std::string()); - prefs->RegisterStringPref( - prefs::kDefaultSearchProviderSuggestURL, std::string()); - prefs->RegisterStringPref( - prefs::kDefaultSearchProviderSearchURL, std::string()); +static bool TemplateURLsHaveSamePrefs(const TemplateURL* url1, + const TemplateURL* url2) { + if (url1 == url2) + return true; + return NULL != url1 && + NULL != url2 && + url1->short_name() == url2->short_name() && + url1->keyword() == url2->keyword() && + TemplateURLRef::SameUrlRefs(url1->url(), url2->url()) && + TemplateURLRef::SameUrlRefs(url1->suggestions_url(), + url2->suggestions_url()) && + url1->GetFavIconURL() == url2->GetFavIconURL() && + url1->safe_for_autoreplace() == url2->safe_for_autoreplace() && + url1->show_in_default_list() == url2->show_in_default_list() && + url1->input_encodings() == url2->input_encodings(); } + bool TemplateURLModel::CanReplaceKeywordForHost( const std::string& host, const TemplateURL** to_replace) { @@ -857,8 +888,8 @@ bool TemplateURLModel::CanReplace(const TemplateURL* t_url) { t_url->safe_for_autoreplace()); } -void TemplateURLModel::Update(const TemplateURL* existing_turl, - const TemplateURL& new_values) { +void TemplateURLModel::UpdateNoNotify(const TemplateURL* existing_turl, + const TemplateURL& new_values) { DCHECK(loaded_); DCHECK(existing_turl); DCHECK(find(template_urls_.begin(), template_urls_.end(), existing_turl) != @@ -877,15 +908,8 @@ void TemplateURLModel::Update(const TemplateURL* existing_turl, if (service_.get()) service_->UpdateKeyword(*existing_turl); - if (default_search_provider_ == existing_turl) { - // Force an update to happen to account for any changes - // that occurred during the update. - default_search_provider_ = NULL; - SetDefaultSearchProvider(existing_turl); - } - - FOR_EACH_OBSERVER(TemplateURLModelObserver, model_observers_, - OnTemplateURLModelChanged()); + if (default_search_provider_ == existing_turl) + SetDefaultSearchProviderNoNotify(existing_turl); } PrefService* TemplateURLModel::GetPrefs() { @@ -1029,7 +1053,181 @@ void TemplateURLModel::GoogleBaseURLChanged() { if (something_changed && loaded_) { UIThreadSearchTermsData search_terms_data; provider_map_.UpdateGoogleBaseURLs(search_terms_data); - FOR_EACH_OBSERVER(TemplateURLModelObserver, model_observers_, - OnTemplateURLModelChanged()); + NotifyObservers(); + } +} + +void TemplateURLModel::UpdateDefaultSearch() { + if (!loaded_) { + // Set |initial_default_search_provider_| from the preferences. We use this + // value for default search provider until the database has been loaded. + if (!LoadDefaultSearchProviderFromPrefs(&initial_default_search_provider_, + &is_default_search_managed_)) { + // Prefs does not specify, so rely on the prepopulated engines. This + // should happen only the first time Chrome is started. + initial_default_search_provider_.reset( + TemplateURLPrepopulateData::GetPrepopulatedDefaultSearch(GetPrefs())); + is_default_search_managed_ = false; + } + return; + } + // Load the default search specified in prefs. + scoped_ptr<TemplateURL> new_default_from_prefs; + bool new_is_default_managed = false; + // Load the default from prefs. It's possible that it won't succeed + // because we are in the middle of doing SaveDefaultSearchProviderToPrefs() + // and all the preference items have not been saved. In that case, we + // don't have yet a default. It would be much better if we could save + // preferences in batches and trigger notifications at the end. + LoadDefaultSearchProviderFromPrefs(&new_default_from_prefs, + &new_is_default_managed); + if (!is_default_search_managed_ && !new_is_default_managed) { + // We're not interested in cases where the default was and remains + // unmanaged. In that case, preferences have no impact on the default. + return; + } + if (is_default_search_managed_ && new_is_default_managed) { + // The default was managed and remains managed. Update the default only + // if it has changed; we don't want to respond to changes triggered by + // SaveDefaultSearchProviderToPrefs. + if (TemplateURLsHaveSamePrefs(default_search_provider_, + new_default_from_prefs.get())) + return; + if (new_default_from_prefs.get()) { + new_default_from_prefs->set_created_by_policy(true); + UpdateNoNotify(default_search_provider_, *new_default_from_prefs.get()); + } else { + const TemplateURL* old_default = default_search_provider_; + SetDefaultSearchProviderNoNotify(NULL); + RemoveNoNotify(old_default); + } + } else if (!is_default_search_managed_ && new_is_default_managed) { + // The default used to be unmanaged and is now managed. Add the new + // managed default to the list of URLs and set it as default. + is_default_search_managed_ = new_is_default_managed; + // AddNoNotify will take ownership of new_template, so it's safe to + // release. + TemplateURL* new_template = new_default_from_prefs.release(); + if (new_template) { + new_template->set_created_by_policy(true); + AddNoNotify(new_template); + } + SetDefaultSearchProviderNoNotify(new_template); + } else { + // The default was managed and is no longer. + DCHECK(is_default_search_managed_ && !new_is_default_managed); + is_default_search_managed_ = new_is_default_managed; + // If we had a default, delete the previous default if created by policy + // and set a likely default. + if (NULL != default_search_provider_ && + default_search_provider_->created_by_policy()) { + const TemplateURL* old_default = default_search_provider_; + default_search_provider_ = NULL; + RemoveNoNotify(old_default); + } + SetDefaultSearchProviderNoNotify(FindNewDefaultSearchProvider()); + } + NotifyObservers(); +} + +const TemplateURL* TemplateURLModel::FindNewDefaultSearchProvider() { + // See if the prepoluated default still exists. + scoped_ptr<TemplateURL> prepopulated_default( + TemplateURLPrepopulateData::GetPrepopulatedDefaultSearch(GetPrefs())); + for (TemplateURLVector::iterator i = template_urls_.begin(); + i != template_urls_.end(); ) { + if ((*i)->prepopulate_id() == prepopulated_default->prepopulate_id()) + return *i; + } + // If not, use the first of the templates. + if (!template_urls_.empty()) { + return template_urls_[0]; + } + return NULL; +} + +void TemplateURLModel::SetDefaultSearchProviderNoNotify( + const TemplateURL* url) { + DCHECK(!url || find(template_urls_.begin(), template_urls_.end(), url) != + template_urls_.end()); + default_search_provider_ = url; + + if (url) { + TemplateURL* modifiable_url = const_cast<TemplateURL*>(url); + // Don't mark the url as edited, otherwise we won't be able to rev the + // template urls we ship with. + modifiable_url->set_show_in_default_list(true); + if (service_.get()) + service_.get()->UpdateKeyword(*url); + + const TemplateURLRef* url_ref = url->url(); + if (url_ref && url_ref->HasGoogleBaseURLs()) { + GoogleURLTracker::RequestServerCheck(); +#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD) + RLZTracker::RecordProductEvent(rlz_lib::CHROME, + rlz_lib::CHROME_OMNIBOX, + rlz_lib::SET_TO_GOOGLE); +#endif + } + } + + if (!is_default_search_managed_) + SaveDefaultSearchProviderToPrefs(url); + + if (service_.get()) + service_->SetDefaultSearchProvider(url); +} + +void TemplateURLModel::AddNoNotify(TemplateURL* template_url) { + DCHECK(template_url); + DCHECK(template_url->id() == 0); + DCHECK(find(template_urls_.begin(), template_urls_.end(), template_url) == + template_urls_.end()); + template_url->set_id(++next_id_); + template_urls_.push_back(template_url); + AddToMaps(template_url); + + if (service_.get()) + service_->AddKeyword(*template_url); +} + +void TemplateURLModel::RemoveNoNotify(const TemplateURL* template_url) { + TemplateURLVector::iterator i = find(template_urls_.begin(), + template_urls_.end(), + template_url); + if (i == template_urls_.end()) + return; + + if (template_url == default_search_provider_) { + // Should never delete the default search provider. + NOTREACHED(); + return; } + + RemoveFromMaps(template_url); + + // Remove it from the vector containing all TemplateURLs. + template_urls_.erase(i); + + if (service_.get()) + service_->RemoveKeyword(*template_url); + + if (profile_) { + HistoryService* history = + profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); + if (history) + history->DeleteAllSearchTermsForKeyword(template_url->id()); + } + + // We own the TemplateURL and need to delete it. + delete template_url; } + +void TemplateURLModel::NotifyObservers() { + if (!loaded_) + return; + + FOR_EACH_OBSERVER(TemplateURLModelObserver, model_observers_, + OnTemplateURLModelChanged()); +} + diff --git a/chrome/browser/search_engines/template_url_model.h b/chrome/browser/search_engines/template_url_model.h index 7dd532f..76204f8 100644 --- a/chrome/browser/search_engines/template_url_model.h +++ b/chrome/browser/search_engines/template_url_model.h @@ -6,7 +6,10 @@ #define CHROME_BROWSER_SEARCH_ENGINES_TEMPLATE_URL_MODEL_H_ #pragma once +#include <map> #include <set> +#include <string> +#include <vector> #include "base/gtest_prod_util.h" #include "base/observer_list.h" @@ -21,9 +24,11 @@ class GURL; class Extension; class PrefService; class Profile; +class PrefSetObserver; class SearchHostToURLsMap; class SearchTermsData; class TemplateURLModelObserver; +class TemplateURLRef; namespace history { struct URLVisitedDetails; @@ -165,7 +170,9 @@ class TemplateURLModel : public WebDataServiceConsumer, const std::wstring& keyword, const std::string& search_url); - // The default search provider. This may be null. + // Set the default search provider. |url| may be null. + // This will assert if the default search is managed; the UI should not be + // invoking this method in that situation. void SetDefaultSearchProvider(const TemplateURL* url); // Returns the default search provider. If the TemplateURLModel hasn't been @@ -175,7 +182,7 @@ class TemplateURLModel : public WebDataServiceConsumer, const TemplateURL* GetDefaultSearchProvider(); // Returns true if the default search is managed through group policy. - bool IsDefaultSearchManaged(); + bool is_default_search_managed() const { return is_default_search_managed_; } // Observers used to listen for changes to the model. // TemplateURLModel does NOT delete the observers when deleted. @@ -209,6 +216,7 @@ class TemplateURLModel : public WebDataServiceConsumer, // corresponds to a keyword. // . NOTIFY_GOOGLE_URL_UPDATED: updates mapping for any keywords containing // a google base url replacement term. + // . PREF_CHANGED: checks whether the default search engine has changed. virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details); @@ -223,6 +231,9 @@ class TemplateURLModel : public WebDataServiceConsumer, return search_engine_dialog_chosen_slot_; } + // Registers the preferences used to save a TemplateURL to prefs. + static void RegisterUserPrefs(PrefService* prefs); + protected: // Cover method for the method of the same name on the HistoryService. // url is the one that was visited with the given search terms. @@ -234,6 +245,7 @@ class TemplateURLModel : public WebDataServiceConsumer, private: FRIEND_TEST_ALL_PREFIXES(TemplateURLModelTest, BuildQueryTerms); + FRIEND_TEST_ALL_PREFIXES(TemplateURLModelTest, TestManagedDefaultSearch); FRIEND_TEST_ALL_PREFIXES(TemplateURLModelTest, UpdateKeywordSearchTermsForURL); FRIEND_TEST_ALL_PREFIXES(TemplateURLModelTest, @@ -276,14 +288,14 @@ class TemplateURLModel : public WebDataServiceConsumer, void SaveDefaultSearchProviderToPrefs(const TemplateURL* url); // Creates a TemplateURL that was previously saved to prefs via - // SaveDefaultSearchProviderToPrefs. Returns true if successful, false - // otherwise. This is used if GetDefaultSearchProvider is invoked before the - // TemplateURL has loaded. If the user has opted for no default search, this + // SaveDefaultSearchProviderToPrefs or set via policy. + // Returns true if successful, false otherwise. + // If the user or the policy has opted for no default search, this // returns true but default_provider is set to NULL. - bool LoadDefaultSearchProviderFromPrefs(TemplateURL** default_provider); - - // Registers the preferences used to save a TemplateURL to prefs. - void RegisterPrefs(PrefService* prefs); + // |*is_managed| specifies whether the default is managed via policy. + bool LoadDefaultSearchProviderFromPrefs( + scoped_ptr<TemplateURL>* default_provider, + bool* is_managed); // Returns true if there is no TemplateURL that has a search url with the // specified host, or the only TemplateURLs matching the specified host can @@ -299,8 +311,9 @@ class TemplateURLModel : public WebDataServiceConsumer, // Updates the information in |existing_turl| using the information from // |new_values|, but the ID for |existing_turl| is retained. - void Update(const TemplateURL* existing_turl, - const TemplateURL& new_values); + // Notifying observers is the responsibility of the caller. + void UpdateNoNotify(const TemplateURL* existing_turl, + const TemplateURL& new_values); // Returns the preferences we use. PrefService* GetPrefs(); @@ -327,6 +340,33 @@ class TemplateURLModel : public WebDataServiceConsumer, // {google:baseSuggestURL}. void GoogleBaseURLChanged(); + // Update the default search. Called at initialization or when a managed + // preference has changed. + void UpdateDefaultSearch(); + + // Returns the default search specified in the prepopulated data, if it + // exists. If not, returns first URL in |template_urls_|, or NULL if that's + // empty. + const TemplateURL* FindNewDefaultSearchProvider(); + + // Set the default search provider even if it is managed. |url| may be null. + // Caller is responsible for notifying observers. + void SetDefaultSearchProviderNoNotify(const TemplateURL* url); + + // Adds a new TemplateURL to this model. TemplateURLModel will own the + // reference, and delete it when the TemplateURL is removed. + // Caller is responsible for notifying observers. + void AddNoNotify(TemplateURL* template_url); + + // Removes the keyword from the model. This deletes the supplied TemplateURL. + // This fails if the supplied template_url is the default search provider. + // Caller is responsible for notifying observers. + void RemoveNoNotify(const TemplateURL* template_url); + + // Notify the observers that the model has changed. This is done only if the + // model is loaded. + void NotifyObservers(); + NotificationRegistrar registrar_; // Mapping from keyword to the TemplateURL. @@ -360,16 +400,24 @@ class TemplateURLModel : public WebDataServiceConsumer, // UpdateKeywordSearchTermsForURL is invoked for each element of the vector. std::vector<history::URLVisitedDetails> visits_to_add_; + // Once loaded, the default search provider. This is a pointer to a + // TemplateURL owned by template_urls_. const TemplateURL* default_search_provider_; // Used for UX testing. Gives the slot in the search engine dialog that was // chosen as the default search engine. int search_engine_dialog_chosen_slot_; - // The default search provider from preferences. This is only valid if - // GetDefaultSearchProvider is invoked and we haven't been loaded or loading - // failed. If loading was successful this is not used. - scoped_ptr<TemplateURL> prefs_default_search_provider_; + // The initial search provider extracted from preferences. This is only valid + // if we haven't been loaded or loading failed. + scoped_ptr<TemplateURL> initial_default_search_provider_; + + // Whether the default search is managed via policy. + bool is_default_search_managed_; + + // The set of preferences observer we use to find if the default search + // preferences have changed. + scoped_ptr<PrefSetObserver> default_search_prefs_; // ID assigned to next TemplateURL added to this model. This is an ever // increasing integer that is initialized from the database. diff --git a/chrome/browser/search_engines/template_url_model_unittest.cc b/chrome/browser/search_engines/template_url_model_unittest.cc index c97fb74..60ea680 100644 --- a/chrome/browser/search_engines/template_url_model_unittest.cc +++ b/chrome/browser/search_engines/template_url_model_unittest.cc @@ -18,6 +18,9 @@ #include "chrome/browser/search_engines/template_url_model_test_util.h" #include "chrome/browser/search_engines/template_url_prepopulate_data.h" #include "chrome/browser/webdata/web_database.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_source.h" #include "chrome/common/pref_names.h" #include "chrome/test/testing_pref_service.h" #include "chrome/test/testing_profile.h" @@ -116,14 +119,22 @@ class TemplateURLModelTest : public testing::Test { TemplateURL* AddKeywordWithDate(const std::wstring& keyword, bool autogenerate_keyword, const std::string& url, + const std::string& suggest_url, + const std::string& fav_icon_url, + const std::string& encodings, const std::wstring& short_name, bool safe_for_autoreplace, Time created_date) { TemplateURL* template_url = new TemplateURL(); template_url->SetURL(url, 0, 0); + template_url->SetSuggestionsURL(suggest_url, 0, 0); + template_url->SetFavIconURL(GURL(fav_icon_url)); template_url->set_keyword(keyword); template_url->set_autogenerate_keyword(autogenerate_keyword); template_url->set_short_name(short_name); + std::vector<std::string> encodings_vector; + SplitString(encodings, ';', &encodings_vector); + template_url->set_input_encodings(encodings_vector); template_url->set_date_created(created_date); template_url->set_safe_for_autoreplace(safe_for_autoreplace); model()->Add(template_url); @@ -131,11 +142,24 @@ class TemplateURLModelTest : public testing::Test { return template_url; } + // Simulate firing by the prefs service specifying that the managed + // preferences have changed. + void NotifyManagedPrefsHaveChanged() { + model()->Observe( + NotificationType::PREF_CHANGED, + Source<PrefService>(profile()->GetTestingPrefService()), + Details<std::string>(NULL)); + } + // Verifies the two TemplateURLs are equal. void AssertEquals(const TemplateURL& expected, const TemplateURL& actual) { - ASSERT_EQ(expected.url()->url(), actual.url()->url()); + ASSERT_TRUE(TemplateURLRef::SameUrlRefs(expected.url(), actual.url())); + ASSERT_TRUE(TemplateURLRef::SameUrlRefs(expected.suggestions_url(), + actual.suggestions_url())); ASSERT_EQ(expected.keyword(), actual.keyword()); ASSERT_EQ(expected.short_name(), actual.short_name()); + ASSERT_EQ(JoinString(expected.input_encodings(), ';'), + JoinString(actual.input_encodings(), ';')); ASSERT_TRUE(expected.GetFavIconURL() == actual.GetFavIconURL()); ASSERT_EQ(expected.id(), actual.id()); ASSERT_EQ(expected.safe_for_autoreplace(), actual.safe_for_autoreplace()); @@ -143,6 +167,96 @@ class TemplateURLModelTest : public testing::Test { ASSERT_TRUE(expected.date_created() == actual.date_created()); } + // Checks that the two TemplateURLs are similar. It does not check the id + // and the date_created. Neither pointer should be NULL. + void ExpectSimilar(const TemplateURL* expected, const TemplateURL* actual) { + ASSERT_TRUE(expected != NULL); + ASSERT_TRUE(actual != NULL); + EXPECT_TRUE(TemplateURLRef::SameUrlRefs(expected->url(), actual->url())); + EXPECT_TRUE(TemplateURLRef::SameUrlRefs(expected->suggestions_url(), + actual->suggestions_url())); + EXPECT_EQ(expected->keyword(), actual->keyword()); + EXPECT_EQ(expected->short_name(), actual->short_name()); + EXPECT_EQ(JoinString(expected->input_encodings(), ';'), + JoinString(actual->input_encodings(), ';')); + EXPECT_TRUE(expected->GetFavIconURL() == actual->GetFavIconURL()); + EXPECT_EQ(expected->safe_for_autoreplace(), actual->safe_for_autoreplace()); + EXPECT_EQ(expected->show_in_default_list(), actual->show_in_default_list()); + } + + // Set the managed preferences for the default search provider and trigger + // notification. + void SetManagedDefaultSearchPreferences(const char* name, + const char* search_url, + const char* suggest_url, + const char* icon_url, + const char* encodings, + const char* keyword) { + TestingPrefService* service = profile()->GetTestingPrefService(); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderEnabled, + Value::CreateBooleanValue(true)); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderName, + Value::CreateStringValue(name)); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderSearchURL, + Value::CreateStringValue(search_url)); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderSuggestURL, + Value::CreateStringValue(suggest_url)); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderIconURL, + Value::CreateStringValue(icon_url)); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderEncodings, + Value::CreateStringValue(encodings)); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderKeyword, + Value::CreateStringValue(keyword)); + // Clear the IDs that are not specified via policy. + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderID, new StringValue("")); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderPrepopulateID, new StringValue("")); + NotifyManagedPrefsHaveChanged(); + } + + // Set the managed preferences for the default search provider and trigger + // notification. + void DisableManagedDefaultSearchProvider() { + TestingPrefService* service = profile()->GetTestingPrefService(); + service->SetManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderEnabled, + Value::CreateBooleanValue(false)); + NotifyManagedPrefsHaveChanged(); + } + + // Remove all the managed preferences for the default search provider and + // trigger notification. + void RemoveManagedDefaultSearchPreferences() { + TestingPrefService* service = profile()->GetTestingPrefService(); + service->RemoveManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderEnabled); + service->RemoveManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderName); + service->RemoveManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderSearchURL); + service->RemoveManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderSuggestURL); + service->RemoveManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderIconURL); + service->RemoveManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderEncodings); + service->RemoveManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderKeyword); + service->RemoveManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderID); + service->RemoveManagedPrefWithoutNotification( + prefs::kDefaultSearchProviderPrepopulateID); + NotifyManagedPrefsHaveChanged(); + } + // Creates a TemplateURL with the same prepopluated id as a real prepopulated // item. The input number determines which prepopulated item. The caller is // responsible for owning the returned TemplateURL*. @@ -392,16 +506,21 @@ TEST_F(TemplateURLModelTest, ClearBrowsingData_Keywords) { EXPECT_EQ(0U, model()->GetTemplateURLs().size()); // Create one with a 0 time. - AddKeywordWithDate(L"key1", false, "http://foo1", L"name1", true, Time()); + AddKeywordWithDate(L"key1", false, "http://foo1", "http://suggest1", + "http://icon1", "UTF-8;UTF-16", L"name1", true, Time()); // Create one for now and +/- 1 day. - AddKeywordWithDate(L"key2", false, "http://foo2", L"name2", true, + AddKeywordWithDate(L"key2", false, "http://foo2", "http://suggest2", + "http://icon2", "UTF-8;UTF-16", L"name2", true, now - one_day); - AddKeywordWithDate(L"key3", false, "http://foo3", L"name3", true, now); - AddKeywordWithDate(L"key4", false, "http://foo4", L"name4", true, - now + one_day); + AddKeywordWithDate(L"key3", false, "http://foo3", "", "", "", L"name3", + true, now); + AddKeywordWithDate(L"key4", false, "http://foo4", "", "", "", L"name4", + true, now + one_day); // Try the other three states. - AddKeywordWithDate(L"key5", false, "http://foo5", L"name5", false, now); - AddKeywordWithDate(L"key6", false, "http://foo6", L"name6", false, + AddKeywordWithDate(L"key5", false, "http://foo5", "http://suggest5", + "http://icon5", "UTF-8;UTF-16", L"name5", false, now); + AddKeywordWithDate(L"key6", false, "http://foo6", "http://suggest6", + "http://icon6", "UTF-8;UTF-16", L"name6", false, month_ago); // We just added a few items, validate them. @@ -483,7 +602,7 @@ TEST_F(TemplateURLModelTest, DefaultSearchProvider) { VerifyLoad(); const size_t initial_count = model()->GetTemplateURLs().size(); TemplateURL* t_url = AddKeywordWithDate(L"key1", false, "http://foo1", - L"name1", true, Time()); + "http://sugg1", "http://icon1", "UTF-8;UTF-16", L"name1", true, Time()); test_util_.ResetObserverCount(); model()->SetDefaultSearchProvider(t_url); @@ -514,8 +633,8 @@ TEST_F(TemplateURLModelTest, TemplateURLWithNoKeyword) { const size_t initial_count = model()->GetTemplateURLs().size(); - AddKeywordWithDate(std::wstring(), false, "http://foo1", L"name1", true, - Time()); + AddKeywordWithDate(std::wstring(), false, "http://foo1", "http://sugg1", + "http://icon1", "UTF-8;UTF-16", L"name1", true, Time()); // We just added a few items, validate them. ASSERT_EQ(initial_count + 1, model()->GetTemplateURLs().size()); @@ -539,7 +658,7 @@ TEST_F(TemplateURLModelTest, CantReplaceWithSameKeyword) { ChangeModelToLoadState(); ASSERT_TRUE(model()->CanReplaceKeyword(L"foo", GURL(), NULL)); TemplateURL* t_url = AddKeywordWithDate(L"foo", false, "http://foo1", - L"name1", true, Time()); + "http://sugg1", "http://icon1", "UTF-8;UTF-16", L"name1", true, Time()); // Can still replace, newly added template url is marked safe to replace. ASSERT_TRUE(model()->CanReplaceKeyword(L"foo", GURL("http://foo2"), NULL)); @@ -556,7 +675,7 @@ TEST_F(TemplateURLModelTest, CantReplaceWithSameHosts) { ChangeModelToLoadState(); ASSERT_TRUE(model()->CanReplaceKeyword(L"foo", GURL("http://foo.com"), NULL)); TemplateURL* t_url = AddKeywordWithDate(L"foo", false, "http://foo.com", - L"name1", true, Time()); + "http://sugg1", "http://icon1", "UTF-8;UTF-16", L"name1", true, Time()); // Can still replace, newly added template url is marked safe to replace. ASSERT_TRUE(model()->CanReplaceKeyword(L"bar", GURL("http://foo.com"), NULL)); @@ -686,8 +805,8 @@ TEST_F(TemplateURLModelTest, UpdateKeywordSearchTermsForURL) { }; ChangeModelToLoadState(); - AddKeywordWithDate(L"x", false, "http://x/foo?q={searchTerms}", L"name", - false, Time()); + AddKeywordWithDate(L"x", false, "http://x/foo?q={searchTerms}", + "http://sugg1", "http://icon1", "UTF-8;UTF-16", L"name", false, Time()); for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) { history::URLVisitedDetails details; @@ -708,7 +827,8 @@ TEST_F(TemplateURLModelTest, DontUpdateKeywordSearchForNonReplaceable) { }; ChangeModelToLoadState(); - AddKeywordWithDate(L"x", false, "http://x/foo", L"name", false, Time()); + AddKeywordWithDate(L"x", false, "http://x/foo", "http://sugg1", + "http://icon1", "UTF-8;UTF-16", L"name", false, Time()); for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) { history::URLVisitedDetails details; @@ -726,7 +846,8 @@ TEST_F(TemplateURLModelTest, ChangeGoogleBaseValue) { ChangeModelToLoadState(); SetGoogleBaseURL("http://google.com/"); const TemplateURL* t_url = AddKeywordWithDate(std::wstring(), true, - "{google:baseURL}?q={searchTerms}", L"name", false, Time()); + "{google:baseURL}?q={searchTerms}", "http://sugg1", "http://icon1", + "UTF-8;UTF-16", L"name", false, Time()); ASSERT_EQ(t_url, model()->GetTemplateURLForHost("google.com")); EXPECT_EQ("google.com", t_url->url()->GetHost()); EXPECT_EQ(L"google.com", t_url->keyword()); @@ -772,7 +893,8 @@ TEST_F(TemplateURLModelTest, GenerateVisitOnKeyword) { // Create a keyword. TemplateURL* t_url = AddKeywordWithDate( L"keyword", false, "http://foo.com/foo?query={searchTerms}", - L"keyword", true, base::Time::Now()); + "http://sugg1", "http://icon1", "UTF-8;UTF-16", L"keyword", true, + base::Time::Now()); // Add a visit that matches the url of the keyword. HistoryService* history = @@ -973,13 +1095,88 @@ TEST_F(TemplateURLModelTest, FailedInit) { } // Verifies that if the default search URL preference is managed, we report -// the default search as managed. -TEST_F(TemplateURLModelTest, ReportDefaultSearchIsManaged) { - TestingPrefService* service = profile()->GetTestingPrefService(); - service->RegisterStringPref(prefs::kDefaultSearchProviderSearchURL, - std::string()); - service->SetManagedPref(prefs::kDefaultSearchProviderSearchURL, - Value::CreateStringValue("http://test.com/{searchTerms}")); - ASSERT_TRUE(model()->IsDefaultSearchManaged()); -} +// the default search as managed. Also check that we are getting the right +// values. +TEST_F(TemplateURLModelTest, TestManagedDefaultSearch) { + VerifyLoad(); + const size_t initial_count = model()->GetTemplateURLs().size(); + test_util_.ResetObserverCount(); + // Set a regular default search provider. + TemplateURL* regular_default = AddKeywordWithDate(L"key1", false, + "http://foo1", "http://sugg1", "http://icon1", "UTF-8;UTF-16", L"name1", + true, Time()); + VerifyObserverCount(1); + model()->SetDefaultSearchProvider(regular_default); + // Adding the URL and setting the default search provider should have caused + // notifications. + VerifyObserverCount(1); + EXPECT_FALSE(model()->is_default_search_managed()); + EXPECT_EQ(1 + initial_count, model()->GetTemplateURLs().size()); + + // Set a managed preference that establishes a default search provider. + const char kName[] = "test1"; + const char kSearchURL[] = "http://test.com/search?t={searchTerms}"; + const char kIconURL[] = "http://test.com/icon.jpg"; + const char kEncodings[] = "UTF-16;UTF-32"; + SetManagedDefaultSearchPreferences(kName, kSearchURL, "", kIconURL, + kEncodings, ""); + VerifyObserverCount(1); + EXPECT_TRUE(model()->is_default_search_managed()); + EXPECT_EQ(2 + initial_count, model()->GetTemplateURLs().size()); + + // Verify that the default manager we are getting is the managed one. + scoped_ptr<TemplateURL> expected_managed_default(new TemplateURL()); + expected_managed_default->SetURL(kSearchURL, 0, 0); + expected_managed_default->SetFavIconURL(GURL(kIconURL)); + expected_managed_default->set_short_name(L"test1"); + std::vector<std::string> encodings_vector; + SplitString(kEncodings, ';', &encodings_vector); + expected_managed_default->set_input_encodings(encodings_vector); + expected_managed_default->set_show_in_default_list(true); + const TemplateURL* actual_managed_default = + model()->GetDefaultSearchProvider(); + ExpectSimilar(actual_managed_default, expected_managed_default.get()); + EXPECT_EQ(actual_managed_default->show_in_default_list(), true); + + // Update the managed preference and check that the model has changed. + const char kNewName[] = "test2"; + const char kNewSearchURL[] = "http://other.com/search?t={searchTerms}"; + const char kNewSuggestURL[] = "http://other.com/suggest?t={searchTerms}"; + SetManagedDefaultSearchPreferences(kNewName, kNewSearchURL, kNewSuggestURL, + "", "", ""); + VerifyObserverCount(1); + EXPECT_TRUE(model()->is_default_search_managed()); + EXPECT_EQ(2 + initial_count, model()->GetTemplateURLs().size()); + + // Verify that the default manager we are now getting is the correct one. + scoped_ptr<TemplateURL> expected_new_managed_default(new TemplateURL()); + expected_new_managed_default->SetURL(kNewSearchURL, 0, 0); + expected_new_managed_default->SetSuggestionsURL(kNewSuggestURL, 0, 0); + expected_new_managed_default->set_short_name(L"test2"); + expected_new_managed_default->set_show_in_default_list(true); + const TemplateURL* actual_new_managed_default = + model()->GetDefaultSearchProvider(); + ExpectSimilar(actual_new_managed_default, expected_new_managed_default.get()); + EXPECT_EQ(actual_new_managed_default->show_in_default_list(), true); + + // Remove all the managed prefs and check that we are no longer managed. + RemoveManagedDefaultSearchPreferences(); + VerifyObserverCount(1); + EXPECT_FALSE(model()->is_default_search_managed()); + EXPECT_EQ(1 + initial_count, model()->GetTemplateURLs().size()); + + // The default should now be the first URL added + const TemplateURL* actual_final_managed_default = + model()->GetDefaultSearchProvider(); + ExpectSimilar(actual_final_managed_default, + model()->GetTemplateURLs()[0]); + EXPECT_EQ(actual_final_managed_default->show_in_default_list(), true); + + // Disable the default search provider through policy. + DisableManagedDefaultSearchProvider(); + VerifyObserverCount(1); + EXPECT_TRUE(model()->is_default_search_managed()); + EXPECT_TRUE(NULL == model()->GetDefaultSearchProvider()); + EXPECT_EQ(1 + initial_count, model()->GetTemplateURLs().size()); +} diff --git a/chrome/browser/search_engines/template_url_prepopulate_data.cc b/chrome/browser/search_engines/template_url_prepopulate_data.cc index 7b745bd..1206b78 100644 --- a/chrome/browser/search_engines/template_url_prepopulate_data.cc +++ b/chrome/browser/search_engines/template_url_prepopulate_data.cc @@ -9,8 +9,10 @@ #endif #include "base/command_line.h" +#include "base/scoped_vector.h" #include "base/string16.h" #include "base/string_util.h" +#include "base/stl_util-inl.h" #include "base/utf_string_conversions.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/search_engines/template_url.h" @@ -3268,4 +3270,18 @@ void GetPrepopulatedEngines(PrefService* prefs, } } +TemplateURL* GetPrepopulatedDefaultSearch(PrefService* prefs) { + TemplateURL* default_search_provider = NULL; + ScopedVector<TemplateURL> loaded_urls; + size_t default_search_index; + // This could be more efficient. We are loading all the URLs to only keep + // the first one. + GetPrepopulatedEngines(prefs, &loaded_urls.get(), &default_search_index); + if (default_search_index < loaded_urls.size()) { + default_search_provider = loaded_urls[default_search_index]; + loaded_urls.weak_erase(loaded_urls.begin() + default_search_index); + } + return default_search_provider; +} + } // namespace TemplateURLPrepopulateData diff --git a/chrome/browser/search_engines/template_url_prepopulate_data.h b/chrome/browser/search_engines/template_url_prepopulate_data.h index 80fa584..75a0138 100644 --- a/chrome/browser/search_engines/template_url_prepopulate_data.h +++ b/chrome/browser/search_engines/template_url_prepopulate_data.h @@ -68,6 +68,10 @@ void GetPrepopulatedEngines(PrefService* prefs, std::vector<TemplateURL*>* t_urls, size_t* default_search_provider_index); +// Returns the default search provider specified by the prepopulate data. +// The caller owns the returned value, which may be NULL. +TemplateURL* GetPrepopulatedDefaultSearch(PrefService* prefs); + } // namespace TemplateURLPrepopulateData #endif // CHROME_BROWSER_SEARCH_ENGINES_TEMPLATE_URL_PREPOPULATE_DATA_H_ diff --git a/chrome/browser/search_engines/util.cc b/chrome/browser/search_engines/util.cc index 8273800..958f740 100644 --- a/chrome/browser/search_engines/util.cc +++ b/chrome/browser/search_engines/util.cc @@ -84,49 +84,51 @@ void MergeEnginesFromPrepopulateData( id_to_turl[prepopulate_id] = *i; } - std::vector<TemplateURL*> loaded_urls; + std::vector<TemplateURL*> prepopulated_urls; size_t default_search_index; TemplateURLPrepopulateData::GetPrepopulatedEngines(prefs, - &loaded_urls, + &prepopulated_urls, &default_search_index); std::set<int> updated_ids; - for (size_t i = 0; i < loaded_urls.size(); ++i) { - // We take ownership of |t_url|. - scoped_ptr<TemplateURL> t_url(loaded_urls[i]); - const int t_url_id = t_url->prepopulate_id(); - if (!t_url_id || updated_ids.count(t_url_id)) { + for (size_t i = 0; i < prepopulated_urls.size(); ++i) { + // We take ownership of |prepopulated_urls[i]|. + scoped_ptr<TemplateURL> prepopulated_url(prepopulated_urls[i]); + const int prepopulated_id = prepopulated_url->prepopulate_id(); + if (!prepopulated_id || updated_ids.count(prepopulated_id)) { // Prepopulate engines need a unique id. NOTREACHED(); continue; } - TemplateURL* current_t_url = t_url.get(); - IDMap::iterator existing_url_iter(id_to_turl.find(t_url_id)); + TemplateURL* existing_url = NULL; + IDMap::iterator existing_url_iter(id_to_turl.find(prepopulated_id)); if (existing_url_iter != id_to_turl.end()) { - current_t_url = existing_url_iter->second; - if (!current_t_url->safe_for_autoreplace()) { + existing_url = existing_url_iter->second; + if (!existing_url->safe_for_autoreplace()) { // User edited the entry, preserve the keyword and description. - t_url->set_safe_for_autoreplace(false); - t_url->set_keyword(current_t_url->keyword()); - t_url->set_autogenerate_keyword( - current_t_url->autogenerate_keyword()); - t_url->set_short_name(current_t_url->short_name()); + prepopulated_url->set_safe_for_autoreplace(false); + prepopulated_url->set_keyword(existing_url->keyword()); + prepopulated_url->set_autogenerate_keyword( + existing_url->autogenerate_keyword()); + prepopulated_url->set_short_name(existing_url->short_name()); } - t_url->set_id(current_t_url->id()); + prepopulated_url->set_id(existing_url->id()); - *current_t_url = *t_url; + *existing_url = *prepopulated_url; if (service) { - service->UpdateKeyword(*current_t_url); + service->UpdateKeyword(*existing_url); } id_to_turl.erase(existing_url_iter); } else { - template_urls->push_back(t_url.release()); + existing_url = prepopulated_url.get(); + template_urls->push_back(prepopulated_url.release()); } + DCHECK(existing_url); if (i == default_search_index && !*default_search_provider) - *default_search_provider = current_t_url; + *default_search_provider = existing_url; - updated_ids.insert(t_url_id); + updated_ids.insert(prepopulated_id); } // Remove any prepopulated engines which are no longer in the master list, as @@ -194,3 +196,4 @@ void GetSearchProvidersUsingKeywordResult( *new_resource_keyword_version = resource_keyword_version; } } + |