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/SnappyCompressOutputStream.h"
86 #include "mozilla/SpinEventLoopUntil.h"
87 #include "mozilla/StaticPtr.h"
88 #include "mozilla/TimeStamp.h"
89 #include "mozilla/UniquePtr.h"
90 #include "mozilla/Unused.h"
91 #include "mozilla/Variant.h"
92 #include "mozilla/dom/BlobImpl.h"
93 #include "mozilla/dom/ContentParent.h"
94 #include "mozilla/dom/FileBlobImpl.h"
95 #include "mozilla/dom/FlippedOnce.h"
96 #include "mozilla/dom/IDBCursorBinding.h"
97 #include "mozilla/dom/IDBFactory.h"
98 #include "mozilla/dom/IPCBlob.h"
99 #include "mozilla/dom/IPCBlobUtils.h"
100 #include "mozilla/dom/IndexedDatabase.h"
101 #include "mozilla/dom/Nullable.h"
102 #include "mozilla/dom/PContentParent.h"
103 #include "mozilla/dom/ScriptSettings.h"
104 #include "mozilla/dom/indexedDB/IDBResult.h"
105 #include "mozilla/dom/indexedDB/Key.h"
106 #include "mozilla/dom/indexedDB/PBackgroundIDBCursor.h"
107 #include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h"
108 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabase.h"
109 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h"
110 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
111 #include "mozilla/dom/indexedDB/PBackgroundIDBFactory.h"
112 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h"
113 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h"
114 #include "mozilla/dom/indexedDB/PBackgroundIDBRequest.h"
115 #include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h"
116 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
117 #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h"
118 #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h"
119 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsParent.h"
120 #include "mozilla/dom/ipc/IdType.h"
121 #include "mozilla/dom/quota/Assertions.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/DebugOnlyMacro.h"
127 #include "mozilla/dom/quota/DirectoryLock.h"
128 #include "mozilla/dom/quota/DecryptingInputStream_impl.h"
129 #include "mozilla/dom/quota/EncryptingOutputStream_impl.h"
130 #include "mozilla/dom/quota/FileStreams.h"
131 #include "mozilla/dom/quota/OriginScope.h"
132 #include "mozilla/dom/quota/PersistenceType.h"
133 #include "mozilla/dom/quota/QuotaCommon.h"
134 #include "mozilla/dom/quota/QuotaManager.h"
135 #include "mozilla/dom/quota/QuotaObject.h"
136 #include "mozilla/dom/quota/ResultExtensions.h"
137 #include "mozilla/dom/quota/UsageInfo.h"
138 #include "mozilla/fallible.h"
139 #include "mozilla/ipc/BackgroundParent.h"
140 #include "mozilla/ipc/BackgroundUtils.h"
141 #include "mozilla/ipc/InputStreamParams.h"
142 #include "mozilla/ipc/PBackgroundParent.h"
143 #include "mozilla/ipc/PBackgroundSharedTypes.h"
144 #include "mozilla/ipc/ProtocolUtils.h"
145 #include "mozilla/mozalloc.h"
146 #include "mozilla/storage/Variant.h"
147 #include "nsBaseHashtable.h"
148 #include "nsCOMPtr.h"
149 #include "nsClassHashtable.h"
150 #include "nsContentUtils.h"
151 #include "nsTHashMap.h"
154 #include "nsEscape.h"
155 #include "nsHashKeys.h"
156 #include "nsIAsyncInputStream.h"
158 #include "nsIDUtils.h"
159 #include "nsIDirectoryEnumerator.h"
160 #include "nsIEventTarget.h"
162 #include "nsIFileProtocolHandler.h"
163 #include "nsIFileStreams.h"
164 #include "nsIFileURL.h"
165 #include "nsIInputStream.h"
166 #include "nsIOutputStream.h"
167 #include "nsIProtocolHandler.h"
168 #include "nsIRunnable.h"
169 #include "nsISupports.h"
170 #include "nsISupportsPriority.h"
171 #include "nsISupportsUtils.h"
172 #include "nsIThread.h"
173 #include "nsIThreadInternal.h"
174 #include "nsITimer.h"
175 #include "nsIURIMutator.h"
176 #include "nsIVariant.h"
177 #include "nsLiteralString.h"
178 #include "nsNetCID.h"
179 #include "nsPrintfCString.h"
180 #include "nsProxyRelease.h"
181 #include "nsServiceManagerUtils.h"
182 #include "nsStreamUtils.h"
183 #include "nsString.h"
184 #include "nsStringFlags.h"
185 #include "nsStringFwd.h"
186 #include "nsTArray.h"
187 #include "nsTHashSet.h"
188 #include "nsTHashtable.h"
189 #include "nsTLiteralString.h"
190 #include "nsTStringRepr.h"
191 #include "nsThreadPool.h"
192 #include "nsThreadUtils.h"
194 #include "prinrval.h"
196 #include "prsystem.h"
197 #include "prthread.h"
200 #include "snappy/snappy.h"
207 #define IDB_DEBUG_LOG(_args) \
208 MOZ_LOG(IndexedDatabaseManager::GetLoggingModule(), LogLevel::Debug, _args)
210 #if defined(MOZ_WIDGET_ANDROID)
214 // Helper macros to reduce assertion verbosity
215 // AUUF == ASSERT_UNREACHABLE_UNLESS_FUZZING
218 # define NS_AUUF_OR_WARN(...) NS_WARNING(__VA_ARGS__)
220 # define NS_AUUF_OR_WARN(...) MOZ_ASSERT(false, __VA_ARGS__)
222 # define NS_AUUF_OR_WARN_IF(cond) \
224 if (MOZ_UNLIKELY(aCond)) { \
225 NS_AUUF_OR_WARN(#cond); \
230 # define NS_AUUF_OR_WARN(...) \
233 # define NS_AUUF_OR_WARN_IF(cond) static_cast<bool>(cond)
238 namespace dom::indexedDB
{
240 using namespace mozilla::dom::quota
;
241 using namespace mozilla::ipc
;
242 using mozilla::dom::quota::Client
;
246 class ConnectionPool
;
248 struct DatabaseActorInfo
;
250 class DatabaseLoggingInfo
;
251 class DatabaseMaintenance
;
254 class OpenDatabaseOp
;
255 class TransactionBase
;
256 class TransactionDatabaseOperationBase
;
257 class VersionChangeTransaction
;
258 template <bool StatementHasIndexKeyBindings
>
259 struct ValuePopulateResponseHelper
;
261 /*******************************************************************************
263 ******************************************************************************/
265 const int32_t kStorageProgressGranularity
= 1000;
267 // Changing the value here will override the page size of new databases only.
268 // A journal mode change and VACUUM are needed to change existing databases, so
269 // the best way to do that is to use the schema version upgrade mechanism.
270 const uint32_t kSQLitePageSizeOverride
=
277 static_assert(kSQLitePageSizeOverride
== /* mozStorage default */ 0 ||
278 (kSQLitePageSizeOverride
% 2 == 0 &&
279 kSQLitePageSizeOverride
>= 512 &&
280 kSQLitePageSizeOverride
<= 65536),
281 "Must be 0 (disabled) or a power of 2 between 512 and 65536!");
283 // Set to -1 to use SQLite's default, 0 to disable, or some positive number to
284 // enforce a custom limit.
285 const int32_t kMaxWALPages
= 5000; // 20MB on desktop, 10MB on mobile.
287 // Set to some multiple of the page size to grow the database in larger chunks.
288 const uint32_t kSQLiteGrowthIncrement
= kSQLitePageSizeOverride
* 2;
290 static_assert(kSQLiteGrowthIncrement
>= 0 &&
291 kSQLiteGrowthIncrement
% kSQLitePageSizeOverride
== 0 &&
292 kSQLiteGrowthIncrement
< uint32_t(INT32_MAX
),
293 "Must be 0 (disabled) or a positive multiple of the page size!");
295 // The maximum number of threads that can be used for database activity at a
296 // single time. Please keep in sync with the constants in
297 // test_connection_idle_maintenance*.js tests
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. Please keep in
311 // sync with the timeouts in test_connection_idle_maintenance*.js tests
312 const uint32_t kConnectionIdleMaintenanceMS
= 2 * 1000; // 2 seconds
314 // The length of time that database connections will be held open after all
315 // transactions and maintenance have completed.
316 const uint32_t kConnectionIdleCloseMS
= 10 * 1000; // 10 seconds
318 // The length of time that idle threads will stay alive before being shut down.
319 const uint32_t kConnectionThreadIdleMS
= 30 * 1000; // 30 seconds
321 #define SAVEPOINT_CLAUSE "SAVEPOINT sp;"_ns
323 // For efficiency reasons, kEncryptedStreamBlockSize must be a multiple of large
325 static_assert(kEncryptedStreamBlockSize
% 4096 == 0);
326 // Similarly, the file copy buffer size must be a multiple of the encrypted
328 static_assert(kFileCopyBufferSize
% kEncryptedStreamBlockSize
== 0);
330 constexpr auto kFileManagerDirectoryNameSuffix
= u
".files"_ns
;
331 constexpr auto kSQLiteSuffix
= u
".sqlite"_ns
;
332 constexpr auto kSQLiteJournalSuffix
= u
".sqlite-journal"_ns
;
333 constexpr auto kSQLiteSHMSuffix
= u
".sqlite-shm"_ns
;
334 constexpr auto kSQLiteWALSuffix
= u
".sqlite-wal"_ns
;
336 // The following constants define all names of binding parameters in statements,
337 // where they are bound by name. This should include all parameter names which
338 // are bound by name. Binding may be done by index when the statement definition
339 // and binding are done in the same local scope, and no other reasons prevent
340 // using the indexes (e.g. multiple statement variants with differing number or
341 // order of parameters). Neither the styles of specifying parameter names
342 // (literally vs. via these constants) nor the binding styles (by index vs. by
343 // name) should not be mixed for the same statement. The decision must be made
344 // for each statement based on the proximity of statement and binding calls.
345 constexpr auto kStmtParamNameCurrentKey
= "current_key"_ns
;
346 constexpr auto kStmtParamNameRangeBound
= "range_bound"_ns
;
347 constexpr auto kStmtParamNameObjectStorePosition
= "object_store_position"_ns
;
348 constexpr auto kStmtParamNameLowerKey
= "lower_key"_ns
;
349 constexpr auto kStmtParamNameUpperKey
= "upper_key"_ns
;
350 constexpr auto kStmtParamNameKey
= "key"_ns
;
351 constexpr auto kStmtParamNameObjectStoreId
= "object_store_id"_ns
;
352 constexpr auto kStmtParamNameIndexId
= "index_id"_ns
;
353 // TODO: Maybe the uses of kStmtParamNameId should be replaced by more
354 // specific constants such as kStmtParamNameObjectStoreId.
355 constexpr auto kStmtParamNameId
= "id"_ns
;
356 constexpr auto kStmtParamNameValue
= "value"_ns
;
357 constexpr auto kStmtParamNameObjectDataKey
= "object_data_key"_ns
;
358 constexpr auto kStmtParamNameIndexDataValues
= "index_data_values"_ns
;
359 constexpr auto kStmtParamNameData
= "data"_ns
;
360 constexpr auto kStmtParamNameFileIds
= "file_ids"_ns
;
361 constexpr auto kStmtParamNameValueLocale
= "value_locale"_ns
;
362 constexpr auto kStmtParamNameLimit
= "limit"_ns
;
364 // The following constants define some names of columns in tables, which are
365 // referred to in remote locations, e.g. in calls to
366 // GetBindingClauseForKeyRange.
367 constexpr auto kColumnNameKey
= "key"_ns
;
368 constexpr auto kColumnNameValue
= "value"_ns
;
369 constexpr auto kColumnNameAliasSortKey
= "sort_column"_ns
;
371 // SQL fragments used at multiple locations.
372 constexpr auto kOpenLimit
= " LIMIT "_ns
;
374 // The deletion marker file is created before RemoveDatabaseFilesAndDirectory
375 // begins deleting a database. It is removed as the last step of deletion. If a
376 // deletion marker file is found when initializing the origin, the deletion
377 // routine is run again to ensure that the database and all of its related files
378 // are removed. The primary goal of this mechanism is to avoid situations where
379 // a database has been partially deleted, leading to inconsistent state for the
381 constexpr auto kIdbDeletionMarkerFilePrefix
= u
"idb-deleting-"_ns
;
383 const uint32_t kDeleteTimeoutMs
= 1000;
387 const int32_t kDEBUGThreadPriority
= nsISupportsPriority::PRIORITY_NORMAL
;
388 const uint32_t kDEBUGThreadSleepMS
= 0;
392 /*******************************************************************************
394 ******************************************************************************/
396 // Can be instantiated either on the QuotaManager IO thread or on a
397 // versionchange transaction thread. These threads can never race so this is
399 struct FullIndexMetadata
{
400 IndexMetadata mCommonMetadata
= {0, nsString(), KeyPath(0), nsCString(),
401 false, false, false};
403 FlippedOnce
<false> mDeleted
;
405 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullIndexMetadata
)
408 ~FullIndexMetadata() = default;
411 using IndexTable
= nsTHashMap
<nsUint64HashKey
, SafeRefPtr
<FullIndexMetadata
>>;
413 // Can be instantiated either on the QuotaManager IO thread or on a
414 // versionchange transaction thread. These threads can never race so this is
416 struct FullObjectStoreMetadata
{
417 ObjectStoreMetadata mCommonMetadata
;
420 // The auto increment ids are touched on both the background thread and the
421 // transaction I/O thread, and they must be kept in sync, so we need a mutex
423 struct AutoIncrementIds
{
427 DataMutex
<AutoIncrementIds
> mAutoIncrementIds
;
429 FlippedOnce
<false> mDeleted
;
431 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullObjectStoreMetadata
);
433 bool HasLiveIndexes() const;
435 FullObjectStoreMetadata(ObjectStoreMetadata aCommonMetadata
,
436 const AutoIncrementIds
& aAutoIncrementIds
)
437 : mCommonMetadata
{std::move(aCommonMetadata
)},
438 mAutoIncrementIds
{AutoIncrementIds
{aAutoIncrementIds
},
439 "FullObjectStoreMetadata"} {}
442 ~FullObjectStoreMetadata() = default;
445 using ObjectStoreTable
=
446 nsTHashMap
<nsUint64HashKey
, SafeRefPtr
<FullObjectStoreMetadata
>>;
449 std::is_same_v
<IndexOrObjectStoreId
,
450 std::remove_cv_t
<std::remove_reference_t
<
451 decltype(std::declval
<const ObjectStoreGetParams
&>()
452 .objectStoreId())>>>);
455 IndexOrObjectStoreId
,
456 std::remove_cv_t
<std::remove_reference_t
<
457 decltype(std::declval
<const IndexGetParams
&>().objectStoreId())>>>);
459 struct FullDatabaseMetadata final
: AtomicSafeRefCounted
<FullDatabaseMetadata
> {
460 DatabaseMetadata mCommonMetadata
;
461 nsCString mDatabaseId
;
463 ObjectStoreTable mObjectStores
;
465 IndexOrObjectStoreId mNextObjectStoreId
= 0;
466 IndexOrObjectStoreId mNextIndexId
= 0;
469 explicit FullDatabaseMetadata(const DatabaseMetadata
& aCommonMetadata
)
470 : mCommonMetadata(aCommonMetadata
) {
471 AssertIsOnBackgroundThread();
474 [[nodiscard
]] SafeRefPtr
<FullDatabaseMetadata
> Duplicate() const;
476 MOZ_DECLARE_REFCOUNTED_TYPENAME(FullDatabaseMetadata
)
479 template <class Enumerable
>
480 auto MatchMetadataNameOrId(const Enumerable
& aEnumerable
,
481 IndexOrObjectStoreId aId
,
482 Maybe
<const nsAString
&> aName
= Nothing()) {
483 AssertIsOnBackgroundThread();
486 const auto it
= std::find_if(
487 aEnumerable
.cbegin(), aEnumerable
.cend(),
488 [aId
, aName
](const auto& entry
) {
489 MOZ_ASSERT(entry
.GetKey() != 0);
491 const auto& value
= entry
.GetData();
494 return !value
->mDeleted
&&
495 (aId
== value
->mCommonMetadata
.id() ||
496 (aName
&& *aName
== value
->mCommonMetadata
.name()));
499 return ToMaybeRef(it
!= aEnumerable
.cend() ? it
->GetData().unsafeGetRawPtr()
503 /*******************************************************************************
505 ******************************************************************************/
507 // WARNING: the hash function used for the database name must not change.
508 // That's why this function exists separately from mozilla::HashString(), even
509 // though it is (at the time of writing) equivalent. See bug 780408 and bug
510 // 940315 for details.
511 uint32_t HashName(const nsAString
& aName
) {
513 static uint32_t RotateBitsLeft32(uint32_t aValue
, uint8_t aBits
) {
514 MOZ_ASSERT(aBits
< 32);
515 return (aValue
<< aBits
) | (aValue
>> (32 - aBits
));
519 static const uint32_t kGoldenRatioU32
= 0x9e3779b9u
;
521 return std::accumulate(aName
.BeginReading(), aName
.EndReading(), uint32_t(0),
522 [](uint32_t hash
, char16_t ch
) {
523 return kGoldenRatioU32
*
524 (Helper::RotateBitsLeft32(hash
, 5) ^ ch
);
528 nsresult
ClampResultCode(nsresult aResultCode
) {
529 if (NS_SUCCEEDED(aResultCode
) ||
530 NS_ERROR_GET_MODULE(aResultCode
) == NS_ERROR_MODULE_DOM_INDEXEDDB
) {
534 switch (aResultCode
) {
535 case NS_ERROR_FILE_NO_DEVICE_SPACE
:
536 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR
;
537 case NS_ERROR_STORAGE_CONSTRAINT
:
538 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR
;
541 nsPrintfCString
message("Converting non-IndexedDB error code (0x%" PRIX32
543 "NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR",
544 static_cast<uint32_t>(aResultCode
));
545 NS_WARNING(message
.get());
551 IDB_REPORT_INTERNAL_ERR();
552 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
555 Result
<nsCOMPtr
<nsIFileURL
>, nsresult
> GetDatabaseFileURL(
556 nsIFile
& aDatabaseFile
, const int64_t aDirectoryLockId
,
557 const Maybe
<CipherKey
>& aMaybeKey
) {
558 MOZ_ASSERT(aDirectoryLockId
>= -1);
561 const auto& protocolHandler
,
562 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr
<nsIProtocolHandler
>,
563 MOZ_SELECT_OVERLOAD(do_GetService
),
564 NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX
"file"));
566 QM_TRY_INSPECT(const auto& fileHandler
,
567 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr
<nsIFileProtocolHandler
>,
568 MOZ_SELECT_OVERLOAD(do_QueryInterface
),
571 QM_TRY_INSPECT(const auto& mutator
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
572 nsCOMPtr
<nsIURIMutator
>, fileHandler
,
573 NewFileURIMutator
, &aDatabaseFile
));
575 // aDirectoryLockId should only be -1 when we are called
576 // - from DatabaseFileManager::InitDirectory when the temporary storage
577 // hasn't been initialized yet. At that time, the in-memory objects (e.g.
578 // OriginInfo) are only being created so it doesn't make sense to tunnel
579 // quota information to QuotaVFS to get corresponding QuotaObject instances
581 // - from DeleteDatabaseOp::LoadPreviousVersion, since this might require
582 // temporarily exceeding the quota limit before the database can be
584 const nsCString directoryLockIdClause
=
585 "&directoryLockId="_ns
+ IntToCString(aDirectoryLockId
);
587 const auto keyClause
= [&aMaybeKey
] {
588 nsAutoCString keyClause
;
590 keyClause
.AssignLiteral("&key=");
591 for (uint8_t byte
: IndexedDBCipherStrategy::SerializeKey(*aMaybeKey
)) {
592 keyClause
.AppendPrintf("%02x", byte
);
598 QM_TRY_UNWRAP(auto result
, ([&mutator
, &directoryLockIdClause
, &keyClause
] {
599 nsCOMPtr
<nsIFileURL
> result
;
600 nsresult rv
= NS_MutateURI(mutator
)
601 .SetQuery("cache=private"_ns
+
602 directoryLockIdClause
+ keyClause
)
604 return NS_SUCCEEDED(rv
)
605 ? Result
<nsCOMPtr
<nsIFileURL
>, nsresult
>{result
}
612 nsresult
SetDefaultPragmas(mozIStorageConnection
& aConnection
) {
613 MOZ_ASSERT(!NS_IsMainThread());
615 static constexpr auto kBuiltInPragmas
=
616 // We use foreign keys in DEBUG builds only because there is a performance
617 // cost to using them.
618 "PRAGMA foreign_keys = "
626 // The "INSERT OR REPLACE" statement doesn't fire the update trigger,
627 // instead it fires only the insert trigger. This confuses the update
628 // refcount function. This behavior changes with enabled recursive
629 // triggers, so the statement fires the delete trigger first and then the
631 "PRAGMA recursive_triggers = ON;"
633 // We aggressively truncate the database file when idle so don't bother
634 // overwriting the WAL with 0 during active periods.
635 "PRAGMA secure_delete = OFF;"_ns
;
637 QM_TRY(MOZ_TO_RESULT(aConnection
.ExecuteSimpleSQL(kBuiltInPragmas
)));
639 QM_TRY(MOZ_TO_RESULT(aConnection
.ExecuteSimpleSQL(nsAutoCString
{
640 "PRAGMA synchronous = "_ns
+
641 (IndexedDatabaseManager::FullSynchronous() ? "FULL"_ns
: "NORMAL"_ns
) +
645 if (kSQLiteGrowthIncrement
) {
646 // This is just an optimization so ignore the failure if the disk is
647 // currently too full.
648 QM_TRY(QM_OR_ELSE_WARN_IF(
651 aConnection
.SetGrowthIncrement(kSQLiteGrowthIncrement
, ""_ns
)),
653 IsSpecificError
<NS_ERROR_FILE_TOO_BIG
>,
662 nsresult
SetJournalMode(mozIStorageConnection
& aConnection
) {
663 MOZ_ASSERT(!NS_IsMainThread());
665 // Try enabling WAL mode. This can fail in various circumstances so we have to
666 // check the results here.
667 constexpr auto journalModeQueryStart
= "PRAGMA journal_mode = "_ns
;
668 constexpr auto journalModeWAL
= "wal"_ns
;
670 QM_TRY_INSPECT(const auto& stmt
,
671 CreateAndExecuteSingleStepStatement(
672 aConnection
, journalModeQueryStart
+ journalModeWAL
));
675 const auto& journalMode
,
676 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCString
, *stmt
, GetUTF8String
, 0));
678 if (journalMode
.Equals(journalModeWAL
)) {
679 // WAL mode successfully enabled. Maybe set limits on its size here.
680 if (kMaxWALPages
>= 0) {
681 QM_TRY(MOZ_TO_RESULT(aConnection
.ExecuteSimpleSQL(
682 "PRAGMA wal_autocheckpoint = "_ns
+ IntToCString(kMaxWALPages
))));
685 NS_WARNING("Failed to set WAL mode, falling back to normal journal mode.");
687 QM_TRY(MOZ_TO_RESULT(
688 aConnection
.ExecuteSimpleSQL(journalModeQueryStart
+ "truncate"_ns
)));
695 Result
<MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>>, nsresult
> OpenDatabase(
696 mozIStorageService
& aStorageService
, nsIFileURL
& aFileURL
,
697 const uint32_t aTelemetryId
= 0) {
698 const nsAutoCString telemetryFilename
=
699 aTelemetryId
? "indexedDB-"_ns
+ IntToCString(aTelemetryId
) +
700 NS_ConvertUTF16toUTF8(kSQLiteSuffix
)
703 QM_TRY_UNWRAP(auto connection
,
704 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
705 nsCOMPtr
<mozIStorageConnection
>, aStorageService
,
706 OpenDatabaseWithFileURL
, &aFileURL
, telemetryFilename
,
707 mozIStorageService::CONNECTION_INTERRUPTIBLE
));
709 return WrapMovingNotNull(std::move(connection
));
712 Result
<MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>>, nsresult
>
713 OpenDatabaseAndHandleBusy(mozIStorageService
& aStorageService
,
714 nsIFileURL
& aFileURL
,
715 const uint32_t aTelemetryId
= 0) {
716 MOZ_ASSERT(!NS_IsMainThread());
717 MOZ_ASSERT(!IsOnBackgroundThread());
719 using ConnectionType
= Maybe
<MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>>>;
721 QM_TRY_UNWRAP(auto connection
,
724 OpenDatabase(aStorageService
, aFileURL
, aTelemetryId
)
725 .map([](auto connection
) -> ConnectionType
{
726 return Some(std::move(connection
));
729 IsSpecificError
<NS_ERROR_STORAGE_BUSY
>,
731 ErrToDefaultOk
<ConnectionType
>));
733 if (connection
.isNothing()) {
737 MOZ_ALWAYS_SUCCEEDS(aFileURL
.GetFileName(path
));
739 nsPrintfCString
message(
740 "Received NS_ERROR_STORAGE_BUSY when attempting to open database "
741 "'%s', retrying for up to 10 seconds",
743 NS_WARNING(message
.get());
747 // Another thread must be checkpointing the WAL. Wait up to 10 seconds for
749 const TimeStamp start
= TimeStamp::NowLoRes();
752 PR_Sleep(PR_MillisecondsToInterval(100));
754 QM_TRY_UNWRAP(connection
,
757 OpenDatabase(aStorageService
, aFileURL
, aTelemetryId
)
758 .map([](auto connection
) -> ConnectionType
{
759 return Some(std::move(connection
));
762 ([&start
](nsresult aValue
) {
763 return aValue
== NS_ERROR_STORAGE_BUSY
&&
764 TimeStamp::NowLoRes() - start
<=
765 TimeDuration::FromSeconds(10);
768 ErrToDefaultOk
<ConnectionType
>));
769 } while (connection
.isNothing());
772 return connection
.extract();
775 // Returns true if a given nsIFile exists and is a directory. Returns false if
776 // it doesn't exist. Returns an error if it exists, but is not a directory, or
777 // any other error occurs.
778 Result
<bool, nsresult
> ExistsAsDirectory(nsIFile
& aDirectory
) {
779 QM_TRY_INSPECT(const bool& exists
,
780 MOZ_TO_RESULT_INVOKE_MEMBER(aDirectory
, Exists
));
783 QM_TRY_INSPECT(const bool& isDirectory
,
784 MOZ_TO_RESULT_INVOKE_MEMBER(aDirectory
, IsDirectory
));
786 QM_TRY(OkIf(isDirectory
), Err(NS_ERROR_FAILURE
));
792 constexpr nsresult
mapNoDeviceSpaceError(nsresult aRv
) {
793 if (aRv
== NS_ERROR_FILE_NO_DEVICE_SPACE
) {
794 // mozstorage translates SQLITE_FULL to
795 // NS_ERROR_FILE_NO_DEVICE_SPACE, which we know better as
796 // NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
797 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR
;
802 Result
<MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>>, nsresult
>
803 CreateStorageConnection(nsIFile
& aDBFile
, nsIFile
& aFMDirectory
,
804 const nsAString
& aName
, const nsACString
& aOrigin
,
805 const int64_t aDirectoryLockId
,
806 const uint32_t aTelemetryId
,
807 const Maybe
<CipherKey
>& aMaybeKey
) {
808 AssertIsOnIOThread();
809 MOZ_ASSERT(aDirectoryLockId
>= -1);
811 AUTO_PROFILER_LABEL("CreateStorageConnection", DOM
);
813 QM_TRY_INSPECT(const auto& dbFileUrl
,
814 GetDatabaseFileURL(aDBFile
, aDirectoryLockId
, aMaybeKey
));
816 QM_TRY_INSPECT(const auto& storageService
,
817 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr
<mozIStorageService
>,
818 MOZ_SELECT_OVERLOAD(do_GetService
),
819 MOZ_STORAGE_SERVICE_CONTRACTID
));
825 OpenDatabaseAndHandleBusy(*storageService
, *dbFileUrl
, aTelemetryId
)
826 .map([](auto connection
) -> nsCOMPtr
<mozIStorageConnection
> {
827 return std::move(connection
).unwrapBasePtr();
830 ([&aName
](nsresult aValue
) {
831 // If we're just opening the database during origin initialization,
832 // then we don't want to erase any files. The failure here will fail
833 // origin initialization too.
834 return IsDatabaseCorruptionError(aValue
) && !aName
.IsVoid();
837 ErrToDefaultOk
<nsCOMPtr
<mozIStorageConnection
>>));
840 // XXX Shouldn't we also update quota usage?
842 // Nuke the database file.
843 QM_TRY(MOZ_TO_RESULT(aDBFile
.Remove(false)));
844 QM_TRY_INSPECT(const bool& existsAsDirectory
,
845 ExistsAsDirectory(aFMDirectory
));
847 if (existsAsDirectory
) {
848 QM_TRY(MOZ_TO_RESULT(aFMDirectory
.Remove(true)));
851 QM_TRY_UNWRAP(connection
, OpenDatabaseAndHandleBusy(
852 *storageService
, *dbFileUrl
, aTelemetryId
));
855 QM_TRY(MOZ_TO_RESULT(SetDefaultPragmas(*connection
)));
856 QM_TRY(MOZ_TO_RESULT(connection
->EnableModule("filesystem"_ns
)));
858 // Check to make sure that the database schema is correct.
859 QM_TRY_INSPECT(const int32_t& schemaVersion
,
860 MOZ_TO_RESULT_INVOKE_MEMBER(connection
, GetSchemaVersion
));
862 // Unknown schema will fail origin initialization too.
863 QM_TRY(OkIf(schemaVersion
|| !aName
.IsVoid()),
864 Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
), [](const auto&) {
865 IDB_WARNING("Unable to open IndexedDB database, schema is not set!");
869 OkIf(schemaVersion
<= kSQLiteSchemaVersion
),
870 Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
), [](const auto&) {
871 IDB_WARNING("Unable to open IndexedDB database, schema is too high!");
874 bool journalModeSet
= false;
876 if (schemaVersion
!= kSQLiteSchemaVersion
) {
877 const bool newDatabase
= !schemaVersion
;
880 // Set the page size first.
881 const auto sqlitePageSizeOverride
=
882 aMaybeKey
? 8192 : kSQLitePageSizeOverride
;
883 if (sqlitePageSizeOverride
) {
884 QM_TRY(MOZ_TO_RESULT(connection
->ExecuteSimpleSQL(nsPrintfCString(
885 "PRAGMA page_size = %" PRIu32
";", sqlitePageSizeOverride
))));
888 // We have to set the auto_vacuum mode before opening a transaction.
889 QM_TRY((MOZ_TO_RESULT_INVOKE_MEMBER(
890 connection
, ExecuteSimpleSQL
,
892 // Turn on full auto_vacuum mode to reclaim disk space on
893 // mobile devices (at the cost of some COMMIT speed).
894 "PRAGMA auto_vacuum = FULL;"_ns
896 // Turn on incremental auto_vacuum mode on desktop builds.
897 "PRAGMA auto_vacuum = INCREMENTAL;"_ns
900 .mapErr(mapNoDeviceSpaceError
)));
902 QM_TRY(MOZ_TO_RESULT(SetJournalMode(*connection
)));
904 journalModeSet
= true;
907 // Disable foreign key support while upgrading. This has to be done before
908 // starting a transaction.
910 connection
->ExecuteSimpleSQL("PRAGMA foreign_keys = OFF;"_ns
));
914 bool vacuumNeeded
= false;
916 mozStorageTransaction
transaction(
917 connection
.get(), false, mozIStorageConnection::TRANSACTION_IMMEDIATE
);
919 QM_TRY(MOZ_TO_RESULT(transaction
.Start()));
922 QM_TRY(MOZ_TO_RESULT(CreateTables(*connection
)));
927 const int32_t& schemaVersion
,
928 MOZ_TO_RESULT_INVOKE_MEMBER(connection
, GetSchemaVersion
),
929 QM_ASSERT_UNREACHABLE
);
930 MOZ_ASSERT(schemaVersion
== kSQLiteSchemaVersion
);
934 // The parameter names are not used, parameters are bound by index only
935 // locally in the same function.
938 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
939 nsCOMPtr
<mozIStorageStatement
>, connection
, CreateStatement
,
940 "INSERT INTO database (name, origin) "
941 "VALUES (:name, :origin)"_ns
));
943 QM_TRY(MOZ_TO_RESULT(stmt
->BindStringByIndex(0, aName
)));
944 QM_TRY(MOZ_TO_RESULT(stmt
->BindUTF8StringByIndex(1, aOrigin
)));
945 QM_TRY(MOZ_TO_RESULT(stmt
->Execute()));
947 QM_TRY_UNWRAP(vacuumNeeded
, MaybeUpgradeSchema(*connection
, schemaVersion
,
948 aFMDirectory
, aOrigin
));
951 QM_TRY(MOZ_TO_RESULT_INVOKE_MEMBER(transaction
, Commit
)
952 .mapErr(mapNoDeviceSpaceError
));
956 // Re-enable foreign key support after doing a foreign key check.
957 QM_TRY_INSPECT(const bool& foreignKeyError
,
958 CreateAndExecuteSingleStepStatement
<
959 SingleStepResult::ReturnNullIfNoResult
>(
960 *connection
, "PRAGMA foreign_key_check;"_ns
),
961 QM_ASSERT_UNREACHABLE
);
963 MOZ_ASSERT(!foreignKeyError
, "Database has inconsisistent foreign keys!");
966 connection
->ExecuteSimpleSQL("PRAGMA foreign_keys = OFF;"_ns
));
970 if (kSQLitePageSizeOverride
&& !newDatabase
) {
971 QM_TRY_INSPECT(const auto& stmt
,
972 CreateAndExecuteSingleStepStatement(
973 *connection
, "PRAGMA page_size;"_ns
));
975 QM_TRY_INSPECT(const int32_t& pageSize
,
976 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt32
, 0));
977 MOZ_ASSERT(pageSize
>= 512 && pageSize
<= 65536);
979 if (kSQLitePageSizeOverride
!= uint32_t(pageSize
)) {
980 // We must not be in WAL journal mode to change the page size.
981 QM_TRY(MOZ_TO_RESULT(
982 connection
->ExecuteSimpleSQL("PRAGMA journal_mode = DELETE;"_ns
)));
984 QM_TRY_INSPECT(const auto& stmt
,
985 CreateAndExecuteSingleStepStatement(
986 *connection
, "PRAGMA journal_mode;"_ns
));
988 QM_TRY_INSPECT(const auto& journalMode
,
989 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCString
, *stmt
,
992 if (journalMode
.EqualsLiteral("delete")) {
993 // Successfully set to rollback journal mode so changing the page size
994 // is possible with a VACUUM.
995 QM_TRY(MOZ_TO_RESULT(connection
->ExecuteSimpleSQL(nsPrintfCString(
996 "PRAGMA page_size = %" PRIu32
";", kSQLitePageSizeOverride
))));
998 // We will need to VACUUM in order to change the page size.
1002 "Failed to set journal_mode for database, unable to "
1003 "change the page size!");
1009 QM_TRY(MOZ_TO_RESULT(connection
->ExecuteSimpleSQL("VACUUM;"_ns
)));
1012 if (newDatabase
|| vacuumNeeded
) {
1013 if (journalModeSet
) {
1014 // Make sure we checkpoint to get an accurate file size.
1015 QM_TRY(MOZ_TO_RESULT(
1016 connection
->ExecuteSimpleSQL("PRAGMA wal_checkpoint(FULL);"_ns
)));
1019 QM_TRY_INSPECT(const int64_t& fileSize
,
1020 MOZ_TO_RESULT_INVOKE_MEMBER(aDBFile
, GetFileSize
));
1021 MOZ_ASSERT(fileSize
> 0);
1023 PRTime vacuumTime
= PR_Now();
1024 MOZ_ASSERT(vacuumTime
);
1026 // The parameter names are not used, parameters are bound by index only
1027 // locally in the same function.
1029 const auto& vacuumTimeStmt
,
1030 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr
<mozIStorageStatement
>,
1031 connection
, CreateStatement
,
1033 "SET last_vacuum_time = :time"
1034 ", last_vacuum_size = :size;"_ns
));
1036 QM_TRY(MOZ_TO_RESULT(vacuumTimeStmt
->BindInt64ByIndex(0, vacuumTime
)));
1037 QM_TRY(MOZ_TO_RESULT(vacuumTimeStmt
->BindInt64ByIndex(1, fileSize
)));
1038 QM_TRY(MOZ_TO_RESULT(vacuumTimeStmt
->Execute()));
1042 if (!journalModeSet
) {
1043 QM_TRY(MOZ_TO_RESULT(SetJournalMode(*connection
)));
1046 return WrapMovingNotNullUnchecked(std::move(connection
));
1049 nsCOMPtr
<nsIFile
> GetFileForPath(const nsAString
& aPath
) {
1050 MOZ_ASSERT(!aPath
.IsEmpty());
1052 QM_TRY_RETURN(QM_NewLocalFile(aPath
), nullptr);
1055 Result
<MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>>, nsresult
>
1056 GetStorageConnection(nsIFile
& aDatabaseFile
, const int64_t aDirectoryLockId
,
1057 const uint32_t aTelemetryId
,
1058 const Maybe
<CipherKey
>& aMaybeKey
) {
1059 MOZ_ASSERT(!NS_IsMainThread());
1060 MOZ_ASSERT(!IsOnBackgroundThread());
1061 MOZ_ASSERT(aDirectoryLockId
>= 0);
1063 AUTO_PROFILER_LABEL("GetStorageConnection", DOM
);
1065 QM_TRY_INSPECT(const bool& exists
,
1066 MOZ_TO_RESULT_INVOKE_MEMBER(aDatabaseFile
, Exists
));
1068 QM_TRY(OkIf(exists
), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
),
1069 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
1072 const auto& dbFileUrl
,
1073 GetDatabaseFileURL(aDatabaseFile
, aDirectoryLockId
, aMaybeKey
));
1075 QM_TRY_INSPECT(const auto& storageService
,
1076 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr
<mozIStorageService
>,
1077 MOZ_SELECT_OVERLOAD(do_GetService
),
1078 MOZ_STORAGE_SERVICE_CONTRACTID
));
1081 nsCOMPtr
<mozIStorageConnection
> connection
,
1082 OpenDatabaseAndHandleBusy(*storageService
, *dbFileUrl
, aTelemetryId
));
1084 QM_TRY(MOZ_TO_RESULT(SetDefaultPragmas(*connection
)));
1086 QM_TRY(MOZ_TO_RESULT(SetJournalMode(*connection
)));
1088 return WrapMovingNotNullUnchecked(std::move(connection
));
1091 Result
<MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>>, nsresult
>
1092 GetStorageConnection(const nsAString
& aDatabaseFilePath
,
1093 const int64_t aDirectoryLockId
,
1094 const uint32_t aTelemetryId
,
1095 const Maybe
<CipherKey
>& aMaybeKey
) {
1096 MOZ_ASSERT(!NS_IsMainThread());
1097 MOZ_ASSERT(!IsOnBackgroundThread());
1098 MOZ_ASSERT(!aDatabaseFilePath
.IsEmpty());
1099 MOZ_ASSERT(StringEndsWith(aDatabaseFilePath
, kSQLiteSuffix
));
1100 MOZ_ASSERT(aDirectoryLockId
>= 0);
1102 nsCOMPtr
<nsIFile
> dbFile
= GetFileForPath(aDatabaseFilePath
);
1104 QM_TRY(OkIf(dbFile
), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
),
1105 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
1107 return GetStorageConnection(*dbFile
, aDirectoryLockId
, aTelemetryId
,
1111 /*******************************************************************************
1112 * ConnectionPool declarations
1113 ******************************************************************************/
1115 class DatabaseConnection final
: public CachingDatabaseConnection
{
1116 friend class ConnectionPool
;
1118 enum class CheckpointMode
{ Full
, Restart
, Truncate
};
1121 class AutoSavepoint
;
1122 class UpdateRefcountFunction
;
1125 InitializedOnce
<const NotNull
<SafeRefPtr
<DatabaseFileManager
>>> mFileManager
;
1126 RefPtr
<UpdateRefcountFunction
> mUpdateRefcountFunction
;
1127 RefPtr
<QuotaObject
> mQuotaObject
;
1128 RefPtr
<QuotaObject
> mJournalQuotaObject
;
1129 bool mInReadTransaction
;
1130 bool mInWriteTransaction
;
1133 uint32_t mDEBUGSavepointCount
;
1137 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DatabaseConnection
)
1139 UpdateRefcountFunction
* GetUpdateRefcountFunction() const {
1140 AssertIsOnConnectionThread();
1142 return mUpdateRefcountFunction
;
1145 nsresult
BeginWriteTransaction();
1147 nsresult
CommitWriteTransaction();
1149 void RollbackWriteTransaction();
1151 void FinishWriteTransaction();
1153 nsresult
StartSavepoint();
1155 nsresult
ReleaseSavepoint();
1157 nsresult
RollbackSavepoint();
1159 nsresult
Checkpoint() {
1160 AssertIsOnConnectionThread();
1162 return CheckpointInternal(CheckpointMode::Full
);
1165 void DoIdleProcessing(bool aNeedsCheckpoint
,
1166 const Atomic
<bool>& aInterrupted
);
1170 nsresult
DisableQuotaChecks();
1172 void EnableQuotaChecks();
1176 MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>> aStorageConnection
,
1177 MovingNotNull
<SafeRefPtr
<DatabaseFileManager
>> aFileManager
);
1179 ~DatabaseConnection();
1183 nsresult
CheckpointInternal(CheckpointMode aMode
);
1185 Result
<uint32_t, nsresult
> GetFreelistCount(
1186 CachedStatement
& aCachedStatement
);
1189 * On success, returns whether some pages were freed.
1191 Result
<bool, nsresult
> ReclaimFreePagesWhileIdle(
1192 CachedStatement
& aFreelistStatement
, CachedStatement
& aRollbackStatement
,
1193 uint32_t aFreelistCount
, bool aNeedsCheckpoint
,
1194 const Atomic
<bool>& aInterrupted
);
1196 Result
<int64_t, nsresult
> GetFileSize(const nsAString
& aPath
);
1199 class MOZ_STACK_CLASS
DatabaseConnection::AutoSavepoint final
{
1200 DatabaseConnection
* mConnection
;
1202 const TransactionBase
* mDEBUGTransaction
;
1209 nsresult
Start(const TransactionBase
& aTransaction
);
1214 class DatabaseConnection::UpdateRefcountFunction final
1215 : public mozIStorageFunction
{
1216 class FileInfoEntry
;
1218 enum class UpdateType
{ Increment
, Decrement
};
1220 DatabaseConnection
* const mConnection
;
1221 DatabaseFileManager
& mFileManager
;
1222 nsClassHashtable
<nsUint64HashKey
, FileInfoEntry
> mFileInfoEntries
;
1223 nsTHashMap
<nsUint64HashKey
, NotNull
<FileInfoEntry
*>> mSavepointEntriesIndex
;
1225 nsTArray
<int64_t> mJournalsToCreateBeforeCommit
;
1226 nsTArray
<int64_t> mJournalsToRemoveAfterCommit
;
1227 nsTArray
<int64_t> mJournalsToRemoveAfterAbort
;
1232 NS_DECL_ISUPPORTS_ONEVENTTARGET
1233 NS_DECL_MOZISTORAGEFUNCTION
1235 UpdateRefcountFunction(DatabaseConnection
* aConnection
,
1236 DatabaseFileManager
& aFileManager
);
1238 nsresult
WillCommit();
1244 void StartSavepoint();
1246 void ReleaseSavepoint();
1248 void RollbackSavepoint();
1253 ~UpdateRefcountFunction() = default;
1255 nsresult
ProcessValue(mozIStorageValueArray
* aValues
, int32_t aIndex
,
1256 UpdateType aUpdateType
);
1258 nsresult
CreateJournals();
1260 nsresult
RemoveJournals(const nsTArray
<int64_t>& aJournals
);
1263 class DatabaseConnection::UpdateRefcountFunction::FileInfoEntry final
{
1264 SafeRefPtr
<DatabaseFileInfo
> mFileInfo
;
1266 int32_t mSavepointDelta
;
1269 explicit FileInfoEntry(SafeRefPtr
<DatabaseFileInfo
> aFileInfo
)
1270 : mFileInfo(std::move(aFileInfo
)), mDelta(0), mSavepointDelta(0) {
1271 MOZ_COUNT_CTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry
);
1274 void IncDeltas(bool aUpdateSavepointDelta
) {
1276 if (aUpdateSavepointDelta
) {
1280 void DecDeltas(bool aUpdateSavepointDelta
) {
1282 if (aUpdateSavepointDelta
) {
1286 void DecBySavepointDelta() { mDelta
-= mSavepointDelta
; }
1287 SafeRefPtr
<DatabaseFileInfo
> ReleaseFileInfo() {
1288 return std::move(mFileInfo
);
1290 void MaybeUpdateDBRefs() {
1292 mFileInfo
->UpdateDBRefs(mDelta
);
1296 int32_t Delta() const { return mDelta
; }
1297 int32_t SavepointDelta() const { return mSavepointDelta
; }
1300 MOZ_COUNT_DTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry
);
1304 class ConnectionPool final
{
1306 class FinishCallback
;
1309 class ConnectionRunnable
;
1310 class CloseConnectionRunnable
;
1311 struct DatabaseInfo
;
1312 struct DatabaseCompleteCallback
;
1313 class FinishCallbackWrapper
;
1314 class IdleConnectionRunnable
;
1316 class TransactionInfo
;
1317 struct TransactionInfoPair
;
1319 struct IdleResource
{
1320 TimeStamp mIdleTime
;
1322 IdleResource(const IdleResource
& aOther
) = delete;
1323 IdleResource(IdleResource
&& aOther
) noexcept
1324 : IdleResource(aOther
.mIdleTime
) {}
1325 IdleResource
& operator=(const IdleResource
& aOther
) = delete;
1326 IdleResource
& operator=(IdleResource
&& aOther
) = delete;
1329 explicit IdleResource(const TimeStamp
& aIdleTime
);
1334 struct IdleDatabaseInfo final
: public IdleResource
{
1335 InitializedOnce
<const NotNull
<DatabaseInfo
*>> mDatabaseInfo
;
1338 explicit IdleDatabaseInfo(DatabaseInfo
& aDatabaseInfo
);
1340 IdleDatabaseInfo(const IdleDatabaseInfo
& aOther
) = delete;
1341 IdleDatabaseInfo(IdleDatabaseInfo
&& aOther
) noexcept
1342 : IdleResource(std::move(aOther
)),
1343 mDatabaseInfo
{std::move(aOther
.mDatabaseInfo
)} {
1344 MOZ_ASSERT(mDatabaseInfo
);
1346 MOZ_COUNT_CTOR(ConnectionPool::IdleDatabaseInfo
);
1348 IdleDatabaseInfo
& operator=(const IdleDatabaseInfo
& aOther
) = delete;
1349 IdleDatabaseInfo
& operator=(IdleDatabaseInfo
&& aOther
) = delete;
1351 ~IdleDatabaseInfo();
1353 bool operator==(const IdleDatabaseInfo
& aOther
) const {
1354 return *mDatabaseInfo
== *aOther
.mDatabaseInfo
;
1357 bool operator==(const DatabaseInfo
* aDatabaseInfo
) const {
1358 return *mDatabaseInfo
== aDatabaseInfo
;
1361 bool operator<(const IdleDatabaseInfo
& aOther
) const {
1362 return mIdleTime
< aOther
.mIdleTime
;
1366 struct PerformingIdleMaintenanceDatabaseInfo
{
1367 const NotNull
<DatabaseInfo
*> mDatabaseInfo
;
1368 RefPtr
<IdleConnectionRunnable
> mIdleConnectionRunnable
;
1370 PerformingIdleMaintenanceDatabaseInfo(
1371 DatabaseInfo
& aDatabaseInfo
,
1372 RefPtr
<IdleConnectionRunnable
> aIdleConnectionRunnable
);
1374 PerformingIdleMaintenanceDatabaseInfo(
1375 const PerformingIdleMaintenanceDatabaseInfo
& aOther
) = delete;
1376 PerformingIdleMaintenanceDatabaseInfo(
1377 PerformingIdleMaintenanceDatabaseInfo
&& aOther
) noexcept
1378 : mDatabaseInfo
{aOther
.mDatabaseInfo
},
1379 mIdleConnectionRunnable
{std::move(aOther
.mIdleConnectionRunnable
)} {
1380 MOZ_COUNT_CTOR(ConnectionPool::PerformingIdleMaintenanceDatabaseInfo
);
1382 PerformingIdleMaintenanceDatabaseInfo
& operator=(
1383 const PerformingIdleMaintenanceDatabaseInfo
& aOther
) = delete;
1384 PerformingIdleMaintenanceDatabaseInfo
& operator=(
1385 PerformingIdleMaintenanceDatabaseInfo
&& aOther
) = delete;
1387 ~PerformingIdleMaintenanceDatabaseInfo();
1389 bool operator==(const DatabaseInfo
* aDatabaseInfo
) const {
1390 return mDatabaseInfo
== aDatabaseInfo
;
1394 // This mutex guards mDatabases, see below.
1395 Mutex mDatabasesMutex MOZ_UNANNOTATED
;
1397 nsCOMPtr
<nsIThreadPool
> mIOTarget
;
1398 nsTArray
<IdleDatabaseInfo
> mIdleDatabases
;
1399 nsTArray
<PerformingIdleMaintenanceDatabaseInfo
>
1400 mDatabasesPerformingIdleMaintenance
;
1401 nsCOMPtr
<nsITimer
> mIdleTimer
;
1402 TimeStamp mTargetIdleTime
;
1404 // Only modifed on the owning thread, but read on multiple threads. Therefore
1405 // all modifications and all reads off the owning thread must be protected by
1407 nsClassHashtable
<nsCStringHashKey
, DatabaseInfo
> mDatabases
;
1409 nsClassHashtable
<nsUint64HashKey
, TransactionInfo
> mTransactions
;
1410 nsTArray
<NotNull
<TransactionInfo
*>> mQueuedTransactions
;
1412 nsTArray
<UniquePtr
<DatabaseCompleteCallback
>> mCompleteCallbacks
;
1414 uint64_t mNextTransactionId
;
1415 FlippedOnce
<false> mShutdownRequested
;
1416 FlippedOnce
<false> mShutdownComplete
;
1421 void AssertIsOnOwningThread() const {
1422 NS_ASSERT_OWNINGTHREAD(ConnectionPool
);
1425 Result
<RefPtr
<DatabaseConnection
>, nsresult
> GetOrCreateConnection(
1426 const Database
& aDatabase
);
1428 uint64_t Start(const nsID
& aBackgroundChildLoggingId
,
1429 const nsACString
& aDatabaseId
, int64_t aLoggingSerialNumber
,
1430 const nsTArray
<nsString
>& aObjectStoreNames
,
1431 bool aIsWriteTransaction
,
1432 TransactionDatabaseOperationBase
* aTransactionOp
);
1434 void Dispatch(uint64_t aTransactionId
, nsIRunnable
* aRunnable
);
1436 void Finish(uint64_t aTransactionId
, FinishCallback
* aCallback
);
1438 void CloseDatabaseWhenIdle(const nsACString
& aDatabaseId
) {
1439 Unused
<< CloseDatabaseWhenIdleInternal(aDatabaseId
);
1442 void WaitForDatabaseToComplete(const nsCString
& aDatabaseId
,
1443 nsIRunnable
* aCallback
);
1447 NS_INLINE_DECL_REFCOUNTING(ConnectionPool
)
1452 static void IdleTimerCallback(nsITimer
* aTimer
, void* aClosure
);
1454 static uint32_t SerialNumber() { return ++sSerialNumber
; }
1456 static uint32_t sSerialNumber
;
1460 void AdjustIdleTimer();
1462 void CancelIdleTimer();
1464 void CloseIdleDatabases();
1466 bool ScheduleTransaction(TransactionInfo
& aTransactionInfo
,
1467 bool aFromQueuedTransactions
);
1469 void NoteFinishedTransaction(uint64_t aTransactionId
);
1471 void ScheduleQueuedTransactions();
1473 void NoteIdleDatabase(DatabaseInfo
& aDatabaseInfo
);
1475 void NoteClosedDatabase(DatabaseInfo
& aDatabaseInfo
);
1477 bool MaybeFireCallback(DatabaseCompleteCallback
* aCallback
);
1479 void PerformIdleDatabaseMaintenance(DatabaseInfo
& aDatabaseInfo
);
1481 void CloseDatabase(DatabaseInfo
& aDatabaseInfo
) const;
1483 bool CloseDatabaseWhenIdleInternal(const nsACString
& aDatabaseId
);
1486 class ConnectionPool::ConnectionRunnable
: public Runnable
{
1488 DatabaseInfo
& mDatabaseInfo
;
1489 nsCOMPtr
<nsIEventTarget
> mOwningEventTarget
;
1491 explicit ConnectionRunnable(DatabaseInfo
& aDatabaseInfo
);
1493 ~ConnectionRunnable() override
= default;
1496 class ConnectionPool::IdleConnectionRunnable final
: public ConnectionRunnable
{
1497 const bool mNeedsCheckpoint
;
1498 Atomic
<bool> mInterrupted
;
1501 IdleConnectionRunnable(DatabaseInfo
& aDatabaseInfo
, bool aNeedsCheckpoint
)
1502 : ConnectionRunnable(aDatabaseInfo
), mNeedsCheckpoint(aNeedsCheckpoint
) {}
1504 NS_INLINE_DECL_REFCOUNTING_INHERITED(IdleConnectionRunnable
,
1507 void Interrupt() { mInterrupted
= true; }
1510 ~IdleConnectionRunnable() override
= default;
1515 class ConnectionPool::CloseConnectionRunnable final
1516 : public ConnectionRunnable
{
1518 explicit CloseConnectionRunnable(DatabaseInfo
& aDatabaseInfo
)
1519 : ConnectionRunnable(aDatabaseInfo
) {}
1521 NS_INLINE_DECL_REFCOUNTING_INHERITED(CloseConnectionRunnable
,
1525 ~CloseConnectionRunnable() override
= default;
1530 struct ConnectionPool::DatabaseInfo final
{
1531 friend class mozilla::DefaultDelete
<DatabaseInfo
>;
1533 RefPtr
<ConnectionPool
> mConnectionPool
;
1534 const nsCString mDatabaseId
;
1535 RefPtr
<DatabaseConnection
> mConnection
;
1536 nsClassHashtable
<nsStringHashKey
, TransactionInfoPair
> mBlockingTransactions
;
1537 nsTArray
<NotNull
<TransactionInfo
*>> mTransactionsScheduledDuringClose
;
1538 nsTArray
<NotNull
<TransactionInfo
*>> mScheduledWriteTransactions
;
1539 Maybe
<TransactionInfo
&> mRunningWriteTransaction
;
1540 RefPtr
<TaskQueue
> mEventTarget
;
1541 uint32_t mReadTransactionCount
;
1542 uint32_t mWriteTransactionCount
;
1543 bool mNeedsCheckpoint
;
1545 FlippedOnce
<false> mCloseOnIdle
;
1549 nsISerialEventTarget
* mDEBUGConnectionEventTarget
;
1552 DatabaseInfo(ConnectionPool
* aConnectionPool
, const nsACString
& aDatabaseId
);
1554 void AssertIsOnConnectionThread() const {
1555 MOZ_ASSERT(mDEBUGConnectionEventTarget
);
1556 MOZ_ASSERT(GetCurrentSerialEventTarget() == mDEBUGConnectionEventTarget
);
1559 uint64_t TotalTransactionCount() const {
1560 return mReadTransactionCount
+ mWriteTransactionCount
;
1566 DatabaseInfo(const DatabaseInfo
&) = delete;
1567 DatabaseInfo
& operator=(const DatabaseInfo
&) = delete;
1570 struct ConnectionPool::DatabaseCompleteCallback final
{
1571 friend class DefaultDelete
<DatabaseCompleteCallback
>;
1573 nsCString mDatabaseId
;
1574 nsCOMPtr
<nsIRunnable
> mCallback
;
1576 DatabaseCompleteCallback(const nsCString
& aDatabaseIds
,
1577 nsIRunnable
* aCallback
);
1580 ~DatabaseCompleteCallback();
1583 class NS_NO_VTABLE
ConnectionPool::FinishCallback
: public nsIRunnable
{
1585 // Called on the owning thread before any additional transactions are
1587 virtual void TransactionFinishedBeforeUnblock() = 0;
1589 // Called on the owning thread after additional transactions may have been
1591 virtual void TransactionFinishedAfterUnblock() = 0;
1594 FinishCallback() = default;
1596 virtual ~FinishCallback() = default;
1599 class ConnectionPool::FinishCallbackWrapper final
: public Runnable
{
1600 RefPtr
<ConnectionPool
> mConnectionPool
;
1601 RefPtr
<FinishCallback
> mCallback
;
1602 nsCOMPtr
<nsIEventTarget
> mOwningEventTarget
;
1603 uint64_t mTransactionId
;
1607 FinishCallbackWrapper(ConnectionPool
* aConnectionPool
,
1608 uint64_t aTransactionId
, FinishCallback
* aCallback
);
1610 NS_INLINE_DECL_REFCOUNTING_INHERITED(FinishCallbackWrapper
, Runnable
)
1613 ~FinishCallbackWrapper() override
;
1618 class ConnectionPool::TransactionInfo final
{
1619 friend class mozilla::DefaultDelete
<TransactionInfo
>;
1621 nsTHashSet
<TransactionInfo
*> mBlocking
;
1622 nsTArray
<NotNull
<TransactionInfo
*>> mBlockingOrdered
;
1625 DatabaseInfo
& mDatabaseInfo
;
1626 const nsID mBackgroundChildLoggingId
;
1627 const nsCString mDatabaseId
;
1628 const uint64_t mTransactionId
;
1629 const int64_t mLoggingSerialNumber
;
1630 const nsTArray
<nsString
> mObjectStoreNames
;
1631 nsTHashSet
<TransactionInfo
*> mBlockedOn
;
1632 nsTArray
<nsCOMPtr
<nsIRunnable
>> mQueuedRunnables
;
1633 const bool mIsWriteTransaction
;
1637 FlippedOnce
<false> mFinished
;
1640 TransactionInfo(DatabaseInfo
& aDatabaseInfo
,
1641 const nsID
& aBackgroundChildLoggingId
,
1642 const nsACString
& aDatabaseId
, uint64_t aTransactionId
,
1643 int64_t aLoggingSerialNumber
,
1644 const nsTArray
<nsString
>& aObjectStoreNames
,
1645 bool aIsWriteTransaction
,
1646 TransactionDatabaseOperationBase
* aTransactionOp
);
1648 void AddBlockingTransaction(TransactionInfo
& aTransactionInfo
);
1650 void RemoveBlockingTransactions();
1655 void MaybeUnblock(TransactionInfo
& aTransactionInfo
);
1658 struct ConnectionPool::TransactionInfoPair final
{
1659 // Multiple reading transactions can block future writes.
1660 nsTArray
<NotNull
<TransactionInfo
*>> mLastBlockingWrites
;
1661 // But only a single writing transaction can block future reads.
1662 Maybe
<TransactionInfo
&> mLastBlockingReads
;
1664 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
1665 TransactionInfoPair();
1666 ~TransactionInfoPair();
1670 /*******************************************************************************
1671 * Actor class declarations
1672 ******************************************************************************/
1674 template <IDBCursorType CursorType
>
1675 class CommonOpenOpHelper
;
1676 template <IDBCursorType CursorType
>
1677 class IndexOpenOpHelper
;
1678 template <IDBCursorType CursorType
>
1679 class ObjectStoreOpenOpHelper
;
1680 template <IDBCursorType CursorType
>
1683 class DatabaseOperationBase
: public Runnable
,
1684 public mozIStorageProgressHandler
{
1685 template <IDBCursorType CursorType
>
1686 friend class OpenOpHelper
;
1689 class AutoSetProgressHandler
;
1691 using UniqueIndexTable
= nsTHashMap
<nsUint64HashKey
, bool>;
1693 const nsCOMPtr
<nsIEventTarget
> mOwningEventTarget
;
1694 const nsID mBackgroundChildLoggingId
;
1695 const uint64_t mLoggingSerialNumber
;
1698 nsresult mResultCode
= NS_OK
;
1699 Atomic
<bool> mOperationMayProceed
;
1700 FlippedOnce
<false> mActorDestroyed
;
1703 NS_DECL_ISUPPORTS_INHERITED
1705 bool IsOnOwningThread() const {
1706 MOZ_ASSERT(mOwningEventTarget
);
1709 return NS_SUCCEEDED(mOwningEventTarget
->IsOnCurrentThread(¤t
)) &&
1713 void AssertIsOnOwningThread() const {
1714 MOZ_ASSERT(IsOnBackgroundThread());
1715 MOZ_ASSERT(IsOnOwningThread());
1718 void NoteActorDestroyed() {
1719 AssertIsOnOwningThread();
1721 mActorDestroyed
.EnsureFlipped();
1722 mOperationMayProceed
= false;
1725 bool IsActorDestroyed() const {
1726 AssertIsOnOwningThread();
1728 return mActorDestroyed
;
1731 // May be called on any thread, but you should call IsActorDestroyed() if
1732 // you know you're on the background thread because it is slightly faster.
1733 bool OperationMayProceed() const { return mOperationMayProceed
; }
1735 const nsID
& BackgroundChildLoggingId() const {
1736 return mBackgroundChildLoggingId
;
1739 uint64_t LoggingSerialNumber() const { return mLoggingSerialNumber
; }
1741 nsresult
ResultCode() const { return mResultCode
; }
1743 void SetFailureCode(nsresult aFailureCode
) {
1744 MOZ_ASSERT(NS_SUCCEEDED(mResultCode
));
1745 OverrideFailureCode(aFailureCode
);
1748 void SetFailureCodeIfUnset(nsresult aFailureCode
) {
1749 if (NS_SUCCEEDED(mResultCode
)) {
1750 OverrideFailureCode(aFailureCode
);
1754 bool HasFailed() const { return NS_FAILED(mResultCode
); }
1757 DatabaseOperationBase(const nsID
& aBackgroundChildLoggingId
,
1758 uint64_t aLoggingSerialNumber
)
1759 : Runnable("dom::indexedDB::DatabaseOperationBase"),
1760 mOwningEventTarget(GetCurrentSerialEventTarget()),
1761 mBackgroundChildLoggingId(aBackgroundChildLoggingId
),
1762 mLoggingSerialNumber(aLoggingSerialNumber
),
1763 mOperationMayProceed(true) {
1764 AssertIsOnOwningThread();
1767 ~DatabaseOperationBase() override
{ MOZ_ASSERT(mActorDestroyed
); }
1769 void OverrideFailureCode(nsresult aFailureCode
) {
1770 MOZ_ASSERT(NS_FAILED(aFailureCode
));
1772 mResultCode
= aFailureCode
;
1775 static nsAutoCString
MaybeGetBindingClauseForKeyRange(
1776 const Maybe
<SerializedKeyRange
>& aOptionalKeyRange
,
1777 const nsACString
& aKeyColumnName
);
1779 static nsAutoCString
GetBindingClauseForKeyRange(
1780 const SerializedKeyRange
& aKeyRange
, const nsACString
& aKeyColumnName
);
1782 static uint64_t ReinterpretDoubleAsUInt64(double aDouble
);
1784 static nsresult
BindKeyRangeToStatement(const SerializedKeyRange
& aKeyRange
,
1785 mozIStorageStatement
* aStatement
);
1787 static nsresult
BindKeyRangeToStatement(const SerializedKeyRange
& aKeyRange
,
1788 mozIStorageStatement
* aStatement
,
1789 const nsCString
& aLocale
);
1791 static Result
<IndexDataValuesAutoArray
, nsresult
>
1792 IndexDataValuesFromUpdateInfos(const nsTArray
<IndexUpdateInfo
>& aUpdateInfos
,
1793 const UniqueIndexTable
& aUniqueIndexTable
);
1795 static nsresult
InsertIndexTableRows(
1796 DatabaseConnection
* aConnection
, IndexOrObjectStoreId aObjectStoreId
,
1797 const Key
& aObjectStoreKey
, const nsTArray
<IndexDataValue
>& aIndexValues
);
1799 static nsresult
DeleteIndexDataTableRows(
1800 DatabaseConnection
* aConnection
, const Key
& aObjectStoreKey
,
1801 const nsTArray
<IndexDataValue
>& aIndexValues
);
1803 static nsresult
DeleteObjectStoreDataTableRowsWithIndexes(
1804 DatabaseConnection
* aConnection
, IndexOrObjectStoreId aObjectStoreId
,
1805 const Maybe
<SerializedKeyRange
>& aKeyRange
);
1807 static nsresult
UpdateIndexValues(
1808 DatabaseConnection
* aConnection
, IndexOrObjectStoreId aObjectStoreId
,
1809 const Key
& aObjectStoreKey
, const nsTArray
<IndexDataValue
>& aIndexValues
);
1811 static Result
<bool, nsresult
> ObjectStoreHasIndexes(
1812 DatabaseConnection
& aConnection
, IndexOrObjectStoreId aObjectStoreId
);
1815 template <typename KeyTransformation
>
1816 static nsresult
MaybeBindKeyToStatement(
1817 const Key
& aKey
, mozIStorageStatement
* aStatement
,
1818 const nsACString
& aParameterName
,
1819 const KeyTransformation
& aKeyTransformation
);
1821 template <typename KeyTransformation
>
1822 static nsresult
BindTransformedKeyRangeToStatement(
1823 const SerializedKeyRange
& aKeyRange
, mozIStorageStatement
* aStatement
,
1824 const KeyTransformation
& aKeyTransformation
);
1826 // Not to be overridden by subclasses.
1827 NS_DECL_MOZISTORAGEPROGRESSHANDLER
1830 class MOZ_STACK_CLASS
DatabaseOperationBase::AutoSetProgressHandler final
{
1831 Maybe
<mozIStorageConnection
&> mConnection
;
1833 DatabaseOperationBase
* mDEBUGDatabaseOp
;
1837 AutoSetProgressHandler();
1839 ~AutoSetProgressHandler();
1841 nsresult
Register(mozIStorageConnection
& aConnection
,
1842 DatabaseOperationBase
* aDatabaseOp
);
1847 class TransactionDatabaseOperationBase
: public DatabaseOperationBase
{
1848 enum class InternalState
{
1857 InitializedOnce
<const NotNull
<SafeRefPtr
<TransactionBase
>>> mTransaction
;
1858 // Unique request id within the context of the transaction, allocated by the
1859 // transaction in the content process starting from 0. Values less than 0 are
1860 // impossible and forbidden. Used to support the explicit commit() request.
1861 const int64_t mRequestId
;
1862 InternalState mInternalState
= InternalState::Initial
;
1863 bool mWaitingForContinue
= false;
1864 const bool mTransactionIsAborted
;
1867 const int64_t mTransactionLoggingSerialNumber
;
1869 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1871 // A check only enables when the diagnostic assert turns on. It assumes the
1872 // mUpdateRefcountFunction is a nullptr because the previous
1873 // StartTransactionOp failed on the connection thread and the next write
1874 // operation (e.g. ObjectstoreAddOrPutRequestOp) doesn't have enough time to
1875 // catch up the failure information.
1876 bool mAssumingPreviousOperationFail
= false;
1880 void AssertIsOnConnectionThread() const
1888 uint64_t StartOnConnectionPool(const nsID
& aBackgroundChildLoggingId
,
1889 const nsACString
& aDatabaseId
,
1890 int64_t aLoggingSerialNumber
,
1891 const nsTArray
<nsString
>& aObjectStoreNames
,
1892 bool aIsWriteTransaction
);
1894 void DispatchToConnectionPool();
1896 TransactionBase
& Transaction() { return **mTransaction
; }
1898 const TransactionBase
& Transaction() const { return **mTransaction
; }
1900 bool IsWaitingForContinue() const {
1901 AssertIsOnOwningThread();
1903 return mWaitingForContinue
;
1906 void NoteContinueReceived();
1908 int64_t TransactionLoggingSerialNumber() const {
1909 return mTransactionLoggingSerialNumber
;
1912 // May be overridden by subclasses if they need to perform work on the
1913 // background thread before being dispatched. Returning false will kill the
1914 // child actors and prevent dispatch.
1915 virtual bool Init(TransactionBase
& aTransaction
);
1917 // This callback will be called on the background thread before releasing the
1918 // final reference to this request object. Subclasses may perform any
1919 // additional cleanup here but must always call the base class implementation.
1920 virtual void Cleanup();
1923 TransactionDatabaseOperationBase(SafeRefPtr
<TransactionBase
> aTransaction
,
1924 int64_t aRequestId
);
1926 TransactionDatabaseOperationBase(SafeRefPtr
<TransactionBase
> aTransaction
,
1927 const int64_t aRequestId
,
1928 uint64_t aLoggingSerialNumber
);
1930 ~TransactionDatabaseOperationBase() override
;
1932 virtual void RunOnConnectionThread();
1934 // Must be overridden in subclasses. Called on the target thread to allow the
1935 // subclass to perform necessary database or file operations. A successful
1936 // return value will trigger a SendSuccessResult callback on the background
1937 // thread while a failure value will trigger a SendFailureResult callback.
1938 virtual nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) = 0;
1940 // May be overriden in subclasses. Called on the background thread to decide
1941 // if the subclass needs to send any preprocess info to the child actor.
1942 virtual bool HasPreprocessInfo();
1944 // May be overriden in subclasses. Called on the background thread to allow
1945 // the subclass to serialize its preprocess info and send it to the child
1946 // actor. A successful return value will trigger a wait for a
1947 // NoteContinueReceived callback on the background thread while a failure
1948 // value will trigger a SendFailureResult callback.
1949 virtual nsresult
SendPreprocessInfo();
1951 // Must be overridden in subclasses. Called on the background thread to allow
1952 // the subclass to serialize its results and send them to the child actor. A
1953 // failed return value will trigger a SendFailureResult callback.
1954 virtual nsresult
SendSuccessResult() = 0;
1956 // Must be overridden in subclasses. Called on the background thread to allow
1957 // the subclass to send its failure code. Returning false will cause the
1958 // transaction to be aborted with aResultCode. Returning true will not cause
1959 // the transaction to be aborted.
1960 virtual bool SendFailureResult(nsresult aResultCode
) = 0;
1962 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1963 auto MakeAutoSavepointCleanupHandler(DatabaseConnection
& aConnection
) {
1964 return [this, &aConnection
](const auto) {
1965 if (!aConnection
.GetUpdateRefcountFunction()) {
1966 mAssumingPreviousOperationFail
= true;
1973 void SendToConnectionPool();
1975 void SendPreprocess();
1979 void SendPreprocessInfoOrResults(bool aSendPreprocessInfo
);
1981 // Not to be overridden by subclasses.
1985 class Factory final
: public PBackgroundIDBFactoryParent
,
1986 public AtomicSafeRefCounted
<Factory
> {
1987 nsCString mSystemLocale
;
1988 RefPtr
<DatabaseLoggingInfo
> mLoggingInfo
;
1991 bool mActorDestroyed
;
1994 // Reference counted.
1995 ~Factory() override
;
1998 [[nodiscard
]] static SafeRefPtr
<Factory
> Create(
1999 const LoggingInfo
& aLoggingInfo
, const nsACString
& aSystemLocale
);
2001 DatabaseLoggingInfo
* GetLoggingInfo() const {
2002 AssertIsOnBackgroundThread();
2003 MOZ_ASSERT(mLoggingInfo
);
2005 return mLoggingInfo
;
2008 const nsCString
& GetSystemLocale() const { return mSystemLocale
; }
2010 MOZ_DECLARE_REFCOUNTED_TYPENAME(mozilla::dom::indexedDB::Factory
)
2011 MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Factory
, AtomicSafeRefCounted
)
2013 // Only constructed in Create().
2014 Factory(RefPtr
<DatabaseLoggingInfo
> aLoggingInfo
,
2015 const nsACString
& aSystemLocale
);
2017 // IPDL methods are only called by IPDL.
2018 void ActorDestroy(ActorDestroyReason aWhy
) override
;
2020 mozilla::ipc::IPCResult
RecvDeleteMe() override
;
2022 PBackgroundIDBFactoryRequestParent
* AllocPBackgroundIDBFactoryRequestParent(
2023 const FactoryRequestParams
& aParams
) override
;
2025 mozilla::ipc::IPCResult
RecvPBackgroundIDBFactoryRequestConstructor(
2026 PBackgroundIDBFactoryRequestParent
* aActor
,
2027 const FactoryRequestParams
& aParams
) override
;
2029 bool DeallocPBackgroundIDBFactoryRequestParent(
2030 PBackgroundIDBFactoryRequestParent
* aActor
) override
;
2032 mozilla::ipc::IPCResult
RecvGetDatabases(
2033 const PersistenceType
& aPersistenceType
,
2034 const PrincipalInfo
& aPrincipalInfo
,
2035 GetDatabasesResolver
&& aResolve
) override
;
2038 Maybe
<ContentParentId
> GetContentParentId() const;
2041 class WaitForTransactionsHelper final
: public Runnable
{
2042 const nsCString mDatabaseId
;
2043 nsCOMPtr
<nsIRunnable
> mCallback
;
2045 enum class State
{ Initial
= 0, WaitingForTransactions
, Complete
} mState
;
2048 WaitForTransactionsHelper(const nsACString
& aDatabaseId
,
2049 nsIRunnable
* aCallback
)
2050 : Runnable("dom::indexedDB::WaitForTransactionsHelper"),
2051 mDatabaseId(aDatabaseId
),
2052 mCallback(aCallback
),
2053 mState(State::Initial
) {
2054 AssertIsOnBackgroundThread();
2055 MOZ_ASSERT(!aDatabaseId
.IsEmpty());
2056 MOZ_ASSERT(aCallback
);
2059 void WaitForTransactions();
2061 NS_INLINE_DECL_REFCOUNTING_INHERITED(WaitForTransactionsHelper
, Runnable
)
2064 ~WaitForTransactionsHelper() override
{
2065 MOZ_ASSERT(!mCallback
);
2066 MOZ_ASSERT(mState
== State::Complete
);
2069 void MaybeWaitForTransactions();
2071 void CallCallback();
2076 class Database final
2077 : public PBackgroundIDBDatabaseParent
,
2078 public SupportsCheckedUnsafePtr
<CheckIf
<DiagnosticAssertEnabled
>>,
2079 public AtomicSafeRefCounted
<Database
> {
2080 friend class VersionChangeTransaction
;
2082 class StartTransactionOp
;
2083 class UnmapBlobCallback
;
2086 SafeRefPtr
<Factory
> mFactory
;
2087 SafeRefPtr
<FullDatabaseMetadata
> mMetadata
;
2088 SafeRefPtr
<DatabaseFileManager
> mFileManager
;
2089 RefPtr
<DirectoryLock
> mDirectoryLock
;
2090 nsTHashSet
<TransactionBase
*> mTransactions
;
2091 nsTHashMap
<nsIDHashKey
, SafeRefPtr
<DatabaseFileInfo
>> mMappedBlobs
;
2092 RefPtr
<DatabaseConnection
> mConnection
;
2093 const PrincipalInfo mPrincipalInfo
;
2094 const Maybe
<ContentParentId
> mOptionalContentParentId
;
2095 // XXX Consider changing this to ClientMetadata.
2096 const quota::OriginMetadata mOriginMetadata
;
2097 const nsCString mId
;
2098 const nsString mFilePath
;
2099 const Maybe
<const CipherKey
> mKey
;
2100 int64_t mDirectoryLockId
;
2101 const uint32_t mTelemetryId
;
2102 const PersistenceType mPersistenceType
;
2103 const bool mInPrivateBrowsing
;
2104 FlippedOnce
<false> mClosed
;
2105 FlippedOnce
<false> mInvalidated
;
2106 FlippedOnce
<false> mActorWasAlive
;
2107 FlippedOnce
<false> mActorDestroyed
;
2108 nsCOMPtr
<nsIEventTarget
> mBackgroundThread
;
2110 bool mAllBlobsUnmapped
;
2114 // Created by OpenDatabaseOp.
2115 Database(SafeRefPtr
<Factory
> aFactory
, const PrincipalInfo
& aPrincipalInfo
,
2116 const Maybe
<ContentParentId
>& aOptionalContentParentId
,
2117 const quota::OriginMetadata
& aOriginMetadata
, uint32_t aTelemetryId
,
2118 SafeRefPtr
<FullDatabaseMetadata
> aMetadata
,
2119 SafeRefPtr
<DatabaseFileManager
> aFileManager
,
2120 RefPtr
<DirectoryLock
> aDirectoryLock
, bool aInPrivateBrowsing
,
2121 const Maybe
<const CipherKey
>& aMaybeKey
);
2123 void AssertIsOnConnectionThread() const {
2126 MOZ_ASSERT(mConnection
);
2127 mConnection
->AssertIsOnConnectionThread();
2129 MOZ_ASSERT(!NS_IsMainThread());
2130 MOZ_ASSERT(!IsOnBackgroundThread());
2131 MOZ_ASSERT(mInvalidated
);
2136 NS_IMETHOD_(MozExternalRefCountType
) AddRef() override
{
2137 return AtomicSafeRefCounted
<Database
>::AddRef();
2139 NS_IMETHOD_(MozExternalRefCountType
) Release() override
{
2140 return AtomicSafeRefCounted
<Database
>::Release();
2143 MOZ_DECLARE_REFCOUNTED_TYPENAME(mozilla::dom::indexedDB::Database
)
2147 bool IsOwnedByProcess(ContentParentId aContentParentId
) const {
2148 return mOptionalContentParentId
&&
2149 mOptionalContentParentId
.value() == aContentParentId
;
2152 const quota::OriginMetadata
& OriginMetadata() const {
2153 return mOriginMetadata
;
2156 const nsCString
& Id() const { return mId
; }
2158 Maybe
<DirectoryLock
&> MaybeDirectoryLockRef() const {
2159 AssertIsOnBackgroundThread();
2161 return ToMaybeRef(mDirectoryLock
.get());
2164 int64_t DirectoryLockId() const { return mDirectoryLockId
; }
2166 uint32_t TelemetryId() const { return mTelemetryId
; }
2168 PersistenceType
Type() const { return mPersistenceType
; }
2170 const nsString
& FilePath() const { return mFilePath
; }
2172 DatabaseFileManager
& GetFileManager() const { return *mFileManager
; }
2174 MovingNotNull
<SafeRefPtr
<DatabaseFileManager
>> GetFileManagerPtr() const {
2175 return WrapMovingNotNull(mFileManager
.clonePtr());
2178 const FullDatabaseMetadata
& Metadata() const {
2179 MOZ_ASSERT(mMetadata
);
2183 SafeRefPtr
<FullDatabaseMetadata
> MetadataPtr() const {
2184 MOZ_ASSERT(mMetadata
);
2185 return mMetadata
.clonePtr();
2188 PBackgroundParent
* GetBackgroundParent() const {
2189 AssertIsOnBackgroundThread();
2190 MOZ_ASSERT(!IsActorDestroyed());
2192 return Manager()->Manager();
2195 DatabaseLoggingInfo
* GetLoggingInfo() const {
2196 AssertIsOnBackgroundThread();
2197 MOZ_ASSERT(mFactory
);
2199 return mFactory
->GetLoggingInfo();
2202 bool RegisterTransaction(TransactionBase
& aTransaction
);
2204 void UnregisterTransaction(TransactionBase
& aTransaction
);
2206 void SetActorAlive();
2208 void MapBlob(const IPCBlob
& aIPCBlob
, SafeRefPtr
<DatabaseFileInfo
> aFileInfo
);
2210 bool IsActorAlive() const {
2211 AssertIsOnBackgroundThread();
2213 return mActorWasAlive
&& !mActorDestroyed
;
2216 bool IsActorDestroyed() const {
2217 AssertIsOnBackgroundThread();
2219 return mActorWasAlive
&& mActorDestroyed
;
2222 bool IsClosed() const {
2223 AssertIsOnBackgroundThread();
2228 bool IsInvalidated() const {
2229 AssertIsOnBackgroundThread();
2231 return mInvalidated
;
2234 nsresult
EnsureConnection();
2236 DatabaseConnection
* GetConnection() const {
2239 mConnection
->AssertIsOnConnectionThread();
2246 void Stringify(nsACString
& aResult
) const;
2248 bool IsInPrivateBrowsing() const {
2249 AssertIsOnBackgroundThread();
2250 return mInPrivateBrowsing
;
2253 const Maybe
<const CipherKey
>& MaybeKeyRef() const {
2254 // This can be called on any thread, as it is const.
2255 MOZ_ASSERT(mKey
.isSome() == mInPrivateBrowsing
);
2259 ~Database() override
{
2260 MOZ_ASSERT(mClosed
);
2261 MOZ_ASSERT_IF(mActorWasAlive
, mActorDestroyed
);
2263 NS_ProxyRelease("ReleaseIDBFactory", mBackgroundThread
.get(),
2268 [[nodiscard
]] SafeRefPtr
<DatabaseFileInfo
> GetBlob(const IPCBlob
& aIPCBlob
);
2270 void UnmapBlob(const nsID
& aID
);
2272 void UnmapAllBlobs();
2274 bool CloseInternal();
2276 void MaybeCloseConnection();
2278 void ConnectionClosedCallback();
2280 void CleanupMetadata();
2282 // IPDL methods are only called by IPDL.
2283 void ActorDestroy(ActorDestroyReason aWhy
) override
;
2285 PBackgroundIDBDatabaseFileParent
* AllocPBackgroundIDBDatabaseFileParent(
2286 const IPCBlob
& aIPCBlob
) override
;
2288 bool DeallocPBackgroundIDBDatabaseFileParent(
2289 PBackgroundIDBDatabaseFileParent
* aActor
) override
;
2291 already_AddRefed
<PBackgroundIDBTransactionParent
>
2292 AllocPBackgroundIDBTransactionParent(
2293 const nsTArray
<nsString
>& aObjectStoreNames
, const Mode
& aMode
,
2294 const Durability
& aDurability
) override
;
2296 mozilla::ipc::IPCResult
RecvPBackgroundIDBTransactionConstructor(
2297 PBackgroundIDBTransactionParent
* aActor
,
2298 nsTArray
<nsString
>&& aObjectStoreNames
, const Mode
& aMode
,
2299 const Durability
& aDurability
) override
;
2301 mozilla::ipc::IPCResult
RecvDeleteMe() override
;
2303 mozilla::ipc::IPCResult
RecvBlocked() override
;
2305 mozilla::ipc::IPCResult
RecvClose() override
;
2307 template <typename T
>
2308 static bool InvalidateAll(const nsTBaseHashSet
<nsPtrHashKey
<T
>>& aTable
);
2311 class Database::StartTransactionOp final
2312 : public TransactionDatabaseOperationBase
{
2313 friend class Database
;
2316 explicit StartTransactionOp(SafeRefPtr
<TransactionBase
> aTransaction
)
2317 : TransactionDatabaseOperationBase(std::move(aTransaction
),
2319 /* aLoggingSerialNumber */ 0) {}
2321 ~StartTransactionOp() override
= default;
2323 void RunOnConnectionThread() override
;
2325 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
2327 nsresult
SendSuccessResult() override
;
2329 bool SendFailureResult(nsresult aResultCode
) override
;
2331 void Cleanup() override
;
2334 class Database::UnmapBlobCallback final
2335 : public RemoteLazyInputStreamParentCallback
{
2336 SafeRefPtr
<Database
> mDatabase
;
2337 nsCOMPtr
<nsISerialEventTarget
> mBackgroundThread
;
2340 explicit UnmapBlobCallback(SafeRefPtr
<Database
> aDatabase
)
2341 : mDatabase(std::move(aDatabase
)),
2342 mBackgroundThread(GetCurrentSerialEventTarget()) {
2343 AssertIsOnBackgroundThread();
2346 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Database::UnmapBlobCallback
, override
)
2348 void ActorDestroyed(const nsID
& aID
) override
{
2349 MOZ_ASSERT(mDatabase
);
2350 mBackgroundThread
->Dispatch(NS_NewRunnableFunction(
2351 "UnmapBlobCallback", [aID
, database
= std::move(mDatabase
)] {
2352 AssertIsOnBackgroundThread();
2353 database
->UnmapBlob(aID
);
2358 ~UnmapBlobCallback() = default;
2362 * In coordination with IDBDatabase's mFileActors weak-map on the child side, a
2363 * long-lived mapping from a child process's live Blobs to their corresponding
2364 * DatabaseFileInfo in our owning database. Assists in avoiding redundant IPC
2365 * traffic and disk storage. This includes both:
2366 * - Blobs retrieved from this database and sent to the child that do not need
2367 * to be written to disk because they already exist on disk in this database's
2369 * - Blobs retrieved from other databases or from anywhere else that will need
2370 * to be written to this database's files directory. In this case we will
2371 * hold a reference to its BlobImpl in mBlobImpl until we have successfully
2372 * written the Blob to disk.
2374 * Relevant Blob context: Blobs sent from the parent process to child processes
2375 * are automatically linked back to their source BlobImpl when the child process
2376 * references the Blob via IPC. This is done using the internal IPCBlob
2377 * inputStream actor ID to DatabaseFileInfo mapping. However, when getting an
2378 * actor in the child process for sending an in-child-created Blob to the
2379 * parent process, there is (currently) no Blob machinery to automatically
2380 * establish and reuse a long-lived Actor. As a result, without IDB's weak-map
2381 * cleverness, a memory-backed Blob repeatedly sent from the child to the parent
2382 * would appear as a different Blob each time, requiring the Blob data to be
2383 * sent over IPC each time as well as potentially needing to be written to disk
2386 * This object remains alive as long as there is an active child actor or an
2387 * ObjectStoreAddOrPutRequestOp::StoredFileInfo for a queued or active add/put
2388 * op is holding a reference to us.
2390 class DatabaseFile final
: public PBackgroundIDBDatabaseFileParent
{
2391 // mBlobImpl's ownership lifecycle:
2392 // - Initialized on the background thread at creation time. Then
2393 // responsibility is handed off to the connection thread.
2394 // - Checked and used by the connection thread to generate a stream to write
2395 // the blob to disk by an add/put operation.
2396 // - Cleared on the connection thread once the file has successfully been
2398 InitializedOnce
<const RefPtr
<BlobImpl
>> mBlobImpl
;
2399 const SafeRefPtr
<DatabaseFileInfo
> mFileInfo
;
2402 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::DatabaseFile
);
2404 const DatabaseFileInfo
& GetFileInfo() const {
2405 AssertIsOnBackgroundThread();
2410 SafeRefPtr
<DatabaseFileInfo
> GetFileInfoPtr() const {
2411 AssertIsOnBackgroundThread();
2413 return mFileInfo
.clonePtr();
2417 * If mBlobImpl is non-null (implying the contents of this file have not yet
2418 * been written to disk), then return an input stream. Otherwise, if mBlobImpl
2419 * is null (because the contents have been written to disk), returns null.
2421 [[nodiscard
]] nsCOMPtr
<nsIInputStream
> GetInputStream(ErrorResult
& rv
) const;
2424 * To be called upon successful copying of the stream GetInputStream()
2425 * returned so that we won't try and redundantly write the file to disk in the
2426 * future. This is a separate step from GetInputStream() because
2427 * the write could fail due to quota errors that happen now but that might
2428 * not happen in a future attempt.
2430 void WriteSucceededClearBlobImpl() {
2431 MOZ_ASSERT(!IsOnBackgroundThread());
2433 MOZ_ASSERT(*mBlobImpl
);
2434 mBlobImpl
.destroy();
2438 // Called when sending to the child.
2439 explicit DatabaseFile(SafeRefPtr
<DatabaseFileInfo
> aFileInfo
)
2440 : mBlobImpl
{nullptr}, mFileInfo(std::move(aFileInfo
)) {
2441 AssertIsOnBackgroundThread();
2442 MOZ_ASSERT(mFileInfo
);
2445 // Called when receiving from the child.
2446 DatabaseFile(RefPtr
<BlobImpl
> aBlobImpl
,
2447 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
)
2448 : mBlobImpl(std::move(aBlobImpl
)), mFileInfo(std::move(aFileInfo
)) {
2449 AssertIsOnBackgroundThread();
2450 MOZ_ASSERT(*mBlobImpl
);
2451 MOZ_ASSERT(mFileInfo
);
2455 ~DatabaseFile() override
= default;
2457 void ActorDestroy(ActorDestroyReason aWhy
) override
{
2458 AssertIsOnBackgroundThread();
2462 nsCOMPtr
<nsIInputStream
> DatabaseFile::GetInputStream(ErrorResult
& rv
) const {
2463 // We should only be called from our DB connection thread, not the background
2465 MOZ_ASSERT(!IsOnBackgroundThread());
2467 // If we were constructed without a BlobImpl, or WriteSucceededClearBlobImpl
2468 // was already called, return nullptr.
2469 if (!mBlobImpl
|| !*mBlobImpl
) {
2473 nsCOMPtr
<nsIInputStream
> inputStream
;
2474 (*mBlobImpl
)->CreateInputStream(getter_AddRefs(inputStream
), rv
);
2482 class TransactionBase
: public AtomicSafeRefCounted
<TransactionBase
> {
2483 friend class CursorBase
;
2485 template <IDBCursorType CursorType
>
2486 friend class Cursor
;
2491 using Mode
= IDBTransaction::Mode
;
2492 using Durability
= IDBTransaction::Durability
;
2495 const SafeRefPtr
<Database
> mDatabase
;
2496 nsTArray
<SafeRefPtr
<FullObjectStoreMetadata
>>
2497 mModifiedAutoIncrementObjectStoreMetadataArray
;
2498 LazyInitializedOnceNotNull
<const uint64_t> mTransactionId
;
2499 const nsCString mDatabaseId
;
2500 const int64_t mLoggingSerialNumber
;
2501 uint64_t mActiveRequestCount
;
2502 Atomic
<bool> mInvalidatedOnAnyThread
;
2504 const Durability mDurability
; // TODO: See bug 1883045
2505 FlippedOnce
<false> mInitialized
;
2506 FlippedOnce
<false> mHasBeenActiveOnConnectionThread
;
2507 FlippedOnce
<false> mActorDestroyed
;
2508 FlippedOnce
<false> mInvalidated
;
2511 nsresult mResultCode
;
2512 FlippedOnce
<false> mCommitOrAbortReceived
;
2513 FlippedOnce
<false> mCommittedOrAborted
;
2514 FlippedOnce
<false> mForceAborted
;
2515 LazyInitializedOnce
<const Maybe
<int64_t>> mLastRequestBeforeCommit
;
2516 Maybe
<int64_t> mLastFailedRequest
;
2519 void AssertIsOnConnectionThread() const {
2520 MOZ_ASSERT(mDatabase
);
2521 mDatabase
->AssertIsOnConnectionThread();
2524 bool IsActorDestroyed() const {
2525 AssertIsOnBackgroundThread();
2527 return mActorDestroyed
;
2530 // Must be called on the background thread.
2531 bool IsInvalidated() const {
2532 MOZ_ASSERT(IsOnBackgroundThread(), "Use IsInvalidatedOnAnyThread()");
2533 MOZ_ASSERT_IF(mInvalidated
, NS_FAILED(mResultCode
));
2535 return mInvalidated
;
2538 // May be called on any thread, but is more expensive than IsInvalidated().
2539 bool IsInvalidatedOnAnyThread() const { return mInvalidatedOnAnyThread
; }
2541 void Init(const uint64_t aTransactionId
) {
2542 AssertIsOnBackgroundThread();
2543 MOZ_ASSERT(aTransactionId
);
2545 mTransactionId
.init(aTransactionId
);
2546 mInitialized
.Flip();
2549 void SetActiveOnConnectionThread() {
2550 AssertIsOnConnectionThread();
2551 mHasBeenActiveOnConnectionThread
.Flip();
2554 MOZ_DECLARE_REFCOUNTED_TYPENAME(mozilla::dom::indexedDB::TransactionBase
)
2556 void Abort(nsresult aResultCode
, bool aForce
);
2558 uint64_t TransactionId() const { return *mTransactionId
; }
2560 const nsACString
& DatabaseId() const { return mDatabaseId
; }
2562 Mode
GetMode() const { return mMode
; }
2564 Durability
GetDurability() const { return mDurability
; }
2566 const Database
& GetDatabase() const {
2567 MOZ_ASSERT(mDatabase
);
2572 Database
& GetMutableDatabase() const {
2573 MOZ_ASSERT(mDatabase
);
2578 SafeRefPtr
<Database
> GetDatabasePtr() const {
2579 MOZ_ASSERT(mDatabase
);
2581 return mDatabase
.clonePtr();
2584 DatabaseLoggingInfo
* GetLoggingInfo() const {
2585 AssertIsOnBackgroundThread();
2586 MOZ_ASSERT(mDatabase
);
2588 return mDatabase
->GetLoggingInfo();
2591 int64_t LoggingSerialNumber() const { return mLoggingSerialNumber
; }
2593 bool IsAborted() const {
2594 AssertIsOnBackgroundThread();
2596 return NS_FAILED(mResultCode
);
2599 [[nodiscard
]] SafeRefPtr
<FullObjectStoreMetadata
> GetMetadataForObjectStoreId(
2600 IndexOrObjectStoreId aObjectStoreId
) const;
2602 [[nodiscard
]] SafeRefPtr
<FullIndexMetadata
> GetMetadataForIndexId(
2603 FullObjectStoreMetadata
& aObjectStoreMetadata
,
2604 IndexOrObjectStoreId aIndexId
) const;
2606 PBackgroundParent
* GetBackgroundParent() const {
2607 AssertIsOnBackgroundThread();
2608 MOZ_ASSERT(!IsActorDestroyed());
2610 return GetDatabase().GetBackgroundParent();
2613 void NoteModifiedAutoIncrementObjectStore(
2614 const SafeRefPtr
<FullObjectStoreMetadata
>& aMetadata
);
2616 void ForgetModifiedAutoIncrementObjectStore(
2617 FullObjectStoreMetadata
& aMetadata
);
2619 void NoteActiveRequest();
2621 void NoteFinishedRequest(int64_t aRequestId
, nsresult aResultCode
);
2625 virtual ~TransactionBase();
2628 TransactionBase(SafeRefPtr
<Database
> aDatabase
, Mode aMode
,
2629 Durability aDurability
);
2631 void NoteActorDestroyed() {
2632 AssertIsOnBackgroundThread();
2634 mActorDestroyed
.Flip();
2638 // Only called by VersionChangeTransaction.
2639 void FakeActorDestroyed() { mActorDestroyed
.EnsureFlipped(); }
2642 mozilla::ipc::IPCResult
RecvCommit(IProtocol
* aActor
,
2643 const Maybe
<int64_t> aLastRequest
);
2645 mozilla::ipc::IPCResult
RecvAbort(IProtocol
* aActor
, nsresult aResultCode
);
2647 void MaybeCommitOrAbort() {
2648 AssertIsOnBackgroundThread();
2650 // If we've already committed or aborted then there's nothing else to do.
2651 if (mCommittedOrAborted
) {
2655 // If there are active requests then we have to wait for those requests to
2656 // complete (see NoteFinishedRequest).
2657 if (mActiveRequestCount
) {
2661 // If we haven't yet received a commit or abort message then there could be
2662 // additional requests coming so we should wait unless we're being forced to
2664 if (!mCommitOrAbortReceived
&& !mForceAborted
) {
2671 PBackgroundIDBRequestParent
* AllocRequest(const int64_t aRequestId
,
2672 RequestParams
&& aParams
,
2675 bool StartRequest(PBackgroundIDBRequestParent
* aActor
);
2677 bool DeallocRequest(PBackgroundIDBRequestParent
* aActor
);
2679 already_AddRefed
<PBackgroundIDBCursorParent
> AllocCursor(
2680 const OpenCursorParams
& aParams
, bool aTrustParams
);
2682 bool StartCursor(PBackgroundIDBCursorParent
* aActor
, const int64_t aRequestId
,
2683 const OpenCursorParams
& aParams
);
2685 virtual void UpdateMetadata(nsresult aResult
) {}
2687 virtual void SendCompleteNotification(nsresult aResult
) = 0;
2690 bool VerifyRequestParams(const RequestParams
& aParams
) const;
2692 bool VerifyRequestParams(const SerializedKeyRange
& aParams
) const;
2694 bool VerifyRequestParams(const ObjectStoreAddPutParams
& aParams
) const;
2696 bool VerifyRequestParams(const Maybe
<SerializedKeyRange
>& aParams
) const;
2698 void CommitOrAbort();
2701 class TransactionBase::CommitOp final
: public DatabaseOperationBase
,
2702 public ConnectionPool::FinishCallback
{
2703 friend class TransactionBase
;
2705 SafeRefPtr
<TransactionBase
> mTransaction
;
2706 nsresult mResultCode
; ///< TODO: There is also a mResultCode in
2707 ///< DatabaseOperationBase. Is there a reason not to
2708 ///< use that? At least a more specific name should be
2709 ///< given to this one.
2712 CommitOp(SafeRefPtr
<TransactionBase
> aTransaction
, nsresult aResultCode
);
2714 ~CommitOp() override
= default;
2716 // Writes new autoIncrement counts to database.
2717 nsresult
WriteAutoIncrementCounts();
2719 // Updates counts after a database activity has finished.
2720 void CommitOrRollbackAutoIncrementCounts();
2722 void AssertForeignKeyConsistency(DatabaseConnection
* aConnection
)
2732 void TransactionFinishedBeforeUnblock() override
;
2734 void TransactionFinishedAfterUnblock() override
;
2737 // We need to declare all of nsISupports, because FinishCallback has
2738 // a pure-virtual nsISupports declaration.
2739 NS_DECL_ISUPPORTS_INHERITED
2742 class NormalTransaction final
: public TransactionBase
,
2743 public PBackgroundIDBTransactionParent
{
2744 nsTArray
<SafeRefPtr
<FullObjectStoreMetadata
>> mObjectStores
;
2746 // Reference counted.
2747 ~NormalTransaction() override
= default;
2749 bool IsSameProcessActor();
2751 // Only called by TransactionBase.
2752 void SendCompleteNotification(nsresult aResult
) override
;
2754 // IPDL methods are only called by IPDL.
2755 void ActorDestroy(ActorDestroyReason aWhy
) override
;
2757 mozilla::ipc::IPCResult
RecvDeleteMe() override
;
2759 mozilla::ipc::IPCResult
RecvCommit(
2760 const Maybe
<int64_t>& aLastRequest
) override
;
2762 mozilla::ipc::IPCResult
RecvAbort(const nsresult
& aResultCode
) override
;
2764 PBackgroundIDBRequestParent
* AllocPBackgroundIDBRequestParent(
2765 const int64_t& aRequestId
, const RequestParams
& aParams
) override
;
2767 mozilla::ipc::IPCResult
RecvPBackgroundIDBRequestConstructor(
2768 PBackgroundIDBRequestParent
* aActor
, const int64_t& aRequestId
,
2769 const RequestParams
& aParams
) override
;
2771 bool DeallocPBackgroundIDBRequestParent(
2772 PBackgroundIDBRequestParent
* aActor
) override
;
2774 already_AddRefed
<PBackgroundIDBCursorParent
> AllocPBackgroundIDBCursorParent(
2775 const int64_t& aRequestId
, const OpenCursorParams
& aParams
) override
;
2777 mozilla::ipc::IPCResult
RecvPBackgroundIDBCursorConstructor(
2778 PBackgroundIDBCursorParent
* aActor
, const int64_t& aRequestId
,
2779 const OpenCursorParams
& aParams
) override
;
2782 // This constructor is only called by Database.
2784 SafeRefPtr
<Database
> aDatabase
, TransactionBase::Mode aMode
,
2785 TransactionBase::Durability aDurability
,
2786 nsTArray
<SafeRefPtr
<FullObjectStoreMetadata
>>&& aObjectStores
);
2788 MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(NormalTransaction
, TransactionBase
)
2791 class VersionChangeTransaction final
2792 : public TransactionBase
,
2793 public PBackgroundIDBVersionChangeTransactionParent
{
2794 friend class OpenDatabaseOp
;
2796 RefPtr
<OpenDatabaseOp
> mOpenDatabaseOp
;
2797 SafeRefPtr
<FullDatabaseMetadata
> mOldMetadata
;
2799 FlippedOnce
<false> mActorWasAlive
;
2802 // Only called by OpenDatabaseOp.
2803 explicit VersionChangeTransaction(OpenDatabaseOp
* aOpenDatabaseOp
);
2805 MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(VersionChangeTransaction
,
2809 // Reference counted.
2810 ~VersionChangeTransaction() override
;
2812 bool IsSameProcessActor();
2814 // Only called by OpenDatabaseOp.
2815 bool CopyDatabaseMetadata();
2817 void SetActorAlive();
2819 // Only called by TransactionBase.
2820 void UpdateMetadata(nsresult aResult
) override
;
2822 // Only called by TransactionBase.
2823 void SendCompleteNotification(nsresult aResult
) override
;
2825 // IPDL methods are only called by IPDL.
2826 void ActorDestroy(ActorDestroyReason aWhy
) override
;
2828 mozilla::ipc::IPCResult
RecvDeleteMe() override
;
2830 mozilla::ipc::IPCResult
RecvCommit(
2831 const Maybe
<int64_t>& aLastRequest
) override
;
2833 mozilla::ipc::IPCResult
RecvAbort(const nsresult
& aResultCode
) override
;
2835 mozilla::ipc::IPCResult
RecvCreateObjectStore(
2836 const ObjectStoreMetadata
& aMetadata
) override
;
2838 mozilla::ipc::IPCResult
RecvDeleteObjectStore(
2839 const IndexOrObjectStoreId
& aObjectStoreId
) override
;
2841 mozilla::ipc::IPCResult
RecvRenameObjectStore(
2842 const IndexOrObjectStoreId
& aObjectStoreId
,
2843 const nsAString
& aName
) override
;
2845 mozilla::ipc::IPCResult
RecvCreateIndex(
2846 const IndexOrObjectStoreId
& aObjectStoreId
,
2847 const IndexMetadata
& aMetadata
) override
;
2849 mozilla::ipc::IPCResult
RecvDeleteIndex(
2850 const IndexOrObjectStoreId
& aObjectStoreId
,
2851 const IndexOrObjectStoreId
& aIndexId
) override
;
2853 mozilla::ipc::IPCResult
RecvRenameIndex(
2854 const IndexOrObjectStoreId
& aObjectStoreId
,
2855 const IndexOrObjectStoreId
& aIndexId
, const nsAString
& aName
) override
;
2857 PBackgroundIDBRequestParent
* AllocPBackgroundIDBRequestParent(
2858 const int64_t& aRequestId
, const RequestParams
& aParams
) override
;
2860 mozilla::ipc::IPCResult
RecvPBackgroundIDBRequestConstructor(
2861 PBackgroundIDBRequestParent
* aActor
, const int64_t& aRequestId
,
2862 const RequestParams
& aParams
) override
;
2864 bool DeallocPBackgroundIDBRequestParent(
2865 PBackgroundIDBRequestParent
* aActor
) override
;
2867 already_AddRefed
<PBackgroundIDBCursorParent
> AllocPBackgroundIDBCursorParent(
2868 const int64_t& aRequestId
, const OpenCursorParams
& aParams
) override
;
2870 mozilla::ipc::IPCResult
RecvPBackgroundIDBCursorConstructor(
2871 PBackgroundIDBCursorParent
* aActor
, const int64_t& aRequestId
,
2872 const OpenCursorParams
& aParams
) override
;
2876 : public DatabaseOperationBase
,
2877 public SupportsCheckedUnsafePtr
<CheckIf
<DiagnosticAssertEnabled
>> {
2879 struct MaybeBlockedDatabaseInfo final
{
2880 SafeRefPtr
<Database
> mDatabase
;
2883 MaybeBlockedDatabaseInfo(MaybeBlockedDatabaseInfo
&&) = default;
2884 MaybeBlockedDatabaseInfo
& operator=(MaybeBlockedDatabaseInfo
&&) = default;
2886 MOZ_IMPLICIT
MaybeBlockedDatabaseInfo(SafeRefPtr
<Database
> aDatabase
)
2887 : mDatabase(std::move(aDatabase
)), mBlocked(false) {
2888 MOZ_ASSERT(mDatabase
);
2890 MOZ_COUNT_CTOR(FactoryOp::MaybeBlockedDatabaseInfo
);
2893 ~MaybeBlockedDatabaseInfo() {
2894 MOZ_COUNT_DTOR(FactoryOp::MaybeBlockedDatabaseInfo
);
2897 bool operator==(const Database
* aOther
) const {
2898 return mDatabase
== aOther
;
2901 Database
* operator->() const& MOZ_NO_ADDREF_RELEASE_ON_RETURN
{
2902 return mDatabase
.unsafeGetRawPtr();
2908 // Just created on the PBackground thread, dispatched to the current thread.
2909 // Next step is either SendingResults if opening initialization failed, or
2910 // DirectoryOpenPending if the opening initialization succeeded.
2913 // Waiting for directory open allowed on the PBackground thread. The next
2914 // step is either SendingResults if directory lock failed to acquire, or
2915 // DirectoryWorkOpen if the factory operation is not tied up to a specific
2916 // database, or DatabaseOpenPending otherwise.
2917 DirectoryOpenPending
,
2919 // Waiting to do/doing directory work on the QuotaManager IO thread. Its
2920 // next step is DirectoryWorkDone if directory work was successful or
2921 // SendingResults if directory work failed.
2924 // Checking if database work can be started. If the database is not blocked
2925 // by other factory operations then the next step is DatabaseWorkOpen.
2926 // Otherwise the next step is DatabaseOpenPending.
2929 // Waiting for database open allowed on the PBackground thread. The next
2930 // step is DatabaseWorkOpen.
2931 DatabaseOpenPending
,
2933 // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
2934 // either BeginVersionChange if the requested version doesn't match the
2935 // existing database version or SendingResults if the versions match.
2938 // Starting a version change transaction or deleting a database on the
2939 // PBackground thread. We need to notify other databases that a version
2940 // change is about to happen, and maybe tell the request that a version
2941 // change has been blocked. If databases are notified then the next step is
2942 // WaitingForOtherDatabasesToClose. Otherwise the next step is
2943 // WaitingForTransactionsToComplete.
2946 // Waiting for other databases to close on the PBackground thread. This
2947 // state may persist until all databases are closed. The next state is
2948 // WaitingForTransactionsToComplete.
2949 WaitingForOtherDatabasesToClose
,
2951 // Waiting for all transactions that could interfere with this operation to
2952 // complete on the PBackground thread. Next state is
2953 // DatabaseWorkVersionChange.
2954 WaitingForTransactionsToComplete
,
2956 // Waiting to do/doing work on the "work thread". This involves waiting for
2957 // the VersionChangeOp (OpenDatabaseOp and DeleteDatabaseOp each have a
2958 // different implementation) to do its work. Eventually the state will
2959 // transition to SendingResults.
2960 DatabaseWorkVersionChange
,
2962 // Waiting to send/sending results on the PBackground thread. Next step is
2970 // Must be released on the background thread!
2971 SafeRefPtr
<Factory
> mFactory
;
2973 Maybe
<ContentParentId
> mContentParentId
;
2975 // Must be released on the main thread!
2976 RefPtr
<DirectoryLock
> mDirectoryLock
;
2978 nsTArray
<NotNull
<RefPtr
<FactoryOp
>>> mBlocking
;
2979 nsTArray
<NotNull
<RefPtr
<FactoryOp
>>> mBlockedOn
;
2981 nsTArray
<MaybeBlockedDatabaseInfo
> mMaybeBlockedDatabases
;
2983 const PrincipalInfo mPrincipalInfo
;
2984 OriginMetadata mOriginMetadata
;
2985 Maybe
<nsString
> mDatabaseName
;
2986 Maybe
<nsCString
> mDatabaseId
;
2987 Maybe
<nsString
> mDatabaseFilePath
;
2988 int64_t mDirectoryLockId
;
2989 const PersistenceType mPersistenceType
;
2991 bool mWaitingForPermissionRetry
;
2992 bool mEnforcingQuota
;
2993 const bool mDeleting
;
2994 FlippedOnce
<false> mInPrivateBrowsing
;
2997 const nsACString
& Origin() const {
2998 AssertIsOnOwningThread();
3000 return mOriginMetadata
.mOrigin
;
3003 const Maybe
<nsString
>& DatabaseNameRef() const {
3004 AssertIsOnOwningThread();
3006 return mDatabaseName
;
3009 bool DatabaseFilePathIsKnown() const {
3010 AssertIsOnOwningThread();
3012 return mDatabaseFilePath
.isSome();
3015 const nsAString
& DatabaseFilePath() const {
3016 AssertIsOnOwningThread();
3017 MOZ_ASSERT(mDatabaseFilePath
);
3019 return mDatabaseFilePath
.ref();
3022 void NoteDatabaseBlocked(Database
* aDatabase
);
3024 void NoteDatabaseClosed(Database
* aDatabase
);
3027 bool HasBlockedDatabases() const { return !mMaybeBlockedDatabases
.IsEmpty(); }
3030 void StringifyState(nsACString
& aResult
) const;
3032 void Stringify(nsACString
& aResult
) const;
3035 FactoryOp(SafeRefPtr
<Factory
> aFactory
,
3036 const Maybe
<ContentParentId
>& aContentParentId
,
3037 const PersistenceType aPersistenceType
,
3038 const PrincipalInfo
& aPrincipalInfo
,
3039 const Maybe
<nsString
>& aDatabaseName
, bool aDeleting
);
3041 ~FactoryOp() override
{
3042 // Normally this would be out-of-line since it is a virtual function but
3043 // MSVC 2010 fails to link for some reason if it is not inlined here...
3044 MOZ_ASSERT_IF(OperationMayProceed(),
3045 mState
== State::Initial
|| mState
== State::Completed
);
3050 nsresult
DirectoryOpen();
3052 nsresult
DirectoryWorkDone();
3054 nsresult
SendToIOThread();
3056 void WaitForTransactions();
3058 void CleanupMetadata();
3060 void FinishSendResults();
3062 nsresult
SendVersionChangeMessages(DatabaseActorInfo
* aDatabaseActorInfo
,
3063 Maybe
<Database
&> aOpeningDatabase
,
3064 uint64_t aOldVersion
,
3065 const Maybe
<uint64_t>& aNewVersion
);
3067 // Methods that subclasses must implement.
3068 virtual nsresult
DoDirectoryWork() = 0;
3070 virtual nsresult
DatabaseOpen() = 0;
3072 virtual nsresult
DoDatabaseWork() = 0;
3074 virtual nsresult
BeginVersionChange() = 0;
3076 virtual bool AreActorsAlive() = 0;
3078 virtual nsresult
DispatchToWorkThread() = 0;
3080 // Should only be called by Run().
3081 virtual void SendResults() = 0;
3083 // Common nsIRunnable implementation that subclasses may not override.
3087 void DirectoryLockAcquired(DirectoryLock
* aLock
);
3089 void DirectoryLockFailed();
3091 virtual void SendBlockedNotification() = 0;
3094 // Test whether this FactoryOp needs to wait for the given op.
3095 bool MustWaitFor(const FactoryOp
& aExistingOp
);
3097 void AddBlockingOp(FactoryOp
& aOp
) {
3098 AssertIsOnOwningThread();
3100 mBlocking
.AppendElement(WrapNotNull(&aOp
));
3103 void AddBlockedOnOp(FactoryOp
& aOp
) {
3104 AssertIsOnOwningThread();
3106 mBlockedOn
.AppendElement(WrapNotNull(&aOp
));
3109 void MaybeUnblock(FactoryOp
& aOp
) {
3110 AssertIsOnOwningThread();
3112 mBlockedOn
.RemoveElement(&aOp
);
3113 if (mBlockedOn
.IsEmpty()) {
3114 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
3119 class FactoryRequestOp
: public FactoryOp
,
3120 public PBackgroundIDBFactoryRequestParent
{
3122 const CommonFactoryRequestParams mCommonParams
;
3124 FactoryRequestOp(SafeRefPtr
<Factory
> aFactory
,
3125 const Maybe
<ContentParentId
>& aContentParentId
,
3126 const CommonFactoryRequestParams
& aCommonParams
,
3128 : FactoryOp(std::move(aFactory
), aContentParentId
,
3129 aCommonParams
.metadata().persistenceType(),
3130 aCommonParams
.principalInfo(),
3131 Some(aCommonParams
.metadata().name()), aDeleting
),
3132 mCommonParams(aCommonParams
) {}
3134 nsresult
DoDirectoryWork() override
;
3137 void ActorDestroy(ActorDestroyReason aWhy
) override
;
3140 class OpenDatabaseOp final
: public FactoryRequestOp
{
3141 friend class Database
;
3142 friend class VersionChangeTransaction
;
3144 class VersionChangeOp
;
3146 SafeRefPtr
<FullDatabaseMetadata
> mMetadata
;
3148 uint64_t mRequestedVersion
;
3149 SafeRefPtr
<DatabaseFileManager
> mFileManager
;
3151 SafeRefPtr
<Database
> mDatabase
;
3152 SafeRefPtr
<VersionChangeTransaction
> mVersionChangeTransaction
;
3154 // This is only set while a VersionChangeOp is live. It holds a strong
3155 // reference to its OpenDatabaseOp object so this is a weak pointer to avoid
3157 VersionChangeOp
* mVersionChangeOp
;
3159 uint32_t mTelemetryId
;
3162 OpenDatabaseOp(SafeRefPtr
<Factory
> aFactory
,
3163 const Maybe
<ContentParentId
>& aContentParentId
,
3164 const CommonFactoryRequestParams
& aParams
);
3167 ~OpenDatabaseOp() override
{ MOZ_ASSERT(!mVersionChangeOp
); }
3169 nsresult
LoadDatabaseInformation(mozIStorageConnection
& aConnection
);
3171 nsresult
SendUpgradeNeeded();
3173 void EnsureDatabaseActor();
3175 nsresult
EnsureDatabaseActorIsAlive();
3177 mozilla::Result
<DatabaseSpec
, nsresult
> MetadataToSpec() const;
3179 void AssertMetadataConsistency(const FullDatabaseMetadata
& aMetadata
)
3187 void ConnectionClosedCallback();
3189 void ActorDestroy(ActorDestroyReason aWhy
) override
;
3191 nsresult
DatabaseOpen() override
;
3193 nsresult
DoDatabaseWork() override
;
3195 nsresult
BeginVersionChange() override
;
3197 bool AreActorsAlive() override
;
3199 void SendBlockedNotification() override
;
3201 nsresult
DispatchToWorkThread() override
;
3203 void SendResults() override
;
3205 static nsresult
UpdateLocaleAwareIndex(mozIStorageConnection
& aConnection
,
3206 const IndexMetadata
& aIndexMetadata
,
3207 const nsCString
& aLocale
);
3210 class OpenDatabaseOp::VersionChangeOp final
3211 : public TransactionDatabaseOperationBase
{
3212 friend class OpenDatabaseOp
;
3214 RefPtr
<OpenDatabaseOp
> mOpenDatabaseOp
;
3215 const uint64_t mRequestedVersion
;
3216 uint64_t mPreviousVersion
;
3219 explicit VersionChangeOp(OpenDatabaseOp
* aOpenDatabaseOp
)
3220 : TransactionDatabaseOperationBase(
3221 aOpenDatabaseOp
->mVersionChangeTransaction
.clonePtr(),
3222 /* aRequestId */ 0, aOpenDatabaseOp
->LoggingSerialNumber()),
3223 mOpenDatabaseOp(aOpenDatabaseOp
),
3224 mRequestedVersion(aOpenDatabaseOp
->mRequestedVersion
),
3226 aOpenDatabaseOp
->mMetadata
->mCommonMetadata
.version()) {
3227 MOZ_ASSERT(aOpenDatabaseOp
);
3228 MOZ_ASSERT(mRequestedVersion
);
3231 ~VersionChangeOp() override
{ MOZ_ASSERT(!mOpenDatabaseOp
); }
3233 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3235 nsresult
SendSuccessResult() override
;
3237 bool SendFailureResult(nsresult aResultCode
) override
;
3239 void Cleanup() override
;
3242 class DeleteDatabaseOp final
: public FactoryRequestOp
{
3243 class VersionChangeOp
;
3245 nsString mDatabaseDirectoryPath
;
3246 nsString mDatabaseFilenameBase
;
3247 uint64_t mPreviousVersion
;
3250 DeleteDatabaseOp(SafeRefPtr
<Factory
> aFactory
,
3251 const Maybe
<ContentParentId
>& aContentParentId
,
3252 const CommonFactoryRequestParams
& aParams
)
3253 : FactoryRequestOp(std::move(aFactory
), aContentParentId
, aParams
,
3254 /* aDeleting */ true),
3255 mPreviousVersion(0) {}
3258 ~DeleteDatabaseOp() override
= default;
3260 void LoadPreviousVersion(nsIFile
& aDatabaseFile
);
3262 nsresult
DatabaseOpen() override
;
3264 nsresult
DoDatabaseWork() override
;
3266 nsresult
BeginVersionChange() override
;
3268 bool AreActorsAlive() override
;
3270 void SendBlockedNotification() override
;
3272 nsresult
DispatchToWorkThread() override
;
3274 void SendResults() override
;
3277 class DeleteDatabaseOp::VersionChangeOp final
: public DatabaseOperationBase
{
3278 friend class DeleteDatabaseOp
;
3280 RefPtr
<DeleteDatabaseOp
> mDeleteDatabaseOp
;
3283 explicit VersionChangeOp(DeleteDatabaseOp
* aDeleteDatabaseOp
)
3284 : DatabaseOperationBase(aDeleteDatabaseOp
->BackgroundChildLoggingId(),
3285 aDeleteDatabaseOp
->LoggingSerialNumber()),
3286 mDeleteDatabaseOp(aDeleteDatabaseOp
) {
3287 MOZ_ASSERT(aDeleteDatabaseOp
);
3288 MOZ_ASSERT(!aDeleteDatabaseOp
->mDatabaseDirectoryPath
.IsEmpty());
3291 ~VersionChangeOp() override
= default;
3293 nsresult
RunOnIOThread();
3295 void RunOnOwningThread();
3300 class GetDatabasesOp final
: public FactoryOp
{
3301 nsTHashMap
<nsStringHashKey
, DatabaseMetadata
> mDatabaseMetadataTable
;
3302 nsTArray
<DatabaseMetadata
> mDatabaseMetadataArray
;
3303 Factory::GetDatabasesResolver mResolver
;
3306 GetDatabasesOp(SafeRefPtr
<Factory
> aFactory
,
3307 const Maybe
<ContentParentId
>& aContentParentId
,
3308 const PersistenceType aPersistenceType
,
3309 const PrincipalInfo
& aPrincipalInfo
,
3310 Factory::GetDatabasesResolver
&& aResolver
)
3311 : FactoryOp(std::move(aFactory
), aContentParentId
, aPersistenceType
,
3312 aPrincipalInfo
, Nothing(), /* aDeleting */ false),
3313 mResolver(std::move(aResolver
)) {}
3316 ~GetDatabasesOp() override
= default;
3318 nsresult
DatabasesNotAvailable();
3320 nsresult
DoDirectoryWork() override
;
3322 nsresult
DatabaseOpen() override
;
3324 nsresult
DoDatabaseWork() override
;
3326 nsresult
BeginVersionChange() override
;
3328 bool AreActorsAlive() override
;
3330 void SendBlockedNotification() override
;
3332 nsresult
DispatchToWorkThread() override
;
3334 void SendResults() override
;
3337 class VersionChangeTransactionOp
: public TransactionDatabaseOperationBase
{
3339 void Cleanup() override
;
3342 explicit VersionChangeTransactionOp(
3343 SafeRefPtr
<VersionChangeTransaction
> aTransaction
)
3344 : TransactionDatabaseOperationBase(std::move(aTransaction
),
3345 /* aRequestId */ 0) {}
3347 ~VersionChangeTransactionOp() override
= default;
3350 nsresult
SendSuccessResult() override
;
3352 bool SendFailureResult(nsresult aResultCode
) override
;
3355 class CreateObjectStoreOp final
: public VersionChangeTransactionOp
{
3356 friend class VersionChangeTransaction
;
3358 const ObjectStoreMetadata mMetadata
;
3361 // Only created by VersionChangeTransaction.
3362 CreateObjectStoreOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
3363 const ObjectStoreMetadata
& aMetadata
)
3364 : VersionChangeTransactionOp(std::move(aTransaction
)),
3365 mMetadata(aMetadata
) {
3366 MOZ_ASSERT(aMetadata
.id());
3369 ~CreateObjectStoreOp() override
= default;
3371 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3374 class DeleteObjectStoreOp final
: public VersionChangeTransactionOp
{
3375 friend class VersionChangeTransaction
;
3377 const SafeRefPtr
<FullObjectStoreMetadata
> mMetadata
;
3378 const bool mIsLastObjectStore
;
3381 // Only created by VersionChangeTransaction.
3382 DeleteObjectStoreOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
3383 SafeRefPtr
<FullObjectStoreMetadata
> aMetadata
,
3384 const bool aIsLastObjectStore
)
3385 : VersionChangeTransactionOp(std::move(aTransaction
)),
3386 mMetadata(std::move(aMetadata
)),
3387 mIsLastObjectStore(aIsLastObjectStore
) {
3388 MOZ_ASSERT(mMetadata
->mCommonMetadata
.id());
3391 ~DeleteObjectStoreOp() override
= default;
3393 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3396 class RenameObjectStoreOp final
: public VersionChangeTransactionOp
{
3397 friend class VersionChangeTransaction
;
3400 const nsString mNewName
;
3403 // Only created by VersionChangeTransaction.
3404 RenameObjectStoreOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
3405 FullObjectStoreMetadata
& aMetadata
)
3406 : VersionChangeTransactionOp(std::move(aTransaction
)),
3407 mId(aMetadata
.mCommonMetadata
.id()),
3408 mNewName(aMetadata
.mCommonMetadata
.name()) {
3412 ~RenameObjectStoreOp() override
= default;
3414 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3417 class CreateIndexOp final
: public VersionChangeTransactionOp
{
3418 friend class VersionChangeTransaction
;
3420 class UpdateIndexDataValuesFunction
;
3422 const IndexMetadata mMetadata
;
3423 Maybe
<UniqueIndexTable
> mMaybeUniqueIndexTable
;
3424 const SafeRefPtr
<DatabaseFileManager
> mFileManager
;
3425 const nsCString mDatabaseId
;
3426 const IndexOrObjectStoreId mObjectStoreId
;
3429 // Only created by VersionChangeTransaction.
3430 CreateIndexOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
3431 IndexOrObjectStoreId aObjectStoreId
,
3432 const IndexMetadata
& aMetadata
);
3434 ~CreateIndexOp() override
= default;
3436 nsresult
InsertDataFromObjectStore(DatabaseConnection
* aConnection
);
3438 nsresult
InsertDataFromObjectStoreInternal(
3439 DatabaseConnection
* aConnection
) const;
3441 bool Init(TransactionBase
& aTransaction
) override
;
3443 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3446 class CreateIndexOp::UpdateIndexDataValuesFunction final
3447 : public mozIStorageFunction
{
3448 RefPtr
<CreateIndexOp
> mOp
;
3449 RefPtr
<DatabaseConnection
> mConnection
;
3450 const NotNull
<SafeRefPtr
<Database
>> mDatabase
;
3453 UpdateIndexDataValuesFunction(CreateIndexOp
* aOp
,
3454 DatabaseConnection
* aConnection
,
3455 SafeRefPtr
<Database
> aDatabase
)
3457 mConnection(aConnection
),
3458 mDatabase(WrapNotNull(std::move(aDatabase
))) {
3460 MOZ_ASSERT(aConnection
);
3461 aConnection
->AssertIsOnConnectionThread();
3467 ~UpdateIndexDataValuesFunction() = default;
3469 NS_DECL_MOZISTORAGEFUNCTION
3472 class DeleteIndexOp final
: public VersionChangeTransactionOp
{
3473 friend class VersionChangeTransaction
;
3475 const IndexOrObjectStoreId mObjectStoreId
;
3476 const IndexOrObjectStoreId mIndexId
;
3478 const bool mIsLastIndex
;
3481 // Only created by VersionChangeTransaction.
3482 DeleteIndexOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
3483 IndexOrObjectStoreId aObjectStoreId
,
3484 IndexOrObjectStoreId aIndexId
, const bool aUnique
,
3485 const bool aIsLastIndex
);
3487 ~DeleteIndexOp() override
= default;
3489 nsresult
RemoveReferencesToIndex(
3490 DatabaseConnection
* aConnection
, const Key
& aObjectDataKey
,
3491 nsTArray
<IndexDataValue
>& aIndexValues
) const;
3493 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3496 class RenameIndexOp final
: public VersionChangeTransactionOp
{
3497 friend class VersionChangeTransaction
;
3499 const IndexOrObjectStoreId mObjectStoreId
;
3500 const IndexOrObjectStoreId mIndexId
;
3501 const nsString mNewName
;
3504 // Only created by VersionChangeTransaction.
3505 RenameIndexOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
3506 FullIndexMetadata
& aMetadata
,
3507 IndexOrObjectStoreId aObjectStoreId
)
3508 : VersionChangeTransactionOp(std::move(aTransaction
)),
3509 mObjectStoreId(aObjectStoreId
),
3510 mIndexId(aMetadata
.mCommonMetadata
.id()),
3511 mNewName(aMetadata
.mCommonMetadata
.name()) {
3512 MOZ_ASSERT(mIndexId
);
3515 ~RenameIndexOp() override
= default;
3517 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3520 class NormalTransactionOp
: public TransactionDatabaseOperationBase
,
3521 public PBackgroundIDBRequestParent
{
3527 void Cleanup() override
;
3530 NormalTransactionOp(SafeRefPtr
<TransactionBase
> aTransaction
,
3531 const int64_t aRequestId
)
3532 : TransactionDatabaseOperationBase(std::move(aTransaction
), aRequestId
)
3535 mResponseSent(false)
3540 ~NormalTransactionOp() override
= default;
3542 // An overload of DatabaseOperationBase's function that can avoid doing extra
3543 // work on non-versionchange transactions.
3544 mozilla::Result
<bool, nsresult
> ObjectStoreHasIndexes(
3545 DatabaseConnection
& aConnection
, IndexOrObjectStoreId aObjectStoreId
,
3546 bool aMayHaveIndexes
);
3548 virtual mozilla::Result
<PreprocessParams
, nsresult
> GetPreprocessParams();
3550 // Subclasses use this override to set the IPDL response value.
3551 virtual void GetResponse(RequestResponse
& aResponse
,
3552 size_t* aResponseSize
) = 0;
3555 nsresult
SendPreprocessInfo() override
;
3557 nsresult
SendSuccessResult() override
;
3559 bool SendFailureResult(nsresult aResultCode
) override
;
3562 void ActorDestroy(ActorDestroyReason aWhy
) override
;
3564 mozilla::ipc::IPCResult
RecvContinue(
3565 const PreprocessResponse
& aResponse
) final
;
3568 class ObjectStoreAddOrPutRequestOp final
: public NormalTransactionOp
{
3569 friend class TransactionBase
;
3571 using PersistenceType
= mozilla::dom::quota::PersistenceType
;
3573 class StoredFileInfo final
{
3574 InitializedOnce
<const NotNull
<SafeRefPtr
<DatabaseFileInfo
>>> mFileInfo
;
3575 // Either nothing, a file actor or a non-Blob-backed inputstream to write to
3577 using FileActorOrInputStream
=
3578 Variant
<Nothing
, RefPtr
<DatabaseFile
>, nsCOMPtr
<nsIInputStream
>>;
3579 InitializedOnce
<const FileActorOrInputStream
> mFileActorOrInputStream
;
3581 const StructuredCloneFileBase::FileType mType
;
3583 void EnsureCipherKey();
3584 void AssertInvariants() const;
3586 StoredFileInfo(SafeRefPtr
<DatabaseFileInfo
> aFileInfo
,
3587 RefPtr
<DatabaseFile
> aFileActor
);
3589 StoredFileInfo(SafeRefPtr
<DatabaseFileInfo
> aFileInfo
,
3590 nsCOMPtr
<nsIInputStream
> aInputStream
);
3593 #if defined(NS_BUILD_REFCNT_LOGGING)
3594 // Only for MOZ_COUNT_CTOR.
3595 StoredFileInfo(StoredFileInfo
&& aOther
)
3596 : mFileInfo
{std::move(aOther
.mFileInfo
)},
3597 mFileActorOrInputStream
{std::move(aOther
.mFileActorOrInputStream
)}
3603 MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo
);
3606 StoredFileInfo(StoredFileInfo
&&) = default;
3609 static StoredFileInfo
CreateForBlob(SafeRefPtr
<DatabaseFileInfo
> aFileInfo
,
3610 RefPtr
<DatabaseFile
> aFileActor
);
3611 static StoredFileInfo
CreateForStructuredClone(
3612 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
,
3613 nsCOMPtr
<nsIInputStream
> aInputStream
);
3615 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
3617 AssertIsOnBackgroundThread();
3620 MOZ_COUNT_DTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo
);
3624 bool IsValid() const { return static_cast<bool>(mFileInfo
); }
3626 const DatabaseFileInfo
& GetFileInfo() const { return **mFileInfo
; }
3628 bool ShouldCompress() const;
3630 void NotifyWriteSucceeded() const;
3632 using InputStreamResult
=
3633 mozilla::Result
<nsCOMPtr
<nsIInputStream
>, nsresult
>;
3634 InputStreamResult
GetInputStream();
3636 void Serialize(nsString
& aText
) const;
3638 class SCInputStream
;
3640 ObjectStoreAddPutParams mParams
;
3641 Maybe
<UniqueIndexTable
> mUniqueIndexTable
;
3643 // This must be non-const so that we can update the mNextAutoIncrementId field
3644 // if we are modifying an autoIncrement objectStore.
3645 SafeRefPtr
<FullObjectStoreMetadata
> mMetadata
;
3647 nsTArray
<StoredFileInfo
> mStoredFileInfos
;
3650 const OriginMetadata mOriginMetadata
;
3651 const PersistenceType mPersistenceType
;
3652 const bool mOverwrite
;
3653 bool mObjectStoreMayHaveIndexes
;
3654 bool mDataOverThreshold
;
3657 // Only created by TransactionBase.
3658 ObjectStoreAddOrPutRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
3659 const int64_t aRequestId
,
3660 RequestParams
&& aParams
);
3662 ~ObjectStoreAddOrPutRequestOp() override
= default;
3664 nsresult
RemoveOldIndexDataValues(DatabaseConnection
* aConnection
);
3666 bool Init(TransactionBase
& aTransaction
) override
;
3668 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3670 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
;
3672 void Cleanup() override
;
3675 void ObjectStoreAddOrPutRequestOp::StoredFileInfo::AssertInvariants() const {
3676 // The only allowed types are eStructuredClone, eBlob and eMutableFile.
3677 MOZ_ASSERT(StructuredCloneFileBase::eStructuredClone
== mType
||
3678 StructuredCloneFileBase::eBlob
== mType
||
3679 StructuredCloneFileBase::eMutableFile
== mType
);
3681 // mFileInfo and a file actor in mFileActorOrInputStream are present until
3682 // the object is moved away, but an inputStream in mFileActorOrInputStream
3683 // can be released early.
3684 MOZ_ASSERT_IF(static_cast<bool>(mFileActorOrInputStream
) &&
3685 mFileActorOrInputStream
->is
<RefPtr
<DatabaseFile
>>(),
3686 static_cast<bool>(mFileInfo
));
3689 // In a non-moved StoredFileInfo, one of the following is true:
3690 // - This was an overflow structured clone (eStructuredClone) and
3691 // storedFileInfo.mFileActorOrInputStream CAN be a non-nullptr input
3692 // stream (but that might have been release by ReleaseInputStream).
3694 StructuredCloneFileBase::eStructuredClone
== mType
,
3695 !mFileActorOrInputStream
||
3696 (mFileActorOrInputStream
->is
<nsCOMPtr
<nsIInputStream
>>() &&
3697 mFileActorOrInputStream
->as
<nsCOMPtr
<nsIInputStream
>>()));
3699 // - This is a reference to a Blob (eBlob) that may or may not have
3700 // already been written to disk. storedFileInfo.mFileActorOrInputStream
3701 // MUST be a non-null file actor, but its GetInputStream may return
3702 // nullptr (so don't assert on that).
3703 MOZ_ASSERT_IF(StructuredCloneFileBase::eBlob
== mType
,
3704 mFileActorOrInputStream
->is
<RefPtr
<DatabaseFile
>>() &&
3705 mFileActorOrInputStream
->as
<RefPtr
<DatabaseFile
>>());
3707 // - It's a mutable file (eMutableFile). No writing will be performed,
3708 // and storedFileInfo.mFileActorOrInputStream is Nothing.
3709 MOZ_ASSERT_IF(StructuredCloneFileBase::eMutableFile
== mType
,
3710 mFileActorOrInputStream
->is
<Nothing
>());
3714 void ObjectStoreAddOrPutRequestOp::StoredFileInfo::EnsureCipherKey() {
3715 const auto& fileInfo
= GetFileInfo();
3716 const auto& fileManager
= fileInfo
.Manager();
3718 // No need to generate cipher keys if we are not in PBM
3719 if (!fileManager
.IsInPrivateBrowsingMode()) {
3724 keyId
.AppendInt(fileInfo
.Id());
3726 fileManager
.MutableCipherKeyManagerRef().Ensure(keyId
);
3729 ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
3730 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
, RefPtr
<DatabaseFile
> aFileActor
)
3731 : mFileInfo
{WrapNotNull(std::move(aFileInfo
))},
3732 mFileActorOrInputStream
{std::move(aFileActor
)}
3735 mType
{StructuredCloneFileBase::eBlob
}
3738 AssertIsOnBackgroundThread();
3742 MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo
);
3745 ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
3746 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
,
3747 nsCOMPtr
<nsIInputStream
> aInputStream
)
3748 : mFileInfo
{WrapNotNull(std::move(aFileInfo
))},
3749 mFileActorOrInputStream
{std::move(aInputStream
)}
3752 mType
{StructuredCloneFileBase::eStructuredClone
}
3755 AssertIsOnBackgroundThread();
3759 MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo
);
3762 ObjectStoreAddOrPutRequestOp::StoredFileInfo
3763 ObjectStoreAddOrPutRequestOp::StoredFileInfo::CreateForBlob(
3764 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
, RefPtr
<DatabaseFile
> aFileActor
) {
3765 return {std::move(aFileInfo
), std::move(aFileActor
)};
3768 ObjectStoreAddOrPutRequestOp::StoredFileInfo
3769 ObjectStoreAddOrPutRequestOp::StoredFileInfo::CreateForStructuredClone(
3770 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
,
3771 nsCOMPtr
<nsIInputStream
> aInputStream
) {
3772 return {std::move(aFileInfo
), std::move(aInputStream
)};
3775 bool ObjectStoreAddOrPutRequestOp::StoredFileInfo::ShouldCompress() const {
3776 // Must not be called after moving.
3777 MOZ_ASSERT(IsValid());
3779 // Compression is only necessary for eStructuredClone, i.e. when
3780 // mFileActorOrInputStream stored an input stream. However, this is only
3781 // called after GetInputStream, when mFileActorOrInputStream has been
3782 // cleared, which is only possible for this type.
3783 const bool res
= !mFileActorOrInputStream
;
3784 MOZ_ASSERT(res
== (StructuredCloneFileBase::eStructuredClone
== mType
));
3788 void ObjectStoreAddOrPutRequestOp::StoredFileInfo::NotifyWriteSucceeded()
3790 MOZ_ASSERT(IsValid());
3792 // For eBlob, clear the blob implementation.
3793 if (mFileActorOrInputStream
&&
3794 mFileActorOrInputStream
->is
<RefPtr
<DatabaseFile
>>()) {
3795 mFileActorOrInputStream
->as
<RefPtr
<DatabaseFile
>>()
3796 ->WriteSucceededClearBlobImpl();
3799 // For the other types, no action is necessary.
3802 ObjectStoreAddOrPutRequestOp::StoredFileInfo::InputStreamResult
3803 ObjectStoreAddOrPutRequestOp::StoredFileInfo::GetInputStream() {
3804 if (!mFileActorOrInputStream
) {
3805 MOZ_ASSERT(StructuredCloneFileBase::eStructuredClone
== mType
);
3806 return nsCOMPtr
<nsIInputStream
>{};
3809 // For the different cases, see also the comments in AssertInvariants.
3810 return mFileActorOrInputStream
->match(
3811 [](const Nothing
&) -> InputStreamResult
{
3812 return nsCOMPtr
<nsIInputStream
>{};
3814 [](const RefPtr
<DatabaseFile
>& databaseActor
) -> InputStreamResult
{
3816 auto inputStream
= databaseActor
->GetInputStream(rv
);
3817 if (NS_WARN_IF(rv
.Failed())) {
3818 return Err(rv
.StealNSResult());
3823 [this](const nsCOMPtr
<nsIInputStream
>& inputStream
) -> InputStreamResult
{
3824 auto res
= inputStream
;
3825 // destroy() clears the inputStream parameter, so we needed to make a
3827 mFileActorOrInputStream
.destroy();
3833 void ObjectStoreAddOrPutRequestOp::StoredFileInfo::Serialize(
3834 nsString
& aText
) const {
3836 MOZ_ASSERT(IsValid());
3838 const int64_t id
= (*mFileInfo
)->Id();
3840 auto structuredCloneHandler
= [&aText
, id
](const nsCOMPtr
<nsIInputStream
>&) {
3843 aText
.AppendInt(id
);
3846 // If mFileActorOrInputStream was moved, we had an inputStream before.
3847 if (!mFileActorOrInputStream
) {
3848 structuredCloneHandler(nullptr);
3852 // This encoding is parsed in DeserializeStructuredCloneFile.
3853 mFileActorOrInputStream
->match(
3854 [&aText
, id
](const Nothing
&) {
3856 aText
.AppendInt(-id
);
3858 [&aText
, id
](const RefPtr
<DatabaseFile
>&) {
3860 aText
.AppendInt(id
);
3862 structuredCloneHandler
);
3865 class ObjectStoreAddOrPutRequestOp::SCInputStream final
3866 : public nsIInputStream
{
3867 const JSStructuredCloneData
& mData
;
3868 JSStructuredCloneData::Iterator mIter
;
3871 explicit SCInputStream(const JSStructuredCloneData
& aData
)
3872 : mData(aData
), mIter(aData
.Start()) {}
3875 virtual ~SCInputStream() = default;
3877 NS_DECL_THREADSAFE_ISUPPORTS
3878 NS_DECL_NSIINPUTSTREAM
3881 class ObjectStoreGetRequestOp final
: public NormalTransactionOp
{
3882 friend class TransactionBase
;
3884 const IndexOrObjectStoreId mObjectStoreId
;
3885 SafeRefPtr
<Database
> mDatabase
;
3886 const Maybe
<SerializedKeyRange
> mOptionalKeyRange
;
3887 AutoTArray
<StructuredCloneReadInfoParent
, 1> mResponse
;
3888 PBackgroundParent
* mBackgroundParent
;
3889 uint32_t mPreprocessInfoCount
;
3890 const uint32_t mLimit
;
3894 // Only created by TransactionBase.
3895 ObjectStoreGetRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
3896 const int64_t aRequestId
,
3897 const RequestParams
& aParams
, bool aGetAll
);
3899 ~ObjectStoreGetRequestOp() override
= default;
3901 template <typename T
>
3902 mozilla::Result
<T
, nsresult
> ConvertResponse(
3903 StructuredCloneReadInfoParent
&& aInfo
);
3905 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3907 bool HasPreprocessInfo() override
;
3909 mozilla::Result
<PreprocessParams
, nsresult
> GetPreprocessParams() override
;
3911 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
;
3914 class ObjectStoreGetKeyRequestOp final
: public NormalTransactionOp
{
3915 friend class TransactionBase
;
3917 const IndexOrObjectStoreId mObjectStoreId
;
3918 const Maybe
<SerializedKeyRange
> mOptionalKeyRange
;
3919 const uint32_t mLimit
;
3921 nsTArray
<Key
> mResponse
;
3924 // Only created by TransactionBase.
3925 ObjectStoreGetKeyRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
3926 const int64_t aRequestId
,
3927 const RequestParams
& aParams
, bool aGetAll
);
3929 ~ObjectStoreGetKeyRequestOp() override
= default;
3931 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3933 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
;
3936 class ObjectStoreDeleteRequestOp final
: public NormalTransactionOp
{
3937 friend class TransactionBase
;
3939 const ObjectStoreDeleteParams mParams
;
3940 ObjectStoreDeleteResponse mResponse
;
3941 bool mObjectStoreMayHaveIndexes
;
3944 ObjectStoreDeleteRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
3945 const int64_t aRequestId
,
3946 const ObjectStoreDeleteParams
& aParams
);
3948 ~ObjectStoreDeleteRequestOp() override
= default;
3950 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3952 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
{
3953 aResponse
= std::move(mResponse
);
3958 class ObjectStoreClearRequestOp final
: public NormalTransactionOp
{
3959 friend class TransactionBase
;
3961 const ObjectStoreClearParams mParams
;
3962 ObjectStoreClearResponse mResponse
;
3963 bool mObjectStoreMayHaveIndexes
;
3966 ObjectStoreClearRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
3967 const int64_t aRequestId
,
3968 const ObjectStoreClearParams
& aParams
);
3970 ~ObjectStoreClearRequestOp() override
= default;
3972 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3974 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
{
3975 aResponse
= std::move(mResponse
);
3980 class ObjectStoreCountRequestOp final
: public NormalTransactionOp
{
3981 friend class TransactionBase
;
3983 const ObjectStoreCountParams mParams
;
3984 ObjectStoreCountResponse mResponse
;
3987 ObjectStoreCountRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
3988 const int64_t aRequestId
,
3989 const ObjectStoreCountParams
& aParams
)
3990 : NormalTransactionOp(std::move(aTransaction
), aRequestId
),
3993 ~ObjectStoreCountRequestOp() override
= default;
3995 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
3997 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
{
3998 aResponse
= std::move(mResponse
);
3999 *aResponseSize
= sizeof(uint64_t);
4003 class IndexRequestOpBase
: public NormalTransactionOp
{
4005 const SafeRefPtr
<FullIndexMetadata
> mMetadata
;
4008 IndexRequestOpBase(SafeRefPtr
<TransactionBase
> aTransaction
,
4009 const int64_t aRequestId
, const RequestParams
& aParams
)
4010 : NormalTransactionOp(std::move(aTransaction
), aRequestId
),
4011 mMetadata(IndexMetadataForParams(Transaction(), aParams
)) {}
4013 ~IndexRequestOpBase() override
= default;
4016 static SafeRefPtr
<FullIndexMetadata
> IndexMetadataForParams(
4017 const TransactionBase
& aTransaction
, const RequestParams
& aParams
);
4020 class IndexGetRequestOp final
: public IndexRequestOpBase
{
4021 friend class TransactionBase
;
4023 SafeRefPtr
<Database
> mDatabase
;
4024 const Maybe
<SerializedKeyRange
> mOptionalKeyRange
;
4025 AutoTArray
<StructuredCloneReadInfoParent
, 1> mResponse
;
4026 PBackgroundParent
* mBackgroundParent
;
4027 const uint32_t mLimit
;
4031 // Only created by TransactionBase.
4032 IndexGetRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
4033 const int64_t aRequestId
, const RequestParams
& aParams
,
4036 ~IndexGetRequestOp() override
= default;
4038 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
4040 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
;
4043 class IndexGetKeyRequestOp final
: public IndexRequestOpBase
{
4044 friend class TransactionBase
;
4046 const Maybe
<SerializedKeyRange
> mOptionalKeyRange
;
4047 AutoTArray
<Key
, 1> mResponse
;
4048 const uint32_t mLimit
;
4052 // Only created by TransactionBase.
4053 IndexGetKeyRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
4054 const int64_t aRequestId
, const RequestParams
& aParams
,
4057 ~IndexGetKeyRequestOp() override
= default;
4059 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
4061 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
;
4064 class IndexCountRequestOp final
: public IndexRequestOpBase
{
4065 friend class TransactionBase
;
4067 const IndexCountParams mParams
;
4068 IndexCountResponse mResponse
;
4071 // Only created by TransactionBase.
4072 IndexCountRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
4073 const int64_t aRequestId
, const RequestParams
& aParams
)
4074 : IndexRequestOpBase(std::move(aTransaction
), aRequestId
, aParams
),
4075 mParams(aParams
.get_IndexCountParams()) {}
4077 ~IndexCountRequestOp() override
= default;
4079 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
4081 void GetResponse(RequestResponse
& aResponse
, size_t* aResponseSize
) override
{
4082 aResponse
= std::move(mResponse
);
4083 *aResponseSize
= sizeof(uint64_t);
4087 template <IDBCursorType CursorType
>
4090 constexpr IDBCursorType
ToKeyOnlyType(const IDBCursorType aType
) {
4091 MOZ_ASSERT(aType
== IDBCursorType::ObjectStore
||
4092 aType
== IDBCursorType::ObjectStoreKey
||
4093 aType
== IDBCursorType::Index
|| aType
== IDBCursorType::IndexKey
);
4095 case IDBCursorType::ObjectStore
:
4097 case IDBCursorType::ObjectStoreKey
:
4098 return IDBCursorType::ObjectStoreKey
;
4099 case IDBCursorType::Index
:
4101 case IDBCursorType::IndexKey
:
4102 return IDBCursorType::IndexKey
;
4106 template <IDBCursorType CursorType
>
4107 using CursorPosition
= CursorData
<ToKeyOnlyType(CursorType
)>;
4110 constexpr indexedDB::OpenCursorParams::Type
ToOpenCursorParamsType(
4111 const IDBCursorType aType
) {
4112 MOZ_ASSERT(aType
== IDBCursorType::ObjectStore
||
4113 aType
== IDBCursorType::ObjectStoreKey
||
4114 aType
== IDBCursorType::Index
|| aType
== IDBCursorType::IndexKey
);
4116 case IDBCursorType::ObjectStore
:
4117 return indexedDB::OpenCursorParams::TObjectStoreOpenCursorParams
;
4118 case IDBCursorType::ObjectStoreKey
:
4119 return indexedDB::OpenCursorParams::TObjectStoreOpenKeyCursorParams
;
4120 case IDBCursorType::Index
:
4121 return indexedDB::OpenCursorParams::TIndexOpenCursorParams
;
4122 case IDBCursorType::IndexKey
:
4123 return indexedDB::OpenCursorParams::TIndexOpenKeyCursorParams
;
4128 class CursorBase
: public PBackgroundIDBCursorParent
{
4129 friend class TransactionBase
;
4130 template <IDBCursorType CursorType
>
4131 friend class CommonOpenOpHelper
;
4134 const SafeRefPtr
<TransactionBase
> mTransaction
;
4136 // This should only be touched on the PBackground thread to check whether
4137 // the objectStore has been deleted. Holding these saves a hash lookup for
4138 // every call to continue()/advance().
4139 InitializedOnce
<const NotNull
<SafeRefPtr
<FullObjectStoreMetadata
>>>
4140 mObjectStoreMetadata
;
4142 const IndexOrObjectStoreId mObjectStoreId
;
4144 LazyInitializedOnce
<const Key
>
4145 mLocaleAwareRangeBound
; ///< If the cursor is based on a key range, the
4146 ///< bound in the direction of iteration (e.g.
4147 ///< the upper bound in case of mDirection ==
4148 ///< NEXT). If the cursor is based on a key, it
4149 ///< is unset. If mLocale is set, this was
4150 ///< converted to mLocale.
4152 const Direction mDirection
;
4154 const int32_t mMaxExtraCount
;
4156 const bool mIsSameProcessActor
;
4158 struct ConstructFromTransactionBase
{};
4161 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::CursorBase
,
4164 CursorBase(SafeRefPtr
<TransactionBase
> aTransaction
,
4165 SafeRefPtr
<FullObjectStoreMetadata
> aObjectStoreMetadata
,
4166 Direction aDirection
,
4167 ConstructFromTransactionBase aConstructionTag
);
4170 // Reference counted.
4171 ~CursorBase() override
{ MOZ_ASSERT(!mObjectStoreMetadata
); }
4174 virtual bool Start(const int64_t aRequestId
,
4175 const OpenCursorParams
& aParams
) = 0;
4178 class IndexCursorBase
: public CursorBase
{
4180 bool IsLocaleAware() const { return !mLocale
.IsEmpty(); }
4182 IndexCursorBase(SafeRefPtr
<TransactionBase
> aTransaction
,
4183 SafeRefPtr
<FullObjectStoreMetadata
> aObjectStoreMetadata
,
4184 SafeRefPtr
<FullIndexMetadata
> aIndexMetadata
,
4185 Direction aDirection
,
4186 ConstructFromTransactionBase aConstructionTag
)
4187 : CursorBase
{std::move(aTransaction
), std::move(aObjectStoreMetadata
),
4188 aDirection
, aConstructionTag
},
4189 mIndexMetadata(WrapNotNull(std::move(aIndexMetadata
))),
4190 mIndexId((*mIndexMetadata
)->mCommonMetadata
.id()),
4191 mUniqueIndex((*mIndexMetadata
)->mCommonMetadata
.unique()),
4192 mLocale((*mIndexMetadata
)->mCommonMetadata
.locale()) {}
4195 IndexOrObjectStoreId
Id() const { return mIndexId
; }
4197 // This should only be touched on the PBackground thread to check whether
4198 // the index has been deleted. Holding these saves a hash lookup for every
4199 // call to continue()/advance().
4200 InitializedOnce
<const NotNull
<SafeRefPtr
<FullIndexMetadata
>>> mIndexMetadata
;
4201 const IndexOrObjectStoreId mIndexId
;
4202 const bool mUniqueIndex
;
4204 mLocale
; ///< The locale if the cursor is locale-aware, otherwise empty.
4206 struct ContinueQueries
{
4207 nsCString mContinueQuery
;
4208 nsCString mContinueToQuery
;
4209 nsCString mContinuePrimaryKeyQuery
;
4211 const nsACString
& GetContinueQuery(const bool hasContinueKey
,
4212 const bool hasContinuePrimaryKey
) const {
4213 return hasContinuePrimaryKey
? mContinuePrimaryKeyQuery
4214 : hasContinueKey
? mContinueToQuery
4220 class ObjectStoreCursorBase
: public CursorBase
{
4222 using CursorBase::CursorBase
;
4224 static constexpr bool IsLocaleAware() { return false; }
4227 IndexOrObjectStoreId
Id() const { return mObjectStoreId
; }
4229 struct ContinueQueries
{
4230 nsCString mContinueQuery
;
4231 nsCString mContinueToQuery
;
4233 const nsACString
& GetContinueQuery(const bool hasContinueKey
,
4234 const bool hasContinuePrimaryKey
) const {
4235 MOZ_ASSERT(!hasContinuePrimaryKey
);
4236 return hasContinueKey
? mContinueToQuery
: mContinueQuery
;
4241 using FilesArray
= nsTArray
<nsTArray
<StructuredCloneFileParent
>>;
4243 struct PseudoFilesArray
{
4244 static constexpr bool IsEmpty() { return true; }
4246 static constexpr void Clear() {}
4249 template <IDBCursorType CursorType
>
4251 std::conditional_t
<!CursorTypeTraits
<CursorType
>::IsKeyOnlyCursor
,
4252 FilesArray
, PseudoFilesArray
>;
4254 class ValueCursorBase
{
4255 friend struct ValuePopulateResponseHelper
<true>;
4256 friend struct ValuePopulateResponseHelper
<false>;
4259 explicit ValueCursorBase(TransactionBase
* const aTransaction
)
4260 : mDatabase(aTransaction
->GetDatabasePtr()),
4261 mFileManager(mDatabase
->GetFileManagerPtr()),
4262 mBackgroundParent(WrapNotNull(aTransaction
->GetBackgroundParent())) {
4263 MOZ_ASSERT(mDatabase
);
4266 void ProcessFiles(CursorResponse
& aResponse
, const FilesArray
& aFiles
);
4268 ~ValueCursorBase() { MOZ_ASSERT(!mBackgroundParent
); }
4270 const SafeRefPtr
<Database
> mDatabase
;
4271 const NotNull
<SafeRefPtr
<DatabaseFileManager
>> mFileManager
;
4273 InitializedOnce
<const NotNull
<PBackgroundParent
*>> mBackgroundParent
;
4276 class KeyCursorBase
{
4278 explicit KeyCursorBase(TransactionBase
* const /*aTransaction*/) {}
4280 static constexpr void ProcessFiles(CursorResponse
& aResponse
,
4281 const PseudoFilesArray
& aFiles
) {}
4284 template <IDBCursorType CursorType
>
4285 class CursorOpBaseHelperBase
;
4287 template <IDBCursorType CursorType
>
4289 : public std::conditional_t
<
4290 CursorTypeTraits
<CursorType
>::IsObjectStoreCursor
,
4291 ObjectStoreCursorBase
, IndexCursorBase
>,
4292 public std::conditional_t
<CursorTypeTraits
<CursorType
>::IsKeyOnlyCursor
,
4293 KeyCursorBase
, ValueCursorBase
> {
4295 std::conditional_t
<CursorTypeTraits
<CursorType
>::IsObjectStoreCursor
,
4296 ObjectStoreCursorBase
, IndexCursorBase
>;
4298 using KeyValueBase
=
4299 std::conditional_t
<CursorTypeTraits
<CursorType
>::IsKeyOnlyCursor
,
4300 KeyCursorBase
, ValueCursorBase
>;
4302 static constexpr bool IsIndexCursor
=
4303 !CursorTypeTraits
<CursorType
>::IsObjectStoreCursor
;
4305 static constexpr bool IsValueCursor
=
4306 !CursorTypeTraits
<CursorType
>::IsKeyOnlyCursor
;
4313 using CursorBase::Manager
;
4314 using CursorBase::mDirection
;
4315 using CursorBase::mObjectStoreId
;
4316 using CursorBase::mTransaction
;
4317 using typename
CursorBase::ActorDestroyReason
;
4319 using TypedOpenOpHelper
=
4320 std::conditional_t
<IsIndexCursor
, IndexOpenOpHelper
<CursorType
>,
4321 ObjectStoreOpenOpHelper
<CursorType
>>;
4323 friend class CursorOpBaseHelperBase
<CursorType
>;
4324 friend class CommonOpenOpHelper
<CursorType
>;
4325 friend TypedOpenOpHelper
;
4326 friend class OpenOpHelper
<CursorType
>;
4328 CursorOpBase
* mCurrentlyRunningOp
= nullptr;
4330 LazyInitializedOnce
<const typename
Base::ContinueQueries
> mContinueQueries
;
4332 // Only called by TransactionBase.
4333 bool Start(const int64_t aRequestId
, const OpenCursorParams
& aParams
) final
;
4335 void SendResponseInternal(CursorResponse
& aResponse
,
4336 const FilesArrayT
<CursorType
>& aFiles
);
4338 // Must call SendResponseInternal!
4339 bool SendResponse(const CursorResponse
& aResponse
) = delete;
4342 void ActorDestroy(ActorDestroyReason aWhy
) override
;
4344 mozilla::ipc::IPCResult
RecvDeleteMe() override
;
4346 mozilla::ipc::IPCResult
RecvContinue(
4347 const int64_t& aRequestId
, const CursorRequestParams
& aParams
,
4348 const Key
& aCurrentKey
, const Key
& aCurrentObjectStoreKey
) override
;
4351 Cursor(SafeRefPtr
<TransactionBase
> aTransaction
,
4352 SafeRefPtr
<FullObjectStoreMetadata
> aObjectStoreMetadata
,
4353 SafeRefPtr
<FullIndexMetadata
> aIndexMetadata
,
4354 typename
Base::Direction aDirection
,
4355 typename
Base::ConstructFromTransactionBase aConstructionTag
)
4356 : Base
{std::move(aTransaction
), std::move(aObjectStoreMetadata
),
4357 std::move(aIndexMetadata
), aDirection
, aConstructionTag
},
4358 KeyValueBase
{this->mTransaction
.unsafeGetRawPtr()} {}
4360 Cursor(SafeRefPtr
<TransactionBase
> aTransaction
,
4361 SafeRefPtr
<FullObjectStoreMetadata
> aObjectStoreMetadata
,
4362 typename
Base::Direction aDirection
,
4363 typename
Base::ConstructFromTransactionBase aConstructionTag
)
4364 : Base
{std::move(aTransaction
), std::move(aObjectStoreMetadata
),
4365 aDirection
, aConstructionTag
},
4366 KeyValueBase
{this->mTransaction
.unsafeGetRawPtr()} {}
4369 void SetOptionalKeyRange(const Maybe
<SerializedKeyRange
>& aOptionalKeyRange
,
4372 bool VerifyRequestParams(const CursorRequestParams
& aParams
,
4373 const CursorPosition
<CursorType
>& aPosition
) const;
4375 ~Cursor() final
= default;
4378 template <IDBCursorType CursorType
>
4379 class Cursor
<CursorType
>::CursorOpBase
4380 : public TransactionDatabaseOperationBase
{
4381 friend class CursorOpBaseHelperBase
<CursorType
>;
4384 RefPtr
<Cursor
> mCursor
;
4385 FilesArrayT
<CursorType
> mFiles
; // TODO: Consider removing this member
4386 // entirely if we are no value cursor.
4388 CursorResponse mResponse
;
4395 explicit CursorOpBase(Cursor
* aCursor
, const int64_t aRequestId
)
4396 : TransactionDatabaseOperationBase(aCursor
->mTransaction
.clonePtr(),
4397 /* aRequestId */ aRequestId
),
4401 mResponseSent(false)
4404 AssertIsOnBackgroundThread();
4405 MOZ_ASSERT(aCursor
);
4408 ~CursorOpBase() override
= default;
4410 bool SendFailureResult(nsresult aResultCode
) final
;
4411 nsresult
SendSuccessResult() final
;
4413 void Cleanup() override
;
4416 template <IDBCursorType CursorType
>
4419 using ResponseSizeOrError
= Result
<size_t, nsresult
>;
4421 template <IDBCursorType CursorType
>
4422 class CursorOpBaseHelperBase
{
4424 explicit CursorOpBaseHelperBase(
4425 typename Cursor
<CursorType
>::CursorOpBase
& aOp
)
4428 ResponseSizeOrError
PopulateResponseFromStatement(mozIStorageStatement
* aStmt
,
4429 bool aInitializeResponse
,
4430 Key
* const aOptOutSortKey
);
4432 void PopulateExtraResponses(mozIStorageStatement
* aStmt
,
4433 uint32_t aMaxExtraCount
,
4434 const size_t aInitialResponseSize
,
4435 const nsACString
& aOperation
,
4436 Key
* const aOptPreviousSortKey
);
4439 Cursor
<CursorType
>& GetCursor() {
4440 MOZ_ASSERT(mOp
.mCursor
);
4441 return *mOp
.mCursor
;
4444 void SetResponse(CursorResponse aResponse
) {
4445 mOp
.mResponse
= std::move(aResponse
);
4449 typename Cursor
<CursorType
>::CursorOpBase
& mOp
;
4452 class CommonOpenOpHelperBase
{
4454 static void AppendConditionClause(const nsACString
& aColumnName
,
4455 const nsACString
& aStatementParameterName
,
4456 bool aLessThan
, bool aEquals
,
4457 nsCString
& aResult
);
4460 template <IDBCursorType CursorType
>
4461 class CommonOpenOpHelper
: public CursorOpBaseHelperBase
<CursorType
>,
4462 protected CommonOpenOpHelperBase
{
4464 explicit CommonOpenOpHelper(typename Cursor
<CursorType
>::OpenOp
& aOp
)
4465 : CursorOpBaseHelperBase
<CursorType
>{aOp
} {}
4468 using CursorOpBaseHelperBase
<CursorType
>::GetCursor
;
4469 using CursorOpBaseHelperBase
<CursorType
>::PopulateExtraResponses
;
4470 using CursorOpBaseHelperBase
<CursorType
>::PopulateResponseFromStatement
;
4471 using CursorOpBaseHelperBase
<CursorType
>::SetResponse
;
4473 const Maybe
<SerializedKeyRange
>& GetOptionalKeyRange() const {
4474 // This downcast is safe, since we initialized mOp from an OpenOp in the
4476 return static_cast<typename Cursor
<CursorType
>::OpenOp
&>(this->mOp
)
4480 nsresult
ProcessStatementSteps(mozIStorageStatement
* aStmt
);
4483 template <IDBCursorType CursorType
>
4484 class ObjectStoreOpenOpHelper
: protected CommonOpenOpHelper
<CursorType
> {
4486 using CommonOpenOpHelper
<CursorType
>::CommonOpenOpHelper
;
4489 using CommonOpenOpHelper
<CursorType
>::GetCursor
;
4490 using CommonOpenOpHelper
<CursorType
>::GetOptionalKeyRange
;
4491 using CommonOpenOpHelper
<CursorType
>::AppendConditionClause
;
4493 void PrepareKeyConditionClauses(const nsACString
& aDirectionClause
,
4494 const nsACString
& aQueryStart
);
4497 template <IDBCursorType CursorType
>
4498 class IndexOpenOpHelper
: protected CommonOpenOpHelper
<CursorType
> {
4500 using CommonOpenOpHelper
<CursorType
>::CommonOpenOpHelper
;
4503 using CommonOpenOpHelper
<CursorType
>::GetCursor
;
4504 using CommonOpenOpHelper
<CursorType
>::GetOptionalKeyRange
;
4505 using CommonOpenOpHelper
<CursorType
>::AppendConditionClause
;
4507 void PrepareIndexKeyConditionClause(
4508 const nsACString
& aDirectionClause
,
4509 const nsLiteralCString
& aObjectDataKeyPrefix
, nsAutoCString aQueryStart
);
4513 class OpenOpHelper
<IDBCursorType::ObjectStore
>
4514 : public ObjectStoreOpenOpHelper
<IDBCursorType::ObjectStore
> {
4516 using ObjectStoreOpenOpHelper
<
4517 IDBCursorType::ObjectStore
>::ObjectStoreOpenOpHelper
;
4519 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
);
4523 class OpenOpHelper
<IDBCursorType::ObjectStoreKey
>
4524 : public ObjectStoreOpenOpHelper
<IDBCursorType::ObjectStoreKey
> {
4526 using ObjectStoreOpenOpHelper
<
4527 IDBCursorType::ObjectStoreKey
>::ObjectStoreOpenOpHelper
;
4529 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
);
4533 class OpenOpHelper
<IDBCursorType::Index
>
4534 : IndexOpenOpHelper
<IDBCursorType::Index
> {
4536 void PrepareKeyConditionClauses(const nsACString
& aDirectionClause
,
4537 nsAutoCString aQueryStart
) {
4538 PrepareIndexKeyConditionClause(aDirectionClause
, "index_table."_ns
,
4539 std::move(aQueryStart
));
4543 using IndexOpenOpHelper
<IDBCursorType::Index
>::IndexOpenOpHelper
;
4545 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
);
4549 class OpenOpHelper
<IDBCursorType::IndexKey
>
4550 : IndexOpenOpHelper
<IDBCursorType::IndexKey
> {
4552 void PrepareKeyConditionClauses(const nsACString
& aDirectionClause
,
4553 nsAutoCString aQueryStart
) {
4554 PrepareIndexKeyConditionClause(aDirectionClause
, ""_ns
,
4555 std::move(aQueryStart
));
4559 using IndexOpenOpHelper
<IDBCursorType::IndexKey
>::IndexOpenOpHelper
;
4561 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
);
4564 template <IDBCursorType CursorType
>
4565 class Cursor
<CursorType
>::OpenOp final
: public CursorOpBase
{
4566 friend class Cursor
<CursorType
>;
4567 friend class CommonOpenOpHelper
<CursorType
>;
4569 const Maybe
<SerializedKeyRange
> mOptionalKeyRange
;
4571 using CursorOpBase::mCursor
;
4572 using CursorOpBase::mResponse
;
4574 // Only created by Cursor.
4575 OpenOp(Cursor
* const aCursor
, const int64_t aRequestId
,
4576 const Maybe
<SerializedKeyRange
>& aOptionalKeyRange
)
4577 : CursorOpBase(aCursor
, aRequestId
),
4578 mOptionalKeyRange(aOptionalKeyRange
) {}
4580 // Reference counted.
4581 ~OpenOp() override
= default;
4583 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
4586 template <IDBCursorType CursorType
>
4587 class Cursor
<CursorType
>::ContinueOp final
4588 : public Cursor
<CursorType
>::CursorOpBase
{
4589 friend class Cursor
<CursorType
>;
4591 using CursorOpBase::mCursor
;
4592 using CursorOpBase::mResponse
;
4593 const CursorRequestParams mParams
;
4595 // Only created by Cursor.
4596 ContinueOp(Cursor
* const aCursor
, int64_t aRequestId
,
4597 CursorRequestParams aParams
, CursorPosition
<CursorType
> aPosition
)
4598 : CursorOpBase(aCursor
, aRequestId
),
4599 mParams(std::move(aParams
)),
4600 mCurrentPosition
{std::move(aPosition
)} {
4601 MOZ_ASSERT(mParams
.type() != CursorRequestParams::T__None
);
4604 // Reference counted.
4605 ~ContinueOp() override
= default;
4607 nsresult
DoDatabaseWork(DatabaseConnection
* aConnection
) override
;
4609 const CursorPosition
<CursorType
> mCurrentPosition
;
4612 class Utils final
: public PBackgroundIndexedDBUtilsParent
{
4614 bool mActorDestroyed
;
4620 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Utils
)
4623 // Reference counted.
4626 // IPDL methods are only called by IPDL.
4627 void ActorDestroy(ActorDestroyReason aWhy
) override
;
4629 mozilla::ipc::IPCResult
RecvDeleteMe() override
;
4631 mozilla::ipc::IPCResult
RecvGetFileReferences(
4632 const PersistenceType
& aPersistenceType
, const nsACString
& aOrigin
,
4633 const nsAString
& aDatabaseName
, const int64_t& aFileId
, int32_t* aRefCnt
,
4634 int32_t* aDBRefCnt
, bool* aResult
) override
;
4637 /*******************************************************************************
4638 * Other class declarations
4639 ******************************************************************************/
4641 struct DatabaseActorInfo final
{
4642 friend class mozilla::DefaultDelete
<DatabaseActorInfo
>;
4644 SafeRefPtr
<FullDatabaseMetadata
> mMetadata
;
4645 nsTArray
<NotNull
<CheckedUnsafePtr
<Database
>>> mLiveDatabases
;
4646 RefPtr
<FactoryOp
> mWaitingFactoryOp
;
4648 DatabaseActorInfo(SafeRefPtr
<FullDatabaseMetadata
> aMetadata
,
4649 NotNull
<Database
*> aDatabase
)
4650 : mMetadata(std::move(aMetadata
)) {
4651 MOZ_COUNT_CTOR(DatabaseActorInfo
);
4653 mLiveDatabases
.AppendElement(aDatabase
);
4657 ~DatabaseActorInfo() {
4658 MOZ_ASSERT(mLiveDatabases
.IsEmpty());
4659 MOZ_ASSERT(!mWaitingFactoryOp
|| !mWaitingFactoryOp
->HasBlockedDatabases());
4661 MOZ_COUNT_DTOR(DatabaseActorInfo
);
4665 class DatabaseLoggingInfo final
{
4667 // Just for potential warnings.
4668 friend class Factory
;
4671 LoggingInfo mLoggingInfo
;
4674 explicit DatabaseLoggingInfo(const LoggingInfo
& aLoggingInfo
)
4675 : mLoggingInfo(aLoggingInfo
) {
4676 AssertIsOnBackgroundThread();
4677 MOZ_ASSERT(aLoggingInfo
.nextTransactionSerialNumber());
4678 MOZ_ASSERT(aLoggingInfo
.nextVersionChangeTransactionSerialNumber());
4679 MOZ_ASSERT(aLoggingInfo
.nextRequestSerialNumber());
4682 const nsID
& Id() const {
4683 AssertIsOnBackgroundThread();
4685 return mLoggingInfo
.backgroundChildLoggingId();
4688 int64_t NextTransactionSN(IDBTransaction::Mode aMode
) {
4689 AssertIsOnBackgroundThread();
4690 MOZ_ASSERT(mLoggingInfo
.nextTransactionSerialNumber() < INT64_MAX
);
4691 MOZ_ASSERT(mLoggingInfo
.nextVersionChangeTransactionSerialNumber() >
4694 if (aMode
== IDBTransaction::Mode::VersionChange
) {
4695 return mLoggingInfo
.nextVersionChangeTransactionSerialNumber()--;
4698 return mLoggingInfo
.nextTransactionSerialNumber()++;
4701 uint64_t NextRequestSN() {
4702 AssertIsOnBackgroundThread();
4703 MOZ_ASSERT(mLoggingInfo
.nextRequestSerialNumber() < UINT64_MAX
);
4705 return mLoggingInfo
.nextRequestSerialNumber()++;
4708 NS_INLINE_DECL_REFCOUNTING(DatabaseLoggingInfo
)
4711 ~DatabaseLoggingInfo();
4714 class QuotaClient final
: public mozilla::dom::quota::Client
{
4715 friend class GetDatabasesOp
;
4717 static QuotaClient
* sInstance
;
4719 nsCOMPtr
<nsIEventTarget
> mBackgroundThread
;
4720 nsCOMPtr
<nsITimer
> mDeleteTimer
;
4721 nsTArray
<RefPtr
<Maintenance
>> mMaintenanceQueue
;
4722 RefPtr
<Maintenance
> mCurrentMaintenance
;
4723 RefPtr
<nsThreadPool
> mMaintenanceThreadPool
;
4724 nsClassHashtable
<nsRefPtrHashKey
<DatabaseFileManager
>, nsTArray
<int64_t>>
4725 mPendingDeleteInfos
;
4730 static QuotaClient
* GetInstance() {
4731 AssertIsOnBackgroundThread();
4736 nsIEventTarget
* BackgroundThread() const {
4737 MOZ_ASSERT(mBackgroundThread
);
4738 return mBackgroundThread
;
4741 nsresult
AsyncDeleteFile(DatabaseFileManager
* aFileManager
, int64_t aFileId
);
4743 nsresult
FlushPendingFileDeletions();
4745 RefPtr
<Maintenance
> GetCurrentMaintenance() const {
4746 return mCurrentMaintenance
;
4749 void NoteFinishedMaintenance(Maintenance
* aMaintenance
) {
4750 AssertIsOnBackgroundThread();
4751 MOZ_ASSERT(aMaintenance
);
4752 MOZ_ASSERT(mCurrentMaintenance
== aMaintenance
);
4754 mCurrentMaintenance
= nullptr;
4756 QuotaManager::MaybeRecordQuotaClientShutdownStep(quota::Client::IDB
,
4757 "Maintenance finished"_ns
);
4759 ProcessMaintenanceQueue();
4762 nsThreadPool
* GetOrCreateThreadPool();
4764 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::QuotaClient
,
4767 mozilla::dom::quota::Client::Type
GetType() override
;
4769 nsresult
UpgradeStorageFrom1_0To2_0(nsIFile
* aDirectory
) override
;
4771 nsresult
UpgradeStorageFrom2_1To2_2(nsIFile
* aDirectory
) override
;
4773 Result
<UsageInfo
, nsresult
> InitOrigin(PersistenceType aPersistenceType
,
4774 const OriginMetadata
& aOriginMetadata
,
4775 const AtomicBool
& aCanceled
) override
;
4777 nsresult
InitOriginWithoutTracking(PersistenceType aPersistenceType
,
4778 const OriginMetadata
& aOriginMetadata
,
4779 const AtomicBool
& aCanceled
) override
;
4781 Result
<UsageInfo
, nsresult
> GetUsageForOrigin(
4782 PersistenceType aPersistenceType
, const OriginMetadata
& aOriginMetadata
,
4783 const AtomicBool
& aCanceled
) override
;
4785 void OnOriginClearCompleted(PersistenceType aPersistenceType
,
4786 const nsACString
& aOrigin
) override
;
4788 void OnRepositoryClearCompleted(PersistenceType aPersistenceType
) override
;
4790 void ReleaseIOThreadObjects() override
;
4792 void AbortOperationsForLocks(
4793 const DirectoryLockIdTable
& aDirectoryLockIds
) override
;
4795 void AbortOperationsForProcess(ContentParentId aContentParentId
) override
;
4797 void AbortAllOperations() override
;
4799 void StartIdleMaintenance() override
;
4801 void StopIdleMaintenance() override
;
4804 ~QuotaClient() override
;
4806 void InitiateShutdown() override
;
4807 bool IsShutdownCompleted() const override
;
4808 nsCString
GetShutdownStatus() const override
;
4809 void ForceKillActors() override
;
4810 void FinalizeShutdown() override
;
4812 static void DeleteTimerCallback(nsITimer
* aTimer
, void* aClosure
);
4814 void AbortAllMaintenances();
4816 Result
<nsCOMPtr
<nsIFile
>, nsresult
> GetDirectory(
4817 const OriginMetadata
& aOriginMetadata
);
4819 struct SubdirectoriesToProcessAndDatabaseFilenames
{
4820 AutoTArray
<nsString
, 20> subdirsToProcess
;
4821 nsTHashSet
<nsString
> databaseFilenames
{20};
4824 struct SubdirectoriesToProcessAndDatabaseFilenamesAndObsoleteFilenames
{
4825 AutoTArray
<nsString
, 20> subdirsToProcess
;
4826 nsTHashSet
<nsString
> databaseFilenames
{20};
4827 nsTHashSet
<nsString
> obsoleteFilenames
{20};
4830 enum class ObsoleteFilenamesHandling
{ Include
, Omit
};
4832 template <ObsoleteFilenamesHandling ObsoleteFilenames
>
4833 using GetDatabaseFilenamesResult
= std::conditional_t
<
4834 ObsoleteFilenames
== ObsoleteFilenamesHandling::Include
,
4835 SubdirectoriesToProcessAndDatabaseFilenamesAndObsoleteFilenames
,
4836 SubdirectoriesToProcessAndDatabaseFilenames
>;
4838 // Returns a two-part or three-part structure:
4840 // The first part is an array of subdirectories to process.
4842 // The second part is a hashtable of database filenames.
4844 // When ObsoleteFilenames is ObsoleteFilenamesHandling::Include, will also
4845 // collect files based on the marker files. For now,
4846 // GetUsageForOriginInternal() is the only consumer of this result because it
4847 // checks those unfinished deletion and clean them up after that.
4848 template <ObsoleteFilenamesHandling ObsoleteFilenames
=
4849 ObsoleteFilenamesHandling::Omit
>
4850 Result
<GetDatabaseFilenamesResult
<ObsoleteFilenames
>,
4851 nsresult
> static GetDatabaseFilenames(nsIFile
& aDirectory
,
4852 const AtomicBool
& aCanceled
);
4854 nsresult
GetUsageForOriginInternal(PersistenceType aPersistenceType
,
4855 const OriginMetadata
& aOriginMetadata
,
4856 const AtomicBool
& aCanceled
,
4857 bool aInitializing
, UsageInfo
* aUsageInfo
);
4859 // Runs on the PBackground thread. Checks to see if there's a queued
4860 // Maintenance to run.
4861 void ProcessMaintenanceQueue();
4864 class DeleteFilesRunnable final
: public Runnable
{
4865 using DirectoryLock
= mozilla::dom::quota::DirectoryLock
;
4868 // Just created on the PBackground thread. Next step is
4869 // State_DirectoryOpenPending.
4872 // Waiting for directory open allowed on the main thread. The next step is
4873 // State_DatabaseWorkOpen.
4874 State_DirectoryOpenPending
,
4876 // Waiting to do/doing work on the QuotaManager IO thread. The next step is
4877 // State_UnblockingOpen.
4878 State_DatabaseWorkOpen
,
4880 // Notifying the QuotaManager that it can proceed to the next operation on
4881 // the main thread. Next step is State_Completed.
4882 State_UnblockingOpen
,
4888 nsCOMPtr
<nsIEventTarget
> mOwningEventTarget
;
4889 SafeRefPtr
<DatabaseFileManager
> mFileManager
;
4890 RefPtr
<DirectoryLock
> mDirectoryLock
;
4891 nsTArray
<int64_t> mFileIds
;
4893 DEBUGONLY(bool mDEBUGCountsAsPending
= false);
4895 static uint64_t sPendingRunnables
;
4898 DeleteFilesRunnable(SafeRefPtr
<DatabaseFileManager
> aFileManager
,
4899 nsTArray
<int64_t>&& aFileIds
);
4901 void RunImmediately();
4903 static bool IsDeletionPending() { return sPendingRunnables
> 0; }
4907 ~DeleteFilesRunnable();
4909 ~DeleteFilesRunnable() = default;
4914 void DoDatabaseWork();
4922 void DirectoryLockAcquired(DirectoryLock
* aLock
);
4924 void DirectoryLockFailed();
4927 class Maintenance final
: public Runnable
{
4928 struct DirectoryInfo final
{
4929 InitializedOnce
<const OriginMetadata
> mOriginMetadata
;
4930 InitializedOnce
<const nsTArray
<nsString
>> mDatabasePaths
;
4931 const PersistenceType mPersistenceType
;
4933 DirectoryInfo(PersistenceType aPersistenceType
,
4934 OriginMetadata aOriginMetadata
,
4935 nsTArray
<nsString
>&& aDatabasePaths
);
4937 DirectoryInfo(const DirectoryInfo
& aOther
) = delete;
4938 DirectoryInfo(DirectoryInfo
&& aOther
) = delete;
4940 ~DirectoryInfo() { MOZ_COUNT_DTOR(Maintenance::DirectoryInfo
); }
4944 // Newly created on the PBackground thread. Will proceed immediately or be
4945 // added to the maintenance queue. The next step is either
4946 // DirectoryOpenPending if IndexedDatabaseManager is running, or
4947 // CreateIndexedDatabaseManager if not.
4950 // Create IndexedDatabaseManager on the main thread. The next step is either
4951 // Finishing if IndexedDatabaseManager initialization fails, or
4952 // IndexedDatabaseManagerOpen if initialization succeeds.
4953 CreateIndexedDatabaseManager
,
4955 // Call OpenDirectory() on the PBackground thread. The next step is
4956 // DirectoryOpenPending.
4957 IndexedDatabaseManagerOpen
,
4959 // Waiting for directory open allowed on the PBackground thread. The next
4960 // step is either Finishing if directory lock failed to acquire, or
4961 // DirectoryWorkOpen if directory lock is acquired.
4962 DirectoryOpenPending
,
4964 // Waiting to do/doing work on the QuotaManager IO thread. The next step is
4965 // BeginDatabaseMaintenance.
4968 // Dispatching a runnable for each database on the PBackground thread. The
4969 // next state is either WaitingForDatabaseMaintenancesToComplete if at least
4970 // one runnable has been dispatched, or Finishing otherwise.
4971 BeginDatabaseMaintenance
,
4973 // Waiting for DatabaseMaintenance to finish on maintenance thread pool.
4974 // The next state is Finishing if the last runnable has finished.
4975 WaitingForDatabaseMaintenancesToComplete
,
4977 // Waiting to finish/finishing on the PBackground thread. The next step is
4985 RefPtr
<QuotaClient
> mQuotaClient
;
4987 RefPtr
<UniversalDirectoryLock
> mPendingDirectoryLock
;
4988 RefPtr
<UniversalDirectoryLock
> mDirectoryLock
;
4989 nsTArray
<nsCOMPtr
<nsIRunnable
>> mCompleteCallbacks
;
4990 nsTArray
<DirectoryInfo
> mDirectoryInfos
;
4991 nsTHashMap
<nsStringHashKey
, DatabaseMaintenance
*> mDatabaseMaintenances
;
4992 nsresult mResultCode
;
4993 Atomic
<bool> mAborted
;
4997 explicit Maintenance(QuotaClient
* aQuotaClient
)
4998 : Runnable("dom::indexedDB::Maintenance"),
4999 mQuotaClient(aQuotaClient
),
5000 mStartTime(PR_Now()),
5003 mState(State::Initial
) {
5004 AssertIsOnBackgroundThread();
5005 MOZ_ASSERT(aQuotaClient
);
5006 MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient
);
5007 MOZ_ASSERT(mStartTime
);
5010 nsIEventTarget
* BackgroundThread() const {
5011 MOZ_ASSERT(mQuotaClient
);
5012 return mQuotaClient
->BackgroundThread();
5015 PRTime
StartTime() const { return mStartTime
; }
5017 bool IsAborted() const { return mAborted
; }
5019 void RunImmediately() {
5020 MOZ_ASSERT(mState
== State::Initial
);
5022 Unused
<< this->Run();
5027 void RegisterDatabaseMaintenance(DatabaseMaintenance
* aDatabaseMaintenance
);
5029 void UnregisterDatabaseMaintenance(DatabaseMaintenance
* aDatabaseMaintenance
);
5031 bool HasDatabaseMaintenances() const { return mDatabaseMaintenances
.Count(); }
5033 RefPtr
<DatabaseMaintenance
> GetDatabaseMaintenance(
5034 const nsAString
& aDatabasePath
) const {
5035 AssertIsOnBackgroundThread();
5037 return mDatabaseMaintenances
.Get(aDatabasePath
);
5040 void WaitForCompletion(nsIRunnable
* aCallback
) {
5041 AssertIsOnBackgroundThread();
5042 MOZ_ASSERT(mDatabaseMaintenances
.Count());
5044 mCompleteCallbacks
.AppendElement(aCallback
);
5047 void Stringify(nsACString
& aResult
) const;
5050 ~Maintenance() override
{
5051 MOZ_ASSERT(mState
== State::Complete
);
5052 MOZ_ASSERT(!mDatabaseMaintenances
.Count());
5055 // Runs on the PBackground thread. Checks if IndexedDatabaseManager is
5056 // running. Calls OpenDirectory() or dispatches to the main thread on which
5057 // CreateIndexedDatabaseManager() is called.
5060 // Runs on the main thread. Once IndexedDatabaseManager is created it will
5061 // dispatch to the PBackground thread on which OpenDirectory() is called.
5062 nsresult
CreateIndexedDatabaseManager();
5064 // Runs on the PBackground thread. Once QuotaManager has given a lock it will
5065 // call DirectoryOpen().
5066 nsresult
OpenDirectory();
5068 // Runs on the PBackground thread. Dispatches to the QuotaManager I/O thread.
5069 nsresult
DirectoryOpen();
5071 // Runs on the QuotaManager I/O thread. Once it finds databases it will
5072 // dispatch to the PBackground thread on which BeginDatabaseMaintenance()
5074 nsresult
DirectoryWork();
5076 // Runs on the PBackground thread. It dispatches a runnable for each database.
5077 nsresult
BeginDatabaseMaintenance();
5079 // Runs on the PBackground thread. Called when the maintenance is finished or
5080 // if any of above methods fails.
5085 void DirectoryLockAcquired(DirectoryLock
* aLock
);
5087 void DirectoryLockFailed();
5090 Maintenance::DirectoryInfo::DirectoryInfo(PersistenceType aPersistenceType
,
5091 OriginMetadata aOriginMetadata
,
5092 nsTArray
<nsString
>&& aDatabasePaths
)
5093 : mOriginMetadata(std::move(aOriginMetadata
)),
5094 mDatabasePaths(std::move(aDatabasePaths
)),
5095 mPersistenceType(aPersistenceType
) {
5096 MOZ_ASSERT(aPersistenceType
!= PERSISTENCE_TYPE_INVALID
);
5097 MOZ_ASSERT(!mOriginMetadata
->mGroup
.IsEmpty());
5098 MOZ_ASSERT(!mOriginMetadata
->mOrigin
.IsEmpty());
5100 MOZ_ASSERT(!mDatabasePaths
->IsEmpty());
5101 for (const nsAString
& databasePath
: *mDatabasePaths
) {
5102 MOZ_ASSERT(!databasePath
.IsEmpty());
5106 MOZ_COUNT_CTOR(Maintenance::DirectoryInfo
);
5109 class DatabaseMaintenance final
: public Runnable
{
5110 // The minimum amount of time that has passed since the last vacuum before we
5111 // will attempt to analyze the database for fragmentation.
5112 static const PRTime kMinVacuumAge
=
5113 PRTime(PR_USEC_PER_SEC
) * 60 * 60 * 24 * 7;
5115 // If the percent of database pages that are not in contiguous order is higher
5116 // than this percentage we will attempt a vacuum.
5117 static const int32_t kPercentUnorderedThreshold
= 30;
5119 // If the percent of file size growth since the last vacuum is higher than
5120 // this percentage we will attempt a vacuum.
5121 static const int32_t kPercentFileSizeGrowthThreshold
= 10;
5123 // The number of freelist pages beyond which we will favor an incremental
5124 // vacuum over a full vacuum.
5125 static const int32_t kMaxFreelistThreshold
= 5;
5127 // If the percent of unused file bytes in the database exceeds this percentage
5128 // then we will attempt a full vacuum.
5129 static const int32_t kPercentUnusedThreshold
= 20;
5131 enum class MaintenanceAction
{ Nothing
= 0, IncrementalVacuum
, FullVacuum
};
5133 RefPtr
<Maintenance
> mMaintenance
;
5134 RefPtr
<DirectoryLock
> mDirectoryLock
;
5135 const OriginMetadata mOriginMetadata
;
5136 const nsString mDatabasePath
;
5137 int64_t mDirectoryLockId
;
5138 nsCOMPtr
<nsIRunnable
> mCompleteCallback
;
5139 const PersistenceType mPersistenceType
;
5140 const Maybe
<CipherKey
> mMaybeKey
;
5141 Atomic
<bool> mAborted
;
5142 DataMutex
<nsCOMPtr
<mozIStorageConnection
>> mSharedStorageConnection
;
5145 DatabaseMaintenance(Maintenance
* aMaintenance
, DirectoryLock
* aDirectoryLock
,
5146 PersistenceType aPersistenceType
,
5147 const OriginMetadata
& aOriginMetadata
,
5148 const nsAString
& aDatabasePath
,
5149 const Maybe
<CipherKey
>& aMaybeKey
)
5150 : Runnable("dom::indexedDB::DatabaseMaintenance"),
5151 mMaintenance(aMaintenance
),
5152 mDirectoryLock(aDirectoryLock
),
5153 mOriginMetadata(aOriginMetadata
),
5154 mDatabasePath(aDatabasePath
),
5155 mPersistenceType(aPersistenceType
),
5156 mMaybeKey
{aMaybeKey
},
5158 mSharedStorageConnection("sharedStorageConnection") {
5159 MOZ_ASSERT(aDirectoryLock
);
5161 MOZ_ASSERT(mDirectoryLock
->Id() >= 0);
5162 mDirectoryLockId
= mDirectoryLock
->Id();
5165 const nsAString
& DatabasePath() const { return mDatabasePath
; }
5167 void WaitForCompletion(nsIRunnable
* aCallback
) {
5168 AssertIsOnBackgroundThread();
5169 MOZ_ASSERT(!mCompleteCallback
);
5171 mCompleteCallback
= aCallback
;
5174 void Stringify(nsACString
& aResult
) const;
5179 ~DatabaseMaintenance() override
= default;
5181 // Runs on maintenance thread pool. Does maintenance on the database.
5182 void PerformMaintenanceOnDatabase();
5184 // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
5185 nsresult
CheckIntegrity(mozIStorageConnection
& aConnection
, bool* aOk
);
5187 // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
5188 nsresult
DetermineMaintenanceAction(mozIStorageConnection
& aConnection
,
5189 nsIFile
* aDatabaseFile
,
5190 MaintenanceAction
* aMaintenanceAction
);
5192 // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
5193 void IncrementalVacuum(mozIStorageConnection
& aConnection
);
5195 // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
5196 void FullVacuum(mozIStorageConnection
& aConnection
, nsIFile
* aDatabaseFile
);
5198 // Runs on the PBackground thread. It dispatches a complete callback and
5199 // unregisters from Maintenance.
5200 void RunOnOwningThread();
5202 // Runs on maintenance thread pool. Once it performs database maintenance
5203 // it will dispatch to the PBackground thread on which RunOnOwningThread()
5205 void RunOnConnectionThread();
5207 // TODO: Could QuotaClient::IsShuttingDownOnNonBackgroundThread() call
5208 // be part of mMaintenance::IsAborted() ?
5209 inline bool IsAborted() const {
5210 return mMaintenance
->IsAborted() || mAborted
||
5211 NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread());
5219 class DEBUGThreadSlower final
: public nsIThreadObserver
{
5221 DEBUGThreadSlower() {
5222 AssertIsOnBackgroundThread();
5223 MOZ_ASSERT(kDEBUGThreadSleepMS
);
5229 ~DEBUGThreadSlower() { AssertIsOnBackgroundThread(); }
5231 NS_DECL_NSITHREADOBSERVER
5236 /*******************************************************************************
5238 ******************************************************************************/
5240 // XXX Get rid of FileHelper and move the functions into DatabaseFileManager.
5241 // Then, DatabaseFileManager::Get(Journal)Directory and
5242 // DatabaseFileManager::GetFileForId might eventually be made private.
5243 class MOZ_STACK_CLASS FileHelper final
{
5244 const SafeRefPtr
<DatabaseFileManager
> mFileManager
;
5246 LazyInitializedOnce
<const NotNull
<nsCOMPtr
<nsIFile
>>> mFileDirectory
;
5247 LazyInitializedOnce
<const NotNull
<nsCOMPtr
<nsIFile
>>> mJournalDirectory
;
5250 LazyInitializedOnce
<const NotNull
<RefPtr
<ReadCallback
>>> mReadCallback
;
5253 explicit FileHelper(SafeRefPtr
<DatabaseFileManager
>&& aFileManager
)
5254 : mFileManager(std::move(aFileManager
)) {
5255 MOZ_ASSERT(mFileManager
);
5260 [[nodiscard
]] nsCOMPtr
<nsIFile
> GetFile(const DatabaseFileInfo
& aFileInfo
);
5262 [[nodiscard
]] nsCOMPtr
<nsIFile
> GetJournalFile(
5263 const DatabaseFileInfo
& aFileInfo
);
5265 nsresult
CreateFileFromStream(nsIFile
& aFile
, nsIFile
& aJournalFile
,
5266 nsIInputStream
& aInputStream
, bool aCompress
,
5267 const Maybe
<CipherKey
>& aMaybeKey
);
5270 nsresult
SyncCopy(nsIInputStream
& aInputStream
,
5271 nsIOutputStream
& aOutputStream
, char* aBuffer
,
5272 uint32_t aBufferSize
);
5274 nsresult
SyncRead(nsIInputStream
& aInputStream
, char* aBuffer
,
5275 uint32_t aBufferSize
, uint32_t* aRead
);
5278 /*******************************************************************************
5280 ******************************************************************************/
5282 bool GetFilenameBase(const nsAString
& aFilename
, const nsAString
& aSuffix
,
5283 nsDependentSubstring
& aFilenameBase
) {
5284 MOZ_ASSERT(!aFilename
.IsEmpty());
5285 MOZ_ASSERT(aFilenameBase
.IsEmpty());
5287 if (!StringEndsWith(aFilename
, aSuffix
) ||
5288 aFilename
.Length() == aSuffix
.Length()) {
5292 MOZ_ASSERT(aFilename
.Length() > aSuffix
.Length());
5294 aFilenameBase
.Rebind(aFilename
, 0, aFilename
.Length() - aSuffix
.Length());
5298 class EncryptedFileBlobImpl final
: public FileBlobImpl
{
5300 EncryptedFileBlobImpl(const nsCOMPtr
<nsIFile
>& aNativeFile
,
5301 const DatabaseFileInfo::IdType aId
,
5302 const CipherKey
& aKey
)
5303 : FileBlobImpl
{aNativeFile
}, mKey
{aKey
} {
5307 uint64_t GetSize(ErrorResult
& aRv
) override
{
5308 nsCOMPtr
<nsIInputStream
> inputStream
;
5309 CreateInputStream(getter_AddRefs(inputStream
), aRv
);
5315 MOZ_ASSERT(inputStream
);
5317 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(inputStream
, Available
), 0,
5318 [&aRv
](const nsresult rv
) { aRv
= rv
; });
5321 void CreateInputStream(nsIInputStream
** aInputStream
,
5322 ErrorResult
& aRv
) const override
{
5323 nsCOMPtr
<nsIInputStream
> baseInputStream
;
5324 FileBlobImpl::CreateInputStream(getter_AddRefs(baseInputStream
), aRv
);
5325 if (NS_WARN_IF(aRv
.Failed())) {
5330 MakeAndAddRef
<DecryptingInputStream
<IndexedDBCipherStrategy
>>(
5331 WrapNotNull(std::move(baseInputStream
)), kEncryptedStreamBlockSize
,
5336 void GetBlobImplType(nsAString
& aBlobImplType
) const override
{
5337 aBlobImplType
= u
"EncryptedFileBlobImpl"_ns
;
5340 already_AddRefed
<BlobImpl
> CreateSlice(uint64_t aStart
, uint64_t aLength
,
5341 const nsAString
& aContentType
,
5342 ErrorResult
& aRv
) const override
{
5343 MOZ_CRASH("Not implemented because this should be unreachable.");
5347 const CipherKey mKey
;
5350 RefPtr
<BlobImpl
> CreateFileBlobImpl(const Database
& aDatabase
,
5351 const nsCOMPtr
<nsIFile
>& aNativeFile
,
5352 const DatabaseFileInfo::IdType aId
) {
5353 if (aDatabase
.IsInPrivateBrowsing()) {
5355 keyId
.AppendInt(aId
);
5358 aDatabase
.GetFileManager().MutableCipherKeyManagerRef().Get(keyId
);
5360 MOZ_RELEASE_ASSERT(key
.isSome());
5361 return MakeRefPtr
<EncryptedFileBlobImpl
>(aNativeFile
, aId
, *key
);
5364 auto impl
= MakeRefPtr
<FileBlobImpl
>(aNativeFile
);
5365 impl
->SetFileId(aId
);
5370 Result
<nsTArray
<SerializedStructuredCloneFile
>, nsresult
>
5371 SerializeStructuredCloneFiles(const SafeRefPtr
<Database
>& aDatabase
,
5372 const nsTArray
<StructuredCloneFileParent
>& aFiles
,
5373 bool aForPreprocess
) {
5374 AssertIsOnBackgroundThread();
5375 MOZ_ASSERT(aDatabase
);
5377 if (aFiles
.IsEmpty()) {
5378 return nsTArray
<SerializedStructuredCloneFile
>{};
5381 const nsCOMPtr
<nsIFile
> directory
=
5382 aDatabase
->GetFileManager().GetCheckedDirectory();
5383 QM_TRY(OkIf(directory
), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
),
5384 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
5386 nsTArray
<SerializedStructuredCloneFile
> serializedStructuredCloneFiles
;
5387 QM_TRY(OkIf(serializedStructuredCloneFiles
.SetCapacity(aFiles
.Length(),
5389 Err(NS_ERROR_OUT_OF_MEMORY
));
5391 QM_TRY(TransformIfAbortOnErr(
5392 aFiles
, MakeBackInserter(serializedStructuredCloneFiles
),
5393 [aForPreprocess
](const auto& file
) {
5394 return !aForPreprocess
||
5395 file
.Type() == StructuredCloneFileBase::eStructuredClone
;
5397 [&directory
, &aDatabase
, aForPreprocess
](
5398 const auto& file
) -> Result
<SerializedStructuredCloneFile
, nsresult
> {
5399 const int64_t fileId
= file
.FileInfo().Id();
5400 MOZ_ASSERT(fileId
> 0);
5402 const nsCOMPtr
<nsIFile
> nativeFile
=
5403 mozilla::dom::indexedDB::DatabaseFileManager::GetCheckedFileForId(
5405 QM_TRY(OkIf(nativeFile
), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
),
5406 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
5408 switch (file
.Type()) {
5409 case StructuredCloneFileBase::eStructuredClone
:
5410 if (!aForPreprocess
) {
5411 return SerializedStructuredCloneFile
{
5412 null_t(), StructuredCloneFileBase::eStructuredClone
};
5417 case StructuredCloneFileBase::eBlob
: {
5418 const auto impl
= CreateFileBlobImpl(*aDatabase
, nativeFile
,
5419 file
.FileInfo().Id());
5423 // This can only fail if the child has crashed.
5424 QM_TRY(MOZ_TO_RESULT(IPCBlobUtils::Serialize(impl
, ipcBlob
)),
5425 Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
),
5426 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
5428 aDatabase
->MapBlob(ipcBlob
, file
.FileInfoPtr());
5430 return SerializedStructuredCloneFile
{ipcBlob
, file
.Type()};
5433 case StructuredCloneFileBase::eMutableFile
:
5434 case StructuredCloneFileBase::eWasmBytecode
:
5435 case StructuredCloneFileBase::eWasmCompiled
: {
5436 // Set file() to null, support for storing WebAssembly.Modules has
5437 // been removed in bug 1469395. Support for de-serialization of
5438 // WebAssembly.Modules modules has been removed in bug 1561876.
5439 // Support for MutableFile has been removed in bug 1500343. Full
5440 // removal is tracked in bug 1487479.
5442 return SerializedStructuredCloneFile
{null_t(), file
.Type()};
5446 MOZ_CRASH("Should never get here!");
5450 return std::move(serializedStructuredCloneFiles
);
5453 bool IsFileNotFoundError(const nsresult aRv
) {
5454 return aRv
== NS_ERROR_FILE_NOT_FOUND
;
5457 enum struct Idempotency
{ Yes
, No
};
5459 // Delete a file, decreasing the quota usage as appropriate. If the file no
5460 // longer exists but aIdempotency is Idempotency::Yes, success is returned,
5461 // although quota usage can't be decreased. (With the assumption being that the
5462 // file was already deleted prior to this logic running, and the non-existent
5463 // file was no longer tracked by quota because it didn't exist at
5464 // initialization time or a previous deletion call updated the usage.)
5465 nsresult
DeleteFile(nsIFile
& aFile
, QuotaManager
* const aQuotaManager
,
5466 const PersistenceType aPersistenceType
,
5467 const OriginMetadata
& aOriginMetadata
,
5468 const Idempotency aIdempotency
) {
5469 MOZ_ASSERT(!NS_IsMainThread());
5470 MOZ_ASSERT(!IsOnBackgroundThread());
5472 // Callers which pass Idempotency::Yes call this function without checking if
5473 // the file already exists (idempotent usage). QM_OR_ELSE_WARN_IF is not used
5474 // here since we just want to log NS_ERROR_FILE_NOT_FOUND results and not spam
5476 // Theoretically, there should be no QM_OR_ELSE_(WARN|LOG_VERBOSE)_IF when a
5477 // caller passes Idempotency::No, but it's simpler when the predicate just
5478 // always returns false in that case.
5480 const auto isIgnorableError
= [&aIdempotency
]() -> bool (*)(nsresult
) {
5481 if (aIdempotency
== Idempotency::Yes
) {
5482 return IsFileNotFoundError
;
5485 return [](const nsresult rv
) { return false; };
5489 const auto& fileSize
,
5490 ([aQuotaManager
, &aFile
,
5491 isIgnorableError
]() -> Result
<Maybe
<int64_t>, nsresult
> {
5492 if (aQuotaManager
) {
5494 const Maybe
<int64_t>& fileSize
,
5495 QM_OR_ELSE_LOG_VERBOSE_IF(
5497 MOZ_TO_RESULT_INVOKE_MEMBER(aFile
, GetFileSize
)
5498 .map([](const int64_t val
) { return Some(val
); }),
5502 ErrToDefaultOk
<Maybe
<int64_t>>));
5504 // XXX Can we really assert that the file size is not 0 if
5505 // it existed? This might be violated by external
5507 MOZ_ASSERT(!fileSize
|| fileSize
.value() >= 0);
5512 return Some(int64_t(0));
5519 QM_TRY_INSPECT(const auto& didExist
,
5520 QM_OR_ELSE_LOG_VERBOSE_IF(
5522 MOZ_TO_RESULT(aFile
.Remove(false)).map(Some
<Ok
>),
5526 ErrToDefaultOk
<Maybe
<Ok
>>));
5529 // XXX If we get here, this means that the file still existed when we
5530 // queried its size, but no longer when we tried to remove it. Not sure if
5531 // this should really be silently accepted in idempotent mode.
5535 if (fileSize
.value() > 0) {
5536 MOZ_ASSERT(aQuotaManager
);
5538 aQuotaManager
->DecreaseUsageForClient(
5539 ClientMetadata
{aOriginMetadata
, Client::IDB
}, fileSize
.value());
5545 nsresult
DeleteFile(nsIFile
& aDirectory
, const nsAString
& aFilename
,
5546 QuotaManager
* const aQuotaManager
,
5547 const PersistenceType aPersistenceType
,
5548 const OriginMetadata
& aOriginMetadata
,
5549 const Idempotency aIdempotent
) {
5550 AssertIsOnIOThread();
5551 MOZ_ASSERT(!aFilename
.IsEmpty());
5553 QM_TRY_INSPECT(const auto& file
, CloneFileAndAppend(aDirectory
, aFilename
));
5555 return DeleteFile(*file
, aQuotaManager
, aPersistenceType
, aOriginMetadata
,
5559 // Delete files in a directory that you think exists. If the directory doesn't
5560 // exist, an error will not be returned, but warning telemetry will be
5561 // generated! So only call this on directories that you know exist (idempotent
5562 // usage, but it's not recommended).
5563 nsresult
DeleteFilesNoQuota(nsIFile
& aFile
) {
5564 AssertIsOnIOThread();
5566 QM_TRY_INSPECT(const auto& didExist
,
5569 MOZ_TO_RESULT(aFile
.Remove(true)).map(Some
<Ok
>),
5571 IsFileNotFoundError
,
5573 ErrToDefaultOk
<Maybe
<Ok
>>));
5580 nsresult
DeleteFilesNoQuota(nsIFile
* aDirectory
, const nsAString
& aFilename
) {
5581 AssertIsOnIOThread();
5582 MOZ_ASSERT(aDirectory
);
5583 MOZ_ASSERT(!aFilename
.IsEmpty());
5585 // The current using function hasn't initialized the origin, so in here we
5586 // don't update the size of origin. Adding this assertion for preventing from
5588 DebugOnly
<QuotaManager
*> quotaManager
= QuotaManager::Get();
5589 MOZ_ASSERT(!quotaManager
->IsTemporaryStorageInitializedInternal());
5591 QM_TRY_INSPECT(const auto& file
, CloneFileAndAppend(*aDirectory
, aFilename
));
5593 QM_TRY(MOZ_TO_RESULT(DeleteFilesNoQuota(*file
)));
5598 // CreateMarkerFile and RemoveMarkerFile are a pair of functions to indicate
5599 // whether having removed all the files successfully. The marker file should
5600 // be checked before executing the next operation or initialization.
5601 Result
<nsCOMPtr
<nsIFile
>, nsresult
> CreateMarkerFile(
5602 nsIFile
& aBaseDirectory
, const nsAString
& aDatabaseNameBase
) {
5603 AssertIsOnIOThread();
5604 MOZ_ASSERT(!aDatabaseNameBase
.IsEmpty());
5607 const auto& markerFile
,
5608 CloneFileAndAppend(aBaseDirectory
,
5609 kIdbDeletionMarkerFilePrefix
+ aDatabaseNameBase
));
5611 // Callers call this function without checking if the file already exists
5612 // (idempotent usage). QM_OR_ELSE_WARN_IF is not used here since we just want
5613 // to log NS_ERROR_FILE_ALREADY_EXISTS result and not spam the reports.
5615 // TODO: In theory if this file exists, then RemoveDatabaseFilesAndDirectory
5616 // should have cleaned it up, but obviously we can crash and not clean it up,
5617 // which is the whole point of the marker file. In that case, we'll realize
5618 // the marker file exists in OpenDatabaseOp::DoDatabaseWork or
5619 // GetUsageForOriginInternal and resume the removal by calling
5620 // RemoveDatabaseFilesAndDirectory again, but we will also try to create the
5621 // marker file again, so if we see this marker file, it is part
5622 // of our standard operating procedure to redundantly try and create the
5623 // marker here. We currently treat this as idempotent usage, but we could
5624 // add an additional argument to RemoveDatabaseFilesAndDirectory which would
5625 // indicate that we are resuming an unfinished removal, so the marker already
5626 // exists and doesn't have to be created, and change
5627 // QM_OR_ELSE_LOG_VERBOSE_IF to QM_OR_ELSE_WARN_IF in the end.
5628 QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF(
5630 MOZ_TO_RESULT(markerFile
->Create(nsIFile::NORMAL_FILE_TYPE
, 0644)),
5632 IsSpecificError
<NS_ERROR_FILE_ALREADY_EXISTS
>,
5639 nsresult
RemoveMarkerFile(nsIFile
* aMarkerFile
) {
5640 AssertIsOnIOThread();
5641 MOZ_ASSERT(aMarkerFile
);
5643 DebugOnly
<bool> exists
;
5644 MOZ_ASSERT(NS_SUCCEEDED(aMarkerFile
->Exists(&exists
)));
5647 QM_TRY(MOZ_TO_RESULT(aMarkerFile
->Remove(false)));
5652 Result
<Ok
, nsresult
> DeleteFileManagerDirectory(
5653 nsIFile
& aFileManagerDirectory
, QuotaManager
* aQuotaManager
,
5654 const PersistenceType aPersistenceType
,
5655 const OriginMetadata
& aOriginMetadata
) {
5656 // XXX In theory, deleting can continue for other files in case of a failure,
5657 // leaving only those files behind that cause the problem actually. However,
5658 // the current architecture doesn't allow having more databases (for the same
5659 // name) on disk, so trying to delete as much as possible won't help much
5660 // because we need to delete entire .files directory in the end anyway.
5661 QM_TRY(DatabaseFileManager::TraverseFiles(
5662 aFileManagerDirectory
,
5664 [&aQuotaManager
, aPersistenceType
, &aOriginMetadata
](
5665 nsIFile
& file
, const bool isDirectory
) -> Result
<Ok
, nsresult
> {
5667 // The journal directory doesn't count towards quota.
5668 QM_TRY_RETURN(MOZ_TO_RESULT(DeleteFilesNoQuota(file
)));
5671 // Stored files do count towards quota.
5673 MOZ_TO_RESULT(DeleteFile(file
, aQuotaManager
, aPersistenceType
,
5674 aOriginMetadata
, Idempotency::Yes
)));
5676 // UnknownDirEntryOp
5677 [aPersistenceType
, &aOriginMetadata
](
5678 nsIFile
& file
, const bool isDirectory
) -> Result
<Ok
, nsresult
> {
5679 // Unknown files and directories don't count towards quota.
5682 QM_TRY_RETURN(MOZ_TO_RESULT(DeleteFilesNoQuota(file
)));
5685 QM_TRY_RETURN(MOZ_TO_RESULT(
5686 DeleteFile(file
, /* doesn't count */ nullptr, aPersistenceType
,
5687 aOriginMetadata
, Idempotency::Yes
)));
5690 QM_TRY_RETURN(MOZ_TO_RESULT(aFileManagerDirectory
.Remove(false)));
5693 // Idempotently delete all the parts of an IndexedDB database including its
5694 // SQLite database file, its WAL journal, it's shared-memory file, and its
5695 // Blob/Files sub-directory. A marker file is created prior to performing the
5696 // deletion so that in the event we crash or fail to successfully delete the
5697 // database and its files, we will re-attempt the deletion the next time the
5698 // origin is initialized using this method. Because this means the method may be
5699 // called on a partially deleted database, this method uses DeleteFile which
5700 // succeeds when the file we ask it to delete does not actually exist. The
5701 // marker file is removed once deletion has successfully completed.
5702 nsresult
RemoveDatabaseFilesAndDirectory(nsIFile
& aBaseDirectory
,
5703 const nsAString
& aDatabaseFilenameBase
,
5704 QuotaManager
* aQuotaManager
,
5705 const PersistenceType aPersistenceType
,
5706 const OriginMetadata
& aOriginMetadata
,
5707 const nsAString
& aDatabaseName
) {
5708 AssertIsOnIOThread();
5709 MOZ_ASSERT(!aDatabaseFilenameBase
.IsEmpty());
5711 AUTO_PROFILER_LABEL("RemoveDatabaseFilesAndDirectory", DOM
);
5713 QM_TRY_UNWRAP(auto markerFile
,
5714 CreateMarkerFile(aBaseDirectory
, aDatabaseFilenameBase
));
5716 // The database file counts towards quota.
5717 QM_TRY(MOZ_TO_RESULT(DeleteFile(
5718 aBaseDirectory
, aDatabaseFilenameBase
+ kSQLiteSuffix
, aQuotaManager
,
5719 aPersistenceType
, aOriginMetadata
, Idempotency::Yes
)));
5721 // .sqlite-journal files don't count towards quota.
5722 QM_TRY(MOZ_TO_RESULT(DeleteFile(aBaseDirectory
,
5723 aDatabaseFilenameBase
+ kSQLiteJournalSuffix
,
5724 /* doesn't count */ nullptr, aPersistenceType
,
5725 aOriginMetadata
, Idempotency::Yes
)));
5727 // .sqlite-shm files don't count towards quota.
5728 QM_TRY(MOZ_TO_RESULT(DeleteFile(aBaseDirectory
,
5729 aDatabaseFilenameBase
+ kSQLiteSHMSuffix
,
5730 /* doesn't count */ nullptr, aPersistenceType
,
5731 aOriginMetadata
, Idempotency::Yes
)));
5733 // .sqlite-wal files do count towards quota.
5734 QM_TRY(MOZ_TO_RESULT(DeleteFile(
5735 aBaseDirectory
, aDatabaseFilenameBase
+ kSQLiteWALSuffix
, aQuotaManager
,
5736 aPersistenceType
, aOriginMetadata
, Idempotency::Yes
)));
5738 // The files directory counts towards quota.
5740 const auto& fmDirectory
,
5741 CloneFileAndAppend(aBaseDirectory
, aDatabaseFilenameBase
+
5742 kFileManagerDirectoryNameSuffix
));
5744 QM_TRY_INSPECT(const bool& exists
,
5745 MOZ_TO_RESULT_INVOKE_MEMBER(fmDirectory
, Exists
));
5748 QM_TRY_INSPECT(const bool& isDirectory
,
5749 MOZ_TO_RESULT_INVOKE_MEMBER(fmDirectory
, IsDirectory
));
5751 QM_TRY(OkIf(isDirectory
), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
5753 QM_TRY(DeleteFileManagerDirectory(*fmDirectory
, aQuotaManager
,
5754 aPersistenceType
, aOriginMetadata
));
5757 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
5758 MOZ_ASSERT_IF(aQuotaManager
, mgr
);
5761 mgr
->InvalidateFileManager(aPersistenceType
, aOriginMetadata
.mOrigin
,
5765 QM_TRY(MOZ_TO_RESULT(RemoveMarkerFile(markerFile
)));
5770 /*******************************************************************************
5772 ******************************************************************************/
5774 // Counts the number of "live" Factory, FactoryOp and Database instances.
5775 uint64_t gBusyCount
= 0;
5777 using FactoryOpArray
= nsTArray
<CheckedUnsafePtr
<FactoryOp
>>;
5779 StaticAutoPtr
<FactoryOpArray
> gFactoryOps
;
5781 // Maps a database id to information about live database actors.
5782 using DatabaseActorHashtable
=
5783 nsClassHashtable
<nsCStringHashKey
, DatabaseActorInfo
>;
5785 StaticAutoPtr
<DatabaseActorHashtable
> gLiveDatabaseHashtable
;
5787 StaticRefPtr
<ConnectionPool
> gConnectionPool
;
5789 using DatabaseLoggingInfoHashtable
=
5790 nsTHashMap
<nsIDHashKey
, DatabaseLoggingInfo
*>;
5792 StaticAutoPtr
<DatabaseLoggingInfoHashtable
> gLoggingInfoHashtable
;
5794 using TelemetryIdHashtable
= nsTHashMap
<nsUint32HashKey
, uint32_t>;
5796 StaticAutoPtr
<TelemetryIdHashtable
> gTelemetryIdHashtable
;
5798 // Protects all reads and writes to gTelemetryIdHashtable.
5799 StaticAutoPtr
<Mutex
> gTelemetryIdMutex
;
5801 // For private browsing, maps the raw database names provided by content to a
5802 // replacement UUID in order to avoid exposing the name of the database on
5803 // disk or a directly derived value, such as the non-private-browsing
5804 // representation. This mapping will be the same for all databases with the
5805 // same name across all storage keys/origins for the lifetime of the IDB
5806 // QuotaClient. In tests, the QuotaClient may be created and destroyed multiple
5807 // times, but for normal browser use the QuotaClient will last until the
5808 // browser shuts down. Bug 1831835 will improve this implementation to avoid
5809 // using the same mapping across storage keys and to deal with the resulting
5810 // lifecycle issues of the additional memory use.
5811 using StorageDatabaseNameHashtable
= nsTHashMap
<nsString
, nsString
>;
5813 StaticAutoPtr
<StorageDatabaseNameHashtable
> gStorageDatabaseNameHashtable
;
5815 // Protects all reads and writes to gStorageDatabaseNameHashtable.
5816 StaticAutoPtr
<Mutex
> gStorageDatabaseNameMutex
;
5820 StaticRefPtr
<DEBUGThreadSlower
> gDEBUGThreadSlower
;
5824 void IncreaseBusyCount() {
5825 AssertIsOnBackgroundThread();
5827 // If this is the first instance then we need to do some initialization.
5829 MOZ_ASSERT(!gFactoryOps
);
5830 gFactoryOps
= new FactoryOpArray();
5832 MOZ_ASSERT(!gLiveDatabaseHashtable
);
5833 gLiveDatabaseHashtable
= new DatabaseActorHashtable();
5835 MOZ_ASSERT(!gLoggingInfoHashtable
);
5836 gLoggingInfoHashtable
= new DatabaseLoggingInfoHashtable();
5839 if (kDEBUGThreadPriority
!= nsISupportsPriority::PRIORITY_NORMAL
) {
5841 "PBackground thread debugging enabled, priority has been "
5843 nsCOMPtr
<nsISupportsPriority
> thread
=
5844 do_QueryInterface(NS_GetCurrentThread());
5847 MOZ_ALWAYS_SUCCEEDS(thread
->SetPriority(kDEBUGThreadPriority
));
5850 if (kDEBUGThreadSleepMS
) {
5852 "PBackground thread debugging enabled, sleeping after every "
5854 nsCOMPtr
<nsIThreadInternal
> thread
=
5855 do_QueryInterface(NS_GetCurrentThread());
5858 gDEBUGThreadSlower
= new DEBUGThreadSlower();
5860 MOZ_ALWAYS_SUCCEEDS(thread
->AddObserver(gDEBUGThreadSlower
));
5868 void DecreaseBusyCount() {
5869 AssertIsOnBackgroundThread();
5870 MOZ_ASSERT(gBusyCount
);
5872 // Clean up if there are no more instances.
5873 if (--gBusyCount
== 0) {
5874 MOZ_ASSERT(gLoggingInfoHashtable
);
5875 gLoggingInfoHashtable
= nullptr;
5877 MOZ_ASSERT(gLiveDatabaseHashtable
);
5878 MOZ_ASSERT(!gLiveDatabaseHashtable
->Count());
5879 gLiveDatabaseHashtable
= nullptr;
5881 MOZ_ASSERT(gFactoryOps
);
5882 MOZ_ASSERT(gFactoryOps
->IsEmpty());
5883 gFactoryOps
= nullptr;
5886 if (kDEBUGThreadPriority
!= nsISupportsPriority::PRIORITY_NORMAL
) {
5887 nsCOMPtr
<nsISupportsPriority
> thread
=
5888 do_QueryInterface(NS_GetCurrentThread());
5891 MOZ_ALWAYS_SUCCEEDS(
5892 thread
->SetPriority(nsISupportsPriority::PRIORITY_NORMAL
));
5895 if (kDEBUGThreadSleepMS
) {
5896 MOZ_ASSERT(gDEBUGThreadSlower
);
5898 nsCOMPtr
<nsIThreadInternal
> thread
=
5899 do_QueryInterface(NS_GetCurrentThread());
5902 MOZ_ALWAYS_SUCCEEDS(thread
->RemoveObserver(gDEBUGThreadSlower
));
5904 gDEBUGThreadSlower
= nullptr;
5910 template <typename Condition
>
5911 void InvalidateLiveDatabasesMatching(const Condition
& aCondition
) {
5912 AssertIsOnBackgroundThread();
5914 if (!gLiveDatabaseHashtable
) {
5918 // Invalidating a Database will cause it to be removed from the
5919 // gLiveDatabaseHashtable entries' mLiveDatabases, and, if it was the last
5920 // element in mLiveDatabases, to remove the whole hashtable entry. Therefore,
5921 // we need to make a temporary list of the databases to invalidate to avoid
5922 // iterator invalidation.
5924 nsTArray
<SafeRefPtr
<Database
>> databases
;
5926 for (const auto& liveDatabasesEntry
: gLiveDatabaseHashtable
->Values()) {
5927 for (const auto& database
: liveDatabasesEntry
->mLiveDatabases
) {
5928 if (aCondition(*database
)) {
5929 databases
.AppendElement(
5930 SafeRefPtr
{database
.get(), AcquireStrongRefFromRawPtr
{}});
5935 for (const auto& database
: databases
) {
5936 database
->Invalidate();
5940 uint32_t TelemetryIdForFile(nsIFile
* aFile
) {
5941 // May be called on any thread!
5944 MOZ_ASSERT(gTelemetryIdMutex
);
5946 // The storage directory is structured like this:
5948 // <profile>/storage/<persistence>/<origin>/idb/<filename>.sqlite
5950 // For the purposes of this function we're only concerned with the
5951 // <persistence>, <origin>, and <filename> pieces.
5954 MOZ_ALWAYS_SUCCEEDS(aFile
->GetLeafName(filename
));
5956 // Make sure we were given a database file.
5957 MOZ_ASSERT(StringEndsWith(filename
, kSQLiteSuffix
));
5959 filename
.Truncate(filename
.Length() - kSQLiteSuffix
.Length());
5961 // Get the "idb" directory.
5962 nsCOMPtr
<nsIFile
> idbDirectory
;
5963 MOZ_ALWAYS_SUCCEEDS(aFile
->GetParent(getter_AddRefs(idbDirectory
)));
5965 DebugOnly
<nsString
> idbLeafName
;
5966 MOZ_ASSERT(NS_SUCCEEDED(idbDirectory
->GetLeafName(idbLeafName
)));
5967 MOZ_ASSERT(static_cast<nsString
&>(idbLeafName
).EqualsLiteral("idb"));
5969 // Get the <origin> directory.
5970 nsCOMPtr
<nsIFile
> originDirectory
;
5971 MOZ_ALWAYS_SUCCEEDS(idbDirectory
->GetParent(getter_AddRefs(originDirectory
)));
5974 MOZ_ALWAYS_SUCCEEDS(originDirectory
->GetLeafName(origin
));
5976 // Any databases in these directories are owned by the application and should
5977 // not have their filenames masked. Hopefully they also appear in the
5978 // Telemetry.cpp whitelist.
5979 if (origin
.EqualsLiteral("chrome") ||
5980 origin
.EqualsLiteral("moz-safe-about+home")) {
5984 // Get the <persistence> directory.
5985 nsCOMPtr
<nsIFile
> persistenceDirectory
;
5986 MOZ_ALWAYS_SUCCEEDS(
5987 originDirectory
->GetParent(getter_AddRefs(persistenceDirectory
)));
5989 nsString persistence
;
5990 MOZ_ALWAYS_SUCCEEDS(persistenceDirectory
->GetLeafName(persistence
));
5992 constexpr auto separator
= u
"*"_ns
;
5994 uint32_t hashValue
=
5995 HashString(persistence
+ separator
+ origin
+ separator
+ filename
);
5997 MutexAutoLock
lock(*gTelemetryIdMutex
);
5999 if (!gTelemetryIdHashtable
) {
6000 gTelemetryIdHashtable
= new TelemetryIdHashtable();
6003 return gTelemetryIdHashtable
->LookupOrInsertWith(hashValue
, [] {
6004 static uint32_t sNextId
= 1;
6006 // We're locked, no need for atomics.
6011 nsAutoString
GetDatabaseFilenameBase(const nsAString
& aDatabaseName
,
6013 nsAutoString databaseFilenameBase
;
6016 MOZ_DIAGNOSTIC_ASSERT(gStorageDatabaseNameMutex
);
6018 MutexAutoLock
lock(*gStorageDatabaseNameMutex
);
6020 if (!gStorageDatabaseNameHashtable
) {
6021 gStorageDatabaseNameHashtable
= new StorageDatabaseNameHashtable();
6024 databaseFilenameBase
.Append(
6025 gStorageDatabaseNameHashtable
->LookupOrInsertWith(aDatabaseName
, []() {
6026 return NSID_TrimBracketsUTF16(nsID::GenerateUUID());
6029 return databaseFilenameBase
;
6032 // WARNING: do not change this hash function. See the comment in HashName()
6034 databaseFilenameBase
.AppendInt(HashName(aDatabaseName
));
6036 nsAutoCString escapedName
;
6037 if (!NS_Escape(NS_ConvertUTF16toUTF8(aDatabaseName
), escapedName
,
6039 MOZ_CRASH("Can't escape database name!");
6042 const char* forwardIter
= escapedName
.BeginReading();
6043 const char* backwardIter
= escapedName
.EndReading() - 1;
6045 nsAutoCString substring
;
6046 while (forwardIter
<= backwardIter
&& substring
.Length() < 21) {
6047 if (substring
.Length() % 2) {
6048 substring
.Append(*backwardIter
--);
6050 substring
.Append(*forwardIter
++);
6054 databaseFilenameBase
.AppendASCII(substring
.get(), substring
.Length());
6056 return databaseFilenameBase
;
6059 const CommonIndexOpenCursorParams
& GetCommonIndexOpenCursorParams(
6060 const OpenCursorParams
& aParams
) {
6061 switch (aParams
.type()) {
6062 case OpenCursorParams::TIndexOpenCursorParams
:
6063 return aParams
.get_IndexOpenCursorParams().commonIndexParams();
6064 case OpenCursorParams::TIndexOpenKeyCursorParams
:
6065 return aParams
.get_IndexOpenKeyCursorParams().commonIndexParams();
6067 MOZ_CRASH("Should never get here!");
6071 const CommonOpenCursorParams
& GetCommonOpenCursorParams(
6072 const OpenCursorParams
& aParams
) {
6073 switch (aParams
.type()) {
6074 case OpenCursorParams::TObjectStoreOpenCursorParams
:
6075 return aParams
.get_ObjectStoreOpenCursorParams().commonParams();
6076 case OpenCursorParams::TObjectStoreOpenKeyCursorParams
:
6077 return aParams
.get_ObjectStoreOpenKeyCursorParams().commonParams();
6078 case OpenCursorParams::TIndexOpenCursorParams
:
6079 case OpenCursorParams::TIndexOpenKeyCursorParams
:
6080 return GetCommonIndexOpenCursorParams(aParams
).commonParams();
6082 MOZ_CRASH("Should never get here!");
6086 // TODO: Using nsCString as a return type here seems to lead to a dependency on
6087 // some temporaries, which I did not expect. Is it a good idea that the default
6088 // operator+ behaviour constructs such strings? It is certainly useful as an
6089 // optimization, but this should be better done via an appropriately named
6090 // function rather than an operator.
6091 nsAutoCString
MakeColumnPairSelectionList(
6092 const nsLiteralCString
& aPlainColumnName
,
6093 const nsLiteralCString
& aLocaleAwareColumnName
,
6094 const nsLiteralCString
& aSortColumnAlias
, const bool aIsLocaleAware
) {
6095 return aPlainColumnName
+
6096 (aIsLocaleAware
? EmptyCString() : " as "_ns
+ aSortColumnAlias
) +
6097 ", "_ns
+ aLocaleAwareColumnName
+
6098 (aIsLocaleAware
? " as "_ns
+ aSortColumnAlias
: EmptyCString());
6101 constexpr bool IsIncreasingOrder(const IDBCursorDirection aDirection
) {
6102 MOZ_ASSERT(aDirection
== IDBCursorDirection::Next
||
6103 aDirection
== IDBCursorDirection::Nextunique
||
6104 aDirection
== IDBCursorDirection::Prev
||
6105 aDirection
== IDBCursorDirection::Prevunique
);
6107 return aDirection
== IDBCursorDirection::Next
||
6108 aDirection
== IDBCursorDirection::Nextunique
;
6111 constexpr bool IsUnique(const IDBCursorDirection aDirection
) {
6112 MOZ_ASSERT(aDirection
== IDBCursorDirection::Next
||
6113 aDirection
== IDBCursorDirection::Nextunique
||
6114 aDirection
== IDBCursorDirection::Prev
||
6115 aDirection
== IDBCursorDirection::Prevunique
);
6117 return aDirection
== IDBCursorDirection::Nextunique
||
6118 aDirection
== IDBCursorDirection::Prevunique
;
6121 // TODO: In principle, this could be constexpr, if operator+(nsLiteralCString,
6122 // nsLiteralCString) were constexpr and returned a literal type.
6123 nsAutoCString
MakeDirectionClause(const IDBCursorDirection aDirection
) {
6124 return " ORDER BY "_ns
+ kColumnNameKey
+
6125 (IsIncreasingOrder(aDirection
) ? " ASC"_ns
: " DESC"_ns
);
6128 enum struct ComparisonOperator
{
6136 constexpr nsLiteralCString
GetComparisonOperatorString(
6137 const ComparisonOperator aComparisonOperator
) {
6138 switch (aComparisonOperator
) {
6139 case ComparisonOperator::LessThan
:
6141 case ComparisonOperator::LessOrEquals
:
6143 case ComparisonOperator::Equals
:
6145 case ComparisonOperator::GreaterThan
:
6147 case ComparisonOperator::GreaterOrEquals
:
6151 // TODO: This is just to silence the "control reaches end of non-void
6152 // function" warning. Cannot use MOZ_CRASH in a constexpr function,
6157 nsAutoCString
GetKeyClause(const nsACString
& aColumnName
,
6158 const ComparisonOperator aComparisonOperator
,
6159 const nsLiteralCString
& aStmtParamName
) {
6160 return aColumnName
+ " "_ns
+
6161 GetComparisonOperatorString(aComparisonOperator
) + " :"_ns
+
6165 nsAutoCString
GetSortKeyClause(const ComparisonOperator aComparisonOperator
,
6166 const nsLiteralCString
& aStmtParamName
) {
6167 return GetKeyClause(kColumnNameAliasSortKey
, aComparisonOperator
,
6171 template <IDBCursorType CursorType
>
6172 struct PopulateResponseHelper
;
6174 struct CommonPopulateResponseHelper
{
6175 explicit CommonPopulateResponseHelper(
6176 const TransactionDatabaseOperationBase
& aOp
)
6179 nsresult
GetKeys(mozIStorageStatement
* const aStmt
,
6180 Key
* const aOptOutSortKey
) {
6181 QM_TRY(MOZ_TO_RESULT(GetCommonKeys(aStmt
)));
6183 if (aOptOutSortKey
) {
6184 *aOptOutSortKey
= mPosition
;
6190 nsresult
GetCommonKeys(mozIStorageStatement
* const aStmt
) {
6191 MOZ_ASSERT(mPosition
.IsUnset());
6193 QM_TRY(MOZ_TO_RESULT(mPosition
.SetFromStatement(aStmt
, 0)));
6195 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
6196 "PRELOAD: Populating response with key %s", "Populating%.0s",
6197 IDB_LOG_ID_STRING(mOp
.BackgroundChildLoggingId()),
6198 mOp
.TransactionLoggingSerialNumber(), mOp
.LoggingSerialNumber(),
6199 mPosition
.GetBuffer().get());
6204 template <typename Response
>
6205 void FillKeys(Response
& aResponse
) {
6206 MOZ_ASSERT(!mPosition
.IsUnset());
6207 aResponse
.key() = std::move(mPosition
);
6210 template <typename Response
>
6211 static size_t GetKeySize(const Response
& aResponse
) {
6212 return aResponse
.key().GetBuffer().Length();
6216 const Key
& GetPosition() const { return mPosition
; }
6219 const TransactionDatabaseOperationBase
& mOp
;
6223 struct IndexPopulateResponseHelper
: CommonPopulateResponseHelper
{
6224 using CommonPopulateResponseHelper::CommonPopulateResponseHelper
;
6226 nsresult
GetKeys(mozIStorageStatement
* const aStmt
,
6227 Key
* const aOptOutSortKey
) {
6228 MOZ_ASSERT(mLocaleAwarePosition
.IsUnset());
6229 MOZ_ASSERT(mObjectStorePosition
.IsUnset());
6231 QM_TRY(MOZ_TO_RESULT(CommonPopulateResponseHelper::GetCommonKeys(aStmt
)));
6233 QM_TRY(MOZ_TO_RESULT(mLocaleAwarePosition
.SetFromStatement(aStmt
, 1)));
6235 QM_TRY(MOZ_TO_RESULT(mObjectStorePosition
.SetFromStatement(aStmt
, 2)));
6237 if (aOptOutSortKey
) {
6239 mLocaleAwarePosition
.IsUnset() ? GetPosition() : mLocaleAwarePosition
;
6245 template <typename Response
>
6246 void FillKeys(Response
& aResponse
) {
6247 MOZ_ASSERT(!mLocaleAwarePosition
.IsUnset());
6248 MOZ_ASSERT(!mObjectStorePosition
.IsUnset());
6250 CommonPopulateResponseHelper::FillKeys(aResponse
);
6251 aResponse
.sortKey() = std::move(mLocaleAwarePosition
);
6252 aResponse
.objectKey() = std::move(mObjectStorePosition
);
6255 template <typename Response
>
6256 static size_t GetKeySize(Response
& aResponse
) {
6257 return CommonPopulateResponseHelper::GetKeySize(aResponse
) +
6258 aResponse
.sortKey().GetBuffer().Length() +
6259 aResponse
.objectKey().GetBuffer().Length();
6263 Key mLocaleAwarePosition
, mObjectStorePosition
;
6266 struct KeyPopulateResponseHelper
{
6267 static constexpr nsresult
MaybeGetCloneInfo(
6268 mozIStorageStatement
* const /*aStmt*/, const CursorBase
& /*aCursor*/) {
6272 template <typename Response
>
6273 static constexpr void MaybeFillCloneInfo(Response
& /*aResponse*/,
6274 FilesArray
* const /*aFiles*/) {}
6276 template <typename Response
>
6277 static constexpr size_t MaybeGetCloneInfoSize(const Response
& /*aResponse*/) {
6282 template <bool StatementHasIndexKeyBindings
>
6283 struct ValuePopulateResponseHelper
{
6284 nsresult
MaybeGetCloneInfo(mozIStorageStatement
* const aStmt
,
6285 const ValueCursorBase
& aCursor
) {
6286 constexpr auto offset
= StatementHasIndexKeyBindings
? 2 : 0;
6288 QM_TRY_UNWRAP(auto cloneInfo
,
6289 GetStructuredCloneReadInfoFromStatement(
6290 aStmt
, 2 + offset
, 1 + offset
, *aCursor
.mFileManager
));
6292 mCloneInfo
.init(std::move(cloneInfo
));
6294 if (mCloneInfo
->HasPreprocessInfo()) {
6295 IDB_WARNING("Preprocessing for cursors not yet implemented!");
6296 return NS_ERROR_NOT_IMPLEMENTED
;
6302 template <typename Response
>
6303 void MaybeFillCloneInfo(Response
& aResponse
, FilesArray
* const aFiles
) {
6304 auto cloneInfo
= mCloneInfo
.release();
6305 aResponse
.cloneInfo().data().data
= cloneInfo
.ReleaseData();
6306 aFiles
->AppendElement(cloneInfo
.ReleaseFiles());
6309 template <typename Response
>
6310 static size_t MaybeGetCloneInfoSize(const Response
& aResponse
) {
6311 return aResponse
.cloneInfo().data().data
.Size();
6315 LazyInitializedOnceEarlyDestructible
<const StructuredCloneReadInfoParent
>
6320 struct PopulateResponseHelper
<IDBCursorType::ObjectStore
>
6321 : ValuePopulateResponseHelper
<false>, CommonPopulateResponseHelper
{
6322 using CommonPopulateResponseHelper::CommonPopulateResponseHelper
;
6324 static auto& GetTypedResponse(CursorResponse
* const aResponse
) {
6325 return aResponse
->get_ArrayOfObjectStoreCursorResponse();
6330 struct PopulateResponseHelper
<IDBCursorType::ObjectStoreKey
>
6331 : KeyPopulateResponseHelper
, CommonPopulateResponseHelper
{
6332 using CommonPopulateResponseHelper::CommonPopulateResponseHelper
;
6334 static auto& GetTypedResponse(CursorResponse
* const aResponse
) {
6335 return aResponse
->get_ArrayOfObjectStoreKeyCursorResponse();
6340 struct PopulateResponseHelper
<IDBCursorType::Index
>
6341 : ValuePopulateResponseHelper
<true>, IndexPopulateResponseHelper
{
6342 using IndexPopulateResponseHelper::IndexPopulateResponseHelper
;
6344 static auto& GetTypedResponse(CursorResponse
* const aResponse
) {
6345 return aResponse
->get_ArrayOfIndexCursorResponse();
6350 struct PopulateResponseHelper
<IDBCursorType::IndexKey
>
6351 : KeyPopulateResponseHelper
, IndexPopulateResponseHelper
{
6352 using IndexPopulateResponseHelper::IndexPopulateResponseHelper
;
6354 static auto& GetTypedResponse(CursorResponse
* const aResponse
) {
6355 return aResponse
->get_ArrayOfIndexKeyCursorResponse();
6359 nsresult
DispatchAndReturnFileReferences(
6360 PersistenceType aPersistenceType
, const nsACString
& aOrigin
,
6361 const nsAString
& aDatabaseName
, const int64_t aFileId
,
6362 int32_t* const aMemRefCnt
, int32_t* const aDBRefCnt
, bool* const aResult
) {
6363 AssertIsOnBackgroundThread();
6364 MOZ_ASSERT(aMemRefCnt
);
6365 MOZ_ASSERT(aDBRefCnt
);
6366 MOZ_ASSERT(aResult
);
6372 mozilla::Monitor monitor
MOZ_ANNOTATED(__func__
);
6373 bool waiting
= true;
6376 AssertIsOnIOThread();
6379 IndexedDatabaseManager
* const mgr
= IndexedDatabaseManager::Get();
6382 const SafeRefPtr
<DatabaseFileManager
> fileManager
=
6383 mgr
->GetFileManager(aPersistenceType
, aOrigin
, aDatabaseName
);
6386 const SafeRefPtr
<DatabaseFileInfo
> fileInfo
=
6387 fileManager
->GetFileInfo(aFileId
);
6390 fileInfo
->GetReferences(aMemRefCnt
, aDBRefCnt
);
6392 if (*aMemRefCnt
!= -1) {
6393 // We added an extra temp ref, so account for that accordingly.
6402 mozilla::MonitorAutoLock
lock(monitor
);
6403 MOZ_ASSERT(waiting
);
6409 QuotaManager
* const quotaManager
= QuotaManager::Get();
6410 MOZ_ASSERT(quotaManager
);
6412 // XXX can't we simply use NS_DispatchAndSpinEventLoopUntilComplete instead of
6414 QM_TRY(MOZ_TO_RESULT(quotaManager
->IOThread()->Dispatch(
6415 NS_NewRunnableFunction("GetFileReferences", std::move(lambda
)),
6416 NS_DISPATCH_NORMAL
)));
6418 mozilla::MonitorAutoLock
autolock(monitor
);
6426 class DeserializeIndexValueHelper final
: public Runnable
{
6428 DeserializeIndexValueHelper(int64_t aIndexID
, const KeyPath
& aKeyPath
,
6429 bool aMultiEntry
, const nsACString
& aLocale
,
6430 StructuredCloneReadInfoParent
& aCloneReadInfo
,
6431 nsTArray
<IndexUpdateInfo
>& aUpdateInfoArray
)
6432 : Runnable("DeserializeIndexValueHelper"),
6433 mMonitor("DeserializeIndexValueHelper::mMonitor"),
6436 mMultiEntry(aMultiEntry
),
6438 mCloneReadInfo(aCloneReadInfo
),
6439 mUpdateInfoArray(aUpdateInfoArray
),
6440 mStatus(NS_ERROR_FAILURE
) {}
6442 nsresult
DispatchAndWait() {
6443 // FIXME(Bug 1637530) Re-enable optimization using a non-system-principaled
6446 // We don't need to go to the main-thread and use the sandbox. Let's create
6447 // the updateInfo data here.
6448 if (!mCloneReadInfo
.Data().Size()) {
6452 JS::Rooted
<JS::Value
> value(jsapi
.cx());
6453 value
.setUndefined();
6456 IDBObjectStore::AppendIndexUpdateInfo(
6457 mIndexID
, mKeyPath
, mMultiEntry
, &mUpdateInfoArray
,
6458 /* aAutoIncrementedObjectStoreKeyPath */ VoidString(), &rv
);
6459 return rv
.Failed() ? rv
.StealNSResult() : NS_OK
;
6463 // The operation will continue on the main-thread.
6465 MOZ_ASSERT(!(mCloneReadInfo
.Data().Size() % sizeof(uint64_t)));
6467 MonitorAutoLock
lock(mMonitor
);
6469 RefPtr
<Runnable
> self
= this;
6470 QM_TRY(MOZ_TO_RESULT(SchedulerGroup::Dispatch(self
.forget())));
6478 MOZ_ASSERT(NS_IsMainThread());
6482 JSContext
* const cx
= jsapi
.cx();
6484 JS::Rooted
<JSObject
*> global(cx
, GetSandbox(cx
));
6486 QM_TRY(OkIf(global
), NS_OK
,
6487 [this](const NotOk
) { OperationCompleted(NS_ERROR_FAILURE
); });
6489 const JSAutoRealm
ar(cx
, global
);
6491 JS::Rooted
<JS::Value
> value(cx
);
6492 QM_TRY(MOZ_TO_RESULT(DeserializeIndexValue(cx
, &value
)), NS_OK
,
6493 [this](const nsresult rv
) { OperationCompleted(rv
); });
6495 ErrorResult errorResult
;
6496 IDBObjectStore::AppendIndexUpdateInfo(
6497 mIndexID
, mKeyPath
, mMultiEntry
, mLocale
, cx
, value
, &mUpdateInfoArray
,
6498 /* aAutoIncrementedObjectStoreKeyPath */ VoidString(), &errorResult
);
6499 QM_TRY(OkIf(!errorResult
.Failed()), NS_OK
,
6500 ([this, &errorResult
](const NotOk
) {
6501 OperationCompleted(errorResult
.StealNSResult());
6504 OperationCompleted(NS_OK
);
6509 nsresult
DeserializeIndexValue(JSContext
* aCx
,
6510 JS::MutableHandle
<JS::Value
> aValue
) {
6511 static const JSStructuredCloneCallbacks callbacks
= {
6512 StructuredCloneReadCallback
<StructuredCloneReadInfoParent
>,
6521 if (!JS_ReadStructuredClone(
6522 aCx
, mCloneReadInfo
.Data(), JS_STRUCTURED_CLONE_VERSION
,
6523 JS::StructuredCloneScope::DifferentProcessForIndexedDB
, aValue
,
6524 JS::CloneDataPolicy(), &callbacks
, &mCloneReadInfo
)) {
6525 return NS_ERROR_DOM_DATA_CLONE_ERR
;
6531 void OperationCompleted(nsresult aStatus
) {
6534 MonitorAutoLock
lock(mMonitor
);
6538 Monitor mMonitor MOZ_UNANNOTATED
;
6540 const int64_t mIndexID
;
6541 const KeyPath
& mKeyPath
;
6542 const bool mMultiEntry
;
6543 const nsCString mLocale
;
6544 StructuredCloneReadInfoParent
& mCloneReadInfo
;
6545 nsTArray
<IndexUpdateInfo
>& mUpdateInfoArray
;
6549 auto DeserializeIndexValueToUpdateInfos(
6550 int64_t aIndexID
, const KeyPath
& aKeyPath
, bool aMultiEntry
,
6551 const nsACString
& aLocale
, StructuredCloneReadInfoParent
& aCloneReadInfo
) {
6552 MOZ_ASSERT(!NS_IsMainThread());
6554 using ArrayType
= AutoTArray
<IndexUpdateInfo
, 32>;
6555 using ResultType
= Result
<ArrayType
, nsresult
>;
6557 ArrayType updateInfoArray
;
6558 const auto helper
= MakeRefPtr
<DeserializeIndexValueHelper
>(
6559 aIndexID
, aKeyPath
, aMultiEntry
, aLocale
, aCloneReadInfo
,
6561 const nsresult rv
= helper
->DispatchAndWait();
6562 return NS_FAILED(rv
) ? Err(rv
) : ResultType
{std::move(updateInfoArray
)};
6566 const Maybe
<CachingDatabaseConnection::BorrowedStatement
>& aMaybeStmt
) {
6567 return aMaybeStmt
.isSome();
6570 already_AddRefed
<nsIThreadPool
> MakeConnectionIOTarget() {
6571 nsCOMPtr
<nsIThreadPool
> threadPool
= new nsThreadPool();
6573 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetThreadLimit(kMaxConnectionThreadCount
));
6575 MOZ_ALWAYS_SUCCEEDS(
6576 threadPool
->SetIdleThreadLimit(kMaxIdleConnectionThreadCount
));
6578 MOZ_ALWAYS_SUCCEEDS(
6579 threadPool
->SetIdleThreadTimeout(kConnectionThreadIdleMS
));
6581 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetName("IndexedDB IO"_ns
));
6583 return threadPool
.forget();
6588 /*******************************************************************************
6589 * Exported functions
6590 ******************************************************************************/
6592 already_AddRefed
<PBackgroundIDBFactoryParent
> AllocPBackgroundIDBFactoryParent(
6593 const LoggingInfo
& aLoggingInfo
, const nsACString
& aSystemLocale
) {
6594 AssertIsOnBackgroundThread();
6596 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
6600 if (NS_AUUF_OR_WARN_IF(!aLoggingInfo
.nextTransactionSerialNumber()) ||
6602 !aLoggingInfo
.nextVersionChangeTransactionSerialNumber()) ||
6603 NS_AUUF_OR_WARN_IF(!aLoggingInfo
.nextRequestSerialNumber())) {
6607 SafeRefPtr
<Factory
> actor
= Factory::Create(aLoggingInfo
, aSystemLocale
);
6610 return actor
.forget();
6613 bool RecvPBackgroundIDBFactoryConstructor(
6614 PBackgroundIDBFactoryParent
* aActor
, const LoggingInfo
& /* aLoggingInfo */,
6615 const nsACString
& /* aSystemLocale */) {
6616 AssertIsOnBackgroundThread();
6618 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
6623 PBackgroundIndexedDBUtilsParent
* AllocPBackgroundIndexedDBUtilsParent() {
6624 AssertIsOnBackgroundThread();
6626 RefPtr
<Utils
> actor
= new Utils();
6628 return actor
.forget().take();
6631 bool DeallocPBackgroundIndexedDBUtilsParent(
6632 PBackgroundIndexedDBUtilsParent
* aActor
) {
6633 AssertIsOnBackgroundThread();
6636 RefPtr
<Utils
> actor
= dont_AddRef(static_cast<Utils
*>(aActor
));
6640 bool RecvFlushPendingFileDeletions() {
6641 AssertIsOnBackgroundThread();
6643 if (QuotaClient
* quotaClient
= QuotaClient::GetInstance()) {
6644 QM_WARNONLY_TRY(QM_TO_RESULT(quotaClient
->FlushPendingFileDeletions()));
6650 RefPtr
<mozilla::dom::quota::Client
> CreateQuotaClient() {
6651 AssertIsOnBackgroundThread();
6653 return MakeRefPtr
<QuotaClient
>();
6656 nsresult
DatabaseFileManager::AsyncDeleteFile(int64_t aFileId
) {
6657 AssertIsOnBackgroundThread();
6658 MOZ_ASSERT(!mFileInfos
.Contains(aFileId
));
6660 QuotaClient
* quotaClient
= QuotaClient::GetInstance();
6662 QM_TRY(MOZ_TO_RESULT(quotaClient
->AsyncDeleteFile(this, aFileId
)));
6668 /*******************************************************************************
6669 * DatabaseConnection implementation
6670 ******************************************************************************/
6672 DatabaseConnection::DatabaseConnection(
6673 MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>> aStorageConnection
,
6674 MovingNotNull
<SafeRefPtr
<DatabaseFileManager
>> aFileManager
)
6675 : CachingDatabaseConnection(std::move(aStorageConnection
)),
6676 mFileManager(std::move(aFileManager
)),
6677 mInReadTransaction(false),
6678 mInWriteTransaction(false)
6681 mDEBUGSavepointCount(0)
6684 AssertIsOnConnectionThread();
6685 MOZ_ASSERT(mFileManager
);
6688 DatabaseConnection::~DatabaseConnection() {
6689 MOZ_ASSERT(!mFileManager
);
6690 MOZ_ASSERT(!mUpdateRefcountFunction
);
6691 MOZ_DIAGNOSTIC_ASSERT(!mInWriteTransaction
);
6692 MOZ_ASSERT(!mDEBUGSavepointCount
);
6695 nsresult
DatabaseConnection::Init() {
6696 AssertIsOnConnectionThread();
6697 MOZ_ASSERT(!mInReadTransaction
);
6698 MOZ_ASSERT(!mInWriteTransaction
);
6700 QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("BEGIN;"_ns
)));
6702 mInReadTransaction
= true;
6707 nsresult
DatabaseConnection::BeginWriteTransaction() {
6708 AssertIsOnConnectionThread();
6709 MOZ_ASSERT(HasStorageConnection());
6710 MOZ_ASSERT(mInReadTransaction
);
6711 MOZ_ASSERT(!mInWriteTransaction
);
6713 AUTO_PROFILER_LABEL("DatabaseConnection::BeginWriteTransaction", DOM
);
6715 // Release our read locks.
6716 QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("ROLLBACK;"_ns
)));
6718 mInReadTransaction
= false;
6720 if (!mUpdateRefcountFunction
) {
6721 MOZ_ASSERT(mFileManager
);
6723 RefPtr
<UpdateRefcountFunction
> function
=
6724 new UpdateRefcountFunction(this, **mFileManager
);
6726 QM_TRY(MOZ_TO_RESULT(MutableStorageConnection().CreateFunction(
6727 "update_refcount"_ns
,
6728 /* aNumArguments */ 2, function
)));
6730 mUpdateRefcountFunction
= std::move(function
);
6733 // This one cannot obviously use ExecuteCachedStatement because of the custom
6734 // error handling for Execute only. If only Execute can produce
6735 // NS_ERROR_STORAGE_BUSY, we could actually use ExecuteCachedStatement and
6737 QM_TRY_INSPECT(const auto& beginStmt
,
6738 BorrowCachedStatement("BEGIN IMMEDIATE;"_ns
));
6740 QM_TRY(QM_OR_ELSE_WARN_IF(
6742 MOZ_TO_RESULT(beginStmt
->Execute()),
6744 IsSpecificError
<NS_ERROR_STORAGE_BUSY
>,
6746 ([&beginStmt
](nsresult rv
) {
6748 "Received NS_ERROR_STORAGE_BUSY when attempting to start write "
6749 "transaction, retrying for up to 10 seconds");
6751 // Another thread must be using the database. Wait up to 10 seconds
6752 // for that to complete.
6753 const TimeStamp start
= TimeStamp::NowLoRes();
6756 PR_Sleep(PR_MillisecondsToInterval(100));
6758 rv
= beginStmt
->Execute();
6759 if (rv
!= NS_ERROR_STORAGE_BUSY
||
6760 TimeStamp::NowLoRes() - start
> TimeDuration::FromSeconds(10)) {
6765 return MOZ_TO_RESULT(rv
);
6768 mInWriteTransaction
= true;
6773 nsresult
DatabaseConnection::CommitWriteTransaction() {
6774 AssertIsOnConnectionThread();
6775 MOZ_ASSERT(HasStorageConnection());
6776 MOZ_ASSERT(!mInReadTransaction
);
6777 MOZ_ASSERT(mInWriteTransaction
);
6779 AUTO_PROFILER_LABEL("DatabaseConnection::CommitWriteTransaction", DOM
);
6781 QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("COMMIT;"_ns
)));
6783 mInWriteTransaction
= false;
6787 void DatabaseConnection::RollbackWriteTransaction() {
6788 AssertIsOnConnectionThread();
6789 MOZ_ASSERT(!mInReadTransaction
);
6790 MOZ_DIAGNOSTIC_ASSERT(HasStorageConnection());
6792 AUTO_PROFILER_LABEL("DatabaseConnection::RollbackWriteTransaction", DOM
);
6794 if (!mInWriteTransaction
) {
6799 BorrowCachedStatement("ROLLBACK;"_ns
)
6800 .andThen([&self
= *this](const auto& stmt
) -> Result
<Ok
, nsresult
> {
6801 // This may fail if SQLite already rolled back the transaction
6802 // so ignore any errors.
6804 // XXX ROLLBACK can fail quite normmally if a previous statement
6805 // failed to execute successfully so SQLite rolled back the
6806 // transaction already. However, if it failed because of some other
6807 // reason, we could try to close the connection.
6808 Unused
<< stmt
->Execute();
6810 self
.mInWriteTransaction
= false;
6815 void DatabaseConnection::FinishWriteTransaction() {
6816 AssertIsOnConnectionThread();
6817 MOZ_ASSERT(HasStorageConnection());
6818 MOZ_ASSERT(!mInReadTransaction
);
6819 MOZ_ASSERT(!mInWriteTransaction
);
6821 AUTO_PROFILER_LABEL("DatabaseConnection::FinishWriteTransaction", DOM
);
6823 if (mUpdateRefcountFunction
) {
6824 mUpdateRefcountFunction
->Reset();
6827 QM_WARNONLY_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("BEGIN;"_ns
))
6828 .andThen([&](const auto) -> Result
<Ok
, nsresult
> {
6829 mInReadTransaction
= true;
6834 nsresult
DatabaseConnection::StartSavepoint() {
6835 AssertIsOnConnectionThread();
6836 MOZ_ASSERT(HasStorageConnection());
6837 MOZ_ASSERT(mUpdateRefcountFunction
);
6838 MOZ_ASSERT(mInWriteTransaction
);
6840 AUTO_PROFILER_LABEL("DatabaseConnection::StartSavepoint", DOM
);
6842 QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement(SAVEPOINT_CLAUSE
)));
6844 mUpdateRefcountFunction
->StartSavepoint();
6847 MOZ_ASSERT(mDEBUGSavepointCount
< UINT32_MAX
);
6848 mDEBUGSavepointCount
++;
6854 nsresult
DatabaseConnection::ReleaseSavepoint() {
6855 AssertIsOnConnectionThread();
6856 MOZ_ASSERT(HasStorageConnection());
6857 MOZ_ASSERT(mUpdateRefcountFunction
);
6858 MOZ_ASSERT(mInWriteTransaction
);
6860 AUTO_PROFILER_LABEL("DatabaseConnection::ReleaseSavepoint", DOM
);
6862 QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("RELEASE "_ns SAVEPOINT_CLAUSE
)));
6864 mUpdateRefcountFunction
->ReleaseSavepoint();
6867 MOZ_ASSERT(mDEBUGSavepointCount
);
6868 mDEBUGSavepointCount
--;
6874 nsresult
DatabaseConnection::RollbackSavepoint() {
6875 AssertIsOnConnectionThread();
6876 MOZ_ASSERT(HasStorageConnection());
6877 MOZ_ASSERT(mUpdateRefcountFunction
);
6878 MOZ_ASSERT(mInWriteTransaction
);
6880 AUTO_PROFILER_LABEL("DatabaseConnection::RollbackSavepoint", DOM
);
6883 MOZ_ASSERT(mDEBUGSavepointCount
);
6884 mDEBUGSavepointCount
--;
6887 mUpdateRefcountFunction
->RollbackSavepoint();
6889 QM_TRY_INSPECT(const auto& stmt
,
6890 BorrowCachedStatement("ROLLBACK TO "_ns SAVEPOINT_CLAUSE
));
6892 // This may fail if SQLite already rolled back the savepoint so ignore any
6894 Unused
<< stmt
->Execute();
6899 nsresult
DatabaseConnection::CheckpointInternal(CheckpointMode aMode
) {
6900 AssertIsOnConnectionThread();
6901 MOZ_ASSERT(!mInReadTransaction
);
6902 MOZ_ASSERT(!mInWriteTransaction
);
6904 AUTO_PROFILER_LABEL("DatabaseConnection::CheckpointInternal", DOM
);
6906 nsAutoCString stmtString
;
6907 stmtString
.AssignLiteral("PRAGMA wal_checkpoint(");
6910 case CheckpointMode::Full
:
6911 // Ensures that the database is completely checkpointed and flushed to
6913 stmtString
.AppendLiteral("FULL");
6916 case CheckpointMode::Restart
:
6917 // Like Full, but also ensures that the next write will start overwriting
6918 // the existing WAL file rather than letting the WAL file grow.
6919 stmtString
.AppendLiteral("RESTART");
6922 case CheckpointMode::Truncate
:
6923 // Like Restart but also truncates the existing WAL file.
6924 stmtString
.AppendLiteral("TRUNCATE");
6928 MOZ_CRASH("Unknown CheckpointMode!");
6931 stmtString
.AppendLiteral(");");
6933 QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement(stmtString
)));
6938 void DatabaseConnection::DoIdleProcessing(bool aNeedsCheckpoint
,
6939 const Atomic
<bool>& aInterrupted
) {
6940 AssertIsOnConnectionThread();
6941 MOZ_ASSERT(mInReadTransaction
);
6942 MOZ_ASSERT(!mInWriteTransaction
);
6944 AUTO_PROFILER_LABEL("DatabaseConnection::DoIdleProcessing", DOM
);
6946 CachingDatabaseConnection::CachedStatement freelistStmt
;
6947 const uint32_t freelistCount
= [this, &freelistStmt
] {
6948 QM_TRY_RETURN(GetFreelistCount(freelistStmt
), 0u);
6951 CachedStatement rollbackStmt
;
6952 CachedStatement beginStmt
;
6953 if (aNeedsCheckpoint
|| freelistCount
) {
6954 QM_TRY_UNWRAP(rollbackStmt
, GetCachedStatement("ROLLBACK;"_ns
), QM_VOID
);
6955 QM_TRY_UNWRAP(beginStmt
, GetCachedStatement("BEGIN;"_ns
), QM_VOID
);
6957 // Release the connection's normal transaction. It's possible that it could
6958 // fail, but that isn't a problem here.
6959 Unused
<< rollbackStmt
.Borrow()->Execute();
6961 mInReadTransaction
= false;
6964 const bool freedSomePages
=
6965 freelistCount
&& [this, &freelistStmt
, &rollbackStmt
, freelistCount
,
6966 aNeedsCheckpoint
, &aInterrupted
] {
6967 // Warn in case of an error, but do not propagate it. Just indicate we
6968 // didn't free any pages.
6971 ReclaimFreePagesWhileIdle(freelistStmt
, rollbackStmt
, freelistCount
,
6972 aNeedsCheckpoint
, aInterrupted
),
6975 // Make sure we didn't leave a transaction running.
6976 MOZ_ASSERT(!mInReadTransaction
);
6977 MOZ_ASSERT(!mInWriteTransaction
);
6982 // Truncate the WAL if we were asked to or if we managed to free some space.
6983 if (aNeedsCheckpoint
|| freedSomePages
) {
6984 QM_WARNONLY_TRY(QM_TO_RESULT(CheckpointInternal(CheckpointMode::Truncate
)));
6987 // Finally try to restart the read transaction if we rolled it back earlier.
6990 MOZ_TO_RESULT(beginStmt
.Borrow()->Execute())
6991 .andThen([&self
= *this](const Ok
) -> Result
<Ok
, nsresult
> {
6992 self
.mInReadTransaction
= true;
6998 Result
<bool, nsresult
> DatabaseConnection::ReclaimFreePagesWhileIdle(
6999 CachedStatement
& aFreelistStatement
, CachedStatement
& aRollbackStatement
,
7000 uint32_t aFreelistCount
, bool aNeedsCheckpoint
,
7001 const Atomic
<bool>& aInterrupted
) {
7002 AssertIsOnConnectionThread();
7003 MOZ_ASSERT(aFreelistStatement
);
7004 MOZ_ASSERT(aRollbackStatement
);
7005 MOZ_ASSERT(aFreelistCount
);
7006 MOZ_ASSERT(!mInReadTransaction
);
7007 MOZ_ASSERT(!mInWriteTransaction
);
7009 AUTO_PROFILER_LABEL("DatabaseConnection::ReclaimFreePagesWhileIdle", DOM
);
7011 uint32_t pauseOnConnectionThreadMs
= StaticPrefs::
7012 dom_indexedDB_connectionIdleMaintenance_pauseOnConnectionThreadMs();
7013 if (pauseOnConnectionThreadMs
> 0) {
7014 PR_Sleep(PR_MillisecondsToInterval(pauseOnConnectionThreadMs
));
7017 // Make sure we don't keep working if anything else needs this thread.
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;
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 uint64_t previousFreelistCount
= (uint64_t)aFreelistCount
+ 1;
7065 QM_TRY(CollectWhile(
7066 [&aFreelistCount
, &previousFreelistCount
,
7067 &aInterrupted
]() -> Result
<bool, nsresult
> {
7069 // On interrupt, abort and roll back this transaction. It's ok
7070 // if we never make progress here because the idle service
7071 // should eventually reclaim this space.
7074 // If we were not able to free anything, we might either see
7075 // a DB that has no auto-vacuum support at all or some other
7076 // (hopefully temporary) condition that prevents vacuum from
7077 // working. Just carry on in non-DEBUG.
7078 bool madeProgress
= previousFreelistCount
!= aFreelistCount
;
7079 previousFreelistCount
= aFreelistCount
;
7080 MOZ_ASSERT(madeProgress
);
7081 QM_WARNONLY_TRY(MOZ_TO_RESULT(madeProgress
));
7082 return madeProgress
&& (aFreelistCount
!= 0);
7084 [&aFreelistStatement
, &aFreelistCount
, &incrementalVacuumStmt
,
7085 &freedSomePages
, this]() -> mozilla::Result
<Ok
, nsresult
> {
7086 QM_TRY(MOZ_TO_RESULT(incrementalVacuumStmt
.Borrow()->Execute()));
7088 freedSomePages
= true;
7090 QM_TRY_UNWRAP(aFreelistCount
,
7091 GetFreelistCount(aFreelistStatement
));
7095 .andThen([&commitStmt
, &freedSomePages
, &aInterrupted
, &rollback
,
7096 this](Ok
) -> Result
<Ok
, nsresult
> {
7099 freedSomePages
= false;
7102 if (freedSomePages
) {
7103 // Commit the write transaction.
7104 QM_TRY(MOZ_TO_RESULT(commitStmt
.Borrow()->Execute()),
7106 [](const auto&) { NS_WARNING("Failed to commit!"); });
7108 mInWriteTransaction
= false;
7113 QM_PROPAGATE
, rollback
);
7115 return freedSomePages
;
7118 Result
<uint32_t, nsresult
> DatabaseConnection::GetFreelistCount(
7119 CachedStatement
& aCachedStatement
) {
7120 AssertIsOnConnectionThread();
7122 AUTO_PROFILER_LABEL("DatabaseConnection::GetFreelistCount", DOM
);
7124 if (!aCachedStatement
) {
7125 QM_TRY_UNWRAP(aCachedStatement
,
7126 GetCachedStatement("PRAGMA freelist_count;"_ns
));
7129 const auto borrowedStatement
= aCachedStatement
.Borrow();
7131 QM_TRY_UNWRAP(const DebugOnly
<bool> hasResult
,
7132 MOZ_TO_RESULT_INVOKE_MEMBER(&*borrowedStatement
, ExecuteStep
));
7134 MOZ_ASSERT(hasResult
);
7136 QM_TRY_INSPECT(const int32_t& freelistCount
,
7137 MOZ_TO_RESULT_INVOKE_MEMBER(*borrowedStatement
, GetInt32
, 0));
7139 MOZ_ASSERT(freelistCount
>= 0);
7141 return uint32_t(freelistCount
);
7144 void DatabaseConnection::Close() {
7145 AssertIsOnConnectionThread();
7146 MOZ_ASSERT(!mDEBUGSavepointCount
);
7147 MOZ_DIAGNOSTIC_ASSERT(!mInWriteTransaction
);
7149 AUTO_PROFILER_LABEL("DatabaseConnection::Close", DOM
);
7151 if (mUpdateRefcountFunction
) {
7152 MOZ_ALWAYS_SUCCEEDS(
7153 MutableStorageConnection().RemoveFunction("update_refcount"_ns
));
7154 mUpdateRefcountFunction
= nullptr;
7157 CachingDatabaseConnection::Close();
7159 mFileManager
.destroy();
7162 nsresult
DatabaseConnection::DisableQuotaChecks() {
7163 AssertIsOnConnectionThread();
7164 MOZ_ASSERT(HasStorageConnection());
7166 if (!mQuotaObject
) {
7167 MOZ_ASSERT(!mJournalQuotaObject
);
7169 QM_TRY(MOZ_TO_RESULT(MutableStorageConnection().GetQuotaObjects(
7170 getter_AddRefs(mQuotaObject
), getter_AddRefs(mJournalQuotaObject
))));
7172 MOZ_ASSERT(mQuotaObject
);
7173 MOZ_ASSERT(mJournalQuotaObject
);
7176 mQuotaObject
->DisableQuotaCheck();
7177 mJournalQuotaObject
->DisableQuotaCheck();
7182 void DatabaseConnection::EnableQuotaChecks() {
7183 AssertIsOnConnectionThread();
7184 if (!mQuotaObject
) {
7185 MOZ_ASSERT(!mJournalQuotaObject
);
7187 // DisableQuotaChecks failed earlier, so we don't need to enable quota
7192 MOZ_ASSERT(mJournalQuotaObject
);
7194 const RefPtr
<QuotaObject
> quotaObject
= std::move(mQuotaObject
);
7195 const RefPtr
<QuotaObject
> journalQuotaObject
= std::move(mJournalQuotaObject
);
7197 quotaObject
->EnableQuotaCheck();
7198 journalQuotaObject
->EnableQuotaCheck();
7200 QM_TRY_INSPECT(const int64_t& fileSize
, GetFileSize(quotaObject
->Path()),
7202 QM_TRY_INSPECT(const int64_t& journalFileSize
,
7203 GetFileSize(journalQuotaObject
->Path()), QM_VOID
);
7205 DebugOnly
<bool> result
= journalQuotaObject
->MaybeUpdateSize(
7206 journalFileSize
, /* aTruncate */ true);
7209 result
= quotaObject
->MaybeUpdateSize(fileSize
, /* aTruncate */ true);
7213 Result
<int64_t, nsresult
> DatabaseConnection::GetFileSize(
7214 const nsAString
& aPath
) {
7215 MOZ_ASSERT(!aPath
.IsEmpty());
7217 QM_TRY_INSPECT(const auto& file
, QM_NewLocalFile(aPath
));
7218 QM_TRY_INSPECT(const bool& exists
, MOZ_TO_RESULT_INVOKE_MEMBER(file
, Exists
));
7221 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(file
, GetFileSize
));
7227 DatabaseConnection::AutoSavepoint::AutoSavepoint()
7228 : mConnection(nullptr)
7231 mDEBUGTransaction(nullptr)
7234 MOZ_COUNT_CTOR(DatabaseConnection::AutoSavepoint
);
7237 DatabaseConnection::AutoSavepoint::~AutoSavepoint() {
7238 MOZ_COUNT_DTOR(DatabaseConnection::AutoSavepoint
);
7241 mConnection
->AssertIsOnConnectionThread();
7242 MOZ_ASSERT(mDEBUGTransaction
);
7244 mDEBUGTransaction
->GetMode() == IDBTransaction::Mode::ReadWrite
||
7245 mDEBUGTransaction
->GetMode() == IDBTransaction::Mode::ReadWriteFlush
||
7246 mDEBUGTransaction
->GetMode() == IDBTransaction::Mode::Cleanup
||
7247 mDEBUGTransaction
->GetMode() == IDBTransaction::Mode::VersionChange
);
7249 QM_WARNONLY_TRY(QM_TO_RESULT(mConnection
->RollbackSavepoint()));
7253 nsresult
DatabaseConnection::AutoSavepoint::Start(
7254 const TransactionBase
& aTransaction
) {
7255 MOZ_ASSERT(aTransaction
.GetMode() == IDBTransaction::Mode::ReadWrite
||
7256 aTransaction
.GetMode() == IDBTransaction::Mode::ReadWriteFlush
||
7257 aTransaction
.GetMode() == IDBTransaction::Mode::Cleanup
||
7258 aTransaction
.GetMode() == IDBTransaction::Mode::VersionChange
);
7260 DatabaseConnection
* connection
= aTransaction
.GetDatabase().GetConnection();
7261 MOZ_ASSERT(connection
);
7262 connection
->AssertIsOnConnectionThread();
7264 // The previous operation failed to begin a write transaction and the
7265 // following opertion jumped to the connection thread before the previous
7266 // operation has updated its failure to the transaction.
7267 if (!connection
->GetUpdateRefcountFunction()) {
7269 "The connection was closed because the previous operation "
7271 return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
;
7274 MOZ_ASSERT(!mConnection
);
7275 MOZ_ASSERT(!mDEBUGTransaction
);
7277 QM_TRY(MOZ_TO_RESULT(connection
->StartSavepoint()));
7279 mConnection
= connection
;
7281 mDEBUGTransaction
= &aTransaction
;
7287 nsresult
DatabaseConnection::AutoSavepoint::Commit() {
7288 MOZ_ASSERT(mConnection
);
7289 mConnection
->AssertIsOnConnectionThread();
7290 MOZ_ASSERT(mDEBUGTransaction
);
7292 QM_TRY(MOZ_TO_RESULT(mConnection
->ReleaseSavepoint()));
7294 mConnection
= nullptr;
7296 mDEBUGTransaction
= nullptr;
7302 DatabaseConnection::UpdateRefcountFunction::UpdateRefcountFunction(
7303 DatabaseConnection
* const aConnection
, DatabaseFileManager
& aFileManager
)
7304 : mConnection(aConnection
),
7305 mFileManager(aFileManager
),
7306 mInSavepoint(false) {
7307 MOZ_ASSERT(aConnection
);
7308 aConnection
->AssertIsOnConnectionThread();
7311 nsresult
DatabaseConnection::UpdateRefcountFunction::WillCommit() {
7312 MOZ_ASSERT(mConnection
);
7313 mConnection
->AssertIsOnConnectionThread();
7314 MOZ_ASSERT(mConnection
->HasStorageConnection());
7316 AUTO_PROFILER_LABEL("DatabaseConnection::UpdateRefcountFunction::WillCommit",
7319 // The parameter names are not used, parameters are bound by index
7320 // only locally in the same function.
7322 [updateStatement
= LazyStatement
{*mConnection
,
7324 "SET refcount = refcount + :delta "
7325 "WHERE id = :id"_ns
},
7326 selectStatement
= LazyStatement
{*mConnection
,
7329 "WHERE id = :id"_ns
},
7333 "INSERT INTO file (id, refcount) VALUES(:id, :delta)"_ns
},
7334 this](int64_t aId
, int32_t aDelta
) mutable -> Result
<Ok
, nsresult
> {
7335 AUTO_PROFILER_LABEL(
7336 "DatabaseConnection::UpdateRefcountFunction::WillCommit::Update", DOM
);
7338 QM_TRY_INSPECT(const auto& borrowedUpdateStatement
,
7339 updateStatement
.Borrow());
7342 MOZ_TO_RESULT(borrowedUpdateStatement
->BindInt32ByIndex(0, aDelta
)));
7343 QM_TRY(MOZ_TO_RESULT(borrowedUpdateStatement
->BindInt64ByIndex(1, aId
)));
7344 QM_TRY(MOZ_TO_RESULT(borrowedUpdateStatement
->Execute()));
7348 const int32_t& rows
,
7349 MOZ_TO_RESULT_INVOKE_MEMBER(mConnection
->MutableStorageConnection(),
7354 const bool& hasResult
,
7356 .BorrowAndExecuteSingleStep(
7357 [aId
](auto& stmt
) -> Result
<Ok
, nsresult
> {
7358 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(0, aId
)));
7364 // Don't have to create the journal here, we can create all at once,
7365 // just before commit
7366 mJournalsToCreateBeforeCommit
.AppendElement(aId
);
7372 QM_TRY_INSPECT(const auto& borrowedInsertStatement
,
7373 insertStatement
.Borrow());
7375 QM_TRY(MOZ_TO_RESULT(borrowedInsertStatement
->BindInt64ByIndex(0, aId
)));
7376 QM_TRY(MOZ_TO_RESULT(borrowedInsertStatement
->BindInt32ByIndex(1, aDelta
)));
7377 QM_TRY(MOZ_TO_RESULT(borrowedInsertStatement
->Execute()));
7379 mJournalsToRemoveAfterCommit
.AppendElement(aId
);
7384 QM_TRY(CollectEachInRange(
7385 mFileInfoEntries
, [&update
](const auto& entry
) -> Result
<Ok
, nsresult
> {
7386 const auto delta
= entry
.GetData()->Delta();
7388 QM_TRY(update(entry
.GetKey(), delta
));
7394 QM_TRY(MOZ_TO_RESULT(CreateJournals()));
7399 void DatabaseConnection::UpdateRefcountFunction::DidCommit() {
7400 MOZ_ASSERT(mConnection
);
7401 mConnection
->AssertIsOnConnectionThread();
7403 AUTO_PROFILER_LABEL("DatabaseConnection::UpdateRefcountFunction::DidCommit",
7406 for (const auto& entry
: mFileInfoEntries
.Values()) {
7407 entry
->MaybeUpdateDBRefs();
7410 QM_WARNONLY_TRY(QM_TO_RESULT(RemoveJournals(mJournalsToRemoveAfterCommit
)));
7413 void DatabaseConnection::UpdateRefcountFunction::DidAbort() {
7414 MOZ_ASSERT(mConnection
);
7415 mConnection
->AssertIsOnConnectionThread();
7417 AUTO_PROFILER_LABEL("DatabaseConnection::UpdateRefcountFunction::DidAbort",
7420 QM_WARNONLY_TRY(QM_TO_RESULT(RemoveJournals(mJournalsToRemoveAfterAbort
)));
7423 void DatabaseConnection::UpdateRefcountFunction::StartSavepoint() {
7424 MOZ_ASSERT(mConnection
);
7425 mConnection
->AssertIsOnConnectionThread();
7426 MOZ_ASSERT(!mInSavepoint
);
7427 MOZ_ASSERT(!mSavepointEntriesIndex
.Count());
7429 mInSavepoint
= true;
7432 void DatabaseConnection::UpdateRefcountFunction::ReleaseSavepoint() {
7433 MOZ_ASSERT(mConnection
);
7434 mConnection
->AssertIsOnConnectionThread();
7435 MOZ_ASSERT(mInSavepoint
);
7437 mSavepointEntriesIndex
.Clear();
7438 mInSavepoint
= false;
7441 void DatabaseConnection::UpdateRefcountFunction::RollbackSavepoint() {
7442 MOZ_ASSERT(mConnection
);
7443 mConnection
->AssertIsOnConnectionThread();
7444 MOZ_ASSERT(!IsOnBackgroundThread());
7445 MOZ_ASSERT(mInSavepoint
);
7447 for (const auto& entry
: mSavepointEntriesIndex
.Values()) {
7448 entry
->DecBySavepointDelta();
7451 mInSavepoint
= false;
7452 mSavepointEntriesIndex
.Clear();
7455 void DatabaseConnection::UpdateRefcountFunction::Reset() {
7456 MOZ_ASSERT(mConnection
);
7457 mConnection
->AssertIsOnConnectionThread();
7458 MOZ_ASSERT(!mSavepointEntriesIndex
.Count());
7459 MOZ_ASSERT(!mInSavepoint
);
7461 mJournalsToCreateBeforeCommit
.Clear();
7462 mJournalsToRemoveAfterCommit
.Clear();
7463 mJournalsToRemoveAfterAbort
.Clear();
7465 // DatabaseFileInfo implementation automatically removes unreferenced files,
7466 // but it's done asynchronously and with a delay. We want to remove them (and
7467 // decrease quota usage) before we fire the commit event.
7468 for (const auto& entry
: mFileInfoEntries
.Values()) {
7469 // We need to move mFileInfo into a raw pointer in order to release it
7470 // explicitly with aSyncDeleteFile == true.
7471 DatabaseFileInfo
* const fileInfo
= entry
->ReleaseFileInfo().forget().take();
7472 MOZ_ASSERT(fileInfo
);
7474 fileInfo
->Release(/* aSyncDeleteFile */ true);
7477 mFileInfoEntries
.Clear();
7480 nsresult
DatabaseConnection::UpdateRefcountFunction::ProcessValue(
7481 mozIStorageValueArray
* aValues
, int32_t aIndex
, UpdateType aUpdateType
) {
7482 MOZ_ASSERT(mConnection
);
7483 mConnection
->AssertIsOnConnectionThread();
7484 MOZ_ASSERT(aValues
);
7486 AUTO_PROFILER_LABEL(
7487 "DatabaseConnection::UpdateRefcountFunction::ProcessValue", DOM
);
7489 QM_TRY_INSPECT(const int32_t& type
,
7490 MOZ_TO_RESULT_INVOKE_MEMBER(aValues
, GetTypeOfIndex
, aIndex
));
7492 if (type
== mozIStorageValueArray::VALUE_TYPE_NULL
) {
7496 QM_TRY_INSPECT(const auto& ids
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
7497 nsString
, aValues
, GetString
, aIndex
));
7499 QM_TRY_INSPECT(const auto& files
,
7500 DeserializeStructuredCloneFiles(mFileManager
, ids
));
7502 for (const StructuredCloneFileParent
& file
: files
) {
7503 const int64_t id
= file
.FileInfo().Id();
7507 WrapNotNull(mFileInfoEntries
.GetOrInsertNew(id
, file
.FileInfoPtr()));
7510 mSavepointEntriesIndex
.InsertOrUpdate(id
, entry
);
7513 switch (aUpdateType
) {
7514 case UpdateType::Increment
:
7515 entry
->IncDeltas(mInSavepoint
);
7517 case UpdateType::Decrement
:
7518 entry
->DecDeltas(mInSavepoint
);
7521 MOZ_CRASH("Unknown update type!");
7528 nsresult
DatabaseConnection::UpdateRefcountFunction::CreateJournals() {
7529 MOZ_ASSERT(mConnection
);
7530 mConnection
->AssertIsOnConnectionThread();
7532 AUTO_PROFILER_LABEL(
7533 "DatabaseConnection::UpdateRefcountFunction::CreateJournals", DOM
);
7535 const nsCOMPtr
<nsIFile
> journalDirectory
= mFileManager
.GetJournalDirectory();
7536 QM_TRY(OkIf(journalDirectory
), NS_ERROR_FAILURE
);
7538 for (const int64_t id
: mJournalsToCreateBeforeCommit
) {
7539 const nsCOMPtr
<nsIFile
> file
=
7540 DatabaseFileManager::GetFileForId(journalDirectory
, id
);
7541 QM_TRY(OkIf(file
), NS_ERROR_FAILURE
);
7543 QM_TRY(MOZ_TO_RESULT(file
->Create(nsIFile::NORMAL_FILE_TYPE
, 0644)));
7545 mJournalsToRemoveAfterAbort
.AppendElement(id
);
7551 nsresult
DatabaseConnection::UpdateRefcountFunction::RemoveJournals(
7552 const nsTArray
<int64_t>& aJournals
) {
7553 MOZ_ASSERT(mConnection
);
7554 mConnection
->AssertIsOnConnectionThread();
7556 AUTO_PROFILER_LABEL(
7557 "DatabaseConnection::UpdateRefcountFunction::RemoveJournals", DOM
);
7559 nsCOMPtr
<nsIFile
> journalDirectory
= mFileManager
.GetJournalDirectory();
7560 QM_TRY(OkIf(journalDirectory
), NS_ERROR_FAILURE
);
7562 for (const auto& journal
: aJournals
) {
7563 nsCOMPtr
<nsIFile
> file
=
7564 DatabaseFileManager::GetFileForId(journalDirectory
, journal
);
7565 QM_TRY(OkIf(file
), NS_ERROR_FAILURE
);
7567 QM_WARNONLY_TRY(QM_TO_RESULT(file
->Remove(false)));
7573 NS_IMPL_ISUPPORTS(DatabaseConnection::UpdateRefcountFunction
,
7574 mozIStorageFunction
)
7577 DatabaseConnection::UpdateRefcountFunction::OnFunctionCall(
7578 mozIStorageValueArray
* aValues
, nsIVariant
** _retval
) {
7579 MOZ_ASSERT(aValues
);
7580 MOZ_ASSERT(_retval
);
7582 AUTO_PROFILER_LABEL(
7583 "DatabaseConnection::UpdateRefcountFunction::OnFunctionCall", DOM
);
7587 QM_TRY_INSPECT(const uint32_t& numEntries
,
7588 MOZ_TO_RESULT_INVOKE_MEMBER(aValues
, GetNumEntries
),
7589 QM_ASSERT_UNREACHABLE
);
7591 MOZ_ASSERT(numEntries
== 2);
7593 QM_TRY_INSPECT(const int32_t& type1
,
7594 MOZ_TO_RESULT_INVOKE_MEMBER(aValues
, GetTypeOfIndex
, 0),
7595 QM_ASSERT_UNREACHABLE
);
7597 QM_TRY_INSPECT(const int32_t& type2
,
7598 MOZ_TO_RESULT_INVOKE_MEMBER(aValues
, GetTypeOfIndex
, 1),
7599 QM_ASSERT_UNREACHABLE
);
7601 MOZ_ASSERT(!(type1
== mozIStorageValueArray::VALUE_TYPE_NULL
&&
7602 type2
== mozIStorageValueArray::VALUE_TYPE_NULL
));
7606 QM_TRY(MOZ_TO_RESULT(ProcessValue(aValues
, 0, UpdateType::Decrement
)));
7608 QM_TRY(MOZ_TO_RESULT(ProcessValue(aValues
, 1, UpdateType::Increment
)));
7613 /*******************************************************************************
7614 * ConnectionPool implementation
7615 ******************************************************************************/
7617 ConnectionPool::ConnectionPool()
7618 : mDatabasesMutex("ConnectionPool::mDatabasesMutex"),
7619 mIOTarget(MakeConnectionIOTarget()),
7620 mIdleTimer(NS_NewTimer()),
7621 mNextTransactionId(0) {
7622 AssertIsOnOwningThread();
7623 AssertIsOnBackgroundThread();
7624 MOZ_ASSERT(mIdleTimer
);
7627 ConnectionPool::~ConnectionPool() {
7628 AssertIsOnOwningThread();
7629 MOZ_ASSERT(mIdleDatabases
.IsEmpty());
7630 MOZ_ASSERT(!mIdleTimer
);
7631 MOZ_ASSERT(mTargetIdleTime
.IsNull());
7632 MOZ_ASSERT(!mDatabases
.Count());
7633 MOZ_ASSERT(!mTransactions
.Count());
7634 MOZ_ASSERT(mQueuedTransactions
.IsEmpty());
7635 MOZ_ASSERT(mCompleteCallbacks
.IsEmpty());
7636 MOZ_ASSERT(mShutdownRequested
);
7637 MOZ_ASSERT(mShutdownComplete
);
7641 void ConnectionPool::IdleTimerCallback(nsITimer
* aTimer
, void* aClosure
) {
7643 MOZ_ASSERT(aClosure
);
7645 AUTO_PROFILER_LABEL("ConnectionPool::IdleTimerCallback", DOM
);
7647 auto& self
= *static_cast<ConnectionPool
*>(aClosure
);
7648 MOZ_ASSERT(self
.mIdleTimer
);
7649 MOZ_ASSERT(SameCOMIdentity(self
.mIdleTimer
, aTimer
));
7650 MOZ_ASSERT(!self
.mTargetIdleTime
.IsNull());
7652 self
.mTargetIdleTime
= TimeStamp();
7655 const TimeStamp now
=
7656 TimeStamp::NowLoRes() + TimeDuration::FromMilliseconds(500);
7658 // XXX Move this to ArrayAlgorithm.h?
7659 const auto removeUntil
= [](auto& array
, auto&& cond
) {
7660 const auto begin
= array
.begin(), end
= array
.end();
7661 array
.RemoveElementsRange(
7662 begin
, std::find_if(begin
, end
, std::forward
<decltype(cond
)>(cond
)));
7665 removeUntil(self
.mIdleDatabases
, [now
, &self
](const auto& info
) {
7666 if (now
>= info
.mIdleTime
) {
7667 if ((*info
.mDatabaseInfo
)->mIdle
) {
7668 self
.PerformIdleDatabaseMaintenance(*info
.mDatabaseInfo
.ref());
7670 self
.CloseDatabase(*info
.mDatabaseInfo
.ref());
7679 self
.AdjustIdleTimer();
7682 Result
<RefPtr
<DatabaseConnection
>, nsresult
>
7683 ConnectionPool::GetOrCreateConnection(const Database
& aDatabase
) {
7684 MOZ_ASSERT(!NS_IsMainThread());
7685 MOZ_ASSERT(!IsOnBackgroundThread());
7687 AUTO_PROFILER_LABEL("ConnectionPool::GetOrCreateConnection", DOM
);
7689 DatabaseInfo
* dbInfo
;
7691 MutexAutoLock
lock(mDatabasesMutex
);
7693 dbInfo
= mDatabases
.Get(aDatabase
.Id());
7698 if (dbInfo
->mConnection
) {
7699 dbInfo
->AssertIsOnConnectionThread();
7701 return dbInfo
->mConnection
;
7704 MOZ_ASSERT(!dbInfo
->mDEBUGConnectionEventTarget
);
7707 MovingNotNull
<nsCOMPtr
<mozIStorageConnection
>> storageConnection
,
7708 GetStorageConnection(aDatabase
.FilePath(), aDatabase
.DirectoryLockId(),
7709 aDatabase
.TelemetryId(), aDatabase
.MaybeKeyRef()));
7711 RefPtr
<DatabaseConnection
> connection
= new DatabaseConnection(
7712 std::move(storageConnection
), aDatabase
.GetFileManagerPtr());
7714 QM_TRY(MOZ_TO_RESULT(connection
->Init()));
7716 dbInfo
->mConnection
= connection
;
7718 IDB_DEBUG_LOG(("ConnectionPool created connection 0x%p for '%s'",
7719 dbInfo
->mConnection
.get(),
7720 NS_ConvertUTF16toUTF8(aDatabase
.FilePath()).get()));
7723 dbInfo
->mDEBUGConnectionEventTarget
= GetCurrentSerialEventTarget();
7729 uint64_t ConnectionPool::Start(
7730 const nsID
& aBackgroundChildLoggingId
, const nsACString
& aDatabaseId
,
7731 int64_t aLoggingSerialNumber
, const nsTArray
<nsString
>& aObjectStoreNames
,
7732 bool aIsWriteTransaction
,
7733 TransactionDatabaseOperationBase
* aTransactionOp
) {
7734 AssertIsOnOwningThread();
7735 MOZ_ASSERT(!aDatabaseId
.IsEmpty());
7736 MOZ_ASSERT(mNextTransactionId
< UINT64_MAX
);
7737 MOZ_ASSERT(!mShutdownRequested
);
7739 AUTO_PROFILER_LABEL("ConnectionPool::Start", DOM
);
7741 const uint64_t transactionId
= ++mNextTransactionId
;
7743 // To avoid always acquiring a lock, we don't use WithEntryHandle here, which
7744 // would require a lock in any case.
7745 DatabaseInfo
* dbInfo
= mDatabases
.Get(aDatabaseId
);
7747 const bool databaseInfoIsNew
= !dbInfo
;
7749 if (databaseInfoIsNew
) {
7750 MutexAutoLock
lock(mDatabasesMutex
);
7753 .InsertOrUpdate(aDatabaseId
,
7754 MakeUnique
<DatabaseInfo
>(this, aDatabaseId
))
7758 MOZ_ASSERT(!mTransactions
.Contains(transactionId
));
7759 auto& transactionInfo
= *mTransactions
.InsertOrUpdate(
7760 transactionId
, MakeUnique
<TransactionInfo
>(
7761 *dbInfo
, aBackgroundChildLoggingId
, aDatabaseId
,
7762 transactionId
, aLoggingSerialNumber
, aObjectStoreNames
,
7763 aIsWriteTransaction
, aTransactionOp
));
7765 if (aIsWriteTransaction
) {
7766 MOZ_ASSERT(dbInfo
->mWriteTransactionCount
< UINT32_MAX
);
7767 dbInfo
->mWriteTransactionCount
++;
7769 MOZ_ASSERT(dbInfo
->mReadTransactionCount
< UINT32_MAX
);
7770 dbInfo
->mReadTransactionCount
++;
7773 auto& blockingTransactions
= dbInfo
->mBlockingTransactions
;
7775 for (const nsAString
& objectStoreName
: aObjectStoreNames
) {
7776 TransactionInfoPair
* blockInfo
=
7777 blockingTransactions
.GetOrInsertNew(objectStoreName
);
7779 // Mark what we are blocking on.
7780 if (const auto maybeBlockingRead
= blockInfo
->mLastBlockingReads
) {
7781 transactionInfo
.mBlockedOn
.Insert(&maybeBlockingRead
.ref());
7782 maybeBlockingRead
->AddBlockingTransaction(transactionInfo
);
7785 if (aIsWriteTransaction
) {
7786 for (const auto blockingWrite
: blockInfo
->mLastBlockingWrites
) {
7787 transactionInfo
.mBlockedOn
.Insert(blockingWrite
);
7788 blockingWrite
->AddBlockingTransaction(transactionInfo
);
7791 blockInfo
->mLastBlockingReads
= SomeRef(transactionInfo
);
7792 blockInfo
->mLastBlockingWrites
.Clear();
7794 blockInfo
->mLastBlockingWrites
.AppendElement(
7795 WrapNotNullUnchecked(&transactionInfo
));
7799 if (!transactionInfo
.mBlockedOn
.Count()) {
7800 Unused
<< ScheduleTransaction(transactionInfo
,
7801 /* aFromQueuedTransactions */ false);
7804 if (!databaseInfoIsNew
&&
7805 (mIdleDatabases
.RemoveElement(dbInfo
) ||
7806 mDatabasesPerformingIdleMaintenance
.RemoveElement(dbInfo
))) {
7810 return transactionId
;
7813 void ConnectionPool::Dispatch(uint64_t aTransactionId
, nsIRunnable
* aRunnable
) {
7814 AssertIsOnOwningThread();
7815 MOZ_ASSERT(aRunnable
);
7817 AUTO_PROFILER_LABEL("ConnectionPool::Dispatch", DOM
);
7819 auto* const transactionInfo
= mTransactions
.Get(aTransactionId
);
7820 MOZ_ASSERT(transactionInfo
);
7821 MOZ_ASSERT(!transactionInfo
->mFinished
);
7823 if (transactionInfo
->mRunning
) {
7824 DatabaseInfo
& dbInfo
= transactionInfo
->mDatabaseInfo
;
7825 MOZ_ASSERT(dbInfo
.mEventTarget
);
7826 MOZ_ASSERT(!dbInfo
.mClosing
);
7828 transactionInfo
->mIsWriteTransaction
,
7829 dbInfo
.mRunningWriteTransaction
&&
7830 dbInfo
.mRunningWriteTransaction
.refEquals(*transactionInfo
));
7832 MOZ_ALWAYS_SUCCEEDS(
7833 dbInfo
.mEventTarget
->Dispatch(aRunnable
, NS_DISPATCH_NORMAL
));
7835 transactionInfo
->mQueuedRunnables
.AppendElement(aRunnable
);
7839 void ConnectionPool::Finish(uint64_t aTransactionId
,
7840 FinishCallback
* aCallback
) {
7841 AssertIsOnOwningThread();
7844 auto* const transactionInfo
= mTransactions
.Get(aTransactionId
);
7845 MOZ_ASSERT(transactionInfo
);
7846 MOZ_ASSERT(!transactionInfo
->mFinished
);
7849 AUTO_PROFILER_LABEL("ConnectionPool::Finish", DOM
);
7851 RefPtr
<FinishCallbackWrapper
> wrapper
=
7852 new FinishCallbackWrapper(this, aTransactionId
, aCallback
);
7854 Dispatch(aTransactionId
, wrapper
);
7857 transactionInfo
->mFinished
.Flip();
7861 void ConnectionPool::WaitForDatabaseToComplete(const nsCString
& aDatabaseId
,
7862 nsIRunnable
* aCallback
) {
7863 AssertIsOnOwningThread();
7864 MOZ_ASSERT(!aDatabaseId
.IsEmpty());
7865 MOZ_ASSERT(aCallback
);
7867 AUTO_PROFILER_LABEL("ConnectionPool::WaitForDatabaseToComplete", DOM
);
7869 if (!CloseDatabaseWhenIdleInternal(aDatabaseId
)) {
7870 Unused
<< aCallback
->Run();
7874 mCompleteCallbacks
.EmplaceBack(
7875 MakeUnique
<DatabaseCompleteCallback
>(aDatabaseId
, aCallback
));
7878 void ConnectionPool::Shutdown() {
7879 AssertIsOnOwningThread();
7880 MOZ_ASSERT(!mShutdownComplete
);
7882 AUTO_PROFILER_LABEL("ConnectionPool::Shutdown", DOM
);
7884 mShutdownRequested
.Flip();
7887 MOZ_ASSERT(mTargetIdleTime
.IsNull());
7889 mIdleTimer
= nullptr;
7891 CloseIdleDatabases();
7893 if (!mDatabases
.Count()) {
7894 MOZ_ASSERT(!mTransactions
.Count());
7898 MOZ_ASSERT(mShutdownComplete
);
7900 mIOTarget
->Shutdown();
7905 MOZ_ALWAYS_TRUE(SpinEventLoopUntil("ConnectionPool::Shutdown"_ns
, [&]() {
7906 return static_cast<bool>(mShutdownComplete
);
7909 mIOTarget
->Shutdown();
7912 void ConnectionPool::Cleanup() {
7913 AssertIsOnOwningThread();
7914 MOZ_ASSERT(mShutdownRequested
);
7915 MOZ_ASSERT(!mShutdownComplete
);
7916 MOZ_ASSERT(!mDatabases
.Count());
7917 MOZ_ASSERT(!mTransactions
.Count());
7919 AUTO_PROFILER_LABEL("ConnectionPool::Cleanup", DOM
);
7921 if (!mCompleteCallbacks
.IsEmpty()) {
7922 // Run all callbacks manually now.
7925 auto completeCallbacks
= std::move(mCompleteCallbacks
);
7926 for (const auto& completeCallback
: completeCallbacks
) {
7927 MOZ_ASSERT(completeCallback
);
7928 MOZ_ASSERT(completeCallback
->mCallback
);
7930 Unused
<< completeCallback
->mCallback
->Run();
7933 // We expect no new callbacks being completed by running the existing
7935 MOZ_ASSERT(mCompleteCallbacks
.IsEmpty());
7938 // And make sure they get processed.
7939 nsIThread
* currentThread
= NS_GetCurrentThread();
7940 MOZ_ASSERT(currentThread
);
7942 MOZ_ALWAYS_SUCCEEDS(NS_ProcessPendingEvents(currentThread
));
7945 mShutdownComplete
.Flip();
7948 void ConnectionPool::AdjustIdleTimer() {
7949 AssertIsOnOwningThread();
7950 MOZ_ASSERT(mIdleTimer
);
7952 AUTO_PROFILER_LABEL("ConnectionPool::AdjustIdleTimer", DOM
);
7954 // Figure out the next time at which we should release idle resources. This
7955 // includes both databases and threads.
7956 TimeStamp newTargetIdleTime
;
7957 MOZ_ASSERT(newTargetIdleTime
.IsNull());
7959 if (!mIdleDatabases
.IsEmpty()) {
7960 newTargetIdleTime
= mIdleDatabases
[0].mIdleTime
;
7963 MOZ_ASSERT_IF(newTargetIdleTime
.IsNull(), mIdleDatabases
.IsEmpty());
7965 // Cancel the timer if it was running and the new target time is different.
7966 if (!mTargetIdleTime
.IsNull() &&
7967 (newTargetIdleTime
.IsNull() || mTargetIdleTime
!= newTargetIdleTime
)) {
7970 MOZ_ASSERT(mTargetIdleTime
.IsNull());
7973 // Schedule the timer if we have a target time different than before.
7974 if (!newTargetIdleTime
.IsNull() &&
7975 (mTargetIdleTime
.IsNull() || mTargetIdleTime
!= newTargetIdleTime
)) {
7976 double delta
= (newTargetIdleTime
- TimeStamp::NowLoRes()).ToMilliseconds();
7980 delay
= uint32_t(std::min(delta
, double(UINT32_MAX
)));
7985 MOZ_ALWAYS_SUCCEEDS(mIdleTimer
->InitWithNamedFuncCallback(
7986 IdleTimerCallback
, this, delay
, nsITimer::TYPE_ONE_SHOT
,
7987 "ConnectionPool::IdleTimerCallback"));
7989 mTargetIdleTime
= newTargetIdleTime
;
7993 void ConnectionPool::CancelIdleTimer() {
7994 AssertIsOnOwningThread();
7995 MOZ_ASSERT(mIdleTimer
);
7997 if (!mTargetIdleTime
.IsNull()) {
7998 MOZ_ALWAYS_SUCCEEDS(mIdleTimer
->Cancel());
8000 mTargetIdleTime
= TimeStamp();
8001 MOZ_ASSERT(mTargetIdleTime
.IsNull());
8005 void ConnectionPool::CloseIdleDatabases() {
8006 AssertIsOnOwningThread();
8007 MOZ_ASSERT(mShutdownRequested
);
8009 AUTO_PROFILER_LABEL("ConnectionPool::CloseIdleDatabases", DOM
);
8011 if (!mIdleDatabases
.IsEmpty()) {
8012 for (IdleDatabaseInfo
& idleInfo
: mIdleDatabases
) {
8013 CloseDatabase(*idleInfo
.mDatabaseInfo
.ref());
8015 mIdleDatabases
.Clear();
8018 if (!mDatabasesPerformingIdleMaintenance
.IsEmpty()) {
8019 for (PerformingIdleMaintenanceDatabaseInfo
& performingIdleMaintenanceInfo
:
8020 mDatabasesPerformingIdleMaintenance
) {
8021 CloseDatabase(*performingIdleMaintenanceInfo
.mDatabaseInfo
);
8023 mDatabasesPerformingIdleMaintenance
.Clear();
8027 bool ConnectionPool::ScheduleTransaction(TransactionInfo
& aTransactionInfo
,
8028 bool aFromQueuedTransactions
) {
8029 AssertIsOnOwningThread();
8031 AUTO_PROFILER_LABEL("ConnectionPool::ScheduleTransaction", DOM
);
8033 DatabaseInfo
& dbInfo
= aTransactionInfo
.mDatabaseInfo
;
8035 dbInfo
.mIdle
= false;
8037 if (dbInfo
.mClosing
) {
8038 MOZ_ASSERT(!mIdleDatabases
.Contains(&dbInfo
));
8040 !dbInfo
.mTransactionsScheduledDuringClose
.Contains(&aTransactionInfo
));
8042 dbInfo
.mTransactionsScheduledDuringClose
.AppendElement(
8043 WrapNotNullUnchecked(&aTransactionInfo
));
8047 if (!dbInfo
.mEventTarget
) {
8048 const uint32_t serialNumber
= SerialNumber();
8049 const nsCString serialName
=
8050 nsPrintfCString("IndexedDB #%" PRIu32
, serialNumber
);
8052 dbInfo
.mEventTarget
=
8053 TaskQueue::Create(do_AddRef(mIOTarget
), serialName
.get());
8054 MOZ_ASSERT(dbInfo
.mEventTarget
);
8055 IDB_DEBUG_LOG(("ConnectionPool created task queue %" PRIu32
, serialNumber
));
8058 // The number of active operations equals the number of databases minus idle
8059 // databases. The maximum number of database operations which can make
8060 // progress at the same time is kMaxConnectionThreadCount. If we are at this
8061 // limit, all idle processing is interrupted to make room for user
8063 if (mDatabases
.Count() >=
8064 (mIdleDatabases
.Length() + kMaxConnectionThreadCount
) &&
8065 !mDatabasesPerformingIdleMaintenance
.IsEmpty()) {
8066 const auto& busyDbs
= mDatabasesPerformingIdleMaintenance
;
8067 for (auto dbInfo
= busyDbs
.rbegin(); dbInfo
!= busyDbs
.rend(); ++dbInfo
) {
8068 (*dbInfo
).mIdleConnectionRunnable
->Interrupt();
8072 if (aTransactionInfo
.mIsWriteTransaction
) {
8073 if (dbInfo
.mRunningWriteTransaction
) {
8074 // SQLite only allows one write transaction at a time so queue this
8075 // transaction for later.
8077 !dbInfo
.mScheduledWriteTransactions
.Contains(&aTransactionInfo
));
8079 dbInfo
.mScheduledWriteTransactions
.AppendElement(
8080 WrapNotNullUnchecked(&aTransactionInfo
));
8084 dbInfo
.mRunningWriteTransaction
= SomeRef(aTransactionInfo
);
8085 dbInfo
.mNeedsCheckpoint
= true;
8088 MOZ_ASSERT(!aTransactionInfo
.mRunning
);
8089 aTransactionInfo
.mRunning
= true;
8091 nsTArray
<nsCOMPtr
<nsIRunnable
>>& queuedRunnables
=
8092 aTransactionInfo
.mQueuedRunnables
;
8094 if (!queuedRunnables
.IsEmpty()) {
8095 for (auto& queuedRunnable
: queuedRunnables
) {
8096 MOZ_ALWAYS_SUCCEEDS(
8097 dbInfo
.mEventTarget
->Dispatch(queuedRunnable
.forget()));
8100 queuedRunnables
.Clear();
8106 void ConnectionPool::NoteFinishedTransaction(uint64_t aTransactionId
) {
8107 AssertIsOnOwningThread();
8109 AUTO_PROFILER_LABEL("ConnectionPool::NoteFinishedTransaction", DOM
);
8111 auto* const transactionInfo
= mTransactions
.Get(aTransactionId
);
8112 MOZ_ASSERT(transactionInfo
);
8113 MOZ_ASSERT(transactionInfo
->mRunning
);
8114 MOZ_ASSERT(transactionInfo
->mFinished
);
8116 transactionInfo
->mRunning
= false;
8118 DatabaseInfo
& dbInfo
= transactionInfo
->mDatabaseInfo
;
8119 MOZ_ASSERT(mDatabases
.Get(transactionInfo
->mDatabaseId
) == &dbInfo
);
8120 MOZ_ASSERT(dbInfo
.mEventTarget
);
8122 // Schedule the next write transaction if there are any queued.
8123 if (dbInfo
.mRunningWriteTransaction
&&
8124 dbInfo
.mRunningWriteTransaction
.refEquals(*transactionInfo
)) {
8125 MOZ_ASSERT(transactionInfo
->mIsWriteTransaction
);
8126 MOZ_ASSERT(dbInfo
.mNeedsCheckpoint
);
8128 dbInfo
.mRunningWriteTransaction
= Nothing();
8130 if (!dbInfo
.mScheduledWriteTransactions
.IsEmpty()) {
8131 const auto nextWriteTransaction
= dbInfo
.mScheduledWriteTransactions
[0];
8133 dbInfo
.mScheduledWriteTransactions
.RemoveElementAt(0);
8135 MOZ_ALWAYS_TRUE(ScheduleTransaction(*nextWriteTransaction
,
8136 /* aFromQueuedTransactions */ false));
8140 for (const auto& objectStoreName
: transactionInfo
->mObjectStoreNames
) {
8141 TransactionInfoPair
* blockInfo
=
8142 dbInfo
.mBlockingTransactions
.Get(objectStoreName
);
8143 MOZ_ASSERT(blockInfo
);
8145 if (transactionInfo
->mIsWriteTransaction
&& blockInfo
->mLastBlockingReads
&&
8146 blockInfo
->mLastBlockingReads
.refEquals(*transactionInfo
)) {
8147 blockInfo
->mLastBlockingReads
= Nothing();
8150 blockInfo
->mLastBlockingWrites
.RemoveElement(transactionInfo
);
8153 transactionInfo
->RemoveBlockingTransactions();
8155 if (transactionInfo
->mIsWriteTransaction
) {
8156 MOZ_ASSERT(dbInfo
.mWriteTransactionCount
);
8157 dbInfo
.mWriteTransactionCount
--;
8159 MOZ_ASSERT(dbInfo
.mReadTransactionCount
);
8160 dbInfo
.mReadTransactionCount
--;
8163 mTransactions
.Remove(aTransactionId
);
8165 if (!dbInfo
.TotalTransactionCount()) {
8166 MOZ_ASSERT(!dbInfo
.mIdle
);
8167 dbInfo
.mIdle
= true;
8169 NoteIdleDatabase(dbInfo
);
8173 void ConnectionPool::ScheduleQueuedTransactions() {
8174 AssertIsOnOwningThread();
8175 MOZ_ASSERT(!mQueuedTransactions
.IsEmpty());
8177 AUTO_PROFILER_LABEL("ConnectionPool::ScheduleQueuedTransactions", DOM
);
8179 const auto foundIt
= std::find_if(
8180 mQueuedTransactions
.begin(), mQueuedTransactions
.end(),
8181 [&me
= *this](const auto& queuedTransaction
) {
8182 return !me
.ScheduleTransaction(*queuedTransaction
,
8183 /* aFromQueuedTransactions */ true);
8186 mQueuedTransactions
.RemoveElementsRange(mQueuedTransactions
.begin(), foundIt
);
8191 void ConnectionPool::NoteIdleDatabase(DatabaseInfo
& aDatabaseInfo
) {
8192 AssertIsOnOwningThread();
8193 MOZ_ASSERT(!aDatabaseInfo
.TotalTransactionCount());
8194 MOZ_ASSERT(aDatabaseInfo
.mEventTarget
);
8195 MOZ_ASSERT(!mIdleDatabases
.Contains(&aDatabaseInfo
));
8197 AUTO_PROFILER_LABEL("ConnectionPool::NoteIdleDatabase", DOM
);
8199 const bool otherDatabasesWaiting
= !mQueuedTransactions
.IsEmpty();
8201 // We check mShutdownRequested because when it is true, mIdleTimer is null.
8202 if (mShutdownRequested
|| otherDatabasesWaiting
||
8203 aDatabaseInfo
.mCloseOnIdle
) {
8204 // Make sure we close the connection if we're shutting down or giving the
8205 // thread to another database.
8206 CloseDatabase(aDatabaseInfo
);
8208 if (otherDatabasesWaiting
) {
8209 ScheduleQueuedTransactions();
8215 mIdleDatabases
.InsertElementSorted(IdleDatabaseInfo
{aDatabaseInfo
});
8220 void ConnectionPool::NoteClosedDatabase(DatabaseInfo
& aDatabaseInfo
) {
8221 AssertIsOnOwningThread();
8222 MOZ_ASSERT(aDatabaseInfo
.mClosing
);
8223 MOZ_ASSERT(!mIdleDatabases
.Contains(&aDatabaseInfo
));
8225 AUTO_PROFILER_LABEL("ConnectionPool::NoteClosedDatabase", DOM
);
8227 aDatabaseInfo
.mClosing
= false;
8229 // Schedule any transactions that were started while we were closing the
8231 if (!mQueuedTransactions
.IsEmpty()) {
8232 ScheduleQueuedTransactions();
8233 } else if (!aDatabaseInfo
.TotalTransactionCount() && !mShutdownRequested
) {
8237 // Schedule any transactions that were started while we were closing the
8239 if (aDatabaseInfo
.TotalTransactionCount()) {
8240 auto& scheduledTransactions
=
8241 aDatabaseInfo
.mTransactionsScheduledDuringClose
;
8243 MOZ_ASSERT(!scheduledTransactions
.IsEmpty());
8245 for (const auto& scheduledTransaction
: scheduledTransactions
) {
8246 Unused
<< ScheduleTransaction(*scheduledTransaction
,
8247 /* aFromQueuedTransactions */ false);
8250 scheduledTransactions
.Clear();
8255 // There are no more transactions and the connection has been closed. We're
8256 // done with this database.
8258 MutexAutoLock
lock(mDatabasesMutex
);
8260 mDatabases
.Remove(aDatabaseInfo
.mDatabaseId
);
8263 // That just deleted |aDatabaseInfo|, we must not access that below.
8265 // See if we need to fire any complete callbacks now that the database is
8267 mCompleteCallbacks
.RemoveLastElements(
8268 mCompleteCallbacks
.end() -
8269 std::remove_if(mCompleteCallbacks
.begin(), mCompleteCallbacks
.end(),
8270 [&me
= *this](const auto& completeCallback
) {
8271 return me
.MaybeFireCallback(completeCallback
.get());
8274 // If that was the last database and we're supposed to be shutting down then
8276 if (mShutdownRequested
&& !mDatabases
.Count()) {
8277 MOZ_ASSERT(!mTransactions
.Count());
8282 bool ConnectionPool::MaybeFireCallback(DatabaseCompleteCallback
* aCallback
) {
8283 AssertIsOnOwningThread();
8284 MOZ_ASSERT(aCallback
);
8285 MOZ_ASSERT(!aCallback
->mDatabaseId
.IsEmpty());
8286 MOZ_ASSERT(aCallback
->mCallback
);
8288 AUTO_PROFILER_LABEL("ConnectionPool::MaybeFireCallback", DOM
);
8290 if (mDatabases
.Get(aCallback
->mDatabaseId
)) {
8294 Unused
<< aCallback
->mCallback
->Run();
8298 void ConnectionPool::PerformIdleDatabaseMaintenance(
8299 DatabaseInfo
& aDatabaseInfo
) {
8300 AssertIsOnOwningThread();
8301 MOZ_ASSERT(!aDatabaseInfo
.TotalTransactionCount());
8302 MOZ_ASSERT(aDatabaseInfo
.mEventTarget
);
8303 MOZ_ASSERT(aDatabaseInfo
.mIdle
);
8304 MOZ_ASSERT(!aDatabaseInfo
.mCloseOnIdle
);
8305 MOZ_ASSERT(!aDatabaseInfo
.mClosing
);
8306 MOZ_ASSERT(mIdleDatabases
.Contains(&aDatabaseInfo
));
8307 MOZ_ASSERT(!mDatabasesPerformingIdleMaintenance
.Contains(&aDatabaseInfo
));
8309 const bool neededCheckpoint
= aDatabaseInfo
.mNeedsCheckpoint
;
8311 aDatabaseInfo
.mNeedsCheckpoint
= false;
8312 aDatabaseInfo
.mIdle
= false;
8314 auto idleConnectionRunnable
=
8315 MakeRefPtr
<IdleConnectionRunnable
>(aDatabaseInfo
, neededCheckpoint
);
8317 mDatabasesPerformingIdleMaintenance
.AppendElement(
8318 PerformingIdleMaintenanceDatabaseInfo
{aDatabaseInfo
,
8319 idleConnectionRunnable
});
8321 MOZ_ALWAYS_SUCCEEDS(aDatabaseInfo
.mEventTarget
->Dispatch(
8322 idleConnectionRunnable
.forget(), NS_DISPATCH_NORMAL
));
8325 void ConnectionPool::CloseDatabase(DatabaseInfo
& aDatabaseInfo
) const {
8326 AssertIsOnOwningThread();
8327 MOZ_DIAGNOSTIC_ASSERT(!aDatabaseInfo
.TotalTransactionCount());
8328 MOZ_ASSERT(aDatabaseInfo
.mEventTarget
);
8329 MOZ_ASSERT(!aDatabaseInfo
.mClosing
);
8331 aDatabaseInfo
.mIdle
= false;
8332 aDatabaseInfo
.mNeedsCheckpoint
= false;
8333 aDatabaseInfo
.mClosing
= true;
8335 MOZ_ALWAYS_SUCCEEDS(aDatabaseInfo
.mEventTarget
->Dispatch(
8336 MakeAndAddRef
<CloseConnectionRunnable
>(aDatabaseInfo
),
8337 NS_DISPATCH_NORMAL
));
8340 bool ConnectionPool::CloseDatabaseWhenIdleInternal(
8341 const nsACString
& aDatabaseId
) {
8342 AssertIsOnOwningThread();
8343 MOZ_ASSERT(!aDatabaseId
.IsEmpty());
8345 AUTO_PROFILER_LABEL("ConnectionPool::CloseDatabaseWhenIdleInternal", DOM
);
8347 if (DatabaseInfo
* dbInfo
= mDatabases
.Get(aDatabaseId
)) {
8348 if (mIdleDatabases
.RemoveElement(dbInfo
) ||
8349 mDatabasesPerformingIdleMaintenance
.RemoveElement(dbInfo
)) {
8350 CloseDatabase(*dbInfo
);
8353 dbInfo
->mCloseOnIdle
.EnsureFlipped();
8362 ConnectionPool::ConnectionRunnable::ConnectionRunnable(
8363 DatabaseInfo
& aDatabaseInfo
)
8364 : Runnable("dom::indexedDB::ConnectionPool::ConnectionRunnable"),
8365 mDatabaseInfo(aDatabaseInfo
),
8366 mOwningEventTarget(GetCurrentSerialEventTarget()) {
8367 AssertIsOnBackgroundThread();
8368 MOZ_ASSERT(aDatabaseInfo
.mConnectionPool
);
8369 aDatabaseInfo
.mConnectionPool
->AssertIsOnOwningThread();
8370 MOZ_ASSERT(mOwningEventTarget
);
8374 ConnectionPool::IdleConnectionRunnable::Run() {
8375 MOZ_ASSERT(!mDatabaseInfo
.mIdle
);
8377 const nsCOMPtr
<nsIEventTarget
> owningThread
= std::move(mOwningEventTarget
);
8380 mDatabaseInfo
.AssertIsOnConnectionThread();
8382 // The connection could be null if EnsureConnection() didn't run or was not
8383 // successful in TransactionDatabaseOperationBase::RunOnConnectionThread().
8384 if (mDatabaseInfo
.mConnection
) {
8385 mDatabaseInfo
.mConnection
->DoIdleProcessing(mNeedsCheckpoint
,
8389 MOZ_ALWAYS_SUCCEEDS(owningThread
->Dispatch(this, NS_DISPATCH_NORMAL
));
8393 AssertIsOnBackgroundThread();
8395 RefPtr
<ConnectionPool
> connectionPool
= mDatabaseInfo
.mConnectionPool
;
8396 MOZ_ASSERT(connectionPool
);
8398 if (mDatabaseInfo
.mClosing
|| mDatabaseInfo
.TotalTransactionCount()) {
8399 MOZ_ASSERT(!connectionPool
->mDatabasesPerformingIdleMaintenance
.Contains(
8403 connectionPool
->mDatabasesPerformingIdleMaintenance
.RemoveElement(
8406 connectionPool
->NoteIdleDatabase(mDatabaseInfo
);
8413 ConnectionPool::CloseConnectionRunnable::Run() {
8414 AUTO_PROFILER_LABEL("ConnectionPool::CloseConnectionRunnable::Run", DOM
);
8416 if (mOwningEventTarget
) {
8417 MOZ_ASSERT(mDatabaseInfo
.mClosing
);
8419 const nsCOMPtr
<nsIEventTarget
> owningThread
= std::move(mOwningEventTarget
);
8421 // The connection could be null if EnsureConnection() didn't run or was not
8422 // successful in TransactionDatabaseOperationBase::RunOnConnectionThread().
8423 if (mDatabaseInfo
.mConnection
) {
8424 mDatabaseInfo
.AssertIsOnConnectionThread();
8426 mDatabaseInfo
.mConnection
->Close();
8428 IDB_DEBUG_LOG(("ConnectionPool closed connection 0x%p",
8429 mDatabaseInfo
.mConnection
.get()));
8431 mDatabaseInfo
.mConnection
= nullptr;
8434 mDatabaseInfo
.mDEBUGConnectionEventTarget
= nullptr;
8438 MOZ_ALWAYS_SUCCEEDS(owningThread
->Dispatch(this, NS_DISPATCH_NORMAL
));
8442 RefPtr
<ConnectionPool
> connectionPool
= mDatabaseInfo
.mConnectionPool
;
8443 MOZ_ASSERT(connectionPool
);
8445 connectionPool
->NoteClosedDatabase(mDatabaseInfo
);
8449 ConnectionPool::DatabaseInfo::DatabaseInfo(ConnectionPool
* aConnectionPool
,
8450 const nsACString
& aDatabaseId
)
8451 : mConnectionPool(aConnectionPool
),
8452 mDatabaseId(aDatabaseId
),
8453 mReadTransactionCount(0),
8454 mWriteTransactionCount(0),
8455 mNeedsCheckpoint(false),
8460 mDEBUGConnectionEventTarget(nullptr)
8463 AssertIsOnBackgroundThread();
8464 MOZ_ASSERT(aConnectionPool
);
8465 aConnectionPool
->AssertIsOnOwningThread();
8466 MOZ_ASSERT(!aDatabaseId
.IsEmpty());
8468 MOZ_COUNT_CTOR(ConnectionPool::DatabaseInfo
);
8471 ConnectionPool::DatabaseInfo::~DatabaseInfo() {
8472 AssertIsOnBackgroundThread();
8473 MOZ_ASSERT(!mConnection
);
8474 MOZ_ASSERT(mScheduledWriteTransactions
.IsEmpty());
8475 MOZ_ASSERT(!mRunningWriteTransaction
);
8476 MOZ_ASSERT(!TotalTransactionCount());
8478 MOZ_COUNT_DTOR(ConnectionPool::DatabaseInfo
);
8481 ConnectionPool::DatabaseCompleteCallback::DatabaseCompleteCallback(
8482 const nsCString
& aDatabaseId
, nsIRunnable
* aCallback
)
8483 : mDatabaseId(aDatabaseId
), mCallback(aCallback
) {
8484 AssertIsOnBackgroundThread();
8485 MOZ_ASSERT(!mDatabaseId
.IsEmpty());
8486 MOZ_ASSERT(aCallback
);
8488 MOZ_COUNT_CTOR(ConnectionPool::DatabaseCompleteCallback
);
8491 ConnectionPool::DatabaseCompleteCallback::~DatabaseCompleteCallback() {
8492 AssertIsOnBackgroundThread();
8494 MOZ_COUNT_DTOR(ConnectionPool::DatabaseCompleteCallback
);
8497 ConnectionPool::FinishCallbackWrapper::FinishCallbackWrapper(
8498 ConnectionPool
* aConnectionPool
, uint64_t aTransactionId
,
8499 FinishCallback
* aCallback
)
8500 : Runnable("dom::indexedDB::ConnectionPool::FinishCallbackWrapper"),
8501 mConnectionPool(aConnectionPool
),
8502 mCallback(aCallback
),
8503 mOwningEventTarget(GetCurrentSerialEventTarget()),
8504 mTransactionId(aTransactionId
),
8505 mHasRunOnce(false) {
8506 AssertIsOnBackgroundThread();
8507 MOZ_ASSERT(aConnectionPool
);
8508 MOZ_ASSERT(aCallback
);
8509 MOZ_ASSERT(mOwningEventTarget
);
8512 ConnectionPool::FinishCallbackWrapper::~FinishCallbackWrapper() {
8513 MOZ_ASSERT(!mConnectionPool
);
8514 MOZ_ASSERT(!mCallback
);
8517 nsresult
ConnectionPool::FinishCallbackWrapper::Run() {
8518 MOZ_ASSERT(mConnectionPool
);
8519 MOZ_ASSERT(mCallback
);
8520 MOZ_ASSERT(mOwningEventTarget
);
8522 AUTO_PROFILER_LABEL("ConnectionPool::FinishCallbackWrapper::Run", DOM
);
8525 MOZ_ASSERT(!IsOnBackgroundThread());
8529 Unused
<< mCallback
->Run();
8531 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
));
8536 mConnectionPool
->AssertIsOnOwningThread();
8537 MOZ_ASSERT(mHasRunOnce
);
8539 RefPtr
<ConnectionPool
> connectionPool
= std::move(mConnectionPool
);
8540 RefPtr
<FinishCallback
> callback
= std::move(mCallback
);
8542 callback
->TransactionFinishedBeforeUnblock();
8544 connectionPool
->NoteFinishedTransaction(mTransactionId
);
8546 callback
->TransactionFinishedAfterUnblock();
8551 uint32_t ConnectionPool::sSerialNumber
= 0u;
8553 ConnectionPool::IdleResource::IdleResource(const TimeStamp
& aIdleTime
)
8554 : mIdleTime(aIdleTime
) {
8555 AssertIsOnBackgroundThread();
8556 MOZ_ASSERT(!aIdleTime
.IsNull());
8558 MOZ_COUNT_CTOR(ConnectionPool::IdleResource
);
8561 ConnectionPool::IdleResource::~IdleResource() {
8562 AssertIsOnBackgroundThread();
8564 MOZ_COUNT_DTOR(ConnectionPool::IdleResource
);
8567 ConnectionPool::IdleDatabaseInfo::IdleDatabaseInfo(DatabaseInfo
& aDatabaseInfo
)
8569 TimeStamp::NowLoRes() +
8570 (aDatabaseInfo
.mIdle
8571 ? TimeDuration::FromMilliseconds(kConnectionIdleMaintenanceMS
)
8572 : TimeDuration::FromMilliseconds(kConnectionIdleCloseMS
))),
8573 mDatabaseInfo(WrapNotNullUnchecked(&aDatabaseInfo
)) {
8574 AssertIsOnBackgroundThread();
8576 MOZ_COUNT_CTOR(ConnectionPool::IdleDatabaseInfo
);
8579 ConnectionPool::IdleDatabaseInfo::~IdleDatabaseInfo() {
8580 AssertIsOnBackgroundThread();
8582 MOZ_COUNT_DTOR(ConnectionPool::IdleDatabaseInfo
);
8585 ConnectionPool::PerformingIdleMaintenanceDatabaseInfo::
8586 PerformingIdleMaintenanceDatabaseInfo(
8587 DatabaseInfo
& aDatabaseInfo
,
8588 RefPtr
<IdleConnectionRunnable
> aIdleConnectionRunnable
)
8589 : mDatabaseInfo(WrapNotNullUnchecked(&aDatabaseInfo
)),
8590 mIdleConnectionRunnable(std::move(aIdleConnectionRunnable
)) {
8591 AssertIsOnBackgroundThread();
8592 MOZ_ASSERT(mIdleConnectionRunnable
);
8594 MOZ_COUNT_CTOR(ConnectionPool::PerformingIdleMaintenanceDatabaseInfo
);
8597 ConnectionPool::PerformingIdleMaintenanceDatabaseInfo::
8598 ~PerformingIdleMaintenanceDatabaseInfo() {
8599 AssertIsOnBackgroundThread();
8601 MOZ_COUNT_DTOR(ConnectionPool::PerformingIdleMaintenanceDatabaseInfo
);
8604 ConnectionPool::TransactionInfo::TransactionInfo(
8605 DatabaseInfo
& aDatabaseInfo
, const nsID
& aBackgroundChildLoggingId
,
8606 const nsACString
& aDatabaseId
, uint64_t aTransactionId
,
8607 int64_t aLoggingSerialNumber
, const nsTArray
<nsString
>& aObjectStoreNames
,
8608 bool aIsWriteTransaction
, TransactionDatabaseOperationBase
* aTransactionOp
)
8609 : mDatabaseInfo(aDatabaseInfo
),
8610 mBackgroundChildLoggingId(aBackgroundChildLoggingId
),
8611 mDatabaseId(aDatabaseId
),
8612 mTransactionId(aTransactionId
),
8613 mLoggingSerialNumber(aLoggingSerialNumber
),
8614 mObjectStoreNames(aObjectStoreNames
.Clone()),
8615 mIsWriteTransaction(aIsWriteTransaction
),
8617 AssertIsOnBackgroundThread();
8618 aDatabaseInfo
.mConnectionPool
->AssertIsOnOwningThread();
8620 MOZ_COUNT_CTOR(ConnectionPool::TransactionInfo
);
8622 if (aTransactionOp
) {
8623 mQueuedRunnables
.AppendElement(aTransactionOp
);
8627 ConnectionPool::TransactionInfo::~TransactionInfo() {
8628 AssertIsOnBackgroundThread();
8629 MOZ_ASSERT(!mBlockedOn
.Count());
8630 MOZ_ASSERT(mQueuedRunnables
.IsEmpty());
8631 MOZ_ASSERT(!mRunning
);
8632 MOZ_ASSERT(mFinished
);
8634 MOZ_COUNT_DTOR(ConnectionPool::TransactionInfo
);
8637 void ConnectionPool::TransactionInfo::AddBlockingTransaction(
8638 TransactionInfo
& aTransactionInfo
) {
8639 AssertIsOnBackgroundThread();
8641 // XXX Does it really make sense to have both mBlocking and mBlockingOrdered,
8642 // just to reduce the algorithmic complexity of this Contains check? This was
8643 // mentioned in the context of Bug 1290853, but no real justification was
8644 // given. There was the suggestion of encapsulating this in an
8645 // insertion-ordered hashtable implementation, which seems like a good idea.
8646 // If we had that, this would be the appropriate data structure to use here.
8647 if (mBlocking
.EnsureInserted(&aTransactionInfo
)) {
8648 mBlockingOrdered
.AppendElement(WrapNotNullUnchecked(&aTransactionInfo
));
8652 void ConnectionPool::TransactionInfo::RemoveBlockingTransactions() {
8653 AssertIsOnBackgroundThread();
8655 for (const auto blockedInfo
: mBlockingOrdered
) {
8656 blockedInfo
->MaybeUnblock(*this);
8660 mBlockingOrdered
.Clear();
8663 void ConnectionPool::TransactionInfo::MaybeUnblock(
8664 TransactionInfo
& aTransactionInfo
) {
8665 AssertIsOnBackgroundThread();
8666 MOZ_ASSERT(mBlockedOn
.Contains(&aTransactionInfo
));
8668 mBlockedOn
.Remove(&aTransactionInfo
);
8669 if (mBlockedOn
.IsEmpty()) {
8670 ConnectionPool
* connectionPool
= mDatabaseInfo
.mConnectionPool
;
8671 MOZ_ASSERT(connectionPool
);
8672 connectionPool
->AssertIsOnOwningThread();
8674 Unused
<< connectionPool
->ScheduleTransaction(
8676 /* aFromQueuedTransactions */ false);
8680 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
8681 ConnectionPool::TransactionInfoPair::TransactionInfoPair() {
8682 AssertIsOnBackgroundThread();
8684 MOZ_COUNT_CTOR(ConnectionPool::TransactionInfoPair
);
8687 ConnectionPool::TransactionInfoPair::~TransactionInfoPair() {
8688 AssertIsOnBackgroundThread();
8690 MOZ_COUNT_DTOR(ConnectionPool::TransactionInfoPair
);
8694 /*******************************************************************************
8696 ******************************************************************************/
8698 bool FullObjectStoreMetadata::HasLiveIndexes() const {
8699 AssertIsOnBackgroundThread();
8701 return std::any_of(mIndexes
.Values().cbegin(), mIndexes
.Values().cend(),
8702 [](const auto& entry
) { return !entry
->mDeleted
; });
8705 SafeRefPtr
<FullDatabaseMetadata
> FullDatabaseMetadata::Duplicate() const {
8706 AssertIsOnBackgroundThread();
8708 // FullDatabaseMetadata contains two hash tables of pointers that we need to
8709 // duplicate so we can't just use the copy constructor.
8710 auto newMetadata
= MakeSafeRefPtr
<FullDatabaseMetadata
>(mCommonMetadata
);
8712 newMetadata
->mDatabaseId
= mDatabaseId
;
8713 newMetadata
->mFilePath
= mFilePath
;
8714 newMetadata
->mNextObjectStoreId
= mNextObjectStoreId
;
8715 newMetadata
->mNextIndexId
= mNextIndexId
;
8717 for (const auto& objectStoreEntry
: mObjectStores
) {
8718 const auto& objectStoreValue
= objectStoreEntry
.GetData();
8720 auto newOSMetadata
= MakeSafeRefPtr
<FullObjectStoreMetadata
>(
8721 objectStoreValue
->mCommonMetadata
, [&objectStoreValue
] {
8722 const auto&& srcLocked
= objectStoreValue
->mAutoIncrementIds
.Lock();
8726 for (const auto& indexEntry
: objectStoreValue
->mIndexes
) {
8727 const auto& value
= indexEntry
.GetData();
8729 auto newIndexMetadata
= MakeSafeRefPtr
<FullIndexMetadata
>();
8731 newIndexMetadata
->mCommonMetadata
= value
->mCommonMetadata
;
8733 if (NS_WARN_IF(!newOSMetadata
->mIndexes
.InsertOrUpdate(
8734 indexEntry
.GetKey(), std::move(newIndexMetadata
), fallible
))) {
8739 MOZ_ASSERT(objectStoreValue
->mIndexes
.Count() ==
8740 newOSMetadata
->mIndexes
.Count());
8742 if (NS_WARN_IF(!newMetadata
->mObjectStores
.InsertOrUpdate(
8743 objectStoreEntry
.GetKey(), std::move(newOSMetadata
), fallible
))) {
8748 MOZ_ASSERT(mObjectStores
.Count() == newMetadata
->mObjectStores
.Count());
8753 DatabaseLoggingInfo::~DatabaseLoggingInfo() {
8754 AssertIsOnBackgroundThread();
8756 if (gLoggingInfoHashtable
) {
8757 const nsID
& backgroundChildLoggingId
=
8758 mLoggingInfo
.backgroundChildLoggingId();
8760 MOZ_ASSERT(gLoggingInfoHashtable
->Get(backgroundChildLoggingId
) == this);
8762 gLoggingInfoHashtable
->Remove(backgroundChildLoggingId
);
8766 /*******************************************************************************
8768 ******************************************************************************/
8770 Factory::Factory(RefPtr
<DatabaseLoggingInfo
> aLoggingInfo
,
8771 const nsACString
& aSystemLocale
)
8772 : mSystemLocale(aSystemLocale
),
8773 mLoggingInfo(std::move(aLoggingInfo
))
8776 mActorDestroyed(false)
8779 AssertIsOnBackgroundThread();
8780 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
8783 Factory::~Factory() { MOZ_ASSERT(mActorDestroyed
); }
8786 SafeRefPtr
<Factory
> Factory::Create(const LoggingInfo
& aLoggingInfo
,
8787 const nsACString
& aSystemLocale
) {
8788 AssertIsOnBackgroundThread();
8789 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
8791 // Balanced in ActoryDestroy().
8792 IncreaseBusyCount();
8794 MOZ_ASSERT(gLoggingInfoHashtable
);
8795 RefPtr
<DatabaseLoggingInfo
> loggingInfo
=
8796 gLoggingInfoHashtable
->WithEntryHandle(
8797 aLoggingInfo
.backgroundChildLoggingId(), [&](auto&& entry
) {
8799 [[maybe_unused
]] const auto& loggingInfo
= entry
.Data();
8800 MOZ_ASSERT(aLoggingInfo
.backgroundChildLoggingId() ==
8803 NS_WARNING_ASSERTION(
8804 aLoggingInfo
.nextTransactionSerialNumber() ==
8805 loggingInfo
->mLoggingInfo
.nextTransactionSerialNumber(),
8806 "NextTransactionSerialNumber doesn't match!");
8807 NS_WARNING_ASSERTION(
8808 aLoggingInfo
.nextVersionChangeTransactionSerialNumber() ==
8809 loggingInfo
->mLoggingInfo
8810 .nextVersionChangeTransactionSerialNumber(),
8811 "NextVersionChangeTransactionSerialNumber doesn't match!");
8812 NS_WARNING_ASSERTION(
8813 aLoggingInfo
.nextRequestSerialNumber() ==
8814 loggingInfo
->mLoggingInfo
.nextRequestSerialNumber(),
8815 "NextRequestSerialNumber doesn't match!");
8818 entry
.Insert(new DatabaseLoggingInfo(aLoggingInfo
));
8821 return do_AddRef(entry
.Data());
8824 return MakeSafeRefPtr
<Factory
>(std::move(loggingInfo
), aSystemLocale
);
8827 void Factory::ActorDestroy(ActorDestroyReason aWhy
) {
8828 AssertIsOnBackgroundThread();
8829 MOZ_ASSERT(!mActorDestroyed
);
8832 mActorDestroyed
= true;
8835 // Match the IncreaseBusyCount in Create().
8836 DecreaseBusyCount();
8839 mozilla::ipc::IPCResult
Factory::RecvDeleteMe() {
8840 AssertIsOnBackgroundThread();
8841 MOZ_ASSERT(!mActorDestroyed
);
8843 QM_WARNONLY_TRY(OkIf(PBackgroundIDBFactoryParent::Send__delete__(this)));
8848 PBackgroundIDBFactoryRequestParent
*
8849 Factory::AllocPBackgroundIDBFactoryRequestParent(
8850 const FactoryRequestParams
& aParams
) {
8851 AssertIsOnBackgroundThread();
8852 MOZ_ASSERT(aParams
.type() != FactoryRequestParams::T__None
);
8854 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
8858 const CommonFactoryRequestParams
* commonParams
;
8860 switch (aParams
.type()) {
8861 case FactoryRequestParams::TOpenDatabaseRequestParams
: {
8862 const OpenDatabaseRequestParams
& params
=
8863 aParams
.get_OpenDatabaseRequestParams();
8864 commonParams
= ¶ms
.commonParams();
8868 case FactoryRequestParams::TDeleteDatabaseRequestParams
: {
8869 const DeleteDatabaseRequestParams
& params
=
8870 aParams
.get_DeleteDatabaseRequestParams();
8871 commonParams
= ¶ms
.commonParams();
8876 MOZ_CRASH("Should never get here!");
8879 MOZ_ASSERT(commonParams
);
8881 const DatabaseMetadata
& metadata
= commonParams
->metadata();
8883 if (NS_AUUF_OR_WARN_IF(!IsValidPersistenceType(metadata
.persistenceType()))) {
8887 const PrincipalInfo
& principalInfo
= commonParams
->principalInfo();
8889 if (NS_AUUF_OR_WARN_IF(!QuotaManager::IsPrincipalInfoValid(principalInfo
))) {
8890 IPC_FAIL(this, "Invalid principal!");
8894 MOZ_ASSERT(principalInfo
.type() == PrincipalInfo::TSystemPrincipalInfo
||
8895 principalInfo
.type() == PrincipalInfo::TContentPrincipalInfo
);
8897 if (NS_AUUF_OR_WARN_IF(
8898 principalInfo
.type() == PrincipalInfo::TSystemPrincipalInfo
&&
8899 metadata
.persistenceType() != PERSISTENCE_TYPE_PERSISTENT
)) {
8903 if (NS_AUUF_OR_WARN_IF(
8904 principalInfo
.type() == PrincipalInfo::TContentPrincipalInfo
&&
8905 QuotaManager::IsOriginInternal(
8906 principalInfo
.get_ContentPrincipalInfo().originNoSuffix()) &&
8907 metadata
.persistenceType() != PERSISTENCE_TYPE_PERSISTENT
)) {
8911 Maybe
<ContentParentId
> contentParentId
= GetContentParentId();
8913 auto actor
= [&]() -> RefPtr
<FactoryRequestOp
> {
8914 if (aParams
.type() == FactoryRequestParams::TOpenDatabaseRequestParams
) {
8915 return MakeRefPtr
<OpenDatabaseOp
>(SafeRefPtrFromThis(), contentParentId
,
8918 return MakeRefPtr
<DeleteDatabaseOp
>(SafeRefPtrFromThis(), contentParentId
,
8923 gFactoryOps
->AppendElement(actor
);
8925 // Balanced in CleanupMetadata() which is/must always called by SendResults().
8926 IncreaseBusyCount();
8928 // Transfer ownership to IPDL.
8929 return actor
.forget().take();
8932 mozilla::ipc::IPCResult
Factory::RecvPBackgroundIDBFactoryRequestConstructor(
8933 PBackgroundIDBFactoryRequestParent
* aActor
,
8934 const FactoryRequestParams
& aParams
) {
8935 AssertIsOnBackgroundThread();
8937 MOZ_ASSERT(aParams
.type() != FactoryRequestParams::T__None
);
8938 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
8940 auto* op
= static_cast<FactoryRequestOp
*>(aActor
);
8942 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(op
));
8946 bool Factory::DeallocPBackgroundIDBFactoryRequestParent(
8947 PBackgroundIDBFactoryRequestParent
* aActor
) {
8948 AssertIsOnBackgroundThread();
8951 // Transfer ownership back from IPDL.
8952 RefPtr
<FactoryRequestOp
> op
=
8953 dont_AddRef(static_cast<FactoryRequestOp
*>(aActor
));
8957 mozilla::ipc::IPCResult
Factory::RecvGetDatabases(
8958 const PersistenceType
& aPersistenceType
,
8959 const PrincipalInfo
& aPrincipalInfo
, GetDatabasesResolver
&& aResolve
) {
8960 AssertIsOnBackgroundThread();
8962 auto ResolveGetDatabasesAndReturn
= [&aResolve
](const nsresult rv
) {
8967 QM_TRY(MOZ_TO_RESULT(!QuotaClient::IsShuttingDownOnBackgroundThread()),
8968 ResolveGetDatabasesAndReturn
);
8970 QM_TRY(MOZ_TO_RESULT(IsValidPersistenceType(aPersistenceType
)),
8973 QM_TRY(MOZ_TO_RESULT(QuotaManager::IsPrincipalInfoValid(aPrincipalInfo
)),
8976 MOZ_ASSERT(aPrincipalInfo
.type() == PrincipalInfo::TSystemPrincipalInfo
||
8977 aPrincipalInfo
.type() == PrincipalInfo::TContentPrincipalInfo
);
8979 PersistenceType persistenceType
=
8980 IDBFactory::GetPersistenceType(aPrincipalInfo
);
8982 QM_TRY(MOZ_TO_RESULT(aPersistenceType
== persistenceType
), QM_IPC_FAIL(this));
8984 Maybe
<ContentParentId
> contentParentId
= GetContentParentId();
8986 auto op
= MakeRefPtr
<GetDatabasesOp
>(SafeRefPtrFromThis(), contentParentId
,
8987 aPersistenceType
, aPrincipalInfo
,
8988 std::move(aResolve
));
8990 gFactoryOps
->AppendElement(op
);
8992 // Balanced in CleanupMetadata() which is/must always called by SendResults().
8993 IncreaseBusyCount();
8995 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(op
));
9000 Maybe
<ContentParentId
> Factory::GetContentParentId() const {
9001 uint64_t childID
= BackgroundParent::GetChildID(Manager());
9003 // If childID is not zero we are dealing with an other-process actor. We
9004 // want to initialize OpenDatabaseOp/DeleteDatabaseOp here with the ID
9005 // (and later also Database) in that case, so Database::IsOwnedByProcess
9006 // can find Databases belonging to a particular content process when
9007 // QuotaClient::AbortOperationsForProcess is called which is currently used
9008 // to abort operations for content processes only.
9009 return Some(ContentParentId(childID
));
9015 /*******************************************************************************
9016 * WaitForTransactionsHelper
9017 ******************************************************************************/
9019 void WaitForTransactionsHelper::WaitForTransactions() {
9020 MOZ_ASSERT(mState
== State::Initial
);
9022 Unused
<< this->Run();
9025 void WaitForTransactionsHelper::MaybeWaitForTransactions() {
9026 AssertIsOnBackgroundThread();
9027 MOZ_ASSERT(mState
== State::Initial
);
9029 RefPtr
<ConnectionPool
> connectionPool
= gConnectionPool
.get();
9030 if (connectionPool
) {
9031 mState
= State::WaitingForTransactions
;
9033 connectionPool
->WaitForDatabaseToComplete(mDatabaseId
, this);
9041 void WaitForTransactionsHelper::CallCallback() {
9042 AssertIsOnBackgroundThread();
9043 MOZ_ASSERT(mState
== State::Initial
||
9044 mState
== State::WaitingForTransactions
);
9046 const nsCOMPtr
<nsIRunnable
> callback
= std::move(mCallback
);
9050 mState
= State::Complete
;
9054 WaitForTransactionsHelper::Run() {
9055 MOZ_ASSERT(mState
!= State::Complete
);
9056 MOZ_ASSERT(mCallback
);
9059 case State::Initial
:
9060 MaybeWaitForTransactions();
9063 case State::WaitingForTransactions
:
9068 MOZ_CRASH("Should never get here!");
9074 /*******************************************************************************
9076 ******************************************************************************/
9078 Database::Database(SafeRefPtr
<Factory
> aFactory
,
9079 const PrincipalInfo
& aPrincipalInfo
,
9080 const Maybe
<ContentParentId
>& aOptionalContentParentId
,
9081 const quota::OriginMetadata
& aOriginMetadata
,
9082 uint32_t aTelemetryId
,
9083 SafeRefPtr
<FullDatabaseMetadata
> aMetadata
,
9084 SafeRefPtr
<DatabaseFileManager
> aFileManager
,
9085 RefPtr
<DirectoryLock
> aDirectoryLock
,
9086 bool aInPrivateBrowsing
,
9087 const Maybe
<const CipherKey
>& aMaybeKey
)
9088 : mFactory(std::move(aFactory
)),
9089 mMetadata(std::move(aMetadata
)),
9090 mFileManager(std::move(aFileManager
)),
9091 mDirectoryLock(std::move(aDirectoryLock
)),
9092 mPrincipalInfo(aPrincipalInfo
),
9093 mOptionalContentParentId(aOptionalContentParentId
),
9094 mOriginMetadata(aOriginMetadata
),
9095 mId(mMetadata
->mDatabaseId
),
9096 mFilePath(mMetadata
->mFilePath
),
9098 mTelemetryId(aTelemetryId
),
9099 mPersistenceType(mMetadata
->mCommonMetadata
.persistenceType()),
9100 mInPrivateBrowsing(aInPrivateBrowsing
),
9101 mBackgroundThread(GetCurrentSerialEventTarget())
9104 mAllBlobsUnmapped(false)
9107 AssertIsOnBackgroundThread();
9108 MOZ_ASSERT(mFactory
);
9109 MOZ_ASSERT(mMetadata
);
9110 MOZ_ASSERT(mFileManager
);
9112 MOZ_ASSERT(mDirectoryLock
);
9113 MOZ_ASSERT(mDirectoryLock
->Id() >= 0);
9114 mDirectoryLockId
= mDirectoryLock
->Id();
9117 template <typename T
>
9118 bool Database::InvalidateAll(const nsTBaseHashSet
<nsPtrHashKey
<T
>>& aTable
) {
9119 AssertIsOnBackgroundThread();
9121 const uint32_t count
= aTable
.Count();
9126 // XXX Does this really need to be fallible?
9127 QM_TRY_INSPECT(const auto& elementsToInvalidate
,
9128 TransformIntoNewArray(
9129 aTable
, [](const auto& entry
) { return entry
; }, fallible
),
9132 IDB_REPORT_INTERNAL_ERR();
9134 for (const auto& elementToInvalidate
: elementsToInvalidate
) {
9135 MOZ_ASSERT(elementToInvalidate
);
9137 elementToInvalidate
->Invalidate();
9143 void Database::Invalidate() {
9144 AssertIsOnBackgroundThread();
9150 mInvalidated
.Flip();
9152 if (mActorWasAlive
&& !mActorDestroyed
) {
9153 Unused
<< SendInvalidate();
9156 QM_WARNONLY_TRY(OkIf(InvalidateAll(mTransactions
)));
9158 MOZ_ALWAYS_TRUE(CloseInternal());
9161 nsresult
Database::EnsureConnection() {
9162 MOZ_ASSERT(!NS_IsMainThread());
9163 MOZ_ASSERT(!IsOnBackgroundThread());
9165 AUTO_PROFILER_LABEL("Database::EnsureConnection", DOM
);
9167 if (!mConnection
|| !mConnection
->HasStorageConnection()) {
9168 QM_TRY_UNWRAP(mConnection
, gConnectionPool
->GetOrCreateConnection(*this));
9171 AssertIsOnConnectionThread();
9176 bool Database::RegisterTransaction(TransactionBase
& aTransaction
) {
9177 AssertIsOnBackgroundThread();
9178 MOZ_ASSERT(!mTransactions
.Contains(&aTransaction
));
9179 MOZ_ASSERT(mDirectoryLock
);
9180 MOZ_ASSERT(!mInvalidated
);
9181 MOZ_ASSERT(!mClosed
);
9183 if (NS_WARN_IF(!mTransactions
.Insert(&aTransaction
, fallible
))) {
9190 void Database::UnregisterTransaction(TransactionBase
& aTransaction
) {
9191 AssertIsOnBackgroundThread();
9192 MOZ_ASSERT(mTransactions
.Contains(&aTransaction
));
9194 mTransactions
.Remove(&aTransaction
);
9196 MaybeCloseConnection();
9199 void Database::SetActorAlive() {
9200 AssertIsOnBackgroundThread();
9201 MOZ_ASSERT(!mActorDestroyed
);
9203 mActorWasAlive
.Flip();
9206 void Database::MapBlob(const IPCBlob
& aIPCBlob
,
9207 SafeRefPtr
<DatabaseFileInfo
> aFileInfo
) {
9208 AssertIsOnBackgroundThread();
9210 const RemoteLazyStream
& stream
= aIPCBlob
.inputStream();
9211 MOZ_ASSERT(stream
.type() == RemoteLazyStream::TRemoteLazyInputStream
);
9214 MOZ_ALWAYS_SUCCEEDS(
9215 stream
.get_RemoteLazyInputStream()->GetInternalStreamID(id
));
9217 MOZ_ASSERT(!mMappedBlobs
.Contains(id
));
9218 mMappedBlobs
.InsertOrUpdate(id
, std::move(aFileInfo
));
9220 RefPtr
<UnmapBlobCallback
> callback
=
9221 new UnmapBlobCallback(SafeRefPtrFromThis());
9223 auto storage
= RemoteLazyInputStreamStorage::Get();
9224 MOZ_ASSERT(storage
.isOk());
9225 storage
.inspect()->StoreCallback(id
, callback
);
9228 void Database::Stringify(nsACString
& aResult
) const {
9229 AssertIsOnBackgroundThread();
9231 constexpr auto kQuotaGenericDelimiterString
= "|"_ns
;
9234 "DirectoryLock:"_ns
+ IntToCString(!!mDirectoryLock
) +
9235 kQuotaGenericDelimiterString
+
9237 "Transactions:"_ns
+ IntToCString(mTransactions
.Count()) +
9238 kQuotaGenericDelimiterString
+
9240 "OtherProcessActor:"_ns
+
9242 BackgroundParent::IsOtherProcessActor(GetBackgroundParent())) +
9243 kQuotaGenericDelimiterString
+
9245 "Origin:"_ns
+ AnonymizedOriginString(mOriginMetadata
.mOrigin
) +
9246 kQuotaGenericDelimiterString
+
9248 "PersistenceType:"_ns
+ PersistenceTypeToString(mPersistenceType
) +
9249 kQuotaGenericDelimiterString
+
9251 "Closed:"_ns
+ IntToCString(static_cast<bool>(mClosed
)) +
9252 kQuotaGenericDelimiterString
+
9254 "Invalidated:"_ns
+ IntToCString(static_cast<bool>(mInvalidated
)) +
9255 kQuotaGenericDelimiterString
+
9257 "ActorWasAlive:"_ns
+ IntToCString(static_cast<bool>(mActorWasAlive
)) +
9258 kQuotaGenericDelimiterString
+
9260 "ActorDestroyed:"_ns
+ IntToCString(static_cast<bool>(mActorDestroyed
)));
9263 SafeRefPtr
<DatabaseFileInfo
> Database::GetBlob(const IPCBlob
& aIPCBlob
) {
9264 AssertIsOnBackgroundThread();
9266 RefPtr
<RemoteLazyInputStream
> lazyStream
;
9267 switch (aIPCBlob
.inputStream().type()) {
9268 case RemoteLazyStream::TIPCStream
: {
9269 const InputStreamParams
& inputStreamParams
=
9270 aIPCBlob
.inputStream().get_IPCStream().stream();
9271 if (inputStreamParams
.type() !=
9272 InputStreamParams::TRemoteLazyInputStreamParams
) {
9275 lazyStream
= inputStreamParams
.get_RemoteLazyInputStreamParams().stream();
9278 case RemoteLazyStream::TRemoteLazyInputStream
:
9279 lazyStream
= aIPCBlob
.inputStream().get_RemoteLazyInputStream();
9282 MOZ_ASSERT_UNREACHABLE("Unknown RemoteLazyStream type");
9287 MOZ_ASSERT_UNREACHABLE("Unexpected null stream");
9292 nsresult rv
= lazyStream
->GetInternalStreamID(id
);
9293 if (NS_FAILED(rv
)) {
9294 MOZ_ASSERT_UNREACHABLE(
9295 "Received RemoteLazyInputStream doesn't have an actor connection");
9299 const auto fileInfo
= mMappedBlobs
.Lookup(id
);
9300 return fileInfo
? fileInfo
->clonePtr() : nullptr;
9303 void Database::UnmapBlob(const nsID
& aID
) {
9304 AssertIsOnBackgroundThread();
9306 MOZ_ASSERT_IF(!mAllBlobsUnmapped
, mMappedBlobs
.Contains(aID
));
9307 mMappedBlobs
.Remove(aID
);
9310 void Database::UnmapAllBlobs() {
9311 AssertIsOnBackgroundThread();
9314 mAllBlobsUnmapped
= true;
9317 mMappedBlobs
.Clear();
9320 bool Database::CloseInternal() {
9321 AssertIsOnBackgroundThread();
9324 if (NS_WARN_IF(!IsInvalidated())) {
9325 // Signal misbehaving child for sending the close message twice.
9329 // Ignore harmless race when we just invalidated the database.
9335 if (gConnectionPool
) {
9336 gConnectionPool
->CloseDatabaseWhenIdle(Id());
9339 DatabaseActorInfo
* info
;
9340 MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable
->Get(Id(), &info
));
9342 MOZ_ASSERT(info
->mLiveDatabases
.Contains(this));
9344 if (info
->mWaitingFactoryOp
) {
9345 info
->mWaitingFactoryOp
->NoteDatabaseClosed(this);
9348 MaybeCloseConnection();
9353 void Database::MaybeCloseConnection() {
9354 AssertIsOnBackgroundThread();
9356 if (!mTransactions
.Count() && IsClosed() && mDirectoryLock
) {
9357 nsCOMPtr
<nsIRunnable
> callback
=
9358 NewRunnableMethod("dom::indexedDB::Database::ConnectionClosedCallback",
9359 this, &Database::ConnectionClosedCallback
);
9361 RefPtr
<WaitForTransactionsHelper
> helper
=
9362 new WaitForTransactionsHelper(Id(), callback
);
9363 helper
->WaitForTransactions();
9367 void Database::ConnectionClosedCallback() {
9368 AssertIsOnBackgroundThread();
9369 MOZ_ASSERT(mClosed
);
9370 MOZ_ASSERT(!mTransactions
.Count());
9372 mDirectoryLock
= nullptr;
9378 if (IsInvalidated() && IsActorAlive()) {
9379 // Step 3 and 4 of "5.2 Closing a Database":
9380 // 1. Wait for all transactions to complete.
9381 // 2. Fire a close event if forced flag is set, i.e., IsInvalidated() in our
9383 Unused
<< SendCloseAfterInvalidationComplete();
9387 void Database::CleanupMetadata() {
9388 AssertIsOnBackgroundThread();
9390 DatabaseActorInfo
* info
;
9391 MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable
->Get(Id(), &info
));
9392 MOZ_ALWAYS_TRUE(info
->mLiveDatabases
.RemoveElement(this));
9394 QuotaManager::MaybeRecordQuotaClientShutdownStep(
9395 quota::Client::IDB
, "Live database entry removed"_ns
);
9397 if (info
->mLiveDatabases
.IsEmpty()) {
9398 MOZ_ASSERT(!info
->mWaitingFactoryOp
||
9399 !info
->mWaitingFactoryOp
->HasBlockedDatabases());
9400 gLiveDatabaseHashtable
->Remove(Id());
9402 QuotaManager::MaybeRecordQuotaClientShutdownStep(
9403 quota::Client::IDB
, "gLiveDatabaseHashtable entry removed"_ns
);
9406 // Match the IncreaseBusyCount in OpenDatabaseOp::EnsureDatabaseActor().
9407 DecreaseBusyCount();
9410 void Database::ActorDestroy(ActorDestroyReason aWhy
) {
9411 AssertIsOnBackgroundThread();
9413 mActorDestroyed
.Flip();
9415 if (!IsInvalidated()) {
9420 PBackgroundIDBDatabaseFileParent
*
9421 Database::AllocPBackgroundIDBDatabaseFileParent(const IPCBlob
& aIPCBlob
) {
9422 AssertIsOnBackgroundThread();
9424 SafeRefPtr
<DatabaseFileInfo
> fileInfo
= GetBlob(aIPCBlob
);
9425 RefPtr
<DatabaseFile
> actor
;
9428 actor
= new DatabaseFile(std::move(fileInfo
));
9430 // This is a blob we haven't seen before.
9431 fileInfo
= mFileManager
->CreateFileInfo();
9432 if (NS_WARN_IF(!fileInfo
)) {
9436 actor
= new DatabaseFile(IPCBlobUtils::Deserialize(aIPCBlob
),
9437 std::move(fileInfo
));
9442 return actor
.forget().take();
9445 bool Database::DeallocPBackgroundIDBDatabaseFileParent(
9446 PBackgroundIDBDatabaseFileParent
* aActor
) {
9447 AssertIsOnBackgroundThread();
9450 RefPtr
<DatabaseFile
> actor
= dont_AddRef(static_cast<DatabaseFile
*>(aActor
));
9454 already_AddRefed
<PBackgroundIDBTransactionParent
>
9455 Database::AllocPBackgroundIDBTransactionParent(
9456 const nsTArray
<nsString
>& aObjectStoreNames
, const Mode
& aMode
,
9457 const Durability
& aDurability
) {
9458 AssertIsOnBackgroundThread();
9460 // Once a database is closed it must not try to open new transactions.
9461 if (NS_WARN_IF(mClosed
)) {
9462 MOZ_ASSERT_UNLESS_FUZZING(mInvalidated
);
9466 if (NS_AUUF_OR_WARN_IF(aObjectStoreNames
.IsEmpty())) {
9470 if (NS_AUUF_OR_WARN_IF(aMode
!= IDBTransaction::Mode::ReadOnly
&&
9471 aMode
!= IDBTransaction::Mode::ReadWrite
&&
9472 aMode
!= IDBTransaction::Mode::ReadWriteFlush
&&
9473 aMode
!= IDBTransaction::Mode::Cleanup
)) {
9477 if (NS_AUUF_OR_WARN_IF(aDurability
!= IDBTransaction::Durability::Default
&&
9478 aDurability
!= IDBTransaction::Durability::Strict
&&
9479 aDurability
!= IDBTransaction::Durability::Relaxed
)) {
9483 const ObjectStoreTable
& objectStores
= mMetadata
->mObjectStores
;
9484 const uint32_t nameCount
= aObjectStoreNames
.Length();
9486 if (NS_AUUF_OR_WARN_IF(nameCount
> objectStores
.Count())) {
9491 auto objectStoreMetadatas
,
9492 TransformIntoNewArrayAbortOnErr(
9494 [lastName
= Maybe
<const nsString
&>{},
9495 &objectStores
](const nsString
& name
) mutable
9496 -> mozilla::Result
<SafeRefPtr
<FullObjectStoreMetadata
>, nsresult
> {
9498 // Make sure that this name is sorted properly and not a
9500 if (NS_AUUF_OR_WARN_IF(name
<= lastName
.ref())) {
9501 return Err(NS_ERROR_FAILURE
);
9504 lastName
= SomeRef(name
);
9506 const auto foundIt
=
9507 std::find_if(objectStores
.cbegin(), objectStores
.cend(),
9508 [&name
](const auto& entry
) {
9509 const auto& value
= entry
.GetData();
9510 MOZ_ASSERT(entry
.GetKey());
9511 return name
== value
->mCommonMetadata
.name() &&
9514 if (foundIt
== objectStores
.cend()) {
9515 MOZ_ASSERT_UNLESS_FUZZING(false, "ObjectStore not found.");
9516 return Err(NS_ERROR_FAILURE
);
9519 return foundIt
->GetData().clonePtr();
9524 return MakeSafeRefPtr
<NormalTransaction
>(SafeRefPtrFromThis(), aMode
,
9526 std::move(objectStoreMetadatas
))
9530 mozilla::ipc::IPCResult
Database::RecvPBackgroundIDBTransactionConstructor(
9531 PBackgroundIDBTransactionParent
* aActor
,
9532 nsTArray
<nsString
>&& aObjectStoreNames
, const Mode
& aMode
,
9533 const Durability
& aDurability
) { // TODO: See bug 1883045
9534 AssertIsOnBackgroundThread();
9536 MOZ_ASSERT(!aObjectStoreNames
.IsEmpty());
9537 MOZ_ASSERT(aMode
== IDBTransaction::Mode::ReadOnly
||
9538 aMode
== IDBTransaction::Mode::ReadWrite
||
9539 aMode
== IDBTransaction::Mode::ReadWriteFlush
||
9540 aMode
== IDBTransaction::Mode::Cleanup
);
9541 MOZ_ASSERT(aDurability
== IDBTransaction::Durability::Default
||
9542 aDurability
== IDBTransaction::Durability::Strict
||
9543 aDurability
== IDBTransaction::Durability::Relaxed
);
9544 MOZ_ASSERT(!mClosed
);
9546 if (IsInvalidated()) {
9547 // This is an expected race. We don't want the child to die here, just don't
9548 // actually do any work.
9552 if (!gConnectionPool
) {
9553 gConnectionPool
= new ConnectionPool();
9556 auto* transaction
= static_cast<NormalTransaction
*>(aActor
);
9558 RefPtr
<StartTransactionOp
> startOp
= new StartTransactionOp(
9559 SafeRefPtr
{transaction
, AcquireStrongRefFromRawPtr
{}});
9561 uint64_t transactionId
= startOp
->StartOnConnectionPool(
9562 GetLoggingInfo()->Id(), mMetadata
->mDatabaseId
,
9563 transaction
->LoggingSerialNumber(), aObjectStoreNames
,
9564 aMode
!= IDBTransaction::Mode::ReadOnly
);
9566 transaction
->Init(transactionId
);
9568 if (NS_WARN_IF(!RegisterTransaction(*transaction
))) {
9569 IDB_REPORT_INTERNAL_ERR();
9570 transaction
->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
, /* aForce */ false);
9577 mozilla::ipc::IPCResult
Database::RecvDeleteMe() {
9578 AssertIsOnBackgroundThread();
9579 MOZ_ASSERT(!mActorDestroyed
);
9581 QM_WARNONLY_TRY(OkIf(PBackgroundIDBDatabaseParent::Send__delete__(this)));
9586 mozilla::ipc::IPCResult
Database::RecvBlocked() {
9587 AssertIsOnBackgroundThread();
9589 if (NS_WARN_IF(mClosed
)) {
9590 return IPC_FAIL(this, "Database already closed!");
9593 DatabaseActorInfo
* info
;
9594 MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable
->Get(Id(), &info
));
9595 MOZ_ASSERT(info
->mLiveDatabases
.Contains(this));
9597 if (NS_WARN_IF(!info
->mWaitingFactoryOp
)) {
9598 return IPC_FAIL(this, "Database info has no mWaitingFactoryOp!");
9601 info
->mWaitingFactoryOp
->NoteDatabaseBlocked(this);
9606 mozilla::ipc::IPCResult
Database::RecvClose() {
9607 AssertIsOnBackgroundThread();
9609 if (NS_WARN_IF(!CloseInternal())) {
9610 return IPC_FAIL(this, "CloseInternal failed!");
9616 void Database::StartTransactionOp::RunOnConnectionThread() {
9617 MOZ_ASSERT(!IsOnBackgroundThread());
9618 MOZ_ASSERT(!HasFailed());
9620 IDB_LOG_MARK_PARENT_TRANSACTION("Beginning database work", "DB Start",
9621 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
9622 mTransactionLoggingSerialNumber
);
9624 TransactionDatabaseOperationBase::RunOnConnectionThread();
9627 nsresult
Database::StartTransactionOp::DoDatabaseWork(
9628 DatabaseConnection
* aConnection
) {
9629 MOZ_ASSERT(aConnection
);
9630 aConnection
->AssertIsOnConnectionThread();
9632 Transaction().SetActiveOnConnectionThread();
9634 if (Transaction().GetMode() == IDBTransaction::Mode::Cleanup
) {
9635 DebugOnly
<nsresult
> rv
= aConnection
->DisableQuotaChecks();
9636 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
9637 "DisableQuotaChecks failed, trying to continue "
9638 "cleanup transaction with quota checks enabled");
9641 if (Transaction().GetMode() != IDBTransaction::Mode::ReadOnly
) {
9642 QM_TRY(MOZ_TO_RESULT(aConnection
->BeginWriteTransaction()));
9648 nsresult
Database::StartTransactionOp::SendSuccessResult() {
9649 // We don't need to do anything here.
9653 bool Database::StartTransactionOp::SendFailureResult(
9654 nsresult
/* aResultCode */) {
9655 IDB_REPORT_INTERNAL_ERR();
9657 // Abort the transaction.
9661 void Database::StartTransactionOp::Cleanup() {
9663 // StartTransactionOp is not a normal database operation that is tied to an
9664 // actor. Do this to make our assertions happy.
9665 NoteActorDestroyed();
9668 TransactionDatabaseOperationBase::Cleanup();
9671 /*******************************************************************************
9673 ******************************************************************************/
9675 TransactionBase::TransactionBase(SafeRefPtr
<Database
> aDatabase
, Mode aMode
,
9676 Durability aDurability
)
9677 : mDatabase(std::move(aDatabase
)),
9678 mDatabaseId(mDatabase
->Id()),
9679 mLoggingSerialNumber(
9680 mDatabase
->GetLoggingInfo()->NextTransactionSN(aMode
)),
9681 mActiveRequestCount(0),
9682 mInvalidatedOnAnyThread(false),
9684 mDurability(aDurability
),
9685 mResultCode(NS_OK
) {
9686 AssertIsOnBackgroundThread();
9687 MOZ_ASSERT(mDatabase
);
9688 MOZ_ASSERT(mLoggingSerialNumber
);
9691 TransactionBase::~TransactionBase() {
9692 MOZ_ASSERT(!mActiveRequestCount
);
9693 MOZ_ASSERT(mActorDestroyed
);
9694 MOZ_ASSERT_IF(mInitialized
, mCommittedOrAborted
);
9697 void TransactionBase::Abort(nsresult aResultCode
, bool aForce
) {
9698 AssertIsOnBackgroundThread();
9699 MOZ_ASSERT(NS_FAILED(aResultCode
));
9701 if (NS_SUCCEEDED(mResultCode
)) {
9702 mResultCode
= aResultCode
;
9706 mForceAborted
.EnsureFlipped();
9709 MaybeCommitOrAbort();
9712 mozilla::ipc::IPCResult
TransactionBase::RecvCommit(
9713 IProtocol
* aActor
, const Maybe
<int64_t> aLastRequest
) {
9714 AssertIsOnBackgroundThread();
9716 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
9718 aActor
, "Attempt to commit an already comitted/aborted transaction!");
9721 mCommitOrAbortReceived
.Flip();
9722 mLastRequestBeforeCommit
.init(aLastRequest
);
9723 MaybeCommitOrAbort();
9728 mozilla::ipc::IPCResult
TransactionBase::RecvAbort(IProtocol
* aActor
,
9729 nsresult aResultCode
) {
9730 AssertIsOnBackgroundThread();
9732 if (NS_WARN_IF(NS_SUCCEEDED(aResultCode
))) {
9733 return IPC_FAIL(aActor
, "aResultCode must not be a success code!");
9736 if (NS_WARN_IF(NS_ERROR_GET_MODULE(aResultCode
) !=
9737 NS_ERROR_MODULE_DOM_INDEXEDDB
)) {
9738 return IPC_FAIL(aActor
, "aResultCode does not refer to IndexedDB!");
9741 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
9743 aActor
, "Attempt to abort an already comitted/aborted transaction!");
9746 mCommitOrAbortReceived
.Flip();
9747 Abort(aResultCode
, /* aForce */ false);
9752 void TransactionBase::CommitOrAbort() {
9753 AssertIsOnBackgroundThread();
9755 mCommittedOrAborted
.Flip();
9757 if (!mInitialized
) {
9761 // In case of a failed request and explicitly committed transaction, abort
9762 // (cf. https://w3c.github.io/IndexedDB/#async-execute-request step 5.3
9763 // vs. 5.4). It's worth emphasizing this can only happen here when we are
9764 // committing explicitly, otherwise the decision is made by the child.
9765 if (NS_SUCCEEDED(mResultCode
) && mLastFailedRequest
&&
9766 *mLastRequestBeforeCommit
&&
9767 *mLastFailedRequest
== **mLastRequestBeforeCommit
) {
9768 mResultCode
= NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
;
9771 RefPtr
<CommitOp
> commitOp
=
9772 new CommitOp(SafeRefPtrFromThis(), ClampResultCode(mResultCode
));
9774 gConnectionPool
->Finish(TransactionId(), commitOp
);
9777 SafeRefPtr
<FullObjectStoreMetadata
>
9778 TransactionBase::GetMetadataForObjectStoreId(
9779 IndexOrObjectStoreId aObjectStoreId
) const {
9780 AssertIsOnBackgroundThread();
9781 MOZ_ASSERT(aObjectStoreId
);
9783 if (!aObjectStoreId
) {
9787 auto metadata
= mDatabase
->Metadata().mObjectStores
.Lookup(aObjectStoreId
);
9788 if (!metadata
|| (*metadata
)->mDeleted
) {
9792 MOZ_ASSERT((*metadata
)->mCommonMetadata
.id() == aObjectStoreId
);
9794 return metadata
->clonePtr();
9797 SafeRefPtr
<FullIndexMetadata
> TransactionBase::GetMetadataForIndexId(
9798 FullObjectStoreMetadata
& aObjectStoreMetadata
,
9799 IndexOrObjectStoreId aIndexId
) const {
9800 AssertIsOnBackgroundThread();
9801 MOZ_ASSERT(aIndexId
);
9807 auto metadata
= aObjectStoreMetadata
.mIndexes
.Lookup(aIndexId
);
9808 if (!metadata
|| (*metadata
)->mDeleted
) {
9812 MOZ_ASSERT((*metadata
)->mCommonMetadata
.id() == aIndexId
);
9814 return metadata
->clonePtr();
9817 void TransactionBase::NoteModifiedAutoIncrementObjectStore(
9818 const SafeRefPtr
<FullObjectStoreMetadata
>& aMetadata
) {
9819 AssertIsOnConnectionThread();
9821 if (!mModifiedAutoIncrementObjectStoreMetadataArray
.Contains(aMetadata
)) {
9822 mModifiedAutoIncrementObjectStoreMetadataArray
.AppendElement(
9823 aMetadata
.clonePtr());
9827 void TransactionBase::ForgetModifiedAutoIncrementObjectStore(
9828 FullObjectStoreMetadata
& aMetadata
) {
9829 AssertIsOnConnectionThread();
9831 mModifiedAutoIncrementObjectStoreMetadataArray
.RemoveElement(&aMetadata
);
9834 bool TransactionBase::VerifyRequestParams(const RequestParams
& aParams
) const {
9835 AssertIsOnBackgroundThread();
9836 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
9838 switch (aParams
.type()) {
9839 case RequestParams::TObjectStoreAddParams
: {
9840 const ObjectStoreAddPutParams
& params
=
9841 aParams
.get_ObjectStoreAddParams().commonParams();
9842 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
))) {
9848 case RequestParams::TObjectStorePutParams
: {
9849 const ObjectStoreAddPutParams
& params
=
9850 aParams
.get_ObjectStorePutParams().commonParams();
9851 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
))) {
9857 case RequestParams::TObjectStoreGetParams
: {
9858 const ObjectStoreGetParams
& params
= aParams
.get_ObjectStoreGetParams();
9859 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
9860 GetMetadataForObjectStoreId(params
.objectStoreId());
9861 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
9864 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.keyRange()))) {
9870 case RequestParams::TObjectStoreGetKeyParams
: {
9871 const ObjectStoreGetKeyParams
& params
=
9872 aParams
.get_ObjectStoreGetKeyParams();
9873 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
9874 GetMetadataForObjectStoreId(params
.objectStoreId());
9875 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
9878 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.keyRange()))) {
9884 case RequestParams::TObjectStoreGetAllParams
: {
9885 const ObjectStoreGetAllParams
& params
=
9886 aParams
.get_ObjectStoreGetAllParams();
9887 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
9888 GetMetadataForObjectStoreId(params
.objectStoreId());
9889 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
9892 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.optionalKeyRange()))) {
9898 case RequestParams::TObjectStoreGetAllKeysParams
: {
9899 const ObjectStoreGetAllKeysParams
& params
=
9900 aParams
.get_ObjectStoreGetAllKeysParams();
9901 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
9902 GetMetadataForObjectStoreId(params
.objectStoreId());
9903 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
9906 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.optionalKeyRange()))) {
9912 case RequestParams::TObjectStoreDeleteParams
: {
9913 if (NS_AUUF_OR_WARN_IF(mMode
!= IDBTransaction::Mode::ReadWrite
&&
9914 mMode
!= IDBTransaction::Mode::ReadWriteFlush
&&
9915 mMode
!= IDBTransaction::Mode::Cleanup
&&
9916 mMode
!= IDBTransaction::Mode::VersionChange
)) {
9920 const ObjectStoreDeleteParams
& params
=
9921 aParams
.get_ObjectStoreDeleteParams();
9922 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
9923 GetMetadataForObjectStoreId(params
.objectStoreId());
9924 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
9927 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.keyRange()))) {
9933 case RequestParams::TObjectStoreClearParams
: {
9934 if (NS_AUUF_OR_WARN_IF(mMode
!= IDBTransaction::Mode::ReadWrite
&&
9935 mMode
!= IDBTransaction::Mode::ReadWriteFlush
&&
9936 mMode
!= IDBTransaction::Mode::Cleanup
&&
9937 mMode
!= IDBTransaction::Mode::VersionChange
)) {
9941 const ObjectStoreClearParams
& params
=
9942 aParams
.get_ObjectStoreClearParams();
9943 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
9944 GetMetadataForObjectStoreId(params
.objectStoreId());
9945 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
9951 case RequestParams::TObjectStoreCountParams
: {
9952 const ObjectStoreCountParams
& params
=
9953 aParams
.get_ObjectStoreCountParams();
9954 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
9955 GetMetadataForObjectStoreId(params
.objectStoreId());
9956 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
9959 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.optionalKeyRange()))) {
9965 case RequestParams::TIndexGetParams
: {
9966 const IndexGetParams
& params
= aParams
.get_IndexGetParams();
9967 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
9968 GetMetadataForObjectStoreId(params
.objectStoreId());
9969 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
9972 const SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
9973 GetMetadataForIndexId(*objectStoreMetadata
, params
.indexId());
9974 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
9977 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.keyRange()))) {
9983 case RequestParams::TIndexGetKeyParams
: {
9984 const IndexGetKeyParams
& params
= aParams
.get_IndexGetKeyParams();
9985 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
9986 GetMetadataForObjectStoreId(params
.objectStoreId());
9987 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
9990 const SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
9991 GetMetadataForIndexId(*objectStoreMetadata
, params
.indexId());
9992 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
9995 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.keyRange()))) {
10001 case RequestParams::TIndexGetAllParams
: {
10002 const IndexGetAllParams
& params
= aParams
.get_IndexGetAllParams();
10003 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10004 GetMetadataForObjectStoreId(params
.objectStoreId());
10005 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10008 const SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
10009 GetMetadataForIndexId(*objectStoreMetadata
, params
.indexId());
10010 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
10013 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.optionalKeyRange()))) {
10019 case RequestParams::TIndexGetAllKeysParams
: {
10020 const IndexGetAllKeysParams
& params
= aParams
.get_IndexGetAllKeysParams();
10021 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10022 GetMetadataForObjectStoreId(params
.objectStoreId());
10023 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10026 const SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
10027 GetMetadataForIndexId(*objectStoreMetadata
, params
.indexId());
10028 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
10031 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.optionalKeyRange()))) {
10037 case RequestParams::TIndexCountParams
: {
10038 const IndexCountParams
& params
= aParams
.get_IndexCountParams();
10039 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
10040 GetMetadataForObjectStoreId(params
.objectStoreId());
10041 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10044 const SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
10045 GetMetadataForIndexId(*objectStoreMetadata
, params
.indexId());
10046 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
10049 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params
.optionalKeyRange()))) {
10056 MOZ_CRASH("Should never get here!");
10062 bool TransactionBase::VerifyRequestParams(
10063 const SerializedKeyRange
& aParams
) const {
10064 AssertIsOnBackgroundThread();
10066 // XXX Check more here?
10068 if (aParams
.isOnly()) {
10069 if (NS_AUUF_OR_WARN_IF(aParams
.lower().IsUnset())) {
10072 if (NS_AUUF_OR_WARN_IF(!aParams
.upper().IsUnset())) {
10075 if (NS_AUUF_OR_WARN_IF(aParams
.lowerOpen())) {
10078 if (NS_AUUF_OR_WARN_IF(aParams
.upperOpen())) {
10081 } else if (NS_AUUF_OR_WARN_IF(aParams
.lower().IsUnset() &&
10082 aParams
.upper().IsUnset())) {
10089 bool TransactionBase::VerifyRequestParams(
10090 const ObjectStoreAddPutParams
& aParams
) const {
10091 AssertIsOnBackgroundThread();
10093 if (NS_AUUF_OR_WARN_IF(mMode
!= IDBTransaction::Mode::ReadWrite
&&
10094 mMode
!= IDBTransaction::Mode::ReadWriteFlush
&&
10095 mMode
!= IDBTransaction::Mode::VersionChange
)) {
10099 SafeRefPtr
<FullObjectStoreMetadata
> objMetadata
=
10100 GetMetadataForObjectStoreId(aParams
.objectStoreId());
10101 if (NS_AUUF_OR_WARN_IF(!objMetadata
)) {
10105 if (NS_AUUF_OR_WARN_IF(!aParams
.cloneInfo().data().data
.Size())) {
10109 if (objMetadata
->mCommonMetadata
.autoIncrement() &&
10110 objMetadata
->mCommonMetadata
.keyPath().IsValid() &&
10111 aParams
.key().IsUnset()) {
10112 const SerializedStructuredCloneWriteInfo
& cloneInfo
= aParams
.cloneInfo();
10114 if (NS_AUUF_OR_WARN_IF(!cloneInfo
.offsetToKeyProp())) {
10118 if (NS_AUUF_OR_WARN_IF(cloneInfo
.data().data
.Size() < sizeof(uint64_t))) {
10122 if (NS_AUUF_OR_WARN_IF(cloneInfo
.offsetToKeyProp() >
10123 (cloneInfo
.data().data
.Size() - sizeof(uint64_t)))) {
10126 } else if (NS_AUUF_OR_WARN_IF(aParams
.cloneInfo().offsetToKeyProp())) {
10130 for (const auto& updateInfo
: aParams
.indexUpdateInfos()) {
10131 SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
10132 GetMetadataForIndexId(*objMetadata
, updateInfo
.indexId());
10133 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
10137 if (NS_AUUF_OR_WARN_IF(updateInfo
.value().IsUnset())) {
10141 MOZ_ASSERT(!updateInfo
.value().GetBuffer().IsEmpty());
10144 for (const FileAddInfo
& fileAddInfo
: aParams
.fileAddInfos()) {
10145 const PBackgroundIDBDatabaseFileParent
* file
=
10146 fileAddInfo
.file().AsParent();
10148 switch (fileAddInfo
.type()) {
10149 case StructuredCloneFileBase::eBlob
:
10150 if (NS_AUUF_OR_WARN_IF(!file
)) {
10155 case StructuredCloneFileBase::eMutableFile
: {
10159 case StructuredCloneFileBase::eStructuredClone
:
10160 case StructuredCloneFileBase::eWasmBytecode
:
10161 case StructuredCloneFileBase::eWasmCompiled
:
10162 case StructuredCloneFileBase::eEndGuard
:
10163 MOZ_ASSERT_UNLESS_FUZZING(false, "Unsupported.");
10167 MOZ_CRASH("Should never get here!");
10174 bool TransactionBase::VerifyRequestParams(
10175 const Maybe
<SerializedKeyRange
>& aParams
) const {
10176 AssertIsOnBackgroundThread();
10178 if (aParams
.isSome()) {
10179 if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(aParams
.ref()))) {
10187 void TransactionBase::NoteActiveRequest() {
10188 AssertIsOnBackgroundThread();
10189 MOZ_ASSERT(mActiveRequestCount
< UINT64_MAX
);
10191 mActiveRequestCount
++;
10194 void TransactionBase::NoteFinishedRequest(const int64_t aRequestId
,
10195 const nsresult aResultCode
) {
10196 AssertIsOnBackgroundThread();
10197 MOZ_ASSERT(mActiveRequestCount
);
10199 mActiveRequestCount
--;
10201 if (NS_FAILED(aResultCode
)) {
10202 mLastFailedRequest
= Some(aRequestId
);
10205 MaybeCommitOrAbort();
10208 void TransactionBase::Invalidate() {
10209 AssertIsOnBackgroundThread();
10210 MOZ_ASSERT(mInvalidated
== mInvalidatedOnAnyThread
);
10212 if (!mInvalidated
) {
10213 mInvalidated
.Flip();
10214 mInvalidatedOnAnyThread
= true;
10216 Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
, /* aForce */ false);
10220 PBackgroundIDBRequestParent
* TransactionBase::AllocRequest(
10221 const int64_t aRequestId
, RequestParams
&& aParams
, bool aTrustParams
) {
10222 AssertIsOnBackgroundThread();
10223 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
10226 // Always verify parameters in DEBUG builds!
10227 aTrustParams
= false;
10230 if (NS_AUUF_OR_WARN_IF(!aTrustParams
&& !VerifyRequestParams(aParams
))) {
10234 if (NS_AUUF_OR_WARN_IF(mCommitOrAbortReceived
)) {
10238 RefPtr
<NormalTransactionOp
> actor
;
10240 switch (aParams
.type()) {
10241 case RequestParams::TObjectStoreAddParams
:
10242 case RequestParams::TObjectStorePutParams
:
10243 actor
= new ObjectStoreAddOrPutRequestOp(SafeRefPtrFromThis(), aRequestId
,
10244 std::move(aParams
));
10247 case RequestParams::TObjectStoreGetParams
:
10249 new ObjectStoreGetRequestOp(SafeRefPtrFromThis(), aRequestId
, aParams
,
10250 /* aGetAll */ false);
10253 case RequestParams::TObjectStoreGetAllParams
:
10255 new ObjectStoreGetRequestOp(SafeRefPtrFromThis(), aRequestId
, aParams
,
10256 /* aGetAll */ true);
10259 case RequestParams::TObjectStoreGetKeyParams
:
10260 actor
= new ObjectStoreGetKeyRequestOp(SafeRefPtrFromThis(), aRequestId
,
10262 /* aGetAll */ false);
10265 case RequestParams::TObjectStoreGetAllKeysParams
:
10266 actor
= new ObjectStoreGetKeyRequestOp(SafeRefPtrFromThis(), aRequestId
,
10268 /* aGetAll */ true);
10271 case RequestParams::TObjectStoreDeleteParams
:
10273 new ObjectStoreDeleteRequestOp(SafeRefPtrFromThis(), aRequestId
,
10274 aParams
.get_ObjectStoreDeleteParams());
10277 case RequestParams::TObjectStoreClearParams
:
10279 new ObjectStoreClearRequestOp(SafeRefPtrFromThis(), aRequestId
,
10280 aParams
.get_ObjectStoreClearParams());
10283 case RequestParams::TObjectStoreCountParams
:
10285 new ObjectStoreCountRequestOp(SafeRefPtrFromThis(), aRequestId
,
10286 aParams
.get_ObjectStoreCountParams());
10289 case RequestParams::TIndexGetParams
:
10290 actor
= new IndexGetRequestOp(SafeRefPtrFromThis(), aRequestId
, aParams
,
10291 /* aGetAll */ false);
10294 case RequestParams::TIndexGetKeyParams
:
10296 new IndexGetKeyRequestOp(SafeRefPtrFromThis(), aRequestId
, aParams
,
10297 /* aGetAll */ false);
10300 case RequestParams::TIndexGetAllParams
:
10301 actor
= new IndexGetRequestOp(SafeRefPtrFromThis(), aRequestId
, aParams
,
10302 /* aGetAll */ true);
10305 case RequestParams::TIndexGetAllKeysParams
:
10307 new IndexGetKeyRequestOp(SafeRefPtrFromThis(), aRequestId
, aParams
,
10308 /* aGetAll */ true);
10311 case RequestParams::TIndexCountParams
:
10313 new IndexCountRequestOp(SafeRefPtrFromThis(), aRequestId
, aParams
);
10317 MOZ_CRASH("Should never get here!");
10322 // Transfer ownership to IPDL.
10323 return actor
.forget().take();
10326 bool TransactionBase::StartRequest(PBackgroundIDBRequestParent
* aActor
) {
10327 AssertIsOnBackgroundThread();
10328 MOZ_ASSERT(aActor
);
10330 auto* op
= static_cast<NormalTransactionOp
*>(aActor
);
10332 if (NS_WARN_IF(!op
->Init(*this))) {
10337 op
->DispatchToConnectionPool();
10341 bool TransactionBase::DeallocRequest(
10342 PBackgroundIDBRequestParent
* const aActor
) {
10343 AssertIsOnBackgroundThread();
10344 MOZ_ASSERT(aActor
);
10346 // Transfer ownership back from IPDL.
10347 const RefPtr
<NormalTransactionOp
> actor
=
10348 dont_AddRef(static_cast<NormalTransactionOp
*>(aActor
));
10352 already_AddRefed
<PBackgroundIDBCursorParent
> TransactionBase::AllocCursor(
10353 const OpenCursorParams
& aParams
, bool aTrustParams
) {
10354 AssertIsOnBackgroundThread();
10355 MOZ_ASSERT(aParams
.type() != OpenCursorParams::T__None
);
10358 // Always verify parameters in DEBUG builds!
10359 aTrustParams
= false;
10362 const OpenCursorParams::Type type
= aParams
.type();
10363 SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
;
10364 SafeRefPtr
<FullIndexMetadata
> indexMetadata
;
10365 CursorBase::Direction direction
;
10367 // First extract the parameters common to all open cursor variants.
10368 const auto& commonParams
= GetCommonOpenCursorParams(aParams
);
10369 objectStoreMetadata
=
10370 GetMetadataForObjectStoreId(commonParams
.objectStoreId());
10371 if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata
)) {
10374 if (aTrustParams
&& NS_AUUF_OR_WARN_IF(!VerifyRequestParams(
10375 commonParams
.optionalKeyRange()))) {
10378 direction
= commonParams
.direction();
10380 // Now, for the index open cursor variants, extract the additional parameter.
10381 if (type
== OpenCursorParams::TIndexOpenCursorParams
||
10382 type
== OpenCursorParams::TIndexOpenKeyCursorParams
) {
10383 const auto& commonIndexParams
= GetCommonIndexOpenCursorParams(aParams
);
10384 indexMetadata
= GetMetadataForIndexId(*objectStoreMetadata
,
10385 commonIndexParams
.indexId());
10386 if (NS_AUUF_OR_WARN_IF(!indexMetadata
)) {
10391 if (NS_AUUF_OR_WARN_IF(mCommitOrAbortReceived
)) {
10395 // Create Cursor and transfer ownership to IPDL.
10397 case OpenCursorParams::TObjectStoreOpenCursorParams
:
10398 MOZ_ASSERT(!indexMetadata
);
10399 return MakeAndAddRef
<Cursor
<IDBCursorType::ObjectStore
>>(
10400 SafeRefPtrFromThis(), std::move(objectStoreMetadata
), direction
,
10401 CursorBase::ConstructFromTransactionBase
{});
10402 case OpenCursorParams::TObjectStoreOpenKeyCursorParams
:
10403 MOZ_ASSERT(!indexMetadata
);
10404 return MakeAndAddRef
<Cursor
<IDBCursorType::ObjectStoreKey
>>(
10405 SafeRefPtrFromThis(), std::move(objectStoreMetadata
), direction
,
10406 CursorBase::ConstructFromTransactionBase
{});
10407 case OpenCursorParams::TIndexOpenCursorParams
:
10408 return MakeAndAddRef
<Cursor
<IDBCursorType::Index
>>(
10409 SafeRefPtrFromThis(), std::move(objectStoreMetadata
),
10410 std::move(indexMetadata
), direction
,
10411 CursorBase::ConstructFromTransactionBase
{});
10412 case OpenCursorParams::TIndexOpenKeyCursorParams
:
10413 return MakeAndAddRef
<Cursor
<IDBCursorType::IndexKey
>>(
10414 SafeRefPtrFromThis(), std::move(objectStoreMetadata
),
10415 std::move(indexMetadata
), direction
,
10416 CursorBase::ConstructFromTransactionBase
{});
10418 MOZ_CRASH("Cannot get here.");
10422 bool TransactionBase::StartCursor(PBackgroundIDBCursorParent
* const aActor
,
10423 const int64_t aRequestId
,
10424 const OpenCursorParams
& aParams
) {
10425 AssertIsOnBackgroundThread();
10426 MOZ_ASSERT(aActor
);
10427 MOZ_ASSERT(aParams
.type() != OpenCursorParams::T__None
);
10429 auto* const op
= static_cast<CursorBase
*>(aActor
);
10431 if (NS_WARN_IF(!op
->Start(aRequestId
, aParams
))) {
10438 /*******************************************************************************
10439 * NormalTransaction
10440 ******************************************************************************/
10442 NormalTransaction::NormalTransaction(
10443 SafeRefPtr
<Database
> aDatabase
, TransactionBase::Mode aMode
,
10444 TransactionBase::Durability aDurability
,
10445 nsTArray
<SafeRefPtr
<FullObjectStoreMetadata
>>&& aObjectStores
)
10446 : TransactionBase(std::move(aDatabase
), aMode
, aDurability
),
10447 mObjectStores
{std::move(aObjectStores
)} {
10448 AssertIsOnBackgroundThread();
10449 MOZ_ASSERT(!mObjectStores
.IsEmpty());
10452 bool NormalTransaction::IsSameProcessActor() {
10453 AssertIsOnBackgroundThread();
10455 PBackgroundParent
* const actor
= Manager()->Manager()->Manager();
10458 return !BackgroundParent::IsOtherProcessActor(actor
);
10461 void NormalTransaction::SendCompleteNotification(nsresult aResult
) {
10462 AssertIsOnBackgroundThread();
10464 if (!IsActorDestroyed()) {
10465 Unused
<< SendComplete(aResult
);
10469 void NormalTransaction::ActorDestroy(ActorDestroyReason aWhy
) {
10470 AssertIsOnBackgroundThread();
10472 NoteActorDestroyed();
10474 if (!mCommittedOrAborted
) {
10475 if (NS_SUCCEEDED(mResultCode
)) {
10476 IDB_REPORT_INTERNAL_ERR();
10477 mResultCode
= NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
10480 mForceAborted
.EnsureFlipped();
10482 MaybeCommitOrAbort();
10486 mozilla::ipc::IPCResult
NormalTransaction::RecvDeleteMe() {
10487 AssertIsOnBackgroundThread();
10488 MOZ_ASSERT(!IsActorDestroyed());
10490 QM_WARNONLY_TRY(OkIf(PBackgroundIDBTransactionParent::Send__delete__(this)));
10495 mozilla::ipc::IPCResult
NormalTransaction::RecvCommit(
10496 const Maybe
<int64_t>& aLastRequest
) {
10497 AssertIsOnBackgroundThread();
10499 return TransactionBase::RecvCommit(this, aLastRequest
);
10502 mozilla::ipc::IPCResult
NormalTransaction::RecvAbort(
10503 const nsresult
& aResultCode
) {
10504 AssertIsOnBackgroundThread();
10506 return TransactionBase::RecvAbort(this, aResultCode
);
10509 PBackgroundIDBRequestParent
*
10510 NormalTransaction::AllocPBackgroundIDBRequestParent(
10511 const int64_t& aRequestId
, const RequestParams
& aParams
) {
10512 AssertIsOnBackgroundThread();
10513 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
10515 return AllocRequest(aRequestId
,
10516 std::move(const_cast<RequestParams
&>(aParams
)),
10517 IsSameProcessActor());
10520 mozilla::ipc::IPCResult
NormalTransaction::RecvPBackgroundIDBRequestConstructor(
10521 PBackgroundIDBRequestParent
* const aActor
, const int64_t& aRequestId
,
10522 const RequestParams
& aParams
) {
10523 AssertIsOnBackgroundThread();
10524 MOZ_ASSERT(aActor
);
10525 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
10527 if (!StartRequest(aActor
)) {
10528 return IPC_FAIL(this, "StartRequest failed!");
10533 bool NormalTransaction::DeallocPBackgroundIDBRequestParent(
10534 PBackgroundIDBRequestParent
* const aActor
) {
10535 AssertIsOnBackgroundThread();
10536 MOZ_ASSERT(aActor
);
10538 return DeallocRequest(aActor
);
10541 already_AddRefed
<PBackgroundIDBCursorParent
>
10542 NormalTransaction::AllocPBackgroundIDBCursorParent(
10543 const int64_t& aRequestId
, const OpenCursorParams
& aParams
) {
10544 AssertIsOnBackgroundThread();
10546 return AllocCursor(aParams
, IsSameProcessActor());
10549 mozilla::ipc::IPCResult
NormalTransaction::RecvPBackgroundIDBCursorConstructor(
10550 PBackgroundIDBCursorParent
* const aActor
, const int64_t& aRequestId
,
10551 const OpenCursorParams
& aParams
) {
10552 AssertIsOnBackgroundThread();
10553 MOZ_ASSERT(aActor
);
10554 MOZ_ASSERT(aParams
.type() != OpenCursorParams::T__None
);
10556 if (!StartCursor(aActor
, aRequestId
, aParams
)) {
10557 return IPC_FAIL(this, "StartCursor failed!");
10562 /*******************************************************************************
10563 * VersionChangeTransaction
10564 ******************************************************************************/
10566 VersionChangeTransaction::VersionChangeTransaction(
10567 OpenDatabaseOp
* aOpenDatabaseOp
)
10568 : TransactionBase(aOpenDatabaseOp
->mDatabase
.clonePtr(),
10569 IDBTransaction::Mode::VersionChange
,
10570 // VersionChange must not change durability.
10571 IDBTransaction::Durability::Default
), // Not used.
10572 mOpenDatabaseOp(aOpenDatabaseOp
) {
10573 AssertIsOnBackgroundThread();
10574 MOZ_ASSERT(aOpenDatabaseOp
);
10577 VersionChangeTransaction::~VersionChangeTransaction() {
10579 // Silence the base class' destructor assertion if we never made this actor
10581 FakeActorDestroyed();
10585 bool VersionChangeTransaction::IsSameProcessActor() {
10586 AssertIsOnBackgroundThread();
10588 PBackgroundParent
* actor
= Manager()->Manager()->Manager();
10591 return !BackgroundParent::IsOtherProcessActor(actor
);
10594 void VersionChangeTransaction::SetActorAlive() {
10595 AssertIsOnBackgroundThread();
10596 MOZ_ASSERT(!IsActorDestroyed());
10598 mActorWasAlive
.Flip();
10601 bool VersionChangeTransaction::CopyDatabaseMetadata() {
10602 AssertIsOnBackgroundThread();
10603 MOZ_ASSERT(!mOldMetadata
);
10605 const auto& origMetadata
= GetDatabase().Metadata();
10607 SafeRefPtr
<FullDatabaseMetadata
> newMetadata
= origMetadata
.Duplicate();
10608 if (NS_WARN_IF(!newMetadata
)) {
10612 // Replace the live metadata with the new mutable copy.
10613 DatabaseActorInfo
* info
;
10614 MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable
->Get(origMetadata
.mDatabaseId
, &info
));
10615 MOZ_ASSERT(!info
->mLiveDatabases
.IsEmpty());
10616 MOZ_ASSERT(info
->mMetadata
== &origMetadata
);
10618 mOldMetadata
= std::move(info
->mMetadata
);
10619 info
->mMetadata
= std::move(newMetadata
);
10621 // Replace metadata pointers for all live databases.
10622 for (const auto& liveDatabase
: info
->mLiveDatabases
) {
10623 liveDatabase
->mMetadata
= info
->mMetadata
.clonePtr();
10629 void VersionChangeTransaction::UpdateMetadata(nsresult aResult
) {
10630 AssertIsOnBackgroundThread();
10631 MOZ_ASSERT(mOpenDatabaseOp
);
10632 MOZ_ASSERT(!!mActorWasAlive
== !!mOpenDatabaseOp
->mDatabase
);
10633 MOZ_ASSERT_IF(mActorWasAlive
, !mOpenDatabaseOp
->mDatabaseId
.ref().IsEmpty());
10635 if (IsActorDestroyed() || !mActorWasAlive
) {
10639 SafeRefPtr
<FullDatabaseMetadata
> oldMetadata
= std::move(mOldMetadata
);
10641 DatabaseActorInfo
* info
;
10642 if (!gLiveDatabaseHashtable
->Get(oldMetadata
->mDatabaseId
, &info
)) {
10646 MOZ_ASSERT(!info
->mLiveDatabases
.IsEmpty());
10648 if (NS_SUCCEEDED(aResult
)) {
10649 // Remove all deleted objectStores and indexes, then mark immutable.
10650 info
->mMetadata
->mObjectStores
.RemoveIf([](const auto& objectStoreIter
) {
10651 MOZ_ASSERT(objectStoreIter
.Key());
10652 const SafeRefPtr
<FullObjectStoreMetadata
>& metadata
=
10653 objectStoreIter
.Data();
10654 MOZ_ASSERT(metadata
);
10656 if (metadata
->mDeleted
) {
10660 metadata
->mIndexes
.RemoveIf([](const auto& indexIter
) -> bool {
10661 MOZ_ASSERT(indexIter
.Key());
10662 const SafeRefPtr
<FullIndexMetadata
>& index
= indexIter
.Data();
10665 return index
->mDeleted
;
10667 metadata
->mIndexes
.MarkImmutable();
10672 info
->mMetadata
->mObjectStores
.MarkImmutable();
10674 // Replace metadata pointers for all live databases.
10675 info
->mMetadata
= std::move(oldMetadata
);
10677 for (auto& liveDatabase
: info
->mLiveDatabases
) {
10678 liveDatabase
->mMetadata
= info
->mMetadata
.clonePtr();
10683 void VersionChangeTransaction::SendCompleteNotification(nsresult aResult
) {
10684 AssertIsOnBackgroundThread();
10685 MOZ_ASSERT(mOpenDatabaseOp
);
10686 MOZ_ASSERT_IF(!mActorWasAlive
, mOpenDatabaseOp
->HasFailed());
10687 MOZ_ASSERT_IF(!mActorWasAlive
, mOpenDatabaseOp
->mState
>
10688 OpenDatabaseOp::State::SendingResults
);
10690 const RefPtr
<OpenDatabaseOp
> openDatabaseOp
= std::move(mOpenDatabaseOp
);
10692 if (!mActorWasAlive
) {
10696 if (NS_FAILED(aResult
)) {
10697 // 3.3.1 Opening a database:
10698 // "If the upgrade transaction was aborted, run the steps for closing a
10699 // database connection with connection, create and return a new AbortError
10700 // exception and abort these steps."
10701 openDatabaseOp
->SetFailureCodeIfUnset(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
);
10704 openDatabaseOp
->mState
= OpenDatabaseOp::State::SendingResults
;
10706 if (!IsActorDestroyed()) {
10707 Unused
<< SendComplete(aResult
);
10710 MOZ_ALWAYS_SUCCEEDS(openDatabaseOp
->Run());
10713 void VersionChangeTransaction::ActorDestroy(ActorDestroyReason aWhy
) {
10714 AssertIsOnBackgroundThread();
10716 NoteActorDestroyed();
10718 if (!mCommittedOrAborted
) {
10719 if (NS_SUCCEEDED(mResultCode
)) {
10720 IDB_REPORT_INTERNAL_ERR();
10721 mResultCode
= NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
10724 mForceAborted
.EnsureFlipped();
10726 MaybeCommitOrAbort();
10730 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvDeleteMe() {
10731 AssertIsOnBackgroundThread();
10732 MOZ_ASSERT(!IsActorDestroyed());
10735 OkIf(PBackgroundIDBVersionChangeTransactionParent::Send__delete__(this)));
10740 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvCommit(
10741 const Maybe
<int64_t>& aLastRequest
) {
10742 AssertIsOnBackgroundThread();
10744 return TransactionBase::RecvCommit(this, aLastRequest
);
10747 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvAbort(
10748 const nsresult
& aResultCode
) {
10749 AssertIsOnBackgroundThread();
10751 return TransactionBase::RecvAbort(this, aResultCode
);
10754 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvCreateObjectStore(
10755 const ObjectStoreMetadata
& aMetadata
) {
10756 AssertIsOnBackgroundThread();
10758 if (NS_WARN_IF(!aMetadata
.id())) {
10759 return IPC_FAIL(this, "No metadata ID!");
10762 const SafeRefPtr
<FullDatabaseMetadata
> dbMetadata
=
10763 GetDatabase().MetadataPtr();
10765 if (NS_WARN_IF(aMetadata
.id() != dbMetadata
->mNextObjectStoreId
)) {
10766 return IPC_FAIL(this, "Requested metadata ID does not match next ID!");
10770 MatchMetadataNameOrId(dbMetadata
->mObjectStores
, aMetadata
.id(),
10771 SomeRef
<const nsAString
&>(aMetadata
.name()))
10773 return IPC_FAIL(this, "MatchMetadataNameOrId failed!");
10776 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
10777 return IPC_FAIL(this, "Transaction is already committed/aborted!");
10780 const int64_t initialAutoIncrementId
= aMetadata
.autoIncrement() ? 1 : 0;
10781 auto newMetadata
= MakeSafeRefPtr
<FullObjectStoreMetadata
>(
10782 aMetadata
, FullObjectStoreMetadata::AutoIncrementIds
{
10783 initialAutoIncrementId
, initialAutoIncrementId
});
10785 if (NS_WARN_IF(!dbMetadata
->mObjectStores
.InsertOrUpdate(
10786 aMetadata
.id(), std::move(newMetadata
), fallible
))) {
10787 return IPC_FAIL(this, "mObjectStores.InsertOrUpdate failed!");
10790 dbMetadata
->mNextObjectStoreId
++;
10792 RefPtr
<CreateObjectStoreOp
> op
= new CreateObjectStoreOp(
10793 SafeRefPtrFromThis().downcast
<VersionChangeTransaction
>(), aMetadata
);
10795 if (NS_WARN_IF(!op
->Init(*this))) {
10797 return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
10800 op
->DispatchToConnectionPool();
10805 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvDeleteObjectStore(
10806 const IndexOrObjectStoreId
& aObjectStoreId
) {
10807 AssertIsOnBackgroundThread();
10809 if (NS_WARN_IF(!aObjectStoreId
)) {
10810 return IPC_FAIL(this, "No ObjectStoreId!");
10813 const auto& dbMetadata
= GetDatabase().Metadata();
10814 MOZ_ASSERT(dbMetadata
.mNextObjectStoreId
> 0);
10816 if (NS_WARN_IF(aObjectStoreId
>= dbMetadata
.mNextObjectStoreId
)) {
10817 return IPC_FAIL(this, "Invalid ObjectStoreId!");
10820 SafeRefPtr
<FullObjectStoreMetadata
> foundMetadata
=
10821 GetMetadataForObjectStoreId(aObjectStoreId
);
10823 if (NS_WARN_IF(!foundMetadata
)) {
10824 return IPC_FAIL(this, "No metadata found for ObjectStoreId!");
10827 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
10828 return IPC_FAIL(this, "Transaction is already committed/aborted!");
10831 foundMetadata
->mDeleted
.Flip();
10833 DebugOnly
<bool> foundTargetId
= false;
10834 const bool isLastObjectStore
= std::all_of(
10835 dbMetadata
.mObjectStores
.begin(), dbMetadata
.mObjectStores
.end(),
10836 [&foundTargetId
, aObjectStoreId
](const auto& objectStoreEntry
) -> bool {
10837 if (uint64_t(aObjectStoreId
) == objectStoreEntry
.GetKey()) {
10838 foundTargetId
= true;
10842 return objectStoreEntry
.GetData()->mDeleted
;
10844 MOZ_ASSERT_IF(isLastObjectStore
, foundTargetId
);
10846 RefPtr
<DeleteObjectStoreOp
> op
= new DeleteObjectStoreOp(
10847 SafeRefPtrFromThis().downcast
<VersionChangeTransaction
>(),
10848 std::move(foundMetadata
), isLastObjectStore
);
10850 if (NS_WARN_IF(!op
->Init(*this))) {
10852 return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
10855 op
->DispatchToConnectionPool();
10860 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvRenameObjectStore(
10861 const IndexOrObjectStoreId
& aObjectStoreId
, const nsAString
& aName
) {
10862 AssertIsOnBackgroundThread();
10864 if (NS_WARN_IF(!aObjectStoreId
)) {
10865 return IPC_FAIL(this, "No ObjectStoreId!");
10869 const auto& dbMetadata
= GetDatabase().Metadata();
10870 MOZ_ASSERT(dbMetadata
.mNextObjectStoreId
> 0);
10872 if (NS_WARN_IF(aObjectStoreId
>= dbMetadata
.mNextObjectStoreId
)) {
10873 return IPC_FAIL(this, "Invalid ObjectStoreId!");
10877 SafeRefPtr
<FullObjectStoreMetadata
> foundMetadata
=
10878 GetMetadataForObjectStoreId(aObjectStoreId
);
10880 if (NS_WARN_IF(!foundMetadata
)) {
10881 return IPC_FAIL(this, "No metadata found for ObjectStoreId!");
10884 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
10885 return IPC_FAIL(this, "Transaction is already committed/aborted!");
10888 foundMetadata
->mCommonMetadata
.name() = aName
;
10890 RefPtr
<RenameObjectStoreOp
> renameOp
= new RenameObjectStoreOp(
10891 SafeRefPtrFromThis().downcast
<VersionChangeTransaction
>(),
10894 if (NS_WARN_IF(!renameOp
->Init(*this))) {
10895 renameOp
->Cleanup();
10896 return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
10899 renameOp
->DispatchToConnectionPool();
10904 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvCreateIndex(
10905 const IndexOrObjectStoreId
& aObjectStoreId
,
10906 const IndexMetadata
& aMetadata
) {
10907 AssertIsOnBackgroundThread();
10909 if (NS_WARN_IF(!aObjectStoreId
)) {
10910 return IPC_FAIL(this, "No ObjectStoreId!");
10913 if (NS_WARN_IF(!aMetadata
.id())) {
10914 return IPC_FAIL(this, "No Metadata id!");
10917 const auto dbMetadata
= GetDatabase().MetadataPtr();
10919 if (NS_WARN_IF(aMetadata
.id() != dbMetadata
->mNextIndexId
)) {
10920 return IPC_FAIL(this, "Requested metadata ID does not match next ID!");
10923 SafeRefPtr
<FullObjectStoreMetadata
> foundObjectStoreMetadata
=
10924 GetMetadataForObjectStoreId(aObjectStoreId
);
10926 if (NS_WARN_IF(!foundObjectStoreMetadata
)) {
10927 return IPC_FAIL(this, "GetMetadataForObjectStoreId failed!");
10930 if (NS_WARN_IF(MatchMetadataNameOrId(
10931 foundObjectStoreMetadata
->mIndexes
, aMetadata
.id(),
10932 SomeRef
<const nsAString
&>(aMetadata
.name()))
10934 return IPC_FAIL(this, "MatchMetadataNameOrId failed!");
10937 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
10938 return IPC_FAIL(this, "Transaction is already committed/aborted!");
10941 auto newMetadata
= MakeSafeRefPtr
<FullIndexMetadata
>();
10942 newMetadata
->mCommonMetadata
= aMetadata
;
10944 if (NS_WARN_IF(!foundObjectStoreMetadata
->mIndexes
.InsertOrUpdate(
10945 aMetadata
.id(), std::move(newMetadata
), fallible
))) {
10946 return IPC_FAIL(this, "mIndexes.InsertOrUpdate failed!");
10949 dbMetadata
->mNextIndexId
++;
10951 RefPtr
<CreateIndexOp
> op
= new CreateIndexOp(
10952 SafeRefPtrFromThis().downcast
<VersionChangeTransaction
>(), aObjectStoreId
,
10955 if (NS_WARN_IF(!op
->Init(*this))) {
10957 return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
10960 op
->DispatchToConnectionPool();
10965 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvDeleteIndex(
10966 const IndexOrObjectStoreId
& aObjectStoreId
,
10967 const IndexOrObjectStoreId
& aIndexId
) {
10968 AssertIsOnBackgroundThread();
10970 if (NS_WARN_IF(!aObjectStoreId
)) {
10971 return IPC_FAIL(this, "No ObjectStoreId!");
10974 if (NS_WARN_IF(!aIndexId
)) {
10975 return IPC_FAIL(this, "No Index id!");
10978 const auto& dbMetadata
= GetDatabase().Metadata();
10979 MOZ_ASSERT(dbMetadata
.mNextObjectStoreId
> 0);
10980 MOZ_ASSERT(dbMetadata
.mNextIndexId
> 0);
10982 if (NS_WARN_IF(aObjectStoreId
>= dbMetadata
.mNextObjectStoreId
)) {
10983 return IPC_FAIL(this, "Requested ObjectStoreId does not match next ID!");
10986 if (NS_WARN_IF(aIndexId
>= dbMetadata
.mNextIndexId
)) {
10987 return IPC_FAIL(this, "Requested IndexId does not match next ID!");
10991 SafeRefPtr
<FullObjectStoreMetadata
> foundObjectStoreMetadata
=
10992 GetMetadataForObjectStoreId(aObjectStoreId
);
10994 if (NS_WARN_IF(!foundObjectStoreMetadata
)) {
10995 return IPC_FAIL(this, "GetMetadataForObjectStoreId failed!");
10998 SafeRefPtr
<FullIndexMetadata
> foundIndexMetadata
=
10999 GetMetadataForIndexId(*foundObjectStoreMetadata
, aIndexId
);
11001 if (NS_WARN_IF(!foundIndexMetadata
)) {
11002 return IPC_FAIL(this, "GetMetadataForIndexId failed!");
11005 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
11006 return IPC_FAIL(this, "Transaction is already committed/aborted!");
11009 foundIndexMetadata
->mDeleted
.Flip();
11011 DebugOnly
<bool> foundTargetId
= false;
11012 const bool isLastIndex
=
11013 std::all_of(foundObjectStoreMetadata
->mIndexes
.cbegin(),
11014 foundObjectStoreMetadata
->mIndexes
.cend(),
11015 [&foundTargetId
, aIndexId
](const auto& indexEntry
) -> bool {
11016 if (uint64_t(aIndexId
) == indexEntry
.GetKey()) {
11017 foundTargetId
= true;
11021 return indexEntry
.GetData()->mDeleted
;
11023 MOZ_ASSERT_IF(isLastIndex
, foundTargetId
);
11025 RefPtr
<DeleteIndexOp
> op
= new DeleteIndexOp(
11026 SafeRefPtrFromThis().downcast
<VersionChangeTransaction
>(), aObjectStoreId
,
11027 aIndexId
, foundIndexMetadata
->mCommonMetadata
.unique(), isLastIndex
);
11029 if (NS_WARN_IF(!op
->Init(*this))) {
11031 return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
11034 op
->DispatchToConnectionPool();
11039 mozilla::ipc::IPCResult
VersionChangeTransaction::RecvRenameIndex(
11040 const IndexOrObjectStoreId
& aObjectStoreId
,
11041 const IndexOrObjectStoreId
& aIndexId
, const nsAString
& aName
) {
11042 AssertIsOnBackgroundThread();
11044 if (NS_WARN_IF(!aObjectStoreId
)) {
11045 return IPC_FAIL(this, "No ObjectStoreId!");
11048 if (NS_WARN_IF(!aIndexId
)) {
11049 return IPC_FAIL(this, "No Index id!");
11052 const SafeRefPtr
<FullDatabaseMetadata
> dbMetadata
=
11053 GetDatabase().MetadataPtr();
11054 MOZ_ASSERT(dbMetadata
);
11055 MOZ_ASSERT(dbMetadata
->mNextObjectStoreId
> 0);
11056 MOZ_ASSERT(dbMetadata
->mNextIndexId
> 0);
11058 if (NS_WARN_IF(aObjectStoreId
>= dbMetadata
->mNextObjectStoreId
)) {
11059 return IPC_FAIL(this, "Requested ObjectStoreId does not match next ID!");
11062 if (NS_WARN_IF(aIndexId
>= dbMetadata
->mNextIndexId
)) {
11063 return IPC_FAIL(this, "Requested IndexId does not match next ID!");
11066 SafeRefPtr
<FullObjectStoreMetadata
> foundObjectStoreMetadata
=
11067 GetMetadataForObjectStoreId(aObjectStoreId
);
11069 if (NS_WARN_IF(!foundObjectStoreMetadata
)) {
11070 return IPC_FAIL(this, "GetMetadataForObjectStoreId failed!");
11073 SafeRefPtr
<FullIndexMetadata
> foundIndexMetadata
=
11074 GetMetadataForIndexId(*foundObjectStoreMetadata
, aIndexId
);
11076 if (NS_WARN_IF(!foundIndexMetadata
)) {
11077 return IPC_FAIL(this, "GetMetadataForIndexId failed!");
11080 if (NS_WARN_IF(mCommitOrAbortReceived
)) {
11081 return IPC_FAIL(this, "Transaction is already committed/aborted!");
11084 foundIndexMetadata
->mCommonMetadata
.name() = aName
;
11086 RefPtr
<RenameIndexOp
> renameOp
= new RenameIndexOp(
11087 SafeRefPtrFromThis().downcast
<VersionChangeTransaction
>(),
11088 *foundIndexMetadata
, aObjectStoreId
);
11090 if (NS_WARN_IF(!renameOp
->Init(*this))) {
11091 renameOp
->Cleanup();
11092 return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
11095 renameOp
->DispatchToConnectionPool();
11100 PBackgroundIDBRequestParent
*
11101 VersionChangeTransaction::AllocPBackgroundIDBRequestParent(
11102 const int64_t& aRequestId
, const RequestParams
& aParams
) {
11103 AssertIsOnBackgroundThread();
11104 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
11106 return AllocRequest(aRequestId
,
11107 std::move(const_cast<RequestParams
&>(aParams
)),
11108 IsSameProcessActor());
11111 mozilla::ipc::IPCResult
11112 VersionChangeTransaction::RecvPBackgroundIDBRequestConstructor(
11113 PBackgroundIDBRequestParent
* aActor
, const int64_t& aRequestId
,
11114 const RequestParams
& aParams
) {
11115 AssertIsOnBackgroundThread();
11116 MOZ_ASSERT(aActor
);
11117 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
11119 if (!StartRequest(aActor
)) {
11120 return IPC_FAIL(this, "StartRequest failed!");
11125 bool VersionChangeTransaction::DeallocPBackgroundIDBRequestParent(
11126 PBackgroundIDBRequestParent
* aActor
) {
11127 AssertIsOnBackgroundThread();
11128 MOZ_ASSERT(aActor
);
11130 return DeallocRequest(aActor
);
11133 already_AddRefed
<PBackgroundIDBCursorParent
>
11134 VersionChangeTransaction::AllocPBackgroundIDBCursorParent(
11135 const int64_t& aRequestId
, const OpenCursorParams
& aParams
) {
11136 AssertIsOnBackgroundThread();
11138 return AllocCursor(aParams
, IsSameProcessActor());
11141 mozilla::ipc::IPCResult
11142 VersionChangeTransaction::RecvPBackgroundIDBCursorConstructor(
11143 PBackgroundIDBCursorParent
* aActor
, const int64_t& aRequestId
,
11144 const OpenCursorParams
& aParams
) {
11145 AssertIsOnBackgroundThread();
11146 MOZ_ASSERT(aActor
);
11147 MOZ_ASSERT(aParams
.type() != OpenCursorParams::T__None
);
11149 if (!StartCursor(aActor
, aRequestId
, aParams
)) {
11150 return IPC_FAIL(this, "StartCursor failed!");
11155 /*******************************************************************************
11157 ******************************************************************************/
11159 CursorBase::CursorBase(SafeRefPtr
<TransactionBase
> aTransaction
,
11160 SafeRefPtr
<FullObjectStoreMetadata
> aObjectStoreMetadata
,
11161 const Direction aDirection
,
11162 const ConstructFromTransactionBase
/*aConstructionTag*/)
11163 : mTransaction(std::move(aTransaction
)),
11164 mObjectStoreMetadata(WrapNotNull(std::move(aObjectStoreMetadata
))),
11165 mObjectStoreId((*mObjectStoreMetadata
)->mCommonMetadata
.id()),
11166 mDirection(aDirection
),
11167 mMaxExtraCount(IndexedDatabaseManager::MaxPreloadExtraRecords()),
11168 mIsSameProcessActor(!BackgroundParent::IsOtherProcessActor(
11169 mTransaction
->GetBackgroundParent())) {
11170 AssertIsOnBackgroundThread();
11171 MOZ_ASSERT(mTransaction
);
11174 OpenCursorParams::T__None
== 0 && OpenCursorParams::T__Last
== 4,
11175 "Lots of code here assumes only four types of cursors!");
11178 template <IDBCursorType CursorType
>
11179 bool Cursor
<CursorType
>::VerifyRequestParams(
11180 const CursorRequestParams
& aParams
,
11181 const CursorPosition
<CursorType
>& aPosition
) const {
11182 AssertIsOnBackgroundThread();
11183 MOZ_ASSERT(aParams
.type() != CursorRequestParams::T__None
);
11184 MOZ_ASSERT(this->mObjectStoreMetadata
);
11185 if constexpr (IsIndexCursor
) {
11186 MOZ_ASSERT(this->mIndexMetadata
);
11191 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
11192 mTransaction
->GetMetadataForObjectStoreId(mObjectStoreId
);
11193 if (objectStoreMetadata
) {
11194 MOZ_ASSERT(objectStoreMetadata
== (*this->mObjectStoreMetadata
));
11196 MOZ_ASSERT((*this->mObjectStoreMetadata
)->mDeleted
);
11199 if constexpr (IsIndexCursor
) {
11200 if (objectStoreMetadata
) {
11201 const SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
11202 mTransaction
->GetMetadataForIndexId(*objectStoreMetadata
,
11204 if (indexMetadata
) {
11205 MOZ_ASSERT(indexMetadata
== *this->mIndexMetadata
);
11207 MOZ_ASSERT((*this->mIndexMetadata
)->mDeleted
);
11214 if (NS_AUUF_OR_WARN_IF((*this->mObjectStoreMetadata
)->mDeleted
)) {
11218 if constexpr (IsIndexCursor
) {
11219 if (NS_AUUF_OR_WARN_IF(this->mIndexMetadata
&&
11220 (*this->mIndexMetadata
)->mDeleted
)) {
11225 const Key
& sortKey
= aPosition
.GetSortKey(this->IsLocaleAware());
11227 switch (aParams
.type()) {
11228 case CursorRequestParams::TContinueParams
: {
11229 const Key
& key
= aParams
.get_ContinueParams().key();
11230 if (!key
.IsUnset()) {
11231 switch (mDirection
) {
11232 case IDBCursorDirection::Next
:
11233 case IDBCursorDirection::Nextunique
:
11234 if (NS_AUUF_OR_WARN_IF(key
<= sortKey
)) {
11239 case IDBCursorDirection::Prev
:
11240 case IDBCursorDirection::Prevunique
:
11241 if (NS_AUUF_OR_WARN_IF(key
>= sortKey
)) {
11247 MOZ_CRASH("Should never get here!");
11253 case CursorRequestParams::TContinuePrimaryKeyParams
: {
11254 if constexpr (IsIndexCursor
) {
11255 const Key
& key
= aParams
.get_ContinuePrimaryKeyParams().key();
11256 const Key
& primaryKey
=
11257 aParams
.get_ContinuePrimaryKeyParams().primaryKey();
11258 MOZ_ASSERT(!key
.IsUnset());
11259 MOZ_ASSERT(!primaryKey
.IsUnset());
11260 switch (mDirection
) {
11261 case IDBCursorDirection::Next
:
11262 if (NS_AUUF_OR_WARN_IF(key
< sortKey
||
11264 primaryKey
<= aPosition
.mObjectStoreKey
))) {
11269 case IDBCursorDirection::Prev
:
11270 if (NS_AUUF_OR_WARN_IF(key
> sortKey
||
11272 primaryKey
>= aPosition
.mObjectStoreKey
))) {
11278 MOZ_CRASH("Should never get here!");
11284 case CursorRequestParams::TAdvanceParams
:
11285 if (NS_AUUF_OR_WARN_IF(!aParams
.get_AdvanceParams().count())) {
11291 MOZ_CRASH("Should never get here!");
11297 template <IDBCursorType CursorType
>
11298 bool Cursor
<CursorType
>::Start(const int64_t aRequestId
,
11299 const OpenCursorParams
& aParams
) {
11300 AssertIsOnBackgroundThread();
11301 MOZ_ASSERT(aParams
.type() == ToOpenCursorParamsType(CursorType
));
11302 MOZ_ASSERT(this->mObjectStoreMetadata
);
11304 if (NS_AUUF_OR_WARN_IF(mCurrentlyRunningOp
)) {
11308 const Maybe
<SerializedKeyRange
>& optionalKeyRange
=
11309 GetCommonOpenCursorParams(aParams
).optionalKeyRange();
11311 const RefPtr
<OpenOp
> openOp
= new OpenOp(this, aRequestId
, optionalKeyRange
);
11313 if (NS_WARN_IF(!openOp
->Init(*mTransaction
))) {
11318 openOp
->DispatchToConnectionPool();
11319 mCurrentlyRunningOp
= openOp
;
11324 void ValueCursorBase::ProcessFiles(CursorResponse
& aResponse
,
11325 const FilesArray
& aFiles
) {
11327 aResponse
.type() == CursorResponse::Tnsresult
||
11328 aResponse
.type() == CursorResponse::Tvoid_t
||
11329 aResponse
.type() ==
11330 CursorResponse::TArrayOfObjectStoreKeyCursorResponse
||
11331 aResponse
.type() == CursorResponse::TArrayOfIndexKeyCursorResponse
,
11334 for (size_t i
= 0; i
< aFiles
.Length(); ++i
) {
11335 const auto& files
= aFiles
[i
];
11336 if (!files
.IsEmpty()) {
11337 // TODO: Replace this assertion by one that checks if the response type
11338 // matches the cursor type, at a more generic location.
11339 MOZ_ASSERT(aResponse
.type() ==
11340 CursorResponse::TArrayOfObjectStoreCursorResponse
||
11341 aResponse
.type() ==
11342 CursorResponse::TArrayOfIndexCursorResponse
);
11344 SerializedStructuredCloneReadInfo
* serializedInfo
= nullptr;
11345 switch (aResponse
.type()) {
11346 case CursorResponse::TArrayOfObjectStoreCursorResponse
: {
11347 auto& responses
= aResponse
.get_ArrayOfObjectStoreCursorResponse();
11348 MOZ_ASSERT(i
< responses
.Length());
11349 serializedInfo
= &responses
[i
].cloneInfo();
11353 case CursorResponse::TArrayOfIndexCursorResponse
: {
11354 auto& responses
= aResponse
.get_ArrayOfIndexCursorResponse();
11355 MOZ_ASSERT(i
< responses
.Length());
11356 serializedInfo
= &responses
[i
].cloneInfo();
11361 MOZ_CRASH("Should never get here!");
11364 MOZ_ASSERT(serializedInfo
);
11365 MOZ_ASSERT(serializedInfo
->files().IsEmpty());
11366 MOZ_ASSERT(this->mDatabase
);
11368 QM_TRY_UNWRAP(serializedInfo
->files(),
11369 SerializeStructuredCloneFiles(this->mDatabase
, files
,
11370 /* aForPreprocess */ false),
11371 QM_VOID
, [&aResponse
](const nsresult result
) {
11372 aResponse
= ClampResultCode(result
);
11378 template <IDBCursorType CursorType
>
11379 void Cursor
<CursorType
>::SendResponseInternal(
11380 CursorResponse
& aResponse
, const FilesArrayT
<CursorType
>& aFiles
) {
11381 AssertIsOnBackgroundThread();
11382 MOZ_ASSERT(aResponse
.type() != CursorResponse::T__None
);
11383 MOZ_ASSERT_IF(aResponse
.type() == CursorResponse::Tnsresult
,
11384 NS_FAILED(aResponse
.get_nsresult()));
11385 MOZ_ASSERT_IF(aResponse
.type() == CursorResponse::Tnsresult
,
11386 NS_ERROR_GET_MODULE(aResponse
.get_nsresult()) ==
11387 NS_ERROR_MODULE_DOM_INDEXEDDB
);
11388 MOZ_ASSERT(this->mObjectStoreMetadata
);
11389 MOZ_ASSERT(mCurrentlyRunningOp
);
11391 KeyValueBase::ProcessFiles(aResponse
, aFiles
);
11393 // Work around the deleted function by casting to the base class.
11394 QM_WARNONLY_TRY(OkIf(
11395 static_cast<PBackgroundIDBCursorParent
*>(this)->SendResponse(aResponse
)));
11397 mCurrentlyRunningOp
= nullptr;
11400 template <IDBCursorType CursorType
>
11401 void Cursor
<CursorType
>::ActorDestroy(ActorDestroyReason aWhy
) {
11402 AssertIsOnBackgroundThread();
11404 if (mCurrentlyRunningOp
) {
11405 mCurrentlyRunningOp
->NoteActorDestroyed();
11408 if constexpr (IsValueCursor
) {
11409 this->mBackgroundParent
.destroy();
11411 this->mObjectStoreMetadata
.destroy();
11412 if constexpr (IsIndexCursor
) {
11413 this->mIndexMetadata
.destroy();
11417 template <IDBCursorType CursorType
>
11418 mozilla::ipc::IPCResult Cursor
<CursorType
>::RecvDeleteMe() {
11419 AssertIsOnBackgroundThread();
11420 MOZ_ASSERT(this->mObjectStoreMetadata
);
11422 if (NS_WARN_IF(mCurrentlyRunningOp
)) {
11425 "Attempt to delete a cursor with a non-null mCurrentlyRunningOp!");
11428 QM_WARNONLY_TRY(OkIf(PBackgroundIDBCursorParent::Send__delete__(this)));
11433 template <IDBCursorType CursorType
>
11434 mozilla::ipc::IPCResult Cursor
<CursorType
>::RecvContinue(
11435 const int64_t& aRequestId
, const CursorRequestParams
& aParams
,
11436 const Key
& aCurrentKey
, const Key
& aCurrentObjectStoreKey
) {
11437 AssertIsOnBackgroundThread();
11438 MOZ_ASSERT(aParams
.type() != CursorRequestParams::T__None
);
11439 MOZ_ASSERT(this->mObjectStoreMetadata
);
11440 if constexpr (IsIndexCursor
) {
11441 MOZ_ASSERT(this->mIndexMetadata
);
11444 const bool trustParams
=
11446 // Always verify parameters in DEBUG builds!
11449 this->mIsSameProcessActor
11453 MOZ_ASSERT(!aCurrentKey
.IsUnset());
11457 ([&]() -> Result
<CursorPosition
<CursorType
>, mozilla::ipc::IPCResult
> {
11458 if constexpr (IsIndexCursor
) {
11459 auto localeAwarePosition
= Key
{};
11460 if (this->IsLocaleAware()) {
11462 localeAwarePosition
,
11463 aCurrentKey
.ToLocaleAwareKey(this->mLocale
),
11464 Err(IPC_FAIL(this, "aCurrentKey.ToLocaleAwareKey failed!")));
11466 return CursorPosition
<CursorType
>{aCurrentKey
, localeAwarePosition
,
11467 aCurrentObjectStoreKey
};
11469 return CursorPosition
<CursorType
>{aCurrentKey
};
11473 if (!trustParams
&& !VerifyRequestParams(aParams
, position
)) {
11474 return IPC_FAIL(this, "VerifyRequestParams failed!");
11477 if (NS_WARN_IF(mCurrentlyRunningOp
)) {
11478 return IPC_FAIL(this, "Cursor is CurrentlyRunningOp!");
11481 if (NS_WARN_IF(mTransaction
->mCommitOrAbortReceived
)) {
11482 return IPC_FAIL(this, "Transaction is already committed/aborted!");
11485 const RefPtr
<ContinueOp
> continueOp
=
11486 new ContinueOp(this, aRequestId
, aParams
, std::move(position
));
11487 if (NS_WARN_IF(!continueOp
->Init(*mTransaction
))) {
11488 continueOp
->Cleanup();
11489 return IPC_FAIL(this, "ContinueOp initialization failed!");
11492 continueOp
->DispatchToConnectionPool();
11493 mCurrentlyRunningOp
= continueOp
;
11498 /*******************************************************************************
11499 * DatabaseFileManager
11500 ******************************************************************************/
11502 DatabaseFileManager::MutexType
DatabaseFileManager::sMutex
;
11504 DatabaseFileManager::DatabaseFileManager(
11505 PersistenceType aPersistenceType
,
11506 const quota::OriginMetadata
& aOriginMetadata
,
11507 const nsAString
& aDatabaseName
, const nsCString
& aDatabaseID
,
11508 const nsAString
& aDatabaseFilePath
, bool aEnforcingQuota
,
11509 bool aIsInPrivateBrowsingMode
)
11510 : mPersistenceType(aPersistenceType
),
11511 mOriginMetadata(aOriginMetadata
),
11512 mDatabaseName(aDatabaseName
),
11513 mDatabaseID(aDatabaseID
),
11514 mDatabaseFilePath(aDatabaseFilePath
),
11516 aIsInPrivateBrowsingMode
11517 ? new IndexedDBCipherKeyManager("IndexedDBCipherKeyManager")
11519 mDatabaseVersion(0),
11520 mEnforcingQuota(aEnforcingQuota
),
11521 mIsInPrivateBrowsingMode(aIsInPrivateBrowsingMode
) {}
11523 nsresult
DatabaseFileManager::Init(nsIFile
* aDirectory
,
11524 mozIStorageConnection
& aConnection
) {
11525 AssertIsOnIOThread();
11526 MOZ_ASSERT(aDirectory
);
11529 QM_TRY_INSPECT(const bool& existsAsDirectory
,
11530 ExistsAsDirectory(*aDirectory
));
11532 if (!existsAsDirectory
) {
11533 QM_TRY(MOZ_TO_RESULT(aDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0755)));
11536 QM_TRY_UNWRAP(auto path
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
11537 nsString
, aDirectory
, GetPath
));
11539 mDirectoryPath
.init(std::move(path
));
11542 QM_TRY_INSPECT(const auto& journalDirectory
,
11543 CloneFileAndAppend(*aDirectory
, kJournalDirectoryName
));
11545 // We don't care if it doesn't exist at all, but if it does exist, make sure
11546 // it's a directory.
11547 QM_TRY_INSPECT(const bool& existsAsDirectory
,
11548 ExistsAsDirectory(*journalDirectory
));
11549 Unused
<< existsAsDirectory
;
11552 QM_TRY_UNWRAP(auto path
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
11553 nsString
, journalDirectory
, GetPath
));
11555 mJournalDirectoryPath
.init(std::move(path
));
11558 QM_TRY_INSPECT(const auto& stmt
,
11559 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
11560 nsCOMPtr
<mozIStorageStatement
>, aConnection
,
11561 CreateStatement
, "SELECT id, refcount FROM file"_ns
));
11564 CollectWhileHasResult(*stmt
, [this](auto& stmt
) -> Result
<Ok
, nsresult
> {
11565 QM_TRY_INSPECT(const int64_t& id
,
11566 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetInt64
, 0));
11567 QM_TRY_INSPECT(const int32_t& dbRefCnt
,
11568 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetInt32
, 1));
11570 // We put a raw pointer into the hash table, so the memory refcount will
11571 // be 0, but the dbRefCnt is non-zero, which will keep the
11572 // DatabaseFileInfo object alive.
11573 MOZ_ASSERT(dbRefCnt
> 0);
11574 mFileInfos
.InsertOrUpdate(
11575 id
, MakeNotNull
<DatabaseFileInfo
*>(
11576 FileInfoManagerGuard
{}, SafeRefPtrFromThis(), id
,
11577 static_cast<nsrefcnt
>(dbRefCnt
)));
11579 mLastFileId
= std::max(id
, mLastFileId
);
11584 mInitialized
.Flip();
11589 nsCOMPtr
<nsIFile
> DatabaseFileManager::GetDirectory() {
11590 if (!this->AssertValid()) {
11594 return GetFileForPath(*mDirectoryPath
);
11597 nsCOMPtr
<nsIFile
> DatabaseFileManager::GetCheckedDirectory() {
11598 auto directory
= GetDirectory();
11599 if (NS_WARN_IF(!directory
)) {
11603 DebugOnly
<bool> exists
;
11604 MOZ_ASSERT(NS_SUCCEEDED(directory
->Exists(&exists
)));
11605 MOZ_ASSERT(exists
);
11607 DebugOnly
<bool> isDirectory
;
11608 MOZ_ASSERT(NS_SUCCEEDED(directory
->IsDirectory(&isDirectory
)));
11609 MOZ_ASSERT(isDirectory
);
11614 nsCOMPtr
<nsIFile
> DatabaseFileManager::GetJournalDirectory() {
11615 if (!this->AssertValid()) {
11619 return GetFileForPath(*mJournalDirectoryPath
);
11622 nsCOMPtr
<nsIFile
> DatabaseFileManager::EnsureJournalDirectory() {
11623 // This can happen on the IO or on a transaction thread.
11624 MOZ_ASSERT(!NS_IsMainThread());
11626 auto journalDirectory
= GetFileForPath(*mJournalDirectoryPath
);
11627 QM_TRY(OkIf(journalDirectory
), nullptr);
11629 QM_TRY_INSPECT(const bool& exists
,
11630 MOZ_TO_RESULT_INVOKE_MEMBER(journalDirectory
, Exists
),
11634 QM_TRY_INSPECT(const bool& isDirectory
,
11635 MOZ_TO_RESULT_INVOKE_MEMBER(journalDirectory
, IsDirectory
),
11638 QM_TRY(OkIf(isDirectory
), nullptr);
11641 MOZ_TO_RESULT(journalDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0755)),
11645 return journalDirectory
;
11649 nsCOMPtr
<nsIFile
> DatabaseFileManager::GetFileForId(nsIFile
* aDirectory
,
11651 MOZ_ASSERT(aDirectory
);
11652 MOZ_ASSERT(aId
> 0);
11654 QM_TRY_RETURN(CloneFileAndAppend(*aDirectory
, IntToString(aId
)), nullptr);
11658 nsCOMPtr
<nsIFile
> DatabaseFileManager::GetCheckedFileForId(nsIFile
* aDirectory
,
11660 auto file
= GetFileForId(aDirectory
, aId
);
11661 if (NS_WARN_IF(!file
)) {
11665 DebugOnly
<bool> exists
;
11666 MOZ_ASSERT(NS_SUCCEEDED(file
->Exists(&exists
)));
11667 MOZ_ASSERT(exists
);
11669 DebugOnly
<bool> isFile
;
11670 MOZ_ASSERT(NS_SUCCEEDED(file
->IsFile(&isFile
)));
11671 MOZ_ASSERT(isFile
);
11677 nsresult
DatabaseFileManager::InitDirectory(nsIFile
& aDirectory
,
11678 nsIFile
& aDatabaseFile
,
11679 const nsACString
& aOrigin
,
11680 uint32_t aTelemetryId
) {
11681 AssertIsOnIOThread();
11684 QM_TRY_INSPECT(const bool& exists
,
11685 MOZ_TO_RESULT_INVOKE_MEMBER(aDirectory
, Exists
));
11691 QM_TRY_INSPECT(const bool& isDirectory
,
11692 MOZ_TO_RESULT_INVOKE_MEMBER(aDirectory
, IsDirectory
));
11693 QM_TRY(OkIf(isDirectory
), NS_ERROR_FAILURE
);
11696 QM_TRY_INSPECT(const auto& journalDirectory
,
11697 CloneFileAndAppend(aDirectory
, kJournalDirectoryName
));
11699 QM_TRY_INSPECT(const bool& exists
,
11700 MOZ_TO_RESULT_INVOKE_MEMBER(journalDirectory
, Exists
));
11703 QM_TRY_INSPECT(const bool& isDirectory
,
11704 MOZ_TO_RESULT_INVOKE_MEMBER(journalDirectory
, IsDirectory
));
11705 QM_TRY(OkIf(isDirectory
), NS_ERROR_FAILURE
);
11707 bool hasJournals
= false;
11709 QM_TRY(CollectEachFile(
11711 [&hasJournals
](const nsCOMPtr
<nsIFile
>& file
) -> Result
<Ok
, nsresult
> {
11713 const auto& leafName
,
11714 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString
, file
, GetLeafName
));
11717 leafName
.ToInteger64(&rv
);
11718 if (NS_SUCCEEDED(rv
)) {
11719 hasJournals
= true;
11721 UNKNOWN_FILE_WARNING(leafName
);
11728 QM_TRY_UNWRAP(const NotNull
<nsCOMPtr
<mozIStorageConnection
>> connection
,
11729 CreateStorageConnection(
11730 aDatabaseFile
, aDirectory
, VoidString(), aOrigin
,
11731 /* aDirectoryLockId */ -1, aTelemetryId
, Nothing
{}));
11733 mozStorageTransaction
transaction(connection
.get(), false);
11735 QM_TRY(MOZ_TO_RESULT(transaction
.Start()))
11737 QM_TRY(MOZ_TO_RESULT(connection
->ExecuteSimpleSQL(
11738 "CREATE VIRTUAL TABLE fs USING filesystem;"_ns
)));
11740 // The parameter names are not used, parameters are bound by index only
11741 // locally in the same function.
11744 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
11745 nsCOMPtr
<mozIStorageStatement
>, *connection
, CreateStatement
,
11746 "SELECT name, (name IN (SELECT id FROM file)) FROM fs WHERE path = :path"_ns
));
11748 QM_TRY_INSPECT(const auto& path
,
11749 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
11750 nsString
, journalDirectory
, GetPath
));
11752 QM_TRY(MOZ_TO_RESULT(stmt
->BindStringByIndex(0, path
)));
11754 QM_TRY(CollectWhileHasResult(
11756 [&aDirectory
, &journalDirectory
](auto& stmt
) -> Result
<Ok
, nsresult
> {
11758 QM_TRY(MOZ_TO_RESULT(stmt
.GetString(0, name
)));
11761 name
.ToInteger64(&rv
);
11762 if (NS_FAILED(rv
)) {
11766 int32_t flag
= stmt
.AsInt32(1);
11769 QM_TRY_INSPECT(const auto& file
,
11770 CloneFileAndAppend(aDirectory
, name
));
11772 if (NS_FAILED(file
->Remove(false))) {
11773 NS_WARNING("Failed to remove orphaned file!");
11777 QM_TRY_INSPECT(const auto& journalFile
,
11778 CloneFileAndAppend(*journalDirectory
, name
));
11780 if (NS_FAILED(journalFile
->Remove(false))) {
11781 NS_WARNING("Failed to remove journal file!");
11787 QM_TRY(MOZ_TO_RESULT(connection
->ExecuteSimpleSQL("DROP TABLE fs;"_ns
)));
11788 QM_TRY(MOZ_TO_RESULT(transaction
.Commit()));
11796 Result
<FileUsageType
, nsresult
> DatabaseFileManager::GetUsage(
11797 nsIFile
* aDirectory
) {
11798 AssertIsOnIOThread();
11799 MOZ_ASSERT(aDirectory
);
11801 FileUsageType usage
;
11803 QM_TRY(TraverseFiles(
11806 [&usage
](nsIFile
& file
, const bool isDirectory
) -> Result
<Ok
, nsresult
> {
11811 // Usually we only use QM_OR_ELSE_LOG_VERBOSE(_IF) with Remove and
11812 // NS_ERROR_FILE_NOT_FOUND check, but the file was found by a directory
11813 // traversal and ToInteger on the name succeeded, so it should be our
11814 // file and if the file disappears, the use of QM_OR_ELSE_WARN_IF is ok
11816 QM_TRY_INSPECT(const auto& thisUsage
,
11817 QM_OR_ELSE_WARN_IF(
11819 MOZ_TO_RESULT_INVOKE_MEMBER(file
, GetFileSize
)
11820 .map([](const int64_t fileSize
) {
11821 return FileUsageType(Some(uint64_t(fileSize
)));
11824 ([](const nsresult rv
) {
11825 return rv
== NS_ERROR_FILE_NOT_FOUND
;
11827 // Fallback. If the file does no longer exist, treat
11829 ErrToDefaultOk
<FileUsageType
>));
11831 usage
+= thisUsage
;
11835 // UnknownDirEntryOp
11836 [](nsIFile
&, const bool) -> Result
<Ok
, nsresult
> { return Ok
{}; }));
11841 nsresult
DatabaseFileManager::SyncDeleteFile(const int64_t aId
) {
11842 MOZ_ASSERT(!mFileInfos
.Contains(aId
));
11844 if (!this->AssertValid()) {
11845 return NS_ERROR_UNEXPECTED
;
11848 const auto directory
= GetDirectory();
11849 QM_TRY(OkIf(directory
), NS_ERROR_FAILURE
);
11851 const auto journalDirectory
= GetJournalDirectory();
11852 QM_TRY(OkIf(journalDirectory
), NS_ERROR_FAILURE
);
11854 const nsCOMPtr
<nsIFile
> file
= GetFileForId(directory
, aId
);
11855 QM_TRY(OkIf(file
), NS_ERROR_FAILURE
);
11857 const nsCOMPtr
<nsIFile
> journalFile
= GetFileForId(journalDirectory
, aId
);
11858 QM_TRY(OkIf(journalFile
), NS_ERROR_FAILURE
);
11860 return SyncDeleteFile(*file
, *journalFile
);
11863 nsresult
DatabaseFileManager::SyncDeleteFile(nsIFile
& aFile
,
11864 nsIFile
& aJournalFile
) const {
11865 QuotaManager
* const quotaManager
=
11866 EnforcingQuota() ? QuotaManager::Get() : nullptr;
11867 MOZ_ASSERT_IF(EnforcingQuota(), quotaManager
);
11869 QM_TRY(MOZ_TO_RESULT(DeleteFile(aFile
, quotaManager
, Type(), OriginMetadata(),
11870 Idempotency::No
)));
11872 QM_TRY(MOZ_TO_RESULT(aJournalFile
.Remove(false)));
11877 nsresult
DatabaseFileManager::Invalidate() {
11878 if (mCipherKeyManager
) {
11879 mCipherKeyManager
->Invalidate();
11882 QM_TRY(MOZ_TO_RESULT(FileInfoManager::Invalidate()));
11887 /*******************************************************************************
11889 ******************************************************************************/
11891 QuotaClient
* QuotaClient::sInstance
= nullptr;
11893 QuotaClient::QuotaClient() : mDeleteTimer(NS_NewTimer()) {
11894 AssertIsOnBackgroundThread();
11895 MOZ_ASSERT(!sInstance
, "We expect this to be a singleton!");
11896 MOZ_ASSERT(!gTelemetryIdMutex
);
11898 // Always create this so that later access to gTelemetryIdHashtable can be
11899 // properly synchronized.
11900 gTelemetryIdMutex
= new Mutex("IndexedDB gTelemetryIdMutex");
11902 gStorageDatabaseNameMutex
= new Mutex("IndexedDB gStorageDatabaseNameMutex");
11907 QuotaClient::~QuotaClient() {
11908 AssertIsOnBackgroundThread();
11909 MOZ_ASSERT(sInstance
== this, "We expect this to be a singleton!");
11910 MOZ_ASSERT(gTelemetryIdMutex
);
11911 MOZ_ASSERT(!mMaintenanceThreadPool
);
11913 // No one else should be able to touch gTelemetryIdHashtable now that the
11914 // QuotaClient has gone away.
11915 gTelemetryIdHashtable
= nullptr;
11916 gTelemetryIdMutex
= nullptr;
11918 gStorageDatabaseNameHashtable
= nullptr;
11919 gStorageDatabaseNameMutex
= nullptr;
11921 sInstance
= nullptr;
11924 nsresult
QuotaClient::AsyncDeleteFile(DatabaseFileManager
* aFileManager
,
11926 AssertIsOnBackgroundThread();
11928 if (IsShuttingDownOnBackgroundThread()) {
11929 // Whoops! We want to delete an IndexedDB disk-backed File but it's too late
11930 // to actually delete the file! This means we're going to "leak" the file
11931 // and leave it around when we shouldn't! (The file will stay around until
11932 // next storage initialization is triggered when the app is started again).
11933 // Fixing this is tracked by bug 1539377.
11938 MOZ_ASSERT(mDeleteTimer
);
11939 MOZ_ALWAYS_SUCCEEDS(mDeleteTimer
->Cancel());
11941 QM_TRY(MOZ_TO_RESULT(mDeleteTimer
->InitWithNamedFuncCallback(
11942 DeleteTimerCallback
, this, kDeleteTimeoutMs
, nsITimer::TYPE_ONE_SHOT
,
11943 "dom::indexeddb::QuotaClient::AsyncDeleteFile")));
11945 mPendingDeleteInfos
.GetOrInsertNew(aFileManager
)->AppendElement(aFileId
);
11950 nsresult
QuotaClient::FlushPendingFileDeletions() {
11951 AssertIsOnBackgroundThread();
11953 QM_TRY(MOZ_TO_RESULT(mDeleteTimer
->Cancel()));
11955 DeleteTimerCallback(mDeleteTimer
, this);
11960 nsThreadPool
* QuotaClient::GetOrCreateThreadPool() {
11961 AssertIsOnBackgroundThread();
11962 MOZ_ASSERT(!IsShuttingDownOnBackgroundThread());
11964 if (!mMaintenanceThreadPool
) {
11965 RefPtr
<nsThreadPool
> threadPool
= new nsThreadPool();
11967 // PR_GetNumberOfProcessors() can return -1 on error, so make sure we
11968 // don't set some huge number here. We add 2 in case some threads block on
11970 const uint32_t threadCount
=
11971 std::max(int32_t(PR_GetNumberOfProcessors()), int32_t(1)) + 2;
11973 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetThreadLimit(threadCount
));
11975 // Don't keep more than one idle thread.
11976 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetIdleThreadLimit(1));
11978 // Don't keep idle threads alive very long.
11979 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetIdleThreadTimeout(5 * PR_MSEC_PER_SEC
));
11981 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetName("IndexedDB Mnt"_ns
));
11983 mMaintenanceThreadPool
= std::move(threadPool
);
11986 return mMaintenanceThreadPool
;
11989 mozilla::dom::quota::Client::Type
QuotaClient::GetType() {
11990 return QuotaClient::IDB
;
11993 nsresult
QuotaClient::UpgradeStorageFrom1_0To2_0(nsIFile
* aDirectory
) {
11994 AssertIsOnIOThread();
11995 MOZ_ASSERT(aDirectory
);
11997 QM_TRY_INSPECT((const auto& [subdirsToProcess
, databaseFilenames
]),
11998 GetDatabaseFilenames(*aDirectory
,
11999 /* aCanceled */ AtomicBool
{false}));
12001 QM_TRY(CollectEachInRange(
12003 [&databaseFilenames
= databaseFilenames
,
12004 aDirectory
](const nsAString
& subdirName
) -> Result
<Ok
, nsresult
> {
12005 // If the directory has the correct suffix then it should exist in
12006 // databaseFilenames.
12007 nsDependentSubstring subdirNameBase
;
12008 if (GetFilenameBase(subdirName
, kFileManagerDirectoryNameSuffix
,
12010 QM_WARNONLY_TRY(OkIf(databaseFilenames
.Contains(subdirNameBase
)));
12014 // The directory didn't have the right suffix but we might need to
12015 // rename it. Check to see if we have a database that references this
12018 const auto& subdirNameWithSuffix
,
12019 ([&databaseFilenames
,
12020 &subdirName
]() -> Result
<nsAutoString
, NotOk
> {
12021 if (databaseFilenames
.Contains(subdirName
)) {
12022 return nsAutoString
{subdirName
+
12023 kFileManagerDirectoryNameSuffix
};
12026 // Windows doesn't allow a directory to end with a dot ('.'), so
12027 // we have to check that possibility here too. We do this on all
12028 // platforms, because the origin directory may have been created
12029 // on Windows and now accessed on different OS.
12030 const nsAutoString subdirNameWithDot
= subdirName
+ u
"."_ns
;
12031 QM_TRY(OkIf(databaseFilenames
.Contains(subdirNameWithDot
)),
12034 return nsAutoString
{subdirNameWithDot
+
12035 kFileManagerDirectoryNameSuffix
};
12039 // We do have a database that uses this subdir so we should rename it
12041 QM_TRY_INSPECT(const auto& subdir
,
12042 CloneFileAndAppend(*aDirectory
, subdirName
));
12044 DebugOnly
<bool> isDirectory
;
12045 MOZ_ASSERT(NS_SUCCEEDED(subdir
->IsDirectory(&isDirectory
)));
12046 MOZ_ASSERT(isDirectory
);
12048 // Check if the subdir with suffix already exists before renaming.
12049 QM_TRY_INSPECT(const auto& subdirWithSuffix
,
12050 CloneFileAndAppend(*aDirectory
, subdirNameWithSuffix
));
12052 QM_TRY_INSPECT(const bool& exists
,
12053 MOZ_TO_RESULT_INVOKE_MEMBER(subdirWithSuffix
, Exists
));
12056 IDB_WARNING("Deleting old %s files directory!",
12057 NS_ConvertUTF16toUTF8(subdirName
).get());
12059 QM_TRY(MOZ_TO_RESULT(subdir
->Remove(/* aRecursive */ true)));
12064 // Finally, rename the subdir.
12065 QM_TRY(MOZ_TO_RESULT(subdir
->RenameTo(nullptr, subdirNameWithSuffix
)));
12073 nsresult
QuotaClient::UpgradeStorageFrom2_1To2_2(nsIFile
* aDirectory
) {
12074 AssertIsOnIOThread();
12075 MOZ_ASSERT(aDirectory
);
12077 QM_TRY(CollectEachFile(
12078 *aDirectory
, [](const nsCOMPtr
<nsIFile
>& file
) -> Result
<Ok
, nsresult
> {
12079 QM_TRY_INSPECT(const auto& dirEntryKind
, GetDirEntryKind(*file
));
12081 switch (dirEntryKind
) {
12082 case nsIFileKind::ExistsAsDirectory
:
12085 case nsIFileKind::ExistsAsFile
: {
12087 const auto& leafName
,
12088 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString
, file
, GetLeafName
));
12090 // It's reported that files ending with ".tmp" somehow live in the
12091 // indexedDB directories in Bug 1503883. Such files shouldn't exist
12092 // in the indexedDB directory so remove them in this upgrade.
12093 if (StringEndsWith(leafName
, u
".tmp"_ns
)) {
12094 IDB_WARNING("Deleting unknown temporary file!");
12096 QM_TRY(MOZ_TO_RESULT(file
->Remove(false)));
12102 case nsIFileKind::DoesNotExist
:
12103 // Ignore files that got removed externally while iterating.
12113 Result
<UsageInfo
, nsresult
> QuotaClient::InitOrigin(
12114 PersistenceType aPersistenceType
, const OriginMetadata
& aOriginMetadata
,
12115 const AtomicBool
& aCanceled
) {
12116 AssertIsOnIOThread();
12118 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(this, GetUsageForOriginInternal
,
12119 aPersistenceType
, aOriginMetadata
,
12121 /* aInitializing*/ true));
12124 nsresult
QuotaClient::InitOriginWithoutTracking(
12125 PersistenceType aPersistenceType
, const OriginMetadata
& aOriginMetadata
,
12126 const AtomicBool
& aCanceled
) {
12127 AssertIsOnIOThread();
12129 return GetUsageForOriginInternal(aPersistenceType
, aOriginMetadata
, aCanceled
,
12130 /* aInitializing*/ true, nullptr);
12133 Result
<UsageInfo
, nsresult
> QuotaClient::GetUsageForOrigin(
12134 PersistenceType aPersistenceType
, const OriginMetadata
& aOriginMetadata
,
12135 const AtomicBool
& aCanceled
) {
12136 AssertIsOnIOThread();
12138 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(this, GetUsageForOriginInternal
,
12139 aPersistenceType
, aOriginMetadata
,
12141 /* aInitializing*/ false));
12144 nsresult
QuotaClient::GetUsageForOriginInternal(
12145 PersistenceType aPersistenceType
, const OriginMetadata
& aOriginMetadata
,
12146 const AtomicBool
& aCanceled
, const bool aInitializing
,
12147 UsageInfo
* aUsageInfo
) {
12148 AssertIsOnIOThread();
12149 MOZ_ASSERT(aOriginMetadata
.mPersistenceType
== aPersistenceType
);
12151 QM_TRY_INSPECT(const nsCOMPtr
<nsIFile
>& directory
,
12152 GetDirectory(aOriginMetadata
));
12154 // We need to see if there are any files in the directory already. If they
12155 // are database files then we need to cleanup stored files (if it's needed)
12156 // and also get the usage.
12158 // XXX Can we avoid unwrapping into non-const variables here? (Only
12159 // databaseFilenames is currently modified below)
12160 QM_TRY_UNWRAP((auto [subdirsToProcess
, databaseFilenames
, obsoleteFilenames
]),
12161 GetDatabaseFilenames
<ObsoleteFilenamesHandling::Include
>(
12162 *directory
, aCanceled
));
12164 if (aInitializing
) {
12165 QM_TRY(CollectEachInRange(
12167 [&directory
, &obsoleteFilenames
= obsoleteFilenames
,
12168 &databaseFilenames
= databaseFilenames
, aPersistenceType
,
12170 const nsAString
& subdirName
) -> Result
<Ok
, nsresult
> {
12171 // The directory must have the correct suffix.
12172 nsDependentSubstring subdirNameBase
;
12173 QM_TRY(QM_OR_ELSE_WARN(
12175 ([&subdirName
, &subdirNameBase
] {
12176 QM_TRY_RETURN(OkIf(GetFilenameBase(
12177 subdirName
, kFileManagerDirectoryNameSuffix
,
12182 &subdirName
](const NotOk
) -> Result
<Ok
, nsresult
> {
12183 // If there is an unexpected directory in the idb
12184 // directory, trying to delete at first instead of
12185 // breaking the whole initialization.
12186 QM_TRY(MOZ_TO_RESULT(
12187 DeleteFilesNoQuota(directory
, subdirName
)),
12188 Err(NS_ERROR_UNEXPECTED
));
12194 if (obsoleteFilenames
.Contains(subdirNameBase
)) {
12195 // If this fails, it probably means we are in a serious situation.
12196 // e.g. Filesystem corruption. Will handle this in bug 1521541.
12197 QM_TRY(MOZ_TO_RESULT(RemoveDatabaseFilesAndDirectory(
12198 *directory
, subdirNameBase
, /* aQuotaManager */ nullptr,
12199 aPersistenceType
, aOriginMetadata
,
12200 /* aDatabaseName */ u
""_ns
)),
12201 Err(NS_ERROR_UNEXPECTED
));
12203 databaseFilenames
.Remove(subdirNameBase
);
12207 // The directory base must exist in databaseFilenames.
12208 // If there is an unexpected directory in the idb directory, trying to
12209 // delete at first instead of breaking the whole initialization.
12211 // XXX This is still somewhat quirky. It would be nice to make it
12212 // clear that the warning handler is infallible, which would also
12213 // remove the need for the error type conversion.
12214 QM_WARNONLY_TRY(QM_OR_ELSE_WARN(
12216 OkIf(databaseFilenames
.Contains(subdirNameBase
))
12217 .mapErr([](const NotOk
) { return NS_ERROR_FAILURE
; }),
12220 &subdirName
](const nsresult
) -> Result
<Ok
, nsresult
> {
12221 // XXX It seems if we really got here, we can fail the
12222 // MOZ_ASSERT(!quotaManager->IsTemporaryStorageInitializedInternal());
12223 // assertion in DeleteFilesNoQuota.
12224 QM_TRY(MOZ_TO_RESULT(DeleteFilesNoQuota(directory
, subdirName
)),
12225 Err(NS_ERROR_UNEXPECTED
));
12234 for (const auto& databaseFilename
: databaseFilenames
) {
12240 const auto& fmDirectory
,
12241 CloneFileAndAppend(*directory
,
12242 databaseFilename
+ kFileManagerDirectoryNameSuffix
));
12245 const auto& databaseFile
,
12246 CloneFileAndAppend(*directory
, databaseFilename
+ kSQLiteSuffix
));
12248 if (aInitializing
) {
12249 QM_TRY(MOZ_TO_RESULT(DatabaseFileManager::InitDirectory(
12250 *fmDirectory
, *databaseFile
, aOriginMetadata
.mOrigin
,
12251 TelemetryIdForFile(databaseFile
))));
12256 QM_TRY_INSPECT(const int64_t& fileSize
,
12257 MOZ_TO_RESULT_INVOKE_MEMBER(databaseFile
, GetFileSize
));
12259 MOZ_ASSERT(fileSize
>= 0);
12261 *aUsageInfo
+= DatabaseUsageType(Some(uint64_t(fileSize
)));
12265 QM_TRY_INSPECT(const auto& walFile
,
12266 CloneFileAndAppend(*directory
,
12267 databaseFilename
+ kSQLiteWALSuffix
));
12269 // QM_OR_ELSE_WARN_IF is not used here since we just want to log
12270 // NS_ERROR_FILE_NOT_FOUND result and not spam the reports (the -wal
12271 // file doesn't have to exist).
12272 QM_TRY_INSPECT(const int64_t& walFileSize
,
12273 QM_OR_ELSE_LOG_VERBOSE_IF(
12275 MOZ_TO_RESULT_INVOKE_MEMBER(walFile
, GetFileSize
),
12277 ([](const nsresult rv
) {
12278 return rv
== NS_ERROR_FILE_NOT_FOUND
;
12281 (ErrToOk
<0, int64_t>)));
12282 MOZ_ASSERT(walFileSize
>= 0);
12283 *aUsageInfo
+= DatabaseUsageType(Some(uint64_t(walFileSize
)));
12287 QM_TRY_INSPECT(const auto& fileUsage
,
12288 DatabaseFileManager::GetUsage(fmDirectory
));
12290 *aUsageInfo
+= fileUsage
;
12298 void QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType
,
12299 const nsACString
& aOrigin
) {
12300 AssertIsOnIOThread();
12302 if (IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get()) {
12303 mgr
->InvalidateFileManagers(aPersistenceType
, aOrigin
);
12307 void QuotaClient::OnRepositoryClearCompleted(PersistenceType aPersistenceType
) {
12308 AssertIsOnIOThread();
12310 if (IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get()) {
12311 mgr
->InvalidateFileManagers(aPersistenceType
);
12315 void QuotaClient::ReleaseIOThreadObjects() {
12316 AssertIsOnIOThread();
12318 if (IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get()) {
12319 mgr
->InvalidateAllFileManagers();
12323 void QuotaClient::AbortOperationsForLocks(
12324 const DirectoryLockIdTable
& aDirectoryLockIds
) {
12325 AssertIsOnBackgroundThread();
12327 InvalidateLiveDatabasesMatching([&aDirectoryLockIds
](const auto& database
) {
12328 // If the database is registered in gLiveDatabaseHashtable then it must have
12329 // a directory lock.
12330 return IsLockForObjectContainedInLockTable(database
, aDirectoryLockIds
);
12334 void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId
) {
12335 AssertIsOnBackgroundThread();
12337 InvalidateLiveDatabasesMatching([&aContentParentId
](const auto& database
) {
12338 return database
.IsOwnedByProcess(aContentParentId
);
12342 void QuotaClient::AbortAllOperations() {
12343 AssertIsOnBackgroundThread();
12345 AbortAllMaintenances();
12347 InvalidateLiveDatabasesMatching([](const auto&) { return true; });
12350 void QuotaClient::StartIdleMaintenance() {
12351 AssertIsOnBackgroundThread();
12352 if (IsShuttingDownOnBackgroundThread()) {
12353 MOZ_ASSERT(false, "!IsShuttingDownOnBackgroundThread()");
12357 if (!mBackgroundThread
) {
12358 mBackgroundThread
= GetCurrentSerialEventTarget();
12361 mMaintenanceQueue
.EmplaceBack(MakeRefPtr
<Maintenance
>(this));
12362 ProcessMaintenanceQueue();
12365 void QuotaClient::StopIdleMaintenance() {
12366 AssertIsOnBackgroundThread();
12368 AbortAllMaintenances();
12371 void QuotaClient::InitiateShutdown() {
12372 AssertIsOnBackgroundThread();
12373 MOZ_ASSERT(IsShuttingDownOnBackgroundThread());
12375 if (mDeleteTimer
) {
12376 // QuotaClient::AsyncDeleteFile will not schedule new timers beyond
12377 // shutdown. And we expect all critical (PBM) deletions to have been
12378 // triggered before this point via ClearPrivateRepository (w/out using
12379 // DeleteFilesRunnable at all).
12380 mDeleteTimer
->Cancel();
12381 mDeleteTimer
= nullptr;
12382 mPendingDeleteInfos
.Clear();
12385 AbortAllOperations();
12388 bool QuotaClient::IsShutdownCompleted() const {
12389 return (!gFactoryOps
|| gFactoryOps
->IsEmpty()) &&
12390 (!gLiveDatabaseHashtable
|| !gLiveDatabaseHashtable
->Count()) &&
12391 !mCurrentMaintenance
&& !DeleteFilesRunnable::IsDeletionPending();
12394 void QuotaClient::ForceKillActors() {
12395 // Currently we don't implement force killing actors.
12398 nsCString
QuotaClient::GetShutdownStatus() const {
12399 AssertIsOnBackgroundThread();
12403 if (gFactoryOps
&& !gFactoryOps
->IsEmpty()) {
12404 data
.Append("FactoryOperations: "_ns
+
12405 IntToCString(static_cast<uint32_t>(gFactoryOps
->Length())) +
12408 // XXX It might be confusing to remove duplicates here, as the actual list
12409 // won't match the count then.
12410 nsTHashSet
<nsCString
> ids
;
12411 std::transform(gFactoryOps
->cbegin(), gFactoryOps
->cend(),
12412 MakeInserter(ids
), [](const auto& factoryOp
) {
12413 MOZ_ASSERT(factoryOp
);
12416 factoryOp
->Stringify(id
);
12420 StringJoinAppend(data
, ", "_ns
, ids
);
12422 data
.Append(")\n");
12425 if (gLiveDatabaseHashtable
&& gLiveDatabaseHashtable
->Count()) {
12426 data
.Append("LiveDatabases: "_ns
+
12427 IntToCString(gLiveDatabaseHashtable
->Count()) + " ("_ns
);
12429 // XXX What's the purpose of adding these to a hashtable before joining them
12430 // to the string? (Maybe this used to be an ordered container before???)
12431 nsTHashSet
<nsCString
> ids
;
12433 for (const auto& entry
: gLiveDatabaseHashtable
->Values()) {
12436 std::transform(entry
->mLiveDatabases
.cbegin(),
12437 entry
->mLiveDatabases
.cend(), MakeInserter(ids
),
12438 [](const auto& database
) {
12440 database
->Stringify(id
);
12445 StringJoinAppend(data
, ", "_ns
, ids
);
12447 data
.Append(")\n");
12450 if (mCurrentMaintenance
) {
12451 data
.Append("IdleMaintenance: 1 (");
12452 mCurrentMaintenance
->Stringify(data
);
12453 data
.Append(")\n");
12459 void QuotaClient::FinalizeShutdown() {
12460 RefPtr
<ConnectionPool
> connectionPool
= gConnectionPool
.get();
12461 if (connectionPool
) {
12462 connectionPool
->Shutdown();
12464 gConnectionPool
= nullptr;
12467 if (mMaintenanceThreadPool
) {
12468 mMaintenanceThreadPool
->Shutdown();
12469 mMaintenanceThreadPool
= nullptr;
12473 void QuotaClient::DeleteTimerCallback(nsITimer
* aTimer
, void* aClosure
) {
12474 AssertIsOnBackgroundThread();
12475 MOZ_ASSERT(aTimer
);
12477 // Even though we do not schedule new timers after shutdown has started,
12478 // an already existing one might fire afterwards (actually we think it
12479 // shouldn't, but there is no reason to enforce this invariant). We can
12480 // just ignore it, the cleanup work is done in InitiateShutdown.
12481 if (NS_WARN_IF(IsShuttingDownOnBackgroundThread())) {
12485 auto* const self
= static_cast<QuotaClient
*>(aClosure
);
12487 MOZ_ASSERT(self
->mDeleteTimer
);
12488 MOZ_ASSERT(SameCOMIdentity(self
->mDeleteTimer
, aTimer
));
12490 for (const auto& pendingDeleteInfoEntry
: self
->mPendingDeleteInfos
) {
12491 const auto& key
= pendingDeleteInfoEntry
.GetKey();
12492 const auto& value
= pendingDeleteInfoEntry
.GetData();
12493 MOZ_ASSERT(!value
->IsEmpty());
12495 RefPtr
<DeleteFilesRunnable
> runnable
= new DeleteFilesRunnable(
12496 SafeRefPtr
{key
, AcquireStrongRefFromRawPtr
{}}, std::move(*value
));
12498 MOZ_ASSERT(value
->IsEmpty());
12500 runnable
->RunImmediately();
12503 self
->mPendingDeleteInfos
.Clear();
12506 void QuotaClient::AbortAllMaintenances() {
12507 if (mCurrentMaintenance
) {
12508 mCurrentMaintenance
->Abort();
12511 for (const auto& maintenance
: mMaintenanceQueue
) {
12512 maintenance
->Abort();
12516 Result
<nsCOMPtr
<nsIFile
>, nsresult
> QuotaClient::GetDirectory(
12517 const OriginMetadata
& aOriginMetadata
) {
12518 QuotaManager
* const quotaManager
= QuotaManager::Get();
12519 NS_ASSERTION(quotaManager
, "This should never fail!");
12521 QM_TRY_INSPECT(const auto& directory
,
12522 quotaManager
->GetOriginDirectory(aOriginMetadata
));
12524 MOZ_ASSERT(directory
);
12526 QM_TRY(MOZ_TO_RESULT(
12527 directory
->Append(NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME
))));
12532 template <QuotaClient::ObsoleteFilenamesHandling ObsoleteFilenames
>
12533 Result
<QuotaClient::GetDatabaseFilenamesResult
<ObsoleteFilenames
>, nsresult
>
12534 QuotaClient::GetDatabaseFilenames(nsIFile
& aDirectory
,
12535 const AtomicBool
& aCanceled
) {
12536 AssertIsOnIOThread();
12538 GetDatabaseFilenamesResult
<ObsoleteFilenames
> result
;
12540 QM_TRY(CollectEachFileAtomicCancelable(
12541 aDirectory
, aCanceled
,
12542 [&result
](const nsCOMPtr
<nsIFile
>& file
) -> Result
<Ok
, nsresult
> {
12543 QM_TRY_INSPECT(const auto& leafName
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
12544 nsString
, file
, GetLeafName
));
12546 QM_TRY_INSPECT(const auto& dirEntryKind
, GetDirEntryKind(*file
));
12548 switch (dirEntryKind
) {
12549 case nsIFileKind::ExistsAsDirectory
:
12550 result
.subdirsToProcess
.AppendElement(leafName
);
12553 case nsIFileKind::ExistsAsFile
: {
12554 if constexpr (ObsoleteFilenames
==
12555 ObsoleteFilenamesHandling::Include
) {
12556 if (StringBeginsWith(leafName
, kIdbDeletionMarkerFilePrefix
)) {
12557 result
.obsoleteFilenames
.Insert(
12558 Substring(leafName
, kIdbDeletionMarkerFilePrefix
.Length()));
12563 // Skip OS metadata files. These files are only used in different
12564 // platforms, but the profile can be shared across different
12565 // operating systems, so we check it on all platforms.
12566 if (QuotaManager::IsOSMetadata(leafName
)) {
12570 // Skip files starting with ".".
12571 if (QuotaManager::IsDotFile(leafName
)) {
12575 // Skip SQLite temporary files. These files take up space on disk
12576 // but will be deleted as soon as the database is opened, so we
12577 // don't count them towards quota.
12578 if (StringEndsWith(leafName
, kSQLiteJournalSuffix
) ||
12579 StringEndsWith(leafName
, kSQLiteSHMSuffix
)) {
12583 // The SQLite WAL file does count towards quota, but it is handled
12584 // below once we find the actual database file.
12585 if (StringEndsWith(leafName
, kSQLiteWALSuffix
)) {
12589 nsDependentSubstring leafNameBase
;
12590 if (!GetFilenameBase(leafName
, kSQLiteSuffix
, leafNameBase
)) {
12591 UNKNOWN_FILE_WARNING(leafName
);
12595 result
.databaseFilenames
.Insert(leafNameBase
);
12599 case nsIFileKind::DoesNotExist
:
12600 // Ignore files that got removed externally while iterating.
12610 void QuotaClient::ProcessMaintenanceQueue() {
12611 AssertIsOnBackgroundThread();
12613 if (mCurrentMaintenance
|| mMaintenanceQueue
.IsEmpty()) {
12617 mCurrentMaintenance
= mMaintenanceQueue
[0];
12618 mMaintenanceQueue
.RemoveElementAt(0);
12620 mCurrentMaintenance
->RunImmediately();
12623 /*******************************************************************************
12624 * DeleteFilesRunnable
12625 ******************************************************************************/
12627 uint64_t DeleteFilesRunnable::sPendingRunnables
= 0;
12629 DeleteFilesRunnable::DeleteFilesRunnable(
12630 SafeRefPtr
<DatabaseFileManager
> aFileManager
, nsTArray
<int64_t>&& aFileIds
)
12631 : Runnable("dom::indexeddb::DeleteFilesRunnable"),
12632 mOwningEventTarget(GetCurrentSerialEventTarget()),
12633 mFileManager(std::move(aFileManager
)),
12634 mFileIds(std::move(aFileIds
)),
12635 mState(State_Initial
) {}
12638 DeleteFilesRunnable::~DeleteFilesRunnable() {
12639 MOZ_ASSERT(!mDEBUGCountsAsPending
);
12643 void DeleteFilesRunnable::RunImmediately() {
12644 AssertIsOnBackgroundThread();
12645 MOZ_ASSERT(mState
== State_Initial
);
12647 Unused
<< this->Run();
12650 void DeleteFilesRunnable::Open() {
12651 AssertIsOnBackgroundThread();
12652 MOZ_ASSERT(mState
== State_Initial
);
12654 MOZ_ASSERT(!mDEBUGCountsAsPending
);
12655 sPendingRunnables
++;
12656 DEBUGONLY(mDEBUGCountsAsPending
= true);
12658 QuotaManager
* const quotaManager
= QuotaManager::Get();
12659 if (NS_WARN_IF(!quotaManager
)) {
12664 mState
= State_DirectoryOpenPending
;
12667 ->OpenClientDirectory(
12668 {mFileManager
->OriginMetadata(), quota::Client::IDB
})
12670 GetCurrentSerialEventTarget(), __func__
,
12671 [self
= RefPtr(this)](
12672 const ClientDirectoryLockPromise::ResolveOrRejectValue
& aValue
) {
12673 if (aValue
.IsResolve()) {
12674 self
->DirectoryLockAcquired(aValue
.ResolveValue());
12676 self
->DirectoryLockFailed();
12681 void DeleteFilesRunnable::DoDatabaseWork() {
12682 AssertIsOnIOThread();
12683 MOZ_ASSERT(mState
== State_DatabaseWorkOpen
);
12685 if (!mFileManager
->Invalidated()) {
12686 for (int64_t fileId
: mFileIds
) {
12687 if (NS_FAILED(mFileManager
->SyncDeleteFile(fileId
))) {
12688 NS_WARNING("Failed to delete file!");
12696 void DeleteFilesRunnable::Finish() {
12697 MOZ_ASSERT(mState
!= State_UnblockingOpen
);
12699 // Must set mState before dispatching otherwise we will race with the main
12701 mState
= State_UnblockingOpen
;
12703 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
));
12706 void DeleteFilesRunnable::UnblockOpen() {
12707 AssertIsOnBackgroundThread();
12708 MOZ_ASSERT(mState
== State_UnblockingOpen
);
12710 mDirectoryLock
= nullptr;
12711 MOZ_ASSERT(mDEBUGCountsAsPending
);
12712 sPendingRunnables
--;
12713 DEBUGONLY(mDEBUGCountsAsPending
= false);
12715 mState
= State_Completed
;
12719 DeleteFilesRunnable::Run() {
12721 case State_Initial
:
12725 case State_DatabaseWorkOpen
:
12729 case State_UnblockingOpen
:
12733 case State_DirectoryOpenPending
:
12735 MOZ_CRASH("Should never get here!");
12741 void DeleteFilesRunnable::DirectoryLockAcquired(DirectoryLock
* aLock
) {
12742 AssertIsOnBackgroundThread();
12743 MOZ_ASSERT(mState
== State_DirectoryOpenPending
);
12744 MOZ_ASSERT(!mDirectoryLock
);
12746 mDirectoryLock
= aLock
;
12748 QuotaManager
* const quotaManager
= QuotaManager::Get();
12749 MOZ_ASSERT(quotaManager
);
12751 // Must set this before dispatching otherwise we will race with the IO thread
12752 mState
= State_DatabaseWorkOpen
;
12754 QM_TRY(MOZ_TO_RESULT(
12755 quotaManager
->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL
)),
12756 QM_VOID
, [this](const nsresult
) { Finish(); });
12759 void DeleteFilesRunnable::DirectoryLockFailed() {
12760 AssertIsOnBackgroundThread();
12761 MOZ_ASSERT(mState
== State_DirectoryOpenPending
);
12762 MOZ_ASSERT(!mDirectoryLock
);
12767 void Maintenance::Abort() {
12768 AssertIsOnBackgroundThread();
12770 // Safe because mDatabaseMaintenances is modified
12771 // only in the background thread
12772 for (const auto& aDatabaseMaintenance
: mDatabaseMaintenances
) {
12773 aDatabaseMaintenance
.GetData()->Abort();
12776 // mDirectoryLock must be cleared before transition to finished state
12777 mDirectoryLock
= nullptr;
12781 void Maintenance::RegisterDatabaseMaintenance(
12782 DatabaseMaintenance
* aDatabaseMaintenance
) {
12783 AssertIsOnBackgroundThread();
12784 MOZ_ASSERT(aDatabaseMaintenance
);
12785 MOZ_ASSERT(mState
== State::BeginDatabaseMaintenance
);
12787 !mDatabaseMaintenances
.Contains(aDatabaseMaintenance
->DatabasePath()));
12789 mDatabaseMaintenances
.InsertOrUpdate(aDatabaseMaintenance
->DatabasePath(),
12790 aDatabaseMaintenance
);
12793 void Maintenance::UnregisterDatabaseMaintenance(
12794 DatabaseMaintenance
* aDatabaseMaintenance
) {
12795 AssertIsOnBackgroundThread();
12796 MOZ_ASSERT(aDatabaseMaintenance
);
12797 MOZ_ASSERT(mState
== State::WaitingForDatabaseMaintenancesToComplete
);
12798 MOZ_ASSERT(mDatabaseMaintenances
.Get(aDatabaseMaintenance
->DatabasePath()));
12800 mDatabaseMaintenances
.Remove(aDatabaseMaintenance
->DatabasePath());
12802 if (mDatabaseMaintenances
.Count()) {
12806 for (const auto& completeCallback
: mCompleteCallbacks
) {
12807 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(completeCallback
));
12809 mCompleteCallbacks
.Clear();
12811 mState
= State::Finishing
;
12815 void Maintenance::Stringify(nsACString
& aResult
) const {
12816 AssertIsOnBackgroundThread();
12818 aResult
.Append("DatabaseMaintenances: "_ns
+
12819 IntToCString(mDatabaseMaintenances
.Count()) + " ("_ns
);
12821 // XXX It might be confusing to remove duplicates here, as the actual list
12822 // won't match the count then.
12823 nsTHashSet
<nsCString
> ids
;
12824 std::transform(mDatabaseMaintenances
.Values().cbegin(),
12825 mDatabaseMaintenances
.Values().cend(), MakeInserter(ids
),
12826 [](const auto& entry
) {
12830 entry
->Stringify(id
);
12835 StringJoinAppend(aResult
, ", "_ns
, ids
);
12837 aResult
.Append(")");
12840 nsresult
Maintenance::Start() {
12841 AssertIsOnBackgroundThread();
12842 MOZ_ASSERT(mState
== State::Initial
);
12844 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
12846 return NS_ERROR_ABORT
;
12849 // Make sure that the IndexedDatabaseManager is running so that we can check
12850 // for low disk space mode.
12852 if (IndexedDatabaseManager::Get()) {
12857 mState
= State::CreateIndexedDatabaseManager
;
12858 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
12863 nsresult
Maintenance::CreateIndexedDatabaseManager() {
12864 MOZ_ASSERT(NS_IsMainThread());
12865 MOZ_ASSERT(mState
== State::CreateIndexedDatabaseManager
);
12867 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
12869 return NS_ERROR_ABORT
;
12872 IndexedDatabaseManager
* const mgr
= IndexedDatabaseManager::GetOrCreate();
12873 if (NS_WARN_IF(!mgr
)) {
12874 return NS_ERROR_FAILURE
;
12877 mState
= State::IndexedDatabaseManagerOpen
;
12878 MOZ_ALWAYS_SUCCEEDS(
12879 mQuotaClient
->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL
));
12884 nsresult
Maintenance::OpenDirectory() {
12885 AssertIsOnBackgroundThread();
12886 MOZ_ASSERT(mState
== State::Initial
||
12887 mState
== State::IndexedDatabaseManagerOpen
);
12888 MOZ_ASSERT(!mDirectoryLock
);
12889 MOZ_ASSERT(QuotaManager::Get());
12891 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
12893 return NS_ERROR_ABORT
;
12896 QuotaManager
* quotaManager
= QuotaManager::Get();
12897 MOZ_ASSERT(quotaManager
);
12899 // Get a shared lock for <profile>/storage/*/*/idb
12901 mState
= State::DirectoryOpenPending
;
12904 ->OpenStorageDirectory(
12905 Nullable
<PersistenceType
>(), OriginScope::FromNull(),
12906 Nullable
<Client::Type
>(Client::IDB
), /* aExclusive */ false,
12907 DirectoryLockCategory::None
, SomeRef(mPendingDirectoryLock
))
12908 ->Then(GetCurrentSerialEventTarget(), __func__
,
12909 [self
= RefPtr(this)](
12910 const UniversalDirectoryLockPromise::ResolveOrRejectValue
&
12912 if (aValue
.IsResolve()) {
12913 self
->DirectoryLockAcquired(aValue
.ResolveValue());
12915 self
->DirectoryLockFailed();
12922 nsresult
Maintenance::DirectoryOpen() {
12923 AssertIsOnBackgroundThread();
12924 MOZ_ASSERT(mState
== State::DirectoryOpenPending
);
12925 MOZ_ASSERT(mDirectoryLock
);
12927 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
12929 return NS_ERROR_ABORT
;
12932 QuotaManager
* const quotaManager
= QuotaManager::Get();
12933 MOZ_ASSERT(quotaManager
);
12935 mState
= State::DirectoryWorkOpen
;
12937 QM_TRY(MOZ_TO_RESULT(
12938 quotaManager
->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL
)),
12944 nsresult
Maintenance::DirectoryWork() {
12945 AssertIsOnIOThread();
12946 MOZ_ASSERT(mState
== State::DirectoryWorkOpen
);
12948 // The storage directory is structured like this:
12950 // <profile>/storage/<persistence>/<origin>/idb/*.sqlite
12952 // We have to find all database files that match any persistence type and any
12953 // origin. We ignore anything out of the ordinary for now.
12955 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
12957 return NS_ERROR_ABORT
;
12960 QuotaManager
* const quotaManager
= QuotaManager::Get();
12961 MOZ_ASSERT(quotaManager
);
12963 // Since idle maintenance may occur before temporary storage is initialized,
12964 // make sure it's initialized here (all non-persistent origins need to be
12965 // cleaned up and quota info needs to be loaded for them).
12967 // Don't fail whole idle maintenance in case of an error, the persistent
12968 // repository can still
12970 const bool initTemporaryStorageFailed
= ["aManager
] {
12971 QM_TRY(MOZ_TO_RESULT(
12972 quotaManager
->EnsureTemporaryStorageIsInitializedInternal()),
12977 const nsCOMPtr
<nsIFile
> storageDir
=
12978 GetFileForPath(quotaManager
->GetStoragePath());
12979 QM_TRY(OkIf(storageDir
), NS_ERROR_FAILURE
);
12982 QM_TRY_INSPECT(const bool& exists
,
12983 MOZ_TO_RESULT_INVOKE_MEMBER(storageDir
, Exists
));
12985 // XXX No warning here?
12987 return NS_ERROR_NOT_AVAILABLE
;
12992 QM_TRY_INSPECT(const bool& isDirectory
,
12993 MOZ_TO_RESULT_INVOKE_MEMBER(storageDir
, IsDirectory
));
12995 QM_TRY(OkIf(isDirectory
), NS_ERROR_FAILURE
);
12998 // There are currently only 4 persistence types, and we want to iterate them
13000 static const PersistenceType kPersistenceTypes
[] = {
13001 PERSISTENCE_TYPE_PERSISTENT
, PERSISTENCE_TYPE_DEFAULT
,
13002 PERSISTENCE_TYPE_TEMPORARY
, PERSISTENCE_TYPE_PRIVATE
};
13005 ArrayLength(kPersistenceTypes
) == size_t(PERSISTENCE_TYPE_INVALID
),
13006 "Something changed with available persistence types!");
13008 constexpr auto idbDirName
=
13009 NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME
);
13011 for (const PersistenceType persistenceType
: kPersistenceTypes
) {
13012 // Loop over "<persistence>" directories.
13013 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
13015 return NS_ERROR_ABORT
;
13018 // Don't do any maintenance for private browsing databases, which are only
13020 if (persistenceType
== PERSISTENCE_TYPE_PRIVATE
) {
13024 const bool persistent
= persistenceType
== PERSISTENCE_TYPE_PERSISTENT
;
13026 if (!persistent
&& initTemporaryStorageFailed
) {
13027 // Non-persistent (best effort) repositories can't be processed if
13028 // temporary storage initialization failed.
13032 // XXX persistenceType == PERSISTENCE_TYPE_PERSISTENT shouldn't be a special
13034 const auto persistenceTypeString
=
13035 persistenceType
== PERSISTENCE_TYPE_PERSISTENT
13037 : PersistenceTypeToString(persistenceType
);
13039 QM_TRY_INSPECT(const auto& persistenceDir
,
13040 CloneFileAndAppend(*storageDir
, NS_ConvertASCIItoUTF16(
13041 persistenceTypeString
)));
13044 QM_TRY_INSPECT(const bool& exists
,
13045 MOZ_TO_RESULT_INVOKE_MEMBER(persistenceDir
, Exists
));
13051 QM_TRY_INSPECT(const bool& isDirectory
,
13052 MOZ_TO_RESULT_INVOKE_MEMBER(persistenceDir
, IsDirectory
));
13054 if (NS_WARN_IF(!isDirectory
)) {
13059 // Loop over "<origin>/idb" directories.
13060 QM_TRY(CollectEachFile(
13062 [this, "aManager
, persistent
, persistenceType
, &idbDirName
](
13063 const nsCOMPtr
<nsIFile
>& originDir
) -> Result
<Ok
, nsresult
> {
13064 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
13066 return Err(NS_ERROR_ABORT
);
13069 QM_TRY_INSPECT(const auto& dirEntryKind
, GetDirEntryKind(*originDir
));
13071 switch (dirEntryKind
) {
13072 case nsIFileKind::ExistsAsFile
:
13075 case nsIFileKind::ExistsAsDirectory
: {
13076 // Get the necessary information about the origin
13077 // (GetOriginMetadata also checks if it's a valid origin).
13079 QM_TRY_INSPECT(const auto& metadata
,
13080 quotaManager
->GetOriginMetadata(originDir
),
13081 // Not much we can do here...
13084 // We now use a dedicated repository for private browsing
13085 // databases, but there could be some forgotten private browsing
13086 // databases in other repositories, so it's better to check for
13087 // that and don't do any maintenance for such databases.
13088 if (metadata
.mIsPrivate
) {
13093 // We have to check that all persistent origins are cleaned up,
13094 // but there's no way to do that by one call, we need to
13095 // initialize (and possibly clean up) them one by one
13096 // (EnsureTemporaryStorageIsInitializedInternal cleans up only
13097 // non-persistent origins).
13100 const DebugOnly
<bool> created
,
13101 quotaManager
->EnsurePersistentOriginIsInitialized(metadata
)
13102 .map([](const auto& res
) { return res
.second
; }),
13103 // Not much we can do here...
13106 // We found this origin directory by traversing the repository,
13107 // so EnsurePersistentOriginIsInitialized shouldn't report that
13108 // a new directory has been created.
13109 MOZ_ASSERT(!created
);
13112 QM_TRY_INSPECT(const auto& idbDir
,
13113 CloneFileAndAppend(*originDir
, idbDirName
));
13115 QM_TRY_INSPECT(const bool& exists
,
13116 MOZ_TO_RESULT_INVOKE_MEMBER(idbDir
, Exists
));
13122 QM_TRY_INSPECT(const bool& isDirectory
,
13123 MOZ_TO_RESULT_INVOKE_MEMBER(idbDir
, IsDirectory
));
13125 QM_TRY(OkIf(isDirectory
), Ok
{});
13127 nsTArray
<nsString
> databasePaths
;
13129 // Loop over files in the "idb" directory.
13130 QM_TRY(CollectEachFile(
13132 [this, &databasePaths
](const nsCOMPtr
<nsIFile
>& idbDirFile
)
13133 -> Result
<Ok
, nsresult
> {
13134 if (NS_WARN_IF(QuotaClient::
13135 IsShuttingDownOnNonBackgroundThread()) ||
13137 return Err(NS_ERROR_ABORT
);
13140 QM_TRY_UNWRAP(auto idbFilePath
,
13141 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
13142 nsString
, idbDirFile
, GetPath
));
13144 if (!StringEndsWith(idbFilePath
, kSQLiteSuffix
)) {
13148 QM_TRY_INSPECT(const auto& dirEntryKind
,
13149 GetDirEntryKind(*idbDirFile
));
13151 switch (dirEntryKind
) {
13152 case nsIFileKind::ExistsAsDirectory
:
13155 case nsIFileKind::ExistsAsFile
:
13156 // Found a database.
13158 MOZ_ASSERT(!databasePaths
.Contains(idbFilePath
));
13160 databasePaths
.AppendElement(std::move(idbFilePath
));
13163 case nsIFileKind::DoesNotExist
:
13164 // Ignore files that got removed externally while
13172 if (!databasePaths
.IsEmpty()) {
13173 mDirectoryInfos
.EmplaceBack(persistenceType
, metadata
,
13174 std::move(databasePaths
));
13180 case nsIFileKind::DoesNotExist
:
13181 // Ignore files that got removed externally while iterating.
13189 mState
= State::BeginDatabaseMaintenance
;
13191 MOZ_ALWAYS_SUCCEEDS(
13192 mQuotaClient
->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL
));
13197 nsresult
Maintenance::BeginDatabaseMaintenance() {
13198 AssertIsOnBackgroundThread();
13199 MOZ_ASSERT(mState
== State::BeginDatabaseMaintenance
);
13201 class MOZ_STACK_CLASS Helper final
{
13203 static bool IsSafeToRunMaintenance(const nsAString
& aDatabasePath
) {
13205 for (uint32_t index
= gFactoryOps
->Length(); index
> 0; index
--) {
13206 CheckedUnsafePtr
<FactoryOp
>& existingOp
= (*gFactoryOps
)[index
- 1];
13208 if (existingOp
->DatabaseNameRef().isNothing()) {
13212 if (!existingOp
->DatabaseFilePathIsKnown()) {
13216 if (existingOp
->DatabaseFilePath() == aDatabasePath
) {
13222 if (gLiveDatabaseHashtable
) {
13223 return std::all_of(
13224 gLiveDatabaseHashtable
->Values().cbegin(),
13225 gLiveDatabaseHashtable
->Values().cend(),
13226 [&aDatabasePath
](const auto& liveDatabasesEntry
) {
13227 const auto& liveDatabases
= liveDatabasesEntry
->mLiveDatabases
;
13228 return std::all_of(liveDatabases
.cbegin(), liveDatabases
.cend(),
13229 [&aDatabasePath
](const auto& database
) {
13230 return database
->FilePath() != aDatabasePath
;
13239 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
13241 return NS_ERROR_ABORT
;
13244 RefPtr
<nsThreadPool
> threadPool
;
13246 for (DirectoryInfo
& directoryInfo
: mDirectoryInfos
) {
13247 RefPtr
<DirectoryLock
> directoryLock
;
13249 for (const nsAString
& databasePath
: *directoryInfo
.mDatabasePaths
) {
13250 if (Helper::IsSafeToRunMaintenance(databasePath
)) {
13251 if (!directoryLock
) {
13252 directoryLock
= mDirectoryLock
->SpecializeForClient(
13253 directoryInfo
.mPersistenceType
, *directoryInfo
.mOriginMetadata
,
13255 MOZ_ASSERT(directoryLock
);
13258 // No key needs to be passed here, because we skip encrypted databases
13259 // in DoDirectoryWork as long as they are only used in private browsing
13261 const auto databaseMaintenance
= MakeRefPtr
<DatabaseMaintenance
>(
13262 this, directoryLock
, directoryInfo
.mPersistenceType
,
13263 *directoryInfo
.mOriginMetadata
, databasePath
, Nothing
{});
13266 threadPool
= mQuotaClient
->GetOrCreateThreadPool();
13267 MOZ_ASSERT(threadPool
);
13270 // Perform database maintenance on a TaskQueue, as database connections
13271 // require a serial event target when being opened in order to allow
13272 // memory pressure notifications to clear caches (bug 1806751).
13273 const auto taskQueue
= TaskQueue::Create(
13274 do_AddRef(threadPool
), "IndexedDB Database Maintenance");
13276 MOZ_ALWAYS_SUCCEEDS(
13277 taskQueue
->Dispatch(databaseMaintenance
, NS_DISPATCH_NORMAL
));
13279 RegisterDatabaseMaintenance(databaseMaintenance
);
13284 mDirectoryInfos
.Clear();
13286 mDirectoryLock
= nullptr;
13288 if (mDatabaseMaintenances
.Count()) {
13289 mState
= State::WaitingForDatabaseMaintenancesToComplete
;
13291 mState
= State::Finishing
;
13298 void Maintenance::Finish() {
13299 AssertIsOnBackgroundThread();
13300 MOZ_ASSERT(!mDirectoryLock
);
13301 MOZ_ASSERT(mState
== State::Finishing
);
13303 if (NS_FAILED(mResultCode
)) {
13304 nsCString errorName
;
13305 GetErrorName(mResultCode
, errorName
);
13307 IDB_WARNING("Maintenance finished with error: %s", errorName
.get());
13310 // It can happen that we are only referenced by mCurrentMaintenance which is
13311 // cleared in NoteFinishedMaintenance()
13312 const RefPtr
<Maintenance
> kungFuDeathGrip
= this;
13314 mQuotaClient
->NoteFinishedMaintenance(this);
13316 mState
= State::Complete
;
13320 Maintenance::Run() {
13321 MOZ_ASSERT(mState
!= State::Complete
);
13323 const auto handleError
= [this](const nsresult rv
) {
13324 if (mState
!= State::Finishing
) {
13325 if (NS_SUCCEEDED(mResultCode
)) {
13329 // Must set mState before dispatching otherwise we will race with the
13331 mState
= State::Finishing
;
13333 if (IsOnBackgroundThread()) {
13336 MOZ_ALWAYS_SUCCEEDS(mQuotaClient
->BackgroundThread()->Dispatch(
13337 this, NS_DISPATCH_NORMAL
));
13343 case State::Initial
:
13344 QM_TRY(MOZ_TO_RESULT(Start()), NS_OK
, handleError
);
13347 case State::CreateIndexedDatabaseManager
:
13348 QM_TRY(MOZ_TO_RESULT(CreateIndexedDatabaseManager()), NS_OK
, handleError
);
13351 case State::IndexedDatabaseManagerOpen
:
13352 QM_TRY(MOZ_TO_RESULT(OpenDirectory()), NS_OK
, handleError
);
13355 case State::DirectoryWorkOpen
:
13356 QM_TRY(MOZ_TO_RESULT(DirectoryWork()), NS_OK
, handleError
);
13359 case State::BeginDatabaseMaintenance
:
13360 QM_TRY(MOZ_TO_RESULT(BeginDatabaseMaintenance()), NS_OK
, handleError
);
13363 case State::Finishing
:
13368 MOZ_CRASH("Bad state!");
13374 void Maintenance::DirectoryLockAcquired(DirectoryLock
* aLock
) {
13375 AssertIsOnBackgroundThread();
13376 MOZ_ASSERT(mState
== State::DirectoryOpenPending
);
13377 MOZ_ASSERT(!mDirectoryLock
);
13379 mDirectoryLock
= std::exchange(mPendingDirectoryLock
, nullptr);
13381 nsresult rv
= DirectoryOpen();
13382 if (NS_WARN_IF(NS_FAILED(rv
))) {
13383 if (NS_SUCCEEDED(mResultCode
)) {
13387 mState
= State::Finishing
;
13394 void Maintenance::DirectoryLockFailed() {
13395 AssertIsOnBackgroundThread();
13396 MOZ_ASSERT(mState
== State::DirectoryOpenPending
);
13397 MOZ_ASSERT(!mDirectoryLock
);
13399 mPendingDirectoryLock
= nullptr;
13401 if (NS_SUCCEEDED(mResultCode
)) {
13402 mResultCode
= NS_ERROR_FAILURE
;
13405 mState
= State::Finishing
;
13409 void DatabaseMaintenance::Stringify(nsACString
& aResult
) const {
13410 AssertIsOnBackgroundThread();
13412 aResult
.AppendLiteral("Origin:");
13413 aResult
.Append(AnonymizedOriginString(mOriginMetadata
.mOrigin
));
13414 aResult
.Append(kQuotaGenericDelimiter
);
13416 aResult
.AppendLiteral("PersistenceType:");
13417 aResult
.Append(PersistenceTypeToString(mPersistenceType
));
13418 aResult
.Append(kQuotaGenericDelimiter
);
13420 aResult
.AppendLiteral("Duration:");
13421 aResult
.AppendInt((PR_Now() - mMaintenance
->StartTime()) / PR_USEC_PER_MSEC
);
13424 nsresult
DatabaseMaintenance::Abort() {
13425 AssertIsOnBackgroundThread();
13427 // StopIdleMaintenance and AbortAllOperations may request abort independently
13428 if (!mAborted
.compareExchange(false, true)) {
13433 auto shardStorageConnectionLocked
= mSharedStorageConnection
.Lock();
13434 if (nsCOMPtr
<mozIStorageConnection
> connection
=
13435 *shardStorageConnectionLocked
) {
13436 QM_TRY(MOZ_TO_RESULT(connection
->Interrupt()));
13440 // mDirectoryLock must not be released here - otherwise QuotaVFS of storage
13441 // emits a crash to disallow getting a quota object for an unregistered
13442 // directory lock when connection is closed.
13443 // mDirectoryLock will be dropped by RunOnOwningThread in a timely fashion
13444 // after the interrupted maintenance completes.
13449 void DatabaseMaintenance::PerformMaintenanceOnDatabase() {
13450 MOZ_ASSERT(!NS_IsMainThread());
13451 MOZ_ASSERT(!IsOnBackgroundThread());
13452 MOZ_ASSERT(mMaintenance
);
13453 MOZ_ASSERT(mMaintenance
->StartTime());
13454 MOZ_ASSERT(mDirectoryLock
);
13455 MOZ_ASSERT(!mDatabasePath
.IsEmpty());
13456 MOZ_ASSERT(!mOriginMetadata
.mGroup
.IsEmpty());
13457 MOZ_ASSERT(!mOriginMetadata
.mOrigin
.IsEmpty());
13459 if (NS_WARN_IF(IsAborted())) {
13463 const nsCOMPtr
<nsIFile
> databaseFile
= GetFileForPath(mDatabasePath
);
13464 MOZ_ASSERT(databaseFile
);
13467 const NotNull
<nsCOMPtr
<mozIStorageConnection
>> connection
,
13468 GetStorageConnection(*databaseFile
, mDirectoryLockId
,
13469 TelemetryIdForFile(databaseFile
), mMaybeKey
),
13472 auto autoClearConnection
= MakeScopeExit([&]() {
13473 auto sharedStorageConnectionLocked
= mSharedStorageConnection
.Lock();
13474 sharedStorageConnectionLocked
.ref() = nullptr;
13475 connection
->Close();
13479 auto sharedStorageConnectionLocked
= mSharedStorageConnection
.Lock();
13480 sharedStorageConnectionLocked
.ref() = connection
;
13483 auto databaseIsOk
= false;
13484 QM_TRY(MOZ_TO_RESULT(CheckIntegrity(*connection
, &databaseIsOk
)), QM_VOID
);
13486 QM_TRY(OkIf(databaseIsOk
), QM_VOID
, [](auto result
) {
13487 // XXX Handle this somehow! Probably need to clear all storage for the
13488 // origin. See Bug 1760612.
13489 MOZ_ASSERT(false, "Database corruption detected!");
13492 MaintenanceAction maintenanceAction
;
13493 QM_TRY(MOZ_TO_RESULT(DetermineMaintenanceAction(*connection
, databaseFile
,
13494 &maintenanceAction
)),
13497 switch (maintenanceAction
) {
13498 case MaintenanceAction::Nothing
:
13501 case MaintenanceAction::IncrementalVacuum
:
13502 IncrementalVacuum(*connection
);
13505 case MaintenanceAction::FullVacuum
:
13506 FullVacuum(*connection
, databaseFile
);
13510 MOZ_CRASH("Unknown MaintenanceAction!");
13514 nsresult
DatabaseMaintenance::CheckIntegrity(mozIStorageConnection
& aConnection
,
13516 MOZ_ASSERT(!NS_IsMainThread());
13517 MOZ_ASSERT(!IsOnBackgroundThread());
13520 if (NS_WARN_IF(IsAborted())) {
13521 return NS_ERROR_ABORT
;
13524 // First do a full integrity_check. Scope statements tightly here because
13525 // later operations require zero live statements.
13527 QM_TRY_INSPECT(const auto& stmt
,
13528 CreateAndExecuteSingleStepStatement(
13529 aConnection
, "PRAGMA integrity_check(1);"_ns
));
13531 QM_TRY_INSPECT(const auto& result
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
13532 nsString
, *stmt
, GetString
, 0));
13534 QM_TRY(OkIf(result
.EqualsLiteral("ok")), NS_OK
,
13535 [&aOk
](const auto) { *aOk
= false; });
13538 // Now enable and check for foreign key constraints.
13541 const int32_t& foreignKeysWereEnabled
,
13542 ([&aConnection
]() -> Result
<int32_t, nsresult
> {
13543 QM_TRY_INSPECT(const auto& stmt
,
13544 CreateAndExecuteSingleStepStatement(
13545 aConnection
, "PRAGMA foreign_keys;"_ns
));
13547 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt32
, 0));
13550 if (!foreignKeysWereEnabled
) {
13551 QM_TRY(MOZ_TO_RESULT(
13552 aConnection
.ExecuteSimpleSQL("PRAGMA foreign_keys = ON;"_ns
)));
13555 QM_TRY_INSPECT(const bool& foreignKeyError
,
13556 CreateAndExecuteSingleStepStatement
<
13557 SingleStepResult::ReturnNullIfNoResult
>(
13558 aConnection
, "PRAGMA foreign_key_check;"_ns
));
13560 if (!foreignKeysWereEnabled
) {
13561 QM_TRY(MOZ_TO_RESULT(
13562 aConnection
.ExecuteSimpleSQL("PRAGMA foreign_keys = OFF;"_ns
)));
13565 if (foreignKeyError
) {
13575 nsresult
DatabaseMaintenance::DetermineMaintenanceAction(
13576 mozIStorageConnection
& aConnection
, nsIFile
* aDatabaseFile
,
13577 MaintenanceAction
* aMaintenanceAction
) {
13578 MOZ_ASSERT(!NS_IsMainThread());
13579 MOZ_ASSERT(!IsOnBackgroundThread());
13580 MOZ_ASSERT(aDatabaseFile
);
13581 MOZ_ASSERT(aMaintenanceAction
);
13583 if (NS_WARN_IF(IsAborted())) {
13584 return NS_ERROR_ABORT
;
13587 QM_TRY_INSPECT(const int32_t& schemaVersion
,
13588 MOZ_TO_RESULT_INVOKE_MEMBER(aConnection
, GetSchemaVersion
));
13590 // Don't do anything if the schema version is less than 18; before that
13591 // version no databases had |auto_vacuum == INCREMENTAL| set and we didn't
13592 // track the values needed for the heuristics below.
13593 if (schemaVersion
< MakeSchemaVersion(18, 0)) {
13594 *aMaintenanceAction
= MaintenanceAction::Nothing
;
13598 // This method shouldn't make any permanent changes to the database, so make
13599 // sure everything gets rolled back when we leave.
13600 mozStorageTransaction
transaction(&aConnection
,
13601 /* aCommitOnComplete */ false);
13603 QM_TRY(MOZ_TO_RESULT(transaction
.Start()))
13605 // Check to see when we last vacuumed this database.
13606 QM_TRY_INSPECT(const auto& stmt
,
13607 CreateAndExecuteSingleStepStatement(
13609 "SELECT last_vacuum_time, last_vacuum_size "
13610 "FROM database;"_ns
));
13612 QM_TRY_INSPECT(const PRTime
& lastVacuumTime
,
13613 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt64
, 0));
13615 QM_TRY_INSPECT(const int64_t& lastVacuumSize
,
13616 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt64
, 1));
13618 NS_ASSERTION(lastVacuumSize
> 0,
13619 "Thy last vacuum size shall be greater than zero, less than "
13620 "zero shall thy last vacuum size not be. Zero is right out.");
13622 const PRTime startTime
= mMaintenance
->StartTime();
13624 // This shouldn't really be possible...
13625 if (NS_WARN_IF(startTime
<= lastVacuumTime
)) {
13626 *aMaintenanceAction
= MaintenanceAction::Nothing
;
13630 if (startTime
- lastVacuumTime
< kMinVacuumAge
) {
13631 *aMaintenanceAction
= MaintenanceAction::IncrementalVacuum
;
13635 // It has been more than a week since the database was vacuumed, so gather
13636 // statistics on its usage to see if vacuuming is worthwhile.
13638 // Create a temporary copy of the dbstat table to speed up the queries that
13640 QM_TRY(MOZ_TO_RESULT(aConnection
.ExecuteSimpleSQL(
13641 "CREATE VIRTUAL TABLE __stats__ USING dbstat;"
13642 "CREATE TEMP TABLE __temp_stats__ AS SELECT * FROM __stats__;"_ns
)));
13644 { // Calculate the percentage of the database pages that are not in
13645 // contiguous order.
13648 CreateAndExecuteSingleStepStatement(
13650 "SELECT SUM(__ts1__.pageno != __ts2__.pageno + 1) * 100.0 / "
13652 "FROM __temp_stats__ AS __ts1__, __temp_stats__ AS __ts2__ "
13653 "WHERE __ts1__.name = __ts2__.name "
13654 "AND __ts1__.rowid = __ts2__.rowid + 1;"_ns
));
13656 QM_TRY_INSPECT(const int32_t& percentUnordered
,
13657 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt32
, 0));
13659 MOZ_ASSERT(percentUnordered
>= 0);
13660 MOZ_ASSERT(percentUnordered
<= 100);
13662 if (percentUnordered
>= kPercentUnorderedThreshold
) {
13663 *aMaintenanceAction
= MaintenanceAction::FullVacuum
;
13668 // Don't try a full vacuum if the file hasn't grown by 10%.
13669 QM_TRY_INSPECT(const int64_t& currentFileSize
,
13670 MOZ_TO_RESULT_INVOKE_MEMBER(aDatabaseFile
, GetFileSize
));
13672 if (currentFileSize
<= lastVacuumSize
||
13673 (((currentFileSize
- lastVacuumSize
) * 100 / currentFileSize
) <
13674 kPercentFileSizeGrowthThreshold
)) {
13675 *aMaintenanceAction
= MaintenanceAction::IncrementalVacuum
;
13679 { // See if there are any free pages that we can reclaim.
13680 QM_TRY_INSPECT(const auto& stmt
,
13681 CreateAndExecuteSingleStepStatement(
13682 aConnection
, "PRAGMA freelist_count;"_ns
));
13684 QM_TRY_INSPECT(const int32_t& freelistCount
,
13685 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt32
, 0));
13687 MOZ_ASSERT(freelistCount
>= 0);
13689 // If we have too many free pages then we should try an incremental
13690 // vacuum. If that causes too much fragmentation then we'll try a full
13692 if (freelistCount
> kMaxFreelistThreshold
) {
13693 *aMaintenanceAction
= MaintenanceAction::IncrementalVacuum
;
13698 { // Calculate the percentage of unused bytes on pages in the database.
13701 CreateAndExecuteSingleStepStatement(
13703 "SELECT SUM(unused) * 100.0 / SUM(pgsize) FROM __temp_stats__;"_ns
));
13705 QM_TRY_INSPECT(const int32_t& percentUnused
,
13706 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt
, GetInt32
, 0));
13708 MOZ_ASSERT(percentUnused
>= 0);
13709 MOZ_ASSERT(percentUnused
<= 100);
13711 *aMaintenanceAction
= percentUnused
>= kPercentUnusedThreshold
13712 ? MaintenanceAction::FullVacuum
13713 : MaintenanceAction::IncrementalVacuum
;
13719 void DatabaseMaintenance::IncrementalVacuum(
13720 mozIStorageConnection
& aConnection
) {
13721 MOZ_ASSERT(!NS_IsMainThread());
13722 MOZ_ASSERT(!IsOnBackgroundThread());
13724 if (NS_WARN_IF(IsAborted())) {
13728 nsresult rv
= aConnection
.ExecuteSimpleSQL("PRAGMA incremental_vacuum;"_ns
);
13729 if (NS_WARN_IF(NS_FAILED(rv
))) {
13734 void DatabaseMaintenance::FullVacuum(mozIStorageConnection
& aConnection
,
13735 nsIFile
* aDatabaseFile
) {
13736 MOZ_ASSERT(!NS_IsMainThread());
13737 MOZ_ASSERT(!IsOnBackgroundThread());
13738 MOZ_ASSERT(aDatabaseFile
);
13740 if (NS_WARN_IF(IsAborted())) {
13744 QM_WARNONLY_TRY(([&]() -> Result
<Ok
, nsresult
> {
13745 QM_TRY(MOZ_TO_RESULT(aConnection
.ExecuteSimpleSQL("VACUUM;"_ns
)));
13747 const PRTime vacuumTime
= PR_Now();
13748 MOZ_ASSERT(vacuumTime
> 0);
13750 QM_TRY_INSPECT(const int64_t& fileSize
,
13751 MOZ_TO_RESULT_INVOKE_MEMBER(aDatabaseFile
, GetFileSize
));
13753 MOZ_ASSERT(fileSize
> 0);
13755 // The parameter names are not used, parameters are bound by index only
13756 // locally in the same function.
13757 QM_TRY_INSPECT(const auto& stmt
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
13758 nsCOMPtr
<mozIStorageStatement
>,
13759 aConnection
, CreateStatement
,
13761 "SET last_vacuum_time = :time"
13762 ", last_vacuum_size = :size;"_ns
));
13764 QM_TRY(MOZ_TO_RESULT(stmt
->BindInt64ByIndex(0, vacuumTime
)));
13766 QM_TRY(MOZ_TO_RESULT(stmt
->BindInt64ByIndex(1, fileSize
)));
13768 QM_TRY(MOZ_TO_RESULT(stmt
->Execute()));
13773 void DatabaseMaintenance::RunOnOwningThread() {
13774 AssertIsOnBackgroundThread();
13776 mDirectoryLock
= nullptr;
13778 if (mCompleteCallback
) {
13779 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mCompleteCallback
.forget()));
13782 mMaintenance
->UnregisterDatabaseMaintenance(this);
13785 void DatabaseMaintenance::RunOnConnectionThread() {
13786 MOZ_ASSERT(!NS_IsMainThread());
13787 MOZ_ASSERT(!IsOnBackgroundThread());
13789 PerformMaintenanceOnDatabase();
13791 MOZ_ALWAYS_SUCCEEDS(
13792 mMaintenance
->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL
));
13796 DatabaseMaintenance::Run() {
13797 if (IsOnBackgroundThread()) {
13798 RunOnOwningThread();
13800 RunOnConnectionThread();
13806 /*******************************************************************************
13807 * Local class implementations
13808 ******************************************************************************/
13811 nsAutoCString
DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
13812 const Maybe
<SerializedKeyRange
>& aOptionalKeyRange
,
13813 const nsACString
& aKeyColumnName
) {
13814 return aOptionalKeyRange
.isSome()
13815 ? GetBindingClauseForKeyRange(aOptionalKeyRange
.ref(),
13821 nsAutoCString
DatabaseOperationBase::GetBindingClauseForKeyRange(
13822 const SerializedKeyRange
& aKeyRange
, const nsACString
& aKeyColumnName
) {
13823 MOZ_ASSERT(!IsOnBackgroundThread());
13824 MOZ_ASSERT(!aKeyColumnName
.IsEmpty());
13826 constexpr auto andStr
= " AND "_ns
;
13827 constexpr auto spacecolon
= " :"_ns
;
13829 nsAutoCString result
;
13830 if (aKeyRange
.isOnly()) {
13831 // Both keys equal.
13833 andStr
+ aKeyColumnName
+ " ="_ns
+ spacecolon
+ kStmtParamNameLowerKey
;
13835 if (!aKeyRange
.lower().IsUnset()) {
13836 // Lower key is set.
13837 result
.Append(andStr
+ aKeyColumnName
);
13838 result
.AppendLiteral(" >");
13839 if (!aKeyRange
.lowerOpen()) {
13840 result
.AppendLiteral("=");
13842 result
.Append(spacecolon
+ kStmtParamNameLowerKey
);
13845 if (!aKeyRange
.upper().IsUnset()) {
13846 // Upper key is set.
13847 result
.Append(andStr
+ aKeyColumnName
);
13848 result
.AppendLiteral(" <");
13849 if (!aKeyRange
.upperOpen()) {
13850 result
.AppendLiteral("=");
13852 result
.Append(spacecolon
+ kStmtParamNameUpperKey
);
13856 MOZ_ASSERT(!result
.IsEmpty());
13862 uint64_t DatabaseOperationBase::ReinterpretDoubleAsUInt64(double aDouble
) {
13863 // This is a duplicate of the js engine's byte munging in StructuredClone.cpp
13864 return BitwiseCast
<uint64_t>(aDouble
);
13868 template <typename KeyTransformation
>
13869 nsresult
DatabaseOperationBase::MaybeBindKeyToStatement(
13870 const Key
& aKey
, mozIStorageStatement
* const aStatement
,
13871 const nsACString
& aParameterName
,
13872 const KeyTransformation
& aKeyTransformation
) {
13873 MOZ_ASSERT(!IsOnBackgroundThread());
13874 MOZ_ASSERT(aStatement
);
13876 if (!aKey
.IsUnset()) {
13877 // XXX This case distinction could be avoided if QM_TRY_INSPECT would also
13878 // work with a function not returning a Result<V, E> but simply a V (which
13879 // is const Key& here) and then assuming it is always a success. Or the
13880 // transformation could be changed to return Result<const V&, void> but I
13881 // don't think that Result supports that at the moment.
13882 if constexpr (std::is_reference_v
<
13883 std::invoke_result_t
<KeyTransformation
, Key
>>) {
13884 QM_TRY(MOZ_TO_RESULT(aKeyTransformation(aKey
).BindToStatement(
13885 aStatement
, aParameterName
)));
13887 QM_TRY_INSPECT(const auto& transformedKey
, aKeyTransformation(aKey
));
13888 QM_TRY(MOZ_TO_RESULT(
13889 transformedKey
.BindToStatement(aStatement
, aParameterName
)));
13897 template <typename KeyTransformation
>
13898 nsresult
DatabaseOperationBase::BindTransformedKeyRangeToStatement(
13899 const SerializedKeyRange
& aKeyRange
, mozIStorageStatement
* const aStatement
,
13900 const KeyTransformation
& aKeyTransformation
) {
13901 MOZ_ASSERT(!IsOnBackgroundThread());
13902 MOZ_ASSERT(aStatement
);
13904 QM_TRY(MOZ_TO_RESULT(MaybeBindKeyToStatement(aKeyRange
.lower(), aStatement
,
13905 kStmtParamNameLowerKey
,
13906 aKeyTransformation
)));
13908 if (aKeyRange
.isOnly()) {
13912 QM_TRY(MOZ_TO_RESULT(MaybeBindKeyToStatement(aKeyRange
.upper(), aStatement
,
13913 kStmtParamNameUpperKey
,
13914 aKeyTransformation
)));
13920 nsresult
DatabaseOperationBase::BindKeyRangeToStatement(
13921 const SerializedKeyRange
& aKeyRange
,
13922 mozIStorageStatement
* const aStatement
) {
13923 return BindTransformedKeyRangeToStatement(
13924 aKeyRange
, aStatement
, [](const Key
& key
) -> const auto& { return key
; });
13928 nsresult
DatabaseOperationBase::BindKeyRangeToStatement(
13929 const SerializedKeyRange
& aKeyRange
, mozIStorageStatement
* const aStatement
,
13930 const nsCString
& aLocale
) {
13931 MOZ_ASSERT(!aLocale
.IsEmpty());
13933 return BindTransformedKeyRangeToStatement(
13934 aKeyRange
, aStatement
,
13935 [&aLocale
](const Key
& key
) { return key
.ToLocaleAwareKey(aLocale
); });
13939 void CommonOpenOpHelperBase::AppendConditionClause(
13940 const nsACString
& aColumnName
, const nsACString
& aStatementParameterName
,
13941 bool aLessThan
, bool aEquals
, nsCString
& aResult
) {
13942 aResult
+= " AND "_ns
+ aColumnName
+ " "_ns
;
13945 aResult
.Append('<');
13947 aResult
.Append('>');
13951 aResult
.Append('=');
13954 aResult
+= " :"_ns
+ aStatementParameterName
;
13958 Result
<IndexDataValuesAutoArray
, nsresult
>
13959 DatabaseOperationBase::IndexDataValuesFromUpdateInfos(
13960 const nsTArray
<IndexUpdateInfo
>& aUpdateInfos
,
13961 const UniqueIndexTable
& aUniqueIndexTable
) {
13962 MOZ_ASSERT_IF(!aUpdateInfos
.IsEmpty(), aUniqueIndexTable
.Count());
13964 AUTO_PROFILER_LABEL("DatabaseOperationBase::IndexDataValuesFromUpdateInfos",
13967 // XXX We could use TransformIntoNewArray here if it allowed to specify that
13968 // an AutoArray should be created.
13969 IndexDataValuesAutoArray indexValues
;
13971 if (NS_WARN_IF(!indexValues
.SetCapacity(aUpdateInfos
.Length(), fallible
))) {
13972 IDB_REPORT_INTERNAL_ERR();
13973 return Err(NS_ERROR_OUT_OF_MEMORY
);
13976 std::transform(aUpdateInfos
.cbegin(), aUpdateInfos
.cend(),
13977 MakeBackInserter(indexValues
),
13978 [&aUniqueIndexTable
](const IndexUpdateInfo
& updateInfo
) {
13979 const IndexOrObjectStoreId
& indexId
= updateInfo
.indexId();
13981 bool unique
= false;
13982 MOZ_ALWAYS_TRUE(aUniqueIndexTable
.Get(indexId
, &unique
));
13984 return IndexDataValue
{indexId
, unique
, updateInfo
.value(),
13985 updateInfo
.localizedValue()};
13987 indexValues
.Sort();
13989 return indexValues
;
13993 nsresult
DatabaseOperationBase::InsertIndexTableRows(
13994 DatabaseConnection
* aConnection
, const IndexOrObjectStoreId aObjectStoreId
,
13995 const Key
& aObjectStoreKey
, const nsTArray
<IndexDataValue
>& aIndexValues
) {
13996 MOZ_ASSERT(aConnection
);
13997 aConnection
->AssertIsOnConnectionThread();
13998 MOZ_ASSERT(!aObjectStoreKey
.IsUnset());
14000 AUTO_PROFILER_LABEL("DatabaseOperationBase::InsertIndexTableRows", DOM
);
14002 const uint32_t count
= aIndexValues
.Length();
14007 auto insertUniqueStmt
= DatabaseConnection::LazyStatement
{
14009 "INSERT INTO unique_index_data "
14010 "(index_id, value, object_store_id, "
14011 "object_data_key, value_locale) "
14013 kStmtParamNameIndexId
+ ", :"_ns
+ kStmtParamNameValue
+ ", :"_ns
+
14014 kStmtParamNameObjectStoreId
+ ", :"_ns
+ kStmtParamNameObjectDataKey
+
14015 ", :"_ns
+ kStmtParamNameValueLocale
+ ");"_ns
};
14016 auto insertStmt
= DatabaseConnection::LazyStatement
{
14018 "INSERT OR IGNORE INTO index_data "
14019 "(index_id, value, object_data_key, "
14020 "object_store_id, value_locale) "
14022 kStmtParamNameIndexId
+ ", :"_ns
+ kStmtParamNameValue
+ ", :"_ns
+
14023 kStmtParamNameObjectDataKey
+ ", :"_ns
+ kStmtParamNameObjectStoreId
+
14024 ", :"_ns
+ kStmtParamNameValueLocale
+ ");"_ns
};
14026 for (uint32_t index
= 0; index
< count
; index
++) {
14027 const IndexDataValue
& info
= aIndexValues
[index
];
14029 auto& stmt
= info
.mUnique
? insertUniqueStmt
: insertStmt
;
14031 QM_TRY_INSPECT(const auto& borrowedStmt
, stmt
.Borrow());
14033 QM_TRY(MOZ_TO_RESULT(
14034 borrowedStmt
->BindInt64ByName(kStmtParamNameIndexId
, info
.mIndexId
)));
14035 QM_TRY(MOZ_TO_RESULT(
14036 info
.mPosition
.BindToStatement(&*borrowedStmt
, kStmtParamNameValue
)));
14037 QM_TRY(MOZ_TO_RESULT(info
.mLocaleAwarePosition
.BindToStatement(
14038 &*borrowedStmt
, kStmtParamNameValueLocale
)));
14039 QM_TRY(MOZ_TO_RESULT(borrowedStmt
->BindInt64ByName(
14040 kStmtParamNameObjectStoreId
, aObjectStoreId
)));
14041 QM_TRY(MOZ_TO_RESULT(aObjectStoreKey
.BindToStatement(
14042 &*borrowedStmt
, kStmtParamNameObjectDataKey
)));
14044 // QM_OR_ELSE_WARN_IF is not used here since we just want to log the
14045 // collision and not spam the reports.
14046 QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF(
14048 MOZ_TO_RESULT(borrowedStmt
->Execute()),
14050 ([&info
, index
, &aIndexValues
](nsresult rv
) {
14051 if (rv
== NS_ERROR_STORAGE_CONSTRAINT
&& info
.mUnique
) {
14052 // If we're inserting multiple entries for the same unique
14053 // index, then we might have failed to insert due to
14054 // colliding with another entry for the same index in which
14055 // case we should ignore it.
14056 for (int32_t index2
= int32_t(index
) - 1;
14057 index2
>= 0 && aIndexValues
[index2
].mIndexId
== info
.mIndexId
;
14059 if (info
.mPosition
== aIndexValues
[index2
].mPosition
) {
14060 // We found a key with the same value for the same
14061 // index. So we must have had a collision with a value
14062 // we just inserted.
14071 ErrToDefaultOk
<>));
14078 nsresult
DatabaseOperationBase::DeleteIndexDataTableRows(
14079 DatabaseConnection
* aConnection
, const Key
& aObjectStoreKey
,
14080 const nsTArray
<IndexDataValue
>& aIndexValues
) {
14081 MOZ_ASSERT(aConnection
);
14082 aConnection
->AssertIsOnConnectionThread();
14083 MOZ_ASSERT(!aObjectStoreKey
.IsUnset());
14085 AUTO_PROFILER_LABEL("DatabaseOperationBase::DeleteIndexDataTableRows", DOM
);
14087 const uint32_t count
= aIndexValues
.Length();
14092 auto deleteUniqueStmt
= DatabaseConnection::LazyStatement
{
14093 *aConnection
, "DELETE FROM unique_index_data WHERE index_id = :"_ns
+
14094 kStmtParamNameIndexId
+ " AND value = :"_ns
+
14095 kStmtParamNameValue
+ ";"_ns
};
14096 auto deleteStmt
= DatabaseConnection::LazyStatement
{
14097 *aConnection
, "DELETE FROM index_data WHERE index_id = :"_ns
+
14098 kStmtParamNameIndexId
+ " AND value = :"_ns
+
14099 kStmtParamNameValue
+ " AND object_data_key = :"_ns
+
14100 kStmtParamNameObjectDataKey
+ ";"_ns
};
14102 for (uint32_t index
= 0; index
< count
; index
++) {
14103 const IndexDataValue
& indexValue
= aIndexValues
[index
];
14105 auto& stmt
= indexValue
.mUnique
? deleteUniqueStmt
: deleteStmt
;
14107 QM_TRY_INSPECT(const auto& borrowedStmt
, stmt
.Borrow());
14109 QM_TRY(MOZ_TO_RESULT(borrowedStmt
->BindInt64ByName(kStmtParamNameIndexId
,
14110 indexValue
.mIndexId
)));
14112 QM_TRY(MOZ_TO_RESULT(indexValue
.mPosition
.BindToStatement(
14113 &*borrowedStmt
, kStmtParamNameValue
)));
14115 if (!indexValue
.mUnique
) {
14116 QM_TRY(MOZ_TO_RESULT(aObjectStoreKey
.BindToStatement(
14117 &*borrowedStmt
, kStmtParamNameObjectDataKey
)));
14120 QM_TRY(MOZ_TO_RESULT(borrowedStmt
->Execute()));
14127 nsresult
DatabaseOperationBase::DeleteObjectStoreDataTableRowsWithIndexes(
14128 DatabaseConnection
* aConnection
, const IndexOrObjectStoreId aObjectStoreId
,
14129 const Maybe
<SerializedKeyRange
>& aKeyRange
) {
14130 MOZ_ASSERT(aConnection
);
14131 aConnection
->AssertIsOnConnectionThread();
14132 MOZ_ASSERT(aObjectStoreId
);
14136 QM_TRY_INSPECT(const bool& hasIndexes
,
14137 ObjectStoreHasIndexes(*aConnection
, aObjectStoreId
),
14138 QM_PROPAGATE
, [](const auto&) { MOZ_ASSERT(false); });
14139 MOZ_ASSERT(hasIndexes
,
14140 "Don't use this slow method if there are no indexes!");
14144 AUTO_PROFILER_LABEL(
14145 "DatabaseOperationBase::DeleteObjectStoreDataTableRowsWithIndexes", DOM
);
14147 const bool singleRowOnly
= aKeyRange
.isSome() && aKeyRange
.ref().isOnly();
14149 const auto keyRangeClause
=
14150 MaybeGetBindingClauseForKeyRange(aKeyRange
, kColumnNameKey
);
14152 Key objectStoreKey
;
14154 const auto& selectStmt
,
14155 ([singleRowOnly
, &aConnection
, &objectStoreKey
, &aKeyRange
,
14157 -> Result
<CachingDatabaseConnection::BorrowedStatement
, nsresult
> {
14158 if (singleRowOnly
) {
14159 QM_TRY_UNWRAP(auto selectStmt
,
14160 aConnection
->BorrowCachedStatement(
14161 "SELECT index_data_values "
14162 "FROM object_data "
14163 "WHERE object_store_id = :"_ns
+
14164 kStmtParamNameObjectStoreId
+ " AND key = :"_ns
+
14165 kStmtParamNameKey
+ ";"_ns
));
14167 objectStoreKey
= aKeyRange
.ref().lower();
14169 QM_TRY(MOZ_TO_RESULT(
14170 objectStoreKey
.BindToStatement(&*selectStmt
, kStmtParamNameKey
)));
14177 aConnection
->BorrowCachedStatement(
14178 "SELECT index_data_values, "_ns
+ kColumnNameKey
+
14179 " FROM object_data WHERE object_store_id = :"_ns
+
14180 kStmtParamNameObjectStoreId
+ keyRangeClause
+ ";"_ns
));
14182 if (aKeyRange
.isSome()) {
14183 QM_TRY(MOZ_TO_RESULT(
14184 BindKeyRangeToStatement(aKeyRange
.ref(), &*selectStmt
)));
14190 QM_TRY(MOZ_TO_RESULT(selectStmt
->BindInt64ByName(kStmtParamNameObjectStoreId
,
14193 DebugOnly
<uint32_t> resultCountDEBUG
= 0;
14195 QM_TRY(CollectWhileHasResult(
14197 [singleRowOnly
, &objectStoreKey
, &aConnection
, &resultCountDEBUG
,
14198 indexValues
= IndexDataValuesAutoArray
{}](
14199 auto& selectStmt
) mutable -> Result
<Ok
, nsresult
> {
14200 if (!singleRowOnly
) {
14202 MOZ_TO_RESULT(objectStoreKey
.SetFromStatement(&selectStmt
, 1)));
14204 indexValues
.ClearAndRetainStorage();
14207 QM_TRY(MOZ_TO_RESULT(
14208 ReadCompressedIndexDataValues(selectStmt
, 0, indexValues
)));
14209 QM_TRY(MOZ_TO_RESULT(DeleteIndexDataTableRows(
14210 aConnection
, objectStoreKey
, indexValues
)));
14212 resultCountDEBUG
++;
14217 MOZ_ASSERT_IF(singleRowOnly
, resultCountDEBUG
<= 1);
14220 auto deleteManyStmt
,
14221 aConnection
->BorrowCachedStatement(
14222 "DELETE FROM object_data "_ns
+ "WHERE object_store_id = :"_ns
+
14223 kStmtParamNameObjectStoreId
+ keyRangeClause
+ ";"_ns
));
14225 QM_TRY(MOZ_TO_RESULT(deleteManyStmt
->BindInt64ByName(
14226 kStmtParamNameObjectStoreId
, aObjectStoreId
)));
14228 if (aKeyRange
.isSome()) {
14229 QM_TRY(MOZ_TO_RESULT(
14230 BindKeyRangeToStatement(aKeyRange
.ref(), &*deleteManyStmt
)));
14233 QM_TRY(MOZ_TO_RESULT(deleteManyStmt
->Execute()));
14239 nsresult
DatabaseOperationBase::UpdateIndexValues(
14240 DatabaseConnection
* aConnection
, const IndexOrObjectStoreId aObjectStoreId
,
14241 const Key
& aObjectStoreKey
, const nsTArray
<IndexDataValue
>& aIndexValues
) {
14242 MOZ_ASSERT(aConnection
);
14243 aConnection
->AssertIsOnConnectionThread();
14244 MOZ_ASSERT(!aObjectStoreKey
.IsUnset());
14246 AUTO_PROFILER_LABEL("DatabaseOperationBase::UpdateIndexValues", DOM
);
14248 QM_TRY_UNWRAP((auto [indexDataValues
, indexDataValuesLength
]),
14249 MakeCompressedIndexDataValues(aIndexValues
));
14251 MOZ_ASSERT(!indexDataValuesLength
== !(indexDataValues
.get()));
14253 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
14254 "UPDATE object_data SET index_data_values = :"_ns
+
14255 kStmtParamNameIndexDataValues
+ " WHERE object_store_id = :"_ns
+
14256 kStmtParamNameObjectStoreId
+ " AND key = :"_ns
+ kStmtParamNameKey
+
14258 [&indexDataValues
= indexDataValues
,
14259 indexDataValuesLength
= indexDataValuesLength
, aObjectStoreId
,
14261 mozIStorageStatement
& updateStmt
) -> Result
<Ok
, nsresult
> {
14262 QM_TRY(MOZ_TO_RESULT(
14264 ? updateStmt
.BindAdoptedBlobByName(
14265 kStmtParamNameIndexDataValues
, indexDataValues
.release(),
14266 indexDataValuesLength
)
14267 : updateStmt
.BindNullByName(kStmtParamNameIndexDataValues
)));
14269 QM_TRY(MOZ_TO_RESULT(updateStmt
.BindInt64ByName(
14270 kStmtParamNameObjectStoreId
, aObjectStoreId
)));
14272 QM_TRY(MOZ_TO_RESULT(
14273 aObjectStoreKey
.BindToStatement(&updateStmt
, kStmtParamNameKey
)));
14282 Result
<bool, nsresult
> DatabaseOperationBase::ObjectStoreHasIndexes(
14283 DatabaseConnection
& aConnection
,
14284 const IndexOrObjectStoreId aObjectStoreId
) {
14285 aConnection
.AssertIsOnConnectionThread();
14286 MOZ_ASSERT(aObjectStoreId
);
14288 QM_TRY_RETURN(aConnection
14289 .BorrowAndExecuteSingleStepStatement(
14291 "FROM object_store_index "
14292 "WHERE object_store_id = :"_ns
+
14293 kStmtParamNameObjectStoreId
+ kOpenLimit
+ "1;"_ns
,
14294 [aObjectStoreId
](auto& stmt
) -> Result
<Ok
, nsresult
> {
14295 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByName(
14296 kStmtParamNameObjectStoreId
, aObjectStoreId
)));
14302 NS_IMPL_ISUPPORTS_INHERITED(DatabaseOperationBase
, Runnable
,
14303 mozIStorageProgressHandler
)
14306 DatabaseOperationBase::OnProgress(mozIStorageConnection
* aConnection
,
14308 MOZ_ASSERT(!IsOnBackgroundThread());
14309 MOZ_ASSERT(_retval
);
14311 // This is intentionally racy.
14312 *_retval
= QuotaClient::IsShuttingDownOnNonBackgroundThread() ||
14313 !OperationMayProceed();
14317 DatabaseOperationBase::AutoSetProgressHandler::AutoSetProgressHandler()
14318 : mConnection(Nothing())
14321 mDEBUGDatabaseOp(nullptr)
14324 MOZ_ASSERT(!IsOnBackgroundThread());
14327 DatabaseOperationBase::AutoSetProgressHandler::~AutoSetProgressHandler() {
14328 MOZ_ASSERT(!IsOnBackgroundThread());
14335 nsresult
DatabaseOperationBase::AutoSetProgressHandler::Register(
14336 mozIStorageConnection
& aConnection
, DatabaseOperationBase
* aDatabaseOp
) {
14337 MOZ_ASSERT(!IsOnBackgroundThread());
14338 MOZ_ASSERT(aDatabaseOp
);
14339 MOZ_ASSERT(!mConnection
);
14342 const DebugOnly oldProgressHandler
,
14343 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
14344 nsCOMPtr
<mozIStorageProgressHandler
>, aConnection
, SetProgressHandler
,
14345 kStorageProgressGranularity
, aDatabaseOp
));
14347 MOZ_ASSERT(!oldProgressHandler
.inspect());
14349 mConnection
= SomeRef(aConnection
);
14351 mDEBUGDatabaseOp
= aDatabaseOp
;
14357 void DatabaseOperationBase::AutoSetProgressHandler::Unregister() {
14358 MOZ_ASSERT(!IsOnBackgroundThread());
14359 MOZ_ASSERT(mConnection
);
14361 nsCOMPtr
<mozIStorageProgressHandler
> oldHandler
;
14362 MOZ_ALWAYS_SUCCEEDS(
14363 mConnection
->RemoveProgressHandler(getter_AddRefs(oldHandler
)));
14364 MOZ_ASSERT(oldHandler
== mDEBUGDatabaseOp
);
14366 mConnection
= Nothing();
14369 FactoryOp::FactoryOp(SafeRefPtr
<Factory
> aFactory
,
14370 const Maybe
<ContentParentId
>& aContentParentId
,
14371 const PersistenceType aPersistenceType
,
14372 const PrincipalInfo
& aPrincipalInfo
,
14373 const Maybe
<nsString
>& aDatabaseName
, bool aDeleting
)
14374 : DatabaseOperationBase(aFactory
->GetLoggingInfo()->Id(),
14375 aFactory
->GetLoggingInfo()->NextRequestSN()),
14376 mFactory(std::move(aFactory
)),
14377 mContentParentId(aContentParentId
),
14378 mPrincipalInfo(aPrincipalInfo
),
14379 mDatabaseName(aDatabaseName
),
14380 mDirectoryLockId(-1),
14381 mPersistenceType(aPersistenceType
),
14382 mState(State::Initial
),
14383 mWaitingForPermissionRetry(false),
14384 mEnforcingQuota(true),
14385 mDeleting(aDeleting
) {
14386 AssertIsOnBackgroundThread();
14387 MOZ_ASSERT(mFactory
);
14388 MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
14391 void FactoryOp::NoteDatabaseBlocked(Database
* aDatabase
) {
14392 AssertIsOnOwningThread();
14393 MOZ_ASSERT(aDatabase
);
14394 MOZ_ASSERT(mState
== State::WaitingForOtherDatabasesToClose
);
14395 MOZ_ASSERT(!mMaybeBlockedDatabases
.IsEmpty());
14396 MOZ_ASSERT(mMaybeBlockedDatabases
.Contains(aDatabase
));
14398 // Only send the blocked event if all databases have reported back. If the
14399 // database was closed then it will have been removed from the array.
14400 // Otherwise if it was blocked its |mBlocked| flag will be true.
14401 bool sendBlockedEvent
= true;
14403 for (auto& info
: mMaybeBlockedDatabases
) {
14404 if (info
== aDatabase
) {
14405 // This database was blocked, mark accordingly.
14406 info
.mBlocked
= true;
14407 } else if (!info
.mBlocked
) {
14408 // A database has not yet reported back yet, don't send the event yet.
14409 sendBlockedEvent
= false;
14413 if (sendBlockedEvent
) {
14414 SendBlockedNotification();
14418 void FactoryOp::NoteDatabaseClosed(Database
* const aDatabase
) {
14419 AssertIsOnOwningThread();
14420 MOZ_ASSERT(aDatabase
);
14421 MOZ_ASSERT(mState
== State::WaitingForOtherDatabasesToClose
);
14422 MOZ_ASSERT(!mMaybeBlockedDatabases
.IsEmpty());
14423 MOZ_ASSERT(mMaybeBlockedDatabases
.Contains(aDatabase
));
14425 mMaybeBlockedDatabases
.RemoveElement(aDatabase
);
14427 if (!mMaybeBlockedDatabases
.IsEmpty()) {
14431 DatabaseActorInfo
* info
;
14432 MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable
->Get(mDatabaseId
.ref(), &info
));
14433 MOZ_ASSERT(info
->mWaitingFactoryOp
== this);
14435 if (AreActorsAlive()) {
14436 // The IPDL strong reference has not yet been released, so we can clear
14437 // mWaitingFactoryOp immediately.
14438 info
->mWaitingFactoryOp
= nullptr;
14440 WaitForTransactions();
14444 // The IPDL strong reference has been released, mWaitingFactoryOp holds the
14445 // last strong reference to us, so we need to move it to a stack variable
14446 // instead of clearing it immediately (We could clear it immediately if only
14447 // the other actor is destroyed, but we don't need to optimize for that, and
14448 // move it anyway).
14449 const RefPtr
<FactoryOp
> waitingFactoryOp
= std::move(info
->mWaitingFactoryOp
);
14451 IDB_REPORT_INTERNAL_ERR();
14452 SetFailureCodeIfUnset(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
14454 // We hold a strong ref in waitingFactoryOp, so it's safe to call Run()
14457 mState
= State::SendingResults
;
14458 MOZ_ALWAYS_SUCCEEDS(Run());
14461 void FactoryOp::StringifyState(nsACString
& aResult
) const {
14462 AssertIsOnOwningThread();
14465 case State::Initial
:
14466 aResult
.AppendLiteral("Initial");
14469 case State::DirectoryOpenPending
:
14470 aResult
.AppendLiteral("DirectoryOpenPending");
14473 case State::DirectoryWorkOpen
:
14474 aResult
.AppendLiteral("DirectoryWorkOpen");
14477 case State::DirectoryWorkDone
:
14478 aResult
.AppendLiteral("DirectoryWorkDone");
14481 case State::DatabaseOpenPending
:
14482 aResult
.AppendLiteral("DatabaseOpenPending");
14485 case State::DatabaseWorkOpen
:
14486 aResult
.AppendLiteral("DatabaseWorkOpen");
14489 case State::BeginVersionChange
:
14490 aResult
.AppendLiteral("BeginVersionChange");
14493 case State::WaitingForOtherDatabasesToClose
:
14494 aResult
.AppendLiteral("WaitingForOtherDatabasesToClose");
14497 case State::WaitingForTransactionsToComplete
:
14498 aResult
.AppendLiteral("WaitingForTransactionsToComplete");
14501 case State::DatabaseWorkVersionChange
:
14502 aResult
.AppendLiteral("DatabaseWorkVersionChange");
14505 case State::SendingResults
:
14506 aResult
.AppendLiteral("SendingResults");
14509 case State::Completed
:
14510 aResult
.AppendLiteral("Completed");
14514 MOZ_CRASH("Bad state!");
14518 void FactoryOp::Stringify(nsACString
& aResult
) const {
14519 AssertIsOnOwningThread();
14521 aResult
.AppendLiteral("PersistenceType:");
14522 aResult
.Append(PersistenceTypeToString(mPersistenceType
));
14523 aResult
.Append(kQuotaGenericDelimiter
);
14525 aResult
.AppendLiteral("Origin:");
14526 aResult
.Append(AnonymizedOriginString(mOriginMetadata
.mOrigin
));
14527 aResult
.Append(kQuotaGenericDelimiter
);
14529 aResult
.AppendLiteral("State:");
14530 StringifyState(aResult
);
14533 nsresult
FactoryOp::Open() {
14534 AssertIsOnOwningThread();
14535 MOZ_ASSERT(mState
== State::Initial
);
14536 MOZ_ASSERT(mOriginMetadata
.mOrigin
.IsEmpty());
14537 MOZ_ASSERT(!mDirectoryLock
);
14539 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
14540 IsActorDestroyed()) {
14541 IDB_REPORT_INTERNAL_ERR();
14542 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
14545 QM_TRY(QuotaManager::EnsureCreated());
14547 QuotaManager
* const quotaManager
= QuotaManager::Get();
14548 MOZ_ASSERT(quotaManager
);
14551 auto principalMetadata
,
14552 quotaManager
->GetInfoFromValidatedPrincipalInfo(mPrincipalInfo
));
14554 mOriginMetadata
= {std::move(principalMetadata
), mPersistenceType
};
14556 if (mPrincipalInfo
.type() == PrincipalInfo::TSystemPrincipalInfo
) {
14557 MOZ_ASSERT(mPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
);
14559 mEnforcingQuota
= false;
14560 } else if (mPrincipalInfo
.type() == PrincipalInfo::TContentPrincipalInfo
) {
14561 const ContentPrincipalInfo
& contentPrincipalInfo
=
14562 mPrincipalInfo
.get_ContentPrincipalInfo();
14565 QuotaManager::IsOriginInternal(contentPrincipalInfo
.originNoSuffix()),
14566 mPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
);
14568 mEnforcingQuota
= mPersistenceType
!= PERSISTENCE_TYPE_PERSISTENT
;
14570 if (mOriginMetadata
.mIsPrivate
) {
14571 if (StaticPrefs::dom_indexedDB_privateBrowsing_enabled()) {
14572 // Explicitly disallow moz-extension urls from using the encrypted
14573 // indexedDB storage mode when the caller is an extension (see Bug
14575 if (StringBeginsWith(contentPrincipalInfo
.originNoSuffix(),
14576 "moz-extension:"_ns
)) {
14577 return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
;
14580 mInPrivateBrowsing
.Flip();
14582 return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
;
14589 if (mDatabaseName
.isSome()) {
14590 nsCString databaseId
;
14592 QuotaManager::GetStorageId(mPersistenceType
, mOriginMetadata
.mOrigin
,
14593 Client::IDB
, databaseId
);
14595 databaseId
.Append('*');
14596 databaseId
.Append(NS_ConvertUTF16toUTF8(mDatabaseName
.ref()));
14598 mDatabaseId
= Some(std::move(databaseId
));
14600 // Need to get database file path before opening the directory.
14601 // XXX: For what reason?
14603 auto databaseFilePath
,
14604 ([this, quotaManager
]() -> mozilla::Result
<nsString
, nsresult
> {
14605 QM_TRY_INSPECT(const auto& dbFile
,
14606 quotaManager
->GetOriginDirectory(mOriginMetadata
));
14608 QM_TRY(MOZ_TO_RESULT(dbFile
->Append(
14609 NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME
))));
14611 QM_TRY(MOZ_TO_RESULT(dbFile
->Append(
14612 GetDatabaseFilenameBase(mDatabaseName
.ref(),
14613 mOriginMetadata
.mIsPrivate
) +
14617 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString
, dbFile
, GetPath
));
14620 mDatabaseFilePath
= Some(std::move(databaseFilePath
));
14624 mState
= State::DirectoryOpenPending
;
14626 quotaManager
->OpenClientDirectory({mOriginMetadata
, Client::IDB
})
14628 GetCurrentSerialEventTarget(), __func__
,
14629 [self
= RefPtr(this)](
14630 const ClientDirectoryLockPromise::ResolveOrRejectValue
& aValue
) {
14631 if (aValue
.IsResolve()) {
14632 self
->DirectoryLockAcquired(aValue
.ResolveValue());
14634 self
->DirectoryLockFailed();
14641 nsresult
FactoryOp::DirectoryOpen() {
14642 AssertIsOnOwningThread();
14643 MOZ_ASSERT(mState
== State::DirectoryOpenPending
);
14644 MOZ_ASSERT(mDirectoryLock
);
14646 if (mDatabaseName
.isNothing()) {
14647 QuotaManager
* const quotaManager
= QuotaManager::Get();
14648 MOZ_ASSERT(quotaManager
);
14650 // Must set this before dispatching otherwise we will race with the IO
14652 mState
= State::DirectoryWorkOpen
;
14654 QM_TRY(MOZ_TO_RESULT(
14655 quotaManager
->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL
)),
14656 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
14661 mState
= State::DirectoryWorkDone
;
14662 MOZ_ALWAYS_SUCCEEDS(Run());
14667 nsresult
FactoryOp::DirectoryWorkDone() {
14668 AssertIsOnOwningThread();
14669 MOZ_ASSERT(mState
== State::DirectoryWorkDone
);
14670 MOZ_ASSERT(mDirectoryLock
);
14671 MOZ_ASSERT(gFactoryOps
);
14673 // See if this FactoryOp needs to wait.
14674 const bool blocked
= [&self
= *this] {
14675 bool foundThis
= false;
14676 bool blocked
= false;
14678 for (const auto& existingOp
: Reversed(*gFactoryOps
)) {
14679 if (existingOp
== &self
) {
14684 if (foundThis
&& self
.MustWaitFor(*existingOp
)) {
14685 existingOp
->AddBlockingOp(self
);
14686 self
.AddBlockedOnOp(*existingOp
);
14692 }() || [&self
= *this] {
14693 QuotaClient
* quotaClient
= QuotaClient::GetInstance();
14694 MOZ_ASSERT(quotaClient
);
14696 if (RefPtr
<Maintenance
> currentMaintenance
=
14697 quotaClient
->GetCurrentMaintenance()) {
14698 if (self
.mDatabaseName
.isSome()) {
14699 if (RefPtr
<DatabaseMaintenance
> databaseMaintenance
=
14700 currentMaintenance
->GetDatabaseMaintenance(
14701 self
.mDatabaseFilePath
.ref())) {
14702 databaseMaintenance
->WaitForCompletion(&self
);
14705 } else if (currentMaintenance
->HasDatabaseMaintenances()) {
14706 currentMaintenance
->WaitForCompletion(&self
);
14714 mState
= State::DatabaseOpenPending
;
14716 QM_TRY(MOZ_TO_RESULT(DatabaseOpen()));
14722 nsresult
FactoryOp::SendToIOThread() {
14723 AssertIsOnOwningThread();
14724 MOZ_ASSERT(mState
== State::DatabaseOpenPending
);
14726 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
14727 !OperationMayProceed()) {
14728 IDB_REPORT_INTERNAL_ERR();
14729 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
14732 QuotaManager
* const quotaManager
= QuotaManager::Get();
14733 MOZ_ASSERT(quotaManager
);
14735 // Must set this before dispatching otherwise we will race with the IO thread.
14736 mState
= State::DatabaseWorkOpen
;
14738 QM_TRY(MOZ_TO_RESULT(
14739 quotaManager
->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL
)),
14740 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
14745 void FactoryOp::WaitForTransactions() {
14746 AssertIsOnOwningThread();
14747 MOZ_ASSERT(mState
== State::BeginVersionChange
||
14748 mState
== State::WaitingForOtherDatabasesToClose
);
14749 MOZ_ASSERT(!mDatabaseId
.ref().IsEmpty());
14750 MOZ_ASSERT(!IsActorDestroyed());
14752 mState
= State::WaitingForTransactionsToComplete
;
14754 RefPtr
<WaitForTransactionsHelper
> helper
=
14755 new WaitForTransactionsHelper(mDatabaseId
.ref(), this);
14756 helper
->WaitForTransactions();
14759 void FactoryOp::CleanupMetadata() {
14760 AssertIsOnOwningThread();
14762 for (const NotNull
<RefPtr
<FactoryOp
>>& blockingOp
: mBlocking
) {
14763 blockingOp
->MaybeUnblock(*this);
14767 MOZ_ASSERT(gFactoryOps
);
14768 gFactoryOps
->RemoveElement(this);
14770 // We might get here even after QuotaManagerOpen failed, so we need to check
14771 // if we have a quota manager.
14772 quota::QuotaManager::SafeMaybeRecordQuotaClientShutdownStep(
14773 quota::Client::IDB
, "An element was removed from gFactoryOps"_ns
);
14775 // Match the IncreaseBusyCount in AllocPBackgroundIDBFactoryRequestParent().
14776 DecreaseBusyCount();
14779 void FactoryOp::FinishSendResults() {
14780 AssertIsOnOwningThread();
14781 MOZ_ASSERT(mState
== State::SendingResults
);
14782 MOZ_ASSERT(mFactory
);
14784 mState
= State::Completed
;
14786 // Make sure to release the factory on this thread.
14787 mFactory
= nullptr;
14790 nsresult
FactoryOp::SendVersionChangeMessages(
14791 DatabaseActorInfo
* aDatabaseActorInfo
, Maybe
<Database
&> aOpeningDatabase
,
14792 uint64_t aOldVersion
, const Maybe
<uint64_t>& aNewVersion
) {
14793 AssertIsOnOwningThread();
14794 MOZ_ASSERT(aDatabaseActorInfo
);
14795 MOZ_ASSERT(mState
== State::BeginVersionChange
);
14796 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
14797 MOZ_ASSERT(!IsActorDestroyed());
14799 const uint32_t expectedCount
= mDeleting
? 0 : 1;
14800 const uint32_t liveCount
= aDatabaseActorInfo
->mLiveDatabases
.Length();
14801 if (liveCount
> expectedCount
) {
14802 nsTArray
<MaybeBlockedDatabaseInfo
> maybeBlockedDatabases
;
14803 for (const auto& database
: aDatabaseActorInfo
->mLiveDatabases
) {
14804 if ((!aOpeningDatabase
|| database
.get() != &aOpeningDatabase
.ref()) &&
14805 !database
->IsClosed() &&
14806 NS_WARN_IF(!maybeBlockedDatabases
.AppendElement(
14807 SafeRefPtr
{database
.get(), AcquireStrongRefFromRawPtr
{}},
14809 return NS_ERROR_OUT_OF_MEMORY
;
14813 mMaybeBlockedDatabases
= std::move(maybeBlockedDatabases
);
14816 // We don't want to wait forever if we were not able to send the
14818 mMaybeBlockedDatabases
.RemoveLastElements(
14819 mMaybeBlockedDatabases
.end() -
14820 std::remove_if(mMaybeBlockedDatabases
.begin(),
14821 mMaybeBlockedDatabases
.end(),
14822 [aOldVersion
, &aNewVersion
](auto& maybeBlockedDatabase
) {
14823 return !maybeBlockedDatabase
->SendVersionChange(
14824 aOldVersion
, aNewVersion
);
14828 } // namespace indexedDB
14830 bool FactoryOp::MustWaitFor(const FactoryOp
& aExistingOp
) {
14831 AssertIsOnOwningThread();
14833 // If the persistence types don't overlap, the op can proceed.
14834 if (aExistingOp
.mPersistenceType
!= mPersistenceType
) {
14838 // If the origins don't overlap, the op can proceed.
14839 if (aExistingOp
.mOriginMetadata
.mOrigin
!= mOriginMetadata
.mOrigin
) {
14843 // If the database ids don't overlap, the op can proceed.
14844 if (!aExistingOp
.mDatabaseId
.isNothing() && !mDatabaseId
.isNothing() &&
14845 aExistingOp
.mDatabaseId
.ref() != mDatabaseId
.ref()) {
14852 // Run() assumes that the caller holds a strong reference to the object that
14853 // can't be cleared while Run() is being executed.
14854 // So if you call Run() directly (as opposed to dispatching to an event queue)
14855 // you need to make sure there's such a reference.
14856 // See bug 1356824 for more details.
14859 const auto handleError
= [this](const nsresult rv
) {
14860 if (mState
!= State::SendingResults
) {
14861 SetFailureCodeIfUnset(rv
);
14863 // Must set mState before dispatching otherwise we will race with the
14865 mState
= State::SendingResults
;
14867 if (IsOnOwningThread()) {
14870 MOZ_ALWAYS_SUCCEEDS(
14871 mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
));
14877 case State::Initial
:
14878 QM_WARNONLY_TRY(MOZ_TO_RESULT(Open()), handleError
);
14881 case State::DirectoryWorkOpen
:
14882 QM_WARNONLY_TRY(MOZ_TO_RESULT(DoDirectoryWork()), handleError
);
14885 case State::DirectoryWorkDone
:
14886 QM_WARNONLY_TRY(MOZ_TO_RESULT(DirectoryWorkDone()), handleError
);
14889 case State::DatabaseOpenPending
:
14890 QM_WARNONLY_TRY(MOZ_TO_RESULT(DatabaseOpen()), handleError
);
14893 case State::DatabaseWorkOpen
:
14894 QM_WARNONLY_TRY(MOZ_TO_RESULT(DoDatabaseWork()), handleError
);
14897 case State::BeginVersionChange
:
14898 QM_WARNONLY_TRY(MOZ_TO_RESULT(BeginVersionChange()), handleError
);
14901 case State::WaitingForTransactionsToComplete
:
14902 QM_WARNONLY_TRY(MOZ_TO_RESULT(DispatchToWorkThread()), handleError
);
14905 case State::SendingResults
:
14910 MOZ_CRASH("Bad state!");
14916 void FactoryOp::DirectoryLockAcquired(DirectoryLock
* aLock
) {
14917 AssertIsOnOwningThread();
14919 MOZ_ASSERT(mState
== State::DirectoryOpenPending
);
14920 MOZ_ASSERT(!mDirectoryLock
);
14922 mDirectoryLock
= aLock
;
14924 MOZ_ASSERT(mDirectoryLock
->Id() >= 0);
14925 mDirectoryLockId
= mDirectoryLock
->Id();
14927 QM_WARNONLY_TRY(MOZ_TO_RESULT(DirectoryOpen()), [this](const nsresult rv
) {
14928 SetFailureCodeIfUnset(rv
);
14930 // The caller holds a strong reference to us, no need for a self reference
14931 // before calling Run().
14933 mState
= State::SendingResults
;
14934 MOZ_ALWAYS_SUCCEEDS(Run());
14938 void FactoryOp::DirectoryLockFailed() {
14939 AssertIsOnOwningThread();
14940 MOZ_ASSERT(mState
== State::DirectoryOpenPending
);
14941 MOZ_ASSERT(!mDirectoryLock
);
14943 if (!HasFailed()) {
14944 IDB_REPORT_INTERNAL_ERR();
14945 SetFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
14948 // The caller holds a strong reference to us, no need for a self reference
14949 // before calling Run().
14951 mState
= State::SendingResults
;
14952 MOZ_ALWAYS_SUCCEEDS(Run());
14955 nsresult
FactoryRequestOp::DoDirectoryWork() {
14956 MOZ_CRASH("Not implemented because this should be unreachable.");
14959 void FactoryRequestOp::ActorDestroy(ActorDestroyReason aWhy
) {
14960 AssertIsOnBackgroundThread();
14962 NoteActorDestroyed();
14965 OpenDatabaseOp::OpenDatabaseOp(SafeRefPtr
<Factory
> aFactory
,
14966 const Maybe
<ContentParentId
>& aContentParentId
,
14967 const CommonFactoryRequestParams
& aParams
)
14968 : FactoryRequestOp(std::move(aFactory
), aContentParentId
, aParams
,
14969 /* aDeleting */ false),
14970 mMetadata(MakeSafeRefPtr
<FullDatabaseMetadata
>(aParams
.metadata())),
14971 mRequestedVersion(aParams
.metadata().version()),
14972 mVersionChangeOp(nullptr),
14975 void OpenDatabaseOp::ActorDestroy(ActorDestroyReason aWhy
) {
14976 AssertIsOnOwningThread();
14978 FactoryRequestOp::ActorDestroy(aWhy
);
14980 if (mVersionChangeOp
) {
14981 mVersionChangeOp
->NoteActorDestroyed();
14985 nsresult
OpenDatabaseOp::DatabaseOpen() {
14986 AssertIsOnOwningThread();
14987 MOZ_ASSERT(mState
== State::DatabaseOpenPending
);
14989 nsresult rv
= SendToIOThread();
14990 if (NS_WARN_IF(NS_FAILED(rv
))) {
14997 nsresult
OpenDatabaseOp::DoDatabaseWork() {
14998 AssertIsOnIOThread();
14999 MOZ_ASSERT(mState
== State::DatabaseWorkOpen
);
15001 AUTO_PROFILER_LABEL("OpenDatabaseOp::DoDatabaseWork", DOM
);
15003 QM_TRY(OkIf(!QuotaClient::IsShuttingDownOnNonBackgroundThread()),
15004 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
15006 if (!OperationMayProceed()) {
15007 IDB_REPORT_INTERNAL_ERR();
15008 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
15011 const nsAString
& databaseName
= mCommonParams
.metadata().name();
15012 const PersistenceType persistenceType
=
15013 mCommonParams
.metadata().persistenceType();
15015 QuotaManager
* const quotaManager
= QuotaManager::Get();
15016 MOZ_ASSERT(quotaManager
);
15019 const auto& dbDirectory
,
15020 ([persistenceType
, "aManager
, this]()
15021 -> mozilla::Result
<std::pair
<nsCOMPtr
<nsIFile
>, bool>, nsresult
> {
15022 if (persistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
15023 QM_TRY_RETURN(quotaManager
->EnsurePersistentOriginIsInitialized(
15027 QM_TRY(MOZ_TO_RESULT(
15028 quotaManager
->EnsureTemporaryStorageIsInitializedInternal()));
15029 QM_TRY_RETURN(quotaManager
->EnsureTemporaryOriginIsInitialized(
15030 persistenceType
, mOriginMetadata
));
15032 .map([](const auto& res
) { return res
.first
; })));
15034 QM_TRY(MOZ_TO_RESULT(
15035 dbDirectory
->Append(NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME
))));
15038 QM_TRY_INSPECT(const bool& exists
,
15039 MOZ_TO_RESULT_INVOKE_MEMBER(dbDirectory
, Exists
));
15042 QM_TRY(MOZ_TO_RESULT(dbDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0755)));
15047 MOZ_ASSERT(NS_SUCCEEDED(dbDirectory
->IsDirectory(&isDirectory
)));
15048 MOZ_ASSERT(isDirectory
);
15053 const auto databaseFilenameBase
=
15054 GetDatabaseFilenameBase(databaseName
, mOriginMetadata
.mIsPrivate
);
15056 QM_TRY_INSPECT(const auto& markerFile
,
15057 CloneFileAndAppend(*dbDirectory
, kIdbDeletionMarkerFilePrefix
+
15058 databaseFilenameBase
));
15060 QM_TRY_INSPECT(const bool& exists
,
15061 MOZ_TO_RESULT_INVOKE_MEMBER(markerFile
, Exists
));
15064 // Delete the database and directroy since they should be deleted in
15065 // previous operation.
15066 // Note: only update usage to the QuotaManager when mEnforcingQuota == true
15067 QM_TRY(MOZ_TO_RESULT(RemoveDatabaseFilesAndDirectory(
15068 *dbDirectory
, databaseFilenameBase
,
15069 mEnforcingQuota
? quotaManager
: nullptr, persistenceType
,
15070 mOriginMetadata
, databaseName
)));
15074 const auto& dbFile
,
15075 CloneFileAndAppend(*dbDirectory
, databaseFilenameBase
+ kSQLiteSuffix
));
15077 mTelemetryId
= TelemetryIdForFile(dbFile
);
15082 const auto& databaseFilePath
,
15083 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString
, dbFile
, GetPath
));
15085 MOZ_ASSERT(databaseFilePath
== mDatabaseFilePath
.ref());
15090 const auto& fmDirectory
,
15091 CloneFileAndAppend(*dbDirectory
, databaseFilenameBase
+
15092 kFileManagerDirectoryNameSuffix
));
15094 IndexedDatabaseManager
* const idm
= IndexedDatabaseManager::Get();
15097 SafeRefPtr
<DatabaseFileManager
> fileManager
= idm
->GetFileManager(
15098 persistenceType
, mOriginMetadata
.mOrigin
, databaseName
);
15100 if (!fileManager
) {
15101 fileManager
= MakeSafeRefPtr
<DatabaseFileManager
>(
15102 persistenceType
, mOriginMetadata
, databaseName
, mDatabaseId
.ref(),
15103 mDatabaseFilePath
.ref(), mEnforcingQuota
, mInPrivateBrowsing
);
15106 Maybe
<const CipherKey
> maybeKey
=
15108 ? Some(fileManager
->MutableCipherKeyManagerRef().Ensure())
15111 MOZ_RELEASE_ASSERT(mInPrivateBrowsing
== maybeKey
.isSome());
15114 NotNull
<nsCOMPtr
<mozIStorageConnection
>> connection
,
15115 CreateStorageConnection(*dbFile
, *fmDirectory
, databaseName
,
15116 mOriginMetadata
.mOrigin
, mDirectoryLockId
,
15117 mTelemetryId
, maybeKey
));
15119 AutoSetProgressHandler asph
;
15120 QM_TRY(MOZ_TO_RESULT(asph
.Register(*connection
, this)));
15122 QM_TRY(MOZ_TO_RESULT(LoadDatabaseInformation(*connection
)));
15124 MOZ_ASSERT(mMetadata
->mNextObjectStoreId
> mMetadata
->mObjectStores
.Count());
15125 MOZ_ASSERT(mMetadata
->mNextIndexId
> 0);
15127 // See if we need to do a versionchange transaction
15129 // Optional version semantics.
15130 if (!mRequestedVersion
) {
15131 // If the requested version was not specified and the database was created,
15132 // treat it as if version 1 were requested.
15133 // Otherwise, treat it as if the current version were requested.
15134 mRequestedVersion
= mMetadata
->mCommonMetadata
.version() == 0
15136 : mMetadata
->mCommonMetadata
.version();
15139 QM_TRY(OkIf(mMetadata
->mCommonMetadata
.version() <= mRequestedVersion
),
15140 NS_ERROR_DOM_INDEXEDDB_VERSION_ERR
);
15142 if (!fileManager
->Initialized()) {
15143 QM_TRY(MOZ_TO_RESULT(fileManager
->Init(fmDirectory
, *connection
)));
15145 idm
->AddFileManager(fileManager
.clonePtr());
15148 mFileManager
= std::move(fileManager
);
15150 // Must close connection before dispatching otherwise we might race with the
15151 // connection thread which needs to open the same database.
15154 MOZ_ALWAYS_SUCCEEDS(connection
->Close());
15156 // Must set mState before dispatching otherwise we will race with the owning
15158 mState
= (mMetadata
->mCommonMetadata
.version() == mRequestedVersion
)
15159 ? State::SendingResults
15160 : State::BeginVersionChange
;
15162 QM_TRY(MOZ_TO_RESULT(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
)));
15167 nsresult
OpenDatabaseOp::LoadDatabaseInformation(
15168 mozIStorageConnection
& aConnection
) {
15169 AssertIsOnIOThread();
15170 MOZ_ASSERT(mMetadata
);
15173 // Load version information.
15176 CreateAndExecuteSingleStepStatement
<
15177 SingleStepResult::ReturnNullIfNoResult
>(
15178 aConnection
, "SELECT name, origin, version FROM database"_ns
));
15180 QM_TRY(OkIf(stmt
), NS_ERROR_FILE_CORRUPTED
);
15182 QM_TRY_INSPECT(const auto& databaseName
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15183 nsString
, stmt
, GetString
, 0));
15185 QM_TRY(OkIf(mCommonParams
.metadata().name() == databaseName
),
15186 NS_ERROR_FILE_CORRUPTED
);
15188 QM_TRY_INSPECT(const auto& origin
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15189 nsCString
, stmt
, GetUTF8String
, 1));
15191 // We can't just compare these strings directly. See bug 1339081 comment 69.
15192 QM_TRY(OkIf(QuotaManager::AreOriginsEqualOnDisk(mOriginMetadata
.mOrigin
,
15194 NS_ERROR_FILE_CORRUPTED
);
15196 QM_TRY_INSPECT(const int64_t& version
,
15197 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetInt64
, 2));
15199 mMetadata
->mCommonMetadata
.version() = uint64_t(version
);
15202 ObjectStoreTable
& objectStores
= mMetadata
->mObjectStores
;
15205 const auto& lastObjectStoreId
,
15207 &objectStores
]() -> mozilla::Result
<IndexOrObjectStoreId
, nsresult
> {
15208 // Load object store names and ids.
15211 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15212 nsCOMPtr
<mozIStorageStatement
>, aConnection
, CreateStatement
,
15213 "SELECT id, auto_increment, name, key_path "
15214 "FROM object_store"_ns
));
15216 IndexOrObjectStoreId lastObjectStoreId
= 0;
15218 QM_TRY(CollectWhileHasResult(
15220 [&lastObjectStoreId
, &objectStores
,
15221 usedIds
= Maybe
<nsTHashSet
<uint64_t>>{},
15222 usedNames
= Maybe
<nsTHashSet
<nsString
>>{}](
15223 auto& stmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
15224 QM_TRY_INSPECT(const IndexOrObjectStoreId
& objectStoreId
,
15225 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetInt64
, 0));
15231 QM_TRY(OkIf(objectStoreId
> 0), Err(NS_ERROR_FILE_CORRUPTED
));
15232 QM_TRY(OkIf(!usedIds
.ref().Contains(objectStoreId
)),
15233 Err(NS_ERROR_FILE_CORRUPTED
));
15235 QM_TRY(OkIf(usedIds
.ref().Insert(objectStoreId
, fallible
)),
15236 Err(NS_ERROR_OUT_OF_MEMORY
));
15239 QM_TRY(MOZ_TO_RESULT(stmt
.GetString(2, name
)));
15242 usedNames
.emplace();
15245 QM_TRY(OkIf(!usedNames
.ref().Contains(name
)),
15246 Err(NS_ERROR_FILE_CORRUPTED
));
15248 QM_TRY(OkIf(usedNames
.ref().Insert(name
, fallible
)),
15249 Err(NS_ERROR_OUT_OF_MEMORY
));
15251 ObjectStoreMetadata commonMetadata
;
15252 commonMetadata
.id() = objectStoreId
;
15253 commonMetadata
.name() = std::move(name
);
15256 const int32_t& columnType
,
15257 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetTypeOfIndex
, 3));
15259 if (columnType
== mozIStorageStatement::VALUE_TYPE_NULL
) {
15260 commonMetadata
.keyPath() = KeyPath(0);
15262 MOZ_ASSERT(columnType
== mozIStorageStatement::VALUE_TYPE_TEXT
);
15264 nsString keyPathSerialization
;
15265 QM_TRY(MOZ_TO_RESULT(stmt
.GetString(3, keyPathSerialization
)));
15267 commonMetadata
.keyPath() =
15268 KeyPath::DeserializeFromString(keyPathSerialization
);
15269 QM_TRY(OkIf(commonMetadata
.keyPath().IsValid()),
15270 Err(NS_ERROR_FILE_CORRUPTED
));
15273 QM_TRY_INSPECT(const int64_t& nextAutoIncrementId
,
15274 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetInt64
, 1));
15276 commonMetadata
.autoIncrement() = !!nextAutoIncrementId
;
15278 QM_TRY(OkIf(objectStores
.InsertOrUpdate(
15280 MakeSafeRefPtr
<FullObjectStoreMetadata
>(
15281 std::move(commonMetadata
),
15282 FullObjectStoreMetadata::AutoIncrementIds
{
15283 nextAutoIncrementId
, nextAutoIncrementId
}),
15285 Err(NS_ERROR_OUT_OF_MEMORY
));
15287 lastObjectStoreId
= std::max(lastObjectStoreId
, objectStoreId
);
15292 return lastObjectStoreId
;
15296 const auto& lastIndexId
,
15297 ([this, &aConnection
,
15298 &objectStores
]() -> mozilla::Result
<IndexOrObjectStoreId
, nsresult
> {
15299 // Load index information
15302 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15303 nsCOMPtr
<mozIStorageStatement
>, aConnection
, CreateStatement
,
15305 "id, object_store_id, name, key_path, "
15306 "unique_index, multientry, "
15307 "locale, is_auto_locale "
15308 "FROM object_store_index"_ns
));
15310 IndexOrObjectStoreId lastIndexId
= 0;
15312 QM_TRY(CollectWhileHasResult(
15314 [this, &lastIndexId
, &objectStores
, &aConnection
,
15315 usedIds
= Maybe
<nsTHashSet
<uint64_t>>{},
15316 usedNames
= Maybe
<nsTHashSet
<nsString
>>{}](
15317 auto& stmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
15318 QM_TRY_INSPECT(const IndexOrObjectStoreId
& objectStoreId
,
15319 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetInt64
, 1));
15321 // XXX Why does this return NS_ERROR_OUT_OF_MEMORY if we don't
15322 // know the object store id?
15324 auto objectStoreMetadata
= objectStores
.Lookup(objectStoreId
);
15325 QM_TRY(OkIf(static_cast<bool>(objectStoreMetadata
)),
15326 Err(NS_ERROR_OUT_OF_MEMORY
));
15328 MOZ_ASSERT((*objectStoreMetadata
)->mCommonMetadata
.id() ==
15331 IndexOrObjectStoreId indexId
;
15332 QM_TRY(MOZ_TO_RESULT(stmt
.GetInt64(0, &indexId
)));
15338 QM_TRY(OkIf(indexId
> 0), Err(NS_ERROR_FILE_CORRUPTED
));
15339 QM_TRY(OkIf(!usedIds
.ref().Contains(indexId
)),
15340 Err(NS_ERROR_FILE_CORRUPTED
));
15342 QM_TRY(OkIf(usedIds
.ref().Insert(indexId
, fallible
)),
15343 Err(NS_ERROR_OUT_OF_MEMORY
));
15346 QM_TRY(MOZ_TO_RESULT(stmt
.GetString(2, name
)));
15348 const nsAutoString hashName
=
15349 IntToString(indexId
) + u
":"_ns
+ name
;
15352 usedNames
.emplace();
15355 QM_TRY(OkIf(!usedNames
.ref().Contains(hashName
)),
15356 Err(NS_ERROR_FILE_CORRUPTED
));
15358 QM_TRY(OkIf(usedNames
.ref().Insert(hashName
, fallible
)),
15359 Err(NS_ERROR_OUT_OF_MEMORY
));
15361 auto indexMetadata
= MakeSafeRefPtr
<FullIndexMetadata
>();
15362 indexMetadata
->mCommonMetadata
.id() = indexId
;
15363 indexMetadata
->mCommonMetadata
.name() = name
;
15367 int32_t columnType
;
15368 nsresult rv
= stmt
.GetTypeOfIndex(3, &columnType
);
15369 MOZ_ASSERT(NS_SUCCEEDED(rv
));
15370 MOZ_ASSERT(columnType
!= mozIStorageStatement::VALUE_TYPE_NULL
);
15374 nsString keyPathSerialization
;
15375 QM_TRY(MOZ_TO_RESULT(stmt
.GetString(3, keyPathSerialization
)));
15377 indexMetadata
->mCommonMetadata
.keyPath() =
15378 KeyPath::DeserializeFromString(keyPathSerialization
);
15379 QM_TRY(OkIf(indexMetadata
->mCommonMetadata
.keyPath().IsValid()),
15380 Err(NS_ERROR_FILE_CORRUPTED
));
15383 QM_TRY(MOZ_TO_RESULT(stmt
.GetInt32(4, &scratch
)));
15385 indexMetadata
->mCommonMetadata
.unique() = !!scratch
;
15387 QM_TRY(MOZ_TO_RESULT(stmt
.GetInt32(5, &scratch
)));
15389 indexMetadata
->mCommonMetadata
.multiEntry() = !!scratch
;
15391 const bool localeAware
= !stmt
.IsNull(6);
15393 QM_TRY(MOZ_TO_RESULT(stmt
.GetUTF8String(
15394 6, indexMetadata
->mCommonMetadata
.locale())));
15396 QM_TRY(MOZ_TO_RESULT(stmt
.GetInt32(7, &scratch
)));
15398 indexMetadata
->mCommonMetadata
.autoLocale() = !!scratch
;
15400 // Update locale-aware indexes if necessary
15401 const nsCString
& indexedLocale
=
15402 indexMetadata
->mCommonMetadata
.locale();
15403 const bool& isAutoLocale
=
15404 indexMetadata
->mCommonMetadata
.autoLocale();
15405 const nsCString
& systemLocale
= mFactory
->GetSystemLocale();
15406 if (!systemLocale
.IsEmpty() && isAutoLocale
&&
15407 !indexedLocale
.Equals(systemLocale
)) {
15408 QM_TRY(MOZ_TO_RESULT(UpdateLocaleAwareIndex(
15409 aConnection
, indexMetadata
->mCommonMetadata
,
15414 QM_TRY(OkIf((*objectStoreMetadata
)
15415 ->mIndexes
.InsertOrUpdate(
15416 indexId
, std::move(indexMetadata
), fallible
)),
15417 Err(NS_ERROR_OUT_OF_MEMORY
));
15419 lastIndexId
= std::max(lastIndexId
, indexId
);
15424 return lastIndexId
;
15427 QM_TRY(OkIf(lastObjectStoreId
!= INT64_MAX
),
15428 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
15429 QM_TRY(OkIf(lastIndexId
!= INT64_MAX
), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
,
15430 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
15432 mMetadata
->mNextObjectStoreId
= lastObjectStoreId
+ 1;
15433 mMetadata
->mNextIndexId
= lastIndexId
+ 1;
15439 nsresult
OpenDatabaseOp::UpdateLocaleAwareIndex(
15440 mozIStorageConnection
& aConnection
, const IndexMetadata
& aIndexMetadata
,
15441 const nsCString
& aLocale
) {
15442 const auto indexTable
=
15443 aIndexMetadata
.unique() ? "unique_index_data"_ns
: "index_data"_ns
;
15445 // The parameter names are not used, parameters are bound by index only
15446 // locally in the same function.
15447 const nsCString readQuery
= "SELECT value, object_data_key FROM "_ns
+
15448 indexTable
+ " WHERE index_id = :index_id"_ns
;
15450 QM_TRY_INSPECT(const auto& readStmt
,
15451 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15452 nsCOMPtr
<mozIStorageStatement
>, aConnection
,
15453 CreateStatement
, readQuery
));
15455 QM_TRY(MOZ_TO_RESULT(readStmt
->BindInt64ByIndex(0, aIndexMetadata
.id())));
15457 QM_TRY(CollectWhileHasResult(
15459 [&aConnection
, &indexTable
, &aIndexMetadata
, &aLocale
,
15460 writeStmt
= nsCOMPtr
<mozIStorageStatement
>{}](
15461 auto& readStmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
15465 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15466 nsCOMPtr
<mozIStorageStatement
>, aConnection
, CreateStatement
,
15467 "UPDATE "_ns
+ indexTable
+ "SET value_locale = :"_ns
+
15468 kStmtParamNameValueLocale
+ " WHERE index_id = :"_ns
+
15469 kStmtParamNameIndexId
+ " AND value = :"_ns
+
15470 kStmtParamNameValue
+ " AND object_data_key = :"_ns
+
15471 kStmtParamNameObjectDataKey
));
15474 mozStorageStatementScoper
scoper(writeStmt
);
15475 QM_TRY(MOZ_TO_RESULT(writeStmt
->BindInt64ByName(kStmtParamNameIndexId
,
15476 aIndexMetadata
.id())));
15478 Key oldKey
, objectStorePosition
;
15479 QM_TRY(MOZ_TO_RESULT(oldKey
.SetFromStatement(&readStmt
, 0)));
15480 QM_TRY(MOZ_TO_RESULT(
15481 oldKey
.BindToStatement(writeStmt
, kStmtParamNameValue
)));
15483 QM_TRY_INSPECT(const auto& newSortKey
,
15484 oldKey
.ToLocaleAwareKey(aLocale
));
15486 QM_TRY(MOZ_TO_RESULT(
15487 newSortKey
.BindToStatement(writeStmt
, kStmtParamNameValueLocale
)));
15489 MOZ_TO_RESULT(objectStorePosition
.SetFromStatement(&readStmt
, 1)));
15490 QM_TRY(MOZ_TO_RESULT(objectStorePosition
.BindToStatement(
15491 writeStmt
, kStmtParamNameObjectDataKey
)));
15493 QM_TRY(MOZ_TO_RESULT(writeStmt
->Execute()));
15498 // The parameter names are not used, parameters are bound by index only
15499 // locally in the same function.
15500 static constexpr auto metaQuery
=
15501 "UPDATE object_store_index SET "
15502 "locale = :locale WHERE id = :id"_ns
;
15504 QM_TRY_INSPECT(const auto& metaStmt
,
15505 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
15506 nsCOMPtr
<mozIStorageStatement
>, aConnection
,
15507 CreateStatement
, metaQuery
));
15509 QM_TRY(MOZ_TO_RESULT(
15510 metaStmt
->BindStringByIndex(0, NS_ConvertASCIItoUTF16(aLocale
))));
15512 QM_TRY(MOZ_TO_RESULT(metaStmt
->BindInt64ByIndex(1, aIndexMetadata
.id())));
15514 QM_TRY(MOZ_TO_RESULT(metaStmt
->Execute()));
15519 nsresult
OpenDatabaseOp::BeginVersionChange() {
15520 AssertIsOnOwningThread();
15521 MOZ_ASSERT(mState
== State::BeginVersionChange
);
15522 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
15523 MOZ_ASSERT(mMetadata
->mCommonMetadata
.version() <= mRequestedVersion
);
15524 MOZ_ASSERT(!mDatabase
);
15525 MOZ_ASSERT(!mVersionChangeTransaction
);
15527 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
15528 IsActorDestroyed()) {
15529 IDB_REPORT_INTERNAL_ERR();
15530 QM_TRY(MOZ_TO_RESULT(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
));
15533 EnsureDatabaseActor();
15535 if (mDatabase
->IsInvalidated()) {
15536 IDB_REPORT_INTERNAL_ERR();
15537 QM_TRY(MOZ_TO_RESULT(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
));
15540 MOZ_ASSERT(!mDatabase
->IsClosed());
15542 DatabaseActorInfo
* info
;
15543 MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable
->Get(mDatabaseId
.ref(), &info
));
15545 MOZ_ASSERT(info
->mLiveDatabases
.Contains(mDatabase
.unsafeGetRawPtr()));
15546 MOZ_ASSERT(!info
->mWaitingFactoryOp
);
15547 MOZ_ASSERT(info
->mMetadata
== mMetadata
);
15549 auto transaction
= MakeSafeRefPtr
<VersionChangeTransaction
>(this);
15551 if (NS_WARN_IF(!transaction
->CopyDatabaseMetadata())) {
15552 return NS_ERROR_OUT_OF_MEMORY
;
15555 MOZ_ASSERT(info
->mMetadata
!= mMetadata
);
15556 mMetadata
= info
->mMetadata
.clonePtr();
15558 const Maybe
<uint64_t> newVersion
= Some(mRequestedVersion
);
15560 QM_TRY(MOZ_TO_RESULT(SendVersionChangeMessages(
15561 info
, mDatabase
.maybeDeref(), mMetadata
->mCommonMetadata
.version(),
15564 mVersionChangeTransaction
= std::move(transaction
);
15566 if (mMaybeBlockedDatabases
.IsEmpty()) {
15567 // We don't need to wait on any databases, just jump to the transaction
15569 WaitForTransactions();
15573 // If the actor gets destroyed, mWaitingFactoryOp will hold the last strong
15574 // reference to us.
15575 info
->mWaitingFactoryOp
= this;
15577 mState
= State::WaitingForOtherDatabasesToClose
;
15581 bool OpenDatabaseOp::AreActorsAlive() {
15582 AssertIsOnOwningThread();
15583 MOZ_ASSERT(mDatabase
);
15585 return !(IsActorDestroyed() || mDatabase
->IsActorDestroyed());
15588 void OpenDatabaseOp::SendBlockedNotification() {
15589 AssertIsOnOwningThread();
15590 MOZ_ASSERT(mState
== State::WaitingForOtherDatabasesToClose
);
15592 if (!IsActorDestroyed()) {
15593 Unused
<< SendBlocked(mMetadata
->mCommonMetadata
.version());
15597 nsresult
OpenDatabaseOp::DispatchToWorkThread() {
15598 AssertIsOnOwningThread();
15599 MOZ_ASSERT(mState
== State::WaitingForTransactionsToComplete
);
15600 MOZ_ASSERT(mVersionChangeTransaction
);
15601 MOZ_ASSERT(mVersionChangeTransaction
->GetMode() ==
15602 IDBTransaction::Mode::VersionChange
);
15603 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
15605 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
15606 IsActorDestroyed() || mDatabase
->IsInvalidated()) {
15607 IDB_REPORT_INTERNAL_ERR();
15608 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
15611 mState
= State::DatabaseWorkVersionChange
;
15613 // Intentionally empty.
15614 nsTArray
<nsString
> objectStoreNames
;
15616 const int64_t loggingSerialNumber
=
15617 mVersionChangeTransaction
->LoggingSerialNumber();
15618 const nsID
& backgroundChildLoggingId
=
15619 mVersionChangeTransaction
->GetLoggingInfo()->Id();
15621 if (NS_WARN_IF(!mDatabase
->RegisterTransaction(*mVersionChangeTransaction
))) {
15622 return NS_ERROR_OUT_OF_MEMORY
;
15625 if (!gConnectionPool
) {
15626 gConnectionPool
= new ConnectionPool();
15629 RefPtr
<VersionChangeOp
> versionChangeOp
= new VersionChangeOp(this);
15631 uint64_t transactionId
= versionChangeOp
->StartOnConnectionPool(
15632 backgroundChildLoggingId
, mVersionChangeTransaction
->DatabaseId(),
15633 loggingSerialNumber
, objectStoreNames
,
15634 /* aIsWriteTransaction */ true);
15636 mVersionChangeOp
= versionChangeOp
;
15638 mVersionChangeTransaction
->NoteActiveRequest();
15639 mVersionChangeTransaction
->Init(transactionId
);
15644 nsresult
OpenDatabaseOp::SendUpgradeNeeded() {
15645 AssertIsOnOwningThread();
15646 MOZ_ASSERT(mState
== State::DatabaseWorkVersionChange
);
15647 MOZ_ASSERT(mVersionChangeTransaction
);
15648 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
15649 MOZ_ASSERT(!HasFailed());
15650 MOZ_ASSERT_IF(!IsActorDestroyed(), mDatabase
);
15652 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
15653 IsActorDestroyed()) {
15654 IDB_REPORT_INTERNAL_ERR();
15655 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
15658 const SafeRefPtr
<VersionChangeTransaction
> transaction
=
15659 std::move(mVersionChangeTransaction
);
15661 nsresult rv
= EnsureDatabaseActorIsAlive();
15662 if (NS_WARN_IF(NS_FAILED(rv
))) {
15666 // Transfer ownership to IPDL.
15667 transaction
->SetActorAlive();
15669 if (!mDatabase
->SendPBackgroundIDBVersionChangeTransactionConstructor(
15670 transaction
.unsafeGetRawPtr(), mMetadata
->mCommonMetadata
.version(),
15671 mRequestedVersion
, mMetadata
->mNextObjectStoreId
,
15672 mMetadata
->mNextIndexId
)) {
15673 IDB_REPORT_INTERNAL_ERR();
15674 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
15680 void OpenDatabaseOp::SendResults() {
15681 AssertIsOnOwningThread();
15682 MOZ_ASSERT(mState
== State::SendingResults
);
15683 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
15684 MOZ_ASSERT_IF(!HasFailed(), !mVersionChangeTransaction
);
15686 DebugOnly
<DatabaseActorInfo
*> info
= nullptr;
15687 MOZ_ASSERT_IF(mDatabaseId
.isSome() && gLiveDatabaseHashtable
&&
15688 gLiveDatabaseHashtable
->Get(mDatabaseId
.ref(), &info
),
15689 !info
->mWaitingFactoryOp
);
15691 if (mVersionChangeTransaction
) {
15692 MOZ_ASSERT(HasFailed());
15694 mVersionChangeTransaction
->Abort(ResultCode(), /* aForce */ true);
15695 mVersionChangeTransaction
= nullptr;
15698 if (IsActorDestroyed()) {
15699 SetFailureCodeIfUnset(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
15701 FactoryRequestResponse response
;
15703 if (!HasFailed()) {
15704 // If we just successfully completed a versionchange operation then we
15705 // need to update the version in our metadata.
15706 mMetadata
->mCommonMetadata
.version() = mRequestedVersion
;
15708 mFileManager
->UpdateDatabaseVersion(mRequestedVersion
);
15710 nsresult rv
= EnsureDatabaseActorIsAlive();
15711 if (NS_SUCCEEDED(rv
)) {
15712 // We successfully opened a database so use its actor as the success
15713 // result for this request.
15715 // XXX OpenDatabaseRequestResponse stores a raw pointer, can this be
15717 response
= OpenDatabaseRequestResponse
{
15718 WrapNotNull(mDatabase
.unsafeGetRawPtr())};
15720 response
= ClampResultCode(rv
);
15722 SetFailureCode(response
.get_nsresult());
15727 // If something failed then our metadata pointer is now bad. No one should
15728 // ever touch it again though so just null it out in DEBUG builds to make
15729 // sure we find such cases.
15730 mMetadata
= nullptr;
15732 response
= ClampResultCode(ResultCode());
15735 Unused
<< PBackgroundIDBFactoryRequestParent::Send__delete__(this,
15740 MOZ_ASSERT(!mDirectoryLock
);
15743 mDatabase
->Invalidate();
15746 // Make sure to release the database on this thread.
15747 mDatabase
= nullptr;
15750 } else if (mDirectoryLock
) {
15751 // ConnectionClosedCallback will call CleanupMetadata().
15752 nsCOMPtr
<nsIRunnable
> callback
= NewRunnableMethod(
15753 "dom::indexedDB::OpenDatabaseOp::ConnectionClosedCallback", this,
15754 &OpenDatabaseOp::ConnectionClosedCallback
);
15756 RefPtr
<WaitForTransactionsHelper
> helper
=
15757 new WaitForTransactionsHelper(mDatabaseId
.ref(), callback
);
15758 helper
->WaitForTransactions();
15763 FinishSendResults();
15766 void OpenDatabaseOp::ConnectionClosedCallback() {
15767 AssertIsOnOwningThread();
15768 MOZ_ASSERT(HasFailed());
15769 MOZ_ASSERT(mDirectoryLock
);
15771 mDirectoryLock
= nullptr;
15776 void OpenDatabaseOp::EnsureDatabaseActor() {
15777 AssertIsOnOwningThread();
15778 MOZ_ASSERT(mState
== State::BeginVersionChange
||
15779 mState
== State::DatabaseWorkVersionChange
||
15780 mState
== State::SendingResults
);
15781 MOZ_ASSERT(!HasFailed());
15782 MOZ_ASSERT(mDatabaseFilePath
.isSome());
15783 MOZ_ASSERT(!IsActorDestroyed());
15789 MOZ_ASSERT(mMetadata
->mDatabaseId
.IsEmpty());
15790 mMetadata
->mDatabaseId
= mDatabaseId
.ref();
15792 MOZ_ASSERT(mMetadata
->mFilePath
.IsEmpty());
15793 mMetadata
->mFilePath
= mDatabaseFilePath
.ref();
15795 DatabaseActorInfo
* info
;
15796 if (gLiveDatabaseHashtable
->Get(mDatabaseId
.ref(), &info
)) {
15797 AssertMetadataConsistency(*info
->mMetadata
);
15798 mMetadata
= info
->mMetadata
.clonePtr();
15801 Maybe
<const CipherKey
> maybeKey
=
15802 mInPrivateBrowsing
? mFileManager
->MutableCipherKeyManagerRef().Get()
15805 MOZ_RELEASE_ASSERT(mInPrivateBrowsing
== maybeKey
.isSome());
15807 // XXX Shouldn't Manager() return already_AddRefed when
15808 // PBackgroundIDBFactoryParent is declared refcounted?
15809 mDatabase
= MakeSafeRefPtr
<Database
>(
15810 SafeRefPtr
{static_cast<Factory
*>(Manager()),
15811 AcquireStrongRefFromRawPtr
{}},
15812 mCommonParams
.principalInfo(), mContentParentId
, mOriginMetadata
,
15813 mTelemetryId
, mMetadata
.clonePtr(), mFileManager
.clonePtr(),
15814 std::move(mDirectoryLock
), mInPrivateBrowsing
, maybeKey
);
15817 info
->mLiveDatabases
.AppendElement(
15818 WrapNotNullUnchecked(mDatabase
.unsafeGetRawPtr()));
15820 // XXX Maybe use LookupOrInsertWith above, to avoid a second lookup here?
15821 info
= gLiveDatabaseHashtable
15824 MakeUnique
<DatabaseActorInfo
>(
15825 mMetadata
.clonePtr(),
15826 WrapNotNullUnchecked(mDatabase
.unsafeGetRawPtr())))
15830 // Balanced in Database::CleanupMetadata().
15831 IncreaseBusyCount();
15834 nsresult
OpenDatabaseOp::EnsureDatabaseActorIsAlive() {
15835 AssertIsOnOwningThread();
15836 MOZ_ASSERT(mState
== State::DatabaseWorkVersionChange
||
15837 mState
== State::SendingResults
);
15838 MOZ_ASSERT(!HasFailed());
15839 MOZ_ASSERT(!IsActorDestroyed());
15841 EnsureDatabaseActor();
15843 if (mDatabase
->IsActorAlive()) {
15847 auto* const factory
= static_cast<Factory
*>(Manager());
15849 QM_TRY_INSPECT(const auto& spec
, MetadataToSpec());
15851 mDatabase
->SetActorAlive();
15853 if (!factory
->SendPBackgroundIDBDatabaseConstructor(
15854 mDatabase
.unsafeGetRawPtr(), spec
, WrapNotNull(this))) {
15855 IDB_REPORT_INTERNAL_ERR();
15856 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
15862 Result
<DatabaseSpec
, nsresult
> OpenDatabaseOp::MetadataToSpec() const {
15863 AssertIsOnOwningThread();
15864 MOZ_ASSERT(mMetadata
);
15867 spec
.metadata() = mMetadata
->mCommonMetadata
;
15869 QM_TRY_UNWRAP(spec
.objectStores(),
15870 TransformIntoNewArrayAbortOnErr(
15871 mMetadata
->mObjectStores
,
15872 [](const auto& objectStoreEntry
)
15873 -> mozilla::Result
<ObjectStoreSpec
, nsresult
> {
15874 FullObjectStoreMetadata
* metadata
=
15875 objectStoreEntry
.GetWeak();
15876 MOZ_ASSERT(objectStoreEntry
.GetKey());
15877 MOZ_ASSERT(metadata
);
15879 ObjectStoreSpec objectStoreSpec
;
15880 objectStoreSpec
.metadata() = metadata
->mCommonMetadata
;
15882 QM_TRY_UNWRAP(auto indexes
,
15883 TransformIntoNewArray(
15884 metadata
->mIndexes
,
15885 [](const auto& indexEntry
) {
15886 FullIndexMetadata
* indexMetadata
=
15887 indexEntry
.GetWeak();
15888 MOZ_ASSERT(indexEntry
.GetKey());
15889 MOZ_ASSERT(indexMetadata
);
15891 return indexMetadata
->mCommonMetadata
;
15895 objectStoreSpec
.indexes() = std::move(indexes
);
15897 return objectStoreSpec
;
15906 void OpenDatabaseOp::AssertMetadataConsistency(
15907 const FullDatabaseMetadata
& aMetadata
) {
15908 AssertIsOnBackgroundThread();
15910 const FullDatabaseMetadata
& thisDB
= *mMetadata
;
15911 const FullDatabaseMetadata
& otherDB
= aMetadata
;
15913 MOZ_ASSERT(&thisDB
!= &otherDB
);
15915 MOZ_ASSERT(thisDB
.mCommonMetadata
.name() == otherDB
.mCommonMetadata
.name());
15916 MOZ_ASSERT(thisDB
.mCommonMetadata
.version() ==
15917 otherDB
.mCommonMetadata
.version());
15918 MOZ_ASSERT(thisDB
.mCommonMetadata
.persistenceType() ==
15919 otherDB
.mCommonMetadata
.persistenceType());
15920 MOZ_ASSERT(thisDB
.mDatabaseId
== otherDB
.mDatabaseId
);
15921 MOZ_ASSERT(thisDB
.mFilePath
== otherDB
.mFilePath
);
15923 // |thisDB| reflects the latest objectStore and index ids that have committed
15924 // to disk. The in-memory metadata |otherDB| keeps track of objectStores and
15925 // indexes that were created and then removed as well, so the next ids for
15926 // |otherDB| may be higher than for |thisDB|.
15927 MOZ_ASSERT(thisDB
.mNextObjectStoreId
<= otherDB
.mNextObjectStoreId
);
15928 MOZ_ASSERT(thisDB
.mNextIndexId
<= otherDB
.mNextIndexId
);
15930 MOZ_ASSERT(thisDB
.mObjectStores
.Count() == otherDB
.mObjectStores
.Count());
15932 for (const auto& thisObjectStore
: thisDB
.mObjectStores
.Values()) {
15933 MOZ_ASSERT(thisObjectStore
);
15934 MOZ_ASSERT(!thisObjectStore
->mDeleted
);
15936 auto otherObjectStore
= MatchMetadataNameOrId(
15937 otherDB
.mObjectStores
, thisObjectStore
->mCommonMetadata
.id());
15938 MOZ_ASSERT(otherObjectStore
);
15940 MOZ_ASSERT(thisObjectStore
!= &otherObjectStore
.ref());
15942 MOZ_ASSERT(thisObjectStore
->mCommonMetadata
.id() ==
15943 otherObjectStore
->mCommonMetadata
.id());
15944 MOZ_ASSERT(thisObjectStore
->mCommonMetadata
.name() ==
15945 otherObjectStore
->mCommonMetadata
.name());
15946 MOZ_ASSERT(thisObjectStore
->mCommonMetadata
.autoIncrement() ==
15947 otherObjectStore
->mCommonMetadata
.autoIncrement());
15948 MOZ_ASSERT(thisObjectStore
->mCommonMetadata
.keyPath() ==
15949 otherObjectStore
->mCommonMetadata
.keyPath());
15950 // mNextAutoIncrementId and mCommittedAutoIncrementId may be modified
15951 // concurrently with this OpenOp, so it is not possible to assert equality
15952 // here. It's also possible that we've written the new ids to disk but not
15953 // yet updated the in-memory count.
15954 // TODO The first part of the comment should probably be rephrased. I think
15955 // it still applies but it sounds as if this were thread-unsafe like it was
15956 // before, which isn't true anymore.
15958 const auto&& thisAutoIncrementIds
=
15959 thisObjectStore
->mAutoIncrementIds
.Lock();
15960 const auto&& otherAutoIncrementIds
=
15961 otherObjectStore
->mAutoIncrementIds
.Lock();
15963 MOZ_ASSERT(thisAutoIncrementIds
->next
<= otherAutoIncrementIds
->next
);
15965 thisAutoIncrementIds
->committed
<= otherAutoIncrementIds
->committed
||
15966 thisAutoIncrementIds
->committed
== otherAutoIncrementIds
->next
);
15968 MOZ_ASSERT(!otherObjectStore
->mDeleted
);
15970 MOZ_ASSERT(thisObjectStore
->mIndexes
.Count() ==
15971 otherObjectStore
->mIndexes
.Count());
15973 for (const auto& thisIndex
: thisObjectStore
->mIndexes
.Values()) {
15974 MOZ_ASSERT(thisIndex
);
15975 MOZ_ASSERT(!thisIndex
->mDeleted
);
15977 auto otherIndex
= MatchMetadataNameOrId(otherObjectStore
->mIndexes
,
15978 thisIndex
->mCommonMetadata
.id());
15979 MOZ_ASSERT(otherIndex
);
15981 MOZ_ASSERT(thisIndex
!= &otherIndex
.ref());
15983 MOZ_ASSERT(thisIndex
->mCommonMetadata
.id() ==
15984 otherIndex
->mCommonMetadata
.id());
15985 MOZ_ASSERT(thisIndex
->mCommonMetadata
.name() ==
15986 otherIndex
->mCommonMetadata
.name());
15987 MOZ_ASSERT(thisIndex
->mCommonMetadata
.keyPath() ==
15988 otherIndex
->mCommonMetadata
.keyPath());
15989 MOZ_ASSERT(thisIndex
->mCommonMetadata
.unique() ==
15990 otherIndex
->mCommonMetadata
.unique());
15991 MOZ_ASSERT(thisIndex
->mCommonMetadata
.multiEntry() ==
15992 otherIndex
->mCommonMetadata
.multiEntry());
15993 MOZ_ASSERT(!otherIndex
->mDeleted
);
16000 nsresult
OpenDatabaseOp::VersionChangeOp::DoDatabaseWork(
16001 DatabaseConnection
* aConnection
) {
16002 MOZ_ASSERT(aConnection
);
16003 aConnection
->AssertIsOnConnectionThread();
16004 MOZ_ASSERT(mOpenDatabaseOp
);
16005 MOZ_ASSERT(mOpenDatabaseOp
->mState
== State::DatabaseWorkVersionChange
);
16007 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
16008 !OperationMayProceed()) {
16009 IDB_REPORT_INTERNAL_ERR();
16010 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16013 AUTO_PROFILER_LABEL("OpenDatabaseOp::VersionChangeOp::DoDatabaseWork", DOM
);
16015 IDB_LOG_MARK_PARENT_TRANSACTION("Beginning database work", "DB Start",
16016 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
16017 mTransactionLoggingSerialNumber
);
16019 Transaction().SetActiveOnConnectionThread();
16021 QM_TRY(MOZ_TO_RESULT(aConnection
->BeginWriteTransaction()));
16023 // The parameter names are not used, parameters are bound by index only
16024 // locally in the same function.
16025 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
16026 "UPDATE database SET version = :version;"_ns
,
16028 mozIStorageStatement
& updateStmt
) -> mozilla::Result
<Ok
, nsresult
> {
16029 QM_TRY(MOZ_TO_RESULT(
16030 updateStmt
.BindInt64ByIndex(0, int64_t(self
.mRequestedVersion
))));
16038 nsresult
OpenDatabaseOp::VersionChangeOp::SendSuccessResult() {
16039 AssertIsOnOwningThread();
16040 MOZ_ASSERT(mOpenDatabaseOp
);
16041 MOZ_ASSERT(mOpenDatabaseOp
->mState
== State::DatabaseWorkVersionChange
);
16042 MOZ_ASSERT(mOpenDatabaseOp
->mVersionChangeOp
== this);
16044 nsresult rv
= mOpenDatabaseOp
->SendUpgradeNeeded();
16045 if (NS_WARN_IF(NS_FAILED(rv
))) {
16052 bool OpenDatabaseOp::VersionChangeOp::SendFailureResult(nsresult aResultCode
) {
16053 AssertIsOnOwningThread();
16054 MOZ_ASSERT(mOpenDatabaseOp
);
16055 MOZ_ASSERT(mOpenDatabaseOp
->mState
== State::DatabaseWorkVersionChange
);
16056 MOZ_ASSERT(mOpenDatabaseOp
->mVersionChangeOp
== this);
16058 mOpenDatabaseOp
->SetFailureCode(aResultCode
);
16059 mOpenDatabaseOp
->mState
= State::SendingResults
;
16061 MOZ_ALWAYS_SUCCEEDS(mOpenDatabaseOp
->Run());
16066 void OpenDatabaseOp::VersionChangeOp::Cleanup() {
16067 AssertIsOnOwningThread();
16068 MOZ_ASSERT(mOpenDatabaseOp
);
16069 MOZ_ASSERT(mOpenDatabaseOp
->mVersionChangeOp
== this);
16071 mOpenDatabaseOp
->mVersionChangeOp
= nullptr;
16072 mOpenDatabaseOp
= nullptr;
16075 // A bit hacky but the VersionChangeOp is not generated in response to a
16076 // child request like most other database operations. Do this to make our
16077 // assertions happy.
16079 // XXX: Depending on timing, in most cases, NoteActorDestroyed will not have
16080 // been destroyed before, but in some cases it has. This should be reworked in
16081 // a way this hack is not necessary. There are also several similar cases in
16082 // other *Op classes.
16083 if (!IsActorDestroyed()) {
16084 NoteActorDestroyed();
16088 TransactionDatabaseOperationBase::Cleanup();
16091 void DeleteDatabaseOp::LoadPreviousVersion(nsIFile
& aDatabaseFile
) {
16092 AssertIsOnIOThread();
16093 MOZ_ASSERT(mState
== State::DatabaseWorkOpen
);
16094 MOZ_ASSERT(!mPreviousVersion
);
16096 AUTO_PROFILER_LABEL("DeleteDatabaseOp::LoadPreviousVersion", DOM
);
16100 nsCOMPtr
<mozIStorageService
> ss
=
16101 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID
, &rv
);
16102 if (NS_WARN_IF(NS_FAILED(rv
))) {
16106 IndexedDatabaseManager
* const idm
= IndexedDatabaseManager::Get();
16109 const PersistenceType persistenceType
=
16110 mCommonParams
.metadata().persistenceType();
16111 const nsAString
& databaseName
= mCommonParams
.metadata().name();
16113 SafeRefPtr
<DatabaseFileManager
> fileManager
= idm
->GetFileManager(
16114 persistenceType
, mOriginMetadata
.mOrigin
, databaseName
);
16116 if (!fileManager
) {
16117 fileManager
= MakeSafeRefPtr
<DatabaseFileManager
>(
16118 persistenceType
, mOriginMetadata
, databaseName
, mDatabaseId
.ref(),
16119 mDatabaseFilePath
.ref(), mEnforcingQuota
, mInPrivateBrowsing
);
16122 const auto maybeKey
=
16124 ? Some(fileManager
->MutableCipherKeyManagerRef().Ensure())
16127 MOZ_RELEASE_ASSERT(mInPrivateBrowsing
== maybeKey
.isSome());
16129 // Pass -1 as the directoryLockId to disable quota checking, since we might
16130 // temporarily exceed quota before deleting the database.
16131 QM_TRY_INSPECT(const auto& dbFileUrl
,
16132 GetDatabaseFileURL(aDatabaseFile
, -1, maybeKey
), QM_VOID
);
16134 QM_TRY_UNWRAP(const NotNull
<nsCOMPtr
<mozIStorageConnection
>> connection
,
16135 OpenDatabaseAndHandleBusy(*ss
, *dbFileUrl
), QM_VOID
);
16139 QM_TRY_INSPECT(const auto& stmt
,
16140 CreateAndExecuteSingleStepStatement
<
16141 SingleStepResult::ReturnNullIfNoResult
>(
16142 *connection
, "SELECT name FROM database"_ns
),
16145 QM_TRY(OkIf(stmt
), QM_VOID
);
16147 nsString databaseName
;
16148 rv
= stmt
->GetString(0, databaseName
);
16149 if (NS_WARN_IF(NS_FAILED(rv
))) {
16153 MOZ_ASSERT(mCommonParams
.metadata().name() == databaseName
);
16157 QM_TRY_INSPECT(const auto& stmt
,
16158 CreateAndExecuteSingleStepStatement
<
16159 SingleStepResult::ReturnNullIfNoResult
>(
16160 *connection
, "SELECT version FROM database"_ns
),
16163 QM_TRY(OkIf(stmt
), QM_VOID
);
16166 rv
= stmt
->GetInt64(0, &version
);
16167 if (NS_WARN_IF(NS_FAILED(rv
))) {
16171 mPreviousVersion
= uint64_t(version
);
16174 nsresult
DeleteDatabaseOp::DatabaseOpen() {
16175 AssertIsOnOwningThread();
16176 MOZ_ASSERT(mState
== State::DatabaseOpenPending
);
16178 nsresult rv
= SendToIOThread();
16179 if (NS_WARN_IF(NS_FAILED(rv
))) {
16186 nsresult
DeleteDatabaseOp::DoDatabaseWork() {
16187 AssertIsOnIOThread();
16188 MOZ_ASSERT(mState
== State::DatabaseWorkOpen
);
16189 MOZ_ASSERT(mOriginMetadata
.mPersistenceType
==
16190 mCommonParams
.metadata().persistenceType());
16192 AUTO_PROFILER_LABEL("DeleteDatabaseOp::DoDatabaseWork", DOM
);
16194 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
16195 !OperationMayProceed()) {
16196 IDB_REPORT_INTERNAL_ERR();
16197 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16200 const nsAString
& databaseName
= mCommonParams
.metadata().name();
16202 QuotaManager
* const quotaManager
= QuotaManager::Get();
16203 MOZ_ASSERT(quotaManager
);
16205 QM_TRY_UNWRAP(auto directory
,
16206 quotaManager
->GetOriginDirectory(mOriginMetadata
));
16208 QM_TRY(MOZ_TO_RESULT(
16209 directory
->Append(NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME
))));
16211 QM_TRY_UNWRAP(mDatabaseDirectoryPath
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
16212 nsString
, directory
, GetPath
));
16214 mDatabaseFilenameBase
=
16215 GetDatabaseFilenameBase(databaseName
, mOriginMetadata
.mIsPrivate
);
16218 const auto& dbFile
,
16219 CloneFileAndAppend(*directory
, mDatabaseFilenameBase
+ kSQLiteSuffix
));
16224 const auto& databaseFilePath
,
16225 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString
, dbFile
, GetPath
));
16227 MOZ_ASSERT(databaseFilePath
== mDatabaseFilePath
.ref());
16231 QM_TRY_INSPECT(const bool& exists
,
16232 MOZ_TO_RESULT_INVOKE_MEMBER(dbFile
, Exists
));
16235 // Parts of this function may fail but that shouldn't prevent us from
16236 // deleting the file eventually.
16237 LoadPreviousVersion(*dbFile
);
16239 mState
= State::BeginVersionChange
;
16241 mState
= State::SendingResults
;
16244 QM_TRY(MOZ_TO_RESULT(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
)));
16249 nsresult
DeleteDatabaseOp::BeginVersionChange() {
16250 AssertIsOnOwningThread();
16251 MOZ_ASSERT(mState
== State::BeginVersionChange
);
16252 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
16254 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
16255 IsActorDestroyed()) {
16256 IDB_REPORT_INTERNAL_ERR();
16257 QM_TRY(MOZ_TO_RESULT(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
));
16260 DatabaseActorInfo
* info
;
16261 if (gLiveDatabaseHashtable
->Get(mDatabaseId
.ref(), &info
)) {
16262 MOZ_ASSERT(!info
->mWaitingFactoryOp
);
16265 SendVersionChangeMessages(info
, Nothing(), mPreviousVersion
, Nothing());
16266 if (NS_WARN_IF(NS_FAILED(rv
))) {
16270 if (!mMaybeBlockedDatabases
.IsEmpty()) {
16271 // If the actor gets destroyed, mWaitingFactoryOp will hold the last
16272 // strong reference to us.
16273 info
->mWaitingFactoryOp
= this;
16275 mState
= State::WaitingForOtherDatabasesToClose
;
16280 // No other databases need to be notified, just make sure that all
16281 // transactions are complete.
16282 WaitForTransactions();
16286 bool DeleteDatabaseOp::AreActorsAlive() {
16287 AssertIsOnOwningThread();
16289 return !IsActorDestroyed();
16292 nsresult
DeleteDatabaseOp::DispatchToWorkThread() {
16293 AssertIsOnOwningThread();
16294 MOZ_ASSERT(mState
== State::WaitingForTransactionsToComplete
);
16295 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
16297 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
16298 IsActorDestroyed()) {
16299 IDB_REPORT_INTERNAL_ERR();
16300 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16303 mState
= State::DatabaseWorkVersionChange
;
16305 RefPtr
<VersionChangeOp
> versionChangeOp
= new VersionChangeOp(this);
16307 QuotaManager
* const quotaManager
= QuotaManager::Get();
16308 MOZ_ASSERT(quotaManager
);
16310 nsresult rv
= quotaManager
->IOThread()->Dispatch(versionChangeOp
.forget(),
16311 NS_DISPATCH_NORMAL
);
16312 if (NS_WARN_IF(NS_FAILED(rv
))) {
16313 IDB_REPORT_INTERNAL_ERR();
16314 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16320 void DeleteDatabaseOp::SendBlockedNotification() {
16321 AssertIsOnOwningThread();
16322 MOZ_ASSERT(mState
== State::WaitingForOtherDatabasesToClose
);
16324 if (!IsActorDestroyed()) {
16325 Unused
<< SendBlocked(mPreviousVersion
);
16329 void DeleteDatabaseOp::SendResults() {
16330 AssertIsOnOwningThread();
16331 MOZ_ASSERT(mState
== State::SendingResults
);
16332 MOZ_ASSERT(mMaybeBlockedDatabases
.IsEmpty());
16334 DebugOnly
<DatabaseActorInfo
*> info
= nullptr;
16335 MOZ_ASSERT_IF(mDatabaseId
.isSome() && gLiveDatabaseHashtable
&&
16336 gLiveDatabaseHashtable
->Get(mDatabaseId
.ref(), &info
),
16337 !info
->mWaitingFactoryOp
);
16339 if (!IsActorDestroyed()) {
16340 FactoryRequestResponse response
;
16342 if (!HasFailed()) {
16343 response
= DeleteDatabaseRequestResponse(mPreviousVersion
);
16345 response
= ClampResultCode(ResultCode());
16348 Unused
<< PBackgroundIDBFactoryRequestParent::Send__delete__(this,
16352 mDirectoryLock
= nullptr;
16356 FinishSendResults();
16359 nsresult
DeleteDatabaseOp::VersionChangeOp::RunOnIOThread() {
16360 AssertIsOnIOThread();
16361 MOZ_ASSERT(mDeleteDatabaseOp
->mState
== State::DatabaseWorkVersionChange
);
16363 AUTO_PROFILER_LABEL("DeleteDatabaseOp::VersionChangeOp::RunOnIOThread", DOM
);
16365 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
16366 !OperationMayProceed()) {
16367 IDB_REPORT_INTERNAL_ERR();
16368 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16371 const PersistenceType
& persistenceType
=
16372 mDeleteDatabaseOp
->mCommonParams
.metadata().persistenceType();
16374 QuotaManager
* quotaManager
=
16375 mDeleteDatabaseOp
->mEnforcingQuota
? QuotaManager::Get() : nullptr;
16377 MOZ_ASSERT_IF(mDeleteDatabaseOp
->mEnforcingQuota
, quotaManager
);
16379 nsCOMPtr
<nsIFile
> directory
=
16380 GetFileForPath(mDeleteDatabaseOp
->mDatabaseDirectoryPath
);
16381 if (NS_WARN_IF(!directory
)) {
16382 IDB_REPORT_INTERNAL_ERR();
16383 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16386 nsresult rv
= RemoveDatabaseFilesAndDirectory(
16387 *directory
, mDeleteDatabaseOp
->mDatabaseFilenameBase
, quotaManager
,
16388 persistenceType
, mDeleteDatabaseOp
->mOriginMetadata
,
16389 mDeleteDatabaseOp
->mCommonParams
.metadata().name());
16390 if (NS_WARN_IF(NS_FAILED(rv
))) {
16394 rv
= mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
);
16395 if (NS_WARN_IF(NS_FAILED(rv
))) {
16402 void DeleteDatabaseOp::VersionChangeOp::RunOnOwningThread() {
16403 AssertIsOnOwningThread();
16404 MOZ_ASSERT(mDeleteDatabaseOp
->mState
== State::DatabaseWorkVersionChange
);
16406 const RefPtr
<DeleteDatabaseOp
> deleteOp
= std::move(mDeleteDatabaseOp
);
16408 if (deleteOp
->IsActorDestroyed()) {
16409 IDB_REPORT_INTERNAL_ERR();
16410 deleteOp
->SetFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
16411 } else if (HasFailed()) {
16412 deleteOp
->SetFailureCodeIfUnset(ResultCode());
16414 DatabaseActorInfo
* info
;
16416 // Inform all the other databases that they are now invalidated. That
16417 // should remove the previous metadata from our table.
16418 if (gLiveDatabaseHashtable
->Get(deleteOp
->mDatabaseId
.ref(), &info
)) {
16419 MOZ_ASSERT(!info
->mLiveDatabases
.IsEmpty());
16420 MOZ_ASSERT(!info
->mWaitingFactoryOp
);
16422 nsTArray
<SafeRefPtr
<Database
>> liveDatabases
;
16423 if (NS_WARN_IF(!liveDatabases
.SetCapacity(info
->mLiveDatabases
.Length(),
16425 deleteOp
->SetFailureCode(NS_ERROR_OUT_OF_MEMORY
);
16427 std::transform(info
->mLiveDatabases
.cbegin(),
16428 info
->mLiveDatabases
.cend(),
16429 MakeBackInserter(liveDatabases
),
16430 [](const auto& aDatabase
) -> SafeRefPtr
<Database
> {
16431 return {aDatabase
.get(), AcquireStrongRefFromRawPtr
{}};
16435 // The code below should result in the deletion of |info|. Set to null
16436 // here to make sure we find invalid uses later.
16440 for (const auto& database
: liveDatabases
) {
16441 database
->Invalidate();
16444 MOZ_ASSERT(!gLiveDatabaseHashtable
->Get(deleteOp
->mDatabaseId
.ref()));
16449 // We hold a strong ref to the deleteOp, so it's safe to call Run() directly.
16451 deleteOp
->mState
= State::SendingResults
;
16452 MOZ_ALWAYS_SUCCEEDS(deleteOp
->Run());
16455 // A bit hacky but the DeleteDatabaseOp::VersionChangeOp is not really a
16456 // normal database operation that is tied to an actor. Do this to make our
16457 // assertions happy.
16458 NoteActorDestroyed();
16462 nsresult
DeleteDatabaseOp::VersionChangeOp::Run() {
16465 if (IsOnIOThread()) {
16466 rv
= RunOnIOThread();
16468 RunOnOwningThread();
16472 if (NS_WARN_IF(NS_FAILED(rv
))) {
16473 SetFailureCodeIfUnset(rv
);
16475 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
));
16481 nsresult
GetDatabasesOp::DatabasesNotAvailable() {
16482 AssertIsOnIOThread();
16483 MOZ_ASSERT(mState
== State::DatabaseWorkOpen
);
16485 mState
= State::SendingResults
;
16487 QM_TRY(MOZ_TO_RESULT(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
)));
16492 nsresult
GetDatabasesOp::DoDirectoryWork() {
16493 AssertIsOnIOThread();
16494 MOZ_ASSERT(mState
== State::DirectoryWorkOpen
);
16496 // This state (DirectoryWorkOpen) runs immediately on the I/O thread, before
16497 // waiting for existing factory operations to complete (at which point
16498 // DoDatabaseWork will be invoked). To match the spec, we must snapshot the
16499 // current state of any databases that are being created (version = 0) or
16500 // upgraded (version >= 1) now. If we only sampled these values in
16501 // DoDatabaseWork, we would only see their post-creation/post-upgrade
16502 // versions, which would be incorrect.
16504 IndexedDatabaseManager
* const idm
= IndexedDatabaseManager::Get();
16507 const auto& fileManagers
=
16508 idm
->GetFileManagers(mPersistenceType
, mOriginMetadata
.mOrigin
);
16510 for (const auto& fileManager
: fileManagers
) {
16512 mDatabaseMetadataTable
.LookupOrInsert(fileManager
->DatabaseFilePath());
16513 metadata
.name() = fileManager
->DatabaseName();
16514 metadata
.version() = fileManager
->DatabaseVersion();
16517 // Must set this before dispatching otherwise we will race with the IO thread.
16518 mState
= State::DirectoryWorkDone
;
16520 QM_TRY(MOZ_TO_RESULT(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
)));
16525 nsresult
GetDatabasesOp::DatabaseOpen() {
16526 AssertIsOnOwningThread();
16527 MOZ_ASSERT(mState
== State::DatabaseOpenPending
);
16529 nsresult rv
= SendToIOThread();
16530 if (NS_WARN_IF(NS_FAILED(rv
))) {
16537 nsresult
GetDatabasesOp::DoDatabaseWork() {
16538 AssertIsOnIOThread();
16539 MOZ_ASSERT(mState
== State::DatabaseWorkOpen
);
16541 AUTO_PROFILER_LABEL("GetDatabasesOp::DoDatabaseWork", DOM
);
16543 if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
16544 !OperationMayProceed()) {
16545 IDB_REPORT_INTERNAL_ERR();
16546 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
16549 QuotaManager
* const quotaManager
= QuotaManager::Get();
16550 MOZ_ASSERT(quotaManager
);
16552 if (mPersistenceType
!= PERSISTENCE_TYPE_PERSISTENT
) {
16553 QM_TRY(MOZ_TO_RESULT(
16554 quotaManager
->EnsureTemporaryStorageIsInitializedInternal()));
16558 QM_TRY_INSPECT(const bool& exists
,
16559 quotaManager
->DoesOriginDirectoryExist(mOriginMetadata
));
16561 return DatabasesNotAvailable();
16565 QM_TRY((["aManager
, this]()
16566 -> mozilla::Result
<std::pair
<nsCOMPtr
<nsIFile
>, bool>, nsresult
> {
16567 if (mPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
16569 quotaManager
->EnsurePersistentOriginIsInitialized(mOriginMetadata
));
16572 QM_TRY_RETURN(quotaManager
->EnsureTemporaryOriginIsInitialized(
16573 mPersistenceType
, mOriginMetadata
));
16575 .map([](const auto& res
) { return Ok
{}; })));
16578 QM_TRY_INSPECT(const bool& exists
,
16579 quotaManager
->DoesClientDirectoryExist(
16580 ClientMetadata
{mOriginMetadata
, Client::IDB
}));
16582 return DatabasesNotAvailable();
16587 const auto& clientDirectory
,
16588 (["aManager
, this]()
16589 -> mozilla::Result
<std::pair
<nsCOMPtr
<nsIFile
>, bool>, nsresult
> {
16590 if (mPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
16591 QM_TRY_RETURN(quotaManager
->EnsurePersistentClientIsInitialized(
16592 ClientMetadata
{mOriginMetadata
, Client::IDB
}));
16595 QM_TRY_RETURN(quotaManager
->EnsureTemporaryClientIsInitialized(
16596 ClientMetadata
{mOriginMetadata
, Client::IDB
}));
16598 .map([](const auto& res
) { return res
.first
; })));
16601 (const auto& [subdirsToProcess
, databaseFilenames
]),
16602 QuotaClient::GetDatabaseFilenames(*clientDirectory
,
16603 /* aCanceled */ Atomic
<bool>{false}));
16605 for (const auto& databaseFilename
: databaseFilenames
) {
16607 const auto& databaseFile
,
16608 CloneFileAndAppend(*clientDirectory
, databaseFilename
+ kSQLiteSuffix
));
16611 databaseFile
->GetPath(path
);
16613 // Use the snapshotted values from DoDirectoryWork which correctly
16614 // snapshotted the state of any pending creations/upgrades. This does mean
16615 // that we need to skip reporting databases that had a version of 0 at that
16616 // time because they were still being created. In the event that any other
16617 // creation or upgrade requests are made after our operation is created,
16618 // this operation will block those, so it's not possible for this set of
16619 // data to get out of sync. The snapshotting (using cached database name
16620 // and version in DatabaseFileManager) also guarantees that we are not
16621 // touching the SQLite database here on the QuotaManager I/O thread which
16622 // is already open on the connection thread.
16624 auto metadata
= mDatabaseMetadataTable
.Lookup(path
);
16626 if (metadata
->version() != 0) {
16627 mDatabaseMetadataArray
.AppendElement(DatabaseMetadata(
16628 metadata
->name(), metadata
->version(), mPersistenceType
));
16634 // Since the database is not already open (there was no DatabaseFileManager
16635 // for snapshotting in DoDirectoryWork which could provide us with the
16636 // database name and version without needing to open the SQLite database),
16637 // it is safe and necessary for us to open the database on this thread and
16638 // retrieve its name and version. We do not need to worry about racing a
16639 // database open because database opens can only be processed on this
16640 // thread and we are performing the steps below synchronously.
16643 const auto& fmDirectory
,
16644 CloneFileAndAppend(*clientDirectory
,
16645 databaseFilename
+ kFileManagerDirectoryNameSuffix
));
16648 const NotNull
<nsCOMPtr
<mozIStorageConnection
>> connection
,
16649 CreateStorageConnection(*databaseFile
, *fmDirectory
, VoidString(),
16650 mOriginMetadata
.mOrigin
, mDirectoryLockId
,
16651 TelemetryIdForFile(databaseFile
), Nothing
{}));
16654 // Load version information.
16655 QM_TRY_INSPECT(const auto& stmt
,
16656 CreateAndExecuteSingleStepStatement
<
16657 SingleStepResult::ReturnNullIfNoResult
>(
16658 *connection
, "SELECT name, version FROM database"_ns
));
16660 QM_TRY(OkIf(stmt
), NS_ERROR_FILE_CORRUPTED
);
16663 const auto& databaseName
,
16664 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString
, stmt
, GetString
, 0));
16666 QM_TRY_INSPECT(const int64_t& version
,
16667 MOZ_TO_RESULT_INVOKE_MEMBER(stmt
, GetInt64
, 1));
16669 mDatabaseMetadataArray
.AppendElement(
16670 DatabaseMetadata(databaseName
, version
, mPersistenceType
));
16674 mState
= State::SendingResults
;
16676 QM_TRY(MOZ_TO_RESULT(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
)));
16681 nsresult
GetDatabasesOp::BeginVersionChange() {
16682 MOZ_CRASH("Not implemented because this should be unreachable.");
16685 bool GetDatabasesOp::AreActorsAlive() {
16686 MOZ_CRASH("Not implemented because this should be unreachable.");
16689 void GetDatabasesOp::SendBlockedNotification() {
16690 MOZ_CRASH("Not implemented because this should be unreachable.");
16693 nsresult
GetDatabasesOp::DispatchToWorkThread() {
16694 MOZ_CRASH("Not implemented because this should be unreachable.");
16697 void GetDatabasesOp::SendResults() {
16698 AssertIsOnOwningThread();
16699 MOZ_ASSERT(mState
== State::SendingResults
);
16702 NoteActorDestroyed();
16705 mResolver(mDatabaseMetadataArray
);
16707 mDirectoryLock
= nullptr;
16711 FinishSendResults();
16714 TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
16715 SafeRefPtr
<TransactionBase
> aTransaction
, const int64_t aRequestId
)
16716 : DatabaseOperationBase(aTransaction
->GetLoggingInfo()->Id(),
16717 aTransaction
->GetLoggingInfo()->NextRequestSN()),
16718 mTransaction(WrapNotNull(std::move(aTransaction
))),
16719 mRequestId(aRequestId
),
16720 mTransactionIsAborted((*mTransaction
)->IsAborted()),
16721 mTransactionLoggingSerialNumber((*mTransaction
)->LoggingSerialNumber()) {
16722 MOZ_ASSERT(LoggingSerialNumber());
16725 TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
16726 SafeRefPtr
<TransactionBase
> aTransaction
, const int64_t aRequestId
,
16727 uint64_t aLoggingSerialNumber
)
16728 : DatabaseOperationBase(aTransaction
->GetLoggingInfo()->Id(),
16729 aLoggingSerialNumber
),
16730 mTransaction(WrapNotNull(std::move(aTransaction
))),
16731 mRequestId(aRequestId
),
16732 mTransactionIsAborted((*mTransaction
)->IsAborted()),
16733 mTransactionLoggingSerialNumber((*mTransaction
)->LoggingSerialNumber()) {}
16735 TransactionDatabaseOperationBase::~TransactionDatabaseOperationBase() {
16736 MOZ_ASSERT(mInternalState
== InternalState::Completed
);
16737 MOZ_ASSERT(!mTransaction
,
16738 "TransactionDatabaseOperationBase::Cleanup() was not called by a "
16744 void TransactionDatabaseOperationBase::AssertIsOnConnectionThread() const {
16745 (*mTransaction
)->AssertIsOnConnectionThread();
16750 uint64_t TransactionDatabaseOperationBase::StartOnConnectionPool(
16751 const nsID
& aBackgroundChildLoggingId
, const nsACString
& aDatabaseId
,
16752 int64_t aLoggingSerialNumber
, const nsTArray
<nsString
>& aObjectStoreNames
,
16753 bool aIsWriteTransaction
) {
16754 AssertIsOnOwningThread();
16755 MOZ_ASSERT(mInternalState
== InternalState::Initial
);
16757 // Must set mInternalState before dispatching otherwise we will race with the
16758 // connection thread.
16759 mInternalState
= InternalState::DatabaseWork
;
16761 return gConnectionPool
->Start(aBackgroundChildLoggingId
, aDatabaseId
,
16762 aLoggingSerialNumber
, aObjectStoreNames
,
16763 aIsWriteTransaction
, this);
16766 void TransactionDatabaseOperationBase::DispatchToConnectionPool() {
16767 AssertIsOnOwningThread();
16768 MOZ_ASSERT(mInternalState
== InternalState::Initial
);
16770 Unused
<< this->Run();
16773 void TransactionDatabaseOperationBase::RunOnConnectionThread() {
16774 MOZ_ASSERT(!IsOnBackgroundThread());
16775 MOZ_ASSERT(mInternalState
== InternalState::DatabaseWork
);
16776 MOZ_ASSERT(!HasFailed());
16778 AUTO_PROFILER_LABEL("TransactionDatabaseOperationBase::RunOnConnectionThread",
16781 // There are several cases where we don't actually have to to any work here.
16783 if (mTransactionIsAborted
|| (*mTransaction
)->IsInvalidatedOnAnyThread()) {
16784 // This transaction is already set to be aborted or invalidated.
16785 SetFailureCode(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
);
16786 } else if (!OperationMayProceed()) {
16787 // The operation was canceled in some way, likely because the child process
16789 IDB_REPORT_INTERNAL_ERR();
16790 OverrideFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
16792 Database
& database
= (*mTransaction
)->GetMutableDatabase();
16794 // Here we're actually going to perform the database operation.
16795 nsresult rv
= database
.EnsureConnection();
16796 if (NS_WARN_IF(NS_FAILED(rv
))) {
16797 SetFailureCode(rv
);
16799 DatabaseConnection
* connection
= database
.GetConnection();
16800 MOZ_ASSERT(connection
);
16802 auto& storageConnection
= connection
->MutableStorageConnection();
16804 AutoSetProgressHandler autoProgress
;
16805 if (mLoggingSerialNumber
) {
16806 rv
= autoProgress
.Register(storageConnection
, this);
16807 if (NS_WARN_IF(NS_FAILED(rv
))) {
16808 SetFailureCode(rv
);
16812 if (NS_SUCCEEDED(rv
)) {
16813 if (mLoggingSerialNumber
) {
16814 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
16815 "Beginning database work", "DB Start",
16816 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
16817 mTransactionLoggingSerialNumber
, mLoggingSerialNumber
);
16820 rv
= DoDatabaseWork(connection
);
16822 if (mLoggingSerialNumber
) {
16823 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
16824 "Finished database work", "DB End",
16825 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
16826 mTransactionLoggingSerialNumber
, mLoggingSerialNumber
);
16829 if (NS_FAILED(rv
)) {
16830 SetFailureCode(rv
);
16836 // Must set mInternalState before dispatching otherwise we will race with the
16838 if (HasPreprocessInfo()) {
16839 mInternalState
= InternalState::SendingPreprocess
;
16841 mInternalState
= InternalState::SendingResults
;
16844 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
));
16847 bool TransactionDatabaseOperationBase::HasPreprocessInfo() { return false; }
16849 nsresult
TransactionDatabaseOperationBase::SendPreprocessInfo() {
16853 void TransactionDatabaseOperationBase::NoteContinueReceived() {
16854 AssertIsOnOwningThread();
16855 MOZ_ASSERT(mInternalState
== InternalState::WaitingForContinue
);
16857 mWaitingForContinue
= false;
16859 mInternalState
= InternalState::SendingResults
;
16861 // This TransactionDatabaseOperationBase can only be held alive by the IPDL.
16862 // Run() can end up with clearing that last reference. So we need to add
16863 // a self reference here.
16864 RefPtr
<TransactionDatabaseOperationBase
> kungFuDeathGrip
= this;
16866 Unused
<< this->Run();
16869 void TransactionDatabaseOperationBase::SendToConnectionPool() {
16870 AssertIsOnOwningThread();
16871 MOZ_ASSERT(mInternalState
== InternalState::Initial
);
16873 // Must set mInternalState before dispatching otherwise we will race with the
16874 // connection thread.
16875 mInternalState
= InternalState::DatabaseWork
;
16877 gConnectionPool
->Dispatch((*mTransaction
)->TransactionId(), this);
16879 (*mTransaction
)->NoteActiveRequest();
16882 void TransactionDatabaseOperationBase::SendPreprocess() {
16883 AssertIsOnOwningThread();
16884 MOZ_ASSERT(mInternalState
== InternalState::SendingPreprocess
);
16886 SendPreprocessInfoOrResults(/* aSendPreprocessInfo */ true);
16889 void TransactionDatabaseOperationBase::SendResults() {
16890 AssertIsOnOwningThread();
16891 MOZ_ASSERT(mInternalState
== InternalState::SendingResults
);
16893 SendPreprocessInfoOrResults(/* aSendPreprocessInfo */ false);
16896 void TransactionDatabaseOperationBase::SendPreprocessInfoOrResults(
16897 bool aSendPreprocessInfo
) {
16898 AssertIsOnOwningThread();
16899 MOZ_ASSERT(mInternalState
== InternalState::SendingPreprocess
||
16900 mInternalState
== InternalState::SendingResults
);
16902 // The flag is raised only when there is no mUpdateRefcountFunction for the
16903 // executing operation. It assume that is because the previous
16904 // StartTransactionOp was failed to begin a write transaction and it reported
16905 // when this operation has already jumped to the Connection thread.
16906 MOZ_DIAGNOSTIC_ASSERT_IF(mAssumingPreviousOperationFail
,
16907 (*mTransaction
)->IsAborted());
16909 if (NS_WARN_IF(IsActorDestroyed())) {
16910 // Normally we wouldn't need to send any notifications if the actor was
16911 // already destroyed, but this can be a VersionChangeOp which needs to
16912 // notify its parent operation (OpenDatabaseOp) about the failure.
16913 // So SendFailureResult needs to be called even when the actor was
16914 // destroyed. Normal operations redundantly check if the actor was
16915 // destroyed in SendSuccessResult and SendFailureResult, therefore it's
16916 // ok to call it in all cases here.
16917 if (!HasFailed()) {
16918 IDB_REPORT_INTERNAL_ERR();
16919 SetFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
16921 } else if ((*mTransaction
)->IsInvalidated() || (*mTransaction
)->IsAborted()) {
16922 // Aborted transactions always see their requests fail with ABORT_ERR,
16923 // even if the request succeeded or failed with another error.
16924 OverrideFailureCode(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
);
16927 const nsresult rv
= [aSendPreprocessInfo
, this] {
16929 return ResultCode();
16931 if (aSendPreprocessInfo
) {
16932 // This should not release the IPDL reference.
16933 return SendPreprocessInfo();
16935 // This may release the IPDL reference.
16936 return SendSuccessResult();
16939 if (NS_FAILED(rv
)) {
16940 SetFailureCodeIfUnset(rv
);
16942 // This should definitely release the IPDL reference.
16943 if (!SendFailureResult(rv
)) {
16944 // Abort the transaction.
16945 (*mTransaction
)->Abort(rv
, /* aForce */ false);
16949 if (aSendPreprocessInfo
&& !HasFailed()) {
16950 mInternalState
= InternalState::WaitingForContinue
;
16952 mWaitingForContinue
= true;
16954 if (mLoggingSerialNumber
) {
16955 (*mTransaction
)->NoteFinishedRequest(mRequestId
, ResultCode());
16960 mInternalState
= InternalState::Completed
;
16964 bool TransactionDatabaseOperationBase::Init(TransactionBase
& aTransaction
) {
16965 AssertIsOnBackgroundThread();
16966 MOZ_ASSERT(mInternalState
== InternalState::Initial
);
16971 void TransactionDatabaseOperationBase::Cleanup() {
16972 AssertIsOnOwningThread();
16973 MOZ_ASSERT(mInternalState
== InternalState::SendingResults
);
16975 mTransaction
.destroy();
16979 TransactionDatabaseOperationBase::Run() {
16980 switch (mInternalState
) {
16981 case InternalState::Initial
:
16982 SendToConnectionPool();
16985 case InternalState::DatabaseWork
:
16986 RunOnConnectionThread();
16989 case InternalState::SendingPreprocess
:
16993 case InternalState::SendingResults
:
16998 MOZ_CRASH("Bad state!");
17002 TransactionBase::CommitOp::CommitOp(SafeRefPtr
<TransactionBase
> aTransaction
,
17003 nsresult aResultCode
)
17004 : DatabaseOperationBase(aTransaction
->GetLoggingInfo()->Id(),
17005 aTransaction
->GetLoggingInfo()->NextRequestSN()),
17006 mTransaction(std::move(aTransaction
)),
17007 mResultCode(aResultCode
) {
17008 MOZ_ASSERT(mTransaction
);
17009 MOZ_ASSERT(LoggingSerialNumber());
17012 nsresult
TransactionBase::CommitOp::WriteAutoIncrementCounts() {
17013 MOZ_ASSERT(mTransaction
);
17014 mTransaction
->AssertIsOnConnectionThread();
17015 MOZ_ASSERT(mTransaction
->GetMode() == IDBTransaction::Mode::ReadWrite
||
17016 mTransaction
->GetMode() == IDBTransaction::Mode::ReadWriteFlush
||
17017 mTransaction
->GetMode() == IDBTransaction::Mode::Cleanup
||
17018 mTransaction
->GetMode() == IDBTransaction::Mode::VersionChange
);
17020 const nsTArray
<SafeRefPtr
<FullObjectStoreMetadata
>>& metadataArray
=
17021 mTransaction
->mModifiedAutoIncrementObjectStoreMetadataArray
;
17023 if (!metadataArray
.IsEmpty()) {
17024 DatabaseConnection
* connection
=
17025 mTransaction
->GetDatabase().GetConnection();
17026 MOZ_ASSERT(connection
);
17028 // The parameter names are not used, parameters are bound by index only
17029 // locally in the same function.
17030 auto stmt
= DatabaseConnection::LazyStatement(
17032 "UPDATE object_store "
17033 "SET auto_increment = :auto_increment WHERE id "
17034 "= :object_store_id;"_ns
);
17036 for (const auto& metadata
: metadataArray
) {
17037 MOZ_ASSERT(!metadata
->mDeleted
);
17039 const int64_t nextAutoIncrementId
= [&metadata
] {
17040 const auto&& lockedAutoIncrementIds
=
17041 metadata
->mAutoIncrementIds
.Lock();
17042 return lockedAutoIncrementIds
->next
;
17045 MOZ_ASSERT(nextAutoIncrementId
> 1);
17047 QM_TRY_INSPECT(const auto& borrowedStmt
, stmt
.Borrow());
17049 QM_TRY(MOZ_TO_RESULT(
17050 borrowedStmt
->BindInt64ByIndex(1, metadata
->mCommonMetadata
.id())));
17052 QM_TRY(MOZ_TO_RESULT(
17053 borrowedStmt
->BindInt64ByIndex(0, nextAutoIncrementId
)));
17055 QM_TRY(MOZ_TO_RESULT(borrowedStmt
->Execute()));
17062 void TransactionBase::CommitOp::CommitOrRollbackAutoIncrementCounts() {
17063 MOZ_ASSERT(mTransaction
);
17064 mTransaction
->AssertIsOnConnectionThread();
17065 MOZ_ASSERT(mTransaction
->GetMode() == IDBTransaction::Mode::ReadWrite
||
17066 mTransaction
->GetMode() == IDBTransaction::Mode::ReadWriteFlush
||
17067 mTransaction
->GetMode() == IDBTransaction::Mode::Cleanup
||
17068 mTransaction
->GetMode() == IDBTransaction::Mode::VersionChange
);
17070 const auto& metadataArray
=
17071 mTransaction
->mModifiedAutoIncrementObjectStoreMetadataArray
;
17073 if (!metadataArray
.IsEmpty()) {
17074 bool committed
= NS_SUCCEEDED(mResultCode
);
17076 for (const auto& metadata
: metadataArray
) {
17077 auto&& lockedAutoIncrementIds
= metadata
->mAutoIncrementIds
.Lock();
17080 lockedAutoIncrementIds
->committed
= lockedAutoIncrementIds
->next
;
17082 lockedAutoIncrementIds
->next
= lockedAutoIncrementIds
->committed
;
17090 void TransactionBase::CommitOp::AssertForeignKeyConsistency(
17091 DatabaseConnection
* aConnection
) {
17092 MOZ_ASSERT(aConnection
);
17093 MOZ_ASSERT(mTransaction
);
17094 mTransaction
->AssertIsOnConnectionThread();
17095 MOZ_ASSERT(mTransaction
->GetMode() != IDBTransaction::Mode::ReadOnly
);
17099 const auto& pragmaStmt
,
17100 CreateAndExecuteSingleStepStatement(
17101 aConnection
->MutableStorageConnection(), "PRAGMA foreign_keys;"_ns
),
17102 QM_ASSERT_UNREACHABLE_VOID
);
17104 int32_t foreignKeysEnabled
;
17105 MOZ_ALWAYS_SUCCEEDS(pragmaStmt
->GetInt32(0, &foreignKeysEnabled
));
17107 MOZ_ASSERT(foreignKeysEnabled
,
17108 "Database doesn't have foreign keys enabled!");
17112 QM_TRY_INSPECT(const bool& foreignKeyError
,
17113 CreateAndExecuteSingleStepStatement
<
17114 SingleStepResult::ReturnNullIfNoResult
>(
17115 aConnection
->MutableStorageConnection(),
17116 "PRAGMA foreign_key_check;"_ns
),
17117 QM_ASSERT_UNREACHABLE_VOID
);
17119 MOZ_ASSERT(!foreignKeyError
, "Database has inconsisistent foreign keys!");
17125 NS_IMPL_ISUPPORTS_INHERITED0(TransactionBase::CommitOp
, DatabaseOperationBase
)
17128 TransactionBase::CommitOp::Run() {
17129 MOZ_ASSERT(mTransaction
);
17130 mTransaction
->AssertIsOnConnectionThread();
17132 AUTO_PROFILER_LABEL("TransactionBase::CommitOp::Run", DOM
);
17134 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
17135 "Beginning database work", "DB Start",
17136 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
17137 mTransaction
->LoggingSerialNumber(), mLoggingSerialNumber
);
17139 if (mTransaction
->GetMode() != IDBTransaction::Mode::ReadOnly
&&
17140 mTransaction
->mHasBeenActiveOnConnectionThread
) {
17141 if (DatabaseConnection
* connection
=
17142 mTransaction
->GetDatabase().GetConnection()) {
17143 // May be null if the VersionChangeOp was canceled.
17144 DatabaseConnection::UpdateRefcountFunction
* fileRefcountFunction
=
17145 connection
->GetUpdateRefcountFunction();
17147 if (NS_SUCCEEDED(mResultCode
)) {
17148 if (fileRefcountFunction
) {
17149 mResultCode
= fileRefcountFunction
->WillCommit();
17150 NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode
),
17151 "WillCommit() failed!");
17154 if (NS_SUCCEEDED(mResultCode
)) {
17155 mResultCode
= WriteAutoIncrementCounts();
17156 NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode
),
17157 "WriteAutoIncrementCounts() failed!");
17159 if (NS_SUCCEEDED(mResultCode
)) {
17160 AssertForeignKeyConsistency(connection
);
17162 mResultCode
= connection
->CommitWriteTransaction();
17163 NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode
), "Commit failed!");
17165 if (NS_SUCCEEDED(mResultCode
) &&
17166 mTransaction
->GetMode() ==
17167 IDBTransaction::Mode::ReadWriteFlush
) {
17168 mResultCode
= connection
->Checkpoint();
17171 if (NS_SUCCEEDED(mResultCode
) && fileRefcountFunction
) {
17172 fileRefcountFunction
->DidCommit();
17178 if (NS_FAILED(mResultCode
)) {
17179 if (fileRefcountFunction
) {
17180 fileRefcountFunction
->DidAbort();
17183 connection
->RollbackWriteTransaction();
17186 CommitOrRollbackAutoIncrementCounts();
17188 connection
->FinishWriteTransaction();
17190 if (mTransaction
->GetMode() == IDBTransaction::Mode::Cleanup
) {
17191 connection
->DoIdleProcessing(/* aNeedsCheckpoint */ true,
17192 /* aInterrupted */ Atomic
<bool>(false));
17194 connection
->EnableQuotaChecks();
17199 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
17200 "Finished database work", "DB End",
17201 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
17202 mTransaction
->LoggingSerialNumber(), mLoggingSerialNumber
);
17204 IDB_LOG_MARK_PARENT_TRANSACTION("Finished database work", "DB End",
17205 IDB_LOG_ID_STRING(mBackgroundChildLoggingId
),
17206 mTransaction
->LoggingSerialNumber());
17211 void TransactionBase::CommitOp::TransactionFinishedBeforeUnblock() {
17212 AssertIsOnBackgroundThread();
17213 MOZ_ASSERT(mTransaction
);
17215 AUTO_PROFILER_LABEL("CommitOp::TransactionFinishedBeforeUnblock", DOM
);
17217 if (!IsActorDestroyed()) {
17218 mTransaction
->UpdateMetadata(mResultCode
);
17222 void TransactionBase::CommitOp::TransactionFinishedAfterUnblock() {
17223 AssertIsOnBackgroundThread();
17224 MOZ_ASSERT(mTransaction
);
17226 IDB_LOG_MARK_PARENT_TRANSACTION(
17227 "Finished with result 0x%" PRIx32
, "Transaction finished (0x%" PRIx32
")",
17228 IDB_LOG_ID_STRING(mTransaction
->GetLoggingInfo()->Id()),
17229 mTransaction
->LoggingSerialNumber(), static_cast<uint32_t>(mResultCode
));
17231 mTransaction
->SendCompleteNotification(ClampResultCode(mResultCode
));
17233 mTransaction
->GetMutableDatabase().UnregisterTransaction(*mTransaction
);
17235 mTransaction
= nullptr;
17238 // A bit hacky but the CommitOp is not really a normal database operation
17239 // that is tied to an actor. Do this to make our assertions happy.
17240 NoteActorDestroyed();
17244 nsresult
VersionChangeTransactionOp::SendSuccessResult() {
17245 AssertIsOnOwningThread();
17247 // Nothing to send here, the API assumes that this request always succeeds.
17251 bool VersionChangeTransactionOp::SendFailureResult(nsresult aResultCode
) {
17252 AssertIsOnOwningThread();
17254 // The only option here is to cause the transaction to abort.
17258 void VersionChangeTransactionOp::Cleanup() {
17259 AssertIsOnOwningThread();
17262 // A bit hacky but the VersionChangeTransactionOp is not generated in response
17263 // to a child request like most other database operations. Do this to make our
17264 // assertions happy.
17265 NoteActorDestroyed();
17268 TransactionDatabaseOperationBase::Cleanup();
17271 nsresult
CreateObjectStoreOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
17272 MOZ_ASSERT(aConnection
);
17273 aConnection
->AssertIsOnConnectionThread();
17275 AUTO_PROFILER_LABEL("CreateObjectStoreOp::DoDatabaseWork", DOM
);
17279 // Make sure that we're not creating an object store with the same name as
17280 // another that already exists. This should be impossible because we should
17281 // have thrown an error long before now...
17282 // The parameter names are not used, parameters are bound by index only
17283 // locally in the same function.
17284 QM_TRY_INSPECT(const bool& hasResult
,
17286 ->BorrowAndExecuteSingleStepStatement(
17288 "FROM object_store "
17289 "WHERE name = :name;"_ns
,
17290 [&self
= *this](auto& stmt
) -> Result
<Ok
, nsresult
> {
17291 QM_TRY(MOZ_TO_RESULT(stmt
.BindStringByIndex(
17292 0, self
.mMetadata
.name())));
17296 QM_ASSERT_UNREACHABLE
);
17298 MOZ_ASSERT(!hasResult
);
17302 DatabaseConnection::AutoSavepoint autoSave
;
17303 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
17304 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
17306 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
17310 // The parameter names are not used, parameters are bound by index only
17311 // locally in the same function.
17312 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17313 "INSERT INTO object_store (id, auto_increment, name, key_path) "
17314 "VALUES (:id, :auto_increment, :name, :key_path);"_ns
,
17316 mMetadata
](mozIStorageStatement
& stmt
) -> Result
<Ok
, nsresult
> {
17317 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(0, metadata
.id())));
17319 QM_TRY(MOZ_TO_RESULT(
17320 stmt
.BindInt32ByIndex(1, metadata
.autoIncrement() ? 1 : 0)));
17322 QM_TRY(MOZ_TO_RESULT(stmt
.BindStringByIndex(2, metadata
.name())));
17324 if (metadata
.keyPath().IsValid()) {
17325 QM_TRY(MOZ_TO_RESULT(stmt
.BindStringByIndex(
17326 3, metadata
.keyPath().SerializeToString())));
17328 QM_TRY(MOZ_TO_RESULT(stmt
.BindNullByIndex(3)));
17337 MOZ_ALWAYS_SUCCEEDS(
17338 aConnection
->MutableStorageConnection().GetLastInsertRowID(&id
));
17339 MOZ_ASSERT(mMetadata
.id() == id
);
17343 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
17348 nsresult
DeleteObjectStoreOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
17349 MOZ_ASSERT(aConnection
);
17350 aConnection
->AssertIsOnConnectionThread();
17352 AUTO_PROFILER_LABEL("DeleteObjectStoreOp::DoDatabaseWork", DOM
);
17356 // Make sure |mIsLastObjectStore| is telling the truth.
17359 aConnection
->BorrowCachedStatement("SELECT id FROM object_store;"_ns
),
17360 QM_ASSERT_UNREACHABLE
);
17362 bool foundThisObjectStore
= false;
17363 bool foundOtherObjectStore
= false;
17367 MOZ_ALWAYS_SUCCEEDS(stmt
->ExecuteStep(&hasResult
));
17374 MOZ_ALWAYS_SUCCEEDS(stmt
->GetInt64(0, &id
));
17376 if (id
== mMetadata
->mCommonMetadata
.id()) {
17377 foundThisObjectStore
= true;
17379 foundOtherObjectStore
= true;
17383 MOZ_ASSERT_IF(mIsLastObjectStore
,
17384 foundThisObjectStore
&& !foundOtherObjectStore
);
17385 MOZ_ASSERT_IF(!mIsLastObjectStore
,
17386 foundThisObjectStore
&& foundOtherObjectStore
);
17390 DatabaseConnection::AutoSavepoint autoSave
;
17391 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
17392 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
17394 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
17398 if (mIsLastObjectStore
) {
17399 // We can just delete everything if this is the last object store.
17400 QM_TRY(MOZ_TO_RESULT(
17401 aConnection
->ExecuteCachedStatement("DELETE FROM index_data;"_ns
)));
17403 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17404 "DELETE FROM unique_index_data;"_ns
)));
17406 QM_TRY(MOZ_TO_RESULT(
17407 aConnection
->ExecuteCachedStatement("DELETE FROM object_data;"_ns
)));
17409 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17410 "DELETE FROM object_store_index;"_ns
)));
17412 QM_TRY(MOZ_TO_RESULT(
17413 aConnection
->ExecuteCachedStatement("DELETE FROM object_store;"_ns
)));
17416 const bool& hasIndexes
,
17417 ObjectStoreHasIndexes(*aConnection
, mMetadata
->mCommonMetadata
.id()));
17419 const auto bindObjectStoreIdToFirstParameter
=
17420 [this](mozIStorageStatement
& stmt
) -> Result
<Ok
, nsresult
> {
17421 QM_TRY(MOZ_TO_RESULT(
17422 stmt
.BindInt64ByIndex(0, mMetadata
->mCommonMetadata
.id())));
17427 // The parameter name :object_store_id in the SQL statements below is not
17428 // used for binding, parameters are bound by index only locally by
17429 // bindObjectStoreIdToFirstParameter.
17431 QM_TRY(MOZ_TO_RESULT(DeleteObjectStoreDataTableRowsWithIndexes(
17432 aConnection
, mMetadata
->mCommonMetadata
.id(), Nothing())));
17434 // Now clean up the object store index table.
17435 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17436 "DELETE FROM object_store_index "
17437 "WHERE object_store_id = :object_store_id;"_ns
,
17438 bindObjectStoreIdToFirstParameter
)));
17440 // We only have to worry about object data if this object store has no
17442 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17443 "DELETE FROM object_data "
17444 "WHERE object_store_id = :object_store_id;"_ns
,
17445 bindObjectStoreIdToFirstParameter
)));
17448 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17449 "DELETE FROM object_store "
17450 "WHERE id = :object_store_id;"_ns
,
17451 bindObjectStoreIdToFirstParameter
)));
17455 int32_t deletedRowCount
;
17456 MOZ_ALWAYS_SUCCEEDS(
17457 aConnection
->MutableStorageConnection().GetAffectedRows(
17458 &deletedRowCount
));
17459 MOZ_ASSERT(deletedRowCount
== 1);
17464 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
17466 if (mMetadata
->mCommonMetadata
.autoIncrement()) {
17467 Transaction().ForgetModifiedAutoIncrementObjectStore(*mMetadata
);
17473 nsresult
RenameObjectStoreOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
17474 MOZ_ASSERT(aConnection
);
17475 aConnection
->AssertIsOnConnectionThread();
17477 AUTO_PROFILER_LABEL("RenameObjectStoreOp::DoDatabaseWork", DOM
);
17481 // Make sure that we're not renaming an object store with the same name as
17482 // another that already exists. This should be impossible because we should
17483 // have thrown an error long before now...
17484 // The parameter names are not used, parameters are bound by index only
17485 // locally in the same function.
17487 const bool& hasResult
,
17489 ->BorrowAndExecuteSingleStepStatement(
17491 "FROM object_store "
17492 "WHERE name = :name AND id != :id;"_ns
,
17493 [&self
= *this](auto& stmt
) -> Result
<Ok
, nsresult
> {
17495 MOZ_TO_RESULT(stmt
.BindStringByIndex(0, self
.mNewName
)));
17497 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(1, self
.mId
)));
17501 QM_ASSERT_UNREACHABLE
);
17503 MOZ_ASSERT(!hasResult
);
17507 DatabaseConnection::AutoSavepoint autoSave
;
17508 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
17509 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
17511 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
17515 // The parameter names are not used, parameters are bound by index only
17516 // locally in the same function.
17517 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17518 "UPDATE object_store "
17519 "SET name = :name "
17520 "WHERE id = :id;"_ns
,
17521 [&self
= *this](mozIStorageStatement
& stmt
) -> Result
<Ok
, nsresult
> {
17522 QM_TRY(MOZ_TO_RESULT(stmt
.BindStringByIndex(0, self
.mNewName
)));
17524 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(1, self
.mId
)));
17529 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
17534 CreateIndexOp::CreateIndexOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
17535 const IndexOrObjectStoreId aObjectStoreId
,
17536 const IndexMetadata
& aMetadata
)
17537 : VersionChangeTransactionOp(std::move(aTransaction
)),
17538 mMetadata(aMetadata
),
17539 mFileManager(Transaction().GetDatabase().GetFileManagerPtr()),
17540 mDatabaseId(Transaction().DatabaseId()),
17541 mObjectStoreId(aObjectStoreId
) {
17542 MOZ_ASSERT(aObjectStoreId
);
17543 MOZ_ASSERT(aMetadata
.id());
17544 MOZ_ASSERT(mFileManager
);
17545 MOZ_ASSERT(!mDatabaseId
.IsEmpty());
17548 nsresult
CreateIndexOp::InsertDataFromObjectStore(
17549 DatabaseConnection
* aConnection
) {
17550 MOZ_ASSERT(aConnection
);
17551 aConnection
->AssertIsOnConnectionThread();
17552 MOZ_ASSERT(mMaybeUniqueIndexTable
);
17554 AUTO_PROFILER_LABEL("CreateIndexOp::InsertDataFromObjectStore", DOM
);
17556 auto& storageConnection
= aConnection
->MutableStorageConnection();
17558 RefPtr
<UpdateIndexDataValuesFunction
> updateFunction
=
17559 new UpdateIndexDataValuesFunction(this, aConnection
,
17560 Transaction().GetDatabasePtr());
17562 constexpr auto updateFunctionName
= "update_index_data_values"_ns
;
17565 storageConnection
.CreateFunction(updateFunctionName
, 4, updateFunction
);
17566 if (NS_WARN_IF(NS_FAILED(rv
))) {
17570 rv
= InsertDataFromObjectStoreInternal(aConnection
);
17572 MOZ_ALWAYS_SUCCEEDS(storageConnection
.RemoveFunction(updateFunctionName
));
17574 if (NS_WARN_IF(NS_FAILED(rv
))) {
17581 nsresult
CreateIndexOp::InsertDataFromObjectStoreInternal(
17582 DatabaseConnection
* aConnection
) const {
17583 MOZ_ASSERT(aConnection
);
17584 aConnection
->AssertIsOnConnectionThread();
17585 MOZ_ASSERT(mMaybeUniqueIndexTable
);
17587 MOZ_ASSERT(aConnection
->HasStorageConnection());
17589 // The parameter names are not used, parameters are bound by index only
17590 // locally in the same function.
17591 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17592 "UPDATE object_data "
17593 "SET index_data_values = update_index_data_values "
17594 "(key, index_data_values, file_ids, data) "
17595 "WHERE object_store_id = :object_store_id;"_ns
,
17597 mObjectStoreId
](mozIStorageStatement
& stmt
) -> Result
<Ok
, nsresult
> {
17598 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(0, objectStoredId
)));
17606 bool CreateIndexOp::Init(TransactionBase
& aTransaction
) {
17607 AssertIsOnBackgroundThread();
17608 MOZ_ASSERT(mObjectStoreId
);
17609 MOZ_ASSERT(mMaybeUniqueIndexTable
.isNothing());
17611 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
17612 aTransaction
.GetMetadataForObjectStoreId(mObjectStoreId
);
17613 MOZ_ASSERT(objectStoreMetadata
);
17615 const uint32_t indexCount
= objectStoreMetadata
->mIndexes
.Count();
17620 auto uniqueIndexTable
= UniqueIndexTable
{indexCount
};
17622 for (const auto& value
: objectStoreMetadata
->mIndexes
.Values()) {
17623 MOZ_ASSERT(!uniqueIndexTable
.Contains(value
->mCommonMetadata
.id()));
17625 if (NS_WARN_IF(!uniqueIndexTable
.InsertOrUpdate(
17626 value
->mCommonMetadata
.id(), value
->mCommonMetadata
.unique(),
17628 IDB_REPORT_INTERNAL_ERR();
17629 NS_WARNING("out of memory");
17634 uniqueIndexTable
.MarkImmutable();
17636 mMaybeUniqueIndexTable
.emplace(std::move(uniqueIndexTable
));
17641 nsresult
CreateIndexOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
17642 MOZ_ASSERT(aConnection
);
17643 aConnection
->AssertIsOnConnectionThread();
17645 AUTO_PROFILER_LABEL("CreateIndexOp::DoDatabaseWork", DOM
);
17649 // Make sure that we're not creating an index with the same name and object
17650 // store as another that already exists. This should be impossible because
17651 // we should have thrown an error long before now...
17652 // The parameter names are not used, parameters are bound by index only
17653 // locally in the same function.
17655 const bool& hasResult
,
17657 ->BorrowAndExecuteSingleStepStatement(
17659 "FROM object_store_index "
17660 "WHERE object_store_id = :object_store_id AND name = :name;"_ns
,
17661 [&self
= *this](auto& stmt
) -> Result
<Ok
, nsresult
> {
17662 QM_TRY(MOZ_TO_RESULT(
17663 stmt
.BindInt64ByIndex(0, self
.mObjectStoreId
)));
17664 QM_TRY(MOZ_TO_RESULT(
17665 stmt
.BindStringByIndex(1, self
.mMetadata
.name())));
17669 QM_ASSERT_UNREACHABLE
);
17671 MOZ_ASSERT(!hasResult
);
17675 DatabaseConnection::AutoSavepoint autoSave
;
17676 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
17677 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
17679 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
17683 // The parameter names are not used, parameters are bound by index only
17684 // locally in the same function.
17685 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
17686 "INSERT INTO object_store_index (id, name, key_path, unique_index, "
17687 "multientry, object_store_id, locale, "
17689 "VALUES (:id, :name, :key_path, :unique, :multientry, "
17690 ":object_store_id, :locale, :is_auto_locale)"_ns
,
17691 [&metadata
= mMetadata
, objectStoreId
= mObjectStoreId
](
17692 mozIStorageStatement
& stmt
) -> Result
<Ok
, nsresult
> {
17693 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(0, metadata
.id())));
17695 QM_TRY(MOZ_TO_RESULT(stmt
.BindStringByIndex(1, metadata
.name())));
17697 QM_TRY(MOZ_TO_RESULT(
17698 stmt
.BindStringByIndex(2, metadata
.keyPath().SerializeToString())));
17701 MOZ_TO_RESULT(stmt
.BindInt32ByIndex(3, metadata
.unique() ? 1 : 0)));
17703 QM_TRY(MOZ_TO_RESULT(
17704 stmt
.BindInt32ByIndex(4, metadata
.multiEntry() ? 1 : 0)));
17705 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(5, objectStoreId
)));
17707 QM_TRY(MOZ_TO_RESULT(
17708 metadata
.locale().IsEmpty()
17709 ? stmt
.BindNullByIndex(6)
17710 : stmt
.BindUTF8StringByIndex(6, metadata
.locale())));
17712 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt32ByIndex(7, metadata
.autoLocale())));
17720 MOZ_ALWAYS_SUCCEEDS(
17721 aConnection
->MutableStorageConnection().GetLastInsertRowID(&id
));
17722 MOZ_ASSERT(mMetadata
.id() == id
);
17726 QM_TRY(MOZ_TO_RESULT(InsertDataFromObjectStore(aConnection
)));
17728 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
17733 NS_IMPL_ISUPPORTS(CreateIndexOp::UpdateIndexDataValuesFunction
,
17734 mozIStorageFunction
);
17737 CreateIndexOp::UpdateIndexDataValuesFunction::OnFunctionCall(
17738 mozIStorageValueArray
* aValues
, nsIVariant
** _retval
) {
17739 MOZ_ASSERT(aValues
);
17740 MOZ_ASSERT(_retval
);
17741 MOZ_ASSERT(mConnection
);
17742 mConnection
->AssertIsOnConnectionThread();
17744 MOZ_ASSERT(mOp
->mFileManager
);
17746 AUTO_PROFILER_LABEL(
17747 "CreateIndexOp::UpdateIndexDataValuesFunction::OnFunctionCall", DOM
);
17752 MOZ_ALWAYS_SUCCEEDS(aValues
->GetNumEntries(&argCount
));
17753 MOZ_ASSERT(argCount
== 4); // key, index_data_values, file_ids, data
17756 MOZ_ALWAYS_SUCCEEDS(aValues
->GetTypeOfIndex(0, &valueType
));
17757 MOZ_ASSERT(valueType
== mozIStorageValueArray::VALUE_TYPE_BLOB
);
17759 MOZ_ALWAYS_SUCCEEDS(aValues
->GetTypeOfIndex(1, &valueType
));
17760 MOZ_ASSERT(valueType
== mozIStorageValueArray::VALUE_TYPE_NULL
||
17761 valueType
== mozIStorageValueArray::VALUE_TYPE_BLOB
);
17763 MOZ_ALWAYS_SUCCEEDS(aValues
->GetTypeOfIndex(2, &valueType
));
17764 MOZ_ASSERT(valueType
== mozIStorageValueArray::VALUE_TYPE_NULL
||
17765 valueType
== mozIStorageValueArray::VALUE_TYPE_TEXT
);
17767 MOZ_ALWAYS_SUCCEEDS(aValues
->GetTypeOfIndex(3, &valueType
));
17768 MOZ_ASSERT(valueType
== mozIStorageValueArray::VALUE_TYPE_BLOB
||
17769 valueType
== mozIStorageValueArray::VALUE_TYPE_INTEGER
);
17773 QM_TRY_UNWRAP(auto cloneInfo
, GetStructuredCloneReadInfoFromValueArray(
17775 /* aDataIndex */ 3,
17776 /* aFileIdsIndex */ 2, *mOp
->mFileManager
));
17778 const IndexMetadata
& metadata
= mOp
->mMetadata
;
17779 const IndexOrObjectStoreId
& objectStoreId
= mOp
->mObjectStoreId
;
17781 // XXX does this really need a non-const cloneInfo?
17782 QM_TRY_INSPECT(const auto& updateInfos
,
17783 DeserializeIndexValueToUpdateInfos(
17784 metadata
.id(), metadata
.keyPath(), metadata
.multiEntry(),
17785 metadata
.locale(), cloneInfo
));
17787 if (updateInfos
.IsEmpty()) {
17788 // XXX See if we can do this without copying...
17790 nsCOMPtr
<nsIVariant
> unmodifiedValue
;
17792 // No changes needed, just return the original value.
17793 QM_TRY_INSPECT(const int32_t& valueType
,
17794 MOZ_TO_RESULT_INVOKE_MEMBER(aValues
, GetTypeOfIndex
, 1));
17796 MOZ_ASSERT(valueType
== mozIStorageValueArray::VALUE_TYPE_NULL
||
17797 valueType
== mozIStorageValueArray::VALUE_TYPE_BLOB
);
17799 if (valueType
== mozIStorageValueArray::VALUE_TYPE_NULL
) {
17800 unmodifiedValue
= new storage::NullVariant();
17801 unmodifiedValue
.forget(_retval
);
17805 MOZ_ASSERT(valueType
== mozIStorageValueArray::VALUE_TYPE_BLOB
);
17807 const uint8_t* blobData
;
17808 uint32_t blobDataLength
;
17810 MOZ_TO_RESULT(aValues
->GetSharedBlob(1, &blobDataLength
, &blobData
)));
17812 const std::pair
<uint8_t*, int> copiedBlobDataPair(
17813 static_cast<uint8_t*>(malloc(blobDataLength
)), blobDataLength
);
17815 if (!copiedBlobDataPair
.first
) {
17816 IDB_REPORT_INTERNAL_ERR();
17817 return NS_ERROR_OUT_OF_MEMORY
;
17820 memcpy(copiedBlobDataPair
.first
, blobData
, blobDataLength
);
17822 unmodifiedValue
= new storage::AdoptedBlobVariant(copiedBlobDataPair
);
17823 unmodifiedValue
.forget(_retval
);
17829 QM_TRY(MOZ_TO_RESULT(key
.SetFromValueArray(aValues
, 0)));
17831 QM_TRY_UNWRAP(auto indexValues
, ReadCompressedIndexDataValues(*aValues
, 1));
17833 const bool hadPreviousIndexValues
= !indexValues
.IsEmpty();
17835 const uint32_t updateInfoCount
= updateInfos
.Length();
17837 QM_TRY(OkIf(indexValues
.SetCapacity(indexValues
.Length() + updateInfoCount
,
17839 NS_ERROR_OUT_OF_MEMORY
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
17841 // First construct the full list to update the index_data_values row.
17842 for (const IndexUpdateInfo
& info
: updateInfos
) {
17843 MOZ_ALWAYS_TRUE(indexValues
.InsertElementSorted(
17844 IndexDataValue(metadata
.id(), metadata
.unique(), info
.value(),
17845 info
.localizedValue()),
17849 QM_TRY_UNWRAP((auto [indexValuesBlob
, indexValuesBlobLength
]),
17850 MakeCompressedIndexDataValues(indexValues
));
17852 MOZ_ASSERT(!indexValuesBlobLength
== !(indexValuesBlob
.get()));
17854 nsCOMPtr
<nsIVariant
> value
;
17856 if (!indexValuesBlob
) {
17857 value
= new storage::NullVariant();
17859 value
.forget(_retval
);
17863 // Now insert the new table rows. We only need to construct a new list if
17864 // the full list is different.
17865 if (hadPreviousIndexValues
) {
17866 indexValues
.ClearAndRetainStorage();
17868 MOZ_ASSERT(indexValues
.Capacity() >= updateInfoCount
);
17870 for (const IndexUpdateInfo
& info
: updateInfos
) {
17871 MOZ_ALWAYS_TRUE(indexValues
.InsertElementSorted(
17872 IndexDataValue(metadata
.id(), metadata
.unique(), info
.value(),
17873 info
.localizedValue()),
17878 QM_TRY(MOZ_TO_RESULT(
17879 InsertIndexTableRows(mConnection
, objectStoreId
, key
, indexValues
)));
17881 value
= new storage::AdoptedBlobVariant(
17882 std::pair(indexValuesBlob
.release(), indexValuesBlobLength
));
17884 value
.forget(_retval
);
17888 DeleteIndexOp::DeleteIndexOp(SafeRefPtr
<VersionChangeTransaction
> aTransaction
,
17889 const IndexOrObjectStoreId aObjectStoreId
,
17890 const IndexOrObjectStoreId aIndexId
,
17891 const bool aUnique
, const bool aIsLastIndex
)
17892 : VersionChangeTransactionOp(std::move(aTransaction
)),
17893 mObjectStoreId(aObjectStoreId
),
17894 mIndexId(aIndexId
),
17896 mIsLastIndex(aIsLastIndex
) {
17897 MOZ_ASSERT(aObjectStoreId
);
17898 MOZ_ASSERT(aIndexId
);
17901 nsresult
DeleteIndexOp::RemoveReferencesToIndex(
17902 DatabaseConnection
* aConnection
, const Key
& aObjectStoreKey
,
17903 nsTArray
<IndexDataValue
>& aIndexValues
) const {
17904 MOZ_ASSERT(!NS_IsMainThread());
17905 MOZ_ASSERT(!IsOnBackgroundThread());
17906 MOZ_ASSERT(aConnection
);
17907 MOZ_ASSERT(!aObjectStoreKey
.IsUnset());
17908 MOZ_ASSERT_IF(!mIsLastIndex
, !aIndexValues
.IsEmpty());
17910 AUTO_PROFILER_LABEL("DeleteIndexOp::RemoveReferencesToIndex", DOM
);
17912 if (mIsLastIndex
) {
17913 // There is no need to parse the previous entry in the index_data_values
17914 // column if this is the last index. Simply set it to NULL.
17915 QM_TRY_INSPECT(const auto& stmt
,
17916 aConnection
->BorrowCachedStatement(
17917 "UPDATE object_data "
17918 "SET index_data_values = NULL "
17919 "WHERE object_store_id = :"_ns
+
17920 kStmtParamNameObjectStoreId
+ " AND key = :"_ns
+
17921 kStmtParamNameKey
+ ";"_ns
));
17923 QM_TRY(MOZ_TO_RESULT(
17924 stmt
->BindInt64ByName(kStmtParamNameObjectStoreId
, mObjectStoreId
)));
17926 QM_TRY(MOZ_TO_RESULT(
17927 aObjectStoreKey
.BindToStatement(&*stmt
, kStmtParamNameKey
)));
17929 QM_TRY(MOZ_TO_RESULT(stmt
->Execute()));
17935 IndexDataValue search
;
17936 search
.mIndexId
= mIndexId
;
17938 // Use raw pointers for search to avoid redundant index validity checks.
17939 // Maybe this should better be encapsulated in nsTArray.
17940 const auto* const begin
= aIndexValues
.Elements();
17941 const auto* const end
= aIndexValues
.Elements() + aIndexValues
.Length();
17943 const auto indexIdComparator
= [](const IndexDataValue
& aA
,
17944 const IndexDataValue
& aB
) {
17945 return aA
.mIndexId
< aB
.mIndexId
;
17948 MOZ_ASSERT(std::is_sorted(begin
, end
, indexIdComparator
));
17950 const auto [beginRange
, endRange
] =
17951 std::equal_range(begin
, end
, search
, indexIdComparator
);
17952 if (beginRange
== end
) {
17953 IDB_REPORT_INTERNAL_ERR();
17954 return NS_ERROR_FILE_CORRUPTED
;
17957 aIndexValues
.RemoveElementsAt(beginRange
- begin
, endRange
- beginRange
);
17960 QM_TRY(MOZ_TO_RESULT(UpdateIndexValues(aConnection
, mObjectStoreId
,
17961 aObjectStoreKey
, aIndexValues
)));
17966 nsresult
DeleteIndexOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
17967 MOZ_ASSERT(aConnection
);
17968 aConnection
->AssertIsOnConnectionThread();
17972 // Make sure |mIsLastIndex| is telling the truth.
17973 // The parameter names are not used, parameters are bound by index only
17974 // locally in the same function.
17975 QM_TRY_INSPECT(const auto& stmt
,
17976 aConnection
->BorrowCachedStatement(
17978 "FROM object_store_index "
17979 "WHERE object_store_id = :object_store_id;"_ns
),
17980 QM_ASSERT_UNREACHABLE
);
17982 MOZ_ALWAYS_SUCCEEDS(stmt
->BindInt64ByIndex(0, mObjectStoreId
));
17984 bool foundThisIndex
= false;
17985 bool foundOtherIndex
= false;
17989 MOZ_ALWAYS_SUCCEEDS(stmt
->ExecuteStep(&hasResult
));
17996 MOZ_ALWAYS_SUCCEEDS(stmt
->GetInt64(0, &id
));
17998 if (id
== mIndexId
) {
17999 foundThisIndex
= true;
18001 foundOtherIndex
= true;
18005 MOZ_ASSERT_IF(mIsLastIndex
, foundThisIndex
&& !foundOtherIndex
);
18006 MOZ_ASSERT_IF(!mIsLastIndex
, foundThisIndex
&& foundOtherIndex
);
18010 AUTO_PROFILER_LABEL("DeleteIndexOp::DoDatabaseWork", DOM
);
18012 DatabaseConnection::AutoSavepoint autoSave
;
18013 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
18014 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
18016 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
18020 // mozStorage warns that these statements trigger a sort operation but we
18021 // don't care because this is a very rare call and we expect it to be slow.
18022 // The cost of having an index on this field is too high.
18024 const auto& selectStmt
,
18025 aConnection
->BorrowCachedStatement(
18028 ? "/* do not warn (bug someone else) */ "
18029 "SELECT value, object_data_key "
18030 "FROM unique_index_data "
18031 "WHERE index_id = :"_ns
+
18032 kStmtParamNameIndexId
+
18033 " ORDER BY object_data_key ASC;"_ns
18034 : "/* do not warn (bug out) */ "
18035 "SELECT unique_index_data.value, "
18036 "unique_index_data.object_data_key, "
18037 "object_data.index_data_values "
18038 "FROM unique_index_data "
18039 "JOIN object_data "
18040 "ON unique_index_data.object_data_key = object_data.key "
18041 "WHERE unique_index_data.index_id = :"_ns
+
18042 kStmtParamNameIndexId
+
18043 " AND object_data.object_store_id = :"_ns
+
18044 kStmtParamNameObjectStoreId
+
18045 " ORDER BY unique_index_data.object_data_key ASC;"_ns
)
18047 ? "/* do not warn (bug me not) */ "
18048 "SELECT value, object_data_key "
18050 "WHERE index_id = :"_ns
+
18051 kStmtParamNameIndexId
+
18052 " AND object_store_id = :"_ns
+
18053 kStmtParamNameObjectStoreId
+
18054 " ORDER BY object_data_key ASC;"_ns
18055 : "/* do not warn (bug off) */ "
18056 "SELECT index_data.value, "
18057 "index_data.object_data_key, "
18058 "object_data.index_data_values "
18060 "JOIN object_data "
18061 "ON index_data.object_data_key = object_data.key "
18062 "WHERE index_data.index_id = :"_ns
+
18063 kStmtParamNameIndexId
+
18064 " AND object_data.object_store_id = :"_ns
+
18065 kStmtParamNameObjectStoreId
+
18066 " ORDER BY index_data.object_data_key ASC;"_ns
)));
18068 QM_TRY(MOZ_TO_RESULT(
18069 selectStmt
->BindInt64ByName(kStmtParamNameIndexId
, mIndexId
)));
18071 if (!mUnique
|| !mIsLastIndex
) {
18072 QM_TRY(MOZ_TO_RESULT(selectStmt
->BindInt64ByName(
18073 kStmtParamNameObjectStoreId
, mObjectStoreId
)));
18076 Key lastObjectStoreKey
;
18077 IndexDataValuesAutoArray lastIndexValues
;
18079 QM_TRY(CollectWhileHasResult(
18081 [this, &aConnection
, &lastObjectStoreKey
, &lastIndexValues
,
18082 deleteIndexRowStmt
=
18083 DatabaseConnection::LazyStatement
{
18086 ? "DELETE FROM unique_index_data "
18087 "WHERE index_id = :"_ns
+
18088 kStmtParamNameIndexId
+ " AND value = :"_ns
+
18089 kStmtParamNameValue
+ ";"_ns
18090 : "DELETE FROM index_data "
18091 "WHERE index_id = :"_ns
+
18092 kStmtParamNameIndexId
+ " AND value = :"_ns
+
18093 kStmtParamNameValue
+ " AND object_data_key = :"_ns
+
18094 kStmtParamNameObjectDataKey
+ ";"_ns
}](
18095 auto& selectStmt
) mutable -> Result
<Ok
, nsresult
> {
18096 // We always need the index key to delete the index row.
18098 QM_TRY(MOZ_TO_RESULT(indexKey
.SetFromStatement(&selectStmt
, 0)));
18100 QM_TRY(OkIf(!indexKey
.IsUnset()), Err(NS_ERROR_FILE_CORRUPTED
),
18101 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
18103 // Don't call |lastObjectStoreKey.BindToStatement()| directly because we
18104 // don't want to copy the same key multiple times.
18105 const uint8_t* objectStoreKeyData
;
18106 uint32_t objectStoreKeyDataLength
;
18107 QM_TRY(MOZ_TO_RESULT(selectStmt
.GetSharedBlob(
18108 1, &objectStoreKeyDataLength
, &objectStoreKeyData
)));
18110 QM_TRY(OkIf(objectStoreKeyDataLength
), Err(NS_ERROR_FILE_CORRUPTED
),
18111 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
18113 const nsDependentCString
currentObjectStoreKeyBuffer(
18114 reinterpret_cast<const char*>(objectStoreKeyData
),
18115 objectStoreKeyDataLength
);
18116 if (currentObjectStoreKeyBuffer
!= lastObjectStoreKey
.GetBuffer()) {
18117 // We just walked to the next object store key.
18118 if (!lastObjectStoreKey
.IsUnset()) {
18119 // Before we move on to the next key we need to update the previous
18120 // key's index_data_values column.
18121 QM_TRY(MOZ_TO_RESULT(RemoveReferencesToIndex(
18122 aConnection
, lastObjectStoreKey
, lastIndexValues
)));
18125 // Save the object store key.
18126 lastObjectStoreKey
= Key(currentObjectStoreKeyBuffer
);
18128 // And the |index_data_values| row if this isn't the only index.
18129 if (!mIsLastIndex
) {
18130 lastIndexValues
.ClearAndRetainStorage();
18131 QM_TRY(MOZ_TO_RESULT(
18132 ReadCompressedIndexDataValues(selectStmt
, 2, lastIndexValues
)));
18134 QM_TRY(OkIf(!lastIndexValues
.IsEmpty()),
18135 Err(NS_ERROR_FILE_CORRUPTED
),
18136 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
18140 // Now delete the index row.
18142 QM_TRY_INSPECT(const auto& borrowedDeleteIndexRowStmt
,
18143 deleteIndexRowStmt
.Borrow());
18145 QM_TRY(MOZ_TO_RESULT(borrowedDeleteIndexRowStmt
->BindInt64ByName(
18146 kStmtParamNameIndexId
, mIndexId
)));
18148 QM_TRY(MOZ_TO_RESULT(indexKey
.BindToStatement(
18149 &*borrowedDeleteIndexRowStmt
, kStmtParamNameValue
)));
18152 QM_TRY(MOZ_TO_RESULT(lastObjectStoreKey
.BindToStatement(
18153 &*borrowedDeleteIndexRowStmt
, kStmtParamNameObjectDataKey
)));
18156 QM_TRY(MOZ_TO_RESULT(borrowedDeleteIndexRowStmt
->Execute()));
18162 // Take care of the last key.
18163 if (!lastObjectStoreKey
.IsUnset()) {
18164 MOZ_ASSERT_IF(!mIsLastIndex
, !lastIndexValues
.IsEmpty());
18166 QM_TRY(MOZ_TO_RESULT(RemoveReferencesToIndex(
18167 aConnection
, lastObjectStoreKey
, lastIndexValues
)));
18170 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
18171 "DELETE FROM object_store_index "
18172 "WHERE id = :index_id;"_ns
,
18174 mIndexId
](mozIStorageStatement
& deleteStmt
) -> Result
<Ok
, nsresult
> {
18175 QM_TRY(MOZ_TO_RESULT(deleteStmt
.BindInt64ByIndex(0, indexId
)));
18182 int32_t deletedRowCount
;
18183 MOZ_ALWAYS_SUCCEEDS(aConnection
->MutableStorageConnection().GetAffectedRows(
18184 &deletedRowCount
));
18185 MOZ_ASSERT(deletedRowCount
== 1);
18189 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
18194 nsresult
RenameIndexOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
18195 MOZ_ASSERT(aConnection
);
18196 aConnection
->AssertIsOnConnectionThread();
18198 AUTO_PROFILER_LABEL("RenameIndexOp::DoDatabaseWork", DOM
);
18202 // Make sure that we're not renaming an index with the same name as another
18203 // that already exists. This should be impossible because we should have
18204 // thrown an error long before now...
18205 // The parameter names are not used, parameters are bound by index only
18206 // locally in the same function.
18207 QM_TRY_INSPECT(const bool& hasResult
,
18209 ->BorrowAndExecuteSingleStepStatement(
18211 "FROM object_store_index "
18212 "WHERE object_store_id = :object_store_id "
18213 "AND name = :name "
18214 "AND id != :id;"_ns
,
18215 [&self
= *this](auto& stmt
) -> Result
<Ok
, nsresult
> {
18216 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(
18217 0, self
.mObjectStoreId
)));
18218 QM_TRY(MOZ_TO_RESULT(
18219 stmt
.BindStringByIndex(1, self
.mNewName
)));
18220 QM_TRY(MOZ_TO_RESULT(
18221 stmt
.BindInt64ByIndex(2, self
.mIndexId
)));
18226 QM_ASSERT_UNREACHABLE
);
18228 MOZ_ASSERT(!hasResult
);
18231 Unused
<< mObjectStoreId
;
18234 DatabaseConnection::AutoSavepoint autoSave
;
18235 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
18236 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
18238 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
18242 // The parameter names are not used, parameters are bound by index only
18243 // locally in the same function.
18244 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
18245 "UPDATE object_store_index "
18246 "SET name = :name "
18247 "WHERE id = :id;"_ns
,
18248 [&self
= *this](mozIStorageStatement
& stmt
) -> Result
<Ok
, nsresult
> {
18249 QM_TRY(MOZ_TO_RESULT(stmt
.BindStringByIndex(0, self
.mNewName
)));
18251 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByIndex(1, self
.mIndexId
)));
18256 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
18261 Result
<bool, nsresult
> NormalTransactionOp::ObjectStoreHasIndexes(
18262 DatabaseConnection
& aConnection
, const IndexOrObjectStoreId aObjectStoreId
,
18263 const bool aMayHaveIndexes
) {
18264 aConnection
.AssertIsOnConnectionThread();
18265 MOZ_ASSERT(aObjectStoreId
);
18267 if (Transaction().GetMode() == IDBTransaction::Mode::VersionChange
&&
18269 // If this is a version change transaction then mObjectStoreMayHaveIndexes
18270 // could be wrong (e.g. if a unique index failed to be created due to a
18271 // constraint error). We have to check on this thread by asking the database
18273 QM_TRY_RETURN(DatabaseOperationBase::ObjectStoreHasIndexes(aConnection
,
18279 const bool& hasIndexes
,
18280 DatabaseOperationBase::ObjectStoreHasIndexes(aConnection
, aObjectStoreId
),
18281 QM_ASSERT_UNREACHABLE
);
18282 MOZ_ASSERT(aMayHaveIndexes
== hasIndexes
);
18285 return aMayHaveIndexes
;
18288 Result
<PreprocessParams
, nsresult
> NormalTransactionOp::GetPreprocessParams() {
18289 return PreprocessParams
{};
18292 nsresult
NormalTransactionOp::SendPreprocessInfo() {
18293 AssertIsOnOwningThread();
18294 MOZ_ASSERT(!IsActorDestroyed());
18296 QM_TRY_INSPECT(const auto& params
, GetPreprocessParams());
18298 MOZ_ASSERT(params
.type() != PreprocessParams::T__None
);
18300 if (NS_WARN_IF(!PBackgroundIDBRequestParent::SendPreprocess(params
))) {
18301 IDB_REPORT_INTERNAL_ERR();
18302 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
18308 nsresult
NormalTransactionOp::SendSuccessResult() {
18309 AssertIsOnOwningThread();
18311 if (!IsActorDestroyed()) {
18312 static const size_t kMaxIDBMsgOverhead
= 1024 * 1024 * 10; // 10MB
18313 const uint32_t maximalSizeFromPref
=
18314 IndexedDatabaseManager::MaxSerializedMsgSize();
18315 MOZ_ASSERT(maximalSizeFromPref
> kMaxIDBMsgOverhead
);
18316 const size_t kMaxMessageSize
= maximalSizeFromPref
- kMaxIDBMsgOverhead
;
18318 RequestResponse response
;
18319 size_t responseSize
= kMaxMessageSize
;
18320 GetResponse(response
, &responseSize
);
18322 if (responseSize
>= kMaxMessageSize
) {
18323 nsPrintfCString
warning(
18324 "The serialized value is too large"
18325 " (size=%zu bytes, max=%zu bytes).",
18326 responseSize
, kMaxMessageSize
);
18327 NS_WARNING(warning
.get());
18328 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
18331 MOZ_ASSERT(response
.type() != RequestResponse::T__None
);
18333 if (response
.type() == RequestResponse::Tnsresult
) {
18334 MOZ_ASSERT(NS_FAILED(response
.get_nsresult()));
18336 return response
.get_nsresult();
18340 !PBackgroundIDBRequestParent::Send__delete__(this, response
))) {
18341 IDB_REPORT_INTERNAL_ERR();
18342 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
18347 mResponseSent
= true;
18353 bool NormalTransactionOp::SendFailureResult(nsresult aResultCode
) {
18354 AssertIsOnOwningThread();
18355 MOZ_ASSERT(NS_FAILED(aResultCode
));
18357 bool result
= false;
18359 if (!IsActorDestroyed()) {
18360 result
= PBackgroundIDBRequestParent::Send__delete__(
18361 this, ClampResultCode(aResultCode
));
18365 mResponseSent
= true;
18371 void NormalTransactionOp::Cleanup() {
18372 AssertIsOnOwningThread();
18373 MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent
);
18375 TransactionDatabaseOperationBase::Cleanup();
18378 void NormalTransactionOp::ActorDestroy(ActorDestroyReason aWhy
) {
18379 AssertIsOnOwningThread();
18381 NoteActorDestroyed();
18383 // Assume ActorDestroy can happen at any time, so we can't probe the current
18384 // state since mInternalState can be modified on any thread (only one thread
18385 // at a time based on the state machine).
18386 // However we can use mWaitingForContinue which is only touched on the owning
18387 // thread. If mWaitingForContinue is true, we can also modify mInternalState
18388 // since we are guaranteed that there are no pending runnables which would
18389 // probe mInternalState to decide what code needs to run (there shouldn't be
18390 // any running runnables on other threads either).
18392 if (IsWaitingForContinue()) {
18393 NoteContinueReceived();
18396 // We don't have to handle the case when mWaitingForContinue is not true since
18397 // it means that either nothing has been initialized yet, so nothing to
18398 // cleanup or there are pending runnables that will detect that the actor has
18399 // been destroyed and cleanup accordingly.
18402 mozilla::ipc::IPCResult
NormalTransactionOp::RecvContinue(
18403 const PreprocessResponse
& aResponse
) {
18404 AssertIsOnOwningThread();
18406 switch (aResponse
.type()) {
18407 case PreprocessResponse::Tnsresult
:
18408 SetFailureCode(aResponse
.get_nsresult());
18411 case PreprocessResponse::TObjectStoreGetPreprocessResponse
:
18412 case PreprocessResponse::TObjectStoreGetAllPreprocessResponse
:
18416 MOZ_CRASH("Should never get here!");
18419 NoteContinueReceived();
18424 ObjectStoreAddOrPutRequestOp::ObjectStoreAddOrPutRequestOp(
18425 SafeRefPtr
<TransactionBase
> aTransaction
, const int64_t aRequestId
,
18426 RequestParams
&& aParams
)
18427 : NormalTransactionOp(std::move(aTransaction
), aRequestId
),
18429 std::move(aParams
.type() == RequestParams::TObjectStoreAddParams
18430 ? aParams
.get_ObjectStoreAddParams().commonParams()
18431 : aParams
.get_ObjectStorePutParams().commonParams())),
18432 mOriginMetadata(Transaction().GetDatabase().OriginMetadata()),
18433 mPersistenceType(Transaction().GetDatabase().Type()),
18434 mOverwrite(aParams
.type() == RequestParams::TObjectStorePutParams
),
18435 mObjectStoreMayHaveIndexes(false) {
18436 MOZ_ASSERT(aParams
.type() == RequestParams::TObjectStoreAddParams
||
18437 aParams
.type() == RequestParams::TObjectStorePutParams
);
18440 Transaction().GetMetadataForObjectStoreId(mParams
.objectStoreId());
18441 MOZ_ASSERT(mMetadata
);
18443 mObjectStoreMayHaveIndexes
= mMetadata
->HasLiveIndexes();
18445 mDataOverThreshold
=
18446 snappy::MaxCompressedLength(mParams
.cloneInfo().data().data
.Size()) >
18447 IndexedDatabaseManager::DataThreshold();
18450 nsresult
ObjectStoreAddOrPutRequestOp::RemoveOldIndexDataValues(
18451 DatabaseConnection
* aConnection
) {
18452 AssertIsOnConnectionThread();
18453 MOZ_ASSERT(aConnection
);
18454 MOZ_ASSERT(mOverwrite
);
18455 MOZ_ASSERT(!mResponse
.IsUnset());
18459 QM_TRY_INSPECT(const bool& hasIndexes
,
18460 DatabaseOperationBase::ObjectStoreHasIndexes(
18461 *aConnection
, mParams
.objectStoreId()),
18462 QM_ASSERT_UNREACHABLE
);
18464 MOZ_ASSERT(hasIndexes
,
18465 "Don't use this slow method if there are no indexes!");
18470 const auto& indexValuesStmt
,
18471 aConnection
->BorrowAndExecuteSingleStepStatement(
18472 "SELECT index_data_values "
18473 "FROM object_data "
18474 "WHERE object_store_id = :"_ns
+
18475 kStmtParamNameObjectStoreId
+ " AND key = :"_ns
+
18476 kStmtParamNameKey
+ ";"_ns
,
18477 [&self
= *this](auto& stmt
) -> mozilla::Result
<Ok
, nsresult
> {
18478 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByName(
18479 kStmtParamNameObjectStoreId
, self
.mParams
.objectStoreId())));
18481 QM_TRY(MOZ_TO_RESULT(
18482 self
.mResponse
.BindToStatement(&stmt
, kStmtParamNameKey
)));
18487 if (indexValuesStmt
) {
18488 QM_TRY_INSPECT(const auto& existingIndexValues
,
18489 ReadCompressedIndexDataValues(**indexValuesStmt
, 0));
18491 QM_TRY(MOZ_TO_RESULT(
18492 DeleteIndexDataTableRows(aConnection
, mResponse
, existingIndexValues
)));
18498 bool ObjectStoreAddOrPutRequestOp::Init(TransactionBase
& aTransaction
) {
18499 AssertIsOnOwningThread();
18501 const nsTArray
<IndexUpdateInfo
>& indexUpdateInfos
=
18502 mParams
.indexUpdateInfos();
18504 if (!indexUpdateInfos
.IsEmpty()) {
18505 mUniqueIndexTable
.emplace();
18507 for (const auto& updateInfo
: indexUpdateInfos
) {
18508 auto indexMetadata
= mMetadata
->mIndexes
.Lookup(updateInfo
.indexId());
18509 MOZ_ALWAYS_TRUE(indexMetadata
);
18511 MOZ_ASSERT(!(*indexMetadata
)->mDeleted
);
18513 const IndexOrObjectStoreId
& indexId
=
18514 (*indexMetadata
)->mCommonMetadata
.id();
18515 const bool& unique
= (*indexMetadata
)->mCommonMetadata
.unique();
18517 MOZ_ASSERT(indexId
== updateInfo
.indexId());
18518 MOZ_ASSERT_IF(!(*indexMetadata
)->mCommonMetadata
.multiEntry(),
18519 !mUniqueIndexTable
.ref().Contains(indexId
));
18521 if (NS_WARN_IF(!mUniqueIndexTable
.ref().InsertOrUpdate(indexId
, unique
,
18526 } else if (mOverwrite
) {
18527 mUniqueIndexTable
.emplace();
18530 if (mUniqueIndexTable
.isSome()) {
18531 mUniqueIndexTable
.ref().MarkImmutable();
18536 TransformIntoNewArray(
18537 mParams
.fileAddInfos(),
18538 [](const auto& fileAddInfo
) {
18539 MOZ_ASSERT(fileAddInfo
.type() == StructuredCloneFileBase::eBlob
||
18540 fileAddInfo
.type() ==
18541 StructuredCloneFileBase::eMutableFile
);
18543 switch (fileAddInfo
.type()) {
18544 case StructuredCloneFileBase::eBlob
: {
18545 PBackgroundIDBDatabaseFileParent
* file
=
18546 fileAddInfo
.file().AsParent();
18549 auto* const fileActor
= static_cast<DatabaseFile
*>(file
);
18550 MOZ_ASSERT(fileActor
);
18552 return StoredFileInfo::CreateForBlob(
18553 fileActor
->GetFileInfoPtr(), fileActor
);
18557 MOZ_CRASH("Should never get here!");
18563 if (mDataOverThreshold
) {
18565 aTransaction
.GetDatabase().GetFileManager().CreateFileInfo();
18566 if (NS_WARN_IF(!fileInfo
)) {
18570 mStoredFileInfos
.EmplaceBack(StoredFileInfo::CreateForStructuredClone(
18571 std::move(fileInfo
),
18572 MakeRefPtr
<SCInputStream
>(mParams
.cloneInfo().data().data
)));
18578 nsresult
ObjectStoreAddOrPutRequestOp::DoDatabaseWork(
18579 DatabaseConnection
* aConnection
) {
18580 MOZ_ASSERT(aConnection
);
18581 aConnection
->AssertIsOnConnectionThread();
18582 MOZ_ASSERT(aConnection
->HasStorageConnection());
18584 AUTO_PROFILER_LABEL("ObjectStoreAddOrPutRequestOp::DoDatabaseWork", DOM
);
18586 DatabaseConnection::AutoSavepoint autoSave
;
18587 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
18588 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
18590 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
18594 QM_TRY_INSPECT(const bool& objectStoreHasIndexes
,
18595 ObjectStoreHasIndexes(*aConnection
, mParams
.objectStoreId(),
18596 mObjectStoreMayHaveIndexes
));
18598 // This will be the final key we use.
18599 Key
& key
= mResponse
;
18600 key
= mParams
.key();
18602 const bool keyUnset
= key
.IsUnset();
18603 const IndexOrObjectStoreId osid
= mParams
.objectStoreId();
18605 // First delete old index_data_values if we're overwriting something and we
18607 if (mOverwrite
&& !keyUnset
&& objectStoreHasIndexes
) {
18608 QM_TRY(MOZ_TO_RESULT(RemoveOldIndexDataValues(aConnection
)));
18611 int64_t autoIncrementNum
= 0;
18614 // The "|| keyUnset" here is mostly a debugging tool. If a key isn't
18615 // specified we should never have a collision and so it shouldn't matter
18616 // if we allow overwrite or not. By not allowing overwrite we raise
18617 // detectable errors rather than corrupting data.
18618 const auto optReplaceDirective
=
18619 (!mOverwrite
|| keyUnset
) ? ""_ns
: "OR REPLACE "_ns
;
18620 QM_TRY_INSPECT(const auto& stmt
,
18621 aConnection
->BorrowCachedStatement(
18622 "INSERT "_ns
+ optReplaceDirective
+
18623 "INTO object_data "
18624 "(object_store_id, key, file_ids, data) "
18626 kStmtParamNameObjectStoreId
+ ", :"_ns
+
18627 kStmtParamNameKey
+ ", :"_ns
+ kStmtParamNameFileIds
+
18628 ", :"_ns
+ kStmtParamNameData
+ ");"_ns
));
18630 QM_TRY(MOZ_TO_RESULT(
18631 stmt
->BindInt64ByName(kStmtParamNameObjectStoreId
, osid
)));
18633 const SerializedStructuredCloneWriteInfo
& cloneInfo
= mParams
.cloneInfo();
18634 const JSStructuredCloneData
& cloneData
= cloneInfo
.data().data
;
18635 const size_t cloneDataSize
= cloneData
.Size();
18637 MOZ_ASSERT(!keyUnset
|| mMetadata
->mCommonMetadata
.autoIncrement(),
18638 "Should have key unless autoIncrement");
18640 if (mMetadata
->mCommonMetadata
.autoIncrement()) {
18643 const auto&& lockedAutoIncrementIds
=
18644 mMetadata
->mAutoIncrementIds
.Lock();
18646 autoIncrementNum
= lockedAutoIncrementIds
->next
;
18649 MOZ_ASSERT(autoIncrementNum
> 0);
18651 if (autoIncrementNum
> (1LL << 53)) {
18652 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR
;
18655 QM_TRY(key
.SetFromInteger(autoIncrementNum
));
18657 // Update index keys if primary key is preserved in child.
18658 for (auto& updateInfo
: mParams
.indexUpdateInfos()) {
18659 updateInfo
.value().MaybeUpdateAutoIncrementKey(autoIncrementNum
);
18661 } else if (key
.IsFloat()) {
18662 double numericKey
= key
.ToFloat();
18663 numericKey
= std::min(numericKey
, double(1LL << 53));
18664 numericKey
= floor(numericKey
);
18666 const auto&& lockedAutoIncrementIds
=
18667 mMetadata
->mAutoIncrementIds
.Lock();
18668 if (numericKey
>= lockedAutoIncrementIds
->next
) {
18669 autoIncrementNum
= numericKey
;
18673 if (keyUnset
&& mMetadata
->mCommonMetadata
.keyPath().IsValid()) {
18674 const SerializedStructuredCloneWriteInfo
& cloneInfo
=
18675 mParams
.cloneInfo();
18676 MOZ_ASSERT(cloneInfo
.offsetToKeyProp());
18677 MOZ_ASSERT(cloneDataSize
> sizeof(uint64_t));
18678 MOZ_ASSERT(cloneInfo
.offsetToKeyProp() <=
18679 (cloneDataSize
- sizeof(uint64_t)));
18681 // Special case where someone put an object into an autoIncrement'ing
18682 // objectStore with no key in its keyPath set. We needed to figure out
18683 // which row id we would get above before we could set that properly.
18684 uint64_t keyPropValue
=
18685 ReinterpretDoubleAsUInt64(static_cast<double>(autoIncrementNum
));
18687 static const size_t keyPropSize
= sizeof(uint64_t);
18689 char keyPropBuffer
[keyPropSize
];
18690 LittleEndian::writeUint64(keyPropBuffer
, keyPropValue
);
18692 auto iter
= cloneData
.Start();
18693 MOZ_ALWAYS_TRUE(cloneData
.Advance(iter
, cloneInfo
.offsetToKeyProp()));
18695 cloneData
.UpdateBytes(iter
, keyPropBuffer
, keyPropSize
));
18699 key
.BindToStatement(&*stmt
, kStmtParamNameKey
);
18701 if (mDataOverThreshold
) {
18702 // The data we store in the SQLite database is a (signed) 64-bit integer.
18703 // The flags are left-shifted 32 bits so the max value is 0xFFFFFFFF.
18704 // The file_ids index occupies the lower 32 bits and its max is
18706 static const uint32_t kCompressedFlag
= (1 << 0);
18708 uint32_t flags
= 0;
18709 flags
|= kCompressedFlag
;
18711 const uint32_t index
= mStoredFileInfos
.Length() - 1;
18713 const int64_t data
= (uint64_t(flags
) << 32) | index
;
18715 QM_TRY(MOZ_TO_RESULT(stmt
->BindInt64ByName(kStmtParamNameData
, data
)));
18717 AutoTArray
<char, 4096> flatCloneData
; // 4096 from JSStructuredCloneData
18718 QM_TRY(OkIf(flatCloneData
.SetLength(cloneDataSize
, fallible
)),
18719 Err(NS_ERROR_OUT_OF_MEMORY
));
18722 auto iter
= cloneData
.Start();
18724 cloneData
.ReadBytes(iter
, flatCloneData
.Elements(), cloneDataSize
));
18727 // Compress the bytes before adding into the database.
18728 const char* const uncompressed
= flatCloneData
.Elements();
18729 const size_t uncompressedLength
= cloneDataSize
;
18731 size_t compressedLength
= snappy::MaxCompressedLength(uncompressedLength
);
18733 UniqueFreePtr
<char> compressed(
18734 static_cast<char*>(malloc(compressedLength
)));
18735 if (NS_WARN_IF(!compressed
)) {
18736 return NS_ERROR_OUT_OF_MEMORY
;
18739 snappy::RawCompress(uncompressed
, uncompressedLength
, compressed
.get(),
18740 &compressedLength
);
18742 uint8_t* const dataBuffer
=
18743 reinterpret_cast<uint8_t*>(compressed
.release());
18744 const size_t dataBufferLength
= compressedLength
;
18746 QM_TRY(MOZ_TO_RESULT(stmt
->BindAdoptedBlobByName(
18747 kStmtParamNameData
, dataBuffer
, dataBufferLength
)));
18750 if (!mStoredFileInfos
.IsEmpty()) {
18751 // Moved outside the loop to allow it to be cached when demanded by the
18752 // first write. (We may have mStoredFileInfos without any required
18754 Maybe
<FileHelper
> fileHelper
;
18755 nsAutoString fileIds
;
18757 for (auto& storedFileInfo
: mStoredFileInfos
) {
18758 MOZ_ASSERT(storedFileInfo
.IsValid());
18760 QM_TRY_INSPECT(const auto& inputStream
,
18761 storedFileInfo
.GetInputStream());
18764 if (fileHelper
.isNothing()) {
18765 fileHelper
.emplace(Transaction().GetDatabase().GetFileManagerPtr());
18766 QM_TRY(MOZ_TO_RESULT(fileHelper
->Init()),
18767 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
,
18768 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
18771 const DatabaseFileInfo
& fileInfo
= storedFileInfo
.GetFileInfo();
18772 const DatabaseFileManager
& fileManager
= fileInfo
.Manager();
18774 const auto file
= fileHelper
->GetFile(fileInfo
);
18775 QM_TRY(OkIf(file
), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
,
18776 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
18778 const auto journalFile
= fileHelper
->GetJournalFile(fileInfo
);
18779 QM_TRY(OkIf(journalFile
), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
,
18780 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
18782 nsCString fileKeyId
;
18783 fileKeyId
.AppendInt(fileInfo
.Id());
18785 const auto maybeKey
=
18786 fileManager
.IsInPrivateBrowsingMode()
18787 ? fileManager
.MutableCipherKeyManagerRef().Get(fileKeyId
)
18790 QM_TRY(MOZ_TO_RESULT(fileHelper
->CreateFileFromStream(
18791 *file
, *journalFile
, *inputStream
,
18792 storedFileInfo
.ShouldCompress(), maybeKey
))
18793 .mapErr([](const nsresult rv
) {
18794 if (NS_ERROR_GET_MODULE(rv
) !=
18795 NS_ERROR_MODULE_DOM_INDEXEDDB
) {
18796 IDB_REPORT_INTERNAL_ERR();
18797 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
18802 ([&fileManager
, &file
= *file
,
18803 &journalFile
= *journalFile
](const auto) {
18804 // Try to remove the file if the copy failed.
18805 QM_TRY(MOZ_TO_RESULT(
18806 fileManager
.SyncDeleteFile(file
, journalFile
)),
18810 storedFileInfo
.NotifyWriteSucceeded();
18813 if (!fileIds
.IsEmpty()) {
18814 fileIds
.Append(' ');
18816 storedFileInfo
.Serialize(fileIds
);
18819 QM_TRY(MOZ_TO_RESULT(
18820 stmt
->BindStringByName(kStmtParamNameFileIds
, fileIds
)));
18822 QM_TRY(MOZ_TO_RESULT(stmt
->BindNullByName(kStmtParamNameFileIds
)));
18825 QM_TRY(MOZ_TO_RESULT(stmt
->Execute()), QM_PROPAGATE
,
18826 [keyUnset
= DebugOnly
{keyUnset
}](const nsresult rv
) {
18827 if (rv
== NS_ERROR_STORAGE_CONSTRAINT
) {
18828 MOZ_ASSERT(!keyUnset
, "Generated key had a collision!");
18833 // Update our indexes if needed.
18834 if (!mParams
.indexUpdateInfos().IsEmpty()) {
18835 MOZ_ASSERT(mUniqueIndexTable
.isSome());
18837 // Write the index_data_values column.
18838 QM_TRY_INSPECT(const auto& indexValues
,
18839 IndexDataValuesFromUpdateInfos(mParams
.indexUpdateInfos(),
18840 mUniqueIndexTable
.ref()));
18843 MOZ_TO_RESULT(UpdateIndexValues(aConnection
, osid
, key
, indexValues
)));
18845 QM_TRY(MOZ_TO_RESULT(
18846 InsertIndexTableRows(aConnection
, osid
, key
, indexValues
)));
18849 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
18851 if (autoIncrementNum
) {
18853 auto&& lockedAutoIncrementIds
= mMetadata
->mAutoIncrementIds
.Lock();
18855 lockedAutoIncrementIds
->next
= autoIncrementNum
+ 1;
18858 Transaction().NoteModifiedAutoIncrementObjectStore(mMetadata
);
18864 void ObjectStoreAddOrPutRequestOp::GetResponse(RequestResponse
& aResponse
,
18865 size_t* aResponseSize
) {
18866 AssertIsOnOwningThread();
18869 aResponse
= ObjectStorePutResponse(mResponse
);
18870 *aResponseSize
= mResponse
.GetBuffer().Length();
18872 aResponse
= ObjectStoreAddResponse(mResponse
);
18873 *aResponseSize
= mResponse
.GetBuffer().Length();
18877 void ObjectStoreAddOrPutRequestOp::Cleanup() {
18878 AssertIsOnOwningThread();
18880 mStoredFileInfos
.Clear();
18882 NormalTransactionOp::Cleanup();
18885 NS_IMPL_ISUPPORTS(ObjectStoreAddOrPutRequestOp::SCInputStream
, nsIInputStream
)
18888 ObjectStoreAddOrPutRequestOp::SCInputStream::Close() { return NS_OK
; }
18891 ObjectStoreAddOrPutRequestOp::SCInputStream::Available(uint64_t* _retval
) {
18892 return NS_ERROR_NOT_IMPLEMENTED
;
18896 ObjectStoreAddOrPutRequestOp::SCInputStream::StreamStatus() { return NS_OK
; }
18899 ObjectStoreAddOrPutRequestOp::SCInputStream::Read(char* aBuf
, uint32_t aCount
,
18900 uint32_t* _retval
) {
18901 return ReadSegments(NS_CopySegmentToBuffer
, aBuf
, aCount
, _retval
);
18905 ObjectStoreAddOrPutRequestOp::SCInputStream::ReadSegments(
18906 nsWriteSegmentFun aWriter
, void* aClosure
, uint32_t aCount
,
18907 uint32_t* _retval
) {
18911 uint32_t count
= std::min(uint32_t(mIter
.RemainingInSegment()), aCount
);
18913 // We've run out of data in the last segment.
18919 aWriter(this, aClosure
, mIter
.Data(), *_retval
, count
, &written
);
18920 if (NS_WARN_IF(NS_FAILED(rv
))) {
18921 // InputStreams do not propagate errors to caller.
18925 // Writer should write what we asked it to write.
18926 MOZ_ASSERT(written
== count
);
18931 if (NS_WARN_IF(!mData
.Advance(mIter
, count
))) {
18932 // InputStreams do not propagate errors to caller.
18941 ObjectStoreAddOrPutRequestOp::SCInputStream::IsNonBlocking(bool* _retval
) {
18946 ObjectStoreGetRequestOp::ObjectStoreGetRequestOp(
18947 SafeRefPtr
<TransactionBase
> aTransaction
, const int64_t aRequestId
,
18948 const RequestParams
& aParams
, bool aGetAll
)
18949 : NormalTransactionOp(std::move(aTransaction
), aRequestId
),
18950 mObjectStoreId(aGetAll
18951 ? aParams
.get_ObjectStoreGetAllParams().objectStoreId()
18952 : aParams
.get_ObjectStoreGetParams().objectStoreId()),
18953 mDatabase(Transaction().GetDatabasePtr()),
18955 aGetAll
? aParams
.get_ObjectStoreGetAllParams().optionalKeyRange()
18956 : Some(aParams
.get_ObjectStoreGetParams().keyRange())),
18957 mBackgroundParent(Transaction().GetBackgroundParent()),
18958 mPreprocessInfoCount(0),
18959 mLimit(aGetAll
? aParams
.get_ObjectStoreGetAllParams().limit() : 1),
18961 MOZ_ASSERT(aParams
.type() == RequestParams::TObjectStoreGetParams
||
18962 aParams
.type() == RequestParams::TObjectStoreGetAllParams
);
18963 MOZ_ASSERT(mObjectStoreId
);
18964 MOZ_ASSERT(mDatabase
);
18965 MOZ_ASSERT_IF(!aGetAll
, mOptionalKeyRange
.isSome());
18966 MOZ_ASSERT(mBackgroundParent
);
18969 template <typename T
>
18970 Result
<T
, nsresult
> ObjectStoreGetRequestOp::ConvertResponse(
18971 StructuredCloneReadInfoParent
&& aInfo
) {
18974 static_assert(std::is_same_v
<T
, SerializedStructuredCloneReadInfo
> ||
18975 std::is_same_v
<T
, PreprocessInfo
>);
18977 if constexpr (std::is_same_v
<T
, SerializedStructuredCloneReadInfo
>) {
18978 result
.data().data
= aInfo
.ReleaseData();
18979 result
.hasPreprocessInfo() = aInfo
.HasPreprocessInfo();
18982 QM_TRY_UNWRAP(result
.files(), SerializeStructuredCloneFiles(
18983 mDatabase
, aInfo
.Files(),
18984 std::is_same_v
<T
, PreprocessInfo
>));
18989 nsresult
ObjectStoreGetRequestOp::DoDatabaseWork(
18990 DatabaseConnection
* aConnection
) {
18991 MOZ_ASSERT(aConnection
);
18992 aConnection
->AssertIsOnConnectionThread();
18993 MOZ_ASSERT_IF(!mGetAll
, mOptionalKeyRange
.isSome());
18994 MOZ_ASSERT_IF(!mGetAll
, mLimit
== 1);
18996 AUTO_PROFILER_LABEL("ObjectStoreGetRequestOp::DoDatabaseWork", DOM
);
18998 const nsCString query
=
18999 "SELECT file_ids, data "
19000 "FROM object_data "
19001 "WHERE object_store_id = :"_ns
+
19002 kStmtParamNameObjectStoreId
+
19003 MaybeGetBindingClauseForKeyRange(mOptionalKeyRange
, kColumnNameKey
) +
19004 " ORDER BY key ASC"_ns
+
19005 (mLimit
? kOpenLimit
+ IntToCString(mLimit
) : EmptyCString());
19007 QM_TRY_INSPECT(const auto& stmt
, aConnection
->BorrowCachedStatement(query
));
19009 QM_TRY(MOZ_TO_RESULT(
19010 stmt
->BindInt64ByName(kStmtParamNameObjectStoreId
, mObjectStoreId
)));
19012 if (mOptionalKeyRange
.isSome()) {
19013 QM_TRY(MOZ_TO_RESULT(
19014 BindKeyRangeToStatement(mOptionalKeyRange
.ref(), &*stmt
)));
19017 QM_TRY(CollectWhileHasResult(
19018 *stmt
, [this](auto& stmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
19019 QM_TRY_UNWRAP(auto cloneInfo
,
19020 GetStructuredCloneReadInfoFromStatement(
19021 &stmt
, 1, 0, mDatabase
->GetFileManager()));
19023 if (cloneInfo
.HasPreprocessInfo()) {
19024 mPreprocessInfoCount
++;
19027 QM_TRY(OkIf(mResponse
.EmplaceBack(fallible
, std::move(cloneInfo
))),
19028 Err(NS_ERROR_OUT_OF_MEMORY
));
19033 MOZ_ASSERT_IF(!mGetAll
, mResponse
.Length() <= 1);
19038 bool ObjectStoreGetRequestOp::HasPreprocessInfo() {
19039 return mPreprocessInfoCount
> 0;
19042 Result
<PreprocessParams
, nsresult
>
19043 ObjectStoreGetRequestOp::GetPreprocessParams() {
19044 AssertIsOnOwningThread();
19045 MOZ_ASSERT(!mResponse
.IsEmpty());
19048 auto params
= ObjectStoreGetAllPreprocessParams();
19050 auto& preprocessInfos
= params
.preprocessInfos();
19052 !preprocessInfos
.SetCapacity(mPreprocessInfoCount
, fallible
))) {
19053 return Err(NS_ERROR_OUT_OF_MEMORY
);
19056 QM_TRY(TransformIfAbortOnErr(
19057 std::make_move_iterator(mResponse
.begin()),
19058 std::make_move_iterator(mResponse
.end()),
19059 MakeBackInserter(preprocessInfos
),
19060 [](const auto& info
) { return info
.HasPreprocessInfo(); },
19061 [&self
= *this](StructuredCloneReadInfoParent
&& info
) {
19062 return self
.ConvertResponse
<PreprocessInfo
>(std::move(info
));
19065 return PreprocessParams
{std::move(params
)};
19068 auto params
= ObjectStoreGetPreprocessParams();
19070 QM_TRY_UNWRAP(params
.preprocessInfo(),
19071 ConvertResponse
<PreprocessInfo
>(std::move(mResponse
[0])));
19073 return PreprocessParams
{std::move(params
)};
19076 void ObjectStoreGetRequestOp::GetResponse(RequestResponse
& aResponse
,
19077 size_t* aResponseSize
) {
19078 MOZ_ASSERT_IF(mLimit
, mResponse
.Length() <= mLimit
);
19081 aResponse
= ObjectStoreGetAllResponse();
19082 *aResponseSize
= 0;
19084 if (!mResponse
.IsEmpty()) {
19086 aResponse
.get_ObjectStoreGetAllResponse().cloneInfos(),
19087 TransformIntoNewArrayAbortOnErr(
19088 std::make_move_iterator(mResponse
.begin()),
19089 std::make_move_iterator(mResponse
.end()),
19090 [this, &aResponseSize
](StructuredCloneReadInfoParent
&& info
) {
19091 *aResponseSize
+= info
.Size();
19092 return ConvertResponse
<SerializedStructuredCloneReadInfo
>(
19096 QM_VOID
, [&aResponse
](const nsresult result
) { aResponse
= result
; });
19102 aResponse
= ObjectStoreGetResponse();
19103 *aResponseSize
= 0;
19105 if (!mResponse
.IsEmpty()) {
19106 SerializedStructuredCloneReadInfo
& serializedInfo
=
19107 aResponse
.get_ObjectStoreGetResponse().cloneInfo();
19109 *aResponseSize
+= mResponse
[0].Size();
19110 QM_TRY_UNWRAP(serializedInfo
,
19111 ConvertResponse
<SerializedStructuredCloneReadInfo
>(
19112 std::move(mResponse
[0])),
19114 [&aResponse
](const nsresult result
) { aResponse
= result
; });
19118 ObjectStoreGetKeyRequestOp::ObjectStoreGetKeyRequestOp(
19119 SafeRefPtr
<TransactionBase
> aTransaction
, const int64_t aRequestId
,
19120 const RequestParams
& aParams
, bool aGetAll
)
19121 : NormalTransactionOp(std::move(aTransaction
), aRequestId
),
19123 aGetAll
? aParams
.get_ObjectStoreGetAllKeysParams().objectStoreId()
19124 : aParams
.get_ObjectStoreGetKeyParams().objectStoreId()),
19126 aGetAll
? aParams
.get_ObjectStoreGetAllKeysParams().optionalKeyRange()
19127 : Some(aParams
.get_ObjectStoreGetKeyParams().keyRange())),
19128 mLimit(aGetAll
? aParams
.get_ObjectStoreGetAllKeysParams().limit() : 1),
19130 MOZ_ASSERT(aParams
.type() == RequestParams::TObjectStoreGetKeyParams
||
19131 aParams
.type() == RequestParams::TObjectStoreGetAllKeysParams
);
19132 MOZ_ASSERT(mObjectStoreId
);
19133 MOZ_ASSERT_IF(!aGetAll
, mOptionalKeyRange
.isSome());
19136 nsresult
ObjectStoreGetKeyRequestOp::DoDatabaseWork(
19137 DatabaseConnection
* aConnection
) {
19138 MOZ_ASSERT(aConnection
);
19139 aConnection
->AssertIsOnConnectionThread();
19141 AUTO_PROFILER_LABEL("ObjectStoreGetKeyRequestOp::DoDatabaseWork", DOM
);
19143 const nsCString query
=
19145 "FROM object_data "
19146 "WHERE object_store_id = :"_ns
+
19147 kStmtParamNameObjectStoreId
+
19148 MaybeGetBindingClauseForKeyRange(mOptionalKeyRange
, kColumnNameKey
) +
19149 " ORDER BY key ASC"_ns
+
19150 (mLimit
? " LIMIT "_ns
+ IntToCString(mLimit
) : EmptyCString());
19152 QM_TRY_INSPECT(const auto& stmt
, aConnection
->BorrowCachedStatement(query
));
19155 stmt
->BindInt64ByName(kStmtParamNameObjectStoreId
, mObjectStoreId
);
19156 if (NS_WARN_IF(NS_FAILED(rv
))) {
19160 if (mOptionalKeyRange
.isSome()) {
19161 rv
= BindKeyRangeToStatement(mOptionalKeyRange
.ref(), &*stmt
);
19162 if (NS_WARN_IF(NS_FAILED(rv
))) {
19167 QM_TRY(CollectWhileHasResult(
19168 *stmt
, [this](auto& stmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
19169 Key
* const key
= mResponse
.AppendElement(fallible
);
19170 QM_TRY(OkIf(key
), Err(NS_ERROR_OUT_OF_MEMORY
));
19171 QM_TRY(MOZ_TO_RESULT(key
->SetFromStatement(&stmt
, 0)));
19176 MOZ_ASSERT_IF(!mGetAll
, mResponse
.Length() <= 1);
19181 void ObjectStoreGetKeyRequestOp::GetResponse(RequestResponse
& aResponse
,
19182 size_t* aResponseSize
) {
19183 MOZ_ASSERT_IF(mLimit
, mResponse
.Length() <= mLimit
);
19186 aResponse
= ObjectStoreGetAllKeysResponse();
19187 *aResponseSize
= std::accumulate(mResponse
.begin(), mResponse
.end(), 0u,
19188 [](size_t old
, const auto& entry
) {
19189 return old
+ entry
.GetBuffer().Length();
19192 aResponse
.get_ObjectStoreGetAllKeysResponse().keys() = std::move(mResponse
);
19197 aResponse
= ObjectStoreGetKeyResponse();
19198 *aResponseSize
= 0;
19200 if (!mResponse
.IsEmpty()) {
19201 *aResponseSize
= mResponse
[0].GetBuffer().Length();
19202 aResponse
.get_ObjectStoreGetKeyResponse().key() = std::move(mResponse
[0]);
19206 ObjectStoreDeleteRequestOp::ObjectStoreDeleteRequestOp(
19207 SafeRefPtr
<TransactionBase
> aTransaction
, const int64_t aRequestId
,
19208 const ObjectStoreDeleteParams
& aParams
)
19209 : NormalTransactionOp(std::move(aTransaction
), aRequestId
),
19211 mObjectStoreMayHaveIndexes(false) {
19212 AssertIsOnBackgroundThread();
19214 SafeRefPtr
<FullObjectStoreMetadata
> metadata
=
19215 Transaction().GetMetadataForObjectStoreId(mParams
.objectStoreId());
19216 MOZ_ASSERT(metadata
);
19218 mObjectStoreMayHaveIndexes
= metadata
->HasLiveIndexes();
19221 nsresult
ObjectStoreDeleteRequestOp::DoDatabaseWork(
19222 DatabaseConnection
* aConnection
) {
19223 MOZ_ASSERT(aConnection
);
19224 aConnection
->AssertIsOnConnectionThread();
19225 AUTO_PROFILER_LABEL("ObjectStoreDeleteRequestOp::DoDatabaseWork", DOM
);
19227 DatabaseConnection::AutoSavepoint autoSave
;
19228 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
19229 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
19231 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
19235 QM_TRY_INSPECT(const bool& objectStoreHasIndexes
,
19236 ObjectStoreHasIndexes(*aConnection
, mParams
.objectStoreId(),
19237 mObjectStoreMayHaveIndexes
));
19239 if (objectStoreHasIndexes
) {
19240 QM_TRY(MOZ_TO_RESULT(DeleteObjectStoreDataTableRowsWithIndexes(
19241 aConnection
, mParams
.objectStoreId(), Some(mParams
.keyRange()))));
19243 const auto keyRangeClause
=
19244 GetBindingClauseForKeyRange(mParams
.keyRange(), kColumnNameKey
);
19246 QM_TRY(MOZ_TO_RESULT(aConnection
->ExecuteCachedStatement(
19247 "DELETE FROM object_data "
19248 "WHERE object_store_id = :"_ns
+
19249 kStmtParamNameObjectStoreId
+ keyRangeClause
+ ";"_ns
,
19250 [¶ms
= mParams
](
19251 mozIStorageStatement
& stmt
) -> mozilla::Result
<Ok
, nsresult
> {
19252 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByName(kStmtParamNameObjectStoreId
,
19253 params
.objectStoreId())));
19256 MOZ_TO_RESULT(BindKeyRangeToStatement(params
.keyRange(), &stmt
)));
19262 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
19267 ObjectStoreClearRequestOp::ObjectStoreClearRequestOp(
19268 SafeRefPtr
<TransactionBase
> aTransaction
, const int64_t aRequestId
,
19269 const ObjectStoreClearParams
& aParams
)
19270 : NormalTransactionOp(std::move(aTransaction
), aRequestId
),
19272 mObjectStoreMayHaveIndexes(false) {
19273 AssertIsOnBackgroundThread();
19275 SafeRefPtr
<FullObjectStoreMetadata
> metadata
=
19276 Transaction().GetMetadataForObjectStoreId(mParams
.objectStoreId());
19277 MOZ_ASSERT(metadata
);
19279 mObjectStoreMayHaveIndexes
= metadata
->HasLiveIndexes();
19282 nsresult
ObjectStoreClearRequestOp::DoDatabaseWork(
19283 DatabaseConnection
* aConnection
) {
19284 MOZ_ASSERT(aConnection
);
19285 aConnection
->AssertIsOnConnectionThread();
19287 AUTO_PROFILER_LABEL("ObjectStoreClearRequestOp::DoDatabaseWork", DOM
);
19289 DatabaseConnection::AutoSavepoint autoSave
;
19290 QM_TRY(MOZ_TO_RESULT(autoSave
.Start(Transaction()))
19291 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
19293 QM_PROPAGATE
, MakeAutoSavepointCleanupHandler(*aConnection
)
19297 QM_TRY_INSPECT(const bool& objectStoreHasIndexes
,
19298 ObjectStoreHasIndexes(*aConnection
, mParams
.objectStoreId(),
19299 mObjectStoreMayHaveIndexes
));
19301 // The parameter names are not used, parameters are bound by index only
19302 // locally in the same function.
19303 QM_TRY(MOZ_TO_RESULT(
19304 objectStoreHasIndexes
19305 ? DeleteObjectStoreDataTableRowsWithIndexes(
19306 aConnection
, mParams
.objectStoreId(), Nothing())
19307 : aConnection
->ExecuteCachedStatement(
19308 "DELETE FROM object_data "
19309 "WHERE object_store_id = :object_store_id;"_ns
,
19311 mParams
.objectStoreId()](mozIStorageStatement
& stmt
)
19312 -> mozilla::Result
<Ok
, nsresult
> {
19314 MOZ_TO_RESULT(stmt
.BindInt64ByIndex(0, objectStoreId
)));
19319 QM_TRY(MOZ_TO_RESULT(autoSave
.Commit()));
19324 nsresult
ObjectStoreCountRequestOp::DoDatabaseWork(
19325 DatabaseConnection
* aConnection
) {
19326 MOZ_ASSERT(aConnection
);
19327 aConnection
->AssertIsOnConnectionThread();
19329 AUTO_PROFILER_LABEL("ObjectStoreCountRequestOp::DoDatabaseWork", DOM
);
19331 const auto keyRangeClause
= MaybeGetBindingClauseForKeyRange(
19332 mParams
.optionalKeyRange(), kColumnNameKey
);
19335 const auto& maybeStmt
,
19336 aConnection
->BorrowAndExecuteSingleStepStatement(
19338 "FROM object_data "
19339 "WHERE object_store_id = :"_ns
+
19340 kStmtParamNameObjectStoreId
+ keyRangeClause
,
19341 [¶ms
= mParams
](auto& stmt
) -> mozilla::Result
<Ok
, nsresult
> {
19342 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByName(
19343 kStmtParamNameObjectStoreId
, params
.objectStoreId())));
19345 if (params
.optionalKeyRange().isSome()) {
19346 QM_TRY(MOZ_TO_RESULT(BindKeyRangeToStatement(
19347 params
.optionalKeyRange().ref(), &stmt
)));
19353 QM_TRY(OkIf(maybeStmt
.isSome()), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
,
19355 // XXX Why do we have an assertion here, but not at most other
19356 // places using IDB_REPORT_INTERNAL_ERR(_LAMBDA)?
19357 MOZ_ASSERT(false, "This should never be possible!");
19358 IDB_REPORT_INTERNAL_ERR();
19361 const auto& stmt
= *maybeStmt
;
19363 const int64_t count
= stmt
->AsInt64(0);
19364 QM_TRY(OkIf(count
>= 0), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
, [](const auto) {
19365 // XXX Why do we have an assertion here, but not at most other places using
19366 // IDB_REPORT_INTERNAL_ERR(_LAMBDA)?
19367 MOZ_ASSERT(false, "This should never be possible!");
19368 IDB_REPORT_INTERNAL_ERR();
19371 mResponse
.count() = count
;
19377 SafeRefPtr
<FullIndexMetadata
> IndexRequestOpBase::IndexMetadataForParams(
19378 const TransactionBase
& aTransaction
, const RequestParams
& aParams
) {
19379 AssertIsOnBackgroundThread();
19380 MOZ_ASSERT(aParams
.type() == RequestParams::TIndexGetParams
||
19381 aParams
.type() == RequestParams::TIndexGetKeyParams
||
19382 aParams
.type() == RequestParams::TIndexGetAllParams
||
19383 aParams
.type() == RequestParams::TIndexGetAllKeysParams
||
19384 aParams
.type() == RequestParams::TIndexCountParams
);
19386 IndexOrObjectStoreId objectStoreId
;
19387 IndexOrObjectStoreId indexId
;
19389 switch (aParams
.type()) {
19390 case RequestParams::TIndexGetParams
: {
19391 const IndexGetParams
& params
= aParams
.get_IndexGetParams();
19392 objectStoreId
= params
.objectStoreId();
19393 indexId
= params
.indexId();
19397 case RequestParams::TIndexGetKeyParams
: {
19398 const IndexGetKeyParams
& params
= aParams
.get_IndexGetKeyParams();
19399 objectStoreId
= params
.objectStoreId();
19400 indexId
= params
.indexId();
19404 case RequestParams::TIndexGetAllParams
: {
19405 const IndexGetAllParams
& params
= aParams
.get_IndexGetAllParams();
19406 objectStoreId
= params
.objectStoreId();
19407 indexId
= params
.indexId();
19411 case RequestParams::TIndexGetAllKeysParams
: {
19412 const IndexGetAllKeysParams
& params
= aParams
.get_IndexGetAllKeysParams();
19413 objectStoreId
= params
.objectStoreId();
19414 indexId
= params
.indexId();
19418 case RequestParams::TIndexCountParams
: {
19419 const IndexCountParams
& params
= aParams
.get_IndexCountParams();
19420 objectStoreId
= params
.objectStoreId();
19421 indexId
= params
.indexId();
19426 MOZ_CRASH("Should never get here!");
19429 const SafeRefPtr
<FullObjectStoreMetadata
> objectStoreMetadata
=
19430 aTransaction
.GetMetadataForObjectStoreId(objectStoreId
);
19431 MOZ_ASSERT(objectStoreMetadata
);
19433 SafeRefPtr
<FullIndexMetadata
> indexMetadata
=
19434 aTransaction
.GetMetadataForIndexId(*objectStoreMetadata
, indexId
);
19435 MOZ_ASSERT(indexMetadata
);
19437 return indexMetadata
;
19440 IndexGetRequestOp::IndexGetRequestOp(SafeRefPtr
<TransactionBase
> aTransaction
,
19441 const int64_t aRequestId
,
19442 const RequestParams
& aParams
, bool aGetAll
)
19443 : IndexRequestOpBase(std::move(aTransaction
), aRequestId
, aParams
),
19444 mDatabase(Transaction().GetDatabasePtr()),
19445 mOptionalKeyRange(aGetAll
19446 ? aParams
.get_IndexGetAllParams().optionalKeyRange()
19447 : Some(aParams
.get_IndexGetParams().keyRange())),
19448 mBackgroundParent(Transaction().GetBackgroundParent()),
19449 mLimit(aGetAll
? aParams
.get_IndexGetAllParams().limit() : 1),
19451 MOZ_ASSERT(aParams
.type() == RequestParams::TIndexGetParams
||
19452 aParams
.type() == RequestParams::TIndexGetAllParams
);
19453 MOZ_ASSERT(mDatabase
);
19454 MOZ_ASSERT_IF(!aGetAll
, mOptionalKeyRange
.isSome());
19455 MOZ_ASSERT(mBackgroundParent
);
19458 nsresult
IndexGetRequestOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
19459 MOZ_ASSERT(aConnection
);
19460 aConnection
->AssertIsOnConnectionThread();
19461 MOZ_ASSERT_IF(!mGetAll
, mOptionalKeyRange
.isSome());
19462 MOZ_ASSERT_IF(!mGetAll
, mLimit
== 1);
19464 AUTO_PROFILER_LABEL("IndexGetRequestOp::DoDatabaseWork", DOM
);
19466 const auto indexTable
= mMetadata
->mCommonMetadata
.unique()
19467 ? "unique_index_data "_ns
19468 : "index_data "_ns
;
19472 aConnection
->BorrowCachedStatement(
19473 "SELECT file_ids, data "
19474 "FROM object_data "
19478 "ON object_data.object_store_id = "
19479 "index_table.object_store_id "
19480 "AND object_data.key = "
19481 "index_table.object_data_key "
19482 "WHERE index_id = :"_ns
+
19483 kStmtParamNameIndexId
+
19484 MaybeGetBindingClauseForKeyRange(mOptionalKeyRange
,
19485 kColumnNameValue
) +
19486 (mLimit
? kOpenLimit
+ IntToCString(mLimit
) : EmptyCString())));
19488 QM_TRY(MOZ_TO_RESULT(stmt
->BindInt64ByName(kStmtParamNameIndexId
,
19489 mMetadata
->mCommonMetadata
.id())));
19491 if (mOptionalKeyRange
.isSome()) {
19492 QM_TRY(MOZ_TO_RESULT(
19493 BindKeyRangeToStatement(mOptionalKeyRange
.ref(), &*stmt
)));
19496 QM_TRY(CollectWhileHasResult(
19497 *stmt
, [this](auto& stmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
19498 QM_TRY_UNWRAP(auto cloneInfo
,
19499 GetStructuredCloneReadInfoFromStatement(
19500 &stmt
, 1, 0, mDatabase
->GetFileManager()));
19502 if (cloneInfo
.HasPreprocessInfo()) {
19503 IDB_WARNING("Preprocessing for indexes not yet implemented!");
19504 return Err(NS_ERROR_NOT_IMPLEMENTED
);
19507 QM_TRY(OkIf(mResponse
.EmplaceBack(fallible
, std::move(cloneInfo
))),
19508 Err(NS_ERROR_OUT_OF_MEMORY
));
19513 MOZ_ASSERT_IF(!mGetAll
, mResponse
.Length() <= 1);
19518 // XXX This is more or less a duplicate of ObjectStoreGetRequestOp::GetResponse
19519 void IndexGetRequestOp::GetResponse(RequestResponse
& aResponse
,
19520 size_t* aResponseSize
) {
19521 MOZ_ASSERT_IF(!mGetAll
, mResponse
.Length() <= 1);
19523 auto convertResponse
= [this](StructuredCloneReadInfoParent
&& info
)
19524 -> mozilla::Result
<SerializedStructuredCloneReadInfo
, nsresult
> {
19525 SerializedStructuredCloneReadInfo result
;
19527 result
.data().data
= info
.ReleaseData();
19529 QM_TRY_UNWRAP(result
.files(), SerializeStructuredCloneFiles(
19530 mDatabase
, info
.Files(), false));
19536 aResponse
= IndexGetAllResponse();
19537 *aResponseSize
= 0;
19539 if (!mResponse
.IsEmpty()) {
19541 aResponse
.get_IndexGetAllResponse().cloneInfos(),
19542 TransformIntoNewArrayAbortOnErr(
19543 std::make_move_iterator(mResponse
.begin()),
19544 std::make_move_iterator(mResponse
.end()),
19546 &aResponseSize
](StructuredCloneReadInfoParent
&& info
) {
19547 *aResponseSize
+= info
.Size();
19548 return convertResponse(std::move(info
));
19551 QM_VOID
, [&aResponse
](const nsresult result
) { aResponse
= result
; });
19557 aResponse
= IndexGetResponse();
19558 *aResponseSize
= 0;
19560 if (!mResponse
.IsEmpty()) {
19561 SerializedStructuredCloneReadInfo
& serializedInfo
=
19562 aResponse
.get_IndexGetResponse().cloneInfo();
19564 *aResponseSize
+= mResponse
[0].Size();
19565 QM_TRY_UNWRAP(serializedInfo
, convertResponse(std::move(mResponse
[0])),
19567 [&aResponse
](const nsresult result
) { aResponse
= result
; });
19571 IndexGetKeyRequestOp::IndexGetKeyRequestOp(
19572 SafeRefPtr
<TransactionBase
> aTransaction
, const int64_t aRequestId
,
19573 const RequestParams
& aParams
, bool aGetAll
)
19574 : IndexRequestOpBase(std::move(aTransaction
), aRequestId
, aParams
),
19576 aGetAll
? aParams
.get_IndexGetAllKeysParams().optionalKeyRange()
19577 : Some(aParams
.get_IndexGetKeyParams().keyRange())),
19578 mLimit(aGetAll
? aParams
.get_IndexGetAllKeysParams().limit() : 1),
19580 MOZ_ASSERT(aParams
.type() == RequestParams::TIndexGetKeyParams
||
19581 aParams
.type() == RequestParams::TIndexGetAllKeysParams
);
19582 MOZ_ASSERT_IF(!aGetAll
, mOptionalKeyRange
.isSome());
19585 nsresult
IndexGetKeyRequestOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
19586 MOZ_ASSERT(aConnection
);
19587 aConnection
->AssertIsOnConnectionThread();
19588 MOZ_ASSERT_IF(!mGetAll
, mOptionalKeyRange
.isSome());
19589 MOZ_ASSERT_IF(!mGetAll
, mLimit
== 1);
19591 AUTO_PROFILER_LABEL("IndexGetKeyRequestOp::DoDatabaseWork", DOM
);
19593 const bool hasKeyRange
= mOptionalKeyRange
.isSome();
19595 const auto indexTable
= mMetadata
->mCommonMetadata
.unique()
19596 ? "unique_index_data "_ns
19597 : "index_data "_ns
;
19599 const nsCString query
=
19600 "SELECT object_data_key "
19602 indexTable
+ "WHERE index_id = :"_ns
+ kStmtParamNameIndexId
+
19603 MaybeGetBindingClauseForKeyRange(mOptionalKeyRange
, kColumnNameValue
) +
19604 (mLimit
? kOpenLimit
+ IntToCString(mLimit
) : EmptyCString());
19606 QM_TRY_INSPECT(const auto& stmt
, aConnection
->BorrowCachedStatement(query
));
19608 QM_TRY(MOZ_TO_RESULT(stmt
->BindInt64ByName(kStmtParamNameIndexId
,
19609 mMetadata
->mCommonMetadata
.id())));
19612 QM_TRY(MOZ_TO_RESULT(
19613 BindKeyRangeToStatement(mOptionalKeyRange
.ref(), &*stmt
)));
19616 QM_TRY(CollectWhileHasResult(
19617 *stmt
, [this](auto& stmt
) mutable -> mozilla::Result
<Ok
, nsresult
> {
19618 Key
* const key
= mResponse
.AppendElement(fallible
);
19619 QM_TRY(OkIf(key
), Err(NS_ERROR_OUT_OF_MEMORY
));
19620 QM_TRY(MOZ_TO_RESULT(key
->SetFromStatement(&stmt
, 0)));
19625 MOZ_ASSERT_IF(!mGetAll
, mResponse
.Length() <= 1);
19630 void IndexGetKeyRequestOp::GetResponse(RequestResponse
& aResponse
,
19631 size_t* aResponseSize
) {
19632 MOZ_ASSERT_IF(!mGetAll
, mResponse
.Length() <= 1);
19635 aResponse
= IndexGetAllKeysResponse();
19636 *aResponseSize
= std::accumulate(mResponse
.begin(), mResponse
.end(), 0u,
19637 [](size_t old
, const auto& entry
) {
19638 return old
+ entry
.GetBuffer().Length();
19641 aResponse
.get_IndexGetAllKeysResponse().keys() = std::move(mResponse
);
19646 aResponse
= IndexGetKeyResponse();
19647 *aResponseSize
= 0;
19649 if (!mResponse
.IsEmpty()) {
19650 *aResponseSize
= mResponse
[0].GetBuffer().Length();
19651 aResponse
.get_IndexGetKeyResponse().key() = std::move(mResponse
[0]);
19655 nsresult
IndexCountRequestOp::DoDatabaseWork(DatabaseConnection
* aConnection
) {
19656 MOZ_ASSERT(aConnection
);
19657 aConnection
->AssertIsOnConnectionThread();
19659 AUTO_PROFILER_LABEL("IndexCountRequestOp::DoDatabaseWork", DOM
);
19661 const auto indexTable
= mMetadata
->mCommonMetadata
.unique()
19662 ? "unique_index_data "_ns
19663 : "index_data "_ns
;
19665 const auto keyRangeClause
= MaybeGetBindingClauseForKeyRange(
19666 mParams
.optionalKeyRange(), kColumnNameValue
);
19669 const auto& maybeStmt
,
19670 aConnection
->BorrowAndExecuteSingleStepStatement(
19673 indexTable
+ "WHERE index_id = :"_ns
+ kStmtParamNameIndexId
+
19675 [&self
= *this](auto& stmt
) -> mozilla::Result
<Ok
, nsresult
> {
19676 QM_TRY(MOZ_TO_RESULT(stmt
.BindInt64ByName(
19677 kStmtParamNameIndexId
, self
.mMetadata
->mCommonMetadata
.id())));
19679 if (self
.mParams
.optionalKeyRange().isSome()) {
19680 QM_TRY(MOZ_TO_RESULT(BindKeyRangeToStatement(
19681 self
.mParams
.optionalKeyRange().ref(), &stmt
)));
19687 QM_TRY(OkIf(maybeStmt
.isSome()), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
,
19689 // XXX Why do we have an assertion here, but not at most other
19690 // places using IDB_REPORT_INTERNAL_ERR(_LAMBDA)?
19691 MOZ_ASSERT(false, "This should never be possible!");
19692 IDB_REPORT_INTERNAL_ERR();
19695 const auto& stmt
= *maybeStmt
;
19697 const int64_t count
= stmt
->AsInt64(0);
19698 QM_TRY(OkIf(count
>= 0), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
, [](const auto) {
19699 // XXX Why do we have an assertion here, but not at most other places using
19700 // IDB_REPORT_INTERNAL_ERR(_LAMBDA)?
19701 MOZ_ASSERT(false, "This should never be possible!");
19702 IDB_REPORT_INTERNAL_ERR();
19705 mResponse
.count() = count
;
19710 template <IDBCursorType CursorType
>
19711 bool Cursor
<CursorType
>::CursorOpBase::SendFailureResult(nsresult aResultCode
) {
19712 AssertIsOnOwningThread();
19713 MOZ_ASSERT(NS_FAILED(aResultCode
));
19714 MOZ_ASSERT(mCursor
);
19715 MOZ_ASSERT(mCursor
->mCurrentlyRunningOp
== this);
19716 MOZ_ASSERT(!mResponseSent
);
19718 if (!IsActorDestroyed()) {
19719 mResponse
= ClampResultCode(aResultCode
);
19721 // This is an expected race when the transaction is invalidated after
19722 // data is retrieved from database.
19724 // TODO: There seem to be other cases when mFiles is non-empty here, which
19725 // have been present before adding cursor preloading, but with cursor
19726 // preloading they have become more frequent (also during startup). One
19727 // possible cause with cursor preloading is to be addressed by Bug 1597191.
19728 NS_WARNING_ASSERTION(
19729 !mFiles
.IsEmpty() && !Transaction().IsInvalidated(),
19730 "Expected empty mFiles when transaction has not been invalidated");
19732 // SendResponseInternal will assert when mResponse.type() is
19733 // CursorResponse::Tnsresult and mFiles is non-empty, so we clear mFiles
19737 mCursor
->SendResponseInternal(mResponse
, mFiles
);
19741 mResponseSent
= true;
19746 template <IDBCursorType CursorType
>
19747 void Cursor
<CursorType
>::CursorOpBase::Cleanup() {
19748 AssertIsOnOwningThread();
19749 MOZ_ASSERT(mCursor
);
19750 MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent
);
19755 // A bit hacky but the CursorOp request is not generated in response to a
19756 // child request like most other database operations. Do this to make our
19757 // assertions happy.
19758 NoteActorDestroyed();
19761 TransactionDatabaseOperationBase::Cleanup();
19764 template <IDBCursorType CursorType
>
19765 ResponseSizeOrError
19766 CursorOpBaseHelperBase
<CursorType
>::PopulateResponseFromStatement(
19767 mozIStorageStatement
* const aStmt
, const bool aInitializeResponse
,
19768 Key
* const aOptOutSortKey
) {
19769 mOp
.Transaction().AssertIsOnConnectionThread();
19770 MOZ_ASSERT_IF(aInitializeResponse
,
19771 mOp
.mResponse
.type() == CursorResponse::T__None
);
19772 MOZ_ASSERT_IF(!aInitializeResponse
,
19773 mOp
.mResponse
.type() != CursorResponse::T__None
);
19775 mOp
.mFiles
.IsEmpty() &&
19776 (mOp
.mResponse
.type() ==
19777 CursorResponse::TArrayOfObjectStoreCursorResponse
||
19778 mOp
.mResponse
.type() == CursorResponse::TArrayOfIndexCursorResponse
),
19779 aInitializeResponse
);
19781 auto populateResponseHelper
= PopulateResponseHelper
<CursorType
>{mOp
};
19782 auto previousKey
= aOptOutSortKey
? std::move(*aOptOutSortKey
) : Key
{};
19784 QM_TRY(MOZ_TO_RESULT(populateResponseHelper
.GetKeys(aStmt
, aOptOutSortKey
)));
19786 // aOptOutSortKey must be set iff the cursor is a unique cursor. For unique
19787 // cursors, we need to skip records with the same key. The SQL queries
19788 // currently do not filter these out.
19789 if (aOptOutSortKey
&& !previousKey
.IsUnset() &&
19790 previousKey
== *aOptOutSortKey
) {
19794 QM_TRY(MOZ_TO_RESULT(
19795 populateResponseHelper
.MaybeGetCloneInfo(aStmt
, GetCursor())));
19797 // CAUTION: It is important that only the part of the function above this
19798 // comment may fail, and modifications to the data structure (in particular
19799 // mResponse and mFiles) may only be made below. This is necessary to allow to
19800 // discard entries that were attempted to be preloaded without causing an
19801 // inconsistent state.
19803 if (aInitializeResponse
) {
19804 mOp
.mResponse
= std::remove_reference_t
<
19805 decltype(populateResponseHelper
.GetTypedResponse(&mOp
.mResponse
))>();
19808 auto& responses
= populateResponseHelper
.GetTypedResponse(&mOp
.mResponse
);
19809 auto& response
= *responses
.AppendElement();
19811 populateResponseHelper
.FillKeys(response
);
19812 if constexpr (!CursorTypeTraits
<CursorType
>::IsKeyOnlyCursor
) {
19813 populateResponseHelper
.MaybeFillCloneInfo(response
, &mOp
.mFiles
);
19816 return populateResponseHelper
.GetKeySize(response
) +
19817 populateResponseHelper
.MaybeGetCloneInfoSize(response
);
19820 template <IDBCursorType CursorType
>
19821 void CursorOpBaseHelperBase
<CursorType
>::PopulateExtraResponses(
19822 mozIStorageStatement
* const aStmt
, const uint32_t aMaxExtraCount
,
19823 const size_t aInitialResponseSize
, const nsACString
& aOperation
,
19824 Key
* const aOptPreviousSortKey
) {
19825 mOp
.AssertIsOnConnectionThread();
19827 const auto extraCount
= [&]() -> uint32_t {
19828 auto accumulatedResponseSize
= aInitialResponseSize
;
19829 uint32_t extraCount
= 0;
19833 nsresult rv
= aStmt
->ExecuteStep(&hasResult
);
19834 if (NS_WARN_IF(NS_FAILED(rv
))) {
19835 // In case of a failure on one step, do not attempt to execute further
19836 // steps, but use the results already populated.
19845 // PopulateResponseFromStatement does not modify the data in case of
19846 // failure, so we can just use the results already populated, and discard
19847 // any remaining entries, and signal overall success. Probably, future
19848 // attempts to access the same entry will fail as well, but it might never
19849 // be accessed by the application.
19851 const auto& responseSize
,
19852 PopulateResponseFromStatement(aStmt
, false, aOptPreviousSortKey
),
19853 extraCount
, [](const auto&) {
19854 // TODO: Maybe disable preloading for this cursor? The problem will
19855 // probably reoccur on the next attempt, and disabling preloading
19856 // will reduce latency. However, if some problematic entry will be
19857 // skipped over, after that it might be fine again. To judge this,
19858 // the causes for such failures would need to be analyzed more
19859 // thoroughly. Since this seems to be rare, maybe no further action
19860 // is necessary at all.
19863 // Check accumulated size of individual responses and maybe break early.
19864 accumulatedResponseSize
+= responseSize
;
19865 if (accumulatedResponseSize
> IPC::Channel::kMaximumMessageSize
/ 2) {
19866 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
19867 "PRELOAD: %s: Dropping entries because maximum message size is "
19868 "exceeded: %" PRIu32
"/%zu bytes",
19869 "%.0s Dropping too large (%" PRIu32
"/%zu)",
19870 IDB_LOG_ID_STRING(mOp
.mBackgroundChildLoggingId
),
19871 mOp
.mTransactionLoggingSerialNumber
, mOp
.mLoggingSerialNumber
,
19872 PromiseFlatCString(aOperation
).get(), extraCount
,
19873 accumulatedResponseSize
);
19878 // TODO: Do not count entries skipped for unique cursors.
19885 IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
19886 "PRELOAD: %s: Number of extra results populated: %" PRIu32
"/%" PRIu32
,
19887 "%.0s Populated (%" PRIu32
"/%" PRIu32
")",
19888 IDB_LOG_ID_STRING(mOp
.mBackgroundChildLoggingId
),
19889 mOp
.mTransactionLoggingSerialNumber
, mOp
.mLoggingSerialNumber
,
19890 PromiseFlatCString(aOperation
).get(), extraCount
, aMaxExtraCount
);
19893 template <IDBCursorType CursorType
>
19894 void Cursor
<CursorType
>::SetOptionalKeyRange(
19895 const Maybe
<SerializedKeyRange
>& aOptionalKeyRange
, bool* const aOpen
) {
19898 Key localeAwareRangeBound
;
19900 if (aOptionalKeyRange
.isSome()) {
19901 const SerializedKeyRange
& range
= aOptionalKeyRange
.ref();
19903 const bool lowerBound
= !IsIncreasingOrder(mDirection
);
19905 !range
.isOnly() && (lowerBound
? range
.lowerOpen() : range
.upperOpen());
19907 const auto& bound
=
19908 (range
.isOnly() || lowerBound
) ? range
.lower() : range
.upper();
19909 if constexpr (IsIndexCursor
) {
19910 if (this->IsLocaleAware()) {
19911 // XXX Don't we need to propagate the error?
19912 QM_TRY_UNWRAP(localeAwareRangeBound
,
19913 bound
.ToLocaleAwareKey(this->mLocale
), QM_VOID
);
19915 localeAwareRangeBound
= bound
;
19918 localeAwareRangeBound
= bound
;
19924 this->mLocaleAwareRangeBound
.init(std::move(localeAwareRangeBound
));
19927 template <IDBCursorType CursorType
>
19928 void ObjectStoreOpenOpHelper
<CursorType
>::PrepareKeyConditionClauses(
19929 const nsACString
& aDirectionClause
, const nsACString
& aQueryStart
) {
19930 const bool isIncreasingOrder
= IsIncreasingOrder(GetCursor().mDirection
);
19932 nsAutoCString keyRangeClause
;
19933 nsAutoCString continueToKeyRangeClause
;
19934 AppendConditionClause(kStmtParamNameKey
, kStmtParamNameCurrentKey
,
19935 !isIncreasingOrder
, false, keyRangeClause
);
19936 AppendConditionClause(kStmtParamNameKey
, kStmtParamNameCurrentKey
,
19937 !isIncreasingOrder
, true, continueToKeyRangeClause
);
19941 GetCursor().SetOptionalKeyRange(GetOptionalKeyRange(), &open
);
19943 if (GetOptionalKeyRange().isSome() &&
19944 !GetCursor().mLocaleAwareRangeBound
->IsUnset()) {
19945 AppendConditionClause(kStmtParamNameKey
, kStmtParamNameRangeBound
,
19946 isIncreasingOrder
, !open
, keyRangeClause
);
19947 AppendConditionClause(kStmtParamNameKey
, kStmtParamNameRangeBound
,
19948 isIncreasingOrder
, !open
, continueToKeyRangeClause
);
19952 const nsAutoCString suffix
=
19953 aDirectionClause
+ kOpenLimit
+ ":"_ns
+ kStmtParamNameLimit
;
19955 GetCursor().mContinueQueries
.init(
19956 aQueryStart
+ keyRangeClause
+ suffix
,
19957 aQueryStart
+ continueToKeyRangeClause
+ suffix
);
19960 template <IDBCursorType CursorType
>
19961 void IndexOpenOpHelper
<CursorType
>::PrepareIndexKeyConditionClause(
19962 const nsACString
& aDirectionClause
,
19963 const nsLiteralCString
& aObjectDataKeyPrefix
, nsAutoCString aQueryStart
) {
19964 const bool isIncreasingOrder
= IsIncreasingOrder(GetCursor().mDirection
);
19968 GetCursor().SetOptionalKeyRange(GetOptionalKeyRange(), &open
);
19969 if (GetOptionalKeyRange().isSome() &&
19970 !GetCursor().mLocaleAwareRangeBound
->IsUnset()) {
19971 AppendConditionClause(kColumnNameAliasSortKey
, kStmtParamNameRangeBound
,
19972 isIncreasingOrder
, !open
, aQueryStart
);
19976 nsCString continueQuery
, continueToQuery
, continuePrimaryKeyQuery
;
19979 aQueryStart
+ " AND "_ns
+
19980 GetSortKeyClause(isIncreasingOrder
? ComparisonOperator::GreaterOrEquals
19981 : ComparisonOperator::LessOrEquals
,
19982 kStmtParamNameCurrentKey
);
19984 switch (GetCursor().mDirection
) {
19985 case IDBCursorDirection::Next
:
19986 case IDBCursorDirection::Prev
:
19988 aQueryStart
+ " AND "_ns
+
19989 GetSortKeyClause(isIncreasingOrder
19990 ? ComparisonOperator::GreaterOrEquals
19991 : ComparisonOperator::LessOrEquals
,
19992 kStmtParamNameCurrentKey
) +
19994 GetSortKeyClause(isIncreasingOrder
? ComparisonOperator::GreaterThan
19995 : ComparisonOperator::LessThan
,
19996 kStmtParamNameCurrentKey
) +
19998 GetKeyClause(aObjectDataKeyPrefix
+ "object_data_key"_ns
,
19999 isIncreasingOrder
? ComparisonOperator::GreaterThan
20000 : ComparisonOperator::LessThan
,
20001 kStmtParamNameObjectStorePosition
) +
20004 continuePrimaryKeyQuery
=
20008 GetSortKeyClause(ComparisonOperator::Equals
,
20009 kStmtParamNameCurrentKey
) +
20011 GetKeyClause(aObjectDataKeyPrefix
+ "object_data_key"_ns
,
20012 isIncreasingOrder
? ComparisonOperator::GreaterOrEquals
20013 : ComparisonOperator::LessOrEquals
,
20014 kStmtParamNameObjectStorePosition
) +
20016 GetSortKeyClause(isIncreasingOrder
? ComparisonOperator::GreaterThan
20017 : ComparisonOperator::LessThan
,
20018 kStmtParamNameCurrentKey
) +
20022 case IDBCursorDirection::Nextunique
:
20023 case IDBCursorDirection::Prevunique
:
20025 aQueryStart
+ " AND "_ns
+
20026 GetSortKeyClause(isIncreasingOrder
? ComparisonOperator::GreaterThan
20027 : ComparisonOperator::LessThan
,
20028 kStmtParamNameCurrentKey
);
20032 MOZ_CRASH("Should never get here!");
20035 const nsAutoCString suffix
=
20036 aDirectionClause
+ kOpenLimit
+ ":"_ns
+ kStmtParamNameLimit
;
20037 continueQuery
+= suffix
;
20038 continueToQuery
+= suffix
;
20039 if (!continuePrimaryKeyQuery
.IsEmpty()) {
20040 continuePrimaryKeyQuery
+= suffix
;
20043 GetCursor().mContinueQueries
.init(std::move(continueQuery
),
20044 std::move(continueToQuery
),
20045 std::move(continuePrimaryKeyQuery
));
20048 template <IDBCursorType CursorType
>
20049 nsresult CommonOpenOpHelper
<CursorType
>::ProcessStatementSteps(
20050 mozIStorageStatement
* const aStmt
) {
20051 QM_TRY_INSPECT(const bool& hasResult
,
20052 MOZ_TO_RESULT_INVOKE_MEMBER(aStmt
, ExecuteStep
));
20055 SetResponse(void_t
{});
20060 auto* optPreviousKey
=
20061 IsUnique(GetCursor().mDirection
) ? &previousKey
: nullptr;
20063 QM_TRY_INSPECT(const auto& responseSize
,
20064 PopulateResponseFromStatement(aStmt
, true, optPreviousKey
));
20066 // The degree to which extra responses on OpenOp can actually be used depends
20067 // on the parameters of subsequent ContinueOp operations, see also comment in
20068 // ContinueOp::DoDatabaseWork.
20070 // TODO: We should somehow evaluate the effects of this. Maybe use a smaller
20071 // extra count than for ContinueOp?
20072 PopulateExtraResponses(aStmt
, GetCursor().mMaxExtraCount
, responseSize
,
20073 "OpenOp"_ns
, optPreviousKey
);
20078 nsresult OpenOpHelper
<IDBCursorType::ObjectStore
>::DoDatabaseWork(
20079 DatabaseConnection
* aConnection
) {
20080 MOZ_ASSERT(aConnection
);
20081 aConnection
->AssertIsOnConnectionThread();
20082 MOZ_ASSERT(GetCursor().mObjectStoreId
);
20084 AUTO_PROFILER_LABEL("Cursor::OpenOp::DoObjectStoreDatabaseWork", DOM
);
20086 const bool usingKeyRange
= GetOptionalKeyRange().isSome();
20088 const nsCString queryStart
= "SELECT "_ns
+ kColumnNameKey
+
20089 ", file_ids, data "
20090 "FROM object_data "
20091 "WHERE object_store_id = :"_ns
+
20094 const auto keyRangeClause
=
20095 DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
20096 GetOptionalKeyRange(), kColumnNameKey
);
20098 const auto& directionClause
= MakeDirectionClause(GetCursor().mDirection
);
20100 // Note: Changing the number or order of SELECT columns in the query will
20101 // require changes to CursorOpBase::PopulateResponseFromStatement.
20102 const nsCString firstQuery
= queryStart
+ keyRangeClause
+ directionClause
+
20104 IntToCString(1 + GetCursor().mMaxExtraCount
);
20106 QM_TRY_INSPECT(const auto& stmt
,
20107 aConnection
->BorrowCachedStatement(firstQuery
));
20109 QM_TRY(MOZ_TO_RESULT(
20110 stmt
->BindInt64ByName(kStmtParamNameId
, GetCursor().mObjectStoreId
)));
20112 if (usingKeyRange
) {
20113 QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
20114 GetOptionalKeyRange().ref(), &*stmt
)));
20117 // Now we need to make the query for ContinueOp.
20118 PrepareKeyConditionClauses(directionClause
, queryStart
);
20120 return ProcessStatementSteps(&*stmt
);
20123 nsresult OpenOpHelper
<IDBCursorType::ObjectStoreKey
>::DoDatabaseWork(
20124 DatabaseConnection
* aConnection
) {
20125 MOZ_ASSERT(aConnection
);
20126 aConnection
->AssertIsOnConnectionThread();
20127 MOZ_ASSERT(GetCursor().mObjectStoreId
);
20129 AUTO_PROFILER_LABEL("Cursor::OpenOp::DoObjectStoreKeyDatabaseWork", DOM
);
20131 const bool usingKeyRange
= GetOptionalKeyRange().isSome();
20133 const nsCString queryStart
= "SELECT "_ns
+ kColumnNameKey
+
20134 " FROM object_data "
20135 "WHERE object_store_id = :"_ns
+
20138 const auto keyRangeClause
=
20139 DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
20140 GetOptionalKeyRange(), kColumnNameKey
);
20142 const auto& directionClause
= MakeDirectionClause(GetCursor().mDirection
);
20144 // Note: Changing the number or order of SELECT columns in the query will
20145 // require changes to CursorOpBase::PopulateResponseFromStatement.
20146 const nsCString firstQuery
=
20147 queryStart
+ keyRangeClause
+ directionClause
+ kOpenLimit
+ "1"_ns
;
20149 QM_TRY_INSPECT(const auto& stmt
,
20150 aConnection
->BorrowCachedStatement(firstQuery
));
20152 QM_TRY(MOZ_TO_RESULT(
20153 stmt
->BindInt64ByName(kStmtParamNameId
, GetCursor().mObjectStoreId
)));
20155 if (usingKeyRange
) {
20156 QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
20157 GetOptionalKeyRange().ref(), &*stmt
)));
20160 // Now we need to make the query to get the next match.
20161 PrepareKeyConditionClauses(directionClause
, queryStart
);
20163 return ProcessStatementSteps(&*stmt
);
20166 nsresult OpenOpHelper
<IDBCursorType::Index
>::DoDatabaseWork(
20167 DatabaseConnection
* aConnection
) {
20168 MOZ_ASSERT(aConnection
);
20169 aConnection
->AssertIsOnConnectionThread();
20170 MOZ_ASSERT(GetCursor().mObjectStoreId
);
20171 MOZ_ASSERT(GetCursor().mIndexId
);
20173 AUTO_PROFILER_LABEL("Cursor::OpenOp::DoIndexDatabaseWork", DOM
);
20175 const bool usingKeyRange
= GetOptionalKeyRange().isSome();
20177 const auto indexTable
=
20178 GetCursor().mUniqueIndex
? "unique_index_data"_ns
: "index_data"_ns
;
20180 // The result of MakeColumnPairSelectionList is stored in a local variable,
20181 // since inlining it into the next statement causes a crash on some Mac OS X
20182 // builds (see https://bugzilla.mozilla.org/show_bug.cgi?id=1168606#c110).
20183 const auto columnPairSelectionList
= MakeColumnPairSelectionList(
20184 "index_table.value"_ns
, "index_table.value_locale"_ns
,
20185 kColumnNameAliasSortKey
, GetCursor().IsLocaleAware());
20186 const nsCString sortColumnAlias
=
20187 "SELECT "_ns
+ columnPairSelectionList
+ ", "_ns
;
20189 const nsAutoCString queryStart
= sortColumnAlias
+
20190 "index_table.object_data_key, "
20191 "object_data.file_ids, "
20192 "object_data.data "
20196 "JOIN object_data "
20197 "ON index_table.object_store_id = "
20198 "object_data.object_store_id "
20199 "AND index_table.object_data_key = "
20201 "WHERE index_table.index_id = :"_ns
+
20204 const auto keyRangeClause
=
20205 DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
20206 GetOptionalKeyRange(), kColumnNameAliasSortKey
);
20208 nsAutoCString directionClause
= " ORDER BY "_ns
+ kColumnNameAliasSortKey
;
20210 switch (GetCursor().mDirection
) {
20211 case IDBCursorDirection::Next
:
20212 case IDBCursorDirection::Nextunique
:
20213 directionClause
.AppendLiteral(" ASC, index_table.object_data_key ASC");
20216 case IDBCursorDirection::Prev
:
20217 directionClause
.AppendLiteral(" DESC, index_table.object_data_key DESC");
20220 case IDBCursorDirection::Prevunique
:
20221 directionClause
.AppendLiteral(" DESC, index_table.object_data_key ASC");
20225 MOZ_CRASH("Should never get here!");
20228 // Note: Changing the number or order of SELECT columns in the query will
20229 // require changes to CursorOpBase::PopulateResponseFromStatement.
20230 const nsCString firstQuery
= queryStart
+ keyRangeClause
+ directionClause
+
20232 IntToCString(1 + GetCursor().mMaxExtraCount
);
20234 QM_TRY_INSPECT(const auto& stmt
,
20235 aConnection
->BorrowCachedStatement(firstQuery
));
20237 QM_TRY(MOZ_TO_RESULT(
20238 stmt
->BindInt64ByName(kStmtParamNameId
, GetCursor().mIndexId
)));
20240 if (usingKeyRange
) {
20241 if (GetCursor().IsLocaleAware()) {
20242 QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
20243 GetOptionalKeyRange().ref(), &*stmt
, GetCursor().mLocale
)));
20245 QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
20246 GetOptionalKeyRange().ref(), &*stmt
)));
20250 // TODO: At least the last two statements are almost the same in all
20251 // DoDatabaseWork variants, consider removing this duplication.
20253 // Now we need to make the query to get the next match.
20254 PrepareKeyConditionClauses(directionClause
, std::move(queryStart
));
20256 return ProcessStatementSteps(&*stmt
);
20259 nsresult OpenOpHelper
<IDBCursorType::IndexKey
>::DoDatabaseWork(
20260 DatabaseConnection
* aConnection
) {
20261 MOZ_ASSERT(aConnection
);
20262 aConnection
->AssertIsOnConnectionThread();
20263 MOZ_ASSERT(GetCursor().mObjectStoreId
);
20264 MOZ_ASSERT(GetCursor().mIndexId
);
20266 AUTO_PROFILER_LABEL("Cursor::OpenOp::DoIndexKeyDatabaseWork", DOM
);
20268 const bool usingKeyRange
= GetOptionalKeyRange().isSome();
20271 GetCursor().mUniqueIndex
? "unique_index_data"_ns
: "index_data"_ns
;
20273 // The result of MakeColumnPairSelectionList is stored in a local variable,
20274 // since inlining it into the next statement causes a crash on some Mac OS X
20275 // builds (see https://bugzilla.mozilla.org/show_bug.cgi?id=1168606#c110).
20276 const auto columnPairSelectionList
= MakeColumnPairSelectionList(
20277 "value"_ns
, "value_locale"_ns
, kColumnNameAliasSortKey
,
20278 GetCursor().IsLocaleAware());
20279 const nsCString sortColumnAlias
=
20280 "SELECT "_ns
+ columnPairSelectionList
+ ", "_ns
;
20282 const nsAutoCString queryStart
= sortColumnAlias
+
20285 table
+ " WHERE index_id = :"_ns
+
20288 const auto keyRangeClause
=
20289 DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
20290 GetOptionalKeyRange(), kColumnNameAliasSortKey
);
20292 nsAutoCString directionClause
= " ORDER BY "_ns
+ kColumnNameAliasSortKey
;
20294 switch (GetCursor().mDirection
) {
20295 case IDBCursorDirection::Next
:
20296 case IDBCursorDirection::Nextunique
:
20297 directionClause
.AppendLiteral(" ASC, object_data_key ASC");
20300 case IDBCursorDirection::Prev
:
20301 directionClause
.AppendLiteral(" DESC, object_data_key DESC");
20304 case IDBCursorDirection::Prevunique
:
20305 directionClause
.AppendLiteral(" DESC, object_data_key ASC");
20309 MOZ_CRASH("Should never get here!");
20312 // Note: Changing the number or order of SELECT columns in the query will
20313 // require changes to CursorOpBase::PopulateResponseFromStatement.
20314 const nsCString firstQuery
=
20315 queryStart
+ keyRangeClause
+ directionClause
+ kOpenLimit
+ "1"_ns
;
20317 QM_TRY_INSPECT(const auto& stmt
,
20318 aConnection
->BorrowCachedStatement(firstQuery
));
20320 QM_TRY(MOZ_TO_RESULT(
20321 stmt
->BindInt64ByName(kStmtParamNameId
, GetCursor().mIndexId
)));
20323 if (usingKeyRange
) {
20324 if (GetCursor().IsLocaleAware()) {
20325 QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
20326 GetOptionalKeyRange().ref(), &*stmt
, GetCursor().mLocale
)));
20328 QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
20329 GetOptionalKeyRange().ref(), &*stmt
)));
20333 // Now we need to make the query to get the next match.
20334 PrepareKeyConditionClauses(directionClause
, std::move(queryStart
));
20336 return ProcessStatementSteps(&*stmt
);
20339 template <IDBCursorType CursorType
>
20340 nsresult Cursor
<CursorType
>::OpenOp::DoDatabaseWork(
20341 DatabaseConnection
* aConnection
) {
20342 MOZ_ASSERT(aConnection
);
20343 aConnection
->AssertIsOnConnectionThread();
20344 MOZ_ASSERT(mCursor
);
20345 MOZ_ASSERT(!mCursor
->mContinueQueries
);
20347 AUTO_PROFILER_LABEL("Cursor::OpenOp::DoDatabaseWork", DOM
);
20349 auto helper
= OpenOpHelper
<CursorType
>{*this};
20350 const auto rv
= helper
.DoDatabaseWork(aConnection
);
20351 if (NS_WARN_IF(NS_FAILED(rv
))) {
20358 template <IDBCursorType CursorType
>
20359 nsresult Cursor
<CursorType
>::CursorOpBase::SendSuccessResult() {
20360 AssertIsOnOwningThread();
20361 MOZ_ASSERT(mCursor
);
20362 MOZ_ASSERT(mCursor
->mCurrentlyRunningOp
== this);
20363 MOZ_ASSERT(mResponse
.type() != CursorResponse::T__None
);
20365 if (IsActorDestroyed()) {
20366 return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
;
20369 mCursor
->SendResponseInternal(mResponse
, mFiles
);
20372 mResponseSent
= true;
20377 template <IDBCursorType CursorType
>
20378 nsresult Cursor
<CursorType
>::ContinueOp::DoDatabaseWork(
20379 DatabaseConnection
* aConnection
) {
20380 MOZ_ASSERT(aConnection
);
20381 aConnection
->AssertIsOnConnectionThread();
20382 MOZ_ASSERT(mCursor
);
20383 MOZ_ASSERT(mCursor
->mObjectStoreId
);
20384 MOZ_ASSERT(!mCursor
->mContinueQueries
->mContinueQuery
.IsEmpty());
20385 MOZ_ASSERT(!mCursor
->mContinueQueries
->mContinueToQuery
.IsEmpty());
20386 MOZ_ASSERT(!mCurrentPosition
.mKey
.IsUnset());
20388 if constexpr (IsIndexCursor
) {
20390 mCursor
->mDirection
== IDBCursorDirection::Next
||
20391 mCursor
->mDirection
== IDBCursorDirection::Prev
,
20392 !mCursor
->mContinueQueries
->mContinuePrimaryKeyQuery
.IsEmpty());
20393 MOZ_ASSERT(mCursor
->mIndexId
);
20394 MOZ_ASSERT(!mCurrentPosition
.mObjectStoreKey
.IsUnset());
20397 AUTO_PROFILER_LABEL("Cursor::ContinueOp::DoDatabaseWork", DOM
);
20399 // We need to pick a query based on whether or not a key was passed to the
20400 // continue function. If not we'll grab the next item in the database that
20401 // is greater than (or less than, if we're running a PREV cursor) the current
20402 // key. If a key was passed we'll grab the next item in the database that is
20403 // greater than (or less than, if we're running a PREV cursor) or equal to the
20404 // key that was specified.
20406 // TODO: The description above is not complete, it does not take account of
20407 // ContinuePrimaryKey nor Advance.
20409 // Note: Changing the number or order of SELECT columns in the query will
20410 // require changes to CursorOpBase::PopulateResponseFromStatement.
20412 const uint32_t advanceCount
=
20413 mParams
.type() == CursorRequestParams::TAdvanceParams
20414 ? mParams
.get_AdvanceParams().count()
20416 MOZ_ASSERT(advanceCount
> 0);
20418 bool hasContinueKey
= false;
20419 bool hasContinuePrimaryKey
= false;
20421 auto explicitContinueKey
= Key
{};
20423 switch (mParams
.type()) {
20424 case CursorRequestParams::TContinueParams
:
20425 if (!mParams
.get_ContinueParams().key().IsUnset()) {
20426 hasContinueKey
= true;
20427 explicitContinueKey
= mParams
.get_ContinueParams().key();
20430 case CursorRequestParams::TContinuePrimaryKeyParams
:
20431 MOZ_ASSERT(!mParams
.get_ContinuePrimaryKeyParams().key().IsUnset());
20433 !mParams
.get_ContinuePrimaryKeyParams().primaryKey().IsUnset());
20434 MOZ_ASSERT(mCursor
->mDirection
== IDBCursorDirection::Next
||
20435 mCursor
->mDirection
== IDBCursorDirection::Prev
);
20436 hasContinueKey
= true;
20437 hasContinuePrimaryKey
= true;
20438 explicitContinueKey
= mParams
.get_ContinuePrimaryKeyParams().key();
20440 case CursorRequestParams::TAdvanceParams
:
20443 MOZ_CRASH("Should never get here!");
20446 // TODO: Whether it makes sense to preload depends on the kind of the
20447 // subsequent operations, not of the current operation. We could assume that
20448 // the subsequent operations are:
20449 // - the same as the current operation (with the same parameter values)
20450 // - as above, except for Advance, where we assume the count will be 1 on the
20452 // - basic operations (Advance with count 1 or Continue-without-key)
20454 // For now, we implement the second option for now (which correspond to
20455 // !hasContinueKey).
20457 // Based on that, we could in both cases either preload for any assumed
20458 // subsequent operations, or only for the basic operations. For now, we
20459 // preload only for an assumed basic operation. Other operations would require
20460 // more work on the client side for invalidation, and may not make any sense
20462 const uint32_t maxExtraCount
= hasContinueKey
? 0 : mCursor
->mMaxExtraCount
;
20464 QM_TRY_INSPECT(const auto& stmt
,
20465 aConnection
->BorrowCachedStatement(
20466 mCursor
->mContinueQueries
->GetContinueQuery(
20467 hasContinueKey
, hasContinuePrimaryKey
)));
20469 QM_TRY(MOZ_TO_RESULT(stmt
->BindUTF8StringByName(
20470 kStmtParamNameLimit
,
20471 IntToCString(advanceCount
+ mCursor
->mMaxExtraCount
))));
20473 QM_TRY(MOZ_TO_RESULT(stmt
->BindInt64ByName(kStmtParamNameId
, mCursor
->Id())));
20475 // Bind current key.
20476 const auto& continueKey
=
20477 hasContinueKey
? explicitContinueKey
20478 : mCurrentPosition
.GetSortKey(mCursor
->IsLocaleAware());
20479 QM_TRY(MOZ_TO_RESULT(
20480 continueKey
.BindToStatement(&*stmt
, kStmtParamNameCurrentKey
)));
20482 // Bind range bound if it is specified.
20483 if (!mCursor
->mLocaleAwareRangeBound
->IsUnset()) {
20484 QM_TRY(MOZ_TO_RESULT(mCursor
->mLocaleAwareRangeBound
->BindToStatement(
20485 &*stmt
, kStmtParamNameRangeBound
)));
20488 // Bind object store position if duplicates are allowed and we're not
20489 // continuing to a specific key.
20490 if constexpr (IsIndexCursor
) {
20491 if (!hasContinueKey
&& (mCursor
->mDirection
== IDBCursorDirection::Next
||
20492 mCursor
->mDirection
== IDBCursorDirection::Prev
)) {
20493 QM_TRY(MOZ_TO_RESULT(mCurrentPosition
.mObjectStoreKey
.BindToStatement(
20494 &*stmt
, kStmtParamNameObjectStorePosition
)));
20495 } else if (hasContinuePrimaryKey
) {
20496 QM_TRY(MOZ_TO_RESULT(
20497 mParams
.get_ContinuePrimaryKeyParams().primaryKey().BindToStatement(
20498 &*stmt
, kStmtParamNameObjectStorePosition
)));
20502 // TODO: Why do we query the records we don't need and skip them here, rather
20503 // than using a OFFSET clause in the query?
20504 for (uint32_t index
= 0; index
< advanceCount
; index
++) {
20505 QM_TRY_INSPECT(const bool& hasResult
,
20506 MOZ_TO_RESULT_INVOKE_MEMBER(&*stmt
, ExecuteStep
));
20509 mResponse
= void_t();
20515 auto* const optPreviousKey
=
20516 IsUnique(mCursor
->mDirection
) ? &previousKey
: nullptr;
20518 auto helper
= CursorOpBaseHelperBase
<CursorType
>{*this};
20519 QM_TRY_INSPECT(const auto& responseSize
, helper
.PopulateResponseFromStatement(
20520 &*stmt
, true, optPreviousKey
));
20522 helper
.PopulateExtraResponses(&*stmt
, maxExtraCount
, responseSize
,
20523 "ContinueOp"_ns
, optPreviousKey
);
20530 : mActorDestroyed(false)
20533 AssertIsOnBackgroundThread();
20536 Utils::~Utils() { MOZ_ASSERT(mActorDestroyed
); }
20538 void Utils::ActorDestroy(ActorDestroyReason aWhy
) {
20539 AssertIsOnBackgroundThread();
20540 MOZ_ASSERT(!mActorDestroyed
);
20543 mActorDestroyed
= true;
20547 mozilla::ipc::IPCResult
Utils::RecvDeleteMe() {
20548 AssertIsOnBackgroundThread();
20549 MOZ_ASSERT(!mActorDestroyed
);
20551 QM_WARNONLY_TRY(OkIf(PBackgroundIndexedDBUtilsParent::Send__delete__(this)));
20556 mozilla::ipc::IPCResult
Utils::RecvGetFileReferences(
20557 const PersistenceType
& aPersistenceType
, const nsACString
& aOrigin
,
20558 const nsAString
& aDatabaseName
, const int64_t& aFileId
, int32_t* aRefCnt
,
20559 int32_t* aDBRefCnt
, bool* aResult
) {
20560 AssertIsOnBackgroundThread();
20561 MOZ_ASSERT(aRefCnt
);
20562 MOZ_ASSERT(aDBRefCnt
);
20563 MOZ_ASSERT(aResult
);
20564 MOZ_ASSERT(!mActorDestroyed
);
20566 if (NS_WARN_IF(!IndexedDatabaseManager::Get())) {
20567 return IPC_FAIL(this, "No IndexedDatabaseManager active!");
20570 if (NS_WARN_IF(!QuotaManager::Get())) {
20571 return IPC_FAIL(this, "No QuotaManager active!");
20574 if (NS_WARN_IF(!StaticPrefs::dom_indexedDB_testing())) {
20575 return IPC_FAIL(this, "IndexedDB is not in testing mode!");
20578 if (NS_WARN_IF(!IsValidPersistenceType(aPersistenceType
))) {
20579 return IPC_FAIL(this, "PersistenceType is not valid!");
20582 if (NS_WARN_IF(aOrigin
.IsEmpty())) {
20583 return IPC_FAIL(this, "Origin is empty!");
20586 if (NS_WARN_IF(aDatabaseName
.IsEmpty())) {
20587 return IPC_FAIL(this, "DatabaseName is empty!");
20590 if (NS_WARN_IF(aFileId
== 0)) {
20591 return IPC_FAIL(this, "No FileId!");
20595 DispatchAndReturnFileReferences(aPersistenceType
, aOrigin
, aDatabaseName
,
20596 aFileId
, aRefCnt
, aDBRefCnt
, aResult
);
20597 if (NS_WARN_IF(NS_FAILED(rv
))) {
20598 return IPC_FAIL(this, "DispatchAndReturnFileReferences failed!");
20606 NS_IMPL_ISUPPORTS(DEBUGThreadSlower
, nsIThreadObserver
)
20609 DEBUGThreadSlower::OnDispatchedEvent() { MOZ_CRASH("Should never be called!"); }
20612 DEBUGThreadSlower::OnProcessNextEvent(nsIThreadInternal
* /* aThread */,
20613 bool /* aMayWait */) {
20618 DEBUGThreadSlower::AfterProcessNextEvent(nsIThreadInternal
* /* aThread */,
20619 bool /* aEventWasProcessed */) {
20620 MOZ_ASSERT(kDEBUGThreadSleepMS
);
20622 MOZ_ALWAYS_TRUE(PR_Sleep(PR_MillisecondsToInterval(kDEBUGThreadSleepMS
)) ==
20629 nsresult
FileHelper::Init() {
20630 MOZ_ASSERT(!IsOnBackgroundThread());
20632 auto fileDirectory
= mFileManager
->GetCheckedDirectory();
20633 if (NS_WARN_IF(!fileDirectory
)) {
20634 return NS_ERROR_FAILURE
;
20637 auto journalDirectory
= mFileManager
->EnsureJournalDirectory();
20638 if (NS_WARN_IF(!journalDirectory
)) {
20639 return NS_ERROR_FAILURE
;
20642 DebugOnly
<bool> exists
;
20643 MOZ_ASSERT(NS_SUCCEEDED(journalDirectory
->Exists(&exists
)));
20644 MOZ_ASSERT(exists
);
20646 DebugOnly
<bool> isDirectory
;
20647 MOZ_ASSERT(NS_SUCCEEDED(journalDirectory
->IsDirectory(&isDirectory
)));
20648 MOZ_ASSERT(isDirectory
);
20650 mFileDirectory
.init(WrapNotNullUnchecked(std::move(fileDirectory
)));
20651 mJournalDirectory
.init(WrapNotNullUnchecked(std::move(journalDirectory
)));
20656 nsCOMPtr
<nsIFile
> FileHelper::GetFile(const DatabaseFileInfo
& aFileInfo
) {
20657 MOZ_ASSERT(!IsOnBackgroundThread());
20659 return mFileManager
->GetFileForId(mFileDirectory
->get(), aFileInfo
.Id());
20662 nsCOMPtr
<nsIFile
> FileHelper::GetJournalFile(
20663 const DatabaseFileInfo
& aFileInfo
) {
20664 MOZ_ASSERT(!IsOnBackgroundThread());
20666 return mFileManager
->GetFileForId(mJournalDirectory
->get(), aFileInfo
.Id());
20669 nsresult
FileHelper::CreateFileFromStream(nsIFile
& aFile
, nsIFile
& aJournalFile
,
20670 nsIInputStream
& aInputStream
,
20672 const Maybe
<CipherKey
>& aMaybeKey
) {
20673 MOZ_ASSERT(!IsOnBackgroundThread());
20675 QM_TRY_INSPECT(const auto& exists
,
20676 MOZ_TO_RESULT_INVOKE_MEMBER(aFile
, Exists
));
20678 // DOM blobs that are being stored in IDB are cached by calling
20679 // IDBDatabase::GetOrCreateFileActorForBlob. So if the same DOM blob is stored
20680 // again under a different key or in a different object store, we just add
20681 // a new reference instead of creating a new copy (all such stored blobs share
20683 // However, it can happen that CreateFileFromStream failed due to quota
20684 // exceeded error and for some reason the orphaned file couldn't be deleted
20685 // immediately. Now, if the operation is being repeated, the DOM blob is
20686 // already cached, so it has the same file id which clashes with the orphaned
20687 // file. We could do some tricks to restore previous copy loop, but it's safer
20688 // to just delete the orphaned file and start from scratch.
20689 // This corner case is partially simulated in test_file_copy_failure.js
20691 QM_TRY_INSPECT(const auto& isFile
,
20692 MOZ_TO_RESULT_INVOKE_MEMBER(aFile
, IsFile
));
20694 QM_TRY(OkIf(isFile
), NS_ERROR_FAILURE
);
20696 QM_TRY_INSPECT(const auto& journalExists
,
20697 MOZ_TO_RESULT_INVOKE_MEMBER(aJournalFile
, Exists
));
20699 QM_TRY(OkIf(journalExists
), NS_ERROR_FAILURE
);
20701 QM_TRY_INSPECT(const auto& journalIsFile
,
20702 MOZ_TO_RESULT_INVOKE_MEMBER(aJournalFile
, IsFile
));
20704 QM_TRY(OkIf(journalIsFile
), NS_ERROR_FAILURE
);
20706 IDB_WARNING("Deleting orphaned file!");
20708 QM_TRY(MOZ_TO_RESULT(mFileManager
->SyncDeleteFile(aFile
, aJournalFile
)));
20711 // Create a journal file first.
20712 QM_TRY(MOZ_TO_RESULT(aJournalFile
.Create(nsIFile::NORMAL_FILE_TYPE
, 0644)));
20714 // Now try to copy the stream.
20715 QM_TRY_UNWRAP(nsCOMPtr
<nsIOutputStream
> fileOutputStream
,
20716 CreateFileOutputStream(mFileManager
->Type(),
20717 mFileManager
->OriginMetadata(),
20718 Client::IDB
, &aFile
));
20720 AutoTArray
<char, kFileCopyBufferSize
> buffer
;
20721 const auto actualOutputStream
=
20722 [aCompress
, &aMaybeKey
, &buffer
,
20724 std::move(fileOutputStream
)]() mutable -> nsCOMPtr
<nsIOutputStream
> {
20727 MakeRefPtr
<EncryptingOutputStream
<IndexedDBCipherStrategy
>>(
20728 std::move(baseOutputStream
), kEncryptedStreamBlockSize
,
20733 auto snappyOutputStream
=
20734 MakeRefPtr
<SnappyCompressOutputStream
>(baseOutputStream
);
20736 buffer
.SetLength(snappyOutputStream
->BlockSize());
20738 return snappyOutputStream
;
20741 buffer
.SetLength(kFileCopyBufferSize
);
20742 return std::move(baseOutputStream
);
20745 QM_TRY(MOZ_TO_RESULT(SyncCopy(aInputStream
, *actualOutputStream
,
20746 buffer
.Elements(), buffer
.Length())));
20751 class FileHelper::ReadCallback final
: public nsIInputStreamCallback
{
20753 NS_DECL_THREADSAFE_ISUPPORTS
20756 : mMutex("ReadCallback::mMutex"),
20757 mCondVar(mMutex
, "ReadCallback::mCondVar"),
20758 mInputAvailable(false) {}
20761 OnInputStreamReady(nsIAsyncInputStream
* aStream
) override
{
20762 mozilla::MutexAutoLock
autolock(mMutex
);
20764 mInputAvailable
= true;
20770 nsresult
AsyncWait(nsIAsyncInputStream
* aStream
, uint32_t aBufferSize
,
20771 nsIEventTarget
* aTarget
) {
20772 MOZ_ASSERT(aStream
);
20773 mozilla::MutexAutoLock
autolock(mMutex
);
20775 nsresult rv
= aStream
->AsyncWait(this, 0, aBufferSize
, aTarget
);
20776 if (NS_WARN_IF(NS_FAILED(rv
))) {
20780 mInputAvailable
= false;
20781 while (!mInputAvailable
) {
20789 ~ReadCallback() = default;
20791 mozilla::Mutex mMutex MOZ_UNANNOTATED
;
20792 mozilla::CondVar mCondVar
;
20793 bool mInputAvailable
;
20796 NS_IMPL_ADDREF(FileHelper::ReadCallback
);
20797 NS_IMPL_RELEASE(FileHelper::ReadCallback
);
20799 NS_INTERFACE_MAP_BEGIN(FileHelper::ReadCallback
)
20800 NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback
)
20801 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIInputStreamCallback
)
20802 NS_INTERFACE_MAP_END
20804 nsresult
FileHelper::SyncRead(nsIInputStream
& aInputStream
, char* const aBuffer
,
20805 const uint32_t aBufferSize
,
20806 uint32_t* const aRead
) {
20807 MOZ_ASSERT(!IsOnBackgroundThread());
20809 // Let's try to read, directly.
20810 nsresult rv
= aInputStream
.Read(aBuffer
, aBufferSize
, aRead
);
20811 if (NS_SUCCEEDED(rv
) || rv
!= NS_BASE_STREAM_WOULD_BLOCK
) {
20815 // We need to proceed async.
20816 nsCOMPtr
<nsIAsyncInputStream
> asyncStream
= do_QueryInterface(&aInputStream
);
20817 if (!asyncStream
) {
20821 if (!mReadCallback
) {
20822 mReadCallback
.init(MakeNotNull
<RefPtr
<ReadCallback
>>());
20825 // We just need any thread with an event loop for receiving the
20826 // OnInputStreamReady callback. Let's use the I/O thread.
20827 nsCOMPtr
<nsIEventTarget
> target
=
20828 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
20829 MOZ_ASSERT(target
);
20831 rv
= (*mReadCallback
)->AsyncWait(asyncStream
, aBufferSize
, target
);
20832 if (NS_WARN_IF(NS_FAILED(rv
))) {
20836 return SyncRead(aInputStream
, aBuffer
, aBufferSize
, aRead
);
20839 nsresult
FileHelper::SyncCopy(nsIInputStream
& aInputStream
,
20840 nsIOutputStream
& aOutputStream
,
20841 char* const aBuffer
, const uint32_t aBufferSize
) {
20842 MOZ_ASSERT(!IsOnBackgroundThread());
20844 AUTO_PROFILER_LABEL("FileHelper::SyncCopy", DOM
);
20850 rv
= SyncRead(aInputStream
, aBuffer
, aBufferSize
, &numRead
);
20851 if (NS_WARN_IF(NS_FAILED(rv
))) {
20860 rv
= aOutputStream
.Write(aBuffer
, numRead
, &numWrite
);
20861 if (rv
== NS_ERROR_FILE_NO_DEVICE_SPACE
) {
20862 rv
= NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR
;
20864 if (NS_WARN_IF(NS_FAILED(rv
))) {
20868 if (NS_WARN_IF(numWrite
!= numRead
)) {
20869 rv
= NS_ERROR_FAILURE
;
20874 if (NS_SUCCEEDED(rv
)) {
20875 rv
= aOutputStream
.Flush();
20876 if (NS_WARN_IF(NS_FAILED(rv
))) {
20881 nsresult rv2
= aOutputStream
.Close();
20882 if (NS_WARN_IF(NS_FAILED(rv2
))) {
20883 return NS_SUCCEEDED(rv
) ? rv2
: rv
;
20889 } // namespace dom::indexedDB
20890 } // namespace mozilla
20893 #undef IDB_DEBUG_LOG