summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/sync/glue/favicon_cache.cc426
-rw-r--r--chrome/browser/sync/glue/favicon_cache.h32
-rw-r--r--chrome/browser/sync/glue/favicon_cache_unittest.cc944
-rw-r--r--chrome/browser/sync/glue/session_model_associator.cc12
-rw-r--r--chrome/browser/sync/glue/session_model_associator.h3
-rw-r--r--chrome/browser/sync/glue/session_model_associator_unittest.cc2
-rw-r--r--chrome/browser/sync/profile_sync_components_factory_impl.cc21
-rw-r--r--chrome/common/chrome_switches.cc3
-rw-r--r--chrome/common/chrome_switches.h1
9 files changed, 1380 insertions, 64 deletions
diff --git a/chrome/browser/sync/glue/favicon_cache.cc b/chrome/browser/sync/glue/favicon_cache.cc
index 72fd5ae..fd2ebb8 100644
--- a/chrome/browser/sync/glue/favicon_cache.cc
+++ b/chrome/browser/sync/glue/favicon_cache.cc
@@ -4,10 +4,15 @@
#include "chrome/browser/sync/glue/favicon_cache.h"
+#include <set>
+
#include "chrome/browser/favicon/favicon_service.h"
#include "chrome/browser/favicon/favicon_service_factory.h"
#include "chrome/browser/history/history_types.h"
#include "sync/api/time.h"
+#include "sync/protocol/favicon_image_specifics.pb.h"
+#include "sync/protocol/favicon_tracking_specifics.pb.h"
+#include "sync/protocol/sync.pb.h"
#include "ui/gfx/favicon_size.h"
namespace browser_sync {
@@ -88,6 +93,62 @@ std::string IconSizeToString(IconSize icon_size) {
}
}
+// Extract the favicon url from either of the favicon types.
+GURL GetFaviconURLFromSpecifics(const sync_pb::EntitySpecifics& specifics) {
+ if (specifics.has_favicon_tracking())
+ return GURL(specifics.favicon_tracking().favicon_url());
+ else
+ return GURL(specifics.favicon_image().favicon_url());
+}
+
+// Convert protobuf image data into a FaviconBitmapResult.
+history::FaviconBitmapResult GetImageDataFromSpecifics(
+ const sync_pb::FaviconData& favicon_data) {
+ base::RefCountedString* temp_string =
+ new base::RefCountedString();
+ temp_string->data() = favicon_data.favicon();
+ history::FaviconBitmapResult bitmap_result;
+ bitmap_result.bitmap_data = temp_string;
+ bitmap_result.pixel_size.set_height(favicon_data.height());
+ bitmap_result.pixel_size.set_width(favicon_data.width());
+ return bitmap_result;
+}
+
+// Convert a FaviconBitmapResult into protobuf image data.
+void FillSpecificsWithImageData(
+ const history::FaviconBitmapResult& bitmap_result,
+ sync_pb::FaviconData* favicon_data) {
+ if (!bitmap_result.bitmap_data.get())
+ return;
+ favicon_data->set_height(bitmap_result.pixel_size.height());
+ favicon_data->set_width(bitmap_result.pixel_size.width());
+ favicon_data->set_favicon(bitmap_result.bitmap_data->front(),
+ bitmap_result.bitmap_data->size());
+}
+
+// Build a FaviconImageSpecifics from a SyncedFaviconInfo.
+void BuildImageSpecifics(
+ const SyncedFaviconInfo* favicon_info,
+ sync_pb::FaviconImageSpecifics* image_specifics) {
+ image_specifics->set_favicon_url(favicon_info->favicon_url.spec());
+ FillSpecificsWithImageData(favicon_info->bitmap_data[SIZE_16],
+ image_specifics->mutable_favicon_web());
+ // TODO(zea): bring this back if we can handle the load.
+ // FillSpecificsWithImageData(favicon_info->bitmap_data[SIZE_32],
+ // image_specifics->mutable_favicon_web_32());
+ // FillSpecificsWithImageData(favicon_info->bitmap_data[SIZE_64],
+ // image_specifics->mutable_favicon_touch_64());
+}
+
+// Build a FaviconTrackingSpecifics from a SyncedFaviconInfo.
+void BuildTrackingSpecifics(
+ const SyncedFaviconInfo* favicon_info,
+ sync_pb::FaviconTrackingSpecifics* tracking_specifics) {
+ tracking_specifics->set_favicon_url(favicon_info->favicon_url.spec());
+ tracking_specifics->set_last_visit_time_ms(favicon_info->last_visit_time);
+ tracking_specifics->set_is_bookmarked(favicon_info->is_bookmarked);
+}
+
} // namespace
FaviconCacheObserver::~FaviconCacheObserver() {}
@@ -104,23 +165,138 @@ syncer::SyncMergeResult FaviconCache::MergeDataAndStartSyncing(
const syncer::SyncDataList& initial_sync_data,
scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
scoped_ptr<syncer::SyncErrorFactory> error_handler) {
- return syncer::SyncMergeResult(type);
+ DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
+ if (type == syncer::FAVICON_IMAGES)
+ favicon_images_sync_processor_ = sync_processor.Pass();
+ else
+ favicon_tracking_sync_processor_ = sync_processor.Pass();
+
+ syncer::SyncMergeResult merge_result(type);
+ merge_result.set_num_items_before_association(synced_favicons_.size());
+ std::set<GURL> unsynced_favicon_urls;
+ for (FaviconMap::const_iterator iter = synced_favicons_.begin();
+ iter != synced_favicons_.end(); ++iter) {
+ unsynced_favicon_urls.insert(iter->first);
+ }
+
+ syncer::SyncChangeList local_changes;
+ for (syncer::SyncDataList::const_iterator iter = initial_sync_data.begin();
+ iter != initial_sync_data.end(); ++iter) {
+ GURL favicon_url = GetLocalFaviconFromSyncedData(*iter);
+ if (favicon_url.is_valid()) {
+ unsynced_favicon_urls.erase(favicon_url);
+ MergeSyncFavicon(*iter, &local_changes);
+ merge_result.set_num_items_modified(
+ merge_result.num_items_modified() + 1);
+ } else {
+ AddLocalFaviconFromSyncedData(*iter);
+ merge_result.set_num_items_added(merge_result.num_items_added() + 1);
+ }
+ }
+
+ for (std::set<GURL>::const_iterator iter = unsynced_favicon_urls.begin();
+ iter != unsynced_favicon_urls.end(); ++iter) {
+ local_changes.push_back(
+ syncer::SyncChange(FROM_HERE,
+ syncer::SyncChange::ACTION_ADD,
+ CreateSyncDataFromLocalFavicon(type, *iter)));
+ }
+ merge_result.set_num_items_after_association(synced_favicons_.size());
+
+ // TODO(zea): enforce sync limit on favicon count.
+ if (type == syncer::FAVICON_IMAGES) {
+ merge_result.set_error(
+ favicon_images_sync_processor_->ProcessSyncChanges(FROM_HERE,
+ local_changes));
+ } else {
+ merge_result.set_error(
+ favicon_tracking_sync_processor_->ProcessSyncChanges(FROM_HERE,
+ local_changes));
+ }
+ return merge_result;
}
void FaviconCache::StopSyncing(syncer::ModelType type) {
+ favicon_images_sync_processor_.reset();
+ favicon_tracking_sync_processor_.reset();
cancelable_task_tracker_.TryCancelAll();
page_task_map_.clear();
}
syncer::SyncDataList FaviconCache::GetAllSyncData(syncer::ModelType type)
const {
- return syncer::SyncDataList();
+ syncer::SyncDataList data_list;
+ for (FaviconMap::const_iterator iter = synced_favicons_.begin();
+ iter != synced_favicons_.end(); ++iter) {
+ data_list.push_back(CreateSyncDataFromLocalFavicon(type, iter->first));
+ }
+ return data_list;
}
syncer::SyncError FaviconCache::ProcessSyncChanges(
const tracked_objects::Location& from_here,
const syncer::SyncChangeList& change_list) {
- return syncer::SyncError();
+
+ syncer::SyncChangeList new_changes;
+ syncer::SyncError error;
+ syncer::ModelType type = syncer::UNSPECIFIED;
+ for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
+ iter != change_list.end(); ++iter) {
+ type = iter->sync_data().GetDataType();
+ DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
+ GURL favicon_url =
+ GetFaviconURLFromSpecifics(iter->sync_data().GetSpecifics());
+ if (!favicon_url.is_valid()) {
+ error.Reset(FROM_HERE, "Received invalid favicon url.", type);
+ break;
+ }
+ FaviconMap::iterator favicon_iter = synced_favicons_.find(favicon_url);
+ if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) {
+ if (favicon_iter == synced_favicons_.end()) {
+ // Two clients might wind up deleting different parts of the same
+ // favicon, so ignore this.
+ continue;
+ } else {
+ DVLOG(1) << "Deleting favicon at " << favicon_url.spec();
+ synced_favicons_.erase(favicon_iter);
+ // TODO(zea): it's possible that we'll receive a deletion for an image,
+ // but not a tracking data, or vice versa, resulting in an orphan
+ // favicon node in one of the types. We should track how often this
+ // happens, and if it becomes a problem delete each part individually
+ // from the local model.
+ }
+ } else if (iter->change_type() == syncer::SyncChange::ACTION_UPDATE ||
+ iter->change_type() == syncer::SyncChange::ACTION_ADD) {
+ // Adds and updates are treated the same due to the lack of strong
+ // consistency (it's possible we'll receive an update for a tracking info
+ // before we've received the add for the image, and should handle both
+ // gracefully).
+ if (favicon_iter == synced_favicons_.end()) {
+ DVLOG(1) << "Adding favicon at " << favicon_url.spec();
+ AddLocalFaviconFromSyncedData(iter->sync_data());
+ } else {
+ DVLOG(1) << "Updating favicon at " << favicon_url.spec();
+ MergeSyncFavicon(iter->sync_data(), &new_changes);
+ }
+ } else {
+ error.Reset(FROM_HERE, "Invalid action received.", type);
+ break;
+ }
+ }
+
+ if (!error.IsSet() && !new_changes.empty()) {
+ if (type == syncer::FAVICON_IMAGES) {
+ error =
+ favicon_images_sync_processor_->ProcessSyncChanges(FROM_HERE,
+ new_changes);
+ } else {
+ error =
+ favicon_tracking_sync_processor_->ProcessSyncChanges(FROM_HERE,
+ new_changes);
+ }
+ }
+
+ return error;
}
void FaviconCache::OnPageFaviconUpdated(const GURL& page_url) {
@@ -161,7 +337,12 @@ void FaviconCache::OnFaviconVisited(const GURL& page_url,
DVLOG(1) << "Associating " << page_url.spec() << "with favicon at "
<< favicon_url.spec() << " and marking visited.";
page_favicon_map_[page_url] = favicon_url;
- UpdateSyncState(favicon_url, SYNC_TRACKING);
+ synced_favicons_[favicon_url]->last_visit_time = std::max(
+ synced_favicons_[favicon_url]->last_visit_time,
+ syncer::TimeToProtoTime(base::Time::Now()));
+ UpdateSyncState(favicon_url,
+ SYNC_TRACKING,
+ syncer::SyncChange::ACTION_UPDATE);
}
bool FaviconCache::GetSyncedFaviconForFaviconURL(
@@ -198,7 +379,8 @@ bool FaviconCache::GetSyncedFaviconForPageURL(
void FaviconCache::OnReceivedSyncFavicon(const GURL& page_url,
const GURL& icon_url,
- const std::string& icon_bytes) {
+ const std::string& icon_bytes,
+ int64 visit_time_ms) {
if (!icon_url.is_valid() || !page_url.is_valid())
return;
DVLOG(1) << "Associating " << page_url.spec() << " with favicon at "
@@ -221,14 +403,17 @@ void FaviconCache::OnReceivedSyncFavicon(const GURL& page_url,
return;
SyncedFaviconInfo* favicon_info = GetFaviconInfo(icon_url);
+ if (!favicon_info)
+ return; // We reached the in-memory limit.
base::RefCountedString* temp_string = new base::RefCountedString();
temp_string->data() = icon_bytes;
favicon_info->bitmap_data[SIZE_16].bitmap_data = temp_string;
// We assume legacy favicons are 16x16.
favicon_info->bitmap_data[SIZE_16].pixel_size.set_width(16);
- favicon_info->bitmap_data[SIZE_16].pixel_size.set_width(16);
+ favicon_info->bitmap_data[SIZE_16].pixel_size.set_height(16);
+ favicon_info->last_visit_time = visit_time_ms;
- UpdateSyncState(icon_url, SYNC_BOTH);
+ UpdateSyncState(icon_url, SYNC_BOTH, syncer::SyncChange::ACTION_ADD);
}
void FaviconCache::SetLegacyDelegate(FaviconCacheObserver* observer) {
@@ -254,6 +439,7 @@ void FaviconCache::OnFaviconDataAvailable(
return;
}
+ bool added_new_favicon = false;
GURL favicon_url;
for (size_t i = 0; i < bitmap_results.size(); ++i) {
const history::FaviconBitmapResult& bitmap_result = bitmap_results[i];
@@ -261,22 +447,12 @@ void FaviconCache::OnFaviconDataAvailable(
continue; // Can happen if the page is still loading.
favicon_url = bitmap_result.icon_url;
FaviconMap::iterator favicon_iter = synced_favicons_.find(favicon_url);
-
- SyncedFaviconInfo* favicon_info = NULL;
- if (favicon_iter == synced_favicons_.end()) {
- // If we've already reached our in memory favicon limit, just return.
- // TODO(zea): UMA this.
- if (kMaxFaviconsInMem != 0 &&
- synced_favicons_.size() > kMaxFaviconsInMem) {
- return;
- }
- favicon_info = new SyncedFaviconInfo(favicon_url);
- synced_favicons_[favicon_url] = make_linked_ptr(favicon_info);
- } else {
- // TODO(zea): add a dampening factor so animated favicons don't constantly
- // update the image data.
- favicon_info = favicon_iter->second.get();
- }
+ added_new_favicon |= (favicon_iter == synced_favicons_.end());
+ // TODO(zea): Add a dampening factor so animated favicons don't constantly
+ // update the image data.
+ SyncedFaviconInfo* favicon_info = GetFaviconInfo(favicon_url);
+ if (!favicon_info)
+ break; // We reached the in-memory limit.
favicon_info->last_visit_time = std::max(
favicon_info->last_visit_time,
syncer::TimeToProtoTime(base::Time::Now()));
@@ -301,18 +477,63 @@ void FaviconCache::OnFaviconDataAvailable(
}
}
- if (!favicon_url.is_valid())
- return;
page_favicon_map_[page_url] = favicon_url;
- UpdateSyncState(favicon_url, SYNC_BOTH);
+ UpdateSyncState(favicon_url,
+ SYNC_BOTH,
+ (added_new_favicon ?
+ syncer::SyncChange::ACTION_ADD :
+ syncer::SyncChange::ACTION_UPDATE));
if (legacy_delegate_)
legacy_delegate_->OnFaviconUpdated(page_url, favicon_url);
}
-void FaviconCache::UpdateSyncState(const GURL& icon_url,
- SyncState state_to_update) {
- // TODO(zea): implement this.
+void FaviconCache::UpdateSyncState(
+ const GURL& icon_url,
+ SyncState state_to_update,
+ syncer::SyncChange::SyncChangeType change_type) {
+ if (!favicon_images_sync_processor_.get() ||
+ !favicon_tracking_sync_processor_.get())
+ return;
+
+ FaviconMap::const_iterator iter = synced_favicons_.find(icon_url);
+ DCHECK(iter != synced_favicons_.end());
+ const SyncedFaviconInfo* favicon_info = iter->second.get();
+
+ if (state_to_update == SYNC_IMAGE || state_to_update == SYNC_BOTH) {
+ sync_pb::EntitySpecifics new_specifics;
+ sync_pb::FaviconImageSpecifics* image_specifics =
+ new_specifics.mutable_favicon_image();
+ BuildImageSpecifics(favicon_info, image_specifics);
+
+ syncer::SyncChangeList changes;
+ changes.push_back(
+ syncer::SyncChange(FROM_HERE,
+ change_type,
+ syncer::SyncData::CreateLocalData(
+ icon_url.spec(),
+ icon_url.spec(),
+ new_specifics)));
+ // TODO(zea): enforce sync limit on favicon count.
+ favicon_images_sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
+ }
+ if (state_to_update == SYNC_TRACKING || state_to_update == SYNC_BOTH) {
+ sync_pb::EntitySpecifics new_specifics;
+ sync_pb::FaviconTrackingSpecifics* tracking_specifics =
+ new_specifics.mutable_favicon_tracking();
+ BuildTrackingSpecifics(favicon_info, tracking_specifics);
+
+ syncer::SyncChangeList changes;
+ changes.push_back(
+ syncer::SyncChange(FROM_HERE,
+ change_type,
+ syncer::SyncData::CreateLocalData(
+ icon_url.spec(),
+ icon_url.spec(),
+ new_specifics)));
+ // TODO(zea): enforce sync limit on favicon count.
+ favicon_tracking_sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
+ }
}
SyncedFaviconInfo* FaviconCache::GetFaviconInfo(
@@ -326,6 +547,153 @@ SyncedFaviconInfo* FaviconCache::GetFaviconInfo(
return favicon_info;
}
+GURL FaviconCache::GetLocalFaviconFromSyncedData(
+ const syncer::SyncData& sync_favicon) const {
+ syncer::ModelType type = sync_favicon.GetDataType();
+ DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
+ GURL favicon_url = GetFaviconURLFromSpecifics(sync_favicon.GetSpecifics());
+ return (synced_favicons_.count(favicon_url) > 0 ? favicon_url : GURL());
+}
+
+void FaviconCache::MergeSyncFavicon(const syncer::SyncData& sync_favicon,
+ syncer::SyncChangeList* sync_changes) {
+ syncer::ModelType type = sync_favicon.GetDataType();
+ DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
+ sync_pb::EntitySpecifics new_specifics;
+ GURL favicon_url = GetFaviconURLFromSpecifics(sync_favicon.GetSpecifics());
+ if (type == syncer::FAVICON_IMAGES) {
+ sync_pb::FaviconImageSpecifics image_specifics =
+ sync_favicon.GetSpecifics().favicon_image();
+ FaviconMap::const_iterator iter = synced_favicons_.find(favicon_url);
+ DCHECK(iter != synced_favicons_.end());
+ SyncedFaviconInfo* favicon_info = iter->second.get();
+
+ // Remote image data always clobbers local image data.
+ bool needs_update = false;
+ if (image_specifics.has_favicon_web()) {
+ favicon_info->bitmap_data[SIZE_16] = GetImageDataFromSpecifics(
+ image_specifics.favicon_web());
+ } else if (favicon_info->bitmap_data[SIZE_16].bitmap_data.get()) {
+ needs_update = true;
+ }
+ if (image_specifics.has_favicon_web_32()) {
+ favicon_info->bitmap_data[SIZE_32] = GetImageDataFromSpecifics(
+ image_specifics.favicon_web_32());
+ } else if (favicon_info->bitmap_data[SIZE_32].bitmap_data.get()) {
+ needs_update = true;
+ }
+ if (image_specifics.has_favicon_touch_64()) {
+ favicon_info->bitmap_data[SIZE_64] = GetImageDataFromSpecifics(
+ image_specifics.favicon_touch_64());
+ } else if (favicon_info->bitmap_data[SIZE_64].bitmap_data.get()) {
+ needs_update = true;
+ }
+
+ if (needs_update)
+ BuildImageSpecifics(favicon_info, new_specifics.mutable_favicon_image());
+ } else {
+ sync_pb::FaviconTrackingSpecifics tracking_specifics =
+ sync_favicon.GetSpecifics().favicon_tracking();
+ FaviconMap::const_iterator iter = synced_favicons_.find(favicon_url);
+ DCHECK(iter != synced_favicons_.end());
+ SyncedFaviconInfo* favicon_info = iter->second.get();
+
+ // Tracking data is merged, such that bookmark data is the logical OR
+ // of the two, and last visit time is the most recent.
+ favicon_info->last_visit_time =
+ (favicon_info->last_visit_time >
+ tracking_specifics.last_visit_time_ms() ?
+ favicon_info->last_visit_time :
+ tracking_specifics.last_visit_time_ms());
+ favicon_info->is_bookmarked = (favicon_info->is_bookmarked ||
+ tracking_specifics.is_bookmarked());
+
+ if (favicon_info->last_visit_time !=
+ tracking_specifics.last_visit_time_ms() ||
+ favicon_info->is_bookmarked != tracking_specifics.is_bookmarked()) {
+ BuildTrackingSpecifics(favicon_info,
+ new_specifics.mutable_favicon_tracking());
+ }
+ }
+
+ if (new_specifics.has_favicon_image() ||
+ new_specifics.has_favicon_tracking()) {
+ sync_changes->push_back(syncer::SyncChange(
+ FROM_HERE,
+ syncer::SyncChange::ACTION_UPDATE,
+ syncer::SyncData::CreateLocalData(favicon_url.spec(),
+ favicon_url.spec(),
+ new_specifics)));
+ }
+}
+
+void FaviconCache::AddLocalFaviconFromSyncedData(
+ const syncer::SyncData& sync_favicon) {
+ syncer::ModelType type = sync_favicon.GetDataType();
+ DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
+ if (type == syncer::FAVICON_IMAGES) {
+ sync_pb::FaviconImageSpecifics image_specifics =
+ sync_favicon.GetSpecifics().favicon_image();
+ GURL favicon_url = GURL(image_specifics.favicon_url());
+ DCHECK(favicon_url.is_valid());
+ DCHECK(!synced_favicons_.count(favicon_url));
+
+ SyncedFaviconInfo* favicon_info = GetFaviconInfo(favicon_url);
+ if (!favicon_info)
+ return; // We reached the in-memory limit.
+ if (image_specifics.has_favicon_web()) {
+ favicon_info->bitmap_data[SIZE_16] = GetImageDataFromSpecifics(
+ image_specifics.favicon_web());
+ }
+ if (image_specifics.has_favicon_web_32()) {
+ favicon_info->bitmap_data[SIZE_32] = GetImageDataFromSpecifics(
+ image_specifics.favicon_web_32());
+ }
+ if (image_specifics.has_favicon_touch_64()) {
+ favicon_info->bitmap_data[SIZE_64] = GetImageDataFromSpecifics(
+ image_specifics.favicon_touch_64());
+ }
+ } else {
+ sync_pb::FaviconTrackingSpecifics tracking_specifics =
+ sync_favicon.GetSpecifics().favicon_tracking();
+ GURL favicon_url = GURL(tracking_specifics.favicon_url());
+ DCHECK(favicon_url.is_valid());
+ DCHECK(!synced_favicons_.count(favicon_url));
+
+ SyncedFaviconInfo* favicon_info = GetFaviconInfo(favicon_url);
+ if (!favicon_info)
+ return; // We reached the in-memory limit.
+ favicon_info->last_visit_time = tracking_specifics.last_visit_time_ms();
+ favicon_info->is_bookmarked = tracking_specifics.is_bookmarked();
+ }
+}
+
+syncer::SyncData FaviconCache::CreateSyncDataFromLocalFavicon(
+ syncer::ModelType type,
+ const GURL& favicon_url) const {
+ DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
+ DCHECK(favicon_url.is_valid());
+ FaviconMap::const_iterator iter = synced_favicons_.find(favicon_url);
+ DCHECK(iter != synced_favicons_.end());
+ SyncedFaviconInfo* favicon_info = iter->second.get();
+
+ syncer::SyncData data;
+ sync_pb::EntitySpecifics specifics;
+ if (type == syncer::FAVICON_IMAGES) {
+ sync_pb::FaviconImageSpecifics* image_specifics =
+ specifics.mutable_favicon_image();
+ BuildImageSpecifics(favicon_info, image_specifics);
+ } else {
+ sync_pb::FaviconTrackingSpecifics* tracking_specifics =
+ specifics.mutable_favicon_tracking();
+ BuildTrackingSpecifics(favicon_info, tracking_specifics);
+ }
+ data = syncer::SyncData::CreateLocalData(favicon_url.spec(),
+ favicon_url.spec(),
+ specifics);
+ return data;
+}
+
size_t FaviconCache::NumFaviconsForTest() const {
return synced_favicons_.size();
}
diff --git a/chrome/browser/sync/glue/favicon_cache.h b/chrome/browser/sync/glue/favicon_cache.h
index eedb313..30f7e95 100644
--- a/chrome/browser/sync/glue/favicon_cache.h
+++ b/chrome/browser/sync/glue/favicon_cache.h
@@ -19,6 +19,7 @@
#include "chrome/browser/sessions/session_id.h"
#include "chrome/common/cancelable_task_tracker.h"
#include "googleurl/src/gurl.h"
+#include "sync/api/sync_change.h"
#include "sync/api/sync_error_factory.h"
#include "sync/api/syncable_service.h"
@@ -57,7 +58,6 @@ class FaviconCache : public syncer::SyncableService {
virtual ~FaviconCache();
// SyncableService implementation.
- // TODO(zea): implement these.
virtual syncer::SyncMergeResult MergeDataAndStartSyncing(
syncer::ModelType type,
const syncer::SyncDataList& initial_sync_data,
@@ -99,7 +99,8 @@ class FaviconCache : public syncer::SyncableService {
// If |icon_bytes| is empty, only updates the page->favicon url mapping.
void OnReceivedSyncFavicon(const GURL& page_url,
const GURL& icon_url,
- const std::string& icon_bytes);
+ const std::string& icon_bytes,
+ int64 visit_time_ms);
// Support for syncing favicons using the legacy format (within tab sync).
void SetLegacyDelegate(FaviconCacheObserver* observer);
@@ -129,12 +130,33 @@ class FaviconCache : public syncer::SyncableService {
const std::vector<history::FaviconBitmapResult>& bitmap_result);
// Helper method to update the sync state of the favicon at |icon_url|.
- void UpdateSyncState(const GURL& icon_url, SyncState state_to_update);
+ void UpdateSyncState(const GURL& icon_url,
+ SyncState state_to_update,
+ syncer::SyncChange::SyncChangeType change_type);
// Helper method to get favicon info from |synced_favicons_|. If no info
// exists for |icon_url|, creates a new SyncedFaviconInfo and returns it.
SyncedFaviconInfo* GetFaviconInfo(const GURL& icon_url);
+ // Returns the local favicon url associated with |sync_favicon| if one exists
+ // in |synced_favicons_|, else returns an invalid GURL.
+ GURL GetLocalFaviconFromSyncedData(
+ const syncer::SyncData& sync_favicon) const;
+
+ // Merges |sync_favicon| into |synced_favicons_|, updating |local_changes|
+ // with any changes that should be pushed to the sync processor.
+ void MergeSyncFavicon(const syncer::SyncData& sync_favicon,
+ syncer::SyncChangeList* sync_changes);
+
+ // Updates |synced_favicons_| with the favicon data from |sync_favicon|.
+ void AddLocalFaviconFromSyncedData(const syncer::SyncData& sync_favicon);
+
+ // Creates a SyncData object from the |type| data of |favicon_url|
+ // from within |synced_favicons_|.
+ syncer::SyncData CreateSyncDataFromLocalFavicon(
+ syncer::ModelType type,
+ const GURL& favicon_url) const;
+
// For testing only.
size_t NumFaviconsForTest() const;
@@ -155,12 +177,16 @@ class FaviconCache : public syncer::SyncableService {
// TODO(zea): consider creating a favicon handler here for fetching unsynced
// favicons from the web.
+ // Weak pointer factory for favicon loads.
base::WeakPtrFactory<FaviconCache> weak_ptr_factory_;
// Delegate for legacy favicon sync support.
// TODO(zea): Remove this eventually.
FaviconCacheObserver* legacy_delegate_;
+ scoped_ptr<syncer::SyncChangeProcessor> favicon_images_sync_processor_;
+ scoped_ptr<syncer::SyncChangeProcessor> favicon_tracking_sync_processor_;
+
DISALLOW_COPY_AND_ASSIGN(FaviconCache);
};
diff --git a/chrome/browser/sync/glue/favicon_cache_unittest.cc b/chrome/browser/sync/glue/favicon_cache_unittest.cc
index 3727aea..bee69c1 100644
--- a/chrome/browser/sync/glue/favicon_cache_unittest.cc
+++ b/chrome/browser/sync/glue/favicon_cache_unittest.cc
@@ -4,38 +4,383 @@
#include "chrome/browser/sync/glue/favicon_cache.h"
+#include "base/stringprintf.h"
+#include "sync/api/sync_error_factory_mock.h"
+#include "sync/protocol/favicon_image_specifics.pb.h"
+#include "sync/protocol/favicon_tracking_specifics.pb.h"
+#include "sync/protocol/sync.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace browser_sync {
-class SyncFaviconCacheTest : public testing::Test {
+namespace {
+
+// Total number of favicons to use in sync tests.
+const int kTotalFavicons = 10;
+
+// TestChangeProcessor --------------------------------------------------------
+
+// Dummy SyncChangeProcessor used to help review what SyncChanges are pushed
+// back up to Sync.
+class TestChangeProcessor : public syncer::SyncChangeProcessor {
public:
- SyncFaviconCacheTest() : cache_(NULL) {
+ TestChangeProcessor();
+ virtual ~TestChangeProcessor();
+
+ // Store a copy of all the changes passed in so we can examine them later.
+ virtual syncer::SyncError ProcessSyncChanges(
+ const tracked_objects::Location& from_here,
+ const syncer::SyncChangeList& change_list) OVERRIDE;
+
+ bool contains_guid(const std::string& guid) const {
+ return change_map_.count(guid) != 0;
}
- virtual ~SyncFaviconCacheTest() {}
- size_t GetFaviconCount() {
- return cache_.NumFaviconsForTest();
- }
- bool ExpectFaviconEquals(std::string page_url, std::string bytes) {
- GURL gurl(page_url);
- scoped_refptr<base::RefCountedMemory> favicon;
- if (!cache_.GetSyncedFaviconForPageURL(gurl, &favicon))
- return false;
- if (favicon->size() != bytes.size())
- return false;
- for (size_t i = 0; i < favicon->size(); ++i) {
- if (bytes[i] != *(favicon->front() + i))
- return false;
+ syncer::SyncChange change_for_guid(const std::string& guid) const {
+ DCHECK(contains_guid(guid));
+ return change_map_.find(guid)->second;
+ }
+
+ // Returns the last change list received, and resets the internal list.
+ syncer::SyncChangeList GetAndResetChangeList() {
+ syncer::SyncChangeList list;
+ list.swap(change_list_);
+ return list;
+ }
+
+ void set_erroneous(bool erroneous) { erroneous_ = erroneous; }
+
+ private:
+ // Track the changes received in ProcessSyncChanges.
+ std::map<std::string, syncer::SyncChange> change_map_;
+ syncer::SyncChangeList change_list_;
+ bool erroneous_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestChangeProcessor);
+};
+
+TestChangeProcessor::TestChangeProcessor() : erroneous_(false) {
+}
+
+TestChangeProcessor::~TestChangeProcessor() {
+}
+
+syncer::SyncError TestChangeProcessor::ProcessSyncChanges(
+ const tracked_objects::Location& from_here,
+ const syncer::SyncChangeList& change_list) {
+ if (erroneous_) {
+ return syncer::SyncError(
+ FROM_HERE, "Some error.", change_list[0].sync_data().GetDataType());
+ }
+
+ change_list_ = change_list;
+ change_map_.erase(change_map_.begin(), change_map_.end());
+ for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
+ iter != change_list.end(); ++iter) {
+ change_map_[iter->sync_data().GetTitle()] = *iter;
+ }
+ return syncer::SyncError();
+}
+
+
+// SyncChangeProcessorDelegate ------------------------------------------------
+
+class SyncChangeProcessorDelegate : public syncer::SyncChangeProcessor {
+ public:
+ explicit SyncChangeProcessorDelegate(syncer::SyncChangeProcessor* recipient);
+ virtual ~SyncChangeProcessorDelegate();
+
+ // syncer::SyncChangeProcessor implementation.
+ virtual syncer::SyncError ProcessSyncChanges(
+ const tracked_objects::Location& from_here,
+ const syncer::SyncChangeList& change_list) OVERRIDE;
+
+ private:
+ // The recipient of all sync changes.
+ syncer::SyncChangeProcessor* recipient_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncChangeProcessorDelegate);
+};
+
+SyncChangeProcessorDelegate::SyncChangeProcessorDelegate(
+ syncer::SyncChangeProcessor* recipient)
+ : recipient_(recipient) {
+ DCHECK(recipient_);
+}
+
+SyncChangeProcessorDelegate::~SyncChangeProcessorDelegate() {
+}
+
+syncer::SyncError SyncChangeProcessorDelegate::ProcessSyncChanges(
+ const tracked_objects::Location& from_here,
+ const syncer::SyncChangeList& change_list) {
+ return recipient_->ProcessSyncChanges(from_here, change_list);
+}
+
+// TestFaviconData ------------------------------------------------------------
+struct TestFaviconData {
+ TestFaviconData() : last_visit_time(0), is_bookmarked(false) {}
+ GURL page_url;
+ GURL icon_url;
+ std::string image_16;
+ std::string image_32;
+ std::string image_64;
+ int last_visit_time;
+ bool is_bookmarked;
+};
+
+TestFaviconData BuildFaviconData(int index) {
+ TestFaviconData data;
+ data.page_url = GURL(base::StringPrintf("http://bla.com/%i.html", index));
+ data.icon_url = GURL(base::StringPrintf("http://bla.com/%i.ico", index));
+ data.image_16 = base::StringPrintf("16 %i", index);
+ // TODO(zea): enable this once the cache supports writing them.
+ // data.image_32 = base::StringPrintf("32 %i", index);
+ // data.image_64 = base::StringPrintf("64 %i", index);
+ data.last_visit_time = index;
+ return data;
+}
+
+void FillImageSpecifics(
+ const TestFaviconData& test_data,
+ sync_pb::FaviconImageSpecifics* image_specifics) {
+ image_specifics->set_favicon_url(test_data.icon_url.spec());
+ if (!test_data.image_16.empty()) {
+ image_specifics->mutable_favicon_web()->set_height(16);
+ image_specifics->mutable_favicon_web()->set_width(16);
+ image_specifics->mutable_favicon_web()->set_favicon(test_data.image_16);
+ }
+ if (!test_data.image_32.empty()) {
+ image_specifics->mutable_favicon_web_32()->set_height(32);
+ image_specifics->mutable_favicon_web_32()->set_width(32);
+ image_specifics->mutable_favicon_web_32()->set_favicon(test_data.image_32);
+ }
+ if (!test_data.image_64.empty()) {
+ image_specifics->mutable_favicon_touch_64()->set_height(64);
+ image_specifics->mutable_favicon_touch_64()->set_width(64);
+ image_specifics->mutable_favicon_touch_64()->
+ set_favicon(test_data.image_64);
+ }
+}
+
+void FillTrackingSpecifics(
+ const TestFaviconData& test_data,
+ sync_pb::FaviconTrackingSpecifics* tracking_specifics) {
+ tracking_specifics->set_favicon_url(test_data.icon_url.spec());
+ tracking_specifics->set_last_visit_time_ms(test_data.last_visit_time);
+ tracking_specifics->set_is_bookmarked(test_data.is_bookmarked);
+}
+
+testing::AssertionResult CompareFaviconDataToSpecifics(
+ const TestFaviconData& test_data,
+ const sync_pb::EntitySpecifics& specifics) {
+ if (specifics.has_favicon_image()) {
+ sync_pb::FaviconImageSpecifics image_specifics = specifics.favicon_image();
+ if (image_specifics.favicon_url() != test_data.icon_url.spec())
+ return testing::AssertionFailure() << "Image icon url doesn't match.";
+ if (!test_data.image_16.empty()) {
+ if (image_specifics.favicon_web().favicon() != test_data.image_16 ||
+ image_specifics.favicon_web().height() != 16 ||
+ image_specifics.favicon_web().width() != 16) {
+ return testing::AssertionFailure() << "16p image data doesn't match.";
+ }
+ } else if (image_specifics.has_favicon_web()) {
+ return testing::AssertionFailure() << "Missing 16p favicon.";
+ }
+ if (!test_data.image_32.empty()) {
+ if (image_specifics.favicon_web_32().favicon() != test_data.image_32 ||
+ image_specifics.favicon_web().height() != 32 ||
+ image_specifics.favicon_web().width() != 32) {
+ return testing::AssertionFailure() << "32p image data doesn't match.";
+ }
+ } else if (image_specifics.has_favicon_web_32()) {
+ return testing::AssertionFailure() << "Missing 32p favicon.";
+ }
+ if (!test_data.image_64.empty()) {
+ if (image_specifics.favicon_touch_64().favicon() != test_data.image_64 ||
+ image_specifics.favicon_web().height() != 64 ||
+ image_specifics.favicon_web().width() != 64) {
+ return testing::AssertionFailure() << "64p image data doesn't match.";
+ }
+ } else if (image_specifics.has_favicon_touch_64()) {
+ return testing::AssertionFailure() << "Missing 64p favicon.";
}
- return true;
+ } else {
+ sync_pb::FaviconTrackingSpecifics tracking_specifics =
+ specifics.favicon_tracking();
+ if (tracking_specifics.favicon_url() != test_data.icon_url.spec())
+ return testing::AssertionFailure() << "Tracking icon url doesn't match.";
+ if (tracking_specifics.last_visit_time_ms() != test_data.last_visit_time)
+ return testing::AssertionFailure() << "Visit time doesn't match.";
+ if (tracking_specifics.is_bookmarked() != test_data.is_bookmarked)
+ return testing::AssertionFailure() << "Bookmark status doens't match.";
+ }
+ return testing::AssertionSuccess();
+}
+
+testing::AssertionResult VerifyChanges(
+ syncer::ModelType expected_model_type,
+ const std::vector<syncer::SyncChange::SyncChangeType>&
+ expected_change_types,
+ const std::vector<int>& expected_icons,
+ const syncer::SyncChangeList& change_list) {
+ DCHECK_EQ(expected_change_types.size(), expected_icons.size());
+ if (change_list.size() != expected_icons.size())
+ return testing::AssertionFailure() << "Change list size doesn't match.";
+ for (size_t i = 0; i < expected_icons.size(); ++i) {
+ TestFaviconData data = BuildFaviconData(expected_icons[i]);
+ if (change_list[i].sync_data().GetDataType() != expected_model_type)
+ return testing::AssertionFailure() << "Change datatype doesn't match.";
+ if (change_list[i].change_type() != expected_change_types[i])
+ return testing::AssertionFailure() << "Change type doesn't match.";
+ testing::AssertionResult compare_result =
+ CompareFaviconDataToSpecifics(
+ BuildFaviconData(expected_icons[i]),
+ change_list[i].sync_data().GetSpecifics());
+ if (compare_result.failure_message())
+ return compare_result;
}
+ return testing::AssertionSuccess();
+}
+
+} // namespace
+
+class SyncFaviconCacheTest : public testing::Test {
+ public:
+ SyncFaviconCacheTest();
+ virtual ~SyncFaviconCacheTest() {}
+
+ void SetUpInitialSync(const syncer::SyncDataList& initial_image_data,
+ const syncer::SyncDataList& initial_tracking_data);
+
+ size_t GetFaviconCount() const;
+ testing::AssertionResult ExpectFaviconEquals(
+ const std::string& page_url,
+ const std::string& bytes) const;
+ testing::AssertionResult VerifyLocalIcons(
+ const std::vector<int>& expected_icons);
+ testing::AssertionResult VerifyLocalCustomIcons(
+ const std::vector<TestFaviconData>& expected_icons);
+
+ scoped_ptr<syncer::SyncChangeProcessor> CreateAndPassProcessor();
+ scoped_ptr<syncer::SyncErrorFactory> CreateAndPassSyncErrorFactory();
+
FaviconCache* cache() { return &cache_; }
+ TestChangeProcessor* processor() { return sync_processor_.get(); }
private:
FaviconCache cache_;
+
+ // Our dummy ChangeProcessor used to inspect changes pushed to Sync.
+ scoped_ptr<TestChangeProcessor> sync_processor_;
+ scoped_ptr<SyncChangeProcessorDelegate> sync_processor_delegate_;
};
+SyncFaviconCacheTest::SyncFaviconCacheTest()
+ : cache_(NULL),
+ sync_processor_(new TestChangeProcessor),
+ sync_processor_delegate_(new SyncChangeProcessorDelegate(
+ sync_processor_.get())) {
+}
+
+void SyncFaviconCacheTest::SetUpInitialSync(
+ const syncer::SyncDataList& initial_image_data,
+ const syncer::SyncDataList& initial_tracking_data) {
+ cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
+ initial_image_data,
+ CreateAndPassProcessor(),
+ CreateAndPassSyncErrorFactory());
+ ASSERT_EQ(0U, processor()->GetAndResetChangeList().size());
+ cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
+ initial_tracking_data,
+ CreateAndPassProcessor(),
+ CreateAndPassSyncErrorFactory());
+ ASSERT_EQ(0U, processor()->GetAndResetChangeList().size());
+}
+
+size_t SyncFaviconCacheTest::GetFaviconCount() const {
+ return cache_.NumFaviconsForTest();
+}
+
+testing::AssertionResult SyncFaviconCacheTest::ExpectFaviconEquals(
+ const std::string& page_url,
+ const std::string& bytes) const {
+ GURL gurl(page_url);
+ scoped_refptr<base::RefCountedMemory> favicon;
+ if (!cache_.GetSyncedFaviconForPageURL(gurl, &favicon))
+ return testing::AssertionFailure() << "Favicon is missing.";
+ if (favicon->size() != bytes.size())
+ return testing::AssertionFailure() << "Favicon sizes don't match.";
+ for (size_t i = 0; i < favicon->size(); ++i) {
+ if (bytes[i] != *(favicon->front() + i))
+ return testing::AssertionFailure() << "Favicon data doesn't match.";
+ }
+ return testing::AssertionSuccess();
+}
+
+testing::AssertionResult SyncFaviconCacheTest::VerifyLocalIcons(
+ const std::vector<int>& expected_icons) {
+ std::vector<TestFaviconData> expected_custom_icons;
+ for (size_t i = 0; i < expected_icons.size(); ++i) {
+ expected_custom_icons.push_back(BuildFaviconData(expected_icons[i]));
+ }
+ return VerifyLocalCustomIcons(expected_custom_icons);
+}
+
+
+testing::AssertionResult SyncFaviconCacheTest::VerifyLocalCustomIcons(
+ const std::vector<TestFaviconData>& expected_custom_icons) {
+ syncer::SyncDataList image_data_list =
+ cache()->GetAllSyncData(syncer::FAVICON_IMAGES);
+ syncer::SyncDataList tracking_data_list =
+ cache()->GetAllSyncData(syncer::FAVICON_TRACKING);
+ if (expected_custom_icons.size() > image_data_list.size() ||
+ expected_custom_icons.size() > tracking_data_list.size())
+ return testing::AssertionFailure() << "Number of icons doesn't match.";
+ for (size_t i = 0; i < expected_custom_icons.size(); ++i) {
+ const TestFaviconData& test_data = expected_custom_icons[i];
+ // Find the test data in the data lists. Assume that both lists have the
+ // same ordering, which may not match the |expected_custom_icons| ordering.
+ bool found_match = false;
+ for (size_t j = 0; j < image_data_list.size(); ++j) {
+ if (image_data_list[j].GetTitle() != test_data.icon_url.spec())
+ continue;
+ found_match = true;
+ const sync_pb::FaviconImageSpecifics& image_specifics =
+ image_data_list[j].GetSpecifics().favicon_image();
+ sync_pb::FaviconImageSpecifics expected_image_specifics;
+ FillImageSpecifics(test_data, &expected_image_specifics);
+ if (image_specifics.SerializeAsString() !=
+ expected_image_specifics.SerializeAsString()) {
+ return testing::AssertionFailure() << "Image data doesn't match.";
+ }
+ const sync_pb::FaviconTrackingSpecifics& tracking_specifics =
+ tracking_data_list[j].GetSpecifics().favicon_tracking();
+ sync_pb::FaviconTrackingSpecifics expected_tracking_specifics;
+ FillTrackingSpecifics(test_data, &expected_tracking_specifics);
+ if (tracking_specifics.SerializeAsString() !=
+ expected_tracking_specifics.SerializeAsString()) {
+ return testing::AssertionFailure() << "Tracking data doesn't match.";
+ }
+ }
+ if (!found_match)
+ return testing::AssertionFailure() << "Could not find favicon.";
+ }
+ return testing::AssertionSuccess();
+}
+
+scoped_ptr<syncer::SyncChangeProcessor>
+SyncFaviconCacheTest::CreateAndPassProcessor() {
+ return scoped_ptr<syncer::SyncChangeProcessor>(
+ new SyncChangeProcessorDelegate(sync_processor_.get()));
+}
+
+scoped_ptr<syncer::SyncErrorFactory> SyncFaviconCacheTest::
+ CreateAndPassSyncErrorFactory() {
+ return scoped_ptr<syncer::SyncErrorFactory>(
+ new syncer::SyncErrorFactoryMock());
+}
+
// A freshly constructed cache should be empty.
TEST_F(SyncFaviconCacheTest, Empty) {
EXPECT_EQ(0U, GetFaviconCount());
@@ -46,7 +391,7 @@ TEST_F(SyncFaviconCacheTest, ReceiveSyncFavicon) {
std::string fav_url = "http://www.google.com/favicon.ico";
std::string bytes = "bytes";
EXPECT_EQ(0U, GetFaviconCount());
- cache()->OnReceivedSyncFavicon(GURL(page_url), GURL(fav_url), bytes);
+ cache()->OnReceivedSyncFavicon(GURL(page_url), GURL(fav_url), bytes, 0);
EXPECT_EQ(1U, GetFaviconCount());
EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes));
}
@@ -56,12 +401,15 @@ TEST_F(SyncFaviconCacheTest, ReceiveEmptySyncFavicon) {
std::string fav_url = "http://www.google.com/favicon.ico";
std::string bytes = "bytes";
EXPECT_EQ(0U, GetFaviconCount());
- cache()->OnReceivedSyncFavicon(GURL(page_url), GURL(fav_url), std::string());
+ cache()->OnReceivedSyncFavicon(GURL(page_url),
+ GURL(fav_url),
+ std::string(),
+ 0);
EXPECT_EQ(0U, GetFaviconCount());
EXPECT_FALSE(ExpectFaviconEquals(page_url, std::string()));
// Then receive the actual favicon.
- cache()->OnReceivedSyncFavicon(GURL(page_url), GURL(fav_url), bytes);
+ cache()->OnReceivedSyncFavicon(GURL(page_url), GURL(fav_url), bytes, 0);
EXPECT_EQ(1U, GetFaviconCount());
EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes));
}
@@ -72,13 +420,13 @@ TEST_F(SyncFaviconCacheTest, ReceiveUpdatedSyncFavicon) {
std::string bytes = "bytes";
std::string bytes2 = "bytes2";
EXPECT_EQ(0U, GetFaviconCount());
- cache()->OnReceivedSyncFavicon(GURL(page_url), GURL(fav_url), bytes);
+ cache()->OnReceivedSyncFavicon(GURL(page_url), GURL(fav_url), bytes, 0);
EXPECT_EQ(1U, GetFaviconCount());
EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes));
// The cache should not update existing favicons from tab sync favicons
// (which can be reassociated several times).
- cache()->OnReceivedSyncFavicon(GURL(page_url), GURL(fav_url), bytes2);
+ cache()->OnReceivedSyncFavicon(GURL(page_url), GURL(fav_url), bytes2, 0);
EXPECT_EQ(1U, GetFaviconCount());
EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes));
EXPECT_FALSE(ExpectFaviconEquals(page_url, bytes2));
@@ -90,14 +438,562 @@ TEST_F(SyncFaviconCacheTest, MultipleMappings) {
std::string fav_url = "http://www.google.com/favicon.ico";
std::string bytes = "bytes";
EXPECT_EQ(0U, GetFaviconCount());
- cache()->OnReceivedSyncFavicon(GURL(page_url), GURL(fav_url), bytes);
+ cache()->OnReceivedSyncFavicon(GURL(page_url), GURL(fav_url), bytes, 0);
EXPECT_EQ(1U, GetFaviconCount());
EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes));
// Map another page to the same favicon. They should share the same data.
- cache()->OnReceivedSyncFavicon(GURL(page2_url), GURL(fav_url), bytes);
+ cache()->OnReceivedSyncFavicon(GURL(page2_url), GURL(fav_url), bytes, 0);
EXPECT_EQ(1U, GetFaviconCount());
EXPECT_TRUE(ExpectFaviconEquals(page2_url, bytes));
}
+TEST_F(SyncFaviconCacheTest, SyncEmpty) {
+ syncer::SyncMergeResult merge_result =
+ cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
+ syncer::SyncDataList(),
+ CreateAndPassProcessor(),
+ CreateAndPassSyncErrorFactory());
+
+ EXPECT_EQ(0U, cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size());
+ EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
+ EXPECT_EQ(0, merge_result.num_items_added());
+ EXPECT_EQ(0, merge_result.num_items_modified());
+ EXPECT_EQ(0, merge_result.num_items_deleted());
+ EXPECT_EQ(0, merge_result.num_items_before_association());
+ EXPECT_EQ(0, merge_result.num_items_after_association());
+
+ merge_result =
+ cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
+ syncer::SyncDataList(),
+ CreateAndPassProcessor(),
+ CreateAndPassSyncErrorFactory());
+
+ EXPECT_EQ(0U, cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size());
+ EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
+ EXPECT_EQ(0, merge_result.num_items_added());
+ EXPECT_EQ(0, merge_result.num_items_modified());
+ EXPECT_EQ(0, merge_result.num_items_deleted());
+ EXPECT_EQ(0, merge_result.num_items_before_association());
+ EXPECT_EQ(0, merge_result.num_items_after_association());
+}
+
+// Setting up sync with existing local favicons should push those favicons into
+// sync.
+TEST_F(SyncFaviconCacheTest, SyncExistingLocal) {
+ std::vector<syncer::SyncChange::SyncChangeType> expected_change_types;
+ std::vector<int> expected_icons;
+ for (int i = 0; i < kTotalFavicons; ++i) {
+ TestFaviconData favicon = BuildFaviconData(i);
+ cache()->OnReceivedSyncFavicon(favicon.page_url,
+ favicon.icon_url,
+ favicon.image_16,
+ i);
+ expected_change_types.push_back(syncer::SyncChange::ACTION_ADD);
+ expected_icons.push_back(i);
+ }
+
+ syncer::SyncMergeResult merge_result =
+ cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
+ syncer::SyncDataList(),
+ CreateAndPassProcessor(),
+ CreateAndPassSyncErrorFactory());
+ EXPECT_EQ((unsigned long)kTotalFavicons,
+ cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size());
+ syncer::SyncChangeList change_list = processor()->GetAndResetChangeList();
+ EXPECT_TRUE(VerifyChanges(syncer::FAVICON_IMAGES,
+ expected_change_types,
+ expected_icons,
+ change_list));
+ EXPECT_EQ(0, merge_result.num_items_added());
+ EXPECT_EQ(0, merge_result.num_items_modified());
+ EXPECT_EQ(0, merge_result.num_items_deleted());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_before_association());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_after_association());
+
+ merge_result =
+ cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
+ syncer::SyncDataList(),
+ CreateAndPassProcessor(),
+ CreateAndPassSyncErrorFactory());
+ EXPECT_EQ((unsigned long)kTotalFavicons,
+ cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size());
+ change_list = processor()->GetAndResetChangeList();
+ EXPECT_TRUE(VerifyChanges(syncer::FAVICON_TRACKING,
+ expected_change_types,
+ expected_icons,
+ change_list));
+ EXPECT_EQ(0, merge_result.num_items_added());
+ EXPECT_EQ(0, merge_result.num_items_modified());
+ EXPECT_EQ(0, merge_result.num_items_deleted());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_before_association());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_after_association());
+}
+
+// Setting up sync with existing sync data should load that data into the local
+// cache.
+TEST_F(SyncFaviconCacheTest, SyncExistingRemote) {
+ syncer::SyncDataList initial_image_data, initial_tracking_data;
+ std::vector<int> expected_icons;
+ for (int i = 0; i < kTotalFavicons; ++i) {
+ expected_icons.push_back(i);
+ sync_pb::EntitySpecifics image_specifics, tracking_specifics;
+ FillImageSpecifics(BuildFaviconData(i),
+ image_specifics.mutable_favicon_image());
+ initial_image_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ image_specifics));
+ FillTrackingSpecifics(BuildFaviconData(i),
+ tracking_specifics.mutable_favicon_tracking());
+ initial_tracking_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ tracking_specifics));
+ }
+
+ syncer::SyncMergeResult merge_result =
+ cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
+ initial_image_data,
+ CreateAndPassProcessor(),
+ CreateAndPassSyncErrorFactory());
+ EXPECT_EQ((unsigned long)kTotalFavicons,
+ cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size());
+ EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_added());
+ EXPECT_EQ(0, merge_result.num_items_modified());
+ EXPECT_EQ(0, merge_result.num_items_deleted());
+ EXPECT_EQ(0, merge_result.num_items_before_association());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_after_association());
+
+ merge_result =
+ cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
+ initial_tracking_data,
+ CreateAndPassProcessor(),
+ CreateAndPassSyncErrorFactory());
+ EXPECT_EQ((unsigned long)kTotalFavicons,
+ cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size());
+ EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
+ EXPECT_EQ(0, merge_result.num_items_added());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_modified());
+ EXPECT_EQ(0, merge_result.num_items_deleted());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_before_association());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_after_association());
+
+ ASSERT_TRUE(VerifyLocalIcons(expected_icons));
+}
+
+// Setting up sync with local data and sync data should merge the two image
+// sets, with remote data having priority in case both exist.
+TEST_F(SyncFaviconCacheTest, SyncMergesImages) {
+ // First go through and add local 16p favicons.
+ for (int i = 0; i < kTotalFavicons; ++i) {
+ TestFaviconData favicon = BuildFaviconData(i);
+ cache()->OnReceivedSyncFavicon(favicon.page_url,
+ favicon.icon_url,
+ favicon.image_16,
+ i);
+ }
+
+ // Then go through and create the initial sync data, which does not have 16p
+ // favicons for the first half, and has custom 16p favicons for the second.
+ std::vector<syncer::SyncChange::SyncChangeType> expected_change_types;
+ std::vector<int> expected_icons;
+ std::vector<TestFaviconData> expected_data;
+ syncer::SyncDataList initial_image_data, initial_tracking_data;
+ for (int i = 0; i < kTotalFavicons; ++i) {
+ sync_pb::EntitySpecifics image_specifics, tracking_specifics;
+ TestFaviconData test_data = BuildFaviconData(i);
+ if (i < kTotalFavicons/2) {
+ test_data.image_16 = std::string();
+ expected_icons.push_back(i);
+ expected_change_types.push_back(syncer::SyncChange::ACTION_UPDATE);
+ } else {
+ test_data.image_16 += "custom";
+ expected_data.push_back(test_data);
+ }
+ FillImageSpecifics(test_data,
+ image_specifics.mutable_favicon_image());
+
+ initial_image_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ image_specifics));
+ FillTrackingSpecifics(test_data,
+ tracking_specifics.mutable_favicon_tracking());
+ initial_tracking_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ tracking_specifics));
+ }
+
+ syncer::SyncMergeResult merge_result =
+ cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
+ initial_image_data,
+ CreateAndPassProcessor(),
+ CreateAndPassSyncErrorFactory());
+ EXPECT_EQ((unsigned long)kTotalFavicons,
+ cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size());
+ syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
+ EXPECT_EQ((unsigned long)kTotalFavicons/2, changes.size());
+ EXPECT_EQ(0, merge_result.num_items_added());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_modified());
+ EXPECT_EQ(0, merge_result.num_items_deleted());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_before_association());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_after_association());
+
+ merge_result =
+ cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
+ initial_tracking_data,
+ CreateAndPassProcessor(),
+ CreateAndPassSyncErrorFactory());
+ EXPECT_EQ((unsigned long)kTotalFavicons,
+ cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size());
+ EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
+ EXPECT_EQ(0, merge_result.num_items_added());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_modified());
+ EXPECT_EQ(0, merge_result.num_items_deleted());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_before_association());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_after_association());
+
+ ASSERT_TRUE(VerifyLocalIcons(expected_icons));
+ ASSERT_TRUE(VerifyLocalCustomIcons(expected_data));
+ ASSERT_TRUE(VerifyChanges(syncer::FAVICON_IMAGES,
+ expected_change_types,
+ expected_icons,
+ changes));
+}
+
+// Setting up sync with local data and sync data should merge the two tracking
+// sets, such that the visit time is the most recent.
+TEST_F(SyncFaviconCacheTest, SyncMergesTracking) {
+ // First go through and add local 16p favicons.
+ for (int i = 0; i < kTotalFavicons; ++i) {
+ TestFaviconData favicon = BuildFaviconData(i);
+ cache()->OnReceivedSyncFavicon(favicon.page_url,
+ favicon.icon_url,
+ favicon.image_16,
+ i);
+ }
+
+ // Then go through and create the initial sync data, which for the first half
+ // the local has a newer visit, and for the second the remote does.
+ std::vector<syncer::SyncChange::SyncChangeType> expected_change_types;
+ std::vector<int> expected_icons;
+ std::vector<TestFaviconData> expected_data;
+ syncer::SyncDataList initial_image_data, initial_tracking_data;
+ for (int i = 0; i < kTotalFavicons; ++i) {
+ sync_pb::EntitySpecifics image_specifics, tracking_specifics;
+ TestFaviconData test_data = BuildFaviconData(i);
+ if (i < kTotalFavicons/2) {
+ test_data.last_visit_time = i-1;
+ expected_icons.push_back(i);
+ expected_change_types.push_back(syncer::SyncChange::ACTION_UPDATE);
+ } else {
+ test_data.last_visit_time = i+1;
+ expected_data.push_back(test_data);
+ }
+ FillImageSpecifics(test_data,
+ image_specifics.mutable_favicon_image());
+
+ initial_image_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ image_specifics));
+ FillTrackingSpecifics(test_data,
+ tracking_specifics.mutable_favicon_tracking());
+ initial_tracking_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ tracking_specifics));
+ }
+
+ syncer::SyncMergeResult merge_result =
+ cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
+ initial_image_data,
+ CreateAndPassProcessor(),
+ CreateAndPassSyncErrorFactory());
+ EXPECT_EQ((unsigned long)kTotalFavicons,
+ cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size());
+ EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
+ EXPECT_EQ(0, merge_result.num_items_added());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_modified());
+ EXPECT_EQ(0, merge_result.num_items_deleted());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_before_association());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_after_association());
+
+ merge_result =
+ cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
+ initial_tracking_data,
+ CreateAndPassProcessor(),
+ CreateAndPassSyncErrorFactory());
+ EXPECT_EQ((unsigned long)kTotalFavicons,
+ cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size());
+ syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
+ EXPECT_EQ((unsigned long)kTotalFavicons/2, changes.size());
+ EXPECT_EQ(0, merge_result.num_items_added());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_modified());
+ EXPECT_EQ(0, merge_result.num_items_deleted());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_before_association());
+ EXPECT_EQ(kTotalFavicons, merge_result.num_items_after_association());
+
+ ASSERT_TRUE(VerifyLocalIcons(expected_icons));
+ ASSERT_TRUE(VerifyLocalCustomIcons(expected_data));
+ ASSERT_TRUE(VerifyChanges(syncer::FAVICON_TRACKING,
+ expected_change_types,
+ expected_icons,
+ changes));
+}
+
+// Receiving old icons (missing image data) should result in pushing the new
+// merged icons back to the remote syncer.
+TEST_F(SyncFaviconCacheTest, ReceiveStaleImages) {
+ syncer::SyncDataList initial_image_data, initial_tracking_data;
+ syncer::SyncChangeList stale_changes;
+ std::vector<int> expected_icons;
+ std::vector<syncer::SyncChange::SyncChangeType> expected_change_types;
+ for (int i = 0; i < kTotalFavicons; ++i) {
+ expected_icons.push_back(i);
+ sync_pb::EntitySpecifics image_specifics, tracking_specifics;
+ FillImageSpecifics(BuildFaviconData(i),
+ image_specifics.mutable_favicon_image());
+ initial_image_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ image_specifics));
+ expected_change_types.push_back(syncer::SyncChange::ACTION_UPDATE);
+ image_specifics.mutable_favicon_image()->clear_favicon_web();
+ stale_changes.push_back(
+ syncer::SyncChange(
+ FROM_HERE,
+ syncer::SyncChange::ACTION_UPDATE,
+ syncer::SyncData::CreateRemoteData(1, image_specifics)));
+ FillTrackingSpecifics(BuildFaviconData(i),
+ tracking_specifics.mutable_favicon_tracking());
+ initial_tracking_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ tracking_specifics));
+ }
+
+ SetUpInitialSync(initial_image_data, initial_tracking_data);
+
+ // Now receive the same icons as an update, but with missing image data.
+ cache()->ProcessSyncChanges(FROM_HERE, stale_changes);
+ syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
+ ASSERT_TRUE(VerifyLocalIcons(expected_icons));
+ ASSERT_EQ((unsigned long)kTotalFavicons, changes.size());
+ ASSERT_TRUE(VerifyChanges(syncer::FAVICON_IMAGES,
+ expected_change_types,
+ expected_icons,
+ changes));
+}
+
+// New icons should be added locally without pushing anything back to the
+// remote syncer.
+TEST_F(SyncFaviconCacheTest, ReceiveNewImages) {
+ syncer::SyncDataList initial_image_data, initial_tracking_data;
+ syncer::SyncChangeList new_changes;
+ std::vector<int> expected_icons;
+ for (int i = 0; i < kTotalFavicons; ++i) {
+ expected_icons.push_back(i);
+ TestFaviconData test_data = BuildFaviconData(i);
+ sync_pb::EntitySpecifics image_specifics, tracking_specifics;
+ FillImageSpecifics(test_data,
+ image_specifics.mutable_favicon_image());
+ new_changes.push_back(
+ syncer::SyncChange(
+ FROM_HERE,
+ syncer::SyncChange::ACTION_UPDATE,
+ syncer::SyncData::CreateRemoteData(1, image_specifics)));
+ image_specifics.mutable_favicon_image()->mutable_favicon_web()->
+ mutable_favicon()->append("old");
+ initial_image_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ image_specifics));
+ FillTrackingSpecifics(BuildFaviconData(i),
+ tracking_specifics.mutable_favicon_tracking());
+ initial_tracking_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ tracking_specifics));
+ }
+
+ SetUpInitialSync(initial_image_data, initial_tracking_data);
+
+ // Now receive the new icons as an update.
+ cache()->ProcessSyncChanges(FROM_HERE, new_changes);
+ EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
+ ASSERT_TRUE(VerifyLocalIcons(expected_icons));
+}
+
+// Recieving the same icons as the local data should have no effect.
+TEST_F(SyncFaviconCacheTest, ReceiveSameImages) {
+ syncer::SyncDataList initial_image_data, initial_tracking_data;
+ syncer::SyncChangeList same_changes;
+ std::vector<int> expected_icons;
+ for (int i = 0; i < kTotalFavicons; ++i) {
+ expected_icons.push_back(i);
+ TestFaviconData test_data = BuildFaviconData(i);
+ sync_pb::EntitySpecifics image_specifics, tracking_specifics;
+ FillImageSpecifics(test_data,
+ image_specifics.mutable_favicon_image());
+ same_changes.push_back(
+ syncer::SyncChange(
+ FROM_HERE,
+ syncer::SyncChange::ACTION_UPDATE,
+ syncer::SyncData::CreateRemoteData(1, image_specifics)));
+ initial_image_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ image_specifics));
+ FillTrackingSpecifics(BuildFaviconData(i),
+ tracking_specifics.mutable_favicon_tracking());
+ initial_tracking_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ tracking_specifics));
+ }
+
+ SetUpInitialSync(initial_image_data, initial_tracking_data);
+
+ // Now receive the new icons as an update.
+ cache()->ProcessSyncChanges(FROM_HERE, same_changes);
+ EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
+ ASSERT_TRUE(VerifyLocalIcons(expected_icons));
+}
+
+// Receiving stale tracking (old visit times) should result in pushing back
+// the newer visit times to the remote syncer.
+TEST_F(SyncFaviconCacheTest, ReceiveStaleTracking) {
+ syncer::SyncDataList initial_image_data, initial_tracking_data;
+ syncer::SyncChangeList stale_changes;
+ std::vector<int> expected_icons;
+ std::vector<syncer::SyncChange::SyncChangeType> expected_change_types;
+ for (int i = 0; i < kTotalFavicons; ++i) {
+ expected_icons.push_back(i);
+ sync_pb::EntitySpecifics image_specifics, tracking_specifics;
+ FillImageSpecifics(BuildFaviconData(i),
+ image_specifics.mutable_favicon_image());
+ initial_image_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ image_specifics));
+ expected_change_types.push_back(syncer::SyncChange::ACTION_UPDATE);
+ FillTrackingSpecifics(BuildFaviconData(i),
+ tracking_specifics.mutable_favicon_tracking());
+ initial_tracking_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ tracking_specifics));
+ tracking_specifics.mutable_favicon_tracking()->set_last_visit_time_ms(-1);
+ stale_changes.push_back(
+ syncer::SyncChange(
+ FROM_HERE,
+ syncer::SyncChange::ACTION_UPDATE,
+ syncer::SyncData::CreateRemoteData(1, tracking_specifics)));
+ }
+
+ SetUpInitialSync(initial_image_data, initial_tracking_data);
+
+ // Now receive the same icons as an update, but with missing image data.
+ cache()->ProcessSyncChanges(FROM_HERE, stale_changes);
+ syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
+ ASSERT_TRUE(VerifyLocalIcons(expected_icons));
+ ASSERT_EQ((unsigned long)kTotalFavicons, changes.size());
+ ASSERT_TRUE(VerifyChanges(syncer::FAVICON_TRACKING,
+ expected_change_types,
+ expected_icons,
+ changes));
+}
+
+// New tracking information should be added locally without pushing anything
+// back to the remote syncer.
+TEST_F(SyncFaviconCacheTest, ReceiveNewTracking) {
+ syncer::SyncDataList initial_image_data, initial_tracking_data;
+ syncer::SyncChangeList new_changes;
+ std::vector<int> expected_icons;
+ // We start from one here so that we don't have to deal with a -1 visit time.
+ for (int i = 1; i <= kTotalFavicons; ++i) {
+ expected_icons.push_back(i);
+ sync_pb::EntitySpecifics image_specifics, tracking_specifics;
+ FillImageSpecifics(BuildFaviconData(i),
+ image_specifics.mutable_favicon_image());
+ initial_image_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ image_specifics));
+ FillTrackingSpecifics(BuildFaviconData(i),
+ tracking_specifics.mutable_favicon_tracking());
+ new_changes.push_back(
+ syncer::SyncChange(
+ FROM_HERE,
+ syncer::SyncChange::ACTION_UPDATE,
+ syncer::SyncData::CreateRemoteData(1, tracking_specifics)));
+ tracking_specifics.mutable_favicon_tracking()->set_last_visit_time_ms(i-1);
+ initial_tracking_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ tracking_specifics));
+ }
+
+ SetUpInitialSync(initial_image_data, initial_tracking_data);
+
+ // Now receive the new icons as an update.
+ cache()->ProcessSyncChanges(FROM_HERE, new_changes);
+ EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
+ ASSERT_TRUE(VerifyLocalIcons(expected_icons));
+}
+
+// Receiving the same tracking information as the local data should have no
+// effect.
+TEST_F(SyncFaviconCacheTest, ReceiveSameTracking) {
+ syncer::SyncDataList initial_image_data, initial_tracking_data;
+ syncer::SyncChangeList same_changes;
+ std::vector<int> expected_icons;
+ for (int i = 0; i < kTotalFavicons; ++i) {
+ expected_icons.push_back(i);
+ sync_pb::EntitySpecifics image_specifics, tracking_specifics;
+ FillImageSpecifics(BuildFaviconData(i),
+ image_specifics.mutable_favicon_image());
+ initial_image_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ image_specifics));
+ FillTrackingSpecifics(BuildFaviconData(i),
+ tracking_specifics.mutable_favicon_tracking());
+ initial_tracking_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ tracking_specifics));
+ same_changes.push_back(
+ syncer::SyncChange(
+ FROM_HERE,
+ syncer::SyncChange::ACTION_UPDATE,
+ syncer::SyncData::CreateRemoteData(1, tracking_specifics)));
+ }
+
+ SetUpInitialSync(initial_image_data, initial_tracking_data);
+
+ // Now receive the new icons as an update.
+ cache()->ProcessSyncChanges(FROM_HERE, same_changes);
+ EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
+ ASSERT_TRUE(VerifyLocalIcons(expected_icons));
+}
+
+// Verify we can delete favicons after setting up sync.
+TEST_F(SyncFaviconCacheTest, DeleteFavicons) {
+ syncer::SyncDataList initial_image_data, initial_tracking_data;
+ syncer::SyncChangeList deletions;
+ for (int i = 0; i < kTotalFavicons; ++i) {
+ sync_pb::EntitySpecifics image_specifics, tracking_specifics;
+ FillImageSpecifics(BuildFaviconData(i),
+ image_specifics.mutable_favicon_image());
+ initial_image_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ image_specifics));
+ FillTrackingSpecifics(BuildFaviconData(i),
+ tracking_specifics.mutable_favicon_tracking());
+ initial_tracking_data.push_back(
+ syncer::SyncData::CreateRemoteData(1,
+ tracking_specifics));
+ deletions.push_back(
+ syncer::SyncChange(
+ FROM_HERE,
+ syncer::SyncChange::ACTION_DELETE,
+ syncer::SyncData::CreateRemoteData(1, tracking_specifics)));
+ }
+
+ SetUpInitialSync(initial_image_data, initial_tracking_data);
+
+ // Now receive the new icons as an update.
+ EXPECT_EQ((unsigned long)kTotalFavicons, GetFaviconCount());
+ cache()->ProcessSyncChanges(FROM_HERE, deletions);
+ EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
+ EXPECT_EQ(0U, GetFaviconCount());
+}
+
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/session_model_associator.cc b/chrome/browser/sync/glue/session_model_associator.cc
index 5b74fda..7904aca 100644
--- a/chrome/browser/sync/glue/session_model_associator.cc
+++ b/chrome/browser/sync/glue/session_model_associator.cc
@@ -900,7 +900,9 @@ void SessionModelAssociator::LoadForeignTabFavicon(
const std::string& favicon_url = tab.navigation(i).favicon_url();
favicon_cache_.OnReceivedSyncFavicon(GURL(page_url),
GURL(favicon_url),
- std::string());
+ std::string(),
+ syncer::TimeToProtoTime(
+ base::Time::Now()));
}
}
@@ -929,7 +931,11 @@ void SessionModelAssociator::LoadForeignTabFavicon(
const std::string& favicon = tab.favicon();
DVLOG(1) << "Storing synced favicon for url " << navigation_url.spec()
<< " with size " << favicon.size() << " bytes.";
- favicon_cache_.OnReceivedSyncFavicon(navigation_url, favicon_source, favicon);
+ favicon_cache_.OnReceivedSyncFavicon(navigation_url,
+ favicon_source,
+ favicon,
+ syncer::TimeToProtoTime(
+ base::Time::Now()));
}
bool SessionModelAssociator::UpdateSyncModelDataFromClient(
@@ -1113,7 +1119,7 @@ void SessionModelAssociator::QuitLoopForSubtleTesting() {
}
}
-FaviconCache* SessionModelAssociator::GetFaviconCacheForTesting() {
+FaviconCache* SessionModelAssociator::GetFaviconCache() {
return &favicon_cache_;
}
diff --git a/chrome/browser/sync/glue/session_model_associator.h b/chrome/browser/sync/glue/session_model_associator.h
index 0b3579f..b1c0f04 100644
--- a/chrome/browser/sync/glue/session_model_associator.h
+++ b/chrome/browser/sync/glue/session_model_associator.h
@@ -233,6 +233,8 @@ class SessionModelAssociator
current_machine_tag_ = machine_tag;
}
+ FaviconCache* GetFaviconCache();
+
private:
friend class SyncSessionModelAssociatorTest;
FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, WriteSessionToNode);
@@ -351,7 +353,6 @@ class SessionModelAssociator
// For testing only.
void QuitLoopForSubtleTesting();
- FaviconCache* GetFaviconCacheForTesting();
// Unique client tag.
std::string current_machine_tag_;
diff --git a/chrome/browser/sync/glue/session_model_associator_unittest.cc b/chrome/browser/sync/glue/session_model_associator_unittest.cc
index fe931d6..1ad13c5 100644
--- a/chrome/browser/sync/glue/session_model_associator_unittest.cc
+++ b/chrome/browser/sync/glue/session_model_associator_unittest.cc
@@ -61,7 +61,7 @@ class SyncSessionModelAssociatorTest : public testing::Test {
bool FaviconEquals(const GURL page_url,
std::string expected_bytes) {
- FaviconCache* cache = model_associator_.GetFaviconCacheForTesting();
+ FaviconCache* cache = model_associator_.GetFaviconCache();
GURL gurl(page_url);
scoped_refptr<base::RefCountedMemory> favicon;
if (!cache->GetSyncedFaviconForPageURL(gurl, &favicon))
diff --git a/chrome/browser/sync/profile_sync_components_factory_impl.cc b/chrome/browser/sync/profile_sync_components_factory_impl.cc
index 24e3ae0..ab61fda 100644
--- a/chrome/browser/sync/profile_sync_components_factory_impl.cc
+++ b/chrome/browser/sync/profile_sync_components_factory_impl.cc
@@ -51,6 +51,7 @@
#include "chrome/browser/sync/glue/ui_data_type_controller.h"
#include "chrome/browser/sync/profile_sync_components_factory_impl.h"
#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/themes/theme_syncable_service.h"
@@ -151,6 +152,19 @@ void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
new SessionDataTypeController(this, profile_, pss));
}
+ if (command_line_->HasSwitch(switches::kEnableSyncFavicons)) {
+ pss->RegisterDataTypeController(
+ new UIDataTypeController(syncer::FAVICON_IMAGES,
+ this,
+ profile_,
+ pss));
+ pss->RegisterDataTypeController(
+ new UIDataTypeController(syncer::FAVICON_TRACKING,
+ this,
+ profile_,
+ pss));
+ }
+
// Password sync is enabled by default. Register unless explicitly
// disabled.
if (!command_line_->HasSwitch(switches::kDisableSyncPasswords)) {
@@ -316,7 +330,6 @@ base::WeakPtr<syncer::SyncableService> ProfileSyncComponentsFactoryImpl::
profile_, Profile::EXPLICIT_ACCESS);
return history ? history->AsWeakPtr() : base::WeakPtr<HistoryService>();
}
-
#if !defined(OS_ANDROID)
case syncer::SYNCED_NOTIFICATIONS: {
notifier::ChromeNotifierService* notifier_service =
@@ -326,11 +339,13 @@ base::WeakPtr<syncer::SyncableService> ProfileSyncComponentsFactoryImpl::
: base::WeakPtr<syncer::SyncableService>();
}
#endif
-
case syncer::DICTIONARY:
return SpellcheckServiceFactory::GetForProfile(profile_)->
GetCustomDictionary()->AsWeakPtr();
-
+ case syncer::FAVICON_IMAGES:
+ case syncer::FAVICON_TRACKING:
+ return ProfileSyncServiceFactory::GetForProfile(profile_)->
+ GetSessionModelAssociator()->GetFaviconCache()->AsWeakPtr();
default:
// The following datatypes still need to be transitioned to the
// syncer::SyncableService API:
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index a4e91b3..7fe7a04 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -623,6 +623,9 @@ const char kEnableSuggestionsTabPage[] = "enable-suggestions-ntp";
const char kEnableSyncSyncedNotifications[] =
"enable-sync-synced-notifications";
+// Enables synced favicons
+const char kEnableSyncFavicons[] = "enable-sync-favicons";
+
// Enables context menu for selecting groups of tabs.
const char kEnableTabGroupsContextMenu[] = "enable-tab-groups-context-menu";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 66e2fa1..f04428f 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -176,6 +176,7 @@ extern const char kEnableSpellingAutoCorrect[];
extern const char kEnableStackedTabStrip[];
extern const char kEnableSuggestionsTabPage[];
extern const char kEnableSyncSyncedNotifications[];
+extern const char kEnableSyncFavicons[];
extern const char kEnableTabGroupsContextMenu[];
extern const char kEnableUserAlternateProtocolPorts[];
extern const char kEnableWatchdog[];