chromium/components/permissions/contexts/geolocation_permission_context_unittest.cc

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

#include "components/permissions/contexts/geolocation_permission_context.h"

#include <stddef.h>

#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/containers/id_map.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/gtest_prod_util.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/time/clock.h"
#include "build/build_config.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "components/content_settings/browser/test_page_specific_content_settings_delegate.h"
#include "components/content_settings/core/browser/content_settings_observer.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/permissions/features.h"
#include "components/permissions/permission_context_base.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_request.h"
#include "components/permissions/permission_request_id.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/test/mock_permission_prompt_factory.h"
#include "components/permissions/test/permission_test_util.h"
#include "components/permissions/test/test_permissions_client.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_result.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/web_contents_tester.h"
#include "services/device/public/cpp/device_features.h"
#include "services/device/public/cpp/geolocation/buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "url/origin.h"

#if BUILDFLAG(IS_ANDROID)
#include "components/location/android/location_settings_dialog_outcome.h"
#include "components/location/android/mock_location_settings.h"
#include "components/permissions/contexts/geolocation_permission_context_android.h"
#include "components/prefs/pref_service.h"
#endif

#if BUILDFLAG(OS_LEVEL_GEOLOCATION_PERMISSION_SUPPORTED)
#include "components/permissions/contexts/geolocation_permission_context_system.h"
#include "services/device/public/cpp/test/fake_geolocation_system_permission_manager.h"
#endif  // BUILDFLAG(OS_LEVEL_GEOLOCATION_PERMISSION_SUPPORTED)

MockRenderProcessHost;

namespace permissions {
namespace {

PermissionStatus;

class TestGeolocationPermissionContextDelegate
    : public GeolocationPermissionContext::Delegate {};
}  // namespace

// GeolocationPermissionContextTests ------------------------------------------

class GeolocationPermissionContextTests
    : public content::RenderViewHostTestHarness,
      public permissions::Observer {};

GeolocationPermissionContextTests::GeolocationPermissionContextTests() {}

PermissionRequestID GeolocationPermissionContextTests::RequestID(
    int request_id) {}

PermissionRequestID GeolocationPermissionContextTests::RequestIDForTab(
    int tab,
    int request_id) {}

void GeolocationPermissionContextTests::RequestGeolocationPermission(
    const PermissionRequestID& id,
    const GURL& requesting_frame,
    bool user_gesture) {}

blink::mojom::PermissionStatus
GeolocationPermissionContextTests::GetPermissionStatus(
    blink::PermissionType permission,
    const GURL& requesting_origin) {}

void GeolocationPermissionContextTests::PermissionResponse(
    const PermissionRequestID& id,
    ContentSetting content_setting) {}

void GeolocationPermissionContextTests::OnPermissionChanged(
    const ContentSettingsPattern& primary_pattern,
    const ContentSettingsPattern& secondary_pattern,
    ContentSettingsTypeSet content_type_set) {}

void GeolocationPermissionContextTests::CheckPermissionMessageSent(
    int request_id,
    bool allowed) {}

void GeolocationPermissionContextTests::CheckPermissionMessageSentForTab(
    int tab,
    int request_id,
    bool allowed) {}

void GeolocationPermissionContextTests::CheckPermissionMessageSentInternal(
    MockRenderProcessHost* process,
    int request_id,
    bool allowed) {}

void GeolocationPermissionContextTests::AddNewTab(const GURL& url) {}

void GeolocationPermissionContextTests::CheckTabContentsState(
    const GURL& requesting_frame,
    ContentSetting expected_content_setting) {}

std::unique_ptr<content::BrowserContext>
GeolocationPermissionContextTests::CreateBrowserContext() {}

void GeolocationPermissionContextTests::SetUp() {}

void GeolocationPermissionContextTests::TearDown() {}

void GeolocationPermissionContextTests::SetupRequestManager(
    content::WebContents* web_contents) {}

#if BUILDFLAG(IS_ANDROID)

bool GeolocationPermissionContextTests::RequestPermissionIsLSDShown(
    const GURL& origin) {
  NavigateAndCommit(origin);
  RequestManagerDocumentLoadCompleted();
  MockLocationSettings::ClearHasShownLocationSettingsDialog();
  RequestGeolocationPermission(RequestID(0), origin, true);

  return MockLocationSettings::HasShownLocationSettingsDialog();
}

bool GeolocationPermissionContextTests::
    RequestPermissionIsLSDShownWithPermissionPrompt(const GURL& origin) {
  NavigateAndCommit(origin);
  RequestManagerDocumentLoadCompleted();
  MockLocationSettings::ClearHasShownLocationSettingsDialog();
  RequestGeolocationPermission(RequestID(0), origin, true);

  EXPECT_TRUE(HasActivePrompt());
  AcceptPrompt();

  return MockLocationSettings::HasShownLocationSettingsDialog();
}

void GeolocationPermissionContextTests::AddDayOffsetForTesting(int days) {
  GeolocationPermissionContextAndroid::AddDayOffsetForTesting(days);
}
#endif

void GeolocationPermissionContextTests::RequestManagerDocumentLoadCompleted() {}

void GeolocationPermissionContextTests::RequestManagerDocumentLoadCompleted(
    content::WebContents* web_contents) {}

ContentSetting GeolocationPermissionContextTests::GetGeolocationContentSetting(
    GURL frame_0,
    GURL frame_1) {}

void GeolocationPermissionContextTests::SetGeolocationContentSetting(
    GURL frame_0,
    GURL frame_1,
    ContentSetting content_setting) {}

bool GeolocationPermissionContextTests::HasActivePrompt() {}

bool GeolocationPermissionContextTests::HasActivePrompt(
    content::WebContents* web_contents) {}

void GeolocationPermissionContextTests::AcceptPrompt() {}

void GeolocationPermissionContextTests::AcceptPrompt(
    content::WebContents* web_contents) {}

void GeolocationPermissionContextTests::AcceptPromptThisTime() {}

void GeolocationPermissionContextTests::DenyPrompt() {}

void GeolocationPermissionContextTests::ClosePrompt() {}

std::u16string GeolocationPermissionContextTests::GetPromptText() {}

// Tests ----------------------------------------------------------------------

TEST_F(GeolocationPermissionContextTests, SinglePermissionPrompt) {}

TEST_F(GeolocationPermissionContextTests,
       SinglePermissionPromptFailsOnInsecureOrigin) {}

#if BUILDFLAG(IS_ANDROID)
// Tests concerning Android location settings permission
TEST_F(GeolocationPermissionContextTests, GeolocationEnabledDisabled) {
  GURL requesting_frame("https://www.example.com/geolocation");
  NavigateAndCommit(requesting_frame);
  RequestManagerDocumentLoadCompleted();
  base::HistogramTester histograms;
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/true,
      /*has_android_fine_location_permission=*/true,
      /*is_system_location_setting_enabled=*/true);
  EXPECT_FALSE(HasActivePrompt());
  RequestGeolocationPermission(RequestID(0), requesting_frame, true);
  EXPECT_TRUE(HasActivePrompt());
  histograms.ExpectTotalCount("Permissions.Action.Geolocation", 0);

  content::NavigationSimulator::Reload(web_contents());
  histograms.ExpectUniqueSample("Permissions.Action.Geolocation",
                                static_cast<int>(PermissionAction::IGNORED), 1);
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/false,
      /*has_android_fine_location_permission=*/false,
      /*is_system_location_setting_enabled=*/true);
  MockLocationSettings::SetCanPromptForAndroidPermission(false);
  EXPECT_FALSE(HasActivePrompt());
  RequestGeolocationPermission(RequestID(0), requesting_frame, true);
  histograms.ExpectUniqueSample("Permissions.Action.Geolocation",
                                static_cast<int>(PermissionAction::IGNORED), 1);
  EXPECT_FALSE(HasActivePrompt());
}

TEST_F(GeolocationPermissionContextTests, AndroidEnabledCanPromptAndAccept) {
  GURL requesting_frame("https://www.example.com/geolocation");
  NavigateAndCommit(requesting_frame);
  RequestManagerDocumentLoadCompleted();
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/false,
      /*has_android_fine_location_permission=*/false,
      /*is_system_location_setting_enabled=*/true);
  EXPECT_FALSE(HasActivePrompt());
  RequestGeolocationPermission(RequestID(0), requesting_frame, true);
  ASSERT_TRUE(HasActivePrompt());
  base::HistogramTester histograms;
  AcceptPrompt();
  histograms.ExpectUniqueSample("Permissions.Action.Geolocation",
                                static_cast<int>(PermissionAction::GRANTED), 1);
  CheckTabContentsState(requesting_frame, CONTENT_SETTING_ALLOW);
  CheckPermissionMessageSent(0, true);
}

TEST_F(GeolocationPermissionContextTests,
       AndroidEnabledCanPromptAndAcceptThisTime) {
  GURL requesting_frame("https://www.example.com/geolocation");
  NavigateAndCommit(requesting_frame);
  RequestManagerDocumentLoadCompleted();
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/false,
      /*has_android_fine_location_permission=*/false,
      /*is_system_location_setting_enabled=*/true);
  EXPECT_FALSE(HasActivePrompt());
  RequestGeolocationPermission(RequestID(0), requesting_frame, true);
  ASSERT_TRUE(HasActivePrompt());
  base::HistogramTester histograms;
  AcceptPromptThisTime();

  histograms.ExpectUniqueSample(
      "Permissions.Action.Geolocation",
      static_cast<int>(PermissionAction::GRANTED_ONCE), 1);
  CheckTabContentsState(requesting_frame, CONTENT_SETTING_ALLOW);
  CheckPermissionMessageSent(0, true);
}

TEST_F(GeolocationPermissionContextTests, AndroidEnabledCantPrompt) {
  GURL requesting_frame("https://www.example.com/geolocation");
  NavigateAndCommit(requesting_frame);
  RequestManagerDocumentLoadCompleted();
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/false,
      /*has_android_fine_location_permission=*/false,
      /*is_system_location_setting_enabled=*/true);
  MockLocationSettings::SetCanPromptForAndroidPermission(false);
  EXPECT_FALSE(HasActivePrompt());
  RequestGeolocationPermission(RequestID(0), requesting_frame, true);
  EXPECT_FALSE(HasActivePrompt());
}

TEST_F(GeolocationPermissionContextTests, SystemLocationOffLSDDisabled) {
  GURL requesting_frame("https://www.example.com/geolocation");
  NavigateAndCommit(requesting_frame);
  RequestManagerDocumentLoadCompleted();
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/true,
      /*has_android_fine_location_permission=*/true,
      /*is_system_location_setting_enabled=*/false);
  EXPECT_FALSE(HasActivePrompt());
  RequestGeolocationPermission(RequestID(0), requesting_frame, true);
  EXPECT_FALSE(HasActivePrompt());
  EXPECT_FALSE(MockLocationSettings::HasShownLocationSettingsDialog());
}

TEST_F(GeolocationPermissionContextTests, SystemLocationOnNoLSD) {
  GURL requesting_frame("https://www.example.com/geolocation");
  NavigateAndCommit(requesting_frame);
  RequestManagerDocumentLoadCompleted();
  EXPECT_FALSE(HasActivePrompt());
  RequestGeolocationPermission(RequestID(0), requesting_frame, true);
  ASSERT_TRUE(HasActivePrompt());
  AcceptPrompt();
  CheckTabContentsState(requesting_frame, CONTENT_SETTING_ALLOW);
  CheckPermissionMessageSent(0, true);
  EXPECT_FALSE(MockLocationSettings::HasShownLocationSettingsDialog());
}

TEST_F(GeolocationPermissionContextTests, SystemLocationOffLSDAccept) {
  GURL requesting_frame("https://www.example.com/geolocation");
  NavigateAndCommit(requesting_frame);
  RequestManagerDocumentLoadCompleted();
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/true,
      /*has_android_fine_location_permission=*/true,
      /*is_system_location_setting_enabled=*/false);
  MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
                                                        GRANTED);
  EXPECT_FALSE(HasActivePrompt());
  RequestGeolocationPermission(RequestID(0), requesting_frame, true);
  ASSERT_TRUE(HasActivePrompt());
  AcceptPrompt();
  CheckTabContentsState(requesting_frame, CONTENT_SETTING_ALLOW);
  CheckPermissionMessageSent(0, true);
  EXPECT_TRUE(MockLocationSettings::HasShownLocationSettingsDialog());
}

TEST_F(GeolocationPermissionContextTests, SystemLocationOffLSDReject) {
  GURL requesting_frame("https://www.example.com/geolocation");
  NavigateAndCommit(requesting_frame);
  RequestManagerDocumentLoadCompleted();
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/true,
      /*has_android_fine_location_permission=*/true,
      /*is_system_location_setting_enabled=*/false);
  MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
                                                        DENIED);
  EXPECT_FALSE(HasActivePrompt());
  RequestGeolocationPermission(RequestID(0), requesting_frame, true);
  ASSERT_TRUE(HasActivePrompt());
  AcceptPrompt();
  CheckTabContentsState(requesting_frame, CONTENT_SETTING_BLOCK);
  CheckPermissionMessageSent(0, false);
  EXPECT_TRUE(MockLocationSettings::HasShownLocationSettingsDialog());
}

TEST_F(GeolocationPermissionContextTests, LSDBackOffDifferentSites) {
  GURL requesting_frame_1("https://www.example.com/geolocation");
  GURL requesting_frame_2("https://www.example-2.com/geolocation");
  GURL requesting_frame_dse("https://www.dse.com/geolocation");

  delegate_->SetDSEOriginForTesting(url::Origin::Create(requesting_frame_dse));

  // Set all origin geolocation permissions to ALLOW.
  SetGeolocationContentSetting(requesting_frame_1, requesting_frame_1,
                               CONTENT_SETTING_ALLOW);
  SetGeolocationContentSetting(requesting_frame_2, requesting_frame_2,
                               CONTENT_SETTING_ALLOW);
  SetGeolocationContentSetting(requesting_frame_dse, requesting_frame_dse,
                               CONTENT_SETTING_ALLOW);

  // Turn off system location but allow the LSD to be shown, and denied.
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/true,
      /*has_android_fine_location_permission=*/true,
      /*is_system_location_setting_enabled=*/false);
  MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
                                                        DENIED);

  // Now permission requests should trigger the LSD, but the LSD will be denied,
  // putting the requesting origins into backoff. Check that the two non-DSE
  // origins share the same backoff, which is distinct to the DSE origin.
  // First, cancel a LSD prompt on the first non-DSE origin to go into backoff.
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame_1));

  // Now check that the LSD is prevented on this origin.
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame_1));

  // Now ask on the other non-DSE origin and check backoff prevented the prompt.
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame_2));

  // Now request on the DSE and check that the LSD is shown, as the non-DSE
  // backoff should not apply.
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame_dse));

  // Now check that the DSE is in backoff.
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame_dse));
}

TEST_F(GeolocationPermissionContextTests, LSDBackOffTiming) {
  GURL requesting_frame("https://www.example.com/geolocation");
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_ALLOW);

  // Turn off system location but allow the LSD to be shown, and denied.
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/true,
      /*has_android_fine_location_permission=*/true,
      /*is_system_location_setting_enabled=*/false);
  MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
                                                        DENIED);

  // First, cancel a LSD prompt on the first non-DSE origin to go into backoff.
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));

  // Check the LSD is prevented in 6 days time.
  AddDayOffsetForTesting(6);
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));

  // Check it is shown in one more days time, but then not straight after..
  AddDayOffsetForTesting(1);
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));

  // Check that it isn't shown 29 days after that.
  AddDayOffsetForTesting(29);
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));

  // Check it is shown in one more days time, but then not straight after..
  AddDayOffsetForTesting(1);
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));

  // Check that it isn't shown 89 days after that.
  AddDayOffsetForTesting(89);
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));

  // Check it is shown in one more days time, but then not straight after..
  AddDayOffsetForTesting(1);
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));

  // Check that it isn't shown 89 days after that.
  AddDayOffsetForTesting(89);
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));

  // Check it is shown in one more days time, but then not straight after..
  AddDayOffsetForTesting(1);
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));
}

TEST_F(GeolocationPermissionContextTests, LSDBackOffPermissionStatus) {
  GURL requesting_frame("https://www.example.com/geolocation");
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_ALLOW);

  // Turn off system location but allow the LSD to be shown, and denied.
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/true,
      /*has_android_fine_location_permission=*/true,
      /*is_system_location_setting_enabled=*/false);
  MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
                                                        DENIED);

  // The permission status should reflect that the LSD will be shown.
  ASSERT_EQ(PermissionStatus::ASK,
            GetPermissionStatus(blink::PermissionType::GEOLOCATION,
                                requesting_frame));
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));

  // Now that the LSD is in backoff, the permission status should reflect it.
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));
  ASSERT_EQ(PermissionStatus::DENIED,
            GetPermissionStatus(blink::PermissionType::GEOLOCATION,
                                requesting_frame));
}

TEST_F(GeolocationPermissionContextTests, LSDBackOffAskPromptsDespiteBackOff) {
  GURL requesting_frame("https://www.example.com/geolocation");
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_ALLOW);

  // Turn off system location but allow the LSD to be shown, and denied.
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/true,
      /*has_android_fine_location_permission=*/true,
      /*is_system_location_setting_enabled=*/false);
  MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
                                                        DENIED);

  // First, cancel a LSD prompt on the first non-DSE origin to go into backoff.
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));

  // Set the content setting back to ASK. The permission status should be
  // prompt, and the LSD prompt should now be shown.
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_ASK);
  ASSERT_EQ(PermissionStatus::ASK,
            GetPermissionStatus(blink::PermissionType::GEOLOCATION,
                                requesting_frame));
  EXPECT_TRUE(
      RequestPermissionIsLSDShownWithPermissionPrompt(requesting_frame));
}

TEST_F(GeolocationPermissionContextTests,
       LSDBackOffAcceptPermissionResetsBackOff) {
  GURL requesting_frame("https://www.example.com/geolocation");
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_ALLOW);

  // Turn off system location but allow the LSD to be shown, and denied.
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/true,
      /*has_android_fine_location_permission=*/true,
      /*is_system_location_setting_enabled=*/false);
  MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
                                                        DENIED);

  // First, get into the highest backoff state.
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
  AddDayOffsetForTesting(7);
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
  AddDayOffsetForTesting(30);
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
  AddDayOffsetForTesting(90);
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));

  // Now accept a permissions prompt.
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_ASK);
  EXPECT_TRUE(
      RequestPermissionIsLSDShownWithPermissionPrompt(requesting_frame));

  // Denying the LSD stops the content setting from being stored, so explicitly
  // set it to ALLOW.
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_ALLOW);

  // And check that back in the lowest backoff state.
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));
  AddDayOffsetForTesting(7);
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
}

TEST_F(GeolocationPermissionContextTests, LSDBackOffAcceptLSDResetsBackOff) {
  GURL requesting_frame("https://www.example.com/geolocation");
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_ALLOW);

  // Turn off system location but allow the LSD to be shown, and denied.
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/true,
      /*has_android_fine_location_permission=*/true,
      /*is_system_location_setting_enabled=*/false);
  MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
                                                        DENIED);

  // First, get into the highest backoff state.
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
  AddDayOffsetForTesting(7);
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
  AddDayOffsetForTesting(30);
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));

  // Now accept the LSD.
  AddDayOffsetForTesting(90);
  MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
                                                        GRANTED);
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));

  // Check that not in backoff, and that at the lowest backoff state.
  MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
                                                        DENIED);
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
  EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));
  AddDayOffsetForTesting(7);
  EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
}

#endif  // BUILDFLAG(IS_ANDROID)

TEST_F(GeolocationPermissionContextTests, HashIsIgnored) {}

TEST_F(GeolocationPermissionContextTests, DISABLED_PermissionForFileScheme) {}

TEST_F(GeolocationPermissionContextTests, CancelGeolocationPermissionRequest) {}

TEST_F(GeolocationPermissionContextTests, InvalidURL) {}

TEST_F(GeolocationPermissionContextTests, SameOriginMultipleTabs) {}

TEST_F(GeolocationPermissionContextTests, TabDestroyed) {}

#if BUILDFLAG(IS_ANDROID)
TEST_F(GeolocationPermissionContextTests, GeolocationStatusAndroidDisabled) {
  GURL requesting_frame("https://www.example.com/geolocation");

  // With the Android permission off, but location allowed for a domain, the
  // permission status should be ASK.
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_ALLOW);
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/false,
      /*has_android_fine_location_permission=*/false,
      /*is_system_location_setting_enabled=*/true);
  ASSERT_EQ(PermissionStatus::ASK,
            GetPermissionStatus(blink::PermissionType::GEOLOCATION,
                                requesting_frame));

  // With the Android permission off, and location blocked for a domain, the
  // permission status should still be BLOCK.
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_BLOCK);
  ASSERT_EQ(PermissionStatus::DENIED,
            GetPermissionStatus(blink::PermissionType::GEOLOCATION,
                                requesting_frame));

  // With the Android permission off, and location prompt for a domain, the
  // permission status should still be ASK.
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_ASK);
  ASSERT_EQ(PermissionStatus::ASK,
            GetPermissionStatus(blink::PermissionType::GEOLOCATION,
                                requesting_frame));
}

TEST_F(GeolocationPermissionContextTests, GeolocationStatusSystemDisabled) {
  GURL requesting_frame("https://www.example.com/geolocation");

  // With the system permission off, but location allowed for a domain, the
  // permission status should be reflect whether the LSD can be shown.
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_ALLOW);
  MockLocationSettings::SetLocationStatus(
      /*has_android_coarse_location_permission=*/true,
      /*has_android_fine_location_permission=*/true,
      /*is_system_location_setting_enabled=*/false);
  MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
                                                        DENIED);
  ASSERT_EQ(PermissionStatus::ASK,
            GetPermissionStatus(blink::PermissionType::GEOLOCATION,
                                requesting_frame));

  MockLocationSettings::SetLocationSettingsDialogStatus(false /* enabled */,
                                                        GRANTED);
  ASSERT_EQ(PermissionStatus::DENIED,
            GetPermissionStatus(blink::PermissionType::GEOLOCATION,
                                requesting_frame));

  // The result should be the same if the location permission is ASK.
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_ASK);
  MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
                                                        GRANTED);
  ASSERT_EQ(PermissionStatus::ASK,
            GetPermissionStatus(blink::PermissionType::GEOLOCATION,
                                requesting_frame));

  MockLocationSettings::SetLocationSettingsDialogStatus(false /* enabled */,
                                                        GRANTED);
  ASSERT_EQ(PermissionStatus::DENIED,
            GetPermissionStatus(blink::PermissionType::GEOLOCATION,
                                requesting_frame));

  // With the Android permission off, and location blocked for a domain, the
  // permission status should still be BLOCK.
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_BLOCK);
  MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
                                                        GRANTED);
  ASSERT_EQ(PermissionStatus::DENIED,
            GetPermissionStatus(blink::PermissionType::GEOLOCATION,
                                requesting_frame));
}

struct PermissionStateTestEntry {
  bool has_coarse_location;
  bool has_fine_location;
  int bucket;
} kPermissionStateTestEntries[] = {
    {/*has_coarse_location=*/false, /*has_fine_location=*/false, /*bucket=*/0},
    {/*has_coarse_location=*/true, /*has_fine_location=*/false, /*bucket=*/1},
    {/*has_coarse_location=*/false, /*has_fine_location=*/true, /*bucket=*/2},
    {/*has_coarse_location=*/true, /*has_fine_location=*/true, /*bucket=*/2},
};

class GeolocationAndroidPermissionRegularProfileTest
    : public content::RenderViewHostTestHarness,
      public testing::WithParamInterface<PermissionStateTestEntry> {};

TEST_P(GeolocationAndroidPermissionRegularProfileTest, Histogram) {
  const auto& [has_coarse_location, has_fine_location, bucket] = GetParam();
  MockLocationSettings::SetLocationStatus(
      has_coarse_location, has_fine_location,
      /*is_system_location_setting_enabled=*/true);
  base::HistogramTester histogram_tester;
  GeolocationPermissionContextAndroid context(
      browser_context(), /*delegate=*/nullptr, /*is_regular_profile=*/true,
      std::make_unique<MockLocationSettings>());
  histogram_tester.ExpectUniqueSample(
      "Geolocation.Android.LocationPermissionState", bucket,
      /*expected_bucket_count=*/1);
}

INSTANTIATE_TEST_SUITE_P(
    GeolocationAndroidPermissionRegularProfileTests,
    GeolocationAndroidPermissionRegularProfileTest,
    testing::ValuesIn(kPermissionStateTestEntries),
    [](const testing::TestParamInfo<PermissionStateTestEntry>& info) {
      return base::StringPrintf("Location%sFineLocation%s",
                                info.param.has_coarse_location ? "On" : "Off",
                                info.param.has_fine_location ? "On" : "Off");
    });

using GeolocationAndroidPermissionIrregularProfileTest =
    content::RenderViewHostTestHarness;

TEST_F(GeolocationAndroidPermissionIrregularProfileTest, DoesNotRecord) {
  base::HistogramTester histogram_tester;
  GeolocationPermissionContextAndroid context(
      browser_context(), /*delegate=*/nullptr, /*is_regular_profile=*/false,
      std::make_unique<MockLocationSettings>());
  histogram_tester.ExpectTotalCount(
      "Geolocation.Android.LocationPermissionState", /*expected_count=*/0);
}
#endif  // BUILDFLAG(IS_ANDROID)

#if BUILDFLAG(OS_LEVEL_GEOLOCATION_PERMISSION_SUPPORTED)
TEST_F(GeolocationPermissionContextTests,
       AllSystemAndSitePermissionCombinations) {
  GURL requesting_frame("https://www.example.com/geolocation");

  const struct {
    const LocationSystemPermissionStatus system_permission;
    const ContentSetting site_permission;
    const PermissionStatus expected_effective_site_permission;
  } kTestCases[] = {
      {LocationSystemPermissionStatus(LocationSystemPermissionStatus::kDenied),
       ContentSetting(CONTENT_SETTING_ASK), PermissionStatus::ASK},
      {LocationSystemPermissionStatus(LocationSystemPermissionStatus::kDenied),
       ContentSetting(CONTENT_SETTING_BLOCK), PermissionStatus::DENIED},
      {LocationSystemPermissionStatus(LocationSystemPermissionStatus::kDenied),
       ContentSetting(CONTENT_SETTING_ALLOW), PermissionStatus::DENIED},
      {LocationSystemPermissionStatus(
           LocationSystemPermissionStatus::kNotDetermined),
       ContentSetting(CONTENT_SETTING_ASK), PermissionStatus::ASK},
      {LocationSystemPermissionStatus(
           LocationSystemPermissionStatus::kNotDetermined),
       ContentSetting(CONTENT_SETTING_BLOCK), PermissionStatus::DENIED},
      {LocationSystemPermissionStatus(
           LocationSystemPermissionStatus::kNotDetermined),
       ContentSetting(CONTENT_SETTING_ALLOW), PermissionStatus::ASK},
      {LocationSystemPermissionStatus(LocationSystemPermissionStatus::kAllowed),
       ContentSetting(CONTENT_SETTING_ASK), PermissionStatus::ASK},
      {LocationSystemPermissionStatus(LocationSystemPermissionStatus::kAllowed),
       ContentSetting(CONTENT_SETTING_BLOCK), PermissionStatus::DENIED},
      {LocationSystemPermissionStatus(LocationSystemPermissionStatus::kAllowed),
       ContentSetting(CONTENT_SETTING_ALLOW), PermissionStatus::GRANTED},
  };

  for (auto test_case : kTestCases) {
    SetGeolocationContentSetting(requesting_frame, requesting_frame,
                                 test_case.site_permission);
    fake_geolocation_system_permission_manager_->SetSystemPermission(
        test_case.system_permission);
    base::RunLoop().RunUntilIdle();
    ASSERT_EQ(test_case.expected_effective_site_permission,
              GetPermissionStatus(blink::PermissionType::GEOLOCATION,
                                  requesting_frame));
  }
}

TEST_F(GeolocationPermissionContextTests, SystemPermissionUpdates) {
  GURL requesting_frame("https://www.example.com/geolocation");
  ContentSettingsPattern primary_pattern =
      ContentSettingsPattern::FromURLNoWildcard(requesting_frame);
  ContentSettingsPattern secondary_pattern = ContentSettingsPattern::Wildcard();

  geolocation_permission_context_->AddObserver(this);
  expected_primary_pattern_ = &primary_pattern;
  expected_secondary_pattern_ = &secondary_pattern;
  SetGeolocationContentSetting(requesting_frame, requesting_frame,
                               CONTENT_SETTING_ALLOW);
  ASSERT_EQ(1, num_permission_updates_);
  primary_pattern = ContentSettingsPattern::Wildcard();
  fake_geolocation_system_permission_manager_->SetSystemPermission(
      LocationSystemPermissionStatus::kDenied);
  fake_geolocation_system_permission_manager_->SetSystemPermission(
      LocationSystemPermissionStatus::kAllowed);
  base::RunLoop().RunUntilIdle();
  ASSERT_EQ(3, num_permission_updates_);
  geolocation_permission_context_->RemoveObserver(this);
}
#endif  // BUILDFLAG(OS_LEVEL_GEOLOCATION_PERMISSION_SUPPORTED)

}  // namespace permissions