Commit message: None
CVE: CVE-2018-6053
CWE: CWE-200
Vulnerability description: Inappropriate implementation in New Tab Page in Google Chrome prior to 64.0.3282.119 allowed a local attacker to view website thumbnail images after clearing browser data via a crafted HTML page.

Project: Chrome

void TopSitesCache::CountForcedURLs() {
  num_forced_urls_ = 0;
  while (num_forced_urls_ < top_sites_.size()) {
    if (top_sites_[num_forced_urls_].last_forced_time.is_null())
      break;
    num_forced_urls_++;
  }
#if DCHECK_IS_ON()
  for (size_t i = num_forced_urls_; i < top_sites_.size(); ++i) {
    DCHECK(top_sites_[i].last_forced_time.is_null())
        << "All the forced URLs must appear before non-forced URLs.";
  }
#endif
}

Project: Chrome

void TopSitesCache::GenerateCanonicalURLs() {
  canonical_urls_.clear();
  for (size_t i = 0; i < top_sites_.size(); i++)
    StoreRedirectChain(top_sites_[i].redirects, i);
}

Project: Chrome

const GURL& TopSitesCache::GetCanonicalURL(const GURL& url) const {
  CanonicalURLs::const_iterator it = GetCanonicalURLsIterator(url);
  return it == canonical_urls_.end() ? url : it->first.first->url;
}

Project: Chrome

GURL TopSitesCache::GetGeneralizedCanonicalURL(const GURL& url) const {
  CanonicalURLs::const_iterator it_hi =
      canonical_urls_.lower_bound(CanonicalURLQuery(url).entry());
  if (it_hi != canonical_urls_.end()) {
    if (url.ReplaceComponents(clear_query_ref_) ==
        GetURLFromIterator(it_hi).ReplaceComponents(clear_query_ref_)) {
      return it_hi->first.first->url;
    }
  }

  GURL base_url(url.ReplaceComponents(clear_path_query_ref_));
  CanonicalURLs::const_iterator it_lo =
      canonical_urls_.lower_bound(CanonicalURLQuery(base_url).entry());
  if (it_lo == canonical_urls_.end())
    return GURL::EmptyGURL();
  GURL compare_url_lo(GetURLFromIterator(it_lo));
  if (!HaveSameSchemeHostAndPort(base_url, compare_url_lo) ||
      !IsPathPrefix(base_url.path(), compare_url_lo.path())) {
    return GURL::EmptyGURL();
  }

  for (CanonicalURLs::const_iterator it = it_hi; it != it_lo;) {
    --it;
    GURL compare_url(GetURLFromIterator(it));
    DCHECK(HaveSameSchemeHostAndPort(compare_url, url));
    if (IsPathPrefix(compare_url.path(), url.path()))
      return it->first.first->url;
  }

  return GURL::EmptyGURL();
}

Project: Chrome

 Images* TopSitesCache::GetImage(const GURL& url) {
   return &images_[GetCanonicalURL(url)];
 }

Project: Chrome

size_t TopSitesCache::GetNumForcedURLs() const {
  return num_forced_urls_;
}

Project: Chrome

size_t TopSitesCache::GetNumNonForcedURLs() const {
  return top_sites_.size() - num_forced_urls_;
}

Project: Chrome

bool TopSitesCache::GetPageThumbnail(
    const GURL& url,
    scoped_refptr<base::RefCountedMemory>* bytes) const {
  std::map<GURL, Images>::const_iterator found =
      images_.find(GetCanonicalURL(url));
  if (found != images_.end()) {
    base::RefCountedMemory* data = found->second.thumbnail.get();
    if (data) {
      *bytes = data;
      return true;
    }
  }
  return false;
}

Project: Chrome

bool TopSitesCache::GetPageThumbnailScore(const GURL& url,
                                          ThumbnailScore* score) const {
  std::map<GURL, Images>::const_iterator found =
      images_.find(GetCanonicalURL(url));
  if (found != images_.end()) {
    *score = found->second.thumbnail_score;
    return true;
  }
  return false;
}

Project: Chrome

const GURL& TopSitesCache::GetURLFromIterator(
    CanonicalURLs::const_iterator it) const {
  DCHECK(it != canonical_urls_.end());
  return it->first.first->redirects[it->first.second];
}

Project: Chrome

size_t TopSitesCache::GetURLIndex(const GURL& url) const {
  DCHECK(IsKnownURL(url));
  return GetCanonicalURLsIterator(url)->second;
}

Project: Chrome

void TopSitesCache::SetThumbnails(const URLToImagesMap& images) {
   images_ = images;
 }

Project: Chrome

void TopSitesCache::SetTopSites(const MostVisitedURLList& top_sites) {
  top_sites_ = top_sites;
  CountForcedURLs();
  GenerateCanonicalURLs();
}

Project: Chrome

void TopSitesCache::StoreRedirectChain(const RedirectList& redirects,
                                       size_t destination) {

  for (size_t i = 0; i < redirects.size(); i++) {
    if (!IsKnownURL(redirects[i])) {
      CanonicalURLEntry entry;
      entry.first = &(top_sites_[destination]);
      entry.second = i;
      canonical_urls_[entry] = destination;
    }
  }
}

Project: Chrome

TopSitesCache::TopSitesCache() : num_forced_urls_(0) {
  clear_query_ref_.ClearQuery();
  clear_query_ref_.ClearRef();
  clear_path_query_ref_.ClearQuery();
  clear_path_query_ref_.ClearRef();
  clear_path_query_ref_.ClearPath();
}

Project: Chrome

TopSitesCache::CanonicalURLQuery::~CanonicalURLQuery() {
}

Project: Chrome

void TopSitesCacheTest::BuildTopSites(const char** spec, size_t size) {
  std::set<std::string> urls_seen;
  for (size_t i = 0; i < size; ++i) {
    const char* spec_item = spec[i];
    while (*spec_item && *spec_item == ' ')  // Eat indent.
      ++spec_item;
    if (urls_seen.find(spec_item) != urls_seen.end())
      NOTREACHED() << "Duplicate URL found: " << spec_item;
    urls_seen.insert(spec_item);
    if (spec_item == spec[i]) {  // No indent: add new MostVisitedURL.
      base::string16 title(base::ASCIIToUTF16("Title ") +
                     base::Uint64ToString16(top_sites_.size() + 1));
      top_sites_.push_back(MostVisitedURL(GURL(spec_item), title));
    }
    ASSERT_TRUE(!top_sites_.empty());
    top_sites_.back().redirects.push_back(GURL(spec_item));
  }
}

Project: Chrome

void TopSitesCacheTest::InitTopSiteCache(const char** spec, size_t size) {
  BuildTopSites(spec, size);
  cache_.SetTopSites(top_sites_);
}

Project: Chrome

void TopSitesImpl::AddBlacklistedURL(const GURL& url) {
  DCHECK(thread_checker_.CalledOnValidThread());

  auto dummy = base::MakeUnique<base::Value>();
  {
    DictionaryPrefUpdate update(pref_service_, kMostVisitedURLsBlacklist);
    base::DictionaryValue* blacklist = update.Get();
    blacklist->SetWithoutPathExpansion(GetURLHash(url), std::move(dummy));
  }

  ResetThreadSafeCache();
  NotifyTopSitesChanged(TopSitesObserver::ChangeReason::BLACKLIST);
}

Project: Chrome

bool TopSitesImpl::AddForcedURL(const GURL& url, const base::Time& time) {
  DCHECK(thread_checker_.CalledOnValidThread());

  if (!loaded_) {
    return false;
  }

  size_t num_forced = cache_->GetNumForcedURLs();
  MostVisitedURLList new_list(cache_->top_sites());
  MostVisitedURL new_url;

  if (cache_->IsKnownURL(url)) {
    size_t index = cache_->GetURLIndex(url);
    if (new_list[index].last_forced_time.is_null())
      return false;

    new_url = new_list[index];
    new_list.erase(new_list.begin() + index);
    num_forced--;
  } else {
    new_url.url = url;
    new_url.redirects.push_back(url);
  }
  new_url.last_forced_time = time;
  MostVisitedURLList::iterator mid = new_list.begin() + num_forced;
  mid = new_list.insert(mid, new_url);
  std::inplace_merge(new_list.begin(), mid, mid + 1, ForcedURLComparator);
  SetTopSites(new_list, CALL_LOCATION_FROM_FORCED_URLS);
  return true;
}

Project: Chrome

void TopSitesImpl::AddTemporaryThumbnail(const GURL& url,
                                         base::RefCountedMemory* thumbnail,
                                         const ThumbnailScore& score) {
  if (temp_images_.size() == kMaxTempTopImages)
    temp_images_.pop_front();

  TempImage image;
  image.first = url;
  image.second.thumbnail = thumbnail;
  image.second.thumbnail_score = score;
  temp_images_.push_back(image);
}

Project: Chrome

void TopSitesImpl::ApplyBlacklist(const MostVisitedURLList& urls,
                                  MostVisitedURLList* out) {
  const base::DictionaryValue* blacklist =
      pref_service_->GetDictionary(kMostVisitedURLsBlacklist);
  UMA_HISTOGRAM_BOOLEAN("TopSites.NumberOfApplyBlacklist", true);
  UMA_HISTOGRAM_COUNTS_100("TopSites.NumberOfBlacklistedItems",
      (blacklist ? blacklist->size() : 0));
  size_t num_non_forced_urls = 0;
  size_t num_forced_urls = 0;
  for (size_t i = 0; i < urls.size(); ++i) {
    if (!IsBlacklisted(urls[i].url)) {
      if (urls[i].last_forced_time.is_null()) {
        if (num_non_forced_urls >= kNonForcedTopSitesNumber)
          continue;
        num_non_forced_urls++;
      } else {
        if (num_forced_urls >= kForcedTopSitesNumber)
          continue;
        num_forced_urls++;
      }
      out->push_back(urls[i]);
    }
  }
}

Project: Chrome

void TopSitesImpl::ClearBlacklistedURLs() {
  DCHECK(thread_checker_.CalledOnValidThread());
  {
    DictionaryPrefUpdate update(pref_service_, kMostVisitedURLsBlacklist);
    base::DictionaryValue* blacklist = update.Get();
    blacklist->Clear();
  }
  ResetThreadSafeCache();
  NotifyTopSitesChanged(TopSitesObserver::ChangeReason::BLACKLIST);
}

Project: Chrome

void TopSitesImpl::DiffMostVisited(const MostVisitedURLList& old_list,
                                   const MostVisitedURLList& new_list,
                                   TopSitesDelta* delta) {

  std::map<GURL, size_t> all_old_urls;
  size_t num_old_forced = 0;
  for (size_t i = 0; i < old_list.size(); i++) {
    if (!old_list[i].last_forced_time.is_null())
      num_old_forced++;
    DCHECK(old_list[i].last_forced_time.is_null() || i < num_old_forced)
        << "Forced URLs must all appear before non-forced URLs.";
    all_old_urls[old_list[i].url] = i;
  }

  const size_t kAlreadyFoundMarker = static_cast<size_t>(-1);
  int rank = -1;  // Forced URLs have a rank of -1.
  for (size_t i = 0; i < new_list.size(); i++) {
    if (new_list[i].last_forced_time.is_null())
      rank++;
    DCHECK(new_list[i].last_forced_time.is_null() == (rank != -1))
        << "Forced URLs must all appear before non-forced URLs.";
    std::map<GURL, size_t>::iterator found = all_old_urls.find(new_list[i].url);
    if (found == all_old_urls.end()) {
      MostVisitedURLWithRank added;
      added.url = new_list[i];
      added.rank = rank;
      delta->added.push_back(added);
    } else {
      DCHECK(found->second != kAlreadyFoundMarker)
          << "Same URL appears twice in the new list.";
      int old_rank = found->second >= num_old_forced ?
          found->second - num_old_forced : -1;
      if (old_rank != rank ||
          old_list[found->second].last_forced_time !=
              new_list[i].last_forced_time) {
        MostVisitedURLWithRank moved;
        moved.url = new_list[i];
        moved.rank = rank;
        delta->moved.push_back(moved);
      }
      found->second = kAlreadyFoundMarker;
    }
  }

  for (const std::pair<GURL, size_t>& old_url : all_old_urls) {
    if (old_url.second != kAlreadyFoundMarker)
      delta->deleted.push_back(old_list[old_url.second]);
  }
}

Project: Chrome

bool DoTitlesDiffer(const MostVisitedURLList& old_list,
                    const MostVisitedURLList& new_list) {
  if (old_list.size() != new_list.size())
    return true;

  return !std::equal(std::begin(old_list), std::end(old_list),
                     std::begin(new_list),
                     [](const auto& old_item_ptr, const auto& new_item_ptr) {
                       return old_item_ptr.title == new_item_ptr.title;
                     });
}

Project: Chrome

bool TopSitesImpl::EncodeBitmap(const gfx::Image& bitmap,
                                scoped_refptr<base::RefCountedBytes>* bytes) {
  if (bitmap.IsEmpty())
    return false;
  *bytes = new base::RefCountedBytes();
  if (!gfx::JPEG1xEncodedDataFromImage(bitmap, kTopSitesImageQuality,
                                       &(*bytes)->data())) {
    return false;
  }

  (*bytes)->data().shrink_to_fit();
  return true;
}

Project: Chrome

bool ForcedURLComparator(const MostVisitedURL& first,
                         const MostVisitedURL& second) {
  DCHECK(!first.last_forced_time.is_null() &&
         !second.last_forced_time.is_null());
  return first.last_forced_time < second.last_forced_time;
}

Project: Chrome

void TopSitesImpl::GetMostVisitedURLs(
    const GetMostVisitedURLsCallback& callback,
    bool include_forced_urls) {
  MostVisitedURLList filtered_urls;
  {
    base::AutoLock lock(lock_);
    if (!loaded_) {
      pending_callbacks_.push_back(
          base::Bind(&RunOrPostGetMostVisitedURLsCallback,
                     base::RetainedRef(base::ThreadTaskRunnerHandle::Get()),
                     include_forced_urls, callback));
      return;
    }
    if (include_forced_urls) {
      filtered_urls = thread_safe_cache_->top_sites();
    } else {
      filtered_urls.assign(thread_safe_cache_->top_sites().begin() +
                              thread_safe_cache_->GetNumForcedURLs(),
                           thread_safe_cache_->top_sites().end());
    }
  }
  callback.Run(filtered_urls);
}

Project: Chrome

bool TopSitesImpl::GetPageThumbnail(
    const GURL& url,
    bool prefix_match,
    scoped_refptr<base::RefCountedMemory>* bytes) {
  {
    base::AutoLock lock(lock_);
    if (thread_safe_cache_->GetPageThumbnail(url, bytes))
      return true;
  }

  for (const auto& prepopulated_page : prepopulated_pages_) {
    if (url == prepopulated_page.most_visited.url) {
      *bytes =
          ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
              prepopulated_page.thumbnail_id, ui::SCALE_FACTOR_100P);
      return true;
    }
  }

  if (prefix_match) {
    std::vector<GURL> url_list;
    url_list.push_back(url);
    if (url.SchemeIsHTTPOrHTTPS())
      url_list.push_back(ToggleHTTPAndHTTPS(url));

    for (const GURL& url : url_list) {
      base::AutoLock lock(lock_);

      GURL canonical_url = thread_safe_cache_->GetGeneralizedCanonicalURL(url);
      if (!canonical_url.is_empty() &&
          thread_safe_cache_->GetPageThumbnail(canonical_url, bytes)) {
        return true;
      }
    }
  }

  return false;
}

Project: Chrome

bool TopSitesImpl::GetPageThumbnailScore(const GURL& url,
                                         ThumbnailScore* score) {
  base::AutoLock lock(lock_);
  return thread_safe_cache_->GetPageThumbnailScore(url, score);
}

Project: Chrome

int TopSitesImpl::GetRedirectDistanceForURL(const MostVisitedURL& most_visited,
                                            const GURL& url) {
  for (size_t i = 0; i < most_visited.redirects.size(); i++) {
    if (most_visited.redirects[i] == url)
      return static_cast<int>(most_visited.redirects.size() - i - 1);
  }
  NOTREACHED() << "URL should always be found.";
  return 0;
}

Project: Chrome

bool TopSitesImpl::GetTemporaryPageThumbnailScore(const GURL& url,
                                                  ThumbnailScore* score) {
  for (const TempImage& temp_image : temp_images_) {
    if (temp_image.first == url) {
      *score = temp_image.second.thumbnail_score;
      return true;
    }
  }
  return false;
}

Project: Chrome

std::string TopSitesImpl::GetURLHash(const GURL& url) {
  return base::MD5String(url.spec());
}

Project: Chrome

static int IndexOf(const MostVisitedURLList& urls, const GURL& url) {
  for (size_t i = 0; i < urls.size(); i++) {
    if (urls[i].url == url)
      return i;
  }
  return -1;
}

Project: Chrome

void TopSitesImpl::Init(const base::FilePath& db_name) {
  backend_ = new TopSitesBackend();
  backend_->Init(db_name);
  backend_->GetMostVisitedThumbnails(
      base::Bind(&TopSitesImpl::OnGotMostVisitedThumbnails,
                 base::Unretained(this)),
      &cancelable_task_tracker_);
}

Project: Chrome

bool TopSitesImpl::IsBlacklisted(const GURL& url) {
  DCHECK(thread_checker_.CalledOnValidThread());
  const base::DictionaryValue* blacklist =
      pref_service_->GetDictionary(kMostVisitedURLsBlacklist);
  return blacklist && blacklist->HasKey(GetURLHash(url));
}

Project: Chrome

bool TopSitesImpl::IsKnownURL(const GURL& url) {
  return loaded_ && cache_->IsKnownURL(url);
}

Project: Chrome

bool TopSitesImpl::IsNonForcedFull() {
  return loaded_ && cache_->GetNumNonForcedURLs() >= kNonForcedTopSitesNumber;
}

Project: Chrome

size_t TopSitesImpl::MergeCachedForcedURLs(MostVisitedURLList* new_list) const {
  DCHECK(thread_checker_.CalledOnValidThread());
  std::set<GURL> all_new_urls;
  size_t num_forced = 0;
  for (size_t i = 0; i < new_list->size(); ++i) {
    for (size_t j = 0; j < (*new_list)[i].redirects.size(); j++)
      all_new_urls.insert((*new_list)[i].redirects[j]);

    if (!(*new_list)[i].last_forced_time.is_null())
      ++num_forced;
  }

  MostVisitedURLList filtered_forced_urls;
  for (size_t i = 0; i < cache_->GetNumForcedURLs(); ++i) {
    if (all_new_urls.find(cache_->top_sites()[i].url) == all_new_urls.end())
      filtered_forced_urls.push_back(cache_->top_sites()[i]);
  }
  num_forced += filtered_forced_urls.size();

  new_list->insert(new_list->begin(), filtered_forced_urls.begin(),
                   filtered_forced_urls.end());
  std::inplace_merge(
      new_list->begin(), new_list->begin() + filtered_forced_urls.size(),
      new_list->begin() + num_forced, ForcedURLComparator);

  if (num_forced > kForcedTopSitesNumber) {
    new_list->erase(new_list->begin(),
                    new_list->begin() + (num_forced - kForcedTopSitesNumber));
    num_forced = kForcedTopSitesNumber;
  }

  return num_forced;
}

Project: Chrome

void TopSitesImpl::MoveStateToLoaded() {
  DCHECK(thread_checker_.CalledOnValidThread());

  MostVisitedURLList filtered_urls_all;
  MostVisitedURLList filtered_urls_nonforced;
  PendingCallbacks pending_callbacks;
  {
    base::AutoLock lock(lock_);

    if (loaded_)
      return;  // Don't do anything if we're already loaded.
    loaded_ = true;

    if (!pending_callbacks_.empty()) {
      filtered_urls_all = thread_safe_cache_->top_sites();
      filtered_urls_nonforced.assign(thread_safe_cache_->top_sites().begin() +
                                       thread_safe_cache_->GetNumForcedURLs(),
                                     thread_safe_cache_->top_sites().end());
      pending_callbacks.swap(pending_callbacks_);
    }
  }

  for (size_t i = 0; i < pending_callbacks.size(); i++)
    pending_callbacks[i].Run(filtered_urls_all, filtered_urls_nonforced);

  if (history_service_)
    history_service_observer_.Add(history_service_);

  NotifyTopSitesLoaded();
}

Project: Chrome

void TopSitesImpl::OnGotMostVisitedThumbnails(
    const scoped_refptr<MostVisitedThumbnails>& thumbnails) {
  DCHECK(thread_checker_.CalledOnValidThread());

  cache_->SetTopSites(thumbnails->most_visited);
  SetTopSites(thumbnails->most_visited,
              CALL_LOCATION_FROM_ON_GOT_MOST_VISITED_THUMBNAILS);
  cache_->SetThumbnails(thumbnails->url_to_images_map);

  ResetThreadSafeImageCache();

  MoveStateToLoaded();

  timer_.Start(FROM_HERE, kFirstDelayAtStartup, this,
               &TopSitesImpl::StartQueryForMostVisited);
}

Project: Chrome

void TopSitesImpl::OnTopSitesAvailableFromHistory(
    const MostVisitedURLList* pages) {
  DCHECK(pages);
  SetTopSites(*pages, CALL_LOCATION_FROM_OTHER_PLACES);
}

Project: Chrome

void TopSitesImpl::OnURLsDeleted(HistoryService* history_service,
                                 bool all_history,
                                 bool expired,
                                 const URLRows& deleted_rows,
                                 const std::set<GURL>& favicon_urls) {
  if (!loaded_)
    return;

  if (all_history) {
    SetTopSites(MostVisitedURLList(), CALL_LOCATION_FROM_OTHER_PLACES);
    backend_->ResetDatabase();
  } else {
    std::set<size_t> indices_to_delete;  // Indices into top_sites_.
    for (const auto& row : deleted_rows) {
      if (cache_->IsKnownURL(row.url()))
        indices_to_delete.insert(cache_->GetURLIndex(row.url()));
    }

    if (indices_to_delete.empty())
      return;

    MostVisitedURLList new_top_sites(cache_->top_sites());
    for (std::set<size_t>::reverse_iterator i = indices_to_delete.rbegin();
         i != indices_to_delete.rend(); i++) {
      new_top_sites.erase(new_top_sites.begin() + *i);
    }
    SetTopSites(new_top_sites, CALL_LOCATION_FROM_OTHER_PLACES);
  }
  StartQueryForMostVisited();
}

Project: Chrome

void TopSitesImpl::RemoveBlacklistedURL(const GURL& url) {
  DCHECK(thread_checker_.CalledOnValidThread());
  {
    DictionaryPrefUpdate update(pref_service_, kMostVisitedURLsBlacklist);
    base::DictionaryValue* blacklist = update.Get();
    blacklist->RemoveWithoutPathExpansion(GetURLHash(url), nullptr);
  }
  ResetThreadSafeCache();
  NotifyTopSitesChanged(TopSitesObserver::ChangeReason::BLACKLIST);
}

Project: Chrome

void TopSitesImpl::ResetThreadSafeCache() {
  base::AutoLock lock(lock_);
  MostVisitedURLList cached;
  ApplyBlacklist(cache_->top_sites(), &cached);
  thread_safe_cache_->SetTopSites(cached);
}

Project: Chrome

void TopSitesImpl::ResetThreadSafeImageCache() {
  base::AutoLock lock(lock_);
  thread_safe_cache_->SetThumbnails(cache_->images());
}

Project: Chrome

void RunOrPostGetMostVisitedURLsCallback(
    base::TaskRunner* task_runner,
    bool include_forced_urls,
    const TopSitesImpl::GetMostVisitedURLsCallback& callback,
    const MostVisitedURLList& all_urls,
    const MostVisitedURLList& nonforced_urls) {
  const MostVisitedURLList& urls =
      include_forced_urls ? all_urls : nonforced_urls;
  if (task_runner->RunsTasksInCurrentSequence())
    callback.Run(urls);
  else
    task_runner->PostTask(FROM_HERE, base::Bind(callback, urls));
}

Project: Chrome

void TopSitesImpl::ScheduleUpdateTimer() {
  if (timer_.IsRunning())
    return;

  timer_.Start(FROM_HERE, kDelayForUpdates, this,
               &TopSitesImpl::StartQueryForMostVisited);
}

Project: Chrome

bool TopSitesImpl::SetPageThumbnail(const GURL& url,
                                    const gfx::Image& thumbnail,
                                    const ThumbnailScore& score) {
  ThumbnailEvent result = SetPageThumbnailImpl(url, thumbnail, score);

  UMA_HISTOGRAM_ENUMERATION("Thumbnails.AddedToTopSites", result,
                            THUMBNAIL_EVENT_COUNT);

  switch (result) {
    case THUMBNAIL_FAILURE:
    case THUMBNAIL_TOPSITES_FULL:
    case THUMBNAIL_KEPT_EXISTING:
      return false;
    case THUMBNAIL_ADDED_TEMP:
    case THUMBNAIL_ADDED_REGULAR:
      return true;
    case THUMBNAIL_PROMOTED_TEMP_TO_REGULAR:
    case THUMBNAIL_EVENT_COUNT:
      NOTREACHED();
  }

  return false;
}

Project: Chrome

bool TopSitesImpl::SetPageThumbnailEncoded(
    const GURL& url,
    const base::RefCountedMemory* thumbnail,
    const ThumbnailScore& score) {
  DCHECK(cache_->IsKnownURL(url));

  if (!SetPageThumbnailInCache(url, thumbnail, score))
    return false;

  size_t index = cache_->GetURLIndex(url);
  int url_rank = index - cache_->GetNumForcedURLs();
  const MostVisitedURL& most_visited = cache_->top_sites()[index];
  backend_->SetPageThumbnail(most_visited,
                             url_rank < 0 ? -1 : url_rank,
                             *(cache_->GetImage(most_visited.url)));
  return true;
}

Project: Chrome

TopSitesImpl::ThumbnailEvent TopSitesImpl::SetPageThumbnailImpl(
    const GURL& url,
    const gfx::Image& thumbnail,
    const ThumbnailScore& score) {
  DCHECK(thread_checker_.CalledOnValidThread());

  if (!loaded_) {
    return THUMBNAIL_FAILURE;
  }

  bool add_temp_thumbnail = false;
  if (!IsKnownURL(url)) {
    if (IsNonForcedFull()) {
      return THUMBNAIL_TOPSITES_FULL;
    }

    add_temp_thumbnail = true;
  }

  if (!can_add_url_to_history_.Run(url))
    return THUMBNAIL_FAILURE;  // It's not a real webpage.

  scoped_refptr<base::RefCountedBytes> thumbnail_data;
  if (!EncodeBitmap(thumbnail, &thumbnail_data))
    return THUMBNAIL_FAILURE;

  if (add_temp_thumbnail) {
    RemoveTemporaryThumbnailByURL(url);
    AddTemporaryThumbnail(url, thumbnail_data.get(), score);
    return THUMBNAIL_ADDED_TEMP;
  }

  bool success = SetPageThumbnailEncoded(url, thumbnail_data.get(), score);
  return success ? THUMBNAIL_ADDED_REGULAR : THUMBNAIL_KEPT_EXISTING;
}

Project: Chrome

bool TopSitesImpl::SetPageThumbnailInCache(
    const GURL& url,
    const base::RefCountedMemory* thumbnail_data,
    const ThumbnailScore& score) {
  DCHECK(cache_->IsKnownURL(url));

  const MostVisitedURL& most_visited =
      cache_->top_sites()[cache_->GetURLIndex(url)];
  Images* image = cache_->GetImage(url);

  ThumbnailScore new_score_with_redirects(score);
  new_score_with_redirects.redirect_hops_from_dest =
      GetRedirectDistanceForURL(most_visited, url);

  if (image->thumbnail.get() &&
      !ShouldReplaceThumbnailWith(image->thumbnail_score,
                                  new_score_with_redirects)) {
    return false;  // The one we already have is better.
  }

  image->thumbnail = const_cast<base::RefCountedMemory*>(thumbnail_data);
  image->thumbnail_score = new_score_with_redirects;

  ResetThreadSafeImageCache();
  return true;
}

Project: Chrome

void TopSitesImpl::ShutdownOnUIThread() {
  history_service_ = nullptr;
  history_service_observer_.RemoveAll();
  cancelable_task_tracker_.TryCancelAll();
  if (backend_)
    backend_->Shutdown();
}

Project: Chrome

void TopSitesImpl::StartQueryForMostVisited() {
  DCHECK(loaded_);
  timer_.Stop();

  if (!history_service_)
    return;

  provider_->ProvideTopSites(
      num_results_to_request_from_history(),
      base::Bind(&TopSitesImpl::OnTopSitesAvailableFromHistory,
                 base::Unretained(this)),
      &cancelable_task_tracker_);
}

Project: Chrome

void TopSitesImpl::SyncWithHistory() {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (loaded_)
    StartQueryForMostVisited();
}

Project: Chrome

bool TopSitesImpl::loaded() const {
  return loaded_;
}

Project: Chrome

int TopSitesImpl::num_results_to_request_from_history() const {
  DCHECK(thread_checker_.CalledOnValidThread());

  const base::DictionaryValue* blacklist =
      pref_service_->GetDictionary(kMostVisitedURLsBlacklist);
  return kNonForcedTopSitesNumber + (blacklist ? blacklist->size() : 0);
}

Project: Chrome

  bool AddForcedURL(const GURL& url, base::Time time) {
    return top_sites()->AddForcedURL(url, time);
  }

Project: Chrome

  void AddPageToHistory(const GURL& url) {
    RedirectList redirects;
    redirects.push_back(url);
    history_service()->AddPage(
        url, base::Time::Now(), reinterpret_cast<ContextID>(1), 0, GURL(),
        redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED, false);
  }

Project: Chrome

  void AddPageToHistory(const GURL& url, const base::string16& title) {
    RedirectList redirects;
    redirects.push_back(url);
    history_service()->AddPage(
        url, base::Time::Now(), reinterpret_cast<ContextID>(1), 0, GURL(),
        redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED, false);
    history_service()->SetPageTitle(url, title);
  }

Project: Chrome

  void AddPageToHistory(const GURL& url,
                        const base::string16& title,
                        const history::RedirectList& redirects,
                        base::Time time) {
    history_service()->AddPage(
        url, time, reinterpret_cast<ContextID>(1), 0, GURL(),
        redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED,
        false);
    history_service()->SetPageTitle(url, title);
  }

Project: Chrome

  bool AddPrepopulatedPages(MostVisitedURLList* urls) {
    return top_sites()->AddPrepopulatedPages(urls, 0u);
  }

Project: Chrome

void AppendForcedMostVisitedURL(const GURL& url,
                                double last_forced_time,
                                std::vector<MostVisitedURL>* list) {
  MostVisitedURL mv;
  mv.url = url;
  mv.last_forced_time = base::Time::FromJsTime(last_forced_time);
  mv.redirects.push_back(url);
  list->push_back(mv);
}

Project: Chrome

void AppendMostVisitedURL(const GURL& url, std::vector<MostVisitedURL>* list) {
  MostVisitedURL mv;
  mv.url = url;
  mv.redirects.push_back(url);
  list->push_back(mv);
}

Project: Chrome

void AppendMostVisitedURLWithRedirect(const GURL& redirect_source,
                                      const GURL& redirect_dest,
                                      std::vector<MostVisitedURL>* list) {
  MostVisitedURL mv;
  mv.url = redirect_dest;
  mv.redirects.push_back(redirect_source);
  mv.redirects.push_back(redirect_dest);
  list->push_back(mv);
}

Project: Chrome

void AppendMostVisitedURLwithTitle(const GURL& url,
                                   const base::string16& title,
                                   std::vector<MostVisitedURL>* list) {
  MostVisitedURL mv;
  mv.url = url;
  mv.title = title;
  mv.redirects.push_back(url);
  list->push_back(mv);
}

Project: Chrome

  void ContainsPrepopulatePages(const TopSitesQuerier& querier,
                                size_t start_index) {
    PrepopulatedPageList prepopulate_pages = GetPrepopulatedPages();
    ASSERT_LE(start_index + prepopulate_pages.size(), querier.urls().size());
    for (size_t i = 0; i < prepopulate_pages.size(); ++i) {
      EXPECT_EQ(prepopulate_pages[i].most_visited.url.spec(),
                querier.urls()[start_index + i].url.spec())
          << " @ index " << i;
    }
  }

Project: Chrome

  gfx::Image CreateBitmap(SkColor color) {
    SkBitmap thumbnail;
    thumbnail.allocN32Pixels(4, 4);
    thumbnail.eraseColor(color);
    return gfx::Image::CreateFrom1xBitmap(thumbnail);  // adds ref.
  }

Project: Chrome

  void DeleteURL(const GURL& url) { history_service()->DeleteURL(url); }

Project: Chrome

  void DestroyTopSites() {
    if (top_sites_impl_) {
      top_sites_impl_->ShutdownOnUIThread();
      top_sites_impl_ = nullptr;

      scoped_task_environment_.RunUntilIdle();
    }
  }

Project: Chrome

  void EmptyThreadSafeCache() {
    base::AutoLock lock(top_sites()->lock_);
    MostVisitedURLList empty;
    top_sites()->thread_safe_cache_->SetTopSites(empty);
  }

Project: Chrome

  GURL GetCanonicalURL(const GURL& url) {
    return top_sites()->cache_->GetCanonicalURL(url);
  }

Project: Chrome

  PrepopulatedPageList GetPrepopulatedPages() {
    return top_sites()->GetPrepopulatedPages();
  }

Project: Chrome

  bool IsTopSitesLoaded() { return top_sites()->loaded_; }

Project: Chrome

bool MockCanAddURLToHistory(const GURL& url) {
  return url.is_valid() && !url.SchemeIs(kApplicationScheme);
}

Project: Chrome

  MockTopSitesObserver() {}

Project: Chrome

  void OnTopSitesAvailable(base::RunLoop* run_loop,
                           const history::MostVisitedURLList& data) {
    urls_ = data;
    number_of_callbacks_++;
    if (waiting_) {
      run_loop->QuitWhenIdle();
      waiting_ = false;
    }
  }

Project: Chrome

  void QueryAllTopSites(TopSitesImpl* top_sites,
                        bool wait,
                        bool include_forced_urls) {
    int start_number_of_callbacks = number_of_callbacks_;
    base::RunLoop run_loop;
    top_sites->GetMostVisitedURLs(
        base::Bind(&TopSitesQuerier::OnTopSitesAvailable,
                   weak_ptr_factory_.GetWeakPtr(), &run_loop),
        include_forced_urls);
    if (wait && start_number_of_callbacks == number_of_callbacks_) {
      waiting_ = true;
      run_loop.Run();
    }
  }

Project: Chrome

  void QueryTopSites(TopSitesImpl* top_sites, bool wait) {
    QueryAllTopSites(top_sites, wait, false);
  }

Project: Chrome

  void RecreateTopSitesAndBlock() {
    ResetTopSites();
    WaitTopSitesLoaded();
  }

Project: Chrome

  void ResetTopSites() {
    DestroyTopSites();
    DCHECK(!top_sites_impl_);
    PrepopulatedPageList prepopulated_pages;
    prepopulated_pages.push_back(PrepopulatedPage(GURL(kPrepopulatedPageURL),
                                                  base::string16(), -1, -1, 0));
    top_sites_impl_ = new TopSitesImpl(
        pref_service_.get(), history_service_.get(),
        std::make_unique<DefaultTopSitesProvider>(history_service_.get()),
        prepopulated_pages, base::Bind(MockCanAddURLToHistory));
    top_sites_impl_->Init(scoped_temp_dir_.GetPath().Append(kTopSitesFilename));
  }

Project: Chrome

  void SetTopSites(const MostVisitedURLList& new_top_sites) {
    top_sites()->SetTopSites(new_top_sites,
                             TopSitesImpl::CALL_LOCATION_FROM_OTHER_PLACES);
  }

Project: Chrome

  void StartQueryForMostVisited() { top_sites()->StartQueryForMostVisited(); }

Project: Chrome

  bool ThumbnailEqualsBytes(const gfx::Image& image,
                            base::RefCountedMemory* bytes) {
    scoped_refptr<base::RefCountedBytes> encoded_image;
    TopSitesImpl::EncodeBitmap(image, &encoded_image);
    return ThumbnailsAreEqual(encoded_image.get(), bytes);
  }

Project: Chrome

bool ThumbnailsAreEqual(base::RefCountedMemory* t1,
                        base::RefCountedMemory* t2) {
  if (!t1 || !t2)
    return false;
  if (t1->size() != t2->size())
    return false;
  return !memcmp(t1->front(), t2->front(), t1->size());
}

Project: Chrome

  TopSitesImplTest() {}

Project: Chrome

  TopSitesQuerier()
      : number_of_callbacks_(0), waiting_(false), weak_ptr_factory_(this) {}

Project: Chrome

  void WaitForHistory() {
    BlockUntilHistoryProcessesPendingRequests(history_service());
  }

Project: Chrome

  void WaitTopSitesLoaded() {
    DCHECK(top_sites_impl_);
    WaitTopSitesLoadedObserver wait_top_sites_loaded_observer(top_sites_impl_);
    wait_top_sites_loaded_observer.Run();
  }

Project: Chrome

  HistoryService* history_service() { return history_service_.get(); }

Project: Chrome

  bool is_notified() const { return is_notified_; }

Project: Chrome

  int number_of_callbacks() const { return number_of_callbacks_; }

Project: Chrome

  void set_urls(const MostVisitedURLList& urls) { urls_ = urls; }

Project: Chrome

  TopSitesImpl* top_sites() { return top_sites_impl_.get(); }

Project: Chrome

  const MostVisitedURLList& urls() const { return urls_; }