Index: chrome/browser/search_engines/template_url_service.cc |
diff --git a/chrome/browser/search_engines/template_url_service.cc b/chrome/browser/search_engines/template_url_service.cc |
index d3dce0ec3bb8f40ff231b46e6a11cc8c27b4b9dc..22d77b70df24c73382a2333ce694b79fc98c00b1 100644 |
--- a/chrome/browser/search_engines/template_url_service.cc |
+++ b/chrome/browser/search_engines/template_url_service.cc |
@@ -194,8 +194,15 @@ std::string OldBaseURLSearchTermsData::GoogleBaseURLValue() const { |
return old_base_url; |
} |
-} // namespace |
+// Returns true if |turl|'s GUID is not found inside |sync_data|. This is to be |
+// used in MergeDataAndStartSyncing to differentiate between TemplateURLs from |
+// Sync and TemplateURLs that were initially local, assuming |sync_data| is the |
+// |initial_sync_data| parameter. |
+bool IsFromSync(const TemplateURL* turl, const SyncDataMap& sync_data) { |
+ return !!sync_data.count(turl->sync_guid()); |
+} |
+} // namespace |
class TemplateURLService::LessWithPrefix { |
public: |
@@ -1012,17 +1019,19 @@ syncer::SyncError TemplateURLService::ProcessSyncChanges( |
LOG(ERROR) << "Trying to add an existing TemplateURL."; |
continue; |
} |
- std::string guid = turl->sync_guid(); |
- if (!existing_keyword_turl || ResolveSyncKeywordConflict(turl.get(), |
- existing_keyword_turl, &new_changes)) { |
- // Force the local ID to kInvalidTemplateURLID so we can add it. |
- TemplateURLData data(turl->data()); |
- data.id = kInvalidTemplateURLID; |
- Add(new TemplateURL(profile_, data)); |
- |
- // Possibly set the newly added |turl| as the default search provider. |
- SetDefaultSearchProviderIfNewlySynced(guid); |
+ const std::string guid = turl->sync_guid(); |
+ if (existing_keyword_turl) { |
+ // Resolve any conflicts so we can safely add the new entry. |
+ ResolveSyncKeywordConflict(turl.get(), existing_keyword_turl, |
+ &new_changes); |
} |
+ // Force the local ID to kInvalidTemplateURLID so we can add it. |
+ TemplateURLData data(turl->data()); |
+ data.id = kInvalidTemplateURLID; |
+ Add(new TemplateURL(profile_, data)); |
+ |
+ // Possibly set the newly added |turl| as the default search provider. |
+ SetDefaultSearchProviderIfNewlySynced(guid); |
} else if (iter->change_type() == syncer::SyncChange::ACTION_UPDATE) { |
if (!existing_turl) { |
NOTREACHED() << "Unexpected sync change state."; |
@@ -1032,16 +1041,11 @@ syncer::SyncError TemplateURLService::ProcessSyncChanges( |
LOG(ERROR) << "Trying to update a non-existent TemplateURL."; |
continue; |
} |
- // Possibly resolve a keyword conflict if they have the same keywords but |
- // are not the same entry. |
- if (existing_keyword_turl && (existing_keyword_turl != existing_turl) && |
- !ResolveSyncKeywordConflict(turl.get(), existing_keyword_turl, |
- &new_changes)) { |
- // Note that because we're processing changes, this Remove() call won't |
- // generate an ACTION_DELETE; but ResolveSyncKeywordConflict() did |
- // already, so we should be OK. |
- Remove(existing_turl); |
- continue; |
+ if (existing_keyword_turl && (existing_keyword_turl != existing_turl)) { |
+ // Resolve any conflicts with other entries so we can safely update the |
+ // keyword. |
+ ResolveSyncKeywordConflict(turl.get(), existing_keyword_turl, |
+ &new_changes); |
} |
UIThreadSearchTermsData search_terms_data(existing_turl->profile()); |
if (UpdateNoNotify(existing_turl, *turl, search_terms_data)) |
@@ -1129,6 +1133,7 @@ syncer::SyncError TemplateURLService::MergeDataAndStartSyncing( |
} |
if (local_turl) { |
+ DCHECK(IsFromSync(local_turl, sync_data_map)); |
// This local search engine is already synced. If the timestamp differs |
// from Sync, we need to update locally or to the cloud. Note that if the |
// timestamps are equal, we touch neither. |
@@ -1151,36 +1156,12 @@ syncer::SyncError TemplateURLService::MergeDataAndStartSyncing( |
} |
local_data_map.erase(iter->first); |
} else { |
- // The search engine from the cloud has not been synced locally, but there |
- // might be a local search engine that is a duplicate that needs to be |
- // merged. |
- TemplateURL* dupe_turl = FindDuplicateOfSyncTemplateURL(*sync_turl); |
- if (dupe_turl) { |
- // Merge duplicates and remove the processed local TURL from the map. |
- std::string old_guid = dupe_turl->sync_guid(); |
- MergeSyncAndLocalURLDuplicates(sync_turl.release(), dupe_turl, |
- &new_changes); |
- local_data_map.erase(old_guid); |
- } else { |
- std::string guid = sync_turl->sync_guid(); |
- // Keyword conflict is possible in this case. Resolve it first before |
- // adding the new TemplateURL. Note that we don't remove the local TURL |
- // from local_data_map in this case as it may still need to be pushed to |
- // the cloud. We also explicitly don't resolve conflicts against |
- // extension keywords; see comments in ProcessSyncChanges(). |
- TemplateURL* existing_keyword_turl = |
- FindNonExtensionTemplateURLForKeyword(sync_turl->keyword()); |
- if (!existing_keyword_turl || ResolveSyncKeywordConflict( |
- sync_turl.get(), existing_keyword_turl, &new_changes)) { |
- // Force the local ID to kInvalidTemplateURLID so we can add it. |
- TemplateURLData data(sync_turl->data()); |
- data.id = kInvalidTemplateURLID; |
- Add(new TemplateURL(profile_, data)); |
- |
- // Possibly set the newly added |turl| as the default search provider. |
- SetDefaultSearchProviderIfNewlySynced(guid); |
- } |
- } |
+ // The search engine from the cloud has not been synced locally. Merge it |
+ // into our local model. This will handle any conflicts with local (and |
+ // already-synced) TemplateURLs. It will prefer to keep entries from Sync |
+ // over not-yet-synced TemplateURLs. |
+ MergeInSyncTemplateURL(sync_turl.get(), sync_data_map, &new_changes, |
+ &local_data_map); |
} |
} |
@@ -2324,125 +2305,129 @@ string16 TemplateURLService::UniquifyKeyword(const TemplateURL& turl, |
return keyword_candidate; |
} |
-bool TemplateURLService::ResolveSyncKeywordConflict( |
- TemplateURL* sync_turl, |
- TemplateURL* local_turl, |
+bool TemplateURLService::IsLocalTemplateURLBetter( |
+ const TemplateURL* local_turl, |
+ const TemplateURL* sync_turl) { |
+ DCHECK(GetTemplateURLForGUID(local_turl->sync_guid())); |
+ return local_turl->last_modified() > sync_turl->last_modified() || |
+ local_turl->created_by_policy() || |
+ local_turl== GetDefaultSearchProvider(); |
+} |
+ |
+void TemplateURLService::ResolveSyncKeywordConflict( |
+ TemplateURL* unapplied_sync_turl, |
+ TemplateURL* applied_sync_turl, |
syncer::SyncChangeList* change_list) { |
DCHECK(loaded_); |
- DCHECK(sync_turl); |
- DCHECK(local_turl); |
- DCHECK(sync_turl->sync_guid() != local_turl->sync_guid()); |
- DCHECK(!local_turl->IsExtensionKeyword()); |
+ DCHECK(unapplied_sync_turl); |
+ DCHECK(applied_sync_turl); |
DCHECK(change_list); |
- |
- const bool local_is_better = |
- (local_turl->last_modified() > sync_turl->last_modified()) || |
- local_turl->created_by_policy() || |
- (local_turl == GetDefaultSearchProvider()); |
- const bool can_replace_local = CanReplace(local_turl); |
- if (CanReplace(sync_turl) && (local_is_better || !can_replace_local)) { |
- syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*sync_turl); |
- change_list->push_back(syncer::SyncChange(FROM_HERE, |
- syncer::SyncChange::ACTION_DELETE, |
- sync_data)); |
- return false; |
- } |
- if (can_replace_local) { |
- // Since we're processing sync changes, the upcoming Remove() won't generate |
- // an ACTION_DELETE. We need to do it manually to keep the server in sync |
- // with us. Note that if we're being called from |
- // MergeDataAndStartSyncing(), and this TemplateURL was pre-existing rather |
- // than having just been brought down, then this is wrong, because the |
- // server doesn't yet know about this entity; but in this case, |
- // PruneSyncChanges() will prune out the ACTION_DELETE we create here. |
- syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*local_turl); |
- change_list->push_back(syncer::SyncChange(FROM_HERE, |
- syncer::SyncChange::ACTION_DELETE, |
- sync_data)); |
- Remove(local_turl); |
- } else if (local_is_better) { |
- string16 new_keyword = UniquifyKeyword(*sync_turl, false); |
- DCHECK(!GetTemplateURLForKeyword(new_keyword)); |
- sync_turl->data_.SetKeyword(new_keyword); |
- // If we update the cloud TURL, we need to push an update back to sync |
- // informing it that something has changed. |
- syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*sync_turl); |
- change_list->push_back(syncer::SyncChange(FROM_HERE, |
- syncer::SyncChange::ACTION_UPDATE, |
- sync_data)); |
+ DCHECK_EQ(applied_sync_turl->keyword(), unapplied_sync_turl->keyword()); |
+ DCHECK(!applied_sync_turl->IsExtensionKeyword()); |
+ |
+ // Both |unapplied_sync_turl| and |applied_sync_turl| are known to Sync, so |
+ // don't delete either of them. Instead, determine which is "better" and |
+ // uniquify the other one, sending an update to the server for the updated |
+ // entry. |
+ const bool applied_turl_is_better = |
+ IsLocalTemplateURLBetter(applied_sync_turl, unapplied_sync_turl); |
+ TemplateURL* loser = applied_turl_is_better ? |
+ unapplied_sync_turl : applied_sync_turl; |
+ string16 new_keyword = UniquifyKeyword(*loser, false); |
+ DCHECK(!GetTemplateURLForKeyword(new_keyword)); |
+ if (applied_turl_is_better) { |
+ // Just set the keyword of |unapplied_sync_turl|. The caller is responsible |
+ // for adding or updating unapplied_sync_turl in the local model. |
+ unapplied_sync_turl->data_.SetKeyword(new_keyword); |
} else { |
- string16 new_keyword = UniquifyKeyword(*local_turl, false); |
- TemplateURLData data(local_turl->data()); |
+ // Update |applied_sync_turl| in the local model with the new keyword. |
+ TemplateURLData data(applied_sync_turl->data()); |
data.SetKeyword(new_keyword); |
- TemplateURL new_turl(local_turl->profile(), data); |
- UIThreadSearchTermsData search_terms_data(local_turl->profile()); |
- if (UpdateNoNotify(local_turl, new_turl, search_terms_data)) |
+ TemplateURL new_turl(applied_sync_turl->profile(), data); |
+ UIThreadSearchTermsData search_terms_data(applied_sync_turl->profile()); |
+ if (UpdateNoNotify(applied_sync_turl, new_turl, search_terms_data)) |
NotifyObservers(); |
- // Since we're processing sync changes, the UpdateNoNotify() above didn't |
- // generate an ACTION_UPDATE. We need to do it manually to keep the server |
- // in sync with us. Note that if we're being called from |
- // MergeDataAndStartSyncing(), and this TemplateURL was pre-existing rather |
- // than having just been brought down, then this is wrong, because the |
- // server won't know about this entity until it processes the ACTION_ADD our |
- // caller will later generate; but in this case, PruneSyncChanges() will |
- // prune out the ACTION_UPDATE we create here. |
- syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*local_turl); |
- change_list->push_back(syncer::SyncChange(FROM_HERE, |
- syncer::SyncChange::ACTION_UPDATE, |
- sync_data)); |
} |
- return true; |
-} |
- |
-TemplateURL* TemplateURLService::FindDuplicateOfSyncTemplateURL( |
- const TemplateURL& sync_turl) { |
- TemplateURL* existing_turl = GetTemplateURLForKeyword(sync_turl.keyword()); |
- return existing_turl && (existing_turl->url() == sync_turl.url()) ? |
- existing_turl : NULL; |
+ // The losing TemplateURL should have their keyword updated. Send a change to |
+ // the server to reflect this change. |
+ syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*loser); |
+ change_list->push_back(syncer::SyncChange(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data)); |
} |
-void TemplateURLService::MergeSyncAndLocalURLDuplicates( |
+void TemplateURLService::MergeInSyncTemplateURL( |
TemplateURL* sync_turl, |
- TemplateURL* local_turl, |
- syncer::SyncChangeList* change_list) { |
- DCHECK(loaded_); |
+ const SyncDataMap& sync_data, |
+ syncer::SyncChangeList* change_list, |
+ SyncDataMap* local_data) { |
DCHECK(sync_turl); |
- DCHECK(local_turl); |
- DCHECK(change_list); |
- scoped_ptr<TemplateURL> scoped_sync_turl(sync_turl); |
- if (sync_turl->last_modified() > local_turl->last_modified()) { |
- // Fully replace local_url with Sync's copy. Note that because use Add |
- // rather than ResetTemplateURL, |sync_url| is added with a fresh |
- // TemplateURLID. We don't need to sync the new ID back to the server since |
- // it's only relevant locally. |
- bool delete_default = (local_turl == GetDefaultSearchProvider()); |
- DCHECK(!delete_default || !is_default_search_managed_); |
- if (delete_default) |
- default_search_provider_ = NULL; |
- |
- // See comments in ResolveSyncKeywordConflict() regarding generating an |
- // ACTION_DELETE manually since Remove() won't do it. |
- syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*local_turl); |
- change_list->push_back(syncer::SyncChange(FROM_HERE, |
- syncer::SyncChange::ACTION_DELETE, |
- sync_data)); |
- Remove(local_turl); |
+ DCHECK(!GetTemplateURLForGUID(sync_turl->sync_guid())); |
+ DCHECK(IsFromSync(sync_turl, sync_data)); |
+ |
+ TemplateURL* conflicting_turl = |
+ FindNonExtensionTemplateURLForKeyword(sync_turl->keyword()); |
+ bool should_add_sync_turl = true; |
+ |
+ // If there was no TemplateURL in the local model that conflicts with |
+ // |sync_turl|, skip the following preparation steps and just add |sync_turl| |
+ // directly. Otherwise, modify |conflicting_turl| to make room for |
+ // |sync_turl|. |
+ if (conflicting_turl) { |
+ if (IsFromSync(conflicting_turl, sync_data)) { |
+ // |conflicting_turl| is already known to Sync, so we're not allowed to |
+ // remove it. In this case, we want to uniquify the worse one and send an |
+ // update for the changed keyword to sync. We can reuse the logic from |
+ // ResolveSyncKeywordConflict for this. |
+ ResolveSyncKeywordConflict(sync_turl, conflicting_turl, change_list); |
+ } else { |
+ // |conflicting_turl| is not yet known to Sync. If it is better, then we |
+ // want to transfer its values up to sync. Otherwise, we remove it and |
+ // allow the entry from Sync to overtake it in the model. |
+ const std::string guid = conflicting_turl->sync_guid(); |
+ if (IsLocalTemplateURLBetter(conflicting_turl, sync_turl)) { |
+ ResetTemplateURLGUID(conflicting_turl, sync_turl->sync_guid()); |
+ syncer::SyncData sync_data = |
+ CreateSyncDataFromTemplateURL(*conflicting_turl); |
+ change_list->push_back(syncer::SyncChange( |
+ FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data)); |
+ if (conflicting_turl == GetDefaultSearchProvider() && |
+ !pending_synced_default_search_) { |
+ // If we're not waiting for the Synced default to come in, we should |
+ // override the pref with our new GUID. If we are waiting for the |
+ // arrival of a synced default, setting the pref here would cause us |
+ // to lose the GUID we are waiting on. |
+ PrefService* prefs = GetPrefs(); |
+ if (prefs) { |
+ prefs->SetString(prefs::kSyncedDefaultSearchProviderGUID, |
+ conflicting_turl->sync_guid()); |
+ } |
+ } |
+ // Note that in this case we do not add the Sync TemplateURL to the |
+ // local model, since we've effectively "merged" it in by updating the |
+ // local conflicting entry with its sync_guid. |
+ should_add_sync_turl = false; |
+ } else { |
+ // We guarantee that this isn't the local search provider. Otherwise, |
+ // local would have won. |
+ DCHECK(conflicting_turl != GetDefaultSearchProvider()); |
+ Remove(conflicting_turl); |
+ } |
+ // This TemplateURL was either removed or overwritten in the local model. |
+ // Remove the entry from the local data so it isn't pushed up to Sync. |
+ local_data->erase(guid); |
+ } |
+ } |
+ if (should_add_sync_turl) { |
+ const std::string guid = sync_turl->sync_guid(); |
// Force the local ID to kInvalidTemplateURLID so we can add it. |
- sync_turl->data_.id = kInvalidTemplateURLID; |
- Add(scoped_sync_turl.release()); |
- if (delete_default) |
- SetDefaultSearchProvider(sync_turl); |
- } else { |
- // Change the local TURL's GUID to the server's GUID and push an update to |
- // Sync. This ensures that the rest of local_url's fields are sync'd up to |
- // the server, and the next time local_url is synced, it is recognized by |
- // having the same GUID. |
- ResetTemplateURLGUID(local_turl, sync_turl->sync_guid()); |
- syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*local_turl); |
- change_list->push_back(syncer::SyncChange(FROM_HERE, |
- syncer::SyncChange::ACTION_UPDATE, |
- sync_data)); |
+ TemplateURLData data(sync_turl->data()); |
+ data.id = kInvalidTemplateURLID; |
+ Add(new TemplateURL(profile_, data)); |
+ |
+ // Possibly set the newly added |turl| as the default search provider. |
+ SetDefaultSearchProviderIfNewlySynced(guid); |
} |
} |