blob: e61f76ea8adde61c4c3e249515c0cd313ad25a29 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/btm/btm_storage.h"
#include <optional>
#include "base/functional/bind.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/stringprintf.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "base/threading/sequence_bound.h"
#include "base/time/time.h"
#include "content/browser/btm/btm_state.h"
#include "content/browser/btm/btm_utils.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/common/content_features.h"
#include "services/network/public/mojom/clear_data_filter.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace content {
namespace {
class TestStorage : public BtmStorage {
public:
TestStorage() : BtmStorage(std::nullopt) {}
void WriteForTesting(GURL url, const StateValue& state) {
Write(BtmState(this, GetSiteForBtm(url), state));
}
};
// TODO(crbug.com/376754761): Remove this class, since it no longer sets the
// main BTM feature
class ScopedBtmInteractionTtlFeatureEnabledWithParams {
public:
explicit ScopedBtmInteractionTtlFeatureEnabledWithParams(
const base::FieldTrialParams& params) {
features_.InitAndEnableFeatureWithParameters(features::kBtmTtl, params);
}
private:
base::test::ScopedFeatureList features_;
};
} // namespace
TEST(BtmGetSitesToClearTest, FiltersByTriggerParam) {
TestStorage storage;
GURL kBounceUrl("https://bounce.com");
GURL kStorageUrl("https://storage.com");
GURL kStatefulBounceUrl("https://stateful_bounce.com");
TimestampRange event({base::Time::FromSecondsSinceUnixEpoch(1),
base::Time::FromSecondsSinceUnixEpoch(1)});
storage.WriteForTesting(kBounceUrl, StateValue{.bounce_times = event});
storage.WriteForTesting(kStatefulBounceUrl,
StateValue{.bounce_times = event});
// Call 'GetSitesToClear' when the trigger is unset.
{
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
features::kBtm, {{"triggering_action", "none"}});
EXPECT_THAT(storage.GetSitesToClear(std::nullopt), testing::IsEmpty());
}
// Call 'GetSitesToClear' when BTM is triggered by bounces.
{
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
features::kBtm, {{"triggering_action", "bounce"}});
EXPECT_THAT(storage.GetSitesToClear(std::nullopt),
testing::ElementsAre(GetSiteForBtm(kBounceUrl),
GetSiteForBtm(kStatefulBounceUrl)));
}
}
TEST(BtmGetSitesToClearTest, CustomGracePeriod) {
base::SimpleTestClock clock;
clock.SetNow(base::Time::FromSecondsSinceUnixEpoch(1));
base::Time start = clock.Now();
base::Time late_trigger = start + base::Seconds(10);
base::TimeDelta custom_grace_period = base::Seconds(5);
TestStorage storage;
storage.SetClockForTesting(&clock);
GURL kUrl("https://example.com");
GURL kLateUrl("https://late_example.com");
TimestampRange event({start, start});
TimestampRange late_event({late_trigger, late_trigger});
storage.WriteForTesting(kUrl, StateValue{.bounce_times = event});
storage.WriteForTesting(kLateUrl, StateValue{.bounce_times = late_event});
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
features::kBtm,
{{"grace_period", "30s"}, {"triggering_action", "stateful_bounce"}});
// Advance time by less than `features::kBtmGracePeriod` but greater than
// `start + custom_grace_period` and verify that no sites are returned without
// using the custom grace period.
clock.Advance(base::Seconds(10));
EXPECT_THAT(storage.GetSitesToClear(std::nullopt), testing::IsEmpty());
// Verify that using a custom grace period less than the amount time was
// advanced returns only `kUrl` since `kLateUrl` is still within its grace
// period.
EXPECT_THAT(storage.GetSitesToClear(custom_grace_period),
testing::ElementsAre(GetSiteForBtm(kUrl)));
}
TEST(BtmGetSitesToClearTest, CustomGracePeriod_AllTriggers) {
base::SimpleTestClock clock;
base::Time start = clock.Now();
base::TimeDelta grace_period = base::Seconds(1);
TestStorage storage;
storage.SetClockForTesting(&clock);
GURL kBounceUrl("https://bounce.com");
GURL kStorageUrl("https://storage.com");
GURL kStatefulBounceUrl("https://stateful_bounce.com");
TimestampRange event({start, start});
storage.WriteForTesting(kBounceUrl, StateValue{.bounce_times = event});
storage.WriteForTesting(kStatefulBounceUrl,
StateValue{.bounce_times = event});
// Call 'GetSitesToClear' with a custom grace period when the trigger is
// unset.
{
base::test::ScopedFeatureList features;
features.InitAndEnableFeature(features::kBtm);
// Advance time by less than `features::kBtmGracePeriod` and verify that
// no sites are returned
clock.Advance(features::kBtmGracePeriod.Get() / 2);
EXPECT_THAT(storage.GetSitesToClear(std::nullopt), testing::IsEmpty());
// Verify that using a custom grace period less than the amount time was
// advanced still returns nothing when the trigger is unset.
EXPECT_THAT(storage.GetSitesToClear(std::nullopt), testing::IsEmpty());
// Reset `clock` to `start`.
clock.SetNow(start);
}
// Call 'GetSitesToClear' with a custom grace period when BTM is triggered by
// bounces.
{
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
features::kBtm, {{"triggering_action", "bounce"}});
// Advance time by less than `features::kBtmGracePeriod` and verify that
// no sites are returned without using a custom grace period.
clock.Advance(features::kBtmGracePeriod.Get() / 2);
EXPECT_THAT(storage.GetSitesToClear(std::nullopt), testing::IsEmpty());
// Verify that using a custom grace period less than the amount time was
// advanced returns the expected sites for triggering on bounces.
EXPECT_THAT(storage.GetSitesToClear(grace_period),
testing::ElementsAre(GetSiteForBtm(kBounceUrl),
GetSiteForBtm(kStatefulBounceUrl)));
// Reset `clock` to `start`.
clock.SetNow(start);
}
}
class BtmStorageTest : public testing::Test {
public:
BtmStorageTest() = default;
void SetUp() override { storage_.SetClockForTesting(&clock_); }
TimestampRange ToRange(base::Time first, base::Time last) {
return {{first, last}};
}
protected:
base::test::TaskEnvironment env_;
ScopedBtmInteractionTtlFeatureEnabledWithParams feature{
{{"interaction_ttl", "inf"}}};
TestStorage storage_;
base::SimpleTestClock clock_;
};
TEST(DirtyBit, Constructor) {
ASSERT_FALSE(DirtyBit());
ASSERT_TRUE(DirtyBit(true));
ASSERT_FALSE(DirtyBit(false));
}
TEST(DirtyBit, Assignment) {
DirtyBit bit;
bit = true;
ASSERT_TRUE(bit);
bit = false;
ASSERT_FALSE(bit);
}
TEST(DirtyBit, Move) {
DirtyBit bit(true);
DirtyBit moved(std::move(bit));
ASSERT_TRUE(moved);
ASSERT_FALSE(bit); // NOLINT
}
TEST(BtmUtilsTest, GetSiteForBtm) {
EXPECT_EQ("example.com", GetSiteForBtm(GURL("http://example.com/foo")));
EXPECT_EQ("example.com", GetSiteForBtm(GURL("https://www.example.com/bar")));
EXPECT_EQ("example.com", GetSiteForBtm(GURL("http://other.example.com/baz")));
EXPECT_EQ("bar.baz.r.appspot.com",
GetSiteForBtm(GURL("http://foo.bar.baz.r.appspot.com/baz")));
EXPECT_EQ("localhost", GetSiteForBtm(GURL("http://localhost:8000/qux")));
EXPECT_EQ("127.0.0.1", GetSiteForBtm(GURL("http://127.0.0.1:8888/")));
EXPECT_EQ("[::1]", GetSiteForBtm(GURL("http://[::1]/")));
}
TEST_F(BtmStorageTest, NewURL) {
BtmState state = storage_.Read(GURL("http://example.com/"));
EXPECT_FALSE(state.was_loaded());
EXPECT_FALSE(state.user_activation_times().has_value());
EXPECT_FALSE(state.web_authn_assertion_times().has_value());
}
TEST_F(BtmStorageTest, SetValues) {
GURL url("https://example.com");
auto time2 = base::Time::FromSecondsSinceUnixEpoch(2);
auto time3 = base::Time::FromSecondsSinceUnixEpoch(3);
{
BtmState state = storage_.Read(url);
state.update_user_activation_time(time2);
state.update_web_authn_assertion_time(time3);
// Before flushing `state`, reads for the same URL won't include its
// changes.
BtmState state2 = storage_.Read(url);
EXPECT_FALSE(state2.user_activation_times().has_value());
EXPECT_FALSE(state2.web_authn_assertion_times().has_value());
}
BtmState state = storage_.Read(url);
EXPECT_TRUE(state.was_loaded());
EXPECT_EQ(state.user_activation_times()->first, std::make_optional(time2));
EXPECT_EQ(state.web_authn_assertion_times()->first,
std::make_optional(time3));
}
TEST_F(BtmStorageTest, SameSiteSameState) {
// The two urls use different subdomains of example.com; and one is HTTPS
// while the other is HTTP.
GURL url1("https://subdomain1.example.com");
GURL url2("http://subdomain2.example.com");
auto time = base::Time::FromSecondsSinceUnixEpoch(1);
storage_.Read(url1).update_bounce_time(time);
BtmState state = storage_.Read(url2);
// State was recorded for url1, but can be read for url2.
EXPECT_EQ(time, state.bounce_times()->first);
EXPECT_FALSE(state.user_activation_times().has_value());
}
TEST_F(BtmStorageTest, DifferentSiteDifferentState) {
GURL url1("https://example1.com");
GURL url2("https://example2.com");
auto time1 = base::Time::FromSecondsSinceUnixEpoch(1);
auto time2 = base::Time::FromSecondsSinceUnixEpoch(2);
storage_.Read(url1).update_user_activation_time(time1);
storage_.Read(url2).update_user_activation_time(time2);
// Verify that url1 and url2 have independent state:
EXPECT_EQ(storage_.Read(url1).user_activation_times()->first,
std::make_optional(time1));
EXPECT_EQ(storage_.Read(url2).user_activation_times()->first,
std::make_optional(time2));
}
// This test is not all-inclusive as only fucuses on some (deemed) important
// overlapping scenarios.
TEST_F(BtmStorageTest, RemoveByTime_WebAuthnAssertion) {
base::SimpleTestClock clock;
clock.SetNow(base::Time::FromSecondsSinceUnixEpoch(100));
auto tiny_delta = base::Milliseconds(1);
auto delete_begin = clock.Now();
auto delete_end = delete_begin + tiny_delta * 100;
auto i = 0;
{
const GURL url(base::StringPrintf("https://case%d.test", ++i));
storage_.WriteForTesting(url, {{}, {}, ToRange(delete_begin, delete_end)});
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kHistory);
EXPECT_FALSE(storage_.Read(url).was_loaded());
}
{
const GURL url(base::StringPrintf("https://case%d.test", ++i));
storage_.WriteForTesting(
url, {{}, {}, ToRange(delete_begin + tiny_delta, delete_end)});
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kHistory);
EXPECT_FALSE(storage_.Read(url).was_loaded());
}
{
const GURL url(base::StringPrintf("https://case%d.test", ++i));
storage_.WriteForTesting(
url, {{}, {}, ToRange(delete_begin, delete_end - tiny_delta)});
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kHistory);
EXPECT_FALSE(storage_.Read(url).was_loaded());
}
{
const GURL url(base::StringPrintf("https://case%d.test", ++i));
StateValue init_state;
init_state.web_authn_assertion_times =
ToRange(delete_begin, delete_end + tiny_delta);
storage_.WriteForTesting(url, init_state);
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kHistory);
EXPECT_EQ(
storage_.Read(url).web_authn_assertion_times(),
ToRange(delete_end, init_state.web_authn_assertion_times->second));
}
{
const GURL url(base::StringPrintf("https://case%d.test", ++i));
StateValue init_state;
init_state.web_authn_assertion_times =
ToRange(delete_begin - tiny_delta, delete_end);
storage_.WriteForTesting(url, init_state);
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kHistory);
EXPECT_EQ(
storage_.Read(url).web_authn_assertion_times(),
ToRange(init_state.web_authn_assertion_times->first, delete_begin));
}
{
const GURL url(base::StringPrintf("https://case%d.test", ++i));
StateValue init_state;
init_state.web_authn_assertion_times =
ToRange(delete_begin - tiny_delta, delete_end + tiny_delta);
storage_.WriteForTesting(url, init_state);
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kHistory);
EXPECT_EQ(storage_.Read(url).web_authn_assertion_times(),
init_state.web_authn_assertion_times);
}
{
const GURL url(base::StringPrintf("https://case%d.test", ++i));
StateValue init_state;
init_state.web_authn_assertion_times =
ToRange(delete_end + tiny_delta, delete_end + tiny_delta * 2);
storage_.WriteForTesting(url, init_state);
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kHistory);
EXPECT_EQ(storage_.Read(url).web_authn_assertion_times(),
init_state.web_authn_assertion_times);
}
{
const GURL url(base::StringPrintf("https://case%d.test", ++i));
StateValue init_state;
init_state.web_authn_assertion_times =
ToRange(delete_begin - tiny_delta * 2, delete_begin - tiny_delta);
storage_.WriteForTesting(url, init_state);
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kHistory);
EXPECT_EQ(storage_.Read(url).web_authn_assertion_times(),
init_state.web_authn_assertion_times);
}
}
TEST_F(BtmStorageTest, RemoveByTimeWithNullRangeEndTime) {
GURL url1("https://example1.com");
GURL url2("https://example2.com");
base::Time delete_begin = base::Time::FromSecondsSinceUnixEpoch(2);
base::Time delete_end = base::Time();
storage_.WriteForTesting(
url1,
{/*user_activation_times= */ {{base::Time::FromSecondsSinceUnixEpoch(5),
base::Time::FromSecondsSinceUnixEpoch(8)}},
/*bounce_times=*/{{base::Time::FromSecondsSinceUnixEpoch(1),
base::Time::FromSecondsSinceUnixEpoch(3)}}});
storage_.WriteForTesting(
url2,
{/*user_activation_times= */ {{base::Time::FromSecondsSinceUnixEpoch(3),
base::Time::FromSecondsSinceUnixEpoch(5)}},
/*bounce_times=*/TimestampRange()});
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kAll);
BtmState state1 = storage_.Read(url1);
EXPECT_EQ(state1.bounce_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(1))); // no change
EXPECT_EQ(state1.bounce_times()->second,
std::make_optional(delete_begin)); // adjusted
EXPECT_EQ(state1.user_activation_times(),
std::nullopt); // removed
BtmState state2 = storage_.Read(url2);
EXPECT_FALSE(state2.was_loaded()); // removed
}
TEST_F(BtmStorageTest, RemoveByTimeWithNullRangeBeginTime) {
GURL url1("https://example1.com");
GURL url2("https://example2.com");
base::Time delete_begin = base::Time::Min();
base::Time delete_end = base::Time::FromSecondsSinceUnixEpoch(6);
storage_.WriteForTesting(
url1,
{/*user_activation_times= */ {{base::Time::FromSecondsSinceUnixEpoch(5),
base::Time::FromSecondsSinceUnixEpoch(8)}},
/*bounce_times=*/{{base::Time::FromSecondsSinceUnixEpoch(1),
base::Time::FromSecondsSinceUnixEpoch(3)}}});
storage_.WriteForTesting(
url2,
{/*user_activation_times= */ {{base::Time::FromSecondsSinceUnixEpoch(3),
base::Time::FromSecondsSinceUnixEpoch(5)}},
/*bounce_times=*/TimestampRange()});
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kAll);
BtmState state1 = storage_.Read(url1);
EXPECT_EQ(state1.bounce_times(), std::nullopt); // removed
EXPECT_EQ(state1.user_activation_times()->first,
std::make_optional(delete_end)); // adjusted
EXPECT_EQ(state1.user_activation_times()->second,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(8))); // no change
BtmState state2 = storage_.Read(url2);
EXPECT_FALSE(state2.was_loaded()); // removed
}
TEST_F(BtmStorageTest, RemoveByTimeAdjustsOverlappingTimes) {
GURL url1("https://example1.com");
GURL url2("https://example2.com");
base::Time delete_begin = base::Time::FromSecondsSinceUnixEpoch(2);
base::Time delete_end = base::Time::FromSecondsSinceUnixEpoch(6);
storage_.WriteForTesting(
url1, {{{base::Time::FromSecondsSinceUnixEpoch(5),
base::Time::FromSecondsSinceUnixEpoch(8)}},
/*bounce_times=*/{{base::Time::FromSecondsSinceUnixEpoch(1),
base::Time::FromSecondsSinceUnixEpoch(3)}}});
storage_.WriteForTesting(url2, {{{base::Time::FromSecondsSinceUnixEpoch(3),
base::Time::FromSecondsSinceUnixEpoch(5)}},
/*bounce_times=*/TimestampRange()});
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kAll);
BtmState state1 = storage_.Read(url1);
EXPECT_EQ(state1.bounce_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(1))); // no change
EXPECT_EQ(state1.bounce_times()->second,
std::make_optional(delete_begin)); // adjusted
EXPECT_EQ(state1.user_activation_times()->first,
std::make_optional(delete_end)); // adjusted
EXPECT_EQ(state1.user_activation_times()->second,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(8))); // no change
BtmState state2 = storage_.Read(url2);
EXPECT_FALSE(state2.was_loaded()); // removed
}
TEST_F(BtmStorageTest, RemoveByTimeDoesNotAffectTouchingWindowEndpoints) {
GURL url1("https://example1.com");
GURL url2("https://example2.com");
base::Time delete_begin = base::Time::FromSecondsSinceUnixEpoch(3);
base::Time delete_end = base::Time::FromSecondsSinceUnixEpoch(5);
storage_.WriteForTesting(
url1, {{{base::Time::FromSecondsSinceUnixEpoch(5),
base::Time::FromSecondsSinceUnixEpoch(8)}},
/*bounce_times=*/{{base::Time::FromSecondsSinceUnixEpoch(1),
base::Time::FromSecondsSinceUnixEpoch(3)}}});
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kAll);
BtmState state = storage_.Read(url1);
EXPECT_EQ(state.bounce_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(1))); // no change
EXPECT_EQ(state.bounce_times()->second,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(3))); // no change
EXPECT_EQ(state.user_activation_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(5))); // no change
EXPECT_EQ(state.user_activation_times()->second,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(8))); // no change
}
TEST_F(BtmStorageTest, RemoveByTimeInteractionOnly) {
GURL url1("https://example1.com");
GURL url2("https://example2.com");
base::Time delete_begin = base::Time::FromSecondsSinceUnixEpoch(2);
base::Time delete_end = base::Time::FromSecondsSinceUnixEpoch(6);
storage_.WriteForTesting(
url1, {{{base::Time::FromSecondsSinceUnixEpoch(5),
base::Time::FromSecondsSinceUnixEpoch(8)}},
/*bounce_times=*/{{base::Time::FromSecondsSinceUnixEpoch(1),
base::Time::FromSecondsSinceUnixEpoch(3)}}});
storage_.WriteForTesting(url2, {{{base::Time::FromSecondsSinceUnixEpoch(3),
base::Time::FromSecondsSinceUnixEpoch(5)}},
/*bounce_times=*/TimestampRange()});
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kHistory);
BtmState state1 = storage_.Read(url1);
EXPECT_EQ(state1.bounce_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(1))); // no change
EXPECT_EQ(state1.bounce_times()->second,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(3))); // no change
EXPECT_EQ(state1.user_activation_times()->first,
std::make_optional(delete_end)); // adjusted
EXPECT_EQ(state1.user_activation_times()->second,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(8))); // no change
BtmState state2 = storage_.Read(url2);
EXPECT_FALSE(state2.was_loaded()); // removed
}
TEST_F(BtmStorageTest, RemovePopupEventsByTime) {
std::string site1 = GetSiteForBtm(GURL("https://example1.com"));
std::string site2 = GetSiteForBtm(GURL("https://example2.com"));
std::string site3 = GetSiteForBtm(GURL("https://example3.com"));
base::Time delete_begin = base::Time::FromSecondsSinceUnixEpoch(3);
base::Time delete_end = base::Time::FromSecondsSinceUnixEpoch(5);
ASSERT_TRUE(storage_.WritePopup(
site1, site2, /*access_id=*/1u, base::Time::FromSecondsSinceUnixEpoch(2),
/*is_current_interaction=*/true, /*is_authentication_interaction=*/true));
ASSERT_TRUE(storage_.WritePopup(site1, site3, /*access_id=*/2u,
base::Time::FromSecondsSinceUnixEpoch(4),
/*is_current_interaction=*/true,
/*is_authentication_interaction=*/false));
ASSERT_TRUE(storage_.WritePopup(site2, site3, /*access_id=*/3u,
base::Time::FromSecondsSinceUnixEpoch(6),
/*is_current_interaction=*/false,
/*is_authentication_interaction=*/false));
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kHistory);
// Verify that only the second popup event (with timestamp 4) was cleared.
std::optional<PopupsStateValue> popup1 = storage_.ReadPopup(site1, site2);
ASSERT_TRUE(popup1.has_value());
EXPECT_EQ(popup1.value().access_id, 1u);
EXPECT_EQ(popup1.value().last_popup_time,
base::Time::FromSecondsSinceUnixEpoch(2));
EXPECT_TRUE(popup1.value().is_current_interaction);
EXPECT_TRUE(popup1.value().is_authentication_interaction);
std::optional<PopupsStateValue> popup2 = storage_.ReadPopup(site1, site3);
ASSERT_FALSE(popup2.has_value());
std::optional<PopupsStateValue> popup3 = storage_.ReadPopup(site2, site3);
ASSERT_TRUE(popup3.has_value());
EXPECT_EQ(popup3.value().access_id, 3u);
EXPECT_EQ(popup3.value().last_popup_time,
base::Time::FromSecondsSinceUnixEpoch(6));
EXPECT_FALSE(popup3.value().is_current_interaction);
EXPECT_FALSE(popup3.value().is_authentication_interaction);
}
TEST_F(BtmStorageTest, RemoveByTimeBounces) {
GURL url1("https://example1.com");
GURL url2("https://example2.com");
base::Time delete_begin = base::Time::FromSecondsSinceUnixEpoch(2);
base::Time delete_end = base::Time::FromSecondsSinceUnixEpoch(6);
storage_.WriteForTesting(url1,
{TimestampRange(),
{{base::Time::FromSecondsSinceUnixEpoch(1),
base::Time::FromSecondsSinceUnixEpoch(3)}}});
storage_.WriteForTesting(url2,
{TimestampRange(),
{{base::Time::FromSecondsSinceUnixEpoch(3),
base::Time::FromSecondsSinceUnixEpoch(5)}}});
storage_.RemoveEvents(delete_begin, delete_end, nullptr,
BtmEventRemovalType::kStorage);
BtmState state1 = storage_.Read(url1);
EXPECT_EQ(state1.bounce_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(1))); // no change
EXPECT_EQ(state1.bounce_times()->second,
std::make_optional(delete_begin)); // adjusted
BtmState state2 = storage_.Read(url2);
EXPECT_FALSE(state2.was_loaded()); // removed
}
TEST_F(BtmStorageTest, RemoveBySite) {
GURL url1("https://example1.com");
GURL url2("https://example2.com");
GURL url3("https://example3.com");
GURL url4("https://example4.com");
storage_.WriteForTesting(url1,
{{{base::Time::FromSecondsSinceUnixEpoch(2),
base::Time::FromSecondsSinceUnixEpoch(2)}},
{{base::Time::FromSecondsSinceUnixEpoch(3),
base::Time::FromSecondsSinceUnixEpoch(4)}}});
storage_.WriteForTesting(url2,
{{{base::Time::FromSecondsSinceUnixEpoch(2),
base::Time::FromSecondsSinceUnixEpoch(2)}},
{{base::Time::FromSecondsSinceUnixEpoch(3),
base::Time::FromSecondsSinceUnixEpoch(4)}}});
storage_.WriteForTesting(url3,
{TimestampRange(),
{{base::Time::FromSecondsSinceUnixEpoch(3),
base::Time::FromSecondsSinceUnixEpoch(4)}}});
storage_.WriteForTesting(url4,
{{{base::Time::FromSecondsSinceUnixEpoch(2),
base::Time::FromSecondsSinceUnixEpoch(2)}},
{{base::Time::FromSecondsSinceUnixEpoch(3),
base::Time::FromSecondsSinceUnixEpoch(4)}}});
std::unique_ptr<BrowsingDataFilterBuilder> builder =
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
builder->AddRegisterableDomain(GetSiteForBtm(url1));
builder->AddRegisterableDomain(GetSiteForBtm(url3));
storage_.RemoveEvents(base::Time(), base::Time::Max(),
builder->BuildNetworkServiceFilter(),
BtmEventRemovalType::kStorage);
BtmState state1 = storage_.Read(url1);
EXPECT_EQ(state1.user_activation_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(2))); // no change
EXPECT_FALSE(state1.bounce_times().has_value()); // removed
BtmState state2 = storage_.Read(url2);
EXPECT_EQ(state2.user_activation_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(2))); // no change
EXPECT_EQ(state2.bounce_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(3))); // no change
BtmState state3 = storage_.Read(url3);
EXPECT_FALSE(state3.was_loaded()); // removed
BtmState state4 = storage_.Read(url2);
EXPECT_EQ(state4.user_activation_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(2))); // no change
EXPECT_EQ(state4.bounce_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(3))); // no change
}
TEST_F(BtmStorageTest, RemoveBySiteIgnoresDeletionWithTimeRange) {
GURL url1("https://example1.com");
base::Time delete_begin = base::Time::FromSecondsSinceUnixEpoch(2);
base::Time delete_end = base::Time::FromSecondsSinceUnixEpoch(6);
storage_.WriteForTesting(url1,
{{{base::Time::FromSecondsSinceUnixEpoch(2),
base::Time::FromSecondsSinceUnixEpoch(2)}},
{{base::Time::FromSecondsSinceUnixEpoch(3),
base::Time::FromSecondsSinceUnixEpoch(4)}}});
std::unique_ptr<BrowsingDataFilterBuilder> builder =
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
builder->AddRegisterableDomain(GetSiteForBtm(url1));
storage_.RemoveEvents(delete_begin, delete_end,
builder->BuildNetworkServiceFilter(),
BtmEventRemovalType::kStorage);
// Removing events by site (i.e. by using a non-null filter) with a time-range
// (other than base::Time() to base::Time::Max()), is currently unsupported.
// So url1's `BtmState` entry should be unaffected.
BtmState state1 = storage_.Read(url1);
EXPECT_EQ(state1.user_activation_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(2))); // no change
EXPECT_EQ(state1.bounce_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(3))); // no change
}
TEST_F(BtmStorageTest, RemoveRows) {
GURL url1("https://example1.com");
GURL url2("https://example2.com");
ASSERT_TRUE(url1.is_valid());
ASSERT_TRUE(url2.is_valid());
StateValue test_value = {{{base::Time::FromSecondsSinceUnixEpoch(2),
base::Time::FromSecondsSinceUnixEpoch(2)}},
{{base::Time::FromSecondsSinceUnixEpoch(3),
base::Time::FromSecondsSinceUnixEpoch(4)}}};
storage_.WriteForTesting(url1, test_value);
storage_.WriteForTesting(url2, test_value);
ASSERT_EQ(storage_.Read(url1).ToStateValue(), test_value);
ASSERT_EQ(storage_.Read(url2).ToStateValue(), test_value);
storage_.RemoveRows({GetSiteForBtm(url1), GetSiteForBtm(url2)});
EXPECT_FALSE(storage_.Read(url1).was_loaded());
EXPECT_FALSE(storage_.Read(url2).was_loaded());
}
TEST_F(BtmStorageTest, DidSiteHaveUserActivationSince) {
GURL url1("https://example1.com");
EXPECT_FALSE(storage_.DidSiteHaveUserActivationSince(
url1, base::Time::FromSecondsSinceUnixEpoch(0)));
storage_.WriteForTesting(
url1,
StateValue{
.user_activation_times{{base::Time::FromSecondsSinceUnixEpoch(2),
base::Time::FromSecondsSinceUnixEpoch(2)}},
.bounce_times{{base::Time::FromSecondsSinceUnixEpoch(3),
base::Time::FromSecondsSinceUnixEpoch(4)}}});
EXPECT_TRUE(storage_.DidSiteHaveUserActivationSince(
url1, base::Time::FromSecondsSinceUnixEpoch(0)));
EXPECT_TRUE(storage_.DidSiteHaveUserActivationSince(
url1, base::Time::FromSecondsSinceUnixEpoch(1)));
EXPECT_TRUE(storage_.DidSiteHaveUserActivationSince(
url1, base::Time::FromSecondsSinceUnixEpoch(2)));
EXPECT_FALSE(storage_.DidSiteHaveUserActivationSince(
url1, base::Time::FromSecondsSinceUnixEpoch(3)));
EXPECT_FALSE(storage_.DidSiteHaveUserActivationSince(
url1, base::Time::FromSecondsSinceUnixEpoch(4)));
}
TEST_F(BtmStorageTest, GetTimerLastFired_InitiallyReturnsEmpty) {
ASSERT_EQ(storage_.GetTimerLastFired(), std::nullopt);
}
TEST_F(BtmStorageTest, GetTimerLastFired_ReturnsLastSetValue) {
const base::Time time1 = base::Time::FromTimeT(1);
const base::Time time2 = base::Time::FromTimeT(2);
ASSERT_TRUE(storage_.SetTimerLastFired(time1));
ASSERT_TRUE(storage_.SetTimerLastFired(time2));
ASSERT_THAT(storage_.GetTimerLastFired(), testing::Optional(time2));
}
} // namespace content