blob: 338b850d828e519b28608a893b3e9043f65ac066 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/permissions/permission_actions_history.h"
#include <algorithm>
#include <optional>
#include <vector>
#include "base/containers/adapters.h"
#include "base/functional/bind.h"
#include "base/json/json_reader.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/test/gtest_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/permissions/permission_request_enums.h"
#include "components/permissions/permission_uma_util.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/permissions_client.h"
#include "components/permissions/pref_names.h"
#include "components/permissions/request_type.h"
#include "components/permissions/test/test_permissions_client.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
namespace permissions {
namespace {
constexpr int kHeuristicGrantThreshold = 3;
struct TestEntry {
PermissionAction action;
RequestType type;
PermissionPromptDisposition prompt_disposition;
bool advance_clock;
bool is_legacy_entry;
} kTestEntries[]{
{PermissionAction::DENIED, RequestType::kNotifications,
PermissionPromptDisposition::NOT_APPLICABLE, false, true},
{PermissionAction::IGNORED, RequestType::kNotifications,
PermissionPromptDisposition::NOT_APPLICABLE, false, true},
{PermissionAction::DISMISSED, RequestType::kNotifications,
PermissionPromptDisposition::MODAL_DIALOG, true, false},
{PermissionAction::GRANTED, RequestType::kNotifications,
PermissionPromptDisposition::LOCATION_BAR_LEFT_QUIET_CHIP, false, false},
{PermissionAction::DISMISSED, RequestType::kVrSession,
PermissionPromptDisposition::ANCHORED_BUBBLE, true, false},
{PermissionAction::IGNORED, RequestType::kCameraStream,
PermissionPromptDisposition::LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE, false,
false},
{PermissionAction::DISMISSED, RequestType::kGeolocation,
PermissionPromptDisposition::CUSTOM_MODAL_DIALOG, false, false},
{PermissionAction::DENIED, RequestType::kNotifications,
PermissionPromptDisposition::LOCATION_BAR_RIGHT_ANIMATED_ICON, true,
false},
{PermissionAction::GRANTED, RequestType::kNotifications,
PermissionPromptDisposition::ANCHORED_BUBBLE, false, false},
{PermissionAction::GRANTED_ONCE, RequestType::kNotifications,
PermissionPromptDisposition::ANCHORED_BUBBLE, false, false},
};
const char kLegacyPrefs[] = R"({
"notifications": [
{"time": "%s", "action" : 1},
{"time": "%s", "action" : 3}
]
})";
class MockPermissionActionsHistoryObserver
: public permissions::PermissionActionsHistory::Observer {
public:
void OnAutoGrantedHeuristically(
const GURL& origin,
ContentSettingsType content_setting) override {
origin_ = origin;
content_setting_ = content_setting;
call_count_++;
}
int call_count() const { return call_count_; }
const GURL& origin() const { return origin_; }
ContentSettingsType content_setting() const { return content_setting_; }
private:
int call_count_ = 0;
GURL origin_;
ContentSettingsType content_setting_ = ContentSettingsType::DEFAULT;
};
} // namespace
class PermissionActionHistoryTest : public testing::Test {
public:
PermissionActionHistoryTest() = default;
~PermissionActionHistoryTest() override = default;
PermissionActionsHistory* GetPermissionActionsHistory() {
return PermissionsClient::Get()->GetPermissionActionsHistory(
&browser_context_);
}
void SetUp() override {
testing::Test::SetUp();
RecordSetUpActions();
}
PermissionActionHistoryTest(const PermissionActionHistoryTest&) = delete;
PermissionActionHistoryTest& operator=(const PermissionActionHistoryTest&) =
delete;
std::vector<PermissionActionsHistory::Entry> GetHistory(
std::optional<RequestType> type,
PermissionActionsHistory::EntryFilter entry_filter =
PermissionActionsHistory::EntryFilter::WANT_ALL_PROMPTS) {
if (type.has_value()) {
return GetPermissionActionsHistory()->GetHistory(
base::Time(), type.value(), entry_filter);
} else {
return GetPermissionActionsHistory()->GetHistory(base::Time(),
entry_filter);
}
}
void RecordSetUpActions() {
const int64_t time =
base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds();
const std::string formatted_legacy_prefs =
base::StringPrintf(kLegacyPrefs, base::NumberToString(time).c_str(),
base::NumberToString(time).c_str());
std::optional<base::Value> legacy_pref_value = base::JSONReader::Read(
formatted_legacy_prefs, base::JSON_PARSE_CHROMIUM_EXTENSIONS);
GetPermissionActionsHistory()->GetPrefServiceForTesting()->Set(
prefs::kPermissionActions, legacy_pref_value.value());
// Record the actions needed to support test cases. This is the structure
// 3-days ago: {notification, dismissed}
// 2-days ago: {notification, granted}, {vr, dismissed}
// 1-days ago: {geolocation, ignored}, {camera, dismissed}, {notification,
// denied}
// 0-days ago: {notification, granted}
for (const auto& entry : kTestEntries) {
// Legacy entries are added directly to the pref above and not through
// Permission Actions History API
if (entry.is_legacy_entry)
continue;
GetPermissionActionsHistory()->RecordAction(entry.action, entry.type,
entry.prompt_disposition);
if (entry.advance_clock)
task_environment_.AdvanceClock(base::Days(1));
}
}
protected:
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
content::TestBrowserContext browser_context_;
private:
TestPermissionsClient permissions_client_;
};
TEST_F(PermissionActionHistoryTest, GetHistorySortedOrder) {
auto all_entries = GetHistory(std::nullopt);
EXPECT_EQ(10u, all_entries.size());
size_t index = 0;
for (const auto& entry : kTestEntries)
EXPECT_EQ(entry.action, all_entries[index++].action);
for (const auto& request_type :
{RequestType::kVrSession, RequestType::kCameraStream,
RequestType::kGeolocation, RequestType::kNotifications}) {
auto permission_entries = GetHistory(request_type);
index = 0;
for (const auto& entry : kTestEntries) {
if (entry.type != request_type) {
continue;
}
EXPECT_EQ(entry.action, permission_entries[index++].action);
}
}
auto entries_1_day = GetPermissionActionsHistory()->GetHistory(
base::Time::Now() - base::Days(1),
PermissionActionsHistory::EntryFilter::WANT_ALL_PROMPTS);
EXPECT_TRUE(std::ranges::equal(
entries_1_day, std::vector<PermissionActionsHistory::Entry>(
all_entries.begin() + 5, all_entries.end())));
}
TEST_F(PermissionActionHistoryTest, NotificationRecordAction) {
size_t general_count = GetHistory(std::nullopt).size();
size_t notification_count = GetHistory(RequestType::kNotifications).size();
GetPermissionActionsHistory()->RecordAction(
PermissionAction::GRANTED, RequestType::kNotifications,
PermissionPromptDisposition::ANCHORED_BUBBLE);
EXPECT_EQ(general_count + 1, GetHistory(std::nullopt).size());
EXPECT_EQ(notification_count + 1,
GetHistory(RequestType::kNotifications).size());
GetPermissionActionsHistory()->RecordAction(
PermissionAction::GRANTED, RequestType::kGeolocation,
PermissionPromptDisposition::ANCHORED_BUBBLE);
EXPECT_EQ(general_count + 2, GetHistory(std::nullopt).size());
EXPECT_EQ(notification_count + 1,
GetHistory(RequestType::kNotifications).size());
}
TEST_F(PermissionActionHistoryTest, ClearHistory) {
struct {
base::Time begin;
base::Time end;
size_t generic_count;
size_t notifications_count;
} kTests[] = {
// Misc and baseline tests cases.
{base::Time(), base::Time::Max(), 0, 0},
{base::Time(), base::Time::Now(), 2, 2},
{base::Time(), base::Time::Now() + base::Microseconds(1), 0, 0},
// Test cases specifying only the upper bound.
{base::Time(), base::Time::Now() - base::Days(1), 5, 3},
{base::Time(), base::Time::Now() - base::Days(2), 7, 4},
{base::Time(), base::Time::Now() - base::Days(3), 10, 7},
// Test cases specifying only the lower bound.
{base::Time::Now() - base::Days(3), base::Time::Max(), 0, 0},
{base::Time::Now() - base::Days(2), base::Time::Max(), 3, 3},
{base::Time::Now() - base::Days(1), base::Time::Max(), 5, 4},
{base::Time::Now(), base::Time::Max(), 8, 5},
// Test cases with both bounds.
{base::Time::Now() - base::Days(3),
base::Time::Now() + base::Microseconds(1), 0, 0},
{base::Time::Now() - base::Days(3), base::Time::Now(), 2, 2},
{base::Time::Now() - base::Days(3), base::Time::Now() - base::Days(1), 5,
3},
{base::Time::Now() - base::Days(3), base::Time::Now() - base::Days(2), 7,
4},
{base::Time::Now() - base::Days(3), base::Time::Now() - base::Days(3), 10,
7},
{base::Time::Now() - base::Days(2),
base::Time::Now() + base::Microseconds(1), 3, 3},
{base::Time::Now() - base::Days(2), base::Time::Now(), 5, 5},
{base::Time::Now() - base::Days(2), base::Time::Now() - base::Days(1), 8,
6},
{base::Time::Now() - base::Days(2), base::Time::Now() - base::Days(2), 10,
7},
{base::Time::Now() - base::Days(1),
base::Time::Now() + base::Microseconds(1), 5, 4},
{base::Time::Now() - base::Days(1), base::Time::Now(), 7, 6},
{base::Time::Now() - base::Days(1), base::Time::Now() - base::Days(1), 10,
7},
{base::Time::Now(), base::Time::Now() + base::Microseconds(1), 8, 5},
{base::Time::Now(), base::Time::Now(), 10, 7},
};
// We need to account for much we have already advanced the time for each test
// case and so we keep track of how much we need to offset the initial test
// values.
base::TimeDelta current_offset;
for (auto& test : kTests) {
test.begin += current_offset;
test.end += current_offset;
GetPermissionActionsHistory()->ClearHistory(test.begin, test.end);
EXPECT_EQ(test.generic_count, GetHistory(std::nullopt).size());
EXPECT_EQ(test.notifications_count,
GetHistory(RequestType::kNotifications).size());
// Clean up for next test and update offset.
base::Time last_now = base::Time::Now();
GetPermissionActionsHistory()->ClearHistory(base::Time(),
base::Time::Max());
RecordSetUpActions();
current_offset += base::Time::Now() - last_now;
}
}
TEST_F(PermissionActionHistoryTest, EntryFilterTest) {
auto loud_entries =
GetHistory(std::nullopt,
PermissionActionsHistory::EntryFilter::WANT_LOUD_PROMPTS_ONLY);
EXPECT_EQ(6u, loud_entries.size());
auto quiet_entries = GetHistory(
std::nullopt, PermissionActionsHistory::PermissionActionsHistory::
EntryFilter::WANT_QUIET_PROMPTS_ONLY);
EXPECT_EQ(2u, quiet_entries.size());
auto all_entries = GetHistory(
std::nullopt, PermissionActionsHistory::EntryFilter::WANT_ALL_PROMPTS);
EXPECT_EQ(10u, all_entries.size());
auto quiet_entries_in_last_two_days =
GetPermissionActionsHistory()->GetHistory(
base::Time::Now() - base::Days(2),
PermissionActionsHistory::EntryFilter::WANT_QUIET_PROMPTS_ONLY);
EXPECT_EQ(2u, quiet_entries_in_last_two_days.size());
}
TEST_F(PermissionActionHistoryTest, FillInActionCountsTest) {
auto all_entries =
GetHistory(RequestType::kNotifications,
PermissionActionsHistory::EntryFilter::WANT_ALL_PROMPTS);
PredictionRequestFeatures::ActionCounts actions_counts;
PermissionActionsHistory::FillInActionCounts(&actions_counts, all_entries);
EXPECT_EQ(3u, actions_counts.grants);
EXPECT_EQ(7u, actions_counts.total());
// FillInActionCounts combines one time and permanent grants, but on a storage
// level they are still stored separately.
uint32_t permanent_grant_count = 0;
uint32_t one_time_grant_count = 0;
for (auto entry : all_entries) {
if (entry.action == PermissionAction::GRANTED) {
permanent_grant_count++;
}
if (entry.action == PermissionAction::GRANTED_ONCE) {
one_time_grant_count++;
}
}
EXPECT_EQ(2u, permanent_grant_count);
EXPECT_EQ(1u, one_time_grant_count);
EXPECT_EQ(7u, all_entries.size());
}
class PermissionActionHistoryHeuristicGrantTest
: public PermissionActionHistoryTest {
public:
PermissionActionHistoryHeuristicGrantTest() {
scoped_feature_list_.InitWithFeatures(
{blink::features::kGeolocationElement,
permissions::features::kPermissionHeuristicAutoGrant},
{});
}
PermissionActionHistoryHeuristicGrantTest(
const PermissionActionHistoryHeuristicGrantTest&) = delete;
~PermissionActionHistoryHeuristicGrantTest() override = default;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(PermissionActionHistoryHeuristicGrantTest, HeuristicGrant) {
GURL url("https://www.example.com");
ContentSettingsType permission = ContentSettingsType::GEOLOCATION;
auto* history = GetPermissionActionsHistory();
MockPermissionActionsHistoryObserver observer;
history->AddObserver(&observer);
for (int i = 0; i < kHeuristicGrantThreshold; ++i) {
EXPECT_FALSE(history->RecordTemporaryGrant(url, permission));
EXPECT_EQ(0, observer.call_count());
}
// The next time should trigger auto-grant.
EXPECT_TRUE(history->RecordTemporaryGrant(url, permission));
EXPECT_EQ(1, observer.call_count());
EXPECT_EQ(url, observer.origin());
EXPECT_EQ(permission, observer.content_setting());
// Subsequent calls should also return true.
EXPECT_TRUE(history->RecordTemporaryGrant(url, permission));
// The observer is notified again.
EXPECT_EQ(2, observer.call_count());
history->RemoveObserver(&observer);
}
TEST_F(PermissionActionHistoryHeuristicGrantTest, HeuristicGrantReset) {
GURL url("https://www.example.com");
ContentSettingsType permission = ContentSettingsType::GEOLOCATION;
auto* history = GetPermissionActionsHistory();
// Grant twice.
EXPECT_FALSE(history->RecordTemporaryGrant(url, permission));
EXPECT_FALSE(history->RecordTemporaryGrant(url, permission));
// Reset.
history->ResetHeuristicData(url, permission);
for (int i = 0; i < kHeuristicGrantThreshold; ++i) {
EXPECT_FALSE(history->RecordTemporaryGrant(url, permission));
}
// Next time after reset should trigger auto-grant.
EXPECT_TRUE(history->RecordTemporaryGrant(url, permission));
}
TEST_F(PermissionActionHistoryHeuristicGrantTest,
HeuristicGrantMultipleOriginsAndPermissions) {
GURL url1("https://www.example.com");
GURL url2("https://www.google.com");
ContentSettingsType permission1 = ContentSettingsType::GEOLOCATION;
auto* history = GetPermissionActionsHistory();
for (int i = 0; i < kHeuristicGrantThreshold - 1; ++i) {
history->RecordTemporaryGrant(url1, permission1);
history->RecordTemporaryGrant(url2, permission1);
}
// Grant url1/permission1 one more time. Should not auto-grant.
EXPECT_FALSE(history->RecordTemporaryGrant(url1, permission1));
// Grant url1/permission1 another time. Next check will auto-grant.
EXPECT_TRUE(history->RecordTemporaryGrant(url1, permission1));
// The other permissions should not be auto-granted yet.
// The next call will increment to counter and not auto-grant.
EXPECT_FALSE(history->RecordTemporaryGrant(url2, permission1));
// The next call for these will auto-grant.
EXPECT_TRUE(history->RecordTemporaryGrant(url2, permission1));
}
TEST_F(PermissionActionHistoryHeuristicGrantTest,
HeuristicGrantGeolocationOnly) {
GURL url("https://www.example.com");
auto* history = GetPermissionActionsHistory();
// GEOLOCATION should work.
EXPECT_FALSE(
history->RecordTemporaryGrant(url, ContentSettingsType::GEOLOCATION));
// NOTIFICATIONS should crash.
EXPECT_DEATH_IF_SUPPORTED(
history->RecordTemporaryGrant(url, ContentSettingsType::NOTIFICATIONS),
"");
EXPECT_DEATH_IF_SUPPORTED(history->CheckHeuristicallyAutoGranted(
url, ContentSettingsType::NOTIFICATIONS),
"");
}
TEST_F(PermissionActionHistoryHeuristicGrantTest,
CheckHeuristicallyAutoGranted) {
GURL url("https://www.example.com");
ContentSettingsType permission = ContentSettingsType::GEOLOCATION;
auto* history = GetPermissionActionsHistory();
EXPECT_FALSE(history->CheckHeuristicallyAutoGranted(url, permission,
/*needs_update*/ false));
for (int i = 0; i < kHeuristicGrantThreshold; ++i) {
history->RecordTemporaryGrant(url, permission);
}
// Trigger auto-grant.
EXPECT_TRUE(history->RecordTemporaryGrant(url, permission));
EXPECT_TRUE(history->CheckHeuristicallyAutoGranted(url, permission,
/*needs_update*/ false));
// Advance clock past expiration date.
task_environment_.AdvanceClock(base::Days(29));
EXPECT_FALSE(history->CheckHeuristicallyAutoGranted(url, permission,
/*needs_update*/ false));
}
TEST_F(PermissionActionHistoryHeuristicGrantTest,
CheckHeuristicallyAutoGrantedNeedsUpdate) {
GURL url("https://www.example.com");
ContentSettingsType permission = ContentSettingsType::GEOLOCATION;
auto* history = GetPermissionActionsHistory();
// Ensure it's not auto-granted initially.
EXPECT_FALSE(history->CheckHeuristicallyAutoGranted(url, permission,
/*needs_update*/ false));
// Trigger auto-grant.
for (int i = 0; i < kHeuristicGrantThreshold; ++i) {
history->RecordTemporaryGrant(url, permission);
}
EXPECT_TRUE(history->RecordTemporaryGrant(url, permission));
// Check with needs_update = true. Timestamp should change.
task_environment_.AdvanceClock(base::Days(2));
EXPECT_TRUE(history->CheckHeuristicallyAutoGranted(url, permission));
task_environment_.AdvanceClock(base::Days(6));
EXPECT_TRUE(history->CheckHeuristicallyAutoGranted(url, permission,
/*needs_update*/ false));
task_environment_.AdvanceClock(base::Days(20));
EXPECT_TRUE(history->CheckHeuristicallyAutoGranted(url, permission,
/*needs_update*/ false));
task_environment_.AdvanceClock(base::Days(3));
EXPECT_FALSE(history->CheckHeuristicallyAutoGranted(url, permission,
/*needs_update*/ false));
}
TEST_F(PermissionActionHistoryHeuristicGrantTest,
HeuristicGrantExpirationDecaysCount) {
GURL url("https://www.example.com");
ContentSettingsType permission = ContentSettingsType::GEOLOCATION;
auto* history = GetPermissionActionsHistory();
// Grant up to the threshold to enable auto-granting.
for (int i = 0; i < kHeuristicGrantThreshold; ++i) {
history->RecordTemporaryGrant(url, permission);
}
// The next grant will trigger auto-grant.
EXPECT_TRUE(history->RecordTemporaryGrant(url, permission));
EXPECT_EQ(kHeuristicGrantThreshold + 1,
history->GetTemporaryGrantCountForTesting(url, permission));
EXPECT_TRUE(history->CheckHeuristicallyAutoGranted(url, permission,
/*needs_update*/ false));
// Advance clock past expiration date.
task_environment_.AdvanceClock(base::Days(29));
// The grant should have expired.
EXPECT_FALSE(history->CheckHeuristicallyAutoGranted(url, permission,
/*needs_update*/ false));
// The count should have decayed to 2.
EXPECT_EQ(2, history->GetTemporaryGrantCountForTesting(url, permission));
// Advance clock past expiration date again.
task_environment_.AdvanceClock(base::Days(29));
// The grant should still be expired.
EXPECT_FALSE(history->CheckHeuristicallyAutoGranted(url, permission,
/*needs_update*/ false));
// The count should have decayed to 0.
EXPECT_EQ(0, history->GetTemporaryGrantCountForTesting(url, permission));
// The next grant should not auto-grant, but increment the count to 1.
EXPECT_FALSE(history->RecordTemporaryGrant(url, permission));
EXPECT_EQ(1, history->GetTemporaryGrantCountForTesting(url, permission));
// Grant twice more to reach the threshold.
EXPECT_FALSE(history->RecordTemporaryGrant(url, permission));
EXPECT_FALSE(history->RecordTemporaryGrant(url, permission));
EXPECT_EQ(3, history->GetTemporaryGrantCountForTesting(url, permission));
// The next grant should now trigger auto-grant.
EXPECT_TRUE(history->RecordTemporaryGrant(url, permission));
EXPECT_EQ(4, history->GetTemporaryGrantCountForTesting(url, permission));
}
TEST_F(PermissionActionHistoryHeuristicGrantTest,
HeuristicGrantResetWithFilter) {
GURL url1("https://www.example.com");
GURL url2("https://www.google.com");
ContentSettingsType permission = ContentSettingsType::GEOLOCATION;
auto* history = GetPermissionActionsHistory();
// Grant url1 and url2 twice.
EXPECT_FALSE(history->RecordTemporaryGrant(url1, permission));
EXPECT_FALSE(history->RecordTemporaryGrant(url1, permission));
EXPECT_FALSE(history->RecordTemporaryGrant(url2, permission));
EXPECT_FALSE(history->RecordTemporaryGrant(url2, permission));
// Reset for urls matching "example.com".
history->ResetHeuristicData(base::BindRepeating(
[](const GURL& url) { return url.GetHost() == "www.example.com"; }));
// The counter for url1 should be reset. It should take
// `kHeuristicGrantThreshold` more grants to trigger auto-grant.
for (int i = 0; i < kHeuristicGrantThreshold; ++i) {
EXPECT_FALSE(history->RecordTemporaryGrant(url1, permission));
}
EXPECT_TRUE(history->RecordTemporaryGrant(url1, permission));
// The counter for url2 should not be reset. It was granted twice, so it
// needs one more grant to reach the threshold.
EXPECT_FALSE(history->RecordTemporaryGrant(url2, permission));
// The next one should auto-grant.
EXPECT_TRUE(history->RecordTemporaryGrant(url2, permission));
}
TEST_F(PermissionActionHistoryTest, RecordOneTimeGrant) {
GURL url1("https://www.example.com");
GURL url2("https://www.google.com");
auto* history = GetPermissionActionsHistory();
base::HistogramTester histogram_tester;
// Geolocation
history->RecordOneTimeGrant(url1, ContentSettingsType::GEOLOCATION);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.Geolocation.OneTimeGrant", 1, 1);
history->RecordOneTimeGrant(url1, ContentSettingsType::GEOLOCATION);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.Geolocation.OneTimeGrant", 2, 1);
history->RecordOneTimeGrant(url2, ContentSettingsType::GEOLOCATION);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.Geolocation.OneTimeGrant", 1, 2);
// Mic
history->RecordOneTimeGrant(url1, ContentSettingsType::MEDIASTREAM_MIC);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.AudioCapture.OneTimeGrant", 1, 1);
history->RecordOneTimeGrant(url1, ContentSettingsType::MEDIASTREAM_MIC);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.AudioCapture.OneTimeGrant", 2, 1);
// Camera
history->RecordOneTimeGrant(url1, ContentSettingsType::MEDIASTREAM_CAMERA);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.VideoCapture.OneTimeGrant", 1, 1);
// Unsupported type - should be ignored
history->RecordOneTimeGrant(url1, ContentSettingsType::NOTIFICATIONS);
histogram_tester.ExpectTotalCount(
"Permissions.OneTimePermission.Notifications.OneTimeGrant", 0);
// Check total counts
histogram_tester.ExpectTotalCount(
"Permissions.OneTimePermission.Geolocation.OneTimeGrant", 3);
histogram_tester.ExpectTotalCount(
"Permissions.OneTimePermission.AudioCapture.OneTimeGrant", 2);
histogram_tester.ExpectTotalCount(
"Permissions.OneTimePermission.VideoCapture.OneTimeGrant", 1);
}
TEST_F(PermissionActionHistoryTest, RecordOTPCountForGrant) {
auto* history = GetPermissionActionsHistory();
base::HistogramTester histogram_tester;
// Geolocation
history->RecordOTPCountForGrant(ContentSettingsType::GEOLOCATION, 3);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.Geolocation.GrantOTPCount", 3, 1);
history->RecordOTPCountForGrant(ContentSettingsType::GEOLOCATION, 0);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.Geolocation.GrantOTPCount", 0, 1);
// Mic
history->RecordOTPCountForGrant(ContentSettingsType::MEDIASTREAM_MIC, 1);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.AudioCapture.GrantOTPCount", 1, 1);
// Camera
history->RecordOTPCountForGrant(ContentSettingsType::MEDIASTREAM_CAMERA, 5);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.VideoCapture.GrantOTPCount", 5, 1);
// Unsupported type - should be ignored
history->RecordOTPCountForGrant(ContentSettingsType::NOTIFICATIONS, 2);
histogram_tester.ExpectTotalCount(
"Permissions.OneTimePermission.Notifications.GrantOTPCount", 0);
// Check total counts
histogram_tester.ExpectTotalCount(
"Permissions.OneTimePermission.Geolocation.GrantOTPCount", 2);
histogram_tester.ExpectTotalCount(
"Permissions.OneTimePermission.AudioCapture.GrantOTPCount", 1);
histogram_tester.ExpectTotalCount(
"Permissions.OneTimePermission.VideoCapture.GrantOTPCount", 1);
}
TEST_F(PermissionActionHistoryTest, GetOneTimeGrantCount) {
GURL url1("https://www.example.com");
GURL url2("https://www.google.com");
auto* history = GetPermissionActionsHistory();
EXPECT_EQ(
0, history->GetOneTimeGrantCount(url1, ContentSettingsType::GEOLOCATION));
// Record some GRANTED_ONCE actions
history->RecordOneTimeGrant(url1, ContentSettingsType::GEOLOCATION);
EXPECT_EQ(
1, history->GetOneTimeGrantCount(url1, ContentSettingsType::GEOLOCATION));
history->RecordOneTimeGrant(url1, ContentSettingsType::GEOLOCATION);
EXPECT_EQ(
2, history->GetOneTimeGrantCount(url1, ContentSettingsType::GEOLOCATION));
history->RecordOneTimeGrant(url2, ContentSettingsType::GEOLOCATION);
EXPECT_EQ(
1, history->GetOneTimeGrantCount(url2, ContentSettingsType::GEOLOCATION));
EXPECT_EQ(2, history->GetOneTimeGrantCount(
url1, ContentSettingsType::GEOLOCATION)); // url1 unchanged
history->RecordOneTimeGrant(url1, ContentSettingsType::MEDIASTREAM_MIC);
EXPECT_EQ(1, history->GetOneTimeGrantCount(
url1, ContentSettingsType::MEDIASTREAM_MIC));
EXPECT_EQ(0, history->GetOneTimeGrantCount(
url2, ContentSettingsType::MEDIASTREAM_MIC));
// Non-one-time grant actions should not affect the count
history->RecordAction(PermissionAction::GRANTED, RequestType::kGeolocation,
PermissionPromptDisposition::ANCHORED_BUBBLE);
EXPECT_EQ(
2, history->GetOneTimeGrantCount(url1, ContentSettingsType::GEOLOCATION));
history->RecordAction(PermissionAction::DENIED, RequestType::kGeolocation,
PermissionPromptDisposition::ANCHORED_BUBBLE);
EXPECT_EQ(
2, history->GetOneTimeGrantCount(url1, ContentSettingsType::GEOLOCATION));
// Unsupported type
EXPECT_EQ(0, history->GetOneTimeGrantCount(
url1, ContentSettingsType::NOTIFICATIONS));
}
} // namespace permissions