Bug 1725941 - Add AbortSignal support for locks.request() r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D128000
This commit is contained in:
Kagami Sascha Rosylight 2021-10-19 12:01:51 +00:00
Родитель 4839800b46
Коммит f6b5ef8ffa
10 изменённых файлов: 51 добавлений и 99 удалений

Просмотреть файл

@ -50,7 +50,7 @@ Promise& Lock::GetWaitingPromise() {
void Lock::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) {
if (mLockRequestChild) {
locks::PLockRequestChild::Send__delete__(mLockRequestChild);
locks::PLockRequestChild::Send__delete__(mLockRequestChild, false);
mLockRequestChild = nullptr;
}
mReleasedPromise->MaybeResolve(aValue);
@ -58,7 +58,7 @@ void Lock::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) {
void Lock::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) {
if (mLockRequestChild) {
locks::PLockRequestChild::Send__delete__(mLockRequestChild);
locks::PLockRequestChild::Send__delete__(mLockRequestChild, false);
mLockRequestChild = nullptr;
}
mReleasedPromise->MaybeReject(aValue);

Просмотреть файл

@ -125,12 +125,6 @@ already_AddRefed<Promise> LockManager::Request(const nsAString& aName,
return nullptr;
}
if (aOptions.mSignal.WasPassed()) {
// TODO(krosylight): Bug 1725941
aRv.ThrowNotSupportedError("AbortSignal support is not implemented yet");
return nullptr;
}
if (!mActor) {
// TODO: https://github.com/WICG/web-locks/issues/78
aRv.ThrowInvalidStateError(

Просмотреть файл

@ -15,7 +15,7 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(LockManagerChild, Release)
void LockManagerChild::RequestLock(const LockRequest& aRequest,
const LockOptions& aOptions) {
auto requestActor = MakeRefPtr<LockRequestChild>(aRequest);
auto requestActor = MakeRefPtr<LockRequestChild>(aRequest, aOptions.mSignal);
SendPLockRequestConstructor(
requestActor, IPCLockRequest(nsString(aRequest.mName), aOptions.mMode,
aOptions.mIfAvailable, aOptions.mSteal));

Просмотреть файл

@ -14,6 +14,8 @@ namespace mozilla::dom::locks {
using IPCResult = mozilla::ipc::IPCResult;
NS_IMPL_ISUPPORTS(LockRequestChild, nsISupports)
// XXX: should be MOZ_CAN_RUN_SCRIPT, but not sure how to call it from closures
MOZ_CAN_RUN_SCRIPT_BOUNDARY static void RunCallbackAndSettlePromise(
LockGrantedCallback& aCallback, mozilla::dom::Lock* lock,
@ -37,8 +39,13 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY static void RunCallbackAndSettlePromise(
MOZ_ASSERT(!rv.Failed());
}
LockRequestChild::LockRequestChild(const LockRequest& aRequest)
LockRequestChild::LockRequestChild(
const LockRequest& aRequest,
const Optional<OwningNonNull<AbortSignal>>& aSignal)
: mRequest(aRequest) {
if (aSignal.WasPassed()) {
Follow(&aSignal.Value());
}
if (!NS_IsMainThread()) {
mWorkerRef = StrongWorkerRef::Create(
GetCurrentThreadWorkerPrivate(), "LockManager",
@ -48,6 +55,8 @@ LockRequestChild::LockRequestChild(const LockRequest& aRequest)
IPCResult LockRequestChild::RecvResolve(const LockMode& aLockMode,
bool aIsAvailable) {
Unfollow();
RefPtr<Lock> lock;
RefPtr<Promise> promise;
if (aIsAvailable) {
@ -75,8 +84,14 @@ IPCResult LockRequestChild::RecvResolve(const LockMode& aLockMode,
}
IPCResult LockRequestChild::RecvAbort() {
Unfollow();
mRequest.mPromise->MaybeRejectWithAbortError("The lock request is aborted");
return IPC_OK();
}
void LockRequestChild::RunAbortAlgorithm() {
RecvAbort();
Send__delete__(this, true);
}
} // namespace mozilla::dom::locks

Просмотреть файл

@ -10,6 +10,7 @@
#include "mozilla/dom/locks/PLockRequestChild.h"
#include "mozilla/dom/Lock.h"
#include "mozilla/dom/WorkerRef.h"
#include "nsISupportsImpl.h"
namespace mozilla::dom::locks {
@ -20,17 +21,22 @@ struct LockRequest {
};
class LockRequestChild final : public PLockRequestChild,
public AbortFollower,
public SupportsWeakPtr {
using IPCResult = mozilla::ipc::IPCResult;
NS_INLINE_DECL_REFCOUNTING(LockRequestChild)
NS_DECL_ISUPPORTS
public:
explicit LockRequestChild(const LockRequest& aRequest);
explicit LockRequestChild(
const LockRequest& aRequest,
const Optional<OwningNonNull<AbortSignal>>& aSignal);
IPCResult RecvResolve(const LockMode& aLockMode, bool aIsAvailable);
IPCResult RecvAbort();
void RunAbortAlgorithm() final;
private:
~LockRequestChild() = default;

Просмотреть файл

@ -11,15 +11,19 @@
namespace mozilla::dom::locks {
mozilla::ipc::IPCResult LockRequestParent::Recv__delete__() {
mozilla::ipc::IPCResult LockRequestParent::Recv__delete__(bool aAborted) {
RefPtr<LockManagerParent> manager =
static_cast<LockManagerParent*>(Manager());
ManagedLocks& managed = manager->Locks();
DebugOnly<bool> unheld = managed.mHeldLocks.RemoveElement(this);
MOZ_ASSERT(unheld, "No held lock?");
MOZ_ASSERT_IF(!aAborted, unheld);
if (auto queue = managed.mQueueMap.Lookup(mRequest.name())) {
if (aAborted) {
DebugOnly<bool> dequeued = queue.Data().RemoveElement(this);
MOZ_ASSERT_IF(!unheld, dequeued);
}
manager->ProcessRequestQueue(queue.Data());
if (queue.Data().IsEmpty()) {
// Remove if empty, to prevent the queue map from growing forever

Просмотреть файл

@ -22,7 +22,7 @@ class LockRequestParent final : public PLockRequestParent {
const IPCLockRequest& Data() { return mRequest; }
mozilla::ipc::IPCResult Recv__delete__() final;
mozilla::ipc::IPCResult Recv__delete__(bool aAborted);
private:
~LockRequestParent() = default;

Просмотреть файл

@ -21,7 +21,7 @@ protocol PLockRequest {
async Abort();
parent:
async __delete__();
async __delete__(bool aAborted);
};
} // namespace cache

Просмотреть файл

@ -1,83 +0,0 @@
[signal.tentative.https.any.html]
expected: ERROR
[Abort after a timeout]
expected: FAIL
[Synchronously signaled abort]
expected: FAIL
[Abort signaled after lock released]
expected: NOTRUN
[Signal that is not aborted]
expected: FAIL
[Abort signaled after lock granted]
expected: TIMEOUT
[An aborted request results in AbortError]
expected: FAIL
[signal.tentative.https.any.worker.html]
expected: TIMEOUT
[Abort after a timeout]
expected: FAIL
[Synchronously signaled abort]
expected: FAIL
[Abort signaled after lock released]
expected: NOTRUN
[Signal that is not aborted]
expected: FAIL
[Abort signaled after lock granted]
expected: TIMEOUT
[An aborted request results in AbortError]
expected: FAIL
[signal.tentative.https.any.serviceworker.html]
expected: TIMEOUT
[Abort after a timeout]
expected: FAIL
[Synchronously signaled abort]
expected: FAIL
[Abort signaled after lock released]
expected: NOTRUN
[Signal that is not aborted]
expected: FAIL
[Abort signaled after lock granted]
expected: TIMEOUT
[An aborted request results in AbortError]
expected: FAIL
[signal.tentative.https.any.sharedworker.html]
expected: TIMEOUT
[Abort after a timeout]
expected: FAIL
[Synchronously signaled abort]
expected: FAIL
[Abort signaled after lock released]
expected: NOTRUN
[Signal that is not aborted]
expected: FAIL
[Abort signaled after lock granted]
expected: TIMEOUT
[An aborted request results in AbortError]
expected: FAIL

Просмотреть файл

@ -190,3 +190,19 @@ promise_test(async t => {
'Lock released promise should not reject');
}, 'Abort signaled after lock released');
promise_test(async t => {
const res = uniqueName(t);
const controller = new AbortController();
const first = requestLockAndHold(t, res, { signal: controller.signal });
const next = navigator.locks.request(res, () => "resolved");
controller.abort();
await promise_rejects_dom(t, "AbortError", first, "Request should abort");
assert_equals(
await next,
"resolved",
"The next request is processed after abort"
);
}, "Abort should process the next pending lock request");