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
42using base::ASCIIToUTF16;
43using ::testing::_;
44using ::testing::InSequence;
45using ::testing::Mock;
46using ::testing::Pointee;
47using ::testing::StrictMock;
48
49namespace safe_browsing {
50
51namespace {
52
53// The RenderFrame is routing ID 1, and the RenderView is 2.
54const int kRenderViewRoutingId = 2;
55
56class 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
71class 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
82class 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
134class 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
268IN_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
410IN_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
443IN_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
470IN_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
542IN_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
575IN_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
605IN_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