• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/sync/glue/favicon_cache.h"
6 
7 #include "base/message_loop/message_loop.h"
8 #include "base/metrics/histogram.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/favicon/favicon_service.h"
11 #include "chrome/browser/favicon/favicon_service_factory.h"
12 #include "chrome/browser/history/history_notifications.h"
13 #include "chrome/browser/history/history_types.h"
14 #include "content/public/browser/notification_details.h"
15 #include "content/public/browser/notification_source.h"
16 #include "sync/api/time.h"
17 #include "sync/protocol/favicon_image_specifics.pb.h"
18 #include "sync/protocol/favicon_tracking_specifics.pb.h"
19 #include "sync/protocol/sync.pb.h"
20 #include "ui/gfx/favicon_size.h"
21 
22 namespace browser_sync {
23 
24 // Synced favicon storage and tracking.
25 // Note: we don't use the favicon service for storing these because these
26 // favicons are not necessarily associated with any local navigation, and
27 // hence would not work with the current expiration logic. We have custom
28 // expiration logic based on visit time/bookmark status/etc.
29 // See crbug.com/122890.
30 struct SyncedFaviconInfo {
SyncedFaviconInfobrowser_sync::SyncedFaviconInfo31   explicit SyncedFaviconInfo(const GURL& favicon_url)
32       : favicon_url(favicon_url),
33         is_bookmarked(false),
34         received_local_update(false) {}
35 
36   // The actual favicon data.
37   // TODO(zea): don't keep around the actual data for locally sourced
38   // favicons (UI can access those directly).
39   favicon_base::FaviconRawBitmapResult bitmap_data[NUM_SIZES];
40   // The URL this favicon was loaded from.
41   const GURL favicon_url;
42   // Is the favicon for a bookmarked page?
43   bool is_bookmarked;
44   // The last time a tab needed this favicon.
45   // Note: Do not modify this directly! It should only be modified via
46   // UpdateFaviconVisitTime(..).
47   base::Time last_visit_time;
48   // Whether we've received a local update for this favicon since starting up.
49   bool received_local_update;
50 
51  private:
52   DISALLOW_COPY_AND_ASSIGN(SyncedFaviconInfo);
53 };
54 
55 // Information for handling local favicon updates. Used in
56 // OnFaviconDataAvailable.
57 struct LocalFaviconUpdateInfo {
LocalFaviconUpdateInfobrowser_sync::LocalFaviconUpdateInfo58   LocalFaviconUpdateInfo()
59       : new_image(false),
60         new_tracking(false),
61         image_needs_rewrite(false),
62         favicon_info(NULL) {}
63 
64   bool new_image;
65   bool new_tracking;
66   bool image_needs_rewrite;
67   SyncedFaviconInfo* favicon_info;
68 };
69 
70 namespace {
71 
72 // Maximum number of favicons to keep in memory (0 means no limit).
73 const size_t kMaxFaviconsInMem = 0;
74 
75 // Maximum width/height resolution supported.
76 const int kMaxFaviconResolution = 16;
77 
78 // Returns a mask of the supported favicon types.
79 // TODO(zea): Supporting other favicons types will involve some work in the
80 // favicon service and navigation controller. See crbug.com/181068.
SupportedFaviconTypes()81 int SupportedFaviconTypes() { return favicon_base::FAVICON; }
82 
83 // Returns the appropriate IconSize to use for a given gfx::Size pixel
84 // dimensions.
GetIconSizeBinFromBitmapResult(const gfx::Size & pixel_size)85 IconSize GetIconSizeBinFromBitmapResult(const gfx::Size& pixel_size) {
86   int max_size =
87       (pixel_size.width() > pixel_size.height() ?
88        pixel_size.width() : pixel_size.height());
89   // TODO(zea): re-enable 64p and 32p resolutions once we support them.
90   if (max_size > 64)
91     return SIZE_INVALID;
92   else if (max_size > 32)
93     return SIZE_INVALID;
94   else if (max_size > 16)
95     return SIZE_INVALID;
96   else
97     return SIZE_16;
98 }
99 
100 // Helper for debug statements.
IconSizeToString(IconSize icon_size)101 std::string IconSizeToString(IconSize icon_size) {
102   switch (icon_size) {
103     case SIZE_16:
104       return "16";
105     case SIZE_32:
106       return "32";
107     case SIZE_64:
108       return "64";
109     default:
110       return "INVALID";
111   }
112 }
113 
114 // Extract the favicon url from either of the favicon types.
GetFaviconURLFromSpecifics(const sync_pb::EntitySpecifics & specifics)115 GURL GetFaviconURLFromSpecifics(const sync_pb::EntitySpecifics& specifics) {
116   if (specifics.has_favicon_tracking())
117     return GURL(specifics.favicon_tracking().favicon_url());
118   else
119     return GURL(specifics.favicon_image().favicon_url());
120 }
121 
122 // Convert protobuf image data into a FaviconRawBitmapResult.
GetImageDataFromSpecifics(const sync_pb::FaviconData & favicon_data)123 favicon_base::FaviconRawBitmapResult GetImageDataFromSpecifics(
124     const sync_pb::FaviconData& favicon_data) {
125   base::RefCountedString* temp_string =
126       new base::RefCountedString();
127   temp_string->data() = favicon_data.favicon();
128   favicon_base::FaviconRawBitmapResult bitmap_result;
129   bitmap_result.bitmap_data = temp_string;
130   bitmap_result.pixel_size.set_height(favicon_data.height());
131   bitmap_result.pixel_size.set_width(favicon_data.width());
132   return bitmap_result;
133 }
134 
135 // Convert a FaviconRawBitmapResult into protobuf image data.
FillSpecificsWithImageData(const favicon_base::FaviconRawBitmapResult & bitmap_result,sync_pb::FaviconData * favicon_data)136 void FillSpecificsWithImageData(
137     const favicon_base::FaviconRawBitmapResult& bitmap_result,
138     sync_pb::FaviconData* favicon_data) {
139   if (!bitmap_result.bitmap_data.get())
140     return;
141   favicon_data->set_height(bitmap_result.pixel_size.height());
142   favicon_data->set_width(bitmap_result.pixel_size.width());
143   favicon_data->set_favicon(bitmap_result.bitmap_data->front(),
144                             bitmap_result.bitmap_data->size());
145 }
146 
147 // Build a FaviconImageSpecifics from a SyncedFaviconInfo.
BuildImageSpecifics(const SyncedFaviconInfo * favicon_info,sync_pb::FaviconImageSpecifics * image_specifics)148 void BuildImageSpecifics(
149     const SyncedFaviconInfo* favicon_info,
150     sync_pb::FaviconImageSpecifics* image_specifics) {
151   image_specifics->set_favicon_url(favicon_info->favicon_url.spec());
152   FillSpecificsWithImageData(favicon_info->bitmap_data[SIZE_16],
153                              image_specifics->mutable_favicon_web());
154   // TODO(zea): bring this back if we can handle the load.
155   // FillSpecificsWithImageData(favicon_info->bitmap_data[SIZE_32],
156   //                            image_specifics->mutable_favicon_web_32());
157   // FillSpecificsWithImageData(favicon_info->bitmap_data[SIZE_64],
158   //                            image_specifics->mutable_favicon_touch_64());
159 }
160 
161 // Build a FaviconTrackingSpecifics from a SyncedFaviconInfo.
BuildTrackingSpecifics(const SyncedFaviconInfo * favicon_info,sync_pb::FaviconTrackingSpecifics * tracking_specifics)162 void BuildTrackingSpecifics(
163     const SyncedFaviconInfo* favicon_info,
164     sync_pb::FaviconTrackingSpecifics* tracking_specifics) {
165   tracking_specifics->set_favicon_url(favicon_info->favicon_url.spec());
166   tracking_specifics->set_last_visit_time_ms(
167       syncer::TimeToProtoTime(favicon_info->last_visit_time));
168   tracking_specifics->set_is_bookmarked(favicon_info->is_bookmarked);
169 }
170 
171 // Updates |favicon_info| with the image data in |bitmap_result|.
UpdateFaviconFromBitmapResult(const favicon_base::FaviconRawBitmapResult & bitmap_result,SyncedFaviconInfo * favicon_info)172 bool UpdateFaviconFromBitmapResult(
173     const favicon_base::FaviconRawBitmapResult& bitmap_result,
174     SyncedFaviconInfo* favicon_info) {
175   DCHECK_EQ(favicon_info->favicon_url, bitmap_result.icon_url);
176   if (!bitmap_result.is_valid()) {
177     DVLOG(1) << "Received invalid favicon at " << bitmap_result.icon_url.spec();
178     return false;
179   }
180 
181   IconSize icon_size = GetIconSizeBinFromBitmapResult(
182       bitmap_result.pixel_size);
183   if (icon_size == SIZE_INVALID) {
184     DVLOG(1) << "Ignoring unsupported resolution "
185              << bitmap_result.pixel_size.height() << "x"
186              << bitmap_result.pixel_size.width();
187     return false;
188   } else if (!favicon_info->bitmap_data[icon_size].bitmap_data.get() ||
189              !favicon_info->received_local_update) {
190     DVLOG(1) << "Storing " << IconSizeToString(icon_size) << "p"
191              << " favicon for " << favicon_info->favicon_url.spec()
192              << " with size " << bitmap_result.bitmap_data->size()
193              << " bytes.";
194     favicon_info->bitmap_data[icon_size] = bitmap_result;
195     favicon_info->received_local_update = true;
196     return true;
197   } else {
198     // We only allow updating the image data once per restart.
199     DVLOG(2) << "Ignoring local update for " << bitmap_result.icon_url.spec();
200     return false;
201   }
202 }
203 
FaviconInfoHasImages(const SyncedFaviconInfo & favicon_info)204 bool FaviconInfoHasImages(const SyncedFaviconInfo& favicon_info) {
205   return favicon_info.bitmap_data[SIZE_16].bitmap_data.get() ||
206          favicon_info.bitmap_data[SIZE_32].bitmap_data.get() ||
207          favicon_info.bitmap_data[SIZE_64].bitmap_data.get();
208 }
209 
FaviconInfoHasTracking(const SyncedFaviconInfo & favicon_info)210 bool FaviconInfoHasTracking(const SyncedFaviconInfo& favicon_info) {
211   return !favicon_info.last_visit_time.is_null();
212 }
213 
FaviconInfoHasValidTypeData(const SyncedFaviconInfo & favicon_info,syncer::ModelType type)214 bool FaviconInfoHasValidTypeData(const SyncedFaviconInfo& favicon_info,
215                              syncer::ModelType type) {
216   if (type == syncer::FAVICON_IMAGES)
217     return FaviconInfoHasImages(favicon_info);
218   else if (type == syncer::FAVICON_TRACKING)
219     return FaviconInfoHasTracking(favicon_info);
220   NOTREACHED();
221   return false;
222 }
223 
224 }  // namespace
225 
FaviconCache(Profile * profile,int max_sync_favicon_limit)226 FaviconCache::FaviconCache(Profile* profile, int max_sync_favicon_limit)
227     : profile_(profile),
228       max_sync_favicon_limit_(max_sync_favicon_limit),
229       weak_ptr_factory_(this) {
230   notification_registrar_.Add(this,
231                               chrome::NOTIFICATION_HISTORY_URLS_DELETED,
232                               content::Source<Profile>(profile_));
233   DVLOG(1) << "Setting favicon limit to " << max_sync_favicon_limit;
234 }
235 
~FaviconCache()236 FaviconCache::~FaviconCache() {}
237 
MergeDataAndStartSyncing(syncer::ModelType type,const syncer::SyncDataList & initial_sync_data,scoped_ptr<syncer::SyncChangeProcessor> sync_processor,scoped_ptr<syncer::SyncErrorFactory> error_handler)238 syncer::SyncMergeResult FaviconCache::MergeDataAndStartSyncing(
239     syncer::ModelType type,
240     const syncer::SyncDataList& initial_sync_data,
241     scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
242     scoped_ptr<syncer::SyncErrorFactory> error_handler) {
243   DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
244   if (type == syncer::FAVICON_IMAGES)
245     favicon_images_sync_processor_ = sync_processor.Pass();
246   else
247     favicon_tracking_sync_processor_ = sync_processor.Pass();
248 
249   syncer::SyncMergeResult merge_result(type);
250   merge_result.set_num_items_before_association(synced_favicons_.size());
251   std::set<GURL> unsynced_favicon_urls;
252   for (FaviconMap::const_iterator iter = synced_favicons_.begin();
253        iter != synced_favicons_.end(); ++iter) {
254     if (FaviconInfoHasValidTypeData(*(iter->second), type))
255       unsynced_favicon_urls.insert(iter->first);
256   }
257 
258   syncer::SyncChangeList local_changes;
259   for (syncer::SyncDataList::const_iterator iter = initial_sync_data.begin();
260        iter != initial_sync_data.end(); ++iter) {
261     GURL remote_url = GetFaviconURLFromSpecifics(iter->GetSpecifics());
262     GURL favicon_url = GetLocalFaviconFromSyncedData(*iter);
263     if (favicon_url.is_valid()) {
264       unsynced_favicon_urls.erase(favicon_url);
265       MergeSyncFavicon(*iter, &local_changes);
266       merge_result.set_num_items_modified(
267           merge_result.num_items_modified() + 1);
268     } else {
269       AddLocalFaviconFromSyncedData(*iter);
270       merge_result.set_num_items_added(merge_result.num_items_added() + 1);
271     }
272   }
273 
274   // Rather than trigger a bunch of deletions when we set up sync, we drop
275   // local favicons. Those pages that are currently open are likely to result in
276   // loading new favicons/refreshing old favicons anyways, at which point
277   // they'll be re-added and the appropriate synced favicons will be evicted.
278   // TODO(zea): implement a smarter ordering of the which favicons to drop.
279   int available_favicons = max_sync_favicon_limit_ - initial_sync_data.size();
280   UMA_HISTOGRAM_BOOLEAN("Sync.FaviconsAvailableAtMerge",
281                         available_favicons > 0);
282   for (std::set<GURL>::const_iterator iter = unsynced_favicon_urls.begin();
283        iter != unsynced_favicon_urls.end(); ++iter) {
284     if (available_favicons > 0) {
285       local_changes.push_back(
286           syncer::SyncChange(FROM_HERE,
287                              syncer::SyncChange::ACTION_ADD,
288                              CreateSyncDataFromLocalFavicon(type, *iter)));
289       available_favicons--;
290     } else {
291       FaviconMap::iterator favicon_iter = synced_favicons_.find(*iter);
292       DVLOG(1) << "Dropping local favicon "
293                << favicon_iter->second->favicon_url.spec();
294       DropPartialFavicon(favicon_iter, type);
295       merge_result.set_num_items_deleted(merge_result.num_items_deleted() + 1);
296     }
297   }
298   UMA_HISTOGRAM_COUNTS_10000("Sync.FaviconCount", synced_favicons_.size());
299   merge_result.set_num_items_after_association(synced_favicons_.size());
300 
301   if (type == syncer::FAVICON_IMAGES) {
302     favicon_images_sync_processor_->ProcessSyncChanges(FROM_HERE,
303                                                        local_changes);
304   } else {
305     favicon_tracking_sync_processor_->ProcessSyncChanges(FROM_HERE,
306                                                          local_changes);
307   }
308   return merge_result;
309 }
310 
StopSyncing(syncer::ModelType type)311 void FaviconCache::StopSyncing(syncer::ModelType type) {
312   favicon_images_sync_processor_.reset();
313   favicon_tracking_sync_processor_.reset();
314   cancelable_task_tracker_.TryCancelAll();
315   page_task_map_.clear();
316 }
317 
GetAllSyncData(syncer::ModelType type) const318 syncer::SyncDataList FaviconCache::GetAllSyncData(syncer::ModelType type)
319     const {
320   syncer::SyncDataList data_list;
321   for (FaviconMap::const_iterator iter = synced_favicons_.begin();
322        iter != synced_favicons_.end(); ++iter) {
323     if ((type == syncer::FAVICON_IMAGES &&
324          FaviconInfoHasImages(*iter->second)) ||
325         (type == syncer::FAVICON_TRACKING &&
326          FaviconInfoHasTracking(*iter->second))) {
327       data_list.push_back(CreateSyncDataFromLocalFavicon(type, iter->first));
328     }
329   }
330   return data_list;
331 }
332 
ProcessSyncChanges(const tracked_objects::Location & from_here,const syncer::SyncChangeList & change_list)333 syncer::SyncError FaviconCache::ProcessSyncChanges(
334     const tracked_objects::Location& from_here,
335     const syncer::SyncChangeList& change_list) {
336   if (!favicon_images_sync_processor_.get() ||
337       !favicon_tracking_sync_processor_.get()) {
338     return syncer::SyncError(FROM_HERE,
339                              syncer::SyncError::DATATYPE_ERROR,
340                              "One or both favicon types disabled.",
341                              change_list[0].sync_data().GetDataType());
342   }
343 
344   syncer::SyncChangeList new_changes;
345   syncer::SyncError error;
346   syncer::ModelType type = syncer::UNSPECIFIED;
347   for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
348       iter != change_list.end(); ++iter) {
349     type = iter->sync_data().GetDataType();
350     DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
351     GURL favicon_url =
352         GetFaviconURLFromSpecifics(iter->sync_data().GetSpecifics());
353     if (!favicon_url.is_valid()) {
354       error.Reset(FROM_HERE, "Received invalid favicon url.", type);
355       break;
356     }
357     FaviconMap::iterator favicon_iter = synced_favicons_.find(favicon_url);
358     if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) {
359       if (favicon_iter == synced_favicons_.end()) {
360         // Two clients might wind up deleting different parts of the same
361         // favicon, so ignore this.
362         continue;
363       } else {
364         DVLOG(1) << "Deleting favicon at " << favicon_url.spec();
365         // If we only have partial data for the favicon (which implies orphaned
366         // nodes), delete the local favicon only if the type corresponds to the
367         // partial data we have. If we do have orphaned nodes, we rely on the
368         // expiration logic to remove them eventually.
369         DropPartialFavicon(favicon_iter, type);
370       }
371     } else if (iter->change_type() == syncer::SyncChange::ACTION_UPDATE ||
372                iter->change_type() == syncer::SyncChange::ACTION_ADD) {
373       // Adds and updates are treated the same due to the lack of strong
374       // consistency (it's possible we'll receive an update for a tracking info
375       // before we've received the add for the image, and should handle both
376       // gracefully).
377       if (favicon_iter == synced_favicons_.end()) {
378         DVLOG(1) << "Adding favicon at " << favicon_url.spec();
379         AddLocalFaviconFromSyncedData(iter->sync_data());
380       } else {
381         DVLOG(1) << "Updating favicon at " << favicon_url.spec();
382         MergeSyncFavicon(iter->sync_data(), &new_changes);
383       }
384     } else {
385       error.Reset(FROM_HERE, "Invalid action received.", type);
386       break;
387     }
388   }
389 
390   // Note: we deliberately do not expire favicons here. If we received new
391   // favicons and are now over the limit, the next local favicon change will
392   // trigger the necessary expiration.
393   if (!error.IsSet() && !new_changes.empty()) {
394     if (type == syncer::FAVICON_IMAGES) {
395         favicon_images_sync_processor_->ProcessSyncChanges(FROM_HERE,
396                                                            new_changes);
397     } else {
398         favicon_tracking_sync_processor_->ProcessSyncChanges(FROM_HERE,
399                                                              new_changes);
400     }
401   }
402 
403   return error;
404 }
405 
OnPageFaviconUpdated(const GURL & page_url)406 void FaviconCache::OnPageFaviconUpdated(const GURL& page_url) {
407   DCHECK(page_url.is_valid());
408 
409   // If a favicon load is already happening for this url, let it finish.
410   if (page_task_map_.find(page_url) != page_task_map_.end())
411     return;
412 
413   PageFaviconMap::const_iterator url_iter = page_favicon_map_.find(page_url);
414   if (url_iter != page_favicon_map_.end()) {
415     FaviconMap::const_iterator icon_iter =
416         synced_favicons_.find(url_iter->second);
417     // TODO(zea): consider what to do when only a subset of supported
418     // resolutions are available.
419     if (icon_iter != synced_favicons_.end() &&
420         icon_iter->second->bitmap_data[SIZE_16].bitmap_data.get()) {
421       DVLOG(2) << "Using cached favicon url for " << page_url.spec()
422                << ": " << icon_iter->second->favicon_url.spec();
423       UpdateFaviconVisitTime(icon_iter->second->favicon_url, base::Time::Now());
424       UpdateSyncState(icon_iter->second->favicon_url,
425                       syncer::SyncChange::ACTION_INVALID,
426                       syncer::SyncChange::ACTION_UPDATE);
427       return;
428     }
429   }
430 
431   DVLOG(1) << "Triggering favicon load for url " << page_url.spec();
432 
433   if (!profile_) {
434     page_task_map_[page_url] = 0;  // For testing only.
435     return;
436   }
437   FaviconService* favicon_service =
438       FaviconServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS);
439   if (!favicon_service)
440     return;
441   // TODO(zea): This appears to only fetch one favicon (best match based on
442   // desired_size_in_dip). Figure out a way to fetch all favicons we support.
443   // See crbug.com/181068.
444   base::CancelableTaskTracker::TaskId id =
445       favicon_service->GetFaviconForPageURL(
446           FaviconService::FaviconForPageURLParams(
447               page_url, SupportedFaviconTypes(), kMaxFaviconResolution),
448           base::Bind(&FaviconCache::OnFaviconDataAvailable,
449                      weak_ptr_factory_.GetWeakPtr(),
450                      page_url),
451           &cancelable_task_tracker_);
452   page_task_map_[page_url] = id;
453 }
454 
OnFaviconVisited(const GURL & page_url,const GURL & favicon_url)455 void FaviconCache::OnFaviconVisited(const GURL& page_url,
456                                     const GURL& favicon_url) {
457   DCHECK(page_url.is_valid());
458   if (!favicon_url.is_valid() ||
459       synced_favicons_.find(favicon_url) == synced_favicons_.end()) {
460     // TODO(zea): consider triggering a favicon load if we have some but not
461     // all desired resolutions?
462     OnPageFaviconUpdated(page_url);
463     return;
464   }
465 
466   DVLOG(1) << "Associating " << page_url.spec() << " with favicon at "
467            << favicon_url.spec() << " and marking visited.";
468   page_favicon_map_[page_url] = favicon_url;
469   bool had_tracking = FaviconInfoHasTracking(
470       *synced_favicons_.find(favicon_url)->second);
471   UpdateFaviconVisitTime(favicon_url, base::Time::Now());
472 
473   UpdateSyncState(favicon_url,
474                   syncer::SyncChange::ACTION_INVALID,
475                   (had_tracking ?
476                    syncer::SyncChange::ACTION_UPDATE :
477                    syncer::SyncChange::ACTION_ADD));
478 }
479 
GetSyncedFaviconForFaviconURL(const GURL & favicon_url,scoped_refptr<base::RefCountedMemory> * favicon_png) const480 bool FaviconCache::GetSyncedFaviconForFaviconURL(
481     const GURL& favicon_url,
482     scoped_refptr<base::RefCountedMemory>* favicon_png) const {
483   if (!favicon_url.is_valid())
484     return false;
485   FaviconMap::const_iterator iter = synced_favicons_.find(favicon_url);
486 
487   UMA_HISTOGRAM_BOOLEAN("Sync.FaviconCacheLookupSucceeded",
488                         iter != synced_favicons_.end());
489   if (iter == synced_favicons_.end())
490     return false;
491 
492   // TODO(zea): support getting other resolutions.
493   if (!iter->second->bitmap_data[SIZE_16].bitmap_data.get())
494     return false;
495 
496   *favicon_png = iter->second->bitmap_data[SIZE_16].bitmap_data;
497   return true;
498 }
499 
GetSyncedFaviconForPageURL(const GURL & page_url,scoped_refptr<base::RefCountedMemory> * favicon_png) const500 bool FaviconCache::GetSyncedFaviconForPageURL(
501     const GURL& page_url,
502     scoped_refptr<base::RefCountedMemory>* favicon_png) const {
503   if (!page_url.is_valid())
504     return false;
505   PageFaviconMap::const_iterator iter = page_favicon_map_.find(page_url);
506 
507   if (iter == page_favicon_map_.end())
508     return false;
509 
510   return GetSyncedFaviconForFaviconURL(iter->second, favicon_png);
511 }
512 
OnReceivedSyncFavicon(const GURL & page_url,const GURL & icon_url,const std::string & icon_bytes,int64 visit_time_ms)513 void FaviconCache::OnReceivedSyncFavicon(const GURL& page_url,
514                                          const GURL& icon_url,
515                                          const std::string& icon_bytes,
516                                          int64 visit_time_ms) {
517   if (!icon_url.is_valid() || !page_url.is_valid() || icon_url.SchemeIs("data"))
518     return;
519   DVLOG(1) << "Associating " << page_url.spec() << " with favicon at "
520            << icon_url.spec();
521   page_favicon_map_[page_url] = icon_url;
522 
523   // If there is no actual image, it means there either is no synced
524   // favicon, or it's on its way (race condition).
525   // TODO(zea): potentially trigger a favicon web download here (delayed?).
526   if (icon_bytes.size() == 0)
527     return;
528 
529   // Post a task to do the actual association because this method may have been
530   // called while in a transaction.
531   base::MessageLoop::current()->PostTask(
532       FROM_HERE,
533       base::Bind(&FaviconCache::OnReceivedSyncFaviconImpl,
534                  weak_ptr_factory_.GetWeakPtr(),
535                  icon_url,
536                  icon_bytes,
537                  visit_time_ms));
538 }
539 
OnReceivedSyncFaviconImpl(const GURL & icon_url,const std::string & icon_bytes,int64 visit_time_ms)540 void FaviconCache::OnReceivedSyncFaviconImpl(
541     const GURL& icon_url,
542     const std::string& icon_bytes,
543     int64 visit_time_ms) {
544   // If this favicon is already synced, do nothing else.
545   if (synced_favicons_.find(icon_url) != synced_favicons_.end())
546     return;
547 
548   // Don't add any more favicons once we hit our in memory limit.
549   // TODO(zea): UMA this.
550   if (kMaxFaviconsInMem != 0 && synced_favicons_.size() > kMaxFaviconsInMem)
551     return;
552 
553   SyncedFaviconInfo* favicon_info = GetFaviconInfo(icon_url);
554   if (!favicon_info)
555     return;  // We reached the in-memory limit.
556   base::RefCountedString* temp_string = new base::RefCountedString();
557   temp_string->data() = icon_bytes;
558   favicon_info->bitmap_data[SIZE_16].bitmap_data = temp_string;
559   // We assume legacy favicons are 16x16.
560   favicon_info->bitmap_data[SIZE_16].pixel_size.set_width(16);
561   favicon_info->bitmap_data[SIZE_16].pixel_size.set_height(16);
562   bool added_tracking = !FaviconInfoHasTracking(*favicon_info);
563   UpdateFaviconVisitTime(icon_url,
564                          syncer::ProtoTimeToTime(visit_time_ms));
565 
566   UpdateSyncState(icon_url,
567                   syncer::SyncChange::ACTION_ADD,
568                   (added_tracking ?
569                    syncer::SyncChange::ACTION_ADD :
570                    syncer::SyncChange::ACTION_UPDATE));
571 }
572 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)573 void FaviconCache::Observe(int type,
574                            const content::NotificationSource& source,
575                            const content::NotificationDetails& details) {
576   DCHECK_EQ(type, chrome::NOTIFICATION_HISTORY_URLS_DELETED);
577 
578   content::Details<history::URLsDeletedDetails> deleted_details(details);
579 
580   // We only care about actual user (or sync) deletions.
581   if (deleted_details->expired)
582     return;
583 
584   if (!deleted_details->all_history) {
585     DeleteSyncedFavicons(deleted_details->favicon_urls);
586     return;
587   }
588 
589   // All history was cleared: just delete all favicons.
590   DVLOG(1) << "History clear detected, deleting all synced favicons.";
591   syncer::SyncChangeList image_deletions, tracking_deletions;
592   while (!synced_favicons_.empty()) {
593     DeleteSyncedFavicon(synced_favicons_.begin(),
594                         &image_deletions,
595                         &tracking_deletions);
596   }
597 
598   if (favicon_images_sync_processor_.get()) {
599     favicon_images_sync_processor_->ProcessSyncChanges(FROM_HERE,
600                                                        image_deletions);
601   }
602   if (favicon_tracking_sync_processor_.get()) {
603     favicon_tracking_sync_processor_->ProcessSyncChanges(FROM_HERE,
604                                                          tracking_deletions);
605   }
606 }
607 
operator ()(const linked_ptr<SyncedFaviconInfo> & lhs,const linked_ptr<SyncedFaviconInfo> & rhs) const608 bool FaviconCache::FaviconRecencyFunctor::operator()(
609     const linked_ptr<SyncedFaviconInfo>& lhs,
610     const linked_ptr<SyncedFaviconInfo>& rhs) const {
611   // TODO(zea): incorporate bookmarked status here once we care about it.
612   if (lhs->last_visit_time < rhs->last_visit_time)
613     return true;
614   else if (lhs->last_visit_time == rhs->last_visit_time)
615     return lhs->favicon_url.spec() < rhs->favicon_url.spec();
616   return false;
617 }
618 
OnFaviconDataAvailable(const GURL & page_url,const std::vector<favicon_base::FaviconRawBitmapResult> & bitmap_results)619 void FaviconCache::OnFaviconDataAvailable(
620     const GURL& page_url,
621     const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) {
622   PageTaskMap::iterator page_iter = page_task_map_.find(page_url);
623   if (page_iter == page_task_map_.end())
624     return;
625   page_task_map_.erase(page_iter);
626 
627   if (bitmap_results.size() == 0) {
628     // Either the favicon isn't loaded yet or there is no valid favicon.
629     // We already cleared the task id, so just return.
630     DVLOG(1) << "Favicon load failed for page " << page_url.spec();
631     return;
632   }
633 
634   base::Time now = base::Time::Now();
635   std::map<GURL, LocalFaviconUpdateInfo> favicon_updates;
636   for (size_t i = 0; i < bitmap_results.size(); ++i) {
637     const favicon_base::FaviconRawBitmapResult& bitmap_result =
638         bitmap_results[i];
639     GURL favicon_url = bitmap_result.icon_url;
640     if (!favicon_url.is_valid() || favicon_url.SchemeIs("data"))
641       continue;  // Can happen if the page is still loading.
642 
643     SyncedFaviconInfo* favicon_info = GetFaviconInfo(favicon_url);
644     if (!favicon_info)
645       return;  // We reached the in-memory limit.
646 
647     favicon_updates[favicon_url].new_image |=
648         !FaviconInfoHasImages(*favicon_info);
649     favicon_updates[favicon_url].new_tracking |=
650         !FaviconInfoHasTracking(*favicon_info);
651     favicon_updates[favicon_url].image_needs_rewrite |=
652         UpdateFaviconFromBitmapResult(bitmap_result, favicon_info);
653     favicon_updates[favicon_url].favicon_info = favicon_info;
654   }
655 
656   for (std::map<GURL, LocalFaviconUpdateInfo>::const_iterator
657            iter = favicon_updates.begin(); iter != favicon_updates.end();
658        ++iter) {
659     SyncedFaviconInfo* favicon_info = iter->second.favicon_info;
660     const GURL& favicon_url = favicon_info->favicon_url;
661 
662     // TODO(zea): support multiple favicon urls per page.
663     page_favicon_map_[page_url] = favicon_url;
664 
665     if (!favicon_info->last_visit_time.is_null()) {
666       UMA_HISTOGRAM_COUNTS_10000(
667           "Sync.FaviconVisitPeriod",
668           (now - favicon_info->last_visit_time).InHours());
669     }
670     favicon_info->received_local_update = true;
671     UpdateFaviconVisitTime(favicon_url, now);
672 
673     syncer::SyncChange::SyncChangeType image_change =
674         syncer::SyncChange::ACTION_INVALID;
675     if (iter->second.new_image)
676       image_change = syncer::SyncChange::ACTION_ADD;
677     else if (iter->second.image_needs_rewrite)
678       image_change = syncer::SyncChange::ACTION_UPDATE;
679     syncer::SyncChange::SyncChangeType tracking_change =
680         syncer::SyncChange::ACTION_UPDATE;
681     if (iter->second.new_tracking)
682       tracking_change = syncer::SyncChange::ACTION_ADD;
683     UpdateSyncState(favicon_url, image_change, tracking_change);
684   }
685 }
686 
UpdateSyncState(const GURL & icon_url,syncer::SyncChange::SyncChangeType image_change_type,syncer::SyncChange::SyncChangeType tracking_change_type)687 void FaviconCache::UpdateSyncState(
688     const GURL& icon_url,
689     syncer::SyncChange::SyncChangeType image_change_type,
690     syncer::SyncChange::SyncChangeType tracking_change_type) {
691   DCHECK(icon_url.is_valid());
692   // It's possible that we'll receive a favicon update before both types
693   // have finished setting up. In that case ignore the update.
694   // TODO(zea): consider tracking these skipped updates somehow?
695   if (!favicon_images_sync_processor_.get() ||
696       !favicon_tracking_sync_processor_.get()) {
697     return;
698   }
699 
700   FaviconMap::const_iterator iter = synced_favicons_.find(icon_url);
701   DCHECK(iter != synced_favicons_.end());
702   const SyncedFaviconInfo* favicon_info = iter->second.get();
703 
704   syncer::SyncChangeList image_changes;
705   syncer::SyncChangeList tracking_changes;
706   if (image_change_type != syncer::SyncChange::ACTION_INVALID) {
707     sync_pb::EntitySpecifics new_specifics;
708     sync_pb::FaviconImageSpecifics* image_specifics =
709         new_specifics.mutable_favicon_image();
710     BuildImageSpecifics(favicon_info, image_specifics);
711 
712     image_changes.push_back(
713         syncer::SyncChange(FROM_HERE,
714                            image_change_type,
715                            syncer::SyncData::CreateLocalData(
716                                icon_url.spec(),
717                                icon_url.spec(),
718                                new_specifics)));
719   }
720   if (tracking_change_type != syncer::SyncChange::ACTION_INVALID) {
721     sync_pb::EntitySpecifics new_specifics;
722     sync_pb::FaviconTrackingSpecifics* tracking_specifics =
723         new_specifics.mutable_favicon_tracking();
724     BuildTrackingSpecifics(favicon_info, tracking_specifics);
725 
726     tracking_changes.push_back(
727         syncer::SyncChange(FROM_HERE,
728                            tracking_change_type,
729                            syncer::SyncData::CreateLocalData(
730                                icon_url.spec(),
731                                icon_url.spec(),
732                                new_specifics)));
733   }
734   ExpireFaviconsIfNecessary(&image_changes, &tracking_changes);
735   if (!image_changes.empty()) {
736     favicon_images_sync_processor_->ProcessSyncChanges(FROM_HERE,
737                                                        image_changes);
738   }
739   if (!tracking_changes.empty()) {
740     favicon_tracking_sync_processor_->ProcessSyncChanges(FROM_HERE,
741                                                          tracking_changes);
742   }
743 }
744 
GetFaviconInfo(const GURL & icon_url)745 SyncedFaviconInfo* FaviconCache::GetFaviconInfo(
746     const GURL& icon_url) {
747   DCHECK_EQ(recent_favicons_.size(), synced_favicons_.size());
748   if (synced_favicons_.count(icon_url) != 0)
749     return synced_favicons_[icon_url].get();
750 
751   // TODO(zea): implement in-memory eviction.
752   DVLOG(1) << "Adding favicon info for " << icon_url.spec();
753   SyncedFaviconInfo* favicon_info = new SyncedFaviconInfo(icon_url);
754   synced_favicons_[icon_url] = make_linked_ptr(favicon_info);
755   recent_favicons_.insert(synced_favicons_[icon_url]);
756   DCHECK_EQ(recent_favicons_.size(), synced_favicons_.size());
757   return favicon_info;
758 }
759 
UpdateFaviconVisitTime(const GURL & icon_url,base::Time time)760 void FaviconCache::UpdateFaviconVisitTime(const GURL& icon_url,
761                                           base::Time time) {
762   DCHECK_EQ(recent_favicons_.size(), synced_favicons_.size());
763   FaviconMap::const_iterator iter = synced_favicons_.find(icon_url);
764   DCHECK(iter != synced_favicons_.end());
765   if (iter->second->last_visit_time >= time)
766     return;
767   // Erase, update the time, then re-insert to maintain ordering.
768   recent_favicons_.erase(iter->second);
769   DVLOG(1) << "Updating " << icon_url.spec() << " visit time to "
770            << syncer::GetTimeDebugString(time);
771   iter->second->last_visit_time = time;
772   recent_favicons_.insert(iter->second);
773 
774   if (VLOG_IS_ON(2)) {
775     for (RecencySet::const_iterator iter = recent_favicons_.begin();
776          iter != recent_favicons_.end(); ++iter) {
777       DVLOG(2) << "Favicon " << iter->get()->favicon_url.spec() << ": "
778                << syncer::GetTimeDebugString(iter->get()->last_visit_time);
779     }
780   }
781   DCHECK_EQ(recent_favicons_.size(), synced_favicons_.size());
782 }
783 
ExpireFaviconsIfNecessary(syncer::SyncChangeList * image_changes,syncer::SyncChangeList * tracking_changes)784 void FaviconCache::ExpireFaviconsIfNecessary(
785     syncer::SyncChangeList* image_changes,
786     syncer::SyncChangeList* tracking_changes) {
787   DCHECK_EQ(recent_favicons_.size(), synced_favicons_.size());
788   // TODO(zea): once we have in-memory eviction, we'll need to track sync
789   // favicon count separately from the synced_favicons_/recent_favicons_.
790 
791   // Iterate until we've removed the necessary amount. |recent_favicons_| is
792   // already in recency order, so just start from the beginning.
793   // TODO(zea): to reduce thrashing, consider removing more than the minimum.
794   while (recent_favicons_.size() > max_sync_favicon_limit_) {
795     linked_ptr<SyncedFaviconInfo> candidate = *recent_favicons_.begin();
796     DVLOG(1) << "Expiring favicon " << candidate->favicon_url.spec();
797     DeleteSyncedFavicon(synced_favicons_.find(candidate->favicon_url),
798                         image_changes,
799                         tracking_changes);
800   }
801   DCHECK_EQ(recent_favicons_.size(), synced_favicons_.size());
802 }
803 
GetLocalFaviconFromSyncedData(const syncer::SyncData & sync_favicon) const804 GURL FaviconCache::GetLocalFaviconFromSyncedData(
805     const syncer::SyncData& sync_favicon) const {
806   syncer::ModelType type = sync_favicon.GetDataType();
807   DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
808   GURL favicon_url = GetFaviconURLFromSpecifics(sync_favicon.GetSpecifics());
809   return (synced_favicons_.count(favicon_url) > 0 ? favicon_url : GURL());
810 }
811 
MergeSyncFavicon(const syncer::SyncData & sync_favicon,syncer::SyncChangeList * sync_changes)812 void FaviconCache::MergeSyncFavicon(const syncer::SyncData& sync_favicon,
813                                     syncer::SyncChangeList* sync_changes) {
814   syncer::ModelType type = sync_favicon.GetDataType();
815   DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
816   sync_pb::EntitySpecifics new_specifics;
817   GURL favicon_url = GetFaviconURLFromSpecifics(sync_favicon.GetSpecifics());
818   FaviconMap::const_iterator iter = synced_favicons_.find(favicon_url);
819   DCHECK(iter != synced_favicons_.end());
820   SyncedFaviconInfo* favicon_info = iter->second.get();
821   if (type == syncer::FAVICON_IMAGES) {
822     sync_pb::FaviconImageSpecifics image_specifics =
823         sync_favicon.GetSpecifics().favicon_image();
824 
825     // Remote image data always clobbers local image data.
826     bool needs_update = false;
827     if (image_specifics.has_favicon_web()) {
828       favicon_info->bitmap_data[SIZE_16] = GetImageDataFromSpecifics(
829           image_specifics.favicon_web());
830     } else if (favicon_info->bitmap_data[SIZE_16].bitmap_data.get()) {
831       needs_update = true;
832     }
833     if (image_specifics.has_favicon_web_32()) {
834       favicon_info->bitmap_data[SIZE_32] = GetImageDataFromSpecifics(
835           image_specifics.favicon_web_32());
836     } else if (favicon_info->bitmap_data[SIZE_32].bitmap_data.get()) {
837       needs_update = true;
838     }
839     if (image_specifics.has_favicon_touch_64()) {
840       favicon_info->bitmap_data[SIZE_64] = GetImageDataFromSpecifics(
841           image_specifics.favicon_touch_64());
842     } else if (favicon_info->bitmap_data[SIZE_64].bitmap_data.get()) {
843       needs_update = true;
844     }
845 
846     if (needs_update)
847       BuildImageSpecifics(favicon_info, new_specifics.mutable_favicon_image());
848   } else {
849     sync_pb::FaviconTrackingSpecifics tracking_specifics =
850         sync_favicon.GetSpecifics().favicon_tracking();
851 
852     // Tracking data is merged, such that bookmark data is the logical OR
853     // of the two, and last visit time is the most recent.
854 
855     base::Time last_visit =  syncer::ProtoTimeToTime(
856         tracking_specifics.last_visit_time_ms());
857     // Due to crbug.com/258196, there are tracking nodes out there with
858     // null visit times. If this is one of those, artificially make it a valid
859     // visit time, so we know the node exists and update it properly on the next
860     // real visit.
861     if (last_visit.is_null())
862       last_visit = last_visit + base::TimeDelta::FromMilliseconds(1);
863     UpdateFaviconVisitTime(favicon_url, last_visit);
864     favicon_info->is_bookmarked = (favicon_info->is_bookmarked ||
865                                    tracking_specifics.is_bookmarked());
866 
867     if (syncer::TimeToProtoTime(favicon_info->last_visit_time) !=
868             tracking_specifics.last_visit_time_ms() ||
869         favicon_info->is_bookmarked != tracking_specifics.is_bookmarked()) {
870       BuildTrackingSpecifics(favicon_info,
871                              new_specifics.mutable_favicon_tracking());
872     }
873     DCHECK(!favicon_info->last_visit_time.is_null());
874   }
875 
876   if (new_specifics.has_favicon_image() ||
877       new_specifics.has_favicon_tracking()) {
878     sync_changes->push_back(syncer::SyncChange(
879         FROM_HERE,
880         syncer::SyncChange::ACTION_UPDATE,
881         syncer::SyncData::CreateLocalData(favicon_url.spec(),
882                                           favicon_url.spec(),
883                                           new_specifics)));
884   }
885 }
886 
AddLocalFaviconFromSyncedData(const syncer::SyncData & sync_favicon)887 void FaviconCache::AddLocalFaviconFromSyncedData(
888     const syncer::SyncData& sync_favicon) {
889   syncer::ModelType type = sync_favicon.GetDataType();
890   DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
891   if (type == syncer::FAVICON_IMAGES) {
892     sync_pb::FaviconImageSpecifics image_specifics =
893         sync_favicon.GetSpecifics().favicon_image();
894     GURL favicon_url = GURL(image_specifics.favicon_url());
895     DCHECK(favicon_url.is_valid());
896     DCHECK(!synced_favicons_.count(favicon_url));
897 
898     SyncedFaviconInfo* favicon_info = GetFaviconInfo(favicon_url);
899     if (!favicon_info)
900       return;  // We reached the in-memory limit.
901     if (image_specifics.has_favicon_web()) {
902       favicon_info->bitmap_data[SIZE_16] = GetImageDataFromSpecifics(
903           image_specifics.favicon_web());
904     }
905     if (image_specifics.has_favicon_web_32()) {
906       favicon_info->bitmap_data[SIZE_32] = GetImageDataFromSpecifics(
907           image_specifics.favicon_web_32());
908     }
909     if (image_specifics.has_favicon_touch_64()) {
910       favicon_info->bitmap_data[SIZE_64] = GetImageDataFromSpecifics(
911           image_specifics.favicon_touch_64());
912     }
913   } else {
914     sync_pb::FaviconTrackingSpecifics tracking_specifics =
915         sync_favicon.GetSpecifics().favicon_tracking();
916     GURL favicon_url = GURL(tracking_specifics.favicon_url());
917     DCHECK(favicon_url.is_valid());
918     DCHECK(!synced_favicons_.count(favicon_url));
919 
920     SyncedFaviconInfo* favicon_info = GetFaviconInfo(favicon_url);
921     if (!favicon_info)
922       return;  // We reached the in-memory limit.
923     base::Time last_visit =  syncer::ProtoTimeToTime(
924         tracking_specifics.last_visit_time_ms());
925     // Due to crbug.com/258196, there are tracking nodes out there with
926     // null visit times. If this is one of those, artificially make it a valid
927     // visit time, so we know the node exists and update it properly on the next
928     // real visit.
929     if (last_visit.is_null())
930       last_visit = last_visit + base::TimeDelta::FromMilliseconds(1);
931     UpdateFaviconVisitTime(favicon_url, last_visit);
932     favicon_info->is_bookmarked = tracking_specifics.is_bookmarked();
933     DCHECK(!favicon_info->last_visit_time.is_null());
934   }
935 }
936 
CreateSyncDataFromLocalFavicon(syncer::ModelType type,const GURL & favicon_url) const937 syncer::SyncData FaviconCache::CreateSyncDataFromLocalFavicon(
938     syncer::ModelType type,
939     const GURL& favicon_url) const {
940   DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
941   DCHECK(favicon_url.is_valid());
942   FaviconMap::const_iterator iter = synced_favicons_.find(favicon_url);
943   DCHECK(iter != synced_favicons_.end());
944   SyncedFaviconInfo* favicon_info = iter->second.get();
945 
946   syncer::SyncData data;
947   sync_pb::EntitySpecifics specifics;
948   if (type == syncer::FAVICON_IMAGES) {
949     sync_pb::FaviconImageSpecifics* image_specifics =
950         specifics.mutable_favicon_image();
951     BuildImageSpecifics(favicon_info, image_specifics);
952   } else {
953     sync_pb::FaviconTrackingSpecifics* tracking_specifics =
954         specifics.mutable_favicon_tracking();
955     BuildTrackingSpecifics(favicon_info, tracking_specifics);
956   }
957   data = syncer::SyncData::CreateLocalData(favicon_url.spec(),
958                                            favicon_url.spec(),
959                                            specifics);
960   return data;
961 }
962 
DeleteSyncedFavicons(const std::set<GURL> & favicon_urls)963 void FaviconCache::DeleteSyncedFavicons(const std::set<GURL>& favicon_urls) {
964   syncer::SyncChangeList image_deletions, tracking_deletions;
965   for (std::set<GURL>::const_iterator iter = favicon_urls.begin();
966        iter != favicon_urls.end(); ++iter) {
967     FaviconMap::iterator favicon_iter = synced_favicons_.find(*iter);
968     if (favicon_iter == synced_favicons_.end())
969       continue;
970     DeleteSyncedFavicon(favicon_iter,
971                         &image_deletions,
972                         &tracking_deletions);
973   }
974   DVLOG(1) << "Deleting " << image_deletions.size() << " synced favicons.";
975   if (favicon_images_sync_processor_.get()) {
976     favicon_images_sync_processor_->ProcessSyncChanges(FROM_HERE,
977                                                        image_deletions);
978   }
979   if (favicon_tracking_sync_processor_.get()) {
980     favicon_tracking_sync_processor_->ProcessSyncChanges(FROM_HERE,
981                                                          tracking_deletions);
982   }
983 }
984 
DeleteSyncedFavicon(FaviconMap::iterator favicon_iter,syncer::SyncChangeList * image_changes,syncer::SyncChangeList * tracking_changes)985 void FaviconCache::DeleteSyncedFavicon(
986     FaviconMap::iterator favicon_iter,
987     syncer::SyncChangeList* image_changes,
988     syncer::SyncChangeList* tracking_changes) {
989   linked_ptr<SyncedFaviconInfo> favicon_info = favicon_iter->second;
990   if (FaviconInfoHasImages(*(favicon_iter->second))) {
991     DVLOG(1) << "Deleting image for "
992              << favicon_iter->second.get()->favicon_url;
993     image_changes->push_back(
994         syncer::SyncChange(FROM_HERE,
995                            syncer::SyncChange::ACTION_DELETE,
996                            syncer::SyncData::CreateLocalDelete(
997                                favicon_info->favicon_url.spec(),
998                                syncer::FAVICON_IMAGES)));
999   }
1000   if (FaviconInfoHasTracking(*(favicon_iter->second))) {
1001     DVLOG(1) << "Deleting tracking for "
1002              << favicon_iter->second.get()->favicon_url;
1003     tracking_changes->push_back(
1004         syncer::SyncChange(FROM_HERE,
1005                            syncer::SyncChange::ACTION_DELETE,
1006                            syncer::SyncData::CreateLocalDelete(
1007                                favicon_info->favicon_url.spec(),
1008                                syncer::FAVICON_TRACKING)));
1009   }
1010   DropSyncedFavicon(favicon_iter);
1011 }
1012 
DropSyncedFavicon(FaviconMap::iterator favicon_iter)1013 void FaviconCache::DropSyncedFavicon(FaviconMap::iterator favicon_iter) {
1014   DVLOG(1) << "Dropping favicon " << favicon_iter->second.get()->favicon_url;
1015   recent_favicons_.erase(favicon_iter->second);
1016   synced_favicons_.erase(favicon_iter);
1017 }
1018 
DropPartialFavicon(FaviconMap::iterator favicon_iter,syncer::ModelType type)1019 void FaviconCache::DropPartialFavicon(FaviconMap::iterator favicon_iter,
1020                                       syncer::ModelType type) {
1021   // If the type being dropped has no valid data, do nothing.
1022   if ((type == syncer::FAVICON_TRACKING &&
1023        !FaviconInfoHasTracking(*favicon_iter->second)) ||
1024       (type == syncer::FAVICON_IMAGES &&
1025        !FaviconInfoHasImages(*favicon_iter->second))) {
1026     return;
1027   }
1028 
1029   // If the type being dropped is the only type with valid data, just delete
1030   // the favicon altogether.
1031   if ((type == syncer::FAVICON_TRACKING &&
1032        !FaviconInfoHasImages(*favicon_iter->second)) ||
1033       (type == syncer::FAVICON_IMAGES &&
1034        !FaviconInfoHasTracking(*favicon_iter->second))) {
1035     DropSyncedFavicon(favicon_iter);
1036     return;
1037   }
1038 
1039   if (type == syncer::FAVICON_IMAGES) {
1040     DVLOG(1) << "Dropping favicon image "
1041              << favicon_iter->second.get()->favicon_url;
1042     for (int i = 0; i < NUM_SIZES; ++i) {
1043       favicon_iter->second->bitmap_data[i] =
1044           favicon_base::FaviconRawBitmapResult();
1045     }
1046     DCHECK(!FaviconInfoHasImages(*favicon_iter->second));
1047   } else {
1048     DCHECK_EQ(type, syncer::FAVICON_TRACKING);
1049     DVLOG(1) << "Dropping favicon tracking "
1050              << favicon_iter->second.get()->favicon_url;
1051     recent_favicons_.erase(favicon_iter->second);
1052     favicon_iter->second->last_visit_time = base::Time();
1053     favicon_iter->second->is_bookmarked = false;
1054     recent_favicons_.insert(favicon_iter->second);
1055     DCHECK(!FaviconInfoHasTracking(*favicon_iter->second));
1056   }
1057 }
1058 
NumFaviconsForTest() const1059 size_t FaviconCache::NumFaviconsForTest() const {
1060   return synced_favicons_.size();
1061 }
1062 
NumTasksForTest() const1063 size_t FaviconCache::NumTasksForTest() const {
1064   return page_task_map_.size();
1065 }
1066 
1067 }  // namespace browser_sync
1068