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"
20 bool OriginAttributes::sFirstPartyIsolation
= false;
21 bool OriginAttributes::sRestrictedOpenerAccess
= false;
22 bool OriginAttributes::sBlockPostMessageForFPI
= false;
24 void OriginAttributes::InitPrefs() {
25 MOZ_ASSERT(NS_IsMainThread());
26 static bool sInited
= false;
29 Preferences::AddBoolVarCache(&sFirstPartyIsolation
,
30 "privacy.firstparty.isolate");
31 Preferences::AddBoolVarCache(
32 &sRestrictedOpenerAccess
,
33 "privacy.firstparty.isolate.restrict_opener_access");
34 Preferences::AddBoolVarCache(
35 &sBlockPostMessageForFPI
,
36 "privacy.firstparty.isolate.block_post_message");
40 void OriginAttributes::SetFirstPartyDomain(const bool aIsTopLevelDocument
,
41 nsIURI
* aURI
, bool aForced
) {
42 bool isFirstPartyEnabled
= IsFirstPartyEnabled();
44 // If the prefs are off or this is not a top level load, bail out.
45 if ((!isFirstPartyEnabled
|| !aIsTopLevelDocument
) && !aForced
) {
49 nsCOMPtr
<nsIEffectiveTLDService
> tldService
=
50 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID
);
51 MOZ_ASSERT(tldService
);
56 nsAutoCString baseDomain
;
57 nsresult rv
= tldService
->GetBaseDomain(aURI
, 0, baseDomain
);
58 if (NS_SUCCEEDED(rv
)) {
59 mFirstPartyDomain
= NS_ConvertUTF8toUTF16(baseDomain
);
63 if (rv
== NS_ERROR_HOST_IS_IP_ADDRESS
) {
64 // If the host is an IPv4/IPv6 address, we still accept it as a
65 // valid firstPartyDomain.
67 rv
= aURI
->GetHost(ipAddr
);
68 NS_ENSURE_SUCCESS_VOID(rv
);
70 if (net_IsValidIPv6Addr(ipAddr
)) {
71 // According to RFC2732, the host of an IPv6 address should be an
72 // IPv6reference. The GetHost() of nsIURI will only return the IPv6
73 // address. So, we need to convert it back to IPv6reference here.
74 mFirstPartyDomain
.Truncate();
75 mFirstPartyDomain
.AssignLiteral("[");
76 mFirstPartyDomain
.Append(NS_ConvertUTF8toUTF16(ipAddr
));
77 mFirstPartyDomain
.AppendLiteral("]");
79 mFirstPartyDomain
= NS_ConvertUTF8toUTF16(ipAddr
);
85 // Saving isInsufficientDomainLevels before rv is overwritten.
86 bool isInsufficientDomainLevels
= (rv
== NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
);
88 rv
= aURI
->GetScheme(scheme
);
89 NS_ENSURE_SUCCESS_VOID(rv
);
90 if (scheme
.EqualsLiteral("about")) {
91 mFirstPartyDomain
.AssignLiteral(ABOUT_URI_FIRST_PARTY_DOMAIN
);
95 nsCOMPtr
<nsIPrincipal
> blobPrincipal
;
96 if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal(
97 aURI
, getter_AddRefs(blobPrincipal
))) {
98 MOZ_ASSERT(blobPrincipal
);
99 mFirstPartyDomain
= blobPrincipal
->OriginAttributesRef().mFirstPartyDomain
;
103 if (isInsufficientDomainLevels
) {
104 nsAutoCString publicSuffix
;
105 rv
= tldService
->GetPublicSuffix(aURI
, publicSuffix
);
106 if (NS_SUCCEEDED(rv
)) {
107 mFirstPartyDomain
= NS_ConvertUTF8toUTF16(publicSuffix
);
113 void OriginAttributes::SetFirstPartyDomain(const bool aIsTopLevelDocument
,
114 const nsACString
& aDomain
) {
115 bool isFirstPartyEnabled
= IsFirstPartyEnabled();
117 // If the pref is off or this is not a top level load, bail out.
118 if (!isFirstPartyEnabled
|| !aIsTopLevelDocument
) {
122 mFirstPartyDomain
= NS_ConvertUTF8toUTF16(aDomain
);
125 void OriginAttributes::CreateSuffix(nsACString
& aStr
) const {
130 // Important: While serializing any string-valued attributes, perform a
131 // release-mode assertion to make sure that they don't contain characters that
132 // will break the quota manager when it uses the serialization for file
136 if (mInIsolatedMozBrowser
) {
137 params
.Set(NS_LITERAL_STRING("inBrowser"), NS_LITERAL_STRING("1"));
140 if (mUserContextId
!= nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID
) {
142 value
.AppendInt(mUserContextId
);
143 params
.Set(NS_LITERAL_STRING("userContextId"), value
);
146 if (mPrivateBrowsingId
) {
148 value
.AppendInt(mPrivateBrowsingId
);
149 params
.Set(NS_LITERAL_STRING("privateBrowsingId"), value
);
152 if (!mFirstPartyDomain
.IsEmpty()) {
153 nsAutoString
sanitizedFirstPartyDomain(mFirstPartyDomain
);
154 sanitizedFirstPartyDomain
.ReplaceChar(
155 dom::quota::QuotaManager::kReplaceChars
, '+');
157 params
.Set(NS_LITERAL_STRING("firstPartyDomain"),
158 sanitizedFirstPartyDomain
);
163 params
.Serialize(value
);
164 if (!value
.IsEmpty()) {
165 aStr
.AppendLiteral("^");
166 aStr
.Append(NS_ConvertUTF16toUTF8(value
));
169 // In debug builds, check the whole string for illegal characters too (just in
174 MOZ_ASSERT(str
.FindCharInSet(dom::quota::QuotaManager::kReplaceChars
) ==
179 void OriginAttributes::CreateAnonymizedSuffix(nsACString
& aStr
) const {
180 OriginAttributes attrs
= *this;
182 if (!attrs
.mFirstPartyDomain
.IsEmpty()) {
183 attrs
.mFirstPartyDomain
.AssignLiteral("_anonymizedFirstPartyDomain_");
186 attrs
.CreateSuffix(aStr
);
191 class MOZ_STACK_CLASS PopulateFromSuffixIterator final
192 : public URLParams::ForEachIterator
{
194 explicit PopulateFromSuffixIterator(OriginAttributes
* aOriginAttributes
)
195 : mOriginAttributes(aOriginAttributes
) {
196 MOZ_ASSERT(aOriginAttributes
);
197 // If mPrivateBrowsingId is passed in as >0 and is not present in the
198 // suffix, then it will remain >0 when it should be 0 according to the
199 // suffix. Set to 0 before iterating to fix this.
200 mOriginAttributes
->mPrivateBrowsingId
= 0;
203 bool URLParamsIterator(const nsAString
& aName
,
204 const nsAString
& aValue
) override
{
205 if (aName
.EqualsLiteral("inBrowser")) {
206 if (!aValue
.EqualsLiteral("1")) {
210 mOriginAttributes
->mInIsolatedMozBrowser
= true;
214 if (aName
.EqualsLiteral("addonId") || aName
.EqualsLiteral("appId")) {
215 // No longer supported. Silently ignore so that legacy origin strings
216 // don't cause failures.
220 if (aName
.EqualsLiteral("userContextId")) {
222 int64_t val
= aValue
.ToInteger64(&rv
);
223 NS_ENSURE_SUCCESS(rv
, false);
224 NS_ENSURE_TRUE(val
<= UINT32_MAX
, false);
225 mOriginAttributes
->mUserContextId
= static_cast<uint32_t>(val
);
230 if (aName
.EqualsLiteral("privateBrowsingId")) {
232 int64_t val
= aValue
.ToInteger64(&rv
);
233 NS_ENSURE_SUCCESS(rv
, false);
234 NS_ENSURE_TRUE(val
>= 0 && val
<= UINT32_MAX
, false);
235 mOriginAttributes
->mPrivateBrowsingId
= static_cast<uint32_t>(val
);
240 if (aName
.EqualsLiteral("firstPartyDomain")) {
241 MOZ_RELEASE_ASSERT(mOriginAttributes
->mFirstPartyDomain
.IsEmpty());
242 mOriginAttributes
->mFirstPartyDomain
.Assign(aValue
);
246 // No other attributes are supported.
251 OriginAttributes
* mOriginAttributes
;
256 bool OriginAttributes::PopulateFromSuffix(const nsACString
& aStr
) {
257 if (aStr
.IsEmpty()) {
261 if (aStr
[0] != '^') {
265 PopulateFromSuffixIterator
iterator(this);
266 return URLParams::Parse(Substring(aStr
, 1, aStr
.Length() - 1), iterator
);
269 bool OriginAttributes::PopulateFromOrigin(const nsACString
& aOrigin
,
270 nsACString
& aOriginNoSuffix
) {
271 // RFindChar is only available on nsCString.
272 nsCString
origin(aOrigin
);
273 int32_t pos
= origin
.RFindChar('^');
275 if (pos
== kNotFound
) {
276 aOriginNoSuffix
= origin
;
280 aOriginNoSuffix
= Substring(origin
, 0, pos
);
281 return PopulateFromSuffix(Substring(origin
, pos
));
284 void OriginAttributes::SyncAttributesWithPrivateBrowsing(
285 bool aInPrivateBrowsing
) {
286 mPrivateBrowsingId
= aInPrivateBrowsing
? 1 : 0;
290 bool OriginAttributes::IsPrivateBrowsing(const nsACString
& aOrigin
) {
292 OriginAttributes attrs
;
293 if (NS_WARN_IF(!attrs
.PopulateFromOrigin(aOrigin
, dummy
))) {
297 return !!attrs
.mPrivateBrowsingId
;
300 } // namespace mozilla