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 // This test creates a fake safebrowsing service, where we can inject
      6 // malware and phishing urls.  It then uses a real browser to go to
      7 // these urls, and sends "goback" or "proceed" commands and verifies
      8 // they work.
      9 
     10 #include "base/bind.h"
     11 #include "base/command_line.h"
     12 #include "base/prefs/pref_service.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "base/values.h"
     15 #include "chrome/browser/browser_process.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/browser/safe_browsing/database_manager.h"
     18 #include "chrome/browser/safe_browsing/malware_details.h"
     19 #include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
     20 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
     21 #include "chrome/browser/safe_browsing/safe_browsing_util.h"
     22 #include "chrome/browser/safe_browsing/ui_manager.h"
     23 #include "chrome/browser/ui/browser.h"
     24 #include "chrome/browser/ui/browser_tabstrip.h"
     25 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     26 #include "chrome/common/pref_names.h"
     27 #include "chrome/common/url_constants.h"
     28 #include "chrome/test/base/in_process_browser_test.h"
     29 #include "chrome/test/base/test_switches.h"
     30 #include "chrome/test/base/ui_test_utils.h"
     31 #include "content/public/browser/interstitial_page.h"
     32 #include "content/public/browser/navigation_controller.h"
     33 #include "content/public/browser/notification_types.h"
     34 #include "content/public/browser/render_frame_host.h"
     35 #include "content/public/browser/render_view_host.h"
     36 #include "content/public/browser/web_contents.h"
     37 #include "content/public/test/test_browser_thread.h"
     38 #include "content/public/test/test_utils.h"
     39 
     40 using content::BrowserThread;
     41 using content::InterstitialPage;
     42 using content::NavigationController;
     43 using content::WebContents;
     44 
     45 namespace {
     46 
     47 const char kEmptyPage[] = "files/empty.html";
     48 const char kMalwarePage[] = "files/safe_browsing/malware.html";
     49 const char kMalwareIframe[] = "files/safe_browsing/malware_iframe.html";
     50 
     51 class InterstitialObserver : public content::WebContentsObserver {
     52  public:
     53   InterstitialObserver(content::WebContents* web_contents,
     54                        const base::Closure& attach_callback,
     55                        const base::Closure& detach_callback)
     56       : WebContentsObserver(web_contents),
     57         attach_callback_(attach_callback),
     58         detach_callback_(detach_callback) {
     59   }
     60 
     61   virtual void DidAttachInterstitialPage() OVERRIDE {
     62     attach_callback_.Run();
     63   }
     64 
     65   virtual void DidDetachInterstitialPage() OVERRIDE {
     66     detach_callback_.Run();
     67   }
     68 
     69  private:
     70   base::Closure attach_callback_;
     71   base::Closure detach_callback_;
     72 
     73   DISALLOW_COPY_AND_ASSIGN(InterstitialObserver);
     74 };
     75 
     76 // A SafeBrowsingDatabaseManager class that allows us to inject the malicious
     77 // URLs.
     78 class FakeSafeBrowsingDatabaseManager :  public SafeBrowsingDatabaseManager {
     79  public:
     80   explicit FakeSafeBrowsingDatabaseManager(SafeBrowsingService* service)
     81       : SafeBrowsingDatabaseManager(service) { }
     82 
     83   // Called on the IO thread to check if the given url is safe or not.  If we
     84   // can synchronously determine that the url is safe, CheckUrl returns true.
     85   // Otherwise it returns false, and "client" is called asynchronously with the
     86   // result when it is ready.
     87   // Overrides SafeBrowsingDatabaseManager::CheckBrowseUrl.
     88   virtual bool CheckBrowseUrl(const GURL& gurl, Client* client) OVERRIDE {
     89     if (badurls[gurl.spec()] == SB_THREAT_TYPE_SAFE)
     90       return true;
     91 
     92     BrowserThread::PostTask(
     93         BrowserThread::IO, FROM_HERE,
     94         base::Bind(&FakeSafeBrowsingDatabaseManager::OnCheckBrowseURLDone,
     95                    this, gurl, client));
     96     return false;
     97   }
     98 
     99   void OnCheckBrowseURLDone(const GURL& gurl, Client* client) {
    100     std::vector<SBThreatType> expected_threats;
    101     expected_threats.push_back(SB_THREAT_TYPE_URL_MALWARE);
    102     expected_threats.push_back(SB_THREAT_TYPE_URL_PHISHING);
    103     SafeBrowsingDatabaseManager::SafeBrowsingCheck sb_check(
    104         std::vector<GURL>(1, gurl),
    105         std::vector<SBFullHash>(),
    106         client,
    107         safe_browsing_util::MALWARE,
    108         expected_threats);
    109     sb_check.url_results[0] = badurls[gurl.spec()];
    110     client->OnSafeBrowsingResult(sb_check);
    111   }
    112 
    113   void SetURLThreatType(const GURL& url, SBThreatType threat_type) {
    114     badurls[url.spec()] = threat_type;
    115   }
    116 
    117  private:
    118   virtual ~FakeSafeBrowsingDatabaseManager() {}
    119 
    120   base::hash_map<std::string, SBThreatType> badurls;
    121   DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingDatabaseManager);
    122 };
    123 
    124 // A SafeBrowingUIManager class that allows intercepting malware details.
    125 class FakeSafeBrowsingUIManager :  public SafeBrowsingUIManager {
    126  public:
    127   explicit FakeSafeBrowsingUIManager(SafeBrowsingService* service) :
    128       SafeBrowsingUIManager(service) { }
    129 
    130   // Overrides SafeBrowsingUIManager
    131   virtual void SendSerializedMalwareDetails(
    132       const std::string& serialized) OVERRIDE {
    133     // Notify the UI thread that we got a report.
    134     BrowserThread::PostTask(
    135         BrowserThread::UI,
    136         FROM_HERE,
    137         base::Bind(&FakeSafeBrowsingUIManager::OnMalwareDetailsDone,
    138                    this,
    139                    serialized));
    140   }
    141 
    142   void OnMalwareDetailsDone(const std::string& serialized) {
    143     EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
    144     report_ = serialized;
    145 
    146     EXPECT_FALSE(malware_details_done_callback_.is_null());
    147     if (!malware_details_done_callback_.is_null()) {
    148       malware_details_done_callback_.Run();
    149       malware_details_done_callback_ = base::Closure();
    150     }
    151   }
    152 
    153   void set_malware_details_done_callback(const base::Closure& callback) {
    154     EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
    155     EXPECT_TRUE(malware_details_done_callback_.is_null());
    156     malware_details_done_callback_ = callback;
    157   }
    158 
    159   std::string GetReport() {
    160     EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
    161     return report_;
    162   }
    163 
    164  protected:
    165   virtual ~FakeSafeBrowsingUIManager() { }
    166 
    167  private:
    168   std::string report_;
    169   base::Closure malware_details_done_callback_;
    170 
    171   DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingUIManager);
    172 };
    173 
    174 class FakeSafeBrowsingService : public SafeBrowsingService {
    175  public:
    176   FakeSafeBrowsingService()
    177       : fake_database_manager_(),
    178         fake_ui_manager_() { }
    179 
    180   // Returned pointer has the same lifespan as the database_manager_ refcounted
    181   // object.
    182   FakeSafeBrowsingDatabaseManager* fake_database_manager() {
    183     return fake_database_manager_;
    184   }
    185   // Returned pointer has the same lifespan as the ui_manager_ refcounted
    186   // object.
    187   FakeSafeBrowsingUIManager* fake_ui_manager() {
    188     return fake_ui_manager_;
    189   }
    190 
    191  protected:
    192   virtual ~FakeSafeBrowsingService() { }
    193 
    194   virtual SafeBrowsingDatabaseManager* CreateDatabaseManager() OVERRIDE {
    195     fake_database_manager_ = new FakeSafeBrowsingDatabaseManager(this);
    196     return fake_database_manager_;
    197   }
    198 
    199   virtual SafeBrowsingUIManager* CreateUIManager() OVERRIDE {
    200     fake_ui_manager_ = new FakeSafeBrowsingUIManager(this);
    201     return fake_ui_manager_;
    202   }
    203 
    204  private:
    205   FakeSafeBrowsingDatabaseManager* fake_database_manager_;
    206   FakeSafeBrowsingUIManager* fake_ui_manager_;
    207 
    208   DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingService);
    209 };
    210 
    211 // Factory that creates FakeSafeBrowsingService instances.
    212 class TestSafeBrowsingServiceFactory : public SafeBrowsingServiceFactory {
    213  public:
    214   TestSafeBrowsingServiceFactory() :
    215       most_recent_service_(NULL) { }
    216   virtual ~TestSafeBrowsingServiceFactory() { }
    217 
    218   virtual SafeBrowsingService* CreateSafeBrowsingService() OVERRIDE {
    219     most_recent_service_ =  new FakeSafeBrowsingService();
    220     return most_recent_service_;
    221   }
    222 
    223   FakeSafeBrowsingService* most_recent_service() const {
    224     return most_recent_service_;
    225   }
    226 
    227  private:
    228   FakeSafeBrowsingService* most_recent_service_;
    229 };
    230 
    231 // A MalwareDetails class lets us intercept calls from the renderer.
    232 class FakeMalwareDetails : public MalwareDetails {
    233  public:
    234   FakeMalwareDetails(
    235       SafeBrowsingUIManager* delegate,
    236       WebContents* web_contents,
    237       const SafeBrowsingUIManager::UnsafeResource& unsafe_resource)
    238       : MalwareDetails(delegate, web_contents, unsafe_resource),
    239         got_dom_(false),
    240         waiting_(false) { }
    241 
    242   virtual void AddDOMDetails(
    243       const std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node>& params)
    244           OVERRIDE {
    245     EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
    246     MalwareDetails::AddDOMDetails(params);
    247 
    248     // Notify the UI thread that we got the dom details.
    249     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    250                             base::Bind(&FakeMalwareDetails::OnDOMDetailsDone,
    251                                        this));
    252   }
    253 
    254   void WaitForDOM() {
    255     if (got_dom_) {
    256       return;
    257     }
    258     // This condition might not trigger normally, but if you add a
    259     // sleep(1) in malware_dom_details it triggers :).
    260     waiting_ = true;
    261     content::RunMessageLoop();
    262     EXPECT_TRUE(got_dom_);
    263   }
    264 
    265  private:
    266   virtual ~FakeMalwareDetails() {}
    267 
    268   void OnDOMDetailsDone() {
    269     got_dom_ = true;
    270     if (waiting_) {
    271       base::MessageLoopForUI::current()->Quit();
    272     }
    273   }
    274 
    275   // Some logic to figure out if we should wait for the dom details or not.
    276   // These variables should only be accessed in the UI thread.
    277   bool got_dom_;
    278   bool waiting_;
    279 };
    280 
    281 class TestMalwareDetailsFactory : public MalwareDetailsFactory {
    282  public:
    283   TestMalwareDetailsFactory() : details_() { }
    284   virtual ~TestMalwareDetailsFactory() { }
    285 
    286   virtual MalwareDetails* CreateMalwareDetails(
    287       SafeBrowsingUIManager* delegate,
    288       WebContents* web_contents,
    289       const SafeBrowsingUIManager::UnsafeResource& unsafe_resource) OVERRIDE {
    290     details_ = new FakeMalwareDetails(delegate, web_contents,
    291                                       unsafe_resource);
    292     return details_;
    293   }
    294 
    295   FakeMalwareDetails* get_details() {
    296     return details_;
    297   }
    298 
    299  private:
    300   FakeMalwareDetails* details_;
    301 };
    302 
    303 // A SafeBrowingBlockingPage class that lets us wait until it's hidden.
    304 class TestSafeBrowsingBlockingPage : public SafeBrowsingBlockingPage {
    305  public:
    306   TestSafeBrowsingBlockingPage(SafeBrowsingUIManager* manager,
    307                                  WebContents* web_contents,
    308                                  const UnsafeResourceList& unsafe_resources)
    309       : SafeBrowsingBlockingPage(manager, web_contents, unsafe_resources),
    310         wait_for_delete_(false) {
    311     // Don't wait the whole 3 seconds for the browser test.
    312     malware_details_proceed_delay_ms_ = 100;
    313   }
    314 
    315   virtual ~TestSafeBrowsingBlockingPage() {
    316     if (!wait_for_delete_)
    317       return;
    318 
    319     // Notify that we are gone
    320     base::MessageLoopForUI::current()->Quit();
    321     wait_for_delete_ = false;
    322   }
    323 
    324   void WaitForDelete() {
    325     wait_for_delete_ = true;
    326     content::RunMessageLoop();
    327   }
    328 
    329   // InterstitialPageDelegate methods:
    330   virtual void CommandReceived(const std::string& command) OVERRIDE {
    331     SafeBrowsingBlockingPage::CommandReceived(command);
    332   }
    333   virtual void OnProceed() OVERRIDE {
    334     SafeBrowsingBlockingPage::OnProceed();
    335   }
    336   virtual void OnDontProceed() OVERRIDE {
    337     SafeBrowsingBlockingPage::OnDontProceed();
    338   }
    339 
    340  private:
    341   bool wait_for_delete_;
    342 };
    343 
    344 class TestSafeBrowsingBlockingPageFactory
    345     : public SafeBrowsingBlockingPageFactory {
    346  public:
    347   TestSafeBrowsingBlockingPageFactory() { }
    348   virtual ~TestSafeBrowsingBlockingPageFactory() { }
    349 
    350   virtual SafeBrowsingBlockingPage* CreateSafeBrowsingPage(
    351       SafeBrowsingUIManager* delegate,
    352       WebContents* web_contents,
    353       const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources)
    354           OVERRIDE {
    355     return new TestSafeBrowsingBlockingPage(delegate, web_contents,
    356                                               unsafe_resources);
    357   }
    358 };
    359 
    360 }  // namespace
    361 
    362 // Tests the safe browsing blocking page in a browser.
    363 class SafeBrowsingBlockingPageBrowserTest
    364     : public InProcessBrowserTest,
    365       public testing::WithParamInterface<int> {
    366  public:
    367   enum Visibility {
    368     VISIBILITY_ERROR = -1,
    369     HIDDEN = 0,
    370     VISIBLE = 1
    371   };
    372 
    373   SafeBrowsingBlockingPageBrowserTest() {
    374   }
    375 
    376   virtual void SetUp() OVERRIDE {
    377     SafeBrowsingService::RegisterFactory(&factory_);
    378     SafeBrowsingBlockingPage::RegisterFactory(&blocking_page_factory_);
    379     MalwareDetails::RegisterFactory(&details_factory_);
    380     InProcessBrowserTest::SetUp();
    381   }
    382 
    383   virtual void TearDown() OVERRIDE {
    384     InProcessBrowserTest::TearDown();
    385     SafeBrowsingBlockingPage::RegisterFactory(NULL);
    386     SafeBrowsingService::RegisterFactory(NULL);
    387     MalwareDetails::RegisterFactory(NULL);
    388   }
    389 
    390   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
    391     ASSERT_TRUE(test_server()->Start());
    392   }
    393 
    394   void SetURLThreatType(const GURL& url, SBThreatType threat_type) {
    395     FakeSafeBrowsingService* service =
    396         static_cast<FakeSafeBrowsingService*>(
    397             g_browser_process->safe_browsing_service());
    398 
    399     ASSERT_TRUE(service);
    400     service->fake_database_manager()->SetURLThreatType(url, threat_type);
    401   }
    402 
    403   // Adds a safebrowsing result of type |threat_type| to the fake safebrowsing
    404   // service, navigates to that page, and returns the url.
    405   GURL SetupWarningAndNavigate(SBThreatType threat_type) {
    406     GURL url = test_server()->GetURL(kEmptyPage);
    407     SetURLThreatType(url, threat_type);
    408 
    409     ui_test_utils::NavigateToURL(browser(), url);
    410     EXPECT_TRUE(WaitForReady());
    411     return url;
    412   }
    413 
    414   // Adds a safebrowsing malware result to the fake safebrowsing service,
    415   // navigates to a page with an iframe containing the malware site, and
    416   // returns the url of the parent page.
    417   GURL SetupMalwareIframeWarningAndNavigate() {
    418     GURL url = test_server()->GetURL(kMalwarePage);
    419     GURL iframe_url = test_server()->GetURL(kMalwareIframe);
    420     SetURLThreatType(iframe_url, SB_THREAT_TYPE_URL_MALWARE);
    421 
    422     ui_test_utils::NavigateToURL(browser(), url);
    423     EXPECT_TRUE(WaitForReady());
    424     return url;
    425   }
    426 
    427   void SendCommand(const std::string& command) {
    428     WebContents* contents =
    429         browser()->tab_strip_model()->GetActiveWebContents();
    430     // We use InterstitialPage::GetInterstitialPage(tab) instead of
    431     // tab->GetInterstitialPage() because the tab doesn't have a pointer
    432     // to its interstital page until it gets a command from the renderer
    433     // that it has indeed displayed it -- and this sometimes happens after
    434     // NavigateToURL returns.
    435     SafeBrowsingBlockingPage* interstitial_page =
    436         static_cast<SafeBrowsingBlockingPage*>(
    437             InterstitialPage::GetInterstitialPage(contents)->
    438                 GetDelegateForTesting());
    439     ASSERT_TRUE(interstitial_page);
    440     interstitial_page->CommandReceived(command);
    441   }
    442 
    443   void DontProceedThroughInterstitial() {
    444     WebContents* contents =
    445         browser()->tab_strip_model()->GetActiveWebContents();
    446     InterstitialPage* interstitial_page = InterstitialPage::GetInterstitialPage(
    447         contents);
    448     ASSERT_TRUE(interstitial_page);
    449     interstitial_page->DontProceed();
    450   }
    451 
    452   void ProceedThroughInterstitial() {
    453     WebContents* contents =
    454         browser()->tab_strip_model()->GetActiveWebContents();
    455     InterstitialPage* interstitial_page = InterstitialPage::GetInterstitialPage(
    456         contents);
    457     ASSERT_TRUE(interstitial_page);
    458     interstitial_page->Proceed();
    459   }
    460 
    461   void AssertNoInterstitial(bool wait_for_delete) {
    462     WebContents* contents =
    463         browser()->tab_strip_model()->GetActiveWebContents();
    464 
    465     if (contents->ShowingInterstitialPage() && wait_for_delete) {
    466       // We'll get notified when the interstitial is deleted.
    467       TestSafeBrowsingBlockingPage* page =
    468           static_cast<TestSafeBrowsingBlockingPage*>(
    469               contents->GetInterstitialPage()->GetDelegateForTesting());
    470       page->WaitForDelete();
    471     }
    472 
    473     // Can't use InterstitialPage::GetInterstitialPage() because that
    474     // gets updated after the TestSafeBrowsingBlockingPage destructor
    475     ASSERT_FALSE(contents->ShowingInterstitialPage());
    476   }
    477 
    478   bool YesInterstitial() {
    479     WebContents* contents =
    480         browser()->tab_strip_model()->GetActiveWebContents();
    481     InterstitialPage* interstitial_page = InterstitialPage::GetInterstitialPage(
    482         contents);
    483     return interstitial_page != NULL;
    484   }
    485 
    486   void WaitForInterstitial() {
    487     WebContents* contents =
    488         browser()->tab_strip_model()->GetActiveWebContents();
    489     scoped_refptr<content::MessageLoopRunner> loop_runner(
    490         new content::MessageLoopRunner);
    491     InterstitialObserver observer(contents,
    492                                   loop_runner->QuitClosure(),
    493                                   base::Closure());
    494     if (!InterstitialPage::GetInterstitialPage(contents))
    495       loop_runner->Run();
    496   }
    497 
    498   void SetReportSentCallback(const base::Closure& callback) {
    499     factory_.most_recent_service()
    500         ->fake_ui_manager()
    501         ->set_malware_details_done_callback(callback);
    502   }
    503 
    504   std::string GetReportSent() {
    505     return factory_.most_recent_service()->fake_ui_manager()->GetReport();
    506   }
    507 
    508   void MalwareRedirectCancelAndProceed(const std::string& open_function) {
    509     GURL load_url = test_server()->GetURL(
    510         "files/safe_browsing/interstitial_cancel.html");
    511     GURL malware_url("http://localhost/files/safe_browsing/malware.html");
    512     SetURLThreatType(malware_url, SB_THREAT_TYPE_URL_MALWARE);
    513 
    514     // Load the test page.
    515     ui_test_utils::NavigateToURL(browser(), load_url);
    516     // Trigger the safe browsing interstitial page via a redirect in
    517     // "openWin()".
    518     ui_test_utils::NavigateToURLWithDisposition(
    519         browser(),
    520         GURL("javascript:" + open_function + "()"),
    521         CURRENT_TAB,
    522         ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
    523     WaitForInterstitial();
    524     // Cancel the redirect request while interstitial page is open.
    525     browser()->tab_strip_model()->ActivateTabAt(0, true);
    526     ui_test_utils::NavigateToURLWithDisposition(
    527         browser(),
    528         GURL("javascript:stopWin()"),
    529         CURRENT_TAB,
    530         ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    531     browser()->tab_strip_model()->ActivateTabAt(1, true);
    532     // Simulate the user clicking "proceed", there should be no crash.  Since
    533     // clicking proceed may do nothing (see comment in MalwareRedirectCanceled
    534     // below, and crbug.com/76460), we use SendCommand to trigger the callback
    535     // directly rather than using ClickAndWaitForDetach since there might not
    536     // be a notification to wait for.
    537     SendCommand("\"proceed\"");
    538   }
    539 
    540   content::RenderViewHost* GetRenderViewHost() {
    541     InterstitialPage* interstitial = InterstitialPage::GetInterstitialPage(
    542         browser()->tab_strip_model()->GetActiveWebContents());
    543     if (!interstitial)
    544       return NULL;
    545     return interstitial->GetRenderViewHostForTesting();
    546   }
    547 
    548   bool WaitForReady() {
    549     content::RenderViewHost* rvh = GetRenderViewHost();
    550     if (!rvh)
    551       return false;
    552     // Wait until all <script> tags have executed, including jstemplate.
    553     // TODO(joaodasilva): it would be nice to avoid the busy loop, though in
    554     // practice it spins at most once or twice.
    555     std::string ready_state;
    556     do {
    557       scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(
    558           rvh->GetMainFrame(), "document.readyState");
    559       if (!value.get() || !value->GetAsString(&ready_state))
    560         return false;
    561     } while (ready_state != "complete");
    562     return true;
    563   }
    564 
    565   Visibility GetVisibility(const std::string& node_id) {
    566     content::RenderViewHost* rvh = GetRenderViewHost();
    567     if (!rvh)
    568       return VISIBILITY_ERROR;
    569     scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(
    570         rvh->GetMainFrame(),
    571         "var node = document.getElementById('" + node_id + "');\n"
    572         "if (node)\n"
    573         "   node.offsetWidth > 0 && node.offsetHeight > 0;"
    574         "else\n"
    575         "  'node not found';\n");
    576     if (!value.get())
    577       return VISIBILITY_ERROR;
    578     bool result = false;
    579     if (!value->GetAsBoolean(&result))
    580       return VISIBILITY_ERROR;
    581     return result ? VISIBLE : HIDDEN;
    582   }
    583 
    584   bool Click(const std::string& node_id) {
    585     content::RenderViewHost* rvh = GetRenderViewHost();
    586     if (!rvh)
    587       return false;
    588     // We don't use ExecuteScriptAndGetValue for this one, since clicking
    589     // the button/link may navigate away before the injected javascript can
    590     // reply, hanging the test.
    591     rvh->GetMainFrame()->ExecuteJavaScript(
    592         base::ASCIIToUTF16(
    593             "document.getElementById('" + node_id + "').click();\n"));
    594     return true;
    595   }
    596 
    597   bool ClickAndWaitForDetach(const std::string& node_id) {
    598     // We wait for interstitial_detached rather than nav_entry_committed, as
    599     // going back from a main-frame malware interstitial page will not cause a
    600     // nav entry committed event.
    601     scoped_refptr<content::MessageLoopRunner> loop_runner(
    602         new content::MessageLoopRunner);
    603     InterstitialObserver observer(
    604         browser()->tab_strip_model()->GetActiveWebContents(),
    605         base::Closure(),
    606         loop_runner->QuitClosure());
    607     if (!Click(node_id))
    608       return false;
    609     loop_runner->Run();
    610     return true;
    611   }
    612 
    613  protected:
    614   TestMalwareDetailsFactory details_factory_;
    615 
    616  private:
    617   TestSafeBrowsingServiceFactory factory_;
    618   TestSafeBrowsingBlockingPageFactory blocking_page_factory_;
    619 
    620   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPageBrowserTest);
    621 };
    622 
    623 // TODO(linux_aura) http://crbug.com/163931
    624 // TODO(win_aura) http://crbug.com/154081
    625 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
    626 #define MAYBE_MalwareRedirectInIFrameCanceled DISABLED_MalwareRedirectInIFrameCanceled
    627 #else
    628 #define MAYBE_MalwareRedirectInIFrameCanceled MalwareRedirectInIFrameCanceled
    629 #endif
    630 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest,
    631                        MAYBE_MalwareRedirectInIFrameCanceled) {
    632   // 1. Test the case that redirect is a subresource.
    633   MalwareRedirectCancelAndProceed("openWinIFrame");
    634   // If the redirect was from subresource but canceled, "proceed" will continue
    635   // with the rest of resources.
    636   AssertNoInterstitial(true);
    637 }
    638 
    639 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest,
    640                        MalwareRedirectCanceled) {
    641   // 2. Test the case that redirect is the only resource.
    642   MalwareRedirectCancelAndProceed("openWin");
    643   // Clicking proceed won't do anything if the main request is cancelled
    644   // already.  See crbug.com/76460.
    645   EXPECT_TRUE(YesInterstitial());
    646 }
    647 
    648 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest,
    649                        MalwareDontProceed) {
    650 #if defined(OS_WIN) && defined(USE_ASH)
    651   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
    652   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
    653     return;
    654 #endif
    655 
    656   SetupWarningAndNavigate(SB_THREAT_TYPE_URL_MALWARE);
    657 
    658   EXPECT_EQ(VISIBLE, GetVisibility("primary-button"));
    659   EXPECT_EQ(HIDDEN, GetVisibility("details"));
    660   EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
    661   EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
    662   EXPECT_TRUE(Click("details-button"));
    663   EXPECT_EQ(VISIBLE, GetVisibility("details"));
    664   EXPECT_EQ(VISIBLE, GetVisibility("proceed-link"));
    665   EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
    666   EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
    667 
    668   AssertNoInterstitial(false);   // Assert the interstitial is gone
    669   EXPECT_EQ(GURL(url::kAboutBlankURL),  // Back to "about:blank"
    670             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
    671 }
    672 
    673 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest,
    674                        HarmfulDontProceed) {
    675 #if defined(OS_WIN) && defined(USE_ASH)
    676   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
    677   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
    678     return;
    679 #endif
    680 
    681   SetupWarningAndNavigate(SB_THREAT_TYPE_URL_HARMFUL);
    682 
    683   EXPECT_EQ(VISIBLE, GetVisibility("primary-button"));
    684   EXPECT_EQ(HIDDEN, GetVisibility("details"));
    685   EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
    686   EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
    687   EXPECT_TRUE(Click("details-button"));
    688   EXPECT_EQ(VISIBLE, GetVisibility("details"));
    689   EXPECT_EQ(VISIBLE, GetVisibility("proceed-link"));
    690   EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
    691   EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
    692 
    693   AssertNoInterstitial(false);   // Assert the interstitial is gone
    694   EXPECT_EQ(GURL(url::kAboutBlankURL),  // Back to "about:blank"
    695             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
    696 }
    697 
    698 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest, MalwareProceed) {
    699   GURL url = SetupWarningAndNavigate(SB_THREAT_TYPE_URL_MALWARE);
    700 
    701   EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
    702   AssertNoInterstitial(true);  // Assert the interstitial is gone.
    703   EXPECT_EQ(url,
    704             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
    705 }
    706 
    707 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest, HarmfulProceed) {
    708   GURL url = SetupWarningAndNavigate(SB_THREAT_TYPE_URL_HARMFUL);
    709 
    710   EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
    711   AssertNoInterstitial(true);  // Assert the interstitial is gone.
    712   EXPECT_EQ(url,
    713             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
    714 }
    715 
    716 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest,
    717                        MalwareIframeDontProceed) {
    718 #if defined(OS_WIN) && defined(USE_ASH)
    719   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
    720   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
    721     return;
    722 #endif
    723 
    724   SetupMalwareIframeWarningAndNavigate();
    725 
    726   EXPECT_EQ(VISIBLE, GetVisibility("primary-button"));
    727   EXPECT_EQ(HIDDEN, GetVisibility("details"));
    728   EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
    729   EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
    730   EXPECT_TRUE(Click("details-button"));
    731   EXPECT_EQ(VISIBLE, GetVisibility("details"));
    732   EXPECT_EQ(VISIBLE, GetVisibility("proceed-link"));
    733   EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
    734   EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
    735 
    736   AssertNoInterstitial(false);  // Assert the interstitial is gone
    737 
    738   EXPECT_EQ(GURL(url::kAboutBlankURL),  // Back to "about:blank"
    739             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
    740 }
    741 
    742 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest,
    743     MalwareIframeProceed) {
    744   GURL url = SetupMalwareIframeWarningAndNavigate();
    745 
    746   EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
    747   AssertNoInterstitial(true);  // Assert the interstitial is gone
    748 
    749   EXPECT_EQ(url,
    750             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
    751 }
    752 
    753 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest,
    754                        MalwareIframeReportDetails) {
    755   scoped_refptr<content::MessageLoopRunner> malware_report_sent_runner(
    756       new content::MessageLoopRunner);
    757   SetReportSentCallback(malware_report_sent_runner->QuitClosure());
    758 
    759   GURL url = SetupMalwareIframeWarningAndNavigate();
    760 
    761   // If the DOM details from renderer did not already return, wait for them.
    762   details_factory_.get_details()->WaitForDOM();
    763 
    764   EXPECT_TRUE(Click("opt-in-checkbox"));
    765   EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
    766   AssertNoInterstitial(true);  // Assert the interstitial is gone
    767 
    768   ASSERT_TRUE(browser()->profile()->GetPrefs()->GetBoolean(
    769       prefs::kSafeBrowsingExtendedReportingEnabled));
    770   EXPECT_EQ(url,
    771             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
    772 
    773   malware_report_sent_runner->Run();
    774   std::string serialized = GetReportSent();
    775   safe_browsing::ClientMalwareReportRequest report;
    776   ASSERT_TRUE(report.ParseFromString(serialized));
    777   // Verify the report is complete.
    778   EXPECT_TRUE(report.complete());
    779 }
    780 
    781 // Verifies that the "proceed anyway" link isn't available when it is disabled
    782 // by the corresponding policy. Also verifies that sending the "proceed"
    783 // command anyway doesn't advance to the malware site.
    784 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest, ProceedDisabled) {
    785 #if defined(OS_WIN) && defined(USE_ASH)
    786   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
    787   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
    788     return;
    789 #endif
    790 
    791   // Simulate a policy disabling the "proceed anyway" link.
    792   browser()->profile()->GetPrefs()->SetBoolean(
    793       prefs::kSafeBrowsingProceedAnywayDisabled, true);
    794 
    795   SetupWarningAndNavigate(SB_THREAT_TYPE_URL_MALWARE);
    796 
    797   EXPECT_EQ(VISIBLE, GetVisibility("primary-button"));
    798   EXPECT_EQ(HIDDEN, GetVisibility("details"));
    799   EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
    800   EXPECT_EQ(HIDDEN, GetVisibility("final-paragraph"));
    801   EXPECT_TRUE(Click("details-button"));
    802   EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
    803   EXPECT_EQ(HIDDEN, GetVisibility("final-paragraph"));
    804   SendCommand("proceed");
    805 
    806   // The "proceed" command should go back instead, if proceeding is disabled.
    807   AssertNoInterstitial(true);
    808   EXPECT_EQ(GURL(url::kAboutBlankURL),  // Back to "about:blank"
    809             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
    810 }
    811 
    812 // Verifies that the reporting checkbox is hidden on non-HTTP pages.
    813 // TODO(mattm): Should also verify that no report is sent, but there isn't a
    814 // good way to do that in the current design.
    815 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest,
    816                        ReportingDisabled) {
    817 #if defined(OS_WIN) && defined(USE_ASH)
    818   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
    819   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
    820     return;
    821 #endif
    822 
    823   browser()->profile()->GetPrefs()->SetBoolean(
    824       prefs::kSafeBrowsingExtendedReportingEnabled, true);
    825 
    826   net::SpawnedTestServer https_server(
    827       net::SpawnedTestServer::TYPE_HTTPS, net::SpawnedTestServer::kLocalhost,
    828       base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
    829   ASSERT_TRUE(https_server.Start());
    830   GURL url = https_server.GetURL(kEmptyPage);
    831   SetURLThreatType(url, SB_THREAT_TYPE_URL_MALWARE);
    832   ui_test_utils::NavigateToURL(browser(), url);
    833   ASSERT_TRUE(WaitForReady());
    834 
    835   EXPECT_EQ(HIDDEN, GetVisibility("malware-opt-in"));
    836   EXPECT_EQ(HIDDEN, GetVisibility("opt-in-checkbox"));
    837   EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
    838   EXPECT_TRUE(Click("details-button"));
    839   EXPECT_EQ(VISIBLE, GetVisibility("help-link"));
    840   EXPECT_EQ(VISIBLE, GetVisibility("proceed-link"));
    841 
    842   EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
    843   AssertNoInterstitial(false);   // Assert the interstitial is gone
    844   EXPECT_EQ(GURL(url::kAboutBlankURL),  // Back to "about:blank"
    845             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
    846 }
    847 
    848 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest,
    849     PhishingDontProceed) {
    850 #if defined(OS_WIN) && defined(USE_ASH)
    851   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
    852   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
    853     return;
    854 #endif
    855 
    856   SetupWarningAndNavigate(SB_THREAT_TYPE_URL_PHISHING);
    857 
    858   EXPECT_EQ(VISIBLE, GetVisibility("primary-button"));
    859   EXPECT_EQ(HIDDEN, GetVisibility("details"));
    860   EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
    861   EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
    862   EXPECT_TRUE(Click("details-button"));
    863   EXPECT_EQ(VISIBLE, GetVisibility("details"));
    864   EXPECT_EQ(VISIBLE, GetVisibility("proceed-link"));
    865   EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
    866   EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
    867 
    868   AssertNoInterstitial(false);  // Assert the interstitial is gone
    869   EXPECT_EQ(GURL(url::kAboutBlankURL),  // We are back to "about:blank".
    870             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
    871 }
    872 
    873 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest, PhishingProceed) {
    874   GURL url = SetupWarningAndNavigate(SB_THREAT_TYPE_URL_PHISHING);
    875   EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
    876   AssertNoInterstitial(true);  // Assert the interstitial is gone
    877   EXPECT_EQ(url,
    878             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
    879 }
    880 
    881 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageBrowserTest, PhishingLearnMore) {
    882   SetupWarningAndNavigate(SB_THREAT_TYPE_URL_PHISHING);
    883   EXPECT_TRUE(ClickAndWaitForDetach("help-link"));
    884   AssertNoInterstitial(false);  // Assert the interstitial is gone
    885 
    886   // We are in the help page.
    887   EXPECT_EQ(
    888       "/transparencyreport/safebrowsing/",
    889        browser()->tab_strip_model()->GetActiveWebContents()->GetURL().path());
    890 }
    891