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/URLSearchParams.h"
11 #include "mozilla/dom/quota/QuotaManager.h"
12 #include "nsIEffectiveTLDService.h"
14 #include "nsURLHelper.h"
16 static const char kSourceChar
= ':';
17 static const char kSanitizedChar
= '+';
23 void OriginAttributes::SetFirstPartyDomain(const bool aIsTopLevelDocument
,
24 nsIURI
* aURI
, bool aForced
) {
25 bool isFirstPartyEnabled
= IsFirstPartyEnabled();
27 // If the prefs are off or this is not a top level load, bail out.
28 if ((!isFirstPartyEnabled
|| !aIsTopLevelDocument
) && !aForced
) {
32 nsCOMPtr
<nsIEffectiveTLDService
> tldService
=
33 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID
);
34 MOZ_ASSERT(tldService
);
39 nsAutoCString baseDomain
;
40 nsresult rv
= tldService
->GetBaseDomain(aURI
, 0, baseDomain
);
41 if (NS_SUCCEEDED(rv
)) {
42 mFirstPartyDomain
= NS_ConvertUTF8toUTF16(baseDomain
);
46 if (rv
== NS_ERROR_HOST_IS_IP_ADDRESS
) {
47 // If the host is an IPv4/IPv6 address, we still accept it as a
48 // valid firstPartyDomain.
50 rv
= aURI
->GetHost(ipAddr
);
51 NS_ENSURE_SUCCESS_VOID(rv
);
53 if (net_IsValidIPv6Addr(ipAddr
)) {
54 // According to RFC2732, the host of an IPv6 address should be an
55 // IPv6reference. The GetHost() of nsIURI will only return the IPv6
56 // address. So, we need to convert it back to IPv6reference here.
57 mFirstPartyDomain
.Truncate();
58 mFirstPartyDomain
.AssignLiteral("[");
59 mFirstPartyDomain
.Append(NS_ConvertUTF8toUTF16(ipAddr
));
60 mFirstPartyDomain
.AppendLiteral("]");
62 mFirstPartyDomain
= NS_ConvertUTF8toUTF16(ipAddr
);
68 // Saving isInsufficientDomainLevels before rv is overwritten.
69 bool isInsufficientDomainLevels
= (rv
== NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
);
72 rv
= aURI
->GetScheme(scheme
);
73 NS_ENSURE_SUCCESS_VOID(rv
);
74 if (scheme
.EqualsLiteral("about")) {
75 mFirstPartyDomain
.AssignLiteral(ABOUT_URI_FIRST_PARTY_DOMAIN
);
80 // Add-on principals should never get any first-party domain
81 // attributes in order to guarantee their storage integrity when switching
83 if (scheme
.EqualsLiteral("moz-extension")) {
87 nsCOMPtr
<nsIPrincipal
> blobPrincipal
;
88 if (aURI
&& dom::BlobURLProtocolHandler::GetBlobURLPrincipal(
89 aURI
, getter_AddRefs(blobPrincipal
))) {
90 MOZ_ASSERT(blobPrincipal
);
91 mFirstPartyDomain
= blobPrincipal
->OriginAttributesRef().mFirstPartyDomain
;
95 if (isInsufficientDomainLevels
) {
96 nsAutoCString publicSuffix
;
97 rv
= tldService
->GetPublicSuffix(aURI
, publicSuffix
);
98 if (NS_SUCCEEDED(rv
)) {
99 mFirstPartyDomain
= NS_ConvertUTF8toUTF16(publicSuffix
);
105 void OriginAttributes::SetFirstPartyDomain(const bool aIsTopLevelDocument
,
106 const nsACString
& aDomain
) {
107 SetFirstPartyDomain(aIsTopLevelDocument
, NS_ConvertUTF8toUTF16(aDomain
));
110 void OriginAttributes::SetFirstPartyDomain(const bool aIsTopLevelDocument
,
111 const nsAString
& aDomain
) {
112 bool isFirstPartyEnabled
= IsFirstPartyEnabled();
114 // If the pref is off or this is not a top level load, bail out.
115 if (!isFirstPartyEnabled
|| !aIsTopLevelDocument
) {
119 mFirstPartyDomain
= aDomain
;
122 void OriginAttributes::CreateSuffix(nsACString
& aStr
) const {
127 // Important: While serializing any string-valued attributes, perform a
128 // release-mode assertion to make sure that they don't contain characters that
129 // will break the quota manager when it uses the serialization for file
133 if (mInIsolatedMozBrowser
) {
134 params
.Set(NS_LITERAL_STRING("inBrowser"), NS_LITERAL_STRING("1"));
137 if (mUserContextId
!= nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID
) {
139 value
.AppendInt(mUserContextId
);
140 params
.Set(NS_LITERAL_STRING("userContextId"), value
);
143 if (mPrivateBrowsingId
) {
145 value
.AppendInt(mPrivateBrowsingId
);
146 params
.Set(NS_LITERAL_STRING("privateBrowsingId"), value
);
149 if (!mFirstPartyDomain
.IsEmpty()) {
150 nsAutoString
sanitizedFirstPartyDomain(mFirstPartyDomain
);
151 sanitizedFirstPartyDomain
.ReplaceChar(kSourceChar
, kSanitizedChar
);
153 params
.Set(NS_LITERAL_STRING("firstPartyDomain"),
154 sanitizedFirstPartyDomain
);
157 if (!mGeckoViewSessionContextId
.IsEmpty()) {
158 nsAutoString
sanitizedGeckoViewUserContextId(mGeckoViewSessionContextId
);
159 sanitizedGeckoViewUserContextId
.ReplaceChar(
160 dom::quota::QuotaManager::kReplaceChars
, kSanitizedChar
);
162 params
.Set(NS_LITERAL_STRING("geckoViewUserContextId"),
163 sanitizedGeckoViewUserContextId
);
168 params
.Serialize(value
);
169 if (!value
.IsEmpty()) {
170 aStr
.AppendLiteral("^");
171 aStr
.Append(NS_ConvertUTF16toUTF8(value
));
174 // In debug builds, check the whole string for illegal characters too (just in
179 MOZ_ASSERT(str
.FindCharInSet(dom::quota::QuotaManager::kReplaceChars
) ==
184 void OriginAttributes::CreateAnonymizedSuffix(nsACString
& aStr
) const {
185 OriginAttributes attrs
= *this;
187 if (!attrs
.mFirstPartyDomain
.IsEmpty()) {
188 attrs
.mFirstPartyDomain
.AssignLiteral("_anonymizedFirstPartyDomain_");
191 attrs
.CreateSuffix(aStr
);
196 class MOZ_STACK_CLASS PopulateFromSuffixIterator final
197 : public URLParams::ForEachIterator
{
199 explicit PopulateFromSuffixIterator(OriginAttributes
* aOriginAttributes
)
200 : mOriginAttributes(aOriginAttributes
) {
201 MOZ_ASSERT(aOriginAttributes
);
202 // If a non-default mPrivateBrowsingId is passed and is not present in the
203 // suffix, then it will retain the id when it should be default according
204 // to the suffix. Set to default before iterating to fix this.
205 mOriginAttributes
->mPrivateBrowsingId
=
206 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID
;
209 bool URLParamsIterator(const nsAString
& aName
,
210 const nsAString
& aValue
) override
{
211 if (aName
.EqualsLiteral("inBrowser")) {
212 if (!aValue
.EqualsLiteral("1")) {
216 mOriginAttributes
->mInIsolatedMozBrowser
= true;
220 if (aName
.EqualsLiteral("addonId") || aName
.EqualsLiteral("appId")) {
221 // No longer supported. Silently ignore so that legacy origin strings
222 // don't cause failures.
226 if (aName
.EqualsLiteral("userContextId")) {
228 int64_t val
= aValue
.ToInteger64(&rv
);
229 NS_ENSURE_SUCCESS(rv
, false);
230 NS_ENSURE_TRUE(val
<= UINT32_MAX
, false);
231 mOriginAttributes
->mUserContextId
= static_cast<uint32_t>(val
);
236 if (aName
.EqualsLiteral("privateBrowsingId")) {
238 int64_t val
= aValue
.ToInteger64(&rv
);
239 NS_ENSURE_SUCCESS(rv
, false);
240 NS_ENSURE_TRUE(val
>= 0 && val
<= UINT32_MAX
, false);
241 mOriginAttributes
->mPrivateBrowsingId
= static_cast<uint32_t>(val
);
246 if (aName
.EqualsLiteral("firstPartyDomain")) {
247 MOZ_RELEASE_ASSERT(mOriginAttributes
->mFirstPartyDomain
.IsEmpty());
248 nsAutoString
firstPartyDomain(aValue
);
249 firstPartyDomain
.ReplaceChar(kSanitizedChar
, kSourceChar
);
250 mOriginAttributes
->mFirstPartyDomain
.Assign(firstPartyDomain
);
254 if (aName
.EqualsLiteral("geckoViewUserContextId")) {
256 mOriginAttributes
->mGeckoViewSessionContextId
.IsEmpty());
257 mOriginAttributes
->mGeckoViewSessionContextId
.Assign(aValue
);
261 // No other attributes are supported.
266 OriginAttributes
* mOriginAttributes
;
271 bool OriginAttributes::PopulateFromSuffix(const nsACString
& aStr
) {
272 if (aStr
.IsEmpty()) {
276 if (aStr
[0] != '^') {
280 PopulateFromSuffixIterator
iterator(this);
281 return URLParams::Parse(Substring(aStr
, 1, aStr
.Length() - 1), iterator
);
284 bool OriginAttributes::PopulateFromOrigin(const nsACString
& aOrigin
,
285 nsACString
& aOriginNoSuffix
) {
286 // RFindChar is only available on nsCString.
287 nsCString
origin(aOrigin
);
288 int32_t pos
= origin
.RFindChar('^');
290 if (pos
== kNotFound
) {
291 aOriginNoSuffix
= origin
;
295 aOriginNoSuffix
= Substring(origin
, 0, pos
);
296 return PopulateFromSuffix(Substring(origin
, pos
));
299 void OriginAttributes::SyncAttributesWithPrivateBrowsing(
300 bool aInPrivateBrowsing
) {
301 mPrivateBrowsingId
= aInPrivateBrowsing
? 1 : 0;
305 bool OriginAttributes::IsPrivateBrowsing(const nsACString
& aOrigin
) {
307 OriginAttributes attrs
;
308 if (NS_WARN_IF(!attrs
.PopulateFromOrigin(aOrigin
, dummy
))) {
312 return !!attrs
.mPrivateBrowsingId
;
315 } // namespace mozilla