summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhuangs@chromium.org <huangs@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-17 01:48:51 +0000
committerhuangs@chromium.org <huangs@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-17 01:48:51 +0000
commit420e6d9eda84004a3abe49af74e246f88f37a64b (patch)
treecd44e2d6f39a0e2fe9e51e470f86747e7003ac77
parent110babd3c23b9d7fde9030929227ce3058ea6d01 (diff)
downloadchromium_src-420e6d9eda84004a3abe49af74e246f88f37a64b.zip
chromium_src-420e6d9eda84004a3abe49af74e246f88f37a64b.tar.gz
chromium_src-420e6d9eda84004a3abe49af74e246f88f37a64b.tar.bz2
Implementing URL prefix match for history thumbnail cache.
We wish to make MostLikely URL recommendations use local thumbnail. To increase the likelihood of a hit, this CL enables a thumbnail request to perform URL prefix match. For example, if thumbnail is stored for ==> http://www.chromium.org/Home but not ==> http://www.chromium.org/ then previously the request ==> chrome://thumb/http://www.chromium.org/ would fail. This CL aims to create a fallback, by adding "thumb2" that matches prefix, i.e.: ==> chrome://thumb2/http://www.chromium.org/ would match "http://www.chromium.org/Home" and display the corresponding thumbnail. Details: - We consider "URL prefix" match, which is not same as "string prefix" match. Specifics: --- Need identical protocol/scheme, host, and port (GURL is smart about this). --- Query strings are ignored. --- For path, we require entire path components to match. E.g., "base/test" is a prefix of "base/test/sub", but is NOT a prefix of "base/testing" or "best/test-case". - If multiple matches exist, the first URL-lexicographical match is taken. This allows the "nearest" children to be matched, but favors siblings that are alphabetically closer. For example, "base" prefers "base/test" over "base/test/sub", but also prefers "base/deep/sub/dir/" over "base/test" (since "deep" < "test"). - To implement the above match, a new comparator is used in the thumbnail cache. - Adding new test TopSitesCacheTest, which also tests existing code. - Adding url_utils.cc in chrome\browser\history, along with unit tests. - Adding the "chrome://thumb2" path, which causes most of the 1-2-line changes in files. BUG=284634 TEST=TopSitesCacheTest.* Review URL: https://chromiumcodereview.appspot.com/23477033 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@223508 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/android/dev_tools_server.cc2
-rw-r--r--chrome/browser/android/most_visited_sites.cc2
-rw-r--r--chrome/browser/android/provider/chrome_browser_provider.cc2
-rw-r--r--chrome/browser/devtools/browser_list_tabcontents_provider.cc2
-rw-r--r--chrome/browser/extensions/extension_service.cc2
-rw-r--r--chrome/browser/history/expire_history_backend_unittest.cc2
-rw-r--r--chrome/browser/history/top_sites.h7
-rw-r--r--chrome/browser/history/top_sites_cache.cc29
-rw-r--r--chrome/browser/history/top_sites_cache.h33
-rw-r--r--chrome/browser/history/top_sites_cache_unittest.cc175
-rw-r--r--chrome/browser/history/top_sites_impl.cc17
-rw-r--r--chrome/browser/history/top_sites_impl.h4
-rw-r--r--chrome/browser/history/top_sites_impl_unittest.cc26
-rw-r--r--chrome/browser/history/url_utils.cc73
-rw-r--r--chrome/browser/history/url_utils.h42
-rw-r--r--chrome/browser/history/url_utils_unittest.cc92
-rw-r--r--chrome/browser/search/instant_service.cc3
-rw-r--r--chrome/browser/thumbnails/thumbnail_service.h5
-rw-r--r--chrome/browser/thumbnails/thumbnail_service_impl.cc7
-rw-r--r--chrome/browser/thumbnails/thumbnail_service_impl.h3
-rw-r--r--chrome/browser/ui/webui/ntp/most_visited_handler.cc7
-rw-r--r--chrome/browser/ui/webui/ntp/suggestions_page_handler.cc2
-rw-r--r--chrome/browser/ui/webui/ntp/thumbnail_source.cc10
-rw-r--r--chrome/browser/ui/webui/ntp/thumbnail_source.h7
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi2
-rw-r--r--chrome/common/url_constants.cc1
-rw-r--r--chrome/common/url_constants.h1
28 files changed, 521 insertions, 39 deletions
diff --git a/chrome/browser/android/dev_tools_server.cc b/chrome/browser/android/dev_tools_server.cc
index 2d00ef1..771aa1e 100644
--- a/chrome/browser/android/dev_tools_server.cc
+++ b/chrome/browser/android/dev_tools_server.cc
@@ -75,7 +75,7 @@ class DevToolsServerDelegate : public content::DevToolsHttpHandlerDelegate {
history::TopSites* top_sites = profile->GetTopSites();
if (top_sites) {
scoped_refptr<base::RefCountedMemory> data;
- if (top_sites->GetPageThumbnail(url, &data))
+ if (top_sites->GetPageThumbnail(url, false, &data))
return std::string(reinterpret_cast<const char*>(data->front()),
data->size());
}
diff --git a/chrome/browser/android/most_visited_sites.cc b/chrome/browser/android/most_visited_sites.cc
index 79ca5c8..c4a04fa 100644
--- a/chrome/browser/android/most_visited_sites.cc
+++ b/chrome/browser/android/most_visited_sites.cc
@@ -112,7 +112,7 @@ void GetUrlThumbnailTask(
GURL gurl(url_string);
scoped_refptr<base::RefCountedMemory> data;
- if (top_sites->GetPageThumbnail(gurl, &data)) {
+ if (top_sites->GetPageThumbnail(gurl, false, &data)) {
SkBitmap thumbnail_bitmap = ExtractThumbnail(*data.get());
if (!thumbnail_bitmap.empty()) {
j_bitmap_ref->Reset(
diff --git a/chrome/browser/android/provider/chrome_browser_provider.cc b/chrome/browser/android/provider/chrome_browser_provider.cc
index 563f46a..f319c66 100644
--- a/chrome/browser/android/provider/chrome_browser_provider.cc
+++ b/chrome/browser/android/provider/chrome_browser_provider.cc
@@ -1563,7 +1563,7 @@ ScopedJavaLocalRef<jbyteArray> ChromeBrowserProvider::GetThumbnail(
// GetPageThumbnail is synchronous and can be called from any thread.
scoped_refptr<base::RefCountedMemory> thumbnail;
if (top_sites_)
- top_sites_->GetPageThumbnail(url, &thumbnail);
+ top_sites_->GetPageThumbnail(url, false, &thumbnail);
if (!thumbnail.get() || !thumbnail->front()) {
return ScopedJavaLocalRef<jbyteArray>();
diff --git a/chrome/browser/devtools/browser_list_tabcontents_provider.cc b/chrome/browser/devtools/browser_list_tabcontents_provider.cc
index 451ed84..1e43a49 100644
--- a/chrome/browser/devtools/browser_list_tabcontents_provider.cc
+++ b/chrome/browser/devtools/browser_list_tabcontents_provider.cc
@@ -78,7 +78,7 @@ std::string BrowserListTabContentsProvider::GetPageThumbnailData(
if (!top_sites)
continue;
scoped_refptr<base::RefCountedMemory> data;
- if (top_sites->GetPageThumbnail(url, &data))
+ if (top_sites->GetPageThumbnail(url, false, &data))
return std::string(
reinterpret_cast<const char*>(data->front()), data->size());
}
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 838d2f9..9256742 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -1166,7 +1166,7 @@ void ExtensionService::NotifyExtensionLoaded(const Extension* extension) {
// Same for chrome://thumb/ resources.
if (extensions::PermissionsData::HasHostPermission(
extension, GURL(chrome::kChromeUIThumbnailURL))) {
- ThumbnailSource* thumbnail_source = new ThumbnailSource(profile_);
+ ThumbnailSource* thumbnail_source = new ThumbnailSource(profile_, false);
content::URLDataSource::Add(profile_, thumbnail_source);
}
}
diff --git a/chrome/browser/history/expire_history_backend_unittest.cc b/chrome/browser/history/expire_history_backend_unittest.cc
index 9e1f6867..fd91b96 100644
--- a/chrome/browser/history/expire_history_backend_unittest.cc
+++ b/chrome/browser/history/expire_history_backend_unittest.cc
@@ -311,7 +311,7 @@ bool ExpireHistoryTest::HasThumbnail(URLID url_id) {
return false;
GURL url = info.url();
scoped_refptr<base::RefCountedMemory> data;
- return top_sites_->GetPageThumbnail(url, &data);
+ return top_sites_->GetPageThumbnail(url, false, &data);
}
void ExpireHistoryTest::EnsureURLInfoGone(const URLRow& row) {
diff --git a/chrome/browser/history/top_sites.h b/chrome/browser/history/top_sites.h
index 6d57d12..e1ae857 100644
--- a/chrome/browser/history/top_sites.h
+++ b/chrome/browser/history/top_sites.h
@@ -73,10 +73,15 @@ class TopSites
// Get a thumbnail for a given page. Returns true iff we have the thumbnail.
// This may be invoked on any thread.
+ // If an exact thumbnail URL match fails, |prefix_match| specifies whether or
+ // not to try harder by matching the query thumbnail URL as URL prefix (as
+ // defined by UrlIsPrefix()).
// As this method may be invoked on any thread the ref count needs to be
// incremented before this method returns, so this takes a scoped_refptr*.
virtual bool GetPageThumbnail(
- const GURL& url, scoped_refptr<base::RefCountedMemory>* bytes) = 0;
+ const GURL& url,
+ bool prefix_match,
+ scoped_refptr<base::RefCountedMemory>* bytes) = 0;
// Get a thumbnail score for a given page. Returns true iff we have the
// thumbnail score. This may be invoked on any thread. The score will
diff --git a/chrome/browser/history/top_sites_cache.cc b/chrome/browser/history/top_sites_cache.cc
index f94013f..6552f73 100644
--- a/chrome/browser/history/top_sites_cache.cc
+++ b/chrome/browser/history/top_sites_cache.cc
@@ -55,7 +55,12 @@ bool TopSitesCache::GetPageThumbnailScore(const GURL& url,
}
const GURL& TopSitesCache::GetCanonicalURL(const GURL& url) {
- CanonicalURLs::iterator i = TopSitesCache::GetCanonicalURLsIterator(url);
+ CanonicalURLs::iterator i = GetCanonicalURLsIterator(url);
+ return i == canonical_urls_.end() ? url : i->first.first->url;
+}
+
+const GURL& TopSitesCache::GetCanonicalURLForPrefix(const GURL& url) {
+ CanonicalURLs::iterator i = GetCanonicalURLsIteratorForPrefix(url);
return i == canonical_urls_.end() ? url : i->first.first->url;
}
@@ -76,7 +81,7 @@ void TopSitesCache::GenerateCanonicalURLs() {
void TopSitesCache::StoreRedirectChain(const RedirectList& redirects,
size_t destination) {
- // redirects is empty if the user pinned a site and there are not enough top
+ // |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.
@@ -101,4 +106,24 @@ TopSitesCache::CanonicalURLs::iterator TopSitesCache::GetCanonicalURLsIterator(
return canonical_urls_.find(entry);
}
+TopSitesCache::CanonicalURLs::iterator
+ TopSitesCache::GetCanonicalURLsIteratorForPrefix(const GURL& prefix_url) {
+ MostVisitedURL most_visited_url;
+ most_visited_url.redirects.push_back(prefix_url);
+ CanonicalURLEntry entry;
+ entry.first = &most_visited_url;
+ entry.second = 0u;
+
+ // Perform effective binary search for URL prefix search.
+ TopSitesCache::CanonicalURLs::iterator it =
+ canonical_urls_.lower_bound(entry);
+ // Perform prefix match.
+ if (it != canonical_urls_.end()) {
+ const GURL& comp_url = it->first.first->redirects[it->first.second];
+ if (!UrlIsPrefix(prefix_url, comp_url))
+ it = canonical_urls_.end();
+ }
+ return it;
+}
+
} // namespace history
diff --git a/chrome/browser/history/top_sites_cache.h b/chrome/browser/history/top_sites_cache.h
index 822d189..939f88d 100644
--- a/chrome/browser/history/top_sites_cache.h
+++ b/chrome/browser/history/top_sites_cache.h
@@ -10,9 +10,27 @@
#include "base/memory/ref_counted.h"
#include "chrome/browser/history/history_types.h"
+#include "chrome/browser/history/url_utils.h"
namespace history {
+// TopSiteCache caches thumbnails for visited pages. Retrieving thumbnails from
+// a given input URL is a two-stage process:
+//
+// input URL --(map 1)--> canonical URL --(map 2)--> image.
+//
+// (map 1) searches input URL in |canonical_urls_|. canonical URL is
+// assigned to the resulting value if found; else input URL.
+//
+// (map 2) simply looks up canonical URL in |images_|.
+//
+// TopSiteCache also provides GetCanonicalURLsIteratorForPrefix(), which is an
+// alternative implementation of (map 1) that does the following:
+// - if canonical URL is a key in |canonical_urls_|, return the value.
+// - else if canonical URL is a "URL prefix" (see comment in url_utils.h) of
+// some key in |canonical_urls_|, return the value corresponding to the key.
+// - else return input URL.
+
// TopSitesCache caches the top sites and thumbnails for TopSites.
class TopSitesCache {
public:
@@ -44,6 +62,11 @@ class TopSitesCache {
// Returns the canonical URL for |url|.
const GURL& GetCanonicalURL(const GURL& url);
+ // Returns the canonical URL for |url_prefix| that matches by prefix.
+ // Multiple matches exst, returns the canonical URL for the first
+ // matching entry under lexicographical order.
+ const GURL& GetCanonicalURLForPrefix(const GURL& url_prefix);
+
// Returns true if |url| is known.
bool IsKnownURL(const GURL& url);
@@ -60,7 +83,8 @@ class TopSitesCache {
public:
bool operator()(const CanonicalURLEntry& e1,
const CanonicalURLEntry& e2) const {
- return e1.first->redirects[e1.second] < e2.first->redirects[e2.second];
+ return CanonicalURLStringCompare(e1.first->redirects[e1.second].spec(),
+ e2.first->redirects[e2.second].spec());
}
};
@@ -80,9 +104,14 @@ class TopSitesCache {
// Stores a set of redirects. This is used by GenerateCanonicalURLs.
void StoreRedirectChain(const RedirectList& redirects, size_t destination);
- // Returns the iterator into canconical_urls_ for the specified url.
+ // Returns the iterator into |canonical_urls_| for the |url|.
CanonicalURLs::iterator GetCanonicalURLsIterator(const GURL& url);
+ // Returns the first iterator into |canonical_urls_| for which |prefix_url|
+ // is a URL prefix. Returns |canonical_urls_.end()| if no match is found.
+ CanonicalURLs::iterator GetCanonicalURLsIteratorForPrefix(
+ const GURL& prefix_url);
+
// The top sites.
MostVisitedURLList top_sites_;
diff --git a/chrome/browser/history/top_sites_cache_unittest.cc b/chrome/browser/history/top_sites_cache_unittest.cc
new file mode 100644
index 0000000..3920c7a
--- /dev/null
+++ b/chrome/browser/history/top_sites_cache_unittest.cc
@@ -0,0 +1,175 @@
+// Copyright 2013 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 <set>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace history {
+
+namespace {
+
+class TopSitesCacheTest : public testing::Test {
+ public:
+ TopSitesCacheTest() {
+ }
+
+ protected:
+ // Initializes |top_sites_| and |cache_| based on |spec|, which is a list of
+ // URL strings with optional indents: indentated URLs redirect to the last
+ // non-indented URL. Titles are assigned as "Title 1", "Title 2", etc., in the
+ // order of appearance. See |kTopSitesSpecBasic| for an example.
+ void InitTopSiteCache(const char** spec, int size);
+
+ MostVisitedURLList top_sites_;
+ TopSitesCache cache_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TopSitesCacheTest);
+};
+
+void TopSitesCacheTest::InitTopSiteCache(const char** spec, int size) {
+ std::set<std::string> urls_seen;
+ for (int 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.
+ string16 title(ASCIIToUTF16("Title ") +
+ base::Uint64ToString16(top_sites_.size() + 1));
+ top_sites_.push_back(MostVisitedURL(GURL(spec_item), title));
+ }
+ ASSERT_TRUE(!top_sites_.empty());
+ // Set up redirect to canonical URL. Canonical URL redirects to itself, too.
+ top_sites_.back().redirects.push_back(GURL(spec_item));
+ }
+ cache_.SetTopSites(top_sites_);
+}
+
+const char* kTopSitesSpecBasic[] = {
+ "http://www.google.com",
+ " http://www.gogle.com", // Redirects.
+ " http://www.gooogle.com", // Redirects.
+ "http://www.youtube.com/a/b",
+ " http://www.youtube.com/a/b?test=1", // Redirects.
+ "https://www.google.com/",
+ " https://www.gogle.com", // Redirects.
+ "http://www.example.com:3141/",
+};
+
+TEST_F(TopSitesCacheTest, GetCanonicalURL) {
+ InitTopSiteCache(kTopSitesSpecBasic, arraysize(kTopSitesSpecBasic));
+ struct {
+ const char* expected;
+ const char* query;
+ } test_cases[] = {
+ // Already is canonical: redirects.
+ {"http://www.google.com/", "http://www.google.com"},
+ // Exact match with stored URL: redirects.
+ {"http://www.google.com/", "http://www.gooogle.com"},
+ // Recognizes despite trailing "/": redirects
+ {"http://www.google.com/", "http://www.gooogle.com/"},
+ // Exact match with URL with query: redirects.
+ {"http://www.youtube.com/a/b", "http://www.youtube.com/a/b?test=1"},
+ // No match with URL with query: as-is.
+ {"http://www.youtube.com/a/b?test", "http://www.youtube.com/a/b?test"},
+ // Never-seen-before URL: as-is.
+ {"http://maps.google.com/", "http://maps.google.com/"},
+ // Changing port number, does not match: as-is.
+ {"http://www.example.com:1234/", "http://www.example.com:1234"},
+ // Smart enough to know that port 80 is HTTP: redirects.
+ {"http://www.google.com/", "http://www.gooogle.com:80"},
+ // Prefix should not work: as-is.
+ {"http://www.youtube.com/a", "http://www.youtube.com/a"},
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+ std::string expected(test_cases[i].expected);
+ std::string query(test_cases[i].query);
+ EXPECT_EQ(expected, cache_.GetCanonicalURL(GURL(query)).spec())
+ << " for test_case[" << i << "]";
+ }
+}
+
+TEST_F(TopSitesCacheTest, IsKnownUrl) {
+ InitTopSiteCache(kTopSitesSpecBasic, arraysize(kTopSitesSpecBasic));
+ // Matches.
+ EXPECT_TRUE(cache_.IsKnownURL(GURL("http://www.google.com")));
+ EXPECT_TRUE(cache_.IsKnownURL(GURL("http://www.gooogle.com")));
+ EXPECT_TRUE(cache_.IsKnownURL(GURL("http://www.google.com/")));
+
+ // Non-matches.
+ EXPECT_FALSE(cache_.IsKnownURL(GURL("http://www.google.com?")));
+ EXPECT_FALSE(cache_.IsKnownURL(GURL("http://www.google.net")));
+ EXPECT_FALSE(cache_.IsKnownURL(GURL("http://www.google.com/stuff")));
+ EXPECT_FALSE(cache_.IsKnownURL(GURL("https://www.gooogle.com")));
+ EXPECT_FALSE(cache_.IsKnownURL(GURL("http://www.youtube.com/a")));
+}
+
+const char* kTopSitesSpecPrefix[] = {
+ "http://www.google.com/",
+ " http://www.google.com/test?q=3", // Redirects.
+ " http://www.google.com/test/y?b", // Redirects.
+ "http://www.google.com/2",
+ " http://www.google.com/test/q", // Redirects.
+ " http://www.google.com/test/y?a", // Redirects.
+ "http://www.google.com/3",
+ " http://www.google.com/testing", // Redirects.
+ "http://www.google.com/test-hyphen",
+ "http://www.google.com/sh",
+ " http://www.google.com/sh/1/2", // Redirects.
+ "http://www.google.com/sh/1",
+};
+
+TEST_F(TopSitesCacheTest, GetCanonicalURLForPrefix) {
+ InitTopSiteCache(kTopSitesSpecPrefix, arraysize(kTopSitesSpecPrefix));
+ struct {
+ const char* expected;
+ const char* query;
+ } test_cases[] = {
+ // Already is canonical: redirects.
+ {"http://www.google.com/", "http://www.google.com"},
+ // Exact match with stored URL: redirects.
+ {"http://www.google.com/", "http://www.google.com/test?q=3"},
+ // Prefix match: redirects.
+ {"http://www.google.com/", "http://www.google.com/test"},
+ // Competing prefix match: redirects to closest.
+ {"http://www.google.com/2", "http://www.google.com/test/q"},
+ // Multiple prefix matches: redirects to first.
+ {"http://www.google.com/2", "http://www.google.com/test/y"},
+ // No prefix match: as-is.
+ {"http://www.google.com/no-match", "http://www.google.com/no-match"},
+ // String prefix match but not URL-prefix match: as-is.
+ {"http://www.google.com/t", "http://www.google.com/t"},
+ // Different protocol: as-is.
+ {"https://www.google.com/test", "https://www.google.com/test"},
+ // Smart enough to know that port 80 is HTTP: redirects.
+ {"http://www.google.com/", "http://www.google.com:80/test"},
+ // Exact match, unaffected by "http://www.google.com/sh/1": redirects.
+ {"http://www.google.com/sh", "http://www.google.com/sh/1/2"},
+ // Suffix match only: as-is
+ {"http://www.google.com/sh/1/2/3", "http://www.google.com/sh/1/2/3"},
+ // Exact match, unaffected by "http://www.google.com/sh": redirects.
+ {"http://www.google.com/sh/1", "http://www.google.com/sh/1"},
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+ std::string expected(test_cases[i].expected);
+ std::string query(test_cases[i].query);
+ EXPECT_EQ(expected, cache_.GetCanonicalURLForPrefix(GURL(query)).spec())
+ << " for test_case[" << i << "]";
+ }
+}
+
+} // namespace
+
+} // namespace history
diff --git a/chrome/browser/history/top_sites_impl.cc b/chrome/browser/history/top_sites_impl.cc
index 3a1077a..8920e1b 100644
--- a/chrome/browser/history/top_sites_impl.cc
+++ b/chrome/browser/history/top_sites_impl.cc
@@ -212,8 +212,11 @@ void TopSitesImpl::GetMostVisitedURLs(
}
bool TopSitesImpl::GetPageThumbnail(
- const GURL& url, scoped_refptr<base::RefCountedMemory>* bytes) {
+ const GURL& url,
+ bool prefix_match,
+ scoped_refptr<base::RefCountedMemory>* bytes) {
// WARNING: this may be invoked on any thread.
+ // Perform exact match.
{
base::AutoLock lock(lock_);
if (thread_safe_cache_->GetPageThumbnail(url, bytes))
@@ -231,6 +234,18 @@ bool TopSitesImpl::GetPageThumbnail(
}
}
+ if (prefix_match) {
+ // Still not found, so strip "?query#ref", and perform prefix match.
+ GURL::Replacements replacements;
+ replacements.ClearQuery();
+ replacements.ClearRef();
+ GURL url_stripped(url.ReplaceComponents(replacements));
+ base::AutoLock lock(lock_);
+ GURL canonical_url(
+ thread_safe_cache_->GetCanonicalURLForPrefix(url_stripped));
+ if (thread_safe_cache_->GetPageThumbnail(canonical_url, bytes))
+ return true;
+ }
return false;
}
diff --git a/chrome/browser/history/top_sites_impl.h b/chrome/browser/history/top_sites_impl.h
index 678bf63..5417b4a 100644
--- a/chrome/browser/history/top_sites_impl.h
+++ b/chrome/browser/history/top_sites_impl.h
@@ -65,7 +65,9 @@ class TopSitesImpl : public TopSites {
virtual void GetMostVisitedURLs(
const GetMostVisitedURLsCallback& callback) OVERRIDE;
virtual bool GetPageThumbnail(
- const GURL& url, scoped_refptr<base::RefCountedMemory>* bytes) OVERRIDE;
+ const GURL& url,
+ bool prefix_match,
+ scoped_refptr<base::RefCountedMemory>* bytes) OVERRIDE;
virtual bool GetPageThumbnailScore(const GURL& url,
ThumbnailScore* score) OVERRIDE;
virtual bool GetTemporaryPageThumbnailScore(const GURL& url,
diff --git a/chrome/browser/history/top_sites_impl_unittest.cc b/chrome/browser/history/top_sites_impl_unittest.cc
index 734132f..a32a01f 100644
--- a/chrome/browser/history/top_sites_impl_unittest.cc
+++ b/chrome/browser/history/top_sites_impl_unittest.cc
@@ -148,7 +148,7 @@ class TopSitesImplTest : public HistoryUnitTestBase {
// Gets the thumbnail for |url| from TopSites.
SkBitmap GetThumbnail(const GURL& url) {
scoped_refptr<base::RefCountedMemory> data;
- return top_sites()->GetPageThumbnail(url, &data) ?
+ return top_sites()->GetPageThumbnail(url, false, &data) ?
ExtractThumbnail(*data.get()) : SkBitmap();
}
@@ -478,13 +478,13 @@ TEST_F(TopSitesImplTest, ThumbnailRemoved) {
// Make sure the thumbnail was actually set.
scoped_refptr<base::RefCountedMemory> result;
- EXPECT_TRUE(top_sites()->GetPageThumbnail(url, &result));
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(url, false, &result));
EXPECT_TRUE(ThumbnailEqualsBytes(thumbnail, result.get()));
// Reset the thumbnails and make sure we don't get it back.
SetTopSites(MostVisitedURLList());
RefreshTopSitesAndRecreate();
- EXPECT_FALSE(top_sites()->GetPageThumbnail(url, &result));
+ EXPECT_FALSE(top_sites()->GetPageThumbnail(url, false, &result));
}
// Tests GetPageThumbnail.
@@ -509,19 +509,21 @@ TEST_F(TopSitesImplTest, GetPageThumbnail) {
scoped_refptr<base::RefCountedMemory> result;
EXPECT_TRUE(top_sites()->SetPageThumbnail(url1.url, thumbnail, score));
- EXPECT_TRUE(top_sites()->GetPageThumbnail(url1.url, &result));
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(url1.url, false, &result));
EXPECT_TRUE(top_sites()->SetPageThumbnail(GURL("http://gmail.com"),
thumbnail, score));
EXPECT_TRUE(top_sites()->GetPageThumbnail(GURL("http://gmail.com"),
+ false,
&result));
// Get a thumbnail via a redirect.
EXPECT_TRUE(top_sites()->GetPageThumbnail(GURL("http://mail.google.com"),
+ false,
&result));
EXPECT_TRUE(top_sites()->SetPageThumbnail(GURL("http://mail.google.com"),
thumbnail, score));
- EXPECT_TRUE(top_sites()->GetPageThumbnail(url2.url, &result));
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(url2.url, false, &result));
EXPECT_TRUE(ThumbnailEqualsBytes(thumbnail, result.get()));
}
@@ -582,7 +584,7 @@ TEST_F(TopSitesImplTest, SaveToDB) {
ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1));
scoped_refptr<base::RefCountedMemory> read_data;
- EXPECT_TRUE(top_sites()->GetPageThumbnail(asdf_url, &read_data));
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(asdf_url, false, &read_data));
EXPECT_TRUE(ThumbnailEqualsBytes(tmp_bitmap, read_data.get()));
}
@@ -647,7 +649,7 @@ TEST_F(TopSitesImplTest, RealDatabase) {
ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1));
scoped_refptr<base::RefCountedMemory> read_data;
- EXPECT_TRUE(top_sites()->GetPageThumbnail(asdf_url, &read_data));
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(asdf_url, false, &read_data));
EXPECT_TRUE(ThumbnailEqualsBytes(asdf_thumbnail, read_data.get()));
}
@@ -679,7 +681,7 @@ TEST_F(TopSitesImplTest, RealDatabase) {
EXPECT_EQ(google1_url, querier.urls()[0].url);
EXPECT_EQ(google_title, querier.urls()[0].title);
ASSERT_EQ(3u, querier.urls()[0].redirects.size());
- EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, &read_data));
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, false, &read_data));
EXPECT_TRUE(ThumbnailEqualsBytes(google_thumbnail, read_data.get()));
EXPECT_EQ(asdf_url, querier.urls()[1].url);
@@ -701,7 +703,7 @@ TEST_F(TopSitesImplTest, RealDatabase) {
RefreshTopSitesAndRecreate();
{
scoped_refptr<base::RefCountedMemory> read_data;
- EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, &read_data));
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, false, &read_data));
EXPECT_TRUE(ThumbnailEqualsBytes(weewar_bitmap, read_data.get()));
}
@@ -721,7 +723,7 @@ TEST_F(TopSitesImplTest, RealDatabase) {
RefreshTopSitesAndRecreate();
{
scoped_refptr<base::RefCountedMemory> read_data;
- EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, &read_data));
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, false, &read_data));
EXPECT_FALSE(ThumbnailEqualsBytes(weewar_bitmap, read_data.get()));
EXPECT_TRUE(ThumbnailEqualsBytes(green_bitmap, read_data.get()));
}
@@ -964,7 +966,7 @@ TEST_F(TopSitesImplTest, AddTemporaryThumbnail) {
// We shouldn't get the thumnail back though (the url isn't in to sites yet).
scoped_refptr<base::RefCountedMemory> out;
- EXPECT_FALSE(top_sites()->GetPageThumbnail(unknown_url, &out));
+ EXPECT_FALSE(top_sites()->GetPageThumbnail(unknown_url, false, &out));
// But we should be able to get the temporary page thumbnail score.
ThumbnailScore out_score;
EXPECT_TRUE(top_sites()->GetTemporaryPageThumbnailScore(unknown_url,
@@ -983,7 +985,7 @@ TEST_F(TopSitesImplTest, AddTemporaryThumbnail) {
// Update URLs. This should result in using thumbnail.
SetTopSites(list);
- ASSERT_TRUE(top_sites()->GetPageThumbnail(unknown_url, &out));
+ ASSERT_TRUE(top_sites()->GetPageThumbnail(unknown_url, false, &out));
EXPECT_TRUE(ThumbnailEqualsBytes(thumbnail, out.get()));
}
diff --git a/chrome/browser/history/url_utils.cc b/chrome/browser/history/url_utils.cc
new file mode 100644
index 0000000..366039c
--- /dev/null
+++ b/chrome/browser/history/url_utils.cc
@@ -0,0 +1,73 @@
+// Copyright 2013 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/url_utils.h"
+
+#include <algorithm>
+
+namespace history {
+
+namespace {
+
+// Comparator to enforce '\0' < '?' < '#' < '/' < other characters.
+int GetURLCharPriority(char ch) {
+ switch (ch) {
+ case '\0': return 0;
+ case '?': return 1;
+ case '#': return 2;
+ case '/': return 3;
+ }
+ return 4;
+}
+
+} // namespace
+
+// Instead of splitting URLs and extract path components, we can implement
+// CanonicalURLStringCompare() using string operations only. The key idea is,
+// treating '/' to be less than any valid path characters would make it behave
+// as a separator, so e.g., "test" < "test-case" would be enforced by
+// "test/..." < "test-case/...". We also force "?" < "/", so "test?query" <
+// "test/stuff". Since the routine is merely lexicographical string comparison
+// with remapping of chracter ordering, so it is a valid strict-weak ordering.
+bool CanonicalURLStringCompare(const std::string& s1, const std::string& s2) {
+ const std::string::value_type* ch1 = s1.c_str();
+ const std::string::value_type* ch2 = s2.c_str();
+ while (*ch1 && *ch2 && *ch1 == *ch2) {
+ ++ch1;
+ ++ch2;
+ }
+ int pri_diff = GetURLCharPriority(*ch1) - GetURLCharPriority(*ch2);
+ // We want false to be returned if |pri_diff| > 0.
+ return (pri_diff != 0) ? pri_diff < 0 : *ch1 < *ch2;
+}
+
+bool UrlIsPrefix(const GURL& url1, const GURL& url2) {
+ if (url1.scheme() != url2.scheme() || url1.host() != url2.host() ||
+ url1.port() != url2.port()) {
+ return false;
+ }
+ // Only need to compare path now. Note that queries are ignored.
+ std::string p1(url1.path());
+ std::string p2(url2.path());
+ if (p1.length() > p2.length())
+ return false;
+ std::pair<std::string::iterator, std::string::iterator> first_diff =
+ std::mismatch(p1.begin(), p1.end(), p2.begin());
+ // Necessary condition: |p1| is a string prefix of |p2|.
+ if (first_diff.first != p1.end())
+ return false; // E.g.: (|p1| = "/test", |p2| = "/exam") => false.
+
+ // |p1| is string prefix.
+ if (first_diff.second == p2.end()) // Is exact match?
+ return true; // E.g.: ("/test", "/test") => true.
+ // |p1| is strict string prefix, check full match of last path component.
+ if (!p1.empty() && *p1.rbegin() == '/') // Ends in '/'?
+ return true; // E.g.: ("/test/", "/test/stuff") => true.
+
+ // Finally, |p1| does not end in "/": check first extra character in |p2|.
+ // E.g.: ("/test", "/test/stuff") => true; ("/test", "/testing") => false.
+ return *(first_diff.second) == '/';
+}
+
+} // namespace history
diff --git a/chrome/browser/history/url_utils.h b/chrome/browser/history/url_utils.h
new file mode 100644
index 0000000..e682873
--- /dev/null
+++ b/chrome/browser/history/url_utils.h
@@ -0,0 +1,42 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_HISTORY_URL_UTILS_H_
+#define CHROME_BROWSER_HISTORY_URL_UTILS_H_
+
+#include <string>
+
+#include "chrome/browser/history/history_types.h"
+
+namespace history {
+
+// CanonicalURLStringCompare performs lexicographical comparison of two strings
+// that represent valid URLs, so that if the pre-path (scheme, host, and port)
+// parts are equal, then the path parts are compared by treating path components
+// (delimited by "/") as separate tokens that form units of comparison.
+// For example, let us compare |s1| and |s2|, with
+// |s1| = "http://www.google.com:80/base/test/ab/cd?query/stuff"
+// |s2| = "http://www.google.com:80/base/test-case/yz#ref/stuff"
+// The pre-path parts "http://www.google.com:80/" match. We treat the paths as
+// |s1| => ["base", "test", "ab", "cd"]
+// |s2| => ["base", "test-case", "yz"]
+// Components 1 "base" are identical. Components 2 yield "test" < "test-case",
+// so we consider |s1| < |s2|, and return true. Note that naive string
+// comparison would yield the opposite (|s1| > |s2|), since '/' > '-' in ASCII.
+// Note that path can be terminated by "?query" or "#ref". The post-path parts
+// are compared in an arbitrary (but consistent) way.
+bool CanonicalURLStringCompare(const std::string& s1, const std::string& s2);
+
+// Returns whether or not |url1| is a "URL prefix" of |url2|. Criteria:
+// - Scheme, host, port: exact match required.
+// - Path: treated as a list of path components (e.g., ["a", "bb"] for "/a/bb"),
+// and |url1|'s list must be a prefix of |url2|'s list.
+// - Query and ref: ignored.
+// Note that "http://www.google.com/test" is NOT a prefix of
+// "http://www.google.com/testing", although "test" is a prefix of "testing".
+bool UrlIsPrefix(const GURL& url1, const GURL& url2);
+
+} // namespace history
+
+#endif // CHROME_BROWSER_HISTORY_URL_UTILS_H_
diff --git a/chrome/browser/history/url_utils_unittest.cc b/chrome/browser/history/url_utils_unittest.cc
new file mode 100644
index 0000000..212ebcd
--- /dev/null
+++ b/chrome/browser/history/url_utils_unittest.cc
@@ -0,0 +1,92 @@
+// Copyright 2013 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/url_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace history {
+
+namespace {
+
+TEST(UrlUtilsTest, CanonicalURLStringCompare) {
+ // Comprehensive test by comparing each pair in sorted list. O(n^2).
+ const char* sorted_list[] = {
+ "http://www.gogle.com/redirects_to_google",
+ "http://www.google.com",
+ "http://www.google.com/",
+ "http://www.google.com/?q",
+ "http://www.google.com/A",
+ "http://www.google.com/index.html",
+ "http://www.google.com/test",
+ "http://www.google.com/test?query",
+ "http://www.google.com/test?r=3",
+ "http://www.google.com/test#hash",
+ "http://www.google.com/test/?query",
+ "http://www.google.com/test/#hash",
+ "http://www.google.com/test/zzzzz",
+ "http://www.google.com/test$dollar",
+ "http://www.google.com/test%E9%9B%80",
+ "http://www.google.com/test-case",
+ "http://www.google.com:80/",
+ "https://www.google.com",
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(sorted_list); ++i) {
+ EXPECT_FALSE(CanonicalURLStringCompare(sorted_list[i], sorted_list[i]))
+ << " for \"" << sorted_list[i] << "\" < \"" << sorted_list[i] << "\"";
+ // Every disjoint pair-wise comparison.
+ for (size_t j = i + 1; j < ARRAYSIZE_UNSAFE(sorted_list); ++j) {
+ EXPECT_TRUE(CanonicalURLStringCompare(sorted_list[i], sorted_list[j]))
+ << " for \"" << sorted_list[i] << "\" < \"" << sorted_list[j] << "\"";
+ EXPECT_FALSE(CanonicalURLStringCompare(sorted_list[j], sorted_list[i]))
+ << " for \"" << sorted_list[j] << "\" < \"" << sorted_list[i] << "\"";
+ }
+ }
+}
+
+TEST(UrlUtilsTest, UrlIsPrefix) {
+ struct {
+ const char* s1;
+ const char* s2;
+ } true_cases[] = {
+ {"http://www.google.com", "http://www.google.com"},
+ {"http://www.google.com/a/b", "http://www.google.com/a/b"},
+ {"http://www.google.com?test=3", "http://www.google.com/"},
+ {"http://www.google.com/#hash", "http://www.google.com/?q"},
+ {"http://www.google.com/", "http://www.google.com/test/with/dir/"},
+ {"http://www.google.com:360", "http://www.google.com:360/?q=1234"},
+ {"http://www.google.com:80", "http://www.google.com/gurl/is/smart"},
+ {"http://www.google.com:80/", "http://www.google.com/"},
+ {"http://www.google.com/test", "http://www.google.com/test/with/dir/"},
+ {"http://www.google.com/test/", "http://www.google.com/test/with/dir"},
+ {"http://www.google.com/test?", "http://www.google.com/test/with/dir/"},
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(true_cases); ++i) {
+ EXPECT_TRUE(UrlIsPrefix(GURL(true_cases[i].s1), GURL(true_cases[i].s2)))
+ << " for true_cases[" << i << "]";
+ }
+ struct {
+ const char* s1;
+ const char* s2;
+ } false_cases[] = {
+ {"http://www.google.com/test", "http://www.google.com"},
+ {"http://www.google.com/a/b/", "http://www.google.com/a/b"}, // Arguable.
+ {"http://www.google.co", "http://www.google.com"},
+ {"http://google.com", "http://www.google.com"},
+ {"http://www.google.com", "https://www.google.com"},
+ {"http://www.google.com/path", "http://www.google.com:137/path"},
+ {"http://www.google.com/same/dir", "http://www.youtube.com/same/dir"},
+ {"http://www.google.com/te", "http://www.google.com/test"},
+ {"http://www.google.com/test", "http://www.google.com/test-bed"},
+ {"http://www.google.com/test-", "http://www.google.com/test?"},
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(false_cases); ++i) {
+ EXPECT_FALSE(UrlIsPrefix(GURL(false_cases[i].s1), GURL(false_cases[i].s2)))
+ << " for false_cases[" << i << "]";
+ }
+}
+
+} // namespace
+
+} // namespace history
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 47d36d3..62e8580 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -93,7 +93,8 @@ InstantService::InstantService(Profile* profile)
content::URLDataSource::Add(profile, new ThemeSource(profile));
#endif // defined(ENABLE_THEMES)
- content::URLDataSource::Add(profile, new ThumbnailSource(profile));
+ content::URLDataSource::Add(profile, new ThumbnailSource(profile, false));
+ content::URLDataSource::Add(profile, new ThumbnailSource(profile, true));
content::URLDataSource::Add(profile, new FaviconSource(
profile, FaviconSource::FAVICON));
content::URLDataSource::Add(profile, new LocalNtpSource(profile));
diff --git a/chrome/browser/thumbnails/thumbnail_service.h b/chrome/browser/thumbnails/thumbnail_service.h
index 11da713..16a02738 100644
--- a/chrome/browser/thumbnails/thumbnail_service.h
+++ b/chrome/browser/thumbnails/thumbnail_service.h
@@ -39,10 +39,13 @@ class ThumbnailService : public RefcountedBrowserContextKeyedService {
// Gets a thumbnail for a given page. Returns true iff we have the thumbnail.
// This may be invoked on any thread.
+ // If an exact thumbnail URL match fails, |prefix_match| specifies whether or
+ // not to try harder by matching the query thumbnail URL as URL prefix.
// As this method may be invoked on any thread the ref count needs to be
// incremented before this method returns, so this takes a scoped_refptr*.
virtual bool GetPageThumbnail(
const GURL& url,
+ bool prefix_match,
scoped_refptr<base::RefCountedMemory>* bytes) = 0;
// Returns true if the page thumbnail should be updated.
@@ -52,6 +55,6 @@ class ThumbnailService : public RefcountedBrowserContextKeyedService {
virtual ~ThumbnailService() {}
};
-}
+} // namespace thumbnails
#endif // CHROME_BROWSER_THUMBNAILS_THUMBNAIL_SERVICE_H_
diff --git a/chrome/browser/thumbnails/thumbnail_service_impl.cc b/chrome/browser/thumbnails/thumbnail_service_impl.cc
index ab3ef62..f4ae3f5 100644
--- a/chrome/browser/thumbnails/thumbnail_service_impl.cc
+++ b/chrome/browser/thumbnails/thumbnail_service_impl.cc
@@ -28,13 +28,13 @@ bool IsThumbnailRetargetingEnabled() {
switches::kEnableThumbnailRetargeting);
}
-}
+} // namespace
namespace thumbnails {
ThumbnailServiceImpl::ThumbnailServiceImpl(Profile* profile)
: top_sites_(profile->GetTopSites()),
- use_thumbnail_retargeting_(IsThumbnailRetargetingEnabled()){
+ use_thumbnail_retargeting_(IsThumbnailRetargetingEnabled()) {
}
ThumbnailServiceImpl::~ThumbnailServiceImpl() {
@@ -51,12 +51,13 @@ bool ThumbnailServiceImpl::SetPageThumbnail(const ThumbnailingContext& context,
bool ThumbnailServiceImpl::GetPageThumbnail(
const GURL& url,
+ bool prefix_match,
scoped_refptr<base::RefCountedMemory>* bytes) {
scoped_refptr<history::TopSites> local_ptr(top_sites_);
if (local_ptr.get() == NULL)
return false;
- return local_ptr->GetPageThumbnail(url, bytes);
+ return local_ptr->GetPageThumbnail(url, prefix_match, bytes);
}
ThumbnailingAlgorithm* ThumbnailServiceImpl::GetThumbnailingAlgorithm()
diff --git a/chrome/browser/thumbnails/thumbnail_service_impl.h b/chrome/browser/thumbnails/thumbnail_service_impl.h
index 4a05a8a..9316501 100644
--- a/chrome/browser/thumbnails/thumbnail_service_impl.h
+++ b/chrome/browser/thumbnails/thumbnail_service_impl.h
@@ -28,6 +28,7 @@ class ThumbnailServiceImpl : public ThumbnailService {
virtual ThumbnailingAlgorithm* GetThumbnailingAlgorithm() const OVERRIDE;
virtual bool GetPageThumbnail(
const GURL& url,
+ bool prefix_match,
scoped_refptr<base::RefCountedMemory>* bytes) OVERRIDE;
virtual bool ShouldAcquirePageThumbnail(const GURL& url) OVERRIDE;
@@ -43,6 +44,6 @@ class ThumbnailServiceImpl : public ThumbnailService {
DISALLOW_COPY_AND_ASSIGN(ThumbnailServiceImpl);
};
-}
+} // namespace thumbnails
#endif // CHROME_BROWSER_THUMBNAILS_THUMBNAIL_SERVICE_IMPL_H_
diff --git a/chrome/browser/ui/webui/ntp/most_visited_handler.cc b/chrome/browser/ui/webui/ntp/most_visited_handler.cc
index a8e17a5..63f1e22 100644
--- a/chrome/browser/ui/webui/ntp/most_visited_handler.cc
+++ b/chrome/browser/ui/webui/ntp/most_visited_handler.cc
@@ -77,8 +77,11 @@ MostVisitedHandler::~MostVisitedHandler() {
void MostVisitedHandler::RegisterMessages() {
Profile* profile = Profile::FromWebUI(web_ui());
// Set up our sources for thumbnail and favicon data.
- ThumbnailSource* thumbnail_src = new ThumbnailSource(profile);
- content::URLDataSource::Add(profile, thumbnail_src);
+ ThumbnailSource* thumbnail_src_exact = new ThumbnailSource(profile, false);
+ content::URLDataSource::Add(profile, thumbnail_src_exact);
+
+ ThumbnailSource* thumbnail_src_prefix = new ThumbnailSource(profile, true);
+ content::URLDataSource::Add(profile, thumbnail_src_prefix);
#if defined(OS_ANDROID)
// Register chrome://touch-icon as a data source for touch icons or favicons.
diff --git a/chrome/browser/ui/webui/ntp/suggestions_page_handler.cc b/chrome/browser/ui/webui/ntp/suggestions_page_handler.cc
index f5a4d46..8173eec 100644
--- a/chrome/browser/ui/webui/ntp/suggestions_page_handler.cc
+++ b/chrome/browser/ui/webui/ntp/suggestions_page_handler.cc
@@ -63,7 +63,7 @@ SuggestionsHandler::~SuggestionsHandler() {
void SuggestionsHandler::RegisterMessages() {
Profile* profile = Profile::FromWebUI(web_ui());
// Set up our sources for thumbnail and favicon data.
- content::URLDataSource::Add(profile, new ThumbnailSource(profile));
+ content::URLDataSource::Add(profile, new ThumbnailSource(profile, false));
content::URLDataSource::Add(
profile, new FaviconSource(profile, FaviconSource::FAVICON));
diff --git a/chrome/browser/ui/webui/ntp/thumbnail_source.cc b/chrome/browser/ui/webui/ntp/thumbnail_source.cc
index 324c694..a14239f 100644
--- a/chrome/browser/ui/webui/ntp/thumbnail_source.cc
+++ b/chrome/browser/ui/webui/ntp/thumbnail_source.cc
@@ -20,16 +20,18 @@
using content::BrowserThread;
// Set ThumbnailService now as Profile isn't thread safe.
-ThumbnailSource::ThumbnailSource(Profile* profile)
+ThumbnailSource::ThumbnailSource(Profile* profile, bool prefix_match)
: thumbnail_service_(ThumbnailServiceFactory::GetForProfile(profile)),
- profile_(profile) {
+ profile_(profile),
+ prefix_match_(prefix_match) {
}
ThumbnailSource::~ThumbnailSource() {
}
std::string ThumbnailSource::GetSource() const {
- return chrome::kChromeUIThumbnailHost;
+ return prefix_match_ ?
+ chrome::kChromeUIThumbnailHost2 : chrome::kChromeUIThumbnailHost;
}
void ThumbnailSource::StartDataRequest(
@@ -38,7 +40,7 @@ void ThumbnailSource::StartDataRequest(
int render_view_id,
const content::URLDataSource::GotDataCallback& callback) {
scoped_refptr<base::RefCountedMemory> data;
- if (thumbnail_service_->GetPageThumbnail(GURL(path), &data)) {
+ if (thumbnail_service_->GetPageThumbnail(GURL(path), prefix_match_, &data)) {
// We have the thumbnail.
callback.Run(data.get());
} else {
diff --git a/chrome/browser/ui/webui/ntp/thumbnail_source.h b/chrome/browser/ui/webui/ntp/thumbnail_source.h
index 8008407..f4ff8f0 100644
--- a/chrome/browser/ui/webui/ntp/thumbnail_source.h
+++ b/chrome/browser/ui/webui/ntp/thumbnail_source.h
@@ -25,7 +25,7 @@ class ThumbnailService;
// thumbnails and the history/top-sites backend that serves these.
class ThumbnailSource : public content::URLDataSource {
public:
- explicit ThumbnailSource(Profile* profile);
+ ThumbnailSource(Profile* profile, bool prefix_match);
// content::URLDataSource implementation.
virtual std::string GetSource() const OVERRIDE;
@@ -53,6 +53,11 @@ class ThumbnailSource : public content::URLDataSource {
// Only used when servicing requests on the UI thread.
Profile* const profile_;
+ // If an exact thumbnail URL match fails, specifies whether or not to try
+ // harder by matching the query thumbnail URL as URL prefix. This affects
+ // GetSource().
+ const bool prefix_match_;
+
DISALLOW_COPY_AND_ASSIGN(ThumbnailSource);
};
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 5eef42c..ae5a18f 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -823,6 +823,8 @@
'browser/history/url_database.h',
'browser/history/url_index_private_data.cc',
'browser/history/url_index_private_data.h',
+ 'browser/history/url_utils.cc',
+ 'browser/history/url_utils.h',
'browser/history/visit_database.cc',
'browser/history/visit_database.h',
'browser/history/visit_filter.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 2cdd57c..2a6cbf9 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -934,10 +934,12 @@
'browser/history/shortcuts_database_unittest.cc',
'browser/history/snippet_unittest.cc',
'browser/history/thumbnail_database_unittest.cc',
+ 'browser/history/top_sites_cache_unittest.cc',
'browser/history/top_sites_database_unittest.cc',
'browser/history/top_sites_impl_unittest.cc',
'browser/history/typed_url_syncable_service_unittest.cc',
'browser/history/url_database_unittest.cc',
+ 'browser/history/url_utils_unittest.cc',
'browser/history/visit_database_unittest.cc',
'browser/history/visit_filter_unittest.cc',
'browser/history/visit_tracker_unittest.cc',
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 9c03fcf..eee49505 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -221,6 +221,7 @@ const char kChromeUITaskManagerHost[] = "tasks";
const char kChromeUITermsHost[] = "terms";
const char kChromeUIThemeHost[] = "theme";
const char kChromeUIThumbnailHost[] = "thumb";
+const char kChromeUIThumbnailHost2[] = "thumb2";
const char kChromeUITouchIconHost[] = "touch-icon";
const char kChromeUITranslateInternalsHost[] = "translate-internals";
const char kChromeUIUberFrameHost[] = "uber-frame";
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 2acdb47..16a1ee3 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -213,6 +213,7 @@ extern const char kChromeUITaskManagerHost[];
extern const char kChromeUITermsHost[];
extern const char kChromeUIThemeHost[];
extern const char kChromeUIThumbnailHost[];
+extern const char kChromeUIThumbnailHost2[];
extern const char kChromeUITouchIconHost[];
extern const char kChromeUITranslateInternalsHost[];
extern const char kChromeUIUberFrameHost[];