| Index: components/safe_browsing/request_checker_unittest.cc | 
| diff --git a/components/safe_browsing/request_checker_unittest.cc b/components/safe_browsing/request_checker_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..486861cd5ebef904ac429edf4adf68b489ee6347 | 
| --- /dev/null | 
| +++ b/components/safe_browsing/request_checker_unittest.cc | 
| @@ -0,0 +1,504 @@ | 
| +// Copyright 2017 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "components/safe_browsing/request_checker.h" | 
| + | 
| +#include <memory> | 
| +#include <set> | 
| +#include <string> | 
| +#include <utility> | 
| + | 
| +#include "base/bind.h" | 
| +#include "base/callback.h" | 
| +#include "base/location.h" | 
| +#include "base/memory/ref_counted.h" | 
| +#include "base/run_loop.h" | 
| +#include "base/time/time.h" | 
| +#include "components/safe_browsing/base_ui_manager.h" | 
| +#include "components/safe_browsing_db/database_manager.h" | 
| +#include "components/safe_browsing_db/hit_report.h" | 
| +#include "components/safe_browsing_db/v4_protocol_manager_util.h" | 
| +#include "components/security_interstitials/content/unsafe_resource.h" | 
| +#include "content/public/browser/browser_thread.h" | 
| +#include "content/public/common/resource_type.h" | 
| +#include "content/public/test/test_browser_thread_bundle.h" | 
| +#include "net/base/load_flags.h" | 
| +#include "net/url_request/url_request.h" | 
| +#include "net/url_request/url_request_test_util.h" | 
| +#include "testing/gmock/include/gmock/gmock.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| +#include "url/gurl.h" | 
| + | 
| +namespace safe_browsing { | 
| + | 
| +namespace { | 
| + | 
| +using ::testing::ElementsAre; | 
| +using ::testing::InSequence; | 
| +using ::testing::Return; | 
| +using ::testing::MockFunction; | 
| +using ::testing::NiceMock; | 
| +using ::testing::SaveArg; | 
| +using ::testing::StrictMock; | 
| +using ::testing::_; | 
| + | 
| +constexpr char kTestUrl[] = "test:"; | 
| +constexpr char kTestUrl2[] = "test:2"; | 
| +constexpr char kTestUrl3[] = "test:3"; | 
| + | 
| +// A checkpoint can be used to verify the relative ordering of mocked calls and | 
| +// real calls to the code under test. | 
| +typedef StrictMock<MockFunction<void(int)>> Checkpoint; | 
| + | 
| +class StubUIManager : public BaseUIManager { | 
| + public: | 
| +  StubUIManager() {} | 
| + | 
| +  // Override all the methods on the base class to eliminate all side-effects. | 
| +  void StopOnIOThread(bool shutdown) override {} | 
| +  void DisplayBlockingPage(const UnsafeResource& resource) override {} | 
| +  void LogPauseDelay(base::TimeDelta time) override {} | 
| +  void SendSerializedThreatDetails(const std::string& serialized) override {} | 
| +  void MaybeReportSafeBrowsingHit( | 
| +      const safe_browsing::HitReport& hit_report) override {} | 
| +  bool IsWhitelisted(const UnsafeResource& resource) override { return false; } | 
| +  bool IsUrlWhitelistedOrPendingForWebContents( | 
| +      const GURL& url, | 
| +      bool is_subresource, | 
| +      content::NavigationEntry* entry, | 
| +      content::WebContents* web_contents, | 
| +      bool whitelist_only, | 
| +      SBThreatType* threat_type) override { | 
| +    return false; | 
| +  } | 
| +  void OnBlockingPageDone(const std::vector<UnsafeResource>& resources, | 
| +                          bool proceed, | 
| +                          content::WebContents* web_contents, | 
| +                          const GURL& main_frame_url) override {} | 
| +  const std::string app_locale() const override { return "C"; } | 
| +  history::HistoryService* history_service( | 
| +      content::WebContents* web_contents) override { | 
| +    return nullptr; | 
| +  } | 
| +  const GURL default_safe_page() const override { return GURL(); } | 
| + | 
| + protected: | 
| +  ~StubUIManager() override {} | 
| + | 
| +  // These should never be called. Override them anyway to be on the safe side. | 
| +  void ReportSafeBrowsingHitOnIOThread( | 
| +      const safe_browsing::HitReport& hit_report) override {} | 
| +  void CreateAndSendHitReport(const UnsafeResource& resource) override {} | 
| +  void ShowBlockingPageForResource(const UnsafeResource& resource) override {} | 
| +}; | 
| + | 
| +// RequestChecker only uses one method from UIManager, LogPauseDelay, so only | 
| +// mock that one. | 
| +class SemiMockUIManager : public StubUIManager { | 
| + public: | 
| +  MOCK_METHOD1(LogPauseDelay, void(base::TimeDelta)); | 
| + | 
| + protected: | 
| +  ~SemiMockUIManager() {} | 
| +}; | 
| + | 
| +class MockDelegate : public RequestChecker::Delegate { | 
| + public: | 
| +  MOCK_METHOD1(MaybeDestroyPrerenderContents, void(const WebContentsGetter&)); | 
| +  MOCK_CONST_METHOD1(GetWebContentsGetterForRequest, | 
| +                     WebContentsGetter(const net::URLRequest* request)); | 
| +  MOCK_METHOD2(StartDisplayingBlockingPage, | 
| +               void(const security_interstitials::UnsafeResource& resource, | 
| +                    scoped_refptr<BaseUIManager> ui_manager)); | 
| +  MOCK_METHOD0(CancelResourceLoad, void()); | 
| +  MOCK_METHOD0(ResumeResourceRequest, void()); | 
| +}; | 
| + | 
| +class MockSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager { | 
| + public: | 
| +  MOCK_METHOD1(CancelApiCheck, bool(Client* client)); | 
| +  MOCK_METHOD1(CancelCheck, void(Client* client)); | 
| +  MOCK_CONST_METHOD1(CanCheckResourceType, | 
| +                     bool(content::ResourceType resource_type)); | 
| +  MOCK_CONST_METHOD1(CanCheckUrl, bool(const GURL& url)); | 
| +  MOCK_CONST_METHOD0(ChecksAreAlwaysAsync, bool()); | 
| +  MOCK_METHOD2(CheckApiBlacklistUrl, bool(const GURL& url, Client* client)); | 
| +  MOCK_METHOD2(CheckBrowseUrl, bool(const GURL& url, Client* client)); | 
| +  MOCK_METHOD2(CheckUrlForSubresourceFilter, | 
| +               bool(const GURL& url, Client* client)); | 
| +  MOCK_METHOD2(CheckDownloadUrl, | 
| +               bool(const std::vector<GURL>& url_chain, Client* client)); | 
| +  MOCK_METHOD2(CheckExtensionIDs, | 
| +               bool(const std::set<std::string>& extension_ids, | 
| +                    Client* client)); | 
| +  MOCK_METHOD2(CheckResourceUrl, bool(const GURL& url, Client* client)); | 
| +  MOCK_METHOD1(MatchCsdWhitelistUrl, bool(const GURL& url)); | 
| +  MOCK_METHOD1(MatchDownloadWhitelistString, bool(const std::string& str)); | 
| +  MOCK_METHOD1(MatchDownloadWhitelistUrl, bool(const GURL& url)); | 
| +  MOCK_METHOD1(MatchMalwareIP, bool(const std::string& ip_address)); | 
| +  MOCK_METHOD1(MatchModuleWhitelistString, bool(const std::string& str)); | 
| +  MOCK_METHOD0(GetStoresForFullHashRequests, StoresToCheck()); | 
| +  MOCK_CONST_METHOD0(GetThreatSource, ThreatSource()); | 
| +  MOCK_METHOD0(IsCsdWhitelistKillSwitchOn, bool()); | 
| +  MOCK_CONST_METHOD0(IsDownloadProtectionEnabled, bool()); | 
| +  MOCK_METHOD0(IsMalwareKillSwitchOn, bool()); | 
| +  MOCK_CONST_METHOD0(IsSupported, bool()); | 
| +  MOCK_METHOD2(StartOnIOThread, | 
| +               void(net::URLRequestContextGetter* request_context_getter, | 
| +                    const V4ProtocolConfig& config)); | 
| +  MOCK_METHOD1(StopOnIOThread, void(bool shutdown)); | 
| + | 
| + protected: | 
| +  ~MockSafeBrowsingDatabaseManager() {} | 
| +}; | 
| + | 
| +class RequestCheckerTest : public ::testing::Test { | 
| + protected: | 
| +  RequestCheckerTest() | 
| +      : request_(url_request_context_.CreateRequest(GURL(kTestUrl), | 
| +                                                    net::DEFAULT_PRIORITY, | 
| +                                                    nullptr)), | 
| +        database_manager_(new NiceMock<MockSafeBrowsingDatabaseManager>()), | 
| +        ui_manager_(new NiceMock<SemiMockUIManager>()), | 
| +        delegate_(base::MakeUnique<NiceMock<MockDelegate>>()), | 
| +        checker_(base::MakeUnique<RequestChecker>( | 
| +            request_.get(), | 
| +            content::RESOURCE_TYPE_SUB_RESOURCE, | 
| +            database_manager_, | 
| +            ui_manager_, | 
| +            delegate_.get())) { | 
| +    // CanCheckResourceType() is called by almost all tests so make it pass by | 
| +    // default. | 
| +    ON_CALL(*database_manager_, CanCheckResourceType(_)) | 
| +        .WillByDefault(Return(true)); | 
| +  } | 
| +  ~RequestCheckerTest() override { | 
| +    // It's possible that something could hold a reference to these objects | 
| +    // after the framework is destroyed, so make sure mock expectations have | 
| +    // been verified. | 
| +    ::testing::Mock::VerifyAndClearExpectations(database_manager_.get()); | 
| +    ::testing::Mock::VerifyAndClearExpectations(ui_manager_.get()); | 
| +  } | 
| + | 
| +  const content::TestBrowserThreadBundle test_browser_thread_bundle_; | 
| +  net::TestURLRequestContext url_request_context_; | 
| +  const std::unique_ptr<net::URLRequest> request_; | 
| +  const scoped_refptr<NiceMock<MockSafeBrowsingDatabaseManager>> | 
| +      database_manager_; | 
| +  const scoped_refptr<NiceMock<SemiMockUIManager>> ui_manager_; | 
| +  const std::unique_ptr<NiceMock<MockDelegate>> delegate_; | 
| +  std::unique_ptr<RequestChecker> checker_; | 
| +}; | 
| + | 
| +TEST_F(RequestCheckerTest, Constructs) { | 
| +  // The framework constructor does all the work. | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, UncheckedResourceType) { | 
| +  EXPECT_CALL(*database_manager_, | 
| +              CanCheckResourceType(content::RESOURCE_TYPE_SUB_RESOURCE)) | 
| +      .WillOnce(Return(false)); | 
| +  EXPECT_EQ(RequestChecker::PROCEED_SKIPPED, checker_->CheckNewRequest()); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, SynchronousPass) { | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(true)); | 
| +  EXPECT_EQ(RequestChecker::PROCEED_SAFE, checker_->CheckNewRequest()); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, CorrectUrlIsPassedToDatabaseManager) { | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(GURL(kTestUrl), _)) | 
| +      .WillOnce(Return(true)); | 
| +  checker_->CheckNewRequest(); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, ZeroDelayIsLogged) { | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(true)); | 
| +  EXPECT_CALL(*ui_manager_, LogPauseDelay(base::TimeDelta())); | 
| +  checker_->CheckNewRequest(); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, SyncMissAsyncProceed) { | 
| +  Checkpoint checkpoint; | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(false)); | 
| +  EXPECT_CALL(*database_manager_, ChecksAreAlwaysAsync()) | 
| +      .WillOnce(Return(false)); | 
| +  { | 
| +    InSequence s; | 
| +    EXPECT_CALL(checkpoint, Call(1)); | 
| +    EXPECT_CALL(*delegate_, ResumeResourceRequest()); | 
| +  } | 
| + | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckNewRequest()); | 
| +  checkpoint.Call(1); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl), SB_THREAT_TYPE_SAFE, | 
| +                                   ThreatMetadata()); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, SyncMissAsyncUnsafe) { | 
| +  security_interstitials::UnsafeResource resource; | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(false)); | 
| +  EXPECT_CALL(*database_manager_, ChecksAreAlwaysAsync()) | 
| +      .WillOnce(Return(false)); | 
| +  EXPECT_CALL(*delegate_, StartDisplayingBlockingPage(_, _)) | 
| +      .WillOnce(SaveArg<0>(&resource)); | 
| +  EXPECT_CALL(*delegate_, GetWebContentsGetterForRequest(request_.get())) | 
| +      .WillOnce(Return( | 
| +          base::Bind([]() -> content::WebContents* { return nullptr; }))); | 
| +  EXPECT_CALL(*database_manager_, GetThreatSource()) | 
| +      .WillOnce(Return(ThreatSource::LOCAL_PVER4)); | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckNewRequest()); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl), SB_THREAT_TYPE_URL_MALWARE, | 
| +                                   ThreatMetadata()); | 
| +  EXPECT_EQ(GURL(kTestUrl), resource.url); | 
| +  EXPECT_EQ(request_->original_url(), resource.original_url); | 
| +  EXPECT_EQ(0u, resource.redirect_urls.size()); | 
| +  EXPECT_TRUE(resource.is_subresource); | 
| +  EXPECT_FALSE(resource.is_subframe); | 
| +  EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE, resource.threat_type); | 
| +  EXPECT_EQ(ThreatMetadata(), resource.threat_metadata); | 
| +  EXPECT_FALSE(resource.callback.is_null()); | 
| +  EXPECT_TRUE(resource.callback_thread); | 
| +  EXPECT_EQ(nullptr, resource.web_contents_getter.Run()); | 
| +  EXPECT_EQ(ThreatSource::LOCAL_PVER4, resource.threat_source); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, SyncMissAsyncUnsafeBlockBypassed) { | 
| +  security_interstitials::UnsafeResource resource; | 
| +  Checkpoint checkpoint; | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(false)); | 
| +  EXPECT_CALL(*database_manager_, ChecksAreAlwaysAsync()) | 
| +      .WillOnce(Return(false)); | 
| +  EXPECT_CALL(*delegate_, StartDisplayingBlockingPage(_, _)) | 
| +      .WillOnce(SaveArg<0>(&resource)); | 
| +  EXPECT_CALL(*delegate_, GetWebContentsGetterForRequest(request_.get())) | 
| +      .WillOnce(Return( | 
| +          base::Bind([]() -> content::WebContents* { return nullptr; }))); | 
| +  EXPECT_CALL(*database_manager_, GetThreatSource()) | 
| +      .WillOnce(Return(ThreatSource::LOCAL_PVER4)); | 
| +  { | 
| +    InSequence s; | 
| +    EXPECT_CALL(checkpoint, Call(1)); | 
| +    EXPECT_CALL(*delegate_, ResumeResourceRequest()); | 
| +  } | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckNewRequest()); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl), SB_THREAT_TYPE_URL_MALWARE, | 
| +                                   ThreatMetadata()); | 
| +  checkpoint.Call(1); | 
| +  resource.callback.Run(true); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, SyncMissAsyncUnsafeBlockRejected) { | 
| +  security_interstitials::UnsafeResource resource; | 
| +  Checkpoint checkpoint; | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(false)); | 
| +  EXPECT_CALL(*database_manager_, ChecksAreAlwaysAsync()) | 
| +      .WillOnce(Return(false)); | 
| +  EXPECT_CALL(*delegate_, StartDisplayingBlockingPage(_, _)) | 
| +      .WillOnce(SaveArg<0>(&resource)); | 
| +  EXPECT_CALL(*delegate_, GetWebContentsGetterForRequest(request_.get())) | 
| +      .WillOnce(Return( | 
| +          base::Bind([]() -> content::WebContents* { return nullptr; }))); | 
| +  EXPECT_CALL(*database_manager_, GetThreatSource()) | 
| +      .WillOnce(Return(ThreatSource::LOCAL_PVER4)); | 
| +  { | 
| +    InSequence s; | 
| +    EXPECT_CALL(checkpoint, Call(1)); | 
| +    EXPECT_CALL(*delegate_, CancelResourceLoad()); | 
| +  } | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckNewRequest()); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl), SB_THREAT_TYPE_URL_MALWARE, | 
| +                                   ThreatMetadata()); | 
| +  checkpoint.Call(1); | 
| +  resource.callback.Run(false); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, AlwaysAsyncSafe) { | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(false)); | 
| +  EXPECT_CALL(*database_manager_, ChecksAreAlwaysAsync()) | 
| +      .WillRepeatedly(Return(true)); | 
| +  // ResumeResourceRequest() must not be called. | 
| +  EXPECT_CALL(*delegate_, ResumeResourceRequest()).Times(0); | 
| +  EXPECT_EQ(RequestChecker::PROCEED_ALWAYS_ASYNC, checker_->CheckNewRequest()); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl), SB_THREAT_TYPE_SAFE, | 
| +                                   ThreatMetadata()); | 
| +  EXPECT_EQ(RequestChecker::PROCEED_SAFE, checker_->ShouldDeferResponse()); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, AlwaysAsyncLateSafe) { | 
| +  Checkpoint checkpoint; | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(false)); | 
| +  EXPECT_CALL(*database_manager_, ChecksAreAlwaysAsync()) | 
| +      .WillRepeatedly(Return(true)); | 
| +  { | 
| +    InSequence s; | 
| +    EXPECT_CALL(checkpoint, Call(1)); | 
| +    EXPECT_CALL(*delegate_, ResumeResourceRequest()); | 
| +  } | 
| +  EXPECT_EQ(RequestChecker::PROCEED_ALWAYS_ASYNC, checker_->CheckNewRequest()); | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->ShouldDeferResponse()); | 
| +  checkpoint.Call(1); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl), SB_THREAT_TYPE_SAFE, | 
| +                                   ThreatMetadata()); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, AlwaysAsyncUnsafe) { | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(false)); | 
| +  EXPECT_CALL(*database_manager_, ChecksAreAlwaysAsync()) | 
| +      .WillRepeatedly(Return(true)); | 
| +  EXPECT_CALL(*delegate_, StartDisplayingBlockingPage(_, _)); | 
| +  EXPECT_CALL(*delegate_, GetWebContentsGetterForRequest(request_.get())) | 
| +      .WillOnce(Return( | 
| +          base::Bind([]() -> content::WebContents* { return nullptr; }))); | 
| +  EXPECT_CALL(*database_manager_, GetThreatSource()) | 
| +      .WillOnce(Return(ThreatSource::LOCAL_PVER4)); | 
| +  // ResumeResourceRequest() must not be called. | 
| +  EXPECT_CALL(*delegate_, ResumeResourceRequest()).Times(0); | 
| +  EXPECT_EQ(RequestChecker::PROCEED_ALWAYS_ASYNC, checker_->CheckNewRequest()); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl), SB_THREAT_TYPE_URL_MALWARE, | 
| +                                   ThreatMetadata()); | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->ShouldDeferResponse()); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, RedirectToSafeSync) { | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)) | 
| +      .WillRepeatedly(Return(true)); | 
| +  EXPECT_EQ(RequestChecker::PROCEED_SAFE, checker_->CheckNewRequest()); | 
| +  EXPECT_EQ(RequestChecker::PROCEED_SAFE, | 
| +            checker_->CheckRedirect(GURL(kTestUrl2))); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, RedirectToSafeAsync) { | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)) | 
| +      .WillOnce(Return(true)) | 
| +      .WillOnce(Return(false)); | 
| +  EXPECT_CALL(*delegate_, ResumeResourceRequest()); | 
| +  EXPECT_EQ(RequestChecker::PROCEED_SAFE, checker_->CheckNewRequest()); | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckRedirect(GURL(kTestUrl2))); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl2), SB_THREAT_TYPE_SAFE, | 
| +                                   ThreatMetadata()); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, RedirectToUnsafe) { | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)) | 
| +      .WillOnce(Return(true)) | 
| +      .WillOnce(Return(false)); | 
| +  EXPECT_CALL(*delegate_, StartDisplayingBlockingPage(_, _)); | 
| +  EXPECT_EQ(RequestChecker::PROCEED_SAFE, checker_->CheckNewRequest()); | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckRedirect(GURL(kTestUrl2))); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl2), SB_THREAT_TYPE_URL_MALWARE, | 
| +                                   ThreatMetadata()); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, RedirectToSafeThenUnsafe) { | 
| +  security_interstitials::UnsafeResource resource; | 
| +  Checkpoint checkpoint; | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)) | 
| +      .WillOnce(Return(true)) | 
| +      .WillRepeatedly(Return(false)); | 
| +  { | 
| +    InSequence s; | 
| +    EXPECT_CALL(checkpoint, Call(1)); | 
| +    EXPECT_CALL(*delegate_, ResumeResourceRequest()); | 
| +    EXPECT_CALL(checkpoint, Call(2)); | 
| +    EXPECT_CALL(*delegate_, StartDisplayingBlockingPage(_, _)) | 
| +        .WillOnce(SaveArg<0>(&resource)); | 
| +  } | 
| +  EXPECT_EQ(RequestChecker::PROCEED_SAFE, checker_->CheckNewRequest()); | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckRedirect(GURL(kTestUrl2))); | 
| +  checkpoint.Call(1); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl2), SB_THREAT_TYPE_SAFE, | 
| +                                   ThreatMetadata()); | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckRedirect(GURL(kTestUrl3))); | 
| +  checkpoint.Call(2); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl3), SB_THREAT_TYPE_URL_MALWARE, | 
| +                                   ThreatMetadata()); | 
| +  EXPECT_EQ(GURL(kTestUrl3), resource.url); | 
| +  EXPECT_EQ(GURL(kTestUrl), resource.original_url); | 
| +  EXPECT_THAT(resource.redirect_urls, | 
| +              ElementsAre(GURL(kTestUrl2), GURL(kTestUrl3))); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, RedirectToSafeAlwaysAsync) { | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)) | 
| +      .WillRepeatedly(Return(false)); | 
| +  EXPECT_CALL(*database_manager_, ChecksAreAlwaysAsync()) | 
| +      .WillRepeatedly(Return(true)); | 
| +  EXPECT_CALL(*delegate_, ResumeResourceRequest()); | 
| +  EXPECT_EQ(RequestChecker::PROCEED_ALWAYS_ASYNC, checker_->CheckNewRequest()); | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckRedirect(GURL(kTestUrl2))); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl), SB_THREAT_TYPE_SAFE, | 
| +                                   ThreatMetadata()); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl2), SB_THREAT_TYPE_SAFE, | 
| +                                   ThreatMetadata()); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, AsyncTimeout) { | 
| +  ScopedTimeoutForTesting scoped_timeout(1); | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(false)); | 
| +  EXPECT_CALL(*database_manager_, CancelCheck(_)); | 
| +  EXPECT_CALL(*delegate_, ResumeResourceRequest()); | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckNewRequest()); | 
| +  base::RunLoop run_loop; | 
| +  content::BrowserThread::PostDelayedTask(content::BrowserThread::IO, FROM_HERE, | 
| +                                          run_loop.QuitClosure(), | 
| +                                          base::TimeDelta::FromMilliseconds(2)); | 
| +  run_loop.Run(); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, TimeoutIsReallyCancelled) { | 
| +  // The test is not flaky because OnCheckBrowseUrlResult() is called | 
| +  // synchronously, and so will always succeed in cancelling the timer before | 
| +  // OnCheckUrlTimeout can be called asynchronously. | 
| +  ScopedTimeoutForTesting scoped_timeout(1); | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(false)); | 
| +  EXPECT_CALL(*database_manager_, CancelCheck(_)).Times(0); | 
| +  EXPECT_CALL(*delegate_, ResumeResourceRequest()); | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckNewRequest()); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl), SB_THREAT_TYPE_SAFE, | 
| +                                   ThreatMetadata()); | 
| +  base::RunLoop run_loop; | 
| +  content::BrowserThread::PostDelayedTask(content::BrowserThread::IO, FROM_HERE, | 
| +                                          run_loop.QuitClosure(), | 
| +                                          base::TimeDelta::FromMilliseconds(2)); | 
| +  run_loop.Run(); | 
| +  // If we get here without hitting a DCHECK then the test passed. | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, PrefetchCancelledOnAsyncBlock) { | 
| +  request_->SetLoadFlags(request_->load_flags() | net::LOAD_PREFETCH); | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(false)); | 
| +  EXPECT_CALL(*delegate_, CancelResourceLoad()); | 
| +  // Blocking page is not displayed for prefetch / prerender. | 
| +  EXPECT_CALL(*delegate_, StartDisplayingBlockingPage(_, _)).Times(0); | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckNewRequest()); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl), SB_THREAT_TYPE_URL_MALWARE, | 
| +                                   ThreatMetadata()); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, PrerenderContentsDestroyed) { | 
| +  request_->SetLoadFlags(request_->load_flags() | net::LOAD_PREFETCH); | 
| +  // Change resource type to MAIN_FRAME. | 
| +  checker_.reset( | 
| +      new RequestChecker(request_.get(), content::RESOURCE_TYPE_MAIN_FRAME, | 
| +                         database_manager_, ui_manager_, delegate_.get())); | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(false)); | 
| +  EXPECT_CALL(*delegate_, CancelResourceLoad()); | 
| +  EXPECT_CALL(*delegate_, MaybeDestroyPrerenderContents(_)); | 
| +  EXPECT_CALL(*delegate_, StartDisplayingBlockingPage(_, _)).Times(0); | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckNewRequest()); | 
| +  checker_->OnCheckBrowseUrlResult(GURL(kTestUrl), SB_THREAT_TYPE_URL_MALWARE, | 
| +                                   ThreatMetadata()); | 
| +} | 
| + | 
| +TEST_F(RequestCheckerTest, CheckCancelledOnDestruction) { | 
| +  EXPECT_CALL(*database_manager_, CheckBrowseUrl(_, _)).WillOnce(Return(false)); | 
| +  EXPECT_CALL(*database_manager_, CancelCheck(_)); | 
| +  EXPECT_EQ(RequestChecker::DEFER, checker_->CheckNewRequest()); | 
| +  checker_.reset(); | 
| +} | 
| + | 
| +}  // namespace | 
| + | 
| +}  // namespace safe_browsing | 
|  |