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 "components/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 "components/rappor/rappor_service.h" 25 #include "components/search_engines/search_engines_pref_names.h" 26 #include "components/search_engines/search_host_to_urls_map.h" 27 #include "components/search_engines/search_terms_data.h" 28 #include "components/search_engines/template_url.h" 29 #include "components/search_engines/template_url_prepopulate_data.h" 30 #include "components/search_engines/template_url_service_client.h" 31 #include "components/search_engines/template_url_service_observer.h" 32 #include "components/search_engines/util.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 const scoped_refptr<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_.get()); 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/details/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_.get()) 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_.get()) 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 if (client_) { 855 // Restore extension info of loaded TemplateURLs. 856 for (size_t i = 0; i < template_urls.size(); ++i) { 857 DCHECK(!template_urls[i]->extension_info_); 858 client_->RestoreExtensionInfoIfNecessary(template_urls[i]); 859 } 860 } 861 862 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); 863 864 PatchMissingSyncGUIDs(&template_urls); 865 SetTemplateURLs(&template_urls); 866 867 // This initializes provider_map_ which should be done before 868 // calling UpdateKeywordSearchTermsForURL. 869 // This also calls NotifyObservers. 870 ChangeToLoadedState(); 871 872 // Index any visits that occurred before we finished loading. 873 for (size_t i = 0; i < visits_to_add_.size(); ++i) 874 UpdateKeywordSearchTermsForURL(visits_to_add_[i]); 875 visits_to_add_.clear(); 876 877 if (new_resource_keyword_version) 878 web_data_service_->SetBuiltinKeywordVersion(new_resource_keyword_version); 879 880 if (default_search_provider_) { 881 UMA_HISTOGRAM_ENUMERATION( 882 "Search.DefaultSearchProviderType", 883 TemplateURLPrepopulateData::GetEngineType( 884 *default_search_provider_, search_terms_data()), 885 SEARCH_ENGINE_MAX); 886 887 if (rappor_service_) { 888 rappor_service_->RecordSample( 889 "Search.DefaultSearchProvider", 890 rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, 891 net::registry_controlled_domains::GetDomainAndRegistry( 892 default_search_provider_->url_ref().GetHost(search_terms_data()), 893 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)); 894 } 895 } 896 } 897 898 base::string16 TemplateURLService::GetKeywordShortName( 899 const base::string16& keyword, 900 bool* is_omnibox_api_extension_keyword) { 901 const TemplateURL* template_url = GetTemplateURLForKeyword(keyword); 902 903 // TODO(sky): Once LocationBarView adds a listener to the TemplateURLService 904 // to track changes to the model, this should become a DCHECK. 905 if (template_url) { 906 *is_omnibox_api_extension_keyword = 907 template_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION; 908 return template_url->AdjustedShortNameForLocaleDirection(); 909 } 910 *is_omnibox_api_extension_keyword = false; 911 return base::string16(); 912 } 913 914 void TemplateURLService::OnHistoryURLVisited(const URLVisitedDetails& details) { 915 if (!loaded_) 916 visits_to_add_.push_back(details); 917 else 918 UpdateKeywordSearchTermsForURL(details); 919 } 920 921 void TemplateURLService::Shutdown() { 922 // This check has to be done at Shutdown() instead of in the dtor to ensure 923 // that no clients of KeywordWebDataService are holding ptrs to it after the 924 // first phase of the KeyedService Shutdown() process. 925 if (load_handle_) { 926 DCHECK(web_data_service_.get()); 927 web_data_service_->CancelRequest(load_handle_); 928 } 929 web_data_service_ = NULL; 930 } 931 932 syncer::SyncDataList TemplateURLService::GetAllSyncData( 933 syncer::ModelType type) const { 934 DCHECK_EQ(syncer::SEARCH_ENGINES, type); 935 936 syncer::SyncDataList current_data; 937 for (TemplateURLVector::const_iterator iter = template_urls_.begin(); 938 iter != template_urls_.end(); ++iter) { 939 // We don't sync keywords managed by policy. 940 if ((*iter)->created_by_policy()) 941 continue; 942 // We don't sync extension-controlled search engines. 943 if ((*iter)->GetType() == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION) 944 continue; 945 current_data.push_back(CreateSyncDataFromTemplateURL(**iter)); 946 } 947 948 return current_data; 949 } 950 951 syncer::SyncError TemplateURLService::ProcessSyncChanges( 952 const tracked_objects::Location& from_here, 953 const syncer::SyncChangeList& change_list) { 954 if (!models_associated_) { 955 syncer::SyncError error(FROM_HERE, 956 syncer::SyncError::DATATYPE_ERROR, 957 "Models not yet associated.", 958 syncer::SEARCH_ENGINES); 959 return error; 960 } 961 DCHECK(loaded_); 962 963 base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true); 964 965 // We've started syncing, so set our origin member to the base Sync value. 966 // As we move through Sync Code, we may set this to increasingly specific 967 // origins so we can tell what exactly caused a DSP change. 968 base::AutoReset<DefaultSearchChangeOrigin> change_origin(&dsp_change_origin_, 969 DSP_CHANGE_SYNC_UNINTENTIONAL); 970 971 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); 972 973 syncer::SyncChangeList new_changes; 974 syncer::SyncError error; 975 for (syncer::SyncChangeList::const_iterator iter = change_list.begin(); 976 iter != change_list.end(); ++iter) { 977 DCHECK_EQ(syncer::SEARCH_ENGINES, iter->sync_data().GetDataType()); 978 979 std::string guid = 980 iter->sync_data().GetSpecifics().search_engine().sync_guid(); 981 TemplateURL* existing_turl = GetTemplateURLForGUID(guid); 982 scoped_ptr<TemplateURL> turl(CreateTemplateURLFromTemplateURLAndSyncData( 983 prefs_, search_terms_data(), existing_turl, iter->sync_data(), 984 &new_changes)); 985 if (!turl.get()) 986 continue; 987 988 // Explicitly don't check for conflicts against extension keywords; in this 989 // case the functions which modify the keyword map know how to handle the 990 // conflicts. 991 // TODO(mpcomplete): If we allow editing extension keywords, then those will 992 // need to undergo conflict resolution. 993 TemplateURL* existing_keyword_turl = 994 FindNonExtensionTemplateURLForKeyword(turl->keyword()); 995 if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) { 996 if (!existing_turl) { 997 error = sync_error_factory_->CreateAndUploadError( 998 FROM_HERE, 999 "ProcessSyncChanges failed on ChangeType ACTION_DELETE"); 1000 continue; 1001 } 1002 if (existing_turl == GetDefaultSearchProvider()) { 1003 // The only way Sync can attempt to delete the default search provider 1004 // is if we had changed the kSyncedDefaultSearchProviderGUID 1005 // preference, but perhaps it has not yet been received. To avoid 1006 // situations where this has come in erroneously, we will un-delete 1007 // the current default search from the Sync data. If the pref really 1008 // does arrive later, then default search will change to the correct 1009 // entry, but we'll have this extra entry sitting around. The result is 1010 // not ideal, but it prevents a far more severe bug where the default is 1011 // unexpectedly swapped to something else. The user can safely delete 1012 // the extra entry again later, if they choose. Most users who do not 1013 // look at the search engines UI will not notice this. 1014 // Note that we append a special character to the end of the keyword in 1015 // an attempt to avoid a ping-poinging situation where receiving clients 1016 // may try to continually delete the resurrected entry. 1017 base::string16 updated_keyword = UniquifyKeyword(*existing_turl, true); 1018 TemplateURLData data(existing_turl->data()); 1019 data.SetKeyword(updated_keyword); 1020 TemplateURL new_turl(data); 1021 if (UpdateNoNotify(existing_turl, new_turl)) 1022 NotifyObservers(); 1023 1024 syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(new_turl); 1025 new_changes.push_back(syncer::SyncChange(FROM_HERE, 1026 syncer::SyncChange::ACTION_ADD, 1027 sync_data)); 1028 // Ignore the delete attempt. This means we never end up resetting the 1029 // default search provider due to an ACTION_DELETE from sync. 1030 continue; 1031 } 1032 1033 Remove(existing_turl); 1034 } else if (iter->change_type() == syncer::SyncChange::ACTION_ADD) { 1035 if (existing_turl) { 1036 error = sync_error_factory_->CreateAndUploadError( 1037 FROM_HERE, 1038 "ProcessSyncChanges failed on ChangeType ACTION_ADD"); 1039 continue; 1040 } 1041 const std::string guid = turl->sync_guid(); 1042 if (existing_keyword_turl) { 1043 // Resolve any conflicts so we can safely add the new entry. 1044 ResolveSyncKeywordConflict(turl.get(), existing_keyword_turl, 1045 &new_changes); 1046 } 1047 base::AutoReset<DefaultSearchChangeOrigin> change_origin( 1048 &dsp_change_origin_, DSP_CHANGE_SYNC_ADD); 1049 // Force the local ID to kInvalidTemplateURLID so we can add it. 1050 TemplateURLData data(turl->data()); 1051 data.id = kInvalidTemplateURLID; 1052 TemplateURL* added = new TemplateURL(data); 1053 if (Add(added)) 1054 MaybeUpdateDSEAfterSync(added); 1055 } else if (iter->change_type() == syncer::SyncChange::ACTION_UPDATE) { 1056 if (!existing_turl) { 1057 error = sync_error_factory_->CreateAndUploadError( 1058 FROM_HERE, 1059 "ProcessSyncChanges failed on ChangeType ACTION_UPDATE"); 1060 continue; 1061 } 1062 if (existing_keyword_turl && (existing_keyword_turl != existing_turl)) { 1063 // Resolve any conflicts with other entries so we can safely update the 1064 // keyword. 1065 ResolveSyncKeywordConflict(turl.get(), existing_keyword_turl, 1066 &new_changes); 1067 } 1068 if (UpdateNoNotify(existing_turl, *turl)) { 1069 NotifyObservers(); 1070 MaybeUpdateDSEAfterSync(existing_turl); 1071 } 1072 } else { 1073 // We've unexpectedly received an ACTION_INVALID. 1074 error = sync_error_factory_->CreateAndUploadError( 1075 FROM_HERE, 1076 "ProcessSyncChanges received an ACTION_INVALID"); 1077 } 1078 } 1079 1080 // If something went wrong, we want to prematurely exit to avoid pushing 1081 // inconsistent data to Sync. We return the last error we received. 1082 if (error.IsSet()) 1083 return error; 1084 1085 error = sync_processor_->ProcessSyncChanges(from_here, new_changes); 1086 1087 return error; 1088 } 1089 1090 syncer::SyncMergeResult TemplateURLService::MergeDataAndStartSyncing( 1091 syncer::ModelType type, 1092 const syncer::SyncDataList& initial_sync_data, 1093 scoped_ptr<syncer::SyncChangeProcessor> sync_processor, 1094 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) { 1095 DCHECK(loaded_); 1096 DCHECK_EQ(type, syncer::SEARCH_ENGINES); 1097 DCHECK(!sync_processor_.get()); 1098 DCHECK(sync_processor.get()); 1099 DCHECK(sync_error_factory.get()); 1100 syncer::SyncMergeResult merge_result(type); 1101 1102 // Disable sync if we failed to load. 1103 if (load_failed_) { 1104 merge_result.set_error(syncer::SyncError( 1105 FROM_HERE, syncer::SyncError::DATATYPE_ERROR, 1106 "Local database load failed.", syncer::SEARCH_ENGINES)); 1107 return merge_result; 1108 } 1109 1110 sync_processor_ = sync_processor.Pass(); 1111 sync_error_factory_ = sync_error_factory.Pass(); 1112 1113 // We do a lot of calls to Add/Remove/ResetTemplateURL here, so ensure we 1114 // don't step on our own toes. 1115 base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true); 1116 1117 // We've started syncing, so set our origin member to the base Sync value. 1118 // As we move through Sync Code, we may set this to increasingly specific 1119 // origins so we can tell what exactly caused a DSP change. 1120 base::AutoReset<DefaultSearchChangeOrigin> change_origin(&dsp_change_origin_, 1121 DSP_CHANGE_SYNC_UNINTENTIONAL); 1122 1123 syncer::SyncChangeList new_changes; 1124 1125 // Build maps of our sync GUIDs to syncer::SyncData. 1126 SyncDataMap local_data_map = CreateGUIDToSyncDataMap( 1127 GetAllSyncData(syncer::SEARCH_ENGINES)); 1128 SyncDataMap sync_data_map = CreateGUIDToSyncDataMap(initial_sync_data); 1129 1130 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); 1131 1132 merge_result.set_num_items_before_association(local_data_map.size()); 1133 for (SyncDataMap::const_iterator iter = sync_data_map.begin(); 1134 iter != sync_data_map.end(); ++iter) { 1135 TemplateURL* local_turl = GetTemplateURLForGUID(iter->first); 1136 scoped_ptr<TemplateURL> sync_turl( 1137 CreateTemplateURLFromTemplateURLAndSyncData( 1138 prefs_, search_terms_data(), local_turl, iter->second, 1139 &new_changes)); 1140 if (!sync_turl.get()) 1141 continue; 1142 1143 if (pre_sync_deletes_.find(sync_turl->sync_guid()) != 1144 pre_sync_deletes_.end()) { 1145 // This entry was deleted before the initial sync began (possibly through 1146 // preprocessing in TemplateURLService's loading code). Ignore it and send 1147 // an ACTION_DELETE up to the server. 1148 new_changes.push_back( 1149 syncer::SyncChange(FROM_HERE, 1150 syncer::SyncChange::ACTION_DELETE, 1151 iter->second)); 1152 UMA_HISTOGRAM_ENUMERATION(kDeleteSyncedEngineHistogramName, 1153 DELETE_ENGINE_PRE_SYNC, DELETE_ENGINE_MAX); 1154 continue; 1155 } 1156 1157 if (local_turl) { 1158 DCHECK(IsFromSync(local_turl, sync_data_map)); 1159 // This local search engine is already synced. If the timestamp differs 1160 // from Sync, we need to update locally or to the cloud. Note that if the 1161 // timestamps are equal, we touch neither. 1162 if (sync_turl->last_modified() > local_turl->last_modified()) { 1163 // We've received an update from Sync. We should replace all synced 1164 // fields in the local TemplateURL. Note that this includes the 1165 // TemplateURLID and the TemplateURL may have to be reparsed. This 1166 // also makes the local data's last_modified timestamp equal to Sync's, 1167 // avoiding an Update on the next MergeData call. 1168 if (UpdateNoNotify(local_turl, *sync_turl)) 1169 NotifyObservers(); 1170 merge_result.set_num_items_modified( 1171 merge_result.num_items_modified() + 1); 1172 } else if (sync_turl->last_modified() < local_turl->last_modified()) { 1173 // Otherwise, we know we have newer data, so update Sync with our 1174 // data fields. 1175 new_changes.push_back( 1176 syncer::SyncChange(FROM_HERE, 1177 syncer::SyncChange::ACTION_UPDATE, 1178 local_data_map[local_turl->sync_guid()])); 1179 } 1180 local_data_map.erase(iter->first); 1181 } else { 1182 // The search engine from the cloud has not been synced locally. Merge it 1183 // into our local model. This will handle any conflicts with local (and 1184 // already-synced) TemplateURLs. It will prefer to keep entries from Sync 1185 // over not-yet-synced TemplateURLs. 1186 MergeInSyncTemplateURL(sync_turl.get(), sync_data_map, &new_changes, 1187 &local_data_map, &merge_result); 1188 } 1189 } 1190 1191 // The remaining SyncData in local_data_map should be everything that needs to 1192 // be pushed as ADDs to sync. 1193 for (SyncDataMap::const_iterator iter = local_data_map.begin(); 1194 iter != local_data_map.end(); ++iter) { 1195 new_changes.push_back( 1196 syncer::SyncChange(FROM_HERE, 1197 syncer::SyncChange::ACTION_ADD, 1198 iter->second)); 1199 } 1200 1201 // Do some post-processing on the change list to ensure that we are sending 1202 // valid changes to sync_processor_. 1203 PruneSyncChanges(&sync_data_map, &new_changes); 1204 1205 LogDuplicatesHistogram(GetTemplateURLs()); 1206 merge_result.set_num_items_after_association( 1207 GetAllSyncData(syncer::SEARCH_ENGINES).size()); 1208 merge_result.set_error( 1209 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes)); 1210 if (merge_result.error().IsSet()) 1211 return merge_result; 1212 1213 // The ACTION_DELETEs from this set are processed. Empty it so we don't try to 1214 // reuse them on the next call to MergeDataAndStartSyncing. 1215 pre_sync_deletes_.clear(); 1216 1217 models_associated_ = true; 1218 return merge_result; 1219 } 1220 1221 void TemplateURLService::StopSyncing(syncer::ModelType type) { 1222 DCHECK_EQ(type, syncer::SEARCH_ENGINES); 1223 models_associated_ = false; 1224 sync_processor_.reset(); 1225 sync_error_factory_.reset(); 1226 } 1227 1228 void TemplateURLService::ProcessTemplateURLChange( 1229 const tracked_objects::Location& from_here, 1230 const TemplateURL* turl, 1231 syncer::SyncChange::SyncChangeType type) { 1232 DCHECK_NE(type, syncer::SyncChange::ACTION_INVALID); 1233 DCHECK(turl); 1234 1235 if (!models_associated_) 1236 return; // Not syncing. 1237 1238 if (processing_syncer_changes_) 1239 return; // These are changes originating from us. Ignore. 1240 1241 // Avoid syncing keywords managed by policy. 1242 if (turl->created_by_policy()) 1243 return; 1244 1245 // Avoid syncing extension-controlled search engines. 1246 if (turl->GetType() == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION) 1247 return; 1248 1249 syncer::SyncChangeList changes; 1250 1251 syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*turl); 1252 changes.push_back(syncer::SyncChange(from_here, 1253 type, 1254 sync_data)); 1255 1256 sync_processor_->ProcessSyncChanges(FROM_HERE, changes); 1257 } 1258 1259 // static 1260 syncer::SyncData TemplateURLService::CreateSyncDataFromTemplateURL( 1261 const TemplateURL& turl) { 1262 sync_pb::EntitySpecifics specifics; 1263 sync_pb::SearchEngineSpecifics* se_specifics = 1264 specifics.mutable_search_engine(); 1265 se_specifics->set_short_name(base::UTF16ToUTF8(turl.short_name())); 1266 se_specifics->set_keyword(base::UTF16ToUTF8(turl.keyword())); 1267 se_specifics->set_favicon_url(turl.favicon_url().spec()); 1268 se_specifics->set_url(turl.url()); 1269 se_specifics->set_safe_for_autoreplace(turl.safe_for_autoreplace()); 1270 se_specifics->set_originating_url(turl.originating_url().spec()); 1271 se_specifics->set_date_created(turl.date_created().ToInternalValue()); 1272 se_specifics->set_input_encodings(JoinString(turl.input_encodings(), ';')); 1273 se_specifics->set_show_in_default_list(turl.show_in_default_list()); 1274 se_specifics->set_suggestions_url(turl.suggestions_url()); 1275 se_specifics->set_prepopulate_id(turl.prepopulate_id()); 1276 se_specifics->set_instant_url(turl.instant_url()); 1277 if (!turl.image_url().empty()) 1278 se_specifics->set_image_url(turl.image_url()); 1279 se_specifics->set_new_tab_url(turl.new_tab_url()); 1280 if (!turl.search_url_post_params().empty()) 1281 se_specifics->set_search_url_post_params(turl.search_url_post_params()); 1282 if (!turl.suggestions_url_post_params().empty()) { 1283 se_specifics->set_suggestions_url_post_params( 1284 turl.suggestions_url_post_params()); 1285 } 1286 if (!turl.instant_url_post_params().empty()) 1287 se_specifics->set_instant_url_post_params(turl.instant_url_post_params()); 1288 if (!turl.image_url_post_params().empty()) 1289 se_specifics->set_image_url_post_params(turl.image_url_post_params()); 1290 se_specifics->set_last_modified(turl.last_modified().ToInternalValue()); 1291 se_specifics->set_sync_guid(turl.sync_guid()); 1292 for (size_t i = 0; i < turl.alternate_urls().size(); ++i) 1293 se_specifics->add_alternate_urls(turl.alternate_urls()[i]); 1294 se_specifics->set_search_terms_replacement_key( 1295 turl.search_terms_replacement_key()); 1296 1297 return syncer::SyncData::CreateLocalData(se_specifics->sync_guid(), 1298 se_specifics->keyword(), 1299 specifics); 1300 } 1301 1302 // static 1303 TemplateURL* TemplateURLService::CreateTemplateURLFromTemplateURLAndSyncData( 1304 PrefService* prefs, 1305 const SearchTermsData& search_terms_data, 1306 TemplateURL* existing_turl, 1307 const syncer::SyncData& sync_data, 1308 syncer::SyncChangeList* change_list) { 1309 DCHECK(change_list); 1310 1311 sync_pb::SearchEngineSpecifics specifics = 1312 sync_data.GetSpecifics().search_engine(); 1313 1314 // Past bugs might have caused either of these fields to be empty. Just 1315 // delete this data off the server. 1316 if (specifics.url().empty() || specifics.sync_guid().empty()) { 1317 change_list->push_back( 1318 syncer::SyncChange(FROM_HERE, 1319 syncer::SyncChange::ACTION_DELETE, 1320 sync_data)); 1321 UMA_HISTOGRAM_ENUMERATION(kDeleteSyncedEngineHistogramName, 1322 DELETE_ENGINE_EMPTY_FIELD, DELETE_ENGINE_MAX); 1323 return NULL; 1324 } 1325 1326 TemplateURLData data(existing_turl ? 1327 existing_turl->data() : TemplateURLData()); 1328 data.short_name = base::UTF8ToUTF16(specifics.short_name()); 1329 data.originating_url = GURL(specifics.originating_url()); 1330 base::string16 keyword(base::UTF8ToUTF16(specifics.keyword())); 1331 // NOTE: Once this code has shipped in a couple of stable releases, we can 1332 // probably remove the migration portion, comment out the 1333 // "autogenerate_keyword" field entirely in the .proto file, and fold the 1334 // empty keyword case into the "delete data" block above. 1335 bool reset_keyword = 1336 specifics.autogenerate_keyword() || specifics.keyword().empty(); 1337 if (reset_keyword) 1338 keyword = base::ASCIIToUTF16("dummy"); // Will be replaced below. 1339 DCHECK(!keyword.empty()); 1340 data.SetKeyword(keyword); 1341 data.SetURL(specifics.url()); 1342 data.suggestions_url = specifics.suggestions_url(); 1343 data.instant_url = specifics.instant_url(); 1344 data.image_url = specifics.image_url(); 1345 data.new_tab_url = specifics.new_tab_url(); 1346 data.search_url_post_params = specifics.search_url_post_params(); 1347 data.suggestions_url_post_params = specifics.suggestions_url_post_params(); 1348 data.instant_url_post_params = specifics.instant_url_post_params(); 1349 data.image_url_post_params = specifics.image_url_post_params(); 1350 data.favicon_url = GURL(specifics.favicon_url()); 1351 data.show_in_default_list = specifics.show_in_default_list(); 1352 data.safe_for_autoreplace = specifics.safe_for_autoreplace(); 1353 base::SplitString(specifics.input_encodings(), ';', &data.input_encodings); 1354 // If the server data has duplicate encodings, we'll want to push an update 1355 // below to correct it. Note that we also fix this in 1356 // GetSearchProvidersUsingKeywordResult(), since otherwise we'd never correct 1357 // local problems for clients which have disabled search engine sync. 1358 bool deduped = DeDupeEncodings(&data.input_encodings); 1359 data.date_created = base::Time::FromInternalValue(specifics.date_created()); 1360 data.last_modified = base::Time::FromInternalValue(specifics.last_modified()); 1361 data.prepopulate_id = specifics.prepopulate_id(); 1362 data.sync_guid = specifics.sync_guid(); 1363 data.alternate_urls.clear(); 1364 for (int i = 0; i < specifics.alternate_urls_size(); ++i) 1365 data.alternate_urls.push_back(specifics.alternate_urls(i)); 1366 data.search_terms_replacement_key = specifics.search_terms_replacement_key(); 1367 1368 TemplateURL* turl = new TemplateURL(data); 1369 // If this TemplateURL matches a built-in prepopulated template URL, it's 1370 // possible that sync is trying to modify fields that should not be touched. 1371 // Revert these fields to the built-in values. 1372 UpdateTemplateURLIfPrepopulated(turl, prefs); 1373 DCHECK_NE(TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION, turl->GetType()); 1374 if (reset_keyword || deduped) { 1375 if (reset_keyword) 1376 turl->ResetKeywordIfNecessary(search_terms_data, true); 1377 syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*turl); 1378 change_list->push_back(syncer::SyncChange(FROM_HERE, 1379 syncer::SyncChange::ACTION_UPDATE, 1380 sync_data)); 1381 } else if (turl->IsGoogleSearchURLWithReplaceableKeyword(search_terms_data)) { 1382 if (!existing_turl) { 1383 // We're adding a new TemplateURL that uses the Google base URL, so set 1384 // its keyword appropriately for the local environment. 1385 turl->ResetKeywordIfNecessary(search_terms_data, false); 1386 } else if (existing_turl->IsGoogleSearchURLWithReplaceableKeyword( 1387 search_terms_data)) { 1388 // Ignore keyword changes triggered by the Google base URL changing on 1389 // another client. If the base URL changes in this client as well, we'll 1390 // pick that up separately at the appropriate time. Otherwise, changing 1391 // the keyword here could result in having the wrong keyword for the local 1392 // environment. 1393 turl->data_.SetKeyword(existing_turl->keyword()); 1394 } 1395 } 1396 1397 return turl; 1398 } 1399 1400 // static 1401 SyncDataMap TemplateURLService::CreateGUIDToSyncDataMap( 1402 const syncer::SyncDataList& sync_data) { 1403 SyncDataMap data_map; 1404 for (syncer::SyncDataList::const_iterator i(sync_data.begin()); 1405 i != sync_data.end(); 1406 ++i) 1407 data_map[i->GetSpecifics().search_engine().sync_guid()] = *i; 1408 return data_map; 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::GoogleBaseURLChanged, 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_.get()) 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 if (client_) { 1743 client_->SetKeywordSearchTermsForURL( 1744 details.url, (*i)->id(), search_terms); 1745 } 1746 } 1747 } 1748 } 1749 1750 void TemplateURLService::AddTabToSearchVisit(const TemplateURL& t_url) { 1751 // Only add visits for entries the user hasn't modified. If the user modified 1752 // the entry the keyword may no longer correspond to the host name. It may be 1753 // possible to do something more sophisticated here, but it's so rare as to 1754 // not be worth it. 1755 if (!t_url.safe_for_autoreplace()) 1756 return; 1757 1758 if (!client_) 1759 return; 1760 1761 GURL url( 1762 url_fixer::FixupURL(base::UTF16ToUTF8(t_url.keyword()), std::string())); 1763 if (!url.is_valid()) 1764 return; 1765 1766 // Synthesize a visit for the keyword. This ensures the url for the keyword is 1767 // autocompleted even if the user doesn't type the url in directly. 1768 client_->AddKeywordGeneratedVisit(url); 1769 } 1770 1771 void TemplateURLService::RequestGoogleURLTrackerServerCheckIfNecessary() { 1772 if (default_search_provider_ && 1773 default_search_provider_->HasGoogleBaseURLs(search_terms_data()) && 1774 google_url_tracker_) 1775 google_url_tracker_->RequestServerCheck(false); 1776 } 1777 1778 void TemplateURLService::GoogleBaseURLChanged() { 1779 if (!loaded_) 1780 return; 1781 1782 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); 1783 bool something_changed = false; 1784 for (TemplateURLVector::iterator i(template_urls_.begin()); 1785 i != template_urls_.end(); ++i) { 1786 TemplateURL* t_url = *i; 1787 if (t_url->HasGoogleBaseURLs(search_terms_data())) { 1788 TemplateURL updated_turl(t_url->data()); 1789 updated_turl.ResetKeywordIfNecessary(search_terms_data(), false); 1790 KeywordToTemplateMap::const_iterator existing_entry = 1791 keyword_to_template_map_.find(updated_turl.keyword()); 1792 if ((existing_entry != keyword_to_template_map_.end()) && 1793 (existing_entry->second != t_url)) { 1794 // The new autogenerated keyword conflicts with another TemplateURL. 1795 // Overwrite it if it's replaceable; otherwise, leave |t_url| using its 1796 // current keyword. (This will not prevent |t_url| from auto-updating 1797 // the keyword in the future if the conflicting TemplateURL disappears.) 1798 // Note that we must still update |t_url| in this case, or the 1799 // |provider_map_| will not be updated correctly. 1800 if (CanReplace(existing_entry->second)) 1801 RemoveNoNotify(existing_entry->second); 1802 else 1803 updated_turl.data_.SetKeyword(t_url->keyword()); 1804 } 1805 something_changed = true; 1806 // This will send the keyword change to sync. Note that other clients 1807 // need to reset the keyword to an appropriate local value when this 1808 // change arrives; see CreateTemplateURLFromTemplateURLAndSyncData(). 1809 UpdateNoNotify(t_url, updated_turl); 1810 } 1811 } 1812 if (something_changed) 1813 NotifyObservers(); 1814 } 1815 1816 void TemplateURLService::OnDefaultSearchChange( 1817 const TemplateURLData* data, 1818 DefaultSearchManager::Source source) { 1819 if (prefs_ && (source == DefaultSearchManager::FROM_USER) && 1820 ((source != default_search_provider_source_) || 1821 !IdenticalSyncGUIDs(data, GetDefaultSearchProvider()))) { 1822 prefs_->SetString(prefs::kSyncedDefaultSearchProviderGUID, data->sync_guid); 1823 } 1824 ApplyDefaultSearchChange(data, source); 1825 } 1826 1827 void TemplateURLService::ApplyDefaultSearchChange( 1828 const TemplateURLData* data, 1829 DefaultSearchManager::Source source) { 1830 if (!ApplyDefaultSearchChangeNoMetrics(data, source)) 1831 return; 1832 1833 UMA_HISTOGRAM_ENUMERATION( 1834 "Search.DefaultSearchChangeOrigin", dsp_change_origin_, DSP_CHANGE_MAX); 1835 1836 if (GetDefaultSearchProvider() && 1837 GetDefaultSearchProvider()->HasGoogleBaseURLs(search_terms_data()) && 1838 !dsp_change_callback_.is_null()) 1839 dsp_change_callback_.Run(); 1840 } 1841 1842 bool TemplateURLService::ApplyDefaultSearchChangeNoMetrics( 1843 const TemplateURLData* data, 1844 DefaultSearchManager::Source source) { 1845 if (!loaded_) { 1846 // Set |initial_default_search_provider_| from the preferences. This is 1847 // mainly so we can hold ownership until we get to the point where the list 1848 // of keywords from Web Data is the owner of everything including the 1849 // default. 1850 bool changed = TemplateURL::MatchesData( 1851 initial_default_search_provider_.get(), data, search_terms_data()); 1852 initial_default_search_provider_.reset( 1853 data ? new TemplateURL(*data) : NULL); 1854 default_search_provider_source_ = source; 1855 return changed; 1856 } 1857 1858 // Prevent recursion if we update the value stored in default_search_manager_. 1859 // Note that we exclude the case of data == NULL because that could cause a 1860 // false positive for recursion when the initial_default_search_provider_ is 1861 // NULL due to policy. We'll never actually get recursion with data == NULL. 1862 if (source == default_search_provider_source_ && data != NULL && 1863 TemplateURL::MatchesData(default_search_provider_, data, 1864 search_terms_data())) 1865 return false; 1866 1867 // This may be deleted later. Use exclusively for pointer comparison to detect 1868 // a change. 1869 TemplateURL* previous_default_search_engine = default_search_provider_; 1870 1871 KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get()); 1872 if (default_search_provider_source_ == DefaultSearchManager::FROM_POLICY || 1873 source == DefaultSearchManager::FROM_POLICY) { 1874 // We do this both to remove any no-longer-applicable policy-defined DSE as 1875 // well as to add the new one, if appropriate. 1876 UpdateProvidersCreatedByPolicy( 1877 &template_urls_, 1878 source == DefaultSearchManager::FROM_POLICY ? data : NULL); 1879 } 1880 1881 if (!data) { 1882 default_search_provider_ = NULL; 1883 } else if (source == DefaultSearchManager::FROM_EXTENSION) { 1884 default_search_provider_ = FindMatchingExtensionTemplateURL( 1885 *data, TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION); 1886 } else if (source == DefaultSearchManager::FROM_FALLBACK) { 1887 default_search_provider_ = 1888 FindPrepopulatedTemplateURL(data->prepopulate_id); 1889 if (default_search_provider_) { 1890 TemplateURLData update_data(*data); 1891 update_data.sync_guid = default_search_provider_->sync_guid(); 1892 if (!default_search_provider_->safe_for_autoreplace()) { 1893 update_data.safe_for_autoreplace = false; 1894 update_data.SetKeyword(default_search_provider_->keyword()); 1895 update_data.short_name = default_search_provider_->short_name(); 1896 } 1897 UpdateNoNotify(default_search_provider_, TemplateURL(update_data)); 1898 } else { 1899 // Normally the prepopulated fallback should be present in 1900 // |template_urls_|, but in a few cases it might not be: 1901 // (1) Tests that initialize the TemplateURLService in peculiar ways. 1902 // (2) If the user deleted the pre-populated default and we subsequently 1903 // lost their user-selected value. 1904 TemplateURL* new_dse = new TemplateURL(*data); 1905 if (AddNoNotify(new_dse, true)) 1906 default_search_provider_ = new_dse; 1907 } 1908 } else if (source == DefaultSearchManager::FROM_USER) { 1909 default_search_provider_ = GetTemplateURLForGUID(data->sync_guid); 1910 if (!default_search_provider_ && data->prepopulate_id) { 1911 default_search_provider_ = 1912 FindPrepopulatedTemplateURL(data->prepopulate_id); 1913 } 1914 TemplateURLData new_data(*data); 1915 new_data.show_in_default_list = true; 1916 if (default_search_provider_) { 1917 UpdateNoNotify(default_search_provider_, TemplateURL(new_data)); 1918 } else { 1919 new_data.id = kInvalidTemplateURLID; 1920 TemplateURL* new_dse = new TemplateURL(new_data); 1921 if (AddNoNotify(new_dse, true)) 1922 default_search_provider_ = new_dse; 1923 } 1924 if (default_search_provider_ && prefs_) { 1925 prefs_->SetString(prefs::kSyncedDefaultSearchProviderGUID, 1926 default_search_provider_->sync_guid()); 1927 } 1928 1929 } 1930 1931 default_search_provider_source_ = source; 1932 1933 bool changed = default_search_provider_ != previous_default_search_engine; 1934 if (changed) 1935 RequestGoogleURLTrackerServerCheckIfNecessary(); 1936 1937 NotifyObservers(); 1938 1939 return changed; 1940 } 1941 1942 bool TemplateURLService::AddNoNotify(TemplateURL* template_url, 1943 bool newly_adding) { 1944 DCHECK(template_url); 1945 1946 if (newly_adding) { 1947 DCHECK_EQ(kInvalidTemplateURLID, template_url->id()); 1948 DCHECK(std::find(template_urls_.begin(), template_urls_.end(), 1949 template_url) == template_urls_.end()); 1950 template_url->data_.id = ++next_id_; 1951 } 1952 1953 template_url->ResetKeywordIfNecessary(search_terms_data(), false); 1954 // Check whether |template_url|'s keyword conflicts with any already in the 1955 // model. 1956 TemplateURL* existing_keyword_turl = 1957 GetTemplateURLForKeyword(template_url->keyword()); 1958 1959 // Check whether |template_url|'s keyword conflicts with any already in the 1960 // model. Note that we can reach here during the loading phase while 1961 // processing the template URLs from the web data service. In this case, 1962 // GetTemplateURLForKeyword() will look not only at what's already in the 1963 // model, but at the |initial_default_search_provider_|. Since this engine 1964 // will presumably also be present in the web data, we need to double-check 1965 // that any "pre-existing" entries we find are actually coming from 1966 // |template_urls_|, lest we detect a "conflict" between the 1967 // |initial_default_search_provider_| and the web data version of itself. 1968 if (existing_keyword_turl && 1969 (std::find(template_urls_.begin(), template_urls_.end(), 1970 existing_keyword_turl) != template_urls_.end())) { 1971 DCHECK_NE(existing_keyword_turl, template_url); 1972 // Only replace one of the TemplateURLs if they are either both extensions, 1973 // or both not extensions. 1974 bool are_same_type = existing_keyword_turl->GetType() == 1975 template_url->GetType(); 1976 if (CanReplace(existing_keyword_turl) && are_same_type) { 1977 RemoveNoNotify(existing_keyword_turl); 1978 } else if (CanReplace(template_url) && are_same_type) { 1979 delete template_url; 1980 return false; 1981 } else { 1982 base::string16 new_keyword = 1983 UniquifyKeyword(*existing_keyword_turl, false); 1984 ResetTemplateURLNoNotify(existing_keyword_turl, 1985 existing_keyword_turl->short_name(), new_keyword, 1986 existing_keyword_turl->url()); 1987 } 1988 } 1989 template_urls_.push_back(template_url); 1990 AddToMaps(template_url); 1991 1992 if (newly_adding && 1993 (template_url->GetType() != 1994 TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION)) { 1995 if (web_data_service_.get()) 1996 web_data_service_->AddKeyword(template_url->data()); 1997 1998 // Inform sync of the addition. Note that this will assign a GUID to 1999 // template_url and add it to the guid_to_template_map_. 2000 ProcessTemplateURLChange(FROM_HERE, 2001 template_url, 2002 syncer::SyncChange::ACTION_ADD); 2003 } 2004 2005 return true; 2006 } 2007 2008 void TemplateURLService::RemoveNoNotify(TemplateURL* template_url) { 2009 DCHECK(template_url != default_search_provider_); 2010 2011 TemplateURLVector::iterator i = 2012 std::find(template_urls_.begin(), template_urls_.end(), template_url); 2013 if (i == template_urls_.end()) 2014 return; 2015 2016 RemoveFromMaps(template_url); 2017 2018 // Remove it from the vector containing all TemplateURLs. 2019 template_urls_.erase(i); 2020 2021 if (template_url->GetType() != TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION) { 2022 if (web_data_service_.get()) 2023 web_data_service_->RemoveKeyword(template_url->id()); 2024 2025 // Inform sync of the deletion. 2026 ProcessTemplateURLChange(FROM_HERE, 2027 template_url, 2028 syncer::SyncChange::ACTION_DELETE); 2029 2030 UMA_HISTOGRAM_ENUMERATION(kDeleteSyncedEngineHistogramName, 2031 DELETE_ENGINE_USER_ACTION, DELETE_ENGINE_MAX); 2032 } 2033 2034 if (loaded_ && client_) 2035 client_->DeleteAllSearchTermsForKeyword(template_url->id()); 2036 2037 // We own the TemplateURL and need to delete it. 2038 delete template_url; 2039 } 2040 2041 bool TemplateURLService::ResetTemplateURLNoNotify( 2042 TemplateURL* url, 2043 const base::string16& title, 2044 const base::string16& keyword, 2045 const std::string& search_url) { 2046 DCHECK(!keyword.empty()); 2047 DCHECK(!search_url.empty()); 2048 TemplateURLData data(url->data()); 2049 data.short_name = title; 2050 data.SetKeyword(keyword); 2051 if (search_url != data.url()) { 2052 data.SetURL(search_url); 2053 // The urls have changed, reset the favicon url. 2054 data.favicon_url = GURL(); 2055 } 2056 data.safe_for_autoreplace = false; 2057 data.last_modified = time_provider_(); 2058 return UpdateNoNotify(url, TemplateURL(data)); 2059 } 2060 2061 void TemplateURLService::NotifyObservers() { 2062 if (!loaded_) 2063 return; 2064 2065 FOR_EACH_OBSERVER(TemplateURLServiceObserver, model_observers_, 2066 OnTemplateURLServiceChanged()); 2067 } 2068 2069 // |template_urls| are the TemplateURLs loaded from the database. 2070 // |default_from_prefs| is the default search provider from the preferences, or 2071 // NULL if the DSE is not policy-defined. 2072 // 2073 // This function removes from the vector and the database all the TemplateURLs 2074 // that were set by policy, unless it is the current default search provider, in 2075 // which case it is updated with the data from prefs. 2076 void TemplateURLService::UpdateProvidersCreatedByPolicy( 2077 TemplateURLVector* template_urls, 2078 const TemplateURLData* default_from_prefs) { 2079 DCHECK(template_urls); 2080 2081 for (TemplateURLVector::iterator i = template_urls->begin(); 2082 i != template_urls->end(); ) { 2083 TemplateURL* template_url = *i; 2084 if (template_url->created_by_policy()) { 2085 if (default_from_prefs && 2086 TemplateURL::MatchesData(template_url, default_from_prefs, 2087 search_terms_data())) { 2088 // If the database specified a default search provider that was set 2089 // by policy, and the default search provider from the preferences 2090 // is also set by policy and they are the same, keep the entry in the 2091 // database and the |default_search_provider|. 2092 default_search_provider_ = template_url; 2093 // Prevent us from saving any other entries, or creating a new one. 2094 default_from_prefs = NULL; 2095 ++i; 2096 continue; 2097 } 2098 2099 RemoveFromMaps(template_url); 2100 i = template_urls->erase(i); 2101 if (web_data_service_.get()) 2102 web_data_service_->RemoveKeyword(template_url->id()); 2103 delete template_url; 2104 } else { 2105 ++i; 2106 } 2107 } 2108 2109 if (default_from_prefs) { 2110 default_search_provider_ = NULL; 2111 default_search_provider_source_ = DefaultSearchManager::FROM_POLICY; 2112 TemplateURLData new_data(*default_from_prefs); 2113 if (new_data.sync_guid.empty()) 2114 new_data.sync_guid = base::GenerateGUID(); 2115 new_data.created_by_policy = true; 2116 TemplateURL* new_dse = new TemplateURL(new_data); 2117 if (AddNoNotify(new_dse, true)) 2118 default_search_provider_ = new_dse; 2119 } 2120 } 2121 2122 void TemplateURLService::ResetTemplateURLGUID(TemplateURL* url, 2123 const std::string& guid) { 2124 DCHECK(loaded_); 2125 DCHECK(!guid.empty()); 2126 2127 TemplateURLData data(url->data()); 2128 data.sync_guid = guid; 2129 UpdateNoNotify(url, TemplateURL(data)); 2130 } 2131 2132 base::string16 TemplateURLService::UniquifyKeyword(const TemplateURL& turl, 2133 bool force) { 2134 if (!force) { 2135 // Already unique. 2136 if (!GetTemplateURLForKeyword(turl.keyword())) 2137 return turl.keyword(); 2138 2139 // First, try to return the generated keyword for the TemplateURL (except 2140 // for extensions, as their keywords are not associated with their URLs). 2141 GURL gurl(turl.url()); 2142 if (gurl.is_valid() && 2143 (turl.GetType() != TemplateURL::OMNIBOX_API_EXTENSION)) { 2144 base::string16 keyword_candidate = TemplateURL::GenerateKeyword(gurl); 2145 if (!GetTemplateURLForKeyword(keyword_candidate)) 2146 return keyword_candidate; 2147 } 2148 } 2149 2150 // We try to uniquify the keyword by appending a special character to the end. 2151 // This is a best-effort approach where we try to preserve the original 2152 // keyword and let the user do what they will after our attempt. 2153 base::string16 keyword_candidate(turl.keyword()); 2154 do { 2155 keyword_candidate.append(base::ASCIIToUTF16("_")); 2156 } while (GetTemplateURLForKeyword(keyword_candidate)); 2157 2158 return keyword_candidate; 2159 } 2160 2161 bool TemplateURLService::IsLocalTemplateURLBetter( 2162 const TemplateURL* local_turl, 2163 const TemplateURL* sync_turl) { 2164 DCHECK(GetTemplateURLForGUID(local_turl->sync_guid())); 2165 return local_turl->last_modified() > sync_turl->last_modified() || 2166 local_turl->created_by_policy() || 2167 local_turl== GetDefaultSearchProvider(); 2168 } 2169 2170 void TemplateURLService::ResolveSyncKeywordConflict( 2171 TemplateURL* unapplied_sync_turl, 2172 TemplateURL* applied_sync_turl, 2173 syncer::SyncChangeList* change_list) { 2174 DCHECK(loaded_); 2175 DCHECK(unapplied_sync_turl); 2176 DCHECK(applied_sync_turl); 2177 DCHECK(change_list); 2178 DCHECK_EQ(applied_sync_turl->keyword(), unapplied_sync_turl->keyword()); 2179 DCHECK_NE(TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION, 2180 applied_sync_turl->GetType()); 2181 2182 // Both |unapplied_sync_turl| and |applied_sync_turl| are known to Sync, so 2183 // don't delete either of them. Instead, determine which is "better" and 2184 // uniquify the other one, sending an update to the server for the updated 2185 // entry. 2186 const bool applied_turl_is_better = 2187 IsLocalTemplateURLBetter(applied_sync_turl, unapplied_sync_turl); 2188 TemplateURL* loser = applied_turl_is_better ? 2189 unapplied_sync_turl : applied_sync_turl; 2190 base::string16 new_keyword = UniquifyKeyword(*loser, false); 2191 DCHECK(!GetTemplateURLForKeyword(new_keyword)); 2192 if (applied_turl_is_better) { 2193 // Just set the keyword of |unapplied_sync_turl|. The caller is responsible 2194 // for adding or updating unapplied_sync_turl in the local model. 2195 unapplied_sync_turl->data_.SetKeyword(new_keyword); 2196 } else { 2197 // Update |applied_sync_turl| in the local model with the new keyword. 2198 TemplateURLData data(applied_sync_turl->data()); 2199 data.SetKeyword(new_keyword); 2200 if (UpdateNoNotify(applied_sync_turl, TemplateURL(data))) 2201 NotifyObservers(); 2202 } 2203 // The losing TemplateURL should have their keyword updated. Send a change to 2204 // the server to reflect this change. 2205 syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*loser); 2206 change_list->push_back(syncer::SyncChange(FROM_HERE, 2207 syncer::SyncChange::ACTION_UPDATE, 2208 sync_data)); 2209 } 2210 2211 void TemplateURLService::MergeInSyncTemplateURL( 2212 TemplateURL* sync_turl, 2213 const SyncDataMap& sync_data, 2214 syncer::SyncChangeList* change_list, 2215 SyncDataMap* local_data, 2216 syncer::SyncMergeResult* merge_result) { 2217 DCHECK(sync_turl); 2218 DCHECK(!GetTemplateURLForGUID(sync_turl->sync_guid())); 2219 DCHECK(IsFromSync(sync_turl, sync_data)); 2220 2221 TemplateURL* conflicting_turl = 2222 FindNonExtensionTemplateURLForKeyword(sync_turl->keyword()); 2223 bool should_add_sync_turl = true; 2224 2225 // If there was no TemplateURL in the local model that conflicts with 2226 // |sync_turl|, skip the following preparation steps and just add |sync_turl| 2227 // directly. Otherwise, modify |conflicting_turl| to make room for 2228 // |sync_turl|. 2229 if (conflicting_turl) { 2230 if (IsFromSync(conflicting_turl, sync_data)) { 2231 // |conflicting_turl| is already known to Sync, so we're not allowed to 2232 // remove it. In this case, we want to uniquify the worse one and send an 2233 // update for the changed keyword to sync. We can reuse the logic from 2234 // ResolveSyncKeywordConflict for this. 2235 ResolveSyncKeywordConflict(sync_turl, conflicting_turl, change_list); 2236 merge_result->set_num_items_modified( 2237 merge_result->num_items_modified() + 1); 2238 } else { 2239 // |conflicting_turl| is not yet known to Sync. If it is better, then we 2240 // want to transfer its values up to sync. Otherwise, we remove it and 2241 // allow the entry from Sync to overtake it in the model. 2242 const std::string guid = conflicting_turl->sync_guid(); 2243 if (IsLocalTemplateURLBetter(conflicting_turl, sync_turl)) { 2244 ResetTemplateURLGUID(conflicting_turl, sync_turl->sync_guid()); 2245 syncer::SyncData sync_data = 2246 CreateSyncDataFromTemplateURL(*conflicting_turl); 2247 change_list->push_back(syncer::SyncChange( 2248 FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data)); 2249 // Note that in this case we do not add the Sync TemplateURL to the 2250 // local model, since we've effectively "merged" it in by updating the 2251 // local conflicting entry with its sync_guid. 2252 should_add_sync_turl = false; 2253 merge_result->set_num_items_modified( 2254 merge_result->num_items_modified() + 1); 2255 } else { 2256 // We guarantee that this isn't the local search provider. Otherwise, 2257 // local would have won. 2258 DCHECK(conflicting_turl != GetDefaultSearchProvider()); 2259 Remove(conflicting_turl); 2260 merge_result->set_num_items_deleted( 2261 merge_result->num_items_deleted() + 1); 2262 } 2263 // This TemplateURL was either removed or overwritten in the local model. 2264 // Remove the entry from the local data so it isn't pushed up to Sync. 2265 local_data->erase(guid); 2266 } 2267 } 2268 2269 if (should_add_sync_turl) { 2270 // Force the local ID to kInvalidTemplateURLID so we can add it. 2271 TemplateURLData data(sync_turl->data()); 2272 data.id = kInvalidTemplateURLID; 2273 TemplateURL* added = new TemplateURL(data); 2274 base::AutoReset<DefaultSearchChangeOrigin> change_origin( 2275 &dsp_change_origin_, DSP_CHANGE_SYNC_ADD); 2276 if (Add(added)) 2277 MaybeUpdateDSEAfterSync(added); 2278 merge_result->set_num_items_added( 2279 merge_result->num_items_added() + 1); 2280 } 2281 } 2282 2283 void TemplateURLService::PatchMissingSyncGUIDs( 2284 TemplateURLVector* template_urls) { 2285 DCHECK(template_urls); 2286 for (TemplateURLVector::iterator i = template_urls->begin(); 2287 i != template_urls->end(); ++i) { 2288 TemplateURL* template_url = *i; 2289 DCHECK(template_url); 2290 if (template_url->sync_guid().empty() && 2291 (template_url->GetType() != 2292 TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION)) { 2293 template_url->data_.sync_guid = base::GenerateGUID(); 2294 if (web_data_service_.get()) 2295 web_data_service_->UpdateKeyword(template_url->data()); 2296 } 2297 } 2298 } 2299 2300 void TemplateURLService::OnSyncedDefaultSearchProviderGUIDChanged() { 2301 base::AutoReset<DefaultSearchChangeOrigin> change_origin( 2302 &dsp_change_origin_, DSP_CHANGE_SYNC_PREF); 2303 2304 std::string new_guid = 2305 prefs_->GetString(prefs::kSyncedDefaultSearchProviderGUID); 2306 if (new_guid.empty()) { 2307 default_search_manager_.ClearUserSelectedDefaultSearchEngine(); 2308 return; 2309 } 2310 2311 TemplateURL* turl = GetTemplateURLForGUID(new_guid); 2312 if (turl) 2313 default_search_manager_.SetUserSelectedDefaultSearchEngine(turl->data()); 2314 } 2315 2316 TemplateURL* TemplateURLService::FindPrepopulatedTemplateURL( 2317 int prepopulated_id) { 2318 for (TemplateURLVector::const_iterator i = template_urls_.begin(); 2319 i != template_urls_.end(); ++i) { 2320 if ((*i)->prepopulate_id() == prepopulated_id) 2321 return *i; 2322 } 2323 return NULL; 2324 } 2325 2326 TemplateURL* TemplateURLService::FindTemplateURLForExtension( 2327 const std::string& extension_id, 2328 TemplateURL::Type type) { 2329 DCHECK_NE(TemplateURL::NORMAL, type); 2330 for (TemplateURLVector::const_iterator i = template_urls_.begin(); 2331 i != template_urls_.end(); ++i) { 2332 if ((*i)->GetType() == type && 2333 (*i)->GetExtensionId() == extension_id) 2334 return *i; 2335 } 2336 return NULL; 2337 } 2338 2339 TemplateURL* TemplateURLService::FindMatchingExtensionTemplateURL( 2340 const TemplateURLData& data, 2341 TemplateURL::Type type) { 2342 DCHECK_NE(TemplateURL::NORMAL, type); 2343 for (TemplateURLVector::const_iterator i = template_urls_.begin(); 2344 i != template_urls_.end(); ++i) { 2345 if ((*i)->GetType() == type && 2346 TemplateURL::MatchesData(*i, &data, search_terms_data())) 2347 return *i; 2348 } 2349 return NULL; 2350 } 2351 2352 void TemplateURLService::UpdateExtensionDefaultSearchEngine() { 2353 TemplateURL* most_recently_intalled_default = NULL; 2354 for (TemplateURLVector::const_iterator i = template_urls_.begin(); 2355 i != template_urls_.end(); ++i) { 2356 if (((*i)->GetType() == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION) && 2357 (*i)->extension_info_->wants_to_be_default_engine && 2358 (*i)->SupportsReplacement(search_terms_data()) && 2359 (!most_recently_intalled_default || 2360 (most_recently_intalled_default->extension_info_->install_time < 2361 (*i)->extension_info_->install_time))) 2362 most_recently_intalled_default = *i; 2363 } 2364 2365 if (most_recently_intalled_default) { 2366 base::AutoReset<DefaultSearchChangeOrigin> change_origin( 2367 &dsp_change_origin_, DSP_CHANGE_OVERRIDE_SETTINGS_EXTENSION); 2368 default_search_manager_.SetExtensionControlledDefaultSearchEngine( 2369 most_recently_intalled_default->data()); 2370 } else { 2371 default_search_manager_.ClearExtensionControlledDefaultSearchEngine(); 2372 } 2373 } 2374