Home | History | Annotate | Download | only in safe_browsing
      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