forked from mirrors/gecko-dev
Given that we only support samesite lax/strict/none in our storage schema, it's useful to introduce a default value, as required by the spec. However, that would it hard to distinguish between none/lax when we switch the default. So, instead of doing that we use the peculiarities of our current schema to our advantage: There's a "sameSite" attribute and a "rawSameSite" attribute, where the latter is the literal value we received from the server. With this patch, we'll interpret the "sameSite" attribute based on the laxByDefault pref. This also has the advantage that various front-end code (e.g., in DevTools) is always reading the "sameSite" value of nsICookies. Differential Revision: https://phabricator.services.mozilla.com/D137460
648 lines
21 KiB
C++
648 lines
21 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "Cookie.h"
|
|
#include "CookieCommons.h"
|
|
#include "CookieLogging.h"
|
|
#include "CookieService.h"
|
|
#include "mozilla/net/CookieServiceChild.h"
|
|
#include "mozilla/net/NeckoChannelParams.h"
|
|
#include "mozilla/LoadInfo.h"
|
|
#include "mozilla/BasePrincipal.h"
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "mozilla/dom/ContentChild.h"
|
|
#include "mozilla/dom/Document.h"
|
|
#include "mozilla/ipc/URIUtils.h"
|
|
#include "mozilla/net/NeckoChild.h"
|
|
#include "mozilla/StaticPrefs_network.h"
|
|
#include "mozilla/StoragePrincipalHelper.h"
|
|
#include "nsNetCID.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsICookieJarSettings.h"
|
|
#include "nsIChannel.h"
|
|
#include "nsIClassifiedChannel.h"
|
|
#include "nsIHttpChannel.h"
|
|
#include "nsIEffectiveTLDService.h"
|
|
#include "nsIURI.h"
|
|
#include "nsIPrefBranch.h"
|
|
#include "nsIWebProgressListener.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "mozilla/TimeStamp.h"
|
|
#include "ThirdPartyUtil.h"
|
|
#include "nsIConsoleReportCollector.h"
|
|
|
|
using namespace mozilla::ipc;
|
|
|
|
namespace mozilla {
|
|
namespace net {
|
|
|
|
// Pref string constants
|
|
static const char kCookieMoveIntervalSecs[] =
|
|
"network.cookie.move.interval_sec";
|
|
|
|
static StaticRefPtr<CookieServiceChild> gCookieChildService;
|
|
static uint32_t gMoveCookiesIntervalSeconds = 10;
|
|
|
|
already_AddRefed<CookieServiceChild> CookieServiceChild::GetSingleton() {
|
|
if (!gCookieChildService) {
|
|
gCookieChildService = new CookieServiceChild();
|
|
ClearOnShutdown(&gCookieChildService);
|
|
}
|
|
|
|
return do_AddRef(gCookieChildService);
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(CookieServiceChild, nsICookieService, nsIObserver,
|
|
nsITimerCallback, nsINamed, nsISupportsWeakReference)
|
|
|
|
CookieServiceChild::CookieServiceChild() {
|
|
NS_ASSERTION(IsNeckoChild(), "not a child process");
|
|
|
|
auto* cc = static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager());
|
|
if (cc->IsShuttingDown()) {
|
|
return;
|
|
}
|
|
|
|
// This corresponds to Release() in DeallocPCookieService.
|
|
NS_ADDREF_THIS();
|
|
|
|
NeckoChild::InitNeckoChild();
|
|
|
|
// Create a child PCookieService actor.
|
|
gNeckoChild->SendPCookieServiceConstructor(this);
|
|
|
|
mThirdPartyUtil = ThirdPartyUtil::GetInstance();
|
|
NS_ASSERTION(mThirdPartyUtil, "couldn't get ThirdPartyUtil service");
|
|
|
|
mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
|
|
NS_ASSERTION(mTLDService, "couldn't get TLDService");
|
|
|
|
// Init our prefs and observer.
|
|
nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
|
NS_WARNING_ASSERTION(prefBranch, "no prefservice");
|
|
if (prefBranch) {
|
|
prefBranch->AddObserver(kCookieMoveIntervalSecs, this, true);
|
|
PrefChanged(prefBranch);
|
|
}
|
|
|
|
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
|
|
if (observerService) {
|
|
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
|
}
|
|
}
|
|
|
|
void CookieServiceChild::MoveCookies() {
|
|
TimeStamp start = TimeStamp::Now();
|
|
for (const auto& cookiesList : mCookiesMap.Values()) {
|
|
CookiesList newCookiesList;
|
|
for (uint32_t i = 0; i < cookiesList->Length(); ++i) {
|
|
Cookie* cookie = cookiesList->ElementAt(i);
|
|
RefPtr<Cookie> newCookie = cookie->Clone();
|
|
newCookiesList.AppendElement(newCookie);
|
|
}
|
|
*cookiesList = std::move(newCookiesList);
|
|
}
|
|
|
|
Telemetry::AccumulateTimeDelta(Telemetry::COOKIE_TIME_MOVING_MS, start);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CookieServiceChild::Notify(nsITimer* aTimer) {
|
|
if (aTimer == mCookieTimer) {
|
|
MoveCookies();
|
|
} else {
|
|
MOZ_CRASH("Unknown timer");
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CookieServiceChild::GetName(nsACString& aName) {
|
|
aName.AssignLiteral("CookieServiceChild");
|
|
return NS_OK;
|
|
}
|
|
|
|
CookieServiceChild::~CookieServiceChild() { gCookieChildService = nullptr; }
|
|
|
|
void CookieServiceChild::TrackCookieLoad(nsIChannel* aChannel) {
|
|
if (!CanSend()) {
|
|
return;
|
|
}
|
|
|
|
uint32_t rejectedReason = 0;
|
|
ThirdPartyAnalysisResult result = mThirdPartyUtil->AnalyzeChannel(
|
|
aChannel, true, nullptr, RequireThirdPartyCheck, &rejectedReason);
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aChannel->GetURI(getter_AddRefs(uri));
|
|
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
|
|
|
|
OriginAttributes attrs = loadInfo->GetOriginAttributes();
|
|
StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes(
|
|
aChannel, attrs);
|
|
|
|
bool isSafeTopLevelNav = CookieCommons::IsSafeTopLevelNav(aChannel);
|
|
bool isSameSiteForeign = CookieCommons::IsSameSiteForeign(aChannel, uri);
|
|
SendPrepareCookieList(
|
|
uri, result.contains(ThirdPartyAnalysis::IsForeign),
|
|
result.contains(ThirdPartyAnalysis::IsThirdPartyTrackingResource),
|
|
result.contains(ThirdPartyAnalysis::IsThirdPartySocialTrackingResource),
|
|
result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted),
|
|
rejectedReason, isSafeTopLevelNav, isSameSiteForeign, attrs);
|
|
}
|
|
|
|
IPCResult CookieServiceChild::RecvRemoveAll() {
|
|
mCookiesMap.Clear();
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult CookieServiceChild::RecvRemoveCookie(const CookieStruct& aCookie,
|
|
const OriginAttributes& aAttrs) {
|
|
nsCString baseDomain;
|
|
CookieCommons::GetBaseDomainFromHost(mTLDService, aCookie.host(), baseDomain);
|
|
CookieKey key(baseDomain, aAttrs);
|
|
CookiesList* cookiesList = nullptr;
|
|
mCookiesMap.Get(key, &cookiesList);
|
|
|
|
if (!cookiesList) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
for (uint32_t i = 0; i < cookiesList->Length(); i++) {
|
|
Cookie* cookie = cookiesList->ElementAt(i);
|
|
if (cookie->Name().Equals(aCookie.name()) &&
|
|
cookie->Host().Equals(aCookie.host()) &&
|
|
cookie->Path().Equals(aCookie.path())) {
|
|
cookiesList->RemoveElementAt(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult CookieServiceChild::RecvAddCookie(const CookieStruct& aCookie,
|
|
const OriginAttributes& aAttrs) {
|
|
RefPtr<Cookie> cookie = Cookie::Create(aCookie, aAttrs);
|
|
RecordDocumentCookie(cookie, aAttrs);
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult CookieServiceChild::RecvRemoveBatchDeletedCookies(
|
|
nsTArray<CookieStruct>&& aCookiesList,
|
|
nsTArray<OriginAttributes>&& aAttrsList) {
|
|
MOZ_ASSERT(aCookiesList.Length() == aAttrsList.Length());
|
|
for (uint32_t i = 0; i < aCookiesList.Length(); i++) {
|
|
CookieStruct cookieStruct = aCookiesList.ElementAt(i);
|
|
RecvRemoveCookie(cookieStruct, aAttrsList.ElementAt(i));
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult CookieServiceChild::RecvTrackCookiesLoad(
|
|
nsTArray<CookieStruct>&& aCookiesList, const OriginAttributes& aAttrs) {
|
|
for (uint32_t i = 0; i < aCookiesList.Length(); i++) {
|
|
RefPtr<Cookie> cookie = Cookie::Create(aCookiesList[i], aAttrs);
|
|
cookie->SetIsHttpOnly(false);
|
|
RecordDocumentCookie(cookie, aAttrs);
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
void CookieServiceChild::PrefChanged(nsIPrefBranch* aPrefBranch) {
|
|
int32_t val;
|
|
if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookieMoveIntervalSecs, &val))) {
|
|
gMoveCookiesIntervalSeconds = clamped<uint32_t>(val, 0, 3600);
|
|
if (gMoveCookiesIntervalSeconds && !mCookieTimer) {
|
|
NS_NewTimerWithCallback(getter_AddRefs(mCookieTimer), this,
|
|
gMoveCookiesIntervalSeconds * 1000,
|
|
nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY);
|
|
}
|
|
if (!gMoveCookiesIntervalSeconds && mCookieTimer) {
|
|
mCookieTimer->Cancel();
|
|
mCookieTimer = nullptr;
|
|
}
|
|
if (mCookieTimer) {
|
|
mCookieTimer->SetDelay(gMoveCookiesIntervalSeconds * 1000);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t CookieServiceChild::CountCookiesFromHashTable(
|
|
const nsACString& aBaseDomain, const OriginAttributes& aOriginAttrs) {
|
|
CookiesList* cookiesList = nullptr;
|
|
|
|
nsCString baseDomain;
|
|
CookieKey key(aBaseDomain, aOriginAttrs);
|
|
mCookiesMap.Get(key, &cookiesList);
|
|
|
|
return cookiesList ? cookiesList->Length() : 0;
|
|
}
|
|
|
|
/* static */ bool CookieServiceChild::RequireThirdPartyCheck(
|
|
nsILoadInfo* aLoadInfo) {
|
|
if (!aLoadInfo) {
|
|
return false;
|
|
}
|
|
|
|
nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
|
|
nsresult rv =
|
|
aLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t cookieBehavior = cookieJarSettings->GetCookieBehavior();
|
|
return cookieBehavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
|
|
cookieBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN ||
|
|
cookieBehavior == nsICookieService::BEHAVIOR_REJECT_TRACKER ||
|
|
cookieBehavior ==
|
|
nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN ||
|
|
StaticPrefs::network_cookie_thirdparty_sessionOnly() ||
|
|
StaticPrefs::network_cookie_thirdparty_nonsecureSessionOnly();
|
|
}
|
|
|
|
void CookieServiceChild::RecordDocumentCookie(Cookie* aCookie,
|
|
const OriginAttributes& aAttrs) {
|
|
nsAutoCString baseDomain;
|
|
CookieCommons::GetBaseDomainFromHost(mTLDService, aCookie->Host(),
|
|
baseDomain);
|
|
|
|
CookieKey key(baseDomain, aAttrs);
|
|
CookiesList* cookiesList = nullptr;
|
|
mCookiesMap.Get(key, &cookiesList);
|
|
|
|
if (!cookiesList) {
|
|
cookiesList = mCookiesMap.GetOrInsertNew(key);
|
|
}
|
|
for (uint32_t i = 0; i < cookiesList->Length(); i++) {
|
|
Cookie* cookie = cookiesList->ElementAt(i);
|
|
if (cookie->Name().Equals(aCookie->Name()) &&
|
|
cookie->Host().Equals(aCookie->Host()) &&
|
|
cookie->Path().Equals(aCookie->Path())) {
|
|
if (cookie->Value().Equals(aCookie->Value()) &&
|
|
cookie->Expiry() == aCookie->Expiry() &&
|
|
cookie->IsSecure() == aCookie->IsSecure() &&
|
|
cookie->SameSite() == aCookie->SameSite() &&
|
|
cookie->RawSameSite() == aCookie->RawSameSite() &&
|
|
cookie->IsSession() == aCookie->IsSession() &&
|
|
cookie->IsHttpOnly() == aCookie->IsHttpOnly()) {
|
|
cookie->SetLastAccessed(aCookie->LastAccessed());
|
|
return;
|
|
}
|
|
cookiesList->RemoveElementAt(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int64_t currentTime = PR_Now() / PR_USEC_PER_SEC;
|
|
if (aCookie->Expiry() <= currentTime) {
|
|
return;
|
|
}
|
|
|
|
cookiesList->AppendElement(aCookie);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CookieServiceChild::Observe(nsISupports* aSubject, const char* aTopic,
|
|
const char16_t* /*aData*/) {
|
|
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
|
if (mCookieTimer) {
|
|
mCookieTimer->Cancel();
|
|
mCookieTimer = nullptr;
|
|
}
|
|
nsCOMPtr<nsIObserverService> observerService =
|
|
services::GetObserverService();
|
|
if (observerService) {
|
|
observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
|
}
|
|
} else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
|
nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
|
|
if (prefBranch) {
|
|
PrefChanged(prefBranch);
|
|
}
|
|
} else {
|
|
MOZ_ASSERT(false, "unexpected topic!");
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CookieServiceChild::GetCookieStringFromDocument(dom::Document* aDocument,
|
|
nsACString& aCookieString) {
|
|
NS_ENSURE_ARG(aDocument);
|
|
|
|
aCookieString.Truncate();
|
|
|
|
nsCOMPtr<nsIPrincipal> principal = aDocument->EffectiveStoragePrincipal();
|
|
|
|
if (!CookieCommons::IsSchemeSupported(principal)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsAutoCString baseDomain;
|
|
nsresult rv = CookieCommons::GetBaseDomain(principal, baseDomain);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return NS_OK;
|
|
}
|
|
|
|
CookieKey key(baseDomain, principal->OriginAttributesRef());
|
|
CookiesList* cookiesList = nullptr;
|
|
mCookiesMap.Get(key, &cookiesList);
|
|
|
|
if (!cookiesList) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsAutoCString hostFromURI;
|
|
rv = nsContentUtils::GetHostOrIPv6WithBrackets(principal, hostFromURI);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsAutoCString pathFromURI;
|
|
principal->GetFilePath(pathFromURI);
|
|
|
|
bool thirdParty = true;
|
|
nsPIDOMWindowInner* innerWindow = aDocument->GetInnerWindow();
|
|
// in gtests we don't have a window, let's consider those requests as 3rd
|
|
// party.
|
|
if (innerWindow) {
|
|
ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
|
|
|
|
if (thirdPartyUtil) {
|
|
Unused << thirdPartyUtil->IsThirdPartyWindow(
|
|
innerWindow->GetOuterWindow(), nullptr, &thirdParty);
|
|
}
|
|
}
|
|
|
|
bool isPotentiallyTrustworthy =
|
|
principal->GetIsOriginPotentiallyTrustworthy();
|
|
int64_t currentTimeInUsec = PR_Now();
|
|
int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
|
|
|
|
cookiesList->Sort(CompareCookiesForSending());
|
|
for (uint32_t i = 0; i < cookiesList->Length(); i++) {
|
|
Cookie* cookie = cookiesList->ElementAt(i);
|
|
// check the host, since the base domain lookup is conservative.
|
|
if (!CookieCommons::DomainMatches(cookie, hostFromURI)) {
|
|
continue;
|
|
}
|
|
|
|
// We don't show HttpOnly cookies in content processes.
|
|
if (cookie->IsHttpOnly()) {
|
|
continue;
|
|
}
|
|
|
|
if (thirdParty &&
|
|
!CookieCommons::ShouldIncludeCrossSiteCookieForDocument(cookie)) {
|
|
continue;
|
|
}
|
|
|
|
// if the cookie is secure and the host scheme isn't, we can't send it
|
|
if (cookie->IsSecure() && !isPotentiallyTrustworthy) {
|
|
continue;
|
|
}
|
|
|
|
// if the nsIURI path doesn't match the cookie path, don't send it back
|
|
if (!CookieCommons::PathMatches(cookie, pathFromURI)) {
|
|
continue;
|
|
}
|
|
|
|
// check if the cookie has expired
|
|
if (cookie->Expiry() <= currentTime) {
|
|
continue;
|
|
}
|
|
|
|
if (!cookie->Name().IsEmpty() || !cookie->Value().IsEmpty()) {
|
|
if (!aCookieString.IsEmpty()) {
|
|
aCookieString.AppendLiteral("; ");
|
|
}
|
|
if (!cookie->Name().IsEmpty()) {
|
|
aCookieString.Append(cookie->Name().get());
|
|
aCookieString.AppendLiteral("=");
|
|
aCookieString.Append(cookie->Value().get());
|
|
} else {
|
|
aCookieString.Append(cookie->Value().get());
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CookieServiceChild::GetCookieStringFromHttp(nsIURI* /*aHostURI*/,
|
|
nsIChannel* /*aChannel*/,
|
|
nsACString& /*aCookieString*/) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CookieServiceChild::SetCookieStringFromDocument(
|
|
dom::Document* aDocument, const nsACString& aCookieString) {
|
|
NS_ENSURE_ARG(aDocument);
|
|
|
|
nsCOMPtr<nsIURI> documentURI;
|
|
nsAutoCString baseDomain;
|
|
OriginAttributes attrs;
|
|
|
|
// This function is executed in this context, I don't need to keep objects
|
|
// alive.
|
|
auto hasExistingCookiesLambda = [&](const nsACString& aBaseDomain,
|
|
const OriginAttributes& aAttrs) {
|
|
return !!CountCookiesFromHashTable(aBaseDomain, aAttrs);
|
|
};
|
|
|
|
RefPtr<Cookie> cookie = CookieCommons::CreateCookieFromDocument(
|
|
aDocument, aCookieString, PR_Now(), mTLDService, mThirdPartyUtil,
|
|
hasExistingCookiesLambda, getter_AddRefs(documentURI), baseDomain, attrs);
|
|
if (!cookie) {
|
|
return NS_OK;
|
|
}
|
|
|
|
bool thirdParty = true;
|
|
nsPIDOMWindowInner* innerWindow = aDocument->GetInnerWindow();
|
|
// in gtests we don't have a window, let's consider those requests as 3rd
|
|
// party.
|
|
if (innerWindow) {
|
|
ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
|
|
|
|
if (thirdPartyUtil) {
|
|
Unused << thirdPartyUtil->IsThirdPartyWindow(
|
|
innerWindow->GetOuterWindow(), nullptr, &thirdParty);
|
|
}
|
|
}
|
|
|
|
if (thirdParty &&
|
|
!CookieCommons::ShouldIncludeCrossSiteCookieForDocument(cookie)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
CookieKey key(baseDomain, attrs);
|
|
CookiesList* cookies = mCookiesMap.Get(key);
|
|
|
|
if (cookies) {
|
|
// We need to see if the cookie we're setting would overwrite an httponly
|
|
// one. This would not affect anything we send over the net (those come
|
|
// from the parent, which already checks this), but script could see an
|
|
// inconsistent view of things.
|
|
for (uint32_t i = 0; i < cookies->Length(); ++i) {
|
|
RefPtr<Cookie> existingCookie = cookies->ElementAt(i);
|
|
if (existingCookie->Name().Equals(cookie->Name()) &&
|
|
existingCookie->Host().Equals(cookie->Host()) &&
|
|
existingCookie->Path().Equals(cookie->Path()) &&
|
|
existingCookie->IsHttpOnly()) {
|
|
// Can't overwrite an httponly cookie from a script context.
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
RecordDocumentCookie(cookie, attrs);
|
|
|
|
if (CanSend()) {
|
|
nsTArray<CookieStruct> cookiesToSend;
|
|
cookiesToSend.AppendElement(cookie->ToIPC());
|
|
|
|
// Asynchronously call the parent.
|
|
SendSetCookies(baseDomain, attrs, documentURI, false, cookiesToSend);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CookieServiceChild::SetCookieStringFromHttp(nsIURI* aHostURI,
|
|
const nsACString& aCookieString,
|
|
nsIChannel* aChannel) {
|
|
NS_ENSURE_ARG(aHostURI);
|
|
NS_ENSURE_ARG(aChannel);
|
|
|
|
if (!CookieCommons::IsSchemeSupported(aHostURI)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// Fast past: don't bother sending IPC messages about nullprincipal'd
|
|
// documents.
|
|
nsAutoCString scheme;
|
|
aHostURI->GetScheme(scheme);
|
|
if (scheme.EqualsLiteral("moz-nullprincipal")) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
|
|
|
|
uint32_t rejectedReason = 0;
|
|
ThirdPartyAnalysisResult result = mThirdPartyUtil->AnalyzeChannel(
|
|
aChannel, false, aHostURI, RequireThirdPartyCheck, &rejectedReason);
|
|
|
|
nsCString cookieString(aCookieString);
|
|
|
|
OriginAttributes attrs = loadInfo->GetOriginAttributes();
|
|
StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes(
|
|
aChannel, attrs);
|
|
|
|
bool requireHostMatch;
|
|
nsCString baseDomain;
|
|
CookieCommons::GetBaseDomain(mTLDService, aHostURI, baseDomain,
|
|
requireHostMatch);
|
|
|
|
nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
|
|
CookieCommons::GetCookieJarSettings(aChannel);
|
|
|
|
nsCOMPtr<nsIConsoleReportCollector> crc = do_QueryInterface(aChannel);
|
|
|
|
CookieStatus cookieStatus = CookieService::CheckPrefs(
|
|
crc, cookieJarSettings, aHostURI,
|
|
result.contains(ThirdPartyAnalysis::IsForeign),
|
|
result.contains(ThirdPartyAnalysis::IsThirdPartyTrackingResource),
|
|
result.contains(ThirdPartyAnalysis::IsThirdPartySocialTrackingResource),
|
|
result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted),
|
|
aCookieString, CountCookiesFromHashTable(baseDomain, attrs), attrs,
|
|
&rejectedReason);
|
|
|
|
if (cookieStatus != STATUS_ACCEPTED &&
|
|
cookieStatus != STATUS_ACCEPT_SESSION) {
|
|
return NS_OK;
|
|
}
|
|
|
|
CookieKey key(baseDomain, attrs);
|
|
|
|
nsTArray<CookieStruct> cookiesToSend;
|
|
|
|
int64_t currentTimeInUsec = PR_Now();
|
|
|
|
bool addonAllowsLoad = false;
|
|
nsCOMPtr<nsIURI> finalChannelURI;
|
|
NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI));
|
|
addonAllowsLoad = BasePrincipal::Cast(loadInfo->TriggeringPrincipal())
|
|
->AddonAllowsLoad(finalChannelURI);
|
|
|
|
bool isForeignAndNotAddon = false;
|
|
if (!addonAllowsLoad) {
|
|
mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI,
|
|
&isForeignAndNotAddon);
|
|
}
|
|
|
|
bool moreCookies;
|
|
do {
|
|
CookieStruct cookieData;
|
|
bool canSetCookie = false;
|
|
moreCookies = CookieService::CanSetCookie(
|
|
aHostURI, baseDomain, cookieData, requireHostMatch, cookieStatus,
|
|
cookieString, true, isForeignAndNotAddon, crc, canSetCookie);
|
|
if (!canSetCookie) {
|
|
continue;
|
|
}
|
|
|
|
// check permissions from site permission list.
|
|
if (!CookieCommons::CheckCookiePermission(aChannel, cookieData)) {
|
|
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieString,
|
|
"cookie rejected by permission manager");
|
|
constexpr auto CONSOLE_REJECTION_CATEGORY = "cookiesRejection"_ns;
|
|
CookieLogging::LogMessageToConsole(
|
|
crc, aHostURI, nsIScriptError::warningFlag,
|
|
CONSOLE_REJECTION_CATEGORY, "CookieRejectedByPermissionManager"_ns,
|
|
AutoTArray<nsString, 1>{
|
|
NS_ConvertUTF8toUTF16(cookieData.name()),
|
|
});
|
|
CookieCommons::NotifyRejected(
|
|
aHostURI, aChannel,
|
|
nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION,
|
|
OPERATION_WRITE);
|
|
continue;
|
|
}
|
|
|
|
RefPtr<Cookie> cookie = Cookie::Create(cookieData, attrs);
|
|
MOZ_ASSERT(cookie);
|
|
|
|
cookie->SetLastAccessed(currentTimeInUsec);
|
|
cookie->SetCreationTime(
|
|
Cookie::GenerateUniqueCreationTime(currentTimeInUsec));
|
|
|
|
RecordDocumentCookie(cookie, attrs);
|
|
cookiesToSend.AppendElement(cookieData);
|
|
} while (moreCookies);
|
|
|
|
// Asynchronously call the parent.
|
|
if (CanSend() && !cookiesToSend.IsEmpty()) {
|
|
SendSetCookies(baseDomain, attrs, aHostURI, true, cookiesToSend);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CookieServiceChild::RunInTransaction(
|
|
nsICookieTransactionCallback* /*aCallback*/) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
} // namespace net
|
|
} // namespace mozilla
|