/* -*- 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 "LockManagerParent.h" #include "LockRequestParent.h" #include "mozilla/ContentPrincipalInfoHashKey.h" #include "mozilla/PrincipalHashKey.h" #include "mozilla/RefPtr.h" #include "mozilla/StaticPtr.h" #include "mozilla/dom/locks/PLockManager.h" #include "mozilla/media/MediaUtils.h" #include "nsIDUtils.h" #include "nsTHashMap.h" namespace mozilla::dom::locks { static StaticAutoPtr< nsTHashMap>> sManagedLocksMap; using IPCResult = mozilla::ipc::IPCResult; LockManagerParent::LockManagerParent( const mozilla::ipc::ContentPrincipalInfo& aPrincipalInfo, const nsID& aClientId) : mClientId(NSID_TrimBracketsUTF16(aClientId)), mPrincipalInfo(aPrincipalInfo) { if (!sManagedLocksMap) { sManagedLocksMap = new nsTHashMap>(); } else { mManagedLocks = sManagedLocksMap->Get(aPrincipalInfo); } if (!mManagedLocks) { mManagedLocks = new ManagedLocks(); sManagedLocksMap->LookupOrInsert(aPrincipalInfo, mManagedLocks); } } void LockManagerParent::ActorDestroy(ActorDestroyReason aWhy) { if (!mManagedLocks) { return; } nsTArray affectedResourceNames; mManagedLocks->mHeldLocks.RemoveElementsBy( [this, &affectedResourceNames](const RefPtr& request) { bool equals = request->Manager() == this; if (equals) { affectedResourceNames.AppendElement(request->Data().name()); } return equals; }); for (auto& queue : mManagedLocks->mQueueMap) { queue.GetModifiableData()->RemoveElementsBy( [this, &name = queue.GetKey(), &affectedResourceNames](const RefPtr& request) { bool equals = request->Manager() == this; if (equals) { affectedResourceNames.AppendElement(name); } return equals; }); } for (const nsString& name : affectedResourceNames) { if (auto queue = mManagedLocks->mQueueMap.Lookup(name)) { ProcessRequestQueue(queue.Data()); } } mManagedLocks = nullptr; // We just decreased the refcount and potentially deleted it, so check whether // the weak pointer still points to anything and remove the entry if not. if (!sManagedLocksMap->Get(mPrincipalInfo)) { sManagedLocksMap->Remove(mPrincipalInfo); } } void LockManagerParent::ProcessRequestQueue( nsTArray>& aQueue) { while (aQueue.Length()) { RefPtr first = aQueue[0]; if (!IsGrantableRequest(first->Data())) { break; } aQueue.RemoveElementAt(0); mManagedLocks->mHeldLocks.AppendElement(first); Unused << NS_WARN_IF(!first->SendResolve(first->Data().lockMode(), true)); } } bool LockManagerParent::IsGrantableRequest(const IPCLockRequest& aRequest) { for (const auto& held : mManagedLocks->mHeldLocks) { if (held->Data().name() == aRequest.name()) { if (aRequest.lockMode() == LockMode::Exclusive) { return false; } MOZ_ASSERT(aRequest.lockMode() == LockMode::Shared); if (held->Data().lockMode() == LockMode::Exclusive) { return false; } } } return true; } IPCResult LockManagerParent::RecvQuery(QueryResolver&& aResolver) { LockManagerSnapshot snapshot; snapshot.mHeld.Construct(); snapshot.mPending.Construct(); for (const auto& queueMapEntry : mManagedLocks->mQueueMap) { for (const RefPtr& request : queueMapEntry.GetData()) { LockInfo info; info.mMode.Construct(request->Data().lockMode()); info.mName.Construct(request->Data().name()); info.mClientId.Construct( static_cast(request->Manager())->mClientId); if (!snapshot.mPending.Value().AppendElement(info, mozilla::fallible)) { return IPC_FAIL(this, "Out of memory"); }; } } for (const RefPtr& request : mManagedLocks->mHeldLocks) { LockInfo info; info.mMode.Construct(request->Data().lockMode()); info.mName.Construct(request->Data().name()); info.mClientId.Construct( static_cast(request->Manager())->mClientId); if (!snapshot.mHeld.Value().AppendElement(info, mozilla::fallible)) { return IPC_FAIL(this, "Out of memory"); }; } aResolver(snapshot); return IPC_OK(); }; already_AddRefed LockManagerParent::AllocPLockRequestParent( const IPCLockRequest& aRequest) { return MakeAndAddRef(aRequest); } IPCResult LockManagerParent::RecvPLockRequestConstructor( PLockRequestParent* aActor, const IPCLockRequest& aRequest) { RefPtr actor = static_cast(aActor); nsTArray>& queue = mManagedLocks->mQueueMap.LookupOrInsert(aRequest.name()); if (aRequest.steal()) { mManagedLocks->mHeldLocks.RemoveElementsBy( [&aRequest](const RefPtr& aHeld) { if (aHeld->Data().name() == aRequest.name()) { Unused << NS_WARN_IF( !PLockRequestParent::Send__delete__(aHeld, true)); return true; } return false; }); queue.InsertElementAt(0, actor); } else if (aRequest.ifAvailable() && (!queue.IsEmpty() || !IsGrantableRequest(actor->Data()))) { Unused << NS_WARN_IF(!aActor->SendResolve(aRequest.lockMode(), false)); return IPC_OK(); } else { queue.AppendElement(actor); } ProcessRequestQueue(queue); return IPC_OK(); } } // namespace mozilla::dom::locks