Bug 1698238 return default dictionary from GetUserMediaRequest#getConstraints() if...
[gecko.git] / dom / quota / DirectoryLockImpl.cpp
blobcb0df8e2d4be7e4408fa19f20041aba0c25e9357
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/. */
7 #include "DirectoryLockImpl.h"
9 #include "mozilla/ReverseIterator.h"
10 #include "mozilla/dom/quota/Client.h"
11 #include "mozilla/dom/quota/QuotaManager.h"
13 namespace mozilla::dom::quota {
15 DirectoryLockImpl::DirectoryLockImpl(
16 MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
17 const Nullable<PersistenceType>& aPersistenceType,
18 const nsACString& aSuffix, const nsACString& aGroup,
19 const OriginScope& aOriginScope, const Nullable<Client::Type>& aClientType,
20 const bool aExclusive, const bool aInternal,
21 const ShouldUpdateLockIdTableFlag aShouldUpdateLockIdTableFlag)
22 : mQuotaManager(std::move(aQuotaManager)),
23 mPersistenceType(aPersistenceType),
24 mSuffix(aSuffix),
25 mGroup(aGroup),
26 mOriginScope(aOriginScope),
27 mClientType(aClientType),
28 mId(mQuotaManager->GenerateDirectoryLockId()),
29 mExclusive(aExclusive),
30 mInternal(aInternal),
31 mShouldUpdateLockIdTable(aShouldUpdateLockIdTableFlag ==
32 ShouldUpdateLockIdTableFlag::Yes),
33 mRegistered(false) {
34 AssertIsOnOwningThread();
35 MOZ_ASSERT_IF(aOriginScope.IsOrigin(), !aOriginScope.GetOrigin().IsEmpty());
36 MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull());
37 MOZ_ASSERT_IF(!aInternal,
38 aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID);
39 MOZ_ASSERT_IF(!aInternal, !aGroup.IsEmpty());
40 MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin());
41 MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull());
42 MOZ_ASSERT_IF(!aInternal, aClientType.Value() < Client::TypeMax());
45 DirectoryLockImpl::~DirectoryLockImpl() {
46 AssertIsOnOwningThread();
48 for (NotNull<RefPtr<DirectoryLockImpl>> blockingLock : mBlocking) {
49 blockingLock->MaybeUnblock(*this);
52 mBlocking.Clear();
54 if (mRegistered) {
55 mQuotaManager->UnregisterDirectoryLock(*this);
58 MOZ_ASSERT(!mRegistered);
61 #ifdef DEBUG
63 void DirectoryLockImpl::AssertIsOnOwningThread() const {
64 mQuotaManager->AssertIsOnOwningThread();
67 #endif // DEBUG
69 bool DirectoryLockImpl::Overlaps(const DirectoryLockImpl& aLock) const {
70 AssertIsOnOwningThread();
72 // If the persistence types don't overlap, the op can proceed.
73 if (!aLock.mPersistenceType.IsNull() && !mPersistenceType.IsNull() &&
74 aLock.mPersistenceType.Value() != mPersistenceType.Value()) {
75 return false;
78 // If the origin scopes don't overlap, the op can proceed.
79 bool match = aLock.mOriginScope.Matches(mOriginScope);
80 if (!match) {
81 return false;
84 // If the client types don't overlap, the op can proceed.
85 if (!aLock.mClientType.IsNull() && !mClientType.IsNull() &&
86 aLock.mClientType.Value() != mClientType.Value()) {
87 return false;
90 // Otherwise, when all attributes overlap (persistence type, origin scope and
91 // client type) the op must wait.
92 return true;
95 bool DirectoryLockImpl::MustWaitFor(const DirectoryLockImpl& aLock) const {
96 AssertIsOnOwningThread();
98 // Waiting is never required if the ops in comparison represent shared locks.
99 if (!aLock.mExclusive && !mExclusive) {
100 return false;
103 // Wait if the ops overlap.
104 return Overlaps(aLock);
107 void DirectoryLockImpl::NotifyOpenListener() {
108 AssertIsOnOwningThread();
110 if (mInvalidated) {
111 (*mOpenListener)->DirectoryLockFailed();
112 } else {
113 (*mOpenListener)
114 ->DirectoryLockAcquired(static_cast<UniversalDirectoryLock*>(this));
117 mOpenListener.destroy();
119 mQuotaManager->RemovePendingDirectoryLock(*this);
121 mPending.Flip();
124 void DirectoryLockImpl::Acquire(RefPtr<OpenDirectoryListener> aOpenListener) {
125 AssertIsOnOwningThread();
126 MOZ_ASSERT(aOpenListener);
128 mOpenListener.init(WrapNotNullUnchecked(std::move(aOpenListener)));
130 mQuotaManager->AddPendingDirectoryLock(*this);
132 // See if this lock needs to wait.
133 bool blocked = false;
135 // XXX It is probably unnecessary to iterate this in reverse order.
136 for (DirectoryLockImpl* const existingLock :
137 Reversed(mQuotaManager->mDirectoryLocks)) {
138 if (MustWaitFor(*existingLock)) {
139 existingLock->AddBlockingLock(*this);
140 AddBlockedOnLock(*existingLock);
141 blocked = true;
145 mQuotaManager->RegisterDirectoryLock(*this);
147 // Otherwise, notify the open listener immediately.
148 if (!blocked) {
149 NotifyOpenListener();
150 return;
153 if (!mExclusive || !mInternal) {
154 return;
157 // All the locks that block this new exclusive internal lock need to be
158 // invalidated. We also need to notify clients to abort operations for them.
159 QuotaManager::DirectoryLockIdTableArray lockIds;
160 lockIds.SetLength(Client::TypeMax());
162 const auto& blockedOnLocks = GetBlockedOnLocks();
163 MOZ_ASSERT(!blockedOnLocks.IsEmpty());
165 for (DirectoryLockImpl* blockedOnLock : blockedOnLocks) {
166 if (!blockedOnLock->IsInternal()) {
167 blockedOnLock->Invalidate();
169 // Clients don't have to handle pending locks. Invalidation is sufficient
170 // in that case (once a lock is ready and the listener needs to be
171 // notified, we will call DirectoryLockFailed instead of
172 // DirectoryLockAcquired which should release any remaining references to
173 // the lock).
174 if (!blockedOnLock->IsPending()) {
175 lockIds[blockedOnLock->ClientType()].Put(blockedOnLock->Id());
180 mQuotaManager->AbortOperationsForLocks(lockIds);
183 void DirectoryLockImpl::AcquireImmediately() {
184 AssertIsOnOwningThread();
186 #ifdef DEBUG
187 for (const DirectoryLockImpl* const existingLock :
188 mQuotaManager->mDirectoryLocks) {
189 MOZ_ASSERT(!MustWaitFor(*existingLock));
191 #endif
193 mQuotaManager->RegisterDirectoryLock(*this);
196 RefPtr<ClientDirectoryLock> DirectoryLockImpl::Specialize(
197 PersistenceType aPersistenceType,
198 const quota::OriginMetadata& aOriginMetadata,
199 Client::Type aClientType) const {
200 AssertIsOnOwningThread();
201 MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID);
202 MOZ_ASSERT(!aOriginMetadata.mGroup.IsEmpty());
203 MOZ_ASSERT(!aOriginMetadata.mOrigin.IsEmpty());
204 MOZ_ASSERT(aClientType < Client::TypeMax());
205 MOZ_ASSERT(!mOpenListener);
206 MOZ_ASSERT(mBlockedOn.IsEmpty());
208 if (NS_WARN_IF(mExclusive)) {
209 return nullptr;
212 RefPtr<DirectoryLockImpl> lock = Create(
213 mQuotaManager, Nullable<PersistenceType>(aPersistenceType),
214 aOriginMetadata.mSuffix, aOriginMetadata.mGroup,
215 OriginScope::FromOrigin(aOriginMetadata.mOrigin),
216 Nullable<Client::Type>(aClientType),
217 /* aExclusive */ false, mInternal, ShouldUpdateLockIdTableFlag::Yes);
218 if (NS_WARN_IF(!Overlaps(*lock))) {
219 return nullptr;
222 #ifdef DEBUG
223 for (DirectoryLockImpl* const existingLock :
224 Reversed(mQuotaManager->mDirectoryLocks)) {
225 if (existingLock != this && !existingLock->MustWaitFor(*this)) {
226 MOZ_ASSERT(!existingLock->MustWaitFor(*lock));
229 #endif
231 for (const auto& blockedLock : mBlocking) {
232 if (blockedLock->MustWaitFor(*lock)) {
233 lock->AddBlockingLock(*blockedLock);
234 blockedLock->AddBlockedOnLock(*lock);
238 mQuotaManager->RegisterDirectoryLock(*lock);
240 if (mInvalidated) {
241 lock->Invalidate();
244 return lock;
247 void DirectoryLockImpl::Log() const {
248 AssertIsOnOwningThread();
250 if (!QM_LOG_TEST()) {
251 return;
254 QM_LOG(("DirectoryLockImpl [%p]", this));
256 nsCString persistenceType;
257 if (mPersistenceType.IsNull()) {
258 persistenceType.AssignLiteral("null");
259 } else {
260 persistenceType.Assign(PersistenceTypeToString(mPersistenceType.Value()));
262 QM_LOG((" mPersistenceType: %s", persistenceType.get()));
264 QM_LOG((" mGroup: %s", mGroup.get()));
266 nsCString originScope;
267 if (mOriginScope.IsOrigin()) {
268 originScope.AssignLiteral("origin:");
269 originScope.Append(mOriginScope.GetOrigin());
270 } else if (mOriginScope.IsPrefix()) {
271 originScope.AssignLiteral("prefix:");
272 originScope.Append(mOriginScope.GetOriginNoSuffix());
273 } else if (mOriginScope.IsPattern()) {
274 originScope.AssignLiteral("pattern:");
275 // Can't call GetJSONPattern since it only works on the main thread.
276 } else {
277 MOZ_ASSERT(mOriginScope.IsNull());
278 originScope.AssignLiteral("null");
280 QM_LOG((" mOriginScope: %s", originScope.get()));
282 const auto clientType = mClientType.IsNull()
283 ? nsAutoCString{"null"_ns}
284 : Client::TypeToText(mClientType.Value());
285 QM_LOG((" mClientType: %s", clientType.get()));
287 nsCString blockedOnString;
288 for (auto blockedOn : mBlockedOn) {
289 blockedOnString.Append(
290 nsPrintfCString(" [%p]", static_cast<void*>(blockedOn)));
292 QM_LOG((" mBlockedOn:%s", blockedOnString.get()));
294 QM_LOG((" mExclusive: %d", mExclusive));
296 QM_LOG((" mInternal: %d", mInternal));
298 QM_LOG((" mInvalidated: %d", static_cast<bool>(mInvalidated)));
300 for (auto blockedOn : mBlockedOn) {
301 blockedOn->Log();
305 } // namespace mozilla::dom::quota