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 "chrome/renderer/safe_browsing/phishing_classifier_delegate.h" 6 7 #include "base/command_line.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/run_loop.h" 10 #include "base/strings/string_number_conversions.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "chrome/browser/ui/browser.h" 13 #include "chrome/browser/ui/browser_commands.h" 14 #include "chrome/browser/ui/tabs/tab_strip_model.h" 15 #include "chrome/common/chrome_switches.h" 16 #include "chrome/common/safe_browsing/csd.pb.h" 17 #include "chrome/common/safe_browsing/safebrowsing_messages.h" 18 #include "chrome/renderer/safe_browsing/features.h" 19 #include "chrome/renderer/safe_browsing/phishing_classifier.h" 20 #include "chrome/renderer/safe_browsing/scorer.h" 21 #include "chrome/test/base/in_process_browser_test.h" 22 #include "chrome/test/base/ui_test_utils.h" 23 #include "content/public/browser/browser_message_filter.h" 24 #include "content/public/browser/browser_thread.h" 25 #include "content/public/browser/render_process_host.h" 26 #include "content/public/browser/web_contents.h" 27 #include "content/public/renderer/render_view.h" 28 #include "content/public/test/browser_test_utils.h" 29 #include "content/public/test/test_navigation_observer.h" 30 #include "content/public/test/test_utils.h" 31 #include "net/dns/mock_host_resolver.h" 32 #include "net/test/embedded_test_server/embedded_test_server.h" 33 #include "net/test/embedded_test_server/http_request.h" 34 #include "net/test/embedded_test_server/http_response.h" 35 #include "testing/gmock/include/gmock/gmock.h" 36 #include "third_party/WebKit/public/platform/WebURL.h" 37 #include "third_party/WebKit/public/platform/WebURLRequest.h" 38 #include "third_party/WebKit/public/web/WebFrame.h" 39 #include "third_party/WebKit/public/web/WebView.h" 40 #include "url/gurl.h" 41 42 using base::ASCIIToUTF16; 43 using ::testing::_; 44 using ::testing::InSequence; 45 using ::testing::Mock; 46 using ::testing::Pointee; 47 using ::testing::StrictMock; 48 49 namespace safe_browsing { 50 51 namespace { 52 53 // The RenderFrame is routing ID 1, and the RenderView is 2. 54 const int kRenderViewRoutingId = 2; 55 56 class MockPhishingClassifier : public PhishingClassifier { 57 public: 58 explicit MockPhishingClassifier(content::RenderView* render_view) 59 : PhishingClassifier(render_view, NULL /* clock */) {} 60 61 virtual ~MockPhishingClassifier() {} 62 63 MOCK_METHOD2(BeginClassification, 64 void(const base::string16*, const DoneCallback&)); 65 MOCK_METHOD0(CancelPendingClassification, void()); 66 67 private: 68 DISALLOW_COPY_AND_ASSIGN(MockPhishingClassifier); 69 }; 70 71 class MockScorer : public Scorer { 72 public: 73 MockScorer() : Scorer() {} 74 virtual ~MockScorer() {} 75 76 MOCK_CONST_METHOD1(ComputeScore, double(const FeatureMap&)); 77 78 private: 79 DISALLOW_COPY_AND_ASSIGN(MockScorer); 80 }; 81 82 class InterceptingMessageFilter : public content::BrowserMessageFilter { 83 public: 84 InterceptingMessageFilter() 85 : BrowserMessageFilter(SafeBrowsingMsgStart), 86 waiting_message_loop_(NULL) { 87 } 88 89 const ClientPhishingRequest* verdict() const { return verdict_.get(); } 90 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 91 bool handled = true; 92 IPC_BEGIN_MESSAGE_MAP(InterceptingMessageFilter, message) 93 IPC_MESSAGE_HANDLER(SafeBrowsingHostMsg_PhishingDetectionDone, 94 OnPhishingDetectionDone) 95 IPC_MESSAGE_UNHANDLED(handled = false); 96 IPC_END_MESSAGE_MAP() 97 return handled; 98 } 99 100 void Reset() { 101 run_loop_.reset(new base::RunLoop()); 102 waiting_message_loop_ = base::MessageLoop::current(); 103 quit_closure_ = run_loop_->QuitClosure(); 104 } 105 106 void RunUntilVerdictReceived() { 107 content::RunThisRunLoop(run_loop_.get()); 108 109 // Clear out the synchronization state just in case. 110 waiting_message_loop_ = NULL; 111 quit_closure_.Reset(); 112 run_loop_.reset(); 113 } 114 115 void OnPhishingDetectionDone(const std::string& verdict_str) { 116 scoped_ptr<ClientPhishingRequest> verdict(new ClientPhishingRequest); 117 if (verdict->ParseFromString(verdict_str) && 118 verdict->IsInitialized()) { 119 verdict_.swap(verdict); 120 } 121 waiting_message_loop_->PostTask(FROM_HERE, quit_closure_); 122 } 123 124 private: 125 virtual ~InterceptingMessageFilter() {} 126 127 scoped_ptr<ClientPhishingRequest> verdict_; 128 base::MessageLoop* waiting_message_loop_; 129 base::Closure quit_closure_; 130 scoped_ptr<base::RunLoop> run_loop_; 131 }; 132 } // namespace 133 134 class PhishingClassifierDelegateTest : public InProcessBrowserTest { 135 public: 136 void CancelCalled() { 137 if (runner_.get()) { 138 content::BrowserThread::PostTask( 139 content::BrowserThread::UI, FROM_HERE, runner_->QuitClosure()); 140 } 141 } 142 143 protected: 144 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 145 command_line->AppendSwitch(switches::kSingleProcess); 146 #if defined(OS_WIN) 147 // Don't want to try to create a GPU process. 148 command_line->AppendSwitch(switches::kDisableGpu); 149 #endif 150 } 151 152 virtual void SetUpOnMainThread() OVERRIDE { 153 intercepting_filter_ = new InterceptingMessageFilter(); 154 content::RenderView* render_view = 155 content::RenderView::FromRoutingID(kRenderViewRoutingId); 156 157 GetWebContents()->GetRenderProcessHost()->AddFilter( 158 intercepting_filter_.get()); 159 classifier_ = new StrictMock<MockPhishingClassifier>(render_view); 160 delegate_ = PhishingClassifierDelegate::Create(render_view, classifier_); 161 162 ASSERT_TRUE(StartTestServer()); 163 host_resolver()->AddRule("*", "127.0.0.1"); 164 } 165 166 // Runs the ClassificationDone callback, then waits for the 167 // PhishingDetectionDone IPC to arrive. 168 void RunClassificationDone(const ClientPhishingRequest& verdict) { 169 // Clear out any previous state. 170 intercepting_filter_->Reset(); 171 PostTaskToInProcessRendererAndWait( 172 base::Bind(&PhishingClassifierDelegate::ClassificationDone, 173 base::Unretained(delegate_), 174 verdict)); 175 intercepting_filter_->RunUntilVerdictReceived(); 176 } 177 178 void OnStartPhishingDetection(const GURL& url) { 179 PostTaskToInProcessRendererAndWait( 180 base::Bind(&PhishingClassifierDelegate::OnStartPhishingDetection, 181 base::Unretained(delegate_), url)); 182 } 183 184 void PageCaptured(base::string16* page_text, bool preliminary_capture) { 185 PostTaskToInProcessRendererAndWait( 186 base::Bind(&PhishingClassifierDelegate::PageCaptured, 187 base::Unretained(delegate_), page_text, 188 preliminary_capture)); 189 } 190 191 bool StartTestServer() { 192 CHECK(!embedded_test_server_); 193 embedded_test_server_.reset(new net::test_server::EmbeddedTestServer()); 194 embedded_test_server_->RegisterRequestHandler( 195 base::Bind(&PhishingClassifierDelegateTest::HandleRequest, 196 base::Unretained(this))); 197 return embedded_test_server_->InitializeAndWaitUntilReady(); 198 } 199 200 scoped_ptr<net::test_server::HttpResponse> HandleRequest( 201 const net::test_server::HttpRequest& request) { 202 std::map<std::string, std::string>::const_iterator host_it = 203 request.headers.find("Host"); 204 if (host_it == request.headers.end()) 205 return scoped_ptr<net::test_server::HttpResponse>(); 206 207 std::string url = 208 std::string("http://") + host_it->second + request.relative_url; 209 if (response_url_.spec() != url) 210 return scoped_ptr<net::test_server::HttpResponse>(); 211 212 scoped_ptr<net::test_server::BasicHttpResponse> http_response( 213 new net::test_server::BasicHttpResponse()); 214 http_response->set_code(net::HTTP_OK); 215 http_response->set_content_type("text/html"); 216 http_response->set_content(response_content_); 217 return http_response.PassAs<net::test_server::HttpResponse>(); 218 } 219 220 content::WebContents* GetWebContents() { 221 return browser()->tab_strip_model()->GetActiveWebContents(); 222 } 223 224 // Returns the URL that was loaded. 225 GURL LoadHtml(const std::string& host, const std::string& content) { 226 GURL::Replacements replace_host; 227 replace_host.SetHostStr(host); 228 response_content_ = content; 229 response_url_ = 230 embedded_test_server_->base_url().ReplaceComponents(replace_host); 231 ui_test_utils::NavigateToURL(browser(), response_url_); 232 return response_url_; 233 } 234 235 void NavigateMainFrame(const GURL& url) { 236 PostTaskToInProcessRendererAndWait( 237 base::Bind(&PhishingClassifierDelegateTest::NavigateMainFrameInternal, 238 base::Unretained(this), url)); 239 } 240 241 void NavigateMainFrameInternal(const GURL& url) { 242 content::RenderView* render_view = 243 content::RenderView::FromRoutingID(kRenderViewRoutingId); 244 render_view->GetWebView()->mainFrame()->firstChild()->loadRequest( 245 blink::WebURLRequest(url)); 246 } 247 248 void GoBack() { 249 GetWebContents()->GetController().GoBack(); 250 content::WaitForLoadStop(GetWebContents()); 251 } 252 253 void GoForward() { 254 GetWebContents()->GetController().GoForward(); 255 content::WaitForLoadStop(GetWebContents()); 256 } 257 258 scoped_refptr<InterceptingMessageFilter> intercepting_filter_; 259 GURL response_url_; 260 std::string response_content_; 261 scoped_ptr<net::test_server::EmbeddedTestServer> embedded_test_server_; 262 scoped_ptr<ClientPhishingRequest> verdict_; 263 StrictMock<MockPhishingClassifier>* classifier_; // Owned by |delegate_|. 264 PhishingClassifierDelegate* delegate_; // Owned by the RenderView. 265 scoped_refptr<content::MessageLoopRunner> runner_; 266 }; 267 268 IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, Navigation) { 269 MockScorer scorer; 270 delegate_->SetPhishingScorer(&scorer); 271 ASSERT_TRUE(classifier_->is_ready()); 272 273 // Test an initial load. We expect classification to happen normally. 274 EXPECT_CALL(*classifier_, CancelPendingClassification()).Times(2); 275 std::string port = base::IntToString(embedded_test_server_->port()); 276 std::string html = "<html><body><iframe src=\"http://sub1.com:"; 277 html += port; 278 html += "/\"></iframe></body></html>"; 279 GURL url = LoadHtml("host.com", html); 280 Mock::VerifyAndClearExpectations(classifier_); 281 OnStartPhishingDetection(url); 282 base::string16 page_text = ASCIIToUTF16("dummy"); 283 { 284 InSequence s; 285 EXPECT_CALL(*classifier_, CancelPendingClassification()); 286 EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _)); 287 PageCaptured(&page_text, false); 288 Mock::VerifyAndClearExpectations(classifier_); 289 } 290 291 // Reloading the same page should not trigger a reclassification. 292 // However, it will cancel any pending classification since the 293 // content is being replaced. 294 EXPECT_CALL(*classifier_, CancelPendingClassification()).Times(2); 295 296 content::TestNavigationObserver observer(GetWebContents()); 297 chrome::Reload(browser(), CURRENT_TAB); 298 observer.Wait(); 299 300 Mock::VerifyAndClearExpectations(classifier_); 301 OnStartPhishingDetection(url); 302 page_text = ASCIIToUTF16("dummy"); 303 EXPECT_CALL(*classifier_, CancelPendingClassification()); 304 PageCaptured(&page_text, false); 305 Mock::VerifyAndClearExpectations(classifier_); 306 307 // Navigating in a subframe will not change the toplevel URL. However, this 308 // should cancel pending classification since the page content is changing. 309 // Currently, we do not start a new classification after subframe loads. 310 EXPECT_CALL(*classifier_, CancelPendingClassification()) 311 .WillOnce(Invoke(this, &PhishingClassifierDelegateTest::CancelCalled)); 312 313 runner_ = new content::MessageLoopRunner; 314 NavigateMainFrame(GURL(std::string("http://sub2.com:") + port + "/")); 315 316 runner_->Run(); 317 runner_ = NULL; 318 319 Mock::VerifyAndClearExpectations(classifier_); 320 321 OnStartPhishingDetection(url); 322 page_text = ASCIIToUTF16("dummy"); 323 EXPECT_CALL(*classifier_, CancelPendingClassification()); 324 PageCaptured(&page_text, false); 325 Mock::VerifyAndClearExpectations(classifier_); 326 327 // Scrolling to an anchor works similarly to a subframe navigation, but 328 // see the TODO in PhishingClassifierDelegate::DidCommitProvisionalLoad. 329 EXPECT_CALL(*classifier_, CancelPendingClassification()); 330 GURL foo_url = GURL(url.spec() + "#foo"); 331 ui_test_utils::NavigateToURL(browser(), foo_url); 332 Mock::VerifyAndClearExpectations(classifier_); 333 OnStartPhishingDetection(url); 334 page_text = ASCIIToUTF16("dummy"); 335 EXPECT_CALL(*classifier_, CancelPendingClassification()); 336 PageCaptured(&page_text, false); 337 Mock::VerifyAndClearExpectations(classifier_); 338 339 // Now load a new toplevel page, which should trigger another classification. 340 EXPECT_CALL(*classifier_, CancelPendingClassification()) 341 .WillOnce(Invoke(this, &PhishingClassifierDelegateTest::CancelCalled)); 342 343 runner_ = new content::MessageLoopRunner; 344 url = LoadHtml("host2.com", "dummy2"); 345 runner_->Run(); 346 runner_ = NULL; 347 348 Mock::VerifyAndClearExpectations(classifier_); 349 page_text = ASCIIToUTF16("dummy2"); 350 OnStartPhishingDetection(url); 351 { 352 InSequence s; 353 EXPECT_CALL(*classifier_, CancelPendingClassification()); 354 EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _)); 355 PageCaptured(&page_text, false); 356 Mock::VerifyAndClearExpectations(classifier_); 357 } 358 359 // No classification should happen on back/forward navigation. 360 // Note: in practice, the browser will not send a StartPhishingDetection IPC 361 // in this case. However, we want to make sure that the delegate behaves 362 // correctly regardless. 363 EXPECT_CALL(*classifier_, CancelPendingClassification()).Times(2); 364 GoBack(); 365 Mock::VerifyAndClearExpectations(classifier_); 366 367 page_text = ASCIIToUTF16("dummy"); 368 OnStartPhishingDetection(url); 369 EXPECT_CALL(*classifier_, CancelPendingClassification()); 370 PageCaptured(&page_text, false); 371 Mock::VerifyAndClearExpectations(classifier_); 372 373 EXPECT_CALL(*classifier_, CancelPendingClassification()); 374 GoForward(); 375 Mock::VerifyAndClearExpectations(classifier_); 376 377 page_text = ASCIIToUTF16("dummy2"); 378 OnStartPhishingDetection(url); 379 EXPECT_CALL(*classifier_, CancelPendingClassification()); 380 PageCaptured(&page_text, false); 381 Mock::VerifyAndClearExpectations(classifier_); 382 383 // Now go back again and scroll to a different anchor. 384 // No classification should happen. 385 EXPECT_CALL(*classifier_, CancelPendingClassification()).Times(2); 386 GoBack(); 387 Mock::VerifyAndClearExpectations(classifier_); 388 page_text = ASCIIToUTF16("dummy"); 389 390 OnStartPhishingDetection(url); 391 EXPECT_CALL(*classifier_, CancelPendingClassification()); 392 PageCaptured(&page_text, false); 393 Mock::VerifyAndClearExpectations(classifier_); 394 395 EXPECT_CALL(*classifier_, CancelPendingClassification()); 396 GURL foo2_url = GURL(foo_url.spec() + "2"); 397 ui_test_utils::NavigateToURL(browser(), foo2_url); 398 Mock::VerifyAndClearExpectations(classifier_); 399 400 OnStartPhishingDetection(url); 401 page_text = ASCIIToUTF16("dummy"); 402 EXPECT_CALL(*classifier_, CancelPendingClassification()); 403 PageCaptured(&page_text, false); 404 Mock::VerifyAndClearExpectations(classifier_); 405 406 // The delegate will cancel pending classification on destruction. 407 EXPECT_CALL(*classifier_, CancelPendingClassification()); 408 } 409 410 IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, NoScorer) { 411 // For this test, we'll create the delegate with no scorer available yet. 412 ASSERT_FALSE(classifier_->is_ready()); 413 414 // Queue up a pending classification, cancel it, then queue up another one. 415 GURL url = LoadHtml("host.com", "dummy"); 416 base::string16 page_text = ASCIIToUTF16("dummy"); 417 OnStartPhishingDetection(url); 418 PageCaptured(&page_text, false); 419 420 url = LoadHtml("host2.com", "dummy2"); 421 page_text = ASCIIToUTF16("dummy2"); 422 OnStartPhishingDetection(url); 423 PageCaptured(&page_text, false); 424 425 // Now set a scorer, which should cause a classifier to be created and 426 // the classification to proceed. 427 page_text = ASCIIToUTF16("dummy2"); 428 EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _)); 429 MockScorer scorer; 430 delegate_->SetPhishingScorer(&scorer); 431 Mock::VerifyAndClearExpectations(classifier_); 432 433 // If we set a new scorer while a classification is going on the 434 // classification should be cancelled. 435 EXPECT_CALL(*classifier_, CancelPendingClassification()); 436 delegate_->SetPhishingScorer(&scorer); 437 Mock::VerifyAndClearExpectations(classifier_); 438 439 // The delegate will cancel pending classification on destruction. 440 EXPECT_CALL(*classifier_, CancelPendingClassification()); 441 } 442 443 IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, NoScorer_Ref) { 444 // Similar to the last test, but navigates within the page before 445 // setting the scorer. 446 ASSERT_FALSE(classifier_->is_ready()); 447 448 // Queue up a pending classification, cancel it, then queue up another one. 449 GURL url = LoadHtml("host.com", "dummy"); 450 base::string16 page_text = ASCIIToUTF16("dummy"); 451 OnStartPhishingDetection(url); 452 PageCaptured(&page_text, false); 453 454 OnStartPhishingDetection(url); 455 page_text = ASCIIToUTF16("dummy"); 456 PageCaptured(&page_text, false); 457 458 // Now set a scorer, which should cause a classifier to be created and 459 // the classification to proceed. 460 page_text = ASCIIToUTF16("dummy"); 461 EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _)); 462 MockScorer scorer; 463 delegate_->SetPhishingScorer(&scorer); 464 Mock::VerifyAndClearExpectations(classifier_); 465 466 // The delegate will cancel pending classification on destruction. 467 EXPECT_CALL(*classifier_, CancelPendingClassification()); 468 } 469 470 IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, 471 NoStartPhishingDetection) { 472 // Tests the behavior when OnStartPhishingDetection has not yet been called 473 // when the page load finishes. 474 MockScorer scorer; 475 delegate_->SetPhishingScorer(&scorer); 476 ASSERT_TRUE(classifier_->is_ready()); 477 478 EXPECT_CALL(*classifier_, CancelPendingClassification()); 479 GURL url = LoadHtml("host.com", "<html><body>phish</body></html>"); 480 Mock::VerifyAndClearExpectations(classifier_); 481 base::string16 page_text = ASCIIToUTF16("phish"); 482 EXPECT_CALL(*classifier_, CancelPendingClassification()); 483 PageCaptured(&page_text, false); 484 Mock::VerifyAndClearExpectations(classifier_); 485 // Now simulate the StartPhishingDetection IPC. We expect classification 486 // to begin. 487 page_text = ASCIIToUTF16("phish"); 488 EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _)); 489 OnStartPhishingDetection(url); 490 Mock::VerifyAndClearExpectations(classifier_); 491 492 // Now try again, but this time we will navigate the page away before 493 // the IPC is sent. 494 EXPECT_CALL(*classifier_, CancelPendingClassification()); 495 LoadHtml("host2.com", "<html><body>phish</body></html>"); 496 Mock::VerifyAndClearExpectations(classifier_); 497 page_text = ASCIIToUTF16("phish"); 498 EXPECT_CALL(*classifier_, CancelPendingClassification()); 499 PageCaptured(&page_text, false); 500 Mock::VerifyAndClearExpectations(classifier_); 501 502 EXPECT_CALL(*classifier_, CancelPendingClassification()); 503 LoadHtml("host3.com", "<html><body>phish</body></html>"); 504 Mock::VerifyAndClearExpectations(classifier_); 505 OnStartPhishingDetection(url); 506 507 // In this test, the original page is a redirect, which we do not get a 508 // StartPhishingDetection IPC for. We use location.replace() to load a 509 // new page while reusing the original session history entry, and check that 510 // classification begins correctly for the landing page. 511 EXPECT_CALL(*classifier_, CancelPendingClassification()); 512 LoadHtml("host4.com", "<html><body>abc</body></html>"); 513 Mock::VerifyAndClearExpectations(classifier_); 514 page_text = ASCIIToUTF16("abc"); 515 EXPECT_CALL(*classifier_, CancelPendingClassification()); 516 PageCaptured(&page_text, false); 517 Mock::VerifyAndClearExpectations(classifier_); 518 EXPECT_CALL(*classifier_, CancelPendingClassification()); 519 520 ui_test_utils::NavigateToURL( 521 browser(), GURL("javascript:location.replace(\'redir\');")); 522 523 Mock::VerifyAndClearExpectations(classifier_); 524 525 std::string url_str = "http://host4.com:"; 526 url_str += base::IntToString(embedded_test_server_->port()); 527 url_str += "/redir"; 528 OnStartPhishingDetection(GURL(url_str)); 529 page_text = ASCIIToUTF16("123"); 530 { 531 InSequence s; 532 EXPECT_CALL(*classifier_, CancelPendingClassification()); 533 EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _)); 534 PageCaptured(&page_text, false); 535 Mock::VerifyAndClearExpectations(classifier_); 536 } 537 538 // The delegate will cancel pending classification on destruction. 539 EXPECT_CALL(*classifier_, CancelPendingClassification()); 540 } 541 542 IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, 543 IgnorePreliminaryCapture) { 544 // Tests that preliminary PageCaptured notifications are ignored. 545 MockScorer scorer; 546 delegate_->SetPhishingScorer(&scorer); 547 ASSERT_TRUE(classifier_->is_ready()); 548 549 EXPECT_CALL(*classifier_, CancelPendingClassification()); 550 GURL url = LoadHtml("host.com", "<html><body>phish</body></html>"); 551 Mock::VerifyAndClearExpectations(classifier_); 552 OnStartPhishingDetection(url); 553 base::string16 page_text = ASCIIToUTF16("phish"); 554 PageCaptured(&page_text, true); 555 556 // Once the non-preliminary capture happens, classification should begin. 557 page_text = ASCIIToUTF16("phish"); 558 { 559 InSequence s; 560 EXPECT_CALL(*classifier_, CancelPendingClassification()); 561 EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _)); 562 PageCaptured(&page_text, false); 563 Mock::VerifyAndClearExpectations(classifier_); 564 } 565 566 // The delegate will cancel pending classification on destruction. 567 EXPECT_CALL(*classifier_, CancelPendingClassification()); 568 } 569 570 #if defined(ADDRESS_SANITIZER) 571 #define Maybe_DuplicatePageCapture DISABLED_DuplicatePageCapture 572 #else 573 #define Maybe_DuplicatePageCapture DuplicatePageCapture 574 #endif 575 IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, 576 Maybe_DuplicatePageCapture) { 577 // Tests that a second PageCaptured notification causes classification to 578 // be cancelled. 579 MockScorer scorer; 580 delegate_->SetPhishingScorer(&scorer); 581 ASSERT_TRUE(classifier_->is_ready()); 582 583 EXPECT_CALL(*classifier_, CancelPendingClassification()); 584 GURL url = LoadHtml("host.com", "<html><body>phish</body></html>"); 585 Mock::VerifyAndClearExpectations(classifier_); 586 OnStartPhishingDetection(url); 587 base::string16 page_text = ASCIIToUTF16("phish"); 588 { 589 InSequence s; 590 EXPECT_CALL(*classifier_, CancelPendingClassification()); 591 EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _)); 592 PageCaptured(&page_text, false); 593 Mock::VerifyAndClearExpectations(classifier_); 594 } 595 596 page_text = ASCIIToUTF16("phish"); 597 EXPECT_CALL(*classifier_, CancelPendingClassification()); 598 PageCaptured(&page_text, false); 599 Mock::VerifyAndClearExpectations(classifier_); 600 601 // The delegate will cancel pending classification on destruction. 602 EXPECT_CALL(*classifier_, CancelPendingClassification()); 603 } 604 605 IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, PhishingDetectionDone) { 606 // Tests that a PhishingDetectionDone IPC is sent to the browser 607 // whenever we finish classification. 608 MockScorer scorer; 609 delegate_->SetPhishingScorer(&scorer); 610 ASSERT_TRUE(classifier_->is_ready()); 611 612 // Start by loading a page to populate the delegate's state. 613 EXPECT_CALL(*classifier_, CancelPendingClassification()); 614 GURL url = LoadHtml("host.com", "<html><body>phish</body></html>"); 615 Mock::VerifyAndClearExpectations(classifier_); 616 base::string16 page_text = ASCIIToUTF16("phish"); 617 OnStartPhishingDetection(url); 618 { 619 InSequence s; 620 EXPECT_CALL(*classifier_, CancelPendingClassification()); 621 EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _)); 622 PageCaptured(&page_text, false); 623 Mock::VerifyAndClearExpectations(classifier_); 624 } 625 626 // Now run the callback to simulate the classifier finishing. 627 ClientPhishingRequest verdict; 628 verdict.set_url(url.spec()); 629 verdict.set_client_score(0.8f); 630 verdict.set_is_phishing(false); // Send IPC even if site is not phishing. 631 RunClassificationDone(verdict); 632 ASSERT_TRUE(intercepting_filter_->verdict()); 633 EXPECT_EQ(verdict.SerializeAsString(), 634 intercepting_filter_->verdict()->SerializeAsString()); 635 636 // The delegate will cancel pending classification on destruction. 637 EXPECT_CALL(*classifier_, CancelPendingClassification()); 638 } 639 640 } // namespace safe_browsing 641