Only call UrlToFullHashes once from LocalSafeBrowsingDatabaseManager::CheckBrowseUrl

Add ContainsBrowseHashes and ContainsUnwantedSoftwareHashes to
SafeBrowsingDatabase, to parallel ContainsBrowseUrl and
ContainsUnwantedSoftwareUrl. These functions take the full hash set instead of
the url, so that the caller can look up both "browse" and "unwanted software"
data for the same url without incurring the overhead of calling UrlToFullHashes
twice.

BUG=576750

Review URL: https://codereview.chromium.org/1579183003

Cr-Commit-Position: refs/heads/master@{#369755}
diff --git a/chrome/browser/safe_browsing/local_database_manager.cc b/chrome/browser/safe_browsing/local_database_manager.cc
index 0abdac70..0721b80 100644
--- a/chrome/browser/safe_browsing/local_database_manager.cc
+++ b/chrome/browser/safe_browsing/local_database_manager.cc
@@ -480,14 +480,17 @@
   // TODO(gab): Refactor SafeBrowsingDatabase to avoid depending on this here.
   std::vector<SBFullHashResult> cache_hits;
 
+  std::vector<SBFullHash> full_hashes;
+  UrlToFullHashes(url, false, &full_hashes);
+
   std::vector<SBPrefix> browse_prefix_hits;
-  bool browse_prefix_match = database_->ContainsBrowseUrl(
-      url, &browse_prefix_hits, &cache_hits);
+  bool browse_prefix_match = database_->ContainsBrowseHashes(
+      full_hashes, &browse_prefix_hits, &cache_hits);
 
   std::vector<SBPrefix> unwanted_prefix_hits;
   std::vector<SBFullHashResult> unused_cache_hits;
-  bool unwanted_prefix_match = database_->ContainsUnwantedSoftwareUrl(
-      url, &unwanted_prefix_hits, &unused_cache_hits);
+  bool unwanted_prefix_match = database_->ContainsUnwantedSoftwareHashes(
+      full_hashes, &unwanted_prefix_hits, &unused_cache_hits);
 
   // Merge the two pre-sorted prefix hits lists.
   // TODO(gab): Refactor SafeBrowsingDatabase for it to return this merged list
diff --git a/chrome/browser/safe_browsing/safe_browsing_database.cc b/chrome/browser/safe_browsing/safe_browsing_database.cc
index b4b7cd2..7b72de1 100644
--- a/chrome/browser/safe_browsing/safe_browsing_database.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_database.cc
@@ -793,6 +793,14 @@
                               cache_hits);
 }
 
+bool SafeBrowsingDatabaseNew::ContainsBrowseHashes(
+    const std::vector<SBFullHash>& full_hashes,
+    std::vector<SBPrefix>* prefix_hits,
+    std::vector<SBFullHashResult>* cache_hits) {
+  return PrefixSetContainsUrlHashes(full_hashes, PrefixSetId::BROWSE,
+                                    prefix_hits, cache_hits);
+}
+
 bool SafeBrowsingDatabaseNew::ContainsUnwantedSoftwareUrl(
     const GURL& url,
     std::vector<SBPrefix>* prefix_hits,
@@ -801,37 +809,37 @@
                               cache_hits);
 }
 
+bool SafeBrowsingDatabaseNew::ContainsUnwantedSoftwareHashes(
+    const std::vector<SBFullHash>& full_hashes,
+    std::vector<SBPrefix>* prefix_hits,
+    std::vector<SBFullHashResult>* cache_hits) {
+  return PrefixSetContainsUrlHashes(full_hashes, PrefixSetId::UNWANTED_SOFTWARE,
+                                    prefix_hits, cache_hits);
+}
+
 bool SafeBrowsingDatabaseNew::PrefixSetContainsUrl(
     const GURL& url,
     PrefixSetId prefix_set_id,
     std::vector<SBPrefix>* prefix_hits,
     std::vector<SBFullHashResult>* cache_hits) {
-  // Clear the results first.
-  prefix_hits->clear();
-  cache_hits->clear();
-
   std::vector<SBFullHash> full_hashes;
   UrlToFullHashes(url, false, &full_hashes);
-  if (full_hashes.empty())
-    return false;
-
   return PrefixSetContainsUrlHashes(full_hashes, prefix_set_id, prefix_hits,
                                     cache_hits);
 }
 
-bool SafeBrowsingDatabaseNew::ContainsBrowseUrlHashesForTesting(
-    const std::vector<SBFullHash>& full_hashes,
-    std::vector<SBPrefix>* prefix_hits,
-    std::vector<SBFullHashResult>* cache_hits) {
-  return PrefixSetContainsUrlHashes(full_hashes, PrefixSetId::BROWSE,
-                                    prefix_hits, cache_hits);
-}
-
 bool SafeBrowsingDatabaseNew::PrefixSetContainsUrlHashes(
     const std::vector<SBFullHash>& full_hashes,
     PrefixSetId prefix_set_id,
     std::vector<SBPrefix>* prefix_hits,
     std::vector<SBFullHashResult>* cache_hits) {
+  // Clear the results first.
+  prefix_hits->clear();
+  cache_hits->clear();
+
+  if (full_hashes.empty())
+    return false;
+
   // Used to determine cache expiration.
   const base::Time now = base::Time::Now();
 
diff --git a/chrome/browser/safe_browsing/safe_browsing_database.h b/chrome/browser/safe_browsing/safe_browsing_database.h
index c7f6599..cae9129d 100644
--- a/chrome/browser/safe_browsing/safe_browsing_database.h
+++ b/chrome/browser/safe_browsing/safe_browsing_database.h
@@ -99,6 +99,16 @@
       std::vector<SBPrefix>* prefix_hits,
       std::vector<SBFullHashResult>* cache_hits) = 0;
 
+  // Returns false if none of the hashes in |full_hashes| are in the browse
+  // database or all were already cached as a miss.  If it returns true,
+  // |prefix_hits| contains sorted unique matching hash prefixes which had no
+  // cached results and |cache_hits| contains any matching cached gethash
+  // results.  This function is safe to call from any thread.
+  virtual bool ContainsBrowseHashes(
+      const std::vector<SBFullHash>& full_hashes,
+      std::vector<SBPrefix>* prefix_hits,
+      std::vector<SBFullHashResult>* cache_hits) = 0;
+
   // Returns true iff the given url is on the unwanted software blacklist.
   // Returns false if |url| is not in the browse database or already was cached
   // as a miss.  If it returns true, |prefix_hits| contains sorted unique
@@ -110,6 +120,18 @@
       std::vector<SBPrefix>* prefix_hits,
       std::vector<SBFullHashResult>* cache_hits) = 0;
 
+  // Returns true iff any of the given hashes are on the unwanted software
+  // blacklist.
+  // Returns false if none of the hashes in |full_hashes| are in the browse
+  // database or all were already cached as a miss.  If it returns true,
+  // |prefix_hits| contains sorted unique matching hash prefixes which had no
+  // cached results and |cache_hits| contains any matching cached gethash
+  // results.  This function is safe to call from any thread.
+  virtual bool ContainsUnwantedSoftwareHashes(
+      const std::vector<SBFullHash>& full_hashes,
+      std::vector<SBPrefix>* prefix_hits,
+      std::vector<SBFullHashResult>* cache_hits) = 0;
+
   // Returns false if none of |prefixes| are in Download database. If it returns
   // true, |prefix_hits| should contain the prefixes for the URLs that were in
   // the database.  This function can ONLY be accessed from creation thread.
@@ -321,10 +343,17 @@
   bool ContainsBrowseUrl(const GURL& url,
                          std::vector<SBPrefix>* prefix_hits,
                          std::vector<SBFullHashResult>* cache_hits) override;
+  bool ContainsBrowseHashes(const std::vector<SBFullHash>& full_hashes,
+                         std::vector<SBPrefix>* prefix_hits,
+                         std::vector<SBFullHashResult>* cache_hits) override;
   bool ContainsUnwantedSoftwareUrl(
       const GURL& url,
       std::vector<SBPrefix>* prefix_hits,
       std::vector<SBFullHashResult>* cache_hits) override;
+  bool ContainsUnwantedSoftwareHashes(
+      const std::vector<SBFullHash>& full_hashes,
+      std::vector<SBPrefix>* prefix_hits,
+      std::vector<SBFullHashResult>* cache_hits) override;
   bool ContainsDownloadUrlPrefixes(const std::vector<SBPrefix>& prefixes,
                                    std::vector<SBPrefix>* prefix_hits) override;
   bool ContainsCsdWhitelistedUrl(const GURL& url) override;
@@ -531,13 +560,6 @@
                             std::vector<SBPrefix>* prefix_hits,
                             std::vector<SBFullHashResult>* cache_hits);
 
-  // Exposed for testing of PrefixSetContainsUrlHashes() on the
-  // PrefixSet backing kMalwareList.
-  bool ContainsBrowseUrlHashesForTesting(
-      const std::vector<SBFullHash>& full_hashes,
-      std::vector<SBPrefix>* prefix_hits,
-      std::vector<SBFullHashResult>* cache_hits);
-
   bool PrefixSetContainsUrlHashes(const std::vector<SBFullHash>& full_hashes,
                                   PrefixSetId prefix_set_id,
                                   std::vector<SBPrefix>* prefix_hits,
diff --git a/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc
index 19484443..f3b094b0 100644
--- a/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc
@@ -477,20 +477,68 @@
 
 // Checks database reading and writing for browse and unwanted PrefixSets.
 TEST_F(SafeBrowsingDatabaseTest, BrowseAndUnwantedDatabasesAndPrefixSets) {
+
   struct TestCase {
     using TestListContainsBadUrl = bool (SafeBrowsingDatabase::*)(
         const GURL& url,
         std::vector<SBPrefix>* prefix_hits,
         std::vector<SBFullHashResult>* cache_hits);
+    using TestListContainsBadHashes = bool (SafeBrowsingDatabase::*)(
+        const std::vector<SBFullHash>& full_hashes,
+        std::vector<SBPrefix>* prefix_hits,
+        std::vector<SBFullHashResult>* cache_hits);
 
     const char* test_list_name;
     size_t expected_list_index;
     TestListContainsBadUrl test_list_contains_bad_url;
-  } const kTestCases[]{
-      {kMalwareList, 0U, &SafeBrowsingDatabase::ContainsBrowseUrl},
-      {kPhishingList, 1U, &SafeBrowsingDatabase::ContainsBrowseUrl},
-      {kUnwantedUrlList, 8U,
-       &SafeBrowsingDatabase::ContainsUnwantedSoftwareUrl},
+    TestListContainsBadHashes test_list_contains_bad_hashes;
+
+    void TestContainsFunctions(SafeBrowsingDatabaseNew& database,
+        bool expected_outcome,
+        const GURL& url,
+        std::vector<SBPrefix>* prefix_hits,
+        std::vector<SBFullHashResult>* cache_hits) const {
+      EXPECT_EQ(expected_outcome,
+          (database.*test_list_contains_bad_url)(url, prefix_hits, cache_hits))
+          << test_list_name << url;
+
+      // Contains*Hashes should always return the same result as Contains*Url.
+      std::vector<SBFullHash> full_hashes;
+      UrlToFullHashes(url, false, &full_hashes);
+      ASSERT_FALSE(full_hashes.empty()) << test_list_name << url;
+
+      std::vector<SBPrefix> hash_prefix_hits;
+      std::vector<SBFullHashResult> hash_cache_hits;
+      EXPECT_EQ(expected_outcome, (database.*test_list_contains_bad_hashes)(
+          full_hashes, &hash_prefix_hits, &hash_cache_hits)) << test_list_name
+          << url;
+
+      EXPECT_EQ(prefix_hits->size(), hash_prefix_hits.size()) << test_list_name
+          << url;
+      EXPECT_EQ(cache_hits->size(), hash_cache_hits.size()) << test_list_name
+          << url;
+    }
+  };
+
+  TestCase const kTestCases[] {
+    {
+      kMalwareList,
+      0U,
+      &SafeBrowsingDatabase::ContainsBrowseUrl,
+      &SafeBrowsingDatabase::ContainsBrowseHashes
+    },
+    {
+      kPhishingList,
+      1U,
+      &SafeBrowsingDatabase::ContainsBrowseUrl,
+      &SafeBrowsingDatabase::ContainsBrowseHashes
+    },
+    {
+      kUnwantedUrlList,
+      8U,
+      &SafeBrowsingDatabase::ContainsUnwantedSoftwareUrl,
+      &SafeBrowsingDatabase::ContainsUnwantedSoftwareHashes
+    },
   };
 
   for (const auto& test_case : kTestCases) {
@@ -526,40 +574,41 @@
 
     std::vector<SBPrefix> prefix_hits;
     std::vector<SBFullHashResult> cache_hits;
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/phishing.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://www.evil.com/phishing.html"), &prefix_hits, &cache_hits);
+
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(SBPrefixForString("www.evil.com/phishing.html"), prefix_hits[0]);
     EXPECT_TRUE(cache_hits.empty());
 
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/malware.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://www.evil.com/malware.html"), &prefix_hits, &cache_hits);
 
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/notevil1.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://www.evil.com/notevil1.html"), &prefix_hits, &cache_hits);
 
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/notevil2.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://www.evil.com/notevil2.html"), &prefix_hits, &cache_hits);
 
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.good.com/good1.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://www.good.com/good1.html"), &prefix_hits, &cache_hits);
 
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.good.com/good2.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://www.good.com/good2.html"), &prefix_hits, &cache_hits);
 
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://192.168.0.1/malware.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://192.168.0.1/malware.html"), &prefix_hits, &cache_hits);
 
-    EXPECT_FALSE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, false,
+        GURL("http://www.evil.com/"), &prefix_hits, &cache_hits);
     EXPECT_TRUE(prefix_hits.empty());
     EXPECT_TRUE(cache_hits.empty());
 
-    EXPECT_FALSE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/robots.txt"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, false,
+        GURL("http://www.evil.com/robots.txt"), &prefix_hits, &cache_hits);
 
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/evil.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://www.evil.com/evil.html"), &prefix_hits, &cache_hits);
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(SBPrefixForString("www.evil.com/evil.html"), prefix_hits[0]);
 
@@ -587,25 +636,25 @@
     database_->InsertChunks(test_case.test_list_name, chunks);
     database_->UpdateFinished(true);
 
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/phishing.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://www.evil.com/phishing.html"), &prefix_hits, &cache_hits);
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(SBPrefixForString("www.evil.com/phishing.html"), prefix_hits[0]);
     EXPECT_TRUE(cache_hits.empty());
 
-    EXPECT_FALSE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/notevil1.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, false,
+        GURL("http://www.evil.com/notevil1.html"), &prefix_hits, &cache_hits);
     EXPECT_TRUE(prefix_hits.empty());
     EXPECT_TRUE(cache_hits.empty());
 
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/notevil2.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://www.evil.com/notevil2.html"), &prefix_hits, &cache_hits);
 
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.good.com/good1.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://www.good.com/good1.html"), &prefix_hits, &cache_hits);
 
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.good.com/good2.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://www.good.com/good2.html"), &prefix_hits, &cache_hits);
 
     GetListsInfo(&lists);
     ASSERT_LE(1U, lists.size());
@@ -635,14 +684,14 @@
     AddDelChunk(test_case.test_list_name, 2);
     database_->UpdateFinished(true);
 
-    EXPECT_FALSE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/notevil2.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, false,
+        GURL("http://www.evil.com/notevil2.html"), &prefix_hits, &cache_hits);
 
-    EXPECT_FALSE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.good.com/good1.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, false,
+        GURL("http://www.good.com/good1.html"), &prefix_hits, &cache_hits);
 
-    EXPECT_FALSE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.good.com/good2.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, false,
+        GURL("http://www.good.com/good2.html"), &prefix_hits, &cache_hits);
 
     GetListsInfo(&lists);
     ASSERT_LE(1U, lists.size());
@@ -685,10 +734,10 @@
     database_->InsertChunks(test_case.test_list_name, chunks);
     database_->UpdateFinished(true);
 
-    EXPECT_FALSE((database_.get()->*test_case.test_list_contains_bad_url)(
+    test_case.TestContainsFunctions(*database_, false,
         GURL("http://www.notevilanymore.com/index.html"),
         &prefix_hits,
-        &cache_hits));
+        &cache_hits);
 
     // Now insert the tardy add chunk and we don't expect them to appear
     // in database because of the previous sub chunk.
@@ -700,32 +749,32 @@
     database_->InsertChunks(test_case.test_list_name, chunks);
     database_->UpdateFinished(true);
 
-    EXPECT_FALSE((database_.get()->*test_case.test_list_contains_bad_url)(
+    test_case.TestContainsFunctions(*database_, false,
         GURL("http://www.notevilanymore.com/index.html"),
         &prefix_hits,
-        &cache_hits));
+        &cache_hits);
 
-    EXPECT_FALSE((database_.get()->*test_case.test_list_contains_bad_url)(
+    test_case.TestContainsFunctions(*database_, false,
         GURL("http://www.notevilanymore.com/good.html"),
         &prefix_hits,
-        &cache_hits));
+        &cache_hits);
 
     // Reset and reload the database.  The database will rely on the prefix set.
     ResetAndReloadFullDatabase();
 
     // Check that a prefix still hits.
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/phishing.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://www.evil.com/phishing.html"), &prefix_hits, &cache_hits);
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(SBPrefixForString("www.evil.com/phishing.html"), prefix_hits[0]);
 
     // Also check that it's not just always returning true in this case.
-    EXPECT_FALSE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, false,
+        GURL("http://www.evil.com/"), &prefix_hits, &cache_hits);
 
     // Check that the full hash is still present.
-    EXPECT_TRUE((database_.get()->*test_case.test_list_contains_bad_url)(
-        GURL("http://www.evil.com/evil.html"), &prefix_hits, &cache_hits));
+    test_case.TestContainsFunctions(*database_, true,
+        GURL("http://www.evil.com/evil.html"), &prefix_hits, &cache_hits);
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(SBPrefixForString("www.evil.com/evil.html"), prefix_hits[0]);
   }
@@ -1713,14 +1762,14 @@
     std::vector<SBFullHash> full_hashes(1, kFullHash1_1);
     std::vector<SBPrefix> prefix_hits;
     std::vector<SBFullHashResult> cache_hits;
-    EXPECT_FALSE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_FALSE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
 
     // kFullHash2_1 gets a hit from the prefix in the database.
     full_hashes.push_back(kFullHash2_1);
     prefix_hits.clear();
     cache_hits.clear();
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(kPrefix2, prefix_hits[0]);
@@ -1761,7 +1810,7 @@
     full_hashes.push_back(kFullHash1_1);
     std::vector<SBPrefix> prefix_hits;
     std::vector<SBFullHashResult> cache_hits;
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(kPrefix1, prefix_hits[0]);
@@ -1771,7 +1820,7 @@
     full_hashes.push_back(kFullHash2_1);
     prefix_hits.clear();
     cache_hits.clear();
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     ASSERT_EQ(2U, prefix_hits.size());
     EXPECT_EQ(kPrefix1, prefix_hits[0]);
@@ -1782,7 +1831,7 @@
     full_hashes.push_back(kFullHash3_1);
     prefix_hits.clear();
     cache_hits.clear();
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     ASSERT_EQ(2U, prefix_hits.size());
     EXPECT_EQ(kPrefix1, prefix_hits[0]);
@@ -1812,7 +1861,7 @@
     std::vector<SBFullHash> full_hashes(1, kFullHash1_1);
     std::vector<SBPrefix> prefix_hits;
     std::vector<SBFullHashResult> cache_hits;
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     EXPECT_TRUE(prefix_hits.empty());
     ASSERT_EQ(1U, cache_hits.size());
@@ -1824,7 +1873,7 @@
     full_hashes.push_back(kFullHash2_1);
     prefix_hits.clear();
     cache_hits.clear();
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(kPrefix2, prefix_hits[0]);
@@ -1836,7 +1885,7 @@
     full_hashes.push_back(kFullHash1_3);
     prefix_hits.clear();
     cache_hits.clear();
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(kPrefix2, prefix_hits[0]);
@@ -1852,7 +1901,7 @@
     std::vector<SBFullHash> full_hashes(1, kFullHash1_3);
     std::vector<SBPrefix> prefix_hits;
     std::vector<SBFullHashResult> cache_hits;
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     EXPECT_TRUE(prefix_hits.empty());
     ASSERT_EQ(1U, cache_hits.size());
@@ -1866,14 +1915,14 @@
     std::vector<SBFullHash> full_hashes(1, kFullHash1_2);
     std::vector<SBPrefix> prefix_hits;
     std::vector<SBFullHashResult> cache_hits;
-    EXPECT_FALSE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_FALSE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
 
     // Other prefix hits possible when kFullHash1_2 hits nothing.
     full_hashes.push_back(kFullHash2_1);
     prefix_hits.clear();
     cache_hits.clear();
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(kPrefix2, prefix_hits[0]);
@@ -1905,14 +1954,14 @@
     std::vector<SBFullHash> full_hashes(1, kFullHash1_3);
     std::vector<SBPrefix> prefix_hits;
     std::vector<SBFullHashResult> cache_hits;
-    EXPECT_FALSE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_FALSE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
 
     // Also one which is present, should have a prefix hit.
     full_hashes.push_back(kFullHash1_1);
     prefix_hits.clear();
     cache_hits.clear();
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(kPrefix1, prefix_hits[0]);
@@ -1922,7 +1971,7 @@
     full_hashes.push_back(kFullHash1_2);
     prefix_hits.clear();
     cache_hits.clear();
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(kPrefix1, prefix_hits[0]);
@@ -1947,7 +1996,7 @@
     std::vector<SBFullHash> full_hashes(1, kFullHash1_3);
     std::vector<SBPrefix> prefix_hits;
     std::vector<SBFullHashResult> cache_hits;
-    EXPECT_FALSE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_FALSE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
 
     // kFullHash1_1 is also not in the cached result, which takes
@@ -1955,14 +2004,14 @@
     prefix_hits.clear();
     full_hashes.push_back(kFullHash1_1);
     cache_hits.clear();
-    EXPECT_FALSE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_FALSE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
 
     // kFullHash1_2 is in the cached result.
     full_hashes.push_back(kFullHash1_2);
     prefix_hits.clear();
     cache_hits.clear();
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     EXPECT_TRUE(prefix_hits.empty());
     ASSERT_EQ(1U, cache_hits.size());
@@ -1987,21 +2036,21 @@
     std::vector<SBFullHash> full_hashes(1, kFullHash1_1);
     std::vector<SBPrefix> prefix_hits;
     std::vector<SBFullHashResult> cache_hits;
-    EXPECT_FALSE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_FALSE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
 
     // Nor kFullHash1_3.
     full_hashes.push_back(kFullHash1_3);
     prefix_hits.clear();
     cache_hits.clear();
-    EXPECT_FALSE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_FALSE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
 
     // Still has kFullHash1_2.
     full_hashes.push_back(kFullHash1_2);
     prefix_hits.clear();
     cache_hits.clear();
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(kPrefix1, prefix_hits[0]);
@@ -2028,7 +2077,7 @@
     full_hashes.push_back(kFullHash1_1);
     full_hashes.push_back(kFullHash1_2);
     full_hashes.push_back(kFullHash1_3);
-    EXPECT_FALSE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_FALSE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
   }
 }
@@ -2053,7 +2102,7 @@
     std::vector<SBFullHash> full_hashes(1, kFullHash1_2);
     std::vector<SBPrefix> prefix_hits;
     std::vector<SBFullHashResult> cache_hits;
-    EXPECT_FALSE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_FALSE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
   }
 
@@ -2070,7 +2119,7 @@
     std::vector<SBFullHash> full_hashes(1, kFullHash1_2);
     std::vector<SBPrefix> prefix_hits;
     std::vector<SBFullHashResult> cache_hits;
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(kPrefix1, prefix_hits[0]);
@@ -2090,7 +2139,7 @@
     std::vector<SBFullHash> full_hashes(1, kFullHash1_2);
     std::vector<SBPrefix> prefix_hits;
     std::vector<SBFullHashResult> cache_hits;
-    EXPECT_TRUE(database_->ContainsBrowseUrlHashesForTesting(
+    EXPECT_TRUE(database_->ContainsBrowseHashes(
         full_hashes, &prefix_hits, &cache_hits));
     ASSERT_EQ(1U, prefix_hits.size());
     EXPECT_EQ(kPrefix1, prefix_hits[0]);
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
index c4765a7..833def4 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -51,6 +51,7 @@
 #include "components/bookmarks/browser/startup_task_runner_service.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/safe_browsing_db/database_manager.h"
+#include "components/safe_browsing_db/util.h"
 #include "content/public/browser/interstitial_page.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/render_frame_host.h"
@@ -195,6 +196,7 @@
   // Deletes the current database and creates a new one.
   bool ResetDatabase() override {
     badurls_.clear();
+    urls_by_hash_.clear();
     return true;
   }
 
@@ -207,6 +209,15 @@
     cache_hits->clear();
     return ContainsUrl(MALWARE, PHISH, std::vector<GURL>(1, url), prefix_hits);
   }
+
+  bool ContainsBrowseHashes(
+      const std::vector<SBFullHash>& full_hashes,
+      std::vector<SBPrefix>* prefix_hits,
+      std::vector<SBFullHashResult>* cache_hits) override {
+    cache_hits->clear();
+    return ContainsUrl(MALWARE, PHISH, UrlsForHashes(full_hashes), prefix_hits);
+  }
+
   bool ContainsUnwantedSoftwareUrl(
       const GURL& url,
       std::vector<SBPrefix>* prefix_hits,
@@ -215,6 +226,16 @@
     return ContainsUrl(UNWANTEDURL, UNWANTEDURL, std::vector<GURL>(1, url),
                        prefix_hits);
   }
+
+  bool ContainsUnwantedSoftwareHashes(
+      const std::vector<SBFullHash>& full_hashes,
+      std::vector<SBPrefix>* prefix_hits,
+      std::vector<SBFullHashResult>* cache_hits) override {
+    cache_hits->clear();
+    return ContainsUrl(UNWANTEDURL, UNWANTEDURL, UrlsForHashes(full_hashes),
+                       prefix_hits);
+  }
+
   bool ContainsDownloadUrlPrefixes(
       const std::vector<SBPrefix>& prefixes,
       std::vector<SBPrefix>* prefix_hits) override {
@@ -272,6 +293,7 @@
                                      prefix_hits.begin(), prefix_hits.end());
     bad_prefixes_.insert(
         std::make_pair(full_hash.list_id, full_hash.hash.prefix));
+    urls_by_hash_[SBFullHashToString(full_hash.hash)] = url;
   }
 
  private:
@@ -307,6 +329,17 @@
     return hit;
   }
 
+  std::vector<GURL> UrlsForHashes(const std::vector<SBFullHash>& full_hashes) {
+    std::vector<GURL> urls;
+    for (auto hash : full_hashes) {
+      auto url_it = urls_by_hash_.find(SBFullHashToString(hash));
+      if (url_it != urls_by_hash_.end()) {
+        urls.push_back(url_it->second);
+      }
+    }
+    return urls;
+  }
+
   bool ContainsUrlPrefixes(int list_id0,
                            int list_id1,
                            const std::vector<SBPrefix>& prefixes,
@@ -326,6 +359,7 @@
 
   base::hash_map<std::string, Hits> badurls_;
   base::hash_set<std::pair<int, SBPrefix>> bad_prefixes_;
+  base::hash_map<std::string, GURL> urls_by_hash_;
 
   DISALLOW_COPY_AND_ASSIGN(TestSafeBrowsingDatabase);
 };