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 "ActorsParent.h"
20 #include <type_traits>
22 #include "ActorsParentCommon.h"
23 #include "CrashAnnotations.h"
24 #include "DatabaseFileInfo.h"
25 #include "DatabaseFileManager.h"
26 #include "DatabaseFileManagerImpl.h"
28 #include "ErrorList.h"
29 #include "IDBCursorType.h"
30 #include "IDBObjectStore.h"
31 #include "IDBTransaction.h"
32 #include "IndexedDBCommon.h"
33 #include "IndexedDatabaseInlines.h"
34 #include "IndexedDatabaseManager.h"
35 #include "IndexedDBCipherKeyManager.h"
37 #include "MainThreadUtils.h"
38 #include "ProfilerHelpers.h"
39 #include "ReportInternalError.h"
40 #include "SafeRefPtr.h"
41 #include "SchemaUpgrades.h"
42 #include "chrome/common/ipc_channel.h"
43 #include "ipc/IPCMessageUtils.h"
44 #include "js/RootingAPI.h"
45 #include "js/StructuredClone.h"
48 #include "mozIStorageAsyncConnection.h"
49 #include "mozIStorageConnection.h"
50 #include "mozIStorageFunction.h"
51 #include "mozIStorageProgressHandler.h"
52 #include "mozIStorageService.h"
53 #include "mozIStorageStatement.h"
54 #include "mozIStorageValueArray.h"
55 #include "mozStorageCID.h"
56 #include "mozStorageHelper.h"
57 #include "mozilla/Algorithm.h"
58 #include "mozilla/ArrayAlgorithm.h"
59 #include "mozilla/ArrayIterator.h"
60 #include "mozilla/Assertions.h"
61 #include "mozilla/Atomics.h"
62 #include "mozilla/Attributes.h"
63 #include "mozilla/Casting.h"
64 #include "mozilla/CondVar.h"
65 #include "mozilla/DebugOnly.h"
66 #include "mozilla/EndianUtils.h"
67 #include "mozilla/ErrorNames.h"
68 #include "mozilla/ErrorResult.h"
69 #include "mozilla/InitializedOnce.h"
70 #include "mozilla/Logging.h"
71 #include "mozilla/MacroForEach.h"
72 #include "mozilla/Maybe.h"
73 #include "mozilla/Monitor.h"
74 #include "mozilla/Mutex.h"
75 #include "mozilla/NotNull.h"
76 #include "mozilla/Preferences.h"
77 #include "mozilla/ProfilerLabels.h"
78 #include "mozilla/RefCountType.h"
79 #include "mozilla/RefCounted.h"
80 #include "mozilla/RemoteLazyInputStreamParent.h"
81 #include "mozilla/RemoteLazyInputStreamStorage.h"
82 #include "mozilla/Result.h"
83 #include "mozilla/ResultExtensions.h"
84 #include "mozilla/SchedulerGroup.h"
85 #include "mozilla/Scoped.h"
86 #include "mozilla/SnappyCompressOutputStream.h"
87 #include "mozilla/SpinEventLoopUntil.h"
88 #include "mozilla/StaticPtr.h"
89 #include "mozilla/TaskCategory.h"
90 #include "mozilla/TimeStamp.h"
91 #include "mozilla/UniquePtr.h"
92 #include "mozilla/Unused.h"
93 #include "mozilla/Variant.h"
94 #include "mozilla/dom/BlobImpl.h"
95 #include "mozilla/dom/ContentParent.h"
96 #include "mozilla/dom/FileBlobImpl.h"
97 #include "mozilla/dom/FlippedOnce.h"
98 #include "mozilla/dom/IDBCursorBinding.h"
99 #include "mozilla/dom/IPCBlob.h"
100 #include "mozilla/dom/IPCBlobUtils.h"
101 #include "mozilla/dom/IndexedDatabase.h"
102 #include "mozilla/dom/Nullable.h"
103 #include "mozilla/dom/PContentParent.h"
104 #include "mozilla/dom/ScriptSettings.h"
105 #include "mozilla/dom/indexedDB/IDBResult.h"
106 #include "mozilla/dom/indexedDB/Key.h"
107 #include "mozilla/dom/indexedDB/PBackgroundIDBCursor.h"
108 #include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h"
109 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabase.h"
110 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h"
111 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
112 #include "mozilla/dom/indexedDB/PBackgroundIDBFactory.h"
113 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h"
114 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h"
115 #include "mozilla/dom/indexedDB/PBackgroundIDBRequest.h"
116 #include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h"
117 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
118 #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h"
119 #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h"
120 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsParent.h"
121 #include "mozilla/dom/ipc/IdType.h"
122 #include "mozilla/dom/quota/CachingDatabaseConnection.h"
123 #include "mozilla/dom/quota/CheckedUnsafePtr.h"
124 #include "mozilla/dom/quota/Client.h"
125 #include "mozilla/dom/quota/ClientImpl.h"
126 #include "mozilla/dom/quota/DirectoryLock.h"
127 #include "mozilla/dom/quota/DecryptingInputStream_impl.h"
128 #include "mozilla/dom/quota/EncryptingOutputStream_impl.h"
129 #include "mozilla/dom/quota/FileStreams.h"
130 #include "mozilla/dom/quota/OriginScope.h"
131 #include "mozilla/dom/quota/PersistenceType.h"
132 #include "mozilla/dom/quota/QuotaCommon.h"
133 #include "mozilla/dom/quota/QuotaManager.h"
134 #include "mozilla/dom/quota/QuotaObject.h"
135 #include "mozilla/dom/quota/ResultExtensions.h"
136 #include "mozilla/dom/quota/UsageInfo.h"
137 #include "mozilla/fallible.h"
138 #include "mozilla/ipc/BackgroundParent.h"
139 #include "mozilla/ipc/BackgroundUtils.h"
140 #include "mozilla/ipc/InputStreamParams.h"
141 #include "mozilla/ipc/PBackgroundParent.h"
142 #include "mozilla/ipc/PBackgroundSharedTypes.h"
143 #include "mozilla/ipc/ProtocolUtils.h"
144 #include "mozilla/mozalloc.h"
145 #include "mozilla/storage/Variant.h"
146 #include "nsBaseHashtable.h"
147 #include "nsCOMPtr.h"
148 #include "nsClassHashtable.h"
149 #include "nsContentUtils.h"
150 #include "nsTHashMap.h"
153 #include "nsEscape.h"
154 #include "nsHashKeys.h"
155 #include "nsIAsyncInputStream.h"
157 #include "nsIDirectoryEnumerator.h"
158 #include "nsIEventTarget.h"
160 #include "nsIFileProtocolHandler.h"
161 #include "nsIFileStreams.h"
162 #include "nsIFileURL.h"
163 #include "nsIInputStream.h"
164 #include "nsIOutputStream.h"
165 #include "nsIProtocolHandler.h"
166 #include "nsIRunnable.h"
167 #include "nsISupports.h"
168 #include "nsISupportsPriority.h"
169 #include "nsISupportsUtils.h"
170 #include "nsIThread.h"
171 #include "nsIThreadInternal.h"
172 #include "nsITimer.h"
173 #include "nsIURIMutator.h"
174 #include "nsIVariant.h"
175 #include "nsLiteralString.h"
176 #include "nsNetCID.h"
177 #include "nsPrintfCString.h"
178 #include "nsProxyRelease.h"
179 #include "nsServiceManagerUtils.h"
180 #include "nsStreamUtils.h"
181 #include "nsString.h"
182 #include "nsStringFlags.h"
183 #include "nsStringFwd.h"
184 #include "nsTArray.h"
185 #include "nsTHashSet.h"
186 #include "nsTHashtable.h"
187 #include "nsTLiteralString.h"
188 #include "nsTStringRepr.h"
189 #include "nsThreadPool.h"
190 #include "nsThreadUtils.h"
192 #include "prinrval.h"
194 #include "prsystem.h"
195 #include "prthread.h"
198 #include "snappy/snappy.h"
205 #define IDB_DEBUG_LOG(_args) \
206 MOZ_LOG(IndexedDatabaseManager::GetLoggingModule(), LogLevel::Debug, _args)
208 #if defined(MOZ_WIDGET_ANDROID)
212 // Helper macros to reduce assertion verbosity
213 // AUUF == ASSERT_UNREACHABLE_UNLESS_FUZZING
216 # define NS_AUUF_OR_WARN(...) NS_WARNING(__VA_ARGS__)
218 # define NS_AUUF_OR_WARN(...) MOZ_ASSERT(false, __VA_ARGS__)
220 # define NS_AUUF_OR_WARN_IF(cond) \
222 if (MOZ_UNLIKELY(aCond)) { \
223 NS_AUUF_OR_WARN(#cond); \
228 # define NS_AUUF_OR_WARN(...) \
231 # define NS_AUUF_OR_WARN_IF(cond) static_cast<bool>(cond)
236 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc
, PRFileDesc
,
239 namespace dom::indexedDB
{
241 using namespace mozilla::dom::quota
;
242 using namespace mozilla::ipc
;
243 using mozilla::dom::quota::Client
;
247 class ConnectionPool
;
249 struct DatabaseActorInfo
;
251 class DatabaseLoggingInfo
;
252 class DatabaseMaintenance
;
255 class OpenDatabaseOp
;
256 class TransactionBase
;
257 class TransactionDatabaseOperationBase
;
258 class VersionChangeTransaction
;
259 template <bool StatementHasIndexKeyBindings
>
260 struct ValuePopulateResponseHelper
;
262 /*******************************************************************************
264 ******************************************************************************/
266 const int32_t kStorageProgressGranularity
= 1000;
268 // Changing the value here will override the page size of new databases only.
269 // A journal mode change and VACUUM are needed to change existing databases, so
270 // the best way to do that is to use the schema version upgrade mechanism.
271 const uint32_t kSQLitePageSizeOverride
=
278 static_assert(kSQLitePageSizeOverride
== /* mozStorage default */ 0 ||
279 (kSQLitePageSizeOverride
% 2 == 0 &&
280 kSQLitePageSizeOverride
>= 512 &&
281 kSQLitePageSizeOverride
<= 65536),
282 "Must be 0 (disabled) or a power of 2 between 512 and 65536!");
284 // Set to -1 to use SQLite's default, 0 to disable, or some positive number to
285 // enforce a custom limit.
286 const int32_t kMaxWALPages
= 5000; // 20MB on desktop, 10MB on mobile.
288 // Set to some multiple of the page size to grow the database in larger chunks.
289 const uint32_t kSQLiteGrowthIncrement
= kSQLitePageSizeOverride
* 2;
291 static_assert(kSQLiteGrowthIncrement
>= 0 &&
292 kSQLiteGrowthIncrement
% kSQLitePageSizeOverride
== 0 &&
293 kSQLiteGrowthIncrement
< uint32_t(INT32_MAX
),
294 "Must be 0 (disabled) or a positive multiple of the page size!");
296 // The maximum number of threads that can be used for database activity at a
298 const uint32_t kMaxConnectionThreadCount
= 20;
300 static_assert(kMaxConnectionThreadCount
, "Must have at least one thread!");
302 // The maximum number of threads to keep when idle. Threads that become idle in
303 // excess of this number will be shut down immediately.
304 const uint32_t kMaxIdleConnectionThreadCount
= 2;
306 static_assert(kMaxConnectionThreadCount
>= kMaxIdleConnectionThreadCount
,
307 "Idle thread limit must be less than total thread limit!");
309 // The length of time that database connections will be held open after all
310 // transactions have completed before doing idle maintenance.
311 const uint32_t kConnectionIdleMaintenanceMS
= 2 * 1000; // 2 seconds
313 // The length of time that database connections will be held open after all
314 // transactions and maintenance have completed.
315 const uint32_t kConnectionIdleCloseMS
= 10 * 1000; // 10 seconds
317 // The length of time that idle threads will stay alive before being shut down.
318 const uint32_t kConnectionThreadIdleMS
= 30 * 1000; // 30 seconds
320 #define SAVEPOINT_CLAUSE "SAVEPOINT sp;"_ns
322 // For efficiency reasons, kEncryptedStreamBlockSize must be a multiple of large
324 static_assert(kEncryptedStreamBlockSize
% 4096 == 0);
325 // Similarly, the file copy buffer size must be a multiple of the encrypted
327 static_assert(kFileCopyBufferSize
% kEncryptedStreamBlockSize
== 0);
329 constexpr auto kFileManagerDirectoryNameSuffix
= u
".files"_ns
;
330 constexpr auto kSQLiteSuffix
= u
".sqlite"_ns
;
331 constexpr auto kSQLiteJournalSuffix
= u
".sqlite-journal"_ns
;
332 constexpr auto kSQLiteSHMSuffix
= u
".sqlite-shm"_ns
;
333 constexpr auto kSQLiteWALSuffix
= u
".sqlite-wal"_ns
;
335 constexpr auto kPermissionStringBase
= "indexedDB-chrome-"_ns
;
336 constexpr auto kPermissionReadSuffix
= "-read"_ns
;
337 constexpr auto kPermissionWriteSuffix
= "-write"_ns
;
339 // The following constants define all names of binding parameters in statements,
340 // where they are bound by name. This should include all parameter names which
341 // are bound by name. Binding may be done by index when the statement definition
342 // and binding are done in the same local scope, and no other reasons prevent
343 // using the indexes (e.g. multiple statement variants with differing number or
344 // order of parameters). Neither the styles of specifying parameter names
345 // (literally vs. via these constants) nor the binding styles (by index vs. by
346 // name) should not be mixed for the same statement. The decision must be made
347 // for each statement based on the proximity of statement and binding calls.
348 constexpr auto kStmtParamNameCurrentKey
= "current_key"_ns
;
349 constexpr auto kStmtParamNameRangeBound
= "range_bound"_ns
;
350 constexpr auto kStmtParamNameObjectStorePosition
= "object_store_position"_ns
;
351 constexpr auto kStmtParamNameLowerKey
= "lower_key"_ns
;
352 constexpr auto kStmtParamNameUpperKey
= "upper_key"_ns
;
353 constexpr auto kStmtParamNameKey
= "key"_ns
;
354 constexpr auto kStmtParamNameObjectStoreId
= "object_store_id"_ns
;
355 constexpr auto kStmtParamNameIndexId
= "index_id"_ns
;
356 // TODO: Maybe the uses of kStmtParamNameId should be replaced by more
357 // specific constants such as kStmtParamNameObjectStoreId.
358 constexpr auto kStmtParamNameId
= "id"_ns
;
359 constexpr auto kStmtParamNameValue
= "value"_ns
;
360 constexpr auto kStmtParamNameObjectDataKey
= "object_data_key"_ns
;
361 constexpr auto kStmtParamNameIndexDataValues
= "index_data_values"_ns
;
362 constexpr auto kStmtParamNameData
= "data"_ns
;
363 constexpr auto kStmtParamNameFileIds
= "file_ids"_ns
;
364 constexpr auto kStmtParamNameValueLocale
= "value_locale"_ns
;
365 constexpr auto kStmtParamNameLimit
= "limit"_ns
;
367 // The following constants define some names of columns in tables, which are
368 // referred to in remote locations, e.g. in calls to
369 // GetBindingClauseForKeyRange.
370 constexpr auto kColumnNameKey
= "key"_ns
;
371 constexpr auto kColumnNameValue
= "value"_ns
;
372 constexpr auto kColumnNameAliasSortKey
= "sort_column"_ns
;
374 // SQL fragments used at multiple locations.
375 constexpr auto kOpenLimit
= " LIMIT "_ns
;
377 // The deletion marker file is created before RemoveDatabaseFilesAndDirectory
378 // begins deleting a database. It is removed as the last step of deletion. If a
379 // deletion marker file is found when initializing the origin, the deletion
380 // routine is run again to ensure that the database and all of its related files
381 // are removed. The primary goal of this mechanism is to avoid situations where
382 // a database has been partially deleted, leading to inconsistent state for the
384 constexpr auto kIdbDeletionMarkerFilePrefix
= u
"idb-deleting-"_ns
;
386 const uint32_t kDeleteTimeoutMs
= 1000;
390 const int32_t kDEBUGThreadPriority
= nsISupportsPriority::PRIORITY_NORMAL
;
391 const uint32_t kDEBUGThreadSleepMS
= 0;
393 const int32_t kDEBUGTransactionThreadPriority
=
394 nsISupportsPriority::PRIORITY_NORMAL
;
395 const uint32_t kDEBUGTransactionThreadSleepMS
= 0;
399 /*******************************************************************************
401 ******************************************************************************/
403 // Can be instantiated either on the QuotaManager IO thread or on a
404 // versionchange transaction thread. These threads can never race so this is
406 struct FullIndexMetadata
{
407 IndexMetadata mCommonMetadata
= {0, nsString(), KeyPath(0), nsCString(),
408 false, false, false};
410 FlippedOnce
<false> mDeleted
;
412 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullIndexMetadata
)
415 ~FullIndexMetadata() = default;
418 using IndexTable
= nsTHashMap
<nsUint64HashKey
, SafeRefPtr
<FullIndexMetadata
>>;
420 // Can be instantiated either on the QuotaManager IO thread or on a
421 // versionchange transaction thread. These threads can never race so this is
423 struct FullObjectStoreMetadata
{
424 ObjectStoreMetadata mCommonMetadata
;
427 // The auto increment ids are touched on both the background thread and the
428 // transaction I/O thread, and they must be kept in sync, so we need a mutex
430 struct AutoIncrementIds
{
434 DataMutex
<AutoIncrementIds
> mAutoIncrementIds
;
436 FlippedOnce
<false> mDeleted
;
438 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullObjectStoreMetadata
);
440 bool HasLiveIndexes() const;
442 FullObjectStoreMetadata(ObjectStoreMetadata aCommonMetadata
,
443 const AutoIncrementIds
& aAutoIncrementIds
)
444 : mCommonMetadata
{std::move(aCommonMetadata
)},
445 mAutoIncrementIds
{AutoIncrementIds
{aAutoIncrementIds
},
446 "FullObjectStoreMetadata"} {}
449 ~FullObjectStoreMetadata() = default;
452 using ObjectStoreTable
=
453 nsTHashMap
<nsUint64HashKey
, SafeRefPtr
<FullObjectStoreMetadata
>>;
456 std::is_same_v
<IndexOrObjectStoreId
,
457 std::remove_cv_t
<std::remove_reference_t
<
458 decltype(std::declval
<const ObjectStoreGetParams
&>()
459 .objectStoreId())>>>);
462 IndexOrObjectStoreId
,
463 std::remove_cv_t
<std::remove_reference_t
<
464 decltype(std::declval
<const IndexGetParams
&>().objectStoreId())>>>);
466 struct FullDatabaseMetadata final
: AtomicSafeRefCounted
<FullDatabaseMetadata
> {
467 DatabaseMetadata mCommonMetadata
;
468 nsCString mDatabaseId
;
470 ObjectStoreTable mObjectStores
;
472 IndexOrObjectStoreId mNextObjectStoreId
= 0;
473 IndexOrObjectStoreId mNextIndexId
= 0;
476 explicit FullDatabaseMetadata(const DatabaseMetadata
& aCommonMetadata
)
477 : mCommonMetadata(aCommonMetadata
) {
478 AssertIsOnBackgroundThread();
481 [[nodiscard
]] SafeRefPtr
<FullDatabaseMetadata
> Duplicate() const;
483 MOZ_DECLARE_REFCOUNTED_TYPENAME(FullDatabaseMetadata
)
486 template <class Enumerable
>
487 auto MatchMetadataNameOrId(const Enumerable
& aEnumerable
,
488 IndexOrObjectStoreId aId
,
489 Maybe
<const nsAString
&> aName
= Nothing()) {
490 AssertIsOnBackgroundThread();
493 const auto it
= std::find_if(
494 aEnumerable
.cbegin(), aEnumerable
.cend(),
495 [aId
, aName
](const auto& entry
) {
496 MOZ_ASSERT(entry
.GetKey() != 0);
498 const auto& value
= entry
.GetData();
501 return !value
->mDeleted
&&
502 (aId
== value
->mCommonMetadata
.id() ||
503 (aName
&& *aName
== value
->mCommonMetadata
.name()));
506 return ToMaybeRef(it
!= aEnumerable
.cend() ? it
->GetData().unsafeGetRawPtr()
510 /*******************************************************************************
512 ******************************************************************************/
514 // WARNING: the hash function used for the database name must not change.
515 // That's why this function exists separately from mozilla::HashString(), even
516 // though it is (at the time of writing) equivalent. See bug 780408 and bug
517 // 940315 for details.
518 uint32_t HashName(const nsAString
& aName
) {
520 static uint32_t RotateBitsLeft32(uint32_t aValue
, uint8_t aBits
) {
521 MOZ_ASSERT(aBits
< 32);
522 return (aValue
<< aBits
) | (aValue
>> (32 - aBits
));
526 static const uint32_t kGoldenRatioU32
= 0x9e3779b9u
;
528 return std::accumulate(aName
.BeginReading(), aName
.EndReading(), uint32_t(0),
529 [](uint32_t hash
, char16_t ch
) {
530 return kGoldenRatioU32
*
531 (Helper::RotateBitsLeft32(hash
, 5) ^ ch
);
535 nsresult
ClampResultCode(nsresult aResultCode
) {
536 if (NS_SUCCEEDED(aResultCode
) ||
537 NS_ERROR_GET_MODULE(aResultCode
) == NS_ERROR_MODULE_DOM_INDEXEDDB
) {
541 switch (aResultCode
) {
542 case NS_ERROR_FILE_NO_DEVICE_SPACE
:
543 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR
;
544 case NS_ERROR_STORAGE_CONSTRAINT
:
545 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR
;
548 nsPrintfCString
message("Converting non-IndexedDB error code (0x%" PRIX32
550 "NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR",
551 static_cast<uint32_t>(aResultCode
));
552 NS_WARNING(message
.get());
558 IDB_REPORT_INTERNAL_ERR();
559 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
562 nsAutoString
GetDatabaseFilenameBase(const nsAString
& aDatabaseName
) {
563 nsAutoString databaseFilenameBase
;
565 // WARNING: do not change this hash function. See the comment in HashName()
567 databaseFilenameBase
.AppendInt(HashName(aDatabaseName
));
569 nsAutoCString escapedName
;
570 if (!NS_Escape(NS_ConvertUTF16toUTF8(aDatabaseName
), escapedName
,
572 MOZ_CRASH("Can't escape database name!");
575 const char* forwardIter
= escapedName
.BeginReading();
576 const char* backwardIter
= escapedName
.EndReading() - 1;
578 nsAutoCString substring
;
579 while (forwardIter
<= backwardIter
&& substring
.Length() < 21) {
580 if (substring
.Length() % 2) {
581 substring
.Append(*backwardIter
--);
583 substring
.Append(*forwardIter
++);
587 databaseFilenameBase
.AppendASCII(substring
.get(), substring
.Length());
589 return databaseFilenameBase
;
592 Result
<nsCOMPtr
<nsIFileURL
>, nsresult
> GetDatabaseFileURL(
593 nsIFile
& aDatabaseFile
, const int64_t aDirectoryLockId
,
594 const Maybe
<CipherKey
>& aMaybeKey
) {
595 MOZ_ASSERT(aDirectoryLockId
>= -1);
598 const auto& protocolHandler
,
599 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr
<nsIProtocolHandler
>,
600 MOZ_SELECT_OVERLOAD(do_GetService
),
601 NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX
"file"));
603 QM_TRY_INSPECT(const auto& fileHandler
,
604 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr
<nsIFileProtocolHandler
>,
605 MOZ_SELECT_OVERLOAD(do_QueryInterface
),
608 QM_TRY_INSPECT(const auto& mutator
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
609 nsCOMPtr
<nsIURIMutator
>, fileHandler
,
610 NewFileURIMutator
, &aDatabaseFile
));
612 // aDirectoryLockId should only be -1 when we are called
613 // - from DatabaseFileManager::InitDirectory when the temporary storage
614 // hasn't been initialized yet. At that time, the in-memory objects (e.g.
615 // OriginInfo) are only being created so it doesn't make sense to tunnel
616 // quota information to QuotaVFS to get corresponding QuotaObject instances
618 // - from DeleteDatabaseOp::LoadPreviousVersion, since this might require
619 // temporarily exceeding the quota limit before the database can be
621 const auto directoryLockIdClause
=
622 aDirectoryLockId
>= 0
623 ? "&directoryLockId="_ns
+ IntToCString(aDirectoryLockId
)
626 const auto keyClause
= [&aMaybeKey
] {
627 nsAutoCString keyClause
;
629 keyClause
.AssignLiteral("&key=");
630 for (uint8_t byte
: IndexedDBCipherStrategy::SerializeKey(*aMaybeKey
)) {
631 keyClause
.AppendPrintf("%02x", byte
);
637 QM_TRY_UNWRAP(auto result
, ([&mutator
, &directoryLockIdClause
, &keyClause
] {
638 nsCOMPtr
<nsIFileURL
> result
;
639 nsresult rv
= NS_MutateURI(mutator
)
640 .SetQuery("cache=private"_ns
+
641 directoryLockIdClause
+ keyClause
)
643 return NS_SUCCEEDED(rv
)
644 ? Result
<nsCOMPtr
<nsIFileURL
>, nsresult
>{result
}
651 nsresult
SetDefaultPragmas(mozIStorageConnection
& aConnection
) {
652 MOZ_ASSERT(!NS_IsMainThread());
654 static constexpr auto kBuiltInPragmas
=
655 // We use foreign keys in DEBUG builds only because there is a performance
656 // cost to using them.
657 "PRAGMA foreign_keys = "
665 // The "INSERT OR REPLACE" statement doesn't fire the update trigger,
666 // instead it fires only the insert trigger. This confuses the update
667 // refcount function. This behavior changes with enabled recursive
668 // triggers, so the statement fires the delete trigger first and then the
670 "PRAGMA recursive_triggers = ON;"
672 // We aggressively truncate the database file when idle so don't bother
673 // overwriting the WAL with 0 during active periods.
674 "PRAGMA secure_delete = OFF;"_ns
;
676 QM_TRY(MOZ_TO_RESULT(aConnection
.ExecuteSimpleSQL(kBuiltInPragmas
)));
678 QM_TRY(MOZ_TO_RESULT(aConnection
.ExecuteSimpleSQL(nsAutoCString
{
679 "PRAGMA synchronous = "_ns
+
680 (IndexedDatabaseManager::FullSynchronous() ? "FULL"_ns
: "NORMAL"_ns
) +
684 if (kSQLiteGrowthIncrement
) {
685 // This is just an optimization so ignore the failure if the disk is
686 // currently too full.
687 QM_TRY(QM_OR_ELSE_WARN_IF(
690 aConnection
.SetGrowthIncrement(kSQLiteGrowthIncrement
, ""_ns
)),
692 IsSpecificError
<NS_ERROR_FILE_TOO_BIG
>,
701 nsresult
SetJournalMode(mozIStorageConnection
& aConnection
) {
702 MOZ_ASSERT(!NS_IsMainThread());
704 // Try enabling WAL mode. This can fail in various circumstances so we have to
705 // check the results here.
706 constexpr auto journalModeQueryStart
= "PRAGMA journal_mode = "_ns
;
707 constexpr auto journalModeWAL
= "wal"_ns
;
709 QM_TRY_INSPECT(const auto& stmt
,
710 CreateAndExecuteSingleStepStatement(
711 aConnection
, journalModeQueryStart
+ journalModeWAL
));
714 const auto& journalMode
,
715 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCString
, *stmt
, GetUTF8String
, 0));
717 if (journalMode
.Equals(journalModeWAL
)) {
718 // WAL mode successfully enabled. Maybe set limits on its size here.
719 if (kMaxWALPages
>= 0) {
720 QM_TRY(MOZ_TO_RESULT(aConnection
.ExecuteSimpleSQL(
721 "PRAGMA wal_autocheckpoint = "_ns
+ IntToCString(kMaxWALPages
))));
724 NS_WARNING("Failed to set WAL mode, falling back to normal journal mode.");
726 QM_TRY(MOZ_TO_RESULT(
727 aConnection
.ExecuteSimpleSQL(journalModeQueryStart
+ "truncate"_ns
)));
734 Result
<MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>>, nsresult
> OpenDatabase(
735 mozIStorageService
& aStorageService
, nsIFileURL
& aFileURL
,
736 const uint32_t aTelemetryId
= 0) {
737 const nsAutoCString telemetryFilename
=
738 aTelemetryId
? "indexedDB-"_ns
+ IntToCString(aTelemetryId
) +
739 NS_ConvertUTF16toUTF8(kSQLiteSuffix
)
742 QM_TRY_UNWRAP(auto connection
,
743 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
744 nsCOMPtr
<mozIStorageConnection
>, aStorageService
,
745 OpenDatabaseWithFileURL
, &aFileURL
, telemetryFilename
,
746 mozIStorageService::CONNECTION_DEFAULT
));
748 return WrapMovingNotNull(std::move(connection
));
751 Result
<MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>>, nsresult
>
752 OpenDatabaseAndHandleBusy(mozIStorageService
& aStorageService
,
753 nsIFileURL
& aFileURL
,
754 const uint32_t aTelemetryId
= 0) {
755 MOZ_ASSERT(!NS_IsMainThread());
756 MOZ_ASSERT(!IsOnBackgroundThread());
758 using ConnectionType
= Maybe
<MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>>>;
760 QM_TRY_UNWRAP(auto connection
,
763 OpenDatabase(aStorageService
, aFileURL
, aTelemetryId
)
764 .map([](auto connection
) -> ConnectionType
{
765 return Some(std::move(connection
));
768 IsSpecificError
<NS_ERROR_STORAGE_BUSY
>,
770 ErrToDefaultOk
<ConnectionType
>));
772 if (connection
.isNothing()) {
776 MOZ_ALWAYS_SUCCEEDS(aFileURL
.GetFileName(path
));
778 nsPrintfCString
message(
779 "Received NS_ERROR_STORAGE_BUSY when attempting to open database "
780 "'%s', retrying for up to 10 seconds",
782 NS_WARNING(message
.get());
786 // Another thread must be checkpointing the WAL. Wait up to 10 seconds for
788 const TimeStamp start
= TimeStamp::NowLoRes();
791 PR_Sleep(PR_MillisecondsToInterval(100));
793 QM_TRY_UNWRAP(connection
,
796 OpenDatabase(aStorageService
, aFileURL
, aTelemetryId
)
797 .map([](auto connection
) -> ConnectionType
{
798 return Some(std::move(connection
));
801 ([&start
](nsresult aValue
) {
802 return aValue
== NS_ERROR_STORAGE_BUSY
&&
803 TimeStamp::NowLoRes() - start
<=
804 TimeDuration::FromSeconds(10);
807 ErrToDefaultOk
<ConnectionType
>));
808 } while (connection
.isNothing());
811 return connection
.extract();
814 // Returns true if a given nsIFile exists and is a directory. Returns false if
815 // it doesn't exist. Returns an error if it exists, but is not a directory, or
816 // any other error occurs.
817 Result
<bool, nsresult
> ExistsAsDirectory(nsIFile
& aDirectory
) {
818 QM_TRY_INSPECT(const bool& exists
,
819 MOZ_TO_RESULT_INVOKE_MEMBER(aDirectory
, Exists
));
822 QM_TRY_INSPECT(const bool& isDirectory
,
823 MOZ_TO_RESULT_INVOKE_MEMBER(aDirectory
, IsDirectory
));
825 QM_TRY(OkIf(isDirectory
), Err(NS_ERROR_FAILURE
));
831 constexpr nsresult
mapNoDeviceSpaceError(nsresult aRv
) {
832 if (aRv
== NS_ERROR_FILE_NO_DEVICE_SPACE
) {
833 // mozstorage translates SQLITE_FULL to
834 // NS_ERROR_FILE_NO_DEVICE_SPACE, which we know better as
835 // NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
836 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR
;
841 Result
<MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>>, nsresult
>
842 CreateStorageConnection(nsIFile
& aDBFile
, nsIFile
& aFMDirectory
,
843 const nsAString
& aName
, const nsACString
& aOrigin
,
844 const int64_t aDirectoryLockId
,
845 const uint32_t aTelemetryId
,
846 const Maybe
<CipherKey
>& aMaybeKey
) {
847 AssertIsOnIOThread();
848 MOZ_ASSERT(aDirectoryLockId
>= -1);
850 AUTO_PROFILER_LABEL("CreateStorageConnection", DOM
);
852 QM_TRY_INSPECT(const auto& dbFileUrl
,
853 GetDatabaseFileURL(aDBFile
, aDirectoryLockId
, aMaybeKey
));
855 QM_TRY_INSPECT(const auto& storageService
,
856 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr
<mozIStorageService
>,
857 MOZ_SELECT_OVERLOAD(do_GetService
),
858 MOZ_STORAGE_SERVICE_CONTRACTID
));
864 OpenDatabaseAndHandleBusy(*storageService
, *dbFileUrl
, aTelemetryId
)
865 .map([](auto connection
) -> nsCOMPtr
<mozIStorageConnection
> {
866 return std::move(connection
).unwrapBasePtr();
869 ([&aName
](nsresult aValue
) {
870 // If we're just opening the database during origin initialization,
871 // then we don't want to erase any files. The failure here will fail
872 // origin initialization too.
873 return IsDatabaseCorruptionError(aValue
) && !aName
.IsVoid();
876 ErrToDefaultOk
<nsCOMPtr
<mozIStorageConnection
>>));
879 // XXX Shouldn't we also update quota usage?
881 // Nuke the database file.
882 QM_TRY(MOZ_TO_RESULT(aDBFile
.Remove(false)));
883 QM_TRY_INSPECT(const bool& existsAsDirectory
,
884 ExistsAsDirectory(aFMDirectory
));
886 if (existsAsDirectory
) {
887 QM_TRY(MOZ_TO_RESULT(aFMDirectory
.Remove(true)));
890 QM_TRY_UNWRAP(connection
, OpenDatabaseAndHandleBusy(
891 *storageService
, *dbFileUrl
, aTelemetryId
));
894 QM_TRY(MOZ_TO_RESULT(SetDefaultPragmas(*connection
)));
895 QM_TRY(MOZ_TO_RESULT(connection
->EnableModule("filesystem"_ns
)));
897 // Check to make sure that the database schema is correct.
898 QM_TRY_INSPECT(const int32_t& schemaVersion
,
899 MOZ_TO_RESULT_INVOKE_MEMBER(connection
, GetSchemaVersion
));
901 // Unknown schema will fail origin initialization too.
902 QM_TRY(OkIf(schemaVersion
|| !aName
.IsVoid()),
903 Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
), [](const auto&) {
904 IDB_WARNING("Unable to open IndexedDB database, schema is not set!");
908 OkIf(schemaVersion
<= kSQLiteSchemaVersion
),
909 Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
), [](const auto&) {
910 IDB_WARNING("Unable to open IndexedDB database, schema is too high!");
913 bool journalModeSet
= false;
915 if (schemaVersion
!= kSQLiteSchemaVersion
) {
916 const bool newDatabase
= !schemaVersion
;
919 // Set the page size first.
920 const auto sqlitePageSizeOverride
=
921 aMaybeKey
? 8192 : kSQLitePageSizeOverride
;
922 if (sqlitePageSizeOverride
) {
923 QM_TRY(MOZ_TO_RESULT(connection
->ExecuteSimpleSQL(nsPrintfCString(
924 "PRAGMA page_size = %" PRIu32
";", sqlitePageSizeOverride
))));
927 // We have to set the auto_vacuum mode before opening a transaction.
928 QM_TRY((MOZ_TO_RESULT_INVOKE_MEMBER(
929 connection
, ExecuteSimpleSQL
,
931 // Turn on full auto_vacuum mode to reclaim disk space on
932 // mobile devices (at the cost of some COMMIT speed).
933 "PRAGMA auto_vacuum = FULL;"_ns
935 // Turn on incremental auto_vacuum mode on desktop builds.
936 "PRAGMA auto_vacuum = INCREMENTAL;"_ns
939 .mapErr(mapNoDeviceSpaceError
)));
941 QM_TRY(MOZ_TO_RESULT(SetJournalMode(*connection
)));
943 journalModeSet
= true;
946 // Disable foreign key support while upgrading. This has to be done before
947 // starting a transaction.
949 connection
->ExecuteSimpleSQL("PRAGMA foreign_keys = OFF;"_ns
));
953 bool vacuumNeeded
= false;
955 mozStorageTransaction
transaction(
956 connection
.get(), false, mozIStorageConnection::TRANSACTION_IMMEDIATE
);
958 QM_TRY(MOZ_TO_RESULT(transaction
.Start()));
961 QM_TRY(MOZ_TO_RESULT(CreateTables(*connection
)));
966 const int32_t& schemaVersion
,
967 MOZ_TO_RESULT_INVOKE_MEMBER(connection
, GetSchemaVersion
),
968 QM_ASSERT_UNREACHABLE
);
969 MOZ_ASSERT(schemaVersion
== kSQLiteSchemaVersion
);
973 // The parameter names are not used, parameters are bound by index only
974 // locally in the same function.
977 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
978 nsCOMPtr
<mozIStorageStatement
>, connection
, CreateStatement
,
979 "INSERT INTO database (name, origin) "
980 "VALUES (:name, :origin)"_ns
));
982 QM_TRY(MOZ_TO_RESULT(stmt
->BindStringByIndex(0, aName
)));
983 QM_TRY(MOZ_TO_RESULT(stmt
->BindUTF8StringByIndex(1, aOrigin
)));
984 QM_TRY(MOZ_TO_RESULT(stmt
->Execute()));
986 QM_TRY_UNWRAP(vacuumNeeded
, MaybeUpgradeSchema(*connection
, schemaVersion
,
987 aFMDirectory
, aOrigin
));
990 QM_TRY(MOZ_TO_RESULT_INVOKE_MEMBER(transaction
, Commit
)
991 .mapErr(mapNoDeviceSpaceError
));
995 // Re-enable foreign key support after doing a foreign key check.
996 QM_TRY_INSPECT(const bool& foreignKeyError
,
997 CreateAndExecuteSingleStepStatement
<
998 SingleStepResult::ReturnNullIfNoResult
>(
999 *connection
, "PRAGMA foreign_key_check;"_ns
),
1000 QM_ASSERT_UNREACHABLE
);
1002 MOZ_ASSERT(!foreignKeyError
, "Database has inconsisistent foreign keys!");
1004 MOZ_ALWAYS_SUCCEEDS(
1005 connection
->ExecuteSimpleSQL("PRAGMA foreign_keys = OFF;"_ns
));
1009 if (kSQLitePageSizeOverride
&& !newDatabase
) {
1010 QM_TRY_INSPECT(const auto& stmt
,
1011 CreateAndExecuteSingleStepStatement(
1012 *connection
, "PRAGMA page_size;"_ns
));
1014 QM_TRY_INSPECT(const int32_t& pageSize
,
1015 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt32
, 0));
1016 MOZ_ASSERT(pageSize
>= 512 && pageSize
<= 65536);
1018 if (kSQLitePageSizeOverride
!= uint32_t(pageSize
)) {
1019 // We must not be in WAL journal mode to change the page size.
1020 QM_TRY(MOZ_TO_RESULT(
1021 connection
->ExecuteSimpleSQL("PRAGMA journal_mode = DELETE;"_ns
)));
1023 QM_TRY_INSPECT(const auto& stmt
,
1024 CreateAndExecuteSingleStepStatement(
1025 *connection
, "PRAGMA journal_mode;"_ns
));
1027 QM_TRY_INSPECT(const auto& journalMode
,
1028 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCString
, *stmt
,
1031 if (journalMode
.EqualsLiteral("delete")) {
1032 // Successfully set to rollback journal mode so changing the page size
1033 // is possible with a VACUUM.
1034 QM_TRY(MOZ_TO_RESULT(connection
->ExecuteSimpleSQL(nsPrintfCString(
1035 "PRAGMA page_size = %" PRIu32
";", kSQLitePageSizeOverride
))));
1037 // We will need to VACUUM in order to change the page size.
1038 vacuumNeeded
= true;
1041 "Failed to set journal_mode for database, unable to "
1042 "change the page size!");
1048 QM_TRY(MOZ_TO_RESULT(connection
->ExecuteSimpleSQL("VACUUM;"_ns
)));
1051 if (newDatabase
|| vacuumNeeded
) {
1052 if (journalModeSet
) {
1053 // Make sure we checkpoint to get an accurate file size.
1054 QM_TRY(MOZ_TO_RESULT(
1055 connection
->ExecuteSimpleSQL("PRAGMA wal_checkpoint(FULL);"_ns
)));
1058 QM_TRY_INSPECT(const int64_t& fileSize
,
1059 MOZ_TO_RESULT_INVOKE_MEMBER(aDBFile
, GetFileSize
));
1060 MOZ_ASSERT(fileSize
> 0);
1062 PRTime vacuumTime
= PR_Now();
1063 MOZ_ASSERT(vacuumTime
);
1065 // The parameter names are not used, parameters are bound by index only
1066 // locally in the same function.
1068 const auto& vacuumTimeStmt
,
1069 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr
<mozIStorageStatement
>,
1070 connection
, CreateStatement
,
1072 "SET last_vacuum_time = :time"
1073 ", last_vacuum_size = :size;"_ns
));
1075 QM_TRY(MOZ_TO_RESULT(vacuumTimeStmt
->BindInt64ByIndex(0, vacuumTime
)));
1076 QM_TRY(MOZ_TO_RESULT(vacuumTimeStmt
->BindInt64ByIndex(1, fileSize
)));
1077 QM_TRY(MOZ_TO_RESULT(vacuumTimeStmt
->Execute()));
1081 if (!journalModeSet
) {
1082 QM_TRY(MOZ_TO_RESULT(SetJournalMode(*connection
)));
1085 return WrapMovingNotNullUnchecked(std::move(connection
));
1088 nsCOMPtr
<nsIFile
> GetFileForPath(const nsAString
& aPath
) {
1089 MOZ_ASSERT(!aPath
.IsEmpty());
1091 QM_TRY_RETURN(QM_NewLocalFile(aPath
), nullptr);
1094 Result
<MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>>, nsresult
>
1095 GetStorageConnection(nsIFile
& aDatabaseFile
, const int64_t aDirectoryLockId
,
1096 const uint32_t aTelemetryId
,
1097 const Maybe
<CipherKey
>& aMaybeKey
) {
1098 MOZ_ASSERT(!NS_IsMainThread());
1099 MOZ_ASSERT(!IsOnBackgroundThread());
1100 MOZ_ASSERT(aDirectoryLockId
>= 0);
1102 AUTO_PROFILER_LABEL("GetStorageConnection", DOM
);
1104 QM_TRY_INSPECT(const bool& exists
,
1105 MOZ_TO_RESULT_INVOKE_MEMBER(aDatabaseFile
, Exists
));
1107 QM_TRY(OkIf(exists
), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
),
1108 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
1111 const auto& dbFileUrl
,
1112 GetDatabaseFileURL(aDatabaseFile
, aDirectoryLockId
, aMaybeKey
));
1114 QM_TRY_INSPECT(const auto& storageService
,
1115 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr
<mozIStorageService
>,
1116 MOZ_SELECT_OVERLOAD(do_GetService
),
1117 MOZ_STORAGE_SERVICE_CONTRACTID
));
1120 nsCOMPtr
<mozIStorageConnection
> connection
,
1121 OpenDatabaseAndHandleBusy(*storageService
, *dbFileUrl
, aTelemetryId
));
1123 QM_TRY(MOZ_TO_RESULT(SetDefaultPragmas(*connection
)));
1125 QM_TRY(MOZ_TO_RESULT(SetJournalMode(*connection
)));
1127 return WrapMovingNotNullUnchecked(std::move(connection
));
1130 Result
<MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>>, nsresult
>
1131 GetStorageConnection(const nsAString
& aDatabaseFilePath
,
1132 const int64_t aDirectoryLockId
,
1133 const uint32_t aTelemetryId
,
1134 const Maybe
<CipherKey
>& aMaybeKey
) {
1135 MOZ_ASSERT(!NS_IsMainThread());
1136 MOZ_ASSERT(!IsOnBackgroundThread());
1137 MOZ_ASSERT(!aDatabaseFilePath
.IsEmpty());
1138 MOZ_ASSERT(StringEndsWith(aDatabaseFilePath
, kSQLiteSuffix
));
1139 MOZ_ASSERT(aDirectoryLockId
>= 0);
1141 nsCOMPtr
<nsIFile
> dbFile
= GetFileForPath(aDatabaseFilePath
);
1143 QM_TRY(OkIf(dbFile
), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
),
1144 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
1146 return GetStorageConnection(*dbFile
, aDirectoryLockId
, aTelemetryId
,
1150 /*******************************************************************************
1151 * ConnectionPool declarations
1152 ******************************************************************************/
1154 class DatabaseConnection final
: public CachingDatabaseConnection
{
1155 friend class ConnectionPool
;
1157 enum class CheckpointMode
{ Full
, Restart
, Truncate
};
1160 class AutoSavepoint
;
1161 class UpdateRefcountFunction
;
1164 InitializedOnce
<const NotNull
<SafeRefPtr
<DatabaseFileManager
>>> mFileManager
;
1165 RefPtr
<UpdateRefcountFunction
> mUpdateRefcountFunction
;
1166 RefPtr
<QuotaObject
> mQuotaObject
;
1167 RefPtr
<QuotaObject
> mJournalQuotaObject
;
1168 bool mInReadTransaction
;
1169 bool mInWriteTransaction
;
1172 uint32_t mDEBUGSavepointCount
;
1176 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DatabaseConnection
)
1178 UpdateRefcountFunction
* GetUpdateRefcountFunction() const {
1179 AssertIsOnConnectionThread();
1181 return mUpdateRefcountFunction
;
1184 nsresult
BeginWriteTransaction();
1186 nsresult
CommitWriteTransaction();
1188 void RollbackWriteTransaction();
1190 void FinishWriteTransaction();
1192 nsresult
StartSavepoint();
1194 nsresult
ReleaseSavepoint();
1196 nsresult
RollbackSavepoint();
1198 nsresult
Checkpoint() {
1199 AssertIsOnConnectionThread();
1201 return CheckpointInternal(CheckpointMode::Full
);
1204 void DoIdleProcessing(bool aNeedsCheckpoint
);
1208 nsresult
DisableQuotaChecks();
1210 void EnableQuotaChecks();
1214 MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>> aStorageConnection
,
1215 MovingNotNull
<SafeRefPtr
<DatabaseFileManager
>> aFileManager
);
1217 ~DatabaseConnection();
1221 nsresult
CheckpointInternal(CheckpointMode aMode
);
1223 Result
<uint32_t, nsresult
> GetFreelistCount(
1224 CachedStatement
& aCachedStatement
);
1227 * On success, returns whether some pages were freed.
1229 Result
<bool, nsresult
> ReclaimFreePagesWhileIdle(
1230 CachedStatement
& aFreelistStatement
, CachedStatement
& aRollbackStatement
,
1231 uint32_t aFreelistCount
, bool aNeedsCheckpoint
);
1233 Result
<int64_t, nsresult
> GetFileSize(const nsAString
& aPath
);
1236 class MOZ_STACK_CLASS
DatabaseConnection::AutoSavepoint final
{
1237 DatabaseConnection
* mConnection
;
1239 const TransactionBase
* mDEBUGTransaction
;
1246 nsresult
Start(const TransactionBase
& aTransaction
);
1251 class DatabaseConnection::UpdateRefcountFunction final
1252 : public mozIStorageFunction
{
1253 class FileInfoEntry
;
1255 enum class UpdateType
{ Increment
, Decrement
};
1257 DatabaseConnection
* const mConnection
;
1258 DatabaseFileManager
& mFileManager
;
1259 nsClassHashtable
<nsUint64HashKey
, FileInfoEntry
> mFileInfoEntries
;
1260 nsTHashMap
<nsUint64HashKey
, NotNull
<FileInfoEntry
*>> mSavepointEntriesIndex
;
1262 nsTArray
<int64_t> mJournalsToCreateBeforeCommit
;
1263 nsTArray
<int64_t> mJournalsToRemoveAfterCommit
;
1264 nsTArray
<int64_t> mJournalsToRemoveAfterAbort
;
1270 NS_DECL_MOZISTORAGEFUNCTION
1272 UpdateRefcountFunction(DatabaseConnection
* aConnection
,
1273 DatabaseFileManager
& aFileManager
);
1275 nsresult
WillCommit();
1281 void StartSavepoint();
1283 void ReleaseSavepoint();
1285 void RollbackSavepoint();
1290 ~UpdateRefcountFunction() = default;
1292 nsresult
ProcessValue(mozIStorageValueArray
* aValues
, int32_t aIndex
,
1293 UpdateType aUpdateType
);
1295 nsresult
CreateJournals();
1297 nsresult
RemoveJournals(const nsTArray
<int64_t>& aJournals
);
1300 class DatabaseConnection::UpdateRefcountFunction::FileInfoEntry final
{
1301 SafeRefPtr
<DatabaseFileInfo
> mFileInfo
;
1303 int32_t mSavepointDelta
;
1306 explicit FileInfoEntry(SafeRefPtr
<DatabaseFileInfo
> aFileInfo
)
1307 : mFileInfo(std::move(aFileInfo
)), mDelta(0), mSavepointDelta(0) {
1308 MOZ_COUNT_CTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry
);
1311 void IncDeltas(bool aUpdateSavepointDelta
) {
1313 if (aUpdateSavepointDelta
) {
1317 void DecDeltas(bool aUpdateSavepointDelta
) {
1319 if (aUpdateSavepointDelta
) {
1323 void DecBySavepointDelta() { mDelta
-= mSavepointDelta
; }
1324 SafeRefPtr
<DatabaseFileInfo
> ReleaseFileInfo() {
1325 return std::move(mFileInfo
);
1327 void MaybeUpdateDBRefs() {
1329 mFileInfo
->UpdateDBRefs(mDelta
);
1333 int32_t Delta() const { return mDelta
; }
1334 int32_t SavepointDelta() const { return mSavepointDelta
; }
1337 MOZ_COUNT_DTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry
);
1341 class ConnectionPool final
{
1343 class FinishCallback
;
1346 class ConnectionRunnable
;
1347 class CloseConnectionRunnable
;
1348 struct DatabaseInfo
;
1349 struct DatabasesCompleteCallback
;
1350 class FinishCallbackWrapper
;
1351 class IdleConnectionRunnable
;
1353 class ThreadRunnable
;
1354 class TransactionInfo
;
1355 struct TransactionInfoPair
;
1357 struct IdleResource
{
1358 TimeStamp mIdleTime
;
1360 IdleResource(const IdleResource
& aOther
) = delete;
1361 IdleResource(IdleResource
&& aOther
) noexcept
1362 : IdleResource(aOther
.mIdleTime
) {}
1363 IdleResource
& operator=(const IdleResource
& aOther
) = delete;
1364 IdleResource
& operator=(IdleResource
&& aOther
) = delete;
1367 explicit IdleResource(const TimeStamp
& aIdleTime
);
1372 struct IdleDatabaseInfo final
: public IdleResource
{
1373 InitializedOnce
<const NotNull
<DatabaseInfo
*>> mDatabaseInfo
;
1376 explicit IdleDatabaseInfo(DatabaseInfo
& aDatabaseInfo
);
1378 IdleDatabaseInfo(const IdleDatabaseInfo
& aOther
) = delete;
1379 IdleDatabaseInfo(IdleDatabaseInfo
&& aOther
) noexcept
1380 : IdleResource(std::move(aOther
)),
1381 mDatabaseInfo
{std::move(aOther
.mDatabaseInfo
)} {
1382 MOZ_ASSERT(mDatabaseInfo
);
1384 MOZ_COUNT_CTOR(ConnectionPool::IdleDatabaseInfo
);
1386 IdleDatabaseInfo
& operator=(const IdleDatabaseInfo
& aOther
) = delete;
1387 IdleDatabaseInfo
& operator=(IdleDatabaseInfo
&& aOther
) = delete;
1389 ~IdleDatabaseInfo();
1391 bool operator==(const IdleDatabaseInfo
& aOther
) const {
1392 return *mDatabaseInfo
== *aOther
.mDatabaseInfo
;
1395 bool operator==(const DatabaseInfo
* aDatabaseInfo
) const {
1396 return *mDatabaseInfo
== aDatabaseInfo
;
1399 bool operator<(const IdleDatabaseInfo
& aOther
) const {
1400 return mIdleTime
< aOther
.mIdleTime
;
1408 ThreadInfo(nsCOMPtr
<nsIThread
> aThread
, RefPtr
<ThreadRunnable
> aRunnable
)
1409 : mThread
{std::move(aThread
)}, mRunnable
{std::move(aRunnable
)} {
1410 AssertIsOnBackgroundThread();
1413 MOZ_COUNT_CTOR(ConnectionPool::ThreadInfo
);
1416 ThreadInfo(const ThreadInfo
& aOther
) = delete;
1417 ThreadInfo
& operator=(const ThreadInfo
& aOther
) = delete;
1419 ThreadInfo(ThreadInfo
&& aOther
) noexcept
;
1420 ThreadInfo
& operator=(ThreadInfo
&& aOther
) = default;
1422 bool IsValid() const {
1423 const bool res
= mThread
;
1432 void AssertValid() const {
1433 MOZ_ASSERT(mThread
);
1434 MOZ_ASSERT(mRunnable
);
1437 void AssertEmpty() const {
1438 MOZ_ASSERT(!mThread
);
1439 MOZ_ASSERT(!mRunnable
);
1442 nsIThread
& ThreadRef() {
1447 std::tuple
<nsCOMPtr
<nsIThread
>, RefPtr
<ThreadRunnable
>> Forget() {
1450 return {std::move(mThread
), std::move(mRunnable
)};
1455 bool operator==(const ThreadInfo
& aOther
) const {
1456 return mThread
== aOther
.mThread
&& mRunnable
== aOther
.mRunnable
;
1460 nsCOMPtr
<nsIThread
> mThread
;
1461 RefPtr
<ThreadRunnable
> mRunnable
;
1464 struct IdleThreadInfo final
: public IdleResource
{
1465 ThreadInfo mThreadInfo
;
1467 explicit IdleThreadInfo(ThreadInfo aThreadInfo
);
1469 IdleThreadInfo(const IdleThreadInfo
& aOther
) = delete;
1470 IdleThreadInfo(IdleThreadInfo
&& aOther
) noexcept
1471 : IdleResource(std::move(aOther
)),
1472 mThreadInfo(std::move(aOther
.mThreadInfo
)) {
1473 AssertIsOnBackgroundThread();
1474 mThreadInfo
.AssertValid();
1476 MOZ_COUNT_CTOR(ConnectionPool::IdleThreadInfo
);
1478 IdleThreadInfo
& operator=(const IdleThreadInfo
& aOther
) = delete;
1479 IdleThreadInfo
& operator=(IdleThreadInfo
&& aOther
) = delete;
1483 bool operator==(const IdleThreadInfo
& aOther
) const {
1484 return mThreadInfo
== aOther
.mThreadInfo
;
1487 bool operator<(const IdleThreadInfo
& aOther
) const {
1488 return mIdleTime
< aOther
.mIdleTime
;
1492 // This mutex guards mDatabases, see below.
1493 Mutex mDatabasesMutex MOZ_UNANNOTATED
;
1495 nsTArray
<IdleThreadInfo
> mIdleThreads
;
1496 nsTArray
<IdleDatabaseInfo
> mIdleDatabases
;
1497 nsTArray
<NotNull
<DatabaseInfo
*>> mDatabasesPerformingIdleMaintenance
;
1498 nsCOMPtr
<nsITimer
> mIdleTimer
;
1499 TimeStamp mTargetIdleTime
;
1501 // Only modifed on the owning thread, but read on multiple threads. Therefore
1502 // all modifications and all reads off the owning thread must be protected by
1504 nsClassHashtable
<nsCStringHashKey
, DatabaseInfo
> mDatabases
;
1506 nsClassHashtable
<nsUint64HashKey
, TransactionInfo
> mTransactions
;
1507 nsTArray
<NotNull
<TransactionInfo
*>> mQueuedTransactions
;
1509 nsTArray
<UniquePtr
<DatabasesCompleteCallback
>> mCompleteCallbacks
;
1511 uint64_t mNextTransactionId
;
1512 uint32_t mTotalThreadCount
;
1513 FlippedOnce
<false> mShutdownRequested
;
1514 FlippedOnce
<false> mShutdownComplete
;
1519 void AssertIsOnOwningThread() const {
1520 NS_ASSERT_OWNINGTHREAD(ConnectionPool
);
1523 Result
<RefPtr
<DatabaseConnection
>, nsresult
> GetOrCreateConnection(
1524 const Database
& aDatabase
);
1526 uint64_t Start(const nsID
& aBackgroundChildLoggingId
,
1527 const nsACString
& aDatabaseId
, int64_t aLoggingSerialNumber
,
1528 const nsTArray
<nsString
>& aObjectStoreNames
,
1529 bool aIsWriteTransaction
,
1530 TransactionDatabaseOperationBase
* aTransactionOp
);
1532 void Dispatch(uint64_t aTransactionId
, nsIRunnable
* aRunnable
);
1534 void Finish(uint64_t aTransactionId
, FinishCallback
* aCallback
);
1536 void CloseDatabaseWhenIdle(const nsACString
& aDatabaseId
) {
1537 Unused
<< CloseDatabaseWhenIdleInternal(aDatabaseId
);
1540 void WaitForDatabasesToComplete(nsTArray
<nsCString
>&& aDatabaseIds
,
1541 nsIRunnable
* aCallback
);
1545 NS_INLINE_DECL_REFCOUNTING(ConnectionPool
)
1550 static void IdleTimerCallback(nsITimer
* aTimer
, void* aClosure
);
1554 void AdjustIdleTimer();
1556 void CancelIdleTimer();
1558 void ShutdownThread(ThreadInfo aThreadInfo
);
1560 void CloseIdleDatabases();
1562 void ShutdownIdleThreads();
1564 bool ScheduleTransaction(TransactionInfo
& aTransactionInfo
,
1565 bool aFromQueuedTransactions
);
1567 void NoteFinishedTransaction(uint64_t aTransactionId
);
1569 void ScheduleQueuedTransactions(ThreadInfo aThreadInfo
);
1571 void NoteIdleDatabase(DatabaseInfo
& aDatabaseInfo
);
1573 void NoteClosedDatabase(DatabaseInfo
& aDatabaseInfo
);
1575 bool MaybeFireCallback(DatabasesCompleteCallback
* aCallback
);
1577 void PerformIdleDatabaseMaintenance(DatabaseInfo
& aDatabaseInfo
);
1579 void CloseDatabase(DatabaseInfo
& aDatabaseInfo
) const;
1581 bool CloseDatabaseWhenIdleInternal(const nsACString
& aDatabaseId
);
1584 class ConnectionPool::ConnectionRunnable
: public Runnable
{
1586 DatabaseInfo
& mDatabaseInfo
;
1587 nsCOMPtr
<nsIEventTarget
> mOwningEventTarget
;
1589 explicit ConnectionRunnable(DatabaseInfo
& aDatabaseInfo
);
1591 ~ConnectionRunnable() override
= default;
1594 class ConnectionPool::IdleConnectionRunnable final
: public ConnectionRunnable
{
1595 const bool mNeedsCheckpoint
;
1598 IdleConnectionRunnable(DatabaseInfo
& aDatabaseInfo
, bool aNeedsCheckpoint
)
1599 : ConnectionRunnable(aDatabaseInfo
), mNeedsCheckpoint(aNeedsCheckpoint
) {}
1601 NS_INLINE_DECL_REFCOUNTING_INHERITED(IdleConnectionRunnable
,
1605 ~IdleConnectionRunnable() override
= default;
1610 class ConnectionPool::CloseConnectionRunnable final
1611 : public ConnectionRunnable
{
1613 explicit CloseConnectionRunnable(DatabaseInfo
& aDatabaseInfo
)
1614 : ConnectionRunnable(aDatabaseInfo
) {}
1616 NS_INLINE_DECL_REFCOUNTING_INHERITED(CloseConnectionRunnable
,
1620 ~CloseConnectionRunnable() override
= default;
1625 struct ConnectionPool::DatabaseInfo final
{
1626 friend class mozilla::DefaultDelete
<DatabaseInfo
>;
1628 RefPtr
<ConnectionPool
> mConnectionPool
;
1629 const nsCString mDatabaseId
;
1630 RefPtr
<DatabaseConnection
> mConnection
;
1631 nsClassHashtable
<nsStringHashKey
, TransactionInfoPair
> mBlockingTransactions
;
1632 nsTArray
<NotNull
<TransactionInfo
*>> mTransactionsScheduledDuringClose
;
1633 nsTArray
<NotNull
<TransactionInfo
*>> mScheduledWriteTransactions
;
1634 Maybe
<TransactionInfo
&> mRunningWriteTransaction
;
1635 ThreadInfo mThreadInfo
;
1636 uint32_t mReadTransactionCount
;
1637 uint32_t mWriteTransactionCount
;
1638 bool mNeedsCheckpoint
;
1640 FlippedOnce
<false> mCloseOnIdle
;
1644 PRThread
* mDEBUGConnectionThread
;
1647 DatabaseInfo(ConnectionPool
* aConnectionPool
, const nsACString
& aDatabaseId
);
1649 void AssertIsOnConnectionThread() const {
1650 MOZ_ASSERT(mDEBUGConnectionThread
);
1651 MOZ_ASSERT(PR_GetCurrentThread() == mDEBUGConnectionThread
);
1654 uint64_t TotalTransactionCount() const {
1655 return mReadTransactionCount
+ mWriteTransactionCount
;
1661 DatabaseInfo(const DatabaseInfo
&) = delete;
1662 DatabaseInfo
& operator=(const DatabaseInfo
&) = delete;
1665 struct ConnectionPool::DatabasesCompleteCallback final
{
1666 friend class DefaultDelete
<DatabasesCompleteCallback
>;
1668 nsTArray
<nsCString
> mDatabaseIds
;
1669 nsCOMPtr
<nsIRunnable
> mCallback
;
1671 DatabasesCompleteCallback(nsTArray
<nsCString
>&& aDatabaseIds
,
1672 nsIRunnable
* aCallback
);
1675 ~DatabasesCompleteCallback();
1678 class NS_NO_VTABLE
ConnectionPool::FinishCallback
: public nsIRunnable
{
1680 // Called on the owning thread before any additional transactions are
1682 virtual void TransactionFinishedBeforeUnblock() = 0;
1684 // Called on the owning thread after additional transactions may have been
1686 virtual void TransactionFinishedAfterUnblock() = 0;
1689 FinishCallback() = default;
1691 virtual ~FinishCallback() = default;
1694 class ConnectionPool::FinishCallbackWrapper final
: public Runnable
{
1695 RefPtr
<ConnectionPool
> mConnectionPool
;
1696 RefPtr
<FinishCallback
> mCallback
;
1697 nsCOMPtr
<nsIEventTarget
> mOwningEventTarget
;
1698 uint64_t mTransactionId
;
1702 FinishCallbackWrapper(ConnectionPool
* aConnectionPool
,
1703 uint64_t aTransactionId
, FinishCallback
* aCallback
);
1705 NS_INLINE_DECL_REFCOUNTING_INHERITED(FinishCallbackWrapper
, Runnable
)
1708 ~FinishCallbackWrapper() override
;
1713 class ConnectionPool::ThreadRunnable final
: public Runnable
{
1714 // Only touched on the background thread.
1715 static uint32_t sNextSerialNumber
;
1717 // Set at construction for logging.
1718 const uint32_t mSerialNumber
;
1720 // These two values are only modified on the connection thread.
1721 FlippedOnce
<true> mFirstRun
;
1722 FlippedOnce
<true> mContinueRunning
;
1727 NS_INLINE_DECL_REFCOUNTING_INHERITED(ThreadRunnable
, Runnable
)
1729 uint32_t SerialNumber() const { return mSerialNumber
; }
1731 nsCString
GetThreadName() const {
1732 return nsPrintfCString("IndexedDB #%" PRIu32
, mSerialNumber
);
1736 ~ThreadRunnable() override
;
1741 class ConnectionPool::TransactionInfo final
{
1742 friend class mozilla::DefaultDelete
<TransactionInfo
>;
1744 nsTHashSet
<TransactionInfo
*> mBlocking
;
1745 nsTArray
<NotNull
<TransactionInfo
*>> mBlockingOrdered
;
1748 DatabaseInfo
& mDatabaseInfo
;
1749 const nsID mBackgroundChildLoggingId
;
1750 const nsCString mDatabaseId
;
1751 const uint64_t mTransactionId
;
1752 const int64_t mLoggingSerialNumber
;
1753 const nsTArray
<nsString
> mObjectStoreNames
;
1754 nsTHashSet
<TransactionInfo
*> mBlockedOn
;
1755 nsTArray
<nsCOMPtr
<nsIRunnable
>> mQueuedRunnables
;
1756 const bool mIsWriteTransaction
;
1760 FlippedOnce
<false> mFinished
;
1763 TransactionInfo(DatabaseInfo
& aDatabaseInfo
,
1764 const nsID
& aBackgroundChildLoggingId
,
1765 const nsACString
& aDatabaseId
, uint64_t aTransactionId
,
1766 int64_t aLoggingSerialNumber
,
1767 const nsTArray
<nsString
>& aObjectStoreNames
,
1768 bool aIsWriteTransaction
,
1769 TransactionDatabaseOperationBase
* aTransactionOp
);
1771 void AddBlockingTransaction(TransactionInfo
& aTransactionInfo
);
1773 void RemoveBlockingTransactions();
1778 void MaybeUnblock(TransactionInfo
& aTransactionInfo
);
1781 struct ConnectionPool::TransactionInfoPair final
{
1782 // Multiple reading transactions can block future writes.
1783 nsTArray
<NotNull
<TransactionInfo
*>> mLastBlockingWrites
;
1784 // But only a single writing transaction can block future reads.
1785 Maybe
<TransactionInfo
&> mLastBlockingReads
;
1787 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
1788 TransactionInfoPair();
1789 ~TransactionInfoPair();
1793 /*******************************************************************************
1794 * Actor class declarations
1795 ******************************************************************************/
1797 template <IDBCursorType CursorType
>
1798 class CommonOpenOpHelper
;
1799 template <IDBCursorType CursorType
>
1800 class IndexOpenOpHelper
;
1801 template <IDBCursorType CursorType
>
1802 class ObjectStoreOpenOpHelper
;
1803 template <IDBCursorType CursorType
>
1806 class DatabaseOperationBase
: public Runnable
,
1807 public mozIStorageProgressHandler
{
1808 template <IDBCursorType CursorType
>
1809 friend class OpenOpHelper
;
1812 class AutoSetProgressHandler
;
1814 using UniqueIndexTable
= nsTHashMap
<nsUint64HashKey
, bool>;
1816 const nsCOMPtr
<nsIEventTarget
> mOwningEventTarget
;
1817 const nsID mBackgroundChildLoggingId
;
1818 const uint64_t mLoggingSerialNumber
;
1821 nsresult mResultCode
= NS_OK
;
1822 Atomic
<bool> mOperationMayProceed
;
1823 FlippedOnce
<false> mActorDestroyed
;
1826 NS_DECL_ISUPPORTS_INHERITED
1828 bool IsOnOwningThread() const {
1829 MOZ_ASSERT(mOwningEventTarget
);
1832 return NS_SUCCEEDED(mOwningEventTarget
->IsOnCurrentThread(¤t
)) &&
1836 void AssertIsOnOwningThread() const {
1837 MOZ_ASSERT(IsOnBackgroundThread());
1838 MOZ_ASSERT(IsOnOwningThread());
1841 void NoteActorDestroyed() {
1842 AssertIsOnOwningThread();
1844 mActorDestroyed
.EnsureFlipped();
1845 mOperationMayProceed
= false;
1848 bool IsActorDestroyed() const {
1849 AssertIsOnOwningThread();
1851 return mActorDestroyed
;
1854 // May be called on any thread, but you should call IsActorDestroyed() if
1855 // you know you're on the background thread because it is slightly faster.
1856 bool OperationMayProceed() const { return mOperationMayProceed
; }
1858 const nsID
& BackgroundChildLoggingId() const {
1859 return mBackgroundChildLoggingId
;
1862 uint64_t LoggingSerialNumber() const { return mLoggingSerialNumber
; }
1864 nsresult
ResultCode() const { return mResultCode
; }
1866 void SetFailureCode(nsresult aFailureCode
) {
1867 MOZ_ASSERT(NS_SUCCEEDED(mResultCode
));
1868 OverrideFailureCode(aFailureCode
);
1871 void SetFailureCodeIfUnset(nsresult aFailureCode
) {
1872 if (NS_SUCCEEDED(mResultCode
)) {
1873 OverrideFailureCode(aFailureCode
);
1877 bool HasFailed() const { return NS_FAILED(mResultCode
); }
1880 DatabaseOperationBase(const nsID
& aBackgroundChildLoggingId
,
1881 uint64_t aLoggingSerialNumber
)
1882 : Runnable("dom::indexedDB::DatabaseOperationBase"),
1883 mOwningEventTarget(GetCurrentSerialEventTarget()),
1884 mBackgroundChildLoggingId(aBackgroundChildLoggingId
),
1885 mLoggingSerialNumber(aLoggingSerialNumber
),
1886 mOperationMayProceed(true) {
1887 AssertIsOnOwningThread();
1890 ~DatabaseOperationBase() override
{ MOZ_ASSERT(mActorDestroyed
); }
1892 void OverrideFailureCode(nsresult aFailureCode
) {
1893 MOZ_ASSERT(NS_FAILED(aFailureCode
));
1895 mResultCode
= aFailureCode
;
1898 static nsAutoCString
MaybeGetBindingClauseForKeyRange(
1899 const Maybe
<SerializedKeyRange
>& aOptionalKeyRange
,
1900 const nsACString
& aKeyColumnName
);
1902 static nsAutoCString
GetBindingClauseForKeyRange(
1903 const SerializedKeyRange
& aKeyRange
, const nsACString
& aKeyColumnName
);
1905 static uint64_t ReinterpretDoubleAsUInt64(double aDouble
);
1907 static nsresult
BindKeyRangeToStatement(const SerializedKeyRange
& aKeyRange
,
1908 mozIStorageStatement
* aStatement
);
1910 static nsresult
BindKeyRangeToStatement(const SerializedKeyRange
& aKeyRange
,
1911 mozIStorageStatement
* aStatement
,
1912 const nsCString
& aLocale
);
1914 static Result
<IndexDataValuesAutoArray
, nsresult
>
1915 IndexDataValuesFromUpdateInfos(const nsTArray
<IndexUpdateInfo
>& aUpdateInfos
,
1916 const UniqueIndexTable
& aUniqueIndexTable
);
1918 static nsresult
InsertIndexTableRows(
1919 DatabaseConnection
* aConnection
, IndexOrObjectStoreId aObjectStoreId
,
1920 const Key
& aObjectStoreKey
, const nsTArray
<IndexDataValue
>& aIndexValues
);
1922 static nsresult
DeleteIndexDataTableRows(
1923 DatabaseConnection
* aConnection
, const Key
& aObjectStoreKey
,
1924 const nsTArray
<IndexDataValue
>& aIndexValues
);
1926 static nsresult
DeleteObjectStoreDataTableRowsWithIndexes(
1927 DatabaseConnection
* aConnection
, IndexOrObjectStoreId aObjectStoreId
,
1928 const Maybe
<SerializedKeyRange
>& aKeyRange
);
1930 static nsresult
UpdateIndexValues(
1931 DatabaseConnection
* aConnection
, IndexOrObjectStoreId aObjectStoreId
,
1932 const Key
& aObjectStoreKey
, const nsTArray
<IndexDataValue
>& aIndexValues
);
1934 static Result
<bool, nsresult
> ObjectStoreHasIndexes(
1935 DatabaseConnection
& aConnection
, IndexOrObjectStoreId aObjectStoreId
);
1938 template <typename KeyTransformation
>
1939 static nsresult
MaybeBindKeyToStatement(
1940 const Key
& aKey
, mozIStorageStatement
* aStatement
,
1941 const nsACString
& aParameterName
,
1942 const KeyTransformation
& aKeyTransformation
);
1944 template <typename KeyTransformation
>
1945 static nsresult
BindTransformedKeyRangeToStatement(
1946 const SerializedKeyRange
& aKeyRange
, mozIStorageStatement
* aStatement
,
1947 const KeyTransformation
& aKeyTransformation
);
1949 // Not to be overridden by subclasses.
1950 NS_DECL_MOZISTORAGEPROGRESSHANDLER
1953 class MOZ_STACK_CLASS
DatabaseOperationBase::AutoSetProgressHandler final
{
1954 Maybe
<mozIStorageConnection
&> mConnection
;
1956 DatabaseOperationBase
* mDEBUGDatabaseOp
;
1960 AutoSetProgressHandler();
1962 ~AutoSetProgressHandler();
1964 nsresult
Register(mozIStorageConnection
& aConnection
,
1965 DatabaseOperationBase
* aDatabaseOp
);
1970 class TransactionDatabaseOperationBase
: public DatabaseOperationBase
{
1971 enum class InternalState
{
1980 InitializedOnce
<const NotNull
<SafeRefPtr
<TransactionBase
>>> mTransaction
;
1981 InternalState mInternalState
= InternalState::Initial
;
1982 bool mWaitingForContinue
= false;
1983 const bool mTransactionIsAborted
;
1986 const int64_t mTransactionLoggingSerialNumber
;
1988 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1990 // A check only enables when the diagnostic assert turns on. It assumes the
1991 // mUpdateRefcountFunction is a nullptr because the previous
1992 // StartTransactionOp failed on the connection thread and the next write
1993 // operation (e.g. ObjectstoreAddOrPutRequestOp) doesn't have enough time to
1994 // catch up the failure information.
1995 bool mAssumingPreviousOperationFail
= false;
1999 void AssertIsOnConnectionThread() const
2007 uint64_t StartOnConnectionPool(const nsID
& aBackgroundChildLoggingId
,
2008 const nsACString
& aDatabaseId
,
2009 int64_t aLoggingSerialNumber
,
2010 const nsTArray
<nsString
>& aObjectStoreNames
,
2011 bool aIsWriteTransaction
);
2013 void DispatchToConnectionPool();
2015 TransactionBase
& Transaction() { return **mTransaction
; }
2017 const TransactionBase
& Transaction() const { return **mTransaction
; }
2019 bool IsWaitingForContinue() const {
2020 AssertIsOnOwningThread();
2022 return mWaitingForContinue
;
2025 void NoteContinueReceived();
2027 int64_t TransactionLoggingSerialNumber() const {
2028 return mTransactionLoggingSerialNumber
;
2031 // May be overridden by subclasses if they need to perform work on the
2032 // background thread before being dispatched. Returning false will kill the
2033 // child actors and prevent dispatch.
2034 virtual bool Init(TransactionBase
& aTransaction
);
2036 // This callback will be called on the background thread before releasing the
2037 // final reference to this request object. Subclasses may perform any
2038 // additional cleanup here but must always call the base class implementation.
2039 virtual void Cleanup();
2042 explicit TransactionDatabaseOperationBase(
2043 SafeRefPtr
<TransactionBase
> aTransaction
);
2045 TransactionDatabaseOperationBase(SafeRefPtr
<TransactionBase
> aTransaction
,
2046 uint64_t aLoggingSerialNumber
);
2048 ~TransactionDatabaseOperationBase() override
;
2050 virtual void RunOnConnectionThread();
2052 // Must be overridden in subclasses. Called on the target thread to allow the
2053 // subclass to perform necessary database or file operations. A successful
2054 // return value will trigger a SendSuccessResult callback on the background
2055 // thread while a failure value will trigger a SendFailureResult callback.
2056 virtual nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) = 0;
2058 // May be overriden in subclasses. Called on the background thread to decide
2059 // if the subclass needs to send any preprocess info to the child actor.
2060 virtual bool HasPreprocessInfo();
2062 // May be overriden in subclasses. Called on the background thread to allow
2063 // the subclass to serialize its preprocess info and send it to the child
2064 // actor. A successful return value will trigger a wait for a
2065 // NoteContinueReceived callback on the background thread while a failure
2066 // value will trigger a SendFailureResult callback.
2067 virtual nsresult
SendPreprocessInfo();
2069 // Must be overridden in subclasses. Called on the background thread to allow
2070 // the subclass to serialize its results and send them to the child actor. A
2071 // failed return value will trigger a SendFailureResult callback.
2072 virtual nsresult
SendSuccessResult() = 0;
2074 // Must be overridden in subclasses. Called on the background thread to allow
2075 // the subclass to send its failure code. Returning false will cause the
2076 // transaction to be aborted with aResultCode. Returning true will not cause
2077 // the transaction to be aborted.
2078 virtual bool SendFailureResult(nsresult aResultCode
) = 0;
2080 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2081 auto MakeAutoSavepointCleanupHandler(DatabaseConnection
& aConnection
) {
2082 return [this, &aConnection
](const auto) {
2083 if (!aConnection
.GetUpdateRefcountFunction()) {
2084 mAssumingPreviousOperationFail
= true;
2091 void SendToConnectionPool();
2093 void SendPreprocess();
2097 void SendPreprocessInfoOrResults(bool aSendPreprocessInfo
);
2099 // Not to be overridden by subclasses.
2103 class Factory final
: public PBackgroundIDBFactoryParent
,
2104 public AtomicSafeRefCounted
<Factory
> {
2105 RefPtr
<DatabaseLoggingInfo
> mLoggingInfo
;
2108 bool mActorDestroyed
;
2111 // Reference counted.
2112 ~Factory() override
;
2115 [[nodiscard
]] static SafeRefPtr
<Factory
> Create(
2116 const LoggingInfo
& aLoggingInfo
);
2118 DatabaseLoggingInfo
* GetLoggingInfo() const {
2119 AssertIsOnBackgroundThread();
2120 MOZ_ASSERT(mLoggingInfo
);
2122 return mLoggingInfo
;
2125 MOZ_DECLARE_REFCOUNTED_TYPENAME(mozilla::dom::indexedDB::Factory
)
2126 MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Factory
, AtomicSafeRefCounted
)
2128 // Only constructed in Create().
2129 explicit Factory(RefPtr
<DatabaseLoggingInfo
> aLoggingInfo
);
2131 // IPDL methods are only called by IPDL.
2132 void ActorDestroy(ActorDestroyReason aWhy
) override
;
2134 mozilla::ipc::IPCResult
RecvDeleteMe() override
;
2136 PBackgroundIDBFactoryRequestParent
* AllocPBackgroundIDBFactoryRequestParent(
2137 const FactoryRequestParams
& aParams
) override
;
2139 mozilla::ipc::IPCResult
RecvPBackgroundIDBFactoryRequestConstructor(
2140 PBackgroundIDBFactoryRequestParent
* aActor
,
2141 const FactoryRequestParams
& aParams
) override
;
2143 bool DeallocPBackgroundIDBFactoryRequestParent(
2144 PBackgroundIDBFactoryRequestParent
* aActor
) override
;
2146 PBackgroundIDBDatabaseParent
* AllocPBackgroundIDBDatabaseParent(
2147 const DatabaseSpec
& aSpec
,
2148 PBackgroundIDBFactoryRequestParent
* aRequest
) override
;
2150 bool DeallocPBackgroundIDBDatabaseParent(
2151 PBackgroundIDBDatabaseParent
* aActor
) override
;
2154 class WaitForTransactionsHelper final
: public Runnable
{
2155 const nsCString mDatabaseId
;
2156 nsCOMPtr
<nsIRunnable
> mCallback
;
2158 enum class State
{ Initial
= 0, WaitingForTransactions
, Complete
} mState
;
2161 WaitForTransactionsHelper(const nsACString
& aDatabaseId
,
2162 nsIRunnable
* aCallback
)
2163 : Runnable("dom::indexedDB::WaitForTransactionsHelper"),
2164 mDatabaseId(aDatabaseId
),
2165 mCallback(aCallback
),
2166 mState(State::Initial
) {
2167 AssertIsOnBackgroundThread();
2168 MOZ_ASSERT(!aDatabaseId
.IsEmpty());
2169 MOZ_ASSERT(aCallback
);
2172 void WaitForTransactions();
2174 NS_INLINE_DECL_REFCOUNTING_INHERITED(WaitForTransactionsHelper
, Runnable
)
2177 ~WaitForTransactionsHelper() override
{
2178 MOZ_ASSERT(!mCallback
);
2179 MOZ_ASSERT(mState
== State::Complete
);
2182 void MaybeWaitForTransactions();
2184 void CallCallback();
2189 class Database final
2190 : public PBackgroundIDBDatabaseParent
,
2191 public SupportsCheckedUnsafePtr
<CheckIf
<DiagnosticAssertEnabled
>>,
2192 public AtomicSafeRefCounted
<Database
> {
2193 friend class VersionChangeTransaction
;
2195 class StartTransactionOp
;
2196 class UnmapBlobCallback
;
2199 SafeRefPtr
<Factory
> mFactory
;
2200 SafeRefPtr
<FullDatabaseMetadata
> mMetadata
;
2201 SafeRefPtr
<DatabaseFileManager
> mFileManager
;
2202 RefPtr
<DirectoryLock
> mDirectoryLock
;
2203 nsTHashSet
<TransactionBase
*> mTransactions
;
2204 nsTHashMap
<nsIDHashKey
, SafeRefPtr
<DatabaseFileInfo
>> mMappedBlobs
;
2205 RefPtr
<DatabaseConnection
> mConnection
;
2206 const PrincipalInfo mPrincipalInfo
;
2207 const Maybe
<ContentParentId
> mOptionalContentParentId
;
2208 // XXX Consider changing this to ClientMetadata.
2209 const quota::OriginMetadata mOriginMetadata
;
2210 const nsCString mId
;
2211 const nsString mFilePath
;
2212 const Maybe
<const CipherKey
> mKey
;
2213 int64_t mDirectoryLockId
;
2214 const uint32_t mTelemetryId
;
2215 const PersistenceType mPersistenceType
;
2216 const bool mChromeWriteAccessAllowed
;
2217 const bool mInPrivateBrowsing
;
2218 FlippedOnce
<false> mClosed
;
2219 FlippedOnce
<false> mInvalidated
;
2220 FlippedOnce
<false> mActorWasAlive
;
2221 FlippedOnce
<false> mActorDestroyed
;
2222 nsCOMPtr
<nsIEventTarget
> mBackgroundThread
;
2224 bool mAllBlobsUnmapped
;
2228 // Created by OpenDatabaseOp.
2229 Database(SafeRefPtr
<Factory
> aFactory
, const PrincipalInfo
& aPrincipalInfo
,
2230 const Maybe
<ContentParentId
>& aOptionalContentParentId
,
2231 const quota::OriginMetadata
& aOriginMetadata
, uint32_t aTelemetryId
,
2232 SafeRefPtr
<FullDatabaseMetadata
> aMetadata
,
2233 SafeRefPtr
<DatabaseFileManager
> aFileManager
,
2234 RefPtr
<DirectoryLock
> aDirectoryLock
, bool aChromeWriteAccessAllowed
,
2235 bool aInPrivateBrowsing
, const Maybe
<const CipherKey
>& aMaybeKey
);
2237 void AssertIsOnConnectionThread() const {
2240 MOZ_ASSERT(mConnection
);
2241 mConnection
->AssertIsOnConnectionThread();
2243 MOZ_ASSERT(!NS_IsMainThread());
2244 MOZ_ASSERT(!IsOnBackgroundThread());
2245 MOZ_ASSERT(mInvalidated
);
2250 MOZ_DECLARE_REFCOUNTED_TYPENAME(mozilla::dom::indexedDB::Database
)
2254 bool IsOwnedByProcess(ContentParentId aContentParentId
) const {
2255 return mOptionalContentParentId
&&
2256 mOptionalContentParentId
.value() == aContentParentId
;
2259 const quota::OriginMetadata
& OriginMetadata() const {
2260 return mOriginMetadata
;
2263 const nsCString
& Id() const { return mId
; }
2265 Maybe
<DirectoryLock
&> MaybeDirectoryLockRef() const {
2266 AssertIsOnBackgroundThread();
2268 return ToMaybeRef(mDirectoryLock
.get());
2271 int64_t DirectoryLockId() const { return mDirectoryLockId
; }
2273 uint32_t TelemetryId() const { return mTelemetryId
; }
2275 PersistenceType
Type() const { return mPersistenceType
; }
2277 const nsString
& FilePath() const { return mFilePath
; }
2279 DatabaseFileManager
& GetFileManager() const { return *mFileManager
; }
2281 MovingNotNull
<SafeRefPtr
<DatabaseFileManager
>> GetFileManagerPtr() const {
2282 return WrapMovingNotNull(mFileManager
.clonePtr());
2285 const FullDatabaseMetadata
& Metadata() const {
2286 MOZ_ASSERT(mMetadata
);
2290 SafeRefPtr
<FullDatabaseMetadata
> MetadataPtr() const {
2291 MOZ_ASSERT(mMetadata
);
2292 return mMetadata
.clonePtr();
2295 PBackgroundParent
* GetBackgroundParent() const {
2296 AssertIsOnBackgroundThread();
2297 MOZ_ASSERT(!IsActorDestroyed());
2299 return Manager()->Manager();
2302 DatabaseLoggingInfo
* GetLoggingInfo() const {
2303 AssertIsOnBackgroundThread();
2304 MOZ_ASSERT(mFactory
);
2306 return mFactory
->GetLoggingInfo();
2309 bool RegisterTransaction(TransactionBase
& aTransaction
);
2311 void UnregisterTransaction(TransactionBase
& aTransaction
);
2313 void SetActorAlive();
2315 void MapBlob(const IPCBlob
& aIPCBlob
, SafeRefPtr
<DatabaseFileInfo
> aFileInfo
);
2317 bool IsActorAlive() const {
2318 AssertIsOnBackgroundThread();
2320 return mActorWasAlive
&& !mActorDestroyed
;
2323 bool IsActorDestroyed() const {
2324 AssertIsOnBackgroundThread();
2326 return mActorWasAlive
&& mActorDestroyed
;
2329 bool IsClosed() const {
2330 AssertIsOnBackgroundThread();
2335 bool IsInvalidated() const {
2336 AssertIsOnBackgroundThread();
2338 return mInvalidated
;
2341 nsresult
EnsureConnection();
2343 DatabaseConnection
* GetConnection() const {
2346 mConnection
->AssertIsOnConnectionThread();
2353 void Stringify(nsACString
& aResult
) const;
2355 bool IsInPrivateBrowsing() const {
2356 AssertIsOnBackgroundThread();
2357 return mInPrivateBrowsing
;
2360 const Maybe
<const CipherKey
>& MaybeKeyRef() const {
2361 // This can be called on any thread, as it is const.
2362 MOZ_ASSERT(mKey
.isSome() == mInPrivateBrowsing
);
2366 ~Database() override
{
2367 MOZ_ASSERT(mClosed
);
2368 MOZ_ASSERT_IF(mActorWasAlive
, mActorDestroyed
);
2370 NS_ProxyRelease("ReleaseIDBFactory", mBackgroundThread
.get(),
2375 [[nodiscard
]] SafeRefPtr
<DatabaseFileInfo
> GetBlob(const IPCBlob
& aIPCBlob
);
2377 void UnmapBlob(const nsID
& aID
);
2379 void UnmapAllBlobs();
2381 bool CloseInternal();
2383 void MaybeCloseConnection();
2385 void ConnectionClosedCallback();
2387 void CleanupMetadata();
2389 // IPDL methods are only called by IPDL.
2390 void ActorDestroy(ActorDestroyReason aWhy
) override
;
2392 PBackgroundIDBDatabaseFileParent
* AllocPBackgroundIDBDatabaseFileParent(
2393 const IPCBlob
& aIPCBlob
) override
;
2395 bool DeallocPBackgroundIDBDatabaseFileParent(
2396 PBackgroundIDBDatabaseFileParent
* aActor
) override
;
2398 already_AddRefed
<PBackgroundIDBTransactionParent
>
2399 AllocPBackgroundIDBTransactionParent(
2400 const nsTArray
<nsString
>& aObjectStoreNames
, const Mode
& aMode
) override
;
2402 mozilla::ipc::IPCResult
RecvPBackgroundIDBTransactionConstructor(
2403 PBackgroundIDBTransactionParent
* aActor
,
2404 nsTArray
<nsString
>&& aObjectStoreNames
, const Mode
& aMode
) override
;
2406 mozilla::ipc::IPCResult
RecvDeleteMe() override
;
2408 mozilla::ipc::IPCResult
RecvBlocked() override
;
2410 mozilla::ipc::IPCResult
RecvClose() override
;
2412 template <typename T
>
2413 static bool InvalidateAll(const nsTBaseHashSet
<nsPtrHashKey
<T
>>& aTable
);
2416 class Database::StartTransactionOp final
2417 : public TransactionDatabaseOperationBase
{
2418 friend class Database
;
2421 explicit StartTransactionOp(SafeRefPtr
<TransactionBase
> aTransaction
)
2422 : TransactionDatabaseOperationBase(std::move(aTransaction
),
2423 /* aLoggingSerialNumber */ 0) {}
2425 ~StartTransactionOp() override
= default;
2427 void RunOnConnectionThread() override
;
2429 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
2431 nsresult
SendSuccessResult() override
;
2433 bool SendFailureResult(nsresult aResultCode
) override
;
2435 void Cleanup() override
;
2438 class Database::UnmapBlobCallback final
2439 : public RemoteLazyInputStreamParentCallback
{
2440 SafeRefPtr
<Database
> mDatabase
;
2441 nsCOMPtr
<nsISerialEventTarget
> mBackgroundThread
;
2444 explicit UnmapBlobCallback(SafeRefPtr
<Database
> aDatabase
)
2445 : mDatabase(std::move(aDatabase
)),
2446 mBackgroundThread(GetCurrentSerialEventTarget()) {
2447 AssertIsOnBackgroundThread();
2450 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Database::UnmapBlobCallback
, override
)
2452 void ActorDestroyed(const nsID
& aID
) override
{
2453 MOZ_ASSERT(mDatabase
);
2454 mBackgroundThread
->Dispatch(NS_NewRunnableFunction(
2455 "UnmapBlobCallback", [aID
, database
= std::move(mDatabase
)] {
2456 AssertIsOnBackgroundThread();
2457 database
->UnmapBlob(aID
);
2462 ~UnmapBlobCallback() = default;
2466 * In coordination with IDBDatabase's mFileActors weak-map on the child side, a
2467 * long-lived mapping from a child process's live Blobs to their corresponding
2468 * DatabaseFileInfo in our owning database. Assists in avoiding redundant IPC
2469 * traffic and disk storage. This includes both:
2470 * - Blobs retrieved from this database and sent to the child that do not need
2471 * to be written to disk because they already exist on disk in this database's
2473 * - Blobs retrieved from other databases or from anywhere else that will need
2474 * to be written to this database's files directory. In this case we will
2475 * hold a reference to its BlobImpl in mBlobImpl until we have successfully
2476 * written the Blob to disk.
2478 * Relevant Blob context: Blobs sent from the parent process to child processes
2479 * are automatically linked back to their source BlobImpl when the child process
2480 * references the Blob via IPC. This is done using the internal IPCBlob
2481 * inputStream actor ID to DatabaseFileInfo mapping. However, when getting an
2482 * actor in the child process for sending an in-child-created Blob to the
2483 * parent process, there is (currently) no Blob machinery to automatically
2484 * establish and reuse a long-lived Actor. As a result, without IDB's weak-map
2485 * cleverness, a memory-backed Blob repeatedly sent from the child to the parent
2486 * would appear as a different Blob each time, requiring the Blob data to be
2487 * sent over IPC each time as well as potentially needing to be written to disk
2490 * This object remains alive as long as there is an active child actor or an
2491 * ObjectStoreAddOrPutRequestOp::StoredFileInfo for a queued or active add/put
2492 * op is holding a reference to us.
2494 class DatabaseFile final
: public PBackgroundIDBDatabaseFileParent
{
2495 // mBlobImpl's ownership lifecycle:
2496 // - Initialized on the background thread at creation time. Then
2497 // responsibility is handed off to the connection thread.
2498 // - Checked and used by the connection thread to generate a stream to write
2499 // the blob to disk by an add/put operation.
2500 // - Cleared on the connection thread once the file has successfully been
2502 InitializedOnce
<const RefPtr
<BlobImpl
>> mBlobImpl
;
2503 const SafeRefPtr
<DatabaseFileInfo
> mFileInfo
;
2506 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::DatabaseFile
);
2508 const DatabaseFileInfo
& GetFileInfo() const {
2509 AssertIsOnBackgroundThread();
2514 SafeRefPtr
<DatabaseFileInfo
> GetFileInfoPtr() const {
2515 AssertIsOnBackgroundThread();
2517 return mFileInfo
.clonePtr();
2521 * If mBlobImpl is non-null (implying the contents of this file have not yet
2522 * been written to disk), then return an input stream. Otherwise, if mBlobImpl
2523 * is null (because the contents have been written to disk), returns null.
2525 [[nodiscard
]] nsCOMPtr
<nsIInputStream
> GetInputStream(ErrorResult
& rv
) const;
2528 * To be called upon successful copying of the stream GetInputStream()
2529 * returned so that we won't try and redundantly write the file to disk in the
2530 * future. This is a separate step from GetInputStream() because
2531 * the write could fail due to quota errors that happen now but that might
2532 * not happen in a future attempt.
2534 void WriteSucceededClearBlobImpl() {
2535 MOZ_ASSERT(!IsOnBackgroundThread());
2537 MOZ_ASSERT(*mBlobImpl
);
2538 mBlobImpl
.destroy();
2542 // Called when sending to the child.
2543 explicit DatabaseFile(SafeRefPtr
<DatabaseFileInfo
> aFileInfo
)
2544 : mBlobImpl
{nullptr}, mFileInfo(std::move(aFileInfo
)) {
2545 AssertIsOnBackgroundThread();
2546 MOZ_ASSERT(mFileInfo
);
2549 // Called when receiving from the child.
2550 DatabaseFile(RefPtr
<BlobImpl
> aBlobImpl
,
2551 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
)
2552 : mBlobImpl(std::move(aBlobImpl
)), mFileInfo(std::move(aFileInfo
)) {
2553 AssertIsOnBackgroundThread();
2554 MOZ_ASSERT(*mBlobImpl
);
2555 MOZ_ASSERT(mFileInfo
);
2559 ~DatabaseFile() override
= default;
2561 void ActorDestroy(ActorDestroyReason aWhy
) override
{
2562 AssertIsOnBackgroundThread();
2566 nsCOMPtr
<nsIInputStream
> DatabaseFile::GetInputStream(ErrorResult
& rv
) const {
2567 // We should only be called from our DB connection thread, not the background
2569 MOZ_ASSERT(!IsOnBackgroundThread());
2571 // If we were constructed without a BlobImpl, or WriteSucceededClearBlobImpl
2572 // was already called, return nullptr.
2573 if (!mBlobImpl
|| !*mBlobImpl
) {
2577 nsCOMPtr
<nsIInputStream
> inputStream
;
2578 (*mBlobImpl
)->CreateInputStream(getter_AddRefs(inputStream
), rv
);
2586 class TransactionBase
: public AtomicSafeRefCounted
<TransactionBase
> {
2587 friend class CursorBase
;
2589 template <IDBCursorType CursorType
>
2590 friend class Cursor
;
2595 using Mode
= IDBTransaction::Mode
;
2598 const SafeRefPtr
<Database
> mDatabase
;
2599 nsTArray
<SafeRefPtr
<FullObjectStoreMetadata
>>
2600 mModifiedAutoIncrementObjectStoreMetadataArray
;
2601 LazyInitializedOnceNotNull
<const uint64_t> mTransactionId
;
2602 const nsCString mDatabaseId
;
2603 const int64_t mLoggingSerialNumber
;
2604 uint64_t mActiveRequestCount
;
2605 Atomic
<bool> mInvalidatedOnAnyThread
;
2607 FlippedOnce
<false> mInitialized
;
2608 FlippedOnce
<false> mHasBeenActiveOnConnectionThread
;
2609 FlippedOnce
<false> mActorDestroyed
;
2610 FlippedOnce
<false> mInvalidated
;
2613 nsresult mResultCode
;
2614 FlippedOnce
<false> mCommitOrAbortReceived
;
2615 FlippedOnce
<false> mCommittedOrAborted
;
2616 FlippedOnce
<false> mForceAborted
;
2617 LazyInitializedOnce
<const Maybe
<int64_t>> mLastRequestBeforeCommit
;
2618 Maybe
<int64_t> mLastFailedRequest
;
2621 void AssertIsOnConnectionThread() const {
2622 MOZ_ASSERT(mDatabase
);
2623 mDatabase
->AssertIsOnConnectionThread();
2626 bool IsActorDestroyed() const {
2627 AssertIsOnBackgroundThread();
2629 return mActorDestroyed
;
2632 // Must be called on the background thread.
2633 bool IsInvalidated() const {
2634 MOZ_ASSERT(IsOnBackgroundThread(), "Use IsInvalidatedOnAnyThread()");
2635 MOZ_ASSERT_IF(mInvalidated
, NS_FAILED(mResultCode
));
2637 return mInvalidated
;
2640 // May be called on any thread, but is more expensive than IsInvalidated().
2641 bool IsInvalidatedOnAnyThread() const { return mInvalidatedOnAnyThread
; }
2643 void Init(const uint64_t aTransactionId
) {
2644 AssertIsOnBackgroundThread();
2645 MOZ_ASSERT(aTransactionId
);
2647 mTransactionId
.init(aTransactionId
);
2648 mInitialized
.Flip();
2651 void SetActiveOnConnectionThread() {
2652 AssertIsOnConnectionThread();
2653 mHasBeenActiveOnConnectionThread
.Flip();
2656 MOZ_DECLARE_REFCOUNTED_TYPENAME(mozilla::dom::indexedDB::TransactionBase
)
2658 void Abort(nsresult aResultCode
, bool aForce
);
2660 uint64_t TransactionId() const { return *mTransactionId
; }
2662 const nsACString
& DatabaseId() const { return mDatabaseId
; }
2664 Mode
GetMode() const { return mMode
; }
2666 const Database
& GetDatabase() const {
2667 MOZ_ASSERT(mDatabase
);
2672 Database
& GetMutableDatabase() const {
2673 MOZ_ASSERT(mDatabase
);
2678 SafeRefPtr
<Database
> GetDatabasePtr() const {
2679 MOZ_ASSERT(mDatabase
);
2681 return mDatabase
.clonePtr();
2684 DatabaseLoggingInfo
* GetLoggingInfo() const {
2685 AssertIsOnBackgroundThread();
2686 MOZ_ASSERT(mDatabase
);
2688 return mDatabase
->GetLoggingInfo();
2691 int64_t LoggingSerialNumber() const { return mLoggingSerialNumber
; }
2693 bool IsAborted() const {
2694 AssertIsOnBackgroundThread();
2696 return NS_FAILED(mResultCode
);
2699 [[nodiscard
]] SafeRefPtr
<FullObjectStoreMetadata
> GetMetadataForObjectStoreId(
2700 IndexOrObjectStoreId aObjectStoreId
) const;
2702 [[nodiscard
]] SafeRefPtr
<FullIndexMetadata
> GetMetadataForIndexId(
2703 FullObjectStoreMetadata
& aObjectStoreMetadata
,
2704 IndexOrObjectStoreId aIndexId
) const;
2706 PBackgroundParent
* GetBackgroundParent() const {
2707 AssertIsOnBackgroundThread();
2708 MOZ_ASSERT(!IsActorDestroyed());
2710 return GetDatabase().GetBackgroundParent();
2713 void NoteModifiedAutoIncrementObjectStore(
2714 const SafeRefPtr
<FullObjectStoreMetadata
>& aMetadata
);
2716 void ForgetModifiedAutoIncrementObjectStore(
2717 FullObjectStoreMetadata
& aMetadata
);
2719 void NoteActiveRequest();
2721 void NoteFinishedRequest(int64_t aRequestId
, nsresult aResultCode
);
2725 virtual ~TransactionBase();
2728 TransactionBase(SafeRefPtr
<Database
> aDatabase
, Mode aMode
);
2730 void NoteActorDestroyed() {
2731 AssertIsOnBackgroundThread();
2733 mActorDestroyed
.Flip();
2737 // Only called by VersionChangeTransaction.
2738 void FakeActorDestroyed() { mActorDestroyed
.EnsureFlipped(); }
2741 mozilla::ipc::IPCResult
RecvCommit(IProtocol
* aActor
,
2742 const Maybe
<int64_t> aLastRequest
);
2744 mozilla::ipc::IPCResult
RecvAbort(IProtocol
* aActor
, nsresult aResultCode
);
2746 void MaybeCommitOrAbort() {
2747 AssertIsOnBackgroundThread();
2749 // If we've already committed or aborted then there's nothing else to do.
2750 if (mCommittedOrAborted
) {
2754 // If there are active requests then we have to wait for those requests to
2755 // complete (see NoteFinishedRequest).
2756 if (mActiveRequestCount
) {
2760 // If we haven't yet received a commit or abort message then there could be
2761 // additional requests coming so we should wait unless we're being forced to
2763 if (!mCommitOrAbortReceived
&& !mForceAborted
) {
2770 PBackgroundIDBRequestParent
* AllocRequest(RequestParams
&& aParams
,
2773 bool StartRequest(PBackgroundIDBRequestParent
* aActor
);
2775 bool DeallocRequest(PBackgroundIDBRequestParent
* aActor
);
2777 already_AddRefed
<PBackgroundIDBCursorParent
> AllocCursor(
2778 const OpenCursorParams
& aParams
, bool aTrustParams
);
2780 bool StartCursor(PBackgroundIDBCursorParent
* aActor
,
2781 const OpenCursorParams
& aParams
);
2783 virtual void UpdateMetadata(nsresult aResult
) {}
2785 virtual void SendCompleteNotification(nsresult aResult
) = 0;
2788 bool VerifyRequestParams(const RequestParams
& aParams
) const;
2790 bool VerifyRequestParams(const SerializedKeyRange
& aParams
) const;
2792 bool VerifyRequestParams(const ObjectStoreAddPutParams
& aParams
) const;
2794 bool VerifyRequestParams(const Maybe
<SerializedKeyRange
>& aParams
) const;
2796 void CommitOrAbort();
2799 class TransactionBase::CommitOp final
: public DatabaseOperationBase
,
2800 public ConnectionPool::FinishCallback
{
2801 friend class TransactionBase
;
2803 SafeRefPtr
<TransactionBase
> mTransaction
;
2804 nsresult mResultCode
; ///< TODO: There is also a mResultCode in
2805 ///< DatabaseOperationBase. Is there a reason not to
2806 ///< use that? At least a more specific name should be
2807 ///< given to this one.
2810 CommitOp(SafeRefPtr
<TransactionBase
> aTransaction
, nsresult aResultCode
);
2812 ~CommitOp() override
= default;
2814 // Writes new autoIncrement counts to database.
2815 nsresult
WriteAutoIncrementCounts();
2817 // Updates counts after a database activity has finished.
2818 void CommitOrRollbackAutoIncrementCounts();
2820 void AssertForeignKeyConsistency(DatabaseConnection
* aConnection
)
2830 void TransactionFinishedBeforeUnblock() override
;
2832 void TransactionFinishedAfterUnblock() override
;
2835 // We need to declare all of nsISupports, because FinishCallback has
2836 // a pure-virtual nsISupports declaration.
2837 NS_DECL_ISUPPORTS_INHERITED
2840 class NormalTransaction final
: public TransactionBase
,
2841 public PBackgroundIDBTransactionParent
{
2842 nsTArray
<SafeRefPtr
<FullObjectStoreMetadata
>> mObjectStores
;
2844 // Reference counted.
2845 ~NormalTransaction() override
= default;
2847 bool IsSameProcessActor();
2849 // Only called by TransactionBase.
2850 void SendCompleteNotification(nsresult aResult
) override
;
2852 // IPDL methods are only called by IPDL.
2853 void ActorDestroy(ActorDestroyReason aWhy
) override
;
2855 mozilla::ipc::IPCResult
RecvDeleteMe() override
;
2857 mozilla::ipc::IPCResult
RecvCommit(
2858 const Maybe
<int64_t>& aLastRequest
) override
;
2860 mozilla::ipc::IPCResult
RecvAbort(const nsresult
& aResultCode
) override
;
2862 PBackgroundIDBRequestParent
* AllocPBackgroundIDBRequestParent(
2863 const RequestParams
& aParams
) override
;
2865 mozilla::ipc::IPCResult
RecvPBackgroundIDBRequestConstructor(
2866 PBackgroundIDBRequestParent
* aActor
,
2867 const RequestParams
& aParams
) override
;
2869 bool DeallocPBackgroundIDBRequestParent(
2870 PBackgroundIDBRequestParent
* aActor
) override
;
2872 already_AddRefed
<PBackgroundIDBCursorParent
> AllocPBackgroundIDBCursorParent(
2873 const OpenCursorParams
& aParams
) override
;
2875 mozilla::ipc::IPCResult
RecvPBackgroundIDBCursorConstructor(
2876 PBackgroundIDBCursorParent
* aActor
,
2877 const OpenCursorParams
& aParams
) override
;
2880 // This constructor is only called by Database.
2882 SafeRefPtr
<Database
> aDatabase
, TransactionBase::Mode aMode
,
2883 nsTArray
<SafeRefPtr
<FullObjectStoreMetadata
>>&& aObjectStores
);
2885 MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(NormalTransaction
, TransactionBase
)
2888 class VersionChangeTransaction final
2889 : public TransactionBase
,
2890 public PBackgroundIDBVersionChangeTransactionParent
{
2891 friend class OpenDatabaseOp
;
2893 RefPtr
<OpenDatabaseOp
> mOpenDatabaseOp
;
2894 SafeRefPtr
<FullDatabaseMetadata
> mOldMetadata
;
2896 FlippedOnce
<false> mActorWasAlive
;
2899 // Only called by OpenDatabaseOp.
2900 explicit VersionChangeTransaction(OpenDatabaseOp
* aOpenDatabaseOp
);
2902 MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(VersionChangeTransaction
,
2906 // Reference counted.
2907 ~VersionChangeTransaction() override
;
2909 bool IsSameProcessActor();
2911 // Only called by OpenDatabaseOp.
2912 bool CopyDatabaseMetadata();
2914 void SetActorAlive();
2916 // Only called by TransactionBase.
2917 void UpdateMetadata(nsresult aResult
) override
;
2919 // Only called by TransactionBase.
2920 void SendCompleteNotification(nsresult aResult
) override
;
2922 // IPDL methods are only called by IPDL.
2923 void ActorDestroy(ActorDestroyReason aWhy
) override
;
2925 mozilla::ipc::IPCResult
RecvDeleteMe() override
;
2927 mozilla::ipc::IPCResult
RecvCommit(
2928 const Maybe
<int64_t>& aLastRequest
) override
;
2930 mozilla::ipc::IPCResult
RecvAbort(const nsresult
& aResultCode
) override
;
2932 mozilla::ipc::IPCResult
RecvCreateObjectStore(
2933 const ObjectStoreMetadata
& aMetadata
) override
;
2935 mozilla::ipc::IPCResult
RecvDeleteObjectStore(
2936 const IndexOrObjectStoreId
& aObjectStoreId
) override
;
2938 mozilla::ipc::IPCResult
RecvRenameObjectStore(
2939 const IndexOrObjectStoreId
& aObjectStoreId
,
2940 const nsAString
& aName
) override
;
2942 mozilla::ipc::IPCResult
RecvCreateIndex(
2943 const IndexOrObjectStoreId
& aObjectStoreId
,
2944 const IndexMetadata
& aMetadata
) override
;
2946 mozilla::ipc::IPCResult
RecvDeleteIndex(
2947 const IndexOrObjectStoreId
& aObjectStoreId
,
2948 const IndexOrObjectStoreId
& aIndexId
) override
;
2950 mozilla::ipc::IPCResult
RecvRenameIndex(
2951 const IndexOrObjectStoreId
& aObjectStoreId
,
2952 const IndexOrObjectStoreId
& aIndexId
, const nsAString
& aName
) override
;
2954 PBackgroundIDBRequestParent
* AllocPBackgroundIDBRequestParent(
2955 const RequestParams
& aParams
) override
;
2957 mozilla::ipc::IPCResult
RecvPBackgroundIDBRequestConstructor(
2958 PBackgroundIDBRequestParent
* aActor
,
2959 const RequestParams
& aParams
) override
;
2961 bool DeallocPBackgroundIDBRequestParent(
2962 PBackgroundIDBRequestParent
* aActor
) override
;
2964 already_AddRefed
<PBackgroundIDBCursorParent
> AllocPBackgroundIDBCursorParent(
2965 const OpenCursorParams
& aParams
) override
;
2967 mozilla::ipc::IPCResult
RecvPBackgroundIDBCursorConstructor(
2968 PBackgroundIDBCursorParent
* aActor
,
2969 const OpenCursorParams
& aParams
) override
;
2973 : public DatabaseOperationBase
,
2974 public OpenDirectoryListener
,
2975 public PBackgroundIDBFactoryRequestParent
,
2976 public SupportsCheckedUnsafePtr
<CheckIf
<DiagnosticAssertEnabled
>> {
2978 struct MaybeBlockedDatabaseInfo final
{
2979 SafeRefPtr
<Database
> mDatabase
;
2982 MaybeBlockedDatabaseInfo(MaybeBlockedDatabaseInfo
&&) = default;
2983 MaybeBlockedDatabaseInfo
& operator=(MaybeBlockedDatabaseInfo
&&) = default;
2985 MOZ_IMPLICIT
MaybeBlockedDatabaseInfo(SafeRefPtr
<Database
> aDatabase
)
2986 : mDatabase(std::move(aDatabase
)), mBlocked(false) {
2987 MOZ_ASSERT(mDatabase
);
2989 MOZ_COUNT_CTOR(FactoryOp::MaybeBlockedDatabaseInfo
);
2992 ~MaybeBlockedDatabaseInfo() {
2993 MOZ_COUNT_DTOR(FactoryOp::MaybeBlockedDatabaseInfo
);
2996 bool operator==(const Database
* aOther
) const {
2997 return mDatabase
== aOther
;
3000 Database
* operator->() const& MOZ_NO_ADDREF_RELEASE_ON_RETURN
{
3001 return mDatabase
.unsafeGetRawPtr();
3007 // Just created on the PBackground thread, dispatched to the main thread.
3008 // Next step is either SendingResults if permission is denied,
3009 // PermissionChallenge if the permission is unknown, or FinishOpen
3010 // if permission is granted.
3013 // Ensuring quota manager is created and opening directory on the
3014 // PBackground thread. Next step is either SendingResults if quota manager
3015 // is not available or DirectoryOpenPending if quota manager is available.
3018 // Waiting for directory open allowed on the PBackground thread. The next
3019 // step is either SendingResults if directory lock failed to acquire, or
3020 // DatabaseOpenPending if directory lock is acquired.
3021 DirectoryOpenPending
,
3023 // Waiting for database open allowed on the PBackground thread. The next
3024 // step is DatabaseWorkOpen.
3025 DatabaseOpenPending
,
3027 // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
3028 // either BeginVersionChange if the requested version doesn't match the
3029 // existing database version or SendingResults if the versions match.
3032 // Starting a version change transaction or deleting a database on the
3033 // PBackground thread. We need to notify other databases that a version
3034 // change is about to happen, and maybe tell the request that a version
3035 // change has been blocked. If databases are notified then the next step is
3036 // WaitingForOtherDatabasesToClose. Otherwise the next step is
3037 // WaitingForTransactionsToComplete.
3040 // Waiting for other databases to close on the PBackground thread. This
3041 // state may persist until all databases are closed. The next state is
3042 // WaitingForTransactionsToComplete.
3043 WaitingForOtherDatabasesToClose
,
3045 // Waiting for all transactions that could interfere with this operation to
3046 // complete on the PBackground thread. Next state is
3047 // DatabaseWorkVersionChange.
3048 WaitingForTransactionsToComplete
,
3050 // Waiting to do/doing work on the "work thread". This involves waiting for
3051 // the VersionChangeOp (OpenDatabaseOp and DeleteDatabaseOp each have a
3052 // different implementation) to do its work. Eventually the state will
3053 // transition to SendingResults.
3054 DatabaseWorkVersionChange
,
3056 // Waiting to send/sending results on the PBackground thread. Next step is
3064 // Must be released on the background thread!
3065 SafeRefPtr
<Factory
> mFactory
;
3067 RefPtr
<ThreadsafeContentParentHandle
> mContentHandle
;
3069 // Must be released on the main thread!
3070 RefPtr
<DirectoryLock
> mDirectoryLock
;
3072 RefPtr
<FactoryOp
> mDelayedOp
;
3073 nsTArray
<MaybeBlockedDatabaseInfo
> mMaybeBlockedDatabases
;
3075 const CommonFactoryRequestParams mCommonParams
;
3076 OriginMetadata mOriginMetadata
;
3077 nsCString mDatabaseId
;
3078 nsString mDatabaseFilePath
;
3079 int64_t mDirectoryLockId
;
3081 bool mWaitingForPermissionRetry
;
3082 bool mEnforcingQuota
;
3083 const bool mDeleting
;
3084 bool mChromeWriteAccessAllowed
;
3085 FlippedOnce
<false> mInPrivateBrowsing
;
3088 const nsACString
& Origin() const {
3089 AssertIsOnOwningThread();
3091 return mOriginMetadata
.mOrigin
;
3094 bool DatabaseFilePathIsKnown() const {
3095 AssertIsOnOwningThread();
3097 return !mDatabaseFilePath
.IsEmpty();
3100 const nsAString
& DatabaseFilePath() const {
3101 AssertIsOnOwningThread();
3102 MOZ_ASSERT(!mDatabaseFilePath
.IsEmpty());
3104 return mDatabaseFilePath
;
3107 void NoteDatabaseBlocked(Database
* aDatabase
);
3109 void NoteDatabaseClosed(Database
* aDatabase
);
3112 bool HasBlockedDatabases() const { return !mMaybeBlockedDatabases
.IsEmpty(); }
3115 void StringifyState(nsACString
& aResult
) const;
3117 void Stringify(nsACString
& aResult
) const;
3120 FactoryOp(SafeRefPtr
<Factory
> aFactory
,
3121 RefPtr
<ThreadsafeContentParentHandle
> aContentHandle
,
3122 const CommonFactoryRequestParams
& aCommonParams
, bool aDeleting
);
3124 ~FactoryOp() override
{
3125 // Normally this would be out-of-line since it is a virtual function but
3126 // MSVC 2010 fails to link for some reason if it is not inlined here...
3127 MOZ_ASSERT_IF(OperationMayProceed(),
3128 mState
== State::Initial
|| mState
== State::Completed
);
3133 nsresult
DirectoryOpen();
3135 nsresult
SendToIOThread();
3137 void WaitForTransactions();
3139 void CleanupMetadata();
3141 void FinishSendResults();
3143 nsresult
SendVersionChangeMessages(DatabaseActorInfo
* aDatabaseActorInfo
,
3144 Maybe
<Database
&> aOpeningDatabase
,
3145 uint64_t aOldVersion
,
3146 const Maybe
<uint64_t>& aNewVersion
);
3148 // Methods that subclasses must implement.
3149 virtual nsresult
DatabaseOpen() = 0;
3151 virtual nsresult
DoDatabaseWork() = 0;
3153 virtual nsresult
BeginVersionChange() = 0;
3155 virtual bool AreActorsAlive() = 0;
3157 virtual nsresult
DispatchToWorkThread() = 0;
3159 // Should only be called by Run().
3160 virtual void SendResults() = 0;
3162 // We need to declare refcounting unconditionally, because
3163 // OpenDirectoryListener has pure-virtual refcounting.
3164 NS_DECL_ISUPPORTS_INHERITED
3166 // Common nsIRunnable implementation that subclasses may not override.
3170 // OpenDirectoryListener overrides.
3171 void DirectoryLockAcquired(DirectoryLock
* aLock
) override
;
3173 void DirectoryLockFailed() override
;
3176 void ActorDestroy(ActorDestroyReason aWhy
) override
;
3178 virtual void SendBlockedNotification() = 0;
3181 mozilla::Result
<PermissionValue
, nsresult
> CheckPermission(
3182 ContentParent
* aContentParent
);
3184 static bool CheckAtLeastOneAppHasPermission(
3185 ContentParent
* aContentParent
, const nsACString
& aPermissionString
);
3187 nsresult
FinishOpen();
3189 // Test whether this FactoryOp needs to wait for the given op.
3190 bool MustWaitFor(const FactoryOp
& aExistingOp
);
3193 class OpenDatabaseOp final
: public FactoryOp
{
3194 friend class Database
;
3195 friend class VersionChangeTransaction
;
3197 class VersionChangeOp
;
3199 SafeRefPtr
<FullDatabaseMetadata
> mMetadata
;
3201 uint64_t mRequestedVersion
;
3202 SafeRefPtr
<DatabaseFileManager
> mFileManager
;
3204 SafeRefPtr
<Database
> mDatabase
;
3205 SafeRefPtr
<VersionChangeTransaction
> mVersionChangeTransaction
;
3207 // This is only set while a VersionChangeOp is live. It holds a strong
3208 // reference to its OpenDatabaseOp object so this is a weak pointer to avoid
3210 VersionChangeOp
* mVersionChangeOp
;
3212 uint32_t mTelemetryId
;
3215 OpenDatabaseOp(SafeRefPtr
<Factory
> aFactory
,
3216 RefPtr
<ThreadsafeContentParentHandle
> aContentHandle
,
3217 const CommonFactoryRequestParams
& aParams
);
3220 ~OpenDatabaseOp() override
{ MOZ_ASSERT(!mVersionChangeOp
); }
3222 nsresult
LoadDatabaseInformation(mozIStorageConnection
& aConnection
);
3224 nsresult
SendUpgradeNeeded();
3226 void EnsureDatabaseActor();
3228 nsresult
EnsureDatabaseActorIsAlive();
3230 mozilla::Result
<DatabaseSpec
, nsresult
> MetadataToSpec() const;
3232 void AssertMetadataConsistency(const FullDatabaseMetadata
& aMetadata
)
3240 void ConnectionClosedCallback();
3242 void ActorDestroy(ActorDestroyReason aWhy
) override
;
3244 nsresult
DatabaseOpen() override
;
3246 nsresult
DoDatabaseWork() override
;
3248 nsresult
BeginVersionChange() override
;
3250 bool AreActorsAlive() override
;
3252 void SendBlockedNotification() override
;
3254 nsresult
DispatchToWorkThread() override
;
3256 void SendResults() override
;
3258 static nsresult
UpdateLocaleAwareIndex(mozIStorageConnection
& aConnection
,
3259 const IndexMetadata
& aIndexMetadata
,
3260 const nsCString
& aLocale
);
3263 class OpenDatabaseOp::VersionChangeOp final
3264 : public TransactionDatabaseOperationBase
{
3265 friend class OpenDatabaseOp
;
3267 RefPtr
<OpenDatabaseOp
> mOpenDatabaseOp
;
3268 const uint64_t mRequestedVersion
;
3269 uint64_t mPreviousVersion
;
3272 explicit VersionChangeOp(OpenDatabaseOp
* aOpenDatabaseOp
)
3273 : TransactionDatabaseOperationBase(
3274 aOpenDatabaseOp
->mVersionChangeTransaction
.clonePtr(),
3275 aOpenDatabaseOp
->LoggingSerialNumber()),
3276 mOpenDatabaseOp(aOpenDatabaseOp
),
3277 mRequestedVersion(aOpenDatabaseOp
->mRequestedVersion
),
3279 aOpenDatabaseOp
->mMetadata
->mCommonMetadata
.version()) {
3280 MOZ_ASSERT(aOpenDatabaseOp
);
3281 MOZ_ASSERT(mRequestedVersion
);
3284 ~VersionChangeOp() override
{ MOZ_ASSERT(!mOpenDatabaseOp
); }
3286 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3288 nsresult
SendSuccessResult() override
;
3290 bool SendFailureResult(nsresult aResultCode
) override
;
3292 void Cleanup() override
;
3295 class DeleteDatabaseOp final
: public FactoryOp
{
3296 class VersionChangeOp
;
3298 nsString mDatabaseDirectoryPath
;
3299 nsString mDatabaseFilenameBase
;
3300 uint64_t mPreviousVersion
;
3303 DeleteDatabaseOp(SafeRefPtr
<Factory
> aFactory
,
3304 RefPtr
<ThreadsafeContentParentHandle
> aContentHandle
,
3305 const CommonFactoryRequestParams
& aParams
)
3306 : FactoryOp(std::move(aFactory
), std::move(aContentHandle
), aParams
,
3307 /* aDeleting */ true),
3308 mPreviousVersion(0) {}
3311 ~DeleteDatabaseOp() override
= default;
3313 void LoadPreviousVersion(nsIFile
& aDatabaseFile
);
3315 nsresult
DatabaseOpen() override
;
3317 nsresult
DoDatabaseWork() override
;
3319 nsresult
BeginVersionChange() override
;
3321 bool AreActorsAlive() override
;
3323 void SendBlockedNotification() override
;
3325 nsresult
DispatchToWorkThread() override
;
3327 void SendResults() override
;
3330 class DeleteDatabaseOp::VersionChangeOp final
: public DatabaseOperationBase
{
3331 friend class DeleteDatabaseOp
;
3333 RefPtr
<DeleteDatabaseOp
> mDeleteDatabaseOp
;
3336 explicit VersionChangeOp(DeleteDatabaseOp
* aDeleteDatabaseOp
)
3337 : DatabaseOperationBase(aDeleteDatabaseOp
->BackgroundChildLoggingId(),
3338 aDeleteDatabaseOp
->LoggingSerialNumber()),
3339 mDeleteDatabaseOp(aDeleteDatabaseOp
) {
3340 MOZ_ASSERT(aDeleteDatabaseOp
);
3341 MOZ_ASSERT(!aDeleteDatabaseOp
->mDatabaseDirectoryPath
.IsEmpty());
3344 ~VersionChangeOp() override
= default;
3346 nsresult
RunOnIOThread();
3348 void RunOnOwningThread();
3353 class VersionChangeTransactionOp
: public TransactionDatabaseOperationBase
{
3355 void Cleanup() override
;
3358 explicit VersionChangeTransactionOp(
3359 SafeRefPtr
<VersionChangeTransaction
> aTransaction
)
3360 : TransactionDatabaseOperationBase(std::move(aTransaction
)) {}
3362 ~VersionChangeTransactionOp() override
= default;
3365 nsresult
SendSuccessResult() override
;
3367 bool SendFailureResult(nsresult aResultCode
) override
;
3370 class CreateObjectStoreOp final
: public VersionChangeTransactionOp
{
3371 friend class VersionChangeTransaction
;
3373 const ObjectStoreMetadata mMetadata
;
3376 // Only created by VersionChangeTransaction.
3377 CreateObjectStoreOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
3378 const ObjectStoreMetadata
& aMetadata
)
3379 : VersionChangeTransactionOp(std::move(aTransaction
)),
3380 mMetadata(aMetadata
) {
3381 MOZ_ASSERT(aMetadata
.id());
3384 ~CreateObjectStoreOp() override
= default;
3386 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3389 class DeleteObjectStoreOp final
: public VersionChangeTransactionOp
{
3390 friend class VersionChangeTransaction
;
3392 const SafeRefPtr
<FullObjectStoreMetadata
> mMetadata
;
3393 const bool mIsLastObjectStore
;
3396 // Only created by VersionChangeTransaction.
3397 DeleteObjectStoreOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
3398 SafeRefPtr
<FullObjectStoreMetadata
> aMetadata
,
3399 const bool aIsLastObjectStore
)
3400 : VersionChangeTransactionOp(std::move(aTransaction
)),
3401 mMetadata(std::move(aMetadata
)),
3402 mIsLastObjectStore(aIsLastObjectStore
) {
3403 MOZ_ASSERT(mMetadata
->mCommonMetadata
.id());
3406 ~DeleteObjectStoreOp() override
= default;
3408 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3411 class RenameObjectStoreOp final
: public VersionChangeTransactionOp
{
3412 friend class VersionChangeTransaction
;
3415 const nsString mNewName
;
3418 // Only created by VersionChangeTransaction.
3419 RenameObjectStoreOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
3420 FullObjectStoreMetadata
& aMetadata
)
3421 : VersionChangeTransactionOp(std::move(aTransaction
)),
3422 mId(aMetadata
.mCommonMetadata
.id()),
3423 mNewName(aMetadata
.mCommonMetadata
.name()) {
3427 ~RenameObjectStoreOp() override
= default;
3429 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3432 class CreateIndexOp final
: public VersionChangeTransactionOp
{
3433 friend class VersionChangeTransaction
;
3435 class UpdateIndexDataValuesFunction
;
3437 const IndexMetadata mMetadata
;
3438 Maybe
<UniqueIndexTable
> mMaybeUniqueIndexTable
;
3439 const SafeRefPtr
<DatabaseFileManager
> mFileManager
;
3440 const nsCString mDatabaseId
;
3441 const IndexOrObjectStoreId mObjectStoreId
;
3444 // Only created by VersionChangeTransaction.
3445 CreateIndexOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
3446 IndexOrObjectStoreId aObjectStoreId
,
3447 const IndexMetadata
& aMetadata
);
3449 ~CreateIndexOp() override
= default;
3451 nsresult
InsertDataFromObjectStore(DatabaseConnection
* aConnection
);
3453 nsresult
InsertDataFromObjectStoreInternal(
3454 DatabaseConnection
* aConnection
) const;
3456 bool Init(TransactionBase
& aTransaction
) override
;
3458 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3461 class CreateIndexOp::UpdateIndexDataValuesFunction final
3462 : public mozIStorageFunction
{
3463 RefPtr
<CreateIndexOp
> mOp
;
3464 RefPtr
<DatabaseConnection
> mConnection
;
3465 const NotNull
<SafeRefPtr
<Database
>> mDatabase
;
3468 UpdateIndexDataValuesFunction(CreateIndexOp
* aOp
,
3469 DatabaseConnection
* aConnection
,
3470 SafeRefPtr
<Database
> aDatabase
)
3472 mConnection(aConnection
),
3473 mDatabase(WrapNotNull(std::move(aDatabase
))) {
3475 MOZ_ASSERT(aConnection
);
3476 aConnection
->AssertIsOnConnectionThread();
3482 ~UpdateIndexDataValuesFunction() = default;
3484 NS_DECL_MOZISTORAGEFUNCTION
3487 class DeleteIndexOp final
: public VersionChangeTransactionOp
{
3488 friend class VersionChangeTransaction
;
3490 const IndexOrObjectStoreId mObjectStoreId
;
3491 const IndexOrObjectStoreId mIndexId
;
3493 const bool mIsLastIndex
;
3496 // Only created by VersionChangeTransaction.
3497 DeleteIndexOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
3498 IndexOrObjectStoreId aObjectStoreId
,
3499 IndexOrObjectStoreId aIndexId
, const bool aUnique
,
3500 const bool aIsLastIndex
);
3502 ~DeleteIndexOp() override
= default;
3504 nsresult
RemoveReferencesToIndex(
3505 DatabaseConnection
* aConnection
, const Key
& aObjectDataKey
,
3506 nsTArray
<IndexDataValue
>& aIndexValues
) const;
3508 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3511 class RenameIndexOp final
: public VersionChangeTransactionOp
{
3512 friend class VersionChangeTransaction
;
3514 const IndexOrObjectStoreId mObjectStoreId
;
3515 const IndexOrObjectStoreId mIndexId
;
3516 const nsString mNewName
;
3519 // Only created by VersionChangeTransaction.
3520 RenameIndexOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
3521 FullIndexMetadata
& aMetadata
,
3522 IndexOrObjectStoreId aObjectStoreId
)
3523 : VersionChangeTransactionOp(std::move(aTransaction
)),
3524 mObjectStoreId(aObjectStoreId
),
3525 mIndexId(aMetadata
.mCommonMetadata
.id()),
3526 mNewName(aMetadata
.mCommonMetadata
.name()) {
3527 MOZ_ASSERT(mIndexId
);
3530 ~RenameIndexOp() override
= default;
3532 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3535 class NormalTransactionOp
: public TransactionDatabaseOperationBase
,
3536 public PBackgroundIDBRequestParent
{
3542 void Cleanup() override
;
3545 explicit NormalTransactionOp(SafeRefPtr
<TransactionBase
> aTransaction
)
3546 : TransactionDatabaseOperationBase(std::move(aTransaction
))
3549 mResponseSent(false)
3554 ~NormalTransactionOp() override
= default;
3556 // An overload of DatabaseOperationBase's function that can avoid doing extra
3557 // work on non-versionchange transactions.
3558 mozilla::Result
<bool, nsresult
> ObjectStoreHasIndexes(
3559 DatabaseConnection
& aConnection
, IndexOrObjectStoreId aObjectStoreId
,
3560 bool aMayHaveIndexes
);
3562 virtual mozilla::Result
<PreprocessParams
, nsresult
> GetPreprocessParams();
3564 // Subclasses use this override to set the IPDL response value.
3565 virtual void GetResponse(RequestResponse
& aResponse
,
3566 size_t* aResponseSize
) = 0;
3569 nsresult
SendPreprocessInfo() override
;
3571 nsresult
SendSuccessResult() override
;
3573 bool SendFailureResult(nsresult aResultCode
) override
;
3576 void ActorDestroy(ActorDestroyReason aWhy
) override
;
3578 mozilla::ipc::IPCResult
RecvContinue(
3579 const PreprocessResponse
& aResponse
) final
;
3582 // XXX Maybe we can avoid a mutex here by moving all accesses to the background
3584 StaticAutoPtr
<IndexedDBCipherKeyManager
> gIndexedDBCipherKeyManager
;
3588 Maybe
<CipherKey
> IndexedDBCipherKeyManager::Get(const nsCString
& aDatabaseID
,
3589 const nsCString
& keyStoreID
) {
3590 auto lockedPrivateBrowsingInfoHashTable
=
3591 mPrivateBrowsingInfoHashTable
.Lock();
3593 auto dbKeyStore
= lockedPrivateBrowsingInfoHashTable
->Lookup(aDatabaseID
);
3598 return dbKeyStore
->MaybeGet(keyStoreID
);
3601 CipherKey
IndexedDBCipherKeyManager::Ensure(const nsCString
& aDatabaseID
,
3602 const nsCString
& keyStoreID
) {
3603 auto lockedPrivateBrowsingInfoHashTable
=
3604 mPrivateBrowsingInfoHashTable
.Lock();
3607 lockedPrivateBrowsingInfoHashTable
->LookupOrInsert(aDatabaseID
);
3609 return dbKeyStore
.LookupOrInsertWith(keyStoreID
, [] {
3610 // Generate a new key if one corresponding to keyStoreID
3611 // does not exists already.
3612 auto keyOrErr
= IndexedDBCipherStrategy::GenerateKey();
3614 // Bug1800110 Propagate the error to the caller rather than asserting.
3615 MOZ_RELEASE_ASSERT(keyOrErr
.isOk());
3616 return keyOrErr
.unwrap();
3620 bool IndexedDBCipherKeyManager::Remove(const nsCString
& aDatabaseID
) {
3621 auto lockedPrivateBrowsingInfoHashTable
=
3622 mPrivateBrowsingInfoHashTable
.Lock();
3623 return lockedPrivateBrowsingInfoHashTable
->Remove(aDatabaseID
);
3626 IndexedDBCipherKeyManager
* GetIndexedDBCipherKeyManager() {
3627 return gIndexedDBCipherKeyManager
;
3632 class ObjectStoreAddOrPutRequestOp final
: public NormalTransactionOp
{
3633 friend class TransactionBase
;
3635 using PersistenceType
= mozilla::dom::quota::PersistenceType
;
3637 class StoredFileInfo final
{
3638 InitializedOnce
<const NotNull
<SafeRefPtr
<DatabaseFileInfo
>>> mFileInfo
;
3639 // Either nothing, a file actor or a non-Blob-backed inputstream to write to
3641 using FileActorOrInputStream
=
3642 Variant
<Nothing
, RefPtr
<DatabaseFile
>, nsCOMPtr
<nsIInputStream
>>;
3643 InitializedOnce
<const FileActorOrInputStream
> mFileActorOrInputStream
;
3645 const StructuredCloneFileBase::FileType mType
;
3647 void EnsureCipherKey();
3648 void AssertInvariants() const;
3650 StoredFileInfo(SafeRefPtr
<DatabaseFileInfo
> aFileInfo
,
3651 RefPtr
<DatabaseFile
> aFileActor
);
3653 StoredFileInfo(SafeRefPtr
<DatabaseFileInfo
> aFileInfo
,
3654 nsCOMPtr
<nsIInputStream
> aInputStream
);
3657 #if defined(NS_BUILD_REFCNT_LOGGING)
3658 // Only for MOZ_COUNT_CTOR.
3659 StoredFileInfo(StoredFileInfo
&& aOther
)
3660 : mFileInfo
{std::move(aOther
.mFileInfo
)},
3661 mFileActorOrInputStream
{std::move(aOther
.mFileActorOrInputStream
)}
3667 MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo
);
3670 StoredFileInfo(StoredFileInfo
&&) = default;
3673 static StoredFileInfo
CreateForBlob(SafeRefPtr
<DatabaseFileInfo
> aFileInfo
,
3674 RefPtr
<DatabaseFile
> aFileActor
);
3675 static StoredFileInfo
CreateForStructuredClone(
3676 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
,
3677 nsCOMPtr
<nsIInputStream
> aInputStream
);
3679 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
3681 AssertIsOnBackgroundThread();
3684 MOZ_COUNT_DTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo
);
3688 bool IsValid() const { return static_cast<bool>(mFileInfo
); }
3690 const DatabaseFileInfo
& GetFileInfo() const { return **mFileInfo
; }
3692 bool ShouldCompress() const;
3694 void NotifyWriteSucceeded() const;
3696 using InputStreamResult
=
3697 mozilla::Result
<nsCOMPtr
<nsIInputStream
>, nsresult
>;
3698 InputStreamResult
GetInputStream();
3700 void Serialize(nsString
& aText
) const;
3702 class SCInputStream
;
3704 const ObjectStoreAddPutParams mParams
;
3705 Maybe
<UniqueIndexTable
> mUniqueIndexTable
;
3707 // This must be non-const so that we can update the mNextAutoIncrementId field
3708 // if we are modifying an autoIncrement objectStore.
3709 SafeRefPtr
<FullObjectStoreMetadata
> mMetadata
;
3711 nsTArray
<StoredFileInfo
> mStoredFileInfos
;
3714 const OriginMetadata mOriginMetadata
;
3715 const PersistenceType mPersistenceType
;
3716 const bool mOverwrite
;
3717 bool mObjectStoreMayHaveIndexes
;
3718 bool mDataOverThreshold
;
3721 // Only created by TransactionBase.
3722 ObjectStoreAddOrPutRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
3723 RequestParams
&& aParams
);
3725 ~ObjectStoreAddOrPutRequestOp() override
= default;
3727 nsresult
RemoveOldIndexDataValues(DatabaseConnection
* aConnection
);
3729 bool Init(TransactionBase
& aTransaction
) override
;
3731 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3733 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
;
3735 void Cleanup() override
;
3738 void ObjectStoreAddOrPutRequestOp::StoredFileInfo::AssertInvariants() const {
3739 // The only allowed types are eStructuredClone, eBlob and eMutableFile.
3740 MOZ_ASSERT(StructuredCloneFileBase::eStructuredClone
== mType
||
3741 StructuredCloneFileBase::eBlob
== mType
||
3742 StructuredCloneFileBase::eMutableFile
== mType
);
3744 // mFileInfo and a file actor in mFileActorOrInputStream are present until
3745 // the object is moved away, but an inputStream in mFileActorOrInputStream
3746 // can be released early.
3747 MOZ_ASSERT_IF(static_cast<bool>(mFileActorOrInputStream
) &&
3748 mFileActorOrInputStream
->is
<RefPtr
<DatabaseFile
>>(),
3749 static_cast<bool>(mFileInfo
));
3752 // In a non-moved StoredFileInfo, one of the following is true:
3753 // - This was an overflow structured clone (eStructuredClone) and
3754 // storedFileInfo.mFileActorOrInputStream CAN be a non-nullptr input
3755 // stream (but that might have been release by ReleaseInputStream).
3757 StructuredCloneFileBase::eStructuredClone
== mType
,
3758 !mFileActorOrInputStream
||
3759 (mFileActorOrInputStream
->is
<nsCOMPtr
<nsIInputStream
>>() &&
3760 mFileActorOrInputStream
->as
<nsCOMPtr
<nsIInputStream
>>()));
3762 // - This is a reference to a Blob (eBlob) that may or may not have
3763 // already been written to disk. storedFileInfo.mFileActorOrInputStream
3764 // MUST be a non-null file actor, but its GetInputStream may return
3765 // nullptr (so don't assert on that).
3766 MOZ_ASSERT_IF(StructuredCloneFileBase::eBlob
== mType
,
3767 mFileActorOrInputStream
->is
<RefPtr
<DatabaseFile
>>() &&
3768 mFileActorOrInputStream
->as
<RefPtr
<DatabaseFile
>>());
3770 // - It's a mutable file (eMutableFile). No writing will be performed,
3771 // and storedFileInfo.mFileActorOrInputStream is Nothing.
3772 MOZ_ASSERT_IF(StructuredCloneFileBase::eMutableFile
== mType
,
3773 mFileActorOrInputStream
->is
<Nothing
>());
3777 void ObjectStoreAddOrPutRequestOp::StoredFileInfo::EnsureCipherKey() {
3778 const auto& fileInfo
= GetFileInfo();
3779 const auto& fileMgr
= fileInfo
.Manager();
3781 // no need to generate cipher keys if we are not in PBM
3782 if (!fileMgr
.IsInPrivateBrowsingMode()) return;
3785 keyId
.AppendInt(fileInfo
.Id());
3786 gIndexedDBCipherKeyManager
->Ensure(fileMgr
.DatabaseID(), keyId
);
3789 ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
3790 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
, RefPtr
<DatabaseFile
> aFileActor
)
3791 : mFileInfo
{WrapNotNull(std::move(aFileInfo
))},
3792 mFileActorOrInputStream
{std::move(aFileActor
)}
3795 mType
{StructuredCloneFileBase::eBlob
}
3798 AssertIsOnBackgroundThread();
3802 MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo
);
3805 ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
3806 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
,
3807 nsCOMPtr
<nsIInputStream
> aInputStream
)
3808 : mFileInfo
{WrapNotNull(std::move(aFileInfo
))},
3809 mFileActorOrInputStream
{std::move(aInputStream
)}
3812 mType
{StructuredCloneFileBase::eStructuredClone
}
3815 AssertIsOnBackgroundThread();
3819 MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo
);
3822 ObjectStoreAddOrPutRequestOp::StoredFileInfo
3823 ObjectStoreAddOrPutRequestOp::StoredFileInfo::CreateForBlob(
3824 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
, RefPtr
<DatabaseFile
> aFileActor
) {
3825 return {std::move(aFileInfo
), std::move(aFileActor
)};
3828 ObjectStoreAddOrPutRequestOp::StoredFileInfo
3829 ObjectStoreAddOrPutRequestOp::StoredFileInfo::CreateForStructuredClone(
3830 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
,
3831 nsCOMPtr
<nsIInputStream
> aInputStream
) {
3832 return {std::move(aFileInfo
), std::move(aInputStream
)};
3835 bool ObjectStoreAddOrPutRequestOp::StoredFileInfo::ShouldCompress() const {
3836 // Must not be called after moving.
3837 MOZ_ASSERT(IsValid());
3839 // Compression is only necessary for eStructuredClone, i.e. when
3840 // mFileActorOrInputStream stored an input stream. However, this is only
3841 // called after GetInputStream, when mFileActorOrInputStream has been
3842 // cleared, which is only possible for this type.
3843 const bool res
= !mFileActorOrInputStream
;
3844 MOZ_ASSERT(res
== (StructuredCloneFileBase::eStructuredClone
== mType
));
3848 void ObjectStoreAddOrPutRequestOp::StoredFileInfo::NotifyWriteSucceeded()
3850 MOZ_ASSERT(IsValid());
3852 // For eBlob, clear the blob implementation.
3853 if (mFileActorOrInputStream
&&
3854 mFileActorOrInputStream
->is
<RefPtr
<DatabaseFile
>>()) {
3855 mFileActorOrInputStream
->as
<RefPtr
<DatabaseFile
>>()
3856 ->WriteSucceededClearBlobImpl();
3859 // For the other types, no action is necessary.
3862 ObjectStoreAddOrPutRequestOp::StoredFileInfo::InputStreamResult
3863 ObjectStoreAddOrPutRequestOp::StoredFileInfo::GetInputStream() {
3864 if (!mFileActorOrInputStream
) {
3865 MOZ_ASSERT(StructuredCloneFileBase::eStructuredClone
== mType
);
3866 return nsCOMPtr
<nsIInputStream
>{};
3869 // For the different cases, see also the comments in AssertInvariants.
3870 return mFileActorOrInputStream
->match(
3871 [](const Nothing
&) -> InputStreamResult
{
3872 return nsCOMPtr
<nsIInputStream
>{};
3874 [](const RefPtr
<DatabaseFile
>& databaseActor
) -> InputStreamResult
{
3876 auto inputStream
= databaseActor
->GetInputStream(rv
);
3877 if (NS_WARN_IF(rv
.Failed())) {
3878 return Err(rv
.StealNSResult());
3883 [this](const nsCOMPtr
<nsIInputStream
>& inputStream
) -> InputStreamResult
{
3884 auto res
= inputStream
;
3885 // destroy() clears the inputStream parameter, so we needed to make a
3887 mFileActorOrInputStream
.destroy();
3893 void ObjectStoreAddOrPutRequestOp::StoredFileInfo::Serialize(
3894 nsString
& aText
) const {
3896 MOZ_ASSERT(IsValid());
3898 const int64_t id
= (*mFileInfo
)->Id();
3900 auto structuredCloneHandler
= [&aText
, id
](const nsCOMPtr
<nsIInputStream
>&) {
3903 aText
.AppendInt(id
);
3906 // If mFileActorOrInputStream was moved, we had an inputStream before.
3907 if (!mFileActorOrInputStream
) {
3908 structuredCloneHandler(nullptr);
3912 // This encoding is parsed in DeserializeStructuredCloneFile.
3913 mFileActorOrInputStream
->match(
3914 [&aText
, id
](const Nothing
&) {
3916 aText
.AppendInt(-id
);
3918 [&aText
, id
](const RefPtr
<DatabaseFile
>&) {
3920 aText
.AppendInt(id
);
3922 structuredCloneHandler
);
3925 class ObjectStoreAddOrPutRequestOp::SCInputStream final
3926 : public nsIInputStream
{
3927 const JSStructuredCloneData
& mData
;
3928 JSStructuredCloneData::Iterator mIter
;
3931 explicit SCInputStream(const JSStructuredCloneData
& aData
)
3932 : mData(aData
), mIter(aData
.Start()) {}
3935 virtual ~SCInputStream() = default;
3937 NS_DECL_THREADSAFE_ISUPPORTS
3938 NS_DECL_NSIINPUTSTREAM
3941 class ObjectStoreGetRequestOp final
: public NormalTransactionOp
{
3942 friend class TransactionBase
;
3944 const IndexOrObjectStoreId mObjectStoreId
;
3945 SafeRefPtr
<Database
> mDatabase
;
3946 const Maybe
<SerializedKeyRange
> mOptionalKeyRange
;
3947 AutoTArray
<StructuredCloneReadInfoParent
, 1> mResponse
;
3948 PBackgroundParent
* mBackgroundParent
;
3949 uint32_t mPreprocessInfoCount
;
3950 const uint32_t mLimit
;
3954 // Only created by TransactionBase.
3955 ObjectStoreGetRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
3956 const RequestParams
& aParams
, bool aGetAll
);
3958 ~ObjectStoreGetRequestOp() override
= default;
3960 template <typename T
>
3961 mozilla::Result
<T
, nsresult
> ConvertResponse(
3962 StructuredCloneReadInfoParent
&& aInfo
);
3964 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3966 bool HasPreprocessInfo() override
;
3968 mozilla::Result
<PreprocessParams
, nsresult
> GetPreprocessParams() override
;
3970 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
;
3973 class ObjectStoreGetKeyRequestOp final
: public NormalTransactionOp
{
3974 friend class TransactionBase
;
3976 const IndexOrObjectStoreId mObjectStoreId
;
3977 const Maybe
<SerializedKeyRange
> mOptionalKeyRange
;
3978 const uint32_t mLimit
;
3980 nsTArray
<Key
> mResponse
;
3983 // Only created by TransactionBase.
3984 ObjectStoreGetKeyRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
3985 const RequestParams
& aParams
, bool aGetAll
);
3987 ~ObjectStoreGetKeyRequestOp() override
= default;
3989 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3991 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
;
3994 class ObjectStoreDeleteRequestOp final
: public NormalTransactionOp
{
3995 friend class TransactionBase
;
3997 const ObjectStoreDeleteParams mParams
;
3998 ObjectStoreDeleteResponse mResponse
;
3999 bool mObjectStoreMayHaveIndexes
;
4002 ObjectStoreDeleteRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
4003 const ObjectStoreDeleteParams
& aParams
);
4005 ~ObjectStoreDeleteRequestOp() override
= default;
4007 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
4009 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
{
4010 aResponse
= std::move(mResponse
);
4015 class ObjectStoreClearRequestOp final
: public NormalTransactionOp
{
4016 friend class TransactionBase
;
4018 const ObjectStoreClearParams mParams
;
4019 ObjectStoreClearResponse mResponse
;
4020 bool mObjectStoreMayHaveIndexes
;
4023 ObjectStoreClearRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
4024 const ObjectStoreClearParams
& aParams
);
4026 ~ObjectStoreClearRequestOp() override
= default;
4028 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
4030 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
{
4031 aResponse
= std::move(mResponse
);
4036 class ObjectStoreCountRequestOp final
: public NormalTransactionOp
{
4037 friend class TransactionBase
;
4039 const ObjectStoreCountParams mParams
;
4040 ObjectStoreCountResponse mResponse
;
4043 ObjectStoreCountRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
4044 const ObjectStoreCountParams
& aParams
)
4045 : NormalTransactionOp(std::move(aTransaction
)), mParams(aParams
) {}
4047 ~ObjectStoreCountRequestOp() override
= default;
4049 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
4051 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
{
4052 aResponse
= std::move(mResponse
);
4053 *aResponseSize
= sizeof(uint64_t);
4057 class IndexRequestOpBase
: public NormalTransactionOp
{
4059 const SafeRefPtr
<FullIndexMetadata
> mMetadata
;
4062 IndexRequestOpBase(SafeRefPtr
<TransactionBase
> aTransaction
,
4063 const RequestParams
& aParams
)
4064 : NormalTransactionOp(std::move(aTransaction
)),
4065 mMetadata(IndexMetadataForParams(Transaction(), aParams
)) {}
4067 ~IndexRequestOpBase() override
= default;
4070 static SafeRefPtr
<FullIndexMetadata
> IndexMetadataForParams(
4071 const TransactionBase
& aTransaction
, const RequestParams
& aParams
);
4074 class IndexGetRequestOp final
: public IndexRequestOpBase
{
4075 friend class TransactionBase
;
4077 SafeRefPtr
<Database
> mDatabase
;
4078 const Maybe
<SerializedKeyRange
> mOptionalKeyRange
;
4079 AutoTArray
<StructuredCloneReadInfoParent
, 1> mResponse
;
4080 PBackgroundParent
* mBackgroundParent
;
4081 const uint32_t mLimit
;
4085 // Only created by TransactionBase.
4086 IndexGetRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
4087 const RequestParams
& aParams
, bool aGetAll
);
4089 ~IndexGetRequestOp() override
= default;
4091 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
4093 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
;
4096 class IndexGetKeyRequestOp final
: public IndexRequestOpBase
{
4097 friend class TransactionBase
;
4099 const Maybe
<SerializedKeyRange
> mOptionalKeyRange
;
4100 AutoTArray
<Key
, 1> mResponse
;
4101 const uint32_t mLimit
;
4105 // Only created by TransactionBase.
4106 IndexGetKeyRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
4107 const RequestParams
& aParams
, bool aGetAll
);
4109 ~IndexGetKeyRequestOp() override
= default;
4111 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
4113 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
;
4116 class IndexCountRequestOp final
: public IndexRequestOpBase
{
4117 friend class TransactionBase
;
4119 const IndexCountParams mParams
;
4120 IndexCountResponse mResponse
;
4123 // Only created by TransactionBase.
4124 IndexCountRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
4125 const RequestParams
& aParams
)
4126 : IndexRequestOpBase(std::move(aTransaction
), aParams
),
4127 mParams(aParams
.get_IndexCountParams()) {}
4129 ~IndexCountRequestOp() override
= default;
4131 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
4133 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
{
4134 aResponse
= std::move(mResponse
);
4135 *aResponseSize
= sizeof(uint64_t);
4139 template <IDBCursorType CursorType
>
4142 constexpr IDBCursorType
ToKeyOnlyType(const IDBCursorType aType
) {
4143 MOZ_ASSERT(aType
== IDBCursorType::ObjectStore
||
4144 aType
== IDBCursorType::ObjectStoreKey
||
4145 aType
== IDBCursorType::Index
|| aType
== IDBCursorType::IndexKey
);
4147 case IDBCursorType::ObjectStore
:
4148 case IDBCursorType::ObjectStoreKey
:
4149 return IDBCursorType::ObjectStoreKey
;
4150 case IDBCursorType::Index
:
4151 case IDBCursorType::IndexKey
:
4152 return IDBCursorType::IndexKey
;
4156 template <IDBCursorType CursorType
>
4157 using CursorPosition
= CursorData
<ToKeyOnlyType(CursorType
)>;
4160 constexpr indexedDB::OpenCursorParams::Type
ToOpenCursorParamsType(
4161 const IDBCursorType aType
) {
4162 MOZ_ASSERT(aType
== IDBCursorType::ObjectStore
||
4163 aType
== IDBCursorType::ObjectStoreKey
||
4164 aType
== IDBCursorType::Index
|| aType
== IDBCursorType::IndexKey
);
4166 case IDBCursorType::ObjectStore
:
4167 return indexedDB::OpenCursorParams::TObjectStoreOpenCursorParams
;
4168 case IDBCursorType::ObjectStoreKey
:
4169 return indexedDB::OpenCursorParams::TObjectStoreOpenKeyCursorParams
;
4170 case IDBCursorType::Index
:
4171 return indexedDB::OpenCursorParams::TIndexOpenCursorParams
;
4172 case IDBCursorType::IndexKey
:
4173 return indexedDB::OpenCursorParams::TIndexOpenKeyCursorParams
;
4178 class CursorBase
: public PBackgroundIDBCursorParent
{
4179 friend class TransactionBase
;
4180 template <IDBCursorType CursorType
>
4181 friend class CommonOpenOpHelper
;
4184 const SafeRefPtr
<TransactionBase
> mTransaction
;
4186 // This should only be touched on the PBackground thread to check whether
4187 // the objectStore has been deleted. Holding these saves a hash lookup for
4188 // every call to continue()/advance().
4189 InitializedOnce
<const NotNull
<SafeRefPtr
<FullObjectStoreMetadata
>>>
4190 mObjectStoreMetadata
;
4192 const IndexOrObjectStoreId mObjectStoreId
;
4194 LazyInitializedOnce
<const Key
>
4195 mLocaleAwareRangeBound
; ///< If the cursor is based on a key range, the
4196 ///< bound in the direction of iteration (e.g.
4197 ///< the upper bound in case of mDirection ==
4198 ///< NEXT). If the cursor is based on a key, it
4199 ///< is unset. If mLocale is set, this was
4200 ///< converted to mLocale.
4202 const Direction mDirection
;
4204 const int32_t mMaxExtraCount
;
4206 const bool mIsSameProcessActor
;
4208 struct ConstructFromTransactionBase
{};
4211 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::CursorBase
,
4214 CursorBase(SafeRefPtr
<TransactionBase
> aTransaction
,
4215 SafeRefPtr
<FullObjectStoreMetadata
> aObjectStoreMetadata
,
4216 Direction aDirection
,
4217 ConstructFromTransactionBase aConstructionTag
);
4220 // Reference counted.
4221 ~CursorBase() override
{ MOZ_ASSERT(!mObjectStoreMetadata
); }
4224 virtual bool Start(const OpenCursorParams
& aParams
) = 0;
4227 class IndexCursorBase
: public CursorBase
{
4229 bool IsLocaleAware() const { return !mLocale
.IsEmpty(); }
4231 IndexCursorBase(SafeRefPtr
<TransactionBase
> aTransaction
,
4232 SafeRefPtr
<FullObjectStoreMetadata
> aObjectStoreMetadata
,
4233 SafeRefPtr
<FullIndexMetadata
> aIndexMetadata
,
4234 Direction aDirection
,
4235 ConstructFromTransactionBase aConstructionTag
)
4236 : CursorBase
{std::move(aTransaction
), std::move(aObjectStoreMetadata
),
4237 aDirection
, aConstructionTag
},
4238 mIndexMetadata(WrapNotNull(std::move(aIndexMetadata
))),
4239 mIndexId((*mIndexMetadata
)->mCommonMetadata
.id()),
4240 mUniqueIndex((*mIndexMetadata
)->mCommonMetadata
.unique()),
4241 mLocale((*mIndexMetadata
)->mCommonMetadata
.locale()) {}
4244 IndexOrObjectStoreId
Id() const { return mIndexId
; }
4246 // This should only be touched on the PBackground thread to check whether
4247 // the index has been deleted. Holding these saves a hash lookup for every
4248 // call to continue()/advance().
4249 InitializedOnce
<const NotNull
<SafeRefPtr
<FullIndexMetadata
>>> mIndexMetadata
;
4250 const IndexOrObjectStoreId mIndexId
;
4251 const bool mUniqueIndex
;
4253 mLocale
; ///< The locale if the cursor is locale-aware, otherwise empty.
4255 struct ContinueQueries
{
4256 nsCString mContinueQuery
;
4257 nsCString mContinueToQuery
;
4258 nsCString mContinuePrimaryKeyQuery
;
4260 const nsACString
& GetContinueQuery(const bool hasContinueKey
,
4261 const bool hasContinuePrimaryKey
) const {
4262 return hasContinuePrimaryKey
? mContinuePrimaryKeyQuery
4263 : hasContinueKey
? mContinueToQuery
4269 class ObjectStoreCursorBase
: public CursorBase
{
4271 using CursorBase::CursorBase
;
4273 static constexpr bool IsLocaleAware() { return false; }
4276 IndexOrObjectStoreId
Id() const { return mObjectStoreId
; }
4278 struct ContinueQueries
{
4279 nsCString mContinueQuery
;
4280 nsCString mContinueToQuery
;
4282 const nsACString
& GetContinueQuery(const bool hasContinueKey
,
4283 const bool hasContinuePrimaryKey
) const {
4284 MOZ_ASSERT(!hasContinuePrimaryKey
);
4285 return hasContinueKey
? mContinueToQuery
: mContinueQuery
;
4290 using FilesArray
= nsTArray
<nsTArray
<StructuredCloneFileParent
>>;
4292 struct PseudoFilesArray
{
4293 static constexpr bool IsEmpty() { return true; }
4295 static constexpr void Clear() {}
4298 template <IDBCursorType CursorType
>
4300 std::conditional_t
<!CursorTypeTraits
<CursorType
>::IsKeyOnlyCursor
,
4301 FilesArray
, PseudoFilesArray
>;
4303 class ValueCursorBase
{
4304 friend struct ValuePopulateResponseHelper
<true>;
4305 friend struct ValuePopulateResponseHelper
<false>;
4308 explicit ValueCursorBase(TransactionBase
* const aTransaction
)
4309 : mDatabase(aTransaction
->GetDatabasePtr()),
4310 mFileManager(mDatabase
->GetFileManagerPtr()),
4311 mBackgroundParent(WrapNotNull(aTransaction
->GetBackgroundParent())) {
4312 MOZ_ASSERT(mDatabase
);
4315 void ProcessFiles(CursorResponse
& aResponse
, const FilesArray
& aFiles
);
4317 ~ValueCursorBase() { MOZ_ASSERT(!mBackgroundParent
); }
4319 const SafeRefPtr
<Database
> mDatabase
;
4320 const NotNull
<SafeRefPtr
<DatabaseFileManager
>> mFileManager
;
4322 InitializedOnce
<const NotNull
<PBackgroundParent
*>> mBackgroundParent
;
4325 class KeyCursorBase
{
4327 explicit KeyCursorBase(TransactionBase
* const /*aTransaction*/) {}
4329 static constexpr void ProcessFiles(CursorResponse
& aResponse
,
4330 const PseudoFilesArray
& aFiles
) {}
4333 template <IDBCursorType CursorType
>
4334 class CursorOpBaseHelperBase
;
4336 template <IDBCursorType CursorType
>
4338 : public std::conditional_t
<
4339 CursorTypeTraits
<CursorType
>::IsObjectStoreCursor
,
4340 ObjectStoreCursorBase
, IndexCursorBase
>,
4341 public std::conditional_t
<CursorTypeTraits
<CursorType
>::IsKeyOnlyCursor
,
4342 KeyCursorBase
, ValueCursorBase
> {
4344 std::conditional_t
<CursorTypeTraits
<CursorType
>::IsObjectStoreCursor
,
4345 ObjectStoreCursorBase
, IndexCursorBase
>;
4347 using KeyValueBase
=
4348 std::conditional_t
<CursorTypeTraits
<CursorType
>::IsKeyOnlyCursor
,
4349 KeyCursorBase
, ValueCursorBase
>;
4351 static constexpr bool IsIndexCursor
=
4352 !CursorTypeTraits
<CursorType
>::IsObjectStoreCursor
;
4354 static constexpr bool IsValueCursor
=
4355 !CursorTypeTraits
<CursorType
>::IsKeyOnlyCursor
;
4362 using CursorBase::Manager
;
4363 using CursorBase::mDirection
;
4364 using CursorBase::mObjectStoreId
;
4365 using CursorBase::mTransaction
;
4366 using typename
CursorBase::ActorDestroyReason
;
4368 using TypedOpenOpHelper
=
4369 std::conditional_t
<IsIndexCursor
, IndexOpenOpHelper
<CursorType
>,
4370 ObjectStoreOpenOpHelper
<CursorType
>>;
4372 friend class CursorOpBaseHelperBase
<CursorType
>;
4373 friend class CommonOpenOpHelper
<CursorType
>;
4374 friend TypedOpenOpHelper
;
4375 friend class OpenOpHelper
<CursorType
>;
4377 CursorOpBase
* mCurrentlyRunningOp
= nullptr;
4379 LazyInitializedOnce
<const typename
Base::ContinueQueries
> mContinueQueries
;
4381 // Only called by TransactionBase.
4382 bool Start(const OpenCursorParams
& aParams
) final
;
4384 void SendResponseInternal(CursorResponse
& aResponse
,
4385 const FilesArrayT
<CursorType
>& aFiles
);
4387 // Must call SendResponseInternal!
4388 bool SendResponse(const CursorResponse
& aResponse
) = delete;
4391 void ActorDestroy(ActorDestroyReason aWhy
) override
;
4393 mozilla::ipc::IPCResult
RecvDeleteMe() override
;
4395 mozilla::ipc::IPCResult
RecvContinue(
4396 const CursorRequestParams
& aParams
, const Key
& aCurrentKey
,
4397 const Key
& aCurrentObjectStoreKey
) override
;
4400 Cursor(SafeRefPtr
<TransactionBase
> aTransaction
,
4401 SafeRefPtr
<FullObjectStoreMetadata
> aObjectStoreMetadata
,
4402 SafeRefPtr
<FullIndexMetadata
> aIndexMetadata
,
4403 typename
Base::Direction aDirection
,
4404 typename
Base::ConstructFromTransactionBase aConstructionTag
)
4405 : Base
{std::move(aTransaction
), std::move(aObjectStoreMetadata
),
4406 std::move(aIndexMetadata
), aDirection
, aConstructionTag
},
4407 KeyValueBase
{this->mTransaction
.unsafeGetRawPtr()} {}
4409 Cursor(SafeRefPtr
<TransactionBase
> aTransaction
,
4410 SafeRefPtr
<FullObjectStoreMetadata
> aObjectStoreMetadata
,
4411 typename
Base::Direction aDirection
,
4412 typename
Base::ConstructFromTransactionBase aConstructionTag
)
4413 : Base
{std::move(aTransaction
), std::move(aObjectStoreMetadata
),
4414 aDirection
, aConstructionTag
},
4415 KeyValueBase
{this->mTransaction
.unsafeGetRawPtr()} {}
4418 void SetOptionalKeyRange(const Maybe
<SerializedKeyRange
>& aOptionalKeyRange
,
4421 bool VerifyRequestParams(const CursorRequestParams
& aParams
,
4422 const CursorPosition
<CursorType
>& aPosition
) const;
4424 ~Cursor() final
= default;
4427 template <IDBCursorType CursorType
>
4428 class Cursor
<CursorType
>::CursorOpBase
4429 : public TransactionDatabaseOperationBase
{
4430 friend class CursorOpBaseHelperBase
<CursorType
>;
4433 RefPtr
<Cursor
> mCursor
;
4434 FilesArrayT
<CursorType
> mFiles
; // TODO: Consider removing this member
4435 // entirely if we are no value cursor.
4437 CursorResponse mResponse
;
4444 explicit CursorOpBase(Cursor
* aCursor
)
4445 : TransactionDatabaseOperationBase(aCursor
->mTransaction
.clonePtr()),
4449 mResponseSent(false)
4452 AssertIsOnBackgroundThread();
4453 MOZ_ASSERT(aCursor
);
4456 ~CursorOpBase() override
= default;
4458 bool SendFailureResult(nsresult aResultCode
) final
;
4459 nsresult
SendSuccessResult() final
;
4461 void Cleanup() override
;
4464 template <IDBCursorType CursorType
>
4467 using ResponseSizeOrError
= Result
<size_t, nsresult
>;
4469 template <IDBCursorType CursorType
>
4470 class CursorOpBaseHelperBase
{
4472 explicit CursorOpBaseHelperBase(
4473 typename Cursor
<CursorType
>::CursorOpBase
& aOp
)
4476 ResponseSizeOrError
PopulateResponseFromStatement(mozIStorageStatement
* aStmt
,
4477 bool aInitializeResponse
,
4478 Key
* const aOptOutSortKey
);
4480 void PopulateExtraResponses(mozIStorageStatement
* aStmt
,
4481 uint32_t aMaxExtraCount
,
4482 const size_t aInitialResponseSize
,
4483 const nsACString
& aOperation
,
4484 Key
* const aOptPreviousSortKey
);
4487 Cursor
<CursorType
>& GetCursor() {
4488 MOZ_ASSERT(mOp
.mCursor
);
4489 return *mOp
.mCursor
;
4492 void SetResponse(CursorResponse aResponse
) {
4493 mOp
.mResponse
= std::move(aResponse
);
4497 typename Cursor
<CursorType
>::CursorOpBase
& mOp
;
4500 class CommonOpenOpHelperBase
{
4502 static void AppendConditionClause(const nsACString
& aColumnName
,
4503 const nsACString
& aStatementParameterName
,
4504 bool aLessThan
, bool aEquals
,
4505 nsCString
& aResult
);
4508 template <IDBCursorType CursorType
>
4509 class CommonOpenOpHelper
: public CursorOpBaseHelperBase
<CursorType
>,
4510 protected CommonOpenOpHelperBase
{
4512 explicit CommonOpenOpHelper(typename Cursor
<CursorType
>::OpenOp
& aOp
)
4513 : CursorOpBaseHelperBase
<CursorType
>{aOp
} {}
4516 using CursorOpBaseHelperBase
<CursorType
>::GetCursor
;
4517 using CursorOpBaseHelperBase
<CursorType
>::PopulateExtraResponses
;
4518 using CursorOpBaseHelperBase
<CursorType
>::PopulateResponseFromStatement
;
4519 using CursorOpBaseHelperBase
<CursorType
>::SetResponse
;
4521 const Maybe
<SerializedKeyRange
>& GetOptionalKeyRange() const {
4522 // This downcast is safe, since we initialized mOp from an OpenOp in the
4524 return static_cast<typename Cursor
<CursorType
>::OpenOp
&>(this->mOp
)
4528 nsresult
ProcessStatementSteps(mozIStorageStatement
* aStmt
);
4531 template <IDBCursorType CursorType
>
4532 class ObjectStoreOpenOpHelper
: protected CommonOpenOpHelper
<CursorType
> {
4534 using CommonOpenOpHelper
<CursorType
>::CommonOpenOpHelper
;
4537 using CommonOpenOpHelper
<CursorType
>::GetCursor
;
4538 using CommonOpenOpHelper
<CursorType
>::GetOptionalKeyRange
;
4539 using CommonOpenOpHelper
<CursorType
>::AppendConditionClause
;
4541 void PrepareKeyConditionClauses(const nsACString
& aDirectionClause
,
4542 const nsACString
& aQueryStart
);
4545 template <IDBCursorType CursorType
>
4546 class IndexOpenOpHelper
: protected CommonOpenOpHelper
<CursorType
> {
4548 using CommonOpenOpHelper
<CursorType
>::CommonOpenOpHelper
;
4551 using CommonOpenOpHelper
<CursorType
>::GetCursor
;
4552 using CommonOpenOpHelper
<CursorType
>::GetOptionalKeyRange
;
4553 using CommonOpenOpHelper
<CursorType
>::AppendConditionClause
;
4555 void PrepareIndexKeyConditionClause(
4556 const nsACString
& aDirectionClause
,
4557 const nsLiteralCString
& aObjectDataKeyPrefix
, nsAutoCString aQueryStart
);
4561 class OpenOpHelper
<IDBCursorType::ObjectStore
>
4562 : public ObjectStoreOpenOpHelper
<IDBCursorType::ObjectStore
> {
4564 using ObjectStoreOpenOpHelper
<
4565 IDBCursorType::ObjectStore
>::ObjectStoreOpenOpHelper
;
4567 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
);
4571 class OpenOpHelper
<IDBCursorType::ObjectStoreKey
>
4572 : public ObjectStoreOpenOpHelper
<IDBCursorType::ObjectStoreKey
> {
4574 using ObjectStoreOpenOpHelper
<
4575 IDBCursorType::ObjectStoreKey
>::ObjectStoreOpenOpHelper
;
4577 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
);
4581 class OpenOpHelper
<IDBCursorType::Index
>
4582 : IndexOpenOpHelper
<IDBCursorType::Index
> {
4584 void PrepareKeyConditionClauses(const nsACString
& aDirectionClause
,
4585 nsAutoCString aQueryStart
) {
4586 PrepareIndexKeyConditionClause(aDirectionClause
, "index_table."_ns
,
4587 std::move(aQueryStart
));
4591 using IndexOpenOpHelper
<IDBCursorType::Index
>::IndexOpenOpHelper
;
4593 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
);
4597 class OpenOpHelper
<IDBCursorType::IndexKey
>
4598 : IndexOpenOpHelper
<IDBCursorType::IndexKey
> {
4600 void PrepareKeyConditionClauses(const nsACString
& aDirectionClause
,
4601 nsAutoCString aQueryStart
) {
4602 PrepareIndexKeyConditionClause(aDirectionClause
, ""_ns
,
4603 std::move(aQueryStart
));
4607 using IndexOpenOpHelper
<IDBCursorType::IndexKey
>::IndexOpenOpHelper
;
4609 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
);
4612 template <IDBCursorType CursorType
>
4613 class Cursor
<CursorType
>::OpenOp final
: public CursorOpBase
{
4614 friend class Cursor
<CursorType
>;
4615 friend class CommonOpenOpHelper
<CursorType
>;
4617 const Maybe
<SerializedKeyRange
> mOptionalKeyRange
;
4619 using CursorOpBase::mCursor
;
4620 using CursorOpBase::mResponse
;
4622 // Only created by Cursor.
4623 OpenOp(Cursor
* const aCursor
,
4624 const Maybe
<SerializedKeyRange
>& aOptionalKeyRange
)
4625 : CursorOpBase(aCursor
), mOptionalKeyRange(aOptionalKeyRange
) {}
4627 // Reference counted.
4628 ~OpenOp() override
= default;
4630 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
4633 template <IDBCursorType CursorType
>
4634 class Cursor
<CursorType
>::ContinueOp final
4635 : public Cursor
<CursorType
>::CursorOpBase
{
4636 friend class Cursor
<CursorType
>;
4638 using CursorOpBase::mCursor
;
4639 using CursorOpBase::mResponse
;
4640 const CursorRequestParams mParams
;
4642 // Only created by Cursor.
4643 ContinueOp(Cursor
* const aCursor
, CursorRequestParams aParams
,
4644 CursorPosition
<CursorType
> aPosition
)
4645 : CursorOpBase(aCursor
),
4646 mParams(std::move(aParams
)),
4647 mCurrentPosition
{std::move(aPosition
)} {
4648 MOZ_ASSERT(mParams
.type() != CursorRequestParams::T__None
);
4651 // Reference counted.
4652 ~ContinueOp() override
= default;
4654 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
4656 const CursorPosition
<CursorType
> mCurrentPosition
;
4659 class Utils final
: public PBackgroundIndexedDBUtilsParent
{
4661 bool mActorDestroyed
;
4667 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Utils
)
4670 // Reference counted.
4673 // IPDL methods are only called by IPDL.
4674 void ActorDestroy(ActorDestroyReason aWhy
) override
;
4676 mozilla::ipc::IPCResult
RecvDeleteMe() override
;
4678 mozilla::ipc::IPCResult
RecvGetFileReferences(
4679 const PersistenceType
& aPersistenceType
, const nsACString
& aOrigin
,
4680 const nsAString
& aDatabaseName
, const int64_t& aFileId
, int32_t* aRefCnt
,
4681 int32_t* aDBRefCnt
, bool* aResult
) override
;
4684 /*******************************************************************************
4685 * Other class declarations
4686 ******************************************************************************/
4688 struct DatabaseActorInfo final
{
4689 friend class mozilla::DefaultDelete
<DatabaseActorInfo
>;
4691 SafeRefPtr
<FullDatabaseMetadata
> mMetadata
;
4692 nsTArray
<NotNull
<CheckedUnsafePtr
<Database
>>> mLiveDatabases
;
4693 RefPtr
<FactoryOp
> mWaitingFactoryOp
;
4695 DatabaseActorInfo(SafeRefPtr
<FullDatabaseMetadata
> aMetadata
,
4696 NotNull
<Database
*> aDatabase
)
4697 : mMetadata(std::move(aMetadata
)) {
4698 MOZ_COUNT_CTOR(DatabaseActorInfo
);
4700 mLiveDatabases
.AppendElement(aDatabase
);
4704 ~DatabaseActorInfo() {
4705 MOZ_ASSERT(mLiveDatabases
.IsEmpty());
4706 MOZ_ASSERT(!mWaitingFactoryOp
|| !mWaitingFactoryOp
->HasBlockedDatabases());
4708 MOZ_COUNT_DTOR(DatabaseActorInfo
);
4712 class DatabaseLoggingInfo final
{
4714 // Just for potential warnings.
4715 friend class Factory
;
4718 LoggingInfo mLoggingInfo
;
4721 explicit DatabaseLoggingInfo(const LoggingInfo
& aLoggingInfo
)
4722 : mLoggingInfo(aLoggingInfo
) {
4723 AssertIsOnBackgroundThread();
4724 MOZ_ASSERT(aLoggingInfo
.nextTransactionSerialNumber());
4725 MOZ_ASSERT(aLoggingInfo
.nextVersionChangeTransactionSerialNumber());
4726 MOZ_ASSERT(aLoggingInfo
.nextRequestSerialNumber());
4729 const nsID
& Id() const {
4730 AssertIsOnBackgroundThread();
4732 return mLoggingInfo
.backgroundChildLoggingId();
4735 int64_t NextTransactionSN(IDBTransaction::Mode aMode
) {
4736 AssertIsOnBackgroundThread();
4737 MOZ_ASSERT(mLoggingInfo
.nextTransactionSerialNumber() < INT64_MAX
);
4738 MOZ_ASSERT(mLoggingInfo
.nextVersionChangeTransactionSerialNumber() >
4741 if (aMode
== IDBTransaction::Mode::VersionChange
) {
4742 return mLoggingInfo
.nextVersionChangeTransactionSerialNumber()--;
4745 return mLoggingInfo
.nextTransactionSerialNumber()++;
4748 uint64_t NextRequestSN() {
4749 AssertIsOnBackgroundThread();
4750 MOZ_ASSERT(mLoggingInfo
.nextRequestSerialNumber() < UINT64_MAX
);
4752 return mLoggingInfo
.nextRequestSerialNumber()++;
4755 NS_INLINE_DECL_REFCOUNTING(DatabaseLoggingInfo
)
4758 ~DatabaseLoggingInfo();
4761 class QuotaClient final
: public mozilla::dom::quota::Client
{
4762 static QuotaClient
* sInstance
;
4764 nsCOMPtr
<nsIEventTarget
> mBackgroundThread
;
4765 nsCOMPtr
<nsITimer
> mDeleteTimer
;
4766 nsTArray
<RefPtr
<Maintenance
>> mMaintenanceQueue
;
4767 RefPtr
<Maintenance
> mCurrentMaintenance
;
4768 RefPtr
<nsThreadPool
> mMaintenanceThreadPool
;
4769 nsClassHashtable
<nsRefPtrHashKey
<DatabaseFileManager
>, nsTArray
<int64_t>>
4770 mPendingDeleteInfos
;
4775 static QuotaClient
* GetInstance() {
4776 AssertIsOnBackgroundThread();
4781 nsIEventTarget
* BackgroundThread() const {
4782 MOZ_ASSERT(mBackgroundThread
);
4783 return mBackgroundThread
;
4786 nsresult
AsyncDeleteFile(DatabaseFileManager
* aFileManager
, int64_t aFileId
);
4788 nsresult
FlushPendingFileDeletions();
4790 RefPtr
<Maintenance
> GetCurrentMaintenance() const {
4791 return mCurrentMaintenance
;
4794 void NoteFinishedMaintenance(Maintenance
* aMaintenance
) {
4795 AssertIsOnBackgroundThread();
4796 MOZ_ASSERT(aMaintenance
);
4797 MOZ_ASSERT(mCurrentMaintenance
== aMaintenance
);
4799 mCurrentMaintenance
= nullptr;
4801 QuotaManager::MaybeRecordQuotaClientShutdownStep(quota::Client::IDB
,
4802 "Maintenance finished"_ns
);
4804 ProcessMaintenanceQueue();
4807 nsThreadPool
* GetOrCreateThreadPool();
4809 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::QuotaClient
,
4812 mozilla::dom::quota::Client::Type
GetType() override
;
4814 nsresult
UpgradeStorageFrom1_0To2_0(nsIFile
* aDirectory
) override
;
4816 nsresult
UpgradeStorageFrom2_1To2_2(nsIFile
* aDirectory
) override
;
4818 Result
<UsageInfo
, nsresult
> InitOrigin(PersistenceType aPersistenceType
,
4819 const OriginMetadata
& aOriginMetadata
,
4820 const AtomicBool
& aCanceled
) override
;
4822 nsresult
InitOriginWithoutTracking(PersistenceType aPersistenceType
,
4823 const OriginMetadata
& aOriginMetadata
,
4824 const AtomicBool
& aCanceled
) override
;
4826 Result
<UsageInfo
, nsresult
> GetUsageForOrigin(
4827 PersistenceType aPersistenceType
, const OriginMetadata
& aOriginMetadata
,
4828 const AtomicBool
& aCanceled
) override
;
4830 void OnOriginClearCompleted(PersistenceType aPersistenceType
,
4831 const nsACString
& aOrigin
) override
;
4833 void ReleaseIOThreadObjects() override
;
4835 void AbortOperationsForLocks(
4836 const DirectoryLockIdTable
& aDirectoryLockIds
) override
;
4838 void AbortOperationsForProcess(ContentParentId aContentParentId
) override
;
4840 void AbortAllOperations() override
;
4842 void StartIdleMaintenance() override
;
4844 void StopIdleMaintenance() override
;
4847 ~QuotaClient() override
;
4849 void InitiateShutdown() override
;
4850 bool IsShutdownCompleted() const override
;
4851 nsCString
GetShutdownStatus() const override
;
4852 void ForceKillActors() override
;
4853 void FinalizeShutdown() override
;
4855 static void DeleteTimerCallback(nsITimer
* aTimer
, void* aClosure
);
4857 Result
<nsCOMPtr
<nsIFile
>, nsresult
> GetDirectory(
4858 PersistenceType aPersistenceType
, const nsACString
& aOrigin
);
4860 struct SubdirectoriesToProcessAndDatabaseFilenames
{
4861 AutoTArray
<nsString
, 20> subdirsToProcess
;
4862 nsTHashSet
<nsString
> databaseFilenames
{20};
4865 struct SubdirectoriesToProcessAndDatabaseFilenamesAndObsoleteFilenames
{
4866 AutoTArray
<nsString
, 20> subdirsToProcess
;
4867 nsTHashSet
<nsString
> databaseFilenames
{20};
4868 nsTHashSet
<nsString
> obsoleteFilenames
{20};
4871 enum class ObsoleteFilenamesHandling
{ Include
, Omit
};
4873 template <ObsoleteFilenamesHandling ObsoleteFilenames
>
4874 using GetDatabaseFilenamesResult
= std::conditional_t
<
4875 ObsoleteFilenames
== ObsoleteFilenamesHandling::Include
,
4876 SubdirectoriesToProcessAndDatabaseFilenamesAndObsoleteFilenames
,
4877 SubdirectoriesToProcessAndDatabaseFilenames
>;
4879 // Returns a two-part or three-part structure:
4881 // The first part is an array of subdirectories to process.
4883 // The second part is a hashtable of database filenames.
4885 // When ObsoleteFilenames is ObsoleteFilenamesHandling::Include, will also
4886 // collect files based on the marker files. For now,
4887 // GetUsageForOriginInternal() is the only consumer of this result because it
4888 // checks those unfinished deletion and clean them up after that.
4889 template <ObsoleteFilenamesHandling ObsoleteFilenames
=
4890 ObsoleteFilenamesHandling::Omit
>
4891 Result
<GetDatabaseFilenamesResult
<ObsoleteFilenames
>, nsresult
>
4892 GetDatabaseFilenames(nsIFile
& aDirectory
, const AtomicBool
& aCanceled
);
4894 nsresult
GetUsageForOriginInternal(PersistenceType aPersistenceType
,
4895 const OriginMetadata
& aOriginMetadata
,
4896 const AtomicBool
& aCanceled
,
4897 bool aInitializing
, UsageInfo
* aUsageInfo
);
4899 // Runs on the PBackground thread. Checks to see if there's a queued
4900 // Maintenance to run.
4901 void ProcessMaintenanceQueue();
4904 class DeleteFilesRunnable final
: public Runnable
,
4905 public OpenDirectoryListener
{
4906 using DirectoryLock
= mozilla::dom::quota::DirectoryLock
;
4909 // Just created on the PBackground thread. Next step is
4910 // State_DirectoryOpenPending.
4913 // Waiting for directory open allowed on the main thread. The next step is
4914 // State_DatabaseWorkOpen.
4915 State_DirectoryOpenPending
,
4917 // Waiting to do/doing work on the QuotaManager IO thread. The next step is
4918 // State_UnblockingOpen.
4919 State_DatabaseWorkOpen
,
4921 // Notifying the QuotaManager that it can proceed to the next operation on
4922 // the main thread. Next step is State_Completed.
4923 State_UnblockingOpen
,
4929 nsCOMPtr
<nsIEventTarget
> mOwningEventTarget
;
4930 SafeRefPtr
<DatabaseFileManager
> mFileManager
;
4931 RefPtr
<DirectoryLock
> mDirectoryLock
;
4932 nsTArray
<int64_t> mFileIds
;
4936 DeleteFilesRunnable(SafeRefPtr
<DatabaseFileManager
> aFileManager
,
4937 nsTArray
<int64_t>&& aFileIds
);
4939 void RunImmediately();
4942 ~DeleteFilesRunnable() = default;
4946 void DoDatabaseWork();
4952 NS_DECL_ISUPPORTS_INHERITED
4955 // OpenDirectoryListener overrides.
4956 virtual void DirectoryLockAcquired(DirectoryLock
* aLock
) override
;
4958 virtual void DirectoryLockFailed() override
;
4961 class Maintenance final
: public Runnable
, public OpenDirectoryListener
{
4962 struct DirectoryInfo final
{
4963 InitializedOnce
<const FullOriginMetadata
> mFullOriginMetadata
;
4964 InitializedOnce
<const nsTArray
<nsString
>> mDatabasePaths
;
4965 const PersistenceType mPersistenceType
;
4967 DirectoryInfo(PersistenceType aPersistenceType
,
4968 FullOriginMetadata aFullOriginMetadata
,
4969 nsTArray
<nsString
>&& aDatabasePaths
);
4971 DirectoryInfo(const DirectoryInfo
& aOther
) = delete;
4972 DirectoryInfo(DirectoryInfo
&& aOther
) = delete;
4974 ~DirectoryInfo() { MOZ_COUNT_DTOR(Maintenance::DirectoryInfo
); }
4978 // Newly created on the PBackground thread. Will proceed immediately or be
4979 // added to the maintenance queue. The next step is either
4980 // DirectoryOpenPending if IndexedDatabaseManager is running, or
4981 // CreateIndexedDatabaseManager if not.
4984 // Create IndexedDatabaseManager on the main thread. The next step is either
4985 // Finishing if IndexedDatabaseManager initialization fails, or
4986 // IndexedDatabaseManagerOpen if initialization succeeds.
4987 CreateIndexedDatabaseManager
,
4989 // Call OpenDirectory() on the PBackground thread. The next step is
4990 // DirectoryOpenPending.
4991 IndexedDatabaseManagerOpen
,
4993 // Waiting for directory open allowed on the PBackground thread. The next
4994 // step is either Finishing if directory lock failed to acquire, or
4995 // DirectoryWorkOpen if directory lock is acquired.
4996 DirectoryOpenPending
,
4998 // Waiting to do/doing work on the QuotaManager IO thread. The next step is
4999 // BeginDatabaseMaintenance.
5002 // Dispatching a runnable for each database on the PBackground thread. The
5003 // next state is either WaitingForDatabaseMaintenancesToComplete if at least
5004 // one runnable has been dispatched, or Finishing otherwise.
5005 BeginDatabaseMaintenance
,
5007 // Waiting for DatabaseMaintenance to finish on maintenance thread pool.
5008 // The next state is Finishing if the last runnable has finished.
5009 WaitingForDatabaseMaintenancesToComplete
,
5011 // Waiting to finish/finishing on the PBackground thread. The next step is
5019 RefPtr
<QuotaClient
> mQuotaClient
;
5021 RefPtr
<UniversalDirectoryLock
> mPendingDirectoryLock
;
5022 RefPtr
<UniversalDirectoryLock
> mDirectoryLock
;
5023 nsTArray
<DirectoryInfo
> mDirectoryInfos
;
5024 nsTHashMap
<nsStringHashKey
, DatabaseMaintenance
*> mDatabaseMaintenances
;
5025 nsresult mResultCode
;
5026 Atomic
<bool> mAborted
;
5030 explicit Maintenance(QuotaClient
* aQuotaClient
)
5031 : Runnable("dom::indexedDB::Maintenance"),
5032 mQuotaClient(aQuotaClient
),
5033 mStartTime(PR_Now()),
5036 mState(State::Initial
) {
5037 AssertIsOnBackgroundThread();
5038 MOZ_ASSERT(aQuotaClient
);
5039 MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient
);
5040 MOZ_ASSERT(mStartTime
);
5043 nsIEventTarget
* BackgroundThread() const {
5044 MOZ_ASSERT(mQuotaClient
);
5045 return mQuotaClient
->BackgroundThread();
5048 PRTime
StartTime() const { return mStartTime
; }
5050 bool IsAborted() const { return mAborted
; }
5052 void RunImmediately() {
5053 MOZ_ASSERT(mState
== State::Initial
);
5055 Unused
<< this->Run();
5059 AssertIsOnBackgroundThread();
5064 void RegisterDatabaseMaintenance(DatabaseMaintenance
* aDatabaseMaintenance
);
5066 void UnregisterDatabaseMaintenance(DatabaseMaintenance
* aDatabaseMaintenance
);
5068 RefPtr
<DatabaseMaintenance
> GetDatabaseMaintenance(
5069 const nsAString
& aDatabasePath
) const {
5070 AssertIsOnBackgroundThread();
5072 return mDatabaseMaintenances
.Get(aDatabasePath
);
5075 void Stringify(nsACString
& aResult
) const;
5078 ~Maintenance() override
{
5079 MOZ_ASSERT(mState
== State::Complete
);
5080 MOZ_ASSERT(!mDatabaseMaintenances
.Count());
5083 // Runs on the PBackground thread. Checks if IndexedDatabaseManager is
5084 // running. Calls OpenDirectory() or dispatches to the main thread on which
5085 // CreateIndexedDatabaseManager() is called.
5088 // Runs on the main thread. Once IndexedDatabaseManager is created it will
5089 // dispatch to the PBackground thread on which OpenDirectory() is called.
5090 nsresult
CreateIndexedDatabaseManager();
5092 // Runs on the PBackground thread. Once QuotaManager has given a lock it will
5093 // call DirectoryOpen().
5094 nsresult
OpenDirectory();
5096 // Runs on the PBackground thread. Dispatches to the QuotaManager I/O thread.
5097 nsresult
DirectoryOpen();
5099 // Runs on the QuotaManager I/O thread. Once it finds databases it will
5100 // dispatch to the PBackground thread on which BeginDatabaseMaintenance()
5102 nsresult
DirectoryWork();
5104 // Runs on the PBackground thread. It dispatches a runnable for each database.
5105 nsresult
BeginDatabaseMaintenance();
5107 // Runs on the PBackground thread. Called when the maintenance is finished or
5108 // if any of above methods fails.
5111 // We need to declare refcounting unconditionally, because
5112 // OpenDirectoryListener has pure-virtual refcounting.
5113 NS_DECL_ISUPPORTS_INHERITED
5117 // OpenDirectoryListener overrides.
5118 void DirectoryLockAcquired(DirectoryLock
* aLock
) override
;
5120 void DirectoryLockFailed() override
;
5123 Maintenance::DirectoryInfo::DirectoryInfo(
5124 PersistenceType aPersistenceType
, FullOriginMetadata aFullOriginMetadata
,
5125 nsTArray
<nsString
>&& aDatabasePaths
)
5126 : mFullOriginMetadata(std::move(aFullOriginMetadata
)),
5127 mDatabasePaths(std::move(aDatabasePaths
)),
5128 mPersistenceType(aPersistenceType
) {
5129 MOZ_ASSERT(aPersistenceType
!= PERSISTENCE_TYPE_INVALID
);
5130 MOZ_ASSERT(!mFullOriginMetadata
->mGroup
.IsEmpty());
5131 MOZ_ASSERT(!mFullOriginMetadata
->mOrigin
.IsEmpty());
5133 MOZ_ASSERT(!mDatabasePaths
->IsEmpty());
5134 for (const nsAString
& databasePath
: *mDatabasePaths
) {
5135 MOZ_ASSERT(!databasePath
.IsEmpty());
5139 MOZ_COUNT_CTOR(Maintenance::DirectoryInfo
);
5142 class DatabaseMaintenance final
: public Runnable
{
5143 // The minimum amount of time that has passed since the last vacuum before we
5144 // will attempt to analyze the database for fragmentation.
5145 static const PRTime kMinVacuumAge
=
5146 PRTime(PR_USEC_PER_SEC
) * 60 * 60 * 24 * 7;
5148 // If the percent of database pages that are not in contiguous order is higher
5149 // than this percentage we will attempt a vacuum.
5150 static const int32_t kPercentUnorderedThreshold
= 30;
5152 // If the percent of file size growth since the last vacuum is higher than
5153 // this percentage we will attempt a vacuum.
5154 static const int32_t kPercentFileSizeGrowthThreshold
= 10;
5156 // The number of freelist pages beyond which we will favor an incremental
5157 // vacuum over a full vacuum.
5158 static const int32_t kMaxFreelistThreshold
= 5;
5160 // If the percent of unused file bytes in the database exceeds this percentage
5161 // then we will attempt a full vacuum.
5162 static const int32_t kPercentUnusedThreshold
= 20;
5164 class AutoProgressHandler
;
5166 enum class MaintenanceAction
{ Nothing
= 0, IncrementalVacuum
, FullVacuum
};
5168 RefPtr
<Maintenance
> mMaintenance
;
5169 RefPtr
<DirectoryLock
> mDirectoryLock
;
5170 const OriginMetadata mOriginMetadata
;
5171 const nsString mDatabasePath
;
5172 int64_t mDirectoryLockId
;
5173 nsCOMPtr
<nsIRunnable
> mCompleteCallback
;
5174 const PersistenceType mPersistenceType
;
5175 const Maybe
<CipherKey
> mMaybeKey
;
5178 DatabaseMaintenance(Maintenance
* aMaintenance
, DirectoryLock
* aDirectoryLock
,
5179 PersistenceType aPersistenceType
,
5180 const OriginMetadata
& aOriginMetadata
,
5181 const nsAString
& aDatabasePath
,
5182 const Maybe
<CipherKey
>& aMaybeKey
)
5183 : Runnable("dom::indexedDB::DatabaseMaintenance"),
5184 mMaintenance(aMaintenance
),
5185 mDirectoryLock(aDirectoryLock
),
5186 mOriginMetadata(aOriginMetadata
),
5187 mDatabasePath(aDatabasePath
),
5188 mPersistenceType(aPersistenceType
),
5189 mMaybeKey
{aMaybeKey
} {
5190 MOZ_ASSERT(aDirectoryLock
);
5192 MOZ_ASSERT(mDirectoryLock
->Id() >= 0);
5193 mDirectoryLockId
= mDirectoryLock
->Id();
5196 const nsAString
& DatabasePath() const { return mDatabasePath
; }
5198 void WaitForCompletion(nsIRunnable
* aCallback
) {
5199 AssertIsOnBackgroundThread();
5200 MOZ_ASSERT(!mCompleteCallback
);
5202 mCompleteCallback
= aCallback
;
5205 void Stringify(nsACString
& aResult
) const;
5208 ~DatabaseMaintenance() override
= default;
5210 // Runs on maintenance thread pool. Does maintenance on the database.
5211 void PerformMaintenanceOnDatabase();
5213 // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
5214 nsresult
CheckIntegrity(mozIStorageConnection
& aConnection
, bool* aOk
);
5216 // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
5217 nsresult
DetermineMaintenanceAction(mozIStorageConnection
& aConnection
,
5218 nsIFile
* aDatabaseFile
,
5219 MaintenanceAction
* aMaintenanceAction
);
5221 // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
5222 void IncrementalVacuum(mozIStorageConnection
& aConnection
);
5224 // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
5225 void FullVacuum(mozIStorageConnection
& aConnection
, nsIFile
* aDatabaseFile
);
5227 // Runs on the PBackground thread. It dispatches a complete callback and
5228 // unregisters from Maintenance.
5229 void RunOnOwningThread();
5231 // Runs on maintenance thread pool. Once it performs database maintenance
5232 // it will dispatch to the PBackground thread on which RunOnOwningThread()
5234 void RunOnConnectionThread();
5239 class MOZ_STACK_CLASS
DatabaseMaintenance::AutoProgressHandler final
5240 : public mozIStorageProgressHandler
{
5241 Maintenance
* mMaintenance
;
5242 LazyInitializedOnce
<const NotNull
<mozIStorageConnection
*>> mConnection
;
5244 NS_DECL_OWNINGTHREAD
5247 // This class is stack-based so we never actually allow AddRef/Release to do
5248 // anything. But we need to know if any consumer *thinks* that they have a
5249 // reference to this object so we track the reference countin DEBUG builds.
5250 nsrefcnt mDEBUGRefCnt
;
5254 explicit AutoProgressHandler(Maintenance
* aMaintenance
)
5255 : mMaintenance(aMaintenance
),
5262 MOZ_ASSERT(!NS_IsMainThread());
5263 MOZ_ASSERT(!IsOnBackgroundThread());
5264 NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler
);
5265 MOZ_ASSERT(aMaintenance
);
5268 ~AutoProgressHandler() {
5269 NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler
);
5275 MOZ_ASSERT(!mDEBUGRefCnt
);
5278 nsresult
Register(NotNull
<mozIStorageConnection
*> aConnection
);
5280 // We don't want the mRefCnt member but this class does not "inherit"
5282 NS_DECL_ISUPPORTS_INHERITED
5287 NS_DECL_MOZISTORAGEPROGRESSHANDLER
5289 // Not available for the heap!
5290 void* operator new(size_t) = delete;
5291 void* operator new[](size_t) = delete;
5292 void operator delete(void*) = delete;
5293 void operator delete[](void*) = delete;
5298 class DEBUGThreadSlower final
: public nsIThreadObserver
{
5300 DEBUGThreadSlower() {
5301 AssertIsOnBackgroundThread();
5302 MOZ_ASSERT(kDEBUGThreadSleepMS
);
5308 ~DEBUGThreadSlower() { AssertIsOnBackgroundThread(); }
5310 NS_DECL_NSITHREADOBSERVER
5315 /*******************************************************************************
5317 ******************************************************************************/
5319 // XXX Get rid of FileHelper and move the functions into DatabaseFileManager.
5320 // Then, DatabaseFileManager::Get(Journal)Directory and
5321 // DatabaseFileManager::GetFileForId might eventually be made private.
5322 class MOZ_STACK_CLASS FileHelper final
{
5323 const SafeRefPtr
<DatabaseFileManager
> mFileManager
;
5325 LazyInitializedOnce
<const NotNull
<nsCOMPtr
<nsIFile
>>> mFileDirectory
;
5326 LazyInitializedOnce
<const NotNull
<nsCOMPtr
<nsIFile
>>> mJournalDirectory
;
5329 LazyInitializedOnce
<const NotNull
<RefPtr
<ReadCallback
>>> mReadCallback
;
5332 explicit FileHelper(SafeRefPtr
<DatabaseFileManager
>&& aFileManager
)
5333 : mFileManager(std::move(aFileManager
)) {
5334 MOZ_ASSERT(mFileManager
);
5339 [[nodiscard
]] nsCOMPtr
<nsIFile
> GetFile(const DatabaseFileInfo
& aFileInfo
);
5341 [[nodiscard
]] nsCOMPtr
<nsIFile
> GetJournalFile(
5342 const DatabaseFileInfo
& aFileInfo
);
5344 nsresult
CreateFileFromStream(nsIFile
& aFile
, nsIFile
& aJournalFile
,
5345 nsIInputStream
& aInputStream
, bool aCompress
,
5346 const Maybe
<CipherKey
>& aMaybeKey
);
5349 nsresult
SyncCopy(nsIInputStream
& aInputStream
,
5350 nsIOutputStream
& aOutputStream
, char* aBuffer
,
5351 uint32_t aBufferSize
);
5353 nsresult
SyncRead(nsIInputStream
& aInputStream
, char* aBuffer
,
5354 uint32_t aBufferSize
, uint32_t* aRead
);
5357 /*******************************************************************************
5359 ******************************************************************************/
5361 bool GetFilenameBase(const nsAString
& aFilename
, const nsAString
& aSuffix
,
5362 nsDependentSubstring
& aFilenameBase
) {
5363 MOZ_ASSERT(!aFilename
.IsEmpty());
5364 MOZ_ASSERT(aFilenameBase
.IsEmpty());
5366 if (!StringEndsWith(aFilename
, aSuffix
) ||
5367 aFilename
.Length() == aSuffix
.Length()) {
5371 MOZ_ASSERT(aFilename
.Length() > aSuffix
.Length());
5373 aFilenameBase
.Rebind(aFilename
, 0, aFilename
.Length() - aSuffix
.Length());
5377 class EncryptedFileBlobImpl final
: public FileBlobImpl
{
5379 EncryptedFileBlobImpl(const nsCOMPtr
<nsIFile
>& aNativeFile
,
5380 const DatabaseFileInfo::IdType aId
,
5381 const CipherKey
& aKey
)
5382 : FileBlobImpl
{aNativeFile
}, mKey
{aKey
} {
5386 uint64_t GetSize(ErrorResult
& aRv
) override
{
5387 nsCOMPtr
<nsIInputStream
> inputStream
;
5388 CreateInputStream(getter_AddRefs(inputStream
), aRv
);
5394 MOZ_ASSERT(inputStream
);
5396 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(inputStream
, Available
), 0,
5397 [&aRv
](const nsresult rv
) { aRv
= rv
; });
5400 void CreateInputStream(nsIInputStream
** aInputStream
,
5401 ErrorResult
& aRv
) const override
{
5402 nsCOMPtr
<nsIInputStream
> baseInputStream
;
5403 FileBlobImpl::CreateInputStream(getter_AddRefs(baseInputStream
), aRv
);
5404 if (NS_WARN_IF(aRv
.Failed())) {
5409 MakeAndAddRef
<DecryptingInputStream
<IndexedDBCipherStrategy
>>(
5410 WrapNotNull(std::move(baseInputStream
)), kEncryptedStreamBlockSize
,
5415 void GetBlobImplType(nsAString
& aBlobImplType
) const override
{
5416 aBlobImplType
= u
"EncryptedFileBlobImpl"_ns
;
5419 already_AddRefed
<BlobImpl
> CreateSlice(uint64_t aStart
, uint64_t aLength
,
5420 const nsAString
& aContentType
,
5421 ErrorResult
& aRv
) const override
{
5422 MOZ_CRASH("Not implemented because this should be unreachable.");
5426 const CipherKey mKey
;
5429 RefPtr
<BlobImpl
> CreateFileBlobImpl(const Database
& aDatabase
,
5430 const nsCOMPtr
<nsIFile
>& aNativeFile
,
5431 const DatabaseFileInfo::IdType aId
) {
5432 if (aDatabase
.IsInPrivateBrowsing()) {
5433 nsCString cipherKeyId
;
5434 cipherKeyId
.AppendInt(aId
);
5437 gIndexedDBCipherKeyManager
->Get(aDatabase
.Id(), cipherKeyId
);
5439 MOZ_RELEASE_ASSERT(key
.isSome());
5440 return MakeRefPtr
<EncryptedFileBlobImpl
>(aNativeFile
, aId
, *key
);
5443 auto impl
= MakeRefPtr
<FileBlobImpl
>(aNativeFile
);
5444 impl
->SetFileId(aId
);
5449 Result
<nsTArray
<SerializedStructuredCloneFile
>, nsresult
>
5450 SerializeStructuredCloneFiles(const SafeRefPtr
<Database
>& aDatabase
,
5451 const nsTArray
<StructuredCloneFileParent
>& aFiles
,
5452 bool aForPreprocess
) {
5453 AssertIsOnBackgroundThread();
5454 MOZ_ASSERT(aDatabase
);
5456 if (aFiles
.IsEmpty()) {
5457 return nsTArray
<SerializedStructuredCloneFile
>{};
5460 const nsCOMPtr
<nsIFile
> directory
=
5461 aDatabase
->GetFileManager().GetCheckedDirectory();
5462 QM_TRY(OkIf(directory
), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
),
5463 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
5465 nsTArray
<SerializedStructuredCloneFile
> serializedStructuredCloneFiles
;
5466 QM_TRY(OkIf(serializedStructuredCloneFiles
.SetCapacity(aFiles
.Length(),
5468 Err(NS_ERROR_OUT_OF_MEMORY
));
5470 QM_TRY(TransformIfAbortOnErr(
5471 aFiles
, MakeBackInserter(serializedStructuredCloneFiles
),
5472 [aForPreprocess
](const auto& file
) {
5473 return !aForPreprocess
||
5474 file
.Type() == StructuredCloneFileBase::eStructuredClone
;
5476 [&directory
, &aDatabase
, aForPreprocess
](
5477 const auto& file
) -> Result
<SerializedStructuredCloneFile
, nsresult
> {
5478 const int64_t fileId
= file
.FileInfo().Id();
5479 MOZ_ASSERT(fileId
> 0);
5481 const nsCOMPtr
<nsIFile
> nativeFile
=
5482 mozilla::dom::indexedDB::DatabaseFileManager::GetCheckedFileForId(
5484 QM_TRY(OkIf(nativeFile
), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
),
5485 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
5487 switch (file
.Type()) {
5488 case StructuredCloneFileBase::eStructuredClone
:
5489 if (!aForPreprocess
) {
5490 return SerializedStructuredCloneFile
{
5491 null_t(), StructuredCloneFileBase::eStructuredClone
};
5496 case StructuredCloneFileBase::eBlob
: {
5497 const auto impl
= CreateFileBlobImpl(*aDatabase
, nativeFile
,
5498 file
.FileInfo().Id());
5502 // This can only fail if the child has crashed.
5503 QM_TRY(MOZ_TO_RESULT(IPCBlobUtils::Serialize(impl
, ipcBlob
)),
5504 Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
),
5505 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
5507 aDatabase
->MapBlob(ipcBlob
, file
.FileInfoPtr());
5509 return SerializedStructuredCloneFile
{ipcBlob
, file
.Type()};
5512 case StructuredCloneFileBase::eMutableFile
:
5513 case StructuredCloneFileBase::eWasmBytecode
:
5514 case StructuredCloneFileBase::eWasmCompiled
: {
5515 // Set file() to null, support for storing WebAssembly.Modules has
5516 // been removed in bug 1469395. Support for de-serialization of
5517 // WebAssembly.Modules modules has been removed in bug 1561876.
5518 // Support for MutableFile has been removed in bug 1500343. Full
5519 // removal is tracked in bug 1487479.
5521 return SerializedStructuredCloneFile
{null_t(), file
.Type()};
5525 MOZ_CRASH("Should never get here!");
5529 return std::move(serializedStructuredCloneFiles
);
5532 bool IsFileNotFoundError(const nsresult aRv
) {
5533 return aRv
== NS_ERROR_FILE_NOT_FOUND
;
5536 enum struct Idempotency
{ Yes
, No
};
5538 // Delete a file, decreasing the quota usage as appropriate. If the file no
5539 // longer exists but aIdempotency is Idempotency::Yes, success is returned,
5540 // although quota usage can't be decreased. (With the assumption being that the
5541 // file was already deleted prior to this logic running, and the non-existent
5542 // file was no longer tracked by quota because it didn't exist at
5543 // initialization time or a previous deletion call updated the usage.)
5544 nsresult
DeleteFile(nsIFile
& aFile
, QuotaManager
* const aQuotaManager
,
5545 const PersistenceType aPersistenceType
,
5546 const OriginMetadata
& aOriginMetadata
,
5547 const Idempotency aIdempotency
) {
5548 MOZ_ASSERT(!NS_IsMainThread());
5549 MOZ_ASSERT(!IsOnBackgroundThread());
5551 // Callers which pass Idempotency::Yes call this function without checking if
5552 // the file already exists (idempotent usage). QM_OR_ELSE_WARN_IF is not used
5553 // here since we just want to log NS_ERROR_FILE_NOT_FOUND results and not spam
5555 // Theoretically, there should be no QM_OR_ELSE_(WARN|LOG_VERBOSE)_IF when a
5556 // caller passes Idempotency::No, but it's simpler when the predicate just
5557 // always returns false in that case.
5559 const auto isIgnorableError
= [&aIdempotency
]() -> bool (*)(nsresult
) {
5560 if (aIdempotency
== Idempotency::Yes
) {
5561 return IsFileNotFoundError
;
5564 return [](const nsresult rv
) { return false; };
5568 const auto& fileSize
,
5569 ([aQuotaManager
, &aFile
,
5570 isIgnorableError
]() -> Result
<Maybe
<int64_t>, nsresult
> {
5571 if (aQuotaManager
) {
5573 const Maybe
<int64_t>& fileSize
,
5574 QM_OR_ELSE_LOG_VERBOSE_IF(
5576 MOZ_TO_RESULT_INVOKE_MEMBER(aFile
, GetFileSize
)
5577 .map([](const int64_t val
) { return Some(val
); }),
5581 ErrToDefaultOk
<Maybe
<int64_t>>));
5583 // XXX Can we really assert that the file size is not 0 if
5584 // it existed? This might be violated by external
5586 MOZ_ASSERT(!fileSize
|| fileSize
.value() >= 0);
5591 return Some(int64_t(0));
5598 QM_TRY_INSPECT(const auto& didExist
,
5599 QM_OR_ELSE_LOG_VERBOSE_IF(
5601 MOZ_TO_RESULT(aFile
.Remove(false)).map(Some
<Ok
>),
5605 ErrToDefaultOk
<Maybe
<Ok
>>));
5608 // XXX If we get here, this means that the file still existed when we
5609 // queried its size, but no longer when we tried to remove it. Not sure if
5610 // this should really be silently accepted in idempotent mode.
5614 if (fileSize
.value() > 0) {
5615 MOZ_ASSERT(aQuotaManager
);
5617 aQuotaManager
->DecreaseUsageForClient(
5618 ClientMetadata
{aOriginMetadata
, Client::IDB
}, fileSize
.value());
5624 nsresult
DeleteFile(nsIFile
& aDirectory
, const nsAString
& aFilename
,
5625 QuotaManager
* const aQuotaManager
,
5626 const PersistenceType aPersistenceType
,
5627 const OriginMetadata
& aOriginMetadata
,
5628 const Idempotency aIdempotent
) {
5629 AssertIsOnIOThread();
5630 MOZ_ASSERT(!aFilename
.IsEmpty());
5632 QM_TRY_INSPECT(const auto& file
, CloneFileAndAppend(aDirectory
, aFilename
));
5634 return DeleteFile(*file
, aQuotaManager
, aPersistenceType
, aOriginMetadata
,
5638 // Delete files in a directory that you think exists. If the directory doesn't
5639 // exist, an error will not be returned, but warning telemetry will be
5640 // generated! So only call this on directories that you know exist (idempotent
5641 // usage, but it's not recommended).
5642 nsresult
DeleteFilesNoQuota(nsIFile
& aFile
) {
5643 AssertIsOnIOThread();
5645 QM_TRY_INSPECT(const auto& didExist
,
5648 MOZ_TO_RESULT(aFile
.Remove(true)).map(Some
<Ok
>),
5650 IsFileNotFoundError
,
5652 ErrToDefaultOk
<Maybe
<Ok
>>));
5659 nsresult
DeleteFilesNoQuota(nsIFile
* aDirectory
, const nsAString
& aFilename
) {
5660 AssertIsOnIOThread();
5661 MOZ_ASSERT(aDirectory
);
5662 MOZ_ASSERT(!aFilename
.IsEmpty());
5664 // The current using function hasn't initialized the origin, so in here we
5665 // don't update the size of origin. Adding this assertion for preventing from
5667 DebugOnly
<QuotaManager
*> quotaManager
= QuotaManager::Get();
5668 MOZ_ASSERT(!quotaManager
->IsTemporaryStorageInitialized());
5670 QM_TRY_INSPECT(const auto& file
, CloneFileAndAppend(*aDirectory
, aFilename
));
5672 QM_TRY(MOZ_TO_RESULT(DeleteFilesNoQuota(*file
)));
5677 // CreateMarkerFile and RemoveMarkerFile are a pair of functions to indicate
5678 // whether having removed all the files successfully. The marker file should
5679 // be checked before executing the next operation or initialization.
5680 Result
<nsCOMPtr
<nsIFile
>, nsresult
> CreateMarkerFile(
5681 nsIFile
& aBaseDirectory
, const nsAString
& aDatabaseNameBase
) {
5682 AssertIsOnIOThread();
5683 MOZ_ASSERT(!aDatabaseNameBase
.IsEmpty());
5686 const auto& markerFile
,
5687 CloneFileAndAppend(aBaseDirectory
,
5688 kIdbDeletionMarkerFilePrefix
+ aDatabaseNameBase
));
5690 // Callers call this function without checking if the file already exists
5691 // (idempotent usage). QM_OR_ELSE_WARN_IF is not used here since we just want
5692 // to log NS_ERROR_FILE_ALREADY_EXISTS result and not spam the reports.
5694 // TODO: In theory if this file exists, then RemoveDatabaseFilesAndDirectory
5695 // should have cleaned it up, but obviously we can crash and not clean it up,
5696 // which is the whole point of the marker file. In that case, we'll realize
5697 // the marker file exists in OpenDatabaseOp::DoDatabaseWork or
5698 // GetUsageForOriginInternal and resume the removal by calling
5699 // RemoveDatabaseFilesAndDirectory again, but we will also try to create the
5700 // marker file again, so if we see this marker file, it is part
5701 // of our standard operating procedure to redundantly try and create the
5702 // marker here. We currently treat this as idempotent usage, but we could
5703 // add an additional argument to RemoveDatabaseFilesAndDirectory which would
5704 // indicate that we are resuming an unfinished removal, so the marker already
5705 // exists and doesn't have to be created, and change
5706 // QM_OR_ELSE_LOG_VERBOSE_IF to QM_OR_ELSE_WARN_IF in the end.
5707 QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF(
5709 MOZ_TO_RESULT(markerFile
->Create(nsIFile::NORMAL_FILE_TYPE
, 0644)),
5711 IsSpecificError
<NS_ERROR_FILE_ALREADY_EXISTS
>,
5718 nsresult
RemoveMarkerFile(nsIFile
* aMarkerFile
) {
5719 AssertIsOnIOThread();
5720 MOZ_ASSERT(aMarkerFile
);
5722 DebugOnly
<bool> exists
;
5723 MOZ_ASSERT(NS_SUCCEEDED(aMarkerFile
->Exists(&exists
)));
5726 QM_TRY(MOZ_TO_RESULT(aMarkerFile
->Remove(false)));
5731 Result
<Ok
, nsresult
> DeleteFileManagerDirectory(
5732 nsIFile
& aFileManagerDirectory
, QuotaManager
* aQuotaManager
,
5733 const PersistenceType aPersistenceType
,
5734 const OriginMetadata
& aOriginMetadata
) {
5735 // XXX In theory, deleting can continue for other files in case of a failure,
5736 // leaving only those files behind that cause the problem actually. However,
5737 // the current architecture doesn't allow having more databases (for the same
5738 // name) on disk, so trying to delete as much as possible won't help much
5739 // because we need to delete entire .files directory in the end anyway.
5740 QM_TRY(DatabaseFileManager::TraverseFiles(
5741 aFileManagerDirectory
,
5743 [&aQuotaManager
, aPersistenceType
, &aOriginMetadata
](
5744 nsIFile
& file
, const bool isDirectory
) -> Result
<Ok
, nsresult
> {
5746 // The journal directory doesn't count towards quota.
5747 QM_TRY_RETURN(MOZ_TO_RESULT(DeleteFilesNoQuota(file
)));
5750 // Stored files do count towards quota.
5752 MOZ_TO_RESULT(DeleteFile(file
, aQuotaManager
, aPersistenceType
,
5753 aOriginMetadata
, Idempotency::Yes
)));
5755 // UnknownDirEntryOp
5756 [aPersistenceType
, &aOriginMetadata
](
5757 nsIFile
& file
, const bool isDirectory
) -> Result
<Ok
, nsresult
> {
5758 // Unknown files and directories don't count towards quota.
5761 QM_TRY_RETURN(MOZ_TO_RESULT(DeleteFilesNoQuota(file
)));
5764 QM_TRY_RETURN(MOZ_TO_RESULT(
5765 DeleteFile(file
, /* doesn't count */ nullptr, aPersistenceType
,
5766 aOriginMetadata
, Idempotency::Yes
)));
5769 QM_TRY_RETURN(MOZ_TO_RESULT(aFileManagerDirectory
.Remove(false)));
5772 // Idempotently delete all the parts of an IndexedDB database including its
5773 // SQLite database file, its WAL journal, it's shared-memory file, and its
5774 // Blob/Files sub-directory. A marker file is created prior to performing the
5775 // deletion so that in the event we crash or fail to successfully delete the
5776 // database and its files, we will re-attempt the deletion the next time the
5777 // origin is initialized using this method. Because this means the method may be
5778 // called on a partially deleted database, this method uses DeleteFile which
5779 // succeeds when the file we ask it to delete does not actually exist. The
5780 // marker file is removed once deletion has successfully completed.
5781 nsresult
RemoveDatabaseFilesAndDirectory(nsIFile
& aBaseDirectory
,
5782 const nsAString
& aDatabaseFilenameBase
,
5783 QuotaManager
* aQuotaManager
,
5784 const PersistenceType aPersistenceType
,
5785 const OriginMetadata
& aOriginMetadata
,
5786 const nsAString
& aDatabaseName
) {
5787 AssertIsOnIOThread();
5788 MOZ_ASSERT(!aDatabaseFilenameBase
.IsEmpty());
5790 AUTO_PROFILER_LABEL("RemoveDatabaseFilesAndDirectory", DOM
);
5792 QM_TRY_UNWRAP(auto markerFile
,
5793 CreateMarkerFile(aBaseDirectory
, aDatabaseFilenameBase
));
5795 // The database file counts towards quota.
5796 QM_TRY(MOZ_TO_RESULT(DeleteFile(
5797 aBaseDirectory
, aDatabaseFilenameBase
+ kSQLiteSuffix
, aQuotaManager
,
5798 aPersistenceType
, aOriginMetadata
, Idempotency::Yes
)));
5800 // .sqlite-journal files don't count towards quota.
5801 QM_TRY(MOZ_TO_RESULT(DeleteFile(aBaseDirectory
,
5802 aDatabaseFilenameBase
+ kSQLiteJournalSuffix
,
5803 /* doesn't count */ nullptr, aPersistenceType
,
5804 aOriginMetadata
, Idempotency::Yes
)));
5806 // .sqlite-shm files don't count towards quota.
5807 QM_TRY(MOZ_TO_RESULT(DeleteFile(aBaseDirectory
,
5808 aDatabaseFilenameBase
+ kSQLiteSHMSuffix
,
5809 /* doesn't count */ nullptr, aPersistenceType
,
5810 aOriginMetadata
, Idempotency::Yes
)));
5812 // .sqlite-wal files do count towards quota.
5813 QM_TRY(MOZ_TO_RESULT(DeleteFile(
5814 aBaseDirectory
, aDatabaseFilenameBase
+ kSQLiteWALSuffix
, aQuotaManager
,
5815 aPersistenceType
, aOriginMetadata
, Idempotency::Yes
)));
5817 // The files directory counts towards quota.
5819 const auto& fmDirectory
,
5820 CloneFileAndAppend(aBaseDirectory
, aDatabaseFilenameBase
+
5821 kFileManagerDirectoryNameSuffix
));
5823 QM_TRY_INSPECT(const bool& exists
,
5824 MOZ_TO_RESULT_INVOKE_MEMBER(fmDirectory
, Exists
));
5827 QM_TRY_INSPECT(const bool& isDirectory
,
5828 MOZ_TO_RESULT_INVOKE_MEMBER(fmDirectory
, IsDirectory
));
5830 QM_TRY(OkIf(isDirectory
), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
5832 QM_TRY(DeleteFileManagerDirectory(*fmDirectory
, aQuotaManager
,
5833 aPersistenceType
, aOriginMetadata
));
5836 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
5837 MOZ_ASSERT_IF(aQuotaManager
, mgr
);
5840 mgr
->InvalidateFileManager(aPersistenceType
, aOriginMetadata
.mOrigin
,
5844 QM_TRY(MOZ_TO_RESULT(RemoveMarkerFile(markerFile
)));
5849 /*******************************************************************************
5851 ******************************************************************************/
5853 // Counts the number of "live" Factory, FactoryOp and Database instances.
5854 uint64_t gBusyCount
= 0;
5856 using FactoryOpArray
= nsTArray
<CheckedUnsafePtr
<FactoryOp
>>;
5858 StaticAutoPtr
<FactoryOpArray
> gFactoryOps
;
5860 // Maps a database id to information about live database actors.
5861 using DatabaseActorHashtable
=
5862 nsClassHashtable
<nsCStringHashKey
, DatabaseActorInfo
>;
5864 StaticAutoPtr
<DatabaseActorHashtable
> gLiveDatabaseHashtable
;
5866 StaticRefPtr
<ConnectionPool
> gConnectionPool
;
5868 using DatabaseLoggingInfoHashtable
=
5869 nsTHashMap
<nsIDHashKey
, DatabaseLoggingInfo
*>;
5871 StaticAutoPtr
<DatabaseLoggingInfoHashtable
> gLoggingInfoHashtable
;
5873 using TelemetryIdHashtable
= nsTHashMap
<nsUint32HashKey
, uint32_t>;
5875 StaticAutoPtr
<TelemetryIdHashtable
> gTelemetryIdHashtable
;
5877 // Protects all reads and writes to gTelemetryIdHashtable.
5878 StaticAutoPtr
<Mutex
> gTelemetryIdMutex
;
5882 StaticRefPtr
<DEBUGThreadSlower
> gDEBUGThreadSlower
;
5886 void IncreaseBusyCount() {
5887 AssertIsOnBackgroundThread();
5889 // If this is the first instance then we need to do some initialization.
5891 MOZ_ASSERT(!gFactoryOps
);
5892 gFactoryOps
= new FactoryOpArray();
5894 MOZ_ASSERT(!gLiveDatabaseHashtable
);
5895 gLiveDatabaseHashtable
= new DatabaseActorHashtable();
5897 MOZ_ASSERT(!gIndexedDBCipherKeyManager
);
5898 gIndexedDBCipherKeyManager
= new IndexedDBCipherKeyManager();
5900 MOZ_ASSERT(!gLoggingInfoHashtable
);
5901 gLoggingInfoHashtable
= new DatabaseLoggingInfoHashtable();
5904 if (kDEBUGThreadPriority
!= nsISupportsPriority::PRIORITY_NORMAL
) {
5906 "PBackground thread debugging enabled, priority has been "
5908 nsCOMPtr
<nsISupportsPriority
> thread
=
5909 do_QueryInterface(NS_GetCurrentThread());
5912 MOZ_ALWAYS_SUCCEEDS(thread
->SetPriority(kDEBUGThreadPriority
));
5915 if (kDEBUGThreadSleepMS
) {
5917 "PBackground thread debugging enabled, sleeping after every "
5919 nsCOMPtr
<nsIThreadInternal
> thread
=
5920 do_QueryInterface(NS_GetCurrentThread());
5923 gDEBUGThreadSlower
= new DEBUGThreadSlower();
5925 MOZ_ALWAYS_SUCCEEDS(thread
->AddObserver(gDEBUGThreadSlower
));
5933 void DecreaseBusyCount() {
5934 AssertIsOnBackgroundThread();
5935 MOZ_ASSERT(gBusyCount
);
5937 // Clean up if there are no more instances.
5938 if (--gBusyCount
== 0) {
5939 MOZ_ASSERT(gLoggingInfoHashtable
);
5940 gLoggingInfoHashtable
= nullptr;
5942 MOZ_ASSERT(gLiveDatabaseHashtable
);
5943 MOZ_ASSERT(!gLiveDatabaseHashtable
->Count());
5944 gLiveDatabaseHashtable
= nullptr;
5946 MOZ_ASSERT(gIndexedDBCipherKeyManager
);
5947 // XXX After we add the private browsing session end listener, we can assert
5949 gIndexedDBCipherKeyManager
= nullptr;
5951 MOZ_ASSERT(gFactoryOps
);
5952 MOZ_ASSERT(gFactoryOps
->IsEmpty());
5953 gFactoryOps
= nullptr;
5956 if (kDEBUGThreadPriority
!= nsISupportsPriority::PRIORITY_NORMAL
) {
5957 nsCOMPtr
<nsISupportsPriority
> thread
=
5958 do_QueryInterface(NS_GetCurrentThread());
5961 MOZ_ALWAYS_SUCCEEDS(
5962 thread
->SetPriority(nsISupportsPriority::PRIORITY_NORMAL
));
5965 if (kDEBUGThreadSleepMS
) {
5966 MOZ_ASSERT(gDEBUGThreadSlower
);
5968 nsCOMPtr
<nsIThreadInternal
> thread
=
5969 do_QueryInterface(NS_GetCurrentThread());
5972 MOZ_ALWAYS_SUCCEEDS(thread
->RemoveObserver(gDEBUGThreadSlower
));
5974 gDEBUGThreadSlower
= nullptr;
5980 template <typename Condition
>
5981 void InvalidateLiveDatabasesMatching(const Condition
& aCondition
) {
5982 AssertIsOnBackgroundThread();
5984 if (!gLiveDatabaseHashtable
) {
5988 // Invalidating a Database will cause it to be removed from the
5989 // gLiveDatabaseHashtable entries' mLiveDatabases, and, if it was the last
5990 // element in mLiveDatabases, to remove the whole hashtable entry. Therefore,
5991 // we need to make a temporary list of the databases to invalidate to avoid
5992 // iterator invalidation.
5994 nsTArray
<SafeRefPtr
<Database
>> databases
;
5996 for (const auto& liveDatabasesEntry
: gLiveDatabaseHashtable
->Values()) {
5997 for (const auto& database
: liveDatabasesEntry
->mLiveDatabases
) {
5998 if (aCondition(*database
)) {
5999 databases
.AppendElement(
6000 SafeRefPtr
{database
.get(), AcquireStrongRefFromRawPtr
{}});
6005 for (const auto& database
: databases
) {
6006 database
->Invalidate();
6010 uint32_t TelemetryIdForFile(nsIFile
* aFile
) {
6011 // May be called on any thread!
6014 MOZ_ASSERT(gTelemetryIdMutex
);
6016 // The storage directory is structured like this:
6018 // <profile>/storage/<persistence>/<origin>/idb/<filename>.sqlite
6020 // For the purposes of this function we're only concerned with the
6021 // <persistence>, <origin>, and <filename> pieces.
6024 MOZ_ALWAYS_SUCCEEDS(aFile
->GetLeafName(filename
));
6026 // Make sure we were given a database file.
6027 MOZ_ASSERT(StringEndsWith(filename
, kSQLiteSuffix
));
6029 filename
.Truncate(filename
.Length() - kSQLiteSuffix
.Length());
6031 // Get the "idb" directory.
6032 nsCOMPtr
<nsIFile
> idbDirectory
;
6033 MOZ_ALWAYS_SUCCEEDS(aFile
->GetParent(getter_AddRefs(idbDirectory
)));
6035 DebugOnly
<nsString
> idbLeafName
;
6036 MOZ_ASSERT(NS_SUCCEEDED(idbDirectory
->GetLeafName(idbLeafName
)));
6037 MOZ_ASSERT(static_cast<nsString
&>(idbLeafName
).EqualsLiteral("idb"));
6039 // Get the <origin> directory.
6040 nsCOMPtr
<nsIFile
> originDirectory
;
6041 MOZ_ALWAYS_SUCCEEDS(idbDirectory
->GetParent(getter_AddRefs(originDirectory
)));
6044 MOZ_ALWAYS_SUCCEEDS(originDirectory
->GetLeafName(origin
));
6046 // Any databases in these directories are owned by the application and should
6047 // not have their filenames masked. Hopefully they also appear in the
6048 // Telemetry.cpp whitelist.
6049 if (origin
.EqualsLiteral("chrome") ||
6050 origin
.EqualsLiteral("moz-safe-about+home")) {
6054 // Get the <persistence> directory.
6055 nsCOMPtr
<nsIFile
> persistenceDirectory
;
6056 MOZ_ALWAYS_SUCCEEDS(
6057 originDirectory
->GetParent(getter_AddRefs(persistenceDirectory
)));
6059 nsString persistence
;
6060 MOZ_ALWAYS_SUCCEEDS(persistenceDirectory
->GetLeafName(persistence
));
6062 constexpr auto separator
= u
"*"_ns
;
6064 uint32_t hashValue
=
6065 HashString(persistence
+ separator
+ origin
+ separator
+ filename
);
6067 MutexAutoLock
lock(*gTelemetryIdMutex
);
6069 if (!gTelemetryIdHashtable
) {
6070 gTelemetryIdHashtable
= new TelemetryIdHashtable();
6073 return gTelemetryIdHashtable
->LookupOrInsertWith(hashValue
, [] {
6074 static uint32_t sNextId
= 1;
6076 // We're locked, no need for atomics.
6081 const CommonIndexOpenCursorParams
& GetCommonIndexOpenCursorParams(
6082 const OpenCursorParams
& aParams
) {
6083 switch (aParams
.type()) {
6084 case OpenCursorParams::TIndexOpenCursorParams
:
6085 return aParams
.get_IndexOpenCursorParams().commonIndexParams();
6086 case OpenCursorParams::TIndexOpenKeyCursorParams
:
6087 return aParams
.get_IndexOpenKeyCursorParams().commonIndexParams();
6089 MOZ_CRASH("Should never get here!");
6093 const CommonOpenCursorParams
& GetCommonOpenCursorParams(
6094 const OpenCursorParams
& aParams
) {
6095 switch (aParams
.type()) {
6096 case OpenCursorParams::TObjectStoreOpenCursorParams
:
6097 return aParams
.get_ObjectStoreOpenCursorParams().commonParams();
6098 case OpenCursorParams::TObjectStoreOpenKeyCursorParams
:
6099 return aParams
.get_ObjectStoreOpenKeyCursorParams().commonParams();
6100 case OpenCursorParams::TIndexOpenCursorParams
:
6101 case OpenCursorParams::TIndexOpenKeyCursorParams
:
6102 return GetCommonIndexOpenCursorParams(aParams
).commonParams();
6104 MOZ_CRASH("Should never get here!");
6108 // TODO: Using nsCString as a return type here seems to lead to a dependency on
6109 // some temporaries, which I did not expect. Is it a good idea that the default
6110 // operator+ behaviour constructs such strings? It is certainly useful as an
6111 // optimization, but this should be better done via an appropriately named
6112 // function rather than an operator.
6113 nsAutoCString
MakeColumnPairSelectionList(
6114 const nsLiteralCString
& aPlainColumnName
,
6115 const nsLiteralCString
& aLocaleAwareColumnName
,
6116 const nsLiteralCString
& aSortColumnAlias
, const bool aIsLocaleAware
) {
6117 return aPlainColumnName
+
6118 (aIsLocaleAware
? EmptyCString() : " as "_ns
+ aSortColumnAlias
) +
6119 ", "_ns
+ aLocaleAwareColumnName
+
6120 (aIsLocaleAware
? " as "_ns
+ aSortColumnAlias
: EmptyCString());
6123 constexpr bool IsIncreasingOrder(const IDBCursorDirection aDirection
) {
6124 MOZ_ASSERT(aDirection
== IDBCursorDirection::Next
||
6125 aDirection
== IDBCursorDirection::Nextunique
||
6126 aDirection
== IDBCursorDirection::Prev
||
6127 aDirection
== IDBCursorDirection::Prevunique
);
6129 return aDirection
== IDBCursorDirection::Next
||
6130 aDirection
== IDBCursorDirection::Nextunique
;
6133 constexpr bool IsUnique(const IDBCursorDirection aDirection
) {
6134 MOZ_ASSERT(aDirection
== IDBCursorDirection::Next
||
6135 aDirection
== IDBCursorDirection::Nextunique
||
6136 aDirection
== IDBCursorDirection::Prev
||
6137 aDirection
== IDBCursorDirection::Prevunique
);
6139 return aDirection
== IDBCursorDirection::Nextunique
||
6140 aDirection
== IDBCursorDirection::Prevunique
;
6143 // TODO: In principle, this could be constexpr, if operator+(nsLiteralCString,
6144 // nsLiteralCString) were constexpr and returned a literal type.
6145 nsAutoCString
MakeDirectionClause(const IDBCursorDirection aDirection
) {
6146 return " ORDER BY "_ns
+ kColumnNameKey
+
6147 (IsIncreasingOrder(aDirection
) ? " ASC"_ns
: " DESC"_ns
);
6150 enum struct ComparisonOperator
{
6158 constexpr nsLiteralCString
GetComparisonOperatorString(
6159 const ComparisonOperator aComparisonOperator
) {
6160 switch (aComparisonOperator
) {
6161 case ComparisonOperator::LessThan
:
6163 case ComparisonOperator::LessOrEquals
:
6165 case ComparisonOperator::Equals
:
6167 case ComparisonOperator::GreaterThan
:
6169 case ComparisonOperator::GreaterOrEquals
:
6173 // TODO: This is just to silence the "control reaches end of non-void
6174 // function" warning. Cannot use MOZ_CRASH in a constexpr function,
6179 nsAutoCString
GetKeyClause(const nsACString
& aColumnName
,
6180 const ComparisonOperator aComparisonOperator
,
6181 const nsLiteralCString
& aStmtParamName
) {
6182 return aColumnName
+ " "_ns
+
6183 GetComparisonOperatorString(aComparisonOperator
) + " :"_ns
+
6187 nsAutoCString
GetSortKeyClause(const ComparisonOperator aComparisonOperator
,
6188 const nsLiteralCString
& aStmtParamName
) {
6189 return GetKeyClause(kColumnNameAliasSortKey
, aComparisonOperator
,
6193 template <IDBCursorType CursorType
>
6194 struct PopulateResponseHelper
;
6196 struct CommonPopulateResponseHelper
{
6197 explicit CommonPopulateResponseHelper(
6198 const TransactionDatabaseOperationBase
& aOp
)
6201 nsresult
GetKeys(mozIStorageStatement
* const aStmt
,
6202 Key
* const aOptOutSortKey
) {
6203 QM_TRY(MOZ_TO_RESULT(GetCommonKeys(aStmt
)));
6205 if (aOptOutSortKey
) {
6206 *aOptOutSortKey
= mPosition
;
6212 nsresult
GetCommonKeys(mozIStorageStatement
* const aStmt
) {
6213 MOZ_ASSERT(mPosition
.IsUnset());
6215 QM_TRY(MOZ_TO_RESULT(mPosition
.SetFromStatement(aStmt
, 0)));
6217 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
6218 "PRELOAD: Populating response with key %s", "Populating%.0s",
6219 IDB_LOG_ID_STRING(mOp
.BackgroundChildLoggingId()),
6220 mOp
.TransactionLoggingSerialNumber(), mOp
.LoggingSerialNumber(),
6221 mPosition
.GetBuffer().get());
6226 template <typename Response
>
6227 void FillKeys(Response
& aResponse
) {
6228 MOZ_ASSERT(!mPosition
.IsUnset());
6229 aResponse
.key() = std::move(mPosition
);
6232 template <typename Response
>
6233 static size_t GetKeySize(const Response
& aResponse
) {
6234 return aResponse
.key().GetBuffer().Length();
6238 const Key
& GetPosition() const { return mPosition
; }
6241 const TransactionDatabaseOperationBase
& mOp
;
6245 struct IndexPopulateResponseHelper
: CommonPopulateResponseHelper
{
6246 using CommonPopulateResponseHelper::CommonPopulateResponseHelper
;
6248 nsresult
GetKeys(mozIStorageStatement
* const aStmt
,
6249 Key
* const aOptOutSortKey
) {
6250 MOZ_ASSERT(mLocaleAwarePosition
.IsUnset());
6251 MOZ_ASSERT(mObjectStorePosition
.IsUnset());
6253 QM_TRY(MOZ_TO_RESULT(CommonPopulateResponseHelper::GetCommonKeys(aStmt
)));
6255 QM_TRY(MOZ_TO_RESULT(mLocaleAwarePosition
.SetFromStatement(aStmt
, 1)));
6257 QM_TRY(MOZ_TO_RESULT(mObjectStorePosition
.SetFromStatement(aStmt
, 2)));
6259 if (aOptOutSortKey
) {
6261 mLocaleAwarePosition
.IsUnset() ? GetPosition() : mLocaleAwarePosition
;
6267 template <typename Response
>
6268 void FillKeys(Response
& aResponse
) {
6269 MOZ_ASSERT(!mLocaleAwarePosition
.IsUnset());
6270 MOZ_ASSERT(!mObjectStorePosition
.IsUnset());
6272 CommonPopulateResponseHelper::FillKeys(aResponse
);
6273 aResponse
.sortKey() = std::move(mLocaleAwarePosition
);
6274 aResponse
.objectKey() = std::move(mObjectStorePosition
);
6277 template <typename Response
>
6278 static size_t GetKeySize(Response
& aResponse
) {
6279 return CommonPopulateResponseHelper::GetKeySize(aResponse
) +
6280 aResponse
.sortKey().GetBuffer().Length() +
6281 aResponse
.objectKey().GetBuffer().Length();
6285 Key mLocaleAwarePosition
, mObjectStorePosition
;
6288 struct KeyPopulateResponseHelper
{
6289 static constexpr nsresult
MaybeGetCloneInfo(
6290 mozIStorageStatement
* const /*aStmt*/, const CursorBase
& /*aCursor*/) {
6294 template <typename Response
>
6295 static constexpr void MaybeFillCloneInfo(Response
& /*aResponse*/,
6296 FilesArray
* const /*aFiles*/) {}
6298 template <typename Response
>
6299 static constexpr size_t MaybeGetCloneInfoSize(const Response
& /*aResponse*/) {
6304 template <bool StatementHasIndexKeyBindings
>
6305 struct ValuePopulateResponseHelper
{
6306 nsresult
MaybeGetCloneInfo(mozIStorageStatement
* const aStmt
,
6307 const ValueCursorBase
& aCursor
) {
6308 constexpr auto offset
= StatementHasIndexKeyBindings
? 2 : 0;
6310 QM_TRY_UNWRAP(auto cloneInfo
,
6311 GetStructuredCloneReadInfoFromStatement(
6312 aStmt
, 2 + offset
, 1 + offset
, *aCursor
.mFileManager
));
6314 mCloneInfo
.init(std::move(cloneInfo
));
6316 if (mCloneInfo
->HasPreprocessInfo()) {
6317 IDB_WARNING("Preprocessing for cursors not yet implemented!");
6318 return NS_ERROR_NOT_IMPLEMENTED
;
6324 template <typename Response
>
6325 void MaybeFillCloneInfo(Response
& aResponse
, FilesArray
* const aFiles
) {
6326 auto cloneInfo
= mCloneInfo
.release();
6327 aResponse
.cloneInfo().data().data
= cloneInfo
.ReleaseData();
6328 aFiles
->AppendElement(cloneInfo
.ReleaseFiles());
6331 template <typename Response
>
6332 static size_t MaybeGetCloneInfoSize(const Response
& aResponse
) {
6333 return aResponse
.cloneInfo().data().data
.Size();
6337 LazyInitializedOnceEarlyDestructible
<const StructuredCloneReadInfoParent
>
6342 struct PopulateResponseHelper
<IDBCursorType::ObjectStore
>
6343 : ValuePopulateResponseHelper
<false>, CommonPopulateResponseHelper
{
6344 using CommonPopulateResponseHelper::CommonPopulateResponseHelper
;
6346 static auto& GetTypedResponse(CursorResponse
* const aResponse
) {
6347 return aResponse
->get_ArrayOfObjectStoreCursorResponse();
6352 struct PopulateResponseHelper
<IDBCursorType::ObjectStoreKey
>
6353 : KeyPopulateResponseHelper
, CommonPopulateResponseHelper
{
6354 using CommonPopulateResponseHelper::CommonPopulateResponseHelper
;
6356 static auto& GetTypedResponse(CursorResponse
* const aResponse
) {
6357 return aResponse
->get_ArrayOfObjectStoreKeyCursorResponse();
6362 struct PopulateResponseHelper
<IDBCursorType::Index
>
6363 : ValuePopulateResponseHelper
<true>, IndexPopulateResponseHelper
{
6364 using IndexPopulateResponseHelper::IndexPopulateResponseHelper
;
6366 static auto& GetTypedResponse(CursorResponse
* const aResponse
) {
6367 return aResponse
->get_ArrayOfIndexCursorResponse();
6372 struct PopulateResponseHelper
<IDBCursorType::IndexKey
>
6373 : KeyPopulateResponseHelper
, IndexPopulateResponseHelper
{
6374 using IndexPopulateResponseHelper::IndexPopulateResponseHelper
;
6376 static auto& GetTypedResponse(CursorResponse
* const aResponse
) {
6377 return aResponse
->get_ArrayOfIndexKeyCursorResponse();
6381 nsresult
DispatchAndReturnFileReferences(
6382 PersistenceType aPersistenceType
, const nsACString
& aOrigin
,
6383 const nsAString
& aDatabaseName
, const int64_t aFileId
,
6384 int32_t* const aMemRefCnt
, int32_t* const aDBRefCnt
, bool* const aResult
) {
6385 AssertIsOnBackgroundThread();
6386 MOZ_ASSERT(aMemRefCnt
);
6387 MOZ_ASSERT(aDBRefCnt
);
6388 MOZ_ASSERT(aResult
);
6394 mozilla::Monitor monitor
MOZ_ANNOTATED(__func__
);
6395 bool waiting
= true;
6398 AssertIsOnIOThread();
6401 IndexedDatabaseManager
* const mgr
= IndexedDatabaseManager::Get();
6404 const SafeRefPtr
<DatabaseFileManager
> fileManager
=
6405 mgr
->GetFileManager(aPersistenceType
, aOrigin
, aDatabaseName
);
6408 const SafeRefPtr
<DatabaseFileInfo
> fileInfo
=
6409 fileManager
->GetFileInfo(aFileId
);
6412 fileInfo
->GetReferences(aMemRefCnt
, aDBRefCnt
);
6414 if (*aMemRefCnt
!= -1) {
6415 // We added an extra temp ref, so account for that accordingly.
6424 mozilla::MonitorAutoLock
lock(monitor
);
6425 MOZ_ASSERT(waiting
);
6431 QuotaManager
* const quotaManager
= QuotaManager::Get();
6432 MOZ_ASSERT(quotaManager
);
6434 // XXX can't we simply use NS_DISPATCH_SYNC instead of using a monitor?
6435 QM_TRY(MOZ_TO_RESULT(quotaManager
->IOThread()->Dispatch(
6436 NS_NewRunnableFunction("GetFileReferences", std::move(lambda
)),
6437 NS_DISPATCH_NORMAL
)));
6439 mozilla::MonitorAutoLock
autolock(monitor
);
6447 class DeserializeIndexValueHelper final
: public Runnable
{
6449 DeserializeIndexValueHelper(int64_t aIndexID
, const KeyPath
& aKeyPath
,
6450 bool aMultiEntry
, const nsACString
& aLocale
,
6451 StructuredCloneReadInfoParent
& aCloneReadInfo
,
6452 nsTArray
<IndexUpdateInfo
>& aUpdateInfoArray
)
6453 : Runnable("DeserializeIndexValueHelper"),
6454 mMonitor("DeserializeIndexValueHelper::mMonitor"),
6457 mMultiEntry(aMultiEntry
),
6459 mCloneReadInfo(aCloneReadInfo
),
6460 mUpdateInfoArray(aUpdateInfoArray
),
6461 mStatus(NS_ERROR_FAILURE
) {}
6463 nsresult
DispatchAndWait() {
6464 // FIXME(Bug 1637530) Re-enable optimization using a non-system-principaled
6467 // We don't need to go to the main-thread and use the sandbox. Let's create
6468 // the updateInfo data here.
6469 if (!mCloneReadInfo
.Data().Size()) {
6473 JS::Rooted
<JS::Value
> value(jsapi
.cx());
6474 value
.setUndefined();
6477 IDBObjectStore::AppendIndexUpdateInfo(mIndexID
, mKeyPath
, mMultiEntry
,
6478 mLocale
, jsapi
.cx(), value
,
6479 &mUpdateInfoArray
, &rv
);
6480 return rv
.Failed() ? rv
.StealNSResult() : NS_OK
;
6484 // The operation will continue on the main-thread.
6486 MOZ_ASSERT(!(mCloneReadInfo
.Data().Size() % sizeof(uint64_t)));
6488 MonitorAutoLock
lock(mMonitor
);
6490 RefPtr
<Runnable
> self
= this;
6491 QM_TRY(MOZ_TO_RESULT(
6492 SchedulerGroup::Dispatch(TaskCategory::Other
, self
.forget())));
6500 MOZ_ASSERT(NS_IsMainThread());
6504 JSContext
* const cx
= jsapi
.cx();
6506 JS::Rooted
<JSObject
*> global(cx
, GetSandbox(cx
));
6508 QM_TRY(OkIf(global
), NS_OK
,
6509 [this](const NotOk
) { OperationCompleted(NS_ERROR_FAILURE
); });
6511 const JSAutoRealm
ar(cx
, global
);
6513 JS::Rooted
<JS::Value
> value(cx
);
6514 QM_TRY(MOZ_TO_RESULT(DeserializeIndexValue(cx
, &value
)), NS_OK
,
6515 [this](const nsresult rv
) { OperationCompleted(rv
); });
6517 ErrorResult errorResult
;
6518 IDBObjectStore::AppendIndexUpdateInfo(mIndexID
, mKeyPath
, mMultiEntry
,
6519 mLocale
, cx
, value
, &mUpdateInfoArray
,
6521 QM_TRY(OkIf(!errorResult
.Failed()), NS_OK
,
6522 ([this, &errorResult
](const NotOk
) {
6523 OperationCompleted(errorResult
.StealNSResult());
6526 OperationCompleted(NS_OK
);
6531 nsresult
DeserializeIndexValue(JSContext
* aCx
,
6532 JS::MutableHandle
<JS::Value
> aValue
) {
6533 static const JSStructuredCloneCallbacks callbacks
= {
6534 StructuredCloneReadCallback
<StructuredCloneReadInfoParent
>,
6543 if (!JS_ReadStructuredClone(
6544 aCx
, mCloneReadInfo
.Data(), JS_STRUCTURED_CLONE_VERSION
,
6545 JS::StructuredCloneScope::DifferentProcessForIndexedDB
, aValue
,
6546 JS::CloneDataPolicy(), &callbacks
, &mCloneReadInfo
)) {
6547 return NS_ERROR_DOM_DATA_CLONE_ERR
;
6553 void OperationCompleted(nsresult aStatus
) {
6556 MonitorAutoLock
lock(mMonitor
);
6560 Monitor mMonitor MOZ_UNANNOTATED
;
6562 const int64_t mIndexID
;
6563 const KeyPath
& mKeyPath
;
6564 const bool mMultiEntry
;
6565 const nsCString mLocale
;
6566 StructuredCloneReadInfoParent
& mCloneReadInfo
;
6567 nsTArray
<IndexUpdateInfo
>& mUpdateInfoArray
;
6571 auto DeserializeIndexValueToUpdateInfos(
6572 int64_t aIndexID
, const KeyPath
& aKeyPath
, bool aMultiEntry
,
6573 const nsACString
& aLocale
, StructuredCloneReadInfoParent
& aCloneReadInfo
) {
6574 MOZ_ASSERT(!NS_IsMainThread());
6576 using ArrayType
= AutoTArray
<IndexUpdateInfo
, 32>;
6577 using ResultType
= Result
<ArrayType
, nsresult
>;
6579 ArrayType updateInfoArray
;
6580 const auto helper
= MakeRefPtr
<DeserializeIndexValueHelper
>(
6581 aIndexID
, aKeyPath
, aMultiEntry
, aLocale
, aCloneReadInfo
,
6583 const nsresult rv
= helper
->DispatchAndWait();
6584 return NS_FAILED(rv
) ? Err(rv
) : ResultType
{std::move(updateInfoArray
)};
6588 const Maybe
<CachingDatabaseConnection::BorrowedStatement
>& aMaybeStmt
) {
6589 return aMaybeStmt
.isSome();
6594 /*******************************************************************************
6595 * Exported functions
6596 ******************************************************************************/
6598 already_AddRefed
<PBackgroundIDBFactoryParent
> AllocPBackgroundIDBFactoryParent(
6599 const LoggingInfo
& aLoggingInfo
) {
6600 AssertIsOnBackgroundThread();
6602 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
6606 if (NS_AUUF_OR_WARN_IF(!aLoggingInfo
.nextTransactionSerialNumber()) ||
6608 !aLoggingInfo
.nextVersionChangeTransactionSerialNumber()) ||
6609 NS_AUUF_OR_WARN_IF(!aLoggingInfo
.nextRequestSerialNumber())) {
6613 SafeRefPtr
<Factory
> actor
= Factory::Create(aLoggingInfo
);
6616 return actor
.forget();
6619 bool RecvPBackgroundIDBFactoryConstructor(
6620 PBackgroundIDBFactoryParent
* aActor
,
6621 const LoggingInfo
& /* aLoggingInfo */) {
6622 AssertIsOnBackgroundThread();
6624 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
6629 PBackgroundIndexedDBUtilsParent
* AllocPBackgroundIndexedDBUtilsParent() {
6630 AssertIsOnBackgroundThread();
6632 RefPtr
<Utils
> actor
= new Utils();
6634 return actor
.forget().take();
6637 bool DeallocPBackgroundIndexedDBUtilsParent(
6638 PBackgroundIndexedDBUtilsParent
* aActor
) {
6639 AssertIsOnBackgroundThread();
6642 RefPtr
<Utils
> actor
= dont_AddRef(static_cast<Utils
*>(aActor
));
6646 bool RecvFlushPendingFileDeletions() {
6647 AssertIsOnBackgroundThread();
6649 if (QuotaClient
* quotaClient
= QuotaClient::GetInstance()) {
6650 QM_WARNONLY_TRY(QM_TO_RESULT(quotaClient
->FlushPendingFileDeletions()));
6656 RefPtr
<mozilla::dom::quota::Client
> CreateQuotaClient() {
6657 AssertIsOnBackgroundThread();
6659 return MakeRefPtr
<QuotaClient
>();
6662 nsresult
DatabaseFileManager::AsyncDeleteFile(int64_t aFileId
) {
6663 AssertIsOnBackgroundThread();
6664 MOZ_ASSERT(!mFileInfos
.Contains(aFileId
));
6666 QuotaClient
* quotaClient
= QuotaClient::GetInstance();
6668 QM_TRY(MOZ_TO_RESULT(quotaClient
->AsyncDeleteFile(this, aFileId
)));
6674 /*******************************************************************************
6675 * DatabaseConnection implementation
6676 ******************************************************************************/
6678 DatabaseConnection::DatabaseConnection(
6679 MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>> aStorageConnection
,
6680 MovingNotNull
<SafeRefPtr
<DatabaseFileManager
>> aFileManager
)
6681 : CachingDatabaseConnection(std::move(aStorageConnection
)),
6682 mFileManager(std::move(aFileManager
)),
6683 mInReadTransaction(false),
6684 mInWriteTransaction(false)
6687 mDEBUGSavepointCount(0)
6690 AssertIsOnConnectionThread();
6691 MOZ_ASSERT(mFileManager
);
6694 DatabaseConnection::~DatabaseConnection() {
6695 MOZ_ASSERT(!mFileManager
);
6696 MOZ_ASSERT(!mUpdateRefcountFunction
);
6697 MOZ_DIAGNOSTIC_ASSERT(!mInWriteTransaction
);
6698 MOZ_ASSERT(!mDEBUGSavepointCount
);
6701 nsresult
DatabaseConnection::Init() {
6702 AssertIsOnConnectionThread();
6703 MOZ_ASSERT(!mInReadTransaction
);
6704 MOZ_ASSERT(!mInWriteTransaction
);
6706 QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("BEGIN;"_ns
)));
6708 mInReadTransaction
= true;
6713 nsresult
DatabaseConnection::BeginWriteTransaction() {
6714 AssertIsOnConnectionThread();
6715 MOZ_ASSERT(HasStorageConnection());
6716 MOZ_ASSERT(mInReadTransaction
);
6717 MOZ_ASSERT(!mInWriteTransaction
);
6719 AUTO_PROFILER_LABEL("DatabaseConnection::BeginWriteTransaction", DOM
);
6721 // Release our read locks.
6722 QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("ROLLBACK;"_ns
)));
6724 mInReadTransaction
= false;
6726 if (!mUpdateRefcountFunction
) {
6727 MOZ_ASSERT(mFileManager
);
6729 RefPtr
<UpdateRefcountFunction
> function
=
6730 new UpdateRefcountFunction(this, **mFileManager
);
6732 QM_TRY(MOZ_TO_RESULT(MutableStorageConnection().CreateFunction(
6733 "update_refcount"_ns
,
6734 /* aNumArguments */ 2, function
)));
6736 mUpdateRefcountFunction
= std::move(function
);
6739 // This one cannot obviously use ExecuteCachedStatement because of the custom
6740 // error handling for Execute only. If only Execute can produce
6741 // NS_ERROR_STORAGE_BUSY, we could actually use ExecuteCachedStatement and
6743 QM_TRY_INSPECT(const auto& beginStmt
,
6744 BorrowCachedStatement("BEGIN IMMEDIATE;"_ns
));
6746 QM_TRY(QM_OR_ELSE_WARN_IF(
6748 MOZ_TO_RESULT(beginStmt
->Execute()),
6750 IsSpecificError
<NS_ERROR_STORAGE_BUSY
>,
6752 ([&beginStmt
](nsresult rv
) {
6754 "Received NS_ERROR_STORAGE_BUSY when attempting to start write "
6755 "transaction, retrying for up to 10 seconds");
6757 // Another thread must be using the database. Wait up to 10 seconds
6758 // for that to complete.
6759 const TimeStamp start
= TimeStamp::NowLoRes();
6762 PR_Sleep(PR_MillisecondsToInterval(100));
6764 rv
= beginStmt
->Execute();
6765 if (rv
!= NS_ERROR_STORAGE_BUSY
||
6766 TimeStamp::NowLoRes() - start
> TimeDuration::FromSeconds(10)) {
6771 return MOZ_TO_RESULT(rv
);
6774 mInWriteTransaction
= true;
6779 nsresult
DatabaseConnection::CommitWriteTransaction() {
6780 AssertIsOnConnectionThread();
6781 MOZ_ASSERT(HasStorageConnection());
6782 MOZ_ASSERT(!mInReadTransaction
);
6783 MOZ_ASSERT(mInWriteTransaction
);
6785 AUTO_PROFILER_LABEL("DatabaseConnection::CommitWriteTransaction", DOM
);
6787 QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("COMMIT;"_ns
)));
6789 mInWriteTransaction
= false;
6793 void DatabaseConnection::RollbackWriteTransaction() {
6794 AssertIsOnConnectionThread();
6795 MOZ_ASSERT(!mInReadTransaction
);
6796 MOZ_DIAGNOSTIC_ASSERT(HasStorageConnection());
6798 AUTO_PROFILER_LABEL("DatabaseConnection::RollbackWriteTransaction", DOM
);
6800 if (!mInWriteTransaction
) {
6805 BorrowCachedStatement("ROLLBACK;"_ns
)
6806 .andThen([&self
= *this](const auto& stmt
) -> Result
<Ok
, nsresult
> {
6807 // This may fail if SQLite already rolled back the transaction
6808 // so ignore any errors.
6810 // XXX ROLLBACK can fail quite normmally if a previous statement
6811 // failed to execute successfully so SQLite rolled back the
6812 // transaction already. However, if it failed because of some other
6813 // reason, we could try to close the connection.
6814 Unused
<< stmt
->Execute();
6816 self
.mInWriteTransaction
= false;
6821 void DatabaseConnection::FinishWriteTransaction() {
6822 AssertIsOnConnectionThread();
6823 MOZ_ASSERT(HasStorageConnection());
6824 MOZ_ASSERT(!mInReadTransaction
);
6825 MOZ_ASSERT(!mInWriteTransaction
);
6827 AUTO_PROFILER_LABEL("DatabaseConnection::FinishWriteTransaction", DOM
);
6829 if (mUpdateRefcountFunction
) {
6830 mUpdateRefcountFunction
->Reset();
6833 QM_WARNONLY_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("BEGIN;"_ns
))
6834 .andThen([&](const auto) -> Result
<Ok
, nsresult
> {
6835 mInReadTransaction
= true;
6840 nsresult
DatabaseConnection::StartSavepoint() {
6841 AssertIsOnConnectionThread();
6842 MOZ_ASSERT(HasStorageConnection());
6843 MOZ_ASSERT(mUpdateRefcountFunction
);
6844 MOZ_ASSERT(mInWriteTransaction
);
6846 AUTO_PROFILER_LABEL("DatabaseConnection::StartSavepoint", DOM
);
6848 QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement(SAVEPOINT_CLAUSE
)));
6850 mUpdateRefcountFunction
->StartSavepoint();
6853 MOZ_ASSERT(mDEBUGSavepointCount
< UINT32_MAX
);
6854 mDEBUGSavepointCount
++;
6860 nsresult
DatabaseConnection::ReleaseSavepoint() {
6861 AssertIsOnConnectionThread();
6862 MOZ_ASSERT(HasStorageConnection());
6863 MOZ_ASSERT(mUpdateRefcountFunction
);
6864 MOZ_ASSERT(mInWriteTransaction
);
6866 AUTO_PROFILER_LABEL("DatabaseConnection::ReleaseSavepoint", DOM
);
6868 QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("RELEASE "_ns SAVEPOINT_CLAUSE
)));
6870 mUpdateRefcountFunction
->ReleaseSavepoint();
6873 MOZ_ASSERT(mDEBUGSavepointCount
);
6874 mDEBUGSavepointCount
--;
6880 nsresult
DatabaseConnection::RollbackSavepoint() {
6881 AssertIsOnConnectionThread();
6882 MOZ_ASSERT(HasStorageConnection());
6883 MOZ_ASSERT(mUpdateRefcountFunction
);
6884 MOZ_ASSERT(mInWriteTransaction
);
6886 AUTO_PROFILER_LABEL("DatabaseConnection::RollbackSavepoint", DOM
);
6889 MOZ_ASSERT(mDEBUGSavepointCount
);
6890 mDEBUGSavepointCount
--;
6893 mUpdateRefcountFunction
->RollbackSavepoint();
6895 QM_TRY_INSPECT(const auto& stmt
,
6896 BorrowCachedStatement("ROLLBACK TO "_ns SAVEPOINT_CLAUSE
));
6898 // This may fail if SQLite already rolled back the savepoint so ignore any
6900 Unused
<< stmt
->Execute();
6905 nsresult
DatabaseConnection::CheckpointInternal(CheckpointMode aMode
) {
6906 AssertIsOnConnectionThread();
6907 MOZ_ASSERT(!mInReadTransaction
);
6908 MOZ_ASSERT(!mInWriteTransaction
);
6910 AUTO_PROFILER_LABEL("DatabaseConnection::CheckpointInternal", DOM
);
6912 nsAutoCString stmtString
;
6913 stmtString
.AssignLiteral("PRAGMA wal_checkpoint(");
6916 case CheckpointMode::Full
:
6917 // Ensures that the database is completely checkpointed and flushed to
6919 stmtString
.AppendLiteral("FULL");
6922 case CheckpointMode::Restart
:
6923 // Like Full, but also ensures that the next write will start overwriting
6924 // the existing WAL file rather than letting the WAL file grow.
6925 stmtString
.AppendLiteral("RESTART");
6928 case CheckpointMode::Truncate
:
6929 // Like Restart but also truncates the existing WAL file.
6930 stmtString
.AppendLiteral("TRUNCATE");
6934 MOZ_CRASH("Unknown CheckpointMode!");
6937 stmtString
.AppendLiteral(");");
6939 QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement(stmtString
)));
6944 void DatabaseConnection::DoIdleProcessing(bool aNeedsCheckpoint
) {
6945 AssertIsOnConnectionThread();
6946 MOZ_ASSERT(mInReadTransaction
);
6947 MOZ_ASSERT(!mInWriteTransaction
);
6949 AUTO_PROFILER_LABEL("DatabaseConnection::DoIdleProcessing", DOM
);
6951 CachingDatabaseConnection::CachedStatement freelistStmt
;
6952 const uint32_t freelistCount
= [this, &freelistStmt
] {
6953 QM_TRY_RETURN(GetFreelistCount(freelistStmt
), 0u);
6956 CachedStatement rollbackStmt
;
6957 CachedStatement beginStmt
;
6958 if (aNeedsCheckpoint
|| freelistCount
) {
6959 QM_TRY_UNWRAP(rollbackStmt
, GetCachedStatement("ROLLBACK;"_ns
), QM_VOID
);
6960 QM_TRY_UNWRAP(beginStmt
, GetCachedStatement("BEGIN;"_ns
), QM_VOID
);
6962 // Release the connection's normal transaction. It's possible that it could
6963 // fail, but that isn't a problem here.
6964 Unused
<< rollbackStmt
.Borrow()->Execute();
6966 mInReadTransaction
= false;
6969 const bool freedSomePages
= freelistCount
&& [this, &freelistStmt
,
6970 &rollbackStmt
, freelistCount
,
6972 // Warn in case of an error, but do not propagate it. Just indicate we
6973 // didn't free any pages.
6974 QM_TRY_INSPECT(const bool& res
,
6975 ReclaimFreePagesWhileIdle(freelistStmt
, rollbackStmt
,
6976 freelistCount
, aNeedsCheckpoint
),
6979 // Make sure we didn't leave a transaction running.
6980 MOZ_ASSERT(!mInReadTransaction
);
6981 MOZ_ASSERT(!mInWriteTransaction
);
6986 // Truncate the WAL if we were asked to or if we managed to free some space.
6987 if (aNeedsCheckpoint
|| freedSomePages
) {
6988 QM_WARNONLY_TRY(QM_TO_RESULT(CheckpointInternal(CheckpointMode::Truncate
)));
6991 // Finally try to restart the read transaction if we rolled it back earlier.
6994 MOZ_TO_RESULT(beginStmt
.Borrow()->Execute())
6995 .andThen([&self
= *this](const Ok
) -> Result
<Ok
, nsresult
> {
6996 self
.mInReadTransaction
= true;
7002 Result
<bool, nsresult
> DatabaseConnection::ReclaimFreePagesWhileIdle(
7003 CachedStatement
& aFreelistStatement
, CachedStatement
& aRollbackStatement
,
7004 uint32_t aFreelistCount
, bool aNeedsCheckpoint
) {
7005 AssertIsOnConnectionThread();
7006 MOZ_ASSERT(aFreelistStatement
);
7007 MOZ_ASSERT(aRollbackStatement
);
7008 MOZ_ASSERT(aFreelistCount
);
7009 MOZ_ASSERT(!mInReadTransaction
);
7010 MOZ_ASSERT(!mInWriteTransaction
);
7012 AUTO_PROFILER_LABEL("DatabaseConnection::ReclaimFreePagesWhileIdle", DOM
);
7014 // Make sure we don't keep working if anything else needs this thread.
7015 nsIThread
* currentThread
= NS_GetCurrentThread();
7016 MOZ_ASSERT(currentThread
);
7018 if (NS_HasPendingEvents(currentThread
)) {
7022 // Make all the statements we'll need up front.
7024 // Only try to free 10% at a time so that we can bail out if this connection
7025 // suddenly becomes active or if the thread is needed otherwise.
7027 const auto& incrementalVacuumStmt
,
7029 "PRAGMA incremental_vacuum("_ns
+
7030 IntToCString(std::max(uint64_t(1), uint64_t(aFreelistCount
/ 10))) +
7033 QM_TRY_INSPECT(const auto& beginImmediateStmt
,
7034 GetCachedStatement("BEGIN IMMEDIATE;"_ns
));
7036 QM_TRY_INSPECT(const auto& commitStmt
, GetCachedStatement("COMMIT;"_ns
));
7038 if (aNeedsCheckpoint
) {
7039 // Freeing pages is a journaled operation, so it will require additional WAL
7040 // space. However, we're idle and are about to checkpoint anyway, so doing a
7041 // RESTART checkpoint here should allow us to reuse any existing space.
7042 QM_TRY(MOZ_TO_RESULT(CheckpointInternal(CheckpointMode::Restart
)));
7045 // Start the write transaction.
7046 QM_TRY(MOZ_TO_RESULT(beginImmediateStmt
.Borrow()->Execute()));
7048 mInWriteTransaction
= true;
7050 bool freedSomePages
= false, interrupted
= false;
7052 const auto rollback
= [&aRollbackStatement
, this](const auto&) {
7053 MOZ_ASSERT(mInWriteTransaction
);
7055 // Something failed, make sure we roll everything back.
7056 Unused
<< aRollbackStatement
.Borrow()->Execute();
7058 // XXX Is rollback infallible? Shouldn't we check the result?
7060 mInWriteTransaction
= false;
7063 QM_TRY(CollectWhile(
7064 [&aFreelistCount
, &interrupted
,
7065 currentThread
]() -> Result
<bool, nsresult
> {
7066 if (NS_HasPendingEvents(currentThread
)) {
7067 // Abort if something else wants to use the thread, and
7068 // roll back this transaction. It's ok if we never make
7069 // progress here because the idle service should
7070 // eventually reclaim this space.
7074 return aFreelistCount
!= 0;
7076 [&aFreelistStatement
, &aFreelistCount
, &incrementalVacuumStmt
,
7077 &freedSomePages
, this]() -> mozilla::Result
<Ok
, nsresult
> {
7078 QM_TRY(MOZ_TO_RESULT(incrementalVacuumStmt
.Borrow()->Execute()));
7080 freedSomePages
= true;
7082 QM_TRY_UNWRAP(aFreelistCount
,
7083 GetFreelistCount(aFreelistStatement
));
7087 .andThen([&commitStmt
, &freedSomePages
, &interrupted
, &rollback
,
7088 this](Ok
) -> Result
<Ok
, nsresult
> {
7091 freedSomePages
= false;
7094 if (freedSomePages
) {
7095 // Commit the write transaction.
7096 QM_TRY(MOZ_TO_RESULT(commitStmt
.Borrow()->Execute()),
7098 [](const auto&) { NS_WARNING("Failed to commit!"); });
7100 mInWriteTransaction
= false;
7105 QM_PROPAGATE
, rollback
);
7107 return freedSomePages
;
7110 Result
<uint32_t, nsresult
> DatabaseConnection::GetFreelistCount(
7111 CachedStatement
& aCachedStatement
) {
7112 AssertIsOnConnectionThread();
7114 AUTO_PROFILER_LABEL("DatabaseConnection::GetFreelistCount", DOM
);
7116 if (!aCachedStatement
) {
7117 QM_TRY_UNWRAP(aCachedStatement
,
7118 GetCachedStatement("PRAGMA freelist_count;"_ns
));
7121 const auto borrowedStatement
= aCachedStatement
.Borrow();
7123 QM_TRY_UNWRAP(const DebugOnly
<bool> hasResult
,
7124 MOZ_TO_RESULT_INVOKE_MEMBER(&*borrowedStatement
, ExecuteStep
));
7126 MOZ_ASSERT(hasResult
);
7128 QM_TRY_INSPECT(const int32_t& freelistCount
,
7129 MOZ_TO_RESULT_INVOKE_MEMBER(*borrowedStatement
, GetInt32
, 0));
7131 MOZ_ASSERT(freelistCount
>= 0);
7133 return uint32_t(freelistCount
);
7136 void DatabaseConnection::Close() {
7137 AssertIsOnConnectionThread();
7138 MOZ_ASSERT(!mDEBUGSavepointCount
);
7139 MOZ_DIAGNOSTIC_ASSERT(!mInWriteTransaction
);
7141 AUTO_PROFILER_LABEL("DatabaseConnection::Close", DOM
);
7143 if (mUpdateRefcountFunction
) {
7144 MOZ_ALWAYS_SUCCEEDS(
7145 MutableStorageConnection().RemoveFunction("update_refcount"_ns
));
7146 mUpdateRefcountFunction
= nullptr;
7149 CachingDatabaseConnection::Close();
7151 mFileManager
.destroy();
7154 nsresult
DatabaseConnection::DisableQuotaChecks() {
7155 AssertIsOnConnectionThread();
7156 MOZ_ASSERT(HasStorageConnection());
7158 if (!mQuotaObject
) {
7159 MOZ_ASSERT(!mJournalQuotaObject
);
7161 QM_TRY(MOZ_TO_RESULT(MutableStorageConnection().GetQuotaObjects(
7162 getter_AddRefs(mQuotaObject
), getter_AddRefs(mJournalQuotaObject
))));
7164 MOZ_ASSERT(mQuotaObject
);
7165 MOZ_ASSERT(mJournalQuotaObject
);
7168 mQuotaObject
->DisableQuotaCheck();
7169 mJournalQuotaObject
->DisableQuotaCheck();
7174 void DatabaseConnection::EnableQuotaChecks() {
7175 AssertIsOnConnectionThread();
7176 if (!mQuotaObject
) {
7177 MOZ_ASSERT(!mJournalQuotaObject
);
7179 // DisableQuotaChecks failed earlier, so we don't need to enable quota
7184 MOZ_ASSERT(mJournalQuotaObject
);
7186 const RefPtr
<QuotaObject
> quotaObject
= std::move(mQuotaObject
);
7187 const RefPtr
<QuotaObject
> journalQuotaObject
= std::move(mJournalQuotaObject
);
7189 quotaObject
->EnableQuotaCheck();
7190 journalQuotaObject
->EnableQuotaCheck();
7192 QM_TRY_INSPECT(const int64_t& fileSize
, GetFileSize(quotaObject
->Path()),
7194 QM_TRY_INSPECT(const int64_t& journalFileSize
,
7195 GetFileSize(journalQuotaObject
->Path()), QM_VOID
);
7197 DebugOnly
<bool> result
= journalQuotaObject
->MaybeUpdateSize(
7198 journalFileSize
, /* aTruncate */ true);
7201 result
= quotaObject
->MaybeUpdateSize(fileSize
, /* aTruncate */ true);
7205 Result
<int64_t, nsresult
> DatabaseConnection::GetFileSize(
7206 const nsAString
& aPath
) {
7207 MOZ_ASSERT(!aPath
.IsEmpty());
7209 QM_TRY_INSPECT(const auto& file
, QM_NewLocalFile(aPath
));
7210 QM_TRY_INSPECT(const bool& exists
, MOZ_TO_RESULT_INVOKE_MEMBER(file
, Exists
));
7213 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(file
, GetFileSize
));
7219 DatabaseConnection::AutoSavepoint::AutoSavepoint()
7220 : mConnection(nullptr)
7223 mDEBUGTransaction(nullptr)
7226 MOZ_COUNT_CTOR(DatabaseConnection::AutoSavepoint
);
7229 DatabaseConnection::AutoSavepoint::~AutoSavepoint() {
7230 MOZ_COUNT_DTOR(DatabaseConnection::AutoSavepoint
);
7233 mConnection
->AssertIsOnConnectionThread();
7234 MOZ_ASSERT(mDEBUGTransaction
);
7236 mDEBUGTransaction
->GetMode() == IDBTransaction::Mode::ReadWrite
||
7237 mDEBUGTransaction
->GetMode() == IDBTransaction::Mode::ReadWriteFlush
||
7238 mDEBUGTransaction
->GetMode() == IDBTransaction::Mode::Cleanup
||
7239 mDEBUGTransaction
->GetMode() == IDBTransaction::Mode::VersionChange
);
7241 QM_WARNONLY_TRY(QM_TO_RESULT(mConnection
->RollbackSavepoint()));
7245 nsresult
DatabaseConnection::AutoSavepoint::Start(
7246 const TransactionBase
& aTransaction
) {
7247 MOZ_ASSERT(aTransaction
.GetMode() == IDBTransaction::Mode::ReadWrite
||
7248 aTransaction
.GetMode() == IDBTransaction::Mode::ReadWriteFlush
||
7249 aTransaction
.GetMode() == IDBTransaction::Mode::Cleanup
||
7250 aTransaction
.GetMode() == IDBTransaction::Mode::VersionChange
);
7252 DatabaseConnection
* connection
= aTransaction
.GetDatabase().GetConnection();
7253 MOZ_ASSERT(connection
);
7254 connection
->AssertIsOnConnectionThread();
7256 // The previous operation failed to begin a write transaction and the
7257 // following opertion jumped to the connection thread before the previous
7258 // operation has updated its failure to the transaction.
7259 if (!connection
->GetUpdateRefcountFunction()) {
7261 "The connection was closed because the previous operation "
7263 return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
;
7266 MOZ_ASSERT(!mConnection
);
7267 MOZ_ASSERT(!mDEBUGTransaction
);
7269 QM_TRY(MOZ_TO_RESULT(connection
->StartSavepoint()));
7271 mConnection
= connection
;
7273 mDEBUGTransaction
= &aTransaction
;
7279 nsresult
DatabaseConnection::AutoSavepoint::Commit() {
7280 MOZ_ASSERT(mConnection
);
7281 mConnection
->AssertIsOnConnectionThread();
7282 MOZ_ASSERT(mDEBUGTransaction
);
7284 QM_TRY(MOZ_TO_RESULT(mConnection
->ReleaseSavepoint()));
7286 mConnection
= nullptr;
7288 mDEBUGTransaction
= nullptr;
7294 DatabaseConnection::UpdateRefcountFunction::UpdateRefcountFunction(
7295 DatabaseConnection
* const aConnection
, DatabaseFileManager
& aFileManager
)
7296 : mConnection(aConnection
),
7297 mFileManager(aFileManager
),
7298 mInSavepoint(false) {
7299 MOZ_ASSERT(aConnection
);
7300 aConnection
->AssertIsOnConnectionThread();
7303 nsresult
DatabaseConnection::UpdateRefcountFunction::WillCommit() {
7304 MOZ_ASSERT(mConnection
);
7305 mConnection
->AssertIsOnConnectionThread();
7306 MOZ_ASSERT(mConnection
->HasStorageConnection());
7308 AUTO_PROFILER_LABEL("DatabaseConnection::UpdateRefcountFunction::WillCommit",
7311 // The parameter names are not used, parameters are bound by index
7312 // only locally in the same function.
7314 [updateStatement
= LazyStatement
{*mConnection
,
7316 "SET refcount = refcount + :delta "
7317 "WHERE id = :id"_ns
},
7318 selectStatement
= LazyStatement
{*mConnection
,
7321 "WHERE id = :id"_ns
},
7325 "INSERT INTO file (id, refcount) VALUES(:id, :delta)"_ns
},
7326 this](int64_t aId
, int32_t aDelta
) mutable -> Result
<Ok
, nsresult
> {
7327 AUTO_PROFILER_LABEL(
7328 "DatabaseConnection::UpdateRefcountFunction::WillCommit::Update", DOM
);
7330 QM_TRY_INSPECT(const auto& borrowedUpdateStatement
,
7331 updateStatement
.Borrow());
7334 MOZ_TO_RESULT(borrowedUpdateStatement
->BindInt32ByIndex(0, aDelta
)));
7335 QM_TRY(MOZ_TO_RESULT(borrowedUpdateStatement
->BindInt64ByIndex(1, aId
)));
7336 QM_TRY(MOZ_TO_RESULT(borrowedUpdateStatement
->Execute()));
7340 const int32_t& rows
,
7341 MOZ_TO_RESULT_INVOKE_MEMBER(mConnection
->MutableStorageConnection(),
7346 const bool& hasResult
,
7348 .BorrowAndExecuteSingleStep(
7349 [aId
](auto& stmt
) -> Result
<Ok
, nsresult
> {
7350 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(0, aId
)));
7356 // Don't have to create the journal here, we can create all at once,
7357 // just before commit
7358 mJournalsToCreateBeforeCommit
.AppendElement(aId
);
7364 QM_TRY_INSPECT(const auto& borrowedInsertStatement
,
7365 insertStatement
.Borrow());
7367 QM_TRY(MOZ_TO_RESULT(borrowedInsertStatement
->BindInt64ByIndex(0, aId
)));
7368 QM_TRY(MOZ_TO_RESULT(borrowedInsertStatement
->BindInt32ByIndex(1, aDelta
)));
7369 QM_TRY(MOZ_TO_RESULT(borrowedInsertStatement
->Execute()));
7371 mJournalsToRemoveAfterCommit
.AppendElement(aId
);
7376 QM_TRY(CollectEachInRange(
7377 mFileInfoEntries
, [&update
](const auto& entry
) -> Result
<Ok
, nsresult
> {
7378 const auto delta
= entry
.GetData()->Delta();
7380 QM_TRY(update(entry
.GetKey(), delta
));
7386 QM_TRY(MOZ_TO_RESULT(CreateJournals()));
7391 void DatabaseConnection::UpdateRefcountFunction::DidCommit() {
7392 MOZ_ASSERT(mConnection
);
7393 mConnection
->AssertIsOnConnectionThread();
7395 AUTO_PROFILER_LABEL("DatabaseConnection::UpdateRefcountFunction::DidCommit",
7398 for (const auto& entry
: mFileInfoEntries
.Values()) {
7399 entry
->MaybeUpdateDBRefs();
7402 QM_WARNONLY_TRY(QM_TO_RESULT(RemoveJournals(mJournalsToRemoveAfterCommit
)));
7405 void DatabaseConnection::UpdateRefcountFunction::DidAbort() {
7406 MOZ_ASSERT(mConnection
);
7407 mConnection
->AssertIsOnConnectionThread();
7409 AUTO_PROFILER_LABEL("DatabaseConnection::UpdateRefcountFunction::DidAbort",
7412 QM_WARNONLY_TRY(QM_TO_RESULT(RemoveJournals(mJournalsToRemoveAfterAbort
)));
7415 void DatabaseConnection::UpdateRefcountFunction::StartSavepoint() {
7416 MOZ_ASSERT(mConnection
);
7417 mConnection
->AssertIsOnConnectionThread();
7418 MOZ_ASSERT(!mInSavepoint
);
7419 MOZ_ASSERT(!mSavepointEntriesIndex
.Count());
7421 mInSavepoint
= true;
7424 void DatabaseConnection::UpdateRefcountFunction::ReleaseSavepoint() {
7425 MOZ_ASSERT(mConnection
);
7426 mConnection
->AssertIsOnConnectionThread();
7427 MOZ_ASSERT(mInSavepoint
);
7429 mSavepointEntriesIndex
.Clear();
7430 mInSavepoint
= false;
7433 void DatabaseConnection::UpdateRefcountFunction::RollbackSavepoint() {
7434 MOZ_ASSERT(mConnection
);
7435 mConnection
->AssertIsOnConnectionThread();
7436 MOZ_ASSERT(!IsOnBackgroundThread());
7437 MOZ_ASSERT(mInSavepoint
);
7439 for (const auto& entry
: mSavepointEntriesIndex
.Values()) {
7440 entry
->DecBySavepointDelta();
7443 mInSavepoint
= false;
7444 mSavepointEntriesIndex
.Clear();
7447 void DatabaseConnection::UpdateRefcountFunction::Reset() {
7448 MOZ_ASSERT(mConnection
);
7449 mConnection
->AssertIsOnConnectionThread();
7450 MOZ_ASSERT(!mSavepointEntriesIndex
.Count());
7451 MOZ_ASSERT(!mInSavepoint
);
7453 mJournalsToCreateBeforeCommit
.Clear();
7454 mJournalsToRemoveAfterCommit
.Clear();
7455 mJournalsToRemoveAfterAbort
.Clear();
7457 // DatabaseFileInfo implementation automatically removes unreferenced files,
7458 // but it's done asynchronously and with a delay. We want to remove them (and
7459 // decrease quota usage) before we fire the commit event.
7460 for (const auto& entry
: mFileInfoEntries
.Values()) {
7461 // We need to move mFileInfo into a raw pointer in order to release it
7462 // explicitly with aSyncDeleteFile == true.
7463 DatabaseFileInfo
* const fileInfo
= entry
->ReleaseFileInfo().forget().take();
7464 MOZ_ASSERT(fileInfo
);
7466 fileInfo
->Release(/* aSyncDeleteFile */ true);
7469 mFileInfoEntries
.Clear();
7472 nsresult
DatabaseConnection::UpdateRefcountFunction::ProcessValue(
7473 mozIStorageValueArray
* aValues
, int32_t aIndex
, UpdateType aUpdateType
) {
7474 MOZ_ASSERT(mConnection
);
7475 mConnection
->AssertIsOnConnectionThread();
7476 MOZ_ASSERT(aValues
);
7478 AUTO_PROFILER_LABEL(
7479 "DatabaseConnection::UpdateRefcountFunction::ProcessValue", DOM
);
7481 QM_TRY_INSPECT(const int32_t& type
,
7482 MOZ_TO_RESULT_INVOKE_MEMBER(aValues
, GetTypeOfIndex
, aIndex
));
7484 if (type
== mozIStorageValueArray::VALUE_TYPE_NULL
) {
7488 QM_TRY_INSPECT(const auto& ids
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
7489 nsString
, aValues
, GetString
, aIndex
));
7491 QM_TRY_INSPECT(const auto& files
,
7492 DeserializeStructuredCloneFiles(mFileManager
, ids
));
7494 for (const StructuredCloneFileParent
& file
: files
) {
7495 const int64_t id
= file
.FileInfo().Id();
7499 WrapNotNull(mFileInfoEntries
.GetOrInsertNew(id
, file
.FileInfoPtr()));
7502 mSavepointEntriesIndex
.InsertOrUpdate(id
, entry
);
7505 switch (aUpdateType
) {
7506 case UpdateType::Increment
:
7507 entry
->IncDeltas(mInSavepoint
);
7509 case UpdateType::Decrement
:
7510 entry
->DecDeltas(mInSavepoint
);
7513 MOZ_CRASH("Unknown update type!");
7520 nsresult
DatabaseConnection::UpdateRefcountFunction::CreateJournals() {
7521 MOZ_ASSERT(mConnection
);
7522 mConnection
->AssertIsOnConnectionThread();
7524 AUTO_PROFILER_LABEL(
7525 "DatabaseConnection::UpdateRefcountFunction::CreateJournals", DOM
);
7527 const nsCOMPtr
<nsIFile
> journalDirectory
= mFileManager
.GetJournalDirectory();
7528 QM_TRY(OkIf(journalDirectory
), NS_ERROR_FAILURE
);
7530 for (const int64_t id
: mJournalsToCreateBeforeCommit
) {
7531 const nsCOMPtr
<nsIFile
> file
=
7532 DatabaseFileManager::GetFileForId(journalDirectory
, id
);
7533 QM_TRY(OkIf(file
), NS_ERROR_FAILURE
);
7535 QM_TRY(MOZ_TO_RESULT(file
->Create(nsIFile::NORMAL_FILE_TYPE
, 0644)));
7537 mJournalsToRemoveAfterAbort
.AppendElement(id
);
7543 nsresult
DatabaseConnection::UpdateRefcountFunction::RemoveJournals(
7544 const nsTArray
<int64_t>& aJournals
) {
7545 MOZ_ASSERT(mConnection
);
7546 mConnection
->AssertIsOnConnectionThread();
7548 AUTO_PROFILER_LABEL(
7549 "DatabaseConnection::UpdateRefcountFunction::RemoveJournals", DOM
);
7551 nsCOMPtr
<nsIFile
> journalDirectory
= mFileManager
.GetJournalDirectory();
7552 QM_TRY(OkIf(journalDirectory
), NS_ERROR_FAILURE
);
7554 for (const auto& journal
: aJournals
) {
7555 nsCOMPtr
<nsIFile
> file
=
7556 DatabaseFileManager::GetFileForId(journalDirectory
, journal
);
7557 QM_TRY(OkIf(file
), NS_ERROR_FAILURE
);
7559 QM_WARNONLY_TRY(QM_TO_RESULT(file
->Remove(false)));
7565 NS_IMPL_ISUPPORTS(DatabaseConnection::UpdateRefcountFunction
,
7566 mozIStorageFunction
)
7569 DatabaseConnection::UpdateRefcountFunction::OnFunctionCall(
7570 mozIStorageValueArray
* aValues
, nsIVariant
** _retval
) {
7571 MOZ_ASSERT(aValues
);
7572 MOZ_ASSERT(_retval
);
7574 AUTO_PROFILER_LABEL(
7575 "DatabaseConnection::UpdateRefcountFunction::OnFunctionCall", DOM
);
7579 QM_TRY_INSPECT(const uint32_t& numEntries
,
7580 MOZ_TO_RESULT_INVOKE_MEMBER(aValues
, GetNumEntries
),
7581 QM_ASSERT_UNREACHABLE
);
7583 MOZ_ASSERT(numEntries
== 2);
7585 QM_TRY_INSPECT(const int32_t& type1
,
7586 MOZ_TO_RESULT_INVOKE_MEMBER(aValues
, GetTypeOfIndex
, 0),
7587 QM_ASSERT_UNREACHABLE
);
7589 QM_TRY_INSPECT(const int32_t& type2
,
7590 MOZ_TO_RESULT_INVOKE_MEMBER(aValues
, GetTypeOfIndex
, 1),
7591 QM_ASSERT_UNREACHABLE
);
7593 MOZ_ASSERT(!(type1
== mozIStorageValueArray::VALUE_TYPE_NULL
&&
7594 type2
== mozIStorageValueArray::VALUE_TYPE_NULL
));
7598 QM_TRY(MOZ_TO_RESULT(ProcessValue(aValues
, 0, UpdateType::Decrement
)));
7600 QM_TRY(MOZ_TO_RESULT(ProcessValue(aValues
, 1, UpdateType::Increment
)));
7605 /*******************************************************************************
7606 * ConnectionPool implementation
7607 ******************************************************************************/
7609 ConnectionPool::ConnectionPool()
7610 : mDatabasesMutex("ConnectionPool::mDatabasesMutex"),
7611 mIdleTimer(NS_NewTimer()),
7612 mNextTransactionId(0),
7613 mTotalThreadCount(0) {
7614 AssertIsOnOwningThread();
7615 AssertIsOnBackgroundThread();
7616 MOZ_ASSERT(mIdleTimer
);
7619 ConnectionPool::~ConnectionPool() {
7620 AssertIsOnOwningThread();
7621 MOZ_ASSERT(mIdleThreads
.IsEmpty());
7622 MOZ_ASSERT(mIdleDatabases
.IsEmpty());
7623 MOZ_ASSERT(!mIdleTimer
);
7624 MOZ_ASSERT(mTargetIdleTime
.IsNull());
7625 MOZ_ASSERT(!mDatabases
.Count());
7626 MOZ_ASSERT(!mTransactions
.Count());
7627 MOZ_ASSERT(mQueuedTransactions
.IsEmpty());
7628 MOZ_ASSERT(mCompleteCallbacks
.IsEmpty());
7629 MOZ_ASSERT(!mTotalThreadCount
);
7630 MOZ_ASSERT(mShutdownRequested
);
7631 MOZ_ASSERT(mShutdownComplete
);
7635 void ConnectionPool::IdleTimerCallback(nsITimer
* aTimer
, void* aClosure
) {
7637 MOZ_ASSERT(aClosure
);
7639 AUTO_PROFILER_LABEL("ConnectionPool::IdleTimerCallback", DOM
);
7641 auto& self
= *static_cast<ConnectionPool
*>(aClosure
);
7642 MOZ_ASSERT(self
.mIdleTimer
);
7643 MOZ_ASSERT(SameCOMIdentity(self
.mIdleTimer
, aTimer
));
7644 MOZ_ASSERT(!self
.mTargetIdleTime
.IsNull());
7645 MOZ_ASSERT_IF(self
.mIdleDatabases
.IsEmpty(), !self
.mIdleThreads
.IsEmpty());
7646 MOZ_ASSERT_IF(self
.mIdleThreads
.IsEmpty(), !self
.mIdleDatabases
.IsEmpty());
7648 self
.mTargetIdleTime
= TimeStamp();
7651 const TimeStamp now
=
7652 TimeStamp::NowLoRes() + TimeDuration::FromMilliseconds(500);
7654 // XXX Move this to ArrayAlgorithm.h?
7655 const auto removeUntil
= [](auto& array
, auto&& cond
) {
7656 const auto begin
= array
.begin(), end
= array
.end();
7657 array
.RemoveElementsRange(
7658 begin
, std::find_if(begin
, end
, std::forward
<decltype(cond
)>(cond
)));
7661 removeUntil(self
.mIdleDatabases
, [now
, &self
](const auto& info
) {
7662 if (now
>= info
.mIdleTime
) {
7663 if ((*info
.mDatabaseInfo
)->mIdle
) {
7664 self
.PerformIdleDatabaseMaintenance(*info
.mDatabaseInfo
.ref());
7666 self
.CloseDatabase(*info
.mDatabaseInfo
.ref());
7675 removeUntil(self
.mIdleThreads
, [now
, &self
](auto& info
) {
7676 info
.mThreadInfo
.AssertValid();
7678 if (now
>= info
.mIdleTime
) {
7679 self
.ShutdownThread(std::move(info
.mThreadInfo
));
7687 self
.AdjustIdleTimer();
7690 Result
<RefPtr
<DatabaseConnection
>, nsresult
>
7691 ConnectionPool::GetOrCreateConnection(const Database
& aDatabase
) {
7692 MOZ_ASSERT(!NS_IsMainThread());
7693 MOZ_ASSERT(!IsOnBackgroundThread());
7695 AUTO_PROFILER_LABEL("ConnectionPool::GetOrCreateConnection", DOM
);
7697 DatabaseInfo
* dbInfo
;
7699 MutexAutoLock
lock(mDatabasesMutex
);
7701 dbInfo
= mDatabases
.Get(aDatabase
.Id());
7706 if (dbInfo
->mConnection
) {
7707 dbInfo
->AssertIsOnConnectionThread();
7709 return dbInfo
->mConnection
;
7712 MOZ_ASSERT(!dbInfo
->mDEBUGConnectionThread
);
7715 MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>> storageConnection
,
7716 GetStorageConnection(aDatabase
.FilePath(), aDatabase
.DirectoryLockId(),
7717 aDatabase
.TelemetryId(), aDatabase
.MaybeKeyRef()));
7719 RefPtr
<DatabaseConnection
> connection
= new DatabaseConnection(
7720 std::move(storageConnection
), aDatabase
.GetFileManagerPtr());
7722 QM_TRY(MOZ_TO_RESULT(connection
->Init()));
7724 dbInfo
->mConnection
= connection
;
7726 IDB_DEBUG_LOG(("ConnectionPool created connection 0x%p for '%s'",
7727 dbInfo
->mConnection
.get(),
7728 NS_ConvertUTF16toUTF8(aDatabase
.FilePath()).get()));
7731 dbInfo
->mDEBUGConnectionThread
= PR_GetCurrentThread();
7737 uint64_t ConnectionPool::Start(
7738 const nsID
& aBackgroundChildLoggingId
, const nsACString
& aDatabaseId
,
7739 int64_t aLoggingSerialNumber
, const nsTArray
<nsString
>& aObjectStoreNames
,
7740 bool aIsWriteTransaction
,
7741 TransactionDatabaseOperationBase
* aTransactionOp
) {
7742 AssertIsOnOwningThread();
7743 MOZ_ASSERT(!aDatabaseId
.IsEmpty());
7744 MOZ_ASSERT(mNextTransactionId
< UINT64_MAX
);
7745 MOZ_ASSERT(!mShutdownRequested
);
7747 AUTO_PROFILER_LABEL("ConnectionPool::Start", DOM
);
7749 const uint64_t transactionId
= ++mNextTransactionId
;
7751 // To avoid always acquiring a lock, we don't use WithEntryHandle here, which
7752 // would require a lock in any case.
7753 DatabaseInfo
* dbInfo
= mDatabases
.Get(aDatabaseId
);
7755 const bool databaseInfoIsNew
= !dbInfo
;
7757 if (databaseInfoIsNew
) {
7758 MutexAutoLock
lock(mDatabasesMutex
);
7761 .InsertOrUpdate(aDatabaseId
,
7762 MakeUnique
<DatabaseInfo
>(this, aDatabaseId
))
7766 MOZ_ASSERT(!mTransactions
.Contains(transactionId
));
7767 auto& transactionInfo
= *mTransactions
.InsertOrUpdate(
7768 transactionId
, MakeUnique
<TransactionInfo
>(
7769 *dbInfo
, aBackgroundChildLoggingId
, aDatabaseId
,
7770 transactionId
, aLoggingSerialNumber
, aObjectStoreNames
,
7771 aIsWriteTransaction
, aTransactionOp
));
7773 if (aIsWriteTransaction
) {
7774 MOZ_ASSERT(dbInfo
->mWriteTransactionCount
< UINT32_MAX
);
7775 dbInfo
->mWriteTransactionCount
++;
7777 MOZ_ASSERT(dbInfo
->mReadTransactionCount
< UINT32_MAX
);
7778 dbInfo
->mReadTransactionCount
++;
7781 auto& blockingTransactions
= dbInfo
->mBlockingTransactions
;
7783 for (const nsAString
& objectStoreName
: aObjectStoreNames
) {
7784 TransactionInfoPair
* blockInfo
=
7785 blockingTransactions
.GetOrInsertNew(objectStoreName
);
7787 // Mark what we are blocking on.
7788 if (const auto maybeBlockingRead
= blockInfo
->mLastBlockingReads
) {
7789 transactionInfo
.mBlockedOn
.Insert(&maybeBlockingRead
.ref());
7790 maybeBlockingRead
->AddBlockingTransaction(transactionInfo
);
7793 if (aIsWriteTransaction
) {
7794 for (const auto blockingWrite
: blockInfo
->mLastBlockingWrites
) {
7795 transactionInfo
.mBlockedOn
.Insert(blockingWrite
);
7796 blockingWrite
->AddBlockingTransaction(transactionInfo
);
7799 blockInfo
->mLastBlockingReads
= SomeRef(transactionInfo
);
7800 blockInfo
->mLastBlockingWrites
.Clear();
7802 blockInfo
->mLastBlockingWrites
.AppendElement(
7803 WrapNotNullUnchecked(&transactionInfo
));
7807 if (!transactionInfo
.mBlockedOn
.Count()) {
7808 Unused
<< ScheduleTransaction(transactionInfo
,
7809 /* aFromQueuedTransactions */ false);
7812 if (!databaseInfoIsNew
&&
7813 (mIdleDatabases
.RemoveElement(dbInfo
) ||
7814 mDatabasesPerformingIdleMaintenance
.RemoveElement(dbInfo
))) {
7818 return transactionId
;
7821 void ConnectionPool::Dispatch(uint64_t aTransactionId
, nsIRunnable
* aRunnable
) {
7822 AssertIsOnOwningThread();
7823 MOZ_ASSERT(aRunnable
);
7825 AUTO_PROFILER_LABEL("ConnectionPool::Dispatch", DOM
);
7827 auto* const transactionInfo
= mTransactions
.Get(aTransactionId
);
7828 MOZ_ASSERT(transactionInfo
);
7829 MOZ_ASSERT(!transactionInfo
->mFinished
);
7831 if (transactionInfo
->mRunning
) {
7832 DatabaseInfo
& dbInfo
= transactionInfo
->mDatabaseInfo
;
7833 dbInfo
.mThreadInfo
.AssertValid();
7834 MOZ_ASSERT(!dbInfo
.mClosing
);
7836 transactionInfo
->mIsWriteTransaction
,
7837 dbInfo
.mRunningWriteTransaction
&&
7838 dbInfo
.mRunningWriteTransaction
.refEquals(*transactionInfo
));
7840 MOZ_ALWAYS_SUCCEEDS(
7841 dbInfo
.mThreadInfo
.ThreadRef().Dispatch(aRunnable
, NS_DISPATCH_NORMAL
));
7843 transactionInfo
->mQueuedRunnables
.AppendElement(aRunnable
);
7847 void ConnectionPool::Finish(uint64_t aTransactionId
,
7848 FinishCallback
* aCallback
) {
7849 AssertIsOnOwningThread();
7852 auto* const transactionInfo
= mTransactions
.Get(aTransactionId
);
7853 MOZ_ASSERT(transactionInfo
);
7854 MOZ_ASSERT(!transactionInfo
->mFinished
);
7857 AUTO_PROFILER_LABEL("ConnectionPool::Finish", DOM
);
7859 RefPtr
<FinishCallbackWrapper
> wrapper
=
7860 new FinishCallbackWrapper(this, aTransactionId
, aCallback
);
7862 Dispatch(aTransactionId
, wrapper
);
7865 transactionInfo
->mFinished
.Flip();
7869 void ConnectionPool::WaitForDatabasesToComplete(
7870 nsTArray
<nsCString
>&& aDatabaseIds
, nsIRunnable
* aCallback
) {
7871 AssertIsOnOwningThread();
7872 MOZ_ASSERT(!aDatabaseIds
.IsEmpty());
7873 MOZ_ASSERT(aCallback
);
7875 AUTO_PROFILER_LABEL("ConnectionPool::WaitForDatabasesToComplete", DOM
);
7877 bool mayRunCallbackImmediately
= true;
7879 for (const nsACString
& databaseId
: aDatabaseIds
) {
7880 MOZ_ASSERT(!databaseId
.IsEmpty());
7882 if (CloseDatabaseWhenIdleInternal(databaseId
)) {
7883 mayRunCallbackImmediately
= false;
7887 if (mayRunCallbackImmediately
) {
7888 Unused
<< aCallback
->Run();
7892 mCompleteCallbacks
.EmplaceBack(MakeUnique
<DatabasesCompleteCallback
>(
7893 std::move(aDatabaseIds
), aCallback
));
7896 void ConnectionPool::Shutdown() {
7897 AssertIsOnOwningThread();
7898 MOZ_ASSERT(!mShutdownComplete
);
7900 AUTO_PROFILER_LABEL("ConnectionPool::Shutdown", DOM
);
7902 mShutdownRequested
.Flip();
7905 MOZ_ASSERT(mTargetIdleTime
.IsNull());
7907 mIdleTimer
= nullptr;
7909 CloseIdleDatabases();
7911 ShutdownIdleThreads();
7913 if (!mDatabases
.Count()) {
7914 MOZ_ASSERT(!mTransactions
.Count());
7918 MOZ_ASSERT(mShutdownComplete
);
7922 MOZ_ALWAYS_TRUE(SpinEventLoopUntil("ConnectionPool::Shutdown"_ns
, [&]() {
7923 return static_cast<bool>(mShutdownComplete
);
7927 void ConnectionPool::Cleanup() {
7928 AssertIsOnOwningThread();
7929 MOZ_ASSERT(mShutdownRequested
);
7930 MOZ_ASSERT(!mShutdownComplete
);
7931 MOZ_ASSERT(!mDatabases
.Count());
7932 MOZ_ASSERT(!mTransactions
.Count());
7933 MOZ_ASSERT(mIdleThreads
.IsEmpty());
7935 AUTO_PROFILER_LABEL("ConnectionPool::Cleanup", DOM
);
7937 if (!mCompleteCallbacks
.IsEmpty()) {
7938 // Run all callbacks manually now.
7941 auto completeCallbacks
= std::move(mCompleteCallbacks
);
7942 for (const auto& completeCallback
: completeCallbacks
) {
7943 MOZ_ASSERT(completeCallback
);
7944 MOZ_ASSERT(completeCallback
->mCallback
);
7946 Unused
<< completeCallback
->mCallback
->Run();
7949 // We expect no new callbacks being completed by running the existing
7951 MOZ_ASSERT(mCompleteCallbacks
.IsEmpty());
7954 // And make sure they get processed.
7955 nsIThread
* currentThread
= NS_GetCurrentThread();
7956 MOZ_ASSERT(currentThread
);
7958 MOZ_ALWAYS_SUCCEEDS(NS_ProcessPendingEvents(currentThread
));
7961 mShutdownComplete
.Flip();
7964 void ConnectionPool::AdjustIdleTimer() {
7965 AssertIsOnOwningThread();
7966 MOZ_ASSERT(mIdleTimer
);
7968 AUTO_PROFILER_LABEL("ConnectionPool::AdjustIdleTimer", DOM
);
7970 // Figure out the next time at which we should release idle resources. This
7971 // includes both databases and threads.
7972 TimeStamp newTargetIdleTime
;
7973 MOZ_ASSERT(newTargetIdleTime
.IsNull());
7975 if (!mIdleDatabases
.IsEmpty()) {
7976 newTargetIdleTime
= mIdleDatabases
[0].mIdleTime
;
7979 if (!mIdleThreads
.IsEmpty()) {
7980 const TimeStamp
& idleTime
= mIdleThreads
[0].mIdleTime
;
7982 if (newTargetIdleTime
.IsNull() || idleTime
< newTargetIdleTime
) {
7983 newTargetIdleTime
= idleTime
;
7987 MOZ_ASSERT_IF(newTargetIdleTime
.IsNull(), mIdleDatabases
.IsEmpty());
7988 MOZ_ASSERT_IF(newTargetIdleTime
.IsNull(), mIdleThreads
.IsEmpty());
7990 // Cancel the timer if it was running and the new target time is different.
7991 if (!mTargetIdleTime
.IsNull() &&
7992 (newTargetIdleTime
.IsNull() || mTargetIdleTime
!= newTargetIdleTime
)) {
7995 MOZ_ASSERT(mTargetIdleTime
.IsNull());
7998 // Schedule the timer if we have a target time different than before.
7999 if (!newTargetIdleTime
.IsNull() &&
8000 (mTargetIdleTime
.IsNull() || mTargetIdleTime
!= newTargetIdleTime
)) {
8001 double delta
= (newTargetIdleTime
- TimeStamp::NowLoRes()).ToMilliseconds();
8005 delay
= uint32_t(std::min(delta
, double(UINT32_MAX
)));
8010 MOZ_ALWAYS_SUCCEEDS(mIdleTimer
->InitWithNamedFuncCallback(
8011 IdleTimerCallback
, this, delay
, nsITimer::TYPE_ONE_SHOT
,
8012 "ConnectionPool::IdleTimerCallback"));
8014 mTargetIdleTime
= newTargetIdleTime
;
8018 void ConnectionPool::CancelIdleTimer() {
8019 AssertIsOnOwningThread();
8020 MOZ_ASSERT(mIdleTimer
);
8022 if (!mTargetIdleTime
.IsNull()) {
8023 MOZ_ALWAYS_SUCCEEDS(mIdleTimer
->Cancel());
8025 mTargetIdleTime
= TimeStamp();
8026 MOZ_ASSERT(mTargetIdleTime
.IsNull());
8030 void ConnectionPool::ShutdownThread(ThreadInfo aThreadInfo
) {
8031 AssertIsOnOwningThread();
8032 MOZ_ASSERT(mTotalThreadCount
);
8034 // We need to move thread and runnable separately.
8035 auto [thread
, runnable
] = aThreadInfo
.Forget();
8037 IDB_DEBUG_LOG(("ConnectionPool shutting down thread %" PRIu32
,
8038 runnable
->SerialNumber()));
8040 // This should clean up the thread with the profiler.
8041 MOZ_ALWAYS_SUCCEEDS(thread
->Dispatch(runnable
.forget(), NS_DISPATCH_NORMAL
));
8043 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod(
8044 "nsIThread::AsyncShutdown", thread
, &nsIThread::AsyncShutdown
)));
8046 mTotalThreadCount
--;
8049 void ConnectionPool::CloseIdleDatabases() {
8050 AssertIsOnOwningThread();
8051 MOZ_ASSERT(mShutdownRequested
);
8053 AUTO_PROFILER_LABEL("ConnectionPool::CloseIdleDatabases", DOM
);
8055 if (!mIdleDatabases
.IsEmpty()) {
8056 for (IdleDatabaseInfo
& idleInfo
: mIdleDatabases
) {
8057 CloseDatabase(*idleInfo
.mDatabaseInfo
.ref());
8059 mIdleDatabases
.Clear();
8062 if (!mDatabasesPerformingIdleMaintenance
.IsEmpty()) {
8063 for (const auto dbInfo
: mDatabasesPerformingIdleMaintenance
) {
8064 CloseDatabase(*dbInfo
);
8066 mDatabasesPerformingIdleMaintenance
.Clear();
8070 void ConnectionPool::ShutdownIdleThreads() {
8071 AssertIsOnOwningThread();
8072 MOZ_ASSERT(mShutdownRequested
);
8074 AUTO_PROFILER_LABEL("ConnectionPool::ShutdownIdleThreads", DOM
);
8076 for (auto& idleThread
: mIdleThreads
) {
8077 ShutdownThread(std::move(idleThread
.mThreadInfo
));
8079 mIdleThreads
.Clear();
8082 bool ConnectionPool::ScheduleTransaction(TransactionInfo
& aTransactionInfo
,
8083 bool aFromQueuedTransactions
) {
8084 AssertIsOnOwningThread();
8086 AUTO_PROFILER_LABEL("ConnectionPool::ScheduleTransaction", DOM
);
8088 DatabaseInfo
& dbInfo
= aTransactionInfo
.mDatabaseInfo
;
8090 dbInfo
.mIdle
= false;
8092 if (dbInfo
.mClosing
) {
8093 MOZ_ASSERT(!mIdleDatabases
.Contains(&dbInfo
));
8095 !dbInfo
.mTransactionsScheduledDuringClose
.Contains(&aTransactionInfo
));
8097 dbInfo
.mTransactionsScheduledDuringClose
.AppendElement(
8098 WrapNotNullUnchecked(&aTransactionInfo
));
8102 if (!dbInfo
.mThreadInfo
.IsValid()) {
8103 if (mIdleThreads
.IsEmpty()) {
8104 bool created
= false;
8106 if (mTotalThreadCount
< kMaxConnectionThreadCount
) {
8107 // This will set the thread up with the profiler.
8108 RefPtr
<ThreadRunnable
> runnable
= new ThreadRunnable();
8110 nsCOMPtr
<nsIThread
> newThread
;
8111 nsresult rv
= NS_NewNamedThread(runnable
->GetThreadName(),
8112 getter_AddRefs(newThread
), runnable
);
8113 if (NS_SUCCEEDED(rv
)) {
8114 newThread
->SetNameForWakeupTelemetry("IndexedDB (all)"_ns
);
8115 MOZ_ASSERT(newThread
);
8117 IDB_DEBUG_LOG(("ConnectionPool created thread %" PRIu32
,
8118 runnable
->SerialNumber()));
8120 dbInfo
.mThreadInfo
=
8121 ThreadInfo
{std::move(newThread
), std::move(runnable
)};
8123 mTotalThreadCount
++;
8126 NS_WARNING("Failed to make new thread!");
8128 } else if (!mDatabasesPerformingIdleMaintenance
.IsEmpty()) {
8129 // We need a thread right now so force all idle processing to stop by
8130 // posting a dummy runnable to each thread that might be doing idle
8133 // This is copied for each database inside the loop below, it is
8134 // deliberately const to prevent the attempt to wrongly optimize the
8135 // refcounting by passing runnable.forget() to the Dispatch method, see
8137 const nsCOMPtr
<nsIRunnable
> runnable
=
8138 new Runnable("IndexedDBDummyRunnable");
8140 for (uint32_t index
= mDatabasesPerformingIdleMaintenance
.Length();
8141 index
> 0; index
--) {
8142 const auto dbInfo
= mDatabasesPerformingIdleMaintenance
[index
- 1];
8143 dbInfo
->mThreadInfo
.AssertValid();
8145 MOZ_ALWAYS_SUCCEEDS(dbInfo
->mThreadInfo
.ThreadRef().Dispatch(
8146 runnable
, NS_DISPATCH_NORMAL
));
8151 if (!aFromQueuedTransactions
) {
8152 MOZ_ASSERT(!mQueuedTransactions
.Contains(&aTransactionInfo
));
8153 mQueuedTransactions
.AppendElement(
8154 WrapNotNullUnchecked(&aTransactionInfo
));
8159 dbInfo
.mThreadInfo
= std::move(mIdleThreads
.PopLastElement().mThreadInfo
);
8165 dbInfo
.mThreadInfo
.AssertValid();
8167 if (aTransactionInfo
.mIsWriteTransaction
) {
8168 if (dbInfo
.mRunningWriteTransaction
) {
8169 // SQLite only allows one write transaction at a time so queue this
8170 // transaction for later.
8172 !dbInfo
.mScheduledWriteTransactions
.Contains(&aTransactionInfo
));
8174 dbInfo
.mScheduledWriteTransactions
.AppendElement(
8175 WrapNotNullUnchecked(&aTransactionInfo
));
8179 dbInfo
.mRunningWriteTransaction
= SomeRef(aTransactionInfo
);
8180 dbInfo
.mNeedsCheckpoint
= true;
8183 MOZ_ASSERT(!aTransactionInfo
.mRunning
);
8184 aTransactionInfo
.mRunning
= true;
8186 nsTArray
<nsCOMPtr
<nsIRunnable
>>& queuedRunnables
=
8187 aTransactionInfo
.mQueuedRunnables
;
8189 if (!queuedRunnables
.IsEmpty()) {
8190 for (auto& queuedRunnable
: queuedRunnables
) {
8191 MOZ_ALWAYS_SUCCEEDS(dbInfo
.mThreadInfo
.ThreadRef().Dispatch(
8192 queuedRunnable
.forget(), NS_DISPATCH_NORMAL
));
8195 queuedRunnables
.Clear();
8201 void ConnectionPool::NoteFinishedTransaction(uint64_t aTransactionId
) {
8202 AssertIsOnOwningThread();
8204 AUTO_PROFILER_LABEL("ConnectionPool::NoteFinishedTransaction", DOM
);
8206 auto* const transactionInfo
= mTransactions
.Get(aTransactionId
);
8207 MOZ_ASSERT(transactionInfo
);
8208 MOZ_ASSERT(transactionInfo
->mRunning
);
8209 MOZ_ASSERT(transactionInfo
->mFinished
);
8211 transactionInfo
->mRunning
= false;
8213 DatabaseInfo
& dbInfo
= transactionInfo
->mDatabaseInfo
;
8214 MOZ_ASSERT(mDatabases
.Get(transactionInfo
->mDatabaseId
) == &dbInfo
);
8215 dbInfo
.mThreadInfo
.AssertValid();
8217 // Schedule the next write transaction if there are any queued.
8218 if (dbInfo
.mRunningWriteTransaction
&&
8219 dbInfo
.mRunningWriteTransaction
.refEquals(*transactionInfo
)) {
8220 MOZ_ASSERT(transactionInfo
->mIsWriteTransaction
);
8221 MOZ_ASSERT(dbInfo
.mNeedsCheckpoint
);
8223 dbInfo
.mRunningWriteTransaction
= Nothing();
8225 if (!dbInfo
.mScheduledWriteTransactions
.IsEmpty()) {
8226 const auto nextWriteTransaction
= dbInfo
.mScheduledWriteTransactions
[0];
8228 dbInfo
.mScheduledWriteTransactions
.RemoveElementAt(0);
8230 MOZ_ALWAYS_TRUE(ScheduleTransaction(*nextWriteTransaction
,
8231 /* aFromQueuedTransactions */ false));
8235 for (const auto& objectStoreName
: transactionInfo
->mObjectStoreNames
) {
8236 TransactionInfoPair
* blockInfo
=
8237 dbInfo
.mBlockingTransactions
.Get(objectStoreName
);
8238 MOZ_ASSERT(blockInfo
);
8240 if (transactionInfo
->mIsWriteTransaction
&& blockInfo
->mLastBlockingReads
&&
8241 blockInfo
->mLastBlockingReads
.refEquals(*transactionInfo
)) {
8242 blockInfo
->mLastBlockingReads
= Nothing();
8245 blockInfo
->mLastBlockingWrites
.RemoveElement(transactionInfo
);
8248 transactionInfo
->RemoveBlockingTransactions();
8250 if (transactionInfo
->mIsWriteTransaction
) {
8251 MOZ_ASSERT(dbInfo
.mWriteTransactionCount
);
8252 dbInfo
.mWriteTransactionCount
--;
8254 MOZ_ASSERT(dbInfo
.mReadTransactionCount
);
8255 dbInfo
.mReadTransactionCount
--;
8258 mTransactions
.Remove(aTransactionId
);
8260 if (!dbInfo
.TotalTransactionCount()) {
8261 MOZ_ASSERT(!dbInfo
.mIdle
);
8262 dbInfo
.mIdle
= true;
8264 NoteIdleDatabase(dbInfo
);
8268 void ConnectionPool::ScheduleQueuedTransactions(ThreadInfo aThreadInfo
) {
8269 AssertIsOnOwningThread();
8270 aThreadInfo
.AssertValid();
8271 MOZ_ASSERT(!mQueuedTransactions
.IsEmpty());
8273 AUTO_PROFILER_LABEL("ConnectionPool::ScheduleQueuedTransactions", DOM
);
8275 auto idleThreadInfo
= IdleThreadInfo
{std::move(aThreadInfo
)};
8276 MOZ_ASSERT(!mIdleThreads
.Contains(idleThreadInfo
));
8277 mIdleThreads
.InsertElementSorted(std::move(idleThreadInfo
));
8279 const auto foundIt
= std::find_if(
8280 mQueuedTransactions
.begin(), mQueuedTransactions
.end(),
8281 [&me
= *this](const auto& queuedTransaction
) {
8282 return !me
.ScheduleTransaction(*queuedTransaction
,
8283 /* aFromQueuedTransactions */ true);
8286 mQueuedTransactions
.RemoveElementsRange(mQueuedTransactions
.begin(), foundIt
);
8291 void ConnectionPool::NoteIdleDatabase(DatabaseInfo
& aDatabaseInfo
) {
8292 AssertIsOnOwningThread();
8293 MOZ_ASSERT(!aDatabaseInfo
.TotalTransactionCount());
8294 aDatabaseInfo
.mThreadInfo
.AssertValid();
8295 MOZ_ASSERT(!mIdleDatabases
.Contains(&aDatabaseInfo
));
8297 AUTO_PROFILER_LABEL("ConnectionPool::NoteIdleDatabase", DOM
);
8299 const bool otherDatabasesWaiting
= !mQueuedTransactions
.IsEmpty();
8301 if (mShutdownRequested
|| otherDatabasesWaiting
||
8302 aDatabaseInfo
.mCloseOnIdle
) {
8303 // Make sure we close the connection if we're shutting down or giving the
8304 // thread to another database.
8305 CloseDatabase(aDatabaseInfo
);
8307 if (otherDatabasesWaiting
) {
8308 // Let another database use this thread.
8309 ScheduleQueuedTransactions(std::move(aDatabaseInfo
.mThreadInfo
));
8310 } else if (mShutdownRequested
) {
8311 // If there are no other databases that need to run then we can shut this
8312 // thread down immediately instead of going through the idle thread
8314 ShutdownThread(std::move(aDatabaseInfo
.mThreadInfo
));
8320 mIdleDatabases
.InsertElementSorted(IdleDatabaseInfo
{aDatabaseInfo
});
8325 void ConnectionPool::NoteClosedDatabase(DatabaseInfo
& aDatabaseInfo
) {
8326 AssertIsOnOwningThread();
8327 MOZ_ASSERT(aDatabaseInfo
.mClosing
);
8328 MOZ_ASSERT(!mIdleDatabases
.Contains(&aDatabaseInfo
));
8330 AUTO_PROFILER_LABEL("ConnectionPool::NoteClosedDatabase", DOM
);
8332 aDatabaseInfo
.mClosing
= false;
8334 // Figure out what to do with this database's thread. It may have already been
8335 // given to another database, in which case there's nothing to do here.
8336 // Otherwise we prioritize the thread as follows:
8337 // 1. Databases that haven't had an opportunity to run at all are highest
8338 // priority. Those live in the |mQueuedTransactions| list.
8339 // 2. If this database has additional transactions that were started after
8340 // we began closing the connection then the thread can be reused for
8341 // those transactions.
8342 // 3. If we're shutting down then we can get rid of the thread.
8343 // 4. Finally, if nothing above took the thread then we can add it to our
8344 // list of idle threads. It may be reused or it may time out. If we have
8345 // too many idle threads then we will shut down the oldest.
8346 if (aDatabaseInfo
.mThreadInfo
.IsValid()) {
8347 if (!mQueuedTransactions
.IsEmpty()) {
8348 // Give the thread to another database.
8349 ScheduleQueuedTransactions(std::move(aDatabaseInfo
.mThreadInfo
));
8350 } else if (!aDatabaseInfo
.TotalTransactionCount()) {
8351 if (mShutdownRequested
) {
8352 ShutdownThread(std::move(aDatabaseInfo
.mThreadInfo
));
8354 auto idleThreadInfo
=
8355 IdleThreadInfo
{std::move(aDatabaseInfo
.mThreadInfo
)};
8356 MOZ_ASSERT(!mIdleThreads
.Contains(idleThreadInfo
));
8358 mIdleThreads
.InsertElementSorted(std::move(idleThreadInfo
));
8360 if (mIdleThreads
.Length() > kMaxIdleConnectionThreadCount
) {
8361 ShutdownThread(std::move(mIdleThreads
[0].mThreadInfo
));
8362 mIdleThreads
.RemoveElementAt(0);
8370 // Schedule any transactions that were started while we were closing the
8372 if (aDatabaseInfo
.TotalTransactionCount()) {
8373 auto& scheduledTransactions
=
8374 aDatabaseInfo
.mTransactionsScheduledDuringClose
;
8376 MOZ_ASSERT(!scheduledTransactions
.IsEmpty());
8378 for (const auto& scheduledTransaction
: scheduledTransactions
) {
8379 Unused
<< ScheduleTransaction(*scheduledTransaction
,
8380 /* aFromQueuedTransactions */ false);
8383 scheduledTransactions
.Clear();
8388 // There are no more transactions and the connection has been closed. We're
8389 // done with this database.
8391 MutexAutoLock
lock(mDatabasesMutex
);
8393 mDatabases
.Remove(aDatabaseInfo
.mDatabaseId
);
8396 // That just deleted |aDatabaseInfo|, we must not access that below.
8398 // See if we need to fire any complete callbacks now that the database is
8400 mCompleteCallbacks
.RemoveLastElements(
8401 mCompleteCallbacks
.end() -
8402 std::remove_if(mCompleteCallbacks
.begin(), mCompleteCallbacks
.end(),
8403 [&me
= *this](const auto& completeCallback
) {
8404 return me
.MaybeFireCallback(completeCallback
.get());
8407 // If that was the last database and we're supposed to be shutting down then
8409 if (mShutdownRequested
&& !mDatabases
.Count()) {
8410 MOZ_ASSERT(!mTransactions
.Count());
8415 bool ConnectionPool::MaybeFireCallback(DatabasesCompleteCallback
* aCallback
) {
8416 AssertIsOnOwningThread();
8417 MOZ_ASSERT(aCallback
);
8418 MOZ_ASSERT(!aCallback
->mDatabaseIds
.IsEmpty());
8419 MOZ_ASSERT(aCallback
->mCallback
);
8421 AUTO_PROFILER_LABEL("ConnectionPool::MaybeFireCallback", DOM
);
8423 if (std::any_of(aCallback
->mDatabaseIds
.begin(),
8424 aCallback
->mDatabaseIds
.end(),
8425 [&databases
= mDatabases
](const auto& databaseId
) {
8426 MOZ_ASSERT(!databaseId
.IsEmpty());
8428 return databases
.Get(databaseId
);
8433 Unused
<< aCallback
->mCallback
->Run();
8437 void ConnectionPool::PerformIdleDatabaseMaintenance(
8438 DatabaseInfo
& aDatabaseInfo
) {
8439 AssertIsOnOwningThread();
8440 MOZ_ASSERT(!aDatabaseInfo
.TotalTransactionCount());
8441 aDatabaseInfo
.mThreadInfo
.AssertValid();
8442 MOZ_ASSERT(aDatabaseInfo
.mIdle
);
8443 MOZ_ASSERT(!aDatabaseInfo
.mCloseOnIdle
);
8444 MOZ_ASSERT(!aDatabaseInfo
.mClosing
);
8445 MOZ_ASSERT(mIdleDatabases
.Contains(&aDatabaseInfo
));
8446 MOZ_ASSERT(!mDatabasesPerformingIdleMaintenance
.Contains(&aDatabaseInfo
));
8448 const bool neededCheckpoint
= aDatabaseInfo
.mNeedsCheckpoint
;
8450 aDatabaseInfo
.mNeedsCheckpoint
= false;
8451 aDatabaseInfo
.mIdle
= false;
8453 mDatabasesPerformingIdleMaintenance
.AppendElement(
8454 WrapNotNullUnchecked(&aDatabaseInfo
));
8456 MOZ_ALWAYS_SUCCEEDS(aDatabaseInfo
.mThreadInfo
.ThreadRef().Dispatch(
8457 MakeAndAddRef
<IdleConnectionRunnable
>(aDatabaseInfo
, neededCheckpoint
),
8458 NS_DISPATCH_NORMAL
));
8461 void ConnectionPool::CloseDatabase(DatabaseInfo
& aDatabaseInfo
) const {
8462 AssertIsOnOwningThread();
8463 MOZ_DIAGNOSTIC_ASSERT(!aDatabaseInfo
.TotalTransactionCount());
8464 aDatabaseInfo
.mThreadInfo
.AssertValid();
8465 MOZ_ASSERT(!aDatabaseInfo
.mClosing
);
8467 aDatabaseInfo
.mIdle
= false;
8468 aDatabaseInfo
.mNeedsCheckpoint
= false;
8469 aDatabaseInfo
.mClosing
= true;
8471 MOZ_ALWAYS_SUCCEEDS(aDatabaseInfo
.mThreadInfo
.ThreadRef().Dispatch(
8472 MakeAndAddRef
<CloseConnectionRunnable
>(aDatabaseInfo
),
8473 NS_DISPATCH_NORMAL
));
8476 bool ConnectionPool::CloseDatabaseWhenIdleInternal(
8477 const nsACString
& aDatabaseId
) {
8478 AssertIsOnOwningThread();
8479 MOZ_ASSERT(!aDatabaseId
.IsEmpty());
8481 AUTO_PROFILER_LABEL("ConnectionPool::CloseDatabaseWhenIdleInternal", DOM
);
8483 if (DatabaseInfo
* dbInfo
= mDatabases
.Get(aDatabaseId
)) {
8484 if (mIdleDatabases
.RemoveElement(dbInfo
) ||
8485 mDatabasesPerformingIdleMaintenance
.RemoveElement(dbInfo
)) {
8486 CloseDatabase(*dbInfo
);
8489 dbInfo
->mCloseOnIdle
.EnsureFlipped();
8498 ConnectionPool::ConnectionRunnable::ConnectionRunnable(
8499 DatabaseInfo
& aDatabaseInfo
)
8500 : Runnable("dom::indexedDB::ConnectionPool::ConnectionRunnable"),
8501 mDatabaseInfo(aDatabaseInfo
),
8502 mOwningEventTarget(GetCurrentSerialEventTarget()) {
8503 AssertIsOnBackgroundThread();
8504 MOZ_ASSERT(aDatabaseInfo
.mConnectionPool
);
8505 aDatabaseInfo
.mConnectionPool
->AssertIsOnOwningThread();
8506 MOZ_ASSERT(mOwningEventTarget
);
8510 ConnectionPool::IdleConnectionRunnable::Run() {
8511 MOZ_ASSERT(!mDatabaseInfo
.mIdle
);
8513 const nsCOMPtr
<nsIEventTarget
> owningThread
= std::move(mOwningEventTarget
);
8516 mDatabaseInfo
.AssertIsOnConnectionThread();
8518 // The connection could be null if EnsureConnection() didn't run or was not
8519 // successful in TransactionDatabaseOperationBase::RunOnConnectionThread().
8520 if (mDatabaseInfo
.mConnection
) {
8521 mDatabaseInfo
.mConnection
->DoIdleProcessing(mNeedsCheckpoint
);
8524 MOZ_ALWAYS_SUCCEEDS(owningThread
->Dispatch(this, NS_DISPATCH_NORMAL
));
8528 AssertIsOnBackgroundThread();
8530 RefPtr
<ConnectionPool
> connectionPool
= mDatabaseInfo
.mConnectionPool
;
8531 MOZ_ASSERT(connectionPool
);
8533 if (mDatabaseInfo
.mClosing
|| mDatabaseInfo
.TotalTransactionCount()) {
8534 MOZ_ASSERT(!connectionPool
->mDatabasesPerformingIdleMaintenance
.Contains(
8538 connectionPool
->mDatabasesPerformingIdleMaintenance
.RemoveElement(
8541 connectionPool
->NoteIdleDatabase(mDatabaseInfo
);
8548 ConnectionPool::CloseConnectionRunnable::Run() {
8549 AUTO_PROFILER_LABEL("ConnectionPool::CloseConnectionRunnable::Run", DOM
);
8551 if (mOwningEventTarget
) {
8552 MOZ_ASSERT(mDatabaseInfo
.mClosing
);
8554 const nsCOMPtr
<nsIEventTarget
> owningThread
= std::move(mOwningEventTarget
);
8556 // The connection could be null if EnsureConnection() didn't run or was not
8557 // successful in TransactionDatabaseOperationBase::RunOnConnectionThread().
8558 if (mDatabaseInfo
.mConnection
) {
8559 mDatabaseInfo
.AssertIsOnConnectionThread();
8561 mDatabaseInfo
.mConnection
->Close();
8563 IDB_DEBUG_LOG(("ConnectionPool closed connection 0x%p",
8564 mDatabaseInfo
.mConnection
.get()));
8566 mDatabaseInfo
.mConnection
= nullptr;
8569 mDatabaseInfo
.mDEBUGConnectionThread
= nullptr;
8573 MOZ_ALWAYS_SUCCEEDS(owningThread
->Dispatch(this, NS_DISPATCH_NORMAL
));
8577 RefPtr
<ConnectionPool
> connectionPool
= mDatabaseInfo
.mConnectionPool
;
8578 MOZ_ASSERT(connectionPool
);
8580 connectionPool
->NoteClosedDatabase(mDatabaseInfo
);
8584 ConnectionPool::DatabaseInfo::DatabaseInfo(ConnectionPool
* aConnectionPool
,
8585 const nsACString
& aDatabaseId
)
8586 : mConnectionPool(aConnectionPool
),
8587 mDatabaseId(aDatabaseId
),
8588 mReadTransactionCount(0),
8589 mWriteTransactionCount(0),
8590 mNeedsCheckpoint(false),
8595 mDEBUGConnectionThread(nullptr)
8598 AssertIsOnBackgroundThread();
8599 MOZ_ASSERT(aConnectionPool
);
8600 aConnectionPool
->AssertIsOnOwningThread();
8601 MOZ_ASSERT(!aDatabaseId
.IsEmpty());
8603 MOZ_COUNT_CTOR(ConnectionPool::DatabaseInfo
);
8606 ConnectionPool::DatabaseInfo::~DatabaseInfo() {
8607 AssertIsOnBackgroundThread();
8608 MOZ_ASSERT(!mConnection
);
8609 MOZ_ASSERT(mScheduledWriteTransactions
.IsEmpty());
8610 MOZ_ASSERT(!mRunningWriteTransaction
);
8611 mThreadInfo
.AssertEmpty();
8612 MOZ_ASSERT(!TotalTransactionCount());
8614 MOZ_COUNT_DTOR(ConnectionPool::DatabaseInfo
);
8617 ConnectionPool::DatabasesCompleteCallback::DatabasesCompleteCallback(
8618 nsTArray
<nsCString
>&& aDatabaseIds
, nsIRunnable
* aCallback
)
8619 : mDatabaseIds(std::move(aDatabaseIds
)), mCallback(aCallback
) {
8620 AssertIsOnBackgroundThread();
8621 MOZ_ASSERT(!mDatabaseIds
.IsEmpty());
8622 MOZ_ASSERT(aCallback
);
8624 MOZ_COUNT_CTOR(ConnectionPool::DatabasesCompleteCallback
);
8627 ConnectionPool::DatabasesCompleteCallback::~DatabasesCompleteCallback() {
8628 AssertIsOnBackgroundThread();
8630 MOZ_COUNT_DTOR(ConnectionPool::DatabasesCompleteCallback
);
8633 ConnectionPool::FinishCallbackWrapper::FinishCallbackWrapper(
8634 ConnectionPool
* aConnectionPool
, uint64_t aTransactionId
,
8635 FinishCallback
* aCallback
)
8636 : Runnable("dom::indexedDB::ConnectionPool::FinishCallbackWrapper"),
8637 mConnectionPool(aConnectionPool
),
8638 mCallback(aCallback
),
8639 mOwningEventTarget(GetCurrentSerialEventTarget()),
8640 mTransactionId(aTransactionId
),
8641 mHasRunOnce(false) {
8642 AssertIsOnBackgroundThread();
8643 MOZ_ASSERT(aConnectionPool
);
8644 MOZ_ASSERT(aCallback
);
8645 MOZ_ASSERT(mOwningEventTarget
);
8648 ConnectionPool::FinishCallbackWrapper::~FinishCallbackWrapper() {
8649 MOZ_ASSERT(!mConnectionPool
);
8650 MOZ_ASSERT(!mCallback
);
8653 nsresult
ConnectionPool::FinishCallbackWrapper::Run() {
8654 MOZ_ASSERT(mConnectionPool
);
8655 MOZ_ASSERT(mCallback
);
8656 MOZ_ASSERT(mOwningEventTarget
);
8658 AUTO_PROFILER_LABEL("ConnectionPool::FinishCallbackWrapper::Run", DOM
);
8661 MOZ_ASSERT(!IsOnBackgroundThread());
8665 Unused
<< mCallback
->Run();
8667 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
));
8672 mConnectionPool
->AssertIsOnOwningThread();
8673 MOZ_ASSERT(mHasRunOnce
);
8675 RefPtr
<ConnectionPool
> connectionPool
= std::move(mConnectionPool
);
8676 RefPtr
<FinishCallback
> callback
= std::move(mCallback
);
8678 callback
->TransactionFinishedBeforeUnblock();
8680 connectionPool
->NoteFinishedTransaction(mTransactionId
);
8682 callback
->TransactionFinishedAfterUnblock();
8687 uint32_t ConnectionPool::ThreadRunnable::sNextSerialNumber
= 0;
8689 ConnectionPool::ThreadRunnable::ThreadRunnable()
8690 : Runnable("dom::indexedDB::ConnectionPool::ThreadRunnable"),
8691 mSerialNumber(++sNextSerialNumber
) {
8692 AssertIsOnBackgroundThread();
8695 ConnectionPool::ThreadRunnable::~ThreadRunnable() {
8696 MOZ_ASSERT(!mFirstRun
);
8697 MOZ_ASSERT(!mContinueRunning
);
8700 nsresult
ConnectionPool::ThreadRunnable::Run() {
8701 MOZ_ASSERT(!IsOnBackgroundThread());
8702 MOZ_ASSERT(mContinueRunning
);
8705 mContinueRunning
.Flip();
8712 // Scope for the profiler label.
8713 AUTO_PROFILER_LABEL("ConnectionPool::ThreadRunnable::Run", DOM
);
8715 DebugOnly
<nsIThread
*> currentThread
= NS_GetCurrentThread();
8716 MOZ_ASSERT(currentThread
);
8719 if (kDEBUGTransactionThreadPriority
!=
8720 nsISupportsPriority::PRIORITY_NORMAL
) {
8722 "ConnectionPool thread debugging enabled, priority has been "
8725 nsCOMPtr
<nsISupportsPriority
> thread
= do_QueryInterface(currentThread
);
8728 MOZ_ALWAYS_SUCCEEDS(thread
->SetPriority(kDEBUGTransactionThreadPriority
));
8731 if (kDEBUGTransactionThreadSleepMS
) {
8733 "TransactionThreadPool thread debugging enabled, sleeping "
8734 "after every event!");
8739 SpinEventLoopUntil("ConnectionPool::ThreadRunnable"_ns
, [&]() -> bool {
8740 if (!mContinueRunning
) {
8745 if (kDEBUGTransactionThreadSleepMS
) {
8746 MOZ_ALWAYS_TRUE(PR_Sleep(PR_MillisecondsToInterval(
8747 kDEBUGTransactionThreadSleepMS
)) == PR_SUCCESS
);
8753 // MSVC can't stringify lambdas, so we have to separate the expression
8754 // generating the value from the assert itself.
8763 ConnectionPool::ThreadInfo::ThreadInfo() {
8764 AssertIsOnBackgroundThread();
8766 MOZ_COUNT_CTOR(ConnectionPool::ThreadInfo
);
8769 ConnectionPool::ThreadInfo::ThreadInfo(ThreadInfo
&& aOther
) noexcept
8770 : mThread(std::move(aOther
.mThread
)),
8771 mRunnable(std::move(aOther
.mRunnable
)) {
8772 AssertIsOnBackgroundThread();
8773 MOZ_ASSERT(mThread
);
8774 MOZ_ASSERT(mRunnable
);
8776 MOZ_COUNT_CTOR(ConnectionPool::ThreadInfo
);
8779 ConnectionPool::ThreadInfo::~ThreadInfo() {
8780 AssertIsOnBackgroundThread();
8782 MOZ_COUNT_DTOR(ConnectionPool::ThreadInfo
);
8785 ConnectionPool::IdleResource::IdleResource(const TimeStamp
& aIdleTime
)
8786 : mIdleTime(aIdleTime
) {
8787 AssertIsOnBackgroundThread();
8788 MOZ_ASSERT(!aIdleTime
.IsNull());
8790 MOZ_COUNT_CTOR(ConnectionPool::IdleResource
);
8793 ConnectionPool::IdleResource::~IdleResource() {
8794 AssertIsOnBackgroundThread();
8796 MOZ_COUNT_DTOR(ConnectionPool::IdleResource
);
8799 ConnectionPool::IdleDatabaseInfo::IdleDatabaseInfo(DatabaseInfo
& aDatabaseInfo
)
8801 TimeStamp::NowLoRes() +
8802 (aDatabaseInfo
.mIdle
8803 ? TimeDuration::FromMilliseconds(kConnectionIdleMaintenanceMS
)
8804 : TimeDuration::FromMilliseconds(kConnectionIdleCloseMS
))),
8805 mDatabaseInfo(WrapNotNullUnchecked(&aDatabaseInfo
)) {
8806 AssertIsOnBackgroundThread();
8808 MOZ_COUNT_CTOR(ConnectionPool::IdleDatabaseInfo
);
8811 ConnectionPool::IdleDatabaseInfo::~IdleDatabaseInfo() {
8812 AssertIsOnBackgroundThread();
8814 MOZ_COUNT_DTOR(ConnectionPool::IdleDatabaseInfo
);
8817 ConnectionPool::IdleThreadInfo::IdleThreadInfo(ThreadInfo aThreadInfo
)
8818 : IdleResource(TimeStamp::NowLoRes() +
8819 TimeDuration::FromMilliseconds(kConnectionThreadIdleMS
)),
8820 mThreadInfo(std::move(aThreadInfo
)) {
8821 AssertIsOnBackgroundThread();
8822 mThreadInfo
.AssertValid();
8824 MOZ_COUNT_CTOR(ConnectionPool::IdleThreadInfo
);
8827 ConnectionPool::IdleThreadInfo::~IdleThreadInfo() {
8828 AssertIsOnBackgroundThread();
8830 MOZ_COUNT_DTOR(ConnectionPool::IdleThreadInfo
);
8833 ConnectionPool::TransactionInfo::TransactionInfo(
8834 DatabaseInfo
& aDatabaseInfo
, const nsID
& aBackgroundChildLoggingId
,
8835 const nsACString
& aDatabaseId
, uint64_t aTransactionId
,
8836 int64_t aLoggingSerialNumber
, const nsTArray
<nsString
>& aObjectStoreNames
,
8837 bool aIsWriteTransaction
, TransactionDatabaseOperationBase
* aTransactionOp
)
8838 : mDatabaseInfo(aDatabaseInfo
),
8839 mBackgroundChildLoggingId(aBackgroundChildLoggingId
),
8840 mDatabaseId(aDatabaseId
),
8841 mTransactionId(aTransactionId
),
8842 mLoggingSerialNumber(aLoggingSerialNumber
),
8843 mObjectStoreNames(aObjectStoreNames
.Clone()),
8844 mIsWriteTransaction(aIsWriteTransaction
),
8846 AssertIsOnBackgroundThread();
8847 aDatabaseInfo
.mConnectionPool
->AssertIsOnOwningThread();
8849 MOZ_COUNT_CTOR(ConnectionPool::TransactionInfo
);
8851 if (aTransactionOp
) {
8852 mQueuedRunnables
.AppendElement(aTransactionOp
);
8856 ConnectionPool::TransactionInfo::~TransactionInfo() {
8857 AssertIsOnBackgroundThread();
8858 MOZ_ASSERT(!mBlockedOn
.Count());
8859 MOZ_ASSERT(mQueuedRunnables
.IsEmpty());
8860 MOZ_ASSERT(!mRunning
);
8861 MOZ_ASSERT(mFinished
);
8863 MOZ_COUNT_DTOR(ConnectionPool::TransactionInfo
);
8866 void ConnectionPool::TransactionInfo::AddBlockingTransaction(
8867 TransactionInfo
& aTransactionInfo
) {
8868 AssertIsOnBackgroundThread();
8870 // XXX Does it really make sense to have both mBlocking and mBlockingOrdered,
8871 // just to reduce the algorithmic complexity of this Contains check? This was
8872 // mentioned in the context of Bug 1290853, but no real justification was
8873 // given. There was the suggestion of encapsulating this in an
8874 // insertion-ordered hashtable implementation, which seems like a good idea.
8875 // If we had that, this would be the appropriate data structure to use here.
8876 if (mBlocking
.EnsureInserted(&aTransactionInfo
)) {
8877 mBlockingOrdered
.AppendElement(WrapNotNullUnchecked(&aTransactionInfo
));
8881 void ConnectionPool::TransactionInfo::RemoveBlockingTransactions() {
8882 AssertIsOnBackgroundThread();
8884 for (const auto blockedInfo
: mBlockingOrdered
) {
8885 blockedInfo
->MaybeUnblock(*this);
8889 mBlockingOrdered
.Clear();
8892 void ConnectionPool::TransactionInfo::MaybeUnblock(
8893 TransactionInfo
& aTransactionInfo
) {
8894 AssertIsOnBackgroundThread();
8895 MOZ_ASSERT(mBlockedOn
.Contains(&aTransactionInfo
));
8897 mBlockedOn
.Remove(&aTransactionInfo
);
8898 if (mBlockedOn
.IsEmpty()) {
8899 ConnectionPool
* connectionPool
= mDatabaseInfo
.mConnectionPool
;
8900 MOZ_ASSERT(connectionPool
);
8901 connectionPool
->AssertIsOnOwningThread();
8903 Unused
<< connectionPool
->ScheduleTransaction(
8905 /* aFromQueuedTransactions */ false);
8909 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
8910 ConnectionPool::TransactionInfoPair::TransactionInfoPair() {
8911 AssertIsOnBackgroundThread();
8913 MOZ_COUNT_CTOR(ConnectionPool::TransactionInfoPair
);
8916 ConnectionPool::TransactionInfoPair::~TransactionInfoPair() {
8917 AssertIsOnBackgroundThread();
8919 MOZ_COUNT_DTOR(ConnectionPool::TransactionInfoPair
);
8923 /*******************************************************************************
8925 ******************************************************************************/
8927 bool FullObjectStoreMetadata::HasLiveIndexes() const {
8928 AssertIsOnBackgroundThread();
8930 return std::any_of(mIndexes
.Values().cbegin(), mIndexes
.Values().cend(),
8931 [](const auto& entry
) { return !entry
->mDeleted
; });
8934 SafeRefPtr
<FullDatabaseMetadata
> FullDatabaseMetadata::Duplicate() const {
8935 AssertIsOnBackgroundThread();
8937 // FullDatabaseMetadata contains two hash tables of pointers that we need to
8938 // duplicate so we can't just use the copy constructor.
8939 auto newMetadata
= MakeSafeRefPtr
<FullDatabaseMetadata
>(mCommonMetadata
);
8941 newMetadata
->mDatabaseId
= mDatabaseId
;
8942 newMetadata
->mFilePath
= mFilePath
;
8943 newMetadata
->mNextObjectStoreId
= mNextObjectStoreId
;
8944 newMetadata
->mNextIndexId
= mNextIndexId
;
8946 for (const auto& objectStoreEntry
: mObjectStores
) {
8947 const auto& objectStoreValue
= objectStoreEntry
.GetData();
8949 auto newOSMetadata
= MakeSafeRefPtr
<FullObjectStoreMetadata
>(
8950 objectStoreValue
->mCommonMetadata
, [&objectStoreValue
] {
8951 const auto&& srcLocked
= objectStoreValue
->mAutoIncrementIds
.Lock();
8955 for (const auto& indexEntry
: objectStoreValue
->mIndexes
) {
8956 const auto& value
= indexEntry
.GetData();
8958 auto newIndexMetadata
= MakeSafeRefPtr
<FullIndexMetadata
>();
8960 newIndexMetadata
->mCommonMetadata
= value
->mCommonMetadata
;
8962 if (NS_WARN_IF(!newOSMetadata
->mIndexes
.InsertOrUpdate(
8963 indexEntry
.GetKey(), std::move(newIndexMetadata
), fallible
))) {
8968 MOZ_ASSERT(objectStoreValue
->mIndexes
.Count() ==
8969 newOSMetadata
->mIndexes
.Count());
8971 if (NS_WARN_IF(!newMetadata
->mObjectStores
.InsertOrUpdate(
8972 objectStoreEntry
.GetKey(), std::move(newOSMetadata
), fallible
))) {
8977 MOZ_ASSERT(mObjectStores
.Count() == newMetadata
->mObjectStores
.Count());
8982 DatabaseLoggingInfo::~DatabaseLoggingInfo() {
8983 AssertIsOnBackgroundThread();
8985 if (gLoggingInfoHashtable
) {
8986 const nsID
& backgroundChildLoggingId
=
8987 mLoggingInfo
.backgroundChildLoggingId();
8989 MOZ_ASSERT(gLoggingInfoHashtable
->Get(backgroundChildLoggingId
) == this);
8991 gLoggingInfoHashtable
->Remove(backgroundChildLoggingId
);
8995 /*******************************************************************************
8997 ******************************************************************************/
8999 Factory::Factory(RefPtr
<DatabaseLoggingInfo
> aLoggingInfo
)
9000 : mLoggingInfo(std::move(aLoggingInfo
))
9003 mActorDestroyed(false)
9006 AssertIsOnBackgroundThread();
9007 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
9010 Factory::~Factory() { MOZ_ASSERT(mActorDestroyed
); }
9013 SafeRefPtr
<Factory
> Factory::Create(const LoggingInfo
& aLoggingInfo
) {
9014 AssertIsOnBackgroundThread();
9015 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
9017 // Balanced in ActoryDestroy().
9018 IncreaseBusyCount();
9020 MOZ_ASSERT(gLoggingInfoHashtable
);
9021 RefPtr
<DatabaseLoggingInfo
> loggingInfo
=
9022 gLoggingInfoHashtable
->WithEntryHandle(
9023 aLoggingInfo
.backgroundChildLoggingId(), [&](auto&& entry
) {
9025 [[maybe_unused
]] const auto& loggingInfo
= entry
.Data();
9026 MOZ_ASSERT(aLoggingInfo
.backgroundChildLoggingId() ==
9029 NS_WARNING_ASSERTION(
9030 aLoggingInfo
.nextTransactionSerialNumber() ==
9031 loggingInfo
->mLoggingInfo
.nextTransactionSerialNumber(),
9032 "NextTransactionSerialNumber doesn't match!");
9033 NS_WARNING_ASSERTION(
9034 aLoggingInfo
.nextVersionChangeTransactionSerialNumber() ==
9035 loggingInfo
->mLoggingInfo
9036 .nextVersionChangeTransactionSerialNumber(),
9037 "NextVersionChangeTransactionSerialNumber doesn't match!");
9038 NS_WARNING_ASSERTION(
9039 aLoggingInfo
.nextRequestSerialNumber() ==
9040 loggingInfo
->mLoggingInfo
.nextRequestSerialNumber(),
9041 "NextRequestSerialNumber doesn't match!");
9044 entry
.Insert(new DatabaseLoggingInfo(aLoggingInfo
));
9047 return do_AddRef(entry
.Data());
9050 return MakeSafeRefPtr
<Factory
>(std::move(loggingInfo
));
9053 void Factory::ActorDestroy(ActorDestroyReason aWhy
) {
9054 AssertIsOnBackgroundThread();
9055 MOZ_ASSERT(!mActorDestroyed
);
9058 mActorDestroyed
= true;
9061 // Match the IncreaseBusyCount in Create().
9062 DecreaseBusyCount();
9065 mozilla::ipc::IPCResult
Factory::RecvDeleteMe() {
9066 AssertIsOnBackgroundThread();
9067 MOZ_ASSERT(!mActorDestroyed
);
9069 QM_WARNONLY_TRY(OkIf(PBackgroundIDBFactoryParent::Send__delete__(this)));
9074 PBackgroundIDBFactoryRequestParent
*
9075 Factory::AllocPBackgroundIDBFactoryRequestParent(
9076 const FactoryRequestParams
& aParams
) {
9077 AssertIsOnBackgroundThread();
9078 MOZ_ASSERT(aParams
.type() != FactoryRequestParams::T__None
);
9080 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
9084 const CommonFactoryRequestParams
* commonParams
;
9086 switch (aParams
.type()) {
9087 case FactoryRequestParams::TOpenDatabaseRequestParams
: {
9088 const OpenDatabaseRequestParams
& params
=
9089 aParams
.get_OpenDatabaseRequestParams();
9090 commonParams
= ¶ms
.commonParams();
9094 case FactoryRequestParams::TDeleteDatabaseRequestParams
: {
9095 const DeleteDatabaseRequestParams
& params
=
9096 aParams
.get_DeleteDatabaseRequestParams();
9097 commonParams
= ¶ms
.commonParams();
9102 MOZ_CRASH("Should never get here!");
9105 MOZ_ASSERT(commonParams
);
9107 const DatabaseMetadata
& metadata
= commonParams
->metadata();
9108 if (NS_AUUF_OR_WARN_IF(!IsValidPersistenceType(metadata
.persistenceType()))) {
9112 const PrincipalInfo
& principalInfo
= commonParams
->principalInfo();
9113 if (NS_AUUF_OR_WARN_IF(principalInfo
.type() ==
9114 PrincipalInfo::TNullPrincipalInfo
)) {
9118 if (NS_AUUF_OR_WARN_IF(
9119 principalInfo
.type() == PrincipalInfo::TSystemPrincipalInfo
&&
9120 metadata
.persistenceType() != PERSISTENCE_TYPE_PERSISTENT
)) {
9124 if (NS_AUUF_OR_WARN_IF(!QuotaManager::IsPrincipalInfoValid(principalInfo
))) {
9128 RefPtr
<ThreadsafeContentParentHandle
> contentHandle
=
9129 BackgroundParent::GetContentParentHandle(Manager());
9131 auto actor
= [&]() -> RefPtr
<FactoryOp
> {
9132 if (aParams
.type() == FactoryRequestParams::TOpenDatabaseRequestParams
) {
9133 return MakeRefPtr
<OpenDatabaseOp
>(
9134 SafeRefPtrFromThis(), std::move(contentHandle
), *commonParams
);
9136 return MakeRefPtr
<DeleteDatabaseOp
>(
9137 SafeRefPtrFromThis(), std::move(contentHandle
), *commonParams
);
9141 gFactoryOps
->AppendElement(actor
);
9143 // Balanced in CleanupMetadata() which is/must always called by SendResults().
9144 IncreaseBusyCount();
9146 // Transfer ownership to IPDL.
9147 return actor
.forget().take();
9150 mozilla::ipc::IPCResult
Factory::RecvPBackgroundIDBFactoryRequestConstructor(
9151 PBackgroundIDBFactoryRequestParent
* aActor
,
9152 const FactoryRequestParams
& aParams
) {
9153 AssertIsOnBackgroundThread();
9155 MOZ_ASSERT(aParams
.type() != FactoryRequestParams::T__None
);
9156 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
9158 auto* op
= static_cast<FactoryOp
*>(aActor
);
9160 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(op
));
9164 bool Factory::DeallocPBackgroundIDBFactoryRequestParent(
9165 PBackgroundIDBFactoryRequestParent
* aActor
) {
9166 AssertIsOnBackgroundThread();
9169 // Transfer ownership back from IPDL.
9170 RefPtr
<FactoryOp
> op
= dont_AddRef(static_cast<FactoryOp
*>(aActor
));
9174 PBackgroundIDBDatabaseParent
* Factory::AllocPBackgroundIDBDatabaseParent(
9175 const DatabaseSpec
& aSpec
, PBackgroundIDBFactoryRequestParent
* aRequest
) {
9177 "PBackgroundIDBDatabaseParent actors should be constructed "
9181 bool Factory::DeallocPBackgroundIDBDatabaseParent(
9182 PBackgroundIDBDatabaseParent
* aActor
) {
9183 AssertIsOnBackgroundThread();
9186 RefPtr
<Database
> database
= dont_AddRef(static_cast<Database
*>(aActor
));
9190 /*******************************************************************************
9191 * WaitForTransactionsHelper
9192 ******************************************************************************/
9194 void WaitForTransactionsHelper::WaitForTransactions() {
9195 MOZ_ASSERT(mState
== State::Initial
);
9197 Unused
<< this->Run();
9200 void WaitForTransactionsHelper::MaybeWaitForTransactions() {
9201 AssertIsOnBackgroundThread();
9202 MOZ_ASSERT(mState
== State::Initial
);
9204 RefPtr
<ConnectionPool
> connectionPool
= gConnectionPool
.get();
9205 if (connectionPool
) {
9206 mState
= State::WaitingForTransactions
;
9208 connectionPool
->WaitForDatabasesToComplete(nsTArray
<nsCString
>{mDatabaseId
},
9216 void WaitForTransactionsHelper::CallCallback() {
9217 AssertIsOnBackgroundThread();
9218 MOZ_ASSERT(mState
== State::Initial
||
9219 mState
== State::WaitingForTransactions
);
9221 const nsCOMPtr
<nsIRunnable
> callback
= std::move(mCallback
);
9225 mState
= State::Complete
;
9229 WaitForTransactionsHelper::Run() {
9230 MOZ_ASSERT(mState
!= State::Complete
);
9231 MOZ_ASSERT(mCallback
);
9234 case State::Initial
:
9235 MaybeWaitForTransactions();
9238 case State::WaitingForTransactions
:
9243 MOZ_CRASH("Should never get here!");
9249 /*******************************************************************************
9251 ******************************************************************************/
9253 Database::Database(SafeRefPtr
<Factory
> aFactory
,
9254 const PrincipalInfo
& aPrincipalInfo
,
9255 const Maybe
<ContentParentId
>& aOptionalContentParentId
,
9256 const quota::OriginMetadata
& aOriginMetadata
,
9257 uint32_t aTelemetryId
,
9258 SafeRefPtr
<FullDatabaseMetadata
> aMetadata
,
9259 SafeRefPtr
<DatabaseFileManager
> aFileManager
,
9260 RefPtr
<DirectoryLock
> aDirectoryLock
,
9261 bool aChromeWriteAccessAllowed
, bool aInPrivateBrowsing
,
9262 const Maybe
<const CipherKey
>& aMaybeKey
)
9263 : mFactory(std::move(aFactory
)),
9264 mMetadata(std::move(aMetadata
)),
9265 mFileManager(std::move(aFileManager
)),
9266 mDirectoryLock(std::move(aDirectoryLock
)),
9267 mPrincipalInfo(aPrincipalInfo
),
9268 mOptionalContentParentId(aOptionalContentParentId
),
9269 mOriginMetadata(aOriginMetadata
),
9270 mId(mMetadata
->mDatabaseId
),
9271 mFilePath(mMetadata
->mFilePath
),
9273 mTelemetryId(aTelemetryId
),
9274 mPersistenceType(mMetadata
->mCommonMetadata
.persistenceType()),
9275 mChromeWriteAccessAllowed(aChromeWriteAccessAllowed
),
9276 mInPrivateBrowsing(aInPrivateBrowsing
),
9277 mBackgroundThread(GetCurrentSerialEventTarget())
9280 mAllBlobsUnmapped(false)
9283 AssertIsOnBackgroundThread();
9284 MOZ_ASSERT(mFactory
);
9285 MOZ_ASSERT(mMetadata
);
9286 MOZ_ASSERT(mFileManager
);
9287 MOZ_ASSERT_IF(aChromeWriteAccessAllowed
,
9288 aPrincipalInfo
.type() == PrincipalInfo::TSystemPrincipalInfo
);
9290 MOZ_ASSERT(mDirectoryLock
);
9291 MOZ_ASSERT(mDirectoryLock
->Id() >= 0);
9292 mDirectoryLockId
= mDirectoryLock
->Id();
9295 template <typename T
>
9296 bool Database::InvalidateAll(const nsTBaseHashSet
<nsPtrHashKey
<T
>>& aTable
) {
9297 AssertIsOnBackgroundThread();
9299 const uint32_t count
= aTable
.Count();
9304 // XXX Does this really need to be fallible?
9305 QM_TRY_INSPECT(const auto& elementsToInvalidate
,
9306 TransformIntoNewArray(
9307 aTable
, [](const auto& entry
) { return entry
; }, fallible
),
9310 IDB_REPORT_INTERNAL_ERR();
9312 for (const auto& elementToInvalidate
: elementsToInvalidate
) {
9313 MOZ_ASSERT(elementToInvalidate
);
9315 elementToInvalidate
->Invalidate();
9321 void Database::Invalidate() {
9322 AssertIsOnBackgroundThread();
9328 mInvalidated
.Flip();
9330 if (mActorWasAlive
&& !mActorDestroyed
) {
9331 Unused
<< SendInvalidate();
9334 QM_WARNONLY_TRY(OkIf(InvalidateAll(mTransactions
)));
9336 MOZ_ALWAYS_TRUE(CloseInternal());
9339 nsresult
Database::EnsureConnection() {
9340 MOZ_ASSERT(!NS_IsMainThread());
9341 MOZ_ASSERT(!IsOnBackgroundThread());
9343 AUTO_PROFILER_LABEL("Database::EnsureConnection", DOM
);
9345 if (!mConnection
|| !mConnection
->HasStorageConnection()) {
9346 QM_TRY_UNWRAP(mConnection
, gConnectionPool
->GetOrCreateConnection(*this));
9349 AssertIsOnConnectionThread();
9354 bool Database::RegisterTransaction(TransactionBase
& aTransaction
) {
9355 AssertIsOnBackgroundThread();
9356 MOZ_ASSERT(!mTransactions
.Contains(&aTransaction
));
9357 MOZ_ASSERT(mDirectoryLock
);
9358 MOZ_ASSERT(!mInvalidated
);
9359 MOZ_ASSERT(!mClosed
);
9361 if (NS_WARN_IF(!mTransactions
.Insert(&aTransaction
, fallible
))) {
9368 void Database::UnregisterTransaction(TransactionBase
& aTransaction
) {
9369 AssertIsOnBackgroundThread();
9370 MOZ_ASSERT(mTransactions
.Contains(&aTransaction
));
9372 mTransactions
.Remove(&aTransaction
);
9374 MaybeCloseConnection();
9377 void Database::SetActorAlive() {
9378 AssertIsOnBackgroundThread();
9379 MOZ_ASSERT(!mActorDestroyed
);
9381 mActorWasAlive
.Flip();
9383 // This reference will be absorbed by IPDL and released when the actor is
9388 void Database::MapBlob(const IPCBlob
& aIPCBlob
,
9389 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
) {
9390 AssertIsOnBackgroundThread();
9392 const RemoteLazyStream
& stream
= aIPCBlob
.inputStream();
9393 MOZ_ASSERT(stream
.type() == RemoteLazyStream::TRemoteLazyInputStream
);
9396 MOZ_ALWAYS_SUCCEEDS(
9397 stream
.get_RemoteLazyInputStream()->GetInternalStreamID(id
));
9399 MOZ_ASSERT(!mMappedBlobs
.Contains(id
));
9400 mMappedBlobs
.InsertOrUpdate(id
, std::move(aFileInfo
));
9402 RefPtr
<UnmapBlobCallback
> callback
=
9403 new UnmapBlobCallback(SafeRefPtrFromThis());
9405 auto storage
= RemoteLazyInputStreamStorage::Get();
9406 MOZ_ASSERT(storage
.isOk());
9407 storage
.inspect()->StoreCallback(id
, callback
);
9410 void Database::Stringify(nsACString
& aResult
) const {
9411 AssertIsOnBackgroundThread();
9413 constexpr auto kQuotaGenericDelimiterString
= "|"_ns
;
9416 "DirectoryLock:"_ns
+ IntToCString(!!mDirectoryLock
) +
9417 kQuotaGenericDelimiterString
+
9419 "Transactions:"_ns
+ IntToCString(mTransactions
.Count()) +
9420 kQuotaGenericDelimiterString
+
9422 "OtherProcessActor:"_ns
+
9424 BackgroundParent::IsOtherProcessActor(GetBackgroundParent())) +
9425 kQuotaGenericDelimiterString
+
9427 "Origin:"_ns
+ AnonymizedOriginString(mOriginMetadata
.mOrigin
) +
9428 kQuotaGenericDelimiterString
+
9430 "PersistenceType:"_ns
+ PersistenceTypeToString(mPersistenceType
) +
9431 kQuotaGenericDelimiterString
+
9433 "Closed:"_ns
+ IntToCString(static_cast<bool>(mClosed
)) +
9434 kQuotaGenericDelimiterString
+
9436 "Invalidated:"_ns
+ IntToCString(static_cast<bool>(mInvalidated
)) +
9437 kQuotaGenericDelimiterString
+
9439 "ActorWasAlive:"_ns
+ IntToCString(static_cast<bool>(mActorWasAlive
)) +
9440 kQuotaGenericDelimiterString
+
9442 "ActorDestroyed:"_ns
+ IntToCString(static_cast<bool>(mActorDestroyed
)));
9445 SafeRefPtr
<DatabaseFileInfo
> Database::GetBlob(const IPCBlob
& aIPCBlob
) {
9446 AssertIsOnBackgroundThread();
9448 RefPtr
<RemoteLazyInputStream
> lazyStream
;
9449 switch (aIPCBlob
.inputStream().type()) {
9450 case RemoteLazyStream::TIPCStream
: {
9451 const InputStreamParams
& inputStreamParams
=
9452 aIPCBlob
.inputStream().get_IPCStream().stream();
9453 if (inputStreamParams
.type() !=
9454 InputStreamParams::TRemoteLazyInputStreamParams
) {
9457 lazyStream
= inputStreamParams
.get_RemoteLazyInputStreamParams().stream();
9460 case RemoteLazyStream::TRemoteLazyInputStream
:
9461 lazyStream
= aIPCBlob
.inputStream().get_RemoteLazyInputStream();
9464 MOZ_ASSERT_UNREACHABLE("Unknown RemoteLazyStream type");
9469 MOZ_ASSERT_UNREACHABLE("Unexpected null stream");
9474 nsresult rv
= lazyStream
->GetInternalStreamID(id
);
9475 if (NS_FAILED(rv
)) {
9476 MOZ_ASSERT_UNREACHABLE(
9477 "Received RemoteLazyInputStream doesn't have an actor connection");
9481 const auto fileInfo
= mMappedBlobs
.Lookup(id
);
9482 return fileInfo
? fileInfo
->clonePtr() : nullptr;
9485 void Database::UnmapBlob(const nsID
& aID
) {
9486 AssertIsOnBackgroundThread();
9488 MOZ_ASSERT_IF(!mAllBlobsUnmapped
, mMappedBlobs
.Contains(aID
));
9489 mMappedBlobs
.Remove(aID
);
9492 void Database::UnmapAllBlobs() {
9493 AssertIsOnBackgroundThread();
9496 mAllBlobsUnmapped
= true;
9499 mMappedBlobs
.Clear();
9502 bool Database::CloseInternal() {
9503 AssertIsOnBackgroundThread();
9506 if (NS_WARN_IF(!IsInvalidated())) {
9507 // Signal misbehaving child for sending the close message twice.
9511 // Ignore harmless race when we just invalidated the database.
9517 if (gConnectionPool
) {
9518 gConnectionPool
->CloseDatabaseWhenIdle(Id());
9521 DatabaseActorInfo
* info
;
9522 MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable
->Get(Id(), &info
));
9524 MOZ_ASSERT(info
->mLiveDatabases
.Contains(this));
9526 if (info
->mWaitingFactoryOp
) {
9527 info
->mWaitingFactoryOp
->NoteDatabaseClosed(this);
9530 MaybeCloseConnection();
9535 void Database::MaybeCloseConnection() {
9536 AssertIsOnBackgroundThread();
9538 if (!mTransactions
.Count() && IsClosed() && mDirectoryLock
) {
9539 nsCOMPtr
<nsIRunnable
> callback
=
9540 NewRunnableMethod("dom::indexedDB::Database::ConnectionClosedCallback",
9541 this, &Database::ConnectionClosedCallback
);
9543 RefPtr
<WaitForTransactionsHelper
> helper
=
9544 new WaitForTransactionsHelper(Id(), callback
);
9545 helper
->WaitForTransactions();
9549 void Database::ConnectionClosedCallback() {
9550 AssertIsOnBackgroundThread();
9551 MOZ_ASSERT(mClosed
);
9552 MOZ_ASSERT(!mTransactions
.Count());
9554 mDirectoryLock
= nullptr;
9560 if (IsInvalidated() && IsActorAlive()) {
9561 // Step 3 and 4 of "5.2 Closing a Database":
9562 // 1. Wait for all transactions to complete.
9563 // 2. Fire a close event if forced flag is set, i.e., IsInvalidated() in our
9565 Unused
<< SendCloseAfterInvalidationComplete();
9569 void Database::CleanupMetadata() {
9570 AssertIsOnBackgroundThread();
9572 DatabaseActorInfo
* info
;
9573 MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable
->Get(Id(), &info
));
9574 MOZ_ALWAYS_TRUE(info
->mLiveDatabases
.RemoveElement(this));
9576 QuotaManager::MaybeRecordQuotaClientShutdownStep(
9577 quota::Client::IDB
, "Live database entry removed"_ns
);
9579 if (info
->mLiveDatabases
.IsEmpty()) {
9580 MOZ_ASSERT(!info
->mWaitingFactoryOp
||
9581 !info
->mWaitingFactoryOp
->HasBlockedDatabases());
9582 gLiveDatabaseHashtable
->Remove(Id());
9584 QuotaManager::MaybeRecordQuotaClientShutdownStep(
9585 quota::Client::IDB
, "gLiveDatabaseHashtable entry removed"_ns
);
9588 // Match the IncreaseBusyCount in OpenDatabaseOp::EnsureDatabaseActor().
9589 DecreaseBusyCount();
9592 void Database::ActorDestroy(ActorDestroyReason aWhy
) {
9593 AssertIsOnBackgroundThread();
9595 mActorDestroyed
.Flip();
9597 if (!IsInvalidated()) {
9602 PBackgroundIDBDatabaseFileParent
*
9603 Database::AllocPBackgroundIDBDatabaseFileParent(const IPCBlob
& aIPCBlob
) {
9604 AssertIsOnBackgroundThread();
9606 SafeRefPtr
<DatabaseFileInfo
> fileInfo
= GetBlob(aIPCBlob
);
9607 RefPtr
<DatabaseFile
> actor
;
9610 actor
= new DatabaseFile(std::move(fileInfo
));
9612 // This is a blob we haven't seen before.
9613 fileInfo
= mFileManager
->CreateFileInfo();
9614 if (NS_WARN_IF(!fileInfo
)) {
9618 actor
= new DatabaseFile(IPCBlobUtils::Deserialize(aIPCBlob
),
9619 std::move(fileInfo
));
9624 return actor
.forget().take();
9627 bool Database::DeallocPBackgroundIDBDatabaseFileParent(
9628 PBackgroundIDBDatabaseFileParent
* aActor
) {
9629 AssertIsOnBackgroundThread();
9632 RefPtr
<DatabaseFile
> actor
= dont_AddRef(static_cast<DatabaseFile
*>(aActor
));
9636 already_AddRefed
<PBackgroundIDBTransactionParent
>
9637 Database::AllocPBackgroundIDBTransactionParent(
9638 const nsTArray
<nsString
>& aObjectStoreNames
, const Mode
& aMode
) {
9639 AssertIsOnBackgroundThread();
9641 // Once a database is closed it must not try to open new transactions.
9642 if (NS_WARN_IF(mClosed
)) {
9643 MOZ_ASSERT_UNLESS_FUZZING(mInvalidated
);
9647 if (NS_AUUF_OR_WARN_IF(aObjectStoreNames
.IsEmpty())) {
9651 if (NS_AUUF_OR_WARN_IF(aMode
!= IDBTransaction::Mode::ReadOnly
&&
9652 aMode
!= IDBTransaction::Mode::ReadWrite
&&
9653 aMode
!= IDBTransaction::Mode::ReadWriteFlush
&&
9654 aMode
!= IDBTransaction::Mode::Cleanup
)) {
9658 // If this is a readwrite transaction to a chrome database make sure the child
9659 // has write access.
9660 // XXX: Maybe add NS_AUUF_OR_WARN_IF also here, see bug 1758875
9661 if (NS_WARN_IF((aMode
== IDBTransaction::Mode::ReadWrite
||
9662 aMode
== IDBTransaction::Mode::ReadWriteFlush
||
9663 aMode
== IDBTransaction::Mode::Cleanup
) &&
9664 mPrincipalInfo
.type() == PrincipalInfo::TSystemPrincipalInfo
&&
9665 !mChromeWriteAccessAllowed
)) {
9669 const ObjectStoreTable
& objectStores
= mMetadata
->mObjectStores
;
9670 const uint32_t nameCount
= aObjectStoreNames
.Length();
9672 if (NS_AUUF_OR_WARN_IF(nameCount
> objectStores
.Count())) {
9677 auto objectStoreMetadatas
,
9678 TransformIntoNewArrayAbortOnErr(
9680 [lastName
= Maybe
<const nsString
&>{},
9681 &objectStores
](const nsString
& name
) mutable
9682 -> mozilla::Result
<SafeRefPtr
<FullObjectStoreMetadata
>, nsresult
> {
9684 // Make sure that this name is sorted properly and not a
9686 if (NS_AUUF_OR_WARN_IF(name
<= lastName
.ref())) {
9687 return Err(NS_ERROR_FAILURE
);
9690 lastName
= SomeRef(name
);
9692 const auto foundIt
=
9693 std::find_if(objectStores
.cbegin(), objectStores
.cend(),
9694 [&name
](const auto& entry
) {
9695 const auto& value
= entry
.GetData();
9696 MOZ_ASSERT(entry
.GetKey());
9697 return name
== value
->mCommonMetadata
.name() &&
9700 if (foundIt
== objectStores
.cend()) {
9701 MOZ_ASSERT_UNLESS_FUZZING(false, "ObjectStore not found.");
9702 return Err(NS_ERROR_FAILURE
);
9705 return foundIt
->GetData().clonePtr();
9710 return MakeSafeRefPtr
<NormalTransaction
>(SafeRefPtrFromThis(), aMode
,
9711 std::move(objectStoreMetadatas
))
9715 mozilla::ipc::IPCResult
Database::RecvPBackgroundIDBTransactionConstructor(
9716 PBackgroundIDBTransactionParent
* aActor
,
9717 nsTArray
<nsString
>&& aObjectStoreNames
, const Mode
& aMode
) {
9718 AssertIsOnBackgroundThread();
9720 MOZ_ASSERT(!aObjectStoreNames
.IsEmpty());
9721 MOZ_ASSERT(aMode
== IDBTransaction::Mode::ReadOnly
||
9722 aMode
== IDBTransaction::Mode::ReadWrite
||
9723 aMode
== IDBTransaction::Mode::ReadWriteFlush
||
9724 aMode
== IDBTransaction::Mode::Cleanup
);
9725 MOZ_ASSERT(!mClosed
);
9727 if (IsInvalidated()) {
9728 // This is an expected race. We don't want the child to die here, just don't
9729 // actually do any work.
9733 if (!gConnectionPool
) {
9734 gConnectionPool
= new ConnectionPool();
9737 auto* transaction
= static_cast<NormalTransaction
*>(aActor
);
9739 RefPtr
<StartTransactionOp
> startOp
= new StartTransactionOp(
9740 SafeRefPtr
{transaction
, AcquireStrongRefFromRawPtr
{}});
9742 uint64_t transactionId
= startOp
->StartOnConnectionPool(
9743 GetLoggingInfo()->Id(), mMetadata
->mDatabaseId
,
9744 transaction
->LoggingSerialNumber(), aObjectStoreNames
,
9745 aMode
!= IDBTransaction::Mode::ReadOnly
);
9747 transaction
->Init(transactionId
);
9749 if (NS_WARN_IF(!RegisterTransaction(*transaction
))) {
9750 IDB_REPORT_INTERNAL_ERR();
9751 transaction
->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
, /* aForce */ false);
9758 mozilla::ipc::IPCResult
Database::RecvDeleteMe() {
9759 AssertIsOnBackgroundThread();
9760 MOZ_ASSERT(!mActorDestroyed
);
9762 QM_WARNONLY_TRY(OkIf(PBackgroundIDBDatabaseParent::Send__delete__(this)));
9767 mozilla::ipc::IPCResult
Database::RecvBlocked() {
9768 AssertIsOnBackgroundThread();
9770 if (NS_WARN_IF(mClosed
)) {
9771 return IPC_FAIL(this, "Database already closed!");
9774 DatabaseActorInfo
* info
;
9775 MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable
->Get(Id(), &info
));
9776 MOZ_ASSERT(info
->mLiveDatabases
.Contains(this));
9778 if (NS_WARN_IF(!info
->mWaitingFactoryOp
)) {
9779 return IPC_FAIL(this, "Database info has no mWaitingFactoryOp!");
9782 info
->mWaitingFactoryOp
->NoteDatabaseBlocked(this);
9787 mozilla::ipc::IPCResult
Database::RecvClose() {
9788 AssertIsOnBackgroundThread();
9790 if (NS_WARN_IF(!CloseInternal())) {
9791 return IPC_FAIL(this, "CloseInternal failed!");
9797 void Database::StartTransactionOp::RunOnConnectionThread() {
9798 MOZ_ASSERT(!IsOnBackgroundThread());
9799 MOZ_ASSERT(!HasFailed());
9801 IDB_LOG_MARK_PARENT_TRANSACTION("Beginning database work", "DB Start",
9802 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
9803 mTransactionLoggingSerialNumber
);
9805 TransactionDatabaseOperationBase::RunOnConnectionThread();
9808 nsresult
Database::StartTransactionOp::DoDatabaseWork(
9809 DatabaseConnection
* aConnection
) {
9810 MOZ_ASSERT(aConnection
);
9811 aConnection
->AssertIsOnConnectionThread();
9813 Transaction().SetActiveOnConnectionThread();
9815 if (Transaction().GetMode() == IDBTransaction::Mode::Cleanup
) {
9816 DebugOnly
<nsresult
> rv
= aConnection
->DisableQuotaChecks();
9817 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
9818 "DisableQuotaChecks failed, trying to continue "
9819 "cleanup transaction with quota checks enabled");
9822 if (Transaction().GetMode() != IDBTransaction::Mode::ReadOnly
) {
9823 QM_TRY(MOZ_TO_RESULT(aConnection
->BeginWriteTransaction()));
9829 nsresult
Database::StartTransactionOp::SendSuccessResult() {
9830 // We don't need to do anything here.
9834 bool Database::StartTransactionOp::SendFailureResult(
9835 nsresult
/* aResultCode */) {
9836 IDB_REPORT_INTERNAL_ERR();
9838 // Abort the transaction.
9842 void Database::StartTransactionOp::Cleanup() {
9844 // StartTransactionOp is not a normal database operation that is tied to an
9845 // actor. Do this to make our assertions happy.
9846 NoteActorDestroyed();
9849 TransactionDatabaseOperationBase::Cleanup();
9852 /*******************************************************************************
9854 ******************************************************************************/
9856 TransactionBase::TransactionBase(SafeRefPtr
<Database
> aDatabase
, Mode aMode
)
9857 : mDatabase(std::move(aDatabase
)),
9858 mDatabaseId(mDatabase
->Id()),
9859 mLoggingSerialNumber(
9860 mDatabase
->GetLoggingInfo()->NextTransactionSN(aMode
)),
9861 mActiveRequestCount(0),
9862 mInvalidatedOnAnyThread(false),
9864 mResultCode(NS_OK
) {
9865 AssertIsOnBackgroundThread();
9866 MOZ_ASSERT(mDatabase
);
9867 MOZ_ASSERT(mLoggingSerialNumber
);
9870 TransactionBase::~TransactionBase() {
9871 MOZ_ASSERT(!mActiveRequestCount
);
9872 MOZ_ASSERT(mActorDestroyed
);
9873 MOZ_ASSERT_IF(mInitialized
, mCommittedOrAborted
);
9876 void TransactionBase::Abort(nsresult aResultCode
, bool aForce
) {
9877 AssertIsOnBackgroundThread();
9878 MOZ_ASSERT(NS_FAILED(aResultCode
));
9880 if (NS_SUCCEEDED(mResultCode
)) {
9881 mResultCode
= aResultCode
;
9885 mForceAborted
.EnsureFlipped();
9888 MaybeCommitOrAbort();
9891 mozilla::ipc::IPCResult
TransactionBase::RecvCommit(
9892 IProtocol
* aActor
, const Maybe
<int64_t> aLastRequest
) {
9893 AssertIsOnBackgroundThread();
9895 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
9897 aActor
, "Attempt to commit an already comitted/aborted transaction!");
9900 mCommitOrAbortReceived
.Flip();
9901 mLastRequestBeforeCommit
.init(aLastRequest
);
9902 MaybeCommitOrAbort();
9907 mozilla::ipc::IPCResult
TransactionBase::RecvAbort(IProtocol
* aActor
,
9908 nsresult aResultCode
) {
9909 AssertIsOnBackgroundThread();
9911 if (NS_WARN_IF(NS_SUCCEEDED(aResultCode
))) {
9912 return IPC_FAIL(aActor
, "aResultCode must not be a success code!");
9915 if (NS_WARN_IF(NS_ERROR_GET_MODULE(aResultCode
) !=
9916 NS_ERROR_MODULE_DOM_INDEXEDDB
)) {
9917 return IPC_FAIL(aActor
, "aResultCode does not refer to IndexedDB!");
9920 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
9922 aActor
, "Attempt to abort an already comitted/aborted transaction!");
9925 mCommitOrAbortReceived
.Flip();
9926 Abort(aResultCode
, /* aForce */ false);
9931 void TransactionBase::CommitOrAbort() {
9932 AssertIsOnBackgroundThread();
9934 mCommittedOrAborted
.Flip();
9936 if (!mInitialized
) {
9940 // In case of a failed request that was started after committing was
9941 // initiated, abort (cf.
9942 // https://w3c.github.io/IndexedDB/#async-execute-request step 5.3 vs. 5.4).
9943 // Note this can only happen here when we are committing explicitly, otherwise
9944 // the decision is made by the child.
9945 if (NS_SUCCEEDED(mResultCode
) && mLastFailedRequest
&&
9946 *mLastRequestBeforeCommit
&&
9947 *mLastFailedRequest
>= **mLastRequestBeforeCommit
) {
9948 mResultCode
= NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
;
9951 RefPtr
<CommitOp
> commitOp
=
9952 new CommitOp(SafeRefPtrFromThis(), ClampResultCode(mResultCode
));
9954 gConnectionPool
->Finish(TransactionId(), commitOp
);
9957 SafeRefPtr
<FullObjectStoreMetadata
>
9958 TransactionBase::GetMetadataForObjectStoreId(
9959 IndexOrObjectStoreId aObjectStoreId
) const {
9960 AssertIsOnBackgroundThread();
9961 MOZ_ASSERT(aObjectStoreId
);
9963 if (!aObjectStoreId
) {
9967 auto metadata
= mDatabase
->Metadata().mObjectStores
.Lookup(aObjectStoreId
);
9968 if (!metadata
|| (*metadata
)->mDeleted
) {
9972 MOZ_ASSERT((*metadata
)->mCommonMetadata
.id() == aObjectStoreId
);
9974 return metadata
->clonePtr();
9977 SafeRefPtr
<FullIndexMetadata
> TransactionBase::GetMetadataForIndexId(
9978 FullObjectStoreMetadata
& aObjectStoreMetadata
,
9979 IndexOrObjectStoreId aIndexId
) const {
9980 AssertIsOnBackgroundThread();
9981 MOZ_ASSERT(aIndexId
);
9987 auto metadata
= aObjectStoreMetadata
.mIndexes
.Lookup(aIndexId
);
9988 if (!metadata
|| (*metadata
)->mDeleted
) {
9992 MOZ_ASSERT((*metadata
)->mCommonMetadata
.id() == aIndexId
);
9994 return metadata
->clonePtr();
9997 void TransactionBase::NoteModifiedAutoIncrementObjectStore(
9998 const SafeRefPtr
<FullObjectStoreMetadata
>& aMetadata
) {
9999 AssertIsOnConnectionThread();
10001 if (!mModifiedAutoIncrementObjectStoreMetadataArray
.Contains(aMetadata
)) {
10002 mModifiedAutoIncrementObjectStoreMetadataArray
.AppendElement(
10003 aMetadata
.clonePtr());
10007 void TransactionBase::ForgetModifiedAutoIncrementObjectStore(
10008 FullObjectStoreMetadata
& aMetadata
) {
10009 AssertIsOnConnectionThread();
10011 mModifiedAutoIncrementObjectStoreMetadataArray
.RemoveElement(&aMetadata
);
10014 bool TransactionBase::VerifyRequestParams(const RequestParams
& aParams
) const {
10015 AssertIsOnBackgroundThread();
10016 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
10018 switch (aParams
.type()) {
10019 case RequestParams::TObjectStoreAddParams
: {
10020 const ObjectStoreAddPutParams
& params
=
10021 aParams
.get_ObjectStoreAddParams().commonParams();
10022 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
))) {
10028 case RequestParams::TObjectStorePutParams
: {
10029 const ObjectStoreAddPutParams
& params
=
10030 aParams
.get_ObjectStorePutParams().commonParams();
10031 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
))) {
10037 case RequestParams::TObjectStoreGetParams
: {
10038 const ObjectStoreGetParams
& params
= aParams
.get_ObjectStoreGetParams();
10039 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10040 GetMetadataForObjectStoreId(params
.objectStoreId());
10041 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10044 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.keyRange()))) {
10050 case RequestParams::TObjectStoreGetKeyParams
: {
10051 const ObjectStoreGetKeyParams
& params
=
10052 aParams
.get_ObjectStoreGetKeyParams();
10053 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10054 GetMetadataForObjectStoreId(params
.objectStoreId());
10055 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10058 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.keyRange()))) {
10064 case RequestParams::TObjectStoreGetAllParams
: {
10065 const ObjectStoreGetAllParams
& params
=
10066 aParams
.get_ObjectStoreGetAllParams();
10067 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10068 GetMetadataForObjectStoreId(params
.objectStoreId());
10069 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10072 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.optionalKeyRange()))) {
10078 case RequestParams::TObjectStoreGetAllKeysParams
: {
10079 const ObjectStoreGetAllKeysParams
& params
=
10080 aParams
.get_ObjectStoreGetAllKeysParams();
10081 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10082 GetMetadataForObjectStoreId(params
.objectStoreId());
10083 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10086 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.optionalKeyRange()))) {
10092 case RequestParams::TObjectStoreDeleteParams
: {
10093 if (NS_AUUF_OR_WARN_IF(mMode
!= IDBTransaction::Mode::ReadWrite
&&
10094 mMode
!= IDBTransaction::Mode::ReadWriteFlush
&&
10095 mMode
!= IDBTransaction::Mode::Cleanup
&&
10096 mMode
!= IDBTransaction::Mode::VersionChange
)) {
10100 const ObjectStoreDeleteParams
& params
=
10101 aParams
.get_ObjectStoreDeleteParams();
10102 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10103 GetMetadataForObjectStoreId(params
.objectStoreId());
10104 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10107 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.keyRange()))) {
10113 case RequestParams::TObjectStoreClearParams
: {
10114 if (NS_AUUF_OR_WARN_IF(mMode
!= IDBTransaction::Mode::ReadWrite
&&
10115 mMode
!= IDBTransaction::Mode::ReadWriteFlush
&&
10116 mMode
!= IDBTransaction::Mode::Cleanup
&&
10117 mMode
!= IDBTransaction::Mode::VersionChange
)) {
10121 const ObjectStoreClearParams
& params
=
10122 aParams
.get_ObjectStoreClearParams();
10123 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10124 GetMetadataForObjectStoreId(params
.objectStoreId());
10125 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10131 case RequestParams::TObjectStoreCountParams
: {
10132 const ObjectStoreCountParams
& params
=
10133 aParams
.get_ObjectStoreCountParams();
10134 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10135 GetMetadataForObjectStoreId(params
.objectStoreId());
10136 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10139 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.optionalKeyRange()))) {
10145 case RequestParams::TIndexGetParams
: {
10146 const IndexGetParams
& params
= aParams
.get_IndexGetParams();
10147 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10148 GetMetadataForObjectStoreId(params
.objectStoreId());
10149 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10152 const SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
10153 GetMetadataForIndexId(*objectStoreMetadata
, params
.indexId());
10154 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
10157 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.keyRange()))) {
10163 case RequestParams::TIndexGetKeyParams
: {
10164 const IndexGetKeyParams
& params
= aParams
.get_IndexGetKeyParams();
10165 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10166 GetMetadataForObjectStoreId(params
.objectStoreId());
10167 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10170 const SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
10171 GetMetadataForIndexId(*objectStoreMetadata
, params
.indexId());
10172 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
10175 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.keyRange()))) {
10181 case RequestParams::TIndexGetAllParams
: {
10182 const IndexGetAllParams
& params
= aParams
.get_IndexGetAllParams();
10183 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10184 GetMetadataForObjectStoreId(params
.objectStoreId());
10185 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10188 const SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
10189 GetMetadataForIndexId(*objectStoreMetadata
, params
.indexId());
10190 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
10193 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.optionalKeyRange()))) {
10199 case RequestParams::TIndexGetAllKeysParams
: {
10200 const IndexGetAllKeysParams
& params
= aParams
.get_IndexGetAllKeysParams();
10201 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10202 GetMetadataForObjectStoreId(params
.objectStoreId());
10203 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10206 const SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
10207 GetMetadataForIndexId(*objectStoreMetadata
, params
.indexId());
10208 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
10211 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.optionalKeyRange()))) {
10217 case RequestParams::TIndexCountParams
: {
10218 const IndexCountParams
& params
= aParams
.get_IndexCountParams();
10219 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10220 GetMetadataForObjectStoreId(params
.objectStoreId());
10221 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10224 const SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
10225 GetMetadataForIndexId(*objectStoreMetadata
, params
.indexId());
10226 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
10229 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.optionalKeyRange()))) {
10236 MOZ_CRASH("Should never get here!");
10242 bool TransactionBase::VerifyRequestParams(
10243 const SerializedKeyRange
& aParams
) const {
10244 AssertIsOnBackgroundThread();
10246 // XXX Check more here?
10248 if (aParams
.isOnly()) {
10249 if (NS_AUUF_OR_WARN_IF(aParams
.lower().IsUnset())) {
10252 if (NS_AUUF_OR_WARN_IF(!aParams
.upper().IsUnset())) {
10255 if (NS_AUUF_OR_WARN_IF(aParams
.lowerOpen())) {
10258 if (NS_AUUF_OR_WARN_IF(aParams
.upperOpen())) {
10261 } else if (NS_AUUF_OR_WARN_IF(aParams
.lower().IsUnset() &&
10262 aParams
.upper().IsUnset())) {
10269 bool TransactionBase::VerifyRequestParams(
10270 const ObjectStoreAddPutParams
& aParams
) const {
10271 AssertIsOnBackgroundThread();
10273 if (NS_AUUF_OR_WARN_IF(mMode
!= IDBTransaction::Mode::ReadWrite
&&
10274 mMode
!= IDBTransaction::Mode::ReadWriteFlush
&&
10275 mMode
!= IDBTransaction::Mode::VersionChange
)) {
10279 SafeRefPtr
<FullObjectStoreMetadata
> objMetadata
=
10280 GetMetadataForObjectStoreId(aParams
.objectStoreId());
10281 if (NS_AUUF_OR_WARN_IF(!objMetadata
)) {
10285 if (NS_AUUF_OR_WARN_IF(!aParams
.cloneInfo().data().data
.Size())) {
10289 if (objMetadata
->mCommonMetadata
.autoIncrement() &&
10290 objMetadata
->mCommonMetadata
.keyPath().IsValid() &&
10291 aParams
.key().IsUnset()) {
10292 const SerializedStructuredCloneWriteInfo
& cloneInfo
= aParams
.cloneInfo();
10294 if (NS_AUUF_OR_WARN_IF(!cloneInfo
.offsetToKeyProp())) {
10298 if (NS_AUUF_OR_WARN_IF(cloneInfo
.data().data
.Size() < sizeof(uint64_t))) {
10302 if (NS_AUUF_OR_WARN_IF(cloneInfo
.offsetToKeyProp() >
10303 (cloneInfo
.data().data
.Size() - sizeof(uint64_t)))) {
10306 } else if (NS_AUUF_OR_WARN_IF(aParams
.cloneInfo().offsetToKeyProp())) {
10310 for (const auto& updateInfo
: aParams
.indexUpdateInfos()) {
10311 SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
10312 GetMetadataForIndexId(*objMetadata
, updateInfo
.indexId());
10313 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
10317 if (NS_AUUF_OR_WARN_IF(updateInfo
.value().IsUnset())) {
10321 MOZ_ASSERT(!updateInfo
.value().GetBuffer().IsEmpty());
10324 for (const FileAddInfo
& fileAddInfo
: aParams
.fileAddInfos()) {
10325 const PBackgroundIDBDatabaseFileParent
* file
= fileAddInfo
.fileParent();
10327 switch (fileAddInfo
.type()) {
10328 case StructuredCloneFileBase::eBlob
:
10329 if (NS_AUUF_OR_WARN_IF(!file
)) {
10334 case StructuredCloneFileBase::eMutableFile
: {
10338 case StructuredCloneFileBase::eStructuredClone
:
10339 case StructuredCloneFileBase::eWasmBytecode
:
10340 case StructuredCloneFileBase::eWasmCompiled
:
10341 case StructuredCloneFileBase::eEndGuard
:
10342 MOZ_ASSERT_UNLESS_FUZZING(false, "Unsupported.");
10346 MOZ_CRASH("Should never get here!");
10353 bool TransactionBase::VerifyRequestParams(
10354 const Maybe
<SerializedKeyRange
>& aParams
) const {
10355 AssertIsOnBackgroundThread();
10357 if (aParams
.isSome()) {
10358 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(aParams
.ref()))) {
10366 void TransactionBase::NoteActiveRequest() {
10367 AssertIsOnBackgroundThread();
10368 MOZ_ASSERT(mActiveRequestCount
< UINT64_MAX
);
10370 mActiveRequestCount
++;
10373 void TransactionBase::NoteFinishedRequest(const int64_t aRequestId
,
10374 const nsresult aResultCode
) {
10375 AssertIsOnBackgroundThread();
10376 MOZ_ASSERT(mActiveRequestCount
);
10378 mActiveRequestCount
--;
10380 if (NS_FAILED(aResultCode
)) {
10381 mLastFailedRequest
= Some(aRequestId
);
10384 MaybeCommitOrAbort();
10387 void TransactionBase::Invalidate() {
10388 AssertIsOnBackgroundThread();
10389 MOZ_ASSERT(mInvalidated
== mInvalidatedOnAnyThread
);
10391 if (!mInvalidated
) {
10392 mInvalidated
.Flip();
10393 mInvalidatedOnAnyThread
= true;
10395 Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
, /* aForce */ false);
10399 PBackgroundIDBRequestParent
* TransactionBase::AllocRequest(
10400 RequestParams
&& aParams
, bool aTrustParams
) {
10401 AssertIsOnBackgroundThread();
10402 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
10405 // Always verify parameters in DEBUG builds!
10406 aTrustParams
= false;
10409 if (NS_AUUF_OR_WARN_IF(!aTrustParams
&& !VerifyRequestParams(aParams
))) {
10413 if (NS_AUUF_OR_WARN_IF(mCommitOrAbortReceived
)) {
10417 RefPtr
<NormalTransactionOp
> actor
;
10419 switch (aParams
.type()) {
10420 case RequestParams::TObjectStoreAddParams
:
10421 case RequestParams::TObjectStorePutParams
:
10422 actor
= new ObjectStoreAddOrPutRequestOp(SafeRefPtrFromThis(),
10423 std::move(aParams
));
10426 case RequestParams::TObjectStoreGetParams
:
10427 actor
= new ObjectStoreGetRequestOp(SafeRefPtrFromThis(), aParams
,
10428 /* aGetAll */ false);
10431 case RequestParams::TObjectStoreGetAllParams
:
10432 actor
= new ObjectStoreGetRequestOp(SafeRefPtrFromThis(), aParams
,
10433 /* aGetAll */ true);
10436 case RequestParams::TObjectStoreGetKeyParams
:
10437 actor
= new ObjectStoreGetKeyRequestOp(SafeRefPtrFromThis(), aParams
,
10438 /* aGetAll */ false);
10441 case RequestParams::TObjectStoreGetAllKeysParams
:
10442 actor
= new ObjectStoreGetKeyRequestOp(SafeRefPtrFromThis(), aParams
,
10443 /* aGetAll */ true);
10446 case RequestParams::TObjectStoreDeleteParams
:
10447 actor
= new ObjectStoreDeleteRequestOp(
10448 SafeRefPtrFromThis(), aParams
.get_ObjectStoreDeleteParams());
10451 case RequestParams::TObjectStoreClearParams
:
10452 actor
= new ObjectStoreClearRequestOp(
10453 SafeRefPtrFromThis(), aParams
.get_ObjectStoreClearParams());
10456 case RequestParams::TObjectStoreCountParams
:
10457 actor
= new ObjectStoreCountRequestOp(
10458 SafeRefPtrFromThis(), aParams
.get_ObjectStoreCountParams());
10461 case RequestParams::TIndexGetParams
:
10462 actor
= new IndexGetRequestOp(SafeRefPtrFromThis(), aParams
,
10463 /* aGetAll */ false);
10466 case RequestParams::TIndexGetKeyParams
:
10467 actor
= new IndexGetKeyRequestOp(SafeRefPtrFromThis(), aParams
,
10468 /* aGetAll */ false);
10471 case RequestParams::TIndexGetAllParams
:
10472 actor
= new IndexGetRequestOp(SafeRefPtrFromThis(), aParams
,
10473 /* aGetAll */ true);
10476 case RequestParams::TIndexGetAllKeysParams
:
10477 actor
= new IndexGetKeyRequestOp(SafeRefPtrFromThis(), aParams
,
10478 /* aGetAll */ true);
10481 case RequestParams::TIndexCountParams
:
10482 actor
= new IndexCountRequestOp(SafeRefPtrFromThis(), aParams
);
10486 MOZ_CRASH("Should never get here!");
10491 // Transfer ownership to IPDL.
10492 return actor
.forget().take();
10495 bool TransactionBase::StartRequest(PBackgroundIDBRequestParent
* aActor
) {
10496 AssertIsOnBackgroundThread();
10497 MOZ_ASSERT(aActor
);
10499 auto* op
= static_cast<NormalTransactionOp
*>(aActor
);
10501 if (NS_WARN_IF(!op
->Init(*this))) {
10506 op
->DispatchToConnectionPool();
10510 bool TransactionBase::DeallocRequest(
10511 PBackgroundIDBRequestParent
* const aActor
) {
10512 AssertIsOnBackgroundThread();
10513 MOZ_ASSERT(aActor
);
10515 // Transfer ownership back from IPDL.
10516 const RefPtr
<NormalTransactionOp
> actor
=
10517 dont_AddRef(static_cast<NormalTransactionOp
*>(aActor
));
10521 already_AddRefed
<PBackgroundIDBCursorParent
> TransactionBase::AllocCursor(
10522 const OpenCursorParams
& aParams
, bool aTrustParams
) {
10523 AssertIsOnBackgroundThread();
10524 MOZ_ASSERT(aParams
.type() != OpenCursorParams::T__None
);
10527 // Always verify parameters in DEBUG builds!
10528 aTrustParams
= false;
10531 const OpenCursorParams::Type type
= aParams
.type();
10532 SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
;
10533 SafeRefPtr
<FullIndexMetadata
> indexMetadata
;
10534 CursorBase::Direction direction
;
10536 // First extract the parameters common to all open cursor variants.
10537 const auto& commonParams
= GetCommonOpenCursorParams(aParams
);
10538 objectStoreMetadata
=
10539 GetMetadataForObjectStoreId(commonParams
.objectStoreId());
10540 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10543 if (aTrustParams
&& NS_AUUF_OR_WARN_IF(!VerifyRequestParams(
10544 commonParams
.optionalKeyRange()))) {
10547 direction
= commonParams
.direction();
10549 // Now, for the index open cursor variants, extract the additional parameter.
10550 if (type
== OpenCursorParams::TIndexOpenCursorParams
||
10551 type
== OpenCursorParams::TIndexOpenKeyCursorParams
) {
10552 const auto& commonIndexParams
= GetCommonIndexOpenCursorParams(aParams
);
10553 indexMetadata
= GetMetadataForIndexId(*objectStoreMetadata
,
10554 commonIndexParams
.indexId());
10555 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
10560 if (NS_AUUF_OR_WARN_IF(mCommitOrAbortReceived
)) {
10564 // Create Cursor and transfer ownership to IPDL.
10566 case OpenCursorParams::TObjectStoreOpenCursorParams
:
10567 MOZ_ASSERT(!indexMetadata
);
10568 return MakeAndAddRef
<Cursor
<IDBCursorType::ObjectStore
>>(
10569 SafeRefPtrFromThis(), std::move(objectStoreMetadata
), direction
,
10570 CursorBase::ConstructFromTransactionBase
{});
10571 case OpenCursorParams::TObjectStoreOpenKeyCursorParams
:
10572 MOZ_ASSERT(!indexMetadata
);
10573 return MakeAndAddRef
<Cursor
<IDBCursorType::ObjectStoreKey
>>(
10574 SafeRefPtrFromThis(), std::move(objectStoreMetadata
), direction
,
10575 CursorBase::ConstructFromTransactionBase
{});
10576 case OpenCursorParams::TIndexOpenCursorParams
:
10577 return MakeAndAddRef
<Cursor
<IDBCursorType::Index
>>(
10578 SafeRefPtrFromThis(), std::move(objectStoreMetadata
),
10579 std::move(indexMetadata
), direction
,
10580 CursorBase::ConstructFromTransactionBase
{});
10581 case OpenCursorParams::TIndexOpenKeyCursorParams
:
10582 return MakeAndAddRef
<Cursor
<IDBCursorType::IndexKey
>>(
10583 SafeRefPtrFromThis(), std::move(objectStoreMetadata
),
10584 std::move(indexMetadata
), direction
,
10585 CursorBase::ConstructFromTransactionBase
{});
10587 MOZ_CRASH("Cannot get here.");
10591 bool TransactionBase::StartCursor(PBackgroundIDBCursorParent
* const aActor
,
10592 const OpenCursorParams
& aParams
) {
10593 AssertIsOnBackgroundThread();
10594 MOZ_ASSERT(aActor
);
10595 MOZ_ASSERT(aParams
.type() != OpenCursorParams::T__None
);
10597 auto* const op
= static_cast<CursorBase
*>(aActor
);
10599 if (NS_WARN_IF(!op
->Start(aParams
))) {
10606 /*******************************************************************************
10607 * NormalTransaction
10608 ******************************************************************************/
10610 NormalTransaction::NormalTransaction(
10611 SafeRefPtr
<Database
> aDatabase
, TransactionBase::Mode aMode
,
10612 nsTArray
<SafeRefPtr
<FullObjectStoreMetadata
>>&& aObjectStores
)
10613 : TransactionBase(std::move(aDatabase
), aMode
),
10614 mObjectStores
{std::move(aObjectStores
)} {
10615 AssertIsOnBackgroundThread();
10616 MOZ_ASSERT(!mObjectStores
.IsEmpty());
10619 bool NormalTransaction::IsSameProcessActor() {
10620 AssertIsOnBackgroundThread();
10622 PBackgroundParent
* const actor
= Manager()->Manager()->Manager();
10625 return !BackgroundParent::IsOtherProcessActor(actor
);
10628 void NormalTransaction::SendCompleteNotification(nsresult aResult
) {
10629 AssertIsOnBackgroundThread();
10631 if (!IsActorDestroyed()) {
10632 Unused
<< SendComplete(aResult
);
10636 void NormalTransaction::ActorDestroy(ActorDestroyReason aWhy
) {
10637 AssertIsOnBackgroundThread();
10639 NoteActorDestroyed();
10641 if (!mCommittedOrAborted
) {
10642 if (NS_SUCCEEDED(mResultCode
)) {
10643 IDB_REPORT_INTERNAL_ERR();
10644 mResultCode
= NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
10647 mForceAborted
.EnsureFlipped();
10649 MaybeCommitOrAbort();
10653 mozilla::ipc::IPCResult
NormalTransaction::RecvDeleteMe() {
10654 AssertIsOnBackgroundThread();
10655 MOZ_ASSERT(!IsActorDestroyed());
10657 QM_WARNONLY_TRY(OkIf(PBackgroundIDBTransactionParent::Send__delete__(this)));
10662 mozilla::ipc::IPCResult
NormalTransaction::RecvCommit(
10663 const Maybe
<int64_t>& aLastRequest
) {
10664 AssertIsOnBackgroundThread();
10666 return TransactionBase::RecvCommit(this, aLastRequest
);
10669 mozilla::ipc::IPCResult
NormalTransaction::RecvAbort(
10670 const nsresult
& aResultCode
) {
10671 AssertIsOnBackgroundThread();
10673 return TransactionBase::RecvAbort(this, aResultCode
);
10676 PBackgroundIDBRequestParent
*
10677 NormalTransaction::AllocPBackgroundIDBRequestParent(
10678 const RequestParams
& aParams
) {
10679 AssertIsOnBackgroundThread();
10680 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
10682 return AllocRequest(std::move(const_cast<RequestParams
&>(aParams
)),
10683 IsSameProcessActor());
10686 mozilla::ipc::IPCResult
NormalTransaction::RecvPBackgroundIDBRequestConstructor(
10687 PBackgroundIDBRequestParent
* const aActor
, const RequestParams
& aParams
) {
10688 AssertIsOnBackgroundThread();
10689 MOZ_ASSERT(aActor
);
10690 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
10692 if (!StartRequest(aActor
)) {
10693 return IPC_FAIL(this, "StartRequest failed!");
10698 bool NormalTransaction::DeallocPBackgroundIDBRequestParent(
10699 PBackgroundIDBRequestParent
* const aActor
) {
10700 AssertIsOnBackgroundThread();
10701 MOZ_ASSERT(aActor
);
10703 return DeallocRequest(aActor
);
10706 already_AddRefed
<PBackgroundIDBCursorParent
>
10707 NormalTransaction::AllocPBackgroundIDBCursorParent(
10708 const OpenCursorParams
& aParams
) {
10709 AssertIsOnBackgroundThread();
10711 return AllocCursor(aParams
, IsSameProcessActor());
10714 mozilla::ipc::IPCResult
NormalTransaction::RecvPBackgroundIDBCursorConstructor(
10715 PBackgroundIDBCursorParent
* const aActor
, const OpenCursorParams
& aParams
) {
10716 AssertIsOnBackgroundThread();
10717 MOZ_ASSERT(aActor
);
10718 MOZ_ASSERT(aParams
.type() != OpenCursorParams::T__None
);
10720 if (!StartCursor(aActor
, aParams
)) {
10721 return IPC_FAIL(this, "StartCursor failed!");
10726 /*******************************************************************************
10727 * VersionChangeTransaction
10728 ******************************************************************************/
10730 VersionChangeTransaction::VersionChangeTransaction(
10731 OpenDatabaseOp
* aOpenDatabaseOp
)
10732 : TransactionBase(aOpenDatabaseOp
->mDatabase
.clonePtr(),
10733 IDBTransaction::Mode::VersionChange
),
10734 mOpenDatabaseOp(aOpenDatabaseOp
) {
10735 AssertIsOnBackgroundThread();
10736 MOZ_ASSERT(aOpenDatabaseOp
);
10739 VersionChangeTransaction::~VersionChangeTransaction() {
10741 // Silence the base class' destructor assertion if we never made this actor
10743 FakeActorDestroyed();
10747 bool VersionChangeTransaction::IsSameProcessActor() {
10748 AssertIsOnBackgroundThread();
10750 PBackgroundParent
* actor
= Manager()->Manager()->Manager();
10753 return !BackgroundParent::IsOtherProcessActor(actor
);
10756 void VersionChangeTransaction::SetActorAlive() {
10757 AssertIsOnBackgroundThread();
10758 MOZ_ASSERT(!IsActorDestroyed());
10760 mActorWasAlive
.Flip();
10763 bool VersionChangeTransaction::CopyDatabaseMetadata() {
10764 AssertIsOnBackgroundThread();
10765 MOZ_ASSERT(!mOldMetadata
);
10767 const auto& origMetadata
= GetDatabase().Metadata();
10769 SafeRefPtr
<FullDatabaseMetadata
> newMetadata
= origMetadata
.Duplicate();
10770 if (NS_WARN_IF(!newMetadata
)) {
10774 // Replace the live metadata with the new mutable copy.
10775 DatabaseActorInfo
* info
;
10776 MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable
->Get(origMetadata
.mDatabaseId
, &info
));
10777 MOZ_ASSERT(!info
->mLiveDatabases
.IsEmpty());
10778 MOZ_ASSERT(info
->mMetadata
== &origMetadata
);
10780 mOldMetadata
= std::move(info
->mMetadata
);
10781 info
->mMetadata
= std::move(newMetadata
);
10783 // Replace metadata pointers for all live databases.
10784 for (const auto& liveDatabase
: info
->mLiveDatabases
) {
10785 liveDatabase
->mMetadata
= info
->mMetadata
.clonePtr();
10791 void VersionChangeTransaction::UpdateMetadata(nsresult aResult
) {
10792 AssertIsOnBackgroundThread();
10793 MOZ_ASSERT(mOpenDatabaseOp
);
10794 MOZ_ASSERT(!!mActorWasAlive
== !!mOpenDatabaseOp
->mDatabase
);
10795 MOZ_ASSERT_IF(mActorWasAlive
, !mOpenDatabaseOp
->mDatabaseId
.IsEmpty());
10797 if (IsActorDestroyed() || !mActorWasAlive
) {
10801 SafeRefPtr
<FullDatabaseMetadata
> oldMetadata
= std::move(mOldMetadata
);
10803 DatabaseActorInfo
* info
;
10804 if (!gLiveDatabaseHashtable
->Get(oldMetadata
->mDatabaseId
, &info
)) {
10808 MOZ_ASSERT(!info
->mLiveDatabases
.IsEmpty());
10810 if (NS_SUCCEEDED(aResult
)) {
10811 // Remove all deleted objectStores and indexes, then mark immutable.
10812 info
->mMetadata
->mObjectStores
.RemoveIf([](const auto& objectStoreIter
) {
10813 MOZ_ASSERT(objectStoreIter
.Key());
10814 const SafeRefPtr
<FullObjectStoreMetadata
>& metadata
=
10815 objectStoreIter
.Data();
10816 MOZ_ASSERT(metadata
);
10818 if (metadata
->mDeleted
) {
10822 metadata
->mIndexes
.RemoveIf([](const auto& indexIter
) -> bool {
10823 MOZ_ASSERT(indexIter
.Key());
10824 const SafeRefPtr
<FullIndexMetadata
>& index
= indexIter
.Data();
10827 return index
->mDeleted
;
10829 metadata
->mIndexes
.MarkImmutable();
10834 info
->mMetadata
->mObjectStores
.MarkImmutable();
10836 // Replace metadata pointers for all live databases.
10837 info
->mMetadata
= std::move(oldMetadata
);
10839 for (auto& liveDatabase
: info
->mLiveDatabases
) {
10840 liveDatabase
->mMetadata
= info
->mMetadata
.clonePtr();
10845 void VersionChangeTransaction::SendCompleteNotification(nsresult aResult
) {
10846 AssertIsOnBackgroundThread();
10847 MOZ_ASSERT(mOpenDatabaseOp
);
10848 MOZ_ASSERT_IF(!mActorWasAlive
, mOpenDatabaseOp
->HasFailed());
10849 MOZ_ASSERT_IF(!mActorWasAlive
, mOpenDatabaseOp
->mState
>
10850 OpenDatabaseOp::State::SendingResults
);
10852 const RefPtr
<OpenDatabaseOp
> openDatabaseOp
= std::move(mOpenDatabaseOp
);
10854 if (!mActorWasAlive
) {
10858 if (NS_FAILED(aResult
)) {
10859 // 3.3.1 Opening a database:
10860 // "If the upgrade transaction was aborted, run the steps for closing a
10861 // database connection with connection, create and return a new AbortError
10862 // exception and abort these steps."
10863 openDatabaseOp
->SetFailureCodeIfUnset(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
);
10866 openDatabaseOp
->mState
= OpenDatabaseOp::State::SendingResults
;
10868 if (!IsActorDestroyed()) {
10869 Unused
<< SendComplete(aResult
);
10872 MOZ_ALWAYS_SUCCEEDS(openDatabaseOp
->Run());
10875 void VersionChangeTransaction::ActorDestroy(ActorDestroyReason aWhy
) {
10876 AssertIsOnBackgroundThread();
10878 NoteActorDestroyed();
10880 if (!mCommittedOrAborted
) {
10881 if (NS_SUCCEEDED(mResultCode
)) {
10882 IDB_REPORT_INTERNAL_ERR();
10883 mResultCode
= NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
10886 mForceAborted
.EnsureFlipped();
10888 MaybeCommitOrAbort();
10892 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvDeleteMe() {
10893 AssertIsOnBackgroundThread();
10894 MOZ_ASSERT(!IsActorDestroyed());
10897 OkIf(PBackgroundIDBVersionChangeTransactionParent::Send__delete__(this)));
10902 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvCommit(
10903 const Maybe
<int64_t>& aLastRequest
) {
10904 AssertIsOnBackgroundThread();
10906 return TransactionBase::RecvCommit(this, aLastRequest
);
10909 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvAbort(
10910 const nsresult
& aResultCode
) {
10911 AssertIsOnBackgroundThread();
10913 return TransactionBase::RecvAbort(this, aResultCode
);
10916 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvCreateObjectStore(
10917 const ObjectStoreMetadata
& aMetadata
) {
10918 AssertIsOnBackgroundThread();
10920 if (NS_WARN_IF(!aMetadata
.id())) {
10921 return IPC_FAIL(this, "No metadata ID!");
10924 const SafeRefPtr
<FullDatabaseMetadata
> dbMetadata
=
10925 GetDatabase().MetadataPtr();
10927 if (NS_WARN_IF(aMetadata
.id() != dbMetadata
->mNextObjectStoreId
)) {
10928 return IPC_FAIL(this, "Requested metadata ID does not match next ID!");
10932 MatchMetadataNameOrId(dbMetadata
->mObjectStores
, aMetadata
.id(),
10933 SomeRef
<const nsAString
&>(aMetadata
.name()))
10935 return IPC_FAIL(this, "MatchMetadataNameOrId failed!");
10938 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
10939 return IPC_FAIL(this, "Transaction is already committed/aborted!");
10942 const int64_t initialAutoIncrementId
= aMetadata
.autoIncrement() ? 1 : 0;
10943 auto newMetadata
= MakeSafeRefPtr
<FullObjectStoreMetadata
>(
10944 aMetadata
, FullObjectStoreMetadata::AutoIncrementIds
{
10945 initialAutoIncrementId
, initialAutoIncrementId
});
10947 if (NS_WARN_IF(!dbMetadata
->mObjectStores
.InsertOrUpdate(
10948 aMetadata
.id(), std::move(newMetadata
), fallible
))) {
10949 return IPC_FAIL(this, "mObjectStores.InsertOrUpdate failed!");
10952 dbMetadata
->mNextObjectStoreId
++;
10954 RefPtr
<CreateObjectStoreOp
> op
= new CreateObjectStoreOp(
10955 SafeRefPtrFromThis().downcast
<VersionChangeTransaction
>(), aMetadata
);
10957 if (NS_WARN_IF(!op
->Init(*this))) {
10959 return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
10962 op
->DispatchToConnectionPool();
10967 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvDeleteObjectStore(
10968 const IndexOrObjectStoreId
& aObjectStoreId
) {
10969 AssertIsOnBackgroundThread();
10971 if (NS_WARN_IF(!aObjectStoreId
)) {
10972 return IPC_FAIL(this, "No ObjectStoreId!");
10975 const auto& dbMetadata
= GetDatabase().Metadata();
10976 MOZ_ASSERT(dbMetadata
.mNextObjectStoreId
> 0);
10978 if (NS_WARN_IF(aObjectStoreId
>= dbMetadata
.mNextObjectStoreId
)) {
10979 return IPC_FAIL(this, "Invalid ObjectStoreId!");
10982 SafeRefPtr
<FullObjectStoreMetadata
> foundMetadata
=
10983 GetMetadataForObjectStoreId(aObjectStoreId
);
10985 if (NS_WARN_IF(!foundMetadata
)) {
10986 return IPC_FAIL(this, "No metadata found for ObjectStoreId!");
10989 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
10990 return IPC_FAIL(this, "Transaction is already committed/aborted!");
10993 foundMetadata
->mDeleted
.Flip();
10995 DebugOnly
<bool> foundTargetId
= false;
10996 const bool isLastObjectStore
= std::all_of(
10997 dbMetadata
.mObjectStores
.begin(), dbMetadata
.mObjectStores
.end(),
10998 [&foundTargetId
, aObjectStoreId
](const auto& objectStoreEntry
) -> bool {
10999 if (uint64_t(aObjectStoreId
) == objectStoreEntry
.GetKey()) {
11000 foundTargetId
= true;
11004 return objectStoreEntry
.GetData()->mDeleted
;
11006 MOZ_ASSERT_IF(isLastObjectStore
, foundTargetId
);
11008 RefPtr
<DeleteObjectStoreOp
> op
= new DeleteObjectStoreOp(
11009 SafeRefPtrFromThis().downcast
<VersionChangeTransaction
>(),
11010 std::move(foundMetadata
), isLastObjectStore
);
11012 if (NS_WARN_IF(!op
->Init(*this))) {
11014 return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
11017 op
->DispatchToConnectionPool();
11022 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvRenameObjectStore(
11023 const IndexOrObjectStoreId
& aObjectStoreId
, const nsAString
& aName
) {
11024 AssertIsOnBackgroundThread();
11026 if (NS_WARN_IF(!aObjectStoreId
)) {
11027 return IPC_FAIL(this, "No ObjectStoreId!");
11031 const auto& dbMetadata
= GetDatabase().Metadata();
11032 MOZ_ASSERT(dbMetadata
.mNextObjectStoreId
> 0);
11034 if (NS_WARN_IF(aObjectStoreId
>= dbMetadata
.mNextObjectStoreId
)) {
11035 return IPC_FAIL(this, "Invalid ObjectStoreId!");
11039 SafeRefPtr
<FullObjectStoreMetadata
> foundMetadata
=
11040 GetMetadataForObjectStoreId(aObjectStoreId
);
11042 if (NS_WARN_IF(!foundMetadata
)) {
11043 return IPC_FAIL(this, "No metadata found for ObjectStoreId!");
11046 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
11047 return IPC_FAIL(this, "Transaction is already committed/aborted!");
11050 foundMetadata
->mCommonMetadata
.name() = aName
;
11052 RefPtr
<RenameObjectStoreOp
> renameOp
= new RenameObjectStoreOp(
11053 SafeRefPtrFromThis().downcast
<VersionChangeTransaction
>(),
11056 if (NS_WARN_IF(!renameOp
->Init(*this))) {
11057 renameOp
->Cleanup();
11058 return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
11061 renameOp
->DispatchToConnectionPool();
11066 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvCreateIndex(
11067 const IndexOrObjectStoreId
& aObjectStoreId
,
11068 const IndexMetadata
& aMetadata
) {
11069 AssertIsOnBackgroundThread();
11071 if (NS_WARN_IF(!aObjectStoreId
)) {
11072 return IPC_FAIL(this, "No ObjectStoreId!");
11075 if (NS_WARN_IF(!aMetadata
.id())) {
11076 return IPC_FAIL(this, "No Metadata id!");
11079 const auto dbMetadata
= GetDatabase().MetadataPtr();
11081 if (NS_WARN_IF(aMetadata
.id() != dbMetadata
->mNextIndexId
)) {
11082 return IPC_FAIL(this, "Requested metadata ID does not match next ID!");
11085 SafeRefPtr
<FullObjectStoreMetadata
> foundObjectStoreMetadata
=
11086 GetMetadataForObjectStoreId(aObjectStoreId
);
11088 if (NS_WARN_IF(!foundObjectStoreMetadata
)) {
11089 return IPC_FAIL(this, "GetMetadataForObjectStoreId failed!");
11092 if (NS_WARN_IF(MatchMetadataNameOrId(
11093 foundObjectStoreMetadata
->mIndexes
, aMetadata
.id(),
11094 SomeRef
<const nsAString
&>(aMetadata
.name()))
11096 return IPC_FAIL(this, "MatchMetadataNameOrId failed!");
11099 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
11100 return IPC_FAIL(this, "Transaction is already committed/aborted!");
11103 auto newMetadata
= MakeSafeRefPtr
<FullIndexMetadata
>();
11104 newMetadata
->mCommonMetadata
= aMetadata
;
11106 if (NS_WARN_IF(!foundObjectStoreMetadata
->mIndexes
.InsertOrUpdate(
11107 aMetadata
.id(), std::move(newMetadata
), fallible
))) {
11108 return IPC_FAIL(this, "mIndexes.InsertOrUpdate failed!");
11111 dbMetadata
->mNextIndexId
++;
11113 RefPtr
<CreateIndexOp
> op
= new CreateIndexOp(
11114 SafeRefPtrFromThis().downcast
<VersionChangeTransaction
>(), aObjectStoreId
,
11117 if (NS_WARN_IF(!op
->Init(*this))) {
11119 return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
11122 op
->DispatchToConnectionPool();
11127 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvDeleteIndex(
11128 const IndexOrObjectStoreId
& aObjectStoreId
,
11129 const IndexOrObjectStoreId
& aIndexId
) {
11130 AssertIsOnBackgroundThread();
11132 if (NS_WARN_IF(!aObjectStoreId
)) {
11133 return IPC_FAIL(this, "No ObjectStoreId!");
11136 if (NS_WARN_IF(!aIndexId
)) {
11137 return IPC_FAIL(this, "No Index id!");
11140 const auto& dbMetadata
= GetDatabase().Metadata();
11141 MOZ_ASSERT(dbMetadata
.mNextObjectStoreId
> 0);
11142 MOZ_ASSERT(dbMetadata
.mNextIndexId
> 0);
11144 if (NS_WARN_IF(aObjectStoreId
>= dbMetadata
.mNextObjectStoreId
)) {
11145 return IPC_FAIL(this, "Requested ObjectStoreId does not match next ID!");
11148 if (NS_WARN_IF(aIndexId
>= dbMetadata
.mNextIndexId
)) {
11149 return IPC_FAIL(this, "Requested IndexId does not match next ID!");
11153 SafeRefPtr
<FullObjectStoreMetadata
> foundObjectStoreMetadata
=
11154 GetMetadataForObjectStoreId(aObjectStoreId
);
11156 if (NS_WARN_IF(!foundObjectStoreMetadata
)) {
11157 return IPC_FAIL(this, "GetMetadataForObjectStoreId failed!");
11160 SafeRefPtr
<FullIndexMetadata
> foundIndexMetadata
=
11161 GetMetadataForIndexId(*foundObjectStoreMetadata
, aIndexId
);
11163 if (NS_WARN_IF(!foundIndexMetadata
)) {
11164 return IPC_FAIL(this, "GetMetadataForIndexId failed!");
11167 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
11168 return IPC_FAIL(this, "Transaction is already committed/aborted!");
11171 foundIndexMetadata
->mDeleted
.Flip();
11173 DebugOnly
<bool> foundTargetId
= false;
11174 const bool isLastIndex
=
11175 std::all_of(foundObjectStoreMetadata
->mIndexes
.cbegin(),
11176 foundObjectStoreMetadata
->mIndexes
.cend(),
11177 [&foundTargetId
, aIndexId
](const auto& indexEntry
) -> bool {
11178 if (uint64_t(aIndexId
) == indexEntry
.GetKey()) {
11179 foundTargetId
= true;
11183 return indexEntry
.GetData()->mDeleted
;
11185 MOZ_ASSERT_IF(isLastIndex
, foundTargetId
);
11187 RefPtr
<DeleteIndexOp
> op
= new DeleteIndexOp(
11188 SafeRefPtrFromThis().downcast
<VersionChangeTransaction
>(), aObjectStoreId
,
11189 aIndexId
, foundIndexMetadata
->mCommonMetadata
.unique(), isLastIndex
);
11191 if (NS_WARN_IF(!op
->Init(*this))) {
11193 return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
11196 op
->DispatchToConnectionPool();
11201 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvRenameIndex(
11202 const IndexOrObjectStoreId
& aObjectStoreId
,
11203 const IndexOrObjectStoreId
& aIndexId
, const nsAString
& aName
) {
11204 AssertIsOnBackgroundThread();
11206 if (NS_WARN_IF(!aObjectStoreId
)) {
11207 return IPC_FAIL(this, "No ObjectStoreId!");
11210 if (NS_WARN_IF(!aIndexId
)) {
11211 return IPC_FAIL(this, "No Index id!");
11214 const SafeRefPtr
<FullDatabaseMetadata
> dbMetadata
=
11215 GetDatabase().MetadataPtr();
11216 MOZ_ASSERT(dbMetadata
);
11217 MOZ_ASSERT(dbMetadata
->mNextObjectStoreId
> 0);
11218 MOZ_ASSERT(dbMetadata
->mNextIndexId
> 0);
11220 if (NS_WARN_IF(aObjectStoreId
>= dbMetadata
->mNextObjectStoreId
)) {
11221 return IPC_FAIL(this, "Requested ObjectStoreId does not match next ID!");
11224 if (NS_WARN_IF(aIndexId
>= dbMetadata
->mNextIndexId
)) {
11225 return IPC_FAIL(this, "Requested IndexId does not match next ID!");
11228 SafeRefPtr
<FullObjectStoreMetadata
> foundObjectStoreMetadata
=
11229 GetMetadataForObjectStoreId(aObjectStoreId
);
11231 if (NS_WARN_IF(!foundObjectStoreMetadata
)) {
11232 return IPC_FAIL(this, "GetMetadataForObjectStoreId failed!");
11235 SafeRefPtr
<FullIndexMetadata
> foundIndexMetadata
=
11236 GetMetadataForIndexId(*foundObjectStoreMetadata
, aIndexId
);
11238 if (NS_WARN_IF(!foundIndexMetadata
)) {
11239 return IPC_FAIL(this, "GetMetadataForIndexId failed!");
11242 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
11243 return IPC_FAIL(this, "Transaction is already committed/aborted!");
11246 foundIndexMetadata
->mCommonMetadata
.name() = aName
;
11248 RefPtr
<RenameIndexOp
> renameOp
= new RenameIndexOp(
11249 SafeRefPtrFromThis().downcast
<VersionChangeTransaction
>(),
11250 *foundIndexMetadata
, aObjectStoreId
);
11252 if (NS_WARN_IF(!renameOp
->Init(*this))) {
11253 renameOp
->Cleanup();
11254 return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
11257 renameOp
->DispatchToConnectionPool();
11262 PBackgroundIDBRequestParent
*
11263 VersionChangeTransaction::AllocPBackgroundIDBRequestParent(
11264 const RequestParams
& aParams
) {
11265 AssertIsOnBackgroundThread();
11266 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
11268 return AllocRequest(std::move(const_cast<RequestParams
&>(aParams
)),
11269 IsSameProcessActor());
11272 mozilla::ipc::IPCResult
11273 VersionChangeTransaction::RecvPBackgroundIDBRequestConstructor(
11274 PBackgroundIDBRequestParent
* aActor
, const RequestParams
& aParams
) {
11275 AssertIsOnBackgroundThread();
11276 MOZ_ASSERT(aActor
);
11277 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
11279 if (!StartRequest(aActor
)) {
11280 return IPC_FAIL(this, "StartRequest failed!");
11285 bool VersionChangeTransaction::DeallocPBackgroundIDBRequestParent(
11286 PBackgroundIDBRequestParent
* aActor
) {
11287 AssertIsOnBackgroundThread();
11288 MOZ_ASSERT(aActor
);
11290 return DeallocRequest(aActor
);
11293 already_AddRefed
<PBackgroundIDBCursorParent
>
11294 VersionChangeTransaction::AllocPBackgroundIDBCursorParent(
11295 const OpenCursorParams
& aParams
) {
11296 AssertIsOnBackgroundThread();
11298 return AllocCursor(aParams
, IsSameProcessActor());
11301 mozilla::ipc::IPCResult
11302 VersionChangeTransaction::RecvPBackgroundIDBCursorConstructor(
11303 PBackgroundIDBCursorParent
* aActor
, const OpenCursorParams
& aParams
) {
11304 AssertIsOnBackgroundThread();
11305 MOZ_ASSERT(aActor
);
11306 MOZ_ASSERT(aParams
.type() != OpenCursorParams::T__None
);
11308 if (!StartCursor(aActor
, aParams
)) {
11309 return IPC_FAIL(this, "StartCursor failed!");
11314 /*******************************************************************************
11316 ******************************************************************************/
11318 CursorBase::CursorBase(SafeRefPtr
<TransactionBase
> aTransaction
,
11319 SafeRefPtr
<FullObjectStoreMetadata
> aObjectStoreMetadata
,
11320 const Direction aDirection
,
11321 const ConstructFromTransactionBase
/*aConstructionTag*/)
11322 : mTransaction(std::move(aTransaction
)),
11323 mObjectStoreMetadata(WrapNotNull(std::move(aObjectStoreMetadata
))),
11324 mObjectStoreId((*mObjectStoreMetadata
)->mCommonMetadata
.id()),
11325 mDirection(aDirection
),
11326 mMaxExtraCount(IndexedDatabaseManager::MaxPreloadExtraRecords()),
11327 mIsSameProcessActor(!BackgroundParent::IsOtherProcessActor(
11328 mTransaction
->GetBackgroundParent())) {
11329 AssertIsOnBackgroundThread();
11330 MOZ_ASSERT(mTransaction
);
11333 OpenCursorParams::T__None
== 0 && OpenCursorParams::T__Last
== 4,
11334 "Lots of code here assumes only four types of cursors!");
11337 template <IDBCursorType CursorType
>
11338 bool Cursor
<CursorType
>::VerifyRequestParams(
11339 const CursorRequestParams
& aParams
,
11340 const CursorPosition
<CursorType
>& aPosition
) const {
11341 AssertIsOnBackgroundThread();
11342 MOZ_ASSERT(aParams
.type() != CursorRequestParams::T__None
);
11343 MOZ_ASSERT(this->mObjectStoreMetadata
);
11344 if constexpr (IsIndexCursor
) {
11345 MOZ_ASSERT(this->mIndexMetadata
);
11350 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
11351 mTransaction
->GetMetadataForObjectStoreId(mObjectStoreId
);
11352 if (objectStoreMetadata
) {
11353 MOZ_ASSERT(objectStoreMetadata
== (*this->mObjectStoreMetadata
));
11355 MOZ_ASSERT((*this->mObjectStoreMetadata
)->mDeleted
);
11358 if constexpr (IsIndexCursor
) {
11359 if (objectStoreMetadata
) {
11360 const SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
11361 mTransaction
->GetMetadataForIndexId(*objectStoreMetadata
,
11363 if (indexMetadata
) {
11364 MOZ_ASSERT(indexMetadata
== *this->mIndexMetadata
);
11366 MOZ_ASSERT((*this->mIndexMetadata
)->mDeleted
);
11373 if (NS_AUUF_OR_WARN_IF((*this->mObjectStoreMetadata
)->mDeleted
)) {
11377 if constexpr (IsIndexCursor
) {
11378 if (NS_AUUF_OR_WARN_IF(this->mIndexMetadata
&&
11379 (*this->mIndexMetadata
)->mDeleted
)) {
11384 const Key
& sortKey
= aPosition
.GetSortKey(this->IsLocaleAware());
11386 switch (aParams
.type()) {
11387 case CursorRequestParams::TContinueParams
: {
11388 const Key
& key
= aParams
.get_ContinueParams().key();
11389 if (!key
.IsUnset()) {
11390 switch (mDirection
) {
11391 case IDBCursorDirection::Next
:
11392 case IDBCursorDirection::Nextunique
:
11393 if (NS_AUUF_OR_WARN_IF(key
<= sortKey
)) {
11398 case IDBCursorDirection::Prev
:
11399 case IDBCursorDirection::Prevunique
:
11400 if (NS_AUUF_OR_WARN_IF(key
>= sortKey
)) {
11406 MOZ_CRASH("Should never get here!");
11412 case CursorRequestParams::TContinuePrimaryKeyParams
: {
11413 if constexpr (IsIndexCursor
) {
11414 const Key
& key
= aParams
.get_ContinuePrimaryKeyParams().key();
11415 const Key
& primaryKey
=
11416 aParams
.get_ContinuePrimaryKeyParams().primaryKey();
11417 MOZ_ASSERT(!key
.IsUnset());
11418 MOZ_ASSERT(!primaryKey
.IsUnset());
11419 switch (mDirection
) {
11420 case IDBCursorDirection::Next
:
11421 if (NS_AUUF_OR_WARN_IF(key
< sortKey
||
11423 primaryKey
<= aPosition
.mObjectStoreKey
))) {
11428 case IDBCursorDirection::Prev
:
11429 if (NS_AUUF_OR_WARN_IF(key
> sortKey
||
11431 primaryKey
>= aPosition
.mObjectStoreKey
))) {
11437 MOZ_CRASH("Should never get here!");
11443 case CursorRequestParams::TAdvanceParams
:
11444 if (NS_AUUF_OR_WARN_IF(!aParams
.get_AdvanceParams().count())) {
11450 MOZ_CRASH("Should never get here!");
11456 template <IDBCursorType CursorType
>
11457 bool Cursor
<CursorType
>::Start(const OpenCursorParams
& aParams
) {
11458 AssertIsOnBackgroundThread();
11459 MOZ_ASSERT(aParams
.type() == ToOpenCursorParamsType(CursorType
));
11460 MOZ_ASSERT(this->mObjectStoreMetadata
);
11462 if (NS_AUUF_OR_WARN_IF(mCurrentlyRunningOp
)) {
11466 const Maybe
<SerializedKeyRange
>& optionalKeyRange
=
11467 GetCommonOpenCursorParams(aParams
).optionalKeyRange();
11469 const RefPtr
<OpenOp
> openOp
= new OpenOp(this, optionalKeyRange
);
11471 if (NS_WARN_IF(!openOp
->Init(*mTransaction
))) {
11476 openOp
->DispatchToConnectionPool();
11477 mCurrentlyRunningOp
= openOp
;
11482 void ValueCursorBase::ProcessFiles(CursorResponse
& aResponse
,
11483 const FilesArray
& aFiles
) {
11485 aResponse
.type() == CursorResponse::Tnsresult
||
11486 aResponse
.type() == CursorResponse::Tvoid_t
||
11487 aResponse
.type() ==
11488 CursorResponse::TArrayOfObjectStoreKeyCursorResponse
||
11489 aResponse
.type() == CursorResponse::TArrayOfIndexKeyCursorResponse
,
11492 for (size_t i
= 0; i
< aFiles
.Length(); ++i
) {
11493 const auto& files
= aFiles
[i
];
11494 if (!files
.IsEmpty()) {
11495 // TODO: Replace this assertion by one that checks if the response type
11496 // matches the cursor type, at a more generic location.
11497 MOZ_ASSERT(aResponse
.type() ==
11498 CursorResponse::TArrayOfObjectStoreCursorResponse
||
11499 aResponse
.type() ==
11500 CursorResponse::TArrayOfIndexCursorResponse
);
11502 SerializedStructuredCloneReadInfo
* serializedInfo
= nullptr;
11503 switch (aResponse
.type()) {
11504 case CursorResponse::TArrayOfObjectStoreCursorResponse
: {
11505 auto& responses
= aResponse
.get_ArrayOfObjectStoreCursorResponse();
11506 MOZ_ASSERT(i
< responses
.Length());
11507 serializedInfo
= &responses
[i
].cloneInfo();
11511 case CursorResponse::TArrayOfIndexCursorResponse
: {
11512 auto& responses
= aResponse
.get_ArrayOfIndexCursorResponse();
11513 MOZ_ASSERT(i
< responses
.Length());
11514 serializedInfo
= &responses
[i
].cloneInfo();
11519 MOZ_CRASH("Should never get here!");
11522 MOZ_ASSERT(serializedInfo
);
11523 MOZ_ASSERT(serializedInfo
->files().IsEmpty());
11524 MOZ_ASSERT(this->mDatabase
);
11526 QM_TRY_UNWRAP(serializedInfo
->files(),
11527 SerializeStructuredCloneFiles(this->mDatabase
, files
,
11528 /* aForPreprocess */ false),
11529 QM_VOID
, [&aResponse
](const nsresult result
) {
11530 aResponse
= ClampResultCode(result
);
11536 template <IDBCursorType CursorType
>
11537 void Cursor
<CursorType
>::SendResponseInternal(
11538 CursorResponse
& aResponse
, const FilesArrayT
<CursorType
>& aFiles
) {
11539 AssertIsOnBackgroundThread();
11540 MOZ_ASSERT(aResponse
.type() != CursorResponse::T__None
);
11541 MOZ_ASSERT_IF(aResponse
.type() == CursorResponse::Tnsresult
,
11542 NS_FAILED(aResponse
.get_nsresult()));
11543 MOZ_ASSERT_IF(aResponse
.type() == CursorResponse::Tnsresult
,
11544 NS_ERROR_GET_MODULE(aResponse
.get_nsresult()) ==
11545 NS_ERROR_MODULE_DOM_INDEXEDDB
);
11546 MOZ_ASSERT(this->mObjectStoreMetadata
);
11547 MOZ_ASSERT(mCurrentlyRunningOp
);
11549 KeyValueBase::ProcessFiles(aResponse
, aFiles
);
11551 // Work around the deleted function by casting to the base class.
11552 QM_WARNONLY_TRY(OkIf(
11553 static_cast<PBackgroundIDBCursorParent
*>(this)->SendResponse(aResponse
)));
11555 mCurrentlyRunningOp
= nullptr;
11558 template <IDBCursorType CursorType
>
11559 void Cursor
<CursorType
>::ActorDestroy(ActorDestroyReason aWhy
) {
11560 AssertIsOnBackgroundThread();
11562 if (mCurrentlyRunningOp
) {
11563 mCurrentlyRunningOp
->NoteActorDestroyed();
11566 if constexpr (IsValueCursor
) {
11567 this->mBackgroundParent
.destroy();
11569 this->mObjectStoreMetadata
.destroy();
11570 if constexpr (IsIndexCursor
) {
11571 this->mIndexMetadata
.destroy();
11575 template <IDBCursorType CursorType
>
11576 mozilla::ipc::IPCResult Cursor
<CursorType
>::RecvDeleteMe() {
11577 AssertIsOnBackgroundThread();
11578 MOZ_ASSERT(this->mObjectStoreMetadata
);
11580 if (NS_WARN_IF(mCurrentlyRunningOp
)) {
11583 "Attempt to delete a cursor with a non-null mCurrentlyRunningOp!");
11586 QM_WARNONLY_TRY(OkIf(PBackgroundIDBCursorParent::Send__delete__(this)));
11591 template <IDBCursorType CursorType
>
11592 mozilla::ipc::IPCResult Cursor
<CursorType
>::RecvContinue(
11593 const CursorRequestParams
& aParams
, const Key
& aCurrentKey
,
11594 const Key
& aCurrentObjectStoreKey
) {
11595 AssertIsOnBackgroundThread();
11596 MOZ_ASSERT(aParams
.type() != CursorRequestParams::T__None
);
11597 MOZ_ASSERT(this->mObjectStoreMetadata
);
11598 if constexpr (IsIndexCursor
) {
11599 MOZ_ASSERT(this->mIndexMetadata
);
11602 const bool trustParams
=
11604 // Always verify parameters in DEBUG builds!
11607 this->mIsSameProcessActor
11611 MOZ_ASSERT(!aCurrentKey
.IsUnset());
11615 ([&]() -> Result
<CursorPosition
<CursorType
>, mozilla::ipc::IPCResult
> {
11616 if constexpr (IsIndexCursor
) {
11617 auto localeAwarePosition
= Key
{};
11618 if (this->IsLocaleAware()) {
11620 localeAwarePosition
,
11621 aCurrentKey
.ToLocaleAwareKey(this->mLocale
),
11622 Err(IPC_FAIL(this, "aCurrentKey.ToLocaleAwareKey failed!")));
11624 return CursorPosition
<CursorType
>{aCurrentKey
, localeAwarePosition
,
11625 aCurrentObjectStoreKey
};
11627 return CursorPosition
<CursorType
>{aCurrentKey
};
11631 if (!trustParams
&& !VerifyRequestParams(aParams
, position
)) {
11632 return IPC_FAIL(this, "VerifyRequestParams failed!");
11635 if (NS_WARN_IF(mCurrentlyRunningOp
)) {
11636 return IPC_FAIL(this, "Cursor is CurrentlyRunningOp!");
11639 if (NS_WARN_IF(mTransaction
->mCommitOrAbortReceived
)) {
11640 return IPC_FAIL(this, "Transaction is already committed/aborted!");
11643 const RefPtr
<ContinueOp
> continueOp
=
11644 new ContinueOp(this, aParams
, std::move(position
));
11645 if (NS_WARN_IF(!continueOp
->Init(*mTransaction
))) {
11646 continueOp
->Cleanup();
11647 return IPC_FAIL(this, "ContinueOp initialization failed!");
11650 continueOp
->DispatchToConnectionPool();
11651 mCurrentlyRunningOp
= continueOp
;
11656 /*******************************************************************************
11657 * DatabaseFileManager
11658 ******************************************************************************/
11660 DatabaseFileManager::MutexType
DatabaseFileManager::sMutex
;
11662 DatabaseFileManager::DatabaseFileManager(
11663 PersistenceType aPersistenceType
,
11664 const quota::OriginMetadata
& aOriginMetadata
,
11665 const nsAString
& aDatabaseName
, const nsCString
& aDatabaseID
,
11666 bool aEnforcingQuota
, bool aIsInPrivateBrowsingMode
)
11667 : mPersistenceType(aPersistenceType
),
11668 mOriginMetadata(aOriginMetadata
),
11669 mDatabaseName(aDatabaseName
),
11670 mDatabaseID(aDatabaseID
),
11671 mEnforcingQuota(aEnforcingQuota
),
11672 mIsInPrivateBrowsingMode(aIsInPrivateBrowsingMode
) {}
11674 nsresult
DatabaseFileManager::Init(nsIFile
* aDirectory
,
11675 mozIStorageConnection
& aConnection
) {
11676 AssertIsOnIOThread();
11677 MOZ_ASSERT(aDirectory
);
11680 QM_TRY_INSPECT(const bool& existsAsDirectory
,
11681 ExistsAsDirectory(*aDirectory
));
11683 if (!existsAsDirectory
) {
11684 QM_TRY(MOZ_TO_RESULT(aDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0755)));
11687 QM_TRY_UNWRAP(auto path
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
11688 nsString
, aDirectory
, GetPath
));
11690 mDirectoryPath
.init(std::move(path
));
11693 QM_TRY_INSPECT(const auto& journalDirectory
,
11694 CloneFileAndAppend(*aDirectory
, kJournalDirectoryName
));
11696 // We don't care if it doesn't exist at all, but if it does exist, make sure
11697 // it's a directory.
11698 QM_TRY_INSPECT(const bool& existsAsDirectory
,
11699 ExistsAsDirectory(*journalDirectory
));
11700 Unused
<< existsAsDirectory
;
11703 QM_TRY_UNWRAP(auto path
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
11704 nsString
, journalDirectory
, GetPath
));
11706 mJournalDirectoryPath
.init(std::move(path
));
11709 QM_TRY_INSPECT(const auto& stmt
,
11710 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
11711 nsCOMPtr
<mozIStorageStatement
>, aConnection
,
11712 CreateStatement
, "SELECT id, refcount FROM file"_ns
));
11715 CollectWhileHasResult(*stmt
, [this](auto& stmt
) -> Result
<Ok
, nsresult
> {
11716 QM_TRY_INSPECT(const int64_t& id
,
11717 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetInt64
, 0));
11718 QM_TRY_INSPECT(const int32_t& dbRefCnt
,
11719 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetInt32
, 1));
11721 // We put a raw pointer into the hash table, so the memory refcount will
11722 // be 0, but the dbRefCnt is non-zero, which will keep the
11723 // DatabaseFileInfo object alive.
11724 MOZ_ASSERT(dbRefCnt
> 0);
11725 mFileInfos
.InsertOrUpdate(
11726 id
, MakeNotNull
<DatabaseFileInfo
*>(
11727 FileInfoManagerGuard
{}, SafeRefPtrFromThis(), id
,
11728 static_cast<nsrefcnt
>(dbRefCnt
)));
11730 mLastFileId
= std::max(id
, mLastFileId
);
11738 nsCOMPtr
<nsIFile
> DatabaseFileManager::GetDirectory() {
11739 if (!this->AssertValid()) {
11743 return GetFileForPath(*mDirectoryPath
);
11746 nsCOMPtr
<nsIFile
> DatabaseFileManager::GetCheckedDirectory() {
11747 auto directory
= GetDirectory();
11748 if (NS_WARN_IF(!directory
)) {
11752 DebugOnly
<bool> exists
;
11753 MOZ_ASSERT(NS_SUCCEEDED(directory
->Exists(&exists
)));
11754 MOZ_ASSERT(exists
);
11756 DebugOnly
<bool> isDirectory
;
11757 MOZ_ASSERT(NS_SUCCEEDED(directory
->IsDirectory(&isDirectory
)));
11758 MOZ_ASSERT(isDirectory
);
11763 nsCOMPtr
<nsIFile
> DatabaseFileManager::GetJournalDirectory() {
11764 if (!this->AssertValid()) {
11768 return GetFileForPath(*mJournalDirectoryPath
);
11771 nsCOMPtr
<nsIFile
> DatabaseFileManager::EnsureJournalDirectory() {
11772 // This can happen on the IO or on a transaction thread.
11773 MOZ_ASSERT(!NS_IsMainThread());
11775 auto journalDirectory
= GetFileForPath(*mJournalDirectoryPath
);
11776 QM_TRY(OkIf(journalDirectory
), nullptr);
11778 QM_TRY_INSPECT(const bool& exists
,
11779 MOZ_TO_RESULT_INVOKE_MEMBER(journalDirectory
, Exists
),
11783 QM_TRY_INSPECT(const bool& isDirectory
,
11784 MOZ_TO_RESULT_INVOKE_MEMBER(journalDirectory
, IsDirectory
),
11787 QM_TRY(OkIf(isDirectory
), nullptr);
11790 MOZ_TO_RESULT(journalDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0755)),
11794 return journalDirectory
;
11798 nsCOMPtr
<nsIFile
> DatabaseFileManager::GetFileForId(nsIFile
* aDirectory
,
11800 MOZ_ASSERT(aDirectory
);
11801 MOZ_ASSERT(aId
> 0);
11803 QM_TRY_RETURN(CloneFileAndAppend(*aDirectory
, IntToString(aId
)), nullptr);
11807 nsCOMPtr
<nsIFile
> DatabaseFileManager::GetCheckedFileForId(nsIFile
* aDirectory
,
11809 auto file
= GetFileForId(aDirectory
, aId
);
11810 if (NS_WARN_IF(!file
)) {
11814 DebugOnly
<bool> exists
;
11815 MOZ_ASSERT(NS_SUCCEEDED(file
->Exists(&exists
)));
11816 MOZ_ASSERT(exists
);
11818 DebugOnly
<bool> isFile
;
11819 MOZ_ASSERT(NS_SUCCEEDED(file
->IsFile(&isFile
)));
11820 MOZ_ASSERT(isFile
);
11826 nsresult
DatabaseFileManager::InitDirectory(nsIFile
& aDirectory
,
11827 nsIFile
& aDatabaseFile
,
11828 const nsACString
& aOrigin
,
11829 uint32_t aTelemetryId
) {
11830 AssertIsOnIOThread();
11833 QM_TRY_INSPECT(const bool& exists
,
11834 MOZ_TO_RESULT_INVOKE_MEMBER(aDirectory
, Exists
));
11840 QM_TRY_INSPECT(const bool& isDirectory
,
11841 MOZ_TO_RESULT_INVOKE_MEMBER(aDirectory
, IsDirectory
));
11842 QM_TRY(OkIf(isDirectory
), NS_ERROR_FAILURE
);
11845 QM_TRY_INSPECT(const auto& journalDirectory
,
11846 CloneFileAndAppend(aDirectory
, kJournalDirectoryName
));
11848 QM_TRY_INSPECT(const bool& exists
,
11849 MOZ_TO_RESULT_INVOKE_MEMBER(journalDirectory
, Exists
));
11852 QM_TRY_INSPECT(const bool& isDirectory
,
11853 MOZ_TO_RESULT_INVOKE_MEMBER(journalDirectory
, IsDirectory
));
11854 QM_TRY(OkIf(isDirectory
), NS_ERROR_FAILURE
);
11856 bool hasJournals
= false;
11858 QM_TRY(CollectEachFile(
11860 [&hasJournals
](const nsCOMPtr
<nsIFile
>& file
) -> Result
<Ok
, nsresult
> {
11862 const auto& leafName
,
11863 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString
, file
, GetLeafName
));
11866 leafName
.ToInteger64(&rv
);
11867 if (NS_SUCCEEDED(rv
)) {
11868 hasJournals
= true;
11870 UNKNOWN_FILE_WARNING(leafName
);
11877 QM_TRY_UNWRAP(const NotNull
<nsCOMPtr
<mozIStorageConnection
>> connection
,
11878 CreateStorageConnection(
11879 aDatabaseFile
, aDirectory
, VoidString(), aOrigin
,
11880 /* aDirectoryLockId */ -1, aTelemetryId
, Nothing
{}));
11882 mozStorageTransaction
transaction(connection
.get(), false);
11884 QM_TRY(MOZ_TO_RESULT(transaction
.Start()))
11886 QM_TRY(MOZ_TO_RESULT(connection
->ExecuteSimpleSQL(
11887 "CREATE VIRTUAL TABLE fs USING filesystem;"_ns
)));
11889 // The parameter names are not used, parameters are bound by index only
11890 // locally in the same function.
11893 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
11894 nsCOMPtr
<mozIStorageStatement
>, *connection
, CreateStatement
,
11895 "SELECT name, (name IN (SELECT id FROM file)) FROM fs WHERE path = :path"_ns
));
11897 QM_TRY_INSPECT(const auto& path
,
11898 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
11899 nsString
, journalDirectory
, GetPath
));
11901 QM_TRY(MOZ_TO_RESULT(stmt
->BindStringByIndex(0, path
)));
11903 QM_TRY(CollectWhileHasResult(
11905 [&aDirectory
, &journalDirectory
](auto& stmt
) -> Result
<Ok
, nsresult
> {
11907 QM_TRY(MOZ_TO_RESULT(stmt
.GetString(0, name
)));
11910 name
.ToInteger64(&rv
);
11911 if (NS_FAILED(rv
)) {
11915 int32_t flag
= stmt
.AsInt32(1);
11918 QM_TRY_INSPECT(const auto& file
,
11919 CloneFileAndAppend(aDirectory
, name
));
11921 if (NS_FAILED(file
->Remove(false))) {
11922 NS_WARNING("Failed to remove orphaned file!");
11926 QM_TRY_INSPECT(const auto& journalFile
,
11927 CloneFileAndAppend(*journalDirectory
, name
));
11929 if (NS_FAILED(journalFile
->Remove(false))) {
11930 NS_WARNING("Failed to remove journal file!");
11936 QM_TRY(MOZ_TO_RESULT(connection
->ExecuteSimpleSQL("DROP TABLE fs;"_ns
)));
11937 QM_TRY(MOZ_TO_RESULT(transaction
.Commit()));
11945 Result
<FileUsageType
, nsresult
> DatabaseFileManager::GetUsage(
11946 nsIFile
* aDirectory
) {
11947 AssertIsOnIOThread();
11948 MOZ_ASSERT(aDirectory
);
11950 FileUsageType usage
;
11952 QM_TRY(TraverseFiles(
11955 [&usage
](nsIFile
& file
, const bool isDirectory
) -> Result
<Ok
, nsresult
> {
11960 // Usually we only use QM_OR_ELSE_LOG_VERBOSE(_IF) with Remove and
11961 // NS_ERROR_FILE_NOT_FOUND check, but the file was found by a directory
11962 // traversal and ToInteger on the name succeeded, so it should be our
11963 // file and if the file disappears, the use of QM_OR_ELSE_WARN_IF is ok
11965 QM_TRY_INSPECT(const auto& thisUsage
,
11966 QM_OR_ELSE_WARN_IF(
11968 MOZ_TO_RESULT_INVOKE_MEMBER(file
, GetFileSize
)
11969 .map([](const int64_t fileSize
) {
11970 return FileUsageType(Some(uint64_t(fileSize
)));
11973 ([](const nsresult rv
) {
11974 return rv
== NS_ERROR_FILE_NOT_FOUND
;
11976 // Fallback. If the file does no longer exist, treat
11978 ErrToDefaultOk
<FileUsageType
>));
11980 usage
+= thisUsage
;
11984 // UnknownDirEntryOp
11985 [](nsIFile
&, const bool) -> Result
<Ok
, nsresult
> { return Ok
{}; }));
11990 nsresult
DatabaseFileManager::SyncDeleteFile(const int64_t aId
) {
11991 MOZ_ASSERT(!mFileInfos
.Contains(aId
));
11993 if (!this->AssertValid()) {
11994 return NS_ERROR_UNEXPECTED
;
11997 const auto directory
= GetDirectory();
11998 QM_TRY(OkIf(directory
), NS_ERROR_FAILURE
);
12000 const auto journalDirectory
= GetJournalDirectory();
12001 QM_TRY(OkIf(journalDirectory
), NS_ERROR_FAILURE
);
12003 const nsCOMPtr
<nsIFile
> file
= GetFileForId(directory
, aId
);
12004 QM_TRY(OkIf(file
), NS_ERROR_FAILURE
);
12006 const nsCOMPtr
<nsIFile
> journalFile
= GetFileForId(journalDirectory
, aId
);
12007 QM_TRY(OkIf(journalFile
), NS_ERROR_FAILURE
);
12009 return SyncDeleteFile(*file
, *journalFile
);
12012 nsresult
DatabaseFileManager::SyncDeleteFile(nsIFile
& aFile
,
12013 nsIFile
& aJournalFile
) const {
12014 QuotaManager
* const quotaManager
=
12015 EnforcingQuota() ? QuotaManager::Get() : nullptr;
12016 MOZ_ASSERT_IF(EnforcingQuota(), quotaManager
);
12018 QM_TRY(MOZ_TO_RESULT(DeleteFile(aFile
, quotaManager
, Type(), OriginMetadata(),
12019 Idempotency::No
)));
12021 QM_TRY(MOZ_TO_RESULT(aJournalFile
.Remove(false)));
12026 /*******************************************************************************
12028 ******************************************************************************/
12030 QuotaClient
* QuotaClient::sInstance
= nullptr;
12032 QuotaClient::QuotaClient() : mDeleteTimer(NS_NewTimer()) {
12033 AssertIsOnBackgroundThread();
12034 MOZ_ASSERT(!sInstance
, "We expect this to be a singleton!");
12035 MOZ_ASSERT(!gTelemetryIdMutex
);
12037 // Always create this so that later access to gTelemetryIdHashtable can be
12038 // properly synchronized.
12039 gTelemetryIdMutex
= new Mutex("IndexedDB gTelemetryIdMutex");
12044 QuotaClient::~QuotaClient() {
12045 AssertIsOnBackgroundThread();
12046 MOZ_ASSERT(sInstance
== this, "We expect this to be a singleton!");
12047 MOZ_ASSERT(gTelemetryIdMutex
);
12048 MOZ_ASSERT(!mMaintenanceThreadPool
);
12050 // No one else should be able to touch gTelemetryIdHashtable now that the
12051 // QuotaClient has gone away.
12052 gTelemetryIdHashtable
= nullptr;
12053 gTelemetryIdMutex
= nullptr;
12055 sInstance
= nullptr;
12058 nsresult
QuotaClient::AsyncDeleteFile(DatabaseFileManager
* aFileManager
,
12060 AssertIsOnBackgroundThread();
12062 if (IsShuttingDownOnBackgroundThread()) {
12063 // Whoops! We want to delete an IndexedDB disk-backed File but it's too late
12064 // to actually delete the file! This means we're going to "leak" the file
12065 // and leave it around when we shouldn't! (The file will stay around until
12066 // next storage initialization is triggered when the app is started again).
12067 // Fixing this is tracked by bug 1539377.
12072 MOZ_ASSERT(mDeleteTimer
);
12073 MOZ_ALWAYS_SUCCEEDS(mDeleteTimer
->Cancel());
12075 QM_TRY(MOZ_TO_RESULT(mDeleteTimer
->InitWithNamedFuncCallback(
12076 DeleteTimerCallback
, this, kDeleteTimeoutMs
, nsITimer::TYPE_ONE_SHOT
,
12077 "dom::indexeddb::QuotaClient::AsyncDeleteFile")));
12079 mPendingDeleteInfos
.GetOrInsertNew(aFileManager
)->AppendElement(aFileId
);
12084 nsresult
QuotaClient::FlushPendingFileDeletions() {
12085 AssertIsOnBackgroundThread();
12087 QM_TRY(MOZ_TO_RESULT(mDeleteTimer
->Cancel()));
12089 DeleteTimerCallback(mDeleteTimer
, this);
12094 nsThreadPool
* QuotaClient::GetOrCreateThreadPool() {
12095 AssertIsOnBackgroundThread();
12096 MOZ_ASSERT(!IsShuttingDownOnBackgroundThread());
12098 if (!mMaintenanceThreadPool
) {
12099 RefPtr
<nsThreadPool
> threadPool
= new nsThreadPool();
12101 // PR_GetNumberOfProcessors() can return -1 on error, so make sure we
12102 // don't set some huge number here. We add 2 in case some threads block on
12104 const uint32_t threadCount
=
12105 std::max(int32_t(PR_GetNumberOfProcessors()), int32_t(1)) + 2;
12107 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetThreadLimit(threadCount
));
12109 // Don't keep more than one idle thread.
12110 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetIdleThreadLimit(1));
12112 // Don't keep idle threads alive very long.
12113 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetIdleThreadTimeout(5 * PR_MSEC_PER_SEC
));
12115 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetName("IndexedDB Mnt"_ns
));
12117 mMaintenanceThreadPool
= std::move(threadPool
);
12120 return mMaintenanceThreadPool
;
12123 mozilla::dom::quota::Client::Type
QuotaClient::GetType() {
12124 return QuotaClient::IDB
;
12127 nsresult
QuotaClient::UpgradeStorageFrom1_0To2_0(nsIFile
* aDirectory
) {
12128 AssertIsOnIOThread();
12129 MOZ_ASSERT(aDirectory
);
12131 QM_TRY_INSPECT((const auto& [subdirsToProcess
, databaseFilenames
]),
12132 GetDatabaseFilenames(*aDirectory
,
12133 /* aCanceled */ AtomicBool
{false}));
12135 QM_TRY(CollectEachInRange(
12137 [&databaseFilenames
= databaseFilenames
,
12138 aDirectory
](const nsAString
& subdirName
) -> Result
<Ok
, nsresult
> {
12139 // If the directory has the correct suffix then it should exist in
12140 // databaseFilenames.
12141 nsDependentSubstring subdirNameBase
;
12142 if (GetFilenameBase(subdirName
, kFileManagerDirectoryNameSuffix
,
12144 QM_WARNONLY_TRY(OkIf(databaseFilenames
.Contains(subdirNameBase
)));
12148 // The directory didn't have the right suffix but we might need to
12149 // rename it. Check to see if we have a database that references this
12152 const auto& subdirNameWithSuffix
,
12153 ([&databaseFilenames
,
12154 &subdirName
]() -> Result
<nsAutoString
, NotOk
> {
12155 if (databaseFilenames
.Contains(subdirName
)) {
12156 return nsAutoString
{subdirName
+
12157 kFileManagerDirectoryNameSuffix
};
12160 // Windows doesn't allow a directory to end with a dot ('.'), so
12161 // we have to check that possibility here too. We do this on all
12162 // platforms, because the origin directory may have been created
12163 // on Windows and now accessed on different OS.
12164 const nsAutoString subdirNameWithDot
= subdirName
+ u
"."_ns
;
12165 QM_TRY(OkIf(databaseFilenames
.Contains(subdirNameWithDot
)),
12168 return nsAutoString
{subdirNameWithDot
+
12169 kFileManagerDirectoryNameSuffix
};
12173 // We do have a database that uses this subdir so we should rename it
12175 QM_TRY_INSPECT(const auto& subdir
,
12176 CloneFileAndAppend(*aDirectory
, subdirName
));
12178 DebugOnly
<bool> isDirectory
;
12179 MOZ_ASSERT(NS_SUCCEEDED(subdir
->IsDirectory(&isDirectory
)));
12180 MOZ_ASSERT(isDirectory
);
12182 // Check if the subdir with suffix already exists before renaming.
12183 QM_TRY_INSPECT(const auto& subdirWithSuffix
,
12184 CloneFileAndAppend(*aDirectory
, subdirNameWithSuffix
));
12186 QM_TRY_INSPECT(const bool& exists
,
12187 MOZ_TO_RESULT_INVOKE_MEMBER(subdirWithSuffix
, Exists
));
12190 IDB_WARNING("Deleting old %s files directory!",
12191 NS_ConvertUTF16toUTF8(subdirName
).get());
12193 QM_TRY(MOZ_TO_RESULT(subdir
->Remove(/* aRecursive */ true)));
12198 // Finally, rename the subdir.
12199 QM_TRY(MOZ_TO_RESULT(subdir
->RenameTo(nullptr, subdirNameWithSuffix
)));
12207 nsresult
QuotaClient::UpgradeStorageFrom2_1To2_2(nsIFile
* aDirectory
) {
12208 AssertIsOnIOThread();
12209 MOZ_ASSERT(aDirectory
);
12211 QM_TRY(CollectEachFile(
12212 *aDirectory
, [](const nsCOMPtr
<nsIFile
>& file
) -> Result
<Ok
, nsresult
> {
12213 QM_TRY_INSPECT(const auto& dirEntryKind
, GetDirEntryKind(*file
));
12215 switch (dirEntryKind
) {
12216 case nsIFileKind::ExistsAsDirectory
:
12219 case nsIFileKind::ExistsAsFile
: {
12221 const auto& leafName
,
12222 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString
, file
, GetLeafName
));
12224 // It's reported that files ending with ".tmp" somehow live in the
12225 // indexedDB directories in Bug 1503883. Such files shouldn't exist
12226 // in the indexedDB directory so remove them in this upgrade.
12227 if (StringEndsWith(leafName
, u
".tmp"_ns
)) {
12228 IDB_WARNING("Deleting unknown temporary file!");
12230 QM_TRY(MOZ_TO_RESULT(file
->Remove(false)));
12236 case nsIFileKind::DoesNotExist
:
12237 // Ignore files that got removed externally while iterating.
12247 Result
<UsageInfo
, nsresult
> QuotaClient::InitOrigin(
12248 PersistenceType aPersistenceType
, const OriginMetadata
& aOriginMetadata
,
12249 const AtomicBool
& aCanceled
) {
12250 AssertIsOnIOThread();
12252 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(this, GetUsageForOriginInternal
,
12253 aPersistenceType
, aOriginMetadata
,
12255 /* aInitializing*/ true));
12258 nsresult
QuotaClient::InitOriginWithoutTracking(
12259 PersistenceType aPersistenceType
, const OriginMetadata
& aOriginMetadata
,
12260 const AtomicBool
& aCanceled
) {
12261 AssertIsOnIOThread();
12263 return GetUsageForOriginInternal(aPersistenceType
, aOriginMetadata
, aCanceled
,
12264 /* aInitializing*/ true, nullptr);
12267 Result
<UsageInfo
, nsresult
> QuotaClient::GetUsageForOrigin(
12268 PersistenceType aPersistenceType
, const OriginMetadata
& aOriginMetadata
,
12269 const AtomicBool
& aCanceled
) {
12270 AssertIsOnIOThread();
12272 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(this, GetUsageForOriginInternal
,
12273 aPersistenceType
, aOriginMetadata
,
12275 /* aInitializing*/ false));
12278 nsresult
QuotaClient::GetUsageForOriginInternal(
12279 PersistenceType aPersistenceType
, const OriginMetadata
& aOriginMetadata
,
12280 const AtomicBool
& aCanceled
, const bool aInitializing
,
12281 UsageInfo
* aUsageInfo
) {
12282 AssertIsOnIOThread();
12284 QM_TRY_INSPECT(const nsCOMPtr
<nsIFile
>& directory
,
12285 GetDirectory(aPersistenceType
, aOriginMetadata
.mOrigin
));
12287 // We need to see if there are any files in the directory already. If they
12288 // are database files then we need to cleanup stored files (if it's needed)
12289 // and also get the usage.
12291 // XXX Can we avoid unwrapping into non-const variables here? (Only
12292 // databaseFilenames is currently modified below)
12293 QM_TRY_UNWRAP((auto [subdirsToProcess
, databaseFilenames
, obsoleteFilenames
]),
12294 GetDatabaseFilenames
<ObsoleteFilenamesHandling::Include
>(
12295 *directory
, aCanceled
));
12297 if (aInitializing
) {
12298 QM_TRY(CollectEachInRange(
12300 [&directory
, &obsoleteFilenames
= obsoleteFilenames
,
12301 &databaseFilenames
= databaseFilenames
, aPersistenceType
,
12303 const nsAString
& subdirName
) -> Result
<Ok
, nsresult
> {
12304 // The directory must have the correct suffix.
12305 nsDependentSubstring subdirNameBase
;
12306 QM_TRY(QM_OR_ELSE_WARN(
12308 ([&subdirName
, &subdirNameBase
] {
12309 QM_TRY_RETURN(OkIf(GetFilenameBase(
12310 subdirName
, kFileManagerDirectoryNameSuffix
,
12315 &subdirName
](const NotOk
) -> Result
<Ok
, nsresult
> {
12316 // If there is an unexpected directory in the idb
12317 // directory, trying to delete at first instead of
12318 // breaking the whole initialization.
12319 QM_TRY(MOZ_TO_RESULT(
12320 DeleteFilesNoQuota(directory
, subdirName
)),
12321 Err(NS_ERROR_UNEXPECTED
));
12327 if (obsoleteFilenames
.Contains(subdirNameBase
)) {
12328 // If this fails, it probably means we are in a serious situation.
12329 // e.g. Filesystem corruption. Will handle this in bug 1521541.
12330 QM_TRY(MOZ_TO_RESULT(RemoveDatabaseFilesAndDirectory(
12331 *directory
, subdirNameBase
, nullptr, aPersistenceType
,
12332 aOriginMetadata
, u
""_ns
)),
12333 Err(NS_ERROR_UNEXPECTED
));
12335 databaseFilenames
.Remove(subdirNameBase
);
12339 // The directory base must exist in databaseFilenames.
12340 // If there is an unexpected directory in the idb directory, trying to
12341 // delete at first instead of breaking the whole initialization.
12343 // XXX This is still somewhat quirky. It would be nice to make it
12344 // clear that the warning handler is infallible, which would also
12345 // remove the need for the error type conversion.
12346 QM_WARNONLY_TRY(QM_OR_ELSE_WARN(
12348 OkIf(databaseFilenames
.Contains(subdirNameBase
))
12349 .mapErr([](const NotOk
) { return NS_ERROR_FAILURE
; }),
12352 &subdirName
](const nsresult
) -> Result
<Ok
, nsresult
> {
12353 // XXX It seems if we really got here, we can fail the
12354 // MOZ_ASSERT(!quotaManager->IsTemporaryStorageInitialized());
12355 // assertion in DeleteFilesNoQuota.
12356 QM_TRY(MOZ_TO_RESULT(DeleteFilesNoQuota(directory
, subdirName
)),
12357 Err(NS_ERROR_UNEXPECTED
));
12366 for (const auto& databaseFilename
: databaseFilenames
) {
12372 const auto& fmDirectory
,
12373 CloneFileAndAppend(*directory
,
12374 databaseFilename
+ kFileManagerDirectoryNameSuffix
));
12377 const auto& databaseFile
,
12378 CloneFileAndAppend(*directory
, databaseFilename
+ kSQLiteSuffix
));
12380 if (aInitializing
) {
12381 QM_TRY(MOZ_TO_RESULT(DatabaseFileManager::InitDirectory(
12382 *fmDirectory
, *databaseFile
, aOriginMetadata
.mOrigin
,
12383 TelemetryIdForFile(databaseFile
))));
12388 QM_TRY_INSPECT(const int64_t& fileSize
,
12389 MOZ_TO_RESULT_INVOKE_MEMBER(databaseFile
, GetFileSize
));
12391 MOZ_ASSERT(fileSize
>= 0);
12393 *aUsageInfo
+= DatabaseUsageType(Some(uint64_t(fileSize
)));
12397 QM_TRY_INSPECT(const auto& walFile
,
12398 CloneFileAndAppend(*directory
,
12399 databaseFilename
+ kSQLiteWALSuffix
));
12401 // QM_OR_ELSE_WARN_IF is not used here since we just want to log
12402 // NS_ERROR_FILE_NOT_FOUND result and not spam the reports (the -wal
12403 // file doesn't have to exist).
12404 QM_TRY_INSPECT(const int64_t& walFileSize
,
12405 QM_OR_ELSE_LOG_VERBOSE_IF(
12407 MOZ_TO_RESULT_INVOKE_MEMBER(walFile
, GetFileSize
),
12409 ([](const nsresult rv
) {
12410 return rv
== NS_ERROR_FILE_NOT_FOUND
;
12413 (ErrToOk
<0, int64_t>)));
12414 MOZ_ASSERT(walFileSize
>= 0);
12415 *aUsageInfo
+= DatabaseUsageType(Some(uint64_t(walFileSize
)));
12419 QM_TRY_INSPECT(const auto& fileUsage
,
12420 DatabaseFileManager::GetUsage(fmDirectory
));
12422 *aUsageInfo
+= fileUsage
;
12430 void QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType
,
12431 const nsACString
& aOrigin
) {
12432 AssertIsOnIOThread();
12434 if (IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get()) {
12435 mgr
->InvalidateFileManagers(aPersistenceType
, aOrigin
);
12439 void QuotaClient::ReleaseIOThreadObjects() {
12440 AssertIsOnIOThread();
12442 if (IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get()) {
12443 mgr
->InvalidateAllFileManagers();
12447 void QuotaClient::AbortOperationsForLocks(
12448 const DirectoryLockIdTable
& aDirectoryLockIds
) {
12449 AssertIsOnBackgroundThread();
12451 InvalidateLiveDatabasesMatching([&aDirectoryLockIds
](const auto& database
) {
12452 // If the database is registered in gLiveDatabaseHashtable then it must have
12453 // a directory lock.
12454 return IsLockForObjectContainedInLockTable(database
, aDirectoryLockIds
);
12458 void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId
) {
12459 AssertIsOnBackgroundThread();
12461 InvalidateLiveDatabasesMatching([&aContentParentId
](const auto& database
) {
12462 return database
.IsOwnedByProcess(aContentParentId
);
12466 void QuotaClient::AbortAllOperations() {
12467 AssertIsOnBackgroundThread();
12469 InvalidateLiveDatabasesMatching([](const auto&) { return true; });
12472 void QuotaClient::StartIdleMaintenance() {
12473 AssertIsOnBackgroundThread();
12474 if (IsShuttingDownOnBackgroundThread()) {
12475 MOZ_ASSERT(false, "!IsShuttingDownOnBackgroundThread()");
12479 mBackgroundThread
= GetCurrentSerialEventTarget();
12481 mMaintenanceQueue
.EmplaceBack(MakeRefPtr
<Maintenance
>(this));
12482 ProcessMaintenanceQueue();
12485 void QuotaClient::StopIdleMaintenance() {
12486 AssertIsOnBackgroundThread();
12488 if (mCurrentMaintenance
) {
12489 mCurrentMaintenance
->Abort();
12492 for (const auto& maintenance
: mMaintenanceQueue
) {
12493 maintenance
->Abort();
12497 void QuotaClient::InitiateShutdown() {
12498 AssertIsOnBackgroundThread();
12500 AbortAllOperations();
12503 bool QuotaClient::IsShutdownCompleted() const {
12504 return (!gFactoryOps
|| gFactoryOps
->IsEmpty()) &&
12505 (!gLiveDatabaseHashtable
|| !gLiveDatabaseHashtable
->Count()) &&
12506 !mCurrentMaintenance
;
12509 void QuotaClient::ForceKillActors() {
12510 // Currently we don't implement force killing actors.
12513 nsCString
QuotaClient::GetShutdownStatus() const {
12514 AssertIsOnBackgroundThread();
12518 if (gFactoryOps
&& !gFactoryOps
->IsEmpty()) {
12519 data
.Append("FactoryOperations: "_ns
+
12520 IntToCString(static_cast<uint32_t>(gFactoryOps
->Length())) +
12523 // XXX It might be confusing to remove duplicates here, as the actual list
12524 // won't match the count then.
12525 nsTHashSet
<nsCString
> ids
;
12526 std::transform(gFactoryOps
->cbegin(), gFactoryOps
->cend(),
12527 MakeInserter(ids
), [](const auto& factoryOp
) {
12528 MOZ_ASSERT(factoryOp
);
12531 factoryOp
->Stringify(id
);
12535 StringJoinAppend(data
, ", "_ns
, ids
);
12537 data
.Append(")\n");
12540 if (gLiveDatabaseHashtable
&& gLiveDatabaseHashtable
->Count()) {
12541 data
.Append("LiveDatabases: "_ns
+
12542 IntToCString(gLiveDatabaseHashtable
->Count()) + " ("_ns
);
12544 // XXX What's the purpose of adding these to a hashtable before joining them
12545 // to the string? (Maybe this used to be an ordered container before???)
12546 nsTHashSet
<nsCString
> ids
;
12548 for (const auto& entry
: gLiveDatabaseHashtable
->Values()) {
12551 std::transform(entry
->mLiveDatabases
.cbegin(),
12552 entry
->mLiveDatabases
.cend(), MakeInserter(ids
),
12553 [](const auto& database
) {
12555 database
->Stringify(id
);
12560 StringJoinAppend(data
, ", "_ns
, ids
);
12562 data
.Append(")\n");
12565 if (mCurrentMaintenance
) {
12566 data
.Append("IdleMaintenance: 1 (");
12567 mCurrentMaintenance
->Stringify(data
);
12568 data
.Append(")\n");
12574 void QuotaClient::FinalizeShutdown() {
12575 RefPtr
<ConnectionPool
> connectionPool
= gConnectionPool
.get();
12576 if (connectionPool
) {
12577 connectionPool
->Shutdown();
12579 gConnectionPool
= nullptr;
12582 if (mMaintenanceThreadPool
) {
12583 mMaintenanceThreadPool
->Shutdown();
12584 mMaintenanceThreadPool
= nullptr;
12587 if (mDeleteTimer
) {
12588 MOZ_ALWAYS_SUCCEEDS(mDeleteTimer
->Cancel());
12589 mDeleteTimer
= nullptr;
12593 void QuotaClient::DeleteTimerCallback(nsITimer
* aTimer
, void* aClosure
) {
12594 AssertIsOnBackgroundThread();
12595 MOZ_ASSERT(aTimer
);
12597 auto* const self
= static_cast<QuotaClient
*>(aClosure
);
12599 MOZ_ASSERT(self
->mDeleteTimer
);
12600 MOZ_ASSERT(SameCOMIdentity(self
->mDeleteTimer
, aTimer
));
12602 for (const auto& pendingDeleteInfoEntry
: self
->mPendingDeleteInfos
) {
12603 const auto& key
= pendingDeleteInfoEntry
.GetKey();
12604 const auto& value
= pendingDeleteInfoEntry
.GetData();
12605 MOZ_ASSERT(!value
->IsEmpty());
12607 RefPtr
<DeleteFilesRunnable
> runnable
= new DeleteFilesRunnable(
12608 SafeRefPtr
{key
, AcquireStrongRefFromRawPtr
{}}, std::move(*value
));
12610 MOZ_ASSERT(value
->IsEmpty());
12612 runnable
->RunImmediately();
12615 self
->mPendingDeleteInfos
.Clear();
12618 Result
<nsCOMPtr
<nsIFile
>, nsresult
> QuotaClient::GetDirectory(
12619 PersistenceType aPersistenceType
, const nsACString
& aOrigin
) {
12620 QuotaManager
* const quotaManager
= QuotaManager::Get();
12621 NS_ASSERTION(quotaManager
, "This should never fail!");
12623 QM_TRY_INSPECT(const auto& directory
, quotaManager
->GetDirectoryForOrigin(
12624 aPersistenceType
, aOrigin
));
12626 MOZ_ASSERT(directory
);
12628 QM_TRY(MOZ_TO_RESULT(
12629 directory
->Append(NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME
))));
12634 template <QuotaClient::ObsoleteFilenamesHandling ObsoleteFilenames
>
12635 Result
<QuotaClient::GetDatabaseFilenamesResult
<ObsoleteFilenames
>, nsresult
>
12636 QuotaClient::GetDatabaseFilenames(nsIFile
& aDirectory
,
12637 const AtomicBool
& aCanceled
) {
12638 AssertIsOnIOThread();
12640 GetDatabaseFilenamesResult
<ObsoleteFilenames
> result
;
12642 QM_TRY(CollectEachFileAtomicCancelable(
12643 aDirectory
, aCanceled
,
12644 [&result
](const nsCOMPtr
<nsIFile
>& file
) -> Result
<Ok
, nsresult
> {
12645 QM_TRY_INSPECT(const auto& leafName
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
12646 nsString
, file
, GetLeafName
));
12648 QM_TRY_INSPECT(const auto& dirEntryKind
, GetDirEntryKind(*file
));
12650 switch (dirEntryKind
) {
12651 case nsIFileKind::ExistsAsDirectory
:
12652 result
.subdirsToProcess
.AppendElement(leafName
);
12655 case nsIFileKind::ExistsAsFile
: {
12656 if constexpr (ObsoleteFilenames
==
12657 ObsoleteFilenamesHandling::Include
) {
12658 if (StringBeginsWith(leafName
, kIdbDeletionMarkerFilePrefix
)) {
12659 result
.obsoleteFilenames
.Insert(
12660 Substring(leafName
, kIdbDeletionMarkerFilePrefix
.Length()));
12665 // Skip OS metadata files. These files are only used in different
12666 // platforms, but the profile can be shared across different
12667 // operating systems, so we check it on all platforms.
12668 if (QuotaManager::IsOSMetadata(leafName
)) {
12672 // Skip files starting with ".".
12673 if (QuotaManager::IsDotFile(leafName
)) {
12677 // Skip SQLite temporary files. These files take up space on disk
12678 // but will be deleted as soon as the database is opened, so we
12679 // don't count them towards quota.
12680 if (StringEndsWith(leafName
, kSQLiteJournalSuffix
) ||
12681 StringEndsWith(leafName
, kSQLiteSHMSuffix
)) {
12685 // The SQLite WAL file does count towards quota, but it is handled
12686 // below once we find the actual database file.
12687 if (StringEndsWith(leafName
, kSQLiteWALSuffix
)) {
12691 nsDependentSubstring leafNameBase
;
12692 if (!GetFilenameBase(leafName
, kSQLiteSuffix
, leafNameBase
)) {
12693 UNKNOWN_FILE_WARNING(leafName
);
12697 result
.databaseFilenames
.Insert(leafNameBase
);
12701 case nsIFileKind::DoesNotExist
:
12702 // Ignore files that got removed externally while iterating.
12712 void QuotaClient::ProcessMaintenanceQueue() {
12713 AssertIsOnBackgroundThread();
12715 if (mCurrentMaintenance
|| mMaintenanceQueue
.IsEmpty()) {
12719 mCurrentMaintenance
= mMaintenanceQueue
[0];
12720 mMaintenanceQueue
.RemoveElementAt(0);
12722 mCurrentMaintenance
->RunImmediately();
12725 /*******************************************************************************
12726 * DeleteFilesRunnable
12727 ******************************************************************************/
12729 DeleteFilesRunnable::DeleteFilesRunnable(
12730 SafeRefPtr
<DatabaseFileManager
> aFileManager
, nsTArray
<int64_t>&& aFileIds
)
12731 : Runnable("dom::indexeddb::DeleteFilesRunnable"),
12732 mOwningEventTarget(GetCurrentSerialEventTarget()),
12733 mFileManager(std::move(aFileManager
)),
12734 mFileIds(std::move(aFileIds
)),
12735 mState(State_Initial
) {}
12737 void DeleteFilesRunnable::RunImmediately() {
12738 AssertIsOnBackgroundThread();
12739 MOZ_ASSERT(mState
== State_Initial
);
12741 Unused
<< this->Run();
12744 void DeleteFilesRunnable::Open() {
12745 AssertIsOnBackgroundThread();
12746 MOZ_ASSERT(mState
== State_Initial
);
12748 QuotaManager
* const quotaManager
= QuotaManager::Get();
12749 if (NS_WARN_IF(!quotaManager
)) {
12754 RefPtr
<DirectoryLock
> directoryLock
= quotaManager
->CreateDirectoryLock(
12755 mFileManager
->Type(), mFileManager
->OriginMetadata(), quota::Client::IDB
,
12756 /* aExclusive */ false);
12758 mState
= State_DirectoryOpenPending
;
12759 directoryLock
->Acquire(this);
12762 void DeleteFilesRunnable::DoDatabaseWork() {
12763 AssertIsOnIOThread();
12764 MOZ_ASSERT(mState
== State_DatabaseWorkOpen
);
12766 if (!mFileManager
->Invalidated()) {
12767 for (int64_t fileId
: mFileIds
) {
12768 if (NS_FAILED(mFileManager
->SyncDeleteFile(fileId
))) {
12769 NS_WARNING("Failed to delete file!");
12777 void DeleteFilesRunnable::Finish() {
12778 MOZ_ASSERT(mState
!= State_UnblockingOpen
);
12780 // Must set mState before dispatching otherwise we will race with the main
12782 mState
= State_UnblockingOpen
;
12784 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
));
12787 void DeleteFilesRunnable::UnblockOpen() {
12788 AssertIsOnBackgroundThread();
12789 MOZ_ASSERT(mState
== State_UnblockingOpen
);
12791 mDirectoryLock
= nullptr;
12793 mState
= State_Completed
;
12796 NS_IMPL_ISUPPORTS_INHERITED0(DeleteFilesRunnable
, Runnable
)
12799 DeleteFilesRunnable::Run() {
12801 case State_Initial
:
12805 case State_DatabaseWorkOpen
:
12809 case State_UnblockingOpen
:
12813 case State_DirectoryOpenPending
:
12815 MOZ_CRASH("Should never get here!");
12821 void DeleteFilesRunnable::DirectoryLockAcquired(DirectoryLock
* aLock
) {
12822 AssertIsOnBackgroundThread();
12823 MOZ_ASSERT(mState
== State_DirectoryOpenPending
);
12824 MOZ_ASSERT(!mDirectoryLock
);
12826 mDirectoryLock
= aLock
;
12828 QuotaManager
* const quotaManager
= QuotaManager::Get();
12829 MOZ_ASSERT(quotaManager
);
12831 // Must set this before dispatching otherwise we will race with the IO thread
12832 mState
= State_DatabaseWorkOpen
;
12834 QM_TRY(MOZ_TO_RESULT(
12835 quotaManager
->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL
)),
12836 QM_VOID
, [this](const nsresult
) { Finish(); });
12839 void DeleteFilesRunnable::DirectoryLockFailed() {
12840 AssertIsOnBackgroundThread();
12841 MOZ_ASSERT(mState
== State_DirectoryOpenPending
);
12842 MOZ_ASSERT(!mDirectoryLock
);
12847 void Maintenance::RegisterDatabaseMaintenance(
12848 DatabaseMaintenance
* aDatabaseMaintenance
) {
12849 AssertIsOnBackgroundThread();
12850 MOZ_ASSERT(aDatabaseMaintenance
);
12851 MOZ_ASSERT(mState
== State::BeginDatabaseMaintenance
);
12853 !mDatabaseMaintenances
.Contains(aDatabaseMaintenance
->DatabasePath()));
12855 mDatabaseMaintenances
.InsertOrUpdate(aDatabaseMaintenance
->DatabasePath(),
12856 aDatabaseMaintenance
);
12859 void Maintenance::UnregisterDatabaseMaintenance(
12860 DatabaseMaintenance
* aDatabaseMaintenance
) {
12861 AssertIsOnBackgroundThread();
12862 MOZ_ASSERT(aDatabaseMaintenance
);
12863 MOZ_ASSERT(mState
== State::WaitingForDatabaseMaintenancesToComplete
);
12864 MOZ_ASSERT(mDatabaseMaintenances
.Get(aDatabaseMaintenance
->DatabasePath()));
12866 mDatabaseMaintenances
.Remove(aDatabaseMaintenance
->DatabasePath());
12868 if (mDatabaseMaintenances
.Count()) {
12872 mState
= State::Finishing
;
12876 void Maintenance::Stringify(nsACString
& aResult
) const {
12877 AssertIsOnBackgroundThread();
12879 aResult
.Append("DatabaseMaintenances: "_ns
+
12880 IntToCString(mDatabaseMaintenances
.Count()) + " ("_ns
);
12882 // XXX It might be confusing to remove duplicates here, as the actual list
12883 // won't match the count then.
12884 nsTHashSet
<nsCString
> ids
;
12885 std::transform(mDatabaseMaintenances
.Values().cbegin(),
12886 mDatabaseMaintenances
.Values().cend(), MakeInserter(ids
),
12887 [](const auto& entry
) {
12891 entry
->Stringify(id
);
12896 StringJoinAppend(aResult
, ", "_ns
, ids
);
12898 aResult
.Append(")");
12901 nsresult
Maintenance::Start() {
12902 AssertIsOnBackgroundThread();
12903 MOZ_ASSERT(mState
== State::Initial
);
12905 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
12907 return NS_ERROR_ABORT
;
12910 // Make sure that the IndexedDatabaseManager is running so that we can check
12911 // for low disk space mode.
12913 if (IndexedDatabaseManager::Get()) {
12918 mState
= State::CreateIndexedDatabaseManager
;
12919 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
12924 nsresult
Maintenance::CreateIndexedDatabaseManager() {
12925 MOZ_ASSERT(NS_IsMainThread());
12926 MOZ_ASSERT(mState
== State::CreateIndexedDatabaseManager
);
12928 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
12930 return NS_ERROR_ABORT
;
12933 IndexedDatabaseManager
* const mgr
= IndexedDatabaseManager::GetOrCreate();
12934 if (NS_WARN_IF(!mgr
)) {
12935 return NS_ERROR_FAILURE
;
12938 mState
= State::IndexedDatabaseManagerOpen
;
12939 MOZ_ALWAYS_SUCCEEDS(
12940 mQuotaClient
->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL
));
12945 nsresult
Maintenance::OpenDirectory() {
12946 AssertIsOnBackgroundThread();
12947 MOZ_ASSERT(mState
== State::Initial
||
12948 mState
== State::IndexedDatabaseManagerOpen
);
12949 MOZ_ASSERT(!mDirectoryLock
);
12950 MOZ_ASSERT(QuotaManager::Get());
12952 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
12954 return NS_ERROR_ABORT
;
12957 // Get a shared lock for <profile>/storage/*/*/idb
12959 mPendingDirectoryLock
= QuotaManager::Get()->CreateDirectoryLockInternal(
12960 Nullable
<PersistenceType
>(), OriginScope::FromNull(),
12961 Nullable
<Client::Type
>(Client::IDB
),
12962 /* aExclusive */ false);
12964 mState
= State::DirectoryOpenPending
;
12967 // Pin the directory lock, because Acquire might clear mPendingDirectoryLock
12968 // during the Acquire call.
12969 RefPtr pinnedDirectoryLock
= mPendingDirectoryLock
;
12970 pinnedDirectoryLock
->Acquire(this);
12976 nsresult
Maintenance::DirectoryOpen() {
12977 AssertIsOnBackgroundThread();
12978 MOZ_ASSERT(mState
== State::DirectoryOpenPending
);
12979 MOZ_ASSERT(mDirectoryLock
);
12981 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
12983 return NS_ERROR_ABORT
;
12986 QuotaManager
* const quotaManager
= QuotaManager::Get();
12987 MOZ_ASSERT(quotaManager
);
12989 mState
= State::DirectoryWorkOpen
;
12991 QM_TRY(MOZ_TO_RESULT(
12992 quotaManager
->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL
)),
12998 nsresult
Maintenance::DirectoryWork() {
12999 AssertIsOnIOThread();
13000 MOZ_ASSERT(mState
== State::DirectoryWorkOpen
);
13002 // The storage directory is structured like this:
13004 // <profile>/storage/<persistence>/<origin>/idb/*.sqlite
13006 // We have to find all database files that match any persistence type and any
13007 // origin. We ignore anything out of the ordinary for now.
13009 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
13011 return NS_ERROR_ABORT
;
13014 QuotaManager
* const quotaManager
= QuotaManager::Get();
13015 MOZ_ASSERT(quotaManager
);
13017 QM_TRY(MOZ_TO_RESULT(quotaManager
->EnsureStorageIsInitialized()));
13019 // Since idle maintenance may occur before temporary storage is initialized,
13020 // make sure it's initialized here (all non-persistent origins need to be
13021 // cleaned up and quota info needs to be loaded for them).
13023 // Don't fail whole idle maintenance in case of an error, the persistent
13024 // repository can still
13026 const bool initTemporaryStorageFailed
= ["aManager
] {
13027 QM_TRY(MOZ_TO_RESULT(quotaManager
->EnsureTemporaryStorageIsInitialized()),
13032 const nsCOMPtr
<nsIFile
> storageDir
=
13033 GetFileForPath(quotaManager
->GetStoragePath());
13034 QM_TRY(OkIf(storageDir
), NS_ERROR_FAILURE
);
13037 QM_TRY_INSPECT(const bool& exists
,
13038 MOZ_TO_RESULT_INVOKE_MEMBER(storageDir
, Exists
));
13040 // XXX No warning here?
13042 return NS_ERROR_NOT_AVAILABLE
;
13047 QM_TRY_INSPECT(const bool& isDirectory
,
13048 MOZ_TO_RESULT_INVOKE_MEMBER(storageDir
, IsDirectory
));
13050 QM_TRY(OkIf(isDirectory
), NS_ERROR_FAILURE
);
13053 // There are currently only 3 persistence types, and we want to iterate them
13055 static const PersistenceType kPersistenceTypes
[] = {
13056 PERSISTENCE_TYPE_PERSISTENT
, PERSISTENCE_TYPE_DEFAULT
,
13057 PERSISTENCE_TYPE_TEMPORARY
};
13060 ArrayLength(kPersistenceTypes
) == size_t(PERSISTENCE_TYPE_INVALID
),
13061 "Something changed with available persistence types!");
13063 constexpr auto idbDirName
=
13064 NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME
);
13066 for (const PersistenceType persistenceType
: kPersistenceTypes
) {
13067 // Loop over "<persistence>" directories.
13068 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
13070 return NS_ERROR_ABORT
;
13073 const bool persistent
= persistenceType
== PERSISTENCE_TYPE_PERSISTENT
;
13075 if (!persistent
&& initTemporaryStorageFailed
) {
13076 // Non-persistent (best effort) repositories can't be processed if
13077 // temporary storage initialization failed.
13081 // XXX persistenceType == PERSISTENCE_TYPE_PERSISTENT shouldn't be a special
13083 const auto persistenceTypeString
=
13084 persistenceType
== PERSISTENCE_TYPE_PERSISTENT
13086 : PersistenceTypeToString(persistenceType
);
13088 QM_TRY_INSPECT(const auto& persistenceDir
,
13089 CloneFileAndAppend(*storageDir
, NS_ConvertASCIItoUTF16(
13090 persistenceTypeString
)));
13093 QM_TRY_INSPECT(const bool& exists
,
13094 MOZ_TO_RESULT_INVOKE_MEMBER(persistenceDir
, Exists
));
13100 QM_TRY_INSPECT(const bool& isDirectory
,
13101 MOZ_TO_RESULT_INVOKE_MEMBER(persistenceDir
, IsDirectory
));
13103 if (NS_WARN_IF(!isDirectory
)) {
13108 // Loop over "<origin>/idb" directories.
13109 QM_TRY(CollectEachFile(
13111 [this, "aManager
, persistent
, persistenceType
, &idbDirName
](
13112 const nsCOMPtr
<nsIFile
>& originDir
) -> Result
<Ok
, nsresult
> {
13113 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
13115 return Err(NS_ERROR_ABORT
);
13118 QM_TRY_INSPECT(const auto& dirEntryKind
, GetDirEntryKind(*originDir
));
13120 switch (dirEntryKind
) {
13121 case nsIFileKind::ExistsAsFile
:
13124 case nsIFileKind::ExistsAsDirectory
: {
13125 // Get the necessary information about the origin
13126 // (LoadFullOriginMetadataWithRestore also checks if it's a valid
13130 const auto& metadata
,
13131 quotaManager
->LoadFullOriginMetadataWithRestore(originDir
),
13132 // Not much we can do here...
13135 // Don't do any maintenance for private browsing databases, which
13136 // are only temporary.
13137 if (OriginAttributes::IsPrivateBrowsing(metadata
.mOrigin
)) {
13142 // We have to check that all persistent origins are cleaned up,
13143 // but there's no way to do that by one call, we need to
13144 // initialize (and possibly clean up) them one by one
13145 // (EnsureTemporaryStorageIsInitialized cleans up only
13146 // non-persistent origins).
13149 const DebugOnly
<bool> created
,
13150 quotaManager
->EnsurePersistentOriginIsInitialized(metadata
)
13151 .map([](const auto& res
) { return res
.second
; }),
13152 // Not much we can do here...
13155 // We found this origin directory by traversing the repository,
13156 // so EnsurePersistentOriginIsInitialized shouldn't report that
13157 // a new directory has been created.
13158 MOZ_ASSERT(!created
);
13161 QM_TRY_INSPECT(const auto& idbDir
,
13162 CloneFileAndAppend(*originDir
, idbDirName
));
13164 QM_TRY_INSPECT(const bool& exists
,
13165 MOZ_TO_RESULT_INVOKE_MEMBER(idbDir
, Exists
));
13171 QM_TRY_INSPECT(const bool& isDirectory
,
13172 MOZ_TO_RESULT_INVOKE_MEMBER(idbDir
, IsDirectory
));
13174 QM_TRY(OkIf(isDirectory
), Ok
{});
13176 nsTArray
<nsString
> databasePaths
;
13178 // Loop over files in the "idb" directory.
13179 QM_TRY(CollectEachFile(
13181 [this, &databasePaths
](const nsCOMPtr
<nsIFile
>& idbDirFile
)
13182 -> Result
<Ok
, nsresult
> {
13183 if (NS_WARN_IF(QuotaClient::
13184 IsShuttingDownOnNonBackgroundThread()) ||
13186 return Err(NS_ERROR_ABORT
);
13189 QM_TRY_UNWRAP(auto idbFilePath
,
13190 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
13191 nsString
, idbDirFile
, GetPath
));
13193 if (!StringEndsWith(idbFilePath
, kSQLiteSuffix
)) {
13197 QM_TRY_INSPECT(const auto& dirEntryKind
,
13198 GetDirEntryKind(*idbDirFile
));
13200 switch (dirEntryKind
) {
13201 case nsIFileKind::ExistsAsDirectory
:
13204 case nsIFileKind::ExistsAsFile
:
13205 // Found a database.
13207 MOZ_ASSERT(!databasePaths
.Contains(idbFilePath
));
13209 databasePaths
.AppendElement(std::move(idbFilePath
));
13212 case nsIFileKind::DoesNotExist
:
13213 // Ignore files that got removed externally while
13221 if (!databasePaths
.IsEmpty()) {
13222 mDirectoryInfos
.EmplaceBack(persistenceType
, metadata
,
13223 std::move(databasePaths
));
13229 case nsIFileKind::DoesNotExist
:
13230 // Ignore files that got removed externally while iterating.
13238 mState
= State::BeginDatabaseMaintenance
;
13240 MOZ_ALWAYS_SUCCEEDS(
13241 mQuotaClient
->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL
));
13246 nsresult
Maintenance::BeginDatabaseMaintenance() {
13247 AssertIsOnBackgroundThread();
13248 MOZ_ASSERT(mState
== State::BeginDatabaseMaintenance
);
13250 class MOZ_STACK_CLASS Helper final
{
13252 static bool IsSafeToRunMaintenance(const nsAString
& aDatabasePath
) {
13254 for (uint32_t index
= gFactoryOps
->Length(); index
> 0; index
--) {
13255 CheckedUnsafePtr
<FactoryOp
>& existingOp
= (*gFactoryOps
)[index
- 1];
13257 if (!existingOp
->DatabaseFilePathIsKnown()) {
13261 if (existingOp
->DatabaseFilePath() == aDatabasePath
) {
13267 if (gLiveDatabaseHashtable
) {
13268 return std::all_of(
13269 gLiveDatabaseHashtable
->Values().cbegin(),
13270 gLiveDatabaseHashtable
->Values().cend(),
13271 [&aDatabasePath
](const auto& liveDatabasesEntry
) {
13272 const auto& liveDatabases
= liveDatabasesEntry
->mLiveDatabases
;
13273 return std::all_of(liveDatabases
.cbegin(), liveDatabases
.cend(),
13274 [&aDatabasePath
](const auto& database
) {
13275 return database
->FilePath() != aDatabasePath
;
13284 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
13286 return NS_ERROR_ABORT
;
13289 RefPtr
<nsThreadPool
> threadPool
;
13291 for (DirectoryInfo
& directoryInfo
: mDirectoryInfos
) {
13292 RefPtr
<DirectoryLock
> directoryLock
;
13294 for (const nsAString
& databasePath
: *directoryInfo
.mDatabasePaths
) {
13295 if (Helper::IsSafeToRunMaintenance(databasePath
)) {
13296 if (!directoryLock
) {
13297 directoryLock
= mDirectoryLock
->SpecializeForClient(
13298 directoryInfo
.mPersistenceType
,
13299 *directoryInfo
.mFullOriginMetadata
, Client::IDB
);
13300 MOZ_ASSERT(directoryLock
);
13303 // No key needs to be passed here, because we skip encrypted databases
13304 // in DoDirectoryWork as long as they are only used in private browsing
13306 const auto databaseMaintenance
= MakeRefPtr
<DatabaseMaintenance
>(
13307 this, directoryLock
, directoryInfo
.mPersistenceType
,
13308 *directoryInfo
.mFullOriginMetadata
, databasePath
, Nothing
{});
13311 threadPool
= mQuotaClient
->GetOrCreateThreadPool();
13312 MOZ_ASSERT(threadPool
);
13315 // Perform database maintenance on a TaskQueue, as database connections
13316 // require a serial event target when being opened in order to allow
13317 // memory pressure notifications to clear caches (bug 1806751).
13318 const auto taskQueue
= TaskQueue::Create(
13319 do_AddRef(threadPool
), "IndexedDB Database Maintenance");
13321 MOZ_ALWAYS_SUCCEEDS(
13322 taskQueue
->Dispatch(databaseMaintenance
, NS_DISPATCH_NORMAL
));
13324 RegisterDatabaseMaintenance(databaseMaintenance
);
13329 mDirectoryInfos
.Clear();
13331 mDirectoryLock
= nullptr;
13333 if (mDatabaseMaintenances
.Count()) {
13334 mState
= State::WaitingForDatabaseMaintenancesToComplete
;
13336 mState
= State::Finishing
;
13343 void Maintenance::Finish() {
13344 AssertIsOnBackgroundThread();
13345 MOZ_ASSERT(!mDirectoryLock
);
13346 MOZ_ASSERT(mState
== State::Finishing
);
13348 if (NS_FAILED(mResultCode
)) {
13349 nsCString errorName
;
13350 GetErrorName(mResultCode
, errorName
);
13352 IDB_WARNING("Maintenance finished with error: %s", errorName
.get());
13355 // It can happen that we are only referenced by mCurrentMaintenance which is
13356 // cleared in NoteFinishedMaintenance()
13357 const RefPtr
<Maintenance
> kungFuDeathGrip
= this;
13359 mQuotaClient
->NoteFinishedMaintenance(this);
13361 mState
= State::Complete
;
13364 NS_IMPL_ISUPPORTS_INHERITED0(Maintenance
, Runnable
)
13367 Maintenance::Run() {
13368 MOZ_ASSERT(mState
!= State::Complete
);
13370 const auto handleError
= [this](const nsresult rv
) {
13371 if (mState
!= State::Finishing
) {
13372 if (NS_SUCCEEDED(mResultCode
)) {
13376 // Must set mState before dispatching otherwise we will race with the
13378 mState
= State::Finishing
;
13380 if (IsOnBackgroundThread()) {
13383 MOZ_ALWAYS_SUCCEEDS(mQuotaClient
->BackgroundThread()->Dispatch(
13384 this, NS_DISPATCH_NORMAL
));
13390 case State::Initial
:
13391 QM_TRY(MOZ_TO_RESULT(Start()), NS_OK
, handleError
);
13394 case State::CreateIndexedDatabaseManager
:
13395 QM_TRY(MOZ_TO_RESULT(CreateIndexedDatabaseManager()), NS_OK
, handleError
);
13398 case State::IndexedDatabaseManagerOpen
:
13399 QM_TRY(MOZ_TO_RESULT(OpenDirectory()), NS_OK
, handleError
);
13402 case State::DirectoryWorkOpen
:
13403 QM_TRY(MOZ_TO_RESULT(DirectoryWork()), NS_OK
, handleError
);
13406 case State::BeginDatabaseMaintenance
:
13407 QM_TRY(MOZ_TO_RESULT(BeginDatabaseMaintenance()), NS_OK
, handleError
);
13410 case State::Finishing
:
13415 MOZ_CRASH("Bad state!");
13421 void Maintenance::DirectoryLockAcquired(DirectoryLock
* aLock
) {
13422 AssertIsOnBackgroundThread();
13423 MOZ_ASSERT(mState
== State::DirectoryOpenPending
);
13424 MOZ_ASSERT(!mDirectoryLock
);
13426 mDirectoryLock
= std::exchange(mPendingDirectoryLock
, nullptr);
13428 nsresult rv
= DirectoryOpen();
13429 if (NS_WARN_IF(NS_FAILED(rv
))) {
13430 if (NS_SUCCEEDED(mResultCode
)) {
13434 mState
= State::Finishing
;
13441 void Maintenance::DirectoryLockFailed() {
13442 AssertIsOnBackgroundThread();
13443 MOZ_ASSERT(mState
== State::DirectoryOpenPending
);
13444 MOZ_ASSERT(!mDirectoryLock
);
13446 mPendingDirectoryLock
= nullptr;
13448 if (NS_SUCCEEDED(mResultCode
)) {
13449 mResultCode
= NS_ERROR_FAILURE
;
13452 mState
= State::Finishing
;
13456 void DatabaseMaintenance::Stringify(nsACString
& aResult
) const {
13457 AssertIsOnBackgroundThread();
13459 aResult
.AppendLiteral("Origin:");
13460 aResult
.Append(AnonymizedOriginString(mOriginMetadata
.mOrigin
));
13461 aResult
.Append(kQuotaGenericDelimiter
);
13463 aResult
.AppendLiteral("PersistenceType:");
13464 aResult
.Append(PersistenceTypeToString(mPersistenceType
));
13465 aResult
.Append(kQuotaGenericDelimiter
);
13467 aResult
.AppendLiteral("Duration:");
13468 aResult
.AppendInt((PR_Now() - mMaintenance
->StartTime()) / PR_USEC_PER_MSEC
);
13471 void DatabaseMaintenance::PerformMaintenanceOnDatabase() {
13472 MOZ_ASSERT(!NS_IsMainThread());
13473 MOZ_ASSERT(!IsOnBackgroundThread());
13474 MOZ_ASSERT(mMaintenance
);
13475 MOZ_ASSERT(mMaintenance
->StartTime());
13476 MOZ_ASSERT(mDirectoryLock
);
13477 MOZ_ASSERT(!mDatabasePath
.IsEmpty());
13478 MOZ_ASSERT(!mOriginMetadata
.mGroup
.IsEmpty());
13479 MOZ_ASSERT(!mOriginMetadata
.mOrigin
.IsEmpty());
13481 class MOZ_STACK_CLASS AutoClose final
{
13482 NotNull
<nsCOMPtr
<mozIStorageConnection
>> mConnection
;
13485 explicit AutoClose(
13486 MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>> aConnection
)
13487 : mConnection(std::move(aConnection
)) {}
13489 ~AutoClose() { MOZ_ALWAYS_SUCCEEDS(mConnection
->Close()); }
13492 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
13493 mMaintenance
->IsAborted()) {
13497 const nsCOMPtr
<nsIFile
> databaseFile
= GetFileForPath(mDatabasePath
);
13498 MOZ_ASSERT(databaseFile
);
13501 const NotNull
<nsCOMPtr
<mozIStorageConnection
>> connection
,
13502 GetStorageConnection(*databaseFile
, mDirectoryLockId
,
13503 TelemetryIdForFile(databaseFile
), mMaybeKey
),
13506 AutoClose
autoClose(connection
);
13508 AutoProgressHandler
progressHandler(mMaintenance
);
13509 if (NS_WARN_IF(NS_FAILED(progressHandler
.Register(connection
)))) {
13514 nsresult rv
= CheckIntegrity(*connection
, &databaseIsOk
);
13515 if (NS_WARN_IF(NS_FAILED(rv
))) {
13519 if (NS_WARN_IF(!databaseIsOk
)) {
13520 // XXX Handle this somehow! Probably need to clear all storage for the
13521 // origin. Needs followup.
13522 MOZ_ASSERT(false, "Database corruption detected!");
13526 MaintenanceAction maintenanceAction
;
13528 DetermineMaintenanceAction(*connection
, databaseFile
, &maintenanceAction
);
13529 if (NS_WARN_IF(NS_FAILED(rv
))) {
13533 switch (maintenanceAction
) {
13534 case MaintenanceAction::Nothing
:
13537 case MaintenanceAction::IncrementalVacuum
:
13538 IncrementalVacuum(*connection
);
13541 case MaintenanceAction::FullVacuum
:
13542 FullVacuum(*connection
, databaseFile
);
13546 MOZ_CRASH("Unknown MaintenanceAction!");
13550 nsresult
DatabaseMaintenance::CheckIntegrity(mozIStorageConnection
& aConnection
,
13552 MOZ_ASSERT(!NS_IsMainThread());
13553 MOZ_ASSERT(!IsOnBackgroundThread());
13556 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
13557 mMaintenance
->IsAborted()) {
13558 return NS_ERROR_ABORT
;
13561 // First do a full integrity_check. Scope statements tightly here because
13562 // later operations require zero live statements.
13564 QM_TRY_INSPECT(const auto& stmt
,
13565 CreateAndExecuteSingleStepStatement(
13566 aConnection
, "PRAGMA integrity_check(1);"_ns
));
13568 QM_TRY_INSPECT(const auto& result
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
13569 nsString
, *stmt
, GetString
, 0));
13571 QM_TRY(OkIf(result
.EqualsLiteral("ok")), NS_OK
,
13572 [&aOk
](const auto) { *aOk
= false; });
13575 // Now enable and check for foreign key constraints.
13578 const int32_t& foreignKeysWereEnabled
,
13579 ([&aConnection
]() -> Result
<int32_t, nsresult
> {
13580 QM_TRY_INSPECT(const auto& stmt
,
13581 CreateAndExecuteSingleStepStatement(
13582 aConnection
, "PRAGMA foreign_keys;"_ns
));
13584 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt32
, 0));
13587 if (!foreignKeysWereEnabled
) {
13588 QM_TRY(MOZ_TO_RESULT(
13589 aConnection
.ExecuteSimpleSQL("PRAGMA foreign_keys = ON;"_ns
)));
13592 QM_TRY_INSPECT(const bool& foreignKeyError
,
13593 CreateAndExecuteSingleStepStatement
<
13594 SingleStepResult::ReturnNullIfNoResult
>(
13595 aConnection
, "PRAGMA foreign_key_check;"_ns
));
13597 if (!foreignKeysWereEnabled
) {
13598 QM_TRY(MOZ_TO_RESULT(
13599 aConnection
.ExecuteSimpleSQL("PRAGMA foreign_keys = OFF;"_ns
)));
13602 if (foreignKeyError
) {
13612 nsresult
DatabaseMaintenance::DetermineMaintenanceAction(
13613 mozIStorageConnection
& aConnection
, nsIFile
* aDatabaseFile
,
13614 MaintenanceAction
* aMaintenanceAction
) {
13615 MOZ_ASSERT(!NS_IsMainThread());
13616 MOZ_ASSERT(!IsOnBackgroundThread());
13617 MOZ_ASSERT(aDatabaseFile
);
13618 MOZ_ASSERT(aMaintenanceAction
);
13620 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
13621 mMaintenance
->IsAborted()) {
13622 return NS_ERROR_ABORT
;
13625 QM_TRY_INSPECT(const int32_t& schemaVersion
,
13626 MOZ_TO_RESULT_INVOKE_MEMBER(aConnection
, GetSchemaVersion
));
13628 // Don't do anything if the schema version is less than 18; before that
13629 // version no databases had |auto_vacuum == INCREMENTAL| set and we didn't
13630 // track the values needed for the heuristics below.
13631 if (schemaVersion
< MakeSchemaVersion(18, 0)) {
13632 *aMaintenanceAction
= MaintenanceAction::Nothing
;
13636 // This method shouldn't make any permanent changes to the database, so make
13637 // sure everything gets rolled back when we leave.
13638 mozStorageTransaction
transaction(&aConnection
,
13639 /* aCommitOnComplete */ false);
13641 QM_TRY(MOZ_TO_RESULT(transaction
.Start()))
13643 // Check to see when we last vacuumed this database.
13644 QM_TRY_INSPECT(const auto& stmt
,
13645 CreateAndExecuteSingleStepStatement(
13647 "SELECT last_vacuum_time, last_vacuum_size "
13648 "FROM database;"_ns
));
13650 QM_TRY_INSPECT(const PRTime
& lastVacuumTime
,
13651 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt64
, 0));
13653 QM_TRY_INSPECT(const int64_t& lastVacuumSize
,
13654 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt64
, 1));
13656 NS_ASSERTION(lastVacuumSize
> 0,
13657 "Thy last vacuum size shall be greater than zero, less than "
13658 "zero shall thy last vacuum size not be. Zero is right out.");
13660 const PRTime startTime
= mMaintenance
->StartTime();
13662 // This shouldn't really be possible...
13663 if (NS_WARN_IF(startTime
<= lastVacuumTime
)) {
13664 *aMaintenanceAction
= MaintenanceAction::Nothing
;
13668 if (startTime
- lastVacuumTime
< kMinVacuumAge
) {
13669 *aMaintenanceAction
= MaintenanceAction::IncrementalVacuum
;
13673 // It has been more than a week since the database was vacuumed, so gather
13674 // statistics on its usage to see if vacuuming is worthwhile.
13676 // Create a temporary copy of the dbstat table to speed up the queries that
13678 QM_TRY(MOZ_TO_RESULT(aConnection
.ExecuteSimpleSQL(
13679 "CREATE VIRTUAL TABLE __stats__ USING dbstat;"
13680 "CREATE TEMP TABLE __temp_stats__ AS SELECT * FROM __stats__;"_ns
)));
13682 { // Calculate the percentage of the database pages that are not in
13683 // contiguous order.
13686 CreateAndExecuteSingleStepStatement(
13688 "SELECT SUM(__ts1__.pageno != __ts2__.pageno + 1) * 100.0 / "
13690 "FROM __temp_stats__ AS __ts1__, __temp_stats__ AS __ts2__ "
13691 "WHERE __ts1__.name = __ts2__.name "
13692 "AND __ts1__.rowid = __ts2__.rowid + 1;"_ns
));
13694 QM_TRY_INSPECT(const int32_t& percentUnordered
,
13695 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt32
, 0));
13697 MOZ_ASSERT(percentUnordered
>= 0);
13698 MOZ_ASSERT(percentUnordered
<= 100);
13700 if (percentUnordered
>= kPercentUnorderedThreshold
) {
13701 *aMaintenanceAction
= MaintenanceAction::FullVacuum
;
13706 // Don't try a full vacuum if the file hasn't grown by 10%.
13707 QM_TRY_INSPECT(const int64_t& currentFileSize
,
13708 MOZ_TO_RESULT_INVOKE_MEMBER(aDatabaseFile
, GetFileSize
));
13710 if (currentFileSize
<= lastVacuumSize
||
13711 (((currentFileSize
- lastVacuumSize
) * 100 / currentFileSize
) <
13712 kPercentFileSizeGrowthThreshold
)) {
13713 *aMaintenanceAction
= MaintenanceAction::IncrementalVacuum
;
13717 { // See if there are any free pages that we can reclaim.
13718 QM_TRY_INSPECT(const auto& stmt
,
13719 CreateAndExecuteSingleStepStatement(
13720 aConnection
, "PRAGMA freelist_count;"_ns
));
13722 QM_TRY_INSPECT(const int32_t& freelistCount
,
13723 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt32
, 0));
13725 MOZ_ASSERT(freelistCount
>= 0);
13727 // If we have too many free pages then we should try an incremental
13728 // vacuum. If that causes too much fragmentation then we'll try a full
13730 if (freelistCount
> kMaxFreelistThreshold
) {
13731 *aMaintenanceAction
= MaintenanceAction::IncrementalVacuum
;
13736 { // Calculate the percentage of unused bytes on pages in the database.
13739 CreateAndExecuteSingleStepStatement(
13741 "SELECT SUM(unused) * 100.0 / SUM(pgsize) FROM __temp_stats__;"_ns
));
13743 QM_TRY_INSPECT(const int32_t& percentUnused
,
13744 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt32
, 0));
13746 MOZ_ASSERT(percentUnused
>= 0);
13747 MOZ_ASSERT(percentUnused
<= 100);
13749 *aMaintenanceAction
= percentUnused
>= kPercentUnusedThreshold
13750 ? MaintenanceAction::FullVacuum
13751 : MaintenanceAction::IncrementalVacuum
;
13757 void DatabaseMaintenance::IncrementalVacuum(
13758 mozIStorageConnection
& aConnection
) {
13759 MOZ_ASSERT(!NS_IsMainThread());
13760 MOZ_ASSERT(!IsOnBackgroundThread());
13762 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
13763 mMaintenance
->IsAborted()) {
13767 nsresult rv
= aConnection
.ExecuteSimpleSQL("PRAGMA incremental_vacuum;"_ns
);
13768 if (NS_WARN_IF(NS_FAILED(rv
))) {
13773 void DatabaseMaintenance::FullVacuum(mozIStorageConnection
& aConnection
,
13774 nsIFile
* aDatabaseFile
) {
13775 MOZ_ASSERT(!NS_IsMainThread());
13776 MOZ_ASSERT(!IsOnBackgroundThread());
13777 MOZ_ASSERT(aDatabaseFile
);
13779 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
13780 mMaintenance
->IsAborted()) {
13784 QM_WARNONLY_TRY(([&]() -> Result
<Ok
, nsresult
> {
13785 QM_TRY(MOZ_TO_RESULT(aConnection
.ExecuteSimpleSQL("VACUUM;"_ns
)));
13787 const PRTime vacuumTime
= PR_Now();
13788 MOZ_ASSERT(vacuumTime
> 0);
13790 QM_TRY_INSPECT(const int64_t& fileSize
,
13791 MOZ_TO_RESULT_INVOKE_MEMBER(aDatabaseFile
, GetFileSize
));
13793 MOZ_ASSERT(fileSize
> 0);
13795 // The parameter names are not used, parameters are bound by index only
13796 // locally in the same function.
13797 QM_TRY_INSPECT(const auto& stmt
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
13798 nsCOMPtr
<mozIStorageStatement
>,
13799 aConnection
, CreateStatement
,
13801 "SET last_vacuum_time = :time"
13802 ", last_vacuum_size = :size;"_ns
));
13804 QM_TRY(MOZ_TO_RESULT(stmt
->BindInt64ByIndex(0, vacuumTime
)));
13806 QM_TRY(MOZ_TO_RESULT(stmt
->BindInt64ByIndex(1, fileSize
)));
13808 QM_TRY(MOZ_TO_RESULT(stmt
->Execute()));
13813 void DatabaseMaintenance::RunOnOwningThread() {
13814 AssertIsOnBackgroundThread();
13816 mDirectoryLock
= nullptr;
13818 if (mCompleteCallback
) {
13819 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mCompleteCallback
.forget()));
13822 mMaintenance
->UnregisterDatabaseMaintenance(this);
13825 void DatabaseMaintenance::RunOnConnectionThread() {
13826 MOZ_ASSERT(!NS_IsMainThread());
13827 MOZ_ASSERT(!IsOnBackgroundThread());
13829 PerformMaintenanceOnDatabase();
13831 MOZ_ALWAYS_SUCCEEDS(
13832 mMaintenance
->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL
));
13836 DatabaseMaintenance::Run() {
13837 if (IsOnBackgroundThread()) {
13838 RunOnOwningThread();
13840 RunOnConnectionThread();
13846 nsresult
DatabaseMaintenance::AutoProgressHandler::Register(
13847 NotNull
<mozIStorageConnection
*> aConnection
) {
13848 MOZ_ASSERT(!NS_IsMainThread());
13849 MOZ_ASSERT(!IsOnBackgroundThread());
13851 // We want to quickly bail out of any operation if the user becomes active, so
13852 // use a small granularity here since database performance isn't critical.
13853 static const int32_t kProgressGranularity
= 50;
13855 nsCOMPtr
<mozIStorageProgressHandler
> oldHandler
;
13856 nsresult rv
= aConnection
->SetProgressHandler(kProgressGranularity
, this,
13857 getter_AddRefs(oldHandler
));
13858 if (NS_WARN_IF(NS_FAILED(rv
))) {
13862 MOZ_ASSERT(!oldHandler
);
13863 mConnection
.init(aConnection
);
13868 void DatabaseMaintenance::AutoProgressHandler::Unregister() {
13869 MOZ_ASSERT(!NS_IsMainThread());
13870 MOZ_ASSERT(!IsOnBackgroundThread());
13871 MOZ_ASSERT(mConnection
);
13873 nsCOMPtr
<mozIStorageProgressHandler
> oldHandler
;
13875 mConnection
->get()->RemoveProgressHandler(getter_AddRefs(oldHandler
));
13876 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
13878 MOZ_ASSERT_IF(NS_SUCCEEDED(rv
), oldHandler
== this);
13881 NS_IMETHODIMP_(MozExternalRefCountType
)
13882 DatabaseMaintenance::AutoProgressHandler::AddRef() {
13883 NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler
);
13891 NS_IMETHODIMP_(MozExternalRefCountType
)
13892 DatabaseMaintenance::AutoProgressHandler::Release() {
13893 NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler
);
13901 NS_IMPL_QUERY_INTERFACE(DatabaseMaintenance::AutoProgressHandler
,
13902 mozIStorageProgressHandler
)
13905 DatabaseMaintenance::AutoProgressHandler::OnProgress(
13906 mozIStorageConnection
* aConnection
, bool* _retval
) {
13907 NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler
);
13908 MOZ_ASSERT(*mConnection
== aConnection
);
13909 MOZ_ASSERT(_retval
);
13911 *_retval
= QuotaClient::IsShuttingDownOnNonBackgroundThread() ||
13912 mMaintenance
->IsAborted();
13917 /*******************************************************************************
13918 * Local class implementations
13919 ******************************************************************************/
13922 nsAutoCString
DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
13923 const Maybe
<SerializedKeyRange
>& aOptionalKeyRange
,
13924 const nsACString
& aKeyColumnName
) {
13925 return aOptionalKeyRange
.isSome()
13926 ? GetBindingClauseForKeyRange(aOptionalKeyRange
.ref(),
13932 nsAutoCString
DatabaseOperationBase::GetBindingClauseForKeyRange(
13933 const SerializedKeyRange
& aKeyRange
, const nsACString
& aKeyColumnName
) {
13934 MOZ_ASSERT(!IsOnBackgroundThread());
13935 MOZ_ASSERT(!aKeyColumnName
.IsEmpty());
13937 constexpr auto andStr
= " AND "_ns
;
13938 constexpr auto spacecolon
= " :"_ns
;
13940 nsAutoCString result
;
13941 if (aKeyRange
.isOnly()) {
13942 // Both keys equal.
13944 andStr
+ aKeyColumnName
+ " ="_ns
+ spacecolon
+ kStmtParamNameLowerKey
;
13946 if (!aKeyRange
.lower().IsUnset()) {
13947 // Lower key is set.
13948 result
.Append(andStr
+ aKeyColumnName
);
13949 result
.AppendLiteral(" >");
13950 if (!aKeyRange
.lowerOpen()) {
13951 result
.AppendLiteral("=");
13953 result
.Append(spacecolon
+ kStmtParamNameLowerKey
);
13956 if (!aKeyRange
.upper().IsUnset()) {
13957 // Upper key is set.
13958 result
.Append(andStr
+ aKeyColumnName
);
13959 result
.AppendLiteral(" <");
13960 if (!aKeyRange
.upperOpen()) {
13961 result
.AppendLiteral("=");
13963 result
.Append(spacecolon
+ kStmtParamNameUpperKey
);
13967 MOZ_ASSERT(!result
.IsEmpty());
13973 uint64_t DatabaseOperationBase::ReinterpretDoubleAsUInt64(double aDouble
) {
13974 // This is a duplicate of the js engine's byte munging in StructuredClone.cpp
13975 return BitwiseCast
<uint64_t>(aDouble
);
13979 template <typename KeyTransformation
>
13980 nsresult
DatabaseOperationBase::MaybeBindKeyToStatement(
13981 const Key
& aKey
, mozIStorageStatement
* const aStatement
,
13982 const nsACString
& aParameterName
,
13983 const KeyTransformation
& aKeyTransformation
) {
13984 MOZ_ASSERT(!IsOnBackgroundThread());
13985 MOZ_ASSERT(aStatement
);
13987 if (!aKey
.IsUnset()) {
13988 // XXX This case distinction could be avoided if QM_TRY_INSPECT would also
13989 // work with a function not returning a Result<V, E> but simply a V (which
13990 // is const Key& here) and then assuming it is always a success. Or the
13991 // transformation could be changed to return Result<const V&, void> but I
13992 // don't think that Result supports that at the moment.
13993 if constexpr (std::is_reference_v
<
13994 std::invoke_result_t
<KeyTransformation
, Key
>>) {
13995 QM_TRY(MOZ_TO_RESULT(aKeyTransformation(aKey
).BindToStatement(
13996 aStatement
, aParameterName
)));
13998 QM_TRY_INSPECT(const auto& transformedKey
, aKeyTransformation(aKey
));
13999 QM_TRY(MOZ_TO_RESULT(
14000 transformedKey
.BindToStatement(aStatement
, aParameterName
)));
14008 template <typename KeyTransformation
>
14009 nsresult
DatabaseOperationBase::BindTransformedKeyRangeToStatement(
14010 const SerializedKeyRange
& aKeyRange
, mozIStorageStatement
* const aStatement
,
14011 const KeyTransformation
& aKeyTransformation
) {
14012 MOZ_ASSERT(!IsOnBackgroundThread());
14013 MOZ_ASSERT(aStatement
);
14015 QM_TRY(MOZ_TO_RESULT(MaybeBindKeyToStatement(aKeyRange
.lower(), aStatement
,
14016 kStmtParamNameLowerKey
,
14017 aKeyTransformation
)));
14019 if (aKeyRange
.isOnly()) {
14023 QM_TRY(MOZ_TO_RESULT(MaybeBindKeyToStatement(aKeyRange
.upper(), aStatement
,
14024 kStmtParamNameUpperKey
,
14025 aKeyTransformation
)));
14031 nsresult
DatabaseOperationBase::BindKeyRangeToStatement(
14032 const SerializedKeyRange
& aKeyRange
,
14033 mozIStorageStatement
* const aStatement
) {
14034 return BindTransformedKeyRangeToStatement(
14035 aKeyRange
, aStatement
, [](const Key
& key
) -> const auto& { return key
; });
14039 nsresult
DatabaseOperationBase::BindKeyRangeToStatement(
14040 const SerializedKeyRange
& aKeyRange
, mozIStorageStatement
* const aStatement
,
14041 const nsCString
& aLocale
) {
14042 MOZ_ASSERT(!aLocale
.IsEmpty());
14044 return BindTransformedKeyRangeToStatement(
14045 aKeyRange
, aStatement
,
14046 [&aLocale
](const Key
& key
) { return key
.ToLocaleAwareKey(aLocale
); });
14050 void CommonOpenOpHelperBase::AppendConditionClause(
14051 const nsACString
& aColumnName
, const nsACString
& aStatementParameterName
,
14052 bool aLessThan
, bool aEquals
, nsCString
& aResult
) {
14053 aResult
+= " AND "_ns
+ aColumnName
+ " "_ns
;
14056 aResult
.Append('<');
14058 aResult
.Append('>');
14062 aResult
.Append('=');
14065 aResult
+= " :"_ns
+ aStatementParameterName
;
14069 Result
<IndexDataValuesAutoArray
, nsresult
>
14070 DatabaseOperationBase::IndexDataValuesFromUpdateInfos(
14071 const nsTArray
<IndexUpdateInfo
>& aUpdateInfos
,
14072 const UniqueIndexTable
& aUniqueIndexTable
) {
14073 MOZ_ASSERT_IF(!aUpdateInfos
.IsEmpty(), aUniqueIndexTable
.Count());
14075 AUTO_PROFILER_LABEL("DatabaseOperationBase::IndexDataValuesFromUpdateInfos",
14078 // XXX We could use TransformIntoNewArray here if it allowed to specify that
14079 // an AutoArray should be created.
14080 IndexDataValuesAutoArray indexValues
;
14082 if (NS_WARN_IF(!indexValues
.SetCapacity(aUpdateInfos
.Length(), fallible
))) {
14083 IDB_REPORT_INTERNAL_ERR();
14084 return Err(NS_ERROR_OUT_OF_MEMORY
);
14087 std::transform(aUpdateInfos
.cbegin(), aUpdateInfos
.cend(),
14088 MakeBackInserter(indexValues
),
14089 [&aUniqueIndexTable
](const IndexUpdateInfo
& updateInfo
) {
14090 const IndexOrObjectStoreId
& indexId
= updateInfo
.indexId();
14092 bool unique
= false;
14093 MOZ_ALWAYS_TRUE(aUniqueIndexTable
.Get(indexId
, &unique
));
14095 return IndexDataValue
{indexId
, unique
, updateInfo
.value(),
14096 updateInfo
.localizedValue()};
14098 indexValues
.Sort();
14100 return indexValues
;
14104 nsresult
DatabaseOperationBase::InsertIndexTableRows(
14105 DatabaseConnection
* aConnection
, const IndexOrObjectStoreId aObjectStoreId
,
14106 const Key
& aObjectStoreKey
, const nsTArray
<IndexDataValue
>& aIndexValues
) {
14107 MOZ_ASSERT(aConnection
);
14108 aConnection
->AssertIsOnConnectionThread();
14109 MOZ_ASSERT(!aObjectStoreKey
.IsUnset());
14111 AUTO_PROFILER_LABEL("DatabaseOperationBase::InsertIndexTableRows", DOM
);
14113 const uint32_t count
= aIndexValues
.Length();
14118 auto insertUniqueStmt
= DatabaseConnection::LazyStatement
{
14120 "INSERT INTO unique_index_data "
14121 "(index_id, value, object_store_id, "
14122 "object_data_key, value_locale) "
14124 kStmtParamNameIndexId
+ ", :"_ns
+ kStmtParamNameValue
+ ", :"_ns
+
14125 kStmtParamNameObjectStoreId
+ ", :"_ns
+ kStmtParamNameObjectDataKey
+
14126 ", :"_ns
+ kStmtParamNameValueLocale
+ ");"_ns
};
14127 auto insertStmt
= DatabaseConnection::LazyStatement
{
14129 "INSERT OR IGNORE INTO index_data "
14130 "(index_id, value, object_data_key, "
14131 "object_store_id, value_locale) "
14133 kStmtParamNameIndexId
+ ", :"_ns
+ kStmtParamNameValue
+ ", :"_ns
+
14134 kStmtParamNameObjectDataKey
+ ", :"_ns
+ kStmtParamNameObjectStoreId
+
14135 ", :"_ns
+ kStmtParamNameValueLocale
+ ");"_ns
};
14137 for (uint32_t index
= 0; index
< count
; index
++) {
14138 const IndexDataValue
& info
= aIndexValues
[index
];
14140 auto& stmt
= info
.mUnique
? insertUniqueStmt
: insertStmt
;
14142 QM_TRY_INSPECT(const auto& borrowedStmt
, stmt
.Borrow());
14144 QM_TRY(MOZ_TO_RESULT(
14145 borrowedStmt
->BindInt64ByName(kStmtParamNameIndexId
, info
.mIndexId
)));
14146 QM_TRY(MOZ_TO_RESULT(
14147 info
.mPosition
.BindToStatement(&*borrowedStmt
, kStmtParamNameValue
)));
14148 QM_TRY(MOZ_TO_RESULT(info
.mLocaleAwarePosition
.BindToStatement(
14149 &*borrowedStmt
, kStmtParamNameValueLocale
)));
14150 QM_TRY(MOZ_TO_RESULT(borrowedStmt
->BindInt64ByName(
14151 kStmtParamNameObjectStoreId
, aObjectStoreId
)));
14152 QM_TRY(MOZ_TO_RESULT(aObjectStoreKey
.BindToStatement(
14153 &*borrowedStmt
, kStmtParamNameObjectDataKey
)));
14155 // QM_OR_ELSE_WARN_IF is not used here since we just want to log the
14156 // collision and not spam the reports.
14157 QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF(
14159 MOZ_TO_RESULT(borrowedStmt
->Execute()),
14161 ([&info
, index
, &aIndexValues
](nsresult rv
) {
14162 if (rv
== NS_ERROR_STORAGE_CONSTRAINT
&& info
.mUnique
) {
14163 // If we're inserting multiple entries for the same unique
14164 // index, then we might have failed to insert due to
14165 // colliding with another entry for the same index in which
14166 // case we should ignore it.
14167 for (int32_t index2
= int32_t(index
) - 1;
14168 index2
>= 0 && aIndexValues
[index2
].mIndexId
== info
.mIndexId
;
14170 if (info
.mPosition
== aIndexValues
[index2
].mPosition
) {
14171 // We found a key with the same value for the same
14172 // index. So we must have had a collision with a value
14173 // we just inserted.
14182 ErrToDefaultOk
<>));
14189 nsresult
DatabaseOperationBase::DeleteIndexDataTableRows(
14190 DatabaseConnection
* aConnection
, const Key
& aObjectStoreKey
,
14191 const nsTArray
<IndexDataValue
>& aIndexValues
) {
14192 MOZ_ASSERT(aConnection
);
14193 aConnection
->AssertIsOnConnectionThread();
14194 MOZ_ASSERT(!aObjectStoreKey
.IsUnset());
14196 AUTO_PROFILER_LABEL("DatabaseOperationBase::DeleteIndexDataTableRows", DOM
);
14198 const uint32_t count
= aIndexValues
.Length();
14203 auto deleteUniqueStmt
= DatabaseConnection::LazyStatement
{
14204 *aConnection
, "DELETE FROM unique_index_data WHERE index_id = :"_ns
+
14205 kStmtParamNameIndexId
+ " AND value = :"_ns
+
14206 kStmtParamNameValue
+ ";"_ns
};
14207 auto deleteStmt
= DatabaseConnection::LazyStatement
{
14208 *aConnection
, "DELETE FROM index_data WHERE index_id = :"_ns
+
14209 kStmtParamNameIndexId
+ " AND value = :"_ns
+
14210 kStmtParamNameValue
+ " AND object_data_key = :"_ns
+
14211 kStmtParamNameObjectDataKey
+ ";"_ns
};
14213 for (uint32_t index
= 0; index
< count
; index
++) {
14214 const IndexDataValue
& indexValue
= aIndexValues
[index
];
14216 auto& stmt
= indexValue
.mUnique
? deleteUniqueStmt
: deleteStmt
;
14218 QM_TRY_INSPECT(const auto& borrowedStmt
, stmt
.Borrow());
14220 QM_TRY(MOZ_TO_RESULT(borrowedStmt
->BindInt64ByName(kStmtParamNameIndexId
,
14221 indexValue
.mIndexId
)));
14223 QM_TRY(MOZ_TO_RESULT(indexValue
.mPosition
.BindToStatement(
14224 &*borrowedStmt
, kStmtParamNameValue
)));
14226 if (!indexValue
.mUnique
) {
14227 QM_TRY(MOZ_TO_RESULT(aObjectStoreKey
.BindToStatement(
14228 &*borrowedStmt
, kStmtParamNameObjectDataKey
)));
14231 QM_TRY(MOZ_TO_RESULT(borrowedStmt
->Execute()));
14238 nsresult
DatabaseOperationBase::DeleteObjectStoreDataTableRowsWithIndexes(
14239 DatabaseConnection
* aConnection
, const IndexOrObjectStoreId aObjectStoreId
,
14240 const Maybe
<SerializedKeyRange
>& aKeyRange
) {
14241 MOZ_ASSERT(aConnection
);
14242 aConnection
->AssertIsOnConnectionThread();
14243 MOZ_ASSERT(aObjectStoreId
);
14247 QM_TRY_INSPECT(const bool& hasIndexes
,
14248 ObjectStoreHasIndexes(*aConnection
, aObjectStoreId
),
14249 QM_PROPAGATE
, [](const auto&) { MOZ_ASSERT(false); });
14250 MOZ_ASSERT(hasIndexes
,
14251 "Don't use this slow method if there are no indexes!");
14255 AUTO_PROFILER_LABEL(
14256 "DatabaseOperationBase::DeleteObjectStoreDataTableRowsWithIndexes", DOM
);
14258 const bool singleRowOnly
= aKeyRange
.isSome() && aKeyRange
.ref().isOnly();
14260 Key objectStoreKey
;
14262 const auto& selectStmt
,
14263 ([singleRowOnly
, &aConnection
, &objectStoreKey
, &aKeyRange
]()
14264 -> Result
<CachingDatabaseConnection::BorrowedStatement
, nsresult
> {
14265 if (singleRowOnly
) {
14266 QM_TRY_UNWRAP(auto selectStmt
,
14267 aConnection
->BorrowCachedStatement(
14268 "SELECT index_data_values "
14269 "FROM object_data "
14270 "WHERE object_store_id = :"_ns
+
14271 kStmtParamNameObjectStoreId
+ " AND key = :"_ns
+
14272 kStmtParamNameKey
+ ";"_ns
));
14274 objectStoreKey
= aKeyRange
.ref().lower();
14276 QM_TRY(MOZ_TO_RESULT(
14277 objectStoreKey
.BindToStatement(&*selectStmt
, kStmtParamNameKey
)));
14282 const auto keyRangeClause
=
14283 MaybeGetBindingClauseForKeyRange(aKeyRange
, kColumnNameKey
);
14287 aConnection
->BorrowCachedStatement(
14288 "SELECT index_data_values, "_ns
+ kColumnNameKey
+
14289 " FROM object_data WHERE object_store_id = :"_ns
+
14290 kStmtParamNameObjectStoreId
+ keyRangeClause
+ ";"_ns
));
14292 if (aKeyRange
.isSome()) {
14293 QM_TRY(MOZ_TO_RESULT(
14294 BindKeyRangeToStatement(aKeyRange
.ref(), &*selectStmt
)));
14300 QM_TRY(MOZ_TO_RESULT(selectStmt
->BindInt64ByName(kStmtParamNameObjectStoreId
,
14303 DebugOnly
<uint32_t> resultCountDEBUG
= 0;
14305 QM_TRY(CollectWhileHasResult(
14307 [singleRowOnly
, aObjectStoreId
, &objectStoreKey
, &aConnection
,
14308 &resultCountDEBUG
, indexValues
= IndexDataValuesAutoArray
{},
14309 deleteStmt
= DatabaseConnection::LazyStatement
{
14311 "DELETE FROM object_data "
14312 "WHERE object_store_id = :"_ns
+
14313 kStmtParamNameObjectStoreId
+ " AND key = :"_ns
+
14314 kStmtParamNameKey
+
14315 ";"_ns
}](auto& selectStmt
) mutable -> Result
<Ok
, nsresult
> {
14316 if (!singleRowOnly
) {
14318 MOZ_TO_RESULT(objectStoreKey
.SetFromStatement(&selectStmt
, 1)));
14320 indexValues
.ClearAndRetainStorage();
14323 QM_TRY(MOZ_TO_RESULT(
14324 ReadCompressedIndexDataValues(selectStmt
, 0, indexValues
)));
14325 QM_TRY(MOZ_TO_RESULT(DeleteIndexDataTableRows(
14326 aConnection
, objectStoreKey
, indexValues
)));
14328 QM_TRY_INSPECT(const auto& borrowedDeleteStmt
, deleteStmt
.Borrow());
14330 QM_TRY(MOZ_TO_RESULT(borrowedDeleteStmt
->BindInt64ByName(
14331 kStmtParamNameObjectStoreId
, aObjectStoreId
)));
14332 QM_TRY(MOZ_TO_RESULT(objectStoreKey
.BindToStatement(
14333 &*borrowedDeleteStmt
, kStmtParamNameKey
)));
14334 QM_TRY(MOZ_TO_RESULT(borrowedDeleteStmt
->Execute()));
14336 resultCountDEBUG
++;
14341 MOZ_ASSERT_IF(singleRowOnly
, resultCountDEBUG
<= 1);
14347 nsresult
DatabaseOperationBase::UpdateIndexValues(
14348 DatabaseConnection
* aConnection
, const IndexOrObjectStoreId aObjectStoreId
,
14349 const Key
& aObjectStoreKey
, const nsTArray
<IndexDataValue
>& aIndexValues
) {
14350 MOZ_ASSERT(aConnection
);
14351 aConnection
->AssertIsOnConnectionThread();
14352 MOZ_ASSERT(!aObjectStoreKey
.IsUnset());
14354 AUTO_PROFILER_LABEL("DatabaseOperationBase::UpdateIndexValues", DOM
);
14356 QM_TRY_UNWRAP((auto [indexDataValues
, indexDataValuesLength
]),
14357 MakeCompressedIndexDataValues(aIndexValues
));
14359 MOZ_ASSERT(!indexDataValuesLength
== !(indexDataValues
.get()));
14361 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
14362 "UPDATE object_data SET index_data_values = :"_ns
+
14363 kStmtParamNameIndexDataValues
+ " WHERE object_store_id = :"_ns
+
14364 kStmtParamNameObjectStoreId
+ " AND key = :"_ns
+ kStmtParamNameKey
+
14366 [&indexDataValues
= indexDataValues
,
14367 indexDataValuesLength
= indexDataValuesLength
, aObjectStoreId
,
14369 mozIStorageStatement
& updateStmt
) -> Result
<Ok
, nsresult
> {
14370 QM_TRY(MOZ_TO_RESULT(
14372 ? updateStmt
.BindAdoptedBlobByName(
14373 kStmtParamNameIndexDataValues
, indexDataValues
.release(),
14374 indexDataValuesLength
)
14375 : updateStmt
.BindNullByName(kStmtParamNameIndexDataValues
)));
14377 QM_TRY(MOZ_TO_RESULT(updateStmt
.BindInt64ByName(
14378 kStmtParamNameObjectStoreId
, aObjectStoreId
)));
14380 QM_TRY(MOZ_TO_RESULT(
14381 aObjectStoreKey
.BindToStatement(&updateStmt
, kStmtParamNameKey
)));
14390 Result
<bool, nsresult
> DatabaseOperationBase::ObjectStoreHasIndexes(
14391 DatabaseConnection
& aConnection
,
14392 const IndexOrObjectStoreId aObjectStoreId
) {
14393 aConnection
.AssertIsOnConnectionThread();
14394 MOZ_ASSERT(aObjectStoreId
);
14396 QM_TRY_RETURN(aConnection
14397 .BorrowAndExecuteSingleStepStatement(
14399 "FROM object_store_index "
14400 "WHERE object_store_id = :"_ns
+
14401 kStmtParamNameObjectStoreId
+ kOpenLimit
+ "1;"_ns
,
14402 [aObjectStoreId
](auto& stmt
) -> Result
<Ok
, nsresult
> {
14403 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByName(
14404 kStmtParamNameObjectStoreId
, aObjectStoreId
)));
14410 NS_IMPL_ISUPPORTS_INHERITED(DatabaseOperationBase
, Runnable
,
14411 mozIStorageProgressHandler
)
14414 DatabaseOperationBase::OnProgress(mozIStorageConnection
* aConnection
,
14416 MOZ_ASSERT(!IsOnBackgroundThread());
14417 MOZ_ASSERT(_retval
);
14419 // This is intentionally racy.
14420 *_retval
= QuotaClient::IsShuttingDownOnNonBackgroundThread() ||
14421 !OperationMayProceed();
14425 DatabaseOperationBase::AutoSetProgressHandler::AutoSetProgressHandler()
14426 : mConnection(Nothing())
14429 mDEBUGDatabaseOp(nullptr)
14432 MOZ_ASSERT(!IsOnBackgroundThread());
14435 DatabaseOperationBase::AutoSetProgressHandler::~AutoSetProgressHandler() {
14436 MOZ_ASSERT(!IsOnBackgroundThread());
14443 nsresult
DatabaseOperationBase::AutoSetProgressHandler::Register(
14444 mozIStorageConnection
& aConnection
, DatabaseOperationBase
* aDatabaseOp
) {
14445 MOZ_ASSERT(!IsOnBackgroundThread());
14446 MOZ_ASSERT(aDatabaseOp
);
14447 MOZ_ASSERT(!mConnection
);
14450 const DebugOnly oldProgressHandler
,
14451 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
14452 nsCOMPtr
<mozIStorageProgressHandler
>, aConnection
, SetProgressHandler
,
14453 kStorageProgressGranularity
, aDatabaseOp
));
14455 MOZ_ASSERT(!oldProgressHandler
.inspect());
14457 mConnection
= SomeRef(aConnection
);
14459 mDEBUGDatabaseOp
= aDatabaseOp
;
14465 void DatabaseOperationBase::AutoSetProgressHandler::Unregister() {
14466 MOZ_ASSERT(!IsOnBackgroundThread());
14467 MOZ_ASSERT(mConnection
);
14469 nsCOMPtr
<mozIStorageProgressHandler
> oldHandler
;
14470 MOZ_ALWAYS_SUCCEEDS(
14471 mConnection
->RemoveProgressHandler(getter_AddRefs(oldHandler
)));
14472 MOZ_ASSERT(oldHandler
== mDEBUGDatabaseOp
);
14474 mConnection
= Nothing();
14477 FactoryOp::FactoryOp(SafeRefPtr
<Factory
> aFactory
,
14478 RefPtr
<ThreadsafeContentParentHandle
> aContentHandle
,
14479 const CommonFactoryRequestParams
& aCommonParams
,
14481 : DatabaseOperationBase(aFactory
->GetLoggingInfo()->Id(),
14482 aFactory
->GetLoggingInfo()->NextRequestSN()),
14483 mFactory(std::move(aFactory
)),
14484 mContentHandle(std::move(aContentHandle
)),
14485 mCommonParams(aCommonParams
),
14486 mDirectoryLockId(-1),
14487 mState(State::Initial
),
14488 mWaitingForPermissionRetry(false),
14489 mEnforcingQuota(true),
14490 mDeleting(aDeleting
),
14491 mChromeWriteAccessAllowed(false) {
14492 AssertIsOnBackgroundThread();
14493 MOZ_ASSERT(mFactory
);
14494 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
14497 void FactoryOp::NoteDatabaseBlocked(Database
* aDatabase
) {
14498 AssertIsOnOwningThread();
14499 MOZ_ASSERT(aDatabase
);
14500 MOZ_ASSERT(mState
== State::WaitingForOtherDatabasesToClose
);
14501 MOZ_ASSERT(!mMaybeBlockedDatabases
.IsEmpty());
14502 MOZ_ASSERT(mMaybeBlockedDatabases
.Contains(aDatabase
));
14504 // Only send the blocked event if all databases have reported back. If the
14505 // database was closed then it will have been removed from the array.
14506 // Otherwise if it was blocked its |mBlocked| flag will be true.
14507 bool sendBlockedEvent
= true;
14509 for (auto& info
: mMaybeBlockedDatabases
) {
14510 if (info
== aDatabase
) {
14511 // This database was blocked, mark accordingly.
14512 info
.mBlocked
= true;
14513 } else if (!info
.mBlocked
) {
14514 // A database has not yet reported back yet, don't send the event yet.
14515 sendBlockedEvent
= false;
14519 if (sendBlockedEvent
) {
14520 SendBlockedNotification();
14524 void FactoryOp::NoteDatabaseClosed(Database
* const aDatabase
) {
14525 AssertIsOnOwningThread();
14526 MOZ_ASSERT(aDatabase
);
14527 MOZ_ASSERT(mState
== State::WaitingForOtherDatabasesToClose
);
14528 MOZ_ASSERT(!mMaybeBlockedDatabases
.IsEmpty());
14529 MOZ_ASSERT(mMaybeBlockedDatabases
.Contains(aDatabase
));
14531 mMaybeBlockedDatabases
.RemoveElement(aDatabase
);
14533 if (!mMaybeBlockedDatabases
.IsEmpty()) {
14537 DatabaseActorInfo
* info
;
14538 MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable
->Get(mDatabaseId
, &info
));
14539 MOZ_ASSERT(info
->mWaitingFactoryOp
== this);
14541 if (AreActorsAlive()) {
14542 // The IPDL strong reference has not yet been released, so we can clear
14543 // mWaitingFactoryOp immediately.
14544 info
->mWaitingFactoryOp
= nullptr;
14546 WaitForTransactions();
14550 // The IPDL strong reference has been released, mWaitingFactoryOp holds the
14551 // last strong reference to us, so we need to move it to a stack variable
14552 // instead of clearing it immediately (We could clear it immediately if only
14553 // the other actor is destroyed, but we don't need to optimize for that, and
14554 // move it anyway).
14555 const RefPtr
<FactoryOp
> waitingFactoryOp
= std::move(info
->mWaitingFactoryOp
);
14557 IDB_REPORT_INTERNAL_ERR();
14558 SetFailureCodeIfUnset(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
14560 // We hold a strong ref in waitingFactoryOp, so it's safe to call Run()
14563 mState
= State::SendingResults
;
14564 MOZ_ALWAYS_SUCCEEDS(Run());
14567 void FactoryOp::StringifyState(nsACString
& aResult
) const {
14568 AssertIsOnOwningThread();
14571 case State::Initial
:
14572 aResult
.AppendLiteral("Initial");
14575 case State::FinishOpen
:
14576 aResult
.AppendLiteral("FinishOpen");
14579 case State::DirectoryOpenPending
:
14580 aResult
.AppendLiteral("DirectoryOpenPending");
14583 case State::DatabaseOpenPending
:
14584 aResult
.AppendLiteral("DatabaseOpenPending");
14587 case State::DatabaseWorkOpen
:
14588 aResult
.AppendLiteral("DatabaseWorkOpen");
14591 case State::BeginVersionChange
:
14592 aResult
.AppendLiteral("BeginVersionChange");
14595 case State::WaitingForOtherDatabasesToClose
:
14596 aResult
.AppendLiteral("WaitingForOtherDatabasesToClose");
14599 case State::WaitingForTransactionsToComplete
:
14600 aResult
.AppendLiteral("WaitingForTransactionsToComplete");
14603 case State::DatabaseWorkVersionChange
:
14604 aResult
.AppendLiteral("DatabaseWorkVersionChange");
14607 case State::SendingResults
:
14608 aResult
.AppendLiteral("SendingResults");
14611 case State::Completed
:
14612 aResult
.AppendLiteral("Completed");
14616 MOZ_CRASH("Bad state!");
14620 void FactoryOp::Stringify(nsACString
& aResult
) const {
14621 AssertIsOnOwningThread();
14623 aResult
.AppendLiteral("PersistenceType:");
14625 PersistenceTypeToString(mCommonParams
.metadata().persistenceType()));
14626 aResult
.Append(kQuotaGenericDelimiter
);
14628 aResult
.AppendLiteral("Origin:");
14629 aResult
.Append(AnonymizedOriginString(mOriginMetadata
.mOrigin
));
14630 aResult
.Append(kQuotaGenericDelimiter
);
14632 aResult
.AppendLiteral("State:");
14633 StringifyState(aResult
);
14636 nsresult
FactoryOp::Open() {
14637 AssertIsOnMainThread();
14638 MOZ_ASSERT(mState
== State::Initial
);
14640 RefPtr
<ContentParent
> contentParent
=
14641 mContentHandle
? mContentHandle
->GetContentParent() : nullptr;
14643 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
14644 !OperationMayProceed()) {
14645 IDB_REPORT_INTERNAL_ERR();
14646 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
14649 QM_TRY_INSPECT(const auto& permission
, CheckPermission(contentParent
));
14651 MOZ_ASSERT(permission
== PermissionValue::kPermissionAllowed
||
14652 permission
== PermissionValue::kPermissionDenied
);
14654 if (permission
== PermissionValue::kPermissionDenied
) {
14655 return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
;
14659 // These services have to be started on the main thread currently.
14661 IndexedDatabaseManager
* mgr
;
14662 if (NS_WARN_IF(!(mgr
= IndexedDatabaseManager::GetOrCreate()))) {
14663 IDB_REPORT_INTERNAL_ERR();
14664 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
14667 nsCOMPtr
<mozIStorageService
> ss
;
14668 if (NS_WARN_IF(!(ss
= do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID
)))) {
14669 IDB_REPORT_INTERNAL_ERR();
14670 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
14674 const DatabaseMetadata
& metadata
= mCommonParams
.metadata();
14676 QuotaManager::GetStorageId(metadata
.persistenceType(),
14677 mOriginMetadata
.mOrigin
, Client::IDB
, mDatabaseId
);
14679 mDatabaseId
.Append('*');
14680 mDatabaseId
.Append(NS_ConvertUTF16toUTF8(metadata
.name()));
14682 MOZ_ASSERT(permission
== PermissionValue::kPermissionAllowed
);
14684 if (mInPrivateBrowsing
) {
14685 gIndexedDBCipherKeyManager
->Ensure(mDatabaseId
);
14688 mState
= State::FinishOpen
;
14689 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
));
14694 nsresult
FactoryOp::DirectoryOpen() {
14695 AssertIsOnOwningThread();
14696 MOZ_ASSERT(mState
== State::DirectoryOpenPending
);
14697 MOZ_ASSERT(mDirectoryLock
);
14698 MOZ_ASSERT(!mDatabaseFilePath
.IsEmpty());
14699 MOZ_ASSERT(gFactoryOps
);
14701 // See if this FactoryOp needs to wait.
14702 const bool delayed
=
14704 gFactoryOps
->rbegin(), gFactoryOps
->rend(),
14705 [foundThis
= false, &self
= *this](const auto& existingOp
) mutable {
14706 if (existingOp
== &self
) {
14711 if (foundThis
&& self
.MustWaitFor(*existingOp
)) {
14712 // Only one op can be delayed.
14713 MOZ_ASSERT(!existingOp
->mDelayedOp
);
14714 existingOp
->mDelayedOp
= &self
;
14721 QuotaClient
* quotaClient
= QuotaClient::GetInstance();
14722 MOZ_ASSERT(quotaClient
);
14724 if (RefPtr
<Maintenance
> currentMaintenance
=
14725 quotaClient
->GetCurrentMaintenance()) {
14726 if (RefPtr
<DatabaseMaintenance
> databaseMaintenance
=
14727 currentMaintenance
->GetDatabaseMaintenance(
14728 self
.mDatabaseFilePath
)) {
14729 databaseMaintenance
->WaitForCompletion(&self
);
14737 mState
= State::DatabaseOpenPending
;
14739 QM_TRY(MOZ_TO_RESULT(DatabaseOpen()));
14745 nsresult
FactoryOp::SendToIOThread() {
14746 AssertIsOnOwningThread();
14747 MOZ_ASSERT(mState
== State::DatabaseOpenPending
);
14749 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
14750 !OperationMayProceed()) {
14751 IDB_REPORT_INTERNAL_ERR();
14752 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
14755 QuotaManager
* const quotaManager
= QuotaManager::Get();
14756 MOZ_ASSERT(quotaManager
);
14758 // Must set this before dispatching otherwise we will race with the IO thread.
14759 mState
= State::DatabaseWorkOpen
;
14761 QM_TRY(MOZ_TO_RESULT(
14762 quotaManager
->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL
)),
14763 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
14768 void FactoryOp::WaitForTransactions() {
14769 AssertIsOnOwningThread();
14770 MOZ_ASSERT(mState
== State::BeginVersionChange
||
14771 mState
== State::WaitingForOtherDatabasesToClose
);
14772 MOZ_ASSERT(!mDatabaseId
.IsEmpty());
14773 MOZ_ASSERT(!IsActorDestroyed());
14775 mState
= State::WaitingForTransactionsToComplete
;
14777 RefPtr
<WaitForTransactionsHelper
> helper
=
14778 new WaitForTransactionsHelper(mDatabaseId
, this);
14779 helper
->WaitForTransactions();
14782 void FactoryOp::CleanupMetadata() {
14783 AssertIsOnOwningThread();
14786 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mDelayedOp
.forget()));
14789 MOZ_ASSERT(gFactoryOps
);
14790 gFactoryOps
->RemoveElement(this);
14792 // We might get here even after QuotaManagerOpen failed, so we need to check
14793 // if we have a quota manager.
14794 quota::QuotaManager::SafeMaybeRecordQuotaClientShutdownStep(
14795 quota::Client::IDB
, "An element was removed from gFactoryOps"_ns
);
14797 // Match the IncreaseBusyCount in AllocPBackgroundIDBFactoryRequestParent().
14798 DecreaseBusyCount();
14801 void FactoryOp::FinishSendResults() {
14802 AssertIsOnOwningThread();
14803 MOZ_ASSERT(mState
== State::SendingResults
);
14804 MOZ_ASSERT(mFactory
);
14806 mState
= State::Completed
;
14808 // Make sure to release the factory on this thread.
14809 mFactory
= nullptr;
14812 Result
<PermissionValue
, nsresult
> FactoryOp::CheckPermission(
14813 ContentParent
* aContentParent
) {
14814 MOZ_ASSERT(NS_IsMainThread());
14815 MOZ_ASSERT(mState
== State::Initial
);
14817 const PrincipalInfo
& principalInfo
= mCommonParams
.principalInfo();
14818 if (principalInfo
.type() != PrincipalInfo::TSystemPrincipalInfo
) {
14819 if (principalInfo
.type() != PrincipalInfo::TContentPrincipalInfo
) {
14820 if (aContentParent
) {
14821 // We just want ContentPrincipalInfo or SystemPrincipalInfo.
14822 aContentParent
->KillHard("IndexedDB CheckPermission 0");
14825 return Err(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
14828 const ContentPrincipalInfo
& contentPrincipalInfo
=
14829 principalInfo
.get_ContentPrincipalInfo();
14830 if (contentPrincipalInfo
.attrs().mPrivateBrowsingId
!= 0) {
14831 if (StaticPrefs::dom_indexedDB_privateBrowsing_enabled()) {
14832 // XXX Not sure if this should be done from here, it goes beyond
14833 // checking the permissions.
14834 mInPrivateBrowsing
.Flip();
14836 return Err(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
14841 PersistenceType persistenceType
= mCommonParams
.metadata().persistenceType();
14843 MOZ_ASSERT(principalInfo
.type() != PrincipalInfo::TNullPrincipalInfo
);
14845 if (principalInfo
.type() == PrincipalInfo::TSystemPrincipalInfo
) {
14846 MOZ_ASSERT(mState
== State::Initial
);
14847 MOZ_ASSERT(persistenceType
== PERSISTENCE_TYPE_PERSISTENT
);
14849 if (aContentParent
) {
14850 // Check to make sure that the child process has access to the database it
14852 NS_ConvertUTF16toUTF8
databaseName(mCommonParams
.metadata().name());
14854 const nsAutoCString permissionStringWrite
=
14855 kPermissionStringBase
+ databaseName
+ kPermissionWriteSuffix
;
14856 const nsAutoCString permissionStringRead
=
14857 kPermissionStringBase
+ databaseName
+ kPermissionReadSuffix
;
14859 bool canWrite
= CheckAtLeastOneAppHasPermission(aContentParent
,
14860 permissionStringWrite
);
14864 MOZ_ASSERT(CheckAtLeastOneAppHasPermission(aContentParent
,
14865 permissionStringRead
));
14868 canRead
= CheckAtLeastOneAppHasPermission(aContentParent
,
14869 permissionStringRead
);
14872 // Deleting a database requires write permissions.
14873 if (mDeleting
&& !canWrite
) {
14874 aContentParent
->KillHard("IndexedDB CheckPermission 2");
14875 IDB_REPORT_INTERNAL_ERR();
14876 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
14879 // Opening or deleting requires read permissions.
14881 aContentParent
->KillHard("IndexedDB CheckPermission 3");
14882 IDB_REPORT_INTERNAL_ERR();
14883 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
14886 mChromeWriteAccessAllowed
= canWrite
;
14888 mChromeWriteAccessAllowed
= true;
14891 if (State::Initial
== mState
) {
14892 mOriginMetadata
= {QuotaManager::GetInfoForChrome(), persistenceType
};
14894 MOZ_ASSERT(QuotaManager::IsOriginInternal(mOriginMetadata
.mOrigin
));
14896 mEnforcingQuota
= false;
14899 return PermissionValue::kPermissionAllowed
;
14902 MOZ_ASSERT(principalInfo
.type() == PrincipalInfo::TContentPrincipalInfo
);
14904 QM_TRY_INSPECT(const auto& principal
,
14905 PrincipalInfoToPrincipal(principalInfo
));
14907 QM_TRY_UNWRAP(auto principalMetadata
,
14908 QuotaManager::GetInfoFromPrincipal(principal
));
14911 const auto& permission
,
14912 ([persistenceType
, &origin
= principalMetadata
.mOrigin
,
14914 *principal
]() -> mozilla::Result
<PermissionValue
, nsresult
> {
14915 if (persistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
14916 if (QuotaManager::IsOriginInternal(origin
)) {
14917 return PermissionValue::kPermissionAllowed
;
14919 return Err(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
14921 return PermissionValue::kPermissionAllowed
;
14924 if (permission
!= PermissionValue::kPermissionDenied
&&
14925 State::Initial
== mState
) {
14926 mOriginMetadata
= {std::move(principalMetadata
), persistenceType
};
14928 mEnforcingQuota
= persistenceType
!= PERSISTENCE_TYPE_PERSISTENT
;
14934 nsresult
FactoryOp::SendVersionChangeMessages(
14935 DatabaseActorInfo
* aDatabaseActorInfo
, Maybe
<Database
&> aOpeningDatabase
,
14936 uint64_t aOldVersion
, const Maybe
<uint64_t>& aNewVersion
) {
14937 AssertIsOnOwningThread();
14938 MOZ_ASSERT(aDatabaseActorInfo
);
14939 MOZ_ASSERT(mState
== State::BeginVersionChange
);
14940 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
14941 MOZ_ASSERT(!IsActorDestroyed());
14943 const uint32_t expectedCount
= mDeleting
? 0 : 1;
14944 const uint32_t liveCount
= aDatabaseActorInfo
->mLiveDatabases
.Length();
14945 if (liveCount
> expectedCount
) {
14946 nsTArray
<MaybeBlockedDatabaseInfo
> maybeBlockedDatabases
;
14947 for (const auto& database
: aDatabaseActorInfo
->mLiveDatabases
) {
14948 if ((!aOpeningDatabase
|| database
.get() != &aOpeningDatabase
.ref()) &&
14949 !database
->IsClosed() &&
14950 NS_WARN_IF(!maybeBlockedDatabases
.AppendElement(
14951 SafeRefPtr
{database
.get(), AcquireStrongRefFromRawPtr
{}},
14953 return NS_ERROR_OUT_OF_MEMORY
;
14957 mMaybeBlockedDatabases
= std::move(maybeBlockedDatabases
);
14960 // We don't want to wait forever if we were not able to send the
14962 mMaybeBlockedDatabases
.RemoveLastElements(
14963 mMaybeBlockedDatabases
.end() -
14964 std::remove_if(mMaybeBlockedDatabases
.begin(),
14965 mMaybeBlockedDatabases
.end(),
14966 [aOldVersion
, &aNewVersion
](auto& maybeBlockedDatabase
) {
14967 return !maybeBlockedDatabase
->SendVersionChange(
14968 aOldVersion
, aNewVersion
);
14972 } // namespace indexedDB
14975 bool FactoryOp::CheckAtLeastOneAppHasPermission(
14976 ContentParent
* aContentParent
, const nsACString
& aPermissionString
) {
14977 MOZ_ASSERT(NS_IsMainThread());
14978 MOZ_ASSERT(aContentParent
);
14979 MOZ_ASSERT(!aPermissionString
.IsEmpty());
14984 nsresult
FactoryOp::FinishOpen() {
14985 AssertIsOnOwningThread();
14986 MOZ_ASSERT(mState
== State::FinishOpen
);
14987 MOZ_ASSERT(!mOriginMetadata
.mOrigin
.IsEmpty());
14988 MOZ_ASSERT(!mDirectoryLock
);
14990 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
14991 IsActorDestroyed()) {
14992 IDB_REPORT_INTERNAL_ERR();
14993 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
14996 QM_TRY(QuotaManager::EnsureCreated());
14998 const PersistenceType persistenceType
=
14999 mCommonParams
.metadata().persistenceType();
15001 QuotaManager
* const quotaManager
= QuotaManager::Get();
15002 MOZ_ASSERT(quotaManager
);
15004 // Need to get database file path before opening the directory.
15005 // XXX: For what reason?
15006 QM_TRY_UNWRAP(mDatabaseFilePath
,
15007 ([this, quotaManager
,
15008 persistenceType
]() -> mozilla::Result
<nsString
, nsresult
> {
15009 QM_TRY_INSPECT(const auto& dbFile
,
15010 quotaManager
->GetDirectoryForOrigin(
15011 persistenceType
, mOriginMetadata
.mOrigin
));
15013 QM_TRY(MOZ_TO_RESULT(dbFile
->Append(
15014 NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME
))));
15016 QM_TRY(MOZ_TO_RESULT(dbFile
->Append(
15017 GetDatabaseFilenameBase(mCommonParams
.metadata().name()) +
15020 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15021 nsString
, dbFile
, GetPath
));
15025 RefPtr
<DirectoryLock
> directoryLock
= quotaManager
->CreateDirectoryLock(
15026 persistenceType
, mOriginMetadata
, Client::IDB
,
15027 /* aExclusive */ false);
15029 mState
= State::DirectoryOpenPending
;
15031 directoryLock
->Acquire(this);
15036 bool FactoryOp::MustWaitFor(const FactoryOp
& aExistingOp
) {
15037 AssertIsOnOwningThread();
15039 // Things for the same persistence type, the same origin and the same
15040 // database must wait.
15041 return aExistingOp
.mCommonParams
.metadata().persistenceType() ==
15042 mCommonParams
.metadata().persistenceType() &&
15043 aExistingOp
.mOriginMetadata
.mOrigin
== mOriginMetadata
.mOrigin
&&
15044 aExistingOp
.mDatabaseId
== mDatabaseId
;
15047 NS_IMPL_ISUPPORTS_INHERITED0(FactoryOp
, DatabaseOperationBase
)
15049 // Run() assumes that the caller holds a strong reference to the object that
15050 // can't be cleared while Run() is being executed.
15051 // So if you call Run() directly (as opposed to dispatching to an event queue)
15052 // you need to make sure there's such a reference.
15053 // See bug 1356824 for more details.
15056 const auto handleError
= [this](const nsresult rv
) {
15057 if (mState
!= State::SendingResults
) {
15058 SetFailureCodeIfUnset(rv
);
15060 // Must set mState before dispatching otherwise we will race with the
15062 mState
= State::SendingResults
;
15064 if (IsOnOwningThread()) {
15067 MOZ_ALWAYS_SUCCEEDS(
15068 mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
));
15074 case State::Initial
:
15075 QM_WARNONLY_TRY(MOZ_TO_RESULT(Open()), handleError
);
15078 case State::FinishOpen
:
15079 QM_WARNONLY_TRY(MOZ_TO_RESULT(FinishOpen()), handleError
);
15082 case State::DatabaseOpenPending
:
15083 QM_WARNONLY_TRY(MOZ_TO_RESULT(DatabaseOpen()), handleError
);
15086 case State::DatabaseWorkOpen
:
15087 QM_WARNONLY_TRY(MOZ_TO_RESULT(DoDatabaseWork()), handleError
);
15090 case State::BeginVersionChange
:
15091 QM_WARNONLY_TRY(MOZ_TO_RESULT(BeginVersionChange()), handleError
);
15094 case State::WaitingForTransactionsToComplete
:
15095 QM_WARNONLY_TRY(MOZ_TO_RESULT(DispatchToWorkThread()), handleError
);
15098 case State::SendingResults
:
15103 MOZ_CRASH("Bad state!");
15109 void FactoryOp::DirectoryLockAcquired(DirectoryLock
* aLock
) {
15110 AssertIsOnOwningThread();
15112 MOZ_ASSERT(mState
== State::DirectoryOpenPending
);
15113 MOZ_ASSERT(!mDirectoryLock
);
15115 mDirectoryLock
= aLock
;
15117 MOZ_ASSERT(mDirectoryLock
->Id() >= 0);
15118 mDirectoryLockId
= mDirectoryLock
->Id();
15120 QM_WARNONLY_TRY(MOZ_TO_RESULT(DirectoryOpen()), [this](const nsresult rv
) {
15121 SetFailureCodeIfUnset(rv
);
15123 // The caller holds a strong reference to us, no need for a self reference
15124 // before calling Run().
15126 mState
= State::SendingResults
;
15127 MOZ_ALWAYS_SUCCEEDS(Run());
15131 void FactoryOp::DirectoryLockFailed() {
15132 AssertIsOnOwningThread();
15133 MOZ_ASSERT(mState
== State::DirectoryOpenPending
);
15134 MOZ_ASSERT(!mDirectoryLock
);
15136 if (!HasFailed()) {
15137 IDB_REPORT_INTERNAL_ERR();
15138 SetFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
15141 // The caller holds a strong reference to us, no need for a self reference
15142 // before calling Run().
15144 mState
= State::SendingResults
;
15145 MOZ_ALWAYS_SUCCEEDS(Run());
15148 void FactoryOp::ActorDestroy(ActorDestroyReason aWhy
) {
15149 AssertIsOnBackgroundThread();
15151 NoteActorDestroyed();
15154 OpenDatabaseOp::OpenDatabaseOp(
15155 SafeRefPtr
<Factory
> aFactory
,
15156 RefPtr
<ThreadsafeContentParentHandle
> aContentHandle
,
15157 const CommonFactoryRequestParams
& aParams
)
15158 : FactoryOp(std::move(aFactory
), std::move(aContentHandle
), aParams
,
15159 /* aDeleting */ false),
15160 mMetadata(MakeSafeRefPtr
<FullDatabaseMetadata
>(aParams
.metadata())),
15161 mRequestedVersion(aParams
.metadata().version()),
15162 mVersionChangeOp(nullptr),
15165 void OpenDatabaseOp::ActorDestroy(ActorDestroyReason aWhy
) {
15166 AssertIsOnOwningThread();
15168 FactoryOp::ActorDestroy(aWhy
);
15170 if (mVersionChangeOp
) {
15171 mVersionChangeOp
->NoteActorDestroyed();
15175 nsresult
OpenDatabaseOp::DatabaseOpen() {
15176 AssertIsOnOwningThread();
15177 MOZ_ASSERT(mState
== State::DatabaseOpenPending
);
15179 nsresult rv
= SendToIOThread();
15180 if (NS_WARN_IF(NS_FAILED(rv
))) {
15187 nsresult
OpenDatabaseOp::DoDatabaseWork() {
15188 AssertIsOnIOThread();
15189 MOZ_ASSERT(mState
== State::DatabaseWorkOpen
);
15191 AUTO_PROFILER_LABEL("OpenDatabaseOp::DoDatabaseWork", DOM
);
15193 QM_TRY(OkIf(!QuotaClient::IsShuttingDownOnNonBackgroundThread()),
15194 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
15196 if (!OperationMayProceed()) {
15197 IDB_REPORT_INTERNAL_ERR();
15198 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
15201 const nsAString
& databaseName
= mCommonParams
.metadata().name();
15202 const PersistenceType persistenceType
=
15203 mCommonParams
.metadata().persistenceType();
15205 QuotaManager
* const quotaManager
= QuotaManager::Get();
15206 MOZ_ASSERT(quotaManager
);
15208 QM_TRY(MOZ_TO_RESULT(quotaManager
->EnsureStorageIsInitialized()));
15211 const auto& dbDirectory
,
15212 ([persistenceType
, "aManager
, this]()
15213 -> mozilla::Result
<std::pair
<nsCOMPtr
<nsIFile
>, bool>, nsresult
> {
15214 if (persistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
15215 QM_TRY_RETURN(quotaManager
->EnsurePersistentOriginIsInitialized(
15220 MOZ_TO_RESULT(quotaManager
->EnsureTemporaryStorageIsInitialized()));
15221 QM_TRY_RETURN(quotaManager
->EnsureTemporaryOriginIsInitialized(
15222 persistenceType
, mOriginMetadata
));
15224 .map([](const auto& res
) { return res
.first
; })));
15226 QM_TRY(MOZ_TO_RESULT(
15227 dbDirectory
->Append(NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME
))));
15230 QM_TRY_INSPECT(const bool& exists
,
15231 MOZ_TO_RESULT_INVOKE_MEMBER(dbDirectory
, Exists
));
15234 QM_TRY(MOZ_TO_RESULT(dbDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0755)));
15239 MOZ_ASSERT(NS_SUCCEEDED(dbDirectory
->IsDirectory(&isDirectory
)));
15240 MOZ_ASSERT(isDirectory
);
15245 const auto databaseFilenameBase
= GetDatabaseFilenameBase(databaseName
);
15247 QM_TRY_INSPECT(const auto& markerFile
,
15248 CloneFileAndAppend(*dbDirectory
, kIdbDeletionMarkerFilePrefix
+
15249 databaseFilenameBase
));
15251 QM_TRY_INSPECT(const bool& exists
,
15252 MOZ_TO_RESULT_INVOKE_MEMBER(markerFile
, Exists
));
15255 // Delete the database and directroy since they should be deleted in
15256 // previous operation.
15257 // Note: only update usage to the QuotaManager when mEnforcingQuota == true
15258 QM_TRY(MOZ_TO_RESULT(RemoveDatabaseFilesAndDirectory(
15259 *dbDirectory
, databaseFilenameBase
,
15260 mEnforcingQuota
? quotaManager
: nullptr, persistenceType
,
15261 mOriginMetadata
, databaseName
)));
15265 const auto& dbFile
,
15266 CloneFileAndAppend(*dbDirectory
, databaseFilenameBase
+ kSQLiteSuffix
));
15268 mTelemetryId
= TelemetryIdForFile(dbFile
);
15273 const auto& databaseFilePath
,
15274 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString
, dbFile
, GetPath
));
15276 MOZ_ASSERT(databaseFilePath
== mDatabaseFilePath
);
15281 const auto& fmDirectory
,
15282 CloneFileAndAppend(*dbDirectory
, databaseFilenameBase
+
15283 kFileManagerDirectoryNameSuffix
));
15285 Maybe
<const CipherKey
> maybeKey
=
15286 mInPrivateBrowsing
? gIndexedDBCipherKeyManager
->Get(mDatabaseId
)
15289 MOZ_RELEASE_ASSERT(mInPrivateBrowsing
== maybeKey
.isSome());
15292 NotNull
<nsCOMPtr
<mozIStorageConnection
>> connection
,
15293 CreateStorageConnection(*dbFile
, *fmDirectory
, databaseName
,
15294 mOriginMetadata
.mOrigin
, mDirectoryLockId
,
15295 mTelemetryId
, maybeKey
));
15297 AutoSetProgressHandler asph
;
15298 QM_TRY(MOZ_TO_RESULT(asph
.Register(*connection
, this)));
15300 QM_TRY(MOZ_TO_RESULT(LoadDatabaseInformation(*connection
)));
15302 MOZ_ASSERT(mMetadata
->mNextObjectStoreId
> mMetadata
->mObjectStores
.Count());
15303 MOZ_ASSERT(mMetadata
->mNextIndexId
> 0);
15305 // See if we need to do a versionchange transaction
15307 // Optional version semantics.
15308 if (!mRequestedVersion
) {
15309 // If the requested version was not specified and the database was created,
15310 // treat it as if version 1 were requested.
15311 // Otherwise, treat it as if the current version were requested.
15312 mRequestedVersion
= mMetadata
->mCommonMetadata
.version() == 0
15314 : mMetadata
->mCommonMetadata
.version();
15317 QM_TRY(OkIf(mMetadata
->mCommonMetadata
.version() <= mRequestedVersion
),
15318 NS_ERROR_DOM_INDEXEDDB_VERSION_ERR
);
15322 ([this, persistenceType
, &databaseName
, &fmDirectory
, &connection
]()
15323 -> mozilla::Result
<SafeRefPtr
<DatabaseFileManager
>, nsresult
> {
15324 IndexedDatabaseManager
* const mgr
= IndexedDatabaseManager::Get();
15327 SafeRefPtr
<DatabaseFileManager
> fileManager
= mgr
->GetFileManager(
15328 persistenceType
, mOriginMetadata
.mOrigin
, databaseName
);
15330 if (!fileManager
) {
15331 fileManager
= MakeSafeRefPtr
<DatabaseFileManager
>(
15332 persistenceType
, mOriginMetadata
, databaseName
, mDatabaseId
,
15333 mEnforcingQuota
, mInPrivateBrowsing
);
15335 QM_TRY(MOZ_TO_RESULT(fileManager
->Init(fmDirectory
, *connection
)));
15337 mgr
->AddFileManager(fileManager
.clonePtr());
15340 return fileManager
;
15343 // Must close connection before dispatching otherwise we might race with the
15344 // connection thread which needs to open the same database.
15347 MOZ_ALWAYS_SUCCEEDS(connection
->Close());
15349 // Must set mState before dispatching otherwise we will race with the owning
15351 mState
= (mMetadata
->mCommonMetadata
.version() == mRequestedVersion
)
15352 ? State::SendingResults
15353 : State::BeginVersionChange
;
15355 QM_TRY(MOZ_TO_RESULT(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
)));
15360 nsresult
OpenDatabaseOp::LoadDatabaseInformation(
15361 mozIStorageConnection
& aConnection
) {
15362 AssertIsOnIOThread();
15363 MOZ_ASSERT(mMetadata
);
15366 // Load version information.
15369 CreateAndExecuteSingleStepStatement
<
15370 SingleStepResult::ReturnNullIfNoResult
>(
15371 aConnection
, "SELECT name, origin, version FROM database"_ns
));
15373 QM_TRY(OkIf(stmt
), NS_ERROR_FILE_CORRUPTED
);
15375 QM_TRY_INSPECT(const auto& databaseName
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15376 nsString
, stmt
, GetString
, 0));
15378 QM_TRY(OkIf(mCommonParams
.metadata().name() == databaseName
),
15379 NS_ERROR_FILE_CORRUPTED
);
15381 QM_TRY_INSPECT(const auto& origin
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15382 nsCString
, stmt
, GetUTF8String
, 1));
15384 // We can't just compare these strings directly. See bug 1339081 comment 69.
15385 QM_TRY(OkIf(QuotaManager::AreOriginsEqualOnDisk(mOriginMetadata
.mOrigin
,
15387 NS_ERROR_FILE_CORRUPTED
);
15389 QM_TRY_INSPECT(const int64_t& version
,
15390 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetInt64
, 2));
15392 mMetadata
->mCommonMetadata
.version() = uint64_t(version
);
15395 ObjectStoreTable
& objectStores
= mMetadata
->mObjectStores
;
15398 const auto& lastObjectStoreId
,
15400 &objectStores
]() -> mozilla::Result
<IndexOrObjectStoreId
, nsresult
> {
15401 // Load object store names and ids.
15404 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15405 nsCOMPtr
<mozIStorageStatement
>, aConnection
, CreateStatement
,
15406 "SELECT id, auto_increment, name, key_path "
15407 "FROM object_store"_ns
));
15409 IndexOrObjectStoreId lastObjectStoreId
= 0;
15411 QM_TRY(CollectWhileHasResult(
15413 [&lastObjectStoreId
, &objectStores
,
15414 usedIds
= Maybe
<nsTHashSet
<uint64_t>>{},
15415 usedNames
= Maybe
<nsTHashSet
<nsString
>>{}](
15416 auto& stmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
15417 QM_TRY_INSPECT(const IndexOrObjectStoreId
& objectStoreId
,
15418 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetInt64
, 0));
15424 QM_TRY(OkIf(objectStoreId
> 0), Err(NS_ERROR_FILE_CORRUPTED
));
15425 QM_TRY(OkIf(!usedIds
.ref().Contains(objectStoreId
)),
15426 Err(NS_ERROR_FILE_CORRUPTED
));
15428 QM_TRY(OkIf(usedIds
.ref().Insert(objectStoreId
, fallible
)),
15429 Err(NS_ERROR_OUT_OF_MEMORY
));
15432 QM_TRY(MOZ_TO_RESULT(stmt
.GetString(2, name
)));
15435 usedNames
.emplace();
15438 QM_TRY(OkIf(!usedNames
.ref().Contains(name
)),
15439 Err(NS_ERROR_FILE_CORRUPTED
));
15441 QM_TRY(OkIf(usedNames
.ref().Insert(name
, fallible
)),
15442 Err(NS_ERROR_OUT_OF_MEMORY
));
15444 ObjectStoreMetadata commonMetadata
;
15445 commonMetadata
.id() = objectStoreId
;
15446 commonMetadata
.name() = std::move(name
);
15449 const int32_t& columnType
,
15450 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetTypeOfIndex
, 3));
15452 if (columnType
== mozIStorageStatement::VALUE_TYPE_NULL
) {
15453 commonMetadata
.keyPath() = KeyPath(0);
15455 MOZ_ASSERT(columnType
== mozIStorageStatement::VALUE_TYPE_TEXT
);
15457 nsString keyPathSerialization
;
15458 QM_TRY(MOZ_TO_RESULT(stmt
.GetString(3, keyPathSerialization
)));
15460 commonMetadata
.keyPath() =
15461 KeyPath::DeserializeFromString(keyPathSerialization
);
15462 QM_TRY(OkIf(commonMetadata
.keyPath().IsValid()),
15463 Err(NS_ERROR_FILE_CORRUPTED
));
15466 QM_TRY_INSPECT(const int64_t& nextAutoIncrementId
,
15467 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetInt64
, 1));
15469 commonMetadata
.autoIncrement() = !!nextAutoIncrementId
;
15471 QM_TRY(OkIf(objectStores
.InsertOrUpdate(
15473 MakeSafeRefPtr
<FullObjectStoreMetadata
>(
15474 std::move(commonMetadata
),
15475 FullObjectStoreMetadata::AutoIncrementIds
{
15476 nextAutoIncrementId
, nextAutoIncrementId
}),
15478 Err(NS_ERROR_OUT_OF_MEMORY
));
15480 lastObjectStoreId
= std::max(lastObjectStoreId
, objectStoreId
);
15485 return lastObjectStoreId
;
15489 const auto& lastIndexId
,
15491 &objectStores
]() -> mozilla::Result
<IndexOrObjectStoreId
, nsresult
> {
15492 // Load index information
15495 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15496 nsCOMPtr
<mozIStorageStatement
>, aConnection
, CreateStatement
,
15498 "id, object_store_id, name, key_path, "
15499 "unique_index, multientry, "
15500 "locale, is_auto_locale "
15501 "FROM object_store_index"_ns
));
15503 IndexOrObjectStoreId lastIndexId
= 0;
15505 QM_TRY(CollectWhileHasResult(
15507 [&lastIndexId
, &objectStores
, &aConnection
,
15508 usedIds
= Maybe
<nsTHashSet
<uint64_t>>{},
15509 usedNames
= Maybe
<nsTHashSet
<nsString
>>{}](
15510 auto& stmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
15511 QM_TRY_INSPECT(const IndexOrObjectStoreId
& objectStoreId
,
15512 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetInt64
, 1));
15514 // XXX Why does this return NS_ERROR_OUT_OF_MEMORY if we don't
15515 // know the object store id?
15517 auto objectStoreMetadata
= objectStores
.Lookup(objectStoreId
);
15518 QM_TRY(OkIf(static_cast<bool>(objectStoreMetadata
)),
15519 Err(NS_ERROR_OUT_OF_MEMORY
));
15521 MOZ_ASSERT((*objectStoreMetadata
)->mCommonMetadata
.id() ==
15524 IndexOrObjectStoreId indexId
;
15525 QM_TRY(MOZ_TO_RESULT(stmt
.GetInt64(0, &indexId
)));
15531 QM_TRY(OkIf(indexId
> 0), Err(NS_ERROR_FILE_CORRUPTED
));
15532 QM_TRY(OkIf(!usedIds
.ref().Contains(indexId
)),
15533 Err(NS_ERROR_FILE_CORRUPTED
));
15535 QM_TRY(OkIf(usedIds
.ref().Insert(indexId
, fallible
)),
15536 Err(NS_ERROR_OUT_OF_MEMORY
));
15539 QM_TRY(MOZ_TO_RESULT(stmt
.GetString(2, name
)));
15541 const nsAutoString hashName
=
15542 IntToString(indexId
) + u
":"_ns
+ name
;
15545 usedNames
.emplace();
15548 QM_TRY(OkIf(!usedNames
.ref().Contains(hashName
)),
15549 Err(NS_ERROR_FILE_CORRUPTED
));
15551 QM_TRY(OkIf(usedNames
.ref().Insert(hashName
, fallible
)),
15552 Err(NS_ERROR_OUT_OF_MEMORY
));
15554 auto indexMetadata
= MakeSafeRefPtr
<FullIndexMetadata
>();
15555 indexMetadata
->mCommonMetadata
.id() = indexId
;
15556 indexMetadata
->mCommonMetadata
.name() = name
;
15560 int32_t columnType
;
15561 nsresult rv
= stmt
.GetTypeOfIndex(3, &columnType
);
15562 MOZ_ASSERT(NS_SUCCEEDED(rv
));
15563 MOZ_ASSERT(columnType
!= mozIStorageStatement::VALUE_TYPE_NULL
);
15567 nsString keyPathSerialization
;
15568 QM_TRY(MOZ_TO_RESULT(stmt
.GetString(3, keyPathSerialization
)));
15570 indexMetadata
->mCommonMetadata
.keyPath() =
15571 KeyPath::DeserializeFromString(keyPathSerialization
);
15572 QM_TRY(OkIf(indexMetadata
->mCommonMetadata
.keyPath().IsValid()),
15573 Err(NS_ERROR_FILE_CORRUPTED
));
15576 QM_TRY(MOZ_TO_RESULT(stmt
.GetInt32(4, &scratch
)));
15578 indexMetadata
->mCommonMetadata
.unique() = !!scratch
;
15580 QM_TRY(MOZ_TO_RESULT(stmt
.GetInt32(5, &scratch
)));
15582 indexMetadata
->mCommonMetadata
.multiEntry() = !!scratch
;
15584 const bool localeAware
= !stmt
.IsNull(6);
15586 QM_TRY(MOZ_TO_RESULT(stmt
.GetUTF8String(
15587 6, indexMetadata
->mCommonMetadata
.locale())));
15589 QM_TRY(MOZ_TO_RESULT(stmt
.GetInt32(7, &scratch
)));
15591 indexMetadata
->mCommonMetadata
.autoLocale() = !!scratch
;
15593 // Update locale-aware indexes if necessary
15594 const nsCString
& indexedLocale
=
15595 indexMetadata
->mCommonMetadata
.locale();
15596 const bool& isAutoLocale
=
15597 indexMetadata
->mCommonMetadata
.autoLocale();
15598 const nsCString
& systemLocale
=
15599 IndexedDatabaseManager::GetLocale();
15600 if (!systemLocale
.IsEmpty() && isAutoLocale
&&
15601 !indexedLocale
.Equals(systemLocale
)) {
15602 QM_TRY(MOZ_TO_RESULT(UpdateLocaleAwareIndex(
15603 aConnection
, indexMetadata
->mCommonMetadata
,
15608 QM_TRY(OkIf((*objectStoreMetadata
)
15609 ->mIndexes
.InsertOrUpdate(
15610 indexId
, std::move(indexMetadata
), fallible
)),
15611 Err(NS_ERROR_OUT_OF_MEMORY
));
15613 lastIndexId
= std::max(lastIndexId
, indexId
);
15618 return lastIndexId
;
15621 QM_TRY(OkIf(lastObjectStoreId
!= INT64_MAX
),
15622 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
15623 QM_TRY(OkIf(lastIndexId
!= INT64_MAX
), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
,
15624 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
15626 mMetadata
->mNextObjectStoreId
= lastObjectStoreId
+ 1;
15627 mMetadata
->mNextIndexId
= lastIndexId
+ 1;
15633 nsresult
OpenDatabaseOp::UpdateLocaleAwareIndex(
15634 mozIStorageConnection
& aConnection
, const IndexMetadata
& aIndexMetadata
,
15635 const nsCString
& aLocale
) {
15636 const auto indexTable
=
15637 aIndexMetadata
.unique() ? "unique_index_data"_ns
: "index_data"_ns
;
15639 // The parameter names are not used, parameters are bound by index only
15640 // locally in the same function.
15641 const nsCString readQuery
= "SELECT value, object_data_key FROM "_ns
+
15642 indexTable
+ " WHERE index_id = :index_id"_ns
;
15644 QM_TRY_INSPECT(const auto& readStmt
,
15645 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15646 nsCOMPtr
<mozIStorageStatement
>, aConnection
,
15647 CreateStatement
, readQuery
));
15649 QM_TRY(MOZ_TO_RESULT(readStmt
->BindInt64ByIndex(0, aIndexMetadata
.id())));
15651 QM_TRY(CollectWhileHasResult(
15653 [&aConnection
, &indexTable
, &aIndexMetadata
, &aLocale
,
15654 writeStmt
= nsCOMPtr
<mozIStorageStatement
>{}](
15655 auto& readStmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
15659 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15660 nsCOMPtr
<mozIStorageStatement
>, aConnection
, CreateStatement
,
15661 "UPDATE "_ns
+ indexTable
+ "SET value_locale = :"_ns
+
15662 kStmtParamNameValueLocale
+ " WHERE index_id = :"_ns
+
15663 kStmtParamNameIndexId
+ " AND value = :"_ns
+
15664 kStmtParamNameValue
+ " AND object_data_key = :"_ns
+
15665 kStmtParamNameObjectDataKey
));
15668 mozStorageStatementScoper
scoper(writeStmt
);
15669 QM_TRY(MOZ_TO_RESULT(writeStmt
->BindInt64ByName(kStmtParamNameIndexId
,
15670 aIndexMetadata
.id())));
15672 Key oldKey
, objectStorePosition
;
15673 QM_TRY(MOZ_TO_RESULT(oldKey
.SetFromStatement(&readStmt
, 0)));
15674 QM_TRY(MOZ_TO_RESULT(
15675 oldKey
.BindToStatement(writeStmt
, kStmtParamNameValue
)));
15677 QM_TRY_INSPECT(const auto& newSortKey
,
15678 oldKey
.ToLocaleAwareKey(aLocale
));
15680 QM_TRY(MOZ_TO_RESULT(
15681 newSortKey
.BindToStatement(writeStmt
, kStmtParamNameValueLocale
)));
15683 MOZ_TO_RESULT(objectStorePosition
.SetFromStatement(&readStmt
, 1)));
15684 QM_TRY(MOZ_TO_RESULT(objectStorePosition
.BindToStatement(
15685 writeStmt
, kStmtParamNameObjectDataKey
)));
15687 QM_TRY(MOZ_TO_RESULT(writeStmt
->Execute()));
15692 // The parameter names are not used, parameters are bound by index only
15693 // locally in the same function.
15694 static constexpr auto metaQuery
=
15695 "UPDATE object_store_index SET "
15696 "locale = :locale WHERE id = :id"_ns
;
15698 QM_TRY_INSPECT(const auto& metaStmt
,
15699 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15700 nsCOMPtr
<mozIStorageStatement
>, aConnection
,
15701 CreateStatement
, metaQuery
));
15703 QM_TRY(MOZ_TO_RESULT(
15704 metaStmt
->BindStringByIndex(0, NS_ConvertASCIItoUTF16(aLocale
))));
15706 QM_TRY(MOZ_TO_RESULT(metaStmt
->BindInt64ByIndex(1, aIndexMetadata
.id())));
15708 QM_TRY(MOZ_TO_RESULT(metaStmt
->Execute()));
15713 nsresult
OpenDatabaseOp::BeginVersionChange() {
15714 AssertIsOnOwningThread();
15715 MOZ_ASSERT(mState
== State::BeginVersionChange
);
15716 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
15717 MOZ_ASSERT(mMetadata
->mCommonMetadata
.version() <= mRequestedVersion
);
15718 MOZ_ASSERT(!mDatabase
);
15719 MOZ_ASSERT(!mVersionChangeTransaction
);
15721 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
15722 IsActorDestroyed()) {
15723 IDB_REPORT_INTERNAL_ERR();
15724 QM_TRY(MOZ_TO_RESULT(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
));
15727 EnsureDatabaseActor();
15729 if (mDatabase
->IsInvalidated()) {
15730 IDB_REPORT_INTERNAL_ERR();
15731 QM_TRY(MOZ_TO_RESULT(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
));
15734 MOZ_ASSERT(!mDatabase
->IsClosed());
15736 DatabaseActorInfo
* info
;
15737 MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable
->Get(mDatabaseId
, &info
));
15739 MOZ_ASSERT(info
->mLiveDatabases
.Contains(mDatabase
.unsafeGetRawPtr()));
15740 MOZ_ASSERT(!info
->mWaitingFactoryOp
);
15741 MOZ_ASSERT(info
->mMetadata
== mMetadata
);
15743 auto transaction
= MakeSafeRefPtr
<VersionChangeTransaction
>(this);
15745 if (NS_WARN_IF(!transaction
->CopyDatabaseMetadata())) {
15746 return NS_ERROR_OUT_OF_MEMORY
;
15749 MOZ_ASSERT(info
->mMetadata
!= mMetadata
);
15750 mMetadata
= info
->mMetadata
.clonePtr();
15752 const Maybe
<uint64_t> newVersion
= Some(mRequestedVersion
);
15754 QM_TRY(MOZ_TO_RESULT(SendVersionChangeMessages(
15755 info
, mDatabase
.maybeDeref(), mMetadata
->mCommonMetadata
.version(),
15758 mVersionChangeTransaction
= std::move(transaction
);
15760 if (mMaybeBlockedDatabases
.IsEmpty()) {
15761 // We don't need to wait on any databases, just jump to the transaction
15763 WaitForTransactions();
15767 // If the actor gets destroyed, mWaitingFactoryOp will hold the last strong
15768 // reference to us.
15769 info
->mWaitingFactoryOp
= this;
15771 mState
= State::WaitingForOtherDatabasesToClose
;
15775 bool OpenDatabaseOp::AreActorsAlive() {
15776 AssertIsOnOwningThread();
15777 MOZ_ASSERT(mDatabase
);
15779 return !(IsActorDestroyed() || mDatabase
->IsActorDestroyed());
15782 void OpenDatabaseOp::SendBlockedNotification() {
15783 AssertIsOnOwningThread();
15784 MOZ_ASSERT(mState
== State::WaitingForOtherDatabasesToClose
);
15786 if (!IsActorDestroyed()) {
15787 Unused
<< SendBlocked(mMetadata
->mCommonMetadata
.version());
15791 nsresult
OpenDatabaseOp::DispatchToWorkThread() {
15792 AssertIsOnOwningThread();
15793 MOZ_ASSERT(mState
== State::WaitingForTransactionsToComplete
);
15794 MOZ_ASSERT(mVersionChangeTransaction
);
15795 MOZ_ASSERT(mVersionChangeTransaction
->GetMode() ==
15796 IDBTransaction::Mode::VersionChange
);
15797 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
15799 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
15800 IsActorDestroyed() || mDatabase
->IsInvalidated()) {
15801 IDB_REPORT_INTERNAL_ERR();
15802 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
15805 mState
= State::DatabaseWorkVersionChange
;
15807 // Intentionally empty.
15808 nsTArray
<nsString
> objectStoreNames
;
15810 const int64_t loggingSerialNumber
=
15811 mVersionChangeTransaction
->LoggingSerialNumber();
15812 const nsID
& backgroundChildLoggingId
=
15813 mVersionChangeTransaction
->GetLoggingInfo()->Id();
15815 if (NS_WARN_IF(!mDatabase
->RegisterTransaction(*mVersionChangeTransaction
))) {
15816 return NS_ERROR_OUT_OF_MEMORY
;
15819 if (!gConnectionPool
) {
15820 gConnectionPool
= new ConnectionPool();
15823 RefPtr
<VersionChangeOp
> versionChangeOp
= new VersionChangeOp(this);
15825 uint64_t transactionId
= versionChangeOp
->StartOnConnectionPool(
15826 backgroundChildLoggingId
, mVersionChangeTransaction
->DatabaseId(),
15827 loggingSerialNumber
, objectStoreNames
,
15828 /* aIsWriteTransaction */ true);
15830 mVersionChangeOp
= versionChangeOp
;
15832 mVersionChangeTransaction
->NoteActiveRequest();
15833 mVersionChangeTransaction
->Init(transactionId
);
15838 nsresult
OpenDatabaseOp::SendUpgradeNeeded() {
15839 AssertIsOnOwningThread();
15840 MOZ_ASSERT(mState
== State::DatabaseWorkVersionChange
);
15841 MOZ_ASSERT(mVersionChangeTransaction
);
15842 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
15843 MOZ_ASSERT(!HasFailed());
15844 MOZ_ASSERT_IF(!IsActorDestroyed(), mDatabase
);
15846 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
15847 IsActorDestroyed()) {
15848 IDB_REPORT_INTERNAL_ERR();
15849 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
15852 const SafeRefPtr
<VersionChangeTransaction
> transaction
=
15853 std::move(mVersionChangeTransaction
);
15855 nsresult rv
= EnsureDatabaseActorIsAlive();
15856 if (NS_WARN_IF(NS_FAILED(rv
))) {
15860 // Transfer ownership to IPDL.
15861 transaction
->SetActorAlive();
15863 if (!mDatabase
->SendPBackgroundIDBVersionChangeTransactionConstructor(
15864 transaction
.unsafeGetRawPtr(), mMetadata
->mCommonMetadata
.version(),
15865 mRequestedVersion
, mMetadata
->mNextObjectStoreId
,
15866 mMetadata
->mNextIndexId
)) {
15867 IDB_REPORT_INTERNAL_ERR();
15868 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
15874 void OpenDatabaseOp::SendResults() {
15875 AssertIsOnOwningThread();
15876 MOZ_ASSERT(mState
== State::SendingResults
);
15877 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
15878 MOZ_ASSERT_IF(!HasFailed(), !mVersionChangeTransaction
);
15880 DebugOnly
<DatabaseActorInfo
*> info
= nullptr;
15882 gLiveDatabaseHashtable
&& gLiveDatabaseHashtable
->Get(mDatabaseId
, &info
),
15883 !info
->mWaitingFactoryOp
);
15885 if (mVersionChangeTransaction
) {
15886 MOZ_ASSERT(HasFailed());
15888 mVersionChangeTransaction
->Abort(ResultCode(), /* aForce */ true);
15889 mVersionChangeTransaction
= nullptr;
15892 if (IsActorDestroyed()) {
15893 SetFailureCodeIfUnset(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
15895 FactoryRequestResponse response
;
15897 if (!HasFailed()) {
15898 // If we just successfully completed a versionchange operation then we
15899 // need to update the version in our metadata.
15900 mMetadata
->mCommonMetadata
.version() = mRequestedVersion
;
15902 nsresult rv
= EnsureDatabaseActorIsAlive();
15903 if (NS_SUCCEEDED(rv
)) {
15904 // We successfully opened a database so use its actor as the success
15905 // result for this request.
15907 // XXX OpenDatabaseRequestResponse stores a raw pointer, can this be
15910 OpenDatabaseRequestResponse
{mDatabase
.unsafeGetRawPtr(), nullptr};
15912 response
= ClampResultCode(rv
);
15914 SetFailureCode(response
.get_nsresult());
15919 // If something failed then our metadata pointer is now bad. No one should
15920 // ever touch it again though so just null it out in DEBUG builds to make
15921 // sure we find such cases.
15922 mMetadata
= nullptr;
15924 response
= ClampResultCode(ResultCode());
15927 Unused
<< PBackgroundIDBFactoryRequestParent::Send__delete__(this,
15932 MOZ_ASSERT(!mDirectoryLock
);
15935 mDatabase
->Invalidate();
15938 // Make sure to release the database on this thread.
15939 mDatabase
= nullptr;
15942 } else if (mDirectoryLock
) {
15943 // ConnectionClosedCallback will call CleanupMetadata().
15944 nsCOMPtr
<nsIRunnable
> callback
= NewRunnableMethod(
15945 "dom::indexedDB::OpenDatabaseOp::ConnectionClosedCallback", this,
15946 &OpenDatabaseOp::ConnectionClosedCallback
);
15948 RefPtr
<WaitForTransactionsHelper
> helper
=
15949 new WaitForTransactionsHelper(mDatabaseId
, callback
);
15950 helper
->WaitForTransactions();
15955 FinishSendResults();
15958 void OpenDatabaseOp::ConnectionClosedCallback() {
15959 AssertIsOnOwningThread();
15960 MOZ_ASSERT(HasFailed());
15961 MOZ_ASSERT(mDirectoryLock
);
15963 mDirectoryLock
= nullptr;
15968 void OpenDatabaseOp::EnsureDatabaseActor() {
15969 AssertIsOnOwningThread();
15970 MOZ_ASSERT(mState
== State::BeginVersionChange
||
15971 mState
== State::DatabaseWorkVersionChange
||
15972 mState
== State::SendingResults
);
15973 MOZ_ASSERT(!HasFailed());
15974 MOZ_ASSERT(!mDatabaseFilePath
.IsEmpty());
15975 MOZ_ASSERT(!IsActorDestroyed());
15981 MOZ_ASSERT(mMetadata
->mDatabaseId
.IsEmpty());
15982 mMetadata
->mDatabaseId
= mDatabaseId
;
15984 MOZ_ASSERT(mMetadata
->mFilePath
.IsEmpty());
15985 mMetadata
->mFilePath
= mDatabaseFilePath
;
15987 DatabaseActorInfo
* info
;
15988 if (gLiveDatabaseHashtable
->Get(mDatabaseId
, &info
)) {
15989 AssertMetadataConsistency(*info
->mMetadata
);
15990 mMetadata
= info
->mMetadata
.clonePtr();
15993 Maybe
<const CipherKey
> maybeKey
=
15994 mInPrivateBrowsing
? gIndexedDBCipherKeyManager
->Get(mDatabaseId
)
15997 MOZ_RELEASE_ASSERT(mInPrivateBrowsing
== maybeKey
.isSome());
15999 // XXX Shouldn't Manager() return already_AddRefed when
16000 // PBackgroundIDBFactoryParent is declared refcounted?
16001 mDatabase
= MakeSafeRefPtr
<Database
>(
16002 SafeRefPtr
{static_cast<Factory
*>(Manager()),
16003 AcquireStrongRefFromRawPtr
{}},
16004 mCommonParams
.principalInfo(),
16005 mContentHandle
? Some(mContentHandle
->ChildID()) : Nothing(),
16006 mOriginMetadata
, mTelemetryId
, mMetadata
.clonePtr(),
16007 mFileManager
.clonePtr(), std::move(mDirectoryLock
),
16008 mChromeWriteAccessAllowed
, mInPrivateBrowsing
, maybeKey
);
16011 info
->mLiveDatabases
.AppendElement(
16012 WrapNotNullUnchecked(mDatabase
.unsafeGetRawPtr()));
16014 // XXX Maybe use LookupOrInsertWith above, to avoid a second lookup here?
16015 info
= gLiveDatabaseHashtable
16018 MakeUnique
<DatabaseActorInfo
>(
16019 mMetadata
.clonePtr(),
16020 WrapNotNullUnchecked(mDatabase
.unsafeGetRawPtr())))
16024 // Balanced in Database::CleanupMetadata().
16025 IncreaseBusyCount();
16028 nsresult
OpenDatabaseOp::EnsureDatabaseActorIsAlive() {
16029 AssertIsOnOwningThread();
16030 MOZ_ASSERT(mState
== State::DatabaseWorkVersionChange
||
16031 mState
== State::SendingResults
);
16032 MOZ_ASSERT(!HasFailed());
16033 MOZ_ASSERT(!IsActorDestroyed());
16035 EnsureDatabaseActor();
16037 if (mDatabase
->IsActorAlive()) {
16041 auto* const factory
= static_cast<Factory
*>(Manager());
16043 QM_TRY_INSPECT(const auto& spec
, MetadataToSpec());
16045 // Transfer ownership to IPDL.
16046 mDatabase
->SetActorAlive();
16048 if (!factory
->SendPBackgroundIDBDatabaseConstructor(
16049 mDatabase
.unsafeGetRawPtr(), spec
, this)) {
16050 IDB_REPORT_INTERNAL_ERR();
16051 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16057 Result
<DatabaseSpec
, nsresult
> OpenDatabaseOp::MetadataToSpec() const {
16058 AssertIsOnOwningThread();
16059 MOZ_ASSERT(mMetadata
);
16062 spec
.metadata() = mMetadata
->mCommonMetadata
;
16064 QM_TRY_UNWRAP(spec
.objectStores(),
16065 TransformIntoNewArrayAbortOnErr(
16066 mMetadata
->mObjectStores
,
16067 [](const auto& objectStoreEntry
)
16068 -> mozilla::Result
<ObjectStoreSpec
, nsresult
> {
16069 FullObjectStoreMetadata
* metadata
=
16070 objectStoreEntry
.GetWeak();
16071 MOZ_ASSERT(objectStoreEntry
.GetKey());
16072 MOZ_ASSERT(metadata
);
16074 ObjectStoreSpec objectStoreSpec
;
16075 objectStoreSpec
.metadata() = metadata
->mCommonMetadata
;
16077 QM_TRY_UNWRAP(auto indexes
,
16078 TransformIntoNewArray(
16079 metadata
->mIndexes
,
16080 [](const auto& indexEntry
) {
16081 FullIndexMetadata
* indexMetadata
=
16082 indexEntry
.GetWeak();
16083 MOZ_ASSERT(indexEntry
.GetKey());
16084 MOZ_ASSERT(indexMetadata
);
16086 return indexMetadata
->mCommonMetadata
;
16090 objectStoreSpec
.indexes() = std::move(indexes
);
16092 return objectStoreSpec
;
16101 void OpenDatabaseOp::AssertMetadataConsistency(
16102 const FullDatabaseMetadata
& aMetadata
) {
16103 AssertIsOnBackgroundThread();
16105 const FullDatabaseMetadata
& thisDB
= *mMetadata
;
16106 const FullDatabaseMetadata
& otherDB
= aMetadata
;
16108 MOZ_ASSERT(&thisDB
!= &otherDB
);
16110 MOZ_ASSERT(thisDB
.mCommonMetadata
.name() == otherDB
.mCommonMetadata
.name());
16111 MOZ_ASSERT(thisDB
.mCommonMetadata
.version() ==
16112 otherDB
.mCommonMetadata
.version());
16113 MOZ_ASSERT(thisDB
.mCommonMetadata
.persistenceType() ==
16114 otherDB
.mCommonMetadata
.persistenceType());
16115 MOZ_ASSERT(thisDB
.mDatabaseId
== otherDB
.mDatabaseId
);
16116 MOZ_ASSERT(thisDB
.mFilePath
== otherDB
.mFilePath
);
16118 // |thisDB| reflects the latest objectStore and index ids that have committed
16119 // to disk. The in-memory metadata |otherDB| keeps track of objectStores and
16120 // indexes that were created and then removed as well, so the next ids for
16121 // |otherDB| may be higher than for |thisDB|.
16122 MOZ_ASSERT(thisDB
.mNextObjectStoreId
<= otherDB
.mNextObjectStoreId
);
16123 MOZ_ASSERT(thisDB
.mNextIndexId
<= otherDB
.mNextIndexId
);
16125 MOZ_ASSERT(thisDB
.mObjectStores
.Count() == otherDB
.mObjectStores
.Count());
16127 for (const auto& thisObjectStore
: thisDB
.mObjectStores
.Values()) {
16128 MOZ_ASSERT(thisObjectStore
);
16129 MOZ_ASSERT(!thisObjectStore
->mDeleted
);
16131 auto otherObjectStore
= MatchMetadataNameOrId(
16132 otherDB
.mObjectStores
, thisObjectStore
->mCommonMetadata
.id());
16133 MOZ_ASSERT(otherObjectStore
);
16135 MOZ_ASSERT(thisObjectStore
!= &otherObjectStore
.ref());
16137 MOZ_ASSERT(thisObjectStore
->mCommonMetadata
.id() ==
16138 otherObjectStore
->mCommonMetadata
.id());
16139 MOZ_ASSERT(thisObjectStore
->mCommonMetadata
.name() ==
16140 otherObjectStore
->mCommonMetadata
.name());
16141 MOZ_ASSERT(thisObjectStore
->mCommonMetadata
.autoIncrement() ==
16142 otherObjectStore
->mCommonMetadata
.autoIncrement());
16143 MOZ_ASSERT(thisObjectStore
->mCommonMetadata
.keyPath() ==
16144 otherObjectStore
->mCommonMetadata
.keyPath());
16145 // mNextAutoIncrementId and mCommittedAutoIncrementId may be modified
16146 // concurrently with this OpenOp, so it is not possible to assert equality
16147 // here. It's also possible that we've written the new ids to disk but not
16148 // yet updated the in-memory count.
16149 // TODO The first part of the comment should probably be rephrased. I think
16150 // it still applies but it sounds as if this were thread-unsafe like it was
16151 // before, which isn't true anymore.
16153 const auto&& thisAutoIncrementIds
=
16154 thisObjectStore
->mAutoIncrementIds
.Lock();
16155 const auto&& otherAutoIncrementIds
=
16156 otherObjectStore
->mAutoIncrementIds
.Lock();
16158 MOZ_ASSERT(thisAutoIncrementIds
->next
<= otherAutoIncrementIds
->next
);
16160 thisAutoIncrementIds
->committed
<= otherAutoIncrementIds
->committed
||
16161 thisAutoIncrementIds
->committed
== otherAutoIncrementIds
->next
);
16163 MOZ_ASSERT(!otherObjectStore
->mDeleted
);
16165 MOZ_ASSERT(thisObjectStore
->mIndexes
.Count() ==
16166 otherObjectStore
->mIndexes
.Count());
16168 for (const auto& thisIndex
: thisObjectStore
->mIndexes
.Values()) {
16169 MOZ_ASSERT(thisIndex
);
16170 MOZ_ASSERT(!thisIndex
->mDeleted
);
16172 auto otherIndex
= MatchMetadataNameOrId(otherObjectStore
->mIndexes
,
16173 thisIndex
->mCommonMetadata
.id());
16174 MOZ_ASSERT(otherIndex
);
16176 MOZ_ASSERT(thisIndex
!= &otherIndex
.ref());
16178 MOZ_ASSERT(thisIndex
->mCommonMetadata
.id() ==
16179 otherIndex
->mCommonMetadata
.id());
16180 MOZ_ASSERT(thisIndex
->mCommonMetadata
.name() ==
16181 otherIndex
->mCommonMetadata
.name());
16182 MOZ_ASSERT(thisIndex
->mCommonMetadata
.keyPath() ==
16183 otherIndex
->mCommonMetadata
.keyPath());
16184 MOZ_ASSERT(thisIndex
->mCommonMetadata
.unique() ==
16185 otherIndex
->mCommonMetadata
.unique());
16186 MOZ_ASSERT(thisIndex
->mCommonMetadata
.multiEntry() ==
16187 otherIndex
->mCommonMetadata
.multiEntry());
16188 MOZ_ASSERT(!otherIndex
->mDeleted
);
16195 nsresult
OpenDatabaseOp::VersionChangeOp::DoDatabaseWork(
16196 DatabaseConnection
* aConnection
) {
16197 MOZ_ASSERT(aConnection
);
16198 aConnection
->AssertIsOnConnectionThread();
16199 MOZ_ASSERT(mOpenDatabaseOp
);
16200 MOZ_ASSERT(mOpenDatabaseOp
->mState
== State::DatabaseWorkVersionChange
);
16202 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
16203 !OperationMayProceed()) {
16204 IDB_REPORT_INTERNAL_ERR();
16205 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16208 AUTO_PROFILER_LABEL("OpenDatabaseOp::VersionChangeOp::DoDatabaseWork", DOM
);
16210 IDB_LOG_MARK_PARENT_TRANSACTION("Beginning database work", "DB Start",
16211 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
16212 mTransactionLoggingSerialNumber
);
16214 Transaction().SetActiveOnConnectionThread();
16216 QM_TRY(MOZ_TO_RESULT(aConnection
->BeginWriteTransaction()));
16218 // The parameter names are not used, parameters are bound by index only
16219 // locally in the same function.
16220 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
16221 "UPDATE database SET version = :version;"_ns
,
16223 mozIStorageStatement
& updateStmt
) -> mozilla::Result
<Ok
, nsresult
> {
16224 QM_TRY(MOZ_TO_RESULT(
16225 updateStmt
.BindInt64ByIndex(0, int64_t(self
.mRequestedVersion
))));
16233 nsresult
OpenDatabaseOp::VersionChangeOp::SendSuccessResult() {
16234 AssertIsOnOwningThread();
16235 MOZ_ASSERT(mOpenDatabaseOp
);
16236 MOZ_ASSERT(mOpenDatabaseOp
->mState
== State::DatabaseWorkVersionChange
);
16237 MOZ_ASSERT(mOpenDatabaseOp
->mVersionChangeOp
== this);
16239 nsresult rv
= mOpenDatabaseOp
->SendUpgradeNeeded();
16240 if (NS_WARN_IF(NS_FAILED(rv
))) {
16247 bool OpenDatabaseOp::VersionChangeOp::SendFailureResult(nsresult aResultCode
) {
16248 AssertIsOnOwningThread();
16249 MOZ_ASSERT(mOpenDatabaseOp
);
16250 MOZ_ASSERT(mOpenDatabaseOp
->mState
== State::DatabaseWorkVersionChange
);
16251 MOZ_ASSERT(mOpenDatabaseOp
->mVersionChangeOp
== this);
16253 mOpenDatabaseOp
->SetFailureCode(aResultCode
);
16254 mOpenDatabaseOp
->mState
= State::SendingResults
;
16256 MOZ_ALWAYS_SUCCEEDS(mOpenDatabaseOp
->Run());
16261 void OpenDatabaseOp::VersionChangeOp::Cleanup() {
16262 AssertIsOnOwningThread();
16263 MOZ_ASSERT(mOpenDatabaseOp
);
16264 MOZ_ASSERT(mOpenDatabaseOp
->mVersionChangeOp
== this);
16266 mOpenDatabaseOp
->mVersionChangeOp
= nullptr;
16267 mOpenDatabaseOp
= nullptr;
16270 // A bit hacky but the VersionChangeOp is not generated in response to a
16271 // child request like most other database operations. Do this to make our
16272 // assertions happy.
16274 // XXX: Depending on timing, in most cases, NoteActorDestroyed will not have
16275 // been destroyed before, but in some cases it has. This should be reworked in
16276 // a way this hack is not necessary. There are also several similar cases in
16277 // other *Op classes.
16278 if (!IsActorDestroyed()) {
16279 NoteActorDestroyed();
16283 TransactionDatabaseOperationBase::Cleanup();
16286 void DeleteDatabaseOp::LoadPreviousVersion(nsIFile
& aDatabaseFile
) {
16287 AssertIsOnIOThread();
16288 MOZ_ASSERT(mState
== State::DatabaseWorkOpen
);
16289 MOZ_ASSERT(!mPreviousVersion
);
16291 AUTO_PROFILER_LABEL("DeleteDatabaseOp::LoadPreviousVersion", DOM
);
16295 nsCOMPtr
<mozIStorageService
> ss
=
16296 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID
, &rv
);
16297 if (NS_WARN_IF(NS_FAILED(rv
))) {
16301 const auto maybeKey
= mInPrivateBrowsing
16302 ? gIndexedDBCipherKeyManager
->Get(mDatabaseId
)
16305 MOZ_RELEASE_ASSERT(mInPrivateBrowsing
== maybeKey
.isSome());
16307 // Pass -1 as the directoryLockId to disable quota checking, since we might
16308 // temporarily exceed quota before deleting the database.
16309 QM_TRY_INSPECT(const auto& dbFileUrl
,
16310 GetDatabaseFileURL(aDatabaseFile
, -1, maybeKey
), QM_VOID
);
16312 QM_TRY_UNWRAP(const NotNull
<nsCOMPtr
<mozIStorageConnection
>> connection
,
16313 OpenDatabaseAndHandleBusy(*ss
, *dbFileUrl
), QM_VOID
);
16317 QM_TRY_INSPECT(const auto& stmt
,
16318 CreateAndExecuteSingleStepStatement
<
16319 SingleStepResult::ReturnNullIfNoResult
>(
16320 *connection
, "SELECT name FROM database"_ns
),
16323 QM_TRY(OkIf(stmt
), QM_VOID
);
16325 nsString databaseName
;
16326 rv
= stmt
->GetString(0, databaseName
);
16327 if (NS_WARN_IF(NS_FAILED(rv
))) {
16331 MOZ_ASSERT(mCommonParams
.metadata().name() == databaseName
);
16335 QM_TRY_INSPECT(const auto& stmt
,
16336 CreateAndExecuteSingleStepStatement
<
16337 SingleStepResult::ReturnNullIfNoResult
>(
16338 *connection
, "SELECT version FROM database"_ns
),
16341 QM_TRY(OkIf(stmt
), QM_VOID
);
16344 rv
= stmt
->GetInt64(0, &version
);
16345 if (NS_WARN_IF(NS_FAILED(rv
))) {
16349 mPreviousVersion
= uint64_t(version
);
16352 nsresult
DeleteDatabaseOp::DatabaseOpen() {
16353 AssertIsOnOwningThread();
16354 MOZ_ASSERT(mState
== State::DatabaseOpenPending
);
16356 nsresult rv
= SendToIOThread();
16357 if (NS_WARN_IF(NS_FAILED(rv
))) {
16364 nsresult
DeleteDatabaseOp::DoDatabaseWork() {
16365 AssertIsOnIOThread();
16366 MOZ_ASSERT(mState
== State::DatabaseWorkOpen
);
16368 AUTO_PROFILER_LABEL("DeleteDatabaseOp::DoDatabaseWork", DOM
);
16370 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
16371 !OperationMayProceed()) {
16372 IDB_REPORT_INTERNAL_ERR();
16373 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16376 const nsAString
& databaseName
= mCommonParams
.metadata().name();
16377 const PersistenceType persistenceType
=
16378 mCommonParams
.metadata().persistenceType();
16380 QuotaManager
* const quotaManager
= QuotaManager::Get();
16381 MOZ_ASSERT(quotaManager
);
16383 QM_TRY_UNWRAP(auto directory
, quotaManager
->GetDirectoryForOrigin(
16384 persistenceType
, mOriginMetadata
.mOrigin
));
16386 QM_TRY(MOZ_TO_RESULT(
16387 directory
->Append(NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME
))));
16389 QM_TRY_UNWRAP(mDatabaseDirectoryPath
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
16390 nsString
, directory
, GetPath
));
16392 mDatabaseFilenameBase
= GetDatabaseFilenameBase(databaseName
);
16395 const auto& dbFile
,
16396 CloneFileAndAppend(*directory
, mDatabaseFilenameBase
+ kSQLiteSuffix
));
16401 const auto& databaseFilePath
,
16402 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString
, dbFile
, GetPath
));
16404 MOZ_ASSERT(databaseFilePath
== mDatabaseFilePath
);
16408 QM_TRY_INSPECT(const bool& exists
,
16409 MOZ_TO_RESULT_INVOKE_MEMBER(dbFile
, Exists
));
16412 // Parts of this function may fail but that shouldn't prevent us from
16413 // deleting the file eventually.
16414 LoadPreviousVersion(*dbFile
);
16416 mState
= State::BeginVersionChange
;
16418 mState
= State::SendingResults
;
16421 QM_TRY(MOZ_TO_RESULT(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
)));
16426 nsresult
DeleteDatabaseOp::BeginVersionChange() {
16427 AssertIsOnOwningThread();
16428 MOZ_ASSERT(mState
== State::BeginVersionChange
);
16429 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
16431 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
16432 IsActorDestroyed()) {
16433 IDB_REPORT_INTERNAL_ERR();
16434 QM_TRY(MOZ_TO_RESULT(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
));
16437 DatabaseActorInfo
* info
;
16438 if (gLiveDatabaseHashtable
->Get(mDatabaseId
, &info
)) {
16439 MOZ_ASSERT(!info
->mWaitingFactoryOp
);
16442 SendVersionChangeMessages(info
, Nothing(), mPreviousVersion
, Nothing());
16443 if (NS_WARN_IF(NS_FAILED(rv
))) {
16447 if (!mMaybeBlockedDatabases
.IsEmpty()) {
16448 // If the actor gets destroyed, mWaitingFactoryOp will hold the last
16449 // strong reference to us.
16450 info
->mWaitingFactoryOp
= this;
16452 mState
= State::WaitingForOtherDatabasesToClose
;
16457 // No other databases need to be notified, just make sure that all
16458 // transactions are complete.
16459 WaitForTransactions();
16463 bool DeleteDatabaseOp::AreActorsAlive() {
16464 AssertIsOnOwningThread();
16466 return !IsActorDestroyed();
16469 nsresult
DeleteDatabaseOp::DispatchToWorkThread() {
16470 AssertIsOnOwningThread();
16471 MOZ_ASSERT(mState
== State::WaitingForTransactionsToComplete
);
16472 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
16474 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
16475 IsActorDestroyed()) {
16476 IDB_REPORT_INTERNAL_ERR();
16477 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16480 mState
= State::DatabaseWorkVersionChange
;
16482 RefPtr
<VersionChangeOp
> versionChangeOp
= new VersionChangeOp(this);
16484 QuotaManager
* const quotaManager
= QuotaManager::Get();
16485 MOZ_ASSERT(quotaManager
);
16487 nsresult rv
= quotaManager
->IOThread()->Dispatch(versionChangeOp
.forget(),
16488 NS_DISPATCH_NORMAL
);
16489 if (NS_WARN_IF(NS_FAILED(rv
))) {
16490 IDB_REPORT_INTERNAL_ERR();
16491 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16497 void DeleteDatabaseOp::SendBlockedNotification() {
16498 AssertIsOnOwningThread();
16499 MOZ_ASSERT(mState
== State::WaitingForOtherDatabasesToClose
);
16501 if (!IsActorDestroyed()) {
16502 Unused
<< SendBlocked(mPreviousVersion
);
16506 void DeleteDatabaseOp::SendResults() {
16507 AssertIsOnOwningThread();
16508 MOZ_ASSERT(mState
== State::SendingResults
);
16509 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
16511 DebugOnly
<DatabaseActorInfo
*> info
= nullptr;
16513 gLiveDatabaseHashtable
&& gLiveDatabaseHashtable
->Get(mDatabaseId
, &info
),
16514 !info
->mWaitingFactoryOp
);
16516 if (!IsActorDestroyed()) {
16517 FactoryRequestResponse response
;
16519 if (!HasFailed()) {
16520 response
= DeleteDatabaseRequestResponse(mPreviousVersion
);
16522 response
= ClampResultCode(ResultCode());
16525 Unused
<< PBackgroundIDBFactoryRequestParent::Send__delete__(this,
16529 mDirectoryLock
= nullptr;
16533 FinishSendResults();
16536 nsresult
DeleteDatabaseOp::VersionChangeOp::RunOnIOThread() {
16537 AssertIsOnIOThread();
16538 MOZ_ASSERT(mDeleteDatabaseOp
->mState
== State::DatabaseWorkVersionChange
);
16540 AUTO_PROFILER_LABEL("DeleteDatabaseOp::VersionChangeOp::RunOnIOThread", DOM
);
16542 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
16543 !OperationMayProceed()) {
16544 IDB_REPORT_INTERNAL_ERR();
16545 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16548 const PersistenceType
& persistenceType
=
16549 mDeleteDatabaseOp
->mCommonParams
.metadata().persistenceType();
16551 QuotaManager
* quotaManager
=
16552 mDeleteDatabaseOp
->mEnforcingQuota
? QuotaManager::Get() : nullptr;
16554 MOZ_ASSERT_IF(mDeleteDatabaseOp
->mEnforcingQuota
, quotaManager
);
16556 nsCOMPtr
<nsIFile
> directory
=
16557 GetFileForPath(mDeleteDatabaseOp
->mDatabaseDirectoryPath
);
16558 if (NS_WARN_IF(!directory
)) {
16559 IDB_REPORT_INTERNAL_ERR();
16560 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16563 nsresult rv
= RemoveDatabaseFilesAndDirectory(
16564 *directory
, mDeleteDatabaseOp
->mDatabaseFilenameBase
, quotaManager
,
16565 persistenceType
, mDeleteDatabaseOp
->mOriginMetadata
,
16566 mDeleteDatabaseOp
->mCommonParams
.metadata().name());
16567 if (NS_WARN_IF(NS_FAILED(rv
))) {
16571 if (mDeleteDatabaseOp
->mInPrivateBrowsing
) {
16572 DebugOnly
<bool> ok
=
16573 gIndexedDBCipherKeyManager
->Remove(mDeleteDatabaseOp
->mDatabaseId
);
16577 rv
= mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
);
16578 if (NS_WARN_IF(NS_FAILED(rv
))) {
16585 void DeleteDatabaseOp::VersionChangeOp::RunOnOwningThread() {
16586 AssertIsOnOwningThread();
16587 MOZ_ASSERT(mDeleteDatabaseOp
->mState
== State::DatabaseWorkVersionChange
);
16589 const RefPtr
<DeleteDatabaseOp
> deleteOp
= std::move(mDeleteDatabaseOp
);
16591 if (deleteOp
->IsActorDestroyed()) {
16592 IDB_REPORT_INTERNAL_ERR();
16593 deleteOp
->SetFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
16594 } else if (HasFailed()) {
16595 deleteOp
->SetFailureCodeIfUnset(ResultCode());
16597 DatabaseActorInfo
* info
;
16599 // Inform all the other databases that they are now invalidated. That
16600 // should remove the previous metadata from our table.
16601 if (gLiveDatabaseHashtable
->Get(deleteOp
->mDatabaseId
, &info
)) {
16602 MOZ_ASSERT(!info
->mLiveDatabases
.IsEmpty());
16603 MOZ_ASSERT(!info
->mWaitingFactoryOp
);
16605 nsTArray
<SafeRefPtr
<Database
>> liveDatabases
;
16606 if (NS_WARN_IF(!liveDatabases
.SetCapacity(info
->mLiveDatabases
.Length(),
16608 deleteOp
->SetFailureCode(NS_ERROR_OUT_OF_MEMORY
);
16610 std::transform(info
->mLiveDatabases
.cbegin(),
16611 info
->mLiveDatabases
.cend(),
16612 MakeBackInserter(liveDatabases
),
16613 [](const auto& aDatabase
) -> SafeRefPtr
<Database
> {
16614 return {aDatabase
.get(), AcquireStrongRefFromRawPtr
{}};
16618 // The code below should result in the deletion of |info|. Set to null
16619 // here to make sure we find invalid uses later.
16623 for (const auto& database
: liveDatabases
) {
16624 database
->Invalidate();
16627 MOZ_ASSERT(!gLiveDatabaseHashtable
->Get(deleteOp
->mDatabaseId
));
16632 // We hold a strong ref to the deleteOp, so it's safe to call Run() directly.
16634 deleteOp
->mState
= State::SendingResults
;
16635 MOZ_ALWAYS_SUCCEEDS(deleteOp
->Run());
16638 // A bit hacky but the DeleteDatabaseOp::VersionChangeOp is not really a
16639 // normal database operation that is tied to an actor. Do this to make our
16640 // assertions happy.
16641 NoteActorDestroyed();
16645 nsresult
DeleteDatabaseOp::VersionChangeOp::Run() {
16648 if (IsOnIOThread()) {
16649 rv
= RunOnIOThread();
16651 RunOnOwningThread();
16655 if (NS_WARN_IF(NS_FAILED(rv
))) {
16656 SetFailureCodeIfUnset(rv
);
16658 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
));
16664 TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
16665 SafeRefPtr
<TransactionBase
> aTransaction
)
16666 : DatabaseOperationBase(aTransaction
->GetLoggingInfo()->Id(),
16667 aTransaction
->GetLoggingInfo()->NextRequestSN()),
16668 mTransaction(WrapNotNull(std::move(aTransaction
))),
16669 mTransactionIsAborted((*mTransaction
)->IsAborted()),
16670 mTransactionLoggingSerialNumber((*mTransaction
)->LoggingSerialNumber()) {
16671 MOZ_ASSERT(LoggingSerialNumber());
16674 TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
16675 SafeRefPtr
<TransactionBase
> aTransaction
, uint64_t aLoggingSerialNumber
)
16676 : DatabaseOperationBase(aTransaction
->GetLoggingInfo()->Id(),
16677 aLoggingSerialNumber
),
16678 mTransaction(WrapNotNull(std::move(aTransaction
))),
16679 mTransactionIsAborted((*mTransaction
)->IsAborted()),
16680 mTransactionLoggingSerialNumber((*mTransaction
)->LoggingSerialNumber()) {}
16682 TransactionDatabaseOperationBase::~TransactionDatabaseOperationBase() {
16683 MOZ_ASSERT(mInternalState
== InternalState::Completed
);
16684 MOZ_ASSERT(!mTransaction
,
16685 "TransactionDatabaseOperationBase::Cleanup() was not called by a "
16691 void TransactionDatabaseOperationBase::AssertIsOnConnectionThread() const {
16692 (*mTransaction
)->AssertIsOnConnectionThread();
16697 uint64_t TransactionDatabaseOperationBase::StartOnConnectionPool(
16698 const nsID
& aBackgroundChildLoggingId
, const nsACString
& aDatabaseId
,
16699 int64_t aLoggingSerialNumber
, const nsTArray
<nsString
>& aObjectStoreNames
,
16700 bool aIsWriteTransaction
) {
16701 AssertIsOnOwningThread();
16702 MOZ_ASSERT(mInternalState
== InternalState::Initial
);
16704 // Must set mInternalState before dispatching otherwise we will race with the
16705 // connection thread.
16706 mInternalState
= InternalState::DatabaseWork
;
16708 return gConnectionPool
->Start(aBackgroundChildLoggingId
, aDatabaseId
,
16709 aLoggingSerialNumber
, aObjectStoreNames
,
16710 aIsWriteTransaction
, this);
16713 void TransactionDatabaseOperationBase::DispatchToConnectionPool() {
16714 AssertIsOnOwningThread();
16715 MOZ_ASSERT(mInternalState
== InternalState::Initial
);
16717 Unused
<< this->Run();
16720 void TransactionDatabaseOperationBase::RunOnConnectionThread() {
16721 MOZ_ASSERT(!IsOnBackgroundThread());
16722 MOZ_ASSERT(mInternalState
== InternalState::DatabaseWork
);
16723 MOZ_ASSERT(!HasFailed());
16725 AUTO_PROFILER_LABEL("TransactionDatabaseOperationBase::RunOnConnectionThread",
16728 // There are several cases where we don't actually have to to any work here.
16730 if (mTransactionIsAborted
|| (*mTransaction
)->IsInvalidatedOnAnyThread()) {
16731 // This transaction is already set to be aborted or invalidated.
16732 SetFailureCode(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
);
16733 } else if (!OperationMayProceed()) {
16734 // The operation was canceled in some way, likely because the child process
16736 IDB_REPORT_INTERNAL_ERR();
16737 OverrideFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
16739 Database
& database
= (*mTransaction
)->GetMutableDatabase();
16741 // Here we're actually going to perform the database operation.
16742 nsresult rv
= database
.EnsureConnection();
16743 if (NS_WARN_IF(NS_FAILED(rv
))) {
16744 SetFailureCode(rv
);
16746 DatabaseConnection
* connection
= database
.GetConnection();
16747 MOZ_ASSERT(connection
);
16749 auto& storageConnection
= connection
->MutableStorageConnection();
16751 AutoSetProgressHandler autoProgress
;
16752 if (mLoggingSerialNumber
) {
16753 rv
= autoProgress
.Register(storageConnection
, this);
16754 if (NS_WARN_IF(NS_FAILED(rv
))) {
16755 SetFailureCode(rv
);
16759 if (NS_SUCCEEDED(rv
)) {
16760 if (mLoggingSerialNumber
) {
16761 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
16762 "Beginning database work", "DB Start",
16763 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
16764 mTransactionLoggingSerialNumber
, mLoggingSerialNumber
);
16767 rv
= DoDatabaseWork(connection
);
16769 if (mLoggingSerialNumber
) {
16770 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
16771 "Finished database work", "DB End",
16772 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
16773 mTransactionLoggingSerialNumber
, mLoggingSerialNumber
);
16776 if (NS_FAILED(rv
)) {
16777 SetFailureCode(rv
);
16783 // Must set mInternalState before dispatching otherwise we will race with the
16785 if (HasPreprocessInfo()) {
16786 mInternalState
= InternalState::SendingPreprocess
;
16788 mInternalState
= InternalState::SendingResults
;
16791 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
));
16794 bool TransactionDatabaseOperationBase::HasPreprocessInfo() { return false; }
16796 nsresult
TransactionDatabaseOperationBase::SendPreprocessInfo() {
16800 void TransactionDatabaseOperationBase::NoteContinueReceived() {
16801 AssertIsOnOwningThread();
16802 MOZ_ASSERT(mInternalState
== InternalState::WaitingForContinue
);
16804 mWaitingForContinue
= false;
16806 mInternalState
= InternalState::SendingResults
;
16808 // This TransactionDatabaseOperationBase can only be held alive by the IPDL.
16809 // Run() can end up with clearing that last reference. So we need to add
16810 // a self reference here.
16811 RefPtr
<TransactionDatabaseOperationBase
> kungFuDeathGrip
= this;
16813 Unused
<< this->Run();
16816 void TransactionDatabaseOperationBase::SendToConnectionPool() {
16817 AssertIsOnOwningThread();
16818 MOZ_ASSERT(mInternalState
== InternalState::Initial
);
16820 // Must set mInternalState before dispatching otherwise we will race with the
16821 // connection thread.
16822 mInternalState
= InternalState::DatabaseWork
;
16824 gConnectionPool
->Dispatch((*mTransaction
)->TransactionId(), this);
16826 (*mTransaction
)->NoteActiveRequest();
16829 void TransactionDatabaseOperationBase::SendPreprocess() {
16830 AssertIsOnOwningThread();
16831 MOZ_ASSERT(mInternalState
== InternalState::SendingPreprocess
);
16833 SendPreprocessInfoOrResults(/* aSendPreprocessInfo */ true);
16836 void TransactionDatabaseOperationBase::SendResults() {
16837 AssertIsOnOwningThread();
16838 MOZ_ASSERT(mInternalState
== InternalState::SendingResults
);
16840 SendPreprocessInfoOrResults(/* aSendPreprocessInfo */ false);
16843 void TransactionDatabaseOperationBase::SendPreprocessInfoOrResults(
16844 bool aSendPreprocessInfo
) {
16845 AssertIsOnOwningThread();
16846 MOZ_ASSERT(mInternalState
== InternalState::SendingPreprocess
||
16847 mInternalState
== InternalState::SendingResults
);
16849 // The flag is raised only when there is no mUpdateRefcountFunction for the
16850 // executing operation. It assume that is because the previous
16851 // StartTransactionOp was failed to begin a write transaction and it reported
16852 // when this operation has already jumped to the Connection thread.
16853 MOZ_DIAGNOSTIC_ASSERT_IF(mAssumingPreviousOperationFail
,
16854 (*mTransaction
)->IsAborted());
16856 if (NS_WARN_IF(IsActorDestroyed())) {
16857 // Normally we wouldn't need to send any notifications if the actor was
16858 // already destroyed, but this can be a VersionChangeOp which needs to
16859 // notify its parent operation (OpenDatabaseOp) about the failure.
16860 // So SendFailureResult needs to be called even when the actor was
16861 // destroyed. Normal operations redundantly check if the actor was
16862 // destroyed in SendSuccessResult and SendFailureResult, therefore it's
16863 // ok to call it in all cases here.
16864 if (!HasFailed()) {
16865 IDB_REPORT_INTERNAL_ERR();
16866 SetFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
16868 } else if ((*mTransaction
)->IsInvalidated() || (*mTransaction
)->IsAborted()) {
16869 // Aborted transactions always see their requests fail with ABORT_ERR,
16870 // even if the request succeeded or failed with another error.
16871 OverrideFailureCode(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
);
16874 const nsresult rv
= [aSendPreprocessInfo
, this] {
16876 return ResultCode();
16878 if (aSendPreprocessInfo
) {
16879 // This should not release the IPDL reference.
16880 return SendPreprocessInfo();
16882 // This may release the IPDL reference.
16883 return SendSuccessResult();
16886 if (NS_FAILED(rv
)) {
16887 SetFailureCodeIfUnset(rv
);
16889 // This should definitely release the IPDL reference.
16890 if (!SendFailureResult(rv
)) {
16891 // Abort the transaction.
16892 (*mTransaction
)->Abort(rv
, /* aForce */ false);
16896 if (aSendPreprocessInfo
&& !HasFailed()) {
16897 mInternalState
= InternalState::WaitingForContinue
;
16899 mWaitingForContinue
= true;
16901 if (mLoggingSerialNumber
) {
16902 (*mTransaction
)->NoteFinishedRequest(mLoggingSerialNumber
, ResultCode());
16907 mInternalState
= InternalState::Completed
;
16911 bool TransactionDatabaseOperationBase::Init(TransactionBase
& aTransaction
) {
16912 AssertIsOnBackgroundThread();
16913 MOZ_ASSERT(mInternalState
== InternalState::Initial
);
16918 void TransactionDatabaseOperationBase::Cleanup() {
16919 AssertIsOnOwningThread();
16920 MOZ_ASSERT(mInternalState
== InternalState::SendingResults
);
16922 mTransaction
.destroy();
16926 TransactionDatabaseOperationBase::Run() {
16927 switch (mInternalState
) {
16928 case InternalState::Initial
:
16929 SendToConnectionPool();
16932 case InternalState::DatabaseWork
:
16933 RunOnConnectionThread();
16936 case InternalState::SendingPreprocess
:
16940 case InternalState::SendingResults
:
16945 MOZ_CRASH("Bad state!");
16949 TransactionBase::CommitOp::CommitOp(SafeRefPtr
<TransactionBase
> aTransaction
,
16950 nsresult aResultCode
)
16951 : DatabaseOperationBase(aTransaction
->GetLoggingInfo()->Id(),
16952 aTransaction
->GetLoggingInfo()->NextRequestSN()),
16953 mTransaction(std::move(aTransaction
)),
16954 mResultCode(aResultCode
) {
16955 MOZ_ASSERT(mTransaction
);
16956 MOZ_ASSERT(LoggingSerialNumber());
16959 nsresult
TransactionBase::CommitOp::WriteAutoIncrementCounts() {
16960 MOZ_ASSERT(mTransaction
);
16961 mTransaction
->AssertIsOnConnectionThread();
16962 MOZ_ASSERT(mTransaction
->GetMode() == IDBTransaction::Mode::ReadWrite
||
16963 mTransaction
->GetMode() == IDBTransaction::Mode::ReadWriteFlush
||
16964 mTransaction
->GetMode() == IDBTransaction::Mode::Cleanup
||
16965 mTransaction
->GetMode() == IDBTransaction::Mode::VersionChange
);
16967 const nsTArray
<SafeRefPtr
<FullObjectStoreMetadata
>>& metadataArray
=
16968 mTransaction
->mModifiedAutoIncrementObjectStoreMetadataArray
;
16970 if (!metadataArray
.IsEmpty()) {
16971 DatabaseConnection
* connection
=
16972 mTransaction
->GetDatabase().GetConnection();
16973 MOZ_ASSERT(connection
);
16975 // The parameter names are not used, parameters are bound by index only
16976 // locally in the same function.
16977 auto stmt
= DatabaseConnection::LazyStatement(
16979 "UPDATE object_store "
16980 "SET auto_increment = :auto_increment WHERE id "
16981 "= :object_store_id;"_ns
);
16983 for (const auto& metadata
: metadataArray
) {
16984 MOZ_ASSERT(!metadata
->mDeleted
);
16986 const int64_t nextAutoIncrementId
= [&metadata
] {
16987 const auto&& lockedAutoIncrementIds
=
16988 metadata
->mAutoIncrementIds
.Lock();
16989 return lockedAutoIncrementIds
->next
;
16992 MOZ_ASSERT(nextAutoIncrementId
> 1);
16994 QM_TRY_INSPECT(const auto& borrowedStmt
, stmt
.Borrow());
16996 QM_TRY(MOZ_TO_RESULT(
16997 borrowedStmt
->BindInt64ByIndex(1, metadata
->mCommonMetadata
.id())));
16999 QM_TRY(MOZ_TO_RESULT(
17000 borrowedStmt
->BindInt64ByIndex(0, nextAutoIncrementId
)));
17002 QM_TRY(MOZ_TO_RESULT(borrowedStmt
->Execute()));
17009 void TransactionBase::CommitOp::CommitOrRollbackAutoIncrementCounts() {
17010 MOZ_ASSERT(mTransaction
);
17011 mTransaction
->AssertIsOnConnectionThread();
17012 MOZ_ASSERT(mTransaction
->GetMode() == IDBTransaction::Mode::ReadWrite
||
17013 mTransaction
->GetMode() == IDBTransaction::Mode::ReadWriteFlush
||
17014 mTransaction
->GetMode() == IDBTransaction::Mode::Cleanup
||
17015 mTransaction
->GetMode() == IDBTransaction::Mode::VersionChange
);
17017 const auto& metadataArray
=
17018 mTransaction
->mModifiedAutoIncrementObjectStoreMetadataArray
;
17020 if (!metadataArray
.IsEmpty()) {
17021 bool committed
= NS_SUCCEEDED(mResultCode
);
17023 for (const auto& metadata
: metadataArray
) {
17024 auto&& lockedAutoIncrementIds
= metadata
->mAutoIncrementIds
.Lock();
17027 lockedAutoIncrementIds
->committed
= lockedAutoIncrementIds
->next
;
17029 lockedAutoIncrementIds
->next
= lockedAutoIncrementIds
->committed
;
17037 void TransactionBase::CommitOp::AssertForeignKeyConsistency(
17038 DatabaseConnection
* aConnection
) {
17039 MOZ_ASSERT(aConnection
);
17040 MOZ_ASSERT(mTransaction
);
17041 mTransaction
->AssertIsOnConnectionThread();
17042 MOZ_ASSERT(mTransaction
->GetMode() != IDBTransaction::Mode::ReadOnly
);
17046 const auto& pragmaStmt
,
17047 CreateAndExecuteSingleStepStatement(
17048 aConnection
->MutableStorageConnection(), "PRAGMA foreign_keys;"_ns
),
17049 QM_ASSERT_UNREACHABLE_VOID
);
17051 int32_t foreignKeysEnabled
;
17052 MOZ_ALWAYS_SUCCEEDS(pragmaStmt
->GetInt32(0, &foreignKeysEnabled
));
17054 MOZ_ASSERT(foreignKeysEnabled
,
17055 "Database doesn't have foreign keys enabled!");
17059 QM_TRY_INSPECT(const bool& foreignKeyError
,
17060 CreateAndExecuteSingleStepStatement
<
17061 SingleStepResult::ReturnNullIfNoResult
>(
17062 aConnection
->MutableStorageConnection(),
17063 "PRAGMA foreign_key_check;"_ns
),
17064 QM_ASSERT_UNREACHABLE_VOID
);
17066 MOZ_ASSERT(!foreignKeyError
, "Database has inconsisistent foreign keys!");
17072 NS_IMPL_ISUPPORTS_INHERITED0(TransactionBase::CommitOp
, DatabaseOperationBase
)
17075 TransactionBase::CommitOp::Run() {
17076 MOZ_ASSERT(mTransaction
);
17077 mTransaction
->AssertIsOnConnectionThread();
17079 AUTO_PROFILER_LABEL("TransactionBase::CommitOp::Run", DOM
);
17081 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
17082 "Beginning database work", "DB Start",
17083 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
17084 mTransaction
->LoggingSerialNumber(), mLoggingSerialNumber
);
17086 if (mTransaction
->GetMode() != IDBTransaction::Mode::ReadOnly
&&
17087 mTransaction
->mHasBeenActiveOnConnectionThread
) {
17088 if (DatabaseConnection
* connection
=
17089 mTransaction
->GetDatabase().GetConnection()) {
17090 // May be null if the VersionChangeOp was canceled.
17091 DatabaseConnection::UpdateRefcountFunction
* fileRefcountFunction
=
17092 connection
->GetUpdateRefcountFunction();
17094 if (NS_SUCCEEDED(mResultCode
)) {
17095 if (fileRefcountFunction
) {
17096 mResultCode
= fileRefcountFunction
->WillCommit();
17097 NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode
),
17098 "WillCommit() failed!");
17101 if (NS_SUCCEEDED(mResultCode
)) {
17102 mResultCode
= WriteAutoIncrementCounts();
17103 NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode
),
17104 "WriteAutoIncrementCounts() failed!");
17106 if (NS_SUCCEEDED(mResultCode
)) {
17107 AssertForeignKeyConsistency(connection
);
17109 mResultCode
= connection
->CommitWriteTransaction();
17110 NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode
), "Commit failed!");
17112 if (NS_SUCCEEDED(mResultCode
) &&
17113 mTransaction
->GetMode() ==
17114 IDBTransaction::Mode::ReadWriteFlush
) {
17115 mResultCode
= connection
->Checkpoint();
17118 if (NS_SUCCEEDED(mResultCode
) && fileRefcountFunction
) {
17119 fileRefcountFunction
->DidCommit();
17125 if (NS_FAILED(mResultCode
)) {
17126 if (fileRefcountFunction
) {
17127 fileRefcountFunction
->DidAbort();
17130 connection
->RollbackWriteTransaction();
17133 CommitOrRollbackAutoIncrementCounts();
17135 connection
->FinishWriteTransaction();
17137 if (mTransaction
->GetMode() == IDBTransaction::Mode::Cleanup
) {
17138 connection
->DoIdleProcessing(/* aNeedsCheckpoint */ true);
17140 connection
->EnableQuotaChecks();
17145 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
17146 "Finished database work", "DB End",
17147 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
17148 mTransaction
->LoggingSerialNumber(), mLoggingSerialNumber
);
17150 IDB_LOG_MARK_PARENT_TRANSACTION("Finished database work", "DB End",
17151 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
17152 mTransaction
->LoggingSerialNumber());
17157 void TransactionBase::CommitOp::TransactionFinishedBeforeUnblock() {
17158 AssertIsOnBackgroundThread();
17159 MOZ_ASSERT(mTransaction
);
17161 AUTO_PROFILER_LABEL("CommitOp::TransactionFinishedBeforeUnblock", DOM
);
17163 if (!IsActorDestroyed()) {
17164 mTransaction
->UpdateMetadata(mResultCode
);
17168 void TransactionBase::CommitOp::TransactionFinishedAfterUnblock() {
17169 AssertIsOnBackgroundThread();
17170 MOZ_ASSERT(mTransaction
);
17172 IDB_LOG_MARK_PARENT_TRANSACTION(
17173 "Finished with result 0x%" PRIx32
, "Transaction finished (0x%" PRIx32
")",
17174 IDB_LOG_ID_STRING(mTransaction
->GetLoggingInfo()->Id()),
17175 mTransaction
->LoggingSerialNumber(), static_cast<uint32_t>(mResultCode
));
17177 mTransaction
->SendCompleteNotification(ClampResultCode(mResultCode
));
17179 mTransaction
->GetMutableDatabase().UnregisterTransaction(*mTransaction
);
17181 mTransaction
= nullptr;
17184 // A bit hacky but the CommitOp is not really a normal database operation
17185 // that is tied to an actor. Do this to make our assertions happy.
17186 NoteActorDestroyed();
17190 nsresult
VersionChangeTransactionOp::SendSuccessResult() {
17191 AssertIsOnOwningThread();
17193 // Nothing to send here, the API assumes that this request always succeeds.
17197 bool VersionChangeTransactionOp::SendFailureResult(nsresult aResultCode
) {
17198 AssertIsOnOwningThread();
17200 // The only option here is to cause the transaction to abort.
17204 void VersionChangeTransactionOp::Cleanup() {
17205 AssertIsOnOwningThread();
17208 // A bit hacky but the VersionChangeTransactionOp is not generated in response
17209 // to a child request like most other database operations. Do this to make our
17210 // assertions happy.
17211 NoteActorDestroyed();
17214 TransactionDatabaseOperationBase::Cleanup();
17217 nsresult
CreateObjectStoreOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
17218 MOZ_ASSERT(aConnection
);
17219 aConnection
->AssertIsOnConnectionThread();
17221 AUTO_PROFILER_LABEL("CreateObjectStoreOp::DoDatabaseWork", DOM
);
17225 // Make sure that we're not creating an object store with the same name as
17226 // another that already exists. This should be impossible because we should
17227 // have thrown an error long before now...
17228 // The parameter names are not used, parameters are bound by index only
17229 // locally in the same function.
17230 QM_TRY_INSPECT(const bool& hasResult
,
17232 ->BorrowAndExecuteSingleStepStatement(
17234 "FROM object_store "
17235 "WHERE name = :name;"_ns
,
17236 [&self
= *this](auto& stmt
) -> Result
<Ok
, nsresult
> {
17237 QM_TRY(MOZ_TO_RESULT(stmt
.BindStringByIndex(
17238 0, self
.mMetadata
.name())));
17242 QM_ASSERT_UNREACHABLE
);
17244 MOZ_ASSERT(!hasResult
);
17248 DatabaseConnection::AutoSavepoint autoSave
;
17249 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
17250 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
17252 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
17256 // The parameter names are not used, parameters are bound by index only
17257 // locally in the same function.
17258 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17259 "INSERT INTO object_store (id, auto_increment, name, key_path) "
17260 "VALUES (:id, :auto_increment, :name, :key_path);"_ns
,
17262 mMetadata
](mozIStorageStatement
& stmt
) -> Result
<Ok
, nsresult
> {
17263 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(0, metadata
.id())));
17265 QM_TRY(MOZ_TO_RESULT(
17266 stmt
.BindInt32ByIndex(1, metadata
.autoIncrement() ? 1 : 0)));
17268 QM_TRY(MOZ_TO_RESULT(stmt
.BindStringByIndex(2, metadata
.name())));
17270 if (metadata
.keyPath().IsValid()) {
17271 QM_TRY(MOZ_TO_RESULT(stmt
.BindStringByIndex(
17272 3, metadata
.keyPath().SerializeToString())));
17274 QM_TRY(MOZ_TO_RESULT(stmt
.BindNullByIndex(3)));
17283 MOZ_ALWAYS_SUCCEEDS(
17284 aConnection
->MutableStorageConnection().GetLastInsertRowID(&id
));
17285 MOZ_ASSERT(mMetadata
.id() == id
);
17289 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
17294 nsresult
DeleteObjectStoreOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
17295 MOZ_ASSERT(aConnection
);
17296 aConnection
->AssertIsOnConnectionThread();
17298 AUTO_PROFILER_LABEL("DeleteObjectStoreOp::DoDatabaseWork", DOM
);
17302 // Make sure |mIsLastObjectStore| is telling the truth.
17305 aConnection
->BorrowCachedStatement("SELECT id FROM object_store;"_ns
),
17306 QM_ASSERT_UNREACHABLE
);
17308 bool foundThisObjectStore
= false;
17309 bool foundOtherObjectStore
= false;
17313 MOZ_ALWAYS_SUCCEEDS(stmt
->ExecuteStep(&hasResult
));
17320 MOZ_ALWAYS_SUCCEEDS(stmt
->GetInt64(0, &id
));
17322 if (id
== mMetadata
->mCommonMetadata
.id()) {
17323 foundThisObjectStore
= true;
17325 foundOtherObjectStore
= true;
17329 MOZ_ASSERT_IF(mIsLastObjectStore
,
17330 foundThisObjectStore
&& !foundOtherObjectStore
);
17331 MOZ_ASSERT_IF(!mIsLastObjectStore
,
17332 foundThisObjectStore
&& foundOtherObjectStore
);
17336 DatabaseConnection::AutoSavepoint autoSave
;
17337 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
17338 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
17340 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
17344 if (mIsLastObjectStore
) {
17345 // We can just delete everything if this is the last object store.
17346 QM_TRY(MOZ_TO_RESULT(
17347 aConnection
->ExecuteCachedStatement("DELETE FROM index_data;"_ns
)));
17349 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17350 "DELETE FROM unique_index_data;"_ns
)));
17352 QM_TRY(MOZ_TO_RESULT(
17353 aConnection
->ExecuteCachedStatement("DELETE FROM object_data;"_ns
)));
17355 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17356 "DELETE FROM object_store_index;"_ns
)));
17358 QM_TRY(MOZ_TO_RESULT(
17359 aConnection
->ExecuteCachedStatement("DELETE FROM object_store;"_ns
)));
17362 const bool& hasIndexes
,
17363 ObjectStoreHasIndexes(*aConnection
, mMetadata
->mCommonMetadata
.id()));
17365 const auto bindObjectStoreIdToFirstParameter
=
17366 [this](mozIStorageStatement
& stmt
) -> Result
<Ok
, nsresult
> {
17367 QM_TRY(MOZ_TO_RESULT(
17368 stmt
.BindInt64ByIndex(0, mMetadata
->mCommonMetadata
.id())));
17373 // The parameter name :object_store_id in the SQL statements below is not
17374 // used for binding, parameters are bound by index only locally by
17375 // bindObjectStoreIdToFirstParameter.
17377 QM_TRY(MOZ_TO_RESULT(DeleteObjectStoreDataTableRowsWithIndexes(
17378 aConnection
, mMetadata
->mCommonMetadata
.id(), Nothing())));
17380 // Now clean up the object store index table.
17381 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17382 "DELETE FROM object_store_index "
17383 "WHERE object_store_id = :object_store_id;"_ns
,
17384 bindObjectStoreIdToFirstParameter
)));
17386 // We only have to worry about object data if this object store has no
17388 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17389 "DELETE FROM object_data "
17390 "WHERE object_store_id = :object_store_id;"_ns
,
17391 bindObjectStoreIdToFirstParameter
)));
17394 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17395 "DELETE FROM object_store "
17396 "WHERE id = :object_store_id;"_ns
,
17397 bindObjectStoreIdToFirstParameter
)));
17401 int32_t deletedRowCount
;
17402 MOZ_ALWAYS_SUCCEEDS(
17403 aConnection
->MutableStorageConnection().GetAffectedRows(
17404 &deletedRowCount
));
17405 MOZ_ASSERT(deletedRowCount
== 1);
17410 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
17412 if (mMetadata
->mCommonMetadata
.autoIncrement()) {
17413 Transaction().ForgetModifiedAutoIncrementObjectStore(*mMetadata
);
17419 nsresult
RenameObjectStoreOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
17420 MOZ_ASSERT(aConnection
);
17421 aConnection
->AssertIsOnConnectionThread();
17423 AUTO_PROFILER_LABEL("RenameObjectStoreOp::DoDatabaseWork", DOM
);
17427 // Make sure that we're not renaming an object store with the same name as
17428 // another that already exists. This should be impossible because we should
17429 // have thrown an error long before now...
17430 // The parameter names are not used, parameters are bound by index only
17431 // locally in the same function.
17433 const bool& hasResult
,
17435 ->BorrowAndExecuteSingleStepStatement(
17437 "FROM object_store "
17438 "WHERE name = :name AND id != :id;"_ns
,
17439 [&self
= *this](auto& stmt
) -> Result
<Ok
, nsresult
> {
17441 MOZ_TO_RESULT(stmt
.BindStringByIndex(0, self
.mNewName
)));
17443 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(1, self
.mId
)));
17447 QM_ASSERT_UNREACHABLE
);
17449 MOZ_ASSERT(!hasResult
);
17453 DatabaseConnection::AutoSavepoint autoSave
;
17454 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
17455 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
17457 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
17461 // The parameter names are not used, parameters are bound by index only
17462 // locally in the same function.
17463 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17464 "UPDATE object_store "
17465 "SET name = :name "
17466 "WHERE id = :id;"_ns
,
17467 [&self
= *this](mozIStorageStatement
& stmt
) -> Result
<Ok
, nsresult
> {
17468 QM_TRY(MOZ_TO_RESULT(stmt
.BindStringByIndex(0, self
.mNewName
)));
17470 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(1, self
.mId
)));
17475 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
17480 CreateIndexOp::CreateIndexOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
17481 const IndexOrObjectStoreId aObjectStoreId
,
17482 const IndexMetadata
& aMetadata
)
17483 : VersionChangeTransactionOp(std::move(aTransaction
)),
17484 mMetadata(aMetadata
),
17485 mFileManager(Transaction().GetDatabase().GetFileManagerPtr()),
17486 mDatabaseId(Transaction().DatabaseId()),
17487 mObjectStoreId(aObjectStoreId
) {
17488 MOZ_ASSERT(aObjectStoreId
);
17489 MOZ_ASSERT(aMetadata
.id());
17490 MOZ_ASSERT(mFileManager
);
17491 MOZ_ASSERT(!mDatabaseId
.IsEmpty());
17494 nsresult
CreateIndexOp::InsertDataFromObjectStore(
17495 DatabaseConnection
* aConnection
) {
17496 MOZ_ASSERT(aConnection
);
17497 aConnection
->AssertIsOnConnectionThread();
17498 MOZ_ASSERT(mMaybeUniqueIndexTable
);
17500 AUTO_PROFILER_LABEL("CreateIndexOp::InsertDataFromObjectStore", DOM
);
17502 auto& storageConnection
= aConnection
->MutableStorageConnection();
17504 RefPtr
<UpdateIndexDataValuesFunction
> updateFunction
=
17505 new UpdateIndexDataValuesFunction(this, aConnection
,
17506 Transaction().GetDatabasePtr());
17508 constexpr auto updateFunctionName
= "update_index_data_values"_ns
;
17511 storageConnection
.CreateFunction(updateFunctionName
, 4, updateFunction
);
17512 if (NS_WARN_IF(NS_FAILED(rv
))) {
17516 rv
= InsertDataFromObjectStoreInternal(aConnection
);
17518 MOZ_ALWAYS_SUCCEEDS(storageConnection
.RemoveFunction(updateFunctionName
));
17520 if (NS_WARN_IF(NS_FAILED(rv
))) {
17527 nsresult
CreateIndexOp::InsertDataFromObjectStoreInternal(
17528 DatabaseConnection
* aConnection
) const {
17529 MOZ_ASSERT(aConnection
);
17530 aConnection
->AssertIsOnConnectionThread();
17531 MOZ_ASSERT(mMaybeUniqueIndexTable
);
17533 MOZ_ASSERT(aConnection
->HasStorageConnection());
17535 // The parameter names are not used, parameters are bound by index only
17536 // locally in the same function.
17537 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17538 "UPDATE object_data "
17539 "SET index_data_values = update_index_data_values "
17540 "(key, index_data_values, file_ids, data) "
17541 "WHERE object_store_id = :object_store_id;"_ns
,
17543 mObjectStoreId
](mozIStorageStatement
& stmt
) -> Result
<Ok
, nsresult
> {
17544 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(0, objectStoredId
)));
17552 bool CreateIndexOp::Init(TransactionBase
& aTransaction
) {
17553 AssertIsOnBackgroundThread();
17554 MOZ_ASSERT(mObjectStoreId
);
17555 MOZ_ASSERT(mMaybeUniqueIndexTable
.isNothing());
17557 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
17558 aTransaction
.GetMetadataForObjectStoreId(mObjectStoreId
);
17559 MOZ_ASSERT(objectStoreMetadata
);
17561 const uint32_t indexCount
= objectStoreMetadata
->mIndexes
.Count();
17566 auto uniqueIndexTable
= UniqueIndexTable
{indexCount
};
17568 for (const auto& value
: objectStoreMetadata
->mIndexes
.Values()) {
17569 MOZ_ASSERT(!uniqueIndexTable
.Contains(value
->mCommonMetadata
.id()));
17571 if (NS_WARN_IF(!uniqueIndexTable
.InsertOrUpdate(
17572 value
->mCommonMetadata
.id(), value
->mCommonMetadata
.unique(),
17574 IDB_REPORT_INTERNAL_ERR();
17575 NS_WARNING("out of memory");
17580 uniqueIndexTable
.MarkImmutable();
17582 mMaybeUniqueIndexTable
.emplace(std::move(uniqueIndexTable
));
17587 nsresult
CreateIndexOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
17588 MOZ_ASSERT(aConnection
);
17589 aConnection
->AssertIsOnConnectionThread();
17591 AUTO_PROFILER_LABEL("CreateIndexOp::DoDatabaseWork", DOM
);
17595 // Make sure that we're not creating an index with the same name and object
17596 // store as another that already exists. This should be impossible because
17597 // we should have thrown an error long before now...
17598 // The parameter names are not used, parameters are bound by index only
17599 // locally in the same function.
17601 const bool& hasResult
,
17603 ->BorrowAndExecuteSingleStepStatement(
17605 "FROM object_store_index "
17606 "WHERE object_store_id = :object_store_id AND name = :name;"_ns
,
17607 [&self
= *this](auto& stmt
) -> Result
<Ok
, nsresult
> {
17608 QM_TRY(MOZ_TO_RESULT(
17609 stmt
.BindInt64ByIndex(0, self
.mObjectStoreId
)));
17610 QM_TRY(MOZ_TO_RESULT(
17611 stmt
.BindStringByIndex(1, self
.mMetadata
.name())));
17615 QM_ASSERT_UNREACHABLE
);
17617 MOZ_ASSERT(!hasResult
);
17621 DatabaseConnection::AutoSavepoint autoSave
;
17622 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
17623 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
17625 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
17629 // The parameter names are not used, parameters are bound by index only
17630 // locally in the same function.
17631 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17632 "INSERT INTO object_store_index (id, name, key_path, unique_index, "
17633 "multientry, object_store_id, locale, "
17635 "VALUES (:id, :name, :key_path, :unique, :multientry, "
17636 ":object_store_id, :locale, :is_auto_locale)"_ns
,
17637 [&metadata
= mMetadata
, objectStoreId
= mObjectStoreId
](
17638 mozIStorageStatement
& stmt
) -> Result
<Ok
, nsresult
> {
17639 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(0, metadata
.id())));
17641 QM_TRY(MOZ_TO_RESULT(stmt
.BindStringByIndex(1, metadata
.name())));
17643 QM_TRY(MOZ_TO_RESULT(
17644 stmt
.BindStringByIndex(2, metadata
.keyPath().SerializeToString())));
17647 MOZ_TO_RESULT(stmt
.BindInt32ByIndex(3, metadata
.unique() ? 1 : 0)));
17649 QM_TRY(MOZ_TO_RESULT(
17650 stmt
.BindInt32ByIndex(4, metadata
.multiEntry() ? 1 : 0)));
17651 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(5, objectStoreId
)));
17653 QM_TRY(MOZ_TO_RESULT(
17654 metadata
.locale().IsEmpty()
17655 ? stmt
.BindNullByIndex(6)
17656 : stmt
.BindUTF8StringByIndex(6, metadata
.locale())));
17658 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt32ByIndex(7, metadata
.autoLocale())));
17666 MOZ_ALWAYS_SUCCEEDS(
17667 aConnection
->MutableStorageConnection().GetLastInsertRowID(&id
));
17668 MOZ_ASSERT(mMetadata
.id() == id
);
17672 QM_TRY(MOZ_TO_RESULT(InsertDataFromObjectStore(aConnection
)));
17674 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
17679 NS_IMPL_ISUPPORTS(CreateIndexOp::UpdateIndexDataValuesFunction
,
17680 mozIStorageFunction
);
17683 CreateIndexOp::UpdateIndexDataValuesFunction::OnFunctionCall(
17684 mozIStorageValueArray
* aValues
, nsIVariant
** _retval
) {
17685 MOZ_ASSERT(aValues
);
17686 MOZ_ASSERT(_retval
);
17687 MOZ_ASSERT(mConnection
);
17688 mConnection
->AssertIsOnConnectionThread();
17690 MOZ_ASSERT(mOp
->mFileManager
);
17692 AUTO_PROFILER_LABEL(
17693 "CreateIndexOp::UpdateIndexDataValuesFunction::OnFunctionCall", DOM
);
17698 MOZ_ALWAYS_SUCCEEDS(aValues
->GetNumEntries(&argCount
));
17699 MOZ_ASSERT(argCount
== 4); // key, index_data_values, file_ids, data
17702 MOZ_ALWAYS_SUCCEEDS(aValues
->GetTypeOfIndex(0, &valueType
));
17703 MOZ_ASSERT(valueType
== mozIStorageValueArray::VALUE_TYPE_BLOB
);
17705 MOZ_ALWAYS_SUCCEEDS(aValues
->GetTypeOfIndex(1, &valueType
));
17706 MOZ_ASSERT(valueType
== mozIStorageValueArray::VALUE_TYPE_NULL
||
17707 valueType
== mozIStorageValueArray::VALUE_TYPE_BLOB
);
17709 MOZ_ALWAYS_SUCCEEDS(aValues
->GetTypeOfIndex(2, &valueType
));
17710 MOZ_ASSERT(valueType
== mozIStorageValueArray::VALUE_TYPE_NULL
||
17711 valueType
== mozIStorageValueArray::VALUE_TYPE_TEXT
);
17713 MOZ_ALWAYS_SUCCEEDS(aValues
->GetTypeOfIndex(3, &valueType
));
17714 MOZ_ASSERT(valueType
== mozIStorageValueArray::VALUE_TYPE_BLOB
||
17715 valueType
== mozIStorageValueArray::VALUE_TYPE_INTEGER
);
17719 QM_TRY_UNWRAP(auto cloneInfo
, GetStructuredCloneReadInfoFromValueArray(
17721 /* aDataIndex */ 3,
17722 /* aFileIdsIndex */ 2, *mOp
->mFileManager
));
17724 const IndexMetadata
& metadata
= mOp
->mMetadata
;
17725 const IndexOrObjectStoreId
& objectStoreId
= mOp
->mObjectStoreId
;
17727 // XXX does this really need a non-const cloneInfo?
17728 QM_TRY_INSPECT(const auto& updateInfos
,
17729 DeserializeIndexValueToUpdateInfos(
17730 metadata
.id(), metadata
.keyPath(), metadata
.multiEntry(),
17731 metadata
.locale(), cloneInfo
));
17733 if (updateInfos
.IsEmpty()) {
17734 // XXX See if we can do this without copying...
17736 nsCOMPtr
<nsIVariant
> unmodifiedValue
;
17738 // No changes needed, just return the original value.
17739 QM_TRY_INSPECT(const int32_t& valueType
,
17740 MOZ_TO_RESULT_INVOKE_MEMBER(aValues
, GetTypeOfIndex
, 1));
17742 MOZ_ASSERT(valueType
== mozIStorageValueArray::VALUE_TYPE_NULL
||
17743 valueType
== mozIStorageValueArray::VALUE_TYPE_BLOB
);
17745 if (valueType
== mozIStorageValueArray::VALUE_TYPE_NULL
) {
17746 unmodifiedValue
= new storage::NullVariant();
17747 unmodifiedValue
.forget(_retval
);
17751 MOZ_ASSERT(valueType
== mozIStorageValueArray::VALUE_TYPE_BLOB
);
17753 const uint8_t* blobData
;
17754 uint32_t blobDataLength
;
17756 MOZ_TO_RESULT(aValues
->GetSharedBlob(1, &blobDataLength
, &blobData
)));
17758 const std::pair
<uint8_t*, int> copiedBlobDataPair(
17759 static_cast<uint8_t*>(malloc(blobDataLength
)), blobDataLength
);
17761 if (!copiedBlobDataPair
.first
) {
17762 IDB_REPORT_INTERNAL_ERR();
17763 return NS_ERROR_OUT_OF_MEMORY
;
17766 memcpy(copiedBlobDataPair
.first
, blobData
, blobDataLength
);
17768 unmodifiedValue
= new storage::AdoptedBlobVariant(copiedBlobDataPair
);
17769 unmodifiedValue
.forget(_retval
);
17775 QM_TRY(MOZ_TO_RESULT(key
.SetFromValueArray(aValues
, 0)));
17777 QM_TRY_UNWRAP(auto indexValues
, ReadCompressedIndexDataValues(*aValues
, 1));
17779 const bool hadPreviousIndexValues
= !indexValues
.IsEmpty();
17781 const uint32_t updateInfoCount
= updateInfos
.Length();
17783 QM_TRY(OkIf(indexValues
.SetCapacity(indexValues
.Length() + updateInfoCount
,
17785 NS_ERROR_OUT_OF_MEMORY
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
17787 // First construct the full list to update the index_data_values row.
17788 for (const IndexUpdateInfo
& info
: updateInfos
) {
17789 MOZ_ALWAYS_TRUE(indexValues
.InsertElementSorted(
17790 IndexDataValue(metadata
.id(), metadata
.unique(), info
.value(),
17791 info
.localizedValue()),
17795 QM_TRY_UNWRAP((auto [indexValuesBlob
, indexValuesBlobLength
]),
17796 MakeCompressedIndexDataValues(indexValues
));
17798 MOZ_ASSERT(!indexValuesBlobLength
== !(indexValuesBlob
.get()));
17800 nsCOMPtr
<nsIVariant
> value
;
17802 if (!indexValuesBlob
) {
17803 value
= new storage::NullVariant();
17805 value
.forget(_retval
);
17809 // Now insert the new table rows. We only need to construct a new list if
17810 // the full list is different.
17811 if (hadPreviousIndexValues
) {
17812 indexValues
.ClearAndRetainStorage();
17814 MOZ_ASSERT(indexValues
.Capacity() >= updateInfoCount
);
17816 for (const IndexUpdateInfo
& info
: updateInfos
) {
17817 MOZ_ALWAYS_TRUE(indexValues
.InsertElementSorted(
17818 IndexDataValue(metadata
.id(), metadata
.unique(), info
.value(),
17819 info
.localizedValue()),
17824 QM_TRY(MOZ_TO_RESULT(
17825 InsertIndexTableRows(mConnection
, objectStoreId
, key
, indexValues
)));
17827 value
= new storage::AdoptedBlobVariant(
17828 std::pair(indexValuesBlob
.release(), indexValuesBlobLength
));
17830 value
.forget(_retval
);
17834 DeleteIndexOp::DeleteIndexOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
17835 const IndexOrObjectStoreId aObjectStoreId
,
17836 const IndexOrObjectStoreId aIndexId
,
17837 const bool aUnique
, const bool aIsLastIndex
)
17838 : VersionChangeTransactionOp(std::move(aTransaction
)),
17839 mObjectStoreId(aObjectStoreId
),
17840 mIndexId(aIndexId
),
17842 mIsLastIndex(aIsLastIndex
) {
17843 MOZ_ASSERT(aObjectStoreId
);
17844 MOZ_ASSERT(aIndexId
);
17847 nsresult
DeleteIndexOp::RemoveReferencesToIndex(
17848 DatabaseConnection
* aConnection
, const Key
& aObjectStoreKey
,
17849 nsTArray
<IndexDataValue
>& aIndexValues
) const {
17850 MOZ_ASSERT(!NS_IsMainThread());
17851 MOZ_ASSERT(!IsOnBackgroundThread());
17852 MOZ_ASSERT(aConnection
);
17853 MOZ_ASSERT(!aObjectStoreKey
.IsUnset());
17854 MOZ_ASSERT_IF(!mIsLastIndex
, !aIndexValues
.IsEmpty());
17856 AUTO_PROFILER_LABEL("DeleteIndexOp::RemoveReferencesToIndex", DOM
);
17858 if (mIsLastIndex
) {
17859 // There is no need to parse the previous entry in the index_data_values
17860 // column if this is the last index. Simply set it to NULL.
17861 QM_TRY_INSPECT(const auto& stmt
,
17862 aConnection
->BorrowCachedStatement(
17863 "UPDATE object_data "
17864 "SET index_data_values = NULL "
17865 "WHERE object_store_id = :"_ns
+
17866 kStmtParamNameObjectStoreId
+ " AND key = :"_ns
+
17867 kStmtParamNameKey
+ ";"_ns
));
17869 QM_TRY(MOZ_TO_RESULT(
17870 stmt
->BindInt64ByName(kStmtParamNameObjectStoreId
, mObjectStoreId
)));
17872 QM_TRY(MOZ_TO_RESULT(
17873 aObjectStoreKey
.BindToStatement(&*stmt
, kStmtParamNameKey
)));
17875 QM_TRY(MOZ_TO_RESULT(stmt
->Execute()));
17881 IndexDataValue search
;
17882 search
.mIndexId
= mIndexId
;
17884 // Use raw pointers for search to avoid redundant index validity checks.
17885 // Maybe this should better be encapsulated in nsTArray.
17886 const auto* const begin
= aIndexValues
.Elements();
17887 const auto* const end
= aIndexValues
.Elements() + aIndexValues
.Length();
17889 const auto indexIdComparator
= [](const IndexDataValue
& aA
,
17890 const IndexDataValue
& aB
) {
17891 return aA
.mIndexId
< aB
.mIndexId
;
17894 MOZ_ASSERT(std::is_sorted(begin
, end
, indexIdComparator
));
17896 const auto [beginRange
, endRange
] =
17897 std::equal_range(begin
, end
, search
, indexIdComparator
);
17898 if (beginRange
== end
) {
17899 IDB_REPORT_INTERNAL_ERR();
17900 return NS_ERROR_FILE_CORRUPTED
;
17903 aIndexValues
.RemoveElementsAt(beginRange
- begin
, endRange
- beginRange
);
17906 QM_TRY(MOZ_TO_RESULT(UpdateIndexValues(aConnection
, mObjectStoreId
,
17907 aObjectStoreKey
, aIndexValues
)));
17912 nsresult
DeleteIndexOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
17913 MOZ_ASSERT(aConnection
);
17914 aConnection
->AssertIsOnConnectionThread();
17918 // Make sure |mIsLastIndex| is telling the truth.
17919 // The parameter names are not used, parameters are bound by index only
17920 // locally in the same function.
17921 QM_TRY_INSPECT(const auto& stmt
,
17922 aConnection
->BorrowCachedStatement(
17924 "FROM object_store_index "
17925 "WHERE object_store_id = :object_store_id;"_ns
),
17926 QM_ASSERT_UNREACHABLE
);
17928 MOZ_ALWAYS_SUCCEEDS(stmt
->BindInt64ByIndex(0, mObjectStoreId
));
17930 bool foundThisIndex
= false;
17931 bool foundOtherIndex
= false;
17935 MOZ_ALWAYS_SUCCEEDS(stmt
->ExecuteStep(&hasResult
));
17942 MOZ_ALWAYS_SUCCEEDS(stmt
->GetInt64(0, &id
));
17944 if (id
== mIndexId
) {
17945 foundThisIndex
= true;
17947 foundOtherIndex
= true;
17951 MOZ_ASSERT_IF(mIsLastIndex
, foundThisIndex
&& !foundOtherIndex
);
17952 MOZ_ASSERT_IF(!mIsLastIndex
, foundThisIndex
&& foundOtherIndex
);
17956 AUTO_PROFILER_LABEL("DeleteIndexOp::DoDatabaseWork", DOM
);
17958 DatabaseConnection::AutoSavepoint autoSave
;
17959 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
17960 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
17962 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
17966 // mozStorage warns that these statements trigger a sort operation but we
17967 // don't care because this is a very rare call and we expect it to be slow.
17968 // The cost of having an index on this field is too high.
17970 const auto& selectStmt
,
17971 aConnection
->BorrowCachedStatement(
17974 ? "/* do not warn (bug someone else) */ "
17975 "SELECT value, object_data_key "
17976 "FROM unique_index_data "
17977 "WHERE index_id = :"_ns
+
17978 kStmtParamNameIndexId
+
17979 " ORDER BY object_data_key ASC;"_ns
17980 : "/* do not warn (bug out) */ "
17981 "SELECT unique_index_data.value, "
17982 "unique_index_data.object_data_key, "
17983 "object_data.index_data_values "
17984 "FROM unique_index_data "
17985 "JOIN object_data "
17986 "ON unique_index_data.object_data_key = object_data.key "
17987 "WHERE unique_index_data.index_id = :"_ns
+
17988 kStmtParamNameIndexId
+
17989 " AND object_data.object_store_id = :"_ns
+
17990 kStmtParamNameObjectStoreId
+
17991 " ORDER BY unique_index_data.object_data_key ASC;"_ns
)
17993 ? "/* do not warn (bug me not) */ "
17994 "SELECT value, object_data_key "
17996 "WHERE index_id = :"_ns
+
17997 kStmtParamNameIndexId
+
17998 " AND object_store_id = :"_ns
+
17999 kStmtParamNameObjectStoreId
+
18000 " ORDER BY object_data_key ASC;"_ns
18001 : "/* do not warn (bug off) */ "
18002 "SELECT index_data.value, "
18003 "index_data.object_data_key, "
18004 "object_data.index_data_values "
18006 "JOIN object_data "
18007 "ON index_data.object_data_key = object_data.key "
18008 "WHERE index_data.index_id = :"_ns
+
18009 kStmtParamNameIndexId
+
18010 " AND object_data.object_store_id = :"_ns
+
18011 kStmtParamNameObjectStoreId
+
18012 " ORDER BY index_data.object_data_key ASC;"_ns
)));
18014 QM_TRY(MOZ_TO_RESULT(
18015 selectStmt
->BindInt64ByName(kStmtParamNameIndexId
, mIndexId
)));
18017 if (!mUnique
|| !mIsLastIndex
) {
18018 QM_TRY(MOZ_TO_RESULT(selectStmt
->BindInt64ByName(
18019 kStmtParamNameObjectStoreId
, mObjectStoreId
)));
18022 Key lastObjectStoreKey
;
18023 IndexDataValuesAutoArray lastIndexValues
;
18025 QM_TRY(CollectWhileHasResult(
18027 [this, &aConnection
, &lastObjectStoreKey
, &lastIndexValues
,
18028 deleteIndexRowStmt
=
18029 DatabaseConnection::LazyStatement
{
18032 ? "DELETE FROM unique_index_data "
18033 "WHERE index_id = :"_ns
+
18034 kStmtParamNameIndexId
+ " AND value = :"_ns
+
18035 kStmtParamNameValue
+ ";"_ns
18036 : "DELETE FROM index_data "
18037 "WHERE index_id = :"_ns
+
18038 kStmtParamNameIndexId
+ " AND value = :"_ns
+
18039 kStmtParamNameValue
+ " AND object_data_key = :"_ns
+
18040 kStmtParamNameObjectDataKey
+ ";"_ns
}](
18041 auto& selectStmt
) mutable -> Result
<Ok
, nsresult
> {
18042 // We always need the index key to delete the index row.
18044 QM_TRY(MOZ_TO_RESULT(indexKey
.SetFromStatement(&selectStmt
, 0)));
18046 QM_TRY(OkIf(!indexKey
.IsUnset()), Err(NS_ERROR_FILE_CORRUPTED
),
18047 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
18049 // Don't call |lastObjectStoreKey.BindToStatement()| directly because we
18050 // don't want to copy the same key multiple times.
18051 const uint8_t* objectStoreKeyData
;
18052 uint32_t objectStoreKeyDataLength
;
18053 QM_TRY(MOZ_TO_RESULT(selectStmt
.GetSharedBlob(
18054 1, &objectStoreKeyDataLength
, &objectStoreKeyData
)));
18056 QM_TRY(OkIf(objectStoreKeyDataLength
), Err(NS_ERROR_FILE_CORRUPTED
),
18057 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
18059 const nsDependentCString
currentObjectStoreKeyBuffer(
18060 reinterpret_cast<const char*>(objectStoreKeyData
),
18061 objectStoreKeyDataLength
);
18062 if (currentObjectStoreKeyBuffer
!= lastObjectStoreKey
.GetBuffer()) {
18063 // We just walked to the next object store key.
18064 if (!lastObjectStoreKey
.IsUnset()) {
18065 // Before we move on to the next key we need to update the previous
18066 // key's index_data_values column.
18067 QM_TRY(MOZ_TO_RESULT(RemoveReferencesToIndex(
18068 aConnection
, lastObjectStoreKey
, lastIndexValues
)));
18071 // Save the object store key.
18072 lastObjectStoreKey
= Key(currentObjectStoreKeyBuffer
);
18074 // And the |index_data_values| row if this isn't the only index.
18075 if (!mIsLastIndex
) {
18076 lastIndexValues
.ClearAndRetainStorage();
18077 QM_TRY(MOZ_TO_RESULT(
18078 ReadCompressedIndexDataValues(selectStmt
, 2, lastIndexValues
)));
18080 QM_TRY(OkIf(!lastIndexValues
.IsEmpty()),
18081 Err(NS_ERROR_FILE_CORRUPTED
),
18082 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
18086 // Now delete the index row.
18088 QM_TRY_INSPECT(const auto& borrowedDeleteIndexRowStmt
,
18089 deleteIndexRowStmt
.Borrow());
18091 QM_TRY(MOZ_TO_RESULT(borrowedDeleteIndexRowStmt
->BindInt64ByName(
18092 kStmtParamNameIndexId
, mIndexId
)));
18094 QM_TRY(MOZ_TO_RESULT(indexKey
.BindToStatement(
18095 &*borrowedDeleteIndexRowStmt
, kStmtParamNameValue
)));
18098 QM_TRY(MOZ_TO_RESULT(lastObjectStoreKey
.BindToStatement(
18099 &*borrowedDeleteIndexRowStmt
, kStmtParamNameObjectDataKey
)));
18102 QM_TRY(MOZ_TO_RESULT(borrowedDeleteIndexRowStmt
->Execute()));
18108 // Take care of the last key.
18109 if (!lastObjectStoreKey
.IsUnset()) {
18110 MOZ_ASSERT_IF(!mIsLastIndex
, !lastIndexValues
.IsEmpty());
18112 QM_TRY(MOZ_TO_RESULT(RemoveReferencesToIndex(
18113 aConnection
, lastObjectStoreKey
, lastIndexValues
)));
18116 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
18117 "DELETE FROM object_store_index "
18118 "WHERE id = :index_id;"_ns
,
18120 mIndexId
](mozIStorageStatement
& deleteStmt
) -> Result
<Ok
, nsresult
> {
18121 QM_TRY(MOZ_TO_RESULT(deleteStmt
.BindInt64ByIndex(0, indexId
)));
18128 int32_t deletedRowCount
;
18129 MOZ_ALWAYS_SUCCEEDS(aConnection
->MutableStorageConnection().GetAffectedRows(
18130 &deletedRowCount
));
18131 MOZ_ASSERT(deletedRowCount
== 1);
18135 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
18140 nsresult
RenameIndexOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
18141 MOZ_ASSERT(aConnection
);
18142 aConnection
->AssertIsOnConnectionThread();
18144 AUTO_PROFILER_LABEL("RenameIndexOp::DoDatabaseWork", DOM
);
18148 // Make sure that we're not renaming an index with the same name as another
18149 // that already exists. This should be impossible because we should have
18150 // thrown an error long before now...
18151 // The parameter names are not used, parameters are bound by index only
18152 // locally in the same function.
18153 QM_TRY_INSPECT(const bool& hasResult
,
18155 ->BorrowAndExecuteSingleStepStatement(
18157 "FROM object_store_index "
18158 "WHERE object_store_id = :object_store_id "
18159 "AND name = :name "
18160 "AND id != :id;"_ns
,
18161 [&self
= *this](auto& stmt
) -> Result
<Ok
, nsresult
> {
18162 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(
18163 0, self
.mObjectStoreId
)));
18164 QM_TRY(MOZ_TO_RESULT(
18165 stmt
.BindStringByIndex(1, self
.mNewName
)));
18166 QM_TRY(MOZ_TO_RESULT(
18167 stmt
.BindInt64ByIndex(2, self
.mIndexId
)));
18172 QM_ASSERT_UNREACHABLE
);
18174 MOZ_ASSERT(!hasResult
);
18177 Unused
<< mObjectStoreId
;
18180 DatabaseConnection::AutoSavepoint autoSave
;
18181 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
18182 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
18184 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
18188 // The parameter names are not used, parameters are bound by index only
18189 // locally in the same function.
18190 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
18191 "UPDATE object_store_index "
18192 "SET name = :name "
18193 "WHERE id = :id;"_ns
,
18194 [&self
= *this](mozIStorageStatement
& stmt
) -> Result
<Ok
, nsresult
> {
18195 QM_TRY(MOZ_TO_RESULT(stmt
.BindStringByIndex(0, self
.mNewName
)));
18197 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(1, self
.mIndexId
)));
18202 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
18207 Result
<bool, nsresult
> NormalTransactionOp::ObjectStoreHasIndexes(
18208 DatabaseConnection
& aConnection
, const IndexOrObjectStoreId aObjectStoreId
,
18209 const bool aMayHaveIndexes
) {
18210 aConnection
.AssertIsOnConnectionThread();
18211 MOZ_ASSERT(aObjectStoreId
);
18213 if (Transaction().GetMode() == IDBTransaction::Mode::VersionChange
&&
18215 // If this is a version change transaction then mObjectStoreMayHaveIndexes
18216 // could be wrong (e.g. if a unique index failed to be created due to a
18217 // constraint error). We have to check on this thread by asking the database
18219 QM_TRY_RETURN(DatabaseOperationBase::ObjectStoreHasIndexes(aConnection
,
18225 const bool& hasIndexes
,
18226 DatabaseOperationBase::ObjectStoreHasIndexes(aConnection
, aObjectStoreId
),
18227 QM_ASSERT_UNREACHABLE
);
18228 MOZ_ASSERT(aMayHaveIndexes
== hasIndexes
);
18231 return aMayHaveIndexes
;
18234 Result
<PreprocessParams
, nsresult
> NormalTransactionOp::GetPreprocessParams() {
18235 return PreprocessParams
{};
18238 nsresult
NormalTransactionOp::SendPreprocessInfo() {
18239 AssertIsOnOwningThread();
18240 MOZ_ASSERT(!IsActorDestroyed());
18242 QM_TRY_INSPECT(const auto& params
, GetPreprocessParams());
18244 MOZ_ASSERT(params
.type() != PreprocessParams::T__None
);
18246 if (NS_WARN_IF(!PBackgroundIDBRequestParent::SendPreprocess(params
))) {
18247 IDB_REPORT_INTERNAL_ERR();
18248 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
18254 nsresult
NormalTransactionOp::SendSuccessResult() {
18255 AssertIsOnOwningThread();
18257 if (!IsActorDestroyed()) {
18258 static const size_t kMaxIDBMsgOverhead
= 1024 * 1024 * 10; // 10MB
18259 const uint32_t maximalSizeFromPref
=
18260 IndexedDatabaseManager::MaxSerializedMsgSize();
18261 MOZ_ASSERT(maximalSizeFromPref
> kMaxIDBMsgOverhead
);
18262 const size_t kMaxMessageSize
= maximalSizeFromPref
- kMaxIDBMsgOverhead
;
18264 RequestResponse response
;
18265 size_t responseSize
= kMaxMessageSize
;
18266 GetResponse(response
, &responseSize
);
18268 if (responseSize
>= kMaxMessageSize
) {
18269 nsPrintfCString
warning(
18270 "The serialized value is too large"
18271 " (size=%zu bytes, max=%zu bytes).",
18272 responseSize
, kMaxMessageSize
);
18273 NS_WARNING(warning
.get());
18274 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
18277 MOZ_ASSERT(response
.type() != RequestResponse::T__None
);
18279 if (response
.type() == RequestResponse::Tnsresult
) {
18280 MOZ_ASSERT(NS_FAILED(response
.get_nsresult()));
18282 return response
.get_nsresult();
18286 !PBackgroundIDBRequestParent::Send__delete__(this, response
))) {
18287 IDB_REPORT_INTERNAL_ERR();
18288 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
18293 mResponseSent
= true;
18299 bool NormalTransactionOp::SendFailureResult(nsresult aResultCode
) {
18300 AssertIsOnOwningThread();
18301 MOZ_ASSERT(NS_FAILED(aResultCode
));
18303 bool result
= false;
18305 if (!IsActorDestroyed()) {
18306 result
= PBackgroundIDBRequestParent::Send__delete__(
18307 this, ClampResultCode(aResultCode
));
18311 mResponseSent
= true;
18317 void NormalTransactionOp::Cleanup() {
18318 AssertIsOnOwningThread();
18319 MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent
);
18321 TransactionDatabaseOperationBase::Cleanup();
18324 void NormalTransactionOp::ActorDestroy(ActorDestroyReason aWhy
) {
18325 AssertIsOnOwningThread();
18327 NoteActorDestroyed();
18329 // Assume ActorDestroy can happen at any time, so we can't probe the current
18330 // state since mInternalState can be modified on any thread (only one thread
18331 // at a time based on the state machine).
18332 // However we can use mWaitingForContinue which is only touched on the owning
18333 // thread. If mWaitingForContinue is true, we can also modify mInternalState
18334 // since we are guaranteed that there are no pending runnables which would
18335 // probe mInternalState to decide what code needs to run (there shouldn't be
18336 // any running runnables on other threads either).
18338 if (IsWaitingForContinue()) {
18339 NoteContinueReceived();
18342 // We don't have to handle the case when mWaitingForContinue is not true since
18343 // it means that either nothing has been initialized yet, so nothing to
18344 // cleanup or there are pending runnables that will detect that the actor has
18345 // been destroyed and cleanup accordingly.
18348 mozilla::ipc::IPCResult
NormalTransactionOp::RecvContinue(
18349 const PreprocessResponse
& aResponse
) {
18350 AssertIsOnOwningThread();
18352 switch (aResponse
.type()) {
18353 case PreprocessResponse::Tnsresult
:
18354 SetFailureCode(aResponse
.get_nsresult());
18357 case PreprocessResponse::TObjectStoreGetPreprocessResponse
:
18358 case PreprocessResponse::TObjectStoreGetAllPreprocessResponse
:
18362 MOZ_CRASH("Should never get here!");
18365 NoteContinueReceived();
18370 ObjectStoreAddOrPutRequestOp::ObjectStoreAddOrPutRequestOp(
18371 SafeRefPtr
<TransactionBase
> aTransaction
, RequestParams
&& aParams
)
18372 : NormalTransactionOp(std::move(aTransaction
)),
18374 std::move(aParams
.type() == RequestParams::TObjectStoreAddParams
18375 ? aParams
.get_ObjectStoreAddParams().commonParams()
18376 : aParams
.get_ObjectStorePutParams().commonParams())),
18377 mOriginMetadata(Transaction().GetDatabase().OriginMetadata()),
18378 mPersistenceType(Transaction().GetDatabase().Type()),
18379 mOverwrite(aParams
.type() == RequestParams::TObjectStorePutParams
),
18380 mObjectStoreMayHaveIndexes(false) {
18381 MOZ_ASSERT(aParams
.type() == RequestParams::TObjectStoreAddParams
||
18382 aParams
.type() == RequestParams::TObjectStorePutParams
);
18385 Transaction().GetMetadataForObjectStoreId(mParams
.objectStoreId());
18386 MOZ_ASSERT(mMetadata
);
18388 mObjectStoreMayHaveIndexes
= mMetadata
->HasLiveIndexes();
18390 mDataOverThreshold
=
18391 snappy::MaxCompressedLength(mParams
.cloneInfo().data().data
.Size()) >
18392 IndexedDatabaseManager::DataThreshold();
18395 nsresult
ObjectStoreAddOrPutRequestOp::RemoveOldIndexDataValues(
18396 DatabaseConnection
* aConnection
) {
18397 AssertIsOnConnectionThread();
18398 MOZ_ASSERT(aConnection
);
18399 MOZ_ASSERT(mOverwrite
);
18400 MOZ_ASSERT(!mResponse
.IsUnset());
18404 QM_TRY_INSPECT(const bool& hasIndexes
,
18405 DatabaseOperationBase::ObjectStoreHasIndexes(
18406 *aConnection
, mParams
.objectStoreId()),
18407 QM_ASSERT_UNREACHABLE
);
18409 MOZ_ASSERT(hasIndexes
,
18410 "Don't use this slow method if there are no indexes!");
18415 const auto& indexValuesStmt
,
18416 aConnection
->BorrowAndExecuteSingleStepStatement(
18417 "SELECT index_data_values "
18418 "FROM object_data "
18419 "WHERE object_store_id = :"_ns
+
18420 kStmtParamNameObjectStoreId
+ " AND key = :"_ns
+
18421 kStmtParamNameKey
+ ";"_ns
,
18422 [&self
= *this](auto& stmt
) -> mozilla::Result
<Ok
, nsresult
> {
18423 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByName(
18424 kStmtParamNameObjectStoreId
, self
.mParams
.objectStoreId())));
18426 QM_TRY(MOZ_TO_RESULT(
18427 self
.mResponse
.BindToStatement(&stmt
, kStmtParamNameKey
)));
18432 if (indexValuesStmt
) {
18433 QM_TRY_INSPECT(const auto& existingIndexValues
,
18434 ReadCompressedIndexDataValues(**indexValuesStmt
, 0));
18436 QM_TRY(MOZ_TO_RESULT(
18437 DeleteIndexDataTableRows(aConnection
, mResponse
, existingIndexValues
)));
18443 bool ObjectStoreAddOrPutRequestOp::Init(TransactionBase
& aTransaction
) {
18444 AssertIsOnOwningThread();
18446 const nsTArray
<IndexUpdateInfo
>& indexUpdateInfos
=
18447 mParams
.indexUpdateInfos();
18449 if (!indexUpdateInfos
.IsEmpty()) {
18450 mUniqueIndexTable
.emplace();
18452 for (const auto& updateInfo
: indexUpdateInfos
) {
18453 auto indexMetadata
= mMetadata
->mIndexes
.Lookup(updateInfo
.indexId());
18454 MOZ_ALWAYS_TRUE(indexMetadata
);
18456 MOZ_ASSERT(!(*indexMetadata
)->mDeleted
);
18458 const IndexOrObjectStoreId
& indexId
=
18459 (*indexMetadata
)->mCommonMetadata
.id();
18460 const bool& unique
= (*indexMetadata
)->mCommonMetadata
.unique();
18462 MOZ_ASSERT(indexId
== updateInfo
.indexId());
18463 MOZ_ASSERT_IF(!(*indexMetadata
)->mCommonMetadata
.multiEntry(),
18464 !mUniqueIndexTable
.ref().Contains(indexId
));
18466 if (NS_WARN_IF(!mUniqueIndexTable
.ref().InsertOrUpdate(indexId
, unique
,
18471 } else if (mOverwrite
) {
18472 mUniqueIndexTable
.emplace();
18475 if (mUniqueIndexTable
.isSome()) {
18476 mUniqueIndexTable
.ref().MarkImmutable();
18481 TransformIntoNewArray(
18482 mParams
.fileAddInfos(),
18483 [](const auto& fileAddInfo
) {
18484 MOZ_ASSERT(fileAddInfo
.type() == StructuredCloneFileBase::eBlob
||
18485 fileAddInfo
.type() ==
18486 StructuredCloneFileBase::eMutableFile
);
18488 switch (fileAddInfo
.type()) {
18489 case StructuredCloneFileBase::eBlob
: {
18490 PBackgroundIDBDatabaseFileParent
* file
=
18491 fileAddInfo
.fileParent();
18494 auto* const fileActor
= static_cast<DatabaseFile
*>(file
);
18495 MOZ_ASSERT(fileActor
);
18497 return StoredFileInfo::CreateForBlob(
18498 fileActor
->GetFileInfoPtr(), fileActor
);
18502 MOZ_CRASH("Should never get here!");
18508 if (mDataOverThreshold
) {
18510 aTransaction
.GetDatabase().GetFileManager().CreateFileInfo();
18511 if (NS_WARN_IF(!fileInfo
)) {
18515 mStoredFileInfos
.EmplaceBack(StoredFileInfo::CreateForStructuredClone(
18516 std::move(fileInfo
),
18517 MakeRefPtr
<SCInputStream
>(mParams
.cloneInfo().data().data
)));
18523 nsresult
ObjectStoreAddOrPutRequestOp::DoDatabaseWork(
18524 DatabaseConnection
* aConnection
) {
18525 MOZ_ASSERT(aConnection
);
18526 aConnection
->AssertIsOnConnectionThread();
18527 MOZ_ASSERT(aConnection
->HasStorageConnection());
18529 AUTO_PROFILER_LABEL("ObjectStoreAddOrPutRequestOp::DoDatabaseWork", DOM
);
18531 DatabaseConnection::AutoSavepoint autoSave
;
18532 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
18533 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
18535 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
18539 QM_TRY_INSPECT(const bool& objectStoreHasIndexes
,
18540 ObjectStoreHasIndexes(*aConnection
, mParams
.objectStoreId(),
18541 mObjectStoreMayHaveIndexes
));
18543 // This will be the final key we use.
18544 Key
& key
= mResponse
;
18545 key
= mParams
.key();
18547 const bool keyUnset
= key
.IsUnset();
18548 const IndexOrObjectStoreId osid
= mParams
.objectStoreId();
18550 // First delete old index_data_values if we're overwriting something and we
18552 if (mOverwrite
&& !keyUnset
&& objectStoreHasIndexes
) {
18553 QM_TRY(MOZ_TO_RESULT(RemoveOldIndexDataValues(aConnection
)));
18556 int64_t autoIncrementNum
= 0;
18559 // The "|| keyUnset" here is mostly a debugging tool. If a key isn't
18560 // specified we should never have a collision and so it shouldn't matter
18561 // if we allow overwrite or not. By not allowing overwrite we raise
18562 // detectable errors rather than corrupting data.
18563 const auto optReplaceDirective
=
18564 (!mOverwrite
|| keyUnset
) ? ""_ns
: "OR REPLACE "_ns
;
18565 QM_TRY_INSPECT(const auto& stmt
,
18566 aConnection
->BorrowCachedStatement(
18567 "INSERT "_ns
+ optReplaceDirective
+
18568 "INTO object_data "
18569 "(object_store_id, key, file_ids, data) "
18571 kStmtParamNameObjectStoreId
+ ", :"_ns
+
18572 kStmtParamNameKey
+ ", :"_ns
+ kStmtParamNameFileIds
+
18573 ", :"_ns
+ kStmtParamNameData
+ ");"_ns
));
18575 QM_TRY(MOZ_TO_RESULT(
18576 stmt
->BindInt64ByName(kStmtParamNameObjectStoreId
, osid
)));
18578 const SerializedStructuredCloneWriteInfo
& cloneInfo
= mParams
.cloneInfo();
18579 const JSStructuredCloneData
& cloneData
= cloneInfo
.data().data
;
18580 const size_t cloneDataSize
= cloneData
.Size();
18582 MOZ_ASSERT(!keyUnset
|| mMetadata
->mCommonMetadata
.autoIncrement(),
18583 "Should have key unless autoIncrement");
18585 if (mMetadata
->mCommonMetadata
.autoIncrement()) {
18588 const auto&& lockedAutoIncrementIds
=
18589 mMetadata
->mAutoIncrementIds
.Lock();
18591 autoIncrementNum
= lockedAutoIncrementIds
->next
;
18594 MOZ_ASSERT(autoIncrementNum
> 0);
18596 if (autoIncrementNum
> (1LL << 53)) {
18597 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR
;
18600 QM_TRY(key
.SetFromInteger(autoIncrementNum
));
18601 } else if (key
.IsFloat()) {
18602 double numericKey
= key
.ToFloat();
18603 numericKey
= std::min(numericKey
, double(1LL << 53));
18604 numericKey
= floor(numericKey
);
18606 const auto&& lockedAutoIncrementIds
=
18607 mMetadata
->mAutoIncrementIds
.Lock();
18608 if (numericKey
>= lockedAutoIncrementIds
->next
) {
18609 autoIncrementNum
= numericKey
;
18613 if (keyUnset
&& mMetadata
->mCommonMetadata
.keyPath().IsValid()) {
18614 const SerializedStructuredCloneWriteInfo
& cloneInfo
=
18615 mParams
.cloneInfo();
18616 MOZ_ASSERT(cloneInfo
.offsetToKeyProp());
18617 MOZ_ASSERT(cloneDataSize
> sizeof(uint64_t));
18618 MOZ_ASSERT(cloneInfo
.offsetToKeyProp() <=
18619 (cloneDataSize
- sizeof(uint64_t)));
18621 // Special case where someone put an object into an autoIncrement'ing
18622 // objectStore with no key in its keyPath set. We needed to figure out
18623 // which row id we would get above before we could set that properly.
18624 uint64_t keyPropValue
=
18625 ReinterpretDoubleAsUInt64(static_cast<double>(autoIncrementNum
));
18627 static const size_t keyPropSize
= sizeof(uint64_t);
18629 char keyPropBuffer
[keyPropSize
];
18630 LittleEndian::writeUint64(keyPropBuffer
, keyPropValue
);
18632 auto iter
= cloneData
.Start();
18633 MOZ_ALWAYS_TRUE(cloneData
.Advance(iter
, cloneInfo
.offsetToKeyProp()));
18635 cloneData
.UpdateBytes(iter
, keyPropBuffer
, keyPropSize
));
18639 key
.BindToStatement(&*stmt
, kStmtParamNameKey
);
18641 if (mDataOverThreshold
) {
18642 // The data we store in the SQLite database is a (signed) 64-bit integer.
18643 // The flags are left-shifted 32 bits so the max value is 0xFFFFFFFF.
18644 // The file_ids index occupies the lower 32 bits and its max is
18646 static const uint32_t kCompressedFlag
= (1 << 0);
18648 uint32_t flags
= 0;
18649 flags
|= kCompressedFlag
;
18651 const uint32_t index
= mStoredFileInfos
.Length() - 1;
18653 const int64_t data
= (uint64_t(flags
) << 32) | index
;
18655 QM_TRY(MOZ_TO_RESULT(stmt
->BindInt64ByName(kStmtParamNameData
, data
)));
18657 AutoTArray
<char, 4096> flatCloneData
; // 4096 from JSStructuredCloneData
18658 QM_TRY(OkIf(flatCloneData
.SetLength(cloneDataSize
, fallible
)),
18659 Err(NS_ERROR_OUT_OF_MEMORY
));
18662 auto iter
= cloneData
.Start();
18664 cloneData
.ReadBytes(iter
, flatCloneData
.Elements(), cloneDataSize
));
18667 // Compress the bytes before adding into the database.
18668 const char* const uncompressed
= flatCloneData
.Elements();
18669 const size_t uncompressedLength
= cloneDataSize
;
18671 size_t compressedLength
= snappy::MaxCompressedLength(uncompressedLength
);
18673 UniqueFreePtr
<char> compressed(
18674 static_cast<char*>(malloc(compressedLength
)));
18675 if (NS_WARN_IF(!compressed
)) {
18676 return NS_ERROR_OUT_OF_MEMORY
;
18679 snappy::RawCompress(uncompressed
, uncompressedLength
, compressed
.get(),
18680 &compressedLength
);
18682 uint8_t* const dataBuffer
=
18683 reinterpret_cast<uint8_t*>(compressed
.release());
18684 const size_t dataBufferLength
= compressedLength
;
18686 QM_TRY(MOZ_TO_RESULT(stmt
->BindAdoptedBlobByName(
18687 kStmtParamNameData
, dataBuffer
, dataBufferLength
)));
18690 if (!mStoredFileInfos
.IsEmpty()) {
18691 // Moved outside the loop to allow it to be cached when demanded by the
18692 // first write. (We may have mStoredFileInfos without any required
18694 Maybe
<FileHelper
> fileHelper
;
18695 nsAutoString fileIds
;
18697 for (auto& storedFileInfo
: mStoredFileInfos
) {
18698 MOZ_ASSERT(storedFileInfo
.IsValid());
18700 QM_TRY_INSPECT(const auto& inputStream
,
18701 storedFileInfo
.GetInputStream());
18704 if (fileHelper
.isNothing()) {
18705 fileHelper
.emplace(Transaction().GetDatabase().GetFileManagerPtr());
18706 QM_TRY(MOZ_TO_RESULT(fileHelper
->Init()),
18707 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
,
18708 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
18711 const DatabaseFileInfo
& fileInfo
= storedFileInfo
.GetFileInfo();
18713 const auto file
= fileHelper
->GetFile(fileInfo
);
18714 QM_TRY(OkIf(file
), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
,
18715 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
18717 const auto journalFile
= fileHelper
->GetJournalFile(fileInfo
);
18718 QM_TRY(OkIf(journalFile
), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
,
18719 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
18721 nsCString fileKeyId
;
18722 fileKeyId
.AppendInt(fileInfo
.Id());
18724 const auto maybeKey
=
18728 .IsInPrivateBrowsingMode()
18729 ? gIndexedDBCipherKeyManager
->Get(
18730 Transaction().GetDatabase().Id(), fileKeyId
)
18734 MOZ_TO_RESULT(fileHelper
->CreateFileFromStream(
18735 *file
, *journalFile
, *inputStream
,
18736 storedFileInfo
.ShouldCompress(), maybeKey
))
18737 .mapErr([](const nsresult rv
) {
18738 if (NS_ERROR_GET_MODULE(rv
) !=
18739 NS_ERROR_MODULE_DOM_INDEXEDDB
) {
18740 IDB_REPORT_INTERNAL_ERR();
18741 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
18746 ([this, &file
= *file
, &journalFile
= *journalFile
](const auto) {
18747 // Try to remove the file if the copy failed.
18748 QM_TRY(MOZ_TO_RESULT(Transaction()
18751 .SyncDeleteFile(file
, journalFile
)),
18755 storedFileInfo
.NotifyWriteSucceeded();
18758 if (!fileIds
.IsEmpty()) {
18759 fileIds
.Append(' ');
18761 storedFileInfo
.Serialize(fileIds
);
18764 QM_TRY(MOZ_TO_RESULT(
18765 stmt
->BindStringByName(kStmtParamNameFileIds
, fileIds
)));
18767 QM_TRY(MOZ_TO_RESULT(stmt
->BindNullByName(kStmtParamNameFileIds
)));
18770 QM_TRY(MOZ_TO_RESULT(stmt
->Execute()), QM_PROPAGATE
,
18771 [keyUnset
= DebugOnly
{keyUnset
}](const nsresult rv
) {
18772 if (rv
== NS_ERROR_STORAGE_CONSTRAINT
) {
18773 MOZ_ASSERT(!keyUnset
, "Generated key had a collision!");
18778 // Update our indexes if needed.
18779 if (!mParams
.indexUpdateInfos().IsEmpty()) {
18780 MOZ_ASSERT(mUniqueIndexTable
.isSome());
18782 // Write the index_data_values column.
18783 QM_TRY_INSPECT(const auto& indexValues
,
18784 IndexDataValuesFromUpdateInfos(mParams
.indexUpdateInfos(),
18785 mUniqueIndexTable
.ref()));
18788 MOZ_TO_RESULT(UpdateIndexValues(aConnection
, osid
, key
, indexValues
)));
18790 QM_TRY(MOZ_TO_RESULT(
18791 InsertIndexTableRows(aConnection
, osid
, key
, indexValues
)));
18794 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
18796 if (autoIncrementNum
) {
18798 auto&& lockedAutoIncrementIds
= mMetadata
->mAutoIncrementIds
.Lock();
18800 lockedAutoIncrementIds
->next
= autoIncrementNum
+ 1;
18803 Transaction().NoteModifiedAutoIncrementObjectStore(mMetadata
);
18809 void ObjectStoreAddOrPutRequestOp::GetResponse(RequestResponse
& aResponse
,
18810 size_t* aResponseSize
) {
18811 AssertIsOnOwningThread();
18814 aResponse
= ObjectStorePutResponse(mResponse
);
18815 *aResponseSize
= mResponse
.GetBuffer().Length();
18817 aResponse
= ObjectStoreAddResponse(mResponse
);
18818 *aResponseSize
= mResponse
.GetBuffer().Length();
18822 void ObjectStoreAddOrPutRequestOp::Cleanup() {
18823 AssertIsOnOwningThread();
18825 mStoredFileInfos
.Clear();
18827 NormalTransactionOp::Cleanup();
18830 NS_IMPL_ISUPPORTS(ObjectStoreAddOrPutRequestOp::SCInputStream
, nsIInputStream
)
18833 ObjectStoreAddOrPutRequestOp::SCInputStream::Close() { return NS_OK
; }
18836 ObjectStoreAddOrPutRequestOp::SCInputStream::Available(uint64_t* _retval
) {
18837 return NS_ERROR_NOT_IMPLEMENTED
;
18841 ObjectStoreAddOrPutRequestOp::SCInputStream::Read(char* aBuf
, uint32_t aCount
,
18842 uint32_t* _retval
) {
18843 return ReadSegments(NS_CopySegmentToBuffer
, aBuf
, aCount
, _retval
);
18847 ObjectStoreAddOrPutRequestOp::SCInputStream::ReadSegments(
18848 nsWriteSegmentFun aWriter
, void* aClosure
, uint32_t aCount
,
18849 uint32_t* _retval
) {
18853 uint32_t count
= std::min(uint32_t(mIter
.RemainingInSegment()), aCount
);
18855 // We've run out of data in the last segment.
18861 aWriter(this, aClosure
, mIter
.Data(), *_retval
, count
, &written
);
18862 if (NS_WARN_IF(NS_FAILED(rv
))) {
18863 // InputStreams do not propagate errors to caller.
18867 // Writer should write what we asked it to write.
18868 MOZ_ASSERT(written
== count
);
18873 if (NS_WARN_IF(!mData
.Advance(mIter
, count
))) {
18874 // InputStreams do not propagate errors to caller.
18883 ObjectStoreAddOrPutRequestOp::SCInputStream::IsNonBlocking(bool* _retval
) {
18888 ObjectStoreGetRequestOp::ObjectStoreGetRequestOp(
18889 SafeRefPtr
<TransactionBase
> aTransaction
, const RequestParams
& aParams
,
18891 : NormalTransactionOp(std::move(aTransaction
)),
18892 mObjectStoreId(aGetAll
18893 ? aParams
.get_ObjectStoreGetAllParams().objectStoreId()
18894 : aParams
.get_ObjectStoreGetParams().objectStoreId()),
18895 mDatabase(Transaction().GetDatabasePtr()),
18897 aGetAll
? aParams
.get_ObjectStoreGetAllParams().optionalKeyRange()
18898 : Some(aParams
.get_ObjectStoreGetParams().keyRange())),
18899 mBackgroundParent(Transaction().GetBackgroundParent()),
18900 mPreprocessInfoCount(0),
18901 mLimit(aGetAll
? aParams
.get_ObjectStoreGetAllParams().limit() : 1),
18903 MOZ_ASSERT(aParams
.type() == RequestParams::TObjectStoreGetParams
||
18904 aParams
.type() == RequestParams::TObjectStoreGetAllParams
);
18905 MOZ_ASSERT(mObjectStoreId
);
18906 MOZ_ASSERT(mDatabase
);
18907 MOZ_ASSERT_IF(!aGetAll
, mOptionalKeyRange
.isSome());
18908 MOZ_ASSERT(mBackgroundParent
);
18911 template <typename T
>
18912 Result
<T
, nsresult
> ObjectStoreGetRequestOp::ConvertResponse(
18913 StructuredCloneReadInfoParent
&& aInfo
) {
18916 static_assert(std::is_same_v
<T
, SerializedStructuredCloneReadInfo
> ||
18917 std::is_same_v
<T
, PreprocessInfo
>);
18919 if constexpr (std::is_same_v
<T
, SerializedStructuredCloneReadInfo
>) {
18920 result
.data().data
= aInfo
.ReleaseData();
18921 result
.hasPreprocessInfo() = aInfo
.HasPreprocessInfo();
18924 QM_TRY_UNWRAP(result
.files(), SerializeStructuredCloneFiles(
18925 mDatabase
, aInfo
.Files(),
18926 std::is_same_v
<T
, PreprocessInfo
>));
18931 nsresult
ObjectStoreGetRequestOp::DoDatabaseWork(
18932 DatabaseConnection
* aConnection
) {
18933 MOZ_ASSERT(aConnection
);
18934 aConnection
->AssertIsOnConnectionThread();
18935 MOZ_ASSERT_IF(!mGetAll
, mOptionalKeyRange
.isSome());
18936 MOZ_ASSERT_IF(!mGetAll
, mLimit
== 1);
18938 AUTO_PROFILER_LABEL("ObjectStoreGetRequestOp::DoDatabaseWork", DOM
);
18940 const nsCString query
=
18941 "SELECT file_ids, data "
18942 "FROM object_data "
18943 "WHERE object_store_id = :"_ns
+
18944 kStmtParamNameObjectStoreId
+
18945 MaybeGetBindingClauseForKeyRange(mOptionalKeyRange
, kColumnNameKey
) +
18946 " ORDER BY key ASC"_ns
+
18947 (mLimit
? kOpenLimit
+ IntToCString(mLimit
) : EmptyCString());
18949 QM_TRY_INSPECT(const auto& stmt
, aConnection
->BorrowCachedStatement(query
));
18951 QM_TRY(MOZ_TO_RESULT(
18952 stmt
->BindInt64ByName(kStmtParamNameObjectStoreId
, mObjectStoreId
)));
18954 if (mOptionalKeyRange
.isSome()) {
18955 QM_TRY(MOZ_TO_RESULT(
18956 BindKeyRangeToStatement(mOptionalKeyRange
.ref(), &*stmt
)));
18959 QM_TRY(CollectWhileHasResult(
18960 *stmt
, [this](auto& stmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
18961 QM_TRY_UNWRAP(auto cloneInfo
,
18962 GetStructuredCloneReadInfoFromStatement(
18963 &stmt
, 1, 0, mDatabase
->GetFileManager()));
18965 if (cloneInfo
.HasPreprocessInfo()) {
18966 mPreprocessInfoCount
++;
18969 QM_TRY(OkIf(mResponse
.EmplaceBack(fallible
, std::move(cloneInfo
))),
18970 Err(NS_ERROR_OUT_OF_MEMORY
));
18975 MOZ_ASSERT_IF(!mGetAll
, mResponse
.Length() <= 1);
18980 bool ObjectStoreGetRequestOp::HasPreprocessInfo() {
18981 return mPreprocessInfoCount
> 0;
18984 Result
<PreprocessParams
, nsresult
>
18985 ObjectStoreGetRequestOp::GetPreprocessParams() {
18986 AssertIsOnOwningThread();
18987 MOZ_ASSERT(!mResponse
.IsEmpty());
18990 auto params
= ObjectStoreGetAllPreprocessParams();
18992 auto& preprocessInfos
= params
.preprocessInfos();
18994 !preprocessInfos
.SetCapacity(mPreprocessInfoCount
, fallible
))) {
18995 return Err(NS_ERROR_OUT_OF_MEMORY
);
18998 QM_TRY(TransformIfAbortOnErr(
18999 std::make_move_iterator(mResponse
.begin()),
19000 std::make_move_iterator(mResponse
.end()),
19001 MakeBackInserter(preprocessInfos
),
19002 [](const auto& info
) { return info
.HasPreprocessInfo(); },
19003 [&self
= *this](StructuredCloneReadInfoParent
&& info
) {
19004 return self
.ConvertResponse
<PreprocessInfo
>(std::move(info
));
19007 return PreprocessParams
{std::move(params
)};
19010 auto params
= ObjectStoreGetPreprocessParams();
19012 QM_TRY_UNWRAP(params
.preprocessInfo(),
19013 ConvertResponse
<PreprocessInfo
>(std::move(mResponse
[0])));
19015 return PreprocessParams
{std::move(params
)};
19018 void ObjectStoreGetRequestOp::GetResponse(RequestResponse
& aResponse
,
19019 size_t* aResponseSize
) {
19020 MOZ_ASSERT_IF(mLimit
, mResponse
.Length() <= mLimit
);
19023 aResponse
= ObjectStoreGetAllResponse();
19024 *aResponseSize
= 0;
19026 if (!mResponse
.IsEmpty()) {
19028 aResponse
.get_ObjectStoreGetAllResponse().cloneInfos(),
19029 TransformIntoNewArrayAbortOnErr(
19030 std::make_move_iterator(mResponse
.begin()),
19031 std::make_move_iterator(mResponse
.end()),
19032 [this, &aResponseSize
](StructuredCloneReadInfoParent
&& info
) {
19033 *aResponseSize
+= info
.Size();
19034 return ConvertResponse
<SerializedStructuredCloneReadInfo
>(
19038 QM_VOID
, [&aResponse
](const nsresult result
) { aResponse
= result
; });
19044 aResponse
= ObjectStoreGetResponse();
19045 *aResponseSize
= 0;
19047 if (!mResponse
.IsEmpty()) {
19048 SerializedStructuredCloneReadInfo
& serializedInfo
=
19049 aResponse
.get_ObjectStoreGetResponse().cloneInfo();
19051 *aResponseSize
+= mResponse
[0].Size();
19052 QM_TRY_UNWRAP(serializedInfo
,
19053 ConvertResponse
<SerializedStructuredCloneReadInfo
>(
19054 std::move(mResponse
[0])),
19056 [&aResponse
](const nsresult result
) { aResponse
= result
; });
19060 ObjectStoreGetKeyRequestOp::ObjectStoreGetKeyRequestOp(
19061 SafeRefPtr
<TransactionBase
> aTransaction
, const RequestParams
& aParams
,
19063 : NormalTransactionOp(std::move(aTransaction
)),
19065 aGetAll
? aParams
.get_ObjectStoreGetAllKeysParams().objectStoreId()
19066 : aParams
.get_ObjectStoreGetKeyParams().objectStoreId()),
19068 aGetAll
? aParams
.get_ObjectStoreGetAllKeysParams().optionalKeyRange()
19069 : Some(aParams
.get_ObjectStoreGetKeyParams().keyRange())),
19070 mLimit(aGetAll
? aParams
.get_ObjectStoreGetAllKeysParams().limit() : 1),
19072 MOZ_ASSERT(aParams
.type() == RequestParams::TObjectStoreGetKeyParams
||
19073 aParams
.type() == RequestParams::TObjectStoreGetAllKeysParams
);
19074 MOZ_ASSERT(mObjectStoreId
);
19075 MOZ_ASSERT_IF(!aGetAll
, mOptionalKeyRange
.isSome());
19078 nsresult
ObjectStoreGetKeyRequestOp::DoDatabaseWork(
19079 DatabaseConnection
* aConnection
) {
19080 MOZ_ASSERT(aConnection
);
19081 aConnection
->AssertIsOnConnectionThread();
19083 AUTO_PROFILER_LABEL("ObjectStoreGetKeyRequestOp::DoDatabaseWork", DOM
);
19085 const nsCString query
=
19087 "FROM object_data "
19088 "WHERE object_store_id = :"_ns
+
19089 kStmtParamNameObjectStoreId
+
19090 MaybeGetBindingClauseForKeyRange(mOptionalKeyRange
, kColumnNameKey
) +
19091 " ORDER BY key ASC"_ns
+
19092 (mLimit
? " LIMIT "_ns
+ IntToCString(mLimit
) : EmptyCString());
19094 QM_TRY_INSPECT(const auto& stmt
, aConnection
->BorrowCachedStatement(query
));
19097 stmt
->BindInt64ByName(kStmtParamNameObjectStoreId
, mObjectStoreId
);
19098 if (NS_WARN_IF(NS_FAILED(rv
))) {
19102 if (mOptionalKeyRange
.isSome()) {
19103 rv
= BindKeyRangeToStatement(mOptionalKeyRange
.ref(), &*stmt
);
19104 if (NS_WARN_IF(NS_FAILED(rv
))) {
19109 QM_TRY(CollectWhileHasResult(
19110 *stmt
, [this](auto& stmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
19111 Key
* const key
= mResponse
.AppendElement(fallible
);
19112 QM_TRY(OkIf(key
), Err(NS_ERROR_OUT_OF_MEMORY
));
19113 QM_TRY(MOZ_TO_RESULT(key
->SetFromStatement(&stmt
, 0)));
19118 MOZ_ASSERT_IF(!mGetAll
, mResponse
.Length() <= 1);
19123 void ObjectStoreGetKeyRequestOp::GetResponse(RequestResponse
& aResponse
,
19124 size_t* aResponseSize
) {
19125 MOZ_ASSERT_IF(mLimit
, mResponse
.Length() <= mLimit
);
19128 aResponse
= ObjectStoreGetAllKeysResponse();
19129 *aResponseSize
= std::accumulate(mResponse
.begin(), mResponse
.end(), 0u,
19130 [](size_t old
, const auto& entry
) {
19131 return old
+ entry
.GetBuffer().Length();
19134 aResponse
.get_ObjectStoreGetAllKeysResponse().keys() = std::move(mResponse
);
19139 aResponse
= ObjectStoreGetKeyResponse();
19140 *aResponseSize
= 0;
19142 if (!mResponse
.IsEmpty()) {
19143 *aResponseSize
= mResponse
[0].GetBuffer().Length();
19144 aResponse
.get_ObjectStoreGetKeyResponse().key() = std::move(mResponse
[0]);
19148 ObjectStoreDeleteRequestOp::ObjectStoreDeleteRequestOp(
19149 SafeRefPtr
<TransactionBase
> aTransaction
,
19150 const ObjectStoreDeleteParams
& aParams
)
19151 : NormalTransactionOp(std::move(aTransaction
)),
19153 mObjectStoreMayHaveIndexes(false) {
19154 AssertIsOnBackgroundThread();
19156 SafeRefPtr
<FullObjectStoreMetadata
> metadata
=
19157 Transaction().GetMetadataForObjectStoreId(mParams
.objectStoreId());
19158 MOZ_ASSERT(metadata
);
19160 mObjectStoreMayHaveIndexes
= metadata
->HasLiveIndexes();
19163 nsresult
ObjectStoreDeleteRequestOp::DoDatabaseWork(
19164 DatabaseConnection
* aConnection
) {
19165 MOZ_ASSERT(aConnection
);
19166 aConnection
->AssertIsOnConnectionThread();
19167 AUTO_PROFILER_LABEL("ObjectStoreDeleteRequestOp::DoDatabaseWork", DOM
);
19169 DatabaseConnection::AutoSavepoint autoSave
;
19170 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
19171 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
19173 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
19177 QM_TRY_INSPECT(const bool& objectStoreHasIndexes
,
19178 ObjectStoreHasIndexes(*aConnection
, mParams
.objectStoreId(),
19179 mObjectStoreMayHaveIndexes
));
19181 if (objectStoreHasIndexes
) {
19182 QM_TRY(MOZ_TO_RESULT(DeleteObjectStoreDataTableRowsWithIndexes(
19183 aConnection
, mParams
.objectStoreId(), Some(mParams
.keyRange()))));
19185 const auto keyRangeClause
=
19186 GetBindingClauseForKeyRange(mParams
.keyRange(), kColumnNameKey
);
19188 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
19189 "DELETE FROM object_data "
19190 "WHERE object_store_id = :"_ns
+
19191 kStmtParamNameObjectStoreId
+ keyRangeClause
+ ";"_ns
,
19192 [¶ms
= mParams
](
19193 mozIStorageStatement
& stmt
) -> mozilla::Result
<Ok
, nsresult
> {
19194 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByName(kStmtParamNameObjectStoreId
,
19195 params
.objectStoreId())));
19198 MOZ_TO_RESULT(BindKeyRangeToStatement(params
.keyRange(), &stmt
)));
19204 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
19209 ObjectStoreClearRequestOp::ObjectStoreClearRequestOp(
19210 SafeRefPtr
<TransactionBase
> aTransaction
,
19211 const ObjectStoreClearParams
& aParams
)
19212 : NormalTransactionOp(std::move(aTransaction
)),
19214 mObjectStoreMayHaveIndexes(false) {
19215 AssertIsOnBackgroundThread();
19217 SafeRefPtr
<FullObjectStoreMetadata
> metadata
=
19218 Transaction().GetMetadataForObjectStoreId(mParams
.objectStoreId());
19219 MOZ_ASSERT(metadata
);
19221 mObjectStoreMayHaveIndexes
= metadata
->HasLiveIndexes();
19224 nsresult
ObjectStoreClearRequestOp::DoDatabaseWork(
19225 DatabaseConnection
* aConnection
) {
19226 MOZ_ASSERT(aConnection
);
19227 aConnection
->AssertIsOnConnectionThread();
19229 AUTO_PROFILER_LABEL("ObjectStoreClearRequestOp::DoDatabaseWork", DOM
);
19231 DatabaseConnection::AutoSavepoint autoSave
;
19232 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
19233 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
19235 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
19239 QM_TRY_INSPECT(const bool& objectStoreHasIndexes
,
19240 ObjectStoreHasIndexes(*aConnection
, mParams
.objectStoreId(),
19241 mObjectStoreMayHaveIndexes
));
19243 // The parameter names are not used, parameters are bound by index only
19244 // locally in the same function.
19245 QM_TRY(MOZ_TO_RESULT(
19246 objectStoreHasIndexes
19247 ? DeleteObjectStoreDataTableRowsWithIndexes(
19248 aConnection
, mParams
.objectStoreId(), Nothing())
19249 : aConnection
->ExecuteCachedStatement(
19250 "DELETE FROM object_data "
19251 "WHERE object_store_id = :object_store_id;"_ns
,
19253 mParams
.objectStoreId()](mozIStorageStatement
& stmt
)
19254 -> mozilla::Result
<Ok
, nsresult
> {
19256 MOZ_TO_RESULT(stmt
.BindInt64ByIndex(0, objectStoreId
)));
19261 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
19266 nsresult
ObjectStoreCountRequestOp::DoDatabaseWork(
19267 DatabaseConnection
* aConnection
) {
19268 MOZ_ASSERT(aConnection
);
19269 aConnection
->AssertIsOnConnectionThread();
19271 AUTO_PROFILER_LABEL("ObjectStoreCountRequestOp::DoDatabaseWork", DOM
);
19273 const auto keyRangeClause
= MaybeGetBindingClauseForKeyRange(
19274 mParams
.optionalKeyRange(), kColumnNameKey
);
19277 const auto& maybeStmt
,
19278 aConnection
->BorrowAndExecuteSingleStepStatement(
19280 "FROM object_data "
19281 "WHERE object_store_id = :"_ns
+
19282 kStmtParamNameObjectStoreId
+ keyRangeClause
,
19283 [¶ms
= mParams
](auto& stmt
) -> mozilla::Result
<Ok
, nsresult
> {
19284 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByName(
19285 kStmtParamNameObjectStoreId
, params
.objectStoreId())));
19287 if (params
.optionalKeyRange().isSome()) {
19288 QM_TRY(MOZ_TO_RESULT(BindKeyRangeToStatement(
19289 params
.optionalKeyRange().ref(), &stmt
)));
19295 QM_TRY(OkIf(maybeStmt
.isSome()), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
,
19297 // XXX Why do we have an assertion here, but not at most other
19298 // places using IDB_REPORT_INTERNAL_ERR(_LAMBDA)?
19299 MOZ_ASSERT(false, "This should never be possible!");
19300 IDB_REPORT_INTERNAL_ERR();
19303 const auto& stmt
= *maybeStmt
;
19305 const int64_t count
= stmt
->AsInt64(0);
19306 QM_TRY(OkIf(count
>= 0), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
, [](const auto) {
19307 // XXX Why do we have an assertion here, but not at most other places using
19308 // IDB_REPORT_INTERNAL_ERR(_LAMBDA)?
19309 MOZ_ASSERT(false, "This should never be possible!");
19310 IDB_REPORT_INTERNAL_ERR();
19313 mResponse
.count() = count
;
19319 SafeRefPtr
<FullIndexMetadata
> IndexRequestOpBase::IndexMetadataForParams(
19320 const TransactionBase
& aTransaction
, const RequestParams
& aParams
) {
19321 AssertIsOnBackgroundThread();
19322 MOZ_ASSERT(aParams
.type() == RequestParams::TIndexGetParams
||
19323 aParams
.type() == RequestParams::TIndexGetKeyParams
||
19324 aParams
.type() == RequestParams::TIndexGetAllParams
||
19325 aParams
.type() == RequestParams::TIndexGetAllKeysParams
||
19326 aParams
.type() == RequestParams::TIndexCountParams
);
19328 IndexOrObjectStoreId objectStoreId
;
19329 IndexOrObjectStoreId indexId
;
19331 switch (aParams
.type()) {
19332 case RequestParams::TIndexGetParams
: {
19333 const IndexGetParams
& params
= aParams
.get_IndexGetParams();
19334 objectStoreId
= params
.objectStoreId();
19335 indexId
= params
.indexId();
19339 case RequestParams::TIndexGetKeyParams
: {
19340 const IndexGetKeyParams
& params
= aParams
.get_IndexGetKeyParams();
19341 objectStoreId
= params
.objectStoreId();
19342 indexId
= params
.indexId();
19346 case RequestParams::TIndexGetAllParams
: {
19347 const IndexGetAllParams
& params
= aParams
.get_IndexGetAllParams();
19348 objectStoreId
= params
.objectStoreId();
19349 indexId
= params
.indexId();
19353 case RequestParams::TIndexGetAllKeysParams
: {
19354 const IndexGetAllKeysParams
& params
= aParams
.get_IndexGetAllKeysParams();
19355 objectStoreId
= params
.objectStoreId();
19356 indexId
= params
.indexId();
19360 case RequestParams::TIndexCountParams
: {
19361 const IndexCountParams
& params
= aParams
.get_IndexCountParams();
19362 objectStoreId
= params
.objectStoreId();
19363 indexId
= params
.indexId();
19368 MOZ_CRASH("Should never get here!");
19371 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
19372 aTransaction
.GetMetadataForObjectStoreId(objectStoreId
);
19373 MOZ_ASSERT(objectStoreMetadata
);
19375 SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
19376 aTransaction
.GetMetadataForIndexId(*objectStoreMetadata
, indexId
);
19377 MOZ_ASSERT(indexMetadata
);
19379 return indexMetadata
;
19382 IndexGetRequestOp::IndexGetRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
19383 const RequestParams
& aParams
, bool aGetAll
)
19384 : IndexRequestOpBase(std::move(aTransaction
), aParams
),
19385 mDatabase(Transaction().GetDatabasePtr()),
19386 mOptionalKeyRange(aGetAll
19387 ? aParams
.get_IndexGetAllParams().optionalKeyRange()
19388 : Some(aParams
.get_IndexGetParams().keyRange())),
19389 mBackgroundParent(Transaction().GetBackgroundParent()),
19390 mLimit(aGetAll
? aParams
.get_IndexGetAllParams().limit() : 1),
19392 MOZ_ASSERT(aParams
.type() == RequestParams::TIndexGetParams
||
19393 aParams
.type() == RequestParams::TIndexGetAllParams
);
19394 MOZ_ASSERT(mDatabase
);
19395 MOZ_ASSERT_IF(!aGetAll
, mOptionalKeyRange
.isSome());
19396 MOZ_ASSERT(mBackgroundParent
);
19399 nsresult
IndexGetRequestOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
19400 MOZ_ASSERT(aConnection
);
19401 aConnection
->AssertIsOnConnectionThread();
19402 MOZ_ASSERT_IF(!mGetAll
, mOptionalKeyRange
.isSome());
19403 MOZ_ASSERT_IF(!mGetAll
, mLimit
== 1);
19405 AUTO_PROFILER_LABEL("IndexGetRequestOp::DoDatabaseWork", DOM
);
19407 const auto indexTable
= mMetadata
->mCommonMetadata
.unique()
19408 ? "unique_index_data "_ns
19409 : "index_data "_ns
;
19413 aConnection
->BorrowCachedStatement(
19414 "SELECT file_ids, data "
19415 "FROM object_data "
19419 "ON object_data.object_store_id = "
19420 "index_table.object_store_id "
19421 "AND object_data.key = "
19422 "index_table.object_data_key "
19423 "WHERE index_id = :"_ns
+
19424 kStmtParamNameIndexId
+
19425 MaybeGetBindingClauseForKeyRange(mOptionalKeyRange
,
19426 kColumnNameValue
) +
19427 (mLimit
? kOpenLimit
+ IntToCString(mLimit
) : EmptyCString())));
19429 QM_TRY(MOZ_TO_RESULT(stmt
->BindInt64ByName(kStmtParamNameIndexId
,
19430 mMetadata
->mCommonMetadata
.id())));
19432 if (mOptionalKeyRange
.isSome()) {
19433 QM_TRY(MOZ_TO_RESULT(
19434 BindKeyRangeToStatement(mOptionalKeyRange
.ref(), &*stmt
)));
19437 QM_TRY(CollectWhileHasResult(
19438 *stmt
, [this](auto& stmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
19439 QM_TRY_UNWRAP(auto cloneInfo
,
19440 GetStructuredCloneReadInfoFromStatement(
19441 &stmt
, 1, 0, mDatabase
->GetFileManager()));
19443 if (cloneInfo
.HasPreprocessInfo()) {
19444 IDB_WARNING("Preprocessing for indexes not yet implemented!");
19445 return Err(NS_ERROR_NOT_IMPLEMENTED
);
19448 QM_TRY(OkIf(mResponse
.EmplaceBack(fallible
, std::move(cloneInfo
))),
19449 Err(NS_ERROR_OUT_OF_MEMORY
));
19454 MOZ_ASSERT_IF(!mGetAll
, mResponse
.Length() <= 1);
19459 // XXX This is more or less a duplicate of ObjectStoreGetRequestOp::GetResponse
19460 void IndexGetRequestOp::GetResponse(RequestResponse
& aResponse
,
19461 size_t* aResponseSize
) {
19462 MOZ_ASSERT_IF(!mGetAll
, mResponse
.Length() <= 1);
19464 auto convertResponse
= [this](StructuredCloneReadInfoParent
&& info
)
19465 -> mozilla::Result
<SerializedStructuredCloneReadInfo
, nsresult
> {
19466 SerializedStructuredCloneReadInfo result
;
19468 result
.data().data
= info
.ReleaseData();
19470 QM_TRY_UNWRAP(result
.files(), SerializeStructuredCloneFiles(
19471 mDatabase
, info
.Files(), false));
19477 aResponse
= IndexGetAllResponse();
19478 *aResponseSize
= 0;
19480 if (!mResponse
.IsEmpty()) {
19482 aResponse
.get_IndexGetAllResponse().cloneInfos(),
19483 TransformIntoNewArrayAbortOnErr(
19484 std::make_move_iterator(mResponse
.begin()),
19485 std::make_move_iterator(mResponse
.end()),
19487 &aResponseSize
](StructuredCloneReadInfoParent
&& info
) {
19488 *aResponseSize
+= info
.Size();
19489 return convertResponse(std::move(info
));
19492 QM_VOID
, [&aResponse
](const nsresult result
) { aResponse
= result
; });
19498 aResponse
= IndexGetResponse();
19499 *aResponseSize
= 0;
19501 if (!mResponse
.IsEmpty()) {
19502 SerializedStructuredCloneReadInfo
& serializedInfo
=
19503 aResponse
.get_IndexGetResponse().cloneInfo();
19505 *aResponseSize
+= mResponse
[0].Size();
19506 QM_TRY_UNWRAP(serializedInfo
, convertResponse(std::move(mResponse
[0])),
19508 [&aResponse
](const nsresult result
) { aResponse
= result
; });
19512 IndexGetKeyRequestOp::IndexGetKeyRequestOp(
19513 SafeRefPtr
<TransactionBase
> aTransaction
, const RequestParams
& aParams
,
19515 : IndexRequestOpBase(std::move(aTransaction
), aParams
),
19517 aGetAll
? aParams
.get_IndexGetAllKeysParams().optionalKeyRange()
19518 : Some(aParams
.get_IndexGetKeyParams().keyRange())),
19519 mLimit(aGetAll
? aParams
.get_IndexGetAllKeysParams().limit() : 1),
19521 MOZ_ASSERT(aParams
.type() == RequestParams::TIndexGetKeyParams
||
19522 aParams
.type() == RequestParams::TIndexGetAllKeysParams
);
19523 MOZ_ASSERT_IF(!aGetAll
, mOptionalKeyRange
.isSome());
19526 nsresult
IndexGetKeyRequestOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
19527 MOZ_ASSERT(aConnection
);
19528 aConnection
->AssertIsOnConnectionThread();
19529 MOZ_ASSERT_IF(!mGetAll
, mOptionalKeyRange
.isSome());
19530 MOZ_ASSERT_IF(!mGetAll
, mLimit
== 1);
19532 AUTO_PROFILER_LABEL("IndexGetKeyRequestOp::DoDatabaseWork", DOM
);
19534 const bool hasKeyRange
= mOptionalKeyRange
.isSome();
19536 const auto indexTable
= mMetadata
->mCommonMetadata
.unique()
19537 ? "unique_index_data "_ns
19538 : "index_data "_ns
;
19540 const nsCString query
=
19541 "SELECT object_data_key "
19543 indexTable
+ "WHERE index_id = :"_ns
+ kStmtParamNameIndexId
+
19544 MaybeGetBindingClauseForKeyRange(mOptionalKeyRange
, kColumnNameValue
) +
19545 (mLimit
? kOpenLimit
+ IntToCString(mLimit
) : EmptyCString());
19547 QM_TRY_INSPECT(const auto& stmt
, aConnection
->BorrowCachedStatement(query
));
19549 QM_TRY(MOZ_TO_RESULT(stmt
->BindInt64ByName(kStmtParamNameIndexId
,
19550 mMetadata
->mCommonMetadata
.id())));
19553 QM_TRY(MOZ_TO_RESULT(
19554 BindKeyRangeToStatement(mOptionalKeyRange
.ref(), &*stmt
)));
19557 QM_TRY(CollectWhileHasResult(
19558 *stmt
, [this](auto& stmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
19559 Key
* const key
= mResponse
.AppendElement(fallible
);
19560 QM_TRY(OkIf(key
), Err(NS_ERROR_OUT_OF_MEMORY
));
19561 QM_TRY(MOZ_TO_RESULT(key
->SetFromStatement(&stmt
, 0)));
19566 MOZ_ASSERT_IF(!mGetAll
, mResponse
.Length() <= 1);
19571 void IndexGetKeyRequestOp::GetResponse(RequestResponse
& aResponse
,
19572 size_t* aResponseSize
) {
19573 MOZ_ASSERT_IF(!mGetAll
, mResponse
.Length() <= 1);
19576 aResponse
= IndexGetAllKeysResponse();
19577 *aResponseSize
= std::accumulate(mResponse
.begin(), mResponse
.end(), 0u,
19578 [](size_t old
, const auto& entry
) {
19579 return old
+ entry
.GetBuffer().Length();
19582 aResponse
.get_IndexGetAllKeysResponse().keys() = std::move(mResponse
);
19587 aResponse
= IndexGetKeyResponse();
19588 *aResponseSize
= 0;
19590 if (!mResponse
.IsEmpty()) {
19591 *aResponseSize
= mResponse
[0].GetBuffer().Length();
19592 aResponse
.get_IndexGetKeyResponse().key() = std::move(mResponse
[0]);
19596 nsresult
IndexCountRequestOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
19597 MOZ_ASSERT(aConnection
);
19598 aConnection
->AssertIsOnConnectionThread();
19600 AUTO_PROFILER_LABEL("IndexCountRequestOp::DoDatabaseWork", DOM
);
19602 const auto indexTable
= mMetadata
->mCommonMetadata
.unique()
19603 ? "unique_index_data "_ns
19604 : "index_data "_ns
;
19606 const auto keyRangeClause
= MaybeGetBindingClauseForKeyRange(
19607 mParams
.optionalKeyRange(), kColumnNameValue
);
19610 const auto& maybeStmt
,
19611 aConnection
->BorrowAndExecuteSingleStepStatement(
19614 indexTable
+ "WHERE index_id = :"_ns
+ kStmtParamNameIndexId
+
19616 [&self
= *this](auto& stmt
) -> mozilla::Result
<Ok
, nsresult
> {
19617 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByName(
19618 kStmtParamNameIndexId
, self
.mMetadata
->mCommonMetadata
.id())));
19620 if (self
.mParams
.optionalKeyRange().isSome()) {
19621 QM_TRY(MOZ_TO_RESULT(BindKeyRangeToStatement(
19622 self
.mParams
.optionalKeyRange().ref(), &stmt
)));
19628 QM_TRY(OkIf(maybeStmt
.isSome()), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
,
19630 // XXX Why do we have an assertion here, but not at most other
19631 // places using IDB_REPORT_INTERNAL_ERR(_LAMBDA)?
19632 MOZ_ASSERT(false, "This should never be possible!");
19633 IDB_REPORT_INTERNAL_ERR();
19636 const auto& stmt
= *maybeStmt
;
19638 const int64_t count
= stmt
->AsInt64(0);
19639 QM_TRY(OkIf(count
>= 0), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
, [](const auto) {
19640 // XXX Why do we have an assertion here, but not at most other places using
19641 // IDB_REPORT_INTERNAL_ERR(_LAMBDA)?
19642 MOZ_ASSERT(false, "This should never be possible!");
19643 IDB_REPORT_INTERNAL_ERR();
19646 mResponse
.count() = count
;
19651 template <IDBCursorType CursorType
>
19652 bool Cursor
<CursorType
>::CursorOpBase::SendFailureResult(nsresult aResultCode
) {
19653 AssertIsOnOwningThread();
19654 MOZ_ASSERT(NS_FAILED(aResultCode
));
19655 MOZ_ASSERT(mCursor
);
19656 MOZ_ASSERT(mCursor
->mCurrentlyRunningOp
== this);
19657 MOZ_ASSERT(!mResponseSent
);
19659 if (!IsActorDestroyed()) {
19660 mResponse
= ClampResultCode(aResultCode
);
19662 // This is an expected race when the transaction is invalidated after
19663 // data is retrieved from database.
19665 // TODO: There seem to be other cases when mFiles is non-empty here, which
19666 // have been present before adding cursor preloading, but with cursor
19667 // preloading they have become more frequent (also during startup). One
19668 // possible cause with cursor preloading is to be addressed by Bug 1597191.
19669 NS_WARNING_ASSERTION(
19670 !mFiles
.IsEmpty() && !Transaction().IsInvalidated(),
19671 "Expected empty mFiles when transaction has not been invalidated");
19673 // SendResponseInternal will assert when mResponse.type() is
19674 // CursorResponse::Tnsresult and mFiles is non-empty, so we clear mFiles
19678 mCursor
->SendResponseInternal(mResponse
, mFiles
);
19682 mResponseSent
= true;
19687 template <IDBCursorType CursorType
>
19688 void Cursor
<CursorType
>::CursorOpBase::Cleanup() {
19689 AssertIsOnOwningThread();
19690 MOZ_ASSERT(mCursor
);
19691 MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent
);
19696 // A bit hacky but the CursorOp request is not generated in response to a
19697 // child request like most other database operations. Do this to make our
19698 // assertions happy.
19699 NoteActorDestroyed();
19702 TransactionDatabaseOperationBase::Cleanup();
19705 template <IDBCursorType CursorType
>
19706 ResponseSizeOrError
19707 CursorOpBaseHelperBase
<CursorType
>::PopulateResponseFromStatement(
19708 mozIStorageStatement
* const aStmt
, const bool aInitializeResponse
,
19709 Key
* const aOptOutSortKey
) {
19710 mOp
.Transaction().AssertIsOnConnectionThread();
19711 MOZ_ASSERT_IF(aInitializeResponse
,
19712 mOp
.mResponse
.type() == CursorResponse::T__None
);
19713 MOZ_ASSERT_IF(!aInitializeResponse
,
19714 mOp
.mResponse
.type() != CursorResponse::T__None
);
19716 mOp
.mFiles
.IsEmpty() &&
19717 (mOp
.mResponse
.type() ==
19718 CursorResponse::TArrayOfObjectStoreCursorResponse
||
19719 mOp
.mResponse
.type() == CursorResponse::TArrayOfIndexCursorResponse
),
19720 aInitializeResponse
);
19722 auto populateResponseHelper
= PopulateResponseHelper
<CursorType
>{mOp
};
19723 auto previousKey
= aOptOutSortKey
? std::move(*aOptOutSortKey
) : Key
{};
19725 QM_TRY(MOZ_TO_RESULT(populateResponseHelper
.GetKeys(aStmt
, aOptOutSortKey
)));
19727 // aOptOutSortKey must be set iff the cursor is a unique cursor. For unique
19728 // cursors, we need to skip records with the same key. The SQL queries
19729 // currently do not filter these out.
19730 if (aOptOutSortKey
&& !previousKey
.IsUnset() &&
19731 previousKey
== *aOptOutSortKey
) {
19735 QM_TRY(MOZ_TO_RESULT(
19736 populateResponseHelper
.MaybeGetCloneInfo(aStmt
, GetCursor())));
19738 // CAUTION: It is important that only the part of the function above this
19739 // comment may fail, and modifications to the data structure (in particular
19740 // mResponse and mFiles) may only be made below. This is necessary to allow to
19741 // discard entries that were attempted to be preloaded without causing an
19742 // inconsistent state.
19744 if (aInitializeResponse
) {
19745 mOp
.mResponse
= std::remove_reference_t
<
19746 decltype(populateResponseHelper
.GetTypedResponse(&mOp
.mResponse
))>();
19749 auto& responses
= populateResponseHelper
.GetTypedResponse(&mOp
.mResponse
);
19750 auto& response
= *responses
.AppendElement();
19752 populateResponseHelper
.FillKeys(response
);
19753 if constexpr (!CursorTypeTraits
<CursorType
>::IsKeyOnlyCursor
) {
19754 populateResponseHelper
.MaybeFillCloneInfo(response
, &mOp
.mFiles
);
19757 return populateResponseHelper
.GetKeySize(response
) +
19758 populateResponseHelper
.MaybeGetCloneInfoSize(response
);
19761 template <IDBCursorType CursorType
>
19762 void CursorOpBaseHelperBase
<CursorType
>::PopulateExtraResponses(
19763 mozIStorageStatement
* const aStmt
, const uint32_t aMaxExtraCount
,
19764 const size_t aInitialResponseSize
, const nsACString
& aOperation
,
19765 Key
* const aOptPreviousSortKey
) {
19766 mOp
.AssertIsOnConnectionThread();
19768 const auto extraCount
= [&]() -> uint32_t {
19769 auto accumulatedResponseSize
= aInitialResponseSize
;
19770 uint32_t extraCount
= 0;
19774 nsresult rv
= aStmt
->ExecuteStep(&hasResult
);
19775 if (NS_WARN_IF(NS_FAILED(rv
))) {
19776 // In case of a failure on one step, do not attempt to execute further
19777 // steps, but use the results already populated.
19786 // PopulateResponseFromStatement does not modify the data in case of
19787 // failure, so we can just use the results already populated, and discard
19788 // any remaining entries, and signal overall success. Probably, future
19789 // attempts to access the same entry will fail as well, but it might never
19790 // be accessed by the application.
19792 const auto& responseSize
,
19793 PopulateResponseFromStatement(aStmt
, false, aOptPreviousSortKey
),
19794 extraCount
, [](const auto&) {
19795 // TODO: Maybe disable preloading for this cursor? The problem will
19796 // probably reoccur on the next attempt, and disabling preloading
19797 // will reduce latency. However, if some problematic entry will be
19798 // skipped over, after that it might be fine again. To judge this,
19799 // the causes for such failures would need to be analyzed more
19800 // thoroughly. Since this seems to be rare, maybe no further action
19801 // is necessary at all.
19804 // Check accumulated size of individual responses and maybe break early.
19805 accumulatedResponseSize
+= responseSize
;
19806 if (accumulatedResponseSize
> IPC::Channel::kMaximumMessageSize
/ 2) {
19807 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
19808 "PRELOAD: %s: Dropping entries because maximum message size is "
19809 "exceeded: %" PRIu32
"/%zu bytes",
19810 "%.0s Dropping too large (%" PRIu32
"/%zu)",
19811 IDB_LOG_ID_STRING(mOp
.mBackgroundChildLoggingId
),
19812 mOp
.mTransactionLoggingSerialNumber
, mOp
.mLoggingSerialNumber
,
19813 PromiseFlatCString(aOperation
).get(), extraCount
,
19814 accumulatedResponseSize
);
19819 // TODO: Do not count entries skipped for unique cursors.
19826 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
19827 "PRELOAD: %s: Number of extra results populated: %" PRIu32
"/%" PRIu32
,
19828 "%.0s Populated (%" PRIu32
"/%" PRIu32
")",
19829 IDB_LOG_ID_STRING(mOp
.mBackgroundChildLoggingId
),
19830 mOp
.mTransactionLoggingSerialNumber
, mOp
.mLoggingSerialNumber
,
19831 PromiseFlatCString(aOperation
).get(), extraCount
, aMaxExtraCount
);
19834 template <IDBCursorType CursorType
>
19835 void Cursor
<CursorType
>::SetOptionalKeyRange(
19836 const Maybe
<SerializedKeyRange
>& aOptionalKeyRange
, bool* const aOpen
) {
19839 Key localeAwareRangeBound
;
19841 if (aOptionalKeyRange
.isSome()) {
19842 const SerializedKeyRange
& range
= aOptionalKeyRange
.ref();
19844 const bool lowerBound
= !IsIncreasingOrder(mDirection
);
19846 !range
.isOnly() && (lowerBound
? range
.lowerOpen() : range
.upperOpen());
19848 const auto& bound
=
19849 (range
.isOnly() || lowerBound
) ? range
.lower() : range
.upper();
19850 if constexpr (IsIndexCursor
) {
19851 if (this->IsLocaleAware()) {
19852 // XXX Don't we need to propagate the error?
19853 QM_TRY_UNWRAP(localeAwareRangeBound
,
19854 bound
.ToLocaleAwareKey(this->mLocale
), QM_VOID
);
19856 localeAwareRangeBound
= bound
;
19859 localeAwareRangeBound
= bound
;
19865 this->mLocaleAwareRangeBound
.init(std::move(localeAwareRangeBound
));
19868 template <IDBCursorType CursorType
>
19869 void ObjectStoreOpenOpHelper
<CursorType
>::PrepareKeyConditionClauses(
19870 const nsACString
& aDirectionClause
, const nsACString
& aQueryStart
) {
19871 const bool isIncreasingOrder
= IsIncreasingOrder(GetCursor().mDirection
);
19873 nsAutoCString keyRangeClause
;
19874 nsAutoCString continueToKeyRangeClause
;
19875 AppendConditionClause(kStmtParamNameKey
, kStmtParamNameCurrentKey
,
19876 !isIncreasingOrder
, false, keyRangeClause
);
19877 AppendConditionClause(kStmtParamNameKey
, kStmtParamNameCurrentKey
,
19878 !isIncreasingOrder
, true, continueToKeyRangeClause
);
19882 GetCursor().SetOptionalKeyRange(GetOptionalKeyRange(), &open
);
19884 if (GetOptionalKeyRange().isSome() &&
19885 !GetCursor().mLocaleAwareRangeBound
->IsUnset()) {
19886 AppendConditionClause(kStmtParamNameKey
, kStmtParamNameRangeBound
,
19887 isIncreasingOrder
, !open
, keyRangeClause
);
19888 AppendConditionClause(kStmtParamNameKey
, kStmtParamNameRangeBound
,
19889 isIncreasingOrder
, !open
, continueToKeyRangeClause
);
19893 const nsAutoCString suffix
=
19894 aDirectionClause
+ kOpenLimit
+ ":"_ns
+ kStmtParamNameLimit
;
19896 GetCursor().mContinueQueries
.init(
19897 aQueryStart
+ keyRangeClause
+ suffix
,
19898 aQueryStart
+ continueToKeyRangeClause
+ suffix
);
19901 template <IDBCursorType CursorType
>
19902 void IndexOpenOpHelper
<CursorType
>::PrepareIndexKeyConditionClause(
19903 const nsACString
& aDirectionClause
,
19904 const nsLiteralCString
& aObjectDataKeyPrefix
, nsAutoCString aQueryStart
) {
19905 const bool isIncreasingOrder
= IsIncreasingOrder(GetCursor().mDirection
);
19909 GetCursor().SetOptionalKeyRange(GetOptionalKeyRange(), &open
);
19910 if (GetOptionalKeyRange().isSome() &&
19911 !GetCursor().mLocaleAwareRangeBound
->IsUnset()) {
19912 AppendConditionClause(kColumnNameAliasSortKey
, kStmtParamNameRangeBound
,
19913 isIncreasingOrder
, !open
, aQueryStart
);
19917 nsCString continueQuery
, continueToQuery
, continuePrimaryKeyQuery
;
19920 aQueryStart
+ " AND "_ns
+
19921 GetSortKeyClause(isIncreasingOrder
? ComparisonOperator::GreaterOrEquals
19922 : ComparisonOperator::LessOrEquals
,
19923 kStmtParamNameCurrentKey
);
19925 switch (GetCursor().mDirection
) {
19926 case IDBCursorDirection::Next
:
19927 case IDBCursorDirection::Prev
:
19929 aQueryStart
+ " AND "_ns
+
19930 GetSortKeyClause(isIncreasingOrder
19931 ? ComparisonOperator::GreaterOrEquals
19932 : ComparisonOperator::LessOrEquals
,
19933 kStmtParamNameCurrentKey
) +
19935 GetSortKeyClause(isIncreasingOrder
? ComparisonOperator::GreaterThan
19936 : ComparisonOperator::LessThan
,
19937 kStmtParamNameCurrentKey
) +
19939 GetKeyClause(aObjectDataKeyPrefix
+ "object_data_key"_ns
,
19940 isIncreasingOrder
? ComparisonOperator::GreaterThan
19941 : ComparisonOperator::LessThan
,
19942 kStmtParamNameObjectStorePosition
) +
19945 continuePrimaryKeyQuery
=
19949 GetSortKeyClause(ComparisonOperator::Equals
,
19950 kStmtParamNameCurrentKey
) +
19952 GetKeyClause(aObjectDataKeyPrefix
+ "object_data_key"_ns
,
19953 isIncreasingOrder
? ComparisonOperator::GreaterOrEquals
19954 : ComparisonOperator::LessOrEquals
,
19955 kStmtParamNameObjectStorePosition
) +
19957 GetSortKeyClause(isIncreasingOrder
? ComparisonOperator::GreaterThan
19958 : ComparisonOperator::LessThan
,
19959 kStmtParamNameCurrentKey
) +
19963 case IDBCursorDirection::Nextunique
:
19964 case IDBCursorDirection::Prevunique
:
19966 aQueryStart
+ " AND "_ns
+
19967 GetSortKeyClause(isIncreasingOrder
? ComparisonOperator::GreaterThan
19968 : ComparisonOperator::LessThan
,
19969 kStmtParamNameCurrentKey
);
19973 MOZ_CRASH("Should never get here!");
19976 const nsAutoCString suffix
=
19977 aDirectionClause
+ kOpenLimit
+ ":"_ns
+ kStmtParamNameLimit
;
19978 continueQuery
+= suffix
;
19979 continueToQuery
+= suffix
;
19980 if (!continuePrimaryKeyQuery
.IsEmpty()) {
19981 continuePrimaryKeyQuery
+= suffix
;
19984 GetCursor().mContinueQueries
.init(std::move(continueQuery
),
19985 std::move(continueToQuery
),
19986 std::move(continuePrimaryKeyQuery
));
19989 template <IDBCursorType CursorType
>
19990 nsresult CommonOpenOpHelper
<CursorType
>::ProcessStatementSteps(
19991 mozIStorageStatement
* const aStmt
) {
19992 QM_TRY_INSPECT(const bool& hasResult
,
19993 MOZ_TO_RESULT_INVOKE_MEMBER(aStmt
, ExecuteStep
));
19996 SetResponse(void_t
{});
20001 auto* optPreviousKey
=
20002 IsUnique(GetCursor().mDirection
) ? &previousKey
: nullptr;
20004 QM_TRY_INSPECT(const auto& responseSize
,
20005 PopulateResponseFromStatement(aStmt
, true, optPreviousKey
));
20007 // The degree to which extra responses on OpenOp can actually be used depends
20008 // on the parameters of subsequent ContinueOp operations, see also comment in
20009 // ContinueOp::DoDatabaseWork.
20011 // TODO: We should somehow evaluate the effects of this. Maybe use a smaller
20012 // extra count than for ContinueOp?
20013 PopulateExtraResponses(aStmt
, GetCursor().mMaxExtraCount
, responseSize
,
20014 "OpenOp"_ns
, optPreviousKey
);
20019 nsresult OpenOpHelper
<IDBCursorType::ObjectStore
>::DoDatabaseWork(
20020 DatabaseConnection
* aConnection
) {
20021 MOZ_ASSERT(aConnection
);
20022 aConnection
->AssertIsOnConnectionThread();
20023 MOZ_ASSERT(GetCursor().mObjectStoreId
);
20025 AUTO_PROFILER_LABEL("Cursor::OpenOp::DoObjectStoreDatabaseWork", DOM
);
20027 const bool usingKeyRange
= GetOptionalKeyRange().isSome();
20029 const nsCString queryStart
= "SELECT "_ns
+ kColumnNameKey
+
20030 ", file_ids, data "
20031 "FROM object_data "
20032 "WHERE object_store_id = :"_ns
+
20035 const auto keyRangeClause
=
20036 DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
20037 GetOptionalKeyRange(), kColumnNameKey
);
20039 const auto& directionClause
= MakeDirectionClause(GetCursor().mDirection
);
20041 // Note: Changing the number or order of SELECT columns in the query will
20042 // require changes to CursorOpBase::PopulateResponseFromStatement.
20043 const nsCString firstQuery
= queryStart
+ keyRangeClause
+ directionClause
+
20045 IntToCString(1 + GetCursor().mMaxExtraCount
);
20047 QM_TRY_INSPECT(const auto& stmt
,
20048 aConnection
->BorrowCachedStatement(firstQuery
));
20050 QM_TRY(MOZ_TO_RESULT(
20051 stmt
->BindInt64ByName(kStmtParamNameId
, GetCursor().mObjectStoreId
)));
20053 if (usingKeyRange
) {
20054 QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
20055 GetOptionalKeyRange().ref(), &*stmt
)));
20058 // Now we need to make the query for ContinueOp.
20059 PrepareKeyConditionClauses(directionClause
, queryStart
);
20061 return ProcessStatementSteps(&*stmt
);
20064 nsresult OpenOpHelper
<IDBCursorType::ObjectStoreKey
>::DoDatabaseWork(
20065 DatabaseConnection
* aConnection
) {
20066 MOZ_ASSERT(aConnection
);
20067 aConnection
->AssertIsOnConnectionThread();
20068 MOZ_ASSERT(GetCursor().mObjectStoreId
);
20070 AUTO_PROFILER_LABEL("Cursor::OpenOp::DoObjectStoreKeyDatabaseWork", DOM
);
20072 const bool usingKeyRange
= GetOptionalKeyRange().isSome();
20074 const nsCString queryStart
= "SELECT "_ns
+ kColumnNameKey
+
20075 " FROM object_data "
20076 "WHERE object_store_id = :"_ns
+
20079 const auto keyRangeClause
=
20080 DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
20081 GetOptionalKeyRange(), kColumnNameKey
);
20083 const auto& directionClause
= MakeDirectionClause(GetCursor().mDirection
);
20085 // Note: Changing the number or order of SELECT columns in the query will
20086 // require changes to CursorOpBase::PopulateResponseFromStatement.
20087 const nsCString firstQuery
=
20088 queryStart
+ keyRangeClause
+ directionClause
+ kOpenLimit
+ "1"_ns
;
20090 QM_TRY_INSPECT(const auto& stmt
,
20091 aConnection
->BorrowCachedStatement(firstQuery
));
20093 QM_TRY(MOZ_TO_RESULT(
20094 stmt
->BindInt64ByName(kStmtParamNameId
, GetCursor().mObjectStoreId
)));
20096 if (usingKeyRange
) {
20097 QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
20098 GetOptionalKeyRange().ref(), &*stmt
)));
20101 // Now we need to make the query to get the next match.
20102 PrepareKeyConditionClauses(directionClause
, queryStart
);
20104 return ProcessStatementSteps(&*stmt
);
20107 nsresult OpenOpHelper
<IDBCursorType::Index
>::DoDatabaseWork(
20108 DatabaseConnection
* aConnection
) {
20109 MOZ_ASSERT(aConnection
);
20110 aConnection
->AssertIsOnConnectionThread();
20111 MOZ_ASSERT(GetCursor().mObjectStoreId
);
20112 MOZ_ASSERT(GetCursor().mIndexId
);
20114 AUTO_PROFILER_LABEL("Cursor::OpenOp::DoIndexDatabaseWork", DOM
);
20116 const bool usingKeyRange
= GetOptionalKeyRange().isSome();
20118 const auto indexTable
=
20119 GetCursor().mUniqueIndex
? "unique_index_data"_ns
: "index_data"_ns
;
20121 // The result of MakeColumnPairSelectionList is stored in a local variable,
20122 // since inlining it into the next statement causes a crash on some Mac OS X
20123 // builds (see https://bugzilla.mozilla.org/show_bug.cgi?id=1168606#c110).
20124 const auto columnPairSelectionList
= MakeColumnPairSelectionList(
20125 "index_table.value"_ns
, "index_table.value_locale"_ns
,
20126 kColumnNameAliasSortKey
, GetCursor().IsLocaleAware());
20127 const nsCString sortColumnAlias
=
20128 "SELECT "_ns
+ columnPairSelectionList
+ ", "_ns
;
20130 const nsAutoCString queryStart
= sortColumnAlias
+
20131 "index_table.object_data_key, "
20132 "object_data.file_ids, "
20133 "object_data.data "
20137 "JOIN object_data "
20138 "ON index_table.object_store_id = "
20139 "object_data.object_store_id "
20140 "AND index_table.object_data_key = "
20142 "WHERE index_table.index_id = :"_ns
+
20145 const auto keyRangeClause
=
20146 DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
20147 GetOptionalKeyRange(), kColumnNameAliasSortKey
);
20149 nsAutoCString directionClause
= " ORDER BY "_ns
+ kColumnNameAliasSortKey
;
20151 switch (GetCursor().mDirection
) {
20152 case IDBCursorDirection::Next
:
20153 case IDBCursorDirection::Nextunique
:
20154 directionClause
.AppendLiteral(" ASC, index_table.object_data_key ASC");
20157 case IDBCursorDirection::Prev
:
20158 directionClause
.AppendLiteral(" DESC, index_table.object_data_key DESC");
20161 case IDBCursorDirection::Prevunique
:
20162 directionClause
.AppendLiteral(" DESC, index_table.object_data_key ASC");
20166 MOZ_CRASH("Should never get here!");
20169 // Note: Changing the number or order of SELECT columns in the query will
20170 // require changes to CursorOpBase::PopulateResponseFromStatement.
20171 const nsCString firstQuery
= queryStart
+ keyRangeClause
+ directionClause
+
20173 IntToCString(1 + GetCursor().mMaxExtraCount
);
20175 QM_TRY_INSPECT(const auto& stmt
,
20176 aConnection
->BorrowCachedStatement(firstQuery
));
20178 QM_TRY(MOZ_TO_RESULT(
20179 stmt
->BindInt64ByName(kStmtParamNameId
, GetCursor().mIndexId
)));
20181 if (usingKeyRange
) {
20182 if (GetCursor().IsLocaleAware()) {
20183 QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
20184 GetOptionalKeyRange().ref(), &*stmt
, GetCursor().mLocale
)));
20186 QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
20187 GetOptionalKeyRange().ref(), &*stmt
)));
20191 // TODO: At least the last two statements are almost the same in all
20192 // DoDatabaseWork variants, consider removing this duplication.
20194 // Now we need to make the query to get the next match.
20195 PrepareKeyConditionClauses(directionClause
, std::move(queryStart
));
20197 return ProcessStatementSteps(&*stmt
);
20200 nsresult OpenOpHelper
<IDBCursorType::IndexKey
>::DoDatabaseWork(
20201 DatabaseConnection
* aConnection
) {
20202 MOZ_ASSERT(aConnection
);
20203 aConnection
->AssertIsOnConnectionThread();
20204 MOZ_ASSERT(GetCursor().mObjectStoreId
);
20205 MOZ_ASSERT(GetCursor().mIndexId
);
20207 AUTO_PROFILER_LABEL("Cursor::OpenOp::DoIndexKeyDatabaseWork", DOM
);
20209 const bool usingKeyRange
= GetOptionalKeyRange().isSome();
20212 GetCursor().mUniqueIndex
? "unique_index_data"_ns
: "index_data"_ns
;
20214 // The result of MakeColumnPairSelectionList is stored in a local variable,
20215 // since inlining it into the next statement causes a crash on some Mac OS X
20216 // builds (see https://bugzilla.mozilla.org/show_bug.cgi?id=1168606#c110).
20217 const auto columnPairSelectionList
= MakeColumnPairSelectionList(
20218 "value"_ns
, "value_locale"_ns
, kColumnNameAliasSortKey
,
20219 GetCursor().IsLocaleAware());
20220 const nsCString sortColumnAlias
=
20221 "SELECT "_ns
+ columnPairSelectionList
+ ", "_ns
;
20223 const nsAutoCString queryStart
= sortColumnAlias
+
20226 table
+ " WHERE index_id = :"_ns
+
20229 const auto keyRangeClause
=
20230 DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
20231 GetOptionalKeyRange(), kColumnNameAliasSortKey
);
20233 nsAutoCString directionClause
= " ORDER BY "_ns
+ kColumnNameAliasSortKey
;
20235 switch (GetCursor().mDirection
) {
20236 case IDBCursorDirection::Next
:
20237 case IDBCursorDirection::Nextunique
:
20238 directionClause
.AppendLiteral(" ASC, object_data_key ASC");
20241 case IDBCursorDirection::Prev
:
20242 directionClause
.AppendLiteral(" DESC, object_data_key DESC");
20245 case IDBCursorDirection::Prevunique
:
20246 directionClause
.AppendLiteral(" DESC, object_data_key ASC");
20250 MOZ_CRASH("Should never get here!");
20253 // Note: Changing the number or order of SELECT columns in the query will
20254 // require changes to CursorOpBase::PopulateResponseFromStatement.
20255 const nsCString firstQuery
=
20256 queryStart
+ keyRangeClause
+ directionClause
+ kOpenLimit
+ "1"_ns
;
20258 QM_TRY_INSPECT(const auto& stmt
,
20259 aConnection
->BorrowCachedStatement(firstQuery
));
20261 QM_TRY(MOZ_TO_RESULT(
20262 stmt
->BindInt64ByName(kStmtParamNameId
, GetCursor().mIndexId
)));
20264 if (usingKeyRange
) {
20265 if (GetCursor().IsLocaleAware()) {
20266 QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
20267 GetOptionalKeyRange().ref(), &*stmt
, GetCursor().mLocale
)));
20269 QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
20270 GetOptionalKeyRange().ref(), &*stmt
)));
20274 // Now we need to make the query to get the next match.
20275 PrepareKeyConditionClauses(directionClause
, std::move(queryStart
));
20277 return ProcessStatementSteps(&*stmt
);
20280 template <IDBCursorType CursorType
>
20281 nsresult Cursor
<CursorType
>::OpenOp::DoDatabaseWork(
20282 DatabaseConnection
* aConnection
) {
20283 MOZ_ASSERT(aConnection
);
20284 aConnection
->AssertIsOnConnectionThread();
20285 MOZ_ASSERT(mCursor
);
20286 MOZ_ASSERT(!mCursor
->mContinueQueries
);
20288 AUTO_PROFILER_LABEL("Cursor::OpenOp::DoDatabaseWork", DOM
);
20290 auto helper
= OpenOpHelper
<CursorType
>{*this};
20291 const auto rv
= helper
.DoDatabaseWork(aConnection
);
20292 if (NS_WARN_IF(NS_FAILED(rv
))) {
20299 template <IDBCursorType CursorType
>
20300 nsresult Cursor
<CursorType
>::CursorOpBase::SendSuccessResult() {
20301 AssertIsOnOwningThread();
20302 MOZ_ASSERT(mCursor
);
20303 MOZ_ASSERT(mCursor
->mCurrentlyRunningOp
== this);
20304 MOZ_ASSERT(mResponse
.type() != CursorResponse::T__None
);
20306 if (IsActorDestroyed()) {
20307 return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
;
20310 mCursor
->SendResponseInternal(mResponse
, mFiles
);
20313 mResponseSent
= true;
20318 template <IDBCursorType CursorType
>
20319 nsresult Cursor
<CursorType
>::ContinueOp::DoDatabaseWork(
20320 DatabaseConnection
* aConnection
) {
20321 MOZ_ASSERT(aConnection
);
20322 aConnection
->AssertIsOnConnectionThread();
20323 MOZ_ASSERT(mCursor
);
20324 MOZ_ASSERT(mCursor
->mObjectStoreId
);
20325 MOZ_ASSERT(!mCursor
->mContinueQueries
->mContinueQuery
.IsEmpty());
20326 MOZ_ASSERT(!mCursor
->mContinueQueries
->mContinueToQuery
.IsEmpty());
20327 MOZ_ASSERT(!mCurrentPosition
.mKey
.IsUnset());
20329 if constexpr (IsIndexCursor
) {
20331 mCursor
->mDirection
== IDBCursorDirection::Next
||
20332 mCursor
->mDirection
== IDBCursorDirection::Prev
,
20333 !mCursor
->mContinueQueries
->mContinuePrimaryKeyQuery
.IsEmpty());
20334 MOZ_ASSERT(mCursor
->mIndexId
);
20335 MOZ_ASSERT(!mCurrentPosition
.mObjectStoreKey
.IsUnset());
20338 AUTO_PROFILER_LABEL("Cursor::ContinueOp::DoDatabaseWork", DOM
);
20340 // We need to pick a query based on whether or not a key was passed to the
20341 // continue function. If not we'll grab the next item in the database that
20342 // is greater than (or less than, if we're running a PREV cursor) the current
20343 // key. If a key was passed we'll grab the next item in the database that is
20344 // greater than (or less than, if we're running a PREV cursor) or equal to the
20345 // key that was specified.
20347 // TODO: The description above is not complete, it does not take account of
20348 // ContinuePrimaryKey nor Advance.
20350 // Note: Changing the number or order of SELECT columns in the query will
20351 // require changes to CursorOpBase::PopulateResponseFromStatement.
20353 const uint32_t advanceCount
=
20354 mParams
.type() == CursorRequestParams::TAdvanceParams
20355 ? mParams
.get_AdvanceParams().count()
20357 MOZ_ASSERT(advanceCount
> 0);
20359 bool hasContinueKey
= false;
20360 bool hasContinuePrimaryKey
= false;
20362 auto explicitContinueKey
= Key
{};
20364 switch (mParams
.type()) {
20365 case CursorRequestParams::TContinueParams
:
20366 if (!mParams
.get_ContinueParams().key().IsUnset()) {
20367 hasContinueKey
= true;
20368 explicitContinueKey
= mParams
.get_ContinueParams().key();
20371 case CursorRequestParams::TContinuePrimaryKeyParams
:
20372 MOZ_ASSERT(!mParams
.get_ContinuePrimaryKeyParams().key().IsUnset());
20374 !mParams
.get_ContinuePrimaryKeyParams().primaryKey().IsUnset());
20375 MOZ_ASSERT(mCursor
->mDirection
== IDBCursorDirection::Next
||
20376 mCursor
->mDirection
== IDBCursorDirection::Prev
);
20377 hasContinueKey
= true;
20378 hasContinuePrimaryKey
= true;
20379 explicitContinueKey
= mParams
.get_ContinuePrimaryKeyParams().key();
20381 case CursorRequestParams::TAdvanceParams
:
20384 MOZ_CRASH("Should never get here!");
20387 // TODO: Whether it makes sense to preload depends on the kind of the
20388 // subsequent operations, not of the current operation. We could assume that
20389 // the subsequent operations are:
20390 // - the same as the current operation (with the same parameter values)
20391 // - as above, except for Advance, where we assume the count will be 1 on the
20393 // - basic operations (Advance with count 1 or Continue-without-key)
20395 // For now, we implement the second option for now (which correspond to
20396 // !hasContinueKey).
20398 // Based on that, we could in both cases either preload for any assumed
20399 // subsequent operations, or only for the basic operations. For now, we
20400 // preload only for an assumed basic operation. Other operations would require
20401 // more work on the client side for invalidation, and may not make any sense
20403 const uint32_t maxExtraCount
= hasContinueKey
? 0 : mCursor
->mMaxExtraCount
;
20405 QM_TRY_INSPECT(const auto& stmt
,
20406 aConnection
->BorrowCachedStatement(
20407 mCursor
->mContinueQueries
->GetContinueQuery(
20408 hasContinueKey
, hasContinuePrimaryKey
)));
20410 QM_TRY(MOZ_TO_RESULT(stmt
->BindUTF8StringByName(
20411 kStmtParamNameLimit
,
20412 IntToCString(advanceCount
+ mCursor
->mMaxExtraCount
))));
20414 QM_TRY(MOZ_TO_RESULT(stmt
->BindInt64ByName(kStmtParamNameId
, mCursor
->Id())));
20416 // Bind current key.
20417 const auto& continueKey
=
20418 hasContinueKey
? explicitContinueKey
20419 : mCurrentPosition
.GetSortKey(mCursor
->IsLocaleAware());
20420 QM_TRY(MOZ_TO_RESULT(
20421 continueKey
.BindToStatement(&*stmt
, kStmtParamNameCurrentKey
)));
20423 // Bind range bound if it is specified.
20424 if (!mCursor
->mLocaleAwareRangeBound
->IsUnset()) {
20425 QM_TRY(MOZ_TO_RESULT(mCursor
->mLocaleAwareRangeBound
->BindToStatement(
20426 &*stmt
, kStmtParamNameRangeBound
)));
20429 // Bind object store position if duplicates are allowed and we're not
20430 // continuing to a specific key.
20431 if constexpr (IsIndexCursor
) {
20432 if (!hasContinueKey
&& (mCursor
->mDirection
== IDBCursorDirection::Next
||
20433 mCursor
->mDirection
== IDBCursorDirection::Prev
)) {
20434 QM_TRY(MOZ_TO_RESULT(mCurrentPosition
.mObjectStoreKey
.BindToStatement(
20435 &*stmt
, kStmtParamNameObjectStorePosition
)));
20436 } else if (hasContinuePrimaryKey
) {
20437 QM_TRY(MOZ_TO_RESULT(
20438 mParams
.get_ContinuePrimaryKeyParams().primaryKey().BindToStatement(
20439 &*stmt
, kStmtParamNameObjectStorePosition
)));
20443 // TODO: Why do we query the records we don't need and skip them here, rather
20444 // than using a OFFSET clause in the query?
20445 for (uint32_t index
= 0; index
< advanceCount
; index
++) {
20446 QM_TRY_INSPECT(const bool& hasResult
,
20447 MOZ_TO_RESULT_INVOKE_MEMBER(&*stmt
, ExecuteStep
));
20450 mResponse
= void_t();
20456 auto* const optPreviousKey
=
20457 IsUnique(mCursor
->mDirection
) ? &previousKey
: nullptr;
20459 auto helper
= CursorOpBaseHelperBase
<CursorType
>{*this};
20460 QM_TRY_INSPECT(const auto& responseSize
, helper
.PopulateResponseFromStatement(
20461 &*stmt
, true, optPreviousKey
));
20463 helper
.PopulateExtraResponses(&*stmt
, maxExtraCount
, responseSize
,
20464 "ContinueOp"_ns
, optPreviousKey
);
20471 : mActorDestroyed(false)
20474 AssertIsOnBackgroundThread();
20477 Utils::~Utils() { MOZ_ASSERT(mActorDestroyed
); }
20479 void Utils::ActorDestroy(ActorDestroyReason aWhy
) {
20480 AssertIsOnBackgroundThread();
20481 MOZ_ASSERT(!mActorDestroyed
);
20484 mActorDestroyed
= true;
20488 mozilla::ipc::IPCResult
Utils::RecvDeleteMe() {
20489 AssertIsOnBackgroundThread();
20490 MOZ_ASSERT(!mActorDestroyed
);
20492 QM_WARNONLY_TRY(OkIf(PBackgroundIndexedDBUtilsParent::Send__delete__(this)));
20497 mozilla::ipc::IPCResult
Utils::RecvGetFileReferences(
20498 const PersistenceType
& aPersistenceType
, const nsACString
& aOrigin
,
20499 const nsAString
& aDatabaseName
, const int64_t& aFileId
, int32_t* aRefCnt
,
20500 int32_t* aDBRefCnt
, bool* aResult
) {
20501 AssertIsOnBackgroundThread();
20502 MOZ_ASSERT(aRefCnt
);
20503 MOZ_ASSERT(aDBRefCnt
);
20504 MOZ_ASSERT(aResult
);
20505 MOZ_ASSERT(!mActorDestroyed
);
20507 if (NS_WARN_IF(!IndexedDatabaseManager::Get())) {
20508 return IPC_FAIL(this, "No IndexedDatabaseManager active!");
20511 if (NS_WARN_IF(!QuotaManager::Get())) {
20512 return IPC_FAIL(this, "No QuotaManager active!");
20515 if (NS_WARN_IF(!StaticPrefs::dom_indexedDB_testing())) {
20516 return IPC_FAIL(this, "IndexedDB is not in testing mode!");
20519 if (NS_WARN_IF(!IsValidPersistenceType(aPersistenceType
))) {
20520 return IPC_FAIL(this, "PersistenceType is not valid!");
20523 if (NS_WARN_IF(aOrigin
.IsEmpty())) {
20524 return IPC_FAIL(this, "Origin is empty!");
20527 if (NS_WARN_IF(aDatabaseName
.IsEmpty())) {
20528 return IPC_FAIL(this, "DatabaseName is empty!");
20531 if (NS_WARN_IF(aFileId
== 0)) {
20532 return IPC_FAIL(this, "No FileId!");
20536 DispatchAndReturnFileReferences(aPersistenceType
, aOrigin
, aDatabaseName
,
20537 aFileId
, aRefCnt
, aDBRefCnt
, aResult
);
20538 if (NS_WARN_IF(NS_FAILED(rv
))) {
20539 return IPC_FAIL(this, "DispatchAndReturnFileReferences failed!");
20547 NS_IMPL_ISUPPORTS(DEBUGThreadSlower
, nsIThreadObserver
)
20550 DEBUGThreadSlower::OnDispatchedEvent() { MOZ_CRASH("Should never be called!"); }
20553 DEBUGThreadSlower::OnProcessNextEvent(nsIThreadInternal
* /* aThread */,
20554 bool /* aMayWait */) {
20559 DEBUGThreadSlower::AfterProcessNextEvent(nsIThreadInternal
* /* aThread */,
20560 bool /* aEventWasProcessed */) {
20561 MOZ_ASSERT(kDEBUGThreadSleepMS
);
20563 MOZ_ALWAYS_TRUE(PR_Sleep(PR_MillisecondsToInterval(kDEBUGThreadSleepMS
)) ==
20570 nsresult
FileHelper::Init() {
20571 MOZ_ASSERT(!IsOnBackgroundThread());
20573 auto fileDirectory
= mFileManager
->GetCheckedDirectory();
20574 if (NS_WARN_IF(!fileDirectory
)) {
20575 return NS_ERROR_FAILURE
;
20578 auto journalDirectory
= mFileManager
->EnsureJournalDirectory();
20579 if (NS_WARN_IF(!journalDirectory
)) {
20580 return NS_ERROR_FAILURE
;
20583 DebugOnly
<bool> exists
;
20584 MOZ_ASSERT(NS_SUCCEEDED(journalDirectory
->Exists(&exists
)));
20585 MOZ_ASSERT(exists
);
20587 DebugOnly
<bool> isDirectory
;
20588 MOZ_ASSERT(NS_SUCCEEDED(journalDirectory
->IsDirectory(&isDirectory
)));
20589 MOZ_ASSERT(isDirectory
);
20591 mFileDirectory
.init(WrapNotNullUnchecked(std::move(fileDirectory
)));
20592 mJournalDirectory
.init(WrapNotNullUnchecked(std::move(journalDirectory
)));
20597 nsCOMPtr
<nsIFile
> FileHelper::GetFile(const DatabaseFileInfo
& aFileInfo
) {
20598 MOZ_ASSERT(!IsOnBackgroundThread());
20600 return mFileManager
->GetFileForId(mFileDirectory
->get(), aFileInfo
.Id());
20603 nsCOMPtr
<nsIFile
> FileHelper::GetJournalFile(
20604 const DatabaseFileInfo
& aFileInfo
) {
20605 MOZ_ASSERT(!IsOnBackgroundThread());
20607 return mFileManager
->GetFileForId(mJournalDirectory
->get(), aFileInfo
.Id());
20610 nsresult
FileHelper::CreateFileFromStream(nsIFile
& aFile
, nsIFile
& aJournalFile
,
20611 nsIInputStream
& aInputStream
,
20613 const Maybe
<CipherKey
>& aMaybeKey
) {
20614 MOZ_ASSERT(!IsOnBackgroundThread());
20616 QM_TRY_INSPECT(const auto& exists
,
20617 MOZ_TO_RESULT_INVOKE_MEMBER(aFile
, Exists
));
20619 // DOM blobs that are being stored in IDB are cached by calling
20620 // IDBDatabase::GetOrCreateFileActorForBlob. So if the same DOM blob is stored
20621 // again under a different key or in a different object store, we just add
20622 // a new reference instead of creating a new copy (all such stored blobs share
20624 // However, it can happen that CreateFileFromStream failed due to quota
20625 // exceeded error and for some reason the orphaned file couldn't be deleted
20626 // immediately. Now, if the operation is being repeated, the DOM blob is
20627 // already cached, so it has the same file id which clashes with the orphaned
20628 // file. We could do some tricks to restore previous copy loop, but it's safer
20629 // to just delete the orphaned file and start from scratch.
20630 // This corner case is partially simulated in test_file_copy_failure.js
20632 QM_TRY_INSPECT(const auto& isFile
,
20633 MOZ_TO_RESULT_INVOKE_MEMBER(aFile
, IsFile
));
20635 QM_TRY(OkIf(isFile
), NS_ERROR_FAILURE
);
20637 QM_TRY_INSPECT(const auto& journalExists
,
20638 MOZ_TO_RESULT_INVOKE_MEMBER(aJournalFile
, Exists
));
20640 QM_TRY(OkIf(journalExists
), NS_ERROR_FAILURE
);
20642 QM_TRY_INSPECT(const auto& journalIsFile
,
20643 MOZ_TO_RESULT_INVOKE_MEMBER(aJournalFile
, IsFile
));
20645 QM_TRY(OkIf(journalIsFile
), NS_ERROR_FAILURE
);
20647 IDB_WARNING("Deleting orphaned file!");
20649 QM_TRY(MOZ_TO_RESULT(mFileManager
->SyncDeleteFile(aFile
, aJournalFile
)));
20652 // Create a journal file first.
20653 QM_TRY(MOZ_TO_RESULT(aJournalFile
.Create(nsIFile::NORMAL_FILE_TYPE
, 0644)));
20655 // Now try to copy the stream.
20656 QM_TRY_UNWRAP(nsCOMPtr
<nsIOutputStream
> fileOutputStream
,
20657 CreateFileOutputStream(mFileManager
->Type(),
20658 mFileManager
->OriginMetadata(),
20659 Client::IDB
, &aFile
));
20661 AutoTArray
<char, kFileCopyBufferSize
> buffer
;
20662 const auto actualOutputStream
=
20663 [aCompress
, &aMaybeKey
, &buffer
,
20665 std::move(fileOutputStream
)]() mutable -> nsCOMPtr
<nsIOutputStream
> {
20668 MakeRefPtr
<EncryptingOutputStream
<IndexedDBCipherStrategy
>>(
20669 std::move(baseOutputStream
), kEncryptedStreamBlockSize
,
20674 auto snappyOutputStream
=
20675 MakeRefPtr
<SnappyCompressOutputStream
>(baseOutputStream
);
20677 buffer
.SetLength(snappyOutputStream
->BlockSize());
20679 return snappyOutputStream
;
20682 buffer
.SetLength(kFileCopyBufferSize
);
20683 return std::move(baseOutputStream
);
20686 QM_TRY(MOZ_TO_RESULT(SyncCopy(aInputStream
, *actualOutputStream
,
20687 buffer
.Elements(), buffer
.Length())));
20692 class FileHelper::ReadCallback final
: public nsIInputStreamCallback
{
20694 NS_DECL_THREADSAFE_ISUPPORTS
20697 : mMutex("ReadCallback::mMutex"),
20698 mCondVar(mMutex
, "ReadCallback::mCondVar"),
20699 mInputAvailable(false) {}
20702 OnInputStreamReady(nsIAsyncInputStream
* aStream
) override
{
20703 mozilla::MutexAutoLock
autolock(mMutex
);
20705 mInputAvailable
= true;
20711 nsresult
AsyncWait(nsIAsyncInputStream
* aStream
, uint32_t aBufferSize
,
20712 nsIEventTarget
* aTarget
) {
20713 MOZ_ASSERT(aStream
);
20714 mozilla::MutexAutoLock
autolock(mMutex
);
20716 nsresult rv
= aStream
->AsyncWait(this, 0, aBufferSize
, aTarget
);
20717 if (NS_WARN_IF(NS_FAILED(rv
))) {
20721 mInputAvailable
= false;
20722 while (!mInputAvailable
) {
20730 ~ReadCallback() = default;
20732 mozilla::Mutex mMutex MOZ_UNANNOTATED
;
20733 mozilla::CondVar mCondVar
;
20734 bool mInputAvailable
;
20737 NS_IMPL_ADDREF(FileHelper::ReadCallback
);
20738 NS_IMPL_RELEASE(FileHelper::ReadCallback
);
20740 NS_INTERFACE_MAP_BEGIN(FileHelper::ReadCallback
)
20741 NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback
)
20742 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIInputStreamCallback
)
20743 NS_INTERFACE_MAP_END
20745 nsresult
FileHelper::SyncRead(nsIInputStream
& aInputStream
, char* const aBuffer
,
20746 const uint32_t aBufferSize
,
20747 uint32_t* const aRead
) {
20748 MOZ_ASSERT(!IsOnBackgroundThread());
20750 // Let's try to read, directly.
20751 nsresult rv
= aInputStream
.Read(aBuffer
, aBufferSize
, aRead
);
20752 if (NS_SUCCEEDED(rv
) || rv
!= NS_BASE_STREAM_WOULD_BLOCK
) {
20756 // We need to proceed async.
20757 nsCOMPtr
<nsIAsyncInputStream
> asyncStream
= do_QueryInterface(&aInputStream
);
20758 if (!asyncStream
) {
20762 if (!mReadCallback
) {
20763 mReadCallback
.init(MakeNotNull
<RefPtr
<ReadCallback
>>());
20766 // We just need any thread with an event loop for receiving the
20767 // OnInputStreamReady callback. Let's use the I/O thread.
20768 nsCOMPtr
<nsIEventTarget
> target
=
20769 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
20770 MOZ_ASSERT(target
);
20772 rv
= (*mReadCallback
)->AsyncWait(asyncStream
, aBufferSize
, target
);
20773 if (NS_WARN_IF(NS_FAILED(rv
))) {
20777 return SyncRead(aInputStream
, aBuffer
, aBufferSize
, aRead
);
20780 nsresult
FileHelper::SyncCopy(nsIInputStream
& aInputStream
,
20781 nsIOutputStream
& aOutputStream
,
20782 char* const aBuffer
, const uint32_t aBufferSize
) {
20783 MOZ_ASSERT(!IsOnBackgroundThread());
20785 AUTO_PROFILER_LABEL("FileHelper::SyncCopy", DOM
);
20791 rv
= SyncRead(aInputStream
, aBuffer
, aBufferSize
, &numRead
);
20792 if (NS_WARN_IF(NS_FAILED(rv
))) {
20801 rv
= aOutputStream
.Write(aBuffer
, numRead
, &numWrite
);
20802 if (rv
== NS_ERROR_FILE_NO_DEVICE_SPACE
) {
20803 rv
= NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR
;
20805 if (NS_WARN_IF(NS_FAILED(rv
))) {
20809 if (NS_WARN_IF(numWrite
!= numRead
)) {
20810 rv
= NS_ERROR_FAILURE
;
20815 if (NS_SUCCEEDED(rv
)) {
20816 rv
= aOutputStream
.Flush();
20817 if (NS_WARN_IF(NS_FAILED(rv
))) {
20822 nsresult rv2
= aOutputStream
.Close();
20823 if (NS_WARN_IF(NS_FAILED(rv2
))) {
20824 return NS_SUCCEEDED(rv
) ? rv2
: rv
;
20830 } // namespace dom::indexedDB
20831 } // namespace mozilla
20834 #undef IDB_DEBUG_LOG