| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/search_engines/template_url_service.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <utility> |
| 9 |
| 10 #include "base/auto_reset.h" |
| 11 #include "base/command_line.h" |
| 12 #include "base/compiler_specific.h" |
| 13 #include "base/guid.h" |
| 14 #include "base/i18n/case_conversion.h" |
| 15 #include "base/memory/scoped_vector.h" |
| 16 #include "base/metrics/histogram.h" |
| 17 #include "base/prefs/pref_service.h" |
| 18 #include "base/stl_util.h" |
| 19 #include "base/strings/string_number_conversions.h" |
| 20 #include "base/strings/string_split.h" |
| 21 #include "base/strings/string_util.h" |
| 22 #include "base/strings/utf_string_conversions.h" |
| 23 #include "base/time/time.h" |
| 24 #include "chrome/browser/search_engines/search_host_to_urls_map.h" |
| 25 #include "chrome/browser/search_engines/template_url_service_client.h" |
| 26 #include "chrome/browser/search_engines/template_url_service_observer.h" |
| 27 #include "chrome/browser/search_engines/util.h" |
| 28 #include "components/rappor/rappor_service.h" |
| 29 #include "components/search_engines/search_engines_pref_names.h" |
| 30 #include "components/search_engines/search_terms_data.h" |
| 31 #include "components/search_engines/template_url.h" |
| 32 #include "components/search_engines/template_url_prepopulate_data.h" |
| 33 #include "components/url_fixer/url_fixer.h" |
| 34 #include "net/base/net_util.h" |
| 35 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| 36 #include "sync/api/sync_change.h" |
| 37 #include "sync/api/sync_error_factory.h" |
| 38 #include "sync/protocol/search_engine_specifics.pb.h" |
| 39 #include "sync/protocol/sync.pb.h" |
| 40 #include "url/gurl.h" |
| 41 |
| 42 typedef SearchHostToURLsMap::TemplateURLSet TemplateURLSet; |
| 43 typedef TemplateURLService::SyncDataMap SyncDataMap; |
| 44 |
| 45 namespace { |
| 46 |
| 47 bool IdenticalSyncGUIDs(const TemplateURLData* data, const TemplateURL* turl) { |
| 48 if (!data || !turl) |
| 49 return !data && !turl; |
| 50 |
| 51 return data->sync_guid == turl->sync_guid(); |
| 52 } |
| 53 |
| 54 const char kDeleteSyncedEngineHistogramName[] = |
| 55 "Search.DeleteSyncedSearchEngine"; |
| 56 |
| 57 // Values for an enumerated histogram used to track whenever an ACTION_DELETE is |
| 58 // sent to the server for search engines. |
| 59 enum DeleteSyncedSearchEngineEvent { |
| 60 DELETE_ENGINE_USER_ACTION, |
| 61 DELETE_ENGINE_PRE_SYNC, |
| 62 DELETE_ENGINE_EMPTY_FIELD, |
| 63 DELETE_ENGINE_MAX, |
| 64 }; |
| 65 |
| 66 // Returns true iff the change in |change_list| at index |i| should not be sent |
| 67 // up to the server based on its GUIDs presence in |sync_data| or when compared |
| 68 // to changes after it in |change_list|. |
| 69 // The criteria is: |
| 70 // 1) It is an ACTION_UPDATE or ACTION_DELETE and the sync_guid associated |
| 71 // with it is NOT found in |sync_data|. We can only update and remove |
| 72 // entries that were originally from the Sync server. |
| 73 // 2) It is an ACTION_ADD and the sync_guid associated with it is found in |
| 74 // |sync_data|. We cannot re-add entries that Sync already knew about. |
| 75 // 3) There is an update after an update for the same GUID. We prune earlier |
| 76 // ones just to save bandwidth (Sync would normally coalesce them). |
| 77 bool ShouldRemoveSyncChange(size_t index, |
| 78 syncer::SyncChangeList* change_list, |
| 79 const SyncDataMap* sync_data) { |
| 80 DCHECK(index < change_list->size()); |
| 81 const syncer::SyncChange& change_i = (*change_list)[index]; |
| 82 const std::string guid = change_i.sync_data().GetSpecifics() |
| 83 .search_engine().sync_guid(); |
| 84 syncer::SyncChange::SyncChangeType type = change_i.change_type(); |
| 85 if ((type == syncer::SyncChange::ACTION_UPDATE || |
| 86 type == syncer::SyncChange::ACTION_DELETE) && |
| 87 sync_data->find(guid) == sync_data->end()) |
| 88 return true; |
| 89 if (type == syncer::SyncChange::ACTION_ADD && |
| 90 sync_data->find(guid) != sync_data->end()) |
| 91 return true; |
| 92 if (type == syncer::SyncChange::ACTION_UPDATE) { |
| 93 for (size_t j = index + 1; j < change_list->size(); j++) { |
| 94 const syncer::SyncChange& change_j = (*change_list)[j]; |
| 95 if ((syncer::SyncChange::ACTION_UPDATE == change_j.change_type()) && |
| 96 (change_j.sync_data().GetSpecifics().search_engine().sync_guid() == |
| 97 guid)) |
| 98 return true; |
| 99 } |
| 100 } |
| 101 return false; |
| 102 } |
| 103 |
| 104 // Remove SyncChanges that should not be sent to the server from |change_list|. |
| 105 // This is done to eliminate incorrect SyncChanges added by the merge and |
| 106 // conflict resolution logic when it is unsure of whether or not an entry is new |
| 107 // from Sync or originally from the local model. This also removes changes that |
| 108 // would be otherwise be coalesced by Sync in order to save bandwidth. |
| 109 void PruneSyncChanges(const SyncDataMap* sync_data, |
| 110 syncer::SyncChangeList* change_list) { |
| 111 for (size_t i = 0; i < change_list->size(); ) { |
| 112 if (ShouldRemoveSyncChange(i, change_list, sync_data)) |
| 113 change_list->erase(change_list->begin() + i); |
| 114 else |
| 115 ++i; |
| 116 } |
| 117 } |
| 118 |
| 119 // Returns true if |turl|'s GUID is not found inside |sync_data|. This is to be |
| 120 // used in MergeDataAndStartSyncing to differentiate between TemplateURLs from |
| 121 // Sync and TemplateURLs that were initially local, assuming |sync_data| is the |
| 122 // |initial_sync_data| parameter. |
| 123 bool IsFromSync(const TemplateURL* turl, const SyncDataMap& sync_data) { |
| 124 return !!sync_data.count(turl->sync_guid()); |
| 125 } |
| 126 |
| 127 // Log the number of instances of a keyword that exist, with zero or more |
| 128 // underscores, which could occur as the result of conflict resolution. |
| 129 void LogDuplicatesHistogram( |
| 130 const TemplateURLService::TemplateURLVector& template_urls) { |
| 131 std::map<std::string, int> duplicates; |
| 132 for (TemplateURLService::TemplateURLVector::const_iterator it = |
| 133 template_urls.begin(); it != template_urls.end(); ++it) { |
| 134 std::string keyword = base::UTF16ToASCII((*it)->keyword()); |
| 135 base::TrimString(keyword, "_", &keyword); |
| 136 duplicates[keyword]++; |
| 137 } |
| 138 |
| 139 // Count the keywords with duplicates. |
| 140 int num_dupes = 0; |
| 141 for (std::map<std::string, int>::const_iterator it = duplicates.begin(); |
| 142 it != duplicates.end(); ++it) { |
| 143 if (it->second > 1) |
| 144 num_dupes++; |
| 145 } |
| 146 |
| 147 UMA_HISTOGRAM_COUNTS_100("Search.SearchEngineDuplicateCounts", num_dupes); |
| 148 } |
| 149 |
| 150 } // namespace |
| 151 |
| 152 |
| 153 // TemplateURLService::LessWithPrefix ----------------------------------------- |
| 154 |
| 155 class TemplateURLService::LessWithPrefix { |
| 156 public: |
| 157 // We want to find the set of keywords that begin with a prefix. The STL |
| 158 // algorithms will return the set of elements that are "equal to" the |
| 159 // prefix, where "equal(x, y)" means "!(cmp(x, y) || cmp(y, x))". When |
| 160 // cmp() is the typical std::less<>, this results in lexicographic equality; |
| 161 // we need to extend this to mark a prefix as "not less than" a keyword it |
| 162 // begins, which will cause the desired elements to be considered "equal to" |
| 163 // the prefix. Note: this is still a strict weak ordering, as required by |
| 164 // equal_range() (though I will not prove that here). |
| 165 // |
| 166 // Unfortunately the calling convention is not "prefix and element" but |
| 167 // rather "two elements", so we pass the prefix as a fake "element" which has |
| 168 // a NULL KeywordDataElement pointer. |
| 169 bool operator()(const KeywordToTemplateMap::value_type& elem1, |
| 170 const KeywordToTemplateMap::value_type& elem2) const { |
| 171 return (elem1.second == NULL) ? |
| 172 (elem2.first.compare(0, elem1.first.length(), elem1.first) > 0) : |
| 173 (elem1.first < elem2.first); |
| 174 } |
| 175 }; |
| 176 |
| 177 |
| 178 // TemplateURLService --------------------------------------------------------- |
| 179 |
| 180 TemplateURLService::TemplateURLService( |
| 181 PrefService* prefs, |
| 182 scoped_ptr<SearchTermsData> search_terms_data, |
| 183 KeywordWebDataService* web_data_service, |
| 184 scoped_ptr<TemplateURLServiceClient> client, |
| 185 GoogleURLTracker* google_url_tracker, |
| 186 rappor::RapporService* rappor_service, |
| 187 const base::Closure& dsp_change_callback) |
| 188 : prefs_(prefs), |
| 189 search_terms_data_(search_terms_data.Pass()), |
| 190 web_data_service_(web_data_service), |
| 191 client_(client.Pass()), |
| 192 google_url_tracker_(google_url_tracker), |
| 193 rappor_service_(rappor_service), |
| 194 dsp_change_callback_(dsp_change_callback), |
| 195 provider_map_(new SearchHostToURLsMap), |
| 196 loaded_(false), |
| 197 load_failed_(false), |
| 198 load_handle_(0), |
| 199 default_search_provider_(NULL), |
| 200 next_id_(kInvalidTemplateURLID + 1), |
| 201 time_provider_(&base::Time::Now), |
| 202 models_associated_(false), |
| 203 processing_syncer_changes_(false), |
| 204 dsp_change_origin_(DSP_CHANGE_OTHER), |
| 205 default_search_manager_( |
| 206 prefs_, |
| 207 base::Bind(&TemplateURLService::OnDefaultSearchChange, |
| 208 base::Unretained(this))) { |
| 209 DCHECK(search_terms_data_); |
| 210 Init(NULL, 0); |
| 211 } |
| 212 |
| 213 TemplateURLService::TemplateURLService(const Initializer* initializers, |
| 214 const int count) |
| 215 : prefs_(NULL), |
| 216 search_terms_data_(new SearchTermsData), |
| 217 web_data_service_(NULL), |
| 218 google_url_tracker_(NULL), |
| 219 rappor_service_(NULL), |
| 220 provider_map_(new SearchHostToURLsMap), |
| 221 loaded_(false), |
| 222 load_failed_(false), |
| 223 load_handle_(0), |
| 224 default_search_provider_(NULL), |
| 225 next_id_(kInvalidTemplateURLID + 1), |
| 226 time_provider_(&base::Time::Now), |
| 227 models_associated_(false), |
| 228 processing_syncer_changes_(false), |
| 229 dsp_change_origin_(DSP_CHANGE_OTHER), |
| 230 default_search_manager_( |
| 231 prefs_, |
| 232 base::Bind(&TemplateURLService::OnDefaultSearchChange, |
| 233 base::Unretained(this))) { |
| 234 Init(initializers, count); |
| 235 } |
| 236 |
| 237 TemplateURLService::~TemplateURLService() { |
| 238 // |web_data_service_| should be deleted during Shutdown(). |
| 239 DCHECK(!web_data_service_); |
| 240 STLDeleteElements(&template_urls_); |
| 241 } |
| 242 |
| 243 // static |
| 244 bool TemplateURLService::LoadDefaultSearchProviderFromPrefs( |
| 245 PrefService* prefs, |
| 246 scoped_ptr<TemplateURLData>* default_provider_data, |
| 247 bool* is_managed) { |
| 248 if (!prefs || !prefs->HasPrefPath(prefs::kDefaultSearchProviderSearchURL) || |
| 249 !prefs->HasPrefPath(prefs::kDefaultSearchProviderKeyword)) |
| 250 return false; |
| 251 |
| 252 const PrefService::Preference* pref = |
| 253 prefs->FindPreference(prefs::kDefaultSearchProviderSearchURL); |
| 254 *is_managed = pref && pref->IsManaged(); |
| 255 |
| 256 if (!prefs->GetBoolean(prefs::kDefaultSearchProviderEnabled)) { |
| 257 // The user doesn't want a default search provider. |
| 258 default_provider_data->reset(NULL); |
| 259 return true; |
| 260 } |
| 261 |
| 262 base::string16 name = |
| 263 base::UTF8ToUTF16(prefs->GetString(prefs::kDefaultSearchProviderName)); |
| 264 base::string16 keyword = |
| 265 base::UTF8ToUTF16(prefs->GetString(prefs::kDefaultSearchProviderKeyword)); |
| 266 if (keyword.empty()) |
| 267 return false; |
| 268 std::string search_url = |
| 269 prefs->GetString(prefs::kDefaultSearchProviderSearchURL); |
| 270 // Force URL to be non-empty. We've never supported this case, but past bugs |
| 271 // might have resulted in it slipping through; eventually this code can be |
| 272 // replaced with a DCHECK(!search_url.empty());. |
| 273 if (search_url.empty()) |
| 274 return false; |
| 275 std::string suggest_url = |
| 276 prefs->GetString(prefs::kDefaultSearchProviderSuggestURL); |
| 277 std::string instant_url = |
| 278 prefs->GetString(prefs::kDefaultSearchProviderInstantURL); |
| 279 std::string image_url = |
| 280 prefs->GetString(prefs::kDefaultSearchProviderImageURL); |
| 281 std::string new_tab_url = |
| 282 prefs->GetString(prefs::kDefaultSearchProviderNewTabURL); |
| 283 std::string search_url_post_params = |
| 284 prefs->GetString(prefs::kDefaultSearchProviderSearchURLPostParams); |
| 285 std::string suggest_url_post_params = |
| 286 prefs->GetString(prefs::kDefaultSearchProviderSuggestURLPostParams); |
| 287 std::string instant_url_post_params = |
| 288 prefs->GetString(prefs::kDefaultSearchProviderInstantURLPostParams); |
| 289 std::string image_url_post_params = |
| 290 prefs->GetString(prefs::kDefaultSearchProviderImageURLPostParams); |
| 291 std::string icon_url = |
| 292 prefs->GetString(prefs::kDefaultSearchProviderIconURL); |
| 293 std::string encodings = |
| 294 prefs->GetString(prefs::kDefaultSearchProviderEncodings); |
| 295 std::string id_string = prefs->GetString(prefs::kDefaultSearchProviderID); |
| 296 std::string prepopulate_id = |
| 297 prefs->GetString(prefs::kDefaultSearchProviderPrepopulateID); |
| 298 const base::ListValue* alternate_urls = |
| 299 prefs->GetList(prefs::kDefaultSearchProviderAlternateURLs); |
| 300 std::string search_terms_replacement_key = prefs->GetString( |
| 301 prefs::kDefaultSearchProviderSearchTermsReplacementKey); |
| 302 |
| 303 default_provider_data->reset(new TemplateURLData); |
| 304 (*default_provider_data)->short_name = name; |
| 305 (*default_provider_data)->SetKeyword(keyword); |
| 306 (*default_provider_data)->SetURL(search_url); |
| 307 (*default_provider_data)->suggestions_url = suggest_url; |
| 308 (*default_provider_data)->instant_url = instant_url; |
| 309 (*default_provider_data)->image_url = image_url; |
| 310 (*default_provider_data)->new_tab_url = new_tab_url; |
| 311 (*default_provider_data)->search_url_post_params = search_url_post_params; |
| 312 (*default_provider_data)->suggestions_url_post_params = |
| 313 suggest_url_post_params; |
| 314 (*default_provider_data)->instant_url_post_params = instant_url_post_params; |
| 315 (*default_provider_data)->image_url_post_params = image_url_post_params; |
| 316 (*default_provider_data)->favicon_url = GURL(icon_url); |
| 317 (*default_provider_data)->show_in_default_list = true; |
| 318 (*default_provider_data)->alternate_urls.clear(); |
| 319 for (size_t i = 0; i < alternate_urls->GetSize(); ++i) { |
| 320 std::string alternate_url; |
| 321 if (alternate_urls->GetString(i, &alternate_url)) |
| 322 (*default_provider_data)->alternate_urls.push_back(alternate_url); |
| 323 } |
| 324 (*default_provider_data)->search_terms_replacement_key = |
| 325 search_terms_replacement_key; |
| 326 base::SplitString(encodings, ';', &(*default_provider_data)->input_encodings); |
| 327 if (!id_string.empty() && !*is_managed) { |
| 328 int64 value; |
| 329 base::StringToInt64(id_string, &value); |
| 330 (*default_provider_data)->id = value; |
| 331 } |
| 332 if (!prepopulate_id.empty() && !*is_managed) { |
| 333 int value; |
| 334 base::StringToInt(prepopulate_id, &value); |
| 335 (*default_provider_data)->prepopulate_id = value; |
| 336 } |
| 337 return true; |
| 338 } |
| 339 |
| 340 // static |
| 341 base::string16 TemplateURLService::CleanUserInputKeyword( |
| 342 const base::string16& keyword) { |
| 343 // Remove the scheme. |
| 344 base::string16 result(base::i18n::ToLower(keyword)); |
| 345 base::TrimWhitespace(result, base::TRIM_ALL, &result); |
| 346 url::Component scheme_component; |
| 347 if (url::ExtractScheme(base::UTF16ToUTF8(keyword).c_str(), |
| 348 static_cast<int>(keyword.length()), |
| 349 &scheme_component)) { |
| 350 // If the scheme isn't "http" or "https", bail. The user isn't trying to |
| 351 // type a web address, but rather an FTP, file:, or other scheme URL, or a |
| 352 // search query with some sort of initial operator (e.g. "site:"). |
| 353 if (result.compare(0, scheme_component.end(), |
| 354 base::ASCIIToUTF16(url::kHttpScheme)) && |
| 355 result.compare(0, scheme_component.end(), |
| 356 base::ASCIIToUTF16(url::kHttpsScheme))) |
| 357 return base::string16(); |
| 358 |
| 359 // Include trailing ':'. |
| 360 result.erase(0, scheme_component.end() + 1); |
| 361 // Many schemes usually have "//" after them, so strip it too. |
| 362 const base::string16 after_scheme(base::ASCIIToUTF16("//")); |
| 363 if (result.compare(0, after_scheme.length(), after_scheme) == 0) |
| 364 result.erase(0, after_scheme.length()); |
| 365 } |
| 366 |
| 367 // Remove leading "www.". |
| 368 result = net::StripWWW(result); |
| 369 |
| 370 // Remove trailing "/". |
| 371 return (result.length() > 0 && result[result.length() - 1] == '/') ? |
| 372 result.substr(0, result.length() - 1) : result; |
| 373 } |
| 374 |
| 375 // static |
| 376 void TemplateURLService::SaveDefaultSearchProviderToPrefs( |
| 377 const TemplateURL* t_url, |
| 378 PrefService* prefs) { |
| 379 if (!prefs) |
| 380 return; |
| 381 |
| 382 bool enabled = false; |
| 383 std::string search_url; |
| 384 std::string suggest_url; |
| 385 std::string instant_url; |
| 386 std::string image_url; |
| 387 std::string new_tab_url; |
| 388 std::string search_url_post_params; |
| 389 std::string suggest_url_post_params; |
| 390 std::string instant_url_post_params; |
| 391 std::string image_url_post_params; |
| 392 std::string icon_url; |
| 393 std::string encodings; |
| 394 std::string short_name; |
| 395 std::string keyword; |
| 396 std::string id_string; |
| 397 std::string prepopulate_id; |
| 398 base::ListValue alternate_urls; |
| 399 std::string search_terms_replacement_key; |
| 400 if (t_url) { |
| 401 DCHECK_EQ(TemplateURL::NORMAL, t_url->GetType()); |
| 402 enabled = true; |
| 403 search_url = t_url->url(); |
| 404 suggest_url = t_url->suggestions_url(); |
| 405 instant_url = t_url->instant_url(); |
| 406 image_url = t_url->image_url(); |
| 407 new_tab_url = t_url->new_tab_url(); |
| 408 search_url_post_params = t_url->search_url_post_params(); |
| 409 suggest_url_post_params = t_url->suggestions_url_post_params(); |
| 410 instant_url_post_params = t_url->instant_url_post_params(); |
| 411 image_url_post_params = t_url->image_url_post_params(); |
| 412 GURL icon_gurl = t_url->favicon_url(); |
| 413 if (!icon_gurl.is_empty()) |
| 414 icon_url = icon_gurl.spec(); |
| 415 encodings = JoinString(t_url->input_encodings(), ';'); |
| 416 short_name = base::UTF16ToUTF8(t_url->short_name()); |
| 417 keyword = base::UTF16ToUTF8(t_url->keyword()); |
| 418 id_string = base::Int64ToString(t_url->id()); |
| 419 prepopulate_id = base::Int64ToString(t_url->prepopulate_id()); |
| 420 for (size_t i = 0; i < t_url->alternate_urls().size(); ++i) |
| 421 alternate_urls.AppendString(t_url->alternate_urls()[i]); |
| 422 search_terms_replacement_key = t_url->search_terms_replacement_key(); |
| 423 } |
| 424 prefs->SetBoolean(prefs::kDefaultSearchProviderEnabled, enabled); |
| 425 prefs->SetString(prefs::kDefaultSearchProviderSearchURL, search_url); |
| 426 prefs->SetString(prefs::kDefaultSearchProviderSuggestURL, suggest_url); |
| 427 prefs->SetString(prefs::kDefaultSearchProviderInstantURL, instant_url); |
| 428 prefs->SetString(prefs::kDefaultSearchProviderImageURL, image_url); |
| 429 prefs->SetString(prefs::kDefaultSearchProviderNewTabURL, new_tab_url); |
| 430 prefs->SetString(prefs::kDefaultSearchProviderSearchURLPostParams, |
| 431 search_url_post_params); |
| 432 prefs->SetString(prefs::kDefaultSearchProviderSuggestURLPostParams, |
| 433 suggest_url_post_params); |
| 434 prefs->SetString(prefs::kDefaultSearchProviderInstantURLPostParams, |
| 435 instant_url_post_params); |
| 436 prefs->SetString(prefs::kDefaultSearchProviderImageURLPostParams, |
| 437 image_url_post_params); |
| 438 prefs->SetString(prefs::kDefaultSearchProviderIconURL, icon_url); |
| 439 prefs->SetString(prefs::kDefaultSearchProviderEncodings, encodings); |
| 440 prefs->SetString(prefs::kDefaultSearchProviderName, short_name); |
| 441 prefs->SetString(prefs::kDefaultSearchProviderKeyword, keyword); |
| 442 prefs->SetString(prefs::kDefaultSearchProviderID, id_string); |
| 443 prefs->SetString(prefs::kDefaultSearchProviderPrepopulateID, prepopulate_id); |
| 444 prefs->Set(prefs::kDefaultSearchProviderAlternateURLs, alternate_urls); |
| 445 prefs->SetString(prefs::kDefaultSearchProviderSearchTermsReplacementKey, |
| 446 search_terms_replacement_key); |
| 447 } |
| 448 |
| 449 bool TemplateURLService::CanReplaceKeyword( |
| 450 const base::string16& keyword, |
| 451 const GURL& url, |
| 452 TemplateURL** template_url_to_replace) { |
| 453 DCHECK(!keyword.empty()); // This should only be called for non-empty |
| 454 // keywords. If we need to support empty kewords |
| 455 // the code needs to change slightly. |
| 456 TemplateURL* existing_url = GetTemplateURLForKeyword(keyword); |
| 457 if (template_url_to_replace) |
| 458 *template_url_to_replace = existing_url; |
| 459 if (existing_url) { |
| 460 // We already have a TemplateURL for this keyword. Only allow it to be |
| 461 // replaced if the TemplateURL can be replaced. |
| 462 return CanReplace(existing_url); |
| 463 } |
| 464 |
| 465 // We don't have a TemplateURL with keyword. Only allow a new one if there |
| 466 // isn't a TemplateURL for the specified host, or there is one but it can |
| 467 // be replaced. We do this to ensure that if the user assigns a different |
| 468 // keyword to a generated TemplateURL, we won't regenerate another keyword for |
| 469 // the same host. |
| 470 return !url.is_valid() || url.host().empty() || |
| 471 CanReplaceKeywordForHost(url.host(), template_url_to_replace); |
| 472 } |
| 473 |
| 474 void TemplateURLService::FindMatchingKeywords( |
| 475 const base::string16& prefix, |
| 476 bool support_replacement_only, |
| 477 TemplateURLVector* matches) { |
| 478 // Sanity check args. |
| 479 if (prefix.empty()) |
| 480 return; |
| 481 DCHECK(matches != NULL); |
| 482 DCHECK(matches->empty()); // The code for exact matches assumes this. |
| 483 |
| 484 // Required for VS2010: http://connect.microsoft.com/VisualStudio/feedback/det
ails/520043/error-converting-from-null-to-a-pointer-type-in-std-pair |
| 485 TemplateURL* const kNullTemplateURL = NULL; |
| 486 |
| 487 // Find matching keyword range. Searches the element map for keywords |
| 488 // beginning with |prefix| and stores the endpoints of the resulting set in |
| 489 // |match_range|. |
| 490 const std::pair<KeywordToTemplateMap::const_iterator, |
| 491 KeywordToTemplateMap::const_iterator> match_range( |
| 492 std::equal_range( |
| 493 keyword_to_template_map_.begin(), keyword_to_template_map_.end(), |
| 494 KeywordToTemplateMap::value_type(prefix, kNullTemplateURL), |
| 495 LessWithPrefix())); |
| 496 |
| 497 // Return vector of matching keywords. |
| 498 for (KeywordToTemplateMap::const_iterator i(match_range.first); |
| 499 i != match_range.second; ++i) { |
| 500 if (!support_replacement_only || |
| 501 i->second->url_ref().SupportsReplacement(search_terms_data())) |
| 502 matches->push_back(i->second); |
| 503 } |
| 504 } |
| 505 |
| 506 TemplateURL* TemplateURLService::GetTemplateURLForKeyword( |
| 507 const base::string16& keyword) { |
| 508 KeywordToTemplateMap::const_iterator elem( |
| 509 keyword_to_template_map_.find(keyword)); |
| 510 if (elem != keyword_to_template_map_.end()) |
| 511 return elem->second; |
| 512 return (!loaded_ && |
| 513 initial_default_search_provider_.get() && |
| 514 (initial_default_search_provider_->keyword() == keyword)) ? |
| 515 initial_default_search_provider_.get() : NULL; |
| 516 } |
| 517 |
| 518 TemplateURL* TemplateURLService::GetTemplateURLForGUID( |
| 519 const std::string& sync_guid) { |
| 520 GUIDToTemplateMap::const_iterator elem(guid_to_template_map_.find(sync_guid)); |
| 521 if (elem != guid_to_template_map_.end()) |
| 522 return elem->second; |
| 523 return (!loaded_ && |
| 524 initial_default_search_provider_.get() && |
| 525 (initial_default_search_provider_->sync_guid() == sync_guid)) ? |
| 526 initial_default_search_provider_.get() : NULL; |
| 527 } |
| 528 |
| 529 TemplateURL* TemplateURLService::GetTemplateURLForHost( |
| 530 const std::string& host) { |
| 531 if (loaded_) |
| 532 return provider_map_->GetTemplateURLForHost(host); |
| 533 TemplateURL* initial_dsp = initial_default_search_provider_.get(); |
| 534 if (!initial_dsp) |
| 535 return NULL; |
| 536 return (initial_dsp->GenerateSearchURL(search_terms_data()).host() == host) ? |
| 537 initial_dsp : NULL; |
| 538 } |
| 539 |
| 540 bool TemplateURLService::Add(TemplateURL* template_url) { |
| 541 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); |
| 542 if (!AddNoNotify(template_url, true)) |
| 543 return false; |
| 544 NotifyObservers(); |
| 545 return true; |
| 546 } |
| 547 |
| 548 void TemplateURLService::AddWithOverrides(TemplateURL* template_url, |
| 549 const base::string16& short_name, |
| 550 const base::string16& keyword, |
| 551 const std::string& url) { |
| 552 DCHECK(!keyword.empty()); |
| 553 DCHECK(!url.empty()); |
| 554 template_url->data_.short_name = short_name; |
| 555 template_url->data_.SetKeyword(keyword); |
| 556 template_url->SetURL(url); |
| 557 Add(template_url); |
| 558 } |
| 559 |
| 560 void TemplateURLService::AddExtensionControlledTURL( |
| 561 TemplateURL* template_url, |
| 562 scoped_ptr<TemplateURL::AssociatedExtensionInfo> info) { |
| 563 DCHECK(loaded_); |
| 564 DCHECK(template_url); |
| 565 DCHECK_EQ(kInvalidTemplateURLID, template_url->id()); |
| 566 DCHECK(info); |
| 567 DCHECK_NE(TemplateURL::NORMAL, info->type); |
| 568 DCHECK_EQ(info->wants_to_be_default_engine, |
| 569 template_url->show_in_default_list()); |
| 570 DCHECK(!FindTemplateURLForExtension(info->extension_id, info->type)); |
| 571 template_url->extension_info_.swap(info); |
| 572 |
| 573 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); |
| 574 if (AddNoNotify(template_url, true)) { |
| 575 if (template_url->extension_info_->wants_to_be_default_engine) |
| 576 UpdateExtensionDefaultSearchEngine(); |
| 577 NotifyObservers(); |
| 578 } |
| 579 } |
| 580 |
| 581 void TemplateURLService::Remove(TemplateURL* template_url) { |
| 582 RemoveNoNotify(template_url); |
| 583 NotifyObservers(); |
| 584 } |
| 585 |
| 586 void TemplateURLService::RemoveExtensionControlledTURL( |
| 587 const std::string& extension_id, |
| 588 TemplateURL::Type type) { |
| 589 DCHECK(loaded_); |
| 590 TemplateURL* url = FindTemplateURLForExtension(extension_id, type); |
| 591 if (!url) |
| 592 return; |
| 593 // NULL this out so that we can call RemoveNoNotify. |
| 594 // UpdateExtensionDefaultSearchEngine will cause it to be reset. |
| 595 if (default_search_provider_ == url) |
| 596 default_search_provider_ = NULL; |
| 597 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); |
| 598 RemoveNoNotify(url); |
| 599 UpdateExtensionDefaultSearchEngine(); |
| 600 NotifyObservers(); |
| 601 } |
| 602 |
| 603 void TemplateURLService::RemoveAutoGeneratedSince(base::Time created_after) { |
| 604 RemoveAutoGeneratedBetween(created_after, base::Time()); |
| 605 } |
| 606 |
| 607 void TemplateURLService::RemoveAutoGeneratedBetween(base::Time created_after, |
| 608 base::Time created_before) { |
| 609 RemoveAutoGeneratedForOriginBetween(GURL(), created_after, created_before); |
| 610 } |
| 611 |
| 612 void TemplateURLService::RemoveAutoGeneratedForOriginBetween( |
| 613 const GURL& origin, |
| 614 base::Time created_after, |
| 615 base::Time created_before) { |
| 616 GURL o(origin.GetOrigin()); |
| 617 bool should_notify = false; |
| 618 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); |
| 619 for (size_t i = 0; i < template_urls_.size();) { |
| 620 if (template_urls_[i]->date_created() >= created_after && |
| 621 (created_before.is_null() || |
| 622 template_urls_[i]->date_created() < created_before) && |
| 623 CanReplace(template_urls_[i]) && |
| 624 (o.is_empty() || |
| 625 template_urls_[i]->GenerateSearchURL( |
| 626 search_terms_data()).GetOrigin() == o)) { |
| 627 RemoveNoNotify(template_urls_[i]); |
| 628 should_notify = true; |
| 629 } else { |
| 630 ++i; |
| 631 } |
| 632 } |
| 633 if (should_notify) |
| 634 NotifyObservers(); |
| 635 } |
| 636 |
| 637 void TemplateURLService::RegisterOmniboxKeyword( |
| 638 const std::string& extension_id, |
| 639 const std::string& extension_name, |
| 640 const std::string& keyword, |
| 641 const std::string& template_url_string) { |
| 642 DCHECK(loaded_); |
| 643 |
| 644 if (FindTemplateURLForExtension(extension_id, |
| 645 TemplateURL::OMNIBOX_API_EXTENSION)) |
| 646 return; |
| 647 |
| 648 TemplateURLData data; |
| 649 data.short_name = base::UTF8ToUTF16(extension_name); |
| 650 data.SetKeyword(base::UTF8ToUTF16(keyword)); |
| 651 data.SetURL(template_url_string); |
| 652 TemplateURL* url = new TemplateURL(data); |
| 653 scoped_ptr<TemplateURL::AssociatedExtensionInfo> info( |
| 654 new TemplateURL::AssociatedExtensionInfo( |
| 655 TemplateURL::OMNIBOX_API_EXTENSION, extension_id)); |
| 656 AddExtensionControlledTURL(url, info.Pass()); |
| 657 } |
| 658 |
| 659 TemplateURLService::TemplateURLVector TemplateURLService::GetTemplateURLs() { |
| 660 return template_urls_; |
| 661 } |
| 662 |
| 663 void TemplateURLService::IncrementUsageCount(TemplateURL* url) { |
| 664 DCHECK(url); |
| 665 // Extension-controlled search engines are not persisted. |
| 666 if (url->GetType() == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION) |
| 667 return; |
| 668 if (std::find(template_urls_.begin(), template_urls_.end(), url) == |
| 669 template_urls_.end()) |
| 670 return; |
| 671 ++url->data_.usage_count; |
| 672 |
| 673 if (web_data_service_) |
| 674 web_data_service_->UpdateKeyword(url->data()); |
| 675 } |
| 676 |
| 677 void TemplateURLService::ResetTemplateURL(TemplateURL* url, |
| 678 const base::string16& title, |
| 679 const base::string16& keyword, |
| 680 const std::string& search_url) { |
| 681 if (ResetTemplateURLNoNotify(url, title, keyword, search_url)) |
| 682 NotifyObservers(); |
| 683 } |
| 684 |
| 685 bool TemplateURLService::CanMakeDefault(const TemplateURL* url) { |
| 686 return |
| 687 ((default_search_provider_source_ == DefaultSearchManager::FROM_USER) || |
| 688 (default_search_provider_source_ == |
| 689 DefaultSearchManager::FROM_FALLBACK)) && |
| 690 (url != GetDefaultSearchProvider()) && |
| 691 url->url_ref().SupportsReplacement(search_terms_data()) && |
| 692 (url->GetType() == TemplateURL::NORMAL); |
| 693 } |
| 694 |
| 695 void TemplateURLService::SetUserSelectedDefaultSearchProvider( |
| 696 TemplateURL* url) { |
| 697 // Omnibox keywords cannot be made default. Extension-controlled search |
| 698 // engines can be made default only by the extension itself because they |
| 699 // aren't persisted. |
| 700 DCHECK(!url || (url->GetType() == TemplateURL::NORMAL)); |
| 701 if (load_failed_) { |
| 702 // Skip the DefaultSearchManager, which will persist to user preferences. |
| 703 if ((default_search_provider_source_ == DefaultSearchManager::FROM_USER) || |
| 704 (default_search_provider_source_ == |
| 705 DefaultSearchManager::FROM_FALLBACK)) { |
| 706 ApplyDefaultSearchChange(url ? &url->data() : NULL, |
| 707 DefaultSearchManager::FROM_USER); |
| 708 } |
| 709 } else { |
| 710 // We rely on the DefaultSearchManager to call OnDefaultSearchChange if, in |
| 711 // fact, the effective DSE changes. |
| 712 if (url) |
| 713 default_search_manager_.SetUserSelectedDefaultSearchEngine(url->data()); |
| 714 else |
| 715 default_search_manager_.ClearUserSelectedDefaultSearchEngine(); |
| 716 } |
| 717 } |
| 718 |
| 719 TemplateURL* TemplateURLService::GetDefaultSearchProvider() { |
| 720 return loaded_ ? |
| 721 default_search_provider_ : initial_default_search_provider_.get(); |
| 722 } |
| 723 |
| 724 bool TemplateURLService::IsSearchResultsPageFromDefaultSearchProvider( |
| 725 const GURL& url) { |
| 726 TemplateURL* default_provider = GetDefaultSearchProvider(); |
| 727 return default_provider && |
| 728 default_provider->IsSearchURL(url, search_terms_data()); |
| 729 } |
| 730 |
| 731 bool TemplateURLService::IsExtensionControlledDefaultSearch() { |
| 732 return default_search_provider_source_ == |
| 733 DefaultSearchManager::FROM_EXTENSION; |
| 734 } |
| 735 |
| 736 void TemplateURLService::RepairPrepopulatedSearchEngines() { |
| 737 // Can't clean DB if it hasn't been loaded. |
| 738 DCHECK(loaded()); |
| 739 |
| 740 if ((default_search_provider_source_ == DefaultSearchManager::FROM_USER) || |
| 741 (default_search_provider_source_ == |
| 742 DefaultSearchManager::FROM_FALLBACK)) { |
| 743 // Clear |default_search_provider_| in case we want to remove the engine it |
| 744 // points to. This will get reset at the end of the function anyway. |
| 745 default_search_provider_ = NULL; |
| 746 } |
| 747 |
| 748 size_t default_search_provider_index = 0; |
| 749 ScopedVector<TemplateURLData> prepopulated_urls = |
| 750 TemplateURLPrepopulateData::GetPrepopulatedEngines( |
| 751 prefs_, &default_search_provider_index); |
| 752 DCHECK(!prepopulated_urls.empty()); |
| 753 ActionsFromPrepopulateData actions(CreateActionsFromCurrentPrepopulateData( |
| 754 &prepopulated_urls, template_urls_, default_search_provider_)); |
| 755 |
| 756 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); |
| 757 |
| 758 // Remove items. |
| 759 for (std::vector<TemplateURL*>::iterator i = actions.removed_engines.begin(); |
| 760 i < actions.removed_engines.end(); ++i) |
| 761 RemoveNoNotify(*i); |
| 762 |
| 763 // Edit items. |
| 764 for (EditedEngines::iterator i(actions.edited_engines.begin()); |
| 765 i < actions.edited_engines.end(); ++i) { |
| 766 TemplateURL new_values(i->second); |
| 767 UpdateNoNotify(i->first, new_values); |
| 768 } |
| 769 |
| 770 // Add items. |
| 771 for (std::vector<TemplateURLData>::const_iterator i = |
| 772 actions.added_engines.begin(); |
| 773 i < actions.added_engines.end(); |
| 774 ++i) { |
| 775 AddNoNotify(new TemplateURL(*i), true); |
| 776 } |
| 777 |
| 778 base::AutoReset<DefaultSearchChangeOrigin> change_origin( |
| 779 &dsp_change_origin_, DSP_CHANGE_PROFILE_RESET); |
| 780 |
| 781 default_search_manager_.ClearUserSelectedDefaultSearchEngine(); |
| 782 |
| 783 if (!default_search_provider_) { |
| 784 // If the default search provider came from a user pref we would have been |
| 785 // notified of the new (fallback-provided) value in |
| 786 // ClearUserSelectedDefaultSearchEngine() above. Since we are here, the |
| 787 // value was presumably originally a fallback value (which may have been |
| 788 // repaired). |
| 789 DefaultSearchManager::Source source; |
| 790 const TemplateURLData* new_dse = |
| 791 default_search_manager_.GetDefaultSearchEngine(&source); |
| 792 // ApplyDefaultSearchChange will notify observers once it is done. |
| 793 ApplyDefaultSearchChange(new_dse, source); |
| 794 } else { |
| 795 NotifyObservers(); |
| 796 } |
| 797 } |
| 798 |
| 799 void TemplateURLService::AddObserver(TemplateURLServiceObserver* observer) { |
| 800 model_observers_.AddObserver(observer); |
| 801 } |
| 802 |
| 803 void TemplateURLService::RemoveObserver(TemplateURLServiceObserver* observer) { |
| 804 model_observers_.RemoveObserver(observer); |
| 805 } |
| 806 |
| 807 void TemplateURLService::Load() { |
| 808 if (loaded_ || load_handle_) |
| 809 return; |
| 810 |
| 811 if (web_data_service_) |
| 812 load_handle_ = web_data_service_->GetKeywords(this); |
| 813 else |
| 814 ChangeToLoadedState(); |
| 815 } |
| 816 |
| 817 scoped_ptr<TemplateURLService::Subscription> |
| 818 TemplateURLService::RegisterOnLoadedCallback( |
| 819 const base::Closure& callback) { |
| 820 return loaded_ ? |
| 821 scoped_ptr<TemplateURLService::Subscription>() : |
| 822 on_loaded_callbacks_.Add(callback); |
| 823 } |
| 824 |
| 825 void TemplateURLService::OnWebDataServiceRequestDone( |
| 826 KeywordWebDataService::Handle h, |
| 827 const WDTypedResult* result) { |
| 828 // Reset the load_handle so that we don't try and cancel the load in |
| 829 // the destructor. |
| 830 load_handle_ = 0; |
| 831 |
| 832 if (!result) { |
| 833 // Results are null if the database went away or (most likely) wasn't |
| 834 // loaded. |
| 835 load_failed_ = true; |
| 836 web_data_service_ = NULL; |
| 837 ChangeToLoadedState(); |
| 838 return; |
| 839 } |
| 840 |
| 841 TemplateURLVector template_urls; |
| 842 int new_resource_keyword_version = 0; |
| 843 GetSearchProvidersUsingKeywordResult( |
| 844 *result, |
| 845 web_data_service_.get(), |
| 846 prefs_, |
| 847 &template_urls, |
| 848 (default_search_provider_source_ == DefaultSearchManager::FROM_USER) ? |
| 849 initial_default_search_provider_.get() : NULL, |
| 850 search_terms_data(), |
| 851 &new_resource_keyword_version, |
| 852 &pre_sync_deletes_); |
| 853 |
| 854 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); |
| 855 |
| 856 PatchMissingSyncGUIDs(&template_urls); |
| 857 SetTemplateURLs(&template_urls); |
| 858 |
| 859 // This initializes provider_map_ which should be done before |
| 860 // calling UpdateKeywordSearchTermsForURL. |
| 861 // This also calls NotifyObservers. |
| 862 ChangeToLoadedState(); |
| 863 |
| 864 // Index any visits that occurred before we finished loading. |
| 865 for (size_t i = 0; i < visits_to_add_.size(); ++i) |
| 866 UpdateKeywordSearchTermsForURL(visits_to_add_[i]); |
| 867 visits_to_add_.clear(); |
| 868 |
| 869 if (new_resource_keyword_version) |
| 870 web_data_service_->SetBuiltinKeywordVersion(new_resource_keyword_version); |
| 871 |
| 872 if (default_search_provider_) { |
| 873 UMA_HISTOGRAM_ENUMERATION( |
| 874 "Search.DefaultSearchProviderType", |
| 875 TemplateURLPrepopulateData::GetEngineType( |
| 876 *default_search_provider_, search_terms_data()), |
| 877 SEARCH_ENGINE_MAX); |
| 878 |
| 879 if (rappor_service_) { |
| 880 rappor_service_->RecordSample( |
| 881 "Search.DefaultSearchProvider", |
| 882 rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, |
| 883 net::registry_controlled_domains::GetDomainAndRegistry( |
| 884 default_search_provider_->url_ref().GetHost(search_terms_data()), |
| 885 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)); |
| 886 } |
| 887 } |
| 888 } |
| 889 |
| 890 base::string16 TemplateURLService::GetKeywordShortName( |
| 891 const base::string16& keyword, |
| 892 bool* is_omnibox_api_extension_keyword) { |
| 893 const TemplateURL* template_url = GetTemplateURLForKeyword(keyword); |
| 894 |
| 895 // TODO(sky): Once LocationBarView adds a listener to the TemplateURLService |
| 896 // to track changes to the model, this should become a DCHECK. |
| 897 if (template_url) { |
| 898 *is_omnibox_api_extension_keyword = |
| 899 template_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION; |
| 900 return template_url->AdjustedShortNameForLocaleDirection(); |
| 901 } |
| 902 *is_omnibox_api_extension_keyword = false; |
| 903 return base::string16(); |
| 904 } |
| 905 |
| 906 void TemplateURLService::OnHistoryURLVisited(const URLVisitedDetails& details) { |
| 907 if (!loaded_) |
| 908 visits_to_add_.push_back(details); |
| 909 else |
| 910 UpdateKeywordSearchTermsForURL(details); |
| 911 } |
| 912 |
| 913 void TemplateURLService::Shutdown() { |
| 914 // This check has to be done at Shutdown() instead of in the dtor to ensure |
| 915 // that no clients of KeywordWebDataService are holding ptrs to it after the |
| 916 // first phase of the KeyedService Shutdown() process. |
| 917 if (load_handle_) { |
| 918 DCHECK(web_data_service_.get()); |
| 919 web_data_service_->CancelRequest(load_handle_); |
| 920 } |
| 921 web_data_service_ = NULL; |
| 922 } |
| 923 |
| 924 syncer::SyncDataList TemplateURLService::GetAllSyncData( |
| 925 syncer::ModelType type) const { |
| 926 DCHECK_EQ(syncer::SEARCH_ENGINES, type); |
| 927 |
| 928 syncer::SyncDataList current_data; |
| 929 for (TemplateURLVector::const_iterator iter = template_urls_.begin(); |
| 930 iter != template_urls_.end(); ++iter) { |
| 931 // We don't sync keywords managed by policy. |
| 932 if ((*iter)->created_by_policy()) |
| 933 continue; |
| 934 // We don't sync extension-controlled search engines. |
| 935 if ((*iter)->GetType() == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION) |
| 936 continue; |
| 937 current_data.push_back(CreateSyncDataFromTemplateURL(**iter)); |
| 938 } |
| 939 |
| 940 return current_data; |
| 941 } |
| 942 |
| 943 syncer::SyncError TemplateURLService::ProcessSyncChanges( |
| 944 const tracked_objects::Location& from_here, |
| 945 const syncer::SyncChangeList& change_list) { |
| 946 if (!models_associated_) { |
| 947 syncer::SyncError error(FROM_HERE, |
| 948 syncer::SyncError::DATATYPE_ERROR, |
| 949 "Models not yet associated.", |
| 950 syncer::SEARCH_ENGINES); |
| 951 return error; |
| 952 } |
| 953 DCHECK(loaded_); |
| 954 |
| 955 base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true); |
| 956 |
| 957 // We've started syncing, so set our origin member to the base Sync value. |
| 958 // As we move through Sync Code, we may set this to increasingly specific |
| 959 // origins so we can tell what exactly caused a DSP change. |
| 960 base::AutoReset<DefaultSearchChangeOrigin> change_origin(&dsp_change_origin_, |
| 961 DSP_CHANGE_SYNC_UNINTENTIONAL); |
| 962 |
| 963 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); |
| 964 |
| 965 syncer::SyncChangeList new_changes; |
| 966 syncer::SyncError error; |
| 967 for (syncer::SyncChangeList::const_iterator iter = change_list.begin(); |
| 968 iter != change_list.end(); ++iter) { |
| 969 DCHECK_EQ(syncer::SEARCH_ENGINES, iter->sync_data().GetDataType()); |
| 970 |
| 971 std::string guid = |
| 972 iter->sync_data().GetSpecifics().search_engine().sync_guid(); |
| 973 TemplateURL* existing_turl = GetTemplateURLForGUID(guid); |
| 974 scoped_ptr<TemplateURL> turl(CreateTemplateURLFromTemplateURLAndSyncData( |
| 975 prefs_, search_terms_data(), existing_turl, iter->sync_data(), |
| 976 &new_changes)); |
| 977 if (!turl.get()) |
| 978 continue; |
| 979 |
| 980 // Explicitly don't check for conflicts against extension keywords; in this |
| 981 // case the functions which modify the keyword map know how to handle the |
| 982 // conflicts. |
| 983 // TODO(mpcomplete): If we allow editing extension keywords, then those will |
| 984 // need to undergo conflict resolution. |
| 985 TemplateURL* existing_keyword_turl = |
| 986 FindNonExtensionTemplateURLForKeyword(turl->keyword()); |
| 987 if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) { |
| 988 if (!existing_turl) { |
| 989 error = sync_error_factory_->CreateAndUploadError( |
| 990 FROM_HERE, |
| 991 "ProcessSyncChanges failed on ChangeType ACTION_DELETE"); |
| 992 continue; |
| 993 } |
| 994 if (existing_turl == GetDefaultSearchProvider()) { |
| 995 // The only way Sync can attempt to delete the default search provider |
| 996 // is if we had changed the kSyncedDefaultSearchProviderGUID |
| 997 // preference, but perhaps it has not yet been received. To avoid |
| 998 // situations where this has come in erroneously, we will un-delete |
| 999 // the current default search from the Sync data. If the pref really |
| 1000 // does arrive later, then default search will change to the correct |
| 1001 // entry, but we'll have this extra entry sitting around. The result is |
| 1002 // not ideal, but it prevents a far more severe bug where the default is |
| 1003 // unexpectedly swapped to something else. The user can safely delete |
| 1004 // the extra entry again later, if they choose. Most users who do not |
| 1005 // look at the search engines UI will not notice this. |
| 1006 // Note that we append a special character to the end of the keyword in |
| 1007 // an attempt to avoid a ping-poinging situation where receiving clients |
| 1008 // may try to continually delete the resurrected entry. |
| 1009 base::string16 updated_keyword = UniquifyKeyword(*existing_turl, true); |
| 1010 TemplateURLData data(existing_turl->data()); |
| 1011 data.SetKeyword(updated_keyword); |
| 1012 TemplateURL new_turl(data); |
| 1013 if (UpdateNoNotify(existing_turl, new_turl)) |
| 1014 NotifyObservers(); |
| 1015 |
| 1016 syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(new_turl); |
| 1017 new_changes.push_back(syncer::SyncChange(FROM_HERE, |
| 1018 syncer::SyncChange::ACTION_ADD, |
| 1019 sync_data)); |
| 1020 // Ignore the delete attempt. This means we never end up resetting the |
| 1021 // default search provider due to an ACTION_DELETE from sync. |
| 1022 continue; |
| 1023 } |
| 1024 |
| 1025 Remove(existing_turl); |
| 1026 } else if (iter->change_type() == syncer::SyncChange::ACTION_ADD) { |
| 1027 if (existing_turl) { |
| 1028 error = sync_error_factory_->CreateAndUploadError( |
| 1029 FROM_HERE, |
| 1030 "ProcessSyncChanges failed on ChangeType ACTION_ADD"); |
| 1031 continue; |
| 1032 } |
| 1033 const std::string guid = turl->sync_guid(); |
| 1034 if (existing_keyword_turl) { |
| 1035 // Resolve any conflicts so we can safely add the new entry. |
| 1036 ResolveSyncKeywordConflict(turl.get(), existing_keyword_turl, |
| 1037 &new_changes); |
| 1038 } |
| 1039 base::AutoReset<DefaultSearchChangeOrigin> change_origin( |
| 1040 &dsp_change_origin_, DSP_CHANGE_SYNC_ADD); |
| 1041 // Force the local ID to kInvalidTemplateURLID so we can add it. |
| 1042 TemplateURLData data(turl->data()); |
| 1043 data.id = kInvalidTemplateURLID; |
| 1044 TemplateURL* added = new TemplateURL(data); |
| 1045 if (Add(added)) |
| 1046 MaybeUpdateDSEAfterSync(added); |
| 1047 } else if (iter->change_type() == syncer::SyncChange::ACTION_UPDATE) { |
| 1048 if (!existing_turl) { |
| 1049 error = sync_error_factory_->CreateAndUploadError( |
| 1050 FROM_HERE, |
| 1051 "ProcessSyncChanges failed on ChangeType ACTION_UPDATE"); |
| 1052 continue; |
| 1053 } |
| 1054 if (existing_keyword_turl && (existing_keyword_turl != existing_turl)) { |
| 1055 // Resolve any conflicts with other entries so we can safely update the |
| 1056 // keyword. |
| 1057 ResolveSyncKeywordConflict(turl.get(), existing_keyword_turl, |
| 1058 &new_changes); |
| 1059 } |
| 1060 if (UpdateNoNotify(existing_turl, *turl)) { |
| 1061 NotifyObservers(); |
| 1062 MaybeUpdateDSEAfterSync(existing_turl); |
| 1063 } |
| 1064 } else { |
| 1065 // We've unexpectedly received an ACTION_INVALID. |
| 1066 error = sync_error_factory_->CreateAndUploadError( |
| 1067 FROM_HERE, |
| 1068 "ProcessSyncChanges received an ACTION_INVALID"); |
| 1069 } |
| 1070 } |
| 1071 |
| 1072 // If something went wrong, we want to prematurely exit to avoid pushing |
| 1073 // inconsistent data to Sync. We return the last error we received. |
| 1074 if (error.IsSet()) |
| 1075 return error; |
| 1076 |
| 1077 error = sync_processor_->ProcessSyncChanges(from_here, new_changes); |
| 1078 |
| 1079 return error; |
| 1080 } |
| 1081 |
| 1082 syncer::SyncMergeResult TemplateURLService::MergeDataAndStartSyncing( |
| 1083 syncer::ModelType type, |
| 1084 const syncer::SyncDataList& initial_sync_data, |
| 1085 scoped_ptr<syncer::SyncChangeProcessor> sync_processor, |
| 1086 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) { |
| 1087 DCHECK(loaded_); |
| 1088 DCHECK_EQ(type, syncer::SEARCH_ENGINES); |
| 1089 DCHECK(!sync_processor_.get()); |
| 1090 DCHECK(sync_processor.get()); |
| 1091 DCHECK(sync_error_factory.get()); |
| 1092 syncer::SyncMergeResult merge_result(type); |
| 1093 |
| 1094 // Disable sync if we failed to load. |
| 1095 if (load_failed_) { |
| 1096 merge_result.set_error(syncer::SyncError( |
| 1097 FROM_HERE, syncer::SyncError::DATATYPE_ERROR, |
| 1098 "Local database load failed.", syncer::SEARCH_ENGINES)); |
| 1099 return merge_result; |
| 1100 } |
| 1101 |
| 1102 sync_processor_ = sync_processor.Pass(); |
| 1103 sync_error_factory_ = sync_error_factory.Pass(); |
| 1104 |
| 1105 // We do a lot of calls to Add/Remove/ResetTemplateURL here, so ensure we |
| 1106 // don't step on our own toes. |
| 1107 base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true); |
| 1108 |
| 1109 // We've started syncing, so set our origin member to the base Sync value. |
| 1110 // As we move through Sync Code, we may set this to increasingly specific |
| 1111 // origins so we can tell what exactly caused a DSP change. |
| 1112 base::AutoReset<DefaultSearchChangeOrigin> change_origin(&dsp_change_origin_, |
| 1113 DSP_CHANGE_SYNC_UNINTENTIONAL); |
| 1114 |
| 1115 syncer::SyncChangeList new_changes; |
| 1116 |
| 1117 // Build maps of our sync GUIDs to syncer::SyncData. |
| 1118 SyncDataMap local_data_map = CreateGUIDToSyncDataMap( |
| 1119 GetAllSyncData(syncer::SEARCH_ENGINES)); |
| 1120 SyncDataMap sync_data_map = CreateGUIDToSyncDataMap(initial_sync_data); |
| 1121 |
| 1122 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); |
| 1123 |
| 1124 merge_result.set_num_items_before_association(local_data_map.size()); |
| 1125 for (SyncDataMap::const_iterator iter = sync_data_map.begin(); |
| 1126 iter != sync_data_map.end(); ++iter) { |
| 1127 TemplateURL* local_turl = GetTemplateURLForGUID(iter->first); |
| 1128 scoped_ptr<TemplateURL> sync_turl( |
| 1129 CreateTemplateURLFromTemplateURLAndSyncData( |
| 1130 prefs_, search_terms_data(), local_turl, iter->second, |
| 1131 &new_changes)); |
| 1132 if (!sync_turl.get()) |
| 1133 continue; |
| 1134 |
| 1135 if (pre_sync_deletes_.find(sync_turl->sync_guid()) != |
| 1136 pre_sync_deletes_.end()) { |
| 1137 // This entry was deleted before the initial sync began (possibly through |
| 1138 // preprocessing in TemplateURLService's loading code). Ignore it and send |
| 1139 // an ACTION_DELETE up to the server. |
| 1140 new_changes.push_back( |
| 1141 syncer::SyncChange(FROM_HERE, |
| 1142 syncer::SyncChange::ACTION_DELETE, |
| 1143 iter->second)); |
| 1144 UMA_HISTOGRAM_ENUMERATION(kDeleteSyncedEngineHistogramName, |
| 1145 DELETE_ENGINE_PRE_SYNC, DELETE_ENGINE_MAX); |
| 1146 continue; |
| 1147 } |
| 1148 |
| 1149 if (local_turl) { |
| 1150 DCHECK(IsFromSync(local_turl, sync_data_map)); |
| 1151 // This local search engine is already synced. If the timestamp differs |
| 1152 // from Sync, we need to update locally or to the cloud. Note that if the |
| 1153 // timestamps are equal, we touch neither. |
| 1154 if (sync_turl->last_modified() > local_turl->last_modified()) { |
| 1155 // We've received an update from Sync. We should replace all synced |
| 1156 // fields in the local TemplateURL. Note that this includes the |
| 1157 // TemplateURLID and the TemplateURL may have to be reparsed. This |
| 1158 // also makes the local data's last_modified timestamp equal to Sync's, |
| 1159 // avoiding an Update on the next MergeData call. |
| 1160 if (UpdateNoNotify(local_turl, *sync_turl)) |
| 1161 NotifyObservers(); |
| 1162 merge_result.set_num_items_modified( |
| 1163 merge_result.num_items_modified() + 1); |
| 1164 } else if (sync_turl->last_modified() < local_turl->last_modified()) { |
| 1165 // Otherwise, we know we have newer data, so update Sync with our |
| 1166 // data fields. |
| 1167 new_changes.push_back( |
| 1168 syncer::SyncChange(FROM_HERE, |
| 1169 syncer::SyncChange::ACTION_UPDATE, |
| 1170 local_data_map[local_turl->sync_guid()])); |
| 1171 } |
| 1172 local_data_map.erase(iter->first); |
| 1173 } else { |
| 1174 // The search engine from the cloud has not been synced locally. Merge it |
| 1175 // into our local model. This will handle any conflicts with local (and |
| 1176 // already-synced) TemplateURLs. It will prefer to keep entries from Sync |
| 1177 // over not-yet-synced TemplateURLs. |
| 1178 MergeInSyncTemplateURL(sync_turl.get(), sync_data_map, &new_changes, |
| 1179 &local_data_map, &merge_result); |
| 1180 } |
| 1181 } |
| 1182 |
| 1183 // The remaining SyncData in local_data_map should be everything that needs to |
| 1184 // be pushed as ADDs to sync. |
| 1185 for (SyncDataMap::const_iterator iter = local_data_map.begin(); |
| 1186 iter != local_data_map.end(); ++iter) { |
| 1187 new_changes.push_back( |
| 1188 syncer::SyncChange(FROM_HERE, |
| 1189 syncer::SyncChange::ACTION_ADD, |
| 1190 iter->second)); |
| 1191 } |
| 1192 |
| 1193 // Do some post-processing on the change list to ensure that we are sending |
| 1194 // valid changes to sync_processor_. |
| 1195 PruneSyncChanges(&sync_data_map, &new_changes); |
| 1196 |
| 1197 LogDuplicatesHistogram(GetTemplateURLs()); |
| 1198 merge_result.set_num_items_after_association( |
| 1199 GetAllSyncData(syncer::SEARCH_ENGINES).size()); |
| 1200 merge_result.set_error( |
| 1201 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes)); |
| 1202 if (merge_result.error().IsSet()) |
| 1203 return merge_result; |
| 1204 |
| 1205 // The ACTION_DELETEs from this set are processed. Empty it so we don't try to |
| 1206 // reuse them on the next call to MergeDataAndStartSyncing. |
| 1207 pre_sync_deletes_.clear(); |
| 1208 |
| 1209 models_associated_ = true; |
| 1210 return merge_result; |
| 1211 } |
| 1212 |
| 1213 void TemplateURLService::StopSyncing(syncer::ModelType type) { |
| 1214 DCHECK_EQ(type, syncer::SEARCH_ENGINES); |
| 1215 models_associated_ = false; |
| 1216 sync_processor_.reset(); |
| 1217 sync_error_factory_.reset(); |
| 1218 } |
| 1219 |
| 1220 void TemplateURLService::ProcessTemplateURLChange( |
| 1221 const tracked_objects::Location& from_here, |
| 1222 const TemplateURL* turl, |
| 1223 syncer::SyncChange::SyncChangeType type) { |
| 1224 DCHECK_NE(type, syncer::SyncChange::ACTION_INVALID); |
| 1225 DCHECK(turl); |
| 1226 |
| 1227 if (!models_associated_) |
| 1228 return; // Not syncing. |
| 1229 |
| 1230 if (processing_syncer_changes_) |
| 1231 return; // These are changes originating from us. Ignore. |
| 1232 |
| 1233 // Avoid syncing keywords managed by policy. |
| 1234 if (turl->created_by_policy()) |
| 1235 return; |
| 1236 |
| 1237 // Avoid syncing extension-controlled search engines. |
| 1238 if (turl->GetType() == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION) |
| 1239 return; |
| 1240 |
| 1241 syncer::SyncChangeList changes; |
| 1242 |
| 1243 syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*turl); |
| 1244 changes.push_back(syncer::SyncChange(from_here, |
| 1245 type, |
| 1246 sync_data)); |
| 1247 |
| 1248 sync_processor_->ProcessSyncChanges(FROM_HERE, changes); |
| 1249 } |
| 1250 |
| 1251 // static |
| 1252 syncer::SyncData TemplateURLService::CreateSyncDataFromTemplateURL( |
| 1253 const TemplateURL& turl) { |
| 1254 sync_pb::EntitySpecifics specifics; |
| 1255 sync_pb::SearchEngineSpecifics* se_specifics = |
| 1256 specifics.mutable_search_engine(); |
| 1257 se_specifics->set_short_name(base::UTF16ToUTF8(turl.short_name())); |
| 1258 se_specifics->set_keyword(base::UTF16ToUTF8(turl.keyword())); |
| 1259 se_specifics->set_favicon_url(turl.favicon_url().spec()); |
| 1260 se_specifics->set_url(turl.url()); |
| 1261 se_specifics->set_safe_for_autoreplace(turl.safe_for_autoreplace()); |
| 1262 se_specifics->set_originating_url(turl.originating_url().spec()); |
| 1263 se_specifics->set_date_created(turl.date_created().ToInternalValue()); |
| 1264 se_specifics->set_input_encodings(JoinString(turl.input_encodings(), ';')); |
| 1265 se_specifics->set_show_in_default_list(turl.show_in_default_list()); |
| 1266 se_specifics->set_suggestions_url(turl.suggestions_url()); |
| 1267 se_specifics->set_prepopulate_id(turl.prepopulate_id()); |
| 1268 se_specifics->set_instant_url(turl.instant_url()); |
| 1269 if (!turl.image_url().empty()) |
| 1270 se_specifics->set_image_url(turl.image_url()); |
| 1271 se_specifics->set_new_tab_url(turl.new_tab_url()); |
| 1272 if (!turl.search_url_post_params().empty()) |
| 1273 se_specifics->set_search_url_post_params(turl.search_url_post_params()); |
| 1274 if (!turl.suggestions_url_post_params().empty()) { |
| 1275 se_specifics->set_suggestions_url_post_params( |
| 1276 turl.suggestions_url_post_params()); |
| 1277 } |
| 1278 if (!turl.instant_url_post_params().empty()) |
| 1279 se_specifics->set_instant_url_post_params(turl.instant_url_post_params()); |
| 1280 if (!turl.image_url_post_params().empty()) |
| 1281 se_specifics->set_image_url_post_params(turl.image_url_post_params()); |
| 1282 se_specifics->set_last_modified(turl.last_modified().ToInternalValue()); |
| 1283 se_specifics->set_sync_guid(turl.sync_guid()); |
| 1284 for (size_t i = 0; i < turl.alternate_urls().size(); ++i) |
| 1285 se_specifics->add_alternate_urls(turl.alternate_urls()[i]); |
| 1286 se_specifics->set_search_terms_replacement_key( |
| 1287 turl.search_terms_replacement_key()); |
| 1288 |
| 1289 return syncer::SyncData::CreateLocalData(se_specifics->sync_guid(), |
| 1290 se_specifics->keyword(), |
| 1291 specifics); |
| 1292 } |
| 1293 |
| 1294 // static |
| 1295 TemplateURL* TemplateURLService::CreateTemplateURLFromTemplateURLAndSyncData( |
| 1296 PrefService* prefs, |
| 1297 const SearchTermsData& search_terms_data, |
| 1298 TemplateURL* existing_turl, |
| 1299 const syncer::SyncData& sync_data, |
| 1300 syncer::SyncChangeList* change_list) { |
| 1301 DCHECK(change_list); |
| 1302 |
| 1303 sync_pb::SearchEngineSpecifics specifics = |
| 1304 sync_data.GetSpecifics().search_engine(); |
| 1305 |
| 1306 // Past bugs might have caused either of these fields to be empty. Just |
| 1307 // delete this data off the server. |
| 1308 if (specifics.url().empty() || specifics.sync_guid().empty()) { |
| 1309 change_list->push_back( |
| 1310 syncer::SyncChange(FROM_HERE, |
| 1311 syncer::SyncChange::ACTION_DELETE, |
| 1312 sync_data)); |
| 1313 UMA_HISTOGRAM_ENUMERATION(kDeleteSyncedEngineHistogramName, |
| 1314 DELETE_ENGINE_EMPTY_FIELD, DELETE_ENGINE_MAX); |
| 1315 return NULL; |
| 1316 } |
| 1317 |
| 1318 TemplateURLData data(existing_turl ? |
| 1319 existing_turl->data() : TemplateURLData()); |
| 1320 data.short_name = base::UTF8ToUTF16(specifics.short_name()); |
| 1321 data.originating_url = GURL(specifics.originating_url()); |
| 1322 base::string16 keyword(base::UTF8ToUTF16(specifics.keyword())); |
| 1323 // NOTE: Once this code has shipped in a couple of stable releases, we can |
| 1324 // probably remove the migration portion, comment out the |
| 1325 // "autogenerate_keyword" field entirely in the .proto file, and fold the |
| 1326 // empty keyword case into the "delete data" block above. |
| 1327 bool reset_keyword = |
| 1328 specifics.autogenerate_keyword() || specifics.keyword().empty(); |
| 1329 if (reset_keyword) |
| 1330 keyword = base::ASCIIToUTF16("dummy"); // Will be replaced below. |
| 1331 DCHECK(!keyword.empty()); |
| 1332 data.SetKeyword(keyword); |
| 1333 data.SetURL(specifics.url()); |
| 1334 data.suggestions_url = specifics.suggestions_url(); |
| 1335 data.instant_url = specifics.instant_url(); |
| 1336 data.image_url = specifics.image_url(); |
| 1337 data.new_tab_url = specifics.new_tab_url(); |
| 1338 data.search_url_post_params = specifics.search_url_post_params(); |
| 1339 data.suggestions_url_post_params = specifics.suggestions_url_post_params(); |
| 1340 data.instant_url_post_params = specifics.instant_url_post_params(); |
| 1341 data.image_url_post_params = specifics.image_url_post_params(); |
| 1342 data.favicon_url = GURL(specifics.favicon_url()); |
| 1343 data.show_in_default_list = specifics.show_in_default_list(); |
| 1344 data.safe_for_autoreplace = specifics.safe_for_autoreplace(); |
| 1345 base::SplitString(specifics.input_encodings(), ';', &data.input_encodings); |
| 1346 // If the server data has duplicate encodings, we'll want to push an update |
| 1347 // below to correct it. Note that we also fix this in |
| 1348 // GetSearchProvidersUsingKeywordResult(), since otherwise we'd never correct |
| 1349 // local problems for clients which have disabled search engine sync. |
| 1350 bool deduped = DeDupeEncodings(&data.input_encodings); |
| 1351 data.date_created = base::Time::FromInternalValue(specifics.date_created()); |
| 1352 data.last_modified = base::Time::FromInternalValue(specifics.last_modified()); |
| 1353 data.prepopulate_id = specifics.prepopulate_id(); |
| 1354 data.sync_guid = specifics.sync_guid(); |
| 1355 data.alternate_urls.clear(); |
| 1356 for (int i = 0; i < specifics.alternate_urls_size(); ++i) |
| 1357 data.alternate_urls.push_back(specifics.alternate_urls(i)); |
| 1358 data.search_terms_replacement_key = specifics.search_terms_replacement_key(); |
| 1359 |
| 1360 TemplateURL* turl = new TemplateURL(data); |
| 1361 // If this TemplateURL matches a built-in prepopulated template URL, it's |
| 1362 // possible that sync is trying to modify fields that should not be touched. |
| 1363 // Revert these fields to the built-in values. |
| 1364 UpdateTemplateURLIfPrepopulated(turl, prefs); |
| 1365 DCHECK_NE(TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION, turl->GetType()); |
| 1366 if (reset_keyword || deduped) { |
| 1367 if (reset_keyword) |
| 1368 turl->ResetKeywordIfNecessary(search_terms_data, true); |
| 1369 syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*turl); |
| 1370 change_list->push_back(syncer::SyncChange(FROM_HERE, |
| 1371 syncer::SyncChange::ACTION_UPDATE, |
| 1372 sync_data)); |
| 1373 } else if (turl->IsGoogleSearchURLWithReplaceableKeyword(search_terms_data)) { |
| 1374 if (!existing_turl) { |
| 1375 // We're adding a new TemplateURL that uses the Google base URL, so set |
| 1376 // its keyword appropriately for the local environment. |
| 1377 turl->ResetKeywordIfNecessary(search_terms_data, false); |
| 1378 } else if (existing_turl->IsGoogleSearchURLWithReplaceableKeyword( |
| 1379 search_terms_data)) { |
| 1380 // Ignore keyword changes triggered by the Google base URL changing on |
| 1381 // another client. If the base URL changes in this client as well, we'll |
| 1382 // pick that up separately at the appropriate time. Otherwise, changing |
| 1383 // the keyword here could result in having the wrong keyword for the local |
| 1384 // environment. |
| 1385 turl->data_.SetKeyword(existing_turl->keyword()); |
| 1386 } |
| 1387 } |
| 1388 |
| 1389 return turl; |
| 1390 } |
| 1391 |
| 1392 // static |
| 1393 SyncDataMap TemplateURLService::CreateGUIDToSyncDataMap( |
| 1394 const syncer::SyncDataList& sync_data) { |
| 1395 SyncDataMap data_map; |
| 1396 for (syncer::SyncDataList::const_iterator i(sync_data.begin()); |
| 1397 i != sync_data.end(); |
| 1398 ++i) |
| 1399 data_map[i->GetSpecifics().search_engine().sync_guid()] = *i; |
| 1400 return data_map; |
| 1401 } |
| 1402 |
| 1403 void TemplateURLService::SetKeywordSearchTermsForURL( |
| 1404 const TemplateURL* t_url, |
| 1405 const GURL& url, |
| 1406 const base::string16& term) { |
| 1407 if (client_) |
| 1408 client_->SetKeywordSearchTermsForURL(url, t_url->id(), term); |
| 1409 } |
| 1410 |
| 1411 void TemplateURLService::Init(const Initializer* initializers, |
| 1412 int num_initializers) { |
| 1413 if (client_) |
| 1414 client_->SetOwner(this); |
| 1415 |
| 1416 // GoogleURLTracker is not created in tests. |
| 1417 if (google_url_tracker_) { |
| 1418 google_url_updated_subscription_ = |
| 1419 google_url_tracker_->RegisterCallback(base::Bind( |
| 1420 &TemplateURLService::OnGoogleURLUpdated, base::Unretained(this))); |
| 1421 } |
| 1422 |
| 1423 if (prefs_) { |
| 1424 pref_change_registrar_.Init(prefs_); |
| 1425 pref_change_registrar_.Add( |
| 1426 prefs::kSyncedDefaultSearchProviderGUID, |
| 1427 base::Bind( |
| 1428 &TemplateURLService::OnSyncedDefaultSearchProviderGUIDChanged, |
| 1429 base::Unretained(this))); |
| 1430 } |
| 1431 |
| 1432 DefaultSearchManager::Source source = DefaultSearchManager::FROM_USER; |
| 1433 TemplateURLData* dse = |
| 1434 default_search_manager_.GetDefaultSearchEngine(&source); |
| 1435 ApplyDefaultSearchChange(dse, source); |
| 1436 |
| 1437 if (num_initializers > 0) { |
| 1438 // This path is only hit by test code and is used to simulate a loaded |
| 1439 // TemplateURLService. |
| 1440 ChangeToLoadedState(); |
| 1441 |
| 1442 // Add specific initializers, if any. |
| 1443 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); |
| 1444 for (int i(0); i < num_initializers; ++i) { |
| 1445 DCHECK(initializers[i].keyword); |
| 1446 DCHECK(initializers[i].url); |
| 1447 DCHECK(initializers[i].content); |
| 1448 |
| 1449 // TemplateURLService ends up owning the TemplateURL, don't try and free |
| 1450 // it. |
| 1451 TemplateURLData data; |
| 1452 data.short_name = base::UTF8ToUTF16(initializers[i].content); |
| 1453 data.SetKeyword(base::UTF8ToUTF16(initializers[i].keyword)); |
| 1454 data.SetURL(initializers[i].url); |
| 1455 TemplateURL* template_url = new TemplateURL(data); |
| 1456 AddNoNotify(template_url, true); |
| 1457 |
| 1458 // Set the first provided identifier to be the default. |
| 1459 if (i == 0) |
| 1460 default_search_manager_.SetUserSelectedDefaultSearchEngine(data); |
| 1461 } |
| 1462 } |
| 1463 |
| 1464 // Request a server check for the correct Google URL if Google is the |
| 1465 // default search engine. |
| 1466 RequestGoogleURLTrackerServerCheckIfNecessary(); |
| 1467 } |
| 1468 |
| 1469 void TemplateURLService::RemoveFromMaps(TemplateURL* template_url) { |
| 1470 const base::string16& keyword = template_url->keyword(); |
| 1471 DCHECK_NE(0U, keyword_to_template_map_.count(keyword)); |
| 1472 if (keyword_to_template_map_[keyword] == template_url) { |
| 1473 // We need to check whether the keyword can now be provided by another |
| 1474 // TemplateURL. See the comments in AddToMaps() for more information on |
| 1475 // extension keywords and how they can coexist with non-extension keywords. |
| 1476 // In the case of more than one extension, we use the most recently |
| 1477 // installed (which will be the most recently added, which will have the |
| 1478 // highest ID). |
| 1479 TemplateURL* best_fallback = NULL; |
| 1480 for (TemplateURLVector::const_iterator i(template_urls_.begin()); |
| 1481 i != template_urls_.end(); ++i) { |
| 1482 TemplateURL* turl = *i; |
| 1483 // This next statement relies on the fact that there can only be one |
| 1484 // non-Omnibox API TemplateURL with a given keyword. |
| 1485 if ((turl != template_url) && (turl->keyword() == keyword) && |
| 1486 (!best_fallback || |
| 1487 (best_fallback->GetType() != TemplateURL::OMNIBOX_API_EXTENSION) || |
| 1488 ((turl->GetType() == TemplateURL::OMNIBOX_API_EXTENSION) && |
| 1489 (turl->id() > best_fallback->id())))) |
| 1490 best_fallback = turl; |
| 1491 } |
| 1492 if (best_fallback) |
| 1493 keyword_to_template_map_[keyword] = best_fallback; |
| 1494 else |
| 1495 keyword_to_template_map_.erase(keyword); |
| 1496 } |
| 1497 |
| 1498 if (!template_url->sync_guid().empty()) |
| 1499 guid_to_template_map_.erase(template_url->sync_guid()); |
| 1500 // |provider_map_| is only initialized after loading has completed. |
| 1501 if (loaded_) { |
| 1502 provider_map_->Remove(template_url); |
| 1503 } |
| 1504 } |
| 1505 |
| 1506 void TemplateURLService::AddToMaps(TemplateURL* template_url) { |
| 1507 bool template_url_is_omnibox_api = |
| 1508 template_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION; |
| 1509 const base::string16& keyword = template_url->keyword(); |
| 1510 KeywordToTemplateMap::const_iterator i = |
| 1511 keyword_to_template_map_.find(keyword); |
| 1512 if (i == keyword_to_template_map_.end()) { |
| 1513 keyword_to_template_map_[keyword] = template_url; |
| 1514 } else { |
| 1515 const TemplateURL* existing_url = i->second; |
| 1516 // We should only have overlapping keywords when at least one comes from |
| 1517 // an extension. In that case, the ranking order is: |
| 1518 // Manually-modified keywords > extension keywords > replaceable keywords |
| 1519 // When there are multiple extensions, the last-added wins. |
| 1520 bool existing_url_is_omnibox_api = |
| 1521 existing_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION; |
| 1522 DCHECK(existing_url_is_omnibox_api || template_url_is_omnibox_api); |
| 1523 if (existing_url_is_omnibox_api ? |
| 1524 !CanReplace(template_url) : CanReplace(existing_url)) |
| 1525 keyword_to_template_map_[keyword] = template_url; |
| 1526 } |
| 1527 |
| 1528 if (!template_url->sync_guid().empty()) |
| 1529 guid_to_template_map_[template_url->sync_guid()] = template_url; |
| 1530 // |provider_map_| is only initialized after loading has completed. |
| 1531 if (loaded_) |
| 1532 provider_map_->Add(template_url, search_terms_data()); |
| 1533 } |
| 1534 |
| 1535 // Helper for partition() call in next function. |
| 1536 bool HasValidID(TemplateURL* t_url) { |
| 1537 return t_url->id() != kInvalidTemplateURLID; |
| 1538 } |
| 1539 |
| 1540 void TemplateURLService::SetTemplateURLs(TemplateURLVector* urls) { |
| 1541 // Partition the URLs first, instead of implementing the loops below by simply |
| 1542 // scanning the input twice. While it's not supposed to happen normally, it's |
| 1543 // possible for corrupt databases to return multiple entries with the same |
| 1544 // keyword. In this case, the first loop may delete the first entry when |
| 1545 // adding the second. If this happens, the second loop must not attempt to |
| 1546 // access the deleted entry. Partitioning ensures this constraint. |
| 1547 TemplateURLVector::iterator first_invalid( |
| 1548 std::partition(urls->begin(), urls->end(), HasValidID)); |
| 1549 |
| 1550 // First, add the items that already have id's, so that the next_id_ gets |
| 1551 // properly set. |
| 1552 for (TemplateURLVector::const_iterator i = urls->begin(); i != first_invalid; |
| 1553 ++i) { |
| 1554 next_id_ = std::max(next_id_, (*i)->id()); |
| 1555 AddNoNotify(*i, false); |
| 1556 } |
| 1557 |
| 1558 // Next add the new items that don't have id's. |
| 1559 for (TemplateURLVector::const_iterator i = first_invalid; i != urls->end(); |
| 1560 ++i) |
| 1561 AddNoNotify(*i, true); |
| 1562 |
| 1563 // Clear the input vector to reduce the chance callers will try to use a |
| 1564 // (possibly deleted) entry. |
| 1565 urls->clear(); |
| 1566 } |
| 1567 |
| 1568 void TemplateURLService::ChangeToLoadedState() { |
| 1569 DCHECK(!loaded_); |
| 1570 |
| 1571 provider_map_->Init(template_urls_, search_terms_data()); |
| 1572 loaded_ = true; |
| 1573 |
| 1574 // This will cause a call to NotifyObservers(). |
| 1575 ApplyDefaultSearchChangeNoMetrics( |
| 1576 initial_default_search_provider_ ? |
| 1577 &initial_default_search_provider_->data() : NULL, |
| 1578 default_search_provider_source_); |
| 1579 initial_default_search_provider_.reset(); |
| 1580 on_loaded_callbacks_.Notify(); |
| 1581 } |
| 1582 |
| 1583 bool TemplateURLService::CanReplaceKeywordForHost( |
| 1584 const std::string& host, |
| 1585 TemplateURL** to_replace) { |
| 1586 DCHECK(!to_replace || !*to_replace); |
| 1587 const TemplateURLSet* urls = provider_map_->GetURLsForHost(host); |
| 1588 if (!urls) |
| 1589 return true; |
| 1590 for (TemplateURLSet::const_iterator i(urls->begin()); i != urls->end(); ++i) { |
| 1591 if (CanReplace(*i)) { |
| 1592 if (to_replace) |
| 1593 *to_replace = *i; |
| 1594 return true; |
| 1595 } |
| 1596 } |
| 1597 return false; |
| 1598 } |
| 1599 |
| 1600 bool TemplateURLService::CanReplace(const TemplateURL* t_url) { |
| 1601 return (t_url != default_search_provider_ && !t_url->show_in_default_list() && |
| 1602 t_url->safe_for_autoreplace()); |
| 1603 } |
| 1604 |
| 1605 TemplateURL* TemplateURLService::FindNonExtensionTemplateURLForKeyword( |
| 1606 const base::string16& keyword) { |
| 1607 TemplateURL* keyword_turl = GetTemplateURLForKeyword(keyword); |
| 1608 if (!keyword_turl || (keyword_turl->GetType() == TemplateURL::NORMAL)) |
| 1609 return keyword_turl; |
| 1610 // The extension keyword in the model may be hiding a replaceable |
| 1611 // non-extension keyword. Look for it. |
| 1612 for (TemplateURLVector::const_iterator i(template_urls_.begin()); |
| 1613 i != template_urls_.end(); ++i) { |
| 1614 if (((*i)->GetType() == TemplateURL::NORMAL) && |
| 1615 ((*i)->keyword() == keyword)) |
| 1616 return *i; |
| 1617 } |
| 1618 return NULL; |
| 1619 } |
| 1620 |
| 1621 bool TemplateURLService::UpdateNoNotify(TemplateURL* existing_turl, |
| 1622 const TemplateURL& new_values) { |
| 1623 DCHECK(existing_turl); |
| 1624 if (std::find(template_urls_.begin(), template_urls_.end(), existing_turl) == |
| 1625 template_urls_.end()) |
| 1626 return false; |
| 1627 |
| 1628 base::string16 old_keyword(existing_turl->keyword()); |
| 1629 keyword_to_template_map_.erase(old_keyword); |
| 1630 if (!existing_turl->sync_guid().empty()) |
| 1631 guid_to_template_map_.erase(existing_turl->sync_guid()); |
| 1632 |
| 1633 // |provider_map_| is only initialized after loading has completed. |
| 1634 if (loaded_) |
| 1635 provider_map_->Remove(existing_turl); |
| 1636 |
| 1637 TemplateURLID previous_id = existing_turl->id(); |
| 1638 existing_turl->CopyFrom(new_values); |
| 1639 existing_turl->data_.id = previous_id; |
| 1640 |
| 1641 if (loaded_) { |
| 1642 provider_map_->Add(existing_turl, search_terms_data()); |
| 1643 } |
| 1644 |
| 1645 const base::string16& keyword = existing_turl->keyword(); |
| 1646 KeywordToTemplateMap::const_iterator i = |
| 1647 keyword_to_template_map_.find(keyword); |
| 1648 if (i == keyword_to_template_map_.end()) { |
| 1649 keyword_to_template_map_[keyword] = existing_turl; |
| 1650 } else { |
| 1651 // We can theoretically reach here in two cases: |
| 1652 // * There is an existing extension keyword and sync brings in a rename of |
| 1653 // a non-extension keyword to match. In this case we just need to pick |
| 1654 // which keyword has priority to update the keyword map. |
| 1655 // * Autogeneration of the keyword for a Google default search provider |
| 1656 // at load time causes it to conflict with an existing keyword. In this |
| 1657 // case we delete the existing keyword if it's replaceable, or else undo |
| 1658 // the change in keyword for |existing_turl|. |
| 1659 TemplateURL* existing_keyword_turl = i->second; |
| 1660 if (existing_keyword_turl->GetType() != TemplateURL::NORMAL) { |
| 1661 if (!CanReplace(existing_turl)) |
| 1662 keyword_to_template_map_[keyword] = existing_turl; |
| 1663 } else { |
| 1664 if (CanReplace(existing_keyword_turl)) { |
| 1665 RemoveNoNotify(existing_keyword_turl); |
| 1666 } else { |
| 1667 existing_turl->data_.SetKeyword(old_keyword); |
| 1668 keyword_to_template_map_[old_keyword] = existing_turl; |
| 1669 } |
| 1670 } |
| 1671 } |
| 1672 if (!existing_turl->sync_guid().empty()) |
| 1673 guid_to_template_map_[existing_turl->sync_guid()] = existing_turl; |
| 1674 |
| 1675 if (web_data_service_) |
| 1676 web_data_service_->UpdateKeyword(existing_turl->data()); |
| 1677 |
| 1678 // Inform sync of the update. |
| 1679 ProcessTemplateURLChange( |
| 1680 FROM_HERE, existing_turl, syncer::SyncChange::ACTION_UPDATE); |
| 1681 |
| 1682 if (default_search_provider_ == existing_turl && |
| 1683 default_search_provider_source_ == DefaultSearchManager::FROM_USER) { |
| 1684 default_search_manager_.SetUserSelectedDefaultSearchEngine( |
| 1685 default_search_provider_->data()); |
| 1686 } |
| 1687 return true; |
| 1688 } |
| 1689 |
| 1690 // static |
| 1691 void TemplateURLService::UpdateTemplateURLIfPrepopulated( |
| 1692 TemplateURL* template_url, |
| 1693 PrefService* prefs) { |
| 1694 int prepopulate_id = template_url->prepopulate_id(); |
| 1695 if (template_url->prepopulate_id() == 0) |
| 1696 return; |
| 1697 |
| 1698 size_t default_search_index; |
| 1699 ScopedVector<TemplateURLData> prepopulated_urls = |
| 1700 TemplateURLPrepopulateData::GetPrepopulatedEngines( |
| 1701 prefs, &default_search_index); |
| 1702 |
| 1703 for (size_t i = 0; i < prepopulated_urls.size(); ++i) { |
| 1704 if (prepopulated_urls[i]->prepopulate_id == prepopulate_id) { |
| 1705 MergeIntoPrepopulatedEngineData(template_url, prepopulated_urls[i]); |
| 1706 template_url->CopyFrom(TemplateURL(*prepopulated_urls[i])); |
| 1707 } |
| 1708 } |
| 1709 } |
| 1710 |
| 1711 void TemplateURLService::MaybeUpdateDSEAfterSync(TemplateURL* synced_turl) { |
| 1712 if (prefs_ && |
| 1713 (synced_turl->sync_guid() == |
| 1714 prefs_->GetString(prefs::kSyncedDefaultSearchProviderGUID))) { |
| 1715 default_search_manager_.SetUserSelectedDefaultSearchEngine( |
| 1716 synced_turl->data()); |
| 1717 } |
| 1718 } |
| 1719 |
| 1720 void TemplateURLService::UpdateKeywordSearchTermsForURL( |
| 1721 const URLVisitedDetails& details) { |
| 1722 if (!details.url.is_valid()) |
| 1723 return; |
| 1724 |
| 1725 const TemplateURLSet* urls_for_host = |
| 1726 provider_map_->GetURLsForHost(details.url.host()); |
| 1727 if (!urls_for_host) |
| 1728 return; |
| 1729 |
| 1730 for (TemplateURLSet::const_iterator i = urls_for_host->begin(); |
| 1731 i != urls_for_host->end(); ++i) { |
| 1732 base::string16 search_terms; |
| 1733 if ((*i)->ExtractSearchTermsFromURL(details.url, search_terms_data(), |
| 1734 &search_terms) && |
| 1735 !search_terms.empty()) { |
| 1736 if (details.is_keyword_transition) { |
| 1737 // The visit is the result of the user entering a keyword, generate a |
| 1738 // KEYWORD_GENERATED visit for the KEYWORD so that the keyword typed |
| 1739 // count is boosted. |
| 1740 AddTabToSearchVisit(**i); |
| 1741 } |
| 1742 SetKeywordSearchTermsForURL(*i, details.url, search_terms); |
| 1743 } |
| 1744 } |
| 1745 } |
| 1746 |
| 1747 void TemplateURLService::AddTabToSearchVisit(const TemplateURL& t_url) { |
| 1748 // Only add visits for entries the user hasn't modified. If the user modified |
| 1749 // the entry the keyword may no longer correspond to the host name. It may be |
| 1750 // possible to do something more sophisticated here, but it's so rare as to |
| 1751 // not be worth it. |
| 1752 if (!t_url.safe_for_autoreplace()) |
| 1753 return; |
| 1754 |
| 1755 if (!client_) |
| 1756 return; |
| 1757 |
| 1758 GURL url( |
| 1759 url_fixer::FixupURL(base::UTF16ToUTF8(t_url.keyword()), std::string())); |
| 1760 if (!url.is_valid()) |
| 1761 return; |
| 1762 |
| 1763 // Synthesize a visit for the keyword. This ensures the url for the keyword is |
| 1764 // autocompleted even if the user doesn't type the url in directly. |
| 1765 client_->AddKeywordGeneratedVisit(url); |
| 1766 } |
| 1767 |
| 1768 void TemplateURLService::RequestGoogleURLTrackerServerCheckIfNecessary() { |
| 1769 if (default_search_provider_ && |
| 1770 default_search_provider_->HasGoogleBaseURLs(search_terms_data()) && |
| 1771 google_url_tracker_) |
| 1772 google_url_tracker_->RequestServerCheck(false); |
| 1773 } |
| 1774 |
| 1775 void TemplateURLService::GoogleBaseURLChanged() { |
| 1776 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); |
| 1777 bool something_changed = false; |
| 1778 for (TemplateURLVector::iterator i(template_urls_.begin()); |
| 1779 i != template_urls_.end(); ++i) { |
| 1780 TemplateURL* t_url = *i; |
| 1781 if (t_url->HasGoogleBaseURLs(search_terms_data())) { |
| 1782 TemplateURL updated_turl(t_url->data()); |
| 1783 updated_turl.ResetKeywordIfNecessary(search_terms_data(), false); |
| 1784 KeywordToTemplateMap::const_iterator existing_entry = |
| 1785 keyword_to_template_map_.find(updated_turl.keyword()); |
| 1786 if ((existing_entry != keyword_to_template_map_.end()) && |
| 1787 (existing_entry->second != t_url)) { |
| 1788 // The new autogenerated keyword conflicts with another TemplateURL. |
| 1789 // Overwrite it if it's replaceable; otherwise, leave |t_url| using its |
| 1790 // current keyword. (This will not prevent |t_url| from auto-updating |
| 1791 // the keyword in the future if the conflicting TemplateURL disappears.) |
| 1792 // Note that we must still update |t_url| in this case, or the |
| 1793 // |provider_map_| will not be updated correctly. |
| 1794 if (CanReplace(existing_entry->second)) |
| 1795 RemoveNoNotify(existing_entry->second); |
| 1796 else |
| 1797 updated_turl.data_.SetKeyword(t_url->keyword()); |
| 1798 } |
| 1799 something_changed = true; |
| 1800 // This will send the keyword change to sync. Note that other clients |
| 1801 // need to reset the keyword to an appropriate local value when this |
| 1802 // change arrives; see CreateTemplateURLFromTemplateURLAndSyncData(). |
| 1803 UpdateNoNotify(t_url, updated_turl); |
| 1804 } |
| 1805 } |
| 1806 if (something_changed) |
| 1807 NotifyObservers(); |
| 1808 } |
| 1809 |
| 1810 void TemplateURLService::OnGoogleURLUpdated(GURL old_url, GURL new_url) { |
| 1811 if (loaded_) |
| 1812 GoogleBaseURLChanged(); |
| 1813 } |
| 1814 |
| 1815 void TemplateURLService::OnDefaultSearchChange( |
| 1816 const TemplateURLData* data, |
| 1817 DefaultSearchManager::Source source) { |
| 1818 if (prefs_ && (source == DefaultSearchManager::FROM_USER) && |
| 1819 ((source != default_search_provider_source_) || |
| 1820 !IdenticalSyncGUIDs(data, GetDefaultSearchProvider()))) { |
| 1821 prefs_->SetString(prefs::kSyncedDefaultSearchProviderGUID, data->sync_guid); |
| 1822 } |
| 1823 ApplyDefaultSearchChange(data, source); |
| 1824 } |
| 1825 |
| 1826 void TemplateURLService::ApplyDefaultSearchChange( |
| 1827 const TemplateURLData* data, |
| 1828 DefaultSearchManager::Source source) { |
| 1829 if (!ApplyDefaultSearchChangeNoMetrics(data, source)) |
| 1830 return; |
| 1831 |
| 1832 UMA_HISTOGRAM_ENUMERATION( |
| 1833 "Search.DefaultSearchChangeOrigin", dsp_change_origin_, DSP_CHANGE_MAX); |
| 1834 |
| 1835 if (GetDefaultSearchProvider() && |
| 1836 GetDefaultSearchProvider()->HasGoogleBaseURLs(search_terms_data()) && |
| 1837 !dsp_change_callback_.is_null()) |
| 1838 dsp_change_callback_.Run(); |
| 1839 } |
| 1840 |
| 1841 bool TemplateURLService::ApplyDefaultSearchChangeNoMetrics( |
| 1842 const TemplateURLData* data, |
| 1843 DefaultSearchManager::Source source) { |
| 1844 if (!loaded_) { |
| 1845 // Set |initial_default_search_provider_| from the preferences. This is |
| 1846 // mainly so we can hold ownership until we get to the point where the list |
| 1847 // of keywords from Web Data is the owner of everything including the |
| 1848 // default. |
| 1849 bool changed = TemplateURL::MatchesData( |
| 1850 initial_default_search_provider_.get(), data, search_terms_data()); |
| 1851 initial_default_search_provider_.reset( |
| 1852 data ? new TemplateURL(*data) : NULL); |
| 1853 default_search_provider_source_ = source; |
| 1854 return changed; |
| 1855 } |
| 1856 |
| 1857 // Prevent recursion if we update the value stored in default_search_manager_. |
| 1858 // Note that we exclude the case of data == NULL because that could cause a |
| 1859 // false positive for recursion when the initial_default_search_provider_ is |
| 1860 // NULL due to policy. We'll never actually get recursion with data == NULL. |
| 1861 if (source == default_search_provider_source_ && data != NULL && |
| 1862 TemplateURL::MatchesData(default_search_provider_, data, |
| 1863 search_terms_data())) |
| 1864 return false; |
| 1865 |
| 1866 // This may be deleted later. Use exclusively for pointer comparison to detect |
| 1867 // a change. |
| 1868 TemplateURL* previous_default_search_engine = default_search_provider_; |
| 1869 |
| 1870 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); |
| 1871 if (default_search_provider_source_ == DefaultSearchManager::FROM_POLICY || |
| 1872 source == DefaultSearchManager::FROM_POLICY) { |
| 1873 // We do this both to remove any no-longer-applicable policy-defined DSE as |
| 1874 // well as to add the new one, if appropriate. |
| 1875 UpdateProvidersCreatedByPolicy( |
| 1876 &template_urls_, |
| 1877 source == DefaultSearchManager::FROM_POLICY ? data : NULL); |
| 1878 } |
| 1879 |
| 1880 if (!data) { |
| 1881 default_search_provider_ = NULL; |
| 1882 } else if (source == DefaultSearchManager::FROM_EXTENSION) { |
| 1883 default_search_provider_ = FindMatchingExtensionTemplateURL( |
| 1884 *data, TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION); |
| 1885 } else if (source == DefaultSearchManager::FROM_FALLBACK) { |
| 1886 default_search_provider_ = |
| 1887 FindPrepopulatedTemplateURL(data->prepopulate_id); |
| 1888 if (default_search_provider_) { |
| 1889 TemplateURLData update_data(*data); |
| 1890 update_data.sync_guid = default_search_provider_->sync_guid(); |
| 1891 if (!default_search_provider_->safe_for_autoreplace()) { |
| 1892 update_data.safe_for_autoreplace = false; |
| 1893 update_data.SetKeyword(default_search_provider_->keyword()); |
| 1894 update_data.short_name = default_search_provider_->short_name(); |
| 1895 } |
| 1896 UpdateNoNotify(default_search_provider_, TemplateURL(update_data)); |
| 1897 } else { |
| 1898 // Normally the prepopulated fallback should be present in |
| 1899 // |template_urls_|, but in a few cases it might not be: |
| 1900 // (1) Tests that initialize the TemplateURLService in peculiar ways. |
| 1901 // (2) If the user deleted the pre-populated default and we subsequently |
| 1902 // lost their user-selected value. |
| 1903 TemplateURL* new_dse = new TemplateURL(*data); |
| 1904 if (AddNoNotify(new_dse, true)) |
| 1905 default_search_provider_ = new_dse; |
| 1906 } |
| 1907 } else if (source == DefaultSearchManager::FROM_USER) { |
| 1908 default_search_provider_ = GetTemplateURLForGUID(data->sync_guid); |
| 1909 if (!default_search_provider_ && data->prepopulate_id) { |
| 1910 default_search_provider_ = |
| 1911 FindPrepopulatedTemplateURL(data->prepopulate_id); |
| 1912 } |
| 1913 TemplateURLData new_data(*data); |
| 1914 new_data.show_in_default_list = true; |
| 1915 if (default_search_provider_) { |
| 1916 UpdateNoNotify(default_search_provider_, TemplateURL(new_data)); |
| 1917 } else { |
| 1918 new_data.id = kInvalidTemplateURLID; |
| 1919 TemplateURL* new_dse = new TemplateURL(new_data); |
| 1920 if (AddNoNotify(new_dse, true)) |
| 1921 default_search_provider_ = new_dse; |
| 1922 } |
| 1923 if (default_search_provider_ && prefs_) { |
| 1924 prefs_->SetString(prefs::kSyncedDefaultSearchProviderGUID, |
| 1925 default_search_provider_->sync_guid()); |
| 1926 } |
| 1927 |
| 1928 } |
| 1929 |
| 1930 default_search_provider_source_ = source; |
| 1931 |
| 1932 bool changed = default_search_provider_ != previous_default_search_engine; |
| 1933 if (changed) |
| 1934 RequestGoogleURLTrackerServerCheckIfNecessary(); |
| 1935 |
| 1936 NotifyObservers(); |
| 1937 |
| 1938 return changed; |
| 1939 } |
| 1940 |
| 1941 bool TemplateURLService::AddNoNotify(TemplateURL* template_url, |
| 1942 bool newly_adding) { |
| 1943 DCHECK(template_url); |
| 1944 |
| 1945 if (newly_adding) { |
| 1946 DCHECK_EQ(kInvalidTemplateURLID, template_url->id()); |
| 1947 DCHECK(std::find(template_urls_.begin(), template_urls_.end(), |
| 1948 template_url) == template_urls_.end()); |
| 1949 template_url->data_.id = ++next_id_; |
| 1950 } |
| 1951 |
| 1952 template_url->ResetKeywordIfNecessary(search_terms_data(), false); |
| 1953 // Check whether |template_url|'s keyword conflicts with any already in the |
| 1954 // model. |
| 1955 TemplateURL* existing_keyword_turl = |
| 1956 GetTemplateURLForKeyword(template_url->keyword()); |
| 1957 |
| 1958 // Check whether |template_url|'s keyword conflicts with any already in the |
| 1959 // model. Note that we can reach here during the loading phase while |
| 1960 // processing the template URLs from the web data service. In this case, |
| 1961 // GetTemplateURLForKeyword() will look not only at what's already in the |
| 1962 // model, but at the |initial_default_search_provider_|. Since this engine |
| 1963 // will presumably also be present in the web data, we need to double-check |
| 1964 // that any "pre-existing" entries we find are actually coming from |
| 1965 // |template_urls_|, lest we detect a "conflict" between the |
| 1966 // |initial_default_search_provider_| and the web data version of itself. |
| 1967 if (existing_keyword_turl && |
| 1968 (std::find(template_urls_.begin(), template_urls_.end(), |
| 1969 existing_keyword_turl) != template_urls_.end())) { |
| 1970 DCHECK_NE(existing_keyword_turl, template_url); |
| 1971 // Only replace one of the TemplateURLs if they are either both extensions, |
| 1972 // or both not extensions. |
| 1973 bool are_same_type = existing_keyword_turl->GetType() == |
| 1974 template_url->GetType(); |
| 1975 if (CanReplace(existing_keyword_turl) && are_same_type) { |
| 1976 RemoveNoNotify(existing_keyword_turl); |
| 1977 } else if (CanReplace(template_url) && are_same_type) { |
| 1978 delete template_url; |
| 1979 return false; |
| 1980 } else { |
| 1981 base::string16 new_keyword = |
| 1982 UniquifyKeyword(*existing_keyword_turl, false); |
| 1983 ResetTemplateURLNoNotify(existing_keyword_turl, |
| 1984 existing_keyword_turl->short_name(), new_keyword, |
| 1985 existing_keyword_turl->url()); |
| 1986 } |
| 1987 } |
| 1988 template_urls_.push_back(template_url); |
| 1989 AddToMaps(template_url); |
| 1990 |
| 1991 if (newly_adding && |
| 1992 (template_url->GetType() != |
| 1993 TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION)) { |
| 1994 if (web_data_service_) |
| 1995 web_data_service_->AddKeyword(template_url->data()); |
| 1996 |
| 1997 // Inform sync of the addition. Note that this will assign a GUID to |
| 1998 // template_url and add it to the guid_to_template_map_. |
| 1999 ProcessTemplateURLChange(FROM_HERE, |
| 2000 template_url, |
| 2001 syncer::SyncChange::ACTION_ADD); |
| 2002 } |
| 2003 |
| 2004 return true; |
| 2005 } |
| 2006 |
| 2007 void TemplateURLService::RemoveNoNotify(TemplateURL* template_url) { |
| 2008 DCHECK(template_url != default_search_provider_); |
| 2009 |
| 2010 TemplateURLVector::iterator i = |
| 2011 std::find(template_urls_.begin(), template_urls_.end(), template_url); |
| 2012 if (i == template_urls_.end()) |
| 2013 return; |
| 2014 |
| 2015 RemoveFromMaps(template_url); |
| 2016 |
| 2017 // Remove it from the vector containing all TemplateURLs. |
| 2018 template_urls_.erase(i); |
| 2019 |
| 2020 if (template_url->GetType() != TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION) { |
| 2021 if (web_data_service_) |
| 2022 web_data_service_->RemoveKeyword(template_url->id()); |
| 2023 |
| 2024 // Inform sync of the deletion. |
| 2025 ProcessTemplateURLChange(FROM_HERE, |
| 2026 template_url, |
| 2027 syncer::SyncChange::ACTION_DELETE); |
| 2028 |
| 2029 UMA_HISTOGRAM_ENUMERATION(kDeleteSyncedEngineHistogramName, |
| 2030 DELETE_ENGINE_USER_ACTION, DELETE_ENGINE_MAX); |
| 2031 } |
| 2032 |
| 2033 if (loaded_ && client_) |
| 2034 client_->DeleteAllSearchTermsForKeyword(template_url->id()); |
| 2035 |
| 2036 // We own the TemplateURL and need to delete it. |
| 2037 delete template_url; |
| 2038 } |
| 2039 |
| 2040 bool TemplateURLService::ResetTemplateURLNoNotify( |
| 2041 TemplateURL* url, |
| 2042 const base::string16& title, |
| 2043 const base::string16& keyword, |
| 2044 const std::string& search_url) { |
| 2045 DCHECK(!keyword.empty()); |
| 2046 DCHECK(!search_url.empty()); |
| 2047 TemplateURLData data(url->data()); |
| 2048 data.short_name = title; |
| 2049 data.SetKeyword(keyword); |
| 2050 if (search_url != data.url()) { |
| 2051 data.SetURL(search_url); |
| 2052 // The urls have changed, reset the favicon url. |
| 2053 data.favicon_url = GURL(); |
| 2054 } |
| 2055 data.safe_for_autoreplace = false; |
| 2056 data.last_modified = time_provider_(); |
| 2057 return UpdateNoNotify(url, TemplateURL(data)); |
| 2058 } |
| 2059 |
| 2060 void TemplateURLService::NotifyObservers() { |
| 2061 if (!loaded_) |
| 2062 return; |
| 2063 |
| 2064 FOR_EACH_OBSERVER(TemplateURLServiceObserver, model_observers_, |
| 2065 OnTemplateURLServiceChanged()); |
| 2066 } |
| 2067 |
| 2068 // |template_urls| are the TemplateURLs loaded from the database. |
| 2069 // |default_from_prefs| is the default search provider from the preferences, or |
| 2070 // NULL if the DSE is not policy-defined. |
| 2071 // |
| 2072 // This function removes from the vector and the database all the TemplateURLs |
| 2073 // that were set by policy, unless it is the current default search provider, in |
| 2074 // which case it is updated with the data from prefs. |
| 2075 void TemplateURLService::UpdateProvidersCreatedByPolicy( |
| 2076 TemplateURLVector* template_urls, |
| 2077 const TemplateURLData* default_from_prefs) { |
| 2078 DCHECK(template_urls); |
| 2079 |
| 2080 for (TemplateURLVector::iterator i = template_urls->begin(); |
| 2081 i != template_urls->end(); ) { |
| 2082 TemplateURL* template_url = *i; |
| 2083 if (template_url->created_by_policy()) { |
| 2084 if (default_from_prefs && |
| 2085 TemplateURL::MatchesData(template_url, default_from_prefs, |
| 2086 search_terms_data())) { |
| 2087 // If the database specified a default search provider that was set |
| 2088 // by policy, and the default search provider from the preferences |
| 2089 // is also set by policy and they are the same, keep the entry in the |
| 2090 // database and the |default_search_provider|. |
| 2091 default_search_provider_ = template_url; |
| 2092 // Prevent us from saving any other entries, or creating a new one. |
| 2093 default_from_prefs = NULL; |
| 2094 ++i; |
| 2095 continue; |
| 2096 } |
| 2097 |
| 2098 RemoveFromMaps(template_url); |
| 2099 i = template_urls->erase(i); |
| 2100 if (web_data_service_) |
| 2101 web_data_service_->RemoveKeyword(template_url->id()); |
| 2102 delete template_url; |
| 2103 } else { |
| 2104 ++i; |
| 2105 } |
| 2106 } |
| 2107 |
| 2108 if (default_from_prefs) { |
| 2109 default_search_provider_ = NULL; |
| 2110 default_search_provider_source_ = DefaultSearchManager::FROM_POLICY; |
| 2111 TemplateURLData new_data(*default_from_prefs); |
| 2112 if (new_data.sync_guid.empty()) |
| 2113 new_data.sync_guid = base::GenerateGUID(); |
| 2114 new_data.created_by_policy = true; |
| 2115 TemplateURL* new_dse = new TemplateURL(new_data); |
| 2116 if (AddNoNotify(new_dse, true)) |
| 2117 default_search_provider_ = new_dse; |
| 2118 } |
| 2119 } |
| 2120 |
| 2121 void TemplateURLService::ResetTemplateURLGUID(TemplateURL* url, |
| 2122 const std::string& guid) { |
| 2123 DCHECK(loaded_); |
| 2124 DCHECK(!guid.empty()); |
| 2125 |
| 2126 TemplateURLData data(url->data()); |
| 2127 data.sync_guid = guid; |
| 2128 UpdateNoNotify(url, TemplateURL(data)); |
| 2129 } |
| 2130 |
| 2131 base::string16 TemplateURLService::UniquifyKeyword(const TemplateURL& turl, |
| 2132 bool force) { |
| 2133 if (!force) { |
| 2134 // Already unique. |
| 2135 if (!GetTemplateURLForKeyword(turl.keyword())) |
| 2136 return turl.keyword(); |
| 2137 |
| 2138 // First, try to return the generated keyword for the TemplateURL (except |
| 2139 // for extensions, as their keywords are not associated with their URLs). |
| 2140 GURL gurl(turl.url()); |
| 2141 if (gurl.is_valid() && |
| 2142 (turl.GetType() != TemplateURL::OMNIBOX_API_EXTENSION)) { |
| 2143 base::string16 keyword_candidate = TemplateURL::GenerateKeyword(gurl); |
| 2144 if (!GetTemplateURLForKeyword(keyword_candidate)) |
| 2145 return keyword_candidate; |
| 2146 } |
| 2147 } |
| 2148 |
| 2149 // We try to uniquify the keyword by appending a special character to the end. |
| 2150 // This is a best-effort approach where we try to preserve the original |
| 2151 // keyword and let the user do what they will after our attempt. |
| 2152 base::string16 keyword_candidate(turl.keyword()); |
| 2153 do { |
| 2154 keyword_candidate.append(base::ASCIIToUTF16("_")); |
| 2155 } while (GetTemplateURLForKeyword(keyword_candidate)); |
| 2156 |
| 2157 return keyword_candidate; |
| 2158 } |
| 2159 |
| 2160 bool TemplateURLService::IsLocalTemplateURLBetter( |
| 2161 const TemplateURL* local_turl, |
| 2162 const TemplateURL* sync_turl) { |
| 2163 DCHECK(GetTemplateURLForGUID(local_turl->sync_guid())); |
| 2164 return local_turl->last_modified() > sync_turl->last_modified() || |
| 2165 local_turl->created_by_policy() || |
| 2166 local_turl== GetDefaultSearchProvider(); |
| 2167 } |
| 2168 |
| 2169 void TemplateURLService::ResolveSyncKeywordConflict( |
| 2170 TemplateURL* unapplied_sync_turl, |
| 2171 TemplateURL* applied_sync_turl, |
| 2172 syncer::SyncChangeList* change_list) { |
| 2173 DCHECK(loaded_); |
| 2174 DCHECK(unapplied_sync_turl); |
| 2175 DCHECK(applied_sync_turl); |
| 2176 DCHECK(change_list); |
| 2177 DCHECK_EQ(applied_sync_turl->keyword(), unapplied_sync_turl->keyword()); |
| 2178 DCHECK_NE(TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION, |
| 2179 applied_sync_turl->GetType()); |
| 2180 |
| 2181 // Both |unapplied_sync_turl| and |applied_sync_turl| are known to Sync, so |
| 2182 // don't delete either of them. Instead, determine which is "better" and |
| 2183 // uniquify the other one, sending an update to the server for the updated |
| 2184 // entry. |
| 2185 const bool applied_turl_is_better = |
| 2186 IsLocalTemplateURLBetter(applied_sync_turl, unapplied_sync_turl); |
| 2187 TemplateURL* loser = applied_turl_is_better ? |
| 2188 unapplied_sync_turl : applied_sync_turl; |
| 2189 base::string16 new_keyword = UniquifyKeyword(*loser, false); |
| 2190 DCHECK(!GetTemplateURLForKeyword(new_keyword)); |
| 2191 if (applied_turl_is_better) { |
| 2192 // Just set the keyword of |unapplied_sync_turl|. The caller is responsible |
| 2193 // for adding or updating unapplied_sync_turl in the local model. |
| 2194 unapplied_sync_turl->data_.SetKeyword(new_keyword); |
| 2195 } else { |
| 2196 // Update |applied_sync_turl| in the local model with the new keyword. |
| 2197 TemplateURLData data(applied_sync_turl->data()); |
| 2198 data.SetKeyword(new_keyword); |
| 2199 if (UpdateNoNotify(applied_sync_turl, TemplateURL(data))) |
| 2200 NotifyObservers(); |
| 2201 } |
| 2202 // The losing TemplateURL should have their keyword updated. Send a change to |
| 2203 // the server to reflect this change. |
| 2204 syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*loser); |
| 2205 change_list->push_back(syncer::SyncChange(FROM_HERE, |
| 2206 syncer::SyncChange::ACTION_UPDATE, |
| 2207 sync_data)); |
| 2208 } |
| 2209 |
| 2210 void TemplateURLService::MergeInSyncTemplateURL( |
| 2211 TemplateURL* sync_turl, |
| 2212 const SyncDataMap& sync_data, |
| 2213 syncer::SyncChangeList* change_list, |
| 2214 SyncDataMap* local_data, |
| 2215 syncer::SyncMergeResult* merge_result) { |
| 2216 DCHECK(sync_turl); |
| 2217 DCHECK(!GetTemplateURLForGUID(sync_turl->sync_guid())); |
| 2218 DCHECK(IsFromSync(sync_turl, sync_data)); |
| 2219 |
| 2220 TemplateURL* conflicting_turl = |
| 2221 FindNonExtensionTemplateURLForKeyword(sync_turl->keyword()); |
| 2222 bool should_add_sync_turl = true; |
| 2223 |
| 2224 // If there was no TemplateURL in the local model that conflicts with |
| 2225 // |sync_turl|, skip the following preparation steps and just add |sync_turl| |
| 2226 // directly. Otherwise, modify |conflicting_turl| to make room for |
| 2227 // |sync_turl|. |
| 2228 if (conflicting_turl) { |
| 2229 if (IsFromSync(conflicting_turl, sync_data)) { |
| 2230 // |conflicting_turl| is already known to Sync, so we're not allowed to |
| 2231 // remove it. In this case, we want to uniquify the worse one and send an |
| 2232 // update for the changed keyword to sync. We can reuse the logic from |
| 2233 // ResolveSyncKeywordConflict for this. |
| 2234 ResolveSyncKeywordConflict(sync_turl, conflicting_turl, change_list); |
| 2235 merge_result->set_num_items_modified( |
| 2236 merge_result->num_items_modified() + 1); |
| 2237 } else { |
| 2238 // |conflicting_turl| is not yet known to Sync. If it is better, then we |
| 2239 // want to transfer its values up to sync. Otherwise, we remove it and |
| 2240 // allow the entry from Sync to overtake it in the model. |
| 2241 const std::string guid = conflicting_turl->sync_guid(); |
| 2242 if (IsLocalTemplateURLBetter(conflicting_turl, sync_turl)) { |
| 2243 ResetTemplateURLGUID(conflicting_turl, sync_turl->sync_guid()); |
| 2244 syncer::SyncData sync_data = |
| 2245 CreateSyncDataFromTemplateURL(*conflicting_turl); |
| 2246 change_list->push_back(syncer::SyncChange( |
| 2247 FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data)); |
| 2248 // Note that in this case we do not add the Sync TemplateURL to the |
| 2249 // local model, since we've effectively "merged" it in by updating the |
| 2250 // local conflicting entry with its sync_guid. |
| 2251 should_add_sync_turl = false; |
| 2252 merge_result->set_num_items_modified( |
| 2253 merge_result->num_items_modified() + 1); |
| 2254 } else { |
| 2255 // We guarantee that this isn't the local search provider. Otherwise, |
| 2256 // local would have won. |
| 2257 DCHECK(conflicting_turl != GetDefaultSearchProvider()); |
| 2258 Remove(conflicting_turl); |
| 2259 merge_result->set_num_items_deleted( |
| 2260 merge_result->num_items_deleted() + 1); |
| 2261 } |
| 2262 // This TemplateURL was either removed or overwritten in the local model. |
| 2263 // Remove the entry from the local data so it isn't pushed up to Sync. |
| 2264 local_data->erase(guid); |
| 2265 } |
| 2266 } |
| 2267 |
| 2268 if (should_add_sync_turl) { |
| 2269 // Force the local ID to kInvalidTemplateURLID so we can add it. |
| 2270 TemplateURLData data(sync_turl->data()); |
| 2271 data.id = kInvalidTemplateURLID; |
| 2272 TemplateURL* added = new TemplateURL(data); |
| 2273 base::AutoReset<DefaultSearchChangeOrigin> change_origin( |
| 2274 &dsp_change_origin_, DSP_CHANGE_SYNC_ADD); |
| 2275 if (Add(added)) |
| 2276 MaybeUpdateDSEAfterSync(added); |
| 2277 merge_result->set_num_items_added( |
| 2278 merge_result->num_items_added() + 1); |
| 2279 } |
| 2280 } |
| 2281 |
| 2282 void TemplateURLService::PatchMissingSyncGUIDs( |
| 2283 TemplateURLVector* template_urls) { |
| 2284 DCHECK(template_urls); |
| 2285 for (TemplateURLVector::iterator i = template_urls->begin(); |
| 2286 i != template_urls->end(); ++i) { |
| 2287 TemplateURL* template_url = *i; |
| 2288 DCHECK(template_url); |
| 2289 if (template_url->sync_guid().empty() && |
| 2290 (template_url->GetType() != |
| 2291 TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION)) { |
| 2292 template_url->data_.sync_guid = base::GenerateGUID(); |
| 2293 if (web_data_service_) |
| 2294 web_data_service_->UpdateKeyword(template_url->data()); |
| 2295 } |
| 2296 } |
| 2297 } |
| 2298 |
| 2299 void TemplateURLService::OnSyncedDefaultSearchProviderGUIDChanged() { |
| 2300 base::AutoReset<DefaultSearchChangeOrigin> change_origin( |
| 2301 &dsp_change_origin_, DSP_CHANGE_SYNC_PREF); |
| 2302 |
| 2303 std::string new_guid = |
| 2304 prefs_->GetString(prefs::kSyncedDefaultSearchProviderGUID); |
| 2305 if (new_guid.empty()) { |
| 2306 default_search_manager_.ClearUserSelectedDefaultSearchEngine(); |
| 2307 return; |
| 2308 } |
| 2309 |
| 2310 TemplateURL* turl = GetTemplateURLForGUID(new_guid); |
| 2311 if (turl) |
| 2312 default_search_manager_.SetUserSelectedDefaultSearchEngine(turl->data()); |
| 2313 } |
| 2314 |
| 2315 TemplateURL* TemplateURLService::FindPrepopulatedTemplateURL( |
| 2316 int prepopulated_id) { |
| 2317 for (TemplateURLVector::const_iterator i = template_urls_.begin(); |
| 2318 i != template_urls_.end(); ++i) { |
| 2319 if ((*i)->prepopulate_id() == prepopulated_id) |
| 2320 return *i; |
| 2321 } |
| 2322 return NULL; |
| 2323 } |
| 2324 |
| 2325 TemplateURL* TemplateURLService::FindTemplateURLForExtension( |
| 2326 const std::string& extension_id, |
| 2327 TemplateURL::Type type) { |
| 2328 DCHECK_NE(TemplateURL::NORMAL, type); |
| 2329 for (TemplateURLVector::const_iterator i = template_urls_.begin(); |
| 2330 i != template_urls_.end(); ++i) { |
| 2331 if ((*i)->GetType() == type && |
| 2332 (*i)->GetExtensionId() == extension_id) |
| 2333 return *i; |
| 2334 } |
| 2335 return NULL; |
| 2336 } |
| 2337 |
| 2338 TemplateURL* TemplateURLService::FindMatchingExtensionTemplateURL( |
| 2339 const TemplateURLData& data, |
| 2340 TemplateURL::Type type) { |
| 2341 DCHECK_NE(TemplateURL::NORMAL, type); |
| 2342 for (TemplateURLVector::const_iterator i = template_urls_.begin(); |
| 2343 i != template_urls_.end(); ++i) { |
| 2344 if ((*i)->GetType() == type && |
| 2345 TemplateURL::MatchesData(*i, &data, search_terms_data())) |
| 2346 return *i; |
| 2347 } |
| 2348 return NULL; |
| 2349 } |
| 2350 |
| 2351 void TemplateURLService::UpdateExtensionDefaultSearchEngine() { |
| 2352 TemplateURL* most_recently_intalled_default = NULL; |
| 2353 for (TemplateURLVector::const_iterator i = template_urls_.begin(); |
| 2354 i != template_urls_.end(); ++i) { |
| 2355 if (((*i)->GetType() == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION) && |
| 2356 (*i)->extension_info_->wants_to_be_default_engine && |
| 2357 (*i)->SupportsReplacement(search_terms_data()) && |
| 2358 (!most_recently_intalled_default || |
| 2359 (most_recently_intalled_default->extension_info_->install_time < |
| 2360 (*i)->extension_info_->install_time))) |
| 2361 most_recently_intalled_default = *i; |
| 2362 } |
| 2363 |
| 2364 if (most_recently_intalled_default) { |
| 2365 base::AutoReset<DefaultSearchChangeOrigin> change_origin( |
| 2366 &dsp_change_origin_, DSP_CHANGE_OVERRIDE_SETTINGS_EXTENSION); |
| 2367 default_search_manager_.SetExtensionControlledDefaultSearchEngine( |
| 2368 most_recently_intalled_default->data()); |
| 2369 } else { |
| 2370 default_search_manager_.ClearExtensionControlledDefaultSearchEngine(); |
| 2371 } |
| 2372 } |
| OLD | NEW |