Bug 1475599 - part 8 - CookieStore API - e10s support, r=smaug,cookie-reviewers,timhuang

Differential Revision: https://phabricator.services.mozilla.com/D217574
This commit is contained in:
Andrea Marchesini 2024-09-12 16:48:21 +00:00
parent c993dc5823
commit 05ebc836d2
18 changed files with 752 additions and 164 deletions

View File

@ -7,6 +7,7 @@
#include "CookieStore.h"
#include "CookieStoreChild.h"
#include "CookieStoreNotifier.h"
#include "CookieStoreNotificationWatcherWrapper.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Promise.h"
@ -219,6 +220,10 @@ already_AddRefed<CookieStore> CookieStore::Create(nsIGlobalObject* aGlobal) {
CookieStore::CookieStore(nsIGlobalObject* aGlobal)
: DOMEventTargetHelper(aGlobal) {
mNotifier = CookieStoreNotifier::Create(this);
// This must be created _after_ CookieStoreNotifier because we rely on the
// notification order.
mNotificationWatcher = CookieStoreNotificationWatcherWrapper::Create(this);
}
CookieStore::~CookieStore() { Shutdown(); }
@ -324,6 +329,21 @@ already_AddRefed<Promise> CookieStore::Set(const CookieInit& aOptions,
return;
}
if (!self->mNotificationWatcher) {
promise->MaybeReject(NS_ERROR_UNEXPECTED);
return;
}
nsID operationID;
rv = nsID::GenerateUUIDInPlace(operationID);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(NS_ERROR_UNEXPECTED);
return;
}
self->mNotificationWatcher->ResolvePromiseWhenNotified(operationID,
promise);
RefPtr<CookieStoreChild::SetRequestPromise> ipcPromise =
self->mActor->SendSetRequest(
aOptions.mDomain.IsEmpty() ? nsString(baseDomain)
@ -336,7 +356,7 @@ already_AddRefed<Promise> CookieStore::Set(const CookieInit& aOptions,
? INT64_MAX
: static_cast<int64_t>(aOptions.mExpires.Value() / 1000),
path, SameSiteToConst(aOptions.mSameSite),
aOptions.mPartitioned);
aOptions.mPartitioned, operationID);
if (NS_WARN_IF(!ipcPromise)) {
promise->MaybeResolveWithUndefined();
return;
@ -344,9 +364,15 @@ already_AddRefed<Promise> CookieStore::Set(const CookieInit& aOptions,
ipcPromise->Then(
NS_GetCurrentThread(), __func__,
[promise = RefPtr<dom::Promise>(promise)](
[promise = RefPtr<dom::Promise>(promise), self = RefPtr(self),
operationID](
const CookieStoreChild::SetRequestPromise::ResolveOrRejectValue&
aResult) { promise->MaybeResolveWithUndefined(); });
aResult) {
if (!aResult.ResolveValue()) {
self->mNotificationWatcher->ForgetOperationID(operationID);
promise->MaybeResolveWithUndefined();
}
});
}));
return promise.forget();
@ -416,24 +442,44 @@ already_AddRefed<Promise> CookieStore::Delete(
return;
}
if (!self->mNotificationWatcher) {
promise->MaybeReject(NS_ERROR_UNEXPECTED);
return;
}
nsID operationID;
rv = nsID::GenerateUUIDInPlace(operationID);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(NS_ERROR_UNEXPECTED);
return;
}
self->mNotificationWatcher->ResolvePromiseWhenNotified(operationID,
promise);
RefPtr<CookieStoreChild::DeleteRequestPromise> ipcPromise =
self->mActor->SendDeleteRequest(
aOptions.mDomain.IsEmpty() ? nsString(baseDomain)
: nsString(aOptions.mDomain),
cookiePrincipal->OriginAttributesRef(),
nsString(aOptions.mName), path, aOptions.mPartitioned);
nsString(aOptions.mName), path, aOptions.mPartitioned,
operationID);
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();
});
ipcPromise->Then(
NS_GetCurrentThread(), __func__,
[promise = RefPtr<dom::Promise>(promise), self = RefPtr(self),
operationID](const CookieStoreChild::DeleteRequestPromise::
ResolveOrRejectValue& aResult) {
MOZ_ASSERT(aResult.IsResolve());
if (!aResult.ResolveValue()) {
self->mNotificationWatcher->ForgetOperationID(operationID);
promise->MaybeResolveWithUndefined();
}
});
}));
return promise.forget();

View File

@ -17,6 +17,7 @@ namespace mozilla::dom {
class CookieData;
class CookieStoreChild;
class CookieStoreNotificationWatcherWrapper;
class CookieStoreNotifier;
class Promise;
@ -72,6 +73,7 @@ class CookieStore final : public DOMEventTargetHelper {
RefPtr<CookieStoreChild> mActor;
RefPtr<CookieStoreNotifier> mNotifier;
RefPtr<CookieStoreNotificationWatcherWrapper> mNotificationWatcher;
};
} // namespace mozilla::dom

View File

@ -0,0 +1,129 @@
/* -*- 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 "CookieStoreNotificationWatcher.h"
#include "mozilla/Services.h"
#include "mozilla/Unused.h"
#include "nsICookie.h"
#include "nsICookieNotification.h"
#include "nsIObserverService.h"
namespace mozilla::dom {
NS_IMPL_ISUPPORTS(CookieStoreNotificationWatcher, nsIObserver,
nsISupportsWeakReference)
// static
already_AddRefed<CookieStoreNotificationWatcher>
CookieStoreNotificationWatcher::Create(bool aPrivateBrowsing) {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<CookieStoreNotificationWatcher> watcher =
new CookieStoreNotificationWatcher();
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (NS_WARN_IF(!os)) {
return nullptr;
}
nsresult rv = os->AddObserver(
watcher, aPrivateBrowsing ? "private-cookie-changed" : "cookie-changed",
true);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return watcher.forget();
}
NS_IMETHODIMP
CookieStoreNotificationWatcher::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);
nsID* operationID = nullptr;
nsresult rv = notification->GetOperationID(&operationID);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
if (!operationID) {
return NS_OK;
}
for (uint32_t i = 0; i < mPendingOperations.Length(); ++i) {
PendingOperation& pendingOperation = mPendingOperations[i];
if (pendingOperation.mOperationID.Equals(*operationID)) {
pendingOperation.mCallback();
mPendingOperations.RemoveElementAt(i);
break;
}
}
return NS_OK;
}
void CookieStoreNotificationWatcher::CallbackWhenNotified(
const nsID& aOperationID, MoveOnlyFunction<void()> aCallback) {
MOZ_ASSERT(NS_IsMainThread());
mPendingOperations.AppendElement(
PendingOperation{std::move(aCallback), aOperationID});
}
void CookieStoreNotificationWatcher::ForgetOperationID(
const nsID& aOperationID) {
MOZ_ASSERT(NS_IsMainThread());
for (uint32_t i = 0; i < mPendingOperations.Length(); ++i) {
PendingOperation& pendingOperation = mPendingOperations[i];
if (pendingOperation.mOperationID.Equals(aOperationID)) {
mPendingOperations.RemoveElementAt(i);
return;
}
}
}
// static
void CookieStoreNotificationWatcher::ReleaseOnMainThread(
already_AddRefed<CookieStoreNotificationWatcher> aWatcher) {
RefPtr<CookieStoreNotificationWatcher> watcher(aWatcher);
if (!watcher || NS_IsMainThread()) {
return;
}
class ReleaseWatcher final : public Runnable {
public:
explicit ReleaseWatcher(
already_AddRefed<CookieStoreNotificationWatcher> aWatcher)
: Runnable("ReleaseWatcher"), mDoomed(std::move(aWatcher)) {}
NS_IMETHOD Run() override {
mDoomed = nullptr;
return NS_OK;
}
private:
~ReleaseWatcher() {
// If we still have to release the watcher, better to leak it.
if (mDoomed) {
Unused << mDoomed.forget().take();
}
}
RefPtr<CookieStoreNotificationWatcher> mDoomed;
};
RefPtr<ReleaseWatcher> runnable(new ReleaseWatcher(watcher.forget()));
NS_DispatchToMainThread(runnable);
}
} // namespace mozilla::dom

View File

@ -0,0 +1,51 @@
/* -*- 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_CookieStoreNotificationWatcher_h
#define mozilla_dom_CookieStoreNotificationWatcher_h
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "mozilla/OriginAttributes.h"
#include "mozilla/MoveOnlyFunction.h"
namespace mozilla::dom {
class CookieStoreNotificationWatcher final : public nsIObserver,
public nsSupportsWeakReference {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
static already_AddRefed<CookieStoreNotificationWatcher> Create(
bool aPrivateBrowsing);
static void ReleaseOnMainThread(
already_AddRefed<CookieStoreNotificationWatcher> aWatcher);
void CallbackWhenNotified(const nsID& aOperationID,
MoveOnlyFunction<void()> aCallback);
void ForgetOperationID(const nsID& aOperationID);
private:
CookieStoreNotificationWatcher() = default;
~CookieStoreNotificationWatcher() = default;
void MaybeResolveOperations(const nsID* aOperationID);
struct PendingOperation {
MoveOnlyFunction<void()> mCallback;
nsID mOperationID;
};
// This is a simple list because I don't think we will have so many concurrent
// operations to motivate an hash table.
nsTArray<PendingOperation> mPendingOperations;
};
} // namespace mozilla::dom
#endif /* mozilla_dom_CookieStoreNotificationWatcher_h */

View File

@ -0,0 +1,158 @@
/* -*- 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 "CookieStoreNotificationWatcherWrapper.h"
#include "CookieStoreNotificationWatcher.h"
#include "CookieStore.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRef.h"
#include "nsProxyRelease.h"
namespace mozilla::dom {
// static
already_AddRefed<CookieStoreNotificationWatcherWrapper>
CookieStoreNotificationWatcherWrapper::Create(CookieStore* aCookieStore) {
MOZ_ASSERT(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;
}
RefPtr<CookieStoreNotificationWatcherWrapper> wrapper =
new CookieStoreNotificationWatcherWrapper();
bool privateBrowsing = principal->OriginAttributesRef().IsPrivateBrowsing();
if (!NS_IsMainThread()) {
NS_DispatchToMainThread(
NS_NewRunnableFunction(__func__, [wrapper, privateBrowsing] {
wrapper->CreateWatcherOnMainThread(privateBrowsing);
}));
} else {
wrapper->CreateWatcherOnMainThread(privateBrowsing);
}
return wrapper.forget();
}
CookieStoreNotificationWatcherWrapper::
~CookieStoreNotificationWatcherWrapper() {
CookieStoreNotificationWatcher::ReleaseOnMainThread(
mWatcherOnMainThread.forget());
}
void CookieStoreNotificationWatcherWrapper::CreateWatcherOnMainThread(
bool aPrivateBrowsing) {
MOZ_ASSERT(NS_IsMainThread());
mWatcherOnMainThread =
CookieStoreNotificationWatcher::Create(aPrivateBrowsing);
}
void CookieStoreNotificationWatcherWrapper::ForgetOperationID(
const nsID& aOperationID) {
if (!NS_IsMainThread()) {
NS_DispatchToMainThread(
NS_NewRunnableFunction(__func__, [self = RefPtr(this), aOperationID] {
self->ForgetOperationID(aOperationID);
}));
return;
}
if (mWatcherOnMainThread) {
mWatcherOnMainThread->ForgetOperationID(aOperationID);
}
}
void CookieStoreNotificationWatcherWrapper::ResolvePromiseWhenNotified(
const nsID& aOperationID, Promise* aPromise) {
MOZ_ASSERT(aPromise);
class PromiseResolver final : public Runnable {
public:
explicit PromiseResolver(Promise* aPromise)
: Runnable("CookieStoreNotificationWatcherWrapper::PromiseResolver"),
mPromise(aPromise),
mEventTarget(GetCurrentSerialEventTarget()) {}
NS_IMETHOD Run() override {
mPromise->MaybeResolveWithUndefined();
mPromise = nullptr;
return NS_OK;
}
bool HasPromise() const { return !!mPromise; }
private:
~PromiseResolver() {
NS_ProxyRelease(
"CookieStoreNotificationWatcherWrapper::PromiseResolver::mPromise",
mEventTarget, mPromise.forget());
}
RefPtr<Promise> mPromise;
RefPtr<nsISerialEventTarget> mEventTarget;
};
RefPtr<PromiseResolver> resolver(new PromiseResolver(aPromise));
RefPtr<ThreadSafeWorkerRef> workerRef;
if (!NS_IsMainThread()) {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
RefPtr<StrongWorkerRef> strongWorkerRef = StrongWorkerRef::Create(
workerPrivate, "CookieStoreNotificationWatcher::PromiseResolver",
[resolver = RefPtr(resolver)]() { resolver->Run(); });
workerRef = new ThreadSafeWorkerRef(strongWorkerRef);
}
auto callback = [resolver = RefPtr(resolver),
eventTarget = RefPtr(GetCurrentSerialEventTarget()),
workerRef = RefPtr(workerRef)] {
if (resolver->HasPromise()) {
RefPtr<Runnable> runnable(resolver);
eventTarget->Dispatch(runnable.forget());
}
};
if (!NS_IsMainThread()) {
NS_DispatchToMainThread(NS_NewRunnableFunction(
__func__, [self = RefPtr(this), callback, aOperationID] {
self->mWatcherOnMainThread->CallbackWhenNotified(aOperationID,
callback);
}));
return;
}
if (mWatcherOnMainThread) {
mWatcherOnMainThread->CallbackWhenNotified(aOperationID, callback);
}
}
} // namespace mozilla::dom

View File

@ -0,0 +1,39 @@
/* -*- 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_CookieStoreNotificationWatcherWrapper_h
#define mozilla_dom_CookieStoreNotificationWatcherWrapper_h
#include "mozilla/OriginAttributes.h"
namespace mozilla::dom {
class Promise;
class CookieStore;
class CookieStoreNotificationWatcher;
class CookieStoreNotificationWatcherWrapper final {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CookieStoreNotificationWatcherWrapper)
static already_AddRefed<CookieStoreNotificationWatcherWrapper> Create(
CookieStore* aCookieStore);
void ResolvePromiseWhenNotified(const nsID& aOperationID, Promise* aPromise);
void ForgetOperationID(const nsID& aOperationID);
private:
CookieStoreNotificationWatcherWrapper() = default;
~CookieStoreNotificationWatcherWrapper();
void CreateWatcherOnMainThread(bool aPrivateBrowsing);
RefPtr<CookieStoreNotificationWatcher> mWatcherOnMainThread;
};
} // namespace mozilla::dom
#endif /* mozilla_dom_CookieStoreNotificationWatcherWrapper_h */

View File

@ -6,6 +6,7 @@
#include "CookieStoreNotifier.h"
#include "CookieStore.h"
#include "CookieChangeEvent.h"
#include "mozilla/net/CookieCommons.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/WorkerPrivate.h"
@ -192,46 +193,56 @@ CookieStoreNotifier::Observe(nsISupports* aSubject, const char* aTopic,
bool deletedEvent = action == nsICookieNotification::COOKIE_DELETED;
mEventTarget->Dispatch(NS_NewRunnableFunction(__func__, [self = RefPtr(this),
item, deletedEvent] {
if (!self->mCookieStore) {
return;
}
if (mEventTarget->IsOnCurrentThread()) {
DispatchEvent(item, deletedEvent);
} else {
mEventTarget->Dispatch(NS_NewRunnableFunction(
__func__, [self = RefPtr(this), item, deletedEvent] {
self->DispatchEvent(item, deletedEvent);
}));
}
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() || (window->GetExtantDoc() &&
window->GetExtantDoc()->GetBFCacheEntry())) {
self->mDelayedDOMEvents.AppendElement(event);
return;
}
}
self->mCookieStore->DispatchEvent(*event);
}));
return NS_OK;
}
void CookieStoreNotifier::DispatchEvent(const CookieListItem& aItem,
bool aDeletedEvent) {
MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
if (!mCookieStore) {
return;
}
RefPtr<Event> event =
aDeletedEvent
? CookieChangeEvent::CreateForDeletedCookie(mCookieStore, aItem)
: CookieChangeEvent::CreateForChangedCookie(mCookieStore, aItem);
if (!event) {
return;
}
if (NS_IsMainThread()) {
nsCOMPtr<nsPIDOMWindowInner> window = mCookieStore->GetOwnerWindow();
if (!window) {
return;
}
RefPtr<BrowsingContext> bc = window->GetBrowsingContext();
if (!bc) {
return;
}
if (bc->IsInBFCache() ||
(window->GetExtantDoc() && window->GetExtantDoc()->GetBFCacheEntry())) {
mDelayedDOMEvents.AppendElement(event);
return;
}
}
mCookieStore->DispatchEvent(*event);
}
void CookieStoreNotifier::FireDelayedDOMEvents() {
MOZ_ASSERT(NS_IsMainThread());

View File

@ -9,6 +9,7 @@
#include "nsIObserver.h"
#include "mozilla/OriginAttributes.h"
#include "mozilla/MoveOnlyFunction.h"
class nsISerialEventTarget;
@ -38,6 +39,8 @@ class CookieStoreNotifier final : public nsIObserver {
void AddObserversOnMainThread(bool aPrivateBrowsing);
void RemoveObserversOnMainThread(bool aPrivateBrowsing);
void DispatchEvent(const CookieListItem& aItem, bool aDeletedEvent);
// Raw pointer because this object is kept alive by this CookieStore object.
CookieStore* mCookieStore;

View File

@ -5,23 +5,116 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CookieStoreParent.h"
#include "CookieStoreNotificationWatcher.h"
#include "mozilla/Maybe.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/net/CookieCommons.h"
#include "mozilla/Unused.h"
#include "nsICookieManager.h"
#include "nsProxyRelease.h"
using namespace mozilla::ipc;
namespace mozilla::dom {
namespace {
CookieStoreParent::CookieStoreParent() { AssertIsOnBackgroundThread(); }
void GetRequestHelper(const nsAString& aDomain,
const OriginAttributes& aOriginAttributes,
bool aMatchName, const nsAString& aName,
const nsACString& aPath, bool aOnlyFirstMatch,
nsTArray<CookieData>& aResults) {
CookieStoreParent::~CookieStoreParent() {
AssertIsOnBackgroundThread();
CookieStoreNotificationWatcher::ReleaseOnMainThread(
mNotificationWatcherOnMainThread.forget());
}
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) {
AssertIsOnBackgroundThread();
InvokeAsync(GetMainThreadSerialEventTarget(), __func__,
[self = RefPtr(this), aDomain, aOriginAttributes, aMatchName,
aName, aPath, aOnlyFirstMatch]() {
CopyableTArray<CookieData> results;
self->GetRequestOnMainThread(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, const nsID& aOperationID,
SetRequestResolver&& aResolver) {
AssertIsOnBackgroundThread();
InvokeAsync(
GetMainThreadSerialEventTarget(), __func__,
[self = RefPtr(this), aDomain, aOriginAttributes, aName, aValue, aSession,
aExpires, aPath, aSameSite, aPartitioned, aOperationID]() {
bool waitForNotification = self->SetRequestOnMainThread(
aDomain, aOriginAttributes, aName, aValue, aSession, aExpires,
aPath, aSameSite, aPartitioned, aOperationID);
return SetDeleteRequestPromise::CreateAndResolve(waitForNotification,
__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,
const nsID& aOperationID, DeleteRequestResolver&& aResolver) {
AssertIsOnBackgroundThread();
InvokeAsync(GetMainThreadSerialEventTarget(), __func__,
[self = RefPtr(this), aDomain, aOriginAttributes, aName, aPath,
aPartitioned, aOperationID]() {
bool waitForNotification = self->DeleteRequestOnMainThread(
aDomain, aOriginAttributes, aName, aPath, aPartitioned,
aOperationID);
return SetDeleteRequestPromise::CreateAndResolve(
waitForNotification, __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();
}
void CookieStoreParent::GetRequestOnMainThread(
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 =
@ -90,19 +183,30 @@ void GetRequestHelper(const nsAString& aDomain,
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) {
bool CookieStoreParent::SetRequestOnMainThread(
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, const nsID& aOperationID) {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsICookieManager> service =
do_GetService(NS_COOKIEMANAGER_CONTRACTID);
if (!service) {
return;
return false;
}
bool notified = false;
auto notificationCb = [&]() { notified = true; };
CookieStoreNotificationWatcher* notificationWatcher =
GetOrCreateNotificationWatcherOnMainThread(aOriginAttributes);
if (!notificationWatcher) {
return false;
}
notificationWatcher->CallbackWhenNotified(aOperationID, notificationCb);
OriginAttributes attrs(aOriginAttributes);
nsresult rv = service->AddNative(
NS_ConvertUTF16toUTF8(aDomain), NS_ConvertUTF16toUTF8(aPath),
@ -110,22 +214,26 @@ void SetRequestHelper(const nsAString& aDomain,
true, // secure
false, // mHttpOnly,
aSession, aSession ? PR_Now() : aExpires, &attrs, aSameSite,
nsICookie::SCHEME_HTTPS, aPartitioned, nullptr);
nsICookie::SCHEME_HTTPS, aPartitioned, &aOperationID);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
return false;
}
notificationWatcher->ForgetOperationID(aOperationID);
return notified;
}
void DeleteRequestHelper(const nsAString& aDomain,
const OriginAttributes& aOriginAttributes,
const nsAString& aName, const nsAString& aPath,
bool aPartitioned) {
bool CookieStoreParent::DeleteRequestOnMainThread(
const nsAString& aDomain, const OriginAttributes& aOriginAttributes,
const nsAString& aName, const nsAString& aPath, bool aPartitioned,
const nsID& aOperationID) {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsICookieManager> service =
do_GetService(NS_COOKIEMANAGER_CONTRACTID);
if (!service) {
return;
return false;
}
NS_ConvertUTF16toUTF8 domainUtf8(aDomain);
@ -134,7 +242,7 @@ void DeleteRequestHelper(const nsAString& aDomain,
nsTArray<RefPtr<nsICookie>> results;
nsresult rv = service->GetCookiesFromHostNative(domainUtf8, &attrs, results);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
return false;
}
NS_ConvertUTF16toUTF8 matchName(aName);
@ -146,7 +254,7 @@ void DeleteRequestHelper(const nsAString& aDomain,
nsAutoCString name;
rv = cookie->GetName(name);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
return false;
}
if (!matchName.Equals(name)) {
@ -156,7 +264,7 @@ void DeleteRequestHelper(const nsAString& aDomain,
nsAutoCString path;
rv = cookie->GetPath(path);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
return false;
}
if (!matchPath.IsEmpty() && !matchPath.Equals(path)) {
@ -166,97 +274,47 @@ void DeleteRequestHelper(const nsAString& aDomain,
bool isPartitioned = false;
rv = cookie->GetIsPartitioned(&isPartitioned);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
return false;
}
if (isPartitioned != aPartitioned) continue;
rv = service->RemoveNative(domainUtf8, matchName, path, &attrs, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
bool notified = false;
auto notificationCb = [&]() { notified = true; };
CookieStoreNotificationWatcher* notificationWatcher =
GetOrCreateNotificationWatcherOnMainThread(aOriginAttributes);
if (!notificationWatcher) {
return false;
}
notificationWatcher->CallbackWhenNotified(aOperationID, notificationCb);
rv = service->RemoveNative(domainUtf8, matchName, path, &attrs,
&aOperationID);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
notificationWatcher->ForgetOperationID(aOperationID);
return notified;
}
return false;
}
} // namespace
CookieStoreNotificationWatcher*
CookieStoreParent::GetOrCreateNotificationWatcherOnMainThread(
const OriginAttributes& aOriginAttributes) {
MOZ_ASSERT(NS_IsMainThread());
CookieStoreParent::CookieStoreParent() { AssertIsOnBackgroundThread(); }
if (!mNotificationWatcherOnMainThread) {
mNotificationWatcherOnMainThread = CookieStoreNotificationWatcher::Create(
aOriginAttributes.IsPrivateBrowsing());
}
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();
return mNotificationWatcherOnMainThread;
}
} // namespace mozilla::dom

View File

@ -12,7 +12,7 @@
namespace mozilla::dom {
class CookieStoreService;
class CookieStoreNotificationWatcher;
class CookieStoreParent final : public PCookieStoreParent {
friend class PCookieStoreParent;
@ -22,7 +22,7 @@ class CookieStoreParent final : public PCookieStoreParent {
MozPromise<CopyableTArray<CookieData>, nsresult, true>;
using SetDeleteRequestPromise = MozPromise<bool, nsresult, true>;
NS_INLINE_DECL_REFCOUNTING(CookieStoreParent)
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CookieStoreParent)
CookieStoreParent();
@ -38,14 +38,42 @@ class CookieStoreParent final : public PCookieStoreParent {
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);
const bool& aPartitioned, const nsID& aOperationID,
SetRequestResolver&& aResolver);
mozilla::ipc::IPCResult RecvDeleteRequest(
const nsString& aDomain, const OriginAttributes& aOriginAttributes,
const nsString& aName, const nsString& aPath, const bool& aPartitioned,
DeleteRequestResolver&& aResolver);
const nsID& aOperationID, DeleteRequestResolver&& aResolver);
mozilla::ipc::IPCResult RecvClose();
void GetRequestOnMainThread(const nsAString& aDomain,
const OriginAttributes& aOriginAttributes,
bool aMatchName, const nsAString& aName,
const nsACString& aPath, bool aOnlyFirstMatch,
nsTArray<CookieData>& aResults);
// Returns true if a cookie notification has been generated while completing
// the operation.
bool SetRequestOnMainThread(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, const nsID& aOperationID);
// Returns true if a cookie notification has been generated while completing
// the operation.
bool DeleteRequestOnMainThread(const nsAString& aDomain,
const OriginAttributes& aOriginAttributes,
const nsAString& aName, const nsAString& aPath,
bool aPartitioned, const nsID& aOperationID);
CookieStoreNotificationWatcher* GetOrCreateNotificationWatcherOnMainThread(
const OriginAttributes& aOriginAttributes);
RefPtr<CookieStoreNotificationWatcher> mNotificationWatcherOnMainThread;
};
} // namespace mozilla::dom

View File

@ -6,6 +6,7 @@ include protocol PBackground;
using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
using struct nsID from "nsID.h";
namespace mozilla {
namespace dom {
@ -44,13 +45,15 @@ parent:
int64_t expires,
nsString path,
int32_t sameSite,
bool partitioned) returns (bool v);
bool partitioned,
nsID operationId) returns (bool waitForNotification);
async DeleteRequest(nsString domain,
OriginAttributes attrs,
nsString name,
nsString path,
bool partitioned) returns (bool v);
bool partitioned,
nsID operationID) returns (bool waitForNotification);
async Close();

View File

@ -16,6 +16,8 @@ UNIFIED_SOURCES += [
"CookieChangeEvent.cpp",
"CookieStore.cpp",
"CookieStoreChild.cpp",
"CookieStoreNotificationWatcher.cpp",
"CookieStoreNotificationWatcherWrapper.cpp",
"CookieStoreNotifier.cpp",
"CookieStoreParent.cpp",
]

View File

@ -3830,6 +3830,8 @@ ContentParent::Observe(nsISupports* aSubject, const char* aTopic,
return NS_OK;
}
auto* cs = static_cast<CookieServiceParent*>(csParent);
MOZ_ASSERT(mCookieInContentListCache.IsEmpty());
if (action == nsICookieNotification::COOKIES_BATCH_DELETED) {
nsCOMPtr<nsIArray> cookieList;
DebugOnly<nsresult> rv =
@ -6124,6 +6126,17 @@ nsresult ContentParent::TransmitPermissionsForPrincipal(
return NS_OK;
}
void ContentParent::AddPrincipalToCookieInProcessCache(
nsIPrincipal* aPrincipal) {
MOZ_ASSERT(aPrincipal);
mCookieInContentListCache.AppendElement(aPrincipal);
}
void ContentParent::TakeCookieInProcessCache(
nsTArray<nsCOMPtr<nsIPrincipal>>& aList) {
aList.SwapElements(mCookieInContentListCache);
}
void ContentParent::TransmitBlobURLsForPrincipal(nsIPrincipal* aPrincipal) {
// If we're already broadcasting BlobURLs with this principal, we don't need
// to send them here.

View File

@ -629,6 +629,12 @@ class ContentParent final : public PContentParent,
// to this content process forever.
void TransmitBlobURLsForPrincipal(nsIPrincipal* aPrincipal);
// Update a cache list of allowed domains to store cookies for the current
// process. This method is called when PCookieServiceParent actor is not
// available yet.
void AddPrincipalToCookieInProcessCache(nsIPrincipal* aPrincipal);
void TakeCookieInProcessCache(nsTArray<nsCOMPtr<nsIPrincipal>>& aList);
nsresult TransmitPermissionsForPrincipal(nsIPrincipal* aPrincipal);
// Whenever receiving a Principal we need to validate that Principal case
@ -1567,6 +1573,8 @@ class ContentParent final : public PContentParent,
nsTArray<nsCString> mBlobURLs;
nsTArray<nsCOMPtr<nsIPrincipal>> mCookieInContentListCache;
// This is intended to be a memory and time efficient means of determining
// whether an origin has ever existed in a process so that Blob URL broadcast
// doesn't need to transmit every Blob URL to every content process. False

View File

@ -17,6 +17,9 @@
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundParent.h"
#include "mozilla/net/CookieServiceParent.h"
#include "mozilla/net/NeckoParent.h"
#include "mozilla/net/CookieServiceParent.h"
#include "mozilla/StaticPrefs_extensions.h"
#include "nsCOMPtr.h"
#include "nsImportModule.h"
@ -36,6 +39,7 @@ mozilla::LazyLogModule gRemoteWorkerManagerLog("RemoteWorkerManager");
namespace mozilla {
using namespace ipc;
using namespace net;
namespace dom {
@ -50,7 +54,7 @@ bool IsServiceWorker(const RemoteWorkerData& aData) {
OptionalServiceWorkerData::TServiceWorkerData;
}
void TransmitPermissionsAndBlobURLsForPrincipalInfo(
void TransmitPermissionsAndCookiesAndBlobURLsForPrincipalInfo(
ContentParent* aContentParent, const PrincipalInfo& aPrincipalInfo) {
AssertIsOnMainThread();
MOZ_ASSERT(aContentParent);
@ -67,6 +71,25 @@ void TransmitPermissionsAndBlobURLsForPrincipalInfo(
MOZ_ALWAYS_SUCCEEDS(
aContentParent->TransmitPermissionsForPrincipal(principal));
CookieServiceParent* cs = nullptr;
PNeckoParent* neckoParent =
LoneManagedOrNullAsserts(aContentParent->ManagedPNeckoParent());
if (neckoParent) {
PCookieServiceParent* csParent =
LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
if (csParent) {
cs = static_cast<CookieServiceParent*>(csParent);
}
}
if (cs) {
nsCOMPtr<nsIURI> uri = principal->GetURI();
cs->UpdateCookieInContentList(uri, principal->OriginAttributesRef());
} else {
aContentParent->AddPrincipalToCookieInProcessCache(principal);
}
}
} // namespace
@ -313,8 +336,8 @@ void RemoteWorkerManager::LaunchInternal(
AssertIsOnMainThread();
if (RefPtr<ContentParent> contentParent =
contentHandle->GetContentParent()) {
TransmitPermissionsAndBlobURLsForPrincipalInfo(contentParent,
principalInfo);
TransmitPermissionsAndCookiesAndBlobURLsForPrincipalInfo(
contentParent, principalInfo);
}
});

View File

@ -6,9 +6,9 @@
#include "CookieCommons.h"
#include "CookieLogging.h"
#include "CookieServiceParent.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/net/CookieService.h"
#include "mozilla/net/CookieServiceParent.h"
#include "mozilla/net/NeckoParent.h"
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/StoragePrincipalHelper.h"
@ -25,7 +25,9 @@ using namespace mozilla::ipc;
namespace mozilla {
namespace net {
CookieServiceParent::CookieServiceParent() {
CookieServiceParent::CookieServiceParent(dom::ContentParent* aContentParent) {
MOZ_ASSERT(aContentParent);
// Instantiate the cookieservice via the service manager, so it sticks around
// until shutdown.
nsCOMPtr<nsICookieService> cs = do_GetService(NS_COOKIESERVICE_CONTRACTID);
@ -38,6 +40,14 @@ CookieServiceParent::CookieServiceParent() {
MOZ_ALWAYS_TRUE(mTLDService);
mProcessingCookie = false;
nsTArray<nsCOMPtr<nsIPrincipal>> list;
aContentParent->TakeCookieInProcessCache(list);
for (nsIPrincipal* principal : list) {
nsCOMPtr<nsIURI> uri = principal->GetURI();
UpdateCookieInContentList(uri, principal->OriginAttributesRef());
}
}
void CookieServiceParent::RemoveBatchDeletedCookies(nsIArray* aCookieList) {

View File

@ -13,7 +13,11 @@ class nsIArray;
class nsICookie;
namespace mozilla {
class OriginAttributes;
}
namespace dom {
class ContentParent;
} // namespace dom
} // namespace mozilla
class nsIEffectiveTLDService;
@ -27,7 +31,7 @@ class CookieServiceParent : public PCookieServiceParent {
friend class PCookieServiceParent;
public:
CookieServiceParent();
explicit CookieServiceParent(dom::ContentParent* aContentParent);
virtual ~CookieServiceParent() = default;
void TrackCookieLoad(nsIChannel* aChannel);

View File

@ -287,7 +287,7 @@ mozilla::ipc::IPCResult NeckoParent::RecvPDocumentChannelConstructor(
}
PCookieServiceParent* NeckoParent::AllocPCookieServiceParent() {
return new CookieServiceParent();
return new CookieServiceParent(static_cast<ContentParent*>(Manager()));
}
bool NeckoParent::DeallocPCookieServiceParent(PCookieServiceParent* cs) {