LockManagerParent.cpp (5955B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "LockManagerParent.h" 8 9 #include "LockRequestParent.h" 10 #include "mozilla/PrincipalHashKey.h" 11 #include "mozilla/RefPtr.h" 12 #include "mozilla/StaticPtr.h" 13 #include "mozilla/dom/locks/PLockManager.h" 14 #include "mozilla/media/MediaUtils.h" 15 #include "nsIDUtils.h" 16 #include "nsTHashMap.h" 17 18 namespace mozilla::dom::locks { 19 20 static StaticAutoPtr<nsTHashMap<PrincipalHashKey, WeakPtr<ManagedLocks>>> 21 sManagedLocksMap; 22 23 using IPCResult = mozilla::ipc::IPCResult; 24 25 LockManagerParent::LockManagerParent(NotNull<nsIPrincipal*> aPrincipal, 26 const Maybe<nsID>& aClientId) 27 : mPrincipal(aPrincipal) { 28 if (aClientId.isSome()) { 29 mClientId = NSID_TrimBracketsUTF16(aClientId.value()); 30 } else { 31 mClientId = EmptyString(); 32 } 33 34 if (!sManagedLocksMap) { 35 sManagedLocksMap = 36 new nsTHashMap<PrincipalHashKey, WeakPtr<ManagedLocks>>(); 37 } else { 38 mManagedLocks = sManagedLocksMap->Get(aPrincipal); 39 } 40 41 if (!mManagedLocks) { 42 mManagedLocks = new ManagedLocks(); 43 sManagedLocksMap->LookupOrInsert(aPrincipal, mManagedLocks); 44 } 45 } 46 47 void LockManagerParent::ActorDestroy(ActorDestroyReason aWhy) { 48 if (!mManagedLocks) { 49 return; 50 } 51 52 nsTArray<nsString> affectedResourceNames; 53 54 mManagedLocks->mHeldLocks.RemoveElementsBy( 55 [this, &affectedResourceNames](const RefPtr<LockRequestParent>& request) { 56 bool equals = request->Manager() == this; 57 if (equals) { 58 affectedResourceNames.AppendElement(request->Data().name()); 59 } 60 return equals; 61 }); 62 63 for (auto& queue : mManagedLocks->mQueueMap) { 64 queue.GetModifiableData()->RemoveElementsBy( 65 [this, &name = queue.GetKey(), 66 &affectedResourceNames](const RefPtr<LockRequestParent>& request) { 67 bool equals = request->Manager() == this; 68 if (equals) { 69 affectedResourceNames.AppendElement(name); 70 } 71 return equals; 72 }); 73 } 74 75 for (const nsString& name : affectedResourceNames) { 76 if (auto queue = mManagedLocks->mQueueMap.Lookup(name)) { 77 ProcessRequestQueue(queue.Data()); 78 } 79 } 80 81 mManagedLocks = nullptr; 82 // We just decreased the refcount and potentially deleted it, so check whether 83 // the weak pointer still points to anything and remove the entry if not. 84 if (!sManagedLocksMap->Get(mPrincipal)) { 85 sManagedLocksMap->Remove(mPrincipal); 86 } 87 } 88 89 void LockManagerParent::ProcessRequestQueue( 90 nsTArray<RefPtr<LockRequestParent>>& aQueue) { 91 while (aQueue.Length()) { 92 RefPtr<LockRequestParent> first = aQueue[0]; 93 if (!IsGrantableRequest(first->Data())) { 94 break; 95 } 96 aQueue.RemoveElementAt(0); 97 mManagedLocks->mHeldLocks.AppendElement(first); 98 (void)NS_WARN_IF(!first->SendResolve(first->Data().lockMode(), true)); 99 } 100 } 101 102 bool LockManagerParent::IsGrantableRequest(const IPCLockRequest& aRequest) { 103 for (const auto& held : mManagedLocks->mHeldLocks) { 104 if (held->Data().name() == aRequest.name()) { 105 if (aRequest.lockMode() == LockMode::Exclusive) { 106 return false; 107 } 108 MOZ_ASSERT(aRequest.lockMode() == LockMode::Shared); 109 if (held->Data().lockMode() == LockMode::Exclusive) { 110 return false; 111 } 112 } 113 } 114 return true; 115 } 116 117 IPCResult LockManagerParent::RecvQuery(QueryResolver&& aResolver) { 118 LockManagerSnapshot snapshot; 119 snapshot.mHeld.Construct(); 120 snapshot.mPending.Construct(); 121 for (const auto& queueMapEntry : mManagedLocks->mQueueMap) { 122 for (const RefPtr<LockRequestParent>& request : queueMapEntry.GetData()) { 123 LockInfo info; 124 info.mMode.Construct(request->Data().lockMode()); 125 info.mName.Construct(request->Data().name()); 126 info.mClientId.Construct( 127 static_cast<LockManagerParent*>(request->Manager())->mClientId); 128 if (!snapshot.mPending.Value().AppendElement(info, mozilla::fallible)) { 129 return IPC_FAIL(this, "Out of memory"); 130 }; 131 } 132 } 133 for (const RefPtr<LockRequestParent>& request : mManagedLocks->mHeldLocks) { 134 LockInfo info; 135 info.mMode.Construct(request->Data().lockMode()); 136 info.mName.Construct(request->Data().name()); 137 info.mClientId.Construct( 138 static_cast<LockManagerParent*>(request->Manager())->mClientId); 139 if (!snapshot.mHeld.Value().AppendElement(info, mozilla::fallible)) { 140 return IPC_FAIL(this, "Out of memory"); 141 }; 142 } 143 aResolver(snapshot); 144 return IPC_OK(); 145 }; 146 147 already_AddRefed<PLockRequestParent> LockManagerParent::AllocPLockRequestParent( 148 const IPCLockRequest& aRequest) { 149 return MakeAndAddRef<LockRequestParent>(aRequest); 150 } 151 152 IPCResult LockManagerParent::RecvPLockRequestConstructor( 153 PLockRequestParent* aActor, const IPCLockRequest& aRequest) { 154 RefPtr<LockRequestParent> actor = static_cast<LockRequestParent*>(aActor); 155 nsTArray<RefPtr<LockRequestParent>>& queue = 156 mManagedLocks->mQueueMap.LookupOrInsert(aRequest.name()); 157 if (aRequest.steal()) { 158 mManagedLocks->mHeldLocks.RemoveElementsBy( 159 [&aRequest](const RefPtr<LockRequestParent>& aHeld) { 160 if (aHeld->Data().name() == aRequest.name()) { 161 (void)NS_WARN_IF(!PLockRequestParent::Send__delete__(aHeld, true)); 162 return true; 163 } 164 return false; 165 }); 166 queue.InsertElementAt(0, actor); 167 } else if (aRequest.ifAvailable() && 168 (!queue.IsEmpty() || !IsGrantableRequest(actor->Data()))) { 169 (void)NS_WARN_IF(!aActor->SendResolve(aRequest.lockMode(), false)); 170 return IPC_OK(); 171 } else { 172 queue.AppendElement(actor); 173 } 174 ProcessRequestQueue(queue); 175 return IPC_OK(); 176 } 177 178 } // namespace mozilla::dom::locks