| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/history/top_sites_cache.h" |
| |
| #include "base/logging.h" |
| #include "base/memory/ref_counted_memory.h" |
| |
| namespace history { |
| |
| TopSitesCache::CanonicalURLQuery::CanonicalURLQuery(const GURL& url) { |
| most_visited_url_.redirects.push_back(url); |
| entry_.first = &most_visited_url_; |
| entry_.second = 0u; |
| } |
| |
| TopSitesCache::CanonicalURLQuery::~CanonicalURLQuery() { |
| } |
| |
| TopSitesCache::TopSitesCache() { |
| clear_query_ref_.ClearQuery(); |
| clear_query_ref_.ClearRef(); |
| clear_path_query_ref_.ClearQuery(); |
| clear_path_query_ref_.ClearRef(); |
| clear_path_query_ref_.ClearPath(); |
| } |
| |
| TopSitesCache::~TopSitesCache() { |
| } |
| |
| void TopSitesCache::SetTopSites(const MostVisitedURLList& top_sites) { |
| top_sites_ = top_sites; |
| CountForcedURLs(); |
| GenerateCanonicalURLs(); |
| } |
| |
| void TopSitesCache::SetThumbnails(const URLToImagesMap& images) { |
| images_ = images; |
| } |
| |
| Images* TopSitesCache::GetImage(const GURL& url) { |
| return &images_[GetCanonicalURL(url)]; |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| const GURL& TopSitesCache::GetCanonicalURL(const GURL& url) const { |
| CanonicalURLs::const_iterator it = GetCanonicalURLsIterator(url); |
| return it == canonical_urls_.end() ? url : it->first.first->url; |
| } |
| |
| 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()) { |
| // Test match ignoring "?query#ref". This also handles exact match. |
| if (url.ReplaceComponents(clear_query_ref_) == |
| GetURLFromIterator(it_hi).ReplaceComponents(clear_query_ref_)) { |
| return it_hi->first.first->url; |
| } |
| } |
| // Everything on or after |it_hi| is irrelevant. |
| |
| 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(); |
| } |
| // Everything before |it_lo| is irrelevant. |
| |
| // Search in [|it_lo|, |it_hi|) in reversed order. The first URL found that's |
| // a prefix of |url| (ignoring "?query#ref") would be returned. |
| 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(); |
| } |
| |
| bool TopSitesCache::IsKnownURL(const GURL& url) const { |
| return GetCanonicalURLsIterator(url) != canonical_urls_.end(); |
| } |
| |
| size_t TopSitesCache::GetURLIndex(const GURL& url) const { |
| DCHECK(IsKnownURL(url)); |
| return GetCanonicalURLsIterator(url)->second; |
| } |
| |
| size_t TopSitesCache::GetNumNonForcedURLs() const { |
| return top_sites_.size() - num_forced_urls_; |
| } |
| |
| size_t TopSitesCache::GetNumForcedURLs() const { |
| return num_forced_urls_; |
| } |
| |
| void TopSitesCache::CountForcedURLs() { |
| num_forced_urls_ = 0; |
| while (num_forced_urls_ < top_sites_.size()) { |
| // Forced sites are all at the beginning. |
| if (top_sites_[num_forced_urls_].last_forced_time.is_null()) |
| break; |
| num_forced_urls_++; |
| } |
| // In debug, ensure the cache user has no forced URLs pass that point. |
| 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."; |
| } |
| } |
| } |
| |
| void TopSitesCache::GenerateCanonicalURLs() { |
| canonical_urls_.clear(); |
| for (size_t i = 0; i < top_sites_.size(); i++) |
| StoreRedirectChain(top_sites_[i].redirects, i); |
| } |
| |
| void TopSitesCache::StoreRedirectChain(const RedirectList& redirects, |
| size_t destination) { |
| // |redirects| is empty if the user pinned a site and there are not enough top |
| // sites before the pinned site. |
| |
| // Map all the redirected URLs to the destination. |
| for (size_t i = 0; i < redirects.size(); i++) { |
| // If this redirect is already known, don't replace it with a new one. |
| if (!IsKnownURL(redirects[i])) { |
| CanonicalURLEntry entry; |
| entry.first = &(top_sites_[destination]); |
| entry.second = i; |
| canonical_urls_[entry] = destination; |
| } |
| } |
| } |
| |
| TopSitesCache::CanonicalURLs::const_iterator |
| TopSitesCache::GetCanonicalURLsIterator(const GURL& url) const { |
| return canonical_urls_.find(CanonicalURLQuery(url).entry()); |
| } |
| |
| const GURL& TopSitesCache::GetURLFromIterator( |
| CanonicalURLs::const_iterator it) const { |
| DCHECK(it != canonical_urls_.end()); |
| return it->first.first->redirects[it->first.second]; |
| } |
| |
| } // namespace history |