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 27 using ::testing::_; 28 using ::testing::DeleteArg; 29 using ::testing::DoAll; 30 using ::testing::Eq; 31 using ::testing::Mock; 32 using ::testing::NiceMock; 33 using ::testing::NotNull; 34 using ::testing::Pointee; 35 using ::testing::Return; 36 using ::testing::SaveArg; 37 using ::testing::SetArgumentPointee; 38 using ::testing::StrictMock; 39 40 namespace { 41 const bool kFalse = false; 42 const bool kTrue = true; 43 } 44 45 namespace safe_browsing { 46 47 MATCHER_P(EqualsProto, other, "") { 48 return other.SerializeAsString() == arg.SerializeAsString(); 49 } 50 51 class 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 69 class 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 93 class 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. 102 void QuitUIMessageLoop() { 103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 104 BrowserThread::PostTask(BrowserThread::UI, 105 FROM_HERE, 106 new MessageLoop::QuitTask()); 107 } 108 109 class 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 219 TEST_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 227 TEST_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 258 TEST_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 284 TEST_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 337 TEST_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 417 TEST_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 436 TEST_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