Fie.


git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8111 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/autocomplete/autocomplete_edit.cc b/chrome/browser/autocomplete/autocomplete_edit.cc
index 3d43dbb..1cff5eeeb 100644
--- a/chrome/browser/autocomplete/autocomplete_edit.cc
+++ b/chrome/browser/autocomplete/autocomplete_edit.cc
@@ -21,9 +21,9 @@
 #include "chrome/browser/controller.h"
 #include "chrome/browser/drag_utils.h"
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_model.h"
 #include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/url_fixer_upper.h"
 #include "chrome/browser/user_metrics.h"
 #include "chrome/browser/views/location_bar_view.h"
diff --git a/chrome/browser/autocomplete/autocomplete_popup.cc b/chrome/browser/autocomplete/autocomplete_popup.cc
index d2437a3..23535c3 100644
--- a/chrome/browser/autocomplete/autocomplete_popup.cc
+++ b/chrome/browser/autocomplete/autocomplete_popup.cc
@@ -13,8 +13,8 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/net/dns_global.h"
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/views/location_bar_view.h"
 #include "chrome/common/gfx/chrome_canvas.h"
 #include "chrome/common/l10n_util.h"
diff --git a/chrome/browser/autocomplete/keyword_provider.cc b/chrome/browser/autocomplete/keyword_provider.cc
index defb656..9f0eb3ef 100644
--- a/chrome/browser/autocomplete/keyword_provider.cc
+++ b/chrome/browser/autocomplete/keyword_provider.cc
@@ -8,8 +8,8 @@
 
 #include "base/string_util.h"
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/common/l10n_util.h"
 #include "net/base/escape.h"
 #include "net/base/net_util.h"
diff --git a/chrome/browser/autocomplete/keyword_provider_unittest.cc b/chrome/browser/autocomplete/keyword_provider_unittest.cc
index a650701..e82e8d6c 100644
--- a/chrome/browser/autocomplete/keyword_provider_unittest.cc
+++ b/chrome/browser/autocomplete/keyword_provider_unittest.cc
@@ -4,8 +4,8 @@
 
 #include "base/message_loop.h"
 #include "chrome/browser/autocomplete/keyword_provider.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
 #include "googleurl/src/gurl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/autocomplete/search_provider.cc b/chrome/browser/autocomplete/search_provider.cc
index d302b30..cd25d13 100644
--- a/chrome/browser/autocomplete/search_provider.cc
+++ b/chrome/browser/autocomplete/search_provider.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/google_util.h"
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/url_fixer_upper.h"
 #include "chrome/common/json_value_serializer.h"
 #include "chrome/common/l10n_util.h"
diff --git a/chrome/browser/autocomplete/search_provider.h b/chrome/browser/autocomplete/search_provider.h
index 687ea07..8fe402f8 100644
--- a/chrome/browser/autocomplete/search_provider.h
+++ b/chrome/browser/autocomplete/search_provider.h
@@ -17,7 +17,7 @@
 
 #include "chrome/browser/autocomplete/autocomplete.h"
 #include "chrome/browser/history/history.h"
-#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/template_url.h"
 #include "chrome/browser/url_fetcher.h"
 
 class Profile;
diff --git a/chrome/browser/browser.scons b/chrome/browser/browser.scons
index 0eb3d4bd..e937d99 100644
--- a/chrome/browser/browser.scons
+++ b/chrome/browser/browser.scons
@@ -115,15 +115,15 @@
       'safe_browsing/safe_browsing_database_bloom.cc',
       'safe_browsing/safe_browsing_database_impl.cc',
       'safe_browsing/safe_browsing_util.cc',
-      'search_engines/template_url.cc',
-      'search_engines/template_url_model.cc',
-      'search_engines/template_url_parser.cc',
       'session_startup_pref.cc',
       'sessions/session_command.cc',
       'sessions/session_id.cc',
       'spellcheck_worditerator.cc',
       'spellchecker.cc',
       'ssl_error_info.cc',
+      'template_url.cc',
+      'template_url_model.cc',
+      'template_url_parser.cc',
       'url_fetcher.cc',
       'url_fetcher_protect.cc',
       'user_metrics.cc',
@@ -259,8 +259,6 @@
       'safe_browsing/safe_browsing_blocking_page.cc',
       'safe_browsing/safe_browsing_service.cc',
       'sandbox_policy.cc',
-      'search_engines/template_url_fetcher.cc',
-      'search_engines/template_url_prepopulate_data.cc',
       'sessions/base_session_service.cc',
       'sessions/session_backend.cc',
       'sessions/session_restore.cc',
@@ -293,6 +291,8 @@
       'tabs/tab_strip_model_order_controller.cc',
       'task_manager.cc',
       'task_manager_resource_providers.cc',
+      'template_url_fetcher.cc',
+      'template_url_prepopulate_data.cc',
       'toolbar_model.cc',
       'url_fixer_upper.cc',
       'user_data_manager.cc',
diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj
index cd93d66..0fca784c 100644
--- a/chrome/browser/browser.vcproj
+++ b/chrome/browser/browser.vcproj
@@ -602,6 +602,46 @@
 				>
 			</File>
 			<File
+				RelativePath=".\template_url.cc"
+				>
+			</File>
+			<File
+				RelativePath=".\template_url.h"
+				>
+			</File>
+			<File
+				RelativePath=".\template_url_fetcher.cc"
+				>
+			</File>
+			<File
+				RelativePath=".\template_url_fetcher.h"
+				>
+			</File>
+			<File
+				RelativePath=".\template_url_model.cc"
+				>
+			</File>
+			<File
+				RelativePath=".\template_url_model.h"
+				>
+			</File>
+			<File
+				RelativePath=".\template_url_parser.cc"
+				>
+			</File>
+			<File
+				RelativePath=".\template_url_parser.h"
+				>
+			</File>
+			<File
+				RelativePath=".\template_url_prepopulate_data.cc"
+				>
+			</File>
+			<File
+				RelativePath=".\template_url_prepopulate_data.h"
+				>
+			</File>
+			<File
 				RelativePath=".\url_fetcher.cc"
 				>
 			</File>
@@ -2510,140 +2550,6 @@
 				>
 			</File>
 		</Filter>
-		<Filter
-			Name="Search Engines"
-			>
-			<File
-				RelativePath=".\search_engines\template_url.cc"
-				>
-				<FileConfiguration
-					Name="Debug|Win32"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						ObjectFile="$(IntDir)\$(InputName)1.obj"
-						XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Win32"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						ObjectFile="$(IntDir)\$(InputName)1.obj"
-						XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
-					/>
-				</FileConfiguration>
-			</File>
-			<File
-				RelativePath=".\search_engines\template_url.h"
-				>
-			</File>
-			<File
-				RelativePath=".\search_engines\template_url_fetcher.cc"
-				>
-				<FileConfiguration
-					Name="Debug|Win32"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						ObjectFile="$(IntDir)\$(InputName)1.obj"
-						XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Win32"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						ObjectFile="$(IntDir)\$(InputName)1.obj"
-						XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
-					/>
-				</FileConfiguration>
-			</File>
-			<File
-				RelativePath=".\search_engines\template_url_fetcher.h"
-				>
-			</File>
-			<File
-				RelativePath=".\search_engines\template_url_model.cc"
-				>
-				<FileConfiguration
-					Name="Debug|Win32"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						ObjectFile="$(IntDir)\$(InputName)1.obj"
-						XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Win32"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						ObjectFile="$(IntDir)\$(InputName)1.obj"
-						XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
-					/>
-				</FileConfiguration>
-			</File>
-			<File
-				RelativePath=".\search_engines\template_url_model.h"
-				>
-			</File>
-			<File
-				RelativePath=".\search_engines\template_url_parser.cc"
-				>
-				<FileConfiguration
-					Name="Debug|Win32"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						ObjectFile="$(IntDir)\$(InputName)1.obj"
-						XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Win32"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						ObjectFile="$(IntDir)\$(InputName)1.obj"
-						XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
-					/>
-				</FileConfiguration>
-			</File>
-			<File
-				RelativePath=".\search_engines\template_url_parser.h"
-				>
-			</File>
-			<File
-				RelativePath=".\search_engines\template_url_prepopulate_data.cc"
-				>
-				<FileConfiguration
-					Name="Debug|Win32"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						ObjectFile="$(IntDir)\$(InputName)1.obj"
-						XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Win32"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						ObjectFile="$(IntDir)\$(InputName)1.obj"
-						XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
-					/>
-				</FileConfiguration>
-			</File>
-			<File
-				RelativePath=".\search_engines\template_url_prepopulate_data.h"
-				>
-			</File>
-		</Filter>
 		<File
 			RelativePath=".\browser_trial.cc"
 			>
diff --git a/chrome/browser/browser_prefs.cc b/chrome/browser/browser_prefs.cc
index 4f83bac..abbf875 100644
--- a/chrome/browser/browser_prefs.cc
+++ b/chrome/browser/browser_prefs.cc
@@ -19,7 +19,7 @@
 #include "chrome/browser/ssl_manager.h"
 #include "chrome/browser/tabs/tab_strip_model.h"
 #include "chrome/browser/task_manager.h"
-#include "chrome/browser/search_engines/template_url_prepopulate_data.h"
+#include "chrome/browser/template_url_prepopulate_data.h"
 #include "chrome/browser/views/bookmark_bar_view.h"
 #include "chrome/browser/views/bookmark_manager_view.h"
 #include "chrome/browser/views/bookmark_table_view.h"
diff --git a/chrome/browser/browsing_data_remover.cc b/chrome/browser/browsing_data_remover.cc
index e9140b28..86f8e98 100644
--- a/chrome/browser/browsing_data_remover.cc
+++ b/chrome/browser/browsing_data_remover.cc
@@ -8,9 +8,9 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/download/download_manager.h"
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url_model.h"
 #include "chrome/browser/sessions/session_service.h"
 #include "chrome/browser/sessions/tab_restore_service.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/user_metrics.h"
 #include "chrome/browser/webdata/web_data_service.h"
 #include "chrome/common/notification_service.h"
diff --git a/chrome/browser/dom_ui/new_tab_ui.cc b/chrome/browser/dom_ui/new_tab_ui.cc
index 7bb7d9f..9b7107a 100644
--- a/chrome/browser/dom_ui/new_tab_ui.cc
+++ b/chrome/browser/dom_ui/new_tab_ui.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/render_view_host.h"
 #include "chrome/browser/sessions/session_types.h"
 #include "chrome/browser/tab_contents/navigation_entry.h"
-#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/template_url.h"
 #include "chrome/browser/user_data_manager.h"
 #include "chrome/browser/user_metrics.h"
 #include "chrome/browser/views/keyword_editor_view.h"
diff --git a/chrome/browser/dom_ui/new_tab_ui.h b/chrome/browser/dom_ui/new_tab_ui.h
index a8391e8..8f700b2 100644
--- a/chrome/browser/dom_ui/new_tab_ui.h
+++ b/chrome/browser/dom_ui/new_tab_ui.h
@@ -9,8 +9,8 @@
 #include "chrome/browser/dom_ui/dom_ui_host.h"
 #include "chrome/browser/dom_ui/chrome_url_data_manager.h"
 #include "chrome/browser/history/history.h"
-#include "chrome/browser/search_engines/template_url_model.h"
 #include "chrome/browser/sessions/tab_restore_service.h"
+#include "chrome/browser/template_url_model.h"
 
 class GURL;
 class Profile;
diff --git a/chrome/browser/history/history.h b/chrome/browser/history/history.h
index 0e5a492..dcd55185 100644
--- a/chrome/browser/history/history.h
+++ b/chrome/browser/history/history.h
@@ -20,7 +20,7 @@
 #include "chrome/browser/chrome_thread.h"
 #include "chrome/browser/history/history_notifications.h"
 #include "chrome/browser/history/history_types.h"
-#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/template_url.h"
 #include "chrome/common/notification_service.h"
 #include "chrome/common/page_transition_types.h"
 #include "chrome/common/ref_counted_util.h"
diff --git a/chrome/browser/history/url_database.h b/chrome/browser/history/url_database.h
index 20aeb78..745c7e3 100644
--- a/chrome/browser/history/url_database.h
+++ b/chrome/browser/history/url_database.h
@@ -7,7 +7,7 @@
 
 #include "base/basictypes.h"
 #include "chrome/browser/history/history_types.h"
-#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/template_url.h"
 
 // Temporary until DBCloseScoper moves elsewhere.
 #include "chrome/common/sqlite_compiled_statement.h"
diff --git a/chrome/browser/importer/firefox2_importer.cc b/chrome/browser/importer/firefox2_importer.cc
index b2f61c1..bbc4e6f 100644
--- a/chrome/browser/importer/firefox2_importer.cc
+++ b/chrome/browser/importer/firefox2_importer.cc
@@ -11,8 +11,8 @@
 #include "base/values.h"
 #include "chrome/browser/importer/firefox_importer_utils.h"
 #include "chrome/browser/importer/mork_reader.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_parser.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_parser.h"
 #include "chrome/common/l10n_util.h"
 #include "chrome/common/time_format.h"
 #include "generated_resources.h"
diff --git a/chrome/browser/importer/firefox_importer_utils.cc b/chrome/browser/importer/firefox_importer_utils.cc
index 023fcf9..719ebcc 100644
--- a/chrome/browser/importer/firefox_importer_utils.cc
+++ b/chrome/browser/importer/firefox_importer_utils.cc
@@ -13,9 +13,9 @@
 #include "base/string_util.h"
 #include "base/sys_string_conversions.h"
 #include "base/time.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_model.h"
-#include "chrome/browser/search_engines/template_url_parser.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
+#include "chrome/browser/template_url_parser.h"
 #include "chrome/common/win_util.h"
 #include "googleurl/src/gurl.h"
 #include "net/base/base64.h"
diff --git a/chrome/browser/importer/ie_importer.cc b/chrome/browser/importer/ie_importer.cc
index d362739..55bed64 100644
--- a/chrome/browser/importer/ie_importer.cc
+++ b/chrome/browser/importer/ie_importer.cc
@@ -19,7 +19,7 @@
 #include "base/win_util.h"
 #include "chrome/browser/bookmarks/bookmark_model.h"
 #include "chrome/browser/password_manager/ie7_password.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/common/l10n_util.h"
 #include "chrome/common/time_format.h"
 #include "chrome/common/win_util.h"
diff --git a/chrome/browser/importer/importer.cc b/chrome/browser/importer/importer.cc
index 52a035d..dcc1438b 100644
--- a/chrome/browser/importer/importer.cc
+++ b/chrome/browser/importer/importer.cc
@@ -21,7 +21,7 @@
 #include "chrome/browser/importer/firefox_profile_lock.h"
 #include "chrome/browser/importer/ie_importer.h"
 #include "chrome/browser/importer/toolbar_importer.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/shell_integration.h"
 #include "chrome/browser/views/importer_lock_view.h"
 #include "chrome/browser/webdata/web_data_service.h"
diff --git a/chrome/browser/importer/importer.h b/chrome/browser/importer/importer.h
index e169eb7..df13150 100644
--- a/chrome/browser/importer/importer.h
+++ b/chrome/browser/importer/importer.h
@@ -19,7 +19,7 @@
 #include "chrome/browser/password_manager/ie7_password.h"
 #endif
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/template_url.h"
 #include "chrome/common/notification_service.h"
 #include "googleurl/src/gurl.h"
 #include "webkit/glue/password_form.h"
diff --git a/chrome/browser/metrics_service.cc b/chrome/browser/metrics_service.cc
index bb2ba6242..7909a0b9 100644
--- a/chrome/browser/metrics_service.cc
+++ b/chrome/browser/metrics_service.cc
@@ -173,8 +173,8 @@
 #include "chrome/browser/plugin_service.h"
 #include "chrome/browser/profile.h"
 #include "chrome/browser/render_process_host.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/libxml_utils.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc
index 7d3a947..a849bf10 100644
--- a/chrome/browser/profile.cc
+++ b/chrome/browser/profile.cc
@@ -23,12 +23,12 @@
 #include "chrome/browser/net/chrome_url_request_context.h"
 #include "chrome/browser/profile_manager.h"
 #include "chrome/browser/render_process_host.h"
-#include "chrome/browser/search_engines/template_url_fetcher.h"
-#include "chrome/browser/search_engines/template_url_model.h"
 #include "chrome/browser/sessions/session_service.h"
 #include "chrome/browser/sessions/tab_restore_service.h"
 #include "chrome/browser/spellchecker.h"
 #include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/template_url_fetcher.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/visitedlink_master.h"
 #include "chrome/browser/webdata/web_data_service.h"
 #include "chrome/common/chrome_constants.h"
diff --git a/chrome/browser/render_view_context_menu.cc b/chrome/browser/render_view_context_menu.cc
index 311ba273..83082b43 100644
--- a/chrome/browser/render_view_context_menu.cc
+++ b/chrome/browser/render_view_context_menu.cc
@@ -8,8 +8,8 @@
 #include "chrome/app/chrome_dll_resource.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url_model.h"
 #include "chrome/browser/spellchecker.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/common/l10n_util.h"
 #include "webkit/glue/context_node_types.h"
 
diff --git a/chrome/browser/render_view_context_menu_controller.cc b/chrome/browser/render_view_context_menu_controller.cc
index bdb34e7..7a75577 100644
--- a/chrome/browser/render_view_context_menu_controller.cc
+++ b/chrome/browser/render_view_context_menu_controller.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/download/download_manager.h"
 #include "chrome/browser/download/save_package.h"
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/views/page_info_window.h"
 #include "chrome/browser/tab_contents/navigation_controller.h"
 #include "chrome/browser/tab_contents/navigation_entry.h"
diff --git a/chrome/browser/rlz/rlz.cc b/chrome/browser/rlz/rlz.cc
index 6434fb1..d65f180 100644
--- a/chrome/browser/rlz/rlz.cc
+++ b/chrome/browser/rlz/rlz.cc
@@ -19,7 +19,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profile.h"
 #include "chrome/browser/profile_manager.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/env_vars.h"
 #include "chrome/common/notification_service.h"
diff --git a/chrome/browser/search_engines/template_url.cc b/chrome/browser/search_engines/template_url.cc
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url.cc
+++ /dev/null
diff --git a/chrome/browser/search_engines/template_url.h b/chrome/browser/search_engines/template_url.h
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url.h
+++ /dev/null
diff --git a/chrome/browser/search_engines/template_url_fetcher.cc b/chrome/browser/search_engines/template_url_fetcher.cc
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url_fetcher.cc
+++ /dev/null
diff --git a/chrome/browser/search_engines/template_url_fetcher.h b/chrome/browser/search_engines/template_url_fetcher.h
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url_fetcher.h
+++ /dev/null
diff --git a/chrome/browser/search_engines/template_url_model.cc b/chrome/browser/search_engines/template_url_model.cc
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url_model.cc
+++ /dev/null
diff --git a/chrome/browser/search_engines/template_url_model.h b/chrome/browser/search_engines/template_url_model.h
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url_model.h
+++ /dev/null
diff --git a/chrome/browser/search_engines/template_url_model_unittest.cc b/chrome/browser/search_engines/template_url_model_unittest.cc
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url_model_unittest.cc
+++ /dev/null
diff --git a/chrome/browser/search_engines/template_url_parser.cc b/chrome/browser/search_engines/template_url_parser.cc
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url_parser.cc
+++ /dev/null
diff --git a/chrome/browser/search_engines/template_url_parser.h b/chrome/browser/search_engines/template_url_parser.h
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url_parser.h
+++ /dev/null
diff --git a/chrome/browser/search_engines/template_url_parser_unittest.cc b/chrome/browser/search_engines/template_url_parser_unittest.cc
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url_parser_unittest.cc
+++ /dev/null
diff --git a/chrome/browser/search_engines/template_url_prepopulate_data.cc b/chrome/browser/search_engines/template_url_prepopulate_data.cc
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url_prepopulate_data.cc
+++ /dev/null
diff --git a/chrome/browser/search_engines/template_url_prepopulate_data.h b/chrome/browser/search_engines/template_url_prepopulate_data.h
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url_prepopulate_data.h
+++ /dev/null
diff --git a/chrome/browser/search_engines/template_url_prepopulate_data_unittest.cc b/chrome/browser/search_engines/template_url_prepopulate_data_unittest.cc
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url_prepopulate_data_unittest.cc
+++ /dev/null
diff --git a/chrome/browser/search_engines/template_url_unittest.cc b/chrome/browser/search_engines/template_url_unittest.cc
deleted file mode 100644
index e69de29..0000000
--- a/chrome/browser/search_engines/template_url_unittest.cc
+++ /dev/null
diff --git a/chrome/browser/tab_contents/web_contents.cc b/chrome/browser/tab_contents/web_contents.cc
index 6c6ec47..77faa55 100644
--- a/chrome/browser/tab_contents/web_contents.cc
+++ b/chrome/browser/tab_contents/web_contents.cc
@@ -30,11 +30,11 @@
 #include "chrome/browser/printing/print_job.h"
 #include "chrome/browser/render_view_host.h"
 #include "chrome/browser/render_widget_host_view_win.h"  // TODO(brettw) delete me.
-#include "chrome/browser/search_engines/template_url_fetcher.h"
-#include "chrome/browser/search_engines/template_url_model.h"
 #include "chrome/browser/tab_contents/navigation_entry.h"
 #include "chrome/browser/tab_contents/web_contents_view.h"
 #include "chrome/browser/tab_contents/web_contents_view_win.h"
+#include "chrome/browser/template_url_fetcher.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/views/hung_renderer_view.h"  // TODO(brettw) delete me.
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/l10n_util.h"
diff --git a/chrome/browser/tabs/tab_strip_model.cc b/chrome/browser/tabs/tab_strip_model.cc
index 3b0c4bb..2e42a641 100644
--- a/chrome/browser/tabs/tab_strip_model.cc
+++ b/chrome/browser/tabs/tab_strip_model.cc
@@ -533,7 +533,7 @@
     // them. Once they have fired, we'll get a message back saying whether
     // to proceed closing the page or not, which sends us back to this method
     // with the HasUnloadListener bit cleared.
-    WebContents* web_contents = detached_contents->AsWebContents();
+    WebContents* web_contents = GetContentsAt(index)->AsWebContents();
     // If we hit this code path, the tab had better be a WebContents tab.
     DCHECK(web_contents);
     web_contents->render_view_host()->FirePageBeforeUnload();
diff --git a/chrome/browser/template_url.cc b/chrome/browser/template_url.cc
new file mode 100644
index 0000000..844ec54
--- /dev/null
+++ b/chrome/browser/template_url.cc
@@ -0,0 +1,558 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/template_url.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/rlz/rlz.h"
+#include "chrome/browser/google_url_tracker.h"
+#include "chrome/browser/template_url_model.h"
+#include "chrome/common/gfx/favicon_size.h"
+#include "chrome/common/l10n_util.h"
+#include "net/base/escape.h"
+
+// The TemplateURLRef has any number of terms that need to be replaced. Each of
+// the terms is enclosed in braces. If the character preceeding the final
+// brace is a ?, it indicates the term is optional and can be replaced with
+// an empty string.
+static const wchar_t kStartParameter = '{';
+static const wchar_t kEndParameter = '}';
+static const wchar_t kOptional = '?';
+
+// Known parameters found in the URL.
+static const wchar_t kSearchTermsParameter[] = L"searchTerms";
+static const wchar_t kSearchTermsParameterFull[] = L"{searchTerms}";
+static const wchar_t kCountParameter[] = L"count";
+static const wchar_t kStartIndexParameter[] = L"startIndex";
+static const wchar_t kStartPageParameter[] = L"startPage";
+static const wchar_t kLanguageParameter[] = L"language";
+static const wchar_t kInputEncodingParameter[] = L"inputEncoding";
+static const wchar_t kOutputEncodingParameter[] = L"outputEncoding";
+
+static const wchar_t kGoogleAcceptedSuggestionParameter[] =
+    L"google:acceptedSuggestion";
+// Host/Domain Google searches are relative to.
+static const wchar_t kGoogleBaseURLParameter[] = L"google:baseURL";
+static const wchar_t kGoogleBaseURLParameterFull[] = L"{google:baseURL}";
+// Like google:baseURL, but for the Search Suggest capability.
+static const wchar_t kGoogleBaseSuggestURLParameter[] =
+    L"google:baseSuggestURL";
+static const wchar_t kGoogleBaseSuggestURLParameterFull[] =
+    L"{google:baseSuggestURL}";
+static const wchar_t kGoogleOriginalQueryForSuggestionParameter[] =
+    L"google:originalQueryForSuggestion";
+static const wchar_t kGoogleRLZParameter[] = L"google:RLZ";
+// Same as kSearchTermsParameter, with no escaping.
+static const wchar_t kGoogleUnescapedSearchTermsParameter[] =
+    L"google:unescapedSearchTerms";
+static const wchar_t kGoogleUnescapedSearchTermsParameterFull[] =
+    L"{google:unescapedSearchTerms}";
+
+// Display value for kSearchTermsParameter.
+static const wchar_t kDisplaySearchTerms[] = L"%s";
+
+// Display value for kGoogleUnescapedSearchTermsParameter.
+static const wchar_t kDisplayUnescapedSearchTerms[] = L"%S";
+
+// Used if the count parameter is not optional. Indicates we want 10 search
+// results.
+static const wchar_t kDefaultCount[] = L"10";
+
+// Used if the parameter kOutputEncodingParameter is required.
+static const wchar_t kOutputEncodingType[] = L"UTF-8";
+
+// static
+std::wstring* TemplateURLRef::google_base_url_ = NULL;
+
+TemplateURLRef::TemplateURLRef() {
+  Set(std::wstring(), 0, 0);
+}
+
+void TemplateURLRef::Set(const std::wstring& url,
+                         int index_offset,
+                         int page_offset) {
+  url_ = url;
+  index_offset_ = index_offset;
+  page_offset_ = page_offset;
+  InvalidateCachedValues();
+}
+
+bool TemplateURLRef::ParseParameter(size_t start,
+                                    size_t end,
+                                    std::wstring* url,
+                                    Replacements* replacements) const {
+  DCHECK(start != std::string::npos &&
+         end != std::string::npos && end > start);
+  size_t length = end - start - 1;
+  bool optional = false;
+  if ((*url)[end - 1] == kOptional) {
+    optional = true;
+    length--;
+  }
+  std::wstring parameter(url->substr(start + 1, length));
+  // Remove the parameter from the string.
+  url->erase(start, end - start + 1);
+  if (parameter == kSearchTermsParameter) {
+    replacements->push_back(Replacement(SEARCH_TERMS, static_cast<int>(start)));
+  } else if (parameter == kCountParameter) {
+    if (!optional)
+      url->insert(start, kDefaultCount);
+  } else if (parameter == kStartIndexParameter) {
+    if (!optional) {
+      url->insert(start, IntToWString(index_offset_));
+    }
+  } else if (parameter == kStartPageParameter) {
+    if (!optional) {
+      url->insert(start, IntToWString(page_offset_));
+    }
+  } else if (parameter == kLanguageParameter) {
+    replacements->push_back(Replacement(LANGUAGE, static_cast<int>(start)));
+  } else if (parameter == kInputEncodingParameter) {
+    replacements->push_back(Replacement(ENCODING, static_cast<int>(start)));
+  } else if (parameter == kOutputEncodingParameter) {
+    if (!optional)
+      url->insert(start, kOutputEncodingType);
+  } else if (parameter == kGoogleAcceptedSuggestionParameter) {
+    replacements->push_back(Replacement(GOOGLE_ACCEPTED_SUGGESTION,
+                                        static_cast<int>(start)));
+  } else if (parameter == kGoogleBaseURLParameter) {
+    replacements->push_back(Replacement(GOOGLE_BASE_URL,
+                                        static_cast<int>(start)));
+  } else if (parameter == kGoogleBaseSuggestURLParameter) {
+    replacements->push_back(Replacement(GOOGLE_BASE_SUGGEST_URL,
+                                        static_cast<int>(start)));
+  } else if (parameter == kGoogleOriginalQueryForSuggestionParameter) {
+    replacements->push_back(Replacement(GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION,
+                                        static_cast<int>(start)));
+  } else if (parameter == kGoogleRLZParameter) {
+    replacements->push_back(Replacement(GOOGLE_RLZ, static_cast<int>(start)));
+  } else if (parameter == kGoogleUnescapedSearchTermsParameter) {
+    replacements->push_back(Replacement(GOOGLE_UNESCAPED_SEARCH_TERMS,
+                                        static_cast<int>(start)));
+  } else if (!optional) {
+    // Unknown required parameter. No idea what to replace this with,
+    // so fail.
+    return false;
+  }
+  return true;
+}
+
+std::wstring TemplateURLRef::ParseURL(const std::wstring& url,
+                                      Replacements* replacements,
+                                      bool* valid) const {
+  *valid = false;
+  std::wstring parsed_url = url;
+  for (size_t last = 0; last != std::string::npos; ) {
+    last = parsed_url.find(kStartParameter, last);
+    if (last != std::string::npos) {
+      size_t endTemplate = parsed_url.find(kEndParameter, last);
+      if (endTemplate != std::string::npos) {
+        if (!ParseParameter(last, endTemplate, &parsed_url, replacements)) {
+          // Not a valid parameter, return.
+          return std::wstring();
+        }
+        // ParseParamter erases from the string, as such we don't need
+        // to update last.
+      } else {
+        // Open brace without a closing brace, return.
+        return std::wstring();
+      }
+    }
+  }
+  *valid = true;
+  return parsed_url;
+}
+
+void TemplateURLRef::ParseIfNecessary() const {
+  if (!parsed_) {
+    parsed_ = true;
+    parsed_url_ = ParseURL(url_, &replacements_, &valid_);
+    supports_replacements_ = false;
+    if (valid_) {
+      bool has_only_one_search_term = false;
+      for (Replacements::const_iterator i = replacements_.begin();
+           i != replacements_.end(); ++i) {
+        if ((i->type == SEARCH_TERMS) ||
+            (i->type == GOOGLE_UNESCAPED_SEARCH_TERMS)) {
+          if (has_only_one_search_term) {
+            has_only_one_search_term = false;
+            break;
+          }
+          has_only_one_search_term = true;
+          supports_replacements_ = true;
+        }
+      }
+      // Only parse the host/key if there is one search term. Technically there
+      // could be more than one term, but it's uncommon; so we punt.
+      if (has_only_one_search_term)
+        ParseHostAndSearchTermKey();
+    }
+  }
+}
+
+void TemplateURLRef::ParseHostAndSearchTermKey() const {
+  std::wstring url_string = url_;
+  ReplaceSubstringsAfterOffset(&url_string, 0, kGoogleBaseURLParameterFull,
+                               GoogleBaseURLValue());
+  ReplaceSubstringsAfterOffset(&url_string, 0,
+                               kGoogleBaseSuggestURLParameterFull,
+                               GoogleBaseSuggestURLValue());
+
+  GURL url(WideToUTF8(url_string));
+  if (!url.is_valid())
+    return;
+
+  std::string query_string = url.query();
+  if (query_string.empty())
+    return;
+
+  url_parse::Component query, key, value;
+  query.len = static_cast<int>(query_string.size());
+  while (url_parse::ExtractQueryKeyValue(query_string.c_str(), &query, &key,
+                                         &value)) {
+    if (key.is_nonempty() && value.is_nonempty()) {
+      std::string value_string = query_string.substr(value.begin, value.len);
+      if (value_string.find(WideToASCII(kSearchTermsParameterFull), 0) !=
+          std::string::npos ||
+          value_string.find(
+              WideToASCII(kGoogleUnescapedSearchTermsParameterFull), 0) !=
+          std::string::npos) {
+        search_term_key_ = query_string.substr(key.begin, key.len);
+        host_ = url.host();
+        path_ = url.path();
+        break;
+      }
+    }
+  }
+}
+
+GURL TemplateURLRef::ReplaceSearchTerms(
+    const TemplateURL& host,
+    const std::wstring& terms,
+    int accepted_suggestion,
+    const std::wstring& original_query_for_suggestion) const {
+  ParseIfNecessary();
+  if (!valid_)
+    return GURL();
+
+  if (replacements_.empty())
+    return GURL(WideToUTF8(parsed_url_));
+
+  // Encode the search terms so that we know the encoding.
+  const std::vector<std::string>& encodings = host.input_encodings();
+  std::wstring encoded_terms;
+  std::wstring encoded_original_query;
+  std::wstring input_encoding;
+  for (size_t i = 0; i < encodings.size(); ++i) {
+    if (EscapeQueryParamValue(terms, encodings[i].c_str(), &encoded_terms)) {
+      if (!original_query_for_suggestion.empty()) {
+        EscapeQueryParamValue(original_query_for_suggestion,
+                              encodings[i].c_str(), &encoded_original_query);
+      }
+      input_encoding = ASCIIToWide(encodings[i]);
+      break;
+    }
+  }
+  if (input_encoding.empty()) {
+    encoded_terms = EscapeQueryParamValueUTF8(terms);
+    if (!original_query_for_suggestion.empty()) {
+      encoded_original_query =
+          EscapeQueryParamValueUTF8(original_query_for_suggestion);
+    }
+    input_encoding = L"UTF-8";
+  }
+
+  std::wstring url = parsed_url_;
+
+  // replacements_ is ordered in ascending order, as such we need to iterate
+  // from the back.
+  for (Replacements::reverse_iterator i = replacements_.rbegin();
+       i != replacements_.rend(); ++i) {
+    switch (i->type) {
+      case ENCODING:
+        url.insert(i->index, input_encoding);
+        break;
+
+      case GOOGLE_ACCEPTED_SUGGESTION:
+        if (accepted_suggestion == NO_SUGGESTION_CHOSEN)
+          url.insert(i->index, L"aq=f&");
+        else if (accepted_suggestion != NO_SUGGESTIONS_AVAILABLE)
+          url.insert(i->index, StringPrintf(L"aq=%d&", accepted_suggestion));
+        break;
+
+      case GOOGLE_BASE_URL:
+        url.insert(i->index, GoogleBaseURLValue());
+        break;
+
+      case GOOGLE_BASE_SUGGEST_URL:
+        url.insert(i->index, GoogleBaseSuggestURLValue());
+        break;
+
+      case GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION:
+        if (accepted_suggestion >= 0)
+          url.insert(i->index, L"oq=" + encoded_original_query + L"&");
+        break;
+
+      case GOOGLE_RLZ: {
+        std::wstring rlz_string;
+        RLZTracker::GetAccessPointRlz(RLZTracker::CHROME_OMNIBOX, &rlz_string);
+        if (!rlz_string.empty()) {
+          rlz_string = L"rlz=" + rlz_string + L"&";
+          url.insert(i->index, rlz_string);
+        }
+        break;
+      }
+
+      case GOOGLE_UNESCAPED_SEARCH_TERMS: {
+        std::string unescaped_terms;
+        WideToCodepage(terms, WideToASCII(input_encoding).c_str(),
+                       OnStringUtilConversionError::SKIP, &unescaped_terms);
+        url.insert(i->index, std::wstring(unescaped_terms.begin(),
+                                          unescaped_terms.end()));
+        break;
+      }
+
+      case LANGUAGE:
+        url.insert(i->index, g_browser_process->GetApplicationLocale());
+        break;
+
+      case SEARCH_TERMS:
+        url.insert(i->index, encoded_terms);
+        break;
+
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  return GURL(WideToUTF8(url));
+}
+
+bool TemplateURLRef::SupportsReplacement() const {
+  ParseIfNecessary();
+  return valid_ && supports_replacements_;
+}
+
+bool TemplateURLRef::IsValid() const {
+  ParseIfNecessary();
+  return valid_;
+}
+
+std::wstring TemplateURLRef::DisplayURL() const {
+  ParseIfNecessary();
+  if (!valid_)
+    return url_;  // If we're not valid, don't escape anything.
+
+  if (replacements_.empty())
+    return url_;  // Nothing to replace, return the url.
+
+  std::wstring result = url_;
+  ReplaceSubstringsAfterOffset(&result, 0, kSearchTermsParameterFull,
+                               kDisplaySearchTerms);
+
+  ReplaceSubstringsAfterOffset(&result, 0,
+                               kGoogleUnescapedSearchTermsParameterFull,
+                               kDisplayUnescapedSearchTerms);
+
+  return result;
+}
+
+// static
+std::wstring TemplateURLRef::DisplayURLToURLRef(
+    const std::wstring& display_url) {
+  std::wstring result = display_url;
+  ReplaceSubstringsAfterOffset(&result, 0, kDisplaySearchTerms,
+                               kSearchTermsParameterFull);
+  ReplaceSubstringsAfterOffset(&result, 0, kDisplayUnescapedSearchTerms,
+                               kGoogleUnescapedSearchTermsParameterFull);
+  return result;
+}
+
+const std::string& TemplateURLRef::GetHost() const {
+  ParseIfNecessary();
+  return host_;
+}
+
+const std::string& TemplateURLRef::GetPath() const {
+  ParseIfNecessary();
+  return path_;
+}
+
+const std::string& TemplateURLRef::GetSearchTermKey() const {
+  ParseIfNecessary();
+  return search_term_key_;
+}
+
+std::wstring TemplateURLRef::SearchTermToWide(const TemplateURL& host,
+                                              const std::string& term) const {
+  const std::vector<std::string>& encodings = host.input_encodings();
+  std::wstring result;
+
+  std::string unescaped =
+      UnescapeURLComponent(term, UnescapeRule::REPLACE_PLUS_WITH_SPACE);
+  for (size_t i = 0; i < encodings.size(); ++i) {
+    if (CodepageToWide(unescaped, encodings[i].c_str(),
+                       OnStringUtilConversionError::FAIL, &result))
+      return result;
+  }
+
+  // Always fall back on UTF-8 if it works.
+  if (CodepageToWide(unescaped, "UTF-8",
+                     OnStringUtilConversionError::FAIL, &result))
+    return result;
+
+  // When nothing worked, just use the escaped text. We have no idea what the
+  // encoding is. We need to substitute spaces for pluses ourselves since we're
+  // not sending it through an unescaper.
+  result = UTF8ToWide(term);
+  std::replace(result.begin(), result.end(), '+', ' ');
+  return result;
+}
+
+bool TemplateURLRef::HasGoogleBaseURLs() const {
+  ParseIfNecessary();
+  for (size_t i = 0; i < replacements_.size(); ++i) {
+    if ((replacements_[i].type == GOOGLE_BASE_URL) ||
+        (replacements_[i].type == GOOGLE_BASE_SUGGEST_URL))
+      return true;
+  }
+  return false;
+}
+
+void TemplateURLRef::InvalidateCachedValues() const {
+  supports_replacements_ = valid_ = parsed_ = false;
+  host_.clear();
+  path_.clear();
+  search_term_key_.clear();
+  replacements_.clear();
+}
+
+// Returns the value to use for replacements of type GOOGLE_BASE_URL.
+// static
+std::wstring TemplateURLRef::GoogleBaseURLValue() {
+  return google_base_url_ ?
+    (*google_base_url_) : UTF8ToWide(GoogleURLTracker::GoogleURL().spec());
+}
+
+// Returns the value to use for replacements of type GOOGLE_BASE_SUGGEST_URL.
+// static
+std::wstring TemplateURLRef::GoogleBaseSuggestURLValue() {
+  // The suggest base URL we want at the end is something like
+  // "http://clients1.google.TLD/complete/".  The key bit we want from the
+  // original Google base URL is the TLD.
+
+  // Start with the Google base URL.
+  const GURL base_url(google_base_url_ ?
+      GURL(WideToUTF8(*google_base_url_)) : GoogleURLTracker::GoogleURL());
+  DCHECK(base_url.is_valid());
+
+  // Change "www." to "clients1." in the hostname.  If no "www." was found, just
+  // prepend "clients1.".
+  const std::string base_host(base_url.host());
+  GURL::Replacements repl;
+  const std::string suggest_host("clients1." +
+      (base_host.compare(0, 4, "www.") ? base_host : base_host.substr(4)));
+  repl.SetHostStr(suggest_host);
+
+  // Replace any existing path with "/complete/".
+  static const std::string suggest_path("/complete/");
+  repl.SetPathStr(suggest_path);
+
+  // Clear the query and ref.
+  repl.ClearQuery();
+  repl.ClearRef();
+  return UTF8ToWide(base_url.ReplaceComponents(repl).spec());
+}
+
+// TemplateURL ----------------------------------------------------------------
+
+// static
+GURL TemplateURL::GenerateFaviconURL(const GURL& url) {
+  DCHECK(url.is_valid());
+  GURL::Replacements rep;
+
+  const char favicon_path[] = "/favicon.ico";
+  int favicon_path_len = arraysize(favicon_path) - 1;
+
+  rep.SetPath(favicon_path, url_parse::Component(0, favicon_path_len));
+  rep.ClearUsername();
+  rep.ClearPassword();
+  rep.ClearQuery();
+  rep.ClearRef();
+  return url.ReplaceComponents(rep);
+}
+
+void TemplateURL::SetSuggestionsURL(const std::wstring& suggestions_url,
+                                    int index_offset,
+                                    int page_offset) {
+  suggestions_url_.Set(suggestions_url, index_offset, page_offset);
+}
+
+void TemplateURL::SetURL(const std::wstring& url,
+                         int index_offset,
+                         int page_offset) {
+  url_.Set(url, index_offset, page_offset);
+}
+
+void TemplateURL::set_keyword(const std::wstring& keyword) {
+  // Case sensitive keyword matching is confusing. As such, we force all
+  // keywords to be lower case.
+  keyword_ = l10n_util::ToLower(keyword);
+  autogenerate_keyword_ = false;
+}
+
+const std::wstring& TemplateURL::keyword() const {
+  if (autogenerate_keyword_ && keyword_.empty()) {
+    // Generate a keyword and cache it.
+    keyword_ = TemplateURLModel::GenerateKeyword(
+        TemplateURLModel::GenerateSearchURL(this).GetWithEmptyPath(), true);
+  }
+  return keyword_;
+}
+
+bool TemplateURL::ShowInDefaultList() const {
+  return show_in_default_list() && url() && url()->SupportsReplacement();
+}
+
+void TemplateURL::SetFavIconURL(const GURL& url) {
+  for (std::vector<ImageRef>::iterator i = image_refs_.begin();
+       i != image_refs_.end(); ++i) {
+    if (i->type == L"image/x-icon" &&
+        i->width == kFavIconSize && i->height == kFavIconSize) {
+      if (!url.is_valid())
+        image_refs_.erase(i);
+      else
+        i->url = url;
+      return;
+    }
+  }
+  // Don't have one yet, add it.
+  if (url.is_valid()) {
+    add_image_ref(
+        TemplateURL::ImageRef(L"image/x-icon", kFavIconSize, kFavIconSize,
+                              url));
+  }
+}
+
+GURL TemplateURL::GetFavIconURL() const {
+  for (std::vector<ImageRef>::const_iterator i = image_refs_.begin();
+       i != image_refs_.end(); ++i) {
+    if ((i->type == L"image/x-icon" || i->type == L"image/vnd.microsoft.icon")
+        && i->width == kFavIconSize && i->height == kFavIconSize) {
+      return i->url;
+    }
+  }
+  return GURL();
+}
+
+void TemplateURL::InvalidateCachedValues() const {
+  url_.InvalidateCachedValues();
+  suggestions_url_.InvalidateCachedValues();
+  if (autogenerate_keyword_)
+    keyword_.clear();
+}
+
diff --git a/chrome/browser/template_url.h b/chrome/browser/template_url.h
new file mode 100644
index 0000000..a675eec
--- /dev/null
+++ b/chrome/browser/template_url.h
@@ -0,0 +1,436 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_TEMPLATE_URL_H__
+#define CHROME_BROWSER_TEMPLATE_URL_H__
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/time.h"
+#include "googleurl/src/gurl.h"
+
+class TemplateURL;
+
+// TemplateURL represents the relevant portions of the Open Search Description
+// Document (http://www.opensearch.org/Specifications/OpenSearch).
+// The main use case for TemplateURL is to use the TemplateURLRef returned by
+// suggestions_url or url for keyword/suggestion expansion:
+// . suggestions_url describes a URL that is ideal for as you type suggestions.
+//   The returned results are in the mime type application/x-suggestions+json.
+// . url describes a URL that may be used as a shortcut. Returned results are
+//   are text/html.
+// Before using either one, make sure it's non-NULL, and if you intend to use
+// it to replace search terms, make sure SupportsReplacement returns true.
+// To use either URL invoke the ReplaceSearchTerms method on the corresponding
+// TemplateURLRef.
+//
+// For files parsed from the Web, be sure and invoke IsValid. IsValid returns
+// true if the URL could be parsed.
+//
+// Both TemplateURL and TemplateURLRef have value semantics. This allows the
+// UI to create a copy while the user modifies the values.
+class TemplateURLRef {
+ public:
+  // Magic numbers to pass to ReplaceSearchTerms() for the |accepted_suggestion|
+  // parameter.  Most callers aren't using Suggest capabilities and should just
+  // pass NO_SUGGESTIONS_AVAILABLE.
+  // NOTE: Because positive values are meaningful, make sure these are negative!
+  enum AcceptedSuggestion {
+    NO_SUGGESTION_CHOSEN = -1,
+    NO_SUGGESTIONS_AVAILABLE = -2,
+  };
+
+  TemplateURLRef();
+
+  TemplateURLRef(const std::wstring& url, int index_offset, int page_offset)
+      : url_(url),
+        index_offset_(index_offset),
+        page_offset_(page_offset),
+        parsed_(false),
+        valid_(false),
+        supports_replacements_(false) {
+  }
+
+  // Returns true if this URL supports replacement.
+  bool SupportsReplacement() const;
+
+  // Returns a string that is the result of replacing the search terms in
+  // the url with the specified value.
+  //
+  // If this TemplateURLRef does not support replacement (SupportsReplacement
+  // returns false), an empty string is returned.
+  //
+  // The TemplateURL is used to determine the input encoding for the term.
+  GURL ReplaceSearchTerms(
+      const TemplateURL& host,
+      const std::wstring& terms,
+      int accepted_suggestion,
+      const std::wstring& original_query_for_suggestion) const;
+
+  // Returns the raw URL. None of the parameters will have been replaced.
+  const std::wstring& url() const { return url_; }
+
+  // Returns the index number of the first search result.
+  int index_offset() const { return index_offset_; }
+
+  // Returns the page number of the first search results.
+  int page_offset() const { return page_offset_; }
+
+  // Returns true if the TemplateURLRef is valid. An invalid TemplateURLRef is
+  // one that contains unknown terms, or invalid characters.
+  bool IsValid() const;
+
+  // Returns a string representation of this TemplateURLRef suitable for
+  // display. The display format is the same as the format used by Firefox.
+  std::wstring DisplayURL() const;
+
+  // Converts a string as returned by DisplayURL back into a string as
+  // understood by TemplateURLRef.
+  static std::wstring DisplayURLToURLRef(const std::wstring& display_url);
+
+  // If this TemplateURLRef is valid and contains one search term, this returns
+  // the host/path of the URL, otherwise this returns an empty string.
+  const std::string& GetHost() const;
+  const std::string& GetPath() const;
+
+  // If this TemplateURLRef is valid and contains one search term, this returns
+  // the key of the search term, otherwise this returns an empty string.
+  const std::string& GetSearchTermKey() const;
+
+  // Converts the specified term in the encoding of the host TemplateURL to a
+  // wide string.
+  std::wstring SearchTermToWide(const TemplateURL& host,
+                                const std::string& term) const;
+
+  // Returns true if this TemplateURLRef has a replacement term of
+  // {google:baseURL} or {google:baseSuggestURL}.
+  bool HasGoogleBaseURLs() const;
+
+ private:
+  friend class TemplateURL;
+  friend class TemplateURLModelTest;
+  friend class TemplateURLTest;
+
+  // Enumeration of the known types.
+  enum ReplacementType {
+    ENCODING,
+    GOOGLE_ACCEPTED_SUGGESTION,
+    GOOGLE_BASE_URL,
+    GOOGLE_BASE_SUGGEST_URL,
+    GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION,
+    GOOGLE_RLZ,
+    GOOGLE_UNESCAPED_SEARCH_TERMS,
+    LANGUAGE,
+    SEARCH_TERMS,
+  };
+
+  // Used to identify an element of the raw url that can be replaced.
+  struct Replacement {
+    Replacement(ReplacementType type, int index) : type(type), index(index) {}
+    ReplacementType type;
+    int index;
+  };
+
+  // The list of elements to replace.
+  typedef std::vector<struct Replacement> Replacements;
+
+  // TemplateURLRef internally caches values to make replacement quick. This
+  // method invalidates any cached values.
+  void InvalidateCachedValues() const;
+
+  // Resets the url.
+  void Set(const std::wstring& url, int index_offset, int page_offset);
+
+  // Parses the parameter in url at the specified offset. start/end specify the
+  // range of the parameter in the url, including the braces. If the parameter
+  // is valid, url is updated to reflect the appropriate parameter. If
+  // the parameter is one of the known parameters an element is added to
+  // replacements indicating the type and range of the element.
+  //
+  // If the parameter is not a known parameter, false is returned.
+  bool ParseParameter(size_t start,
+                      size_t end,
+                      std::wstring* url,
+                      Replacements* replacements) const;
+
+  // Parses the specified url, replacing parameters as necessary. If
+  // successful, valid is set to true, and the parsed url is returned. For all
+  // known parameters that are encountered an entry is added to replacements.
+  // If there is an error parsing (unknown parameter, or bogus url), valid is
+  // set to false, and an empty string is returned.
+  std::wstring ParseURL(const std::wstring& url,
+                        Replacements* replacements,
+                        bool* valid) const;
+
+  // If the url has not yet been parsed, ParseURL is invoked.
+  // NOTE: While this is const, it modifies parsed_, valid_, parsed_url_ and
+  // search_offset_.
+  void ParseIfNecessary() const;
+
+  // Extracts the query key and host from the url.
+  void ParseHostAndSearchTermKey() const;
+
+  // Returns the value for the GOOGLE_BASE_URL term.
+  static std::wstring GoogleBaseURLValue();
+
+  // Returns the value for the GOOGLE_BASE_SUGGEST_URL term.
+  static std::wstring GoogleBaseSuggestURLValue();
+
+  // The raw URL. Where as this contains all the terms (such as {searchTerms}),
+  // parsed_url_ has them all stripped out.
+  std::wstring url_;
+
+  // indexOffset defined for the Url element.
+  int index_offset_;
+
+  // searchOffset defined for the Url element.
+  int page_offset_;
+
+  // Whether the URL has been parsed.
+  mutable bool parsed_;
+
+  // Whether the url was successfully parsed.
+  mutable bool valid_;
+
+  // The parsed URL. All terms have been stripped out of this with
+  // replacements_ giving the index of the terms to replace.
+  mutable std::wstring parsed_url_;
+
+  // Do we support replacement?
+  mutable bool supports_replacements_;
+
+  // The replaceable parts of url (parsed_url_). These are ordered by index
+  // into the string, and may be empty.
+  mutable Replacements replacements_;
+
+  // Host, path and key of the search term. These are only set if the url
+  // contains one search term.
+  mutable std::string host_;
+  mutable std::string path_;
+  mutable std::string search_term_key_;
+
+  // For testing. If non-null this is the replacement value for GOOGLE_BASE_URL
+  // terms.
+  static std::wstring* google_base_url_;
+};
+
+// Describes the relevant portions of a single OSD document.
+class TemplateURL {
+ public:
+  typedef int64 IDType;
+
+  // Describes a single image reference. Each TemplateURL may have
+  // any number (including 0) of ImageRefs.
+  //
+  // If a TemplateURL has no images, the favicon for the generated URL
+  // should be used.
+  struct ImageRef {
+    ImageRef(const std::wstring& type, int width, int height)
+        : type(type), width(width), height(height) {
+    }
+
+    ImageRef(const std::wstring& type, int width, int height, const GURL& url)
+      : type(type), width(width), height(height), url(url) {
+    }
+
+    // Mime type for the image.
+    // ICO image will have the format: image/x-icon or image/vnd.microsoft.icon
+    std::wstring type;
+
+    // Size of the image
+    int width;
+    int height;
+
+    // URL of the image.
+    GURL url;
+  };
+
+  // Generates a favicon URL from the specified url.
+  static GURL GenerateFaviconURL(const GURL& url);
+
+  TemplateURL()
+      : autogenerate_keyword_(false),
+        show_in_default_list_(false),
+        safe_for_autoreplace_(false),
+        id_(0),
+        date_created_(base::Time::Now()),
+        usage_count_(0),
+        prepopulate_id_(0) {}
+  ~TemplateURL() {}
+
+  // A short description of the template. This is the name we show to the user
+  // in various places that use keywords. For example, the location bar shows
+  // this when the user selects the keyword.
+  void set_short_name(const std::wstring& short_name) {
+    short_name_ = short_name;
+  }
+  const std::wstring& short_name() const { return short_name_; }
+
+  // A description of the template; this may be empty.
+  void set_description(const std::wstring& description) {
+    description_ = description;
+  }
+  const std::wstring& description() const { return description_; }
+
+  // URL providing JSON results. This is typically used to provide suggestions
+  // as your type. If NULL, this url does not support suggestions.
+  // Be sure and check the resulting TemplateURLRef for SupportsReplacement
+  // before using.
+  void SetSuggestionsURL(const std::wstring& suggestions_url,
+                         int index_offset,
+                         int page_offset);
+  const TemplateURLRef* suggestions_url() const {
+    if (suggestions_url_.url().empty())
+      return NULL;
+    return &suggestions_url_;
+  }
+
+  // Parameterized URL for providing the results. This may be NULL.
+  // Be sure and check the resulting TemplateURLRef for SupportsReplacement
+  // before using.
+  void SetURL(const std::wstring& url, int index_offset, int page_offset);
+  // Returns the TemplateURLRef that may be used for search results. This
+  // returns NULL if a url element was not specified.
+  const TemplateURLRef* url() const {
+    if (url_.url().empty())
+      return NULL;
+    return &url_;
+  }
+
+  // URL to the OSD file this came from. May be empty.
+  void set_originating_url(const GURL& url) {
+    originating_url_ = url;
+  }
+  const GURL& originating_url() const { return originating_url_; }
+
+  // The shortcut for this template url. May be empty.
+  void set_keyword(const std::wstring& keyword);
+  const std::wstring& keyword() const;
+
+  // Whether to autogenerate a keyword from the url() in GetKeyword().  Most
+  // consumers should not need this.
+  // NOTE: Calling set_keyword() turns this back off.  Manual and automatic
+  // keywords are mutually exclusive.
+  void set_autogenerate_keyword(bool autogenerate_keyword) {
+    autogenerate_keyword_ = autogenerate_keyword;
+    if (autogenerate_keyword_)
+      keyword_.clear();
+  }
+  bool autogenerate_keyword() const {
+    return autogenerate_keyword_;
+  }
+
+  // Whether this keyword is shown in the default list of search providers. This
+  // is just a property and does not indicate whether this TemplateURL has
+  // a TemplateURLRef that supports replacement. Use ShowInDefaultList to
+  // test both.
+  // The default value is false.
+  void set_show_in_default_list(bool show_in_default_list) {
+    show_in_default_list_ = show_in_default_list;
+  }
+  bool show_in_default_list() const { return show_in_default_list_; }
+
+  // Returns true if show_in_default_list() is true and this TemplateURL has a
+  // TemplateURLRef that supports replacement.
+  bool ShowInDefaultList() const;
+
+  // Whether it's safe for auto-modification code (the autogenerator and the
+  // code that imports data from other browsers) to replace the TemplateURL.
+  // This should be set to false for any keyword the user edits, or any keyword
+  // that the user clearly manually edited in the past, like a bookmark keyword
+  // from another browser.
+  void set_safe_for_autoreplace(bool safe_for_autoreplace) {
+    safe_for_autoreplace_ = safe_for_autoreplace;
+  }
+  bool safe_for_autoreplace() const { return safe_for_autoreplace_; }
+
+  // Images for this URL. May be empty.
+  void add_image_ref(const ImageRef& ref) { image_refs_.push_back(ref); }
+  const std::vector<ImageRef>& image_refs() const { return image_refs_; }
+
+  // Convenience methods for getting/setting an ImageRef that points to a
+  // favicon. A TemplateURL need not have an ImageRef for a favicon. In such
+  // a situation GetFavIconURL returns an invalid url.
+  //
+  // If url is empty and there is an image ref for a favicon, it is removed.
+  void SetFavIconURL(const GURL& url);
+  GURL GetFavIconURL() const;
+
+  // Set of languages supported. This may be empty.
+  void add_language(const std::wstring& language) {
+    languages_.push_back(language);
+  }
+  const std::vector<std::wstring>& languages() const { return languages_; }
+
+  // Date this keyword was created.
+  //
+  // NOTE: this may be 0, which indicates the keyword was created before we
+  // started tracking creation time.
+  void set_date_created(base::Time time) { date_created_ = time; }
+  base::Time date_created() const { return date_created_; }
+
+  // Number of times this keyword has been explicitly used to load a URL.  We
+  // don't increment this for uses as the "default search engine" since that's
+  // not really "explicit" usage and incrementing would result in pinning the
+  // user's default search engine(s) to the top of the list of searches on the
+  // New Tab page, de-emphasizing the omnibox as "where you go to search".
+  void set_usage_count(int count) { usage_count_ = count; }
+  int usage_count() const { return usage_count_; }
+
+  // The list of supported encodings for the search terms. This may be empty,
+  // which indicates the terms should be encoded with UTF-8.
+  void set_input_encodings(const std::vector<std::string>& encodings) {
+    input_encodings_ = encodings;
+  }
+  void add_input_encoding(const std::string& encoding) {
+    input_encodings_.push_back(encoding);
+  }
+  const std::vector<std::string>& input_encodings() const {
+    return input_encodings_;
+  }
+
+  // Returns the unique identifier of this TemplateURL. The unique ID is set
+  // by the TemplateURLModel when the TemplateURL is added to it.
+  IDType id() const { return id_; }
+
+  // If this TemplateURL comes from prepopulated data the prepopulate_id is > 0.
+  void set_prepopulate_id(int id) { prepopulate_id_ = id; }
+  int prepopulate_id() const { return prepopulate_id_; }
+
+ private:
+  friend class WebDatabaseTest;
+  friend class WebDatabase;
+  friend class TemplateURLModel;
+
+  // Invalidates cached values on this object and its child TemplateURLRefs.
+  void InvalidateCachedValues() const;
+
+  // Unique identifier, used when archived to the database.
+  void set_id(IDType id) { id_ = id;}
+
+  std::wstring short_name_;
+  std::wstring description_;
+  TemplateURLRef suggestions_url_;
+  TemplateURLRef url_;
+  GURL originating_url_;
+  mutable std::wstring keyword_;
+  bool autogenerate_keyword_;  // If this is set, |keyword_| holds the cached
+                               // generated keyword if available.
+  bool show_in_default_list_;
+  bool safe_for_autoreplace_;
+  std::vector<ImageRef> image_refs_;
+  std::vector<std::wstring> languages_;
+  // List of supported input encodings.
+  std::vector<std::string> input_encodings_;
+  IDType id_;
+  base::Time date_created_;
+  int usage_count_;
+  int prepopulate_id_;
+
+  // TODO(sky): Add date last parsed OSD file.
+};
+
+#endif // CHROME_BROWSER_TEMPLATE_URL_PARSER_H__
+
diff --git a/chrome/browser/template_url_fetcher.cc b/chrome/browser/template_url_fetcher.cc
new file mode 100644
index 0000000..57bb259
--- /dev/null
+++ b/chrome/browser/template_url_fetcher.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/template_url_fetcher.h"
+
+#include "chrome/browser/profile.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
+#include "chrome/browser/template_url_parser.h"
+#include "chrome/browser/views/edit_keyword_controller.h"
+
+// RequestDelegate ------------------------------------------------------------
+
+void TemplateURLFetcher::RequestDelegate::OnURLFetchComplete(
+    const URLFetcher* source,
+    const GURL& url,
+    const URLRequestStatus& status,
+    int response_code,
+    const ResponseCookies& cookies,
+    const std::string& data) {
+  // Make sure we can still replace the keyword.
+  if (response_code != 200) {
+    fetcher_->RequestCompleted(this);
+    // WARNING: RequestCompleted deletes us.
+    return;
+  }
+
+  scoped_ptr<TemplateURL> template_url(new TemplateURL());
+  if (TemplateURLParser::Parse(
+          reinterpret_cast<const unsigned char*>(data.c_str()),
+                                                 data.length(),
+                                                 NULL,
+                                                 template_url.get()) &&
+      template_url->url() && template_url->url()->SupportsReplacement()) {
+    TemplateURLModel* model = fetcher_->profile()->GetTemplateURLModel();
+    const TemplateURL* existing_url;
+    if (!model || !model->loaded() ||
+        !model->CanReplaceKeyword(keyword_, template_url->url()->url(),
+                                  &existing_url)) {
+      // TODO(pamg): If we're coming from JS (not autodetected) and this URL
+      // already exists in the model, consider bringing up the
+      // EditKeywordController to edit it.  This would be helpful feedback in
+      // the case of clicking a button twice, and annoying in the case of a
+      // page that calls AddSearchProvider() in JS without a user action.
+      fetcher_->RequestCompleted(this);
+      // WARNING: RequestCompleted deletes us.
+      return;
+    }
+
+    if (existing_url)
+      model->Remove(existing_url);
+
+    // The short name is what is shown to the user. We reset it to make sure
+    // we don't display random text from the web.
+    template_url->set_short_name(keyword_);
+    template_url->set_keyword(keyword_);
+    template_url->set_originating_url(osdd_url_);
+
+    // The page may have specified a URL to use for favicons, if not, set it.
+    if (!template_url->GetFavIconURL().is_valid())
+      template_url->SetFavIconURL(favicon_url_);
+
+    if (autodetected_) {
+      // Mark the keyword as replaceable so it can be removed if necessary.
+      template_url->set_safe_for_autoreplace(true);
+      model->Add(template_url.release());
+    } else {
+      // Confirm addition and allow user to edit default choices. It's ironic
+      // that only *non*-autodetected additions get confirmed, but the user
+      // expects feedback that his action did something.
+      // The edit controller will take care of adding the URL to the model,
+      // which takes ownership, or of deleting it if the add is cancelled.
+      EditKeywordController* controller =
+          new EditKeywordController(parent_window_,
+                                    template_url.release(),
+                                    NULL,  // no KeywordEditorView
+                                    fetcher_->profile());
+      controller->Show();
+    }
+  }
+  fetcher_->RequestCompleted(this);
+  // WARNING: RequestCompleted deletes us.
+}
+
+// TemplateURLFetcher ---------------------------------------------------------
+
+TemplateURLFetcher::TemplateURLFetcher(Profile* profile) : profile_(profile) {
+  DCHECK(profile_);
+}
+
+void TemplateURLFetcher::ScheduleDownload(const std::wstring& keyword,
+                                          const GURL& osdd_url,
+                                          const GURL& favicon_url,
+                                          const HWND parent_window,
+                                          bool autodetected) {
+  DCHECK(!keyword.empty() && osdd_url.is_valid());
+  // Make sure we aren't already downloading this request.
+  for (std::vector<RequestDelegate*>::iterator i = requests_->begin();
+       i != requests_->end(); ++i) {
+    if ((*i)->url() == osdd_url || (*i)->keyword() == keyword)
+      return;
+  }
+
+  requests_->push_back(
+      new RequestDelegate(this, keyword, osdd_url, favicon_url, parent_window,
+                          autodetected));
+}
+
+void TemplateURLFetcher::RequestCompleted(RequestDelegate* request) {
+  DCHECK(find(requests_->begin(), requests_->end(), request) !=
+         requests_->end());
+  requests_->erase(find(requests_->begin(), requests_->end(), request));
+  delete request;
+}
+
diff --git a/chrome/browser/template_url_fetcher.h b/chrome/browser/template_url_fetcher.h
new file mode 100644
index 0000000..a07277f
--- /dev/null
+++ b/chrome/browser/template_url_fetcher.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_TEMPLATE_URL_FETCHER_H__
+#define CHROME_BROWSER_TEMPLATE_URL_FETCHER_H__
+
+#include "chrome/browser/profile.h"
+#include "chrome/browser/url_fetcher.h"
+#include "chrome/common/scoped_vector.h"
+
+class GURL;
+class Profile;
+class TemplateURL;
+class WebContents;
+
+// TemplateURLFetcher is responsible for downloading OpenSearch description
+// documents, creating a TemplateURL from the OSDD, and adding the TemplateURL
+// to the TemplateURLModel. Downloading is done in the background.
+//
+class TemplateURLFetcher {
+ public:
+  // Creates a TemplateURLFetcher with the specified Profile.
+  explicit TemplateURLFetcher(Profile* profile);
+
+  // If TemplateURLFetcher is not already downloading the OSDD for osdd_url,
+  // it is downloaded. If successful and the result can be parsed, a TemplateURL
+  // is added to the TemplateURLModel.
+  void ScheduleDownload(const std::wstring& keyword,
+                        const GURL& osdd_url,
+                        const GURL& favicon_url,
+                        const HWND parent_window,
+                        bool autodetected);
+
+ private:
+  friend class RequestDelegate;
+
+  // A RequestDelegate is created to download each OSDD. When done downloading
+  // RequestCompleted is invoked back on the TemplateURLFetcher.
+  class RequestDelegate : public URLFetcher::Delegate {
+   public:
+    RequestDelegate(TemplateURLFetcher* fetcher,
+                    const std::wstring& keyword,
+                    const GURL& osdd_url,
+                    const GURL& favicon_url,
+                    const HWND parent_window,
+                    bool autodetected)
+#pragma warning(disable:4355)
+        : url_fetcher_(osdd_url, URLFetcher::GET, this),
+          fetcher_(fetcher),
+          keyword_(keyword),
+          osdd_url_(osdd_url),
+          favicon_url_(favicon_url),
+          parent_window_(parent_window),
+          autodetected_(autodetected) {
+      url_fetcher_.set_request_context(fetcher->profile()->GetRequestContext());
+      url_fetcher_.Start();
+    }
+
+    // If data contains a valid OSDD, a TemplateURL is created and added to
+    // the TemplateURLModel.
+    virtual void OnURLFetchComplete(const URLFetcher* source,
+                                    const GURL& url,
+                                    const URLRequestStatus& status,
+                                    int response_code,
+                                    const ResponseCookies& cookies,
+                                    const std::string& data);
+
+    // URL of the OSDD.
+    const GURL& url() const { return osdd_url_; }
+
+    // Keyword to use.
+    const std::wstring keyword() const { return keyword_; }
+
+   private:
+    URLFetcher url_fetcher_;
+    TemplateURLFetcher* fetcher_;
+    const std::wstring keyword_;
+    const GURL osdd_url_;
+    const GURL favicon_url_;
+    bool autodetected_;
+
+    // Used to determine where to place a confirmation dialog. May be NULL,
+    // in which case the confirmation will be centered in the screen if needed.
+    const HWND parent_window_;
+
+    DISALLOW_EVIL_CONSTRUCTORS(RequestDelegate);
+  };
+
+  Profile* profile() const { return profile_; }
+
+  // Invoked from the RequestDelegate when done downloading.
+  void RequestCompleted(RequestDelegate* request);
+
+  Profile* profile_;
+
+  // In progress requests.
+  ScopedVector<RequestDelegate> requests_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(TemplateURLFetcher);
+};
+
+#endif  // CHROME_BROWSER_OSDD_FETCHER_H__
+
diff --git a/chrome/browser/template_url_model.cc b/chrome/browser/template_url_model.cc
new file mode 100644
index 0000000..a3ffd5f
--- /dev/null
+++ b/chrome/browser/template_url_model.cc
@@ -0,0 +1,980 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/template_url_model.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/google_url_tracker.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/rlz/rlz.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_prepopulate_data.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/stl_util-inl.h"
+#include "googleurl/src/gurl.h"
+#include "googleurl/src/url_parse.h"
+#include "net/base/net_util.h"
+#include "unicode/rbbi.h"
+#include "unicode/uchar.h"
+
+using base::Time;
+
+// String in the URL that is replaced by the search term.
+static const wchar_t kSearchTermParameter[] = L"{searchTerms}";
+
+// String in Initializer that is replaced with kSearchTermParameter.
+static const wchar_t kTemplateParameter[] = L"%s";
+
+// Term used when generating a search url. Use something obscure so that on
+// the rare case the term replaces the URL it's unlikely another keyword would
+// have the same url.
+static const wchar_t kReplacementTerm[] = L"blah.blah.blah.blah.blah";
+
+class TemplateURLModel::LessWithPrefix {
+ public:
+  // We want to find the set of keywords that begin with a prefix.  The STL
+  // algorithms will return the set of elements that are "equal to" the
+  // prefix, where "equal(x, y)" means "!(cmp(x, y) || cmp(y, x))".  When
+  // cmp() is the typical std::less<>, this results in lexicographic equality;
+  // we need to extend this to mark a prefix as "not less than" a keyword it
+  // begins, which will cause the desired elements to be considered "equal to"
+  // the prefix.  Note: this is still a strict weak ordering, as required by
+  // equal_range() (though I will not prove that here).
+  //
+  // Unfortunately the calling convention is not "prefix and element" but
+  // rather "two elements", so we pass the prefix as a fake "element" which has
+  // a NULL KeywordDataElement pointer.
+  bool operator()(const KeywordToTemplateMap::value_type& elem1,
+                  const KeywordToTemplateMap::value_type& elem2) const {
+    return (elem1.second == NULL) ?
+        (elem2.first.compare(0, elem1.first.length(), elem1.first) > 0) :
+        (elem1.first < elem2.first);
+  }
+};
+
+TemplateURLModel::TemplateURLModel(Profile* profile)
+    : profile_(profile),
+      loaded_(false),
+      load_handle_(0),
+      default_search_provider_(NULL),
+      next_id_(1) {
+  DCHECK(profile_);
+  Init(NULL, 0);
+}
+
+TemplateURLModel::TemplateURLModel(const Initializer* initializers,
+                                   const int count)
+    : profile_(NULL),
+      loaded_(true),
+      load_handle_(0),
+      service_(NULL),
+      default_search_provider_(NULL),
+      next_id_(1) {
+  Init(initializers, count);
+}
+
+TemplateURLModel::~TemplateURLModel() {
+  if (load_handle_) {
+    DCHECK(service_.get());
+    service_->CancelRequest(load_handle_);
+  }
+
+  STLDeleteElements(&template_urls_);
+
+  NotificationService* ns = NotificationService::current();
+  if (profile_) {
+    ns->RemoveObserver(this, NOTIFY_HISTORY_URL_VISITED,
+                       Source<Profile>(profile_->GetOriginalProfile()));
+  }
+  ns->RemoveObserver(this, NOTIFY_GOOGLE_URL_UPDATED,
+                     NotificationService::AllSources());
+}
+
+void TemplateURLModel::Init(const Initializer* initializers,
+                            int num_initializers) {
+  // Register for notifications.
+  NotificationService* ns = NotificationService::current();
+  if (profile_) {
+    // TODO(sky): bug 1166191. The keywords should be moved into the history
+    // db, which will mean we no longer need this notification and the history
+    // backend can handle automatically adding the search terms as the user
+    // navigates.
+    ns->AddObserver(this, NOTIFY_HISTORY_URL_VISITED,
+                    Source<Profile>(profile_->GetOriginalProfile()));
+  }
+  ns->AddObserver(this, NOTIFY_GOOGLE_URL_UPDATED,
+                  NotificationService::AllSources());
+
+  // Add specific initializers, if any.
+  for (int i(0); i < num_initializers; ++i) {
+    DCHECK(initializers[i].keyword);
+    DCHECK(initializers[i].url);
+    DCHECK(initializers[i].content);
+
+    size_t template_position =
+        std::wstring(initializers[i].url).find(kTemplateParameter);
+    DCHECK(template_position != std::wstring::npos);
+    std::wstring osd_url(initializers[i].url);
+    osd_url.replace(template_position, arraysize(kTemplateParameter) - 1,
+                    kSearchTermParameter);
+
+    // TemplateURLModel ends up owning the TemplateURL, don't try and free it.
+    TemplateURL* template_url = new TemplateURL();
+    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);
+  }
+
+  // Request a server check for the correct Google URL if Google is the default
+  // search engine.
+  const TemplateURL* default_provider = GetDefaultSearchProvider();
+  if (default_provider) {
+    const TemplateURLRef* default_provider_ref = default_provider->url();
+    if (default_provider_ref && default_provider_ref->HasGoogleBaseURLs())
+      GoogleURLTracker::RequestServerCheck();
+  }
+}
+
+// static
+std::wstring TemplateURLModel::GenerateKeyword(const GURL& url,
+                                               bool autodetected) {
+  // Don't autogenerate keywords for referrers that are the result of a form
+  // submission (TODO: right now we approximate this by checking for the URL
+  // having a query, but we should replace this with a call to WebCore to see if
+  // the originating page was actually a form submission), anything other than
+  // http, or referrers with a path.
+  //
+  // If we relax the path constraint, we need to be sure to sanitize the path
+  // elements and update AutocompletePopup to look for keywords using the path.
+  // See http://b/issue?id=863583.
+  if (!url.is_valid() ||
+      (autodetected && (url.has_query() || (url.scheme() != "http") ||
+                        ((url.path() != "") && (url.path() != "/")))))
+    return std::wstring();
+
+  // Strip "www." off the front of the keyword; otherwise the keyword won't work
+  // properly.  See http://b/issue?id=1205573.
+  return net::StripWWW(UTF8ToWide(url.host()));
+}
+
+// static
+std::wstring TemplateURLModel::CleanUserInputKeyword(
+    const std::wstring& keyword) {
+  // Remove the scheme.
+  std::wstring result(l10n_util::ToLower(keyword));
+  url_parse::Component scheme_component;
+  if (url_parse::ExtractScheme(WideToUTF8(keyword).c_str(),
+                               static_cast<int>(keyword.length()),
+                               &scheme_component)) {
+    // Include trailing ':'.
+    result.erase(0, scheme_component.end() + 1);
+    // Many schemes usually have "//" after them, so strip it too.
+    const std::wstring after_scheme(L"//");
+    if (result.compare(0, after_scheme.length(), after_scheme) == 0)
+      result.erase(0, after_scheme.length());
+  }
+
+  // Remove leading "www.".
+  result = net::StripWWW(result);
+
+  // Remove trailing "/".
+  return (result.length() > 0 && result[result.length() - 1] == L'/') ?
+      result.substr(0, result.length() - 1) : result;
+}
+
+// static
+GURL TemplateURLModel::GenerateSearchURL(const TemplateURL* t_url) {
+  DCHECK(t_url);
+  const TemplateURLRef* search_ref = t_url->url();
+  if (!search_ref || !search_ref->IsValid())
+    return GURL();
+
+  if (!search_ref->SupportsReplacement())
+    return GURL(WideToUTF8(search_ref->url()));
+
+  return search_ref->ReplaceSearchTerms(
+      *t_url,
+      kReplacementTerm,
+      TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring());
+}
+
+bool TemplateURLModel::CanReplaceKeyword(
+    const std::wstring& keyword,
+    const std::wstring& 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.
+  const TemplateURL* existing_url = GetTemplateURLForKeyword(keyword);
+  if (existing_url) {
+    // We already have a TemplateURL for this keyword. Only allow it to be
+    // replaced if the TemplateURL can be replaced.
+    if (template_url_to_replace)
+      *template_url_to_replace = existing_url;
+    return CanReplace(existing_url);
+  }
+
+  // We don't have a TemplateURL with keyword. Only allow a new one if there
+  // isn't a TemplateURL for the specified host, or there is one but it can
+  // be replaced. We do this to ensure that if the user assigns a different
+  // keyword to a generated TemplateURL, we won't regenerate another keyword for
+  // the same host.
+  GURL gurl(WideToUTF8(url));
+  if (gurl.is_valid() && !gurl.host().empty())
+    return CanReplaceKeywordForHost(gurl.host(), template_url_to_replace);
+  return true;
+}
+
+void TemplateURLModel::FindMatchingKeywords(
+    const std::wstring& prefix,
+    bool support_replacement_only,
+    std::vector<std::wstring>* matches) const {
+  // Sanity check args.
+  if (prefix.empty())
+    return;
+  DCHECK(matches != NULL);
+  DCHECK(matches->empty());  // The code for exact matches assumes this.
+
+  // Find matching keyword range.  Searches the element map for keywords
+  // beginning with |prefix| and stores the endpoints of the resulting set in
+  // |match_range|.
+  const std::pair<KeywordToTemplateMap::const_iterator,
+                  KeywordToTemplateMap::const_iterator> match_range(
+      std::equal_range(
+          keyword_to_template_map_.begin(), keyword_to_template_map_.end(),
+          KeywordToTemplateMap::value_type(prefix, NULL), LessWithPrefix()));
+
+  // Return vector of matching keywords.
+  for (KeywordToTemplateMap::const_iterator i(match_range.first);
+       i != match_range.second; ++i) {
+    DCHECK(i->second->url());
+    if (!support_replacement_only || i->second->url()->SupportsReplacement())
+      matches->push_back(i->first);
+  }
+}
+
+const TemplateURL* TemplateURLModel::GetTemplateURLForKeyword(
+                                     const std::wstring& keyword) const {
+  KeywordToTemplateMap::const_iterator elem(
+      keyword_to_template_map_.find(keyword));
+  return (elem == keyword_to_template_map_.end()) ? NULL : elem->second;
+}
+
+const TemplateURL* TemplateURLModel::GetTemplateURLForHost(
+    const std::string& host) const {
+  HostToURLsMap::const_iterator iter = host_to_urls_map_.find(host);
+  if (iter == host_to_urls_map_.end() || iter->second.empty())
+    return NULL;
+  return *(iter->second.begin());  // Return the 1st element.
+}
+
+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());
+  }
+}
+
+void TemplateURLModel::AddToMaps(const TemplateURL* template_url) {
+  if (!template_url->keyword().empty())
+    keyword_to_template_map_[template_url->keyword()] = template_url;
+
+  const GURL url(GenerateSearchURL(template_url));
+  if (url.is_valid() && url.has_host())
+    host_to_urls_map_[url.host()].insert(template_url);
+}
+
+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;
+}
+
+void TemplateURLModel::Replace(const TemplateURL* existing_turl,
+                               TemplateURL* new_turl) {
+  DCHECK(existing_turl && new_turl);
+
+  TemplateURLVector::iterator i = find(template_urls_.begin(),
+                                       template_urls_.end(),
+                                       existing_turl);
+  DCHECK(i != template_urls_.end());
+  RemoveFromMaps(existing_turl);
+  template_urls_.erase(i);
+
+  new_turl->set_id(existing_turl->id());
+
+  template_urls_.push_back(new_turl);
+  AddToMaps(new_turl);
+
+  if (service_.get())
+    service_->UpdateKeyword(*new_turl);
+
+  if (default_search_provider_ == existing_turl)
+    SetDefaultSearchProvider(new_turl);
+
+  if (loaded_) {
+    FOR_EACH_OBSERVER(TemplateURLModelObserver, model_observers_,
+                      OnTemplateURLModelChanged());
+  }
+
+  delete existing_turl;
+}
+
+void TemplateURLModel::RemoveAutoGeneratedBetween(Time created_after,
+                                                  Time created_before) {
+  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]);
+    } else {
+      ++i;
+    }
+  }
+}
+
+void TemplateURLModel::RemoveAutoGeneratedSince(Time created_after) {
+  RemoveAutoGeneratedBetween(created_after, Time());
+}
+
+void TemplateURLModel::SetKeywordSearchTermsForURL(const TemplateURL* t_url,
+                                                   const GURL& url,
+                                                   const std::wstring& term) {
+  HistoryService* history = profile_  ?
+      profile_->GetHistoryService(Profile::EXPLICIT_ACCESS) : NULL;
+  if (!history)
+    return;
+  history->SetKeywordSearchTermsForURL(url, t_url->id(), term);
+}
+
+void TemplateURLModel::RemoveFromMaps(const TemplateURL* template_url) {
+  if (!template_url->keyword().empty()) {
+    keyword_to_template_map_.erase(template_url->keyword());
+  }
+
+  const GURL url(GenerateSearchURL(template_url));
+  if (url.is_valid() && url.has_host()) {
+    const std::string host(url.host());
+    DCHECK(host_to_urls_map_.find(host) != host_to_urls_map_.end());
+    TemplateURLSet& urls = host_to_urls_map_[host];
+    DCHECK(urls.find(template_url) != urls.end());
+    urls.erase(urls.find(template_url));
+    if (urls.empty())
+      host_to_urls_map_.erase(host_to_urls_map_.find(host));
+  }
+}
+
+void TemplateURLModel::RemoveFromMapsByPointer(
+    const TemplateURL* template_url) {
+  DCHECK(template_url);
+  for (KeywordToTemplateMap::iterator i = keyword_to_template_map_.begin();
+       i != keyword_to_template_map_.end(); ++i) {
+    if (i->second == template_url) {
+      keyword_to_template_map_.erase(i);
+      // A given TemplateURL only occurs once in the map. As soon as we find the
+      // entry, stop.
+      break;
+    }
+  }
+
+  for (HostToURLsMap::iterator i = host_to_urls_map_.begin();
+       i != host_to_urls_map_.end(); ++i) {
+    TemplateURLSet::iterator url_set_iterator = i->second.find(template_url);
+    if (url_set_iterator != i->second.end()) {
+      i->second.erase(url_set_iterator);
+      if (i->second.empty())
+        host_to_urls_map_.erase(i);
+      // A given TemplateURL only occurs once in the map. As soon as we find the
+      // entry, stop.
+      return;
+    }
+  }
+}
+
+void TemplateURLModel::SetTemplateURLs(
+      const std::vector<const TemplateURL*>& urls) {
+  DCHECK(template_urls_.empty()); // This should only be called on load,
+                                  // when we have no TemplateURLs.
+
+  // Add mappings for the new items.
+  for (TemplateURLVector::const_iterator i = urls.begin(); i != urls.end();
+       ++i) {
+    next_id_ = std::max(next_id_, (*i)->id());
+    AddToMaps(*i);
+  }
+
+  template_urls_ = urls;
+}
+
+std::vector<const TemplateURL*> TemplateURLModel::GetTemplateURLs() const {
+  return template_urls_;
+}
+
+void TemplateURLModel::IncrementUsageCount(const TemplateURL* url) {
+  DCHECK(url && find(template_urls_.begin(), template_urls_.end(), url) !=
+         template_urls_.end());
+  const_cast<TemplateURL*>(url)->set_usage_count(url->usage_count() + 1);
+  if (service_.get())
+    service_.get()->UpdateKeyword(*url);
+}
+
+void TemplateURLModel::ResetTemplateURL(const TemplateURL* url,
+                                        const std::wstring& title,
+                                        const std::wstring& keyword,
+                                        const std::wstring& search_url) {
+  DCHECK(url && find(template_urls_.begin(), template_urls_.end(), url) !=
+         template_urls_.end());
+  RemoveFromMaps(url);
+  TemplateURL* modifiable_url = const_cast<TemplateURL*>(url);
+  modifiable_url->set_short_name(title);
+  modifiable_url->set_keyword(keyword);
+  if ((modifiable_url->url() && search_url.empty()) ||
+      (!modifiable_url->url() && !search_url.empty()) ||
+      (modifiable_url->url() && modifiable_url->url()->url() != search_url)) {
+    // The urls have changed, reset the favicon url.
+    modifiable_url->SetFavIconURL(GURL());
+    modifiable_url->SetURL(search_url, 0, 0);
+  }
+  modifiable_url->set_safe_for_autoreplace(false);
+  AddToMaps(url);
+  if (service_.get())
+    service_.get()->UpdateKeyword(*url);
+
+  FOR_EACH_OBSERVER(TemplateURLModelObserver, model_observers_,
+                    OnTemplateURLModelChanged());
+}
+
+void TemplateURLModel::SetDefaultSearchProvider(const TemplateURL* url) {
+  if (default_search_provider_ == url)
+    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();
+      RLZTracker::RecordProductEvent(RLZTracker::CHROME,
+                                     RLZTracker::CHROME_OMNIBOX,
+                                     RLZTracker::SET_TO_GOOGLE);
+    }
+  }
+
+  SaveDefaultSearchProviderToPrefs(url);
+
+  if (service_.get())
+    service_->SetDefaultSearchProvider(url);
+
+  if (loaded_) {
+    FOR_EACH_OBSERVER(TemplateURLModelObserver, model_observers_,
+                      OnTemplateURLModelChanged());
+  }
+}
+
+const TemplateURL* TemplateURLModel::GetDefaultSearchProvider() {
+  if (loaded_)
+    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();
+}
+
+void TemplateURLModel::AddObserver(TemplateURLModelObserver* observer) {
+  model_observers_.AddObserver(observer);
+}
+
+void TemplateURLModel::RemoveObserver(TemplateURLModelObserver* observer) {
+  model_observers_.RemoveObserver(observer);
+}
+
+void TemplateURLModel::Load() {
+  if (loaded_ || load_handle_)
+    return;
+
+  if (!service_.get())
+    service_ = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS);
+
+  if (service_.get()) {
+    load_handle_ = service_->GetKeywords(this);
+  } else {
+    loaded_ = true;
+    NotifyLoaded();
+  }
+}
+
+void TemplateURLModel::OnWebDataServiceRequestDone(
+                       WebDataService::Handle h,
+                       const WDTypedResult* result) {
+  // Reset the load_handle so that we don't try and cancel the load in
+  // the destructor.
+  load_handle_ = 0;
+
+  if (!result) {
+    // Results are null if the database went away.
+    loaded_ = true;
+    NotifyLoaded();
+    return;
+  }
+
+  DCHECK(result->GetType() == KEYWORDS_RESULT);
+
+  WDKeywordsResult keyword_result = reinterpret_cast<
+      const WDResult<WDKeywordsResult>*>(result)->GetValue();
+
+  // prefs_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();
+
+  // Compiler won't implicitly convert std::vector<TemplateURL*> to
+  // std::vector<const TemplateURL*>, and reinterpret_cast is unsafe,
+  // so we just copy it.
+  std::vector<const TemplateURL*> template_urls(keyword_result.keywords.begin(),
+                                                keyword_result.keywords.end());
+
+  const int resource_keyword_version =
+      TemplateURLPrepopulateData::GetDataVersion();
+  if (keyword_result.builtin_keyword_version != resource_keyword_version) {
+    // There should never be duplicate TemplateURLs. We had a bug such that
+    // duplicate TemplateURLs existed for one locale. As such we invoke
+    // RemoveDuplicatePrepopulateIDs to nuke the duplicates.
+    RemoveDuplicatePrepopulateIDs(&template_urls);
+  }
+  SetTemplateURLs(template_urls);
+
+  if (keyword_result.default_search_provider_id) {
+    // See if we can find the default search provider.
+    for (TemplateURLVector::iterator i = template_urls_.begin();
+         i != template_urls_.end(); ++i) {
+      if ((*i)->id() == keyword_result.default_search_provider_id) {
+        default_search_provider_ = *i;
+        break;
+      }
+    }
+  }
+
+  if (keyword_result.builtin_keyword_version != resource_keyword_version) {
+    MergeEnginesFromPrepopulateData();
+    service_->SetBuiltinKeywordVersion(resource_keyword_version);
+  }
+
+  // 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_);
+
+  // Delete any hosts that were deleted before we finished loading.
+  for (std::vector<std::wstring>::iterator i = hosts_to_delete_.begin();
+       i != hosts_to_delete_.end(); ++i) {
+    DeleteGeneratedKeywordsMatchingHost(*i);
+  }
+  hosts_to_delete_.clear();
+
+  // Index any visits that occurred before we finished loading.
+  for (size_t i = 0; i < visits_to_add_.size(); ++i)
+    UpdateKeywordSearchTermsForURL(visits_to_add_[i]);
+  visits_to_add_.clear();
+
+  loaded_ = true;
+
+  FOR_EACH_OBSERVER(TemplateURLModelObserver, model_observers_,
+                    OnTemplateURLModelChanged());
+
+  NotifyLoaded();
+}
+
+void TemplateURLModel::RemoveDuplicatePrepopulateIDs(
+    std::vector<const TemplateURL*>* urls) {
+  std::set<int> ids;
+  for (std::vector<const TemplateURL*>::iterator i = urls->begin();
+       i != urls->end(); ) {
+    int prepopulate_id = (*i)->prepopulate_id();
+    if (prepopulate_id) {
+      if (ids.find(prepopulate_id) != ids.end()) {
+        if (service_.get())
+          service_->RemoveKeyword(**i);
+        delete *i;
+        i = urls->erase(i);
+      } else {
+        ids.insert(prepopulate_id);
+        ++i;
+      }
+    } else {
+      ++i;
+    }
+  }
+}
+
+void TemplateURLModel::Observe(NotificationType type,
+                               const NotificationSource& source,
+                               const NotificationDetails& details) {
+  if (type == NOTIFY_HISTORY_URL_VISITED) {
+    Details<history::URLVisitedDetails> visit_details(details);
+
+    if (!loaded())
+      visits_to_add_.push_back(visit_details->row);
+    else
+      UpdateKeywordSearchTermsForURL(visit_details->row);
+  } else if (type == NOTIFY_GOOGLE_URL_UPDATED) {
+    if (loaded_)
+      GoogleBaseURLChanged();
+  } else {
+    NOTREACHED();
+  }
+}
+
+void TemplateURLModel::DeleteGeneratedKeywordsMatchingHost(
+    const std::wstring& host) {
+  const std::wstring host_slash = host + L"/";
+  // Iterate backwards as we may end up removing multiple entries.
+  for (int i = static_cast<int>(template_urls_.size()) - 1; i >= 0; --i) {
+    if (CanReplace(template_urls_[i]) &&
+        (template_urls_[i]->keyword() == host ||
+         template_urls_[i]->keyword().compare(0, host_slash.length(),
+                                              host_slash) == 0)) {
+      Remove(template_urls_[i]);
+    }
+  }
+}
+
+void TemplateURLModel::NotifyLoaded() {
+  NotificationService::current()->
+      Notify(TEMPLATE_URL_MODEL_LOADED, Source<TemplateURLModel>(this),
+             NotificationService::NoDetails());
+}
+
+void TemplateURLModel::MergeEnginesFromPrepopulateData() {
+  // Build a map from prepopulate id to TemplateURL of existing urls.
+  std::map<int, const TemplateURL*> id_to_turl;
+  for (size_t i = 0; i < template_urls_.size(); ++i) {
+    if (template_urls_[i]->prepopulate_id() > 0)
+      id_to_turl[template_urls_[i]->prepopulate_id()] = template_urls_[i];
+  }
+
+  std::vector<TemplateURL*> loaded_urls;
+  size_t default_search_index;
+  TemplateURLPrepopulateData::GetPrepopulatedEngines(GetPrefs(),
+                                                     &loaded_urls,
+                                                     &default_search_index);
+
+  for (size_t i = 0; i < loaded_urls.size(); ++i) {
+    scoped_ptr<TemplateURL> t_url(loaded_urls[i]);
+
+    if (!t_url->prepopulate_id()) {
+      // Prepopulate engines need an id.
+      NOTREACHED();
+      continue;
+    }
+
+    const TemplateURL* existing_url = id_to_turl[t_url->prepopulate_id()];
+    if (existing_url) {
+      if (!existing_url->safe_for_autoreplace()) {
+        // User edited the entry, preserve the keyword and description.
+        loaded_urls[i]->set_safe_for_autoreplace(false);
+        loaded_urls[i]->set_keyword(existing_url->keyword());
+        loaded_urls[i]->set_autogenerate_keyword(
+            existing_url->autogenerate_keyword());
+        loaded_urls[i]->set_short_name(existing_url->short_name());
+      }
+      Replace(existing_url, loaded_urls[i]);
+      id_to_turl[t_url->prepopulate_id()] = loaded_urls[i];
+    } else {
+      Add(loaded_urls[i]);
+    }
+    if (i == default_search_index && !default_search_provider_)
+      SetDefaultSearchProvider(loaded_urls[i]);
+
+    t_url.release();
+  }
+}
+
+void TemplateURLModel::SaveDefaultSearchProviderToPrefs(
+    const TemplateURL* t_url) {
+  PrefService* prefs = GetPrefs();
+  if (!prefs)
+    return;
+
+  RegisterPrefs(prefs);
+
+  const std::wstring search_url =
+      (t_url && t_url->url()) ? t_url->url()->url() : std::wstring();
+  prefs->SetString(prefs::kDefaultSearchProviderSearchURL, search_url);
+
+  const std::wstring suggest_url =
+      (t_url && t_url->suggestions_url()) ? t_url->suggestions_url()->url() :
+                                            std::wstring();
+  prefs->SetString(prefs::kDefaultSearchProviderSuggestURL, suggest_url);
+
+  const std::wstring name =
+      t_url ? t_url->short_name() : std::wstring();
+  prefs->SetString(prefs::kDefaultSearchProviderName, name);
+
+  const std::wstring id_string =
+      t_url ? Int64ToWString(t_url->id()) : std::wstring();
+  prefs->SetString(prefs::kDefaultSearchProviderID, id_string);
+
+  prefs->ScheduleSavePersistentPrefs(g_browser_process->file_thread());
+}
+
+bool TemplateURLModel::LoadDefaultSearchProviderFromPrefs(
+    TemplateURL** default_provider) {
+  PrefService* prefs = GetPrefs();
+  if (!prefs || !prefs->HasPrefPath(prefs::kDefaultSearchProviderSearchURL) ||
+      !prefs->HasPrefPath(prefs::kDefaultSearchProviderSuggestURL) ||
+      !prefs->HasPrefPath(prefs::kDefaultSearchProviderName) ||
+      !prefs->HasPrefPath(prefs::kDefaultSearchProviderID)) {
+    return false;
+  }
+  RegisterPrefs(prefs);
+
+  std::wstring suggest_url =
+      prefs->GetString(prefs::kDefaultSearchProviderSuggestURL);
+  std::wstring search_url =
+      prefs->GetString(prefs::kDefaultSearchProviderSearchURL);
+
+  if (suggest_url.empty() && search_url.empty()) {
+    // The user doesn't want a default search provider.
+    *default_provider = NULL;
+    return true;
+  }
+
+  std::wstring name = prefs->GetString(prefs::kDefaultSearchProviderName);
+
+  std::wstring id_string = prefs->GetString(prefs::kDefaultSearchProviderID);
+
+  *default_provider = new TemplateURL();
+  (*default_provider)->set_short_name(name);
+  (*default_provider)->SetURL(search_url, 0, 0);
+  (*default_provider)->SetSuggestionsURL(suggest_url, 0, 0);
+  if (!id_string.empty())
+    (*default_provider)->set_id(StringToInt64(id_string));
+  return true;
+}
+
+void TemplateURLModel::RegisterPrefs(PrefService* prefs) {
+  if (prefs->IsPrefRegistered(prefs::kDefaultSearchProviderName))
+    return;
+  prefs->RegisterStringPref(
+      prefs::kDefaultSearchProviderName, std::wstring());
+  prefs->RegisterStringPref(
+      prefs::kDefaultSearchProviderID, std::wstring());
+  prefs->RegisterStringPref(
+      prefs::kDefaultSearchProviderSuggestURL, std::wstring());
+  prefs->RegisterStringPref(
+      prefs::kDefaultSearchProviderSearchURL, std::wstring());
+}
+
+bool TemplateURLModel::CanReplaceKeywordForHost(
+    const std::string& host,
+    const TemplateURL** to_replace) {
+  const HostToURLsMap::iterator matching_urls = host_to_urls_map_.find(host);
+  const bool have_matching_urls = (matching_urls != host_to_urls_map_.end());
+  if (have_matching_urls) {
+    TemplateURLSet& urls = matching_urls->second;
+    for (TemplateURLSet::iterator i = urls.begin(); i != urls.end(); ++i) {
+      const TemplateURL* url = *i;
+      if (CanReplace(url)) {
+        if (to_replace)
+          *to_replace = url;
+        return true;
+      }
+    }
+  }
+
+  if (to_replace)
+    *to_replace = NULL;
+  return !have_matching_urls;
+}
+
+bool TemplateURLModel::CanReplace(const TemplateURL* t_url) {
+  return (t_url != default_search_provider_ && !t_url->show_in_default_list() &&
+          t_url->safe_for_autoreplace());
+}
+
+PrefService* TemplateURLModel::GetPrefs() {
+  return profile_ ? profile_->GetPrefs() : NULL;
+}
+
+void TemplateURLModel::UpdateKeywordSearchTermsForURL(
+    const history::URLRow& row) {
+  if (!row.url().is_valid() ||
+      !row.url().parsed_for_possibly_invalid_spec().query.is_nonempty()) {
+    return;
+  }
+
+  HostToURLsMap::const_iterator t_urls_for_host_iterator =
+      host_to_urls_map_.find(row.url().host());
+  if (t_urls_for_host_iterator == host_to_urls_map_.end() ||
+      t_urls_for_host_iterator->second.empty()) {
+    return;
+  }
+
+  const TemplateURLSet& urls_for_host = t_urls_for_host_iterator->second;
+  QueryTerms query_terms;
+  bool built_terms = false;  // Most URLs won't match a TemplateURLs host;
+                             // so we lazily build the query_terms.
+  const std::string path = row.url().path();
+
+  for (TemplateURLSet::const_iterator i = urls_for_host.begin();
+       i != urls_for_host.end(); ++i) {
+    const TemplateURLRef* search_ref = (*i)->url();
+
+    // Count the URL against a TemplateURL if the host and path of the
+    // visited URL match that of the TemplateURL as well as the search term's
+    // key of the TemplateURL occurring in the visited url.
+    //
+    // NOTE: Even though we're iterating over TemplateURLs indexed by the host
+    // of the URL we still need to call GetHost on the search_ref. In
+    // particular, GetHost returns an empty string if search_ref doesn't support
+    // replacement or isn't valid for use in keyword search terms.
+
+    if (search_ref && search_ref->GetHost() == row.url().host() &&
+        search_ref->GetPath() == path) {
+      if (!built_terms && !BuildQueryTerms(row.url(), &query_terms)) {
+        // No query terms. No need to continue with the rest of the
+        // TemplateURLs.
+        return;
+      }
+      built_terms = true;
+
+      QueryTerms::iterator terms_iterator =
+          query_terms.find(search_ref->GetSearchTermKey());
+      if (terms_iterator != query_terms.end() &&
+          !terms_iterator->second.empty()) {
+        SetKeywordSearchTermsForURL(
+            *i, row.url(), search_ref->SearchTermToWide(*(*i),
+            terms_iterator->second));
+      }
+    }
+  }
+}
+
+// static
+bool TemplateURLModel::BuildQueryTerms(const GURL& url,
+                                       QueryTerms* query_terms) {
+  url_parse::Component query = url.parsed_for_possibly_invalid_spec().query;
+  url_parse::Component key, value;
+  size_t valid_term_count = 0;
+  while (url_parse::ExtractQueryKeyValue(url.spec().c_str(), &query, &key,
+                                         &value)) {
+    if (key.is_nonempty() && value.is_nonempty()) {
+      std::string key_string = url.spec().substr(key.begin, key.len);
+      std::string value_string = url.spec().substr(value.begin, value.len);
+      QueryTerms::iterator query_terms_iterator =
+          query_terms->find(key_string);
+      if (query_terms_iterator != query_terms->end()) {
+        if (!query_terms_iterator->second.empty() &&
+            query_terms_iterator->second != value_string) {
+          // The term occurs in multiple places with different values. Treat
+          // this as if the term doesn't occur by setting the value to an empty
+          // string.
+          (*query_terms)[key_string] = std::string();
+          DCHECK (valid_term_count > 0);
+          valid_term_count--;
+        }
+      } else {
+        valid_term_count++;
+        (*query_terms)[key_string] = value_string;
+      }
+    }
+  }
+  return (valid_term_count > 0);
+}
+
+void TemplateURLModel::GoogleBaseURLChanged() {
+  bool something_changed = false;
+  for (size_t i = 0; i < template_urls_.size(); ++i) {
+    const TemplateURL* t_url = template_urls_[i];
+    if ((t_url->url() && t_url->url()->HasGoogleBaseURLs()) ||
+        (t_url->suggestions_url() &&
+         t_url->suggestions_url()->HasGoogleBaseURLs())) {
+      RemoveFromMapsByPointer(t_url);
+      t_url->InvalidateCachedValues();
+      AddToMaps(t_url);
+      something_changed = true;
+    }
+  }
+
+  if (something_changed && loaded_) {
+    FOR_EACH_OBSERVER(TemplateURLModelObserver, model_observers_,
+                      OnTemplateURLModelChanged());
+  }
+}
diff --git a/chrome/browser/template_url_model.h b/chrome/browser/template_url_model.h
new file mode 100644
index 0000000..6b9569b
--- /dev/null
+++ b/chrome/browser/template_url_model.h
@@ -0,0 +1,348 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_TEMPLATE_URL_MODEL_H__
+#define CHROME_BROWSER_TEMPLATE_URL_MODEL_H__
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/observer_list.h"
+#include "chrome/browser/history/history_notifications.h"
+#include "chrome/browser/history/history_types.h"
+#include "chrome/browser/webdata/web_data_service.h"
+#include "chrome/common/notification_service.h"
+
+class GURL;
+class PrefService;
+class Profile;
+class TemplateURL;
+class TemplateURLModelTest;
+
+// TemplateURLModel is the backend for keywords. It's used by
+// KeywordAutocomplete.
+//
+// TemplateURLModel stores a vector of TemplateURLs. The TemplateURLs are
+// persisted to the database maintained by WebDataService. *ALL* mutations
+// to the TemplateURLs must funnel through TemplateURLModel. This allows
+// TemplateURLModel to notify listeners of changes as well as keep the
+// database in sync.
+//
+// There is a TemplateURLModel per Profile.
+//
+// TemplateURLModel does not load the vector of TemplateURLs in it's
+// constructor (except for testing). Use the Load method to trigger a load.
+// When TemplateURLModel has completed loading, observers are notified via
+// OnTemplateURLModelChanged as well as the TEMPLATE_URL_MODEL_LOADED
+// notification message.
+//
+// TemplateURLModel takes ownership of any TemplateURL passed to it. If there
+// is a WebDataService, deletion is handled by WebDataService, otherwise
+// TemplateURLModel handles deletion.
+
+// TemplateURLModelObserver is notified whenever the set of TemplateURLs
+// are modified.
+class TemplateURLModelObserver {
+ public:
+  // Notification that the template url model has changed in some way.
+  virtual void OnTemplateURLModelChanged() = 0;
+};
+
+class TemplateURLModel : public WebDataServiceConsumer,
+                         public NotificationObserver {
+ public:
+  typedef std::map<std::string, std::string> QueryTerms;
+
+  // Struct used for initializing the data store with fake data.
+  // Each initializer is mapped to a TemplateURL.
+  struct Initializer {
+    const wchar_t* const keyword;
+    const wchar_t* const url;
+    const wchar_t* const content;
+  };
+
+  explicit TemplateURLModel(Profile* profile);
+  // The following is for testing.
+  TemplateURLModel(const Initializer* initializers, const int count);
+
+  ~TemplateURLModel();
+
+  // Generates a suitable keyword for the specified url.  Returns an empty
+  // string if a keyword couldn't be generated.  If |autodetected| is true, we
+  // don't generate keywords for a variety of situations where we would probably
+  // not want to auto-add keywords, such as keywords for searches on pages that
+  // themselves come from form submissions.
+  static std::wstring GenerateKeyword(const GURL& url, bool autodetected);
+
+  // Removes any unnecessary characters from a user input keyword.
+  // This removes the leading scheme, "www." and any trailing slash.
+  static std::wstring CleanUserInputKeyword(const std::wstring& keyword);
+
+  // Returns the search url for t_url.  Returns an empty GURL if t_url has no
+  // url().
+  static GURL GenerateSearchURL(const TemplateURL* t_url);
+
+  // Returns true if there is no TemplateURL that conflicts with the
+  // keyword/url pair, or there is one but it can be replaced. If there is an
+  // existing keyword that can be replaced and template_url_to_replace is
+  // non-NULL, template_url_to_replace is set to the keyword to replace.
+  //
+  // url gives the url of the search query. The url is used to avoid generating
+  // a TemplateURL for an existing TemplateURL that shares the same host.
+  bool CanReplaceKeyword(const std::wstring& keyword,
+                         const std::wstring& url,
+                         const TemplateURL** template_url_to_replace);
+
+  // Returns (in |matches|) all keywords beginning with |prefix|, sorted
+  // shortest-first. If support_replacement_only is true, only keywords that
+  // support replacement are returned.
+  void FindMatchingKeywords(const std::wstring& prefix,
+                            bool support_replacement_only,
+                            std::vector<std::wstring>* matches) const;
+
+  // Looks up |keyword| and returns the element it maps to.  Returns NULL if
+  // the keyword was not found.
+  // The caller should not try to delete the returned pointer; the data store
+  // retains ownership of it.
+  const TemplateURL* GetTemplateURLForKeyword(
+    const std::wstring& keyword) const;
+
+  // Returns the first TemplateURL found with a URL using the specified |host|,
+  // or NULL if there are no such TemplateURLs
+  const TemplateURL* GetTemplateURLForHost(const std::string& host) const;
+
+  // Adds a new TemplateURL to this model. TemplateURLModel will own the
+  // reference, and delete it when the TemplateURL is removed.
+  void Add(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.
+  void Remove(const TemplateURL* template_url);
+
+  // Removes all auto-generated keywords that were created in the specified
+  // range.
+  void RemoveAutoGeneratedBetween(base::Time created_after, base::Time created_before);
+
+  // Replaces existing_turl with new_turl. new_turl is given the same ID as
+  // existing_turl. If existing_turl was the default, new_turl is made the
+  // default. After this call existing_turl is deleted. As with Add,
+  // TemplateURLModel takes ownership of existing_turl.
+  void Replace(const TemplateURL* existing_turl,
+               TemplateURL* new_turl);
+
+  // Removes all auto-generated keywords that were created on or after the
+  // date passed in.
+  void RemoveAutoGeneratedSince(base::Time created_after);
+
+  // Returns the set of URLs describing the keywords. The elements are owned
+  // by TemplateURLModel and should not be deleted.
+  std::vector<const TemplateURL*> GetTemplateURLs() const;
+
+  // Increment the usage count of a keyword.
+  // Called when a URL is loaded that was generated from a keyword.
+  void IncrementUsageCount(const TemplateURL* url);
+
+  // Resets the title, keyword and search url of the specified TemplateURL.
+  // The TemplateURL is marked as not replaceable.
+  void ResetTemplateURL(const TemplateURL* url,
+                        const std::wstring& title,
+                        const std::wstring& keyword,
+                        const std::wstring& search_url);
+
+  // The default search provider. This may be null.
+  void SetDefaultSearchProvider(const TemplateURL* url);
+
+  // Returns the default search provider. If the TemplateURLModel hasn't been
+  // loaded, the default search provider is pulled from preferences.
+  //
+  // NOTE: At least in unittest mode, this may return NULL.
+  const TemplateURL* GetDefaultSearchProvider();
+
+  // Observers used to listen for changes to the model.
+  // TemplateURLModel does NOT delete the observers when deleted.
+  void AddObserver(TemplateURLModelObserver* observer);
+  void RemoveObserver(TemplateURLModelObserver* observer);
+
+  // Loads the keywords. This has no effect if the keywords have already been
+  // loaded.
+  // Observers are notified when loading completes via the method
+  // OnTemplateURLsReset.
+  void Load();
+
+  // Whether or not the keywords have been loaded.
+  bool loaded() { return loaded_; }
+
+  // Notification that the keywords have been loaded.
+  // This is invoked from WebDataService, and should not be directly
+  // invoked.
+  virtual void OnWebDataServiceRequestDone(WebDataService::Handle h,
+                                           const WDTypedResult* result);
+
+  // Removes (and deletes) TemplateURLs from |urls| that have duplicate
+  // prepopulate ids. Duplicate prepopulate ids are not allowed, but due to a
+  // bug it was possible get dups. This step is only called when the version
+  // number changes.
+  void RemoveDuplicatePrepopulateIDs(std::vector<const TemplateURL*>* urls);
+
+  // NotificationObserver method. TemplateURLModel listens for three
+  // notification types:
+  // . NOTIFY_HISTORY_URL_VISITED: adds keyword search terms if the visit
+  //   corresponds to a keyword.
+  // . NOTIFY_GOOGLE_URL_UPDATED: updates mapping for any keywords containing
+  //   a google base url replacement term.
+  virtual void Observe(NotificationType type,
+                       const NotificationSource& source,
+                       const NotificationDetails& details);
+
+  Profile* profile() const { return profile_; }
+
+ 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.
+  //
+  // This exists and is virtual for testing.
+  virtual void SetKeywordSearchTermsForURL(const TemplateURL* t_url,
+                                           const GURL& url,
+                                           const std::wstring& term);
+
+ private:
+  FRIEND_TEST(TemplateURLModelTest, BuildQueryTerms);
+  FRIEND_TEST(TemplateURLModelTest, UpdateKeywordSearchTermsForURL);
+  FRIEND_TEST(TemplateURLModelTest, DontUpdateKeywordSearchForNonReplaceable);
+  FRIEND_TEST(TemplateURLModelTest, ChangeGoogleBaseValue);
+  friend class TemplateURLModelTest;
+
+  typedef std::map<std::wstring, const TemplateURL*> KeywordToTemplateMap;
+  typedef std::vector<const TemplateURL*> TemplateURLVector;
+
+  // Helper functor for FindMatchingKeywords(), for finding the range of
+  // keywords which begin with a prefix.
+  class LessWithPrefix;
+
+  void Init(const Initializer* initializers, int num_initializers);
+
+  void RemoveFromMaps(const TemplateURL* template_url);
+
+  // Removes the supplied template_url from the maps. This searches through all
+  // entries in the maps and does not generate the host or keyword.
+  // This is used when the cached content of the TemplateURL changes.
+  void RemoveFromMapsByPointer(const TemplateURL* template_url);
+
+  void AddToMaps(const TemplateURL* template_url);
+
+  // Sets the keywords. This is used once the keywords have been loaded.
+  // This does NOT notify the delegate or the database.
+  void SetTemplateURLs(const std::vector<const TemplateURL*>& urls);
+
+  void DeleteGeneratedKeywordsMatchingHost(const std::wstring& host);
+
+  // If there is a notification service, sends TEMPLATE_URL_MODEL_LOADED
+  // notification.
+  void NotifyLoaded();
+
+  // Loads engines from prepopulate data and merges them in with the existing
+  // engines.  This is invoked when the version of the prepopulate data changes.
+  void MergeEnginesFromPrepopulateData();
+
+  // Saves enough of url to preferences so that it can be loaded from
+  // preferences on start up.
+  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
+  // 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);
+
+  // 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
+  // be replaced.
+  bool CanReplaceKeywordForHost(const std::string& host,
+                                const TemplateURL** to_replace);
+
+  // Returns true if the TemplateURL is replaceable. This doesn't look at the
+  // uniqueness of the keyword or host and is intended to be called after those
+  // checks have been done. This returns true if the TemplateURL doesn't appear
+  // in the default list and is marked as safe_for_autoreplace.
+  bool CanReplace(const TemplateURL* t_url);
+
+  // Returns the preferences we use.
+  PrefService* GetPrefs();
+
+  // Iterates through the TemplateURLs to see if one matches the visited url.
+  // For each TemplateURL whose url matches the visited url
+  // SetKeywordSearchTermsForURL is invoked.
+  void UpdateKeywordSearchTermsForURL(const history::URLRow& row);
+
+  // Adds each of the query terms in the specified url whose key and value are
+  // non-empty to query_terms. If a query key appears multiple times, the value
+  // is set to an empty string. Returns true if there is at least one key that
+  // does not occur multiple times.
+  static bool BuildQueryTerms(
+      const GURL& url,
+      std::map<std::string,std::string>* query_terms);
+
+  // Invoked when the Google base URL has changed. Updates the mapping for all
+  // TemplateURLs that have a replacement term of {google:baseURL} or
+  // {google:baseSuggestURL}.
+  void GoogleBaseURLChanged();
+
+  // Mapping from keyword to the TemplateURL.
+  KeywordToTemplateMap keyword_to_template_map_;
+
+  TemplateURLVector template_urls_;
+
+  ObserverList<TemplateURLModelObserver> model_observers_;
+
+  // Maps from host to set of TemplateURLs whose search url host is host.
+  typedef std::set<const TemplateURL*> TemplateURLSet;
+  typedef std::map<std::string, TemplateURLSet> HostToURLsMap;
+  HostToURLsMap host_to_urls_map_;
+
+  // Used to obtain the WebDataService.
+  // When Load is invoked, if we haven't yet loaded, the WebDataService is
+  // obtained from the Profile. This allows us to lazily access the database.
+  Profile* profile_;
+
+  // Whether the keywords have been loaded.
+  bool loaded_;
+
+  // If non-zero, we're waiting on a load.
+  WebDataService::Handle load_handle_;
+
+  // Service used to store entries.
+  scoped_refptr<WebDataService> service_;
+
+  // List of hosts to feed to DeleteGeneratedKeywordsMatchingHost. When
+  // we receive NOTIFY_HOST_DELETED_FROM_HISTORY if we haven't loaded yet,
+  // we force a load and add the host to hosts_to_delete_. When done loading
+  // we invoke DeleteGeneratedKeywordsMatchingHost with all the elements of
+  // the vector.
+  std::vector<std::wstring> hosts_to_delete_;
+
+  // All visits that occurred before we finished loading. Once loaded
+  // UpdateKeywordSearchTermsForURL is invoked for each element of the vector.
+  std::vector<history::URLRow> visits_to_add_;
+
+  const TemplateURL* default_search_provider_;
+
+  // The default search provider from preferences. This is only valid if
+  // GetDefaultSearchProvider is invoked and we haven't been loaded. Once loaded
+  // this is not used.
+  scoped_ptr<TemplateURL> prefs_default_search_provider_;
+
+  // ID assigned to next TemplateURL added to this model. This is an ever
+  // increasing integer that is initialized from the database.
+  TemplateURL::IDType next_id_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(TemplateURLModel);
+};
+
+#endif  // CHROME_BROWSER_TEMPLATE_URL_MODEL_H__
+
diff --git a/chrome/browser/template_url_model_unittest.cc b/chrome/browser/template_url_model_unittest.cc
new file mode 100644
index 0000000..b5198e4
--- /dev/null
+++ b/chrome/browser/template_url_model_unittest.cc
@@ -0,0 +1,633 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/test/testing_profile.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Time;
+using base::TimeDelta;
+
+// A Task used to coordinate when the database has finished processing
+// requests. See note in BlockTillServiceProcessesRequests for details.
+//
+// When Run() schedules a QuitTask on the message loop it was created with.
+class QuitTask2 : public Task {
+ public:
+  QuitTask2() : main_loop_(MessageLoop::current()) {}
+
+  virtual void Run() {
+    main_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+  }
+
+ private:
+  MessageLoop* main_loop_;
+};
+
+// Subclass the TestingProfile so that it can return a WebDataService.
+class TemplateURLModelTestingProfile : public TestingProfile {
+ public:
+  TemplateURLModelTestingProfile() : TestingProfile() { }
+
+  void SetUp() {
+    // Name a subdirectory of the temp directory.
+    ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_));
+    file_util::AppendToPath(&test_dir_, L"TemplateURLModelTest");
+
+    // Create a fresh, empty copy of this directory.
+    file_util::Delete(test_dir_, true);
+    file_util::CreateDirectory(test_dir_);
+
+    std::wstring path = test_dir_;
+    file_util::AppendToPath(&path, L"TestDataService.db");
+    service_ = new WebDataService;
+    EXPECT_TRUE(service_->InitWithPath(path));
+  }
+
+  void TearDown() {
+    // Clean up the test directory.
+    service_->Shutdown();
+    ASSERT_TRUE(file_util::Delete(test_dir_, true));
+    ASSERT_FALSE(file_util::PathExists(test_dir_));
+  }
+
+  virtual WebDataService* GetWebDataService(ServiceAccessType access) {
+    return service_.get();
+  }
+
+ private:
+  scoped_refptr<WebDataService> service_;
+  std::wstring test_dir_;
+};
+
+// Trivial subclass of TemplateURLModel that records the last invocation of
+// SetKeywordSearchTermsForURL.
+class TestingTemplateURLModel : public TemplateURLModel {
+ public:
+  explicit TestingTemplateURLModel(Profile* profile)
+      : TemplateURLModel(profile) {
+  }
+
+  std::wstring GetAndClearSearchTerm() {
+    std::wstring search_term;
+    search_term.swap(search_term_);
+    return search_term;
+  }
+
+ protected:
+  virtual void SetKeywordSearchTermsForURL(const TemplateURL* t_url,
+                                           const GURL& url,
+                                           const std::wstring& term) {
+    search_term_ = term;
+  }
+
+ private:
+  std::wstring search_term_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(TestingTemplateURLModel);
+};
+
+class TemplateURLModelTest : public testing::Test,
+                             public TemplateURLModelObserver {
+ public:
+  TemplateURLModelTest() : changed_count_(0) {
+  }
+
+  virtual void SetUp() {
+    profile_.reset(new TemplateURLModelTestingProfile());
+    profile_->SetUp();
+    model_.reset(new TestingTemplateURLModel(profile_.get()));
+    model_->AddObserver(this);
+  }
+
+  virtual void TearDown() {
+    profile_->TearDown();
+    delete TemplateURLRef::google_base_url_;
+    TemplateURLRef::google_base_url_ = NULL;
+
+    // Flush the message loop to make Purify happy.
+    message_loop_.RunAllPending();
+  }
+
+  TemplateURL* AddKeywordWithDate(const std::wstring& keyword,
+                                  bool autogenerate_keyword,
+                                  const std::wstring& url,
+                                  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->set_keyword(keyword);
+    template_url->set_autogenerate_keyword(autogenerate_keyword);
+    template_url->set_short_name(short_name);
+    template_url->set_date_created(created_date);
+    template_url->set_safe_for_autoreplace(safe_for_autoreplace);
+    model_->Add(template_url);
+    EXPECT_NE(0, template_url->id());
+    return template_url;
+  }
+
+  virtual void OnTemplateURLModelChanged() {
+    changed_count_++;
+  }
+
+  void VerifyObserverCount(int expected_changed_count) {
+    ASSERT_EQ(expected_changed_count, changed_count_);
+    changed_count_ = 0;
+  }
+
+  // Blocks the caller until the service has finished servicing all pending
+  // requests.
+  void BlockTillServiceProcessesRequests() {
+    // Schedule a task on the background thread that is processed after all
+    // pending requests on the background thread.
+    profile_->GetWebDataService(Profile::EXPLICIT_ACCESS)->thread()->
+        message_loop()->PostTask(FROM_HERE, new QuitTask2());
+    // Run the current message loop. QuitTask2, when run, invokes Quit,
+    // which unblocks this.
+    MessageLoop::current()->Run();
+  }
+
+  // Makes sure the load was successful and sent the correct notification.
+  void VerifyLoad() {
+    ASSERT_FALSE(model_->loaded());
+    model_->Load();
+    BlockTillServiceProcessesRequests();
+    VerifyObserverCount(1);
+    changed_count_ = 0;
+  }
+
+  // Creates a new TemplateURLModel.
+  void ResetModel(bool verify_load) {
+    model_.reset(new TestingTemplateURLModel(profile_.get()));
+    model_->AddObserver(this);
+    changed_count_ = 0;
+    if (verify_load)
+      VerifyLoad();
+  }
+
+  // Verifies the two TemplateURLs are equal.
+  void AssertEquals(const TemplateURL& expected, const TemplateURL& actual) {
+    ASSERT_EQ(expected.url()->url(), actual.url()->url());
+    ASSERT_EQ(expected.keyword(), actual.keyword());
+    ASSERT_EQ(expected.short_name(), actual.short_name());
+    ASSERT_TRUE(expected.GetFavIconURL() == actual.GetFavIconURL());
+    ASSERT_EQ(expected.id(), actual.id());
+    ASSERT_EQ(expected.safe_for_autoreplace(), actual.safe_for_autoreplace());
+    ASSERT_EQ(expected.show_in_default_list(), actual.show_in_default_list());
+    ASSERT_TRUE(expected.date_created() == actual.date_created());
+  }
+
+  std::wstring GetAndClearSearchTerm() {
+    return model_->GetAndClearSearchTerm();
+  }
+
+  void SetGoogleBaseURL(const std::wstring& base_url) const {
+    delete TemplateURLRef::google_base_url_;
+    TemplateURLRef::google_base_url_ = new std::wstring(base_url);
+  }
+
+  MessageLoopForUI message_loop_;
+  scoped_ptr<TemplateURLModelTestingProfile> profile_;
+  scoped_ptr<TestingTemplateURLModel> model_;
+  int changed_count_;
+};
+
+TEST_F(TemplateURLModelTest, Load) {
+  VerifyLoad();
+}
+
+TEST_F(TemplateURLModelTest, AddUpdateRemove) {
+  // Add a new TemplateURL.
+  VerifyLoad();
+  const size_t initial_count = model_->GetTemplateURLs().size();
+
+  TemplateURL* t_url = new TemplateURL();
+  t_url->SetURL(L"http://www.google.com/foo/bar", 0, 0);
+  t_url->set_keyword(L"keyword");
+  t_url->set_short_name(L"google");
+  GURL favicon_url("http://favicon.url");
+  t_url->SetFavIconURL(favicon_url);
+  t_url->set_date_created(Time::FromTimeT(100));
+  t_url->set_safe_for_autoreplace(true);
+  model_->Add(t_url);
+  ASSERT_TRUE(model_->CanReplaceKeyword(L"keyword", std::wstring(), NULL));
+  VerifyObserverCount(1);
+  BlockTillServiceProcessesRequests();
+  // We need to clone as model takes ownership of TemplateURL and will
+  // delete it.
+  TemplateURL cloned_url(*t_url);
+  ASSERT_EQ(1 + initial_count, model_->GetTemplateURLs().size());
+  ASSERT_TRUE(model_->GetTemplateURLForKeyword(t_url->keyword()) == t_url);
+  ASSERT_TRUE(t_url->date_created() == cloned_url.date_created());
+
+  // Reload the model to verify it was actually saved to the database.
+  ResetModel(true);
+  ASSERT_EQ(1 + initial_count, model_->GetTemplateURLs().size());
+  const TemplateURL* loaded_url = model_->GetTemplateURLForKeyword(L"keyword");
+  ASSERT_TRUE(loaded_url != NULL);
+  AssertEquals(cloned_url, *loaded_url);
+  ASSERT_TRUE(model_->CanReplaceKeyword(L"keyword", std::wstring(), NULL));
+
+  // Mutate an element and verify it succeeded.
+  model_->ResetTemplateURL(loaded_url, L"a", L"b", L"c");
+  ASSERT_EQ(L"a", loaded_url->short_name());
+  ASSERT_EQ(L"b", loaded_url->keyword());
+  ASSERT_EQ(L"c", loaded_url->url()->url());
+  ASSERT_FALSE(loaded_url->safe_for_autoreplace());
+  ASSERT_TRUE(model_->CanReplaceKeyword(L"keyword", std::wstring(), NULL));
+  ASSERT_FALSE(model_->CanReplaceKeyword(L"b", std::wstring(), NULL));
+  cloned_url = *loaded_url;
+  BlockTillServiceProcessesRequests();
+  ResetModel(true);
+  ASSERT_EQ(1 + initial_count, model_->GetTemplateURLs().size());
+  loaded_url = model_->GetTemplateURLForKeyword(L"b");
+  ASSERT_TRUE(loaded_url != NULL);
+  AssertEquals(cloned_url, *loaded_url);
+
+  // Remove an element and verify it succeeded.
+  model_->Remove(loaded_url);
+  VerifyObserverCount(1);
+  ResetModel(true);
+  ASSERT_EQ(initial_count, model_->GetTemplateURLs().size());
+  EXPECT_TRUE(model_->GetTemplateURLForKeyword(L"b") == NULL);
+}
+
+TEST_F(TemplateURLModelTest, GenerateKeyword) {
+  ASSERT_EQ(L"", TemplateURLModel::GenerateKeyword(GURL(), true));
+  // Shouldn't generate keywords for https.
+  ASSERT_EQ(L"", TemplateURLModel::GenerateKeyword(GURL("https://blah"), true));
+  ASSERT_EQ(L"foo", TemplateURLModel::GenerateKeyword(GURL("http://foo"),
+                                                      true));
+  // www. should be stripped.
+  ASSERT_EQ(L"foo", TemplateURLModel::GenerateKeyword(GURL("http://www.foo"),
+                                                      true));
+  // Shouldn't generate keywords with paths, if autodetected.
+  ASSERT_EQ(L"", TemplateURLModel::GenerateKeyword(GURL("http://blah/foo"),
+                                                   true));
+  ASSERT_EQ(L"blah", TemplateURLModel::GenerateKeyword(GURL("http://blah/foo"),
+                                                       false));
+  // FTP shouldn't generate a keyword.
+  ASSERT_EQ(L"", TemplateURLModel::GenerateKeyword(GURL("ftp://blah/"), true));
+  // Make sure we don't get a trailing /
+  ASSERT_EQ(L"blah", TemplateURLModel::GenerateKeyword(GURL("http://blah/"),
+                                                       true));
+}
+
+TEST_F(TemplateURLModelTest, ClearBrowsingData_Keywords) {
+  Time now = Time::Now();
+  TimeDelta one_day = TimeDelta::FromDays(1);
+  Time month_ago = now - TimeDelta::FromDays(30);
+
+  // Nothing has been added.
+  EXPECT_EQ(0U, model_->GetTemplateURLs().size());
+
+  // Create one with a 0 time.
+  AddKeywordWithDate(L"key1", false, L"http://foo1", L"name1", true, Time());
+  // Create one for now and +/- 1 day.
+  AddKeywordWithDate(L"key2", false, L"http://foo2", L"name2", true,
+                     now - one_day);
+  AddKeywordWithDate(L"key3", false, L"http://foo3", L"name3", true, now);
+  AddKeywordWithDate(L"key4", false, L"http://foo4", L"name4", true,
+                     now + one_day);
+  // Try the other three states.
+  AddKeywordWithDate(L"key5", false, L"http://foo5", L"name5", false, now);
+  AddKeywordWithDate(L"key6", false, L"http://foo6", L"name6", false, month_ago);
+
+  // We just added a few items, validate them.
+  EXPECT_EQ(6U, model_->GetTemplateURLs().size());
+
+  // Try removing from current timestamp. This should delete the one in the
+  // future and one very recent one.
+  model_->RemoveAutoGeneratedSince(now);
+  EXPECT_EQ(4U, model_->GetTemplateURLs().size());
+
+  // Try removing from two months ago. This should only delete items that are
+  // auto-generated.
+  model_->RemoveAutoGeneratedSince(now - TimeDelta::FromDays(60));
+  EXPECT_EQ(3U, model_->GetTemplateURLs().size());
+
+  // Make sure the right values remain.
+  EXPECT_EQ(L"key1", model_->GetTemplateURLs()[0]->keyword());
+  EXPECT_TRUE(model_->GetTemplateURLs()[0]->safe_for_autoreplace());
+  EXPECT_EQ(0U, model_->GetTemplateURLs()[0]->date_created().ToInternalValue());
+
+  EXPECT_EQ(L"key5", model_->GetTemplateURLs()[1]->keyword());
+  EXPECT_FALSE(model_->GetTemplateURLs()[1]->safe_for_autoreplace());
+  EXPECT_EQ(now.ToInternalValue(),
+            model_->GetTemplateURLs()[1]->date_created().ToInternalValue());
+
+  EXPECT_EQ(L"key6", model_->GetTemplateURLs()[2]->keyword());
+  EXPECT_FALSE(model_->GetTemplateURLs()[2]->safe_for_autoreplace());
+  EXPECT_EQ(month_ago.ToInternalValue(),
+            model_->GetTemplateURLs()[2]->date_created().ToInternalValue());
+
+  // Try removing from Time=0. This should delete one more.
+  model_->RemoveAutoGeneratedSince(Time());
+  EXPECT_EQ(2U, model_->GetTemplateURLs().size());
+}
+
+TEST_F(TemplateURLModelTest, Reset) {
+  // Add a new TemplateURL.
+  VerifyLoad();
+  const size_t initial_count = model_->GetTemplateURLs().size();
+  TemplateURL* t_url = new TemplateURL();
+  t_url->SetURL(L"http://www.google.com/foo/bar", 0, 0);
+  t_url->set_keyword(L"keyword");
+  t_url->set_short_name(L"google");
+  GURL favicon_url("http://favicon.url");
+  t_url->SetFavIconURL(favicon_url);
+  t_url->set_date_created(Time::FromTimeT(100));
+  model_->Add(t_url);
+
+  VerifyObserverCount(1);
+  BlockTillServiceProcessesRequests();
+
+  // Reset the short name, keyword, url and make sure it takes.
+  const std::wstring new_short_name(L"a");
+  const std::wstring new_keyword(L"b");
+  const std::wstring new_url(L"c");
+  model_->ResetTemplateURL(t_url, new_short_name, new_keyword, new_url);
+  ASSERT_EQ(new_short_name, t_url->short_name());
+  ASSERT_EQ(new_keyword, t_url->keyword());
+  ASSERT_EQ(new_url, t_url->url()->url());
+
+  // Make sure the mappings in the model were updated.
+  ASSERT_TRUE(model_->GetTemplateURLForKeyword(new_keyword) == t_url);
+  ASSERT_TRUE(model_->GetTemplateURLForKeyword(L"keyword") == NULL);
+
+  TemplateURL last_url = *t_url;
+
+  // Reload the model from the database and make sure the change took.
+  ResetModel(true);
+  t_url = NULL;
+  EXPECT_EQ(initial_count + 1, model_->GetTemplateURLs().size());
+  const TemplateURL* read_url = model_->GetTemplateURLForKeyword(new_keyword);
+  ASSERT_TRUE(read_url);
+  AssertEquals(last_url, *read_url);
+}
+
+TEST_F(TemplateURLModelTest, DefaultSearchProvider) {
+  // Add a new TemplateURL.
+  VerifyLoad();
+  const size_t initial_count = model_->GetTemplateURLs().size();
+  TemplateURL* t_url = AddKeywordWithDate(L"key1", false, L"http://foo1",
+                                          L"name1", true, Time());
+
+  changed_count_ = 0;
+  model_->SetDefaultSearchProvider(t_url);
+
+  ASSERT_EQ(t_url, model_->GetDefaultSearchProvider());
+
+  ASSERT_TRUE(t_url->safe_for_autoreplace());
+  ASSERT_TRUE(t_url->show_in_default_list());
+
+  // Setting the default search provider should have caused notification.
+  VerifyObserverCount(1);
+
+  BlockTillServiceProcessesRequests();
+
+  TemplateURL cloned_url = *t_url;
+
+  ResetModel(true);
+  t_url = NULL;
+
+  // Make sure when we reload we get a default search provider.
+  EXPECT_EQ(1 + initial_count, model_->GetTemplateURLs().size());
+  ASSERT_TRUE(model_->GetDefaultSearchProvider());
+  AssertEquals(cloned_url, *model_->GetDefaultSearchProvider());
+}
+
+TEST_F(TemplateURLModelTest, TemplateURLWithNoKeyword) {
+  VerifyLoad();
+
+  const size_t initial_count = model_->GetTemplateURLs().size();
+
+  AddKeywordWithDate(std::wstring(), false, L"http://foo1", L"name1", true,
+                     Time());
+
+  // We just added a few items, validate them.
+  ASSERT_EQ(initial_count + 1, model_->GetTemplateURLs().size());
+
+  // Reload the model from the database and make sure we get the url back.
+  ResetModel(true);
+
+  ASSERT_EQ(1 + initial_count, model_->GetTemplateURLs().size());
+
+  bool found_keyword = false;
+  for (size_t i = 0; i < initial_count + 1; ++i) {
+    if (model_->GetTemplateURLs()[i]->keyword().empty()) {
+      found_keyword = true;
+      break;
+    }
+  }
+  ASSERT_TRUE(found_keyword);
+}
+
+TEST_F(TemplateURLModelTest, CantReplaceWithSameKeyword) {
+  ASSERT_TRUE(model_->CanReplaceKeyword(L"foo", std::wstring(), NULL));
+  TemplateURL* t_url = AddKeywordWithDate(L"foo", false, L"http://foo1",
+                                          L"name1", true, Time());
+
+  // Can still replace, newly added template url is marked safe to replace.
+  ASSERT_TRUE(model_->CanReplaceKeyword(L"foo", L"http://foo2", NULL));
+
+  // ResetTemplateURL marks the TemplateURL as unsafe to replace, so it should
+  // no longer be replaceable.
+  model_->ResetTemplateURL(t_url, t_url->short_name(), t_url->keyword(),
+                           t_url->url()->url());
+
+  ASSERT_FALSE(model_->CanReplaceKeyword(L"foo", L"http://foo2", NULL));
+}
+
+TEST_F(TemplateURLModelTest, CantReplaceWithSameHosts) {
+  ASSERT_TRUE(model_->CanReplaceKeyword(L"foo", L"http://foo.com", NULL));
+  TemplateURL* t_url = AddKeywordWithDate(L"foo", false, L"http://foo.com",
+                                          L"name1", true, Time());
+
+  // Can still replace, newly added template url is marked safe to replace.
+  ASSERT_TRUE(model_->CanReplaceKeyword(L"bar", L"http://foo.com", NULL));
+
+  // ResetTemplateURL marks the TemplateURL as unsafe to replace, so it should
+  // no longer be replaceable.
+  model_->ResetTemplateURL(t_url, t_url->short_name(), t_url->keyword(),
+                           t_url->url()->url());
+
+  ASSERT_FALSE(model_->CanReplaceKeyword(L"bar", L"http://foo.com", NULL));
+}
+
+TEST_F(TemplateURLModelTest, HasDefaultSearchProvider) {
+  // We should have a default search provider even if we haven't loaded.
+  ASSERT_TRUE(model_->GetDefaultSearchProvider());
+
+  // Now force the model to load and make sure we still have a default.
+  VerifyLoad();
+
+  ASSERT_TRUE(model_->GetDefaultSearchProvider());
+}
+
+TEST_F(TemplateURLModelTest, DefaultSearchProviderLoadedFromPrefs) {
+  VerifyLoad();
+
+  TemplateURL* template_url = new TemplateURL();
+  template_url->SetURL(L"http://url", 0, 0);
+  template_url->SetSuggestionsURL(L"http://url2", 0, 0);
+  template_url->set_short_name(L"a");
+  template_url->set_safe_for_autoreplace(true);
+  template_url->set_date_created(Time::FromTimeT(100));
+
+  model_->Add(template_url);
+
+  const TemplateURL::IDType id = template_url->id();
+
+  model_->SetDefaultSearchProvider(template_url);
+
+  BlockTillServiceProcessesRequests();
+
+  TemplateURL first_default_search_provider = *template_url;
+
+  template_url = NULL;
+
+  // Reset the model and don't load it. The template url we set as the default
+  // should be pulled from prefs now.
+  ResetModel(false);
+
+  // NOTE: This doesn't use AssertEquals as only a subset of the TemplateURLs
+  // value are persisted to prefs.
+  const TemplateURL* default_turl = model_->GetDefaultSearchProvider();
+  ASSERT_TRUE(default_turl);
+  ASSERT_TRUE(default_turl->url());
+  ASSERT_EQ(L"http://url", default_turl->url()->url());
+  ASSERT_TRUE(default_turl->suggestions_url());
+  ASSERT_EQ(L"http://url2", default_turl->suggestions_url()->url());
+  ASSERT_EQ(L"a", default_turl->short_name());
+  ASSERT_EQ(id, default_turl->id());
+
+  // Now do a load and make sure the default search provider really takes.
+  VerifyLoad();
+
+  ASSERT_TRUE(model_->GetDefaultSearchProvider());
+  AssertEquals(first_default_search_provider,
+               *model_->GetDefaultSearchProvider());
+}
+
+TEST_F(TemplateURLModelTest, BuildQueryTerms) {
+  struct TestData {
+    const std::string url;
+    const bool result;
+    // Keys and values are a semicolon separated list of expected values in the
+    // map.
+    const std::string keys;
+    const std::string values;
+  } data[] = {
+    // No query should return false.
+    { "http://blah/", false, "", "" },
+
+    // Query with empty key should return false.
+    { "http://blah/foo?=y", false, "", "" },
+
+    // Query with key occurring multiple times should return false.
+    { "http://blah/foo?x=y&x=z", false, "", "" },
+
+    { "http://blah/foo?x=y", true, "x", "y" },
+    { "http://blah/foo?x=y&y=z", true, "x;y", "y;z" },
+
+    // Key occurring multiple times should get an empty string.
+    { "http://blah/foo?x=y&x=z&y=z", true, "x;y", ";z" },
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
+    TemplateURLModel::QueryTerms terms;
+    ASSERT_EQ(data[i].result,
+              TemplateURLModel::BuildQueryTerms(GURL(data[i].url), &terms));
+    if (data[i].result) {
+      std::vector<std::string> keys;
+      std::vector<std::string> values;
+      SplitString(data[i].keys, ';', &keys);
+      SplitString(data[i].values, ';', &values);
+      ASSERT_TRUE(keys.size() == values.size());
+      ASSERT_EQ(keys.size(), terms.size());
+      for (size_t j = 0; j < keys.size(); ++j) {
+        TemplateURLModel::QueryTerms::iterator term_iterator =
+            terms.find(keys[j]);
+        ASSERT_TRUE(term_iterator != terms.end());
+        ASSERT_EQ(values[j], term_iterator->second);
+      }
+    }
+  }
+}
+
+TEST_F(TemplateURLModelTest, UpdateKeywordSearchTermsForURL) {
+  struct TestData {
+    const std::string url;
+    const std::wstring term;
+  } data[] = {
+    { "http://foo/", L"" },
+    { "http://foo/foo?q=xx", L"" },
+    { "http://x/bar?q=xx", L"" },
+    { "http://x/foo?y=xx", L"" },
+    { "http://x/foo?q=xx", L"xx" },
+    { "http://x/foo?a=b&q=xx", L"xx" },
+    { "http://x/foo?q=b&q=xx", L"" },
+  };
+
+  AddKeywordWithDate(L"x", false, L"http://x/foo?q={searchTerms}", L"name",
+                     false, Time());
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
+    model_->UpdateKeywordSearchTermsForURL(history::URLRow(GURL(data[i].url)));
+    EXPECT_EQ(data[i].term, GetAndClearSearchTerm());
+  }
+}
+
+TEST_F(TemplateURLModelTest, DontUpdateKeywordSearchForNonReplaceable) {
+  struct TestData {
+    const std::string url;
+  } data[] = {
+    { "http://foo/" },
+    { "http://x/bar?q=xx" },
+    { "http://x/foo?y=xx" },
+  };
+
+  AddKeywordWithDate(L"x", false, L"http://x/foo", L"name", false, Time());
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
+    model_->UpdateKeywordSearchTermsForURL(history::URLRow(GURL(data[i].url)));
+    ASSERT_EQ(std::wstring(), GetAndClearSearchTerm());
+  }
+}
+
+TEST_F(TemplateURLModelTest, ChangeGoogleBaseValue) {
+  // NOTE: Do not do a VerifyLoad() here as it will load the prepopulate data,
+  // which also has a {google:baseURL} keyword in it, which will confuse this
+  // test.
+  SetGoogleBaseURL(L"http://google.com/");
+  const TemplateURL* t_url = AddKeywordWithDate(std::wstring(), true,
+      L"{google:baseURL}?q={searchTerms}", 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());
+
+  // Change the Google base url.
+  model_->loaded_ = true;  // Hack to make sure we get notified of the base URL
+                           // changing.
+  SetGoogleBaseURL(L"http://foo.com/");
+  model_->GoogleBaseURLChanged();
+  VerifyObserverCount(1);
+
+  // Make sure the host->TemplateURL map was updated appropriately.
+  ASSERT_EQ(t_url, model_->GetTemplateURLForHost("foo.com"));
+  EXPECT_TRUE(model_->GetTemplateURLForHost("google.com") == NULL);
+  EXPECT_EQ("foo.com", t_url->url()->GetHost());
+  EXPECT_EQ(L"foo.com", t_url->keyword());
+  EXPECT_EQ("http://foo.com/?q=x", t_url->url()->ReplaceSearchTerms(*t_url,
+      L"x", TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring()).spec());
+}
diff --git a/chrome/browser/template_url_parser.cc b/chrome/browser/template_url_parser.cc
new file mode 100644
index 0000000..93f9f79
--- /dev/null
+++ b/chrome/browser/template_url_parser.cc
@@ -0,0 +1,586 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/template_url_parser.h"
+
+#include <map>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "chrome/browser/template_url.h"
+#include "googleurl/src/gurl.h"
+#include "libxml/parser.h"
+#include "libxml/xmlwriter.h"
+
+namespace {
+
+//
+// NOTE: libxml uses the UTF-8 encoding. As 0-127 of UTF-8 corresponds
+// to that of char, the following names are all in terms of char. This avoids
+// having to convert to wide, then do comparisons
+
+// Defines for element names of the OSD document:
+static const char kURLElement[] = "Url";
+static const char kParamElement[] = "Param";
+static const char kShortNameElement[] = "ShortName";
+static const char kDescriptionElement[] = "Description";
+static const char kImageElement[] = "Image";
+static const char kOpenSearchDescriptionElement[] = "OpenSearchDescription";
+static const char kFirefoxSearchDescriptionElement[] = "SearchPlugin";
+static const char kLanguageElement[] = "Language";
+static const char kInputEncodingElement[] = "InputEncoding";
+
+// Various XML attributes used.
+static const char kURLTypeAttribute[] = "type";
+static const char kURLTemplateAttribute[] = "template";
+static const char kImageTypeAttribute[] = "type";
+static const char kImageWidthAttribute[] = "width";
+static const char kImageHeightAttribute[] = "height";
+static const char kURLIndexOffsetAttribute[] = "indexOffset";
+static const char kURLPageOffsetAttribute[] = "pageOffset";
+static const char kParamNameAttribute[] = "name";
+static const char kParamValueAttribute[] = "value";
+static const char kParamMethodAttribute[] = "method";
+
+// Mime type for search results.
+static const char kHTMLType[] = "text/html";
+
+// Mime type for as you type suggestions.
+static const char kSuggestionType[] = "application/x-suggestions+json";
+
+// Namespace identifier.
+static const char kOSDNS[] = "xmlns";
+
+// The namespace for documents we understand.
+static const char kNameSpace[] = "http://a9.com/-/spec/opensearch/1.1/";
+
+// Removes the namespace from the specified |name|, ex: os:Url -> Url.
+static void PruneNamespace(std::string* name) {
+  size_t index = name->find_first_of(":");
+  if (index != std::string::npos)
+    name->erase(0, index + 1);
+}
+
+//
+// To minimize memory overhead while parsing, a SAX style parser is used.
+// ParsingContext is used to maintain the state we're in the document
+// while parsing.
+class ParsingContext {
+ public:
+  // Enum of the known element types.
+  enum ElementType {
+    UNKNOWN,
+    OPEN_SEARCH_DESCRIPTION,
+    URL,
+    PARAM,
+    SHORT_NAME,
+    DESCRIPTION,
+    IMAGE,
+    LANGUAGE,
+    INPUT_ENCODING,
+  };
+
+  enum Method {
+    GET,
+    POST
+  };
+
+  // Key/value of a Param node.
+  typedef std::pair<std::string, std::string> Param;
+
+  ParsingContext(TemplateURLParser::ParameterFilter* parameter_filter,
+                 TemplateURL* url)
+      : url_(url),
+        parameter_filter_(parameter_filter),
+        method_(GET),
+        suggestion_method_(GET),
+        is_suggest_url_(false),
+        derive_image_from_url_(false) {
+    if (kElementNameToElementTypeMap == NULL)
+      InitMapping();
+  }
+
+  // Invoked when an element starts.
+  void PushElement(const std::string& element) {
+    ElementType type;
+    if (kElementNameToElementTypeMap->find(element) ==
+        kElementNameToElementTypeMap->end()) {
+      type = UNKNOWN;
+    } else {
+      type = (*kElementNameToElementTypeMap)[element];
+    }
+    elements_.push_back(type);
+  }
+
+  void PopElement() {
+    elements_.pop_back();
+  }
+
+  // Returns the current ElementType.
+  ElementType GetKnownType() {
+    if (elements_.size() == 2 && elements_[0] == OPEN_SEARCH_DESCRIPTION)
+      return elements_[1];
+
+    // We only expect PARAM nodes under the Url node
+    if (elements_.size() == 3 && elements_[0] == OPEN_SEARCH_DESCRIPTION &&
+        elements_[1] == URL && elements_[2] == PARAM)
+      return PARAM;
+
+    return UNKNOWN;
+  }
+
+  TemplateURL* template_url() { return url_; }
+
+  void AddImageRef(const std::wstring& type, int width, int height) {
+    if (width > 0 && height > 0)
+      current_image_.reset(new TemplateURL::ImageRef(type, width, height));
+  }
+
+  void EndImage() {
+    current_image_.reset();
+  }
+
+  void SetImageURL(const std::wstring& url) {
+    if (current_image_.get()) {
+      current_image_->url = GURL(WideToUTF8(url));
+      url_->add_image_ref(*current_image_);
+      current_image_.reset();
+    }
+  }
+
+  void ResetString() {
+    string_.clear();
+  }
+
+  void AppendString(const std::wstring& string) {
+    string_ += string;
+  }
+
+  const std::wstring& GetString() {
+    return string_;
+  }
+
+  void ResetExtraParams() {
+    extra_params_.clear();
+  }
+
+  void AddExtraParams(const std::string& key, const std::string& value) {
+    if (parameter_filter_ && !parameter_filter_->KeepParameter(key, value))
+      return;
+    extra_params_.push_back(Param(key, value));
+  }
+
+  const std::vector<Param>& extra_params() const { return extra_params_; }
+
+  void set_is_suggestion(bool value) { is_suggest_url_ = value; }
+  bool is_suggestion() const { return is_suggest_url_; }
+
+  TemplateURLParser::ParameterFilter* parameter_filter() const {
+    return parameter_filter_;
+  }
+
+  void set_derive_image_from_url(bool derive_image_from_url) {
+    derive_image_from_url_ = derive_image_from_url;
+  }
+
+  void set_method(Method method) { method_ = method; }
+  Method method() { return method_; }
+
+  void set_suggestion_method(Method method) { suggestion_method_ = method; }
+  Method suggestion_method() { return suggestion_method_; }
+
+  // Builds the image URL from the Template search URL if no image URL has been
+  // set.
+  void DeriveImageFromURL() {
+    if (derive_image_from_url_ &&
+        url_->GetFavIconURL().is_empty() && url_->url()) {
+      GURL url(WideToUTF8(url_->url()->url()));  // More url's please...
+      url_->SetFavIconURL(TemplateURL::GenerateFaviconURL(url));
+    }
+  }
+
+ private:
+  static void InitMapping() {
+    kElementNameToElementTypeMap = new std::map<std::string,ElementType>;
+    (*kElementNameToElementTypeMap)[kURLElement] = URL;
+    (*kElementNameToElementTypeMap)[kParamElement] = PARAM;
+    (*kElementNameToElementTypeMap)[kShortNameElement] = SHORT_NAME;
+    (*kElementNameToElementTypeMap)[kDescriptionElement] = DESCRIPTION;
+    (*kElementNameToElementTypeMap)[kImageElement] = IMAGE;
+    (*kElementNameToElementTypeMap)[kOpenSearchDescriptionElement] =
+        OPEN_SEARCH_DESCRIPTION;
+    (*kElementNameToElementTypeMap)[kFirefoxSearchDescriptionElement] =
+        OPEN_SEARCH_DESCRIPTION;
+    (*kElementNameToElementTypeMap)[kLanguageElement] =
+        LANGUAGE;
+    (*kElementNameToElementTypeMap)[kInputEncodingElement] =
+        INPUT_ENCODING;
+  }
+
+  // Key is UTF8 encoded.
+  static std::map<std::string,ElementType>* kElementNameToElementTypeMap;
+  // TemplateURL supplied to Read method. It's owned by the caller, so we
+  // don't need to free it.
+  TemplateURL* url_;
+  std::vector<ElementType> elements_;
+  scoped_ptr<TemplateURL::ImageRef> current_image_;
+
+  // Character content for the current element.
+  std::wstring string_;
+
+  TemplateURLParser::ParameterFilter* parameter_filter_;
+
+  // The list of parameters parsed in the Param nodes of a Url node.
+  std::vector<Param> extra_params_;
+
+  // The HTTP methods used.
+  Method method_;
+  Method suggestion_method_;
+
+  // If true, we are currently parsing a suggest URL, otherwise it is an HTML
+  // search.  Note that we don't need a stack as Url nodes cannot be nested.
+  bool is_suggest_url_;
+
+  // Whether we should derive the image from the URL (when images are data
+  // URLs).
+  bool derive_image_from_url_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ParsingContext);
+};
+
+//static
+std::map<std::string,ParsingContext::ElementType>*
+    ParsingContext::kElementNameToElementTypeMap = NULL;
+
+std::wstring XMLCharToWide(const xmlChar* value) {
+  return UTF8ToWide(std::string((const char*)value));
+}
+
+std::wstring XMLCharToWide(const xmlChar* value, int length) {
+  return UTF8ToWide(std::string((const char*)value, length));
+}
+
+std::string XMLCharToString(const xmlChar* value) {
+  return std::string((const char*)value);
+}
+
+// Returns true if input_encoding contains a valid input encoding string. This
+// doesn't verify that we have a valid encoding for the string, just that the
+// string contains characters that constitute a valid input encoding.
+bool IsValidEncodingString(const std::string& input_encoding) {
+  if (input_encoding.empty())
+    return false;
+
+  if (!IsAsciiAlpha(input_encoding[0]))
+    return false;
+
+  for (size_t i = 1, max = input_encoding.size(); i < max; ++i) {
+    char c = input_encoding[i];
+    if (!IsAsciiAlpha(c) && !IsAsciiDigit(c) && c != '.' && c != '_' &&
+        c != '-') {
+      return false;
+    }
+  }
+  return true;
+}
+
+void ParseURL(const xmlChar** atts, ParsingContext* context) {
+  if (!atts)
+    return;
+
+  TemplateURL* turl = context->template_url();
+  const xmlChar** attributes = atts;
+  std::wstring template_url;
+  bool is_post = false;
+  bool is_html_url = false;
+  bool is_suggest_url = false;
+  int index_offset = 1;
+  int page_offset = 1;
+
+  while (*attributes) {
+    std::string name(XMLCharToString(*attributes));
+    const xmlChar* value = attributes[1];
+    if (name == kURLTypeAttribute) {
+      std::string type = XMLCharToString(value);
+      is_html_url = (type == kHTMLType);
+      is_suggest_url = (type == kSuggestionType);
+    } else if (name == kURLTemplateAttribute) {
+      template_url = XMLCharToWide(value);
+    } else if (name == kURLIndexOffsetAttribute) {
+      index_offset = std::max(1, StringToInt(XMLCharToWide(value)));
+    } else if (name == kURLPageOffsetAttribute) {
+      page_offset = std::max(1, StringToInt(XMLCharToWide(value)));
+    } else if (name == kParamMethodAttribute) {
+      is_post = LowerCaseEqualsASCII(XMLCharToString(value), "post");
+    }
+    attributes += 2;
+  }
+  if (is_html_url) {
+    turl->SetURL(template_url, index_offset, page_offset);
+    context->set_is_suggestion(false);
+    if (is_post)
+      context->set_method(ParsingContext::POST);
+  } else if (is_suggest_url) {
+    turl->SetSuggestionsURL(template_url, index_offset, page_offset);
+    context->set_is_suggestion(true);
+    if (is_post)
+      context->set_suggestion_method(ParsingContext::POST);
+  }
+}
+
+void ParseImage(const xmlChar** atts, ParsingContext* context) {
+  if (!atts)
+    return;
+
+  const xmlChar** attributes = atts;
+  int width = 0;
+  int height = 0;
+  std::wstring type;
+  while (*attributes) {
+    std::string name(XMLCharToString(*attributes));
+    const xmlChar* value = attributes[1];
+    if (name == kImageTypeAttribute) {
+      type = XMLCharToWide(value);
+    } else if (name == kImageWidthAttribute) {
+      width = StringToInt(XMLCharToWide(value));
+    } else if (name == kImageHeightAttribute) {
+      height = StringToInt(XMLCharToWide(value));
+    }
+    attributes += 2;
+  }
+  if (width > 0 && height > 0 && !type.empty()) {
+    // Valid Image URL.
+    context->AddImageRef(type, width, height);
+  }
+}
+
+void ParseParam(const xmlChar** atts, ParsingContext* context) {
+  if (!atts)
+    return;
+
+  const xmlChar** attributes = atts;
+  std::wstring type;
+  std::string key, value;
+  while (*attributes) {
+    std::string name(XMLCharToString(*attributes));
+    const xmlChar* val = attributes[1];
+    if (name == kParamNameAttribute) {
+      key = XMLCharToString(val);
+    } else if (name == kParamValueAttribute) {
+      value = XMLCharToString(val);
+    }
+    attributes += 2;
+  }
+  if (!key.empty())
+    context->AddExtraParams(key, value);
+}
+
+static void AppendParamToQuery(const std::string& key,
+                               const std::string& value,
+                               std::string* query) {
+  if (!query->empty())
+   query->append("&");
+  if (!key.empty()) {
+   query->append(key);
+   query->append("=");
+  }
+  query->append(value);
+}
+
+void ProcessURLParams(ParsingContext* context) {
+  TemplateURL* t_url = context->template_url();
+  const TemplateURLRef* t_url_ref =
+      context->is_suggestion() ? t_url->suggestions_url() :
+                                 t_url->url();
+  if (!t_url_ref)
+    return;
+
+  if (!context->parameter_filter() && context->extra_params().empty())
+    return;
+
+  GURL url(WideToUTF8(t_url_ref->url()));
+  // If there is a parameter filter, parse the existing URL and remove any
+  // unwanted parameter.
+  TemplateURLParser::ParameterFilter* filter = context->parameter_filter();
+  std::string new_query;
+  bool modified = false;
+  if (filter) {
+    url_parse::Component query = url.parsed_for_possibly_invalid_spec().query;
+    url_parse::Component key, value;
+    const char* url_spec = url.spec().c_str();
+    while (url_parse::ExtractQueryKeyValue(url_spec, &query, &key, &value)) {
+      std::string key_str(url_spec, key.begin, key.len);
+      std::string value_str(url_spec, value.begin, value.len);
+      if (filter->KeepParameter(key_str, value_str)) {
+        AppendParamToQuery(key_str, value_str, &new_query);
+      } else {
+        modified = true;
+      }
+    }
+  }
+  if (!modified)
+    new_query = url.query();
+
+  // Add the extra parameters if any.
+  const std::vector<ParsingContext::Param>& params = context->extra_params();
+  if (!params.empty()) {
+    modified = true;
+    std::vector<ParsingContext::Param>::const_iterator iter;
+    for (iter = params.begin(); iter != params.end(); ++iter)
+      AppendParamToQuery(iter->first, iter->second, &new_query);
+  }
+
+  if (modified) {
+    GURL::Replacements repl;
+    repl.SetQueryStr(new_query);
+    url = url.ReplaceComponents(repl);
+    if (context->is_suggestion()) {
+      t_url->SetSuggestionsURL(UTF8ToWide(url.spec()),
+                               t_url_ref->index_offset(),
+                               t_url_ref->page_offset());
+    } else {
+      t_url->SetURL(UTF8ToWide(url.spec()),
+                    t_url_ref->index_offset(),
+                    t_url_ref->page_offset());
+    }
+  }
+}
+
+void StartElementImpl(void *ctx, const xmlChar *name, const xmlChar **atts) {
+  ParsingContext* context = reinterpret_cast<ParsingContext*>(ctx);
+  std::string node_name((const char*)name);
+  PruneNamespace(&node_name);
+  context->PushElement(node_name);
+  switch (context->GetKnownType()) {
+    case ParsingContext::URL:
+      context->ResetExtraParams();
+      ParseURL(atts, context);
+      break;
+    case ParsingContext::IMAGE:
+      ParseImage(atts, context);
+      break;
+    case ParsingContext::PARAM:
+      ParseParam(atts, context);
+      break;
+    default:
+      break;
+  }
+  context->ResetString();
+}
+
+void EndElementImpl(void *ctx, const xmlChar *name) {
+  ParsingContext* context = reinterpret_cast<ParsingContext*>(ctx);
+  switch (context->GetKnownType()) {
+    case ParsingContext::SHORT_NAME:
+      context->template_url()->set_short_name(context->GetString());
+      break;
+    case ParsingContext::DESCRIPTION:
+      context->template_url()->set_description(context->GetString());
+      break;
+    case ParsingContext::IMAGE: {
+      GURL image_url(WideToUTF8(context->GetString()));
+      if (image_url.SchemeIs("data")) {
+        // TODO (jcampan): bug 1169256: when dealing with data URL, we need to
+        // decode the data URL in the renderer. For now, we'll just point to the
+        // fav icon from the URL.
+        context->set_derive_image_from_url(true);
+      } else {
+        context->SetImageURL(context->GetString());
+      }
+      context->EndImage();
+      break;
+    }
+    case ParsingContext::LANGUAGE:
+      context->template_url()->add_language(context->GetString());
+      break;
+    case ParsingContext::INPUT_ENCODING: {
+      std::string input_encoding = WideToASCII(context->GetString());
+      if (IsValidEncodingString(input_encoding))
+        context->template_url()->add_input_encoding(input_encoding);
+      break;
+    }
+    case ParsingContext::URL:
+      ProcessURLParams(context);
+      break;
+    default:
+      break;
+  }
+  context->ResetString();
+  context->PopElement();
+}
+
+void CharactersImpl(void *ctx, const xmlChar *ch, int len) {
+  ParsingContext* context = reinterpret_cast<ParsingContext*>(ctx);
+  context->AppendString(XMLCharToWide(ch, len));
+}
+
+// Returns true if the ref is null, or the url wrapped by ref is
+// valid with a spec of http/https.
+bool IsHTTPRef(const TemplateURLRef* ref) {
+  if (ref == NULL)
+    return true;
+  GURL url(WideToUTF8(ref->url()));
+  return (url.is_valid() && (url.SchemeIs("http") || url.SchemeIs("https")));
+}
+
+// Returns true if the TemplateURL is legal. A legal TemplateURL is one
+// where all URLs have a spec of http/https.
+bool IsLegal(TemplateURL* url) {
+  if (!IsHTTPRef(url->url()) || !IsHTTPRef(url->suggestions_url()))
+    return false;
+  // Make sure all the image refs are legal.
+  const std::vector<TemplateURL::ImageRef>& image_refs = url->image_refs();
+  for (size_t i = 0; i < image_refs.size(); i++) {
+    GURL image_url(image_refs[i].url);
+    if (!image_url.is_valid() ||
+        !(image_url.SchemeIs("http") || image_url.SchemeIs("https"))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+} // namespace
+
+// static
+bool TemplateURLParser::Parse(const unsigned char* data, size_t length,
+                              TemplateURLParser::ParameterFilter* param_filter,
+                              TemplateURL* url) {
+  DCHECK(url);
+  // xmlSubstituteEntitiesDefault(1) makes it so that &amp; isn't mapped to
+  // &#38; . Unfortunately xmlSubstituteEntitiesDefault effects global state.
+  // If this becomes problematic we'll need to provide our own entity
+  // type for &amp;, or strip out &#34; by hand after parsing.
+  int last_sub_entities_value = xmlSubstituteEntitiesDefault(1);
+  ParsingContext context(param_filter, url);
+  xmlSAXHandler sax_handler;
+  memset(&sax_handler, 0, sizeof(sax_handler));
+  sax_handler.startElement = &StartElementImpl;
+  sax_handler.endElement = &EndElementImpl;
+  sax_handler.characters = &CharactersImpl;
+  xmlSAXUserParseMemory(&sax_handler, &context,
+                        reinterpret_cast<const char*>(data),
+                        static_cast<int>(length));
+  xmlSubstituteEntitiesDefault(last_sub_entities_value);
+  // If the image was a data URL, use the favicon from the search URL instead.
+  // (see TODO inEndElementImpl()).
+  context.DeriveImageFromURL();
+
+  // TODO(jcampan): http://b/issue?id=1196285 we do not support search engines
+  //                that use POST yet.
+  if (context.method() == ParsingContext::POST)
+    return false;
+  if (context.suggestion_method() == ParsingContext::POST)
+    url->SetSuggestionsURL(L"", 0, 0);
+
+  if (!url->short_name().empty() && !url->description().empty()) {
+    // So far so good, make sure the urls are http.
+    return IsLegal(url);
+  }
+  return false;
+}
+
+
diff --git a/chrome/browser/template_url_parser.h b/chrome/browser/template_url_parser.h
new file mode 100644
index 0000000..facf7c69
--- /dev/null
+++ b/chrome/browser/template_url_parser.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_TEMPLATE_URL_PARSER_H__
+#define CHROME_BROWSER_TEMPLATE_URL_PARSER_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+
+class TemplateURL;
+
+// TemplateURLParser, as the name implies, handling reading of TemplateURLs
+// from OpenSearch description documents.
+class TemplateURLParser {
+ public:
+  class ParameterFilter {
+   public:
+    // Invoked for each parameter of the template URL while parsing.  If this
+    // methods returns false, the parameter is not included.
+    virtual bool KeepParameter(const std::string& key,
+                               const std::string& value) = 0;
+  };
+  // Decodes the chunk of data representing a TemplateURL. If data does
+  // not describe a valid TemplateURL false is returned. Additionally, if the
+  // URLs referenced do not point to valid http/https resources, false is
+  // returned. |parameter_filter| can be used if you want to filter out some
+  // parameters out of the URL. For example when importing from another browser
+  // we remove any parameter identifying that browser.  If set to NULL, the URL
+  // is not modified.
+  //
+  // NOTE: This does not clear all values of the supplied TemplateURL; it's
+  // expected callers will supply a new TemplateURL to this method.
+  static bool Parse(const unsigned char* data,
+                    size_t length,
+                    ParameterFilter* parameter_filter,
+                    TemplateURL* url);
+
+ private:
+  // No one should create one of these.
+  TemplateURLParser();
+
+  DISALLOW_EVIL_CONSTRUCTORS(TemplateURLParser);
+};
+
+#endif // CHROME_BROWSER_TEMPLATE_URL_PARSER_H__
+
diff --git a/chrome/browser/template_url_parser_unittest.cc b/chrome/browser/template_url_parser_unittest.cc
new file mode 100644
index 0000000..381eba0a
--- /dev/null
+++ b/chrome/browser/template_url_parser_unittest.cc
@@ -0,0 +1,240 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_parser.h"
+#include "chrome/common/chrome_paths.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TemplateURLParserTest : public testing::Test {
+ public:
+  TemplateURLParserTest() : parse_result_(true) {
+  }
+
+  virtual void SetUp() {
+    ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &full_path_));
+    file_util::AppendToPath(&full_path_, L"osdd");
+    if (!file_util::PathExists(full_path_)) {
+      LOG(ERROR) <<
+          L"This test can't be run without some non-redistributable data";
+      full_path_.clear();
+    }
+  }
+
+  bool IsDisabled() {
+    return full_path_.empty();
+  }
+
+  // Parses the OpenSearch description document at file_name (relative to
+  // the data dir). The TemplateURL is placed in template_url_.
+  // The result of Parse is stored in the field parse_result_ (this doesn't
+  // use a return value due to internally using ASSERT_).
+  void ParseFile(const std::wstring& file_name,
+                 TemplateURLParser::ParameterFilter* filter) {
+    std::wstring full_path(full_path_);
+    file_util::AppendToPath(&full_path, file_name);
+    parse_result_ = false;
+    ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &full_path));
+    file_util::AppendToPath(&full_path, L"osdd");
+    file_util::AppendToPath(&full_path, file_name);
+    ASSERT_TRUE(file_util::PathExists(full_path));
+
+    std::string contents;
+    file_util::ReadFileToString(full_path, &contents);
+    parse_result_ = TemplateURLParser::Parse(
+        reinterpret_cast<const unsigned char*>(contents.c_str()),
+        contents.length(), filter, &template_url_);
+  }
+
+  // ParseFile parses the results into this template_url.
+  TemplateURL template_url_;
+
+  std::wstring full_path_;
+
+  // Result of the parse.
+  bool parse_result_;
+};
+
+TEST_F(TemplateURLParserTest, FailOnBogusURL) {
+  if (IsDisabled())
+    return;
+  ParseFile(L"bogus.xml", NULL);
+  EXPECT_FALSE(parse_result_);
+}
+
+TEST_F(TemplateURLParserTest, PassOnHTTPS) {
+  if (IsDisabled())
+    return;
+  ParseFile(L"https.xml", NULL);
+  EXPECT_TRUE(parse_result_);
+}
+
+TEST_F(TemplateURLParserTest, FailOnPost) {
+  if (IsDisabled())
+    return;
+  ParseFile(L"post.xml", NULL);
+  EXPECT_FALSE(parse_result_);
+}
+
+TEST_F(TemplateURLParserTest, TestDictionary) {
+  if (IsDisabled())
+    return;
+  ParseFile(L"dictionary.xml", NULL);
+  ASSERT_TRUE(parse_result_);
+  EXPECT_EQ(L"Dictionary.com", template_url_.short_name());
+  EXPECT_TRUE(template_url_.GetFavIconURL() ==
+              GURL("http://cache.lexico.com/g/d/favicon.ico"));
+  EXPECT_TRUE(template_url_.url() != NULL);
+  EXPECT_TRUE(template_url_.url()->SupportsReplacement());
+  EXPECT_EQ(template_url_.url()->url(),
+            L"http://dictionary.reference.com/browse/{searchTerms}?r=75");
+}
+
+TEST_F(TemplateURLParserTest, TestMSDN) {
+  if (IsDisabled())
+    return;
+  ParseFile(L"msdn.xml", NULL);
+  ASSERT_TRUE(parse_result_);
+  EXPECT_EQ(L"Search \" MSDN", template_url_.short_name());
+  EXPECT_TRUE(template_url_.GetFavIconURL() ==
+              GURL("http://search.msdn.microsoft.com/search/favicon.ico"));
+  EXPECT_TRUE(template_url_.url() != NULL);
+  EXPECT_TRUE(template_url_.url()->SupportsReplacement());
+  EXPECT_EQ(template_url_.url()->url(),
+            L"http://search.msdn.microsoft.com/search/default.aspx?Query={searchTerms}&brand=msdn&locale=en-US");
+}
+
+TEST_F(TemplateURLParserTest, TestWikipedia) {
+  if (IsDisabled())
+    return;
+  ParseFile(L"wikipedia.xml", NULL);
+  ASSERT_TRUE(parse_result_);
+  EXPECT_EQ(L"Wikipedia (English)", template_url_.short_name());
+  EXPECT_TRUE(template_url_.GetFavIconURL() ==
+              GURL("http://en.wikipedia.org/favicon.ico"));
+  EXPECT_TRUE(template_url_.url() != NULL);
+  EXPECT_TRUE(template_url_.url()->SupportsReplacement());
+  EXPECT_EQ(template_url_.url()->url(),
+      L"http://en.wikipedia.org/w/index.php?title=Special:Search&search={searchTerms}");
+  EXPECT_TRUE(template_url_.suggestions_url() != NULL);
+  EXPECT_TRUE(template_url_.suggestions_url()->SupportsReplacement());
+  EXPECT_EQ(template_url_.suggestions_url()->url(),
+      L"http://en.wikipedia.org/w/api.php?action=opensearch&search={searchTerms}");
+  ASSERT_EQ(2U, template_url_.input_encodings().size());
+  EXPECT_EQ("UTF-8", template_url_.input_encodings()[0]);
+  EXPECT_EQ("Shift_JIS", template_url_.input_encodings()[1]);
+}
+
+TEST_F(TemplateURLParserTest, NoCrashOnEmptyAttributes) {
+  if (IsDisabled())
+    return;
+  ParseFile(L"url_with_no_attributes.xml", NULL);
+}
+
+// Filters any param which as an occurrence of name_str_ in its name or an
+// occurrence of value_str_ in its value.
+class ParamFilterImpl : public TemplateURLParser::ParameterFilter {
+ public:
+  ParamFilterImpl(std::string name_str, std::string value_str)
+     : name_str_(name_str),
+       value_str_(value_str) {
+  }
+
+  bool KeepParameter(const std::string& key, const std::string& value) {
+    return (name_str_.empty() || key.find(name_str_) == std::string::npos) &&
+           (value_str_.empty() || value.find(value_str_) == std::string::npos);
+  }
+
+ private:
+  std::string name_str_;
+  std::string value_str_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ParamFilterImpl);
+};
+
+TEST_F(TemplateURLParserTest, TestFirefoxEbay) {
+  if (IsDisabled())
+    return;
+  // This file uses the Parameter extension
+  // (see http://www.opensearch.org/Specifications/OpenSearch/Extensions/Parameter/1.0)
+  ParamFilterImpl filter("ebay", "ebay");
+  ParseFile(L"firefox_ebay.xml", &filter);
+  ASSERT_TRUE(parse_result_);
+  EXPECT_EQ(L"eBay", template_url_.short_name());
+  EXPECT_TRUE(template_url_.url() != NULL);
+  EXPECT_TRUE(template_url_.url()->SupportsReplacement());
+  std::wstring exp_url =
+      L"http://search.ebay.com/search/search.dll?query={searchTerms}&"
+      L"MfcISAPICommand=GetResult&ht=1&srchdesc=n&maxRecordsReturned=300&"
+      L"maxRecordsPerPage=50&SortProperty=MetaEndSort";
+  EXPECT_EQ(exp_url, template_url_.url()->url());
+  ASSERT_EQ(1U, template_url_.input_encodings().size());
+  EXPECT_EQ("ISO-8859-1", template_url_.input_encodings()[0]);
+  EXPECT_EQ(GURL("http://search.ebay.com/favicon.ico"),
+            template_url_.GetFavIconURL());
+}
+
+TEST_F(TemplateURLParserTest, TestFirefoxWebster) {
+  if (IsDisabled())
+    return;
+  // This XML file uses a namespace.
+  ParamFilterImpl filter("", "Mozilla");
+  ParseFile(L"firefox_webster.xml", &filter);
+  ASSERT_TRUE(parse_result_);
+  EXPECT_EQ(L"Webster", template_url_.short_name());
+  EXPECT_TRUE(template_url_.url() != NULL);
+  EXPECT_TRUE(template_url_.url()->SupportsReplacement());
+  EXPECT_EQ(L"http://www.webster.com/cgi-bin/dictionary?va={searchTerms}",
+            template_url_.url()->url());
+  ASSERT_EQ(1U, template_url_.input_encodings().size());
+  EXPECT_EQ("ISO-8859-1", template_url_.input_encodings()[0]);
+  EXPECT_EQ(GURL("http://www.webster.com/favicon.ico"),
+            template_url_.GetFavIconURL());
+}
+
+TEST_F(TemplateURLParserTest, TestFirefoxYahoo) {
+  if (IsDisabled())
+    return;
+  // This XML file uses a namespace.
+  ParamFilterImpl filter("", "Mozilla");
+  ParseFile(L"firefox_yahoo.xml", &filter);
+  ASSERT_TRUE(parse_result_);
+  EXPECT_EQ(L"Yahoo", template_url_.short_name());
+  EXPECT_TRUE(template_url_.url() != NULL);
+  EXPECT_TRUE(template_url_.url()->SupportsReplacement());
+  EXPECT_EQ(L"http://ff.search.yahoo.com/gossip?"
+            L"output=fxjson&command={searchTerms}",
+            template_url_.suggestions_url()->url());
+  EXPECT_EQ(L"http://search.yahoo.com/search?p={searchTerms}&ei=UTF-8",
+            template_url_.url()->url());
+  ASSERT_EQ(1U, template_url_.input_encodings().size());
+  EXPECT_EQ("UTF-8", template_url_.input_encodings()[0]);
+  EXPECT_EQ(GURL("http://search.yahoo.com/favicon.ico"),
+            template_url_.GetFavIconURL());
+}
+
+// Make sure we ignore POST suggestions (this is the same XML file as
+// firefox_yahoo.xml, the suggestion method was just changed to POST).
+TEST_F(TemplateURLParserTest, TestPostSuggestion) {
+  if (IsDisabled())
+    return;
+  // This XML file uses a namespace.
+  ParamFilterImpl filter("", "Mozilla");
+  ParseFile(L"post_suggestion.xml", &filter);
+  ASSERT_TRUE(parse_result_);
+  EXPECT_EQ(L"Yahoo", template_url_.short_name());
+  EXPECT_TRUE(template_url_.url() != NULL);
+  EXPECT_TRUE(template_url_.url()->SupportsReplacement());
+  EXPECT_TRUE(template_url_.suggestions_url() == NULL);
+  EXPECT_EQ(L"http://search.yahoo.com/search?p={searchTerms}&ei=UTF-8",
+            template_url_.url()->url());
+  ASSERT_EQ(1U, template_url_.input_encodings().size());
+  EXPECT_EQ("UTF-8", template_url_.input_encodings()[0]);
+  EXPECT_EQ(GURL("http://search.yahoo.com/favicon.ico"),
+            template_url_.GetFavIconURL());
+}
diff --git a/chrome/browser/template_url_prepopulate_data.cc b/chrome/browser/template_url_prepopulate_data.cc
new file mode 100644
index 0000000..11ebf21
--- /dev/null
+++ b/chrome/browser/template_url_prepopulate_data.cc
@@ -0,0 +1,3082 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/template_url_prepopulate_data.h"
+
+#include "base/command_line.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#undef IN  // On Windows, windef.h defines this, which screws up "India" cases.
+
+using base::Time;
+
+namespace {
+
+// NOTE: See comments in GetDataVersion() below!  You should probably not change
+// the data in this file without changing the result of that function!
+
+// Engine definitions //////////////////////////////////////////////////////////
+
+struct PrepopulatedEngine {
+  const wchar_t* const name;
+  // If NULL, we'll autogenerate a keyword based on the search_url every time
+  // someone asks.  Only entries which need keywords to auto-track a dynamically
+  // generated search URL should use this.
+  // If the empty string, the engine has no keyword.
+  const wchar_t* const keyword;      
+  const wchar_t* const favicon_url;  // If NULL, there is no favicon.
+  const wchar_t* const search_url;
+  const char* const encoding;
+  const wchar_t* const suggest_url;  // If NULL, this engine does not support
+                                     // suggestions.
+  // Unique id for this prepopulate engine (corresponds to
+  // TemplateURL::prepopulate_id). This ID must be greater than zero and must
+  // remain the same for a particular site regardless of how the url changes;
+  // the ID is used when modifying engine data in subsequent versions, so that
+  // we can find the "old" entry to update even when the name or URL changes.
+  //
+  // This ID must be "unique" within one country's prepopulated data, but two
+  // entries can share an ID if they represent the "same" engine (e.g. Yahoo! US
+  // vs. Yahoo! UK) and will not appear in the same user-visible data set.  This
+  // facilitates changes like adding more specific per-country data in the
+  // future; in such a case the localized engines will transparently replace the
+  // previous, non-localized versions.  For engines where we need two instances
+  // to appear for one country (e.g. Live Search U.S. English and Spanish), we
+  // must use two different unique IDs (and different keywords).
+  //
+  // The following unique IDs are available: 66, 93, 103+
+  // NOTE: CHANGE THE ABOVE NUMBERS IF YOU ADD A NEW ENGINE; ID conflicts = bad!
+  const int id;
+};
+
+const PrepopulatedEngine abcsok = {
+  L"ABC S\x00f8k",
+  L"abcsok.no",
+  L"http://abcsok.no/favicon.ico",
+  L"http://abcsok.no/index.html?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  72,
+};
+
+const PrepopulatedEngine adonde = {
+  L"Adonde.com",
+  L"adonde.com",
+  L"http://www.adonde.com/favicon.ico",
+  L"http://www.adonde.com/peru/peru.html?sitesearch=adonde.com&"
+      L"client=pub-6263803831447773&ie={inputEncoding}&cof=GALT%3A%23CC0000"
+      L"%3BGL%3A1%3BDIV%3A%23E6E6E6%3BVLC%3A663399%3BAH%3Acenter%3BBGC%3AFFFFFF"
+      L"%3BLBGC%3AFFFFFF%3BALC%3A000000%3BLC%3A000000%3BT%3A0066CC%3BGFNT"
+      L"%3ACCCCCC%3BGIMP%3ACCCCCC%3BFORID%3A11&q={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  95,
+};
+
+const PrepopulatedEngine aeiou = {
+  L"AEIOU",
+  L"aeiou.pt",
+  L"http://aeiou.pt/favicon.ico",
+  L"http://aeiou.pt/pesquisa/index.php?p={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  79,
+};
+
+const PrepopulatedEngine aladin = {
+  L"Aladin",
+  L"aladin.info",
+  L"http://www.aladin.info/favicon.ico",
+  L"http://www.aladin.info/search/index.php?term={searchTerms}&req=search&"
+      L"source=2",
+  "UTF-8",
+  NULL,
+  18,
+};
+
+const PrepopulatedEngine altavista = {
+  L"AltaVista",
+  L"altavista.com",
+  L"http://www.altavista.com/favicon.ico",
+  L"http://www.altavista.com/web/results?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  89,
+};
+
+const PrepopulatedEngine altavista_ar = {
+  L"AltaVista",
+  L"ar.altavista.com",
+  L"http://ar.altavista.com/favicon.ico",
+  L"http://ar.altavista.com/web/results?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  89,
+};
+
+const PrepopulatedEngine altavista_es = {
+  L"AltaVista",
+  L"es.altavista.com",
+  L"http://es.altavista.com/favicon.ico",
+  L"http://es.altavista.com/web/results?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  89,
+};
+
+const PrepopulatedEngine altavista_mx = {
+  L"AltaVista",
+  L"mx.altavista.com",
+  L"http://mx.altavista.com/favicon.ico",
+  L"http://mx.altavista.com/web/results?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  89,
+};
+
+const PrepopulatedEngine altavista_se = {
+  L"AltaVista",
+  L"se.altavista.com",
+  L"http://se.altavista.com/favicon.ico",
+  L"http://se.altavista.com/web/results?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  89,
+};
+
+const PrepopulatedEngine aol = {
+  L"AOL",
+  L"aol.com",
+  L"http://search.aol.com/favicon.ico",
+  L"http://search.aol.com/aol/search?query={searchTerms}",
+  "UTF-8",
+  NULL,
+  35,
+};
+
+const PrepopulatedEngine aol_fr = {
+  L"AOL",
+  L"aol.fr",
+  L"http://www.aol.fr/favicon.ico",
+  L"http://www.recherche.aol.fr/aol/search?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  35,
+};
+
+const PrepopulatedEngine aonde = {
+  L"AONDE.com",
+  L"aonde.com",
+  L"http://busca.aonde.com/favicon.ico",
+  L"http://busca.aonde.com/?keys={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  80,
+};
+
+const PrepopulatedEngine araby = {
+  L"\x0639\x0631\x0628\x064a",
+  L"araby.com",
+  L"http://araby.com/favicon.ico",
+  L"http://araby.com/?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  12,
+};
+
+const PrepopulatedEngine ask = {
+  L"Ask",
+  L"ask.com",
+  L"http://www.ask.com/favicon.ico",
+  L"http://www.ask.com/web?q={searchTerms}",
+  "UTF-8",
+  L"http://ss.ask.com/query?q={searchTerms}&li=ff",
+  4,
+};
+
+const PrepopulatedEngine ask_de = {
+  L"Ask.com Deutschland",
+  L"de.ask.com",
+  L"http://de.ask.com/favicon.ico",
+  L"http://de.ask.com/web?q={searchTerms}",
+  "UTF-8",
+  L"http://ss.de.ask.com/query?q={searchTerms}&li=ff",
+  4,
+};
+
+const PrepopulatedEngine ask_es = {
+  L"Ask.com Espa" L"\x00f1" L"a",
+  L"es.ask.com",
+  L"http://es.ask.com/favicon.ico",
+  L"http://es.ask.com/web?q={searchTerms}",
+  "UTF-8",
+  L"http://ss.es.ask.com/query?q={searchTerms}&li=ff",
+  4,
+};
+
+const PrepopulatedEngine ask_it = {
+  L"Ask.com Italia",
+  L"it.ask.com",
+  L"http://it.ask.com/favicon.ico",
+  L"http://it.ask.com/web?q={searchTerms}",
+  "UTF-8",
+  L"http://ss.it.ask.com/query?q={searchTerms}&li=ff",
+  4,
+};
+
+const PrepopulatedEngine ask_uk = {
+  L"Ask.com UK",
+  L"uk.ask.com",
+  L"http://uk.ask.com/favicon.ico",
+  L"http://uk.ask.com/web?q={searchTerms}",
+  "UTF-8",
+  L"http://ss.uk.ask.com/query?q={searchTerms}&li=ff",
+  4,
+};
+
+const PrepopulatedEngine atlas_cz = {
+  L"Atlas",
+  L"atlas.cz",
+  L"http://img.atlas.cz/favicon.ico",
+  L"http://search.atlas.cz/?q={searchTerms}",
+  "windows-1250",
+  NULL,
+  27,
+};
+
+const PrepopulatedEngine atlas_sk = {
+  L"ATLAS.SK",
+  L"atlas.sk",
+  L"http://www.atlas.sk/images/favicon.ico",
+  L"http://hladaj.atlas.sk/fulltext/?phrase={searchTerms}",
+  "UTF-8",
+  NULL,
+  27,
+};
+
+const PrepopulatedEngine baidu = {
+  L"\x767e\x5ea6",
+  L"baidu.com",
+  L"http://www.baidu.com/favicon.ico",
+  L"http://www.baidu.com/s?wd={searchTerms}",
+  "GB2312",
+  NULL,
+  21,
+};
+
+const PrepopulatedEngine biglobe = {
+  L"BIGLOBE",
+  L"biglobe.ne.jp",
+  L"http://cgi.search.biglobe.ne.jp/favicon.ico",
+  L"http://cgi.search.biglobe.ne.jp/cgi-bin/search2-b?q={searchTerms}",
+  "Shift_JIS",
+  NULL,
+  64,
+};
+
+const PrepopulatedEngine bigmir = {
+  L"bigmir)net",
+  L"bigmir.net",
+  L"http://i.bigmir.net/favicon.ico",
+  L"http://search.bigmir.net/index.php?q={searchTerms}",
+  "windows-1251",
+  NULL,
+  33,
+};
+
+const PrepopulatedEngine bluewin = {
+  L"Bluewin",
+  L"search.bluewin.ch",
+  L"http://search.bluewin.ch/favicon.ico",
+  L"http://search.bluewin.ch/bw/search/web/de/result.jsp?query={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  52,
+};
+
+const PrepopulatedEngine centrum_cz = {
+  L"Centrum.cz",
+  L"centrum.cz",
+  L"http://img.centrum.cz/6/vy2/o/favicon.ico",
+  L"http://search.centrum.cz/index.php?charset={inputEncoding}&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  26,
+};
+
+const PrepopulatedEngine centrum_sk = {
+  L"Centrum.sk",
+  L"centrum.sk",
+  L"http://img.centrum.sk/4/favicon.ico",
+  L"http://search.centrum.sk/index.php?charset={inputEncoding}&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  26,
+};
+
+const PrepopulatedEngine conexcol = {
+  L"Conexcol.com",
+  L"conexcol.com",
+  L"http://www.conexcol.com/favicon.ico",
+  L"http://buscar.conexcol.com/cgi-ps/busqueda.cgi?query={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  91,
+};
+
+const PrepopulatedEngine daum = {
+  L"Daum",
+  L"daum.net",
+  L"http://search.daum.net/favicon.ico",
+  L"http://search.daum.net/search?q={searchTerms}",
+  "EUC-KR",
+   L"http://sug.search.daum.net/search_nsuggest?mod=fxjson&q={searchTerms}",
+  68,
+};
+
+const PrepopulatedEngine delfi_ee = {
+  L"DELFI",
+  L"delfi.ee",
+  L"http://g.delfi.ee/s/search.png",
+  L"http://otsing.delfi.ee/i.php?q={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  45,
+};
+
+const PrepopulatedEngine delfi_lt = {
+  L"DELFI",
+  L"delfi.lt",
+  L"http://search.delfi.lt/img/favicon.png",
+  L"http://search.delfi.lt/search.php?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  45,
+};
+
+const PrepopulatedEngine delfi_lv = {
+  L"DELFI",
+  L"delfi.lv",
+  L"http://smart.delfi.lv/img/smart_search.png",
+  L"http://smart.delfi.lv/i.php?enc={inputEncoding}&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  45,
+};
+
+const PrepopulatedEngine embla = {
+  L"Embla",
+  L"embla.is",
+  L"http://embla.is/favicon.ico",
+  L"http://embla.is/mm/embla/?s={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  60,
+};
+
+const PrepopulatedEngine empas = {
+  L"\xc5e0\xd30c\xc2a4",
+  L"empas.com",
+  L"http://search.empas.com/favicon.ico",
+  L"http://search.empas.com/search/all.html?q={searchTerms}",
+  "EUC-KR",
+  // http://www.empas.com/ac/do.tsp?q={searchTerms}
+  // returns non-Firefox JSON.  searchTerms needs to be in Java notation
+  // (\uAC00\uAC01).
+  NULL,
+  70,
+};
+
+const PrepopulatedEngine eniro_dk = {
+  L"Eniro",
+  L"eniro.dk",
+  L"http://eniro.dk/favicon.ico",
+  L"http://eniro.dk/query?search_word={searchTerms}&what=web_local",
+  "ISO-8859-1",
+  NULL,
+  29,
+};
+
+const PrepopulatedEngine eniro_fi = {
+  L"Eniro",
+  L"eniro.fi",
+  L"http://eniro.fi/favicon.ico",
+  L"http://eniro.fi/query?search_word={searchTerms}&what=web_local",
+  "ISO-8859-1",
+  NULL,
+  29,
+};
+
+const PrepopulatedEngine eniro_se = {
+  L"Eniro",
+  L"eniro.se",
+  L"http://eniro.se/favicon.ico",
+  L"http://eniro.se/query?search_word={searchTerms}&what=web_local",
+  "ISO-8859-1",
+  NULL,
+  29,
+};
+
+const PrepopulatedEngine finna = {
+  L"FINNA",
+  L"finna.is",
+  L"http://finna.is/favicon.ico",
+  L"http://finna.is/WWW_Search/?query={searchTerms}",
+  "UTF-8",
+  NULL,
+  61,
+};
+
+const PrepopulatedEngine fonecta_02_fi = {
+  L"Fonecta 02.fi",
+  L"www.fi",
+  L"http://www.02.fi/img/favicon.ico",
+  L"http://www.02.fi/haku/{searchTerms}",
+  "UTF-8",
+  NULL,
+  46,
+};
+
+const PrepopulatedEngine forthnet = {
+  L"Forthnet",
+  L"forthnet.gr",
+  L"http://search.forthnet.gr/favicon.ico",
+  L"http://search.forthnet.gr/cgi-bin/query?mss=search&q={searchTerms}",
+  "windows-1253",
+  NULL,
+  53,
+};
+
+const PrepopulatedEngine gigabusca = {
+  L"GiGaBusca",
+  L"gigabusca.com.br",
+  L"http://www.gigabusca.com.br/favicon.ico",
+  L"http://www.gigabusca.com.br/buscar.php?query={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  81,
+};
+
+const PrepopulatedEngine go = {
+  L"GO.com",
+  L"go.com",
+  L"http://search.yahoo.com/favicon.ico",
+  L"http://search.yahoo.com/search?ei={inputEncoding}&p={searchTerms}&"
+      L"fr=hsusgo1",
+  "ISO-8859-1",
+  NULL,
+  40,
+};
+
+const PrepopulatedEngine goo = {
+  L"goo",
+  L"goo.ne.jp",
+  L"http://goo.ne.jp/gooicon.ico",
+  L"http://search.goo.ne.jp/web.jsp?MT={searchTerms}&IE={inputEncoding}",
+  "UTF-8",
+  NULL,
+  92,
+};
+
+const PrepopulatedEngine google = {
+  L"Google",
+  NULL,
+  L"http://www.google.com/favicon.ico",
+  L"{google:baseURL}search?{google:RLZ}{google:acceptedSuggestion}"
+      L"{google:originalQueryForSuggestion}sourceid=chrome&ie={inputEncoding}&"
+      L"q={searchTerms}",
+  "UTF-8",
+  L"{google:baseSuggestURL}search?client=chrome&output=chrome&hl={language}&"
+      L"q={searchTerms}",
+  1,
+};
+
+const PrepopulatedEngine guruji = {
+  L"guruji",
+  L"guruji.com",
+  L"http://guruji.com/favicon.ico",
+  L"http://guruji.com/search?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  38,
+};
+
+const PrepopulatedEngine iafrica = {
+  L"iafrica.com",
+  L"iafrica.com",
+  NULL,
+  L"http://search.iafrica.com/search?q={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  43,
+};
+
+const PrepopulatedEngine ilse = {
+  L"Ilse",
+  L"ilse.nl",
+  L"http://search.ilse.nl/images/favicon.ico",
+  L"http://search.ilse.nl/web?search_for={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  30,
+};
+
+const PrepopulatedEngine in = {
+  L"in.gr",
+  L"in.gr",
+  L"http://www.in.gr/favicon.ico",
+  L"http://find.in.gr/result.asp?q={searchTerms}",
+  "ISO-8859-7",
+  NULL,
+  54,
+};
+
+const PrepopulatedEngine jabse = {
+  L"Jabse",
+  L"jabse.com",
+  L"http://www.jabse.com/favicon.ico",
+  L"http://www.jabse.com/searchmachine.php?query={searchTerms}",
+  "UTF-8",
+  NULL,
+  19,
+};
+
+const PrepopulatedEngine jamaicalive = {
+  L"JamaicaLive",
+  L"jalive.com.jm",
+  L"http://jalive.com.jm/favicon.ico",
+  L"http://jalive.com.jm/search/?mode=allwords&search={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  39,
+};
+
+const PrepopulatedEngine jubii = {
+  L"Jubii",
+  L"jubii.dk",
+  L"http://search.jubii.dk/favicon_jubii.ico",
+  L"http://search.jubii.dk/cgi-bin/pursuit?query={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  28,
+};
+
+const PrepopulatedEngine krstarica = {
+  L"Krstarica",
+  L"krstarica.rs",
+  L"http://pretraga.krstarica.com/favicon.ico",
+  L"http://pretraga.krstarica.com/index.php?q={searchTerms}",
+  "windows-1250",
+  NULL,
+  84,
+};
+
+const PrepopulatedEngine kvasir = {
+  L"Kvasir",
+  L"kvasir.no",
+  L"http://www.kvasir.no/img/favicon.ico",
+  L"http://www.kvasir.no/nettsok/searchResult.html?searchExpr={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  73,
+};
+
+const PrepopulatedEngine latne = {
+  L"LATNE",
+  L"latne.lv",
+  L"http://latne.lv/favicon.ico",
+  L"http://latne.lv/siets.php?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  71,
+};
+
+const PrepopulatedEngine leit = {
+  L"leit.is",
+  L"leit.is",
+  L"http://leit.is/leit.ico",
+  L"http://leit.is/query.aspx?qt={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  59,
+};
+
+const PrepopulatedEngine libero = {
+  L"Libero",
+  L"libero.it",
+  L"http://arianna.libero.it/favicon.ico",
+  L"http://arianna.libero.it/search/abin/integrata.cgi?query={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  63,
+};
+
+const PrepopulatedEngine live = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_ar_XA = {
+  L"Live Search (\x0627\x0644\x0639\x0631\x0628\x064a\x0629)",
+  L"",  // "live.com" is already taken by live_en_XA (see comment on ID below).
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?setlang=ar-XA&mkt=ar-XA&"
+      L"q={searchTerms}",
+  "UTF-8",
+  NULL,
+  7,  // Can't be 3 as this has to appear in the Arabian countries' lists
+      // alongside live_en_XA.
+};
+
+const PrepopulatedEngine live_bg_BG = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=bg-BG&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_cs_CZ = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=cs-CZ&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_el_GR = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=el-GR&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_en_ID = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=en_ID&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_en_NZ = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=en-NZ&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_en_US = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?setlang=en-US&mkt=en-US&"
+      L"q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_en_XA = {
+  L"Live Search (English)",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?setlang=en-XA&mkt=en-XA&"
+      L"q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_et_EE = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=et-EE&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_hr_HR = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=hr-HR&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_hu_HU = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=hu-HU&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_it_IT = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=it-IT&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_lt_LT = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=lt-LT&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_pl_PL = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=pl-PL&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_pt_PT = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=pt-PT&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_ro_RO = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=ro-RO&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_ru_RU = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=ru-RU&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_sk_SK = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=sk-SK&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_sl_SI = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=sl-SI&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine live_th_TH = {
+  L"Live Search",
+  L"live.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=th-TH&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine lycos_es = {
+  L"Lycos Espa" L"\x00f1" L"a",
+  L"lycos.es",
+  L"http://buscador.lycos.es/favicon.ico",
+  L"http://buscador.lycos.es/cgi-bin/pursuit?query={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  34,
+};
+
+const PrepopulatedEngine lycos_nl = {
+  L"Lycos",
+  L"lycos.nl",
+  L"http://zoek.lycos.nl/favicon.ico",
+  L"http://zoek.lycos.nl/cgi-bin/pursuit?query={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  34,
+};
+
+const PrepopulatedEngine mail_ru = {
+  L"@MAIL.RU",
+  L"mail.ru",
+  L"http://img.go.mail.ru/favicon.ico",
+  L"http://go.mail.ru/search?q={searchTerms}",
+  "windows-1251",
+  NULL,
+  83,
+};
+
+const PrepopulatedEngine maktoob = {
+  L"\x0645\x0643\x062a\x0648\x0628",
+  L"maktoob.com",
+  L"http://www.maktoob.com/favicon.ico",
+  L"http://www.maktoob.com/searchResult.php?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  13,
+};
+
+const PrepopulatedEngine masrawy = {
+  L"\x0645\x0635\x0631\x0627\x0648\x064a",
+  L"masrawy.com",
+  L"http://www.masrawy.com/new/images/masrawy.ico",
+  L"http://masrawy.com/new/search.aspx?sr={searchTerms}",
+  "windows-1256",
+  NULL,
+  14,
+};
+
+const PrepopulatedEngine matkurja = {
+  L"Mat'Kurja",
+  L"matkurja.com",
+  L"http://matkurja.com/favicon.ico",
+  L"http://matkurja.com/si/iskalnik/?q={searchTerms}&search_source=directory",
+  "ISO-8859-2",
+  NULL,
+  88,
+};
+
+const PrepopulatedEngine meta = {
+  L"<META>",
+  L"meta.ua",
+  L"http://meta.ua/favicon.ico",
+  L"http://meta.ua/search.asp?q={searchTerms}",
+  "windows-1251",
+  L"http://meta.ua/suggestions/?output=fxjson&oe=utf-8&q={searchTerms}",
+  102,
+};
+
+const PrepopulatedEngine msn = {
+  L"MSN",
+  L"msn.com",
+  L"http://search.msn.com/s/wlflag.ico",
+  L"http://search.msn.com/results.aspx?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_ar_XA = {
+  L"MSN (\x0627\x0644\x0639\x0631\x0628\x064a\x0629)",
+  L"",  // "arabia.msn.com" is already taken by msn_en_XA (see comment on ID
+        // below).
+  L"http://search.msn.com/s/wlflag.ico",
+  L"http://search.msn.com/results.aspx?setlang=ar-XA&mkt=ar-XA&"
+      L"q={searchTerms}",
+  "UTF-8",
+  NULL,
+  7,  // Can't be 3 as this has to appear in the Arabian countries' lists
+      // alongside msn_en_XA.
+};
+
+const PrepopulatedEngine msn_da_DK = {
+  L"MSN Danmark",
+  L"dk.msn.com",
+  L"http://search.msn.dk/s/wlflag.ico",
+  L"http://search.msn.dk/results.aspx?mkt=da-DK&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_de_AT = {
+  L"MSN \x00d6sterreich",
+  L"at.msn.com",
+  L"http://search.msn.at/s/wlflag.ico",
+  L"http://search.msn.at/results.aspx?mkt=de-AT&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_de_CH = {
+  L"MSN Schweiz (Deutsch)",
+  L"ch.msn.com",
+  L"http://search.msn.ch/s/wlflag.ico",
+  L"http://search.msn.ch/results.aspx?setlang=de-CH&mkt=de-CH&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_de_DE = {
+  L"MSN",
+  L"de.msn.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=de-DE&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_en_AU = {
+  L"ninemsn.com.au",
+  L"ninemsn.com.au",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=en-AU&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_en_CA = {
+  L"Sympatico / MSN (English)",
+  L"sympatico.msn.ca",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?setlang=en-CA&mkt=en-CA&"
+      L"q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_en_GB = {
+  L"MSN UK",
+  L"uk.msn.com",
+  L"http://search.msn.co.uk/s/wlflag.ico",
+  L"http://search.msn.co.uk/results.aspx?mkt=en-GB&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_en_IE = {
+  L"MSN IE",
+  L"ie.msn.com",
+  L"http://search.msn.ie/s/wlflag.ico",
+  L"http://search.msn.ie/results.aspx?mkt=en-IE&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_en_IN = {
+  L"MSN India",
+  L"in.msn.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=en-IN&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_en_MY = {
+  L"MSN Malaysia",
+  L"malaysia.msn.com",
+  L"http://search.msn.com.my/s/wlflag.ico",
+  L"http://search.msn.com.my/results.aspx?mkt=en-MY&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_en_PH = {
+  L"MSN Philippines",
+  L"ph.msn.com",
+  L"http://search.msn.com.ph/s/wlflag.ico",
+  L"http://search.msn.com.ph/results.aspx?mkt=en-PH&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_en_SG = {
+  L"MSN Singapore",
+  L"sg.msn.com",
+  L"http://search.msn.com.sg/s/wlflag.ico",
+  L"http://search.msn.com.sg/results.aspx?mkt=en-SG&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_en_XA = {
+  L"MSN (English)",
+  L"arabia.msn.com",
+  L"http://search.msn.com/s/wlflag.ico",
+  L"http://search.msn.com/results.aspx?setlang=en-XA&mkt=en-XA&"
+      L"q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_en_ZA = {
+  L"MSN ZA",
+  L"za.msn.com",
+  L"http://search.msn.co.za/s/wlflag.ico",
+  L"http://search.msn.co.za/results.aspx?mkt=en-ZA&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_es_AR = {
+  L"MSN Argentina",
+  L"ar.msn.com",
+  L"http://search.msn.com/s/wlflag.ico",
+  L"http://search.msn.com/results.aspx?mkt=es-AR&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_es_CL = {
+  L"MSN Chile",
+  L"cl.msn.com",
+  L"http://search.msn.com/s/wlflag.ico",
+  L"http://search.msn.com/results.aspx?mkt=es-CL&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_es_CO = {
+  L"MSN Colombia",
+  L"co.msn.com",
+  L"http://search.msn.com/s/wlflag.ico",
+  L"http://search.msn.com/results.aspx?mkt=es-CO&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_es_ES = {
+  L"MSN Espa" L"\x00f1" L"a",
+  L"es.msn.com",
+  L"http://search.msn.es/s/wlflag.ico",
+  L"http://search.msn.es/results.aspx?mkt=es-ES&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_es_MX = {
+  L"Prodigy / MSN",
+  L"prodigy.msn.com",
+  L"http://search.prodigy.msn.com/s/wlflag.ico",
+  L"http://search.prodigy.msn.com/results.aspx?mkt=es-MX&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_es_XL = {
+  L"MSN Latinoam\x00e9rica",
+  L"latam.msn.com",
+  L"http://search.msn.com/s/wlflag.ico",
+  L"http://search.msn.com/results.aspx?mkt=es-XL&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_fi_FI = {
+  L"MSN",
+  L"fi.msn.com",
+  L"http://search.msn.fi/s/wlflag.ico",
+  L"http://search.msn.fi/results.aspx?mkt=fi-FI&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_fr_BE = {
+  L"MSN Belgique (Fran" L"\x00e7" L"ais)",
+  L"",  // "be.msn.com" is already taken by msn_nl_BE (see comment on ID below).
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?setlang=fr-BE&mkt=fr-BE&"
+      L"q={searchTerms}",
+  "UTF-8",
+  NULL,
+  8,  // Can't be 3 as this has to appear in the Belgium list alongside
+      // msn_nl_BE.
+};
+
+const PrepopulatedEngine msn_fr_CA = {
+  L"Sympatico / MSN (Fran" L"\x00e7" L"ais)",
+  L"",  // "sympatico.msn.ca" is already taken by msn_en_CA (see comment on ID
+        // below).
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?setlang=fr-CA&mkt=fr-CA&"
+      L"q={searchTerms}",
+  "UTF-8",
+  NULL,
+  9,  // Can't be 3 as this has to appear in the Canada list alongside
+      // msn_en_CA.
+};
+
+const PrepopulatedEngine msn_fr_CH = {
+  L"MSN Suisse (Fran" L"\x00e7" L"ais)",
+  L"",  // "ch.msn.com" is already taken by msn_de_CH (see comment on ID below).
+  L"http://search.msn.ch/s/wlflag.ico",
+  L"http://search.msn.ch/results.aspx?setlang=fr-CH&mkt=fr-CH&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  10,  // Can't be 3 as this has to appear in the Switzerland list alongside
+       // msn_de_CH.
+};
+
+const PrepopulatedEngine msn_fr_FR = {
+  L"MSN France",
+  L"fr.msn.com",
+  L"http://search.msn.fr/s/wlflag.ico",
+  L"http://search.msn.fr/results.aspx?mkt=fr-FR&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_he_IL = {
+  L"msn.co.il",
+  L"msn.co.il",
+  L"http://msn.co.il/favicon.ico",
+  L"http://search.msn.co.il/Search.aspx?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_ja_JP = {
+  L"MSN Japan",
+  L"jp.msn.com",
+  L"http://search.msn.co.jp/s/wlflag.ico",
+  L"http://search.msn.co.jp/results.aspx?mkt=ja-JP&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_nb_NO = {
+  L"MSN Norge",
+  L"no.msn.com",
+  L"http://search.msn.no/s/wlflag.ico",
+  L"http://search.msn.no/results.aspx?mkt=nb-NO&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_nl_BE = {
+  L"MSN (Nederlandstalige)",
+  L"be.msn.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?setlang=nl-BE&mkt=nl-BE&"
+      L"q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_nl_NL = {
+  L"MSN.nl",
+  L"nl.msn.com",
+  L"http://search.msn.nl/s/wlflag.ico",
+  L"http://search.msn.nl/results.aspx?mkt=nl-NL&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_pt_BR = {
+  L"MSN Brasil",
+  L"br.msn.com",
+  L"http://search.live.com/s/wlflag.ico",
+  L"http://search.live.com/results.aspx?mkt=pt-BR&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_sv_SE = {
+  L"MSN",
+  L"se.msn.com",
+  L"http://search.msn.se/s/wlflag.ico",
+  L"http://search.msn.se/results.aspx?mkt=pv-SE&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_tr_TR = {
+  L"MSN T\x00fckiye'ye",
+  L"tr.msn.com",
+  L"http://search.msn.com.tr/s/wlflag.ico",
+  L"http://search.msn.com.tr/results.aspx?mkt=tr-TR&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine msn_zh_HK = {
+  L"MSN Hong Kong",
+  L"hk.msn.com",
+  L"http://search.msn.com.hk/s/wlflag.ico",
+  L"http://search.msn.com.hk/results.aspx?mkt=zh-HK&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  3,
+};
+
+const PrepopulatedEngine mweb = {
+  L"MWEB",
+  L"mweb.co.za",
+  L"http://mweb.co.za/favicon.ico",
+  L"http://search.mweb.co.za/search?&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  42,
+};
+
+const PrepopulatedEngine mynet = {
+  L"MYNET",
+  L"mynet.com",
+  L"http://img.mynet.com/mynetfavori.ico",
+  L"http://arama.mynet.com/search.aspx?q={searchTerms}&pg=q",
+  "windows-1254",
+  NULL,
+  101,
+};
+
+const PrepopulatedEngine mywebsearch = {
+  L"mywebsearch",
+  L"mywebsearch.com",
+  NULL,
+  L"http://search.mywebsearch.com/mywebsearch/AJmain.jhtml?"
+      L"searchfor={searchTerms}",
+  "UTF-8",
+  NULL,
+  97,
+};
+
+const PrepopulatedEngine najdi = {
+  L"Najdi.si",
+  L"najdi.si",
+  L"http://www.najdi.si/master/favicon.ico",
+  L"http://www.najdi.si/search.jsp?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  87,
+};
+
+const PrepopulatedEngine nana10 = {
+  L"\x05e0\x05e2\x05e0\x05e2 10",
+  L"nana10.co.il",
+  L"http://f.nau.co.il/Common/Includes/Favicon.ico",
+  L"http://index.nana10.co.il/search.asp?q={searchTerms}",
+  "windows-1255",
+  NULL,
+  56,
+};
+
+const PrepopulatedEngine nate = {
+  L"\xb124\xc774\xd2b8\xb2f7\xcef4",
+  L"nate.com",
+  L"http://nate.search.empas.com/favicon.ico",
+  L"http://nate.search.empas.com/search/all.html?q={searchTerms}",
+  "EUC-KR",
+  NULL,
+  69,
+};
+
+const PrepopulatedEngine naver = {
+  L"\xb124\xc774\xbc84",
+  L"naver.com",
+  L"http://search.naver.com/favicon.ico",
+  L"http://search.naver.com/search.naver?ie={inputEncoding}"
+      L"&query={searchTerms}",
+  "UTF-8",
+  L"http://ac.search.naver.com/autocompl?m=s&ie={inputEncoding}&oe=utf-8&"
+      L"q={searchTerms}",
+  67,
+};
+
+const PrepopulatedEngine neti = {
+  L"NETI",
+  L"neti.ee",
+  L"http://www.neti.ee/favicon.ico",
+  L"http://www.neti.ee/cgi-bin/otsing?query={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  44,
+};
+
+const PrepopulatedEngine netindex = {
+  L"NetINDEX",
+  L"netindex.pt",
+  L"http://www.netindex.pt/favicon.ico",
+  L"http://www.netindex.pt/cgi-bin/index.cgi?question={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  78,
+};
+
+const PrepopulatedEngine nifty = {
+  L"@nifty",
+  L"nifty.com",
+  L"http://www.nifty.com/favicon.ico",
+  L"http://search.nifty.com/cgi-bin/search.cgi?Text={searchTerms}",
+  "Shift_JIS",
+  NULL,
+  65,
+};
+
+const PrepopulatedEngine ohperu = {
+  L"Oh Per\x00fa",
+  L"ohperu.com",
+  NULL,
+  L"http://www.google.com.pe/custom?q={searchTerms}&"
+      L"client=pub-1950414869696311&ie={inputEncoding}&cof=GALT%3A%23000000"
+      L"%3BGL%3A1%3BDIV%3A%23FFFFFF%3BVLC%3A000000%3BAH%3Acenter%3BBGC%3AFFFFFF"
+      L"%3BLBGC%3AFFFFFF%3BALC%3A000000%3BLC%3A000000%3BT%3A000000%3BGFNT"
+      L"%3A000000%3BGIMP%3A000000%3BLH%3A50%3BLW%3A142%3BL%3Ahttp%3A%2F%2F"
+      L"www.ohperu.com%2Fohperu-logo-inv2.gif%3BS%3Ahttps%3A%2F%2Fblue-sea-697d.quartiers047.workers.dev%3A443%2Fhttp%2Fwww.ohperu.com"
+      L"%3BFORID%3A1",
+  "ISO-8859-1",
+  NULL,
+  96,
+};
+
+const PrepopulatedEngine ok = {
+  L"OK.hu",
+  L"ok.hu",
+  L"http://ok.hu/gfx/favicon.ico",
+  L"http://ok.hu/katalogus?q={searchTerms}",
+  "ISO-8859-2",
+  NULL,
+  6,
+};
+
+const PrepopulatedEngine onet = {
+  L"Onet.pl",
+  L"onet.pl",
+  L"http://szukaj.onet.pl/favicon.ico",
+  L"http://szukaj.onet.pl/query.html?qt={searchTerms}",
+  "ISO-8859-2",
+  NULL,
+  75,
+};
+
+const PrepopulatedEngine orange = {
+  L"Orange",
+  L"orange.fr",
+  L"http://www.orange.fr/favicon.ico",
+  L"http://rws.search.ke.voila.fr/RW/S/opensearch_orange?rdata={searchTerms}",
+  "ISO-8859-1",
+  L"http://search.ke.voila.fr/fr/cmplopensearch/xml/fullxml?"
+  L"rdata={searchTerms}",
+  48,
+};
+
+const PrepopulatedEngine ozu = {
+  L"OZ\x00da",
+  L"ozu.es",
+  L"http://www.ozu.es/favicon.ico",
+  L"http://buscar.ozu.es/index.php?q={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  98,
+};
+
+const PrepopulatedEngine pogodak_ba = {
+  L"Pogodak!",
+  L"pogodak.ba",
+  L"http://www.pogodak.ba/favicon.ico",
+  L"http://www.pogodak.ba/search.jsp?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  24,
+};
+
+const PrepopulatedEngine pogodak_hr = {
+  L"Pogodak!",
+  L"pogodak.hr",
+  L"http://www.pogodak.hr/favicon.ico",
+  L"http://www.pogodak.hr/search.jsp?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  24,
+};
+
+const PrepopulatedEngine pogodak_rs = {
+  L"Pogodak!",
+  L"pogodak.rs",
+  L"http://www.pogodak.rs/favicon.ico",
+  L"http://www.pogodak.rs/search.jsp?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  24,
+};
+
+const PrepopulatedEngine pogodok = {
+  L"\x041f\x043e\x0433\x043e\x0434\x043e\x043a!",
+  L"pogodok.com.mk",
+  L"http://www.pogodok.com.mk/favicon.ico",
+  L"http://www.pogodok.com.mk/search.jsp?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  24,  // Really the same engine as Pogodak, just has a small name change.
+};
+
+const PrepopulatedEngine rambler = {
+  L"Rambler",
+  L"rambler.ru",
+  L"http://www.rambler.ru/favicon.ico",
+  L"http://www.rambler.ru/srch?words={searchTerms}",
+  "windows-1251",
+  NULL,
+  16,
+};
+
+const PrepopulatedEngine rediff = {
+  L"Rediff",
+  L"rediff.com",
+  L"http://search1.rediff.com/favicon.ico",
+  L"http://search1.rediff.com/dirsrch/default.asp?MT={searchTerms}",
+  "UTF-8",
+  NULL,
+  37,
+};
+
+const PrepopulatedEngine rednano = {
+  L"Rednano",
+  L"rednano.sg",
+  L"http://rednano.sg/favicon.ico",
+  L"http://rednano.sg/sfe/lwi.action?querystring={searchTerms}",
+  "UTF-8",
+  NULL,
+  41,
+};
+
+const PrepopulatedEngine sanook = {
+  L"\x0e2a\x0e19\x0e38\x0e01!",
+  L"sanook.com",
+  L"http://search.sanook.com/favicon.ico",
+  L"http://search.sanook.com/search.php?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  100,
+};
+
+const PrepopulatedEngine sapo = {
+  L"SAPO",
+  L"sapo.pt",
+  L"http://imgs.sapo.pt/images/sapo.ico",
+  L"http://pesquisa.sapo.pt/?q={searchTerms}",
+  "UTF-8",
+  L"http://pesquisa.sapo.pt/livesapo?q={searchTerms}",
+  77,
+};
+
+const PrepopulatedEngine search_ch = {
+  L"search.ch",
+  L"search.ch",
+  L"http://www.search.ch/favicon.ico",
+  L"http://www.search.ch/?q={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  51,
+};
+
+const PrepopulatedEngine sensis = {
+  L"sensis.com.au",
+  L"sensis.com.au",
+  L"http://www.sensis.com.au/favicon.ico",
+  L"http://www.sensis.com.au/search.do?find={searchTerms}",
+  "UTF-8",
+  NULL,
+  32,
+};
+
+const PrepopulatedEngine sesam = {
+  L"Sesam",
+  L"sesam.no",
+  L"http://sesam.no/images/favicon.gif",
+  L"http://sesam.no/search/?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  74,
+};
+
+const PrepopulatedEngine seznam = {
+  L"Seznam",
+  L"seznam.cz",
+  L"http://1.im.cz/szn/img/favicon.ico",
+  L"http://search.seznam.cz/?q={searchTerms}",
+  "UTF-8",
+  L"http:///suggest.fulltext.seznam.cz/?dict=fulltext_ff&phrase={searchTerms}&"
+      L"encoding={inputEncoding}&response_encoding=utf-8",
+  25,
+};
+
+const PrepopulatedEngine sogou = {
+  L"\x641c\x72d7",
+  L"sogou.com",
+  L"http://www.sogou.com/favicon.ico",
+  L"http://www.sogou.com/web?query={searchTerms}",
+  "GB2312",
+  NULL,
+  20,
+};
+
+const PrepopulatedEngine soso = {
+  L"\x641c\x641c",
+  L"soso.com",
+  L"http://www.soso.com/favicon.ico",
+  L"http://www.soso.com/q?w={searchTerms}",
+  "GB2312",
+  NULL,
+  22,
+};
+
+const PrepopulatedEngine spray = {
+  L"Spray",
+  L"spray.se",
+  L"http://www.eniro.se/favicon.ico",
+  L"http://www.eniro.se/query?ax=spray&search_word={searchTerms}&what=web",
+  "ISO-8859-1",
+  NULL,
+  99,
+};
+
+const PrepopulatedEngine szm = {
+  L"SZM.sk",
+  L"szm.sk",
+  L"http://szm.sk/favicon.ico",
+  L"http://szm.sk/search/?co=1&q={searchTerms}",
+  "windows-1250",
+  NULL,
+  86,
+};
+
+const PrepopulatedEngine t_online = {
+  L"T-Online",
+  L"suche.t-online.de",
+  L"http://suche.t-online.de/favicon.ico",
+  L"http://suche.t-online.de/fast-cgi/tsc?sr=chrome&q={searchTerms}",
+  "UTF-8",
+  NULL,
+  49,
+};
+
+const PrepopulatedEngine tango = {
+  L"Tango",
+  L"tango.hu",
+  L"http://tango.hu/favicon.ico",
+  L"http://tango.hu/search.php?q={searchTerms}",
+  "windows-1250",
+  NULL,
+  58,
+};
+
+const PrepopulatedEngine tapuz = {
+  L"\x05ea\x05e4\x05d5\x05d6 \x05d0\x05e0\x05e9\x05d9\x05dd",
+  L"tapuz.co.il",
+  L"http://www.tapuz.co.il/favicon.ico",
+  L"http://www.tapuz.co.il/search/search.asp?q={searchTerms}",
+  "windows-1255",
+  NULL,
+  57,
+};
+
+const PrepopulatedEngine terra_ar = {
+  L"Terra Argentina",
+  L"terra.com.ar",
+  L"http://buscar.terra.com.ar/favicon.ico",
+  L"http://buscar.terra.com.ar/Default.aspx?query={searchTerms}&source=Search",
+  "ISO-8859-1",
+  NULL,
+  90,
+};
+
+const PrepopulatedEngine terra_ec = {
+  L"Terra Ecuador",
+  L"terra.com.ec",
+  L"http://buscador.terra.com.ec/favicon.ico",
+  L"http://buscador.terra.com.ec/Default.aspx?query={searchTerms}&"
+      L"source=Search",
+  "ISO-8859-1",
+  NULL,
+  90,
+};
+
+const PrepopulatedEngine terra_es = {
+  L"Terra",
+  L"terra.es",
+  L"http://buscador.terra.es/favicon.ico",
+  L"http://buscador.terra.es/Default.aspx?query={searchTerms}&source=Search",
+  "ISO-8859-1",
+  NULL,
+  90,
+};
+
+const PrepopulatedEngine terra_mx = {
+  L"Terra",
+  L"terra.com.mx",
+  L"http://buscador.terra.com.mx/favicon.ico",
+  L"http://buscador.terra.com.mx/Default.aspx?query={searchTerms}&"
+      L"source=Search",
+  "ISO-8859-1",
+  NULL,
+  90,
+};
+
+const PrepopulatedEngine terra_pe = {
+  L"Terra",
+  L"terra.com.pe",
+  L"http://buscador.terra.com.pe/favicon.ico",
+  L"http://buscador.terra.com.pe/Default.aspx?query={searchTerms}&"
+      L"source=Search",
+  "ISO-8859-1",
+  NULL,
+  90,
+};
+
+const PrepopulatedEngine toile = {
+  L"La Toile du Qu" L"\x00e9" L"bec",
+  L"toile.com",
+  L"http://static.search.canoe.ca/s-toile/img/favicon_toile.ico",
+  L"http://www.toile.com/search?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  36,
+};
+
+const PrepopulatedEngine tut = {
+  L"TUT.BY",
+  L"tut.by",
+  L"http://www.tut.by/favicon.ico",
+  L"http://search.tut.by/?query={searchTerms}",
+  "windows-1251",
+  NULL,
+  17,
+};
+
+const PrepopulatedEngine uol = {
+  L"UOL Busca",
+  L"busca.uol.com.br",
+  L"http://busca.uol.com.br/favicon.ico",
+  L"http://busca.uol.com.br/www/index.html?q={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  82,
+};
+
+const PrepopulatedEngine vinden = {
+  L"Vinden.nl",
+  L"vinden.nl",
+  L"http://www.vinden.nl/favicon.ico",
+  L"http://www.vinden.nl/?q={searchTerms}",
+  "UTF-8",
+  NULL,
+  31,
+};
+
+const PrepopulatedEngine virgilio = {
+  L"Virgilio",
+  L"virgilio.alice.it",
+  L"http://ricerca.alice.it/favicon.ico",
+  L"http://ricerca.alice.it/ricerca?qs={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  62,
+};
+
+const PrepopulatedEngine voila = {
+  L"Voila",
+  L"voila.fr",
+  L"http://search.ke.voila.fr/favicon.ico",
+  L"http://rws.search.ke.voila.fr/RW/S/opensearch_voila?rdata={searchTerms}",
+  "ISO-8859-1",
+  L"http://search.ke.voila.fr/fr/cmplopensearch/xml/fullxml?"
+  L"rdata={searchTerms}",
+  47,
+};
+
+const PrepopulatedEngine walla = {
+  L"\x05d5\x05d5\x05d0\x05dc\x05d4!",
+  L"walla.co.il",
+  L"http://www.walla.co.il/favicon.ico",
+  L"http://search.walla.co.il/?e=hew&q={searchTerms}",
+  "windows-1255",
+  NULL,
+  55,
+};
+
+const PrepopulatedEngine web_de = {
+  L"WEB.DE",
+  L"web.de",
+  L"http://img.ui-portal.de/search/img/webde/favicon.ico",
+  L"http://suche.web.de/search/web/?su={searchTerms}",
+  "ISO-8859-1",
+  NULL,
+  50,
+};
+
+const PrepopulatedEngine wp = {
+  L"Wirtualna Polska",
+  L"wp.pl",
+  L"http://szukaj.wp.pl/favicon.ico",
+  L"http://szukaj.wp.pl/szukaj.html?szukaj={searchTerms}",
+  "ISO-8859-2",
+  NULL,
+  76,
+};
+
+const PrepopulatedEngine yagua = {
+  L"Yagua.com",
+  L"yagua.com",
+  L"http://yagua.paraguay.com/favicon.ico",
+  L"http://yagua.paraguay.com/buscador.php?q={searchTerms}&cs={inputEncoding}",
+  "ISO-8859-1",
+  NULL,
+  94,
+};
+
+const PrepopulatedEngine yahoo = {
+  L"Yahoo!",
+  L"yahoo.com",
+  L"http://search.yahoo.com/favicon.ico",
+  L"http://search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+  "UTF-8",
+  L"http://ff.search.yahoo.com/gossip?output=fxjson&command={searchTerms}",
+  2,
+};
+
+// For regional Yahoo variants without region-specific suggestion service,
+// suggestion is disabled. For some of them, we might consider
+// using a fallback (e.g. de for at/ch, ca or fr for qc, en for nl, no, hk).
+const PrepopulatedEngine yahoo_ar = {
+  L"Yahoo! Argentina",
+  L"ar.yahoo.com",
+  L"http://ar.search.yahoo.com/favicon.ico",
+  L"http://ar.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://ar-sayt.ff.search.yahoo.com/gossip-ar-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_at = {
+  L"Yahoo! Suche",
+  L"at.yahoo.com",
+  L"http://at.search.yahoo.com/favicon.ico",
+  L"http://at.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  NULL,
+  2,
+};
+
+const PrepopulatedEngine yahoo_au = {
+  L"Yahoo!7",
+  L"au.yahoo.com",
+  L"http://au.search.yahoo.com/favicon.ico",
+  L"http://au.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://aue-sayt.ff.search.yahoo.com/gossip-au-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_br = {
+  L"Yahoo! Brasil",
+  L"br.yahoo.com",
+  L"http://br.search.yahoo.com/favicon.ico",
+  L"http://br.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://br-sayt.ff.search.yahoo.com/gossip-br-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_ca = {
+  L"Yahoo! Canada",
+  L"ca.yahoo.com",
+  L"http://ca.search.yahoo.com/favicon.ico",
+  L"http://ca.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://gossip.ca.yahoo.com/gossip-ca-sayt?output=fxjsonp&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_ch = {
+  L"Yahoo! Suche",
+  L"ch.yahoo.com",
+  L"http://ch.search.yahoo.com/favicon.ico",
+  L"http://ch.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  NULL,
+  2,
+};
+
+const PrepopulatedEngine yahoo_cl = {
+  L"Yahoo! Chile",
+  L"cl.yahoo.com",
+  L"http://cl.search.yahoo.com/favicon.ico",
+  L"http://cl.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://gossip.telemundo.yahoo.com/gossip-e1-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_cn = {
+  L"\x4e2d\x56fd\x96c5\x864e",
+  L"cn.yahoo.com",
+  L"http://search.cn.yahoo.com/favicon.ico",
+  L"http://search.cn.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "GB2312",
+  // http://cn.yahoo.com/cnsuggestion/suggestion.inc.php?of=fxjson&query=
+  // returns in a proprietary format ('|' delimeted word list).
+  NULL,
+  2,
+};
+
+const PrepopulatedEngine yahoo_co = {
+  L"Yahoo! Colombia",
+  L"co.yahoo.com",
+  L"http://co.search.yahoo.com/favicon.ico",
+  L"http://co.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://gossip.telemundo.yahoo.com/gossip-e1-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_de = {
+  L"Yahoo! Deutschland",
+  L"de.yahoo.com",
+  L"http://de.search.yahoo.com/favicon.ico",
+  L"http://de.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://de-sayt.ff.search.yahoo.com/gossip-de-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_dk = {
+  L"Yahoo! Danmark",
+  L"dk.yahoo.com",
+  L"http://dk.search.yahoo.com/favicon.ico",
+  L"http://dk.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  NULL,
+  2,
+};
+
+const PrepopulatedEngine yahoo_es = {
+  L"Yahoo! Espa" L"\x00f1" L"a",
+  L"es.yahoo.com",
+  L"http://es.search.yahoo.com/favicon.ico",
+  L"http://es.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://es-sayt.ff.search.yahoo.com/gossip-es-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_fi = {
+  L"Yahoo!-haku",
+  L"fi.yahoo.com",
+  L"http://fi.search.yahoo.com/favicon.ico",
+  L"http://fi.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  NULL,
+  2,
+};
+
+const PrepopulatedEngine yahoo_fr = {
+  L"Yahoo! France",
+  L"fr.yahoo.com",
+  L"http://fr.search.yahoo.com/favicon.ico",
+  L"http://fr.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://fr-sayt.ff.search.yahoo.com/gossip-fr-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_hk = {
+  L"Yahoo! Hong Kong",
+  L"hk.yahoo.com",
+  L"http://hk.search.yahoo.com/favicon.ico",
+  L"http://hk.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  // http://history.hk.search.yahoo.com/ac/ac_msearch.php?query={searchTerms}
+  // returns a JSON with key-value pairs. Setting parameters (ot, of, output)
+  // to fxjson, json, or js doesn't help. 
+  NULL,
+  2,
+};
+
+const PrepopulatedEngine yahoo_id = {
+  L"Yahoo! Indonesia",
+  L"id.yahoo.com",
+  L"http://id.search.yahoo.com/favicon.ico",
+  L"http://id.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://id-sayt.ff.search.yahoo.com/gossip-id-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_in = {
+  L"Yahoo! India",
+  L"in.yahoo.com",
+  L"http://in.search.yahoo.com/favicon.ico",
+  L"http://in.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://in-sayt.ff.search.yahoo.com/gossip-in-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_it = {
+  L"Yahoo! Italia",
+  L"it.yahoo.com",
+  L"http://it.search.yahoo.com/favicon.ico",
+  L"http://it.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://it-sayt.ff.search.yahoo.com/gossip-it-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_jp = {
+  L"Yahoo! JAPAN",
+  L"yahoo.co.jp",
+  L"http://search.yahoo.co.jp/favicon.ico",
+  L"http://search.yahoo.co.jp/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  NULL,
+  2,
+};
+
+const PrepopulatedEngine yahoo_kr = {
+  L"\xc57c\xd6c4! \xcf54\xb9ac\xc544",
+  L"kr.yahoo.com",
+  L"http://kr.search.yahoo.com/favicon.ico",
+  L"http://kr.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://kr.atc.search.yahoo.com/atcx.php?property=main&ot=fxjson&"
+     L"ei=utf8&eo=utf8&command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_malaysia = {
+  L"Yahoo! Malaysia",
+  L"malaysia.yahoo.com",
+  L"http://malaysia.search.yahoo.com/favicon.ico",
+  L"http://malaysia.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://my-sayt.ff.search.yahoo.com/gossip-my-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_mx = {
+  L"Yahoo! M\x00e9xico",
+  L"mx.yahoo.com",
+  L"http://mx.search.yahoo.com/favicon.ico",
+  L"http://mx.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://gossip.mx.yahoo.com/gossip-mx-sayt?output=fxjsonp&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_nl = {
+  L"Yahoo! Nederland",
+  L"nl.yahoo.com",
+  L"http://nl.search.yahoo.com/favicon.ico",
+  L"http://nl.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  NULL,
+  2,
+};
+
+const PrepopulatedEngine yahoo_no = {
+  L"Yahoo! Norge",
+  L"no.yahoo.com",
+  L"http://no.search.yahoo.com/favicon.ico",
+  L"http://no.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  NULL,
+  2,
+};
+
+const PrepopulatedEngine yahoo_nz = {
+  L"Yahoo!Xtra",
+  L"nz.yahoo.com",
+  L"http://nz.search.yahoo.com/favicon.ico",
+  L"http://nz.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://aue-sayt.ff.search.yahoo.com/gossip-nz-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_pe = {
+  L"Yahoo! Per\x00fa",
+  L"pe.yahoo.com",
+  L"http://pe.search.yahoo.com/favicon.ico",
+  L"http://pe.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://gossip.telemundo.yahoo.com/gossip-e1-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_ph = {
+  L"Yahoo! Philippines",
+  L"ph.yahoo.com",
+  L"http://ph.search.yahoo.com/favicon.ico",
+  L"http://ph.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://ph-sayt.ff.search.yahoo.com/gossip-ph-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_qc = {
+  L"Yahoo! Qu" L"\x00e9" L"bec",
+  L"qc.yahoo.com",
+  L"http://qc.search.yahoo.com/favicon.ico",
+  L"http://qc.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  NULL,
+  5,  // Can't be 2 as this has to appear in the Canada list alongside yahoo_ca.
+};
+
+const PrepopulatedEngine yahoo_ru = {
+  L"Yahoo! \x043f\x043e-\x0440\x0443\x0441\x0441\x043a\x0438",
+  L"ru.yahoo.com",
+  L"http://ru.search.yahoo.com/favicon.ico",
+  L"http://ru.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  NULL,
+  2,
+};
+
+const PrepopulatedEngine yahoo_sg = {
+  L"Yahoo! Singapore",
+  L"sg.yahoo.com",
+  L"http://sg.search.yahoo.com/favicon.ico",
+  L"http://sg.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://sg-sayt.ff.search.yahoo.com/gossip-sg-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_th = {
+  L"Yahoo! \x0e1b\x0e23\x0e30\x0e40\x0e17\x0e28\x0e44\x0e17\x0e22",
+  L"th.yahoo.com",
+  L"http://th.search.yahoo.com/favicon.ico",
+  L"http://th.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://th-sayt.ff.search.yahoo.com/gossip-th-sayt?output=fxjson&"
+    L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_tw = {
+  L"Yahoo!\x5947\x6469",
+  L"tw.yahoo.com",
+  L"http://tw.search.yahoo.com/favicon.ico",
+  L"http://tw.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  // "http://tw.yahoo.com/ac/ac_search.php?eo=utf8&of=js&prop=web&query="
+  // returns a JSON file prepended with 'fxjson={'.
+  NULL,
+  2,
+};
+
+const PrepopulatedEngine yahoo_uk = {
+  L"Yahoo! UK & Ireland",
+  L"uk.yahoo.com",
+  L"http://uk.search.yahoo.com/favicon.ico",
+  L"http://uk.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://uk-sayt.ff.search.yahoo.com/gossip-uk-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_ve = {
+  L"Yahoo! Venezuela",
+  L"ve.yahoo.com",
+  L"http://ve.search.yahoo.com/favicon.ico",
+  L"http://ve.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://gossip.telemundo.yahoo.com/gossip-e1-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yahoo_vn = {
+  L"Yahoo! Vi\x1ec7t Nam",
+  L"vn.yahoo.com",
+  L"http://vn.search.yahoo.com/favicon.ico",
+  L"http://vn.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&"
+      L"p={searchTerms}",
+  "UTF-8",
+  L"http://vn-sayt.ff.search.yahoo.com/gossip-vn-sayt?output=fxjson&"
+      L"command={searchTerms}",
+  2,
+};
+
+const PrepopulatedEngine yam = {
+  L"\u5929\u7a7a",
+  L"yam.com",
+  L"http://www.yam.com/i/8/sky.ico",
+  L"http://search.yam.com/wps?k={searchTerms}",
+  "Big5",
+  NULL,
+  23,
+};
+
+const PrepopulatedEngine yamli = {
+  L"Yamli",
+  L"yamli.com",
+  L"http://www.yamli.com/favicon.ico",
+  L"http://www.yamli.com/#q={searchTerms}",
+  "UTF-8",
+  NULL,
+  11,
+};
+
+const PrepopulatedEngine yandex_ru = {
+  L"\x042f\x043d\x0434\x0435\x043a\x0441",
+  L"yandex.ru",
+  L"http://yandex.ru/favicon.ico",
+  L"http://yandex.ru/yandsearch?text={searchTerms}",
+  "UTF-8",
+  L"http://suggest.yandex.net/suggest-ff.cgi?part={searchTerms}",
+  15,
+};
+
+const PrepopulatedEngine yandex_ua = {
+  L"\x042f\x043d\x0434\x0435\x043a\x0441",
+  L"yandex.ua",
+  L"http://yandex.ua/favicon.ico",
+  L"http://yandex.ua/yandsearch?text={searchTerms}",
+  "UTF-8",
+  L"http://suggest.yandex.net/suggest-ff.cgi?part={searchTerms}",
+  15,
+};
+
+const PrepopulatedEngine zoznam = {
+  L"Zoznam",
+  L"zoznam.sk",
+  L"http://zoznam.sk/favicon.ico",
+  L"http://zoznam.sk/hladaj.fcgi?s={searchTerms}",
+  "windows-1250",
+  NULL,
+  85,
+};
+
+// Lists of engines per country ////////////////////////////////////////////////
+
+// Put these in order with most interesting/important first.  The default will
+// be the first engine.
+
+// Default (for countries with no better engine set)
+const PrepopulatedEngine* engines_default[] = { &google, &yahoo, &live, };
+
+// United Arab Emirates
+const PrepopulatedEngine* engines_AE[] =
+    { &google, &maktoob, &yahoo, &yamli, &araby, &msn_en_XA, &msn_ar_XA, };
+
+// Albania
+const PrepopulatedEngine* engines_AL[] =
+    { &google, &yahoo, &live_en_XA, &live_ar_XA, };
+
+// Argentina
+const PrepopulatedEngine* engines_AR[] =
+    { &google, &msn_es_AR, &altavista_ar, &terra_ar, &yahoo_ar, };
+
+// Austria
+const PrepopulatedEngine* engines_AT[] = { &google, &yahoo_at, &msn_de_AT, };
+
+// Australia
+const PrepopulatedEngine* engines_AU[] =
+    { &google, &yahoo_au, &msn_en_AU, &sensis, };
+
+// Bosnia and Herzegovina
+const PrepopulatedEngine* engines_BA[] =
+    { &google, &pogodak_ba, &yahoo, &live, };
+
+// Belgium
+const PrepopulatedEngine* engines_BE[] =
+    { &google, &yahoo, &msn_nl_BE, &msn_fr_BE, };
+
+// Bulgaria
+// The commented-out entry for "dir" below is for dir.bg, &which we don't
+// currently support because it uses POST instead of GET for its searches.
+// See http://b/1196285
+const PrepopulatedEngine* engines_BG[] =
+    { &google, &/*dir,*/ yahoo, &jabse, &live_bg_BG, };
+
+// Bahrain
+const PrepopulatedEngine* engines_BH[] =
+    { &google, &maktoob, &yamli, &yahoo, &araby, &msn_en_XA, &msn_ar_XA, };
+
+// Brunei
+const PrepopulatedEngine* engines_BN[] =
+    { &google, &yahoo_malaysia, &msn_en_MY, };
+
+// Bolivia
+const PrepopulatedEngine* engines_BO[] =
+    { &google, &altavista, &msn_es_XL, &yahoo, &ask_es, };
+
+// Brazil
+const PrepopulatedEngine* engines_BR[] =
+    { &google, &msn_pt_BR, &yahoo_br, &aonde, &gigabusca, &uol, };
+
+// Belarus
+const PrepopulatedEngine* engines_BY[] =
+    { &google, &yandex_ru, &rambler, &yahoo, &tut, };
+
+// Belize
+const PrepopulatedEngine* engines_BZ[] = { &google, &yahoo, &live, &aol, };
+
+// Canada
+const PrepopulatedEngine* engines_CA[] =
+    { &google, &msn_en_CA, &msn_fr_CA, &yahoo_ca, &yahoo_qc, &toile, };
+
+// Switzerland
+const PrepopulatedEngine* engines_CH[] =
+    { &google, &search_ch, &yahoo_ch, &msn_de_CH, &msn_fr_CH, &bluewin, };
+
+// Chile
+const PrepopulatedEngine* engines_CL[] =
+    { &google, &yahoo_cl, &altavista, &msn_es_CL, };
+
+// China
+const PrepopulatedEngine* engines_CN[] =
+    { &google, &baidu, &yahoo_cn, &sogou, &soso, };
+
+// Colombia
+const PrepopulatedEngine* engines_CO[] =
+    { &google, &msn_es_CO, &ask_es, &altavista, &conexcol, &yahoo_co, };
+
+// Costa Rica
+const PrepopulatedEngine* engines_CR[] =
+    { &google, &msn_es_XL, &yahoo, &altavista, &aol, &lycos_es, };
+
+// Czech Republic
+const PrepopulatedEngine* engines_CZ[] =
+    { &google, &seznam, &centrum_cz, &atlas_cz, &live_cs_CZ, };
+
+// Germany
+const PrepopulatedEngine* engines_DE[] =
+    { &google, &msn_de_DE, &yahoo_de, &t_online, &ask_de, &web_de, };
+
+// Denmark
+const PrepopulatedEngine* engines_DK[] =
+    { &google, &jubii, &msn_da_DK, &yahoo_dk, &eniro_dk, };
+
+// Dominican Republic
+const PrepopulatedEngine* engines_DO[] =
+    { &google, &msn_es_XL, &yahoo, &altavista, &go, &aol, };
+
+// Algeria
+const PrepopulatedEngine* engines_DZ[] =
+    { &google, &yahoo, &yamli, &msn_en_XA, &msn_ar_XA, &araby, };
+
+// Ecuador
+const PrepopulatedEngine* engines_EC[] =
+    { &google, &msn_es_XL, &yahoo, &terra_ec, };
+
+// Estonia
+const PrepopulatedEngine* engines_EE[] =
+    { &google, &neti, &delfi_ee, &yahoo, &live_et_EE, };
+
+// Egypt
+const PrepopulatedEngine* engines_EG[] =
+    { &google, &masrawy, &yahoo, &maktoob, &araby, &msn_en_XA, &msn_ar_XA, };
+
+// Spain
+const PrepopulatedEngine* engines_ES[] =
+    { &google, &msn_es_ES, &yahoo_es, &terra_es, &ozu, &altavista_es, };
+
+// Faroe Islands
+const PrepopulatedEngine* engines_FO[] =
+    { &google, &jubii, &msn_da_DK, &yahoo_dk, &eniro_dk, };
+
+// Finland
+const PrepopulatedEngine* engines_FI[] =
+    { &google, &msn_fi_FI, &yahoo_fi, &eniro_fi, &fonecta_02_fi, };
+
+// France
+const PrepopulatedEngine* engines_FR[] =
+    { &google, &voila, &yahoo_fr, &msn_fr_FR, &orange, &aol_fr, };
+
+// Greece
+const PrepopulatedEngine* engines_GR[] =
+    { &google, &yahoo, &forthnet, &in, &live_el_GR };
+
+// Guatemala
+const PrepopulatedEngine* engines_GT[] =
+    { &google, &msn_es_XL, &yahoo, &ask_es, &altavista, &go, };
+
+// Hong Kong
+const PrepopulatedEngine* engines_HK[] =
+    { &google, &yahoo_hk, &msn_zh_HK, &sogou, &baidu, };
+
+// Honduras
+const PrepopulatedEngine* engines_HN[] =
+    { &google, &msn_es_XL, &yahoo, &ask_es, &altavista, };
+
+// Croatia
+const PrepopulatedEngine* engines_HR[] =
+    { &google, &yahoo, &pogodak_hr, &live_hr_HR, };
+
+// Hungary
+const PrepopulatedEngine* engines_HU[] = { &google, &tango, &ok, &live_hu_HU, };
+
+// Indonesia
+const PrepopulatedEngine* engines_ID[] = { &google, &yahoo_id, &live_en_ID, };
+
+// Ireland
+const PrepopulatedEngine* engines_IE[] = { &google, &yahoo_uk, &msn_en_IE, };
+
+// Israel
+const PrepopulatedEngine* engines_IL[] =
+    { &google, &walla, &nana10, &tapuz, &msn_he_IL, };
+
+// India
+const PrepopulatedEngine* engines_IN[] =
+    { &google, &yahoo_in, &msn_en_IN, &rediff, &guruji, };
+
+// Iraq
+const PrepopulatedEngine* engines_IQ[] =
+    { &google, &maktoob, &yamli, &yahoo, &araby, &msn_en_XA, &msn_ar_XA, };
+
+// Iran
+const PrepopulatedEngine* engines_IR[] = { &google, };
+
+// Iceland
+const PrepopulatedEngine* engines_IS[] = { &google, &leit, &embla, &finna, };
+
+// Italy
+const PrepopulatedEngine* engines_IT[] =
+    { &google, &virgilio, &yahoo_it, &libero, &ask_it, &live_it_IT, };
+
+// Jamaica
+const PrepopulatedEngine* engines_JM[] =
+    { &google, &jamaicalive, &yahoo, &live, &go, &aol, };
+
+// Jordan
+const PrepopulatedEngine* engines_JO[] =
+    { &google, &maktoob, &yamli, &yahoo, &araby, &msn_en_XA, &msn_ar_XA, };
+
+// Japan
+const PrepopulatedEngine* engines_JP[] =
+    { &google, &yahoo_jp, &msn_ja_JP, &biglobe, &goo, &nifty, };
+
+// Kenya
+const PrepopulatedEngine* engines_KE[] = { &google, &yahoo, &msn, };
+
+// Kuwait
+const PrepopulatedEngine* engines_KW[] =
+    { &google, &maktoob, &yahoo, &yamli, &araby, &msn_en_XA, &msn_ar_XA, };
+
+// South Korea
+const PrepopulatedEngine* engines_KR[] =
+    { &google, &naver, &daum, &yahoo_kr, &nate, &empas, };
+
+// Lebanon
+const PrepopulatedEngine* engines_LB[] =
+    { &google, &maktoob, &yahoo, &yamli, &araby, &msn_en_XA, &msn_ar_XA, };
+
+// Liechtenstein
+const PrepopulatedEngine* engines_LI[] =
+    { &google, &msn_de_DE, &yahoo_de, &t_online, &ask_de, &web_de, };
+
+// Lithuania
+const PrepopulatedEngine* engines_LT[] =
+    { &google, &delfi_lt, &yahoo, &yandex_ru, &live_lt_LT, };
+
+// Luxembourg
+const PrepopulatedEngine* engines_LU[] =
+    { &google, &voila, &yahoo_fr, &msn_fr_FR, &orange, &aol_fr, };
+
+// Latvia
+const PrepopulatedEngine* engines_LV[] =
+    { &google, &delfi_lv, &yahoo, &yandex_ru, &latne, };
+
+// Libya
+const PrepopulatedEngine* engines_LY[] =
+    { &google, &maktoob, &yahoo, &yamli, &araby, &msn_en_XA, &msn_ar_XA, };
+
+// Morocco
+const PrepopulatedEngine* engines_MA[] =
+    { &google, &yamli, &araby, &yahoo, &msn_en_XA, &msn_ar_XA, };
+
+// Monaco
+const PrepopulatedEngine* engines_MC[] =
+    { &google, &voila, &yahoo_fr, &msn_fr_FR, &orange, &aol_fr, };
+
+// Macedonia
+const PrepopulatedEngine* engines_MK[] = { &google, &pogodok, &yahoo, &live, };
+
+// Mexico
+const PrepopulatedEngine* engines_MX[] =
+    { &google, &msn_es_MX, &yahoo_mx, &ask_es, &altavista_mx, &terra_mx, };
+
+// Malaysia
+const PrepopulatedEngine* engines_MY[] =
+    { &google, &yahoo_malaysia, &msn_en_MY, };
+
+// Nicaragua
+const PrepopulatedEngine* engines_NI[] =
+    { &google, &msn_es_XL, &yahoo, &ask_es, &altavista, };
+
+// Netherlands
+const PrepopulatedEngine* engines_NL[] =
+    { &google, &ilse, &msn_nl_NL, &yahoo_nl, &lycos_nl, &vinden, };
+
+// Norway
+const PrepopulatedEngine* engines_NO[] =
+    { &google, &msn_nb_NO, &abcsok, &yahoo_no, &kvasir, &sesam, };
+
+// New Zealand
+const PrepopulatedEngine* engines_NZ[] = { &google, &yahoo_nz, &live_en_NZ, };
+
+// Oman
+const PrepopulatedEngine* engines_OM[] =
+    { &google, &maktoob, &yahoo, &yamli, &araby, &msn_en_XA, &msn_ar_XA, };
+
+// Panama
+const PrepopulatedEngine* engines_PA[] =
+    { &google, &msn_es_XL, &yahoo, &ask_es, &altavista, &lycos_es, };
+
+// Peru
+const PrepopulatedEngine* engines_PE[] =
+    { &google, &msn_es_XL, &yahoo_pe, &terra_pe, &adonde, &ohperu, };
+
+// Philippines
+const PrepopulatedEngine* engines_PH[] = { &google, &yahoo_ph, &msn_en_PH, };
+
+// Pakistan
+const PrepopulatedEngine* engines_PK[] = { &google, &yahoo, &msn, };
+
+// Puerto Rico
+const PrepopulatedEngine* engines_PR[] =
+    { &google, &msn_es_XL, &yahoo, &ask_es, &altavista, &mywebsearch, };
+
+// Poland
+const PrepopulatedEngine* engines_PL[] = { &google, &onet, &wp, &live_pl_PL, };
+
+// Portugal
+const PrepopulatedEngine* engines_PT[] =
+    { &google, &sapo, &yahoo, &live_pt_PT, &netindex, &aeiou, };
+
+// Paraguay
+const PrepopulatedEngine* engines_PY[] =
+    { &google, &msn_es_XL, &yahoo, &lycos_es, &yagua, &go, };
+
+// Qatar
+const PrepopulatedEngine* engines_QA[] =
+    { &google, &maktoob, &yahoo, &araby, &msn_en_XA, &msn_ar_XA, };
+
+// Romania
+const PrepopulatedEngine* engines_RO[] = { &google, &yahoo, &live_ro_RO, };
+
+// Serbia/Montenegro
+const PrepopulatedEngine* engines_RS_ME[] =
+    { &google, &yahoo, &krstarica, &pogodak_rs, &aladin, &live, };
+
+// Russia
+const PrepopulatedEngine* engines_RU[] =
+    { &google, &yandex_ru, &rambler, &mail_ru, &yahoo_ru, &live_ru_RU, };
+
+// Saudi Arabia
+const PrepopulatedEngine* engines_SA[] =
+    { &google, &yahoo, &araby, &msn_en_XA, &msn_ar_XA, &maktoob, };
+
+// Sweden
+const PrepopulatedEngine* engines_SE[] =
+    { &google, &eniro_se, &msn_sv_SE, &altavista_se, &spray, };
+
+// Singapore
+const PrepopulatedEngine* engines_SG[] =
+    { &google, &yahoo_sg, &msn_en_SG, &rednano, };
+
+// Slovenia
+const PrepopulatedEngine* engines_SI[] =
+    { &google, &najdi, &yahoo, &matkurja, &live_sl_SI, };
+
+// Slovakia
+const PrepopulatedEngine* engines_SK[] =
+    { &google, &zoznam, &centrum_sk, &atlas_sk, &szm, &live_sk_SK, };
+
+// El Salvador
+const PrepopulatedEngine* engines_SV[] =
+    { &google, &msn_es_XL, &yahoo, &ask_es, &altavista, &go, };
+
+// Syria
+const PrepopulatedEngine* engines_SY[] =
+    { &google, &yahoo, &maktoob, &yamli, &araby, &msn_en_XA, &msn_ar_XA, };
+
+// Thailand
+const PrepopulatedEngine* engines_TH[] =
+    { &google, &sanook, &yahoo_th, &live_th_TH, };
+
+// Tunisia
+const PrepopulatedEngine* engines_TN[] =
+    { &google, &maktoob, &yamli, &yahoo, &msn_en_XA, &msn_ar_XA, };
+
+// Turkey
+const PrepopulatedEngine* engines_TR[] =
+    { &google, &msn_tr_TR, &yahoo, &mynet, };
+
+// Trinidad and Tobago
+const PrepopulatedEngine* engines_TT[] = { &google, &live, &yahoo, &go, &aol, };
+
+// Taiwan
+const PrepopulatedEngine* engines_TW[] = { &google, &yahoo_tw, &yam, };
+
+// Ukraine
+const PrepopulatedEngine* engines_UA[] =
+    { &google, &meta, &yandex_ua, &bigmir, &rambler, };
+
+// United Kingdom
+const PrepopulatedEngine* engines_UK[] =
+    { &google, &yahoo_uk, &msn_en_GB, &ask_uk, };
+
+// United States
+const PrepopulatedEngine* engines_US[] =
+    { &google, &yahoo, &live_en_US, &aol, &ask, };
+
+// Uruguay
+const PrepopulatedEngine* engines_UY[] =
+    { &google, &msn_es_XL, &yahoo, &go, &lycos_es, };
+
+// Venezuela
+const PrepopulatedEngine* engines_VE[] =
+    { &google, &msn_es_XL, &yahoo_ve, &altavista, };
+
+// Vietnam
+const PrepopulatedEngine* engines_VN[] = { &google, &yahoo_vn, };
+
+// Yemen
+const PrepopulatedEngine* engines_YE[] =
+    { &google, &yahoo, &maktoob, &yamli, &araby, &msn_en_XA, &msn_ar_XA, };
+
+// South Africa
+const PrepopulatedEngine* engines_ZA[] =
+    { &google, &yahoo, &msn_en_ZA, &mweb, &iafrica, };
+
+// Zimbabwe
+const PrepopulatedEngine* engines_ZW[] = { &google, &yahoo, &msn, };
+
+// GeoID mappings //////////////////////////////////////////////////////////////
+
+LONG GetCurrentGeoID() {
+  // TODO(pkasting): http://b/1225276  Much of this should live in a utility
+  // function somewhere.
+  typedef GEOID (WINAPI *GetUserGeoIDFunction)(GEOCLASS);
+  const HMODULE kernel32_handle = GetModuleHandle(L"kernel32.dll");
+  if (!kernel32_handle) {
+    NOTREACHED();
+    return GEOID_NOT_AVAILABLE;
+  }
+  const GetUserGeoIDFunction GetUserGeoIDPtr =
+      reinterpret_cast<GetUserGeoIDFunction>(GetProcAddress(kernel32_handle,
+                                                            "GetUserGeoID"));
+  return GetUserGeoIDPtr ?
+      ((*GetUserGeoIDPtr)(GEOCLASS_NATION)) : GEOID_NOT_AVAILABLE;
+}
+
+int GetGeoIDFromPrefs(PrefService* prefs) {
+  // See if the user overrode the GeoID on the command line.
+  CommandLine parsed_command_line;
+  const std::wstring geoID(
+      parsed_command_line.GetSwitchValue(switches::kGeoID));
+  if (!geoID.empty())
+    return _wtoi(geoID.c_str());
+
+  // Cache first run GeoID value in prefs, and use it afterwards.  This ensures
+  // that just because the user moves around, we won't automatically make major
+  // changes to their available search providers, which would feel surprising.
+  if (!prefs)
+    return GetCurrentGeoID();
+  if (!prefs->HasPrefPath(prefs::kGeoIDAtInstall))
+    prefs->SetInteger(prefs::kGeoIDAtInstall, GetCurrentGeoID());
+  return prefs->GetInteger(prefs::kGeoIDAtInstall);
+}
+
+void GetPrepopulationSetFromGeoID(PrefService* prefs,
+                                  const PrepopulatedEngine*** engines,
+                                  size_t* num_engines) {
+  // NOTE: This function should ALWAYS set its outparams.
+
+  // If you add a new geo id make sure and update the unit test for coverage.
+
+  // GeoIDs are from http://msdn.microsoft.com/en-us/library/ms776390.aspx .
+  // Country codes and names are from http://www.geonames.org/countries/ .
+  switch (GetGeoIDFromPrefs(prefs)) {
+
+#define UNHANDLED_COUNTRY(id, code)\
+    case id:
+#define END_UNHANDLED_COUNTRIES(code)\
+      *engines = engines_##code;\
+      *num_engines = arraysize(engines_##code);\
+      return;
+#define DECLARE_COUNTRY(id, code)\
+    UNHANDLED_COUNTRY(id, code)\
+    END_UNHANDLED_COUNTRIES(code)
+
+    // Countries with their own, dedicated engine set.
+    DECLARE_COUNTRY(0x4,    DZ)     // Algeria
+    DECLARE_COUNTRY(0x6,    AL)     // Albania
+    DECLARE_COUNTRY(0xB,    AR)     // Argentina
+    DECLARE_COUNTRY(0xC,    AU)     // Australia
+    DECLARE_COUNTRY(0xE,    AT)     // Austria
+    DECLARE_COUNTRY(0x11,   BH)     // Bahrain
+    DECLARE_COUNTRY(0x15,   BE)     // Belgium
+    DECLARE_COUNTRY(0x18,   BZ)     // Belize
+    DECLARE_COUNTRY(0x19,   BA)     // Bosnia and Herzegovina
+    DECLARE_COUNTRY(0x1A,   BO)     // Bolivia
+    DECLARE_COUNTRY(0x1D,   BY)     // Belarus
+    DECLARE_COUNTRY(0x20,   BR)     // Brazil
+    DECLARE_COUNTRY(0x23,   BG)     // Bulgaria
+    DECLARE_COUNTRY(0x25,   BN)     // Brunei
+    DECLARE_COUNTRY(0x27,   CA)     // Canada
+    DECLARE_COUNTRY(0x2D,   CN)     // China
+    DECLARE_COUNTRY(0x2E,   CL)     // Chile
+    DECLARE_COUNTRY(0x33,   CO)     // Colombia
+    DECLARE_COUNTRY(0x36,   CR)     // Costa Rica
+    DECLARE_COUNTRY(0x3D,   DK)     // Denmark
+    DECLARE_COUNTRY(0x41,   DO)     // Dominican Republic
+    DECLARE_COUNTRY(0x42,   EC)     // Ecuador
+    DECLARE_COUNTRY(0x43,   EG)     // Egypt
+    DECLARE_COUNTRY(0x44,   IE)     // Ireland
+    DECLARE_COUNTRY(0x46,   EE)     // Estonia
+    DECLARE_COUNTRY(0x48,   SV)     // El Salvador
+    DECLARE_COUNTRY(0x4B,   CZ)     // Czech Republic
+    DECLARE_COUNTRY(0x4D,   FI)     // Finland
+    DECLARE_COUNTRY(0x51,   FO)     // Faroe Islands
+    DECLARE_COUNTRY(0x54,   FR)     // France
+    DECLARE_COUNTRY(0x5E,   DE)     // Germany
+    DECLARE_COUNTRY(0x62,   GR)     // Greece
+    DECLARE_COUNTRY(0x63,   GT)     // Guatemala
+    DECLARE_COUNTRY(0x68,   HK)     // Hong Kong
+    DECLARE_COUNTRY(0x6A,   HN)     // Honduras
+    DECLARE_COUNTRY(0x6C,   HR)     // Croatia
+    DECLARE_COUNTRY(0x6D,   HU)     // Hungary
+    DECLARE_COUNTRY(0x6E,   IS)     // Iceland
+    DECLARE_COUNTRY(0x6F,   ID)     // Indonesia
+    DECLARE_COUNTRY(0x71,   IN)     // India
+    DECLARE_COUNTRY(0x74,   IR)     // Iran
+    DECLARE_COUNTRY(0x75,   IL)     // Israel
+    DECLARE_COUNTRY(0x76,   IT)     // Italy
+    DECLARE_COUNTRY(0x79,   IQ)     // Iraq
+    DECLARE_COUNTRY(0x7A,   JP)     // Japan
+    DECLARE_COUNTRY(0x7C,   JM)     // Jamaica
+    DECLARE_COUNTRY(0x7E,   JO)     // Jordan
+    DECLARE_COUNTRY(0x81,   KE)     // Kenya
+    DECLARE_COUNTRY(0x86,   KR)     // South Korea
+    DECLARE_COUNTRY(0x88,   KW)     // Kuwait
+    DECLARE_COUNTRY(0x8B,   LB)     // Lebanon
+    DECLARE_COUNTRY(0x8C,   LV)     // Latvia
+    DECLARE_COUNTRY(0x8D,   LT)     // Lithuania
+    DECLARE_COUNTRY(0x8F,   SK)     // Slovakia
+    DECLARE_COUNTRY(0x91,   LI)     // Liechtenstein
+    DECLARE_COUNTRY(0x93,   LU)     // Luxembourg
+    DECLARE_COUNTRY(0x94,   LY)     // Libya
+    DECLARE_COUNTRY(0x9E,   MC)     // Monaco
+    DECLARE_COUNTRY(0x9F,   MA)     // Morocco
+    DECLARE_COUNTRY(0xA4,   OM)     // Oman
+    DECLARE_COUNTRY(0xA6,   MX)     // Mexico
+    DECLARE_COUNTRY(0xA7,   MY)     // Malaysia
+    DECLARE_COUNTRY(0xB0,   NL)     // Netherlands
+    DECLARE_COUNTRY(0xB1,   NO)     // Norway
+    DECLARE_COUNTRY(0xB6,   NI)     // Nicaragua
+    DECLARE_COUNTRY(0xB7,   NZ)     // New Zealand
+    DECLARE_COUNTRY(0xB9,   PY)     // Paraguay
+    DECLARE_COUNTRY(0xBB,   PE)     // Peru
+    DECLARE_COUNTRY(0xBE,   PK)     // Pakistan
+    DECLARE_COUNTRY(0xBF,   PL)     // Poland
+    DECLARE_COUNTRY(0xC0,   PA)     // Panama
+    DECLARE_COUNTRY(0xC1,   PT)     // Portugal
+    DECLARE_COUNTRY(0xC5,   QA)     // Qatar
+    DECLARE_COUNTRY(0xC8,   RO)     // Romania
+    DECLARE_COUNTRY(0xC9,   PH)     // Philippines
+    DECLARE_COUNTRY(0xCA,   PR)     // Puerto Rico
+    DECLARE_COUNTRY(0xCB,   RU)     // Russia
+    DECLARE_COUNTRY(0xCD,   SA)     // Saudi Arabia
+    DECLARE_COUNTRY(0xD1,   ZA)     // South Africa
+    DECLARE_COUNTRY(0xD4,   SI)     // Slovenia
+    DECLARE_COUNTRY(0xD7,   SG)     // Singapore
+    DECLARE_COUNTRY(0xD9,   ES)     // Spain
+    DECLARE_COUNTRY(0xDD,   SE)     // Sweden
+    DECLARE_COUNTRY(0xDE,   SY)     // Syria
+    DECLARE_COUNTRY(0xDF,   CH)     // Switzerland
+    DECLARE_COUNTRY(0xE0,   AE)     // United Arab Emirates
+    DECLARE_COUNTRY(0xE1,   TT)     // Trinidad and Tobago
+    DECLARE_COUNTRY(0xE3,   TH)     // Thailand
+    DECLARE_COUNTRY(0xEA,   TN)     // Tunisia
+    DECLARE_COUNTRY(0xEB,   TR)     // Turkey
+    DECLARE_COUNTRY(0xED,   TW)     // Taiwan
+    DECLARE_COUNTRY(0xF1,   UA)     // Ukraine
+    DECLARE_COUNTRY(0xF2,   UK)     // United Kingdom
+    DECLARE_COUNTRY(0xF4,   US)     // United States
+    DECLARE_COUNTRY(0xF6,   UY)     // Uruguay
+    DECLARE_COUNTRY(0xF9,   VE)     // Venezuela
+    DECLARE_COUNTRY(0xFB,   VN)     // Vietnam
+    DECLARE_COUNTRY(0x105,  YE)     // Yemen
+    DECLARE_COUNTRY(0x108,  ZW)     // Zimbabwe
+    DECLARE_COUNTRY(0x10D,  RS_ME)  // Serbia/Montenegro
+    DECLARE_COUNTRY(0x4CA2, MK)     // Macedonia
+
+    // Countries using the "Australia" engine set.
+    UNHANDLED_COUNTRY(0x130, XX)  // Ashmore and Cartier Islands
+    UNHANDLED_COUNTRY(0x135, CX)  // Christmas Island
+    UNHANDLED_COUNTRY(0x137, CC)  // Cocos Islands
+    UNHANDLED_COUNTRY(0x139, XX)  // Coral Sea Islands
+    UNHANDLED_COUNTRY(0x145, HM)  // Heard Island and McDonald Islands
+    UNHANDLED_COUNTRY(0x150, NF)  // Norfolk Island
+    END_UNHANDLED_COUNTRIES(AU)
+
+    // Countries using the "China" engine set.
+    UNHANDLED_COUNTRY(0x97, MO)  // Macao
+    END_UNHANDLED_COUNTRIES(CN)
+
+    // Countries using the "Denmark" engine set.
+    UNHANDLED_COUNTRY(0x5D, GL)  // Greenland
+    END_UNHANDLED_COUNTRIES(DK)
+
+    // Countries using the "Spain" engine set.
+    UNHANDLED_COUNTRY(0x8, AD)  // Andorra
+    END_UNHANDLED_COUNTRIES(ES)
+
+    // Countries using the "France" engine set.
+    UNHANDLED_COUNTRY(0x1C,  BJ)  // Benin
+    UNHANDLED_COUNTRY(0x26,  BI)  // Burundi
+    UNHANDLED_COUNTRY(0x29,  TD)  // Chad
+    UNHANDLED_COUNTRY(0x2B,  CG)  // Congo - Brazzaville
+    UNHANDLED_COUNTRY(0x2C,  CD)  // Congo - Kinshasa
+    UNHANDLED_COUNTRY(0x31,  CM)  // Cameroon
+    UNHANDLED_COUNTRY(0x37,  CF)  // Central African Republic
+    UNHANDLED_COUNTRY(0x3E,  DJ)  // Djibouti
+    UNHANDLED_COUNTRY(0x57,  GA)  // Gabon
+    UNHANDLED_COUNTRY(0x64,  GN)  // Guinea
+    UNHANDLED_COUNTRY(0x67,  HT)  // Haiti
+    UNHANDLED_COUNTRY(0x77,  CI)  // Ivory Coast
+    UNHANDLED_COUNTRY(0x9D,  ML)  // Mali
+    UNHANDLED_COUNTRY(0xAD,  NE)  // Niger
+    UNHANDLED_COUNTRY(0xC6,  RE)  // Reunion
+    UNHANDLED_COUNTRY(0xCE,  PM)  // Saint Pierre and Miquelon
+    UNHANDLED_COUNTRY(0xD2,  SN)  // Senegal
+    UNHANDLED_COUNTRY(0xE8,  TG)  // Togo
+    UNHANDLED_COUNTRY(0xF5,  BF)  // Burkina Faso
+    UNHANDLED_COUNTRY(0x136, XX)  // Clipperton Island
+    UNHANDLED_COUNTRY(0x13D, GF)  // French Guiana
+    UNHANDLED_COUNTRY(0x13E, PF)  // French Polynesia
+    UNHANDLED_COUNTRY(0x13F, TF)  // French Southern Territories
+    UNHANDLED_COUNTRY(0x141, GP)  // Guadeloupe
+    UNHANDLED_COUNTRY(0x14A, MQ)  // Martinique
+    UNHANDLED_COUNTRY(0x14B, YT)  // Mayotte
+    UNHANDLED_COUNTRY(0x14E, NC)  // New Caledonia
+    UNHANDLED_COUNTRY(0x160, WF)  // Wallis and Futuna
+    END_UNHANDLED_COUNTRIES(FR)
+
+    // Countries using the "Greece" engine set.
+    UNHANDLED_COUNTRY(0x3B, CY)  // Cyprus
+    END_UNHANDLED_COUNTRIES(GR)
+
+    // Countries using the "Italy" engine set.
+    UNHANDLED_COUNTRY(0xD6, SM)  // San Marino
+    UNHANDLED_COUNTRY(0xFD, VA)  // Vatican
+    END_UNHANDLED_COUNTRIES(IT)
+
+    // Countries using the "Netherlands" engine set.
+    UNHANDLED_COUNTRY(0x12E, AW)  // Aruba
+    UNHANDLED_COUNTRY(0x14D, AN)  // Netherlands Antilles
+    END_UNHANDLED_COUNTRIES(NL)
+
+    // Countries using the "Norway" engine set.
+    UNHANDLED_COUNTRY(0x7D,  SJ)  // [Svalbard and] Jan Mayen
+    UNHANDLED_COUNTRY(0xDC,  SJ)  // Svalbard [and Jan Mayen]
+    UNHANDLED_COUNTRY(0x132, BV)  // Bouvet Island
+    END_UNHANDLED_COUNTRIES(NO)
+
+    // Countries using the "New Zealand" engine set.
+    UNHANDLED_COUNTRY(0x138, CK)  // Cook Islands
+    UNHANDLED_COUNTRY(0x14F, NU)  // Niue
+    UNHANDLED_COUNTRY(0x15B, TK)  // Tokelau
+    END_UNHANDLED_COUNTRIES(NZ)
+
+    // Countries using the "Portugal" engine set.
+    UNHANDLED_COUNTRY(0x39,     CV)  // Cape Verde
+    UNHANDLED_COUNTRY(0xA8,     MZ)  // Mozambique
+    UNHANDLED_COUNTRY(0xC4,     GW)  // Guinea-Bissau
+    UNHANDLED_COUNTRY(0xE9,     ST)  // Sao Tome and Principe
+    UNHANDLED_COUNTRY(0x6F60E7, TL)  // East Timor
+    END_UNHANDLED_COUNTRIES(PT)
+
+    // Countries using the "Russia" engine set.
+    UNHANDLED_COUNTRY(0x5,  AZ)  // Azerbaijan
+    UNHANDLED_COUNTRY(0x7,  AM)  // Armenia
+    UNHANDLED_COUNTRY(0x82, KG)  // Kyrgyzstan
+    UNHANDLED_COUNTRY(0x89, KZ)  // Kazakhstan
+    UNHANDLED_COUNTRY(0xE4, TJ)  // Tajikistan
+    UNHANDLED_COUNTRY(0xEE, TM)  // Turkmenistan
+    UNHANDLED_COUNTRY(0xF7, UZ)  // Uzbekistan
+    END_UNHANDLED_COUNTRIES(RU)
+
+    // Countries using the "Saudi Arabia" engine set.
+    UNHANDLED_COUNTRY(0xA2, MR)  // Mauritania
+    UNHANDLED_COUNTRY(0xB8, PS)  // Palestinian Territory
+    UNHANDLED_COUNTRY(0xDB, SD)  // Sudan
+    END_UNHANDLED_COUNTRIES(SA)
+
+    // Countries using the "United Kingdom" engine set.
+    UNHANDLED_COUNTRY(0x14,   BM)  // Bermuda
+    UNHANDLED_COUNTRY(0x5A,   GI)  // Gibraltar
+    UNHANDLED_COUNTRY(0x72,   IO)  // British Indian Ocean Territory
+    UNHANDLED_COUNTRY(0xA3,   MT)  // Malta
+    UNHANDLED_COUNTRY(0x12F,  XX)  // Ascension Island
+    UNHANDLED_COUNTRY(0x133,  KY)  // Cayman Islands
+    UNHANDLED_COUNTRY(0x134,  XX)  // Channel Islands
+    UNHANDLED_COUNTRY(0x13A,  XX)  // Diego Garcia
+    UNHANDLED_COUNTRY(0x13B,  FK)  // Falkland Islands
+    UNHANDLED_COUNTRY(0x144,  GG)  // Guernsey
+    UNHANDLED_COUNTRY(0x148,  JE)  // Jersey
+    UNHANDLED_COUNTRY(0x14C,  MS)  // Montserrat
+    UNHANDLED_COUNTRY(0x153,  PN)  // Pitcairn Islands
+    UNHANDLED_COUNTRY(0x156,  GS)  // South Georgia and the South Sandwich
+                                   // Islands
+    UNHANDLED_COUNTRY(0x157,  SH)  // Saint Helena
+    UNHANDLED_COUNTRY(0x15C,  XX)  // Tristan da Cunha
+    UNHANDLED_COUNTRY(0x15D,  TC)  // Turks and Caicos Islands
+    UNHANDLED_COUNTRY(0x15F,  VG)  // British Virgin Islands
+    UNHANDLED_COUNTRY(0x3B16, IM)  // Isle of Man
+    END_UNHANDLED_COUNTRIES(UK)
+
+    // Countries using the "United States" engine set.
+    UNHANDLED_COUNTRY(0xA,    AS)  // American Samoa
+    UNHANDLED_COUNTRY(0x7F,   XX)  // Johnston Atoll
+    UNHANDLED_COUNTRY(0xFC,   VI)  // U.S. Virgin Islands
+    UNHANDLED_COUNTRY(0x102,  XX)  // Wake Island
+    UNHANDLED_COUNTRY(0x131,  XX)  // Baker Island
+    UNHANDLED_COUNTRY(0x142,  GU)  // Guam
+    UNHANDLED_COUNTRY(0x146,  XX)  // Howland Island
+    UNHANDLED_COUNTRY(0x147,  XX)  // Jarvis Island
+    UNHANDLED_COUNTRY(0x149,  XX)  // Kingman Reef
+    UNHANDLED_COUNTRY(0x151,  MP)  // Northern Mariana Islands
+    UNHANDLED_COUNTRY(0x152,  XX)  // Palmyra Atoll
+    UNHANDLED_COUNTRY(0x154,  XX)  // Rota Island
+    UNHANDLED_COUNTRY(0x155,  XX)  // Saipan
+    UNHANDLED_COUNTRY(0x15A,  XX)  // Tinian Island
+    UNHANDLED_COUNTRY(0x52FA, XX)  // Midway Islands
+    END_UNHANDLED_COUNTRIES(US)
+
+    // Countries using the "default" engine set.
+    UNHANDLED_COUNTRY(0x2,                 AG)  // Antigua and Barbuda
+    UNHANDLED_COUNTRY(0x3,                 AF)  // Afghanistan
+    UNHANDLED_COUNTRY(0x9,                 AO)  // Angola
+    UNHANDLED_COUNTRY(0x12,                BB)  // Barbados
+    UNHANDLED_COUNTRY(0x13,                BW)  // Botswana
+    UNHANDLED_COUNTRY(0x16,                BS)  // Bahamas
+    UNHANDLED_COUNTRY(0x17,                BD)  // Bangladesh
+    UNHANDLED_COUNTRY(0x1B,                MM)  // Myanmar
+    UNHANDLED_COUNTRY(0x1E,                SB)  // Solomon Islands
+    UNHANDLED_COUNTRY(0x22,                BT)  // Bhutan
+    UNHANDLED_COUNTRY(0x28,                KH)  // Cambodia
+    UNHANDLED_COUNTRY(0x2A,                LK)  // Sri Lanka
+    UNHANDLED_COUNTRY(0x32,                KM)  // Comoros
+    UNHANDLED_COUNTRY(0x38,                CU)  // Cuba
+    UNHANDLED_COUNTRY(0x3F,                DM)  // Dominica
+    UNHANDLED_COUNTRY(0x45,                GQ)  // Equatorial Guinea
+    UNHANDLED_COUNTRY(0x47,                ER)  // Eritrea
+    UNHANDLED_COUNTRY(0x49,                ET)  // Ethiopia
+    UNHANDLED_COUNTRY(0x4E,                FJ)  // Fiji
+    UNHANDLED_COUNTRY(0x50,                FM)  // Micronesia
+    UNHANDLED_COUNTRY(0x56,                GM)  // Gambia
+    UNHANDLED_COUNTRY(0x58,                GE)  // Georgia
+    UNHANDLED_COUNTRY(0x59,                GH)  // Ghana
+    UNHANDLED_COUNTRY(0x5B,                GD)  // Grenada
+    UNHANDLED_COUNTRY(0x65,                GY)  // Guyana
+    UNHANDLED_COUNTRY(0x83,                KP)  // North Korea
+    UNHANDLED_COUNTRY(0x85,                KI)  // Kiribati
+    UNHANDLED_COUNTRY(0x8A,                LA)  // Laos
+    UNHANDLED_COUNTRY(0x8E,                LR)  // Liberia
+    UNHANDLED_COUNTRY(0x92,                LS)  // Lesotho
+    UNHANDLED_COUNTRY(0x95,                MG)  // Madagascar
+    UNHANDLED_COUNTRY(0x98,                MD)  // Moldova
+    UNHANDLED_COUNTRY(0x9A,                MN)  // Mongolia
+    UNHANDLED_COUNTRY(0x9C,                MW)  // Malawi
+    UNHANDLED_COUNTRY(0xA0,                MU)  // Mauritius
+    UNHANDLED_COUNTRY(0xA5,                MV)  // Maldives
+    UNHANDLED_COUNTRY(0xAE,                VU)  // Vanuatu
+    UNHANDLED_COUNTRY(0xAF,                NG)  // Nigeria
+    UNHANDLED_COUNTRY(0xB2,                NP)  // Nepal
+    UNHANDLED_COUNTRY(0xB4,                NR)  // Nauru
+    UNHANDLED_COUNTRY(0xB5,                SR)  // Suriname
+    UNHANDLED_COUNTRY(0xC2,                PG)  // Papua New Guinea
+    UNHANDLED_COUNTRY(0xC3,                PW)  // Palau
+    UNHANDLED_COUNTRY(0xC7,                MH)  // Marshall Islands
+    UNHANDLED_COUNTRY(0xCC,                RW)  // Rwanda
+    UNHANDLED_COUNTRY(0xCF,                KN)  // Saint Kitts and Nevis
+    UNHANDLED_COUNTRY(0xD0,                SC)  // Seychelles
+    UNHANDLED_COUNTRY(0xD5,                SL)  // Sierra Leone
+    UNHANDLED_COUNTRY(0xD8,                SO)  // Somalia
+    UNHANDLED_COUNTRY(0xDA,                LC)  // Saint Lucia
+    UNHANDLED_COUNTRY(0xE7,                TO)  // Tonga
+    UNHANDLED_COUNTRY(0xEC,                TV)  // Tuvalu
+    UNHANDLED_COUNTRY(0xEF,                TZ)  // Tanzania
+    UNHANDLED_COUNTRY(0xF0,                UG)  // Uganda
+    UNHANDLED_COUNTRY(0xF8,                VC)  // Saint Vincent and the
+                                                // Grenadines
+    UNHANDLED_COUNTRY(0xFE,                NA)  // Namibia
+    UNHANDLED_COUNTRY(0x103,               WS)  // Samoa
+    UNHANDLED_COUNTRY(0x104,               SZ)  // Swaziland
+    UNHANDLED_COUNTRY(0x107,               ZM)  // Zambia
+    UNHANDLED_COUNTRY(0x12C,               AI)  // Anguilla
+    UNHANDLED_COUNTRY(0x12D,               AQ)  // Antarctica
+    UNHANDLED_COUNTRY(0x143,               XX)  // Guantanamo Bay
+    UNHANDLED_COUNTRY(GEOID_NOT_AVAILABLE, XX)  // Unknown location
+    default:                                    // Unhandled location
+    END_UNHANDLED_COUNTRIES(default)
+  }
+}
+
+}  // namespace
+
+namespace TemplateURLPrepopulateData {
+
+void RegisterUserPrefs(PrefService* prefs) {
+  prefs->RegisterIntegerPref(prefs::kGeoIDAtInstall, -1);
+}
+
+int GetDataVersion() {
+  return 19;  // Increment this if you change the above data in ways that mean
+              // users with existing data should get a new version.
+}
+
+void GetPrepopulatedEngines(PrefService* prefs,
+                            std::vector<TemplateURL*>* t_urls,
+                            size_t* default_search_provider_index) {
+  // TODO(pkasting): http://b/1225464 GeoID is not available on Win2k.  We'll
+  // need to do something else there.
+  const PrepopulatedEngine** engines;
+  size_t num_engines;
+  GetPrepopulationSetFromGeoID(prefs, &engines, &num_engines);
+  *default_search_provider_index = 0;
+
+  for (size_t i = 0; i < num_engines; ++i) {
+    TemplateURL* new_turl = new TemplateURL();
+    new_turl->SetURL(engines[i]->search_url, 0, 0);
+    if (engines[i]->favicon_url)
+      new_turl->SetFavIconURL(GURL(engines[i]->favicon_url));
+    if (engines[i]->suggest_url)
+      new_turl->SetSuggestionsURL(engines[i]->suggest_url, 0, 0);
+    new_turl->set_short_name(engines[i]->name);
+    if (engines[i]->keyword == NULL)
+      new_turl->set_autogenerate_keyword(true);
+    else
+      new_turl->set_keyword(engines[i]->keyword);
+    new_turl->set_show_in_default_list(true);
+    new_turl->set_safe_for_autoreplace(true);
+    new_turl->set_date_created(Time());
+    std::vector<std::string> turl_encodings;
+    turl_encodings.push_back(engines[i]->encoding);
+    new_turl->set_input_encodings(turl_encodings);
+    new_turl->set_prepopulate_id(engines[i]->id);
+    t_urls->push_back(new_turl);
+  }
+}
+
+}  // namespace TemplateURLPrepopulateData
diff --git a/chrome/browser/template_url_prepopulate_data.h b/chrome/browser/template_url_prepopulate_data.h
new file mode 100644
index 0000000..9bc3a70
--- /dev/null
+++ b/chrome/browser/template_url_prepopulate_data.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_TEMPLATE_URL_PREPOPULATE_DATA_H__
+#define CHROME_BROWSER_TEMPLATE_URL_PREPOPULATE_DATA_H__
+
+#include <vector>
+
+class PrefService;
+class TemplateURL;
+
+namespace TemplateURLPrepopulateData {
+
+void RegisterUserPrefs(PrefService* prefs);
+
+// Returns the current version of the prepopulate data, so callers can know when
+// they need to re-merge.
+int GetDataVersion();
+
+// Loads the set of TemplateURLs from the prepopulate data.  Ownership of the
+// TemplateURLs is passed to the caller.  On return,
+// |default_search_provider_index| is set to the index of the default search
+// provider.
+void GetPrepopulatedEngines(PrefService* prefs,
+                            std::vector<TemplateURL*>* t_urls,
+                            size_t* default_search_provider_index);
+
+}  // namespace TemplateURLPrepopulateData
+
+#endif  // CHROME_BROWSER_TEMPLATE_URL_PREPOPULATE_DATA_H__
+
diff --git a/chrome/browser/template_url_prepopulate_data_unittest.cc b/chrome/browser/template_url_prepopulate_data_unittest.cc
new file mode 100644
index 0000000..14c6abba
--- /dev/null
+++ b/chrome/browser/template_url_prepopulate_data_unittest.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_prepopulate_data.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/scoped_vector.h"
+#include "chrome/test/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+typedef testing::Test TemplateURLPrepopulateDataTest;
+
+// Verifies the set of prepopulate data doesn't contain entries with duplicate
+// ids.
+TEST_F(TemplateURLPrepopulateDataTest, UniqueIDs) {
+  // GEO ids.
+  int ids[] = { 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC,
+                0xE, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+                0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x20, 0x22, 0x23, 0x25, 0x26,
+                0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x31, 0x32,
+                0x33, 0x36, 0x37, 0x38, 0x39, 0x3B, 0x3D, 0x3E, 0x3F, 0x41,
+                0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4B, 0x4D,
+                0x4E, 0x50, 0x51, 0x54, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B,
+                0x5D, 0x5E, 0x62, 0x63, 0x64, 0x65, 0x67, 0x68, 0x6A, 0x6C,
+                0x6D, 0x6E, 0x6F, 0x71, 0x72, 0x74, 0x75, 0x76, 0x77, 0x79,
+                0x7A, 0x7C, 0x7D, 0x7E, 0x7F, 0x81, 0x82, 0x83, 0x85, 0x86,
+                0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x91, 0x92,
+                0x93, 0x94, 0x95, 0x97, 0x98, 0x9A, 0x9C, 0x9D, 0x9E, 0x9F,
+                0xA0, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xAD, 0xAE,
+                0xAF, 0xB0, 0xB1, 0xB2, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9,
+                0xBB, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6,
+                0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0,
+                0xD1, 0xD2, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB,
+                0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE3, 0xE4, 0xE7, 0xE8,
+                0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2,
+                0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFB, 0xFC, 0xFD, 0xFE,
+                0x102, 0x103, 0x104, 0x105, 0x107, 0x108, 0x10D, 0x12C, 0x12D,
+                0x12E, 0x12F, 0x130, 0x131, 0x132, 0x133, 0x134, 0x135, 0x136,
+                0x137, 0x138, 0x139, 0x13A, 0x13B, 0x13D, 0x13E, 0x13F, 0x141,
+                0x142, 0x143, 0x144, 0x145, 0x146, 0x147, 0x148, 0x149, 0x14A,
+                0x14B, 0x14C, 0x14D, 0x14E, 0x14F, 0x150, 0x151, 0x152, 0x153,
+                0x154, 0x155, 0x156, 0x157, 0x15A, 0x15B, 0x15C, 0x15D, 0x15F,
+                0x160, 0x3B16, 0x4CA2, 0x52FA, 0x6F60E7, -1 };
+  TestingProfile profile;
+  for (size_t i = 0; i < arraysize(ids); ++i) {
+    profile.GetPrefs()->SetInteger(prefs::kGeoIDAtInstall, ids[i]);
+    ScopedVector<TemplateURL> urls;
+    size_t url_count;
+    TemplateURLPrepopulateData::GetPrepopulatedEngines(
+        profile.GetPrefs(), &(urls.get()), &url_count);
+    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()) ==
+                  unique_ids.end());
+      unique_ids.insert(urls[turl_i]->prepopulate_id());
+    }
+  }
+}
diff --git a/chrome/browser/template_url_unittest.cc b/chrome/browser/template_url_unittest.cc
new file mode 100644
index 0000000..d5d745f
--- /dev/null
+++ b/chrome/browser/template_url_unittest.cc
@@ -0,0 +1,386 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/string_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/rlz/rlz.h"
+#include "chrome/browser/template_url.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TemplateURLTest : public testing::Test {
+ public:
+  virtual void TearDown() {
+    delete TemplateURLRef::google_base_url_;
+    TemplateURLRef::google_base_url_ = NULL;
+  }
+
+  void CheckSuggestBaseURL(const wchar_t* base_url,
+                           const wchar_t* base_suggest_url) const {
+    delete TemplateURLRef::google_base_url_;
+    TemplateURLRef::google_base_url_ = new std::wstring(base_url);
+    EXPECT_STREQ(base_suggest_url,
+                 TemplateURLRef::GoogleBaseSuggestURLValue().c_str());
+  }
+};
+
+TEST_F(TemplateURLTest, Defaults) {
+  TemplateURL url;
+  ASSERT_FALSE(url.show_in_default_list());
+  ASSERT_FALSE(url.safe_for_autoreplace());
+  ASSERT_EQ(0, url.prepopulate_id());
+}
+
+TEST_F(TemplateURLTest, TestValidWithComplete) {
+  TemplateURLRef ref(L"{searchTerms}", 0, 0);
+  ASSERT_TRUE(ref.IsValid());
+}
+
+TEST_F(TemplateURLTest, URLRefTestSearchTerms) {
+  TemplateURL t_url;
+  TemplateURLRef ref(L"http://foo{searchTerms}", 0, 0);
+  ASSERT_TRUE(ref.IsValid());
+
+  ASSERT_TRUE(ref.SupportsReplacement());
+  GURL result = ref.ReplaceSearchTerms(t_url, L"search",
+      TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring());
+  ASSERT_TRUE(result.is_valid());
+  ASSERT_EQ("http://foosearch/", result.spec());
+}
+
+TEST_F(TemplateURLTest, URLRefTestCount) {
+  TemplateURL t_url;
+  TemplateURLRef ref(L"http://foo{searchTerms}{count?}", 0, 0);
+  ASSERT_TRUE(ref.IsValid());
+  ASSERT_TRUE(ref.SupportsReplacement());
+  GURL result = ref.ReplaceSearchTerms(t_url, L"X",
+      TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring());
+  ASSERT_TRUE(result.is_valid());
+  ASSERT_EQ("http://foox/", result.spec());
+}
+
+TEST_F(TemplateURLTest, URLRefTestCount2) {
+  TemplateURL t_url;
+  TemplateURLRef ref(L"http://foo{searchTerms}{count}", 0, 0);
+  ASSERT_TRUE(ref.IsValid());
+  ASSERT_TRUE(ref.SupportsReplacement());
+  GURL result = ref.ReplaceSearchTerms(t_url, L"X",
+      TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring());
+  ASSERT_TRUE(result.is_valid());
+  ASSERT_EQ("http://foox10/", result.spec());
+}
+
+TEST_F(TemplateURLTest, URLRefTestIndices) {
+  TemplateURL t_url;
+  TemplateURLRef ref(L"http://foo{searchTerms}x{startIndex?}y{startPage?}",
+                     1, 2);
+  ASSERT_TRUE(ref.IsValid());
+  ASSERT_TRUE(ref.SupportsReplacement());
+  GURL result = ref.ReplaceSearchTerms(t_url, L"X",
+      TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring());
+  ASSERT_TRUE(result.is_valid());
+  ASSERT_EQ("http://fooxxy/", result.spec());
+}
+
+TEST_F(TemplateURLTest, URLRefTestIndices2) {
+  TemplateURL t_url;
+  TemplateURLRef ref(L"http://foo{searchTerms}x{startIndex}y{startPage}", 1, 2);
+  ASSERT_TRUE(ref.IsValid());
+  ASSERT_TRUE(ref.SupportsReplacement());
+  GURL result = ref.ReplaceSearchTerms(t_url, L"X",
+      TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring());
+  ASSERT_TRUE(result.is_valid());
+  ASSERT_EQ("http://fooxx1y2/", result.spec());
+}
+
+TEST_F(TemplateURLTest, URLRefTestEncoding) {
+  TemplateURL t_url;
+  TemplateURLRef ref(
+      L"http://foo{searchTerms}x{inputEncoding?}y{outputEncoding?}a", 1, 2);
+  ASSERT_TRUE(ref.IsValid());
+  ASSERT_TRUE(ref.SupportsReplacement());
+  GURL result = ref.ReplaceSearchTerms(t_url, L"X",
+      TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring());
+  ASSERT_TRUE(result.is_valid());
+  ASSERT_EQ("http://fooxxutf-8ya/", result.spec());
+}
+
+TEST_F(TemplateURLTest, InputEncodingBeforeSearchTerm) {
+  TemplateURL t_url;
+  TemplateURLRef ref(
+      L"http://foox{inputEncoding?}a{searchTerms}y{outputEncoding?}b", 1, 2);
+  ASSERT_TRUE(ref.IsValid());
+  ASSERT_TRUE(ref.SupportsReplacement());
+  GURL result = ref.ReplaceSearchTerms(t_url, L"X",
+      TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring());
+  ASSERT_TRUE(result.is_valid());
+  ASSERT_EQ("http://fooxutf-8axyb/", result.spec());
+}
+
+TEST_F(TemplateURLTest, URLRefTestEncoding2) {
+  TemplateURL t_url;
+  TemplateURLRef ref(
+      L"http://foo{searchTerms}x{inputEncoding}y{outputEncoding}a", 1, 2);
+  ASSERT_TRUE(ref.IsValid());
+  ASSERT_TRUE(ref.SupportsReplacement());
+  GURL result = ref.ReplaceSearchTerms(t_url, L"X",
+      TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring());
+  ASSERT_TRUE(result.is_valid());
+  ASSERT_EQ("http://fooxxutf-8yutf-8a/", result.spec());
+}
+
+TEST_F(TemplateURLTest, URLRefTermToWide) {
+  struct ToWideCase {
+    const char* encoded_search_term;
+    const wchar_t* expected_decoded_term;
+  } to_wide_cases[] = {
+    {"hello+world", L"hello world"},
+    // Test some big-5 input.
+    {"%a7A%A6%6e+to+you", L"\x4f60\x597d to you"},
+    // Test some UTF-8 input. We should fall back to this when the encoding
+    // doesn't look like big-5. We have a '5' in the middle, which is an invalid
+    // Big-5 trailing byte.
+    {"%e4%bd%a05%e5%a5%bd+to+you", L"\x4f60\x35\x597d to you"},
+    // Undecodable input should stay escaped.
+    {"%91%01+abcd", L"%91%01 abcd"},
+  };
+
+  TemplateURL t_url;
+
+  // Set one input encoding: big-5. This is so we can test fallback to UTF-8.
+  std::vector<std::string> encodings;
+  encodings.push_back("big-5");
+  t_url.set_input_encodings(encodings);
+
+  TemplateURLRef ref(L"http://foo?q={searchTerms}", 1, 2);
+  ASSERT_TRUE(ref.IsValid());
+  ASSERT_TRUE(ref.SupportsReplacement());
+
+  for (int i = 0; i < arraysize(to_wide_cases); i++) {
+    std::wstring result = ref.SearchTermToWide(t_url,
+        to_wide_cases[i].encoded_search_term);
+
+    EXPECT_EQ(std::wstring(to_wide_cases[i].expected_decoded_term), result);
+  }
+}
+
+TEST_F(TemplateURLTest, SetFavIcon) {
+  TemplateURL url;
+  GURL favicon_url("http://favicon.url");
+  url.SetFavIconURL(favicon_url);
+  ASSERT_EQ(1, url.image_refs().size());
+  ASSERT_TRUE(favicon_url == url.GetFavIconURL());
+
+  GURL favicon_url2("http://favicon2.url");
+  url.SetFavIconURL(favicon_url2);
+  ASSERT_EQ(1, url.image_refs().size());
+  ASSERT_TRUE(favicon_url2 == url.GetFavIconURL());
+}
+
+TEST_F(TemplateURLTest, DisplayURLToURLRef) {
+  struct TestData {
+    const std::wstring url;
+    const std::wstring expected_result;
+  } data[] = {
+    { L"http://foo{searchTerms}x{inputEncoding}y{outputEncoding}a",
+      L"http://foo%sx{inputEncoding}y{outputEncoding}a" },
+    { L"http://X",
+      L"http://X" },
+    { L"http://foo{searchTerms",
+      L"http://foo{searchTerms" },
+    { L"http://foo{searchTerms}{language}",
+      L"http://foo%s{language}" },
+  };
+  for (int i = 0; i < arraysize(data); ++i) {
+    TemplateURLRef ref(data[i].url, 1, 2);
+    EXPECT_EQ(data[i].expected_result, ref.DisplayURL());
+    EXPECT_EQ(data[i].url,
+              TemplateURLRef::DisplayURLToURLRef(ref.DisplayURL()));
+  }
+}
+
+TEST_F(TemplateURLTest, ReplaceSearchTerms) {
+  struct TestData {
+    const std::wstring url;
+    const std::string expected_result;
+  } data[] = {
+    { L"http://foo/{language}{searchTerms}{inputEncoding}",
+      "http://foo/{language}XUTF-8" },
+    { L"http://foo/{language}{inputEncoding}{searchTerms}",
+      "http://foo/{language}UTF-8X" },
+    { L"http://foo/{searchTerms}{language}{inputEncoding}",
+      "http://foo/X{language}UTF-8" },
+    { L"http://foo/{searchTerms}{inputEncoding}{language}",
+      "http://foo/XUTF-8{language}" },
+    { L"http://foo/{inputEncoding}{searchTerms}{language}",
+      "http://foo/UTF-8X{language}" },
+    { L"http://foo/{inputEncoding}{language}{searchTerms}",
+      "http://foo/UTF-8{language}X" },
+    { L"http://foo/{language}a{searchTerms}a{inputEncoding}a",
+      "http://foo/{language}aXaUTF-8a" },
+    { L"http://foo/{language}a{inputEncoding}a{searchTerms}a",
+      "http://foo/{language}aUTF-8aXa" },
+    { L"http://foo/{searchTerms}a{language}a{inputEncoding}a",
+      "http://foo/Xa{language}aUTF-8a" },
+    { L"http://foo/{searchTerms}a{inputEncoding}a{language}a",
+      "http://foo/XaUTF-8a{language}a" },
+    { L"http://foo/{inputEncoding}a{searchTerms}a{language}a",
+      "http://foo/UTF-8aXa{language}a" },
+    { L"http://foo/{inputEncoding}a{language}a{searchTerms}a",
+      "http://foo/UTF-8a{language}aXa" },
+  };
+  TemplateURL turl;
+  turl.add_input_encoding("UTF-8");
+  for (int i = 0; i < arraysize(data); ++i) {
+    TemplateURLRef ref(data[i].url, 1, 2);
+    EXPECT_TRUE(ref.IsValid());
+    EXPECT_TRUE(ref.SupportsReplacement());
+    std::string expected_result = data[i].expected_result;
+    ReplaceSubstringsAfterOffset(&expected_result, 0, "{language}",
+        WideToASCII(g_browser_process->GetApplicationLocale()));
+    GURL result = ref.ReplaceSearchTerms(turl, L"X",
+        TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring());
+    EXPECT_TRUE(result.is_valid());
+    EXPECT_EQ(expected_result, result.spec());
+  }
+}
+
+
+// Tests replacing search terms in various encodings and making sure the
+// generated URL matches the expected value.
+TEST_F(TemplateURLTest, ReplaceArbitrarySearchTerms) {
+  struct TestData {
+    const std::string encoding;
+    const std::wstring search_term;
+    const std::wstring url;
+    const std::string expected_result;
+  } data[] = {
+    { "BIG5",  L"\x60BD", L"http://foo/{searchTerms}{inputEncoding}",
+      "http://foo/%B1~BIG5" },
+    { "UTF-8", L"blah",   L"http://foo/{searchTerms}{inputEncoding}",
+      "http://foo/blahUTF-8" },
+  };
+  for (int i = 0; i < arraysize(data); ++i) {
+    TemplateURL turl;
+    turl.add_input_encoding(data[i].encoding);
+    TemplateURLRef ref(data[i].url, 1, 2);
+    GURL result = ref.ReplaceSearchTerms(turl, data[i].search_term,
+        TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring());
+    EXPECT_TRUE(result.is_valid());
+    EXPECT_EQ(data[i].expected_result, result.spec());
+  }
+}
+
+TEST_F(TemplateURLTest, Suggestions) {
+  struct TestData {
+    const int accepted_suggestion;
+    const std::wstring original_query_for_suggestion;
+    const std::string expected_result;
+  } data[] = {
+    { TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring(),
+      "http://bar/foo?q=foobar" },
+    { TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, L"foo",
+      "http://bar/foo?q=foobar" },
+    { TemplateURLRef::NO_SUGGESTION_CHOSEN, std::wstring(),
+      "http://bar/foo?aq=f&q=foobar" },
+    { TemplateURLRef::NO_SUGGESTION_CHOSEN, L"foo",
+      "http://bar/foo?aq=f&q=foobar" },
+    { 0, std::wstring(), "http://bar/foo?aq=0&oq=&q=foobar" },
+    { 1, L"foo", "http://bar/foo?aq=1&oq=foo&q=foobar" },
+  };
+  TemplateURL turl;
+  turl.add_input_encoding("UTF-8");
+  TemplateURLRef ref(L"http://bar/foo?{google:acceptedSuggestion}"
+      L"{google:originalQueryForSuggestion}q={searchTerms}", 1, 2);
+  ASSERT_TRUE(ref.IsValid());
+  ASSERT_TRUE(ref.SupportsReplacement());
+  for (int i = 0; i < arraysize(data); ++i) {
+    GURL result = ref.ReplaceSearchTerms(turl, L"foobar",
+        data[i].accepted_suggestion, data[i].original_query_for_suggestion);
+    EXPECT_TRUE(result.is_valid());
+    EXPECT_EQ(data[i].expected_result, result.spec());
+  }
+}
+
+TEST_F(TemplateURLTest, RLZ) {
+  std::wstring rlz_string;
+  RLZTracker::GetAccessPointRlz(RLZTracker::CHROME_OMNIBOX, &rlz_string);
+
+  TemplateURL t_url;
+  TemplateURLRef ref(L"http://bar/{google:RLZ}{searchTerms}", 1, 2);
+  ASSERT_TRUE(ref.IsValid());
+  ASSERT_TRUE(ref.SupportsReplacement());
+  GURL result = ref.ReplaceSearchTerms(t_url, L"x",
+      TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring());
+  ASSERT_TRUE(result.is_valid());
+  // TODO(levin): fix this!
+  //  ASSERT_EQ("http://bar/" + WideToUTF8(rlz_string) + "x", result.spec());
+}
+
+TEST_F(TemplateURLTest, HostAndSearchTermKey) {
+  struct TestData {
+    const std::wstring url;
+    const std::string host;
+    const std::string path;
+    const std::string search_term_key;
+  } data[] = {
+    { L"http://blah/?foo=bar&q={searchTerms}&b=x", "blah", "/", "q"},
+
+    // No query key should result in empty values.
+    { L"http://blah/{searchTerms}", "", "", ""},
+
+    // No term should result in empty values.
+    { L"http://blah/", "", "", ""},
+
+    // Multiple terms should result in empty values.
+    { L"http://blah/?q={searchTerms}&x={searchTerms}", "", "", ""},
+
+    // Term in the host shouldn't match.
+    { L"http://{searchTerms}", "", "", ""},
+
+    { L"http://blah/?q={searchTerms}", "blah", "/", "q"},
+
+    // Single term with extra chars in value should match.
+    { L"http://blah/?q=stock:{searchTerms}", "blah", "/", "q"},
+
+  };
+
+  TemplateURL t_url;
+  for (int i = 0; i < arraysize(data); ++i) {
+    t_url.SetURL(data[i].url, 0, 0);
+    EXPECT_EQ(data[i].host, t_url.url()->GetHost());
+    EXPECT_EQ(data[i].path, t_url.url()->GetPath());
+    EXPECT_EQ(data[i].search_term_key, t_url.url()->GetSearchTermKey());
+  }
+}
+
+TEST_F(TemplateURLTest, GoogleBaseSuggestURL) {
+  static const struct {
+    const wchar_t* const base_url;
+    const wchar_t* const base_suggest_url;
+  } data[] = {
+    { L"http://google.com/", L"http://clients1.google.com/complete/", },
+    { L"http://www.google.com/", L"http://clients1.google.com/complete/", },
+    { L"http://www.google.co.uk/", L"http://clients1.google.co.uk/complete/", },
+    { L"http://www.google.com.by/",
+      L"http://clients1.google.com.by/complete/", },
+    { L"http://google.com/intl/xx/", L"http://clients1.google.com/complete/", },
+  };
+
+  for (int i = 0; i < arraysize(data); ++i)
+    CheckSuggestBaseURL(data[i].base_url, data[i].base_suggest_url);
+}
+
+TEST_F(TemplateURLTest, Keyword) {
+  TemplateURL t_url;
+  t_url.SetURL(L"http://www.google.com/search", 0, 0);
+  EXPECT_FALSE(t_url.autogenerate_keyword());
+  t_url.set_keyword(L"foo");
+  EXPECT_EQ(L"foo", t_url.keyword());
+  t_url.set_autogenerate_keyword(true);
+  EXPECT_TRUE(t_url.autogenerate_keyword());
+  EXPECT_EQ(L"google.com", t_url.keyword());
+  t_url.set_keyword(L"foo");
+  EXPECT_FALSE(t_url.autogenerate_keyword());
+  EXPECT_EQ(L"foo", t_url.keyword());
+}
diff --git a/chrome/browser/views/clear_browsing_data.cc b/chrome/browser/views/clear_browsing_data.cc
index 2bdd1f2e..b71bf99f 100644
--- a/chrome/browser/views/clear_browsing_data.cc
+++ b/chrome/browser/views/clear_browsing_data.cc
@@ -6,7 +6,7 @@
 
 #include "chrome/app/locales/locale_settings.h"
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/views/standard_layout.h"
 #include "chrome/common/l10n_util.h"
 #include "chrome/views/background.h"
diff --git a/chrome/browser/views/edit_keyword_controller.cc b/chrome/browser/views/edit_keyword_controller.cc
index 86f1cd0..4eb913f 100644
--- a/chrome/browser/views/edit_keyword_controller.cc
+++ b/chrome/browser/views/edit_keyword_controller.cc
@@ -7,8 +7,8 @@
 #include "base/string_util.h"
 #include "chrome/app/theme/theme_resources.h"
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/url_fixer_upper.h"
 #include "chrome/browser/user_metrics.h"
 #include "chrome/browser/views/keyword_editor_view.h"
diff --git a/chrome/browser/views/first_run_bubble.cc b/chrome/browser/views/first_run_bubble.cc
index 25b01db..ac6678aa 100644
--- a/chrome/browser/views/first_run_bubble.cc
+++ b/chrome/browser/views/first_run_bubble.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/browser_list.h"
 #include "chrome/browser/browser_window.h"
 #include "chrome/browser/options_window.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/views/standard_layout.h"
 #include "chrome/common/l10n_util.h"
 #include "chrome/common/resource_bundle.h"
diff --git a/chrome/browser/views/keyword_editor_view.cc b/chrome/browser/views/keyword_editor_view.cc
index e4acaa6..be35283 100644
--- a/chrome/browser/views/keyword_editor_view.cc
+++ b/chrome/browser/views/keyword_editor_view.cc
@@ -12,8 +12,8 @@
 #include "chrome/app/theme/theme_resources.h"
 #include "chrome/browser/history/history.h"
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/user_metrics.h"
 #include "chrome/browser/views/edit_keyword_controller.h"
 #include "chrome/browser/views/standard_layout.h"
diff --git a/chrome/browser/views/keyword_editor_view.h b/chrome/browser/views/keyword_editor_view.h
index c2135a45..879ed24 100644
--- a/chrome/browser/views/keyword_editor_view.h
+++ b/chrome/browser/views/keyword_editor_view.h
@@ -9,7 +9,7 @@
 #include <map>
 
 #include "base/logging.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/views/dialog_delegate.h"
 #include "chrome/views/native_button.h"
 #include "chrome/views/table_view.h"
diff --git a/chrome/browser/views/keyword_editor_view_unittest.cc b/chrome/browser/views/keyword_editor_view_unittest.cc
index 4d04a07b..1bbb43c 100644
--- a/chrome/browser/views/keyword_editor_view_unittest.cc
+++ b/chrome/browser/views/keyword_editor_view_unittest.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/views/keyword_editor_view.h"
 #include "chrome/test/testing_profile.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc
index 8c727b7..cad8583 100644
--- a/chrome/browser/views/location_bar_view.cc
+++ b/chrome/browser/views/location_bar_view.cc
@@ -13,9 +13,9 @@
 #include "chrome/browser/browser_list.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_model.h"
 #include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/view_ids.h"
 #include "chrome/browser/views/info_bubble.h"
 #include "chrome/browser/views/first_run_bubble.h"
diff --git a/chrome/browser/views/options/general_page_view.cc b/chrome/browser/views/options/general_page_view.cc
index a7964f6..d506fdc 100644
--- a/chrome/browser/views/options/general_page_view.cc
+++ b/chrome/browser/views/options/general_page_view.cc
@@ -15,10 +15,10 @@
 #include "chrome/browser/dom_ui/new_tab_ui.h"
 #include "chrome/browser/history/history.h"
 #include "chrome/browser/profile.h"
-#include "chrome/browser/session_startup_pref.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_model.h"
 #include "chrome/browser/shell_integration.h"
+#include "chrome/browser/session_startup_pref.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/browser/url_fixer_upper.h"
 #include "chrome/browser/views/keyword_editor_view.h"
 #include "chrome/browser/views/options/options_group_view.h"
diff --git a/chrome/browser/views/tabs/tab_strip.cc b/chrome/browser/views/tabs/tab_strip.cc
index 7791289b..3232ec4 100644
--- a/chrome/browser/views/tabs/tab_strip.cc
+++ b/chrome/browser/views/tabs/tab_strip.cc
@@ -1470,17 +1470,8 @@
 }
 
 void TabStrip::StartInsertTabAnimation(int index) {
-  // Don't shock users by letting all tabs move when they are focused
-  // on the tab-strip. Wait for later, when they aren't looking.
-  int last_tab_index = GetTabCount() - 2;
-  if (last_tab_index > 0) {
-    Tab* last_tab = GetTabAt(last_tab_index);
-	  available_width_for_tabs_ = std::min(
-		  GetAvailableWidthForTabs(last_tab) + last_tab->width(),
-		  width() - (kNewTabButtonHOffset + newtab_button_size_.width()));
-  } else {
-    available_width_for_tabs_ = -1;
-  }
+  // The TabStrip can now use its entire width to lay out Tabs.
+  available_width_for_tabs_ = -1;
   if (active_animation_.get())
     active_animation_->Stop();
   active_animation_.reset(new InsertTabAnimation(this, index));
diff --git a/chrome/browser/webdata/web_data_service.cc b/chrome/browser/webdata/web_data_service.cc
index a3f8e550..d736ac4 100644
--- a/chrome/browser/webdata/web_data_service.cc
+++ b/chrome/browser/webdata/web_data_service.cc
@@ -10,7 +10,7 @@
 #include "base/path_service.h"
 #include "base/scoped_ptr.h"
 #include "chrome/browser/password_manager/ie7_password.h"
-#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/template_url.h"
 #include "chrome/common/chrome_constants.h"
 #include "webkit/glue/password_form.h"
 #include "webkit/glue/autofill_form.h"
diff --git a/chrome/browser/webdata/web_database.cc b/chrome/browser/webdata/web_database.cc
index af78244..5998282 100644
--- a/chrome/browser/webdata/web_database.cc
+++ b/chrome/browser/webdata/web_database.cc
@@ -15,7 +15,7 @@
 #include "base/values.h"
 #include "chrome/browser/history/history_database.h"
 #include "chrome/browser/password_manager/encryptor.h"
-#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/template_url.h"
 #include "chrome/common/l10n_util.h"
 #include "chrome/common/scoped_vector.h"
 #include "webkit/glue/password_form.h"
diff --git a/chrome/browser/webdata/web_database.h b/chrome/browser/webdata/web_database.h
index 54ec840..19059ee8 100644
--- a/chrome/browser/webdata/web_database.h
+++ b/chrome/browser/webdata/web_database.h
@@ -10,7 +10,7 @@
 
 #include "base/basictypes.h"
 #include "chrome/browser/meta_table_helper.h"
-#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/template_url.h"
 #include "chrome/common/sqlite_utils.h"
 #include "skia/include/SkBitmap.h"
 #include "webkit/glue/autofill_form.h"
diff --git a/chrome/browser/webdata/web_database_unittest.cc b/chrome/browser/webdata/web_database_unittest.cc
index 8842e40..349b803 100644
--- a/chrome/browser/webdata/web_database_unittest.cc
+++ b/chrome/browser/webdata/web_database_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/string_util.h"
 #include "base/time.h"
 #include "base/values.h"
-#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/template_url.h"
 #include "chrome/browser/webdata/web_database.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/stl_util-inl.h"
diff --git a/chrome/chrome.xcodeproj/project.pbxproj b/chrome/chrome.xcodeproj/project.pbxproj
index 364e2726..4596bf2 100644
--- a/chrome/chrome.xcodeproj/project.pbxproj
+++ b/chrome/chrome.xcodeproj/project.pbxproj
@@ -287,6 +287,8 @@
 		E45075F70F150C0C003BE099 /* session_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = E45075F60F150C0C003BE099 /* session_id.cc */; };
 		E45075FA0F150C28003BE099 /* spellcheck_worditerator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BF9220E9D4839009A6919 /* spellcheck_worditerator.cc */; };
 		E45075FF0F150C84003BE099 /* ssl_error_info.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BF9280E9D4839009A6919 /* ssl_error_info.cc */; };
+		E45076010F150C9D003BE099 /* template_url.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BF9420E9D4839009A6919 /* template_url.cc */; };
+		E45076180F150DD2003BE099 /* template_url_model.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BF9460E9D4839009A6919 /* template_url_model.cc */; };
 		E450761A0F150DE9003BE099 /* url_fetcher.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BF9510E9D4839009A6919 /* url_fetcher.cc */; };
 		E45076200F150E0C003BE099 /* web_database.cc in Sources */ = {isa = PBXBuildFile; fileRef = E450761E0F150E0C003BE099 /* web_database.cc */; };
 		E45076850F1530CD003BE099 /* pref_service.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFBEA0E9D4C9F009A6919 /* pref_service.cc */; };
@@ -319,6 +321,7 @@
 		E4F324600EE5D011002533CE /* url_database.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFA130E9D48F7009A6919 /* url_database.cc */; };
 		E4F324650EE5D082002533CE /* sdch_dictionary_fetcher.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFA720E9D4981009A6919 /* sdch_dictionary_fetcher.cc */; };
 		E4F3247A0EE5D17E002533CE /* referrer.cc in Sources */ = {isa = PBXBuildFile; fileRef = E4F324790EE5D17E002533CE /* referrer.cc */; };
+		E4F324860EE5D26F002533CE /* template_url_parser.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BF9490E9D4839009A6919 /* template_url_parser.cc */; };
 		E4F324950EE5D758002533CE /* extension_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = E4F324420EE5CE94002533CE /* extension_unittest.cc */; };
 		E4F324980EE5D7DE002533CE /* snippet_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFA060E9D48F7009A6919 /* snippet_unittest.cc */; };
 		E4F3256E0EE82C83002533CE /* chrome_paths.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFB8F0E9D4C9F009A6919 /* chrome_paths.cc */; };
@@ -329,9 +332,6 @@
 		E4F3258C0EE83767002533CE /* libgoogleurl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D7BFF6E0E9D540F009A6919 /* libgoogleurl.a */; };
 		E4F325C80EE83A45002533CE /* ipc_fuzzing_tests.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFBAD0E9D4C9F009A6919 /* ipc_fuzzing_tests.cc */; };
 		E4F325D10EE83B71002533CE /* ipc_tests.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFBBE0E9D4C9F009A6919 /* ipc_tests.cc */; };
-		9A1EE0F9187ACE2DCB8512E1 /* template_url.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3CCF8AA8A56FF8FE59F0C299 /* template_url.cc */; };
-		C39F08C4FFD9C2F98384F56B /* template_url_model.cc in Sources */ = {isa = PBXBuildFile; fileRef = EA72C084DB3FC0FC595E525E /* template_url_model.cc */; };
-		B9BF55F87A4BB2FD366B6DDC /* template_url_parser.cc in Sources */ = {isa = PBXBuildFile; fileRef = 28AA584AB2ECFB33C7C7FD8A /* template_url_parser.cc */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -1189,12 +1189,17 @@
 		4D7BF93F0E9D4839009A6919 /* task_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = task_manager.h; sourceTree = "<group>"; };
 		4D7BF9400E9D4839009A6919 /* task_manager_resource_providers.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = task_manager_resource_providers.cc; sourceTree = "<group>"; };
 		4D7BF9410E9D4839009A6919 /* task_manager_resource_providers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = task_manager_resource_providers.h; sourceTree = "<group>"; };
+		4D7BF9420E9D4839009A6919 /* template_url.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = template_url.cc; sourceTree = "<group>"; };
 		4D7BF9430E9D4839009A6919 /* template_url.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = template_url.h; sourceTree = "<group>"; };
+		4D7BF9440E9D4839009A6919 /* template_url_fetcher.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = template_url_fetcher.cc; sourceTree = "<group>"; };
 		4D7BF9450E9D4839009A6919 /* template_url_fetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = template_url_fetcher.h; sourceTree = "<group>"; };
+		4D7BF9460E9D4839009A6919 /* template_url_model.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = template_url_model.cc; sourceTree = "<group>"; };
 		4D7BF9470E9D4839009A6919 /* template_url_model.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = template_url_model.h; sourceTree = "<group>"; };
 		4D7BF9480E9D4839009A6919 /* template_url_model_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = template_url_model_unittest.cc; sourceTree = "<group>"; };
+		4D7BF9490E9D4839009A6919 /* template_url_parser.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = template_url_parser.cc; sourceTree = "<group>"; };
 		4D7BF94A0E9D4839009A6919 /* template_url_parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = template_url_parser.h; sourceTree = "<group>"; };
 		4D7BF94B0E9D4839009A6919 /* template_url_parser_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = template_url_parser_unittest.cc; sourceTree = "<group>"; };
+		4D7BF94C0E9D4839009A6919 /* template_url_prepopulate_data.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = template_url_prepopulate_data.cc; sourceTree = "<group>"; };
 		4D7BF94D0E9D4839009A6919 /* template_url_prepopulate_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = template_url_prepopulate_data.h; sourceTree = "<group>"; };
 		4D7BF94E0E9D4839009A6919 /* template_url_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = template_url_unittest.cc; sourceTree = "<group>"; };
 		4D7BF94F0E9D4839009A6919 /* toolbar_model.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = toolbar_model.cc; sourceTree = "<group>"; };
@@ -1734,9 +1739,6 @@
 		E4F324420EE5CE94002533CE /* extension_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = extension_unittest.cc; sourceTree = "<group>"; };
 		E4F324780EE5D17E002533CE /* referrer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = referrer.h; sourceTree = "<group>"; };
 		E4F324790EE5D17E002533CE /* referrer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = referrer.cc; sourceTree = "<group>"; };
-		3CCF8AA8A56FF8FE59F0C299 /* template_url.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = template_url.cc; path = browser/search_engines/template_url.cc; sourceTree = SOURCE_ROOT; };
-		EA72C084DB3FC0FC595E525E /* template_url_model.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = template_url_model.cc; path = browser/search_engines/template_url_model.cc; sourceTree = SOURCE_ROOT; };
-		28AA584AB2ECFB33C7C7FD8A /* template_url_parser.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = template_url_parser.cc; path = browser/search_engines/template_url_parser.cc; sourceTree = SOURCE_ROOT; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -1944,9 +1946,6 @@
 				4D7BFDD00E9D527E009A6919 /* Frameworks */,
 				4D7BFB090E9D4BA1009A6919 /* Projects */,
 				4D7BF3070E9D477E009A6919 /* Products */,
-				3CCF8AA8A56FF8FE59F0C299 /* template_url.cc */,
-				EA72C084DB3FC0FC595E525E /* template_url_model.cc */,
-				28AA584AB2ECFB33C7C7FD8A /* template_url_parser.cc */,
 			);
 			sourceTree = "<group>";
 		};
@@ -2220,12 +2219,17 @@
 				4D7BF93F0E9D4839009A6919 /* task_manager.h */,
 				4D7BF9400E9D4839009A6919 /* task_manager_resource_providers.cc */,
 				4D7BF9410E9D4839009A6919 /* task_manager_resource_providers.h */,
+				4D7BF9420E9D4839009A6919 /* template_url.cc */,
 				4D7BF9430E9D4839009A6919 /* template_url.h */,
+				4D7BF9440E9D4839009A6919 /* template_url_fetcher.cc */,
 				4D7BF9450E9D4839009A6919 /* template_url_fetcher.h */,
+				4D7BF9460E9D4839009A6919 /* template_url_model.cc */,
 				4D7BF9470E9D4839009A6919 /* template_url_model.h */,
 				4D7BF9480E9D4839009A6919 /* template_url_model_unittest.cc */,
+				4D7BF9490E9D4839009A6919 /* template_url_parser.cc */,
 				4D7BF94A0E9D4839009A6919 /* template_url_parser.h */,
 				4D7BF94B0E9D4839009A6919 /* template_url_parser_unittest.cc */,
+				4D7BF94C0E9D4839009A6919 /* template_url_prepopulate_data.cc */,
 				4D7BF94D0E9D4839009A6919 /* template_url_prepopulate_data.h */,
 				4D640D1A0EAE87BD00EBCFC0 /* template_url_prepopulate_data_unittest.cc */,
 				4D7BF94E0E9D4839009A6919 /* template_url_unittest.cc */,
@@ -3730,9 +3734,9 @@
 				E45075FF0F150C84003BE099 /* ssl_error_info.cc in Sources */,
 				E4F3245D0EE5CFDF002533CE /* starred_url_database.cc in Sources */,
 				E45075EE0F150ABA003BE099 /* sync_resource_handler.cc in Sources */,
-				9A1EE0F9187ACE2DCB8512E1 /* template_url.cc in Sources */,
-				C39F08C4FFD9C2F98384F56B /* template_url_model.cc in Sources */,
-				B9BF55F87A4BB2FD366B6DDC /* template_url_parser.cc in Sources */,
+				E45076010F150C9D003BE099 /* template_url.cc in Sources */,
+				E45076180F150DD2003BE099 /* template_url_model.cc in Sources */,
+				E4F324860EE5D26F002533CE /* template_url_parser.cc in Sources */,
 				4D7BFA320E9D4912009A6919 /* text_database.cc in Sources */,
 				4D7BFA370E9D4915009A6919 /* text_database_manager.cc in Sources */,
 				4D7BFA390E9D4918009A6919 /* thumbnail_database.cc in Sources */,
diff --git a/chrome/test/testing_profile.h b/chrome/test/testing_profile.h
index 2d622a7..37e6c17 100644
--- a/chrome/test/testing_profile.h
+++ b/chrome/test/testing_profile.h
@@ -12,8 +12,8 @@
 #include "chrome/browser/browser_prefs.h"
 #include "chrome/browser/history/history.h"
 #include "chrome/browser/profile.h"
-#include "chrome/browser/search_engines/template_url_model.h"
 #include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/template_url_model.h"
 #include "chrome/common/pref_service.h"
 
 class TestingProfile : public Profile {
diff --git a/chrome/test/unit/unit_tests.scons b/chrome/test/unit/unit_tests.scons
index 9a06328..7d0c68a4 100644
--- a/chrome/test/unit/unit_tests.scons
+++ b/chrome/test/unit/unit_tests.scons
@@ -213,10 +213,10 @@
       '$CHROME_DIR/browser/sessions/tab_restore_service_unittest.cc',
       '$CHROME_DIR/browser/site_instance_unittest.cc',
       '$CHROME_DIR/browser/tabs/tab_strip_model_unittest.cc',
-      '$CHROME_DIR/browser/search_engines/template_url_model_unittest.cc',
-      '$CHROME_DIR/browser/search_engines/template_url_parser_unittest.cc',
-      '$CHROME_DIR/browser/search_engines/template_url_prepopulate_data_unittest.cc',
-      '$CHROME_DIR/browser/search_engines/template_url_unittest.cc',
+      '$CHROME_DIR/browser/template_url_model_unittest.cc',
+      '$CHROME_DIR/browser/template_url_parser_unittest.cc',
+      '$CHROME_DIR/browser/template_url_prepopulate_data_unittest.cc',
+      '$CHROME_DIR/browser/template_url_unittest.cc',
       '$CHROME_DIR/browser/url_fixer_upper_unittest.cc',
       '$CHROME_DIR/browser/views/bookmark_editor_view_unittest.cc',
       '$CHROME_DIR/browser/views/keyword_editor_view_unittest.cc',
diff --git a/chrome/test/unit/unittests.vcproj b/chrome/test/unit/unittests.vcproj
index 6b1a977..f00154ee 100644
--- a/chrome/test/unit/unittests.vcproj
+++ b/chrome/test/unit/unittests.vcproj
@@ -687,19 +687,19 @@
 			>
 		</File>
 		<File
-			RelativePath="..\..\browser\search_engines\template_url_model_unittest.cc"
+			RelativePath="..\..\browser\template_url_model_unittest.cc"
 			>
 		</File>
 		<File
-			RelativePath="..\..\browser\search_engines\template_url_parser_unittest.cc"
+			RelativePath="..\..\browser\template_url_parser_unittest.cc"
 			>
 		</File>
 		<File
-			RelativePath="..\..\browser\search_engines\template_url_prepopulate_data_unittest.cc"
+			RelativePath="..\..\browser\template_url_prepopulate_data_unittest.cc"
 			>
 		</File>
 		<File
-			RelativePath="..\..\browser\search_engines\template_url_unittest.cc"
+			RelativePath="..\..\browser\template_url_unittest.cc"
 			>
 		</File>
 		<File