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

#include <memory>
#include <variant>

#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/permissions/quiet_notification_permission_ui_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
#include "chrome/browser/ui/views/content_setting_bubble_contents.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"
#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
#include "chrome/browser/ui/views/page_info/page_info_view_factory.h"
#include "chrome/browser/ui/views/permissions/chip/chip_controller.h"
#include "chrome/browser/ui/views/permissions/chip/permission_chip_theme.h"
#include "chrome/browser/ui/views/permissions/chip/permission_chip_view.h"
#include "chrome/browser/ui/views/permissions/permission_prompt_chip.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/permissions/permission_request_manager_test_api.h"
#include "components/metrics/content/subprocess_metrics_provider.h"
#include "components/permissions/features.h"
#include "components/permissions/origin_keyed_permission_action_service.h"
#include "components/permissions/permission_uma_util.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/permissions_client.h"
#include "components/permissions/prediction_service/permission_ui_selector.h"
#include "components/permissions/request_type.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 "components/strings/grit/components_strings.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/accessibility_notification_waiter.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/permissions_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/test/test_event.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/button/toggle_button.h"
#include "ui/views/interaction/element_tracker_views.h"
#include "ui/views/test/button_test_api.h"
#include "ui/views/test/views_test_utils.h"
#include "ui/views/view_utils.h"

namespace {

constexpr char kAddNotificationsEventListener[] = R"(
    new Promise(async resolve => {
      const PermissionStatus =
        await navigator.permissions.query({name: 'notifications'});
        PermissionStatus.onchange = () => {};
      resolve(true);
    })
    )";

constexpr char kCheckNotifications[] = R"(
    new Promise(async resolve => {
      const PermissionStatus =
        await navigator.permissions.query({name: 'notifications'});
      resolve(PermissionStatus.state === 'granted');
    })
    )";

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

class ChipExpansionObserver : public PermissionChipInterface::Observer {
 public:
  explicit ChipExpansionObserver(PermissionChipInterface* chip) {
    observation_.Observe(chip);
  }

  void WaitForChipToExpand() { loop_.Run(); }

  void OnExpandAnimationEnded() override { loop_.Quit(); }

  base::ScopedObservation<PermissionChipInterface,
                          PermissionChipInterface::Observer>
      observation_{this};
  base::RunLoop loop_;
};

class ChipPromptWaiter : public ChipController::Observer {
 public:
  explicit ChipPromptWaiter(ChipController* chip_controller)
      : chip_controller_(chip_controller) {
    show_run_loop_ = std::make_unique<base::RunLoop>(
        base::RunLoop::Type::kNestableTasksAllowed);
    hide_run_loop_ = std::make_unique<base::RunLoop>(
        base::RunLoop::Type::kNestableTasksAllowed);
    chip_controller_->AddObserver(this);
  }

  ~ChipPromptWaiter() override { chip_controller_->RemoveObserver(this); }

  void OnPermissionPromptShown() override { show_run_loop_->Quit(); }

  void WaitForShow() { show_run_loop_->Run(); }

  // Triggered when the permission prompt hides.
  void OnPermissionPromptHidden() override { hide_run_loop_->Quit(); }

  void WaitForHide() { hide_run_loop_->Run(); }

 private:
  raw_ptr<ChipController> chip_controller_ = nullptr;
  std::unique_ptr<base::RunLoop> show_run_loop_;
  std::unique_ptr<base::RunLoop> hide_run_loop_;
};
}  // namespace

class PermissionChipInteractiveUITest : public InProcessBrowserTest {
 public:
  PermissionChipInteractiveUITest() = default;
  PermissionChipInteractiveUITest(const PermissionChipInteractiveUITest&) =
      delete;
  PermissionChipInteractiveUITest& operator=(
      const PermissionChipInteractiveUITest&) = delete;

  // InProcessBrowserTest:
  void SetUpOnMainThread() override {
    test_api_ =
        std::make_unique<test::PermissionRequestManagerTestApi>(browser());
  }

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

  void RequestPermission(permissions::RequestType type) {
    GURL requesting_origin("https://example.com");
    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), requesting_origin));
    test_api_->AddSimpleRequest(GetActiveMainFrame(), type);
    base::RunLoop().RunUntilIdle();
    views::test::RunScheduledLayout(GetLocationBarView());
  }

  LocationBarView* GetLocationBarView() {
    BrowserView* browser_view =
        BrowserView::GetBrowserViewForBrowser(browser());
    return browser_view->toolbar()->location_bar_view();
  }

  ChipController* GetChipController() {
    BrowserView* browser_view =
        BrowserView::GetBrowserViewForBrowser(browser());
    LocationBar* lb = browser_view->toolbar()->location_bar();

    return lb->GetChipController();
  }

  PermissionChipView* GetChip() {
    return views::AsViewClass<PermissionChipView>(
        views::ElementTrackerViews::GetInstance()->GetFirstMatchingView(
            PermissionChipView::kPermissionRequestChipElementId,
            views::ElementTrackerViews::GetContextForView(
                BrowserView::GetBrowserViewForBrowser(browser()))));
  }

  void ClickOnChip(PermissionChipView* chip) {
    ASSERT_TRUE(chip != nullptr);
    ASSERT_TRUE(chip->GetVisible());
    ASSERT_FALSE(GetChipController()->GetBubbleWidget());

    views::test::ButtonTestApi(chip).NotifyClick(
        ui::MouseEvent(ui::EventType::kMousePressed, gfx::Point(), gfx::Point(),
                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0));
    base::RunLoop().RunUntilIdle();
  }

  // Create an <iframe> inside |parent_rfh|, and navigate it toward |url|.
  // |permission_policy| can be used to set permission policy to the iframe.
  // For instance:
  // ```
  // child = CreateIframe(parent, url, "geolocation *; camera *");
  // ```
  // This returns the new RenderFrameHost associated with new document created
  // in the iframe.
  content::RenderFrameHost* CreateIframe(
      content::RenderFrameHost* parent_rfh,
      const GURL& url,
      const std::string& permission_policy = "") {
    std::string script = content::JsReplace(R"(
    new Promise((resolve) => {
      const iframe = document.createElement("iframe");
      iframe.src = $1;
      iframe.allow = $2;
      iframe.onload = _ => { resolve("iframe loaded"); };
      document.body.appendChild(iframe);
    }))",
                                            url, permission_policy);

    EXPECT_EQ("iframe loaded", content::EvalJs(parent_rfh, script));

    return ChildFrameAt(parent_rfh, 0);
  }

  std::unique_ptr<test::PermissionRequestManagerTestApi> test_api_;
};

class LocationBarIconOverrideTest : public PermissionChipInteractiveUITest {
 public:
  LocationBarIconOverrideTest() {
    scoped_feature_list_.InitWithFeatures(
        {}, {content_settings::features::kLeftHandSideActivityIndicators});
  }

  bool IsLocationIconVisible() {
    return BrowserView::GetBrowserViewForBrowser(browser())
        ->GetLocationBarView()
        ->location_icon_view()
        ->GetVisible();
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_F(LocationBarIconOverrideTest,
                       OverrideLocationBarIconDuringChipOnlyForOverrideFlags) {
  // Initially the location bar icon should be visible for any feature flag
  // configuration
  EXPECT_TRUE(IsLocationIconVisible());

  RequestPermission(permissions::RequestType::kGeolocation);

  // After a request, a chip is shown, which should override the lock icon.
  EXPECT_FALSE(IsLocationIconVisible());

  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(
      test_api_->manager()->current_request_prompt_disposition_for_testing(),
      permissions::PermissionPromptDisposition::
          LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE);

  test_api_->manager()->Accept(/*prompt_options=*/std::monostate());

  base::RunLoop().RunUntilIdle();

  // Force synchronous update of layout values. In the actual code,
  // InvalidateLayout() is sufficient, but leaves stale visibility values for
  // testing.
  BrowserView::GetBrowserViewForBrowser(browser())
      ->GetLocationBarView()
      ->DeprecatedLayoutImmediately();

  // Test with confirmation chip.
  // Verify chip is still visible and has the confirmation text
  EXPECT_TRUE(GetChip()->GetVisible());
  EXPECT_TRUE(GetChip()->GetText() ==
              l10n_util::GetStringUTF16(
                  IDS_PERMISSIONS_PERMISSION_ALLOWED_CONFIRMATION));

  EXPECT_FALSE(IsLocationIconVisible());

  // Check collapse timer is running and fast forward fire callback. Then,
  // fast forward animation to trigger callback and wait until it completes.
  EXPECT_TRUE(GetChipController()->is_collapse_timer_running_for_testing());
  GetChipController()->fire_collapse_timer_for_testing();
  GetChip()->animation_for_testing()->End();
  base::RunLoop().RunUntilIdle();

  // Force synchronous update of layout values. In the actual code,
  // InvalidateLayout() is sufficient, but leaves stale visibility values for
  // testing.
  BrowserView::GetBrowserViewForBrowser(browser())
      ->GetLocationBarView()
      ->DeprecatedLayoutImmediately();

  // With any feature flag configuration, we have to ensure that the location
  // bar icon is visible after the chip collapsed.
  EXPECT_FALSE(GetChip()->GetVisible());
  EXPECT_TRUE(IsLocationIconVisible());
}

class ConfirmationChipEnabledInteractiveTest
    : public PermissionChipInteractiveUITest {
 public:
  ConfirmationChipEnabledInteractiveTest() = default;
};

IN_PROC_BROWSER_TEST_F(ConfirmationChipEnabledInteractiveTest,
                       ShouldDisplayAllowAndDenyConfirmationCorrectly) {
  RequestPermission(permissions::RequestType::kGeolocation);
  base::RunLoop().RunUntilIdle();

  // Chip should be visible and show geolocation request
  EXPECT_TRUE(GetChip()->GetVisible());
  EXPECT_TRUE(GetChip()->GetText() ==
              l10n_util::GetStringUTF16(IDS_GEOLOCATION_PERMISSION_CHIP));

  test_api_->manager()->Accept(/*prompt_options=*/std::monostate());

  // Confirmation chip should be visible
  EXPECT_TRUE(GetChip()->GetVisible());
  EXPECT_TRUE(GetChip()->GetText() ==
              l10n_util::GetStringUTF16(
                  IDS_PERMISSIONS_PERMISSION_ALLOWED_CONFIRMATION));
  EXPECT_EQ(GetChip()->theme(), PermissionChipTheme::kNormalVisibility);

  // Check collapse timer is running and fast forward fire callback. Then,
  // fast forward animation to trigger callback and wait until it completes.
  EXPECT_TRUE(GetChipController()->is_collapse_timer_running_for_testing());
  GetChipController()->fire_collapse_timer_for_testing();
  GetChip()->animation_for_testing()->End();
  base::RunLoop().RunUntilIdle();

  // Chip should no longer be visible.
  EXPECT_FALSE(GetChip()->GetVisible());

  // Request second permission
  RequestPermission(permissions::RequestType::kNotifications);
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(GetChip()->GetVisible());
  EXPECT_EQ(GetChip()->GetText(),
            l10n_util::GetStringUTF16(IDS_NOTIFICATION_PERMISSIONS_CHIP));

  test_api_->manager()->Deny(/*prompt_options=*/std::monostate());

  // After deny, the deny confirmation should be displayed
  EXPECT_TRUE(GetChip()->GetVisible());
  EXPECT_EQ(GetChip()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_PERMISSIONS_PERMISSION_NOT_ALLOWED_CONFIRMATION));
  EXPECT_EQ(GetChip()->theme(), PermissionChipTheme::kLowVisibility);
}

IN_PROC_BROWSER_TEST_F(ConfirmationChipEnabledInteractiveTest,
                       IncomingRequestShouldOverrideConfirmation) {
  RequestPermission(permissions::RequestType::kGeolocation);
  base::RunLoop().RunUntilIdle();

  test_api_->manager()->Accept(/*prompt_options=*/std::monostate());

  RequestPermission(permissions::RequestType::kNotifications);
  base::RunLoop().RunUntilIdle();

  // Since a new request came in, the new request should be displayed
  EXPECT_TRUE(GetChip()->GetVisible());
  EXPECT_EQ(GetChip()->GetText(),
            l10n_util::GetStringUTF16(IDS_NOTIFICATION_PERMISSIONS_CHIP));

  test_api_->manager()->Deny(/*prompt_options=*/std::monostate());

  // After the deny, the deny confirmation should be displayed
  EXPECT_TRUE(GetChip()->GetVisible());
  EXPECT_EQ(GetChip()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_PERMISSIONS_PERMISSION_NOT_ALLOWED_CONFIRMATION));
}

IN_PROC_BROWSER_TEST_F(ConfirmationChipEnabledInteractiveTest,
                       ClickOnConfirmationChipShouldOpenPageInfoDialog) {
  RequestPermission(permissions::RequestType::kGeolocation);
  base::RunLoop().RunUntilIdle();

  test_api_->manager()->Accept(/*prompt_options=*/std::monostate());

  ClickOnChip(GetChip());

  base::RunLoop().RunUntilIdle();
  views::View* bubble_view =
      GetChipController()->get_prompt_bubble_view_for_testing();
  PageInfoBubbleView* page_info_bubble =
      static_cast<PageInfoBubbleView*>(bubble_view);
  ASSERT_NE(page_info_bubble, nullptr);

  // Ensure closing the bubble works, and that this will start the collapse
  // animation of the chip.
  page_info_bubble->CloseBubble();

  // Fast forward animation to trigger callback and wait until it completes.
  GetChip()->animation_for_testing()->End();
  base::RunLoop().RunUntilIdle();

  ASSERT_FALSE(GetChip()->GetVisible());
}

IN_PROC_BROWSER_TEST_F(ConfirmationChipEnabledInteractiveTest,
                       HideChipWhenOmniboxIsEdited) {
  RequestPermission(permissions::RequestType::kGeolocation);
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(GetChip()->GetVisible());
  EXPECT_TRUE(GetChip()->GetText() ==
              l10n_util::GetStringUTF16(IDS_GEOLOCATION_PERMISSION_CHIP));

  test_api_->manager()->Accept(/*prompt_options=*/std::monostate());
  EXPECT_TRUE(GetChip()->GetVisible());
  EXPECT_TRUE(GetChip()->GetText() ==
              l10n_util::GetStringUTF16(
                  IDS_PERMISSIONS_PERMISSION_ALLOWED_CONFIRMATION));
  EXPECT_EQ(GetChip()->theme(), PermissionChipTheme::kNormalVisibility);

  // Simulate the user editing the omnibox.
  OmniboxView* omnibox_view = GetLocationBarView()->GetOmniboxView();
  omnibox_view->SetFocus(/*is_user_initiated=*/true);
  omnibox_view->SetUserText(u"Typing in the Omnibox...");
  views::test::RunScheduledLayout(GetLocationBarView());
  EXPECT_TRUE(GetLocationBarView()->IsEditingOrEmpty());
  EXPECT_FALSE(GetChip()->GetVisible());
}


class PageInfoChangedWithin1mUmaTest : public PermissionChipInteractiveUITest {
 public:
  PageInfoChangedWithin1mUmaTest() = default;

  void InitAndRequestNotification() {
    ASSERT_TRUE(embedded_test_server()->Start());
    url_ = (embedded_test_server()->GetURL("/empty.html"));
    content::RenderFrameHost* main_rfh =
        ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(),
                                                                  url_, 1);
    content::WebContents::FromRenderFrameHost(main_rfh)->Focus();
    content::WebContents* embedder_contents =
        browser()->GetTabStripModel()->GetActiveWebContents();
    ASSERT_TRUE(embedder_contents);

    permissions::PermissionRequestObserver observer(embedder_contents);

    EXPECT_TRUE(content::ExecJs(
        main_rfh, kRequestNotifications,
        content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));

    observer.Wait();
  }

  PageInfoBubbleView* OpenAndGetPageInfoBubbleView() {
    base::RunLoop run_loop;
    GetPageInfoDialogCreatedCallbackForTesting() = run_loop.QuitClosure();
    OpenPageInfoBubble(browser());
    run_loop.Run();
    auto* bubble_view = static_cast<PageInfoBubbleView*>(
        PageInfoBubbleView::GetPageInfoBubbleForTesting());
    EXPECT_TRUE(bubble_view);

    return bubble_view;
  }

  void OpenPageInfoAndTogglePermission() {
    PageInfoBubbleView* page_info = OpenAndGetPageInfoBubbleView();
    views::View* permisison_toggle =
        GetViewWithinPageInfo(
            page_info, PageInfoViewFactory::VIEW_ID_PAGE_INFO_PERMISSION_VIEW)
            ->GetViewByID(PageInfoViewFactory::
                              VIEW_ID_PERMISSION_TOGGLE_ROW_TOGGLE_BUTTON);

    ASSERT_TRUE(permisison_toggle);

    PerformMouseClickOnView(
        static_cast<views::ToggleButton*>(permisison_toggle));
  }

  void OpenPageInfoAndClickReset() {
    PageInfoBubbleView* page_info = OpenAndGetPageInfoBubbleView();
    views::View* reset_permissions_view = GetViewWithinPageInfo(
        page_info,
        PageInfoViewFactory::VIEW_ID_PAGE_INFO_RESET_PERMISSIONS_BUTTON);

    PerformMouseClickOnView(
        static_cast<views::MdTextButton*>(reset_permissions_view));
  }

 private:
  void OpenPageInfoBubble(Browser* browser) {
    BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
    LocationIconView* location_icon_view =
        browser_view->toolbar()->location_bar_view()->location_icon_view();
    ASSERT_TRUE(location_icon_view);
    ui::test::TestEvent event;
    location_icon_view->ShowBubble(event);
    views::BubbleDialogDelegateView* page_info =
        PageInfoBubbleView::GetPageInfoBubbleForTesting();
    EXPECT_NE(nullptr, page_info);
    page_info->set_close_on_deactivate(false);
  }

  views::View* GetViewWithinPageInfo(PageInfoBubbleView* page_info_bubble,
                                     int view_id) {
    views::View* view = page_info_bubble->GetViewByID(view_id);
    EXPECT_TRUE(view);
    return view;
  }

  void PerformMouseClickOnView(views::Button* button) {
    views::test::ButtonTestApi(button).NotifyClick(
        ui::MouseEvent(ui::EventType::kMousePressed, gfx::Point(), gfx::Point(),
                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0));
  }

  GURL url_;
};

IN_PROC_BROWSER_TEST_F(PageInfoChangedWithin1mUmaTest,
                       VerifyResetFromAllowedUmaMetric) {
  InitAndRequestNotification();
  base::HistogramTester histograms;

  test_api_->manager()->Accept(/*prompt_options=*/std::monostate());

  OpenPageInfoAndClickReset();

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.PageInfo.ChangedWithin1m.Notifications",
      static_cast<int>(permissions::PermissionChangeAction::RESET_FROM_ALLOWED),
      1);
}

IN_PROC_BROWSER_TEST_F(PageInfoChangedWithin1mUmaTest,
                       VerifyResetFromDeniedUmaMetric) {
  InitAndRequestNotification();
  base::HistogramTester histograms;

  test_api_->manager()->Deny(/*prompt_options=*/std::monostate());

  OpenPageInfoAndClickReset();

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.PageInfo.ChangedWithin1m.Notifications",
      static_cast<int>(permissions::PermissionChangeAction::RESET_FROM_DENIED),
      1);
}

IN_PROC_BROWSER_TEST_F(PageInfoChangedWithin1mUmaTest, VerifyRevokedUmaMetric) {
  InitAndRequestNotification();
  base::HistogramTester histograms;

  test_api_->manager()->Accept(/*prompt_options=*/std::monostate());

  OpenPageInfoAndTogglePermission();

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.PageInfo.ChangedWithin1m.Notifications",
      static_cast<int>(permissions::PermissionChangeAction::REVOKED), 1);
}

IN_PROC_BROWSER_TEST_F(PageInfoChangedWithin1mUmaTest, VerifyReallowUmaMetric) {
  InitAndRequestNotification();
  base::HistogramTester histograms;

  test_api_->manager()->Deny(/*prompt_options=*/std::monostate());

  OpenPageInfoAndTogglePermission();

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.PageInfo.ChangedWithin1m.Notifications",
      static_cast<int>(permissions::PermissionChangeAction::REALLOWED), 1);
}

IN_PROC_BROWSER_TEST_F(PageInfoChangedWithin1mUmaTest,
                       VerifyNoReallowFromDenyRecordedAfter2mUmaMetric) {
  InitAndRequestNotification();
  base::HistogramTester histograms;

  test_api_->manager()->Deny(/*prompt_options=*/std::monostate());

  content::WebContents* web_contents = GetLocationBarView()->GetWebContents();
  const GURL& origin = permissions::PermissionUtil::GetLastCommittedOriginAsURL(
      web_contents->GetPrimaryMainFrame());
  permissions::OriginKeyedPermissionActionService* permission_action_service =
      permissions::PermissionsClient::Get()
          ->GetOriginKeyedPermissionActionService(
              GetLocationBarView()->GetWebContents()->GetBrowserContext());

  // Get recorded entry and manually change its time to 2 minutes ago.
  std::optional<permissions::PermissionActionTime> record =
      permission_action_service->GetLastActionEntry(
          origin, ContentSettingsType::NOTIFICATIONS);
  EXPECT_TRUE(record.has_value());
  permission_action_service->RecordActionWithTimeForTesting(
      origin, ContentSettingsType::NOTIFICATIONS, record->first,
      record->second - base::Minutes(2));

  OpenPageInfoAndTogglePermission();

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.PageInfo.ChangedWithin1m.Notifications",
      static_cast<int>(permissions::PermissionChangeAction::REALLOWED), 0);
}

class ChipGestureSensitiveEnabledInteractiveTest
    : public PermissionChipInteractiveUITest {
 public:
  ChipGestureSensitiveEnabledInteractiveTest() = default;
};
IN_PROC_BROWSER_TEST_F(ChipGestureSensitiveEnabledInteractiveTest,
                       ChipAutoPopupBubbleEnabled) {
  RequestPermission(permissions::RequestType::kGeolocation);

  EXPECT_EQ(
      test_api_->manager()->current_request_prompt_disposition_for_testing(),
      permissions::PermissionPromptDisposition::
          LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE);

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

  RequestPermission(permissions::RequestType::kNotifications);

  EXPECT_EQ(
      test_api_->manager()->current_request_prompt_disposition_for_testing(),
      permissions::PermissionPromptDisposition::
          LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE);

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

  RequestPermission(permissions::RequestType::kMidiSysex);

  EXPECT_EQ(
      test_api_->manager()->current_request_prompt_disposition_for_testing(),
      permissions::PermissionPromptDisposition::
          LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE);
}

class QuietChipAutoPopupBubbleInteractiveTest
    : public PermissionChipInteractiveUITest {
 public:
  QuietChipAutoPopupBubbleInteractiveTest() {
    scoped_feature_list_.InitWithFeatures({features::kQuietNotificationPrompts},
                                          {});
  }

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

  void SetCannedUiDecision(const Decision& decision) {
    test_api_->manager()->set_permission_ui_selector_for_testing(
        std::make_unique<MockPermissionUiSelector>(decision));
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_F(QuietChipAutoPopupBubbleInteractiveTest,
                       IgnoreChipHistogramsTest) {
  base::HistogramTester histograms;

  RequestPermission(permissions::RequestType::kGeolocation);

  ASSERT_EQ(
      test_api_->manager()->current_request_prompt_disposition_for_testing(),
      permissions::PermissionPromptDisposition::
          LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE);

  test_api_->manager()->Ignore(/*prompt_options=*/std::monostate());

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.Prompt.Geolocation.LocationBarLeftChipAutoBubble.Action",
      static_cast<int>(permissions::PermissionAction::IGNORED), 1);
}

IN_PROC_BROWSER_TEST_F(QuietChipAutoPopupBubbleInteractiveTest,
                       GrantedChipHistogramsTest) {
  base::HistogramTester histograms;

  RequestPermission(permissions::RequestType::kGeolocation);

  ASSERT_EQ(
      test_api_->manager()->current_request_prompt_disposition_for_testing(),
      permissions::PermissionPromptDisposition::
          LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE);

  test_api_->manager()->Accept(/*prompt_options=*/std::monostate());

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.Prompt.Geolocation.LocationBarLeftChipAutoBubble.Action",
      static_cast<int>(permissions::PermissionAction::GRANTED), 1);
}

IN_PROC_BROWSER_TEST_F(QuietChipAutoPopupBubbleInteractiveTest,
                       DeniedChipHistogramsTest) {
  base::HistogramTester histograms;

  RequestPermission(permissions::RequestType::kGeolocation);

  ASSERT_EQ(
      test_api_->manager()->current_request_prompt_disposition_for_testing(),
      permissions::PermissionPromptDisposition::
          LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE);

  test_api_->manager()->Deny(/*prompt_options=*/std::monostate());

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.Prompt.Geolocation.LocationBarLeftChipAutoBubble.Action",
      static_cast<int>(permissions::PermissionAction::DENIED), 1);
}

IN_PROC_BROWSER_TEST_F(QuietChipAutoPopupBubbleInteractiveTest,
                       DismissedChipHistogramsTest) {
  base::HistogramTester histograms;

  RequestPermission(permissions::RequestType::kGeolocation);

  ASSERT_EQ(
      test_api_->manager()->current_request_prompt_disposition_for_testing(),
      permissions::PermissionPromptDisposition::
          LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE);
  base::TimeDelta duration = base::Milliseconds(42);
  test_api_->manager()->set_time_to_decision_for_test(duration);

  test_api_->manager()->Dismiss(/*prompt_options=*/std::monostate());

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.Prompt.Geolocation.LocationBarLeftChipAutoBubble.Action",
      static_cast<int>(permissions::PermissionAction::DISMISSED), 1);

  histograms.ExpectTimeBucketCount(
      "Permissions.Prompt.Geolocation.LocationBarLeftChipAutoBubble.Dismissed."
      "TimeToAction",
      duration, 1);
}

IN_PROC_BROWSER_TEST_F(QuietChipAutoPopupBubbleInteractiveTest,
                       QuietChipNonAbusiveUmaTest) {
  base::HistogramTester histograms;

  for (QuietUiReason reason :
       {QuietUiReason::kEnabledInPrefs,
        QuietUiReason::kServicePredictedVeryUnlikelyGrant}) {
    SetCannedUiDecision(
        Decision::UseQuietUi(reason, Decision::ShowNoWarning()));

    RequestPermission(permissions::RequestType::kNotifications);

    ASSERT_EQ(
        test_api_->manager()->current_request_prompt_disposition_for_testing(),
        permissions::PermissionPromptDisposition::LOCATION_BAR_LEFT_QUIET_CHIP);

    ClickOnChip(GetChip());

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

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.Prompt.Notifications.LocationBarLeftQuietChip.Ignored."
      "DidShowBubble",
      static_cast<int>(true), 2);

  histograms.ExpectBucketCount(
      "Permissions.Prompt.Notifications.LocationBarLeftQuietChip.Ignored."
      "DidClickManage",
      static_cast<int>(false), 2);
}

IN_PROC_BROWSER_TEST_F(QuietChipAutoPopupBubbleInteractiveTest,
                       QuietChipNonAbusiveClickManageUmaTest) {
  base::HistogramTester histograms;

  for (QuietUiReason reason :
       {QuietUiReason::kEnabledInPrefs,
        QuietUiReason::kServicePredictedVeryUnlikelyGrant}) {
    SetCannedUiDecision(
        Decision::UseQuietUi(reason, Decision::ShowNoWarning()));

    RequestPermission(permissions::RequestType::kNotifications);

    ASSERT_EQ(
        test_api_->manager()->current_request_prompt_disposition_for_testing(),
        permissions::PermissionPromptDisposition::LOCATION_BAR_LEFT_QUIET_CHIP);

    ClickOnChip(GetChip());

    views::View* bubble_view =
        GetChipController()->get_prompt_bubble_view_for_testing();
    ContentSettingBubbleContents* permission_prompt_bubble =
        static_cast<ContentSettingBubbleContents*>(bubble_view);

    ASSERT_TRUE(permission_prompt_bubble != nullptr);

    permission_prompt_bubble->managed_button_clicked_for_test();

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

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.Prompt.Notifications.LocationBarLeftQuietChip.Ignored."
      "DidShowBubble",
      static_cast<int>(true), 2);

  histograms.ExpectBucketCount(
      "Permissions.Prompt.Notifications.LocationBarLeftQuietChip.Ignored."
      "DidClickManage",
      static_cast<int>(true), 2);
}

IN_PROC_BROWSER_TEST_F(QuietChipAutoPopupBubbleInteractiveTest,
                       PermissionIgnoredQuietChipAbusiveUmaTest) {
  base::HistogramTester histograms;

  for (QuietUiReason reason : {QuietUiReason::kTriggeredByCrowdDeny,
                               QuietUiReason::kTriggeredDueToAbusiveRequests,
                               QuietUiReason::kTriggeredDueToAbusiveContent}) {
    SetCannedUiDecision(
        Decision::UseQuietUi(reason, Decision::ShowNoWarning()));

    RequestPermission(permissions::RequestType::kNotifications);

    ASSERT_EQ(
        test_api_->manager()->current_request_prompt_disposition_for_testing(),
        permissions::PermissionPromptDisposition::
            LOCATION_BAR_LEFT_QUIET_ABUSIVE_CHIP);

    ClickOnChip(GetChip());

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

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.Prompt.Notifications.LocationBarLeftQuietAbusiveChip."
      "Ignored."
      "DidShowBubble",
      static_cast<int>(true), 3);

  histograms.ExpectBucketCount(
      "Permissions.Prompt.Notifications.LocationBarLeftQuietAbusiveChip."
      "Ignored.DidClickLearnMore",
      static_cast<int>(false), 3);
}

IN_PROC_BROWSER_TEST_F(QuietChipAutoPopupBubbleInteractiveTest,
                       PermissionGrantedQuietChipAbusiveUmaTest) {
  base::HistogramTester histograms;

  for (QuietUiReason reason : {QuietUiReason::kTriggeredByCrowdDeny,
                               QuietUiReason::kTriggeredDueToAbusiveRequests,
                               QuietUiReason::kTriggeredDueToAbusiveContent}) {
    SetCannedUiDecision(
        Decision::UseQuietUi(reason, Decision::ShowNoWarning()));

    RequestPermission(permissions::RequestType::kNotifications);

    ASSERT_EQ(
        test_api_->manager()->current_request_prompt_disposition_for_testing(),
        permissions::PermissionPromptDisposition::
            LOCATION_BAR_LEFT_QUIET_ABUSIVE_CHIP);

    ClickOnChip(GetChip());

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

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.Prompt.Notifications.LocationBarLeftQuietAbusiveChip."
      "Accepted.DidClickLearnMore",
      static_cast<int>(false), 3);
}

IN_PROC_BROWSER_TEST_F(QuietChipAutoPopupBubbleInteractiveTest,
                       PermissionGrantedOnceQuietChipAbusiveUmaTest) {
  base::HistogramTester histograms;

  for (QuietUiReason reason : {QuietUiReason::kTriggeredByCrowdDeny,
                               QuietUiReason::kTriggeredDueToAbusiveRequests,
                               QuietUiReason::kTriggeredDueToAbusiveContent}) {
    SetCannedUiDecision(
        Decision::UseQuietUi(reason, Decision::ShowNoWarning()));

    RequestPermission(permissions::RequestType::kNotifications);

    ASSERT_EQ(
        test_api_->manager()->current_request_prompt_disposition_for_testing(),
        permissions::PermissionPromptDisposition::
            LOCATION_BAR_LEFT_QUIET_ABUSIVE_CHIP);

    ClickOnChip(GetChip());

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

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.Prompt.Notifications.LocationBarLeftQuietAbusiveChip."
      "AcceptedOnce.DidClickLearnMore",
      static_cast<int>(false), 3);
}

IN_PROC_BROWSER_TEST_F(QuietChipAutoPopupBubbleInteractiveTest,
                       PermissionDeniedOnceQuietChipAbusiveUmaTest) {
  base::HistogramTester histograms;

  for (QuietUiReason reason : {QuietUiReason::kTriggeredByCrowdDeny,
                               QuietUiReason::kTriggeredDueToAbusiveRequests,
                               QuietUiReason::kTriggeredDueToAbusiveContent}) {
    SetCannedUiDecision(
        Decision::UseQuietUi(reason, Decision::ShowNoWarning()));

    RequestPermission(permissions::RequestType::kNotifications);

    ASSERT_EQ(
        test_api_->manager()->current_request_prompt_disposition_for_testing(),
        permissions::PermissionPromptDisposition::
            LOCATION_BAR_LEFT_QUIET_ABUSIVE_CHIP);

    ClickOnChip(GetChip());

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

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.Prompt.Notifications.LocationBarLeftQuietAbusiveChip."
      "Denied.DidClickLearnMore",
      static_cast<int>(false), 3);
}

IN_PROC_BROWSER_TEST_F(QuietChipAutoPopupBubbleInteractiveTest,
                       PermissionDismissedOnceQuietChipAbusiveUmaTest) {
  base::HistogramTester histograms;

  for (QuietUiReason reason : {QuietUiReason::kTriggeredByCrowdDeny,
                               QuietUiReason::kTriggeredDueToAbusiveRequests,
                               QuietUiReason::kTriggeredDueToAbusiveContent}) {
    SetCannedUiDecision(
        Decision::UseQuietUi(reason, Decision::ShowNoWarning()));

    RequestPermission(permissions::RequestType::kNotifications);

    ASSERT_EQ(
        test_api_->manager()->current_request_prompt_disposition_for_testing(),
        permissions::PermissionPromptDisposition::
            LOCATION_BAR_LEFT_QUIET_ABUSIVE_CHIP);

    ClickOnChip(GetChip());

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

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.Prompt.Notifications.LocationBarLeftQuietAbusiveChip."
      "Dismissed.DidClickLearnMore",
      static_cast<int>(false), 3);
}

IN_PROC_BROWSER_TEST_F(QuietChipAutoPopupBubbleInteractiveTest,
                       QuietChipAbusiveClickLearnMoreUmaTest) {
  base::HistogramTester histograms;

  for (QuietUiReason reason : {QuietUiReason::kTriggeredByCrowdDeny,
                               QuietUiReason::kTriggeredDueToAbusiveRequests,
                               QuietUiReason::kTriggeredDueToAbusiveContent}) {
    SetCannedUiDecision(
        Decision::UseQuietUi(reason, Decision::ShowNoWarning()));

    RequestPermission(permissions::RequestType::kNotifications);

    ASSERT_EQ(
        test_api_->manager()->current_request_prompt_disposition_for_testing(),
        permissions::PermissionPromptDisposition::
            LOCATION_BAR_LEFT_QUIET_ABUSIVE_CHIP);

    ClickOnChip(GetChip());

    views::View* bubble_view =
        GetChipController()->get_prompt_bubble_view_for_testing();
    ContentSettingBubbleContents* permission_prompt_bubble =
        static_cast<ContentSettingBubbleContents*>(bubble_view);

    ASSERT_TRUE(permission_prompt_bubble != nullptr);

    permission_prompt_bubble->learn_more_button_clicked_for_test();

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

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.Prompt.Notifications.LocationBarLeftQuietAbusiveChip."
      "Ignored."
      "DidShowBubble",
      static_cast<int>(true), 3);

  histograms.ExpectBucketCount(
      "Permissions.Prompt.Notifications.LocationBarLeftQuietAbusiveChip."
      "Ignored.DidClickLearnMore",
      static_cast<int>(true), 3);
}

IN_PROC_BROWSER_TEST_F(QuietChipAutoPopupBubbleInteractiveTest,
                       QuietChipAutoPopupBubbleEnabled) {
  ASSERT_TRUE(
      base::FeatureList::IsEnabled(features::kQuietNotificationPrompts));

  SetCannedUiDecision(Decision::UseQuietUi(
      QuietUiReason::kTriggeredDueToAbusiveContent, Decision::ShowNoWarning()));

  RequestPermission(permissions::RequestType::kCameraStream);

  EXPECT_EQ(
      test_api_->manager()->current_request_prompt_disposition_for_testing(),
      permissions::PermissionPromptDisposition::
          LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE);

  RequestPermission(permissions::RequestType::kGeolocation);

  EXPECT_EQ(
      test_api_->manager()->current_request_prompt_disposition_for_testing(),
      permissions::PermissionPromptDisposition::
          LOCATION_BAR_LEFT_QUIET_ABUSIVE_CHIP);

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

  RequestPermission(permissions::RequestType::kNotifications);

  EXPECT_EQ(
      test_api_->manager()->current_request_prompt_disposition_for_testing(),
      permissions::PermissionPromptDisposition::
          LOCATION_BAR_LEFT_QUIET_ABUSIVE_CHIP);

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

  RequestPermission(permissions::RequestType::kMidiSysex);

  EXPECT_EQ(
      test_api_->manager()->current_request_prompt_disposition_for_testing(),
      permissions::PermissionPromptDisposition::
          LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE);
}

class QuietChipFailFastInteractiveTest
    : public PermissionChipInteractiveUITest {
 public:
  QuietChipFailFastInteractiveTest() {
    scoped_feature_list_.InitWithFeatures({features::kQuietNotificationPrompts},
                                          {});
  }

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

  void SetCannedUiDecision(const Decision& decision) {
    test_api_->manager()->set_permission_ui_selector_for_testing(
        std::make_unique<MockPermissionUiSelector>(decision));
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_F(QuietChipFailFastInteractiveTest,
                       NormalChipNoPreignoreTest) {
  base::HistogramTester histograms;

  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL url(embedded_test_server()->GetURL("/title1.html"));
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  content::RenderFrameHost* main_rfh =
      ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
                                                                1);
  ASSERT_TRUE(main_rfh);

  // Keep it above `IsSubscribedToPermissionChangeEvent` to make sure it does
  // not influence it.
  EXPECT_EQ(false, content::EvalJs(main_rfh, kCheckNotifications));

  bool IsPermissionStatusSubscribed =
      web_contents->GetBrowserContext()
          ->GetPermissionController()
          ->IsSubscribedToPermissionChangeEvent(
              blink::PermissionType::NOTIFICATIONS, main_rfh);

  EXPECT_FALSE(IsPermissionStatusSubscribed);

  base::RunLoop run_loop;

  content::AddNotifyListenerObserver(
      main_rfh->GetBrowserContext()->GetPermissionController(),
      run_loop.QuitClosure());

  EXPECT_EQ(true, content::EvalJs(main_rfh, kAddNotificationsEventListener));

  // `kAddNotificationsEventListener` execution is async. To informing that an
  // event listener has been added for a permission we should wait otherwise
  // `IsPermissionStatusSubscribed` is flaky.
  run_loop.Run();

  IsPermissionStatusSubscribed =
      web_contents->GetBrowserContext()
          ->GetPermissionController()
          ->IsSubscribedToPermissionChangeEvent(
              blink::PermissionType::NOTIFICATIONS, main_rfh);

  EXPECT_TRUE(IsPermissionStatusSubscribed);

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

  EXPECT_FALSE(manager->IsRequestInProgress());

  {
    permissions::PermissionRequestObserver observer(web_contents);

    // Request permission in foreground tab, prompt should be shown.
    EXPECT_TRUE(content::ExecJs(
        main_rfh, kRequestNotifications,
        content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));

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

  EXPECT_TRUE(manager->IsRequestInProgress());

  manager->Accept(/*prompt_options=*/std::monostate());

  EXPECT_EQ(true, content::EvalJs(main_rfh, kCheckNotifications));
  EXPECT_FALSE(manager->IsRequestInProgress());

  IsPermissionStatusSubscribed =
      web_contents->GetBrowserContext()
          ->GetPermissionController()
          ->IsSubscribedToPermissionChangeEvent(
              blink::PermissionType::NOTIFICATIONS, main_rfh);

  EXPECT_TRUE(IsPermissionStatusSubscribed);

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.QuietPrompt.Preignore",
      static_cast<int>(blink::PermissionType::NOTIFICATIONS), 0);
}

IN_PROC_BROWSER_TEST_F(QuietChipFailFastInteractiveTest,
                       EventListenerAddedTest) {
  base::HistogramTester histograms;

  SetCannedUiDecision(
      Decision::UseQuietUi(QuietUiReason::kTriggeredDueToAbusiveRequests,
                           WarningReason::kAbusiveRequests));

  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL url(embedded_test_server()->GetURL("/title1.html"));
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  content::RenderFrameHost* main_rfh =
      ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
                                                                1);
  ASSERT_TRUE(main_rfh);

  // Keep it above `IsSubscribedToPermissionChangeEvent` to make sure it does
  // not influence it.
  EXPECT_EQ(false, content::EvalJs(main_rfh, kCheckNotifications));

  bool IsPermissionStatusSubscribed =
      web_contents->GetBrowserContext()
          ->GetPermissionController()
          ->IsSubscribedToPermissionChangeEvent(
              blink::PermissionType::NOTIFICATIONS, main_rfh);

  EXPECT_FALSE(IsPermissionStatusSubscribed);

  base::RunLoop run_loop;

  content::AddNotifyListenerObserver(
      main_rfh->GetBrowserContext()->GetPermissionController(),
      run_loop.QuitClosure());

  EXPECT_EQ(true, content::EvalJs(main_rfh, kAddNotificationsEventListener));

  // `kAddNotificationsEventListener` execution is async. To informing that an
  // event listener has been added for a permission we should wait otherwise
  // `IsPermissionStatusSubscribed` is flaky.
  run_loop.Run();

  IsPermissionStatusSubscribed =
      web_contents->GetBrowserContext()
          ->GetPermissionController()
          ->IsSubscribedToPermissionChangeEvent(
              blink::PermissionType::NOTIFICATIONS, main_rfh);

  EXPECT_TRUE(IsPermissionStatusSubscribed);

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

  EXPECT_FALSE(manager->IsRequestInProgress());

  EXPECT_EQ("default", content::EvalJs(main_rfh, kRequestNotifications));

  EXPECT_TRUE(manager->IsRequestInProgress());
  manager->Accept(/*prompt_options=*/std::monostate());

  EXPECT_EQ(true, content::EvalJs(main_rfh, kCheckNotifications));
  EXPECT_FALSE(manager->IsRequestInProgress());

  IsPermissionStatusSubscribed =
      web_contents->GetBrowserContext()
          ->GetPermissionController()
          ->IsSubscribedToPermissionChangeEvent(
              blink::PermissionType::NOTIFICATIONS, main_rfh);

  EXPECT_TRUE(IsPermissionStatusSubscribed);

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  histograms.ExpectBucketCount(
      "Permissions.QuietPrompt.Preignore",
      static_cast<int>(blink::PermissionType::NOTIFICATIONS), 1);
}

// There are two ways of setting `change` event listener:
// `PermissionStatus.onchange` and `PermissionStatus.addEventListener`. There
// are two ways of removing the listener: `PermissionStatus.onchange = null`,
// `PermissionStatus.removeEventListener`. Any of the listeners should
// initialize internal subscription map. We should remove the internal
// subscription only if there is no `change` event listener left.
IN_PROC_BROWSER_TEST_F(QuietChipFailFastInteractiveTest,
                       EventListenerRemovedTest) {
  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL url(embedded_test_server()->GetURL("/title1.html"));
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  content::RenderFrameHost* main_rfh =
      ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
                                                                1);
  ASSERT_TRUE(main_rfh);

  // Init global `PermissionStatus` variable and add API for assigning and
  // removing event listeners.
  ASSERT_TRUE(content::EvalJs(main_rfh, R"(
    var PermissionStatus;

    function onChangeListener(event) {}

    function addOnChange(){
      PermissionStatus.onchange = () => {};
    }

    function addEventListener(){
      PermissionStatus.addEventListener('change', onChangeListener);
    }

    function removeOnchange(){
      PermissionStatus.onchange = null;
    }

    function removeEventListener(){
      PermissionStatus.removeEventListener("change", onChangeListener);
    }
    )")
                  .is_ok());

  // Initialize global JS variable `PermissionStatus`.
  EXPECT_EQ(true, content::EvalJs(main_rfh, R"(
    new Promise(async resolve => {
      PermissionStatus =
        await navigator.permissions.query({name: 'notifications'});
      resolve(true);
    })
    )"));

  bool IsPermissionStatusSubscribed =
      web_contents->GetBrowserContext()
          ->GetPermissionController()
          ->IsSubscribedToPermissionChangeEvent(
              blink::PermissionType::NOTIFICATIONS, main_rfh);

  EXPECT_FALSE(IsPermissionStatusSubscribed);

  {
    // For each event create a new RunLoop otherwise crashes with
    // `run_loop.cc(335)] Check failed: run_allowed_`.
    base::RunLoop run_loop;
    content::AddNotifyListenerObserver(
        main_rfh->GetBrowserContext()->GetPermissionController(),
        run_loop.QuitClosure());

    // Set PermissionState.onchange listener.
    ASSERT_TRUE(content::EvalJs(main_rfh, "addOnChange()").is_ok());

    // `kAddNotificationsEventListener` execution is async. To informing that an
    // event listener has been added for a permission we should wait otherwise
    // `IsPermissionStatusSubscribed` is flaky.
    run_loop.Run();

    IsPermissionStatusSubscribed =
        web_contents->GetBrowserContext()
            ->GetPermissionController()
            ->IsSubscribedToPermissionChangeEvent(
                blink::PermissionType::NOTIFICATIONS, main_rfh);

    EXPECT_TRUE(IsPermissionStatusSubscribed);
  }

  {
    base::RunLoop run_loop;
    content::AddNotifyListenerObserver(
        main_rfh->GetBrowserContext()->GetPermissionController(),
        run_loop.QuitClosure());

    ASSERT_TRUE(content::EvalJs(main_rfh, "removeOnchange()").is_ok());

    run_loop.Run();

    IsPermissionStatusSubscribed =
        web_contents->GetBrowserContext()
            ->GetPermissionController()
            ->IsSubscribedToPermissionChangeEvent(
                blink::PermissionType::NOTIFICATIONS, main_rfh);

    EXPECT_FALSE(IsPermissionStatusSubscribed);
  }

  {
    base::RunLoop run_loop;
    content::AddNotifyListenerObserver(
        main_rfh->GetBrowserContext()->GetPermissionController(),
        run_loop.QuitClosure());

    // Add `change` event listener.
    ASSERT_TRUE(content::EvalJs(main_rfh, "addEventListener()").is_ok());

    run_loop.Run();

    IsPermissionStatusSubscribed =
        web_contents->GetBrowserContext()
            ->GetPermissionController()
            ->IsSubscribedToPermissionChangeEvent(
                blink::PermissionType::NOTIFICATIONS, main_rfh);

    EXPECT_TRUE(IsPermissionStatusSubscribed);
  }

  {
    base::RunLoop run_loop;
    content::AddNotifyListenerObserver(
        main_rfh->GetBrowserContext()->GetPermissionController(),
        run_loop.QuitClosure());
    // Add the second lisener.
    ASSERT_TRUE(content::EvalJs(main_rfh, "addOnChange()").is_ok());
    run_loop.Run();
  }

  {
    // Removing the first listener should not endup in disablign
    // `IsPermissionStatusSubscribed`. Do not need to call `run_loop.Run()` as
    // that event will not be processed.
    ASSERT_TRUE(content::EvalJs(main_rfh, "removeEventListener()").is_ok());
    // run_loop.Run();

    IsPermissionStatusSubscribed =
        web_contents->GetBrowserContext()
            ->GetPermissionController()
            ->IsSubscribedToPermissionChangeEvent(
                blink::PermissionType::NOTIFICATIONS, main_rfh);

    EXPECT_TRUE(IsPermissionStatusSubscribed);

    base::RunLoop run_loop;
    content::AddNotifyListenerObserver(
        main_rfh->GetBrowserContext()->GetPermissionController(),
        run_loop.QuitClosure());
    // This will remove the internal listener.
    ASSERT_TRUE(content::EvalJs(main_rfh, "removeOnchange()").is_ok());
    run_loop.Run();

    IsPermissionStatusSubscribed =
        web_contents->GetBrowserContext()
            ->GetPermissionController()
            ->IsSubscribedToPermissionChangeEvent(
                blink::PermissionType::NOTIFICATIONS, main_rfh);

    EXPECT_FALSE(IsPermissionStatusSubscribed);
  }
}

class PermissionChipGestureGatedDisabledInteractiveUITest
    : public PermissionChipInteractiveUITest {
 public:
  PermissionChipGestureGatedDisabledInteractiveUITest() {
    scoped_feature_list_.InitAndDisableFeature(
        permissions::features::kPermissionsGestureGatedPrompts);
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_F(PermissionChipGestureGatedDisabledInteractiveUITest,
                       PermissionChipWithAndWithoutUserGesture) {
  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL url(embedded_test_server()->GetURL("/title1.html"));
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  content::RenderFrameHost* main_rfh =
      ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
                                                                1);
  ASSERT_TRUE(main_rfh);

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

  EXPECT_FALSE(manager->IsRequestInProgress());

  // Request permission without user gesture
  {
    permissions::PermissionRequestObserver observer(web_contents);

    EXPECT_TRUE(content::ExecJs(
        main_rfh, kRequestNotifications,
        content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES |
            content::EvalJsOptions::EXECUTE_SCRIPT_NO_USER_GESTURE));

    observer.Wait();
    EXPECT_TRUE(observer.request_shown());

    EXPECT_TRUE(manager->IsRequestInProgress());
    EXPECT_FALSE(permissions::PermissionUtil::HasUserGesture(manager));
    std::optional<permissions::PermissionPromptDisposition> disposition =
        manager->current_request_prompt_disposition_for_testing();

    ASSERT_TRUE(disposition.has_value());
    EXPECT_EQ(permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
              disposition.value());
    manager->Dismiss(/*prompt_options=*/std::monostate());
  }

  // Request permission with user gesture
  {
    permissions::PermissionRequestObserver observer(web_contents);

    EXPECT_TRUE(content::ExecJs(
        main_rfh, kRequestNotifications,
        content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));

    observer.Wait();
    EXPECT_TRUE(observer.request_shown());

    EXPECT_TRUE(manager->IsRequestInProgress());
    EXPECT_TRUE(permissions::PermissionUtil::HasUserGesture(manager));
    std::optional<permissions::PermissionPromptDisposition> disposition =
        manager->current_request_prompt_disposition_for_testing();

    ASSERT_TRUE(disposition.has_value());
    EXPECT_EQ(permissions::PermissionPromptDisposition::
                  LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE,
              disposition.value());
  }
}

IN_PROC_BROWSER_TEST_F(PermissionChipInteractiveUITest,
                       PermissionRequestWithSameDocumentNavigation) {
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL top_url = embedded_test_server()->GetURL("/empty.html");
  GURL embedded_url = embedded_test_server()->GetURL("/empty.html");
  content::RenderFrameHost* main_rfh =
      ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(),
                                                                top_url, 1);
  ASSERT_TRUE(main_rfh);

  content::RenderFrameHost* subframe = CreateIframe(main_rfh, embedded_url);
  ASSERT_TRUE(subframe);

  EXPECT_EQ(false, content::EvalJs(main_rfh, kCheckNotifications));

  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  ASSERT_TRUE(web_contents);

  auto* manager =
      permissions::PermissionRequestManager::FromWebContents(web_contents);
  permissions::PermissionRequestObserver observer(web_contents);

  LocationBarView* location_bar =
      BrowserView::GetBrowserViewForBrowser(browser())->GetLocationBarView();
  ASSERT_TRUE(location_bar);
  ChipController* chip_controller = location_bar->GetChipController();
  ChipExpansionObserver chip_expansion_observer(chip_controller->chip());

  EXPECT_FALSE(manager->IsRequestInProgress());

  EXPECT_TRUE(content::ExecJs(
      main_rfh, kRequestNotifications,
      content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));

  // Wait until a permission request is shown.
  observer.Wait();

  // Wait until chip finished expanding
  chip_expansion_observer.WaitForChipToExpand();

  EXPECT_TRUE(manager->IsRequestInProgress());
  EXPECT_TRUE(observer.request_shown());
  EXPECT_TRUE(manager->GetCurrentPrompt());
  EXPECT_TRUE(chip_controller->IsPermissionPromptChipVisible());

  // At first, we verify that the same document navigation on both, top level
  // and embedded frames will not end up resolving or hiding the current active
  // permission request chip.

  // Same document navigation on the main frame.
  content::TestNavigationObserver main_frame_navigation_observer(web_contents,
                                                                 1);
  ASSERT_TRUE(content::ExecJs(main_rfh, "window.location = '#navigateHere'"));
  main_frame_navigation_observer.Wait();

  EXPECT_TRUE(manager->IsRequestInProgress());
  EXPECT_TRUE(chip_controller->IsPermissionPromptChipVisible());
  EXPECT_FALSE(chip_controller->IsAnimating());

  // Same document navigation on the child frame.
  content::TestNavigationObserver embedded_frame_navigation_observer(
      web_contents, 1);
  ASSERT_TRUE(content::ExecJs(subframe, "window.location = '#navigateHere'"));
  embedded_frame_navigation_observer.Wait();

  EXPECT_TRUE(manager->IsRequestInProgress());
  EXPECT_TRUE(chip_controller->IsPermissionPromptChipVisible());

  // Second, we verify that cross-origin navigation of the embedded iframe will
  // not end up resolving or hiding the current active permission request chip.
  ASSERT_TRUE(NavigateToURLFromRenderer(subframe, embedded_url));
  EXPECT_TRUE(manager->IsRequestInProgress());
  EXPECT_TRUE(chip_controller->IsPermissionPromptChipVisible());

  manager->Accept(/*prompt_options=*/std::monostate());

  EXPECT_EQ(true, content::EvalJs(main_rfh, kCheckNotifications));
}

IN_PROC_BROWSER_TEST_F(PermissionChipInteractiveUITest,
                       ObserverListensToPromptBubbleEvents) {
  auto permission_prompt_waiter =
      std::make_unique<ChipPromptWaiter>(GetChipController());
  RequestPermission(permissions::RequestType::kGeolocation);
  permission_prompt_waiter->WaitForShow();

  GetChipController()->GetBubbleWidget()->Close();
  permission_prompt_waiter->WaitForHide();
}

IN_PROC_BROWSER_TEST_F(PermissionChipInteractiveUITest,
                       IgnoreRequestWhenOmniboxIsEdited) {
  RequestPermission(permissions::RequestType::kGeolocation);
  EXPECT_TRUE(GetChip()->GetVisible());
  EXPECT_TRUE(test_api_->manager()->IsRequestInProgress());

  // Simulate the user editing the omnibox.
  OmniboxView* omnibox_view = GetLocationBarView()->GetOmniboxView();
  omnibox_view->SetFocus(/*is_user_initiated=*/true);
  omnibox_view->SetUserText(u"Typing in the Omnibox...");
  views::test::RunScheduledLayout(GetLocationBarView());
  ASSERT_TRUE(GetLocationBarView()->IsEditingOrEmpty());

  EXPECT_FALSE(test_api_->manager()->IsRequestInProgress());
  EXPECT_FALSE(GetChip()->GetVisible());
}

class TestWebContentsObserver : content::WebContentsObserver {
 public:
  explicit TestWebContentsObserver(content::WebContents* web_contents)
      : content::WebContentsObserver(web_contents) {}

  void Wait() { loop_.Run(); }

  void OnCapabilityTypesChanged(
      content::WebContentsCapabilityType capability_type,
      bool used) override {
    if (capability_type == content::WebContentsCapabilityType::kGeolocation) {
      loop_.Quit();
    }
  }
  base::RunLoop loop_;
};

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

  void SetPermission(ContentSettingsType type,
                     ContentSetting setting,
                     const GURL url) {
    HostContentSettingsMap* map =
        HostContentSettingsMapFactory::GetForProfile(browser()->GetProfile());

    map->SetContentSettingDefaultScope(url, url, type, setting);
  }

 private:
  std::unique_ptr<device::ScopedGeolocationOverrider> geolocation_overrider_;
};

IN_PROC_BROWSER_TEST_F(GeolocationUsageObserverBrowsertest,
                       GetCurrentPosition) {
  ASSERT_TRUE(embedded_test_server()->Start());
  content::WebContents* embedder_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  ASSERT_TRUE(embedder_contents);
  const GURL url(embedded_test_server()->GetURL("/empty.html"));
  content::RenderFrameHost* main_rfh =
      ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
                                                                1);
  content::WebContents::FromRenderFrameHost(main_rfh)->Focus();
  ASSERT_TRUE(main_rfh);
  ASSERT_FALSE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));

  // Set geolocation permission to allow.
  SetPermission(ContentSettingsType::GEOLOCATION,
                ContentSetting::CONTENT_SETTING_ALLOW, url);

  constexpr char kGetCurrentPosition[] = R"(
     navigator.geolocation.getCurrentPosition(_=>_);
    )";

  // The Geolocation service should stop automatically after one call.
  TestWebContentsObserver observer_1(embedder_contents);
  ASSERT_TRUE(content::ExecJs(main_rfh, kGetCurrentPosition));
  observer_1.Wait();
  EXPECT_TRUE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));
  TestWebContentsObserver observer_2(embedder_contents);
  observer_2.Wait();
  EXPECT_FALSE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));
}

IN_PROC_BROWSER_TEST_F(GeolocationUsageObserverBrowsertest,
                       WatchPositionAndClearWatch) {
  ASSERT_TRUE(embedded_test_server()->Start());
  content::WebContents* embedder_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  ASSERT_TRUE(embedder_contents);
  const GURL url(embedded_test_server()->GetURL("/empty.html"));
  content::RenderFrameHost* main_rfh =
      ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
                                                                1);
  content::WebContents::FromRenderFrameHost(main_rfh)->Focus();
  ASSERT_TRUE(main_rfh);

  ASSERT_FALSE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));

  // Set geolocation permission to allow.
  SetPermission(ContentSettingsType::GEOLOCATION,
                ContentSetting::CONTENT_SETTING_ALLOW, url);

  constexpr char kWatchPosition[] = R"(
     navigator.geolocation.watchPosition(_=>_);
    )";

  constexpr char kClearWatch[] = R"(
     navigator.geolocation.clearWatch(1);
    )";

  // javascript watchPosition() should start geolocation service.
  TestWebContentsObserver observer_1(embedder_contents);
  ASSERT_TRUE(content::ExecJs(main_rfh, kWatchPosition));
  observer_1.Wait();
  EXPECT_TRUE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));
  // javascript clearWatch() should stop geolocation service.
  TestWebContentsObserver observer_2(embedder_contents);
  ASSERT_TRUE(content::ExecJs(main_rfh, kClearWatch));
  observer_2.Wait();
  EXPECT_FALSE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));
}

IN_PROC_BROWSER_TEST_F(GeolocationUsageObserverBrowsertest,
                       GetCurrentPositionWhileWatchingPosition) {
  ASSERT_TRUE(embedded_test_server()->Start());
  content::WebContents* embedder_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  ASSERT_TRUE(embedder_contents);
  const GURL url(embedded_test_server()->GetURL("/empty.html"));
  content::RenderFrameHost* main_rfh =
      ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
                                                                1);
  content::WebContents::FromRenderFrameHost(main_rfh)->Focus();
  ASSERT_TRUE(main_rfh);

  ASSERT_FALSE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));

  // Set geolocation permission to allow.
  SetPermission(ContentSettingsType::GEOLOCATION,
                ContentSetting::CONTENT_SETTING_ALLOW, url);

  constexpr char kGetCurrentPosition[] = R"(
     navigator.geolocation.getCurrentPosition(_=>_);
    )";

  constexpr char kWatchPosition[] = R"(
     navigator.geolocation.watchPosition(_=>_);
    )";

  constexpr char kClearWatch[] = R"(
     navigator.geolocation.clearWatch(1);
    )";

  TestWebContentsObserver observer_1(embedder_contents);
  ASSERT_TRUE(content::ExecJs(main_rfh, kWatchPosition));
  observer_1.Wait();
  EXPECT_TRUE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));
  ASSERT_TRUE(content::ExecJs(main_rfh, kGetCurrentPosition));
  EXPECT_TRUE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));
  TestWebContentsObserver observer_2(embedder_contents);
  ASSERT_TRUE(content::ExecJs(main_rfh, kClearWatch));
  observer_2.Wait();
  EXPECT_FALSE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));
}

IN_PROC_BROWSER_TEST_F(GeolocationUsageObserverBrowsertest,
                       StartGeolocationInDifferentTab) {
  ASSERT_TRUE(embedded_test_server()->Start());
  content::WebContents* embedder_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  ASSERT_TRUE(embedder_contents);
  const GURL url(embedded_test_server()->GetURL("/empty.html"));
  content::RenderFrameHost* main_rfh =
      ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
                                                                1);
  content::WebContents::FromRenderFrameHost(main_rfh)->Focus();
  ASSERT_TRUE(main_rfh);

  ASSERT_FALSE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));

  // Set geolocation permission to allow.
  SetPermission(ContentSettingsType::GEOLOCATION,
                ContentSetting::CONTENT_SETTING_ALLOW, url);

  constexpr char kWatchPosition[] = R"(
     navigator.geolocation.watchPosition(_=>_);
    )";

  // Watch geolocation on different tab.
  // The usage will not record on main tab even they have the same origin.
  TabStripModel* tab_strip = browser()->GetTabStripModel();
  chrome::NewTabToRight(browser());
  EXPECT_EQ(2, tab_strip->count());
  tab_strip->ActivateTabAt(1);
  content::RenderFrameHost* rfh_tab_1 =
      ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
                                                                1);
  content::WebContents* web_contents_2 =
      browser()->GetTabStripModel()->GetActiveWebContents();
  TestWebContentsObserver observer_2(web_contents_2);
  ASSERT_TRUE(content::ExecJs(rfh_tab_1, kWatchPosition));
  observer_2.Wait();
  tab_strip->ActivateTabAt(0);
  EXPECT_FALSE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));
}

IN_PROC_BROWSER_TEST_F(GeolocationUsageObserverBrowsertest, ReloadPage) {
  ASSERT_TRUE(embedded_test_server()->Start());
  content::WebContents* embedder_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  ASSERT_TRUE(embedder_contents);
  const GURL url(embedded_test_server()->GetURL("/empty.html"));
  content::RenderFrameHost* main_rfh =
      ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
                                                                1);
  content::WebContents::FromRenderFrameHost(main_rfh)->Focus();
  ASSERT_TRUE(main_rfh);

  ASSERT_FALSE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));

  // Set geolocation permission to allow.
  SetPermission(ContentSettingsType::GEOLOCATION,
                ContentSetting::CONTENT_SETTING_ALLOW, url);

  constexpr char kWatchPosition[] = R"(
     navigator.geolocation.watchPosition(_=>_);
    )";

  // watchPosition then reload page, the geolocation service should remain.
  TestWebContentsObserver observer_1(embedder_contents);
  ASSERT_TRUE(content::ExecJs(main_rfh, kWatchPosition));
  observer_1.Wait();
  EXPECT_TRUE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));
  content::TestNavigationObserver reload_observer(embedder_contents);
  main_rfh->Reload();
  reload_observer.Wait();
  EXPECT_FALSE(embedder_contents->IsCapabilityActive(
      content::WebContentsCapabilityType::kGeolocation));
}
