1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/history/top_sites_cache.h" 6 7 #include "base/logging.h" 8 #include "base/memory/ref_counted_memory.h" 9 10 namespace history { 11 12 TopSitesCache::CanonicalURLQuery::CanonicalURLQuery(const GURL& url) { 13 most_visited_url_.redirects.push_back(url); 14 entry_.first = &most_visited_url_; 15 entry_.second = 0u; 16 } 17 18 TopSitesCache::CanonicalURLQuery::~CanonicalURLQuery() { 19 } 20 21 TopSitesCache::TopSitesCache() { 22 clear_query_ref_.ClearQuery(); 23 clear_query_ref_.ClearRef(); 24 clear_path_query_ref_.ClearQuery(); 25 clear_path_query_ref_.ClearRef(); 26 clear_path_query_ref_.ClearPath(); 27 } 28 29 TopSitesCache::~TopSitesCache() { 30 } 31 32 void TopSitesCache::SetTopSites(const MostVisitedURLList& top_sites) { 33 top_sites_ = top_sites; 34 CountForcedURLs(); 35 GenerateCanonicalURLs(); 36 } 37 38 void TopSitesCache::SetThumbnails(const URLToImagesMap& images) { 39 images_ = images; 40 } 41 42 Images* TopSitesCache::GetImage(const GURL& url) { 43 return &images_[GetCanonicalURL(url)]; 44 } 45 46 bool TopSitesCache::GetPageThumbnail( 47 const GURL& url, 48 scoped_refptr<base::RefCountedMemory>* bytes) const { 49 std::map<GURL, Images>::const_iterator found = 50 images_.find(GetCanonicalURL(url)); 51 if (found != images_.end()) { 52 base::RefCountedMemory* data = found->second.thumbnail.get(); 53 if (data) { 54 *bytes = data; 55 return true; 56 } 57 } 58 return false; 59 } 60 61 bool TopSitesCache::GetPageThumbnailScore(const GURL& url, 62 ThumbnailScore* score) const { 63 std::map<GURL, Images>::const_iterator found = 64 images_.find(GetCanonicalURL(url)); 65 if (found != images_.end()) { 66 *score = found->second.thumbnail_score; 67 return true; 68 } 69 return false; 70 } 71 72 const GURL& TopSitesCache::GetCanonicalURL(const GURL& url) const { 73 CanonicalURLs::const_iterator it = GetCanonicalURLsIterator(url); 74 return it == canonical_urls_.end() ? url : it->first.first->url; 75 } 76 77 GURL TopSitesCache::GetGeneralizedCanonicalURL(const GURL& url) const { 78 CanonicalURLs::const_iterator it_hi = 79 canonical_urls_.lower_bound(CanonicalURLQuery(url).entry()); 80 if (it_hi != canonical_urls_.end()) { 81 // Test match ignoring "?query#ref". This also handles exact match. 82 if (url.ReplaceComponents(clear_query_ref_) == 83 GetURLFromIterator(it_hi).ReplaceComponents(clear_query_ref_)) { 84 return it_hi->first.first->url; 85 } 86 } 87 // Everything on or after |it_hi| is irrelevant. 88 89 GURL base_url(url.ReplaceComponents(clear_path_query_ref_)); 90 CanonicalURLs::const_iterator it_lo = 91 canonical_urls_.lower_bound(CanonicalURLQuery(base_url).entry()); 92 if (it_lo == canonical_urls_.end()) 93 return GURL::EmptyGURL(); 94 GURL compare_url_lo(GetURLFromIterator(it_lo)); 95 if (!HaveSameSchemeHostAndPort(base_url, compare_url_lo) || 96 !IsPathPrefix(base_url.path(), compare_url_lo.path())) { 97 return GURL::EmptyGURL(); 98 } 99 // Everything before |it_lo| is irrelevant. 100 101 // Search in [|it_lo|, |it_hi|) in reversed order. The first URL found that's 102 // a prefix of |url| (ignoring "?query#ref") would be returned. 103 for (CanonicalURLs::const_iterator it = it_hi; it != it_lo;) { 104 --it; 105 GURL compare_url(GetURLFromIterator(it)); 106 DCHECK(HaveSameSchemeHostAndPort(compare_url, url)); 107 if (IsPathPrefix(compare_url.path(), url.path())) 108 return it->first.first->url; 109 } 110 111 return GURL::EmptyGURL(); 112 } 113 114 bool TopSitesCache::IsKnownURL(const GURL& url) const { 115 return GetCanonicalURLsIterator(url) != canonical_urls_.end(); 116 } 117 118 size_t TopSitesCache::GetURLIndex(const GURL& url) const { 119 DCHECK(IsKnownURL(url)); 120 return GetCanonicalURLsIterator(url)->second; 121 } 122 123 size_t TopSitesCache::GetNumNonForcedURLs() const { 124 return top_sites_.size() - num_forced_urls_; 125 } 126 127 size_t TopSitesCache::GetNumForcedURLs() const { 128 return num_forced_urls_; 129 } 130 131 void TopSitesCache::CountForcedURLs() { 132 num_forced_urls_ = 0; 133 while (num_forced_urls_ < top_sites_.size()) { 134 // Forced sites are all at the beginning. 135 if (top_sites_[num_forced_urls_].last_forced_time.is_null()) 136 break; 137 num_forced_urls_++; 138 } 139 // In debug, ensure the cache user has no forced URLs pass that point. 140 if (DCHECK_IS_ON()) { 141 for (size_t i = num_forced_urls_; i < top_sites_.size(); ++i) { 142 DCHECK(top_sites_[i].last_forced_time.is_null()) 143 << "All the forced URLs must appear before non-forced URLs."; 144 } 145 } 146 } 147 148 void TopSitesCache::GenerateCanonicalURLs() { 149 canonical_urls_.clear(); 150 for (size_t i = 0; i < top_sites_.size(); i++) 151 StoreRedirectChain(top_sites_[i].redirects, i); 152 } 153 154 void TopSitesCache::StoreRedirectChain(const RedirectList& redirects, 155 size_t destination) { 156 // |redirects| is empty if the user pinned a site and there are not enough top 157 // sites before the pinned site. 158 159 // Map all the redirected URLs to the destination. 160 for (size_t i = 0; i < redirects.size(); i++) { 161 // If this redirect is already known, don't replace it with a new one. 162 if (!IsKnownURL(redirects[i])) { 163 CanonicalURLEntry entry; 164 entry.first = &(top_sites_[destination]); 165 entry.second = i; 166 canonical_urls_[entry] = destination; 167 } 168 } 169 } 170 171 TopSitesCache::CanonicalURLs::const_iterator 172 TopSitesCache::GetCanonicalURLsIterator(const GURL& url) const { 173 return canonical_urls_.find(CanonicalURLQuery(url).entry()); 174 } 175 176 const GURL& TopSitesCache::GetURLFromIterator( 177 CanonicalURLs::const_iterator it) const { 178 DCHECK(it != canonical_urls_.end()); 179 return it->first.first->redirects[it->first.second]; 180 } 181 182 } // namespace history 183