910e62b5创建于 1月15日历史提交
// Copyright 2012 The Chromium Authors
// 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 <memory>

#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/download/download_permission_request.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/permissions/features.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/test/mock_permission_prompt_factory.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/input/web_gesture_event.h"
#include "third_party/blink/public/common/input/web_keyboard_event.h"
#include "third_party/blink/public/common/input/web_mouse_event.h"
#include "third_party/blink/public/common/input/web_touch_event.h"

#if BUILDFLAG(IS_ANDROID)
#include "chrome/browser/flags/android/chrome_feature_list.h"
#endif

using content::WebContents;

namespace {
enum TestingAction {
  ACCEPT,
  CANCEL,
  WAIT
};
}  // namespace

class DownloadRequestLimiterTest : public ChromeRenderViewHostTestHarness {
 public:
  void SetUp() override {
    ChromeRenderViewHostTestHarness::SetUp();

    permissions::PermissionRequestManager::CreateForWebContents(web_contents());
    permissions::PermissionRequestManager* manager =
        permissions::PermissionRequestManager::FromWebContents(web_contents());
    mock_permission_prompt_factory_ =
        std::make_unique<permissions::MockPermissionPromptFactory>(manager);

    UpdateExpectations(ACCEPT);
    cancel_count_ = continue_count_ = 0;
    download_request_limiter_ = new DownloadRequestLimiter();
  }

  void TearDown() override {
    mock_permission_prompt_factory_.reset();
    ChromeRenderViewHostTestHarness::TearDown();
  }

  void CanDownload(const GURL& url) { CanDownloadFor(url, web_contents()); }

  void CanDownloadFor(const GURL& url, WebContents* web_contents) {
    CanDownloadFor(url, web_contents, std::nullopt);
  }

  void CanDownloadFor(const GURL& url,
                      WebContents* web_contents,
                      std::optional<url::Origin> origin) {
    download_request_limiter_->CanDownloadImpl(
        url, web_contents,
        "GET",  // request method
        std::move(origin),
        false,  // from_download_cross_origin_redirect
        base::BindOnce(&DownloadRequestLimiterTest::ContinueDownload,
                       base::Unretained(this)));
    base::RunLoop().RunUntilIdle();
  }

  void OnUserInteraction(blink::WebInputEvent::Type type) {
    OnUserInteractionFor(web_contents(), type);
  }

  void OnUserInteractionFor(WebContents* web_contents,
                            blink::WebInputEvent::Type type) {
    DownloadRequestLimiter::TabDownloadState* state =
        download_request_limiter_->GetDownloadState(web_contents, false);
    if (!state)
      return;
    std::unique_ptr<blink::WebInputEvent> event;
    switch (type) {
      case blink::WebInputEvent::Type::kRawKeyDown:
        event = std::make_unique<blink::WebKeyboardEvent>();
        break;
      case blink::WebInputEvent::Type::kGestureScrollBegin:
        event = std::make_unique<blink::WebGestureEvent>();
        break;
      case blink::WebInputEvent::Type::kMouseDown:
        event = std::make_unique<blink::WebMouseEvent>();
        break;
      case blink::WebInputEvent::Type::kTouchStart:
        event = std::make_unique<blink::WebTouchEvent>();
        break;
      default:
        NOTREACHED();
    }
    event->SetType(type);
    state->DidGetUserInteraction(*event);
  }

  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, AskAllowCount()) << "line " << line;
    continue_count_ = cancel_count_ = 0;
    mock_permission_prompt_factory_->ResetCounts();
  }

  void UpdateContentSettings(WebContents* web_contents,
                             ContentSetting setting) {
    // Ensure a download state exists.
    download_request_limiter_->GetDownloadState(web_contents, true);
    SetHostContentSetting(web_contents, setting);
  }

 protected:
  const GURL kTestURL = GURL("http://foo.com/bar");

  void ContinueDownload(bool allow) {
    if (allow) {
      continue_count_++;
    } else {
      cancel_count_++;
    }
  }

  void SetHostContentSetting(WebContents* contents, ContentSetting setting) {
    SetHostContentSetting(contents, contents->GetURL(), setting);
  }

  void SetHostContentSetting(WebContents* contents,
                             const GURL& host,
                             ContentSetting setting) {
    HostContentSettingsMapFactory::GetForProfile(
        Profile::FromBrowserContext(contents->GetBrowserContext()))
        ->SetContentSettingDefaultScope(
            host, GURL(), ContentSettingsType::AUTOMATIC_DOWNLOADS, setting);
  }

  void LoadCompleted() {
    mock_permission_prompt_factory_
        ->DocumentOnLoadCompletedInPrimaryMainFrame();
  }

  int AskAllowCount() { return mock_permission_prompt_factory_->show_count(); }

  void UpdateExpectations(TestingAction action) {
    // Set expectations for PermissionRequestManager.
    permissions::PermissionRequestManager::AutoResponseType response_type =
        permissions::PermissionRequestManager::DISMISS;
    switch (action) {
      case ACCEPT:
        response_type = permissions::PermissionRequestManager::ACCEPT_ALL;
        break;
      case CANCEL:
        response_type = permissions::PermissionRequestManager::DENY_ALL;
        break;
      case WAIT:
        response_type = permissions::PermissionRequestManager::NONE;
        break;
    }
    mock_permission_prompt_factory_->set_response_type(response_type);
  }

  scoped_refptr<DownloadRequestLimiter> download_request_limiter_;

  // Number of times ContinueDownload was invoked.
  int continue_count_;

  // Number of times CancelDownload was invoked.
  int cancel_count_;

  std::unique_ptr<permissions::MockPermissionPromptFactory>
      mock_permission_prompt_factory_;
};

TEST_F(DownloadRequestLimiterTest, Allow) {
  NavigateAndCommit(kTestURL);
  LoadCompleted();

  // All tabs should initially start at ALLOW_ONE_DOWNLOAD and
  // DOWNLOAD_UI_DEFAULT.
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Ask if the tab can do a download. This moves to PROMPT_BEFORE_DOWNLOAD and
  // stays in DOWNLOAD_UI_DEFAULT.
  CanDownload(kTestURL);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(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.
  UpdateExpectations(ACCEPT);
  CanDownload(kTestURL);
  // This should ask us if the download is allowed. We should have been told we
  // can download, and the UI state should reflect this.
  ExpectAndResetCounts(1, 0, 1, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Ask again and make sure continue is invoked.
  CanDownload(kTestURL);
  // The state is at ALLOW_ALL_DOWNLOADS, which means the delegate shouldn't be
  // asked. We should have been told we can download.
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
}

TEST_F(DownloadRequestLimiterTest, ResetOnNavigation) {
  NavigateAndCommit(kTestURL);
  LoadCompleted();

  // Do two downloads, allowing the second so that we end up with allow all.
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  UpdateExpectations(ACCEPT);
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 1, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED,
            download_request_limiter_->GetDownloadUiStatus(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"));
  LoadCompleted();

  // We reset the UI state to DEFAULT on navigating prior to invoking a
  // download.
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // After resetting the state, the first download will always change the
  // state to prompt.
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Content settings will be checked for prompt status and change the UI state.
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  // Now we have triggered a download so the UI state reflects the
  // ALLOW_ALL_DOWNLOADS internal state.
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Do a user gesture, that will reset all the state if current state is not
  // DOWNLOADS_NOT_ALLOWED or ALLOW_ALL_DOWNLOADS or content setting is not
  // block.
  OnUserInteraction(blink::WebInputEvent::Type::kRawKeyDown);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Navigate to a completely different host, which should reset the state.
  GURL different_host("http://fooey.com");
  NavigateAndCommit(different_host);
  LoadCompleted();
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Do two downloads, blocking the second so that we end up with downloads not
  // allowed.
  CanDownload(different_host);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  UpdateExpectations(CANCEL);
  CanDownload(different_host);
  ExpectAndResetCounts(0, 1, 1, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Navigate to a new URL with the same host, which will reset the allowed
  // state.
  NavigateAndCommit(GURL("http://fooey.com/bar2"));
  LoadCompleted();

  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
  CanDownload(different_host);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Try downloading again and this should move to the blocked UI state.
  CanDownload(different_host);
  ExpectAndResetCounts(0, 1, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
}

TEST_F(DownloadRequestLimiterTest, RendererInitiated) {
  NavigateAndCommit(kTestURL);
  LoadCompleted();

  // Do one download so we end up in PROMPT.
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Set up a renderer-initiated navigation to the same host.
  content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://foo.com/bar2"), web_contents()->GetPrimaryMainFrame());
  LoadCompleted();

  // The state should not be reset.
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Renderer-initiated nav to a different host shouldn't reset the state.
  content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://fooey.com/bar"), web_contents()->GetPrimaryMainFrame());
  LoadCompleted();
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Set up a subframe. Navigations in the subframe shouldn't reset the state.
  content::RenderFrameHostTester* rfh_tester =
      content::RenderFrameHostTester::For(
          web_contents()->GetPrimaryMainFrame());
  content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe");
  subframe = content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://foo.com"), subframe);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  subframe = content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://foobargoo.com"), subframe);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Set up a blocked state.
  UpdateExpectations(CANCEL);
  CanDownload(kTestURL);
  ExpectAndResetCounts(0, 1, 1, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // The state should not be reset on a renderer-initiated load to either the
  // same host or a different host, in either the main frame or the subframe.
  // The UI state goes to DEFAULT until an actual download is triggered.
  content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://fooey.com/bar2"), web_contents()->GetPrimaryMainFrame());
  LoadCompleted();
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://foo.com/bar"), web_contents()->GetPrimaryMainFrame());
  LoadCompleted();
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  rfh_tester = content::RenderFrameHostTester::For(
      web_contents()->GetPrimaryMainFrame());
  subframe = rfh_tester->AppendChild("subframe");
  subframe = content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://foo.com"), subframe);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  subframe = content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://foobarfoo.com"), subframe);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Browser-initiated navigation to a different host, which should reset the
  // state.
  GURL different_host("http://foobar.com");
  NavigateAndCommit(different_host);
  LoadCompleted();
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Set up an allow all state.
  CanDownload(different_host);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  UpdateExpectations(ACCEPT);
  // Trigger 1 download, this causes all following renderer-intiated
  // download to prompt for user approval.
  CanDownload(different_host);
  ExpectAndResetCounts(1, 0, 1, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // The state should not be reset on a pending renderer-initiated load to
  // the same host.
  content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://foobar.com/bar"), web_contents()->GetPrimaryMainFrame());
  LoadCompleted();
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // The state should not be reset for a subframe nav to the same host.
  rfh_tester = content::RenderFrameHostTester::For(
      web_contents()->GetPrimaryMainFrame());
  subframe = rfh_tester->AppendChild("subframe");
  subframe = content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://foobar.com/bar"), subframe);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  subframe = content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://foobarfoo.com/"), subframe);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Even a pending load to a different host in the main frame should not
  // reset the state.
  content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://foo.com"), web_contents()->GetPrimaryMainFrame());
  LoadCompleted();
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
  // Since a download is allowed earlier, a new download will prompt user.
  CanDownload(different_host);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
}

// Test that history back will not change the tab download state if all the
// previous navigations are renderer-initiated.
TEST_F(DownloadRequestLimiterTest, HistoryBack) {
  NavigateAndCommit(kTestURL);
  LoadCompleted();

  // Do one download so we end up in PROMPT.
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Renderer-initiated navigation to a different host shouldn't reset the
  // state.
  content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://foobar.com/bar"), web_contents()->GetPrimaryMainFrame());
  LoadCompleted();
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // History back shouldn't reset the state, either.
  auto backward_navigation =
      content::NavigationSimulator::CreateHistoryNavigation(
          -1 /* Offset */, web_contents(), false /* is_renderer_initiated */);
  backward_navigation->Start();
  backward_navigation->Commit();
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Browser-initiated navigation to a different host, which will not reset the
  // state either.
  NavigateAndCommit(GURL("http://foobar.com"));
  LoadCompleted();
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // History back should use the old download state, as one of the origin
  // is in a restricted state.
  backward_navigation = content::NavigationSimulator::CreateHistoryNavigation(
      -1 /* Offset */, web_contents(), false /* is_renderer_initiated */);
  backward_navigation->Start();
  backward_navigation->Commit();
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
}

// Tab download state shouldn't change when forward/back between to a
// renderer-initiated page.
TEST_F(DownloadRequestLimiterTest, HistoryForwardBack) {
  NavigateAndCommit(kTestURL);
  LoadCompleted();

  // Do one download so we end up in PROMPT.
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Renderer-initiated navigation to a different host shouldn't reset the
  // state.
  content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://foobar.com/bar"), web_contents()->GetPrimaryMainFrame());
  LoadCompleted();
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // History back shouldn't reset the state, either.
  auto backward_navigation =
      content::NavigationSimulator::CreateHistoryNavigation(
          -1 /* Offset */, web_contents(), false /* is_renderer_initiated */);
  backward_navigation->Start();
  backward_navigation->Commit();
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // History forward shouldn't reset the state, as the host is encountered
  // before.
  auto forward_navigation =
      content::NavigationSimulator::CreateHistoryNavigation(
          1 /* Offset */, web_contents(), false /* is_renderer_initiated */);
  forward_navigation->Start();
  forward_navigation->Commit();
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // History backward again, nothing should change.
  backward_navigation = content::NavigationSimulator::CreateHistoryNavigation(
      -1 /* Offset */, web_contents(), false /* is_renderer_initiated */);
  backward_navigation->Start();
  backward_navigation->Commit();
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
}

TEST_F(DownloadRequestLimiterTest, DownloadRequestLimiter_ResetOnUserGesture) {
  NavigateAndCommit(kTestURL);
  LoadCompleted();

  // Do one download, which should change to prompt before download.
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Do a user gesture with scroll, which should be ignored.
  OnUserInteraction(blink::WebInputEvent::Type::kGestureScrollBegin);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
  // Do a user gesture with mouse click, which should reset back to allow one.
  OnUserInteraction(blink::WebInputEvent::Type::kMouseDown);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Do one download, which should change to prompt before download.
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Do a touch event, which should reset back to allow one.
  OnUserInteraction(blink::WebInputEvent::Type::kTouchStart);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Do one download, which should change to prompt before download.
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Do a user gesture with keyboard down, which should reset back to allow one.
  OnUserInteraction(blink::WebInputEvent::Type::kRawKeyDown);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Ask twice, which triggers calling the delegate. Don't allow the download
  // so that we end up with not allowed.
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  UpdateExpectations(CANCEL);
  CanDownload(kTestURL);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
  ExpectAndResetCounts(0, 1, 1, __LINE__);

  // A user gesture now should NOT change the state.
  OnUserInteraction(blink::WebInputEvent::Type::kMouseDown);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
  // And make sure we really can't download.
  CanDownload(kTestURL);
  ExpectAndResetCounts(0, 1, 0, __LINE__);
  // And the state shouldn't have changed.
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
}

TEST_F(DownloadRequestLimiterTest, ResetOnReload) {
  // This is a regression test for https://crbug.com/110707.
  NavigateAndCommit(kTestURL);
  LoadCompleted();
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // A reload should keep us in ALLOW_ONE_DOWNLOAD.
  content::NavigationSimulator::Reload(web_contents());
  LoadCompleted();
  base::RunLoop().RunUntilIdle();
  ExpectAndResetCounts(0, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // If the user refreshes the page without responding to the prompt, pretend
  // like the refresh is the initial load: they get 1 free download (probably
  // the same as the actual initial load), then a prompt.
  UpdateExpectations(WAIT);

  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  CanDownload(kTestURL);
  ExpectAndResetCounts(0, 0, 1, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Reload the page again.
  content::NavigationSimulator::Reload(web_contents());
  LoadCompleted();
  base::RunLoop().RunUntilIdle();
  ExpectAndResetCounts(0, 1, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));

  // After a browser-initiated reload, we return to the default UI state until
  // we see a download.
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  CanDownload(kTestURL);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
  ExpectAndResetCounts(1, 0, 0, __LINE__);

  UpdateExpectations(CANCEL);
  CanDownload(kTestURL);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
  ExpectAndResetCounts(0, 1, 1, __LINE__);

  // Reload should reset DOWNLOADS_NOT_ALLOWED status.
  content::NavigationSimulator::Reload(web_contents());
  LoadCompleted();
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Download again will fail as content setting is already set to block.
  CanDownload(kTestURL);
  ExpectAndResetCounts(0, 1, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Renderer initiated reload will not reset download status.
  content::NavigationSimulator::NavigateAndCommitFromDocument(
      GURL("http://foo.com/bar"), web_contents()->GetPrimaryMainFrame());
  LoadCompleted();
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
}

TEST_F(DownloadRequestLimiterTest, RawWebContents) {
  std::unique_ptr<WebContents> web_contents(CreateTestWebContents());

  web_contents->GetController().LoadURL(
      kTestURL, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());

  // DownloadRequestLimiter won't try to make a permission request or infobar
  // if there is no PermissionRequestManager, and we want to test that it will
  // CancelOnce() instead of prompting.
  ExpectAndResetCounts(0, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents.get()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents.get()));

  // You get one freebie.
  CanDownloadFor(kTestURL, web_contents.get());
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents.get()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents.get()));

  OnUserInteractionFor(web_contents.get(),
                       blink::WebInputEvent::Type::kTouchStart);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents.get()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents.get()));

  CanDownloadFor(kTestURL, web_contents.get());
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents.get()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents.get()));

  CanDownloadFor(kTestURL, web_contents.get());
  ExpectAndResetCounts(0, 1, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents.get()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED,
            download_request_limiter_->GetDownloadUiStatus(web_contents.get()));

  OnUserInteractionFor(web_contents.get(),
                       blink::WebInputEvent::Type::kRawKeyDown);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents.get()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents.get()));

  CanDownloadFor(kTestURL, web_contents.get());
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents.get()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents.get()));
}

TEST_F(DownloadRequestLimiterTest, SetHostContentSetting) {
  NavigateAndCommit(kTestURL);
  LoadCompleted();
  SetHostContentSetting(web_contents(), CONTENT_SETTING_ALLOW);

  // The content setting will be checked first and overwrite the
  // ALLOW_ONE_DOWNLOAD default status.
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));

  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));

  SetHostContentSetting(web_contents(), CONTENT_SETTING_BLOCK);

  CanDownload(kTestURL);
  ExpectAndResetCounts(0, 1, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));

  CanDownload(kTestURL);
  ExpectAndResetCounts(0, 1, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
}

TEST_F(DownloadRequestLimiterTest, ContentSettingChanged) {
  NavigateAndCommit(kTestURL);
  LoadCompleted();
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));

  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));

  // Simulate an accidental deny.
  UpdateExpectations(CANCEL);
  CanDownload(kTestURL);
  ExpectAndResetCounts(0, 1, 1, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));

  // Set the content setting to allow and send the notification. Ensure that the
  // limiter states update to match.
  UpdateContentSettings(web_contents(), CONTENT_SETTING_ALLOW);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));

  // Ask to download, and assert that it succeeded and we are still in allow.
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));

  // Set the content setting to block and send the notification. Ensure that the
  // limiter states updates to match.
  UpdateContentSettings(web_contents(), CONTENT_SETTING_BLOCK);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));

  // Ensure downloads are blocked.
  CanDownload(kTestURL);
  ExpectAndResetCounts(0, 1, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));

  // Reset to ask. Verify that the download counts have not changed on the
  // content settings change (ensuring there is no "free" download after
  // changing the content setting).
  UpdateContentSettings(web_contents(), CONTENT_SETTING_ASK);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  UpdateExpectations(WAIT);
  CanDownload(kTestURL);
  ExpectAndResetCounts(0, 0, 1, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
}

// Test that renderer initiated download from another origin are handled
// properly.
TEST_F(DownloadRequestLimiterTest, RendererInitiatedDownloadFromAnotherOrigin) {
  NavigateAndCommit(kTestURL);
  LoadCompleted();

  // Sets the content setting to block for another origin.
  SetHostContentSetting(web_contents(), GURL("http://foobar.com"),
                        CONTENT_SETTING_BLOCK);

  // Trigger a renderer initiated download from the other origin.
  CanDownloadFor(kTestURL, web_contents(),
                 url::Origin::Create(GURL("http://foobar.com")));
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // The current tab is affected, will prompt for download. After accepting
  // the prompt, the current tab should be able to download more files.
  CanDownloadFor(kTestURL, web_contents());
  ExpectAndResetCounts(1, 0, 1, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Change the content setting to allow for the other origin.
  SetHostContentSetting(web_contents(), GURL("http://foobar.com"),
                        CONTENT_SETTING_ALLOW);
  CanDownloadFor(kTestURL, web_contents(),
                 url::Origin::Create(GURL("http://foobar.com")));
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Trigger another download in the current tab, since the tab is already
  // prompted, the new download will go through.
  CanDownloadFor(kTestURL, web_contents());
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Download should proceed for the other origin.
  CanDownloadFor(kTestURL, web_contents(),
                 url::Origin::Create(GURL("http://foobar.com")));
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
}

// Test that user interaction on the current page won't reset download status
// for another origin.
TEST_F(DownloadRequestLimiterTest,
       DownloadStatusForOtherOriginsNotResetOnUserInteraction) {
  NavigateAndCommit(kTestURL);
  LoadCompleted();

  // Trigger a renderer initiated download from the other origin.
  CanDownloadFor(kTestURL, web_contents(),
                 url::Origin::Create(GURL("http://foobar.com")));
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // The current tab is affected, will prompt for download.
  UpdateExpectations(CANCEL);
  CanDownloadFor(kTestURL, web_contents());
  ExpectAndResetCounts(0, 1, 1, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // On user interaction, since the content setting for the main origin is to
  // block, download is not allowed.
  OnUserInteraction(blink::WebInputEvent::Type::kTouchStart);
  CanDownloadFor(kTestURL, web_contents());
  ExpectAndResetCounts(0, 1, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Download status from the other origin should reset.
  CanDownloadFor(kTestURL, web_contents(),
                 url::Origin::Create(GURL("http://foobar.com")));
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  UpdateExpectations(CANCEL);
  CanDownloadFor(kTestURL, web_contents(),
                 url::Origin::Create(GURL("http://foobar.com")));
  ExpectAndResetCounts(0, 1, 1, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
}

// Test that renderer initiated download from opaque origins are correctly
// limited.
TEST_F(DownloadRequestLimiterTest, OpaqueOrigins) {
  // about:blank is an opaque origin.
  NavigateAndCommit(GURL("about:blank"));
  LoadCompleted();

  // Create another opaque origin that will trigger all the download.
  url::Origin origin;
  // The first download should go through.
  CanDownloadFor(kTestURL, web_contents(), origin);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // The 2nd download will be canceled, there is no prompt since the origin
  // is opaque.
  CanDownloadFor(kTestURL, web_contents(), origin);
  ExpectAndResetCounts(0, 1, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Trigger another download from about:blank, that should prompt user
  // as one download already takes place.
  CanDownloadFor(kTestURL, web_contents());
  ExpectAndResetCounts(1, 0, 1, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
}

TEST_F(DownloadRequestLimiterTest,
       EmptyRequestInitiator_BrowserInitiatedDownload) {
  NavigateAndCommit(kTestURL);
  LoadCompleted();

  // Trigger a browser initiated download from a different host.
  GURL download_url("http://test.com/abc");
  CanDownload(download_url);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Set the content setting for the new host to block, so download will
  // be blocked.
  SetHostContentSetting(web_contents(), GURL("http://test.com"),
                        CONTENT_SETTING_BLOCK);
  CanDownload(download_url);
  ExpectAndResetCounts(0, 1, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
}

TEST_F(DownloadRequestLimiterTest,
       EmptyRequestInitiator_DownloadWithOpaqueOrigin) {
  NavigateAndCommit(kTestURL);
  LoadCompleted();

  // Trigger a browser initiated download from an opaque origin.
  GURL download_url("about:blank");
  CanDownload(download_url);
  ExpectAndResetCounts(1, 0, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  // Do one more download and it should get canceled silently.
  CanDownload(download_url);
  ExpectAndResetCounts(0, 1, 0, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));

  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 1, __LINE__);
  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
            download_request_limiter_->GetDownloadStatus(web_contents()));
  EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED,
            download_request_limiter_->GetDownloadUiStatus(web_contents()));
}

// Verify AUTOMATIC_DOWNLOADS permission granted through Permission Prompt
// is correctly marked as eligible (i.e. `last_visited` timestamp is tracked)
// for Safety Hub auto-revocation when the
// kSafetyHubUnusedPermissionRevocationForAllSurfaces flag is enabled.
TEST_F(DownloadRequestLimiterTest, SetContentSetting_LastVisited_Tracked) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(
      permissions::features::
          kSafetyHubUnusedPermissionRevocationForAllSurfaces);

  NavigateAndCommit(kTestURL);
  LoadCompleted();

  // Simulate the first download that is always allowed (expect_continues = 1).
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);

  // Simulate the second download attempt that should prompt the user to choose
  // whether to allow or not consequent downloads (expect_asks = 1).
  // User chooses to ALLOW (expect_continues = 1).
  UpdateExpectations(ACCEPT);
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 1, __LINE__);

  // Verify that `last_visited` was recorded and lies within the past 7 days.
  //
  // The `last_visited` is coarsed by `GetCoarseVisitedTime` [1] due to privacy.
  // It rounds given timestamp down to the nearest multiple of 7 in the past.
  // [1] components/content_settings/core/browser/content_settings_utils.cc
  base::Time now = base::Time::Now();
  content_settings::SettingInfo info;
  HostContentSettingsMap* hcsm = HostContentSettingsMapFactory::GetForProfile(
      Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
  hcsm->GetWebsiteSetting(kTestURL, kTestURL,
                          ContentSettingsType::AUTOMATIC_DOWNLOADS, &info);
  EXPECT_GE(info.metadata.last_visited(), now - base::Days(7));
  EXPECT_LE(info.metadata.last_visited(), now);
}

// Verify AUTOMATIC_DOWNLOADS permission blocked through Permission Prompt
// is not marked as eligible (i.e. `last_visited` timestamp is tracked)
// for Safety Hub auto-revocation even when the
// kSafetyHubUnusedPermissionRevocationForAllSurfaces flag is enabled.
TEST_F(DownloadRequestLimiterTest,
       SetContentSetting_LastVisited_NotTracked_WrongValue) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(
      permissions::features::
          kSafetyHubUnusedPermissionRevocationForAllSurfaces);

  NavigateAndCommit(kTestURL);
  LoadCompleted();

  // Simulate the first download that is always allowed (expect_continues = 1).
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);

  // Simulate the second download attempt that should prompt the user to choose
  // whether to allow or not consequent downloads (expect_asks = 1).
  // User chooses to BLOCK (expect_cancels = 1).
  UpdateExpectations(CANCEL);
  CanDownload(kTestURL);
  ExpectAndResetCounts(0, 1, 1, __LINE__);

  // Verify that `last_visited` is not recorded unless the value is ALLOW.
  content_settings::SettingInfo info;
  HostContentSettingsMap* hcsm = HostContentSettingsMapFactory::GetForProfile(
      Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
  hcsm->GetWebsiteSetting(kTestURL, kTestURL,
                          ContentSettingsType::AUTOMATIC_DOWNLOADS, &info);
  EXPECT_EQ(base::Time(), info.metadata.last_visited());
}

// Verify AUTOMATIC_DOWNLOADS permission granted through Permission Prompt
// is not marked as eligible (i.e. `last_visited` timestamp is tracked)
// for Safety Hub auto-revocation because the
// kSafetyHubUnusedPermissionRevocationForAllSurfaces flag is disabled.
TEST_F(DownloadRequestLimiterTest,
       SetContentSetting_LastVisited_NotTracked_FeatureOff) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(
      permissions::features::
          kSafetyHubUnusedPermissionRevocationForAllSurfaces);

  NavigateAndCommit(kTestURL);
  LoadCompleted();

  // Simulate the first download that is always allowed (expect_continues = 1).
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 0, __LINE__);

  // Simulate the second download attempt that should prompt the user to choose
  // whether to allow or not consequent downloads (expect_asks = 1).
  // User chooses to ALLOW (expect_continues = 1).
  UpdateExpectations(ACCEPT);
  CanDownload(kTestURL);
  ExpectAndResetCounts(1, 0, 1, __LINE__);

  // Verify that `last_visited` is not recorded when the feature is off.
  content_settings::SettingInfo info;
  HostContentSettingsMap* hcsm = HostContentSettingsMapFactory::GetForProfile(
      Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
  hcsm->GetWebsiteSetting(kTestURL, kTestURL,
                          ContentSettingsType::AUTOMATIC_DOWNLOADS, &info);
  EXPECT_EQ(base::Time(), info.metadata.last_visited());
}