Bug 1675375 Part 6: Break test_group_hittest.html into parts. r=botond
[gecko.git] / caps / OriginAttributes.cpp
blob3e30e1d4d1bd22736d18f29cc53f64e843d14426
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et 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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/OriginAttributes.h"
8 #include "mozilla/Preferences.h"
9 #include "mozilla/dom/BlobURLProtocolHandler.h"
10 #include "mozilla/dom/quota/QuotaManager.h"
11 #include "nsIEffectiveTLDService.h"
12 #include "nsIURI.h"
13 #include "nsURLHelper.h"
15 static const char kSourceChar = ':';
16 static const char kSanitizedChar = '+';
18 namespace mozilla {
20 static void MakeTopLevelInfo(const nsACString& aScheme, const nsACString& aHost,
21 int32_t aPort, bool aUseSite,
22 nsAString& aTopLevelInfo) {
23 if (!aUseSite) {
24 aTopLevelInfo.Assign(NS_ConvertUTF8toUTF16(aHost));
25 return;
28 // Note: If you change the serialization of the partition-key, please update
29 // StoragePrincipalHelper.cpp too.
31 nsAutoCString site;
32 site.AssignLiteral("(");
33 site.Append(aScheme);
34 site.Append(",");
35 site.Append(aHost);
36 if (aPort != -1) {
37 site.Append(",");
38 site.AppendInt(aPort);
40 site.AppendLiteral(")");
42 aTopLevelInfo.Assign(NS_ConvertUTF8toUTF16(site));
45 static void MakeTopLevelInfo(const nsACString& aScheme, const nsACString& aHost,
46 bool aUseSite, nsAString& aTopLevelInfo) {
47 MakeTopLevelInfo(aScheme, aHost, -1, aUseSite, aTopLevelInfo);
50 static void PopulateTopLevelInfoFromURI(const bool aIsTopLevelDocument,
51 nsIURI* aURI, bool aIsFirstPartyEnabled,
52 bool aForced, bool aUseSite,
53 nsString OriginAttributes::*aTarget,
54 OriginAttributes& aOriginAttributes) {
55 nsresult rv;
57 if (!aURI) {
58 return;
61 // If the prefs are off or this is not a top level load, bail out.
62 if ((!aIsFirstPartyEnabled || !aIsTopLevelDocument) && !aForced) {
63 return;
66 nsAString& topLevelInfo = aOriginAttributes.*aTarget;
68 nsAutoCString scheme;
69 rv = aURI->GetScheme(scheme);
70 NS_ENSURE_SUCCESS_VOID(rv);
72 if (scheme.EqualsLiteral("about")) {
73 MakeTopLevelInfo(scheme, nsLiteralCString(ABOUT_URI_FIRST_PARTY_DOMAIN),
74 aUseSite, topLevelInfo);
75 return;
78 // Add-on principals should never get any first-party domain
79 // attributes in order to guarantee their storage integrity when switching
80 // FPI on and off.
81 if (scheme.EqualsLiteral("moz-extension")) {
82 return;
85 nsCOMPtr<nsIPrincipal> blobPrincipal;
86 if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal(
87 aURI, getter_AddRefs(blobPrincipal))) {
88 MOZ_ASSERT(blobPrincipal);
89 topLevelInfo = blobPrincipal->OriginAttributesRef().*aTarget;
90 return;
93 nsCOMPtr<nsIEffectiveTLDService> tldService =
94 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
95 MOZ_ASSERT(tldService);
96 NS_ENSURE_TRUE_VOID(tldService);
98 nsAutoCString baseDomain;
99 rv = tldService->GetBaseDomain(aURI, 0, baseDomain);
100 if (NS_SUCCEEDED(rv)) {
101 MakeTopLevelInfo(scheme, baseDomain, aUseSite, topLevelInfo);
102 return;
105 // Saving before rv is overwritten.
106 bool isIpAddress = (rv == NS_ERROR_HOST_IS_IP_ADDRESS);
107 bool isInsufficientDomainLevels = (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS);
109 int32_t port;
110 rv = aURI->GetPort(&port);
111 NS_ENSURE_SUCCESS_VOID(rv);
113 nsAutoCString host;
114 rv = aURI->GetHost(host);
115 NS_ENSURE_SUCCESS_VOID(rv);
117 if (isIpAddress) {
118 // If the host is an IPv4/IPv6 address, we still accept it as a
119 // valid topLevelInfo.
120 nsAutoCString ipAddr;
122 if (net_IsValidIPv6Addr(host)) {
123 // According to RFC2732, the host of an IPv6 address should be an
124 // IPv6reference. The GetHost() of nsIURI will only return the IPv6
125 // address. So, we need to convert it back to IPv6reference here.
126 ipAddr.AssignLiteral("[");
127 ipAddr.Append(host);
128 ipAddr.AppendLiteral("]");
129 } else {
130 ipAddr = host;
133 MakeTopLevelInfo(scheme, ipAddr, port, aUseSite, topLevelInfo);
134 return;
137 if (aUseSite) {
138 MakeTopLevelInfo(scheme, host, port, aUseSite, topLevelInfo);
139 return;
142 if (isInsufficientDomainLevels) {
143 nsAutoCString publicSuffix;
144 rv = tldService->GetPublicSuffix(aURI, publicSuffix);
145 if (NS_SUCCEEDED(rv)) {
146 MakeTopLevelInfo(scheme, publicSuffix, port, aUseSite, topLevelInfo);
147 return;
152 void OriginAttributes::SetFirstPartyDomain(const bool aIsTopLevelDocument,
153 nsIURI* aURI, bool aForced) {
154 PopulateTopLevelInfoFromURI(
155 aIsTopLevelDocument, aURI, IsFirstPartyEnabled(), aForced,
156 StaticPrefs::privacy_firstparty_isolate_use_site(),
157 &OriginAttributes::mFirstPartyDomain, *this);
160 void OriginAttributes::SetFirstPartyDomain(const bool aIsTopLevelDocument,
161 const nsACString& aDomain) {
162 SetFirstPartyDomain(aIsTopLevelDocument, NS_ConvertUTF8toUTF16(aDomain));
165 void OriginAttributes::SetFirstPartyDomain(const bool aIsTopLevelDocument,
166 const nsAString& aDomain,
167 bool aForced) {
168 // If the pref is off or this is not a top level load, bail out.
169 if ((!IsFirstPartyEnabled() || !aIsTopLevelDocument) && !aForced) {
170 return;
173 mFirstPartyDomain = aDomain;
176 void OriginAttributes::SetPartitionKey(nsIURI* aURI) {
177 PopulateTopLevelInfoFromURI(
178 false /* aIsTopLevelDocument */, aURI, IsFirstPartyEnabled(),
179 true /* aForced */, StaticPrefs::privacy_dynamic_firstparty_use_site(),
180 &OriginAttributes::mPartitionKey, *this);
183 void OriginAttributes::SetPartitionKey(const nsACString& aDomain) {
184 SetPartitionKey(NS_ConvertUTF8toUTF16(aDomain));
187 void OriginAttributes::SetPartitionKey(const nsAString& aDomain) {
188 mPartitionKey = aDomain;
191 void OriginAttributes::CreateSuffix(nsACString& aStr) const {
192 URLParams params;
193 nsAutoString value;
196 // Important: While serializing any string-valued attributes, perform a
197 // release-mode assertion to make sure that they don't contain characters that
198 // will break the quota manager when it uses the serialization for file
199 // naming.
202 if (mInIsolatedMozBrowser) {
203 params.Set(u"inBrowser"_ns, u"1"_ns);
206 if (mUserContextId != nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID) {
207 value.Truncate();
208 value.AppendInt(mUserContextId);
209 params.Set(u"userContextId"_ns, value);
212 if (mPrivateBrowsingId) {
213 value.Truncate();
214 value.AppendInt(mPrivateBrowsingId);
215 params.Set(u"privateBrowsingId"_ns, value);
218 if (!mFirstPartyDomain.IsEmpty()) {
219 nsAutoString sanitizedFirstPartyDomain(mFirstPartyDomain);
220 sanitizedFirstPartyDomain.ReplaceChar(kSourceChar, kSanitizedChar);
222 params.Set(u"firstPartyDomain"_ns, sanitizedFirstPartyDomain);
225 if (!mGeckoViewSessionContextId.IsEmpty()) {
226 nsAutoString sanitizedGeckoViewUserContextId(mGeckoViewSessionContextId);
227 sanitizedGeckoViewUserContextId.ReplaceChar(
228 dom::quota::QuotaManager::kReplaceChars, kSanitizedChar);
230 params.Set(u"geckoViewUserContextId"_ns, sanitizedGeckoViewUserContextId);
233 if (!mPartitionKey.IsEmpty()) {
234 nsAutoString sanitizedPartitionKey(mPartitionKey);
235 sanitizedPartitionKey.ReplaceChar(kSourceChar, kSanitizedChar);
237 params.Set(u"partitionKey"_ns, sanitizedPartitionKey);
240 aStr.Truncate();
242 params.Serialize(value);
243 if (!value.IsEmpty()) {
244 aStr.AppendLiteral("^");
245 aStr.Append(NS_ConvertUTF16toUTF8(value));
248 // In debug builds, check the whole string for illegal characters too (just in
249 // case).
250 #ifdef DEBUG
251 nsAutoCString str;
252 str.Assign(aStr);
253 MOZ_ASSERT(str.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) ==
254 kNotFound);
255 #endif
258 void OriginAttributes::CreateAnonymizedSuffix(nsACString& aStr) const {
259 OriginAttributes attrs = *this;
261 if (!attrs.mFirstPartyDomain.IsEmpty()) {
262 attrs.mFirstPartyDomain.AssignLiteral("_anonymizedFirstPartyDomain_");
265 if (!attrs.mPartitionKey.IsEmpty()) {
266 attrs.mPartitionKey.AssignLiteral("_anonymizedPartitionKey_");
269 attrs.CreateSuffix(aStr);
272 bool OriginAttributes::PopulateFromSuffix(const nsACString& aStr) {
273 if (aStr.IsEmpty()) {
274 return true;
277 if (aStr[0] != '^') {
278 return false;
281 // If a non-default mPrivateBrowsingId is passed and is not present in the
282 // suffix, then it will retain the id when it should be default according
283 // to the suffix. Set to default before iterating to fix this.
284 mPrivateBrowsingId = nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
286 return URLParams::Parse(
287 Substring(aStr, 1, aStr.Length() - 1),
288 [this](const nsAString& aName, const nsAString& aValue) {
289 if (aName.EqualsLiteral("inBrowser")) {
290 if (!aValue.EqualsLiteral("1")) {
291 return false;
294 mInIsolatedMozBrowser = true;
295 return true;
298 if (aName.EqualsLiteral("addonId") || aName.EqualsLiteral("appId")) {
299 // No longer supported. Silently ignore so that legacy origin strings
300 // don't cause failures.
301 return true;
304 if (aName.EqualsLiteral("userContextId")) {
305 nsresult rv;
306 int64_t val = aValue.ToInteger64(&rv);
307 NS_ENSURE_SUCCESS(rv, false);
308 NS_ENSURE_TRUE(val <= UINT32_MAX, false);
309 mUserContextId = static_cast<uint32_t>(val);
311 return true;
314 if (aName.EqualsLiteral("privateBrowsingId")) {
315 nsresult rv;
316 int64_t val = aValue.ToInteger64(&rv);
317 NS_ENSURE_SUCCESS(rv, false);
318 NS_ENSURE_TRUE(val >= 0 && val <= UINT32_MAX, false);
319 mPrivateBrowsingId = static_cast<uint32_t>(val);
321 return true;
324 if (aName.EqualsLiteral("firstPartyDomain")) {
325 MOZ_RELEASE_ASSERT(mFirstPartyDomain.IsEmpty());
326 nsAutoString firstPartyDomain(aValue);
327 firstPartyDomain.ReplaceChar(kSanitizedChar, kSourceChar);
328 mFirstPartyDomain.Assign(firstPartyDomain);
329 return true;
332 if (aName.EqualsLiteral("geckoViewUserContextId")) {
333 MOZ_RELEASE_ASSERT(mGeckoViewSessionContextId.IsEmpty());
334 mGeckoViewSessionContextId.Assign(aValue);
335 return true;
338 if (aName.EqualsLiteral("partitionKey")) {
339 MOZ_RELEASE_ASSERT(mPartitionKey.IsEmpty());
340 nsAutoString partitionKey(aValue);
341 partitionKey.ReplaceChar(kSanitizedChar, kSourceChar);
342 mPartitionKey.Assign(partitionKey);
343 return true;
346 // No other attributes are supported.
347 return false;
351 bool OriginAttributes::PopulateFromOrigin(const nsACString& aOrigin,
352 nsACString& aOriginNoSuffix) {
353 // RFindChar is only available on nsCString.
354 nsCString origin(aOrigin);
355 int32_t pos = origin.RFindChar('^');
357 if (pos == kNotFound) {
358 aOriginNoSuffix = origin;
359 return true;
362 aOriginNoSuffix = Substring(origin, 0, pos);
363 return PopulateFromSuffix(Substring(origin, pos));
366 void OriginAttributes::SyncAttributesWithPrivateBrowsing(
367 bool aInPrivateBrowsing) {
368 mPrivateBrowsingId = aInPrivateBrowsing ? 1 : 0;
371 /* static */
372 bool OriginAttributes::IsPrivateBrowsing(const nsACString& aOrigin) {
373 nsAutoCString dummy;
374 OriginAttributes attrs;
375 if (NS_WARN_IF(!attrs.PopulateFromOrigin(aOrigin, dummy))) {
376 return false;
379 return !!attrs.mPrivateBrowsingId;
382 } // namespace mozilla