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