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