12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/sync/glue/favicon_cache.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
79ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/metrics/histogram.h"
97dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/favicon/favicon_service.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/favicon/favicon_service_factory.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/history/history_notifications.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/history/history_types.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/notification_details.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/notification_source.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/api/time.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/protocol/favicon_image_specifics.pb.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/protocol/favicon_tracking_specifics.pb.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/protocol/sync.pb.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/favicon_size.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace browser_sync {
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Synced favicon storage and tracking.
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Note: we don't use the favicon service for storing these because these
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// favicons are not necessarily associated with any local navigation, and
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// hence would not work with the current expiration logic. We have custom
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// expiration logic based on visit time/bookmark status/etc.
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// See crbug.com/122890.
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)struct SyncedFaviconInfo {
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  explicit SyncedFaviconInfo(const GURL& favicon_url)
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      : favicon_url(favicon_url),
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        is_bookmarked(false),
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        received_local_update(false) {}
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The actual favicon data.
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(zea): don't keep around the actual data for locally sourced
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // favicons (UI can access those directly).
3990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  chrome::FaviconBitmapResult bitmap_data[NUM_SIZES];
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The URL this favicon was loaded from.
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const GURL favicon_url;
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Is the favicon for a bookmarked page?
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool is_bookmarked;
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The last time a tab needed this favicon.
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Note: Do not modify this directly! It should only be modified via
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // UpdateFaviconVisitTime(..).
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::Time last_visit_time;
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Whether we've received a local update for this favicon since starting up.
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool received_local_update;
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private:
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(SyncedFaviconInfo);
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Information for handling local favicon updates. Used in
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// OnFaviconDataAvailable.
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)struct LocalFaviconUpdateInfo {
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  LocalFaviconUpdateInfo()
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      : new_image(false),
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        new_tracking(false),
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        image_needs_rewrite(false),
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        favicon_info(NULL) {}
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bool new_image;
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bool new_tracking;
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bool image_needs_rewrite;
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SyncedFaviconInfo* favicon_info;
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Maximum number of favicons to keep in memory (0 means no limit).
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const size_t kMaxFaviconsInMem = 0;
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Maximum width/height resolution supported.
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kMaxFaviconResolution = 16;
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns a mask of the supported favicon types.
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// TODO(zea): Supporting other favicons types will involve some work in the
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// favicon service and navigation controller. See crbug.com/181068.
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int SupportedFaviconTypes() {
8290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return chrome::FAVICON;
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns the appropriate IconSize to use for a given gfx::Size pixel
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// dimensions.
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)IconSize GetIconSizeBinFromBitmapResult(const gfx::Size& pixel_size) {
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int max_size =
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (pixel_size.width() > pixel_size.height() ?
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       pixel_size.width() : pixel_size.height());
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // TODO(zea): re-enable 64p and 32p resolutions once we support them.
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (max_size > 64)
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return SIZE_INVALID;
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  else if (max_size > 32)
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return SIZE_INVALID;
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  else if (max_size > 16)
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return SIZE_INVALID;
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  else
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return SIZE_16;
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Helper for debug statements.
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)std::string IconSizeToString(IconSize icon_size) {
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  switch (icon_size) {
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case SIZE_16:
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return "16";
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case SIZE_32:
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return "32";
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case SIZE_64:
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return "64";
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    default:
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return "INVALID";
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Extract the favicon url from either of the favicon types.
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)GURL GetFaviconURLFromSpecifics(const sync_pb::EntitySpecifics& specifics) {
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (specifics.has_favicon_tracking())
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return GURL(specifics.favicon_tracking().favicon_url());
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  else
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return GURL(specifics.favicon_image().favicon_url());
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Convert protobuf image data into a FaviconBitmapResult.
12590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)chrome::FaviconBitmapResult GetImageDataFromSpecifics(
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const sync_pb::FaviconData& favicon_data) {
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::RefCountedString* temp_string =
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new base::RefCountedString();
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  temp_string->data() = favicon_data.favicon();
13090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  chrome::FaviconBitmapResult bitmap_result;
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap_result.bitmap_data = temp_string;
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap_result.pixel_size.set_height(favicon_data.height());
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap_result.pixel_size.set_width(favicon_data.width());
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return bitmap_result;
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Convert a FaviconBitmapResult into protobuf image data.
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FillSpecificsWithImageData(
13990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    const chrome::FaviconBitmapResult& bitmap_result,
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_pb::FaviconData* favicon_data) {
141868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (!bitmap_result.bitmap_data.get())
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  favicon_data->set_height(bitmap_result.pixel_size.height());
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  favicon_data->set_width(bitmap_result.pixel_size.width());
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  favicon_data->set_favicon(bitmap_result.bitmap_data->front(),
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            bitmap_result.bitmap_data->size());
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Build a FaviconImageSpecifics from a SyncedFaviconInfo.
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void BuildImageSpecifics(
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const SyncedFaviconInfo* favicon_info,
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_pb::FaviconImageSpecifics* image_specifics) {
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  image_specifics->set_favicon_url(favicon_info->favicon_url.spec());
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FillSpecificsWithImageData(favicon_info->bitmap_data[SIZE_16],
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             image_specifics->mutable_favicon_web());
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(zea): bring this back if we can handle the load.
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // FillSpecificsWithImageData(favicon_info->bitmap_data[SIZE_32],
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  //                            image_specifics->mutable_favicon_web_32());
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // FillSpecificsWithImageData(favicon_info->bitmap_data[SIZE_64],
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  //                            image_specifics->mutable_favicon_touch_64());
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Build a FaviconTrackingSpecifics from a SyncedFaviconInfo.
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void BuildTrackingSpecifics(
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const SyncedFaviconInfo* favicon_info,
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_pb::FaviconTrackingSpecifics* tracking_specifics) {
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  tracking_specifics->set_favicon_url(favicon_info->favicon_url.spec());
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  tracking_specifics->set_last_visit_time_ms(
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      syncer::TimeToProtoTime(favicon_info->last_visit_time));
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  tracking_specifics->set_is_bookmarked(favicon_info->is_bookmarked);
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Updates |favicon_info| with the image data in |bitmap_result|.
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool UpdateFaviconFromBitmapResult(
17590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    const chrome::FaviconBitmapResult& bitmap_result,
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SyncedFaviconInfo* favicon_info) {
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(favicon_info->favicon_url, bitmap_result.icon_url);
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!bitmap_result.is_valid()) {
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DVLOG(1) << "Received invalid favicon at " << bitmap_result.icon_url.spec();
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  IconSize icon_size = GetIconSizeBinFromBitmapResult(
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      bitmap_result.pixel_size);
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (icon_size == SIZE_INVALID) {
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DVLOG(1) << "Ignoring unsupported resolution "
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             << bitmap_result.pixel_size.height() << "x"
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             << bitmap_result.pixel_size.width();
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (!favicon_info->bitmap_data[icon_size].bitmap_data.get() ||
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             !favicon_info->received_local_update) {
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DVLOG(1) << "Storing " << IconSizeToString(icon_size) << "p"
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             << " favicon for " << favicon_info->favicon_url.spec()
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             << " with size " << bitmap_result.bitmap_data->size()
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             << " bytes.";
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    favicon_info->bitmap_data[icon_size] = bitmap_result;
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    favicon_info->received_local_update = true;
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return true;
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // We only allow updating the image data once per restart.
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DVLOG(2) << "Ignoring local update for " << bitmap_result.icon_url.spec();
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool FaviconInfoHasImages(const SyncedFaviconInfo& favicon_info) {
207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return favicon_info.bitmap_data[SIZE_16].bitmap_data.get() ||
208c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)         favicon_info.bitmap_data[SIZE_32].bitmap_data.get() ||
209c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)         favicon_info.bitmap_data[SIZE_64].bitmap_data.get();
210c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
211c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
212c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool FaviconInfoHasTracking(const SyncedFaviconInfo& favicon_info) {
213c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return !favicon_info.last_visit_time.is_null();
214c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
215c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochbool FaviconInfoHasValidTypeData(const SyncedFaviconInfo& favicon_info,
2177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                             syncer::ModelType type) {
2187dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (type == syncer::FAVICON_IMAGES)
2197dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    return FaviconInfoHasImages(favicon_info);
2207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  else if (type == syncer::FAVICON_TRACKING)
2217dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    return FaviconInfoHasTracking(favicon_info);
2227dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  NOTREACHED();
2237dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  return false;
2247dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch}
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2267dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch}  // namespace
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)FaviconCache::FaviconCache(Profile* profile, int max_sync_favicon_limit)
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : profile_(profile),
230c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(this),
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      max_sync_favicon_limit_(max_sync_favicon_limit) {
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  notification_registrar_.Add(this,
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              chrome::NOTIFICATION_HISTORY_URLS_DELETED,
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              content::Source<Profile>(profile_));
235c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DVLOG(1) << "Setting favicon limit to " << max_sync_favicon_limit;
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)FaviconCache::~FaviconCache() {}
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncMergeResult FaviconCache::MergeDataAndStartSyncing(
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::ModelType type,
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const syncer::SyncDataList& initial_sync_data,
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_ptr<syncer::SyncErrorFactory> error_handler) {
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (type == syncer::FAVICON_IMAGES)
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    favicon_images_sync_processor_ = sync_processor.Pass();
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  else
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    favicon_tracking_sync_processor_ = sync_processor.Pass();
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncMergeResult merge_result(type);
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  merge_result.set_num_items_before_association(synced_favicons_.size());
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::set<GURL> unsynced_favicon_urls;
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (FaviconMap::const_iterator iter = synced_favicons_.begin();
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       iter != synced_favicons_.end(); ++iter) {
2567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (FaviconInfoHasValidTypeData(*(iter->second), type))
2577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      unsynced_favicon_urls.insert(iter->first);
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncChangeList local_changes;
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (syncer::SyncDataList::const_iterator iter = initial_sync_data.begin();
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       iter != initial_sync_data.end(); ++iter) {
2637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    GURL remote_url = GetFaviconURLFromSpecifics(iter->GetSpecifics());
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GURL favicon_url = GetLocalFaviconFromSyncedData(*iter);
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (favicon_url.is_valid()) {
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      unsynced_favicon_urls.erase(favicon_url);
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      MergeSyncFavicon(*iter, &local_changes);
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      merge_result.set_num_items_modified(
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          merge_result.num_items_modified() + 1);
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AddLocalFaviconFromSyncedData(*iter);
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      merge_result.set_num_items_added(merge_result.num_items_added() + 1);
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Rather than trigger a bunch of deletions when we set up sync, we drop
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // local favicons. Those pages that are currently open are likely to result in
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // loading new favicons/refreshing old favicons anyways, at which point
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // they'll be re-added and the appropriate synced favicons will be evicted.
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(zea): implement a smarter ordering of the which favicons to drop.
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int available_favicons = max_sync_favicon_limit_ - initial_sync_data.size();
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UMA_HISTOGRAM_BOOLEAN("Sync.FaviconsAvailableAtMerge",
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        available_favicons > 0);
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (std::set<GURL>::const_iterator iter = unsynced_favicon_urls.begin();
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       iter != unsynced_favicon_urls.end(); ++iter) {
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (available_favicons > 0) {
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      local_changes.push_back(
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          syncer::SyncChange(FROM_HERE,
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             syncer::SyncChange::ACTION_ADD,
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             CreateSyncDataFromLocalFavicon(type, *iter)));
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      available_favicons--;
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FaviconMap::iterator favicon_iter = synced_favicons_.find(*iter);
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DVLOG(1) << "Dropping local favicon "
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)               << favicon_iter->second->favicon_url.spec();
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DropSyncedFavicon(favicon_iter);
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      merge_result.set_num_items_deleted(merge_result.num_items_deleted() + 1);
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UMA_HISTOGRAM_COUNTS_10000("Sync.FaviconCount", synced_favicons_.size());
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  merge_result.set_num_items_after_association(synced_favicons_.size());
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (type == syncer::FAVICON_IMAGES) {
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      merge_result.set_error(
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          favicon_images_sync_processor_->ProcessSyncChanges(FROM_HERE,
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                             local_changes));
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      merge_result.set_error(
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          favicon_tracking_sync_processor_->ProcessSyncChanges(FROM_HERE,
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                               local_changes));
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return merge_result;
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FaviconCache::StopSyncing(syncer::ModelType type) {
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  favicon_images_sync_processor_.reset();
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  favicon_tracking_sync_processor_.reset();
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  cancelable_task_tracker_.TryCancelAll();
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  page_task_map_.clear();
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncDataList FaviconCache::GetAllSyncData(syncer::ModelType type)
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const {
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncDataList data_list;
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (FaviconMap::const_iterator iter = synced_favicons_.begin();
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       iter != synced_favicons_.end(); ++iter) {
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    data_list.push_back(CreateSyncDataFromLocalFavicon(type, iter->first));
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return data_list;
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncError FaviconCache::ProcessSyncChanges(
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const tracked_objects::Location& from_here,
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const syncer::SyncChangeList& change_list) {
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!favicon_images_sync_processor_.get() ||
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      !favicon_tracking_sync_processor_.get()) {
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return syncer::SyncError(FROM_HERE,
338eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                             syncer::SyncError::DATATYPE_ERROR,
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             "One or both favicon types disabled.",
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             change_list[0].sync_data().GetDataType());
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncChangeList new_changes;
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncError error;
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::ModelType type = syncer::UNSPECIFIED;
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      iter != change_list.end(); ++iter) {
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    type = iter->sync_data().GetDataType();
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GURL favicon_url =
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        GetFaviconURLFromSpecifics(iter->sync_data().GetSpecifics());
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!favicon_url.is_valid()) {
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      error.Reset(FROM_HERE, "Received invalid favicon url.", type);
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FaviconMap::iterator favicon_iter = synced_favicons_.find(favicon_url);
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) {
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (favicon_iter == synced_favicons_.end()) {
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // Two clients might wind up deleting different parts of the same
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // favicon, so ignore this.
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        continue;
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        DVLOG(1) << "Deleting favicon at " << favicon_url.spec();
364c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        // If we only have partial data for the favicon (which implies orphaned
365c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        // nodes), delete the local favicon only if the type corresponds to the
366c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        // partial data we have. If we do have orphaned nodes, we rely on the
367c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        // expiration logic to remove them eventually.
368c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (type == syncer::FAVICON_IMAGES &&
369c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            FaviconInfoHasImages(*(favicon_iter->second)) &&
370c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            !FaviconInfoHasTracking(*(favicon_iter->second))) {
371c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          DropSyncedFavicon(favicon_iter);
372c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        } else if (type == syncer::FAVICON_TRACKING &&
373c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                   !FaviconInfoHasImages(*(favicon_iter->second)) &&
374c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                   FaviconInfoHasTracking(*(favicon_iter->second))) {
375c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          DropSyncedFavicon(favicon_iter);
376c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        } else {
377c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          // Only delete the data for the modified type.
378c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          if (type == syncer::FAVICON_TRACKING) {
379c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            recent_favicons_.erase(favicon_iter->second);
380c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            favicon_iter->second->last_visit_time = base::Time();
381c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            favicon_iter->second->is_bookmarked = false;
382c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            recent_favicons_.insert(favicon_iter->second);
383c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            DCHECK(!FaviconInfoHasTracking(*(favicon_iter->second)));
3847dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            DCHECK(FaviconInfoHasImages(*(favicon_iter->second)));
385c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          } else {
386c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            for (int i = 0; i < NUM_SIZES; ++i) {
387c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              favicon_iter->second->bitmap_data[i] =
38890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                  chrome::FaviconBitmapResult();
389c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            }
3907dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            DCHECK(FaviconInfoHasTracking(*(favicon_iter->second)));
391c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            DCHECK(!FaviconInfoHasImages(*(favicon_iter->second)));
392c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          }
393c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        }
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else if (iter->change_type() == syncer::SyncChange::ACTION_UPDATE ||
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)               iter->change_type() == syncer::SyncChange::ACTION_ADD) {
3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Adds and updates are treated the same due to the lack of strong
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // consistency (it's possible we'll receive an update for a tracking info
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // before we've received the add for the image, and should handle both
4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // gracefully).
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (favicon_iter == synced_favicons_.end()) {
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        DVLOG(1) << "Adding favicon at " << favicon_url.spec();
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        AddLocalFaviconFromSyncedData(iter->sync_data());
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        DVLOG(1) << "Updating favicon at " << favicon_url.spec();
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        MergeSyncFavicon(iter->sync_data(), &new_changes);
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      error.Reset(FROM_HERE, "Invalid action received.", type);
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Note: we deliberately do not expire favicons here. If we received new
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // favicons and are now over the limit, the next local favicon change will
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // trigger the necessary expiration.
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!error.IsSet() && !new_changes.empty()) {
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (type == syncer::FAVICON_IMAGES) {
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        error =
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            favicon_images_sync_processor_->ProcessSyncChanges(FROM_HERE,
4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                               new_changes);
4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        error =
4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            favicon_tracking_sync_processor_->ProcessSyncChanges(FROM_HERE,
4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                                 new_changes);
4262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return error;
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FaviconCache::OnPageFaviconUpdated(const GURL& page_url) {
4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(page_url.is_valid());
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If a favicon load is already happening for this url, let it finish.
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (page_task_map_.find(page_url) != page_task_map_.end())
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
439c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  PageFaviconMap::const_iterator url_iter = page_favicon_map_.find(page_url);
440c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (url_iter != page_favicon_map_.end()) {
441c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    FaviconMap::const_iterator icon_iter =
442c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        synced_favicons_.find(url_iter->second);
443c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // TODO(zea): consider what to do when only a subset of supported
444c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // resolutions are available.
445c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (icon_iter != synced_favicons_.end() &&
446c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        icon_iter->second->bitmap_data[SIZE_16].bitmap_data.get()) {
447c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "Using cached favicon url for " << page_url.spec()
448c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)               << ": " << icon_iter->second->favicon_url.spec();
449c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      UpdateFaviconVisitTime(icon_iter->second->favicon_url, base::Time::Now());
450c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      UpdateSyncState(icon_iter->second->favicon_url,
451c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                      syncer::SyncChange::ACTION_INVALID,
452c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                      syncer::SyncChange::ACTION_UPDATE);
453c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return;
454c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
455c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
456c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "Triggering favicon load for url " << page_url.spec();
4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!profile_) {
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    page_task_map_[page_url] = 0;  // For testing only.
4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FaviconService* favicon_service =
4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FaviconServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS);
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!favicon_service)
4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(zea): This appears to only fetch one favicon (best match based on
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // desired_size_in_dip). Figure out a way to fetch all favicons we support.
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // See crbug.com/181068.
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CancelableTaskTracker::TaskId id = favicon_service->GetFaviconForURL(
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FaviconService::FaviconForURLParams(
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          profile_, page_url, SupportedFaviconTypes(), kMaxFaviconResolution),
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&FaviconCache::OnFaviconDataAvailable,
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr(), page_url),
4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      &cancelable_task_tracker_);
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  page_task_map_[page_url] = id;
4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FaviconCache::OnFaviconVisited(const GURL& page_url,
4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    const GURL& favicon_url) {
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(page_url.is_valid());
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!favicon_url.is_valid() ||
4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      synced_favicons_.find(favicon_url) == synced_favicons_.end()) {
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // TODO(zea): consider triggering a favicon load if we have some but not
4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // all desired resolutions?
4862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    OnPageFaviconUpdated(page_url);
4872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "Associating " << page_url.spec() << " with favicon at "
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           << favicon_url.spec() << " and marking visited.";
4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  page_favicon_map_[page_url] = favicon_url;
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UpdateFaviconVisitTime(favicon_url, base::Time::Now());
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UpdateSyncState(favicon_url,
495c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                  syncer::SyncChange::ACTION_INVALID,
496c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                  (FaviconInfoHasTracking(
497c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                       *synced_favicons_.find(favicon_url)->second) ?
498c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                   syncer::SyncChange::ACTION_UPDATE :
499c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                   syncer::SyncChange::ACTION_ADD));
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool FaviconCache::GetSyncedFaviconForFaviconURL(
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const GURL& favicon_url,
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_refptr<base::RefCountedMemory>* favicon_png) const {
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!favicon_url.is_valid())
5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FaviconMap::const_iterator iter = synced_favicons_.find(favicon_url);
5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UMA_HISTOGRAM_BOOLEAN("Sync.FaviconCacheLookupSucceeded",
5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        iter != synced_favicons_.end());
5112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (iter == synced_favicons_.end())
5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(zea): support getting other resolutions.
515868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (!iter->second->bitmap_data[SIZE_16].bitmap_data.get())
5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *favicon_png = iter->second->bitmap_data[SIZE_16].bitmap_data;
5192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return true;
5202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool FaviconCache::GetSyncedFaviconForPageURL(
5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const GURL& page_url,
5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_refptr<base::RefCountedMemory>* favicon_png) const {
5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!page_url.is_valid())
5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
5272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  PageFaviconMap::const_iterator iter = page_favicon_map_.find(page_url);
5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (iter == page_favicon_map_.end())
5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
5312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return GetSyncedFaviconForFaviconURL(iter->second, favicon_png);
5332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FaviconCache::OnReceivedSyncFavicon(const GURL& page_url,
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         const GURL& icon_url,
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         const std::string& icon_bytes,
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         int64 visit_time_ms) {
539c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!icon_url.is_valid() || !page_url.is_valid() || icon_url.SchemeIs("data"))
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "Associating " << page_url.spec() << " with favicon at "
5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           << icon_url.spec();
5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  page_favicon_map_[page_url] = icon_url;
5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If there is no actual image, it means there either is no synced
5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // favicon, or it's on its way (race condition).
5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(zea): potentially trigger a favicon web download here (delayed?).
5482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (icon_bytes.size() == 0)
5492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
5502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
551c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Post a task to do the actual association because this method may have been
552c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // called while in a transaction.
55390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  base::MessageLoop::current()->PostTask(
554c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      FROM_HERE,
555c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&FaviconCache::OnReceivedSyncFaviconImpl,
556c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr(),
557c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 icon_url,
558c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 icon_bytes,
559c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 visit_time_ms));
560c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
561c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
562c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void FaviconCache::OnReceivedSyncFaviconImpl(
563c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const GURL& icon_url,
564c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& icon_bytes,
565c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int64 visit_time_ms) {
566c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // If this favicon is already synced, do nothing else.
567c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (synced_favicons_.find(icon_url) != synced_favicons_.end())
568c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
569c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Don't add any more favicons once we hit our in memory limit.
5712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(zea): UMA this.
5722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (kMaxFaviconsInMem != 0 && synced_favicons_.size() > kMaxFaviconsInMem)
5732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
5742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SyncedFaviconInfo* favicon_info = GetFaviconInfo(icon_url);
5762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!favicon_info)
5772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;  // We reached the in-memory limit.
5782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::RefCountedString* temp_string = new base::RefCountedString();
5792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  temp_string->data() = icon_bytes;
5802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  favicon_info->bitmap_data[SIZE_16].bitmap_data = temp_string;
5812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We assume legacy favicons are 16x16.
5822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  favicon_info->bitmap_data[SIZE_16].pixel_size.set_width(16);
5832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  favicon_info->bitmap_data[SIZE_16].pixel_size.set_height(16);
584c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bool added_tracking = !FaviconInfoHasTracking(*favicon_info);
585c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  UpdateFaviconVisitTime(icon_url,
586c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                         syncer::ProtoTimeToTime(visit_time_ms));
587c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
588c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  UpdateSyncState(icon_url,
589c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                  syncer::SyncChange::ACTION_ADD,
590c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                  (added_tracking ?
591c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                   syncer::SyncChange::ACTION_ADD :
592c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                   syncer::SyncChange::ACTION_UPDATE));
5932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FaviconCache::Observe(int type,
5962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           const content::NotificationSource& source,
5972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           const content::NotificationDetails& details) {
5982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(type, chrome::NOTIFICATION_HISTORY_URLS_DELETED);
5992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content::Details<history::URLsDeletedDetails> deleted_details(details);
6012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We only care about actual user (or sync) deletions.
6032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (deleted_details->archived)
6042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
6052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!deleted_details->all_history) {
6072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DeleteSyncedFavicons(deleted_details->favicon_urls);
6082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
6092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // All history was cleared: just delete all favicons.
6122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "History clear detected, deleting all synced favicons.";
6132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncChangeList image_deletions, tracking_deletions;
6147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  while (!synced_favicons_.empty()) {
6157dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    DeleteSyncedFavicon(synced_favicons_.begin(),
6167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                        &image_deletions,
6177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                        &tracking_deletions);
6182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (favicon_images_sync_processor_.get()) {
6212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    favicon_images_sync_processor_->ProcessSyncChanges(FROM_HERE,
6222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                       image_deletions);
6237dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
6247dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (favicon_tracking_sync_processor_.get()) {
6252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    favicon_tracking_sync_processor_->ProcessSyncChanges(FROM_HERE,
6262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                         tracking_deletions);
6272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool FaviconCache::FaviconRecencyFunctor::operator()(
6312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const linked_ptr<SyncedFaviconInfo>& lhs,
6322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const linked_ptr<SyncedFaviconInfo>& rhs) const {
6332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(zea): incorporate bookmarked status here once we care about it.
6342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (lhs->last_visit_time < rhs->last_visit_time)
6352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return true;
6362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  else if (lhs->last_visit_time == rhs->last_visit_time)
6372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return lhs->favicon_url.spec() < rhs->favicon_url.spec();
6382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
6392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FaviconCache::OnFaviconDataAvailable(
6422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const GURL& page_url,
64390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    const std::vector<chrome::FaviconBitmapResult>& bitmap_results) {
6442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  PageTaskMap::iterator page_iter = page_task_map_.find(page_url);
6452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (page_iter == page_task_map_.end())
6462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
6472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  page_task_map_.erase(page_iter);
6482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (bitmap_results.size() == 0) {
6502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Either the favicon isn't loaded yet or there is no valid favicon.
6512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // We already cleared the task id, so just return.
6522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DVLOG(1) << "Favicon load failed for page " << page_url.spec();
6532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
6542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::Time now = base::Time::Now();
657c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::map<GURL, LocalFaviconUpdateInfo> favicon_updates;
6582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < bitmap_results.size(); ++i) {
65990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    const chrome::FaviconBitmapResult& bitmap_result = bitmap_results[i];
6602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GURL favicon_url = bitmap_result.icon_url;
661c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (!favicon_url.is_valid() || favicon_url.SchemeIs("data"))
6622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;  // Can happen if the page is still loading.
6632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SyncedFaviconInfo* favicon_info = GetFaviconInfo(favicon_url);
6652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!favicon_info)
6662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;  // We reached the in-memory limit.
6672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
668c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    favicon_updates[favicon_url].new_image |=
669c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        !FaviconInfoHasImages(*favicon_info);
670c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    favicon_updates[favicon_url].new_tracking |=
671c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        !FaviconInfoHasTracking(*favicon_info);
672c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    favicon_updates[favicon_url].image_needs_rewrite |=
673c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        UpdateFaviconFromBitmapResult(bitmap_result, favicon_info);
674c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    favicon_updates[favicon_url].favicon_info = favicon_info;
6752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
677c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (std::map<GURL, LocalFaviconUpdateInfo>::const_iterator
678c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)           iter = favicon_updates.begin(); iter != favicon_updates.end();
679c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       ++iter) {
680c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    SyncedFaviconInfo* favicon_info = iter->second.favicon_info;
6812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const GURL& favicon_url = favicon_info->favicon_url;
6822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!favicon_info->last_visit_time.is_null()) {
6832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      UMA_HISTOGRAM_COUNTS_10000(
6842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "Sync.FaviconVisitPeriod",
6852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          (now - favicon_info->last_visit_time).InHours());
6862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
6872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    favicon_info->received_local_update = true;
6882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    UpdateFaviconVisitTime(favicon_url, now);
689c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
690c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    syncer::SyncChange::SyncChangeType image_change =
691c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        syncer::SyncChange::ACTION_INVALID;
692c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (iter->second.new_image)
693c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      image_change = syncer::SyncChange::ACTION_ADD;
694c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    else if (iter->second.image_needs_rewrite)
695c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      image_change = syncer::SyncChange::ACTION_UPDATE;
696c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    syncer::SyncChange::SyncChangeType tracking_change =
697c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        syncer::SyncChange::ACTION_UPDATE;
698c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (iter->second.new_tracking)
699c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      tracking_change = syncer::SyncChange::ACTION_ADD;
700c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    UpdateSyncState(favicon_url, image_change, tracking_change);
7012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // TODO(zea): support multiple favicon urls per page.
7032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    page_favicon_map_[page_url] = favicon_url;
7042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
7052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FaviconCache::UpdateSyncState(
7082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const GURL& icon_url,
709c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    syncer::SyncChange::SyncChangeType image_change_type,
710c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    syncer::SyncChange::SyncChangeType tracking_change_type) {
7112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(icon_url.is_valid());
7122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // It's possible that we'll receive a favicon update before both types
7132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // have finished setting up. In that case ignore the update.
7142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(zea): consider tracking these skipped updates somehow?
7152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!favicon_images_sync_processor_.get() ||
7167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      !favicon_tracking_sync_processor_.get()) {
7172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
7187dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
7192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FaviconMap::const_iterator iter = synced_favicons_.find(icon_url);
7212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(iter != synced_favicons_.end());
7222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const SyncedFaviconInfo* favicon_info = iter->second.get();
7232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncChangeList image_changes;
7252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncChangeList tracking_changes;
726c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (image_change_type != syncer::SyncChange::ACTION_INVALID) {
7272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_pb::EntitySpecifics new_specifics;
7282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_pb::FaviconImageSpecifics* image_specifics =
7292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        new_specifics.mutable_favicon_image();
7302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BuildImageSpecifics(favicon_info, image_specifics);
7312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    image_changes.push_back(
7332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        syncer::SyncChange(FROM_HERE,
734c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                           image_change_type,
7352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           syncer::SyncData::CreateLocalData(
7362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               icon_url.spec(),
7372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               icon_url.spec(),
7382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               new_specifics)));
7392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
740c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (tracking_change_type != syncer::SyncChange::ACTION_INVALID) {
7412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_pb::EntitySpecifics new_specifics;
7422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_pb::FaviconTrackingSpecifics* tracking_specifics =
7432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        new_specifics.mutable_favicon_tracking();
7442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BuildTrackingSpecifics(favicon_info, tracking_specifics);
7452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    tracking_changes.push_back(
7472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        syncer::SyncChange(FROM_HERE,
748c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                           tracking_change_type,
7492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           syncer::SyncData::CreateLocalData(
7502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               icon_url.spec(),
7512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               icon_url.spec(),
7522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               new_specifics)));
7532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
7542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExpireFaviconsIfNecessary(&image_changes, &tracking_changes);
7552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!image_changes.empty()) {
7562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    favicon_images_sync_processor_->ProcessSyncChanges(FROM_HERE,
7572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                       image_changes);
7582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
7592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!tracking_changes.empty()) {
7602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    favicon_tracking_sync_processor_->ProcessSyncChanges(FROM_HERE,
7612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                         tracking_changes);
7622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
7632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SyncedFaviconInfo* FaviconCache::GetFaviconInfo(
7662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const GURL& icon_url) {
7672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(recent_favicons_.size(), synced_favicons_.size());
7682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (synced_favicons_.count(icon_url) != 0)
7692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return synced_favicons_[icon_url].get();
7702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(zea): implement in-memory eviction.
7722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "Adding favicon info for " << icon_url.spec();
7732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SyncedFaviconInfo* favicon_info = new SyncedFaviconInfo(icon_url);
7742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  synced_favicons_[icon_url] = make_linked_ptr(favicon_info);
7752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  recent_favicons_.insert(synced_favicons_[icon_url]);
7762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(recent_favicons_.size(), synced_favicons_.size());
7772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return favicon_info;
7782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FaviconCache::UpdateFaviconVisitTime(const GURL& icon_url,
7812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                          base::Time time) {
7822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(recent_favicons_.size(), synced_favicons_.size());
7832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FaviconMap::const_iterator iter = synced_favicons_.find(icon_url);
7842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(iter != synced_favicons_.end());
7852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (iter->second->last_visit_time >= time)
7862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
7872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Erase, update the time, then re-insert to maintain ordering.
7882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  recent_favicons_.erase(iter->second);
789c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DVLOG(1) << "Updating " << icon_url.spec() << " visit time to "
790c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)           << syncer::GetTimeDebugString(time);
7912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  iter->second->last_visit_time = time;
7922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  recent_favicons_.insert(iter->second);
7932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (VLOG_IS_ON(2)) {
7952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (RecencySet::const_iterator iter = recent_favicons_.begin();
7962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         iter != recent_favicons_.end(); ++iter) {
797c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "Favicon " << iter->get()->favicon_url.spec() << ": "
798c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)               << syncer::GetTimeDebugString(iter->get()->last_visit_time);
7992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
8002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
8012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(recent_favicons_.size(), synced_favicons_.size());
8022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
8032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FaviconCache::ExpireFaviconsIfNecessary(
8052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncChangeList* image_changes,
8062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncChangeList* tracking_changes) {
8072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(recent_favicons_.size(), synced_favicons_.size());
8082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(zea): once we have in-memory eviction, we'll need to track sync
8092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // favicon count separately from the synced_favicons_/recent_favicons_.
8102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Iterate until we've removed the necessary amount. |recent_favicons_| is
8122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // already in recency order, so just start from the beginning.
8132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(zea): to reduce thrashing, consider removing more than the minimum.
8142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  while (recent_favicons_.size() > max_sync_favicon_limit_) {
8152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    linked_ptr<SyncedFaviconInfo> candidate = *recent_favicons_.begin();
8162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DVLOG(1) << "Expiring favicon " << candidate->favicon_url.spec();
8172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DeleteSyncedFavicon(synced_favicons_.find(candidate->favicon_url),
8182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        image_changes,
8192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        tracking_changes);
8202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
8212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(recent_favicons_.size(), synced_favicons_.size());
8222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
8232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)GURL FaviconCache::GetLocalFaviconFromSyncedData(
8252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const syncer::SyncData& sync_favicon) const {
8262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::ModelType type = sync_favicon.GetDataType();
8272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
8282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GURL favicon_url = GetFaviconURLFromSpecifics(sync_favicon.GetSpecifics());
8292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return (synced_favicons_.count(favicon_url) > 0 ? favicon_url : GURL());
8302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
8312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FaviconCache::MergeSyncFavicon(const syncer::SyncData& sync_favicon,
8332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    syncer::SyncChangeList* sync_changes) {
8342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::ModelType type = sync_favicon.GetDataType();
8352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
8362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sync_pb::EntitySpecifics new_specifics;
8372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GURL favicon_url = GetFaviconURLFromSpecifics(sync_favicon.GetSpecifics());
8387dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  FaviconMap::const_iterator iter = synced_favicons_.find(favicon_url);
8397dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  DCHECK(iter != synced_favicons_.end());
8407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  SyncedFaviconInfo* favicon_info = iter->second.get();
8412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (type == syncer::FAVICON_IMAGES) {
8422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_pb::FaviconImageSpecifics image_specifics =
8432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        sync_favicon.GetSpecifics().favicon_image();
8442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Remote image data always clobbers local image data.
8462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bool needs_update = false;
8472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (image_specifics.has_favicon_web()) {
8482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      favicon_info->bitmap_data[SIZE_16] = GetImageDataFromSpecifics(
8492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          image_specifics.favicon_web());
850868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    } else if (favicon_info->bitmap_data[SIZE_16].bitmap_data.get()) {
8512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      needs_update = true;
8522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
8532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (image_specifics.has_favicon_web_32()) {
8542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      favicon_info->bitmap_data[SIZE_32] = GetImageDataFromSpecifics(
8552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          image_specifics.favicon_web_32());
856868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    } else if (favicon_info->bitmap_data[SIZE_32].bitmap_data.get()) {
8572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      needs_update = true;
8582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
8592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (image_specifics.has_favicon_touch_64()) {
8602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      favicon_info->bitmap_data[SIZE_64] = GetImageDataFromSpecifics(
8612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          image_specifics.favicon_touch_64());
862868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    } else if (favicon_info->bitmap_data[SIZE_64].bitmap_data.get()) {
8632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      needs_update = true;
8642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
8652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (needs_update)
8672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BuildImageSpecifics(favicon_info, new_specifics.mutable_favicon_image());
8682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
8692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_pb::FaviconTrackingSpecifics tracking_specifics =
8702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        sync_favicon.GetSpecifics().favicon_tracking();
8712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Tracking data is merged, such that bookmark data is the logical OR
8732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // of the two, and last visit time is the most recent.
8747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
8757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    base::Time last_visit =  syncer::ProtoTimeToTime(
8767dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        tracking_specifics.last_visit_time_ms());
8777dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // Due to crbug.com/258196, there are tracking nodes out there with
8787dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // null visit times. If this is one of those, artificially make it a valid
8797dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // visit time, so we know the node exists and update it properly on the next
8807dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // real visit.
8817dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (last_visit.is_null())
8827dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      last_visit = last_visit + base::TimeDelta::FromMilliseconds(1);
8837dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    UpdateFaviconVisitTime(favicon_url, last_visit);
8842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    favicon_info->is_bookmarked = (favicon_info->is_bookmarked ||
8852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   tracking_specifics.is_bookmarked());
8862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (syncer::TimeToProtoTime(favicon_info->last_visit_time) !=
8882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            tracking_specifics.last_visit_time_ms() ||
8892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        favicon_info->is_bookmarked != tracking_specifics.is_bookmarked()) {
8902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BuildTrackingSpecifics(favicon_info,
8912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             new_specifics.mutable_favicon_tracking());
8922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
8937dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    DCHECK(!favicon_info->last_visit_time.is_null());
8942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
8952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (new_specifics.has_favicon_image() ||
8972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new_specifics.has_favicon_tracking()) {
8982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_changes->push_back(syncer::SyncChange(
8992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        FROM_HERE,
9002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        syncer::SyncChange::ACTION_UPDATE,
9012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        syncer::SyncData::CreateLocalData(favicon_url.spec(),
9022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                          favicon_url.spec(),
9032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                          new_specifics)));
9042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
9052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
9062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FaviconCache::AddLocalFaviconFromSyncedData(
9082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const syncer::SyncData& sync_favicon) {
9092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::ModelType type = sync_favicon.GetDataType();
9102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
9112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (type == syncer::FAVICON_IMAGES) {
9122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_pb::FaviconImageSpecifics image_specifics =
9132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        sync_favicon.GetSpecifics().favicon_image();
9142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GURL favicon_url = GURL(image_specifics.favicon_url());
9152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(favicon_url.is_valid());
9162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(!synced_favicons_.count(favicon_url));
9172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SyncedFaviconInfo* favicon_info = GetFaviconInfo(favicon_url);
9192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!favicon_info)
9202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;  // We reached the in-memory limit.
9212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (image_specifics.has_favicon_web()) {
9222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      favicon_info->bitmap_data[SIZE_16] = GetImageDataFromSpecifics(
9232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          image_specifics.favicon_web());
9242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
9252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (image_specifics.has_favicon_web_32()) {
9262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      favicon_info->bitmap_data[SIZE_32] = GetImageDataFromSpecifics(
9272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          image_specifics.favicon_web_32());
9282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
9292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (image_specifics.has_favicon_touch_64()) {
9302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      favicon_info->bitmap_data[SIZE_64] = GetImageDataFromSpecifics(
9312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          image_specifics.favicon_touch_64());
9322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
9332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
9342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_pb::FaviconTrackingSpecifics tracking_specifics =
9352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        sync_favicon.GetSpecifics().favicon_tracking();
9362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GURL favicon_url = GURL(tracking_specifics.favicon_url());
9372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(favicon_url.is_valid());
9382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(!synced_favicons_.count(favicon_url));
9392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SyncedFaviconInfo* favicon_info = GetFaviconInfo(favicon_url);
9412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!favicon_info)
9422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;  // We reached the in-memory limit.
9437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    base::Time last_visit =  syncer::ProtoTimeToTime(
9447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        tracking_specifics.last_visit_time_ms());
9457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // Due to crbug.com/258196, there are tracking nodes out there with
9467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // null visit times. If this is one of those, artificially make it a valid
9477dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // visit time, so we know the node exists and update it properly on the next
9487dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // real visit.
9497dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (last_visit.is_null())
9507dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      last_visit = last_visit + base::TimeDelta::FromMilliseconds(1);
9517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    UpdateFaviconVisitTime(favicon_url, last_visit);
9522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    favicon_info->is_bookmarked = tracking_specifics.is_bookmarked();
9537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    DCHECK(!favicon_info->last_visit_time.is_null());
9542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
9552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
9562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncData FaviconCache::CreateSyncDataFromLocalFavicon(
9582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::ModelType type,
9592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const GURL& favicon_url) const {
9602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(type == syncer::FAVICON_IMAGES || type == syncer::FAVICON_TRACKING);
9612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(favicon_url.is_valid());
9622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FaviconMap::const_iterator iter = synced_favicons_.find(favicon_url);
9632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(iter != synced_favicons_.end());
9642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SyncedFaviconInfo* favicon_info = iter->second.get();
9652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncData data;
9672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sync_pb::EntitySpecifics specifics;
9682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (type == syncer::FAVICON_IMAGES) {
9692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_pb::FaviconImageSpecifics* image_specifics =
9702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        specifics.mutable_favicon_image();
9712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BuildImageSpecifics(favicon_info, image_specifics);
9722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
9732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_pb::FaviconTrackingSpecifics* tracking_specifics =
9742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        specifics.mutable_favicon_tracking();
9752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BuildTrackingSpecifics(favicon_info, tracking_specifics);
9762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
9772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  data = syncer::SyncData::CreateLocalData(favicon_url.spec(),
9782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                           favicon_url.spec(),
9792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                           specifics);
9802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return data;
9812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
9822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FaviconCache::DeleteSyncedFavicons(const std::set<GURL>& favicon_urls) {
9842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncChangeList image_deletions, tracking_deletions;
9852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (std::set<GURL>::const_iterator iter = favicon_urls.begin();
9862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       iter != favicon_urls.end(); ++iter) {
9872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FaviconMap::iterator favicon_iter = synced_favicons_.find(*iter);
9882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (favicon_iter == synced_favicons_.end())
9892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
9902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DeleteSyncedFavicon(favicon_iter,
9912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        &image_deletions,
9922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        &tracking_deletions);
9932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
9942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "Deleting " << image_deletions.size() << " synced favicons.";
9957dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (favicon_images_sync_processor_.get()) {
9962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    favicon_images_sync_processor_->ProcessSyncChanges(FROM_HERE,
9972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                       image_deletions);
9987dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
9997dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (favicon_tracking_sync_processor_.get()) {
10002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    favicon_tracking_sync_processor_->ProcessSyncChanges(FROM_HERE,
10012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                         tracking_deletions);
10022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
10032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
10042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
10057dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochvoid FaviconCache::DeleteSyncedFavicon(
10062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FaviconMap::iterator favicon_iter,
10072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncChangeList* image_changes,
10082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncChangeList* tracking_changes) {
10092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  linked_ptr<SyncedFaviconInfo> favicon_info = favicon_iter->second;
1010c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (FaviconInfoHasImages(*(favicon_iter->second))) {
1011c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    image_changes->push_back(
1012c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        syncer::SyncChange(FROM_HERE,
1013c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                           syncer::SyncChange::ACTION_DELETE,
1014c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                           syncer::SyncData::CreateLocalDelete(
1015c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                               favicon_info->favicon_url.spec(),
1016c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                               syncer::FAVICON_IMAGES)));
1017c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
1018c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (FaviconInfoHasTracking(*(favicon_iter->second))) {
1019c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    tracking_changes->push_back(
1020c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        syncer::SyncChange(FROM_HERE,
1021c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                           syncer::SyncChange::ACTION_DELETE,
1022c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                           syncer::SyncData::CreateLocalDelete(
1023c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                               favicon_info->favicon_url.spec(),
1024c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                               syncer::FAVICON_TRACKING)));
1025c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
10262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DropSyncedFavicon(favicon_iter);
10272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
10282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
10292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FaviconCache::DropSyncedFavicon(FaviconMap::iterator favicon_iter) {
10302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  recent_favicons_.erase(favicon_iter->second);
10312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  synced_favicons_.erase(favicon_iter);
10322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
10332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
10342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)size_t FaviconCache::NumFaviconsForTest() const {
10352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return synced_favicons_.size();
10362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
10372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
10382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)size_t FaviconCache::NumTasksForTest() const {
10392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return page_task_map_.size();
10402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
10412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
10422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace browser_sync
1043