Bug 1803601 - Part 1: Adding a Services.cookieBanners.setDomainPrefAndPersistInPrivateBrowsing() to allow persist cookie banner domain pref for the private browsing. r=pbz

This patch adds a new function setDomainPrefAndPersistInPrivateBrowsing()
to nsICookieBannerService. This function allows persisting domain pref
in the private browsing mode.

Differential Revision: https://phabricator.services.mozilla.com/D164797
This commit is contained in:
Tim Huang 2022-12-19 11:39:46 +00:00
Родитель 0be0de0347
Коммит 3bd7091229
5 изменённых файлов: 190 добавлений и 60 удалений

Просмотреть файл

@ -19,12 +19,15 @@
#include "nsVariant.h"
#define COOKIE_BANNER_CONTENT_PREF_NAME u"cookiebanner"_ns
#define COOKIE_BANNER_CONTENT_PREF_NAME_PRIVATE u"cookiebannerprivate"_ns
namespace mozilla {
NS_IMPL_ISUPPORTS(CookieBannerDomainPrefService, nsIAsyncShutdownBlocker,
nsIObserver)
NS_IMPL_ISUPPORTS(CookieBannerDomainPrefService::DomainPrefData, nsISupports)
LazyLogModule gCookieBannerPerSitePrefLog("CookieBannerDomainPref");
static StaticRefPtr<CookieBannerDomainPrefService>
@ -90,7 +93,7 @@ void CookieBannerDomainPrefService::Init() {
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Fail to add observer for 'last-pb-context-exited'.");
auto initCallback = MakeRefPtr<InitialLoadContentPrefCallback>(this);
auto initCallback = MakeRefPtr<InitialLoadContentPrefCallback>(this, false);
// Populate the content pref for cookie banner domain preferences.
rv = contentPrefService->GetByName(COOKIE_BANNER_CONTENT_PREF_NAME, nullptr,
@ -98,6 +101,15 @@ void CookieBannerDomainPrefService::Init() {
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Fail to get all content prefs during init.");
auto initPrivateCallback =
MakeRefPtr<InitialLoadContentPrefCallback>(this, true);
// Populate the content pref for the private browsing.
rv = contentPrefService->GetByName(COOKIE_BANNER_CONTENT_PREF_NAME_PRIVATE,
nullptr, initPrivateCallback);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Fail to get all content prefs during init.");
rv = AddShutdownBlocker();
NS_ENSURE_SUCCESS_VOID(rv);
@ -123,10 +135,8 @@ void CookieBannerDomainPrefService::Shutdown() {
Maybe<nsICookieBannerService::Modes> CookieBannerDomainPrefService::GetPref(
const nsACString& aDomain, bool aIsPrivate) {
// For private windows, the domain prefs will only be stored in memory.
if (aIsPrivate) {
return mPrefsPrivate.MaybeGet(aDomain);
}
bool isContentPrefLoaded =
aIsPrivate ? mIsPrivateContentPrefLoaded : mIsContentPrefLoaded;
// We return nothing if the first reading of the content pref is not completed
// yet. Note that, we won't be able to get the domain pref for early loads.
@ -135,16 +145,23 @@ Maybe<nsICookieBannerService::Modes> CookieBannerDomainPrefService::GetPref(
// handling. So, there should be consent cookies in place to prevent banner
// showing. In this case, our cookie injection and banner clicking won't do
// anything.
if (!mIsContentPrefLoaded) {
if (!isContentPrefLoaded) {
return Nothing();
}
return mPrefs.MaybeGet(aDomain);
Maybe<RefPtr<DomainPrefData>> data =
aIsPrivate ? mPrefsPrivate.MaybeGet(aDomain) : mPrefs.MaybeGet(aDomain);
if (!data) {
return Nothing();
}
return Some(data.ref()->mMode);
}
nsresult CookieBannerDomainPrefService::SetPref(
const nsACString& aDomain, nsICookieBannerService::Modes aMode,
bool aIsPrivate) {
bool aIsPrivate, bool aPersistInPrivateBrowsing) {
MOZ_ASSERT(NS_IsMainThread());
// Don't do anything if we are shutting down.
if (NS_WARN_IF(mIsShuttingDown)) {
@ -153,16 +170,36 @@ nsresult CookieBannerDomainPrefService::SetPref(
return NS_OK;
}
// For private windows, the domain prefs will only be stored in memory.
if (aIsPrivate) {
Unused << mPrefsPrivate.InsertOrUpdate(aDomain, aMode);
return NS_OK;
}
EnsureInitCompleted(aIsPrivate);
EnsureInitCompleted();
// Create the domain pref data. The data is always persistent for normal
// windows. For private windows, the data is only persistent if requested.
auto domainPrefData = MakeRefPtr<DomainPrefData>(
aMode, aIsPrivate ? aPersistInPrivateBrowsing : true);
bool wasPersistentInPrivate = false;
// Update the in-memory domain preference map.
Unused << mPrefs.InsertOrUpdate(aDomain, aMode);
if (aIsPrivate) {
Maybe<RefPtr<DomainPrefData>> data = mPrefsPrivate.MaybeGet(aDomain);
wasPersistentInPrivate = data ? data.ref()->mIsPersistent : false;
Unused << mPrefsPrivate.InsertOrUpdate(aDomain, domainPrefData);
} else {
Unused << mPrefs.InsertOrUpdate(aDomain, domainPrefData);
}
// For private windows, the domain prefs will only be stored in memory.
// Unless, this function is instructed to persist setting for private
// browsing. To make the disk state consistent with the memory state, we need
// to clear the domain pref in the disk when we no longer need to persist the
// domain pref for the domain in PBM.
if (!aPersistInPrivateBrowsing && aIsPrivate) {
// Clear the domain pref in disk if it was persistent.
if (wasPersistentInPrivate) {
return RemoveContentPrefForDomain(aDomain, true);
}
return NS_OK;
}
// Set the preference to the content pref service.
nsCOMPtr<nsIContentPrefService2> contentPrefService =
@ -178,8 +215,10 @@ nsresult CookieBannerDomainPrefService::SetPref(
// Store the domain preference to the content pref service.
rv = contentPrefService->Set(NS_ConvertUTF8toUTF16(aDomain),
COOKIE_BANNER_CONTENT_PREF_NAME, variant,
nullptr, callback);
aIsPrivate
? COOKIE_BANNER_CONTENT_PREF_NAME_PRIVATE
: COOKIE_BANNER_CONTENT_PREF_NAME,
variant, nullptr, callback);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Fail to set cookie banner domain pref.");
@ -196,30 +235,16 @@ nsresult CookieBannerDomainPrefService::RemovePref(const nsACString& aDomain,
return NS_OK;
}
// For private windows, we only need to remove in-memory settings.
EnsureInitCompleted(aIsPrivate);
// Clear in-memory domain pref.
if (aIsPrivate) {
mPrefsPrivate.Remove(aDomain);
return NS_OK;
} else {
mPrefs.Remove(aDomain);
}
EnsureInitCompleted();
mPrefs.Remove(aDomain);
nsCOMPtr<nsIContentPrefService2> contentPrefService =
do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(contentPrefService, NS_ERROR_FAILURE);
auto callback = MakeRefPtr<WriteContentPrefCallback>(this);
mWritingCount++;
// Remove the domain preference from the content pref service.
nsresult rv = contentPrefService->RemoveByDomainAndName(
NS_ConvertUTF8toUTF16(aDomain), COOKIE_BANNER_CONTENT_PREF_NAME, nullptr,
callback);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Fail to remove cookie banner domain pref.");
return rv;
return RemoveContentPrefForDomain(aDomain, aIsPrivate);
}
nsresult CookieBannerDomainPrefService::RemoveAll(bool aIsPrivate) {
@ -231,16 +256,15 @@ nsresult CookieBannerDomainPrefService::RemoveAll(bool aIsPrivate) {
return NS_OK;
}
// For private windows, we only need to remove in-memory settings.
EnsureInitCompleted(aIsPrivate);
// Clear in-memory domain pref.
if (aIsPrivate) {
mPrefsPrivate.Clear();
return NS_OK;
} else {
mPrefs.Clear();
}
EnsureInitCompleted();
mPrefs.Clear();
nsCOMPtr<nsIContentPrefService2> contentPrefService =
do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(contentPrefService, NS_ERROR_FAILURE);
@ -250,21 +274,25 @@ nsresult CookieBannerDomainPrefService::RemoveAll(bool aIsPrivate) {
// Remove all the domain preferences.
nsresult rv = contentPrefService->RemoveByName(
COOKIE_BANNER_CONTENT_PREF_NAME, nullptr, callback);
aIsPrivate ? COOKIE_BANNER_CONTENT_PREF_NAME_PRIVATE
: COOKIE_BANNER_CONTENT_PREF_NAME,
nullptr, callback);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Fail to remove all cookie banner domain prefs.");
return rv;
}
void CookieBannerDomainPrefService::EnsureInitCompleted() {
if (mIsContentPrefLoaded) {
void CookieBannerDomainPrefService::EnsureInitCompleted(bool aIsPrivate) {
bool& isContentPrefLoaded =
aIsPrivate ? mIsPrivateContentPrefLoaded : mIsContentPrefLoaded;
if (isContentPrefLoaded) {
return;
}
// Wait until the service is fully initialized.
SpinEventLoopUntil("CookieBannerDomainPrefService::EnsureUpdateComplete"_ns,
[&] { return mIsContentPrefLoaded; });
[&] { return isContentPrefLoaded; });
}
nsresult CookieBannerDomainPrefService::AddShutdownBlocker() {
@ -287,6 +315,26 @@ nsresult CookieBannerDomainPrefService::RemoveShutdownBlocker() {
return GetShutdownBarrier()->RemoveBlocker(this);
}
nsresult CookieBannerDomainPrefService::RemoveContentPrefForDomain(
const nsACString& aDomain, bool aIsPrivate) {
nsCOMPtr<nsIContentPrefService2> contentPrefService =
do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(contentPrefService, NS_ERROR_FAILURE);
auto callback = MakeRefPtr<WriteContentPrefCallback>(this);
mWritingCount++;
// Remove the domain preference from the content pref service.
nsresult rv = contentPrefService->RemoveByDomainAndName(
NS_ConvertUTF8toUTF16(aDomain),
aIsPrivate ? COOKIE_BANNER_CONTENT_PREF_NAME_PRIVATE
: COOKIE_BANNER_CONTENT_PREF_NAME,
nullptr, callback);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Fail to remove cookie banner domain pref.");
return rv;
}
NS_IMPL_ISUPPORTS(CookieBannerDomainPrefService::BaseContentPrefCallback,
nsIContentPrefCallback2)
@ -312,8 +360,17 @@ CookieBannerDomainPrefService::InitialLoadContentPrefCallback::HandleResult(
rv = value->GetAsUint8(&data);
NS_ENSURE_SUCCESS(rv, rv);
Unused << mService->mPrefs.InsertOrUpdate(
NS_ConvertUTF16toUTF8(domain), nsICookieBannerService::Modes(data));
// Create the domain pref data and indicate it's persistent.
auto domainPrefData =
MakeRefPtr<DomainPrefData>(nsICookieBannerService::Modes(data), true);
if (mIsPrivate) {
Unused << mService->mPrefsPrivate.InsertOrUpdate(
NS_ConvertUTF16toUTF8(domain), domainPrefData);
} else {
Unused << mService->mPrefs.InsertOrUpdate(NS_ConvertUTF16toUTF8(domain),
domainPrefData);
}
return NS_OK;
}
@ -323,7 +380,11 @@ CookieBannerDomainPrefService::InitialLoadContentPrefCallback::HandleCompletion(
uint16_t aReason) {
MOZ_ASSERT(mService);
mService->mIsContentPrefLoaded = true;
if (mIsPrivate) {
mService->mIsPrivateContentPrefLoaded = true;
} else {
mService->mIsContentPrefLoaded = true;
}
return NS_OK;
}
@ -385,9 +446,12 @@ CookieBannerDomainPrefService::Observe(nsISupports* /*aSubject*/,
return NS_ERROR_UNEXPECTED;
}
// Clear the private browsing domain prefs if we observe the private browsing
// session has ended.
mPrefsPrivate.Clear();
// Clear the private browsing domain prefs that are not persistent when we
// observe the private browsing session has ended.
mPrefsPrivate.RemoveIf([](const auto& iter) {
const RefPtr<DomainPrefData>& data = iter.Data();
return !data->mIsPersistent;
});
return NS_OK;
}

Просмотреть файл

@ -38,7 +38,8 @@ class CookieBannerDomainPrefService final : public nsIAsyncShutdownBlocker,
// Set the preference for the given domain.
[[nodiscard]] nsresult SetPref(const nsACString& aDomain,
nsICookieBannerService::Modes aMode,
bool aIsPrivate);
bool aIsPrivate,
bool aPersistInPrivateBrowsing);
// Remove the preference for the given domain.
[[nodiscard]] nsresult RemovePref(const nsACString& aDomain, bool aIsPrivate);
@ -54,6 +55,7 @@ class CookieBannerDomainPrefService final : public nsIAsyncShutdownBlocker,
CookieBannerDomainPrefService()
: mIsInitialized(false),
mIsContentPrefLoaded(false),
mIsPrivateContentPrefLoaded(false),
mIsShuttingDown(false) {}
// Indicates whether the service is initialized.
@ -62,22 +64,48 @@ class CookieBannerDomainPrefService final : public nsIAsyncShutdownBlocker,
// Indicates whether the first reading of content pref completed.
bool mIsContentPrefLoaded;
// Indicates whether the first reading of content pref for the private
// browsing completed.
bool mIsPrivateContentPrefLoaded;
// Indicates whether we are shutting down.
bool mIsShuttingDown;
// A class to represent the domain pref. It's consist of the service mode and
// a boolean to indicated if the domain pref persists in the disk.
class DomainPrefData final : public nsISupports {
public:
NS_DECL_ISUPPORTS
explicit DomainPrefData(nsICookieBannerService::Modes aMode,
bool aIsPersistent)
: mMode(aMode), mIsPersistent(aIsPersistent) {}
private:
~DomainPrefData() = default;
friend class CookieBannerDomainPrefService;
nsICookieBannerService::Modes mMode;
bool mIsPersistent;
};
// Map of the per site preference keyed by domain.
nsTHashMap<nsCStringHashKey, nsICookieBannerService::Modes> mPrefs;
nsTHashMap<nsCStringHashKey, RefPtr<DomainPrefData>> mPrefs;
// Map of the per site preference for private windows keyed by domain.
nsTHashMap<nsCStringHashKey, nsICookieBannerService::Modes> mPrefsPrivate;
nsTHashMap<nsCStringHashKey, RefPtr<DomainPrefData>> mPrefsPrivate;
// A helper function that will wait until the initialization of the content
// pref completed.
void EnsureInitCompleted();
void EnsureInitCompleted(bool aIsPrivate);
nsresult AddShutdownBlocker();
nsresult RemoveShutdownBlocker();
nsresult RemoveContentPrefForDomain(const nsACString& aDomain,
bool aIsPrivate);
class BaseContentPrefCallback : public nsIContentPrefCallback2 {
public:
NS_DECL_ISUPPORTS
@ -99,8 +127,11 @@ class CookieBannerDomainPrefService final : public nsIAsyncShutdownBlocker,
NS_DECL_NSICONTENTPREFCALLBACK2
explicit InitialLoadContentPrefCallback(
CookieBannerDomainPrefService* aService)
: BaseContentPrefCallback(aService) {}
CookieBannerDomainPrefService* aService, bool aIsPrivate)
: BaseContentPrefCallback(aService), mIsPrivate(aIsPrivate) {}
private:
bool mIsPrivate;
};
class WriteContentPrefCallback final : public BaseContentPrefCallback {

Просмотреть файл

@ -625,6 +625,23 @@ nsCookieBannerService::SetDomainPref(nsIURI* aTopLevelURI,
const bool aIsPrivate) {
NS_ENSURE_ARG_POINTER(aTopLevelURI);
return SetDomainPrefInternal(aTopLevelURI, aModes, aIsPrivate, false);
}
NS_IMETHODIMP
nsCookieBannerService::SetDomainPrefAndPersistInPrivateBrowsing(
nsIURI* aTopLevelURI, nsICookieBannerService::Modes aModes) {
NS_ENSURE_ARG_POINTER(aTopLevelURI);
return SetDomainPrefInternal(aTopLevelURI, aModes, true, true);
};
NS_IMETHODIMP
nsCookieBannerService::SetDomainPrefInternal(
nsIURI* aTopLevelURI, nsICookieBannerService::Modes aModes,
const bool aIsPrivate, const bool aPersistInPrivateBrowsing) {
NS_ENSURE_ARG_POINTER(aTopLevelURI);
if (!mIsInitialized) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -638,7 +655,8 @@ nsCookieBannerService::SetDomainPref(nsIURI* aTopLevelURI,
rv = eTLDService->GetBaseDomain(aTopLevelURI, 0, baseDomain);
NS_ENSURE_SUCCESS(rv, rv);
rv = mDomainPrefService->SetPref(baseDomain, aModes, aIsPrivate);
rv = mDomainPrefService->SetPref(baseDomain, aModes, aIsPrivate,
aPersistInPrivateBrowsing);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;

Просмотреть файл

@ -81,6 +81,11 @@ class nsCookieBannerService final : public nsIObserver,
nsICookieBannerRule** aRule,
bool aReportTelemetry = false);
nsresult SetDomainPrefInternal(nsIURI* aTopLevelURI,
nsICookieBannerService::Modes aModes,
const bool aIsPrivate,
const bool aPersistInPrivateBrowsing);
/**
* Get the rule matching the provided URI.
* @param aURI - The URI to match the rule for.

Просмотреть файл

@ -96,6 +96,18 @@ interface nsICookieBannerService : nsISupports {
in nsICookieBannerService_Modes aMode,
in boolean aIsPrivate);
/**
* Set the domain preference of the given top-level URI. It will persist the
* domain preference for private browsing.
*
* WARNING: setting permanent domain preference _will_ leak data in private
* browsing. Only use if you understand the consequences and trade-offs. If
* you are unsure, |setDomainPref| is very likely what you want to use
* instead.
*/
void setDomainPrefAndPersistInPrivateBrowsing(in nsIURI aTopLevelURI,
in nsICookieBannerService_Modes aMode);
/**
* Remove the domain preference of the given top-level URI.
*/