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