| // Copyright (c) 2012 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 "chrome/browser/download/download_request_limiter.h" |
| |
| #include "base/bind.h" |
| #include "base/run_loop.h" |
| #include "chrome/browser/download/download_request_infobar_delegate.h" |
| #include "chrome/browser/infobars/infobar_service.h" |
| #include "chrome/browser/ui/website_settings/permission_bubble_manager.h" |
| #include "chrome/test/base/chrome_render_view_host_test_harness.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/web_contents.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using content::WebContents; |
| |
| class DownloadRequestLimiterTest; |
| |
| class FakePermissionBubbleView : public PermissionBubbleView { |
| public: |
| explicit FakePermissionBubbleView(DownloadRequestLimiterTest *test) |
| : test_(test), delegate_(NULL) {} |
| |
| ~FakePermissionBubbleView() override { |
| if (delegate_) |
| delegate_->SetView(NULL); |
| } |
| |
| void Close() { |
| if (delegate_) |
| delegate_->Closing(); |
| } |
| |
| // PermissionBubbleView: |
| void SetDelegate(Delegate* delegate) override { delegate_ = delegate; } |
| |
| void Show(const std::vector<PermissionBubbleRequest*>& requests, |
| const std::vector<bool>& accept_state, |
| bool customization_mode) override; |
| |
| bool CanAcceptRequestUpdate() override { return false; } |
| |
| void Hide() override {} |
| bool IsVisible() override { return false; } |
| |
| private: |
| DownloadRequestLimiterTest* test_; |
| Delegate* delegate_; |
| }; |
| |
| class DownloadRequestLimiterTest : public ChromeRenderViewHostTestHarness { |
| public: |
| enum TestingAction { |
| ACCEPT, |
| CANCEL, |
| WAIT |
| }; |
| |
| void SetUp() override { |
| ChromeRenderViewHostTestHarness::SetUp(); |
| InfoBarService::CreateForWebContents(web_contents()); |
| |
| PermissionBubbleManager::CreateForWebContents(web_contents()); |
| view_.reset(new FakePermissionBubbleView(this)); |
| PermissionBubbleManager* manager = |
| PermissionBubbleManager::FromWebContents(web_contents()); |
| manager->SetView(view_.get()); |
| |
| testing_action_ = ACCEPT; |
| ask_allow_count_ = cancel_count_ = continue_count_ = 0; |
| download_request_limiter_ = new DownloadRequestLimiter(); |
| fake_create_callback_ = base::Bind( |
| &DownloadRequestLimiterTest::FakeCreate, base::Unretained(this)); |
| DownloadRequestInfoBarDelegate::SetCallbackForTesting( |
| &fake_create_callback_); |
| content_settings_ = new HostContentSettingsMap(profile_.GetPrefs(), false); |
| DownloadRequestLimiter::SetContentSettingsForTesting( |
| content_settings_.get()); |
| } |
| |
| int GetAction() { |
| return testing_action_; |
| } |
| |
| void AskAllow() { |
| ask_allow_count_++; |
| } |
| |
| void FakeCreate( |
| InfoBarService* infobar_service, |
| base::WeakPtr<DownloadRequestLimiter::TabDownloadState> host) { |
| AskAllow(); |
| switch (testing_action_) { |
| case ACCEPT: |
| host->Accept(); |
| break; |
| case CANCEL: |
| host->Cancel(); |
| break; |
| case WAIT: |
| break; |
| } |
| } |
| |
| void TearDown() override { |
| content_settings_->ShutdownOnUIThread(); |
| content_settings_ = NULL; |
| UnsetDelegate(); |
| ChromeRenderViewHostTestHarness::TearDown(); |
| } |
| |
| virtual void UnsetDelegate() { |
| DownloadRequestInfoBarDelegate::SetCallbackForTesting(NULL); |
| } |
| |
| void CanDownload() { |
| CanDownloadFor(web_contents()); |
| } |
| |
| void CanDownloadFor(WebContents* web_contents) { |
| download_request_limiter_->CanDownloadImpl( |
| web_contents, |
| "GET", // request method |
| base::Bind(&DownloadRequestLimiterTest::ContinueDownload, |
| base::Unretained(this))); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void OnUserGesture() { |
| OnUserGestureFor(web_contents()); |
| } |
| |
| void OnUserGestureFor(WebContents* web_contents) { |
| DownloadRequestLimiter::TabDownloadState* state = |
| download_request_limiter_->GetDownloadState(web_contents, NULL, false); |
| if (state) |
| state->DidGetUserGesture(); |
| } |
| |
| void AboutToNavigateRenderView() { |
| view_->Close(); |
| DownloadRequestLimiter::TabDownloadState* state = |
| download_request_limiter_->GetDownloadState( |
| web_contents(), NULL, false); |
| state->AboutToNavigateRenderView(NULL); |
| } |
| |
| void ExpectAndResetCounts( |
| int expect_continues, |
| int expect_cancels, |
| int expect_asks, |
| int line) { |
| EXPECT_EQ(expect_continues, continue_count_) << "line " << line; |
| EXPECT_EQ(expect_cancels, cancel_count_) << "line " << line; |
| EXPECT_EQ(expect_asks, ask_allow_count_) << "line " << line; |
| continue_count_ = cancel_count_ = ask_allow_count_ = 0; |
| } |
| |
| protected: |
| void ContinueDownload(bool allow) { |
| if (allow) { |
| continue_count_++; |
| } else { |
| cancel_count_++; |
| } |
| } |
| |
| void SetHostContentSetting(WebContents* contents, ContentSetting setting) { |
| content_settings_->SetContentSetting( |
| ContentSettingsPattern::FromURL(contents->GetURL()), |
| ContentSettingsPattern::Wildcard(), |
| CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, |
| std::string(), |
| setting); |
| } |
| |
| scoped_refptr<DownloadRequestLimiter> download_request_limiter_; |
| |
| // The action that FakeCreate() should take. |
| TestingAction testing_action_; |
| |
| // Number of times ContinueDownload was invoked. |
| int continue_count_; |
| |
| // Number of times CancelDownload was invoked. |
| int cancel_count_; |
| |
| // Number of times ShouldAllowDownload was invoked. |
| int ask_allow_count_; |
| |
| scoped_refptr<HostContentSettingsMap> content_settings_; |
| |
| private: |
| DownloadRequestInfoBarDelegate::FakeCreateCallback fake_create_callback_; |
| TestingProfile profile_; |
| scoped_ptr<FakePermissionBubbleView> view_; |
| }; |
| |
| void FakePermissionBubbleView::Show( |
| const std::vector<PermissionBubbleRequest*>& requests, |
| const std::vector<bool>& accept_state, |
| bool customization_mode) { |
| test_->AskAllow(); |
| int action = test_->GetAction(); |
| if (action == DownloadRequestLimiterTest::ACCEPT) { |
| delegate_->Accept(); |
| } else if (action == DownloadRequestLimiterTest::CANCEL) { |
| delegate_->Deny(); |
| } else if (action == DownloadRequestLimiterTest::WAIT) { |
| // do nothing. |
| } else { |
| delegate_->Closing(); |
| } |
| } |
| |
| TEST_F(DownloadRequestLimiterTest, |
| DownloadRequestLimiter_Allow) { |
| // All tabs should initially start at ALLOW_ONE_DOWNLOAD. |
| ASSERT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| // Ask if the tab can do a download. This moves to PROMPT_BEFORE_DOWNLOAD. |
| CanDownload(); |
| ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| // We should have been told we can download. |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| |
| // Ask again. This triggers asking the delegate for allow/disallow. |
| testing_action_ = ACCEPT; |
| CanDownload(); |
| // This should ask us if the download is allowed. |
| // We should have been told we can download. |
| ExpectAndResetCounts(1, 0, 1, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| // Ask again and make sure continue is invoked. |
| CanDownload(); |
| // The state is at allow_all, which means the delegate shouldn't be asked. |
| // We should have been told we can download. |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| } |
| |
| TEST_F(DownloadRequestLimiterTest, |
| DownloadRequestLimiter_ResetOnNavigation) { |
| NavigateAndCommit(GURL("http://foo.com/bar")); |
| |
| // Do two downloads, allowing the second so that we end up with allow all. |
| CanDownload(); |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| testing_action_ = ACCEPT; |
| CanDownload(); |
| ExpectAndResetCounts(1, 0, 1, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| // Navigate to a new URL with the same host, which shouldn't reset the allow |
| // all state. |
| NavigateAndCommit(GURL("http://foo.com/bar2")); |
| CanDownload(); |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| // Do a user gesture, because we're at allow all, this shouldn't change the |
| // state. |
| OnUserGesture(); |
| ASSERT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| // Navigate to a completely different host, which should reset the state. |
| NavigateAndCommit(GURL("http://fooey.com")); |
| ASSERT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| // Do two downloads, allowing the second so that we end up with allow all. |
| CanDownload(); |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| testing_action_ = CANCEL; |
| CanDownload(); |
| ExpectAndResetCounts(0, 1, 1, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| // Navigate to a new URL with the same host, which shouldn't reset the allow |
| // all state. |
| NavigateAndCommit(GURL("http://fooey.com/bar2")); |
| CanDownload(); |
| ExpectAndResetCounts(0, 1, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| } |
| |
| TEST_F(DownloadRequestLimiterTest, |
| DownloadRequestLimiter_ResetOnUserGesture) { |
| NavigateAndCommit(GURL("http://foo.com/bar")); |
| |
| // Do one download, which should change to prompt before download. |
| CanDownload(); |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| // Do a user gesture, which should reset back to allow one. |
| OnUserGesture(); |
| ASSERT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| // Ask twice, which triggers calling the delegate. Don't allow the download |
| // so that we end up with not allowed. |
| CanDownload(); |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| testing_action_ = CANCEL; |
| CanDownload(); |
| ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| ExpectAndResetCounts(0, 1, 1, __LINE__); |
| |
| // A user gesture now should NOT change the state. |
| OnUserGesture(); |
| ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| // And make sure we really can't download. |
| CanDownload(); |
| ExpectAndResetCounts(0, 1, 0, __LINE__); |
| // And the state shouldn't have changed. |
| ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| } |
| |
| TEST_F(DownloadRequestLimiterTest, |
| DownloadRequestLimiter_ResetOnReload) { |
| NavigateAndCommit(GURL("http://foo.com/bar")); |
| ASSERT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| // If the user refreshes the page without responding to the infobar, pretend |
| // like the refresh is the initial load: they get 1 free download (probably |
| // the same as the actual initial load), then an infobar. |
| testing_action_ = WAIT; |
| |
| CanDownload(); |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| CanDownload(); |
| ExpectAndResetCounts(0, 0, 1, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| AboutToNavigateRenderView(); |
| base::RunLoop().RunUntilIdle(); |
| ExpectAndResetCounts(0, 1, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| CanDownload(); |
| ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| |
| testing_action_ = CANCEL; |
| CanDownload(); |
| ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| ExpectAndResetCounts(0, 1, 1, __LINE__); |
| |
| AboutToNavigateRenderView(); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| CanDownload(); |
| ExpectAndResetCounts(0, 1, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| } |
| |
| TEST_F(DownloadRequestLimiterTest, |
| DownloadRequestLimiter_RawWebContents) { |
| scoped_ptr<WebContents> web_contents(CreateTestWebContents()); |
| |
| // DownloadRequestLimiter won't try to make a permission bubble if there's |
| // no permission bubble manager, so don't put one on the test WebContents. |
| |
| // DownloadRequestLimiter won't try to make an infobar if it doesn't have an |
| // InfoBarService, and we want to test that it will Cancel() instead of |
| // prompting when it doesn't have a InfoBarService, so unset the delegate. |
| UnsetDelegate(); |
| ExpectAndResetCounts(0, 0, 0, __LINE__); |
| EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents.get())); |
| // You get one freebie. |
| CanDownloadFor(web_contents.get()); |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents.get())); |
| OnUserGestureFor(web_contents.get()); |
| EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents.get())); |
| CanDownloadFor(web_contents.get()); |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents.get())); |
| CanDownloadFor(web_contents.get()); |
| ExpectAndResetCounts(0, 1, 0, __LINE__); |
| EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED, |
| download_request_limiter_->GetDownloadStatus(web_contents.get())); |
| OnUserGestureFor(web_contents.get()); |
| EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents.get())); |
| CanDownloadFor(web_contents.get()); |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents.get())); |
| } |
| |
| TEST_F(DownloadRequestLimiterTest, |
| DownloadRequestLimiter_SetHostContentSetting) { |
| NavigateAndCommit(GURL("http://foo.com/bar")); |
| SetHostContentSetting(web_contents(), CONTENT_SETTING_ALLOW); |
| |
| CanDownload(); |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| CanDownload(); |
| ExpectAndResetCounts(1, 0, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| SetHostContentSetting(web_contents(), CONTENT_SETTING_BLOCK); |
| |
| CanDownload(); |
| ExpectAndResetCounts(0, 1, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| |
| CanDownload(); |
| ExpectAndResetCounts(0, 1, 0, __LINE__); |
| ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, |
| download_request_limiter_->GetDownloadStatus(web_contents())); |
| } |