зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1475599 - part 2 - CookieStore API - IPDL, r=edgul,webidl,smaug
Differential Revision: https://phabricator.services.mozilla.com/D215145
This commit is contained in:
Родитель
c6b5fb82a2
Коммит
4b3f5d6dbb
|
@ -6551,43 +6551,28 @@ void Document::GetCookie(nsAString& aCookie, ErrorResult& aRv) {
|
||||||
aCookie.Truncate(); // clear current cookie in case service fails;
|
aCookie.Truncate(); // clear current cookie in case service fails;
|
||||||
// no cookie isn't an error condition.
|
// no cookie isn't an error condition.
|
||||||
|
|
||||||
if (mDisableCookieAccess) {
|
nsCOMPtr<nsIPrincipal> cookiePrincipal;
|
||||||
return;
|
nsCOMPtr<nsIPrincipal> cookiePartitionedPrincipal;
|
||||||
}
|
|
||||||
|
|
||||||
// If the document's sandboxed origin flag is set, then reading cookies
|
CookieCommons::SecurityChecksResult checkResult =
|
||||||
// is prohibited.
|
CookieCommons::CheckGlobalAndRetrieveCookiePrincipals(
|
||||||
if (mSandboxFlags & SANDBOXED_ORIGIN) {
|
this, getter_AddRefs(cookiePrincipal),
|
||||||
aRv.ThrowSecurityError(
|
getter_AddRefs(cookiePartitionedPrincipal));
|
||||||
"Forbidden in a sandboxed document without the 'allow-same-origin' "
|
switch (checkResult) {
|
||||||
"flag.");
|
case CookieCommons::SecurityChecksResult::eSandboxedError:
|
||||||
return;
|
aRv.ThrowSecurityError(
|
||||||
}
|
"Forbidden in a sandboxed document without the 'allow-same-origin' "
|
||||||
|
"flag.");
|
||||||
// GTests do not create an inner window and because of these a few security
|
|
||||||
// checks will block this method.
|
|
||||||
if (!StaticPrefs::dom_cookie_testing_enabled()) {
|
|
||||||
StorageAccess storageAccess = CookieAllowedForDocument(this);
|
|
||||||
if (storageAccess == StorageAccess::eDeny) {
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (ShouldPartitionStorage(storageAccess) &&
|
case CookieCommons::SecurityChecksResult::eSecurityError:
|
||||||
!StoragePartitioningEnabled(storageAccess, CookieJarSettings())) {
|
[[fallthrough]];
|
||||||
|
|
||||||
|
case CookieCommons::SecurityChecksResult::eDoNotContinue:
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// If the document is a cookie-averse Document... return the empty string.
|
case CookieCommons::SecurityChecksResult::eContinue:
|
||||||
if (IsCookieAverse()) {
|
break;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// not having a cookie service isn't an error
|
|
||||||
nsCOMPtr<nsICookieService> service =
|
|
||||||
do_GetService(NS_COOKIESERVICE_CONTRACTID);
|
|
||||||
if (!service) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool thirdParty = true;
|
bool thirdParty = true;
|
||||||
|
@ -6603,35 +6588,13 @@ void Document::GetCookie(nsAString& aCookie, ErrorResult& aRv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIPrincipal> cookiePrincipal = EffectiveCookiePrincipal();
|
|
||||||
|
|
||||||
nsTArray<nsCOMPtr<nsIPrincipal>> principals;
|
nsTArray<nsCOMPtr<nsIPrincipal>> principals;
|
||||||
|
|
||||||
|
MOZ_ASSERT(cookiePrincipal);
|
||||||
principals.AppendElement(cookiePrincipal);
|
principals.AppendElement(cookiePrincipal);
|
||||||
|
|
||||||
// CHIPS - If CHIPS is enabled the partitioned cookie jar is always available
|
if (cookiePartitionedPrincipal) {
|
||||||
// (and therefore the partitioned principal), the unpartitioned cookie jar is
|
principals.AppendElement(cookiePartitionedPrincipal);
|
||||||
// only available in first-party or third-party with storageAccess contexts.
|
|
||||||
// In both cases, the document will have storage access.
|
|
||||||
bool isCHIPS = StaticPrefs::network_cookie_CHIPS_enabled() &&
|
|
||||||
CookieJarSettings()->GetPartitionForeign();
|
|
||||||
bool documentHasStorageAccess = false;
|
|
||||||
nsresult rv = HasStorageAccessSync(documentHasStorageAccess);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isCHIPS && documentHasStorageAccess) {
|
|
||||||
// Assert that the cookie principal is unpartitioned.
|
|
||||||
MOZ_ASSERT(cookiePrincipal->OriginAttributesRef().mPartitionKey.IsEmpty());
|
|
||||||
// Only append the partitioned originAttributes if the partitionKey is set.
|
|
||||||
// The partitionKey could be empty for partitionKey in partitioned
|
|
||||||
// originAttributes if the document is for privilege context, such as the
|
|
||||||
// extension's background page.
|
|
||||||
if (!PartitionedPrincipal()
|
|
||||||
->OriginAttributesRef()
|
|
||||||
.mPartitionKey.IsEmpty()) {
|
|
||||||
principals.AppendElement(PartitionedPrincipal());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsTArray<RefPtr<Cookie>> cookieList;
|
nsTArray<RefPtr<Cookie>> cookieList;
|
||||||
|
@ -6639,13 +6602,16 @@ void Document::GetCookie(nsAString& aCookie, ErrorResult& aRv) {
|
||||||
int64_t currentTimeInUsec = PR_Now();
|
int64_t currentTimeInUsec = PR_Now();
|
||||||
int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
|
int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
|
||||||
|
|
||||||
for (auto& principal : principals) {
|
// not having a cookie service isn't an error
|
||||||
if (!CookieCommons::IsSchemeSupported(principal)) {
|
nsCOMPtr<nsICookieService> service =
|
||||||
return;
|
do_GetService(NS_COOKIESERVICE_CONTRACTID);
|
||||||
}
|
if (!service) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& principal : principals) {
|
||||||
nsAutoCString baseDomain;
|
nsAutoCString baseDomain;
|
||||||
rv = CookieCommons::GetBaseDomain(principal, baseDomain);
|
nsresult rv = CookieCommons::GetBaseDomain(principal, baseDomain);
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -6740,32 +6706,26 @@ void Document::GetCookie(nsAString& aCookie, ErrorResult& aRv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Document::SetCookie(const nsAString& aCookieString, ErrorResult& aRv) {
|
void Document::SetCookie(const nsAString& aCookieString, ErrorResult& aRv) {
|
||||||
if (mDisableCookieAccess) {
|
nsCOMPtr<nsIPrincipal> cookiePrincipal;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the document's sandboxed origin flag is set, then setting cookies
|
CookieCommons::SecurityChecksResult checkResult =
|
||||||
// is prohibited.
|
CookieCommons::CheckGlobalAndRetrieveCookiePrincipals(
|
||||||
if (mSandboxFlags & SANDBOXED_ORIGIN) {
|
this, getter_AddRefs(cookiePrincipal), nullptr);
|
||||||
aRv.ThrowSecurityError(
|
switch (checkResult) {
|
||||||
"Forbidden in a sandboxed document without the 'allow-same-origin' "
|
case CookieCommons::SecurityChecksResult::eSandboxedError:
|
||||||
"flag.");
|
aRv.ThrowSecurityError(
|
||||||
return;
|
"Forbidden in a sandboxed document without the 'allow-same-origin' "
|
||||||
}
|
"flag.");
|
||||||
|
return;
|
||||||
|
|
||||||
StorageAccess storageAccess = CookieAllowedForDocument(this);
|
case CookieCommons::SecurityChecksResult::eSecurityError:
|
||||||
if (storageAccess == StorageAccess::eDeny) {
|
[[fallthrough]];
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ShouldPartitionStorage(storageAccess) &&
|
case CookieCommons::SecurityChecksResult::eDoNotContinue:
|
||||||
!StoragePartitioningEnabled(storageAccess, CookieJarSettings())) {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the document is a cookie-averse Document... do nothing.
|
case CookieCommons::SecurityChecksResult::eContinue:
|
||||||
if (IsCookieAverse()) {
|
break;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mDocumentURI) {
|
if (!mDocumentURI) {
|
||||||
|
|
|
@ -1098,6 +1098,8 @@ class Document : public nsINode,
|
||||||
*/
|
*/
|
||||||
void DisableCookieAccess() { mDisableCookieAccess = true; }
|
void DisableCookieAccess() { mDisableCookieAccess = true; }
|
||||||
|
|
||||||
|
bool CookieAccessDisabled() const { return mDisableCookieAccess; }
|
||||||
|
|
||||||
void SetLinkHandlingEnabled(bool aValue) { mLinksEnabled = aValue; }
|
void SetLinkHandlingEnabled(bool aValue) { mLinksEnabled = aValue; }
|
||||||
bool LinkHandlingEnabled() { return mLinksEnabled; }
|
bool LinkHandlingEnabled() { return mLinksEnabled; }
|
||||||
|
|
||||||
|
|
|
@ -5932,6 +5932,10 @@ nsresult nsGlobalWindowInner::FireDelayedDOMEvents(bool aIncludeSubWindows) {
|
||||||
// Fires an offline status event if the offline status has changed
|
// Fires an offline status event if the offline status has changed
|
||||||
FireOfflineStatusEventIfChanged();
|
FireOfflineStatusEventIfChanged();
|
||||||
|
|
||||||
|
if (mCookieStore) {
|
||||||
|
mCookieStore->FireDelayedDOMEvents();
|
||||||
|
}
|
||||||
|
|
||||||
if (!aIncludeSubWindows) {
|
if (!aIncludeSubWindows) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,4 +50,30 @@ void CookieChangeEvent::GetDeleted(nsTArray<CookieListItem>& aList) const {
|
||||||
return event.forget();
|
return event.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */ already_AddRefed<CookieChangeEvent>
|
||||||
|
CookieChangeEvent::CreateForChangedCookie(EventTarget* aEventTarget,
|
||||||
|
const CookieListItem& aItem) {
|
||||||
|
RefPtr<CookieChangeEvent> event =
|
||||||
|
new CookieChangeEvent(aEventTarget, nullptr, nullptr);
|
||||||
|
|
||||||
|
event->InitEvent(u"change"_ns, false, false);
|
||||||
|
event->SetTrusted(true);
|
||||||
|
|
||||||
|
event->mChanged.AppendElement(aItem);
|
||||||
|
return event.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ already_AddRefed<CookieChangeEvent>
|
||||||
|
CookieChangeEvent::CreateForDeletedCookie(EventTarget* aEventTarget,
|
||||||
|
const CookieListItem& aItem) {
|
||||||
|
RefPtr<CookieChangeEvent> event =
|
||||||
|
new CookieChangeEvent(aEventTarget, nullptr, nullptr);
|
||||||
|
|
||||||
|
event->InitEvent(u"change"_ns, false, false);
|
||||||
|
event->SetTrusted(true);
|
||||||
|
|
||||||
|
event->mDeleted.AppendElement(aItem);
|
||||||
|
return event.forget();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
|
|
@ -29,6 +29,12 @@ class CookieChangeEvent final : public Event {
|
||||||
|
|
||||||
void GetDeleted(nsTArray<CookieListItem>& aList) const;
|
void GetDeleted(nsTArray<CookieListItem>& aList) const;
|
||||||
|
|
||||||
|
static already_AddRefed<CookieChangeEvent> CreateForChangedCookie(
|
||||||
|
EventTarget* aEventTarget, const CookieListItem& aItem);
|
||||||
|
|
||||||
|
static already_AddRefed<CookieChangeEvent> CreateForDeletedCookie(
|
||||||
|
EventTarget* aEventTarget, const CookieListItem& aItem);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~CookieChangeEvent();
|
~CookieChangeEvent();
|
||||||
|
|
||||||
|
|
|
@ -5,16 +5,200 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "CookieStore.h"
|
#include "CookieStore.h"
|
||||||
|
#include "CookieStoreChild.h"
|
||||||
|
#include "CookieStoreNotifier.h"
|
||||||
|
|
||||||
|
#include "mozilla/dom/Document.h"
|
||||||
#include "mozilla/dom/Promise.h"
|
#include "mozilla/dom/Promise.h"
|
||||||
|
#include "mozilla/dom/WorkerCommon.h"
|
||||||
|
#include "mozilla/dom/WorkerPrivate.h"
|
||||||
|
#include "mozilla/ipc/BackgroundChild.h"
|
||||||
|
#include "mozilla/ipc/PBackgroundChild.h"
|
||||||
|
#include "mozilla/net/CookieCommons.h"
|
||||||
|
#include "mozilla/StorageAccess.h"
|
||||||
|
#include "nsICookie.h"
|
||||||
#include "nsIGlobalObject.h"
|
#include "nsIGlobalObject.h"
|
||||||
|
#include "nsIPrincipal.h"
|
||||||
|
#include "nsReadableUtils.h"
|
||||||
|
#include "nsSandboxFlags.h"
|
||||||
|
|
||||||
|
using namespace mozilla::net;
|
||||||
|
|
||||||
namespace mozilla::dom {
|
namespace mozilla::dom {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
int32_t SameSiteToConst(const CookieSameSite& aSameSite) {
|
||||||
|
switch (aSameSite) {
|
||||||
|
case CookieSameSite::Strict:
|
||||||
|
return nsICookie::SAMESITE_STRICT;
|
||||||
|
case CookieSameSite::Lax:
|
||||||
|
return nsICookie::SAMESITE_LAX;
|
||||||
|
default:
|
||||||
|
MOZ_ASSERT(aSameSite == CookieSameSite::None);
|
||||||
|
return nsICookie::SAMESITE_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ValidateCookieNameOrValue(const nsAString& aStr) {
|
||||||
|
for (auto iter = aStr.BeginReading(), end = aStr.EndReading(); iter < end;
|
||||||
|
++iter) {
|
||||||
|
if (*iter == 0x3B || *iter == 0x7F || (*iter <= 0x1F && *iter != 0x09)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ValidateCookieNameAndValue(const nsAString& aName, const nsAString& aValue,
|
||||||
|
Promise* aPromise) {
|
||||||
|
MOZ_ASSERT(aPromise);
|
||||||
|
|
||||||
|
if (!ValidateCookieNameOrValue(aName)) {
|
||||||
|
aPromise->MaybeRejectWithTypeError("Cookie name contains invalid chars");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ValidateCookieNameOrValue(aValue)) {
|
||||||
|
aPromise->MaybeRejectWithTypeError("Cookie value contains invalid chars");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aName.IsEmpty() && aValue.Contains('=')) {
|
||||||
|
aPromise->MaybeRejectWithTypeError(
|
||||||
|
"Cookie value cannot contain '=' if the name is empty");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aName.IsEmpty() && aValue.IsEmpty()) {
|
||||||
|
aPromise->MaybeRejectWithTypeError(
|
||||||
|
"Cookie name and value both cannot be empty");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aName.Length() + aValue.Length() > 1024) {
|
||||||
|
aPromise->MaybeRejectWithTypeError(
|
||||||
|
"Cookie name and value size cannot be greater than 1024 bytes");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ValidateCookieDomain(const nsAString& aHost, const nsAString& aDomain,
|
||||||
|
Promise* aPromise) {
|
||||||
|
MOZ_ASSERT(aPromise);
|
||||||
|
|
||||||
|
if (aDomain.IsEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aDomain[0] == '.') {
|
||||||
|
aPromise->MaybeRejectWithTypeError("Cookie domain cannot start with '.'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aHost != aDomain) {
|
||||||
|
if ((aHost.Length() < aDomain.Length() + 1) ||
|
||||||
|
!StringEndsWith(aHost, aDomain) ||
|
||||||
|
aHost[aHost.Length() - aDomain.Length() - 1] != '.') {
|
||||||
|
aPromise->MaybeRejectWithTypeError(
|
||||||
|
"Cookie domain must domain-match current host");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aDomain.Length() > 1024) {
|
||||||
|
aPromise->MaybeRejectWithTypeError(
|
||||||
|
"Cookie domain size cannot be greater than 1024 bytes");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ValidateCookiePath(const nsAString& aPath, nsAString& retPath,
|
||||||
|
Promise* aPromise) {
|
||||||
|
MOZ_ASSERT(aPromise);
|
||||||
|
|
||||||
|
if (!aPath.IsEmpty() && aPath[0] != '/') {
|
||||||
|
aPromise->MaybeRejectWithTypeError("Cookie path must start with '/'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsString path(aPath);
|
||||||
|
if (path.IsEmpty() || path[path.Length() - 1] != '/') {
|
||||||
|
path.Append('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.Length() > 1024) {
|
||||||
|
aPromise->MaybeRejectWithTypeError(
|
||||||
|
"Cookie domain size cannot be greater than 1024 bytes");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
retPath.Assign(path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject cookies whose name starts with the magic prefixes from
|
||||||
|
// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis
|
||||||
|
// if they do not meet the criteria required by the prefix.
|
||||||
|
bool ValidateCookieNamePrefix(const nsAString& aName,
|
||||||
|
const nsAString& aOptionDomain,
|
||||||
|
const nsAString& aPath, Promise* aPromise) {
|
||||||
|
MOZ_ASSERT(aPromise);
|
||||||
|
|
||||||
|
if (!StringBeginsWith(aName, u"__Host-"_ns,
|
||||||
|
nsCaseInsensitiveStringComparator)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aOptionDomain.IsEmpty()) {
|
||||||
|
aPromise->MaybeRejectWithTypeError(
|
||||||
|
"Cookie domain cannot be used when the cookie name uses special "
|
||||||
|
"prefixes");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aPath.EqualsLiteral("/")) {
|
||||||
|
aPromise->MaybeRejectWithTypeError(
|
||||||
|
"Cookie path cannot be different than '/' when the cookie name uses "
|
||||||
|
"special prefixes");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CookieDataToItem(const CookieData& aData, CookieListItem* aItem) {
|
||||||
|
aItem->mName.Construct(aData.name());
|
||||||
|
aItem->mValue.Construct(aData.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CookieDataToList(const nsTArray<CookieData>& aData,
|
||||||
|
nsTArray<CookieListItem>& aResult) {
|
||||||
|
for (const CookieData& data : aData) {
|
||||||
|
CookieListItem* item = aResult.AppendElement();
|
||||||
|
CookieDataToItem(data, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolvePromiseAsync(Promise* aPromise) {
|
||||||
|
MOZ_ASSERT(aPromise);
|
||||||
|
|
||||||
|
NS_DispatchToCurrentThread(NS_NewRunnableFunction(
|
||||||
|
__func__,
|
||||||
|
[promise = RefPtr(aPromise)] { promise->MaybeResolveWithUndefined(); }));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_CLASS(CookieStore)
|
NS_IMPL_CYCLE_COLLECTION_CLASS(CookieStore)
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CookieStore,
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CookieStore,
|
||||||
DOMEventTargetHelper)
|
DOMEventTargetHelper)
|
||||||
|
tmp->Shutdown();
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CookieStore,
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CookieStore,
|
||||||
|
@ -33,9 +217,11 @@ already_AddRefed<CookieStore> CookieStore::Create(nsIGlobalObject* aGlobal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CookieStore::CookieStore(nsIGlobalObject* aGlobal)
|
CookieStore::CookieStore(nsIGlobalObject* aGlobal)
|
||||||
: DOMEventTargetHelper(aGlobal) {}
|
: DOMEventTargetHelper(aGlobal) {
|
||||||
|
mNotifier = CookieStoreNotifier::Create(this);
|
||||||
|
}
|
||||||
|
|
||||||
CookieStore::~CookieStore() = default;
|
CookieStore::~CookieStore() { Shutdown(); }
|
||||||
|
|
||||||
JSObject* CookieStore::WrapObject(JSContext* aCx,
|
JSObject* CookieStore::WrapObject(JSContext* aCx,
|
||||||
JS::Handle<JSObject*> aGivenProto) {
|
JS::Handle<JSObject*> aGivenProto) {
|
||||||
|
@ -44,50 +230,423 @@ JSObject* CookieStore::WrapObject(JSContext* aCx,
|
||||||
|
|
||||||
already_AddRefed<Promise> CookieStore::Get(const nsAString& aName,
|
already_AddRefed<Promise> CookieStore::Get(const nsAString& aName,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
CookieStoreGetOptions options;
|
||||||
return nullptr;
|
options.mName.Construct(aName);
|
||||||
|
return Get(options, aRv);
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<Promise> CookieStore::Get(
|
already_AddRefed<Promise> CookieStore::Get(
|
||||||
const CookieStoreGetOptions& aOptions, ErrorResult& aRv) {
|
const CookieStoreGetOptions& aOptions, ErrorResult& aRv) {
|
||||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
return GetInternal(aOptions, true, aRv);
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<Promise> CookieStore::GetAll(const nsAString& aName,
|
already_AddRefed<Promise> CookieStore::GetAll(const nsAString& aName,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
CookieStoreGetOptions options;
|
||||||
return nullptr;
|
options.mName.Construct(aName);
|
||||||
|
return GetAll(options, aRv);
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<Promise> CookieStore::GetAll(
|
already_AddRefed<Promise> CookieStore::GetAll(
|
||||||
const CookieStoreGetOptions& aOptions, ErrorResult& aRv) {
|
const CookieStoreGetOptions& aOptions, ErrorResult& aRv) {
|
||||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
return GetInternal(aOptions, false, aRv);
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<Promise> CookieStore::Set(const nsAString& aName,
|
already_AddRefed<Promise> CookieStore::Set(const nsAString& aName,
|
||||||
const nsAString& aValue,
|
const nsAString& aValue,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
CookieInit init;
|
||||||
return nullptr;
|
init.mName = aName;
|
||||||
|
init.mValue = aValue;
|
||||||
|
return Set(init, aRv);
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<Promise> CookieStore::Set(const CookieInit& aOptions,
|
already_AddRefed<Promise> CookieStore::Set(const CookieInit& aOptions,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
RefPtr<Promise> promise = Promise::Create(GetOwnerGlobal(), aRv);
|
||||||
return nullptr;
|
if (NS_WARN_IF(!promise)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIPrincipal> cookiePrincipal;
|
||||||
|
switch (CookieCommons::CheckGlobalAndRetrieveCookiePrincipals(
|
||||||
|
MaybeGetDocument(), getter_AddRefs(cookiePrincipal), nullptr)) {
|
||||||
|
case CookieCommons::SecurityChecksResult::eSandboxedError:
|
||||||
|
[[fallthrough]];
|
||||||
|
|
||||||
|
case CookieCommons::SecurityChecksResult::eSecurityError:
|
||||||
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
case CookieCommons::SecurityChecksResult::eDoNotContinue:
|
||||||
|
ResolvePromiseAsync(promise);
|
||||||
|
return promise.forget();
|
||||||
|
|
||||||
|
case CookieCommons::SecurityChecksResult::eContinue:
|
||||||
|
MOZ_ASSERT(cookiePrincipal);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_DispatchToCurrentThread(NS_NewRunnableFunction(
|
||||||
|
__func__, [self = RefPtr(this), promise = RefPtr(promise), aOptions,
|
||||||
|
cookiePrincipal = RefPtr(cookiePrincipal.get())]() {
|
||||||
|
if (!ValidateCookieNameAndValue(aOptions.mName, aOptions.mValue,
|
||||||
|
promise)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString baseDomainUtf8;
|
||||||
|
nsresult rv =
|
||||||
|
net::CookieCommons::GetBaseDomain(cookiePrincipal, baseDomainUtf8);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
promise->MaybeRejectWithNotAllowedError("Permission denied");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ConvertUTF8toUTF16 baseDomain(baseDomainUtf8);
|
||||||
|
|
||||||
|
if (!ValidateCookieDomain(baseDomain, aOptions.mDomain, promise)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsString path;
|
||||||
|
if (!ValidateCookiePath(aOptions.mPath, path, promise)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ValidateCookieNamePrefix(aOptions.mName, aOptions.mDomain, path,
|
||||||
|
promise)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->MaybeCreateActor()) {
|
||||||
|
promise->MaybeRejectWithNotAllowedError("Permission denied");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<CookieStoreChild::SetRequestPromise> ipcPromise =
|
||||||
|
self->mActor->SendSetRequest(
|
||||||
|
aOptions.mDomain.IsEmpty() ? nsString(baseDomain)
|
||||||
|
: nsString(aOptions.mDomain),
|
||||||
|
cookiePrincipal->OriginAttributesRef(),
|
||||||
|
nsString(aOptions.mName), nsString(aOptions.mValue),
|
||||||
|
// If expires is not set, it's a session cookie.
|
||||||
|
aOptions.mExpires.IsNull(),
|
||||||
|
aOptions.mExpires.IsNull()
|
||||||
|
? INT64_MAX
|
||||||
|
: static_cast<int64_t>(aOptions.mExpires.Value() / 1000),
|
||||||
|
path, SameSiteToConst(aOptions.mSameSite),
|
||||||
|
aOptions.mPartitioned);
|
||||||
|
if (NS_WARN_IF(!ipcPromise)) {
|
||||||
|
promise->MaybeResolveWithUndefined();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcPromise->Then(
|
||||||
|
NS_GetCurrentThread(), __func__,
|
||||||
|
[promise = RefPtr<dom::Promise>(promise)](
|
||||||
|
const CookieStoreChild::SetRequestPromise::ResolveOrRejectValue&
|
||||||
|
aResult) { promise->MaybeResolveWithUndefined(); });
|
||||||
|
}));
|
||||||
|
|
||||||
|
return promise.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<Promise> CookieStore::Delete(const nsAString& aName,
|
already_AddRefed<Promise> CookieStore::Delete(const nsAString& aName,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
CookieStoreDeleteOptions options;
|
||||||
return nullptr;
|
options.mName = aName;
|
||||||
|
return Delete(options, aRv);
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<Promise> CookieStore::Delete(
|
already_AddRefed<Promise> CookieStore::Delete(
|
||||||
const CookieStoreDeleteOptions& aOptions, ErrorResult& aRv) {
|
const CookieStoreDeleteOptions& aOptions, ErrorResult& aRv) {
|
||||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
RefPtr<Promise> promise = Promise::Create(GetOwnerGlobal(), aRv);
|
||||||
|
if (NS_WARN_IF(!promise)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIPrincipal> cookiePrincipal;
|
||||||
|
switch (CookieCommons::CheckGlobalAndRetrieveCookiePrincipals(
|
||||||
|
MaybeGetDocument(), getter_AddRefs(cookiePrincipal), nullptr)) {
|
||||||
|
case CookieCommons::SecurityChecksResult::eSandboxedError:
|
||||||
|
[[fallthrough]];
|
||||||
|
|
||||||
|
case CookieCommons::SecurityChecksResult::eSecurityError:
|
||||||
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
case CookieCommons::SecurityChecksResult::eDoNotContinue:
|
||||||
|
ResolvePromiseAsync(promise);
|
||||||
|
return promise.forget();
|
||||||
|
|
||||||
|
case CookieCommons::SecurityChecksResult::eContinue:
|
||||||
|
MOZ_ASSERT(cookiePrincipal);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_DispatchToCurrentThread(NS_NewRunnableFunction(
|
||||||
|
__func__, [self = RefPtr(this), promise = RefPtr(promise), aOptions,
|
||||||
|
cookiePrincipal = RefPtr(cookiePrincipal.get())]() {
|
||||||
|
nsAutoCString baseDomainUtf8;
|
||||||
|
nsresult rv =
|
||||||
|
net::CookieCommons::GetBaseDomain(cookiePrincipal, baseDomainUtf8);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
promise->MaybeRejectWithNotAllowedError("Permission denied");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ConvertUTF8toUTF16 baseDomain(baseDomainUtf8);
|
||||||
|
if (!ValidateCookieDomain(baseDomain, aOptions.mDomain, promise)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsString path;
|
||||||
|
if (!ValidateCookiePath(aOptions.mPath, path, promise)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ValidateCookieNamePrefix(aOptions.mName, aOptions.mDomain, path,
|
||||||
|
promise)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->MaybeCreateActor()) {
|
||||||
|
promise->MaybeRejectWithNotAllowedError("Permission denied");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<CookieStoreChild::DeleteRequestPromise> ipcPromise =
|
||||||
|
self->mActor->SendDeleteRequest(
|
||||||
|
aOptions.mDomain.IsEmpty() ? nsString(baseDomain)
|
||||||
|
: nsString(aOptions.mDomain),
|
||||||
|
cookiePrincipal->OriginAttributesRef(),
|
||||||
|
nsString(aOptions.mName), path, aOptions.mPartitioned);
|
||||||
|
if (NS_WARN_IF(!ipcPromise)) {
|
||||||
|
promise->MaybeResolveWithUndefined();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcPromise->Then(NS_GetCurrentThread(), __func__,
|
||||||
|
[promise = RefPtr<dom::Promise>(promise)](
|
||||||
|
const CookieStoreChild::DeleteRequestPromise::
|
||||||
|
ResolveOrRejectValue& aResult) {
|
||||||
|
MOZ_ASSERT(aResult.IsResolve());
|
||||||
|
promise->MaybeResolveWithUndefined();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
return promise.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CookieStore::Shutdown() {
|
||||||
|
if (mActor) {
|
||||||
|
mActor->Close();
|
||||||
|
mActor = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mNotifier) {
|
||||||
|
mNotifier->Disentangle();
|
||||||
|
mNotifier = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CookieStore::MaybeCreateActor() {
|
||||||
|
if (mActor) {
|
||||||
|
return mActor->CanSend();
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::PBackgroundChild* actorChild =
|
||||||
|
mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
|
||||||
|
if (NS_WARN_IF(!actorChild)) {
|
||||||
|
// The process is probably shutting down. Let's return a 'generic' error.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PCookieStoreChild* actor = actorChild->SendPCookieStoreConstructor();
|
||||||
|
if (!actor) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mActor = static_cast<CookieStoreChild*>(actor);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<Promise> CookieStore::GetInternal(
|
||||||
|
const CookieStoreGetOptions& aOptions, bool aOnlyTheFirstMatch,
|
||||||
|
ErrorResult& aRv) {
|
||||||
|
RefPtr<Promise> promise = Promise::Create(GetOwnerGlobal(), aRv);
|
||||||
|
if (NS_WARN_IF(!promise)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIPrincipal> cookiePrincipal;
|
||||||
|
switch (CookieCommons::CheckGlobalAndRetrieveCookiePrincipals(
|
||||||
|
MaybeGetDocument(), getter_AddRefs(cookiePrincipal), nullptr)) {
|
||||||
|
case CookieCommons::SecurityChecksResult::eSandboxedError:
|
||||||
|
[[fallthrough]];
|
||||||
|
|
||||||
|
case CookieCommons::SecurityChecksResult::eSecurityError:
|
||||||
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
case CookieCommons::SecurityChecksResult::eDoNotContinue:
|
||||||
|
ResolvePromiseAsync(promise);
|
||||||
|
return promise.forget();
|
||||||
|
|
||||||
|
case CookieCommons::SecurityChecksResult::eContinue:
|
||||||
|
MOZ_ASSERT(cookiePrincipal);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_DispatchToCurrentThread(NS_NewRunnableFunction(
|
||||||
|
__func__,
|
||||||
|
[self = RefPtr(this), promise = RefPtr(promise), aOptions,
|
||||||
|
cookiePrincipal = RefPtr(cookiePrincipal.get()), aOnlyTheFirstMatch]() {
|
||||||
|
nsAutoString name;
|
||||||
|
if (aOptions.mName.WasPassed()) {
|
||||||
|
name = aOptions.mName.Value();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString path;
|
||||||
|
nsresult rv = cookiePrincipal->GetFilePath(path);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aOptions.mUrl.WasPassed()) {
|
||||||
|
nsString url(aOptions.mUrl.Value());
|
||||||
|
|
||||||
|
if (NS_IsMainThread()) {
|
||||||
|
nsCOMPtr<nsPIDOMWindowInner> window = self->GetOwnerWindow();
|
||||||
|
MOZ_ASSERT(window);
|
||||||
|
|
||||||
|
nsCOMPtr<Document> document = window->GetExtantDoc();
|
||||||
|
if (NS_WARN_IF(!document)) {
|
||||||
|
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIURI* creationURI = document->GetOriginalURI();
|
||||||
|
if (NS_WARN_IF(!creationURI)) {
|
||||||
|
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIURI> resolvedURI;
|
||||||
|
rv = NS_NewURI(getter_AddRefs(resolvedURI), url, nullptr,
|
||||||
|
creationURI);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
promise->MaybeRejectWithTypeError<MSG_INVALID_URL>(
|
||||||
|
NS_ConvertUTF16toUTF8(url));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool equal = false;
|
||||||
|
if (!resolvedURI ||
|
||||||
|
NS_WARN_IF(NS_FAILED(
|
||||||
|
resolvedURI->EqualsExceptRef(creationURI, &equal))) ||
|
||||||
|
!equal) {
|
||||||
|
promise->MaybeRejectWithTypeError<MSG_INVALID_URL>(
|
||||||
|
NS_ConvertUTF16toUTF8(url));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nsCOMPtr<nsIURI> baseURI = cookiePrincipal->GetURI();
|
||||||
|
if (NS_WARN_IF(!baseURI)) {
|
||||||
|
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIURI> resolvedURI;
|
||||||
|
rv = NS_NewURI(getter_AddRefs(resolvedURI), url, nullptr, baseURI);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
promise->MaybeRejectWithTypeError<MSG_INVALID_URL>(
|
||||||
|
NS_ConvertUTF16toUTF8(url));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cookiePrincipal->IsSameOrigin(resolvedURI)) {
|
||||||
|
promise->MaybeRejectWithTypeError<MSG_INVALID_URL>(
|
||||||
|
NS_ConvertUTF16toUTF8(url));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = resolvedURI->GetFilePath(path);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->MaybeCreateActor()) {
|
||||||
|
promise->MaybeRejectWithNotAllowedError("Permission denied");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString baseDomain;
|
||||||
|
rv = net::CookieCommons::GetBaseDomain(cookiePrincipal, baseDomain);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
promise->MaybeRejectWithNotAllowedError("Permission denied");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<CookieStoreChild::GetRequestPromise> ipcPromise =
|
||||||
|
self->mActor->SendGetRequest(NS_ConvertUTF8toUTF16(baseDomain),
|
||||||
|
cookiePrincipal->OriginAttributesRef(),
|
||||||
|
aOptions.mName.WasPassed(),
|
||||||
|
nsString(name), path,
|
||||||
|
aOnlyTheFirstMatch);
|
||||||
|
if (NS_WARN_IF(!ipcPromise)) {
|
||||||
|
promise->MaybeResolveWithUndefined();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcPromise->Then(
|
||||||
|
NS_GetCurrentThread(), __func__,
|
||||||
|
[promise = RefPtr<dom::Promise>(promise), aOnlyTheFirstMatch](
|
||||||
|
const CookieStoreChild::GetRequestPromise::ResolveOrRejectValue&
|
||||||
|
aResult) {
|
||||||
|
nsTArray<CookieListItem> list;
|
||||||
|
MOZ_ASSERT(aResult.IsResolve());
|
||||||
|
|
||||||
|
CookieDataToList(aResult.ResolveValue(), list);
|
||||||
|
|
||||||
|
if (!aOnlyTheFirstMatch) {
|
||||||
|
promise->MaybeResolve(list);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.IsEmpty()) {
|
||||||
|
promise->MaybeResolve(JS::NullHandleValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
promise->MaybeResolve(list[0]);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
return promise.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CookieStore::FireDelayedDOMEvents() {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
if (mNotifier) {
|
||||||
|
mNotifier->FireDelayedDOMEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Document* CookieStore::MaybeGetDocument() const {
|
||||||
|
if (NS_IsMainThread()) {
|
||||||
|
nsCOMPtr<nsPIDOMWindowInner> window = GetOwnerWindow();
|
||||||
|
MOZ_ASSERT(window);
|
||||||
|
return window->GetExtantDoc();
|
||||||
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,9 @@ class nsIGlobalObject;
|
||||||
|
|
||||||
namespace mozilla::dom {
|
namespace mozilla::dom {
|
||||||
|
|
||||||
|
class CookieData;
|
||||||
|
class CookieStoreChild;
|
||||||
|
class CookieStoreNotifier;
|
||||||
class Promise;
|
class Promise;
|
||||||
|
|
||||||
class CookieStore final : public DOMEventTargetHelper {
|
class CookieStore final : public DOMEventTargetHelper {
|
||||||
|
@ -29,6 +32,8 @@ class CookieStore final : public DOMEventTargetHelper {
|
||||||
JSObject* WrapObject(JSContext* aCx,
|
JSObject* WrapObject(JSContext* aCx,
|
||||||
JS::Handle<JSObject*> aGivenProto) override;
|
JS::Handle<JSObject*> aGivenProto) override;
|
||||||
|
|
||||||
|
void FireDelayedDOMEvents();
|
||||||
|
|
||||||
already_AddRefed<Promise> Get(const nsAString& aName, ErrorResult& aRv);
|
already_AddRefed<Promise> Get(const nsAString& aName, ErrorResult& aRv);
|
||||||
|
|
||||||
already_AddRefed<Promise> Get(const CookieStoreGetOptions& aOptions,
|
already_AddRefed<Promise> Get(const CookieStoreGetOptions& aOptions,
|
||||||
|
@ -54,6 +59,19 @@ class CookieStore final : public DOMEventTargetHelper {
|
||||||
private:
|
private:
|
||||||
explicit CookieStore(nsIGlobalObject* aGlobal);
|
explicit CookieStore(nsIGlobalObject* aGlobal);
|
||||||
~CookieStore();
|
~CookieStore();
|
||||||
|
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
Document* MaybeGetDocument() const;
|
||||||
|
|
||||||
|
already_AddRefed<Promise> GetInternal(const CookieStoreGetOptions& aOptions,
|
||||||
|
bool aOnlyTheFirstMatch,
|
||||||
|
ErrorResult& aRv);
|
||||||
|
|
||||||
|
bool MaybeCreateActor();
|
||||||
|
|
||||||
|
RefPtr<CookieStoreChild> mActor;
|
||||||
|
RefPtr<CookieStoreNotifier> mNotifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* 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 "CookieStore.h"
|
||||||
|
#include "CookieStoreChild.h"
|
||||||
|
#include "mozilla/ipc/PBackgroundChild.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
using namespace ipc;
|
||||||
|
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
CookieStoreChild::CookieStoreChild() = default;
|
||||||
|
|
||||||
|
CookieStoreChild::~CookieStoreChild() = default;
|
||||||
|
|
||||||
|
void CookieStoreChild::Close() {
|
||||||
|
if (CanSend()) {
|
||||||
|
SendClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,31 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_CookieStoreChild_h
|
||||||
|
#define mozilla_dom_CookieStoreChild_h
|
||||||
|
|
||||||
|
#include "mozilla/dom/PCookieStoreChild.h"
|
||||||
|
#include "nsISupportsImpl.h"
|
||||||
|
|
||||||
|
namespace mozilla::dom {
|
||||||
|
|
||||||
|
class CookieStoreChild final : public PCookieStoreChild {
|
||||||
|
friend class PCookieStoreChild;
|
||||||
|
|
||||||
|
public:
|
||||||
|
NS_INLINE_DECL_REFCOUNTING(CookieStoreChild)
|
||||||
|
|
||||||
|
CookieStoreChild();
|
||||||
|
|
||||||
|
void Close();
|
||||||
|
|
||||||
|
private:
|
||||||
|
~CookieStoreChild();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla::dom
|
||||||
|
|
||||||
|
#endif // mozilla_dom_CookieStoreChild_h
|
|
@ -0,0 +1,245 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* 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 "CookieStoreNotifier.h"
|
||||||
|
#include "CookieStore.h"
|
||||||
|
#include "mozilla/net/CookieCommons.h"
|
||||||
|
#include "mozilla/dom/Document.h"
|
||||||
|
#include "mozilla/dom/WorkerPrivate.h"
|
||||||
|
#include "mozilla/Unused.h"
|
||||||
|
#include "nsICookie.h"
|
||||||
|
#include "nsICookieNotification.h"
|
||||||
|
#include "nsISerialEventTarget.h"
|
||||||
|
|
||||||
|
namespace mozilla::dom {
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS(CookieStoreNotifier, nsIObserver);
|
||||||
|
|
||||||
|
// static
|
||||||
|
already_AddRefed<CookieStoreNotifier> CookieStoreNotifier::Create(
|
||||||
|
CookieStore* aCookieStore) {
|
||||||
|
nsIPrincipal* principal = nullptr;
|
||||||
|
|
||||||
|
if (!NS_IsMainThread()) {
|
||||||
|
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||||
|
MOZ_ASSERT(workerPrivate);
|
||||||
|
principal = workerPrivate->GetPrincipal();
|
||||||
|
} else {
|
||||||
|
nsCOMPtr<nsPIDOMWindowInner> window = aCookieStore->GetOwnerWindow();
|
||||||
|
MOZ_ASSERT(window);
|
||||||
|
|
||||||
|
nsCOMPtr<Document> document = window->GetExtantDoc();
|
||||||
|
if (NS_WARN_IF(!document)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
principal = document->NodePrincipal();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_WARN_IF(!principal)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCString baseDomain;
|
||||||
|
if (NS_WARN_IF(NS_FAILED(
|
||||||
|
net::CookieCommons::GetBaseDomain(principal, baseDomain)))) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseDomain.IsEmpty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<CookieStoreNotifier> notifier = new CookieStoreNotifier(
|
||||||
|
aCookieStore, baseDomain, principal->OriginAttributesRef());
|
||||||
|
|
||||||
|
bool privateBrowsing = principal->OriginAttributesRef().IsPrivateBrowsing();
|
||||||
|
|
||||||
|
notifier->mEventTarget = GetCurrentSerialEventTarget();
|
||||||
|
|
||||||
|
if (!NS_IsMainThread()) {
|
||||||
|
NS_DispatchToMainThread(
|
||||||
|
NS_NewRunnableFunction(__func__, [notifier, privateBrowsing] {
|
||||||
|
notifier->AddObserversOnMainThread(privateBrowsing);
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
notifier->AddObserversOnMainThread(privateBrowsing);
|
||||||
|
}
|
||||||
|
|
||||||
|
return notifier.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
CookieStoreNotifier::CookieStoreNotifier(
|
||||||
|
CookieStore* aCookieStore, const nsACString& aBaseDomain,
|
||||||
|
const OriginAttributes& aOriginAttributes)
|
||||||
|
: mCookieStore(aCookieStore),
|
||||||
|
mBaseDomain(aBaseDomain),
|
||||||
|
mOriginAttributes(aOriginAttributes) {
|
||||||
|
MOZ_ASSERT(aCookieStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
CookieStoreNotifier::~CookieStoreNotifier() = default;
|
||||||
|
|
||||||
|
void CookieStoreNotifier::Disentangle() {
|
||||||
|
mCookieStore = nullptr;
|
||||||
|
|
||||||
|
bool privateBrowsing = mOriginAttributes.IsPrivateBrowsing();
|
||||||
|
|
||||||
|
if (!NS_IsMainThread()) {
|
||||||
|
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||||
|
__func__, [self = RefPtr(this), privateBrowsing] {
|
||||||
|
self->RemoveObserversOnMainThread(privateBrowsing);
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
RemoveObserversOnMainThread(privateBrowsing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CookieStoreNotifier::AddObserversOnMainThread(bool aPrivateBrowsing) {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||||
|
if (NS_WARN_IF(!os)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult rv = os->AddObserver(
|
||||||
|
this, aPrivateBrowsing ? "private-cookie-changed" : "cookie-changed",
|
||||||
|
false);
|
||||||
|
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CookieStoreNotifier::RemoveObserversOnMainThread(bool aPrivateBrowsing) {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||||
|
if (NS_WARN_IF(!os)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult rv = os->RemoveObserver(
|
||||||
|
this, aPrivateBrowsing ? "private-cookie-changed" : "cookie-changed");
|
||||||
|
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
CookieStoreNotifier::Observe(nsISupports* aSubject, const char* aTopic,
|
||||||
|
const char16_t* aData) {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
nsCOMPtr<nsICookieNotification> notification = do_QueryInterface(aSubject);
|
||||||
|
NS_ENSURE_TRUE(notification, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
auto action = notification->GetAction();
|
||||||
|
if (action != nsICookieNotification::COOKIE_DELETED &&
|
||||||
|
action != nsICookieNotification::COOKIE_ADDED &&
|
||||||
|
action != nsICookieNotification::COOKIE_CHANGED) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString baseDomain;
|
||||||
|
nsresult rv = notification->GetBaseDomain(baseDomain);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv)) || baseDomain.IsEmpty()) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseDomain != mBaseDomain) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsICookie> cookie;
|
||||||
|
rv = notification->GetCookie(getter_AddRefs(cookie));
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cookie->OriginAttributesNative() != mOriginAttributes) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isHttpOnly;
|
||||||
|
rv = cookie->GetIsHttpOnly(&isHttpOnly);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isHttpOnly) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
CookieListItem item;
|
||||||
|
|
||||||
|
nsAutoCString name;
|
||||||
|
rv = cookie->GetName(name);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.mName.Construct(NS_ConvertUTF8toUTF16(name));
|
||||||
|
|
||||||
|
if (action != nsICookieNotification::COOKIE_DELETED) {
|
||||||
|
nsAutoCString value;
|
||||||
|
rv = cookie->GetValue(value);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.mValue.Construct(NS_ConvertUTF8toUTF16(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deletedEvent = action == nsICookieNotification::COOKIE_DELETED;
|
||||||
|
|
||||||
|
mEventTarget->Dispatch(NS_NewRunnableFunction(
|
||||||
|
__func__, [self = RefPtr(this), item, deletedEvent] {
|
||||||
|
if (!self->mCookieStore) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<Event> event = deletedEvent
|
||||||
|
? CookieChangeEvent::CreateForDeletedCookie(
|
||||||
|
self->mCookieStore, item)
|
||||||
|
: CookieChangeEvent::CreateForChangedCookie(
|
||||||
|
self->mCookieStore, item);
|
||||||
|
|
||||||
|
if (!event) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_IsMainThread()) {
|
||||||
|
nsCOMPtr<nsPIDOMWindowInner> window =
|
||||||
|
self->mCookieStore->GetOwnerWindow();
|
||||||
|
if (!window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<BrowsingContext> bc = window->GetBrowsingContext();
|
||||||
|
if (!bc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bc->IsInBFCache()) {
|
||||||
|
self->mDelayedDOMEvents.AppendElement(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self->mCookieStore->DispatchEvent(*event);
|
||||||
|
}));
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CookieStoreNotifier::FireDelayedDOMEvents() {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
nsTArray<RefPtr<Event>> delayedDOMEvents;
|
||||||
|
delayedDOMEvents.SwapElements(mDelayedDOMEvents);
|
||||||
|
|
||||||
|
for (Event* event : delayedDOMEvents) {
|
||||||
|
mCookieStore->DispatchEvent(*event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mozilla::dom
|
|
@ -0,0 +1,54 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_CookieStoreNotifier_h
|
||||||
|
#define mozilla_dom_CookieStoreNotifier_h
|
||||||
|
|
||||||
|
#include "nsIObserver.h"
|
||||||
|
#include "mozilla/OriginAttributes.h"
|
||||||
|
|
||||||
|
class nsISerialEventTarget;
|
||||||
|
|
||||||
|
namespace mozilla::dom {
|
||||||
|
|
||||||
|
class CookieStore;
|
||||||
|
struct CookieListItem;
|
||||||
|
class Event;
|
||||||
|
|
||||||
|
class CookieStoreNotifier final : public nsIObserver {
|
||||||
|
public:
|
||||||
|
NS_DECL_THREADSAFE_ISUPPORTS
|
||||||
|
NS_DECL_NSIOBSERVER
|
||||||
|
|
||||||
|
static already_AddRefed<CookieStoreNotifier> Create(
|
||||||
|
CookieStore* aCookieStore);
|
||||||
|
|
||||||
|
void Disentangle();
|
||||||
|
|
||||||
|
void FireDelayedDOMEvents();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CookieStoreNotifier(CookieStore* aCookieStore, const nsACString& aBaseDomain,
|
||||||
|
const OriginAttributes& aOriginAttributes);
|
||||||
|
~CookieStoreNotifier();
|
||||||
|
|
||||||
|
void AddObserversOnMainThread(bool aPrivateBrowsing);
|
||||||
|
void RemoveObserversOnMainThread(bool aPrivateBrowsing);
|
||||||
|
|
||||||
|
// Raw pointer because this object is kept alive by this CookieStore object.
|
||||||
|
CookieStore* mCookieStore;
|
||||||
|
|
||||||
|
nsCString mBaseDomain;
|
||||||
|
OriginAttributes mOriginAttributes;
|
||||||
|
|
||||||
|
RefPtr<nsISerialEventTarget> mEventTarget;
|
||||||
|
|
||||||
|
nsTArray<RefPtr<Event>> mDelayedDOMEvents;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla::dom
|
||||||
|
|
||||||
|
#endif /* mozilla_dom_CookieStoreNotifier_h */
|
|
@ -0,0 +1,262 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* 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 "CookieStoreParent.h"
|
||||||
|
#include "mozilla/Maybe.h"
|
||||||
|
#include "mozilla/ipc/BackgroundParent.h"
|
||||||
|
#include "mozilla/net/CookieCommons.h"
|
||||||
|
#include "mozilla/Unused.h"
|
||||||
|
#include "nsICookieManager.h"
|
||||||
|
|
||||||
|
using namespace mozilla::ipc;
|
||||||
|
|
||||||
|
namespace mozilla::dom {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void GetRequestHelper(const nsAString& aDomain,
|
||||||
|
const OriginAttributes& aOriginAttributes,
|
||||||
|
bool aMatchName, const nsAString& aName,
|
||||||
|
const nsACString& aPath, bool aOnlyFirstMatch,
|
||||||
|
nsTArray<CookieData>& aResults) {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
nsCOMPtr<nsICookieManager> service =
|
||||||
|
do_GetService(NS_COOKIEMANAGER_CONTRACTID);
|
||||||
|
if (!service) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OriginAttributes attrs(aOriginAttributes);
|
||||||
|
nsTArray<RefPtr<nsICookie>> results;
|
||||||
|
nsresult rv = service->GetCookiesFromHostNative(
|
||||||
|
NS_ConvertUTF16toUTF8(aDomain), &attrs, results);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ConvertUTF16toUTF8 matchName(aName);
|
||||||
|
|
||||||
|
nsTArray<CookieData> list;
|
||||||
|
for (nsICookie* cookie : results) {
|
||||||
|
bool isHttpOnly;
|
||||||
|
rv = cookie->GetIsHttpOnly(&isHttpOnly);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isHttpOnly) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString name;
|
||||||
|
rv = cookie->GetName(name);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aMatchName && !matchName.Equals(name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString cookiePath;
|
||||||
|
rv = cookie->GetPath(cookiePath);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!net::CookieCommons::PathMatches(cookiePath, aPath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString value;
|
||||||
|
rv = cookie->GetValue(value);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CookieData* data = list.AppendElement();
|
||||||
|
data->name() = NS_ConvertUTF8toUTF16(name);
|
||||||
|
data->value() = NS_ConvertUTF8toUTF16(value);
|
||||||
|
|
||||||
|
if (aOnlyFirstMatch) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aResults.SwapElements(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetRequestHelper(const nsAString& aDomain,
|
||||||
|
const OriginAttributes& aOriginAttributes,
|
||||||
|
const nsAString& aName, const nsAString& aValue,
|
||||||
|
bool aSession, int64_t aExpires, const nsAString& aPath,
|
||||||
|
int32_t aSameSite, bool aPartitioned) {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
nsCOMPtr<nsICookieManager> service =
|
||||||
|
do_GetService(NS_COOKIEMANAGER_CONTRACTID);
|
||||||
|
if (!service) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OriginAttributes attrs(aOriginAttributes);
|
||||||
|
nsresult rv = service->AddNative(
|
||||||
|
NS_ConvertUTF16toUTF8(aDomain), NS_ConvertUTF16toUTF8(aPath),
|
||||||
|
NS_ConvertUTF16toUTF8(aName), NS_ConvertUTF16toUTF8(aValue),
|
||||||
|
true, // secure
|
||||||
|
false, // mHttpOnly,
|
||||||
|
aSession, aSession ? PR_Now() : aExpires, &attrs, aSameSite,
|
||||||
|
nsICookie::SCHEME_HTTPS);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteRequestHelper(const nsAString& aDomain,
|
||||||
|
const OriginAttributes& aOriginAttributes,
|
||||||
|
const nsAString& aName, const nsAString& aPath,
|
||||||
|
bool aPartitioned) {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
nsCOMPtr<nsICookieManager> service =
|
||||||
|
do_GetService(NS_COOKIEMANAGER_CONTRACTID);
|
||||||
|
if (!service) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ConvertUTF16toUTF8 domainUtf8(aDomain);
|
||||||
|
|
||||||
|
OriginAttributes attrs(aOriginAttributes);
|
||||||
|
nsTArray<RefPtr<nsICookie>> results;
|
||||||
|
nsresult rv = service->GetCookiesFromHostNative(domainUtf8, &attrs, results);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ConvertUTF16toUTF8 matchName(aName);
|
||||||
|
NS_ConvertUTF16toUTF8 matchPath(aPath);
|
||||||
|
|
||||||
|
for (nsICookie* cookie : results) {
|
||||||
|
MOZ_ASSERT(cookie);
|
||||||
|
|
||||||
|
nsAutoCString name;
|
||||||
|
rv = cookie->GetName(name);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matchName.Equals(name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString path;
|
||||||
|
rv = cookie->GetPath(path);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matchPath.IsEmpty() && !matchPath.Equals(path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isPartitioned = false;
|
||||||
|
rv = cookie->GetIsPartitioned(&isPartitioned);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPartitioned != aPartitioned) continue;
|
||||||
|
|
||||||
|
rv = service->RemoveNative(domainUtf8, matchName, path, &attrs);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
CookieStoreParent::CookieStoreParent() { AssertIsOnBackgroundThread(); }
|
||||||
|
|
||||||
|
CookieStoreParent::~CookieStoreParent() { AssertIsOnBackgroundThread(); }
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult CookieStoreParent::RecvGetRequest(
|
||||||
|
const nsString& aDomain, const OriginAttributes& aOriginAttributes,
|
||||||
|
const bool& aMatchName, const nsString& aName, const nsCString& aPath,
|
||||||
|
const bool& aOnlyFirstMatch, GetRequestResolver&& aResolver) {
|
||||||
|
InvokeAsync(GetMainThreadSerialEventTarget(), __func__,
|
||||||
|
[aDomain, aOriginAttributes, aMatchName, aName, aPath,
|
||||||
|
aOnlyFirstMatch]() {
|
||||||
|
CopyableTArray<CookieData> results;
|
||||||
|
GetRequestHelper(aDomain, aOriginAttributes, aMatchName, aName,
|
||||||
|
aPath, aOnlyFirstMatch, results);
|
||||||
|
return GetRequestPromise::CreateAndResolve(std::move(results),
|
||||||
|
__func__);
|
||||||
|
})
|
||||||
|
->Then(GetCurrentSerialEventTarget(), __func__,
|
||||||
|
[aResolver = std::move(aResolver)](
|
||||||
|
const GetRequestPromise::ResolveOrRejectValue& aResult) {
|
||||||
|
MOZ_ASSERT(aResult.IsResolve());
|
||||||
|
aResolver(aResult.ResolveValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult CookieStoreParent::RecvSetRequest(
|
||||||
|
const nsString& aDomain, const OriginAttributes& aOriginAttributes,
|
||||||
|
const nsString& aName, const nsString& aValue, const bool& aSession,
|
||||||
|
const int64_t& aExpires, const nsString& aPath, const int32_t& aSameSite,
|
||||||
|
const bool& aPartitioned, SetRequestResolver&& aResolver) {
|
||||||
|
InvokeAsync(
|
||||||
|
GetMainThreadSerialEventTarget(), __func__,
|
||||||
|
[aDomain, aOriginAttributes, aName, aValue, aSession, aExpires, aPath,
|
||||||
|
aSameSite, aPartitioned]() {
|
||||||
|
SetRequestHelper(aDomain, aOriginAttributes, aName, aValue, aSession,
|
||||||
|
aExpires, aPath, aSameSite, aPartitioned);
|
||||||
|
return SetDeleteRequestPromise::CreateAndResolve(true, __func__);
|
||||||
|
})
|
||||||
|
->Then(GetCurrentSerialEventTarget(), __func__,
|
||||||
|
[aResolver = std::move(aResolver)](
|
||||||
|
const SetDeleteRequestPromise::ResolveOrRejectValue& aResult) {
|
||||||
|
MOZ_ASSERT(aResult.IsResolve());
|
||||||
|
aResolver(aResult.ResolveValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult CookieStoreParent::RecvDeleteRequest(
|
||||||
|
const nsString& aDomain, const OriginAttributes& aOriginAttributes,
|
||||||
|
const nsString& aName, const nsString& aPath, const bool& aPartitioned,
|
||||||
|
DeleteRequestResolver&& aResolver) {
|
||||||
|
AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
|
InvokeAsync(GetMainThreadSerialEventTarget(), __func__,
|
||||||
|
[aDomain, aOriginAttributes, aName, aPath, aPartitioned]() {
|
||||||
|
DeleteRequestHelper(aDomain, aOriginAttributes, aName, aPath,
|
||||||
|
aPartitioned);
|
||||||
|
return SetDeleteRequestPromise::CreateAndResolve(true,
|
||||||
|
__func__);
|
||||||
|
})
|
||||||
|
->Then(GetCurrentSerialEventTarget(), __func__,
|
||||||
|
[aResolver = std::move(aResolver)](
|
||||||
|
const SetDeleteRequestPromise::ResolveOrRejectValue& aResult) {
|
||||||
|
MOZ_ASSERT(aResult.IsResolve());
|
||||||
|
aResolver(aResult.ResolveValue());
|
||||||
|
});
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult CookieStoreParent::RecvClose() {
|
||||||
|
AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
|
Unused << Send__delete__(this);
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mozilla::dom
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_CookieStoreParent_h
|
||||||
|
#define mozilla_dom_CookieStoreParent_h
|
||||||
|
|
||||||
|
#include "mozilla/dom/PCookieStoreParent.h"
|
||||||
|
#include "mozilla/MozPromise.h"
|
||||||
|
|
||||||
|
namespace mozilla::dom {
|
||||||
|
|
||||||
|
class CookieStoreService;
|
||||||
|
|
||||||
|
class CookieStoreParent final : public PCookieStoreParent {
|
||||||
|
friend class PCookieStoreParent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using GetRequestPromise =
|
||||||
|
MozPromise<CopyableTArray<CookieData>, nsresult, true>;
|
||||||
|
using SetDeleteRequestPromise = MozPromise<bool, nsresult, true>;
|
||||||
|
|
||||||
|
NS_INLINE_DECL_REFCOUNTING(CookieStoreParent)
|
||||||
|
|
||||||
|
CookieStoreParent();
|
||||||
|
|
||||||
|
private:
|
||||||
|
~CookieStoreParent();
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult RecvGetRequest(
|
||||||
|
const nsString& aDomain, const OriginAttributes& aOriginAttributes,
|
||||||
|
const bool& aMatchName, const nsString& aName, const nsCString& aPath,
|
||||||
|
const bool& aOnlyFirstMatch, GetRequestResolver&& aResolver);
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult RecvSetRequest(
|
||||||
|
const nsString& aDomain, const OriginAttributes& aOriginAttributes,
|
||||||
|
const nsString& aName, const nsString& aValue, const bool& aSession,
|
||||||
|
const int64_t& aExpires, const nsString& aPath, const int32_t& aSameSite,
|
||||||
|
const bool& aPartitioned, SetRequestResolver&& aResolver);
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult RecvDeleteRequest(
|
||||||
|
const nsString& aDomain, const OriginAttributes& aOriginAttributes,
|
||||||
|
const nsString& aName, const nsString& aPath, const bool& aPartitioned,
|
||||||
|
DeleteRequestResolver&& aResolver);
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult RecvClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla::dom
|
||||||
|
|
||||||
|
#endif // mozilla_dom_CookieStoreParent_h
|
|
@ -0,0 +1,62 @@
|
||||||
|
/* 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 protocol PBackground;
|
||||||
|
|
||||||
|
using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
|
||||||
|
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
struct CookieData
|
||||||
|
{
|
||||||
|
nsString name;
|
||||||
|
nsString value;
|
||||||
|
};
|
||||||
|
|
||||||
|
union MaybeCookieData
|
||||||
|
{
|
||||||
|
CookieData;
|
||||||
|
void_t;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This protocol is used for the CookieStore API
|
||||||
|
[ManualDealloc]
|
||||||
|
protocol PCookieStore
|
||||||
|
{
|
||||||
|
manager PBackground;
|
||||||
|
|
||||||
|
parent:
|
||||||
|
async GetRequest(nsString domain,
|
||||||
|
OriginAttributes attrs,
|
||||||
|
bool matchName,
|
||||||
|
nsString name,
|
||||||
|
nsCString path,
|
||||||
|
bool onlyFirstMatch) returns (CookieData[] data);
|
||||||
|
|
||||||
|
async SetRequest(nsString domain,
|
||||||
|
OriginAttributes attrs,
|
||||||
|
nsString name,
|
||||||
|
nsString value,
|
||||||
|
bool session,
|
||||||
|
int64_t expires,
|
||||||
|
nsString path,
|
||||||
|
int32_t sameSite,
|
||||||
|
bool partitioned) returns (bool v);
|
||||||
|
|
||||||
|
async DeleteRequest(nsString domain,
|
||||||
|
OriginAttributes attrs,
|
||||||
|
nsString name,
|
||||||
|
nsString path,
|
||||||
|
bool partitioned) returns (bool v);
|
||||||
|
|
||||||
|
async Close();
|
||||||
|
|
||||||
|
child:
|
||||||
|
async __delete__();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -8,11 +8,16 @@ with Files("**"):
|
||||||
EXPORTS.mozilla.dom += [
|
EXPORTS.mozilla.dom += [
|
||||||
"CookieChangeEvent.h",
|
"CookieChangeEvent.h",
|
||||||
"CookieStore.h",
|
"CookieStore.h",
|
||||||
|
"CookieStoreChild.h",
|
||||||
|
"CookieStoreParent.h",
|
||||||
]
|
]
|
||||||
|
|
||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
"CookieChangeEvent.cpp",
|
"CookieChangeEvent.cpp",
|
||||||
"CookieStore.cpp",
|
"CookieStore.cpp",
|
||||||
|
"CookieStoreChild.cpp",
|
||||||
|
"CookieStoreNotifier.cpp",
|
||||||
|
"CookieStoreParent.cpp",
|
||||||
]
|
]
|
||||||
|
|
||||||
LOCAL_INCLUDES += [
|
LOCAL_INCLUDES += [
|
||||||
|
@ -20,6 +25,10 @@ LOCAL_INCLUDES += [
|
||||||
"../events",
|
"../events",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
IPDL_SOURCES += [
|
||||||
|
"PCookieStore.ipdl",
|
||||||
|
]
|
||||||
|
|
||||||
include("/ipc/chromium/chromium-config.mozbuild")
|
include("/ipc/chromium/chromium-config.mozbuild")
|
||||||
|
|
||||||
FINAL_LIBRARY = "xul"
|
FINAL_LIBRARY = "xul"
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "mozilla/dom/PBackgroundLSRequestChild.h"
|
#include "mozilla/dom/PBackgroundLSRequestChild.h"
|
||||||
#include "mozilla/dom/PBackgroundLSSimpleRequestChild.h"
|
#include "mozilla/dom/PBackgroundLSSimpleRequestChild.h"
|
||||||
#include "mozilla/dom/PBackgroundSDBConnectionChild.h"
|
#include "mozilla/dom/PBackgroundSDBConnectionChild.h"
|
||||||
|
#include "mozilla/dom/CookieStoreChild.h"
|
||||||
#include "mozilla/dom/PFileSystemRequestChild.h"
|
#include "mozilla/dom/PFileSystemRequestChild.h"
|
||||||
#include "mozilla/dom/EndpointForReportChild.h"
|
#include "mozilla/dom/EndpointForReportChild.h"
|
||||||
#include "mozilla/dom/PVsync.h"
|
#include "mozilla/dom/PVsync.h"
|
||||||
|
@ -320,6 +321,26 @@ bool BackgroundChildImpl::DeallocPBroadcastChannelChild(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// CookieStore API
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
dom::PCookieStoreChild* BackgroundChildImpl::AllocPCookieStoreChild() {
|
||||||
|
RefPtr<dom::CookieStoreChild> child = new dom::CookieStoreChild();
|
||||||
|
return child.forget().take();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BackgroundChildImpl::DeallocPCookieStoreChild(PCookieStoreChild* aActor) {
|
||||||
|
RefPtr<dom::CookieStoreChild> child =
|
||||||
|
dont_AddRef(static_cast<dom::CookieStoreChild*>(aActor));
|
||||||
|
MOZ_ASSERT(child);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Camera API
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
camera::PCamerasChild* BackgroundChildImpl::AllocPCamerasChild() {
|
camera::PCamerasChild* BackgroundChildImpl::AllocPCamerasChild() {
|
||||||
#ifdef MOZ_WEBRTC
|
#ifdef MOZ_WEBRTC
|
||||||
RefPtr<camera::CamerasChild> agent = new camera::CamerasChild();
|
RefPtr<camera::CamerasChild> agent = new camera::CamerasChild();
|
||||||
|
|
|
@ -125,6 +125,10 @@ class BackgroundChildImpl : public PBackgroundChild {
|
||||||
virtual bool DeallocPBroadcastChannelChild(
|
virtual bool DeallocPBroadcastChannelChild(
|
||||||
PBroadcastChannelChild* aActor) override;
|
PBroadcastChannelChild* aActor) override;
|
||||||
|
|
||||||
|
virtual PCookieStoreChild* AllocPCookieStoreChild() override;
|
||||||
|
|
||||||
|
virtual bool DeallocPCookieStoreChild(PCookieStoreChild* aActor) override;
|
||||||
|
|
||||||
virtual PServiceWorkerManagerChild* AllocPServiceWorkerManagerChild()
|
virtual PServiceWorkerManagerChild* AllocPServiceWorkerManagerChild()
|
||||||
override;
|
override;
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "mozilla/dom/BackgroundSessionStorageServiceParent.h"
|
#include "mozilla/dom/BackgroundSessionStorageServiceParent.h"
|
||||||
#include "mozilla/dom/ClientManagerActors.h"
|
#include "mozilla/dom/ClientManagerActors.h"
|
||||||
#include "mozilla/dom/ContentParent.h"
|
#include "mozilla/dom/ContentParent.h"
|
||||||
|
#include "mozilla/dom/CookieStoreParent.h"
|
||||||
#include "mozilla/dom/DOMTypes.h"
|
#include "mozilla/dom/DOMTypes.h"
|
||||||
#include "mozilla/dom/EndpointForReportParent.h"
|
#include "mozilla/dom/EndpointForReportParent.h"
|
||||||
#include "mozilla/dom/FetchParent.h"
|
#include "mozilla/dom/FetchParent.h"
|
||||||
|
@ -821,6 +822,27 @@ bool BackgroundParentImpl::DeallocPBroadcastChannelParent(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mozilla::dom::PCookieStoreParent*
|
||||||
|
BackgroundParentImpl::AllocPCookieStoreParent() {
|
||||||
|
AssertIsInMainProcess();
|
||||||
|
AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
|
RefPtr<mozilla::dom::CookieStoreParent> actor =
|
||||||
|
new mozilla::dom::CookieStoreParent();
|
||||||
|
return actor.forget().take();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BackgroundParentImpl::DeallocPCookieStoreParent(
|
||||||
|
PCookieStoreParent* aActor) {
|
||||||
|
AssertIsInMainProcess();
|
||||||
|
AssertIsOnBackgroundThread();
|
||||||
|
MOZ_ASSERT(aActor);
|
||||||
|
|
||||||
|
RefPtr<mozilla::dom::CookieStoreParent> actor =
|
||||||
|
dont_AddRef(static_cast<mozilla::dom::CookieStoreParent*>(aActor));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
mozilla::dom::PServiceWorkerManagerParent*
|
mozilla::dom::PServiceWorkerManagerParent*
|
||||||
BackgroundParentImpl::AllocPServiceWorkerManagerParent() {
|
BackgroundParentImpl::AllocPServiceWorkerManagerParent() {
|
||||||
AssertIsInMainProcess();
|
AssertIsInMainProcess();
|
||||||
|
|
|
@ -192,6 +192,10 @@ class BackgroundParentImpl : public PBackgroundParent {
|
||||||
|
|
||||||
bool DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
|
bool DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
|
||||||
|
|
||||||
|
virtual PCookieStoreParent* AllocPCookieStoreParent() override;
|
||||||
|
|
||||||
|
virtual bool DeallocPCookieStoreParent(PCookieStoreParent* aActor) override;
|
||||||
|
|
||||||
PServiceWorkerManagerParent* AllocPServiceWorkerManagerParent() override;
|
PServiceWorkerManagerParent* AllocPServiceWorkerManagerParent() override;
|
||||||
|
|
||||||
bool DeallocPServiceWorkerManagerParent(
|
bool DeallocPServiceWorkerManagerParent(
|
||||||
|
|
|
@ -20,6 +20,7 @@ include protocol PCache;
|
||||||
include protocol PCacheStorage;
|
include protocol PCacheStorage;
|
||||||
include protocol PCacheStreamControl;
|
include protocol PCacheStreamControl;
|
||||||
include protocol PClientManager;
|
include protocol PClientManager;
|
||||||
|
include protocol PCookieStore;
|
||||||
include protocol PEndpointForReport;
|
include protocol PEndpointForReport;
|
||||||
include protocol PFileSystemManager;
|
include protocol PFileSystemManager;
|
||||||
include protocol PFileSystemRequest;
|
include protocol PFileSystemRequest;
|
||||||
|
@ -99,6 +100,7 @@ sync protocol PBackground
|
||||||
manages PCacheStorage;
|
manages PCacheStorage;
|
||||||
manages PCacheStreamControl;
|
manages PCacheStreamControl;
|
||||||
manages PClientManager;
|
manages PClientManager;
|
||||||
|
manages PCookieStore;
|
||||||
manages PEndpointForReport;
|
manages PEndpointForReport;
|
||||||
manages PFileSystemRequest;
|
manages PFileSystemRequest;
|
||||||
manages PGamepadEventChannel;
|
manages PGamepadEventChannel;
|
||||||
|
@ -197,6 +199,8 @@ parent:
|
||||||
async PUDPSocket(PrincipalInfo? pInfo, nsCString filter);
|
async PUDPSocket(PrincipalInfo? pInfo, nsCString filter);
|
||||||
async PBroadcastChannel(PrincipalInfo pInfo, nsCString origin, nsString channel);
|
async PBroadcastChannel(PrincipalInfo pInfo, nsCString origin, nsString channel);
|
||||||
|
|
||||||
|
async PCookieStore();
|
||||||
|
|
||||||
async PServiceWorkerManager();
|
async PServiceWorkerManager();
|
||||||
|
|
||||||
async ShutdownServiceWorkerRegistrar();
|
async ShutdownServiceWorkerRegistrar();
|
||||||
|
|
|
@ -12090,7 +12090,7 @@
|
||||||
|
|
||||||
# Whether to support CHIPS(Cookies Having Independent Partitioned State).
|
# Whether to support CHIPS(Cookies Having Independent Partitioned State).
|
||||||
- name: network.cookie.CHIPS.enabled
|
- name: network.cookie.CHIPS.enabled
|
||||||
type: bool
|
type: RelaxedAtomicBool
|
||||||
value: true
|
value: true
|
||||||
mirror: always
|
mirror: always
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#include "mozilla/dom/CanonicalBrowsingContext.h"
|
#include "mozilla/dom/CanonicalBrowsingContext.h"
|
||||||
#include "mozilla/dom/Document.h"
|
#include "mozilla/dom/Document.h"
|
||||||
#include "mozilla/dom/WindowGlobalParent.h"
|
#include "mozilla/dom/WindowGlobalParent.h"
|
||||||
|
#include "mozilla/dom/WorkerCommon.h"
|
||||||
|
#include "mozilla/dom/WorkerPrivate.h"
|
||||||
#include "mozilla/net/CookieJarSettings.h"
|
#include "mozilla/net/CookieJarSettings.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
#include "mozIThirdPartyUtil.h"
|
#include "mozIThirdPartyUtil.h"
|
||||||
|
@ -23,10 +25,12 @@
|
||||||
#include "nsICookiePermission.h"
|
#include "nsICookiePermission.h"
|
||||||
#include "nsICookieService.h"
|
#include "nsICookieService.h"
|
||||||
#include "nsIEffectiveTLDService.h"
|
#include "nsIEffectiveTLDService.h"
|
||||||
|
#include "nsIGlobalObject.h"
|
||||||
#include "nsIHttpChannel.h"
|
#include "nsIHttpChannel.h"
|
||||||
#include "nsIRedirectHistoryEntry.h"
|
#include "nsIRedirectHistoryEntry.h"
|
||||||
#include "nsIWebProgressListener.h"
|
#include "nsIWebProgressListener.h"
|
||||||
#include "nsNetUtil.h"
|
#include "nsNetUtil.h"
|
||||||
|
#include "nsSandboxFlags.h"
|
||||||
#include "nsScriptSecurityManager.h"
|
#include "nsScriptSecurityManager.h"
|
||||||
#include "ThirdPartyUtil.h"
|
#include "ThirdPartyUtil.h"
|
||||||
|
|
||||||
|
@ -47,31 +51,35 @@ bool CookieCommons::DomainMatches(Cookie* aCookie, const nsACString& aHost) {
|
||||||
|
|
||||||
// static
|
// static
|
||||||
bool CookieCommons::PathMatches(Cookie* aCookie, const nsACString& aPath) {
|
bool CookieCommons::PathMatches(Cookie* aCookie, const nsACString& aPath) {
|
||||||
const nsCString& cookiePath(aCookie->Path());
|
return PathMatches(aCookie->Path(), aPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool CookieCommons::PathMatches(const nsACString& aCookiePath,
|
||||||
|
const nsACString& aPath) {
|
||||||
// if our cookie path is empty we can't really perform our prefix check, and
|
// if our cookie path is empty we can't really perform our prefix check, and
|
||||||
// also we can't check the last character of the cookie path, so we would
|
// also we can't check the last character of the cookie path, so we would
|
||||||
// never return a successful match.
|
// never return a successful match.
|
||||||
if (cookiePath.IsEmpty()) {
|
if (aCookiePath.IsEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the cookie path and the request path are identical, they match.
|
// if the cookie path and the request path are identical, they match.
|
||||||
if (cookiePath.Equals(aPath)) {
|
if (aCookiePath.Equals(aPath)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the cookie path is a prefix of the request path, and the last character
|
// if the cookie path is a prefix of the request path, and the last character
|
||||||
// of the cookie path is %x2F ("/"), they match.
|
// of the cookie path is %x2F ("/"), they match.
|
||||||
bool isPrefix = StringBeginsWith(aPath, cookiePath);
|
bool isPrefix = StringBeginsWith(aPath, aCookiePath);
|
||||||
if (isPrefix && cookiePath.Last() == '/') {
|
if (isPrefix && aCookiePath.Last() == '/') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the cookie path is a prefix of the request path, and the first character
|
// if the cookie path is a prefix of the request path, and the first character
|
||||||
// of the request path that is not included in the cookie path is a %x2F ("/")
|
// of the request path that is not included in the cookie path is a %x2F ("/")
|
||||||
// character, they match.
|
// character, they match.
|
||||||
uint32_t cookiePathLen = cookiePath.Length();
|
uint32_t cookiePathLen = aCookiePath.Length();
|
||||||
return isPrefix && aPath[cookiePathLen] == '/';
|
return isPrefix && aPath[cookiePathLen] == '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -826,6 +834,138 @@ void CookieCommons::ComposeCookieString(nsTArray<RefPtr<Cookie>>& aCookieList,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
CookieCommons::SecurityChecksResult
|
||||||
|
CookieCommons::CheckGlobalAndRetrieveCookiePrincipals(
|
||||||
|
Document* aDocument, nsIPrincipal** aCookiePrincipal,
|
||||||
|
nsIPrincipal** aCookiePartitionedPrincipal) {
|
||||||
|
MOZ_ASSERT(aCookiePrincipal);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIPrincipal> cookiePrincipal;
|
||||||
|
nsCOMPtr<nsIPrincipal> cookiePartitionedPrincipal;
|
||||||
|
|
||||||
|
if (!NS_IsMainThread()) {
|
||||||
|
MOZ_ASSERT(!aDocument);
|
||||||
|
|
||||||
|
dom::WorkerPrivate* workerPrivate = dom::GetCurrentThreadWorkerPrivate();
|
||||||
|
MOZ_ASSERT(workerPrivate);
|
||||||
|
|
||||||
|
StorageAccess storageAccess = workerPrivate->StorageAccess();
|
||||||
|
if (storageAccess == StorageAccess::eDeny) {
|
||||||
|
return SecurityChecksResult::eDoNotContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cookiePrincipal = workerPrivate->GetPrincipal();
|
||||||
|
if (NS_WARN_IF(!cookiePrincipal) || cookiePrincipal->GetIsNullPrincipal()) {
|
||||||
|
return SecurityChecksResult::eSecurityError;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHIPS - If CHIPS is enabled the partitioned cookie jar is always
|
||||||
|
// available (and therefore the partitioned principal), the unpartitioned
|
||||||
|
// cookie jar is only available in first-party or third-party with
|
||||||
|
// storageAccess contexts. In both cases, the Worker will have storage
|
||||||
|
// access.
|
||||||
|
bool isCHIPS = StaticPrefs::network_cookie_CHIPS_enabled() &&
|
||||||
|
workerPrivate->CookieJarSettings()->GetPartitionForeign();
|
||||||
|
bool workerHasStorageAccess =
|
||||||
|
workerPrivate->StorageAccess() == StorageAccess::eAllow;
|
||||||
|
|
||||||
|
if (isCHIPS && workerHasStorageAccess) {
|
||||||
|
// Assert that the cookie principal is unpartitioned.
|
||||||
|
MOZ_ASSERT(
|
||||||
|
cookiePrincipal->OriginAttributesRef().mPartitionKey.IsEmpty());
|
||||||
|
// Only retrieve the partitioned originAttributes if the partitionKey is
|
||||||
|
// set. The partitionKey could be empty for partitionKey in partitioned
|
||||||
|
// originAttributes if the aWorker is for privilege context, such as the
|
||||||
|
// extension's background page.
|
||||||
|
nsCOMPtr<nsIPrincipal> partitionedPrincipal =
|
||||||
|
workerPrivate->GetPartitionedPrincipal();
|
||||||
|
if (partitionedPrincipal && !partitionedPrincipal->OriginAttributesRef()
|
||||||
|
.mPartitionKey.IsEmpty()) {
|
||||||
|
cookiePartitionedPrincipal = partitionedPrincipal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!aDocument) {
|
||||||
|
return SecurityChecksResult::eDoNotContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the document's sandboxed origin flag is set, then reading cookies
|
||||||
|
// is prohibited.
|
||||||
|
if (aDocument->GetSandboxFlags() & SANDBOXED_ORIGIN) {
|
||||||
|
return SecurityChecksResult::eSandboxedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
cookiePrincipal = aDocument->EffectiveCookiePrincipal();
|
||||||
|
if (NS_WARN_IF(!cookiePrincipal) || cookiePrincipal->GetIsNullPrincipal()) {
|
||||||
|
return SecurityChecksResult::eSecurityError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aDocument->CookieAccessDisabled()) {
|
||||||
|
return SecurityChecksResult::eDoNotContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GTests do not create an inner window and because of these a few security
|
||||||
|
// checks will block this method.
|
||||||
|
if (!StaticPrefs::dom_cookie_testing_enabled()) {
|
||||||
|
StorageAccess storageAccess = CookieAllowedForDocument(aDocument);
|
||||||
|
if (storageAccess == StorageAccess::eDeny) {
|
||||||
|
return SecurityChecksResult::eDoNotContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldPartitionStorage(storageAccess) &&
|
||||||
|
!StoragePartitioningEnabled(storageAccess,
|
||||||
|
aDocument->CookieJarSettings())) {
|
||||||
|
return SecurityChecksResult::eDoNotContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the document is a cookie-averse Document... return the empty string.
|
||||||
|
if (aDocument->IsCookieAverse()) {
|
||||||
|
return SecurityChecksResult::eDoNotContinue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHIPS - If CHIPS is enabled the partitioned cookie jar is always
|
||||||
|
// available (and therefore the partitioned principal), the unpartitioned
|
||||||
|
// cookie jar is only available in first-party or third-party with
|
||||||
|
// storageAccess contexts. In both cases, the aDocument will have storage
|
||||||
|
// access.
|
||||||
|
bool isCHIPS = StaticPrefs::network_cookie_CHIPS_enabled() &&
|
||||||
|
aDocument->CookieJarSettings()->GetPartitionForeign();
|
||||||
|
bool documentHasStorageAccess = false;
|
||||||
|
nsresult rv = aDocument->HasStorageAccessSync(documentHasStorageAccess);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return SecurityChecksResult::eDoNotContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCHIPS && documentHasStorageAccess) {
|
||||||
|
// Assert that the cookie principal is unpartitioned.
|
||||||
|
MOZ_ASSERT(
|
||||||
|
cookiePrincipal->OriginAttributesRef().mPartitionKey.IsEmpty());
|
||||||
|
// Only append the partitioned originAttributes if the partitionKey is
|
||||||
|
// set. The partitionKey could be empty for partitionKey in partitioned
|
||||||
|
// originAttributes if the aDocument is for privilege context, such as the
|
||||||
|
// extension's background page.
|
||||||
|
if (!aDocument->PartitionedPrincipal()
|
||||||
|
->OriginAttributesRef()
|
||||||
|
.mPartitionKey.IsEmpty()) {
|
||||||
|
cookiePartitionedPrincipal = aDocument->PartitionedPrincipal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsSchemeSupported(cookiePrincipal)) {
|
||||||
|
return SecurityChecksResult::eDoNotContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cookiePrincipal.forget(aCookiePrincipal);
|
||||||
|
|
||||||
|
if (aCookiePartitionedPrincipal) {
|
||||||
|
cookiePartitionedPrincipal.forget(aCookiePartitionedPrincipal);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SecurityChecksResult::eContinue;
|
||||||
|
}
|
||||||
// static
|
// static
|
||||||
void CookieCommons::GetServerDateHeader(nsIChannel* aChannel,
|
void CookieCommons::GetServerDateHeader(nsIChannel* aChannel,
|
||||||
nsACString& aServerDateHeader) {
|
nsACString& aServerDateHeader) {
|
||||||
|
|
|
@ -69,6 +69,9 @@ class CookieCommons final {
|
||||||
|
|
||||||
static bool PathMatches(Cookie* aCookie, const nsACString& aPath);
|
static bool PathMatches(Cookie* aCookie, const nsACString& aPath);
|
||||||
|
|
||||||
|
static bool PathMatches(const nsACString& aCookiePath,
|
||||||
|
const nsACString& aPath);
|
||||||
|
|
||||||
static nsresult GetBaseDomain(nsIEffectiveTLDService* aTLDService,
|
static nsresult GetBaseDomain(nsIEffectiveTLDService* aTLDService,
|
||||||
nsIURI* aHostURI, nsACString& aBaseDomain,
|
nsIURI* aHostURI, nsACString& aBaseDomain,
|
||||||
bool& aRequireHostMatch);
|
bool& aRequireHostMatch);
|
||||||
|
@ -145,6 +148,26 @@ class CookieCommons final {
|
||||||
|
|
||||||
static void GetServerDateHeader(nsIChannel* aChannel,
|
static void GetServerDateHeader(nsIChannel* aChannel,
|
||||||
nsACString& aServerDateHeader);
|
nsACString& aServerDateHeader);
|
||||||
|
|
||||||
|
enum class SecurityChecksResult {
|
||||||
|
// A sandboxed context detected.
|
||||||
|
eSandboxedError,
|
||||||
|
// A security error needs to be thrown.
|
||||||
|
eSecurityError,
|
||||||
|
// This context should not see cookies without returning errors.
|
||||||
|
eDoNotContinue,
|
||||||
|
// No security issues found. Proceed to expose cookies.
|
||||||
|
eContinue,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Runs the security checks requied by specs on the current context (Document
|
||||||
|
// or Worker) to see if it's allowed to set/get cookies. In case it does
|
||||||
|
// (eContinue), the cookie principals are returned. Use the
|
||||||
|
// `aCookiePartitionedPrincipal` to retrieve CHIP cookies. Use
|
||||||
|
// `aCookiePrincipal` to retrieve non-CHIP cookies.
|
||||||
|
static SecurityChecksResult CheckGlobalAndRetrieveCookiePrincipals(
|
||||||
|
mozilla::dom::Document* aDocument, nsIPrincipal** aCookiePrincipal,
|
||||||
|
nsIPrincipal** aCookiePartitionedPrincipal);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace net
|
} // namespace net
|
||||||
|
|
|
@ -395,7 +395,7 @@ void CookieJarSettings::Serialize(CookieJarSettingsArgs& aData) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ipc::PrincipalInfo principalInfo;
|
mozilla::ipc::PrincipalInfo principalInfo;
|
||||||
rv = PrincipalToPrincipalInfo(principal, &principalInfo,
|
rv = PrincipalToPrincipalInfo(principal, &principalInfo,
|
||||||
true /* aSkipBaseDomain */);
|
true /* aSkipBaseDomain */);
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
|
|
@ -1266,6 +1266,18 @@ CookieService::GetCookiesFromHost(const nsACString& aHost,
|
||||||
JS::Handle<JS::Value> aOriginAttributes,
|
JS::Handle<JS::Value> aOriginAttributes,
|
||||||
JSContext* aCx,
|
JSContext* aCx,
|
||||||
nsTArray<RefPtr<nsICookie>>& aResult) {
|
nsTArray<RefPtr<nsICookie>>& aResult) {
|
||||||
|
OriginAttributes attrs;
|
||||||
|
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
|
||||||
|
return NS_ERROR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetCookiesFromHostNative(aHost, &attrs, aResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
CookieService::GetCookiesFromHostNative(const nsACString& aHost,
|
||||||
|
OriginAttributes* aAttrs,
|
||||||
|
nsTArray<RefPtr<nsICookie>>& aResult) {
|
||||||
// first, normalize the hostname, and fail if it contains illegal characters.
|
// first, normalize the hostname, and fail if it contains illegal characters.
|
||||||
nsAutoCString host(aHost);
|
nsAutoCString host(aHost);
|
||||||
nsresult rv = NormalizeHost(host);
|
nsresult rv = NormalizeHost(host);
|
||||||
|
@ -1275,19 +1287,14 @@ CookieService::GetCookiesFromHost(const nsACString& aHost,
|
||||||
rv = CookieCommons::GetBaseDomainFromHost(mTLDService, host, baseDomain);
|
rv = CookieCommons::GetBaseDomainFromHost(mTLDService, host, baseDomain);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
OriginAttributes attrs;
|
|
||||||
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
|
|
||||||
return NS_ERROR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsInitialized()) {
|
if (!IsInitialized()) {
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
CookieStorage* storage = PickStorage(attrs);
|
CookieStorage* storage = PickStorage(*aAttrs);
|
||||||
|
|
||||||
nsTArray<RefPtr<Cookie>> cookies;
|
nsTArray<RefPtr<Cookie>> cookies;
|
||||||
storage->GetCookiesFromHost(baseDomain, attrs, cookies);
|
storage->GetCookiesFromHost(baseDomain, *aAttrs, cookies);
|
||||||
|
|
||||||
if (cookies.IsEmpty()) {
|
if (cookies.IsEmpty()) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
|
@ -229,6 +229,11 @@ interface nsICookieManager : nsISupports
|
||||||
Array<nsICookie> getCookiesFromHost(in AUTF8String aHost,
|
Array<nsICookie> getCookiesFromHost(in AUTF8String aHost,
|
||||||
in jsval aOriginAttributes);
|
in jsval aOriginAttributes);
|
||||||
|
|
||||||
|
[notxpcom]
|
||||||
|
nsresult getCookiesFromHostNative(in AUTF8String aHost,
|
||||||
|
in OriginAttributesPtr aOriginAttributes,
|
||||||
|
out Array<nsICookie> aCookies);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of all cookies whose origin attributes matches aPattern
|
* Returns an array of all cookies whose origin attributes matches aPattern
|
||||||
*
|
*
|
||||||
|
|
Загрузка…
Ссылка в новой задаче