/* -*- 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 "LockManagerChild.h" #include "LockRequestChild.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/WorkerPrivate.h" namespace mozilla::dom::locks { using IPCResult = mozilla::ipc::IPCResult; NS_IMPL_ISUPPORTS(LockRequestChild, nsISupports) MOZ_CAN_RUN_SCRIPT static void RunCallbackAndSettlePromise( LockGrantedCallback& aCallback, mozilla::dom::Lock* lock, Promise& aPromise) { ErrorResult rv; if (RefPtr result = aCallback.Call( lock, rv, nullptr, CallbackObject::eRethrowExceptions)) { aPromise.MaybeResolve(result); } else if (rv.Failed() && !rv.IsUncatchableException()) { aPromise.MaybeReject(std::move(rv)); return; } else { aPromise.MaybeResolveWithUndefined(); } // This is required even with no failure. IgnoredErrorResult is not an option // since MaybeReject does not accept it. rv.WouldReportJSException(); if (NS_WARN_IF(rv.IsUncatchableException())) { rv.SuppressException(); // XXX: Why does this happen anyway? } MOZ_ASSERT(!rv.Failed()); } LockRequestChild::LockRequestChild( const LockRequest& aRequest, const Optional>& aSignal) : mRequest(aRequest) { if (aSignal.WasPassed()) { Follow(&aSignal.Value()); } } void LockRequestChild::MaybeSetWorkerRef() { if (!NS_IsMainThread()) { mWorkerRef = StrongWorkerRef::Create( GetCurrentThreadWorkerPrivate(), "LockManager", [self = RefPtr(this)]() { self->mWorkerRef = nullptr; }); } } void LockRequestChild::ActorDestroy(ActorDestroyReason aReason) { CastedManager()->NotifyRequestDestroy(); } IPCResult LockRequestChild::RecvResolve(const LockMode& aLockMode, bool aIsAvailable) { Unfollow(); RefPtr lock; RefPtr promise; if (aIsAvailable) { IgnoredErrorResult err; lock = new Lock(CastedManager()->GetParentObject(), this, mRequest.mName, aLockMode, mRequest.mPromise, err); if (MOZ_UNLIKELY(err.Failed())) { mRequest.mPromise->MaybeRejectWithUnknownError( "Failed to allocate a lock"); return IPC_OK(); } lock->GetWaitingPromise().AppendNativeHandler(lock); promise = &lock->GetWaitingPromise(); } else { // We are in `ifAvailable: true` mode and the lock is not available. // There is no waitingPromise since there is no lock, so settle the promise // from the request instead. // This matches "If ifAvailable is true and request is not grantable" step. promise = mRequest.mPromise; } // XXX(krosylight): MOZ_KnownLive shouldn't be needed here, mRequest is const RunCallbackAndSettlePromise(MOZ_KnownLive(*mRequest.mCallback), lock, *promise); return IPC_OK(); } IPCResult LockRequestChild::Recv__delete__(bool aAborted) { MOZ_ASSERT(aAborted, "__delete__ is currently only for abort"); Unfollow(); mRequest.mPromise->MaybeRejectWithAbortError("The lock request is aborted"); return IPC_OK(); } void LockRequestChild::RunAbortAlgorithm() { AutoJSAPI jsapi; if (NS_WARN_IF( !jsapi.Init(static_cast(Signal())->GetOwnerGlobal()))) { mRequest.mPromise->MaybeRejectWithAbortError("The lock request is aborted"); } else { JSContext* cx = jsapi.cx(); JS::Rooted reason(cx); Signal()->GetReason(cx, &reason); mRequest.mPromise->MaybeReject(reason); } Unfollow(); Send__delete__(this, true); } inline LockManagerChild* LockRequestChild::CastedManager() const { return static_cast(Manager()); }; } // namespace mozilla::dom::locks