client_side_detection_host_unittest.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
1// Copyright (c) 2011 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/command_line.h"
6#include "base/file_path.h"
7#include "base/memory/ref_counted.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/memory/scoped_temp_dir.h"
10#include "base/task.h"
11#include "chrome/browser/safe_browsing/client_side_detection_host.h"
12#include "chrome/browser/safe_browsing/client_side_detection_service.h"
13#include "chrome/browser/safe_browsing/safe_browsing_service.h"
14#include "chrome/common/chrome_switches.h"
15#include "chrome/common/safe_browsing/csd.pb.h"
16#include "chrome/common/safe_browsing/safebrowsing_messages.h"
17#include "chrome/test/testing_profile.h"
18#include "chrome/test/ui_test_utils.h"
19#include "content/browser/browser_thread.h"
20#include "content/browser/renderer_host/test_render_view_host.h"
21#include "content/browser/tab_contents/test_tab_contents.h"
22#include "googleurl/src/gurl.h"
23#include "ipc/ipc_test_sink.h"
24#include "testing/gmock/include/gmock/gmock.h"
25#include "testing/gtest/include/gtest/gtest.h"
26
27using ::testing::_;
28using ::testing::DeleteArg;
29using ::testing::DoAll;
30using ::testing::Eq;
31using ::testing::Mock;
32using ::testing::NiceMock;
33using ::testing::NotNull;
34using ::testing::Pointee;
35using ::testing::Return;
36using ::testing::SaveArg;
37using ::testing::SetArgumentPointee;
38using ::testing::StrictMock;
39
40namespace {
41const bool kFalse = false;
42const bool kTrue = true;
43}
44
45namespace safe_browsing {
46
47MATCHER_P(EqualsProto, other, "") {
48  return other.SerializeAsString() == arg.SerializeAsString();
49}
50
51class MockClientSideDetectionService : public ClientSideDetectionService {
52 public:
53  explicit MockClientSideDetectionService(const FilePath& model_path)
54      : ClientSideDetectionService(model_path, NULL) {}
55  virtual ~MockClientSideDetectionService() {};
56
57  MOCK_METHOD2(SendClientReportPhishingRequest,
58               void(ClientPhishingRequest*,
59                    ClientReportPhishingRequestCallback*));
60  MOCK_CONST_METHOD1(IsPrivateIPAddress, bool(const std::string&));
61  MOCK_METHOD2(GetValidCachedResult, bool(const GURL&, bool*));
62  MOCK_METHOD1(IsInCache, bool(const GURL&));
63  MOCK_METHOD0(OverReportLimit, bool());
64
65 private:
66  DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService);
67};
68
69class MockSafeBrowsingService : public SafeBrowsingService {
70 public:
71  MockSafeBrowsingService() {}
72  virtual ~MockSafeBrowsingService() {}
73
74  MOCK_METHOD8(DisplayBlockingPage,
75               void(const GURL&, const GURL&, const std::vector<GURL>&,
76                    ResourceType::Type, UrlCheckResult, Client*, int, int));
77  MOCK_METHOD1(MatchCsdWhitelistUrl, bool(const GURL&));
78
79  // Helper function which calls OnBlockingPageComplete for this client
80  // object.
81  void InvokeOnBlockingPageComplete(SafeBrowsingService::Client* client) {
82    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
83    DCHECK(client);
84    // Note: this will delete the client object in the case of the CsdClient
85    // implementation.
86    client->OnBlockingPageComplete(false);
87  }
88
89 private:
90  DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingService);
91};
92
93class MockTestingProfile : public TestingProfile {
94 public:
95  MockTestingProfile() {}
96  virtual ~MockTestingProfile() {}
97
98  MOCK_METHOD0(IsOffTheRecord, bool());
99};
100
101// Helper function which quits the UI message loop from the IO message loop.
102void QuitUIMessageLoop() {
103  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
104  BrowserThread::PostTask(BrowserThread::UI,
105                          FROM_HERE,
106                          new MessageLoop::QuitTask());
107}
108
109class ClientSideDetectionHostTest : public RenderViewHostTestHarness {
110 public:
111  virtual void SetUp() {
112    // Set custom profile object so that we can mock calls to IsOffTheRecord.
113    // This needs to happen before we call the parent SetUp() function.  We use
114    // a nice mock because other parts of the code are calling IsOffTheRecord.
115    mock_profile_ = new NiceMock<MockTestingProfile>();
116    profile_.reset(mock_profile_);
117
118    RenderViewHostTestHarness::SetUp();
119    ui_thread_.reset(new BrowserThread(BrowserThread::UI, &message_loop_));
120    // Note: we're starting a real IO thread to make sure our DCHECKs that
121    // verify which thread is running are actually tested.
122    io_thread_.reset(new BrowserThread(BrowserThread::IO));
123    ASSERT_TRUE(io_thread_->Start());
124
125    // Inject service classes.
126    ScopedTempDir tmp_dir;
127    ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
128    FilePath model_path = tmp_dir.path().AppendASCII("model");
129
130    csd_service_.reset(new StrictMock<MockClientSideDetectionService>(
131        model_path));
132    sb_service_ = new StrictMock<MockSafeBrowsingService>();
133    csd_host_ = contents()->safebrowsing_detection_host();
134    csd_host_->set_client_side_detection_service(csd_service_.get());
135    csd_host_->set_safe_browsing_service(sb_service_.get());
136
137    // Save command-line so that it can be restored for every test.
138    original_cmd_line_.reset(
139        new CommandLine(*CommandLine::ForCurrentProcess()));
140  }
141
142  virtual void TearDown() {
143    io_thread_.reset();
144    ui_thread_.reset();
145    RenderViewHostTestHarness::TearDown();
146    // Restore the command-line like it was before we ran the test.
147    *CommandLine::ForCurrentProcess() = *original_cmd_line_;
148  }
149
150  void OnDetectedPhishingSite(const std::string& verdict_str) {
151    csd_host_->OnDetectedPhishingSite(verdict_str);
152  }
153
154  void FlushIOMessageLoop() {
155    // If there was a message posted on the IO thread to display the
156    // interstitial page we know that it would have been posted before
157    // we put the quit message there.
158    BrowserThread::PostTask(BrowserThread::IO,
159                            FROM_HERE,
160                            NewRunnableFunction(&QuitUIMessageLoop));
161    MessageLoop::current()->Run();
162  }
163
164  void ExpectPreClassificationChecks(const GURL& url,
165                                     const bool* is_private,
166                                     const bool* is_incognito,
167                                     const bool* match_csd_whitelist,
168                                     const bool* get_valid_cached_result,
169                                     const bool* is_in_cache,
170                                     const bool* over_report_limit) {
171    if (is_private) {
172      EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_))
173          .WillOnce(Return(*is_private));
174    }
175    if (is_incognito) {
176      EXPECT_CALL(*mock_profile_, IsOffTheRecord())
177          .WillRepeatedly(Return(*is_incognito));
178    }
179    if (match_csd_whitelist) {
180      EXPECT_CALL(*sb_service_, MatchCsdWhitelistUrl(url))
181          .WillOnce(Return(*match_csd_whitelist));
182    }
183    if (get_valid_cached_result) {
184      EXPECT_CALL(*csd_service_, GetValidCachedResult(url, NotNull()))
185          .WillOnce(DoAll(SetArgumentPointee<1>(true),
186                          Return(*get_valid_cached_result)));
187    }
188    if (is_in_cache) {
189      EXPECT_CALL(*csd_service_, IsInCache(url)).WillOnce(Return(*is_in_cache));
190    }
191    if (over_report_limit) {
192      EXPECT_CALL(*csd_service_, OverReportLimit())
193          .WillOnce(Return(*over_report_limit));
194    }
195  }
196
197  void WaitAndCheckPreClassificationChecks() {
198    // Wait for CheckCsdWhitelist to be called if at all.
199    FlushIOMessageLoop();
200    // Checks for CheckCache() to be called if at all.
201    MessageLoop::current()->RunAllPending();
202    EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
203    EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get()));
204    EXPECT_TRUE(Mock::VerifyAndClear(mock_profile_));
205  }
206
207 protected:
208  ClientSideDetectionHost* csd_host_;
209  scoped_ptr<StrictMock<MockClientSideDetectionService> > csd_service_;
210  scoped_refptr<StrictMock<MockSafeBrowsingService> > sb_service_;
211  MockTestingProfile* mock_profile_;  // We don't own this object
212
213 private:
214  scoped_ptr<BrowserThread> ui_thread_;
215  scoped_ptr<BrowserThread> io_thread_;
216  scoped_ptr<CommandLine> original_cmd_line_;
217};
218
219TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteInvalidVerdict) {
220  // Case 0: renderer sends an invalid verdict string that we're unable to
221  // parse.
222  EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(_, _)).Times(0);
223  OnDetectedPhishingSite("Invalid Protocol Buffer");
224  EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
225}
226
227TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteNotPhishing) {
228  // Case 1: client thinks the page is phishing.  The server does not agree.
229  // No interstitial is shown.
230  ClientSideDetectionService::ClientReportPhishingRequestCallback* cb;
231  ClientPhishingRequest verdict;
232  verdict.set_url("http://phishingurl.com/");
233  verdict.set_client_score(1.0f);
234  verdict.set_is_phishing(true);
235
236  EXPECT_CALL(*csd_service_,
237              SendClientReportPhishingRequest(Pointee(EqualsProto(verdict)), _))
238      .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
239  OnDetectedPhishingSite(verdict.SerializeAsString());
240  EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
241  ASSERT_TRUE(cb);
242
243  // Make sure DisplayBlockingPage is not going to be called.
244  EXPECT_CALL(*sb_service_,
245              DisplayBlockingPage(_, _, _, _, _, _, _, _)).Times(0);
246  cb->Run(GURL(verdict.url()), false);
247  delete cb;
248  // If there was a message posted on the IO thread to display the
249  // interstitial page we know that it would have been posted before
250  // we put the quit message there.
251  BrowserThread::PostTask(BrowserThread::IO,
252                          FROM_HERE,
253                          NewRunnableFunction(&QuitUIMessageLoop));
254  MessageLoop::current()->Run();
255  EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get()));
256}
257
258TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteDisabled) {
259  // Case 2: client thinks the page is phishing and so does the server but
260  // showing the interstitial is disabled => no interstitial is shown.
261  ClientSideDetectionService::ClientReportPhishingRequestCallback* cb;
262  ClientPhishingRequest verdict;
263  verdict.set_url("http://phishingurl.com/");
264  verdict.set_client_score(1.0f);
265  verdict.set_is_phishing(true);
266
267  EXPECT_CALL(*csd_service_,
268              SendClientReportPhishingRequest(Pointee(EqualsProto(verdict)), _))
269      .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
270  OnDetectedPhishingSite(verdict.SerializeAsString());
271  EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
272  ASSERT_TRUE(cb);
273
274  // Make sure DisplayBlockingPage is not going to be called.
275  EXPECT_CALL(*sb_service_,
276              DisplayBlockingPage(_, _, _, _, _, _, _, _)).Times(0);
277  cb->Run(GURL(verdict.url()), false);
278  delete cb;
279
280  FlushIOMessageLoop();
281  EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get()));
282}
283
284TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteShowInterstitial) {
285  // Case 3: client thinks the page is phishing and so does the server.
286  // We show an interstitial.
287  ClientSideDetectionService::ClientReportPhishingRequestCallback* cb;
288  GURL phishing_url("http://phishingurl.com/");
289  ClientPhishingRequest verdict;
290  verdict.set_url(phishing_url.spec());
291  verdict.set_client_score(1.0f);
292  verdict.set_is_phishing(true);
293
294  CommandLine::ForCurrentProcess()->AppendSwitch(
295      switches::kEnableClientSidePhishingInterstitial);
296
297  EXPECT_CALL(*csd_service_,
298              SendClientReportPhishingRequest(Pointee(EqualsProto(verdict)), _))
299      .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
300  OnDetectedPhishingSite(verdict.SerializeAsString());
301  EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
302  ASSERT_TRUE(cb);
303
304  SafeBrowsingService::Client* client;
305  EXPECT_CALL(*sb_service_,
306              DisplayBlockingPage(
307                  phishing_url,
308                  phishing_url,
309                  _,
310                  ResourceType::MAIN_FRAME,
311                  SafeBrowsingService::URL_PHISHING,
312                  _ /* a CsdClient object */,
313                  contents()->GetRenderProcessHost()->id(),
314                  contents()->render_view_host()->routing_id()))
315      .WillOnce(SaveArg<5>(&client));
316
317  cb->Run(phishing_url, true);
318  delete cb;
319
320  FlushIOMessageLoop();
321  EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get()));
322
323  // Make sure the client object will be deleted.
324  BrowserThread::PostTask(
325      BrowserThread::IO,
326      FROM_HERE,
327      NewRunnableMethod(
328          sb_service_.get(),
329          &MockSafeBrowsingService::InvokeOnBlockingPageComplete,
330          client));
331  // Since the CsdClient object will be deleted on the UI thread I need
332  // to run the UI message loop.  Post a task to stop the UI message loop
333  // after the client object destructor is called.
334  FlushIOMessageLoop();
335}
336
337TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteMultiplePings) {
338  // Case 4 & 5: client thinks a page is phishing then navigates to
339  // another page which is also considered phishing by the client
340  // before the server responds with a verdict.  After a while the
341  // server responds for both requests with a phishing verdict.  Only
342  // a single interstitial is shown for the second URL.
343  ClientSideDetectionService::ClientReportPhishingRequestCallback* cb;
344  GURL phishing_url("http://phishingurl.com/");
345  ClientPhishingRequest verdict;
346  verdict.set_url(phishing_url.spec());
347  verdict.set_client_score(1.0f);
348  verdict.set_is_phishing(true);
349
350  CommandLine::ForCurrentProcess()->AppendSwitch(
351      switches::kEnableClientSidePhishingInterstitial);
352
353  EXPECT_CALL(*csd_service_,
354              SendClientReportPhishingRequest(Pointee(EqualsProto(verdict)), _))
355      .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
356  OnDetectedPhishingSite(verdict.SerializeAsString());
357  EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
358  ASSERT_TRUE(cb);
359  GURL other_phishing_url("http://other_phishing_url.com/bla");
360  ExpectPreClassificationChecks(other_phishing_url, &kFalse, &kFalse, &kFalse,
361                                &kFalse, &kFalse, &kFalse);
362  // We navigate away.  The callback cb should be revoked.
363  NavigateAndCommit(other_phishing_url);
364  // Wait for the pre-classification checks to finish for other_phishing_url.
365  WaitAndCheckPreClassificationChecks();
366
367  ClientSideDetectionService::ClientReportPhishingRequestCallback* cb_other;
368  verdict.set_url(other_phishing_url.spec());
369  verdict.set_client_score(0.8f);
370  EXPECT_CALL(*csd_service_,
371              SendClientReportPhishingRequest(Pointee(EqualsProto(verdict)), _))
372      .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb_other)));
373  OnDetectedPhishingSite(verdict.SerializeAsString());
374  EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
375  ASSERT_TRUE(cb_other);
376
377  // We expect that the interstitial is shown for the second phishing URL and
378  // not for the first phishing URL.
379  EXPECT_CALL(*sb_service_,
380              DisplayBlockingPage(phishing_url, phishing_url,_, _, _, _, _, _))
381      .Times(0);
382  SafeBrowsingService::Client* client;
383  EXPECT_CALL(*sb_service_,
384              DisplayBlockingPage(
385                  other_phishing_url,
386                  other_phishing_url,
387                  _,
388                  ResourceType::MAIN_FRAME,
389                  SafeBrowsingService::URL_PHISHING,
390                  _ /* a CsdClient object */,
391                  contents()->GetRenderProcessHost()->id(),
392                  contents()->render_view_host()->routing_id()))
393      .WillOnce(SaveArg<5>(&client));
394
395  cb->Run(phishing_url, true);  // Should have no effect.
396  delete cb;
397  cb_other->Run(other_phishing_url, true);  // Should show interstitial.
398  delete cb_other;
399
400  FlushIOMessageLoop();
401  EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get()));
402
403  // Make sure the client object will be deleted.
404  BrowserThread::PostTask(
405      BrowserThread::IO,
406      FROM_HERE,
407      NewRunnableMethod(
408          sb_service_.get(),
409          &MockSafeBrowsingService::InvokeOnBlockingPageComplete,
410          client));
411  // Since the CsdClient object will be deleted on the UI thread I need
412  // to run the UI message loop.  Post a task to stop the UI message loop
413  // after the client object destructor is called.
414  FlushIOMessageLoop();
415}
416
417TEST_F(ClientSideDetectionHostTest, NavigationCancelsShouldClassifyUrl) {
418  // Test that canceling pending should classify requests works as expected.
419
420  GURL first_url("http://first.phishy.url.com");
421  // The proxy checks is done synchronously so check that it has been done
422  // for the first URL.
423  ExpectPreClassificationChecks(first_url, &kFalse, &kFalse, &kFalse, NULL,
424                                NULL, NULL);
425  NavigateAndCommit(first_url);
426
427  // Don't flush the message loop, as we want to navigate to a different
428  // url before the final pre-classification checks are run.
429  GURL second_url("http://second.url.com/");
430  ExpectPreClassificationChecks(second_url, &kFalse, &kFalse, &kFalse, &kFalse,
431                                &kFalse, &kFalse);
432  NavigateAndCommit(second_url);
433  WaitAndCheckPreClassificationChecks();
434}
435
436TEST_F(ClientSideDetectionHostTest, ShouldClassifyUrl) {
437  // Navigate the tab to a page.  We should see a StartPhishingDetection IPC.
438  GURL url("http://host.com/");
439  ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
440                                &kFalse, &kFalse);
441  NavigateAndCommit(url);
442  WaitAndCheckPreClassificationChecks();
443
444  const IPC::Message* msg = process()->sink().GetFirstMessageMatching(
445      SafeBrowsingMsg_StartPhishingDetection::ID);
446  ASSERT_TRUE(msg);
447  Tuple1<GURL> actual_url;
448  SafeBrowsingMsg_StartPhishingDetection::Read(msg, &actual_url);
449  EXPECT_EQ(url, actual_url.a);
450  EXPECT_EQ(rvh()->routing_id(), msg->routing_id());
451  process()->sink().ClearMessages();
452
453  // Now try an in-page navigation.  This should not trigger an IPC.
454  EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_)).Times(0);
455  url = GURL("http://host.com/#foo");
456  ExpectPreClassificationChecks(url, NULL, NULL, NULL, NULL, NULL, NULL);
457  NavigateAndCommit(url);
458  WaitAndCheckPreClassificationChecks();
459
460  msg = process()->sink().GetFirstMessageMatching(
461      SafeBrowsingMsg_StartPhishingDetection::ID);
462  ASSERT_FALSE(msg);
463
464  // Check that XHTML is supported, in addition to the default HTML type.
465  // Note: for this test to work correctly, the new URL must be on the
466  // same domain as the previous URL, otherwise it will create a new
467  // RenderViewHost that won't have the mime type set.
468  url = GURL("http://host.com/xhtml");
469  rvh()->set_contents_mime_type("application/xhtml+xml");
470  ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
471                                &kFalse, &kFalse);
472  NavigateAndCommit(url);
473  WaitAndCheckPreClassificationChecks();
474  msg = process()->sink().GetFirstMessageMatching(
475      SafeBrowsingMsg_StartPhishingDetection::ID);
476  ASSERT_TRUE(msg);
477  SafeBrowsingMsg_StartPhishingDetection::Read(msg, &actual_url);
478  EXPECT_EQ(url, actual_url.a);
479  EXPECT_EQ(rvh()->routing_id(), msg->routing_id());
480  process()->sink().ClearMessages();
481
482  // Navigate to a new host, which should cause another IPC.
483  url = GURL("http://host2.com/");
484  ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
485                                &kFalse, &kFalse);
486  NavigateAndCommit(url);
487  WaitAndCheckPreClassificationChecks();
488  msg = process()->sink().GetFirstMessageMatching(
489      SafeBrowsingMsg_StartPhishingDetection::ID);
490  ASSERT_TRUE(msg);
491  SafeBrowsingMsg_StartPhishingDetection::Read(msg, &actual_url);
492  EXPECT_EQ(url, actual_url.a);
493  EXPECT_EQ(rvh()->routing_id(), msg->routing_id());
494  process()->sink().ClearMessages();
495
496  // If the mime type is not one that we support, no IPC should be triggered.
497  // Note: for this test to work correctly, the new URL must be on the
498  // same domain as the previous URL, otherwise it will create a new
499  // RenderViewHost that won't have the mime type set.
500  url = GURL("http://host2.com/image.jpg");
501  rvh()->set_contents_mime_type("image/jpeg");
502  ExpectPreClassificationChecks(url, NULL, NULL, NULL, NULL, NULL, NULL);
503  NavigateAndCommit(url);
504  WaitAndCheckPreClassificationChecks();
505  msg = process()->sink().GetFirstMessageMatching(
506      SafeBrowsingMsg_StartPhishingDetection::ID);
507  ASSERT_FALSE(msg);
508
509  // If IsPrivateIPAddress returns true, no IPC should be triggered.
510  url = GURL("http://host3.com/");
511  ExpectPreClassificationChecks(url, &kTrue, NULL, NULL, NULL, NULL, NULL);
512  NavigateAndCommit(url);
513  WaitAndCheckPreClassificationChecks();
514  msg = process()->sink().GetFirstMessageMatching(
515      SafeBrowsingMsg_StartPhishingDetection::ID);
516  ASSERT_FALSE(msg);
517
518  // If the connection is proxied, no IPC should be triggered.
519  // Note: for this test to work correctly, the new URL must be on the
520  // same domain as the previous URL, otherwise it will create a new
521  // RenderViewHost that won't have simulate_fetch_via_proxy set.
522  url = GURL("http://host3.com/abc");
523  rvh()->set_simulate_fetch_via_proxy(true);
524  ExpectPreClassificationChecks(url, NULL, NULL, NULL, NULL, NULL, NULL);
525  NavigateAndCommit(url);
526  WaitAndCheckPreClassificationChecks();
527  msg = process()->sink().GetFirstMessageMatching(
528      SafeBrowsingMsg_StartPhishingDetection::ID);
529  ASSERT_FALSE(msg);
530
531  // If the tab is incognito there should be no IPC.  Also, we shouldn't
532  // even check the csd-whitelist.
533  url = GURL("http://host4.com/");
534  ExpectPreClassificationChecks(url, &kFalse, &kTrue, NULL, NULL, NULL, NULL);
535  NavigateAndCommit(url);
536  WaitAndCheckPreClassificationChecks();
537  msg = process()->sink().GetFirstMessageMatching(
538      SafeBrowsingMsg_StartPhishingDetection::ID);
539  ASSERT_FALSE(msg);
540
541  // If the URL is on the csd whitelist, no IPC should be triggered.
542  url = GURL("http://host5.com/");
543  ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kTrue, NULL, NULL,
544                                NULL);
545  NavigateAndCommit(url);
546  WaitAndCheckPreClassificationChecks();
547  msg = process()->sink().GetFirstMessageMatching(
548      SafeBrowsingMsg_StartPhishingDetection::ID);
549  ASSERT_FALSE(msg);
550
551  // If item is in the cache but it isn't valid, we will classify regardless
552  // of whether we are over the reporting limit.
553  url = GURL("http://host6.com/");
554  ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, &kTrue,
555                                NULL);
556  NavigateAndCommit(url);
557  WaitAndCheckPreClassificationChecks();
558  msg = process()->sink().GetFirstMessageMatching(
559      SafeBrowsingMsg_StartPhishingDetection::ID);
560  ASSERT_TRUE(msg);
561  SafeBrowsingMsg_StartPhishingDetection::Read(msg, &actual_url);
562  EXPECT_EQ(url, actual_url.a);
563  EXPECT_EQ(rvh()->routing_id(), msg->routing_id());
564  process()->sink().ClearMessages();
565
566  // If the url isn't in the cache and we are over the reporting limit, we
567  // don't do classification.
568  url = GURL("http://host7.com/");
569  ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
570                                &kFalse, &kTrue);
571  NavigateAndCommit(url);
572  WaitAndCheckPreClassificationChecks();
573  msg = process()->sink().GetFirstMessageMatching(
574      SafeBrowsingMsg_StartPhishingDetection::ID);
575  ASSERT_FALSE(msg);
576
577  // If result is cached, we will try and display the blocking page directly
578  // with no start classification message.
579  CommandLine::ForCurrentProcess()->AppendSwitch(
580      switches::kEnableClientSidePhishingInterstitial);
581  url = GURL("http://host8.com/");
582  ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kTrue, NULL,
583                                NULL);
584  EXPECT_CALL(*sb_service_,
585              DisplayBlockingPage(Eq(url), Eq(url), _, _, _, _, _, _))
586      .WillOnce(DeleteArg<5>());
587  NavigateAndCommit(url);
588  // Wait for CheckCsdWhitelist to be called on the IO thread.
589  FlushIOMessageLoop();
590  // Wait for CheckCache() to be called on the UI thread.
591  MessageLoop::current()->RunAllPending();
592  // Wait for DisplayBlockingPage to be called on the IO thread.
593  FlushIOMessageLoop();
594  // Now we check that all expected functions were indeed called on the two
595  // service objects.
596  EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
597  EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get()));
598  msg = process()->sink().GetFirstMessageMatching(
599      SafeBrowsingMsg_StartPhishingDetection::ID);
600  ASSERT_FALSE(msg);
601}
602
603}  // namespace safe_browsing
604