// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/permissions/permission_request_manager.h"

#include <memory>

#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/run_loop.h"
#include "base/test/mock_callback.h"
#include "base/test/run_until.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/values_test_util.h"
#include "build/build_config.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/download/download_permission_request.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/omnibox/omnibox_next_features.h"
#include "chrome/browser/ui/tabs/split_tab_metrics.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
#include "chrome/browser/ui/test/test_browser_dialog.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/permissions/permission_request_manager_test_api.h"
#include "components/back_forward_cache/back_forward_cache_disable.h"
#include "components/content_settings/browser/page_specific_content_settings.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/content_settings/core/common/features.h"
#include "components/custom_handlers/register_protocol_handler_permission_request.h"
#include "components/permissions/content_setting_permission_context_base.h"
#include "components/permissions/permission_request.h"
#include "components/permissions/permission_uma_util.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/prediction_service/permission_ui_selector.h"
#include "components/permissions/prediction_service/prediction_service_messages.pb.h"
#include "components/permissions/request_type.h"
#include "components/permissions/resolvers/permission_prompt_options.h"
#include "components/permissions/test/mock_permission_prompt_factory.h"
#include "components/permissions/test/mock_permission_request.h"
#include "components/permissions/test/mock_permission_ui_selector.h"
#include "components/permissions/test/permission_request_observer.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_descriptor_util.h"
#include "content/public/browser/permission_result.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/common/content_features.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "third_party/blink/public/mojom/permissions/permission_status.mojom-shared.h"
#include "url/gurl.h"
#include "url/origin.h"

namespace {
using PredictionGrantLikelihood =
    permissions::PermissionUiSelector::PredictionGrantLikelihood;
using ::permissions::PermissionRequestRelevance;

using ::testing::_;
using ::testing::DoAll;
using ::testing::IsEmpty;
using ::testing::Optional;
using ::testing::SaveArg;

const char* kPermissionsKillSwitchFieldStudy = permissions::
    ContentSettingPermissionContextBase::kPermissionsKillSwitchFieldStudy;
const char* kPermissionsKillSwitchBlockedValue = permissions::
    ContentSettingPermissionContextBase::kPermissionsKillSwitchBlockedValue;
const char kPermissionsKillSwitchTestGroup[] = "TestGroup";

class PermissionRequestManagerBrowserTestBase : public InProcessBrowserTest {
 public:
  PermissionRequestManagerBrowserTestBase() = default;

  void SetUpOnMainThread() override {
    InProcessBrowserTest::SetUpOnMainThread();
    host_resolver()->AddRule("*", "127.0.0.1");
  }

  permissions::PermissionRequestManager* GetPermissionRequestManager() {
    return permissions::PermissionRequestManager::FromWebContents(
        browser()->GetTabStripModel()->GetActiveWebContents());
  }

  content::RenderFrameHost* GetActiveMainFrame() {
    return browser()
        ->GetTabStripModel()
        ->GetActiveWebContents()
        ->GetPrimaryMainFrame();
  }

  content::PermissionResult RequestPermissionFromDocumentSync(
      content::RenderFrameHost* rfh,
      blink::mojom::PermissionDescriptorPtr permission_descriptor) {
    return RequestPermissionFromDocumentSync(
        rfh,
        content::PermissionRequestDescription(std::move(permission_descriptor),
                                              /*user_gesture=*/true));
  }

  content::PermissionResult RequestPermissionFromDocumentSync(
      content::RenderFrameHost* rfh,
      content::PermissionRequestDescription request_description) {
    base::RunLoop run_loop;
    base::MockOnceCallback<void(content::PermissionResult)> callback;
    content::PermissionResult result;
    EXPECT_CALL(callback, Run(_)).WillOnce(DoAll(SaveArg<0>(&result), [&] {
      run_loop.Quit();
    }));

    browser()
        ->GetProfile()
        ->GetPermissionController()
        ->RequestPermissionFromCurrentDocument(
            rfh, std::move(request_description), callback.Get());

    run_loop.Run();
    return result;
  }
};

class PermissionRequestManagerBrowserTest
    : public PermissionRequestManagerBrowserTestBase {
 public:
  PermissionRequestManagerBrowserTest()
      : PermissionRequestManagerBrowserTest({}, {}) {}

  PermissionRequestManagerBrowserTest(
      const PermissionRequestManagerBrowserTest&) = delete;
  PermissionRequestManagerBrowserTest& operator=(
      const PermissionRequestManagerBrowserTest&) = delete;

  ~PermissionRequestManagerBrowserTest() override = default;

 protected:
  PermissionRequestManagerBrowserTest(
      const std::vector<base::test::FeatureRef>& enabled_features,
      const std::vector<base::test::FeatureRef>& disabled_features) {
    std::vector<base::test::FeatureRef> all_disabled_features =
        disabled_features;
    all_disabled_features.push_back(
        permissions::features::kBackForwardCacheUnblockPermissionRequest);
    scoped_feature_list_.InitWithFeatures(enabled_features,
                                          all_disabled_features);
  }

 public:
  void SetUpOnMainThread() override {
    PermissionRequestManagerBrowserTestBase::SetUpOnMainThread();
    permissions::PermissionRequestManager* manager =
        GetPermissionRequestManager();
    mock_permission_prompt_factory_ =
        std::make_unique<permissions::MockPermissionPromptFactory>(manager);
  }

  void TearDownOnMainThread() override {
    PermissionRequestManagerBrowserTestBase::TearDownOnMainThread();
    ShutDownFirstTabMockPermissionPromptFactory();
  }

  void ShutDownFirstTabMockPermissionPromptFactory() {
    mock_permission_prompt_factory_.reset();
  }

  permissions::MockPermissionPromptFactory* bubble_factory() {
    return mock_permission_prompt_factory_.get();
  }

  void EnableKillSwitch(ContentSettingsType content_settings_type) {
    std::map<std::string, std::string> params;
    params[permissions::PermissionUtil::GetPermissionString(
        content_settings_type)] = kPermissionsKillSwitchBlockedValue;
    base::AssociateFieldTrialParams(kPermissionsKillSwitchFieldStudy,
                                    kPermissionsKillSwitchTestGroup, params);
    base::FieldTrialList::CreateFieldTrial(kPermissionsKillSwitchFieldStudy,
                                           kPermissionsKillSwitchTestGroup);
  }

  void TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
      void navigation_action(content::WebContents*, const GURL&),
      bool expect_cooldown) {
    ASSERT_TRUE(embedded_test_server()->Start());

    const GURL kInitialURL = embedded_test_server()->GetURL(
        "a.localhost", "/permissions/killswitch_tester.html");
    const GURL kSecondURL = embedded_test_server()->GetURL(
        "b.localhost", "/permissions/killswitch_tester.html");
    const GURL kThirdURL = embedded_test_server()->GetURL(
        "c.localhost", "/permissions/killswitch_tester.html");

    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kInitialURL));
    bubble_factory()->ResetCounts();
    bubble_factory()->set_response_type(
        permissions::PermissionRequestManager::AutoResponseType::DENY_ALL);

    // Simulate a notification permission request that is denied by the user.
    content::WebContents* web_contents =
        browser()->GetTabStripModel()->GetActiveWebContents();
    ASSERT_EQ("denied",
              content::EvalJs(web_contents, "requestNotification();",
                              content::EXECUTE_SCRIPT_NO_USER_GESTURE));
    ASSERT_EQ(1, bubble_factory()->show_count());
    ASSERT_EQ(1, bubble_factory()->TotalRequestCount());

    // In response, simulate the website automatically triggering a
    // renderer-initiated cross-origin navigation without user gesture.
    content::TestNavigationObserver navigation_observer(web_contents);
    ASSERT_TRUE(content::ExecJs(
        web_contents, "window.location = \"" + kSecondURL.spec() + "\";",
        content::EXECUTE_SCRIPT_NO_USER_GESTURE));
    navigation_observer.Wait();

    bubble_factory()->ResetCounts();
    bubble_factory()->set_response_type(
        permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);

    // Request the notification permission again from a different origin.
    // Cross-origin permission prompt cool-down should be in effect.
    ASSERT_EQ("default",
              content::EvalJs(web_contents, "requestNotification();",
                              content::EXECUTE_SCRIPT_NO_USER_GESTURE));
    ASSERT_EQ(0, bubble_factory()->show_count());
    ASSERT_EQ(0, bubble_factory()->TotalRequestCount());

    // Now try one of a number other kinds of navigations, and request the
    // notification permission again.
    navigation_action(web_contents, kThirdURL);
    std::string result =
        content::EvalJs(web_contents, "requestNotification();",
                        content::EXECUTE_SCRIPT_NO_USER_GESTURE)
            .ExtractString();

    // Cross-origin prompt cool-down may or may not be in effect anymore
    // depending on the type of navigation.
    if (expect_cooldown) {
      EXPECT_EQ(0, bubble_factory()->show_count());
      EXPECT_EQ(0, bubble_factory()->TotalRequestCount());
      EXPECT_EQ("default", result);
    } else {
      EXPECT_EQ(1, bubble_factory()->show_count());
      EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
      EXPECT_EQ("granted", result);
    }
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
  std::unique_ptr<permissions::MockPermissionPromptFactory>
      mock_permission_prompt_factory_;
};

class PermissionRequestManagerWithBackForwardCacheBrowserTest
    : public PermissionRequestManagerBrowserTest {
 public:
  PermissionRequestManagerWithBackForwardCacheBrowserTest() {
    feature_list_.InitWithFeaturesAndParameters(
        content::GetDefaultEnabledBackForwardCacheFeaturesForTesting(),
        content::GetDefaultDisabledBackForwardCacheFeaturesForTesting());
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

class PermissionRequestManagerWithPrerenderingTest
    : public PermissionRequestManagerBrowserTest {
 public:
  PermissionRequestManagerWithPrerenderingTest()
      : prerender_test_helper_(base::BindRepeating(
            &PermissionRequestManagerWithPrerenderingTest::GetWebContents,
            base::Unretained(this))) {}

  void SetUp() override {
    prerender_test_helper_.RegisterServerRequestMonitor(embedded_test_server());
    PermissionRequestManagerBrowserTest::SetUp();
  }

  void SetUpOnMainThread() override {
    PermissionRequestManagerBrowserTest::SetUpOnMainThread();
    ASSERT_TRUE(embedded_test_server()->Start());
  }

  content::test::PrerenderTestHelper& prerender_test_helper() {
    return prerender_test_helper_;
  }

 private:
  content::WebContents* GetWebContents() {
    return browser()->GetTabStripModel()->GetActiveWebContents();
  }

  content::test::PrerenderTestHelper prerender_test_helper_;
};

class PermissionRequestManagerWithBackForwardCacheUnblockBrowserTest
    : public PermissionRequestManagerWithBackForwardCacheBrowserTest {
 public:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    PermissionRequestManagerBrowserTest::SetUpCommandLine(command_line);
    feature_list_.InitAndEnableFeature(
        permissions::features::kBackForwardCacheUnblockPermissionRequest);
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

// Requests before the load event should be bundled into one bubble.
// http://crbug.com/41190115 flaky
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       DISABLED_RequestsBeforeLoad) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL("/permissions/requests-before-load.html"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(2, bubble_factory()->TotalRequestCount());
}

// Verifies that when a background tab is discarded (e.g. by memory saver) and
// subsequently reloads in the background, requesting permissions inside it does
// not pop up permission prompt bubbles while another tab is active.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       TestSplitViewDiscardInitializationRace) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Open tab 0 (title1.html) and tab 1 (title2.html in foreground).
  EXPECT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/title1.html")));

  EXPECT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
      browser(), embedded_test_server()->GetURL("/title2.html"),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));

  // Discard background tab 0, replacing its WebContents with a new hidden one.
  content::WebContents::CreateParams params(browser()->GetProfile());
  params.initially_hidden = true;
  std::unique_ptr<content::WebContents> new_contents =
      content::WebContents::Create(params);
  content::WebContents* raw_new_contents = new_contents.get();
  browser()->GetTabStripModel()->DiscardWebContents(
      browser()->GetTabStripModel()->GetWebContentsAt(0),
      std::move(new_contents));

  permissions::PermissionRequestManager* manager =
      permissions::PermissionRequestManager::FromWebContents(raw_new_contents);

  // Reload discarded tab 0 while tab 1 remains the active foreground tab.
  raw_new_contents->GetController().LoadURL(
      embedded_test_server()->GetURL("/title1.html"), content::Referrer(),
      ui::PAGE_TRANSITION_LINK, std::string());

  EXPECT_TRUE(content::WaitForLoadStop(raw_new_contents));

  EXPECT_EQ(1, browser()->GetTabStripModel()->active_index());

  permissions::MockPermissionPromptFactory mock_factory(manager);

  // Issue a notifications permission request in background tab 0.
  ExecuteScriptAsync(raw_new_contents, "Notification.requestPermission();");

  // Wait for the request to reach tab 0's manager, then verify no prompt bubble
  // is shown on screen because tab 0 is inactive in the tab strip.
  ASSERT_TRUE(
      base::test::RunUntil([&]() { return !manager->Requests().empty(); }));

  EXPECT_EQ(0, mock_factory.TotalRequestCount());

  // After the discarded tab is revived by activating it, the pending request's
  // prompt must appear.
  browser()->GetTabStripModel()->ActivateTabAt(0);
  EXPECT_TRUE(base::test::RunUntil(
      [&]() { return mock_factory.TotalRequestCount() == 1; }));
}

// Requests before the load should not be bundled with a request after the
// load.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       RequestsBeforeAfterLoad) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL(
          "/permissions/requests-before-after-load.html"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
}

// Navigating twice to the same URL should be equivalent to refresh. This
// means showing the bubbles twice. http://crbug.com/41190115 flaky
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest, DISABLED_NavTwice) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL("/permissions/requests-before-load.html"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL("/permissions/requests-before-load.html"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  EXPECT_EQ(2, bubble_factory()->show_count());
  EXPECT_EQ(2, bubble_factory()->TotalRequestCount());
}

// Navigating twice to the same URL with a hash should be navigation within
// the page. This means the bubble is only shown once. http://crbug.com/41190115
// flaky
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       DISABLED_NavTwiceWithHash) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL("/permissions/requests-before-load.html"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL(
          "/permissions/requests-before-load.html#0"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
}

// Bubble requests should be shown after same-document navigation.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       SameDocumentNavigation) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/empty.html"), 1);

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/empty.html#0"), 1);

  // Request 'geolocation' permission.
  ASSERT_TRUE(content::ExecJs(
      GetActiveMainFrame(),
      "navigator.geolocation.getCurrentPosition(function(){});"));
  bubble_factory()->WaitForPermissionBubble();

  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
}

class PermissionRequestManagerOmniboxEverywhereBrowserTest
    : public PermissionRequestManagerBrowserTest {
 private:
  base::test::ScopedFeatureList enable_omnibox_everywhere{
      omnibox::kOmniboxEverywhere};
};

// Request via omnibox everywhere. PEPC should be used.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerOmniboxEverywhereBrowserTest,
                       OmniboxEverywhereUsesEmbeddedPermissionPrompt) {
  // Navigate to Omnibox Everywhere URL.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), GURL(chrome::kChromeUIOmniboxEverywhereURL)));

  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  auto* manager =
      permissions::PermissionRequestManager::FromWebContents(web_contents);

  // Request mic permission from the surface.
  GURL omnibox_url(chrome::kChromeUIOmniboxEverywhereURL);
  GURL canonical_origin = permissions::PermissionUtil::GetCanonicalOrigin(
      ContentSettingsType::MEDIASTREAM_MIC, omnibox_url, omnibox_url);
  auto request = std::make_unique<permissions::MockPermissionRequest>(
      canonical_origin, permissions::RequestType::kMicStream,
      /*embedded_permission_element_initiated=*/true);
  manager->AddRequest(web_contents->GetPrimaryMainFrame(), std::move(request));

  bubble_factory()->WaitForPermissionBubble();

  // Verify it uses the embedded permission prompt flow model.
  EXPECT_TRUE(manager->IsRequestInProgress());
  EXPECT_TRUE(permissions::PermissionUtil::
                  ShouldCurrentRequestUsePermissionElementSecondaryUI(
                      manager, web_contents));
  EXPECT_NE(nullptr, manager->GetEmbeddedPromptFlowModel());

  manager->Dismiss(/*prompt_options=*/std::monostate());
  EXPECT_TRUE(base::test::RunUntil(
      [manager]() { return !manager->IsRequestInProgress(); }));
}

// Ignored permission request should not trigger a blocked activity indicator on
// a new document.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       SameOriginCrossDocumentNavigation) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/empty.html"), 1);

  auto* pscs = content_settings::PageSpecificContentSettings::GetForFrame(
      GetActiveMainFrame());

  ASSERT_TRUE(pscs);
  EXPECT_FALSE(pscs->IsContentBlocked(ContentSettingsType::GEOLOCATION));
  EXPECT_FALSE(pscs->IsContentAllowed(ContentSettingsType::GEOLOCATION));

  // Request 'geolocation' permission.
  ASSERT_TRUE(content::ExecJs(
      GetActiveMainFrame(),
      "navigator.geolocation.getCurrentPosition(function(){});"));

  bubble_factory()->WaitForPermissionBubble();
  EXPECT_TRUE(bubble_factory()->is_visible());

  // Start a same-origin cross-document navigation. This will resolve currently
  // visible permission prompt as `Ignored`.
  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/empty.html"), 1);

  EXPECT_FALSE(bubble_factory()->is_visible());

  // After a new started navigation PSCS will be deleted. Get a new instance.
  pscs = content_settings::PageSpecificContentSettings::GetForFrame(
      GetActiveMainFrame());
  // Geolocation content setting was not blocked nor allowed. In other words,
  // there is no visible activity indicator after Geolocation permission prompt
  // was resolved as `Ignored`.
  EXPECT_FALSE(pscs->IsContentBlocked(ContentSettingsType::GEOLOCATION));
  EXPECT_FALSE(pscs->IsContentAllowed(ContentSettingsType::GEOLOCATION));
}

// Prompts are only shown for active tabs and (on Desktop) hidden on tab
// switching
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest, MultipleTabs) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/empty.html"), 1);

  ui_test_utils::NavigateToURLWithDisposition(
      browser(), embedded_test_server()->GetURL("/empty.html"),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  // SetUp() only creates a mock prompt factory for the first tab.
  permissions::MockPermissionPromptFactory* bubble_factory_0 = bubble_factory();
  std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory_1(
      std::make_unique<permissions::MockPermissionPromptFactory>(
          GetPermissionRequestManager()));

  TabStripModel* tab_strip_model = browser()->GetTabStripModel();
  ASSERT_EQ(2, tab_strip_model->count());
  ASSERT_EQ(1, tab_strip_model->active_index());

  constexpr char kRequestNotifications[] = R"(
      new Promise(resolve => {
        Notification.requestPermission().then(function (permission) {
          resolve(permission)
        });
      })
      )";

  {
    permissions::PermissionRequestObserver observer(
        tab_strip_model->GetWebContentsAt(1));

    // Request permission in foreground tab, prompt should be shown.
    EXPECT_TRUE(content::ExecJs(
        tab_strip_model->GetWebContentsAt(1)->GetPrimaryMainFrame(),
        kRequestNotifications,
        content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));

    observer.Wait();
  }

  EXPECT_EQ(1, bubble_factory_1->show_count());
  EXPECT_FALSE(bubble_factory_0->is_visible());
  EXPECT_TRUE(bubble_factory_1->is_visible());

  tab_strip_model->ActivateTabAt(0);
  EXPECT_FALSE(bubble_factory_0->is_visible());
  EXPECT_FALSE(bubble_factory_1->is_visible());

  tab_strip_model->ActivateTabAt(1);
  EXPECT_EQ(2, bubble_factory_1->show_count());
  EXPECT_FALSE(bubble_factory_0->is_visible());
  EXPECT_TRUE(bubble_factory_1->is_visible());

  {
    permissions::PermissionRequestObserver observer(
        tab_strip_model->GetWebContentsAt(0));

    // Request notification in background tab. No prompt is shown until the
    // tab itself is activated.
    EXPECT_TRUE(content::ExecJs(
        tab_strip_model->GetWebContentsAt(0)->GetPrimaryMainFrame(),
        kRequestNotifications,
        content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));

    observer.Wait();
    EXPECT_TRUE(observer.is_prompt_show_failed_hidden_tab());
  }

  EXPECT_FALSE(bubble_factory_0->is_visible());
  EXPECT_EQ(2, bubble_factory_1->show_count());

  tab_strip_model->ActivateTabAt(0);
  EXPECT_TRUE(bubble_factory_0->is_visible());
  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(2, bubble_factory_1->show_count());
}

// Prompts are only shown for active tabs and (on Desktop) hidden on tab
// switching
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       MultipleTabsInSplitView) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/empty.html"), 1);

  ui_test_utils::NavigateToURLWithDisposition(
      browser(), embedded_test_server()->GetURL("/empty.html"),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  // SetUp() only creates a mock prompt factory for the first tab.
  permissions::MockPermissionPromptFactory* bubble_factory_0 = bubble_factory();
  std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory_1(
      std::make_unique<permissions::MockPermissionPromptFactory>(
          GetPermissionRequestManager()));

  // Create a split with the two tabs.
  TabStripModel* tab_strip_model = browser()->GetTabStripModel();
  tab_strip_model->AddToNewSplit(
      {0}, split_tabs::SplitTabVisualData(),
      split_tabs::SplitTabCreatedSource::kToolbarButton);
  ASSERT_EQ(2, tab_strip_model->count());
  ASSERT_EQ(1, tab_strip_model->active_index());

  constexpr char kRequestNotifications[] = R"(
      new Promise(resolve => {
        Notification.requestPermission().then(function (permission) {
          resolve(permission)
        });
      })
      )";

  {
    permissions::PermissionRequestObserver observer(
        tab_strip_model->GetWebContentsAt(1));

    // Request permission in foreground tab, prompt should be shown.
    EXPECT_TRUE(content::ExecJs(
        tab_strip_model->GetWebContentsAt(1)->GetPrimaryMainFrame(),
        kRequestNotifications,
        content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));

    observer.Wait();
  }

  EXPECT_EQ(1, bubble_factory_1->show_count());
  EXPECT_FALSE(bubble_factory_0->is_visible());
  EXPECT_TRUE(bubble_factory_1->is_visible());

  tab_strip_model->ActivateTabAt(0);
  EXPECT_FALSE(bubble_factory_0->is_visible());
  EXPECT_FALSE(bubble_factory_1->is_visible());

  tab_strip_model->ActivateTabAt(1);
  EXPECT_EQ(2, bubble_factory_1->show_count());
  EXPECT_FALSE(bubble_factory_0->is_visible());
  EXPECT_TRUE(bubble_factory_1->is_visible());

  {
    permissions::PermissionRequestObserver observer(
        tab_strip_model->GetWebContentsAt(0));

    // Request notification in background tab. No prompt is shown until the
    // tab itself is activated.
    EXPECT_TRUE(content::ExecJs(
        tab_strip_model->GetWebContentsAt(0)->GetPrimaryMainFrame(),
        kRequestNotifications,
        content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));

    observer.Wait();
    EXPECT_TRUE(observer.is_prompt_show_failed_hidden_tab());
  }

  EXPECT_FALSE(bubble_factory_0->is_visible());
  EXPECT_EQ(2, bubble_factory_1->show_count());

  tab_strip_model->ActivateTabAt(0);
  EXPECT_TRUE(bubble_factory_0->is_visible());
  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(2, bubble_factory_1->show_count());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       BackgroundTabNavigation) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/empty.html"), 1);

  // Request Notifications, prompt should be shown.
  ASSERT_TRUE(content::ExecJs(
      browser()->GetTabStripModel()->GetWebContentsAt(0)->GetPrimaryMainFrame(),
      "Notification.requestPermission()",
      content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
  bubble_factory()->WaitForPermissionBubble();
  EXPECT_TRUE(bubble_factory()->is_visible());
  EXPECT_EQ(1, bubble_factory()->show_count());

  // SetUp() only creates a mock prompt factory for the first tab but this
  // test doesn't request any permissions in the second tab so it doesn't need
  // one.
  ui_test_utils::NavigateToURLWithDisposition(
      browser(), embedded_test_server()->GetURL("/empty.html"),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  // Navigate background tab, prompt should be removed.
  content::TestNavigationObserver observer(
      browser()->GetTabStripModel()->GetWebContentsAt(0));

  ASSERT_TRUE(content::ExecJs(
      browser()->GetTabStripModel()->GetWebContentsAt(0)->GetPrimaryMainFrame(),
      "window.location = 'simple.html'"));

  observer.Wait();
  EXPECT_FALSE(bubble_factory()->is_visible());

  browser()->GetTabStripModel()->ActivateTabAt(0);
  EXPECT_FALSE(bubble_factory()->is_visible());
  EXPECT_EQ(1, bubble_factory()->show_count());
}

// Bubble requests should not be shown when the killswitch is on.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       KillSwitchGeolocation) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(),
      embedded_test_server()->GetURL("/permissions/killswitch_tester.html")));

  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  EXPECT_TRUE(content::ExecJs(web_contents, "requestGeolocation();",
                              content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
  bubble_factory()->WaitForPermissionBubble();
  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());

  // Now enable the geolocation killswitch.
  EnableKillSwitch(ContentSettingsType::GEOLOCATION);

  // Reload the page to get around blink layer caching for geolocation
  // requests.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(),
      embedded_test_server()->GetURL("/permissions/killswitch_tester.html")));

  EXPECT_EQ("denied", content::EvalJs(web_contents, "requestGeolocation();"));
  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
}

// Regression test for crbug.com/40600616.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       CrossOriginPromptCooldown) {
  ASSERT_TRUE(embedded_test_server()->Start());

  const GURL kInitialURL = embedded_test_server()->GetURL(
      "a.localhost", "/permissions/killswitch_tester.html");
  const GURL kSecondURL = embedded_test_server()->GetURL(
      "b.localhost", "/permissions/killswitch_tester.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kInitialURL));
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::DENY_ALL);

  // Simulate a notification permission request that is denied by the user.
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  ASSERT_EQ("denied", content::EvalJs(web_contents, "requestNotification();",
                                      content::EXECUTE_SCRIPT_NO_USER_GESTURE));
  ASSERT_EQ(1, bubble_factory()->show_count());
  ASSERT_EQ(1, bubble_factory()->TotalRequestCount());

  // In response, simulate the website automatically triggering a
  // renderer-initiated cross-origin navigation without user gesture.
  content::TestNavigationObserver navigation_observer(web_contents);
  ASSERT_TRUE(content::ExecJs(
      web_contents, "window.location = \"" + kSecondURL.spec() + "\";",
      content::EXECUTE_SCRIPT_NO_USER_GESTURE));
  navigation_observer.Wait();

  // Request the notification permission again from a different origin.
  // Cross-origin permission prompt cool-down should be in effect.
  bubble_factory()->ResetCounts();
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
  ASSERT_EQ("default",
            content::EvalJs(web_contents, "requestNotification();",
                            content::EXECUTE_SCRIPT_NO_USER_GESTURE));
  EXPECT_EQ(0, bubble_factory()->show_count());
  EXPECT_EQ(0, bubble_factory()->TotalRequestCount());
}

// Regression test for crbug.com/40600616.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       CooldownEndsOnUserInitiatedReload) {
  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
      [](content::WebContents* web_contents, const GURL& unused_url) {
        content::NavigationController& controller =
            web_contents->GetController();
        controller.Reload(content::ReloadType::NORMAL, false);
        EXPECT_TRUE(content::WaitForLoadStop(web_contents));
      },
      false /* expect_cooldown */);
}

// Regression test for crbug.com/40600616.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       CooldownEndsOnBrowserInitiateNavigation) {
  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
      [](content::WebContents* web_contents, const GURL& url) {
        EXPECT_TRUE(content::NavigateToURL(web_contents, url));
      },
      false /* expect_cooldown */);
}

// Regression test for crbug.com/40600616.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       CooldownEndsOnRendererInitiateNavigationWithGesture) {
  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
      [](content::WebContents* web_contents, const GURL& url) {
        content::TestNavigationObserver navigation_observer(web_contents);
        EXPECT_TRUE(content::ExecJs(
            web_contents, "window.location = \"" + url.spec() + "\";"));
        navigation_observer.Wait();
      },
      false /* expect_cooldown */);
}

// Regression test for crbug.com/40600616.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       CooldownOutlastsRendererInitiatedReload) {
  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
      [](content::WebContents* web_contents, const GURL& unused_url) {
        content::TestNavigationObserver navigation_observer(web_contents);
        EXPECT_TRUE(content::ExecJs(web_contents, "window.location.reload();",
                                    content::EXECUTE_SCRIPT_NO_USER_GESTURE));
        navigation_observer.Wait();
      },
      true /* expect_cooldown */);
}

// Regression test for crbug.com/40600616.
IN_PROC_BROWSER_TEST_F(
    PermissionRequestManagerBrowserTest,
    CooldownOutlastsRendererInitiateNavigationWithoutGesture) {
  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
      [](content::WebContents* web_contents, const GURL& url) {
        content::TestNavigationObserver navigation_observer(web_contents);
        EXPECT_TRUE(content::ExecJs(web_contents,
                                    "window.location = \"" + url.spec() + "\";",
                                    content::EXECUTE_SCRIPT_NO_USER_GESTURE));
        navigation_observer.Wait();
      },
      true /* expect_cooldown */);
}

// Bubble requests should not be shown when the killswitch is on.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       KillSwitchNotifications) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(),
      embedded_test_server()->GetURL("/permissions/killswitch_tester.html")));

  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  EXPECT_TRUE(content::ExecJs(web_contents, "requestNotification();",
                              content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
  bubble_factory()->WaitForPermissionBubble();
  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());

  // Now enable the notifications killswitch.
  EnableKillSwitch(ContentSettingsType::NOTIFICATIONS);

  EXPECT_EQ("denied", content::EvalJs(web_contents, "requestNotification();"));
  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       PendingRequestsDisableBackForwardCache) {
  content::BackForwardCacheDisabledTester back_forward_cache_tester;
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL(
          "/permissions/requests-before-after-load.html"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  content::RenderFrameHost* main_frame = GetActiveMainFrame();
  int main_frame_process_id = main_frame->GetProcess()->GetDeprecatedID();
  int main_frame_routing_id = main_frame->GetRoutingID();

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("b.com", "/title1.html"), 1);
  EXPECT_TRUE(back_forward_cache_tester.IsDisabledForFrameWithReason(
      main_frame_process_id, main_frame_routing_id,
      back_forward_cache::DisabledReason(
          back_forward_cache::DisabledReasonId::kPermissionRequestManager)));
}

class PermissionRequestManagerPostPromptBrowserTest
    : public PermissionRequestManagerBrowserTest {
 public:
  void SetUpOnMainThread() override {
    PermissionRequestManagerBrowserTest::SetUpOnMainThread();
    ASSERT_TRUE(embedded_test_server()->Start());
  }

  void RequestPermission(permissions::RequestType request_type) {
    const GURL kInitialURL =
        embedded_test_server()->GetURL("/permissions/killswitch_tester.html");
    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kInitialURL));
    bubble_factory()->set_response_type(
        permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);

    auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();
    auto request_supported =
        std::make_unique<permissions::MockPermissionRequest>(
            kInitialURL, request_type,
            permissions::PermissionRequestGestureType::GESTURE,
            /*request_state=*/nullptr);
    GetPermissionRequestManager()->AddRequest(
        web_contents->GetPrimaryMainFrame(), std::move(request_supported));

    bubble_factory()->WaitForPermissionBubble();
    EXPECT_EQ(1, bubble_factory()->show_count());
  }

  void CloseTab() {
    browser()->GetTabStripModel()->CloseWebContentsAt(
        browser()->GetTabStripModel()->active_index(),
        TabCloseTypes::CLOSE_USER_GESTURE);
  }

  void NavigateAway(const std::string& url) {
    const GURL gurl = embedded_test_server()->GetURL(url);
    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), gurl));
  }

  void SetFakePromptDisplayTime(permissions::RequestType request_type,
                                base::TimeDelta duration) {
    base::TimeTicks fake_display_time = base::TimeTicks::Now() - duration;
    if (request_type == permissions::RequestType::kNotifications) {
      GetPermissionRequestManager()
          ->set_notification_request_first_display_time_for_testing(
              fake_display_time);
    } else if (request_type == permissions::RequestType::kGeolocation) {
      GetPermissionRequestManager()
          ->set_geolocation_request_first_display_time_for_testing(
              fake_display_time);
    }
  }
};

class PermissionRequestManagerQuietUiBrowserTest
    : public PermissionRequestManagerBrowserTest {
 public:
  PermissionRequestManagerQuietUiBrowserTest()
      : PermissionRequestManagerBrowserTest(
            /*enabled_features=*/{},
            /*disabled_features=*/
            {permissions::features::kPermissionsGestureGatedPrompts}) {}

 protected:
  using UiDecision = permissions::PermissionUiSelector::Decision;
  using QuietUiReason = permissions::PermissionUiSelector::QuietUiReason;
  using WarningReason = permissions::PermissionUiSelector::WarningReason;

  MockPermissionUiSelector* SetUiSelectorWithCannedDecision(
      const UiDecision& decision) {
    auto selector = std::make_unique<MockPermissionUiSelector>(decision);
    auto selector_ptr = selector.get();
    GetPermissionRequestManager()->set_permission_ui_selector_for_testing(
        std::move(selector));
    return selector_ptr;
  }
};

// Re-enable when 1016233 is fixed.
// Quiet permission requests are cancelled when a new request is made.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerQuietUiBrowserTest,
                       DISABLED_QuietPendingRequestsKilledOnNewRequest) {
  content::RenderFrameHost* source_frame = GetActiveMainFrame();
  // First add a quiet permission request. Ensure that this request is decided
  // by the end of this test.
  permissions::MockPermissionRequest::MockPermissionRequestState
      request_quiet_state;
  auto request_quiet = std::make_unique<permissions::MockPermissionRequest>(
      GURL(permissions::MockPermissionRequest::kDefaultOrigin),
      permissions::RequestType::kNotifications,
      request_quiet_state.GetWeakPtr());
  GetPermissionRequestManager()->AddRequest(source_frame,
                                            std::move(request_quiet));
  base::RunLoop().RunUntilIdle();

  // Add a second permission request. This ones should cause the initial
  // request to be cancelled.
  auto request_loud = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kGeolocation, /*request_state=*/nullptr);
  GetPermissionRequestManager()->AddRequest(source_frame,
                                            std::move(request_loud));
  base::RunLoop().RunUntilIdle();

  // The first dialog should now have been decided.
  EXPECT_TRUE(request_quiet_state.finished);
  EXPECT_EQ(1u, GetPermissionRequestManager()->Requests().size());

  // Cleanup remaining request. And check that this was the last request.
  GetPermissionRequestManager()->Dismiss(/*prompt_options=*/std::monostate());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(0u, GetPermissionRequestManager()->Requests().size());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerQuietUiBrowserTest,
                       PopulatesMetricFieldsForSupportedRequests) {
  const bool kWasDecisionHeldBack = true;
  const PredictionGrantLikelihood kRequestGrantLikelihood =
      PredictionGrantLikelihood::
          PermissionPrediction_Likelihood_DiscretizedLikelihood_NEUTRAL;
  constexpr PermissionRequestRelevance kPermissionRequestRelevance =
      PermissionRequestRelevance::kVeryLow;

  MockPermissionUiSelector* selector =
      SetUiSelectorWithCannedDecision(UiDecision::UseQuietUi(
          QuietUiReason::kEnabledInPrefs, UiDecision::ShowNoWarning()));
  selector->was_decision_held_back_ = std::make_optional(kWasDecisionHeldBack);
  selector->last_request_grant_likelihood_ =
      std::make_optional(kRequestGrantLikelihood);
  selector->last_permission_request_relevance_ =
      std::make_optional(kPermissionRequestRelevance);

  auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();
  auto request_supported = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kNotifications, /*request_state=*/nullptr);
  GetPermissionRequestManager()->AddRequest(web_contents->GetPrimaryMainFrame(),
                                            std::move(request_supported));

  bubble_factory()->WaitForPermissionBubble();
  auto* manager = GetPermissionRequestManager();

  EXPECT_THAT(manager->was_decision_held_back_for_testing(),
              Optional(kWasDecisionHeldBack));
  EXPECT_THAT(manager->prediction_grant_likelihood_for_testing(),
              Optional(kRequestGrantLikelihood));
  EXPECT_THAT(manager->permission_request_relevance_for_testing(),
              Optional(kPermissionRequestRelevance));

  manager->Dismiss(/*prompt_options=*/std::monostate());
  base::RunLoop().RunUntilIdle();
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerQuietUiBrowserTest,
                       DoesntPopulateMetricFieldsForUnsupportedRequests) {
  const bool kWasDecisionHeldBack = true;
  const PredictionGrantLikelihood kRequestGrantLikelihood =
      PredictionGrantLikelihood::
          PermissionPrediction_Likelihood_DiscretizedLikelihood_NEUTRAL;
  constexpr PermissionRequestRelevance kPermissionRequestRelevance =
      PermissionRequestRelevance::kVeryLow;

  MockPermissionUiSelector* selector =
      SetUiSelectorWithCannedDecision(UiDecision::UseQuietUi(
          QuietUiReason::kEnabledInPrefs, UiDecision::ShowNoWarning()));
  selector->was_decision_held_back_ = std::make_optional(kWasDecisionHeldBack);
  selector->last_request_grant_likelihood_ =
      std::make_optional(kRequestGrantLikelihood);
  selector->last_permission_request_relevance_ =
      std::make_optional(kPermissionRequestRelevance);

  auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();
  auto request_not_supported =
      std::make_unique<permissions::MockPermissionRequest>(
          permissions::RequestType::kClipboard);
  GetPermissionRequestManager()->AddRequest(web_contents->GetPrimaryMainFrame(),
                                            std::move(request_not_supported));

  bubble_factory()->WaitForPermissionBubble();
  auto* manager = GetPermissionRequestManager();

  EXPECT_EQ(manager->was_decision_held_back_for_testing(), std::nullopt);
  EXPECT_EQ(manager->prediction_grant_likelihood_for_testing(), std::nullopt);
  EXPECT_EQ(manager->permission_request_relevance_for_testing(), std::nullopt);

  manager->Dismiss(/*prompt_options=*/std::monostate());
  base::RunLoop().RunUntilIdle();
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerQuietUiBrowserTest,
                       PermissionPromptDisposition) {
  SetUiSelectorWithCannedDecision(
      UiDecision::UseQuietUi(QuietUiReason::kTriggeredDueToAbusiveContent,
                             WarningReason::kAbusiveContent));

  auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();
  auto request_quiet = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kNotifications);
  GetPermissionRequestManager()->AddRequest(web_contents->GetPrimaryMainFrame(),
                                            std::move(request_quiet));

  bubble_factory()->WaitForPermissionBubble();
  auto* manager = GetPermissionRequestManager();

  std::optional<permissions::PermissionPromptDisposition> disposition =
      manager->current_request_prompt_disposition_for_testing();
  auto disposition_from_prompt_bubble =
      manager->GetCurrentPrompt()->GetPromptDisposition();

  manager->Dismiss(/*prompt_options=*/std::monostate());
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(disposition.has_value());
  EXPECT_EQ(disposition.value(), disposition_from_prompt_bubble);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerQuietUiBrowserTest,
                       PermissionPromptDispositionHidden) {
  SetUiSelectorWithCannedDecision(
      UiDecision::UseQuietUi(QuietUiReason::kTriggeredDueToAbusiveContent,
                             WarningReason::kAbusiveContent));

  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/empty.html"), 1);

  auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();
  auto request_quiet = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kNotifications);
  GetPermissionRequestManager()->AddRequest(web_contents->GetPrimaryMainFrame(),
                                            std::move(request_quiet));

  bubble_factory()->WaitForPermissionBubble();
  auto* manager = GetPermissionRequestManager();
  auto disposition_from_prompt_bubble =
      manager->GetCurrentPrompt()->GetPromptDisposition();

  // There will be no instance of PermissionPromptImpl after a new tab is opened
  // and existing tab marked as HIDDEN.
  ui_test_utils::NavigateToURLWithDisposition(
      browser(), embedded_test_server()->GetURL("/empty.html"),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  std::optional<permissions::PermissionPromptDisposition> disposition =
      manager->current_request_prompt_disposition_for_testing();

  EXPECT_TRUE(disposition.has_value());
  EXPECT_EQ(disposition.value(), disposition_from_prompt_bubble);

  //  DCHECK failure if Closing executed on HIDDEN PermissionRequestManager.
  browser()->GetTabStripModel()->ActivateTabAt(0);
  manager->Dismiss(/*prompt_options=*/std::monostate());
  base::RunLoop().RunUntilIdle();
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerQuietUiBrowserTest,
                       ConsoleMessages) {
  const struct {
    UiDecision simulated_decision;
    const char* expected_message;
  } kTestCases[] = {
      {UiDecision::UseNormalUiAndShowNoWarning(), nullptr},
      {UiDecision::UseQuietUi(QuietUiReason::kEnabledInPrefs,
                              UiDecision::ShowNoWarning()),
       nullptr},
      {UiDecision::UseQuietUi(QuietUiReason::kTriggeredByCrowdDeny,
                              UiDecision::ShowNoWarning()),
       nullptr},
      {UiDecision::UseQuietUi(QuietUiReason::kTriggeredDueToAbusiveRequests,
                              UiDecision::ShowNoWarning()),
       permissions::kAbusiveNotificationRequestsEnforcementMessage},
      {UiDecision::UseNormalUi(WarningReason::kAbusiveRequests),
       permissions::kAbusiveNotificationRequestsWarningMessage},
      {UiDecision::UseQuietUi(QuietUiReason::kTriggeredDueToAbusiveContent,
                              UiDecision::ShowNoWarning()),
       permissions::kAbusiveNotificationContentEnforcementMessage},
      {UiDecision::UseNormalUi(WarningReason::kAbusiveContent),
       permissions::kAbusiveNotificationContentWarningMessage},
  };

  constexpr char kCounterVerificationPattern[] = "NOTHING";

  for (const auto& test : kTestCases) {
    SCOPED_TRACE(testing::Message() << "Test index: " << (&test - kTestCases));

    SetUiSelectorWithCannedDecision(test.simulated_decision);

    auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();
    content::WebContentsConsoleObserver console_observer(web_contents);

    auto request_quiet = std::make_unique<permissions::MockPermissionRequest>(
        permissions::RequestType::kNotifications);
    GetPermissionRequestManager()->AddRequest(
        web_contents->GetPrimaryMainFrame(), std::move(request_quiet));

    bubble_factory()->WaitForPermissionBubble();
    GetPermissionRequestManager()->Dismiss(/*prompt_options=*/std::monostate());
    base::RunLoop().RunUntilIdle();

    if (!test.expected_message) {
      web_contents->GetPrimaryMainFrame()->AddMessageToConsole(
          blink::mojom::ConsoleMessageLevel::kInfo,
          kCounterVerificationPattern);
    }

    ASSERT_TRUE(console_observer.Wait());

    ASSERT_EQ(1u, console_observer.messages().size());
    if (test.expected_message) {
      EXPECT_EQ(test.expected_message, console_observer.GetMessageAt(0));
      EXPECT_EQ(blink::mojom::ConsoleMessageLevel::kWarning,
                console_observer.messages()[0].log_level);
    } else {
      EXPECT_EQ(kCounterVerificationPattern, console_observer.GetMessageAt(0));
    }
  }
}

// Two loud requests are simply queued one after another.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       LoudPendingRequestsQueued) {
  content::RenderFrameHost* source_frame = GetActiveMainFrame();
  permissions::MockPermissionRequest::MockPermissionRequestState request1_state;
  auto request1 = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kClipboard, request1_state.GetWeakPtr());
  GetPermissionRequestManager()->AddRequest(source_frame, std::move(request1));
  base::RunLoop().RunUntilIdle();

  permissions::MockPermissionRequest::MockPermissionRequestState request2_state;
  auto request2 = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kMicStream, request2_state.GetWeakPtr());
  GetPermissionRequestManager()->AddRequest(source_frame, std::move(request2));
  base::RunLoop().RunUntilIdle();

  // Both requests are still pending (though only one is active).
  EXPECT_FALSE(request1_state.finished);
  EXPECT_FALSE(request2_state.finished);

  EXPECT_EQ(1u, GetPermissionRequestManager()->Requests().size());

  // Close first request.
  GetPermissionRequestManager()->Dismiss(/*prompt_options=*/std::monostate());
  base::RunLoop().RunUntilIdle();

  if (permissions::PermissionUtil::DoesPlatformSupportChip()) {
    EXPECT_FALSE(request1_state.finished);
    EXPECT_TRUE(request2_state.finished);
  } else {
    EXPECT_TRUE(request1_state.finished);
    EXPECT_FALSE(request2_state.finished);
  }
  EXPECT_EQ(1u, GetPermissionRequestManager()->Requests().size());

  // Close second request. No more requests pending
  GetPermissionRequestManager()->Dismiss(/*prompt_options=*/std::monostate());
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(request1_state.finished);
  EXPECT_TRUE(request2_state.finished);
  EXPECT_EQ(0u, GetPermissionRequestManager()->Requests().size());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithBackForwardCacheBrowserTest,
                       NoPermissionBubbleShownForPagesInCache) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url_a = embedded_test_server()->GetURL("a.com", "/title1.html");
  GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_a));
  content::RenderFrameHostWrapper rfh_a(GetActiveMainFrame());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_b));
  EXPECT_EQ(rfh_a->GetLifecycleState(),
            content::RenderFrameHost::LifecycleState::kInBackForwardCache);

  auto req = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kNotifications);
  GetPermissionRequestManager()->AddRequest(rfh_a.get(), std::move(req));

  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(0, bubble_factory()->show_count());
  EXPECT_EQ(0, bubble_factory()->TotalRequestCount());
  // Page gets evicted if bubble would have been shown.
  ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithBackForwardCacheBrowserTest,
                       RequestsForPagesInCacheNotGrouped) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url_a = embedded_test_server()->GetURL("a.com", "/title1.html");
  GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_a));
  content::RenderFrameHostWrapper rfh_a(GetActiveMainFrame());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_b));
  EXPECT_EQ(rfh_a->GetLifecycleState(),
            content::RenderFrameHost::LifecycleState::kInBackForwardCache);
  content::RenderFrameHostWrapper rfh_b(GetActiveMainFrame());

  // Mic, camera, and pan/tilt/zoom requests are grouped if they come from the
  // same origin. Make sure this will not include requests from a cached
  // frame. Note pages will not be cached when navigating within the same
  // origin, so we have different urls in the navigations above but use the
  // same (default) url for the MockPermissionRequest here.
  permissions::MockPermissionRequest::MockPermissionRequestState req_a_1_state;
  auto req_a_1 = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kCameraPanTiltZoom,
      permissions::PermissionRequestGestureType::GESTURE,
      req_a_1_state.GetWeakPtr());
  auto req_b_1 = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kCameraStream,
      permissions::PermissionRequestGestureType::GESTURE);
  auto req_b_2 = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kMicStream,
      permissions::PermissionRequestGestureType::GESTURE);
  GetPermissionRequestManager()->AddRequest(
      rfh_a.get(),
      std::move(req_a_1));  // Should be skipped
  GetPermissionRequestManager()->AddRequest(rfh_b.get(), std::move(req_b_1));
  GetPermissionRequestManager()->AddRequest(rfh_b.get(), std::move(req_b_2));

  bubble_factory()->WaitForPermissionBubble();

  // One bubble with the two grouped requests and not the skipped one.
  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(2, bubble_factory()->TotalRequestCount());
  EXPECT_TRUE(req_a_1_state.cancelled);

  // Page gets evicted if bubble would have been shown.
  ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());

  // Cleanup before we delete the requests.
  GetPermissionRequestManager()->Dismiss(/*prompt_options=*/std::monostate());
}

class PermissionRequestManagerOneTimePermissionBrowserTest
    : public PermissionRequestManagerBrowserTest {
 public:
  PermissionRequestManagerOneTimePermissionBrowserTest() {
    geolocation_overrider_ =
        std::make_unique<device::ScopedGeolocationOverrider>(0, 0);
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
  std::unique_ptr<device::ScopedGeolocationOverrider> geolocation_overrider_;
};

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerOneTimePermissionBrowserTest,
                       RequestForPermission) {
  const char kQueryCurrentPosition[] = R"(
        new Promise(resolve => {
          navigator.geolocation.getCurrentPosition(
            _ => resolve('success'),
            _ => resolve('failure'));
        });
      )";
  ASSERT_TRUE(embedded_test_server()->Start());
  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/title1.html"), 1);
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ONCE);

  // Request 'geolocation' permission.
  std::string result =
      content::EvalJs(GetActiveMainFrame(), kQueryCurrentPosition)
          .ExtractString();
  EXPECT_EQ("success", result);
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());

  // Request 'geolocation' permission. There should not be a 2nd prompt.
  result = content::EvalJs(GetActiveMainFrame(), kQueryCurrentPosition)
               .ExtractString();
  EXPECT_EQ("success", result);
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());

  // Open a new tab with same domain.
  ui_test_utils::NavigateToURLWithDisposition(
      browser(), embedded_test_server()->GetURL("/title1.html"),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  // Create a new mock permission prompt factory for the second tab.
  std::unique_ptr<permissions::MockPermissionPromptFactory>
      second_tab_bubble_factory(
          std::make_unique<permissions::MockPermissionPromptFactory>(
              GetPermissionRequestManager()));

  // Request 'geolocation' permission.
  result = content::EvalJs(GetActiveMainFrame(), kQueryCurrentPosition)
               .ExtractString();
  EXPECT_EQ("success", result);
  // There should be no permission prompt.
  EXPECT_EQ(0, second_tab_bubble_factory.get()->TotalRequestCount());

  // Open a new empty tab before closing the first two tabs.
  ui_test_utils::NavigateToURLWithDisposition(
      browser(), GURL(url::kAboutBlankURL),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  // Need to close the mock permission managers before closing the tabs.
  // Otherwise the tab instances can't be destroyed due to a DCHECK
  ShutDownFirstTabMockPermissionPromptFactory();
  second_tab_bubble_factory.reset();

  // Close the first two tabs.
  TabStripModel* tab_strip_model = browser()->GetTabStripModel();
  tab_strip_model->CloseWebContentsAt(0, TabCloseTypes::CLOSE_USER_GESTURE);
  tab_strip_model->CloseWebContentsAt(0, TabCloseTypes::CLOSE_USER_GESTURE);

  ASSERT_EQ(1, tab_strip_model->count());

  // Create a new mock permission prompt factory for the third tab.
  std::unique_ptr<permissions::MockPermissionPromptFactory>
      third_tab_bubble_factory(
          std::make_unique<permissions::MockPermissionPromptFactory>(
              GetPermissionRequestManager()));

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/title1.html"), 1);
  third_tab_bubble_factory.get()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ONCE);

  // Request 'geolocation' permission. We should get a prompt.
  result = content::EvalJs(GetActiveMainFrame(), kQueryCurrentPosition)
               .ExtractString();
  EXPECT_EQ("success", result);

  EXPECT_EQ(1, third_tab_bubble_factory.get()->TotalRequestCount());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithPrerenderingTest,
                       RequestForPermission) {
  GURL initial_url = embedded_test_server()->GetURL("a.test", "/empty.html");
  GURL prerender_url = embedded_test_server()->GetURL("a.test", "/title1.html");
  ASSERT_NE(ui_test_utils::NavigateToURL(browser(), initial_url), nullptr);
  ASSERT_EQ(GetActiveMainFrame()->GetLastCommittedURL(), initial_url);

  prerender_test_helper().AddPrerender(prerender_url);
  content::PrerenderHostId host_id =
      prerender_test_helper().GetHostForUrl(prerender_url);
  content::RenderFrameHost* prerender_frame =
      prerender_test_helper().GetPrerenderedMainFrameHost(host_id);
  EXPECT_NE(prerender_frame, nullptr);

  content::RenderFrameDeletedObserver deleted_observer(prerender_frame);
  permissions::MockPermissionRequest::MockPermissionRequestState request_state;
  auto request = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kNotifications, request_state.GetWeakPtr());
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::ACCEPT_ALL);
  GetPermissionRequestManager()->AddRequest(prerender_frame,
                                            std::move(request));

  deleted_observer.WaitUntilDeleted();

  // Permission request should be denied and prerender that sent the request
  // should be discarded.
  EXPECT_TRUE(request_state.cancelled);
  EXPECT_TRUE(deleted_observer.deleted());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithPrerenderingTest,
                       DuplicateRequestForPermission) {
  GURL initial_url = embedded_test_server()->GetURL("a.test", "/empty.html");
  GURL prerender_url = embedded_test_server()->GetURL("a.test", "/title1.html");
  ASSERT_NE(ui_test_utils::NavigateToURL(browser(), initial_url), nullptr);
  ASSERT_EQ(GetActiveMainFrame()->GetLastCommittedURL(), initial_url);

  prerender_test_helper().AddPrerender(prerender_url);
  content::PrerenderHostId host_id =
      prerender_test_helper().GetHostForUrl(prerender_url);
  content::RenderFrameHost* prerender_frame =
      prerender_test_helper().GetPrerenderedMainFrameHost(host_id);
  EXPECT_NE(prerender_frame, nullptr);

  content::RenderFrameDeletedObserver deleted_observer(prerender_frame);
  permissions::MockPermissionRequest::MockPermissionRequestState
      request_1_state;
  auto request_1 = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kNotifications, request_1_state.GetWeakPtr());
  permissions::MockPermissionRequest::MockPermissionRequestState
      request_2_state;
  auto request_2 =
      request_1->CreateDuplicateRequest(request_2_state.GetWeakPtr());
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::ACCEPT_ALL);
  GetPermissionRequestManager()->AddRequest(GetActiveMainFrame(),
                                            std::move(request_1));
  GetPermissionRequestManager()->AddRequest(prerender_frame,
                                            std::move(request_2));

  base::RunLoop().RunUntilIdle();

  // Permission request from main frame should be granted, similar request
  // from prerender should be denied.
  EXPECT_TRUE(request_1_state.granted);
  EXPECT_TRUE(request_2_state.cancelled);
  EXPECT_TRUE(deleted_observer.deleted());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithPrerenderingTest,
                       PrerenderLoadsWhileRequestsPending) {
  GURL initial_url = embedded_test_server()->GetURL("a.test", "/empty.html");
  GURL prerender_url = embedded_test_server()->GetURL("a.test", "/title1.html");
  GURL next_url = embedded_test_server()->GetURL("b.test", "/title1.html");
  ASSERT_NE(ui_test_utils::NavigateToURL(browser(), initial_url), nullptr);
  ASSERT_EQ(GetActiveMainFrame()->GetLastCommittedURL(), initial_url);

  permissions::MockPermissionRequest::MockPermissionRequestState
      request_1_state;
  permissions::MockPermissionRequest::MockPermissionRequestState
      request_2_state;
  auto request_1 = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kNotifications, request_1_state.GetWeakPtr());
  auto request_2 = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kGeolocation, request_2_state.GetWeakPtr());
  GetPermissionRequestManager()->AddRequest(GetActiveMainFrame(),
                                            std::move(request_1));
  GetPermissionRequestManager()->AddRequest(GetActiveMainFrame(),
                                            std::move(request_2));

  prerender_test_helper().AddPrerender(prerender_url);
  content::PrerenderHostId host_id =
      prerender_test_helper().GetHostForUrl(prerender_url);
  content::RenderFrameHost* prerender_frame =
      prerender_test_helper().GetPrerenderedMainFrameHost(host_id);
  EXPECT_NE(prerender_frame, nullptr);

  // Prerender's navigation should not cancel pending primary main frame
  // permission requests.
  EXPECT_FALSE(request_1_state.cancelled);
  EXPECT_FALSE(request_2_state.cancelled);

  // Navigate primary main frame.
  ASSERT_NE(ui_test_utils::NavigateToURL(browser(), next_url), nullptr);

  // Primary main frame navigation should cancel pending permission requests.
  EXPECT_TRUE(request_1_state.cancelled);
  EXPECT_TRUE(request_2_state.cancelled);
}

class PermissionRequestManagerWithFencedFrameTest
    : public PermissionRequestManagerBrowserTest {
 public:
  PermissionRequestManagerWithFencedFrameTest() = default;
  ~PermissionRequestManagerWithFencedFrameTest() override = default;

  PermissionRequestManagerWithFencedFrameTest(
      const PermissionRequestManagerWithFencedFrameTest&) = delete;
  PermissionRequestManagerWithFencedFrameTest& operator=(
      const PermissionRequestManagerWithFencedFrameTest&) = delete;

 protected:
  content::test::FencedFrameTestHelper& fenced_frame_test_helper() {
    return fenced_frame_helper_;
  }

 private:
  content::test::FencedFrameTestHelper fenced_frame_helper_;
};

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithFencedFrameTest,
                       GetCurrentPosition) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/title1.html")));
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();

  // Load a fenced frame.
  GURL fenced_frame_url =
      embedded_test_server()->GetURL("/fenced_frames/title1.html");
  content::RenderFrameHost* fenced_frame_host =
      fenced_frame_test_helper().CreateFencedFrame(
          web_contents->GetPrimaryMainFrame(), fenced_frame_url);
  ASSERT_TRUE(fenced_frame_host);

  const char kQueryPermission[] = R"(
      (async () => {
        const status = await navigator.permissions.query({name: 'geolocation'});
        return status.state;
      })();
    )";

  // The result of query 'geolocation' permission in the fenced frame should
  // be 'denied'.
  EXPECT_EQ("denied", content::EvalJs(fenced_frame_host, kQueryPermission));

  const char kQueryCurrentPosition[] = R"(
      (async () => {
        return await new Promise(resolve => {
          navigator.geolocation.getCurrentPosition(
              () => resolve('granted'), () => resolve('denied'));
        });
      })();
    )";

  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ONCE);

  // The getCurrentPosition() call in the fenced frame should be denied.
  EXPECT_EQ("denied",
            content::EvalJs(fenced_frame_host, kQueryCurrentPosition));
  // The permission prompt should not be shown.
  EXPECT_EQ(0, bubble_factory()->TotalRequestCount());
}

// Tests that the permission request for a fenced frame is blocked
// when the permission is requested thru PermissionControllerDelegate.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithFencedFrameTest,
                       RequestPermissionThruDelegate) {
  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
  https_server.AddDefaultHandlers(GetChromeTestDataDir());
  https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
  ASSERT_TRUE(https_server.Start());
  auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();

  GURL initial_url = https_server.GetURL("/title1.html");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));

  // Load a fenced frame.
  GURL fenced_frame_url = https_server.GetURL("/fenced_frames/title1.html");
  content::RenderFrameHost* fenced_frame_host =
      fenced_frame_test_helper().CreateFencedFrame(
          web_contents->GetPrimaryMainFrame(), fenced_frame_url);
  ASSERT_TRUE(fenced_frame_host);

  // The permission request is denied because it's from the fenced frame.
  const char kExpectedConsolePattern[] =
      "*blocked because it was requested inside a fenced frame*";
  content::WebContentsConsoleObserver console_observer(web_contents);
  console_observer.SetFilter(base::BindRepeating(
      [](content::RenderFrameHost* render_frame_host,
         const content::WebContentsConsoleObserver::Message& message) {
        return message.source_frame == render_frame_host;
      },
      fenced_frame_host->GetOutermostMainFrame()));
  console_observer.SetPattern(kExpectedConsolePattern);

  EXPECT_EQ(
      RequestPermissionFromDocumentSync(
          fenced_frame_host, content::PermissionDescriptorUtil::
                                 CreatePermissionDescriptorForPermissionType(
                                     blink::PermissionType::SENSORS)),
      content::PermissionResult(blink::mojom::PermissionStatus::DENIED,
                                content::PermissionStatusSource::FENCED_FRAME));

  ASSERT_TRUE(console_observer.Wait());
  ASSERT_EQ(1u, console_observer.messages().size());
}

IN_PROC_BROWSER_TEST_F(
    PermissionRequestManagerWithBackForwardCacheUnblockBrowserTest,
    PendingRequestsDoNotDisableBackForwardCache) {
  content::BackForwardCacheDisabledTester back_forward_cache_tester;
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL(
          "/permissions/requests-before-after-load.html"),
      1);
  bubble_factory()->WaitForPermissionBubble();
  content::RenderFrameHostWrapper rfh_a(GetActiveMainFrame());
  content::RenderFrameHost* main_frame = GetActiveMainFrame();
  int main_frame_process_id = main_frame->GetProcess()->GetDeprecatedID();
  int main_frame_routing_id = main_frame->GetRoutingID();

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("b.com", "/title1.html"), 1);
  // A goes into bfcache.
  EXPECT_FALSE(back_forward_cache_tester.IsDisabledForFrameWithReason(
      main_frame_process_id, main_frame_routing_id,
      back_forward_cache::DisabledReason(
          back_forward_cache::DisabledReasonId::kPermissionRequestManager)));
  EXPECT_EQ(rfh_a->GetLifecycleState(),
            content::RenderFrameHost::LifecycleState::kInBackForwardCache);
  EXPECT_FALSE(bubble_factory()->is_visible());
}

IN_PROC_BROWSER_TEST_F(
    PermissionRequestManagerWithBackForwardCacheUnblockBrowserTest,
    PermissionRequestsCancelledInBackForwardCache) {
  content::BackForwardCacheDisabledTester back_forward_cache_tester;
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/title1.html"), 1);
  // Create a geolocation permission request.
  permissions::MockPermissionRequest::MockPermissionRequestState request_state;
  auto request = std::make_unique<permissions::MockPermissionRequest>(
      permissions::RequestType::kGeolocation, request_state.GetWeakPtr());
  GetPermissionRequestManager()->AddRequest(GetActiveMainFrame(),
                                            std::move(request));
  bubble_factory()->WaitForPermissionBubble();

  content::RenderFrameHostWrapper rfh_a(GetActiveMainFrame());
  content::RenderFrameHost* main_frame = GetActiveMainFrame();
  int main_frame_process_id = main_frame->GetProcess()->GetDeprecatedID();
  int main_frame_routing_id = main_frame->GetRoutingID();
  // Request is not cancelled.
  EXPECT_FALSE(request_state.cancelled);

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("b.com", "/title1.html"), 1);
  // A goes into bfcache.
  EXPECT_FALSE(back_forward_cache_tester.IsDisabledForFrameWithReason(
      main_frame_process_id, main_frame_routing_id,
      back_forward_cache::DisabledReason(
          back_forward_cache::DisabledReasonId::kPermissionRequestManager)));
  EXPECT_EQ(rfh_a->GetLifecycleState(),
            content::RenderFrameHost::LifecycleState::kInBackForwardCache);
  // Request should be cancelled.
  EXPECT_TRUE(request_state.cancelled);
  EXPECT_FALSE(bubble_factory()->is_visible());

  auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();
  web_contents->GetController().GoBack();
  EXPECT_TRUE(request_state.cancelled);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       PrePromptSessionDuration_Notification_1m) {
  ASSERT_TRUE(embedded_test_server()->Start());
  base::HistogramTester histogram_tester;
  const GURL kInitialURL =
      embedded_test_server()->GetURL("/permissions/killswitch_tester.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kInitialURL));
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);

  // Set the page loaded time to 50 seconds ago.
  GetPermissionRequestManager()->set_on_page_loaded_time_for_testing(
      base::TimeTicks::Now() - base::Seconds(50));

  // Request notification permission.
  auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();
  auto request_supported = std::make_unique<permissions::MockPermissionRequest>(
      kInitialURL, permissions::RequestType::kNotifications,
      permissions::PermissionRequestGestureType::GESTURE,
      /*request_state=*/nullptr);
  GetPermissionRequestManager()->AddRequest(web_contents->GetPrimaryMainFrame(),
                                            std::move(request_supported));

  bubble_factory()->WaitForPermissionBubble();
  EXPECT_EQ(1, bubble_factory()->show_count());

  // Histogram should be recorded.
  histogram_tester.ExpectUniqueSample(
      "Permissions.PredictionService.Notifications."
      "PrePromptSessionDuration1m",
      base::Seconds(50).InMilliseconds(), 1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       PrePromptSessionDuration_Geolocation_1m) {
  ASSERT_TRUE(embedded_test_server()->Start());
  base::HistogramTester histogram_tester;
  const GURL kInitialURL =
      embedded_test_server()->GetURL("/permissions/killswitch_tester.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kInitialURL));
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);

  // Set the page loaded time to 50 seconds ago.
  GetPermissionRequestManager()->set_on_page_loaded_time_for_testing(
      base::TimeTicks::Now() - base::Seconds(50));

  // Request geolocation permission.
  auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();
  auto request_supported = std::make_unique<permissions::MockPermissionRequest>(
      kInitialURL, permissions::RequestType::kGeolocation,
      permissions::PermissionRequestGestureType::GESTURE,
      /*request_state=*/nullptr);
  GetPermissionRequestManager()->AddRequest(web_contents->GetPrimaryMainFrame(),
                                            std::move(request_supported));

  bubble_factory()->WaitForPermissionBubble();
  EXPECT_EQ(1, bubble_factory()->show_count());

  // Histogram should be recorded.
  histogram_tester.ExpectUniqueSample(
      "Permissions.PredictionService.Geolocation."
      "PrePromptSessionDuration1m",
      base::Seconds(50).InMilliseconds(), 1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       PrePromptSessionDuration_Notification_5m) {
  ASSERT_TRUE(embedded_test_server()->Start());
  base::HistogramTester histogram_tester;
  const GURL kInitialURL =
      embedded_test_server()->GetURL("/permissions/killswitch_tester.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kInitialURL));
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);

  // Set the page loaded time to 200 seconds ago.
  GetPermissionRequestManager()->set_on_page_loaded_time_for_testing(
      base::TimeTicks::Now() - base::Seconds(200));

  // Request notification permission.
  auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();
  auto request_supported = std::make_unique<permissions::MockPermissionRequest>(
      kInitialURL, permissions::RequestType::kNotifications,
      permissions::PermissionRequestGestureType::GESTURE,
      /*request_state=*/nullptr);
  GetPermissionRequestManager()->AddRequest(web_contents->GetPrimaryMainFrame(),
                                            std::move(request_supported));

  bubble_factory()->WaitForPermissionBubble();
  EXPECT_EQ(1, bubble_factory()->show_count());

  // Histogram should be recorded.
  histogram_tester.ExpectUniqueSample(
      "Permissions.PredictionService.Notifications."
      "PrePromptSessionDuration5m",
      base::Seconds(200).InMilliseconds(), 1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       PrePromptSessionDuration_Geolocation_5m) {
  ASSERT_TRUE(embedded_test_server()->Start());
  base::HistogramTester histogram_tester;
  const GURL kInitialURL =
      embedded_test_server()->GetURL("/permissions/killswitch_tester.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kInitialURL));
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);

  // Set the page loaded time to 200 seconds ago.
  GetPermissionRequestManager()->set_on_page_loaded_time_for_testing(
      base::TimeTicks::Now() - base::Seconds(200));

  // Request geolocation permission.
  auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();
  auto request_supported = std::make_unique<permissions::MockPermissionRequest>(
      kInitialURL, permissions::RequestType::kGeolocation,
      permissions::PermissionRequestGestureType::GESTURE,
      /*request_state=*/nullptr);
  GetPermissionRequestManager()->AddRequest(web_contents->GetPrimaryMainFrame(),
                                            std::move(request_supported));

  bubble_factory()->WaitForPermissionBubble();
  EXPECT_EQ(1, bubble_factory()->show_count());

  // Histogram should be recorded.
  histogram_tester.ExpectUniqueSample(
      "Permissions.PredictionService.Geolocation."
      "PrePromptSessionDuration5m",
      base::Seconds(200).InMilliseconds(), 1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       PrePromptSessionDuration_Notification_1h) {
  ASSERT_TRUE(embedded_test_server()->Start());
  base::HistogramTester histogram_tester;
  const GURL kInitialURL =
      embedded_test_server()->GetURL("/permissions/killswitch_tester.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kInitialURL));
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);

  // Set the page loaded time to 50 minutes ago.
  GetPermissionRequestManager()->set_on_page_loaded_time_for_testing(
      base::TimeTicks::Now() - base::Minutes(50));

  // Request notification permission.
  auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();
  auto request_supported = std::make_unique<permissions::MockPermissionRequest>(
      kInitialURL, permissions::RequestType::kNotifications,
      permissions::PermissionRequestGestureType::GESTURE,
      /*request_state=*/nullptr);
  GetPermissionRequestManager()->AddRequest(web_contents->GetPrimaryMainFrame(),
                                            std::move(request_supported));

  bubble_factory()->WaitForPermissionBubble();
  EXPECT_EQ(1, bubble_factory()->show_count());

  // Histogram should be recorded.
  histogram_tester.ExpectUniqueSample(
      "Permissions.PredictionService.Notifications."
      "PrePromptSessionDuration1h",
      base::Minutes(50).InMilliseconds(), 1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       PrePromptSessionDuration_Geolocation_1h) {
  ASSERT_TRUE(embedded_test_server()->Start());
  base::HistogramTester histogram_tester;
  const GURL kInitialURL =
      embedded_test_server()->GetURL("/permissions/killswitch_tester.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kInitialURL));
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);

  // Set the page loaded time to 50 minutes ago.
  GetPermissionRequestManager()->set_on_page_loaded_time_for_testing(
      base::TimeTicks::Now() - base::Minutes(50));

  // Request geolocation permission.
  auto* web_contents = browser()->GetTabStripModel()->GetActiveWebContents();
  auto request_supported = std::make_unique<permissions::MockPermissionRequest>(
      kInitialURL, permissions::RequestType::kGeolocation,
      permissions::PermissionRequestGestureType::GESTURE,
      /*request_state=*/nullptr);
  GetPermissionRequestManager()->AddRequest(web_contents->GetPrimaryMainFrame(),
                                            std::move(request_supported));

  bubble_factory()->WaitForPermissionBubble();
  EXPECT_EQ(1, bubble_factory()->show_count());

  // Histogram should be recorded.
  histogram_tester.ExpectUniqueSample(
      "Permissions.PredictionService.Geolocation."
      "PrePromptSessionDuration1h",
      base::Minutes(50).InMilliseconds(), 1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_TabClose) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kNotifications);
  CloseTab();
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications.PostPromptSessionDuration",
      1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_TwoNavigations) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kNotifications);
  NavigateAway("/title1.html");
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications.PostPromptSessionDuration",
      1);
  NavigateAway("/title2.html");
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications.PostPromptSessionDuration",
      1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_TabClose_Geolocation) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kGeolocation);
  CloseTab();
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration", 1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_TwoNavigations_Geolocation) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kGeolocation);
  NavigateAway("/title1.html");
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration", 1);
  NavigateAway("/title2.html");
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration", 1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_10s_Notifications) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kNotifications);
  SetFakePromptDisplayTime(permissions::RequestType::kNotifications,
                           base::Seconds(5));
  CloseTab();
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications."
      "PostPromptSessionDuration10s",
      1);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications.PostPromptSessionDuration",
      1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_10s_Geolocation) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kGeolocation);
  SetFakePromptDisplayTime(permissions::RequestType::kGeolocation,
                           base::Seconds(5));
  CloseTab();
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration10s",
      1);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration", 1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_1m_Notifications) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kNotifications);
  SetFakePromptDisplayTime(permissions::RequestType::kNotifications,
                           base::Seconds(30));
  CloseTab();
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications."
      "PostPromptSessionDuration10s",
      0);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications.PostPromptSessionDuration1m",
      1);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications.PostPromptSessionDuration",
      1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_Notifications_Over1m) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kNotifications);
  SetFakePromptDisplayTime(permissions::RequestType::kNotifications,
                           base::Seconds(70));
  CloseTab();
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications."
      "PostPromptSessionDuration10s",
      0);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications.PostPromptSessionDuration1m",
      0);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications.PostPromptSessionDuration",
      1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_1m_Geolocation) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kGeolocation);
  SetFakePromptDisplayTime(permissions::RequestType::kGeolocation,
                           base::Seconds(30));
  CloseTab();
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration10s",
      0);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration1m",
      1);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration", 1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_Geolocation_Over1m) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kGeolocation);
  SetFakePromptDisplayTime(permissions::RequestType::kGeolocation,
                           base::Seconds(70));
  CloseTab();
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration10s",
      0);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration1m",
      0);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration", 1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_5m_Notifications) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kNotifications);
  SetFakePromptDisplayTime(permissions::RequestType::kNotifications,
                           base::Minutes(3));
  CloseTab();
  histogram_tester.ExpectUniqueSample(
      "Permissions.PredictionService.Notifications.PostPromptSessionDuration5m",
      base::Minutes(3).InMilliseconds(), 1);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications.PostPromptSessionDuration",
      1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_5m_Geolocation) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kGeolocation);
  SetFakePromptDisplayTime(permissions::RequestType::kGeolocation,
                           base::Minutes(3));
  CloseTab();
  histogram_tester.ExpectUniqueSample(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration5m",
      base::Minutes(3).InMilliseconds(), 1);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration", 1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_30m_Notifications) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kNotifications);
  SetFakePromptDisplayTime(permissions::RequestType::kNotifications,
                           base::Minutes(27));
  CloseTab();
  histogram_tester.ExpectUniqueSample(
      "Permissions.PredictionService.Notifications."
      "PostPromptSessionDuration30m",
      base::Minutes(27).InMilliseconds(), 1);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications.PostPromptSessionDuration",
      1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_30m_Geolocation) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kGeolocation);
  SetFakePromptDisplayTime(permissions::RequestType::kGeolocation,
                           base::Minutes(28));
  CloseTab();
  histogram_tester.ExpectUniqueSample(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration30m",
      base::Minutes(28).InMilliseconds(), 1);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration", 1);
}

IN_PROC_BROWSER_TEST_F(
    PermissionRequestManagerPostPromptBrowserTest,
    PostPromptSessionDuration_TwoNavigations_Geolocation_30m) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kGeolocation);
  SetFakePromptDisplayTime(permissions::RequestType::kGeolocation,
                           base::Minutes(28));
  NavigateAway("/title1.html");
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration30m",
      1);
  NavigateAway("/title2.html");
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration30m",
      1);
}

IN_PROC_BROWSER_TEST_F(
    PermissionRequestManagerPostPromptBrowserTest,
    PostPromptSessionDuration_TwoNavigations_Notifications_30m) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kNotifications);
  SetFakePromptDisplayTime(permissions::RequestType::kNotifications,
                           base::Minutes(28));
  NavigateAway("/title1.html");
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications."
      "PostPromptSessionDuration30m",
      1);
  NavigateAway("/title2.html");
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications."
      "PostPromptSessionDuration30m",
      1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_10m_Notifications) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kNotifications);
  SetFakePromptDisplayTime(permissions::RequestType::kNotifications,
                           base::Minutes(7));
  CloseTab();
  histogram_tester.ExpectUniqueSample(
      "Permissions.PredictionService.Notifications."
      "PostPromptSessionDuration10m",
      base::Minutes(7).InMilliseconds(), 1);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications.PostPromptSessionDuration",
      1);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerPostPromptBrowserTest,
                       PostPromptSessionDuration_10m_Geolocation) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kGeolocation);
  SetFakePromptDisplayTime(permissions::RequestType::kGeolocation,
                           base::Minutes(8));
  CloseTab();
  histogram_tester.ExpectUniqueSample(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration10m",
      base::Minutes(8).InMilliseconds(), 1);
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration", 1);
}

IN_PROC_BROWSER_TEST_F(
    PermissionRequestManagerPostPromptBrowserTest,
    PostPromptSessionDuration_TwoNavigations_Geolocation_10m) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kGeolocation);
  SetFakePromptDisplayTime(permissions::RequestType::kGeolocation,
                           base::Minutes(8));
  NavigateAway("/title1.html");
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration10m",
      1);
  NavigateAway("/title2.html");
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Geolocation.PostPromptSessionDuration10m",
      1);
}

IN_PROC_BROWSER_TEST_F(
    PermissionRequestManagerPostPromptBrowserTest,
    PostPromptSessionDuration_TwoNavigations_Notifications_10m) {
  base::HistogramTester histogram_tester;
  RequestPermission(permissions::RequestType::kNotifications);
  SetFakePromptDisplayTime(permissions::RequestType::kNotifications,
                           base::Minutes(8));
  NavigateAway("/title1.html");
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications."
      "PostPromptSessionDuration10m",
      1);
  NavigateAway("/title2.html");
  histogram_tester.ExpectTotalCount(
      "Permissions.PredictionService.Notifications."
      "PostPromptSessionDuration10m",
      1);
}

class PermissionRequestManagerApproximateLocationBrowserTest
    : public PermissionRequestManagerBrowserTestBase {
 public:
  void SetUpOnMainThread() override {
    PermissionRequestManagerBrowserTestBase::SetUpOnMainThread();
    ASSERT_TRUE(embedded_test_server()->Start());
    GURL initial_url = embedded_test_server()->GetURL("/title1.html");
    ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(),
                                                              initial_url, 1);

    ASSERT_TRUE(GetActiveMainFrame()->IsActive());
  }

  content::PermissionResult RequestPermissionFromCurrentDocumentSync(
      blink::mojom::PermissionDescriptorPtr permission_descriptor) {
    return RequestPermissionFromDocumentSync(GetActiveMainFrame(),
                                             std::move(permission_descriptor));
  }

  content::PermissionResult RequestPermissionFromCurrentDocumentSync(
      content::PermissionRequestDescription description) {
    return RequestPermissionFromDocumentSync(GetActiveMainFrame(),
                                             std::move(description));
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_ =
      base::test::ScopedFeatureList(
          content_settings::features::kApproximateGeolocationPermission);
};

static const blink::mojom::PermissionDescriptorPtr
    kPreciseGeolocationDescriptor = content::PermissionDescriptorUtil::
        CreatePermissionDescriptorForPermissionType(
            blink::PermissionType::GEOLOCATION);
static const blink::mojom::PermissionDescriptorPtr
    kApproximateGeolocationDescriptor = content::PermissionDescriptorUtil::
        CreatePermissionDescriptorForPermissionType(
            blink::PermissionType::GEOLOCATION_APPROXIMATE);

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerApproximateLocationBrowserTest,
                       RequestGeolocationAndApproximateLocationGranted) {
  content::PermissionResult approx_only_permission_result(
      blink::mojom::PermissionStatus::GRANTED,
      content::PermissionStatusSource::UNSPECIFIED,
      GeolocationSetting({.approximate = PermissionOption::kAllowed,
                          .precise = PermissionOption::kDenied}));

  permissions::PermissionRequestManager* request_manager =
      GetPermissionRequestManager();
  content::PermissionController* permission_controller =
      browser()->GetProfile()->GetPermissionController();

  {
    base::HistogramTester histograms;
    request_manager->set_auto_response_for_test(
        permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
    request_manager->set_auto_response_prompt_options_for_test(
        GeolocationPromptOptions{.selected_accuracy =
                                     GeolocationAccuracy::kApproximate});
    EXPECT_EQ(RequestPermissionFromCurrentDocumentSync(
                  kPreciseGeolocationDescriptor.Clone()),
              approx_only_permission_result);
    histograms.ExpectUniqueSample(
        permissions::PermissionUmaUtil::kPermissionsPromptShown,
        permissions::RequestTypeForUma::
            PERMISSION_GEOLOCATION_APPROXIMATE_OR_PRECISE,
        1);
  }

  // Now request the permission again. This should not trigger another prompt
  // but it should keep returning the granted permission.
  {
    base::HistogramTester histograms;
    request_manager->set_auto_response_for_test(
        permissions::PermissionRequestManager::AutoResponseType::NONE);
    EXPECT_EQ(permission_controller->GetPermissionResultForCurrentDocument(
                  kPreciseGeolocationDescriptor, GetActiveMainFrame()),
              approx_only_permission_result);
    EXPECT_EQ(RequestPermissionFromCurrentDocumentSync(
                  kPreciseGeolocationDescriptor.Clone()),
              approx_only_permission_result);
    EXPECT_THAT(histograms.GetAllSamples(
                    permissions::PermissionUmaUtil::kPermissionsPromptShown),
                IsEmpty());
  }

  // Now request approximate geolocation permission only. This should not
  // trigger another prompt but it should keep returning the granted
  // permission.
  {
    base::HistogramTester histograms;
    request_manager->set_auto_response_for_test(
        permissions::PermissionRequestManager::AutoResponseType::NONE);
    EXPECT_EQ(permission_controller->GetPermissionResultForCurrentDocument(
                  kApproximateGeolocationDescriptor, GetActiveMainFrame()),
              approx_only_permission_result);
    EXPECT_EQ(RequestPermissionFromCurrentDocumentSync(
                  kApproximateGeolocationDescriptor.Clone()),
              approx_only_permission_result);
    EXPECT_THAT(histograms.GetAllSamples(
                    permissions::PermissionUmaUtil::kPermissionsPromptShown),
                IsEmpty());
  }
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerApproximateLocationBrowserTest,
                       RequestApproximateAndPreciseGeolocation) {
  content::PermissionResult approx_only_permission_result(
      blink::mojom::PermissionStatus::GRANTED,
      content::PermissionStatusSource::UNSPECIFIED,
      GeolocationSetting({.approximate = PermissionOption::kAllowed,
                          .precise = PermissionOption::kAsk}));

  permissions::PermissionRequestManager* request_manager =
      GetPermissionRequestManager();
  content::PermissionController* permission_controller =
      browser()->GetProfile()->GetPermissionController();

  {
    base::HistogramTester histograms;
    request_manager->set_auto_response_for_test(
        permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
    request_manager->set_auto_response_prompt_options_for_test(
        GeolocationPromptOptions{.selected_accuracy =
                                     GeolocationAccuracy::kApproximate});
    EXPECT_EQ(RequestPermissionFromCurrentDocumentSync(
                  kApproximateGeolocationDescriptor.Clone()),
              approx_only_permission_result);
    histograms.ExpectUniqueSample(
        permissions::PermissionUmaUtil::kPermissionsPromptShown,
        permissions::RequestTypeForUma::PERMISSION_GEOLOCATION_APPROXIMATE, 1);
  }

  EXPECT_EQ(permission_controller->GetPermissionResultForCurrentDocument(
                kApproximateGeolocationDescriptor, GetActiveMainFrame()),
            approx_only_permission_result);
  EXPECT_EQ(permission_controller->GetPermissionResultForCurrentDocument(
                kPreciseGeolocationDescriptor, GetActiveMainFrame()),
            content::PermissionResult(
                blink::mojom::PermissionStatus::ASK,
                content::PermissionStatusSource::UNSPECIFIED,
                GeolocationSetting({.approximate = PermissionOption::kAllowed,
                                    .precise = PermissionOption::kAsk})));

  // Now request the permission again. This should not trigger another prompt
  // but it should keep returning the granted permission.
  {
    base::HistogramTester histograms;
    request_manager->set_auto_response_for_test(
        permissions::PermissionRequestManager::AutoResponseType::NONE);
    EXPECT_EQ(RequestPermissionFromCurrentDocumentSync(
                  kApproximateGeolocationDescriptor.Clone()),
              approx_only_permission_result);
    EXPECT_THAT(histograms.GetAllSamples(
                    permissions::PermissionUmaUtil::kPermissionsPromptShown),
                IsEmpty());
  }

  content::PermissionResult precise_permission_result(
      blink::mojom::PermissionStatus::GRANTED,
      content::PermissionStatusSource::UNSPECIFIED,
      GeolocationSetting({.approximate = PermissionOption::kAllowed,
                          .precise = PermissionOption::kAllowed}));

  // Now request precise geolocation permission. This should trigger a prompt.
  {
    base::HistogramTester histograms;
    request_manager->set_auto_response_for_test(
        permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
    request_manager->set_auto_response_prompt_options_for_test(
        GeolocationPromptOptions{.selected_accuracy =
                                     GeolocationAccuracy::kPrecise});
    EXPECT_EQ(RequestPermissionFromCurrentDocumentSync(
                  kPreciseGeolocationDescriptor.Clone()),
              precise_permission_result);
    histograms.ExpectUniqueSample(
        permissions::PermissionUmaUtil::kPermissionsPromptShown,
        permissions::RequestTypeForUma::PERMISSION_GEOLOCATION_UPGRADE, 1);
  }

  // Now request approximate permission. This should not trigger another prompt.
  {
    base::HistogramTester histograms;
    request_manager->set_auto_response_for_test(
        permissions::PermissionRequestManager::AutoResponseType::NONE);
    EXPECT_EQ(RequestPermissionFromCurrentDocumentSync(
                  kApproximateGeolocationDescriptor.Clone()),
              precise_permission_result);
    EXPECT_THAT(histograms.GetAllSamples(
                    permissions::PermissionUmaUtil::kPermissionsPromptShown),
                IsEmpty());
  }
}

IN_PROC_BROWSER_TEST_F(
    PermissionRequestManagerApproximateLocationBrowserTest,
    RequestApproximateGeolocationTriggersApproximateOnlyPrompt) {
  content::PermissionResult approx_only_permission_result(
      blink::mojom::PermissionStatus::GRANTED,
      content::PermissionStatusSource::UNSPECIFIED,
      GeolocationSetting({.approximate = PermissionOption::kAllowed,
                          .precise = PermissionOption::kAsk}));

  permissions::PermissionRequestManager* request_manager =
      GetPermissionRequestManager();
  content::PermissionController* permission_controller =
      browser()->GetProfile()->GetPermissionController();

  base::HistogramTester histograms;
  request_manager->set_auto_response_for_test(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
  request_manager->set_auto_response_prompt_options_for_test(
      GeolocationPromptOptions{.selected_accuracy =
                                   GeolocationAccuracy::kApproximate});

  content::PermissionRequestDescription description(
      kApproximateGeolocationDescriptor.Clone(),
      /*user_gesture=*/true);
  EXPECT_EQ(RequestPermissionFromCurrentDocumentSync(std::move(description)),
            approx_only_permission_result);
  histograms.ExpectUniqueSample(
      permissions::PermissionUmaUtil::kPermissionsPromptShown,
      permissions::RequestTypeForUma::PERMISSION_GEOLOCATION_APPROXIMATE, 1);

  // Verify that precise location is not granted.
  EXPECT_EQ(permission_controller->GetPermissionResultForCurrentDocument(
                kPreciseGeolocationDescriptor.Clone(), GetActiveMainFrame()),
            content::PermissionResult(
                blink::mojom::PermissionStatus::ASK,
                content::PermissionStatusSource::UNSPECIFIED,
                GeolocationSetting({.approximate = PermissionOption::kAllowed,
                                    .precise = PermissionOption::kAsk})));

  // Verify that approximate location is granted.
  EXPECT_EQ(
      permission_controller->GetPermissionResultForCurrentDocument(
          kApproximateGeolocationDescriptor.Clone(), GetActiveMainFrame()),
      approx_only_permission_result);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerApproximateLocationBrowserTest,
                       PermissionAPI) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/title1.html")));
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();

  const char kQueryPermission[] = R"(
      (async () => {
        const status = await navigator.permissions.query({name: 'geolocation'});
        return {name: status.name, state: status.state,
                toString: status.toString()};
      })();
    )";

  const char kSubscribeToPermissionChanges[] = R"(
        var statuses = [];
        navigator.permissions.query({name: 'geolocation'}).then(status => {
          status.onchange = () => statuses.push(status.state);
        });
    )";

  EXPECT_TRUE(content::ExecJs(web_contents, kSubscribeToPermissionChanges));

  EXPECT_THAT(content::EvalJs(web_contents, kQueryPermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation",
     "state": "prompt",
     "toString": "[object PermissionStatus]"
  })")));

  HostContentSettingsMap* hcsm = HostContentSettingsMapFactory::GetForProfile(
      Profile::FromBrowserContext(web_contents->GetBrowserContext()));
  GURL permission_origin = embedded_test_server()->GetURL("/");

  hcsm->SetPermissionSettingDefaultScope(
      permission_origin, GURL(), ContentSettingsType::GEOLOCATION_WITH_OPTIONS,
      GeolocationSetting{.approximate = PermissionOption::kAllowed,
                         .precise = PermissionOption::kAsk});

  EXPECT_THAT(content::EvalJs(web_contents, kQueryPermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation",
     "state": "granted",
     "toString": "[object PermissionStatus]"
  })")));

  hcsm->SetPermissionSettingDefaultScope(
      permission_origin, GURL(), ContentSettingsType::GEOLOCATION_WITH_OPTIONS,
      GeolocationSetting{.approximate = PermissionOption::kAllowed,
                         .precise = PermissionOption::kDenied});

  EXPECT_THAT(content::EvalJs(web_contents, kQueryPermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation",
     "state": "granted",
     "toString": "[object PermissionStatus]"
  })")));

  hcsm->SetPermissionSettingDefaultScope(
      permission_origin, GURL(), ContentSettingsType::GEOLOCATION_WITH_OPTIONS,
      GeolocationSetting{.approximate = PermissionOption::kAllowed,
                         .precise = PermissionOption::kAllowed});

  EXPECT_THAT(content::EvalJs(web_contents, kQueryPermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation",
     "state": "granted",
     "toString": "[object PermissionStatus]"
  })")));

  hcsm->SetPermissionSettingDefaultScope(
      permission_origin, GURL(), ContentSettingsType::GEOLOCATION_WITH_OPTIONS,
      GeolocationSetting{.approximate = PermissionOption::kDenied,
                         .precise = PermissionOption::kDenied});

  EXPECT_THAT(content::EvalJs(web_contents, kQueryPermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation",
     "state": "denied",
     "toString": "[object PermissionStatus]"
  })")));

  hcsm->SetPermissionSettingDefaultScope(
      permission_origin, GURL(), ContentSettingsType::GEOLOCATION_WITH_OPTIONS,
      GeolocationSetting{.approximate = PermissionOption::kAsk,
                         .precise = PermissionOption::kDenied});

  EXPECT_THAT(content::EvalJs(web_contents, kQueryPermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation",
     "state": "prompt",
     "toString": "[object PermissionStatus]"
  })")));

  // onchange events should only be delivered when there is an actual,
  // observable change.
  EXPECT_THAT(content::EvalJs(web_contents, "statuses"),
              content::EvalJsResult::IsOkAndHolds(
                  base::test::IsJson(R"(["granted", "denied", "prompt"])")));
}

class
    PermissionRequestManagerApproximateLocationExperimentalPermissionBrowserTest
    : public PermissionRequestManagerApproximateLocationBrowserTest {
  void SetUpCommandLine(base::CommandLine* command_line) override {
    PermissionRequestManagerApproximateLocationBrowserTest::SetUpCommandLine(
        command_line);
    command_line->AppendSwitchASCII(
        "enable-blink-features",
        "ApproximateGeolocationPermissionAPI,"
        "ApproximateGeolocationPermissionAccuracyMode");
  }
};

IN_PROC_BROWSER_TEST_F(
    PermissionRequestManagerApproximateLocationExperimentalPermissionBrowserTest,
    PermissionAPI) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/title1.html")));
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();

  const char kQueryPermission[] = R"(
      (async () => {
        const status = await navigator.permissions.query({name: 'geolocation'});
        return {name: status.name, state: status.state,
                toString: status.toString(), accuracyMode: status.accuracyMode};
      })();
    )";

  const char kQueryApproximatePermission[] = R"(
      (async () => {
        const status = await navigator.permissions.query(
            {name: 'geolocation-approximate'});
        return {name: status.name, state: status.state,
                toString: status.toString()};
      })();
    )";

  const char kSubscribeToPermissionChanges[] = R"(
        var statuses = [];
        var approximateStatuses = [];
        navigator.permissions.query({name: 'geolocation'}).then(status => {
          status.onchange = () =>
              statuses.push({state: status.state,
                accuracyMode: status.accuracyMode});
        });
        navigator.permissions.query(
            {name: 'geolocation-approximate'}).then(status => {
          status.onchange = () =>
              approximateStatuses.push(status.state);
        });
    )";

  EXPECT_TRUE(content::ExecJs(web_contents, kSubscribeToPermissionChanges));

  EXPECT_THAT(content::EvalJs(web_contents, kQueryPermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation",
     "state": "prompt",
     "toString": "[object GeolocationPermissionStatus]",
     "accuracyMode": null
  })")));
  EXPECT_THAT(content::EvalJs(web_contents, kQueryApproximatePermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation-approximate",
     "state": "prompt",
     "toString": "[object PermissionStatus]",
  })")));

  HostContentSettingsMap* hcsm = HostContentSettingsMapFactory::GetForProfile(
      Profile::FromBrowserContext(web_contents->GetBrowserContext()));
  GURL permission_origin = embedded_test_server()->GetURL("/");

  hcsm->SetPermissionSettingDefaultScope(
      permission_origin, GURL(), ContentSettingsType::GEOLOCATION_WITH_OPTIONS,
      GeolocationSetting{.approximate = PermissionOption::kAllowed,
                         .precise = PermissionOption::kAsk});

  EXPECT_THAT(content::EvalJs(web_contents, kQueryPermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation",
     "state": "prompt",
     "toString": "[object GeolocationPermissionStatus]",
     "accuracyMode": null
  })")));
  EXPECT_THAT(content::EvalJs(web_contents, kQueryApproximatePermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation-approximate",
     "state": "granted",
     "toString": "[object PermissionStatus]",
  })")));

  hcsm->SetPermissionSettingDefaultScope(
      permission_origin, GURL(), ContentSettingsType::GEOLOCATION_WITH_OPTIONS,
      GeolocationSetting{.approximate = PermissionOption::kAllowed,
                         .precise = PermissionOption::kDenied});

  EXPECT_THAT(content::EvalJs(web_contents, kQueryPermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation",
     "state": "granted",
     "toString": "[object GeolocationPermissionStatus]",
     "accuracyMode": "approximate"
  })")));
  EXPECT_THAT(content::EvalJs(web_contents, kQueryApproximatePermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation-approximate",
     "state": "granted",
     "toString": "[object PermissionStatus]",
  })")));

  hcsm->SetPermissionSettingDefaultScope(
      permission_origin, GURL(), ContentSettingsType::GEOLOCATION_WITH_OPTIONS,
      GeolocationSetting{.approximate = PermissionOption::kAllowed,
                         .precise = PermissionOption::kAllowed});

  EXPECT_THAT(content::EvalJs(web_contents, kQueryPermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation",
     "state": "granted",
     "toString": "[object GeolocationPermissionStatus]",
     "accuracyMode": "precise"
  })")));
  EXPECT_THAT(content::EvalJs(web_contents, kQueryApproximatePermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation-approximate",
     "state": "granted",
     "toString": "[object PermissionStatus]",
  })")));

  hcsm->SetPermissionSettingDefaultScope(
      permission_origin, GURL(), ContentSettingsType::GEOLOCATION_WITH_OPTIONS,
      GeolocationSetting{.approximate = PermissionOption::kDenied,
                         .precise = PermissionOption::kDenied});

  EXPECT_THAT(content::EvalJs(web_contents, kQueryPermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation",
     "state": "denied",
     "toString": "[object GeolocationPermissionStatus]",
     "accuracyMode": null
  })")));
  EXPECT_THAT(content::EvalJs(web_contents, kQueryApproximatePermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation-approximate",
     "state": "denied",
     "toString": "[object PermissionStatus]",
  })")));

  hcsm->SetPermissionSettingDefaultScope(
      permission_origin, GURL(), ContentSettingsType::GEOLOCATION_WITH_OPTIONS,
      GeolocationSetting{.approximate = PermissionOption::kAsk,
                         .precise = PermissionOption::kDenied});

  EXPECT_THAT(content::EvalJs(web_contents, kQueryPermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation",
     "state": "prompt",
     "toString": "[object GeolocationPermissionStatus]",
     "accuracyMode": null
  })")));
  EXPECT_THAT(content::EvalJs(web_contents, kQueryApproximatePermission),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"({
     "name": "geolocation-approximate",
     "state": "prompt",
     "toString": "[object PermissionStatus]",
  })")));

  // onchange events should only be delivered when there is an actual,
  // observable change.
  EXPECT_THAT(content::EvalJs(web_contents, "statuses"),
              content::EvalJsResult::IsOkAndHolds(base::test::IsJson(
                  R"([
    { "state": "granted", "accuracyMode": "approximate" },
    { "state": "granted", "accuracyMode": "precise" },
    { "state": "denied", "accuracyMode": null },
    { "state": "prompt", "accuracyMode": null },
  ])")));
  EXPECT_THAT(content::EvalJs(web_contents, "approximateStatuses"),
              content::EvalJsResult::IsOkAndHolds(
                  base::test::IsJson(R"(["granted", "denied", "prompt"])")));
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       PermissionRequestInSandboxedTopLevelFrame) {
  embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
      [](const net::test_server::HttpRequest& request)
          -> std::unique_ptr<net::test_server::HttpResponse> {
        if (request.relative_url == "/sandbox_csp") {
          auto response =
              std::make_unique<net::test_server::BasicHttpResponse>();
          response->set_code(net::HTTP_OK);
          response->set_content("<html><body>Sandboxed Page</body></html>");
          response->set_content_type("text/html");
          response->AddCustomHeader("Content-Security-Policy",
                                    "sandbox allow-scripts");
          return response;
        }
        return nullptr;
      }));
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL target = embedded_test_server()->GetURL("/sandbox_csp");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), target));

  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);

  EXPECT_EQ(
      RequestPermissionFromDocumentSync(
          GetActiveMainFrame(), content::PermissionDescriptorUtil::
                                    CreatePermissionDescriptorForPermissionType(
                                        blink::PermissionType::GEOLOCATION))
          .status,
      blink::mojom::PermissionStatus::GRANTED);
}

}  // anonymous namespace
