1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "PartitioningExceptionList.h"
9 #include "AntiTrackingLog.h"
10 #include "nsContentUtils.h"
11 #include "nsServiceManagerUtils.h"
13 #include "mozilla/ClearOnShutdown.h"
14 #include "mozilla/StaticPtr.h"
20 static constexpr std::array
<nsLiteralCString
, 2> kSupportedSchemes
= {
21 {"https://"_ns
, "http://"_ns
}};
23 StaticRefPtr
<PartitioningExceptionList
> gPartitioningExceptionList
;
27 NS_IMPL_ISUPPORTS(PartitioningExceptionList
,
28 nsIPartitioningExceptionListObserver
)
30 bool PartitioningExceptionList::Check(const nsACString
& aFirstPartyOrigin
,
31 const nsACString
& aThirdPartyOrigin
) {
32 if (!StaticPrefs::privacy_antitracking_enableWebcompat()) {
33 LOG(("Partition exception list disabled via pref"));
37 if (aFirstPartyOrigin
.IsEmpty() || aFirstPartyOrigin
== "null" ||
38 aThirdPartyOrigin
.IsEmpty() || aThirdPartyOrigin
== "null") {
42 LOG(("Check partitioning exception list for url %s and %s",
43 PromiseFlatCString(aFirstPartyOrigin
).get(),
44 PromiseFlatCString(aThirdPartyOrigin
).get()));
46 for (PartitionExceptionListEntry
& entry
: GetOrCreate()->mExceptionList
) {
47 if (OriginMatchesPattern(aFirstPartyOrigin
, entry
.mFirstParty
) &&
48 OriginMatchesPattern(aThirdPartyOrigin
, entry
.mThirdParty
)) {
49 LOG(("Found partitioning exception list entry for %s and %s",
50 PromiseFlatCString(aFirstPartyOrigin
).get(),
51 PromiseFlatCString(aThirdPartyOrigin
).get()));
60 PartitioningExceptionList
* PartitioningExceptionList::GetOrCreate() {
61 if (!gPartitioningExceptionList
) {
62 gPartitioningExceptionList
= new PartitioningExceptionList();
63 gPartitioningExceptionList
->Init();
66 gPartitioningExceptionList
->Shutdown();
67 gPartitioningExceptionList
= nullptr;
71 return gPartitioningExceptionList
;
74 nsresult
PartitioningExceptionList::Init() {
76 do_GetService("@mozilla.org/partitioning/exception-list-service;1");
77 if (NS_WARN_IF(!mService
)) {
78 return NS_ERROR_FAILURE
;
81 mService
->RegisterAndRunExceptionListObserver(this);
85 void PartitioningExceptionList::Shutdown() {
87 mService
->UnregisterExceptionListObserver(this);
91 mExceptionList
.Clear();
95 PartitioningExceptionList::OnExceptionListUpdate(const nsACString
& aList
) {
96 mExceptionList
.Clear();
99 for (const nsACString
& item
: aList
.Split(';')) {
100 auto origins
= item
.Split(',');
101 auto originsIt
= origins
.begin();
103 if (originsIt
== origins
.end()) {
104 LOG(("Ignoring empty exception entry"));
108 PartitionExceptionListEntry entry
;
110 rv
= GetExceptionListPattern(*originsIt
, entry
.mFirstParty
);
111 if (NS_WARN_IF(NS_FAILED(rv
))) {
117 if (originsIt
== origins
.end()) {
118 LOG(("Ignoring incomplete exception entry"));
122 rv
= GetExceptionListPattern(*originsIt
, entry
.mThirdParty
);
123 if (NS_WARN_IF(NS_FAILED(rv
))) {
127 if (entry
.mFirstParty
.mSuffix
== "*" && entry
.mThirdParty
.mSuffix
== "*") {
128 LOG(("Ignoring *,* exception entry"));
132 LOG(("onExceptionListUpdate: %s%s - %s%s", entry
.mFirstParty
.mScheme
.get(),
133 entry
.mFirstParty
.mSuffix
.get(), entry
.mThirdParty
.mScheme
.get(),
134 entry
.mThirdParty
.mSuffix
.get()));
136 mExceptionList
.AppendElement(entry
);
142 nsresult
PartitioningExceptionList::GetSchemeFromOrigin(
143 const nsACString
& aOrigin
, nsACString
& aScheme
,
144 nsACString
& aOriginNoScheme
) {
145 NS_ENSURE_FALSE(aOrigin
.IsEmpty(), NS_ERROR_INVALID_ARG
);
147 for (const auto& scheme
: kSupportedSchemes
) {
148 if (aOrigin
.Length() <= scheme
.Length() ||
149 !StringBeginsWith(aOrigin
, scheme
)) {
152 aScheme
= Substring(aOrigin
, 0, scheme
.Length());
153 aOriginNoScheme
= Substring(aOrigin
, scheme
.Length());
157 return NS_ERROR_FAILURE
;
160 bool PartitioningExceptionList::OriginMatchesPattern(
161 const nsACString
& aOrigin
, const PartitionExceptionListPattern
& aPattern
) {
162 if (NS_WARN_IF(aOrigin
.IsEmpty())) {
166 if (aPattern
.mSuffix
== "*") {
170 nsAutoCString scheme
, originNoScheme
;
171 nsresult rv
= GetSchemeFromOrigin(aOrigin
, scheme
, originNoScheme
);
172 NS_ENSURE_SUCCESS(rv
, false);
174 // Always strict match scheme.
175 if (scheme
!= aPattern
.mScheme
) {
179 if (!aPattern
.mIsWildCard
) {
180 // aPattern is not a wildcard, match strict.
181 return originNoScheme
== aPattern
.mSuffix
;
184 // For wildcard patterns, check if origin suffix matches pattern suffix.
185 return StringEndsWith(originNoScheme
, aPattern
.mSuffix
);
188 // Parses a string with an origin or an origin-pattern into a
189 // PartitionExceptionListPattern.
190 nsresult
PartitioningExceptionList::GetExceptionListPattern(
191 const nsACString
& aOriginPattern
, PartitionExceptionListPattern
& aPattern
) {
192 NS_ENSURE_FALSE(aOriginPattern
.IsEmpty(), NS_ERROR_INVALID_ARG
);
194 if (aOriginPattern
== "*") {
195 aPattern
.mIsWildCard
= true;
196 aPattern
.mSuffix
= "*";
201 nsAutoCString originPatternNoScheme
;
202 nsresult rv
= GetSchemeFromOrigin(aOriginPattern
, aPattern
.mScheme
,
203 originPatternNoScheme
);
204 NS_ENSURE_SUCCESS(rv
, rv
);
206 if (StringBeginsWith(originPatternNoScheme
, "*"_ns
)) {
207 NS_ENSURE_TRUE(originPatternNoScheme
.Length() > 2, NS_ERROR_INVALID_ARG
);
209 aPattern
.mIsWildCard
= true;
210 aPattern
.mSuffix
= Substring(originPatternNoScheme
, 1);
215 aPattern
.mIsWildCard
= false;
216 aPattern
.mSuffix
= originPatternNoScheme
;
221 } // namespace mozilla