Home | History | Annotate | Download | only in safe_browsing
      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 "base/files/file_path.h"
      6 #include "base/memory/ref_counted.h"
      7 #include "base/memory/scoped_ptr.h"
      8 #include "base/run_loop.h"
      9 #include "base/strings/stringprintf.h"
     10 #include "base/synchronization/waitable_event.h"
     11 #include "chrome/browser/safe_browsing/browser_feature_extractor.h"
     12 #include "chrome/browser/safe_browsing/client_side_detection_host.h"
     13 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
     14 #include "chrome/browser/safe_browsing/database_manager.h"
     15 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
     16 #include "chrome/browser/safe_browsing/ui_manager.h"
     17 #include "chrome/common/chrome_switches.h"
     18 #include "chrome/common/safe_browsing/csd.pb.h"
     19 #include "chrome/common/safe_browsing/safebrowsing_messages.h"
     20 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
     21 #include "chrome/test/base/testing_profile.h"
     22 #include "content/public/browser/navigation_entry.h"
     23 #include "content/public/browser/web_contents.h"
     24 #include "content/public/test/mock_render_process_host.h"
     25 #include "content/public/test/test_browser_thread.h"
     26 #include "content/public/test/test_renderer_host.h"
     27 #include "content/public/test/web_contents_tester.h"
     28 #include "ipc/ipc_test_sink.h"
     29 #include "testing/gmock/include/gmock/gmock.h"
     30 #include "testing/gtest/include/gtest/gtest.h"
     31 #include "url/gurl.h"
     32 
     33 using ::testing::_;
     34 using ::testing::DeleteArg;
     35 using ::testing::DoAll;
     36 using ::testing::Eq;
     37 using ::testing::IsNull;
     38 using ::testing::Mock;
     39 using ::testing::NiceMock;
     40 using ::testing::NotNull;
     41 using ::testing::Pointee;
     42 using ::testing::Return;
     43 using ::testing::SaveArg;
     44 using ::testing::SetArgumentPointee;
     45 using ::testing::StrictMock;
     46 using content::BrowserThread;
     47 using content::RenderViewHostTester;
     48 using content::WebContents;
     49 
     50 namespace {
     51 
     52 const bool kFalse = false;
     53 const bool kTrue = true;
     54 
     55 }  // namespace
     56 
     57 namespace safe_browsing {
     58 namespace {
     59 // This matcher verifies that the client computed verdict
     60 // (ClientPhishingRequest) which is passed to SendClientReportPhishingRequest
     61 // has the expected fields set.  Note: we can't simply compare the protocol
     62 // buffer strings because the BrowserFeatureExtractor might add features to the
     63 // verdict object before calling SendClientReportPhishingRequest.
     64 MATCHER_P(PartiallyEqualVerdict, other, "") {
     65   return (other.url() == arg.url() &&
     66           other.client_score() == arg.client_score() &&
     67           other.is_phishing() == arg.is_phishing());
     68 }
     69 
     70 MATCHER_P(PartiallyEqualMalwareVerdict, other, "") {
     71   if (other.url() != arg.url() ||
     72       other.referrer_url() != arg.referrer_url() ||
     73       other.bad_ip_url_info_size() != arg.bad_ip_url_info_size())
     74     return false;
     75 
     76   for (int i = 0; i < other.bad_ip_url_info_size(); ++i) {
     77     if (other.bad_ip_url_info(i).ip() != arg.bad_ip_url_info(i).ip() ||
     78         other.bad_ip_url_info(i).url() != arg.bad_ip_url_info(i).url())
     79     return false;
     80   }
     81   return true;
     82 }
     83 
     84 // Test that the callback is NULL when the verdict is not phishing.
     85 MATCHER(CallbackIsNull, "") {
     86   return arg.is_null();
     87 }
     88 
     89 ACTION(QuitUIMessageLoop) {
     90   EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
     91   base::MessageLoopForUI::current()->Quit();
     92 }
     93 
     94 // It's kind of insane that InvokeArgument doesn't work with callbacks, but it
     95 // doesn't seem like it.
     96 ACTION_TEMPLATE(InvokeCallbackArgument,
     97                 HAS_1_TEMPLATE_PARAMS(int, k),
     98                 AND_2_VALUE_PARAMS(p0, p1)) {
     99   ::std::tr1::get<k>(args).Run(p0, p1);
    100 }
    101 
    102 ACTION_P(InvokeMalwareCallback, verdict) {
    103   scoped_ptr<ClientMalwareRequest> request(::std::tr1::get<1>(args));
    104   request->CopyFrom(*verdict);
    105   ::std::tr1::get<2>(args).Run(true, request.Pass());
    106 }
    107 
    108 void EmptyUrlCheckCallback(bool processed) {
    109 }
    110 
    111 class MockClientSideDetectionService : public ClientSideDetectionService {
    112  public:
    113   MockClientSideDetectionService() : ClientSideDetectionService(NULL) {}
    114   virtual ~MockClientSideDetectionService() {}
    115 
    116   MOCK_METHOD2(SendClientReportPhishingRequest,
    117                void(ClientPhishingRequest*,
    118                     const ClientReportPhishingRequestCallback&));
    119   MOCK_METHOD2(SendClientReportMalwareRequest,
    120                void(ClientMalwareRequest*,
    121                     const ClientReportMalwareRequestCallback&));
    122   MOCK_CONST_METHOD1(IsPrivateIPAddress, bool(const std::string&));
    123   MOCK_METHOD2(GetValidCachedResult, bool(const GURL&, bool*));
    124   MOCK_METHOD1(IsInCache, bool(const GURL&));
    125   MOCK_METHOD0(OverPhishingReportLimit, bool());
    126   MOCK_METHOD0(OverMalwareReportLimit, bool());
    127 
    128  private:
    129   DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService);
    130 };
    131 
    132 class MockSafeBrowsingUIManager : public SafeBrowsingUIManager {
    133  public:
    134   explicit MockSafeBrowsingUIManager(SafeBrowsingService* service)
    135       : SafeBrowsingUIManager(service) { }
    136 
    137   MOCK_METHOD1(DisplayBlockingPage, void(const UnsafeResource& resource));
    138 
    139   // Helper function which calls OnBlockingPageComplete for this client
    140   // object.
    141   void InvokeOnBlockingPageComplete(const UrlCheckCallback& callback) {
    142     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    143     // Note: this will delete the client object in the case of the CsdClient
    144     // implementation.
    145     if (!callback.is_null())
    146       callback.Run(false);
    147   }
    148 
    149  protected:
    150   virtual ~MockSafeBrowsingUIManager() { }
    151 
    152  private:
    153   DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingUIManager);
    154 };
    155 
    156 class MockSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager {
    157  public:
    158   explicit MockSafeBrowsingDatabaseManager(SafeBrowsingService* service)
    159       : SafeBrowsingDatabaseManager(service) { }
    160 
    161   MOCK_METHOD1(MatchCsdWhitelistUrl, bool(const GURL&));
    162   MOCK_METHOD1(MatchMalwareIP, bool(const std::string& ip_address));
    163   MOCK_METHOD0(IsMalwareKillSwitchOn, bool());
    164 
    165  protected:
    166   virtual ~MockSafeBrowsingDatabaseManager() {}
    167 
    168  private:
    169   DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager);
    170 };
    171 
    172 class MockTestingProfile : public TestingProfile {
    173  public:
    174   MockTestingProfile() {}
    175   virtual ~MockTestingProfile() {}
    176 
    177   MOCK_CONST_METHOD0(IsOffTheRecord, bool());
    178 };
    179 
    180 class MockBrowserFeatureExtractor : public BrowserFeatureExtractor {
    181  public:
    182   explicit MockBrowserFeatureExtractor(
    183       WebContents* tab,
    184       ClientSideDetectionHost* host)
    185       : BrowserFeatureExtractor(tab, host) {}
    186   virtual ~MockBrowserFeatureExtractor() {}
    187 
    188   MOCK_METHOD3(ExtractFeatures,
    189                void(const BrowseInfo*,
    190                     ClientPhishingRequest*,
    191                     const BrowserFeatureExtractor::DoneCallback&));
    192 
    193   MOCK_METHOD3(ExtractMalwareFeatures,
    194                void(BrowseInfo*,
    195                     ClientMalwareRequest*,
    196                     const BrowserFeatureExtractor::MalwareDoneCallback&));
    197 };
    198 
    199 }  // namespace
    200 
    201 class ClientSideDetectionHostTest : public ChromeRenderViewHostTestHarness {
    202  public:
    203   typedef SafeBrowsingUIManager::UnsafeResource UnsafeResource;
    204 
    205   virtual void SetUp() {
    206     ChromeRenderViewHostTestHarness::SetUp();
    207 
    208     // Inject service classes.
    209     csd_service_.reset(new StrictMock<MockClientSideDetectionService>());
    210     // Only used for initializing mock objects.
    211     SafeBrowsingService* sb_service =
    212         SafeBrowsingService::CreateSafeBrowsingService();
    213     database_manager_ =
    214         new StrictMock<MockSafeBrowsingDatabaseManager>(sb_service);
    215     ui_manager_ = new StrictMock<MockSafeBrowsingUIManager>(sb_service);
    216     csd_host_.reset(safe_browsing::ClientSideDetectionHost::Create(
    217         web_contents()));
    218     csd_host_->set_client_side_detection_service(csd_service_.get());
    219     csd_host_->set_safe_browsing_managers(ui_manager_.get(),
    220                                           database_manager_.get());
    221     // We need to create this here since we don't call DidStopLanding in
    222     // this test.
    223     csd_host_->browse_info_.reset(new BrowseInfo);
    224   }
    225 
    226   virtual void TearDown() {
    227     // Delete the host object on the UI thread and release the
    228     // SafeBrowsingService.
    229     BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE,
    230                               csd_host_.release());
    231     database_manager_ = NULL;
    232     ui_manager_ = NULL;
    233     base::RunLoop().RunUntilIdle();
    234     ChromeRenderViewHostTestHarness::TearDown();
    235   }
    236 
    237   virtual content::BrowserContext* CreateBrowserContext() OVERRIDE {
    238     // Set custom profile object so that we can mock calls to IsOffTheRecord.
    239     // This needs to happen before we call the parent SetUp() function.  We use
    240     // a nice mock because other parts of the code are calling IsOffTheRecord.
    241     mock_profile_ = new NiceMock<MockTestingProfile>();
    242     return mock_profile_;
    243   }
    244 
    245   void OnPhishingDetectionDone(const std::string& verdict_str) {
    246     csd_host_->OnPhishingDetectionDone(verdict_str);
    247   }
    248 
    249   void DidStopLoading() {
    250     csd_host_->DidStopLoading(pending_rvh());
    251   }
    252 
    253   void UpdateIPUrlMap(const std::string& ip, const std::string& host) {
    254     csd_host_->UpdateIPUrlMap(ip, host, "", "", ResourceType::OBJECT);
    255   }
    256 
    257   BrowseInfo* GetBrowseInfo() {
    258     return csd_host_->browse_info_.get();
    259   }
    260 
    261   void ExpectPreClassificationChecks(const GURL& url,
    262                                      const bool* is_private,
    263                                      const bool* is_incognito,
    264                                      const bool* match_csd_whitelist,
    265                                      const bool* malware_killswitch,
    266                                      const bool* get_valid_cached_result,
    267                                      const bool* is_in_cache,
    268                                      const bool* over_phishing_report_limit,
    269                                      const bool* over_malware_report_limit) {
    270     if (is_private) {
    271       EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_))
    272           .WillOnce(Return(*is_private));
    273     }
    274     if (is_incognito) {
    275       EXPECT_CALL(*mock_profile_, IsOffTheRecord())
    276           .WillRepeatedly(Return(*is_incognito));
    277     }
    278     if (match_csd_whitelist) {
    279       EXPECT_CALL(*database_manager_.get(), MatchCsdWhitelistUrl(url))
    280           .WillOnce(Return(*match_csd_whitelist));
    281     }
    282     if (malware_killswitch) {
    283       EXPECT_CALL(*database_manager_.get(), IsMalwareKillSwitchOn())
    284           .WillRepeatedly(Return(*malware_killswitch));
    285     }
    286     if (get_valid_cached_result) {
    287       EXPECT_CALL(*csd_service_, GetValidCachedResult(url, NotNull()))
    288           .WillOnce(DoAll(SetArgumentPointee<1>(true),
    289                           Return(*get_valid_cached_result)));
    290     }
    291     if (is_in_cache) {
    292       EXPECT_CALL(*csd_service_, IsInCache(url)).WillOnce(Return(*is_in_cache));
    293     }
    294     if (over_phishing_report_limit) {
    295       EXPECT_CALL(*csd_service_, OverPhishingReportLimit())
    296           .WillOnce(Return(*over_phishing_report_limit));
    297     }
    298     if (over_malware_report_limit) {
    299       EXPECT_CALL(*csd_service_, OverMalwareReportLimit())
    300           .WillOnce(Return(*over_malware_report_limit));
    301     }
    302   }
    303 
    304   void WaitAndCheckPreClassificationChecks() {
    305     // Wait for CheckCsdWhitelist and CheckCache() to be called if at all.
    306     base::RunLoop().RunUntilIdle();
    307     EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
    308     EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
    309     EXPECT_TRUE(Mock::VerifyAndClear(database_manager_.get()));
    310     EXPECT_TRUE(Mock::VerifyAndClear(mock_profile_));
    311   }
    312 
    313   void SetFeatureExtractor(BrowserFeatureExtractor* extractor) {
    314     csd_host_->feature_extractor_.reset(extractor);
    315   }
    316 
    317   void SetRedirectChain(const std::vector<GURL>& redirect_chain) {
    318     csd_host_->browse_info_->url_redirects = redirect_chain;
    319   }
    320 
    321   void SetReferrer(const GURL& referrer) {
    322     csd_host_->browse_info_->referrer = referrer;
    323   }
    324 
    325   void ExpectShouldClassifyForMalwareResult(bool should_classify) {
    326     EXPECT_EQ(should_classify, csd_host_->should_classify_for_malware_);
    327   }
    328 
    329   void ExpectStartPhishingDetection(const GURL* url) {
    330     const IPC::Message* msg = process()->sink().GetFirstMessageMatching(
    331         SafeBrowsingMsg_StartPhishingDetection::ID);
    332     if (url) {
    333       ASSERT_TRUE(msg);
    334       Tuple1<GURL> actual_url;
    335       SafeBrowsingMsg_StartPhishingDetection::Read(msg, &actual_url);
    336       EXPECT_EQ(*url, actual_url.a);
    337       EXPECT_EQ(rvh()->GetRoutingID(), msg->routing_id());
    338       process()->sink().ClearMessages();
    339     } else {
    340       ASSERT_FALSE(msg);
    341     }
    342   }
    343 
    344   void TestUnsafeResourceCopied(const UnsafeResource& resource) {
    345     ASSERT_TRUE(csd_host_->unsafe_resource_.get());
    346     // Test that the resource from OnSafeBrowsingHit notification was copied
    347     // into the CSDH.
    348     EXPECT_EQ(resource.url, csd_host_->unsafe_resource_->url);
    349     EXPECT_EQ(resource.original_url, csd_host_->unsafe_resource_->original_url);
    350     EXPECT_EQ(resource.is_subresource,
    351               csd_host_->unsafe_resource_->is_subresource);
    352     EXPECT_EQ(resource.threat_type, csd_host_->unsafe_resource_->threat_type);
    353     EXPECT_TRUE(csd_host_->unsafe_resource_->callback.is_null());
    354     EXPECT_EQ(resource.render_process_host_id,
    355               csd_host_->unsafe_resource_->render_process_host_id);
    356     EXPECT_EQ(resource.render_view_id,
    357               csd_host_->unsafe_resource_->render_view_id);
    358   }
    359 
    360   void SetUnsafeSubResourceForCurrent() {
    361     UnsafeResource resource;
    362     resource.url = GURL("http://www.malware.com/");
    363     resource.original_url = web_contents()->GetURL();
    364     resource.is_subresource = true;
    365     resource.threat_type = SB_THREAT_TYPE_URL_MALWARE;
    366     resource.callback = base::Bind(&EmptyUrlCheckCallback);
    367     resource.render_process_host_id = web_contents()->GetRenderProcessHost()->
    368         GetID();
    369     resource.render_view_id =
    370         web_contents()->GetRenderViewHost()->GetRoutingID();
    371     ASSERT_FALSE(csd_host_->DidPageReceiveSafeBrowsingMatch());
    372     csd_host_->OnSafeBrowsingMatch(resource);
    373     ASSERT_TRUE(csd_host_->DidPageReceiveSafeBrowsingMatch());
    374     csd_host_->OnSafeBrowsingHit(resource);
    375     ASSERT_TRUE(csd_host_->DidPageReceiveSafeBrowsingMatch());
    376     resource.callback.Reset();
    377     ASSERT_TRUE(csd_host_->DidShowSBInterstitial());
    378     TestUnsafeResourceCopied(resource);
    379   }
    380 
    381   void NavigateWithSBHitAndCommit(const GURL& url) {
    382     // Create a pending navigation.
    383     controller().LoadURL(
    384         url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string());
    385 
    386     ASSERT_TRUE(pending_rvh());
    387     if (web_contents()->GetRenderViewHost()->GetProcess()->GetID() ==
    388         pending_rvh()->GetProcess()->GetID()) {
    389       EXPECT_NE(web_contents()->GetRenderViewHost()->GetRoutingID(),
    390                 pending_rvh()->GetRoutingID());
    391     }
    392 
    393     // Simulate a safebrowsing hit before navigation completes.
    394     UnsafeResource resource;
    395     resource.url = url;
    396     resource.original_url = url;
    397     resource.is_subresource = false;
    398     resource.threat_type = SB_THREAT_TYPE_URL_MALWARE;
    399     resource.callback = base::Bind(&EmptyUrlCheckCallback);
    400     resource.render_process_host_id = pending_rvh()->GetProcess()->GetID();
    401     resource.render_view_id = pending_rvh()->GetRoutingID();
    402     csd_host_->OnSafeBrowsingMatch(resource);
    403     csd_host_->OnSafeBrowsingHit(resource);
    404     resource.callback.Reset();
    405 
    406     ASSERT_TRUE(csd_host_->DidPageReceiveSafeBrowsingMatch());
    407 
    408     // LoadURL created a navigation entry, now simulate the RenderView sending
    409     // a notification that it actually navigated.
    410     content::WebContentsTester::For(web_contents())->CommitPendingNavigation();
    411 
    412     ASSERT_TRUE(csd_host_->DidPageReceiveSafeBrowsingMatch());
    413     ASSERT_TRUE(csd_host_->DidShowSBInterstitial());
    414     TestUnsafeResourceCopied(resource);
    415   }
    416 
    417   void NavigateWithoutSBHitAndCommit(const GURL& safe_url) {
    418     controller().LoadURL(
    419         safe_url, content::Referrer(), content::PAGE_TRANSITION_LINK,
    420         std::string());
    421 
    422     ASSERT_TRUE(pending_rvh());
    423     if (web_contents()->GetRenderViewHost()->GetProcess()->GetID() ==
    424         pending_rvh()->GetProcess()->GetID()) {
    425       EXPECT_NE(web_contents()->GetRenderViewHost()->GetRoutingID(),
    426                 pending_rvh()->GetRoutingID());
    427     }
    428     ASSERT_FALSE(csd_host_->DidPageReceiveSafeBrowsingMatch());
    429     ASSERT_FALSE(csd_host_->DidShowSBInterstitial());
    430 
    431     content::WebContentsTester::For(web_contents())->CommitPendingNavigation();
    432     ASSERT_FALSE(csd_host_->DidPageReceiveSafeBrowsingMatch());
    433     ASSERT_FALSE(csd_host_->DidShowSBInterstitial());
    434   }
    435 
    436   void CheckIPUrlEqual(const std::vector<IPUrlInfo>& expect,
    437                        const std::vector<IPUrlInfo>& result) {
    438     ASSERT_EQ(expect.size(), result.size());
    439 
    440     for (unsigned int i = 0; i < expect.size(); ++i) {
    441       EXPECT_EQ(expect[i].url, result[i].url);
    442       EXPECT_EQ(expect[i].method, result[i].method);
    443       EXPECT_EQ(expect[i].referrer, result[i].referrer);
    444       EXPECT_EQ(expect[i].resource_type, result[i].resource_type);
    445     }
    446   }
    447 
    448  protected:
    449   scoped_ptr<ClientSideDetectionHost> csd_host_;
    450   scoped_ptr<StrictMock<MockClientSideDetectionService> > csd_service_;
    451   scoped_refptr<StrictMock<MockSafeBrowsingUIManager> > ui_manager_;
    452   scoped_refptr<StrictMock<MockSafeBrowsingDatabaseManager> > database_manager_;
    453   MockTestingProfile* mock_profile_;  // We don't own this object
    454 };
    455 
    456 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneInvalidVerdict) {
    457   // Case 0: renderer sends an invalid verdict string that we're unable to
    458   // parse.
    459   MockBrowserFeatureExtractor* mock_extractor =
    460       new StrictMock<MockBrowserFeatureExtractor>(
    461           web_contents(),
    462           csd_host_.get());
    463   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
    464   EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _)).Times(0);
    465   OnPhishingDetectionDone("Invalid Protocol Buffer");
    466   EXPECT_TRUE(Mock::VerifyAndClear(mock_extractor));
    467 }
    468 
    469 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneNotPhishing) {
    470   // Case 1: client thinks the page is phishing.  The server does not agree.
    471   // No interstitial is shown.
    472   MockBrowserFeatureExtractor* mock_extractor =
    473       new StrictMock<MockBrowserFeatureExtractor>(
    474           web_contents(),
    475           csd_host_.get());
    476   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
    477 
    478   ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
    479   ClientPhishingRequest verdict;
    480   verdict.set_url("http://phishingurl.com/");
    481   verdict.set_client_score(1.0f);
    482   verdict.set_is_phishing(true);
    483 
    484   EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _))
    485       .WillOnce(DoAll(DeleteArg<1>(),
    486                       InvokeCallbackArgument<2>(true, &verdict)));
    487   EXPECT_CALL(*csd_service_,
    488               SendClientReportPhishingRequest(
    489                   Pointee(PartiallyEqualVerdict(verdict)), _))
    490       .WillOnce(SaveArg<1>(&cb));
    491   OnPhishingDetectionDone(verdict.SerializeAsString());
    492   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
    493   ASSERT_FALSE(cb.is_null());
    494 
    495   // Make sure DisplayBlockingPage is not going to be called.
    496   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)).Times(0);
    497   cb.Run(GURL(verdict.url()), false);
    498   base::RunLoop().RunUntilIdle();
    499   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
    500 }
    501 
    502 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneDisabled) {
    503   // Case 2: client thinks the page is phishing and so does the server but
    504   // showing the interstitial is disabled => no interstitial is shown.
    505   MockBrowserFeatureExtractor* mock_extractor =
    506       new StrictMock<MockBrowserFeatureExtractor>(
    507           web_contents(),
    508           csd_host_.get());
    509   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
    510 
    511   ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
    512   ClientPhishingRequest verdict;
    513   verdict.set_url("http://phishingurl.com/");
    514   verdict.set_client_score(1.0f);
    515   verdict.set_is_phishing(true);
    516 
    517   EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _))
    518       .WillOnce(DoAll(DeleteArg<1>(),
    519                       InvokeCallbackArgument<2>(true, &verdict)));
    520   EXPECT_CALL(*csd_service_,
    521               SendClientReportPhishingRequest(
    522                   Pointee(PartiallyEqualVerdict(verdict)), _))
    523       .WillOnce(SaveArg<1>(&cb));
    524   OnPhishingDetectionDone(verdict.SerializeAsString());
    525   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
    526   ASSERT_FALSE(cb.is_null());
    527 
    528   // Make sure DisplayBlockingPage is not going to be called.
    529   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)).Times(0);
    530   cb.Run(GURL(verdict.url()), false);
    531   base::RunLoop().RunUntilIdle();
    532   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
    533 }
    534 
    535 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneShowInterstitial) {
    536   // Case 3: client thinks the page is phishing and so does the server.
    537   // We show an interstitial.
    538   MockBrowserFeatureExtractor* mock_extractor =
    539       new StrictMock<MockBrowserFeatureExtractor>(
    540           web_contents(),
    541           csd_host_.get());
    542   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
    543 
    544   ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
    545   GURL phishing_url("http://phishingurl.com/");
    546   ClientPhishingRequest verdict;
    547   verdict.set_url(phishing_url.spec());
    548   verdict.set_client_score(1.0f);
    549   verdict.set_is_phishing(true);
    550 
    551   EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _))
    552       .WillOnce(DoAll(DeleteArg<1>(),
    553                       InvokeCallbackArgument<2>(true, &verdict)));
    554   EXPECT_CALL(*csd_service_,
    555               SendClientReportPhishingRequest(
    556                   Pointee(PartiallyEqualVerdict(verdict)), _))
    557       .WillOnce(SaveArg<1>(&cb));
    558   OnPhishingDetectionDone(verdict.SerializeAsString());
    559   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
    560   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
    561   ASSERT_FALSE(cb.is_null());
    562 
    563   UnsafeResource resource;
    564   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
    565       .WillOnce(SaveArg<0>(&resource));
    566   cb.Run(phishing_url, true);
    567 
    568   base::RunLoop().RunUntilIdle();
    569   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
    570   EXPECT_EQ(phishing_url, resource.url);
    571   EXPECT_EQ(phishing_url, resource.original_url);
    572   EXPECT_FALSE(resource.is_subresource);
    573   EXPECT_EQ(SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL, resource.threat_type);
    574   EXPECT_EQ(web_contents()->GetRenderProcessHost()->GetID(),
    575             resource.render_process_host_id);
    576   EXPECT_EQ(web_contents()->GetRenderViewHost()->GetRoutingID(),
    577             resource.render_view_id);
    578 
    579   // Make sure the client object will be deleted.
    580   BrowserThread::PostTask(
    581       BrowserThread::IO,
    582       FROM_HERE,
    583       base::Bind(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete,
    584                  ui_manager_, resource.callback));
    585 }
    586 
    587 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneMultiplePings) {
    588   // Case 4 & 5: client thinks a page is phishing then navigates to
    589   // another page which is also considered phishing by the client
    590   // before the server responds with a verdict.  After a while the
    591   // server responds for both requests with a phishing verdict.  Only
    592   // a single interstitial is shown for the second URL.
    593   MockBrowserFeatureExtractor* mock_extractor =
    594       new StrictMock<MockBrowserFeatureExtractor>(
    595           web_contents(),
    596           csd_host_.get());
    597   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
    598 
    599   ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
    600   GURL phishing_url("http://phishingurl.com/");
    601   ClientPhishingRequest verdict;
    602   verdict.set_url(phishing_url.spec());
    603   verdict.set_client_score(1.0f);
    604   verdict.set_is_phishing(true);
    605 
    606   EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _))
    607       .WillOnce(DoAll(DeleteArg<1>(),
    608                       InvokeCallbackArgument<2>(true, &verdict)));
    609   EXPECT_CALL(*csd_service_,
    610               SendClientReportPhishingRequest(
    611                   Pointee(PartiallyEqualVerdict(verdict)), _))
    612       .WillOnce(SaveArg<1>(&cb));
    613   OnPhishingDetectionDone(verdict.SerializeAsString());
    614   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
    615   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
    616   ASSERT_FALSE(cb.is_null());
    617 
    618   // Set this back to a normal browser feature extractor since we're using
    619   // NavigateAndCommit() and it's easier to use the real thing than setting up
    620   // mock expectations.
    621   SetFeatureExtractor(new BrowserFeatureExtractor(web_contents(),
    622                                                   csd_host_.get()));
    623   GURL other_phishing_url("http://other_phishing_url.com/bla");
    624   ExpectPreClassificationChecks(other_phishing_url, &kFalse, &kFalse, &kFalse,
    625                                 &kFalse, &kFalse, &kFalse, &kFalse, &kFalse);
    626   // We navigate away.  The callback cb should be revoked.
    627   NavigateAndCommit(other_phishing_url);
    628   // Wait for the pre-classification checks to finish for other_phishing_url.
    629   WaitAndCheckPreClassificationChecks();
    630 
    631   ClientSideDetectionService::ClientReportPhishingRequestCallback cb_other;
    632   verdict.set_url(other_phishing_url.spec());
    633   verdict.set_client_score(0.8f);
    634   EXPECT_CALL(*csd_service_,
    635               SendClientReportPhishingRequest(
    636                   Pointee(PartiallyEqualVerdict(verdict)), _))
    637       .WillOnce(DoAll(DeleteArg<0>(),
    638                       SaveArg<1>(&cb_other),
    639                       QuitUIMessageLoop()));
    640   std::vector<GURL> redirect_chain;
    641   redirect_chain.push_back(other_phishing_url);
    642   SetRedirectChain(redirect_chain);
    643   OnPhishingDetectionDone(verdict.SerializeAsString());
    644   base::MessageLoop::current()->Run();
    645   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
    646   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
    647   ASSERT_FALSE(cb_other.is_null());
    648 
    649   // We expect that the interstitial is shown for the second phishing URL and
    650   // not for the first phishing URL.
    651   UnsafeResource resource;
    652   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
    653       .WillOnce(SaveArg<0>(&resource));
    654 
    655   cb.Run(phishing_url, true);  // Should have no effect.
    656   cb_other.Run(other_phishing_url, true);  // Should show interstitial.
    657 
    658   base::RunLoop().RunUntilIdle();
    659   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
    660   EXPECT_EQ(other_phishing_url, resource.url);
    661   EXPECT_EQ(other_phishing_url, resource.original_url);
    662   EXPECT_FALSE(resource.is_subresource);
    663   EXPECT_EQ(SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL, resource.threat_type);
    664   EXPECT_EQ(web_contents()->GetRenderProcessHost()->GetID(),
    665             resource.render_process_host_id);
    666   EXPECT_EQ(web_contents()->GetRenderViewHost()->GetRoutingID(),
    667             resource.render_view_id);
    668 
    669   // Make sure the client object will be deleted.
    670   BrowserThread::PostTask(
    671       BrowserThread::IO,
    672       FROM_HERE,
    673       base::Bind(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete,
    674                  ui_manager_, resource.callback));
    675 }
    676 
    677 TEST_F(ClientSideDetectionHostTest,
    678        OnPhishingDetectionDoneVerdictNotPhishing) {
    679   // Case 6: renderer sends a verdict string that isn't phishing.
    680   MockBrowserFeatureExtractor* mock_extractor =
    681       new StrictMock<MockBrowserFeatureExtractor>(
    682           web_contents(),
    683           csd_host_.get());
    684   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
    685 
    686   ClientPhishingRequest verdict;
    687   verdict.set_url("http://not-phishing.com/");
    688   verdict.set_client_score(0.1f);
    689   verdict.set_is_phishing(false);
    690 
    691   EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _)).Times(0);
    692   OnPhishingDetectionDone(verdict.SerializeAsString());
    693   EXPECT_TRUE(Mock::VerifyAndClear(mock_extractor));
    694 }
    695 
    696 TEST_F(ClientSideDetectionHostTest,
    697        OnPhishingDetectionDoneVerdictNotPhishingButSBMatchSubResource) {
    698   // Case 7: renderer sends a verdict string that isn't phishing but the URL
    699   // of a subresource was on the regular phishing or malware lists.
    700   GURL url("http://not-phishing.com/");
    701   ClientPhishingRequest verdict;
    702   verdict.set_url(url.spec());
    703   verdict.set_client_score(0.1f);
    704   verdict.set_is_phishing(false);
    705 
    706   // First we have to navigate to the URL to set the unique page ID.
    707   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
    708                                 &kFalse, &kFalse, &kFalse, &kFalse);
    709   NavigateAndCommit(url);
    710   WaitAndCheckPreClassificationChecks();
    711   SetUnsafeSubResourceForCurrent();
    712 
    713   EXPECT_CALL(*csd_service_,
    714               SendClientReportPhishingRequest(
    715                   Pointee(PartiallyEqualVerdict(verdict)), CallbackIsNull()))
    716       .WillOnce(DoAll(DeleteArg<0>(), QuitUIMessageLoop()));
    717   std::vector<GURL> redirect_chain;
    718   redirect_chain.push_back(url);
    719   SetRedirectChain(redirect_chain);
    720   OnPhishingDetectionDone(verdict.SerializeAsString());
    721   base::MessageLoop::current()->Run();
    722   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
    723 }
    724 
    725 TEST_F(ClientSideDetectionHostTest,
    726        OnPhishingDetectionDoneVerdictNotPhishingButSBMatchOnNewRVH) {
    727   // When navigating to a different host (thus creating a pending RVH) which
    728   // matches regular malware list, and after navigation the renderer sends a
    729   // verdict string that isn't phishing, we should still send the report.
    730 
    731   // Do an initial navigation to a safe host.
    732   GURL start_url("http://safe.example.com/");
    733   ExpectPreClassificationChecks(
    734       start_url, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse,
    735       &kFalse);
    736   NavigateAndCommit(start_url);
    737   WaitAndCheckPreClassificationChecks();
    738 
    739   // Now navigate to a different host which will have a malware hit before the
    740   // navigation commits.
    741   GURL url("http://malware-but-not-phishing.com/");
    742   ClientPhishingRequest verdict;
    743   verdict.set_url(url.spec());
    744   verdict.set_client_score(0.1f);
    745   verdict.set_is_phishing(false);
    746 
    747   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
    748                                 &kFalse, &kFalse, &kFalse, &kFalse);
    749   NavigateWithSBHitAndCommit(url);
    750   WaitAndCheckPreClassificationChecks();
    751 
    752   EXPECT_CALL(*csd_service_,
    753               SendClientReportPhishingRequest(
    754                   Pointee(PartiallyEqualVerdict(verdict)), CallbackIsNull()))
    755       .WillOnce(DoAll(DeleteArg<0>(), QuitUIMessageLoop()));
    756   std::vector<GURL> redirect_chain;
    757   redirect_chain.push_back(url);
    758   SetRedirectChain(redirect_chain);
    759   OnPhishingDetectionDone(verdict.SerializeAsString());
    760   base::MessageLoop::current()->Run();
    761   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
    762 
    763   ExpectPreClassificationChecks(start_url, &kFalse, &kFalse, &kFalse, &kFalse,
    764                                 &kFalse, &kFalse, &kFalse, &kFalse);
    765   NavigateWithoutSBHitAndCommit(start_url);
    766   WaitAndCheckPreClassificationChecks();
    767 }
    768 
    769 TEST_F(ClientSideDetectionHostTest,
    770        DidStopLoadingShowMalwareInterstitial) {
    771   // Case 9: client thinks the page match malware IP and so does the server.
    772   // We show an sub-resource malware interstitial.
    773   MockBrowserFeatureExtractor* mock_extractor =
    774       new StrictMock<MockBrowserFeatureExtractor>(
    775           web_contents(),
    776           csd_host_.get());
    777   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
    778 
    779   GURL malware_landing_url("http://malware.com/");
    780   GURL malware_ip_url("http://badip.com");
    781   ClientMalwareRequest malware_verdict;
    782   malware_verdict.set_url("http://malware.com/");
    783   ClientMalwareRequest::UrlInfo* badipurl =
    784       malware_verdict.add_bad_ip_url_info();
    785   badipurl->set_ip("1.2.3.4");
    786   badipurl->set_url("http://badip.com");
    787 
    788   ExpectPreClassificationChecks(GURL(malware_verdict.url()), &kFalse, &kFalse,
    789                                 &kFalse, &kFalse, &kFalse, &kFalse, &kFalse,
    790                                 &kFalse);
    791   NavigateAndCommit(GURL(malware_verdict.url()));
    792   WaitAndCheckPreClassificationChecks();
    793 
    794   ClientSideDetectionService::ClientReportMalwareRequestCallback cb;
    795   EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _, _))
    796       .WillOnce(InvokeMalwareCallback(&malware_verdict));
    797   EXPECT_CALL(*csd_service_,
    798               SendClientReportMalwareRequest(
    799                   Pointee(PartiallyEqualMalwareVerdict(malware_verdict)), _))
    800       .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
    801   DidStopLoading();
    802   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
    803   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
    804   ASSERT_FALSE(cb.is_null());
    805 
    806   UnsafeResource resource;
    807   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
    808       .WillOnce(SaveArg<0>(&resource));
    809   cb.Run(malware_landing_url, malware_ip_url, true);
    810 
    811   base::RunLoop().RunUntilIdle();
    812   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
    813   EXPECT_EQ(malware_ip_url, resource.url);
    814   EXPECT_EQ(malware_landing_url, resource.original_url);
    815   EXPECT_TRUE(resource.is_subresource);
    816   EXPECT_EQ(SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL, resource.threat_type);
    817   EXPECT_EQ(web_contents()->GetRenderProcessHost()->GetID(),
    818             resource.render_process_host_id);
    819   EXPECT_EQ(web_contents()->GetRenderViewHost()->GetRoutingID(),
    820             resource.render_view_id);
    821 
    822   // Make sure the client object will be deleted.
    823   BrowserThread::PostTask(
    824       BrowserThread::IO,
    825       FROM_HERE,
    826       base::Bind(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete,
    827                  ui_manager_, resource.callback));
    828 }
    829 
    830 TEST_F(ClientSideDetectionHostTest, UpdateIPUrlMap) {
    831   BrowseInfo* browse_info = GetBrowseInfo();
    832 
    833   // Empty IP or host are skipped
    834   UpdateIPUrlMap("250.10.10.10", std::string());
    835   ASSERT_EQ(0U, browse_info->ips.size());
    836   UpdateIPUrlMap(std::string(), "http://google.com/a");
    837   ASSERT_EQ(0U, browse_info->ips.size());
    838   UpdateIPUrlMap(std::string(), std::string());
    839   ASSERT_EQ(0U, browse_info->ips.size());
    840 
    841   std::vector<IPUrlInfo> expected_urls;
    842   for (int i = 0; i < 20; i++) {
    843     std::string url = base::StringPrintf("http://%d.com/", i);
    844     expected_urls.push_back(IPUrlInfo(url, "", "", ResourceType::OBJECT));
    845     UpdateIPUrlMap("250.10.10.10", url);
    846   }
    847   ASSERT_EQ(1U, browse_info->ips.size());
    848   ASSERT_EQ(20U, browse_info->ips["250.10.10.10"].size());
    849   CheckIPUrlEqual(expected_urls,
    850                   browse_info->ips["250.10.10.10"]);
    851 
    852   // Add more urls for this ip, it exceeds max limit and won't be added
    853   UpdateIPUrlMap("250.10.10.10", "http://21.com/");
    854   ASSERT_EQ(1U, browse_info->ips.size());
    855   ASSERT_EQ(20U, browse_info->ips["250.10.10.10"].size());
    856   CheckIPUrlEqual(expected_urls,
    857                   browse_info->ips["250.10.10.10"]);
    858 
    859   // Add 199 more IPs
    860   for (int i = 0; i < 199; i++) {
    861     std::string ip = base::StringPrintf("%d.%d.%d.256", i, i, i);
    862     expected_urls.clear();
    863     expected_urls.push_back(IPUrlInfo("test.com/", "", "",
    864                             ResourceType::OBJECT));
    865     UpdateIPUrlMap(ip, "test.com/");
    866     ASSERT_EQ(1U, browse_info->ips[ip].size());
    867     CheckIPUrlEqual(expected_urls,
    868                     browse_info->ips[ip]);
    869   }
    870   ASSERT_EQ(200U, browse_info->ips.size());
    871 
    872   // Exceeding max ip limit 200, these won't be added
    873   UpdateIPUrlMap("250.250.250.250", "goo.com/");
    874   UpdateIPUrlMap("250.250.250.250", "bar.com/");
    875   UpdateIPUrlMap("250.250.0.250", "foo.com/");
    876   ASSERT_EQ(200U, browse_info->ips.size());
    877 
    878   // Add url to existing IPs succeed
    879   UpdateIPUrlMap("100.100.100.256", "more.com/");
    880   ASSERT_EQ(2U, browse_info->ips["100.100.100.256"].size());
    881   expected_urls.clear();
    882   expected_urls.push_back(IPUrlInfo("test.com/", "", "", ResourceType::OBJECT));
    883   expected_urls.push_back(IPUrlInfo("more.com/", "", "", ResourceType::OBJECT));
    884   CheckIPUrlEqual(expected_urls,
    885                   browse_info->ips["100.100.100.256"]);
    886 }
    887 
    888 TEST_F(ClientSideDetectionHostTest, NavigationCancelsShouldClassifyUrl) {
    889   // Test that canceling pending should classify requests works as expected.
    890 
    891   GURL first_url("http://first.phishy.url.com");
    892   GURL second_url("http://second.url.com/");
    893   // The first few checks are done synchronously so check that they have been
    894   // done for the first URL, while the second URL has all the checks done.  We
    895   // need to manually set up the IsPrivateIPAddress mock since if the same mock
    896   // expectation is specified twice, gmock will only use the last instance of
    897   // it, meaning the first will never be matched.
    898   EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_))
    899       .WillOnce(Return(false))
    900       .WillOnce(Return(false));
    901   ExpectPreClassificationChecks(first_url, NULL, &kFalse, &kFalse, &kFalse,
    902                                 NULL, NULL, NULL, NULL);
    903   ExpectPreClassificationChecks(second_url, NULL, &kFalse, &kFalse, &kFalse,
    904                                 &kFalse, &kFalse, &kFalse, &kFalse);
    905 
    906   NavigateAndCommit(first_url);
    907   // Don't flush the message loop, as we want to navigate to a different
    908   // url before the final pre-classification checks are run.
    909   NavigateAndCommit(second_url);
    910   WaitAndCheckPreClassificationChecks();
    911 }
    912 
    913 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckPass) {
    914   // Navigate the tab to a page.  We should see a StartPhishingDetection IPC.
    915   GURL url("http://host.com/");
    916   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
    917                                 &kFalse, &kFalse, &kFalse, &kFalse);
    918   NavigateAndCommit(url);
    919   WaitAndCheckPreClassificationChecks();
    920 
    921   ExpectStartPhishingDetection(&url);
    922   ExpectShouldClassifyForMalwareResult(true);
    923 }
    924 
    925 TEST_F(ClientSideDetectionHostTest,
    926        TestPreClassificationCheckInPageNavigation) {
    927   GURL url("http://host.com/");
    928   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
    929                                 &kFalse, &kFalse, &kFalse, &kFalse);
    930   NavigateAndCommit(url);
    931   WaitAndCheckPreClassificationChecks();
    932 
    933   ExpectStartPhishingDetection(&url);
    934   ExpectShouldClassifyForMalwareResult(true);
    935 
    936   // Now try an in-page navigation.  This should not trigger an IPC.
    937   EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_)).Times(0);
    938   GURL inpage("http://host.com/#foo");
    939   ExpectPreClassificationChecks(inpage, NULL, NULL, NULL, NULL, NULL, NULL,
    940                                 NULL, NULL);
    941   NavigateAndCommit(inpage);
    942   WaitAndCheckPreClassificationChecks();
    943 
    944   ExpectStartPhishingDetection(NULL);
    945   ExpectShouldClassifyForMalwareResult(true);
    946 }
    947 
    948 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckXHTML) {
    949   // Check that XHTML is supported, in addition to the default HTML type.
    950   GURL url("http://host.com/xhtml");
    951   rvh_tester()->SetContentsMimeType("application/xhtml+xml");
    952   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
    953                                 &kFalse, &kFalse, &kFalse, &kFalse);
    954   NavigateAndCommit(url);
    955   WaitAndCheckPreClassificationChecks();
    956 
    957   ExpectStartPhishingDetection(&url);
    958   ExpectShouldClassifyForMalwareResult(true);
    959 }
    960 
    961 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckTwoNavigations) {
    962   // Navigate to two hosts, which should cause two IPCs.
    963   GURL url1("http://host1.com/");
    964   ExpectPreClassificationChecks(url1, &kFalse, &kFalse, &kFalse, &kFalse,
    965                                 &kFalse, &kFalse, &kFalse, &kFalse);
    966   NavigateAndCommit(url1);
    967   WaitAndCheckPreClassificationChecks();
    968 
    969   ExpectStartPhishingDetection(&url1);
    970   ExpectShouldClassifyForMalwareResult(true);
    971 
    972   GURL url2("http://host2.com/");
    973   ExpectPreClassificationChecks(url2, &kFalse, &kFalse, &kFalse, &kFalse,
    974                                 &kFalse, &kFalse, &kFalse, &kFalse);
    975   NavigateAndCommit(url2);
    976   WaitAndCheckPreClassificationChecks();
    977 
    978   ExpectStartPhishingDetection(&url2);
    979   ExpectShouldClassifyForMalwareResult(true);
    980 }
    981 
    982 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckMimeType) {
    983   // If the mime type is not one that we support, no IPC should be triggered
    984   // but all pre-classification checks should run because we might classify
    985   // other mime types for malware.
    986   // Note: for this test to work correctly, the new URL must be on the
    987   // same domain as the previous URL, otherwise it will create a new
    988   // RenderViewHost that won't have the mime type set.
    989   GURL url("http://host2.com/image.jpg");
    990   rvh_tester()->SetContentsMimeType("image/jpeg");
    991   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
    992                                 &kFalse, &kFalse, &kFalse, &kFalse);
    993   NavigateAndCommit(url);
    994   WaitAndCheckPreClassificationChecks();
    995 
    996   ExpectStartPhishingDetection(NULL);
    997   ExpectShouldClassifyForMalwareResult(true);
    998 }
    999 
   1000 TEST_F(ClientSideDetectionHostTest,
   1001        TestPreClassificationCheckPrivateIpAddress) {
   1002   // If IsPrivateIPAddress returns true, no IPC should be triggered.
   1003   GURL url("http://host3.com/");
   1004   ExpectPreClassificationChecks(url, &kTrue, &kFalse, NULL, NULL, NULL, NULL,
   1005                                 NULL, NULL);
   1006   NavigateAndCommit(url);
   1007   WaitAndCheckPreClassificationChecks();
   1008   const IPC::Message* msg = process()->sink().GetFirstMessageMatching(
   1009       SafeBrowsingMsg_StartPhishingDetection::ID);
   1010   ASSERT_FALSE(msg);
   1011   ExpectShouldClassifyForMalwareResult(false);
   1012 }
   1013 
   1014 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckIncognito) {
   1015   // If the tab is incognito there should be no IPC.  Also, we shouldn't
   1016   // even check the csd-whitelist.
   1017   GURL url("http://host4.com/");
   1018   ExpectPreClassificationChecks(url, &kFalse, &kTrue, NULL, NULL, NULL, NULL,
   1019                                 NULL, NULL);
   1020   NavigateAndCommit(url);
   1021   WaitAndCheckPreClassificationChecks();
   1022 
   1023   ExpectStartPhishingDetection(NULL);
   1024   ExpectShouldClassifyForMalwareResult(false);
   1025 }
   1026 
   1027 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckCsdWhitelist) {
   1028   // If the URL is on the csd whitelist no phishing IPC should be sent
   1029   // but we should classify the URL for malware.
   1030   GURL url("http://host5.com/");
   1031   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kTrue, &kFalse, &kFalse,
   1032                                 &kFalse, &kFalse, &kFalse);
   1033   NavigateAndCommit(url);
   1034   WaitAndCheckPreClassificationChecks();
   1035 
   1036   ExpectStartPhishingDetection(NULL);
   1037   ExpectShouldClassifyForMalwareResult(true);
   1038 }
   1039 
   1040 TEST_F(ClientSideDetectionHostTest,
   1041        TestPreClassificationCheckMalwareKillSwitch) {
   1042   // If the malware killswitch is on we shouldn't classify the page for malware.
   1043   GURL url("http://host5.com/kill-switch");
   1044   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kTrue, &kFalse,
   1045                                 &kFalse, &kFalse, &kFalse);
   1046   NavigateAndCommit(url);
   1047   WaitAndCheckPreClassificationChecks();
   1048 
   1049   ExpectStartPhishingDetection(&url);
   1050   ExpectShouldClassifyForMalwareResult(false);
   1051 }
   1052 
   1053 TEST_F(ClientSideDetectionHostTest,
   1054        TestPreClassificationCheckKillswitchAndCsdWhitelist) {
   1055   // If both the malware kill-swtich is on and the URL is on the csd whitelist,
   1056   // we will leave pre-classification checks early.
   1057   GURL url("http://host5.com/kill-switch-and-whitelisted");
   1058   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kTrue, &kTrue, NULL,
   1059                                 NULL, NULL, NULL);
   1060   NavigateAndCommit(url);
   1061   WaitAndCheckPreClassificationChecks();
   1062 
   1063   ExpectStartPhishingDetection(NULL);
   1064   ExpectShouldClassifyForMalwareResult(false);
   1065 }
   1066 
   1067 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckInvalidCache) {
   1068   // If item is in the cache but it isn't valid, we will classify regardless
   1069   // of whether we are over the reporting limit.
   1070   GURL url("http://host6.com/");
   1071   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
   1072                                 &kFalse, &kTrue, NULL, &kFalse);
   1073 
   1074   NavigateAndCommit(url);
   1075   WaitAndCheckPreClassificationChecks();
   1076 
   1077   ExpectStartPhishingDetection(&url);
   1078   ExpectShouldClassifyForMalwareResult(true);
   1079 }
   1080 
   1081 TEST_F(ClientSideDetectionHostTest,
   1082        TestPreClassificationCheckOverPhishingReportingLimit) {
   1083   // If the url isn't in the cache and we are over the reporting limit, we
   1084   // don't do classification.
   1085   GURL url("http://host7.com/");
   1086   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
   1087                                 &kFalse, &kFalse, &kTrue, &kFalse);
   1088   NavigateAndCommit(url);
   1089   WaitAndCheckPreClassificationChecks();
   1090 
   1091   ExpectStartPhishingDetection(NULL);
   1092   ExpectShouldClassifyForMalwareResult(true);
   1093 }
   1094 
   1095 TEST_F(ClientSideDetectionHostTest,
   1096        TestPreClassificationCheckOverMalwareReportingLimit) {
   1097   GURL url("http://host.com/");
   1098   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
   1099                                 &kFalse, &kFalse, &kFalse, &kTrue);
   1100   NavigateAndCommit(url);
   1101   WaitAndCheckPreClassificationChecks();
   1102 
   1103   ExpectStartPhishingDetection(&url);
   1104   ExpectShouldClassifyForMalwareResult(false);
   1105 }
   1106 
   1107 TEST_F(ClientSideDetectionHostTest,
   1108        TestPreClassificationCheckOverBothReportingLimits) {
   1109   GURL url("http://host.com/");
   1110   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
   1111                                 &kFalse, &kFalse, &kTrue, &kTrue);
   1112   NavigateAndCommit(url);
   1113   WaitAndCheckPreClassificationChecks();
   1114 
   1115   ExpectStartPhishingDetection(NULL);
   1116   ExpectShouldClassifyForMalwareResult(false);
   1117 }
   1118 
   1119 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckHttpsUrl) {
   1120   GURL url("https://host.com/");
   1121   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
   1122                                 &kFalse, &kFalse, &kFalse, &kFalse);
   1123   NavigateAndCommit(url);
   1124   WaitAndCheckPreClassificationChecks();
   1125 
   1126   ExpectStartPhishingDetection(NULL);
   1127   ExpectShouldClassifyForMalwareResult(true);
   1128 }
   1129 
   1130 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckValidCached) {
   1131   // If result is cached, we will try and display the blocking page directly
   1132   // with no start classification message.
   1133   GURL url("http://host8.com/");
   1134   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, &kTrue,
   1135                                 &kFalse, &kFalse, &kFalse);
   1136 
   1137   UnsafeResource resource;
   1138   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
   1139       .WillOnce(SaveArg<0>(&resource));
   1140 
   1141   NavigateAndCommit(url);
   1142   WaitAndCheckPreClassificationChecks();
   1143   EXPECT_EQ(url, resource.url);
   1144   EXPECT_EQ(url, resource.original_url);
   1145 
   1146   ExpectStartPhishingDetection(NULL);
   1147 
   1148   // Showing a phishing warning will invalidate all the weak pointers which
   1149   // means we will not extract malware features.
   1150   ExpectShouldClassifyForMalwareResult(false);
   1151 }
   1152 }  // namespace safe_browsing
   1153