Commit 537f71f2 authored by sauski's avatar sauski Committed by Commit Bot

Access Context Auditing: Prevent recording repeated cookie accesses

To reduce the number of records pending update in the access context
audit database, an in memory set of previously seen cookie accesses is
added. This set is consulted before attempting database insertion and
is cleared on navigation.

Bug: 1132947
Change-Id: I87f1b2e1283c8c9a1ee76dc3649ce2f2f598f82b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2438391
Commit-Queue: Theodore Olsauskas-Warren <sauski@google.com>
Reviewed-by: default avatarMartin Šrámek <msramek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#813646}
parent 35b04f21
......@@ -6,6 +6,7 @@
#include "base/path_service.h"
#include "base/test/bind_test_util.h"
#include "base/test/simple_test_clock.h"
#include "base/test/test_timeouts.h"
#include "chrome/browser/browsing_data/access_context_audit_service.h"
#include "chrome/browser/browsing_data/access_context_audit_service_factory.h"
......@@ -469,6 +470,34 @@ IN_PROC_BROWSER_TEST_F(AccessContextAuditBrowserTest, TreeModelDeletion) {
EXPECT_EQ(cookies.size(), 0u);
}
IN_PROC_BROWSER_TEST_F(AccessContextAuditBrowserTest, MultipleAccesses) {
// Ensure that renavigating to a page in the same tab correctly re-records
// accesses.
base::SimpleTestClock clock;
clock.SetNow(base::Time::Now());
AccessContextAuditServiceFactory::GetForProfile(browser()->profile())
->SetClockForTesting(&clock);
NavigateToTopLevelPage();
NavigateToEmbeddedPage();
// Check all records have the initial access time.
auto records = GetAllAccessRecords();
for (const auto& record : records)
EXPECT_EQ(record.last_access_time, clock.Now());
// Renavigate to the same pages, this should update the access times on all
// records.
clock.Advance(base::TimeDelta::FromHours(1));
NavigateToTopLevelPage();
NavigateToEmbeddedPage();
// All records should now have the updated time.
records = GetAllAccessRecords();
for (const auto& record : records)
EXPECT_EQ(record.last_access_time, clock.Now());
}
class AccessContextAuditSessionRestoreBrowserTest
: public AccessContextAuditBrowserTest {
public:
......
......@@ -16,6 +16,37 @@
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "content/public/browser/storage_partition.h"
AccessContextAuditService::CookieAccessHelper::CookieAccessHelper(
AccessContextAuditService* service)
: service_(service) {
DCHECK(service);
deletion_observer_.Add(service);
}
AccessContextAuditService::CookieAccessHelper::~CookieAccessHelper() = default;
void AccessContextAuditService::CookieAccessHelper::OnCookieDeleted(
const net::CanonicalCookie& cookie) {
seen_cookies_.erase(cookie);
}
void AccessContextAuditService::CookieAccessHelper::RecordCookieAccess(
const net::CookieList& accessed_cookies,
const url::Origin& top_frame_origin) {
net::CookieList new_cookies;
for (const auto& cookie : accessed_cookies) {
if (!seen_cookies_.count(cookie)) {
new_cookies.push_back(cookie);
seen_cookies_.insert(cookie);
}
}
if (!new_cookies.empty())
service_->RecordCookieAccess(new_cookies, top_frame_origin);
}
void AccessContextAuditService::CookieAccessHelper::ClearSeenCookies() {
seen_cookies_.clear();
}
AccessContextAuditService::AccessContextAuditService(Profile* profile)
: clock_(base::DefaultClock::GetInstance()), profile_(profile) {}
AccessContextAuditService::~AccessContextAuditService() = default;
......@@ -68,8 +99,8 @@ void AccessContextAuditService::RecordCookieAccess(
continue;
access_records.emplace_back(top_frame_origin, cookie.Name(),
cookie.Domain(), cookie.Path(),
cookie.LastAccessDate(), cookie.IsPersistent());
cookie.Domain(), cookie.Path(), now,
cookie.IsPersistent());
}
database_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&AccessContextAuditDatabase::AddRecords,
......@@ -198,6 +229,10 @@ void AccessContextAuditService::OnCookieChange(
case net::CookieChangeCause::EXPIRED:
case net::CookieChangeCause::EVICTED:
case net::CookieChangeCause::EXPIRED_OVERWRITE: {
// Notify helpers so that future accesses to this cookie are reported.
for (auto& helper : cookie_access_helpers_) {
helper.OnCookieDeleted(change.cookie);
}
// Remove records of deleted cookie from database.
database_task_runner_->PostTask(
FROM_HERE,
......@@ -251,6 +286,14 @@ void AccessContextAuditService::OnURLsDeleted(
}
}
void AccessContextAuditService::AddObserver(CookieAccessHelper* helper) {
cookie_access_helpers_.AddObserver(helper);
}
void AccessContextAuditService::RemoveObserver(CookieAccessHelper* helper) {
cookie_access_helpers_.RemoveObserver(helper);
}
void AccessContextAuditService::SetClockForTesting(base::Clock* clock) {
clock_ = clock;
}
......
......@@ -8,6 +8,7 @@
#include "base/updateable_sequenced_task_runner.h"
#include "chrome/browser/browsing_data/access_context_audit_database.h"
#include "chrome/browser/profiles/profile.h"
#include "components/browsing_data/content/canonical_cookie_hash.h"
#include "components/browsing_data/content/local_shared_objects_container.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_service_observer.h"
......@@ -27,6 +28,43 @@ class AccessContextAuditService
public history::HistoryServiceObserver,
public content::StoragePartition::DataRemovalObserver {
public:
class CookieAccessHelper;
void AddObserver(CookieAccessHelper* helper);
void RemoveObserver(CookieAccessHelper* helper);
// A helper class used to report cookie accesses to the audit service. Keeps
// an internal record of cookie accesses which have already been seen.
// Repeated calls to RecordCookieAccess are ignored until the cookie is
// observed as deleted, or the set of seen cookies is cleared via
// ClearSeenCookies.
class CookieAccessHelper : public base::CheckedObserver {
public:
explicit CookieAccessHelper(AccessContextAuditService* service);
~CookieAccessHelper() override;
// Selectively forwards cookie accesses to the audit service based on
// whether this helper has previously seen the cookie.
void RecordCookieAccess(const net::CookieList& accessed_cookies,
const url::Origin& top_frame_origin);
// Observer method called by the audit service when a cookie has been
// deleted and future accesses should be reported.
void OnCookieDeleted(const net::CanonicalCookie& cookie);
// Resets the internal set of seen cookies, resulting in future reported
// accesses to those cookies being forwarded to the service for recording.
// This should be called at least prior to every top-frame navigation,
// calling more frequently increases accuracy of access timestamps but also
// increases performance overhead.
void ClearSeenCookies();
private:
AccessContextAuditService* service_;
canonical_cookie::CookieHashSet seen_cookies_;
ScopedObserver<AccessContextAuditService, CookieAccessHelper>
deletion_observer_{this};
};
explicit AccessContextAuditService(Profile* profile);
~AccessContextAuditService() override;
......@@ -37,10 +75,6 @@ class AccessContextAuditService
history::HistoryService* history_service,
content::StoragePartition* storage_partition);
// Records accesses for all cookies in |details| against |top_frame_origin|.
void RecordCookieAccess(const net::CookieList& accessed_cookies,
const url::Origin& top_frame_origin);
// Records access for |storage_origin|'s storage of |type| against
// |top_frame_origin|.
void RecordStorageAPIAccess(const url::Origin& storage_origin,
......@@ -89,8 +123,20 @@ class AccessContextAuditService
private:
friend class AccessContextAuditServiceTest;
FRIEND_TEST_ALL_PREFIXES(AccessContextAuditServiceTest, CookieRecords);
FRIEND_TEST_ALL_PREFIXES(AccessContextAuditServiceTest, ExpiredCookies);
FRIEND_TEST_ALL_PREFIXES(AccessContextAuditServiceTest, HistoryDeletion);
FRIEND_TEST_ALL_PREFIXES(AccessContextAuditServiceTest, AllHistoryDeletion);
FRIEND_TEST_ALL_PREFIXES(AccessContextAuditServiceTest,
TimeRangeHistoryDeletion);
FRIEND_TEST_ALL_PREFIXES(AccessContextAuditServiceTest, OpaqueOrigins);
FRIEND_TEST_ALL_PREFIXES(AccessContextAuditServiceTest, SessionOnlyRecords);
// Records accesses for all cookies in |details| against |top_frame_origin|.
// Should only be accessed via the CookieAccessHelper.
void RecordCookieAccess(const net::CookieList& accessed_cookies,
const url::Origin& top_frame_origin);
// Removes any records which are session only from the database.
void ClearSessionOnlyRecords();
......@@ -102,6 +148,8 @@ class AccessContextAuditService
base::Clock* clock_;
Profile* profile_;
base::ObserverList<CookieAccessHelper> cookie_access_helpers_;
mojo::Receiver<network::mojom::CookieChangeListener>
cookie_listener_receiver_{this};
ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
......
......@@ -36,6 +36,7 @@ namespace {
void CheckContainsCookieRecord(
net::CanonicalCookie* cookie,
url::Origin top_frame_origin,
base::Time last_access_time,
const std::vector<AccessContextAuditDatabase::AccessRecord>& records) {
EXPECT_NE(
std::find_if(
......@@ -47,7 +48,7 @@ void CheckContainsCookieRecord(
record.name == cookie->Name() &&
record.domain == cookie->Domain() &&
record.path == cookie->Path() &&
record.last_access_time == cookie->LastAccessDate() &&
record.last_access_time == last_access_time &&
record.is_persistent == cookie->IsPersistent();
}),
records.end());
......@@ -59,13 +60,15 @@ void CheckContainsStorageAPIRecord(
url::Origin storage_origin,
AccessContextAuditDatabase::StorageAPIType type,
url::Origin top_frame_origin,
base::Time last_access_time,
const std::vector<AccessContextAuditDatabase::AccessRecord>& records) {
EXPECT_NE(
std::find_if(records.begin(), records.end(),
[=](const AccessContextAuditDatabase::AccessRecord& record) {
return record.type == type &&
record.origin == storage_origin &&
record.top_frame_origin == top_frame_origin;
record.top_frame_origin == top_frame_origin &&
record.last_access_time == last_access_time;
}),
records.end());
}
......@@ -196,14 +199,17 @@ TEST_F(AccessContextAuditServiceTest, CookieRecords) {
GURL kTestCookieURL("https://example.com");
std::string kTestCookieName = "test";
std::string kTestNonPersistentCookieName = "test-non-persistent";
base::Time initial_cookie_access_time = base::Time::Now();
const base::Time kAccessTime1 = base::Time::Now();
clock()->SetNow(kAccessTime1);
service()->SetClockForTesting(clock());
auto test_cookie = net::CanonicalCookie::Create(
kTestCookieURL, kTestCookieName + "=1; max-age=3600",
initial_cookie_access_time, base::nullopt /* server_time */);
kTestCookieURL, kTestCookieName + "=1; max-age=3600", kAccessTime1,
base::nullopt /* server_time */);
auto test_non_persistent_cookie = net::CanonicalCookie::Create(
kTestCookieURL, kTestNonPersistentCookieName + "=1",
initial_cookie_access_time, base::nullopt /* server_time */);
kTestCookieURL, kTestNonPersistentCookieName + "=1", kAccessTime1,
base::nullopt /* server_time */);
// Record access to these cookies against a URL.
url::Origin kTopFrameOrigin = url::Origin::Create(GURL("https://test.com"));
service()->RecordCookieAccess({*test_cookie, *test_non_persistent_cookie},
......@@ -212,9 +218,10 @@ TEST_F(AccessContextAuditServiceTest, CookieRecords) {
// Ensure that the record of these accesses is correctly returned.
auto records = GetAllAccessRecords();
EXPECT_EQ(2u, records.size());
CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, records);
CheckContainsCookieRecord(test_non_persistent_cookie.get(), kTopFrameOrigin,
CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, kAccessTime1,
records);
CheckContainsCookieRecord(test_non_persistent_cookie.get(), kTopFrameOrigin,
kAccessTime1, records);
// Check that informing the service of non-deletion changes to the cookies
// via the CookieChangeInterface is a no-op.
......@@ -227,23 +234,23 @@ TEST_F(AccessContextAuditServiceTest, CookieRecords) {
records = GetAllAccessRecords();
EXPECT_EQ(2u, records.size());
CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, records);
CheckContainsCookieRecord(test_non_persistent_cookie.get(), kTopFrameOrigin,
CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, kAccessTime1,
records);
CheckContainsCookieRecord(test_non_persistent_cookie.get(), kTopFrameOrigin,
kAccessTime1, records);
// Check that a repeated access correctly updates associated timestamp.
base::Time repeat_cookie_access_time =
initial_cookie_access_time + base::TimeDelta::FromHours(2);
test_cookie->SetLastAccessDate(repeat_cookie_access_time);
test_non_persistent_cookie->SetLastAccessDate(repeat_cookie_access_time);
clock()->Advance(base::TimeDelta::FromHours(1));
const base::Time kAccessTime2 = clock()->Now();
service()->RecordCookieAccess({*test_cookie, *test_non_persistent_cookie},
kTopFrameOrigin);
records = GetAllAccessRecords();
EXPECT_EQ(2u, records.size());
CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, records);
CheckContainsCookieRecord(test_non_persistent_cookie.get(), kTopFrameOrigin,
CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, kAccessTime2,
records);
CheckContainsCookieRecord(test_non_persistent_cookie.get(), kTopFrameOrigin,
kAccessTime2, records);
// Inform the service the cookies have been deleted and check they are no
// longer returned.
......@@ -283,6 +290,9 @@ TEST_F(AccessContextAuditServiceTest, OriginKeyedStorageDeleted) {
url::Origin::Create(GURL("https://example2.com"));
const url::Origin kTestTopLevelOrigin =
url::Origin::Create(GURL("https://example3.com"));
const base::Time kAccessTime = base::Time::Now();
clock()->SetNow(kAccessTime);
service()->SetClockForTesting(clock());
// Record accesses for the 4 possible test type and origin combinations.
service()->RecordStorageAPIAccess(kTestOrigin1, kTestStorageType1,
......@@ -302,11 +312,11 @@ TEST_F(AccessContextAuditServiceTest, OriginKeyedStorageDeleted) {
auto records = GetAllAccessRecords();
EXPECT_EQ(3u, records.size());
CheckContainsStorageAPIRecord(kTestOrigin1, kTestStorageType2,
kTestTopLevelOrigin, records);
kTestTopLevelOrigin, kAccessTime, records);
CheckContainsStorageAPIRecord(kTestOrigin2, kTestStorageType1,
kTestTopLevelOrigin, records);
kTestTopLevelOrigin, kAccessTime, records);
CheckContainsStorageAPIRecord(kTestOrigin2, kTestStorageType2,
kTestTopLevelOrigin, records);
kTestTopLevelOrigin, kAccessTime, records);
}
TEST_F(AccessContextAuditServiceTest, HistoryDeletion) {
......@@ -318,15 +328,19 @@ TEST_F(AccessContextAuditServiceTest, HistoryDeletion) {
url::Origin::Create(GURL("http://test.com"));
const GURL kTestCookieURL("https://example.com");
const std::string kTestCookieName = "test";
auto test_cookie = net::CanonicalCookie::Create(
kTestCookieURL, kTestCookieName + "=1; max-age=3600", base::Time::Now(),
base::nullopt /* server_time */);
const GURL kURL1 = GURL("https://remaining-entries.com/test1");
const GURL kURL2 = GURL("https://remaining-entries.com/test2");
const GURL kURL3 = GURL("https://no-remaining-entries.com/test1");
const url::Origin kHistoryEntriesRemainingOrigin = url::Origin::Create(kURL1);
const url::Origin kNoRemainingHistoryEntriesOrigin =
url::Origin::Create(kURL3);
const base::Time kAccessTime = base::Time::Now();
clock()->SetNow(kAccessTime);
service()->SetClockForTesting(clock());
auto test_cookie = net::CanonicalCookie::Create(
kTestCookieURL, kTestCookieName + "=1; max-age=3600", kAccessTime,
base::nullopt /* server_time */);
// Record access for two top level origins for the same storage and cookie.
service()->RecordCookieAccess({*test_cookie}, kHistoryEntriesRemainingOrigin);
......@@ -363,9 +377,10 @@ TEST_F(AccessContextAuditServiceTest, HistoryDeletion) {
auto records = GetAllAccessRecords();
EXPECT_EQ(2u, records.size());
CheckContainsCookieRecord(test_cookie.get(), kHistoryEntriesRemainingOrigin,
records);
kAccessTime, records);
CheckContainsStorageAPIRecord(kTestStorageOrigin, kTestStorageType,
kHistoryEntriesRemainingOrigin, records);
kHistoryEntriesRemainingOrigin, kAccessTime,
records);
}
TEST_F(AccessContextAuditServiceTest, AllHistoryDeletion) {
......@@ -462,7 +477,9 @@ TEST_F(AccessContextAuditServiceTest, TimeRangeHistoryDeletion) {
kTestCookieURL, "outside=1; max-age=3600", kOutsideTimeRange,
base::nullopt /* server_time */);
clock()->SetNow(kInsideTimeRange);
service()->RecordCookieAccess({*cookie_accessed_in_range}, kOrigin1);
clock()->SetNow(kOutsideTimeRange);
service()->RecordCookieAccess({*cookie_accessed_outside_range}, kOrigin1);
service()->RecordCookieAccess({*cookie_accessed_outside_range}, kOrigin2);
......@@ -489,8 +506,9 @@ TEST_F(AccessContextAuditServiceTest, TimeRangeHistoryDeletion) {
auto records = GetAllAccessRecords();
EXPECT_EQ(2u, records.size());
CheckContainsCookieRecord(cookie_accessed_outside_range.get(), kOrigin1,
records);
CheckContainsStorageAPIRecord(kOrigin1, kTestStorageType2, kOrigin1, records);
kOutsideTimeRange, records);
CheckContainsStorageAPIRecord(kOrigin1, kTestStorageType2, kOrigin1,
kOutsideTimeRange, records);
}
TEST_F(AccessContextAuditServiceTest, SessionOnlyRecords) {
......@@ -503,23 +521,26 @@ TEST_F(AccessContextAuditServiceTest, SessionOnlyRecords) {
std::string kTestCookieName = "test";
const auto kTestStorageType =
AccessContextAuditDatabase::StorageAPIType::kWebDatabase;
const base::Time kAccessTime = base::Time::Now();
clock()->SetNow(kAccessTime);
service()->SetClockForTesting(clock());
// Create a cookie that will persist after shutdown.
auto test_cookie_persistent = net::CanonicalCookie::Create(
kTestPersistentURL, kTestCookieName + "=1; max-age=3600",
base::Time::Now(), base::nullopt /* server_time */);
kTestPersistentURL, kTestCookieName + "=1; max-age=3600", kAccessTime,
base::nullopt /* server_time */);
// Create a cookie that will persist (be cleared on next startup) because it
// is explicitly session only.
auto test_cookie_session_only_explicit = net::CanonicalCookie::Create(
kTestSessionOnlyExplicitURL, kTestCookieName + "=1", base::Time::Now(),
kTestSessionOnlyExplicitURL, kTestCookieName + "=1", kAccessTime,
base::nullopt /* server_time */);
// Create a cookie that will be cleared because the content setting associated
// with the cookie domain is set to session only.
auto test_cookie_session_only_content_setting = net::CanonicalCookie::Create(
kTestSessionOnlyContentSettingURL, kTestCookieName + "=1; max-age=3600",
base::Time::Now(), base::nullopt /* server_time */);
kAccessTime, base::nullopt /* server_time */);
service()->RecordCookieAccess(
{*test_cookie_persistent, *test_cookie_session_only_explicit,
......@@ -551,11 +572,12 @@ TEST_F(AccessContextAuditServiceTest, SessionOnlyRecords) {
auto records = GetAllAccessRecords();
ASSERT_EQ(3u, records.size());
CheckContainsCookieRecord(test_cookie_persistent.get(), kTopFrameOrigin,
records);
kAccessTime, records);
CheckContainsCookieRecord(test_cookie_session_only_explicit.get(),
kTopFrameOrigin, records);
kTopFrameOrigin, kAccessTime, records);
CheckContainsStorageAPIRecord(url::Origin::Create(GURL(kTestPersistentURL)),
kTestStorageType, kTopFrameOrigin, records);
kTestStorageType, kTopFrameOrigin, kAccessTime,
records);
// Update the default content setting to SESSION_ONLY and ensure that all
// records are cleared.
......@@ -590,10 +612,12 @@ TEST_F(AccessContextAuditServiceTest, OnOriginDataCleared) {
kTopFrameOrigin);
clock()->Advance(base::TimeDelta::FromHours(1));
const base::Time kAccessTime1 = clock()->Now();
service()->RecordStorageAPIAccess(kTestOrigin2, kTestStorageType2,
kTopFrameOrigin);
clock()->Advance(base::TimeDelta::FromHours(1));
const base::Time kAccessTime2 = clock()->Now();
service()->RecordStorageAPIAccess(kTestOrigin3, kTestStorageType3,
kTopFrameOrigin);
EXPECT_EQ(3U, GetAllAccessRecords().size());
......@@ -609,9 +633,9 @@ TEST_F(AccessContextAuditServiceTest, OnOriginDataCleared) {
auto records = GetAllAccessRecords();
ASSERT_EQ(2U, records.size());
CheckContainsStorageAPIRecord(kTestOrigin2, kTestStorageType2,
kTopFrameOrigin, records);
kTopFrameOrigin, kAccessTime1, records);
CheckContainsStorageAPIRecord(kTestOrigin3, kTestStorageType3,
kTopFrameOrigin, records);
kTopFrameOrigin, kAccessTime2, records);
// Provide more generalised parameters that target TestOrigin2's record.
service()->OnOriginDataCleared(
......@@ -622,7 +646,7 @@ TEST_F(AccessContextAuditServiceTest, OnOriginDataCleared) {
records = GetAllAccessRecords();
ASSERT_EQ(1U, records.size());
CheckContainsStorageAPIRecord(kTestOrigin3, kTestStorageType3,
kTopFrameOrigin, records);
kTopFrameOrigin, kAccessTime2, records);
// Provide broadest possible parameters which should result in the final
// record being removed.
......@@ -647,3 +671,69 @@ TEST_F(AccessContextAuditServiceTest, OpaqueOrigins) {
auto records = GetAllAccessRecords();
ASSERT_EQ(0U, records.size());
}
TEST_F(AccessContextAuditServiceTest, CookieAccessHelper) {
// Check that the CookieAccessHelper is correctly forwarding accesses as
// appropriate and responding to deletions.
url::Origin kTopFrameOrigin = url::Origin::Create(GURL("https://test.com"));
GURL kTestCookieURL("https://example.com");
std::string kTestCookieName = "test";
const base::Time kAccessTime1 = base::Time::Now();
clock()->SetNow(kAccessTime1);
service()->SetClockForTesting(clock());
auto test_cookie = net::CanonicalCookie::Create(
kTestCookieURL, kTestCookieName + "=1; max-age=3600", kAccessTime1,
base::nullopt /* server_time */);
// Record access to the cookie via a helper.
AccessContextAuditService::CookieAccessHelper helper(service());
helper.RecordCookieAccess({*test_cookie}, kTopFrameOrigin);
// Confirm cookie access has been recorded.
auto records = GetAllAccessRecords();
EXPECT_EQ(1u, records.size());
CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, kAccessTime1,
records);
// Reaccess the cookie at a later time.
const base::Time kAccessTime2 =
clock()->Now() + base::TimeDelta::FromMinutes(1);
clock()->SetNow(kAccessTime2);
helper.RecordCookieAccess({*test_cookie}, kTopFrameOrigin);
// The only record should match the first access.
records = GetAllAccessRecords();
EXPECT_EQ(1u, records.size());
CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, kAccessTime1,
records);
// Clear seen cookies on the helper and reaccess the cookie, and confirm that
// the database record is also updated.
helper.ClearSeenCookies();
helper.RecordCookieAccess({*test_cookie}, kTopFrameOrigin);
records = GetAllAccessRecords();
EXPECT_EQ(1u, records.size());
CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, kAccessTime2,
records);
// Inform the audit service that the cookie has been deleted, which should
// cause the helper to clear it from the set of accessed cookies.
service()->OnCookieChange(
net::CookieChangeInfo(*test_cookie, net::CookieAccessResult(),
net::CookieChangeCause::EXPLICIT));
// Update the access time and reaccess the cookie, this should update the
// record in the database as the deletion should have cleared it from the set
// of seen cookies.
const base::Time kAccessTime3 =
clock()->Now() + base::TimeDelta::FromMinutes(1);
clock()->SetNow(kAccessTime3);
helper.RecordCookieAccess({*test_cookie}, kTopFrameOrigin);
records = GetAllAccessRecords();
EXPECT_EQ(1u, records.size());
CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, kAccessTime3,
records);
}
......@@ -52,7 +52,18 @@ namespace chrome {
PageSpecificContentSettingsDelegate::PageSpecificContentSettingsDelegate(
content::WebContents* web_contents)
: WebContentsObserver(web_contents) {}
: WebContentsObserver(web_contents) {
#if !defined(OS_ANDROID)
auto* access_context_audit_service =
AccessContextAuditServiceFactory::GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext()));
if (access_context_audit_service) {
cookie_access_helper_ =
std::make_unique<AccessContextAuditService::CookieAccessHelper>(
access_context_audit_service);
}
#endif // !defined(OS_ANDROID)
}
PageSpecificContentSettingsDelegate::~PageSpecificContentSettingsDelegate() =
default;
......@@ -202,13 +213,11 @@ void PageSpecificContentSettingsDelegate::OnCacheStorageAccessAllowed(
void PageSpecificContentSettingsDelegate::OnCookieAccessAllowed(
const net::CookieList& accessed_cookies) {
#if !defined(OS_ANDROID)
auto* access_context_audit_service =
AccessContextAuditServiceFactory::GetForProfile(
Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
if (access_context_audit_service)
access_context_audit_service->RecordCookieAccess(
if (cookie_access_helper_) {
cookie_access_helper_->RecordCookieAccess(
accessed_cookies,
url::Origin::Create(web_contents()->GetLastCommittedURL()));
}
#endif // !defined(OS_ANDROID)
}
......@@ -256,6 +265,15 @@ void PageSpecificContentSettingsDelegate::OnWebDatabaseAccessAllowed(
web_contents());
#endif // !defined(OS_ANDROID)
}
void PageSpecificContentSettingsDelegate::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
#if !defined(OS_ANDROID)
if (cookie_access_helper_ && navigation_handle->IsInMainFrame() &&
!navigation_handle->IsSameDocument()) {
cookie_access_helper_->ClearSeenCookies();
}
#endif // !defined(OS_ANDROID)
}
void PageSpecificContentSettingsDelegate::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
......
......@@ -5,9 +5,14 @@
#ifndef CHROME_BROWSER_CONTENT_SETTINGS_PAGE_SPECIFIC_CONTENT_SETTINGS_DELEGATE_H_
#define CHROME_BROWSER_CONTENT_SETTINGS_PAGE_SPECIFIC_CONTENT_SETTINGS_DELEGATE_H_
#include "build/build_config.h"
#include "chrome/common/custom_handlers/protocol_handler.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#if !defined(OS_ANDROID)
#include "chrome/browser/browsing_data/access_context_audit_service.h"
#endif // !defined(OS_ANDROID)
namespace chrome {
class PageSpecificContentSettingsDelegate
......@@ -89,6 +94,8 @@ class PageSpecificContentSettingsDelegate
void OnWebDatabaseAccessAllowed(const url::Origin& origin) override;
// content::WebContentsObserver:
void DidStartNavigation(
content::NavigationHandle* navigation_handle) override;
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
......@@ -108,6 +115,11 @@ class PageSpecificContentSettingsDelegate
// The setting on the pending protocol handler registration. Persisted in case
// the user opens the bubble and makes changes multiple times.
ContentSetting pending_protocol_handler_setting_ = CONTENT_SETTING_DEFAULT;
#if !defined(OS_ANDROID)
std::unique_ptr<AccessContextAuditService::CookieAccessHelper>
cookie_access_helper_;
#endif // !defined(OS_ANDROID)
};
} // namespace chrome
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment