Profile Reset: support "soft reset" of search engines, homepage and startup pages. The soft reset doesn't clear user-defined search engines, homepage URL and startup URLs.

BUG=235037

Review URL: https://codereview.chromium.org/23496004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@226880 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/profile_resetter/profile_resetter.cc b/chrome/browser/profile_resetter/profile_resetter.cc
index 3c797a7..0777632f 100644
--- a/chrome/browser/profile_resetter/profile_resetter.cc
+++ b/chrome/browser/profile_resetter/profile_resetter.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/google/google_url_tracker.h"
 #include "chrome/browser/profile_resetter/brandcoded_default_settings.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/search_terms_data.h"
 #include "chrome/browser/search_engines/template_url_prepopulate_data.h"
 #include "chrome/browser/search_engines/template_url_service.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -47,14 +48,19 @@
   DCHECK(CalledOnValidThread());
   DCHECK(master_settings);
 
-  master_settings_.swap(master_settings);
-
   // We should never be called with unknown flags.
   CHECK_EQ(static_cast<ResettableFlags>(0), resettable_flags & ~ALL);
 
   // We should never be called when a previous reset has not finished.
   CHECK_EQ(static_cast<ResettableFlags>(0), pending_reset_flags_);
 
+  if (!resettable_flags) {
+    content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
+                                     callback);
+    return;
+  }
+
+  master_settings_.swap(master_settings);
   callback_ = callback;
 
   // These flags are set to false by the individual reset functions.
@@ -63,7 +69,7 @@
   struct {
     Resettable flag;
     void (ProfileResetter::*method)();
-  } flag2Method [] = {
+  } flagToMethod [] = {
       { DEFAULT_SEARCH_ENGINE, &ProfileResetter::ResetDefaultSearchEngine },
       { HOMEPAGE, &ProfileResetter::ResetHomepage },
       { CONTENT_SETTINGS, &ProfileResetter::ResetContentSettings },
@@ -74,10 +80,10 @@
   };
 
   ResettableFlags reset_triggered_for_flags = 0;
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(flag2Method); ++i) {
-    if (resettable_flags & flag2Method[i].flag) {
-      reset_triggered_for_flags |= flag2Method[i].flag;
-      (this->*flag2Method[i].method)();
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(flagToMethod); ++i) {
+    if (resettable_flags & flagToMethod[i].flag) {
+      reset_triggered_for_flags |= flagToMethod[i].flag;
+      (this->*flagToMethod[i].method)();
     }
   }
 
@@ -124,7 +130,7 @@
       update->Swap(search_engines.get());
     }
 
-    template_url_service_->ResetURLs();
+    template_url_service_->RepairPrepopulatedSearchEngines();
 
     // Reset Google search URL.
     prefs->ClearPref(prefs::kLastPromptedGoogleURL);
@@ -149,8 +155,6 @@
 
   if (master_settings_->GetHomepage(&homepage))
     prefs->SetString(prefs::kHomePage, homepage);
-  else
-    prefs->ClearPref(prefs::kHomePage);
 
   if (master_settings_->GetHomepageIsNewTab(&homepage_is_ntp))
     prefs->SetBoolean(prefs::kHomePageIsNewTabPage, homepage_is_ntp);
@@ -217,8 +221,6 @@
   scoped_ptr<ListValue> url_list(master_settings_->GetUrlsToRestoreOnStartup());
   if (url_list)
     ListPrefUpdate(prefs, prefs::kURLsToRestoreOnStartup)->Swap(url_list.get());
-  else
-    prefs->ClearPref(prefs::kURLsToRestoreOnStartup);
 
   int restore_on_startup;
   if (master_settings_->GetRestoreOnStartup(&restore_on_startup))
diff --git a/chrome/browser/profile_resetter/profile_resetter_unittest.cc b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
index 3bac3e8..a1aaf6b 100644
--- a/chrome/browser/profile_resetter/profile_resetter_unittest.cc
+++ b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
@@ -276,32 +276,9 @@
 
 /********************* Tests *********************/
 
-TEST_F(ProfileResetterTest, ResetDefaultSearchEngine) {
-  // Search engine's logic is tested by
-  // TemplateURLServiceTest.ResetURLs.
-  PrefService* prefs = profile()->GetPrefs();
-  DCHECK(prefs);
-  prefs->SetString(prefs::kLastPromptedGoogleURL, "http://www.foo.com/");
-
-  scoped_refptr<Extension> extension = CreateExtension(
-      ASCIIToUTF16("xxx"),
-      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
-      Manifest::COMPONENT,
-      extensions::Manifest::TYPE_EXTENSION,
-      false);
-  service_->AddExtension(extension.get());
-
-  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE);
-
-  // TemplateURLService should reset extension search engines.
-  TemplateURLService* model =
-      TemplateURLServiceFactory::GetForProfile(profile());
-  TemplateURL* ext_url = model->GetTemplateURLForKeyword(ASCIIToUTF16("xxx"));
-  ASSERT_TRUE(ext_url);
-  EXPECT_TRUE(ext_url->IsExtensionKeyword());
-  EXPECT_EQ(ASCIIToUTF16("xxx"), ext_url->keyword());
-  EXPECT_EQ(ASCIIToUTF16("xxx"), ext_url->short_name());
-  EXPECT_EQ("", prefs->GetString(prefs::kLastPromptedGoogleURL));
+TEST_F(ProfileResetterTest, ResetNothing) {
+  // The callback should be called even if there is nothing to reset.
+  ResetAndWait(0);
 }
 
 TEST_F(ProfileResetterTest, ResetDefaultSearchEngineNonOrganic) {
@@ -313,7 +290,6 @@
 
   TemplateURLService* model =
       TemplateURLServiceFactory::GetForProfile(profile());
-  EXPECT_EQ(1u, model->GetTemplateURLs().size());
   TemplateURL* default_engine = model->GetDefaultSearchProvider();
   ASSERT_NE(static_cast<TemplateURL*>(NULL), default_engine);
   EXPECT_EQ(ASCIIToUTF16("first"), default_engine->short_name());
@@ -323,18 +299,25 @@
   EXPECT_EQ("", prefs->GetString(prefs::kLastPromptedGoogleURL));
 }
 
-TEST_F(ProfileResetterTest, ResetHomepage) {
+TEST_F(ProfileResetterTest, ResetDefaultSearchEnginePartially) {
+  // Search engine's logic is tested by
+  // TemplateURLServiceTest.RepairPrepopulatedSearchEngines.
   PrefService* prefs = profile()->GetPrefs();
   DCHECK(prefs);
-  prefs->SetBoolean(prefs::kHomePageIsNewTabPage, false);
-  prefs->SetString(prefs::kHomePage, "http://google.com");
-  prefs->SetBoolean(prefs::kShowHomeButton, true);
+  prefs->SetString(prefs::kLastPromptedGoogleURL, "http://www.foo.com/");
 
-  ResetAndWait(ProfileResetter::HOMEPAGE);
+  // Make sure TemplateURLService has loaded.
+  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE);
 
-  EXPECT_TRUE(prefs->GetBoolean(prefs::kHomePageIsNewTabPage));
-  EXPECT_EQ(std::string(), prefs->GetString(prefs::kHomePage));
-  EXPECT_FALSE(prefs->GetBoolean(prefs::kShowHomeButton));
+  TemplateURLService* model =
+      TemplateURLServiceFactory::GetForProfile(profile());
+  TemplateURLService::TemplateURLVector urls = model->GetTemplateURLs();
+
+  // The second call should produce no effect.
+  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE);
+
+  EXPECT_EQ(urls, model->GetTemplateURLs());
+  EXPECT_EQ(std::string(), prefs->GetString(prefs::kLastPromptedGoogleURL));
 }
 
 TEST_F(ProfileResetterTest, ResetHomepageNonOrganic) {
@@ -351,6 +334,20 @@
   EXPECT_TRUE(prefs->GetBoolean(prefs::kShowHomeButton));
 }
 
+TEST_F(ProfileResetterTest, ResetHomepagePartially) {
+  PrefService* prefs = profile()->GetPrefs();
+  DCHECK(prefs);
+  prefs->SetBoolean(prefs::kHomePageIsNewTabPage, false);
+  prefs->SetString(prefs::kHomePage, "http://www.foo.com");
+  prefs->SetBoolean(prefs::kShowHomeButton, true);
+
+  ResetAndWait(ProfileResetter::HOMEPAGE);
+
+  EXPECT_TRUE(prefs->GetBoolean(prefs::kHomePageIsNewTabPage));
+  EXPECT_EQ("http://www.foo.com", prefs->GetString(prefs::kHomePage));
+  EXPECT_FALSE(prefs->GetBoolean(prefs::kShowHomeButton));
+}
+
 TEST_F(ProfileResetterTest, ResetContentSettings) {
   HostContentSettingsMap* host_content_settings_map =
       profile()->GetHostContentSettingsMap();
@@ -572,22 +569,6 @@
   EXPECT_TRUE(theme_service->UsingDefaultTheme());
 }
 
-TEST_F(ProfileResetterTest, ResetStartPage) {
-  PrefService* prefs = profile()->GetPrefs();
-  DCHECK(prefs);
-
-  SessionStartupPref startup_pref(SessionStartupPref::URLS);
-  startup_pref.urls.push_back(GURL("http://foo"));
-  startup_pref.urls.push_back(GURL("http://bar"));
-  SessionStartupPref::SetStartupPref(prefs, startup_pref);
-
-  ResetAndWait(ProfileResetter::STARTUP_PAGES);
-
-  startup_pref = SessionStartupPref::GetStartupPref(prefs);
-  EXPECT_EQ(SessionStartupPref::GetDefaultStartupType(), startup_pref.type);
-  EXPECT_EQ(std::vector<GURL>(), startup_pref.urls);
-}
-
 TEST_F(ProfileResetterTest, ResetStartPageNonOrganic) {
   PrefService* prefs = profile()->GetPrefs();
   DCHECK(prefs);
@@ -603,6 +584,23 @@
   EXPECT_EQ(std::vector<GURL>(urls, urls + arraysize(urls)), startup_pref.urls);
 }
 
+
+TEST_F(ProfileResetterTest, ResetStartPagePartially) {
+  PrefService* prefs = profile()->GetPrefs();
+  DCHECK(prefs);
+
+  const GURL urls[] = {GURL("http://foo"), GURL("http://bar")};
+  SessionStartupPref startup_pref(SessionStartupPref::URLS);
+  startup_pref.urls.assign(urls, urls + arraysize(urls));
+  SessionStartupPref::SetStartupPref(prefs, startup_pref);
+
+  ResetAndWait(ProfileResetter::STARTUP_PAGES, std::string());
+
+  startup_pref = SessionStartupPref::GetStartupPref(prefs);
+  EXPECT_EQ(SessionStartupPref::GetDefaultStartupType(), startup_pref.type);
+  EXPECT_EQ(std::vector<GURL>(urls, urls + arraysize(urls)), startup_pref.urls);
+}
+
 TEST_F(PinnedTabsResetTest, ResetPinnedTabs) {
   scoped_refptr<Extension> extension_app = CreateExtension(
       ASCIIToUTF16("hello!"),
@@ -715,7 +713,8 @@
   // Reset to non organic defaults.
   ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
                ProfileResetter::HOMEPAGE |
-               ProfileResetter::STARTUP_PAGES, master_prefs);
+               ProfileResetter::STARTUP_PAGES,
+               master_prefs);
 
   ResettableSettingsSnapshot nonorganic_snap(profile());
   EXPECT_EQ(ResettableSettingsSnapshot::ALL_FIELDS,
@@ -756,7 +755,8 @@
   // Reset to non organic defaults.
   ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
                ProfileResetter::HOMEPAGE |
-               ProfileResetter::STARTUP_PAGES, kDistributionConfig);
+               ProfileResetter::STARTUP_PAGES,
+               kDistributionConfig);
 
   scoped_refptr<Extension> ext = CreateExtension(
       ASCIIToUTF16("example"),
@@ -769,7 +769,7 @@
 
   const ResettableSettingsSnapshot nonorganic_snap(profile());
 
-  COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 63,
+  COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 15,
                  expand_this_test);
   for (int field_mask = 0; field_mask <= ResettableSettingsSnapshot::ALL_FIELDS;
        ++field_mask) {
@@ -792,13 +792,13 @@
     ListValue* extensions;
     int initiator = 0;
 
-    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::STARTUP_URLS),
+    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::STARTUP_MODE),
               dict->GetList("startup_urls", &startup_urls));
-    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::STARTUP_TYPE),
+    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::STARTUP_MODE),
               dict->GetInteger("startup_type", &startup_type));
     EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::HOMEPAGE),
               dict->GetString("homepage", &homepage));
-    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::HOMEPAGE_IS_NTP),
+    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::HOMEPAGE),
               dict->GetBoolean("homepage_is_ntp", &homepage_is_ntp));
     EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::DSE_URL),
               dict->GetString("default_search_engine", &default_search_engine));
diff --git a/chrome/browser/profile_resetter/resettable_settings_snapshot.cc b/chrome/browser/profile_resetter/resettable_settings_snapshot.cc
index 095efcf..a5b8101 100644
--- a/chrome/browser/profile_resetter/resettable_settings_snapshot.cc
+++ b/chrome/browser/profile_resetter/resettable_settings_snapshot.cc
@@ -79,10 +79,6 @@
 
 void ResettableSettingsSnapshot::Subtract(
     const ResettableSettingsSnapshot& snapshot) {
-  std::vector<GURL> urls = base::STLSetDifference<std::vector<GURL> >(
-      startup_.urls, snapshot.startup_.urls);
-  startup_.urls.swap(urls);
-
   ExtensionList extensions = base::STLSetDifference<ExtensionList>(
       enabled_extensions_, snapshot.enabled_extensions_);
   enabled_extensions_.swap(extensions);
@@ -92,26 +88,21 @@
     const ResettableSettingsSnapshot& snapshot) const {
   int bit_mask = 0;
 
-  if (startup_.urls != snapshot.startup_.urls) {
-    bit_mask |= STARTUP_URLS;
-  }
+  if (startup_.type != snapshot.startup_.type ||
+      startup_.urls != snapshot.startup_.urls)
+    bit_mask |= STARTUP_MODE;
 
-  if (startup_.type != snapshot.startup_.type)
-    bit_mask |= STARTUP_TYPE;
-
-  if (homepage_ != snapshot.homepage_)
+  if (homepage_is_ntp_ != snapshot.homepage_is_ntp_ ||
+      homepage_ != snapshot.homepage_)
     bit_mask |= HOMEPAGE;
 
-  if (homepage_is_ntp_ != snapshot.homepage_is_ntp_)
-    bit_mask |= HOMEPAGE_IS_NTP;
-
   if (dse_url_ != snapshot.dse_url_)
     bit_mask |= DSE_URL;
 
   if (enabled_extensions_ != snapshot.enabled_extensions_)
     bit_mask |= EXTENSIONS;
 
-  COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 63,
+  COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 15,
                  add_new_field_here);
 
   return bit_mask;
@@ -122,23 +113,20 @@
                                     SnapshotCaller source) {
   DictionaryValue dict;
 
-  if (field_mask & ResettableSettingsSnapshot::STARTUP_URLS) {
+  if (field_mask & ResettableSettingsSnapshot::STARTUP_MODE) {
     ListValue* list = new ListValue;
     const std::vector<GURL>& urls = snapshot.startup_urls();
     for (std::vector<GURL>::const_iterator i = urls.begin();
          i != urls.end(); ++i)
       list->AppendString(i->spec());
     dict.Set(kStartupURLPath, list);
+    dict.SetInteger(kStartupTypePath, snapshot.startup_type());
   }
 
-  if (field_mask & ResettableSettingsSnapshot::STARTUP_TYPE)
-    dict.SetInteger(kStartupTypePath, snapshot.startup_type());
-
-  if (field_mask & ResettableSettingsSnapshot::HOMEPAGE)
+  if (field_mask & ResettableSettingsSnapshot::HOMEPAGE) {
     dict.SetString(kHomepagePath, snapshot.homepage());
-
-  if (field_mask & ResettableSettingsSnapshot::HOMEPAGE_IS_NTP)
     dict.SetBoolean(kHomepageIsNewTabPage, snapshot.homepage_is_ntp());
+  }
 
   if (field_mask & ResettableSettingsSnapshot::DSE_URL)
     dict.SetString(kDefaultSearchEnginePath, snapshot.dse_url());
@@ -157,7 +145,7 @@
     dict.Set(kEnabledExtensions, list);
   }
 
-  COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 63,
+  COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 15,
                  serialize_new_field_here);
 
   dict.SetInteger(kFeedbackCaller, source);
diff --git a/chrome/browser/profile_resetter/resettable_settings_snapshot.h b/chrome/browser/profile_resetter/resettable_settings_snapshot.h
index 5abac6f8..d3062a37 100644
--- a/chrome/browser/profile_resetter/resettable_settings_snapshot.h
+++ b/chrome/browser/profile_resetter/resettable_settings_snapshot.h
@@ -21,15 +21,12 @@
   typedef std::vector<std::pair<std::string, std::string> > ExtensionList;
   // All types of settings handled by this class.
   enum Field {
-    STARTUP_URLS = 1 << 0,
-    STARTUP_TYPE = 1 << 1,
-    HOMEPAGE = 1 << 2,
-    HOMEPAGE_IS_NTP = 1 << 3,
-    DSE_URL = 1 << 4,
-    EXTENSIONS = 1 << 5,
+    STARTUP_MODE = 1 << 0,
+    HOMEPAGE = 1 << 1,
+    DSE_URL = 1 << 2,
+    EXTENSIONS = 1 << 3,
 
-    ALL_FIELDS = STARTUP_URLS | STARTUP_TYPE | HOMEPAGE |
-                 HOMEPAGE_IS_NTP | DSE_URL | EXTENSIONS,
+    ALL_FIELDS = STARTUP_MODE | HOMEPAGE | DSE_URL | EXTENSIONS,
   };
 
   explicit ResettableSettingsSnapshot(Profile* profile);
@@ -50,7 +47,6 @@
     return enabled_extensions_;
   }
 
-  // Substitutes |startup_.urls| with |startup_.urls|\|snapshot.startup_.urls|.
   // Substitutes |enabled_extensions_| with
   // |enabled_extensions_|\|snapshot.enabled_extensions_|.
   void Subtract(const ResettableSettingsSnapshot& snapshot);
diff --git a/chrome/browser/search_engines/template_url_prepopulate_data.cc b/chrome/browser/search_engines/template_url_prepopulate_data.cc
index 652ecf58..ba234ffd 100644
--- a/chrome/browser/search_engines/template_url_prepopulate_data.cc
+++ b/chrome/browser/search_engines/template_url_prepopulate_data.cc
@@ -10,7 +10,6 @@
 
 #include "base/command_line.h"
 #include "base/logging.h"
-#include "base/memory/scoped_vector.h"
 #include "base/prefs/pref_service.h"
 #include "base/stl_util.h"
 #include "base/strings/string16.h"
@@ -1118,15 +1117,15 @@
   return new TemplateURL(profile, data);
 }
 
-void GetPrepopulatedTemplateFromPrefs(Profile* profile,
-                                      std::vector<TemplateURL*>* t_urls) {
+ScopedVector<TemplateURL> GetPrepopulatedTemplateFromPrefs(Profile* profile) {
+  ScopedVector<TemplateURL> t_urls;
   if (!profile)
-    return;
+    return t_urls.Pass();
 
   const ListValue* list =
       profile->GetPrefs()->GetList(prefs::kSearchProviderOverrides);
   if (!list)
-    return;
+    return t_urls.Pass();
 
   size_t num_engines = list->GetSize();
   for (size_t i = 0; i != num_engines; ++i) {
@@ -1169,7 +1168,7 @@
       engine->GetList("alternate_urls", &alternate_urls);
       engine->GetString("search_terms_replacement_key",
           &search_terms_replacement_key);
-      t_urls->push_back(MakePrepopulatedTemplateURL(profile, name, keyword,
+      t_urls.push_back(MakePrepopulatedTemplateURL(profile, name, keyword,
           search_url, suggest_url, instant_url, image_url, new_tab_url,
           search_url_post_params, suggest_url_post_params,
           instant_url_post_params, image_url_post_params,
@@ -1177,6 +1176,7 @@
           id));
     }
   }
+  return t_urls.Pass();
 }
 
 // The caller owns the returned TemplateURL.
@@ -1248,24 +1248,24 @@
       kCurrentDataVersion;
 }
 
-void GetPrepopulatedEngines(Profile* profile,
-                            std::vector<TemplateURL*>* t_urls,
-                            size_t* default_search_provider_index) {
+ScopedVector<TemplateURL> GetPrepopulatedEngines(
+    Profile* profile, size_t* default_search_provider_index) {
   // If there is a set of search engines in the preferences file, it overrides
   // the built-in set.
   *default_search_provider_index = 0;
-  GetPrepopulatedTemplateFromPrefs(profile, t_urls);
-  if (!t_urls->empty())
-    return;
+  ScopedVector<TemplateURL> t_urls = GetPrepopulatedTemplateFromPrefs(profile);
+  if (!t_urls.empty())
+    return t_urls.Pass();
 
   const PrepopulatedEngine** engines;
   size_t num_engines;
   GetPrepopulationSetFromCountryID(profile ? profile->GetPrefs() : NULL,
                                    &engines, &num_engines);
   for (size_t i = 0; i != num_engines; ++i) {
-    t_urls->push_back(
+    t_urls.push_back(
         MakePrepopulatedTemplateURLFromPrepopulateEngine(profile, *engines[i]));
   }
+  return t_urls.Pass();
 }
 
 void ClearPrepopulatedEnginesInPrefs(Profile* profile) {
@@ -1280,11 +1280,11 @@
 
 TemplateURL* GetPrepopulatedDefaultSearch(Profile* profile) {
   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(profile, &loaded_urls.get(), &default_search_index);
+  ScopedVector<TemplateURL> loaded_urls = GetPrepopulatedEngines(
+      profile, &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);
diff --git a/chrome/browser/search_engines/template_url_prepopulate_data.h b/chrome/browser/search_engines/template_url_prepopulate_data.h
index 40f6cd7..2d644982 100644
--- a/chrome/browser/search_engines/template_url_prepopulate_data.h
+++ b/chrome/browser/search_engines/template_url_prepopulate_data.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/memory/scoped_vector.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/search_engines/search_engine_type.h"
 
@@ -44,13 +45,11 @@
 // file then it returns the version specified there.
 int GetDataVersion(PrefService* prefs);
 
-// Loads the set of TemplateURLs from the prepopulate data.  Ownership of the
-// TemplateURLs is passed to the caller.  On return,
+// Loads the set of TemplateURLs from the prepopulate data. On return,
 // |default_search_provider_index| is set to the index of the default search
 // provider.
-void GetPrepopulatedEngines(Profile* profile,
-                            std::vector<TemplateURL*>* t_urls,
-                            size_t* default_search_provider_index);
+ScopedVector<TemplateURL> GetPrepopulatedEngines(
+    Profile* profile, size_t* default_search_provider_index);
 
 // Removes prepopulated engines and their version stored in user prefs.
 void ClearPrepopulatedEnginesInPrefs(Profile* profile);
diff --git a/chrome/browser/search_engines/template_url_prepopulate_data_unittest.cc b/chrome/browser/search_engines/template_url_prepopulate_data_unittest.cc
index d2e4db4..4e33b43 100644
--- a/chrome/browser/search_engines/template_url_prepopulate_data_unittest.cc
+++ b/chrome/browser/search_engines/template_url_prepopulate_data_unittest.cc
@@ -86,10 +86,10 @@
   TestingProfile profile;
   for (size_t i = 0; i < arraysize(kCountryIds); ++i) {
     profile.GetPrefs()->SetInteger(prefs::kCountryIDAtInstall, kCountryIds[i]);
-    ScopedVector<TemplateURL> urls;
     size_t default_index;
-    TemplateURLPrepopulateData::GetPrepopulatedEngines(&profile, &urls.get(),
-                                                       &default_index);
+    ScopedVector<TemplateURL> urls =
+        TemplateURLPrepopulateData::GetPrepopulatedEngines(&profile,
+                                                           &default_index);
     std::set<int> unique_ids;
     for (size_t turl_i = 0; turl_i < urls.size(); ++turl_i) {
       ASSERT_TRUE(unique_ids.find(urls[turl_i]->prepopulate_id()) ==
@@ -121,10 +121,10 @@
   int version = TemplateURLPrepopulateData::GetDataVersion(prefs);
   EXPECT_EQ(1, version);
 
-  ScopedVector<TemplateURL> t_urls;
   size_t default_index;
-  TemplateURLPrepopulateData::GetPrepopulatedEngines(&profile, &t_urls.get(),
-                                                     &default_index);
+  ScopedVector<TemplateURL> t_urls =
+      TemplateURLPrepopulateData::GetPrepopulatedEngines(&profile,
+                                                         &default_index);
 
   ASSERT_EQ(1u, t_urls.size());
   EXPECT_EQ(ASCIIToUTF16("foo"), t_urls[0]->short_name());
@@ -149,9 +149,8 @@
   overrides->Append(entry->DeepCopy());
   prefs->SetUserPref(prefs::kSearchProviderOverrides, overrides);
 
-  t_urls.clear();
-  TemplateURLPrepopulateData::GetPrepopulatedEngines(&profile, &t_urls.get(),
-                                                     &default_index);
+  t_urls = TemplateURLPrepopulateData::GetPrepopulatedEngines(&profile,
+                                                              &default_index);
   ASSERT_EQ(1u, t_urls.size());
   EXPECT_EQ(ASCIIToUTF16("foo"), t_urls[0]->short_name());
   EXPECT_EQ(ASCIIToUTF16("fook"), t_urls[0]->keyword());
@@ -184,9 +183,8 @@
   overrides->Append(entry->DeepCopy());
   prefs->SetUserPref(prefs::kSearchProviderOverrides, overrides);
 
-  t_urls.clear();
-  TemplateURLPrepopulateData::GetPrepopulatedEngines(&profile, &t_urls.get(),
-                                                     &default_index);
+  t_urls = TemplateURLPrepopulateData::GetPrepopulatedEngines(&profile,
+                                                              &default_index);
   EXPECT_EQ(2u, t_urls.size());
 }
 
@@ -216,10 +214,10 @@
   version = TemplateURLPrepopulateData::GetDataVersion(prefs);
   EXPECT_EQ(TemplateURLPrepopulateData::kCurrentDataVersion, version);
 
-  ScopedVector<TemplateURL> t_urls;
   size_t default_index;
-  TemplateURLPrepopulateData::GetPrepopulatedEngines(&profile, &t_urls.get(),
-                                                     &default_index);
+  ScopedVector<TemplateURL> t_urls =
+      TemplateURLPrepopulateData::GetPrepopulatedEngines(&profile,
+                                                         &default_index);
   ASSERT_FALSE(t_urls.empty());
   for (size_t i = 0; i < t_urls.size(); ++i) {
     EXPECT_NE(ASCIIToUTF16("foo"), t_urls[i]->short_name());
@@ -245,10 +243,10 @@
   CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       switches::kCountry, "US");
   TestingProfile profile;
-  ScopedVector<TemplateURL> t_urls;
   size_t default_index;
-  TemplateURLPrepopulateData::GetPrepopulatedEngines(&profile, &t_urls.get(),
-                                                     &default_index);
+  ScopedVector<TemplateURL> t_urls =
+      TemplateURLPrepopulateData::GetPrepopulatedEngines(&profile,
+                                                         &default_index);
 
   // Ensure all the URLs have the required fields populated.
   ASSERT_FALSE(t_urls.empty());
diff --git a/chrome/browser/search_engines/template_url_service.cc b/chrome/browser/search_engines/template_url_service.cc
index 02f1551..cca1a360 100644
--- a/chrome/browser/search_engines/template_url_service.cc
+++ b/chrome/browser/search_engines/template_url_service.cc
@@ -708,53 +708,47 @@
   return FirstPotentialDefaultEngine(template_urls_);
 }
 
-void TemplateURLService::ResetURLs() {
+void TemplateURLService::RepairPrepopulatedSearchEngines() {
   // Can't clean DB if it hasn't been loaded.
   DCHECK(loaded());
-  DCHECK(service_);
-  ClearDefaultProviderFromPrefs();
 
-  TemplateURLVector entries_to_process = template_urls_;
-  // Clear default provider to be able to delete it.
-  default_search_provider_ = NULL;
-  for (TemplateURLVector::const_iterator i = entries_to_process.begin();
-       i != entries_to_process.end(); ++i)
+  size_t default_search_provider_index = 0;
+  ScopedVector<TemplateURL> prepopulated_urls =
+      TemplateURLPrepopulateData::GetPrepopulatedEngines(
+          profile_, &default_search_provider_index);
+  DCHECK(!prepopulated_urls.empty());
+  int default_search_engine_id =
+      prepopulated_urls[default_search_provider_index]->prepopulate_id();
+  TemplateURL* current_dse = default_search_provider_;
+  ActionsFromPrepopulateData actions(CreateActionsFromCurrentPrepopulateData(
+      &prepopulated_urls, template_urls_, current_dse));
+  // Remove items.
+  for (std::vector<TemplateURL*>::iterator i = actions.removed_engines.begin();
+       i < actions.removed_engines.end(); ++i)
     RemoveNoNotify(*i);
 
-  entries_to_process.clear();
-  provider_map_.reset(new SearchHostToURLsMap);
-  UIThreadSearchTermsData search_terms_data(profile_);
-  provider_map_->Init(TemplateURLVector(), search_terms_data);
-
-  TemplateURL* default_search_provider = NULL;
-  // Force GetSearchProvidersUsingLoadedEngines() to include the prepopulated
-  // engines in the list by claiming we are currently on version 0, ensuring
-  // that the prepopulate data version will be newer.
-  int new_resource_keyword_version = 0;
-  GetSearchProvidersUsingLoadedEngines(service_.get(), profile_,
-                                       &entries_to_process,
-                                       &default_search_provider,
-                                       &new_resource_keyword_version,
-                                       &pre_sync_deletes_);
-  // Setup search engines and a default one.
-  base::AutoReset<DefaultSearchChangeOrigin> change_origin(
-      &dsp_change_origin_, DSP_CHANGE_PROFILE_RESET);
-  AddTemplateURLsAndSetupDefaultEngine(&entries_to_process,
-                                       default_search_provider);
-
-  // Repopulate extension keywords.
-  std::vector<ExtensionKeyword> extension_keywords =
-      GetExtensionKeywords(profile());
-  for (size_t i = 0; i < extension_keywords.size(); ++i) {
-    TemplateURL* extension_url =
-        CreateTemplateURLForExtension(extension_keywords[i]);
-    AddNoNotify(extension_url, true);
+  // Edit items.
+  for (EditedEngines::iterator i(actions.edited_engines.begin());
+       i < actions.edited_engines.end(); ++i) {
+    UIThreadSearchTermsData search_terms_data(profile());
+    TemplateURL new_values(profile(), i->second);
+    UpdateNoNotify(i->first, new_values, search_terms_data);
   }
 
-  if (new_resource_keyword_version)
-    service_->SetBuiltinKeywordVersion(new_resource_keyword_version);
+  // Add items.
+  for (std::vector<TemplateURL*>::iterator i = actions.added_engines.begin();
+       i < actions.added_engines.end(); ++i)
+    AddNoNotify(*i, true);
 
-  EnsureDefaultSearchProviderExists();
+  // Change the DSE.
+  TemplateURL* new_dse = FindURLByPrepopulateID(template_urls_,
+                                                default_search_engine_id);
+  DCHECK(new_dse);
+  if (CanMakeDefault(new_dse)) {
+    base::AutoReset<DefaultSearchChangeOrigin> change_origin(
+        &dsp_change_origin_, DSP_CHANGE_PROFILE_RESET);
+    SetDefaultSearchProviderNoNotify(new_dse);
+  }
   NotifyObservers();
 }
 
@@ -1880,10 +1874,10 @@
   if (template_url->prepopulate_id() == 0)
     return;
 
-  ScopedVector<TemplateURL> prepopulated_urls;
   size_t default_search_index;
-  TemplateURLPrepopulateData::GetPrepopulatedEngines(profile,
-      &prepopulated_urls.get(), &default_search_index);
+  ScopedVector<TemplateURL> prepopulated_urls =
+      TemplateURLPrepopulateData::GetPrepopulatedEngines(profile,
+                                                         &default_search_index);
 
   for (size_t i = 0; i < prepopulated_urls.size(); ++i) {
     if (prepopulated_urls[i]->prepopulate_id() == prepopulate_id) {
diff --git a/chrome/browser/search_engines/template_url_service.h b/chrome/browser/search_engines/template_url_service.h
index 019552f6..c47c85e 100644
--- a/chrome/browser/search_engines/template_url_service.h
+++ b/chrome/browser/search_engines/template_url_service.h
@@ -241,12 +241,16 @@
   // destroyed at any time so should be used right after the call.
   TemplateURL* FindNewDefaultSearchProvider();
 
-  // Resets the search providers to the prepopulated engines plus any keywords
-  // from currently-installed extensions.  The user will lose all auto-added
-  // keywords from webpages, all edits to both normal and extension keywords,
-  // and any keywords belonging to no-longer-installed extensions.
-  // Modifications will be synced later.
-  void ResetURLs();
+  // Performs the same actions that happen when the prepopulate data version is
+  // revved: all existing prepopulated entries are checked against the current
+  // prepopulate data, any now-extraneous safe_for_autoreplace() entries are
+  // removed, any existing engines are reset to the provided data (except for
+  // user-edited names or keywords), and any new prepopulated anegines are
+  // added.
+  //
+  // After this, the default search engine is reset to the default entry in the
+  // prepopulate data.
+  void RepairPrepopulatedSearchEngines();
 
   // Observers used to listen for changes to the model.
   // TemplateURLService does NOT delete the observers when deleted.
diff --git a/chrome/browser/search_engines/template_url_service_sync_unittest.cc b/chrome/browser/search_engines/template_url_service_sync_unittest.cc
index e41aeda..c4c9cfd 100644
--- a/chrome/browser/search_engines/template_url_service_sync_unittest.cc
+++ b/chrome/browser/search_engines/template_url_service_sync_unittest.cc
@@ -1885,10 +1885,10 @@
 TEST_F(TemplateURLServiceSyncTest, PreSyncUpdates) {
   const char* kNewKeyword = "somethingnew";
   // Fetch the prepopulate search engines so we know what they are.
-  ScopedVector<TemplateURL> prepop_turls;
   size_t default_search_provider_index = 0;
-  TemplateURLPrepopulateData::GetPrepopulatedEngines(
-      profile_a(), &prepop_turls.get(), &default_search_provider_index);
+  ScopedVector<TemplateURL> prepop_turls =
+      TemplateURLPrepopulateData::GetPrepopulatedEngines(
+          profile_a(), &default_search_provider_index);
 
   // We have to prematurely exit this test if for some reason this machine does
   // not have any prepopulate TemplateURLs.
diff --git a/chrome/browser/search_engines/template_url_service_unittest.cc b/chrome/browser/search_engines/template_url_service_unittest.cc
index 41c117e..252c58e 100644
--- a/chrome/browser/search_engines/template_url_service_unittest.cc
+++ b/chrome/browser/search_engines/template_url_service_unittest.cc
@@ -138,54 +138,6 @@
 }  // namespace
 
 
-// TemplateURLServiceExtensionTest --------------------------------------------
-
-class TemplateURLServiceExtensionTest : public ExtensionServiceTestBase,
-                                        public TemplateURLServiceTestUtilBase {
- public:
-  TemplateURLServiceExtensionTest();
-  virtual ~TemplateURLServiceExtensionTest();
-
-  // ExtensionServiceTestBase:
-  virtual void SetUp() OVERRIDE;
-
-  // TemplateURLServiceTestUtilBase:
-  virtual TestingProfile* profile() const OVERRIDE;
-
-  void CheckExtensionKeyword(const string16& keyword, const string16& name);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TemplateURLServiceExtensionTest);
-};
-
-TemplateURLServiceExtensionTest::TemplateURLServiceExtensionTest() {}
-
-TemplateURLServiceExtensionTest::~TemplateURLServiceExtensionTest() {}
-
-void TemplateURLServiceExtensionTest::SetUp() {
-  ExtensionServiceTestBase::SetUp();
-  ExtensionServiceInitParams params;
-  InitializeExtensionService(params);
-  TemplateURLServiceTestUtilBase::CreateTemplateUrlService();
-}
-
-
-TestingProfile* TemplateURLServiceExtensionTest::profile() const {
-  return profile_.get();
-}
-
-void TemplateURLServiceExtensionTest::CheckExtensionKeyword(
-    const string16& keyword, const string16& name) {
-#if !defined(OS_ANDROID)
-  TemplateURL* ext_url =
-      model()->GetTemplateURLForKeyword(keyword);
-  ASSERT_TRUE(ext_url);
-  EXPECT_EQ(keyword, ext_url->keyword());
-  EXPECT_EQ(name, ext_url->short_name());
-#endif
-}
-
-
 // TemplateURLServiceTest -----------------------------------------------------
 
 class TemplateURLServiceTest : public testing::Test {
@@ -338,10 +290,10 @@
     bool safe_for_autoreplace,
     size_t index_offset_from_default,
     string16* prepopulated_display_url) {
-  ScopedVector<TemplateURL> prepopulated_urls;
   size_t default_search_provider_index = 0;
-  TemplateURLPrepopulateData::GetPrepopulatedEngines(test_util_.profile(),
-      &prepopulated_urls.get(), &default_search_provider_index);
+  ScopedVector<TemplateURL> prepopulated_urls =
+      TemplateURLPrepopulateData::GetPrepopulatedEngines(
+          test_util_.profile(), &default_search_provider_index);
   EXPECT_LT(index_offset_from_default, prepopulated_urls.size());
   size_t prepopulated_index = (default_search_provider_index +
       index_offset_from_default) % prepopulated_urls.size();
@@ -932,68 +884,57 @@
   AssertEquals(*cloned_url, *model()->GetDefaultSearchProvider());
 }
 
-TEST_F(TemplateURLServiceExtensionTest, ResetURLs) {
-  VerifyLoad();
+TEST_F(TemplateURLServiceTest, RepairPrepopulatedSearchEngines) {
+  test_util_.VerifyLoad();
 
-  TemplateURL* new_provider = AddKeywordWithDate(
-      model(), "short_name", "keyword", "http://test.com/search?t={searchTerms}",
+  // Edit Google search engine.
+  TemplateURL* google = model()->GetTemplateURLForKeyword(
+      ASCIIToUTF16("google.com"));
+  ASSERT_TRUE(google);
+  model()->ResetTemplateURL(google, ASCIIToUTF16("trash"), ASCIIToUTF16("xxx"),
+                            "http://www.foo.com/s?q={searchTerms}");
+  EXPECT_EQ(ASCIIToUTF16("trash"), google->short_name());
+  EXPECT_EQ(ASCIIToUTF16("xxx"), google->keyword());
+
+  // Add third-party default search engine.
+  TemplateURL* user_dse = AddKeywordWithDate(
+      "malware", "google.com", "http://www.goo.com/s?q={searchTerms}",
       std::string(), std::string(), std::string(),
       true, "UTF-8", Time(), Time());
-  model()->SetDefaultSearchProvider(new_provider);
-  AddKeywordWithDate(
-      model(), "extension1", "ext_keyword",
-      std::string(extensions::kExtensionScheme) + "://test1", std::string(),
-      std::string(), std::string(), false, "UTF-8", Time(), Time());
-  AddKeywordWithDate(
-      model(), "extension2", "ext_keyword2",
-      std::string(extensions::kExtensionScheme) + "://test2", std::string(),
-      std::string(), std::string(), false, "UTF-8", Time(), Time());
-  TemplateURL* default_provider = model()->GetDefaultSearchProvider();
-  EXPECT_NE(SEARCH_ENGINE_GOOGLE,
-            TemplateURLPrepopulateData::GetEngineType(*default_provider));
+  model()->SetDefaultSearchProvider(user_dse);
+  EXPECT_EQ(user_dse, model()->GetDefaultSearchProvider());
 
-  DictionaryValue manifest;
-  manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0");
-  manifest.SetString(extensions::manifest_keys::kName, "ext1");
-  manifest.SetString("app.launch.web_url", "http://www.google.com");
-  manifest.SetString(extensions::manifest_keys::kOmniboxKeyword, "ext_keyword");
-  std::string error;
-  scoped_refptr<extensions::Extension> extension =
-      extensions::Extension::Create(
-          base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
-          extensions::Manifest::COMPONENT,
-          manifest,
-          extensions::Extension::NO_FLAGS,
-          &error);
-  EXPECT_TRUE(extension.get() != NULL) << error;
-  ExtensionService* extension_service = profile()->GetExtensionService();
-  ASSERT_TRUE(extension_service);
-  extension_service->AddExtension(extension.get());
-  // All URLs except |extension_keywords| should go away. Default search engine
-  // is Google again.
-  model()->ResetURLs();
+  // Remove bing.
+  TemplateURL* bing = model()->GetTemplateURLForKeyword(
+      ASCIIToUTF16("bing.com"));
+  ASSERT_TRUE(bing);
+  model()->Remove(bing);
+  EXPECT_FALSE(model()->GetTemplateURLForKeyword(ASCIIToUTF16("bing.com")));
 
-  default_provider = model()->GetDefaultSearchProvider();
-  ASSERT_TRUE(default_provider);
-  EXPECT_EQ(SEARCH_ENGINE_GOOGLE,
-            TemplateURLPrepopulateData::GetEngineType(*default_provider));
-  CheckExtensionKeyword(ASCIIToUTF16("ext_keyword"), ASCIIToUTF16("ext1"));
-  EXPECT_FALSE(model()->GetTemplateURLForKeyword(ASCIIToUTF16("ext_keyword2")));
-  EXPECT_FALSE(model()->GetTemplateURLForKeyword(ASCIIToUTF16("keyword")));
+  // Register an extension with bing keyword.
+  model()->RegisterExtensionKeyword("abcdefg", "extension_name", "bing.com");
+  EXPECT_TRUE(model()->GetTemplateURLForKeyword(ASCIIToUTF16("bing.com")));
 
-  // Reload URLs. Result should be the same, as extension keywords are now
-  // persisted.
-  ResetModel(true);
-  default_provider = model()->GetDefaultSearchProvider();
-  ASSERT_TRUE(default_provider);
-  EXPECT_EQ(SEARCH_ENGINE_GOOGLE,
-            TemplateURLPrepopulateData::GetEngineType(*default_provider));
-  CheckExtensionKeyword(ASCIIToUTF16("ext_keyword"), ASCIIToUTF16("ext1"));
-  EXPECT_FALSE(model()->GetTemplateURLForKeyword(ASCIIToUTF16("ext_keyword2")));
-  EXPECT_FALSE(model()->GetTemplateURLForKeyword(ASCIIToUTF16("keyword")));
+  model()->RepairPrepopulatedSearchEngines();
+
+  // Google is default.
+  ASSERT_EQ(google, model()->GetDefaultSearchProvider());
+  // The keyword wasn't reverted.
+  EXPECT_EQ(ASCIIToUTF16("trash"), google->short_name());
+  EXPECT_EQ("www.google.com",
+            TemplateURLService::GenerateSearchURL(google).host());
+
+  // Bing was repaired.
+  bing = model()->GetTemplateURLForKeyword(ASCIIToUTF16("bing.com"));
+  ASSERT_TRUE(bing);
+  EXPECT_FALSE(bing->IsExtensionKeyword());
+
+  // User search engine is preserved.
+  EXPECT_EQ(user_dse, model()->GetTemplateURLForHost("www.goo.com"));
+  EXPECT_EQ(ASCIIToUTF16("google.com"), user_dse->keyword());
 }
 
-TEST_F(TemplateURLServiceExtensionTest, ResetURLsWithManagedDefault) {
+TEST_F(TemplateURLServiceTest, RepairSearchEnginesWithManagedDefault) {
   // Set a managed preference that establishes a default search provider.
   const char kName[] = "test1";
   const char kKeyword[] = "test.com";
@@ -1002,12 +943,12 @@
   const char kEncodings[] = "UTF-16;UTF-32";
   const char kAlternateURL[] = "http://test.com/search#t={searchTerms}";
   const char kSearchTermsReplacementKey[] = "espv";
-  SetManagedDefaultSearchPreferences(true, kName, kKeyword,
-                                     kSearchURL, std::string(),
-                                     kIconURL, kEncodings,
-                                     kAlternateURL,
-                                     kSearchTermsReplacementKey);
-  VerifyLoad();
+  test_util_.SetManagedDefaultSearchPreferences(true, kName, kKeyword,
+                                                kSearchURL, std::string(),
+                                                kIconURL, kEncodings,
+                                                kAlternateURL,
+                                                kSearchTermsReplacementKey);
+  test_util_.VerifyLoad();
   // Verify that the default manager we are getting is the managed one.
   TemplateURLData data;
   data.short_name = ASCIIToUTF16(kName);
@@ -1018,15 +959,15 @@
   base::SplitString(kEncodings, ';', &data.input_encodings);
   data.alternate_urls.push_back(kAlternateURL);
   data.search_terms_replacement_key = kSearchTermsReplacementKey;
-  scoped_ptr<TemplateURL> expected_managed_default(new TemplateURL(profile(),
-                                                                   data));
+  scoped_ptr<TemplateURL> expected_managed_default(new TemplateURL(
+      test_util_.profile(), data));
   EXPECT_TRUE(model()->is_default_search_managed());
   const TemplateURL* actual_managed_default =
       model()->GetDefaultSearchProvider();
   ExpectSimilar(expected_managed_default.get(), actual_managed_default);
 
   // The following call has no effect on the managed search engine.
-  model()->ResetURLs();
+  model()->RepairPrepopulatedSearchEngines();
 
   EXPECT_TRUE(model()->is_default_search_managed());
   actual_managed_default = model()->GetDefaultSearchProvider();
diff --git a/chrome/browser/search_engines/util.cc b/chrome/browser/search_engines/util.cc
index 6aa478e..0ccf57f6 100644
--- a/chrome/browser/search_engines/util.cc
+++ b/chrome/browser/search_engines/util.cc
@@ -150,6 +150,17 @@
   return NULL;
 }
 
+TemplateURL* FindURLByPrepopulateID(
+    const TemplateURLService::TemplateURLVector& template_urls,
+    int prepopulate_id) {
+  for (std::vector<TemplateURL*>::const_iterator i = template_urls.begin();
+       i < template_urls.end(); ++i) {
+    if ((*i)->prepopulate_id() == prepopulate_id)
+      return *i;
+  }
+  return NULL;
+}
+
 void MergeIntoPrepopulatedEngineData(TemplateURLData* prepopulated_url,
                                      const TemplateURL* original_turl) {
   DCHECK_EQ(original_turl->prepopulate_id(), prepopulated_url->prepopulate_id);
@@ -164,7 +175,10 @@
   prepopulated_url->last_modified = original_turl->last_modified();
 }
 
-// Merges the provided prepopulated engines with the provided existing engines.
+ActionsFromPrepopulateData::ActionsFromPrepopulateData() {}
+
+ActionsFromPrepopulateData::~ActionsFromPrepopulateData() {}
+
 // This is invoked when the version of the prepopulate data changes.
 // If |removed_keyword_guids| is not NULL, the Sync GUID of each item removed
 // from the DB will be added to it.  Note that this function will take
@@ -178,15 +192,69 @@
     TemplateURL** default_search_provider,
     std::set<std::string>* removed_keyword_guids) {
   DCHECK(service == NULL || BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(prepopulated_urls);
   DCHECK(template_urls);
   DCHECK(default_search_provider);
 
+  int default_prepopulated_id =
+      (*prepopulated_urls)[default_search_index]->prepopulate_id();
+  ActionsFromPrepopulateData actions(CreateActionsFromCurrentPrepopulateData(
+      prepopulated_urls, *template_urls, *default_search_provider));
+
+  // Remove items.
+  for (std::vector<TemplateURL*>::iterator i = actions.removed_engines.begin();
+       i < actions.removed_engines.end(); ++i) {
+    scoped_ptr<TemplateURL> template_url(*i);
+    TemplateURLService::TemplateURLVector::iterator j =
+        std::find(template_urls->begin(), template_urls->end(), template_url);
+    DCHECK(j != template_urls->end());
+    DCHECK(*j != *default_search_provider);
+    template_urls->erase(j);
+    if (service) {
+      service->RemoveKeyword(template_url->id());
+      if (removed_keyword_guids)
+        removed_keyword_guids->insert(template_url->sync_guid());
+    }
+  }
+
+  // Edit items.
+  for (EditedEngines::iterator i(actions.edited_engines.begin());
+       i < actions.edited_engines.end(); ++i) {
+    TemplateURLData& data = i->second;
+    scoped_ptr<TemplateURL> existing_url(i->first);
+    if (service)
+      service->UpdateKeyword(data);
+
+    // Replace the entry in |template_urls| with the updated one.
+    TemplateURLService::TemplateURLVector::iterator j = std::find(
+        template_urls->begin(), template_urls->end(), existing_url.get());
+    *j = new TemplateURL(profile, data);
+    if (*default_search_provider == existing_url.get())
+      *default_search_provider = *j;
+  }
+
+  // Add items.
+  template_urls->insert(template_urls->end(), actions.added_engines.begin(),
+                        actions.added_engines.end());
+
+  if (!*default_search_provider) {
+    // The user had no existing default search provider, so set the
+    // default to the default prepopulated engine.
+    *default_search_provider = FindURLByPrepopulateID(*template_urls,
+                                                      default_prepopulated_id);
+  }
+}
+
+ActionsFromPrepopulateData CreateActionsFromCurrentPrepopulateData(
+    ScopedVector<TemplateURL>* prepopulated_urls,
+    const TemplateURLService::TemplateURLVector& existing_urls,
+    const TemplateURL* default_search_provider) {
   // Create a map to hold all provided |template_urls| that originally came from
   // prepopulate data (i.e. have a non-zero prepopulate_id()).
   typedef std::map<int, TemplateURL*> IDMap;
   IDMap id_to_turl;
-  for (TemplateURLService::TemplateURLVector::iterator i(
-       template_urls->begin()); i != template_urls->end(); ++i) {
+  for (TemplateURLService::TemplateURLVector::const_iterator i(
+       existing_urls.begin()); i != existing_urls.end(); ++i) {
     int prepopulate_id = (*i)->prepopulate_id();
     if (prepopulate_id > 0)
       id_to_turl[prepopulate_id] = *i;
@@ -195,45 +263,30 @@
   // For each current prepopulated URL, check whether |template_urls| contained
   // a matching prepopulated URL.  If so, update the passed-in URL to match the
   // current data.  (If the passed-in URL was user-edited, we persist the user's
-  // name and keyword.)  If not, add the prepopulated URL to |template_urls|.
-  // Along the way, point |default_search_provider| at the default prepopulated
-  // URL, if the user hasn't already set another URL as default.
+  // name and keyword.)  If not, add the prepopulated URL.
+  ActionsFromPrepopulateData actions;
   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();
     DCHECK_NE(0, prepopulated_id);
 
-    TemplateURL* url_in_vector = NULL;
     IDMap::iterator existing_url_iter(id_to_turl.find(prepopulated_id));
     if (existing_url_iter != id_to_turl.end()) {
-      // Update the data store with the new prepopulated data.  Preserve user
+      // Update the data store with the new prepopulated data. Preserve user
       // edits to the name and keyword.
       TemplateURLData data(prepopulated_url->data());
-      scoped_ptr<TemplateURL> existing_url(existing_url_iter->second);
+      TemplateURL* existing_url(existing_url_iter->second);
       id_to_turl.erase(existing_url_iter);
-      MergeIntoPrepopulatedEngineData(&data, existing_url.get());
+      MergeIntoPrepopulatedEngineData(&data, existing_url);
       // Update last_modified to ensure that if this entry is later merged with
       // entries from Sync, the conflict resolution logic knows that this was
       // updated and propagates the new values to the server.
       data.last_modified = base::Time::Now();
-      if (service)
-        service->UpdateKeyword(data);
-
-      // Replace the entry in |template_urls| with the updated one.
-      TemplateURLService::TemplateURLVector::iterator j = std::find(
-          template_urls->begin(), template_urls->end(), existing_url.get());
-      *j = new TemplateURL(profile, data);
-      url_in_vector = *j;
-      if (*default_search_provider == existing_url.get())
-        *default_search_provider = url_in_vector;
+      actions.edited_engines.push_back(std::make_pair(existing_url, data));
     } else {
-      template_urls->push_back(prepopulated_url.release());
-      url_in_vector = template_urls->back();
+      actions.added_engines.push_back(prepopulated_url.release());
     }
-    DCHECK(url_in_vector);
-    if (i == default_search_index && !*default_search_provider)
-      *default_search_provider = url_in_vector;
   }
   // The above loop takes ownership of all the contents of prepopulated_urls.
   // Clear the pointers.
@@ -244,21 +297,13 @@
   // found in the prepopulate data.  Any remaining URLs that haven't been
   // user-edited or made default can be removed from the data store.
   for (IDMap::iterator i(id_to_turl.begin()); i != id_to_turl.end(); ++i) {
-    const TemplateURL* template_url = i->second;
+    TemplateURL* template_url = i->second;
     if ((template_url->safe_for_autoreplace()) &&
-        (template_url != *default_search_provider)) {
-      TemplateURLService::TemplateURLVector::iterator j =
-          std::find(template_urls->begin(), template_urls->end(), template_url);
-      DCHECK(j != template_urls->end());
-      template_urls->erase(j);
-       if (service) {
-         service->RemoveKeyword(template_url->id());
-         if (removed_keyword_guids)
-           removed_keyword_guids->insert(template_url->sync_guid());
-       }
-      delete template_url;
-    }
+        (template_url != default_search_provider))
+      actions.removed_engines.push_back(template_url);
   }
+
+  return actions;
 }
 
 void GetSearchProvidersUsingKeywordResult(
@@ -321,10 +366,10 @@
   DCHECK(default_search_provider);
   DCHECK(resource_keyword_version);
 
-  ScopedVector<TemplateURL> prepopulated_urls;
   size_t default_search_index;
-  TemplateURLPrepopulateData::GetPrepopulatedEngines(profile,
-      &prepopulated_urls.get(), &default_search_index);
+  ScopedVector<TemplateURL> prepopulated_urls =
+      TemplateURLPrepopulateData::GetPrepopulatedEngines(profile,
+                                                         &default_search_index);
   RemoveDuplicatePrepopulateIDs(service, prepopulated_urls,
                                 *default_search_provider, template_urls,
                                 removed_keyword_guids);
diff --git a/chrome/browser/search_engines/util.h b/chrome/browser/search_engines/util.h
index b496e2a..56553de 100644
--- a/chrome/browser/search_engines/util.h
+++ b/chrome/browser/search_engines/util.h
@@ -27,11 +27,55 @@
 // |profile|.
 GURL GetDefaultSearchURLForSearchTerms(Profile* profile, const string16& terms);
 
+// Returns matching URL from |template_urls| or NULL.
+TemplateURL* FindURLByPrepopulateID(
+    const TemplateURLService::TemplateURLVector& template_urls,
+    int prepopulate_id);
+
 // Modifies |prepopulated_url| so that it contains user-modified fields from
 // |original_turl|. Both URLs must have the same prepopulate_id.
 void MergeIntoPrepopulatedEngineData(TemplateURLData* prepopulated_url,
                                      const TemplateURL* original_turl);
 
+// CreateActionsFromCurrentPrepopulateData() (see below) takes in the current
+// prepopulated URLs as well as the user's current URLs, and returns an instance
+// of the following struct representing the changes necessary to bring the
+// user's URLs in line with the prepopulated URLs.
+//
+// There are three types of changes:
+// (1) Previous prepopulated engines that no longer exist in the current set of
+//     prepopulated engines and thus should be removed from the user's current
+//     URLs.
+// (2) Previous prepopulated engines whose data has changed.  The existing
+//     entries for these engines should be updated to reflect the new data,
+//     except for any user-set names and keywords, which can be preserved.
+// (3) New prepopulated engines not in the user's engine list, which should be
+//     added.
+
+// The pair of current search engine and its new value.
+typedef std::pair<TemplateURL*, TemplateURLData> EditedSearchEngine;
+typedef std::vector<EditedSearchEngine> EditedEngines;
+
+struct ActionsFromPrepopulateData {
+  ActionsFromPrepopulateData();
+  ~ActionsFromPrepopulateData();
+
+  TemplateURLService::TemplateURLVector removed_engines;
+  EditedEngines edited_engines;
+  TemplateURLService::TemplateURLVector added_engines;
+};
+
+// Given the user's current URLs and the current set of prepopulated URLs,
+// produces the set of actions (see above) required to make the user's URLs
+// reflect the prepopulate data.  |default_search_provider| is used to avoid
+// placing the current default provider on the "to be removed" list.
+//
+// NOTE: Takes ownership of, and clears, |prepopulated_urls|.
+ActionsFromPrepopulateData CreateActionsFromCurrentPrepopulateData(
+    ScopedVector<TemplateURL>* prepopulated_urls,
+    const TemplateURLService::TemplateURLVector& existing_urls,
+    const TemplateURL* default_search_provider);
+
 // Processes the results of WebDataService::GetKeywords, combining it with
 // prepopulated search providers to result in:
 //  * a set of template_urls (search providers). The caller owns the