Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "ActorsParent.h"
8 :
9 : #include <algorithm>
10 : #include "FileInfo.h"
11 : #include "FileManager.h"
12 : #include "IDBObjectStore.h"
13 : #include "IDBTransaction.h"
14 : #include "IndexedDatabase.h"
15 : #include "IndexedDatabaseInlines.h"
16 : #include "IndexedDatabaseManager.h"
17 : #include "js/StructuredClone.h"
18 : #include "js/Value.h"
19 : #include "jsapi.h"
20 : #include "KeyPath.h"
21 : #include "mozilla/Attributes.h"
22 : #include "mozilla/AutoRestore.h"
23 : #include "mozilla/Casting.h"
24 : #include "mozilla/CycleCollectedJSRuntime.h"
25 : #include "mozilla/EndianUtils.h"
26 : #include "mozilla/ErrorNames.h"
27 : #include "mozilla/LazyIdleThread.h"
28 : #include "mozilla/Maybe.h"
29 : #include "mozilla/Preferences.h"
30 : #include "mozilla/Services.h"
31 : #include "mozilla/SizePrintfMacros.h"
32 : #include "mozilla/SnappyCompressOutputStream.h"
33 : #include "mozilla/SnappyUncompressInputStream.h"
34 : #include "mozilla/StaticPtr.h"
35 : #include "mozilla/storage.h"
36 : #include "mozilla/Unused.h"
37 : #include "mozilla/UniquePtrExtensions.h"
38 : #include "mozilla/dom/ContentParent.h"
39 : #include "mozilla/dom/File.h"
40 : #include "mozilla/dom/FileBlobImpl.h"
41 : #include "mozilla/dom/StructuredCloneTags.h"
42 : #include "mozilla/dom/TabParent.h"
43 : #include "mozilla/dom/filehandle/ActorsParent.h"
44 : #include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h"
45 : #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
46 : #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h"
47 : #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestParent.h"
48 : #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h"
49 : #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h"
50 : #include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h"
51 : #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h"
52 : #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h"
53 : #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsParent.h"
54 : #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestParent.h"
55 : #include "mozilla/dom/IPCBlobUtils.h"
56 : #include "mozilla/dom/ipc/IPCBlobInputStreamParent.h"
57 : #include "mozilla/dom/quota/Client.h"
58 : #include "mozilla/dom/quota/FileStreams.h"
59 : #include "mozilla/dom/quota/OriginScope.h"
60 : #include "mozilla/dom/quota/QuotaManager.h"
61 : #include "mozilla/dom/quota/UsageInfo.h"
62 : #include "mozilla/ipc/BackgroundParent.h"
63 : #include "mozilla/ipc/BackgroundUtils.h"
64 : #include "mozilla/ipc/InputStreamParams.h"
65 : #include "mozilla/ipc/InputStreamUtils.h"
66 : #include "mozilla/ipc/PBackground.h"
67 : #include "mozilla/ipc/PBackgroundParent.h"
68 : #include "mozilla/Scoped.h"
69 : #include "mozilla/storage/Variant.h"
70 : #include "nsAutoPtr.h"
71 : #include "nsCharSeparatedTokenizer.h"
72 : #include "nsClassHashtable.h"
73 : #include "nsCOMPtr.h"
74 : #include "nsDataHashtable.h"
75 : #include "nsEscape.h"
76 : #include "nsHashKeys.h"
77 : #include "nsNetUtil.h"
78 : #include "nsIAsyncInputStream.h"
79 : #include "nsISimpleEnumerator.h"
80 : #include "nsIEventTarget.h"
81 : #include "nsIFile.h"
82 : #include "nsIFileURL.h"
83 : #include "nsIFileProtocolHandler.h"
84 : #include "nsIInputStream.h"
85 : #include "nsIInterfaceRequestor.h"
86 : #include "nsInterfaceHashtable.h"
87 : #include "nsIOutputStream.h"
88 : #include "nsIPipe.h"
89 : #include "nsIPrincipal.h"
90 : #include "nsIScriptSecurityManager.h"
91 : #include "nsISupports.h"
92 : #include "nsISupportsImpl.h"
93 : #include "nsISupportsPriority.h"
94 : #include "nsIThread.h"
95 : #include "nsITimer.h"
96 : #include "nsIURI.h"
97 : #include "nsNetUtil.h"
98 : #include "nsPrintfCString.h"
99 : #include "nsQueryObject.h"
100 : #include "nsRefPtrHashtable.h"
101 : #include "nsStreamUtils.h"
102 : #include "nsString.h"
103 : #include "nsStringStream.h"
104 : #include "nsThreadPool.h"
105 : #include "nsThreadUtils.h"
106 : #include "nsXPCOMCID.h"
107 : #include "PermissionRequestBase.h"
108 : #include "ProfilerHelpers.h"
109 : #include "prsystem.h"
110 : #include "prtime.h"
111 : #include "ReportInternalError.h"
112 : #include "snappy/snappy.h"
113 :
114 : #define DISABLE_ASSERTS_FOR_FUZZING 0
115 :
116 : #if DISABLE_ASSERTS_FOR_FUZZING
117 : #define ASSERT_UNLESS_FUZZING(...) do { } while (0)
118 : #else
119 : #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
120 : #endif
121 :
122 : #define IDB_DEBUG_LOG(_args) \
123 : MOZ_LOG(IndexedDatabaseManager::GetLoggingModule(), \
124 : LogLevel::Debug, \
125 : _args )
126 :
127 : #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
128 : #define IDB_MOBILE
129 : #endif
130 :
131 : namespace mozilla {
132 :
133 0 : MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc,
134 : PRFileDesc,
135 : PR_Close);
136 :
137 : namespace dom {
138 : namespace indexedDB {
139 :
140 : using namespace mozilla::dom::quota;
141 : using namespace mozilla::ipc;
142 :
143 : namespace {
144 :
145 : class ConnectionPool;
146 : class Cursor;
147 : class Database;
148 : struct DatabaseActorInfo;
149 : class DatabaseFile;
150 : class DatabaseLoggingInfo;
151 : class DatabaseMaintenance;
152 : class Factory;
153 : class Maintenance;
154 : class MutableFile;
155 : class OpenDatabaseOp;
156 : class TransactionBase;
157 : class TransactionDatabaseOperationBase;
158 : class VersionChangeTransaction;
159 :
160 : /*******************************************************************************
161 : * Constants
162 : ******************************************************************************/
163 :
164 : // If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
165 : // schema version.
166 : static_assert(JS_STRUCTURED_CLONE_VERSION == 8,
167 : "Need to update the major schema version.");
168 :
169 : // Major schema version. Bump for almost everything.
170 : const uint32_t kMajorSchemaVersion = 26;
171 :
172 : // Minor schema version. Should almost always be 0 (maybe bump on release
173 : // branches if we have to).
174 : const uint32_t kMinorSchemaVersion = 0;
175 :
176 : // The schema version we store in the SQLite database is a (signed) 32-bit
177 : // integer. The major version is left-shifted 4 bits so the max value is
178 : // 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
179 : static_assert(kMajorSchemaVersion <= 0xFFFFFFF,
180 : "Major version needs to fit in 28 bits.");
181 : static_assert(kMinorSchemaVersion <= 0xF,
182 : "Minor version needs to fit in 4 bits.");
183 :
184 : const int32_t kSQLiteSchemaVersion =
185 : int32_t((kMajorSchemaVersion << 4) + kMinorSchemaVersion);
186 :
187 : const int32_t kStorageProgressGranularity = 1000;
188 :
189 : // Changing the value here will override the page size of new databases only.
190 : // A journal mode change and VACUUM are needed to change existing databases, so
191 : // the best way to do that is to use the schema version upgrade mechanism.
192 : const uint32_t kSQLitePageSizeOverride =
193 : #ifdef IDB_MOBILE
194 : 2048;
195 : #else
196 : 4096;
197 : #endif
198 :
199 : static_assert(kSQLitePageSizeOverride == /* mozStorage default */ 0 ||
200 : (kSQLitePageSizeOverride % 2 == 0 &&
201 : kSQLitePageSizeOverride >= 512 &&
202 : kSQLitePageSizeOverride <= 65536),
203 : "Must be 0 (disabled) or a power of 2 between 512 and 65536!");
204 :
205 : // Set to -1 to use SQLite's default, 0 to disable, or some positive number to
206 : // enforce a custom limit.
207 : const int32_t kMaxWALPages = 5000; // 20MB on desktop, 10MB on mobile.
208 :
209 : // Set to some multiple of the page size to grow the database in larger chunks.
210 : const uint32_t kSQLiteGrowthIncrement = kSQLitePageSizeOverride * 2;
211 :
212 : static_assert(kSQLiteGrowthIncrement >= 0 &&
213 : kSQLiteGrowthIncrement % kSQLitePageSizeOverride == 0 &&
214 : kSQLiteGrowthIncrement < uint32_t(INT32_MAX),
215 : "Must be 0 (disabled) or a positive multiple of the page size!");
216 :
217 : // The maximum number of threads that can be used for database activity at a
218 : // single time.
219 : const uint32_t kMaxConnectionThreadCount = 20;
220 :
221 : static_assert(kMaxConnectionThreadCount, "Must have at least one thread!");
222 :
223 : // The maximum number of threads to keep when idle. Threads that become idle in
224 : // excess of this number will be shut down immediately.
225 : const uint32_t kMaxIdleConnectionThreadCount = 2;
226 :
227 : static_assert(kMaxConnectionThreadCount >= kMaxIdleConnectionThreadCount,
228 : "Idle thread limit must be less than total thread limit!");
229 :
230 : // The length of time that database connections will be held open after all
231 : // transactions have completed before doing idle maintenance.
232 : const uint32_t kConnectionIdleMaintenanceMS = 2 * 1000; // 2 seconds
233 :
234 : // The length of time that database connections will be held open after all
235 : // transactions and maintenance have completed.
236 : const uint32_t kConnectionIdleCloseMS = 10 * 1000; // 10 seconds
237 :
238 : // The length of time that idle threads will stay alive before being shut down.
239 : const uint32_t kConnectionThreadIdleMS = 30 * 1000; // 30 seconds
240 :
241 : #define SAVEPOINT_CLAUSE "SAVEPOINT sp;"
242 :
243 : const uint32_t kFileCopyBufferSize = 32768;
244 :
245 : #define JOURNAL_DIRECTORY_NAME "journals"
246 :
247 : const char kFileManagerDirectoryNameSuffix[] = ".files";
248 : const char kSQLiteSuffix[] = ".sqlite";
249 : const char kSQLiteJournalSuffix[] = ".sqlite-journal";
250 : const char kSQLiteSHMSuffix[] = ".sqlite-shm";
251 : const char kSQLiteWALSuffix[] = ".sqlite-wal";
252 :
253 : const char kPrefIndexedDBEnabled[] = "dom.indexedDB.enabled";
254 :
255 : const char kPrefFileHandleEnabled[] = "dom.fileHandle.enabled";
256 :
257 : #define IDB_PREFIX "indexedDB"
258 :
259 : #define PERMISSION_STRING_CHROME_BASE IDB_PREFIX "-chrome-"
260 : #define PERMISSION_STRING_CHROME_READ_SUFFIX "-read"
261 : #define PERMISSION_STRING_CHROME_WRITE_SUFFIX "-write"
262 :
263 : #ifdef DEBUG
264 :
265 : const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL;
266 : const uint32_t kDEBUGThreadSleepMS = 0;
267 :
268 : const int32_t kDEBUGTransactionThreadPriority =
269 : nsISupportsPriority::PRIORITY_NORMAL;
270 : const uint32_t kDEBUGTransactionThreadSleepMS = 0;
271 :
272 : #endif
273 :
274 : template <size_t N>
275 : constexpr size_t
276 0 : LiteralStringLength(const char (&aArr)[N])
277 : {
278 : static_assert(N, "Zero-length string literal?!");
279 :
280 : // Don't include the null terminator.
281 0 : return N - 1;
282 : }
283 :
284 : /*******************************************************************************
285 : * Metadata classes
286 : ******************************************************************************/
287 :
288 : struct FullIndexMetadata
289 : {
290 : IndexMetadata mCommonMetadata;
291 :
292 : bool mDeleted;
293 :
294 : public:
295 0 : FullIndexMetadata()
296 0 : : mCommonMetadata(0, nsString(), KeyPath(0), nsCString(), false, false, false)
297 0 : , mDeleted(false)
298 : {
299 : // This can happen either on the QuotaManager IO thread or on a
300 : // versionchange transaction thread. These threads can never race so this is
301 : // totally safe.
302 0 : }
303 :
304 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullIndexMetadata)
305 :
306 : private:
307 0 : ~FullIndexMetadata() = default;
308 : };
309 :
310 : typedef nsRefPtrHashtable<nsUint64HashKey, FullIndexMetadata> IndexTable;
311 :
312 : struct FullObjectStoreMetadata
313 : {
314 : ObjectStoreMetadata mCommonMetadata;
315 : IndexTable mIndexes;
316 :
317 : // These two members are only ever touched on a transaction thread!
318 : int64_t mNextAutoIncrementId;
319 : int64_t mCommittedAutoIncrementId;
320 :
321 : bool mDeleted;
322 :
323 : public:
324 0 : FullObjectStoreMetadata()
325 0 : : mCommonMetadata(0, nsString(), KeyPath(0), false)
326 : , mNextAutoIncrementId(0)
327 : , mCommittedAutoIncrementId(0)
328 0 : , mDeleted(false)
329 : {
330 : // This can happen either on the QuotaManager IO thread or on a
331 : // versionchange transaction thread. These threads can never race so this is
332 : // totally safe.
333 0 : }
334 :
335 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullObjectStoreMetadata);
336 :
337 : bool
338 : HasLiveIndexes() const;
339 :
340 : private:
341 0 : ~FullObjectStoreMetadata() = default;
342 : };
343 :
344 : typedef nsRefPtrHashtable<nsUint64HashKey, FullObjectStoreMetadata>
345 : ObjectStoreTable;
346 :
347 : struct FullDatabaseMetadata
348 : {
349 : DatabaseMetadata mCommonMetadata;
350 : nsCString mDatabaseId;
351 : nsString mFilePath;
352 : ObjectStoreTable mObjectStores;
353 :
354 : int64_t mNextObjectStoreId;
355 : int64_t mNextIndexId;
356 :
357 : public:
358 0 : explicit FullDatabaseMetadata(const DatabaseMetadata& aCommonMetadata)
359 0 : : mCommonMetadata(aCommonMetadata)
360 : , mNextObjectStoreId(0)
361 0 : , mNextIndexId(0)
362 : {
363 0 : AssertIsOnBackgroundThread();
364 0 : }
365 :
366 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullDatabaseMetadata)
367 :
368 : already_AddRefed<FullDatabaseMetadata>
369 : Duplicate() const;
370 :
371 : private:
372 0 : ~FullDatabaseMetadata() = default;
373 : };
374 :
375 : template <class MetadataType>
376 0 : class MOZ_STACK_CLASS MetadataNameOrIdMatcher final
377 : {
378 : typedef MetadataNameOrIdMatcher<MetadataType> SelfType;
379 :
380 : const int64_t mId;
381 : const nsString mName;
382 : RefPtr<MetadataType> mMetadata;
383 : bool mCheckName;
384 :
385 : public:
386 : template <class Enumerable>
387 : static MetadataType*
388 0 : Match(const Enumerable& aEnumerable, uint64_t aId, const nsAString& aName)
389 : {
390 0 : AssertIsOnBackgroundThread();
391 0 : MOZ_ASSERT(aId);
392 :
393 0 : SelfType closure(aId, aName);
394 0 : MatchHelper(aEnumerable, &closure);
395 :
396 0 : return closure.mMetadata;
397 : }
398 :
399 : template <class Enumerable>
400 : static MetadataType*
401 0 : Match(const Enumerable& aEnumerable, uint64_t aId)
402 : {
403 0 : AssertIsOnBackgroundThread();
404 0 : MOZ_ASSERT(aId);
405 :
406 0 : SelfType closure(aId);
407 0 : MatchHelper(aEnumerable, &closure);
408 :
409 0 : return closure.mMetadata;
410 : }
411 :
412 : private:
413 0 : MetadataNameOrIdMatcher(const int64_t& aId, const nsAString& aName)
414 : : mId(aId)
415 : , mName(PromiseFlatString(aName))
416 : , mMetadata(nullptr)
417 0 : , mCheckName(true)
418 : {
419 0 : AssertIsOnBackgroundThread();
420 0 : MOZ_ASSERT(aId);
421 0 : }
422 :
423 0 : explicit MetadataNameOrIdMatcher(const int64_t& aId)
424 : : mId(aId)
425 : , mMetadata(nullptr)
426 0 : , mCheckName(false)
427 : {
428 0 : AssertIsOnBackgroundThread();
429 0 : MOZ_ASSERT(aId);
430 0 : }
431 :
432 : template <class Enumerable>
433 : static void
434 0 : MatchHelper(const Enumerable& aEnumerable, SelfType* aClosure)
435 : {
436 0 : AssertIsOnBackgroundThread();
437 0 : MOZ_ASSERT(aClosure);
438 :
439 0 : for (auto iter = aEnumerable.ConstIter(); !iter.Done(); iter.Next()) {
440 : #ifdef DEBUG
441 0 : const uint64_t key = iter.Key();
442 : #endif
443 0 : MetadataType* value = iter.UserData();
444 0 : MOZ_ASSERT(key != 0);
445 0 : MOZ_ASSERT(value);
446 :
447 0 : if (!value->mDeleted &&
448 0 : (aClosure->mId == value->mCommonMetadata.id() ||
449 0 : (aClosure->mCheckName &&
450 0 : aClosure->mName == value->mCommonMetadata.name()))) {
451 0 : aClosure->mMetadata = value;
452 0 : break;
453 : }
454 : }
455 0 : }
456 : };
457 :
458 : struct IndexDataValue final
459 : {
460 : int64_t mIndexId;
461 : Key mKey;
462 : Key mSortKey;
463 : bool mUnique;
464 :
465 0 : IndexDataValue()
466 0 : : mIndexId(0)
467 0 : , mUnique(false)
468 : {
469 0 : MOZ_COUNT_CTOR(IndexDataValue);
470 0 : }
471 :
472 : explicit
473 0 : IndexDataValue(const IndexDataValue& aOther)
474 0 : : mIndexId(aOther.mIndexId)
475 : , mKey(aOther.mKey)
476 : , mSortKey(aOther.mSortKey)
477 0 : , mUnique(aOther.mUnique)
478 : {
479 0 : MOZ_ASSERT(!aOther.mKey.IsUnset());
480 :
481 0 : MOZ_COUNT_CTOR(IndexDataValue);
482 0 : }
483 :
484 0 : IndexDataValue(int64_t aIndexId, bool aUnique, const Key& aKey)
485 0 : : mIndexId(aIndexId)
486 : , mKey(aKey)
487 0 : , mUnique(aUnique)
488 : {
489 0 : MOZ_ASSERT(!aKey.IsUnset());
490 :
491 0 : MOZ_COUNT_CTOR(IndexDataValue);
492 0 : }
493 :
494 0 : IndexDataValue(int64_t aIndexId, bool aUnique, const Key& aKey,
495 : const Key& aSortKey)
496 0 : : mIndexId(aIndexId)
497 : , mKey(aKey)
498 : , mSortKey(aSortKey)
499 0 : , mUnique(aUnique)
500 : {
501 0 : MOZ_ASSERT(!aKey.IsUnset());
502 :
503 0 : MOZ_COUNT_CTOR(IndexDataValue);
504 0 : }
505 :
506 0 : ~IndexDataValue()
507 0 : {
508 0 : MOZ_COUNT_DTOR(IndexDataValue);
509 0 : }
510 :
511 : bool
512 0 : operator==(const IndexDataValue& aOther) const
513 : {
514 0 : if (mIndexId != aOther.mIndexId) {
515 0 : return false;
516 : }
517 0 : if (mSortKey.IsUnset()) {
518 0 : return mKey == aOther.mKey;
519 : }
520 0 : return mSortKey == aOther.mSortKey;
521 : }
522 :
523 : bool
524 0 : operator<(const IndexDataValue& aOther) const
525 : {
526 0 : if (mIndexId == aOther.mIndexId) {
527 0 : if (mSortKey.IsUnset()) {
528 0 : return mKey < aOther.mKey;
529 : }
530 0 : return mSortKey < aOther.mSortKey;
531 : }
532 :
533 0 : return mIndexId < aOther.mIndexId;
534 : }
535 : };
536 :
537 : /*******************************************************************************
538 : * SQLite functions
539 : ******************************************************************************/
540 :
541 : int32_t
542 0 : MakeSchemaVersion(uint32_t aMajorSchemaVersion,
543 : uint32_t aMinorSchemaVersion)
544 : {
545 0 : return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion);
546 : }
547 :
548 : uint32_t
549 0 : HashName(const nsAString& aName)
550 : {
551 : struct Helper
552 : {
553 : static uint32_t
554 0 : RotateBitsLeft32(uint32_t aValue, uint8_t aBits)
555 : {
556 0 : MOZ_ASSERT(aBits < 32);
557 0 : return (aValue << aBits) | (aValue >> (32 - aBits));
558 : }
559 : };
560 :
561 : static const uint32_t kGoldenRatioU32 = 0x9e3779b9u;
562 :
563 0 : const char16_t* str = aName.BeginReading();
564 0 : size_t length = aName.Length();
565 :
566 0 : uint32_t hash = 0;
567 0 : for (size_t i = 0; i < length; i++) {
568 0 : hash = kGoldenRatioU32 * (Helper::RotateBitsLeft32(hash, 5) ^ str[i]);
569 : }
570 :
571 0 : return hash;
572 : }
573 :
574 : nsresult
575 0 : ClampResultCode(nsresult aResultCode)
576 : {
577 0 : if (NS_SUCCEEDED(aResultCode) ||
578 0 : NS_ERROR_GET_MODULE(aResultCode) == NS_ERROR_MODULE_DOM_INDEXEDDB) {
579 0 : return aResultCode;
580 : }
581 :
582 0 : switch (aResultCode) {
583 : case NS_ERROR_FILE_NO_DEVICE_SPACE:
584 0 : return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
585 : case NS_ERROR_STORAGE_CONSTRAINT:
586 0 : return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
587 : default:
588 : #ifdef DEBUG
589 : nsPrintfCString message("Converting non-IndexedDB error code (0x%" PRIX32 ") to "
590 : "NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR",
591 0 : static_cast<uint32_t>(aResultCode));
592 0 : NS_WARNING(message.get());
593 : #else
594 : ;
595 : #endif
596 : }
597 :
598 0 : IDB_REPORT_INTERNAL_ERR();
599 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
600 : }
601 :
602 : void
603 0 : GetDatabaseFilename(const nsAString& aName,
604 : nsAutoString& aDatabaseFilename)
605 : {
606 0 : MOZ_ASSERT(aDatabaseFilename.IsEmpty());
607 :
608 0 : aDatabaseFilename.AppendInt(HashName(aName));
609 :
610 0 : nsCString escapedName;
611 0 : if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) {
612 0 : MOZ_CRASH("Can't escape database name!");
613 : }
614 :
615 0 : const char* forwardIter = escapedName.BeginReading();
616 0 : const char* backwardIter = escapedName.EndReading() - 1;
617 :
618 0 : nsAutoCString substring;
619 0 : while (forwardIter <= backwardIter && substring.Length() < 21) {
620 0 : if (substring.Length() % 2) {
621 0 : substring.Append(*backwardIter--);
622 : } else {
623 0 : substring.Append(*forwardIter++);
624 : }
625 : }
626 :
627 0 : aDatabaseFilename.AppendASCII(substring.get(), substring.Length());
628 0 : }
629 :
630 : uint32_t
631 0 : CompressedByteCountForNumber(uint64_t aNumber)
632 : {
633 : // All bytes have 7 bits available.
634 0 : uint32_t count = 1;
635 0 : while ((aNumber >>= 7)) {
636 0 : count++;
637 : }
638 :
639 0 : return count;
640 : }
641 :
642 : uint32_t
643 0 : CompressedByteCountForIndexId(int64_t aIndexId)
644 : {
645 0 : MOZ_ASSERT(aIndexId);
646 0 : MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId),
647 : "Overflow!");
648 :
649 0 : return CompressedByteCountForNumber(uint64_t(aIndexId * 2));
650 : }
651 :
652 : void
653 0 : WriteCompressedNumber(uint64_t aNumber, uint8_t** aIterator)
654 : {
655 0 : MOZ_ASSERT(aIterator);
656 0 : MOZ_ASSERT(*aIterator);
657 :
658 0 : uint8_t*& buffer = *aIterator;
659 :
660 : #ifdef DEBUG
661 0 : const uint8_t* bufferStart = buffer;
662 0 : const uint64_t originalNumber = aNumber;
663 : #endif
664 :
665 : while (true) {
666 0 : uint64_t shiftedNumber = aNumber >> 7;
667 0 : if (shiftedNumber) {
668 0 : *buffer++ = uint8_t(0x80 | (aNumber & 0x7f));
669 0 : aNumber = shiftedNumber;
670 : } else {
671 0 : *buffer++ = uint8_t(aNumber);
672 0 : break;
673 : }
674 0 : }
675 :
676 0 : MOZ_ASSERT(buffer > bufferStart);
677 0 : MOZ_ASSERT(uint32_t(buffer - bufferStart) ==
678 : CompressedByteCountForNumber(originalNumber));
679 0 : }
680 :
681 : uint64_t
682 0 : ReadCompressedNumber(const uint8_t** aIterator, const uint8_t* aEnd)
683 : {
684 0 : MOZ_ASSERT(aIterator);
685 0 : MOZ_ASSERT(*aIterator);
686 0 : MOZ_ASSERT(aEnd);
687 0 : MOZ_ASSERT(*aIterator < aEnd);
688 :
689 0 : const uint8_t*& buffer = *aIterator;
690 :
691 0 : uint8_t shiftCounter = 0;
692 0 : uint64_t result = 0;
693 :
694 0 : while (true) {
695 0 : MOZ_ASSERT(shiftCounter <= 56, "Shifted too many bits!");
696 :
697 0 : result += (uint64_t(*buffer & 0x7f) << shiftCounter);
698 0 : shiftCounter += 7;
699 :
700 0 : if (!(*buffer++ & 0x80)) {
701 0 : break;
702 : }
703 :
704 0 : if (NS_WARN_IF(buffer == aEnd)) {
705 0 : MOZ_ASSERT(false);
706 : break;
707 : }
708 : }
709 :
710 0 : return result;
711 : }
712 :
713 : void
714 0 : WriteCompressedIndexId(int64_t aIndexId, bool aUnique, uint8_t** aIterator)
715 : {
716 0 : MOZ_ASSERT(aIndexId);
717 0 : MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId),
718 : "Overflow!");
719 0 : MOZ_ASSERT(aIterator);
720 0 : MOZ_ASSERT(*aIterator);
721 :
722 0 : const uint64_t indexId = (uint64_t(aIndexId * 2) | (aUnique ? 1 : 0));
723 0 : WriteCompressedNumber(indexId, aIterator);
724 0 : }
725 :
726 : void
727 0 : ReadCompressedIndexId(const uint8_t** aIterator,
728 : const uint8_t* aEnd,
729 : int64_t* aIndexId,
730 : bool* aUnique)
731 : {
732 0 : MOZ_ASSERT(aIterator);
733 0 : MOZ_ASSERT(*aIterator);
734 0 : MOZ_ASSERT(aIndexId);
735 0 : MOZ_ASSERT(aUnique);
736 :
737 0 : uint64_t indexId = ReadCompressedNumber(aIterator, aEnd);
738 :
739 0 : if (indexId % 2) {
740 0 : *aUnique = true;
741 0 : indexId--;
742 : } else {
743 0 : *aUnique = false;
744 : }
745 :
746 0 : MOZ_ASSERT(UINT64_MAX / 2 >= uint64_t(indexId), "Bad index id!");
747 :
748 0 : *aIndexId = int64_t(indexId / 2);
749 0 : }
750 :
751 : // static
752 : nsresult
753 0 : MakeCompressedIndexDataValues(
754 : const FallibleTArray<IndexDataValue>& aIndexValues,
755 : UniqueFreePtr<uint8_t>& aCompressedIndexDataValues,
756 : uint32_t* aCompressedIndexDataValuesLength)
757 : {
758 0 : MOZ_ASSERT(!NS_IsMainThread());
759 0 : MOZ_ASSERT(!IsOnBackgroundThread());
760 0 : MOZ_ASSERT(!aCompressedIndexDataValues);
761 0 : MOZ_ASSERT(aCompressedIndexDataValuesLength);
762 :
763 0 : AUTO_PROFILER_LABEL("MakeCompressedIndexDataValues", STORAGE);
764 :
765 0 : const uint32_t arrayLength = aIndexValues.Length();
766 0 : if (!arrayLength) {
767 0 : *aCompressedIndexDataValuesLength = 0;
768 0 : return NS_OK;
769 : }
770 :
771 : // First calculate the size of the final buffer.
772 0 : uint32_t blobDataLength = 0;
773 :
774 0 : for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
775 0 : const IndexDataValue& info = aIndexValues[arrayIndex];
776 0 : const nsCString& keyBuffer = info.mKey.GetBuffer();
777 0 : const nsCString& sortKeyBuffer = info.mSortKey.GetBuffer();
778 0 : const uint32_t keyBufferLength = keyBuffer.Length();
779 0 : const uint32_t sortKeyBufferLength = sortKeyBuffer.Length();
780 :
781 0 : MOZ_ASSERT(!keyBuffer.IsEmpty());
782 :
783 : // Don't let |infoLength| overflow.
784 0 : if (NS_WARN_IF(UINT32_MAX - keyBuffer.Length() <
785 : CompressedByteCountForIndexId(info.mIndexId) +
786 : CompressedByteCountForNumber(keyBufferLength) +
787 : CompressedByteCountForNumber(sortKeyBufferLength))) {
788 0 : IDB_REPORT_INTERNAL_ERR();
789 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
790 : }
791 :
792 : const uint32_t infoLength =
793 0 : CompressedByteCountForIndexId(info.mIndexId) +
794 0 : CompressedByteCountForNumber(keyBufferLength) +
795 0 : CompressedByteCountForNumber(sortKeyBufferLength) +
796 : keyBufferLength +
797 0 : sortKeyBufferLength;
798 :
799 : // Don't let |blobDataLength| overflow.
800 0 : if (NS_WARN_IF(UINT32_MAX - infoLength < blobDataLength)) {
801 0 : IDB_REPORT_INTERNAL_ERR();
802 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
803 : }
804 :
805 0 : blobDataLength += infoLength;
806 : }
807 :
808 : UniqueFreePtr<uint8_t> blobData(
809 0 : static_cast<uint8_t*>(malloc(blobDataLength)));
810 0 : if (NS_WARN_IF(!blobData)) {
811 0 : IDB_REPORT_INTERNAL_ERR();
812 0 : return NS_ERROR_OUT_OF_MEMORY;
813 : }
814 :
815 0 : uint8_t* blobDataIter = blobData.get();
816 :
817 0 : for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
818 0 : const IndexDataValue& info = aIndexValues[arrayIndex];
819 0 : const nsCString& keyBuffer = info.mKey.GetBuffer();
820 0 : const nsCString& sortKeyBuffer = info.mSortKey.GetBuffer();
821 0 : const uint32_t keyBufferLength = keyBuffer.Length();
822 0 : const uint32_t sortKeyBufferLength = sortKeyBuffer.Length();
823 :
824 0 : WriteCompressedIndexId(info.mIndexId, info.mUnique, &blobDataIter);
825 0 : WriteCompressedNumber(keyBufferLength, &blobDataIter);
826 :
827 0 : memcpy(blobDataIter, keyBuffer.get(), keyBufferLength);
828 0 : blobDataIter += keyBufferLength;
829 :
830 0 : WriteCompressedNumber(sortKeyBufferLength, &blobDataIter);
831 :
832 0 : memcpy(blobDataIter, sortKeyBuffer.get(), sortKeyBufferLength);
833 0 : blobDataIter += sortKeyBufferLength;
834 : }
835 :
836 0 : MOZ_ASSERT(blobDataIter == blobData.get() + blobDataLength);
837 :
838 0 : aCompressedIndexDataValues.swap(blobData);
839 0 : *aCompressedIndexDataValuesLength = uint32_t(blobDataLength);
840 :
841 0 : return NS_OK;
842 : }
843 :
844 : nsresult
845 0 : ReadCompressedIndexDataValuesFromBlob(const uint8_t* aBlobData,
846 : uint32_t aBlobDataLength,
847 : nsTArray<IndexDataValue>& aIndexValues)
848 : {
849 0 : MOZ_ASSERT(!NS_IsMainThread());
850 0 : MOZ_ASSERT(!IsOnBackgroundThread());
851 0 : MOZ_ASSERT(aBlobData);
852 0 : MOZ_ASSERT(aBlobDataLength);
853 0 : MOZ_ASSERT(aIndexValues.IsEmpty());
854 :
855 0 : AUTO_PROFILER_LABEL("ReadCompressedIndexDataValuesFromBlob", STORAGE);
856 :
857 0 : const uint8_t* blobDataIter = aBlobData;
858 0 : const uint8_t* blobDataEnd = aBlobData + aBlobDataLength;
859 :
860 0 : while (blobDataIter < blobDataEnd) {
861 : int64_t indexId;
862 : bool unique;
863 0 : ReadCompressedIndexId(&blobDataIter, blobDataEnd, &indexId, &unique);
864 :
865 0 : if (NS_WARN_IF(blobDataIter == blobDataEnd)) {
866 0 : IDB_REPORT_INTERNAL_ERR();
867 0 : return NS_ERROR_FILE_CORRUPTED;
868 : }
869 :
870 : // Read key buffer length.
871 : const uint64_t keyBufferLength =
872 0 : ReadCompressedNumber(&blobDataIter, blobDataEnd);
873 :
874 0 : if (NS_WARN_IF(blobDataIter == blobDataEnd) ||
875 0 : NS_WARN_IF(keyBufferLength > uint64_t(UINT32_MAX)) ||
876 0 : NS_WARN_IF(blobDataIter + keyBufferLength > blobDataEnd)) {
877 0 : IDB_REPORT_INTERNAL_ERR();
878 0 : return NS_ERROR_FILE_CORRUPTED;
879 : }
880 :
881 : nsCString keyBuffer(reinterpret_cast<const char*>(blobDataIter),
882 0 : uint32_t(keyBufferLength));
883 0 : blobDataIter += keyBufferLength;
884 :
885 0 : IndexDataValue idv(indexId, unique, Key(keyBuffer));
886 :
887 : // Read sort key buffer length.
888 : const uint64_t sortKeyBufferLength =
889 0 : ReadCompressedNumber(&blobDataIter, blobDataEnd);
890 :
891 0 : if (sortKeyBufferLength > 0) {
892 0 : if (NS_WARN_IF(blobDataIter == blobDataEnd) ||
893 0 : NS_WARN_IF(sortKeyBufferLength > uint64_t(UINT32_MAX)) ||
894 0 : NS_WARN_IF(blobDataIter + sortKeyBufferLength > blobDataEnd)) {
895 0 : IDB_REPORT_INTERNAL_ERR();
896 0 : return NS_ERROR_FILE_CORRUPTED;
897 : }
898 :
899 : nsCString sortKeyBuffer(reinterpret_cast<const char*>(blobDataIter),
900 0 : uint32_t(sortKeyBufferLength));
901 0 : blobDataIter += sortKeyBufferLength;
902 :
903 0 : idv.mSortKey = Key(sortKeyBuffer);
904 : }
905 :
906 0 : if (NS_WARN_IF(!aIndexValues.InsertElementSorted(idv, fallible))) {
907 0 : IDB_REPORT_INTERNAL_ERR();
908 0 : return NS_ERROR_OUT_OF_MEMORY;
909 : }
910 : }
911 :
912 0 : MOZ_ASSERT(blobDataIter == blobDataEnd);
913 :
914 0 : return NS_OK;
915 : }
916 :
917 : // static
918 : template <typename T>
919 : nsresult
920 0 : ReadCompressedIndexDataValuesFromSource(T* aSource,
921 : uint32_t aColumnIndex,
922 : nsTArray<IndexDataValue>& aIndexValues)
923 : {
924 0 : MOZ_ASSERT(!NS_IsMainThread());
925 0 : MOZ_ASSERT(!IsOnBackgroundThread());
926 0 : MOZ_ASSERT(aSource);
927 0 : MOZ_ASSERT(aIndexValues.IsEmpty());
928 :
929 : int32_t columnType;
930 0 : nsresult rv = aSource->GetTypeOfIndex(aColumnIndex, &columnType);
931 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
932 0 : return rv;
933 : }
934 :
935 0 : if (columnType == mozIStorageStatement::VALUE_TYPE_NULL) {
936 0 : return NS_OK;
937 : }
938 :
939 0 : MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_BLOB);
940 :
941 : const uint8_t* blobData;
942 : uint32_t blobDataLength;
943 0 : rv = aSource->GetSharedBlob(aColumnIndex, &blobDataLength, &blobData);
944 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
945 0 : return rv;
946 : }
947 :
948 0 : if (NS_WARN_IF(!blobDataLength)) {
949 0 : IDB_REPORT_INTERNAL_ERR();
950 0 : return NS_ERROR_FILE_CORRUPTED;
951 : }
952 :
953 0 : rv = ReadCompressedIndexDataValuesFromBlob(blobData,
954 : blobDataLength,
955 : aIndexValues);
956 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
957 0 : return rv;
958 : }
959 :
960 0 : return NS_OK;
961 : }
962 :
963 : nsresult
964 0 : ReadCompressedIndexDataValues(mozIStorageStatement* aStatement,
965 : uint32_t aColumnIndex,
966 : nsTArray<IndexDataValue>& aIndexValues)
967 : {
968 : return ReadCompressedIndexDataValuesFromSource(aStatement,
969 : aColumnIndex,
970 0 : aIndexValues);
971 : }
972 :
973 : nsresult
974 0 : ReadCompressedIndexDataValues(mozIStorageValueArray* aValues,
975 : uint32_t aColumnIndex,
976 : nsTArray<IndexDataValue>& aIndexValues)
977 : {
978 : return ReadCompressedIndexDataValuesFromSource(aValues,
979 : aColumnIndex,
980 0 : aIndexValues);
981 : }
982 :
983 : nsresult
984 0 : CreateFileTables(mozIStorageConnection* aConnection)
985 : {
986 0 : AssertIsOnIOThread();
987 0 : MOZ_ASSERT(aConnection);
988 :
989 0 : AUTO_PROFILER_LABEL("CreateFileTables", STORAGE);
990 :
991 : // Table `file`
992 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
993 : "CREATE TABLE file ("
994 : "id INTEGER PRIMARY KEY, "
995 : "refcount INTEGER NOT NULL"
996 : ");"
997 0 : ));
998 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
999 0 : return rv;
1000 : }
1001 :
1002 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1003 : "CREATE TRIGGER object_data_insert_trigger "
1004 : "AFTER INSERT ON object_data "
1005 : "FOR EACH ROW "
1006 : "WHEN NEW.file_ids IS NOT NULL "
1007 : "BEGIN "
1008 : "SELECT update_refcount(NULL, NEW.file_ids); "
1009 : "END;"
1010 0 : ));
1011 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1012 0 : return rv;
1013 : }
1014 :
1015 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1016 : "CREATE TRIGGER object_data_update_trigger "
1017 : "AFTER UPDATE OF file_ids ON object_data "
1018 : "FOR EACH ROW "
1019 : "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
1020 : "BEGIN "
1021 : "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
1022 : "END;"
1023 0 : ));
1024 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1025 0 : return rv;
1026 : }
1027 :
1028 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1029 : "CREATE TRIGGER object_data_delete_trigger "
1030 : "AFTER DELETE ON object_data "
1031 : "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
1032 : "BEGIN "
1033 : "SELECT update_refcount(OLD.file_ids, NULL); "
1034 : "END;"
1035 0 : ));
1036 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1037 0 : return rv;
1038 : }
1039 :
1040 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1041 : "CREATE TRIGGER file_update_trigger "
1042 : "AFTER UPDATE ON file "
1043 : "FOR EACH ROW WHEN NEW.refcount = 0 "
1044 : "BEGIN "
1045 : "DELETE FROM file WHERE id = OLD.id; "
1046 : "END;"
1047 0 : ));
1048 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1049 0 : return rv;
1050 : }
1051 :
1052 0 : return NS_OK;
1053 : }
1054 :
1055 : nsresult
1056 0 : CreateTables(mozIStorageConnection* aConnection)
1057 : {
1058 0 : AssertIsOnIOThread();
1059 0 : MOZ_ASSERT(aConnection);
1060 :
1061 0 : AUTO_PROFILER_LABEL("CreateTables", STORAGE);
1062 :
1063 : // Table `database`
1064 :
1065 : // There are two reasons for having the origin column.
1066 : // First, we can ensure that we don't have collisions in the origin hash we
1067 : // use for the path because when we open the db we can make sure that the
1068 : // origins exactly match. Second, chrome code crawling through the idb
1069 : // directory can figure out the origin of every db without having to
1070 : // reverse-engineer our hash scheme.
1071 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1072 : "CREATE TABLE database"
1073 : "( name TEXT PRIMARY KEY"
1074 : ", origin TEXT NOT NULL"
1075 : ", version INTEGER NOT NULL DEFAULT 0"
1076 : ", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
1077 : ", last_analyze_time INTEGER NOT NULL DEFAULT 0"
1078 : ", last_vacuum_size INTEGER NOT NULL DEFAULT 0"
1079 : ") WITHOUT ROWID;"
1080 0 : ));
1081 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1082 0 : return rv;
1083 : }
1084 :
1085 : // Table `object_store`
1086 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1087 : "CREATE TABLE object_store"
1088 : "( id INTEGER PRIMARY KEY"
1089 : ", auto_increment INTEGER NOT NULL DEFAULT 0"
1090 : ", name TEXT NOT NULL"
1091 : ", key_path TEXT"
1092 : ");"
1093 0 : ));
1094 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1095 0 : return rv;
1096 : }
1097 :
1098 : // Table `object_store_index`
1099 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1100 : "CREATE TABLE object_store_index"
1101 : "( id INTEGER PRIMARY KEY"
1102 : ", object_store_id INTEGER NOT NULL"
1103 : ", name TEXT NOT NULL"
1104 : ", key_path TEXT NOT NULL"
1105 : ", unique_index INTEGER NOT NULL"
1106 : ", multientry INTEGER NOT NULL"
1107 : ", locale TEXT"
1108 : ", is_auto_locale BOOLEAN NOT NULL"
1109 : ", FOREIGN KEY (object_store_id) "
1110 : "REFERENCES object_store(id) "
1111 : ");"
1112 0 : ));
1113 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1114 0 : return rv;
1115 : }
1116 :
1117 : // Table `object_data`
1118 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1119 : "CREATE TABLE object_data"
1120 : "( object_store_id INTEGER NOT NULL"
1121 : ", key BLOB NOT NULL"
1122 : ", index_data_values BLOB DEFAULT NULL"
1123 : ", file_ids TEXT"
1124 : ", data BLOB NOT NULL"
1125 : ", PRIMARY KEY (object_store_id, key)"
1126 : ", FOREIGN KEY (object_store_id) "
1127 : "REFERENCES object_store(id) "
1128 : ") WITHOUT ROWID;"
1129 0 : ));
1130 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1131 0 : return rv;
1132 : }
1133 :
1134 : // Table `index_data`
1135 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1136 : "CREATE TABLE index_data"
1137 : "( index_id INTEGER NOT NULL"
1138 : ", value BLOB NOT NULL"
1139 : ", object_data_key BLOB NOT NULL"
1140 : ", object_store_id INTEGER NOT NULL"
1141 : ", value_locale BLOB"
1142 : ", PRIMARY KEY (index_id, value, object_data_key)"
1143 : ", FOREIGN KEY (index_id) "
1144 : "REFERENCES object_store_index(id) "
1145 : ", FOREIGN KEY (object_store_id, object_data_key) "
1146 : "REFERENCES object_data(object_store_id, key) "
1147 : ") WITHOUT ROWID;"
1148 0 : ));
1149 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1150 0 : return rv;
1151 : }
1152 :
1153 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1154 : "CREATE INDEX index_data_value_locale_index "
1155 : "ON index_data (index_id, value_locale, object_data_key, value) "
1156 : "WHERE value_locale IS NOT NULL;"
1157 0 : ));
1158 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1159 0 : return rv;
1160 : }
1161 :
1162 : // Table `unique_index_data`
1163 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1164 : "CREATE TABLE unique_index_data"
1165 : "( index_id INTEGER NOT NULL"
1166 : ", value BLOB NOT NULL"
1167 : ", object_store_id INTEGER NOT NULL"
1168 : ", object_data_key BLOB NOT NULL"
1169 : ", value_locale BLOB"
1170 : ", PRIMARY KEY (index_id, value)"
1171 : ", FOREIGN KEY (index_id) "
1172 : "REFERENCES object_store_index(id) "
1173 : ", FOREIGN KEY (object_store_id, object_data_key) "
1174 : "REFERENCES object_data(object_store_id, key) "
1175 : ") WITHOUT ROWID;"
1176 0 : ));
1177 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1178 0 : return rv;
1179 : }
1180 :
1181 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1182 : "CREATE INDEX unique_index_data_value_locale_index "
1183 : "ON unique_index_data (index_id, value_locale, object_data_key, value) "
1184 : "WHERE value_locale IS NOT NULL;"
1185 0 : ));
1186 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1187 0 : return rv;
1188 : }
1189 :
1190 0 : rv = CreateFileTables(aConnection);
1191 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1192 0 : return rv;
1193 : }
1194 :
1195 0 : rv = aConnection->SetSchemaVersion(kSQLiteSchemaVersion);
1196 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1197 0 : return rv;
1198 : }
1199 :
1200 0 : return NS_OK;
1201 : }
1202 :
1203 : nsresult
1204 0 : UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection)
1205 : {
1206 0 : AssertIsOnIOThread();
1207 0 : MOZ_ASSERT(aConnection);
1208 :
1209 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom4To5", STORAGE);
1210 :
1211 : nsresult rv;
1212 :
1213 : // All we changed is the type of the version column, so lets try to
1214 : // convert that to an integer, and if we fail, set it to 0.
1215 0 : nsCOMPtr<mozIStorageStatement> stmt;
1216 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
1217 : "SELECT name, version, dataVersion "
1218 : "FROM database"
1219 0 : ), getter_AddRefs(stmt));
1220 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1221 0 : return rv;
1222 : }
1223 :
1224 0 : nsString name;
1225 : int32_t intVersion;
1226 : int64_t dataVersion;
1227 :
1228 : {
1229 0 : mozStorageStatementScoper scoper(stmt);
1230 :
1231 : bool hasResults;
1232 0 : rv = stmt->ExecuteStep(&hasResults);
1233 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1234 0 : return rv;
1235 : }
1236 0 : if (NS_WARN_IF(!hasResults)) {
1237 0 : return NS_ERROR_FAILURE;
1238 : }
1239 :
1240 0 : nsString version;
1241 0 : rv = stmt->GetString(1, version);
1242 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1243 0 : return rv;
1244 : }
1245 :
1246 0 : intVersion = version.ToInteger(&rv);
1247 0 : if (NS_FAILED(rv)) {
1248 0 : intVersion = 0;
1249 : }
1250 :
1251 0 : rv = stmt->GetString(0, name);
1252 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1253 0 : return rv;
1254 : }
1255 :
1256 0 : rv = stmt->GetInt64(2, &dataVersion);
1257 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1258 0 : return rv;
1259 : }
1260 : }
1261 :
1262 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1263 : "DROP TABLE database"
1264 0 : ));
1265 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1266 0 : return rv;
1267 : }
1268 :
1269 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1270 : "CREATE TABLE database ("
1271 : "name TEXT NOT NULL, "
1272 : "version INTEGER NOT NULL DEFAULT 0, "
1273 : "dataVersion INTEGER NOT NULL"
1274 : ");"
1275 0 : ));
1276 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1277 0 : return rv;
1278 : }
1279 :
1280 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
1281 : "INSERT INTO database (name, version, dataVersion) "
1282 : "VALUES (:name, :version, :dataVersion)"
1283 0 : ), getter_AddRefs(stmt));
1284 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1285 0 : return rv;
1286 : }
1287 :
1288 : {
1289 0 : mozStorageStatementScoper scoper(stmt);
1290 :
1291 0 : rv = stmt->BindStringByIndex(0, name);
1292 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1293 0 : return rv;
1294 : }
1295 :
1296 0 : rv = stmt->BindInt32ByIndex(1, intVersion);
1297 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1298 0 : return rv;
1299 : }
1300 :
1301 0 : rv = stmt->BindInt64ByIndex(2, dataVersion);
1302 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1303 0 : return rv;
1304 : }
1305 :
1306 0 : rv = stmt->Execute();
1307 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1308 0 : return rv;
1309 : }
1310 : }
1311 :
1312 0 : rv = aConnection->SetSchemaVersion(5);
1313 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1314 0 : return rv;
1315 : }
1316 :
1317 0 : return NS_OK;
1318 : }
1319 :
1320 : nsresult
1321 0 : UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection)
1322 : {
1323 0 : AssertIsOnIOThread();
1324 0 : MOZ_ASSERT(aConnection);
1325 :
1326 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom5To6", STORAGE);
1327 :
1328 : // First, drop all the indexes we're no longer going to use.
1329 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1330 : "DROP INDEX key_index;"
1331 0 : ));
1332 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1333 0 : return rv;
1334 : }
1335 :
1336 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1337 : "DROP INDEX ai_key_index;"
1338 0 : ));
1339 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1340 0 : return rv;
1341 : }
1342 :
1343 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1344 : "DROP INDEX value_index;"
1345 0 : ));
1346 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1347 0 : return rv;
1348 : }
1349 :
1350 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1351 : "DROP INDEX ai_value_index;"
1352 0 : ));
1353 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1354 0 : return rv;
1355 : }
1356 :
1357 : // Now, reorder the columns of object_data to put the blob data last. We do
1358 : // this by copying into a temporary table, dropping the original, then copying
1359 : // back into a newly created table.
1360 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1361 : "CREATE TEMPORARY TABLE temp_upgrade ("
1362 : "id INTEGER PRIMARY KEY, "
1363 : "object_store_id, "
1364 : "key_value, "
1365 : "data "
1366 : ");"
1367 0 : ));
1368 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1369 0 : return rv;
1370 : }
1371 :
1372 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1373 : "INSERT INTO temp_upgrade "
1374 : "SELECT id, object_store_id, key_value, data "
1375 : "FROM object_data;"
1376 0 : ));
1377 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1378 0 : return rv;
1379 : }
1380 :
1381 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1382 : "DROP TABLE object_data;"
1383 0 : ));
1384 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1385 0 : return rv;
1386 : }
1387 :
1388 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1389 : "CREATE TABLE object_data ("
1390 : "id INTEGER PRIMARY KEY, "
1391 : "object_store_id INTEGER NOT NULL, "
1392 : "key_value DEFAULT NULL, "
1393 : "data BLOB NOT NULL, "
1394 : "UNIQUE (object_store_id, key_value), "
1395 : "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1396 : "CASCADE"
1397 : ");"
1398 0 : ));
1399 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1400 0 : return rv;
1401 : }
1402 :
1403 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1404 : "INSERT INTO object_data "
1405 : "SELECT id, object_store_id, key_value, data "
1406 : "FROM temp_upgrade;"
1407 0 : ));
1408 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1409 0 : return rv;
1410 : }
1411 :
1412 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1413 : "DROP TABLE temp_upgrade;"
1414 0 : ));
1415 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1416 0 : return rv;
1417 : }
1418 :
1419 : // We need to add a unique constraint to our ai_object_data table. Copy all
1420 : // the data out of it using a temporary table as before.
1421 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1422 : "CREATE TEMPORARY TABLE temp_upgrade ("
1423 : "id INTEGER PRIMARY KEY, "
1424 : "object_store_id, "
1425 : "data "
1426 : ");"
1427 0 : ));
1428 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1429 0 : return rv;
1430 : }
1431 :
1432 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1433 : "INSERT INTO temp_upgrade "
1434 : "SELECT id, object_store_id, data "
1435 : "FROM ai_object_data;"
1436 0 : ));
1437 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1438 0 : return rv;
1439 : }
1440 :
1441 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1442 : "DROP TABLE ai_object_data;"
1443 0 : ));
1444 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1445 0 : return rv;
1446 : }
1447 :
1448 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1449 : "CREATE TABLE ai_object_data ("
1450 : "id INTEGER PRIMARY KEY AUTOINCREMENT, "
1451 : "object_store_id INTEGER NOT NULL, "
1452 : "data BLOB NOT NULL, "
1453 : "UNIQUE (object_store_id, id), "
1454 : "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1455 : "CASCADE"
1456 : ");"
1457 0 : ));
1458 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1459 0 : return rv;
1460 : }
1461 :
1462 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1463 : "INSERT INTO ai_object_data "
1464 : "SELECT id, object_store_id, data "
1465 : "FROM temp_upgrade;"
1466 0 : ));
1467 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1468 0 : return rv;
1469 : }
1470 :
1471 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1472 : "DROP TABLE temp_upgrade;"
1473 0 : ));
1474 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1475 0 : return rv;
1476 : }
1477 :
1478 : // Fix up the index_data table. We're reordering the columns as well as
1479 : // changing the primary key from being a simple id to being a composite.
1480 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1481 : "CREATE TEMPORARY TABLE temp_upgrade ("
1482 : "index_id, "
1483 : "value, "
1484 : "object_data_key, "
1485 : "object_data_id "
1486 : ");"
1487 0 : ));
1488 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1489 0 : return rv;
1490 : }
1491 :
1492 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1493 : "INSERT INTO temp_upgrade "
1494 : "SELECT index_id, value, object_data_key, object_data_id "
1495 : "FROM index_data;"
1496 0 : ));
1497 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1498 0 : return rv;
1499 : }
1500 :
1501 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1502 : "DROP TABLE index_data;"
1503 0 : ));
1504 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1505 0 : return rv;
1506 : }
1507 :
1508 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1509 : "CREATE TABLE index_data ("
1510 : "index_id INTEGER NOT NULL, "
1511 : "value NOT NULL, "
1512 : "object_data_key NOT NULL, "
1513 : "object_data_id INTEGER NOT NULL, "
1514 : "PRIMARY KEY (index_id, value, object_data_key), "
1515 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1516 : "CASCADE, "
1517 : "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1518 : "CASCADE"
1519 : ");"
1520 0 : ));
1521 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1522 0 : return rv;
1523 : }
1524 :
1525 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1526 : "INSERT OR IGNORE INTO index_data "
1527 : "SELECT index_id, value, object_data_key, object_data_id "
1528 : "FROM temp_upgrade;"
1529 0 : ));
1530 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1531 0 : return rv;
1532 : }
1533 :
1534 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1535 : "DROP TABLE temp_upgrade;"
1536 0 : ));
1537 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1538 0 : return rv;
1539 : }
1540 :
1541 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1542 : "CREATE INDEX index_data_object_data_id_index "
1543 : "ON index_data (object_data_id);"
1544 0 : ));
1545 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1546 0 : return rv;
1547 : }
1548 :
1549 : // Fix up the unique_index_data table. We're reordering the columns as well as
1550 : // changing the primary key from being a simple id to being a composite.
1551 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1552 : "CREATE TEMPORARY TABLE temp_upgrade ("
1553 : "index_id, "
1554 : "value, "
1555 : "object_data_key, "
1556 : "object_data_id "
1557 : ");"
1558 0 : ));
1559 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1560 0 : return rv;
1561 : }
1562 :
1563 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1564 : "INSERT INTO temp_upgrade "
1565 : "SELECT index_id, value, object_data_key, object_data_id "
1566 : "FROM unique_index_data;"
1567 0 : ));
1568 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1569 0 : return rv;
1570 : }
1571 :
1572 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1573 : "DROP TABLE unique_index_data;"
1574 0 : ));
1575 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1576 0 : return rv;
1577 : }
1578 :
1579 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1580 : "CREATE TABLE unique_index_data ("
1581 : "index_id INTEGER NOT NULL, "
1582 : "value NOT NULL, "
1583 : "object_data_key NOT NULL, "
1584 : "object_data_id INTEGER NOT NULL, "
1585 : "PRIMARY KEY (index_id, value, object_data_key), "
1586 : "UNIQUE (index_id, value), "
1587 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1588 : "CASCADE "
1589 : "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1590 : "CASCADE"
1591 : ");"
1592 0 : ));
1593 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1594 0 : return rv;
1595 : }
1596 :
1597 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1598 : "INSERT INTO unique_index_data "
1599 : "SELECT index_id, value, object_data_key, object_data_id "
1600 : "FROM temp_upgrade;"
1601 0 : ));
1602 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1603 0 : return rv;
1604 : }
1605 :
1606 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1607 : "DROP TABLE temp_upgrade;"
1608 0 : ));
1609 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1610 0 : return rv;
1611 : }
1612 :
1613 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1614 : "CREATE INDEX unique_index_data_object_data_id_index "
1615 : "ON unique_index_data (object_data_id);"
1616 0 : ));
1617 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1618 0 : return rv;
1619 : }
1620 :
1621 : // Fix up the ai_index_data table. We're reordering the columns as well as
1622 : // changing the primary key from being a simple id to being a composite.
1623 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1624 : "CREATE TEMPORARY TABLE temp_upgrade ("
1625 : "index_id, "
1626 : "value, "
1627 : "ai_object_data_id "
1628 : ");"
1629 0 : ));
1630 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1631 0 : return rv;
1632 : }
1633 :
1634 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1635 : "INSERT INTO temp_upgrade "
1636 : "SELECT index_id, value, ai_object_data_id "
1637 : "FROM ai_index_data;"
1638 0 : ));
1639 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1640 0 : return rv;
1641 : }
1642 :
1643 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1644 : "DROP TABLE ai_index_data;"
1645 0 : ));
1646 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1647 0 : return rv;
1648 : }
1649 :
1650 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1651 : "CREATE TABLE ai_index_data ("
1652 : "index_id INTEGER NOT NULL, "
1653 : "value NOT NULL, "
1654 : "ai_object_data_id INTEGER NOT NULL, "
1655 : "PRIMARY KEY (index_id, value, ai_object_data_id), "
1656 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1657 : "CASCADE, "
1658 : "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
1659 : "CASCADE"
1660 : ");"
1661 0 : ));
1662 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1663 0 : return rv;
1664 : }
1665 :
1666 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1667 : "INSERT OR IGNORE INTO ai_index_data "
1668 : "SELECT index_id, value, ai_object_data_id "
1669 : "FROM temp_upgrade;"
1670 0 : ));
1671 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1672 0 : return rv;
1673 : }
1674 :
1675 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1676 : "DROP TABLE temp_upgrade;"
1677 0 : ));
1678 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1679 0 : return rv;
1680 : }
1681 :
1682 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1683 : "CREATE INDEX ai_index_data_ai_object_data_id_index "
1684 : "ON ai_index_data (ai_object_data_id);"
1685 0 : ));
1686 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1687 0 : return rv;
1688 : }
1689 :
1690 : // Fix up the ai_unique_index_data table. We're reordering the columns as well
1691 : // as changing the primary key from being a simple id to being a composite.
1692 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1693 : "CREATE TEMPORARY TABLE temp_upgrade ("
1694 : "index_id, "
1695 : "value, "
1696 : "ai_object_data_id "
1697 : ");"
1698 0 : ));
1699 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1700 0 : return rv;
1701 : }
1702 :
1703 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1704 : "INSERT INTO temp_upgrade "
1705 : "SELECT index_id, value, ai_object_data_id "
1706 : "FROM ai_unique_index_data;"
1707 0 : ));
1708 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1709 0 : return rv;
1710 : }
1711 :
1712 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1713 : "DROP TABLE ai_unique_index_data;"
1714 0 : ));
1715 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1716 0 : return rv;
1717 : }
1718 :
1719 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1720 : "CREATE TABLE ai_unique_index_data ("
1721 : "index_id INTEGER NOT NULL, "
1722 : "value NOT NULL, "
1723 : "ai_object_data_id INTEGER NOT NULL, "
1724 : "UNIQUE (index_id, value), "
1725 : "PRIMARY KEY (index_id, value, ai_object_data_id), "
1726 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1727 : "CASCADE, "
1728 : "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
1729 : "CASCADE"
1730 : ");"
1731 0 : ));
1732 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1733 0 : return rv;
1734 : }
1735 :
1736 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1737 : "INSERT INTO ai_unique_index_data "
1738 : "SELECT index_id, value, ai_object_data_id "
1739 : "FROM temp_upgrade;"
1740 0 : ));
1741 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1742 0 : return rv;
1743 : }
1744 :
1745 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1746 : "DROP TABLE temp_upgrade;"
1747 0 : ));
1748 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1749 0 : return rv;
1750 : }
1751 :
1752 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1753 : "CREATE INDEX ai_unique_index_data_ai_object_data_id_index "
1754 : "ON ai_unique_index_data (ai_object_data_id);"
1755 0 : ));
1756 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1757 0 : return rv;
1758 : }
1759 :
1760 0 : rv = aConnection->SetSchemaVersion(6);
1761 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1762 0 : return rv;
1763 : }
1764 :
1765 0 : return NS_OK;
1766 : }
1767 :
1768 : nsresult
1769 0 : UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection)
1770 : {
1771 0 : AssertIsOnIOThread();
1772 0 : MOZ_ASSERT(aConnection);
1773 :
1774 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom6To7", STORAGE);
1775 :
1776 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1777 : "CREATE TEMPORARY TABLE temp_upgrade ("
1778 : "id, "
1779 : "name, "
1780 : "key_path, "
1781 : "auto_increment"
1782 : ");"
1783 0 : ));
1784 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1785 0 : return rv;
1786 : }
1787 :
1788 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1789 : "INSERT INTO temp_upgrade "
1790 : "SELECT id, name, key_path, auto_increment "
1791 : "FROM object_store;"
1792 0 : ));
1793 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1794 0 : return rv;
1795 : }
1796 :
1797 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1798 : "DROP TABLE object_store;"
1799 0 : ));
1800 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1801 0 : return rv;
1802 : }
1803 :
1804 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1805 : "CREATE TABLE object_store ("
1806 : "id INTEGER PRIMARY KEY, "
1807 : "auto_increment INTEGER NOT NULL DEFAULT 0, "
1808 : "name TEXT NOT NULL, "
1809 : "key_path TEXT, "
1810 : "UNIQUE (name)"
1811 : ");"
1812 0 : ));
1813 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1814 0 : return rv;
1815 : }
1816 :
1817 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1818 : "INSERT INTO object_store "
1819 : "SELECT id, auto_increment, name, nullif(key_path, '') "
1820 : "FROM temp_upgrade;"
1821 0 : ));
1822 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1823 0 : return rv;
1824 : }
1825 :
1826 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1827 : "DROP TABLE temp_upgrade;"
1828 0 : ));
1829 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1830 0 : return rv;
1831 : }
1832 :
1833 0 : rv = aConnection->SetSchemaVersion(7);
1834 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1835 0 : return rv;
1836 : }
1837 :
1838 0 : return NS_OK;
1839 : }
1840 :
1841 : nsresult
1842 0 : UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection)
1843 : {
1844 0 : AssertIsOnIOThread();
1845 0 : MOZ_ASSERT(aConnection);
1846 :
1847 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom7To8", STORAGE);
1848 :
1849 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1850 : "CREATE TEMPORARY TABLE temp_upgrade ("
1851 : "id, "
1852 : "object_store_id, "
1853 : "name, "
1854 : "key_path, "
1855 : "unique_index, "
1856 : "object_store_autoincrement"
1857 : ");"
1858 0 : ));
1859 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1860 0 : return rv;
1861 : }
1862 :
1863 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1864 : "INSERT INTO temp_upgrade "
1865 : "SELECT id, object_store_id, name, key_path, "
1866 : "unique_index, object_store_autoincrement "
1867 : "FROM object_store_index;"
1868 0 : ));
1869 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1870 0 : return rv;
1871 : }
1872 :
1873 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1874 : "DROP TABLE object_store_index;"
1875 0 : ));
1876 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1877 0 : return rv;
1878 : }
1879 :
1880 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1881 : "CREATE TABLE object_store_index ("
1882 : "id INTEGER, "
1883 : "object_store_id INTEGER NOT NULL, "
1884 : "name TEXT NOT NULL, "
1885 : "key_path TEXT NOT NULL, "
1886 : "unique_index INTEGER NOT NULL, "
1887 : "multientry INTEGER NOT NULL, "
1888 : "object_store_autoincrement INTERGER NOT NULL, "
1889 : "PRIMARY KEY (id), "
1890 : "UNIQUE (object_store_id, name), "
1891 : "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1892 : "CASCADE"
1893 : ");"
1894 0 : ));
1895 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1896 0 : return rv;
1897 : }
1898 :
1899 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1900 : "INSERT INTO object_store_index "
1901 : "SELECT id, object_store_id, name, key_path, "
1902 : "unique_index, 0, object_store_autoincrement "
1903 : "FROM temp_upgrade;"
1904 0 : ));
1905 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1906 0 : return rv;
1907 : }
1908 :
1909 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1910 : "DROP TABLE temp_upgrade;"
1911 0 : ));
1912 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1913 0 : return rv;
1914 : }
1915 :
1916 0 : rv = aConnection->SetSchemaVersion(8);
1917 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1918 0 : return rv;
1919 : }
1920 :
1921 0 : return NS_OK;
1922 : }
1923 :
1924 0 : class CompressDataBlobsFunction final
1925 : : public mozIStorageFunction
1926 : {
1927 : public:
1928 : NS_DECL_ISUPPORTS
1929 :
1930 : private:
1931 : ~CompressDataBlobsFunction() = default;
1932 :
1933 : NS_IMETHOD
1934 0 : OnFunctionCall(mozIStorageValueArray* aArguments,
1935 : nsIVariant** aResult) override
1936 : {
1937 0 : MOZ_ASSERT(aArguments);
1938 0 : MOZ_ASSERT(aResult);
1939 :
1940 0 : AUTO_PROFILER_LABEL("CompressDataBlobsFunction::OnFunctionCall", STORAGE);
1941 :
1942 : uint32_t argc;
1943 0 : nsresult rv = aArguments->GetNumEntries(&argc);
1944 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1945 0 : return rv;
1946 : }
1947 :
1948 0 : if (argc != 1) {
1949 0 : NS_WARNING("Don't call me with the wrong number of arguments!");
1950 0 : return NS_ERROR_UNEXPECTED;
1951 : }
1952 :
1953 : int32_t type;
1954 0 : rv = aArguments->GetTypeOfIndex(0, &type);
1955 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1956 0 : return rv;
1957 : }
1958 :
1959 0 : if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
1960 0 : NS_WARNING("Don't call me with the wrong type of arguments!");
1961 0 : return NS_ERROR_UNEXPECTED;
1962 : }
1963 :
1964 : const uint8_t* uncompressed;
1965 : uint32_t uncompressedLength;
1966 0 : rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed);
1967 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1968 0 : return rv;
1969 : }
1970 :
1971 0 : size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
1972 : UniqueFreePtr<uint8_t> compressed(
1973 0 : static_cast<uint8_t*>(malloc(compressedLength)));
1974 0 : if (NS_WARN_IF(!compressed)) {
1975 0 : return NS_ERROR_OUT_OF_MEMORY;
1976 : }
1977 :
1978 0 : snappy::RawCompress(reinterpret_cast<const char*>(uncompressed),
1979 : uncompressedLength,
1980 0 : reinterpret_cast<char*>(compressed.get()),
1981 0 : &compressedLength);
1982 :
1983 0 : std::pair<uint8_t *, int> data(compressed.release(),
1984 0 : int(compressedLength));
1985 :
1986 0 : nsCOMPtr<nsIVariant> result = new mozilla::storage::AdoptedBlobVariant(data);
1987 :
1988 0 : result.forget(aResult);
1989 0 : return NS_OK;
1990 : }
1991 : };
1992 :
1993 : nsresult
1994 0 : UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection)
1995 : {
1996 0 : AssertIsOnIOThread();
1997 0 : MOZ_ASSERT(aConnection);
1998 :
1999 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom8To9_0", STORAGE);
2000 :
2001 : // We no longer use the dataVersion column.
2002 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2003 : "UPDATE database SET dataVersion = 0;"
2004 0 : ));
2005 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2006 0 : return rv;
2007 : }
2008 :
2009 0 : nsCOMPtr<mozIStorageFunction> compressor = new CompressDataBlobsFunction();
2010 :
2011 0 : NS_NAMED_LITERAL_CSTRING(compressorName, "compress");
2012 :
2013 0 : rv = aConnection->CreateFunction(compressorName, 1, compressor);
2014 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2015 0 : return rv;
2016 : }
2017 :
2018 : // Turn off foreign key constraints before we do anything here.
2019 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2020 : "UPDATE object_data SET data = compress(data);"
2021 0 : ));
2022 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2023 0 : return rv;
2024 : }
2025 :
2026 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2027 : "UPDATE ai_object_data SET data = compress(data);"
2028 0 : ));
2029 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2030 0 : return rv;
2031 : }
2032 :
2033 0 : rv = aConnection->RemoveFunction(compressorName);
2034 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2035 0 : return rv;
2036 : }
2037 :
2038 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0));
2039 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2040 0 : return rv;
2041 : }
2042 :
2043 0 : return NS_OK;
2044 : }
2045 :
2046 : nsresult
2047 0 : UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection)
2048 : {
2049 0 : AssertIsOnIOThread();
2050 0 : MOZ_ASSERT(aConnection);
2051 :
2052 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom9_0To10_0", STORAGE);
2053 :
2054 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2055 : "ALTER TABLE object_data ADD COLUMN file_ids TEXT;"
2056 0 : ));
2057 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2058 0 : return rv;
2059 : }
2060 :
2061 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2062 : "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;"
2063 0 : ));
2064 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2065 0 : return rv;
2066 : }
2067 :
2068 0 : rv = CreateFileTables(aConnection);
2069 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2070 0 : return rv;
2071 : }
2072 :
2073 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0));
2074 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2075 0 : return rv;
2076 : }
2077 :
2078 0 : return NS_OK;
2079 : }
2080 :
2081 : nsresult
2082 0 : UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection)
2083 : {
2084 0 : AssertIsOnIOThread();
2085 0 : MOZ_ASSERT(aConnection);
2086 :
2087 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom10_0To11_0", STORAGE);
2088 :
2089 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2090 : "CREATE TEMPORARY TABLE temp_upgrade ("
2091 : "id, "
2092 : "object_store_id, "
2093 : "name, "
2094 : "key_path, "
2095 : "unique_index, "
2096 : "multientry"
2097 : ");"
2098 0 : ));
2099 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2100 0 : return rv;
2101 : }
2102 :
2103 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2104 : "INSERT INTO temp_upgrade "
2105 : "SELECT id, object_store_id, name, key_path, "
2106 : "unique_index, multientry "
2107 : "FROM object_store_index;"
2108 0 : ));
2109 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2110 0 : return rv;
2111 : }
2112 :
2113 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2114 : "DROP TABLE object_store_index;"
2115 0 : ));
2116 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2117 0 : return rv;
2118 : }
2119 :
2120 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2121 : "CREATE TABLE object_store_index ("
2122 : "id INTEGER PRIMARY KEY, "
2123 : "object_store_id INTEGER NOT NULL, "
2124 : "name TEXT NOT NULL, "
2125 : "key_path TEXT NOT NULL, "
2126 : "unique_index INTEGER NOT NULL, "
2127 : "multientry INTEGER NOT NULL, "
2128 : "UNIQUE (object_store_id, name), "
2129 : "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
2130 : "CASCADE"
2131 : ");"
2132 0 : ));
2133 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2134 0 : return rv;
2135 : }
2136 :
2137 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2138 : "INSERT INTO object_store_index "
2139 : "SELECT id, object_store_id, name, key_path, "
2140 : "unique_index, multientry "
2141 : "FROM temp_upgrade;"
2142 0 : ));
2143 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2144 0 : return rv;
2145 : }
2146 :
2147 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2148 : "DROP TABLE temp_upgrade;"
2149 0 : ));
2150 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2151 0 : return rv;
2152 : }
2153 :
2154 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2155 : "DROP TRIGGER object_data_insert_trigger;"
2156 0 : ));
2157 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2158 0 : return rv;
2159 : }
2160 :
2161 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2162 : "INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
2163 : "SELECT object_store_id, id, data, file_ids "
2164 : "FROM ai_object_data;"
2165 0 : ));
2166 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2167 0 : return rv;
2168 : }
2169 :
2170 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2171 : "CREATE TRIGGER object_data_insert_trigger "
2172 : "AFTER INSERT ON object_data "
2173 : "FOR EACH ROW "
2174 : "WHEN NEW.file_ids IS NOT NULL "
2175 : "BEGIN "
2176 : "SELECT update_refcount(NULL, NEW.file_ids); "
2177 : "END;"
2178 0 : ));
2179 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2180 0 : return rv;
2181 : }
2182 :
2183 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2184 : "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) "
2185 : "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id "
2186 : "FROM ai_index_data "
2187 : "INNER JOIN object_store_index ON "
2188 : "object_store_index.id = ai_index_data.index_id "
2189 : "INNER JOIN object_data ON "
2190 : "object_data.object_store_id = object_store_index.object_store_id AND "
2191 : "object_data.key_value = ai_index_data.ai_object_data_id;"
2192 0 : ));
2193 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2194 0 : return rv;
2195 : }
2196 :
2197 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2198 : "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) "
2199 : "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id "
2200 : "FROM ai_unique_index_data "
2201 : "INNER JOIN object_store_index ON "
2202 : "object_store_index.id = ai_unique_index_data.index_id "
2203 : "INNER JOIN object_data ON "
2204 : "object_data.object_store_id = object_store_index.object_store_id AND "
2205 : "object_data.key_value = ai_unique_index_data.ai_object_data_id;"
2206 0 : ));
2207 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2208 0 : return rv;
2209 : }
2210 :
2211 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2212 : "UPDATE object_store "
2213 : "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 "
2214 : "WHERE auto_increment;"
2215 0 : ));
2216 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2217 0 : return rv;
2218 : }
2219 :
2220 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2221 : "DROP TABLE ai_unique_index_data;"
2222 0 : ));
2223 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2224 0 : return rv;
2225 : }
2226 :
2227 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2228 : "DROP TABLE ai_index_data;"
2229 0 : ));
2230 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2231 0 : return rv;
2232 : }
2233 :
2234 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2235 : "DROP TABLE ai_object_data;"
2236 0 : ));
2237 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2238 0 : return rv;
2239 : }
2240 :
2241 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0));
2242 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2243 0 : return rv;
2244 : }
2245 :
2246 0 : return NS_OK;
2247 : }
2248 :
2249 0 : class EncodeKeysFunction final
2250 : : public mozIStorageFunction
2251 : {
2252 : public:
2253 : NS_DECL_ISUPPORTS
2254 :
2255 : private:
2256 : ~EncodeKeysFunction() = default;
2257 :
2258 : NS_IMETHOD
2259 0 : OnFunctionCall(mozIStorageValueArray* aArguments,
2260 : nsIVariant** aResult) override
2261 : {
2262 0 : MOZ_ASSERT(aArguments);
2263 0 : MOZ_ASSERT(aResult);
2264 :
2265 0 : AUTO_PROFILER_LABEL("EncodeKeysFunction::OnFunctionCall", STORAGE);
2266 :
2267 : uint32_t argc;
2268 0 : nsresult rv = aArguments->GetNumEntries(&argc);
2269 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2270 0 : return rv;
2271 : }
2272 :
2273 0 : if (argc != 1) {
2274 0 : NS_WARNING("Don't call me with the wrong number of arguments!");
2275 0 : return NS_ERROR_UNEXPECTED;
2276 : }
2277 :
2278 : int32_t type;
2279 0 : rv = aArguments->GetTypeOfIndex(0, &type);
2280 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2281 0 : return rv;
2282 : }
2283 :
2284 0 : Key key;
2285 0 : if (type == mozIStorageStatement::VALUE_TYPE_INTEGER) {
2286 : int64_t intKey;
2287 0 : aArguments->GetInt64(0, &intKey);
2288 0 : key.SetFromInteger(intKey);
2289 0 : } else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) {
2290 0 : nsString stringKey;
2291 0 : aArguments->GetString(0, stringKey);
2292 0 : key.SetFromString(stringKey);
2293 : } else {
2294 0 : NS_WARNING("Don't call me with the wrong type of arguments!");
2295 0 : return NS_ERROR_UNEXPECTED;
2296 : }
2297 :
2298 0 : const nsCString& buffer = key.GetBuffer();
2299 :
2300 0 : std::pair<const void *, int> data(static_cast<const void*>(buffer.get()),
2301 0 : int(buffer.Length()));
2302 :
2303 0 : nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
2304 :
2305 0 : result.forget(aResult);
2306 0 : return NS_OK;
2307 : }
2308 : };
2309 :
2310 : nsresult
2311 0 : UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection)
2312 : {
2313 0 : AssertIsOnIOThread();
2314 0 : MOZ_ASSERT(aConnection);
2315 :
2316 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom11_0To12_0", STORAGE);
2317 :
2318 0 : NS_NAMED_LITERAL_CSTRING(encoderName, "encode");
2319 :
2320 0 : nsCOMPtr<mozIStorageFunction> encoder = new EncodeKeysFunction();
2321 :
2322 0 : nsresult rv = aConnection->CreateFunction(encoderName, 1, encoder);
2323 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2324 0 : return rv;
2325 : }
2326 :
2327 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2328 : "CREATE TEMPORARY TABLE temp_upgrade ("
2329 : "id INTEGER PRIMARY KEY, "
2330 : "object_store_id, "
2331 : "key_value, "
2332 : "data, "
2333 : "file_ids "
2334 : ");"
2335 0 : ));
2336 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2337 0 : return rv;
2338 : }
2339 :
2340 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2341 : "INSERT INTO temp_upgrade "
2342 : "SELECT id, object_store_id, encode(key_value), data, file_ids "
2343 : "FROM object_data;"
2344 0 : ));
2345 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2346 0 : return rv;
2347 : }
2348 :
2349 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2350 : "DROP TABLE object_data;"
2351 0 : ));
2352 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2353 0 : return rv;
2354 : }
2355 :
2356 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2357 : "CREATE TABLE object_data ("
2358 : "id INTEGER PRIMARY KEY, "
2359 : "object_store_id INTEGER NOT NULL, "
2360 : "key_value BLOB DEFAULT NULL, "
2361 : "file_ids TEXT, "
2362 : "data BLOB NOT NULL, "
2363 : "UNIQUE (object_store_id, key_value), "
2364 : "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
2365 : "CASCADE"
2366 : ");"
2367 0 : ));
2368 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2369 0 : return rv;
2370 : }
2371 :
2372 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2373 : "INSERT INTO object_data "
2374 : "SELECT id, object_store_id, key_value, file_ids, data "
2375 : "FROM temp_upgrade;"
2376 0 : ));
2377 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2378 0 : return rv;
2379 : }
2380 :
2381 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2382 : "DROP TABLE temp_upgrade;"
2383 0 : ));
2384 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2385 0 : return rv;
2386 : }
2387 :
2388 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2389 : "CREATE TRIGGER object_data_insert_trigger "
2390 : "AFTER INSERT ON object_data "
2391 : "FOR EACH ROW "
2392 : "WHEN NEW.file_ids IS NOT NULL "
2393 : "BEGIN "
2394 : "SELECT update_refcount(NULL, NEW.file_ids); "
2395 : "END;"
2396 0 : ));
2397 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2398 0 : return rv;
2399 : }
2400 :
2401 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2402 : "CREATE TRIGGER object_data_update_trigger "
2403 : "AFTER UPDATE OF file_ids ON object_data "
2404 : "FOR EACH ROW "
2405 : "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
2406 : "BEGIN "
2407 : "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
2408 : "END;"
2409 0 : ));
2410 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2411 0 : return rv;
2412 : }
2413 :
2414 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2415 : "CREATE TRIGGER object_data_delete_trigger "
2416 : "AFTER DELETE ON object_data "
2417 : "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
2418 : "BEGIN "
2419 : "SELECT update_refcount(OLD.file_ids, NULL); "
2420 : "END;"
2421 0 : ));
2422 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2423 0 : return rv;
2424 : }
2425 :
2426 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2427 : "CREATE TEMPORARY TABLE temp_upgrade ("
2428 : "index_id, "
2429 : "value, "
2430 : "object_data_key, "
2431 : "object_data_id "
2432 : ");"
2433 0 : ));
2434 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2435 0 : return rv;
2436 : }
2437 :
2438 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2439 : "INSERT INTO temp_upgrade "
2440 : "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
2441 : "FROM index_data;"
2442 0 : ));
2443 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2444 0 : return rv;
2445 : }
2446 :
2447 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2448 : "DROP TABLE index_data;"
2449 0 : ));
2450 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2451 0 : return rv;
2452 : }
2453 :
2454 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2455 : "CREATE TABLE index_data ("
2456 : "index_id INTEGER NOT NULL, "
2457 : "value BLOB NOT NULL, "
2458 : "object_data_key BLOB NOT NULL, "
2459 : "object_data_id INTEGER NOT NULL, "
2460 : "PRIMARY KEY (index_id, value, object_data_key), "
2461 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
2462 : "CASCADE, "
2463 : "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
2464 : "CASCADE"
2465 : ");"
2466 0 : ));
2467 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2468 0 : return rv;
2469 : }
2470 :
2471 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2472 : "INSERT INTO index_data "
2473 : "SELECT index_id, value, object_data_key, object_data_id "
2474 : "FROM temp_upgrade;"
2475 0 : ));
2476 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2477 0 : return rv;
2478 : }
2479 :
2480 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2481 : "DROP TABLE temp_upgrade;"
2482 0 : ));
2483 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2484 0 : return rv;
2485 : }
2486 :
2487 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2488 : "CREATE INDEX index_data_object_data_id_index "
2489 : "ON index_data (object_data_id);"
2490 0 : ));
2491 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2492 0 : return rv;
2493 : }
2494 :
2495 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2496 : "CREATE TEMPORARY TABLE temp_upgrade ("
2497 : "index_id, "
2498 : "value, "
2499 : "object_data_key, "
2500 : "object_data_id "
2501 : ");"
2502 0 : ));
2503 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2504 0 : return rv;
2505 : }
2506 :
2507 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2508 : "INSERT INTO temp_upgrade "
2509 : "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
2510 : "FROM unique_index_data;"
2511 0 : ));
2512 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2513 0 : return rv;
2514 : }
2515 :
2516 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2517 : "DROP TABLE unique_index_data;"
2518 0 : ));
2519 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2520 0 : return rv;
2521 : }
2522 :
2523 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2524 : "CREATE TABLE unique_index_data ("
2525 : "index_id INTEGER NOT NULL, "
2526 : "value BLOB NOT NULL, "
2527 : "object_data_key BLOB NOT NULL, "
2528 : "object_data_id INTEGER NOT NULL, "
2529 : "PRIMARY KEY (index_id, value, object_data_key), "
2530 : "UNIQUE (index_id, value), "
2531 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
2532 : "CASCADE "
2533 : "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
2534 : "CASCADE"
2535 : ");"
2536 0 : ));
2537 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2538 0 : return rv;
2539 : }
2540 :
2541 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2542 : "INSERT INTO unique_index_data "
2543 : "SELECT index_id, value, object_data_key, object_data_id "
2544 : "FROM temp_upgrade;"
2545 0 : ));
2546 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2547 0 : return rv;
2548 : }
2549 :
2550 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2551 : "DROP TABLE temp_upgrade;"
2552 0 : ));
2553 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2554 0 : return rv;
2555 : }
2556 :
2557 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2558 : "CREATE INDEX unique_index_data_object_data_id_index "
2559 : "ON unique_index_data (object_data_id);"
2560 0 : ));
2561 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2562 0 : return rv;
2563 : }
2564 :
2565 0 : rv = aConnection->RemoveFunction(encoderName);
2566 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2567 0 : return rv;
2568 : }
2569 :
2570 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(12, 0));
2571 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2572 0 : return rv;
2573 : }
2574 :
2575 0 : return NS_OK;
2576 : }
2577 :
2578 : nsresult
2579 0 : UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection,
2580 : bool* aVacuumNeeded)
2581 : {
2582 0 : AssertIsOnIOThread();
2583 0 : MOZ_ASSERT(aConnection);
2584 :
2585 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom12_0To13_0", STORAGE);
2586 :
2587 : nsresult rv;
2588 :
2589 : #ifdef IDB_MOBILE
2590 : int32_t defaultPageSize;
2591 : rv = aConnection->GetDefaultPageSize(&defaultPageSize);
2592 : if (NS_WARN_IF(NS_FAILED(rv))) {
2593 : return rv;
2594 : }
2595 :
2596 : // Enable auto_vacuum mode and update the page size to the platform default.
2597 : nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = ");
2598 : upgradeQuery.AppendInt(defaultPageSize);
2599 :
2600 : rv = aConnection->ExecuteSimpleSQL(upgradeQuery);
2601 : if (NS_WARN_IF(NS_FAILED(rv))) {
2602 : return rv;
2603 : }
2604 :
2605 : *aVacuumNeeded = true;
2606 : #endif
2607 :
2608 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(13, 0));
2609 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2610 0 : return rv;
2611 : }
2612 :
2613 0 : return NS_OK;
2614 : }
2615 :
2616 : nsresult
2617 0 : UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection)
2618 : {
2619 0 : AssertIsOnIOThread();
2620 0 : MOZ_ASSERT(aConnection);
2621 :
2622 : // The only change between 13 and 14 was a different structured
2623 : // clone format, but it's backwards-compatible.
2624 0 : nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(14, 0));
2625 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2626 0 : return rv;
2627 : }
2628 :
2629 0 : return NS_OK;
2630 : }
2631 :
2632 : nsresult
2633 0 : UpgradeSchemaFrom14_0To15_0(mozIStorageConnection* aConnection)
2634 : {
2635 : // The only change between 14 and 15 was a different structured
2636 : // clone format, but it's backwards-compatible.
2637 0 : nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(15, 0));
2638 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2639 0 : return rv;
2640 : }
2641 :
2642 0 : return NS_OK;
2643 : }
2644 :
2645 : nsresult
2646 0 : UpgradeSchemaFrom15_0To16_0(mozIStorageConnection* aConnection)
2647 : {
2648 : // The only change between 15 and 16 was a different structured
2649 : // clone format, but it's backwards-compatible.
2650 0 : nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(16, 0));
2651 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2652 0 : return rv;
2653 : }
2654 :
2655 0 : return NS_OK;
2656 : }
2657 :
2658 : nsresult
2659 0 : UpgradeSchemaFrom16_0To17_0(mozIStorageConnection* aConnection)
2660 : {
2661 : // The only change between 16 and 17 was a different structured
2662 : // clone format, but it's backwards-compatible.
2663 0 : nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(17, 0));
2664 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2665 0 : return rv;
2666 : }
2667 :
2668 0 : return NS_OK;
2669 : }
2670 :
2671 : class UpgradeSchemaFrom17_0To18_0Helper final
2672 : {
2673 : class InsertIndexDataValuesFunction;
2674 : class UpgradeKeyFunction;
2675 :
2676 : public:
2677 : static nsresult
2678 : DoUpgrade(mozIStorageConnection* aConnection, const nsACString& aOrigin);
2679 :
2680 : private:
2681 : static nsresult
2682 : DoUpgradeInternal(mozIStorageConnection* aConnection,
2683 : const nsACString& aOrigin);
2684 :
2685 : UpgradeSchemaFrom17_0To18_0Helper() = delete;
2686 : ~UpgradeSchemaFrom17_0To18_0Helper() = delete;
2687 : };
2688 :
2689 : class UpgradeSchemaFrom17_0To18_0Helper::InsertIndexDataValuesFunction final
2690 : : public mozIStorageFunction
2691 : {
2692 : public:
2693 0 : InsertIndexDataValuesFunction()
2694 0 : { }
2695 :
2696 : NS_DECL_ISUPPORTS
2697 :
2698 : private:
2699 : ~InsertIndexDataValuesFunction() = default;
2700 :
2701 : NS_DECL_MOZISTORAGEFUNCTION
2702 : };
2703 :
2704 0 : NS_IMPL_ISUPPORTS(UpgradeSchemaFrom17_0To18_0Helper::
2705 : InsertIndexDataValuesFunction,
2706 : mozIStorageFunction);
2707 :
2708 : NS_IMETHODIMP
2709 0 : UpgradeSchemaFrom17_0To18_0Helper::
2710 : InsertIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aValues,
2711 : nsIVariant** _retval)
2712 : {
2713 0 : MOZ_ASSERT(!NS_IsMainThread());
2714 0 : MOZ_ASSERT(!IsOnBackgroundThread());
2715 0 : MOZ_ASSERT(aValues);
2716 0 : MOZ_ASSERT(_retval);
2717 :
2718 : #ifdef DEBUG
2719 : {
2720 : uint32_t argCount;
2721 0 : MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
2722 0 : MOZ_ASSERT(argCount == 4);
2723 :
2724 : int32_t valueType;
2725 0 : MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType));
2726 0 : MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
2727 : valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
2728 :
2729 0 : MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(1, &valueType));
2730 0 : MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
2731 :
2732 0 : MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(2, &valueType));
2733 0 : MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
2734 :
2735 0 : MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(3, &valueType));
2736 0 : MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
2737 : }
2738 : #endif
2739 :
2740 : // Read out the previous value. It may be NULL, in which case we'll just end
2741 : // up with an empty array.
2742 0 : AutoTArray<IndexDataValue, 32> indexValues;
2743 0 : nsresult rv = ReadCompressedIndexDataValues(aValues, 0, indexValues);
2744 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2745 0 : return rv;
2746 : }
2747 :
2748 : int64_t indexId;
2749 0 : rv = aValues->GetInt64(1, &indexId);
2750 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2751 0 : return rv;
2752 : }
2753 :
2754 : int32_t unique;
2755 0 : rv = aValues->GetInt32(2, &unique);
2756 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2757 0 : return rv;
2758 : }
2759 :
2760 0 : Key value;
2761 0 : rv = value.SetFromValueArray(aValues, 3);
2762 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2763 0 : return rv;
2764 : }
2765 :
2766 : // Update the array with the new addition.
2767 0 : if (NS_WARN_IF(!indexValues.SetCapacity(indexValues.Length() + 1,
2768 : fallible))) {
2769 0 : IDB_REPORT_INTERNAL_ERR();
2770 0 : return NS_ERROR_OUT_OF_MEMORY;
2771 : }
2772 :
2773 0 : MOZ_ALWAYS_TRUE(
2774 : indexValues.InsertElementSorted(IndexDataValue(indexId, !!unique, value),
2775 : fallible));
2776 :
2777 : // Compress the array.
2778 0 : UniqueFreePtr<uint8_t> indexValuesBlob;
2779 : uint32_t indexValuesBlobLength;
2780 0 : rv = MakeCompressedIndexDataValues(indexValues,
2781 : indexValuesBlob,
2782 0 : &indexValuesBlobLength);
2783 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2784 0 : return rv;
2785 : }
2786 :
2787 : // The compressed blob is the result of this function.
2788 0 : std::pair<uint8_t *, int> indexValuesBlobPair(indexValuesBlob.release(),
2789 0 : indexValuesBlobLength);
2790 :
2791 : nsCOMPtr<nsIVariant> result =
2792 0 : new storage::AdoptedBlobVariant(indexValuesBlobPair);
2793 :
2794 0 : result.forget(_retval);
2795 0 : return NS_OK;
2796 : }
2797 :
2798 : class UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction final
2799 : : public mozIStorageFunction
2800 : {
2801 : public:
2802 0 : UpgradeKeyFunction()
2803 0 : { }
2804 :
2805 : static nsresult
2806 0 : CopyAndUpgradeKeyBuffer(const uint8_t* aSource,
2807 : const uint8_t* aSourceEnd,
2808 : uint8_t* aDestination)
2809 : {
2810 : return CopyAndUpgradeKeyBufferInternal(aSource,
2811 : aSourceEnd,
2812 : aDestination,
2813 : 0 /* aTagOffset */,
2814 0 : 0 /* aRecursionDepth */);
2815 : }
2816 :
2817 : NS_DECL_ISUPPORTS
2818 :
2819 : private:
2820 : ~UpgradeKeyFunction() = default;
2821 :
2822 : static nsresult
2823 : CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource,
2824 : const uint8_t* aSourceEnd,
2825 : uint8_t*& aDestination,
2826 : uint8_t aTagOffset,
2827 : uint8_t aRecursionDepth);
2828 :
2829 : static uint32_t
2830 0 : AdjustedSize(uint32_t aMaxSize,
2831 : const uint8_t* aSource,
2832 : const uint8_t* aSourceEnd)
2833 : {
2834 0 : MOZ_ASSERT(aMaxSize);
2835 0 : MOZ_ASSERT(aSource);
2836 0 : MOZ_ASSERT(aSourceEnd);
2837 0 : MOZ_ASSERT(aSource <= aSourceEnd);
2838 :
2839 0 : return std::min(aMaxSize, uint32_t(aSourceEnd - aSource));
2840 : }
2841 :
2842 : NS_DECL_MOZISTORAGEFUNCTION
2843 : };
2844 :
2845 : // static
2846 : nsresult
2847 0 : UpgradeSchemaFrom17_0To18_0Helper::
2848 : UpgradeKeyFunction::CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource,
2849 : const uint8_t* aSourceEnd,
2850 : uint8_t*& aDestination,
2851 : uint8_t aTagOffset,
2852 : uint8_t aRecursionDepth)
2853 : {
2854 0 : MOZ_ASSERT(!NS_IsMainThread());
2855 0 : MOZ_ASSERT(!IsOnBackgroundThread());
2856 0 : MOZ_ASSERT(aSource);
2857 0 : MOZ_ASSERT(*aSource);
2858 0 : MOZ_ASSERT(aSourceEnd);
2859 0 : MOZ_ASSERT(aSource < aSourceEnd);
2860 0 : MOZ_ASSERT(aDestination);
2861 0 : MOZ_ASSERT(aTagOffset <= Key::kMaxArrayCollapse);
2862 :
2863 : static constexpr uint8_t kOldNumberTag = 0x1;
2864 : static constexpr uint8_t kOldDateTag = 0x2;
2865 : static constexpr uint8_t kOldStringTag = 0x3;
2866 : static constexpr uint8_t kOldArrayTag = 0x4;
2867 : static constexpr uint8_t kOldMaxType = kOldArrayTag;
2868 :
2869 0 : if (NS_WARN_IF(aRecursionDepth > Key::kMaxRecursionDepth)) {
2870 0 : IDB_REPORT_INTERNAL_ERR();
2871 0 : return NS_ERROR_FILE_CORRUPTED;
2872 : }
2873 :
2874 0 : const uint8_t sourceTag = *aSource - (aTagOffset * kOldMaxType);
2875 0 : MOZ_ASSERT(sourceTag);
2876 :
2877 0 : if (NS_WARN_IF(sourceTag > kOldMaxType * Key::kMaxArrayCollapse)) {
2878 0 : IDB_REPORT_INTERNAL_ERR();
2879 0 : return NS_ERROR_FILE_CORRUPTED;
2880 : }
2881 :
2882 0 : if (sourceTag == kOldNumberTag || sourceTag == kOldDateTag) {
2883 : // Write the new tag.
2884 0 : *aDestination++ =
2885 0 : (sourceTag == kOldNumberTag ? Key::eFloat : Key::eDate) +
2886 0 : (aTagOffset * Key::eMaxType);
2887 0 : aSource++;
2888 :
2889 : // Numbers and Dates are encoded as 64-bit integers, but trailing 0
2890 : // bytes have been removed.
2891 : const uint32_t byteCount =
2892 0 : AdjustedSize(sizeof(uint64_t), aSource, aSourceEnd);
2893 :
2894 0 : for (uint32_t count = 0; count < byteCount; count++) {
2895 0 : *aDestination++ = *aSource++;
2896 : }
2897 :
2898 0 : return NS_OK;
2899 : }
2900 :
2901 0 : if (sourceTag == kOldStringTag) {
2902 : // Write the new tag.
2903 0 : *aDestination++ = Key::eString + (aTagOffset * Key::eMaxType);
2904 0 : aSource++;
2905 :
2906 0 : while (aSource < aSourceEnd) {
2907 0 : const uint8_t byte = *aSource++;
2908 0 : *aDestination++ = byte;
2909 :
2910 0 : if (!byte) {
2911 : // Just copied the terminator.
2912 0 : break;
2913 : }
2914 :
2915 : // Maybe copy one or two extra bytes if the byte is tagged and we have
2916 : // enough source space.
2917 0 : if (byte & 0x80) {
2918 : const uint32_t byteCount =
2919 0 : AdjustedSize((byte & 0x40) ? 2 : 1, aSource, aSourceEnd);
2920 :
2921 0 : for (uint32_t count = 0; count < byteCount; count++) {
2922 0 : *aDestination++ = *aSource++;
2923 : }
2924 : }
2925 : }
2926 :
2927 0 : return NS_OK;
2928 : }
2929 :
2930 0 : if (NS_WARN_IF(sourceTag < kOldArrayTag)) {
2931 0 : IDB_REPORT_INTERNAL_ERR();
2932 0 : return NS_ERROR_FILE_CORRUPTED;
2933 : }
2934 :
2935 0 : aTagOffset++;
2936 :
2937 0 : if (aTagOffset == Key::kMaxArrayCollapse) {
2938 0 : MOZ_ASSERT(sourceTag == kOldArrayTag);
2939 :
2940 0 : *aDestination++ = (aTagOffset * Key::eMaxType);
2941 0 : aSource++;
2942 :
2943 0 : aTagOffset = 0;
2944 : }
2945 :
2946 0 : while (aSource < aSourceEnd &&
2947 0 : (*aSource - (aTagOffset * kOldMaxType)) != Key::eTerminator) {
2948 0 : nsresult rv = CopyAndUpgradeKeyBufferInternal(aSource,
2949 : aSourceEnd,
2950 : aDestination,
2951 : aTagOffset,
2952 0 : aRecursionDepth + 1);
2953 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2954 0 : return rv;
2955 : }
2956 :
2957 0 : aTagOffset = 0;
2958 : }
2959 :
2960 0 : if (aSource < aSourceEnd) {
2961 0 : MOZ_ASSERT((*aSource - (aTagOffset * kOldMaxType)) == Key::eTerminator);
2962 0 : *aDestination++ = Key::eTerminator + (aTagOffset * Key::eMaxType);
2963 0 : aSource++;
2964 : }
2965 :
2966 0 : return NS_OK;
2967 : }
2968 :
2969 0 : NS_IMPL_ISUPPORTS(UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction,
2970 : mozIStorageFunction);
2971 :
2972 : NS_IMETHODIMP
2973 0 : UpgradeSchemaFrom17_0To18_0Helper::
2974 : UpgradeKeyFunction::OnFunctionCall(mozIStorageValueArray* aValues,
2975 : nsIVariant** _retval)
2976 : {
2977 0 : MOZ_ASSERT(!NS_IsMainThread());
2978 0 : MOZ_ASSERT(!IsOnBackgroundThread());
2979 0 : MOZ_ASSERT(aValues);
2980 0 : MOZ_ASSERT(_retval);
2981 :
2982 : #ifdef DEBUG
2983 : {
2984 : uint32_t argCount;
2985 0 : MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
2986 0 : MOZ_ASSERT(argCount == 1);
2987 :
2988 : int32_t valueType;
2989 0 : MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType));
2990 0 : MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
2991 : }
2992 : #endif
2993 :
2994 : // Dig the old key out of the values.
2995 : const uint8_t* blobData;
2996 : uint32_t blobDataLength;
2997 0 : nsresult rv = aValues->GetSharedBlob(0, &blobDataLength, &blobData);
2998 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2999 0 : return rv;
3000 : }
3001 :
3002 : // Upgrading the key doesn't change the amount of space needed to hold it.
3003 : UniqueFreePtr<uint8_t> upgradedBlobData(
3004 0 : static_cast<uint8_t*>(malloc(blobDataLength)));
3005 0 : if (NS_WARN_IF(!upgradedBlobData)) {
3006 0 : IDB_REPORT_INTERNAL_ERR();
3007 0 : return NS_ERROR_OUT_OF_MEMORY;
3008 : }
3009 :
3010 0 : rv = CopyAndUpgradeKeyBuffer(blobData,
3011 : blobData + blobDataLength,
3012 0 : upgradedBlobData.get());
3013 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3014 0 : return rv;
3015 : }
3016 :
3017 : // The upgraded key is the result of this function.
3018 0 : std::pair<uint8_t*, int> data(upgradedBlobData.release(),
3019 0 : int(blobDataLength));
3020 :
3021 0 : nsCOMPtr<nsIVariant> result = new mozilla::storage::AdoptedBlobVariant(data);
3022 :
3023 0 : result.forget(_retval);
3024 0 : return NS_OK;
3025 : }
3026 :
3027 : // static
3028 : nsresult
3029 0 : UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(mozIStorageConnection* aConnection,
3030 : const nsACString& aOrigin)
3031 : {
3032 0 : MOZ_ASSERT(aConnection);
3033 0 : MOZ_ASSERT(!aOrigin.IsEmpty());
3034 :
3035 : // Register the |upgrade_key| function.
3036 0 : RefPtr<UpgradeKeyFunction> updateFunction = new UpgradeKeyFunction();
3037 :
3038 0 : NS_NAMED_LITERAL_CSTRING(upgradeKeyFunctionName, "upgrade_key");
3039 :
3040 : nsresult rv =
3041 0 : aConnection->CreateFunction(upgradeKeyFunctionName, 1, updateFunction);
3042 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3043 0 : return rv;
3044 : }
3045 :
3046 : // Register the |insert_idv| function.
3047 : RefPtr<InsertIndexDataValuesFunction> insertIDVFunction =
3048 0 : new InsertIndexDataValuesFunction();
3049 :
3050 0 : NS_NAMED_LITERAL_CSTRING(insertIDVFunctionName, "insert_idv");
3051 :
3052 0 : rv = aConnection->CreateFunction(insertIDVFunctionName, 4, insertIDVFunction);
3053 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3054 0 : MOZ_ALWAYS_SUCCEEDS(aConnection->RemoveFunction(upgradeKeyFunctionName));
3055 0 : return rv;
3056 : }
3057 :
3058 0 : rv = DoUpgradeInternal(aConnection, aOrigin);
3059 :
3060 0 : MOZ_ALWAYS_SUCCEEDS(aConnection->RemoveFunction(upgradeKeyFunctionName));
3061 0 : MOZ_ALWAYS_SUCCEEDS(aConnection->RemoveFunction(insertIDVFunctionName));
3062 :
3063 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3064 0 : return rv;
3065 : }
3066 :
3067 0 : return NS_OK;
3068 : }
3069 :
3070 : // static
3071 : nsresult
3072 0 : UpgradeSchemaFrom17_0To18_0Helper::DoUpgradeInternal(
3073 : mozIStorageConnection* aConnection,
3074 : const nsACString& aOrigin)
3075 : {
3076 0 : MOZ_ASSERT(aConnection);
3077 0 : MOZ_ASSERT(!aOrigin.IsEmpty());
3078 :
3079 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3080 : // Drop these triggers to avoid unnecessary work during the upgrade process.
3081 : "DROP TRIGGER object_data_insert_trigger;"
3082 : "DROP TRIGGER object_data_update_trigger;"
3083 : "DROP TRIGGER object_data_delete_trigger;"
3084 0 : ));
3085 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3086 0 : return rv;
3087 : }
3088 :
3089 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3090 : // Drop these indexes before we do anything else to free disk space.
3091 : "DROP INDEX index_data_object_data_id_index;"
3092 : "DROP INDEX unique_index_data_object_data_id_index;"
3093 0 : ));
3094 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3095 0 : return rv;
3096 : }
3097 :
3098 : // Create the new tables and triggers first.
3099 :
3100 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3101 : // This will eventually become the |database| table.
3102 : "CREATE TABLE database_upgrade "
3103 : "( name TEXT PRIMARY KEY"
3104 : ", origin TEXT NOT NULL"
3105 : ", version INTEGER NOT NULL DEFAULT 0"
3106 : ", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
3107 : ", last_analyze_time INTEGER NOT NULL DEFAULT 0"
3108 : ", last_vacuum_size INTEGER NOT NULL DEFAULT 0"
3109 : ") WITHOUT ROWID;"
3110 0 : ));
3111 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3112 0 : return rv;
3113 : }
3114 :
3115 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3116 : // This will eventually become the |object_store| table.
3117 : "CREATE TABLE object_store_upgrade"
3118 : "( id INTEGER PRIMARY KEY"
3119 : ", auto_increment INTEGER NOT NULL DEFAULT 0"
3120 : ", name TEXT NOT NULL"
3121 : ", key_path TEXT"
3122 : ");"
3123 0 : ));
3124 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3125 0 : return rv;
3126 : }
3127 :
3128 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3129 : // This will eventually become the |object_store_index| table.
3130 : "CREATE TABLE object_store_index_upgrade"
3131 : "( id INTEGER PRIMARY KEY"
3132 : ", object_store_id INTEGER NOT NULL"
3133 : ", name TEXT NOT NULL"
3134 : ", key_path TEXT NOT NULL"
3135 : ", unique_index INTEGER NOT NULL"
3136 : ", multientry INTEGER NOT NULL"
3137 : ", FOREIGN KEY (object_store_id) "
3138 : "REFERENCES object_store(id) "
3139 : ");"
3140 0 : ));
3141 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3142 0 : return rv;
3143 : }
3144 :
3145 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3146 : // This will eventually become the |object_data| table.
3147 : "CREATE TABLE object_data_upgrade"
3148 : "( object_store_id INTEGER NOT NULL"
3149 : ", key BLOB NOT NULL"
3150 : ", index_data_values BLOB DEFAULT NULL"
3151 : ", file_ids TEXT"
3152 : ", data BLOB NOT NULL"
3153 : ", PRIMARY KEY (object_store_id, key)"
3154 : ", FOREIGN KEY (object_store_id) "
3155 : "REFERENCES object_store(id) "
3156 : ") WITHOUT ROWID;"
3157 0 : ));
3158 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3159 0 : return rv;
3160 : }
3161 :
3162 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3163 : // This will eventually become the |index_data| table.
3164 : "CREATE TABLE index_data_upgrade"
3165 : "( index_id INTEGER NOT NULL"
3166 : ", value BLOB NOT NULL"
3167 : ", object_data_key BLOB NOT NULL"
3168 : ", object_store_id INTEGER NOT NULL"
3169 : ", PRIMARY KEY (index_id, value, object_data_key)"
3170 : ", FOREIGN KEY (index_id) "
3171 : "REFERENCES object_store_index(id) "
3172 : ", FOREIGN KEY (object_store_id, object_data_key) "
3173 : "REFERENCES object_data(object_store_id, key) "
3174 : ") WITHOUT ROWID;"
3175 0 : ));
3176 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3177 0 : return rv;
3178 : }
3179 :
3180 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3181 : // This will eventually become the |unique_index_data| table.
3182 : "CREATE TABLE unique_index_data_upgrade"
3183 : "( index_id INTEGER NOT NULL"
3184 : ", value BLOB NOT NULL"
3185 : ", object_store_id INTEGER NOT NULL"
3186 : ", object_data_key BLOB NOT NULL"
3187 : ", PRIMARY KEY (index_id, value)"
3188 : ", FOREIGN KEY (index_id) "
3189 : "REFERENCES object_store_index(id) "
3190 : ", FOREIGN KEY (object_store_id, object_data_key) "
3191 : "REFERENCES object_data(object_store_id, key) "
3192 : ") WITHOUT ROWID;"
3193 0 : ));
3194 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3195 0 : return rv;
3196 : }
3197 :
3198 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3199 : // Temporarily store |index_data_values| that we build during the upgrade of
3200 : // the index tables. We will later move this to the |object_data| table.
3201 : "CREATE TEMPORARY TABLE temp_index_data_values "
3202 : "( object_store_id INTEGER NOT NULL"
3203 : ", key BLOB NOT NULL"
3204 : ", index_data_values BLOB DEFAULT NULL"
3205 : ", PRIMARY KEY (object_store_id, key)"
3206 : ") WITHOUT ROWID;"
3207 0 : ));
3208 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3209 0 : return rv;
3210 : }
3211 :
3212 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3213 : // These two triggers help build the |index_data_values| blobs. The nested
3214 : // SELECT statements help us achieve an "INSERT OR UPDATE"-like behavior.
3215 : "CREATE TEMPORARY TRIGGER unique_index_data_upgrade_insert_trigger "
3216 : "AFTER INSERT ON unique_index_data_upgrade "
3217 : "BEGIN "
3218 : "INSERT OR REPLACE INTO temp_index_data_values "
3219 : "VALUES "
3220 : "( NEW.object_store_id"
3221 : ", NEW.object_data_key"
3222 : ", insert_idv("
3223 : "( SELECT index_data_values "
3224 : "FROM temp_index_data_values "
3225 : "WHERE object_store_id = NEW.object_store_id "
3226 : "AND key = NEW.object_data_key "
3227 : "), NEW.index_id"
3228 : ", 1" /* unique */
3229 : ", NEW.value"
3230 : ")"
3231 : ");"
3232 : "END;"
3233 0 : ));
3234 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3235 0 : return rv;
3236 : }
3237 :
3238 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3239 : "CREATE TEMPORARY TRIGGER index_data_upgrade_insert_trigger "
3240 : "AFTER INSERT ON index_data_upgrade "
3241 : "BEGIN "
3242 : "INSERT OR REPLACE INTO temp_index_data_values "
3243 : "VALUES "
3244 : "( NEW.object_store_id"
3245 : ", NEW.object_data_key"
3246 : ", insert_idv("
3247 : "("
3248 : "SELECT index_data_values "
3249 : "FROM temp_index_data_values "
3250 : "WHERE object_store_id = NEW.object_store_id "
3251 : "AND key = NEW.object_data_key "
3252 : "), NEW.index_id"
3253 : ", 0" /* not unique */
3254 : ", NEW.value"
3255 : ")"
3256 : ");"
3257 : "END;"
3258 0 : ));
3259 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3260 0 : return rv;
3261 : }
3262 :
3263 : // Update the |unique_index_data| table to change the column order, remove the
3264 : // ON DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization.
3265 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3266 : // Insert all the data.
3267 : "INSERT INTO unique_index_data_upgrade "
3268 : "SELECT "
3269 : "unique_index_data.index_id, "
3270 : "upgrade_key(unique_index_data.value), "
3271 : "object_data.object_store_id, "
3272 : "upgrade_key(unique_index_data.object_data_key) "
3273 : "FROM unique_index_data "
3274 : "JOIN object_data "
3275 : "ON unique_index_data.object_data_id = object_data.id;"
3276 0 : ));
3277 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3278 0 : return rv;
3279 : }
3280 :
3281 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3282 : // The trigger is no longer needed.
3283 : "DROP TRIGGER unique_index_data_upgrade_insert_trigger;"
3284 0 : ));
3285 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3286 0 : return rv;
3287 : }
3288 :
3289 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3290 : // The old table is no longer needed.
3291 : "DROP TABLE unique_index_data;"
3292 0 : ));
3293 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3294 0 : return rv;
3295 : }
3296 :
3297 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3298 : // Rename the table.
3299 : "ALTER TABLE unique_index_data_upgrade "
3300 : "RENAME TO unique_index_data;"
3301 0 : ));
3302 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3303 0 : return rv;
3304 : }
3305 :
3306 : // Update the |index_data| table to change the column order, remove the ON
3307 : // DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization.
3308 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3309 : // Insert all the data.
3310 : "INSERT INTO index_data_upgrade "
3311 : "SELECT "
3312 : "index_data.index_id, "
3313 : "upgrade_key(index_data.value), "
3314 : "upgrade_key(index_data.object_data_key), "
3315 : "object_data.object_store_id "
3316 : "FROM index_data "
3317 : "JOIN object_data "
3318 : "ON index_data.object_data_id = object_data.id;"
3319 0 : ));
3320 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3321 0 : return rv;
3322 : }
3323 :
3324 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3325 : // The trigger is no longer needed.
3326 : "DROP TRIGGER index_data_upgrade_insert_trigger;"
3327 0 : ));
3328 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3329 0 : return rv;
3330 : }
3331 :
3332 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3333 : // The old table is no longer needed.
3334 : "DROP TABLE index_data;"
3335 0 : ));
3336 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3337 0 : return rv;
3338 : }
3339 :
3340 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3341 : // Rename the table.
3342 : "ALTER TABLE index_data_upgrade "
3343 : "RENAME TO index_data;"
3344 0 : ));
3345 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3346 0 : return rv;
3347 : }
3348 :
3349 : // Update the |object_data| table to add the |index_data_values| column,
3350 : // remove the ON DELETE CASCADE clause, and apply the WITHOUT ROWID
3351 : // optimization.
3352 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3353 : // Insert all the data.
3354 : "INSERT INTO object_data_upgrade "
3355 : "SELECT "
3356 : "object_data.object_store_id, "
3357 : "upgrade_key(object_data.key_value), "
3358 : "temp_index_data_values.index_data_values, "
3359 : "object_data.file_ids, "
3360 : "object_data.data "
3361 : "FROM object_data "
3362 : "LEFT JOIN temp_index_data_values "
3363 : "ON object_data.object_store_id = "
3364 : "temp_index_data_values.object_store_id "
3365 : "AND upgrade_key(object_data.key_value) = "
3366 : "temp_index_data_values.key;"
3367 0 : ));
3368 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3369 0 : return rv;
3370 : }
3371 :
3372 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3373 : // The temporary table is no longer needed.
3374 : "DROP TABLE temp_index_data_values;"
3375 0 : ));
3376 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3377 0 : return rv;
3378 : }
3379 :
3380 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3381 : // The old table is no longer needed.
3382 : "DROP TABLE object_data;"
3383 0 : ));
3384 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3385 0 : return rv;
3386 : }
3387 :
3388 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3389 : // Rename the table.
3390 : "ALTER TABLE object_data_upgrade "
3391 : "RENAME TO object_data;"
3392 0 : ));
3393 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3394 0 : return rv;
3395 : }
3396 :
3397 : // Update the |object_store_index| table to remove the UNIQUE constraint and
3398 : // the ON DELETE CASCADE clause.
3399 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3400 : "INSERT INTO object_store_index_upgrade "
3401 : "SELECT * "
3402 : "FROM object_store_index;"
3403 0 : ));
3404 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3405 0 : return rv;
3406 : }
3407 :
3408 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3409 : "DROP TABLE object_store_index;"
3410 0 : ));
3411 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3412 0 : return rv;
3413 : }
3414 :
3415 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3416 : "ALTER TABLE object_store_index_upgrade "
3417 : "RENAME TO object_store_index;"
3418 0 : ));
3419 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3420 0 : return rv;
3421 : }
3422 :
3423 : // Update the |object_store| table to remove the UNIQUE constraint.
3424 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3425 : "INSERT INTO object_store_upgrade "
3426 : "SELECT * "
3427 : "FROM object_store;"
3428 0 : ));
3429 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3430 0 : return rv;
3431 : }
3432 :
3433 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3434 : "DROP TABLE object_store;"
3435 0 : ));
3436 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3437 0 : return rv;
3438 : }
3439 :
3440 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3441 : "ALTER TABLE object_store_upgrade "
3442 : "RENAME TO object_store;"
3443 0 : ));
3444 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3445 0 : return rv;
3446 : }
3447 :
3448 : // Update the |database| table to include the origin, vacuum information, and
3449 : // apply the WITHOUT ROWID optimization.
3450 0 : nsCOMPtr<mozIStorageStatement> stmt;
3451 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
3452 : "INSERT INTO database_upgrade "
3453 : "SELECT name, :origin, version, 0, 0, 0 "
3454 : "FROM database;"
3455 0 : ), getter_AddRefs(stmt));
3456 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3457 0 : return rv;
3458 : }
3459 :
3460 0 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), aOrigin);
3461 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3462 0 : return rv;
3463 : }
3464 :
3465 0 : rv = stmt->Execute();
3466 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3467 0 : return rv;
3468 : }
3469 :
3470 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3471 : "DROP TABLE database;"
3472 0 : ));
3473 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3474 0 : return rv;
3475 : }
3476 :
3477 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3478 : "ALTER TABLE database_upgrade "
3479 : "RENAME TO database;"
3480 0 : ));
3481 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3482 0 : return rv;
3483 : }
3484 :
3485 : #ifdef DEBUG
3486 : {
3487 : // Make sure there's only one entry in the |database| table.
3488 0 : nsCOMPtr<mozIStorageStatement> stmt;
3489 0 : MOZ_ASSERT(NS_SUCCEEDED(
3490 : aConnection->CreateStatement(
3491 : NS_LITERAL_CSTRING("SELECT COUNT(*) "
3492 : "FROM database;"),
3493 : getter_AddRefs(stmt))));
3494 :
3495 : bool hasResult;
3496 0 : MOZ_ASSERT(NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)));
3497 :
3498 : int64_t count;
3499 0 : MOZ_ASSERT(NS_SUCCEEDED(stmt->GetInt64(0, &count)));
3500 :
3501 0 : MOZ_ASSERT(count == 1);
3502 : }
3503 : #endif
3504 :
3505 : // Recreate file table triggers.
3506 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3507 : "CREATE TRIGGER object_data_insert_trigger "
3508 : "AFTER INSERT ON object_data "
3509 : "WHEN NEW.file_ids IS NOT NULL "
3510 : "BEGIN "
3511 : "SELECT update_refcount(NULL, NEW.file_ids);"
3512 : "END;"
3513 0 : ));
3514 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3515 0 : return rv;
3516 : }
3517 :
3518 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3519 : "CREATE TRIGGER object_data_update_trigger "
3520 : "AFTER UPDATE OF file_ids ON object_data "
3521 : "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
3522 : "BEGIN "
3523 : "SELECT update_refcount(OLD.file_ids, NEW.file_ids);"
3524 : "END;"
3525 0 : ));
3526 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3527 0 : return rv;
3528 : }
3529 :
3530 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3531 : "CREATE TRIGGER object_data_delete_trigger "
3532 : "AFTER DELETE ON object_data "
3533 : "WHEN OLD.file_ids IS NOT NULL "
3534 : "BEGIN "
3535 : "SELECT update_refcount(OLD.file_ids, NULL);"
3536 : "END;"
3537 0 : ));
3538 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3539 0 : return rv;
3540 : }
3541 :
3542 : // Finally, turn on auto_vacuum mode. We use full auto_vacuum mode to reclaim
3543 : // disk space on mobile devices (at the cost of some COMMIT speed), and
3544 : // incremental auto_vacuum mode on desktop builds.
3545 0 : rv = aConnection->ExecuteSimpleSQL(
3546 : #ifdef IDB_MOBILE
3547 : NS_LITERAL_CSTRING("PRAGMA auto_vacuum = FULL;")
3548 : #else
3549 0 : NS_LITERAL_CSTRING("PRAGMA auto_vacuum = INCREMENTAL;")
3550 : #endif
3551 0 : );
3552 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3553 0 : return rv;
3554 : }
3555 :
3556 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(18, 0));
3557 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3558 0 : return rv;
3559 : }
3560 :
3561 0 : return NS_OK;
3562 : }
3563 :
3564 : nsresult
3565 0 : UpgradeSchemaFrom17_0To18_0(mozIStorageConnection* aConnection,
3566 : const nsACString& aOrigin)
3567 : {
3568 0 : MOZ_ASSERT(aConnection);
3569 0 : MOZ_ASSERT(!aOrigin.IsEmpty());
3570 :
3571 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom17_0To18_0", STORAGE);
3572 :
3573 0 : return UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(aConnection, aOrigin);
3574 : }
3575 :
3576 : nsresult
3577 0 : UpgradeSchemaFrom18_0To19_0(mozIStorageConnection* aConnection)
3578 : {
3579 0 : AssertIsOnIOThread();
3580 0 : MOZ_ASSERT(aConnection);
3581 :
3582 : nsresult rv;
3583 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom18_0To19_0", STORAGE);
3584 :
3585 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3586 : "ALTER TABLE object_store_index "
3587 : "ADD COLUMN locale TEXT;"
3588 0 : ));
3589 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3590 0 : return rv;
3591 : }
3592 :
3593 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3594 : "ALTER TABLE object_store_index "
3595 : "ADD COLUMN is_auto_locale BOOLEAN;"
3596 0 : ));
3597 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3598 0 : return rv;
3599 : }
3600 :
3601 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3602 : "ALTER TABLE index_data "
3603 : "ADD COLUMN value_locale BLOB;"
3604 0 : ));
3605 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3606 0 : return rv;
3607 : }
3608 :
3609 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3610 : "ALTER TABLE unique_index_data "
3611 : "ADD COLUMN value_locale BLOB;"
3612 0 : ));
3613 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3614 0 : return rv;
3615 : }
3616 :
3617 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3618 : "CREATE INDEX index_data_value_locale_index "
3619 : "ON index_data (index_id, value_locale, object_data_key, value) "
3620 : "WHERE value_locale IS NOT NULL;"
3621 0 : ));
3622 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3623 0 : return rv;
3624 : }
3625 :
3626 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3627 : "CREATE INDEX unique_index_data_value_locale_index "
3628 : "ON unique_index_data (index_id, value_locale, object_data_key, value) "
3629 : "WHERE value_locale IS NOT NULL;"
3630 0 : ));
3631 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3632 0 : return rv;
3633 : }
3634 :
3635 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(19, 0));
3636 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3637 0 : return rv;
3638 : }
3639 :
3640 0 : return NS_OK;
3641 : }
3642 :
3643 : #if !defined(MOZ_B2G)
3644 :
3645 : class NormalJSContext;
3646 :
3647 : class UpgradeFileIdsFunction final
3648 : : public mozIStorageFunction
3649 : {
3650 : RefPtr<FileManager> mFileManager;
3651 : nsAutoPtr<NormalJSContext> mContext;
3652 :
3653 : public:
3654 0 : UpgradeFileIdsFunction()
3655 0 : {
3656 0 : AssertIsOnIOThread();
3657 0 : }
3658 :
3659 : nsresult
3660 : Init(nsIFile* aFMDirectory,
3661 : mozIStorageConnection* aConnection);
3662 :
3663 : NS_DECL_ISUPPORTS
3664 :
3665 : private:
3666 0 : ~UpgradeFileIdsFunction()
3667 0 : {
3668 0 : AssertIsOnIOThread();
3669 :
3670 0 : if (mFileManager) {
3671 0 : mFileManager->Invalidate();
3672 : }
3673 0 : }
3674 :
3675 : NS_IMETHOD
3676 : OnFunctionCall(mozIStorageValueArray* aArguments,
3677 : nsIVariant** aResult) override;
3678 : };
3679 :
3680 : #endif // MOZ_B2G
3681 :
3682 : nsresult
3683 0 : UpgradeSchemaFrom19_0To20_0(nsIFile* aFMDirectory,
3684 : mozIStorageConnection* aConnection)
3685 : {
3686 0 : AssertIsOnIOThread();
3687 0 : MOZ_ASSERT(aConnection);
3688 :
3689 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom19_0To20_0", STORAGE);
3690 :
3691 : #if defined(MOZ_B2G)
3692 :
3693 : // We don't have to do the upgrade of file ids on B2G. The old format was
3694 : // only used by the previous single process implementation and B2G was
3695 : // always multi process. This is a nice optimization since the upgrade needs
3696 : // to deserialize all structured clones which reference a stored file or
3697 : // a mutable file.
3698 : nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(20, 0));
3699 : if (NS_WARN_IF(NS_FAILED(rv))) {
3700 : return rv;
3701 : }
3702 :
3703 : #else // MOZ_B2G
3704 :
3705 0 : nsCOMPtr<mozIStorageStatement> stmt;
3706 0 : nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
3707 : "SELECT count(*) "
3708 : "FROM object_data "
3709 : "WHERE file_ids IS NOT NULL"
3710 0 : ), getter_AddRefs(stmt));
3711 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3712 0 : return rv;
3713 : }
3714 :
3715 : int64_t count;
3716 :
3717 : {
3718 0 : mozStorageStatementScoper scoper(stmt);
3719 :
3720 : bool hasResult;
3721 0 : rv = stmt->ExecuteStep(&hasResult);
3722 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3723 0 : return rv;
3724 : }
3725 :
3726 0 : if (NS_WARN_IF(!hasResult)) {
3727 0 : MOZ_ASSERT(false, "This should never be possible!");
3728 : return NS_ERROR_FAILURE;
3729 : }
3730 :
3731 0 : count = stmt->AsInt64(0);
3732 0 : if (NS_WARN_IF(count < 0)) {
3733 0 : MOZ_ASSERT(false, "This should never be possible!");
3734 : return NS_ERROR_FAILURE;
3735 : }
3736 : }
3737 :
3738 0 : if (count == 0) {
3739 : // Nothing to upgrade.
3740 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(20, 0));
3741 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3742 0 : return rv;
3743 : }
3744 :
3745 0 : return NS_OK;
3746 : }
3747 :
3748 0 : RefPtr<UpgradeFileIdsFunction> function = new UpgradeFileIdsFunction();
3749 :
3750 0 : rv = function->Init(aFMDirectory, aConnection);
3751 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3752 0 : return rv;
3753 : }
3754 :
3755 0 : NS_NAMED_LITERAL_CSTRING(functionName, "upgrade");
3756 :
3757 0 : rv = aConnection->CreateFunction(functionName, 2, function);
3758 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3759 0 : return rv;
3760 : }
3761 :
3762 : // Disable update trigger.
3763 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3764 : "DROP TRIGGER object_data_update_trigger;"
3765 0 : ));
3766 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3767 0 : return rv;
3768 : }
3769 :
3770 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3771 : "UPDATE object_data "
3772 : "SET file_ids = upgrade(file_ids, data) "
3773 : "WHERE file_ids IS NOT NULL;"
3774 0 : ));
3775 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3776 0 : return rv;
3777 : }
3778 :
3779 : // Enable update trigger.
3780 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3781 : "CREATE TRIGGER object_data_update_trigger "
3782 : "AFTER UPDATE OF file_ids ON object_data "
3783 : "FOR EACH ROW "
3784 : "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
3785 : "BEGIN "
3786 : "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
3787 : "END;"
3788 0 : ));
3789 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3790 0 : return rv;
3791 : }
3792 :
3793 0 : rv = aConnection->RemoveFunction(functionName);
3794 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3795 0 : return rv;
3796 : }
3797 :
3798 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(20, 0));
3799 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3800 0 : return rv;
3801 : }
3802 :
3803 : #endif // MOZ_B2G
3804 :
3805 0 : return NS_OK;
3806 : }
3807 :
3808 : class UpgradeIndexDataValuesFunction final
3809 : : public mozIStorageFunction
3810 : {
3811 : public:
3812 0 : UpgradeIndexDataValuesFunction()
3813 0 : {
3814 0 : AssertIsOnIOThread();
3815 0 : }
3816 :
3817 : NS_DECL_ISUPPORTS
3818 :
3819 : private:
3820 0 : ~UpgradeIndexDataValuesFunction()
3821 0 : {
3822 0 : AssertIsOnIOThread();
3823 0 : }
3824 :
3825 : nsresult
3826 : ReadOldCompressedIDVFromBlob(const uint8_t* aBlobData,
3827 : uint32_t aBlobDataLength,
3828 : nsTArray<IndexDataValue>& aIndexValues);
3829 :
3830 : NS_IMETHOD
3831 : OnFunctionCall(mozIStorageValueArray* aArguments,
3832 : nsIVariant** aResult) override;
3833 : };
3834 :
3835 0 : NS_IMPL_ISUPPORTS(UpgradeIndexDataValuesFunction, mozIStorageFunction)
3836 :
3837 : nsresult
3838 0 : UpgradeIndexDataValuesFunction::ReadOldCompressedIDVFromBlob(
3839 : const uint8_t* aBlobData,
3840 : uint32_t aBlobDataLength,
3841 : nsTArray<IndexDataValue>& aIndexValues)
3842 : {
3843 0 : MOZ_ASSERT(!NS_IsMainThread());
3844 0 : MOZ_ASSERT(!IsOnBackgroundThread());
3845 0 : MOZ_ASSERT(aBlobData);
3846 0 : MOZ_ASSERT(aBlobDataLength);
3847 0 : MOZ_ASSERT(aIndexValues.IsEmpty());
3848 :
3849 0 : const uint8_t* blobDataIter = aBlobData;
3850 0 : const uint8_t* blobDataEnd = aBlobData + aBlobDataLength;
3851 :
3852 : int64_t indexId;
3853 : bool unique;
3854 0 : bool nextIndexIdAlreadyRead = false;
3855 :
3856 0 : while (blobDataIter < blobDataEnd) {
3857 0 : if (!nextIndexIdAlreadyRead) {
3858 0 : ReadCompressedIndexId(&blobDataIter, blobDataEnd, &indexId, &unique);
3859 : }
3860 0 : nextIndexIdAlreadyRead = false;
3861 :
3862 0 : if (NS_WARN_IF(blobDataIter == blobDataEnd)) {
3863 0 : IDB_REPORT_INTERNAL_ERR();
3864 0 : return NS_ERROR_FILE_CORRUPTED;
3865 : }
3866 :
3867 : // Read key buffer length.
3868 : const uint64_t keyBufferLength =
3869 0 : ReadCompressedNumber(&blobDataIter, blobDataEnd);
3870 :
3871 0 : if (NS_WARN_IF(blobDataIter == blobDataEnd) ||
3872 0 : NS_WARN_IF(keyBufferLength > uint64_t(UINT32_MAX)) ||
3873 0 : NS_WARN_IF(blobDataIter + keyBufferLength > blobDataEnd)) {
3874 0 : IDB_REPORT_INTERNAL_ERR();
3875 0 : return NS_ERROR_FILE_CORRUPTED;
3876 : }
3877 :
3878 : nsCString keyBuffer(reinterpret_cast<const char*>(blobDataIter),
3879 0 : uint32_t(keyBufferLength));
3880 0 : blobDataIter += keyBufferLength;
3881 :
3882 0 : IndexDataValue idv(indexId, unique, Key(keyBuffer));
3883 :
3884 0 : if (blobDataIter < blobDataEnd) {
3885 : // Read either a sort key buffer length or an index id.
3886 0 : uint64_t maybeIndexId = ReadCompressedNumber(&blobDataIter, blobDataEnd);
3887 :
3888 : // Locale-aware indexes haven't been around long enough to have any users,
3889 : // we can safely assume all sort key buffer lengths will be zero.
3890 0 : if (maybeIndexId != 0) {
3891 0 : if (maybeIndexId % 2) {
3892 0 : unique = true;
3893 0 : maybeIndexId--;
3894 : } else {
3895 0 : unique = false;
3896 : }
3897 0 : indexId = maybeIndexId/2;
3898 0 : nextIndexIdAlreadyRead = true;
3899 : }
3900 : }
3901 :
3902 0 : if (NS_WARN_IF(!aIndexValues.InsertElementSorted(idv, fallible))) {
3903 0 : IDB_REPORT_INTERNAL_ERR();
3904 0 : return NS_ERROR_OUT_OF_MEMORY;
3905 : }
3906 : }
3907 :
3908 0 : MOZ_ASSERT(blobDataIter == blobDataEnd);
3909 :
3910 0 : return NS_OK;
3911 : }
3912 :
3913 : NS_IMETHODIMP
3914 0 : UpgradeIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
3915 : nsIVariant** aResult)
3916 : {
3917 0 : MOZ_ASSERT(aArguments);
3918 0 : MOZ_ASSERT(aResult);
3919 :
3920 0 : AUTO_PROFILER_LABEL(
3921 : "UpgradeIndexDataValuesFunction::OnFunctionCall", STORAGE);
3922 :
3923 : uint32_t argc;
3924 0 : nsresult rv = aArguments->GetNumEntries(&argc);
3925 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3926 0 : return rv;
3927 : }
3928 :
3929 0 : if (argc != 1) {
3930 0 : NS_WARNING("Don't call me with the wrong number of arguments!");
3931 0 : return NS_ERROR_UNEXPECTED;
3932 : }
3933 :
3934 : int32_t type;
3935 0 : rv = aArguments->GetTypeOfIndex(0, &type);
3936 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3937 0 : return rv;
3938 : }
3939 :
3940 0 : if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
3941 0 : NS_WARNING("Don't call me with the wrong type of arguments!");
3942 0 : return NS_ERROR_UNEXPECTED;
3943 : }
3944 :
3945 : const uint8_t* oldBlob;
3946 : uint32_t oldBlobLength;
3947 0 : rv = aArguments->GetSharedBlob(0, &oldBlobLength, &oldBlob);
3948 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3949 0 : return rv;
3950 : }
3951 :
3952 0 : AutoTArray<IndexDataValue, 32> oldIdv;
3953 0 : rv = ReadOldCompressedIDVFromBlob(oldBlob, oldBlobLength, oldIdv);
3954 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3955 0 : return rv;
3956 : }
3957 :
3958 0 : UniqueFreePtr<uint8_t> newIdv;
3959 : uint32_t newIdvLength;
3960 0 : rv = MakeCompressedIndexDataValues(oldIdv, newIdv, &newIdvLength);
3961 :
3962 0 : std::pair<uint8_t*, int> data(newIdv.release(), newIdvLength);
3963 :
3964 0 : nsCOMPtr<nsIVariant> result = new storage::AdoptedBlobVariant(data);
3965 :
3966 0 : result.forget(aResult);
3967 0 : return NS_OK;
3968 : }
3969 :
3970 : nsresult
3971 0 : UpgradeSchemaFrom20_0To21_0(mozIStorageConnection* aConnection)
3972 : {
3973 : // This should have been part of the 18 to 19 upgrade, where we changed the
3974 : // layout of the index_data_values blobs but didn't upgrade the existing data.
3975 : // See bug 1202788.
3976 :
3977 0 : AssertIsOnIOThread();
3978 0 : MOZ_ASSERT(aConnection);
3979 :
3980 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom20_0To21_0", STORAGE);
3981 :
3982 : RefPtr<UpgradeIndexDataValuesFunction> function =
3983 0 : new UpgradeIndexDataValuesFunction();
3984 :
3985 0 : NS_NAMED_LITERAL_CSTRING(functionName, "upgrade_idv");
3986 :
3987 0 : nsresult rv = aConnection->CreateFunction(functionName, 1, function);
3988 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3989 0 : return rv;
3990 : }
3991 :
3992 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3993 : "UPDATE object_data "
3994 : "SET index_data_values = upgrade_idv(index_data_values) "
3995 : "WHERE index_data_values IS NOT NULL;"
3996 0 : ));
3997 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3998 0 : return rv;
3999 : }
4000 :
4001 0 : rv = aConnection->RemoveFunction(functionName);
4002 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4003 0 : return rv;
4004 : }
4005 :
4006 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(21, 0));
4007 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4008 0 : return rv;
4009 : }
4010 :
4011 0 : return NS_OK;
4012 : }
4013 :
4014 : nsresult
4015 0 : UpgradeSchemaFrom21_0To22_0(mozIStorageConnection* aConnection)
4016 : {
4017 : // The only change between 21 and 22 was a different structured clone format,
4018 : // but it's backwards-compatible.
4019 0 : nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(22, 0));
4020 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4021 0 : return rv;
4022 : }
4023 :
4024 0 : return NS_OK;
4025 : }
4026 :
4027 : nsresult
4028 0 : UpgradeSchemaFrom22_0To23_0(mozIStorageConnection* aConnection,
4029 : const nsACString& aOrigin)
4030 : {
4031 0 : AssertIsOnIOThread();
4032 0 : MOZ_ASSERT(aConnection);
4033 0 : MOZ_ASSERT(!aOrigin.IsEmpty());
4034 :
4035 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom22_0To23_0", STORAGE);
4036 :
4037 0 : nsCOMPtr<mozIStorageStatement> stmt;
4038 0 : nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
4039 : "UPDATE database "
4040 : "SET origin = :origin;"
4041 0 : ), getter_AddRefs(stmt));
4042 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4043 0 : return rv;
4044 : }
4045 :
4046 0 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), aOrigin);
4047 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4048 0 : return rv;
4049 : }
4050 :
4051 0 : rv = stmt->Execute();
4052 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4053 0 : return rv;
4054 : }
4055 :
4056 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(23, 0));
4057 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4058 0 : return rv;
4059 : }
4060 :
4061 0 : return NS_OK;
4062 : }
4063 :
4064 : nsresult
4065 0 : UpgradeSchemaFrom23_0To24_0(mozIStorageConnection* aConnection)
4066 : {
4067 : // The only change between 23 and 24 was a different structured clone format,
4068 : // but it's backwards-compatible.
4069 0 : nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(24, 0));
4070 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4071 0 : return rv;
4072 : }
4073 :
4074 0 : return NS_OK;
4075 : }
4076 :
4077 : nsresult
4078 0 : UpgradeSchemaFrom24_0To25_0(mozIStorageConnection* aConnection)
4079 : {
4080 : // The changes between 24 and 25 were an upgraded snappy library, a different
4081 : // structured clone format and a different file_ds format. But everything is
4082 : // backwards-compatible.
4083 0 : nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(25, 0));
4084 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4085 0 : return rv;
4086 : }
4087 :
4088 0 : return NS_OK;
4089 : }
4090 :
4091 0 : class StripObsoleteOriginAttributesFunction final
4092 : : public mozIStorageFunction
4093 : {
4094 : public:
4095 : NS_DECL_ISUPPORTS
4096 :
4097 : private:
4098 0 : ~StripObsoleteOriginAttributesFunction()
4099 0 : { }
4100 :
4101 : NS_IMETHOD
4102 0 : OnFunctionCall(mozIStorageValueArray* aArguments,
4103 : nsIVariant** aResult) override
4104 : {
4105 0 : MOZ_ASSERT(aArguments);
4106 0 : MOZ_ASSERT(aResult);
4107 :
4108 0 : AUTO_PROFILER_LABEL(
4109 : "StripObsoleteOriginAttributesFunction::OnFunctionCall", STORAGE);
4110 :
4111 : #ifdef DEBUG
4112 : {
4113 : uint32_t argCount;
4114 0 : MOZ_ALWAYS_SUCCEEDS(aArguments->GetNumEntries(&argCount));
4115 0 : MOZ_ASSERT(argCount == 1);
4116 :
4117 : int32_t type;
4118 0 : MOZ_ALWAYS_SUCCEEDS(aArguments->GetTypeOfIndex(0, &type));
4119 0 : MOZ_ASSERT(type == mozIStorageValueArray::VALUE_TYPE_TEXT);
4120 : }
4121 : #endif
4122 :
4123 0 : nsCString origin;
4124 0 : nsresult rv = aArguments->GetUTF8String(0, origin);
4125 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4126 0 : return rv;
4127 : }
4128 :
4129 : // Deserialize and re-serialize to automatically drop any obsolete origin
4130 : // attributes.
4131 0 : OriginAttributes oa;
4132 :
4133 0 : nsCString originNoSuffix;
4134 0 : bool ok = oa.PopulateFromOrigin(origin, originNoSuffix);
4135 0 : if (NS_WARN_IF(!ok)) {
4136 0 : return NS_ERROR_FAILURE;
4137 : }
4138 :
4139 0 : nsCString suffix;
4140 0 : oa.CreateSuffix(suffix);
4141 :
4142 : nsCOMPtr<nsIVariant> result =
4143 0 : new mozilla::storage::UTF8TextVariant(originNoSuffix + suffix);
4144 :
4145 0 : result.forget(aResult);
4146 0 : return NS_OK;
4147 : }
4148 : };
4149 :
4150 : nsresult
4151 0 : UpgradeSchemaFrom25_0To26_0(mozIStorageConnection* aConnection)
4152 : {
4153 0 : AssertIsOnIOThread();
4154 0 : MOZ_ASSERT(aConnection);
4155 :
4156 0 : AUTO_PROFILER_LABEL("UpgradeSchemaFrom25_0To26_0", STORAGE);
4157 :
4158 0 : NS_NAMED_LITERAL_CSTRING(functionName, "strip_obsolete_attributes");
4159 :
4160 : nsCOMPtr<mozIStorageFunction> stripObsoleteAttributes =
4161 0 : new StripObsoleteOriginAttributesFunction();
4162 :
4163 0 : nsresult rv = aConnection->CreateFunction(functionName,
4164 : /* aNumArguments */ 1,
4165 0 : stripObsoleteAttributes);
4166 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4167 0 : return rv;
4168 : }
4169 :
4170 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
4171 : "UPDATE DATABASE "
4172 : "SET origin = strip_obsolete_attributes(origin) "
4173 : "WHERE origin LIKE '%^%';"
4174 0 : ));
4175 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4176 0 : return rv;
4177 : }
4178 :
4179 0 : rv = aConnection->RemoveFunction(functionName);
4180 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4181 0 : return rv;
4182 : }
4183 :
4184 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(26, 0));
4185 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4186 0 : return rv;
4187 : }
4188 :
4189 0 : return NS_OK;
4190 : }
4191 :
4192 : nsresult
4193 0 : GetDatabaseFileURL(nsIFile* aDatabaseFile,
4194 : PersistenceType aPersistenceType,
4195 : const nsACString& aGroup,
4196 : const nsACString& aOrigin,
4197 : uint32_t aTelemetryId,
4198 : nsIFileURL** aResult)
4199 : {
4200 0 : MOZ_ASSERT(aDatabaseFile);
4201 0 : MOZ_ASSERT(aResult);
4202 :
4203 : nsresult rv;
4204 :
4205 : nsCOMPtr<nsIProtocolHandler> protocolHandler(
4206 0 : do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "file", &rv));
4207 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4208 0 : return rv;
4209 : }
4210 :
4211 : nsCOMPtr<nsIFileProtocolHandler> fileHandler(
4212 0 : do_QueryInterface(protocolHandler, &rv));
4213 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4214 0 : return rv;
4215 : }
4216 :
4217 0 : nsCOMPtr<nsIURI> uri;
4218 0 : rv = fileHandler->NewFileURI(aDatabaseFile, getter_AddRefs(uri));
4219 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4220 0 : return rv;
4221 : }
4222 :
4223 0 : nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
4224 0 : MOZ_ASSERT(fileUrl);
4225 :
4226 0 : nsAutoCString type;
4227 0 : PersistenceTypeToText(aPersistenceType, type);
4228 :
4229 0 : nsAutoCString telemetryFilenameClause;
4230 0 : if (aTelemetryId) {
4231 0 : telemetryFilenameClause.AssignLiteral("&telemetryFilename=indexedDB-");
4232 0 : telemetryFilenameClause.AppendInt(aTelemetryId);
4233 0 : telemetryFilenameClause.AppendLiteral(".sqlite");
4234 : }
4235 :
4236 0 : rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type +
4237 0 : NS_LITERAL_CSTRING("&group=") + aGroup +
4238 0 : NS_LITERAL_CSTRING("&origin=") + aOrigin +
4239 0 : NS_LITERAL_CSTRING("&cache=private") +
4240 0 : telemetryFilenameClause);
4241 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4242 0 : return rv;
4243 : }
4244 :
4245 0 : fileUrl.forget(aResult);
4246 0 : return NS_OK;
4247 : }
4248 :
4249 : nsresult
4250 0 : SetDefaultPragmas(mozIStorageConnection* aConnection)
4251 : {
4252 0 : MOZ_ASSERT(!NS_IsMainThread());
4253 0 : MOZ_ASSERT(aConnection);
4254 :
4255 : static const char kBuiltInPragmas[] =
4256 : // We use foreign keys in DEBUG builds only because there is a performance
4257 : // cost to using them.
4258 : "PRAGMA foreign_keys = "
4259 : #ifdef DEBUG
4260 : "ON"
4261 : #else
4262 : "OFF"
4263 : #endif
4264 : ";"
4265 :
4266 : // The "INSERT OR REPLACE" statement doesn't fire the update trigger,
4267 : // instead it fires only the insert trigger. This confuses the update
4268 : // refcount function. This behavior changes with enabled recursive triggers,
4269 : // so the statement fires the delete trigger first and then the insert
4270 : // trigger.
4271 : "PRAGMA recursive_triggers = ON;"
4272 :
4273 : // We aggressively truncate the database file when idle so don't bother
4274 : // overwriting the WAL with 0 during active periods.
4275 : "PRAGMA secure_delete = OFF;"
4276 : ;
4277 :
4278 : nsresult rv =
4279 : aConnection->ExecuteSimpleSQL(
4280 0 : nsDependentCString(kBuiltInPragmas,
4281 0 : LiteralStringLength(kBuiltInPragmas)));
4282 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4283 0 : return rv;
4284 : }
4285 :
4286 0 : nsAutoCString pragmaStmt;
4287 0 : pragmaStmt.AssignLiteral("PRAGMA synchronous = ");
4288 :
4289 0 : if (IndexedDatabaseManager::FullSynchronous()) {
4290 0 : pragmaStmt.AppendLiteral("FULL");
4291 : } else {
4292 0 : pragmaStmt.AppendLiteral("NORMAL");
4293 : }
4294 0 : pragmaStmt.Append(';');
4295 :
4296 0 : rv = aConnection->ExecuteSimpleSQL(pragmaStmt);
4297 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4298 0 : return rv;
4299 : }
4300 :
4301 : #ifndef IDB_MOBILE
4302 : if (kSQLiteGrowthIncrement) {
4303 : // This is just an optimization so ignore the failure if the disk is
4304 : // currently too full.
4305 0 : rv = aConnection->SetGrowthIncrement(kSQLiteGrowthIncrement,
4306 0 : EmptyCString());
4307 0 : if (rv != NS_ERROR_FILE_TOO_BIG && NS_WARN_IF(NS_FAILED(rv))) {
4308 0 : return rv;
4309 : }
4310 : }
4311 : #endif // IDB_MOBILE
4312 :
4313 0 : return NS_OK;
4314 : }
4315 :
4316 : nsresult
4317 0 : SetJournalMode(mozIStorageConnection* aConnection)
4318 : {
4319 0 : MOZ_ASSERT(!NS_IsMainThread());
4320 0 : MOZ_ASSERT(aConnection);
4321 :
4322 : // Try enabling WAL mode. This can fail in various circumstances so we have to
4323 : // check the results here.
4324 0 : NS_NAMED_LITERAL_CSTRING(journalModeQueryStart, "PRAGMA journal_mode = ");
4325 0 : NS_NAMED_LITERAL_CSTRING(journalModeWAL, "wal");
4326 :
4327 0 : nsCOMPtr<mozIStorageStatement> stmt;
4328 : nsresult rv =
4329 0 : aConnection->CreateStatement(journalModeQueryStart + journalModeWAL,
4330 0 : getter_AddRefs(stmt));
4331 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4332 0 : return rv;
4333 : }
4334 :
4335 : bool hasResult;
4336 0 : rv = stmt->ExecuteStep(&hasResult);
4337 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4338 0 : return rv;
4339 : }
4340 :
4341 0 : MOZ_ASSERT(hasResult);
4342 :
4343 0 : nsCString journalMode;
4344 0 : rv = stmt->GetUTF8String(0, journalMode);
4345 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4346 0 : return rv;
4347 : }
4348 :
4349 0 : if (journalMode.Equals(journalModeWAL)) {
4350 : // WAL mode successfully enabled. Maybe set limits on its size here.
4351 : if (kMaxWALPages >= 0) {
4352 0 : nsAutoCString pageCount;
4353 0 : pageCount.AppendInt(kMaxWALPages);
4354 :
4355 0 : rv = aConnection->ExecuteSimpleSQL(
4356 0 : NS_LITERAL_CSTRING("PRAGMA wal_autocheckpoint = ") + pageCount);
4357 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4358 0 : return rv;
4359 : }
4360 : }
4361 : } else {
4362 0 : NS_WARNING("Failed to set WAL mode, falling back to normal journal mode.");
4363 : #ifdef IDB_MOBILE
4364 : rv = aConnection->ExecuteSimpleSQL(journalModeQueryStart +
4365 : NS_LITERAL_CSTRING("truncate"));
4366 : if (NS_WARN_IF(NS_FAILED(rv))) {
4367 : return rv;
4368 : }
4369 : #endif
4370 : }
4371 :
4372 0 : return NS_OK;
4373 : }
4374 :
4375 : template <class FileOrURLType>
4376 : struct StorageOpenTraits;
4377 :
4378 : template <>
4379 : struct StorageOpenTraits<nsIFileURL*>
4380 : {
4381 : static nsresult
4382 0 : Open(mozIStorageService* aStorageService,
4383 : nsIFileURL* aFileURL,
4384 : mozIStorageConnection** aConnection)
4385 : {
4386 0 : return aStorageService->OpenDatabaseWithFileURL(aFileURL, aConnection);
4387 : }
4388 :
4389 : #ifdef DEBUG
4390 : static void
4391 0 : GetPath(nsIFileURL* aFileURL, nsCString& aPath)
4392 : {
4393 0 : MOZ_ALWAYS_SUCCEEDS(aFileURL->GetFileName(aPath));
4394 0 : }
4395 : #endif
4396 : };
4397 :
4398 : template <>
4399 : struct StorageOpenTraits<nsIFile*>
4400 : {
4401 : static nsresult
4402 0 : Open(mozIStorageService* aStorageService,
4403 : nsIFile* aFile,
4404 : mozIStorageConnection** aConnection)
4405 : {
4406 0 : return aStorageService->OpenUnsharedDatabase(aFile, aConnection);
4407 : }
4408 :
4409 : #ifdef DEBUG
4410 : static void
4411 0 : GetPath(nsIFile* aFile, nsCString& aPath)
4412 : {
4413 0 : nsString path;
4414 0 : MOZ_ALWAYS_SUCCEEDS(aFile->GetPath(path));
4415 :
4416 0 : aPath.AssignWithConversion(path);
4417 0 : }
4418 : #endif
4419 : };
4420 :
4421 : template <template <class> class SmartPtr, class FileOrURLType>
4422 : struct StorageOpenTraits<SmartPtr<FileOrURLType>>
4423 : : public StorageOpenTraits<FileOrURLType*>
4424 : { };
4425 :
4426 : template <class FileOrURLType>
4427 : nsresult
4428 0 : OpenDatabaseAndHandleBusy(mozIStorageService* aStorageService,
4429 : FileOrURLType aFileOrURL,
4430 : mozIStorageConnection** aConnection)
4431 : {
4432 0 : MOZ_ASSERT(!NS_IsMainThread());
4433 0 : MOZ_ASSERT(!IsOnBackgroundThread());
4434 0 : MOZ_ASSERT(aStorageService);
4435 0 : MOZ_ASSERT(aFileOrURL);
4436 0 : MOZ_ASSERT(aConnection);
4437 :
4438 0 : nsCOMPtr<mozIStorageConnection> connection;
4439 : nsresult rv =
4440 0 : StorageOpenTraits<FileOrURLType>::Open(aStorageService,
4441 : aFileOrURL,
4442 0 : getter_AddRefs(connection));
4443 :
4444 0 : if (rv == NS_ERROR_STORAGE_BUSY) {
4445 : #ifdef DEBUG
4446 : {
4447 0 : nsCString path;
4448 0 : StorageOpenTraits<FileOrURLType>::GetPath(aFileOrURL, path);
4449 :
4450 : nsPrintfCString message("Received NS_ERROR_STORAGE_BUSY when attempting "
4451 : "to open database '%s', retrying for up to 10 "
4452 : "seconds",
4453 0 : path.get());
4454 0 : NS_WARNING(message.get());
4455 : }
4456 : #endif
4457 :
4458 : // Another thread must be checkpointing the WAL. Wait up to 10 seconds for
4459 : // that to complete.
4460 0 : TimeStamp start = TimeStamp::NowLoRes();
4461 :
4462 : while (true) {
4463 0 : PR_Sleep(PR_MillisecondsToInterval(100));
4464 :
4465 0 : rv = StorageOpenTraits<FileOrURLType>::Open(aStorageService,
4466 : aFileOrURL,
4467 : getter_AddRefs(connection));
4468 0 : if (rv != NS_ERROR_STORAGE_BUSY ||
4469 0 : TimeStamp::NowLoRes() - start > TimeDuration::FromSeconds(10)) {
4470 0 : break;
4471 : }
4472 : }
4473 : }
4474 :
4475 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4476 0 : return rv;
4477 : }
4478 :
4479 0 : connection.forget(aConnection);
4480 0 : return NS_OK;
4481 : }
4482 :
4483 : nsresult
4484 0 : CreateStorageConnection(nsIFile* aDBFile,
4485 : nsIFile* aFMDirectory,
4486 : const nsAString& aName,
4487 : PersistenceType aPersistenceType,
4488 : const nsACString& aGroup,
4489 : const nsACString& aOrigin,
4490 : uint32_t aTelemetryId,
4491 : mozIStorageConnection** aConnection)
4492 : {
4493 0 : AssertIsOnIOThread();
4494 0 : MOZ_ASSERT(aDBFile);
4495 0 : MOZ_ASSERT(aFMDirectory);
4496 0 : MOZ_ASSERT(aConnection);
4497 :
4498 0 : AUTO_PROFILER_LABEL("CreateStorageConnection", STORAGE);
4499 :
4500 : nsresult rv;
4501 : bool exists;
4502 :
4503 0 : if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
4504 0 : rv = aDBFile->Exists(&exists);
4505 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4506 0 : return rv;
4507 : }
4508 :
4509 0 : if (!exists) {
4510 0 : NS_WARNING("Refusing to create database because disk space is low!");
4511 0 : return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
4512 : }
4513 : }
4514 :
4515 0 : nsCOMPtr<nsIFileURL> dbFileUrl;
4516 0 : rv = GetDatabaseFileURL(aDBFile,
4517 : aPersistenceType,
4518 : aGroup,
4519 : aOrigin,
4520 : aTelemetryId,
4521 0 : getter_AddRefs(dbFileUrl));
4522 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4523 0 : return rv;
4524 : }
4525 :
4526 : nsCOMPtr<mozIStorageService> ss =
4527 0 : do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
4528 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4529 0 : return rv;
4530 : }
4531 :
4532 0 : nsCOMPtr<mozIStorageConnection> connection;
4533 0 : rv = OpenDatabaseAndHandleBusy(ss, dbFileUrl, getter_AddRefs(connection));
4534 0 : if (rv == NS_ERROR_FILE_CORRUPTED) {
4535 : // If we're just opening the database during origin initialization, then
4536 : // we don't want to erase any files. The failure here will fail origin
4537 : // initialization too.
4538 0 : if (aName.IsVoid()) {
4539 0 : return rv;
4540 : }
4541 :
4542 : // Nuke the database file.
4543 0 : rv = aDBFile->Remove(false);
4544 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4545 0 : return rv;
4546 : }
4547 :
4548 0 : rv = aFMDirectory->Exists(&exists);
4549 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4550 0 : return rv;
4551 : }
4552 :
4553 0 : if (exists) {
4554 : bool isDirectory;
4555 0 : rv = aFMDirectory->IsDirectory(&isDirectory);
4556 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4557 0 : return rv;
4558 : }
4559 0 : if (NS_WARN_IF(!isDirectory)) {
4560 0 : IDB_REPORT_INTERNAL_ERR();
4561 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4562 : }
4563 :
4564 0 : rv = aFMDirectory->Remove(true);
4565 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4566 0 : return rv;
4567 : }
4568 : }
4569 :
4570 0 : rv = OpenDatabaseAndHandleBusy(ss, dbFileUrl, getter_AddRefs(connection));
4571 : }
4572 :
4573 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4574 0 : return rv;
4575 : }
4576 :
4577 0 : rv = SetDefaultPragmas(connection);
4578 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4579 0 : return rv;
4580 : }
4581 :
4582 0 : rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem"));
4583 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4584 0 : return rv;
4585 : }
4586 :
4587 : // Check to make sure that the database schema is correct.
4588 : int32_t schemaVersion;
4589 0 : rv = connection->GetSchemaVersion(&schemaVersion);
4590 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4591 0 : return rv;
4592 : }
4593 :
4594 : // Unknown schema will fail origin initialization too.
4595 0 : if (!schemaVersion && aName.IsVoid()) {
4596 0 : IDB_WARNING("Unable to open IndexedDB database, schema is not set!");
4597 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4598 : }
4599 :
4600 0 : if (schemaVersion > kSQLiteSchemaVersion) {
4601 0 : IDB_WARNING("Unable to open IndexedDB database, schema is too high!");
4602 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4603 : }
4604 :
4605 0 : bool journalModeSet = false;
4606 :
4607 0 : if (schemaVersion != kSQLiteSchemaVersion) {
4608 0 : const bool newDatabase = !schemaVersion;
4609 :
4610 0 : if (newDatabase) {
4611 : // Set the page size first.
4612 : if (kSQLitePageSizeOverride) {
4613 0 : rv = connection->ExecuteSimpleSQL(
4614 0 : nsPrintfCString("PRAGMA page_size = %" PRIu32 ";", kSQLitePageSizeOverride)
4615 0 : );
4616 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4617 0 : return rv;
4618 : }
4619 : }
4620 :
4621 : // We have to set the auto_vacuum mode before opening a transaction.
4622 0 : rv = connection->ExecuteSimpleSQL(
4623 : #ifdef IDB_MOBILE
4624 : // Turn on full auto_vacuum mode to reclaim disk space on mobile
4625 : // devices (at the cost of some COMMIT speed).
4626 : NS_LITERAL_CSTRING("PRAGMA auto_vacuum = FULL;")
4627 : #else
4628 : // Turn on incremental auto_vacuum mode on desktop builds.
4629 0 : NS_LITERAL_CSTRING("PRAGMA auto_vacuum = INCREMENTAL;")
4630 : #endif
4631 0 : );
4632 0 : if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
4633 : // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
4634 : // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
4635 0 : rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
4636 : }
4637 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4638 0 : return rv;
4639 : }
4640 :
4641 0 : rv = SetJournalMode(connection);
4642 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4643 0 : return rv;
4644 : }
4645 :
4646 0 : journalModeSet = true;
4647 : } else {
4648 : #ifdef DEBUG
4649 : // Disable foreign key support while upgrading. This has to be done before
4650 : // starting a transaction.
4651 0 : MOZ_ALWAYS_SUCCEEDS(
4652 : connection->ExecuteSimpleSQL(
4653 : NS_LITERAL_CSTRING("PRAGMA foreign_keys = OFF;")));
4654 : #endif
4655 : }
4656 :
4657 0 : bool vacuumNeeded = false;
4658 :
4659 : mozStorageTransaction transaction(connection, false,
4660 0 : mozIStorageConnection::TRANSACTION_IMMEDIATE);
4661 :
4662 0 : if (newDatabase) {
4663 0 : rv = CreateTables(connection);
4664 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4665 0 : return rv;
4666 : }
4667 :
4668 0 : MOZ_ASSERT(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)));
4669 0 : MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
4670 :
4671 0 : nsCOMPtr<mozIStorageStatement> stmt;
4672 0 : nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING(
4673 : "INSERT INTO database (name, origin) "
4674 : "VALUES (:name, :origin)"
4675 0 : ), getter_AddRefs(stmt));
4676 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4677 0 : return rv;
4678 : }
4679 :
4680 0 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
4681 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4682 0 : return rv;
4683 : }
4684 :
4685 0 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), aOrigin);
4686 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4687 0 : return rv;
4688 : }
4689 :
4690 0 : rv = stmt->Execute();
4691 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4692 0 : return rv;
4693 : }
4694 : } else {
4695 : // This logic needs to change next time we change the schema!
4696 : static_assert(kSQLiteSchemaVersion == int32_t((26 << 4) + 0),
4697 : "Upgrade function needed due to schema version increase.");
4698 :
4699 0 : while (schemaVersion != kSQLiteSchemaVersion) {
4700 0 : if (schemaVersion == 4) {
4701 0 : rv = UpgradeSchemaFrom4To5(connection);
4702 0 : } else if (schemaVersion == 5) {
4703 0 : rv = UpgradeSchemaFrom5To6(connection);
4704 0 : } else if (schemaVersion == 6) {
4705 0 : rv = UpgradeSchemaFrom6To7(connection);
4706 0 : } else if (schemaVersion == 7) {
4707 0 : rv = UpgradeSchemaFrom7To8(connection);
4708 0 : } else if (schemaVersion == 8) {
4709 0 : rv = UpgradeSchemaFrom8To9_0(connection);
4710 0 : vacuumNeeded = true;
4711 0 : } else if (schemaVersion == MakeSchemaVersion(9, 0)) {
4712 0 : rv = UpgradeSchemaFrom9_0To10_0(connection);
4713 0 : } else if (schemaVersion == MakeSchemaVersion(10, 0)) {
4714 0 : rv = UpgradeSchemaFrom10_0To11_0(connection);
4715 0 : } else if (schemaVersion == MakeSchemaVersion(11, 0)) {
4716 0 : rv = UpgradeSchemaFrom11_0To12_0(connection);
4717 0 : } else if (schemaVersion == MakeSchemaVersion(12, 0)) {
4718 0 : rv = UpgradeSchemaFrom12_0To13_0(connection, &vacuumNeeded);
4719 0 : } else if (schemaVersion == MakeSchemaVersion(13, 0)) {
4720 0 : rv = UpgradeSchemaFrom13_0To14_0(connection);
4721 0 : } else if (schemaVersion == MakeSchemaVersion(14, 0)) {
4722 0 : rv = UpgradeSchemaFrom14_0To15_0(connection);
4723 0 : } else if (schemaVersion == MakeSchemaVersion(15, 0)) {
4724 0 : rv = UpgradeSchemaFrom15_0To16_0(connection);
4725 0 : } else if (schemaVersion == MakeSchemaVersion(16, 0)) {
4726 0 : rv = UpgradeSchemaFrom16_0To17_0(connection);
4727 0 : } else if (schemaVersion == MakeSchemaVersion(17, 0)) {
4728 0 : rv = UpgradeSchemaFrom17_0To18_0(connection, aOrigin);
4729 0 : vacuumNeeded = true;
4730 0 : } else if (schemaVersion == MakeSchemaVersion(18, 0)) {
4731 0 : rv = UpgradeSchemaFrom18_0To19_0(connection);
4732 0 : } else if (schemaVersion == MakeSchemaVersion(19, 0)) {
4733 0 : rv = UpgradeSchemaFrom19_0To20_0(aFMDirectory, connection);
4734 0 : } else if (schemaVersion == MakeSchemaVersion(20, 0)) {
4735 0 : rv = UpgradeSchemaFrom20_0To21_0(connection);
4736 0 : } else if (schemaVersion == MakeSchemaVersion(21, 0)) {
4737 0 : rv = UpgradeSchemaFrom21_0To22_0(connection);
4738 0 : } else if (schemaVersion == MakeSchemaVersion(22, 0)) {
4739 0 : rv = UpgradeSchemaFrom22_0To23_0(connection, aOrigin);
4740 0 : } else if (schemaVersion == MakeSchemaVersion(23, 0)) {
4741 0 : rv = UpgradeSchemaFrom23_0To24_0(connection);
4742 0 : } else if (schemaVersion == MakeSchemaVersion(24, 0)) {
4743 0 : rv = UpgradeSchemaFrom24_0To25_0(connection);
4744 0 : } else if (schemaVersion == MakeSchemaVersion(25, 0)) {
4745 0 : rv = UpgradeSchemaFrom25_0To26_0(connection);
4746 : } else {
4747 0 : IDB_WARNING("Unable to open IndexedDB database, no upgrade path is "
4748 : "available!");
4749 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4750 : }
4751 :
4752 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4753 0 : return rv;
4754 : }
4755 :
4756 0 : rv = connection->GetSchemaVersion(&schemaVersion);
4757 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4758 0 : return rv;
4759 : }
4760 : }
4761 :
4762 0 : MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
4763 : }
4764 :
4765 0 : rv = transaction.Commit();
4766 0 : if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
4767 : // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
4768 : // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
4769 0 : rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
4770 : }
4771 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4772 0 : return rv;
4773 : }
4774 :
4775 : #ifdef DEBUG
4776 0 : if (!newDatabase) {
4777 : // Re-enable foreign key support after doing a foreign key check.
4778 0 : nsCOMPtr<mozIStorageStatement> checkStmt;
4779 0 : MOZ_ALWAYS_SUCCEEDS(
4780 : connection->CreateStatement(
4781 : NS_LITERAL_CSTRING("PRAGMA foreign_key_check;"),
4782 : getter_AddRefs(checkStmt)));
4783 :
4784 : bool hasResult;
4785 0 : MOZ_ALWAYS_SUCCEEDS(checkStmt->ExecuteStep(&hasResult));
4786 0 : MOZ_ASSERT(!hasResult, "Database has inconsisistent foreign keys!");
4787 :
4788 0 : MOZ_ALWAYS_SUCCEEDS(
4789 : connection->ExecuteSimpleSQL(
4790 : NS_LITERAL_CSTRING("PRAGMA foreign_keys = OFF;")));
4791 : }
4792 : #endif
4793 :
4794 0 : if (kSQLitePageSizeOverride && !newDatabase) {
4795 0 : nsCOMPtr<mozIStorageStatement> stmt;
4796 0 : rv = connection->CreateStatement(NS_LITERAL_CSTRING(
4797 : "PRAGMA page_size;"
4798 0 : ), getter_AddRefs(stmt));
4799 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4800 0 : return rv;
4801 : }
4802 :
4803 : bool hasResult;
4804 0 : rv = stmt->ExecuteStep(&hasResult);
4805 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4806 0 : return rv;
4807 : }
4808 :
4809 0 : MOZ_ASSERT(hasResult);
4810 :
4811 : int32_t pageSize;
4812 0 : rv = stmt->GetInt32(0, &pageSize);
4813 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4814 0 : return rv;
4815 : }
4816 :
4817 0 : MOZ_ASSERT(pageSize >= 512 && pageSize <= 65536);
4818 :
4819 0 : if (kSQLitePageSizeOverride != uint32_t(pageSize)) {
4820 : // We must not be in WAL journal mode to change the page size.
4821 0 : rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
4822 : "PRAGMA journal_mode = DELETE;"
4823 0 : ));
4824 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4825 0 : return rv;
4826 : }
4827 :
4828 0 : rv = connection->CreateStatement(NS_LITERAL_CSTRING(
4829 : "PRAGMA journal_mode;"
4830 0 : ), getter_AddRefs(stmt));
4831 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4832 0 : return rv;
4833 : }
4834 :
4835 0 : rv = stmt->ExecuteStep(&hasResult);
4836 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4837 0 : return rv;
4838 : }
4839 :
4840 0 : MOZ_ASSERT(hasResult);
4841 :
4842 0 : nsCString journalMode;
4843 0 : rv = stmt->GetUTF8String(0, journalMode);
4844 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4845 0 : return rv;
4846 : }
4847 :
4848 0 : if (journalMode.EqualsLiteral("delete")) {
4849 : // Successfully set to rollback journal mode so changing the page size
4850 : // is possible with a VACUUM.
4851 0 : rv = connection->ExecuteSimpleSQL(
4852 0 : nsPrintfCString("PRAGMA page_size = %" PRIu32 ";", kSQLitePageSizeOverride)
4853 0 : );
4854 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4855 0 : return rv;
4856 : }
4857 :
4858 : // We will need to VACUUM in order to change the page size.
4859 0 : vacuumNeeded = true;
4860 : } else {
4861 : NS_WARNING("Failed to set journal_mode for database, unable to "
4862 0 : "change the page size!");
4863 : }
4864 : }
4865 : }
4866 :
4867 0 : if (vacuumNeeded) {
4868 0 : rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;"));
4869 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4870 0 : return rv;
4871 : }
4872 : }
4873 :
4874 0 : if (newDatabase || vacuumNeeded) {
4875 0 : if (journalModeSet) {
4876 : // Make sure we checkpoint to get an accurate file size.
4877 0 : rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
4878 : "PRAGMA wal_checkpoint(FULL);"
4879 0 : ));
4880 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4881 0 : return rv;
4882 : }
4883 : }
4884 :
4885 : int64_t fileSize;
4886 0 : rv = aDBFile->GetFileSize(&fileSize);
4887 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4888 0 : return rv;
4889 : }
4890 :
4891 0 : MOZ_ASSERT(fileSize > 0);
4892 :
4893 0 : PRTime vacuumTime = PR_Now();
4894 0 : MOZ_ASSERT(vacuumTime);
4895 :
4896 0 : nsCOMPtr<mozIStorageStatement> vacuumTimeStmt;
4897 0 : rv = connection->CreateStatement(NS_LITERAL_CSTRING(
4898 : "UPDATE database "
4899 : "SET last_vacuum_time = :time"
4900 : ", last_vacuum_size = :size;"
4901 0 : ), getter_AddRefs(vacuumTimeStmt));
4902 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4903 0 : return rv;
4904 : }
4905 :
4906 0 : rv = vacuumTimeStmt->BindInt64ByName(NS_LITERAL_CSTRING("time"),
4907 0 : vacuumTime);
4908 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4909 0 : return rv;
4910 : }
4911 :
4912 0 : rv = vacuumTimeStmt->BindInt64ByName(NS_LITERAL_CSTRING("size"),
4913 0 : fileSize);
4914 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4915 0 : return rv;
4916 : }
4917 :
4918 0 : rv = vacuumTimeStmt->Execute();
4919 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4920 0 : return rv;
4921 : }
4922 : }
4923 : }
4924 :
4925 0 : if (!journalModeSet) {
4926 0 : rv = SetJournalMode(connection);
4927 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4928 0 : return rv;
4929 : }
4930 : }
4931 :
4932 0 : connection.forget(aConnection);
4933 0 : return NS_OK;
4934 : }
4935 :
4936 : already_AddRefed<nsIFile>
4937 0 : GetFileForPath(const nsAString& aPath)
4938 : {
4939 0 : MOZ_ASSERT(!aPath.IsEmpty());
4940 :
4941 0 : nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
4942 0 : if (NS_WARN_IF(!file)) {
4943 0 : return nullptr;
4944 : }
4945 :
4946 0 : if (NS_WARN_IF(NS_FAILED(file->InitWithPath(aPath)))) {
4947 0 : return nullptr;
4948 : }
4949 :
4950 0 : return file.forget();
4951 : }
4952 :
4953 : nsresult
4954 0 : GetStorageConnection(nsIFile* aDatabaseFile,
4955 : PersistenceType aPersistenceType,
4956 : const nsACString& aGroup,
4957 : const nsACString& aOrigin,
4958 : uint32_t aTelemetryId,
4959 : mozIStorageConnection** aConnection)
4960 : {
4961 0 : MOZ_ASSERT(!NS_IsMainThread());
4962 0 : MOZ_ASSERT(!IsOnBackgroundThread());
4963 0 : MOZ_ASSERT(aDatabaseFile);
4964 0 : MOZ_ASSERT(aConnection);
4965 :
4966 0 : AUTO_PROFILER_LABEL("GetStorageConnection", STORAGE);
4967 :
4968 : bool exists;
4969 0 : nsresult rv = aDatabaseFile->Exists(&exists);
4970 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4971 0 : return rv;
4972 : }
4973 :
4974 0 : if (NS_WARN_IF(!exists)) {
4975 0 : IDB_REPORT_INTERNAL_ERR();
4976 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4977 : }
4978 :
4979 0 : nsCOMPtr<nsIFileURL> dbFileUrl;
4980 0 : rv = GetDatabaseFileURL(aDatabaseFile,
4981 : aPersistenceType,
4982 : aGroup,
4983 : aOrigin,
4984 : aTelemetryId,
4985 0 : getter_AddRefs(dbFileUrl));
4986 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4987 0 : return rv;
4988 : }
4989 :
4990 : nsCOMPtr<mozIStorageService> ss =
4991 0 : do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
4992 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4993 0 : return rv;
4994 : }
4995 :
4996 0 : nsCOMPtr<mozIStorageConnection> connection;
4997 0 : rv = OpenDatabaseAndHandleBusy(ss, dbFileUrl, getter_AddRefs(connection));
4998 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4999 0 : return rv;
5000 : }
5001 :
5002 0 : rv = SetDefaultPragmas(connection);
5003 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5004 0 : return rv;
5005 : }
5006 :
5007 0 : rv = SetJournalMode(connection);
5008 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5009 0 : return rv;
5010 : }
5011 :
5012 0 : connection.forget(aConnection);
5013 0 : return NS_OK;
5014 : }
5015 :
5016 : nsresult
5017 0 : GetStorageConnection(const nsAString& aDatabaseFilePath,
5018 : PersistenceType aPersistenceType,
5019 : const nsACString& aGroup,
5020 : const nsACString& aOrigin,
5021 : uint32_t aTelemetryId,
5022 : mozIStorageConnection** aConnection)
5023 : {
5024 0 : MOZ_ASSERT(!NS_IsMainThread());
5025 0 : MOZ_ASSERT(!IsOnBackgroundThread());
5026 0 : MOZ_ASSERT(!aDatabaseFilePath.IsEmpty());
5027 0 : MOZ_ASSERT(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")));
5028 0 : MOZ_ASSERT(aConnection);
5029 :
5030 0 : nsCOMPtr<nsIFile> dbFile = GetFileForPath(aDatabaseFilePath);
5031 0 : if (NS_WARN_IF(!dbFile)) {
5032 0 : IDB_REPORT_INTERNAL_ERR();
5033 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
5034 : }
5035 :
5036 0 : return GetStorageConnection(dbFile,
5037 : aPersistenceType,
5038 : aGroup,
5039 : aOrigin,
5040 : aTelemetryId,
5041 0 : aConnection);
5042 : }
5043 :
5044 : /*******************************************************************************
5045 : * ConnectionPool declarations
5046 : ******************************************************************************/
5047 :
5048 : class DatabaseConnection final
5049 : {
5050 : friend class ConnectionPool;
5051 :
5052 : enum class CheckpointMode
5053 : {
5054 : Full,
5055 : Restart,
5056 : Truncate
5057 : };
5058 :
5059 : public:
5060 : class AutoSavepoint;
5061 : class CachedStatement;
5062 : class UpdateRefcountFunction;
5063 :
5064 : private:
5065 : nsCOMPtr<mozIStorageConnection> mStorageConnection;
5066 : RefPtr<FileManager> mFileManager;
5067 : nsInterfaceHashtable<nsCStringHashKey, mozIStorageStatement>
5068 : mCachedStatements;
5069 : RefPtr<UpdateRefcountFunction> mUpdateRefcountFunction;
5070 : RefPtr<QuotaObject> mQuotaObject;
5071 : RefPtr<QuotaObject> mJournalQuotaObject;
5072 : bool mInReadTransaction;
5073 : bool mInWriteTransaction;
5074 :
5075 : #ifdef DEBUG
5076 : uint32_t mDEBUGSavepointCount;
5077 : #endif
5078 :
5079 : NS_DECL_OWNINGTHREAD
5080 :
5081 : public:
5082 : void
5083 0 : AssertIsOnConnectionThread() const
5084 : {
5085 0 : NS_ASSERT_OWNINGTHREAD(DatabaseConnection);
5086 0 : }
5087 :
5088 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DatabaseConnection)
5089 :
5090 : mozIStorageConnection*
5091 0 : GetStorageConnection() const
5092 : {
5093 0 : if (mStorageConnection) {
5094 0 : AssertIsOnConnectionThread();
5095 0 : return mStorageConnection;
5096 : }
5097 :
5098 0 : return nullptr;
5099 : }
5100 :
5101 : UpdateRefcountFunction*
5102 0 : GetUpdateRefcountFunction() const
5103 : {
5104 0 : AssertIsOnConnectionThread();
5105 :
5106 0 : return mUpdateRefcountFunction;
5107 : }
5108 :
5109 : nsresult
5110 : GetCachedStatement(const nsACString& aQuery,
5111 : CachedStatement* aCachedStatement);
5112 :
5113 : nsresult
5114 : BeginWriteTransaction();
5115 :
5116 : nsresult
5117 : CommitWriteTransaction();
5118 :
5119 : void
5120 : RollbackWriteTransaction();
5121 :
5122 : void
5123 : FinishWriteTransaction();
5124 :
5125 : nsresult
5126 : StartSavepoint();
5127 :
5128 : nsresult
5129 : ReleaseSavepoint();
5130 :
5131 : nsresult
5132 : RollbackSavepoint();
5133 :
5134 : nsresult
5135 0 : Checkpoint()
5136 : {
5137 0 : AssertIsOnConnectionThread();
5138 :
5139 0 : return CheckpointInternal(CheckpointMode::Full);
5140 : }
5141 :
5142 : void
5143 : DoIdleProcessing(bool aNeedsCheckpoint);
5144 :
5145 : void
5146 : Close();
5147 :
5148 : nsresult
5149 : DisableQuotaChecks();
5150 :
5151 : void
5152 : EnableQuotaChecks();
5153 :
5154 : private:
5155 : DatabaseConnection(mozIStorageConnection* aStorageConnection,
5156 : FileManager* aFileManager);
5157 :
5158 : ~DatabaseConnection();
5159 :
5160 : nsresult
5161 : Init();
5162 :
5163 : nsresult
5164 : CheckpointInternal(CheckpointMode aMode);
5165 :
5166 : nsresult
5167 : GetFreelistCount(CachedStatement& aCachedStatement, uint32_t* aFreelistCount);
5168 :
5169 : nsresult
5170 : ReclaimFreePagesWhileIdle(CachedStatement& aFreelistStatement,
5171 : CachedStatement& aRollbackStatement,
5172 : uint32_t aFreelistCount,
5173 : bool aNeedsCheckpoint,
5174 : bool* aFreedSomePages);
5175 :
5176 : nsresult
5177 : GetFileSize(const nsAString& aPath, int64_t* aResult);
5178 : };
5179 :
5180 : class MOZ_STACK_CLASS DatabaseConnection::AutoSavepoint final
5181 : {
5182 : DatabaseConnection* mConnection;
5183 : #ifdef DEBUG
5184 : const TransactionBase* mDEBUGTransaction;
5185 : #endif
5186 :
5187 : public:
5188 : AutoSavepoint();
5189 : ~AutoSavepoint();
5190 :
5191 : nsresult
5192 : Start(const TransactionBase* aTransaction);
5193 :
5194 : nsresult
5195 : Commit();
5196 : };
5197 :
5198 : class DatabaseConnection::CachedStatement final
5199 : {
5200 : friend class DatabaseConnection;
5201 :
5202 : nsCOMPtr<mozIStorageStatement> mStatement;
5203 : Maybe<mozStorageStatementScoper> mScoper;
5204 :
5205 : #ifdef DEBUG
5206 : DatabaseConnection* mDEBUGConnection;
5207 : #endif
5208 :
5209 : public:
5210 : CachedStatement();
5211 : ~CachedStatement();
5212 :
5213 : void
5214 0 : AssertIsOnConnectionThread() const
5215 : {
5216 : #ifdef DEBUG
5217 0 : if (mDEBUGConnection) {
5218 0 : mDEBUGConnection->AssertIsOnConnectionThread();
5219 : }
5220 0 : MOZ_ASSERT(!NS_IsMainThread());
5221 0 : MOZ_ASSERT(!IsOnBackgroundThread());
5222 : #endif
5223 0 : }
5224 :
5225 : operator mozIStorageStatement*() const;
5226 :
5227 : mozIStorageStatement*
5228 : operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN;
5229 :
5230 : void
5231 : Reset();
5232 :
5233 : private:
5234 : // Only called by DatabaseConnection.
5235 : void
5236 : Assign(DatabaseConnection* aConnection,
5237 : already_AddRefed<mozIStorageStatement> aStatement);
5238 :
5239 : // No funny business allowed.
5240 : CachedStatement(const CachedStatement&) = delete;
5241 : CachedStatement& operator=(const CachedStatement&) = delete;
5242 : };
5243 :
5244 : class DatabaseConnection::UpdateRefcountFunction final
5245 : : public mozIStorageFunction
5246 : {
5247 : class DatabaseUpdateFunction;
5248 : class FileInfoEntry;
5249 :
5250 : enum class UpdateType
5251 : {
5252 : Increment,
5253 : Decrement
5254 : };
5255 :
5256 : DatabaseConnection* mConnection;
5257 : FileManager* mFileManager;
5258 : nsClassHashtable<nsUint64HashKey, FileInfoEntry> mFileInfoEntries;
5259 : nsDataHashtable<nsUint64HashKey, FileInfoEntry*> mSavepointEntriesIndex;
5260 :
5261 : nsTArray<int64_t> mJournalsToCreateBeforeCommit;
5262 : nsTArray<int64_t> mJournalsToRemoveAfterCommit;
5263 : nsTArray<int64_t> mJournalsToRemoveAfterAbort;
5264 :
5265 : bool mInSavepoint;
5266 :
5267 : public:
5268 : NS_DECL_ISUPPORTS
5269 : NS_DECL_MOZISTORAGEFUNCTION
5270 :
5271 : UpdateRefcountFunction(DatabaseConnection* aConnection,
5272 : FileManager* aFileManager);
5273 :
5274 : nsresult
5275 : WillCommit();
5276 :
5277 : void
5278 : DidCommit();
5279 :
5280 : void
5281 : DidAbort();
5282 :
5283 : void
5284 : StartSavepoint();
5285 :
5286 : void
5287 : ReleaseSavepoint();
5288 :
5289 : void
5290 : RollbackSavepoint();
5291 :
5292 : void
5293 : Reset();
5294 :
5295 : private:
5296 0 : ~UpdateRefcountFunction() = default;
5297 :
5298 : nsresult
5299 : ProcessValue(mozIStorageValueArray* aValues,
5300 : int32_t aIndex,
5301 : UpdateType aUpdateType);
5302 :
5303 : nsresult
5304 : CreateJournals();
5305 :
5306 : nsresult
5307 : RemoveJournals(const nsTArray<int64_t>& aJournals);
5308 : };
5309 :
5310 : class DatabaseConnection::UpdateRefcountFunction::DatabaseUpdateFunction final
5311 : {
5312 : CachedStatement mUpdateStatement;
5313 : CachedStatement mSelectStatement;
5314 : CachedStatement mInsertStatement;
5315 :
5316 : UpdateRefcountFunction* mFunction;
5317 :
5318 : nsresult mErrorCode;
5319 :
5320 : public:
5321 : explicit
5322 0 : DatabaseUpdateFunction(UpdateRefcountFunction* aFunction)
5323 0 : : mFunction(aFunction)
5324 0 : , mErrorCode(NS_OK)
5325 : {
5326 0 : MOZ_COUNT_CTOR(
5327 : DatabaseConnection::UpdateRefcountFunction::DatabaseUpdateFunction);
5328 0 : }
5329 :
5330 0 : ~DatabaseUpdateFunction()
5331 0 : {
5332 0 : MOZ_COUNT_DTOR(
5333 : DatabaseConnection::UpdateRefcountFunction::DatabaseUpdateFunction);
5334 0 : }
5335 :
5336 : bool
5337 : Update(int64_t aId, int32_t aDelta);
5338 :
5339 : nsresult
5340 0 : ErrorCode() const
5341 : {
5342 0 : return mErrorCode;
5343 : }
5344 :
5345 : private:
5346 : nsresult
5347 : UpdateInternal(int64_t aId, int32_t aDelta);
5348 : };
5349 :
5350 : class DatabaseConnection::UpdateRefcountFunction::FileInfoEntry final
5351 : {
5352 : friend class UpdateRefcountFunction;
5353 :
5354 : RefPtr<FileInfo> mFileInfo;
5355 : int32_t mDelta;
5356 : int32_t mSavepointDelta;
5357 :
5358 : public:
5359 : explicit
5360 0 : FileInfoEntry(FileInfo* aFileInfo)
5361 0 : : mFileInfo(aFileInfo)
5362 : , mDelta(0)
5363 0 : , mSavepointDelta(0)
5364 : {
5365 0 : MOZ_COUNT_CTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry);
5366 0 : }
5367 :
5368 0 : ~FileInfoEntry()
5369 0 : {
5370 0 : MOZ_COUNT_DTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry);
5371 0 : }
5372 : };
5373 :
5374 : class ConnectionPool final
5375 : {
5376 : public:
5377 : class FinishCallback;
5378 :
5379 : private:
5380 : class ConnectionRunnable;
5381 : class CloseConnectionRunnable;
5382 : struct DatabaseInfo;
5383 : struct DatabasesCompleteCallback;
5384 : class FinishCallbackWrapper;
5385 : class IdleConnectionRunnable;
5386 : struct IdleDatabaseInfo;
5387 : struct IdleResource;
5388 : struct IdleThreadInfo;
5389 : struct ThreadInfo;
5390 : class ThreadRunnable;
5391 : class TransactionInfo;
5392 : struct TransactionInfoPair;
5393 :
5394 : // This mutex guards mDatabases, see below.
5395 : Mutex mDatabasesMutex;
5396 :
5397 : nsTArray<IdleThreadInfo> mIdleThreads;
5398 : nsTArray<IdleDatabaseInfo> mIdleDatabases;
5399 : nsTArray<DatabaseInfo*> mDatabasesPerformingIdleMaintenance;
5400 : nsCOMPtr<nsITimer> mIdleTimer;
5401 : TimeStamp mTargetIdleTime;
5402 :
5403 : // Only modifed on the owning thread, but read on multiple threads. Therefore
5404 : // all modifications and all reads off the owning thread must be protected by
5405 : // mDatabasesMutex.
5406 : nsClassHashtable<nsCStringHashKey, DatabaseInfo> mDatabases;
5407 :
5408 : nsClassHashtable<nsUint64HashKey, TransactionInfo> mTransactions;
5409 : nsTArray<TransactionInfo*> mQueuedTransactions;
5410 :
5411 : nsTArray<nsAutoPtr<DatabasesCompleteCallback>> mCompleteCallbacks;
5412 :
5413 : uint64_t mNextTransactionId;
5414 : uint32_t mTotalThreadCount;
5415 : bool mShutdownRequested;
5416 : bool mShutdownComplete;
5417 :
5418 : public:
5419 : ConnectionPool();
5420 :
5421 : void
5422 0 : AssertIsOnOwningThread() const
5423 : {
5424 0 : NS_ASSERT_OWNINGTHREAD(ConnectionPool);
5425 0 : }
5426 :
5427 : nsresult
5428 : GetOrCreateConnection(const Database* aDatabase,
5429 : DatabaseConnection** aConnection);
5430 :
5431 : uint64_t
5432 : Start(const nsID& aBackgroundChildLoggingId,
5433 : const nsACString& aDatabaseId,
5434 : int64_t aLoggingSerialNumber,
5435 : const nsTArray<nsString>& aObjectStoreNames,
5436 : bool aIsWriteTransaction,
5437 : TransactionDatabaseOperationBase* aTransactionOp);
5438 :
5439 : void
5440 : Dispatch(uint64_t aTransactionId, nsIRunnable* aRunnable);
5441 :
5442 : void
5443 : Finish(uint64_t aTransactionId, FinishCallback* aCallback);
5444 :
5445 : void
5446 0 : CloseDatabaseWhenIdle(const nsACString& aDatabaseId)
5447 : {
5448 0 : Unused << CloseDatabaseWhenIdleInternal(aDatabaseId);
5449 0 : }
5450 :
5451 : void
5452 : WaitForDatabasesToComplete(nsTArray<nsCString>&& aDatabaseIds,
5453 : nsIRunnable* aCallback);
5454 :
5455 : void
5456 : Shutdown();
5457 :
5458 0 : NS_INLINE_DECL_REFCOUNTING(ConnectionPool)
5459 :
5460 : private:
5461 : ~ConnectionPool();
5462 :
5463 : static void
5464 : IdleTimerCallback(nsITimer* aTimer, void* aClosure);
5465 :
5466 : void
5467 : Cleanup();
5468 :
5469 : void
5470 : AdjustIdleTimer();
5471 :
5472 : void
5473 : CancelIdleTimer();
5474 :
5475 : void
5476 : ShutdownThread(ThreadInfo& aThreadInfo);
5477 :
5478 : void
5479 : CloseIdleDatabases();
5480 :
5481 : void
5482 : ShutdownIdleThreads();
5483 :
5484 : bool
5485 : ScheduleTransaction(TransactionInfo* aTransactionInfo,
5486 : bool aFromQueuedTransactions);
5487 :
5488 : void
5489 : NoteFinishedTransaction(uint64_t aTransactionId);
5490 :
5491 : void
5492 : ScheduleQueuedTransactions(ThreadInfo& aThreadInfo);
5493 :
5494 : void
5495 : NoteIdleDatabase(DatabaseInfo* aDatabaseInfo);
5496 :
5497 : void
5498 : NoteClosedDatabase(DatabaseInfo* aDatabaseInfo);
5499 :
5500 : bool
5501 : MaybeFireCallback(DatabasesCompleteCallback* aCallback);
5502 :
5503 : void
5504 : PerformIdleDatabaseMaintenance(DatabaseInfo* aDatabaseInfo);
5505 :
5506 : void
5507 : CloseDatabase(DatabaseInfo* aDatabaseInfo);
5508 :
5509 : bool
5510 : CloseDatabaseWhenIdleInternal(const nsACString& aDatabaseId);
5511 : };
5512 :
5513 : class ConnectionPool::ConnectionRunnable
5514 : : public Runnable
5515 : {
5516 : protected:
5517 : DatabaseInfo* mDatabaseInfo;
5518 : nsCOMPtr<nsIEventTarget> mOwningEventTarget;
5519 :
5520 : explicit
5521 : ConnectionRunnable(DatabaseInfo* aDatabaseInfo);
5522 :
5523 0 : ~ConnectionRunnable() override = default;
5524 : };
5525 :
5526 : class ConnectionPool::IdleConnectionRunnable final
5527 : : public ConnectionRunnable
5528 : {
5529 : bool mNeedsCheckpoint;
5530 :
5531 : public:
5532 0 : IdleConnectionRunnable(DatabaseInfo* aDatabaseInfo, bool aNeedsCheckpoint)
5533 0 : : ConnectionRunnable(aDatabaseInfo)
5534 0 : , mNeedsCheckpoint(aNeedsCheckpoint)
5535 0 : { }
5536 :
5537 : NS_DECL_ISUPPORTS_INHERITED
5538 :
5539 : private:
5540 0 : ~IdleConnectionRunnable() override = default;
5541 :
5542 : NS_DECL_NSIRUNNABLE
5543 : };
5544 :
5545 : class ConnectionPool::CloseConnectionRunnable final
5546 : : public ConnectionRunnable
5547 : {
5548 : public:
5549 : explicit
5550 0 : CloseConnectionRunnable(DatabaseInfo* aDatabaseInfo)
5551 0 : : ConnectionRunnable(aDatabaseInfo)
5552 0 : { }
5553 :
5554 : NS_DECL_ISUPPORTS_INHERITED
5555 :
5556 : private:
5557 0 : ~CloseConnectionRunnable() override = default;
5558 :
5559 : NS_DECL_NSIRUNNABLE
5560 : };
5561 :
5562 : struct ConnectionPool::ThreadInfo
5563 : {
5564 : nsCOMPtr<nsIThread> mThread;
5565 : RefPtr<ThreadRunnable> mRunnable;
5566 :
5567 : ThreadInfo();
5568 :
5569 : explicit
5570 : ThreadInfo(const ThreadInfo& aOther);
5571 :
5572 : ~ThreadInfo();
5573 : };
5574 :
5575 : struct ConnectionPool::DatabaseInfo final
5576 : {
5577 : friend class nsAutoPtr<DatabaseInfo>;
5578 :
5579 : RefPtr<ConnectionPool> mConnectionPool;
5580 : const nsCString mDatabaseId;
5581 : RefPtr<DatabaseConnection> mConnection;
5582 : nsClassHashtable<nsStringHashKey, TransactionInfoPair> mBlockingTransactions;
5583 : nsTArray<TransactionInfo*> mTransactionsScheduledDuringClose;
5584 : nsTArray<TransactionInfo*> mScheduledWriteTransactions;
5585 : TransactionInfo* mRunningWriteTransaction;
5586 : ThreadInfo mThreadInfo;
5587 : uint32_t mReadTransactionCount;
5588 : uint32_t mWriteTransactionCount;
5589 : bool mNeedsCheckpoint;
5590 : bool mIdle;
5591 : bool mCloseOnIdle;
5592 : bool mClosing;
5593 :
5594 : #ifdef DEBUG
5595 : PRThread* mDEBUGConnectionThread;
5596 : #endif
5597 :
5598 : DatabaseInfo(ConnectionPool* aConnectionPool,
5599 : const nsACString& aDatabaseId);
5600 :
5601 : void
5602 0 : AssertIsOnConnectionThread() const
5603 : {
5604 0 : MOZ_ASSERT(mDEBUGConnectionThread);
5605 0 : MOZ_ASSERT(GetCurrentPhysicalThread() == mDEBUGConnectionThread);
5606 0 : }
5607 :
5608 : uint64_t
5609 0 : TotalTransactionCount() const
5610 : {
5611 0 : return mReadTransactionCount + mWriteTransactionCount;
5612 : }
5613 :
5614 : private:
5615 : ~DatabaseInfo();
5616 :
5617 : DatabaseInfo(const DatabaseInfo&) = delete;
5618 : DatabaseInfo& operator=(const DatabaseInfo&) = delete;
5619 : };
5620 :
5621 : struct ConnectionPool::DatabasesCompleteCallback final
5622 : {
5623 : friend class nsAutoPtr<DatabasesCompleteCallback>;
5624 :
5625 : nsTArray<nsCString> mDatabaseIds;
5626 : nsCOMPtr<nsIRunnable> mCallback;
5627 :
5628 : DatabasesCompleteCallback(nsTArray<nsCString>&& aDatabaseIds,
5629 : nsIRunnable* aCallback);
5630 :
5631 : private:
5632 : ~DatabasesCompleteCallback();
5633 : };
5634 :
5635 : class NS_NO_VTABLE ConnectionPool::FinishCallback
5636 : : public nsIRunnable
5637 : {
5638 : public:
5639 : // Called on the owning thread before any additional transactions are
5640 : // unblocked.
5641 : virtual void
5642 : TransactionFinishedBeforeUnblock() = 0;
5643 :
5644 : // Called on the owning thread after additional transactions may have been
5645 : // unblocked.
5646 : virtual void
5647 : TransactionFinishedAfterUnblock() = 0;
5648 :
5649 : protected:
5650 0 : FinishCallback()
5651 0 : { }
5652 :
5653 0 : virtual ~FinishCallback() = default;
5654 : };
5655 :
5656 : class ConnectionPool::FinishCallbackWrapper final
5657 : : public Runnable
5658 : {
5659 : RefPtr<ConnectionPool> mConnectionPool;
5660 : RefPtr<FinishCallback> mCallback;
5661 : nsCOMPtr<nsIEventTarget> mOwningEventTarget;
5662 : uint64_t mTransactionId;
5663 : bool mHasRunOnce;
5664 :
5665 : public:
5666 : FinishCallbackWrapper(ConnectionPool* aConnectionPool,
5667 : uint64_t aTransactionId,
5668 : FinishCallback* aCallback);
5669 :
5670 : NS_DECL_ISUPPORTS_INHERITED
5671 :
5672 : private:
5673 : ~FinishCallbackWrapper() override;
5674 :
5675 : NS_DECL_NSIRUNNABLE
5676 : };
5677 :
5678 : struct ConnectionPool::IdleResource
5679 : {
5680 : TimeStamp mIdleTime;
5681 :
5682 : protected:
5683 : explicit
5684 : IdleResource(const TimeStamp& aIdleTime);
5685 :
5686 : explicit
5687 : IdleResource(const IdleResource& aOther) = delete;
5688 :
5689 : ~IdleResource();
5690 : };
5691 :
5692 : struct ConnectionPool::IdleDatabaseInfo final
5693 : : public IdleResource
5694 : {
5695 : DatabaseInfo* mDatabaseInfo;
5696 :
5697 : public:
5698 : MOZ_IMPLICIT
5699 : IdleDatabaseInfo(DatabaseInfo* aDatabaseInfo);
5700 :
5701 : explicit
5702 : IdleDatabaseInfo(const IdleDatabaseInfo& aOther) = delete;
5703 :
5704 : ~IdleDatabaseInfo();
5705 :
5706 : bool
5707 0 : operator==(const IdleDatabaseInfo& aOther) const
5708 : {
5709 0 : return mDatabaseInfo == aOther.mDatabaseInfo;
5710 : }
5711 :
5712 : bool
5713 0 : operator<(const IdleDatabaseInfo& aOther) const
5714 : {
5715 0 : return mIdleTime < aOther.mIdleTime;
5716 : }
5717 : };
5718 :
5719 : struct ConnectionPool::IdleThreadInfo final
5720 : : public IdleResource
5721 : {
5722 : ThreadInfo mThreadInfo;
5723 :
5724 : public:
5725 : // Boo, this is needed because nsTArray::InsertElementSorted() doesn't yet
5726 : // work with rvalue references.
5727 : MOZ_IMPLICIT
5728 : IdleThreadInfo(const ThreadInfo& aThreadInfo);
5729 :
5730 : explicit
5731 : IdleThreadInfo(const IdleThreadInfo& aOther) = delete;
5732 :
5733 : ~IdleThreadInfo();
5734 :
5735 : bool
5736 0 : operator==(const IdleThreadInfo& aOther) const
5737 : {
5738 0 : return mThreadInfo.mRunnable == aOther.mThreadInfo.mRunnable &&
5739 0 : mThreadInfo.mThread == aOther.mThreadInfo.mThread;
5740 : }
5741 :
5742 : bool
5743 0 : operator<(const IdleThreadInfo& aOther) const
5744 : {
5745 0 : return mIdleTime < aOther.mIdleTime;
5746 : }
5747 : };
5748 :
5749 : class ConnectionPool::ThreadRunnable final
5750 : : public Runnable
5751 : {
5752 : // Only touched on the background thread.
5753 : static uint32_t sNextSerialNumber;
5754 :
5755 : // Set at construction for logging.
5756 : const uint32_t mSerialNumber;
5757 :
5758 : // These two values are only modified on the connection thread.
5759 : bool mFirstRun;
5760 : bool mContinueRunning;
5761 :
5762 : public:
5763 : ThreadRunnable();
5764 :
5765 : NS_DECL_ISUPPORTS_INHERITED
5766 :
5767 : uint32_t
5768 0 : SerialNumber() const
5769 : {
5770 0 : return mSerialNumber;
5771 : }
5772 :
5773 0 : nsCString GetThreadName() const
5774 : {
5775 0 : return nsPrintfCString("IndexedDB #%" PRIu32, mSerialNumber);
5776 : }
5777 :
5778 : private:
5779 : ~ThreadRunnable() override;
5780 :
5781 : NS_DECL_NSIRUNNABLE
5782 : };
5783 :
5784 : class ConnectionPool::TransactionInfo final
5785 : {
5786 : friend class nsAutoPtr<TransactionInfo>;
5787 :
5788 : nsTHashtable<nsPtrHashKey<TransactionInfo>> mBlocking;
5789 : nsTArray<TransactionInfo*> mBlockingOrdered;
5790 :
5791 : public:
5792 : DatabaseInfo* mDatabaseInfo;
5793 : const nsID mBackgroundChildLoggingId;
5794 : const nsCString mDatabaseId;
5795 : const uint64_t mTransactionId;
5796 : const int64_t mLoggingSerialNumber;
5797 : const nsTArray<nsString> mObjectStoreNames;
5798 : nsTHashtable<nsPtrHashKey<TransactionInfo>> mBlockedOn;
5799 : nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
5800 : const bool mIsWriteTransaction;
5801 : bool mRunning;
5802 :
5803 : #ifdef DEBUG
5804 : bool mFinished;
5805 : #endif
5806 :
5807 : TransactionInfo(DatabaseInfo* aDatabaseInfo,
5808 : const nsID& aBackgroundChildLoggingId,
5809 : const nsACString& aDatabaseId,
5810 : uint64_t aTransactionId,
5811 : int64_t aLoggingSerialNumber,
5812 : const nsTArray<nsString>& aObjectStoreNames,
5813 : bool aIsWriteTransaction,
5814 : TransactionDatabaseOperationBase* aTransactionOp);
5815 :
5816 : void
5817 : AddBlockingTransaction(TransactionInfo* aTransactionInfo);
5818 :
5819 : void
5820 : RemoveBlockingTransactions();
5821 :
5822 : private:
5823 : ~TransactionInfo();
5824 :
5825 : void
5826 : MaybeUnblock(TransactionInfo* aTransactionInfo);
5827 : };
5828 :
5829 : struct ConnectionPool::TransactionInfoPair final
5830 : {
5831 : friend class nsAutoPtr<TransactionInfoPair>;
5832 :
5833 : // Multiple reading transactions can block future writes.
5834 : nsTArray<TransactionInfo*> mLastBlockingWrites;
5835 : // But only a single writing transaction can block future reads.
5836 : TransactionInfo* mLastBlockingReads;
5837 :
5838 : TransactionInfoPair();
5839 :
5840 : private:
5841 : ~TransactionInfoPair();
5842 : };
5843 :
5844 : /*******************************************************************************
5845 : * Actor class declarations
5846 : ******************************************************************************/
5847 :
5848 : class DatabaseOperationBase
5849 : : public Runnable
5850 : , public mozIStorageProgressHandler
5851 : {
5852 : friend class UpgradeFileIdsFunction;
5853 :
5854 : protected:
5855 : class AutoSetProgressHandler;
5856 :
5857 : typedef nsDataHashtable<nsUint64HashKey, bool> UniqueIndexTable;
5858 :
5859 : nsCOMPtr<nsIEventTarget> mOwningEventTarget;
5860 : const nsID mBackgroundChildLoggingId;
5861 : const uint64_t mLoggingSerialNumber;
5862 : nsresult mResultCode;
5863 :
5864 : private:
5865 : Atomic<bool> mOperationMayProceed;
5866 : bool mActorDestroyed;
5867 :
5868 : public:
5869 : NS_DECL_ISUPPORTS_INHERITED
5870 :
5871 : bool
5872 0 : IsOnOwningThread() const
5873 : {
5874 0 : MOZ_ASSERT(mOwningEventTarget);
5875 :
5876 : bool current;
5877 0 : return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(¤t)) && current;
5878 : }
5879 :
5880 : void
5881 0 : AssertIsOnOwningThread() const
5882 : {
5883 0 : MOZ_ASSERT(IsOnBackgroundThread());
5884 0 : MOZ_ASSERT(IsOnOwningThread());
5885 0 : }
5886 :
5887 : void
5888 0 : NoteActorDestroyed()
5889 : {
5890 0 : AssertIsOnOwningThread();
5891 :
5892 0 : mActorDestroyed = true;
5893 0 : mOperationMayProceed = false;
5894 0 : }
5895 :
5896 : bool
5897 0 : IsActorDestroyed() const
5898 : {
5899 0 : AssertIsOnOwningThread();
5900 :
5901 0 : return mActorDestroyed;
5902 : }
5903 :
5904 : // May be called on any thread, but you should call IsActorDestroyed() if
5905 : // you know you're on the background thread because it is slightly faster.
5906 : bool
5907 0 : OperationMayProceed() const
5908 : {
5909 0 : return mOperationMayProceed;
5910 : }
5911 :
5912 : const nsID&
5913 0 : BackgroundChildLoggingId() const
5914 : {
5915 0 : return mBackgroundChildLoggingId;
5916 : }
5917 :
5918 : uint64_t
5919 0 : LoggingSerialNumber() const
5920 : {
5921 0 : return mLoggingSerialNumber;
5922 : }
5923 :
5924 : nsresult
5925 0 : ResultCode() const
5926 : {
5927 0 : return mResultCode;
5928 : }
5929 :
5930 : void
5931 0 : SetFailureCode(nsresult aErrorCode)
5932 : {
5933 0 : MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
5934 0 : MOZ_ASSERT(NS_FAILED(aErrorCode));
5935 :
5936 0 : mResultCode = aErrorCode;
5937 0 : }
5938 :
5939 : protected:
5940 0 : DatabaseOperationBase(const nsID& aBackgroundChildLoggingId,
5941 : uint64_t aLoggingSerialNumber)
5942 0 : : Runnable("dom::indexedDB::DatabaseOperationBase")
5943 : , mOwningEventTarget(GetCurrentThreadEventTarget())
5944 : , mBackgroundChildLoggingId(aBackgroundChildLoggingId)
5945 : , mLoggingSerialNumber(aLoggingSerialNumber)
5946 : , mResultCode(NS_OK)
5947 : , mOperationMayProceed(true)
5948 0 : , mActorDestroyed(false)
5949 : {
5950 0 : AssertIsOnOwningThread();
5951 0 : }
5952 :
5953 0 : ~DatabaseOperationBase() override
5954 0 : {
5955 0 : MOZ_ASSERT(mActorDestroyed);
5956 0 : }
5957 :
5958 : static void
5959 : GetBindingClauseForKeyRange(const SerializedKeyRange& aKeyRange,
5960 : const nsACString& aKeyColumnName,
5961 : nsAutoCString& aBindingClause);
5962 :
5963 : static uint64_t
5964 : ReinterpretDoubleAsUInt64(double aDouble);
5965 :
5966 : static nsresult
5967 0 : GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement,
5968 : uint32_t aDataIndex,
5969 : uint32_t aFileIdsIndex,
5970 : FileManager* aFileManager,
5971 : StructuredCloneReadInfo* aInfo)
5972 : {
5973 : return GetStructuredCloneReadInfoFromSource(aStatement,
5974 : aDataIndex,
5975 : aFileIdsIndex,
5976 : aFileManager,
5977 0 : aInfo);
5978 : }
5979 :
5980 : static nsresult
5981 0 : GetStructuredCloneReadInfoFromValueArray(mozIStorageValueArray* aValues,
5982 : uint32_t aDataIndex,
5983 : uint32_t aFileIdsIndex,
5984 : FileManager* aFileManager,
5985 : StructuredCloneReadInfo* aInfo)
5986 : {
5987 : return GetStructuredCloneReadInfoFromSource(aValues,
5988 : aDataIndex,
5989 : aFileIdsIndex,
5990 : aFileManager,
5991 0 : aInfo);
5992 : }
5993 :
5994 : static nsresult
5995 : BindKeyRangeToStatement(const SerializedKeyRange& aKeyRange,
5996 : mozIStorageStatement* aStatement);
5997 :
5998 : static nsresult
5999 : BindKeyRangeToStatement(const SerializedKeyRange& aKeyRange,
6000 : mozIStorageStatement* aStatement,
6001 : const nsCString& aLocale);
6002 :
6003 : static void
6004 : AppendConditionClause(const nsACString& aColumnName,
6005 : const nsACString& aArgName,
6006 : bool aLessThan,
6007 : bool aEquals,
6008 : nsAutoCString& aResult);
6009 :
6010 : static nsresult
6011 : GetUniqueIndexTableForObjectStore(
6012 : TransactionBase* aTransaction,
6013 : int64_t aObjectStoreId,
6014 : Maybe<UniqueIndexTable>& aMaybeUniqueIndexTable);
6015 :
6016 : static nsresult
6017 : IndexDataValuesFromUpdateInfos(const nsTArray<IndexUpdateInfo>& aUpdateInfos,
6018 : const UniqueIndexTable& aUniqueIndexTable,
6019 : nsTArray<IndexDataValue>& aIndexValues);
6020 :
6021 : static nsresult
6022 : InsertIndexTableRows(DatabaseConnection* aConnection,
6023 : const int64_t aObjectStoreId,
6024 : const Key& aObjectStoreKey,
6025 : const FallibleTArray<IndexDataValue>& aIndexValues);
6026 :
6027 : static nsresult
6028 : DeleteIndexDataTableRows(DatabaseConnection* aConnection,
6029 : const Key& aObjectStoreKey,
6030 : const FallibleTArray<IndexDataValue>& aIndexValues);
6031 :
6032 : static nsresult
6033 : DeleteObjectStoreDataTableRowsWithIndexes(DatabaseConnection* aConnection,
6034 : const int64_t aObjectStoreId,
6035 : const OptionalKeyRange& aKeyRange);
6036 :
6037 : static nsresult
6038 : UpdateIndexValues(DatabaseConnection* aConnection,
6039 : const int64_t aObjectStoreId,
6040 : const Key& aObjectStoreKey,
6041 : const FallibleTArray<IndexDataValue>& aIndexValues);
6042 :
6043 : static nsresult
6044 : ObjectStoreHasIndexes(DatabaseConnection* aConnection,
6045 : const int64_t aObjectStoreId,
6046 : bool* aHasIndexes);
6047 :
6048 : private:
6049 : template <typename T>
6050 : static nsresult
6051 : GetStructuredCloneReadInfoFromSource(T* aSource,
6052 : uint32_t aDataIndex,
6053 : uint32_t aFileIdsIndex,
6054 : FileManager* aFileManager,
6055 : StructuredCloneReadInfo* aInfo);
6056 :
6057 : static nsresult
6058 : GetStructuredCloneReadInfoFromBlob(const uint8_t* aBlobData,
6059 : uint32_t aBlobDataLength,
6060 : FileManager* aFileManager,
6061 : const nsAString& aFileIds,
6062 : StructuredCloneReadInfo* aInfo);
6063 :
6064 : static nsresult
6065 : GetStructuredCloneReadInfoFromExternalBlob(uint64_t aIntData,
6066 : FileManager* aFileManager,
6067 : const nsAString& aFileIds,
6068 : StructuredCloneReadInfo* aInfo);
6069 :
6070 : // Not to be overridden by subclasses.
6071 : NS_DECL_MOZISTORAGEPROGRESSHANDLER
6072 : };
6073 :
6074 : class MOZ_STACK_CLASS DatabaseOperationBase::AutoSetProgressHandler final
6075 : {
6076 : mozIStorageConnection* mConnection;
6077 : #ifdef DEBUG
6078 : DatabaseOperationBase* mDEBUGDatabaseOp;
6079 : #endif
6080 :
6081 : public:
6082 : AutoSetProgressHandler();
6083 :
6084 : ~AutoSetProgressHandler();
6085 :
6086 : nsresult
6087 : Register(mozIStorageConnection* aConnection,
6088 : DatabaseOperationBase* aDatabaseOp);
6089 : };
6090 :
6091 : class TransactionDatabaseOperationBase
6092 : : public DatabaseOperationBase
6093 : {
6094 : enum class InternalState
6095 : {
6096 : Initial,
6097 : DatabaseWork,
6098 : SendingPreprocess,
6099 : WaitingForContinue,
6100 : SendingResults,
6101 : Completed
6102 : };
6103 :
6104 : RefPtr<TransactionBase> mTransaction;
6105 : const int64_t mTransactionLoggingSerialNumber;
6106 : InternalState mInternalState;
6107 : const bool mTransactionIsAborted;
6108 :
6109 : public:
6110 : void
6111 : AssertIsOnConnectionThread() const
6112 : #ifdef DEBUG
6113 : ;
6114 : #else
6115 : { }
6116 : #endif
6117 :
6118 : uint64_t
6119 : StartOnConnectionPool(const nsID& aBackgroundChildLoggingId,
6120 : const nsACString& aDatabaseId,
6121 : int64_t aLoggingSerialNumber,
6122 : const nsTArray<nsString>& aObjectStoreNames,
6123 : bool aIsWriteTransaction);
6124 :
6125 : void
6126 : DispatchToConnectionPool();
6127 :
6128 : TransactionBase*
6129 0 : Transaction() const
6130 : {
6131 0 : MOZ_ASSERT(mTransaction);
6132 :
6133 0 : return mTransaction;
6134 : }
6135 :
6136 : void
6137 : NoteContinueReceived();
6138 :
6139 : // May be overridden by subclasses if they need to perform work on the
6140 : // background thread before being dispatched. Returning false will kill the
6141 : // child actors and prevent dispatch.
6142 : virtual bool
6143 : Init(TransactionBase* aTransaction);
6144 :
6145 : // This callback will be called on the background thread before releasing the
6146 : // final reference to this request object. Subclasses may perform any
6147 : // additional cleanup here but must always call the base class implementation.
6148 : virtual void
6149 : Cleanup();
6150 :
6151 : protected:
6152 : explicit
6153 : TransactionDatabaseOperationBase(TransactionBase* aTransaction);
6154 :
6155 : TransactionDatabaseOperationBase(TransactionBase* aTransaction,
6156 : uint64_t aLoggingSerialNumber);
6157 :
6158 : ~TransactionDatabaseOperationBase() override;
6159 :
6160 : virtual void
6161 : RunOnConnectionThread();
6162 :
6163 : // Must be overridden in subclasses. Called on the target thread to allow the
6164 : // subclass to perform necessary database or file operations. A successful
6165 : // return value will trigger a SendSuccessResult callback on the background
6166 : // thread while a failure value will trigger a SendFailureResult callback.
6167 : virtual nsresult
6168 : DoDatabaseWork(DatabaseConnection* aConnection) = 0;
6169 :
6170 : // May be overriden in subclasses. Called on the background thread to decide
6171 : // if the subclass needs to send any preprocess info to the child actor.
6172 : virtual bool
6173 : HasPreprocessInfo();
6174 :
6175 : // May be overriden in subclasses. Called on the background thread to allow
6176 : // the subclass to serialize its preprocess info and send it to the child
6177 : // actor. A successful return value will trigger a wait for a
6178 : // NoteContinueReceived callback on the background thread while a failure
6179 : // value will trigger a SendFailureResult callback.
6180 : virtual nsresult
6181 : SendPreprocessInfo();
6182 :
6183 : // Must be overridden in subclasses. Called on the background thread to allow
6184 : // the subclass to serialize its results and send them to the child actor. A
6185 : // failed return value will trigger a SendFailureResult callback.
6186 : virtual nsresult
6187 : SendSuccessResult() = 0;
6188 :
6189 : // Must be overridden in subclasses. Called on the background thread to allow
6190 : // the subclass to send its failure code. Returning false will cause the
6191 : // transaction to be aborted with aResultCode. Returning true will not cause
6192 : // the transaction to be aborted.
6193 : virtual bool
6194 : SendFailureResult(nsresult aResultCode) = 0;
6195 :
6196 : private:
6197 : void
6198 : SendToConnectionPool();
6199 :
6200 : void
6201 : SendPreprocess();
6202 :
6203 : void
6204 : SendResults();
6205 :
6206 : void
6207 : SendPreprocessInfoOrResults(bool aSendPreprocessInfo);
6208 :
6209 : // Not to be overridden by subclasses.
6210 : NS_DECL_NSIRUNNABLE
6211 : };
6212 :
6213 : class Factory final
6214 : : public PBackgroundIDBFactoryParent
6215 : {
6216 :
6217 : RefPtr<DatabaseLoggingInfo> mLoggingInfo;
6218 :
6219 : #ifdef DEBUG
6220 : bool mActorDestroyed;
6221 : #endif
6222 :
6223 : public:
6224 : static already_AddRefed<Factory>
6225 : Create(const LoggingInfo& aLoggingInfo);
6226 :
6227 : DatabaseLoggingInfo*
6228 0 : GetLoggingInfo() const
6229 : {
6230 0 : AssertIsOnBackgroundThread();
6231 0 : MOZ_ASSERT(mLoggingInfo);
6232 :
6233 0 : return mLoggingInfo;
6234 : }
6235 :
6236 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Factory)
6237 :
6238 : private:
6239 : // Only constructed in Create().
6240 : explicit
6241 : Factory(already_AddRefed<DatabaseLoggingInfo> aLoggingInfo);
6242 :
6243 : // Reference counted.
6244 : ~Factory() override;
6245 :
6246 : // IPDL methods are only called by IPDL.
6247 : void
6248 : ActorDestroy(ActorDestroyReason aWhy) override;
6249 :
6250 : mozilla::ipc::IPCResult
6251 : RecvDeleteMe() override;
6252 :
6253 : mozilla::ipc::IPCResult
6254 : RecvIncrementLoggingRequestSerialNumber() override;
6255 :
6256 : PBackgroundIDBFactoryRequestParent*
6257 : AllocPBackgroundIDBFactoryRequestParent(const FactoryRequestParams& aParams)
6258 : override;
6259 :
6260 : mozilla::ipc::IPCResult
6261 : RecvPBackgroundIDBFactoryRequestConstructor(
6262 : PBackgroundIDBFactoryRequestParent* aActor,
6263 : const FactoryRequestParams& aParams)
6264 : override;
6265 :
6266 : bool
6267 : DeallocPBackgroundIDBFactoryRequestParent(
6268 : PBackgroundIDBFactoryRequestParent* aActor)
6269 : override;
6270 :
6271 : PBackgroundIDBDatabaseParent*
6272 : AllocPBackgroundIDBDatabaseParent(
6273 : const DatabaseSpec& aSpec,
6274 : PBackgroundIDBFactoryRequestParent* aRequest)
6275 : override;
6276 :
6277 : bool
6278 : DeallocPBackgroundIDBDatabaseParent(PBackgroundIDBDatabaseParent* aActor)
6279 : override;
6280 : };
6281 :
6282 : class WaitForTransactionsHelper final
6283 : : public Runnable
6284 : {
6285 : const nsCString mDatabaseId;
6286 : nsCOMPtr<nsIRunnable> mCallback;
6287 :
6288 : enum class State
6289 : {
6290 : Initial = 0,
6291 : WaitingForTransactions,
6292 : WaitingForFileHandles,
6293 : Complete
6294 : } mState;
6295 :
6296 : public:
6297 0 : WaitForTransactionsHelper(const nsCString& aDatabaseId,
6298 : nsIRunnable* aCallback)
6299 0 : : Runnable("dom::indexedDB::WaitForTransactionsHelper")
6300 : , mDatabaseId(aDatabaseId)
6301 : , mCallback(aCallback)
6302 0 : , mState(State::Initial)
6303 : {
6304 0 : AssertIsOnBackgroundThread();
6305 0 : MOZ_ASSERT(!aDatabaseId.IsEmpty());
6306 0 : MOZ_ASSERT(aCallback);
6307 0 : }
6308 :
6309 : void
6310 : WaitForTransactions();
6311 :
6312 : NS_DECL_ISUPPORTS_INHERITED
6313 :
6314 : private:
6315 0 : ~WaitForTransactionsHelper() override
6316 0 : {
6317 0 : MOZ_ASSERT(!mCallback);
6318 0 : MOZ_ASSERT(mState == State::Complete);
6319 0 : }
6320 :
6321 : void
6322 : MaybeWaitForTransactions();
6323 :
6324 : void
6325 : MaybeWaitForFileHandles();
6326 :
6327 : void
6328 : CallCallback();
6329 :
6330 : NS_DECL_NSIRUNNABLE
6331 : };
6332 :
6333 : class Database final
6334 : : public PBackgroundIDBDatabaseParent
6335 : {
6336 : friend class VersionChangeTransaction;
6337 :
6338 : class StartTransactionOp;
6339 : class UnmapBlobCallback;
6340 :
6341 : private:
6342 : RefPtr<Factory> mFactory;
6343 : RefPtr<FullDatabaseMetadata> mMetadata;
6344 : RefPtr<FileManager> mFileManager;
6345 : RefPtr<DirectoryLock> mDirectoryLock;
6346 : nsTHashtable<nsPtrHashKey<TransactionBase>> mTransactions;
6347 : nsTHashtable<nsPtrHashKey<MutableFile>> mMutableFiles;
6348 : nsRefPtrHashtable<nsIDHashKey, FileInfo> mMappedBlobs;
6349 : RefPtr<DatabaseConnection> mConnection;
6350 : const PrincipalInfo mPrincipalInfo;
6351 : const Maybe<ContentParentId> mOptionalContentParentId;
6352 : const nsCString mGroup;
6353 : const nsCString mOrigin;
6354 : const nsCString mId;
6355 : const nsString mFilePath;
6356 : uint32_t mActiveMutableFileCount;
6357 : const uint32_t mTelemetryId;
6358 : const PersistenceType mPersistenceType;
6359 : const bool mFileHandleDisabled;
6360 : const bool mChromeWriteAccessAllowed;
6361 : bool mClosed;
6362 : bool mInvalidated;
6363 : bool mActorWasAlive;
6364 : bool mActorDestroyed;
6365 : bool mMetadataCleanedUp;
6366 : #ifdef DEBUG
6367 : bool mAllBlobsUnmapped;
6368 : #endif
6369 :
6370 : public:
6371 : // Created by OpenDatabaseOp.
6372 : Database(Factory* aFactory,
6373 : const PrincipalInfo& aPrincipalInfo,
6374 : const Maybe<ContentParentId>& aOptionalContentParentId,
6375 : const nsACString& aGroup,
6376 : const nsACString& aOrigin,
6377 : uint32_t aTelemetryId,
6378 : FullDatabaseMetadata* aMetadata,
6379 : FileManager* aFileManager,
6380 : already_AddRefed<DirectoryLock> aDirectoryLock,
6381 : bool aFileHandleDisabled,
6382 : bool aChromeWriteAccessAllowed);
6383 :
6384 : void
6385 0 : AssertIsOnConnectionThread() const
6386 : {
6387 : #ifdef DEBUG
6388 0 : if (mConnection) {
6389 0 : MOZ_ASSERT(mConnection);
6390 0 : mConnection->AssertIsOnConnectionThread();
6391 : } else {
6392 0 : MOZ_ASSERT(!NS_IsMainThread());
6393 0 : MOZ_ASSERT(!IsOnBackgroundThread());
6394 0 : MOZ_ASSERT(mInvalidated);
6395 : }
6396 : #endif
6397 0 : }
6398 :
6399 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Database)
6400 :
6401 : void
6402 : Invalidate();
6403 :
6404 : bool
6405 0 : IsOwnedByProcess(ContentParentId aContentParentId) const
6406 : {
6407 : return
6408 0 : mOptionalContentParentId &&
6409 0 : mOptionalContentParentId.value() == aContentParentId;
6410 : }
6411 :
6412 : const nsCString&
6413 0 : Group() const
6414 : {
6415 0 : return mGroup;
6416 : }
6417 :
6418 : const nsCString&
6419 0 : Origin() const
6420 : {
6421 0 : return mOrigin;
6422 : }
6423 :
6424 : const nsCString&
6425 0 : Id() const
6426 : {
6427 0 : return mId;
6428 : }
6429 :
6430 : uint32_t
6431 0 : TelemetryId() const
6432 : {
6433 0 : return mTelemetryId;
6434 : }
6435 :
6436 : PersistenceType
6437 0 : Type() const
6438 : {
6439 0 : return mPersistenceType;
6440 : }
6441 :
6442 : const nsString&
6443 0 : FilePath() const
6444 : {
6445 0 : return mFilePath;
6446 : }
6447 :
6448 : FileManager*
6449 0 : GetFileManager() const
6450 : {
6451 0 : return mFileManager;
6452 : }
6453 :
6454 : FullDatabaseMetadata*
6455 0 : Metadata() const
6456 : {
6457 0 : MOZ_ASSERT(mMetadata);
6458 0 : return mMetadata;
6459 : }
6460 :
6461 : PBackgroundParent*
6462 0 : GetBackgroundParent() const
6463 : {
6464 0 : AssertIsOnBackgroundThread();
6465 0 : MOZ_ASSERT(!IsActorDestroyed());
6466 :
6467 0 : return Manager()->Manager();
6468 : }
6469 :
6470 : DatabaseLoggingInfo*
6471 0 : GetLoggingInfo() const
6472 : {
6473 0 : AssertIsOnBackgroundThread();
6474 0 : MOZ_ASSERT(mFactory);
6475 :
6476 0 : return mFactory->GetLoggingInfo();
6477 : }
6478 :
6479 : bool
6480 : RegisterTransaction(TransactionBase* aTransaction);
6481 :
6482 : void
6483 : UnregisterTransaction(TransactionBase* aTransaction);
6484 :
6485 : bool
6486 0 : IsFileHandleDisabled() const
6487 : {
6488 0 : return mFileHandleDisabled;
6489 : }
6490 :
6491 : bool
6492 : RegisterMutableFile(MutableFile* aMutableFile);
6493 :
6494 : void
6495 : UnregisterMutableFile(MutableFile* aMutableFile);
6496 :
6497 : void
6498 : NoteActiveMutableFile();
6499 :
6500 : void
6501 : NoteInactiveMutableFile();
6502 :
6503 : void
6504 : SetActorAlive();
6505 :
6506 : void
6507 : MapBlob(const IPCBlob& aIPCBlob, FileInfo* aFileInfo);
6508 :
6509 : bool
6510 0 : IsActorAlive() const
6511 : {
6512 0 : AssertIsOnBackgroundThread();
6513 :
6514 0 : return mActorWasAlive && !mActorDestroyed;
6515 : }
6516 :
6517 : bool
6518 0 : IsActorDestroyed() const
6519 : {
6520 0 : AssertIsOnBackgroundThread();
6521 :
6522 0 : return mActorWasAlive && mActorDestroyed;
6523 : }
6524 :
6525 : bool
6526 0 : IsClosed() const
6527 : {
6528 0 : AssertIsOnBackgroundThread();
6529 :
6530 0 : return mClosed;
6531 : }
6532 :
6533 : bool
6534 0 : IsInvalidated() const
6535 : {
6536 0 : AssertIsOnBackgroundThread();
6537 :
6538 0 : return mInvalidated;
6539 : }
6540 :
6541 : nsresult
6542 : EnsureConnection();
6543 :
6544 : DatabaseConnection*
6545 0 : GetConnection() const
6546 : {
6547 : #ifdef DEBUG
6548 0 : if (mConnection) {
6549 0 : mConnection->AssertIsOnConnectionThread();
6550 : }
6551 : #endif
6552 :
6553 0 : return mConnection;
6554 : }
6555 :
6556 : private:
6557 : // Reference counted.
6558 0 : ~Database() override
6559 0 : {
6560 0 : MOZ_ASSERT(mClosed);
6561 0 : MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed);
6562 0 : }
6563 :
6564 : already_AddRefed<FileInfo>
6565 : GetBlob(const IPCBlob& aID);
6566 :
6567 : void
6568 : UnmapBlob(const nsID& aID);
6569 :
6570 : void
6571 : UnmapAllBlobs();
6572 :
6573 : bool
6574 : CloseInternal();
6575 :
6576 : void
6577 : MaybeCloseConnection();
6578 :
6579 : void
6580 : ConnectionClosedCallback();
6581 :
6582 : void
6583 : CleanupMetadata();
6584 :
6585 : bool
6586 : VerifyRequestParams(const DatabaseRequestParams& aParams) const;
6587 :
6588 : // IPDL methods are only called by IPDL.
6589 : void
6590 : ActorDestroy(ActorDestroyReason aWhy) override;
6591 :
6592 : PBackgroundIDBDatabaseFileParent*
6593 : AllocPBackgroundIDBDatabaseFileParent(const IPCBlob& aIPCBlob) override;
6594 :
6595 : bool
6596 : DeallocPBackgroundIDBDatabaseFileParent(
6597 : PBackgroundIDBDatabaseFileParent* aActor)
6598 : override;
6599 :
6600 : PBackgroundIDBDatabaseRequestParent*
6601 : AllocPBackgroundIDBDatabaseRequestParent(const DatabaseRequestParams& aParams)
6602 : override;
6603 :
6604 : mozilla::ipc::IPCResult
6605 : RecvPBackgroundIDBDatabaseRequestConstructor(
6606 : PBackgroundIDBDatabaseRequestParent* aActor,
6607 : const DatabaseRequestParams& aParams)
6608 : override;
6609 :
6610 : bool
6611 : DeallocPBackgroundIDBDatabaseRequestParent(
6612 : PBackgroundIDBDatabaseRequestParent* aActor)
6613 : override;
6614 :
6615 : PBackgroundIDBTransactionParent*
6616 : AllocPBackgroundIDBTransactionParent(
6617 : const nsTArray<nsString>& aObjectStoreNames,
6618 : const Mode& aMode)
6619 : override;
6620 :
6621 : mozilla::ipc::IPCResult
6622 : RecvPBackgroundIDBTransactionConstructor(
6623 : PBackgroundIDBTransactionParent* aActor,
6624 : InfallibleTArray<nsString>&& aObjectStoreNames,
6625 : const Mode& aMode)
6626 : override;
6627 :
6628 : bool
6629 : DeallocPBackgroundIDBTransactionParent(
6630 : PBackgroundIDBTransactionParent* aActor)
6631 : override;
6632 :
6633 : PBackgroundIDBVersionChangeTransactionParent*
6634 : AllocPBackgroundIDBVersionChangeTransactionParent(
6635 : const uint64_t& aCurrentVersion,
6636 : const uint64_t& aRequestedVersion,
6637 : const int64_t& aNextObjectStoreId,
6638 : const int64_t& aNextIndexId)
6639 : override;
6640 :
6641 : bool
6642 : DeallocPBackgroundIDBVersionChangeTransactionParent(
6643 : PBackgroundIDBVersionChangeTransactionParent* aActor)
6644 : override;
6645 :
6646 : PBackgroundMutableFileParent*
6647 : AllocPBackgroundMutableFileParent(const nsString& aName,
6648 : const nsString& aType) override;
6649 :
6650 : bool
6651 : DeallocPBackgroundMutableFileParent(PBackgroundMutableFileParent* aActor)
6652 : override;
6653 :
6654 : mozilla::ipc::IPCResult
6655 : RecvDeleteMe() override;
6656 :
6657 : mozilla::ipc::IPCResult
6658 : RecvBlocked() override;
6659 :
6660 : mozilla::ipc::IPCResult
6661 : RecvClose() override;
6662 : };
6663 :
6664 : class Database::StartTransactionOp final
6665 : : public TransactionDatabaseOperationBase
6666 : {
6667 : friend class Database;
6668 :
6669 : private:
6670 : explicit
6671 0 : StartTransactionOp(TransactionBase* aTransaction)
6672 0 : : TransactionDatabaseOperationBase(aTransaction,
6673 0 : /* aLoggingSerialNumber */ 0)
6674 0 : { }
6675 :
6676 0 : ~StartTransactionOp() override = default;
6677 :
6678 : void
6679 : RunOnConnectionThread() override;
6680 :
6681 : nsresult
6682 : DoDatabaseWork(DatabaseConnection* aConnection) override;
6683 :
6684 : nsresult
6685 : SendSuccessResult() override;
6686 :
6687 : bool
6688 : SendFailureResult(nsresult aResultCode) override;
6689 :
6690 : void
6691 : Cleanup() override;
6692 : };
6693 :
6694 : class Database::UnmapBlobCallback final
6695 : : public IPCBlobInputStreamParentCallback
6696 : {
6697 : RefPtr<Database> mDatabase;
6698 :
6699 : public:
6700 0 : explicit UnmapBlobCallback(Database* aDatabase)
6701 0 : : mDatabase(aDatabase)
6702 : {
6703 0 : AssertIsOnBackgroundThread();
6704 0 : }
6705 :
6706 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Database::UnmapBlobCallback, override)
6707 :
6708 : void
6709 0 : ActorDestroyed(const nsID& aID) override
6710 : {
6711 0 : AssertIsOnBackgroundThread();
6712 0 : MOZ_ASSERT(mDatabase);
6713 :
6714 0 : RefPtr<Database> database;
6715 0 : mDatabase.swap(database);
6716 :
6717 0 : database->UnmapBlob(aID);
6718 0 : }
6719 :
6720 : private:
6721 0 : ~UnmapBlobCallback() = default;
6722 : };
6723 :
6724 : /**
6725 : * In coordination with IDBDatabase's mFileActors weak-map on the child side, a
6726 : * long-lived mapping from a child process's live Blobs to their corresponding
6727 : * FileInfo in our owning database. Assists in avoiding redundant IPC traffic
6728 : * and disk storage. This includes both:
6729 : * - Blobs retrieved from this database and sent to the child that do not need
6730 : * to be written to disk because they already exist on disk in this database's
6731 : * files directory.
6732 : * - Blobs retrieved from other databases or from anywhere else that will need
6733 : * to be written to this database's files directory. In this case we will
6734 : * hold a reference to its BlobImpl in mBlobImpl until we have successfully
6735 : * written the Blob to disk.
6736 : *
6737 : * Relevant Blob context: Blobs sent from the parent process to child processes
6738 : * are automatically linked back to their source BlobImpl when the child process
6739 : * references the Blob via IPC. This is done using the internal IPCBlob
6740 : * inputStream actor ID to FileInfo mapping. However, when getting an actor
6741 : * in the child process for sending an in-child-created Blob to the parent
6742 : * process, there is (currently) no Blob machinery to automatically establish
6743 : * and reuse a long-lived Actor. As a result, without IDB's weak-map
6744 : * cleverness, a memory-backed Blob repeatedly sent from the child to the parent
6745 : * would appear as a different Blob each time, requiring the Blob data to be
6746 : * sent over IPC each time as well as potentially needing to be written to disk
6747 : * each time.
6748 : *
6749 : * This object remains alive as long as there is an active child actor or an
6750 : * ObjectStoreAddOrPutRequestOp::StoredFileInfo for a queued or active add/put
6751 : * op is holding a reference to us.
6752 : */
6753 : class DatabaseFile final
6754 : : public PBackgroundIDBDatabaseFileParent
6755 : {
6756 : friend class Database;
6757 :
6758 : // mBlobImpl's ownership lifecycle:
6759 : // - Initialized on the background thread at creation time. Then
6760 : // responsibility is handed off to the connection thread.
6761 : // - Checked and used by the connection thread to generate a stream to write
6762 : // the blob to disk by an add/put operation.
6763 : // - Cleared on the connection thread once the file has successfully been
6764 : // written to disk.
6765 : RefPtr<BlobImpl> mBlobImpl;
6766 : RefPtr<FileInfo> mFileInfo;
6767 :
6768 : public:
6769 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::DatabaseFile);
6770 :
6771 : FileInfo*
6772 0 : GetFileInfo() const
6773 : {
6774 0 : AssertIsOnBackgroundThread();
6775 :
6776 0 : return mFileInfo;
6777 : }
6778 :
6779 : /**
6780 : * If mBlobImpl is non-null (implying the contents of this file have not yet
6781 : * been written to disk), then return an input stream. Otherwise, if mBlobImpl
6782 : * is null (because the contents have been written to disk), returns null.
6783 : */
6784 : already_AddRefed<nsIInputStream>
6785 : GetInputStream(ErrorResult &rv) const;
6786 :
6787 : /**
6788 : * To be called upon successful copying of the stream GetInputStream()
6789 : * returned so that we won't try and redundantly write the file to disk in the
6790 : * future. This is a separate step from GetInputStream() because
6791 : * the write could fail due to quota errors that happen now but that might
6792 : * not happen in a future attempt.
6793 : */
6794 : void
6795 0 : WriteSucceededClearBlobImpl()
6796 : {
6797 0 : MOZ_ASSERT(!IsOnBackgroundThread());
6798 :
6799 0 : mBlobImpl = nullptr;
6800 0 : }
6801 :
6802 : private:
6803 : // Called when sending to the child.
6804 0 : explicit DatabaseFile(FileInfo* aFileInfo)
6805 0 : : mFileInfo(aFileInfo)
6806 : {
6807 0 : AssertIsOnBackgroundThread();
6808 0 : MOZ_ASSERT(aFileInfo);
6809 0 : }
6810 :
6811 : // Called when receiving from the child.
6812 0 : DatabaseFile(BlobImpl* aBlobImpl, FileInfo* aFileInfo)
6813 0 : : mBlobImpl(aBlobImpl)
6814 0 : , mFileInfo(aFileInfo)
6815 : {
6816 0 : AssertIsOnBackgroundThread();
6817 0 : MOZ_ASSERT(aBlobImpl);
6818 0 : MOZ_ASSERT(aFileInfo);
6819 0 : }
6820 :
6821 0 : ~DatabaseFile() override = default;
6822 :
6823 : void
6824 0 : ActorDestroy(ActorDestroyReason aWhy) override
6825 : {
6826 0 : AssertIsOnBackgroundThread();
6827 0 : }
6828 : };
6829 :
6830 : already_AddRefed<nsIInputStream>
6831 0 : DatabaseFile::GetInputStream(ErrorResult &rv) const
6832 : {
6833 : // We should only be called from our DB connection thread, not the background
6834 : // thread.
6835 0 : MOZ_ASSERT(!IsOnBackgroundThread());
6836 :
6837 0 : if (!mBlobImpl) {
6838 0 : return nullptr;
6839 : }
6840 :
6841 0 : nsCOMPtr<nsIInputStream> inputStream;
6842 0 : mBlobImpl->GetInternalStream(getter_AddRefs(inputStream), rv);
6843 0 : if (rv.Failed()) {
6844 0 : return nullptr;
6845 : }
6846 :
6847 0 : return inputStream.forget();
6848 : }
6849 :
6850 :
6851 : class TransactionBase
6852 : {
6853 : friend class Cursor;
6854 :
6855 : class CommitOp;
6856 :
6857 : protected:
6858 : typedef IDBTransaction::Mode Mode;
6859 :
6860 : private:
6861 : RefPtr<Database> mDatabase;
6862 : nsTArray<RefPtr<FullObjectStoreMetadata>>
6863 : mModifiedAutoIncrementObjectStoreMetadataArray;
6864 : uint64_t mTransactionId;
6865 : const nsCString mDatabaseId;
6866 : const int64_t mLoggingSerialNumber;
6867 : uint64_t mActiveRequestCount;
6868 : Atomic<bool> mInvalidatedOnAnyThread;
6869 : const Mode mMode;
6870 : bool mHasBeenActive;
6871 : bool mHasBeenActiveOnConnectionThread;
6872 : bool mActorDestroyed;
6873 : bool mInvalidated;
6874 :
6875 : protected:
6876 : nsresult mResultCode;
6877 : bool mCommitOrAbortReceived;
6878 : bool mCommittedOrAborted;
6879 : bool mForceAborted;
6880 :
6881 : public:
6882 : void
6883 0 : AssertIsOnConnectionThread() const
6884 : {
6885 0 : MOZ_ASSERT(mDatabase);
6886 0 : mDatabase->AssertIsOnConnectionThread();
6887 0 : }
6888 :
6889 : bool
6890 0 : IsActorDestroyed() const
6891 : {
6892 0 : AssertIsOnBackgroundThread();
6893 :
6894 0 : return mActorDestroyed;
6895 : }
6896 :
6897 : // Must be called on the background thread.
6898 : bool
6899 0 : IsInvalidated() const
6900 : {
6901 0 : MOZ_ASSERT(IsOnBackgroundThread(), "Use IsInvalidatedOnAnyThread()");
6902 0 : MOZ_ASSERT_IF(mInvalidated, NS_FAILED(mResultCode));
6903 :
6904 0 : return mInvalidated;
6905 : }
6906 :
6907 : // May be called on any thread, but is more expensive than IsInvalidated().
6908 : bool
6909 0 : IsInvalidatedOnAnyThread() const
6910 : {
6911 0 : return mInvalidatedOnAnyThread;
6912 : }
6913 :
6914 : void
6915 0 : SetActive(uint64_t aTransactionId)
6916 : {
6917 0 : AssertIsOnBackgroundThread();
6918 0 : MOZ_ASSERT(aTransactionId);
6919 :
6920 0 : mTransactionId = aTransactionId;
6921 0 : mHasBeenActive = true;
6922 0 : }
6923 :
6924 : void
6925 0 : SetActiveOnConnectionThread()
6926 : {
6927 0 : AssertIsOnConnectionThread();
6928 0 : mHasBeenActiveOnConnectionThread = true;
6929 0 : }
6930 :
6931 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(
6932 : mozilla::dom::indexedDB::TransactionBase)
6933 :
6934 : void
6935 : Abort(nsresult aResultCode, bool aForce);
6936 :
6937 : uint64_t
6938 0 : TransactionId() const
6939 : {
6940 0 : return mTransactionId;
6941 : }
6942 :
6943 : const nsCString&
6944 0 : DatabaseId() const
6945 : {
6946 0 : return mDatabaseId;
6947 : }
6948 :
6949 : Mode
6950 0 : GetMode() const
6951 : {
6952 0 : return mMode;
6953 : }
6954 :
6955 : Database*
6956 0 : GetDatabase() const
6957 : {
6958 0 : MOZ_ASSERT(mDatabase);
6959 :
6960 0 : return mDatabase;
6961 : }
6962 :
6963 : DatabaseLoggingInfo*
6964 0 : GetLoggingInfo() const
6965 : {
6966 0 : AssertIsOnBackgroundThread();
6967 0 : MOZ_ASSERT(mDatabase);
6968 :
6969 0 : return mDatabase->GetLoggingInfo();
6970 : }
6971 :
6972 : int64_t
6973 0 : LoggingSerialNumber() const
6974 : {
6975 0 : return mLoggingSerialNumber;
6976 : }
6977 :
6978 : bool
6979 0 : IsAborted() const
6980 : {
6981 0 : AssertIsOnBackgroundThread();
6982 :
6983 0 : return NS_FAILED(mResultCode);
6984 : }
6985 :
6986 : already_AddRefed<FullObjectStoreMetadata>
6987 : GetMetadataForObjectStoreId(int64_t aObjectStoreId) const;
6988 :
6989 : already_AddRefed<FullIndexMetadata>
6990 : GetMetadataForIndexId(FullObjectStoreMetadata* const aObjectStoreMetadata,
6991 : int64_t aIndexId) const;
6992 :
6993 : PBackgroundParent*
6994 0 : GetBackgroundParent() const
6995 : {
6996 0 : AssertIsOnBackgroundThread();
6997 0 : MOZ_ASSERT(!IsActorDestroyed());
6998 :
6999 0 : return GetDatabase()->GetBackgroundParent();
7000 : }
7001 :
7002 : void
7003 : NoteModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata);
7004 :
7005 : void
7006 : ForgetModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata);
7007 :
7008 : void
7009 : NoteActiveRequest();
7010 :
7011 : void
7012 : NoteFinishedRequest();
7013 :
7014 : void
7015 : Invalidate();
7016 :
7017 : protected:
7018 : TransactionBase(Database* aDatabase, Mode aMode);
7019 :
7020 : virtual
7021 : ~TransactionBase();
7022 :
7023 : void
7024 0 : NoteActorDestroyed()
7025 : {
7026 0 : AssertIsOnBackgroundThread();
7027 0 : MOZ_ASSERT(!mActorDestroyed);
7028 :
7029 0 : mActorDestroyed = true;
7030 0 : }
7031 :
7032 : #ifdef DEBUG
7033 : // Only called by VersionChangeTransaction.
7034 : void
7035 0 : FakeActorDestroyed()
7036 : {
7037 0 : mActorDestroyed = true;
7038 0 : }
7039 : #endif
7040 :
7041 : bool
7042 : RecvCommit();
7043 :
7044 : bool
7045 : RecvAbort(nsresult aResultCode);
7046 :
7047 : void
7048 0 : MaybeCommitOrAbort()
7049 : {
7050 0 : AssertIsOnBackgroundThread();
7051 :
7052 : // If we've already committed or aborted then there's nothing else to do.
7053 0 : if (mCommittedOrAborted) {
7054 0 : return;
7055 : }
7056 :
7057 : // If there are active requests then we have to wait for those requests to
7058 : // complete (see NoteFinishedRequest).
7059 0 : if (mActiveRequestCount) {
7060 0 : return;
7061 : }
7062 :
7063 : // If we haven't yet received a commit or abort message then there could be
7064 : // additional requests coming so we should wait unless we're being forced to
7065 : // abort.
7066 0 : if (!mCommitOrAbortReceived && !mForceAborted) {
7067 0 : return;
7068 : }
7069 :
7070 0 : CommitOrAbort();
7071 : }
7072 :
7073 : PBackgroundIDBRequestParent*
7074 : AllocRequest(const RequestParams& aParams, bool aTrustParams);
7075 :
7076 : bool
7077 : StartRequest(PBackgroundIDBRequestParent* aActor);
7078 :
7079 : bool
7080 : DeallocRequest(PBackgroundIDBRequestParent* aActor);
7081 :
7082 : PBackgroundIDBCursorParent*
7083 : AllocCursor(const OpenCursorParams& aParams, bool aTrustParams);
7084 :
7085 : bool
7086 : StartCursor(PBackgroundIDBCursorParent* aActor,
7087 : const OpenCursorParams& aParams);
7088 :
7089 : bool
7090 : DeallocCursor(PBackgroundIDBCursorParent* aActor);
7091 :
7092 : virtual void
7093 0 : UpdateMetadata(nsresult aResult)
7094 0 : { }
7095 :
7096 : virtual void
7097 : SendCompleteNotification(nsresult aResult) = 0;
7098 :
7099 : private:
7100 : bool
7101 : VerifyRequestParams(const RequestParams& aParams) const;
7102 :
7103 : bool
7104 : VerifyRequestParams(const SerializedKeyRange& aKeyRange) const;
7105 :
7106 : bool
7107 : VerifyRequestParams(const ObjectStoreAddPutParams& aParams) const;
7108 :
7109 : bool
7110 : VerifyRequestParams(const OptionalKeyRange& aKeyRange) const;
7111 :
7112 : void
7113 : CommitOrAbort();
7114 : };
7115 :
7116 : class TransactionBase::CommitOp final
7117 : : public DatabaseOperationBase
7118 : , public ConnectionPool::FinishCallback
7119 : {
7120 : friend class TransactionBase;
7121 :
7122 : RefPtr<TransactionBase> mTransaction;
7123 : nsresult mResultCode;
7124 :
7125 : private:
7126 : CommitOp(TransactionBase* aTransaction, nsresult aResultCode);
7127 :
7128 0 : ~CommitOp() override = default;
7129 :
7130 : // Writes new autoIncrement counts to database.
7131 : nsresult
7132 : WriteAutoIncrementCounts();
7133 :
7134 : // Updates counts after a database activity has finished.
7135 : void
7136 : CommitOrRollbackAutoIncrementCounts();
7137 :
7138 : void
7139 : AssertForeignKeyConsistency(DatabaseConnection* aConnection)
7140 : #ifdef DEBUG
7141 : ;
7142 : #else
7143 : { }
7144 : #endif
7145 :
7146 : NS_DECL_NSIRUNNABLE
7147 :
7148 : void
7149 : TransactionFinishedBeforeUnblock() override;
7150 :
7151 : void
7152 : TransactionFinishedAfterUnblock() override;
7153 :
7154 : public:
7155 : NS_DECL_ISUPPORTS_INHERITED
7156 : };
7157 :
7158 : class NormalTransaction final
7159 : : public TransactionBase
7160 : , public PBackgroundIDBTransactionParent
7161 : {
7162 : friend class Database;
7163 :
7164 : nsTArray<RefPtr<FullObjectStoreMetadata>> mObjectStores;
7165 :
7166 : private:
7167 : // This constructor is only called by Database.
7168 : NormalTransaction(Database* aDatabase,
7169 : TransactionBase::Mode aMode,
7170 : nsTArray<RefPtr<FullObjectStoreMetadata>>& aObjectStores);
7171 :
7172 : // Reference counted.
7173 0 : ~NormalTransaction() override = default;
7174 :
7175 : bool
7176 : IsSameProcessActor();
7177 :
7178 : // Only called by TransactionBase.
7179 : void
7180 : SendCompleteNotification(nsresult aResult) override;
7181 :
7182 : // IPDL methods are only called by IPDL.
7183 : void
7184 : ActorDestroy(ActorDestroyReason aWhy) override;
7185 :
7186 : mozilla::ipc::IPCResult
7187 : RecvDeleteMe() override;
7188 :
7189 : mozilla::ipc::IPCResult
7190 : RecvCommit() override;
7191 :
7192 : mozilla::ipc::IPCResult
7193 : RecvAbort(const nsresult& aResultCode) override;
7194 :
7195 : PBackgroundIDBRequestParent*
7196 : AllocPBackgroundIDBRequestParent(const RequestParams& aParams) override;
7197 :
7198 : mozilla::ipc::IPCResult
7199 : RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent* aActor,
7200 : const RequestParams& aParams)
7201 : override;
7202 :
7203 : bool
7204 : DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent* aActor)
7205 : override;
7206 :
7207 : PBackgroundIDBCursorParent*
7208 : AllocPBackgroundIDBCursorParent(const OpenCursorParams& aParams) override;
7209 :
7210 : mozilla::ipc::IPCResult
7211 : RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent* aActor,
7212 : const OpenCursorParams& aParams)
7213 : override;
7214 :
7215 : bool
7216 : DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent* aActor)
7217 : override;
7218 : };
7219 :
7220 : class VersionChangeTransaction final
7221 : : public TransactionBase
7222 : , public PBackgroundIDBVersionChangeTransactionParent
7223 : {
7224 : friend class OpenDatabaseOp;
7225 :
7226 : RefPtr<OpenDatabaseOp> mOpenDatabaseOp;
7227 : RefPtr<FullDatabaseMetadata> mOldMetadata;
7228 :
7229 : bool mActorWasAlive;
7230 :
7231 : private:
7232 : // Only called by OpenDatabaseOp.
7233 : explicit VersionChangeTransaction(OpenDatabaseOp* aOpenDatabaseOp);
7234 :
7235 : // Reference counted.
7236 : ~VersionChangeTransaction() override;
7237 :
7238 : bool
7239 : IsSameProcessActor();
7240 :
7241 : // Only called by OpenDatabaseOp.
7242 : bool
7243 : CopyDatabaseMetadata();
7244 :
7245 : void
7246 : SetActorAlive();
7247 :
7248 : // Only called by TransactionBase.
7249 : void
7250 : UpdateMetadata(nsresult aResult) override;
7251 :
7252 : // Only called by TransactionBase.
7253 : void
7254 : SendCompleteNotification(nsresult aResult) override;
7255 :
7256 : // IPDL methods are only called by IPDL.
7257 : void
7258 : ActorDestroy(ActorDestroyReason aWhy) override;
7259 :
7260 : mozilla::ipc::IPCResult
7261 : RecvDeleteMe() override;
7262 :
7263 : mozilla::ipc::IPCResult
7264 : RecvCommit() override;
7265 :
7266 : mozilla::ipc::IPCResult
7267 : RecvAbort(const nsresult& aResultCode) override;
7268 :
7269 : mozilla::ipc::IPCResult
7270 : RecvCreateObjectStore(const ObjectStoreMetadata& aMetadata) override;
7271 :
7272 : mozilla::ipc::IPCResult
7273 : RecvDeleteObjectStore(const int64_t& aObjectStoreId) override;
7274 :
7275 : mozilla::ipc::IPCResult
7276 : RecvRenameObjectStore(const int64_t& aObjectStoreId,
7277 : const nsString& aName) override;
7278 :
7279 : mozilla::ipc::IPCResult
7280 : RecvCreateIndex(const int64_t& aObjectStoreId,
7281 : const IndexMetadata& aMetadata) override;
7282 :
7283 : mozilla::ipc::IPCResult
7284 : RecvDeleteIndex(const int64_t& aObjectStoreId,
7285 : const int64_t& aIndexId) override;
7286 :
7287 : mozilla::ipc::IPCResult
7288 : RecvRenameIndex(const int64_t& aObjectStoreId,
7289 : const int64_t& aIndexId,
7290 : const nsString& aName) override;
7291 :
7292 : PBackgroundIDBRequestParent*
7293 : AllocPBackgroundIDBRequestParent(const RequestParams& aParams) override;
7294 :
7295 : mozilla::ipc::IPCResult
7296 : RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent* aActor,
7297 : const RequestParams& aParams)
7298 : override;
7299 :
7300 : bool
7301 : DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent* aActor)
7302 : override;
7303 :
7304 : PBackgroundIDBCursorParent*
7305 : AllocPBackgroundIDBCursorParent(const OpenCursorParams& aParams) override;
7306 :
7307 : mozilla::ipc::IPCResult
7308 : RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent* aActor,
7309 : const OpenCursorParams& aParams)
7310 : override;
7311 :
7312 : bool
7313 : DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent* aActor)
7314 : override;
7315 : };
7316 :
7317 : class MutableFile
7318 : : public BackgroundMutableFileParentBase
7319 : {
7320 : RefPtr<Database> mDatabase;
7321 : RefPtr<FileInfo> mFileInfo;
7322 :
7323 : public:
7324 : static already_AddRefed<MutableFile>
7325 : Create(nsIFile* aFile,
7326 : Database* aDatabase,
7327 : FileInfo* aFileInfo);
7328 :
7329 : Database*
7330 0 : GetDatabase() const
7331 : {
7332 0 : AssertIsOnBackgroundThread();
7333 0 : MOZ_ASSERT(mDatabase);
7334 :
7335 0 : return mDatabase;
7336 : }
7337 :
7338 : FileInfo*
7339 0 : GetFileInfo() const
7340 : {
7341 0 : AssertIsOnBackgroundThread();
7342 0 : MOZ_ASSERT(mFileInfo);
7343 :
7344 0 : return mFileInfo;
7345 : }
7346 :
7347 : void
7348 : NoteActiveState() override;
7349 :
7350 : void
7351 : NoteInactiveState() override;
7352 :
7353 : PBackgroundParent*
7354 : GetBackgroundParent() const override;
7355 :
7356 : already_AddRefed<nsISupports>
7357 : CreateStream(bool aReadOnly) override;
7358 :
7359 : already_AddRefed<BlobImpl>
7360 : CreateBlobImpl() override;
7361 :
7362 : private:
7363 : MutableFile(nsIFile* aFile,
7364 : Database* aDatabase,
7365 : FileInfo* aFileInfo);
7366 :
7367 : ~MutableFile() override;
7368 :
7369 : PBackgroundFileHandleParent*
7370 : AllocPBackgroundFileHandleParent(const FileMode& aMode) override;
7371 :
7372 : mozilla::ipc::IPCResult
7373 : RecvPBackgroundFileHandleConstructor(PBackgroundFileHandleParent* aActor,
7374 : const FileMode& aMode) override;
7375 :
7376 : mozilla::ipc::IPCResult
7377 : RecvGetFileId(int64_t* aFileId) override;
7378 : };
7379 :
7380 : class FactoryOp
7381 : : public DatabaseOperationBase
7382 : , public OpenDirectoryListener
7383 : , public PBackgroundIDBFactoryRequestParent
7384 : {
7385 : public:
7386 : struct MaybeBlockedDatabaseInfo;
7387 :
7388 : protected:
7389 : enum class State
7390 : {
7391 : // Just created on the PBackground thread, dispatched to the main thread.
7392 : // Next step is either SendingResults if permission is denied,
7393 : // PermissionChallenge if the permission is unknown, or FinishOpen
7394 : // if permission is granted.
7395 : Initial,
7396 :
7397 : // Sending a permission challenge message to the child on the PBackground
7398 : // thread. Next step is PermissionRetry.
7399 : PermissionChallenge,
7400 :
7401 : // Retrying permission check after a challenge on the main thread. Next step
7402 : // is either SendingResults if permission is denied or FinishOpen
7403 : // if permission is granted.
7404 : PermissionRetry,
7405 :
7406 : // Opening directory or initializing quota manager on the PBackground
7407 : // thread. Next step is either DirectoryOpenPending if quota manager is
7408 : // already initialized or QuotaManagerPending if quota manager needs to be
7409 : // initialized.
7410 : FinishOpen,
7411 :
7412 : // Waiting for quota manager initialization to complete on the PBackground
7413 : // thread. Next step is either SendingResults if initialization failed or
7414 : // DirectoryOpenPending if initialization succeeded.
7415 : QuotaManagerPending,
7416 :
7417 : // Waiting for directory open allowed on the PBackground thread. The next
7418 : // step is either SendingResults if directory lock failed to acquire, or
7419 : // DatabaseOpenPending if directory lock is acquired.
7420 : DirectoryOpenPending,
7421 :
7422 : // Waiting for database open allowed on the PBackground thread. The next
7423 : // step is DatabaseWorkOpen.
7424 : DatabaseOpenPending,
7425 :
7426 : // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
7427 : // either BeginVersionChange if the requested version doesn't match the
7428 : // existing database version or SendingResults if the versions match.
7429 : DatabaseWorkOpen,
7430 :
7431 : // Starting a version change transaction or deleting a database on the
7432 : // PBackground thread. We need to notify other databases that a version
7433 : // change is about to happen, and maybe tell the request that a version
7434 : // change has been blocked. If databases are notified then the next step is
7435 : // WaitingForOtherDatabasesToClose. Otherwise the next step is
7436 : // WaitingForTransactionsToComplete.
7437 : BeginVersionChange,
7438 :
7439 : // Waiting for other databases to close on the PBackground thread. This
7440 : // state may persist until all databases are closed. The next state is
7441 : // WaitingForTransactionsToComplete.
7442 : WaitingForOtherDatabasesToClose,
7443 :
7444 : // Waiting for all transactions that could interfere with this operation to
7445 : // complete on the PBackground thread. Next state is
7446 : // DatabaseWorkVersionChange.
7447 : WaitingForTransactionsToComplete,
7448 :
7449 : // Waiting to do/doing work on the "work thread". This involves waiting for
7450 : // the VersionChangeOp (OpenDatabaseOp and DeleteDatabaseOp each have a
7451 : // different implementation) to do its work. Eventually the state will
7452 : // transition to SendingResults.
7453 : DatabaseWorkVersionChange,
7454 :
7455 : // Waiting to send/sending results on the PBackground thread. Next step is
7456 : // Completed.
7457 : SendingResults,
7458 :
7459 : // All done.
7460 : Completed
7461 : };
7462 :
7463 : // Must be released on the background thread!
7464 : RefPtr<Factory> mFactory;
7465 :
7466 : // Must be released on the main thread!
7467 : RefPtr<ContentParent> mContentParent;
7468 :
7469 : // Must be released on the main thread!
7470 : RefPtr<DirectoryLock> mDirectoryLock;
7471 :
7472 : RefPtr<FactoryOp> mDelayedOp;
7473 : nsTArray<MaybeBlockedDatabaseInfo> mMaybeBlockedDatabases;
7474 :
7475 : const CommonFactoryRequestParams mCommonParams;
7476 : nsCString mSuffix;
7477 : nsCString mGroup;
7478 : nsCString mOrigin;
7479 : nsCString mDatabaseId;
7480 : nsString mDatabaseFilePath;
7481 : State mState;
7482 : bool mEnforcingQuota;
7483 : const bool mDeleting;
7484 : bool mBlockedDatabaseOpen;
7485 : bool mChromeWriteAccessAllowed;
7486 : bool mFileHandleDisabled;
7487 :
7488 : public:
7489 : void
7490 : NoteDatabaseBlocked(Database* aDatabase);
7491 :
7492 : virtual void
7493 : NoteDatabaseClosed(Database* aDatabase) = 0;
7494 :
7495 : #ifdef DEBUG
7496 : bool
7497 0 : HasBlockedDatabases() const
7498 : {
7499 0 : return !mMaybeBlockedDatabases.IsEmpty();
7500 : }
7501 : #endif
7502 :
7503 : const nsString&
7504 0 : DatabaseFilePath() const
7505 : {
7506 0 : return mDatabaseFilePath;
7507 : }
7508 :
7509 : protected:
7510 : FactoryOp(Factory* aFactory,
7511 : already_AddRefed<ContentParent> aContentParent,
7512 : const CommonFactoryRequestParams& aCommonParams,
7513 : bool aDeleting);
7514 :
7515 0 : ~FactoryOp() override
7516 0 : {
7517 : // Normally this would be out-of-line since it is a virtual function but
7518 : // MSVC 2010 fails to link for some reason if it is not inlined here...
7519 0 : MOZ_ASSERT_IF(OperationMayProceed(),
7520 : mState == State::Initial || mState == State::Completed);
7521 0 : }
7522 :
7523 : nsresult
7524 : Open();
7525 :
7526 : nsresult
7527 : ChallengePermission();
7528 :
7529 : nsresult
7530 : RetryCheckPermission();
7531 :
7532 : nsresult
7533 : DirectoryOpen();
7534 :
7535 : nsresult
7536 : SendToIOThread();
7537 :
7538 : void
7539 : WaitForTransactions();
7540 :
7541 : void
7542 : FinishSendResults();
7543 :
7544 : nsresult
7545 : SendVersionChangeMessages(DatabaseActorInfo* aDatabaseActorInfo,
7546 : Database* aOpeningDatabase,
7547 : uint64_t aOldVersion,
7548 : const NullableVersion& aNewVersion);
7549 :
7550 : // Methods that subclasses must implement.
7551 : virtual nsresult
7552 : DatabaseOpen() = 0;
7553 :
7554 : virtual nsresult
7555 : DoDatabaseWork() = 0;
7556 :
7557 : virtual nsresult
7558 : BeginVersionChange() = 0;
7559 :
7560 : virtual nsresult
7561 : DispatchToWorkThread() = 0;
7562 :
7563 : // Should only be called by Run().
7564 : virtual void
7565 : SendResults() = 0;
7566 :
7567 : NS_DECL_ISUPPORTS_INHERITED
7568 :
7569 : // Common nsIRunnable implementation that subclasses may not override.
7570 : NS_IMETHOD
7571 : Run() final;
7572 :
7573 : // OpenDirectoryListener overrides.
7574 : void
7575 : DirectoryLockAcquired(DirectoryLock* aLock) override;
7576 :
7577 : void
7578 : DirectoryLockFailed() override;
7579 :
7580 : // IPDL methods.
7581 : void
7582 : ActorDestroy(ActorDestroyReason aWhy) override;
7583 :
7584 : mozilla::ipc::IPCResult
7585 : RecvPermissionRetry() override;
7586 :
7587 : virtual void
7588 : SendBlockedNotification() = 0;
7589 :
7590 : private:
7591 : nsresult
7592 : CheckPermission(ContentParent* aContentParent,
7593 : PermissionRequestBase::PermissionValue* aPermission);
7594 :
7595 : static bool
7596 : CheckAtLeastOneAppHasPermission(ContentParent* aContentParent,
7597 : const nsACString& aPermissionString);
7598 :
7599 : nsresult
7600 : FinishOpen();
7601 :
7602 : nsresult
7603 : QuotaManagerOpen();
7604 :
7605 : nsresult
7606 : OpenDirectory();
7607 :
7608 : // Test whether this FactoryOp needs to wait for the given op.
7609 : bool
7610 : MustWaitFor(const FactoryOp& aExistingOp);
7611 : };
7612 :
7613 : struct FactoryOp::MaybeBlockedDatabaseInfo final
7614 : {
7615 : RefPtr<Database> mDatabase;
7616 : bool mBlocked;
7617 :
7618 0 : MOZ_IMPLICIT MaybeBlockedDatabaseInfo(Database* aDatabase)
7619 0 : : mDatabase(aDatabase)
7620 0 : , mBlocked(false)
7621 : {
7622 0 : MOZ_ASSERT(aDatabase);
7623 :
7624 0 : MOZ_COUNT_CTOR(FactoryOp::MaybeBlockedDatabaseInfo);
7625 0 : }
7626 :
7627 0 : ~MaybeBlockedDatabaseInfo()
7628 0 : {
7629 0 : MOZ_COUNT_DTOR(FactoryOp::MaybeBlockedDatabaseInfo);
7630 0 : }
7631 :
7632 : bool
7633 0 : operator==(const MaybeBlockedDatabaseInfo& aOther) const
7634 : {
7635 0 : return mDatabase == aOther.mDatabase;
7636 : }
7637 :
7638 : Database*
7639 0 : operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN
7640 : {
7641 0 : return mDatabase;
7642 : }
7643 : };
7644 :
7645 : class OpenDatabaseOp final
7646 : : public FactoryOp
7647 : {
7648 : friend class Database;
7649 : friend class VersionChangeTransaction;
7650 :
7651 : class VersionChangeOp;
7652 :
7653 : Maybe<ContentParentId> mOptionalContentParentId;
7654 :
7655 : RefPtr<FullDatabaseMetadata> mMetadata;
7656 :
7657 : uint64_t mRequestedVersion;
7658 : RefPtr<FileManager> mFileManager;
7659 :
7660 : RefPtr<Database> mDatabase;
7661 : RefPtr<VersionChangeTransaction> mVersionChangeTransaction;
7662 :
7663 : // This is only set while a VersionChangeOp is live. It holds a strong
7664 : // reference to its OpenDatabaseOp object so this is a weak pointer to avoid
7665 : // cycles.
7666 : VersionChangeOp* mVersionChangeOp;
7667 :
7668 : uint32_t mTelemetryId;
7669 :
7670 : public:
7671 : OpenDatabaseOp(Factory* aFactory,
7672 : already_AddRefed<ContentParent> aContentParent,
7673 : const CommonFactoryRequestParams& aParams);
7674 :
7675 : private:
7676 0 : ~OpenDatabaseOp() override
7677 0 : {
7678 0 : MOZ_ASSERT(!mVersionChangeOp);
7679 0 : }
7680 :
7681 : nsresult
7682 : LoadDatabaseInformation(mozIStorageConnection* aConnection);
7683 :
7684 : nsresult
7685 : SendUpgradeNeeded();
7686 :
7687 : void
7688 : EnsureDatabaseActor();
7689 :
7690 : nsresult
7691 : EnsureDatabaseActorIsAlive();
7692 :
7693 : void
7694 : MetadataToSpec(DatabaseSpec& aSpec);
7695 :
7696 : void
7697 : AssertMetadataConsistency(const FullDatabaseMetadata* aMetadata)
7698 : #ifdef DEBUG
7699 : ;
7700 : #else
7701 : { }
7702 : #endif
7703 :
7704 : void
7705 : ConnectionClosedCallback();
7706 :
7707 : void
7708 : ActorDestroy(ActorDestroyReason aWhy) override;
7709 :
7710 : nsresult
7711 : DatabaseOpen() override;
7712 :
7713 : nsresult
7714 : DoDatabaseWork() override;
7715 :
7716 : nsresult
7717 : BeginVersionChange() override;
7718 :
7719 : void
7720 : NoteDatabaseClosed(Database* aDatabase) override;
7721 :
7722 : void
7723 : SendBlockedNotification() override;
7724 :
7725 : nsresult
7726 : DispatchToWorkThread() override;
7727 :
7728 : void
7729 : SendResults() override;
7730 :
7731 : #ifdef ENABLE_INTL_API
7732 : static nsresult
7733 : UpdateLocaleAwareIndex(mozIStorageConnection* aConnection,
7734 : const IndexMetadata& aIndexMetadata,
7735 : const nsCString& aLocale);
7736 : #endif
7737 : };
7738 :
7739 : class OpenDatabaseOp::VersionChangeOp final
7740 : : public TransactionDatabaseOperationBase
7741 : {
7742 : friend class OpenDatabaseOp;
7743 :
7744 : RefPtr<OpenDatabaseOp> mOpenDatabaseOp;
7745 : const uint64_t mRequestedVersion;
7746 : uint64_t mPreviousVersion;
7747 :
7748 : private:
7749 : explicit
7750 0 : VersionChangeOp(OpenDatabaseOp* aOpenDatabaseOp)
7751 0 : : TransactionDatabaseOperationBase(
7752 : aOpenDatabaseOp->mVersionChangeTransaction,
7753 : aOpenDatabaseOp->LoggingSerialNumber())
7754 : , mOpenDatabaseOp(aOpenDatabaseOp)
7755 0 : , mRequestedVersion(aOpenDatabaseOp->mRequestedVersion)
7756 0 : , mPreviousVersion(aOpenDatabaseOp->mMetadata->mCommonMetadata.version())
7757 : {
7758 0 : MOZ_ASSERT(aOpenDatabaseOp);
7759 0 : MOZ_ASSERT(mRequestedVersion);
7760 0 : }
7761 :
7762 0 : ~VersionChangeOp() override
7763 0 : {
7764 0 : MOZ_ASSERT(!mOpenDatabaseOp);
7765 0 : }
7766 :
7767 : nsresult
7768 : DoDatabaseWork(DatabaseConnection* aConnection) override;
7769 :
7770 : nsresult
7771 : SendSuccessResult() override;
7772 :
7773 : bool
7774 : SendFailureResult(nsresult aResultCode) override;
7775 :
7776 : void
7777 : Cleanup() override;
7778 : };
7779 :
7780 : class DeleteDatabaseOp final
7781 : : public FactoryOp
7782 : {
7783 : class VersionChangeOp;
7784 :
7785 : nsString mDatabaseDirectoryPath;
7786 : nsString mDatabaseFilenameBase;
7787 : uint64_t mPreviousVersion;
7788 :
7789 : public:
7790 0 : DeleteDatabaseOp(Factory* aFactory,
7791 : already_AddRefed<ContentParent> aContentParent,
7792 : const CommonFactoryRequestParams& aParams)
7793 0 : : FactoryOp(aFactory, Move(aContentParent), aParams, /* aDeleting */ true)
7794 0 : , mPreviousVersion(0)
7795 0 : { }
7796 :
7797 : private:
7798 0 : ~DeleteDatabaseOp() override = default;
7799 :
7800 : void
7801 : LoadPreviousVersion(nsIFile* aDatabaseFile);
7802 :
7803 : nsresult
7804 : DatabaseOpen() override;
7805 :
7806 : nsresult
7807 : DoDatabaseWork() override;
7808 :
7809 : nsresult
7810 : BeginVersionChange() override;
7811 :
7812 : void
7813 : NoteDatabaseClosed(Database* aDatabase) override;
7814 :
7815 : void
7816 : SendBlockedNotification() override;
7817 :
7818 : nsresult
7819 : DispatchToWorkThread() override;
7820 :
7821 : void
7822 : SendResults() override;
7823 : };
7824 :
7825 : class DeleteDatabaseOp::VersionChangeOp final
7826 : : public DatabaseOperationBase
7827 : {
7828 : friend class DeleteDatabaseOp;
7829 :
7830 : RefPtr<DeleteDatabaseOp> mDeleteDatabaseOp;
7831 :
7832 : private:
7833 : explicit
7834 0 : VersionChangeOp(DeleteDatabaseOp* aDeleteDatabaseOp)
7835 0 : : DatabaseOperationBase(aDeleteDatabaseOp->BackgroundChildLoggingId(),
7836 : aDeleteDatabaseOp->LoggingSerialNumber())
7837 0 : , mDeleteDatabaseOp(aDeleteDatabaseOp)
7838 : {
7839 0 : MOZ_ASSERT(aDeleteDatabaseOp);
7840 0 : MOZ_ASSERT(!aDeleteDatabaseOp->mDatabaseDirectoryPath.IsEmpty());
7841 0 : }
7842 :
7843 0 : ~VersionChangeOp() override = default;
7844 :
7845 : nsresult
7846 : RunOnIOThread();
7847 :
7848 : void
7849 : RunOnOwningThread();
7850 :
7851 : nsresult
7852 : DeleteFile(nsIFile* aDirectory,
7853 : const nsAString& aFilename,
7854 : QuotaManager* aQuotaManager);
7855 :
7856 : NS_DECL_NSIRUNNABLE
7857 : };
7858 :
7859 : class DatabaseOp
7860 : : public DatabaseOperationBase
7861 : , public PBackgroundIDBDatabaseRequestParent
7862 : {
7863 : protected:
7864 : RefPtr<Database> mDatabase;
7865 :
7866 : enum class State
7867 : {
7868 : // Just created on the PBackground thread, dispatched to the main thread.
7869 : // Next step is DatabaseWork.
7870 : Initial,
7871 :
7872 : // Waiting to do/doing work on the QuotaManager IO thread. Next step is
7873 : // SendingResults.
7874 : DatabaseWork,
7875 :
7876 : // Waiting to send/sending results on the PBackground thread. Next step is
7877 : // Completed.
7878 : SendingResults,
7879 :
7880 : // All done.
7881 : Completed
7882 : };
7883 :
7884 : State mState;
7885 :
7886 : public:
7887 : void
7888 0 : RunImmediately()
7889 : {
7890 0 : MOZ_ASSERT(mState == State::Initial);
7891 :
7892 0 : Unused << this->Run();
7893 0 : }
7894 :
7895 : protected:
7896 : DatabaseOp(Database* aDatabase);
7897 :
7898 0 : ~DatabaseOp() override
7899 0 : {
7900 0 : MOZ_ASSERT_IF(OperationMayProceed(),
7901 : mState == State::Initial || mState == State::Completed);
7902 0 : }
7903 :
7904 : nsresult
7905 : SendToIOThread();
7906 :
7907 : // Methods that subclasses must implement.
7908 : virtual nsresult
7909 : DoDatabaseWork() = 0;
7910 :
7911 : virtual void
7912 : SendResults() = 0;
7913 :
7914 : // Common nsIRunnable implementation that subclasses may not override.
7915 : NS_IMETHOD
7916 : Run() final;
7917 :
7918 : // IPDL methods.
7919 : void
7920 : ActorDestroy(ActorDestroyReason aWhy) override;
7921 : };
7922 :
7923 : class CreateFileOp final
7924 : : public DatabaseOp
7925 : {
7926 : const CreateFileParams mParams;
7927 :
7928 : RefPtr<FileInfo> mFileInfo;
7929 :
7930 : public:
7931 : CreateFileOp(Database* aDatabase,
7932 : const DatabaseRequestParams& aParams);
7933 :
7934 : private:
7935 0 : ~CreateFileOp() override = default;
7936 :
7937 : nsresult
7938 : CreateMutableFile(MutableFile** aMutableFile);
7939 :
7940 : nsresult
7941 : DoDatabaseWork() override;
7942 :
7943 : void
7944 : SendResults() override;
7945 : };
7946 :
7947 : class VersionChangeTransactionOp
7948 : : public TransactionDatabaseOperationBase
7949 : {
7950 : public:
7951 : void
7952 : Cleanup() override;
7953 :
7954 : protected:
7955 0 : explicit VersionChangeTransactionOp(VersionChangeTransaction* aTransaction)
7956 0 : : TransactionDatabaseOperationBase(aTransaction)
7957 0 : { }
7958 :
7959 0 : ~VersionChangeTransactionOp() override = default;
7960 :
7961 : private:
7962 : nsresult
7963 : SendSuccessResult() override;
7964 :
7965 : bool
7966 : SendFailureResult(nsresult aResultCode) override;
7967 : };
7968 :
7969 : class CreateObjectStoreOp final
7970 : : public VersionChangeTransactionOp
7971 : {
7972 : friend class VersionChangeTransaction;
7973 :
7974 : const ObjectStoreMetadata mMetadata;
7975 :
7976 : private:
7977 : // Only created by VersionChangeTransaction.
7978 0 : CreateObjectStoreOp(VersionChangeTransaction* aTransaction,
7979 : const ObjectStoreMetadata& aMetadata)
7980 0 : : VersionChangeTransactionOp(aTransaction)
7981 0 : , mMetadata(aMetadata)
7982 : {
7983 0 : MOZ_ASSERT(aMetadata.id());
7984 0 : }
7985 :
7986 0 : ~CreateObjectStoreOp() override = default;
7987 :
7988 : nsresult
7989 : DoDatabaseWork(DatabaseConnection* aConnection) override;
7990 : };
7991 :
7992 : class DeleteObjectStoreOp final
7993 : : public VersionChangeTransactionOp
7994 : {
7995 : friend class VersionChangeTransaction;
7996 :
7997 : const RefPtr<FullObjectStoreMetadata> mMetadata;
7998 : const bool mIsLastObjectStore;
7999 :
8000 : private:
8001 : // Only created by VersionChangeTransaction.
8002 0 : DeleteObjectStoreOp(VersionChangeTransaction* aTransaction,
8003 : FullObjectStoreMetadata* const aMetadata,
8004 : const bool aIsLastObjectStore)
8005 0 : : VersionChangeTransactionOp(aTransaction)
8006 : , mMetadata(aMetadata)
8007 0 : , mIsLastObjectStore(aIsLastObjectStore)
8008 : {
8009 0 : MOZ_ASSERT(aMetadata->mCommonMetadata.id());
8010 0 : }
8011 :
8012 0 : ~DeleteObjectStoreOp() override = default;
8013 :
8014 : nsresult
8015 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8016 : };
8017 :
8018 : class RenameObjectStoreOp final
8019 : : public VersionChangeTransactionOp
8020 : {
8021 : friend class VersionChangeTransaction;
8022 :
8023 : const int64_t mId;
8024 : const nsString mNewName;
8025 :
8026 : private:
8027 : // Only created by VersionChangeTransaction.
8028 0 : RenameObjectStoreOp(VersionChangeTransaction* aTransaction,
8029 : FullObjectStoreMetadata* const aMetadata)
8030 0 : : VersionChangeTransactionOp(aTransaction)
8031 0 : , mId(aMetadata->mCommonMetadata.id())
8032 0 : , mNewName(aMetadata->mCommonMetadata.name())
8033 : {
8034 0 : MOZ_ASSERT(mId);
8035 0 : }
8036 :
8037 0 : ~RenameObjectStoreOp() override = default;
8038 :
8039 : nsresult
8040 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8041 : };
8042 :
8043 : class CreateIndexOp final
8044 : : public VersionChangeTransactionOp
8045 : {
8046 : friend class VersionChangeTransaction;
8047 :
8048 : class ThreadLocalJSContext;
8049 : class UpdateIndexDataValuesFunction;
8050 :
8051 : static const unsigned int kBadThreadLocalIndex =
8052 : static_cast<unsigned int>(-1);
8053 :
8054 : static unsigned int sThreadLocalIndex;
8055 :
8056 : const IndexMetadata mMetadata;
8057 : Maybe<UniqueIndexTable> mMaybeUniqueIndexTable;
8058 : RefPtr<FileManager> mFileManager;
8059 : const nsCString mDatabaseId;
8060 : const uint64_t mObjectStoreId;
8061 :
8062 : private:
8063 : // Only created by VersionChangeTransaction.
8064 : CreateIndexOp(VersionChangeTransaction* aTransaction,
8065 : const int64_t aObjectStoreId,
8066 : const IndexMetadata& aMetadata);
8067 :
8068 0 : ~CreateIndexOp() override = default;
8069 :
8070 : nsresult
8071 : InsertDataFromObjectStore(DatabaseConnection* aConnection);
8072 :
8073 : nsresult
8074 : InsertDataFromObjectStoreInternal(DatabaseConnection* aConnection);
8075 :
8076 : bool
8077 : Init(TransactionBase* aTransaction) override;
8078 :
8079 : nsresult
8080 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8081 : };
8082 :
8083 : class NormalJSContext
8084 : {
8085 : friend class nsAutoPtr<NormalJSContext>;
8086 :
8087 : static const JSClass sGlobalClass;
8088 : static const uint32_t kContextHeapSize = 768 * 1024;
8089 :
8090 : JSContext* mContext;
8091 : JSObject* mGlobal;
8092 :
8093 : public:
8094 : static NormalJSContext*
8095 : Create();
8096 :
8097 : JSContext*
8098 0 : Context() const
8099 : {
8100 0 : return mContext;
8101 : }
8102 :
8103 : JSObject*
8104 0 : Global() const
8105 : {
8106 0 : return mGlobal;
8107 : }
8108 :
8109 : protected:
8110 0 : NormalJSContext()
8111 0 : : mContext(nullptr)
8112 0 : , mGlobal(nullptr)
8113 : {
8114 0 : MOZ_COUNT_CTOR(NormalJSContext);
8115 0 : }
8116 :
8117 0 : ~NormalJSContext()
8118 0 : {
8119 0 : MOZ_COUNT_DTOR(NormalJSContext);
8120 :
8121 0 : if (mContext) {
8122 0 : JS_DestroyContext(mContext);
8123 : }
8124 0 : }
8125 :
8126 : bool
8127 : Init();
8128 : };
8129 :
8130 : class CreateIndexOp::ThreadLocalJSContext final
8131 : : public NormalJSContext
8132 : {
8133 : friend class CreateIndexOp;
8134 : friend class nsAutoPtr<ThreadLocalJSContext>;
8135 :
8136 : public:
8137 : static ThreadLocalJSContext*
8138 : GetOrCreate();
8139 :
8140 : private:
8141 0 : ThreadLocalJSContext()
8142 0 : {
8143 0 : MOZ_COUNT_CTOR(CreateIndexOp::ThreadLocalJSContext);
8144 0 : }
8145 :
8146 0 : ~ThreadLocalJSContext()
8147 0 : {
8148 0 : MOZ_COUNT_DTOR(CreateIndexOp::ThreadLocalJSContext);
8149 0 : }
8150 : };
8151 :
8152 : class CreateIndexOp::UpdateIndexDataValuesFunction final
8153 : : public mozIStorageFunction
8154 : {
8155 : RefPtr<CreateIndexOp> mOp;
8156 : RefPtr<DatabaseConnection> mConnection;
8157 : JSContext* mCx;
8158 :
8159 : public:
8160 0 : UpdateIndexDataValuesFunction(CreateIndexOp* aOp,
8161 : DatabaseConnection* aConnection,
8162 : JSContext* aCx)
8163 0 : : mOp(aOp)
8164 : , mConnection(aConnection)
8165 0 : , mCx(aCx)
8166 : {
8167 0 : MOZ_ASSERT(aOp);
8168 0 : MOZ_ASSERT(aConnection);
8169 0 : aConnection->AssertIsOnConnectionThread();
8170 0 : MOZ_ASSERT(aCx);
8171 0 : }
8172 :
8173 : NS_DECL_ISUPPORTS
8174 :
8175 : private:
8176 0 : ~UpdateIndexDataValuesFunction() = default;
8177 :
8178 : NS_DECL_MOZISTORAGEFUNCTION
8179 : };
8180 :
8181 : class DeleteIndexOp final
8182 : : public VersionChangeTransactionOp
8183 : {
8184 : friend class VersionChangeTransaction;
8185 :
8186 : const int64_t mObjectStoreId;
8187 : const int64_t mIndexId;
8188 : const bool mUnique;
8189 : const bool mIsLastIndex;
8190 :
8191 : private:
8192 : // Only created by VersionChangeTransaction.
8193 : DeleteIndexOp(VersionChangeTransaction* aTransaction,
8194 : const int64_t aObjectStoreId,
8195 : const int64_t aIndexId,
8196 : const bool aUnique,
8197 : const bool aIsLastIndex);
8198 :
8199 0 : ~DeleteIndexOp() override = default;
8200 :
8201 : nsresult
8202 : RemoveReferencesToIndex(DatabaseConnection* aConnection,
8203 : const Key& aObjectDataKey,
8204 : nsTArray<IndexDataValue>& aIndexValues);
8205 :
8206 : nsresult
8207 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8208 : };
8209 :
8210 : class RenameIndexOp final
8211 : : public VersionChangeTransactionOp
8212 : {
8213 : friend class VersionChangeTransaction;
8214 :
8215 : const int64_t mObjectStoreId;
8216 : const int64_t mIndexId;
8217 : const nsString mNewName;
8218 :
8219 : private:
8220 : // Only created by VersionChangeTransaction.
8221 0 : RenameIndexOp(VersionChangeTransaction* aTransaction,
8222 : FullIndexMetadata* const aMetadata,
8223 : int64_t aObjectStoreId)
8224 0 : : VersionChangeTransactionOp(aTransaction)
8225 : , mObjectStoreId(aObjectStoreId)
8226 0 : , mIndexId(aMetadata->mCommonMetadata.id())
8227 0 : , mNewName(aMetadata->mCommonMetadata.name())
8228 : {
8229 0 : MOZ_ASSERT(mIndexId);
8230 0 : }
8231 :
8232 0 : ~RenameIndexOp() override = default;
8233 :
8234 : nsresult
8235 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8236 : };
8237 :
8238 : class NormalTransactionOp
8239 : : public TransactionDatabaseOperationBase
8240 : , public PBackgroundIDBRequestParent
8241 : {
8242 : #ifdef DEBUG
8243 : bool mResponseSent;
8244 : #endif
8245 :
8246 : public:
8247 : void
8248 : Cleanup() override;
8249 :
8250 : protected:
8251 0 : explicit NormalTransactionOp(TransactionBase* aTransaction)
8252 0 : : TransactionDatabaseOperationBase(aTransaction)
8253 : #ifdef DEBUG
8254 0 : , mResponseSent(false)
8255 : #endif
8256 0 : { }
8257 :
8258 0 : ~NormalTransactionOp() override = default;
8259 :
8260 : // An overload of DatabaseOperationBase's function that can avoid doing extra
8261 : // work on non-versionchange transactions.
8262 : static nsresult
8263 : ObjectStoreHasIndexes(NormalTransactionOp* aOp,
8264 : DatabaseConnection* aConnection,
8265 : const int64_t aObjectStoreId,
8266 : const bool aMayHaveIndexes,
8267 : bool* aHasIndexes);
8268 :
8269 : virtual nsresult
8270 : GetPreprocessParams(PreprocessParams& aParams);
8271 :
8272 :
8273 : // Subclasses use this override to set the IPDL response value.
8274 : virtual void
8275 : GetResponse(RequestResponse& aResponse) = 0;
8276 :
8277 : private:
8278 : nsresult
8279 : SendPreprocessInfo() override;
8280 :
8281 : nsresult
8282 : SendSuccessResult() override;
8283 :
8284 : bool
8285 : SendFailureResult(nsresult aResultCode) override;
8286 :
8287 : // IPDL methods.
8288 : void
8289 : ActorDestroy(ActorDestroyReason aWhy) override;
8290 :
8291 : mozilla::ipc::IPCResult
8292 : RecvContinue(const PreprocessResponse& aResponse) override;
8293 : };
8294 :
8295 : class ObjectStoreAddOrPutRequestOp final
8296 : : public NormalTransactionOp
8297 : {
8298 : friend class TransactionBase;
8299 :
8300 : typedef mozilla::dom::quota::PersistenceType PersistenceType;
8301 :
8302 : struct StoredFileInfo;
8303 : class SCInputStream;
8304 :
8305 : const ObjectStoreAddPutParams mParams;
8306 : Maybe<UniqueIndexTable> mUniqueIndexTable;
8307 :
8308 : // This must be non-const so that we can update the mNextAutoIncrementId field
8309 : // if we are modifying an autoIncrement objectStore.
8310 : RefPtr<FullObjectStoreMetadata> mMetadata;
8311 :
8312 : FallibleTArray<StoredFileInfo> mStoredFileInfos;
8313 :
8314 : Key mResponse;
8315 : const nsCString mGroup;
8316 : const nsCString mOrigin;
8317 : const PersistenceType mPersistenceType;
8318 : const bool mOverwrite;
8319 : bool mObjectStoreMayHaveIndexes;
8320 : bool mDataOverThreshold;
8321 :
8322 : private:
8323 : // Only created by TransactionBase.
8324 : ObjectStoreAddOrPutRequestOp(TransactionBase* aTransaction,
8325 : const RequestParams& aParams);
8326 :
8327 0 : ~ObjectStoreAddOrPutRequestOp() override = default;
8328 :
8329 : nsresult
8330 : RemoveOldIndexDataValues(DatabaseConnection* aConnection);
8331 :
8332 : bool
8333 : Init(TransactionBase* aTransaction) override;
8334 :
8335 : nsresult
8336 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8337 :
8338 : void
8339 : GetResponse(RequestResponse& aResponse) override;
8340 :
8341 : void
8342 : Cleanup() override;
8343 : };
8344 :
8345 : struct ObjectStoreAddOrPutRequestOp::StoredFileInfo final
8346 : {
8347 : RefPtr<DatabaseFile> mFileActor;
8348 : RefPtr<FileInfo> mFileInfo;
8349 : // A non-Blob-backed inputstream to write to disk. If null, mFileActor may
8350 : // still have a stream for us to write.
8351 : nsCOMPtr<nsIInputStream> mInputStream;
8352 : StructuredCloneFile::FileType mType;
8353 :
8354 0 : StoredFileInfo()
8355 0 : : mType(StructuredCloneFile::eBlob)
8356 : {
8357 0 : AssertIsOnBackgroundThread();
8358 :
8359 0 : MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
8360 0 : }
8361 :
8362 0 : ~StoredFileInfo()
8363 0 : {
8364 0 : AssertIsOnBackgroundThread();
8365 :
8366 0 : MOZ_COUNT_DTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
8367 0 : }
8368 :
8369 : void
8370 0 : Serialize(nsString& aText)
8371 : {
8372 0 : MOZ_ASSERT(mFileInfo);
8373 :
8374 0 : const int64_t id = mFileInfo->Id();
8375 :
8376 0 : switch (mType) {
8377 : case StructuredCloneFile::eBlob:
8378 0 : aText.AppendInt(id);
8379 0 : break;
8380 :
8381 : case StructuredCloneFile::eMutableFile:
8382 0 : aText.AppendInt(-id);
8383 0 : break;
8384 :
8385 : case StructuredCloneFile::eStructuredClone:
8386 0 : aText.Append('.');
8387 0 : aText.AppendInt(id);
8388 0 : break;
8389 :
8390 : case StructuredCloneFile::eWasmBytecode:
8391 0 : aText.Append('/');
8392 0 : aText.AppendInt(id);
8393 0 : break;
8394 :
8395 : case StructuredCloneFile::eWasmCompiled:
8396 0 : aText.Append('\\');
8397 0 : aText.AppendInt(id);
8398 0 : break;
8399 :
8400 : default:
8401 0 : MOZ_CRASH("Should never get here!");
8402 : }
8403 0 : }
8404 : };
8405 :
8406 : class ObjectStoreAddOrPutRequestOp::SCInputStream final
8407 : : public nsIInputStream
8408 : {
8409 : const JSStructuredCloneData& mData;
8410 : JSStructuredCloneData::IterImpl mIter;
8411 :
8412 : public:
8413 0 : explicit SCInputStream(const JSStructuredCloneData& aData)
8414 0 : : mData(aData)
8415 0 : , mIter(aData.Iter())
8416 0 : { }
8417 :
8418 : private:
8419 0 : virtual ~SCInputStream() = default;
8420 :
8421 : NS_DECL_THREADSAFE_ISUPPORTS
8422 : NS_DECL_NSIINPUTSTREAM
8423 : };
8424 :
8425 : class ObjectStoreGetRequestOp final
8426 : : public NormalTransactionOp
8427 : {
8428 : friend class TransactionBase;
8429 :
8430 : const uint32_t mObjectStoreId;
8431 : RefPtr<Database> mDatabase;
8432 : const OptionalKeyRange mOptionalKeyRange;
8433 : AutoTArray<StructuredCloneReadInfo, 1> mResponse;
8434 : PBackgroundParent* mBackgroundParent;
8435 : uint32_t mPreprocessInfoCount;
8436 : const uint32_t mLimit;
8437 : const bool mGetAll;
8438 :
8439 : private:
8440 : // Only created by TransactionBase.
8441 : ObjectStoreGetRequestOp(TransactionBase* aTransaction,
8442 : const RequestParams& aParams,
8443 : bool aGetAll);
8444 :
8445 0 : ~ObjectStoreGetRequestOp() override = default;
8446 :
8447 : template <bool aForPreprocess, typename T>
8448 : nsresult
8449 : ConvertResponse(StructuredCloneReadInfo& aInfo, T& aResult);
8450 :
8451 : nsresult
8452 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8453 :
8454 : bool
8455 : HasPreprocessInfo() override;
8456 :
8457 : nsresult
8458 : GetPreprocessParams(PreprocessParams& aParams) override;
8459 :
8460 : void
8461 : GetResponse(RequestResponse& aResponse) override;
8462 : };
8463 :
8464 : class ObjectStoreGetKeyRequestOp final
8465 : : public NormalTransactionOp
8466 : {
8467 : friend class TransactionBase;
8468 :
8469 : const uint32_t mObjectStoreId;
8470 : const OptionalKeyRange mOptionalKeyRange;
8471 : const uint32_t mLimit;
8472 : const bool mGetAll;
8473 : FallibleTArray<Key> mResponse;
8474 :
8475 : private:
8476 : // Only created by TransactionBase.
8477 : ObjectStoreGetKeyRequestOp(TransactionBase* aTransaction,
8478 : const RequestParams& aParams,
8479 : bool aGetAll);
8480 :
8481 0 : ~ObjectStoreGetKeyRequestOp() override = default;
8482 :
8483 : nsresult
8484 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8485 :
8486 : void
8487 : GetResponse(RequestResponse& aResponse) override;
8488 : };
8489 :
8490 : class ObjectStoreDeleteRequestOp final
8491 : : public NormalTransactionOp
8492 : {
8493 : friend class TransactionBase;
8494 :
8495 : const ObjectStoreDeleteParams mParams;
8496 : ObjectStoreDeleteResponse mResponse;
8497 : bool mObjectStoreMayHaveIndexes;
8498 :
8499 : private:
8500 : ObjectStoreDeleteRequestOp(TransactionBase* aTransaction,
8501 : const ObjectStoreDeleteParams& aParams);
8502 :
8503 0 : ~ObjectStoreDeleteRequestOp() override = default;
8504 :
8505 : nsresult
8506 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8507 :
8508 : void
8509 0 : GetResponse(RequestResponse& aResponse) override
8510 : {
8511 0 : aResponse = Move(mResponse);
8512 0 : }
8513 : };
8514 :
8515 : class ObjectStoreClearRequestOp final
8516 : : public NormalTransactionOp
8517 : {
8518 : friend class TransactionBase;
8519 :
8520 : const ObjectStoreClearParams mParams;
8521 : ObjectStoreClearResponse mResponse;
8522 : bool mObjectStoreMayHaveIndexes;
8523 :
8524 : private:
8525 : ObjectStoreClearRequestOp(TransactionBase* aTransaction,
8526 : const ObjectStoreClearParams& aParams);
8527 :
8528 0 : ~ObjectStoreClearRequestOp() override = default;
8529 :
8530 : nsresult
8531 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8532 :
8533 : void
8534 0 : GetResponse(RequestResponse& aResponse) override
8535 : {
8536 0 : aResponse = Move(mResponse);
8537 0 : }
8538 : };
8539 :
8540 : class ObjectStoreCountRequestOp final
8541 : : public NormalTransactionOp
8542 : {
8543 : friend class TransactionBase;
8544 :
8545 : const ObjectStoreCountParams mParams;
8546 : ObjectStoreCountResponse mResponse;
8547 :
8548 : private:
8549 0 : ObjectStoreCountRequestOp(TransactionBase* aTransaction,
8550 : const ObjectStoreCountParams& aParams)
8551 0 : : NormalTransactionOp(aTransaction)
8552 0 : , mParams(aParams)
8553 0 : { }
8554 :
8555 0 : ~ObjectStoreCountRequestOp() override = default;
8556 :
8557 : nsresult
8558 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8559 :
8560 : void
8561 0 : GetResponse(RequestResponse& aResponse) override
8562 : {
8563 0 : aResponse = Move(mResponse);
8564 0 : }
8565 : };
8566 :
8567 : class IndexRequestOpBase
8568 : : public NormalTransactionOp
8569 : {
8570 : protected:
8571 : const RefPtr<FullIndexMetadata> mMetadata;
8572 :
8573 : protected:
8574 0 : IndexRequestOpBase(TransactionBase* aTransaction,
8575 : const RequestParams& aParams)
8576 0 : : NormalTransactionOp(aTransaction)
8577 0 : , mMetadata(IndexMetadataForParams(aTransaction, aParams))
8578 0 : { }
8579 :
8580 :
8581 0 : ~IndexRequestOpBase() override = default;
8582 :
8583 : private:
8584 : static already_AddRefed<FullIndexMetadata>
8585 : IndexMetadataForParams(TransactionBase* aTransaction,
8586 : const RequestParams& aParams);
8587 : };
8588 :
8589 : class IndexGetRequestOp final
8590 : : public IndexRequestOpBase
8591 : {
8592 : friend class TransactionBase;
8593 :
8594 : RefPtr<Database> mDatabase;
8595 : const OptionalKeyRange mOptionalKeyRange;
8596 : AutoTArray<StructuredCloneReadInfo, 1> mResponse;
8597 : PBackgroundParent* mBackgroundParent;
8598 : const uint32_t mLimit;
8599 : const bool mGetAll;
8600 :
8601 : private:
8602 : // Only created by TransactionBase.
8603 : IndexGetRequestOp(TransactionBase* aTransaction,
8604 : const RequestParams& aParams,
8605 : bool aGetAll);
8606 :
8607 0 : ~IndexGetRequestOp() override = default;
8608 :
8609 : nsresult
8610 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8611 :
8612 : void
8613 : GetResponse(RequestResponse& aResponse) override;
8614 : };
8615 :
8616 : class IndexGetKeyRequestOp final
8617 : : public IndexRequestOpBase
8618 : {
8619 : friend class TransactionBase;
8620 :
8621 : const OptionalKeyRange mOptionalKeyRange;
8622 : AutoTArray<Key, 1> mResponse;
8623 : const uint32_t mLimit;
8624 : const bool mGetAll;
8625 :
8626 : private:
8627 : // Only created by TransactionBase.
8628 : IndexGetKeyRequestOp(TransactionBase* aTransaction,
8629 : const RequestParams& aParams,
8630 : bool aGetAll);
8631 :
8632 0 : ~IndexGetKeyRequestOp() override = default;
8633 :
8634 : nsresult
8635 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8636 :
8637 : void
8638 : GetResponse(RequestResponse& aResponse) override;
8639 : };
8640 :
8641 : class IndexCountRequestOp final
8642 : : public IndexRequestOpBase
8643 : {
8644 : friend class TransactionBase;
8645 :
8646 : const IndexCountParams mParams;
8647 : IndexCountResponse mResponse;
8648 :
8649 : private:
8650 : // Only created by TransactionBase.
8651 0 : IndexCountRequestOp(TransactionBase* aTransaction,
8652 : const RequestParams& aParams)
8653 0 : : IndexRequestOpBase(aTransaction, aParams)
8654 0 : , mParams(aParams.get_IndexCountParams())
8655 0 : { }
8656 :
8657 0 : ~IndexCountRequestOp() override = default;
8658 :
8659 : nsresult
8660 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8661 :
8662 : void
8663 0 : GetResponse(RequestResponse& aResponse) override
8664 : {
8665 0 : aResponse = Move(mResponse);
8666 0 : }
8667 : };
8668 :
8669 : class Cursor final :
8670 : public PBackgroundIDBCursorParent
8671 : {
8672 : friend class TransactionBase;
8673 :
8674 : class ContinueOp;
8675 : class CursorOpBase;
8676 : class OpenOp;
8677 :
8678 : public:
8679 : typedef OpenCursorParams::Type Type;
8680 :
8681 : private:
8682 : RefPtr<TransactionBase> mTransaction;
8683 : RefPtr<Database> mDatabase;
8684 : RefPtr<FileManager> mFileManager;
8685 : PBackgroundParent* mBackgroundParent;
8686 :
8687 : // These should only be touched on the PBackground thread to check whether the
8688 : // objectStore or index has been deleted. Holding these saves a hash lookup
8689 : // for every call to continue()/advance().
8690 : RefPtr<FullObjectStoreMetadata> mObjectStoreMetadata;
8691 : RefPtr<FullIndexMetadata> mIndexMetadata;
8692 :
8693 : const int64_t mObjectStoreId;
8694 : const int64_t mIndexId;
8695 :
8696 : nsCString mContinueQuery;
8697 : nsCString mContinueToQuery;
8698 : nsCString mContinuePrimaryKeyQuery;
8699 : nsCString mLocale;
8700 :
8701 : Key mKey;
8702 : Key mObjectKey;
8703 : Key mRangeKey;
8704 : Key mSortKey;
8705 :
8706 : CursorOpBase* mCurrentlyRunningOp;
8707 :
8708 : const Type mType;
8709 : const Direction mDirection;
8710 :
8711 : const bool mUniqueIndex;
8712 : const bool mIsSameProcessActor;
8713 : bool mActorDestroyed;
8714 :
8715 : public:
8716 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Cursor)
8717 :
8718 : private:
8719 : // Only created by TransactionBase.
8720 : Cursor(TransactionBase* aTransaction,
8721 : Type aType,
8722 : FullObjectStoreMetadata* aObjectStoreMetadata,
8723 : FullIndexMetadata* aIndexMetadata,
8724 : Direction aDirection);
8725 :
8726 : // Reference counted.
8727 0 : ~Cursor() override
8728 0 : {
8729 0 : MOZ_ASSERT(mActorDestroyed);
8730 0 : }
8731 :
8732 : bool
8733 : VerifyRequestParams(const CursorRequestParams& aParams) const;
8734 :
8735 : // Only called by TransactionBase.
8736 : bool
8737 : Start(const OpenCursorParams& aParams);
8738 :
8739 : void
8740 : SendResponseInternal(
8741 : CursorResponse& aResponse,
8742 : const nsTArray<FallibleTArray<StructuredCloneFile>>& aFiles);
8743 :
8744 : // Must call SendResponseInternal!
8745 : bool
8746 : SendResponse(const CursorResponse& aResponse) = delete;
8747 :
8748 : // IPDL methods.
8749 : void
8750 : ActorDestroy(ActorDestroyReason aWhy) override;
8751 :
8752 : mozilla::ipc::IPCResult
8753 : RecvDeleteMe() override;
8754 :
8755 : mozilla::ipc::IPCResult
8756 : RecvContinue(const CursorRequestParams& aParams) override;
8757 :
8758 : bool
8759 0 : IsLocaleAware() const {
8760 0 : return !mLocale.IsEmpty();
8761 : }
8762 : };
8763 :
8764 : class Cursor::CursorOpBase
8765 : : public TransactionDatabaseOperationBase
8766 : {
8767 : protected:
8768 : RefPtr<Cursor> mCursor;
8769 : nsTArray<FallibleTArray<StructuredCloneFile>> mFiles;
8770 :
8771 : CursorResponse mResponse;
8772 :
8773 : #ifdef DEBUG
8774 : bool mResponseSent;
8775 : #endif
8776 :
8777 : protected:
8778 0 : explicit CursorOpBase(Cursor* aCursor)
8779 0 : : TransactionDatabaseOperationBase(aCursor->mTransaction)
8780 : , mCursor(aCursor)
8781 : #ifdef DEBUG
8782 0 : , mResponseSent(false)
8783 : #endif
8784 : {
8785 0 : AssertIsOnBackgroundThread();
8786 0 : MOZ_ASSERT(aCursor);
8787 0 : }
8788 :
8789 :
8790 0 : ~CursorOpBase() override = default;
8791 :
8792 : bool
8793 : SendFailureResult(nsresult aResultCode) override;
8794 :
8795 : void
8796 : Cleanup() override;
8797 :
8798 : nsresult
8799 : PopulateResponseFromStatement(DatabaseConnection::CachedStatement& aStmt,
8800 : bool aInitializeResponse);
8801 : };
8802 :
8803 : class Cursor::OpenOp final
8804 : : public Cursor::CursorOpBase
8805 : {
8806 : friend class Cursor;
8807 :
8808 : const OptionalKeyRange mOptionalKeyRange;
8809 :
8810 : private:
8811 : // Only created by Cursor.
8812 0 : OpenOp(Cursor* aCursor,
8813 : const OptionalKeyRange& aOptionalKeyRange)
8814 0 : : CursorOpBase(aCursor)
8815 0 : , mOptionalKeyRange(aOptionalKeyRange)
8816 0 : { }
8817 :
8818 : // Reference counted.
8819 0 : ~OpenOp() override = default;
8820 :
8821 : void
8822 : GetRangeKeyInfo(bool aLowerBound, Key* aKey, bool* aOpen);
8823 :
8824 : nsresult
8825 : DoObjectStoreDatabaseWork(DatabaseConnection* aConnection);
8826 :
8827 : nsresult
8828 : DoObjectStoreKeyDatabaseWork(DatabaseConnection* aConnection);
8829 :
8830 : nsresult
8831 : DoIndexDatabaseWork(DatabaseConnection* aConnection);
8832 :
8833 : nsresult
8834 : DoIndexKeyDatabaseWork(DatabaseConnection* aConnection);
8835 :
8836 : nsresult
8837 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8838 :
8839 : nsresult
8840 : SendSuccessResult() override;
8841 : };
8842 :
8843 : class Cursor::ContinueOp final
8844 : : public Cursor::CursorOpBase
8845 : {
8846 : friend class Cursor;
8847 :
8848 : const CursorRequestParams mParams;
8849 :
8850 : private:
8851 : // Only created by Cursor.
8852 0 : ContinueOp(Cursor* aCursor,
8853 : const CursorRequestParams& aParams)
8854 0 : : CursorOpBase(aCursor)
8855 0 : , mParams(aParams)
8856 : {
8857 0 : MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None);
8858 0 : }
8859 :
8860 : // Reference counted.
8861 0 : ~ContinueOp() override = default;
8862 :
8863 : nsresult
8864 : DoDatabaseWork(DatabaseConnection* aConnection) override;
8865 :
8866 : nsresult
8867 : SendSuccessResult() override;
8868 : };
8869 :
8870 : class Utils final
8871 : : public PBackgroundIndexedDBUtilsParent
8872 : {
8873 : #ifdef DEBUG
8874 : bool mActorDestroyed;
8875 : #endif
8876 :
8877 : public:
8878 : Utils();
8879 :
8880 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Utils)
8881 :
8882 : private:
8883 : // Reference counted.
8884 : ~Utils() override;
8885 :
8886 : // IPDL methods are only called by IPDL.
8887 : void
8888 : ActorDestroy(ActorDestroyReason aWhy) override;
8889 :
8890 : mozilla::ipc::IPCResult
8891 : RecvDeleteMe() override;
8892 :
8893 : mozilla::ipc::IPCResult
8894 : RecvGetFileReferences(const PersistenceType& aPersistenceType,
8895 : const nsCString& aOrigin,
8896 : const nsString& aDatabaseName,
8897 : const int64_t& aFileId,
8898 : int32_t* aRefCnt,
8899 : int32_t* aDBRefCnt,
8900 : int32_t* aSliceRefCnt,
8901 : bool* aResult) override;
8902 : };
8903 :
8904 : class GetFileReferencesHelper final
8905 : : public Runnable
8906 : {
8907 : PersistenceType mPersistenceType;
8908 : nsCString mOrigin;
8909 : nsString mDatabaseName;
8910 : int64_t mFileId;
8911 :
8912 : mozilla::Mutex mMutex;
8913 : mozilla::CondVar mCondVar;
8914 : int32_t mMemRefCnt;
8915 : int32_t mDBRefCnt;
8916 : int32_t mSliceRefCnt;
8917 : bool mResult;
8918 : bool mWaiting;
8919 :
8920 : public:
8921 0 : GetFileReferencesHelper(PersistenceType aPersistenceType,
8922 : const nsACString& aOrigin,
8923 : const nsAString& aDatabaseName,
8924 : int64_t aFileId)
8925 0 : : Runnable("dom::indexedDB::GetFileReferencesHelper")
8926 : , mPersistenceType(aPersistenceType)
8927 : , mOrigin(aOrigin)
8928 : , mDatabaseName(aDatabaseName)
8929 : , mFileId(aFileId)
8930 : , mMutex("GetFileReferencesHelper::mMutex")
8931 : , mCondVar(mMutex, "GetFileReferencesHelper::mCondVar")
8932 : , mMemRefCnt(-1)
8933 : , mDBRefCnt(-1)
8934 : , mSliceRefCnt(-1)
8935 : , mResult(false)
8936 0 : , mWaiting(true)
8937 0 : { }
8938 :
8939 : nsresult
8940 : DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
8941 : int32_t* aDBRefCnt,
8942 : int32_t* aSliceRefCnt,
8943 : bool* aResult);
8944 :
8945 : private:
8946 0 : ~GetFileReferencesHelper() override = default;
8947 :
8948 : NS_DECL_NSIRUNNABLE
8949 : };
8950 :
8951 : class FlushPendingFileDeletionsRunnable final
8952 : : public Runnable
8953 : {
8954 : public:
8955 0 : FlushPendingFileDeletionsRunnable() : Runnable("FlushPendingFileDeletionsRunnable") {}
8956 :
8957 : private:
8958 0 : ~FlushPendingFileDeletionsRunnable() override = default;
8959 :
8960 : NS_DECL_NSIRUNNABLE
8961 : };
8962 :
8963 : class PermissionRequestHelper final
8964 : : public PermissionRequestBase
8965 : , public PIndexedDBPermissionRequestParent
8966 : {
8967 : bool mActorDestroyed;
8968 :
8969 : public:
8970 0 : PermissionRequestHelper(Element* aOwnerElement,
8971 : nsIPrincipal* aPrincipal)
8972 0 : : PermissionRequestBase(aOwnerElement, aPrincipal)
8973 0 : , mActorDestroyed(false)
8974 0 : { }
8975 :
8976 : protected:
8977 0 : ~PermissionRequestHelper() override = default;
8978 :
8979 : private:
8980 : void
8981 : OnPromptComplete(PermissionValue aPermissionValue) override;
8982 :
8983 : void
8984 : ActorDestroy(ActorDestroyReason aWhy) override;
8985 : };
8986 :
8987 : /*******************************************************************************
8988 : * Other class declarations
8989 : ******************************************************************************/
8990 :
8991 : struct DatabaseActorInfo final
8992 : {
8993 : friend class nsAutoPtr<DatabaseActorInfo>;
8994 :
8995 : RefPtr<FullDatabaseMetadata> mMetadata;
8996 : nsTArray<Database*> mLiveDatabases;
8997 : RefPtr<FactoryOp> mWaitingFactoryOp;
8998 :
8999 0 : DatabaseActorInfo(FullDatabaseMetadata* aMetadata,
9000 : Database* aDatabase)
9001 0 : : mMetadata(aMetadata)
9002 : {
9003 0 : MOZ_ASSERT(aDatabase);
9004 :
9005 0 : MOZ_COUNT_CTOR(DatabaseActorInfo);
9006 :
9007 0 : mLiveDatabases.AppendElement(aDatabase);
9008 0 : }
9009 :
9010 : private:
9011 0 : ~DatabaseActorInfo()
9012 0 : {
9013 0 : MOZ_ASSERT(mLiveDatabases.IsEmpty());
9014 0 : MOZ_ASSERT(!mWaitingFactoryOp ||
9015 : !mWaitingFactoryOp->HasBlockedDatabases());
9016 :
9017 0 : MOZ_COUNT_DTOR(DatabaseActorInfo);
9018 0 : }
9019 : };
9020 :
9021 : class DatabaseLoggingInfo final
9022 : {
9023 : #ifdef DEBUG
9024 : // Just for potential warnings.
9025 : friend class Factory;
9026 : #endif
9027 :
9028 : LoggingInfo mLoggingInfo;
9029 :
9030 : public:
9031 : explicit
9032 0 : DatabaseLoggingInfo(const LoggingInfo& aLoggingInfo)
9033 0 : : mLoggingInfo(aLoggingInfo)
9034 : {
9035 0 : AssertIsOnBackgroundThread();
9036 0 : MOZ_ASSERT(aLoggingInfo.nextTransactionSerialNumber());
9037 0 : MOZ_ASSERT(aLoggingInfo.nextVersionChangeTransactionSerialNumber());
9038 0 : MOZ_ASSERT(aLoggingInfo.nextRequestSerialNumber());
9039 0 : }
9040 :
9041 : const nsID&
9042 0 : Id() const
9043 : {
9044 0 : AssertIsOnBackgroundThread();
9045 :
9046 0 : return mLoggingInfo.backgroundChildLoggingId();
9047 : }
9048 :
9049 : int64_t
9050 0 : NextTransactionSN(IDBTransaction::Mode aMode)
9051 : {
9052 0 : AssertIsOnBackgroundThread();
9053 0 : MOZ_ASSERT(mLoggingInfo.nextTransactionSerialNumber() < INT64_MAX);
9054 0 : MOZ_ASSERT(mLoggingInfo.nextVersionChangeTransactionSerialNumber() >
9055 : INT64_MIN);
9056 :
9057 0 : if (aMode == IDBTransaction::VERSION_CHANGE) {
9058 0 : return mLoggingInfo.nextVersionChangeTransactionSerialNumber()--;
9059 : }
9060 :
9061 0 : return mLoggingInfo.nextTransactionSerialNumber()++;
9062 : }
9063 :
9064 : uint64_t
9065 0 : NextRequestSN()
9066 : {
9067 0 : AssertIsOnBackgroundThread();
9068 0 : MOZ_ASSERT(mLoggingInfo.nextRequestSerialNumber() < UINT64_MAX);
9069 :
9070 0 : return mLoggingInfo.nextRequestSerialNumber()++;
9071 : }
9072 :
9073 0 : NS_INLINE_DECL_REFCOUNTING(DatabaseLoggingInfo)
9074 :
9075 : private:
9076 : ~DatabaseLoggingInfo();
9077 : };
9078 :
9079 : class QuotaClient final
9080 : : public mozilla::dom::quota::Client
9081 : {
9082 : static QuotaClient* sInstance;
9083 :
9084 : nsCOMPtr<nsIEventTarget> mBackgroundThread;
9085 : nsTArray<RefPtr<Maintenance>> mMaintenanceQueue;
9086 : RefPtr<Maintenance> mCurrentMaintenance;
9087 : RefPtr<nsThreadPool> mMaintenanceThreadPool;
9088 : bool mShutdownRequested;
9089 :
9090 : public:
9091 : QuotaClient();
9092 :
9093 : static QuotaClient*
9094 0 : GetInstance()
9095 : {
9096 0 : AssertIsOnBackgroundThread();
9097 :
9098 0 : return sInstance;
9099 : }
9100 :
9101 : static bool
9102 0 : IsShuttingDownOnBackgroundThread()
9103 : {
9104 0 : AssertIsOnBackgroundThread();
9105 :
9106 0 : if (sInstance) {
9107 0 : return sInstance->IsShuttingDown();
9108 : }
9109 :
9110 0 : return QuotaManager::IsShuttingDown();
9111 : }
9112 :
9113 : static bool
9114 0 : IsShuttingDownOnNonBackgroundThread()
9115 : {
9116 0 : MOZ_ASSERT(!IsOnBackgroundThread());
9117 :
9118 0 : return QuotaManager::IsShuttingDown();
9119 : }
9120 :
9121 : nsIEventTarget*
9122 0 : BackgroundThread() const
9123 : {
9124 0 : MOZ_ASSERT(mBackgroundThread);
9125 0 : return mBackgroundThread;
9126 : }
9127 :
9128 : bool
9129 0 : IsShuttingDown() const
9130 : {
9131 0 : AssertIsOnBackgroundThread();
9132 :
9133 0 : return mShutdownRequested;
9134 : }
9135 :
9136 : already_AddRefed<Maintenance>
9137 0 : GetCurrentMaintenance() const
9138 : {
9139 0 : RefPtr<Maintenance> result = mCurrentMaintenance;
9140 0 : return result.forget();
9141 : }
9142 :
9143 : void
9144 0 : NoteFinishedMaintenance(Maintenance* aMaintenance)
9145 : {
9146 0 : AssertIsOnBackgroundThread();
9147 0 : MOZ_ASSERT(aMaintenance);
9148 0 : MOZ_ASSERT(mCurrentMaintenance == aMaintenance);
9149 :
9150 0 : mCurrentMaintenance = nullptr;
9151 0 : ProcessMaintenanceQueue();
9152 0 : }
9153 :
9154 : nsThreadPool*
9155 : GetOrCreateThreadPool();
9156 :
9157 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
9158 :
9159 : mozilla::dom::quota::Client::Type
9160 : GetType() override;
9161 :
9162 : nsresult
9163 : UpgradeStorageFrom1_0To2_0(nsIFile* aDirectory) override;
9164 :
9165 : nsresult
9166 : InitOrigin(PersistenceType aPersistenceType,
9167 : const nsACString& aGroup,
9168 : const nsACString& aOrigin,
9169 : const AtomicBool& aCanceled,
9170 : UsageInfo* aUsageInfo) override;
9171 :
9172 : nsresult
9173 : GetUsageForOrigin(PersistenceType aPersistenceType,
9174 : const nsACString& aGroup,
9175 : const nsACString& aOrigin,
9176 : const AtomicBool& aCanceled,
9177 : UsageInfo* aUsageInfo) override;
9178 :
9179 : void
9180 : OnOriginClearCompleted(PersistenceType aPersistenceType,
9181 : const nsACString& aOrigin)
9182 : override;
9183 :
9184 : void
9185 : ReleaseIOThreadObjects() override;
9186 :
9187 : void
9188 : AbortOperations(const nsACString& aOrigin) override;
9189 :
9190 : void
9191 : AbortOperationsForProcess(ContentParentId aContentParentId) override;
9192 :
9193 : void
9194 : StartIdleMaintenance() override;
9195 :
9196 : void
9197 : StopIdleMaintenance() override;
9198 :
9199 : void
9200 : ShutdownWorkThreads() override;
9201 :
9202 : void
9203 : DidInitialize(QuotaManager* aQuotaManager) override;
9204 :
9205 : void
9206 : WillShutdown() override;
9207 :
9208 : private:
9209 : ~QuotaClient() override;
9210 :
9211 : nsresult
9212 : GetDirectory(PersistenceType aPersistenceType,
9213 : const nsACString& aOrigin,
9214 : nsIFile** aDirectory);
9215 :
9216 : nsresult
9217 : GetDatabaseFilenames(nsIFile* aDirectory,
9218 : const AtomicBool& aCanceled,
9219 : bool aForUpgrade,
9220 : nsTArray<nsString>& aSubdirsToProcess,
9221 : nsTHashtable<nsStringHashKey>& aDatabaseFilename);
9222 :
9223 : nsresult
9224 : GetUsageForDirectoryInternal(nsIFile* aDirectory,
9225 : const AtomicBool& aCanceled,
9226 : UsageInfo* aUsageInfo,
9227 : bool aDatabaseFiles);
9228 :
9229 : // Runs on the PBackground thread. Checks to see if there's a queued
9230 : // Maintenance to run.
9231 : void
9232 : ProcessMaintenanceQueue();
9233 : };
9234 :
9235 : class Maintenance final
9236 : : public Runnable
9237 : , public OpenDirectoryListener
9238 : {
9239 : struct DirectoryInfo;
9240 :
9241 : enum class State
9242 : {
9243 : // Newly created on the PBackground thread. Will proceed immediately or be
9244 : // added to the maintenance queue. The next step is either
9245 : // DirectoryOpenPending if IndexedDatabaseManager is running, or
9246 : // CreateIndexedDatabaseManager if not.
9247 : Initial = 0,
9248 :
9249 : // Create IndexedDatabaseManager on the main thread. The next step is either
9250 : // Finishing if IndexedDatabaseManager initialization fails, or
9251 : // IndexedDatabaseManagerOpen if initialization succeeds.
9252 : CreateIndexedDatabaseManager,
9253 :
9254 : // Call OpenDirectory() on the PBackground thread. The next step is
9255 : // DirectoryOpenPending.
9256 : IndexedDatabaseManagerOpen,
9257 :
9258 : // Waiting for directory open allowed on the PBackground thread. The next
9259 : // step is either Finishing if directory lock failed to acquire, or
9260 : // DirectoryWorkOpen if directory lock is acquired.
9261 : DirectoryOpenPending,
9262 :
9263 : // Waiting to do/doing work on the QuotaManager IO thread. The next step is
9264 : // BeginDatabaseMaintenance.
9265 : DirectoryWorkOpen,
9266 :
9267 : // Dispatching a runnable for each database on the PBackground thread. The
9268 : // next state is either WaitingForDatabaseMaintenancesToComplete if at least
9269 : // one runnable has been dispatched, or Finishing otherwise.
9270 : BeginDatabaseMaintenance,
9271 :
9272 : // Waiting for DatabaseMaintenance to finish on maintenance thread pool.
9273 : // The next state is Finishing if the last runnable has finished.
9274 : WaitingForDatabaseMaintenancesToComplete,
9275 :
9276 : // Waiting to finish/finishing on the PBackground thread. The next step is
9277 : // Completed.
9278 : Finishing,
9279 :
9280 : // All done.
9281 : Complete
9282 : };
9283 :
9284 : RefPtr<QuotaClient> mQuotaClient;
9285 : PRTime mStartTime;
9286 : RefPtr<DirectoryLock> mDirectoryLock;
9287 : nsTArray<DirectoryInfo> mDirectoryInfos;
9288 : nsDataHashtable<nsStringHashKey, DatabaseMaintenance*> mDatabaseMaintenances;
9289 : nsresult mResultCode;
9290 : Atomic<bool> mAborted;
9291 : State mState;
9292 :
9293 : public:
9294 0 : explicit Maintenance(QuotaClient* aQuotaClient)
9295 0 : : Runnable("dom::indexedDB::Maintenance")
9296 : , mQuotaClient(aQuotaClient)
9297 0 : , mStartTime(PR_Now())
9298 : , mResultCode(NS_OK)
9299 : , mAborted(false)
9300 0 : , mState(State::Initial)
9301 : {
9302 0 : AssertIsOnBackgroundThread();
9303 0 : MOZ_ASSERT(aQuotaClient);
9304 0 : MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient);
9305 0 : MOZ_ASSERT(mStartTime);
9306 0 : }
9307 :
9308 : nsIEventTarget*
9309 0 : BackgroundThread() const
9310 : {
9311 0 : MOZ_ASSERT(mQuotaClient);
9312 0 : return mQuotaClient->BackgroundThread();
9313 : }
9314 :
9315 : PRTime
9316 0 : StartTime() const
9317 : {
9318 0 : return mStartTime;
9319 : }
9320 :
9321 : bool
9322 0 : IsAborted() const
9323 : {
9324 0 : return mAborted;
9325 : }
9326 :
9327 : void
9328 0 : RunImmediately()
9329 : {
9330 0 : MOZ_ASSERT(mState == State::Initial);
9331 :
9332 0 : Unused << this->Run();
9333 0 : }
9334 :
9335 : void
9336 0 : Abort()
9337 : {
9338 0 : AssertIsOnBackgroundThread();
9339 :
9340 0 : mAborted = true;
9341 0 : }
9342 :
9343 : void
9344 : RegisterDatabaseMaintenance(DatabaseMaintenance* aDatabaseMaintenance);
9345 :
9346 : void
9347 : UnregisterDatabaseMaintenance(DatabaseMaintenance* aDatabaseMaintenance);
9348 :
9349 : already_AddRefed<DatabaseMaintenance>
9350 0 : GetDatabaseMaintenance(const nsAString& aDatabasePath) const
9351 : {
9352 0 : AssertIsOnBackgroundThread();
9353 :
9354 : RefPtr<DatabaseMaintenance> result =
9355 0 : mDatabaseMaintenances.Get(aDatabasePath);
9356 0 : return result.forget();
9357 : }
9358 :
9359 : private:
9360 0 : ~Maintenance() override
9361 0 : {
9362 0 : MOZ_ASSERT(mState == State::Complete);
9363 0 : MOZ_ASSERT(!mDatabaseMaintenances.Count());
9364 0 : }
9365 :
9366 : // Runs on the PBackground thread. Checks if IndexedDatabaseManager is
9367 : // running. Calls OpenDirectory() or dispatches to the main thread on which
9368 : // CreateIndexedDatabaseManager() is called.
9369 : nsresult
9370 : Start();
9371 :
9372 : // Runs on the main thread. Once IndexedDatabaseManager is created it will
9373 : // dispatch to the PBackground thread on which OpenDirectory() is called.
9374 : nsresult
9375 : CreateIndexedDatabaseManager();
9376 :
9377 : // Runs on the PBackground thread. Once QuotaManager has given a lock it will
9378 : // call DirectoryOpen().
9379 : nsresult
9380 : OpenDirectory();
9381 :
9382 : // Runs on the PBackground thread. Dispatches to the QuotaManager I/O thread.
9383 : nsresult
9384 : DirectoryOpen();
9385 :
9386 : // Runs on the QuotaManager I/O thread. Once it finds databases it will
9387 : // dispatch to the PBackground thread on which BeginDatabaseMaintenance()
9388 : // is called.
9389 : nsresult
9390 : DirectoryWork();
9391 :
9392 : // Runs on the PBackground thread. It dispatches a runnable for each database.
9393 : nsresult
9394 : BeginDatabaseMaintenance();
9395 :
9396 : // Runs on the PBackground thread. Called when the maintenance is finished or
9397 : // if any of above methods fails.
9398 : void
9399 : Finish();
9400 :
9401 : NS_DECL_ISUPPORTS_INHERITED
9402 :
9403 : NS_DECL_NSIRUNNABLE
9404 :
9405 : // OpenDirectoryListener overrides.
9406 : void
9407 : DirectoryLockAcquired(DirectoryLock* aLock) override;
9408 :
9409 : void
9410 : DirectoryLockFailed() override;
9411 : };
9412 :
9413 : struct Maintenance::DirectoryInfo final
9414 : {
9415 : const nsCString mGroup;
9416 : const nsCString mOrigin;
9417 : nsTArray<nsString> mDatabasePaths;
9418 : const PersistenceType mPersistenceType;
9419 :
9420 0 : DirectoryInfo(PersistenceType aPersistenceType,
9421 : const nsACString& aGroup,
9422 : const nsACString& aOrigin,
9423 : nsTArray<nsString>&& aDatabasePaths)
9424 0 : : mGroup(aGroup)
9425 : , mOrigin(aOrigin)
9426 0 : , mDatabasePaths(Move(aDatabasePaths))
9427 0 : , mPersistenceType(aPersistenceType)
9428 : {
9429 0 : MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID);
9430 0 : MOZ_ASSERT(!aGroup.IsEmpty());
9431 0 : MOZ_ASSERT(!aOrigin.IsEmpty());
9432 : #ifdef DEBUG
9433 0 : MOZ_ASSERT(!mDatabasePaths.IsEmpty());
9434 0 : for (const nsString& databasePath : mDatabasePaths) {
9435 0 : MOZ_ASSERT(!databasePath.IsEmpty());
9436 : }
9437 : #endif
9438 :
9439 0 : MOZ_COUNT_CTOR(Maintenance::DirectoryInfo);
9440 0 : }
9441 :
9442 0 : DirectoryInfo(DirectoryInfo&& aOther)
9443 0 : : mGroup(Move(aOther.mGroup))
9444 0 : , mOrigin(Move(aOther.mOrigin))
9445 0 : , mDatabasePaths(Move(aOther.mDatabasePaths))
9446 0 : , mPersistenceType(Move(aOther.mPersistenceType))
9447 : {
9448 : #ifdef DEBUG
9449 0 : MOZ_ASSERT(!mDatabasePaths.IsEmpty());
9450 0 : for (const nsString& databasePath : mDatabasePaths) {
9451 0 : MOZ_ASSERT(!databasePath.IsEmpty());
9452 : }
9453 : #endif
9454 :
9455 0 : MOZ_COUNT_CTOR(Maintenance::DirectoryInfo);
9456 0 : }
9457 :
9458 0 : ~DirectoryInfo()
9459 0 : {
9460 0 : MOZ_COUNT_DTOR(Maintenance::DirectoryInfo);
9461 0 : }
9462 :
9463 : DirectoryInfo(const DirectoryInfo& aOther) = delete;
9464 : };
9465 :
9466 : class DatabaseMaintenance final
9467 : : public Runnable
9468 : {
9469 : // The minimum amount of time that has passed since the last vacuum before we
9470 : // will attempt to analyze the database for fragmentation.
9471 : static const PRTime kMinVacuumAge =
9472 : PRTime(PR_USEC_PER_SEC) * 60 * 60 * 24 * 7;
9473 :
9474 : // If the percent of database pages that are not in contiguous order is higher
9475 : // than this percentage we will attempt a vacuum.
9476 : static const int32_t kPercentUnorderedThreshold = 30;
9477 :
9478 : // If the percent of file size growth since the last vacuum is higher than
9479 : // this percentage we will attempt a vacuum.
9480 : static const int32_t kPercentFileSizeGrowthThreshold = 10;
9481 :
9482 : // The number of freelist pages beyond which we will favor an incremental
9483 : // vacuum over a full vacuum.
9484 : static const int32_t kMaxFreelistThreshold = 5;
9485 :
9486 : // If the percent of unused file bytes in the database exceeds this percentage
9487 : // then we will attempt a full vacuum.
9488 : static const int32_t kPercentUnusedThreshold = 20;
9489 :
9490 : class AutoProgressHandler;
9491 :
9492 : enum class MaintenanceAction
9493 : {
9494 : Nothing = 0,
9495 : IncrementalVacuum,
9496 : FullVacuum
9497 : };
9498 :
9499 : RefPtr<Maintenance> mMaintenance;
9500 : const nsCString mGroup;
9501 : const nsCString mOrigin;
9502 : const nsString mDatabasePath;
9503 : nsCOMPtr<nsIRunnable> mCompleteCallback;
9504 : const PersistenceType mPersistenceType;
9505 :
9506 : public:
9507 0 : DatabaseMaintenance(Maintenance* aMaintenance,
9508 : PersistenceType aPersistenceType,
9509 : const nsCString& aGroup,
9510 : const nsCString& aOrigin,
9511 : const nsString& aDatabasePath)
9512 0 : : Runnable("dom::indexedDB::DatabaseMaintenance")
9513 : , mMaintenance(aMaintenance)
9514 : , mGroup(aGroup)
9515 : , mOrigin(aOrigin)
9516 : , mDatabasePath(aDatabasePath)
9517 0 : , mPersistenceType(aPersistenceType)
9518 0 : { }
9519 :
9520 : const nsString&
9521 0 : DatabasePath() const
9522 : {
9523 0 : return mDatabasePath;
9524 : }
9525 :
9526 : void
9527 0 : WaitForCompletion(nsIRunnable* aCallback)
9528 : {
9529 0 : AssertIsOnBackgroundThread();
9530 0 : MOZ_ASSERT(!mCompleteCallback);
9531 :
9532 0 : mCompleteCallback = aCallback;
9533 0 : }
9534 :
9535 : private:
9536 0 : ~DatabaseMaintenance() override = default;
9537 :
9538 : // Runs on maintenance thread pool. Does maintenance on the database.
9539 : void
9540 : PerformMaintenanceOnDatabase();
9541 :
9542 : // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
9543 : nsresult
9544 : CheckIntegrity(mozIStorageConnection* aConnection, bool* aOk);
9545 :
9546 : // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
9547 : nsresult
9548 : DetermineMaintenanceAction(mozIStorageConnection* aConnection,
9549 : nsIFile* aDatabaseFile,
9550 : MaintenanceAction* aMaintenanceAction);
9551 :
9552 : // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
9553 : void
9554 : IncrementalVacuum(mozIStorageConnection* aConnection);
9555 :
9556 : // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
9557 : void
9558 : FullVacuum(mozIStorageConnection* aConnection,
9559 : nsIFile* aDatabaseFile);
9560 :
9561 : // Runs on the PBackground thread. It dispatches a complete callback and
9562 : // unregisters from Maintenance.
9563 : void
9564 : RunOnOwningThread();
9565 :
9566 : // Runs on maintenance thread pool. Once it performs database maintenance
9567 : // it will dispatch to the PBackground thread on which RunOnOwningThread()
9568 : // is called.
9569 : void
9570 : RunOnConnectionThread();
9571 :
9572 : NS_DECL_NSIRUNNABLE
9573 : };
9574 :
9575 : class MOZ_STACK_CLASS DatabaseMaintenance::AutoProgressHandler final
9576 : : public mozIStorageProgressHandler
9577 : {
9578 : Maintenance* mMaintenance;
9579 : mozIStorageConnection* mConnection;
9580 :
9581 : NS_DECL_OWNINGTHREAD
9582 :
9583 : #ifdef DEBUG
9584 : // This class is stack-based so we never actually allow AddRef/Release to do
9585 : // anything. But we need to know if any consumer *thinks* that they have a
9586 : // reference to this object so we track the reference countin DEBUG builds.
9587 : nsrefcnt mDEBUGRefCnt;
9588 : #endif
9589 :
9590 : public:
9591 0 : explicit AutoProgressHandler(Maintenance* aMaintenance)
9592 0 : : mMaintenance(aMaintenance)
9593 : , mConnection(nullptr)
9594 : #ifdef DEBUG
9595 0 : , mDEBUGRefCnt(0)
9596 : #endif
9597 : {
9598 0 : MOZ_ASSERT(!NS_IsMainThread());
9599 0 : MOZ_ASSERT(!IsOnBackgroundThread());
9600 0 : NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
9601 0 : MOZ_ASSERT(aMaintenance);
9602 0 : }
9603 :
9604 0 : ~AutoProgressHandler()
9605 0 : {
9606 0 : NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
9607 :
9608 0 : if (mConnection) {
9609 0 : Unregister();
9610 : }
9611 :
9612 0 : MOZ_ASSERT(!mDEBUGRefCnt);
9613 0 : }
9614 :
9615 : nsresult
9616 : Register(mozIStorageConnection* aConnection);
9617 :
9618 : // We don't want the mRefCnt member but this class does not "inherit"
9619 : // nsISupports.
9620 : NS_DECL_ISUPPORTS_INHERITED
9621 :
9622 : private:
9623 : void
9624 : Unregister();
9625 :
9626 : NS_DECL_MOZISTORAGEPROGRESSHANDLER
9627 :
9628 : // Not available for the heap!
9629 : void*
9630 : operator new(size_t) = delete;
9631 : void*
9632 : operator new[](size_t) = delete;
9633 : void
9634 : operator delete(void*) = delete;
9635 : void
9636 : operator delete[](void*) = delete;
9637 : };
9638 :
9639 0 : class IntString : public nsAutoString
9640 : {
9641 : public:
9642 : explicit
9643 0 : IntString(int64_t aInteger)
9644 0 : {
9645 0 : AppendInt(aInteger);
9646 0 : }
9647 : };
9648 :
9649 : #ifdef DEBUG
9650 :
9651 : class DEBUGThreadSlower final
9652 : : public nsIThreadObserver
9653 : {
9654 : public:
9655 : DEBUGThreadSlower()
9656 : {
9657 : AssertIsOnBackgroundThread();
9658 : MOZ_ASSERT(kDEBUGThreadSleepMS);
9659 : }
9660 :
9661 : NS_DECL_ISUPPORTS
9662 :
9663 : private:
9664 0 : ~DEBUGThreadSlower()
9665 0 : {
9666 0 : AssertIsOnBackgroundThread();
9667 0 : }
9668 :
9669 : NS_DECL_NSITHREADOBSERVER
9670 : };
9671 :
9672 : #endif // DEBUG
9673 :
9674 : /*******************************************************************************
9675 : * Helper classes
9676 : ******************************************************************************/
9677 :
9678 0 : class MOZ_STACK_CLASS FileHelper final
9679 : {
9680 : RefPtr<FileManager> mFileManager;
9681 :
9682 : nsCOMPtr<nsIFile> mFileDirectory;
9683 : nsCOMPtr<nsIFile> mJournalDirectory;
9684 :
9685 : class ReadCallback;
9686 : RefPtr<ReadCallback> mReadCallback;
9687 :
9688 : public:
9689 0 : explicit FileHelper(FileManager* aFileManager)
9690 0 : : mFileManager(aFileManager)
9691 0 : { }
9692 :
9693 : nsresult
9694 : Init();
9695 :
9696 : already_AddRefed<nsIFile>
9697 : GetFile(FileInfo* aFileInfo);
9698 :
9699 : already_AddRefed<nsIFile>
9700 : GetCheckedFile(FileInfo* aFileInfo);
9701 :
9702 : already_AddRefed<nsIFile>
9703 : GetJournalFile(FileInfo* aFileInfo);
9704 :
9705 : nsresult
9706 : CreateFileFromStream(nsIFile* aFile,
9707 : nsIFile* aJournalFile,
9708 : nsIInputStream* aInputStream,
9709 : bool aCompress);
9710 :
9711 : nsresult
9712 : ReplaceFile(nsIFile* aFile,
9713 : nsIFile* aNewFile,
9714 : nsIFile* aNewJournalFile);
9715 :
9716 : nsresult
9717 : RemoveFile(nsIFile* aFile,
9718 : nsIFile* aJournalFile);
9719 :
9720 : already_AddRefed<FileInfo>
9721 : GetNewFileInfo();
9722 :
9723 : private:
9724 : nsresult
9725 : SyncCopy(nsIInputStream* aInputStream,
9726 : nsIOutputStream* aOutputStream,
9727 : char* aBuffer,
9728 : uint32_t aBufferSize);
9729 :
9730 : nsresult
9731 : SyncRead(nsIInputStream* aInputStream,
9732 : char* aBuffer,
9733 : uint32_t aBufferSize,
9734 : uint32_t* aRead);
9735 : };
9736 :
9737 : /*******************************************************************************
9738 : * Helper Functions
9739 : ******************************************************************************/
9740 :
9741 : bool
9742 0 : TokenizerIgnoreNothing(char16_t /* aChar */)
9743 : {
9744 0 : return false;
9745 : }
9746 :
9747 : nsresult
9748 0 : DeserializeStructuredCloneFile(FileManager* aFileManager,
9749 : const nsString& aText,
9750 : StructuredCloneFile* aFile)
9751 : {
9752 0 : MOZ_ASSERT(!aText.IsEmpty());
9753 0 : MOZ_ASSERT(aFile);
9754 :
9755 : StructuredCloneFile::FileType type;
9756 :
9757 0 : switch (aText.First()) {
9758 : case char16_t('-'):
9759 0 : type = StructuredCloneFile::eMutableFile;
9760 0 : break;
9761 :
9762 : case char16_t('.'):
9763 0 : type = StructuredCloneFile::eStructuredClone;
9764 0 : break;
9765 :
9766 : case char16_t('/'):
9767 0 : type = StructuredCloneFile::eWasmBytecode;
9768 0 : break;
9769 :
9770 : case char16_t('\\'):
9771 0 : type = StructuredCloneFile::eWasmCompiled;
9772 0 : break;
9773 :
9774 : default:
9775 0 : type = StructuredCloneFile::eBlob;
9776 : }
9777 :
9778 : nsresult rv;
9779 : int32_t id;
9780 :
9781 0 : if (type == StructuredCloneFile::eBlob) {
9782 0 : id = aText.ToInteger(&rv);
9783 : } else {
9784 0 : nsString text(Substring(aText, 1));
9785 :
9786 0 : id = text.ToInteger(&rv);
9787 : }
9788 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9789 0 : return rv;
9790 : }
9791 :
9792 0 : RefPtr<FileInfo> fileInfo = aFileManager->GetFileInfo(id);
9793 0 : MOZ_ASSERT(fileInfo);
9794 :
9795 0 : aFile->mFileInfo.swap(fileInfo);
9796 0 : aFile->mType = type;
9797 :
9798 0 : return NS_OK;
9799 : }
9800 :
9801 : nsresult
9802 0 : CheckWasmModule(FileHelper* aFileHelper,
9803 : StructuredCloneFile* aBytecodeFile,
9804 : StructuredCloneFile* aCompiledFile)
9805 : {
9806 0 : MOZ_ASSERT(!IsOnBackgroundThread());
9807 0 : MOZ_ASSERT(aFileHelper);
9808 0 : MOZ_ASSERT(aBytecodeFile);
9809 0 : MOZ_ASSERT(aCompiledFile);
9810 0 : MOZ_ASSERT(aBytecodeFile->mType == StructuredCloneFile::eWasmBytecode);
9811 0 : MOZ_ASSERT(aCompiledFile->mType == StructuredCloneFile::eWasmCompiled);
9812 :
9813 : nsCOMPtr<nsIFile> compiledFile =
9814 0 : aFileHelper->GetCheckedFile(aCompiledFile->mFileInfo);
9815 0 : if (NS_WARN_IF(!compiledFile)) {
9816 0 : return NS_ERROR_FAILURE;
9817 : }
9818 :
9819 : nsresult rv;
9820 :
9821 : bool match;
9822 : {
9823 0 : ScopedPRFileDesc compiledFileDesc;
9824 0 : rv = compiledFile->OpenNSPRFileDesc(PR_RDONLY,
9825 : 0644,
9826 0 : &compiledFileDesc.rwget());
9827 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9828 0 : return rv;
9829 : }
9830 :
9831 0 : JS::BuildIdCharVector buildId;
9832 0 : bool ok = GetBuildId(&buildId);
9833 0 : if (NS_WARN_IF(!ok)) {
9834 0 : return NS_ERROR_FAILURE;
9835 : }
9836 :
9837 0 : match = JS::CompiledWasmModuleAssumptionsMatch(compiledFileDesc,
9838 0 : Move(buildId));
9839 : }
9840 0 : if (match) {
9841 0 : return NS_OK;
9842 : }
9843 :
9844 : // Re-compile the module. It would be preferable to do this in the child
9845 : // (content process) instead of here in the parent, but that would be way more
9846 : // complex and without significant memory allocation or security benefits.
9847 : // See the discussion starting from
9848 : // https://bugzilla.mozilla.org/show_bug.cgi?id=1312808#c9 for more details.
9849 : nsCOMPtr<nsIFile> bytecodeFile =
9850 0 : aFileHelper->GetCheckedFile(aBytecodeFile->mFileInfo);
9851 0 : if (NS_WARN_IF(!bytecodeFile)) {
9852 0 : return NS_ERROR_FAILURE;
9853 : }
9854 :
9855 0 : ScopedPRFileDesc bytecodeFileDesc;
9856 0 : rv = bytecodeFile->OpenNSPRFileDesc(PR_RDONLY,
9857 : 0644,
9858 0 : &bytecodeFileDesc.rwget());
9859 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9860 0 : return rv;
9861 : }
9862 :
9863 0 : JS::BuildIdCharVector buildId;
9864 0 : bool ok = GetBuildId(&buildId);
9865 0 : if (NS_WARN_IF(!ok)) {
9866 0 : return NS_ERROR_FAILURE;
9867 : }
9868 :
9869 : RefPtr<JS::WasmModule> module = JS::DeserializeWasmModule(bytecodeFileDesc,
9870 : nullptr,
9871 0 : Move(buildId),
9872 : nullptr,
9873 : 0,
9874 0 : 0);
9875 0 : if (NS_WARN_IF(!module)) {
9876 0 : return NS_ERROR_FAILURE;
9877 : }
9878 :
9879 : size_t compiledSize;
9880 0 : module->serializedSize(nullptr, &compiledSize);
9881 :
9882 0 : UniquePtr<uint8_t[]> compiled(new (fallible) uint8_t[compiledSize]);
9883 0 : if (NS_WARN_IF(!compiled)) {
9884 0 : return NS_ERROR_OUT_OF_MEMORY;
9885 : }
9886 :
9887 0 : module->serialize(nullptr, 0, compiled.get(), compiledSize);
9888 :
9889 0 : nsCOMPtr<nsIInputStream> inputStream;
9890 0 : rv = NS_NewByteInputStream(getter_AddRefs(inputStream),
9891 0 : reinterpret_cast<const char*>(compiled.get()),
9892 0 : compiledSize);
9893 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9894 0 : return rv;
9895 : }
9896 :
9897 0 : RefPtr<FileInfo> newFileInfo = aFileHelper->GetNewFileInfo();
9898 :
9899 0 : nsCOMPtr<nsIFile> newFile = aFileHelper->GetFile(newFileInfo);
9900 0 : if (NS_WARN_IF(!newFile)) {
9901 0 : return NS_ERROR_FAILURE;
9902 : }
9903 :
9904 : nsCOMPtr<nsIFile> newJournalFile =
9905 0 : aFileHelper->GetJournalFile(newFileInfo);
9906 0 : if (NS_WARN_IF(!newJournalFile)) {
9907 0 : return NS_ERROR_FAILURE;
9908 : }
9909 :
9910 0 : rv = aFileHelper->CreateFileFromStream(newFile,
9911 : newJournalFile,
9912 : inputStream,
9913 0 : /* aCompress */ false);
9914 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9915 0 : nsresult rv2 = aFileHelper->RemoveFile(newFile, newJournalFile);
9916 0 : if (NS_WARN_IF(NS_FAILED(rv2))) {
9917 0 : return rv;
9918 : }
9919 0 : return rv;
9920 : }
9921 :
9922 0 : rv = aFileHelper->ReplaceFile(compiledFile, newFile, newJournalFile);
9923 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9924 0 : nsresult rv2 = aFileHelper->RemoveFile(newFile, newJournalFile);
9925 0 : if (NS_WARN_IF(NS_FAILED(rv2))) {
9926 0 : return rv;
9927 : }
9928 0 : return rv;
9929 : }
9930 :
9931 0 : return NS_OK;
9932 : }
9933 :
9934 : nsresult
9935 0 : DeserializeStructuredCloneFiles(FileManager* aFileManager,
9936 : const nsAString& aText,
9937 : nsTArray<StructuredCloneFile>& aResult,
9938 : bool* aHasPreprocessInfo)
9939 : {
9940 0 : MOZ_ASSERT(!IsOnBackgroundThread());
9941 :
9942 : nsCharSeparatedTokenizerTemplate<TokenizerIgnoreNothing>
9943 0 : tokenizer(aText, ' ');
9944 :
9945 0 : nsAutoString token;
9946 : nsresult rv;
9947 0 : Maybe<FileHelper> fileHelper;
9948 :
9949 0 : while (tokenizer.hasMoreTokens()) {
9950 0 : token = tokenizer.nextToken();
9951 0 : MOZ_ASSERT(!token.IsEmpty());
9952 :
9953 0 : StructuredCloneFile* file = aResult.AppendElement();
9954 0 : rv = DeserializeStructuredCloneFile(aFileManager, token, file);
9955 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9956 0 : return rv;
9957 : }
9958 :
9959 0 : if (!aHasPreprocessInfo) {
9960 0 : continue;
9961 : }
9962 :
9963 0 : if (file->mType == StructuredCloneFile::eWasmBytecode) {
9964 0 : *aHasPreprocessInfo = true;
9965 : }
9966 0 : else if (file->mType == StructuredCloneFile::eWasmCompiled) {
9967 0 : if (fileHelper.isNothing()) {
9968 0 : fileHelper.emplace(aFileManager);
9969 :
9970 0 : rv = fileHelper->Init();
9971 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9972 0 : return rv;
9973 : }
9974 : }
9975 :
9976 0 : MOZ_ASSERT(aResult.Length() > 1);
9977 0 : MOZ_ASSERT(aResult[aResult.Length() - 2].mType ==
9978 : StructuredCloneFile::eWasmBytecode);
9979 :
9980 0 : StructuredCloneFile* previousFile = &aResult[aResult.Length() - 2];
9981 :
9982 0 : rv = CheckWasmModule(fileHelper.ptr(), previousFile, file);
9983 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9984 0 : return rv;
9985 : }
9986 :
9987 0 : *aHasPreprocessInfo = true;
9988 : }
9989 : }
9990 :
9991 0 : return NS_OK;
9992 : }
9993 :
9994 : bool
9995 0 : GetBaseFilename(const nsAString& aFilename,
9996 : const nsAString& aSuffix,
9997 : nsDependentSubstring& aBaseFilename)
9998 : {
9999 0 : MOZ_ASSERT(!aFilename.IsEmpty());
10000 0 : MOZ_ASSERT(aBaseFilename.IsEmpty());
10001 :
10002 0 : if (!StringEndsWith(aFilename, aSuffix) ||
10003 0 : aFilename.Length() == aSuffix.Length()) {
10004 0 : return false;
10005 : }
10006 :
10007 0 : MOZ_ASSERT(aFilename.Length() > aSuffix.Length());
10008 :
10009 0 : aBaseFilename.Rebind(aFilename, 0, aFilename.Length() - aSuffix.Length());
10010 0 : return true;
10011 : }
10012 :
10013 : nsresult
10014 0 : SerializeStructuredCloneFiles(
10015 : PBackgroundParent* aBackgroundActor,
10016 : Database* aDatabase,
10017 : const nsTArray<StructuredCloneFile>& aFiles,
10018 : bool aForPreprocess,
10019 : FallibleTArray<SerializedStructuredCloneFile>& aResult)
10020 : {
10021 0 : AssertIsOnBackgroundThread();
10022 0 : MOZ_ASSERT(aBackgroundActor);
10023 0 : MOZ_ASSERT(aDatabase);
10024 0 : MOZ_ASSERT(aResult.IsEmpty());
10025 :
10026 0 : if (aFiles.IsEmpty()) {
10027 0 : return NS_OK;
10028 : }
10029 :
10030 0 : FileManager* fileManager = aDatabase->GetFileManager();
10031 :
10032 0 : nsCOMPtr<nsIFile> directory = fileManager->GetCheckedDirectory();
10033 0 : if (NS_WARN_IF(!directory)) {
10034 0 : IDB_REPORT_INTERNAL_ERR();
10035 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
10036 : }
10037 :
10038 0 : const uint32_t count = aFiles.Length();
10039 :
10040 0 : if (NS_WARN_IF(!aResult.SetCapacity(count, fallible))) {
10041 0 : return NS_ERROR_OUT_OF_MEMORY;
10042 : }
10043 :
10044 0 : for (uint32_t index = 0; index < count; index++) {
10045 0 : const StructuredCloneFile& file = aFiles[index];
10046 :
10047 0 : if (aForPreprocess &&
10048 0 : file.mType != StructuredCloneFile::eWasmBytecode &&
10049 0 : file.mType != StructuredCloneFile::eWasmCompiled) {
10050 0 : continue;
10051 : }
10052 :
10053 0 : const int64_t fileId = file.mFileInfo->Id();
10054 0 : MOZ_ASSERT(fileId > 0);
10055 :
10056 : nsCOMPtr<nsIFile> nativeFile =
10057 0 : fileManager->GetCheckedFileForId(directory, fileId);
10058 0 : if (NS_WARN_IF(!nativeFile)) {
10059 0 : IDB_REPORT_INTERNAL_ERR();
10060 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
10061 : }
10062 :
10063 0 : switch (file.mType) {
10064 : case StructuredCloneFile::eBlob: {
10065 0 : RefPtr<FileBlobImpl> impl = new FileBlobImpl(nativeFile);
10066 0 : impl->SetFileId(file.mFileInfo->Id());
10067 :
10068 0 : IPCBlob ipcBlob;
10069 0 : nsresult rv = IPCBlobUtils::Serialize(impl, aBackgroundActor, ipcBlob);
10070 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10071 : // This can only fail if the child has crashed.
10072 0 : IDB_REPORT_INTERNAL_ERR();
10073 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
10074 : }
10075 :
10076 : SerializedStructuredCloneFile* serializedFile =
10077 0 : aResult.AppendElement(fallible);
10078 0 : MOZ_ASSERT(serializedFile);
10079 :
10080 0 : serializedFile->file() = ipcBlob;
10081 0 : serializedFile->type() = StructuredCloneFile::eBlob;
10082 :
10083 0 : aDatabase->MapBlob(ipcBlob, file.mFileInfo);
10084 0 : break;
10085 : }
10086 :
10087 : case StructuredCloneFile::eMutableFile: {
10088 0 : if (aDatabase->IsFileHandleDisabled()) {
10089 0 : SerializedStructuredCloneFile* file = aResult.AppendElement(fallible);
10090 0 : MOZ_ASSERT(file);
10091 :
10092 0 : file->file() = null_t();
10093 0 : file->type() = StructuredCloneFile::eMutableFile;
10094 : } else {
10095 : RefPtr<MutableFile> actor =
10096 0 : MutableFile::Create(nativeFile, aDatabase, file.mFileInfo);
10097 0 : if (!actor) {
10098 0 : IDB_REPORT_INTERNAL_ERR();
10099 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
10100 : }
10101 :
10102 : // Transfer ownership to IPDL.
10103 0 : actor->SetActorAlive();
10104 :
10105 0 : if (!aDatabase->SendPBackgroundMutableFileConstructor(actor,
10106 : EmptyString(),
10107 0 : EmptyString())) {
10108 : // This can only fail if the child has crashed.
10109 0 : IDB_REPORT_INTERNAL_ERR();
10110 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
10111 : }
10112 :
10113 0 : SerializedStructuredCloneFile* file = aResult.AppendElement(fallible);
10114 0 : MOZ_ASSERT(file);
10115 :
10116 0 : file->file() = actor;
10117 0 : file->type() = StructuredCloneFile::eMutableFile;
10118 : }
10119 :
10120 0 : break;
10121 : }
10122 :
10123 : case StructuredCloneFile::eStructuredClone: {
10124 0 : SerializedStructuredCloneFile* file = aResult.AppendElement(fallible);
10125 0 : MOZ_ASSERT(file);
10126 :
10127 0 : file->file() = null_t();
10128 0 : file->type() = StructuredCloneFile::eStructuredClone;
10129 :
10130 0 : break;
10131 : }
10132 :
10133 : case StructuredCloneFile::eWasmBytecode:
10134 : case StructuredCloneFile::eWasmCompiled: {
10135 0 : if (!aForPreprocess) {
10136 : SerializedStructuredCloneFile* serializedFile =
10137 0 : aResult.AppendElement(fallible);
10138 0 : MOZ_ASSERT(serializedFile);
10139 :
10140 0 : serializedFile->file() = null_t();
10141 0 : serializedFile->type() = file.mType;
10142 : } else {
10143 0 : RefPtr<FileBlobImpl> impl = new FileBlobImpl(nativeFile);
10144 0 : impl->SetFileId(file.mFileInfo->Id());
10145 :
10146 0 : IPCBlob ipcBlob;
10147 : nsresult rv =
10148 0 : IPCBlobUtils::Serialize(impl, aBackgroundActor, ipcBlob);
10149 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10150 : // This can only fail if the child has crashed.
10151 0 : IDB_REPORT_INTERNAL_ERR();
10152 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
10153 : }
10154 :
10155 : SerializedStructuredCloneFile* serializedFile =
10156 0 : aResult.AppendElement(fallible);
10157 0 : MOZ_ASSERT(serializedFile);
10158 :
10159 0 : serializedFile->file() = ipcBlob;
10160 0 : serializedFile->type() = file.mType;
10161 :
10162 0 : aDatabase->MapBlob(ipcBlob, file.mFileInfo);
10163 : }
10164 :
10165 0 : break;
10166 : }
10167 :
10168 : default:
10169 0 : MOZ_CRASH("Should never get here!");
10170 : }
10171 : }
10172 :
10173 0 : return NS_OK;
10174 : }
10175 :
10176 : already_AddRefed<nsIFile>
10177 0 : GetFileForFileInfo(FileInfo* aFileInfo)
10178 : {
10179 0 : FileManager* fileManager = aFileInfo->Manager();
10180 0 : nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
10181 0 : if (NS_WARN_IF(!directory)) {
10182 0 : return nullptr;
10183 : }
10184 :
10185 0 : nsCOMPtr<nsIFile> file = fileManager->GetFileForId(directory,
10186 0 : aFileInfo->Id());
10187 0 : if (NS_WARN_IF(!file)) {
10188 0 : return nullptr;
10189 : }
10190 :
10191 0 : return file.forget();
10192 : }
10193 :
10194 : /*******************************************************************************
10195 : * Globals
10196 : ******************************************************************************/
10197 :
10198 : // Counts the number of "live" Factory, FactoryOp and Database instances.
10199 : uint64_t gBusyCount = 0;
10200 :
10201 : typedef nsTArray<RefPtr<FactoryOp>> FactoryOpArray;
10202 :
10203 3 : StaticAutoPtr<FactoryOpArray> gFactoryOps;
10204 :
10205 : // Maps a database id to information about live database actors.
10206 : typedef nsClassHashtable<nsCStringHashKey, DatabaseActorInfo>
10207 : DatabaseActorHashtable;
10208 :
10209 3 : StaticAutoPtr<DatabaseActorHashtable> gLiveDatabaseHashtable;
10210 :
10211 3 : StaticRefPtr<ConnectionPool> gConnectionPool;
10212 :
10213 3 : StaticRefPtr<FileHandleThreadPool> gFileHandleThreadPool;
10214 :
10215 : typedef nsDataHashtable<nsIDHashKey, DatabaseLoggingInfo*>
10216 : DatabaseLoggingInfoHashtable;
10217 :
10218 3 : StaticAutoPtr<DatabaseLoggingInfoHashtable> gLoggingInfoHashtable;
10219 :
10220 : typedef nsDataHashtable<nsUint32HashKey, uint32_t> TelemetryIdHashtable;
10221 :
10222 3 : StaticAutoPtr<TelemetryIdHashtable> gTelemetryIdHashtable;
10223 :
10224 : // Protects all reads and writes to gTelemetryIdHashtable.
10225 3 : StaticAutoPtr<Mutex> gTelemetryIdMutex;
10226 :
10227 : #ifdef DEBUG
10228 :
10229 3 : StaticRefPtr<DEBUGThreadSlower> gDEBUGThreadSlower;
10230 :
10231 : #endif // DEBUG
10232 :
10233 :
10234 : void
10235 0 : IncreaseBusyCount()
10236 : {
10237 0 : AssertIsOnBackgroundThread();
10238 :
10239 : // If this is the first instance then we need to do some initialization.
10240 0 : if (!gBusyCount) {
10241 0 : MOZ_ASSERT(!gFactoryOps);
10242 0 : gFactoryOps = new FactoryOpArray();
10243 :
10244 0 : MOZ_ASSERT(!gLiveDatabaseHashtable);
10245 0 : gLiveDatabaseHashtable = new DatabaseActorHashtable();
10246 :
10247 0 : MOZ_ASSERT(!gLoggingInfoHashtable);
10248 0 : gLoggingInfoHashtable = new DatabaseLoggingInfoHashtable();
10249 :
10250 : #ifdef DEBUG
10251 : if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) {
10252 : NS_WARNING("PBackground thread debugging enabled, priority has been "
10253 : "modified!");
10254 : nsCOMPtr<nsISupportsPriority> thread =
10255 : do_QueryInterface(NS_GetCurrentThread());
10256 : MOZ_ASSERT(thread);
10257 :
10258 : MOZ_ALWAYS_SUCCEEDS(thread->SetPriority(kDEBUGThreadPriority));
10259 : }
10260 :
10261 : if (kDEBUGThreadSleepMS) {
10262 : NS_WARNING("PBackground thread debugging enabled, sleeping after every "
10263 : "event!");
10264 : nsCOMPtr<nsIThreadInternal> thread =
10265 : do_QueryInterface(NS_GetCurrentThread());
10266 : MOZ_ASSERT(thread);
10267 :
10268 : gDEBUGThreadSlower = new DEBUGThreadSlower();
10269 :
10270 : MOZ_ALWAYS_SUCCEEDS(thread->AddObserver(gDEBUGThreadSlower));
10271 : }
10272 : #endif // DEBUG
10273 : }
10274 :
10275 0 : gBusyCount++;
10276 0 : }
10277 :
10278 : void
10279 0 : DecreaseBusyCount()
10280 : {
10281 0 : AssertIsOnBackgroundThread();
10282 0 : MOZ_ASSERT(gBusyCount);
10283 :
10284 : // Clean up if there are no more instances.
10285 0 : if (--gBusyCount == 0) {
10286 0 : MOZ_ASSERT(gLoggingInfoHashtable);
10287 0 : gLoggingInfoHashtable = nullptr;
10288 :
10289 0 : MOZ_ASSERT(gLiveDatabaseHashtable);
10290 0 : MOZ_ASSERT(!gLiveDatabaseHashtable->Count());
10291 0 : gLiveDatabaseHashtable = nullptr;
10292 :
10293 0 : MOZ_ASSERT(gFactoryOps);
10294 0 : MOZ_ASSERT(gFactoryOps->IsEmpty());
10295 0 : gFactoryOps = nullptr;
10296 :
10297 : #ifdef DEBUG
10298 : if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) {
10299 : nsCOMPtr<nsISupportsPriority> thread =
10300 : do_QueryInterface(NS_GetCurrentThread());
10301 : MOZ_ASSERT(thread);
10302 :
10303 : MOZ_ALWAYS_SUCCEEDS(
10304 : thread->SetPriority(nsISupportsPriority::PRIORITY_NORMAL));
10305 : }
10306 :
10307 : if (kDEBUGThreadSleepMS) {
10308 : MOZ_ASSERT(gDEBUGThreadSlower);
10309 :
10310 : nsCOMPtr<nsIThreadInternal> thread =
10311 : do_QueryInterface(NS_GetCurrentThread());
10312 : MOZ_ASSERT(thread);
10313 :
10314 : MOZ_ALWAYS_SUCCEEDS(thread->RemoveObserver(gDEBUGThreadSlower));
10315 :
10316 : gDEBUGThreadSlower = nullptr;
10317 : }
10318 : #endif // DEBUG
10319 : }
10320 0 : }
10321 :
10322 : uint32_t
10323 0 : TelemetryIdForFile(nsIFile* aFile)
10324 : {
10325 : // May be called on any thread!
10326 :
10327 0 : MOZ_ASSERT(aFile);
10328 0 : MOZ_ASSERT(gTelemetryIdMutex);
10329 :
10330 : // The storage directory is structured like this:
10331 : //
10332 : // <profile>/storage/<persistence>/<origin>/idb/<filename>.sqlite
10333 : //
10334 : // For the purposes of this function we're only concerned with the
10335 : // <persistence>, <origin>, and <filename> pieces.
10336 :
10337 0 : nsString filename;
10338 0 : MOZ_ALWAYS_SUCCEEDS(aFile->GetLeafName(filename));
10339 :
10340 : // Make sure we were given a database file.
10341 0 : NS_NAMED_LITERAL_STRING(sqliteExtension, ".sqlite");
10342 :
10343 0 : MOZ_ASSERT(StringEndsWith(filename, sqliteExtension));
10344 :
10345 0 : filename.Truncate(filename.Length() - sqliteExtension.Length());
10346 :
10347 : // Get the "idb" directory.
10348 0 : nsCOMPtr<nsIFile> idbDirectory;
10349 0 : MOZ_ALWAYS_SUCCEEDS(aFile->GetParent(getter_AddRefs(idbDirectory)));
10350 :
10351 0 : DebugOnly<nsString> idbLeafName;
10352 0 : MOZ_ASSERT(NS_SUCCEEDED(idbDirectory->GetLeafName(idbLeafName)));
10353 0 : MOZ_ASSERT(static_cast<nsString&>(idbLeafName).EqualsLiteral("idb"));
10354 :
10355 : // Get the <origin> directory.
10356 0 : nsCOMPtr<nsIFile> originDirectory;
10357 0 : MOZ_ALWAYS_SUCCEEDS(
10358 : idbDirectory->GetParent(getter_AddRefs(originDirectory)));
10359 :
10360 0 : nsString origin;
10361 0 : MOZ_ALWAYS_SUCCEEDS(originDirectory->GetLeafName(origin));
10362 :
10363 : // Any databases in these directories are owned by the application and should
10364 : // not have their filenames masked. Hopefully they also appear in the
10365 : // Telemetry.cpp whitelist.
10366 0 : if (origin.EqualsLiteral("chrome") ||
10367 0 : origin.EqualsLiteral("moz-safe-about+home")) {
10368 0 : return 0;
10369 : }
10370 :
10371 : // Get the <persistence> directory.
10372 0 : nsCOMPtr<nsIFile> persistenceDirectory;
10373 0 : MOZ_ALWAYS_SUCCEEDS(
10374 : originDirectory->GetParent(getter_AddRefs(persistenceDirectory)));
10375 :
10376 0 : nsString persistence;
10377 0 : MOZ_ALWAYS_SUCCEEDS(persistenceDirectory->GetLeafName(persistence));
10378 :
10379 0 : NS_NAMED_LITERAL_STRING(separator, "*");
10380 :
10381 0 : uint32_t hashValue = HashString(persistence + separator +
10382 0 : origin + separator +
10383 0 : filename);
10384 :
10385 0 : MutexAutoLock lock(*gTelemetryIdMutex);
10386 :
10387 0 : if (!gTelemetryIdHashtable) {
10388 0 : gTelemetryIdHashtable = new TelemetryIdHashtable();
10389 : }
10390 :
10391 : uint32_t id;
10392 0 : if (!gTelemetryIdHashtable->Get(hashValue, &id)) {
10393 : static uint32_t sNextId = 1;
10394 :
10395 : // We're locked, no need for atomics.
10396 0 : id = sNextId++;
10397 :
10398 0 : gTelemetryIdHashtable->Put(hashValue, id);
10399 : }
10400 :
10401 0 : return id;
10402 : }
10403 :
10404 : } // namespace
10405 :
10406 : /*******************************************************************************
10407 : * Exported functions
10408 : ******************************************************************************/
10409 :
10410 : PBackgroundIDBFactoryParent*
10411 0 : AllocPBackgroundIDBFactoryParent(const LoggingInfo& aLoggingInfo)
10412 : {
10413 0 : AssertIsOnBackgroundThread();
10414 :
10415 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
10416 0 : return nullptr;
10417 : }
10418 :
10419 0 : if (NS_WARN_IF(!aLoggingInfo.nextTransactionSerialNumber()) ||
10420 0 : NS_WARN_IF(!aLoggingInfo.nextVersionChangeTransactionSerialNumber()) ||
10421 0 : NS_WARN_IF(!aLoggingInfo.nextRequestSerialNumber())) {
10422 0 : ASSERT_UNLESS_FUZZING();
10423 : return nullptr;
10424 : }
10425 :
10426 0 : RefPtr<Factory> actor = Factory::Create(aLoggingInfo);
10427 0 : MOZ_ASSERT(actor);
10428 :
10429 0 : return actor.forget().take();
10430 : }
10431 :
10432 : bool
10433 0 : RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent* aActor,
10434 : const LoggingInfo& /* aLoggingInfo */)
10435 : {
10436 0 : AssertIsOnBackgroundThread();
10437 0 : MOZ_ASSERT(aActor);
10438 0 : MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
10439 :
10440 0 : return true;
10441 : }
10442 :
10443 : bool
10444 0 : DeallocPBackgroundIDBFactoryParent(PBackgroundIDBFactoryParent* aActor)
10445 : {
10446 0 : AssertIsOnBackgroundThread();
10447 0 : MOZ_ASSERT(aActor);
10448 :
10449 0 : RefPtr<Factory> actor = dont_AddRef(static_cast<Factory*>(aActor));
10450 0 : return true;
10451 : }
10452 :
10453 : PBackgroundIndexedDBUtilsParent*
10454 0 : AllocPBackgroundIndexedDBUtilsParent()
10455 : {
10456 0 : AssertIsOnBackgroundThread();
10457 :
10458 0 : RefPtr<Utils> actor = new Utils();
10459 :
10460 0 : return actor.forget().take();
10461 : }
10462 :
10463 : bool
10464 0 : DeallocPBackgroundIndexedDBUtilsParent(PBackgroundIndexedDBUtilsParent* aActor)
10465 : {
10466 0 : AssertIsOnBackgroundThread();
10467 0 : MOZ_ASSERT(aActor);
10468 :
10469 0 : RefPtr<Utils> actor = dont_AddRef(static_cast<Utils*>(aActor));
10470 0 : return true;
10471 : }
10472 :
10473 : bool
10474 0 : RecvFlushPendingFileDeletions()
10475 : {
10476 0 : AssertIsOnBackgroundThread();
10477 :
10478 : RefPtr<FlushPendingFileDeletionsRunnable> runnable =
10479 0 : new FlushPendingFileDeletionsRunnable();
10480 :
10481 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
10482 :
10483 0 : return true;
10484 : }
10485 :
10486 : PIndexedDBPermissionRequestParent*
10487 0 : AllocPIndexedDBPermissionRequestParent(Element* aOwnerElement,
10488 : nsIPrincipal* aPrincipal)
10489 : {
10490 0 : MOZ_ASSERT(NS_IsMainThread());
10491 :
10492 : RefPtr<PermissionRequestHelper> actor =
10493 0 : new PermissionRequestHelper(aOwnerElement, aPrincipal);
10494 0 : return actor.forget().take();
10495 : }
10496 :
10497 : bool
10498 0 : RecvPIndexedDBPermissionRequestConstructor(
10499 : PIndexedDBPermissionRequestParent* aActor)
10500 : {
10501 0 : MOZ_ASSERT(NS_IsMainThread());
10502 0 : MOZ_ASSERT(aActor);
10503 :
10504 0 : auto* actor = static_cast<PermissionRequestHelper*>(aActor);
10505 :
10506 : PermissionRequestBase::PermissionValue permission;
10507 0 : nsresult rv = actor->PromptIfNeeded(&permission);
10508 0 : if (NS_FAILED(rv)) {
10509 0 : return false;
10510 : }
10511 :
10512 0 : if (permission != PermissionRequestBase::kPermissionPrompt) {
10513 : Unused <<
10514 0 : PIndexedDBPermissionRequestParent::Send__delete__(actor, permission);
10515 : }
10516 :
10517 0 : return true;
10518 : }
10519 :
10520 : bool
10521 0 : DeallocPIndexedDBPermissionRequestParent(
10522 : PIndexedDBPermissionRequestParent* aActor)
10523 : {
10524 0 : MOZ_ASSERT(NS_IsMainThread());
10525 0 : MOZ_ASSERT(aActor);
10526 :
10527 : RefPtr<PermissionRequestHelper> actor =
10528 0 : dont_AddRef(static_cast<PermissionRequestHelper*>(aActor));
10529 0 : return true;
10530 : }
10531 :
10532 : already_AddRefed<mozilla::dom::quota::Client>
10533 0 : CreateQuotaClient()
10534 : {
10535 0 : AssertIsOnBackgroundThread();
10536 :
10537 0 : RefPtr<QuotaClient> client = new QuotaClient();
10538 0 : return client.forget();
10539 : }
10540 :
10541 : FileHandleThreadPool*
10542 0 : GetFileHandleThreadPool()
10543 : {
10544 0 : AssertIsOnBackgroundThread();
10545 :
10546 0 : if (!gFileHandleThreadPool) {
10547 : RefPtr<FileHandleThreadPool> fileHandleThreadPool =
10548 0 : FileHandleThreadPool::Create();
10549 0 : if (NS_WARN_IF(!fileHandleThreadPool)) {
10550 0 : return nullptr;
10551 : }
10552 :
10553 0 : gFileHandleThreadPool = fileHandleThreadPool;
10554 : }
10555 :
10556 0 : return gFileHandleThreadPool;
10557 : }
10558 :
10559 : /*******************************************************************************
10560 : * DatabaseConnection implementation
10561 : ******************************************************************************/
10562 :
10563 0 : DatabaseConnection::DatabaseConnection(
10564 : mozIStorageConnection* aStorageConnection,
10565 0 : FileManager* aFileManager)
10566 : : mStorageConnection(aStorageConnection)
10567 : , mFileManager(aFileManager)
10568 : , mInReadTransaction(false)
10569 : , mInWriteTransaction(false)
10570 : #ifdef DEBUG
10571 0 : , mDEBUGSavepointCount(0)
10572 : #endif
10573 : {
10574 0 : AssertIsOnConnectionThread();
10575 0 : MOZ_ASSERT(aStorageConnection);
10576 0 : MOZ_ASSERT(aFileManager);
10577 0 : }
10578 :
10579 0 : DatabaseConnection::~DatabaseConnection()
10580 : {
10581 0 : MOZ_ASSERT(!mStorageConnection);
10582 0 : MOZ_ASSERT(!mFileManager);
10583 0 : MOZ_ASSERT(!mCachedStatements.Count());
10584 0 : MOZ_ASSERT(!mUpdateRefcountFunction);
10585 0 : MOZ_ASSERT(!mInWriteTransaction);
10586 0 : MOZ_ASSERT(!mDEBUGSavepointCount);
10587 0 : }
10588 :
10589 : nsresult
10590 0 : DatabaseConnection::Init()
10591 : {
10592 0 : AssertIsOnConnectionThread();
10593 0 : MOZ_ASSERT(!mInReadTransaction);
10594 0 : MOZ_ASSERT(!mInWriteTransaction);
10595 :
10596 0 : CachedStatement stmt;
10597 0 : nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN;"), &stmt);
10598 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10599 0 : return rv;
10600 : }
10601 :
10602 0 : rv = stmt->Execute();
10603 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10604 0 : return rv;
10605 : }
10606 :
10607 0 : mInReadTransaction = true;
10608 :
10609 0 : return NS_OK;
10610 : }
10611 :
10612 : nsresult
10613 0 : DatabaseConnection::GetCachedStatement(const nsACString& aQuery,
10614 : CachedStatement* aCachedStatement)
10615 : {
10616 0 : AssertIsOnConnectionThread();
10617 0 : MOZ_ASSERT(!aQuery.IsEmpty());
10618 0 : MOZ_ASSERT(aCachedStatement);
10619 0 : MOZ_ASSERT(mStorageConnection);
10620 :
10621 0 : AUTO_PROFILER_LABEL("DatabaseConnection::GetCachedStatement", STORAGE);
10622 :
10623 0 : nsCOMPtr<mozIStorageStatement> stmt;
10624 :
10625 0 : if (!mCachedStatements.Get(aQuery, getter_AddRefs(stmt))) {
10626 : nsresult rv =
10627 0 : mStorageConnection->CreateStatement(aQuery, getter_AddRefs(stmt));
10628 0 : if (NS_FAILED(rv)) {
10629 : #ifdef DEBUG
10630 0 : nsCString msg;
10631 0 : MOZ_ALWAYS_SUCCEEDS(mStorageConnection->GetLastErrorString(msg));
10632 :
10633 : nsAutoCString error =
10634 0 : NS_LITERAL_CSTRING("The statement '") + aQuery +
10635 0 : NS_LITERAL_CSTRING("' failed to compile with the error message '") +
10636 0 : msg + NS_LITERAL_CSTRING("'.");
10637 :
10638 0 : NS_WARNING(error.get());
10639 : #endif
10640 0 : return rv;
10641 : }
10642 :
10643 0 : mCachedStatements.Put(aQuery, stmt);
10644 : }
10645 :
10646 0 : aCachedStatement->Assign(this, stmt.forget());
10647 0 : return NS_OK;
10648 : }
10649 :
10650 : nsresult
10651 0 : DatabaseConnection::BeginWriteTransaction()
10652 : {
10653 0 : AssertIsOnConnectionThread();
10654 0 : MOZ_ASSERT(mStorageConnection);
10655 0 : MOZ_ASSERT(mInReadTransaction);
10656 0 : MOZ_ASSERT(!mInWriteTransaction);
10657 :
10658 0 : AUTO_PROFILER_LABEL("DatabaseConnection::BeginWriteTransaction", STORAGE);
10659 :
10660 : // Release our read locks.
10661 0 : CachedStatement rollbackStmt;
10662 : nsresult rv =
10663 0 : GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK;"), &rollbackStmt);
10664 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10665 0 : return rv;
10666 : }
10667 :
10668 0 : rv = rollbackStmt->Execute();
10669 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10670 0 : return rv;
10671 : }
10672 :
10673 0 : mInReadTransaction = false;
10674 :
10675 0 : if (!mUpdateRefcountFunction) {
10676 0 : MOZ_ASSERT(mFileManager);
10677 :
10678 : RefPtr<UpdateRefcountFunction> function =
10679 0 : new UpdateRefcountFunction(this, mFileManager);
10680 :
10681 : rv =
10682 0 : mStorageConnection->CreateFunction(NS_LITERAL_CSTRING("update_refcount"),
10683 : /* aNumArguments */ 2,
10684 0 : function);
10685 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10686 0 : return rv;
10687 : }
10688 :
10689 0 : mUpdateRefcountFunction.swap(function);
10690 : }
10691 :
10692 0 : CachedStatement beginStmt;
10693 0 : rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN IMMEDIATE;"), &beginStmt);
10694 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10695 0 : return rv;
10696 : }
10697 :
10698 0 : rv = beginStmt->Execute();
10699 0 : if (rv == NS_ERROR_STORAGE_BUSY) {
10700 : NS_WARNING("Received NS_ERROR_STORAGE_BUSY when attempting to start write "
10701 0 : "transaction, retrying for up to 10 seconds");
10702 :
10703 : // Another thread must be using the database. Wait up to 10 seconds for
10704 : // that to complete.
10705 0 : TimeStamp start = TimeStamp::NowLoRes();
10706 :
10707 : while (true) {
10708 0 : PR_Sleep(PR_MillisecondsToInterval(100));
10709 :
10710 0 : rv = beginStmt->Execute();
10711 0 : if (rv != NS_ERROR_STORAGE_BUSY ||
10712 0 : TimeStamp::NowLoRes() - start > TimeDuration::FromSeconds(10)) {
10713 0 : break;
10714 : }
10715 : }
10716 : }
10717 :
10718 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10719 0 : return rv;
10720 : }
10721 :
10722 0 : mInWriteTransaction = true;
10723 :
10724 0 : return NS_OK;
10725 : }
10726 :
10727 : nsresult
10728 0 : DatabaseConnection::CommitWriteTransaction()
10729 : {
10730 0 : AssertIsOnConnectionThread();
10731 0 : MOZ_ASSERT(mStorageConnection);
10732 0 : MOZ_ASSERT(!mInReadTransaction);
10733 0 : MOZ_ASSERT(mInWriteTransaction);
10734 :
10735 0 : AUTO_PROFILER_LABEL("DatabaseConnection::CommitWriteTransaction", STORAGE);
10736 :
10737 0 : CachedStatement stmt;
10738 0 : nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("COMMIT;"), &stmt);
10739 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10740 0 : return rv;
10741 : }
10742 :
10743 0 : rv = stmt->Execute();
10744 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10745 0 : return rv;
10746 : }
10747 :
10748 0 : mInWriteTransaction = false;
10749 0 : return NS_OK;
10750 : }
10751 :
10752 : void
10753 0 : DatabaseConnection::RollbackWriteTransaction()
10754 : {
10755 0 : AssertIsOnConnectionThread();
10756 0 : MOZ_ASSERT(!mInReadTransaction);
10757 0 : MOZ_ASSERT(mStorageConnection);
10758 :
10759 0 : AUTO_PROFILER_LABEL("DatabaseConnection::RollbackWriteTransaction", STORAGE);
10760 :
10761 0 : if (!mInWriteTransaction) {
10762 0 : return;
10763 : }
10764 :
10765 0 : DatabaseConnection::CachedStatement stmt;
10766 0 : nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK;"), &stmt);
10767 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10768 0 : return;
10769 : }
10770 :
10771 : // This may fail if SQLite already rolled back the transaction so ignore any
10772 : // errors.
10773 0 : Unused << stmt->Execute();
10774 :
10775 0 : mInWriteTransaction = false;
10776 : }
10777 :
10778 : void
10779 0 : DatabaseConnection::FinishWriteTransaction()
10780 : {
10781 0 : AssertIsOnConnectionThread();
10782 0 : MOZ_ASSERT(mStorageConnection);
10783 0 : MOZ_ASSERT(!mInReadTransaction);
10784 0 : MOZ_ASSERT(!mInWriteTransaction);
10785 :
10786 0 : AUTO_PROFILER_LABEL("DatabaseConnection::FinishWriteTransaction", STORAGE);
10787 :
10788 0 : if (mUpdateRefcountFunction) {
10789 0 : mUpdateRefcountFunction->Reset();
10790 : }
10791 :
10792 0 : CachedStatement stmt;
10793 0 : nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN;"), &stmt);
10794 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10795 0 : return;
10796 : }
10797 :
10798 0 : rv = stmt->Execute();
10799 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10800 0 : return;
10801 : }
10802 :
10803 0 : mInReadTransaction = true;
10804 : }
10805 :
10806 : nsresult
10807 0 : DatabaseConnection::StartSavepoint()
10808 : {
10809 0 : AssertIsOnConnectionThread();
10810 0 : MOZ_ASSERT(mStorageConnection);
10811 0 : MOZ_ASSERT(mUpdateRefcountFunction);
10812 0 : MOZ_ASSERT(mInWriteTransaction);
10813 :
10814 0 : AUTO_PROFILER_LABEL("DatabaseConnection::StartSavepoint", STORAGE);
10815 :
10816 0 : CachedStatement stmt;
10817 0 : nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING(SAVEPOINT_CLAUSE), &stmt);
10818 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10819 0 : return rv;
10820 : }
10821 :
10822 0 : rv = stmt->Execute();
10823 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10824 0 : return rv;
10825 : }
10826 :
10827 0 : mUpdateRefcountFunction->StartSavepoint();
10828 :
10829 : #ifdef DEBUG
10830 0 : MOZ_ASSERT(mDEBUGSavepointCount < UINT32_MAX);
10831 0 : mDEBUGSavepointCount++;
10832 : #endif
10833 :
10834 0 : return NS_OK;
10835 : }
10836 :
10837 : nsresult
10838 0 : DatabaseConnection::ReleaseSavepoint()
10839 : {
10840 0 : AssertIsOnConnectionThread();
10841 0 : MOZ_ASSERT(mStorageConnection);
10842 0 : MOZ_ASSERT(mUpdateRefcountFunction);
10843 0 : MOZ_ASSERT(mInWriteTransaction);
10844 :
10845 0 : AUTO_PROFILER_LABEL("DatabaseConnection::ReleaseSavepoint", STORAGE);
10846 :
10847 0 : CachedStatement stmt;
10848 0 : nsresult rv = GetCachedStatement(
10849 0 : NS_LITERAL_CSTRING("RELEASE " SAVEPOINT_CLAUSE),
10850 0 : &stmt);
10851 0 : if (NS_SUCCEEDED(rv)) {
10852 0 : rv = stmt->Execute();
10853 0 : if (NS_SUCCEEDED(rv)) {
10854 0 : mUpdateRefcountFunction->ReleaseSavepoint();
10855 :
10856 : #ifdef DEBUG
10857 0 : MOZ_ASSERT(mDEBUGSavepointCount);
10858 0 : mDEBUGSavepointCount--;
10859 : #endif
10860 : }
10861 : }
10862 :
10863 0 : return rv;
10864 : }
10865 :
10866 : nsresult
10867 0 : DatabaseConnection::RollbackSavepoint()
10868 : {
10869 0 : AssertIsOnConnectionThread();
10870 0 : MOZ_ASSERT(mStorageConnection);
10871 0 : MOZ_ASSERT(mUpdateRefcountFunction);
10872 0 : MOZ_ASSERT(mInWriteTransaction);
10873 :
10874 0 : AUTO_PROFILER_LABEL("DatabaseConnection::RollbackSavepoint", STORAGE);
10875 :
10876 : #ifdef DEBUG
10877 0 : MOZ_ASSERT(mDEBUGSavepointCount);
10878 0 : mDEBUGSavepointCount--;
10879 : #endif
10880 :
10881 0 : mUpdateRefcountFunction->RollbackSavepoint();
10882 :
10883 0 : CachedStatement stmt;
10884 0 : nsresult rv = GetCachedStatement(
10885 0 : NS_LITERAL_CSTRING("ROLLBACK TO " SAVEPOINT_CLAUSE),
10886 0 : &stmt);
10887 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10888 0 : return rv;
10889 : }
10890 :
10891 : // This may fail if SQLite already rolled back the savepoint so ignore any
10892 : // errors.
10893 0 : Unused << stmt->Execute();
10894 :
10895 0 : return NS_OK;
10896 : }
10897 :
10898 : nsresult
10899 0 : DatabaseConnection::CheckpointInternal(CheckpointMode aMode)
10900 : {
10901 0 : AssertIsOnConnectionThread();
10902 0 : MOZ_ASSERT(!mInReadTransaction);
10903 0 : MOZ_ASSERT(!mInWriteTransaction);
10904 :
10905 0 : AUTO_PROFILER_LABEL("DatabaseConnection::CheckpointInternal", STORAGE);
10906 :
10907 0 : nsAutoCString stmtString;
10908 0 : stmtString.AssignLiteral("PRAGMA wal_checkpoint(");
10909 :
10910 0 : switch (aMode) {
10911 : case CheckpointMode::Full:
10912 : // Ensures that the database is completely checkpointed and flushed to
10913 : // disk.
10914 0 : stmtString.AppendLiteral("FULL");
10915 0 : break;
10916 :
10917 : case CheckpointMode::Restart:
10918 : // Like Full, but also ensures that the next write will start overwriting
10919 : // the existing WAL file rather than letting the WAL file grow.
10920 0 : stmtString.AppendLiteral("RESTART");
10921 0 : break;
10922 :
10923 : case CheckpointMode::Truncate:
10924 : // Like Restart but also truncates the existing WAL file.
10925 0 : stmtString.AppendLiteral("TRUNCATE");
10926 0 : break;
10927 :
10928 : default:
10929 0 : MOZ_CRASH("Unknown CheckpointMode!");
10930 : }
10931 :
10932 0 : stmtString.AppendLiteral(");");
10933 :
10934 0 : CachedStatement stmt;
10935 0 : nsresult rv = GetCachedStatement(stmtString, &stmt);
10936 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10937 0 : return rv;
10938 : }
10939 :
10940 0 : rv = stmt->Execute();
10941 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10942 0 : return rv;
10943 : }
10944 :
10945 0 : return NS_OK;
10946 : }
10947 :
10948 : void
10949 0 : DatabaseConnection::DoIdleProcessing(bool aNeedsCheckpoint)
10950 : {
10951 0 : AssertIsOnConnectionThread();
10952 0 : MOZ_ASSERT(mInReadTransaction);
10953 0 : MOZ_ASSERT(!mInWriteTransaction);
10954 :
10955 0 : AUTO_PROFILER_LABEL("DatabaseConnection::DoIdleProcessing", STORAGE);
10956 :
10957 0 : DatabaseConnection::CachedStatement freelistStmt;
10958 : uint32_t freelistCount;
10959 0 : nsresult rv = GetFreelistCount(freelistStmt, &freelistCount);
10960 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10961 0 : freelistCount = 0;
10962 : }
10963 :
10964 0 : CachedStatement rollbackStmt;
10965 0 : CachedStatement beginStmt;
10966 0 : if (aNeedsCheckpoint || freelistCount) {
10967 0 : rv = GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK;"), &rollbackStmt);
10968 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10969 0 : return;
10970 : }
10971 :
10972 0 : rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN;"), &beginStmt);
10973 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10974 0 : return;
10975 : }
10976 :
10977 : // Release the connection's normal transaction. It's possible that it could
10978 : // fail, but that isn't a problem here.
10979 0 : Unused << rollbackStmt->Execute();
10980 :
10981 0 : mInReadTransaction = false;
10982 : }
10983 :
10984 0 : bool freedSomePages = false;
10985 :
10986 0 : if (freelistCount) {
10987 0 : rv = ReclaimFreePagesWhileIdle(freelistStmt,
10988 : rollbackStmt,
10989 : freelistCount,
10990 : aNeedsCheckpoint,
10991 0 : &freedSomePages);
10992 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
10993 0 : MOZ_ASSERT(!freedSomePages);
10994 : }
10995 :
10996 : // Make sure we didn't leave a transaction running.
10997 0 : MOZ_ASSERT(!mInReadTransaction);
10998 0 : MOZ_ASSERT(!mInWriteTransaction);
10999 : }
11000 :
11001 : // Truncate the WAL if we were asked to or if we managed to free some space.
11002 0 : if (aNeedsCheckpoint || freedSomePages) {
11003 0 : rv = CheckpointInternal(CheckpointMode::Truncate);
11004 0 : Unused << NS_WARN_IF(NS_FAILED(rv));
11005 : }
11006 :
11007 : // Finally try to restart the read transaction if we rolled it back earlier.
11008 0 : if (beginStmt) {
11009 0 : rv = beginStmt->Execute();
11010 0 : if (NS_SUCCEEDED(rv)) {
11011 0 : mInReadTransaction = true;
11012 : } else {
11013 0 : NS_WARNING("Falied to restart read transaction!");
11014 : }
11015 : }
11016 : }
11017 :
11018 : nsresult
11019 0 : DatabaseConnection::ReclaimFreePagesWhileIdle(
11020 : CachedStatement& aFreelistStatement,
11021 : CachedStatement& aRollbackStatement,
11022 : uint32_t aFreelistCount,
11023 : bool aNeedsCheckpoint,
11024 : bool* aFreedSomePages)
11025 : {
11026 0 : AssertIsOnConnectionThread();
11027 0 : MOZ_ASSERT(aFreelistStatement);
11028 0 : MOZ_ASSERT(aRollbackStatement);
11029 0 : MOZ_ASSERT(aFreelistCount);
11030 0 : MOZ_ASSERT(aFreedSomePages);
11031 0 : MOZ_ASSERT(!mInReadTransaction);
11032 0 : MOZ_ASSERT(!mInWriteTransaction);
11033 :
11034 0 : AUTO_PROFILER_LABEL("DatabaseConnection::ReclaimFreePagesWhileIdle", STORAGE);
11035 :
11036 : // Make sure we don't keep working if anything else needs this thread.
11037 0 : nsIThread* currentThread = NS_GetCurrentThread();
11038 0 : MOZ_ASSERT(currentThread);
11039 :
11040 0 : if (NS_HasPendingEvents(currentThread)) {
11041 0 : *aFreedSomePages = false;
11042 0 : return NS_OK;
11043 : }
11044 :
11045 : // Only try to free 10% at a time so that we can bail out if this connection
11046 : // suddenly becomes active or if the thread is needed otherwise.
11047 0 : nsAutoCString stmtString;
11048 0 : stmtString.AssignLiteral("PRAGMA incremental_vacuum(");
11049 0 : stmtString.AppendInt(std::max(uint64_t(1), uint64_t(aFreelistCount / 10)));
11050 0 : stmtString.AppendLiteral(");");
11051 :
11052 : // Make all the statements we'll need up front.
11053 0 : CachedStatement incrementalVacuumStmt;
11054 0 : nsresult rv = GetCachedStatement(stmtString, &incrementalVacuumStmt);
11055 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11056 0 : return rv;
11057 : }
11058 :
11059 0 : CachedStatement beginImmediateStmt;
11060 0 : rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN IMMEDIATE;"),
11061 0 : &beginImmediateStmt);
11062 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11063 0 : return rv;
11064 : }
11065 :
11066 0 : CachedStatement commitStmt;
11067 0 : rv = GetCachedStatement(NS_LITERAL_CSTRING("COMMIT;"), &commitStmt);
11068 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11069 0 : return rv;
11070 : }
11071 :
11072 0 : if (aNeedsCheckpoint) {
11073 : // Freeing pages is a journaled operation, so it will require additional WAL
11074 : // space. However, we're idle and are about to checkpoint anyway, so doing a
11075 : // RESTART checkpoint here should allow us to reuse any existing space.
11076 0 : rv = CheckpointInternal(CheckpointMode::Restart);
11077 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11078 0 : return rv;
11079 : }
11080 : }
11081 :
11082 : // Start the write transaction.
11083 0 : rv = beginImmediateStmt->Execute();
11084 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11085 0 : return rv;
11086 : }
11087 :
11088 0 : mInWriteTransaction = true;
11089 :
11090 0 : bool freedSomePages = false;
11091 :
11092 0 : while (aFreelistCount) {
11093 0 : if (NS_HasPendingEvents(currentThread)) {
11094 : // Something else wants to use the thread so roll back this transaction.
11095 : // It's ok if we never make progress here because the idle service should
11096 : // eventually reclaim this space.
11097 0 : rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
11098 0 : break;
11099 : }
11100 :
11101 0 : rv = incrementalVacuumStmt->Execute();
11102 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11103 0 : break;
11104 : }
11105 :
11106 0 : freedSomePages = true;
11107 :
11108 0 : rv = GetFreelistCount(aFreelistStatement, &aFreelistCount);
11109 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11110 0 : break;
11111 : }
11112 : }
11113 :
11114 0 : if (NS_SUCCEEDED(rv) && freedSomePages) {
11115 : // Commit the write transaction.
11116 0 : rv = commitStmt->Execute();
11117 0 : if (NS_SUCCEEDED(rv)) {
11118 0 : mInWriteTransaction = false;
11119 : } else {
11120 0 : NS_WARNING("Failed to commit!");
11121 : }
11122 : }
11123 :
11124 0 : if (NS_FAILED(rv)) {
11125 0 : MOZ_ASSERT(mInWriteTransaction);
11126 :
11127 : // Something failed, make sure we roll everything back.
11128 0 : Unused << aRollbackStatement->Execute();
11129 :
11130 0 : mInWriteTransaction = false;
11131 :
11132 0 : return rv;
11133 : }
11134 :
11135 0 : *aFreedSomePages = freedSomePages;
11136 0 : return NS_OK;
11137 : }
11138 :
11139 : nsresult
11140 0 : DatabaseConnection::GetFreelistCount(CachedStatement& aCachedStatement,
11141 : uint32_t* aFreelistCount)
11142 : {
11143 0 : AssertIsOnConnectionThread();
11144 0 : MOZ_ASSERT(aFreelistCount);
11145 :
11146 0 : AUTO_PROFILER_LABEL("DatabaseConnection::GetFreelistCount", STORAGE);
11147 :
11148 : nsresult rv;
11149 :
11150 0 : if (!aCachedStatement) {
11151 0 : rv = GetCachedStatement(NS_LITERAL_CSTRING("PRAGMA freelist_count;"),
11152 0 : &aCachedStatement);
11153 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11154 0 : return rv;
11155 : }
11156 : }
11157 :
11158 : bool hasResult;
11159 0 : rv = aCachedStatement->ExecuteStep(&hasResult);
11160 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11161 0 : return rv;
11162 : }
11163 :
11164 0 : MOZ_ASSERT(hasResult);
11165 :
11166 : // Make sure this statement is reset when leaving this function since we're
11167 : // not using the normal stack-based protection of CachedStatement.
11168 0 : mozStorageStatementScoper scoper(aCachedStatement);
11169 :
11170 : int32_t freelistCount;
11171 0 : rv = aCachedStatement->GetInt32(0, &freelistCount);
11172 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11173 0 : return rv;
11174 : }
11175 :
11176 0 : MOZ_ASSERT(freelistCount >= 0);
11177 :
11178 0 : *aFreelistCount = uint32_t(freelistCount);
11179 0 : return NS_OK;
11180 : }
11181 :
11182 : void
11183 0 : DatabaseConnection::Close()
11184 : {
11185 0 : AssertIsOnConnectionThread();
11186 0 : MOZ_ASSERT(mStorageConnection);
11187 0 : MOZ_ASSERT(!mDEBUGSavepointCount);
11188 0 : MOZ_ASSERT(!mInWriteTransaction);
11189 :
11190 0 : AUTO_PROFILER_LABEL("DatabaseConnection::Close", STORAGE);
11191 :
11192 0 : if (mUpdateRefcountFunction) {
11193 0 : MOZ_ALWAYS_SUCCEEDS(
11194 : mStorageConnection->RemoveFunction(
11195 : NS_LITERAL_CSTRING("update_refcount")));
11196 0 : mUpdateRefcountFunction = nullptr;
11197 : }
11198 :
11199 0 : mCachedStatements.Clear();
11200 :
11201 0 : MOZ_ALWAYS_SUCCEEDS(mStorageConnection->Close());
11202 0 : mStorageConnection = nullptr;
11203 :
11204 0 : mFileManager = nullptr;
11205 0 : }
11206 :
11207 : nsresult
11208 0 : DatabaseConnection::DisableQuotaChecks()
11209 : {
11210 0 : AssertIsOnConnectionThread();
11211 0 : MOZ_ASSERT(mStorageConnection);
11212 :
11213 0 : if (!mQuotaObject) {
11214 0 : MOZ_ASSERT(!mJournalQuotaObject);
11215 :
11216 0 : nsresult rv = mStorageConnection->GetQuotaObjects(
11217 0 : getter_AddRefs(mQuotaObject),
11218 0 : getter_AddRefs(mJournalQuotaObject));
11219 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11220 0 : return rv;
11221 : }
11222 :
11223 0 : MOZ_ASSERT(mQuotaObject);
11224 0 : MOZ_ASSERT(mJournalQuotaObject);
11225 : }
11226 :
11227 0 : mQuotaObject->DisableQuotaCheck();
11228 0 : mJournalQuotaObject->DisableQuotaCheck();
11229 :
11230 0 : return NS_OK;
11231 : }
11232 :
11233 : void
11234 0 : DatabaseConnection::EnableQuotaChecks()
11235 : {
11236 0 : AssertIsOnConnectionThread();
11237 0 : MOZ_ASSERT(mQuotaObject);
11238 0 : MOZ_ASSERT(mJournalQuotaObject);
11239 :
11240 0 : RefPtr<QuotaObject> quotaObject;
11241 0 : RefPtr<QuotaObject> journalQuotaObject;
11242 :
11243 0 : mQuotaObject.swap(quotaObject);
11244 0 : mJournalQuotaObject.swap(journalQuotaObject);
11245 :
11246 0 : quotaObject->EnableQuotaCheck();
11247 0 : journalQuotaObject->EnableQuotaCheck();
11248 :
11249 : int64_t fileSize;
11250 0 : nsresult rv = GetFileSize(quotaObject->Path(), &fileSize);
11251 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11252 0 : return;
11253 : }
11254 :
11255 : int64_t journalFileSize;
11256 0 : rv = GetFileSize(journalQuotaObject->Path(), &journalFileSize);
11257 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11258 0 : return;
11259 : }
11260 :
11261 : DebugOnly<bool> result =
11262 0 : journalQuotaObject->MaybeUpdateSize(journalFileSize, /* aTruncate */ true);
11263 0 : MOZ_ASSERT(result);
11264 :
11265 0 : result = quotaObject->MaybeUpdateSize(fileSize, /* aTruncate */ true);
11266 0 : MOZ_ASSERT(result);
11267 : }
11268 :
11269 : nsresult
11270 0 : DatabaseConnection::GetFileSize(const nsAString& aPath, int64_t* aResult)
11271 : {
11272 0 : MOZ_ASSERT(!aPath.IsEmpty());
11273 0 : MOZ_ASSERT(aResult);
11274 :
11275 : nsresult rv;
11276 0 : nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
11277 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11278 0 : return rv;
11279 : }
11280 :
11281 0 : rv = file->InitWithPath(aPath);
11282 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11283 0 : return rv;
11284 : }
11285 :
11286 : int64_t fileSize;
11287 :
11288 : bool exists;
11289 0 : rv = file->Exists(&exists);
11290 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11291 0 : return rv;
11292 : }
11293 :
11294 0 : if (exists) {
11295 0 : rv = file->GetFileSize(&fileSize);
11296 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11297 0 : return rv;
11298 : }
11299 : } else {
11300 0 : fileSize = 0;
11301 : }
11302 :
11303 0 : *aResult = fileSize;
11304 0 : return NS_OK;
11305 : }
11306 :
11307 0 : DatabaseConnection::
11308 0 : CachedStatement::CachedStatement()
11309 : #ifdef DEBUG
11310 0 : : mDEBUGConnection(nullptr)
11311 : #endif
11312 : {
11313 0 : AssertIsOnConnectionThread();
11314 :
11315 0 : MOZ_COUNT_CTOR(DatabaseConnection::CachedStatement);
11316 0 : }
11317 :
11318 0 : DatabaseConnection::
11319 0 : CachedStatement::~CachedStatement()
11320 : {
11321 0 : AssertIsOnConnectionThread();
11322 :
11323 0 : MOZ_COUNT_DTOR(DatabaseConnection::CachedStatement);
11324 0 : }
11325 :
11326 0 : DatabaseConnection::
11327 : CachedStatement::operator mozIStorageStatement*() const
11328 : {
11329 0 : AssertIsOnConnectionThread();
11330 :
11331 0 : return mStatement;
11332 : }
11333 :
11334 : mozIStorageStatement*
11335 0 : DatabaseConnection::
11336 : CachedStatement::operator->() const
11337 : {
11338 0 : AssertIsOnConnectionThread();
11339 0 : MOZ_ASSERT(mStatement);
11340 :
11341 0 : return mStatement;
11342 : }
11343 :
11344 : void
11345 0 : DatabaseConnection::
11346 : CachedStatement::Reset()
11347 : {
11348 0 : AssertIsOnConnectionThread();
11349 0 : MOZ_ASSERT_IF(mStatement, mScoper);
11350 :
11351 0 : if (mStatement) {
11352 0 : mScoper.reset();
11353 0 : mScoper.emplace(mStatement);
11354 : }
11355 0 : }
11356 :
11357 : void
11358 0 : DatabaseConnection::
11359 : CachedStatement::Assign(DatabaseConnection* aConnection,
11360 : already_AddRefed<mozIStorageStatement> aStatement)
11361 : {
11362 : #ifdef DEBUG
11363 0 : MOZ_ASSERT(aConnection);
11364 0 : aConnection->AssertIsOnConnectionThread();
11365 0 : MOZ_ASSERT_IF(mDEBUGConnection, mDEBUGConnection == aConnection);
11366 :
11367 0 : mDEBUGConnection = aConnection;
11368 : #endif
11369 0 : AssertIsOnConnectionThread();
11370 :
11371 0 : mScoper.reset();
11372 :
11373 0 : mStatement = aStatement;
11374 :
11375 0 : if (mStatement) {
11376 0 : mScoper.emplace(mStatement);
11377 : }
11378 0 : }
11379 :
11380 0 : DatabaseConnection::
11381 0 : AutoSavepoint::AutoSavepoint()
11382 : : mConnection(nullptr)
11383 : #ifdef DEBUG
11384 0 : , mDEBUGTransaction(nullptr)
11385 : #endif
11386 : {
11387 0 : MOZ_COUNT_CTOR(DatabaseConnection::AutoSavepoint);
11388 0 : }
11389 :
11390 0 : DatabaseConnection::
11391 0 : AutoSavepoint::~AutoSavepoint()
11392 : {
11393 0 : MOZ_COUNT_DTOR(DatabaseConnection::AutoSavepoint);
11394 :
11395 0 : if (mConnection) {
11396 0 : mConnection->AssertIsOnConnectionThread();
11397 0 : MOZ_ASSERT(mDEBUGTransaction);
11398 0 : MOZ_ASSERT(mDEBUGTransaction->GetMode() == IDBTransaction::READ_WRITE ||
11399 : mDEBUGTransaction->GetMode() ==
11400 : IDBTransaction::READ_WRITE_FLUSH ||
11401 : mDEBUGTransaction->GetMode() == IDBTransaction::CLEANUP ||
11402 : mDEBUGTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
11403 :
11404 0 : if (NS_FAILED(mConnection->RollbackSavepoint())) {
11405 0 : NS_WARNING("Failed to rollback savepoint!");
11406 : }
11407 : }
11408 0 : }
11409 :
11410 : nsresult
11411 0 : DatabaseConnection::
11412 : AutoSavepoint::Start(const TransactionBase* aTransaction)
11413 : {
11414 0 : MOZ_ASSERT(aTransaction);
11415 0 : MOZ_ASSERT(aTransaction->GetMode() == IDBTransaction::READ_WRITE ||
11416 : aTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH ||
11417 : aTransaction->GetMode() == IDBTransaction::CLEANUP ||
11418 : aTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
11419 :
11420 0 : DatabaseConnection* connection = aTransaction->GetDatabase()->GetConnection();
11421 0 : MOZ_ASSERT(connection);
11422 0 : connection->AssertIsOnConnectionThread();
11423 :
11424 0 : MOZ_ASSERT(!mConnection);
11425 0 : MOZ_ASSERT(!mDEBUGTransaction);
11426 :
11427 0 : nsresult rv = connection->StartSavepoint();
11428 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11429 0 : return rv;
11430 : }
11431 :
11432 0 : mConnection = connection;
11433 : #ifdef DEBUG
11434 0 : mDEBUGTransaction = aTransaction;
11435 : #endif
11436 :
11437 0 : return NS_OK;
11438 : }
11439 :
11440 : nsresult
11441 0 : DatabaseConnection::
11442 : AutoSavepoint::Commit()
11443 : {
11444 0 : MOZ_ASSERT(mConnection);
11445 0 : mConnection->AssertIsOnConnectionThread();
11446 0 : MOZ_ASSERT(mDEBUGTransaction);
11447 :
11448 0 : nsresult rv = mConnection->ReleaseSavepoint();
11449 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11450 0 : return rv;
11451 : }
11452 :
11453 0 : mConnection = nullptr;
11454 : #ifdef DEBUG
11455 0 : mDEBUGTransaction = nullptr;
11456 : #endif
11457 :
11458 0 : return NS_OK;
11459 : }
11460 :
11461 0 : DatabaseConnection::
11462 : UpdateRefcountFunction::UpdateRefcountFunction(DatabaseConnection* aConnection,
11463 0 : FileManager* aFileManager)
11464 : : mConnection(aConnection)
11465 : , mFileManager(aFileManager)
11466 0 : , mInSavepoint(false)
11467 : {
11468 0 : MOZ_ASSERT(aConnection);
11469 0 : aConnection->AssertIsOnConnectionThread();
11470 0 : MOZ_ASSERT(aFileManager);
11471 0 : }
11472 :
11473 : nsresult
11474 0 : DatabaseConnection::
11475 : UpdateRefcountFunction::WillCommit()
11476 : {
11477 0 : MOZ_ASSERT(mConnection);
11478 0 : mConnection->AssertIsOnConnectionThread();
11479 :
11480 0 : AUTO_PROFILER_LABEL("DatabaseConnection::UpdateRefcountFunction::WillCommit", STORAGE);
11481 :
11482 0 : DatabaseUpdateFunction function(this);
11483 0 : for (auto iter = mFileInfoEntries.ConstIter(); !iter.Done(); iter.Next()) {
11484 0 : auto key = iter.Key();
11485 0 : FileInfoEntry* value = iter.Data();
11486 0 : MOZ_ASSERT(value);
11487 :
11488 0 : if (value->mDelta && !function.Update(key, value->mDelta)) {
11489 0 : break;
11490 : }
11491 : }
11492 :
11493 0 : nsresult rv = function.ErrorCode();
11494 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11495 0 : return rv;
11496 : }
11497 :
11498 0 : rv = CreateJournals();
11499 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11500 0 : return rv;
11501 : }
11502 :
11503 0 : return NS_OK;
11504 : }
11505 :
11506 : void
11507 0 : DatabaseConnection::
11508 : UpdateRefcountFunction::DidCommit()
11509 : {
11510 0 : MOZ_ASSERT(mConnection);
11511 0 : mConnection->AssertIsOnConnectionThread();
11512 :
11513 0 : AUTO_PROFILER_LABEL(
11514 : "DatabaseConnection::UpdateRefcountFunction::DidCommit", STORAGE);
11515 :
11516 0 : for (auto iter = mFileInfoEntries.ConstIter(); !iter.Done(); iter.Next()) {
11517 0 : FileInfoEntry* value = iter.Data();
11518 :
11519 0 : MOZ_ASSERT(value);
11520 :
11521 0 : if (value->mDelta) {
11522 0 : value->mFileInfo->UpdateDBRefs(value->mDelta);
11523 : }
11524 : }
11525 :
11526 0 : if (NS_FAILED(RemoveJournals(mJournalsToRemoveAfterCommit))) {
11527 0 : NS_WARNING("RemoveJournals failed!");
11528 : }
11529 0 : }
11530 :
11531 : void
11532 0 : DatabaseConnection::
11533 : UpdateRefcountFunction::DidAbort()
11534 : {
11535 0 : MOZ_ASSERT(mConnection);
11536 0 : mConnection->AssertIsOnConnectionThread();
11537 :
11538 0 : AUTO_PROFILER_LABEL(
11539 : "DatabaseConnection::UpdateRefcountFunction::DidAbort", STORAGE);
11540 :
11541 0 : if (NS_FAILED(RemoveJournals(mJournalsToRemoveAfterAbort))) {
11542 0 : NS_WARNING("RemoveJournals failed!");
11543 : }
11544 0 : }
11545 :
11546 : void
11547 0 : DatabaseConnection::
11548 : UpdateRefcountFunction::StartSavepoint()
11549 : {
11550 0 : MOZ_ASSERT(mConnection);
11551 0 : mConnection->AssertIsOnConnectionThread();
11552 0 : MOZ_ASSERT(!mInSavepoint);
11553 0 : MOZ_ASSERT(!mSavepointEntriesIndex.Count());
11554 :
11555 0 : mInSavepoint = true;
11556 0 : }
11557 :
11558 : void
11559 0 : DatabaseConnection::
11560 : UpdateRefcountFunction::ReleaseSavepoint()
11561 : {
11562 0 : MOZ_ASSERT(mConnection);
11563 0 : mConnection->AssertIsOnConnectionThread();
11564 0 : MOZ_ASSERT(mInSavepoint);
11565 :
11566 0 : mSavepointEntriesIndex.Clear();
11567 0 : mInSavepoint = false;
11568 0 : }
11569 :
11570 : void
11571 0 : DatabaseConnection::
11572 : UpdateRefcountFunction::RollbackSavepoint()
11573 : {
11574 0 : MOZ_ASSERT(mConnection);
11575 0 : mConnection->AssertIsOnConnectionThread();
11576 0 : MOZ_ASSERT(!IsOnBackgroundThread());
11577 0 : MOZ_ASSERT(mInSavepoint);
11578 :
11579 0 : for (auto iter = mSavepointEntriesIndex.ConstIter();
11580 0 : !iter.Done(); iter.Next()) {
11581 0 : auto value = iter.Data();
11582 0 : value->mDelta -= value->mSavepointDelta;
11583 : }
11584 :
11585 0 : mInSavepoint = false;
11586 0 : mSavepointEntriesIndex.Clear();
11587 0 : }
11588 :
11589 : void
11590 0 : DatabaseConnection::
11591 : UpdateRefcountFunction::Reset()
11592 : {
11593 0 : MOZ_ASSERT(mConnection);
11594 0 : mConnection->AssertIsOnConnectionThread();
11595 0 : MOZ_ASSERT(!mSavepointEntriesIndex.Count());
11596 0 : MOZ_ASSERT(!mInSavepoint);
11597 :
11598 0 : class MOZ_STACK_CLASS CustomCleanupCallback final
11599 : : public FileInfo::CustomCleanupCallback
11600 : {
11601 : nsCOMPtr<nsIFile> mDirectory;
11602 : nsCOMPtr<nsIFile> mJournalDirectory;
11603 :
11604 : public:
11605 : nsresult
11606 0 : Cleanup(FileManager* aFileManager, int64_t aId) override
11607 : {
11608 0 : if (!mDirectory) {
11609 0 : MOZ_ASSERT(!mJournalDirectory);
11610 :
11611 0 : mDirectory = aFileManager->GetDirectory();
11612 0 : if (NS_WARN_IF(!mDirectory)) {
11613 0 : return NS_ERROR_FAILURE;
11614 : }
11615 :
11616 0 : mJournalDirectory = aFileManager->GetJournalDirectory();
11617 0 : if (NS_WARN_IF(!mJournalDirectory)) {
11618 0 : return NS_ERROR_FAILURE;
11619 : }
11620 : }
11621 :
11622 0 : nsCOMPtr<nsIFile> file = aFileManager->GetFileForId(mDirectory, aId);
11623 0 : if (NS_WARN_IF(!file)) {
11624 0 : return NS_ERROR_FAILURE;
11625 : }
11626 :
11627 : nsresult rv;
11628 : int64_t fileSize;
11629 :
11630 0 : if (aFileManager->EnforcingQuota()) {
11631 0 : rv = file->GetFileSize(&fileSize);
11632 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11633 0 : return rv;
11634 : }
11635 : }
11636 :
11637 0 : rv = file->Remove(false);
11638 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11639 0 : return rv;
11640 : }
11641 :
11642 0 : if (aFileManager->EnforcingQuota()) {
11643 0 : QuotaManager* quotaManager = QuotaManager::Get();
11644 0 : MOZ_ASSERT(quotaManager);
11645 :
11646 0 : quotaManager->DecreaseUsageForOrigin(aFileManager->Type(),
11647 : aFileManager->Group(),
11648 : aFileManager->Origin(),
11649 0 : fileSize);
11650 : }
11651 :
11652 0 : file = aFileManager->GetFileForId(mJournalDirectory, aId);
11653 0 : if (NS_WARN_IF(!file)) {
11654 0 : return NS_ERROR_FAILURE;
11655 : }
11656 :
11657 0 : rv = file->Remove(false);
11658 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11659 0 : return rv;
11660 : }
11661 :
11662 0 : return NS_OK;
11663 : }
11664 : };
11665 :
11666 0 : mJournalsToCreateBeforeCommit.Clear();
11667 0 : mJournalsToRemoveAfterCommit.Clear();
11668 0 : mJournalsToRemoveAfterAbort.Clear();
11669 :
11670 : // FileInfo implementation automatically removes unreferenced files, but it's
11671 : // done asynchronously and with a delay. We want to remove them (and decrease
11672 : // quota usage) before we fire the commit event.
11673 0 : for (auto iter = mFileInfoEntries.ConstIter(); !iter.Done(); iter.Next()) {
11674 0 : FileInfoEntry* value = iter.Data();
11675 :
11676 0 : MOZ_ASSERT(value);
11677 :
11678 0 : FileInfo* fileInfo = value->mFileInfo.forget().take();
11679 :
11680 0 : MOZ_ASSERT(fileInfo);
11681 :
11682 0 : CustomCleanupCallback customCleanupCallback;
11683 0 : fileInfo->Release(&customCleanupCallback);
11684 : }
11685 :
11686 0 : mFileInfoEntries.Clear();
11687 0 : }
11688 :
11689 : nsresult
11690 0 : DatabaseConnection::
11691 : UpdateRefcountFunction::ProcessValue(mozIStorageValueArray* aValues,
11692 : int32_t aIndex,
11693 : UpdateType aUpdateType)
11694 : {
11695 0 : MOZ_ASSERT(mConnection);
11696 0 : mConnection->AssertIsOnConnectionThread();
11697 0 : MOZ_ASSERT(aValues);
11698 :
11699 0 : AUTO_PROFILER_LABEL(
11700 : "DatabaseConnection::UpdateRefcountFunction::ProcessValue", STORAGE);
11701 :
11702 : int32_t type;
11703 0 : nsresult rv = aValues->GetTypeOfIndex(aIndex, &type);
11704 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11705 0 : return rv;
11706 : }
11707 :
11708 0 : if (type == mozIStorageValueArray::VALUE_TYPE_NULL) {
11709 0 : return NS_OK;
11710 : }
11711 :
11712 0 : nsString ids;
11713 0 : rv = aValues->GetString(aIndex, ids);
11714 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11715 0 : return rv;
11716 : }
11717 :
11718 0 : nsTArray<StructuredCloneFile> files;
11719 0 : rv = DeserializeStructuredCloneFiles(mFileManager, ids, files, nullptr);
11720 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11721 0 : return rv;
11722 : }
11723 :
11724 0 : for (uint32_t i = 0; i < files.Length(); i++) {
11725 0 : const StructuredCloneFile& file = files[i];
11726 :
11727 0 : const int64_t id = file.mFileInfo->Id();
11728 0 : MOZ_ASSERT(id > 0);
11729 :
11730 : FileInfoEntry* entry;
11731 0 : if (!mFileInfoEntries.Get(id, &entry)) {
11732 0 : entry = new FileInfoEntry(file.mFileInfo);
11733 0 : mFileInfoEntries.Put(id, entry);
11734 : }
11735 :
11736 0 : if (mInSavepoint) {
11737 0 : mSavepointEntriesIndex.Put(id, entry);
11738 : }
11739 :
11740 0 : switch (aUpdateType) {
11741 : case UpdateType::Increment:
11742 0 : entry->mDelta++;
11743 0 : if (mInSavepoint) {
11744 0 : entry->mSavepointDelta++;
11745 : }
11746 0 : break;
11747 : case UpdateType::Decrement:
11748 0 : entry->mDelta--;
11749 0 : if (mInSavepoint) {
11750 0 : entry->mSavepointDelta--;
11751 : }
11752 0 : break;
11753 : default:
11754 0 : MOZ_CRASH("Unknown update type!");
11755 : }
11756 : }
11757 :
11758 0 : return NS_OK;
11759 : }
11760 :
11761 : nsresult
11762 0 : DatabaseConnection::
11763 : UpdateRefcountFunction::CreateJournals()
11764 : {
11765 0 : MOZ_ASSERT(mConnection);
11766 0 : mConnection->AssertIsOnConnectionThread();
11767 :
11768 0 : AUTO_PROFILER_LABEL(
11769 : "DatabaseConnection::UpdateRefcountFunction::CreateJournals", STORAGE);
11770 :
11771 0 : nsCOMPtr<nsIFile> journalDirectory = mFileManager->GetJournalDirectory();
11772 0 : if (NS_WARN_IF(!journalDirectory)) {
11773 0 : return NS_ERROR_FAILURE;
11774 : }
11775 :
11776 0 : for (uint32_t i = 0; i < mJournalsToCreateBeforeCommit.Length(); i++) {
11777 0 : int64_t id = mJournalsToCreateBeforeCommit[i];
11778 :
11779 : nsCOMPtr<nsIFile> file =
11780 0 : mFileManager->GetFileForId(journalDirectory, id);
11781 0 : if (NS_WARN_IF(!file)) {
11782 0 : return NS_ERROR_FAILURE;
11783 : }
11784 :
11785 0 : nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
11786 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11787 0 : return rv;
11788 : }
11789 :
11790 0 : mJournalsToRemoveAfterAbort.AppendElement(id);
11791 : }
11792 :
11793 0 : return NS_OK;
11794 : }
11795 :
11796 : nsresult
11797 0 : DatabaseConnection::
11798 : UpdateRefcountFunction::RemoveJournals(const nsTArray<int64_t>& aJournals)
11799 : {
11800 0 : MOZ_ASSERT(mConnection);
11801 0 : mConnection->AssertIsOnConnectionThread();
11802 :
11803 0 : AUTO_PROFILER_LABEL(
11804 : "DatabaseConnection::UpdateRefcountFunction::RemoveJournals", STORAGE);
11805 :
11806 0 : nsCOMPtr<nsIFile> journalDirectory = mFileManager->GetJournalDirectory();
11807 0 : if (NS_WARN_IF(!journalDirectory)) {
11808 0 : return NS_ERROR_FAILURE;
11809 : }
11810 :
11811 0 : for (uint32_t index = 0; index < aJournals.Length(); index++) {
11812 : nsCOMPtr<nsIFile> file =
11813 0 : mFileManager->GetFileForId(journalDirectory, aJournals[index]);
11814 0 : if (NS_WARN_IF(!file)) {
11815 0 : return NS_ERROR_FAILURE;
11816 : }
11817 :
11818 0 : if (NS_FAILED(file->Remove(false))) {
11819 0 : NS_WARNING("Failed to removed journal!");
11820 : }
11821 : }
11822 :
11823 0 : return NS_OK;
11824 : }
11825 :
11826 0 : NS_IMPL_ISUPPORTS(DatabaseConnection::UpdateRefcountFunction,
11827 : mozIStorageFunction)
11828 :
11829 : NS_IMETHODIMP
11830 0 : DatabaseConnection::
11831 : UpdateRefcountFunction::OnFunctionCall(mozIStorageValueArray* aValues,
11832 : nsIVariant** _retval)
11833 : {
11834 0 : MOZ_ASSERT(aValues);
11835 0 : MOZ_ASSERT(_retval);
11836 :
11837 0 : AUTO_PROFILER_LABEL(
11838 : "DatabaseConnection::UpdateRefcountFunction::OnFunctionCall", STORAGE);
11839 :
11840 : uint32_t numEntries;
11841 0 : nsresult rv = aValues->GetNumEntries(&numEntries);
11842 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11843 0 : return rv;
11844 : }
11845 :
11846 0 : MOZ_ASSERT(numEntries == 2);
11847 :
11848 : #ifdef DEBUG
11849 : {
11850 0 : int32_t type1 = mozIStorageValueArray::VALUE_TYPE_NULL;
11851 0 : MOZ_ASSERT(NS_SUCCEEDED(aValues->GetTypeOfIndex(0, &type1)));
11852 :
11853 0 : int32_t type2 = mozIStorageValueArray::VALUE_TYPE_NULL;
11854 0 : MOZ_ASSERT(NS_SUCCEEDED(aValues->GetTypeOfIndex(1, &type2)));
11855 :
11856 0 : MOZ_ASSERT(!(type1 == mozIStorageValueArray::VALUE_TYPE_NULL &&
11857 : type2 == mozIStorageValueArray::VALUE_TYPE_NULL));
11858 : }
11859 : #endif
11860 :
11861 0 : rv = ProcessValue(aValues, 0, UpdateType::Decrement);
11862 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11863 0 : return rv;
11864 : }
11865 :
11866 0 : rv = ProcessValue(aValues, 1, UpdateType::Increment);
11867 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11868 0 : return rv;
11869 : }
11870 :
11871 0 : return NS_OK;
11872 : }
11873 :
11874 : bool
11875 0 : DatabaseConnection::UpdateRefcountFunction::
11876 : DatabaseUpdateFunction::Update(int64_t aId,
11877 : int32_t aDelta)
11878 : {
11879 0 : nsresult rv = UpdateInternal(aId, aDelta);
11880 0 : if (NS_FAILED(rv)) {
11881 0 : mErrorCode = rv;
11882 0 : return false;
11883 : }
11884 :
11885 0 : return true;
11886 : }
11887 :
11888 : nsresult
11889 0 : DatabaseConnection::UpdateRefcountFunction::
11890 : DatabaseUpdateFunction::UpdateInternal(int64_t aId,
11891 : int32_t aDelta)
11892 : {
11893 0 : MOZ_ASSERT(mFunction);
11894 :
11895 0 : AUTO_PROFILER_LABEL(
11896 : "DatabaseConnection::UpdateRefcountFunction::"
11897 : "DatabaseUpdateFunction::UpdateInternal",
11898 : STORAGE);
11899 :
11900 0 : DatabaseConnection* connection = mFunction->mConnection;
11901 0 : MOZ_ASSERT(connection);
11902 0 : connection->AssertIsOnConnectionThread();
11903 :
11904 0 : MOZ_ASSERT(connection->GetStorageConnection());
11905 :
11906 : nsresult rv;
11907 0 : if (!mUpdateStatement) {
11908 0 : rv = connection->GetCachedStatement(NS_LITERAL_CSTRING(
11909 : "UPDATE file "
11910 : "SET refcount = refcount + :delta "
11911 : "WHERE id = :id"),
11912 0 : &mUpdateStatement);
11913 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11914 0 : return rv;
11915 : }
11916 : }
11917 :
11918 0 : mozStorageStatementScoper updateScoper(mUpdateStatement);
11919 :
11920 0 : rv = mUpdateStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
11921 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11922 0 : return rv;
11923 : }
11924 :
11925 0 : rv = mUpdateStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
11926 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11927 0 : return rv;
11928 : }
11929 :
11930 0 : rv = mUpdateStatement->Execute();
11931 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11932 0 : return rv;
11933 : }
11934 :
11935 : int32_t rows;
11936 0 : rv = connection->GetStorageConnection()->GetAffectedRows(&rows);
11937 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11938 0 : return rv;
11939 : }
11940 :
11941 0 : if (rows > 0) {
11942 0 : if (!mSelectStatement) {
11943 0 : rv = connection->GetCachedStatement(NS_LITERAL_CSTRING(
11944 : "SELECT id "
11945 : "FROM file "
11946 : "WHERE id = :id"),
11947 0 : &mSelectStatement);
11948 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11949 0 : return rv;
11950 : }
11951 : }
11952 :
11953 0 : mozStorageStatementScoper selectScoper(mSelectStatement);
11954 :
11955 0 : rv = mSelectStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
11956 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11957 0 : return rv;
11958 : }
11959 :
11960 : bool hasResult;
11961 0 : rv = mSelectStatement->ExecuteStep(&hasResult);
11962 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11963 0 : return rv;
11964 : }
11965 :
11966 0 : if (!hasResult) {
11967 : // Don't have to create the journal here, we can create all at once,
11968 : // just before commit
11969 0 : mFunction->mJournalsToCreateBeforeCommit.AppendElement(aId);
11970 : }
11971 :
11972 0 : return NS_OK;
11973 : }
11974 :
11975 0 : if (!mInsertStatement) {
11976 0 : rv = connection->GetCachedStatement(NS_LITERAL_CSTRING(
11977 : "INSERT INTO file (id, refcount) "
11978 : "VALUES(:id, :delta)"),
11979 0 : &mInsertStatement);
11980 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11981 0 : return rv;
11982 : }
11983 : }
11984 :
11985 0 : mozStorageStatementScoper insertScoper(mInsertStatement);
11986 :
11987 0 : rv = mInsertStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
11988 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11989 0 : return rv;
11990 : }
11991 :
11992 0 : rv = mInsertStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
11993 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11994 0 : return rv;
11995 : }
11996 :
11997 0 : rv = mInsertStatement->Execute();
11998 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
11999 0 : return rv;
12000 : }
12001 :
12002 0 : mFunction->mJournalsToRemoveAfterCommit.AppendElement(aId);
12003 0 : return NS_OK;
12004 : }
12005 :
12006 : /*******************************************************************************
12007 : * ConnectionPool implementation
12008 : ******************************************************************************/
12009 :
12010 0 : ConnectionPool::ConnectionPool()
12011 : : mDatabasesMutex("ConnectionPool::mDatabasesMutex")
12012 0 : , mIdleTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
12013 : , mNextTransactionId(0)
12014 : , mTotalThreadCount(0)
12015 : , mShutdownRequested(false)
12016 0 : , mShutdownComplete(false)
12017 : {
12018 0 : AssertIsOnOwningThread();
12019 0 : AssertIsOnBackgroundThread();
12020 0 : MOZ_ASSERT(mIdleTimer);
12021 0 : }
12022 :
12023 0 : ConnectionPool::~ConnectionPool()
12024 : {
12025 0 : AssertIsOnOwningThread();
12026 0 : MOZ_ASSERT(mIdleThreads.IsEmpty());
12027 0 : MOZ_ASSERT(mIdleDatabases.IsEmpty());
12028 0 : MOZ_ASSERT(!mIdleTimer);
12029 0 : MOZ_ASSERT(mTargetIdleTime.IsNull());
12030 0 : MOZ_ASSERT(!mDatabases.Count());
12031 0 : MOZ_ASSERT(!mTransactions.Count());
12032 0 : MOZ_ASSERT(mQueuedTransactions.IsEmpty());
12033 0 : MOZ_ASSERT(mCompleteCallbacks.IsEmpty());
12034 0 : MOZ_ASSERT(!mTotalThreadCount);
12035 0 : MOZ_ASSERT(mShutdownRequested);
12036 0 : MOZ_ASSERT(mShutdownComplete);
12037 0 : }
12038 :
12039 : // static
12040 : void
12041 0 : ConnectionPool::IdleTimerCallback(nsITimer* aTimer, void* aClosure)
12042 : {
12043 0 : MOZ_ASSERT(aTimer);
12044 :
12045 0 : AUTO_PROFILER_LABEL("ConnectionPool::IdleTimerCallback", STORAGE);
12046 :
12047 0 : auto* self = static_cast<ConnectionPool*>(aClosure);
12048 0 : MOZ_ASSERT(self);
12049 0 : MOZ_ASSERT(self->mIdleTimer);
12050 0 : MOZ_ASSERT(SameCOMIdentity(self->mIdleTimer, aTimer));
12051 0 : MOZ_ASSERT(!self->mTargetIdleTime.IsNull());
12052 0 : MOZ_ASSERT_IF(self->mIdleDatabases.IsEmpty(), !self->mIdleThreads.IsEmpty());
12053 0 : MOZ_ASSERT_IF(self->mIdleThreads.IsEmpty(), !self->mIdleDatabases.IsEmpty());
12054 :
12055 0 : self->mTargetIdleTime = TimeStamp();
12056 :
12057 : // Cheat a little.
12058 0 : TimeStamp now = TimeStamp::NowLoRes() + TimeDuration::FromMilliseconds(500);
12059 :
12060 0 : uint32_t index = 0;
12061 :
12062 0 : for (uint32_t count = self->mIdleDatabases.Length(); index < count; index++) {
12063 0 : IdleDatabaseInfo& info = self->mIdleDatabases[index];
12064 :
12065 0 : if (now >= info.mIdleTime) {
12066 0 : if (info.mDatabaseInfo->mIdle) {
12067 0 : self->PerformIdleDatabaseMaintenance(info.mDatabaseInfo);
12068 : } else {
12069 0 : self->CloseDatabase(info.mDatabaseInfo);
12070 : }
12071 : } else {
12072 0 : break;
12073 : }
12074 : }
12075 :
12076 0 : if (index) {
12077 0 : self->mIdleDatabases.RemoveElementsAt(0, index);
12078 :
12079 0 : index = 0;
12080 : }
12081 :
12082 0 : for (uint32_t count = self->mIdleThreads.Length(); index < count; index++) {
12083 0 : IdleThreadInfo& info = self->mIdleThreads[index];
12084 0 : MOZ_ASSERT(info.mThreadInfo.mThread);
12085 0 : MOZ_ASSERT(info.mThreadInfo.mRunnable);
12086 :
12087 0 : if (now >= info.mIdleTime) {
12088 0 : self->ShutdownThread(info.mThreadInfo);
12089 : } else {
12090 0 : break;
12091 : }
12092 : }
12093 :
12094 0 : if (index) {
12095 0 : self->mIdleThreads.RemoveElementsAt(0, index);
12096 : }
12097 :
12098 0 : self->AdjustIdleTimer();
12099 0 : }
12100 :
12101 : nsresult
12102 0 : ConnectionPool::GetOrCreateConnection(const Database* aDatabase,
12103 : DatabaseConnection** aConnection)
12104 : {
12105 0 : MOZ_ASSERT(!NS_IsMainThread());
12106 0 : MOZ_ASSERT(!IsOnBackgroundThread());
12107 0 : MOZ_ASSERT(aDatabase);
12108 :
12109 0 : AUTO_PROFILER_LABEL("ConnectionPool::GetOrCreateConnection", STORAGE);
12110 :
12111 : DatabaseInfo* dbInfo;
12112 : {
12113 0 : MutexAutoLock lock(mDatabasesMutex);
12114 :
12115 0 : dbInfo = mDatabases.Get(aDatabase->Id());
12116 : }
12117 :
12118 0 : MOZ_ASSERT(dbInfo);
12119 :
12120 0 : RefPtr<DatabaseConnection> connection = dbInfo->mConnection;
12121 0 : if (!connection) {
12122 0 : MOZ_ASSERT(!dbInfo->mDEBUGConnectionThread);
12123 :
12124 0 : nsCOMPtr<mozIStorageConnection> storageConnection;
12125 : nsresult rv =
12126 0 : GetStorageConnection(aDatabase->FilePath(),
12127 : aDatabase->Type(),
12128 0 : aDatabase->Group(),
12129 0 : aDatabase->Origin(),
12130 : aDatabase->TelemetryId(),
12131 0 : getter_AddRefs(storageConnection));
12132 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
12133 0 : return rv;
12134 : }
12135 :
12136 : connection =
12137 0 : new DatabaseConnection(storageConnection, aDatabase->GetFileManager());
12138 :
12139 0 : rv = connection->Init();
12140 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
12141 0 : return rv;
12142 : }
12143 :
12144 0 : dbInfo->mConnection = connection;
12145 :
12146 0 : IDB_DEBUG_LOG(("ConnectionPool created connection 0x%p for '%s'",
12147 : dbInfo->mConnection.get(),
12148 : NS_ConvertUTF16toUTF8(aDatabase->FilePath()).get()));
12149 :
12150 : #ifdef DEBUG
12151 0 : dbInfo->mDEBUGConnectionThread = GetCurrentPhysicalThread();
12152 : #endif
12153 : }
12154 :
12155 0 : dbInfo->AssertIsOnConnectionThread();
12156 :
12157 0 : connection.forget(aConnection);
12158 0 : return NS_OK;
12159 : }
12160 :
12161 : uint64_t
12162 0 : ConnectionPool::Start(const nsID& aBackgroundChildLoggingId,
12163 : const nsACString& aDatabaseId,
12164 : int64_t aLoggingSerialNumber,
12165 : const nsTArray<nsString>& aObjectStoreNames,
12166 : bool aIsWriteTransaction,
12167 : TransactionDatabaseOperationBase* aTransactionOp)
12168 : {
12169 0 : AssertIsOnOwningThread();
12170 0 : MOZ_ASSERT(!aDatabaseId.IsEmpty());
12171 0 : MOZ_ASSERT(mNextTransactionId < UINT64_MAX);
12172 0 : MOZ_ASSERT(!mShutdownRequested);
12173 :
12174 0 : AUTO_PROFILER_LABEL("ConnectionPool::Start", STORAGE);
12175 :
12176 0 : const uint64_t transactionId = ++mNextTransactionId;
12177 :
12178 0 : DatabaseInfo* dbInfo = mDatabases.Get(aDatabaseId);
12179 :
12180 0 : const bool databaseInfoIsNew = !dbInfo;
12181 :
12182 0 : if (databaseInfoIsNew) {
12183 0 : dbInfo = new DatabaseInfo(this, aDatabaseId);
12184 :
12185 0 : MutexAutoLock lock(mDatabasesMutex);
12186 :
12187 0 : mDatabases.Put(aDatabaseId, dbInfo);
12188 : }
12189 :
12190 : auto* transactionInfo =
12191 : new TransactionInfo(dbInfo,
12192 : aBackgroundChildLoggingId,
12193 : aDatabaseId,
12194 : transactionId,
12195 : aLoggingSerialNumber,
12196 : aObjectStoreNames,
12197 : aIsWriteTransaction,
12198 0 : aTransactionOp);
12199 :
12200 0 : MOZ_ASSERT(!mTransactions.Get(transactionId));
12201 0 : mTransactions.Put(transactionId, transactionInfo);
12202 :
12203 0 : if (aIsWriteTransaction) {
12204 0 : MOZ_ASSERT(dbInfo->mWriteTransactionCount < UINT32_MAX);
12205 0 : dbInfo->mWriteTransactionCount++;
12206 : } else {
12207 0 : MOZ_ASSERT(dbInfo->mReadTransactionCount < UINT32_MAX);
12208 0 : dbInfo->mReadTransactionCount++;
12209 : }
12210 :
12211 0 : auto& blockingTransactions = dbInfo->mBlockingTransactions;
12212 :
12213 0 : for (uint32_t nameIndex = 0, nameCount = aObjectStoreNames.Length();
12214 0 : nameIndex < nameCount;
12215 : nameIndex++) {
12216 0 : const nsString& objectStoreName = aObjectStoreNames[nameIndex];
12217 :
12218 0 : TransactionInfoPair* blockInfo = blockingTransactions.Get(objectStoreName);
12219 0 : if (!blockInfo) {
12220 0 : blockInfo = new TransactionInfoPair();
12221 0 : blockingTransactions.Put(objectStoreName, blockInfo);
12222 : }
12223 :
12224 : // Mark what we are blocking on.
12225 0 : if (TransactionInfo* blockingRead = blockInfo->mLastBlockingReads) {
12226 0 : transactionInfo->mBlockedOn.PutEntry(blockingRead);
12227 0 : blockingRead->AddBlockingTransaction(transactionInfo);
12228 : }
12229 :
12230 0 : if (aIsWriteTransaction) {
12231 0 : if (const uint32_t writeCount = blockInfo->mLastBlockingWrites.Length()) {
12232 0 : for (uint32_t writeIndex = 0; writeIndex < writeCount; writeIndex++) {
12233 : TransactionInfo* blockingWrite =
12234 0 : blockInfo->mLastBlockingWrites[writeIndex];
12235 0 : MOZ_ASSERT(blockingWrite);
12236 :
12237 0 : transactionInfo->mBlockedOn.PutEntry(blockingWrite);
12238 0 : blockingWrite->AddBlockingTransaction(transactionInfo);
12239 : }
12240 : }
12241 :
12242 0 : blockInfo->mLastBlockingReads = transactionInfo;
12243 0 : blockInfo->mLastBlockingWrites.Clear();
12244 : } else {
12245 0 : blockInfo->mLastBlockingWrites.AppendElement(transactionInfo);
12246 : }
12247 : }
12248 :
12249 0 : if (!transactionInfo->mBlockedOn.Count()) {
12250 0 : Unused << ScheduleTransaction(transactionInfo,
12251 : /* aFromQueuedTransactions */ false);
12252 : }
12253 :
12254 0 : if (!databaseInfoIsNew &&
12255 0 : (mIdleDatabases.RemoveElement(dbInfo) ||
12256 0 : mDatabasesPerformingIdleMaintenance.RemoveElement(dbInfo))) {
12257 0 : AdjustIdleTimer();
12258 : }
12259 :
12260 0 : return transactionId;
12261 : }
12262 :
12263 : void
12264 0 : ConnectionPool::Dispatch(uint64_t aTransactionId, nsIRunnable* aRunnable)
12265 : {
12266 0 : AssertIsOnOwningThread();
12267 0 : MOZ_ASSERT(aRunnable);
12268 :
12269 0 : AUTO_PROFILER_LABEL("ConnectionPool::Dispatch", STORAGE);
12270 :
12271 0 : TransactionInfo* transactionInfo = mTransactions.Get(aTransactionId);
12272 0 : MOZ_ASSERT(transactionInfo);
12273 0 : MOZ_ASSERT(!transactionInfo->mFinished);
12274 :
12275 0 : if (transactionInfo->mRunning) {
12276 0 : DatabaseInfo* dbInfo = transactionInfo->mDatabaseInfo;
12277 0 : MOZ_ASSERT(dbInfo);
12278 0 : MOZ_ASSERT(dbInfo->mThreadInfo.mThread);
12279 0 : MOZ_ASSERT(dbInfo->mThreadInfo.mRunnable);
12280 0 : MOZ_ASSERT(!dbInfo->mClosing);
12281 0 : MOZ_ASSERT_IF(transactionInfo->mIsWriteTransaction,
12282 : dbInfo->mRunningWriteTransaction == transactionInfo);
12283 :
12284 0 : MOZ_ALWAYS_SUCCEEDS(
12285 : dbInfo->mThreadInfo.mThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL));
12286 : } else {
12287 0 : transactionInfo->mQueuedRunnables.AppendElement(aRunnable);
12288 : }
12289 0 : }
12290 :
12291 : void
12292 0 : ConnectionPool::Finish(uint64_t aTransactionId, FinishCallback* aCallback)
12293 : {
12294 0 : AssertIsOnOwningThread();
12295 :
12296 : #ifdef DEBUG
12297 0 : TransactionInfo* transactionInfo = mTransactions.Get(aTransactionId);
12298 0 : MOZ_ASSERT(transactionInfo);
12299 0 : MOZ_ASSERT(!transactionInfo->mFinished);
12300 : #endif
12301 :
12302 0 : AUTO_PROFILER_LABEL("ConnectionPool::Finish", STORAGE);
12303 :
12304 : RefPtr<FinishCallbackWrapper> wrapper =
12305 0 : new FinishCallbackWrapper(this, aTransactionId, aCallback);
12306 :
12307 0 : Dispatch(aTransactionId, wrapper);
12308 :
12309 : #ifdef DEBUG
12310 0 : MOZ_ASSERT(!transactionInfo->mFinished);
12311 0 : transactionInfo->mFinished = true;
12312 : #endif
12313 0 : }
12314 :
12315 : void
12316 0 : ConnectionPool::WaitForDatabasesToComplete(nsTArray<nsCString>&& aDatabaseIds,
12317 : nsIRunnable* aCallback)
12318 : {
12319 0 : AssertIsOnOwningThread();
12320 0 : MOZ_ASSERT(!aDatabaseIds.IsEmpty());
12321 0 : MOZ_ASSERT(aCallback);
12322 :
12323 0 : AUTO_PROFILER_LABEL("ConnectionPool::WaitForDatabasesToComplete", STORAGE);
12324 :
12325 0 : bool mayRunCallbackImmediately = true;
12326 :
12327 0 : for (uint32_t index = 0, count = aDatabaseIds.Length();
12328 0 : index < count;
12329 : index++) {
12330 0 : const nsCString& databaseId = aDatabaseIds[index];
12331 0 : MOZ_ASSERT(!databaseId.IsEmpty());
12332 :
12333 0 : if (CloseDatabaseWhenIdleInternal(databaseId)) {
12334 0 : mayRunCallbackImmediately = false;
12335 : }
12336 : }
12337 :
12338 0 : if (mayRunCallbackImmediately) {
12339 0 : Unused << aCallback->Run();
12340 0 : return;
12341 : }
12342 :
12343 : nsAutoPtr<DatabasesCompleteCallback> callback(
12344 0 : new DatabasesCompleteCallback(Move(aDatabaseIds), aCallback));
12345 0 : mCompleteCallbacks.AppendElement(callback.forget());
12346 : }
12347 :
12348 : void
12349 0 : ConnectionPool::Shutdown()
12350 : {
12351 0 : AssertIsOnOwningThread();
12352 0 : MOZ_ASSERT(!mShutdownRequested);
12353 0 : MOZ_ASSERT(!mShutdownComplete);
12354 :
12355 0 : AUTO_PROFILER_LABEL("ConnectionPool::Shutdown", STORAGE);
12356 :
12357 0 : mShutdownRequested = true;
12358 :
12359 0 : CancelIdleTimer();
12360 0 : MOZ_ASSERT(mTargetIdleTime.IsNull());
12361 :
12362 0 : mIdleTimer = nullptr;
12363 :
12364 0 : CloseIdleDatabases();
12365 :
12366 0 : ShutdownIdleThreads();
12367 :
12368 0 : if (!mDatabases.Count()) {
12369 0 : MOZ_ASSERT(!mTransactions.Count());
12370 :
12371 0 : Cleanup();
12372 :
12373 0 : MOZ_ASSERT(mShutdownComplete);
12374 0 : return;
12375 : }
12376 :
12377 0 : MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return mShutdownComplete; }));
12378 : }
12379 :
12380 : void
12381 0 : ConnectionPool::Cleanup()
12382 : {
12383 0 : AssertIsOnOwningThread();
12384 0 : MOZ_ASSERT(mShutdownRequested);
12385 0 : MOZ_ASSERT(!mShutdownComplete);
12386 0 : MOZ_ASSERT(!mDatabases.Count());
12387 0 : MOZ_ASSERT(!mTransactions.Count());
12388 0 : MOZ_ASSERT(mIdleThreads.IsEmpty());
12389 :
12390 0 : AUTO_PROFILER_LABEL("ConnectionPool::Cleanup", STORAGE);
12391 :
12392 0 : if (!mCompleteCallbacks.IsEmpty()) {
12393 : // Run all callbacks manually now.
12394 0 : for (uint32_t count = mCompleteCallbacks.Length(), index = 0;
12395 0 : index < count;
12396 : index++) {
12397 : nsAutoPtr<DatabasesCompleteCallback> completeCallback(
12398 0 : mCompleteCallbacks[index].forget());
12399 0 : MOZ_ASSERT(completeCallback);
12400 0 : MOZ_ASSERT(completeCallback->mCallback);
12401 :
12402 0 : Unused << completeCallback->mCallback->Run();
12403 : }
12404 :
12405 0 : mCompleteCallbacks.Clear();
12406 :
12407 : // And make sure they get processed.
12408 0 : nsIThread* currentThread = NS_GetCurrentThread();
12409 0 : MOZ_ASSERT(currentThread);
12410 :
12411 0 : MOZ_ALWAYS_SUCCEEDS(NS_ProcessPendingEvents(currentThread));
12412 : }
12413 :
12414 0 : mShutdownComplete = true;
12415 0 : }
12416 :
12417 : void
12418 0 : ConnectionPool::AdjustIdleTimer()
12419 : {
12420 0 : AssertIsOnOwningThread();
12421 0 : MOZ_ASSERT(mIdleTimer);
12422 :
12423 0 : AUTO_PROFILER_LABEL("ConnectionPool::AdjustIdleTimer", STORAGE);
12424 :
12425 : // Figure out the next time at which we should release idle resources. This
12426 : // includes both databases and threads.
12427 0 : TimeStamp newTargetIdleTime;
12428 0 : MOZ_ASSERT(newTargetIdleTime.IsNull());
12429 :
12430 0 : if (!mIdleDatabases.IsEmpty()) {
12431 0 : newTargetIdleTime = mIdleDatabases[0].mIdleTime;
12432 : }
12433 :
12434 0 : if (!mIdleThreads.IsEmpty()) {
12435 0 : const TimeStamp& idleTime = mIdleThreads[0].mIdleTime;
12436 :
12437 0 : if (newTargetIdleTime.IsNull() || idleTime < newTargetIdleTime) {
12438 0 : newTargetIdleTime = idleTime;
12439 : }
12440 : }
12441 :
12442 0 : MOZ_ASSERT_IF(newTargetIdleTime.IsNull(), mIdleDatabases.IsEmpty());
12443 0 : MOZ_ASSERT_IF(newTargetIdleTime.IsNull(), mIdleThreads.IsEmpty());
12444 :
12445 : // Cancel the timer if it was running and the new target time is different.
12446 0 : if (!mTargetIdleTime.IsNull() &&
12447 0 : (newTargetIdleTime.IsNull() || mTargetIdleTime != newTargetIdleTime)) {
12448 0 : CancelIdleTimer();
12449 :
12450 0 : MOZ_ASSERT(mTargetIdleTime.IsNull());
12451 : }
12452 :
12453 : // Schedule the timer if we have a target time different than before.
12454 0 : if (!newTargetIdleTime.IsNull() &&
12455 0 : (mTargetIdleTime.IsNull() || mTargetIdleTime != newTargetIdleTime)) {
12456 0 : double delta = (newTargetIdleTime - TimeStamp::NowLoRes()).ToMilliseconds();
12457 :
12458 : uint32_t delay;
12459 0 : if (delta > 0) {
12460 0 : delay = uint32_t(std::min(delta, double(UINT32_MAX)));
12461 : } else {
12462 0 : delay = 0;
12463 : }
12464 :
12465 0 : MOZ_ALWAYS_SUCCEEDS(
12466 : mIdleTimer->InitWithNamedFuncCallback(IdleTimerCallback,
12467 : this,
12468 : delay,
12469 : nsITimer::TYPE_ONE_SHOT,
12470 : "ConnectionPool::IdleTimerCallback"));
12471 :
12472 0 : mTargetIdleTime = newTargetIdleTime;
12473 : }
12474 0 : }
12475 :
12476 : void
12477 0 : ConnectionPool::CancelIdleTimer()
12478 : {
12479 0 : AssertIsOnOwningThread();
12480 0 : MOZ_ASSERT(mIdleTimer);
12481 :
12482 0 : if (!mTargetIdleTime.IsNull()) {
12483 0 : MOZ_ALWAYS_SUCCEEDS(mIdleTimer->Cancel());
12484 :
12485 0 : mTargetIdleTime = TimeStamp();
12486 0 : MOZ_ASSERT(mTargetIdleTime.IsNull());
12487 : }
12488 0 : }
12489 :
12490 : void
12491 0 : ConnectionPool::ShutdownThread(ThreadInfo& aThreadInfo)
12492 : {
12493 0 : AssertIsOnOwningThread();
12494 0 : MOZ_ASSERT(aThreadInfo.mThread);
12495 0 : MOZ_ASSERT(aThreadInfo.mRunnable);
12496 0 : MOZ_ASSERT(mTotalThreadCount);
12497 :
12498 0 : RefPtr<ThreadRunnable> runnable;
12499 0 : aThreadInfo.mRunnable.swap(runnable);
12500 :
12501 0 : nsCOMPtr<nsIThread> thread;
12502 0 : aThreadInfo.mThread.swap(thread);
12503 :
12504 0 : IDB_DEBUG_LOG(("ConnectionPool shutting down thread %" PRIu32,
12505 : runnable->SerialNumber()));
12506 :
12507 : // This should clean up the thread with the profiler.
12508 0 : MOZ_ALWAYS_SUCCEEDS(thread->Dispatch(runnable.forget(),
12509 : NS_DISPATCH_NORMAL));
12510 :
12511 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
12512 : NewRunnableMethod("nsIThread::Shutdown", thread, &nsIThread::Shutdown)));
12513 :
12514 0 : mTotalThreadCount--;
12515 0 : }
12516 :
12517 : void
12518 0 : ConnectionPool::CloseIdleDatabases()
12519 : {
12520 0 : AssertIsOnOwningThread();
12521 0 : MOZ_ASSERT(mShutdownRequested);
12522 :
12523 0 : AUTO_PROFILER_LABEL("ConnectionPool::CloseIdleDatabases", STORAGE);
12524 :
12525 0 : if (!mIdleDatabases.IsEmpty()) {
12526 0 : for (IdleDatabaseInfo& idleInfo : mIdleDatabases) {
12527 0 : CloseDatabase(idleInfo.mDatabaseInfo);
12528 : }
12529 0 : mIdleDatabases.Clear();
12530 : }
12531 :
12532 0 : if (!mDatabasesPerformingIdleMaintenance.IsEmpty()) {
12533 0 : for (DatabaseInfo* dbInfo : mDatabasesPerformingIdleMaintenance) {
12534 0 : MOZ_ASSERT(dbInfo);
12535 0 : CloseDatabase(dbInfo);
12536 : }
12537 0 : mDatabasesPerformingIdleMaintenance.Clear();
12538 : }
12539 0 : }
12540 :
12541 : void
12542 0 : ConnectionPool::ShutdownIdleThreads()
12543 : {
12544 0 : AssertIsOnOwningThread();
12545 0 : MOZ_ASSERT(mShutdownRequested);
12546 :
12547 0 : AUTO_PROFILER_LABEL("ConnectionPool::ShutdownIdleThreads", STORAGE);
12548 :
12549 0 : if (!mIdleThreads.IsEmpty()) {
12550 0 : for (uint32_t threadCount = mIdleThreads.Length(), threadIndex = 0;
12551 0 : threadIndex < threadCount;
12552 : threadIndex++) {
12553 0 : ShutdownThread(mIdleThreads[threadIndex].mThreadInfo);
12554 : }
12555 0 : mIdleThreads.Clear();
12556 : }
12557 0 : }
12558 :
12559 : bool
12560 0 : ConnectionPool::ScheduleTransaction(TransactionInfo* aTransactionInfo,
12561 : bool aFromQueuedTransactions)
12562 : {
12563 0 : AssertIsOnOwningThread();
12564 0 : MOZ_ASSERT(aTransactionInfo);
12565 :
12566 0 : AUTO_PROFILER_LABEL("ConnectionPool::ScheduleTransaction", STORAGE);
12567 :
12568 0 : DatabaseInfo* dbInfo = aTransactionInfo->mDatabaseInfo;
12569 0 : MOZ_ASSERT(dbInfo);
12570 :
12571 0 : dbInfo->mIdle = false;
12572 :
12573 0 : if (dbInfo->mClosing) {
12574 0 : MOZ_ASSERT(!mIdleDatabases.Contains(dbInfo));
12575 0 : MOZ_ASSERT(
12576 : !dbInfo->mTransactionsScheduledDuringClose.Contains(aTransactionInfo));
12577 :
12578 0 : dbInfo->mTransactionsScheduledDuringClose.AppendElement(aTransactionInfo);
12579 0 : return true;
12580 : }
12581 :
12582 0 : if (!dbInfo->mThreadInfo.mThread) {
12583 0 : MOZ_ASSERT(!dbInfo->mThreadInfo.mRunnable);
12584 :
12585 0 : if (mIdleThreads.IsEmpty()) {
12586 0 : bool created = false;
12587 :
12588 0 : if (mTotalThreadCount < kMaxConnectionThreadCount) {
12589 : // This will set the thread up with the profiler.
12590 0 : RefPtr<ThreadRunnable> runnable = new ThreadRunnable();
12591 :
12592 0 : nsCOMPtr<nsIThread> newThread;
12593 : nsresult rv =
12594 0 : NS_NewNamedThread(runnable->GetThreadName(),
12595 0 : getter_AddRefs(newThread), runnable);
12596 0 : if (NS_SUCCEEDED(rv)) {
12597 0 : MOZ_ASSERT(newThread);
12598 :
12599 0 : IDB_DEBUG_LOG(("ConnectionPool created thread %" PRIu32,
12600 : runnable->SerialNumber()));
12601 :
12602 0 : dbInfo->mThreadInfo.mThread.swap(newThread);
12603 0 : dbInfo->mThreadInfo.mRunnable.swap(runnable);
12604 :
12605 0 : mTotalThreadCount++;
12606 0 : created = true;
12607 : } else {
12608 0 : NS_WARNING("Failed to make new thread!");
12609 : }
12610 0 : } else if (!mDatabasesPerformingIdleMaintenance.IsEmpty()) {
12611 : // We need a thread right now so force all idle processing to stop by
12612 : // posting a dummy runnable to each thread that might be doing idle
12613 : // maintenance.
12614 0 : nsCOMPtr<nsIRunnable> runnable = new Runnable("IndexedDBDummyRunnable");
12615 :
12616 0 : for (uint32_t index = mDatabasesPerformingIdleMaintenance.Length();
12617 0 : index > 0;
12618 : index--) {
12619 0 : DatabaseInfo* dbInfo = mDatabasesPerformingIdleMaintenance[index - 1];
12620 0 : MOZ_ASSERT(dbInfo);
12621 0 : MOZ_ASSERT(dbInfo->mThreadInfo.mThread);
12622 :
12623 0 : MOZ_ALWAYS_SUCCEEDS(
12624 : dbInfo->mThreadInfo.mThread->Dispatch(runnable.forget(),
12625 : NS_DISPATCH_NORMAL));
12626 : }
12627 : }
12628 :
12629 0 : if (!created) {
12630 0 : if (!aFromQueuedTransactions) {
12631 0 : MOZ_ASSERT(!mQueuedTransactions.Contains(aTransactionInfo));
12632 0 : mQueuedTransactions.AppendElement(aTransactionInfo);
12633 : }
12634 0 : return false;
12635 : }
12636 : } else {
12637 0 : const uint32_t lastIndex = mIdleThreads.Length() - 1;
12638 :
12639 0 : ThreadInfo& threadInfo = mIdleThreads[lastIndex].mThreadInfo;
12640 :
12641 0 : dbInfo->mThreadInfo.mRunnable.swap(threadInfo.mRunnable);
12642 0 : dbInfo->mThreadInfo.mThread.swap(threadInfo.mThread);
12643 :
12644 0 : mIdleThreads.RemoveElementAt(lastIndex);
12645 :
12646 0 : AdjustIdleTimer();
12647 : }
12648 : }
12649 :
12650 0 : MOZ_ASSERT(dbInfo->mThreadInfo.mThread);
12651 0 : MOZ_ASSERT(dbInfo->mThreadInfo.mRunnable);
12652 :
12653 0 : if (aTransactionInfo->mIsWriteTransaction) {
12654 0 : if (dbInfo->mRunningWriteTransaction) {
12655 : // SQLite only allows one write transaction at a time so queue this
12656 : // transaction for later.
12657 0 : MOZ_ASSERT(
12658 : !dbInfo->mScheduledWriteTransactions.Contains(aTransactionInfo));
12659 :
12660 0 : dbInfo->mScheduledWriteTransactions.AppendElement(aTransactionInfo);
12661 0 : return true;
12662 : }
12663 :
12664 0 : dbInfo->mRunningWriteTransaction = aTransactionInfo;
12665 0 : dbInfo->mNeedsCheckpoint = true;
12666 : }
12667 :
12668 0 : MOZ_ASSERT(!aTransactionInfo->mRunning);
12669 0 : aTransactionInfo->mRunning = true;
12670 :
12671 : nsTArray<nsCOMPtr<nsIRunnable>>& queuedRunnables =
12672 0 : aTransactionInfo->mQueuedRunnables;
12673 :
12674 0 : if (!queuedRunnables.IsEmpty()) {
12675 0 : for (uint32_t index = 0, count = queuedRunnables.Length();
12676 0 : index < count;
12677 : index++) {
12678 0 : nsCOMPtr<nsIRunnable> runnable;
12679 0 : queuedRunnables[index].swap(runnable);
12680 :
12681 0 : MOZ_ALWAYS_SUCCEEDS(
12682 : dbInfo->mThreadInfo.mThread->Dispatch(runnable.forget(),
12683 : NS_DISPATCH_NORMAL));
12684 : }
12685 :
12686 0 : queuedRunnables.Clear();
12687 : }
12688 :
12689 0 : return true;
12690 : }
12691 :
12692 : void
12693 0 : ConnectionPool::NoteFinishedTransaction(uint64_t aTransactionId)
12694 : {
12695 0 : AssertIsOnOwningThread();
12696 :
12697 0 : AUTO_PROFILER_LABEL("ConnectionPool::NoteFinishedTransaction", STORAGE);
12698 :
12699 0 : TransactionInfo* transactionInfo = mTransactions.Get(aTransactionId);
12700 0 : MOZ_ASSERT(transactionInfo);
12701 0 : MOZ_ASSERT(transactionInfo->mRunning);
12702 0 : MOZ_ASSERT(transactionInfo->mFinished);
12703 :
12704 0 : transactionInfo->mRunning = false;
12705 :
12706 0 : DatabaseInfo* dbInfo = transactionInfo->mDatabaseInfo;
12707 0 : MOZ_ASSERT(dbInfo);
12708 0 : MOZ_ASSERT(mDatabases.Get(transactionInfo->mDatabaseId) == dbInfo);
12709 0 : MOZ_ASSERT(dbInfo->mThreadInfo.mThread);
12710 0 : MOZ_ASSERT(dbInfo->mThreadInfo.mRunnable);
12711 :
12712 : // Schedule the next write transaction if there are any queued.
12713 0 : if (dbInfo->mRunningWriteTransaction == transactionInfo) {
12714 0 : MOZ_ASSERT(transactionInfo->mIsWriteTransaction);
12715 0 : MOZ_ASSERT(dbInfo->mNeedsCheckpoint);
12716 :
12717 0 : dbInfo->mRunningWriteTransaction = nullptr;
12718 :
12719 0 : if (!dbInfo->mScheduledWriteTransactions.IsEmpty()) {
12720 : TransactionInfo* nextWriteTransaction =
12721 0 : dbInfo->mScheduledWriteTransactions[0];
12722 0 : MOZ_ASSERT(nextWriteTransaction);
12723 :
12724 0 : dbInfo->mScheduledWriteTransactions.RemoveElementAt(0);
12725 :
12726 0 : MOZ_ALWAYS_TRUE(ScheduleTransaction(nextWriteTransaction,
12727 : /* aFromQueuedTransactions */ false));
12728 : }
12729 : }
12730 :
12731 : const nsTArray<nsString>& objectStoreNames =
12732 0 : transactionInfo->mObjectStoreNames;
12733 :
12734 0 : for (uint32_t index = 0, count = objectStoreNames.Length();
12735 0 : index < count;
12736 : index++) {
12737 : TransactionInfoPair* blockInfo =
12738 0 : dbInfo->mBlockingTransactions.Get(objectStoreNames[index]);
12739 0 : MOZ_ASSERT(blockInfo);
12740 :
12741 0 : if (transactionInfo->mIsWriteTransaction &&
12742 0 : blockInfo->mLastBlockingReads == transactionInfo) {
12743 0 : blockInfo->mLastBlockingReads = nullptr;
12744 : }
12745 :
12746 0 : blockInfo->mLastBlockingWrites.RemoveElement(transactionInfo);
12747 : }
12748 :
12749 0 : transactionInfo->RemoveBlockingTransactions();
12750 :
12751 0 : if (transactionInfo->mIsWriteTransaction) {
12752 0 : MOZ_ASSERT(dbInfo->mWriteTransactionCount);
12753 0 : dbInfo->mWriteTransactionCount--;
12754 : } else {
12755 0 : MOZ_ASSERT(dbInfo->mReadTransactionCount);
12756 0 : dbInfo->mReadTransactionCount--;
12757 : }
12758 :
12759 0 : mTransactions.Remove(aTransactionId);
12760 :
12761 : #ifdef DEBUG
12762 : // That just deleted |transactionInfo|.
12763 0 : transactionInfo = nullptr;
12764 : #endif
12765 :
12766 0 : if (!dbInfo->TotalTransactionCount()) {
12767 0 : MOZ_ASSERT(!dbInfo->mIdle);
12768 0 : dbInfo->mIdle = true;
12769 :
12770 0 : NoteIdleDatabase(dbInfo);
12771 : }
12772 0 : }
12773 :
12774 : void
12775 0 : ConnectionPool::ScheduleQueuedTransactions(ThreadInfo& aThreadInfo)
12776 : {
12777 0 : AssertIsOnOwningThread();
12778 0 : MOZ_ASSERT(aThreadInfo.mThread);
12779 0 : MOZ_ASSERT(aThreadInfo.mRunnable);
12780 0 : MOZ_ASSERT(!mQueuedTransactions.IsEmpty());
12781 0 : MOZ_ASSERT(!mIdleThreads.Contains(aThreadInfo));
12782 :
12783 0 : AUTO_PROFILER_LABEL("ConnectionPool::ScheduleQueuedTransactions", STORAGE);
12784 :
12785 0 : mIdleThreads.InsertElementSorted(aThreadInfo);
12786 :
12787 0 : aThreadInfo.mRunnable = nullptr;
12788 0 : aThreadInfo.mThread = nullptr;
12789 :
12790 0 : uint32_t index = 0;
12791 0 : for (uint32_t count = mQueuedTransactions.Length(); index < count; index++) {
12792 0 : if (!ScheduleTransaction(mQueuedTransactions[index],
12793 : /* aFromQueuedTransactions */ true)) {
12794 0 : break;
12795 : }
12796 : }
12797 :
12798 0 : if (index) {
12799 0 : mQueuedTransactions.RemoveElementsAt(0, index);
12800 : }
12801 :
12802 0 : AdjustIdleTimer();
12803 0 : }
12804 :
12805 : void
12806 0 : ConnectionPool::NoteIdleDatabase(DatabaseInfo* aDatabaseInfo)
12807 : {
12808 0 : AssertIsOnOwningThread();
12809 0 : MOZ_ASSERT(aDatabaseInfo);
12810 0 : MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount());
12811 0 : MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread);
12812 0 : MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
12813 0 : MOZ_ASSERT(!mIdleDatabases.Contains(aDatabaseInfo));
12814 :
12815 0 : AUTO_PROFILER_LABEL("ConnectionPool::NoteIdleDatabase", STORAGE);
12816 :
12817 0 : const bool otherDatabasesWaiting = !mQueuedTransactions.IsEmpty();
12818 :
12819 0 : if (mShutdownRequested ||
12820 0 : otherDatabasesWaiting ||
12821 0 : aDatabaseInfo->mCloseOnIdle) {
12822 : // Make sure we close the connection if we're shutting down or giving the
12823 : // thread to another database.
12824 0 : CloseDatabase(aDatabaseInfo);
12825 :
12826 0 : if (otherDatabasesWaiting) {
12827 : // Let another database use this thread.
12828 0 : ScheduleQueuedTransactions(aDatabaseInfo->mThreadInfo);
12829 0 : } else if (mShutdownRequested) {
12830 : // If there are no other databases that need to run then we can shut this
12831 : // thread down immediately instead of going through the idle thread
12832 : // mechanism.
12833 0 : ShutdownThread(aDatabaseInfo->mThreadInfo);
12834 : }
12835 :
12836 0 : return;
12837 : }
12838 :
12839 0 : mIdleDatabases.InsertElementSorted(aDatabaseInfo);
12840 :
12841 0 : AdjustIdleTimer();
12842 : }
12843 :
12844 : void
12845 0 : ConnectionPool::NoteClosedDatabase(DatabaseInfo* aDatabaseInfo)
12846 : {
12847 0 : AssertIsOnOwningThread();
12848 0 : MOZ_ASSERT(aDatabaseInfo);
12849 0 : MOZ_ASSERT(aDatabaseInfo->mClosing);
12850 0 : MOZ_ASSERT(!mIdleDatabases.Contains(aDatabaseInfo));
12851 :
12852 0 : AUTO_PROFILER_LABEL("ConnectionPool::NoteClosedDatabase", STORAGE);
12853 :
12854 0 : aDatabaseInfo->mClosing = false;
12855 :
12856 : // Figure out what to do with this database's thread. It may have already been
12857 : // given to another database, in which case there's nothing to do here.
12858 : // Otherwise we prioritize the thread as follows:
12859 : // 1. Databases that haven't had an opportunity to run at all are highest
12860 : // priority. Those live in the |mQueuedTransactions| list.
12861 : // 2. If this database has additional transactions that were started after
12862 : // we began closing the connection then the thread can be reused for
12863 : // those transactions.
12864 : // 3. If we're shutting down then we can get rid of the thread.
12865 : // 4. Finally, if nothing above took the thread then we can add it to our
12866 : // list of idle threads. It may be reused or it may time out. If we have
12867 : // too many idle threads then we will shut down the oldest.
12868 0 : if (aDatabaseInfo->mThreadInfo.mThread) {
12869 0 : MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
12870 :
12871 0 : if (!mQueuedTransactions.IsEmpty()) {
12872 : // Give the thread to another database.
12873 0 : ScheduleQueuedTransactions(aDatabaseInfo->mThreadInfo);
12874 0 : } else if (!aDatabaseInfo->TotalTransactionCount()) {
12875 0 : if (mShutdownRequested) {
12876 0 : ShutdownThread(aDatabaseInfo->mThreadInfo);
12877 : } else {
12878 0 : MOZ_ASSERT(!mIdleThreads.Contains(aDatabaseInfo->mThreadInfo));
12879 :
12880 0 : mIdleThreads.InsertElementSorted(aDatabaseInfo->mThreadInfo);
12881 :
12882 0 : aDatabaseInfo->mThreadInfo.mRunnable = nullptr;
12883 0 : aDatabaseInfo->mThreadInfo.mThread = nullptr;
12884 :
12885 0 : if (mIdleThreads.Length() > kMaxIdleConnectionThreadCount) {
12886 0 : ShutdownThread(mIdleThreads[0].mThreadInfo);
12887 0 : mIdleThreads.RemoveElementAt(0);
12888 : }
12889 :
12890 0 : AdjustIdleTimer();
12891 : }
12892 : }
12893 : }
12894 :
12895 : // Schedule any transactions that were started while we were closing the
12896 : // connection.
12897 0 : if (aDatabaseInfo->TotalTransactionCount()) {
12898 : nsTArray<TransactionInfo*>& scheduledTransactions =
12899 0 : aDatabaseInfo->mTransactionsScheduledDuringClose;
12900 :
12901 0 : MOZ_ASSERT(!scheduledTransactions.IsEmpty());
12902 :
12903 0 : for (uint32_t index = 0, count = scheduledTransactions.Length();
12904 0 : index < count;
12905 : index++) {
12906 0 : Unused << ScheduleTransaction(scheduledTransactions[index],
12907 : /* aFromQueuedTransactions */ false);
12908 : }
12909 :
12910 0 : scheduledTransactions.Clear();
12911 :
12912 0 : return;
12913 : }
12914 :
12915 : // There are no more transactions and the connection has been closed. We're
12916 : // done with this database.
12917 : {
12918 0 : MutexAutoLock lock(mDatabasesMutex);
12919 :
12920 0 : mDatabases.Remove(aDatabaseInfo->mDatabaseId);
12921 : }
12922 :
12923 : #ifdef DEBUG
12924 : // That just deleted |aDatabaseInfo|.
12925 0 : aDatabaseInfo = nullptr;
12926 : #endif
12927 :
12928 : // See if we need to fire any complete callbacks now that the database is
12929 : // finished.
12930 0 : for (uint32_t index = 0;
12931 0 : index < mCompleteCallbacks.Length();
12932 : /* conditionally incremented */) {
12933 0 : if (MaybeFireCallback(mCompleteCallbacks[index])) {
12934 0 : mCompleteCallbacks.RemoveElementAt(index);
12935 : } else {
12936 0 : index++;
12937 : }
12938 : }
12939 :
12940 : // If that was the last database and we're supposed to be shutting down then
12941 : // we are finished.
12942 0 : if (mShutdownRequested && !mDatabases.Count()) {
12943 0 : MOZ_ASSERT(!mTransactions.Count());
12944 0 : Cleanup();
12945 : }
12946 : }
12947 :
12948 : bool
12949 0 : ConnectionPool::MaybeFireCallback(DatabasesCompleteCallback* aCallback)
12950 : {
12951 0 : AssertIsOnOwningThread();
12952 0 : MOZ_ASSERT(aCallback);
12953 0 : MOZ_ASSERT(!aCallback->mDatabaseIds.IsEmpty());
12954 0 : MOZ_ASSERT(aCallback->mCallback);
12955 :
12956 0 : AUTO_PROFILER_LABEL("ConnectionPool::MaybeFireCallback", STORAGE);
12957 :
12958 0 : for (uint32_t count = aCallback->mDatabaseIds.Length(), index = 0;
12959 0 : index < count;
12960 : index++) {
12961 0 : const nsCString& databaseId = aCallback->mDatabaseIds[index];
12962 0 : MOZ_ASSERT(!databaseId.IsEmpty());
12963 :
12964 0 : if (mDatabases.Get(databaseId)) {
12965 0 : return false;
12966 : }
12967 : }
12968 :
12969 0 : Unused << aCallback->mCallback->Run();
12970 0 : return true;
12971 : }
12972 :
12973 : void
12974 0 : ConnectionPool::PerformIdleDatabaseMaintenance(DatabaseInfo* aDatabaseInfo)
12975 : {
12976 0 : AssertIsOnOwningThread();
12977 0 : MOZ_ASSERT(aDatabaseInfo);
12978 0 : MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount());
12979 0 : MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread);
12980 0 : MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
12981 0 : MOZ_ASSERT(aDatabaseInfo->mIdle);
12982 0 : MOZ_ASSERT(!aDatabaseInfo->mCloseOnIdle);
12983 0 : MOZ_ASSERT(!aDatabaseInfo->mClosing);
12984 0 : MOZ_ASSERT(mIdleDatabases.Contains(aDatabaseInfo));
12985 0 : MOZ_ASSERT(!mDatabasesPerformingIdleMaintenance.Contains(aDatabaseInfo));
12986 :
12987 : nsCOMPtr<nsIRunnable> runnable =
12988 0 : new IdleConnectionRunnable(aDatabaseInfo, aDatabaseInfo->mNeedsCheckpoint);
12989 :
12990 0 : aDatabaseInfo->mNeedsCheckpoint = false;
12991 0 : aDatabaseInfo->mIdle = false;
12992 :
12993 0 : mDatabasesPerformingIdleMaintenance.AppendElement(aDatabaseInfo);
12994 :
12995 0 : MOZ_ALWAYS_SUCCEEDS(
12996 : aDatabaseInfo->mThreadInfo.mThread->Dispatch(runnable.forget(),
12997 : NS_DISPATCH_NORMAL));
12998 0 : }
12999 :
13000 : void
13001 0 : ConnectionPool::CloseDatabase(DatabaseInfo* aDatabaseInfo)
13002 : {
13003 0 : AssertIsOnOwningThread();
13004 0 : MOZ_ASSERT(aDatabaseInfo);
13005 0 : MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount());
13006 0 : MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread);
13007 0 : MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
13008 0 : MOZ_ASSERT(!aDatabaseInfo->mClosing);
13009 :
13010 0 : aDatabaseInfo->mIdle = false;
13011 0 : aDatabaseInfo->mNeedsCheckpoint = false;
13012 0 : aDatabaseInfo->mClosing = true;
13013 :
13014 0 : nsCOMPtr<nsIRunnable> runnable = new CloseConnectionRunnable(aDatabaseInfo);
13015 :
13016 0 : MOZ_ALWAYS_SUCCEEDS(
13017 : aDatabaseInfo->mThreadInfo.mThread->Dispatch(runnable.forget(),
13018 : NS_DISPATCH_NORMAL));
13019 0 : }
13020 :
13021 : bool
13022 0 : ConnectionPool::CloseDatabaseWhenIdleInternal(const nsACString& aDatabaseId)
13023 : {
13024 0 : AssertIsOnOwningThread();
13025 0 : MOZ_ASSERT(!aDatabaseId.IsEmpty());
13026 :
13027 0 : AUTO_PROFILER_LABEL("ConnectionPool::CloseDatabaseWhenIdleInternal", STORAGE);
13028 :
13029 0 : if (DatabaseInfo* dbInfo = mDatabases.Get(aDatabaseId)) {
13030 0 : if (mIdleDatabases.RemoveElement(dbInfo) ||
13031 0 : mDatabasesPerformingIdleMaintenance.RemoveElement(dbInfo)) {
13032 0 : CloseDatabase(dbInfo);
13033 0 : AdjustIdleTimer();
13034 : } else {
13035 0 : dbInfo->mCloseOnIdle = true;
13036 : }
13037 :
13038 0 : return true;
13039 : }
13040 :
13041 0 : return false;
13042 : }
13043 :
13044 0 : ConnectionPool::
13045 0 : ConnectionRunnable::ConnectionRunnable(DatabaseInfo* aDatabaseInfo)
13046 : : Runnable("dom::indexedDB::ConnectionPool::ConnectionRunnable")
13047 : , mDatabaseInfo(aDatabaseInfo)
13048 0 : , mOwningEventTarget(GetCurrentThreadEventTarget())
13049 : {
13050 0 : AssertIsOnBackgroundThread();
13051 0 : MOZ_ASSERT(aDatabaseInfo);
13052 0 : MOZ_ASSERT(aDatabaseInfo->mConnectionPool);
13053 0 : aDatabaseInfo->mConnectionPool->AssertIsOnOwningThread();
13054 0 : MOZ_ASSERT(mOwningEventTarget);
13055 0 : }
13056 :
13057 0 : NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::IdleConnectionRunnable,
13058 : ConnectionPool::ConnectionRunnable)
13059 :
13060 : NS_IMETHODIMP
13061 0 : ConnectionPool::
13062 : IdleConnectionRunnable::Run()
13063 : {
13064 0 : MOZ_ASSERT(mDatabaseInfo);
13065 0 : MOZ_ASSERT(!mDatabaseInfo->mIdle);
13066 :
13067 0 : nsCOMPtr<nsIEventTarget> owningThread;
13068 0 : mOwningEventTarget.swap(owningThread);
13069 :
13070 0 : if (owningThread) {
13071 0 : mDatabaseInfo->AssertIsOnConnectionThread();
13072 :
13073 : // The connection could be null if EnsureConnection() didn't run or was not
13074 : // successful in TransactionDatabaseOperationBase::RunOnConnectionThread().
13075 0 : if (mDatabaseInfo->mConnection) {
13076 0 : mDatabaseInfo->mConnection->DoIdleProcessing(mNeedsCheckpoint);
13077 :
13078 0 : MOZ_ALWAYS_SUCCEEDS(
13079 : owningThread->Dispatch(this, NS_DISPATCH_NORMAL));
13080 0 : return NS_OK;
13081 : }
13082 : }
13083 :
13084 0 : RefPtr<ConnectionPool> connectionPool = mDatabaseInfo->mConnectionPool;
13085 0 : MOZ_ASSERT(connectionPool);
13086 :
13087 0 : if (mDatabaseInfo->mClosing || mDatabaseInfo->TotalTransactionCount()) {
13088 0 : MOZ_ASSERT(!connectionPool->
13089 : mDatabasesPerformingIdleMaintenance.Contains(mDatabaseInfo));
13090 : } else {
13091 0 : MOZ_ALWAYS_TRUE(
13092 : connectionPool->
13093 : mDatabasesPerformingIdleMaintenance.RemoveElement(mDatabaseInfo));
13094 :
13095 0 : connectionPool->NoteIdleDatabase(mDatabaseInfo);
13096 : }
13097 :
13098 0 : return NS_OK;
13099 : }
13100 :
13101 0 : NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::CloseConnectionRunnable,
13102 : ConnectionPool::ConnectionRunnable)
13103 :
13104 : NS_IMETHODIMP
13105 0 : ConnectionPool::
13106 : CloseConnectionRunnable::Run()
13107 : {
13108 0 : MOZ_ASSERT(mDatabaseInfo);
13109 :
13110 0 : AUTO_PROFILER_LABEL("ConnectionPool::CloseConnectionRunnable::Run", STORAGE);
13111 :
13112 0 : if (mOwningEventTarget) {
13113 0 : MOZ_ASSERT(mDatabaseInfo->mClosing);
13114 :
13115 0 : nsCOMPtr<nsIEventTarget> owningThread;
13116 0 : mOwningEventTarget.swap(owningThread);
13117 :
13118 : // The connection could be null if EnsureConnection() didn't run or was not
13119 : // successful in TransactionDatabaseOperationBase::RunOnConnectionThread().
13120 0 : if (mDatabaseInfo->mConnection) {
13121 0 : mDatabaseInfo->AssertIsOnConnectionThread();
13122 :
13123 0 : mDatabaseInfo->mConnection->Close();
13124 :
13125 0 : IDB_DEBUG_LOG(("ConnectionPool closed connection 0x%p",
13126 : mDatabaseInfo->mConnection.get()));
13127 :
13128 0 : mDatabaseInfo->mConnection = nullptr;
13129 :
13130 : #ifdef DEBUG
13131 0 : mDatabaseInfo->mDEBUGConnectionThread = nullptr;
13132 : #endif
13133 : }
13134 :
13135 0 : MOZ_ALWAYS_SUCCEEDS(
13136 : owningThread->Dispatch(this, NS_DISPATCH_NORMAL));
13137 0 : return NS_OK;
13138 : }
13139 :
13140 0 : RefPtr<ConnectionPool> connectionPool = mDatabaseInfo->mConnectionPool;
13141 0 : MOZ_ASSERT(connectionPool);
13142 :
13143 0 : connectionPool->NoteClosedDatabase(mDatabaseInfo);
13144 0 : return NS_OK;
13145 : }
13146 :
13147 0 : ConnectionPool::
13148 : DatabaseInfo::DatabaseInfo(ConnectionPool* aConnectionPool,
13149 0 : const nsACString& aDatabaseId)
13150 : : mConnectionPool(aConnectionPool)
13151 : , mDatabaseId(aDatabaseId)
13152 : , mRunningWriteTransaction(nullptr)
13153 : , mReadTransactionCount(0)
13154 : , mWriteTransactionCount(0)
13155 : , mNeedsCheckpoint(false)
13156 : , mIdle(false)
13157 : , mCloseOnIdle(false)
13158 : , mClosing(false)
13159 : #ifdef DEBUG
13160 0 : , mDEBUGConnectionThread(nullptr)
13161 : #endif
13162 : {
13163 0 : AssertIsOnBackgroundThread();
13164 0 : MOZ_ASSERT(aConnectionPool);
13165 0 : aConnectionPool->AssertIsOnOwningThread();
13166 0 : MOZ_ASSERT(!aDatabaseId.IsEmpty());
13167 :
13168 0 : MOZ_COUNT_CTOR(ConnectionPool::DatabaseInfo);
13169 0 : }
13170 :
13171 0 : ConnectionPool::
13172 0 : DatabaseInfo::~DatabaseInfo()
13173 : {
13174 0 : AssertIsOnBackgroundThread();
13175 0 : MOZ_ASSERT(!mConnection);
13176 0 : MOZ_ASSERT(mScheduledWriteTransactions.IsEmpty());
13177 0 : MOZ_ASSERT(!mRunningWriteTransaction);
13178 0 : MOZ_ASSERT(!mThreadInfo.mThread);
13179 0 : MOZ_ASSERT(!mThreadInfo.mRunnable);
13180 0 : MOZ_ASSERT(!TotalTransactionCount());
13181 :
13182 0 : MOZ_COUNT_DTOR(ConnectionPool::DatabaseInfo);
13183 0 : }
13184 :
13185 0 : ConnectionPool::
13186 : DatabasesCompleteCallback::DatabasesCompleteCallback(
13187 : nsTArray<nsCString>&& aDatabaseIds,
13188 0 : nsIRunnable* aCallback)
13189 0 : : mDatabaseIds(Move(aDatabaseIds))
13190 0 : , mCallback(aCallback)
13191 : {
13192 0 : AssertIsOnBackgroundThread();
13193 0 : MOZ_ASSERT(!mDatabaseIds.IsEmpty());
13194 0 : MOZ_ASSERT(aCallback);
13195 :
13196 0 : MOZ_COUNT_CTOR(ConnectionPool::DatabasesCompleteCallback);
13197 0 : }
13198 :
13199 0 : ConnectionPool::
13200 0 : DatabasesCompleteCallback::~DatabasesCompleteCallback()
13201 : {
13202 0 : AssertIsOnBackgroundThread();
13203 :
13204 0 : MOZ_COUNT_DTOR(ConnectionPool::DatabasesCompleteCallback);
13205 0 : }
13206 :
13207 0 : ConnectionPool::FinishCallbackWrapper::FinishCallbackWrapper(
13208 : ConnectionPool* aConnectionPool,
13209 : uint64_t aTransactionId,
13210 0 : FinishCallback* aCallback)
13211 : : Runnable("dom::indexedDB::ConnectionPool::FinishCallbackWrapper")
13212 : , mConnectionPool(aConnectionPool)
13213 : , mCallback(aCallback)
13214 : , mOwningEventTarget(GetCurrentThreadEventTarget())
13215 : , mTransactionId(aTransactionId)
13216 0 : , mHasRunOnce(false)
13217 : {
13218 0 : AssertIsOnBackgroundThread();
13219 0 : MOZ_ASSERT(aConnectionPool);
13220 0 : MOZ_ASSERT(aCallback);
13221 0 : MOZ_ASSERT(mOwningEventTarget);
13222 0 : }
13223 :
13224 0 : ConnectionPool::
13225 0 : FinishCallbackWrapper::~FinishCallbackWrapper()
13226 : {
13227 0 : MOZ_ASSERT(!mConnectionPool);
13228 0 : MOZ_ASSERT(!mCallback);
13229 0 : }
13230 :
13231 0 : NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::FinishCallbackWrapper, Runnable)
13232 :
13233 : nsresult
13234 0 : ConnectionPool::
13235 : FinishCallbackWrapper::Run()
13236 : {
13237 0 : MOZ_ASSERT(mConnectionPool);
13238 0 : MOZ_ASSERT(mCallback);
13239 0 : MOZ_ASSERT(mOwningEventTarget);
13240 :
13241 0 : AUTO_PROFILER_LABEL("ConnectionPool::FinishCallbackWrapper::Run", STORAGE);
13242 :
13243 0 : if (!mHasRunOnce) {
13244 0 : MOZ_ASSERT(!IsOnBackgroundThread());
13245 :
13246 0 : mHasRunOnce = true;
13247 :
13248 0 : Unused << mCallback->Run();
13249 :
13250 0 : MOZ_ALWAYS_SUCCEEDS(
13251 : mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
13252 :
13253 0 : return NS_OK;
13254 : }
13255 :
13256 0 : mConnectionPool->AssertIsOnOwningThread();
13257 0 : MOZ_ASSERT(mHasRunOnce);
13258 :
13259 0 : RefPtr<ConnectionPool> connectionPool = Move(mConnectionPool);
13260 0 : RefPtr<FinishCallback> callback = Move(mCallback);
13261 :
13262 0 : callback->TransactionFinishedBeforeUnblock();
13263 :
13264 0 : connectionPool->NoteFinishedTransaction(mTransactionId);
13265 :
13266 0 : callback->TransactionFinishedAfterUnblock();
13267 :
13268 0 : return NS_OK;
13269 : }
13270 :
13271 : uint32_t ConnectionPool::ThreadRunnable::sNextSerialNumber = 0;
13272 :
13273 0 : ConnectionPool::ThreadRunnable::ThreadRunnable()
13274 : : Runnable("dom::indexedDB::ConnectionPool::ThreadRunnable")
13275 0 : , mSerialNumber(++sNextSerialNumber)
13276 : , mFirstRun(true)
13277 0 : , mContinueRunning(true)
13278 : {
13279 0 : AssertIsOnBackgroundThread();
13280 0 : }
13281 :
13282 0 : ConnectionPool::
13283 0 : ThreadRunnable::~ThreadRunnable()
13284 : {
13285 0 : MOZ_ASSERT(!mFirstRun);
13286 0 : MOZ_ASSERT(!mContinueRunning);
13287 0 : }
13288 :
13289 0 : NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::ThreadRunnable, Runnable)
13290 :
13291 : nsresult
13292 0 : ConnectionPool::
13293 : ThreadRunnable::Run()
13294 : {
13295 0 : MOZ_ASSERT(!IsOnBackgroundThread());
13296 0 : MOZ_ASSERT(mContinueRunning);
13297 :
13298 0 : if (!mFirstRun) {
13299 0 : mContinueRunning = false;
13300 0 : return NS_OK;
13301 : }
13302 :
13303 0 : mFirstRun = false;
13304 :
13305 : {
13306 : // Scope for the profiler label.
13307 0 : AUTO_PROFILER_LABEL("ConnectionPool::ThreadRunnable::Run", STORAGE);
13308 :
13309 0 : DebugOnly<nsIThread*> currentThread = NS_GetCurrentThread();
13310 0 : MOZ_ASSERT(currentThread);
13311 :
13312 : #ifdef DEBUG
13313 : if (kDEBUGTransactionThreadPriority !=
13314 : nsISupportsPriority::PRIORITY_NORMAL) {
13315 : NS_WARNING("ConnectionPool thread debugging enabled, priority has been "
13316 : "modified!");
13317 :
13318 : nsCOMPtr<nsISupportsPriority> thread = do_QueryInterface(currentThread);
13319 : MOZ_ASSERT(thread);
13320 :
13321 : MOZ_ALWAYS_SUCCEEDS(
13322 : thread->SetPriority(kDEBUGTransactionThreadPriority));
13323 : }
13324 :
13325 : if (kDEBUGTransactionThreadSleepMS) {
13326 : NS_WARNING("TransactionThreadPool thread debugging enabled, sleeping "
13327 : "after every event!");
13328 : }
13329 : #endif // DEBUG
13330 :
13331 0 : DebugOnly<bool> b = SpinEventLoopUntil([&]() -> bool {
13332 0 : if (!mContinueRunning) {
13333 0 : return true;
13334 : }
13335 :
13336 : #ifdef DEBUG
13337 : if (kDEBUGTransactionThreadSleepMS) {
13338 : MOZ_ALWAYS_TRUE(
13339 : PR_Sleep(PR_MillisecondsToInterval(kDEBUGTransactionThreadSleepMS)) ==
13340 : PR_SUCCESS);
13341 : }
13342 : #endif // DEBUG
13343 :
13344 0 : return false;
13345 0 : });
13346 : // MSVC can't stringify lambdas, so we have to separate the expression
13347 : // generating the value from the assert itself.
13348 : #if DEBUG
13349 0 : MOZ_ALWAYS_TRUE(b);
13350 : #endif
13351 : }
13352 :
13353 0 : return NS_OK;
13354 : }
13355 :
13356 0 : ConnectionPool::
13357 0 : ThreadInfo::ThreadInfo()
13358 : {
13359 0 : AssertIsOnBackgroundThread();
13360 :
13361 0 : MOZ_COUNT_CTOR(ConnectionPool::ThreadInfo);
13362 0 : }
13363 :
13364 0 : ConnectionPool::
13365 0 : ThreadInfo::ThreadInfo(const ThreadInfo& aOther)
13366 : : mThread(aOther.mThread)
13367 0 : , mRunnable(aOther.mRunnable)
13368 : {
13369 0 : AssertIsOnBackgroundThread();
13370 0 : MOZ_ASSERT(aOther.mThread);
13371 0 : MOZ_ASSERT(aOther.mRunnable);
13372 :
13373 0 : MOZ_COUNT_CTOR(ConnectionPool::ThreadInfo);
13374 0 : }
13375 :
13376 0 : ConnectionPool::
13377 0 : ThreadInfo::~ThreadInfo()
13378 : {
13379 0 : AssertIsOnBackgroundThread();
13380 :
13381 0 : MOZ_COUNT_DTOR(ConnectionPool::ThreadInfo);
13382 0 : }
13383 :
13384 0 : ConnectionPool::
13385 0 : IdleResource::IdleResource(const TimeStamp& aIdleTime)
13386 0 : : mIdleTime(aIdleTime)
13387 : {
13388 0 : AssertIsOnBackgroundThread();
13389 0 : MOZ_ASSERT(!aIdleTime.IsNull());
13390 :
13391 0 : MOZ_COUNT_CTOR(ConnectionPool::IdleResource);
13392 0 : }
13393 :
13394 0 : ConnectionPool::
13395 0 : IdleResource::~IdleResource()
13396 : {
13397 0 : AssertIsOnBackgroundThread();
13398 :
13399 0 : MOZ_COUNT_DTOR(ConnectionPool::IdleResource);
13400 0 : }
13401 :
13402 0 : ConnectionPool::
13403 0 : IdleDatabaseInfo::IdleDatabaseInfo(DatabaseInfo* aDatabaseInfo)
13404 0 : : IdleResource(TimeStamp::NowLoRes() +
13405 0 : (aDatabaseInfo->mIdle ?
13406 : TimeDuration::FromMilliseconds(kConnectionIdleMaintenanceMS) :
13407 : TimeDuration::FromMilliseconds(kConnectionIdleCloseMS)))
13408 0 : , mDatabaseInfo(aDatabaseInfo)
13409 : {
13410 0 : AssertIsOnBackgroundThread();
13411 0 : MOZ_ASSERT(aDatabaseInfo);
13412 :
13413 0 : MOZ_COUNT_CTOR(ConnectionPool::IdleDatabaseInfo);
13414 0 : }
13415 :
13416 0 : ConnectionPool::
13417 0 : IdleDatabaseInfo::~IdleDatabaseInfo()
13418 : {
13419 0 : AssertIsOnBackgroundThread();
13420 0 : MOZ_ASSERT(mDatabaseInfo);
13421 :
13422 0 : MOZ_COUNT_DTOR(ConnectionPool::IdleDatabaseInfo);
13423 0 : }
13424 :
13425 0 : ConnectionPool::
13426 0 : IdleThreadInfo::IdleThreadInfo(const ThreadInfo& aThreadInfo)
13427 0 : : IdleResource(TimeStamp::NowLoRes() +
13428 0 : TimeDuration::FromMilliseconds(kConnectionThreadIdleMS))
13429 0 : , mThreadInfo(aThreadInfo)
13430 : {
13431 0 : AssertIsOnBackgroundThread();
13432 0 : MOZ_ASSERT(aThreadInfo.mRunnable);
13433 0 : MOZ_ASSERT(aThreadInfo.mThread);
13434 :
13435 0 : MOZ_COUNT_CTOR(ConnectionPool::IdleThreadInfo);
13436 0 : }
13437 :
13438 0 : ConnectionPool::
13439 0 : IdleThreadInfo::~IdleThreadInfo()
13440 : {
13441 0 : AssertIsOnBackgroundThread();
13442 :
13443 0 : MOZ_COUNT_DTOR(ConnectionPool::IdleThreadInfo);
13444 0 : }
13445 :
13446 0 : ConnectionPool::
13447 : TransactionInfo::TransactionInfo(
13448 : DatabaseInfo* aDatabaseInfo,
13449 : const nsID& aBackgroundChildLoggingId,
13450 : const nsACString& aDatabaseId,
13451 : uint64_t aTransactionId,
13452 : int64_t aLoggingSerialNumber,
13453 : const nsTArray<nsString>& aObjectStoreNames,
13454 : bool aIsWriteTransaction,
13455 0 : TransactionDatabaseOperationBase* aTransactionOp)
13456 : : mDatabaseInfo(aDatabaseInfo)
13457 : , mBackgroundChildLoggingId(aBackgroundChildLoggingId)
13458 : , mDatabaseId(aDatabaseId)
13459 : , mTransactionId(aTransactionId)
13460 : , mLoggingSerialNumber(aLoggingSerialNumber)
13461 : , mObjectStoreNames(aObjectStoreNames)
13462 : , mIsWriteTransaction(aIsWriteTransaction)
13463 : , mRunning(false)
13464 : #ifdef DEBUG
13465 0 : , mFinished(false)
13466 : #endif
13467 : {
13468 0 : AssertIsOnBackgroundThread();
13469 0 : MOZ_ASSERT(aDatabaseInfo);
13470 0 : aDatabaseInfo->mConnectionPool->AssertIsOnOwningThread();
13471 :
13472 0 : MOZ_COUNT_CTOR(ConnectionPool::TransactionInfo);
13473 :
13474 0 : if (aTransactionOp) {
13475 0 : mQueuedRunnables.AppendElement(aTransactionOp);
13476 : }
13477 0 : }
13478 :
13479 0 : ConnectionPool::
13480 0 : TransactionInfo::~TransactionInfo()
13481 : {
13482 0 : AssertIsOnBackgroundThread();
13483 0 : MOZ_ASSERT(!mBlockedOn.Count());
13484 0 : MOZ_ASSERT(mQueuedRunnables.IsEmpty());
13485 0 : MOZ_ASSERT(!mRunning);
13486 0 : MOZ_ASSERT(mFinished);
13487 :
13488 0 : MOZ_COUNT_DTOR(ConnectionPool::TransactionInfo);
13489 0 : }
13490 :
13491 : void
13492 0 : ConnectionPool::
13493 : TransactionInfo::AddBlockingTransaction(TransactionInfo* aTransactionInfo)
13494 : {
13495 0 : AssertIsOnBackgroundThread();
13496 0 : MOZ_ASSERT(aTransactionInfo);
13497 :
13498 0 : if (!mBlocking.Contains(aTransactionInfo)) {
13499 0 : mBlocking.PutEntry(aTransactionInfo);
13500 0 : mBlockingOrdered.AppendElement(aTransactionInfo);
13501 : }
13502 0 : }
13503 :
13504 : void
13505 0 : ConnectionPool::
13506 : TransactionInfo::RemoveBlockingTransactions()
13507 : {
13508 0 : AssertIsOnBackgroundThread();
13509 :
13510 0 : for (uint32_t index = 0, count = mBlockingOrdered.Length();
13511 0 : index < count;
13512 : index++) {
13513 0 : TransactionInfo* blockedInfo = mBlockingOrdered[index];
13514 0 : MOZ_ASSERT(blockedInfo);
13515 :
13516 0 : blockedInfo->MaybeUnblock(this);
13517 : }
13518 :
13519 0 : mBlocking.Clear();
13520 0 : mBlockingOrdered.Clear();
13521 0 : }
13522 :
13523 : void
13524 0 : ConnectionPool::
13525 : TransactionInfo::MaybeUnblock(TransactionInfo* aTransactionInfo)
13526 : {
13527 0 : AssertIsOnBackgroundThread();
13528 0 : MOZ_ASSERT(mBlockedOn.Contains(aTransactionInfo));
13529 :
13530 0 : mBlockedOn.RemoveEntry(aTransactionInfo);
13531 0 : if (!mBlockedOn.Count()) {
13532 0 : MOZ_ASSERT(mDatabaseInfo);
13533 :
13534 0 : ConnectionPool* connectionPool = mDatabaseInfo->mConnectionPool;
13535 0 : MOZ_ASSERT(connectionPool);
13536 0 : connectionPool->AssertIsOnOwningThread();
13537 :
13538 : Unused <<
13539 0 : connectionPool->ScheduleTransaction(this,
13540 : /* aFromQueuedTransactions */ false);
13541 : }
13542 0 : }
13543 :
13544 0 : ConnectionPool::
13545 0 : TransactionInfoPair::TransactionInfoPair()
13546 0 : : mLastBlockingReads(nullptr)
13547 : {
13548 0 : AssertIsOnBackgroundThread();
13549 :
13550 0 : MOZ_COUNT_CTOR(ConnectionPool::TransactionInfoPair);
13551 0 : }
13552 :
13553 0 : ConnectionPool::
13554 0 : TransactionInfoPair::~TransactionInfoPair()
13555 : {
13556 0 : AssertIsOnBackgroundThread();
13557 :
13558 0 : MOZ_COUNT_DTOR(ConnectionPool::TransactionInfoPair);
13559 0 : }
13560 :
13561 : /*******************************************************************************
13562 : * Metadata classes
13563 : ******************************************************************************/
13564 :
13565 : bool
13566 0 : FullObjectStoreMetadata::HasLiveIndexes() const
13567 : {
13568 0 : AssertIsOnBackgroundThread();
13569 :
13570 0 : for (auto iter = mIndexes.ConstIter(); !iter.Done(); iter.Next()) {
13571 0 : if (!iter.Data()->mDeleted) {
13572 0 : return true;
13573 : }
13574 : }
13575 :
13576 0 : return false;
13577 : }
13578 :
13579 : already_AddRefed<FullDatabaseMetadata>
13580 0 : FullDatabaseMetadata::Duplicate() const
13581 : {
13582 0 : AssertIsOnBackgroundThread();
13583 :
13584 : // FullDatabaseMetadata contains two hash tables of pointers that we need to
13585 : // duplicate so we can't just use the copy constructor.
13586 : RefPtr<FullDatabaseMetadata> newMetadata =
13587 0 : new FullDatabaseMetadata(mCommonMetadata);
13588 :
13589 0 : newMetadata->mDatabaseId = mDatabaseId;
13590 0 : newMetadata->mFilePath = mFilePath;
13591 0 : newMetadata->mNextObjectStoreId = mNextObjectStoreId;
13592 0 : newMetadata->mNextIndexId = mNextIndexId;
13593 :
13594 0 : for (auto iter = mObjectStores.ConstIter(); !iter.Done(); iter.Next()) {
13595 0 : auto key = iter.Key();
13596 0 : auto value = iter.Data();
13597 :
13598 : RefPtr<FullObjectStoreMetadata> newOSMetadata =
13599 0 : new FullObjectStoreMetadata();
13600 :
13601 0 : newOSMetadata->mCommonMetadata = value->mCommonMetadata;
13602 0 : newOSMetadata->mNextAutoIncrementId = value->mNextAutoIncrementId;
13603 0 : newOSMetadata->mCommittedAutoIncrementId = value->mCommittedAutoIncrementId;
13604 :
13605 0 : for (auto iter = value->mIndexes.ConstIter(); !iter.Done(); iter.Next()) {
13606 0 : auto key = iter.Key();
13607 0 : auto value = iter.Data();
13608 :
13609 0 : RefPtr<FullIndexMetadata> newIndexMetadata = new FullIndexMetadata();
13610 :
13611 0 : newIndexMetadata->mCommonMetadata = value->mCommonMetadata;
13612 :
13613 0 : if (NS_WARN_IF(!newOSMetadata->mIndexes.Put(key, newIndexMetadata,
13614 : fallible))) {
13615 0 : return nullptr;
13616 : }
13617 : }
13618 :
13619 0 : MOZ_ASSERT(value->mIndexes.Count() == newOSMetadata->mIndexes.Count());
13620 :
13621 0 : if (NS_WARN_IF(!newMetadata->mObjectStores.Put(key, newOSMetadata,
13622 : fallible))) {
13623 0 : return nullptr;
13624 : }
13625 : }
13626 :
13627 0 : MOZ_ASSERT(mObjectStores.Count() == newMetadata->mObjectStores.Count());
13628 :
13629 0 : return newMetadata.forget();
13630 : }
13631 :
13632 0 : DatabaseLoggingInfo::~DatabaseLoggingInfo()
13633 : {
13634 0 : AssertIsOnBackgroundThread();
13635 :
13636 0 : if (gLoggingInfoHashtable) {
13637 : const nsID& backgroundChildLoggingId =
13638 0 : mLoggingInfo.backgroundChildLoggingId();
13639 :
13640 0 : MOZ_ASSERT(gLoggingInfoHashtable->Get(backgroundChildLoggingId) == this);
13641 :
13642 0 : gLoggingInfoHashtable->Remove(backgroundChildLoggingId);
13643 : }
13644 0 : }
13645 :
13646 : /*******************************************************************************
13647 : * Factory
13648 : ******************************************************************************/
13649 :
13650 0 : Factory::Factory(already_AddRefed<DatabaseLoggingInfo> aLoggingInfo)
13651 0 : : mLoggingInfo(Move(aLoggingInfo))
13652 : #ifdef DEBUG
13653 0 : , mActorDestroyed(false)
13654 : #endif
13655 : {
13656 0 : AssertIsOnBackgroundThread();
13657 0 : MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
13658 0 : }
13659 :
13660 0 : Factory::~Factory()
13661 : {
13662 0 : MOZ_ASSERT(mActorDestroyed);
13663 0 : }
13664 :
13665 : // static
13666 : already_AddRefed<Factory>
13667 0 : Factory::Create(const LoggingInfo& aLoggingInfo)
13668 : {
13669 0 : AssertIsOnBackgroundThread();
13670 0 : MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
13671 :
13672 : // Balanced in ActoryDestroy().
13673 0 : IncreaseBusyCount();
13674 :
13675 0 : MOZ_ASSERT(gLoggingInfoHashtable);
13676 : RefPtr<DatabaseLoggingInfo> loggingInfo =
13677 0 : gLoggingInfoHashtable->Get(aLoggingInfo.backgroundChildLoggingId());
13678 0 : if (loggingInfo) {
13679 0 : MOZ_ASSERT(aLoggingInfo.backgroundChildLoggingId() == loggingInfo->Id());
13680 : #if !DISABLE_ASSERTS_FOR_FUZZING
13681 0 : NS_WARNING_ASSERTION(
13682 : aLoggingInfo.nextTransactionSerialNumber() ==
13683 : loggingInfo->mLoggingInfo.nextTransactionSerialNumber(),
13684 : "NextTransactionSerialNumber doesn't match!");
13685 0 : NS_WARNING_ASSERTION(
13686 : aLoggingInfo.nextVersionChangeTransactionSerialNumber() ==
13687 : loggingInfo->mLoggingInfo.
13688 : nextVersionChangeTransactionSerialNumber(),
13689 : "NextVersionChangeTransactionSerialNumber doesn't match!");
13690 0 : NS_WARNING_ASSERTION(
13691 : aLoggingInfo.nextRequestSerialNumber() ==
13692 : loggingInfo->mLoggingInfo.nextRequestSerialNumber(),
13693 : "NextRequestSerialNumber doesn't match!");
13694 : #endif // !DISABLE_ASSERTS_FOR_FUZZING
13695 : } else {
13696 0 : loggingInfo = new DatabaseLoggingInfo(aLoggingInfo);
13697 0 : gLoggingInfoHashtable->Put(aLoggingInfo.backgroundChildLoggingId(),
13698 0 : loggingInfo);
13699 : }
13700 :
13701 0 : RefPtr<Factory> actor = new Factory(loggingInfo.forget());
13702 :
13703 0 : return actor.forget();
13704 : }
13705 :
13706 : void
13707 0 : Factory::ActorDestroy(ActorDestroyReason aWhy)
13708 : {
13709 0 : AssertIsOnBackgroundThread();
13710 0 : MOZ_ASSERT(!mActorDestroyed);
13711 :
13712 : #ifdef DEBUG
13713 0 : mActorDestroyed = true;
13714 : #endif
13715 :
13716 : // Match the IncreaseBusyCount in Create().
13717 0 : DecreaseBusyCount();
13718 0 : }
13719 :
13720 : mozilla::ipc::IPCResult
13721 0 : Factory::RecvDeleteMe()
13722 : {
13723 0 : AssertIsOnBackgroundThread();
13724 0 : MOZ_ASSERT(!mActorDestroyed);
13725 :
13726 0 : IProtocol* mgr = Manager();
13727 0 : if (!PBackgroundIDBFactoryParent::Send__delete__(this)) {
13728 0 : return IPC_FAIL_NO_REASON(mgr);
13729 : }
13730 0 : return IPC_OK();
13731 : }
13732 :
13733 : mozilla::ipc::IPCResult
13734 0 : Factory::RecvIncrementLoggingRequestSerialNumber()
13735 : {
13736 0 : AssertIsOnBackgroundThread();
13737 0 : MOZ_ASSERT(mLoggingInfo);
13738 :
13739 0 : mLoggingInfo->NextRequestSN();
13740 0 : return IPC_OK();
13741 : }
13742 :
13743 : PBackgroundIDBFactoryRequestParent*
13744 0 : Factory::AllocPBackgroundIDBFactoryRequestParent(
13745 : const FactoryRequestParams& aParams)
13746 : {
13747 0 : AssertIsOnBackgroundThread();
13748 0 : MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
13749 :
13750 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
13751 0 : return nullptr;
13752 : }
13753 :
13754 : const CommonFactoryRequestParams* commonParams;
13755 :
13756 0 : switch (aParams.type()) {
13757 : case FactoryRequestParams::TOpenDatabaseRequestParams: {
13758 : const OpenDatabaseRequestParams& params =
13759 0 : aParams.get_OpenDatabaseRequestParams();
13760 0 : commonParams = ¶ms.commonParams();
13761 0 : break;
13762 : }
13763 :
13764 : case FactoryRequestParams::TDeleteDatabaseRequestParams: {
13765 : const DeleteDatabaseRequestParams& params =
13766 0 : aParams.get_DeleteDatabaseRequestParams();
13767 0 : commonParams = ¶ms.commonParams();
13768 0 : break;
13769 : }
13770 :
13771 : default:
13772 0 : MOZ_CRASH("Should never get here!");
13773 : }
13774 :
13775 0 : MOZ_ASSERT(commonParams);
13776 :
13777 0 : const DatabaseMetadata& metadata = commonParams->metadata();
13778 0 : if (NS_WARN_IF(metadata.persistenceType() != PERSISTENCE_TYPE_PERSISTENT &&
13779 : metadata.persistenceType() != PERSISTENCE_TYPE_TEMPORARY &&
13780 : metadata.persistenceType() != PERSISTENCE_TYPE_DEFAULT)) {
13781 0 : ASSERT_UNLESS_FUZZING();
13782 : return nullptr;
13783 : }
13784 :
13785 0 : const PrincipalInfo& principalInfo = commonParams->principalInfo();
13786 0 : if (NS_WARN_IF(principalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
13787 0 : ASSERT_UNLESS_FUZZING();
13788 : return nullptr;
13789 : }
13790 :
13791 0 : if (NS_WARN_IF(principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo &&
13792 : metadata.persistenceType() != PERSISTENCE_TYPE_PERSISTENT)) {
13793 0 : ASSERT_UNLESS_FUZZING();
13794 : return nullptr;
13795 : }
13796 :
13797 : RefPtr<ContentParent> contentParent =
13798 0 : BackgroundParent::GetContentParent(Manager());
13799 :
13800 0 : RefPtr<FactoryOp> actor;
13801 0 : if (aParams.type() == FactoryRequestParams::TOpenDatabaseRequestParams) {
13802 : actor = new OpenDatabaseOp(this,
13803 0 : contentParent.forget(),
13804 0 : *commonParams);
13805 : } else {
13806 0 : actor = new DeleteDatabaseOp(this, contentParent.forget(), *commonParams);
13807 : }
13808 :
13809 : // Transfer ownership to IPDL.
13810 0 : return actor.forget().take();
13811 : }
13812 :
13813 : mozilla::ipc::IPCResult
13814 0 : Factory::RecvPBackgroundIDBFactoryRequestConstructor(
13815 : PBackgroundIDBFactoryRequestParent* aActor,
13816 : const FactoryRequestParams& aParams)
13817 : {
13818 0 : AssertIsOnBackgroundThread();
13819 0 : MOZ_ASSERT(aActor);
13820 0 : MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
13821 0 : MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
13822 :
13823 0 : auto* op = static_cast<FactoryOp*>(aActor);
13824 :
13825 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(op));
13826 0 : return IPC_OK();
13827 : }
13828 :
13829 : bool
13830 0 : Factory::DeallocPBackgroundIDBFactoryRequestParent(
13831 : PBackgroundIDBFactoryRequestParent* aActor)
13832 : {
13833 0 : AssertIsOnBackgroundThread();
13834 0 : MOZ_ASSERT(aActor);
13835 :
13836 : // Transfer ownership back from IPDL.
13837 0 : RefPtr<FactoryOp> op = dont_AddRef(static_cast<FactoryOp*>(aActor));
13838 0 : return true;
13839 : }
13840 :
13841 : PBackgroundIDBDatabaseParent*
13842 0 : Factory::AllocPBackgroundIDBDatabaseParent(
13843 : const DatabaseSpec& aSpec,
13844 : PBackgroundIDBFactoryRequestParent* aRequest)
13845 : {
13846 0 : MOZ_CRASH("PBackgroundIDBDatabaseParent actors should be constructed "
13847 : "manually!");
13848 : }
13849 :
13850 : bool
13851 0 : Factory::DeallocPBackgroundIDBDatabaseParent(
13852 : PBackgroundIDBDatabaseParent* aActor)
13853 : {
13854 0 : AssertIsOnBackgroundThread();
13855 0 : MOZ_ASSERT(aActor);
13856 :
13857 0 : RefPtr<Database> database = dont_AddRef(static_cast<Database*>(aActor));
13858 0 : return true;
13859 : }
13860 :
13861 : /*******************************************************************************
13862 : * WaitForTransactionsHelper
13863 : ******************************************************************************/
13864 :
13865 : void
13866 0 : WaitForTransactionsHelper::WaitForTransactions()
13867 : {
13868 0 : MOZ_ASSERT(mState == State::Initial);
13869 :
13870 0 : Unused << this->Run();
13871 0 : }
13872 :
13873 : void
13874 0 : WaitForTransactionsHelper::MaybeWaitForTransactions()
13875 : {
13876 0 : AssertIsOnBackgroundThread();
13877 0 : MOZ_ASSERT(mState == State::Initial);
13878 :
13879 0 : RefPtr<ConnectionPool> connectionPool = gConnectionPool.get();
13880 0 : if (connectionPool) {
13881 0 : nsTArray<nsCString> ids(1);
13882 0 : ids.AppendElement(mDatabaseId);
13883 :
13884 0 : mState = State::WaitingForTransactions;
13885 :
13886 0 : connectionPool->WaitForDatabasesToComplete(Move(ids), this);
13887 0 : return;
13888 : }
13889 :
13890 0 : MaybeWaitForFileHandles();
13891 : }
13892 :
13893 : void
13894 0 : WaitForTransactionsHelper::MaybeWaitForFileHandles()
13895 : {
13896 0 : AssertIsOnBackgroundThread();
13897 0 : MOZ_ASSERT(mState == State::Initial || mState == State::WaitingForTransactions);
13898 :
13899 : RefPtr<FileHandleThreadPool> fileHandleThreadPool =
13900 0 : gFileHandleThreadPool.get();
13901 0 : if (fileHandleThreadPool) {
13902 0 : nsTArray<nsCString> ids(1);
13903 0 : ids.AppendElement(mDatabaseId);
13904 :
13905 0 : mState = State::WaitingForFileHandles;
13906 :
13907 0 : fileHandleThreadPool->WaitForDirectoriesToComplete(Move(ids), this);
13908 0 : return;
13909 : }
13910 :
13911 0 : CallCallback();
13912 : }
13913 :
13914 : void
13915 0 : WaitForTransactionsHelper::CallCallback()
13916 : {
13917 0 : AssertIsOnBackgroundThread();
13918 0 : MOZ_ASSERT(mState == State::Initial ||
13919 : mState == State::WaitingForTransactions ||
13920 : mState == State::WaitingForFileHandles);
13921 :
13922 0 : nsCOMPtr<nsIRunnable> callback;
13923 0 : mCallback.swap(callback);
13924 :
13925 0 : callback->Run();
13926 :
13927 0 : mState = State::Complete;
13928 0 : }
13929 :
13930 0 : NS_IMPL_ISUPPORTS_INHERITED0(WaitForTransactionsHelper, Runnable)
13931 :
13932 : NS_IMETHODIMP
13933 0 : WaitForTransactionsHelper::Run()
13934 : {
13935 0 : MOZ_ASSERT(mState != State::Complete);
13936 0 : MOZ_ASSERT(mCallback);
13937 :
13938 0 : switch (mState) {
13939 : case State::Initial:
13940 0 : MaybeWaitForTransactions();
13941 0 : break;
13942 :
13943 : case State::WaitingForTransactions:
13944 0 : MaybeWaitForFileHandles();
13945 0 : break;
13946 :
13947 : case State::WaitingForFileHandles:
13948 0 : CallCallback();
13949 0 : break;
13950 :
13951 : default:
13952 0 : MOZ_CRASH("Should never get here!");
13953 : }
13954 :
13955 0 : return NS_OK;
13956 : }
13957 :
13958 : /*******************************************************************************
13959 : * Database
13960 : ******************************************************************************/
13961 :
13962 0 : Database::Database(Factory* aFactory,
13963 : const PrincipalInfo& aPrincipalInfo,
13964 : const Maybe<ContentParentId>& aOptionalContentParentId,
13965 : const nsACString& aGroup,
13966 : const nsACString& aOrigin,
13967 : uint32_t aTelemetryId,
13968 : FullDatabaseMetadata* aMetadata,
13969 : FileManager* aFileManager,
13970 : already_AddRefed<DirectoryLock> aDirectoryLock,
13971 : bool aFileHandleDisabled,
13972 0 : bool aChromeWriteAccessAllowed)
13973 : : mFactory(aFactory)
13974 : , mMetadata(aMetadata)
13975 : , mFileManager(aFileManager)
13976 0 : , mDirectoryLock(Move(aDirectoryLock))
13977 : , mPrincipalInfo(aPrincipalInfo)
13978 : , mOptionalContentParentId(aOptionalContentParentId)
13979 : , mGroup(aGroup)
13980 : , mOrigin(aOrigin)
13981 : , mId(aMetadata->mDatabaseId)
13982 : , mFilePath(aMetadata->mFilePath)
13983 : , mActiveMutableFileCount(0)
13984 : , mTelemetryId(aTelemetryId)
13985 0 : , mPersistenceType(aMetadata->mCommonMetadata.persistenceType())
13986 : , mFileHandleDisabled(aFileHandleDisabled)
13987 : , mChromeWriteAccessAllowed(aChromeWriteAccessAllowed)
13988 : , mClosed(false)
13989 : , mInvalidated(false)
13990 : , mActorWasAlive(false)
13991 : , mActorDestroyed(false)
13992 : , mMetadataCleanedUp(false)
13993 : #ifdef DEBUG
13994 0 : , mAllBlobsUnmapped(false)
13995 : #endif
13996 : {
13997 0 : AssertIsOnBackgroundThread();
13998 0 : MOZ_ASSERT(aFactory);
13999 0 : MOZ_ASSERT(aMetadata);
14000 0 : MOZ_ASSERT(aFileManager);
14001 0 : MOZ_ASSERT_IF(aChromeWriteAccessAllowed,
14002 : aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo);
14003 0 : }
14004 :
14005 : void
14006 0 : Database::Invalidate()
14007 : {
14008 0 : AssertIsOnBackgroundThread();
14009 :
14010 : class MOZ_STACK_CLASS Helper final
14011 : {
14012 : public:
14013 : static bool
14014 0 : InvalidateTransactions(nsTHashtable<nsPtrHashKey<TransactionBase>>& aTable)
14015 : {
14016 0 : AssertIsOnBackgroundThread();
14017 :
14018 0 : const uint32_t count = aTable.Count();
14019 0 : if (!count) {
14020 0 : return true;
14021 : }
14022 :
14023 0 : FallibleTArray<RefPtr<TransactionBase>> transactions;
14024 0 : if (NS_WARN_IF(!transactions.SetCapacity(count, fallible))) {
14025 0 : return false;
14026 : }
14027 :
14028 0 : for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
14029 0 : if (NS_WARN_IF(!transactions.AppendElement(iter.Get()->GetKey(),
14030 : fallible))) {
14031 0 : return false;
14032 : }
14033 : }
14034 :
14035 0 : if (count) {
14036 0 : IDB_REPORT_INTERNAL_ERR();
14037 :
14038 0 : for (uint32_t index = 0; index < count; index++) {
14039 0 : RefPtr<TransactionBase> transaction = transactions[index].forget();
14040 0 : MOZ_ASSERT(transaction);
14041 :
14042 0 : transaction->Invalidate();
14043 : }
14044 : }
14045 :
14046 0 : return true;
14047 : }
14048 :
14049 : static bool
14050 0 : InvalidateMutableFiles(nsTHashtable<nsPtrHashKey<MutableFile>>& aTable)
14051 : {
14052 0 : AssertIsOnBackgroundThread();
14053 :
14054 0 : const uint32_t count = aTable.Count();
14055 0 : if (!count) {
14056 0 : return true;
14057 : }
14058 :
14059 0 : FallibleTArray<RefPtr<MutableFile>> mutableFiles;
14060 0 : if (NS_WARN_IF(!mutableFiles.SetCapacity(count, fallible))) {
14061 0 : return false;
14062 : }
14063 :
14064 0 : for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
14065 0 : if (NS_WARN_IF(!mutableFiles.AppendElement(iter.Get()->GetKey(),
14066 : fallible))) {
14067 0 : return false;
14068 : }
14069 : }
14070 :
14071 0 : if (count) {
14072 0 : IDB_REPORT_INTERNAL_ERR();
14073 :
14074 0 : for (uint32_t index = 0; index < count; index++) {
14075 0 : RefPtr<MutableFile> mutableFile = mutableFiles[index].forget();
14076 0 : MOZ_ASSERT(mutableFile);
14077 :
14078 0 : mutableFile->Invalidate();
14079 : }
14080 : }
14081 :
14082 0 : return true;
14083 : }
14084 : };
14085 :
14086 0 : if (mInvalidated) {
14087 0 : return;
14088 : }
14089 :
14090 0 : mInvalidated = true;
14091 :
14092 0 : if (mActorWasAlive && !mActorDestroyed) {
14093 0 : Unused << SendInvalidate();
14094 : }
14095 :
14096 0 : if (!Helper::InvalidateTransactions(mTransactions)) {
14097 0 : NS_WARNING("Failed to abort all transactions!");
14098 : }
14099 :
14100 0 : if (!Helper::InvalidateMutableFiles(mMutableFiles)) {
14101 0 : NS_WARNING("Failed to abort all mutable files!");
14102 : }
14103 :
14104 0 : MOZ_ALWAYS_TRUE(CloseInternal());
14105 :
14106 0 : CleanupMetadata();
14107 : }
14108 :
14109 : nsresult
14110 0 : Database::EnsureConnection()
14111 : {
14112 0 : MOZ_ASSERT(!NS_IsMainThread());
14113 0 : MOZ_ASSERT(!IsOnBackgroundThread());
14114 :
14115 0 : AUTO_PROFILER_LABEL("Database::EnsureConnection", STORAGE);
14116 :
14117 0 : if (!mConnection || !mConnection->GetStorageConnection()) {
14118 : nsresult rv =
14119 0 : gConnectionPool->GetOrCreateConnection(this, getter_AddRefs(mConnection));
14120 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
14121 0 : return rv;
14122 : }
14123 : }
14124 :
14125 0 : AssertIsOnConnectionThread();
14126 :
14127 0 : return NS_OK;
14128 : }
14129 :
14130 : bool
14131 0 : Database::RegisterTransaction(TransactionBase* aTransaction)
14132 : {
14133 0 : AssertIsOnBackgroundThread();
14134 0 : MOZ_ASSERT(aTransaction);
14135 0 : MOZ_ASSERT(!mTransactions.GetEntry(aTransaction));
14136 0 : MOZ_ASSERT(mDirectoryLock);
14137 0 : MOZ_ASSERT(!mInvalidated);
14138 0 : MOZ_ASSERT(!mClosed);
14139 :
14140 0 : if (NS_WARN_IF(!mTransactions.PutEntry(aTransaction, fallible))) {
14141 0 : return false;
14142 : }
14143 :
14144 0 : return true;
14145 : }
14146 :
14147 : void
14148 0 : Database::UnregisterTransaction(TransactionBase* aTransaction)
14149 : {
14150 0 : AssertIsOnBackgroundThread();
14151 0 : MOZ_ASSERT(aTransaction);
14152 0 : MOZ_ASSERT(mTransactions.GetEntry(aTransaction));
14153 :
14154 0 : mTransactions.RemoveEntry(aTransaction);
14155 :
14156 0 : MaybeCloseConnection();
14157 0 : }
14158 :
14159 : bool
14160 0 : Database::RegisterMutableFile(MutableFile* aMutableFile)
14161 : {
14162 0 : AssertIsOnBackgroundThread();
14163 0 : MOZ_ASSERT(aMutableFile);
14164 0 : MOZ_ASSERT(!mMutableFiles.GetEntry(aMutableFile));
14165 0 : MOZ_ASSERT(mDirectoryLock);
14166 :
14167 0 : if (NS_WARN_IF(!mMutableFiles.PutEntry(aMutableFile, fallible))) {
14168 0 : return false;
14169 : }
14170 :
14171 0 : return true;
14172 : }
14173 :
14174 : void
14175 0 : Database::UnregisterMutableFile(MutableFile* aMutableFile)
14176 : {
14177 0 : AssertIsOnBackgroundThread();
14178 0 : MOZ_ASSERT(aMutableFile);
14179 0 : MOZ_ASSERT(mMutableFiles.GetEntry(aMutableFile));
14180 :
14181 0 : mMutableFiles.RemoveEntry(aMutableFile);
14182 0 : }
14183 :
14184 : void
14185 0 : Database::NoteActiveMutableFile()
14186 : {
14187 0 : AssertIsOnBackgroundThread();
14188 0 : MOZ_ASSERT(mDirectoryLock);
14189 0 : MOZ_ASSERT(mActiveMutableFileCount < UINT32_MAX);
14190 :
14191 0 : ++mActiveMutableFileCount;
14192 0 : }
14193 :
14194 : void
14195 0 : Database::NoteInactiveMutableFile()
14196 : {
14197 0 : AssertIsOnBackgroundThread();
14198 0 : MOZ_ASSERT(mActiveMutableFileCount > 0);
14199 :
14200 0 : --mActiveMutableFileCount;
14201 :
14202 0 : MaybeCloseConnection();
14203 0 : }
14204 :
14205 : void
14206 0 : Database::SetActorAlive()
14207 : {
14208 0 : AssertIsOnBackgroundThread();
14209 0 : MOZ_ASSERT(!mActorWasAlive);
14210 0 : MOZ_ASSERT(!mActorDestroyed);
14211 :
14212 0 : mActorWasAlive = true;
14213 :
14214 : // This reference will be absorbed by IPDL and released when the actor is
14215 : // destroyed.
14216 0 : AddRef();
14217 0 : }
14218 :
14219 : void
14220 0 : Database::MapBlob(const IPCBlob& aIPCBlob, FileInfo* aFileInfo)
14221 : {
14222 0 : AssertIsOnBackgroundThread();
14223 :
14224 0 : const IPCBlobStream& stream = aIPCBlob.inputStream();
14225 0 : MOZ_ASSERT(stream.type() == IPCBlobStream::TPIPCBlobInputStreamParent);
14226 :
14227 : IPCBlobInputStreamParent* actor =
14228 0 : static_cast<IPCBlobInputStreamParent*>(stream.get_PIPCBlobInputStreamParent());
14229 :
14230 0 : MOZ_ASSERT(!mMappedBlobs.GetWeak(actor->ID()));
14231 0 : mMappedBlobs.Put(actor->ID(), aFileInfo);
14232 :
14233 0 : RefPtr<UnmapBlobCallback> callback = new UnmapBlobCallback(this);
14234 0 : actor->SetCallback(callback);
14235 0 : }
14236 :
14237 : already_AddRefed<FileInfo>
14238 0 : Database::GetBlob(const IPCBlob& aIPCBlob)
14239 : {
14240 0 : AssertIsOnBackgroundThread();
14241 :
14242 0 : const IPCBlobStream& stream = aIPCBlob.inputStream();
14243 0 : MOZ_ASSERT(stream.type() == IPCBlobStream::TIPCStream);
14244 :
14245 0 : const IPCStream& ipcStream = stream.get_IPCStream();
14246 :
14247 0 : if (ipcStream.type() != IPCStream::TInputStreamParamsWithFds) {
14248 0 : return nullptr;
14249 : }
14250 :
14251 : const InputStreamParams& inputStreamParams =
14252 0 : ipcStream.get_InputStreamParamsWithFds().stream();
14253 0 : if (inputStreamParams.type() !=
14254 : InputStreamParams::TIPCBlobInputStreamParams) {
14255 0 : return nullptr;
14256 : }
14257 :
14258 0 : const nsID& id = inputStreamParams.get_IPCBlobInputStreamParams().id();
14259 :
14260 0 : RefPtr<FileInfo> fileInfo;
14261 0 : if (!mMappedBlobs.Get(id, getter_AddRefs(fileInfo))) {
14262 0 : return nullptr;
14263 : }
14264 :
14265 0 : return fileInfo.forget();
14266 : }
14267 :
14268 : void
14269 0 : Database::UnmapBlob(const nsID& aID)
14270 : {
14271 0 : AssertIsOnBackgroundThread();
14272 :
14273 0 : MOZ_ASSERT_IF(!mAllBlobsUnmapped, mMappedBlobs.GetWeak(aID));
14274 0 : mMappedBlobs.Remove(aID);
14275 0 : }
14276 :
14277 : void
14278 0 : Database::UnmapAllBlobs()
14279 : {
14280 0 : AssertIsOnBackgroundThread();
14281 :
14282 : #ifdef DEBUG
14283 0 : mAllBlobsUnmapped = true;
14284 : #endif
14285 :
14286 0 : mMappedBlobs.Clear();
14287 0 : }
14288 :
14289 : bool
14290 0 : Database::CloseInternal()
14291 : {
14292 0 : AssertIsOnBackgroundThread();
14293 :
14294 0 : if (mClosed) {
14295 0 : if (NS_WARN_IF(!IsInvalidated())) {
14296 : // Kill misbehaving child for sending the close message twice.
14297 0 : return false;
14298 : }
14299 :
14300 : // Ignore harmless race when we just invalidated the database.
14301 0 : return true;
14302 : }
14303 :
14304 0 : mClosed = true;
14305 :
14306 0 : if (gConnectionPool) {
14307 0 : gConnectionPool->CloseDatabaseWhenIdle(Id());
14308 : }
14309 :
14310 : DatabaseActorInfo* info;
14311 0 : MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info));
14312 :
14313 0 : MOZ_ASSERT(info->mLiveDatabases.Contains(this));
14314 :
14315 0 : if (info->mWaitingFactoryOp) {
14316 0 : info->mWaitingFactoryOp->NoteDatabaseClosed(this);
14317 : }
14318 :
14319 0 : MaybeCloseConnection();
14320 :
14321 0 : return true;
14322 : }
14323 :
14324 : void
14325 0 : Database::MaybeCloseConnection()
14326 : {
14327 0 : AssertIsOnBackgroundThread();
14328 :
14329 0 : if (!mTransactions.Count() &&
14330 0 : !mActiveMutableFileCount &&
14331 0 : IsClosed() &&
14332 0 : mDirectoryLock) {
14333 : nsCOMPtr<nsIRunnable> callback =
14334 0 : NewRunnableMethod("dom::indexedDB::Database::ConnectionClosedCallback",
14335 : this,
14336 0 : &Database::ConnectionClosedCallback);
14337 :
14338 : RefPtr<WaitForTransactionsHelper> helper =
14339 0 : new WaitForTransactionsHelper(Id(), callback);
14340 0 : helper->WaitForTransactions();
14341 : }
14342 0 : }
14343 :
14344 : void
14345 0 : Database::ConnectionClosedCallback()
14346 : {
14347 0 : AssertIsOnBackgroundThread();
14348 0 : MOZ_ASSERT(mClosed);
14349 0 : MOZ_ASSERT(!mTransactions.Count());
14350 0 : MOZ_ASSERT(!mActiveMutableFileCount);
14351 :
14352 0 : mDirectoryLock = nullptr;
14353 :
14354 0 : CleanupMetadata();
14355 :
14356 0 : UnmapAllBlobs();
14357 :
14358 0 : if (IsInvalidated() && IsActorAlive()) {
14359 : // Step 3 and 4 of "5.2 Closing a Database":
14360 : // 1. Wait for all transactions to complete.
14361 : // 2. Fire a close event if forced flag is set, i.e., IsInvalidated() in our
14362 : // implementation.
14363 0 : Unused << SendCloseAfterInvalidationComplete();
14364 : }
14365 0 : }
14366 :
14367 : void
14368 0 : Database::CleanupMetadata()
14369 : {
14370 0 : AssertIsOnBackgroundThread();
14371 :
14372 0 : if (!mMetadataCleanedUp) {
14373 0 : mMetadataCleanedUp = true;
14374 :
14375 : DatabaseActorInfo* info;
14376 0 : MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info));
14377 0 : MOZ_ALWAYS_TRUE(info->mLiveDatabases.RemoveElement(this));
14378 :
14379 0 : if (info->mLiveDatabases.IsEmpty()) {
14380 0 : MOZ_ASSERT(!info->mWaitingFactoryOp ||
14381 : !info->mWaitingFactoryOp->HasBlockedDatabases());
14382 0 : gLiveDatabaseHashtable->Remove(Id());
14383 : }
14384 :
14385 : // Match the IncreaseBusyCount in OpenDatabaseOp::EnsureDatabaseActor().
14386 0 : DecreaseBusyCount();
14387 : }
14388 0 : }
14389 :
14390 : bool
14391 0 : Database::VerifyRequestParams(const DatabaseRequestParams& aParams) const
14392 : {
14393 0 : AssertIsOnBackgroundThread();
14394 0 : MOZ_ASSERT(aParams.type() != DatabaseRequestParams::T__None);
14395 :
14396 0 : switch (aParams.type()) {
14397 : case DatabaseRequestParams::TCreateFileParams: {
14398 0 : if (NS_WARN_IF(mFileHandleDisabled)) {
14399 0 : ASSERT_UNLESS_FUZZING();
14400 : return false;
14401 : }
14402 :
14403 0 : const CreateFileParams& params = aParams.get_CreateFileParams();
14404 :
14405 0 : if (NS_WARN_IF(params.name().IsEmpty())) {
14406 0 : ASSERT_UNLESS_FUZZING();
14407 : return false;
14408 : }
14409 :
14410 0 : break;
14411 : }
14412 :
14413 : default:
14414 0 : MOZ_CRASH("Should never get here!");
14415 : }
14416 :
14417 0 : return true;
14418 : }
14419 :
14420 : void
14421 0 : Database::ActorDestroy(ActorDestroyReason aWhy)
14422 : {
14423 0 : AssertIsOnBackgroundThread();
14424 0 : MOZ_ASSERT(!mActorDestroyed);
14425 :
14426 0 : mActorDestroyed = true;
14427 :
14428 0 : if (!IsInvalidated()) {
14429 0 : Invalidate();
14430 : }
14431 0 : }
14432 :
14433 : PBackgroundIDBDatabaseFileParent*
14434 0 : Database::AllocPBackgroundIDBDatabaseFileParent(const IPCBlob& aIPCBlob)
14435 : {
14436 0 : AssertIsOnBackgroundThread();
14437 :
14438 0 : RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(aIPCBlob);
14439 0 : MOZ_ASSERT(blobImpl);
14440 :
14441 0 : RefPtr<FileInfo> fileInfo = GetBlob(aIPCBlob);
14442 0 : RefPtr<DatabaseFile> actor;
14443 :
14444 0 : if (fileInfo) {
14445 0 : actor = new DatabaseFile(fileInfo);
14446 : } else {
14447 : // This is a blob we haven't seen before.
14448 0 : fileInfo = mFileManager->GetNewFileInfo();
14449 0 : MOZ_ASSERT(fileInfo);
14450 :
14451 0 : actor = new DatabaseFile(blobImpl, fileInfo);
14452 : }
14453 :
14454 0 : MOZ_ASSERT(actor);
14455 :
14456 0 : return actor.forget().take();
14457 : }
14458 :
14459 : bool
14460 0 : Database::DeallocPBackgroundIDBDatabaseFileParent(
14461 : PBackgroundIDBDatabaseFileParent* aActor)
14462 : {
14463 0 : AssertIsOnBackgroundThread();
14464 0 : MOZ_ASSERT(aActor);
14465 :
14466 : RefPtr<DatabaseFile> actor =
14467 0 : dont_AddRef(static_cast<DatabaseFile*>(aActor));
14468 0 : return true;
14469 : }
14470 :
14471 : PBackgroundIDBDatabaseRequestParent*
14472 0 : Database::AllocPBackgroundIDBDatabaseRequestParent(
14473 : const DatabaseRequestParams& aParams)
14474 : {
14475 0 : AssertIsOnBackgroundThread();
14476 0 : MOZ_ASSERT(aParams.type() != DatabaseRequestParams::T__None);
14477 :
14478 : #ifdef DEBUG
14479 : // Always verify parameters in DEBUG builds!
14480 0 : bool trustParams = false;
14481 : #else
14482 : PBackgroundParent* backgroundActor = GetBackgroundParent();
14483 : MOZ_ASSERT(backgroundActor);
14484 :
14485 : bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
14486 : #endif
14487 :
14488 0 : if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
14489 0 : ASSERT_UNLESS_FUZZING();
14490 : return nullptr;
14491 : }
14492 :
14493 0 : RefPtr<DatabaseOp> actor;
14494 :
14495 0 : switch (aParams.type()) {
14496 : case DatabaseRequestParams::TCreateFileParams: {
14497 0 : actor = new CreateFileOp(this, aParams);
14498 0 : break;
14499 : }
14500 :
14501 : default:
14502 0 : MOZ_CRASH("Should never get here!");
14503 : }
14504 :
14505 0 : MOZ_ASSERT(actor);
14506 :
14507 : // Transfer ownership to IPDL.
14508 0 : return actor.forget().take();
14509 : }
14510 :
14511 : mozilla::ipc::IPCResult
14512 0 : Database::RecvPBackgroundIDBDatabaseRequestConstructor(
14513 : PBackgroundIDBDatabaseRequestParent* aActor,
14514 : const DatabaseRequestParams& aParams)
14515 : {
14516 0 : AssertIsOnBackgroundThread();
14517 0 : MOZ_ASSERT(aActor);
14518 0 : MOZ_ASSERT(aParams.type() != DatabaseRequestParams::T__None);
14519 :
14520 0 : auto* op = static_cast<DatabaseOp*>(aActor);
14521 :
14522 0 : op->RunImmediately();
14523 :
14524 0 : return IPC_OK();
14525 : }
14526 :
14527 : bool
14528 0 : Database::DeallocPBackgroundIDBDatabaseRequestParent(
14529 : PBackgroundIDBDatabaseRequestParent* aActor)
14530 : {
14531 0 : AssertIsOnBackgroundThread();
14532 0 : MOZ_ASSERT(aActor);
14533 :
14534 : // Transfer ownership back from IPDL.
14535 0 : RefPtr<DatabaseOp> op = dont_AddRef(static_cast<DatabaseOp*>(aActor));
14536 0 : return true;
14537 : }
14538 :
14539 : PBackgroundIDBTransactionParent*
14540 0 : Database::AllocPBackgroundIDBTransactionParent(
14541 : const nsTArray<nsString>& aObjectStoreNames,
14542 : const Mode& aMode)
14543 : {
14544 0 : AssertIsOnBackgroundThread();
14545 :
14546 : // Once a database is closed it must not try to open new transactions.
14547 0 : if (NS_WARN_IF(mClosed)) {
14548 0 : if (!mInvalidated) {
14549 0 : ASSERT_UNLESS_FUZZING();
14550 : }
14551 0 : return nullptr;
14552 : }
14553 :
14554 0 : if (NS_WARN_IF(aObjectStoreNames.IsEmpty())) {
14555 0 : ASSERT_UNLESS_FUZZING();
14556 : return nullptr;
14557 : }
14558 :
14559 0 : if (NS_WARN_IF(aMode != IDBTransaction::READ_ONLY &&
14560 : aMode != IDBTransaction::READ_WRITE &&
14561 : aMode != IDBTransaction::READ_WRITE_FLUSH &&
14562 : aMode != IDBTransaction::CLEANUP)) {
14563 0 : ASSERT_UNLESS_FUZZING();
14564 : return nullptr;
14565 : }
14566 :
14567 : // If this is a readwrite transaction to a chrome database make sure the child
14568 : // has write access.
14569 0 : if (NS_WARN_IF((aMode == IDBTransaction::READ_WRITE ||
14570 : aMode == IDBTransaction::READ_WRITE_FLUSH ||
14571 : aMode == IDBTransaction::CLEANUP) &&
14572 : mPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo &&
14573 : !mChromeWriteAccessAllowed)) {
14574 0 : return nullptr;
14575 : }
14576 :
14577 0 : const ObjectStoreTable& objectStores = mMetadata->mObjectStores;
14578 0 : const uint32_t nameCount = aObjectStoreNames.Length();
14579 :
14580 0 : if (NS_WARN_IF(nameCount > objectStores.Count())) {
14581 0 : ASSERT_UNLESS_FUZZING();
14582 : return nullptr;
14583 : }
14584 :
14585 0 : FallibleTArray<RefPtr<FullObjectStoreMetadata>> fallibleObjectStores;
14586 0 : if (NS_WARN_IF(!fallibleObjectStores.SetCapacity(nameCount, fallible))) {
14587 0 : return nullptr;
14588 : }
14589 :
14590 0 : for (uint32_t nameIndex = 0; nameIndex < nameCount; nameIndex++) {
14591 0 : const nsString& name = aObjectStoreNames[nameIndex];
14592 :
14593 0 : if (nameIndex) {
14594 : // Make sure that this name is sorted properly and not a duplicate.
14595 0 : if (NS_WARN_IF(name <= aObjectStoreNames[nameIndex - 1])) {
14596 0 : ASSERT_UNLESS_FUZZING();
14597 : return nullptr;
14598 : }
14599 : }
14600 :
14601 0 : for (auto iter = objectStores.ConstIter(); !iter.Done(); iter.Next()) {
14602 0 : auto value = iter.Data();
14603 0 : MOZ_ASSERT(iter.Key());
14604 :
14605 0 : if (name == value->mCommonMetadata.name() && !value->mDeleted) {
14606 0 : if (NS_WARN_IF(!fallibleObjectStores.AppendElement(value, fallible))) {
14607 0 : return nullptr;
14608 : }
14609 0 : break;
14610 : }
14611 : }
14612 : }
14613 :
14614 0 : nsTArray<RefPtr<FullObjectStoreMetadata>> infallibleObjectStores;
14615 0 : infallibleObjectStores.SwapElements(fallibleObjectStores);
14616 :
14617 : RefPtr<NormalTransaction> transaction =
14618 0 : new NormalTransaction(this, aMode, infallibleObjectStores);
14619 :
14620 0 : MOZ_ASSERT(infallibleObjectStores.IsEmpty());
14621 :
14622 0 : return transaction.forget().take();
14623 : }
14624 :
14625 : mozilla::ipc::IPCResult
14626 0 : Database::RecvPBackgroundIDBTransactionConstructor(
14627 : PBackgroundIDBTransactionParent* aActor,
14628 : InfallibleTArray<nsString>&& aObjectStoreNames,
14629 : const Mode& aMode)
14630 : {
14631 0 : AssertIsOnBackgroundThread();
14632 0 : MOZ_ASSERT(aActor);
14633 0 : MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
14634 0 : MOZ_ASSERT(aMode == IDBTransaction::READ_ONLY ||
14635 : aMode == IDBTransaction::READ_WRITE ||
14636 : aMode == IDBTransaction::READ_WRITE_FLUSH ||
14637 : aMode == IDBTransaction::CLEANUP);
14638 0 : MOZ_ASSERT(!mClosed);
14639 :
14640 0 : if (IsInvalidated()) {
14641 : // This is an expected race. We don't want the child to die here, just don't
14642 : // actually do any work.
14643 0 : return IPC_OK();
14644 : }
14645 :
14646 0 : if (!gConnectionPool) {
14647 0 : gConnectionPool = new ConnectionPool();
14648 : }
14649 :
14650 0 : auto* transaction = static_cast<NormalTransaction*>(aActor);
14651 :
14652 0 : RefPtr<StartTransactionOp> startOp = new StartTransactionOp(transaction);
14653 :
14654 : uint64_t transactionId =
14655 0 : startOp->StartOnConnectionPool(GetLoggingInfo()->Id(),
14656 0 : mMetadata->mDatabaseId,
14657 : transaction->LoggingSerialNumber(),
14658 : aObjectStoreNames,
14659 0 : aMode != IDBTransaction::READ_ONLY);
14660 :
14661 0 : transaction->SetActive(transactionId);
14662 :
14663 0 : if (NS_WARN_IF(!RegisterTransaction(transaction))) {
14664 0 : IDB_REPORT_INTERNAL_ERR();
14665 0 : transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, /* aForce */ false);
14666 0 : return IPC_OK();
14667 : }
14668 :
14669 0 : return IPC_OK();
14670 : }
14671 :
14672 : bool
14673 0 : Database::DeallocPBackgroundIDBTransactionParent(
14674 : PBackgroundIDBTransactionParent* aActor)
14675 : {
14676 0 : AssertIsOnBackgroundThread();
14677 0 : MOZ_ASSERT(aActor);
14678 :
14679 : RefPtr<NormalTransaction> transaction =
14680 0 : dont_AddRef(static_cast<NormalTransaction*>(aActor));
14681 0 : return true;
14682 : }
14683 :
14684 : PBackgroundIDBVersionChangeTransactionParent*
14685 0 : Database::AllocPBackgroundIDBVersionChangeTransactionParent(
14686 : const uint64_t& aCurrentVersion,
14687 : const uint64_t& aRequestedVersion,
14688 : const int64_t& aNextObjectStoreId,
14689 : const int64_t& aNextIndexId)
14690 : {
14691 0 : MOZ_CRASH("PBackgroundIDBVersionChangeTransactionParent actors should be "
14692 : "constructed manually!");
14693 : }
14694 :
14695 : bool
14696 0 : Database::DeallocPBackgroundIDBVersionChangeTransactionParent(
14697 : PBackgroundIDBVersionChangeTransactionParent* aActor)
14698 : {
14699 0 : AssertIsOnBackgroundThread();
14700 0 : MOZ_ASSERT(aActor);
14701 :
14702 : RefPtr<VersionChangeTransaction> transaction =
14703 0 : dont_AddRef(static_cast<VersionChangeTransaction*>(aActor));
14704 0 : return true;
14705 : }
14706 :
14707 : Database::PBackgroundMutableFileParent*
14708 0 : Database::AllocPBackgroundMutableFileParent(const nsString& aName,
14709 : const nsString& aType)
14710 : {
14711 0 : MOZ_CRASH("PBackgroundMutableFileParent actors should be constructed "
14712 : "manually!");
14713 : }
14714 :
14715 : bool
14716 0 : Database::DeallocPBackgroundMutableFileParent(
14717 : PBackgroundMutableFileParent* aActor)
14718 : {
14719 0 : AssertIsOnBackgroundThread();
14720 0 : MOZ_ASSERT(aActor);
14721 :
14722 : // Transfer ownership back from IPDL.
14723 : RefPtr<MutableFile> mutableFile =
14724 0 : dont_AddRef(static_cast<MutableFile*>(aActor));
14725 0 : return true;
14726 : }
14727 :
14728 : mozilla::ipc::IPCResult
14729 0 : Database::RecvDeleteMe()
14730 : {
14731 0 : AssertIsOnBackgroundThread();
14732 0 : MOZ_ASSERT(!mActorDestroyed);
14733 :
14734 0 : IProtocol* mgr = Manager();
14735 0 : if (!PBackgroundIDBDatabaseParent::Send__delete__(this)) {
14736 0 : return IPC_FAIL_NO_REASON(mgr);
14737 : }
14738 0 : return IPC_OK();
14739 : }
14740 :
14741 : mozilla::ipc::IPCResult
14742 0 : Database::RecvBlocked()
14743 : {
14744 0 : AssertIsOnBackgroundThread();
14745 :
14746 0 : if (NS_WARN_IF(mClosed)) {
14747 0 : return IPC_FAIL_NO_REASON(this);
14748 : }
14749 :
14750 : DatabaseActorInfo* info;
14751 0 : MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info));
14752 :
14753 0 : MOZ_ASSERT(info->mLiveDatabases.Contains(this));
14754 0 : MOZ_ASSERT(info->mWaitingFactoryOp);
14755 :
14756 0 : info->mWaitingFactoryOp->NoteDatabaseBlocked(this);
14757 :
14758 0 : return IPC_OK();
14759 : }
14760 :
14761 : mozilla::ipc::IPCResult
14762 0 : Database::RecvClose()
14763 : {
14764 0 : AssertIsOnBackgroundThread();
14765 :
14766 0 : if (NS_WARN_IF(!CloseInternal())) {
14767 0 : ASSERT_UNLESS_FUZZING();
14768 : return IPC_FAIL_NO_REASON(this);
14769 : }
14770 :
14771 0 : return IPC_OK();
14772 : }
14773 :
14774 : void
14775 0 : Database::
14776 : StartTransactionOp::RunOnConnectionThread()
14777 : {
14778 0 : MOZ_ASSERT(!IsOnBackgroundThread());
14779 0 : MOZ_ASSERT(Transaction());
14780 0 : MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
14781 :
14782 0 : IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
14783 : "Beginning database work",
14784 : "IndexedDB %s: P T[%lld]: DB Start",
14785 : IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
14786 : mLoggingSerialNumber);
14787 :
14788 0 : TransactionDatabaseOperationBase::RunOnConnectionThread();
14789 0 : }
14790 :
14791 : nsresult
14792 0 : Database::
14793 : StartTransactionOp::DoDatabaseWork(DatabaseConnection* aConnection)
14794 : {
14795 0 : MOZ_ASSERT(aConnection);
14796 0 : aConnection->AssertIsOnConnectionThread();
14797 :
14798 0 : Transaction()->SetActiveOnConnectionThread();
14799 :
14800 0 : if (Transaction()->GetMode() == IDBTransaction::CLEANUP) {
14801 0 : nsresult rv = aConnection->DisableQuotaChecks();
14802 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
14803 0 : return rv;
14804 : }
14805 : }
14806 :
14807 0 : if (Transaction()->GetMode() != IDBTransaction::READ_ONLY) {
14808 0 : nsresult rv = aConnection->BeginWriteTransaction();
14809 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
14810 0 : return rv;
14811 : }
14812 : }
14813 :
14814 0 : return NS_OK;
14815 : }
14816 :
14817 : nsresult
14818 0 : Database::
14819 : StartTransactionOp::SendSuccessResult()
14820 : {
14821 : // We don't need to do anything here.
14822 0 : return NS_OK;
14823 : }
14824 :
14825 : bool
14826 0 : Database::
14827 : StartTransactionOp::SendFailureResult(nsresult /* aResultCode */)
14828 : {
14829 0 : IDB_REPORT_INTERNAL_ERR();
14830 :
14831 : // Abort the transaction.
14832 0 : return false;
14833 : }
14834 :
14835 : void
14836 0 : Database::
14837 : StartTransactionOp::Cleanup()
14838 : {
14839 : #ifdef DEBUG
14840 : // StartTransactionOp is not a normal database operation that is tied to an
14841 : // actor. Do this to make our assertions happy.
14842 0 : NoteActorDestroyed();
14843 : #endif
14844 :
14845 0 : TransactionDatabaseOperationBase::Cleanup();
14846 0 : }
14847 :
14848 : /*******************************************************************************
14849 : * TransactionBase
14850 : ******************************************************************************/
14851 :
14852 0 : TransactionBase::TransactionBase(Database* aDatabase, Mode aMode)
14853 : : mDatabase(aDatabase)
14854 : , mTransactionId(0)
14855 0 : , mDatabaseId(aDatabase->Id())
14856 0 : , mLoggingSerialNumber(aDatabase->GetLoggingInfo()->NextTransactionSN(aMode))
14857 : , mActiveRequestCount(0)
14858 : , mInvalidatedOnAnyThread(false)
14859 : , mMode(aMode)
14860 : , mHasBeenActive(false)
14861 : , mHasBeenActiveOnConnectionThread(false)
14862 : , mActorDestroyed(false)
14863 : , mInvalidated(false)
14864 : , mResultCode(NS_OK)
14865 : , mCommitOrAbortReceived(false)
14866 : , mCommittedOrAborted(false)
14867 0 : , mForceAborted(false)
14868 : {
14869 0 : AssertIsOnBackgroundThread();
14870 0 : MOZ_ASSERT(aDatabase);
14871 0 : MOZ_ASSERT(mLoggingSerialNumber);
14872 0 : }
14873 :
14874 0 : TransactionBase::~TransactionBase()
14875 : {
14876 0 : MOZ_ASSERT(!mActiveRequestCount);
14877 0 : MOZ_ASSERT(mActorDestroyed);
14878 0 : MOZ_ASSERT_IF(mHasBeenActive, mCommittedOrAborted);
14879 0 : }
14880 :
14881 : void
14882 0 : TransactionBase::Abort(nsresult aResultCode, bool aForce)
14883 : {
14884 0 : AssertIsOnBackgroundThread();
14885 0 : MOZ_ASSERT(NS_FAILED(aResultCode));
14886 :
14887 0 : if (NS_SUCCEEDED(mResultCode)) {
14888 0 : mResultCode = aResultCode;
14889 : }
14890 :
14891 0 : if (aForce) {
14892 0 : mForceAborted = true;
14893 : }
14894 :
14895 0 : MaybeCommitOrAbort();
14896 0 : }
14897 :
14898 : bool
14899 0 : TransactionBase::RecvCommit()
14900 : {
14901 0 : AssertIsOnBackgroundThread();
14902 :
14903 0 : if (NS_WARN_IF(mCommitOrAbortReceived)) {
14904 0 : ASSERT_UNLESS_FUZZING();
14905 : return false;
14906 : }
14907 :
14908 0 : mCommitOrAbortReceived = true;
14909 :
14910 0 : MaybeCommitOrAbort();
14911 0 : return true;
14912 : }
14913 :
14914 : bool
14915 0 : TransactionBase::RecvAbort(nsresult aResultCode)
14916 : {
14917 0 : AssertIsOnBackgroundThread();
14918 :
14919 0 : if (NS_WARN_IF(NS_SUCCEEDED(aResultCode))) {
14920 0 : ASSERT_UNLESS_FUZZING();
14921 : return false;
14922 : }
14923 :
14924 0 : if (NS_WARN_IF(NS_ERROR_GET_MODULE(aResultCode) !=
14925 : NS_ERROR_MODULE_DOM_INDEXEDDB)) {
14926 0 : ASSERT_UNLESS_FUZZING();
14927 : return false;
14928 : }
14929 :
14930 0 : if (NS_WARN_IF(mCommitOrAbortReceived)) {
14931 0 : ASSERT_UNLESS_FUZZING();
14932 : return false;
14933 : }
14934 :
14935 0 : mCommitOrAbortReceived = true;
14936 :
14937 0 : Abort(aResultCode, /* aForce */ false);
14938 0 : return true;
14939 : }
14940 :
14941 : void
14942 0 : TransactionBase::CommitOrAbort()
14943 : {
14944 0 : AssertIsOnBackgroundThread();
14945 0 : MOZ_ASSERT(!mCommittedOrAborted);
14946 :
14947 0 : mCommittedOrAborted = true;
14948 :
14949 0 : if (!mHasBeenActive) {
14950 0 : return;
14951 : }
14952 :
14953 : RefPtr<CommitOp> commitOp =
14954 0 : new CommitOp(this, ClampResultCode(mResultCode));
14955 :
14956 0 : gConnectionPool->Finish(TransactionId(), commitOp);
14957 : }
14958 :
14959 : already_AddRefed<FullObjectStoreMetadata>
14960 0 : TransactionBase::GetMetadataForObjectStoreId(int64_t aObjectStoreId) const
14961 : {
14962 0 : AssertIsOnBackgroundThread();
14963 0 : MOZ_ASSERT(aObjectStoreId);
14964 :
14965 0 : if (!aObjectStoreId) {
14966 0 : return nullptr;
14967 : }
14968 :
14969 0 : RefPtr<FullObjectStoreMetadata> metadata;
14970 0 : if (!mDatabase->Metadata()->mObjectStores.Get(aObjectStoreId,
14971 0 : getter_AddRefs(metadata)) ||
14972 0 : metadata->mDeleted) {
14973 0 : return nullptr;
14974 : }
14975 :
14976 0 : MOZ_ASSERT(metadata->mCommonMetadata.id() == aObjectStoreId);
14977 :
14978 0 : return metadata.forget();
14979 : }
14980 :
14981 : already_AddRefed<FullIndexMetadata>
14982 0 : TransactionBase::GetMetadataForIndexId(
14983 : FullObjectStoreMetadata* const aObjectStoreMetadata,
14984 : int64_t aIndexId) const
14985 : {
14986 0 : AssertIsOnBackgroundThread();
14987 0 : MOZ_ASSERT(aIndexId);
14988 :
14989 0 : if (!aIndexId) {
14990 0 : return nullptr;
14991 : }
14992 :
14993 0 : RefPtr<FullIndexMetadata> metadata;
14994 0 : if (!aObjectStoreMetadata->mIndexes.Get(aIndexId, getter_AddRefs(metadata)) ||
14995 0 : metadata->mDeleted) {
14996 0 : return nullptr;
14997 : }
14998 :
14999 0 : MOZ_ASSERT(metadata->mCommonMetadata.id() == aIndexId);
15000 :
15001 0 : return metadata.forget();
15002 : }
15003 :
15004 : void
15005 0 : TransactionBase::NoteModifiedAutoIncrementObjectStore(
15006 : FullObjectStoreMetadata* aMetadata)
15007 : {
15008 0 : AssertIsOnConnectionThread();
15009 0 : MOZ_ASSERT(aMetadata);
15010 :
15011 0 : if (!mModifiedAutoIncrementObjectStoreMetadataArray.Contains(aMetadata)) {
15012 0 : mModifiedAutoIncrementObjectStoreMetadataArray.AppendElement(aMetadata);
15013 : }
15014 0 : }
15015 :
15016 : void
15017 0 : TransactionBase::ForgetModifiedAutoIncrementObjectStore(
15018 : FullObjectStoreMetadata* aMetadata)
15019 : {
15020 0 : AssertIsOnConnectionThread();
15021 0 : MOZ_ASSERT(aMetadata);
15022 :
15023 0 : mModifiedAutoIncrementObjectStoreMetadataArray.RemoveElement(aMetadata);
15024 0 : }
15025 :
15026 : bool
15027 0 : TransactionBase::VerifyRequestParams(const RequestParams& aParams) const
15028 : {
15029 0 : AssertIsOnBackgroundThread();
15030 0 : MOZ_ASSERT(aParams.type() != RequestParams::T__None);
15031 :
15032 0 : switch (aParams.type()) {
15033 : case RequestParams::TObjectStoreAddParams: {
15034 : const ObjectStoreAddPutParams& params =
15035 0 : aParams.get_ObjectStoreAddParams().commonParams();
15036 0 : if (NS_WARN_IF(!VerifyRequestParams(params))) {
15037 0 : ASSERT_UNLESS_FUZZING();
15038 : return false;
15039 : }
15040 0 : break;
15041 : }
15042 :
15043 : case RequestParams::TObjectStorePutParams: {
15044 : const ObjectStoreAddPutParams& params =
15045 0 : aParams.get_ObjectStorePutParams().commonParams();
15046 0 : if (NS_WARN_IF(!VerifyRequestParams(params))) {
15047 0 : ASSERT_UNLESS_FUZZING();
15048 : return false;
15049 : }
15050 0 : break;
15051 : }
15052 :
15053 : case RequestParams::TObjectStoreGetParams: {
15054 0 : const ObjectStoreGetParams& params = aParams.get_ObjectStoreGetParams();
15055 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15056 0 : GetMetadataForObjectStoreId(params.objectStoreId());
15057 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15058 0 : ASSERT_UNLESS_FUZZING();
15059 : return false;
15060 : }
15061 0 : if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
15062 0 : ASSERT_UNLESS_FUZZING();
15063 : return false;
15064 : }
15065 0 : break;
15066 : }
15067 :
15068 : case RequestParams::TObjectStoreGetKeyParams: {
15069 : const ObjectStoreGetKeyParams& params =
15070 0 : aParams.get_ObjectStoreGetKeyParams();
15071 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15072 0 : GetMetadataForObjectStoreId(params.objectStoreId());
15073 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15074 0 : ASSERT_UNLESS_FUZZING();
15075 : return false;
15076 : }
15077 0 : if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
15078 0 : ASSERT_UNLESS_FUZZING();
15079 : return false;
15080 : }
15081 0 : break;
15082 : }
15083 :
15084 : case RequestParams::TObjectStoreGetAllParams: {
15085 : const ObjectStoreGetAllParams& params =
15086 0 : aParams.get_ObjectStoreGetAllParams();
15087 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15088 0 : GetMetadataForObjectStoreId(params.objectStoreId());
15089 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15090 0 : ASSERT_UNLESS_FUZZING();
15091 : return false;
15092 : }
15093 0 : if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15094 0 : ASSERT_UNLESS_FUZZING();
15095 : return false;
15096 : }
15097 0 : break;
15098 : }
15099 :
15100 : case RequestParams::TObjectStoreGetAllKeysParams: {
15101 : const ObjectStoreGetAllKeysParams& params =
15102 0 : aParams.get_ObjectStoreGetAllKeysParams();
15103 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15104 0 : GetMetadataForObjectStoreId(params.objectStoreId());
15105 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15106 0 : ASSERT_UNLESS_FUZZING();
15107 : return false;
15108 : }
15109 0 : if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15110 0 : ASSERT_UNLESS_FUZZING();
15111 : return false;
15112 : }
15113 0 : break;
15114 : }
15115 :
15116 : case RequestParams::TObjectStoreDeleteParams: {
15117 0 : if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
15118 : mMode != IDBTransaction::READ_WRITE_FLUSH &&
15119 : mMode != IDBTransaction::CLEANUP &&
15120 : mMode != IDBTransaction::VERSION_CHANGE)) {
15121 0 : ASSERT_UNLESS_FUZZING();
15122 0 : return false;
15123 : }
15124 :
15125 : const ObjectStoreDeleteParams& params =
15126 0 : aParams.get_ObjectStoreDeleteParams();
15127 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15128 0 : GetMetadataForObjectStoreId(params.objectStoreId());
15129 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15130 0 : ASSERT_UNLESS_FUZZING();
15131 : return false;
15132 : }
15133 0 : if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
15134 0 : ASSERT_UNLESS_FUZZING();
15135 : return false;
15136 : }
15137 0 : break;
15138 : }
15139 :
15140 : case RequestParams::TObjectStoreClearParams: {
15141 0 : if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
15142 : mMode != IDBTransaction::READ_WRITE_FLUSH &&
15143 : mMode != IDBTransaction::CLEANUP &&
15144 : mMode != IDBTransaction::VERSION_CHANGE)) {
15145 0 : ASSERT_UNLESS_FUZZING();
15146 0 : return false;
15147 : }
15148 :
15149 : const ObjectStoreClearParams& params =
15150 0 : aParams.get_ObjectStoreClearParams();
15151 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15152 0 : GetMetadataForObjectStoreId(params.objectStoreId());
15153 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15154 0 : ASSERT_UNLESS_FUZZING();
15155 : return false;
15156 : }
15157 0 : break;
15158 : }
15159 :
15160 : case RequestParams::TObjectStoreCountParams: {
15161 : const ObjectStoreCountParams& params =
15162 0 : aParams.get_ObjectStoreCountParams();
15163 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15164 0 : GetMetadataForObjectStoreId(params.objectStoreId());
15165 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15166 0 : ASSERT_UNLESS_FUZZING();
15167 : return false;
15168 : }
15169 0 : if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15170 0 : ASSERT_UNLESS_FUZZING();
15171 : return false;
15172 : }
15173 0 : break;
15174 : }
15175 :
15176 :
15177 : case RequestParams::TIndexGetParams: {
15178 0 : const IndexGetParams& params = aParams.get_IndexGetParams();
15179 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15180 0 : GetMetadataForObjectStoreId(params.objectStoreId());
15181 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15182 0 : ASSERT_UNLESS_FUZZING();
15183 : return false;
15184 : }
15185 : const RefPtr<FullIndexMetadata> indexMetadata =
15186 0 : GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15187 0 : if (NS_WARN_IF(!indexMetadata)) {
15188 0 : ASSERT_UNLESS_FUZZING();
15189 : return false;
15190 : }
15191 0 : if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
15192 0 : ASSERT_UNLESS_FUZZING();
15193 : return false;
15194 : }
15195 0 : break;
15196 : }
15197 :
15198 : case RequestParams::TIndexGetKeyParams: {
15199 0 : const IndexGetKeyParams& params = aParams.get_IndexGetKeyParams();
15200 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15201 0 : GetMetadataForObjectStoreId(params.objectStoreId());
15202 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15203 0 : ASSERT_UNLESS_FUZZING();
15204 : return false;
15205 : }
15206 : const RefPtr<FullIndexMetadata> indexMetadata =
15207 0 : GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15208 0 : if (NS_WARN_IF(!indexMetadata)) {
15209 0 : ASSERT_UNLESS_FUZZING();
15210 : return false;
15211 : }
15212 0 : if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
15213 0 : ASSERT_UNLESS_FUZZING();
15214 : return false;
15215 : }
15216 0 : break;
15217 : }
15218 :
15219 : case RequestParams::TIndexGetAllParams: {
15220 0 : const IndexGetAllParams& params = aParams.get_IndexGetAllParams();
15221 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15222 0 : GetMetadataForObjectStoreId(params.objectStoreId());
15223 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15224 0 : ASSERT_UNLESS_FUZZING();
15225 : return false;
15226 : }
15227 : const RefPtr<FullIndexMetadata> indexMetadata =
15228 0 : GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15229 0 : if (NS_WARN_IF(!indexMetadata)) {
15230 0 : ASSERT_UNLESS_FUZZING();
15231 : return false;
15232 : }
15233 0 : if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15234 0 : ASSERT_UNLESS_FUZZING();
15235 : return false;
15236 : }
15237 0 : break;
15238 : }
15239 :
15240 : case RequestParams::TIndexGetAllKeysParams: {
15241 0 : const IndexGetAllKeysParams& params = aParams.get_IndexGetAllKeysParams();
15242 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15243 0 : GetMetadataForObjectStoreId(params.objectStoreId());
15244 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15245 0 : ASSERT_UNLESS_FUZZING();
15246 : return false;
15247 : }
15248 : const RefPtr<FullIndexMetadata> indexMetadata =
15249 0 : GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15250 0 : if (NS_WARN_IF(!indexMetadata)) {
15251 0 : ASSERT_UNLESS_FUZZING();
15252 : return false;
15253 : }
15254 0 : if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15255 0 : ASSERT_UNLESS_FUZZING();
15256 : return false;
15257 : }
15258 0 : break;
15259 : }
15260 :
15261 : case RequestParams::TIndexCountParams: {
15262 0 : const IndexCountParams& params = aParams.get_IndexCountParams();
15263 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15264 0 : GetMetadataForObjectStoreId(params.objectStoreId());
15265 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15266 0 : ASSERT_UNLESS_FUZZING();
15267 : return false;
15268 : }
15269 : const RefPtr<FullIndexMetadata> indexMetadata =
15270 0 : GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15271 0 : if (NS_WARN_IF(!indexMetadata)) {
15272 0 : ASSERT_UNLESS_FUZZING();
15273 : return false;
15274 : }
15275 0 : if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15276 0 : ASSERT_UNLESS_FUZZING();
15277 : return false;
15278 : }
15279 0 : break;
15280 : }
15281 :
15282 : default:
15283 0 : MOZ_CRASH("Should never get here!");
15284 : }
15285 :
15286 0 : return true;
15287 : }
15288 :
15289 : bool
15290 0 : TransactionBase::VerifyRequestParams(const SerializedKeyRange& aParams) const
15291 : {
15292 0 : AssertIsOnBackgroundThread();
15293 :
15294 : // XXX Check more here?
15295 :
15296 0 : if (aParams.isOnly()) {
15297 0 : if (NS_WARN_IF(aParams.lower().IsUnset())) {
15298 0 : ASSERT_UNLESS_FUZZING();
15299 : return false;
15300 : }
15301 0 : if (NS_WARN_IF(!aParams.upper().IsUnset())) {
15302 0 : ASSERT_UNLESS_FUZZING();
15303 : return false;
15304 : }
15305 0 : if (NS_WARN_IF(aParams.lowerOpen())) {
15306 0 : ASSERT_UNLESS_FUZZING();
15307 : return false;
15308 : }
15309 0 : if (NS_WARN_IF(aParams.upperOpen())) {
15310 0 : ASSERT_UNLESS_FUZZING();
15311 : return false;
15312 : }
15313 0 : } else if (NS_WARN_IF(aParams.lower().IsUnset() &&
15314 : aParams.upper().IsUnset())) {
15315 0 : ASSERT_UNLESS_FUZZING();
15316 : return false;
15317 : }
15318 :
15319 0 : return true;
15320 : }
15321 :
15322 : bool
15323 0 : TransactionBase::VerifyRequestParams(const ObjectStoreAddPutParams& aParams)
15324 : const
15325 : {
15326 0 : AssertIsOnBackgroundThread();
15327 :
15328 0 : if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
15329 : mMode != IDBTransaction::READ_WRITE_FLUSH &&
15330 : mMode != IDBTransaction::VERSION_CHANGE)) {
15331 0 : ASSERT_UNLESS_FUZZING();
15332 : return false;
15333 : }
15334 :
15335 : RefPtr<FullObjectStoreMetadata> objMetadata =
15336 0 : GetMetadataForObjectStoreId(aParams.objectStoreId());
15337 0 : if (NS_WARN_IF(!objMetadata)) {
15338 0 : ASSERT_UNLESS_FUZZING();
15339 : return false;
15340 : }
15341 :
15342 0 : if (NS_WARN_IF(!aParams.cloneInfo().data().data.Size())) {
15343 0 : ASSERT_UNLESS_FUZZING();
15344 : return false;
15345 : }
15346 :
15347 0 : if (objMetadata->mCommonMetadata.autoIncrement() &&
15348 0 : objMetadata->mCommonMetadata.keyPath().IsValid() &&
15349 0 : aParams.key().IsUnset()) {
15350 0 : const SerializedStructuredCloneWriteInfo cloneInfo = aParams.cloneInfo();
15351 :
15352 0 : if (NS_WARN_IF(!cloneInfo.offsetToKeyProp())) {
15353 0 : ASSERT_UNLESS_FUZZING();
15354 : return false;
15355 : }
15356 :
15357 0 : if (NS_WARN_IF(cloneInfo.data().data.Size() < sizeof(uint64_t))) {
15358 0 : ASSERT_UNLESS_FUZZING();
15359 : return false;
15360 : }
15361 :
15362 0 : if (NS_WARN_IF(cloneInfo.offsetToKeyProp() >
15363 : (cloneInfo.data().data.Size() - sizeof(uint64_t)))) {
15364 0 : ASSERT_UNLESS_FUZZING();
15365 : return false;
15366 : }
15367 0 : } else if (NS_WARN_IF(aParams.cloneInfo().offsetToKeyProp())) {
15368 0 : ASSERT_UNLESS_FUZZING();
15369 : return false;
15370 : }
15371 :
15372 0 : const nsTArray<IndexUpdateInfo>& updates = aParams.indexUpdateInfos();
15373 :
15374 0 : for (uint32_t index = 0; index < updates.Length(); index++) {
15375 : RefPtr<FullIndexMetadata> indexMetadata =
15376 0 : GetMetadataForIndexId(objMetadata, updates[index].indexId());
15377 0 : if (NS_WARN_IF(!indexMetadata)) {
15378 0 : ASSERT_UNLESS_FUZZING();
15379 : return false;
15380 : }
15381 :
15382 0 : if (NS_WARN_IF(updates[index].value().IsUnset())) {
15383 0 : ASSERT_UNLESS_FUZZING();
15384 : return false;
15385 : }
15386 :
15387 0 : MOZ_ASSERT(!updates[index].value().GetBuffer().IsEmpty());
15388 : }
15389 :
15390 0 : const nsTArray<FileAddInfo>& fileAddInfos = aParams.fileAddInfos();
15391 :
15392 0 : for (uint32_t index = 0; index < fileAddInfos.Length(); index++) {
15393 0 : const FileAddInfo& fileAddInfo = fileAddInfos[index];
15394 :
15395 0 : const DatabaseOrMutableFile& file = fileAddInfo.file();
15396 0 : MOZ_ASSERT(file.type() != DatabaseOrMutableFile::T__None);
15397 :
15398 0 : switch (fileAddInfo.type()) {
15399 : case StructuredCloneFile::eBlob:
15400 0 : if (NS_WARN_IF(file.type() !=
15401 : DatabaseOrMutableFile::TPBackgroundIDBDatabaseFileParent)) {
15402 0 : ASSERT_UNLESS_FUZZING();
15403 : return false;
15404 : }
15405 0 : if (NS_WARN_IF(!file.get_PBackgroundIDBDatabaseFileParent())) {
15406 0 : ASSERT_UNLESS_FUZZING();
15407 : return false;
15408 : }
15409 0 : break;
15410 :
15411 : case StructuredCloneFile::eMutableFile: {
15412 0 : if (NS_WARN_IF(file.type() !=
15413 : DatabaseOrMutableFile::TPBackgroundMutableFileParent)) {
15414 0 : ASSERT_UNLESS_FUZZING();
15415 : return false;
15416 : }
15417 :
15418 0 : if (NS_WARN_IF(mDatabase->IsFileHandleDisabled())) {
15419 0 : ASSERT_UNLESS_FUZZING();
15420 : return false;
15421 : }
15422 :
15423 : auto mutableFile =
15424 0 : static_cast<MutableFile*>(file.get_PBackgroundMutableFileParent());
15425 :
15426 0 : if (NS_WARN_IF(!mutableFile)) {
15427 0 : ASSERT_UNLESS_FUZZING();
15428 : return false;
15429 : }
15430 :
15431 0 : Database* database = mutableFile->GetDatabase();
15432 0 : if (NS_WARN_IF(!database)) {
15433 0 : ASSERT_UNLESS_FUZZING();
15434 : return false;
15435 : }
15436 :
15437 0 : if (NS_WARN_IF(database->Id() != mDatabase->Id())) {
15438 0 : ASSERT_UNLESS_FUZZING();
15439 : return false;
15440 : }
15441 :
15442 0 : break;
15443 : }
15444 :
15445 : case StructuredCloneFile::eStructuredClone:
15446 0 : ASSERT_UNLESS_FUZZING();
15447 : return false;
15448 :
15449 : case StructuredCloneFile::eWasmBytecode:
15450 : case StructuredCloneFile::eWasmCompiled:
15451 0 : if (NS_WARN_IF(file.type() !=
15452 : DatabaseOrMutableFile::TPBackgroundIDBDatabaseFileParent)) {
15453 0 : ASSERT_UNLESS_FUZZING();
15454 : return false;
15455 : }
15456 0 : if (NS_WARN_IF(!file.get_PBackgroundIDBDatabaseFileParent())) {
15457 0 : ASSERT_UNLESS_FUZZING();
15458 : return false;
15459 : }
15460 0 : break;
15461 :
15462 : case StructuredCloneFile::eEndGuard:
15463 0 : ASSERT_UNLESS_FUZZING();
15464 : return false;
15465 :
15466 : default:
15467 0 : MOZ_CRASH("Should never get here!");
15468 : }
15469 : }
15470 :
15471 0 : return true;
15472 : }
15473 :
15474 : bool
15475 0 : TransactionBase::VerifyRequestParams(const OptionalKeyRange& aParams) const
15476 : {
15477 0 : AssertIsOnBackgroundThread();
15478 0 : MOZ_ASSERT(aParams.type() != OptionalKeyRange::T__None);
15479 :
15480 0 : switch (aParams.type()) {
15481 : case OptionalKeyRange::TSerializedKeyRange:
15482 0 : if (NS_WARN_IF(!VerifyRequestParams(aParams.get_SerializedKeyRange()))) {
15483 0 : ASSERT_UNLESS_FUZZING();
15484 : return false;
15485 : }
15486 0 : break;
15487 :
15488 : case OptionalKeyRange::Tvoid_t:
15489 0 : break;
15490 :
15491 : default:
15492 0 : MOZ_CRASH("Should never get here!");
15493 : }
15494 :
15495 0 : return true;
15496 : }
15497 :
15498 : void
15499 0 : TransactionBase::NoteActiveRequest()
15500 : {
15501 0 : AssertIsOnBackgroundThread();
15502 0 : MOZ_ASSERT(mActiveRequestCount < UINT64_MAX);
15503 :
15504 0 : mActiveRequestCount++;
15505 0 : }
15506 :
15507 : void
15508 0 : TransactionBase::NoteFinishedRequest()
15509 : {
15510 0 : AssertIsOnBackgroundThread();
15511 0 : MOZ_ASSERT(mActiveRequestCount);
15512 :
15513 0 : mActiveRequestCount--;
15514 :
15515 0 : MaybeCommitOrAbort();
15516 0 : }
15517 :
15518 : void
15519 0 : TransactionBase::Invalidate()
15520 : {
15521 0 : AssertIsOnBackgroundThread();
15522 0 : MOZ_ASSERT(mInvalidated == mInvalidatedOnAnyThread);
15523 :
15524 0 : if (!mInvalidated) {
15525 0 : mInvalidated = true;
15526 0 : mInvalidatedOnAnyThread = true;
15527 :
15528 0 : Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, /* aForce */ false);
15529 : }
15530 0 : }
15531 :
15532 : PBackgroundIDBRequestParent*
15533 0 : TransactionBase::AllocRequest(const RequestParams& aParams, bool aTrustParams)
15534 : {
15535 0 : AssertIsOnBackgroundThread();
15536 0 : MOZ_ASSERT(aParams.type() != RequestParams::T__None);
15537 :
15538 : #ifdef DEBUG
15539 : // Always verify parameters in DEBUG builds!
15540 0 : aTrustParams = false;
15541 : #endif
15542 :
15543 0 : if (!aTrustParams && NS_WARN_IF(!VerifyRequestParams(aParams))) {
15544 0 : ASSERT_UNLESS_FUZZING();
15545 : return nullptr;
15546 : }
15547 :
15548 0 : if (NS_WARN_IF(mCommitOrAbortReceived)) {
15549 0 : ASSERT_UNLESS_FUZZING();
15550 : return nullptr;
15551 : }
15552 :
15553 0 : RefPtr<NormalTransactionOp> actor;
15554 :
15555 0 : switch (aParams.type()) {
15556 : case RequestParams::TObjectStoreAddParams:
15557 : case RequestParams::TObjectStorePutParams:
15558 0 : actor = new ObjectStoreAddOrPutRequestOp(this, aParams);
15559 0 : break;
15560 :
15561 : case RequestParams::TObjectStoreGetParams:
15562 : actor =
15563 0 : new ObjectStoreGetRequestOp(this, aParams, /* aGetAll */ false);
15564 0 : break;
15565 :
15566 : case RequestParams::TObjectStoreGetAllParams:
15567 : actor =
15568 0 : new ObjectStoreGetRequestOp(this, aParams, /* aGetAll */ true);
15569 0 : break;
15570 :
15571 : case RequestParams::TObjectStoreGetKeyParams:
15572 : actor =
15573 0 : new ObjectStoreGetKeyRequestOp(this, aParams, /* aGetAll */ false);
15574 0 : break;
15575 :
15576 : case RequestParams::TObjectStoreGetAllKeysParams:
15577 : actor =
15578 0 : new ObjectStoreGetKeyRequestOp(this, aParams, /* aGetAll */ true);
15579 0 : break;
15580 :
15581 : case RequestParams::TObjectStoreDeleteParams:
15582 : actor =
15583 : new ObjectStoreDeleteRequestOp(this,
15584 0 : aParams.get_ObjectStoreDeleteParams());
15585 0 : break;
15586 :
15587 : case RequestParams::TObjectStoreClearParams:
15588 : actor =
15589 : new ObjectStoreClearRequestOp(this,
15590 0 : aParams.get_ObjectStoreClearParams());
15591 0 : break;
15592 :
15593 : case RequestParams::TObjectStoreCountParams:
15594 : actor =
15595 : new ObjectStoreCountRequestOp(this,
15596 0 : aParams.get_ObjectStoreCountParams());
15597 0 : break;
15598 :
15599 : case RequestParams::TIndexGetParams:
15600 0 : actor = new IndexGetRequestOp(this, aParams, /* aGetAll */ false);
15601 0 : break;
15602 :
15603 : case RequestParams::TIndexGetKeyParams:
15604 0 : actor = new IndexGetKeyRequestOp(this, aParams, /* aGetAll */ false);
15605 0 : break;
15606 :
15607 : case RequestParams::TIndexGetAllParams:
15608 0 : actor = new IndexGetRequestOp(this, aParams, /* aGetAll */ true);
15609 0 : break;
15610 :
15611 : case RequestParams::TIndexGetAllKeysParams:
15612 0 : actor = new IndexGetKeyRequestOp(this, aParams, /* aGetAll */ true);
15613 0 : break;
15614 :
15615 : case RequestParams::TIndexCountParams:
15616 0 : actor = new IndexCountRequestOp(this, aParams);
15617 0 : break;
15618 :
15619 : default:
15620 0 : MOZ_CRASH("Should never get here!");
15621 : }
15622 :
15623 0 : MOZ_ASSERT(actor);
15624 :
15625 : // Transfer ownership to IPDL.
15626 0 : return actor.forget().take();
15627 : }
15628 :
15629 : bool
15630 0 : TransactionBase::StartRequest(PBackgroundIDBRequestParent* aActor)
15631 : {
15632 0 : AssertIsOnBackgroundThread();
15633 0 : MOZ_ASSERT(aActor);
15634 :
15635 0 : auto* op = static_cast<NormalTransactionOp*>(aActor);
15636 :
15637 0 : if (NS_WARN_IF(!op->Init(this))) {
15638 0 : op->Cleanup();
15639 0 : return false;
15640 : }
15641 :
15642 0 : op->DispatchToConnectionPool();
15643 0 : return true;
15644 : }
15645 :
15646 : bool
15647 0 : TransactionBase::DeallocRequest(PBackgroundIDBRequestParent* aActor)
15648 : {
15649 0 : AssertIsOnBackgroundThread();
15650 0 : MOZ_ASSERT(aActor);
15651 :
15652 : // Transfer ownership back from IPDL.
15653 : RefPtr<NormalTransactionOp> actor =
15654 0 : dont_AddRef(static_cast<NormalTransactionOp*>(aActor));
15655 0 : return true;
15656 : }
15657 :
15658 : PBackgroundIDBCursorParent*
15659 0 : TransactionBase::AllocCursor(const OpenCursorParams& aParams, bool aTrustParams)
15660 : {
15661 0 : AssertIsOnBackgroundThread();
15662 0 : MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
15663 :
15664 : #ifdef DEBUG
15665 : // Always verify parameters in DEBUG builds!
15666 0 : aTrustParams = false;
15667 : #endif
15668 :
15669 0 : OpenCursorParams::Type type = aParams.type();
15670 0 : RefPtr<FullObjectStoreMetadata> objectStoreMetadata;
15671 0 : RefPtr<FullIndexMetadata> indexMetadata;
15672 : Cursor::Direction direction;
15673 :
15674 0 : switch (type) {
15675 : case OpenCursorParams::TObjectStoreOpenCursorParams: {
15676 : const ObjectStoreOpenCursorParams& params =
15677 0 : aParams.get_ObjectStoreOpenCursorParams();
15678 0 : objectStoreMetadata = GetMetadataForObjectStoreId(params.objectStoreId());
15679 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15680 0 : ASSERT_UNLESS_FUZZING();
15681 : return nullptr;
15682 : }
15683 0 : if (aTrustParams &&
15684 0 : NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15685 0 : ASSERT_UNLESS_FUZZING();
15686 : return nullptr;
15687 : }
15688 0 : direction = params.direction();
15689 0 : break;
15690 : }
15691 :
15692 : case OpenCursorParams::TObjectStoreOpenKeyCursorParams: {
15693 : const ObjectStoreOpenKeyCursorParams& params =
15694 0 : aParams.get_ObjectStoreOpenKeyCursorParams();
15695 0 : objectStoreMetadata = GetMetadataForObjectStoreId(params.objectStoreId());
15696 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15697 0 : ASSERT_UNLESS_FUZZING();
15698 : return nullptr;
15699 : }
15700 0 : if (aTrustParams &&
15701 0 : NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15702 0 : ASSERT_UNLESS_FUZZING();
15703 : return nullptr;
15704 : }
15705 0 : direction = params.direction();
15706 0 : break;
15707 : }
15708 :
15709 : case OpenCursorParams::TIndexOpenCursorParams: {
15710 0 : const IndexOpenCursorParams& params = aParams.get_IndexOpenCursorParams();
15711 0 : objectStoreMetadata = GetMetadataForObjectStoreId(params.objectStoreId());
15712 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15713 0 : ASSERT_UNLESS_FUZZING();
15714 : return nullptr;
15715 : }
15716 : indexMetadata =
15717 0 : GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15718 0 : if (NS_WARN_IF(!indexMetadata)) {
15719 0 : ASSERT_UNLESS_FUZZING();
15720 : return nullptr;
15721 : }
15722 0 : if (aTrustParams &&
15723 0 : NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15724 0 : ASSERT_UNLESS_FUZZING();
15725 : return nullptr;
15726 : }
15727 0 : direction = params.direction();
15728 0 : break;
15729 : }
15730 :
15731 : case OpenCursorParams::TIndexOpenKeyCursorParams: {
15732 : const IndexOpenKeyCursorParams& params =
15733 0 : aParams.get_IndexOpenKeyCursorParams();
15734 0 : objectStoreMetadata = GetMetadataForObjectStoreId(params.objectStoreId());
15735 0 : if (NS_WARN_IF(!objectStoreMetadata)) {
15736 0 : ASSERT_UNLESS_FUZZING();
15737 : return nullptr;
15738 : }
15739 : indexMetadata =
15740 0 : GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15741 0 : if (NS_WARN_IF(!indexMetadata)) {
15742 0 : ASSERT_UNLESS_FUZZING();
15743 : return nullptr;
15744 : }
15745 0 : if (aTrustParams &&
15746 0 : NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15747 0 : ASSERT_UNLESS_FUZZING();
15748 : return nullptr;
15749 : }
15750 0 : direction = params.direction();
15751 0 : break;
15752 : }
15753 :
15754 : default:
15755 0 : MOZ_CRASH("Should never get here!");
15756 : }
15757 :
15758 0 : if (NS_WARN_IF(mCommitOrAbortReceived)) {
15759 0 : ASSERT_UNLESS_FUZZING();
15760 : return nullptr;
15761 : }
15762 :
15763 : RefPtr<Cursor> actor =
15764 0 : new Cursor(this, type, objectStoreMetadata, indexMetadata, direction);
15765 :
15766 : // Transfer ownership to IPDL.
15767 0 : return actor.forget().take();
15768 : }
15769 :
15770 : bool
15771 0 : TransactionBase::StartCursor(PBackgroundIDBCursorParent* aActor,
15772 : const OpenCursorParams& aParams)
15773 : {
15774 0 : AssertIsOnBackgroundThread();
15775 0 : MOZ_ASSERT(aActor);
15776 0 : MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
15777 :
15778 0 : auto* op = static_cast<Cursor*>(aActor);
15779 :
15780 0 : if (NS_WARN_IF(!op->Start(aParams))) {
15781 0 : return false;
15782 : }
15783 :
15784 0 : return true;
15785 : }
15786 :
15787 : bool
15788 0 : TransactionBase::DeallocCursor(PBackgroundIDBCursorParent* aActor)
15789 : {
15790 0 : AssertIsOnBackgroundThread();
15791 0 : MOZ_ASSERT(aActor);
15792 :
15793 : // Transfer ownership back from IPDL.
15794 0 : RefPtr<Cursor> actor = dont_AddRef(static_cast<Cursor*>(aActor));
15795 0 : return true;
15796 : }
15797 :
15798 : /*******************************************************************************
15799 : * NormalTransaction
15800 : ******************************************************************************/
15801 :
15802 0 : NormalTransaction::NormalTransaction(
15803 : Database* aDatabase,
15804 : TransactionBase::Mode aMode,
15805 0 : nsTArray<RefPtr<FullObjectStoreMetadata>>& aObjectStores)
15806 0 : : TransactionBase(aDatabase, aMode)
15807 : {
15808 0 : AssertIsOnBackgroundThread();
15809 0 : MOZ_ASSERT(!aObjectStores.IsEmpty());
15810 :
15811 0 : mObjectStores.SwapElements(aObjectStores);
15812 0 : }
15813 :
15814 : bool
15815 0 : NormalTransaction::IsSameProcessActor()
15816 : {
15817 0 : AssertIsOnBackgroundThread();
15818 :
15819 0 : PBackgroundParent* actor = Manager()->Manager()->Manager();
15820 0 : MOZ_ASSERT(actor);
15821 :
15822 0 : return !BackgroundParent::IsOtherProcessActor(actor);
15823 : }
15824 :
15825 : void
15826 0 : NormalTransaction::SendCompleteNotification(nsresult aResult)
15827 : {
15828 0 : AssertIsOnBackgroundThread();
15829 :
15830 0 : if (!IsActorDestroyed()) {
15831 0 : Unused << SendComplete(aResult);
15832 : }
15833 0 : }
15834 :
15835 : void
15836 0 : NormalTransaction::ActorDestroy(ActorDestroyReason aWhy)
15837 : {
15838 0 : AssertIsOnBackgroundThread();
15839 :
15840 0 : NoteActorDestroyed();
15841 :
15842 0 : if (!mCommittedOrAborted) {
15843 0 : if (NS_SUCCEEDED(mResultCode)) {
15844 0 : IDB_REPORT_INTERNAL_ERR();
15845 0 : mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
15846 : }
15847 :
15848 0 : mForceAborted = true;
15849 :
15850 0 : MaybeCommitOrAbort();
15851 : }
15852 0 : }
15853 :
15854 : mozilla::ipc::IPCResult
15855 0 : NormalTransaction::RecvDeleteMe()
15856 : {
15857 0 : AssertIsOnBackgroundThread();
15858 0 : MOZ_ASSERT(!IsActorDestroyed());
15859 :
15860 0 : IProtocol* mgr = Manager();
15861 0 : if (!PBackgroundIDBTransactionParent::Send__delete__(this)) {
15862 0 : return IPC_FAIL_NO_REASON(mgr);
15863 : }
15864 0 : return IPC_OK();
15865 : }
15866 :
15867 : mozilla::ipc::IPCResult
15868 0 : NormalTransaction::RecvCommit()
15869 : {
15870 0 : AssertIsOnBackgroundThread();
15871 :
15872 0 : if (!TransactionBase::RecvCommit()) {
15873 0 : return IPC_FAIL_NO_REASON(this);
15874 : }
15875 0 : return IPC_OK();
15876 : }
15877 :
15878 : mozilla::ipc::IPCResult
15879 0 : NormalTransaction::RecvAbort(const nsresult& aResultCode)
15880 : {
15881 0 : AssertIsOnBackgroundThread();
15882 :
15883 0 : if (!TransactionBase::RecvAbort(aResultCode)) {
15884 0 : return IPC_FAIL_NO_REASON(this);
15885 : }
15886 0 : return IPC_OK();
15887 : }
15888 :
15889 : PBackgroundIDBRequestParent*
15890 0 : NormalTransaction::AllocPBackgroundIDBRequestParent(
15891 : const RequestParams& aParams)
15892 : {
15893 0 : AssertIsOnBackgroundThread();
15894 0 : MOZ_ASSERT(aParams.type() != RequestParams::T__None);
15895 :
15896 0 : return AllocRequest(aParams, IsSameProcessActor());
15897 : }
15898 :
15899 : mozilla::ipc::IPCResult
15900 0 : NormalTransaction::RecvPBackgroundIDBRequestConstructor(
15901 : PBackgroundIDBRequestParent* aActor,
15902 : const RequestParams& aParams)
15903 : {
15904 0 : AssertIsOnBackgroundThread();
15905 0 : MOZ_ASSERT(aActor);
15906 0 : MOZ_ASSERT(aParams.type() != RequestParams::T__None);
15907 :
15908 0 : if (!StartRequest(aActor)) {
15909 0 : return IPC_FAIL_NO_REASON(this);
15910 : }
15911 0 : return IPC_OK();
15912 : }
15913 :
15914 : bool
15915 0 : NormalTransaction::DeallocPBackgroundIDBRequestParent(
15916 : PBackgroundIDBRequestParent* aActor)
15917 : {
15918 0 : AssertIsOnBackgroundThread();
15919 0 : MOZ_ASSERT(aActor);
15920 :
15921 0 : return DeallocRequest(aActor);
15922 : }
15923 :
15924 : PBackgroundIDBCursorParent*
15925 0 : NormalTransaction::AllocPBackgroundIDBCursorParent(
15926 : const OpenCursorParams& aParams)
15927 : {
15928 0 : AssertIsOnBackgroundThread();
15929 :
15930 0 : return AllocCursor(aParams, IsSameProcessActor());
15931 : }
15932 :
15933 : mozilla::ipc::IPCResult
15934 0 : NormalTransaction::RecvPBackgroundIDBCursorConstructor(
15935 : PBackgroundIDBCursorParent* aActor,
15936 : const OpenCursorParams& aParams)
15937 : {
15938 0 : AssertIsOnBackgroundThread();
15939 0 : MOZ_ASSERT(aActor);
15940 0 : MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
15941 :
15942 0 : if (!StartCursor(aActor, aParams)) {
15943 0 : return IPC_FAIL_NO_REASON(this);
15944 : }
15945 0 : return IPC_OK();
15946 : }
15947 :
15948 : bool
15949 0 : NormalTransaction::DeallocPBackgroundIDBCursorParent(
15950 : PBackgroundIDBCursorParent* aActor)
15951 : {
15952 0 : AssertIsOnBackgroundThread();
15953 0 : MOZ_ASSERT(aActor);
15954 :
15955 0 : return DeallocCursor(aActor);
15956 : }
15957 :
15958 : /*******************************************************************************
15959 : * VersionChangeTransaction
15960 : ******************************************************************************/
15961 :
15962 0 : VersionChangeTransaction::VersionChangeTransaction(
15963 0 : OpenDatabaseOp* aOpenDatabaseOp)
15964 : : TransactionBase(aOpenDatabaseOp->mDatabase,
15965 : IDBTransaction::VERSION_CHANGE)
15966 : , mOpenDatabaseOp(aOpenDatabaseOp)
15967 0 : , mActorWasAlive(false)
15968 : {
15969 0 : AssertIsOnBackgroundThread();
15970 0 : MOZ_ASSERT(aOpenDatabaseOp);
15971 0 : }
15972 :
15973 0 : VersionChangeTransaction::~VersionChangeTransaction()
15974 : {
15975 : #ifdef DEBUG
15976 : // Silence the base class' destructor assertion if we never made this actor
15977 : // live.
15978 0 : FakeActorDestroyed();
15979 : #endif
15980 0 : }
15981 :
15982 : bool
15983 0 : VersionChangeTransaction::IsSameProcessActor()
15984 : {
15985 0 : AssertIsOnBackgroundThread();
15986 :
15987 0 : PBackgroundParent* actor = Manager()->Manager()->Manager();
15988 0 : MOZ_ASSERT(actor);
15989 :
15990 0 : return !BackgroundParent::IsOtherProcessActor(actor);
15991 : }
15992 :
15993 : void
15994 0 : VersionChangeTransaction::SetActorAlive()
15995 : {
15996 0 : AssertIsOnBackgroundThread();
15997 0 : MOZ_ASSERT(!mActorWasAlive);
15998 0 : MOZ_ASSERT(!IsActorDestroyed());
15999 :
16000 0 : mActorWasAlive = true;
16001 :
16002 : // This reference will be absorbed by IPDL and released when the actor is
16003 : // destroyed.
16004 0 : AddRef();
16005 0 : }
16006 :
16007 : bool
16008 0 : VersionChangeTransaction::CopyDatabaseMetadata()
16009 : {
16010 0 : AssertIsOnBackgroundThread();
16011 0 : MOZ_ASSERT(!mOldMetadata);
16012 :
16013 : const RefPtr<FullDatabaseMetadata> origMetadata =
16014 0 : GetDatabase()->Metadata();
16015 0 : MOZ_ASSERT(origMetadata);
16016 :
16017 0 : RefPtr<FullDatabaseMetadata> newMetadata = origMetadata->Duplicate();
16018 0 : if (NS_WARN_IF(!newMetadata)) {
16019 0 : return false;
16020 : }
16021 :
16022 : // Replace the live metadata with the new mutable copy.
16023 : DatabaseActorInfo* info;
16024 0 : MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(origMetadata->mDatabaseId,
16025 : &info));
16026 0 : MOZ_ASSERT(!info->mLiveDatabases.IsEmpty());
16027 0 : MOZ_ASSERT(info->mMetadata == origMetadata);
16028 :
16029 0 : mOldMetadata = info->mMetadata.forget();
16030 0 : info->mMetadata.swap(newMetadata);
16031 :
16032 : // Replace metadata pointers for all live databases.
16033 0 : for (uint32_t count = info->mLiveDatabases.Length(), index = 0;
16034 0 : index < count;
16035 : index++) {
16036 0 : info->mLiveDatabases[index]->mMetadata = info->mMetadata;
16037 : }
16038 :
16039 0 : return true;
16040 : }
16041 :
16042 : void
16043 0 : VersionChangeTransaction::UpdateMetadata(nsresult aResult)
16044 : {
16045 0 : AssertIsOnBackgroundThread();
16046 0 : MOZ_ASSERT(GetDatabase());
16047 0 : MOZ_ASSERT(mOpenDatabaseOp);
16048 0 : MOZ_ASSERT(!!mActorWasAlive == !!mOpenDatabaseOp->mDatabase);
16049 0 : MOZ_ASSERT_IF(mActorWasAlive, !mOpenDatabaseOp->mDatabaseId.IsEmpty());
16050 :
16051 0 : if (IsActorDestroyed() || !mActorWasAlive) {
16052 0 : return;
16053 : }
16054 :
16055 0 : RefPtr<FullDatabaseMetadata> oldMetadata;
16056 0 : mOldMetadata.swap(oldMetadata);
16057 :
16058 : DatabaseActorInfo* info;
16059 0 : if (!gLiveDatabaseHashtable->Get(oldMetadata->mDatabaseId, &info)) {
16060 0 : return;
16061 : }
16062 :
16063 0 : MOZ_ASSERT(!info->mLiveDatabases.IsEmpty());
16064 :
16065 0 : if (NS_SUCCEEDED(aResult)) {
16066 : // Remove all deleted objectStores and indexes, then mark immutable.
16067 0 : for (auto objectStoreIter = info->mMetadata->mObjectStores.Iter();
16068 0 : !objectStoreIter.Done();
16069 0 : objectStoreIter.Next()) {
16070 0 : MOZ_ASSERT(objectStoreIter.Key());
16071 0 : RefPtr<FullObjectStoreMetadata>& metadata = objectStoreIter.Data();
16072 0 : MOZ_ASSERT(metadata);
16073 :
16074 0 : if (metadata->mDeleted) {
16075 0 : objectStoreIter.Remove();
16076 0 : continue;
16077 : }
16078 :
16079 0 : for (auto indexIter = metadata->mIndexes.Iter();
16080 0 : !indexIter.Done();
16081 0 : indexIter.Next()) {
16082 0 : MOZ_ASSERT(indexIter.Key());
16083 0 : RefPtr<FullIndexMetadata>& index = indexIter.Data();
16084 0 : MOZ_ASSERT(index);
16085 :
16086 0 : if (index->mDeleted) {
16087 0 : indexIter.Remove();
16088 : }
16089 : }
16090 : #ifdef DEBUG
16091 0 : metadata->mIndexes.MarkImmutable();
16092 : #endif
16093 : }
16094 : #ifdef DEBUG
16095 0 : info->mMetadata->mObjectStores.MarkImmutable();
16096 : #endif
16097 : } else {
16098 : // Replace metadata pointers for all live databases.
16099 0 : info->mMetadata = oldMetadata.forget();
16100 :
16101 0 : for (uint32_t count = info->mLiveDatabases.Length(), index = 0;
16102 0 : index < count;
16103 : index++) {
16104 0 : info->mLiveDatabases[index]->mMetadata = info->mMetadata;
16105 : }
16106 : }
16107 : }
16108 :
16109 : void
16110 0 : VersionChangeTransaction::SendCompleteNotification(nsresult aResult)
16111 : {
16112 0 : AssertIsOnBackgroundThread();
16113 0 : MOZ_ASSERT(mOpenDatabaseOp);
16114 0 : MOZ_ASSERT_IF(!mActorWasAlive, NS_FAILED(mOpenDatabaseOp->mResultCode));
16115 0 : MOZ_ASSERT_IF(!mActorWasAlive,
16116 : mOpenDatabaseOp->mState > OpenDatabaseOp::State::SendingResults);
16117 :
16118 0 : RefPtr<OpenDatabaseOp> openDatabaseOp;
16119 0 : mOpenDatabaseOp.swap(openDatabaseOp);
16120 :
16121 0 : if (!mActorWasAlive) {
16122 0 : return;
16123 : }
16124 :
16125 0 : if (NS_FAILED(aResult) && NS_SUCCEEDED(openDatabaseOp->mResultCode)) {
16126 : // 3.3.1 Opening a database:
16127 : // "If the upgrade transaction was aborted, run the steps for closing a
16128 : // database connection with connection, create and return a new AbortError
16129 : // exception and abort these steps."
16130 0 : openDatabaseOp->mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
16131 : }
16132 :
16133 0 : openDatabaseOp->mState = OpenDatabaseOp::State::SendingResults;
16134 :
16135 0 : if (!IsActorDestroyed()) {
16136 0 : Unused << SendComplete(aResult);
16137 : }
16138 :
16139 0 : MOZ_ALWAYS_SUCCEEDS(openDatabaseOp->Run());
16140 : }
16141 :
16142 : void
16143 0 : VersionChangeTransaction::ActorDestroy(ActorDestroyReason aWhy)
16144 : {
16145 0 : AssertIsOnBackgroundThread();
16146 :
16147 0 : NoteActorDestroyed();
16148 :
16149 0 : if (!mCommittedOrAborted) {
16150 0 : if (NS_SUCCEEDED(mResultCode)) {
16151 0 : IDB_REPORT_INTERNAL_ERR();
16152 0 : mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
16153 : }
16154 :
16155 0 : mForceAborted = true;
16156 :
16157 0 : MaybeCommitOrAbort();
16158 : }
16159 0 : }
16160 :
16161 : mozilla::ipc::IPCResult
16162 0 : VersionChangeTransaction::RecvDeleteMe()
16163 : {
16164 0 : AssertIsOnBackgroundThread();
16165 0 : MOZ_ASSERT(!IsActorDestroyed());
16166 :
16167 0 : IProtocol* mgr = Manager();
16168 0 : if (!PBackgroundIDBVersionChangeTransactionParent::Send__delete__(this)) {
16169 0 : return IPC_FAIL_NO_REASON(mgr);
16170 : }
16171 0 : return IPC_OK();
16172 : }
16173 :
16174 : mozilla::ipc::IPCResult
16175 0 : VersionChangeTransaction::RecvCommit()
16176 : {
16177 0 : AssertIsOnBackgroundThread();
16178 :
16179 0 : if (!TransactionBase::RecvCommit()) {
16180 0 : return IPC_FAIL_NO_REASON(this);
16181 : }
16182 0 : return IPC_OK();
16183 : }
16184 :
16185 : mozilla::ipc::IPCResult
16186 0 : VersionChangeTransaction::RecvAbort(const nsresult& aResultCode)
16187 : {
16188 0 : AssertIsOnBackgroundThread();
16189 :
16190 0 : if (!TransactionBase::RecvAbort(aResultCode)) {
16191 0 : return IPC_FAIL_NO_REASON(this);
16192 : }
16193 0 : return IPC_OK();
16194 : }
16195 :
16196 : mozilla::ipc::IPCResult
16197 0 : VersionChangeTransaction::RecvCreateObjectStore(
16198 : const ObjectStoreMetadata& aMetadata)
16199 : {
16200 0 : AssertIsOnBackgroundThread();
16201 :
16202 0 : if (NS_WARN_IF(!aMetadata.id())) {
16203 0 : ASSERT_UNLESS_FUZZING();
16204 : return IPC_FAIL_NO_REASON(this);
16205 : }
16206 :
16207 0 : const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
16208 0 : MOZ_ASSERT(dbMetadata);
16209 :
16210 0 : if (NS_WARN_IF(aMetadata.id() != dbMetadata->mNextObjectStoreId)) {
16211 0 : ASSERT_UNLESS_FUZZING();
16212 : return IPC_FAIL_NO_REASON(this);
16213 : }
16214 :
16215 : auto* foundMetadata =
16216 0 : MetadataNameOrIdMatcher<FullObjectStoreMetadata>::Match(
16217 0 : dbMetadata->mObjectStores, aMetadata.id(), aMetadata.name());
16218 :
16219 0 : if (NS_WARN_IF(foundMetadata)) {
16220 0 : ASSERT_UNLESS_FUZZING();
16221 : return IPC_FAIL_NO_REASON(this);
16222 : }
16223 :
16224 0 : if (NS_WARN_IF(mCommitOrAbortReceived)) {
16225 0 : ASSERT_UNLESS_FUZZING();
16226 : return IPC_FAIL_NO_REASON(this);
16227 : }
16228 :
16229 0 : RefPtr<FullObjectStoreMetadata> newMetadata = new FullObjectStoreMetadata();
16230 0 : newMetadata->mCommonMetadata = aMetadata;
16231 0 : newMetadata->mNextAutoIncrementId = aMetadata.autoIncrement() ? 1 : 0;
16232 0 : newMetadata->mCommittedAutoIncrementId = newMetadata->mNextAutoIncrementId;
16233 :
16234 0 : if (NS_WARN_IF(!dbMetadata->mObjectStores.Put(aMetadata.id(), newMetadata,
16235 : fallible))) {
16236 0 : return IPC_FAIL_NO_REASON(this);
16237 : }
16238 :
16239 0 : dbMetadata->mNextObjectStoreId++;
16240 :
16241 0 : RefPtr<CreateObjectStoreOp> op = new CreateObjectStoreOp(this, aMetadata);
16242 :
16243 0 : if (NS_WARN_IF(!op->Init(this))) {
16244 0 : op->Cleanup();
16245 0 : return IPC_FAIL_NO_REASON(this);
16246 : }
16247 :
16248 0 : op->DispatchToConnectionPool();
16249 :
16250 0 : return IPC_OK();
16251 : }
16252 :
16253 : mozilla::ipc::IPCResult
16254 0 : VersionChangeTransaction::RecvDeleteObjectStore(const int64_t& aObjectStoreId)
16255 : {
16256 0 : AssertIsOnBackgroundThread();
16257 :
16258 0 : if (NS_WARN_IF(!aObjectStoreId)) {
16259 0 : ASSERT_UNLESS_FUZZING();
16260 : return IPC_FAIL_NO_REASON(this);
16261 : }
16262 :
16263 0 : const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
16264 0 : MOZ_ASSERT(dbMetadata);
16265 0 : MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0);
16266 :
16267 0 : if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) {
16268 0 : ASSERT_UNLESS_FUZZING();
16269 : return IPC_FAIL_NO_REASON(this);
16270 : }
16271 :
16272 : RefPtr<FullObjectStoreMetadata> foundMetadata =
16273 0 : GetMetadataForObjectStoreId(aObjectStoreId);
16274 :
16275 0 : if (NS_WARN_IF(!foundMetadata)) {
16276 0 : ASSERT_UNLESS_FUZZING();
16277 : return IPC_FAIL_NO_REASON(this);
16278 : }
16279 :
16280 0 : if (NS_WARN_IF(mCommitOrAbortReceived)) {
16281 0 : ASSERT_UNLESS_FUZZING();
16282 : return IPC_FAIL_NO_REASON(this);
16283 : }
16284 :
16285 0 : foundMetadata->mDeleted = true;
16286 :
16287 0 : bool isLastObjectStore = true;
16288 0 : DebugOnly<bool> foundTargetId = false;
16289 0 : for (auto iter = dbMetadata->mObjectStores.Iter();
16290 0 : !iter.Done();
16291 0 : iter.Next()) {
16292 0 : if (uint64_t(aObjectStoreId) == iter.Key()) {
16293 0 : foundTargetId = true;
16294 0 : } else if (!iter.UserData()->mDeleted) {
16295 0 : isLastObjectStore = false;
16296 0 : break;
16297 : }
16298 : }
16299 0 : MOZ_ASSERT_IF(isLastObjectStore, foundTargetId);
16300 :
16301 : RefPtr<DeleteObjectStoreOp> op =
16302 0 : new DeleteObjectStoreOp(this, foundMetadata, isLastObjectStore);
16303 :
16304 0 : if (NS_WARN_IF(!op->Init(this))) {
16305 0 : op->Cleanup();
16306 0 : return IPC_FAIL_NO_REASON(this);
16307 : }
16308 :
16309 0 : op->DispatchToConnectionPool();
16310 :
16311 0 : return IPC_OK();
16312 : }
16313 :
16314 : mozilla::ipc::IPCResult
16315 0 : VersionChangeTransaction::RecvRenameObjectStore(const int64_t& aObjectStoreId,
16316 : const nsString& aName)
16317 : {
16318 0 : AssertIsOnBackgroundThread();
16319 :
16320 0 : if (NS_WARN_IF(!aObjectStoreId)) {
16321 0 : ASSERT_UNLESS_FUZZING();
16322 : return IPC_FAIL_NO_REASON(this);
16323 : }
16324 :
16325 0 : const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
16326 0 : MOZ_ASSERT(dbMetadata);
16327 0 : MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0);
16328 :
16329 0 : if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) {
16330 0 : ASSERT_UNLESS_FUZZING();
16331 : return IPC_FAIL_NO_REASON(this);
16332 : }
16333 :
16334 : RefPtr<FullObjectStoreMetadata> foundMetadata =
16335 0 : GetMetadataForObjectStoreId(aObjectStoreId);
16336 :
16337 0 : if (NS_WARN_IF(!foundMetadata)) {
16338 0 : ASSERT_UNLESS_FUZZING();
16339 : return IPC_FAIL_NO_REASON(this);
16340 : }
16341 :
16342 0 : if (NS_WARN_IF(mCommitOrAbortReceived)) {
16343 0 : ASSERT_UNLESS_FUZZING();
16344 : return IPC_FAIL_NO_REASON(this);
16345 : }
16346 :
16347 0 : foundMetadata->mCommonMetadata.name() = aName;
16348 :
16349 : RefPtr<RenameObjectStoreOp> renameOp =
16350 0 : new RenameObjectStoreOp(this, foundMetadata);
16351 :
16352 0 : if (NS_WARN_IF(!renameOp->Init(this))) {
16353 0 : renameOp->Cleanup();
16354 0 : return IPC_FAIL_NO_REASON(this);
16355 : }
16356 :
16357 0 : renameOp->DispatchToConnectionPool();
16358 :
16359 0 : return IPC_OK();
16360 : }
16361 :
16362 : mozilla::ipc::IPCResult
16363 0 : VersionChangeTransaction::RecvCreateIndex(const int64_t& aObjectStoreId,
16364 : const IndexMetadata& aMetadata)
16365 : {
16366 0 : AssertIsOnBackgroundThread();
16367 :
16368 0 : if (NS_WARN_IF(!aObjectStoreId)) {
16369 0 : ASSERT_UNLESS_FUZZING();
16370 : return IPC_FAIL_NO_REASON(this);
16371 : }
16372 :
16373 0 : if (NS_WARN_IF(!aMetadata.id())) {
16374 0 : ASSERT_UNLESS_FUZZING();
16375 : return IPC_FAIL_NO_REASON(this);
16376 : }
16377 :
16378 0 : const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
16379 0 : MOZ_ASSERT(dbMetadata);
16380 :
16381 0 : if (NS_WARN_IF(aMetadata.id() != dbMetadata->mNextIndexId)) {
16382 0 : ASSERT_UNLESS_FUZZING();
16383 : return IPC_FAIL_NO_REASON(this);
16384 : }
16385 :
16386 : RefPtr<FullObjectStoreMetadata> foundObjectStoreMetadata =
16387 0 : GetMetadataForObjectStoreId(aObjectStoreId);
16388 :
16389 0 : if (NS_WARN_IF(!foundObjectStoreMetadata)) {
16390 0 : ASSERT_UNLESS_FUZZING();
16391 : return IPC_FAIL_NO_REASON(this);
16392 : }
16393 :
16394 : RefPtr<FullIndexMetadata> foundIndexMetadata =
16395 : MetadataNameOrIdMatcher<FullIndexMetadata>::Match(
16396 0 : foundObjectStoreMetadata->mIndexes, aMetadata.id(), aMetadata.name());
16397 :
16398 0 : if (NS_WARN_IF(foundIndexMetadata)) {
16399 0 : ASSERT_UNLESS_FUZZING();
16400 : return IPC_FAIL_NO_REASON(this);
16401 : }
16402 :
16403 0 : if (NS_WARN_IF(mCommitOrAbortReceived)) {
16404 0 : ASSERT_UNLESS_FUZZING();
16405 : return IPC_FAIL_NO_REASON(this);
16406 : }
16407 :
16408 0 : RefPtr<FullIndexMetadata> newMetadata = new FullIndexMetadata();
16409 0 : newMetadata->mCommonMetadata = aMetadata;
16410 :
16411 0 : if (NS_WARN_IF(!foundObjectStoreMetadata->mIndexes.Put(aMetadata.id(),
16412 : newMetadata,
16413 : fallible))) {
16414 0 : return IPC_FAIL_NO_REASON(this);
16415 : }
16416 :
16417 0 : dbMetadata->mNextIndexId++;
16418 :
16419 : RefPtr<CreateIndexOp> op =
16420 0 : new CreateIndexOp(this, aObjectStoreId, aMetadata);
16421 :
16422 0 : if (NS_WARN_IF(!op->Init(this))) {
16423 0 : op->Cleanup();
16424 0 : return IPC_FAIL_NO_REASON(this);
16425 : }
16426 :
16427 0 : op->DispatchToConnectionPool();
16428 :
16429 0 : return IPC_OK();
16430 : }
16431 :
16432 : mozilla::ipc::IPCResult
16433 0 : VersionChangeTransaction::RecvDeleteIndex(const int64_t& aObjectStoreId,
16434 : const int64_t& aIndexId)
16435 : {
16436 0 : AssertIsOnBackgroundThread();
16437 :
16438 0 : if (NS_WARN_IF(!aObjectStoreId)) {
16439 0 : ASSERT_UNLESS_FUZZING();
16440 : return IPC_FAIL_NO_REASON(this);
16441 : }
16442 :
16443 0 : if (NS_WARN_IF(!aIndexId)) {
16444 0 : ASSERT_UNLESS_FUZZING();
16445 : return IPC_FAIL_NO_REASON(this);
16446 : }
16447 :
16448 0 : const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
16449 0 : MOZ_ASSERT(dbMetadata);
16450 0 : MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0);
16451 0 : MOZ_ASSERT(dbMetadata->mNextIndexId > 0);
16452 :
16453 0 : if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) {
16454 0 : ASSERT_UNLESS_FUZZING();
16455 : return IPC_FAIL_NO_REASON(this);
16456 : }
16457 :
16458 0 : if (NS_WARN_IF(aIndexId >= dbMetadata->mNextIndexId)) {
16459 0 : ASSERT_UNLESS_FUZZING();
16460 : return IPC_FAIL_NO_REASON(this);
16461 : }
16462 :
16463 : RefPtr<FullObjectStoreMetadata> foundObjectStoreMetadata =
16464 0 : GetMetadataForObjectStoreId(aObjectStoreId);
16465 :
16466 0 : if (NS_WARN_IF(!foundObjectStoreMetadata)) {
16467 0 : ASSERT_UNLESS_FUZZING();
16468 : return IPC_FAIL_NO_REASON(this);
16469 : }
16470 :
16471 : RefPtr<FullIndexMetadata> foundIndexMetadata =
16472 0 : GetMetadataForIndexId(foundObjectStoreMetadata, aIndexId);
16473 :
16474 0 : if (NS_WARN_IF(!foundIndexMetadata)) {
16475 0 : ASSERT_UNLESS_FUZZING();
16476 : return IPC_FAIL_NO_REASON(this);
16477 : }
16478 :
16479 0 : if (NS_WARN_IF(mCommitOrAbortReceived)) {
16480 0 : ASSERT_UNLESS_FUZZING();
16481 : return IPC_FAIL_NO_REASON(this);
16482 : }
16483 :
16484 0 : foundIndexMetadata->mDeleted = true;
16485 :
16486 0 : bool isLastIndex = true;
16487 0 : DebugOnly<bool> foundTargetId = false;
16488 0 : for (auto iter = foundObjectStoreMetadata->mIndexes.ConstIter();
16489 0 : !iter.Done();
16490 0 : iter.Next()) {
16491 0 : if (uint64_t(aIndexId) == iter.Key()) {
16492 0 : foundTargetId = true;
16493 0 : } else if (!iter.UserData()->mDeleted) {
16494 0 : isLastIndex = false;
16495 0 : break;
16496 : }
16497 : }
16498 0 : MOZ_ASSERT_IF(isLastIndex, foundTargetId);
16499 :
16500 : RefPtr<DeleteIndexOp> op =
16501 : new DeleteIndexOp(this,
16502 : aObjectStoreId,
16503 : aIndexId,
16504 0 : foundIndexMetadata->mCommonMetadata.unique(),
16505 0 : isLastIndex);
16506 :
16507 0 : if (NS_WARN_IF(!op->Init(this))) {
16508 0 : op->Cleanup();
16509 0 : return IPC_FAIL_NO_REASON(this);
16510 : }
16511 :
16512 0 : op->DispatchToConnectionPool();
16513 :
16514 0 : return IPC_OK();
16515 : }
16516 :
16517 : mozilla::ipc::IPCResult
16518 0 : VersionChangeTransaction::RecvRenameIndex(const int64_t& aObjectStoreId,
16519 : const int64_t& aIndexId,
16520 : const nsString& aName)
16521 : {
16522 0 : AssertIsOnBackgroundThread();
16523 :
16524 0 : if (NS_WARN_IF(!aObjectStoreId)) {
16525 0 : ASSERT_UNLESS_FUZZING();
16526 : return IPC_FAIL_NO_REASON(this);
16527 : }
16528 :
16529 0 : if (NS_WARN_IF(!aIndexId)) {
16530 0 : ASSERT_UNLESS_FUZZING();
16531 : return IPC_FAIL_NO_REASON(this);
16532 : }
16533 :
16534 0 : const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
16535 0 : MOZ_ASSERT(dbMetadata);
16536 0 : MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0);
16537 0 : MOZ_ASSERT(dbMetadata->mNextIndexId > 0);
16538 :
16539 0 : if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) {
16540 0 : ASSERT_UNLESS_FUZZING();
16541 : return IPC_FAIL_NO_REASON(this);
16542 : }
16543 :
16544 0 : if (NS_WARN_IF(aIndexId >= dbMetadata->mNextIndexId)) {
16545 0 : ASSERT_UNLESS_FUZZING();
16546 : return IPC_FAIL_NO_REASON(this);
16547 : }
16548 :
16549 : RefPtr<FullObjectStoreMetadata> foundObjectStoreMetadata =
16550 0 : GetMetadataForObjectStoreId(aObjectStoreId);
16551 :
16552 0 : if (NS_WARN_IF(!foundObjectStoreMetadata)) {
16553 0 : ASSERT_UNLESS_FUZZING();
16554 : return IPC_FAIL_NO_REASON(this);
16555 : }
16556 :
16557 : RefPtr<FullIndexMetadata> foundIndexMetadata =
16558 0 : GetMetadataForIndexId(foundObjectStoreMetadata, aIndexId);
16559 :
16560 0 : if (NS_WARN_IF(!foundIndexMetadata)) {
16561 0 : ASSERT_UNLESS_FUZZING();
16562 : return IPC_FAIL_NO_REASON(this);
16563 : }
16564 :
16565 0 : if (NS_WARN_IF(mCommitOrAbortReceived)) {
16566 0 : ASSERT_UNLESS_FUZZING();
16567 : return IPC_FAIL_NO_REASON(this);
16568 : }
16569 :
16570 0 : foundIndexMetadata->mCommonMetadata.name() = aName;
16571 :
16572 : RefPtr<RenameIndexOp> renameOp =
16573 0 : new RenameIndexOp(this, foundIndexMetadata, aObjectStoreId);
16574 :
16575 0 : if (NS_WARN_IF(!renameOp->Init(this))) {
16576 0 : renameOp->Cleanup();
16577 0 : return IPC_FAIL_NO_REASON(this);
16578 : }
16579 :
16580 0 : renameOp->DispatchToConnectionPool();
16581 :
16582 0 : return IPC_OK();
16583 : }
16584 :
16585 : PBackgroundIDBRequestParent*
16586 0 : VersionChangeTransaction::AllocPBackgroundIDBRequestParent(
16587 : const RequestParams& aParams)
16588 : {
16589 0 : AssertIsOnBackgroundThread();
16590 0 : MOZ_ASSERT(aParams.type() != RequestParams::T__None);
16591 :
16592 0 : return AllocRequest(aParams, IsSameProcessActor());
16593 : }
16594 :
16595 : mozilla::ipc::IPCResult
16596 0 : VersionChangeTransaction::RecvPBackgroundIDBRequestConstructor(
16597 : PBackgroundIDBRequestParent* aActor,
16598 : const RequestParams& aParams)
16599 : {
16600 0 : AssertIsOnBackgroundThread();
16601 0 : MOZ_ASSERT(aActor);
16602 0 : MOZ_ASSERT(aParams.type() != RequestParams::T__None);
16603 :
16604 0 : if (!StartRequest(aActor)) {
16605 0 : return IPC_FAIL_NO_REASON(this);
16606 : }
16607 0 : return IPC_OK();
16608 : }
16609 :
16610 : bool
16611 0 : VersionChangeTransaction::DeallocPBackgroundIDBRequestParent(
16612 : PBackgroundIDBRequestParent* aActor)
16613 : {
16614 0 : AssertIsOnBackgroundThread();
16615 0 : MOZ_ASSERT(aActor);
16616 :
16617 0 : return DeallocRequest(aActor);
16618 : }
16619 :
16620 : PBackgroundIDBCursorParent*
16621 0 : VersionChangeTransaction::AllocPBackgroundIDBCursorParent(
16622 : const OpenCursorParams& aParams)
16623 : {
16624 0 : AssertIsOnBackgroundThread();
16625 :
16626 0 : return AllocCursor(aParams, IsSameProcessActor());
16627 : }
16628 :
16629 : mozilla::ipc::IPCResult
16630 0 : VersionChangeTransaction::RecvPBackgroundIDBCursorConstructor(
16631 : PBackgroundIDBCursorParent* aActor,
16632 : const OpenCursorParams& aParams)
16633 : {
16634 0 : AssertIsOnBackgroundThread();
16635 0 : MOZ_ASSERT(aActor);
16636 0 : MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
16637 :
16638 0 : if (!StartCursor(aActor, aParams)) {
16639 0 : return IPC_FAIL_NO_REASON(this);
16640 : }
16641 0 : return IPC_OK();
16642 : }
16643 :
16644 : bool
16645 0 : VersionChangeTransaction::DeallocPBackgroundIDBCursorParent(
16646 : PBackgroundIDBCursorParent* aActor)
16647 : {
16648 0 : AssertIsOnBackgroundThread();
16649 0 : MOZ_ASSERT(aActor);
16650 :
16651 0 : return DeallocCursor(aActor);
16652 : }
16653 :
16654 : /*******************************************************************************
16655 : * Cursor
16656 : ******************************************************************************/
16657 :
16658 0 : Cursor::Cursor(TransactionBase* aTransaction,
16659 : Type aType,
16660 : FullObjectStoreMetadata* aObjectStoreMetadata,
16661 : FullIndexMetadata* aIndexMetadata,
16662 0 : Direction aDirection)
16663 : : mTransaction(aTransaction)
16664 : , mBackgroundParent(nullptr)
16665 : , mObjectStoreMetadata(aObjectStoreMetadata)
16666 : , mIndexMetadata(aIndexMetadata)
16667 0 : , mObjectStoreId(aObjectStoreMetadata->mCommonMetadata.id())
16668 0 : , mIndexId(aIndexMetadata ? aIndexMetadata->mCommonMetadata.id() : 0)
16669 : , mCurrentlyRunningOp(nullptr)
16670 : , mType(aType)
16671 : , mDirection(aDirection)
16672 0 : , mUniqueIndex(aIndexMetadata ?
16673 0 : aIndexMetadata->mCommonMetadata.unique() :
16674 : false)
16675 0 : , mIsSameProcessActor(!BackgroundParent::IsOtherProcessActor(
16676 : aTransaction->GetBackgroundParent()))
16677 0 : , mActorDestroyed(false)
16678 : {
16679 0 : AssertIsOnBackgroundThread();
16680 0 : MOZ_ASSERT(aTransaction);
16681 0 : MOZ_ASSERT(aType != OpenCursorParams::T__None);
16682 0 : MOZ_ASSERT(aObjectStoreMetadata);
16683 0 : MOZ_ASSERT_IF(aType == OpenCursorParams::TIndexOpenCursorParams ||
16684 : aType == OpenCursorParams::TIndexOpenKeyCursorParams,
16685 : aIndexMetadata);
16686 :
16687 0 : if (mType == OpenCursorParams::TObjectStoreOpenCursorParams ||
16688 0 : mType == OpenCursorParams::TIndexOpenCursorParams) {
16689 0 : mDatabase = aTransaction->GetDatabase();
16690 0 : MOZ_ASSERT(mDatabase);
16691 :
16692 0 : mFileManager = mDatabase->GetFileManager();
16693 0 : MOZ_ASSERT(mFileManager);
16694 :
16695 0 : mBackgroundParent = aTransaction->GetBackgroundParent();
16696 0 : MOZ_ASSERT(mBackgroundParent);
16697 : }
16698 :
16699 0 : if (aIndexMetadata) {
16700 0 : mLocale = aIndexMetadata->mCommonMetadata.locale();
16701 : }
16702 :
16703 : static_assert(OpenCursorParams::T__None == 0 &&
16704 : OpenCursorParams::T__Last == 4,
16705 : "Lots of code here assumes only four types of cursors!");
16706 0 : }
16707 :
16708 : bool
16709 0 : Cursor::VerifyRequestParams(const CursorRequestParams& aParams) const
16710 : {
16711 0 : AssertIsOnBackgroundThread();
16712 0 : MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None);
16713 0 : MOZ_ASSERT(mObjectStoreMetadata);
16714 0 : MOZ_ASSERT_IF(mType == OpenCursorParams::TIndexOpenCursorParams ||
16715 : mType == OpenCursorParams::TIndexOpenKeyCursorParams,
16716 : mIndexMetadata);
16717 :
16718 : #ifdef DEBUG
16719 : {
16720 : RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
16721 0 : mTransaction->GetMetadataForObjectStoreId(mObjectStoreId);
16722 0 : if (objectStoreMetadata) {
16723 0 : MOZ_ASSERT(objectStoreMetadata == mObjectStoreMetadata);
16724 : } else {
16725 0 : MOZ_ASSERT(mObjectStoreMetadata->mDeleted);
16726 : }
16727 :
16728 0 : if (objectStoreMetadata &&
16729 0 : (mType == OpenCursorParams::TIndexOpenCursorParams ||
16730 0 : mType == OpenCursorParams::TIndexOpenKeyCursorParams)) {
16731 : RefPtr<FullIndexMetadata> indexMetadata =
16732 0 : mTransaction->GetMetadataForIndexId(objectStoreMetadata, mIndexId);
16733 0 : if (indexMetadata) {
16734 0 : MOZ_ASSERT(indexMetadata == mIndexMetadata);
16735 : } else {
16736 0 : MOZ_ASSERT(mIndexMetadata->mDeleted);
16737 : }
16738 : }
16739 : }
16740 : #endif
16741 :
16742 0 : if (NS_WARN_IF(mObjectStoreMetadata->mDeleted) ||
16743 0 : (mIndexMetadata && NS_WARN_IF(mIndexMetadata->mDeleted))) {
16744 0 : ASSERT_UNLESS_FUZZING();
16745 : return false;
16746 : }
16747 :
16748 0 : const Key& sortKey = IsLocaleAware() ? mSortKey : mKey;
16749 :
16750 0 : switch (aParams.type()) {
16751 : case CursorRequestParams::TContinueParams: {
16752 0 : const Key& key = aParams.get_ContinueParams().key();
16753 0 : if (!key.IsUnset()) {
16754 0 : switch (mDirection) {
16755 : case IDBCursor::NEXT:
16756 : case IDBCursor::NEXT_UNIQUE:
16757 0 : if (NS_WARN_IF(key <= sortKey)) {
16758 0 : ASSERT_UNLESS_FUZZING();
16759 : return false;
16760 : }
16761 0 : break;
16762 :
16763 : case IDBCursor::PREV:
16764 : case IDBCursor::PREV_UNIQUE:
16765 0 : if (NS_WARN_IF(key >= sortKey)) {
16766 0 : ASSERT_UNLESS_FUZZING();
16767 : return false;
16768 : }
16769 0 : break;
16770 :
16771 : default:
16772 0 : MOZ_CRASH("Should never get here!");
16773 : }
16774 : }
16775 0 : break;
16776 : }
16777 :
16778 : case CursorRequestParams::TContinuePrimaryKeyParams: {
16779 0 : const Key& key = aParams.get_ContinuePrimaryKeyParams().key();
16780 0 : const Key& primaryKey = aParams.get_ContinuePrimaryKeyParams().primaryKey();
16781 0 : MOZ_ASSERT(!key.IsUnset());
16782 0 : MOZ_ASSERT(!primaryKey.IsUnset());
16783 0 : switch (mDirection) {
16784 : case IDBCursor::NEXT:
16785 0 : if (NS_WARN_IF(key < sortKey ||
16786 : (key == sortKey && primaryKey <= mObjectKey))) {
16787 0 : ASSERT_UNLESS_FUZZING();
16788 : return false;
16789 : }
16790 0 : break;
16791 :
16792 : case IDBCursor::PREV:
16793 0 : if (NS_WARN_IF(key > sortKey ||
16794 : (key == sortKey && primaryKey >= mObjectKey))) {
16795 0 : ASSERT_UNLESS_FUZZING();
16796 : return false;
16797 : }
16798 0 : break;
16799 :
16800 : default:
16801 0 : MOZ_CRASH("Should never get here!");
16802 : }
16803 0 : break;
16804 : }
16805 :
16806 : case CursorRequestParams::TAdvanceParams:
16807 0 : if (NS_WARN_IF(!aParams.get_AdvanceParams().count())) {
16808 0 : ASSERT_UNLESS_FUZZING();
16809 : return false;
16810 : }
16811 0 : break;
16812 :
16813 : default:
16814 0 : MOZ_CRASH("Should never get here!");
16815 : }
16816 :
16817 0 : return true;
16818 : }
16819 :
16820 : bool
16821 0 : Cursor::Start(const OpenCursorParams& aParams)
16822 : {
16823 0 : AssertIsOnBackgroundThread();
16824 0 : MOZ_ASSERT(aParams.type() == mType);
16825 0 : MOZ_ASSERT(!mActorDestroyed);
16826 :
16827 0 : if (NS_WARN_IF(mCurrentlyRunningOp)) {
16828 0 : ASSERT_UNLESS_FUZZING();
16829 : return false;
16830 : }
16831 :
16832 : const OptionalKeyRange& optionalKeyRange =
16833 0 : mType == OpenCursorParams::TObjectStoreOpenCursorParams ?
16834 0 : aParams.get_ObjectStoreOpenCursorParams().optionalKeyRange() :
16835 0 : mType == OpenCursorParams::TObjectStoreOpenKeyCursorParams ?
16836 0 : aParams.get_ObjectStoreOpenKeyCursorParams().optionalKeyRange() :
16837 0 : mType == OpenCursorParams::TIndexOpenCursorParams ?
16838 0 : aParams.get_IndexOpenCursorParams().optionalKeyRange() :
16839 0 : aParams.get_IndexOpenKeyCursorParams().optionalKeyRange();
16840 :
16841 0 : RefPtr<OpenOp> openOp = new OpenOp(this, optionalKeyRange);
16842 :
16843 0 : if (NS_WARN_IF(!openOp->Init(mTransaction))) {
16844 0 : openOp->Cleanup();
16845 0 : return false;
16846 : }
16847 :
16848 0 : openOp->DispatchToConnectionPool();
16849 0 : mCurrentlyRunningOp = openOp;
16850 :
16851 0 : return true;
16852 : }
16853 :
16854 : void
16855 0 : Cursor::SendResponseInternal(
16856 : CursorResponse& aResponse,
16857 : const nsTArray<FallibleTArray<StructuredCloneFile>>& aFiles)
16858 : {
16859 0 : AssertIsOnBackgroundThread();
16860 0 : MOZ_ASSERT(aResponse.type() != CursorResponse::T__None);
16861 0 : MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult,
16862 : NS_FAILED(aResponse.get_nsresult()));
16863 0 : MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult,
16864 : NS_ERROR_GET_MODULE(aResponse.get_nsresult()) ==
16865 : NS_ERROR_MODULE_DOM_INDEXEDDB);
16866 0 : MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tvoid_t, mKey.IsUnset());
16867 0 : MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tvoid_t,
16868 : mRangeKey.IsUnset());
16869 0 : MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tvoid_t,
16870 : mObjectKey.IsUnset());
16871 0 : MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult ||
16872 : aResponse.type() == CursorResponse::Tvoid_t ||
16873 : aResponse.type() ==
16874 : CursorResponse::TObjectStoreKeyCursorResponse ||
16875 : aResponse.type() == CursorResponse::TIndexKeyCursorResponse,
16876 : aFiles.IsEmpty());
16877 0 : MOZ_ASSERT(!mActorDestroyed);
16878 0 : MOZ_ASSERT(mCurrentlyRunningOp);
16879 :
16880 0 : for (size_t i = 0; i < aFiles.Length(); ++i) {
16881 0 : const auto& files = aFiles[i];
16882 0 : if (!files.IsEmpty()) {
16883 0 : MOZ_ASSERT(aResponse.type() ==
16884 : CursorResponse::TArrayOfObjectStoreCursorResponse ||
16885 : aResponse.type() == CursorResponse::TIndexCursorResponse);
16886 0 : MOZ_ASSERT(mDatabase);
16887 0 : MOZ_ASSERT(mBackgroundParent);
16888 :
16889 0 : FallibleTArray<SerializedStructuredCloneFile> serializedFiles;
16890 0 : nsresult rv = SerializeStructuredCloneFiles(mBackgroundParent,
16891 : mDatabase,
16892 : files,
16893 : /* aForPreprocess */ false,
16894 0 : serializedFiles);
16895 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
16896 0 : aResponse = ClampResultCode(rv);
16897 0 : break;
16898 : }
16899 :
16900 0 : SerializedStructuredCloneReadInfo* serializedInfo = nullptr;
16901 0 : switch (aResponse.type()) {
16902 : case CursorResponse::TArrayOfObjectStoreCursorResponse: {
16903 0 : auto& responses = aResponse.get_ArrayOfObjectStoreCursorResponse();
16904 0 : MOZ_ASSERT(i < responses.Length());
16905 0 : serializedInfo = &responses[i].cloneInfo();
16906 0 : break;
16907 : }
16908 :
16909 : case CursorResponse::TIndexCursorResponse:
16910 0 : MOZ_ASSERT(i == 0);
16911 0 : serializedInfo = &aResponse.get_IndexCursorResponse().cloneInfo();
16912 0 : break;
16913 :
16914 : default:
16915 0 : MOZ_CRASH("Should never get here!");
16916 : }
16917 :
16918 0 : MOZ_ASSERT(serializedInfo);
16919 0 : MOZ_ASSERT(serializedInfo->files().IsEmpty());
16920 :
16921 0 : serializedInfo->files().SwapElements(serializedFiles);
16922 : }
16923 : }
16924 :
16925 : // Work around the deleted function by casting to the base class.
16926 0 : auto* base = static_cast<PBackgroundIDBCursorParent*>(this);
16927 0 : if (!base->SendResponse(aResponse)) {
16928 0 : NS_WARNING("Failed to send response!");
16929 : }
16930 :
16931 0 : mCurrentlyRunningOp = nullptr;
16932 0 : }
16933 :
16934 : void
16935 0 : Cursor::ActorDestroy(ActorDestroyReason aWhy)
16936 : {
16937 0 : AssertIsOnBackgroundThread();
16938 0 : MOZ_ASSERT(!mActorDestroyed);
16939 :
16940 0 : mActorDestroyed = true;
16941 :
16942 0 : if (mCurrentlyRunningOp) {
16943 0 : mCurrentlyRunningOp->NoteActorDestroyed();
16944 : }
16945 :
16946 0 : mBackgroundParent = nullptr;
16947 :
16948 0 : mObjectStoreMetadata = nullptr;
16949 0 : mIndexMetadata = nullptr;
16950 0 : }
16951 :
16952 : mozilla::ipc::IPCResult
16953 0 : Cursor::RecvDeleteMe()
16954 : {
16955 0 : AssertIsOnBackgroundThread();
16956 0 : MOZ_ASSERT(!mActorDestroyed);
16957 :
16958 0 : if (NS_WARN_IF(mCurrentlyRunningOp)) {
16959 0 : ASSERT_UNLESS_FUZZING();
16960 : return IPC_FAIL_NO_REASON(this);
16961 : }
16962 :
16963 0 : IProtocol* mgr = Manager();
16964 0 : if (!PBackgroundIDBCursorParent::Send__delete__(this)) {
16965 0 : return IPC_FAIL_NO_REASON(mgr);
16966 : }
16967 0 : return IPC_OK();
16968 : }
16969 :
16970 : mozilla::ipc::IPCResult
16971 0 : Cursor::RecvContinue(const CursorRequestParams& aParams)
16972 : {
16973 0 : AssertIsOnBackgroundThread();
16974 0 : MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None);
16975 0 : MOZ_ASSERT(!mActorDestroyed);
16976 0 : MOZ_ASSERT(mObjectStoreMetadata);
16977 0 : MOZ_ASSERT_IF(mType == OpenCursorParams::TIndexOpenCursorParams ||
16978 : mType == OpenCursorParams::TIndexOpenKeyCursorParams,
16979 : mIndexMetadata);
16980 :
16981 : const bool trustParams =
16982 : #ifdef DEBUG
16983 : // Always verify parameters in DEBUG builds!
16984 0 : false
16985 : #else
16986 : mIsSameProcessActor
16987 : #endif
16988 : ;
16989 :
16990 0 : if (!trustParams && !VerifyRequestParams(aParams)) {
16991 0 : ASSERT_UNLESS_FUZZING();
16992 : return IPC_FAIL_NO_REASON(this);
16993 : }
16994 :
16995 0 : if (NS_WARN_IF(mCurrentlyRunningOp)) {
16996 0 : ASSERT_UNLESS_FUZZING();
16997 : return IPC_FAIL_NO_REASON(this);
16998 : }
16999 :
17000 0 : if (NS_WARN_IF(mTransaction->mCommitOrAbortReceived)) {
17001 0 : ASSERT_UNLESS_FUZZING();
17002 : return IPC_FAIL_NO_REASON(this);
17003 : }
17004 :
17005 0 : RefPtr<ContinueOp> continueOp = new ContinueOp(this, aParams);
17006 0 : if (NS_WARN_IF(!continueOp->Init(mTransaction))) {
17007 0 : continueOp->Cleanup();
17008 0 : return IPC_FAIL_NO_REASON(this);
17009 : }
17010 :
17011 0 : continueOp->DispatchToConnectionPool();
17012 0 : mCurrentlyRunningOp = continueOp;
17013 :
17014 0 : return IPC_OK();
17015 : }
17016 :
17017 : /*******************************************************************************
17018 : * FileManager
17019 : ******************************************************************************/
17020 :
17021 0 : FileManager::FileManager(PersistenceType aPersistenceType,
17022 : const nsACString& aGroup,
17023 : const nsACString& aOrigin,
17024 : const nsAString& aDatabaseName,
17025 0 : bool aEnforcingQuota)
17026 : : mPersistenceType(aPersistenceType)
17027 : , mGroup(aGroup)
17028 : , mOrigin(aOrigin)
17029 : , mDatabaseName(aDatabaseName)
17030 : , mLastFileId(0)
17031 : , mEnforcingQuota(aEnforcingQuota)
17032 0 : , mInvalidated(false)
17033 0 : { }
17034 :
17035 : nsresult
17036 0 : FileManager::Init(nsIFile* aDirectory,
17037 : mozIStorageConnection* aConnection)
17038 : {
17039 0 : AssertIsOnIOThread();
17040 0 : MOZ_ASSERT(aDirectory);
17041 0 : MOZ_ASSERT(aConnection);
17042 :
17043 : bool exists;
17044 0 : nsresult rv = aDirectory->Exists(&exists);
17045 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17046 0 : return rv;
17047 : }
17048 :
17049 0 : if (exists) {
17050 : bool isDirectory;
17051 0 : rv = aDirectory->IsDirectory(&isDirectory);
17052 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17053 0 : return rv;
17054 : }
17055 :
17056 0 : if (NS_WARN_IF(!isDirectory)) {
17057 0 : return NS_ERROR_FAILURE;
17058 : }
17059 : } else {
17060 0 : rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
17061 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17062 0 : return rv;
17063 : }
17064 : }
17065 :
17066 0 : rv = aDirectory->GetPath(mDirectoryPath);
17067 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17068 0 : return rv;
17069 : }
17070 :
17071 0 : nsCOMPtr<nsIFile> journalDirectory;
17072 0 : rv = aDirectory->Clone(getter_AddRefs(journalDirectory));
17073 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17074 0 : return rv;
17075 : }
17076 :
17077 0 : rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME));
17078 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17079 0 : return rv;
17080 : }
17081 :
17082 0 : rv = journalDirectory->Exists(&exists);
17083 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17084 0 : return rv;
17085 : }
17086 :
17087 0 : if (exists) {
17088 : bool isDirectory;
17089 0 : rv = journalDirectory->IsDirectory(&isDirectory);
17090 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17091 0 : return rv;
17092 : }
17093 :
17094 0 : if (NS_WARN_IF(!isDirectory)) {
17095 0 : return NS_ERROR_FAILURE;
17096 : }
17097 : }
17098 :
17099 0 : rv = journalDirectory->GetPath(mJournalDirectoryPath);
17100 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17101 0 : return rv;
17102 : }
17103 :
17104 0 : nsCOMPtr<mozIStorageStatement> stmt;
17105 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
17106 : "SELECT id, refcount "
17107 : "FROM file"
17108 0 : ), getter_AddRefs(stmt));
17109 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17110 0 : return rv;
17111 : }
17112 :
17113 : bool hasResult;
17114 0 : while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
17115 : int64_t id;
17116 0 : rv = stmt->GetInt64(0, &id);
17117 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17118 0 : return rv;
17119 : }
17120 :
17121 : int32_t refcount;
17122 0 : rv = stmt->GetInt32(1, &refcount);
17123 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17124 0 : return rv;
17125 : }
17126 :
17127 0 : MOZ_ASSERT(refcount > 0);
17128 :
17129 0 : RefPtr<FileInfo> fileInfo = FileInfo::Create(this, id);
17130 0 : fileInfo->mDBRefCnt = static_cast<nsrefcnt>(refcount);
17131 :
17132 0 : mFileInfos.Put(id, fileInfo);
17133 :
17134 0 : mLastFileId = std::max(id, mLastFileId);
17135 : }
17136 :
17137 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17138 0 : return rv;
17139 : }
17140 :
17141 0 : return NS_OK;
17142 : }
17143 :
17144 : nsresult
17145 0 : FileManager::Invalidate()
17146 : {
17147 0 : if (IndexedDatabaseManager::IsClosed()) {
17148 0 : MOZ_ASSERT(false, "Shouldn't be called after shutdown!");
17149 : return NS_ERROR_UNEXPECTED;
17150 : }
17151 :
17152 0 : MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
17153 :
17154 0 : MOZ_ASSERT(!mInvalidated);
17155 0 : mInvalidated = true;
17156 :
17157 0 : for (auto iter = mFileInfos.Iter(); !iter.Done(); iter.Next()) {
17158 0 : FileInfo* info = iter.Data();
17159 0 : MOZ_ASSERT(info);
17160 :
17161 0 : if (!info->LockedClearDBRefs()) {
17162 0 : iter.Remove();
17163 : }
17164 : }
17165 :
17166 0 : return NS_OK;
17167 : }
17168 :
17169 : already_AddRefed<nsIFile>
17170 0 : FileManager::GetDirectory()
17171 : {
17172 0 : return GetFileForPath(mDirectoryPath);
17173 : }
17174 :
17175 : already_AddRefed<nsIFile>
17176 0 : FileManager::GetCheckedDirectory()
17177 : {
17178 0 : nsCOMPtr<nsIFile> directory = GetDirectory();
17179 0 : if (NS_WARN_IF(!directory)) {
17180 0 : return nullptr;
17181 : }
17182 :
17183 0 : DebugOnly<bool> exists;
17184 0 : MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)));
17185 0 : MOZ_ASSERT(exists);
17186 :
17187 0 : DebugOnly<bool> isDirectory;
17188 0 : MOZ_ASSERT(NS_SUCCEEDED(directory->IsDirectory(&isDirectory)));
17189 0 : MOZ_ASSERT(isDirectory);
17190 :
17191 0 : return directory.forget();
17192 : }
17193 :
17194 : already_AddRefed<nsIFile>
17195 0 : FileManager::GetJournalDirectory()
17196 : {
17197 0 : return GetFileForPath(mJournalDirectoryPath);
17198 : }
17199 :
17200 : already_AddRefed<nsIFile>
17201 0 : FileManager::EnsureJournalDirectory()
17202 : {
17203 : // This can happen on the IO or on a transaction thread.
17204 0 : MOZ_ASSERT(!NS_IsMainThread());
17205 :
17206 0 : nsCOMPtr<nsIFile> journalDirectory = GetFileForPath(mJournalDirectoryPath);
17207 0 : if (NS_WARN_IF(!journalDirectory)) {
17208 0 : return nullptr;
17209 : }
17210 :
17211 : bool exists;
17212 0 : nsresult rv = journalDirectory->Exists(&exists);
17213 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17214 0 : return nullptr;
17215 : }
17216 :
17217 0 : if (exists) {
17218 : bool isDirectory;
17219 0 : rv = journalDirectory->IsDirectory(&isDirectory);
17220 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17221 0 : return nullptr;
17222 : }
17223 :
17224 0 : if (NS_WARN_IF(!isDirectory)) {
17225 0 : return nullptr;
17226 : }
17227 : } else {
17228 0 : rv = journalDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
17229 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17230 0 : return nullptr;
17231 : }
17232 : }
17233 :
17234 0 : return journalDirectory.forget();
17235 : }
17236 :
17237 : already_AddRefed<FileInfo>
17238 0 : FileManager::GetFileInfo(int64_t aId)
17239 : {
17240 0 : if (IndexedDatabaseManager::IsClosed()) {
17241 0 : MOZ_ASSERT(false, "Shouldn't be called after shutdown!");
17242 : return nullptr;
17243 : }
17244 :
17245 : FileInfo* fileInfo;
17246 : {
17247 0 : MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
17248 0 : fileInfo = mFileInfos.Get(aId);
17249 : }
17250 :
17251 0 : RefPtr<FileInfo> result = fileInfo;
17252 0 : return result.forget();
17253 : }
17254 :
17255 : already_AddRefed<FileInfo>
17256 0 : FileManager::GetNewFileInfo()
17257 : {
17258 0 : MOZ_ASSERT(!IndexedDatabaseManager::IsClosed());
17259 :
17260 : FileInfo* fileInfo;
17261 : {
17262 0 : MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
17263 :
17264 0 : int64_t id = mLastFileId + 1;
17265 :
17266 0 : fileInfo = FileInfo::Create(this, id);
17267 :
17268 0 : mFileInfos.Put(id, fileInfo);
17269 :
17270 0 : mLastFileId = id;
17271 : }
17272 :
17273 0 : RefPtr<FileInfo> result = fileInfo;
17274 0 : return result.forget();
17275 : }
17276 :
17277 : // static
17278 : already_AddRefed<nsIFile>
17279 0 : FileManager::GetFileForId(nsIFile* aDirectory, int64_t aId)
17280 : {
17281 0 : MOZ_ASSERT(aDirectory);
17282 0 : MOZ_ASSERT(aId > 0);
17283 :
17284 0 : nsAutoString id;
17285 0 : id.AppendInt(aId);
17286 :
17287 0 : nsCOMPtr<nsIFile> file;
17288 0 : nsresult rv = aDirectory->Clone(getter_AddRefs(file));
17289 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17290 0 : return nullptr;
17291 : }
17292 :
17293 0 : rv = file->Append(id);
17294 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17295 0 : return nullptr;
17296 : }
17297 :
17298 0 : return file.forget();
17299 : }
17300 :
17301 : // static
17302 : already_AddRefed<nsIFile>
17303 0 : FileManager::GetCheckedFileForId(nsIFile* aDirectory, int64_t aId)
17304 : {
17305 0 : nsCOMPtr<nsIFile> file = GetFileForId(aDirectory, aId);
17306 0 : if (NS_WARN_IF(!file)) {
17307 0 : return nullptr;
17308 : }
17309 :
17310 0 : DebugOnly<bool> exists;
17311 0 : MOZ_ASSERT(NS_SUCCEEDED(file->Exists(&exists)));
17312 0 : MOZ_ASSERT(exists);
17313 :
17314 0 : DebugOnly<bool> isFile;
17315 0 : MOZ_ASSERT(NS_SUCCEEDED(file->IsFile(&isFile)));
17316 0 : MOZ_ASSERT(isFile);
17317 :
17318 0 : return file.forget();
17319 : }
17320 :
17321 : // static
17322 : nsresult
17323 0 : FileManager::InitDirectory(nsIFile* aDirectory,
17324 : nsIFile* aDatabaseFile,
17325 : PersistenceType aPersistenceType,
17326 : const nsACString& aGroup,
17327 : const nsACString& aOrigin,
17328 : uint32_t aTelemetryId)
17329 : {
17330 0 : AssertIsOnIOThread();
17331 0 : MOZ_ASSERT(aDirectory);
17332 0 : MOZ_ASSERT(aDatabaseFile);
17333 :
17334 : bool exists;
17335 0 : nsresult rv = aDirectory->Exists(&exists);
17336 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17337 0 : return rv;
17338 : }
17339 :
17340 0 : if (!exists) {
17341 0 : return NS_OK;
17342 : }
17343 :
17344 : bool isDirectory;
17345 0 : rv = aDirectory->IsDirectory(&isDirectory);
17346 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17347 0 : return rv;
17348 : }
17349 :
17350 0 : if (NS_WARN_IF(!isDirectory)) {
17351 0 : return NS_ERROR_FAILURE;
17352 : }
17353 :
17354 0 : nsCOMPtr<nsIFile> journalDirectory;
17355 0 : rv = aDirectory->Clone(getter_AddRefs(journalDirectory));
17356 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17357 0 : return rv;
17358 : }
17359 :
17360 0 : rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME));
17361 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17362 0 : return rv;
17363 : }
17364 :
17365 0 : rv = journalDirectory->Exists(&exists);
17366 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17367 0 : return rv;
17368 : }
17369 :
17370 0 : if (exists) {
17371 0 : rv = journalDirectory->IsDirectory(&isDirectory);
17372 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17373 0 : return rv;
17374 : }
17375 :
17376 0 : if (NS_WARN_IF(!isDirectory)) {
17377 0 : return NS_ERROR_FAILURE;
17378 : }
17379 :
17380 0 : nsCOMPtr<nsISimpleEnumerator> entries;
17381 0 : rv = journalDirectory->GetDirectoryEntries(getter_AddRefs(entries));
17382 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17383 0 : return rv;
17384 : }
17385 :
17386 : bool hasElements;
17387 0 : rv = entries->HasMoreElements(&hasElements);
17388 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17389 0 : return rv;
17390 : }
17391 :
17392 0 : if (hasElements) {
17393 0 : nsCOMPtr<mozIStorageConnection> connection;
17394 0 : rv = CreateStorageConnection(aDatabaseFile,
17395 : aDirectory,
17396 0 : NullString(),
17397 : aPersistenceType,
17398 : aGroup,
17399 : aOrigin,
17400 : aTelemetryId,
17401 0 : getter_AddRefs(connection));
17402 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17403 0 : return rv;
17404 : }
17405 :
17406 0 : mozStorageTransaction transaction(connection, false);
17407 :
17408 0 : rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
17409 : "CREATE VIRTUAL TABLE fs USING filesystem;"
17410 0 : ));
17411 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17412 0 : return rv;
17413 : }
17414 :
17415 0 : nsCOMPtr<mozIStorageStatement> stmt;
17416 0 : rv = connection->CreateStatement(NS_LITERAL_CSTRING(
17417 : "SELECT name, (name IN (SELECT id FROM file)) "
17418 : "FROM fs "
17419 : "WHERE path = :path"
17420 0 : ), getter_AddRefs(stmt));
17421 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17422 0 : return rv;
17423 : }
17424 :
17425 0 : nsString path;
17426 0 : rv = journalDirectory->GetPath(path);
17427 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17428 0 : return rv;
17429 : }
17430 :
17431 0 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("path"), path);
17432 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17433 0 : return rv;
17434 : }
17435 :
17436 : bool hasResult;
17437 0 : while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
17438 0 : nsString name;
17439 0 : rv = stmt->GetString(0, name);
17440 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17441 0 : return rv;
17442 : }
17443 :
17444 0 : int32_t flag = stmt->AsInt32(1);
17445 :
17446 0 : if (!flag) {
17447 0 : nsCOMPtr<nsIFile> file;
17448 0 : rv = aDirectory->Clone(getter_AddRefs(file));
17449 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17450 0 : return rv;
17451 : }
17452 :
17453 0 : rv = file->Append(name);
17454 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17455 0 : return rv;
17456 : }
17457 :
17458 0 : if (NS_FAILED(file->Remove(false))) {
17459 0 : NS_WARNING("Failed to remove orphaned file!");
17460 : }
17461 : }
17462 :
17463 0 : nsCOMPtr<nsIFile> journalFile;
17464 0 : rv = journalDirectory->Clone(getter_AddRefs(journalFile));
17465 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17466 0 : return rv;
17467 : }
17468 :
17469 0 : rv = journalFile->Append(name);
17470 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17471 0 : return rv;
17472 : }
17473 :
17474 0 : if (NS_FAILED(journalFile->Remove(false))) {
17475 0 : NS_WARNING("Failed to remove journal file!");
17476 : }
17477 : }
17478 :
17479 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17480 0 : return rv;
17481 : }
17482 :
17483 0 : rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
17484 : "DROP TABLE fs;"
17485 0 : ));
17486 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17487 0 : return rv;
17488 : }
17489 :
17490 0 : rv = transaction.Commit();
17491 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17492 0 : return rv;
17493 : }
17494 : }
17495 : }
17496 :
17497 0 : return NS_OK;
17498 : }
17499 :
17500 : // static
17501 : nsresult
17502 0 : FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage)
17503 : {
17504 0 : AssertIsOnIOThread();
17505 0 : MOZ_ASSERT(aDirectory);
17506 0 : MOZ_ASSERT(aUsage);
17507 :
17508 : bool exists;
17509 0 : nsresult rv = aDirectory->Exists(&exists);
17510 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17511 0 : return rv;
17512 : }
17513 :
17514 0 : if (!exists) {
17515 0 : *aUsage = 0;
17516 0 : return NS_OK;
17517 : }
17518 :
17519 0 : nsCOMPtr<nsISimpleEnumerator> entries;
17520 0 : rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
17521 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17522 0 : return rv;
17523 : }
17524 :
17525 0 : uint64_t usage = 0;
17526 :
17527 : bool hasMore;
17528 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
17529 0 : nsCOMPtr<nsISupports> entry;
17530 0 : rv = entries->GetNext(getter_AddRefs(entry));
17531 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17532 0 : return rv;
17533 : }
17534 :
17535 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
17536 0 : MOZ_ASSERT(file);
17537 :
17538 0 : nsString leafName;
17539 0 : rv = file->GetLeafName(leafName);
17540 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17541 0 : return rv;
17542 : }
17543 :
17544 0 : if (leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) {
17545 0 : continue;
17546 : }
17547 :
17548 : int64_t fileSize;
17549 0 : rv = file->GetFileSize(&fileSize);
17550 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17551 0 : return rv;
17552 : }
17553 :
17554 0 : UsageInfo::IncrementUsage(&usage, uint64_t(fileSize));
17555 : }
17556 :
17557 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17558 0 : return rv;
17559 : }
17560 :
17561 0 : *aUsage = usage;
17562 0 : return NS_OK;
17563 : }
17564 :
17565 : /*******************************************************************************
17566 : * QuotaClient
17567 : ******************************************************************************/
17568 :
17569 : QuotaClient* QuotaClient::sInstance = nullptr;
17570 :
17571 0 : QuotaClient::QuotaClient()
17572 0 : : mShutdownRequested(false)
17573 : {
17574 0 : AssertIsOnBackgroundThread();
17575 0 : MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
17576 0 : MOZ_ASSERT(!gTelemetryIdMutex);
17577 :
17578 : // Always create this so that later access to gTelemetryIdHashtable can be
17579 : // properly synchronized.
17580 0 : gTelemetryIdMutex = new Mutex("IndexedDB gTelemetryIdMutex");
17581 :
17582 0 : sInstance = this;
17583 0 : }
17584 :
17585 0 : QuotaClient::~QuotaClient()
17586 : {
17587 0 : AssertIsOnBackgroundThread();
17588 0 : MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
17589 0 : MOZ_ASSERT(gTelemetryIdMutex);
17590 0 : MOZ_ASSERT(!mMaintenanceThreadPool);
17591 :
17592 : // No one else should be able to touch gTelemetryIdHashtable now that the
17593 : // QuotaClient has gone away.
17594 0 : gTelemetryIdHashtable = nullptr;
17595 0 : gTelemetryIdMutex = nullptr;
17596 :
17597 0 : sInstance = nullptr;
17598 0 : }
17599 :
17600 : nsThreadPool*
17601 0 : QuotaClient::GetOrCreateThreadPool()
17602 : {
17603 0 : AssertIsOnBackgroundThread();
17604 0 : MOZ_ASSERT(!mShutdownRequested);
17605 :
17606 0 : if (!mMaintenanceThreadPool) {
17607 0 : RefPtr<nsThreadPool> threadPool = new nsThreadPool();
17608 :
17609 : // PR_GetNumberOfProcessors() can return -1 on error, so make sure we
17610 : // don't set some huge number here. We add 2 in case some threads block on
17611 : // the disk I/O.
17612 : const uint32_t threadCount =
17613 0 : std::max(int32_t(PR_GetNumberOfProcessors()), int32_t(1)) +
17614 0 : 2;
17615 :
17616 0 : MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(threadCount));
17617 :
17618 : // Don't keep more than one idle thread.
17619 0 : MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit(1));
17620 :
17621 : // Don't keep idle threads alive very long.
17622 0 : MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadTimeout(5 * PR_MSEC_PER_SEC));
17623 :
17624 0 : MOZ_ALWAYS_SUCCEEDS(threadPool->SetName(NS_LITERAL_CSTRING("IndexedDB Mnt")));
17625 :
17626 0 : mMaintenanceThreadPool = Move(threadPool);
17627 : }
17628 :
17629 0 : return mMaintenanceThreadPool;
17630 : }
17631 :
17632 : mozilla::dom::quota::Client::Type
17633 0 : QuotaClient::GetType()
17634 : {
17635 0 : return QuotaClient::IDB;
17636 : }
17637 :
17638 : nsresult
17639 0 : QuotaClient::UpgradeStorageFrom1_0To2_0(nsIFile* aDirectory)
17640 : {
17641 0 : AssertIsOnIOThread();
17642 0 : MOZ_ASSERT(aDirectory);
17643 :
17644 0 : AtomicBool dummy(false);
17645 0 : AutoTArray<nsString, 20> subdirsToProcess;
17646 0 : nsTHashtable<nsStringHashKey> databaseFilenames(20);
17647 : nsresult rv = GetDatabaseFilenames(aDirectory,
17648 : /* aCanceled */ dummy,
17649 : /* aForUpgrade */ true,
17650 : subdirsToProcess,
17651 0 : databaseFilenames);
17652 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17653 0 : return rv;
17654 : }
17655 :
17656 : const NS_ConvertASCIItoUTF16 filesSuffix(
17657 : kFileManagerDirectoryNameSuffix,
17658 0 : LiteralStringLength(kFileManagerDirectoryNameSuffix));
17659 :
17660 0 : for (uint32_t count = subdirsToProcess.Length(), i = 0; i < count; i++) {
17661 0 : const nsString& subdirName = subdirsToProcess[i];
17662 :
17663 : // If the directory has the correct suffix then it should exist in
17664 : // databaseFilenames.
17665 0 : nsDependentSubstring subdirNameBase;
17666 0 : if (GetBaseFilename(subdirName, filesSuffix, subdirNameBase)) {
17667 0 : Unused << NS_WARN_IF(!databaseFilenames.GetEntry(subdirNameBase));
17668 :
17669 0 : continue;
17670 : }
17671 :
17672 : // The directory didn't have the right suffix but we might need to rename
17673 : // it. Check to see if we have a database that references this directory.
17674 0 : nsString subdirNameWithSuffix;
17675 0 : if (databaseFilenames.GetEntry(subdirName)) {
17676 0 : subdirNameWithSuffix = subdirName + filesSuffix;
17677 : } else {
17678 : // Windows doesn't allow a directory to end with a dot ('.'), so we have
17679 : // to check that possibility here too.
17680 : // We do this on all platforms, because the origin directory may have
17681 : // been created on Windows and now accessed on different OS.
17682 0 : nsString subdirNameWithDot = subdirName + NS_LITERAL_STRING(".");
17683 0 : if (NS_WARN_IF(!databaseFilenames.GetEntry(subdirNameWithDot))) {
17684 0 : continue;
17685 : }
17686 0 : subdirNameWithSuffix = subdirNameWithDot + filesSuffix;
17687 : }
17688 :
17689 : // We do have a database that uses this directory so we should rename it
17690 : // now. However, first check to make sure that we're not overwriting
17691 : // something else.
17692 0 : nsCOMPtr<nsIFile> subdir;
17693 0 : rv = aDirectory->Clone(getter_AddRefs(subdir));
17694 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17695 0 : return rv;
17696 : }
17697 :
17698 0 : rv = subdir->Append(subdirNameWithSuffix);
17699 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17700 0 : return rv;
17701 : }
17702 :
17703 : bool exists;
17704 0 : rv = subdir->Exists(&exists);
17705 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17706 0 : return rv;
17707 : }
17708 :
17709 0 : if (exists) {
17710 0 : IDB_REPORT_INTERNAL_ERR();
17711 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
17712 : }
17713 :
17714 0 : rv = aDirectory->Clone(getter_AddRefs(subdir));
17715 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17716 0 : return rv;
17717 : }
17718 :
17719 0 : rv = subdir->Append(subdirName);
17720 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17721 0 : return rv;
17722 : }
17723 :
17724 0 : DebugOnly<bool> isDirectory;
17725 0 : MOZ_ASSERT(NS_SUCCEEDED(subdir->IsDirectory(&isDirectory)));
17726 0 : MOZ_ASSERT(isDirectory);
17727 :
17728 0 : rv = subdir->RenameTo(nullptr, subdirNameWithSuffix);
17729 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17730 0 : return rv;
17731 : }
17732 : }
17733 :
17734 0 : return NS_OK;
17735 : }
17736 :
17737 : nsresult
17738 0 : QuotaClient::InitOrigin(PersistenceType aPersistenceType,
17739 : const nsACString& aGroup,
17740 : const nsACString& aOrigin,
17741 : const AtomicBool& aCanceled,
17742 : UsageInfo* aUsageInfo)
17743 : {
17744 0 : AssertIsOnIOThread();
17745 :
17746 0 : nsCOMPtr<nsIFile> directory;
17747 : nsresult rv =
17748 0 : GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory));
17749 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17750 0 : return rv;
17751 : }
17752 :
17753 : // We need to see if there are any files in the directory already. If they
17754 : // are database files then we need to cleanup stored files (if it's needed)
17755 : // and also get the usage.
17756 :
17757 0 : AutoTArray<nsString, 20> subdirsToProcess;
17758 0 : nsTHashtable<nsStringHashKey> databaseFilenames(20);
17759 0 : rv = GetDatabaseFilenames(directory,
17760 : aCanceled,
17761 : /* aForUpgrade */ false,
17762 : subdirsToProcess,
17763 0 : databaseFilenames);
17764 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17765 0 : return rv;
17766 : }
17767 :
17768 : const NS_ConvertASCIItoUTF16 filesSuffix(
17769 : kFileManagerDirectoryNameSuffix,
17770 0 : LiteralStringLength(kFileManagerDirectoryNameSuffix));
17771 :
17772 0 : for (uint32_t count = subdirsToProcess.Length(), i = 0; i < count; i++) {
17773 0 : const nsString& subdirName = subdirsToProcess[i];
17774 :
17775 : // The directory must have the correct suffix.
17776 0 : nsDependentSubstring subdirNameBase;
17777 0 : if (NS_WARN_IF(!GetBaseFilename(subdirName, filesSuffix, subdirNameBase))) {
17778 0 : return NS_ERROR_UNEXPECTED;
17779 : }
17780 :
17781 : // The directory base must exist in databaseFilenames.
17782 0 : if (NS_WARN_IF(!databaseFilenames.GetEntry(subdirNameBase))) {
17783 0 : return NS_ERROR_UNEXPECTED;
17784 : }
17785 : }
17786 :
17787 : const NS_ConvertASCIItoUTF16 sqliteSuffix(kSQLiteSuffix,
17788 0 : LiteralStringLength(kSQLiteSuffix));
17789 : const NS_ConvertASCIItoUTF16 walSuffix(kSQLiteWALSuffix,
17790 0 : LiteralStringLength(kSQLiteWALSuffix));
17791 :
17792 0 : for (auto iter = databaseFilenames.ConstIter();
17793 0 : !iter.Done() && !aCanceled;
17794 0 : iter.Next()) {
17795 0 : auto& databaseFilename = iter.Get()->GetKey();
17796 :
17797 0 : nsCOMPtr<nsIFile> fmDirectory;
17798 0 : rv = directory->Clone(getter_AddRefs(fmDirectory));
17799 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17800 0 : return rv;
17801 : }
17802 :
17803 0 : rv = fmDirectory->Append(databaseFilename + filesSuffix);
17804 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17805 0 : return rv;
17806 : }
17807 :
17808 0 : nsCOMPtr<nsIFile> databaseFile;
17809 0 : rv = directory->Clone(getter_AddRefs(databaseFile));
17810 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17811 0 : return rv;
17812 : }
17813 :
17814 0 : rv = databaseFile->Append(databaseFilename + sqliteSuffix);
17815 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17816 0 : return rv;
17817 : }
17818 :
17819 0 : nsCOMPtr<nsIFile> walFile;
17820 0 : if (aUsageInfo) {
17821 0 : rv = directory->Clone(getter_AddRefs(walFile));
17822 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17823 0 : return rv;
17824 : }
17825 :
17826 0 : rv = walFile->Append(databaseFilename + walSuffix);
17827 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17828 0 : return rv;
17829 : }
17830 : }
17831 :
17832 0 : rv = FileManager::InitDirectory(fmDirectory,
17833 : databaseFile,
17834 : aPersistenceType,
17835 : aGroup,
17836 : aOrigin,
17837 0 : TelemetryIdForFile(databaseFile));
17838 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17839 0 : return rv;
17840 : }
17841 :
17842 0 : if (aUsageInfo) {
17843 : int64_t fileSize;
17844 0 : rv = databaseFile->GetFileSize(&fileSize);
17845 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17846 0 : return rv;
17847 : }
17848 :
17849 0 : MOZ_ASSERT(fileSize >= 0);
17850 :
17851 0 : aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
17852 :
17853 0 : rv = walFile->GetFileSize(&fileSize);
17854 0 : if (NS_SUCCEEDED(rv)) {
17855 0 : MOZ_ASSERT(fileSize >= 0);
17856 0 : aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
17857 0 : } else if (NS_WARN_IF(rv != NS_ERROR_FILE_NOT_FOUND &&
17858 : rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)) {
17859 0 : return rv;
17860 : }
17861 :
17862 : uint64_t usage;
17863 0 : rv = FileManager::GetUsage(fmDirectory, &usage);
17864 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17865 0 : return rv;
17866 : }
17867 :
17868 0 : aUsageInfo->AppendToFileUsage(usage);
17869 : }
17870 : }
17871 :
17872 0 : return NS_OK;
17873 : }
17874 :
17875 : nsresult
17876 0 : QuotaClient::GetUsageForOrigin(PersistenceType aPersistenceType,
17877 : const nsACString& aGroup,
17878 : const nsACString& aOrigin,
17879 : const AtomicBool& aCanceled,
17880 : UsageInfo* aUsageInfo)
17881 : {
17882 0 : AssertIsOnIOThread();
17883 0 : MOZ_ASSERT(aUsageInfo);
17884 :
17885 0 : nsCOMPtr<nsIFile> directory;
17886 : nsresult rv =
17887 0 : GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory));
17888 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17889 0 : return rv;
17890 : }
17891 :
17892 0 : rv = GetUsageForDirectoryInternal(directory, aCanceled, aUsageInfo, true);
17893 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
17894 0 : return rv;
17895 : }
17896 :
17897 0 : return NS_OK;
17898 : }
17899 :
17900 : void
17901 0 : QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
17902 : const nsACString& aOrigin)
17903 : {
17904 0 : AssertIsOnIOThread();
17905 :
17906 0 : if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
17907 0 : mgr->InvalidateFileManagers(aPersistenceType, aOrigin);
17908 : }
17909 0 : }
17910 :
17911 : void
17912 0 : QuotaClient::ReleaseIOThreadObjects()
17913 : {
17914 0 : AssertIsOnIOThread();
17915 :
17916 0 : if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
17917 0 : mgr->InvalidateAllFileManagers();
17918 : }
17919 0 : }
17920 :
17921 : void
17922 0 : QuotaClient::AbortOperations(const nsACString& aOrigin)
17923 : {
17924 0 : AssertIsOnBackgroundThread();
17925 :
17926 0 : if (!gLiveDatabaseHashtable) {
17927 0 : return;
17928 : }
17929 :
17930 0 : nsTArray<RefPtr<Database>> databases;
17931 :
17932 0 : for (auto iter = gLiveDatabaseHashtable->ConstIter();
17933 0 : !iter.Done(); iter.Next()) {
17934 0 : for (Database* database : iter.Data()->mLiveDatabases) {
17935 0 : if (aOrigin.IsVoid() || database->Origin() == aOrigin) {
17936 0 : databases.AppendElement(database);
17937 : }
17938 : }
17939 : }
17940 :
17941 0 : for (Database* database : databases) {
17942 0 : database->Invalidate();
17943 : }
17944 :
17945 0 : databases.Clear();
17946 : }
17947 :
17948 : void
17949 0 : QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId)
17950 : {
17951 0 : AssertIsOnBackgroundThread();
17952 :
17953 0 : if (!gLiveDatabaseHashtable) {
17954 0 : return;
17955 : }
17956 :
17957 0 : nsTArray<RefPtr<Database>> databases;
17958 :
17959 0 : for (auto iter = gLiveDatabaseHashtable->ConstIter();
17960 0 : !iter.Done(); iter.Next()) {
17961 0 : for (Database* database : iter.Data()->mLiveDatabases) {
17962 0 : if (database->IsOwnedByProcess(aContentParentId)) {
17963 0 : databases.AppendElement(database);
17964 : }
17965 : }
17966 : }
17967 :
17968 0 : for (Database* database : databases) {
17969 0 : database->Invalidate();
17970 : }
17971 :
17972 0 : databases.Clear();
17973 : }
17974 :
17975 : void
17976 0 : QuotaClient::StartIdleMaintenance()
17977 : {
17978 0 : AssertIsOnBackgroundThread();
17979 0 : MOZ_ASSERT(!mShutdownRequested);
17980 :
17981 0 : mBackgroundThread = GetCurrentThreadEventTarget();
17982 :
17983 0 : RefPtr<Maintenance> maintenance = new Maintenance(this);
17984 :
17985 0 : mMaintenanceQueue.AppendElement(maintenance.forget());
17986 0 : ProcessMaintenanceQueue();
17987 0 : }
17988 :
17989 : void
17990 0 : QuotaClient::StopIdleMaintenance()
17991 : {
17992 0 : AssertIsOnBackgroundThread();
17993 0 : MOZ_ASSERT(!mShutdownRequested);
17994 :
17995 0 : if (mCurrentMaintenance) {
17996 0 : mCurrentMaintenance->Abort();
17997 : }
17998 :
17999 0 : for (RefPtr<Maintenance>& maintenance : mMaintenanceQueue) {
18000 0 : maintenance->Abort();
18001 : }
18002 0 : }
18003 :
18004 : void
18005 0 : QuotaClient::ShutdownWorkThreads()
18006 : {
18007 0 : AssertIsOnBackgroundThread();
18008 0 : MOZ_ASSERT(!mShutdownRequested);
18009 :
18010 0 : mShutdownRequested = true;
18011 :
18012 0 : if (mMaintenanceThreadPool) {
18013 0 : mMaintenanceThreadPool->Shutdown();
18014 0 : mMaintenanceThreadPool = nullptr;
18015 : }
18016 :
18017 0 : RefPtr<ConnectionPool> connectionPool = gConnectionPool.get();
18018 0 : if (connectionPool) {
18019 0 : connectionPool->Shutdown();
18020 :
18021 0 : gConnectionPool = nullptr;
18022 : }
18023 :
18024 : RefPtr<FileHandleThreadPool> fileHandleThreadPool =
18025 0 : gFileHandleThreadPool.get();
18026 0 : if (fileHandleThreadPool) {
18027 0 : fileHandleThreadPool->Shutdown();
18028 :
18029 0 : gFileHandleThreadPool = nullptr;
18030 : }
18031 0 : }
18032 :
18033 : void
18034 0 : QuotaClient::DidInitialize(QuotaManager* aQuotaManager)
18035 : {
18036 0 : MOZ_ASSERT(NS_IsMainThread());
18037 :
18038 0 : if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
18039 0 : mgr->NoteLiveQuotaManager(aQuotaManager);
18040 : }
18041 0 : }
18042 :
18043 : void
18044 0 : QuotaClient::WillShutdown()
18045 : {
18046 0 : MOZ_ASSERT(NS_IsMainThread());
18047 :
18048 0 : if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
18049 0 : mgr->NoteShuttingDownQuotaManager();
18050 : }
18051 0 : }
18052 :
18053 : nsresult
18054 0 : QuotaClient::GetDirectory(PersistenceType aPersistenceType,
18055 : const nsACString& aOrigin, nsIFile** aDirectory)
18056 : {
18057 0 : QuotaManager* quotaManager = QuotaManager::Get();
18058 0 : NS_ASSERTION(quotaManager, "This should never fail!");
18059 :
18060 0 : nsCOMPtr<nsIFile> directory;
18061 0 : nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin,
18062 0 : getter_AddRefs(directory));
18063 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18064 0 : return rv;
18065 : }
18066 :
18067 0 : MOZ_ASSERT(directory);
18068 :
18069 0 : rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
18070 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18071 0 : return rv;
18072 : }
18073 :
18074 0 : directory.forget(aDirectory);
18075 0 : return NS_OK;
18076 : }
18077 :
18078 : nsresult
18079 0 : QuotaClient::GetDatabaseFilenames(
18080 : nsIFile* aDirectory,
18081 : const AtomicBool& aCanceled,
18082 : bool aForUpgrade,
18083 : nsTArray<nsString>& aSubdirsToProcess,
18084 : nsTHashtable<nsStringHashKey>& aDatabaseFilenames)
18085 : {
18086 0 : AssertIsOnIOThread();
18087 0 : MOZ_ASSERT(aDirectory);
18088 :
18089 0 : nsCOMPtr<nsISimpleEnumerator> entries;
18090 0 : nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
18091 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18092 0 : return rv;
18093 : }
18094 :
18095 : const NS_ConvertASCIItoUTF16 sqliteSuffix(kSQLiteSuffix,
18096 0 : LiteralStringLength(kSQLiteSuffix));
18097 : const NS_ConvertASCIItoUTF16 journalSuffix(
18098 : kSQLiteJournalSuffix,
18099 0 : LiteralStringLength(kSQLiteJournalSuffix));
18100 : const NS_ConvertASCIItoUTF16 shmSuffix(kSQLiteSHMSuffix,
18101 0 : LiteralStringLength(kSQLiteSHMSuffix));
18102 : const NS_ConvertASCIItoUTF16 walSuffix(kSQLiteWALSuffix,
18103 0 : LiteralStringLength(kSQLiteWALSuffix));
18104 :
18105 : bool hasMore;
18106 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
18107 0 : hasMore &&
18108 0 : !aCanceled) {
18109 0 : nsCOMPtr<nsISupports> entry;
18110 0 : rv = entries->GetNext(getter_AddRefs(entry));
18111 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18112 0 : return rv;
18113 : }
18114 :
18115 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
18116 0 : MOZ_ASSERT(file);
18117 :
18118 0 : nsString leafName;
18119 0 : rv = file->GetLeafName(leafName);
18120 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18121 0 : return rv;
18122 : }
18123 :
18124 : bool isDirectory;
18125 0 : rv = file->IsDirectory(&isDirectory);
18126 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18127 0 : return rv;
18128 : }
18129 :
18130 0 : if (isDirectory) {
18131 0 : aSubdirsToProcess.AppendElement(leafName);
18132 0 : continue;
18133 : }
18134 :
18135 : // Skip Desktop Service Store (.DS_Store) files. These files are only used
18136 : // on Mac OS X, but the profile can be shared across different operating
18137 : // systems, so we check it on all platforms.
18138 0 : if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
18139 0 : continue;
18140 : }
18141 :
18142 : // Skip SQLite temporary files. These files take up space on disk but will
18143 : // be deleted as soon as the database is opened, so we don't count them
18144 : // towards quota.
18145 0 : if (StringEndsWith(leafName, journalSuffix) ||
18146 0 : StringEndsWith(leafName, shmSuffix)) {
18147 0 : continue;
18148 : }
18149 :
18150 : // The SQLite WAL file does count towards quota, but it is handled below
18151 : // once we find the actual database file.
18152 0 : if (StringEndsWith(leafName, walSuffix)) {
18153 0 : continue;
18154 : }
18155 :
18156 0 : nsDependentSubstring leafNameBase;
18157 0 : if (!GetBaseFilename(leafName, sqliteSuffix, leafNameBase)) {
18158 0 : nsString path;
18159 0 : MOZ_ALWAYS_SUCCEEDS(file->GetPath(path));
18160 0 : MOZ_ASSERT(!path.IsEmpty());
18161 :
18162 : nsPrintfCString warning(R"(An unexpected file exists in the storage )"
18163 : R"(area: "%s")",
18164 0 : NS_ConvertUTF16toUTF8(path).get());
18165 0 : NS_WARNING(warning.get());
18166 0 : if (!aForUpgrade) {
18167 0 : return NS_ERROR_UNEXPECTED;
18168 : }
18169 0 : continue;
18170 : }
18171 :
18172 0 : aDatabaseFilenames.PutEntry(leafNameBase);
18173 : }
18174 :
18175 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18176 0 : return rv;
18177 : }
18178 :
18179 0 : return NS_OK;
18180 : }
18181 :
18182 : nsresult
18183 0 : QuotaClient::GetUsageForDirectoryInternal(nsIFile* aDirectory,
18184 : const AtomicBool& aCanceled,
18185 : UsageInfo* aUsageInfo,
18186 : bool aDatabaseFiles)
18187 : {
18188 0 : AssertIsOnIOThread();
18189 0 : MOZ_ASSERT(aDirectory);
18190 0 : MOZ_ASSERT(aUsageInfo);
18191 :
18192 0 : nsCOMPtr<nsISimpleEnumerator> entries;
18193 0 : nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
18194 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18195 0 : return rv;
18196 : }
18197 :
18198 0 : if (!entries) {
18199 0 : return NS_OK;
18200 : }
18201 :
18202 : const NS_ConvertASCIItoUTF16 journalSuffix(
18203 : kSQLiteJournalSuffix,
18204 0 : LiteralStringLength(kSQLiteJournalSuffix));
18205 : const NS_ConvertASCIItoUTF16 shmSuffix(kSQLiteSHMSuffix,
18206 0 : LiteralStringLength(kSQLiteSHMSuffix));
18207 :
18208 : bool hasMore;
18209 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
18210 0 : hasMore &&
18211 0 : !aCanceled) {
18212 0 : nsCOMPtr<nsISupports> entry;
18213 0 : rv = entries->GetNext(getter_AddRefs(entry));
18214 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18215 0 : return rv;
18216 : }
18217 :
18218 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
18219 0 : MOZ_ASSERT(file);
18220 :
18221 0 : nsString leafName;
18222 0 : rv = file->GetLeafName(leafName);
18223 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18224 0 : return rv;
18225 : }
18226 :
18227 : // Journal files and sqlite-shm files don't count towards usage.
18228 0 : if (StringEndsWith(leafName, journalSuffix) ||
18229 0 : StringEndsWith(leafName, shmSuffix)) {
18230 0 : continue;
18231 : }
18232 :
18233 : bool isDirectory;
18234 0 : rv = file->IsDirectory(&isDirectory);
18235 0 : if (rv == NS_ERROR_FILE_NOT_FOUND ||
18236 : rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
18237 0 : continue;
18238 : }
18239 :
18240 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18241 0 : return rv;
18242 : }
18243 :
18244 0 : if (isDirectory) {
18245 0 : if (aDatabaseFiles) {
18246 0 : rv = GetUsageForDirectoryInternal(file, aCanceled, aUsageInfo, false);
18247 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18248 0 : return rv;
18249 : }
18250 : } else {
18251 0 : nsString leafName;
18252 0 : rv = file->GetLeafName(leafName);
18253 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18254 0 : return rv;
18255 : }
18256 :
18257 0 : if (!leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) {
18258 0 : NS_WARNING("Unknown directory found!");
18259 : }
18260 : }
18261 :
18262 0 : continue;
18263 : }
18264 :
18265 : int64_t fileSize;
18266 0 : rv = file->GetFileSize(&fileSize);
18267 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18268 0 : return rv;
18269 : }
18270 :
18271 0 : MOZ_ASSERT(fileSize >= 0);
18272 :
18273 0 : if (aDatabaseFiles) {
18274 0 : aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
18275 : } else {
18276 0 : aUsageInfo->AppendToFileUsage(uint64_t(fileSize));
18277 : }
18278 : }
18279 :
18280 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18281 0 : return rv;
18282 : }
18283 :
18284 0 : return NS_OK;
18285 : }
18286 :
18287 : void
18288 0 : QuotaClient::ProcessMaintenanceQueue()
18289 : {
18290 0 : AssertIsOnBackgroundThread();
18291 :
18292 0 : if (mCurrentMaintenance || mMaintenanceQueue.IsEmpty()) {
18293 0 : return;
18294 : }
18295 :
18296 0 : mCurrentMaintenance = mMaintenanceQueue[0];
18297 0 : mMaintenanceQueue.RemoveElementAt(0);
18298 :
18299 0 : mCurrentMaintenance->RunImmediately();
18300 : }
18301 :
18302 : void
18303 0 : Maintenance::RegisterDatabaseMaintenance(
18304 : DatabaseMaintenance* aDatabaseMaintenance)
18305 : {
18306 0 : AssertIsOnBackgroundThread();
18307 0 : MOZ_ASSERT(aDatabaseMaintenance);
18308 0 : MOZ_ASSERT(mState == State::BeginDatabaseMaintenance);
18309 0 : MOZ_ASSERT(!mDatabaseMaintenances.Get(aDatabaseMaintenance->DatabasePath()));
18310 :
18311 0 : mDatabaseMaintenances.Put(aDatabaseMaintenance->DatabasePath(),
18312 0 : aDatabaseMaintenance);
18313 0 : }
18314 :
18315 : void
18316 0 : Maintenance::UnregisterDatabaseMaintenance(
18317 : DatabaseMaintenance* aDatabaseMaintenance)
18318 : {
18319 0 : AssertIsOnBackgroundThread();
18320 0 : MOZ_ASSERT(aDatabaseMaintenance);
18321 0 : MOZ_ASSERT(mState == State::WaitingForDatabaseMaintenancesToComplete);
18322 0 : MOZ_ASSERT(mDatabaseMaintenances.Get(aDatabaseMaintenance->DatabasePath()));
18323 :
18324 0 : mDatabaseMaintenances.Remove(aDatabaseMaintenance->DatabasePath());
18325 :
18326 0 : if (mDatabaseMaintenances.Count()) {
18327 0 : return;
18328 : }
18329 :
18330 0 : mState = State::Finishing;
18331 0 : Finish();
18332 : }
18333 :
18334 : nsresult
18335 0 : Maintenance::Start()
18336 : {
18337 0 : AssertIsOnBackgroundThread();
18338 0 : MOZ_ASSERT(mState == State::Initial);
18339 :
18340 0 : if (IsAborted()) {
18341 0 : return NS_ERROR_ABORT;
18342 : }
18343 :
18344 : // Make sure that the IndexedDatabaseManager is running so that we can check
18345 : // for low disk space mode.
18346 :
18347 0 : if (IndexedDatabaseManager::Get()) {
18348 0 : OpenDirectory();
18349 0 : return NS_OK;
18350 : }
18351 :
18352 0 : mState = State::CreateIndexedDatabaseManager;
18353 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
18354 :
18355 0 : return NS_OK;
18356 : }
18357 :
18358 : nsresult
18359 0 : Maintenance::CreateIndexedDatabaseManager()
18360 : {
18361 0 : MOZ_ASSERT(NS_IsMainThread());
18362 0 : MOZ_ASSERT(mState == State::CreateIndexedDatabaseManager);
18363 :
18364 0 : if (IsAborted()) {
18365 0 : return NS_ERROR_ABORT;
18366 : }
18367 :
18368 0 : IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
18369 0 : if (NS_WARN_IF(!mgr)) {
18370 0 : return NS_ERROR_FAILURE;
18371 : }
18372 :
18373 0 : mState = State::IndexedDatabaseManagerOpen;
18374 0 : MOZ_ALWAYS_SUCCEEDS(
18375 : mQuotaClient->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL));
18376 :
18377 0 : return NS_OK;
18378 : }
18379 :
18380 : nsresult
18381 0 : Maintenance::OpenDirectory()
18382 : {
18383 0 : AssertIsOnBackgroundThread();
18384 0 : MOZ_ASSERT(mState == State::Initial ||
18385 : mState == State::IndexedDatabaseManagerOpen);
18386 0 : MOZ_ASSERT(!mDirectoryLock);
18387 0 : MOZ_ASSERT(QuotaManager::Get());
18388 :
18389 0 : if (IsAborted()) {
18390 0 : return NS_ERROR_ABORT;
18391 : }
18392 :
18393 : // Get a shared lock for <profile>/storage/*/*/idb
18394 :
18395 0 : mState = State::DirectoryOpenPending;
18396 0 : QuotaManager::Get()->OpenDirectoryInternal(
18397 0 : Nullable<PersistenceType>(),
18398 0 : OriginScope::FromNull(),
18399 0 : Nullable<Client::Type>(Client::IDB),
18400 : /* aExclusive */ false,
18401 0 : this);
18402 :
18403 0 : return NS_OK;
18404 : }
18405 :
18406 : nsresult
18407 0 : Maintenance::DirectoryOpen()
18408 : {
18409 0 : AssertIsOnBackgroundThread();
18410 0 : MOZ_ASSERT(mState == State::DirectoryOpenPending);
18411 0 : MOZ_ASSERT(mDirectoryLock);
18412 :
18413 0 : if (IsAborted()) {
18414 0 : return NS_ERROR_ABORT;
18415 : }
18416 :
18417 0 : QuotaManager* quotaManager = QuotaManager::Get();
18418 0 : MOZ_ASSERT(quotaManager);
18419 :
18420 0 : mState = State::DirectoryWorkOpen;
18421 :
18422 0 : nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
18423 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18424 0 : return NS_ERROR_FAILURE;
18425 : }
18426 :
18427 0 : return NS_OK;
18428 : }
18429 :
18430 : nsresult
18431 0 : Maintenance::DirectoryWork()
18432 : {
18433 0 : AssertIsOnIOThread();
18434 0 : MOZ_ASSERT(mState == State::DirectoryWorkOpen);
18435 :
18436 : // The storage directory is structured like this:
18437 : //
18438 : // <profile>/storage/<persistence>/<origin>/idb/*.sqlite
18439 : //
18440 : // We have to find all database files that match any persistence type and any
18441 : // origin. We ignore anything out of the ordinary for now.
18442 :
18443 0 : if (IsAborted()) {
18444 0 : return NS_ERROR_ABORT;
18445 : }
18446 :
18447 0 : QuotaManager* quotaManager = QuotaManager::Get();
18448 0 : MOZ_ASSERT(quotaManager);
18449 :
18450 0 : nsresult rv = quotaManager->EnsureStorageIsInitialized();
18451 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18452 0 : return rv;
18453 : }
18454 :
18455 0 : nsCOMPtr<nsIFile> storageDir = GetFileForPath(quotaManager->GetStoragePath());
18456 0 : if (NS_WARN_IF(!storageDir)) {
18457 0 : return NS_ERROR_FAILURE;
18458 : }
18459 :
18460 : bool exists;
18461 0 : rv = storageDir->Exists(&exists);
18462 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18463 0 : return rv;
18464 : }
18465 :
18466 0 : if (!exists) {
18467 0 : return NS_ERROR_NOT_AVAILABLE;
18468 : }
18469 :
18470 : bool isDirectory;
18471 0 : rv = storageDir->IsDirectory(&isDirectory);
18472 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18473 0 : return rv;
18474 : }
18475 :
18476 0 : if (NS_WARN_IF(!isDirectory)) {
18477 0 : return NS_ERROR_FAILURE;
18478 : }
18479 :
18480 : // There are currently only 3 persistence types, and we want to iterate them
18481 : // in this order:
18482 : static const PersistenceType kPersistenceTypes[] = {
18483 : PERSISTENCE_TYPE_PERSISTENT,
18484 : PERSISTENCE_TYPE_DEFAULT,
18485 : PERSISTENCE_TYPE_TEMPORARY
18486 : };
18487 :
18488 : static_assert((sizeof(kPersistenceTypes) / sizeof(kPersistenceTypes[0])) ==
18489 : size_t(PERSISTENCE_TYPE_INVALID),
18490 : "Something changed with available persistence types!");
18491 :
18492 0 : NS_NAMED_LITERAL_STRING(idbDirName, IDB_DIRECTORY_NAME);
18493 0 : NS_NAMED_LITERAL_STRING(sqliteExtension, ".sqlite");
18494 :
18495 0 : for (const PersistenceType persistenceType : kPersistenceTypes) {
18496 : // Loop over "<persistence>" directories.
18497 0 : if (IsAborted()) {
18498 0 : return NS_ERROR_ABORT;
18499 : }
18500 :
18501 0 : nsAutoCString persistenceTypeString;
18502 0 : if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
18503 : // XXX This shouldn't be a special case...
18504 0 : persistenceTypeString.AssignLiteral("permanent");
18505 : } else {
18506 0 : PersistenceTypeToText(persistenceType, persistenceTypeString);
18507 : }
18508 :
18509 0 : nsCOMPtr<nsIFile> persistenceDir;
18510 0 : rv = storageDir->Clone(getter_AddRefs(persistenceDir));
18511 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18512 0 : return rv;
18513 : }
18514 :
18515 0 : rv = persistenceDir->Append(NS_ConvertASCIItoUTF16(persistenceTypeString));
18516 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18517 0 : return rv;
18518 : }
18519 :
18520 0 : rv = persistenceDir->Exists(&exists);
18521 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18522 0 : return rv;
18523 : }
18524 :
18525 0 : if (!exists) {
18526 0 : continue;
18527 : }
18528 :
18529 0 : rv = persistenceDir->IsDirectory(&isDirectory);
18530 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18531 0 : return rv;
18532 : }
18533 :
18534 0 : if (NS_WARN_IF(!isDirectory)) {
18535 0 : continue;
18536 : }
18537 :
18538 0 : nsCOMPtr<nsISimpleEnumerator> persistenceDirEntries;
18539 0 : rv = persistenceDir->GetDirectoryEntries(
18540 0 : getter_AddRefs(persistenceDirEntries));
18541 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18542 0 : return rv;
18543 : }
18544 :
18545 0 : if (!persistenceDirEntries) {
18546 0 : continue;
18547 : }
18548 :
18549 : while (true) {
18550 : // Loop over "<origin>/idb" directories.
18551 0 : if (IsAborted()) {
18552 0 : return NS_ERROR_ABORT;
18553 : }
18554 :
18555 : bool persistenceDirHasMoreEntries;
18556 0 : rv = persistenceDirEntries->HasMoreElements(
18557 0 : &persistenceDirHasMoreEntries);
18558 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18559 0 : return rv;
18560 : }
18561 :
18562 0 : if (!persistenceDirHasMoreEntries) {
18563 0 : break;
18564 : }
18565 :
18566 0 : nsCOMPtr<nsISupports> persistenceDirEntry;
18567 0 : rv = persistenceDirEntries->GetNext(getter_AddRefs(persistenceDirEntry));
18568 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18569 0 : return rv;
18570 : }
18571 :
18572 0 : nsCOMPtr<nsIFile> originDir = do_QueryInterface(persistenceDirEntry);
18573 0 : MOZ_ASSERT(originDir);
18574 :
18575 0 : rv = originDir->Exists(&exists);
18576 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18577 0 : return rv;
18578 : }
18579 :
18580 0 : MOZ_ASSERT(exists);
18581 :
18582 0 : rv = originDir->IsDirectory(&isDirectory);
18583 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18584 0 : return rv;
18585 : }
18586 :
18587 0 : if (!isDirectory) {
18588 0 : continue;
18589 : }
18590 :
18591 0 : nsCOMPtr<nsIFile> idbDir;
18592 0 : rv = originDir->Clone(getter_AddRefs(idbDir));
18593 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18594 0 : return rv;
18595 : }
18596 :
18597 0 : rv = idbDir->Append(idbDirName);
18598 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18599 0 : return rv;
18600 : }
18601 :
18602 0 : rv = idbDir->Exists(&exists);
18603 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18604 0 : return rv;
18605 : }
18606 :
18607 0 : if (!exists) {
18608 0 : continue;
18609 : }
18610 :
18611 0 : rv = idbDir->IsDirectory(&isDirectory);
18612 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18613 0 : return rv;
18614 : }
18615 :
18616 0 : if (NS_WARN_IF(!isDirectory)) {
18617 0 : continue;
18618 : }
18619 :
18620 0 : nsCOMPtr<nsISimpleEnumerator> idbDirEntries;
18621 0 : rv = idbDir->GetDirectoryEntries(getter_AddRefs(idbDirEntries));
18622 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18623 0 : return rv;
18624 : }
18625 :
18626 0 : if (!idbDirEntries) {
18627 0 : continue;
18628 : }
18629 :
18630 0 : nsCString group;
18631 0 : nsCString origin;
18632 0 : nsTArray<nsString> databasePaths;
18633 :
18634 : while (true) {
18635 : // Loop over files in the "idb" directory.
18636 0 : if (IsAborted()) {
18637 0 : return NS_ERROR_ABORT;
18638 : }
18639 :
18640 : bool idbDirHasMoreEntries;
18641 0 : rv = idbDirEntries->HasMoreElements(&idbDirHasMoreEntries);
18642 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18643 0 : return rv;
18644 : }
18645 :
18646 0 : if (!idbDirHasMoreEntries) {
18647 0 : break;
18648 : }
18649 :
18650 0 : nsCOMPtr<nsISupports> idbDirEntry;
18651 0 : rv = idbDirEntries->GetNext(getter_AddRefs(idbDirEntry));
18652 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18653 0 : return rv;
18654 : }
18655 :
18656 0 : nsCOMPtr<nsIFile> idbDirFile = do_QueryInterface(idbDirEntry);
18657 0 : MOZ_ASSERT(idbDirFile);
18658 :
18659 0 : nsString idbFilePath;
18660 0 : rv = idbDirFile->GetPath(idbFilePath);
18661 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18662 0 : return rv;
18663 : }
18664 :
18665 0 : if (!StringEndsWith(idbFilePath, sqliteExtension)) {
18666 0 : continue;
18667 : }
18668 :
18669 0 : rv = idbDirFile->Exists(&exists);
18670 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18671 0 : return rv;
18672 : }
18673 :
18674 0 : MOZ_ASSERT(exists);
18675 :
18676 0 : rv = idbDirFile->IsDirectory(&isDirectory);
18677 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18678 0 : return rv;
18679 : }
18680 :
18681 0 : if (isDirectory) {
18682 0 : continue;
18683 : }
18684 :
18685 : // Found a database.
18686 0 : if (databasePaths.IsEmpty()) {
18687 0 : MOZ_ASSERT(group.IsEmpty());
18688 0 : MOZ_ASSERT(origin.IsEmpty());
18689 :
18690 : int64_t dummyTimeStamp;
18691 : bool dummyPersisted;
18692 0 : nsCString dummySuffix;
18693 0 : if (NS_WARN_IF(NS_FAILED(
18694 : quotaManager->GetDirectoryMetadata2(originDir,
18695 : &dummyTimeStamp,
18696 : &dummyPersisted,
18697 : dummySuffix,
18698 : group,
18699 : origin)))) {
18700 : // Not much we can do here...
18701 0 : continue;
18702 : }
18703 : }
18704 :
18705 0 : MOZ_ASSERT(!databasePaths.Contains(idbFilePath));
18706 :
18707 0 : databasePaths.AppendElement(idbFilePath);
18708 0 : }
18709 :
18710 0 : if (!databasePaths.IsEmpty()) {
18711 0 : mDirectoryInfos.AppendElement(DirectoryInfo(persistenceType,
18712 : group,
18713 : origin,
18714 0 : Move(databasePaths)));
18715 : }
18716 0 : }
18717 : }
18718 :
18719 0 : mState = State::BeginDatabaseMaintenance;
18720 :
18721 0 : MOZ_ALWAYS_SUCCEEDS(
18722 : mQuotaClient->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL));
18723 :
18724 0 : return NS_OK;
18725 : }
18726 :
18727 : nsresult
18728 0 : Maintenance::BeginDatabaseMaintenance()
18729 : {
18730 0 : AssertIsOnBackgroundThread();
18731 0 : MOZ_ASSERT(mState == State::BeginDatabaseMaintenance);
18732 :
18733 : class MOZ_STACK_CLASS Helper final
18734 : {
18735 : public:
18736 : static bool
18737 0 : IsSafeToRunMaintenance(const nsAString& aDatabasePath)
18738 : {
18739 0 : if (gFactoryOps) {
18740 0 : for (uint32_t index = gFactoryOps->Length(); index > 0; index--) {
18741 0 : RefPtr<FactoryOp>& existingOp = (*gFactoryOps)[index - 1];
18742 :
18743 0 : MOZ_ASSERT(!existingOp->DatabaseFilePath().IsEmpty());
18744 :
18745 0 : if (existingOp->DatabaseFilePath() == aDatabasePath) {
18746 0 : return false;
18747 : }
18748 : }
18749 : }
18750 :
18751 0 : if (gLiveDatabaseHashtable) {
18752 0 : for (auto iter = gLiveDatabaseHashtable->ConstIter();
18753 0 : !iter.Done(); iter.Next()) {
18754 0 : for (Database* database : iter.Data()->mLiveDatabases) {
18755 0 : if (database->FilePath() == aDatabasePath) {
18756 0 : return false;
18757 : }
18758 : }
18759 : }
18760 : }
18761 :
18762 0 : return true;
18763 : }
18764 : };
18765 :
18766 0 : RefPtr<nsThreadPool> threadPool;
18767 :
18768 0 : for (DirectoryInfo& directoryInfo : mDirectoryInfos) {
18769 0 : for (const nsString& databasePath : directoryInfo.mDatabasePaths) {
18770 0 : if (Helper::IsSafeToRunMaintenance(databasePath)) {
18771 : RefPtr<DatabaseMaintenance> databaseMaintenance =
18772 : new DatabaseMaintenance(this,
18773 0 : directoryInfo.mPersistenceType,
18774 : directoryInfo.mGroup,
18775 : directoryInfo.mOrigin,
18776 0 : databasePath);
18777 :
18778 0 : if (!threadPool) {
18779 0 : threadPool = mQuotaClient->GetOrCreateThreadPool();
18780 0 : MOZ_ASSERT(threadPool);
18781 : }
18782 :
18783 0 : MOZ_ALWAYS_SUCCEEDS(
18784 : threadPool->Dispatch(databaseMaintenance, NS_DISPATCH_NORMAL));
18785 :
18786 0 : RegisterDatabaseMaintenance(databaseMaintenance);
18787 : }
18788 : }
18789 : }
18790 :
18791 0 : mDirectoryInfos.Clear();
18792 :
18793 0 : if (mDatabaseMaintenances.Count()) {
18794 0 : mState = State::WaitingForDatabaseMaintenancesToComplete;
18795 : } else {
18796 0 : mState = State::Finishing;
18797 0 : Finish();
18798 : }
18799 :
18800 0 : return NS_OK;
18801 : }
18802 :
18803 : void
18804 0 : Maintenance::Finish()
18805 : {
18806 0 : AssertIsOnBackgroundThread();
18807 0 : MOZ_ASSERT(mState == State::Finishing);
18808 :
18809 0 : if (NS_FAILED(mResultCode)) {
18810 0 : nsCString errorName;
18811 0 : GetErrorName(mResultCode, errorName);
18812 :
18813 0 : IDB_WARNING("Maintenance finished with error: %s", errorName.get());
18814 : }
18815 :
18816 0 : mDirectoryLock = nullptr;
18817 :
18818 : // It can happen that we are only referenced by mCurrentMaintenance which is
18819 : // cleared in NoteFinishedMaintenance()
18820 0 : RefPtr<Maintenance> kungFuDeathGrip = this;
18821 :
18822 0 : mQuotaClient->NoteFinishedMaintenance(this);
18823 :
18824 0 : mState = State::Complete;
18825 0 : }
18826 :
18827 0 : NS_IMPL_ISUPPORTS_INHERITED0(Maintenance, Runnable)
18828 :
18829 : NS_IMETHODIMP
18830 0 : Maintenance::Run()
18831 : {
18832 0 : MOZ_ASSERT(mState != State::Complete);
18833 :
18834 : nsresult rv;
18835 :
18836 0 : switch (mState) {
18837 : case State::Initial:
18838 0 : rv = Start();
18839 0 : break;
18840 :
18841 : case State::CreateIndexedDatabaseManager:
18842 0 : rv = CreateIndexedDatabaseManager();
18843 0 : break;
18844 :
18845 : case State::IndexedDatabaseManagerOpen:
18846 0 : rv = OpenDirectory();
18847 0 : break;
18848 :
18849 : case State::DirectoryWorkOpen:
18850 0 : rv = DirectoryWork();
18851 0 : break;
18852 :
18853 : case State::BeginDatabaseMaintenance:
18854 0 : rv = BeginDatabaseMaintenance();
18855 0 : break;
18856 :
18857 : case State::Finishing:
18858 0 : Finish();
18859 0 : return NS_OK;
18860 :
18861 : default:
18862 0 : MOZ_CRASH("Bad state!");
18863 : }
18864 :
18865 0 : if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) {
18866 0 : if (NS_SUCCEEDED(mResultCode)) {
18867 0 : mResultCode = rv;
18868 : }
18869 :
18870 : // Must set mState before dispatching otherwise we will race with the owning
18871 : // thread.
18872 0 : mState = State::Finishing;
18873 :
18874 0 : if (IsOnBackgroundThread()) {
18875 0 : Finish();
18876 : } else {
18877 0 : MOZ_ALWAYS_SUCCEEDS(
18878 : mQuotaClient->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL));
18879 : }
18880 : }
18881 :
18882 0 : return NS_OK;
18883 : }
18884 :
18885 : void
18886 0 : Maintenance::DirectoryLockAcquired(DirectoryLock* aLock)
18887 : {
18888 0 : AssertIsOnBackgroundThread();
18889 0 : MOZ_ASSERT(mState == State::DirectoryOpenPending);
18890 0 : MOZ_ASSERT(!mDirectoryLock);
18891 :
18892 0 : mDirectoryLock = aLock;
18893 :
18894 0 : nsresult rv = DirectoryOpen();
18895 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18896 0 : if (NS_SUCCEEDED(mResultCode)) {
18897 0 : mResultCode = rv;
18898 : }
18899 :
18900 0 : mState = State::Finishing;
18901 0 : Finish();
18902 :
18903 0 : return;
18904 : }
18905 : }
18906 :
18907 : void
18908 0 : Maintenance::DirectoryLockFailed()
18909 : {
18910 0 : AssertIsOnBackgroundThread();
18911 0 : MOZ_ASSERT(mState == State::DirectoryOpenPending);
18912 0 : MOZ_ASSERT(!mDirectoryLock);
18913 :
18914 0 : if (NS_SUCCEEDED(mResultCode)) {
18915 0 : mResultCode = NS_ERROR_FAILURE;
18916 : }
18917 :
18918 0 : mState = State::Finishing;
18919 0 : Finish();
18920 0 : }
18921 :
18922 : void
18923 0 : DatabaseMaintenance::PerformMaintenanceOnDatabase()
18924 : {
18925 0 : MOZ_ASSERT(!NS_IsMainThread());
18926 0 : MOZ_ASSERT(!IsOnBackgroundThread());
18927 0 : MOZ_ASSERT(mMaintenance);
18928 0 : MOZ_ASSERT(mMaintenance->StartTime());
18929 0 : MOZ_ASSERT(!mDatabasePath.IsEmpty());
18930 0 : MOZ_ASSERT(!mGroup.IsEmpty());
18931 0 : MOZ_ASSERT(!mOrigin.IsEmpty());
18932 :
18933 : class MOZ_STACK_CLASS AutoClose final
18934 : {
18935 : nsCOMPtr<mozIStorageConnection> mConnection;
18936 :
18937 : public:
18938 0 : explicit AutoClose(mozIStorageConnection* aConnection)
18939 0 : : mConnection(aConnection)
18940 : {
18941 0 : MOZ_ASSERT(aConnection);
18942 0 : }
18943 :
18944 0 : ~AutoClose()
18945 0 : {
18946 0 : MOZ_ASSERT(mConnection);
18947 :
18948 0 : MOZ_ALWAYS_SUCCEEDS(mConnection->Close());
18949 0 : }
18950 : };
18951 :
18952 0 : nsCOMPtr<nsIFile> databaseFile = GetFileForPath(mDatabasePath);
18953 0 : MOZ_ASSERT(databaseFile);
18954 :
18955 0 : nsCOMPtr<mozIStorageConnection> connection;
18956 0 : nsresult rv = GetStorageConnection(databaseFile,
18957 0 : mPersistenceType,
18958 : mGroup,
18959 : mOrigin,
18960 : TelemetryIdForFile(databaseFile),
18961 0 : getter_AddRefs(connection));
18962 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18963 0 : return;
18964 : }
18965 :
18966 0 : AutoClose autoClose(connection);
18967 :
18968 0 : if (mMaintenance->IsAborted()) {
18969 0 : return;
18970 : }
18971 :
18972 0 : AutoProgressHandler progressHandler(mMaintenance);
18973 0 : if (NS_WARN_IF(NS_FAILED(progressHandler.Register(connection)))) {
18974 0 : return;
18975 : }
18976 :
18977 : bool databaseIsOk;
18978 0 : rv = CheckIntegrity(connection, &databaseIsOk);
18979 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18980 0 : return;
18981 : }
18982 :
18983 0 : if (NS_WARN_IF(!databaseIsOk)) {
18984 : // XXX Handle this somehow! Probably need to clear all storage for the
18985 : // origin. Needs followup.
18986 0 : MOZ_ASSERT(false, "Database corruption detected!");
18987 : return;
18988 : }
18989 :
18990 0 : if (mMaintenance->IsAborted()) {
18991 0 : return;
18992 : }
18993 :
18994 : MaintenanceAction maintenanceAction;
18995 0 : rv = DetermineMaintenanceAction(connection, databaseFile, &maintenanceAction);
18996 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
18997 0 : return;
18998 : }
18999 :
19000 0 : if (mMaintenance->IsAborted()) {
19001 0 : return;
19002 : }
19003 :
19004 0 : switch (maintenanceAction) {
19005 : case MaintenanceAction::Nothing:
19006 0 : break;
19007 :
19008 : case MaintenanceAction::IncrementalVacuum:
19009 0 : IncrementalVacuum(connection);
19010 0 : break;
19011 :
19012 : case MaintenanceAction::FullVacuum:
19013 0 : FullVacuum(connection, databaseFile);
19014 0 : break;
19015 :
19016 : default:
19017 0 : MOZ_CRASH("Unknown MaintenanceAction!");
19018 : }
19019 : }
19020 :
19021 : nsresult
19022 0 : DatabaseMaintenance::CheckIntegrity(mozIStorageConnection* aConnection,
19023 : bool* aOk)
19024 : {
19025 0 : MOZ_ASSERT(!NS_IsMainThread());
19026 0 : MOZ_ASSERT(!IsOnBackgroundThread());
19027 0 : MOZ_ASSERT(aConnection);
19028 0 : MOZ_ASSERT(aOk);
19029 :
19030 : nsresult rv;
19031 :
19032 : // First do a full integrity_check. Scope statements tightly here because
19033 : // later operations require zero live statements.
19034 : {
19035 0 : nsCOMPtr<mozIStorageStatement> stmt;
19036 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19037 : "PRAGMA integrity_check(1);"
19038 0 : ), getter_AddRefs(stmt));
19039 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19040 0 : return rv;
19041 : }
19042 :
19043 : bool hasResult;
19044 0 : rv = stmt->ExecuteStep(&hasResult);
19045 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19046 0 : return rv;
19047 : }
19048 :
19049 0 : MOZ_ASSERT(hasResult);
19050 :
19051 0 : nsString result;
19052 0 : rv = stmt->GetString(0, result);
19053 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19054 0 : return rv;
19055 : }
19056 :
19057 0 : if (NS_WARN_IF(!result.EqualsLiteral("ok"))) {
19058 0 : *aOk = false;
19059 0 : return NS_OK;
19060 : }
19061 : }
19062 :
19063 : // Now enable and check for foreign key constraints.
19064 : {
19065 : int32_t foreignKeysWereEnabled;
19066 : {
19067 0 : nsCOMPtr<mozIStorageStatement> stmt;
19068 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19069 : "PRAGMA foreign_keys;"
19070 0 : ), getter_AddRefs(stmt));
19071 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19072 0 : return rv;
19073 : }
19074 :
19075 : bool hasResult;
19076 0 : rv = stmt->ExecuteStep(&hasResult);
19077 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19078 0 : return rv;
19079 : }
19080 :
19081 0 : MOZ_ASSERT(hasResult);
19082 :
19083 0 : rv = stmt->GetInt32(0, &foreignKeysWereEnabled);
19084 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19085 0 : return rv;
19086 : }
19087 : }
19088 :
19089 0 : if (!foreignKeysWereEnabled) {
19090 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
19091 0 : "PRAGMA foreign_keys = ON;"));
19092 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19093 0 : return rv;
19094 : }
19095 : }
19096 :
19097 : bool foreignKeyError;
19098 : {
19099 0 : nsCOMPtr<mozIStorageStatement> stmt;
19100 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19101 : "PRAGMA foreign_key_check;"
19102 0 : ), getter_AddRefs(stmt));
19103 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19104 0 : return rv;
19105 : }
19106 :
19107 0 : rv = stmt->ExecuteStep(&foreignKeyError);
19108 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19109 0 : return rv;
19110 : }
19111 : }
19112 :
19113 0 : if (!foreignKeysWereEnabled) {
19114 0 : nsAutoCString stmtSQL;
19115 0 : stmtSQL.AssignLiteral("PRAGMA foreign_keys = ");
19116 0 : stmtSQL.AppendLiteral("OFF");
19117 0 : stmtSQL.Append(';');
19118 :
19119 0 : rv = aConnection->ExecuteSimpleSQL(stmtSQL);
19120 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19121 0 : return rv;
19122 : }
19123 : }
19124 :
19125 0 : if (foreignKeyError) {
19126 0 : *aOk = false;
19127 0 : return NS_OK;
19128 : }
19129 : }
19130 :
19131 0 : *aOk = true;
19132 0 : return NS_OK;
19133 : }
19134 :
19135 : nsresult
19136 0 : DatabaseMaintenance::DetermineMaintenanceAction(
19137 : mozIStorageConnection* aConnection,
19138 : nsIFile* aDatabaseFile,
19139 : MaintenanceAction* aMaintenanceAction)
19140 : {
19141 0 : MOZ_ASSERT(!NS_IsMainThread());
19142 0 : MOZ_ASSERT(!IsOnBackgroundThread());
19143 0 : MOZ_ASSERT(aConnection);
19144 0 : MOZ_ASSERT(aDatabaseFile);
19145 0 : MOZ_ASSERT(aMaintenanceAction);
19146 :
19147 : int32_t schemaVersion;
19148 0 : nsresult rv = aConnection->GetSchemaVersion(&schemaVersion);
19149 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19150 0 : return rv;
19151 : }
19152 :
19153 : // Don't do anything if the schema version is less than 18; before that
19154 : // version no databases had |auto_vacuum == INCREMENTAL| set and we didn't
19155 : // track the values needed for the heuristics below.
19156 0 : if (schemaVersion < MakeSchemaVersion(18, 0)) {
19157 0 : *aMaintenanceAction = MaintenanceAction::Nothing;
19158 0 : return NS_OK;
19159 : }
19160 :
19161 0 : bool lowDiskSpace = IndexedDatabaseManager::InLowDiskSpaceMode();
19162 :
19163 0 : if (QuotaManager::IsRunningXPCShellTests()) {
19164 : // If we're running XPCShell then we want to test both the low disk space
19165 : // and normal disk space code paths so pick semi-randomly based on the
19166 : // current time.
19167 0 : lowDiskSpace = ((PR_Now() / PR_USEC_PER_MSEC) % 2) == 0;
19168 : }
19169 :
19170 : // If we're low on disk space then the best we can hope for is that an
19171 : // incremental vacuum might free some space. That is a journaled operation so
19172 : // it may not be possible even then.
19173 0 : if (lowDiskSpace) {
19174 0 : *aMaintenanceAction = MaintenanceAction::IncrementalVacuum;
19175 0 : return NS_OK;
19176 : }
19177 :
19178 : // This method shouldn't make any permanent changes to the database, so make
19179 : // sure everything gets rolled back when we leave.
19180 : mozStorageTransaction transaction(aConnection,
19181 0 : /* aCommitOnComplete */ false);
19182 :
19183 : // Check to see when we last vacuumed this database.
19184 0 : nsCOMPtr<mozIStorageStatement> stmt;
19185 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19186 : "SELECT last_vacuum_time, last_vacuum_size "
19187 : "FROM database;"
19188 0 : ), getter_AddRefs(stmt));
19189 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19190 0 : return rv;
19191 : }
19192 :
19193 : bool hasResult;
19194 0 : rv = stmt->ExecuteStep(&hasResult);
19195 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19196 0 : return rv;
19197 : }
19198 :
19199 0 : MOZ_ASSERT(hasResult);
19200 :
19201 : PRTime lastVacuumTime;
19202 0 : rv = stmt->GetInt64(0, &lastVacuumTime);
19203 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19204 0 : return rv;
19205 : }
19206 :
19207 : int64_t lastVacuumSize;
19208 0 : rv = stmt->GetInt64(1, &lastVacuumSize);
19209 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19210 0 : return rv;
19211 : }
19212 :
19213 0 : NS_ASSERTION(lastVacuumSize > 0, "Thy last vacuum size shall be greater than zero, less than zero shall thy last vacuum size not be. Zero is right out.");
19214 :
19215 0 : PRTime startTime = mMaintenance->StartTime();
19216 :
19217 : // This shouldn't really be possible...
19218 0 : if (NS_WARN_IF(startTime <= lastVacuumTime)) {
19219 0 : *aMaintenanceAction = MaintenanceAction::Nothing;
19220 0 : return NS_OK;
19221 : }
19222 :
19223 0 : if (startTime - lastVacuumTime < kMinVacuumAge) {
19224 0 : *aMaintenanceAction = MaintenanceAction::IncrementalVacuum;
19225 0 : return NS_OK;
19226 : }
19227 :
19228 : // It has been more than a week since the database was vacuumed, so gather
19229 : // statistics on its usage to see if vacuuming is worthwhile.
19230 :
19231 : // Create a temporary copy of the dbstat table to speed up the queries that
19232 : // come later.
19233 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
19234 : "CREATE VIRTUAL TABLE __stats__ USING dbstat;"
19235 : "CREATE TEMP TABLE __temp_stats__ AS SELECT * FROM __stats__;"
19236 0 : ));
19237 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19238 0 : return rv;
19239 : }
19240 :
19241 : // Calculate the percentage of the database pages that are not in contiguous
19242 : // order.
19243 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19244 : "SELECT SUM(__ts1__.pageno != __ts2__.pageno + 1) * 100.0 / COUNT(*) "
19245 : "FROM __temp_stats__ AS __ts1__, __temp_stats__ AS __ts2__ "
19246 : "WHERE __ts1__.name = __ts2__.name "
19247 : "AND __ts1__.rowid = __ts2__.rowid + 1;"
19248 0 : ), getter_AddRefs(stmt));
19249 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19250 0 : return rv;
19251 : }
19252 :
19253 0 : rv = stmt->ExecuteStep(&hasResult);
19254 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19255 0 : return rv;
19256 : }
19257 :
19258 0 : MOZ_ASSERT(hasResult);
19259 :
19260 : int32_t percentUnordered;
19261 0 : rv = stmt->GetInt32(0, &percentUnordered);
19262 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19263 0 : return rv;
19264 : }
19265 :
19266 0 : MOZ_ASSERT(percentUnordered >= 0);
19267 0 : MOZ_ASSERT(percentUnordered <= 100);
19268 :
19269 0 : if (percentUnordered >= kPercentUnorderedThreshold) {
19270 0 : *aMaintenanceAction = MaintenanceAction::FullVacuum;
19271 0 : return NS_OK;
19272 : }
19273 :
19274 : // Don't try a full vacuum if the file hasn't grown by 10%.
19275 : int64_t currentFileSize;
19276 0 : rv = aDatabaseFile->GetFileSize(¤tFileSize);
19277 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19278 0 : return rv;
19279 : }
19280 :
19281 0 : if (currentFileSize <= lastVacuumSize ||
19282 0 : (((currentFileSize - lastVacuumSize) * 100 / currentFileSize) <
19283 : kPercentFileSizeGrowthThreshold)) {
19284 0 : *aMaintenanceAction = MaintenanceAction::IncrementalVacuum;
19285 0 : return NS_OK;
19286 : }
19287 :
19288 : // See if there are any free pages that we can reclaim.
19289 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19290 : "PRAGMA freelist_count;"
19291 0 : ), getter_AddRefs(stmt));
19292 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19293 0 : return rv;
19294 : }
19295 :
19296 0 : rv = stmt->ExecuteStep(&hasResult);
19297 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19298 0 : return rv;
19299 : }
19300 :
19301 0 : MOZ_ASSERT(hasResult);
19302 :
19303 : int32_t freelistCount;
19304 0 : rv = stmt->GetInt32(0, &freelistCount);
19305 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19306 0 : return rv;
19307 : }
19308 :
19309 0 : MOZ_ASSERT(freelistCount >= 0);
19310 :
19311 : // If we have too many free pages then we should try an incremental vacuum. If
19312 : // that causes too much fragmentation then we'll try a full vacuum later.
19313 0 : if (freelistCount > kMaxFreelistThreshold) {
19314 0 : *aMaintenanceAction = MaintenanceAction::IncrementalVacuum;
19315 0 : return NS_OK;
19316 : }
19317 :
19318 : // Calculate the percentage of unused bytes on pages in the database.
19319 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19320 : "SELECT SUM(unused) * 100.0 / SUM(pgsize) "
19321 : "FROM __temp_stats__;"
19322 0 : ), getter_AddRefs(stmt));
19323 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19324 0 : return rv;
19325 : }
19326 :
19327 0 : rv = stmt->ExecuteStep(&hasResult);
19328 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19329 0 : return rv;
19330 : }
19331 :
19332 0 : MOZ_ASSERT(hasResult);
19333 :
19334 : int32_t percentUnused;
19335 0 : rv = stmt->GetInt32(0, &percentUnused);
19336 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19337 0 : return rv;
19338 : }
19339 :
19340 0 : MOZ_ASSERT(percentUnused >= 0);
19341 0 : MOZ_ASSERT(percentUnused <= 100);
19342 :
19343 0 : *aMaintenanceAction = percentUnused >= kPercentUnusedThreshold ?
19344 : MaintenanceAction::FullVacuum :
19345 : MaintenanceAction::IncrementalVacuum;
19346 0 : return NS_OK;
19347 : }
19348 :
19349 : void
19350 0 : DatabaseMaintenance::IncrementalVacuum(mozIStorageConnection* aConnection)
19351 : {
19352 0 : MOZ_ASSERT(!NS_IsMainThread());
19353 0 : MOZ_ASSERT(!IsOnBackgroundThread());
19354 0 : MOZ_ASSERT(aConnection);
19355 :
19356 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
19357 : "PRAGMA incremental_vacuum;"
19358 0 : ));
19359 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19360 0 : return;
19361 : }
19362 : }
19363 :
19364 : void
19365 0 : DatabaseMaintenance::FullVacuum(mozIStorageConnection* aConnection,
19366 : nsIFile* aDatabaseFile)
19367 : {
19368 0 : MOZ_ASSERT(!NS_IsMainThread());
19369 0 : MOZ_ASSERT(!IsOnBackgroundThread());
19370 0 : MOZ_ASSERT(aConnection);
19371 0 : MOZ_ASSERT(aDatabaseFile);
19372 :
19373 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
19374 : "VACUUM;"
19375 0 : ));
19376 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19377 0 : return;
19378 : }
19379 :
19380 0 : PRTime vacuumTime = PR_Now();
19381 0 : MOZ_ASSERT(vacuumTime > 0);
19382 :
19383 : int64_t fileSize;
19384 0 : rv = aDatabaseFile->GetFileSize(&fileSize);
19385 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19386 0 : return;
19387 : }
19388 :
19389 0 : MOZ_ASSERT(fileSize > 0);
19390 :
19391 0 : nsCOMPtr<mozIStorageStatement> stmt;
19392 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19393 : "UPDATE database "
19394 : "SET last_vacuum_time = :time"
19395 : ", last_vacuum_size = :size;"
19396 0 : ), getter_AddRefs(stmt));
19397 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19398 0 : return;
19399 : }
19400 :
19401 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("time"), vacuumTime);
19402 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19403 0 : return;
19404 : }
19405 :
19406 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("size"), fileSize);
19407 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19408 0 : return;
19409 : }
19410 :
19411 0 : rv = stmt->Execute();
19412 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19413 0 : return;
19414 : }
19415 : }
19416 :
19417 : void
19418 0 : DatabaseMaintenance::RunOnOwningThread()
19419 : {
19420 0 : AssertIsOnBackgroundThread();
19421 :
19422 0 : if (mCompleteCallback) {
19423 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mCompleteCallback.forget()));
19424 : }
19425 :
19426 0 : mMaintenance->UnregisterDatabaseMaintenance(this);
19427 0 : }
19428 :
19429 : void
19430 0 : DatabaseMaintenance::RunOnConnectionThread()
19431 : {
19432 0 : MOZ_ASSERT(!NS_IsMainThread());
19433 0 : MOZ_ASSERT(!IsOnBackgroundThread());
19434 :
19435 0 : PerformMaintenanceOnDatabase();
19436 :
19437 0 : MOZ_ALWAYS_SUCCEEDS(
19438 : mMaintenance->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL));
19439 0 : }
19440 :
19441 : NS_IMETHODIMP
19442 0 : DatabaseMaintenance::Run()
19443 : {
19444 0 : if (IsOnBackgroundThread()) {
19445 0 : RunOnOwningThread();
19446 : } else {
19447 0 : RunOnConnectionThread();
19448 : }
19449 :
19450 0 : return NS_OK;
19451 : }
19452 :
19453 : nsresult
19454 0 : DatabaseMaintenance::
19455 : AutoProgressHandler::Register(mozIStorageConnection* aConnection)
19456 : {
19457 0 : MOZ_ASSERT(!NS_IsMainThread());
19458 0 : MOZ_ASSERT(!IsOnBackgroundThread());
19459 0 : MOZ_ASSERT(aConnection);
19460 :
19461 : // We want to quickly bail out of any operation if the user becomes active, so
19462 : // use a small granularity here since database performance isn't critical.
19463 : static const int32_t kProgressGranularity = 50;
19464 :
19465 0 : nsCOMPtr<mozIStorageProgressHandler> oldHandler;
19466 0 : nsresult rv = aConnection->SetProgressHandler(kProgressGranularity,
19467 : this,
19468 0 : getter_AddRefs(oldHandler));
19469 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19470 0 : return rv;
19471 : }
19472 :
19473 0 : MOZ_ASSERT(!oldHandler);
19474 0 : mConnection = aConnection;
19475 :
19476 0 : return NS_OK;
19477 : }
19478 :
19479 : void
19480 0 : DatabaseMaintenance::
19481 : AutoProgressHandler::Unregister()
19482 : {
19483 0 : MOZ_ASSERT(!NS_IsMainThread());
19484 0 : MOZ_ASSERT(!IsOnBackgroundThread());
19485 0 : MOZ_ASSERT(mConnection);
19486 :
19487 0 : nsCOMPtr<mozIStorageProgressHandler> oldHandler;
19488 0 : nsresult rv = mConnection->RemoveProgressHandler(getter_AddRefs(oldHandler));
19489 0 : Unused << NS_WARN_IF(NS_FAILED(rv));
19490 :
19491 0 : MOZ_ASSERT_IF(NS_SUCCEEDED(rv), oldHandler == this);
19492 0 : }
19493 :
19494 : NS_IMETHODIMP_(MozExternalRefCountType)
19495 0 : DatabaseMaintenance::
19496 : AutoProgressHandler::AddRef()
19497 : {
19498 0 : NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
19499 :
19500 : #ifdef DEBUG
19501 0 : mDEBUGRefCnt++;
19502 : #endif
19503 0 : return 2;
19504 : }
19505 :
19506 : NS_IMETHODIMP_(MozExternalRefCountType)
19507 0 : DatabaseMaintenance::
19508 : AutoProgressHandler::Release()
19509 : {
19510 0 : NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
19511 :
19512 : #ifdef DEBUG
19513 0 : mDEBUGRefCnt--;
19514 : #endif
19515 0 : return 1;
19516 : }
19517 :
19518 0 : NS_IMPL_QUERY_INTERFACE(DatabaseMaintenance::AutoProgressHandler,
19519 : mozIStorageProgressHandler)
19520 :
19521 : NS_IMETHODIMP
19522 0 : DatabaseMaintenance::
19523 : AutoProgressHandler::OnProgress(mozIStorageConnection* aConnection,
19524 : bool* _retval)
19525 : {
19526 0 : NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
19527 0 : MOZ_ASSERT(aConnection);
19528 0 : MOZ_ASSERT(mConnection == aConnection);
19529 0 : MOZ_ASSERT(_retval);
19530 :
19531 0 : *_retval = mMaintenance->IsAborted();
19532 :
19533 0 : return NS_OK;
19534 : }
19535 :
19536 : /*******************************************************************************
19537 : * Local class implementations
19538 : ******************************************************************************/
19539 :
19540 0 : NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction)
19541 0 : NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction)
19542 0 : NS_IMPL_ISUPPORTS(StripObsoleteOriginAttributesFunction, mozIStorageFunction);
19543 :
19544 : #if !defined(MOZ_B2G)
19545 :
19546 : nsresult
19547 0 : UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory,
19548 : mozIStorageConnection* aConnection)
19549 : {
19550 : // This file manager doesn't need real origin info, etc. The only purpose is
19551 : // to store file ids without adding more complexity or code duplication.
19552 : RefPtr<FileManager> fileManager =
19553 : new FileManager(PERSISTENCE_TYPE_INVALID,
19554 0 : EmptyCString(),
19555 0 : EmptyCString(),
19556 0 : EmptyString(),
19557 0 : false);
19558 :
19559 0 : nsresult rv = fileManager->Init(aFMDirectory, aConnection);
19560 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19561 0 : return rv;
19562 : }
19563 :
19564 0 : nsAutoPtr<NormalJSContext> context(NormalJSContext::Create());
19565 0 : if (NS_WARN_IF(!context)) {
19566 0 : return NS_ERROR_FAILURE;
19567 : }
19568 :
19569 0 : mFileManager.swap(fileManager);
19570 0 : mContext = context;
19571 0 : return NS_OK;
19572 : }
19573 :
19574 0 : NS_IMPL_ISUPPORTS(UpgradeFileIdsFunction, mozIStorageFunction)
19575 :
19576 : NS_IMETHODIMP
19577 0 : UpgradeFileIdsFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
19578 : nsIVariant** aResult)
19579 : {
19580 0 : MOZ_ASSERT(aArguments);
19581 0 : MOZ_ASSERT(aResult);
19582 0 : MOZ_ASSERT(mFileManager);
19583 0 : MOZ_ASSERT(mContext);
19584 :
19585 0 : AUTO_PROFILER_LABEL("UpgradeFileIdsFunction::OnFunctionCall", STORAGE);
19586 :
19587 : uint32_t argc;
19588 0 : nsresult rv = aArguments->GetNumEntries(&argc);
19589 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19590 0 : return rv;
19591 : }
19592 :
19593 0 : if (argc != 2) {
19594 0 : NS_WARNING("Don't call me with the wrong number of arguments!");
19595 0 : return NS_ERROR_UNEXPECTED;
19596 : }
19597 :
19598 0 : StructuredCloneReadInfo cloneInfo;
19599 0 : DatabaseOperationBase::GetStructuredCloneReadInfoFromValueArray(aArguments,
19600 : 1,
19601 : 0,
19602 : mFileManager,
19603 0 : &cloneInfo);
19604 :
19605 0 : JSContext* cx = mContext->Context();
19606 0 : JSAutoRequest ar(cx);
19607 0 : JSAutoCompartment ac(cx, mContext->Global());
19608 :
19609 0 : JS::Rooted<JS::Value> clone(cx);
19610 0 : if (NS_WARN_IF(!IDBObjectStore::DeserializeUpgradeValue(cx, cloneInfo,
19611 : &clone))) {
19612 0 : return NS_ERROR_DOM_DATA_CLONE_ERR;
19613 : }
19614 :
19615 0 : nsAutoString fileIds;
19616 :
19617 0 : for (uint32_t count = cloneInfo.mFiles.Length(), index = 0;
19618 0 : index < count;
19619 : index++) {
19620 0 : StructuredCloneFile& file = cloneInfo.mFiles[index];
19621 0 : MOZ_ASSERT(file.mFileInfo);
19622 :
19623 0 : const int64_t id = file.mFileInfo->Id();
19624 :
19625 0 : if (index) {
19626 0 : fileIds.Append(' ');
19627 : }
19628 0 : fileIds.AppendInt(file.mType == StructuredCloneFile::eBlob ? id : -id);
19629 : }
19630 :
19631 0 : nsCOMPtr<nsIVariant> result = new mozilla::storage::TextVariant(fileIds);
19632 :
19633 0 : result.forget(aResult);
19634 0 : return NS_OK;
19635 : }
19636 :
19637 : #endif // MOZ_B2G
19638 :
19639 : // static
19640 : void
19641 0 : DatabaseOperationBase::GetBindingClauseForKeyRange(
19642 : const SerializedKeyRange& aKeyRange,
19643 : const nsACString& aKeyColumnName,
19644 : nsAutoCString& aBindingClause)
19645 : {
19646 0 : MOZ_ASSERT(!IsOnBackgroundThread());
19647 0 : MOZ_ASSERT(!aKeyColumnName.IsEmpty());
19648 :
19649 0 : NS_NAMED_LITERAL_CSTRING(andStr, " AND ");
19650 0 : NS_NAMED_LITERAL_CSTRING(spacecolon, " :");
19651 0 : NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key");
19652 :
19653 0 : if (aKeyRange.isOnly()) {
19654 : // Both keys equal.
19655 0 : aBindingClause = andStr + aKeyColumnName + NS_LITERAL_CSTRING(" =") +
19656 0 : spacecolon + lowerKey;
19657 0 : return;
19658 : }
19659 :
19660 0 : aBindingClause.Truncate();
19661 :
19662 0 : if (!aKeyRange.lower().IsUnset()) {
19663 : // Lower key is set.
19664 0 : aBindingClause.Append(andStr + aKeyColumnName);
19665 0 : aBindingClause.AppendLiteral(" >");
19666 0 : if (!aKeyRange.lowerOpen()) {
19667 0 : aBindingClause.AppendLiteral("=");
19668 : }
19669 0 : aBindingClause.Append(spacecolon + lowerKey);
19670 : }
19671 :
19672 0 : if (!aKeyRange.upper().IsUnset()) {
19673 : // Upper key is set.
19674 0 : aBindingClause.Append(andStr + aKeyColumnName);
19675 0 : aBindingClause.AppendLiteral(" <");
19676 0 : if (!aKeyRange.upperOpen()) {
19677 0 : aBindingClause.AppendLiteral("=");
19678 : }
19679 0 : aBindingClause.Append(spacecolon + NS_LITERAL_CSTRING("upper_key"));
19680 : }
19681 :
19682 0 : MOZ_ASSERT(!aBindingClause.IsEmpty());
19683 : }
19684 :
19685 : // static
19686 : uint64_t
19687 0 : DatabaseOperationBase::ReinterpretDoubleAsUInt64(double aDouble)
19688 : {
19689 : // This is a duplicate of the js engine's byte munging in StructuredClone.cpp
19690 0 : return BitwiseCast<uint64_t>(aDouble);
19691 : }
19692 :
19693 : // static
19694 : template <typename T>
19695 : nsresult
19696 0 : DatabaseOperationBase::GetStructuredCloneReadInfoFromSource(
19697 : T* aSource,
19698 : uint32_t aDataIndex,
19699 : uint32_t aFileIdsIndex,
19700 : FileManager* aFileManager,
19701 : StructuredCloneReadInfo* aInfo)
19702 : {
19703 0 : MOZ_ASSERT(!IsOnBackgroundThread());
19704 0 : MOZ_ASSERT(aSource);
19705 0 : MOZ_ASSERT(aFileManager);
19706 0 : MOZ_ASSERT(aInfo);
19707 :
19708 : int32_t columnType;
19709 0 : nsresult rv = aSource->GetTypeOfIndex(aDataIndex, &columnType);
19710 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19711 0 : return rv;
19712 : }
19713 :
19714 0 : MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_BLOB ||
19715 : columnType == mozIStorageStatement::VALUE_TYPE_INTEGER);
19716 :
19717 : bool isNull;
19718 0 : rv = aSource->GetIsNull(aFileIdsIndex, &isNull);
19719 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19720 0 : return rv;
19721 : }
19722 :
19723 0 : nsString fileIds;
19724 :
19725 0 : if (isNull) {
19726 0 : fileIds.SetIsVoid(true);
19727 : } else {
19728 0 : rv = aSource->GetString(aFileIdsIndex, fileIds);
19729 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19730 0 : return rv;
19731 : }
19732 : }
19733 :
19734 0 : if (columnType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
19735 : uint64_t intData;
19736 0 : rv = aSource->GetInt64(aDataIndex, reinterpret_cast<int64_t*>(&intData));
19737 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19738 0 : return rv;
19739 : }
19740 :
19741 0 : rv = GetStructuredCloneReadInfoFromExternalBlob(intData,
19742 : aFileManager,
19743 : fileIds,
19744 : aInfo);
19745 : } else {
19746 : const uint8_t* blobData;
19747 : uint32_t blobDataLength;
19748 0 : rv = aSource->GetSharedBlob(aDataIndex, &blobDataLength, &blobData);
19749 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19750 0 : return rv;
19751 : }
19752 :
19753 0 : rv = GetStructuredCloneReadInfoFromBlob(blobData,
19754 : blobDataLength,
19755 : aFileManager,
19756 : fileIds,
19757 : aInfo);
19758 : }
19759 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19760 0 : return rv;
19761 : }
19762 :
19763 0 : return NS_OK;
19764 : }
19765 :
19766 : // static
19767 : nsresult
19768 0 : DatabaseOperationBase::GetStructuredCloneReadInfoFromBlob(
19769 : const uint8_t* aBlobData,
19770 : uint32_t aBlobDataLength,
19771 : FileManager* aFileManager,
19772 : const nsAString& aFileIds,
19773 : StructuredCloneReadInfo* aInfo)
19774 : {
19775 0 : MOZ_ASSERT(!IsOnBackgroundThread());
19776 0 : MOZ_ASSERT(aFileManager);
19777 0 : MOZ_ASSERT(aInfo);
19778 :
19779 0 : AUTO_PROFILER_LABEL(
19780 : "DatabaseOperationBase::GetStructuredCloneReadInfoFromBlob", STORAGE);
19781 :
19782 0 : const char* compressed = reinterpret_cast<const char*>(aBlobData);
19783 0 : size_t compressedLength = size_t(aBlobDataLength);
19784 :
19785 : size_t uncompressedLength;
19786 0 : if (NS_WARN_IF(!snappy::GetUncompressedLength(compressed, compressedLength,
19787 : &uncompressedLength))) {
19788 0 : return NS_ERROR_FILE_CORRUPTED;
19789 : }
19790 :
19791 0 : AutoTArray<uint8_t, 512> uncompressed;
19792 0 : if (NS_WARN_IF(!uncompressed.SetLength(uncompressedLength, fallible))) {
19793 0 : return NS_ERROR_OUT_OF_MEMORY;
19794 : }
19795 :
19796 0 : char* uncompressedBuffer = reinterpret_cast<char*>(uncompressed.Elements());
19797 :
19798 0 : if (NS_WARN_IF(!snappy::RawUncompress(compressed, compressedLength,
19799 : uncompressedBuffer))) {
19800 0 : return NS_ERROR_FILE_CORRUPTED;
19801 : }
19802 :
19803 0 : if (!aInfo->mData.WriteBytes(uncompressedBuffer, uncompressed.Length())) {
19804 0 : return NS_ERROR_OUT_OF_MEMORY;
19805 : }
19806 :
19807 0 : if (!aFileIds.IsVoid()) {
19808 0 : nsresult rv = DeserializeStructuredCloneFiles(aFileManager,
19809 : aFileIds,
19810 : aInfo->mFiles,
19811 0 : &aInfo->mHasPreprocessInfo);
19812 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19813 0 : return rv;
19814 : }
19815 : }
19816 :
19817 0 : return NS_OK;
19818 : }
19819 :
19820 : // static
19821 : nsresult
19822 0 : DatabaseOperationBase::GetStructuredCloneReadInfoFromExternalBlob(
19823 : uint64_t aIntData,
19824 : FileManager* aFileManager,
19825 : const nsAString& aFileIds,
19826 : StructuredCloneReadInfo* aInfo)
19827 : {
19828 0 : MOZ_ASSERT(!IsOnBackgroundThread());
19829 0 : MOZ_ASSERT(aFileManager);
19830 0 : MOZ_ASSERT(aInfo);
19831 :
19832 0 : AUTO_PROFILER_LABEL(
19833 : "DatabaseOperationBase::GetStructuredCloneReadInfoFromExternalBlob",
19834 : STORAGE);
19835 :
19836 : nsresult rv;
19837 :
19838 0 : if (!aFileIds.IsVoid()) {
19839 0 : rv = DeserializeStructuredCloneFiles(aFileManager,
19840 : aFileIds,
19841 : aInfo->mFiles,
19842 0 : &aInfo->mHasPreprocessInfo);
19843 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19844 0 : return rv;
19845 : }
19846 : }
19847 :
19848 : // Higher and lower 32 bits described
19849 : // in ObjectStoreAddOrPutRequestOp::DoDatabaseWork.
19850 0 : uint32_t index = uint32_t(aIntData & 0xFFFFFFFF);
19851 :
19852 0 : if (index >= aInfo->mFiles.Length()) {
19853 0 : MOZ_ASSERT(false, "Bad index value!");
19854 : return NS_ERROR_UNEXPECTED;
19855 : }
19856 :
19857 0 : StructuredCloneFile& file = aInfo->mFiles[index];
19858 0 : MOZ_ASSERT(file.mFileInfo);
19859 0 : MOZ_ASSERT(file.mType == StructuredCloneFile::eStructuredClone);
19860 :
19861 0 : nsCOMPtr<nsIFile> nativeFile = GetFileForFileInfo(file.mFileInfo);
19862 0 : if (NS_WARN_IF(!nativeFile)) {
19863 0 : return NS_ERROR_FAILURE;
19864 : }
19865 :
19866 0 : nsCOMPtr<nsIInputStream> fileInputStream;
19867 0 : rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), nativeFile);
19868 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19869 0 : return rv;
19870 : }
19871 :
19872 : RefPtr<SnappyUncompressInputStream> snappyInputStream =
19873 0 : new SnappyUncompressInputStream(fileInputStream);
19874 :
19875 : do {
19876 : char buffer[kFileCopyBufferSize];
19877 :
19878 : uint32_t numRead;
19879 0 : rv = snappyInputStream->Read(buffer, sizeof(buffer), &numRead);
19880 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19881 0 : break;
19882 : }
19883 :
19884 0 : if (!numRead) {
19885 0 : break;
19886 : }
19887 :
19888 0 : if (NS_WARN_IF(!aInfo->mData.WriteBytes(buffer, numRead))) {
19889 0 : rv = NS_ERROR_OUT_OF_MEMORY;
19890 0 : break;
19891 0 : }
19892 : } while (true);
19893 :
19894 0 : return rv;
19895 : }
19896 :
19897 : // static
19898 : nsresult
19899 0 : DatabaseOperationBase::BindKeyRangeToStatement(
19900 : const SerializedKeyRange& aKeyRange,
19901 : mozIStorageStatement* aStatement)
19902 : {
19903 0 : MOZ_ASSERT(!IsOnBackgroundThread());
19904 0 : MOZ_ASSERT(aStatement);
19905 :
19906 0 : nsresult rv = NS_OK;
19907 :
19908 0 : if (!aKeyRange.lower().IsUnset()) {
19909 0 : rv = aKeyRange.lower().BindToStatement(aStatement, NS_LITERAL_CSTRING("lower_key"));
19910 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19911 0 : return rv;
19912 : }
19913 : }
19914 :
19915 0 : if (aKeyRange.isOnly()) {
19916 0 : return rv;
19917 : }
19918 :
19919 0 : if (!aKeyRange.upper().IsUnset()) {
19920 0 : rv = aKeyRange.upper().BindToStatement(aStatement, NS_LITERAL_CSTRING("upper_key"));
19921 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19922 0 : return rv;
19923 : }
19924 : }
19925 :
19926 0 : return NS_OK;
19927 : }
19928 :
19929 : // static
19930 : nsresult
19931 0 : DatabaseOperationBase::BindKeyRangeToStatement(
19932 : const SerializedKeyRange& aKeyRange,
19933 : mozIStorageStatement* aStatement,
19934 : const nsCString& aLocale)
19935 : {
19936 : #ifndef ENABLE_INTL_API
19937 : return BindKeyRangeToStatement(aKeyRange, aStatement);
19938 : #else
19939 0 : MOZ_ASSERT(!IsOnBackgroundThread());
19940 0 : MOZ_ASSERT(aStatement);
19941 0 : MOZ_ASSERT(!aLocale.IsEmpty());
19942 :
19943 0 : nsresult rv = NS_OK;
19944 :
19945 0 : if (!aKeyRange.lower().IsUnset()) {
19946 0 : Key lower;
19947 0 : rv = aKeyRange.lower().ToLocaleBasedKey(lower, aLocale);
19948 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19949 0 : return rv;
19950 : }
19951 :
19952 0 : rv = lower.BindToStatement(aStatement, NS_LITERAL_CSTRING("lower_key"));
19953 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19954 0 : return rv;
19955 : }
19956 : }
19957 :
19958 0 : if (aKeyRange.isOnly()) {
19959 0 : return rv;
19960 : }
19961 :
19962 0 : if (!aKeyRange.upper().IsUnset()) {
19963 0 : Key upper;
19964 0 : rv = aKeyRange.upper().ToLocaleBasedKey(upper, aLocale);
19965 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19966 0 : return rv;
19967 : }
19968 :
19969 0 : rv = upper.BindToStatement(aStatement, NS_LITERAL_CSTRING("upper_key"));
19970 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
19971 0 : return rv;
19972 : }
19973 : }
19974 :
19975 0 : return NS_OK;
19976 : #endif
19977 : }
19978 :
19979 : // static
19980 : void
19981 0 : DatabaseOperationBase::AppendConditionClause(const nsACString& aColumnName,
19982 : const nsACString& aArgName,
19983 : bool aLessThan,
19984 : bool aEquals,
19985 : nsAutoCString& aResult)
19986 : {
19987 0 : aResult += NS_LITERAL_CSTRING(" AND ") + aColumnName +
19988 0 : NS_LITERAL_CSTRING(" ");
19989 :
19990 0 : if (aLessThan) {
19991 0 : aResult.Append('<');
19992 : }
19993 : else {
19994 0 : aResult.Append('>');
19995 : }
19996 :
19997 0 : if (aEquals) {
19998 0 : aResult.Append('=');
19999 : }
20000 :
20001 0 : aResult += NS_LITERAL_CSTRING(" :") + aArgName;
20002 0 : }
20003 :
20004 : // static
20005 : nsresult
20006 0 : DatabaseOperationBase::GetUniqueIndexTableForObjectStore(
20007 : TransactionBase* aTransaction,
20008 : int64_t aObjectStoreId,
20009 : Maybe<UniqueIndexTable>& aMaybeUniqueIndexTable)
20010 : {
20011 0 : AssertIsOnBackgroundThread();
20012 0 : MOZ_ASSERT(aTransaction);
20013 0 : MOZ_ASSERT(aObjectStoreId);
20014 0 : MOZ_ASSERT(aMaybeUniqueIndexTable.isNothing());
20015 :
20016 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
20017 0 : aTransaction->GetMetadataForObjectStoreId(aObjectStoreId);
20018 0 : MOZ_ASSERT(objectStoreMetadata);
20019 :
20020 0 : if (!objectStoreMetadata->mIndexes.Count()) {
20021 0 : return NS_OK;
20022 : }
20023 :
20024 0 : const uint32_t indexCount = objectStoreMetadata->mIndexes.Count();
20025 0 : MOZ_ASSERT(indexCount > 0);
20026 :
20027 0 : aMaybeUniqueIndexTable.emplace();
20028 0 : UniqueIndexTable* uniqueIndexTable = aMaybeUniqueIndexTable.ptr();
20029 0 : MOZ_ASSERT(uniqueIndexTable);
20030 :
20031 0 : for (auto iter = objectStoreMetadata->mIndexes.Iter(); !iter.Done(); iter.Next()) {
20032 0 : FullIndexMetadata* value = iter.UserData();
20033 0 : MOZ_ASSERT(!uniqueIndexTable->Get(value->mCommonMetadata.id()));
20034 :
20035 0 : if (NS_WARN_IF(!uniqueIndexTable->Put(value->mCommonMetadata.id(),
20036 : value->mCommonMetadata.unique(),
20037 : fallible))) {
20038 0 : break;
20039 : }
20040 : }
20041 :
20042 0 : if (NS_WARN_IF(aMaybeUniqueIndexTable.ref().Count() != indexCount)) {
20043 0 : IDB_REPORT_INTERNAL_ERR();
20044 0 : aMaybeUniqueIndexTable.reset();
20045 0 : NS_WARNING("out of memory");
20046 0 : return NS_ERROR_OUT_OF_MEMORY;
20047 : }
20048 :
20049 : #ifdef DEBUG
20050 0 : aMaybeUniqueIndexTable.ref().MarkImmutable();
20051 : #endif
20052 :
20053 0 : return NS_OK;
20054 : }
20055 :
20056 : // static
20057 : nsresult
20058 0 : DatabaseOperationBase::IndexDataValuesFromUpdateInfos(
20059 : const nsTArray<IndexUpdateInfo>& aUpdateInfos,
20060 : const UniqueIndexTable& aUniqueIndexTable,
20061 : nsTArray<IndexDataValue>& aIndexValues)
20062 : {
20063 0 : MOZ_ASSERT(aIndexValues.IsEmpty());
20064 0 : MOZ_ASSERT_IF(!aUpdateInfos.IsEmpty(), aUniqueIndexTable.Count());
20065 :
20066 0 : AUTO_PROFILER_LABEL(
20067 : "DatabaseOperationBase::IndexDataValuesFromUpdateInfos", STORAGE);
20068 :
20069 0 : const uint32_t count = aUpdateInfos.Length();
20070 :
20071 0 : if (!count) {
20072 0 : return NS_OK;
20073 : }
20074 :
20075 0 : if (NS_WARN_IF(!aIndexValues.SetCapacity(count, fallible))) {
20076 0 : IDB_REPORT_INTERNAL_ERR();
20077 0 : return NS_ERROR_OUT_OF_MEMORY;
20078 : }
20079 :
20080 0 : for (uint32_t idxIndex = 0; idxIndex < count; idxIndex++) {
20081 0 : const IndexUpdateInfo& updateInfo = aUpdateInfos[idxIndex];
20082 0 : const int64_t& indexId = updateInfo.indexId();
20083 0 : const Key& key = updateInfo.value();
20084 0 : const Key& sortKey = updateInfo.localizedValue();
20085 :
20086 0 : bool unique = false;
20087 0 : MOZ_ALWAYS_TRUE(aUniqueIndexTable.Get(indexId, &unique));
20088 :
20089 0 : IndexDataValue idv(indexId, unique, key, sortKey);
20090 :
20091 0 : MOZ_ALWAYS_TRUE(
20092 : aIndexValues.InsertElementSorted(idv, fallible));
20093 : }
20094 :
20095 0 : return NS_OK;
20096 : }
20097 :
20098 : // static
20099 : nsresult
20100 0 : DatabaseOperationBase::InsertIndexTableRows(
20101 : DatabaseConnection* aConnection,
20102 : const int64_t aObjectStoreId,
20103 : const Key& aObjectStoreKey,
20104 : const FallibleTArray<IndexDataValue>& aIndexValues)
20105 : {
20106 0 : MOZ_ASSERT(aConnection);
20107 0 : aConnection->AssertIsOnConnectionThread();
20108 0 : MOZ_ASSERT(!aObjectStoreKey.IsUnset());
20109 :
20110 0 : AUTO_PROFILER_LABEL("DatabaseOperationBase::InsertIndexTableRows", STORAGE);
20111 :
20112 0 : const uint32_t count = aIndexValues.Length();
20113 0 : if (!count) {
20114 0 : return NS_OK;
20115 : }
20116 :
20117 0 : NS_NAMED_LITERAL_CSTRING(objectStoreIdString, "object_store_id");
20118 0 : NS_NAMED_LITERAL_CSTRING(objectDataKeyString, "object_data_key");
20119 0 : NS_NAMED_LITERAL_CSTRING(indexIdString, "index_id");
20120 0 : NS_NAMED_LITERAL_CSTRING(valueString, "value");
20121 0 : NS_NAMED_LITERAL_CSTRING(valueLocaleString, "value_locale");
20122 :
20123 0 : DatabaseConnection::CachedStatement insertUniqueStmt;
20124 0 : DatabaseConnection::CachedStatement insertStmt;
20125 :
20126 : nsresult rv;
20127 :
20128 0 : for (uint32_t index = 0; index < count; index++) {
20129 0 : const IndexDataValue& info = aIndexValues[index];
20130 :
20131 : DatabaseConnection::CachedStatement& stmt =
20132 0 : info.mUnique ? insertUniqueStmt : insertStmt;
20133 :
20134 0 : if (stmt) {
20135 0 : stmt.Reset();
20136 0 : } else if (info.mUnique) {
20137 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20138 : "INSERT INTO unique_index_data "
20139 : "(index_id, value, object_store_id, object_data_key, value_locale) "
20140 : "VALUES (:index_id, :value, :object_store_id, :object_data_key, :value_locale);"),
20141 0 : &stmt);
20142 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20143 0 : return rv;
20144 : }
20145 : } else {
20146 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20147 : "INSERT OR IGNORE INTO index_data "
20148 : "(index_id, value, object_data_key, object_store_id, value_locale) "
20149 : "VALUES (:index_id, :value, :object_data_key, :object_store_id, :value_locale);"),
20150 0 : &stmt);
20151 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20152 0 : return rv;
20153 : }
20154 : }
20155 :
20156 0 : rv = stmt->BindInt64ByName(indexIdString, info.mIndexId);
20157 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20158 0 : return rv;
20159 : }
20160 :
20161 0 : rv = info.mKey.BindToStatement(stmt, valueString);
20162 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20163 0 : return rv;
20164 : }
20165 :
20166 0 : rv = info.mSortKey.BindToStatement(stmt, valueLocaleString);
20167 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20168 0 : return rv;
20169 : }
20170 :
20171 0 : rv = stmt->BindInt64ByName(objectStoreIdString, aObjectStoreId);
20172 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20173 0 : return rv;
20174 : }
20175 :
20176 0 : rv = aObjectStoreKey.BindToStatement(stmt, objectDataKeyString);
20177 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20178 0 : return rv;
20179 : }
20180 :
20181 0 : rv = stmt->Execute();
20182 0 : if (rv == NS_ERROR_STORAGE_CONSTRAINT && info.mUnique) {
20183 : // If we're inserting multiple entries for the same unique index, then
20184 : // we might have failed to insert due to colliding with another entry for
20185 : // the same index in which case we should ignore it.
20186 0 : for (int32_t index2 = int32_t(index) - 1;
20187 0 : index2 >= 0 && aIndexValues[index2].mIndexId == info.mIndexId;
20188 : --index2) {
20189 0 : if (info.mKey == aIndexValues[index2].mKey) {
20190 : // We found a key with the same value for the same index. So we
20191 : // must have had a collision with a value we just inserted.
20192 0 : rv = NS_OK;
20193 0 : break;
20194 : }
20195 : }
20196 : }
20197 :
20198 0 : if (NS_FAILED(rv)) {
20199 0 : return rv;
20200 : }
20201 : }
20202 :
20203 0 : return NS_OK;
20204 : }
20205 :
20206 : // static
20207 : nsresult
20208 0 : DatabaseOperationBase::DeleteIndexDataTableRows(
20209 : DatabaseConnection* aConnection,
20210 : const Key& aObjectStoreKey,
20211 : const FallibleTArray<IndexDataValue>& aIndexValues)
20212 : {
20213 0 : MOZ_ASSERT(aConnection);
20214 0 : aConnection->AssertIsOnConnectionThread();
20215 0 : MOZ_ASSERT(!aObjectStoreKey.IsUnset());
20216 :
20217 0 : AUTO_PROFILER_LABEL(
20218 : "DatabaseOperationBase::DeleteIndexDataTableRows", STORAGE);
20219 :
20220 0 : const uint32_t count = aIndexValues.Length();
20221 0 : if (!count) {
20222 0 : return NS_OK;
20223 : }
20224 :
20225 0 : NS_NAMED_LITERAL_CSTRING(indexIdString, "index_id");
20226 0 : NS_NAMED_LITERAL_CSTRING(valueString, "value");
20227 0 : NS_NAMED_LITERAL_CSTRING(objectDataKeyString, "object_data_key");
20228 :
20229 0 : DatabaseConnection::CachedStatement deleteUniqueStmt;
20230 0 : DatabaseConnection::CachedStatement deleteStmt;
20231 :
20232 : nsresult rv;
20233 :
20234 0 : for (uint32_t index = 0; index < count; index++) {
20235 0 : const IndexDataValue& indexValue = aIndexValues[index];
20236 :
20237 : DatabaseConnection::CachedStatement& stmt =
20238 0 : indexValue.mUnique ? deleteUniqueStmt : deleteStmt;
20239 :
20240 0 : if (stmt) {
20241 0 : stmt.Reset();
20242 0 : } else if (indexValue.mUnique) {
20243 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20244 : "DELETE FROM unique_index_data "
20245 : "WHERE index_id = :index_id "
20246 : "AND value = :value;"),
20247 0 : &stmt);
20248 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20249 0 : return rv;
20250 : }
20251 : } else {
20252 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20253 : "DELETE FROM index_data "
20254 : "WHERE index_id = :index_id "
20255 : "AND value = :value "
20256 : "AND object_data_key = :object_data_key;"),
20257 0 : &stmt);
20258 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20259 0 : return rv;
20260 : }
20261 : }
20262 :
20263 0 : rv = stmt->BindInt64ByName(indexIdString, indexValue.mIndexId);
20264 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20265 0 : return rv;
20266 : }
20267 :
20268 0 : rv = indexValue.mKey.BindToStatement(stmt, valueString);
20269 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20270 0 : return rv;
20271 : }
20272 :
20273 0 : if (!indexValue.mUnique) {
20274 0 : rv = aObjectStoreKey.BindToStatement(stmt, objectDataKeyString);
20275 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20276 0 : return rv;
20277 : }
20278 : }
20279 :
20280 0 : rv = stmt->Execute();
20281 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20282 0 : return rv;
20283 : }
20284 : }
20285 :
20286 0 : return NS_OK;
20287 : }
20288 :
20289 : // static
20290 : nsresult
20291 0 : DatabaseOperationBase::DeleteObjectStoreDataTableRowsWithIndexes(
20292 : DatabaseConnection* aConnection,
20293 : const int64_t aObjectStoreId,
20294 : const OptionalKeyRange& aKeyRange)
20295 : {
20296 0 : MOZ_ASSERT(aConnection);
20297 0 : aConnection->AssertIsOnConnectionThread();
20298 0 : MOZ_ASSERT(aObjectStoreId);
20299 :
20300 : #ifdef DEBUG
20301 : {
20302 0 : bool hasIndexes = false;
20303 0 : MOZ_ASSERT(NS_SUCCEEDED(
20304 : ObjectStoreHasIndexes(aConnection, aObjectStoreId, &hasIndexes)));
20305 0 : MOZ_ASSERT(hasIndexes,
20306 : "Don't use this slow method if there are no indexes!");
20307 : }
20308 : #endif
20309 :
20310 0 : AUTO_PROFILER_LABEL(
20311 : "DatabaseOperationBase::DeleteObjectStoreDataTableRowsWithIndexes",
20312 : STORAGE);
20313 :
20314 : const bool singleRowOnly =
20315 0 : aKeyRange.type() == OptionalKeyRange::TSerializedKeyRange &&
20316 0 : aKeyRange.get_SerializedKeyRange().isOnly();
20317 :
20318 0 : NS_NAMED_LITERAL_CSTRING(objectStoreIdString, "object_store_id");
20319 0 : NS_NAMED_LITERAL_CSTRING(keyString, "key");
20320 :
20321 : nsresult rv;
20322 0 : Key objectStoreKey;
20323 0 : DatabaseConnection::CachedStatement selectStmt;
20324 :
20325 0 : if (singleRowOnly) {
20326 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20327 : "SELECT index_data_values "
20328 : "FROM object_data "
20329 : "WHERE object_store_id = :object_store_id "
20330 : "AND key = :key;"),
20331 0 : &selectStmt);
20332 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20333 0 : return rv;
20334 : }
20335 :
20336 0 : objectStoreKey = aKeyRange.get_SerializedKeyRange().lower();
20337 :
20338 0 : rv = objectStoreKey.BindToStatement(selectStmt, keyString);
20339 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20340 0 : return rv;
20341 : }
20342 : } else {
20343 0 : nsAutoCString keyRangeClause;
20344 0 : if (aKeyRange.type() == OptionalKeyRange::TSerializedKeyRange) {
20345 0 : GetBindingClauseForKeyRange(aKeyRange.get_SerializedKeyRange(),
20346 : keyString,
20347 0 : keyRangeClause);
20348 : }
20349 :
20350 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20351 : "SELECT index_data_values, key "
20352 : "FROM object_data "
20353 0 : "WHERE object_store_id = :") +
20354 0 : objectStoreIdString +
20355 0 : keyRangeClause +
20356 0 : NS_LITERAL_CSTRING(";"),
20357 0 : &selectStmt);
20358 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20359 0 : return rv;
20360 : }
20361 :
20362 0 : if (aKeyRange.type() == OptionalKeyRange::TSerializedKeyRange) {
20363 0 : rv = BindKeyRangeToStatement(aKeyRange, selectStmt);
20364 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20365 0 : return rv;
20366 : }
20367 : }
20368 : }
20369 :
20370 0 : rv = selectStmt->BindInt64ByName(objectStoreIdString, aObjectStoreId);
20371 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20372 0 : return rv;
20373 : }
20374 :
20375 0 : DatabaseConnection::CachedStatement deleteStmt;
20376 0 : AutoTArray<IndexDataValue, 32> indexValues;
20377 :
20378 0 : DebugOnly<uint32_t> resultCountDEBUG = 0;
20379 :
20380 : bool hasResult;
20381 0 : while (NS_SUCCEEDED(rv = selectStmt->ExecuteStep(&hasResult)) && hasResult) {
20382 0 : if (!singleRowOnly) {
20383 0 : rv = objectStoreKey.SetFromStatement(selectStmt, 1);
20384 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20385 0 : return rv;
20386 : }
20387 :
20388 0 : indexValues.ClearAndRetainStorage();
20389 : }
20390 :
20391 0 : rv = ReadCompressedIndexDataValues(selectStmt, 0, indexValues);
20392 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20393 0 : return rv;
20394 : }
20395 :
20396 0 : rv = DeleteIndexDataTableRows(aConnection, objectStoreKey, indexValues);
20397 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20398 0 : return rv;
20399 : }
20400 :
20401 0 : if (deleteStmt) {
20402 0 : MOZ_ALWAYS_SUCCEEDS(deleteStmt->Reset());
20403 : } else {
20404 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20405 : "DELETE FROM object_data "
20406 : "WHERE object_store_id = :object_store_id "
20407 : "AND key = :key;"),
20408 0 : &deleteStmt);
20409 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20410 0 : return rv;
20411 : }
20412 : }
20413 :
20414 0 : rv = deleteStmt->BindInt64ByName(objectStoreIdString, aObjectStoreId);
20415 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20416 0 : return rv;
20417 : }
20418 :
20419 0 : rv = objectStoreKey.BindToStatement(deleteStmt, keyString);
20420 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20421 0 : return rv;
20422 : }
20423 :
20424 0 : rv = deleteStmt->Execute();
20425 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20426 0 : return rv;
20427 : }
20428 :
20429 0 : resultCountDEBUG++;
20430 : }
20431 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20432 0 : return rv;
20433 : }
20434 :
20435 0 : MOZ_ASSERT_IF(singleRowOnly, resultCountDEBUG <= 1);
20436 :
20437 0 : return NS_OK;
20438 : }
20439 :
20440 : // static
20441 : nsresult
20442 0 : DatabaseOperationBase::UpdateIndexValues(
20443 : DatabaseConnection* aConnection,
20444 : const int64_t aObjectStoreId,
20445 : const Key& aObjectStoreKey,
20446 : const FallibleTArray<IndexDataValue>& aIndexValues)
20447 : {
20448 0 : MOZ_ASSERT(aConnection);
20449 0 : aConnection->AssertIsOnConnectionThread();
20450 0 : MOZ_ASSERT(!aObjectStoreKey.IsUnset());
20451 :
20452 0 : AUTO_PROFILER_LABEL("DatabaseOperationBase::UpdateIndexValues", STORAGE);
20453 :
20454 0 : UniqueFreePtr<uint8_t> indexDataValues;
20455 : uint32_t indexDataValuesLength;
20456 : nsresult rv = MakeCompressedIndexDataValues(aIndexValues,
20457 : indexDataValues,
20458 0 : &indexDataValuesLength);
20459 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20460 0 : return rv;
20461 : }
20462 :
20463 0 : MOZ_ASSERT(!indexDataValuesLength == !(indexDataValues.get()));
20464 :
20465 0 : DatabaseConnection::CachedStatement updateStmt;
20466 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20467 : "UPDATE object_data "
20468 : "SET index_data_values = :index_data_values "
20469 : "WHERE object_store_id = :object_store_id "
20470 : "AND key = :key;"),
20471 0 : &updateStmt);
20472 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20473 0 : return rv;
20474 : }
20475 :
20476 0 : NS_NAMED_LITERAL_CSTRING(indexDataValuesString, "index_data_values");
20477 :
20478 0 : if (indexDataValues) {
20479 0 : rv = updateStmt->BindAdoptedBlobByName(indexDataValuesString,
20480 : indexDataValues.release(),
20481 0 : indexDataValuesLength);
20482 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20483 0 : return rv;
20484 : }
20485 : } else {
20486 0 : rv = updateStmt->BindNullByName(indexDataValuesString);
20487 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20488 0 : return rv;
20489 : }
20490 : }
20491 :
20492 0 : rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
20493 0 : aObjectStoreId);
20494 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20495 0 : return rv;
20496 : }
20497 :
20498 0 : rv = aObjectStoreKey.BindToStatement(updateStmt, NS_LITERAL_CSTRING("key"));
20499 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20500 0 : return rv;
20501 : }
20502 :
20503 0 : rv = updateStmt->Execute();
20504 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20505 0 : return rv;
20506 : }
20507 :
20508 0 : return NS_OK;
20509 : }
20510 :
20511 : // static
20512 : nsresult
20513 0 : DatabaseOperationBase::ObjectStoreHasIndexes(DatabaseConnection* aConnection,
20514 : const int64_t aObjectStoreId,
20515 : bool* aHasIndexes)
20516 : {
20517 0 : MOZ_ASSERT(aConnection);
20518 0 : aConnection->AssertIsOnConnectionThread();
20519 0 : MOZ_ASSERT(aObjectStoreId);
20520 0 : MOZ_ASSERT(aHasIndexes);
20521 :
20522 0 : DatabaseConnection::CachedStatement stmt;
20523 :
20524 0 : nsresult rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20525 : "SELECT id "
20526 : "FROM object_store_index "
20527 : "WHERE object_store_id = :object_store_id "
20528 : "LIMIT 1;"),
20529 0 : &stmt);
20530 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20531 0 : return rv;
20532 : }
20533 :
20534 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
20535 0 : aObjectStoreId);
20536 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20537 0 : return rv;
20538 : }
20539 :
20540 : bool hasResult;
20541 0 : rv = stmt->ExecuteStep(&hasResult);
20542 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20543 0 : return rv;
20544 : }
20545 :
20546 0 : *aHasIndexes = hasResult;
20547 0 : return NS_OK;
20548 : }
20549 :
20550 0 : NS_IMPL_ISUPPORTS_INHERITED(DatabaseOperationBase,
20551 : Runnable,
20552 : mozIStorageProgressHandler)
20553 :
20554 : NS_IMETHODIMP
20555 0 : DatabaseOperationBase::OnProgress(mozIStorageConnection* aConnection,
20556 : bool* _retval)
20557 : {
20558 0 : MOZ_ASSERT(!IsOnBackgroundThread());
20559 0 : MOZ_ASSERT(aConnection);
20560 0 : MOZ_ASSERT(_retval);
20561 :
20562 : // This is intentionally racy.
20563 0 : *_retval = !OperationMayProceed();
20564 0 : return NS_OK;
20565 : }
20566 :
20567 0 : DatabaseOperationBase::
20568 0 : AutoSetProgressHandler::AutoSetProgressHandler()
20569 : : mConnection(nullptr)
20570 : #ifdef DEBUG
20571 0 : , mDEBUGDatabaseOp(nullptr)
20572 : #endif
20573 : {
20574 0 : MOZ_ASSERT(!IsOnBackgroundThread());
20575 0 : }
20576 :
20577 0 : DatabaseOperationBase::
20578 0 : AutoSetProgressHandler::~AutoSetProgressHandler()
20579 : {
20580 0 : MOZ_ASSERT(!IsOnBackgroundThread());
20581 :
20582 0 : if (mConnection) {
20583 0 : nsCOMPtr<mozIStorageProgressHandler> oldHandler;
20584 0 : MOZ_ALWAYS_SUCCEEDS(
20585 : mConnection->RemoveProgressHandler(getter_AddRefs(oldHandler)));
20586 0 : MOZ_ASSERT(oldHandler == mDEBUGDatabaseOp);
20587 : }
20588 0 : }
20589 :
20590 : nsresult
20591 0 : DatabaseOperationBase::
20592 : AutoSetProgressHandler::Register(mozIStorageConnection* aConnection,
20593 : DatabaseOperationBase* aDatabaseOp)
20594 : {
20595 0 : MOZ_ASSERT(!IsOnBackgroundThread());
20596 0 : MOZ_ASSERT(aConnection);
20597 0 : MOZ_ASSERT(aDatabaseOp);
20598 0 : MOZ_ASSERT(!mConnection);
20599 :
20600 0 : nsCOMPtr<mozIStorageProgressHandler> oldProgressHandler;
20601 :
20602 : nsresult rv =
20603 0 : aConnection->SetProgressHandler(kStorageProgressGranularity,
20604 : aDatabaseOp,
20605 0 : getter_AddRefs(oldProgressHandler));
20606 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20607 0 : return rv;
20608 : }
20609 :
20610 0 : MOZ_ASSERT(!oldProgressHandler);
20611 :
20612 0 : mConnection = aConnection;
20613 : #ifdef DEBUG
20614 0 : mDEBUGDatabaseOp = aDatabaseOp;
20615 : #endif
20616 :
20617 0 : return NS_OK;
20618 : }
20619 :
20620 0 : MutableFile::MutableFile(nsIFile* aFile,
20621 : Database* aDatabase,
20622 0 : FileInfo* aFileInfo)
20623 : : BackgroundMutableFileParentBase(FILE_HANDLE_STORAGE_IDB,
20624 0 : aDatabase->Id(),
20625 0 : IntString(aFileInfo->Id()),
20626 : aFile)
20627 : , mDatabase(aDatabase)
20628 0 : , mFileInfo(aFileInfo)
20629 : {
20630 0 : AssertIsOnBackgroundThread();
20631 0 : MOZ_ASSERT(aDatabase);
20632 0 : MOZ_ASSERT(aFileInfo);
20633 0 : }
20634 :
20635 0 : MutableFile::~MutableFile()
20636 : {
20637 0 : mDatabase->UnregisterMutableFile(this);
20638 0 : }
20639 :
20640 : already_AddRefed<MutableFile>
20641 0 : MutableFile::Create(nsIFile* aFile,
20642 : Database* aDatabase,
20643 : FileInfo* aFileInfo)
20644 : {
20645 0 : AssertIsOnBackgroundThread();
20646 :
20647 : RefPtr<MutableFile> newMutableFile =
20648 0 : new MutableFile(aFile, aDatabase, aFileInfo);
20649 :
20650 0 : if (!aDatabase->RegisterMutableFile(newMutableFile)) {
20651 0 : return nullptr;
20652 : }
20653 :
20654 0 : return newMutableFile.forget();
20655 : }
20656 :
20657 : void
20658 0 : MutableFile::NoteActiveState()
20659 : {
20660 0 : AssertIsOnBackgroundThread();
20661 :
20662 0 : mDatabase->NoteActiveMutableFile();
20663 0 : }
20664 :
20665 : void
20666 0 : MutableFile::NoteInactiveState()
20667 : {
20668 0 : AssertIsOnBackgroundThread();
20669 :
20670 0 : mDatabase->NoteInactiveMutableFile();
20671 0 : }
20672 :
20673 : PBackgroundParent*
20674 0 : MutableFile::GetBackgroundParent() const
20675 : {
20676 0 : AssertIsOnBackgroundThread();
20677 0 : MOZ_ASSERT(!IsActorDestroyed());
20678 :
20679 0 : return GetDatabase()->GetBackgroundParent();
20680 : }
20681 :
20682 : already_AddRefed<nsISupports>
20683 0 : MutableFile::CreateStream(bool aReadOnly)
20684 : {
20685 0 : AssertIsOnBackgroundThread();
20686 :
20687 0 : PersistenceType persistenceType = mDatabase->Type();
20688 0 : const nsACString& group = mDatabase->Group();
20689 0 : const nsACString& origin = mDatabase->Origin();
20690 :
20691 0 : nsCOMPtr<nsISupports> result;
20692 :
20693 0 : if (aReadOnly) {
20694 : RefPtr<FileInputStream> stream =
20695 0 : FileInputStream::Create(persistenceType, group, origin, mFile, -1, -1,
20696 0 : nsIFileInputStream::DEFER_OPEN);
20697 0 : result = NS_ISUPPORTS_CAST(nsIFileInputStream*, stream);
20698 : }
20699 : else {
20700 : RefPtr<FileStream> stream =
20701 0 : FileStream::Create(persistenceType, group, origin, mFile, -1, -1,
20702 0 : nsIFileStream::DEFER_OPEN);
20703 0 : result = NS_ISUPPORTS_CAST(nsIFileStream*, stream);
20704 : }
20705 0 : if (NS_WARN_IF(!result)) {
20706 0 : return nullptr;
20707 : }
20708 :
20709 0 : return result.forget();
20710 : }
20711 :
20712 : already_AddRefed<BlobImpl>
20713 0 : MutableFile::CreateBlobImpl()
20714 : {
20715 0 : AssertIsOnBackgroundThread();
20716 :
20717 0 : RefPtr<FileBlobImpl> blobImpl = new FileBlobImpl(mFile);
20718 0 : blobImpl->SetFileId(mFileInfo->Id());
20719 :
20720 0 : return blobImpl.forget();
20721 : }
20722 :
20723 : PBackgroundFileHandleParent*
20724 0 : MutableFile::AllocPBackgroundFileHandleParent(const FileMode& aMode)
20725 : {
20726 0 : AssertIsOnBackgroundThread();
20727 :
20728 : // Once a database is closed it must not try to open new file handles.
20729 0 : if (NS_WARN_IF(mDatabase->IsClosed())) {
20730 0 : if (!mDatabase->IsInvalidated()) {
20731 0 : ASSERT_UNLESS_FUZZING();
20732 : }
20733 0 : return nullptr;
20734 : }
20735 :
20736 0 : if (!gFileHandleThreadPool) {
20737 : RefPtr<FileHandleThreadPool> fileHandleThreadPool =
20738 0 : FileHandleThreadPool::Create();
20739 0 : if (NS_WARN_IF(!fileHandleThreadPool)) {
20740 0 : return nullptr;
20741 : }
20742 :
20743 0 : gFileHandleThreadPool = fileHandleThreadPool;
20744 : }
20745 :
20746 0 : return BackgroundMutableFileParentBase::AllocPBackgroundFileHandleParent(
20747 0 : aMode);
20748 : }
20749 :
20750 : mozilla::ipc::IPCResult
20751 0 : MutableFile::RecvPBackgroundFileHandleConstructor(
20752 : PBackgroundFileHandleParent* aActor,
20753 : const FileMode& aMode)
20754 : {
20755 0 : AssertIsOnBackgroundThread();
20756 0 : MOZ_ASSERT(!mDatabase->IsClosed());
20757 :
20758 0 : if (NS_WARN_IF(mDatabase->IsInvalidated())) {
20759 : // This is an expected race. We don't want the child to die here, just don't
20760 : // actually do any work.
20761 0 : return IPC_OK();
20762 : }
20763 :
20764 : return BackgroundMutableFileParentBase::RecvPBackgroundFileHandleConstructor(
20765 0 : aActor, aMode);
20766 : }
20767 :
20768 : mozilla::ipc::IPCResult
20769 0 : MutableFile::RecvGetFileId(int64_t* aFileId)
20770 : {
20771 0 : AssertIsOnBackgroundThread();
20772 0 : MOZ_ASSERT(mFileInfo);
20773 :
20774 0 : if (NS_WARN_IF(!IndexedDatabaseManager::InTestingMode())) {
20775 0 : ASSERT_UNLESS_FUZZING();
20776 : return IPC_FAIL_NO_REASON(this);
20777 : }
20778 :
20779 0 : *aFileId = mFileInfo->Id();
20780 0 : return IPC_OK();
20781 : }
20782 :
20783 0 : FactoryOp::FactoryOp(Factory* aFactory,
20784 : already_AddRefed<ContentParent> aContentParent,
20785 : const CommonFactoryRequestParams& aCommonParams,
20786 0 : bool aDeleting)
20787 : : DatabaseOperationBase(aFactory->GetLoggingInfo()->Id(),
20788 : aFactory->GetLoggingInfo()->NextRequestSN())
20789 : , mFactory(aFactory)
20790 0 : , mContentParent(Move(aContentParent))
20791 : , mCommonParams(aCommonParams)
20792 : , mState(State::Initial)
20793 : , mEnforcingQuota(true)
20794 : , mDeleting(aDeleting)
20795 : , mBlockedDatabaseOpen(false)
20796 : , mChromeWriteAccessAllowed(false)
20797 0 : , mFileHandleDisabled(false)
20798 : {
20799 0 : AssertIsOnBackgroundThread();
20800 0 : MOZ_ASSERT(aFactory);
20801 0 : MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
20802 0 : }
20803 :
20804 : nsresult
20805 0 : FactoryOp::Open()
20806 : {
20807 0 : MOZ_ASSERT(NS_IsMainThread());
20808 0 : MOZ_ASSERT(mState == State::Initial);
20809 :
20810 : // Swap this to the stack now to ensure that we release it on this thread.
20811 0 : RefPtr<ContentParent> contentParent;
20812 0 : mContentParent.swap(contentParent);
20813 :
20814 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
20815 0 : !OperationMayProceed()) {
20816 0 : IDB_REPORT_INTERNAL_ERR();
20817 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
20818 : }
20819 :
20820 : PermissionRequestBase::PermissionValue permission;
20821 0 : nsresult rv = CheckPermission(contentParent, &permission);
20822 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20823 0 : return rv;
20824 : }
20825 :
20826 0 : MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
20827 : permission == PermissionRequestBase::kPermissionDenied ||
20828 : permission == PermissionRequestBase::kPermissionPrompt);
20829 :
20830 0 : if (permission == PermissionRequestBase::kPermissionDenied) {
20831 0 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
20832 : }
20833 :
20834 : {
20835 : // These services have to be started on the main thread currently.
20836 :
20837 : IndexedDatabaseManager* mgr;
20838 0 : if (NS_WARN_IF(!(mgr = IndexedDatabaseManager::GetOrCreate()))) {
20839 0 : IDB_REPORT_INTERNAL_ERR();
20840 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
20841 : }
20842 :
20843 0 : nsCOMPtr<mozIStorageService> ss;
20844 0 : if (NS_WARN_IF(!(ss = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID)))) {
20845 0 : IDB_REPORT_INTERNAL_ERR();
20846 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
20847 : }
20848 : }
20849 :
20850 0 : const DatabaseMetadata& metadata = mCommonParams.metadata();
20851 :
20852 0 : QuotaManager::GetStorageId(metadata.persistenceType(),
20853 : mOrigin,
20854 : Client::IDB,
20855 0 : mDatabaseId);
20856 :
20857 0 : mDatabaseId.Append('*');
20858 0 : mDatabaseId.Append(NS_ConvertUTF16toUTF8(metadata.name()));
20859 :
20860 0 : if (permission == PermissionRequestBase::kPermissionPrompt) {
20861 0 : mState = State::PermissionChallenge;
20862 0 : MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
20863 0 : return NS_OK;
20864 : }
20865 :
20866 0 : MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed);
20867 :
20868 0 : mState = State::FinishOpen;
20869 0 : MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
20870 :
20871 0 : return NS_OK;
20872 : }
20873 :
20874 : nsresult
20875 0 : FactoryOp::ChallengePermission()
20876 : {
20877 0 : AssertIsOnOwningThread();
20878 0 : MOZ_ASSERT(mState == State::PermissionChallenge);
20879 :
20880 0 : const PrincipalInfo& principalInfo = mCommonParams.principalInfo();
20881 0 : MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
20882 :
20883 0 : if (NS_WARN_IF(!SendPermissionChallenge(principalInfo))) {
20884 0 : return NS_ERROR_FAILURE;
20885 : }
20886 :
20887 0 : return NS_OK;
20888 : }
20889 :
20890 : nsresult
20891 0 : FactoryOp::RetryCheckPermission()
20892 : {
20893 0 : MOZ_ASSERT(NS_IsMainThread());
20894 0 : MOZ_ASSERT(mState == State::PermissionRetry);
20895 0 : MOZ_ASSERT(mCommonParams.principalInfo().type() ==
20896 : PrincipalInfo::TContentPrincipalInfo);
20897 :
20898 : // Swap this to the stack now to ensure that we release it on this thread.
20899 0 : RefPtr<ContentParent> contentParent;
20900 0 : mContentParent.swap(contentParent);
20901 :
20902 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
20903 0 : !OperationMayProceed()) {
20904 0 : IDB_REPORT_INTERNAL_ERR();
20905 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
20906 : }
20907 :
20908 : PermissionRequestBase::PermissionValue permission;
20909 0 : nsresult rv = CheckPermission(contentParent, &permission);
20910 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20911 0 : return rv;
20912 : }
20913 :
20914 0 : MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
20915 : permission == PermissionRequestBase::kPermissionDenied ||
20916 : permission == PermissionRequestBase::kPermissionPrompt);
20917 :
20918 0 : if (permission == PermissionRequestBase::kPermissionDenied ||
20919 0 : permission == PermissionRequestBase::kPermissionPrompt) {
20920 0 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
20921 : }
20922 :
20923 0 : MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed);
20924 :
20925 0 : mState = State::FinishOpen;
20926 0 : MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
20927 :
20928 0 : return NS_OK;
20929 : }
20930 :
20931 : nsresult
20932 0 : FactoryOp::DirectoryOpen()
20933 : {
20934 0 : AssertIsOnOwningThread();
20935 0 : MOZ_ASSERT(mState == State::DirectoryOpenPending);
20936 0 : MOZ_ASSERT(mDirectoryLock);
20937 0 : MOZ_ASSERT(!mDatabaseFilePath.IsEmpty());
20938 :
20939 : // gFactoryOps could be null here if the child process crashed or something
20940 : // and that cleaned up the last Factory actor.
20941 0 : if (!gFactoryOps) {
20942 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
20943 : }
20944 :
20945 : // See if this FactoryOp needs to wait.
20946 0 : bool delayed = false;
20947 0 : for (uint32_t index = gFactoryOps->Length(); index > 0; index--) {
20948 0 : RefPtr<FactoryOp>& existingOp = (*gFactoryOps)[index - 1];
20949 0 : if (MustWaitFor(*existingOp)) {
20950 : // Only one op can be delayed.
20951 0 : MOZ_ASSERT(!existingOp->mDelayedOp);
20952 0 : existingOp->mDelayedOp = this;
20953 0 : delayed = true;
20954 0 : break;
20955 : }
20956 : }
20957 :
20958 : // Adding this to the factory ops list will block any additional ops from
20959 : // proceeding until this one is done.
20960 0 : gFactoryOps->AppendElement(this);
20961 :
20962 0 : if (!delayed) {
20963 0 : QuotaClient* quotaClient = QuotaClient::GetInstance();
20964 0 : MOZ_ASSERT(quotaClient);
20965 :
20966 0 : if (RefPtr<Maintenance> currentMaintenance =
20967 0 : quotaClient->GetCurrentMaintenance()) {
20968 0 : if (RefPtr<DatabaseMaintenance> databaseMaintenance =
20969 0 : currentMaintenance->GetDatabaseMaintenance(mDatabaseFilePath)) {
20970 0 : databaseMaintenance->WaitForCompletion(this);
20971 0 : delayed = true;
20972 : }
20973 : }
20974 : }
20975 :
20976 0 : mBlockedDatabaseOpen = true;
20977 :
20978 : // Balanced in FinishSendResults().
20979 0 : IncreaseBusyCount();
20980 :
20981 0 : mState = State::DatabaseOpenPending;
20982 0 : if (!delayed) {
20983 0 : nsresult rv = DatabaseOpen();
20984 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
20985 0 : return rv;
20986 : }
20987 : }
20988 :
20989 0 : return NS_OK;
20990 : }
20991 :
20992 : nsresult
20993 0 : FactoryOp::SendToIOThread()
20994 : {
20995 0 : AssertIsOnOwningThread();
20996 0 : MOZ_ASSERT(mState == State::DatabaseOpenPending);
20997 :
20998 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
20999 0 : !OperationMayProceed()) {
21000 0 : IDB_REPORT_INTERNAL_ERR();
21001 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
21002 : }
21003 :
21004 0 : QuotaManager* quotaManager = QuotaManager::Get();
21005 0 : MOZ_ASSERT(quotaManager);
21006 :
21007 : // Must set this before dispatching otherwise we will race with the IO thread.
21008 0 : mState = State::DatabaseWorkOpen;
21009 :
21010 0 : nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
21011 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21012 0 : IDB_REPORT_INTERNAL_ERR();
21013 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
21014 : }
21015 :
21016 0 : return NS_OK;
21017 : }
21018 :
21019 : void
21020 0 : FactoryOp::WaitForTransactions()
21021 : {
21022 0 : AssertIsOnOwningThread();
21023 0 : MOZ_ASSERT(mState == State::BeginVersionChange ||
21024 : mState == State::WaitingForOtherDatabasesToClose);
21025 0 : MOZ_ASSERT(!mDatabaseId.IsEmpty());
21026 0 : MOZ_ASSERT(!IsActorDestroyed());
21027 :
21028 0 : mState = State::WaitingForTransactionsToComplete;
21029 :
21030 : RefPtr<WaitForTransactionsHelper> helper =
21031 0 : new WaitForTransactionsHelper(mDatabaseId, this);
21032 0 : helper->WaitForTransactions();
21033 0 : }
21034 :
21035 : void
21036 0 : FactoryOp::FinishSendResults()
21037 : {
21038 0 : AssertIsOnOwningThread();
21039 0 : MOZ_ASSERT(mState == State::SendingResults);
21040 0 : MOZ_ASSERT(mFactory);
21041 :
21042 : // Make sure to release the factory on this thread.
21043 0 : RefPtr<Factory> factory;
21044 0 : mFactory.swap(factory);
21045 :
21046 0 : if (mBlockedDatabaseOpen) {
21047 0 : if (mDelayedOp) {
21048 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mDelayedOp.forget()));
21049 : }
21050 :
21051 0 : MOZ_ASSERT(gFactoryOps);
21052 0 : gFactoryOps->RemoveElement(this);
21053 :
21054 : // Match the IncreaseBusyCount in DirectoryOpen().
21055 0 : DecreaseBusyCount();
21056 : }
21057 :
21058 0 : mState = State::Completed;
21059 0 : }
21060 :
21061 : nsresult
21062 0 : FactoryOp::CheckPermission(ContentParent* aContentParent,
21063 : PermissionRequestBase::PermissionValue* aPermission)
21064 : {
21065 0 : MOZ_ASSERT(NS_IsMainThread());
21066 0 : MOZ_ASSERT(mState == State::Initial || mState == State::PermissionRetry);
21067 :
21068 0 : const PrincipalInfo& principalInfo = mCommonParams.principalInfo();
21069 0 : if (principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo &&
21070 0 : NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) {
21071 0 : if (aContentParent) {
21072 : // The DOM in the other process should have kept us from receiving any
21073 : // indexedDB messages so assume that the child is misbehaving.
21074 0 : aContentParent->KillHard("IndexedDB CheckPermission 1");
21075 : }
21076 0 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
21077 : }
21078 :
21079 0 : if (NS_WARN_IF(mCommonParams.privateBrowsingMode())) {
21080 : // XXX This is only temporary.
21081 0 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
21082 : }
21083 :
21084 0 : mFileHandleDisabled = !Preferences::GetBool(kPrefFileHandleEnabled);
21085 :
21086 0 : PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
21087 :
21088 0 : MOZ_ASSERT(principalInfo.type() != PrincipalInfo::TNullPrincipalInfo);
21089 :
21090 0 : if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
21091 0 : MOZ_ASSERT(mState == State::Initial);
21092 0 : MOZ_ASSERT(persistenceType == PERSISTENCE_TYPE_PERSISTENT);
21093 :
21094 0 : if (aContentParent) {
21095 : // Check to make sure that the child process has access to the database it
21096 : // is accessing.
21097 0 : NS_NAMED_LITERAL_CSTRING(permissionStringBase,
21098 : PERMISSION_STRING_CHROME_BASE);
21099 0 : NS_ConvertUTF16toUTF8 databaseName(mCommonParams.metadata().name());
21100 0 : NS_NAMED_LITERAL_CSTRING(readSuffix, PERMISSION_STRING_CHROME_READ_SUFFIX);
21101 0 : NS_NAMED_LITERAL_CSTRING(writeSuffix, PERMISSION_STRING_CHROME_WRITE_SUFFIX);
21102 :
21103 : const nsAutoCString permissionStringWrite =
21104 0 : permissionStringBase + databaseName + writeSuffix;
21105 : const nsAutoCString permissionStringRead =
21106 0 : permissionStringBase + databaseName + readSuffix;
21107 :
21108 : bool canWrite =
21109 0 : CheckAtLeastOneAppHasPermission(aContentParent, permissionStringWrite);
21110 :
21111 : bool canRead;
21112 0 : if (canWrite) {
21113 0 : MOZ_ASSERT(CheckAtLeastOneAppHasPermission(aContentParent,
21114 : permissionStringRead));
21115 0 : canRead = true;
21116 : } else {
21117 : canRead =
21118 0 : CheckAtLeastOneAppHasPermission(aContentParent, permissionStringRead);
21119 : }
21120 :
21121 : // Deleting a database requires write permissions.
21122 0 : if (mDeleting && !canWrite) {
21123 0 : aContentParent->KillHard("IndexedDB CheckPermission 2");
21124 0 : IDB_REPORT_INTERNAL_ERR();
21125 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
21126 : }
21127 :
21128 : // Opening or deleting requires read permissions.
21129 0 : if (!canRead) {
21130 0 : aContentParent->KillHard("IndexedDB CheckPermission 3");
21131 0 : IDB_REPORT_INTERNAL_ERR();
21132 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
21133 : }
21134 :
21135 0 : mChromeWriteAccessAllowed = canWrite;
21136 : } else {
21137 0 : mChromeWriteAccessAllowed = true;
21138 : }
21139 :
21140 0 : if (State::Initial == mState) {
21141 0 : QuotaManager::GetInfoForChrome(&mSuffix, &mGroup, &mOrigin);
21142 :
21143 0 : MOZ_ASSERT(QuotaManager::IsOriginInternal(mOrigin));
21144 :
21145 0 : mEnforcingQuota = false;
21146 : }
21147 :
21148 0 : *aPermission = PermissionRequestBase::kPermissionAllowed;
21149 0 : return NS_OK;
21150 : }
21151 :
21152 0 : MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
21153 :
21154 : nsresult rv;
21155 : nsCOMPtr<nsIPrincipal> principal =
21156 0 : PrincipalInfoToPrincipal(principalInfo, &rv);
21157 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21158 0 : return rv;
21159 : }
21160 :
21161 0 : nsCString suffix;
21162 0 : nsCString group;
21163 0 : nsCString origin;
21164 0 : rv = QuotaManager::GetInfoFromPrincipal(principal,
21165 : &suffix,
21166 : &group,
21167 : &origin);
21168 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21169 0 : return rv;
21170 : }
21171 :
21172 : PermissionRequestBase::PermissionValue permission;
21173 :
21174 0 : if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
21175 0 : if (QuotaManager::IsOriginInternal(origin)) {
21176 0 : permission = PermissionRequestBase::kPermissionAllowed;
21177 : } else {
21178 : #ifdef IDB_MOBILE
21179 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
21180 : #else
21181 0 : rv = PermissionRequestBase::GetCurrentPermission(principal, &permission);
21182 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21183 0 : return rv;
21184 : }
21185 : #endif
21186 : }
21187 : } else {
21188 0 : permission = PermissionRequestBase::kPermissionAllowed;
21189 : }
21190 :
21191 0 : if (permission != PermissionRequestBase::kPermissionDenied &&
21192 0 : State::Initial == mState) {
21193 0 : mSuffix = suffix;
21194 0 : mGroup = group;
21195 0 : mOrigin = origin;
21196 :
21197 0 : mEnforcingQuota = persistenceType != PERSISTENCE_TYPE_PERSISTENT;
21198 : }
21199 :
21200 0 : *aPermission = permission;
21201 0 : return NS_OK;
21202 : }
21203 :
21204 : nsresult
21205 0 : FactoryOp::SendVersionChangeMessages(DatabaseActorInfo* aDatabaseActorInfo,
21206 : Database* aOpeningDatabase,
21207 : uint64_t aOldVersion,
21208 : const NullableVersion& aNewVersion)
21209 : {
21210 0 : AssertIsOnOwningThread();
21211 0 : MOZ_ASSERT(aDatabaseActorInfo);
21212 0 : MOZ_ASSERT(mState == State::BeginVersionChange);
21213 0 : MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
21214 0 : MOZ_ASSERT(!IsActorDestroyed());
21215 :
21216 0 : const uint32_t expectedCount = mDeleting ? 0 : 1;
21217 0 : const uint32_t liveCount = aDatabaseActorInfo->mLiveDatabases.Length();
21218 0 : if (liveCount > expectedCount) {
21219 0 : FallibleTArray<MaybeBlockedDatabaseInfo> maybeBlockedDatabases;
21220 0 : for (uint32_t index = 0; index < liveCount; index++) {
21221 0 : Database* database = aDatabaseActorInfo->mLiveDatabases[index];
21222 0 : if ((!aOpeningDatabase || database != aOpeningDatabase) &&
21223 0 : !database->IsClosed() &&
21224 0 : NS_WARN_IF(!maybeBlockedDatabases.AppendElement(database, fallible))) {
21225 0 : return NS_ERROR_OUT_OF_MEMORY;
21226 : }
21227 : }
21228 :
21229 0 : if (!maybeBlockedDatabases.IsEmpty()) {
21230 0 : mMaybeBlockedDatabases.SwapElements(maybeBlockedDatabases);
21231 : }
21232 : }
21233 :
21234 0 : if (!mMaybeBlockedDatabases.IsEmpty()) {
21235 0 : for (uint32_t count = mMaybeBlockedDatabases.Length(), index = 0;
21236 0 : index < count;
21237 : /* incremented conditionally */) {
21238 0 : if (mMaybeBlockedDatabases[index]->SendVersionChange(aOldVersion,
21239 : aNewVersion)) {
21240 0 : index++;
21241 : } else {
21242 : // We don't want to wait forever if we were not able to send the
21243 : // message.
21244 0 : mMaybeBlockedDatabases.RemoveElementAt(index);
21245 0 : count--;
21246 : }
21247 : }
21248 : }
21249 :
21250 0 : return NS_OK;
21251 : }
21252 :
21253 : // static
21254 : bool
21255 0 : FactoryOp::CheckAtLeastOneAppHasPermission(ContentParent* aContentParent,
21256 : const nsACString& aPermissionString)
21257 : {
21258 0 : MOZ_ASSERT(NS_IsMainThread());
21259 0 : MOZ_ASSERT(aContentParent);
21260 0 : MOZ_ASSERT(!aPermissionString.IsEmpty());
21261 :
21262 0 : return true;
21263 : }
21264 :
21265 : nsresult
21266 0 : FactoryOp::FinishOpen()
21267 : {
21268 0 : AssertIsOnOwningThread();
21269 0 : MOZ_ASSERT(mState == State::FinishOpen);
21270 0 : MOZ_ASSERT(!mContentParent);
21271 :
21272 0 : if (QuotaManager::Get()) {
21273 0 : nsresult rv = OpenDirectory();
21274 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21275 0 : return rv;
21276 : }
21277 :
21278 0 : return NS_OK;
21279 : }
21280 :
21281 0 : mState = State::QuotaManagerPending;
21282 0 : QuotaManager::GetOrCreate(this);
21283 :
21284 0 : return NS_OK;
21285 : }
21286 :
21287 : nsresult
21288 0 : FactoryOp::QuotaManagerOpen()
21289 : {
21290 0 : AssertIsOnOwningThread();
21291 0 : MOZ_ASSERT(mState == State::QuotaManagerPending);
21292 :
21293 0 : if (NS_WARN_IF(!QuotaManager::Get())) {
21294 0 : return NS_ERROR_FAILURE;
21295 : }
21296 :
21297 0 : nsresult rv = OpenDirectory();
21298 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21299 0 : return rv;
21300 : }
21301 :
21302 0 : return NS_OK;
21303 : }
21304 :
21305 : nsresult
21306 0 : FactoryOp::OpenDirectory()
21307 : {
21308 0 : AssertIsOnOwningThread();
21309 0 : MOZ_ASSERT(mState == State::FinishOpen ||
21310 : mState == State::QuotaManagerPending);
21311 0 : MOZ_ASSERT(!mOrigin.IsEmpty());
21312 0 : MOZ_ASSERT(!mDirectoryLock);
21313 0 : MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
21314 0 : MOZ_ASSERT(QuotaManager::Get());
21315 :
21316 : // Need to get database file path in advance.
21317 0 : const nsString& databaseName = mCommonParams.metadata().name();
21318 0 : PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
21319 :
21320 0 : QuotaManager* quotaManager = QuotaManager::Get();
21321 0 : MOZ_ASSERT(quotaManager);
21322 :
21323 0 : nsCOMPtr<nsIFile> dbFile;
21324 0 : nsresult rv = quotaManager->GetDirectoryForOrigin(persistenceType,
21325 : mOrigin,
21326 0 : getter_AddRefs(dbFile));
21327 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21328 0 : return rv;
21329 : }
21330 :
21331 0 : rv = dbFile->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
21332 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21333 0 : return rv;
21334 : }
21335 :
21336 0 : nsAutoString filename;
21337 0 : GetDatabaseFilename(databaseName, filename);
21338 :
21339 0 : rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
21340 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21341 0 : return rv;
21342 : }
21343 :
21344 0 : rv = dbFile->GetPath(mDatabaseFilePath);
21345 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21346 0 : return rv;
21347 : }
21348 :
21349 0 : mState = State::DirectoryOpenPending;
21350 :
21351 0 : quotaManager->OpenDirectory(persistenceType,
21352 : mGroup,
21353 : mOrigin,
21354 : Client::IDB,
21355 : /* aExclusive */ false,
21356 0 : this);
21357 :
21358 0 : return NS_OK;
21359 : }
21360 :
21361 : bool
21362 0 : FactoryOp::MustWaitFor(const FactoryOp& aExistingOp)
21363 : {
21364 0 : AssertIsOnOwningThread();
21365 :
21366 : // Things for the same persistence type, the same origin and the same
21367 : // database must wait.
21368 0 : return aExistingOp.mCommonParams.metadata().persistenceType() ==
21369 0 : mCommonParams.metadata().persistenceType() &&
21370 0 : aExistingOp.mOrigin == mOrigin &&
21371 0 : aExistingOp.mDatabaseId == mDatabaseId;
21372 : }
21373 :
21374 : void
21375 0 : FactoryOp::NoteDatabaseBlocked(Database* aDatabase)
21376 : {
21377 0 : AssertIsOnOwningThread();
21378 0 : MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose);
21379 0 : MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty());
21380 0 : MOZ_ASSERT(mMaybeBlockedDatabases.Contains(aDatabase));
21381 :
21382 : // Only send the blocked event if all databases have reported back. If the
21383 : // database was closed then it will have been removed from the array.
21384 : // Otherwise if it was blocked its |mBlocked| flag will be true.
21385 0 : bool sendBlockedEvent = true;
21386 :
21387 0 : for (uint32_t count = mMaybeBlockedDatabases.Length(), index = 0;
21388 0 : index < count;
21389 : index++) {
21390 0 : MaybeBlockedDatabaseInfo& info = mMaybeBlockedDatabases[index];
21391 0 : if (info == aDatabase) {
21392 : // This database was blocked, mark accordingly.
21393 0 : info.mBlocked = true;
21394 0 : } else if (!info.mBlocked) {
21395 : // A database has not yet reported back yet, don't send the event yet.
21396 0 : sendBlockedEvent = false;
21397 : }
21398 : }
21399 :
21400 0 : if (sendBlockedEvent) {
21401 0 : SendBlockedNotification();
21402 : }
21403 0 : }
21404 :
21405 0 : NS_IMPL_ISUPPORTS_INHERITED0(FactoryOp, DatabaseOperationBase)
21406 :
21407 : // Run() assumes that the caller holds a strong reference to the object that
21408 : // can't be cleared while Run() is being executed.
21409 : // So if you call Run() directly (as opposed to dispatching to an event queue)
21410 : // you need to make sure there's such a reference.
21411 : // See bug 1356824 for more details.
21412 : NS_IMETHODIMP
21413 0 : FactoryOp::Run()
21414 : {
21415 : nsresult rv;
21416 :
21417 0 : switch (mState) {
21418 : case State::Initial:
21419 0 : rv = Open();
21420 0 : break;
21421 :
21422 : case State::PermissionChallenge:
21423 0 : rv = ChallengePermission();
21424 0 : break;
21425 :
21426 : case State::PermissionRetry:
21427 0 : rv = RetryCheckPermission();
21428 0 : break;
21429 :
21430 : case State::FinishOpen:
21431 0 : rv = FinishOpen();
21432 0 : break;
21433 :
21434 : case State::QuotaManagerPending:
21435 0 : rv = QuotaManagerOpen();
21436 0 : break;
21437 :
21438 : case State::DatabaseOpenPending:
21439 0 : rv = DatabaseOpen();
21440 0 : break;
21441 :
21442 : case State::DatabaseWorkOpen:
21443 0 : rv = DoDatabaseWork();
21444 0 : break;
21445 :
21446 : case State::BeginVersionChange:
21447 0 : rv = BeginVersionChange();
21448 0 : break;
21449 :
21450 : case State::WaitingForTransactionsToComplete:
21451 0 : rv = DispatchToWorkThread();
21452 0 : break;
21453 :
21454 : case State::SendingResults:
21455 0 : SendResults();
21456 0 : return NS_OK;
21457 :
21458 : default:
21459 0 : MOZ_CRASH("Bad state!");
21460 : }
21461 :
21462 0 : if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
21463 0 : if (NS_SUCCEEDED(mResultCode)) {
21464 0 : mResultCode = rv;
21465 : }
21466 :
21467 : // Must set mState before dispatching otherwise we will race with the owning
21468 : // thread.
21469 0 : mState = State::SendingResults;
21470 :
21471 0 : if (IsOnOwningThread()) {
21472 0 : SendResults();
21473 : } else {
21474 0 : MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
21475 : }
21476 : }
21477 :
21478 0 : return NS_OK;
21479 : }
21480 :
21481 : void
21482 0 : FactoryOp::DirectoryLockAcquired(DirectoryLock* aLock)
21483 : {
21484 0 : AssertIsOnOwningThread();
21485 0 : MOZ_ASSERT(mState == State::DirectoryOpenPending);
21486 0 : MOZ_ASSERT(!mDirectoryLock);
21487 :
21488 0 : mDirectoryLock = aLock;
21489 :
21490 0 : nsresult rv = DirectoryOpen();
21491 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21492 0 : if (NS_SUCCEEDED(mResultCode)) {
21493 0 : mResultCode = rv;
21494 : }
21495 :
21496 : // The caller holds a strong reference to us, no need for a self reference
21497 : // before calling Run().
21498 :
21499 0 : mState = State::SendingResults;
21500 0 : MOZ_ALWAYS_SUCCEEDS(Run());
21501 :
21502 0 : return;
21503 : }
21504 : }
21505 :
21506 : void
21507 0 : FactoryOp::DirectoryLockFailed()
21508 : {
21509 0 : AssertIsOnOwningThread();
21510 0 : MOZ_ASSERT(mState == State::DirectoryOpenPending);
21511 0 : MOZ_ASSERT(!mDirectoryLock);
21512 :
21513 0 : if (NS_SUCCEEDED(mResultCode)) {
21514 0 : IDB_REPORT_INTERNAL_ERR();
21515 0 : mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
21516 : }
21517 :
21518 : // The caller holds a strong reference to us, no need for a self reference
21519 : // before calling Run().
21520 :
21521 0 : mState = State::SendingResults;
21522 0 : MOZ_ALWAYS_SUCCEEDS(Run());
21523 0 : }
21524 :
21525 : void
21526 0 : FactoryOp::ActorDestroy(ActorDestroyReason aWhy)
21527 : {
21528 0 : AssertIsOnBackgroundThread();
21529 :
21530 0 : NoteActorDestroyed();
21531 0 : }
21532 :
21533 : mozilla::ipc::IPCResult
21534 0 : FactoryOp::RecvPermissionRetry()
21535 : {
21536 0 : AssertIsOnOwningThread();
21537 0 : MOZ_ASSERT(!IsActorDestroyed());
21538 0 : MOZ_ASSERT(mState == State::PermissionChallenge);
21539 :
21540 0 : mContentParent = BackgroundParent::GetContentParent(Manager()->Manager());
21541 :
21542 0 : mState = State::PermissionRetry;
21543 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
21544 :
21545 0 : return IPC_OK();
21546 : }
21547 :
21548 0 : OpenDatabaseOp::OpenDatabaseOp(Factory* aFactory,
21549 : already_AddRefed<ContentParent> aContentParent,
21550 0 : const CommonFactoryRequestParams& aParams)
21551 0 : : FactoryOp(aFactory, Move(aContentParent), aParams, /* aDeleting */ false)
21552 0 : , mMetadata(new FullDatabaseMetadata(aParams.metadata()))
21553 0 : , mRequestedVersion(aParams.metadata().version())
21554 : , mVersionChangeOp(nullptr)
21555 0 : , mTelemetryId(0)
21556 : {
21557 0 : if (mContentParent) {
21558 : // This is a little scary but it looks safe to call this off the main thread
21559 : // for now.
21560 0 : mOptionalContentParentId = Some(mContentParent->ChildID());
21561 : }
21562 0 : }
21563 :
21564 : void
21565 0 : OpenDatabaseOp::ActorDestroy(ActorDestroyReason aWhy)
21566 : {
21567 0 : AssertIsOnOwningThread();
21568 :
21569 0 : FactoryOp::ActorDestroy(aWhy);
21570 :
21571 0 : if (mVersionChangeOp) {
21572 0 : mVersionChangeOp->NoteActorDestroyed();
21573 : }
21574 0 : }
21575 :
21576 : nsresult
21577 0 : OpenDatabaseOp::DatabaseOpen()
21578 : {
21579 0 : AssertIsOnOwningThread();
21580 0 : MOZ_ASSERT(mState == State::DatabaseOpenPending);
21581 :
21582 0 : nsresult rv = SendToIOThread();
21583 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21584 0 : return rv;
21585 : }
21586 :
21587 0 : return NS_OK;
21588 : }
21589 :
21590 : nsresult
21591 0 : OpenDatabaseOp::DoDatabaseWork()
21592 : {
21593 0 : AssertIsOnIOThread();
21594 0 : MOZ_ASSERT(mState == State::DatabaseWorkOpen);
21595 :
21596 0 : AUTO_PROFILER_LABEL("OpenDatabaseOp::DoDatabaseWork", STORAGE);
21597 :
21598 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
21599 0 : !OperationMayProceed()) {
21600 0 : IDB_REPORT_INTERNAL_ERR();
21601 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
21602 : }
21603 :
21604 0 : const nsString& databaseName = mCommonParams.metadata().name();
21605 0 : PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
21606 :
21607 0 : QuotaManager* quotaManager = QuotaManager::Get();
21608 0 : MOZ_ASSERT(quotaManager);
21609 :
21610 0 : nsCOMPtr<nsIFile> dbDirectory;
21611 :
21612 : nsresult rv =
21613 0 : quotaManager->EnsureOriginIsInitialized(persistenceType,
21614 : mSuffix,
21615 : mGroup,
21616 : mOrigin,
21617 0 : getter_AddRefs(dbDirectory));
21618 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21619 0 : return rv;
21620 : }
21621 :
21622 0 : rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
21623 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21624 0 : return rv;
21625 : }
21626 :
21627 : bool exists;
21628 0 : rv = dbDirectory->Exists(&exists);
21629 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21630 0 : return rv;
21631 : }
21632 :
21633 0 : if (!exists) {
21634 0 : rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
21635 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21636 0 : return rv;
21637 : }
21638 : }
21639 : #ifdef DEBUG
21640 : else {
21641 : bool isDirectory;
21642 0 : MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)));
21643 0 : MOZ_ASSERT(isDirectory);
21644 : }
21645 : #endif
21646 :
21647 0 : nsAutoString filename;
21648 0 : GetDatabaseFilename(databaseName, filename);
21649 :
21650 0 : nsCOMPtr<nsIFile> dbFile;
21651 0 : rv = dbDirectory->Clone(getter_AddRefs(dbFile));
21652 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21653 0 : return rv;
21654 : }
21655 :
21656 0 : rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
21657 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21658 0 : return rv;
21659 : }
21660 :
21661 0 : mTelemetryId = TelemetryIdForFile(dbFile);
21662 :
21663 : #ifdef DEBUG
21664 0 : nsString databaseFilePath;
21665 0 : rv = dbFile->GetPath(databaseFilePath);
21666 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21667 0 : return rv;
21668 : }
21669 :
21670 0 : MOZ_ASSERT(databaseFilePath == mDatabaseFilePath);
21671 : #endif
21672 :
21673 0 : nsCOMPtr<nsIFile> fmDirectory;
21674 0 : rv = dbDirectory->Clone(getter_AddRefs(fmDirectory));
21675 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21676 0 : return rv;
21677 : }
21678 :
21679 0 : const NS_ConvertASCIItoUTF16 filesSuffix(kFileManagerDirectoryNameSuffix);
21680 :
21681 0 : rv = fmDirectory->Append(filename + filesSuffix);
21682 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21683 0 : return rv;
21684 : }
21685 :
21686 0 : nsCOMPtr<mozIStorageConnection> connection;
21687 0 : rv = CreateStorageConnection(dbFile,
21688 : fmDirectory,
21689 : databaseName,
21690 : persistenceType,
21691 : mGroup,
21692 : mOrigin,
21693 : mTelemetryId,
21694 0 : getter_AddRefs(connection));
21695 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21696 0 : return rv;
21697 : }
21698 :
21699 0 : AutoSetProgressHandler asph;
21700 0 : rv = asph.Register(connection, this);
21701 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21702 0 : return rv;
21703 : }
21704 :
21705 0 : rv = LoadDatabaseInformation(connection);
21706 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21707 0 : return rv;
21708 : }
21709 :
21710 0 : MOZ_ASSERT(mMetadata->mNextObjectStoreId > mMetadata->mObjectStores.Count());
21711 0 : MOZ_ASSERT(mMetadata->mNextIndexId > 0);
21712 :
21713 : // See if we need to do a versionchange transaction
21714 :
21715 : // Optional version semantics.
21716 0 : if (!mRequestedVersion) {
21717 : // If the requested version was not specified and the database was created,
21718 : // treat it as if version 1 were requested.
21719 0 : if (mMetadata->mCommonMetadata.version() == 0) {
21720 0 : mRequestedVersion = 1;
21721 : } else {
21722 : // Otherwise, treat it as if the current version were requested.
21723 0 : mRequestedVersion = mMetadata->mCommonMetadata.version();
21724 : }
21725 : }
21726 :
21727 0 : if (NS_WARN_IF(mMetadata->mCommonMetadata.version() > mRequestedVersion)) {
21728 0 : return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR;
21729 : }
21730 :
21731 0 : IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
21732 0 : MOZ_ASSERT(mgr);
21733 :
21734 : RefPtr<FileManager> fileManager =
21735 0 : mgr->GetFileManager(persistenceType, mOrigin, databaseName);
21736 0 : if (!fileManager) {
21737 : fileManager = new FileManager(persistenceType,
21738 : mGroup,
21739 : mOrigin,
21740 : databaseName,
21741 0 : mEnforcingQuota);
21742 :
21743 0 : rv = fileManager->Init(fmDirectory, connection);
21744 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21745 0 : return rv;
21746 : }
21747 :
21748 0 : mgr->AddFileManager(fileManager);
21749 : }
21750 :
21751 0 : mFileManager = fileManager.forget();
21752 :
21753 : // Must set mState before dispatching otherwise we will race with the owning
21754 : // thread.
21755 0 : mState = (mMetadata->mCommonMetadata.version() == mRequestedVersion) ?
21756 : State::SendingResults :
21757 : State::BeginVersionChange;
21758 :
21759 0 : rv = mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
21760 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21761 0 : return rv;
21762 : }
21763 :
21764 0 : return NS_OK;
21765 : }
21766 :
21767 : nsresult
21768 0 : OpenDatabaseOp::LoadDatabaseInformation(mozIStorageConnection* aConnection)
21769 : {
21770 0 : AssertIsOnIOThread();
21771 0 : MOZ_ASSERT(aConnection);
21772 0 : MOZ_ASSERT(mMetadata);
21773 :
21774 : // Load version information.
21775 0 : nsCOMPtr<mozIStorageStatement> stmt;
21776 0 : nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
21777 : "SELECT name, origin, version "
21778 : "FROM database"
21779 0 : ), getter_AddRefs(stmt));
21780 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21781 0 : return rv;
21782 : }
21783 :
21784 : bool hasResult;
21785 0 : rv = stmt->ExecuteStep(&hasResult);
21786 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21787 0 : return rv;
21788 : }
21789 :
21790 0 : if (NS_WARN_IF(!hasResult)) {
21791 0 : return NS_ERROR_FILE_CORRUPTED;
21792 : }
21793 :
21794 0 : nsString databaseName;
21795 0 : rv = stmt->GetString(0, databaseName);
21796 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21797 0 : return rv;
21798 : }
21799 :
21800 0 : if (NS_WARN_IF(mCommonParams.metadata().name() != databaseName)) {
21801 0 : return NS_ERROR_FILE_CORRUPTED;
21802 : }
21803 :
21804 0 : nsCString origin;
21805 0 : rv = stmt->GetUTF8String(1, origin);
21806 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21807 0 : return rv;
21808 : }
21809 :
21810 : // We can't just compare these strings directly. See bug 1339081 comment 69.
21811 0 : if (NS_WARN_IF(!QuotaManager::AreOriginsEqualOnDisk(mOrigin, origin))) {
21812 0 : return NS_ERROR_FILE_CORRUPTED;
21813 : }
21814 :
21815 : int64_t version;
21816 0 : rv = stmt->GetInt64(2, &version);
21817 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21818 0 : return rv;
21819 : }
21820 :
21821 0 : mMetadata->mCommonMetadata.version() = uint64_t(version);
21822 :
21823 0 : ObjectStoreTable& objectStores = mMetadata->mObjectStores;
21824 :
21825 : // Load object store names and ids.
21826 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
21827 : "SELECT id, auto_increment, name, key_path "
21828 : "FROM object_store"
21829 0 : ), getter_AddRefs(stmt));
21830 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21831 0 : return rv;
21832 : }
21833 :
21834 0 : Maybe<nsTHashtable<nsUint64HashKey>> usedIds;
21835 0 : Maybe<nsTHashtable<nsStringHashKey>> usedNames;
21836 :
21837 0 : int64_t lastObjectStoreId = 0;
21838 :
21839 0 : while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
21840 : int64_t objectStoreId;
21841 0 : rv = stmt->GetInt64(0, &objectStoreId);
21842 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21843 0 : return rv;
21844 : }
21845 :
21846 0 : if (!usedIds) {
21847 0 : usedIds.emplace();
21848 : }
21849 :
21850 0 : if (NS_WARN_IF(objectStoreId <= 0) ||
21851 0 : NS_WARN_IF(usedIds.ref().Contains(objectStoreId))) {
21852 0 : return NS_ERROR_FILE_CORRUPTED;
21853 : }
21854 :
21855 0 : if (NS_WARN_IF(!usedIds.ref().PutEntry(objectStoreId, fallible))) {
21856 0 : return NS_ERROR_OUT_OF_MEMORY;
21857 : }
21858 :
21859 0 : nsString name;
21860 0 : rv = stmt->GetString(2, name);
21861 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21862 0 : return rv;
21863 : }
21864 :
21865 0 : if (!usedNames) {
21866 0 : usedNames.emplace();
21867 : }
21868 :
21869 0 : if (NS_WARN_IF(usedNames.ref().Contains(name))) {
21870 0 : return NS_ERROR_FILE_CORRUPTED;
21871 : }
21872 :
21873 0 : if (NS_WARN_IF(!usedNames.ref().PutEntry(name, fallible))) {
21874 0 : return NS_ERROR_OUT_OF_MEMORY;
21875 : }
21876 :
21877 0 : RefPtr<FullObjectStoreMetadata> metadata = new FullObjectStoreMetadata();
21878 0 : metadata->mCommonMetadata.id() = objectStoreId;
21879 0 : metadata->mCommonMetadata.name() = name;
21880 :
21881 : int32_t columnType;
21882 0 : rv = stmt->GetTypeOfIndex(3, &columnType);
21883 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21884 0 : return rv;
21885 : }
21886 :
21887 0 : if (columnType == mozIStorageStatement::VALUE_TYPE_NULL) {
21888 0 : metadata->mCommonMetadata.keyPath() = KeyPath(0);
21889 : } else {
21890 0 : MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_TEXT);
21891 :
21892 0 : nsString keyPathSerialization;
21893 0 : rv = stmt->GetString(3, keyPathSerialization);
21894 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21895 0 : return rv;
21896 : }
21897 :
21898 0 : metadata->mCommonMetadata.keyPath() =
21899 0 : KeyPath::DeserializeFromString(keyPathSerialization);
21900 0 : if (NS_WARN_IF(!metadata->mCommonMetadata.keyPath().IsValid())) {
21901 0 : return NS_ERROR_FILE_CORRUPTED;
21902 : }
21903 : }
21904 :
21905 : int64_t nextAutoIncrementId;
21906 0 : rv = stmt->GetInt64(1, &nextAutoIncrementId);
21907 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21908 0 : return rv;
21909 : }
21910 :
21911 0 : metadata->mCommonMetadata.autoIncrement() = !!nextAutoIncrementId;
21912 0 : metadata->mNextAutoIncrementId = nextAutoIncrementId;
21913 0 : metadata->mCommittedAutoIncrementId = nextAutoIncrementId;
21914 :
21915 0 : if (NS_WARN_IF(!objectStores.Put(objectStoreId, metadata, fallible))) {
21916 0 : return NS_ERROR_OUT_OF_MEMORY;
21917 : }
21918 :
21919 0 : lastObjectStoreId = std::max(lastObjectStoreId, objectStoreId);
21920 : }
21921 :
21922 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21923 0 : return rv;
21924 : }
21925 :
21926 0 : usedIds.reset();
21927 0 : usedNames.reset();
21928 :
21929 : // Load index information
21930 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
21931 : "SELECT "
21932 : "id, object_store_id, name, key_path, unique_index, multientry, "
21933 : "locale, is_auto_locale "
21934 : "FROM object_store_index"
21935 0 : ), getter_AddRefs(stmt));
21936 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21937 0 : return rv;
21938 : }
21939 :
21940 0 : int64_t lastIndexId = 0;
21941 :
21942 0 : while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
21943 : int64_t objectStoreId;
21944 0 : rv = stmt->GetInt64(1, &objectStoreId);
21945 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21946 0 : return rv;
21947 : }
21948 :
21949 0 : RefPtr<FullObjectStoreMetadata> objectStoreMetadata;
21950 0 : if (NS_WARN_IF(!objectStores.Get(objectStoreId,
21951 : getter_AddRefs(objectStoreMetadata)))) {
21952 0 : return NS_ERROR_FILE_CORRUPTED;
21953 : }
21954 :
21955 0 : MOZ_ASSERT(objectStoreMetadata->mCommonMetadata.id() == objectStoreId);
21956 :
21957 : int64_t indexId;
21958 0 : rv = stmt->GetInt64(0, &indexId);
21959 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21960 0 : return rv;
21961 : }
21962 :
21963 0 : if (!usedIds) {
21964 0 : usedIds.emplace();
21965 : }
21966 :
21967 0 : if (NS_WARN_IF(indexId <= 0) ||
21968 0 : NS_WARN_IF(usedIds.ref().Contains(indexId))) {
21969 0 : return NS_ERROR_FILE_CORRUPTED;
21970 : }
21971 :
21972 0 : if (NS_WARN_IF(!usedIds.ref().PutEntry(indexId, fallible))) {
21973 0 : return NS_ERROR_OUT_OF_MEMORY;
21974 : }
21975 :
21976 0 : nsString name;
21977 0 : rv = stmt->GetString(2, name);
21978 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
21979 0 : return rv;
21980 : }
21981 :
21982 0 : nsAutoString hashName;
21983 0 : hashName.AppendInt(indexId);
21984 0 : hashName.Append(':');
21985 0 : hashName.Append(name);
21986 :
21987 0 : if (!usedNames) {
21988 0 : usedNames.emplace();
21989 : }
21990 :
21991 0 : if (NS_WARN_IF(usedNames.ref().Contains(hashName))) {
21992 0 : return NS_ERROR_FILE_CORRUPTED;
21993 : }
21994 :
21995 0 : if (NS_WARN_IF(!usedNames.ref().PutEntry(hashName, fallible))) {
21996 0 : return NS_ERROR_OUT_OF_MEMORY;
21997 : }
21998 :
21999 0 : RefPtr<FullIndexMetadata> indexMetadata = new FullIndexMetadata();
22000 0 : indexMetadata->mCommonMetadata.id() = indexId;
22001 0 : indexMetadata->mCommonMetadata.name() = name;
22002 :
22003 : #ifdef DEBUG
22004 : {
22005 : int32_t columnType;
22006 0 : rv = stmt->GetTypeOfIndex(3, &columnType);
22007 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
22008 0 : MOZ_ASSERT(columnType != mozIStorageStatement::VALUE_TYPE_NULL);
22009 : }
22010 : #endif
22011 :
22012 0 : nsString keyPathSerialization;
22013 0 : rv = stmt->GetString(3, keyPathSerialization);
22014 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22015 0 : return rv;
22016 : }
22017 :
22018 0 : indexMetadata->mCommonMetadata.keyPath() =
22019 0 : KeyPath::DeserializeFromString(keyPathSerialization);
22020 0 : if (NS_WARN_IF(!indexMetadata->mCommonMetadata.keyPath().IsValid())) {
22021 0 : return NS_ERROR_FILE_CORRUPTED;
22022 : }
22023 :
22024 : int32_t scratch;
22025 0 : rv = stmt->GetInt32(4, &scratch);
22026 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22027 0 : return rv;
22028 : }
22029 :
22030 0 : indexMetadata->mCommonMetadata.unique() = !!scratch;
22031 :
22032 0 : rv = stmt->GetInt32(5, &scratch);
22033 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22034 0 : return rv;
22035 : }
22036 :
22037 0 : indexMetadata->mCommonMetadata.multiEntry() = !!scratch;
22038 :
22039 : #ifdef ENABLE_INTL_API
22040 0 : const bool localeAware = !stmt->IsNull(6);
22041 0 : if (localeAware) {
22042 0 : rv = stmt->GetUTF8String(6, indexMetadata->mCommonMetadata.locale());
22043 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22044 0 : return rv;
22045 : }
22046 :
22047 0 : rv = stmt->GetInt32(7, &scratch);
22048 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22049 0 : return rv;
22050 : }
22051 :
22052 0 : indexMetadata->mCommonMetadata.autoLocale() = !!scratch;
22053 :
22054 : // Update locale-aware indexes if necessary
22055 0 : const nsCString& indexedLocale = indexMetadata->mCommonMetadata.locale();
22056 0 : const bool& isAutoLocale = indexMetadata->mCommonMetadata.autoLocale();
22057 0 : nsCString systemLocale = IndexedDatabaseManager::GetLocale();
22058 0 : if (!systemLocale.IsEmpty() &&
22059 0 : isAutoLocale &&
22060 0 : !indexedLocale.EqualsASCII(systemLocale.get())) {
22061 0 : rv = UpdateLocaleAwareIndex(aConnection,
22062 0 : indexMetadata->mCommonMetadata,
22063 0 : systemLocale);
22064 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22065 0 : return rv;
22066 : }
22067 : }
22068 : }
22069 : #endif
22070 :
22071 0 : if (NS_WARN_IF(!objectStoreMetadata->mIndexes.Put(indexId, indexMetadata,
22072 : fallible))) {
22073 0 : return NS_ERROR_OUT_OF_MEMORY;
22074 : }
22075 :
22076 0 : lastIndexId = std::max(lastIndexId, indexId);
22077 : }
22078 :
22079 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22080 0 : return rv;
22081 : }
22082 :
22083 0 : if (NS_WARN_IF(lastObjectStoreId == INT64_MAX) ||
22084 0 : NS_WARN_IF(lastIndexId == INT64_MAX)) {
22085 0 : IDB_REPORT_INTERNAL_ERR();
22086 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22087 : }
22088 :
22089 0 : mMetadata->mNextObjectStoreId = lastObjectStoreId + 1;
22090 0 : mMetadata->mNextIndexId = lastIndexId + 1;
22091 :
22092 0 : return NS_OK;
22093 : }
22094 :
22095 : #ifdef ENABLE_INTL_API
22096 : /* static */
22097 : nsresult
22098 0 : OpenDatabaseOp::UpdateLocaleAwareIndex(mozIStorageConnection* aConnection,
22099 : const IndexMetadata& aIndexMetadata,
22100 : const nsCString& aLocale)
22101 : {
22102 : nsresult rv;
22103 :
22104 0 : nsCString indexTable;
22105 0 : if (aIndexMetadata.unique()) {
22106 0 : indexTable.AssignLiteral("unique_index_data");
22107 : }
22108 : else {
22109 0 : indexTable.AssignLiteral("index_data");
22110 : }
22111 :
22112 0 : nsCString readQuery = NS_LITERAL_CSTRING("SELECT value, object_data_key FROM ") +
22113 0 : indexTable +
22114 0 : NS_LITERAL_CSTRING(" WHERE index_id = :index_id");
22115 0 : nsCOMPtr<mozIStorageStatement> readStmt;
22116 0 : rv = aConnection->CreateStatement(readQuery, getter_AddRefs(readStmt));
22117 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22118 0 : return rv;
22119 : }
22120 :
22121 0 : rv = readStmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
22122 0 : aIndexMetadata.id());
22123 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22124 0 : return rv;
22125 : }
22126 :
22127 0 : nsCOMPtr<mozIStorageStatement> writeStmt;
22128 0 : bool needCreateWriteQuery = true;
22129 : bool hasResult;
22130 0 : while (NS_SUCCEEDED((rv = readStmt->ExecuteStep(&hasResult))) && hasResult) {
22131 0 : if (needCreateWriteQuery) {
22132 0 : needCreateWriteQuery = false;
22133 0 : nsCString writeQuery = NS_LITERAL_CSTRING("UPDATE ") + indexTable +
22134 0 : NS_LITERAL_CSTRING("SET value_locale = :value_locale "
22135 : "WHERE index_id = :index_id AND "
22136 : "value = :value AND "
22137 : "object_data_key = :object_data_key");
22138 0 : rv = aConnection->CreateStatement(writeQuery, getter_AddRefs(writeStmt));
22139 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22140 0 : return rv;
22141 : }
22142 : }
22143 :
22144 0 : mozStorageStatementScoper scoper(writeStmt);
22145 0 : rv = writeStmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
22146 0 : aIndexMetadata.id());
22147 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22148 0 : return rv;
22149 : }
22150 :
22151 0 : Key oldKey, newSortKey, objectKey;
22152 0 : rv = oldKey.SetFromStatement(readStmt, 0);
22153 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22154 0 : return rv;
22155 : }
22156 :
22157 0 : rv = oldKey.BindToStatement(writeStmt, NS_LITERAL_CSTRING("value"));
22158 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22159 0 : return rv;
22160 : }
22161 :
22162 0 : rv = oldKey.ToLocaleBasedKey(newSortKey, aLocale);
22163 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22164 0 : return rv;
22165 : }
22166 :
22167 0 : rv = newSortKey.BindToStatement(writeStmt,
22168 0 : NS_LITERAL_CSTRING("value_locale"));
22169 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22170 0 : return rv;
22171 : }
22172 :
22173 0 : rv = objectKey.SetFromStatement(readStmt, 1);
22174 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22175 0 : return rv;
22176 : }
22177 :
22178 0 : rv = objectKey.BindToStatement(writeStmt,
22179 0 : NS_LITERAL_CSTRING("object_data_key"));
22180 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22181 0 : return rv;
22182 : }
22183 :
22184 0 : rv = writeStmt->Execute();
22185 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22186 0 : return rv;
22187 : }
22188 : }
22189 :
22190 0 : nsCString metaQuery = NS_LITERAL_CSTRING("UPDATE object_store_index SET "
22191 : "locale = :locale WHERE id = :id");
22192 0 : nsCOMPtr<mozIStorageStatement> metaStmt;
22193 0 : rv = aConnection->CreateStatement(metaQuery, getter_AddRefs(metaStmt));
22194 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22195 0 : return rv;
22196 : }
22197 :
22198 0 : nsString locale;
22199 0 : locale.AssignWithConversion(aLocale);
22200 0 : rv = metaStmt->BindStringByName(NS_LITERAL_CSTRING("locale"), locale);
22201 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22202 0 : return rv;
22203 : }
22204 :
22205 0 : rv = metaStmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aIndexMetadata.id());
22206 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22207 0 : return rv;
22208 : }
22209 :
22210 0 : rv = metaStmt->Execute();
22211 0 : return rv;
22212 : }
22213 : #endif
22214 :
22215 : nsresult
22216 0 : OpenDatabaseOp::BeginVersionChange()
22217 : {
22218 0 : AssertIsOnOwningThread();
22219 0 : MOZ_ASSERT(mState == State::BeginVersionChange);
22220 0 : MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
22221 0 : MOZ_ASSERT(mMetadata->mCommonMetadata.version() <= mRequestedVersion);
22222 0 : MOZ_ASSERT(!mDatabase);
22223 0 : MOZ_ASSERT(!mVersionChangeTransaction);
22224 :
22225 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
22226 0 : IsActorDestroyed()) {
22227 0 : IDB_REPORT_INTERNAL_ERR();
22228 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22229 : }
22230 :
22231 0 : EnsureDatabaseActor();
22232 :
22233 0 : if (mDatabase->IsInvalidated()) {
22234 0 : IDB_REPORT_INTERNAL_ERR();
22235 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22236 : }
22237 :
22238 0 : MOZ_ASSERT(!mDatabase->IsClosed());
22239 :
22240 : DatabaseActorInfo* info;
22241 0 : MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info));
22242 :
22243 0 : MOZ_ASSERT(info->mLiveDatabases.Contains(mDatabase));
22244 0 : MOZ_ASSERT(!info->mWaitingFactoryOp);
22245 0 : MOZ_ASSERT(info->mMetadata == mMetadata);
22246 :
22247 : RefPtr<VersionChangeTransaction> transaction =
22248 0 : new VersionChangeTransaction(this);
22249 :
22250 0 : if (NS_WARN_IF(!transaction->CopyDatabaseMetadata())) {
22251 0 : return NS_ERROR_OUT_OF_MEMORY;
22252 : }
22253 :
22254 0 : MOZ_ASSERT(info->mMetadata != mMetadata);
22255 0 : mMetadata = info->mMetadata;
22256 :
22257 0 : NullableVersion newVersion = mRequestedVersion;
22258 :
22259 : nsresult rv =
22260 0 : SendVersionChangeMessages(info,
22261 : mDatabase,
22262 0 : mMetadata->mCommonMetadata.version(),
22263 0 : newVersion);
22264 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22265 0 : return rv;
22266 : }
22267 :
22268 0 : mVersionChangeTransaction.swap(transaction);
22269 :
22270 0 : if (mMaybeBlockedDatabases.IsEmpty()) {
22271 : // We don't need to wait on any databases, just jump to the transaction
22272 : // pool.
22273 0 : WaitForTransactions();
22274 0 : return NS_OK;
22275 : }
22276 :
22277 0 : info->mWaitingFactoryOp = this;
22278 :
22279 0 : mState = State::WaitingForOtherDatabasesToClose;
22280 0 : return NS_OK;
22281 : }
22282 :
22283 : void
22284 0 : OpenDatabaseOp::NoteDatabaseClosed(Database* aDatabase)
22285 : {
22286 0 : AssertIsOnOwningThread();
22287 0 : MOZ_ASSERT(aDatabase);
22288 0 : MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose ||
22289 : mState == State::WaitingForTransactionsToComplete ||
22290 : mState == State::DatabaseWorkVersionChange);
22291 :
22292 0 : if (mState != State::WaitingForOtherDatabasesToClose) {
22293 0 : MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
22294 0 : MOZ_ASSERT(mRequestedVersion >
22295 : aDatabase->Metadata()->mCommonMetadata.version(),
22296 : "Must only be closing databases for a previous version!");
22297 0 : return;
22298 : }
22299 :
22300 0 : MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty());
22301 :
22302 0 : bool actorDestroyed = IsActorDestroyed() || mDatabase->IsActorDestroyed();
22303 :
22304 : nsresult rv;
22305 0 : if (actorDestroyed) {
22306 0 : IDB_REPORT_INTERNAL_ERR();
22307 0 : rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22308 : } else {
22309 0 : rv = NS_OK;
22310 : }
22311 :
22312 : // We are being called with an assumption that mWaitingFactoryOp holds
22313 : // a strong reference to us.
22314 0 : RefPtr<OpenDatabaseOp> kungFuDeathGrip;
22315 :
22316 0 : if (mMaybeBlockedDatabases.RemoveElement(aDatabase) &&
22317 0 : mMaybeBlockedDatabases.IsEmpty()) {
22318 0 : if (actorDestroyed) {
22319 : DatabaseActorInfo* info;
22320 0 : MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info));
22321 0 : MOZ_ASSERT(info->mWaitingFactoryOp == this);
22322 : kungFuDeathGrip =
22323 0 : static_cast<OpenDatabaseOp*>(info->mWaitingFactoryOp.get());
22324 0 : info->mWaitingFactoryOp = nullptr;
22325 : } else {
22326 0 : WaitForTransactions();
22327 : }
22328 : }
22329 :
22330 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22331 0 : if (NS_SUCCEEDED(mResultCode)) {
22332 0 : mResultCode = rv;
22333 : }
22334 :
22335 : // A strong reference is held in kungFuDeathGrip, so it's safe to call Run()
22336 : // directly.
22337 :
22338 0 : mState = State::SendingResults;
22339 0 : MOZ_ALWAYS_SUCCEEDS(Run());
22340 : }
22341 : }
22342 :
22343 : void
22344 0 : OpenDatabaseOp::SendBlockedNotification()
22345 : {
22346 0 : AssertIsOnOwningThread();
22347 0 : MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose);
22348 :
22349 0 : if (!IsActorDestroyed()) {
22350 0 : Unused << SendBlocked(mMetadata->mCommonMetadata.version());
22351 : }
22352 0 : }
22353 :
22354 : nsresult
22355 0 : OpenDatabaseOp::DispatchToWorkThread()
22356 : {
22357 0 : AssertIsOnOwningThread();
22358 0 : MOZ_ASSERT(mState == State::WaitingForTransactionsToComplete);
22359 0 : MOZ_ASSERT(mVersionChangeTransaction);
22360 0 : MOZ_ASSERT(mVersionChangeTransaction->GetMode() ==
22361 : IDBTransaction::VERSION_CHANGE);
22362 0 : MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
22363 :
22364 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
22365 0 : IsActorDestroyed() ||
22366 0 : mDatabase->IsInvalidated()) {
22367 0 : IDB_REPORT_INTERNAL_ERR();
22368 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22369 : }
22370 :
22371 0 : mState = State::DatabaseWorkVersionChange;
22372 :
22373 : // Intentionally empty.
22374 0 : nsTArray<nsString> objectStoreNames;
22375 :
22376 : const int64_t loggingSerialNumber =
22377 0 : mVersionChangeTransaction->LoggingSerialNumber();
22378 : const nsID& backgroundChildLoggingId =
22379 0 : mVersionChangeTransaction->GetLoggingInfo()->Id();
22380 :
22381 0 : if (NS_WARN_IF(!mDatabase->RegisterTransaction(mVersionChangeTransaction))) {
22382 0 : return NS_ERROR_OUT_OF_MEMORY;
22383 : }
22384 :
22385 0 : if (!gConnectionPool) {
22386 0 : gConnectionPool = new ConnectionPool();
22387 : }
22388 :
22389 0 : RefPtr<VersionChangeOp> versionChangeOp = new VersionChangeOp(this);
22390 :
22391 : uint64_t transactionId =
22392 0 : versionChangeOp->StartOnConnectionPool(
22393 : backgroundChildLoggingId,
22394 0 : mVersionChangeTransaction->DatabaseId(),
22395 : loggingSerialNumber,
22396 : objectStoreNames,
22397 0 : /* aIsWriteTransaction */ true);
22398 :
22399 0 : mVersionChangeOp = versionChangeOp;
22400 :
22401 0 : mVersionChangeTransaction->NoteActiveRequest();
22402 0 : mVersionChangeTransaction->SetActive(transactionId);
22403 :
22404 0 : return NS_OK;
22405 : }
22406 :
22407 : nsresult
22408 0 : OpenDatabaseOp::SendUpgradeNeeded()
22409 : {
22410 0 : AssertIsOnOwningThread();
22411 0 : MOZ_ASSERT(mState == State::DatabaseWorkVersionChange);
22412 0 : MOZ_ASSERT(mVersionChangeTransaction);
22413 0 : MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
22414 0 : MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
22415 0 : MOZ_ASSERT_IF(!IsActorDestroyed(), mDatabase);
22416 :
22417 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
22418 0 : IsActorDestroyed()) {
22419 0 : IDB_REPORT_INTERNAL_ERR();
22420 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22421 : }
22422 :
22423 0 : RefPtr<VersionChangeTransaction> transaction;
22424 0 : mVersionChangeTransaction.swap(transaction);
22425 :
22426 0 : nsresult rv = EnsureDatabaseActorIsAlive();
22427 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22428 0 : return rv;
22429 : }
22430 :
22431 : // Transfer ownership to IPDL.
22432 0 : transaction->SetActorAlive();
22433 :
22434 0 : if (!mDatabase->SendPBackgroundIDBVersionChangeTransactionConstructor(
22435 : transaction,
22436 0 : mMetadata->mCommonMetadata.version(),
22437 : mRequestedVersion,
22438 0 : mMetadata->mNextObjectStoreId,
22439 0 : mMetadata->mNextIndexId)) {
22440 0 : IDB_REPORT_INTERNAL_ERR();
22441 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22442 : }
22443 :
22444 0 : return NS_OK;
22445 : }
22446 :
22447 : void
22448 0 : OpenDatabaseOp::SendResults()
22449 : {
22450 0 : AssertIsOnOwningThread();
22451 0 : MOZ_ASSERT(mState == State::SendingResults);
22452 0 : MOZ_ASSERT_IF(NS_SUCCEEDED(mResultCode), mMaybeBlockedDatabases.IsEmpty());
22453 0 : MOZ_ASSERT_IF(NS_SUCCEEDED(mResultCode), !mVersionChangeTransaction);
22454 :
22455 0 : mMaybeBlockedDatabases.Clear();
22456 :
22457 : DatabaseActorInfo* info;
22458 0 : if (gLiveDatabaseHashtable &&
22459 0 : gLiveDatabaseHashtable->Get(mDatabaseId, &info) &&
22460 0 : info->mWaitingFactoryOp) {
22461 0 : MOZ_ASSERT(info->mWaitingFactoryOp == this);
22462 : // SendResults() should only be called by Run() and Run() should only be
22463 : // called if there's a strong reference to the object that can't be cleared
22464 : // here, so it's safe to clear mWaitingFactoryOp without adding additional
22465 : // strong reference.
22466 0 : info->mWaitingFactoryOp = nullptr;
22467 : }
22468 :
22469 0 : if (mVersionChangeTransaction) {
22470 0 : MOZ_ASSERT(NS_FAILED(mResultCode));
22471 :
22472 0 : mVersionChangeTransaction->Abort(mResultCode, /* aForce */ true);
22473 0 : mVersionChangeTransaction = nullptr;
22474 : }
22475 :
22476 0 : if (IsActorDestroyed()) {
22477 0 : if (NS_SUCCEEDED(mResultCode)) {
22478 0 : mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22479 : }
22480 : } else {
22481 0 : FactoryRequestResponse response;
22482 :
22483 0 : if (NS_SUCCEEDED(mResultCode)) {
22484 : // If we just successfully completed a versionchange operation then we
22485 : // need to update the version in our metadata.
22486 0 : mMetadata->mCommonMetadata.version() = mRequestedVersion;
22487 :
22488 0 : nsresult rv = EnsureDatabaseActorIsAlive();
22489 0 : if (NS_SUCCEEDED(rv)) {
22490 : // We successfully opened a database so use its actor as the success
22491 : // result for this request.
22492 0 : OpenDatabaseRequestResponse openResponse;
22493 0 : openResponse.databaseParent() = mDatabase;
22494 0 : response = openResponse;
22495 : } else {
22496 0 : response = ClampResultCode(rv);
22497 : #ifdef DEBUG
22498 0 : mResultCode = response.get_nsresult();
22499 : #endif
22500 : }
22501 : } else {
22502 : #ifdef DEBUG
22503 : // If something failed then our metadata pointer is now bad. No one should
22504 : // ever touch it again though so just null it out in DEBUG builds to make
22505 : // sure we find such cases.
22506 0 : mMetadata = nullptr;
22507 : #endif
22508 0 : response = ClampResultCode(mResultCode);
22509 : }
22510 :
22511 : Unused <<
22512 0 : PBackgroundIDBFactoryRequestParent::Send__delete__(this, response);
22513 : }
22514 :
22515 0 : if (mDatabase) {
22516 0 : MOZ_ASSERT(!mDirectoryLock);
22517 :
22518 0 : if (NS_FAILED(mResultCode)) {
22519 0 : mDatabase->Invalidate();
22520 : }
22521 :
22522 : // Make sure to release the database on this thread.
22523 0 : mDatabase = nullptr;
22524 0 : } else if (mDirectoryLock) {
22525 0 : nsCOMPtr<nsIRunnable> callback = NewRunnableMethod(
22526 : "dom::indexedDB::OpenDatabaseOp::ConnectionClosedCallback",
22527 : this,
22528 0 : &OpenDatabaseOp::ConnectionClosedCallback);
22529 :
22530 : RefPtr<WaitForTransactionsHelper> helper =
22531 0 : new WaitForTransactionsHelper(mDatabaseId, callback);
22532 0 : helper->WaitForTransactions();
22533 : }
22534 :
22535 0 : FinishSendResults();
22536 0 : }
22537 :
22538 : void
22539 0 : OpenDatabaseOp::ConnectionClosedCallback()
22540 : {
22541 0 : AssertIsOnOwningThread();
22542 0 : MOZ_ASSERT(NS_FAILED(mResultCode));
22543 0 : MOZ_ASSERT(mDirectoryLock);
22544 :
22545 0 : mDirectoryLock = nullptr;
22546 0 : }
22547 :
22548 : void
22549 0 : OpenDatabaseOp::EnsureDatabaseActor()
22550 : {
22551 0 : AssertIsOnOwningThread();
22552 0 : MOZ_ASSERT(mState == State::BeginVersionChange ||
22553 : mState == State::DatabaseWorkVersionChange ||
22554 : mState == State::SendingResults);
22555 0 : MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
22556 0 : MOZ_ASSERT(!mDatabaseFilePath.IsEmpty());
22557 0 : MOZ_ASSERT(!IsActorDestroyed());
22558 :
22559 0 : if (mDatabase) {
22560 0 : return;
22561 : }
22562 :
22563 0 : MOZ_ASSERT(mMetadata->mDatabaseId.IsEmpty());
22564 0 : mMetadata->mDatabaseId = mDatabaseId;
22565 :
22566 0 : MOZ_ASSERT(mMetadata->mFilePath.IsEmpty());
22567 0 : mMetadata->mFilePath = mDatabaseFilePath;
22568 :
22569 : DatabaseActorInfo* info;
22570 0 : if (gLiveDatabaseHashtable->Get(mDatabaseId, &info)) {
22571 0 : AssertMetadataConsistency(info->mMetadata);
22572 0 : mMetadata = info->mMetadata;
22573 : }
22574 :
22575 0 : auto factory = static_cast<Factory*>(Manager());
22576 :
22577 : mDatabase = new Database(factory,
22578 0 : mCommonParams.principalInfo(),
22579 : mOptionalContentParentId,
22580 : mGroup,
22581 : mOrigin,
22582 : mTelemetryId,
22583 : mMetadata,
22584 : mFileManager,
22585 0 : mDirectoryLock.forget(),
22586 0 : mFileHandleDisabled,
22587 0 : mChromeWriteAccessAllowed);
22588 :
22589 0 : if (info) {
22590 0 : info->mLiveDatabases.AppendElement(mDatabase);
22591 : } else {
22592 0 : info = new DatabaseActorInfo(mMetadata, mDatabase);
22593 0 : gLiveDatabaseHashtable->Put(mDatabaseId, info);
22594 : }
22595 :
22596 : // Balanced in Database::CleanupMetadata().
22597 0 : IncreaseBusyCount();
22598 : }
22599 :
22600 : nsresult
22601 0 : OpenDatabaseOp::EnsureDatabaseActorIsAlive()
22602 : {
22603 0 : AssertIsOnOwningThread();
22604 0 : MOZ_ASSERT(mState == State::DatabaseWorkVersionChange ||
22605 : mState == State::SendingResults);
22606 0 : MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
22607 0 : MOZ_ASSERT(!IsActorDestroyed());
22608 :
22609 0 : EnsureDatabaseActor();
22610 :
22611 0 : if (mDatabase->IsActorAlive()) {
22612 0 : return NS_OK;
22613 : }
22614 :
22615 0 : auto factory = static_cast<Factory*>(Manager());
22616 :
22617 0 : DatabaseSpec spec;
22618 0 : MetadataToSpec(spec);
22619 :
22620 : // Transfer ownership to IPDL.
22621 0 : mDatabase->SetActorAlive();
22622 :
22623 0 : if (!factory->SendPBackgroundIDBDatabaseConstructor(mDatabase, spec, this)) {
22624 0 : IDB_REPORT_INTERNAL_ERR();
22625 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22626 : }
22627 :
22628 0 : return NS_OK;
22629 : }
22630 :
22631 : void
22632 0 : OpenDatabaseOp::MetadataToSpec(DatabaseSpec& aSpec)
22633 : {
22634 0 : AssertIsOnOwningThread();
22635 0 : MOZ_ASSERT(mMetadata);
22636 :
22637 0 : aSpec.metadata() = mMetadata->mCommonMetadata;
22638 :
22639 0 : for (auto objectStoreIter = mMetadata->mObjectStores.ConstIter();
22640 0 : !objectStoreIter.Done();
22641 0 : objectStoreIter.Next()) {
22642 0 : FullObjectStoreMetadata* metadata = objectStoreIter.UserData();
22643 0 : MOZ_ASSERT(objectStoreIter.Key());
22644 0 : MOZ_ASSERT(metadata);
22645 :
22646 : // XXX This should really be fallible...
22647 0 : ObjectStoreSpec* objectStoreSpec = aSpec.objectStores().AppendElement();
22648 0 : objectStoreSpec->metadata() = metadata->mCommonMetadata;
22649 :
22650 0 : for (auto indexIter = metadata->mIndexes.Iter();
22651 0 : !indexIter.Done();
22652 0 : indexIter.Next()) {
22653 0 : FullIndexMetadata* indexMetadata = indexIter.UserData();
22654 0 : MOZ_ASSERT(indexIter.Key());
22655 0 : MOZ_ASSERT(indexMetadata);
22656 :
22657 : // XXX This should really be fallible...
22658 0 : IndexMetadata* metadata = objectStoreSpec->indexes().AppendElement();
22659 0 : *metadata = indexMetadata->mCommonMetadata;
22660 : }
22661 : }
22662 0 : }
22663 :
22664 : #ifdef DEBUG
22665 :
22666 : void
22667 0 : OpenDatabaseOp::AssertMetadataConsistency(const FullDatabaseMetadata* aMetadata)
22668 : {
22669 0 : AssertIsOnBackgroundThread();
22670 :
22671 0 : const FullDatabaseMetadata* thisDB = mMetadata;
22672 0 : const FullDatabaseMetadata* otherDB = aMetadata;
22673 :
22674 0 : MOZ_ASSERT(thisDB);
22675 0 : MOZ_ASSERT(otherDB);
22676 0 : MOZ_ASSERT(thisDB != otherDB);
22677 :
22678 0 : MOZ_ASSERT(thisDB->mCommonMetadata.name() == otherDB->mCommonMetadata.name());
22679 0 : MOZ_ASSERT(thisDB->mCommonMetadata.version() ==
22680 : otherDB->mCommonMetadata.version());
22681 0 : MOZ_ASSERT(thisDB->mCommonMetadata.persistenceType() ==
22682 : otherDB->mCommonMetadata.persistenceType());
22683 0 : MOZ_ASSERT(thisDB->mDatabaseId == otherDB->mDatabaseId);
22684 0 : MOZ_ASSERT(thisDB->mFilePath == otherDB->mFilePath);
22685 :
22686 : // |thisDB| reflects the latest objectStore and index ids that have committed
22687 : // to disk. The in-memory metadata |otherDB| keeps track of objectStores and
22688 : // indexes that were created and then removed as well, so the next ids for
22689 : // |otherDB| may be higher than for |thisDB|.
22690 0 : MOZ_ASSERT(thisDB->mNextObjectStoreId <= otherDB->mNextObjectStoreId);
22691 0 : MOZ_ASSERT(thisDB->mNextIndexId <= otherDB->mNextIndexId);
22692 :
22693 0 : MOZ_ASSERT(thisDB->mObjectStores.Count() == otherDB->mObjectStores.Count());
22694 :
22695 0 : for (auto objectStoreIter = thisDB->mObjectStores.ConstIter();
22696 0 : !objectStoreIter.Done();
22697 0 : objectStoreIter.Next()) {
22698 0 : FullObjectStoreMetadata* thisObjectStore = objectStoreIter.UserData();
22699 0 : MOZ_ASSERT(thisObjectStore);
22700 0 : MOZ_ASSERT(!thisObjectStore->mDeleted);
22701 :
22702 : auto* otherObjectStore =
22703 0 : MetadataNameOrIdMatcher<FullObjectStoreMetadata>::Match(
22704 0 : otherDB->mObjectStores, thisObjectStore->mCommonMetadata.id());
22705 0 : MOZ_ASSERT(otherObjectStore);
22706 :
22707 0 : MOZ_ASSERT(thisObjectStore != otherObjectStore);
22708 :
22709 0 : MOZ_ASSERT(thisObjectStore->mCommonMetadata.id() ==
22710 : otherObjectStore->mCommonMetadata.id());
22711 0 : MOZ_ASSERT(thisObjectStore->mCommonMetadata.name() ==
22712 : otherObjectStore->mCommonMetadata.name());
22713 0 : MOZ_ASSERT(thisObjectStore->mCommonMetadata.autoIncrement() ==
22714 : otherObjectStore->mCommonMetadata.autoIncrement());
22715 0 : MOZ_ASSERT(thisObjectStore->mCommonMetadata.keyPath() ==
22716 : otherObjectStore->mCommonMetadata.keyPath());
22717 : // mNextAutoIncrementId and mCommittedAutoIncrementId may be modified
22718 : // concurrently with this OpenOp, so it is not possible to assert equality
22719 : // here. It's also possible that we've written the new ids to disk but not
22720 : // yet updated the in-memory count.
22721 0 : MOZ_ASSERT(thisObjectStore->mNextAutoIncrementId <=
22722 : otherObjectStore->mNextAutoIncrementId);
22723 0 : MOZ_ASSERT(thisObjectStore->mCommittedAutoIncrementId <=
22724 : otherObjectStore->mCommittedAutoIncrementId ||
22725 : thisObjectStore->mCommittedAutoIncrementId ==
22726 : otherObjectStore->mNextAutoIncrementId);
22727 0 : MOZ_ASSERT(!otherObjectStore->mDeleted);
22728 :
22729 0 : MOZ_ASSERT(thisObjectStore->mIndexes.Count() ==
22730 : otherObjectStore->mIndexes.Count());
22731 :
22732 0 : for (auto indexIter = thisObjectStore->mIndexes.Iter();
22733 0 : !indexIter.Done();
22734 0 : indexIter.Next()) {
22735 0 : FullIndexMetadata* thisIndex = indexIter.UserData();
22736 0 : MOZ_ASSERT(thisIndex);
22737 0 : MOZ_ASSERT(!thisIndex->mDeleted);
22738 :
22739 : auto* otherIndex =
22740 : MetadataNameOrIdMatcher<FullIndexMetadata>::
22741 0 : Match(otherObjectStore->mIndexes, thisIndex->mCommonMetadata.id());
22742 0 : MOZ_ASSERT(otherIndex);
22743 :
22744 0 : MOZ_ASSERT(thisIndex != otherIndex);
22745 :
22746 0 : MOZ_ASSERT(thisIndex->mCommonMetadata.id() ==
22747 : otherIndex->mCommonMetadata.id());
22748 0 : MOZ_ASSERT(thisIndex->mCommonMetadata.name() ==
22749 : otherIndex->mCommonMetadata.name());
22750 0 : MOZ_ASSERT(thisIndex->mCommonMetadata.keyPath() ==
22751 : otherIndex->mCommonMetadata.keyPath());
22752 0 : MOZ_ASSERT(thisIndex->mCommonMetadata.unique() ==
22753 : otherIndex->mCommonMetadata.unique());
22754 0 : MOZ_ASSERT(thisIndex->mCommonMetadata.multiEntry() ==
22755 : otherIndex->mCommonMetadata.multiEntry());
22756 0 : MOZ_ASSERT(!otherIndex->mDeleted);
22757 : }
22758 : }
22759 0 : }
22760 :
22761 : #endif // DEBUG
22762 :
22763 : nsresult
22764 0 : OpenDatabaseOp::
22765 : VersionChangeOp::DoDatabaseWork(DatabaseConnection* aConnection)
22766 : {
22767 0 : MOZ_ASSERT(aConnection);
22768 0 : aConnection->AssertIsOnConnectionThread();
22769 0 : MOZ_ASSERT(mOpenDatabaseOp);
22770 0 : MOZ_ASSERT(mOpenDatabaseOp->mState == State::DatabaseWorkVersionChange);
22771 :
22772 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
22773 0 : !OperationMayProceed()) {
22774 0 : IDB_REPORT_INTERNAL_ERR();
22775 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22776 : }
22777 :
22778 0 : AUTO_PROFILER_LABEL(
22779 : "OpenDatabaseOp::VersionChangeOp::DoDatabaseWork", STORAGE);
22780 :
22781 0 : IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
22782 : "Beginning database work",
22783 : "IndexedDB %s: P T[%lld]: DB Start",
22784 : IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
22785 : mLoggingSerialNumber);
22786 :
22787 0 : Transaction()->SetActiveOnConnectionThread();
22788 :
22789 0 : nsresult rv = aConnection->BeginWriteTransaction();
22790 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22791 0 : return rv;
22792 : }
22793 :
22794 0 : DatabaseConnection::CachedStatement updateStmt;
22795 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
22796 : "UPDATE database "
22797 : "SET version = :version;"),
22798 0 : &updateStmt);
22799 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22800 0 : return rv;
22801 : }
22802 :
22803 0 : rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("version"),
22804 0 : int64_t(mRequestedVersion));
22805 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22806 0 : return rv;
22807 : }
22808 :
22809 0 : rv = updateStmt->Execute();
22810 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22811 0 : return rv;
22812 : }
22813 :
22814 0 : return NS_OK;
22815 : }
22816 :
22817 : nsresult
22818 0 : OpenDatabaseOp::
22819 : VersionChangeOp::SendSuccessResult()
22820 : {
22821 0 : AssertIsOnOwningThread();
22822 0 : MOZ_ASSERT(mOpenDatabaseOp);
22823 0 : MOZ_ASSERT(mOpenDatabaseOp->mState == State::DatabaseWorkVersionChange);
22824 0 : MOZ_ASSERT(mOpenDatabaseOp->mVersionChangeOp == this);
22825 :
22826 0 : nsresult rv = mOpenDatabaseOp->SendUpgradeNeeded();
22827 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22828 0 : return rv;
22829 : }
22830 :
22831 0 : return NS_OK;
22832 : }
22833 :
22834 : bool
22835 0 : OpenDatabaseOp::
22836 : VersionChangeOp::SendFailureResult(nsresult aResultCode)
22837 : {
22838 0 : AssertIsOnOwningThread();
22839 0 : MOZ_ASSERT(mOpenDatabaseOp);
22840 0 : MOZ_ASSERT(mOpenDatabaseOp->mState == State::DatabaseWorkVersionChange);
22841 0 : MOZ_ASSERT(mOpenDatabaseOp->mVersionChangeOp == this);
22842 :
22843 0 : mOpenDatabaseOp->SetFailureCode(aResultCode);
22844 0 : mOpenDatabaseOp->mState = State::SendingResults;
22845 :
22846 0 : MOZ_ALWAYS_SUCCEEDS(mOpenDatabaseOp->Run());
22847 :
22848 0 : return false;
22849 : }
22850 :
22851 : void
22852 0 : OpenDatabaseOp::
22853 : VersionChangeOp::Cleanup()
22854 : {
22855 0 : AssertIsOnOwningThread();
22856 0 : MOZ_ASSERT(mOpenDatabaseOp);
22857 0 : MOZ_ASSERT(mOpenDatabaseOp->mVersionChangeOp == this);
22858 :
22859 0 : mOpenDatabaseOp->mVersionChangeOp = nullptr;
22860 0 : mOpenDatabaseOp = nullptr;
22861 :
22862 : #ifdef DEBUG
22863 : // A bit hacky but the VersionChangeOp is not generated in response to a
22864 : // child request like most other database operations. Do this to make our
22865 : // assertions happy.
22866 0 : NoteActorDestroyed();
22867 : #endif
22868 :
22869 0 : TransactionDatabaseOperationBase::Cleanup();
22870 0 : }
22871 :
22872 : void
22873 0 : DeleteDatabaseOp::LoadPreviousVersion(nsIFile* aDatabaseFile)
22874 : {
22875 0 : AssertIsOnIOThread();
22876 0 : MOZ_ASSERT(aDatabaseFile);
22877 0 : MOZ_ASSERT(mState == State::DatabaseWorkOpen);
22878 0 : MOZ_ASSERT(!mPreviousVersion);
22879 :
22880 0 : AUTO_PROFILER_LABEL("DeleteDatabaseOp::LoadPreviousVersion", STORAGE);
22881 :
22882 : nsresult rv;
22883 :
22884 : nsCOMPtr<mozIStorageService> ss =
22885 0 : do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
22886 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22887 0 : return;
22888 : }
22889 :
22890 0 : nsCOMPtr<mozIStorageConnection> connection;
22891 0 : rv = OpenDatabaseAndHandleBusy(ss, aDatabaseFile, getter_AddRefs(connection));
22892 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22893 0 : return;
22894 : }
22895 :
22896 : #ifdef DEBUG
22897 : {
22898 0 : nsCOMPtr<mozIStorageStatement> stmt;
22899 0 : MOZ_ALWAYS_SUCCEEDS(
22900 : connection->CreateStatement(NS_LITERAL_CSTRING(
22901 : "SELECT name "
22902 : "FROM database"
22903 : ), getter_AddRefs(stmt)));
22904 :
22905 : bool hasResult;
22906 0 : MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
22907 :
22908 0 : nsString databaseName;
22909 0 : MOZ_ALWAYS_SUCCEEDS(stmt->GetString(0, databaseName));
22910 :
22911 0 : MOZ_ASSERT(mCommonParams.metadata().name() == databaseName);
22912 : }
22913 : #endif
22914 :
22915 0 : nsCOMPtr<mozIStorageStatement> stmt;
22916 0 : rv = connection->CreateStatement(NS_LITERAL_CSTRING(
22917 : "SELECT version "
22918 : "FROM database"
22919 0 : ), getter_AddRefs(stmt));
22920 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22921 0 : return;
22922 : }
22923 :
22924 : bool hasResult;
22925 0 : rv = stmt->ExecuteStep(&hasResult);
22926 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22927 0 : return;
22928 : }
22929 :
22930 0 : if (NS_WARN_IF(!hasResult)) {
22931 0 : return;
22932 : }
22933 :
22934 : int64_t version;
22935 0 : rv = stmt->GetInt64(0, &version);
22936 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22937 0 : return;
22938 : }
22939 :
22940 0 : mPreviousVersion = uint64_t(version);
22941 : }
22942 :
22943 : nsresult
22944 0 : DeleteDatabaseOp::DatabaseOpen()
22945 : {
22946 0 : AssertIsOnOwningThread();
22947 0 : MOZ_ASSERT(mState == State::DatabaseOpenPending);
22948 :
22949 : // Swap this to the stack now to ensure that we release it on this thread.
22950 0 : RefPtr<ContentParent> contentParent;
22951 0 : mContentParent.swap(contentParent);
22952 :
22953 0 : nsresult rv = SendToIOThread();
22954 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22955 0 : return rv;
22956 : }
22957 :
22958 0 : return NS_OK;
22959 : }
22960 :
22961 : nsresult
22962 0 : DeleteDatabaseOp::DoDatabaseWork()
22963 : {
22964 0 : AssertIsOnIOThread();
22965 0 : MOZ_ASSERT(mState == State::DatabaseWorkOpen);
22966 :
22967 0 : AUTO_PROFILER_LABEL("DeleteDatabaseOp::DoDatabaseWork", STORAGE);
22968 :
22969 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
22970 0 : !OperationMayProceed()) {
22971 0 : IDB_REPORT_INTERNAL_ERR();
22972 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22973 : }
22974 :
22975 0 : const nsString& databaseName = mCommonParams.metadata().name();
22976 0 : PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
22977 :
22978 0 : QuotaManager* quotaManager = QuotaManager::Get();
22979 0 : MOZ_ASSERT(quotaManager);
22980 :
22981 0 : nsCOMPtr<nsIFile> directory;
22982 0 : nsresult rv = quotaManager->GetDirectoryForOrigin(persistenceType,
22983 : mOrigin,
22984 0 : getter_AddRefs(directory));
22985 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22986 0 : return rv;
22987 : }
22988 :
22989 0 : rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
22990 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22991 0 : return rv;
22992 : }
22993 :
22994 0 : rv = directory->GetPath(mDatabaseDirectoryPath);
22995 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
22996 0 : return rv;
22997 : }
22998 :
22999 0 : nsAutoString filename;
23000 0 : GetDatabaseFilename(databaseName, filename);
23001 :
23002 0 : mDatabaseFilenameBase = filename;
23003 :
23004 0 : nsCOMPtr<nsIFile> dbFile;
23005 0 : rv = directory->Clone(getter_AddRefs(dbFile));
23006 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23007 0 : return rv;
23008 : }
23009 :
23010 0 : rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
23011 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23012 0 : return rv;
23013 : }
23014 :
23015 : #ifdef DEBUG
23016 0 : nsString databaseFilePath;
23017 0 : rv = dbFile->GetPath(databaseFilePath);
23018 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23019 0 : return rv;
23020 : }
23021 :
23022 0 : MOZ_ASSERT(databaseFilePath == mDatabaseFilePath);
23023 : #endif
23024 :
23025 : bool exists;
23026 0 : rv = dbFile->Exists(&exists);
23027 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23028 0 : return rv;
23029 : }
23030 :
23031 0 : if (exists) {
23032 : // Parts of this function may fail but that shouldn't prevent us from
23033 : // deleting the file eventually.
23034 0 : LoadPreviousVersion(dbFile);
23035 :
23036 0 : mState = State::BeginVersionChange;
23037 : } else {
23038 0 : mState = State::SendingResults;
23039 : }
23040 :
23041 0 : rv = mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
23042 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23043 0 : return rv;
23044 : }
23045 :
23046 0 : return NS_OK;
23047 : }
23048 :
23049 : nsresult
23050 0 : DeleteDatabaseOp::BeginVersionChange()
23051 : {
23052 0 : AssertIsOnOwningThread();
23053 0 : MOZ_ASSERT(mState == State::BeginVersionChange);
23054 0 : MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
23055 :
23056 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
23057 0 : IsActorDestroyed()) {
23058 0 : IDB_REPORT_INTERNAL_ERR();
23059 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23060 : }
23061 :
23062 : DatabaseActorInfo* info;
23063 0 : if (gLiveDatabaseHashtable->Get(mDatabaseId, &info)) {
23064 0 : MOZ_ASSERT(!info->mWaitingFactoryOp);
23065 :
23066 0 : NullableVersion newVersion = null_t();
23067 :
23068 : nsresult rv =
23069 0 : SendVersionChangeMessages(info, nullptr, mPreviousVersion, newVersion);
23070 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23071 0 : return rv;
23072 : }
23073 :
23074 0 : if (!mMaybeBlockedDatabases.IsEmpty()) {
23075 0 : info->mWaitingFactoryOp = this;
23076 :
23077 0 : mState = State::WaitingForOtherDatabasesToClose;
23078 0 : return NS_OK;
23079 : }
23080 : }
23081 :
23082 : // No other databases need to be notified, just make sure that all
23083 : // transactions are complete.
23084 0 : WaitForTransactions();
23085 0 : return NS_OK;
23086 : }
23087 :
23088 : nsresult
23089 0 : DeleteDatabaseOp::DispatchToWorkThread()
23090 : {
23091 0 : AssertIsOnOwningThread();
23092 0 : MOZ_ASSERT(mState == State::WaitingForTransactionsToComplete);
23093 0 : MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
23094 :
23095 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
23096 0 : IsActorDestroyed()) {
23097 0 : IDB_REPORT_INTERNAL_ERR();
23098 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23099 : }
23100 :
23101 0 : mState = State::DatabaseWorkVersionChange;
23102 :
23103 0 : RefPtr<VersionChangeOp> versionChangeOp = new VersionChangeOp(this);
23104 :
23105 0 : QuotaManager* quotaManager = QuotaManager::Get();
23106 0 : MOZ_ASSERT(quotaManager);
23107 :
23108 : nsresult rv =
23109 0 : quotaManager->IOThread()->Dispatch(versionChangeOp.forget(),
23110 0 : NS_DISPATCH_NORMAL);
23111 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23112 0 : IDB_REPORT_INTERNAL_ERR();
23113 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23114 : }
23115 :
23116 0 : return NS_OK;
23117 : }
23118 :
23119 : void
23120 0 : DeleteDatabaseOp::NoteDatabaseClosed(Database* aDatabase)
23121 : {
23122 0 : AssertIsOnOwningThread();
23123 0 : MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose);
23124 0 : MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty());
23125 :
23126 0 : bool actorDestroyed = IsActorDestroyed();
23127 :
23128 : nsresult rv;
23129 0 : if (actorDestroyed) {
23130 0 : IDB_REPORT_INTERNAL_ERR();
23131 0 : rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23132 : } else {
23133 0 : rv = NS_OK;
23134 : }
23135 :
23136 : // We are being called with an assumption that mWaitingFactoryOp holds
23137 : // a strong reference to us.
23138 0 : RefPtr<OpenDatabaseOp> kungFuDeathGrip;
23139 :
23140 0 : if (mMaybeBlockedDatabases.RemoveElement(aDatabase) &&
23141 0 : mMaybeBlockedDatabases.IsEmpty()) {
23142 0 : if (actorDestroyed) {
23143 : DatabaseActorInfo* info;
23144 0 : MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info));
23145 0 : MOZ_ASSERT(info->mWaitingFactoryOp == this);
23146 : kungFuDeathGrip =
23147 0 : static_cast<OpenDatabaseOp*>(info->mWaitingFactoryOp.get());
23148 0 : info->mWaitingFactoryOp = nullptr;
23149 : } else {
23150 0 : WaitForTransactions();
23151 : }
23152 : }
23153 :
23154 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23155 0 : if (NS_SUCCEEDED(mResultCode)) {
23156 0 : mResultCode = rv;
23157 : }
23158 :
23159 : // A strong reference is held in kungFuDeathGrip, so it's safe to call Run()
23160 : // directly.
23161 :
23162 0 : mState = State::SendingResults;
23163 0 : MOZ_ALWAYS_SUCCEEDS(Run());
23164 : }
23165 0 : }
23166 :
23167 : void
23168 0 : DeleteDatabaseOp::SendBlockedNotification()
23169 : {
23170 0 : AssertIsOnOwningThread();
23171 0 : MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose);
23172 :
23173 0 : if (!IsActorDestroyed()) {
23174 0 : Unused << SendBlocked(mPreviousVersion);
23175 : }
23176 0 : }
23177 :
23178 : void
23179 0 : DeleteDatabaseOp::SendResults()
23180 : {
23181 0 : AssertIsOnOwningThread();
23182 0 : MOZ_ASSERT(mState == State::SendingResults);
23183 :
23184 0 : if (!IsActorDestroyed()) {
23185 0 : FactoryRequestResponse response;
23186 :
23187 0 : if (NS_SUCCEEDED(mResultCode)) {
23188 0 : response = DeleteDatabaseRequestResponse(mPreviousVersion);
23189 : } else {
23190 0 : response = ClampResultCode(mResultCode);
23191 : }
23192 :
23193 : Unused <<
23194 0 : PBackgroundIDBFactoryRequestParent::Send__delete__(this, response);
23195 : }
23196 :
23197 0 : mDirectoryLock = nullptr;
23198 :
23199 0 : FinishSendResults();
23200 0 : }
23201 :
23202 : nsresult
23203 0 : DeleteDatabaseOp::
23204 : VersionChangeOp::DeleteFile(nsIFile* aDirectory,
23205 : const nsAString& aFilename,
23206 : QuotaManager* aQuotaManager)
23207 : {
23208 0 : AssertIsOnIOThread();
23209 0 : MOZ_ASSERT(aDirectory);
23210 0 : MOZ_ASSERT(!aFilename.IsEmpty());
23211 0 : MOZ_ASSERT_IF(aQuotaManager, mDeleteDatabaseOp->mEnforcingQuota);
23212 :
23213 0 : MOZ_ASSERT(mDeleteDatabaseOp->mState == State::DatabaseWorkVersionChange);
23214 :
23215 0 : AUTO_PROFILER_LABEL("DeleteDatabaseOp::VersionChangeOp::DeleteFile", STORAGE);
23216 :
23217 0 : nsCOMPtr<nsIFile> file;
23218 0 : nsresult rv = aDirectory->Clone(getter_AddRefs(file));
23219 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23220 0 : return rv;
23221 : }
23222 :
23223 0 : rv = file->Append(aFilename);
23224 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23225 0 : return rv;
23226 : }
23227 :
23228 : int64_t fileSize;
23229 :
23230 0 : if (aQuotaManager) {
23231 0 : rv = file->GetFileSize(&fileSize);
23232 0 : if (rv == NS_ERROR_FILE_NOT_FOUND ||
23233 : rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
23234 0 : return NS_OK;
23235 : }
23236 :
23237 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23238 0 : return rv;
23239 : }
23240 :
23241 0 : MOZ_ASSERT(fileSize >= 0);
23242 : }
23243 :
23244 0 : rv = file->Remove(false);
23245 0 : if (rv == NS_ERROR_FILE_NOT_FOUND ||
23246 : rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
23247 0 : return NS_OK;
23248 : }
23249 :
23250 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23251 0 : return rv;
23252 : }
23253 :
23254 0 : if (aQuotaManager && fileSize > 0) {
23255 : const PersistenceType& persistenceType =
23256 0 : mDeleteDatabaseOp->mCommonParams.metadata().persistenceType();
23257 :
23258 0 : aQuotaManager->DecreaseUsageForOrigin(persistenceType,
23259 0 : mDeleteDatabaseOp->mGroup,
23260 0 : mDeleteDatabaseOp->mOrigin,
23261 0 : fileSize);
23262 : }
23263 :
23264 0 : return NS_OK;
23265 : }
23266 :
23267 : nsresult
23268 0 : DeleteDatabaseOp::
23269 : VersionChangeOp::RunOnIOThread()
23270 : {
23271 0 : AssertIsOnIOThread();
23272 0 : MOZ_ASSERT(mDeleteDatabaseOp->mState == State::DatabaseWorkVersionChange);
23273 :
23274 0 : AUTO_PROFILER_LABEL(
23275 : "DeleteDatabaseOp::VersionChangeOp::RunOnIOThread", STORAGE);
23276 :
23277 0 : if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
23278 0 : !OperationMayProceed()) {
23279 0 : IDB_REPORT_INTERNAL_ERR();
23280 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23281 : }
23282 :
23283 : const PersistenceType& persistenceType =
23284 0 : mDeleteDatabaseOp->mCommonParams.metadata().persistenceType();
23285 :
23286 : QuotaManager* quotaManager =
23287 0 : mDeleteDatabaseOp->mEnforcingQuota ?
23288 : QuotaManager::Get() :
23289 0 : nullptr;
23290 :
23291 0 : MOZ_ASSERT_IF(mDeleteDatabaseOp->mEnforcingQuota, quotaManager);
23292 :
23293 : nsCOMPtr<nsIFile> directory =
23294 0 : GetFileForPath(mDeleteDatabaseOp->mDatabaseDirectoryPath);
23295 0 : if (NS_WARN_IF(!directory)) {
23296 0 : IDB_REPORT_INTERNAL_ERR();
23297 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23298 : }
23299 :
23300 : // The database file counts towards quota.
23301 : nsAutoString filename =
23302 0 : mDeleteDatabaseOp->mDatabaseFilenameBase + NS_LITERAL_STRING(".sqlite");
23303 :
23304 0 : nsresult rv = DeleteFile(directory, filename, quotaManager);
23305 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23306 0 : return rv;
23307 : }
23308 :
23309 : // .sqlite-journal files don't count towards quota.
23310 : const NS_ConvertASCIItoUTF16 journalSuffix(
23311 : kSQLiteJournalSuffix,
23312 0 : LiteralStringLength(kSQLiteJournalSuffix));
23313 :
23314 0 : filename = mDeleteDatabaseOp->mDatabaseFilenameBase + journalSuffix;
23315 :
23316 0 : rv = DeleteFile(directory, filename, /* doesn't count */ nullptr);
23317 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23318 0 : return rv;
23319 : }
23320 :
23321 : // .sqlite-shm files don't count towards quota.
23322 : const NS_ConvertASCIItoUTF16 shmSuffix(kSQLiteSHMSuffix,
23323 0 : LiteralStringLength(kSQLiteSHMSuffix));
23324 :
23325 0 : filename = mDeleteDatabaseOp->mDatabaseFilenameBase + shmSuffix;
23326 :
23327 0 : rv = DeleteFile(directory, filename, /* doesn't count */ nullptr);
23328 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23329 0 : return rv;
23330 : }
23331 :
23332 : // .sqlite-wal files do count towards quota.
23333 : const NS_ConvertASCIItoUTF16 walSuffix(kSQLiteWALSuffix,
23334 0 : LiteralStringLength(kSQLiteWALSuffix));
23335 :
23336 0 : filename = mDeleteDatabaseOp->mDatabaseFilenameBase + walSuffix;
23337 :
23338 0 : rv = DeleteFile(directory, filename, quotaManager);
23339 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23340 0 : return rv;
23341 : }
23342 :
23343 0 : nsCOMPtr<nsIFile> fmDirectory;
23344 0 : rv = directory->Clone(getter_AddRefs(fmDirectory));
23345 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23346 0 : return rv;
23347 : }
23348 :
23349 : // The files directory counts towards quota.
23350 : const NS_ConvertASCIItoUTF16 filesSuffix(
23351 : kFileManagerDirectoryNameSuffix,
23352 0 : LiteralStringLength(kFileManagerDirectoryNameSuffix));
23353 :
23354 0 : rv = fmDirectory->Append(mDeleteDatabaseOp->mDatabaseFilenameBase +
23355 0 : filesSuffix);
23356 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23357 0 : return rv;
23358 : }
23359 :
23360 : bool exists;
23361 0 : rv = fmDirectory->Exists(&exists);
23362 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23363 0 : return rv;
23364 : }
23365 :
23366 0 : if (exists) {
23367 : bool isDirectory;
23368 0 : rv = fmDirectory->IsDirectory(&isDirectory);
23369 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23370 0 : return rv;
23371 : }
23372 :
23373 0 : if (NS_WARN_IF(!isDirectory)) {
23374 0 : IDB_REPORT_INTERNAL_ERR();
23375 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23376 : }
23377 :
23378 0 : uint64_t usage = 0;
23379 :
23380 0 : if (mDeleteDatabaseOp->mEnforcingQuota) {
23381 0 : rv = FileManager::GetUsage(fmDirectory, &usage);
23382 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23383 0 : return rv;
23384 : }
23385 : }
23386 :
23387 0 : rv = fmDirectory->Remove(true);
23388 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23389 : // We may have deleted some files, check if we can and update quota
23390 : // information before returning the error.
23391 0 : if (mDeleteDatabaseOp->mEnforcingQuota) {
23392 : uint64_t newUsage;
23393 0 : if (NS_SUCCEEDED(FileManager::GetUsage(fmDirectory, &newUsage))) {
23394 0 : MOZ_ASSERT(newUsage <= usage);
23395 0 : usage = usage - newUsage;
23396 : }
23397 : }
23398 : }
23399 :
23400 0 : if (mDeleteDatabaseOp->mEnforcingQuota && usage) {
23401 0 : quotaManager->DecreaseUsageForOrigin(persistenceType,
23402 0 : mDeleteDatabaseOp->mGroup,
23403 0 : mDeleteDatabaseOp->mOrigin,
23404 0 : usage);
23405 : }
23406 :
23407 0 : if (NS_FAILED(rv)) {
23408 0 : return rv;
23409 : }
23410 : }
23411 :
23412 0 : IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
23413 0 : MOZ_ASSERT(mgr);
23414 :
23415 : const nsString& databaseName =
23416 0 : mDeleteDatabaseOp->mCommonParams.metadata().name();
23417 :
23418 0 : mgr->InvalidateFileManager(persistenceType,
23419 0 : mDeleteDatabaseOp->mOrigin,
23420 0 : databaseName);
23421 :
23422 0 : rv = mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
23423 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23424 0 : return rv;
23425 : }
23426 :
23427 0 : return NS_OK;
23428 : }
23429 :
23430 : void
23431 0 : DeleteDatabaseOp::
23432 : VersionChangeOp::RunOnOwningThread()
23433 : {
23434 0 : AssertIsOnOwningThread();
23435 0 : MOZ_ASSERT(mDeleteDatabaseOp->mState == State::DatabaseWorkVersionChange);
23436 :
23437 0 : RefPtr<DeleteDatabaseOp> deleteOp;
23438 0 : mDeleteDatabaseOp.swap(deleteOp);
23439 :
23440 0 : if (deleteOp->IsActorDestroyed()) {
23441 0 : IDB_REPORT_INTERNAL_ERR();
23442 0 : deleteOp->SetFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
23443 : } else {
23444 : DatabaseActorInfo* info;
23445 0 : if (gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId, &info) &&
23446 0 : info->mWaitingFactoryOp) {
23447 0 : MOZ_ASSERT(info->mWaitingFactoryOp == deleteOp);
23448 0 : info->mWaitingFactoryOp = nullptr;
23449 : }
23450 :
23451 0 : if (NS_FAILED(mResultCode)) {
23452 0 : if (NS_SUCCEEDED(deleteOp->ResultCode())) {
23453 0 : deleteOp->SetFailureCode(mResultCode);
23454 : }
23455 : } else {
23456 : // Inform all the other databases that they are now invalidated. That
23457 : // should remove the previous metadata from our table.
23458 0 : if (info) {
23459 0 : MOZ_ASSERT(!info->mLiveDatabases.IsEmpty());
23460 :
23461 0 : FallibleTArray<Database*> liveDatabases;
23462 0 : if (NS_WARN_IF(!liveDatabases.AppendElements(info->mLiveDatabases,
23463 : fallible))) {
23464 0 : deleteOp->SetFailureCode(NS_ERROR_OUT_OF_MEMORY);
23465 : } else {
23466 : #ifdef DEBUG
23467 : // The code below should result in the deletion of |info|. Set to null
23468 : // here to make sure we find invalid uses later.
23469 0 : info = nullptr;
23470 : #endif
23471 0 : for (uint32_t count = liveDatabases.Length(), index = 0;
23472 0 : index < count;
23473 : index++) {
23474 0 : RefPtr<Database> database = liveDatabases[index];
23475 0 : database->Invalidate();
23476 : }
23477 :
23478 0 : MOZ_ASSERT(!gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId));
23479 : }
23480 : }
23481 : }
23482 : }
23483 :
23484 : // We hold a strong ref to the deleteOp, so it's safe to call Run() directly.
23485 :
23486 0 : deleteOp->mState = State::SendingResults;
23487 0 : MOZ_ALWAYS_SUCCEEDS(deleteOp->Run());
23488 :
23489 : #ifdef DEBUG
23490 : // A bit hacky but the DeleteDatabaseOp::VersionChangeOp is not really a
23491 : // normal database operation that is tied to an actor. Do this to make our
23492 : // assertions happy.
23493 0 : NoteActorDestroyed();
23494 : #endif
23495 0 : }
23496 :
23497 : nsresult
23498 0 : DeleteDatabaseOp::
23499 : VersionChangeOp::Run()
23500 : {
23501 : nsresult rv;
23502 :
23503 0 : if (IsOnIOThread()) {
23504 0 : rv = RunOnIOThread();
23505 : } else {
23506 0 : RunOnOwningThread();
23507 0 : rv = NS_OK;
23508 : }
23509 :
23510 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23511 0 : if (NS_SUCCEEDED(mResultCode)) {
23512 0 : mResultCode = rv;
23513 : }
23514 :
23515 0 : MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
23516 : }
23517 :
23518 0 : return NS_OK;
23519 : }
23520 :
23521 0 : TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
23522 0 : TransactionBase* aTransaction)
23523 : : DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
23524 : aTransaction->GetLoggingInfo()->NextRequestSN())
23525 : , mTransaction(aTransaction)
23526 0 : , mTransactionLoggingSerialNumber(aTransaction->LoggingSerialNumber())
23527 : , mInternalState(InternalState::Initial)
23528 0 : , mTransactionIsAborted(aTransaction->IsAborted())
23529 : {
23530 0 : MOZ_ASSERT(aTransaction);
23531 0 : MOZ_ASSERT(LoggingSerialNumber());
23532 0 : }
23533 :
23534 0 : TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
23535 : TransactionBase* aTransaction,
23536 0 : uint64_t aLoggingSerialNumber)
23537 : : DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
23538 : aLoggingSerialNumber)
23539 : , mTransaction(aTransaction)
23540 0 : , mTransactionLoggingSerialNumber(aTransaction->LoggingSerialNumber())
23541 : , mInternalState(InternalState::Initial)
23542 0 : , mTransactionIsAborted(aTransaction->IsAborted())
23543 : {
23544 0 : MOZ_ASSERT(aTransaction);
23545 0 : }
23546 :
23547 0 : TransactionDatabaseOperationBase::~TransactionDatabaseOperationBase()
23548 : {
23549 0 : MOZ_ASSERT(mInternalState == InternalState::Completed);
23550 0 : MOZ_ASSERT(!mTransaction,
23551 : "TransactionDatabaseOperationBase::Cleanup() was not called by a "
23552 : "subclass!");
23553 0 : }
23554 :
23555 : #ifdef DEBUG
23556 :
23557 : void
23558 0 : TransactionDatabaseOperationBase::AssertIsOnConnectionThread() const
23559 : {
23560 0 : MOZ_ASSERT(mTransaction);
23561 0 : mTransaction->AssertIsOnConnectionThread();
23562 0 : }
23563 :
23564 : #endif // DEBUG
23565 :
23566 : uint64_t
23567 0 : TransactionDatabaseOperationBase::StartOnConnectionPool(
23568 : const nsID& aBackgroundChildLoggingId,
23569 : const nsACString& aDatabaseId,
23570 : int64_t aLoggingSerialNumber,
23571 : const nsTArray<nsString>& aObjectStoreNames,
23572 : bool aIsWriteTransaction)
23573 : {
23574 0 : AssertIsOnOwningThread();
23575 0 : MOZ_ASSERT(mInternalState == InternalState::Initial);
23576 :
23577 : // Must set mInternalState before dispatching otherwise we will race with the
23578 : // connection thread.
23579 0 : mInternalState = InternalState::DatabaseWork;
23580 :
23581 0 : return gConnectionPool->Start(aBackgroundChildLoggingId,
23582 : aDatabaseId,
23583 : aLoggingSerialNumber,
23584 : aObjectStoreNames,
23585 : aIsWriteTransaction,
23586 0 : this);
23587 : }
23588 :
23589 : void
23590 0 : TransactionDatabaseOperationBase::DispatchToConnectionPool()
23591 : {
23592 0 : AssertIsOnOwningThread();
23593 0 : MOZ_ASSERT(mInternalState == InternalState::Initial);
23594 :
23595 0 : Unused << this->Run();
23596 0 : }
23597 :
23598 : void
23599 0 : TransactionDatabaseOperationBase::RunOnConnectionThread()
23600 : {
23601 0 : MOZ_ASSERT(!IsOnBackgroundThread());
23602 0 : MOZ_ASSERT(mInternalState == InternalState::DatabaseWork);
23603 0 : MOZ_ASSERT(mTransaction);
23604 0 : MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
23605 :
23606 0 : AUTO_PROFILER_LABEL(
23607 : "TransactionDatabaseOperationBase::RunOnConnectionThread", STORAGE);
23608 :
23609 : // There are several cases where we don't actually have to to any work here.
23610 :
23611 0 : if (mTransactionIsAborted || mTransaction->IsInvalidatedOnAnyThread()) {
23612 : // This transaction is already set to be aborted or invalidated.
23613 0 : mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
23614 0 : } else if (!OperationMayProceed()) {
23615 : // The operation was canceled in some way, likely because the child process
23616 : // has crashed.
23617 0 : IDB_REPORT_INTERNAL_ERR();
23618 0 : mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23619 : } else {
23620 0 : Database* database = mTransaction->GetDatabase();
23621 0 : MOZ_ASSERT(database);
23622 :
23623 : // Here we're actually going to perform the database operation.
23624 0 : nsresult rv = database->EnsureConnection();
23625 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23626 0 : mResultCode = rv;
23627 : } else {
23628 0 : DatabaseConnection* connection = database->GetConnection();
23629 0 : MOZ_ASSERT(connection);
23630 0 : MOZ_ASSERT(connection->GetStorageConnection());
23631 :
23632 0 : AutoSetProgressHandler autoProgress;
23633 0 : if (mLoggingSerialNumber) {
23634 0 : rv = autoProgress.Register(connection->GetStorageConnection(), this);
23635 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23636 0 : mResultCode = rv;
23637 : }
23638 : }
23639 :
23640 0 : if (NS_SUCCEEDED(rv)) {
23641 0 : if (mLoggingSerialNumber) {
23642 0 : IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld] Request[%llu]: "
23643 : "Beginning database work",
23644 : "IndexedDB %s: P T[%lld] R[%llu]: DB Start",
23645 : IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
23646 : mTransactionLoggingSerialNumber,
23647 : mLoggingSerialNumber);
23648 : }
23649 :
23650 0 : rv = DoDatabaseWork(connection);
23651 :
23652 0 : if (mLoggingSerialNumber) {
23653 0 : IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld] Request[%llu]: "
23654 : "Finished database work",
23655 : "IndexedDB %s: P T[%lld] R[%llu]: DB End",
23656 : IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
23657 : mTransactionLoggingSerialNumber,
23658 : mLoggingSerialNumber);
23659 : }
23660 :
23661 0 : if (NS_FAILED(rv)) {
23662 0 : mResultCode = rv;
23663 : }
23664 : }
23665 : }
23666 : }
23667 :
23668 : // Must set mInternalState before dispatching otherwise we will race with the
23669 : // owning thread.
23670 0 : if (HasPreprocessInfo()) {
23671 0 : mInternalState = InternalState::SendingPreprocess;
23672 : } else {
23673 0 : mInternalState = InternalState::SendingResults;
23674 : }
23675 :
23676 0 : MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
23677 0 : }
23678 :
23679 : bool
23680 0 : TransactionDatabaseOperationBase::HasPreprocessInfo()
23681 : {
23682 0 : return false;
23683 : }
23684 :
23685 : nsresult
23686 0 : TransactionDatabaseOperationBase::SendPreprocessInfo()
23687 : {
23688 0 : return NS_OK;
23689 : }
23690 :
23691 : void
23692 0 : TransactionDatabaseOperationBase::NoteContinueReceived()
23693 : {
23694 0 : AssertIsOnOwningThread();
23695 0 : MOZ_ASSERT(mInternalState == InternalState::WaitingForContinue);
23696 :
23697 0 : mInternalState = InternalState::SendingResults;
23698 :
23699 : // This TransactionDatabaseOperationBase can only be held alive by the IPDL.
23700 : // Run() can end up with clearing that last reference. So we need to add
23701 : // a self reference here.
23702 0 : RefPtr<TransactionDatabaseOperationBase> kungFuDeathGrip = this;
23703 :
23704 0 : Unused << this->Run();
23705 0 : }
23706 :
23707 : void
23708 0 : TransactionDatabaseOperationBase::SendToConnectionPool()
23709 : {
23710 0 : AssertIsOnOwningThread();
23711 0 : MOZ_ASSERT(mInternalState == InternalState::Initial);
23712 :
23713 : // Must set mInternalState before dispatching otherwise we will race with the
23714 : // connection thread.
23715 0 : mInternalState = InternalState::DatabaseWork;
23716 :
23717 0 : gConnectionPool->Dispatch(mTransaction->TransactionId(), this);
23718 :
23719 0 : mTransaction->NoteActiveRequest();
23720 0 : }
23721 :
23722 : void
23723 0 : TransactionDatabaseOperationBase::SendPreprocess()
23724 : {
23725 0 : AssertIsOnOwningThread();
23726 0 : MOZ_ASSERT(mInternalState == InternalState::SendingPreprocess);
23727 :
23728 0 : SendPreprocessInfoOrResults(/* aSendPreprocessInfo */ true);
23729 0 : }
23730 :
23731 : void
23732 0 : TransactionDatabaseOperationBase::SendResults()
23733 : {
23734 0 : AssertIsOnOwningThread();
23735 0 : MOZ_ASSERT(mInternalState == InternalState::SendingResults);
23736 :
23737 0 : SendPreprocessInfoOrResults(/* aSendPreprocessInfo */ false);
23738 0 : }
23739 :
23740 : void
23741 0 : TransactionDatabaseOperationBase::SendPreprocessInfoOrResults(
23742 : bool aSendPreprocessInfo)
23743 : {
23744 0 : AssertIsOnOwningThread();
23745 0 : MOZ_ASSERT(mInternalState == InternalState::SendingPreprocess ||
23746 : mInternalState == InternalState::SendingResults);
23747 0 : MOZ_ASSERT(mTransaction);
23748 :
23749 0 : if (NS_WARN_IF(IsActorDestroyed())) {
23750 : // Don't send any notifications if the actor was destroyed already.
23751 0 : if (NS_SUCCEEDED(mResultCode)) {
23752 0 : IDB_REPORT_INTERNAL_ERR();
23753 0 : mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23754 : }
23755 : } else {
23756 0 : if (mTransaction->IsInvalidated() || mTransaction->IsAborted()) {
23757 : // Aborted transactions always see their requests fail with ABORT_ERR,
23758 : // even if the request succeeded or failed with another error.
23759 0 : mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
23760 0 : } else if (NS_SUCCEEDED(mResultCode)) {
23761 0 : if (aSendPreprocessInfo) {
23762 : // This should not release the IPDL reference.
23763 0 : mResultCode = SendPreprocessInfo();
23764 : } else {
23765 : // This may release the IPDL reference.
23766 0 : mResultCode = SendSuccessResult();
23767 : }
23768 : }
23769 :
23770 0 : if (NS_FAILED(mResultCode)) {
23771 : // This should definitely release the IPDL reference.
23772 0 : if (!SendFailureResult(mResultCode)) {
23773 : // Abort the transaction.
23774 0 : mTransaction->Abort(mResultCode, /* aForce */ false);
23775 : }
23776 : }
23777 : }
23778 :
23779 0 : if (aSendPreprocessInfo && NS_SUCCEEDED(mResultCode)) {
23780 0 : mInternalState = InternalState::WaitingForContinue;
23781 : } else {
23782 0 : if (mLoggingSerialNumber) {
23783 0 : mTransaction->NoteFinishedRequest();
23784 : }
23785 :
23786 0 : Cleanup();
23787 :
23788 0 : mInternalState = InternalState::Completed;
23789 : }
23790 0 : }
23791 :
23792 : bool
23793 0 : TransactionDatabaseOperationBase::Init(TransactionBase* aTransaction)
23794 : {
23795 0 : AssertIsOnBackgroundThread();
23796 0 : MOZ_ASSERT(mInternalState == InternalState::Initial);
23797 0 : MOZ_ASSERT(aTransaction);
23798 :
23799 0 : return true;
23800 : }
23801 :
23802 : void
23803 0 : TransactionDatabaseOperationBase::Cleanup()
23804 : {
23805 0 : AssertIsOnOwningThread();
23806 0 : MOZ_ASSERT(mInternalState == InternalState::SendingResults);
23807 0 : MOZ_ASSERT(mTransaction);
23808 :
23809 0 : mTransaction = nullptr;
23810 0 : }
23811 :
23812 : NS_IMETHODIMP
23813 0 : TransactionDatabaseOperationBase::Run()
23814 : {
23815 0 : switch (mInternalState) {
23816 : case InternalState::Initial:
23817 0 : SendToConnectionPool();
23818 0 : return NS_OK;
23819 :
23820 : case InternalState::DatabaseWork:
23821 0 : RunOnConnectionThread();
23822 0 : return NS_OK;
23823 :
23824 : case InternalState::SendingPreprocess:
23825 0 : SendPreprocess();
23826 0 : return NS_OK;
23827 :
23828 : case InternalState::SendingResults:
23829 0 : SendResults();
23830 0 : return NS_OK;
23831 :
23832 : default:
23833 0 : MOZ_CRASH("Bad state!");
23834 : }
23835 : }
23836 :
23837 0 : TransactionBase::
23838 0 : CommitOp::CommitOp(TransactionBase* aTransaction, nsresult aResultCode)
23839 : : DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
23840 : aTransaction->GetLoggingInfo()->NextRequestSN())
23841 : , mTransaction(aTransaction)
23842 0 : , mResultCode(aResultCode)
23843 : {
23844 0 : MOZ_ASSERT(aTransaction);
23845 0 : MOZ_ASSERT(LoggingSerialNumber());
23846 0 : }
23847 :
23848 : nsresult
23849 0 : TransactionBase::
23850 : CommitOp::WriteAutoIncrementCounts()
23851 : {
23852 0 : MOZ_ASSERT(mTransaction);
23853 0 : mTransaction->AssertIsOnConnectionThread();
23854 0 : MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::READ_WRITE ||
23855 : mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH ||
23856 : mTransaction->GetMode() == IDBTransaction::CLEANUP ||
23857 : mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
23858 :
23859 : const nsTArray<RefPtr<FullObjectStoreMetadata>>& metadataArray =
23860 0 : mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray;
23861 :
23862 0 : if (!metadataArray.IsEmpty()) {
23863 0 : NS_NAMED_LITERAL_CSTRING(osid, "osid");
23864 0 : NS_NAMED_LITERAL_CSTRING(ai, "ai");
23865 :
23866 0 : Database* database = mTransaction->GetDatabase();
23867 0 : MOZ_ASSERT(database);
23868 :
23869 0 : DatabaseConnection* connection = database->GetConnection();
23870 0 : MOZ_ASSERT(connection);
23871 :
23872 0 : DatabaseConnection::CachedStatement stmt;
23873 : nsresult rv;
23874 :
23875 0 : for (uint32_t count = metadataArray.Length(), index = 0;
23876 0 : index < count;
23877 : index++) {
23878 0 : const RefPtr<FullObjectStoreMetadata>& metadata = metadataArray[index];
23879 0 : MOZ_ASSERT(!metadata->mDeleted);
23880 0 : MOZ_ASSERT(metadata->mNextAutoIncrementId > 1);
23881 :
23882 0 : if (stmt) {
23883 0 : MOZ_ALWAYS_SUCCEEDS(stmt->Reset());
23884 : } else {
23885 0 : rv = connection->GetCachedStatement(
23886 0 : NS_LITERAL_CSTRING("UPDATE object_store "
23887 0 : "SET auto_increment = :") + ai +
23888 0 : NS_LITERAL_CSTRING(" WHERE id = :") + osid +
23889 0 : NS_LITERAL_CSTRING(";"),
23890 0 : &stmt);
23891 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23892 0 : return rv;
23893 : }
23894 : }
23895 :
23896 0 : rv = stmt->BindInt64ByName(osid, metadata->mCommonMetadata.id());
23897 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23898 0 : return rv;
23899 : }
23900 :
23901 0 : rv = stmt->BindInt64ByName(ai, metadata->mNextAutoIncrementId);
23902 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23903 0 : return rv;
23904 : }
23905 :
23906 0 : rv = stmt->Execute();
23907 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
23908 0 : return rv;
23909 : }
23910 : }
23911 : }
23912 :
23913 0 : return NS_OK;
23914 : }
23915 :
23916 : void
23917 0 : TransactionBase::
23918 : CommitOp::CommitOrRollbackAutoIncrementCounts()
23919 : {
23920 0 : MOZ_ASSERT(mTransaction);
23921 0 : mTransaction->AssertIsOnConnectionThread();
23922 0 : MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::READ_WRITE ||
23923 : mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH ||
23924 : mTransaction->GetMode() == IDBTransaction::CLEANUP ||
23925 : mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
23926 :
23927 : nsTArray<RefPtr<FullObjectStoreMetadata>>& metadataArray =
23928 0 : mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray;
23929 :
23930 0 : if (!metadataArray.IsEmpty()) {
23931 0 : bool committed = NS_SUCCEEDED(mResultCode);
23932 :
23933 0 : for (uint32_t count = metadataArray.Length(), index = 0;
23934 0 : index < count;
23935 : index++) {
23936 0 : RefPtr<FullObjectStoreMetadata>& metadata = metadataArray[index];
23937 :
23938 0 : if (committed) {
23939 0 : metadata->mCommittedAutoIncrementId = metadata->mNextAutoIncrementId;
23940 : } else {
23941 0 : metadata->mNextAutoIncrementId = metadata->mCommittedAutoIncrementId;
23942 : }
23943 : }
23944 : }
23945 0 : }
23946 :
23947 : #ifdef DEBUG
23948 :
23949 : void
23950 0 : TransactionBase::
23951 : CommitOp::AssertForeignKeyConsistency(DatabaseConnection* aConnection)
23952 : {
23953 0 : MOZ_ASSERT(aConnection);
23954 0 : MOZ_ASSERT(mTransaction);
23955 0 : mTransaction->AssertIsOnConnectionThread();
23956 0 : MOZ_ASSERT(mTransaction->GetMode() != IDBTransaction::READ_ONLY);
23957 :
23958 0 : DatabaseConnection::CachedStatement pragmaStmt;
23959 0 : MOZ_ALWAYS_SUCCEEDS(
23960 : aConnection->GetCachedStatement(NS_LITERAL_CSTRING("PRAGMA foreign_keys;"),
23961 : &pragmaStmt));
23962 :
23963 : bool hasResult;
23964 0 : MOZ_ALWAYS_SUCCEEDS(pragmaStmt->ExecuteStep(&hasResult));
23965 :
23966 0 : MOZ_ASSERT(hasResult);
23967 :
23968 : int32_t foreignKeysEnabled;
23969 0 : MOZ_ALWAYS_SUCCEEDS(pragmaStmt->GetInt32(0, &foreignKeysEnabled));
23970 :
23971 0 : MOZ_ASSERT(foreignKeysEnabled, "Database doesn't have foreign keys enabled!");
23972 :
23973 0 : DatabaseConnection::CachedStatement checkStmt;
23974 0 : MOZ_ALWAYS_SUCCEEDS(
23975 : aConnection->GetCachedStatement(
23976 : NS_LITERAL_CSTRING("PRAGMA foreign_key_check;"),
23977 : &checkStmt));
23978 :
23979 0 : MOZ_ALWAYS_SUCCEEDS(checkStmt->ExecuteStep(&hasResult));
23980 :
23981 0 : MOZ_ASSERT(!hasResult, "Database has inconsisistent foreign keys!");
23982 0 : }
23983 :
23984 : #endif // DEBUG
23985 :
23986 0 : NS_IMPL_ISUPPORTS_INHERITED0(TransactionBase::CommitOp, DatabaseOperationBase)
23987 :
23988 : NS_IMETHODIMP
23989 0 : TransactionBase::
23990 : CommitOp::Run()
23991 : {
23992 0 : MOZ_ASSERT(mTransaction);
23993 0 : mTransaction->AssertIsOnConnectionThread();
23994 :
23995 0 : AUTO_PROFILER_LABEL("TransactionBase::CommitOp::Run", STORAGE);
23996 :
23997 0 : IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld] Request[%llu]: "
23998 : "Beginning database work",
23999 : "IndexedDB %s: P T[%lld] R[%llu]: DB Start",
24000 : IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
24001 : mTransaction->LoggingSerialNumber(),
24002 : mLoggingSerialNumber);
24003 :
24004 0 : if (mTransaction->GetMode() != IDBTransaction::READ_ONLY &&
24005 0 : mTransaction->mHasBeenActiveOnConnectionThread) {
24006 0 : Database* database = mTransaction->GetDatabase();
24007 0 : MOZ_ASSERT(database);
24008 :
24009 0 : if (DatabaseConnection* connection = database->GetConnection()) {
24010 : // May be null if the VersionChangeOp was canceled.
24011 : DatabaseConnection::UpdateRefcountFunction* fileRefcountFunction =
24012 0 : connection->GetUpdateRefcountFunction();
24013 :
24014 0 : if (NS_SUCCEEDED(mResultCode)) {
24015 0 : if (fileRefcountFunction) {
24016 0 : mResultCode = fileRefcountFunction->WillCommit();
24017 0 : NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode),
24018 : "WillCommit() failed!");
24019 : }
24020 :
24021 0 : if (NS_SUCCEEDED(mResultCode)) {
24022 0 : mResultCode = WriteAutoIncrementCounts();
24023 0 : NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode),
24024 : "WriteAutoIncrementCounts() failed!");
24025 :
24026 0 : if (NS_SUCCEEDED(mResultCode)) {
24027 0 : AssertForeignKeyConsistency(connection);
24028 :
24029 0 : mResultCode = connection->CommitWriteTransaction();
24030 0 : NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode), "Commit failed!");
24031 :
24032 0 : if (NS_SUCCEEDED(mResultCode) &&
24033 0 : mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH) {
24034 0 : mResultCode = connection->Checkpoint();
24035 : }
24036 :
24037 0 : if (NS_SUCCEEDED(mResultCode) && fileRefcountFunction) {
24038 0 : fileRefcountFunction->DidCommit();
24039 : }
24040 : }
24041 : }
24042 : }
24043 :
24044 0 : if (NS_FAILED(mResultCode)) {
24045 0 : if (fileRefcountFunction) {
24046 0 : fileRefcountFunction->DidAbort();
24047 : }
24048 :
24049 0 : connection->RollbackWriteTransaction();
24050 : }
24051 :
24052 0 : CommitOrRollbackAutoIncrementCounts();
24053 :
24054 0 : connection->FinishWriteTransaction();
24055 :
24056 0 : if (mTransaction->GetMode() == IDBTransaction::CLEANUP) {
24057 0 : connection->DoIdleProcessing(/* aNeedsCheckpoint */ true);
24058 :
24059 0 : connection->EnableQuotaChecks();
24060 : }
24061 : }
24062 : }
24063 :
24064 0 : IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld] Request[%llu]: "
24065 : "Finished database work",
24066 : "IndexedDB %s: P T[%lld] R[%llu]: DB End",
24067 : IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
24068 : mTransaction->LoggingSerialNumber(),
24069 : mLoggingSerialNumber);
24070 :
24071 0 : IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
24072 : "Finished database work",
24073 : "IndexedDB %s: P T[%lld]: DB End",
24074 : IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
24075 : mLoggingSerialNumber);
24076 :
24077 0 : return NS_OK;
24078 : }
24079 :
24080 : void
24081 0 : TransactionBase::
24082 : CommitOp::TransactionFinishedBeforeUnblock()
24083 : {
24084 0 : AssertIsOnBackgroundThread();
24085 0 : MOZ_ASSERT(mTransaction);
24086 :
24087 0 : AUTO_PROFILER_LABEL("CommitOp::TransactionFinishedBeforeUnblock", STORAGE);
24088 :
24089 0 : if (!IsActorDestroyed()) {
24090 0 : mTransaction->UpdateMetadata(mResultCode);
24091 : }
24092 0 : }
24093 :
24094 : void
24095 0 : TransactionBase::
24096 : CommitOp::TransactionFinishedAfterUnblock()
24097 : {
24098 0 : AssertIsOnBackgroundThread();
24099 0 : MOZ_ASSERT(mTransaction);
24100 :
24101 0 : IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
24102 : "Finished with result 0x%x",
24103 : "IndexedDB %s: P T[%lld]: Transaction finished (0x%x)",
24104 : IDB_LOG_ID_STRING(mTransaction->GetLoggingInfo()->Id()),
24105 : mTransaction->LoggingSerialNumber(),
24106 : mResultCode);
24107 :
24108 0 : mTransaction->SendCompleteNotification(ClampResultCode(mResultCode));
24109 :
24110 0 : Database* database = mTransaction->GetDatabase();
24111 0 : MOZ_ASSERT(database);
24112 :
24113 0 : database->UnregisterTransaction(mTransaction);
24114 :
24115 0 : mTransaction = nullptr;
24116 :
24117 : #ifdef DEBUG
24118 : // A bit hacky but the CommitOp is not really a normal database operation
24119 : // that is tied to an actor. Do this to make our assertions happy.
24120 0 : NoteActorDestroyed();
24121 : #endif
24122 0 : }
24123 :
24124 0 : DatabaseOp::DatabaseOp(Database* aDatabase)
24125 : : DatabaseOperationBase(aDatabase->GetLoggingInfo()->Id(),
24126 : aDatabase->GetLoggingInfo()->NextRequestSN())
24127 : , mDatabase(aDatabase)
24128 0 : , mState(State::Initial)
24129 : {
24130 0 : AssertIsOnBackgroundThread();
24131 0 : MOZ_ASSERT(aDatabase);
24132 0 : }
24133 :
24134 : nsresult
24135 0 : DatabaseOp::SendToIOThread()
24136 : {
24137 0 : AssertIsOnOwningThread();
24138 0 : MOZ_ASSERT(mState == State::Initial);
24139 :
24140 0 : if (!OperationMayProceed()) {
24141 0 : IDB_REPORT_INTERNAL_ERR();
24142 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24143 : }
24144 :
24145 0 : QuotaManager* quotaManager = QuotaManager::Get();
24146 0 : if (NS_WARN_IF(!quotaManager)) {
24147 0 : IDB_REPORT_INTERNAL_ERR();
24148 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24149 : }
24150 :
24151 : // Must set this before dispatching otherwise we will race with the IO thread.
24152 0 : mState = State::DatabaseWork;
24153 :
24154 0 : nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
24155 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24156 0 : IDB_REPORT_INTERNAL_ERR();
24157 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24158 : }
24159 :
24160 0 : return NS_OK;
24161 : }
24162 :
24163 : NS_IMETHODIMP
24164 0 : DatabaseOp::Run()
24165 : {
24166 : nsresult rv;
24167 :
24168 0 : switch (mState) {
24169 : case State::Initial:
24170 0 : rv = SendToIOThread();
24171 0 : break;
24172 :
24173 : case State::DatabaseWork:
24174 0 : rv = DoDatabaseWork();
24175 0 : break;
24176 :
24177 : case State::SendingResults:
24178 0 : SendResults();
24179 0 : return NS_OK;
24180 :
24181 : default:
24182 0 : MOZ_CRASH("Bad state!");
24183 : }
24184 :
24185 0 : if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
24186 0 : if (NS_SUCCEEDED(mResultCode)) {
24187 0 : mResultCode = rv;
24188 : }
24189 :
24190 : // Must set mState before dispatching otherwise we will race with the owning
24191 : // thread.
24192 0 : mState = State::SendingResults;
24193 :
24194 0 : MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
24195 : }
24196 :
24197 0 : return NS_OK;
24198 : }
24199 :
24200 : void
24201 0 : DatabaseOp::ActorDestroy(ActorDestroyReason aWhy)
24202 : {
24203 0 : AssertIsOnBackgroundThread();
24204 :
24205 0 : NoteActorDestroyed();
24206 0 : }
24207 :
24208 0 : CreateFileOp::CreateFileOp(Database* aDatabase,
24209 0 : const DatabaseRequestParams& aParams)
24210 : : DatabaseOp(aDatabase)
24211 0 : , mParams(aParams.get_CreateFileParams())
24212 : {
24213 0 : MOZ_ASSERT(aParams.type() == DatabaseRequestParams::TCreateFileParams);
24214 0 : }
24215 :
24216 : nsresult
24217 0 : CreateFileOp::CreateMutableFile(MutableFile** aMutableFile)
24218 : {
24219 0 : nsCOMPtr<nsIFile> file = GetFileForFileInfo(mFileInfo);
24220 0 : if (NS_WARN_IF(!file)) {
24221 0 : IDB_REPORT_INTERNAL_ERR();
24222 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24223 : }
24224 :
24225 : RefPtr<MutableFile> mutableFile =
24226 0 : MutableFile::Create(file, mDatabase, mFileInfo);
24227 0 : if (NS_WARN_IF(!mutableFile)) {
24228 0 : IDB_REPORT_INTERNAL_ERR();
24229 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24230 : }
24231 :
24232 : // Transfer ownership to IPDL.
24233 0 : mutableFile->SetActorAlive();
24234 :
24235 0 : if (!mDatabase->SendPBackgroundMutableFileConstructor(mutableFile,
24236 : mParams.name(),
24237 0 : mParams.type())) {
24238 0 : IDB_REPORT_INTERNAL_ERR();
24239 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24240 : }
24241 :
24242 0 : mutableFile.forget(aMutableFile);
24243 0 : return NS_OK;
24244 : }
24245 :
24246 : nsresult
24247 0 : CreateFileOp::DoDatabaseWork()
24248 : {
24249 0 : AssertIsOnIOThread();
24250 0 : MOZ_ASSERT(mState == State::DatabaseWork);
24251 :
24252 0 : AUTO_PROFILER_LABEL("CreateFileOp::DoDatabaseWork", STORAGE);
24253 :
24254 0 : if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
24255 0 : NS_WARNING("Refusing to create file because disk space is low!");
24256 0 : return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
24257 : }
24258 :
24259 0 : if (NS_WARN_IF(QuotaManager::IsShuttingDown()) || !OperationMayProceed()) {
24260 0 : IDB_REPORT_INTERNAL_ERR();
24261 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24262 : }
24263 :
24264 0 : FileManager* fileManager = mDatabase->GetFileManager();
24265 :
24266 0 : mFileInfo = fileManager->GetNewFileInfo();
24267 0 : if (NS_WARN_IF(!mFileInfo)) {
24268 0 : IDB_REPORT_INTERNAL_ERR();
24269 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24270 : }
24271 :
24272 0 : const int64_t fileId = mFileInfo->Id();
24273 :
24274 0 : nsCOMPtr<nsIFile> journalDirectory = fileManager->EnsureJournalDirectory();
24275 0 : if (NS_WARN_IF(!journalDirectory)) {
24276 0 : IDB_REPORT_INTERNAL_ERR();
24277 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24278 : }
24279 :
24280 : nsCOMPtr<nsIFile> journalFile =
24281 0 : fileManager->GetFileForId(journalDirectory, fileId);
24282 0 : if (NS_WARN_IF(!journalFile)) {
24283 0 : IDB_REPORT_INTERNAL_ERR();
24284 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24285 : }
24286 :
24287 0 : nsresult rv = journalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
24288 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24289 0 : return rv;
24290 : }
24291 :
24292 0 : nsCOMPtr<nsIFile> fileDirectory = fileManager->GetDirectory();
24293 0 : if (NS_WARN_IF(!fileDirectory)) {
24294 0 : IDB_REPORT_INTERNAL_ERR();
24295 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24296 : }
24297 :
24298 0 : nsCOMPtr<nsIFile> file = fileManager->GetFileForId(fileDirectory, fileId);
24299 0 : if (NS_WARN_IF(!file)) {
24300 0 : IDB_REPORT_INTERNAL_ERR();
24301 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24302 : }
24303 :
24304 0 : rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
24305 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24306 0 : return rv;
24307 : }
24308 :
24309 : // Must set mState before dispatching otherwise we will race with the owning
24310 : // thread.
24311 0 : mState = State::SendingResults;
24312 :
24313 0 : rv = mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
24314 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24315 0 : return rv;
24316 : }
24317 :
24318 0 : return NS_OK;
24319 : }
24320 :
24321 : void
24322 0 : CreateFileOp::SendResults()
24323 : {
24324 0 : AssertIsOnOwningThread();
24325 0 : MOZ_ASSERT(mState == State::SendingResults);
24326 :
24327 0 : if (!IsActorDestroyed() && !mDatabase->IsInvalidated()) {
24328 0 : DatabaseRequestResponse response;
24329 :
24330 0 : if (NS_SUCCEEDED(mResultCode)) {
24331 0 : RefPtr<MutableFile> mutableFile;
24332 0 : nsresult rv = CreateMutableFile(getter_AddRefs(mutableFile));
24333 0 : if (NS_SUCCEEDED(rv)) {
24334 : // We successfully created a mutable file so use its actor as the
24335 : // success result for this request.
24336 0 : CreateFileRequestResponse createResponse;
24337 0 : createResponse.mutableFileParent() = mutableFile;
24338 0 : response = createResponse;
24339 : } else {
24340 0 : response = ClampResultCode(rv);
24341 : #ifdef DEBUG
24342 0 : mResultCode = response.get_nsresult();
24343 : #endif
24344 : }
24345 : } else {
24346 0 : response = ClampResultCode(mResultCode);
24347 : }
24348 :
24349 : Unused <<
24350 0 : PBackgroundIDBDatabaseRequestParent::Send__delete__(this, response);
24351 : }
24352 :
24353 0 : mState = State::Completed;
24354 0 : }
24355 :
24356 : nsresult
24357 0 : VersionChangeTransactionOp::SendSuccessResult()
24358 : {
24359 0 : AssertIsOnOwningThread();
24360 :
24361 : // Nothing to send here, the API assumes that this request always succeeds.
24362 0 : return NS_OK;
24363 : }
24364 :
24365 : bool
24366 0 : VersionChangeTransactionOp::SendFailureResult(nsresult aResultCode)
24367 : {
24368 0 : AssertIsOnOwningThread();
24369 :
24370 : // The only option here is to cause the transaction to abort.
24371 0 : return false;
24372 : }
24373 :
24374 : void
24375 0 : VersionChangeTransactionOp::Cleanup()
24376 : {
24377 0 : AssertIsOnOwningThread();
24378 :
24379 : #ifdef DEBUG
24380 : // A bit hacky but the VersionChangeTransactionOp is not generated in response
24381 : // to a child request like most other database operations. Do this to make our
24382 : // assertions happy.
24383 0 : NoteActorDestroyed();
24384 : #endif
24385 :
24386 0 : TransactionDatabaseOperationBase::Cleanup();
24387 0 : }
24388 :
24389 : nsresult
24390 0 : CreateObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection)
24391 : {
24392 0 : MOZ_ASSERT(aConnection);
24393 0 : aConnection->AssertIsOnConnectionThread();
24394 :
24395 0 : AUTO_PROFILER_LABEL("CreateObjectStoreOp::DoDatabaseWork", STORAGE);
24396 :
24397 0 : if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
24398 0 : return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
24399 : }
24400 :
24401 : #ifdef DEBUG
24402 : {
24403 : // Make sure that we're not creating an object store with the same name as
24404 : // another that already exists. This should be impossible because we should
24405 : // have thrown an error long before now...
24406 0 : DatabaseConnection::CachedStatement stmt;
24407 0 : MOZ_ALWAYS_SUCCEEDS(
24408 : aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24409 : "SELECT name "
24410 : "FROM object_store "
24411 : "WHERE name = :name;"),
24412 : &stmt));
24413 :
24414 0 : MOZ_ALWAYS_SUCCEEDS(
24415 : stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mMetadata.name()));
24416 :
24417 : bool hasResult;
24418 0 : MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
24419 0 : MOZ_ASSERT(!hasResult);
24420 : }
24421 : #endif
24422 :
24423 0 : DatabaseConnection::AutoSavepoint autoSave;
24424 0 : nsresult rv = autoSave.Start(Transaction());
24425 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24426 0 : return rv;
24427 : }
24428 :
24429 0 : DatabaseConnection::CachedStatement stmt;
24430 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24431 : "INSERT INTO object_store (id, auto_increment, name, key_path) "
24432 : "VALUES (:id, :auto_increment, :name, :key_path);"),
24433 0 : &stmt);
24434 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24435 0 : return rv;
24436 : }
24437 :
24438 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mMetadata.id());
24439 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24440 0 : return rv;
24441 : }
24442 :
24443 0 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"),
24444 0 : mMetadata.autoIncrement() ? 1 : 0);
24445 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24446 0 : return rv;
24447 : }
24448 :
24449 0 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mMetadata.name());
24450 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24451 0 : return rv;
24452 : }
24453 :
24454 0 : NS_NAMED_LITERAL_CSTRING(keyPath, "key_path");
24455 :
24456 0 : if (mMetadata.keyPath().IsValid()) {
24457 0 : nsAutoString keyPathSerialization;
24458 0 : mMetadata.keyPath().SerializeToString(keyPathSerialization);
24459 :
24460 0 : rv = stmt->BindStringByName(keyPath, keyPathSerialization);
24461 : } else {
24462 0 : rv = stmt->BindNullByName(keyPath);
24463 : }
24464 :
24465 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24466 0 : return rv;
24467 : }
24468 :
24469 0 : rv = stmt->Execute();
24470 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24471 0 : return rv;
24472 : }
24473 :
24474 : #ifdef DEBUG
24475 : {
24476 : int64_t id;
24477 0 : MOZ_ALWAYS_SUCCEEDS(
24478 : aConnection->GetStorageConnection()->GetLastInsertRowID(&id));
24479 0 : MOZ_ASSERT(mMetadata.id() == id);
24480 : }
24481 : #endif
24482 :
24483 0 : rv = autoSave.Commit();
24484 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24485 0 : return rv;
24486 : }
24487 :
24488 0 : return NS_OK;
24489 : }
24490 :
24491 : nsresult
24492 0 : DeleteObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection)
24493 : {
24494 0 : MOZ_ASSERT(aConnection);
24495 0 : aConnection->AssertIsOnConnectionThread();
24496 :
24497 0 : AUTO_PROFILER_LABEL("DeleteObjectStoreOp::DoDatabaseWork", STORAGE);
24498 :
24499 0 : NS_NAMED_LITERAL_CSTRING(objectStoreIdString, "object_store_id");
24500 :
24501 : #ifdef DEBUG
24502 : {
24503 : // Make sure |mIsLastObjectStore| is telling the truth.
24504 0 : DatabaseConnection::CachedStatement stmt;
24505 0 : MOZ_ALWAYS_SUCCEEDS(
24506 : aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24507 : "SELECT id "
24508 : "FROM object_store;"),
24509 : &stmt));
24510 :
24511 0 : bool foundThisObjectStore = false;
24512 0 : bool foundOtherObjectStore = false;
24513 :
24514 : while (true) {
24515 : bool hasResult;
24516 0 : MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
24517 :
24518 0 : if (!hasResult) {
24519 0 : break;
24520 : }
24521 :
24522 : int64_t id;
24523 0 : MOZ_ALWAYS_SUCCEEDS(stmt->GetInt64(0, &id));
24524 :
24525 0 : if (id == mMetadata->mCommonMetadata.id()) {
24526 0 : foundThisObjectStore = true;
24527 : } else {
24528 0 : foundOtherObjectStore = true;
24529 : }
24530 0 : }
24531 :
24532 0 : MOZ_ASSERT_IF(mIsLastObjectStore,
24533 : foundThisObjectStore && !foundOtherObjectStore);
24534 0 : MOZ_ASSERT_IF(!mIsLastObjectStore,
24535 : foundThisObjectStore && foundOtherObjectStore);
24536 : }
24537 : #endif
24538 :
24539 0 : DatabaseConnection::AutoSavepoint autoSave;
24540 0 : nsresult rv = autoSave.Start(Transaction());
24541 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24542 0 : return rv;
24543 : }
24544 :
24545 0 : if (mIsLastObjectStore) {
24546 : // We can just delete everything if this is the last object store.
24547 0 : DatabaseConnection::CachedStatement stmt;
24548 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24549 : "DELETE FROM index_data;"),
24550 0 : &stmt);
24551 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24552 0 : return rv;
24553 : }
24554 :
24555 0 : rv = stmt->Execute();
24556 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24557 0 : return rv;
24558 : }
24559 :
24560 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24561 : "DELETE FROM unique_index_data;"),
24562 0 : &stmt);
24563 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24564 0 : return rv;
24565 : }
24566 :
24567 0 : rv = stmt->Execute();
24568 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24569 0 : return rv;
24570 : }
24571 :
24572 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24573 : "DELETE FROM object_data;"),
24574 0 : &stmt);
24575 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24576 0 : return rv;
24577 : }
24578 :
24579 0 : rv = stmt->Execute();
24580 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24581 0 : return rv;
24582 : }
24583 :
24584 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24585 : "DELETE FROM object_store_index;"),
24586 0 : &stmt);
24587 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24588 0 : return rv;
24589 : }
24590 :
24591 0 : rv = stmt->Execute();
24592 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24593 0 : return rv;
24594 : }
24595 :
24596 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24597 : "DELETE FROM object_store;"),
24598 0 : &stmt);
24599 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24600 0 : return rv;
24601 : }
24602 :
24603 0 : rv = stmt->Execute();
24604 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24605 0 : return rv;
24606 : }
24607 : } else {
24608 : bool hasIndexes;
24609 0 : rv = ObjectStoreHasIndexes(aConnection,
24610 0 : mMetadata->mCommonMetadata.id(),
24611 0 : &hasIndexes);
24612 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24613 0 : return rv;
24614 : }
24615 :
24616 0 : if (hasIndexes) {
24617 0 : rv = DeleteObjectStoreDataTableRowsWithIndexes(
24618 : aConnection,
24619 0 : mMetadata->mCommonMetadata.id(),
24620 0 : void_t());
24621 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24622 0 : return rv;
24623 : }
24624 :
24625 : // Now clean up the object store index table.
24626 0 : DatabaseConnection::CachedStatement stmt;
24627 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24628 : "DELETE FROM object_store_index "
24629 : "WHERE object_store_id = :object_store_id;"),
24630 0 : &stmt);
24631 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24632 0 : return rv;
24633 : }
24634 :
24635 0 : rv = stmt->BindInt64ByName(objectStoreIdString,
24636 0 : mMetadata->mCommonMetadata.id());
24637 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24638 0 : return rv;
24639 : }
24640 :
24641 0 : rv = stmt->Execute();
24642 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24643 0 : return rv;
24644 : }
24645 : } else {
24646 : // We only have to worry about object data if this object store has no
24647 : // indexes.
24648 0 : DatabaseConnection::CachedStatement stmt;
24649 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24650 : "DELETE FROM object_data "
24651 : "WHERE object_store_id = :object_store_id;"),
24652 0 : &stmt);
24653 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24654 0 : return rv;
24655 : }
24656 :
24657 0 : rv = stmt->BindInt64ByName(objectStoreIdString,
24658 0 : mMetadata->mCommonMetadata.id());
24659 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24660 0 : return rv;
24661 : }
24662 :
24663 0 : rv = stmt->Execute();
24664 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24665 0 : return rv;
24666 : }
24667 : }
24668 :
24669 0 : DatabaseConnection::CachedStatement stmt;
24670 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24671 : "DELETE FROM object_store "
24672 : "WHERE id = :object_store_id;"),
24673 0 : &stmt);
24674 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24675 0 : return rv;
24676 : }
24677 :
24678 0 : rv = stmt->BindInt64ByName(objectStoreIdString,
24679 0 : mMetadata->mCommonMetadata.id());
24680 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24681 0 : return rv;
24682 : }
24683 :
24684 0 : rv = stmt->Execute();
24685 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24686 0 : return rv;
24687 : }
24688 :
24689 : #ifdef DEBUG
24690 : {
24691 : int32_t deletedRowCount;
24692 0 : MOZ_ALWAYS_SUCCEEDS(
24693 : aConnection->GetStorageConnection()->
24694 : GetAffectedRows(&deletedRowCount));
24695 0 : MOZ_ASSERT(deletedRowCount == 1);
24696 : }
24697 : #endif
24698 : }
24699 :
24700 0 : rv = autoSave.Commit();
24701 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24702 0 : return rv;
24703 : }
24704 :
24705 0 : if (mMetadata->mCommonMetadata.autoIncrement()) {
24706 0 : Transaction()->ForgetModifiedAutoIncrementObjectStore(mMetadata);
24707 : }
24708 :
24709 0 : return NS_OK;
24710 : }
24711 :
24712 : nsresult
24713 0 : RenameObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection)
24714 : {
24715 0 : MOZ_ASSERT(aConnection);
24716 0 : aConnection->AssertIsOnConnectionThread();
24717 :
24718 0 : AUTO_PROFILER_LABEL("RenameObjectStoreOp::DoDatabaseWork", STORAGE);
24719 :
24720 0 : if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
24721 0 : return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
24722 : }
24723 :
24724 : #ifdef DEBUG
24725 : {
24726 : // Make sure that we're not renaming an object store with the same name as
24727 : // another that already exists. This should be impossible because we should
24728 : // have thrown an error long before now...
24729 0 : DatabaseConnection::CachedStatement stmt;
24730 0 : MOZ_ALWAYS_SUCCEEDS(
24731 : aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24732 : "SELECT name "
24733 : "FROM object_store "
24734 : "WHERE name = :name "
24735 : "AND id != :id;"),
24736 : &stmt));
24737 :
24738 0 : MOZ_ALWAYS_SUCCEEDS(
24739 : stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName));
24740 :
24741 0 : MOZ_ALWAYS_SUCCEEDS(
24742 : stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mId));
24743 :
24744 : bool hasResult;
24745 0 : MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
24746 0 : MOZ_ASSERT(!hasResult);
24747 : }
24748 : #endif
24749 :
24750 0 : DatabaseConnection::AutoSavepoint autoSave;
24751 0 : nsresult rv = autoSave.Start(Transaction());
24752 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24753 0 : return rv;
24754 : }
24755 :
24756 0 : DatabaseConnection::CachedStatement stmt;
24757 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24758 : "UPDATE object_store "
24759 : "SET name = :name "
24760 : "WHERE id = :id;"),
24761 0 : &stmt);
24762 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24763 0 : return rv;
24764 : }
24765 :
24766 0 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName);
24767 :
24768 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24769 0 : return rv;
24770 : }
24771 :
24772 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mId);
24773 :
24774 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24775 0 : return rv;
24776 : }
24777 :
24778 0 : rv = stmt->Execute();
24779 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24780 0 : return rv;
24781 : }
24782 :
24783 0 : rv = autoSave.Commit();
24784 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24785 0 : return rv;
24786 : }
24787 :
24788 0 : return NS_OK;
24789 : }
24790 :
24791 0 : CreateIndexOp::CreateIndexOp(VersionChangeTransaction* aTransaction,
24792 : const int64_t aObjectStoreId,
24793 0 : const IndexMetadata& aMetadata)
24794 : : VersionChangeTransactionOp(aTransaction)
24795 : , mMetadata(aMetadata)
24796 : , mFileManager(aTransaction->GetDatabase()->GetFileManager())
24797 0 : , mDatabaseId(aTransaction->DatabaseId())
24798 0 : , mObjectStoreId(aObjectStoreId)
24799 : {
24800 0 : MOZ_ASSERT(aObjectStoreId);
24801 0 : MOZ_ASSERT(aMetadata.id());
24802 0 : MOZ_ASSERT(mFileManager);
24803 0 : MOZ_ASSERT(!mDatabaseId.IsEmpty());
24804 0 : }
24805 :
24806 : unsigned int CreateIndexOp::sThreadLocalIndex = kBadThreadLocalIndex;
24807 :
24808 : nsresult
24809 0 : CreateIndexOp::InsertDataFromObjectStore(DatabaseConnection* aConnection)
24810 : {
24811 0 : MOZ_ASSERT(aConnection);
24812 0 : aConnection->AssertIsOnConnectionThread();
24813 0 : MOZ_ASSERT(!IndexedDatabaseManager::InLowDiskSpaceMode());
24814 0 : MOZ_ASSERT(mMaybeUniqueIndexTable);
24815 :
24816 0 : AUTO_PROFILER_LABEL("CreateIndexOp::InsertDataFromObjectStore", STORAGE);
24817 :
24818 : nsCOMPtr<mozIStorageConnection> storageConnection =
24819 0 : aConnection->GetStorageConnection();
24820 0 : MOZ_ASSERT(storageConnection);
24821 :
24822 0 : ThreadLocalJSContext* context = ThreadLocalJSContext::GetOrCreate();
24823 0 : if (NS_WARN_IF(!context)) {
24824 0 : IDB_REPORT_INTERNAL_ERR();
24825 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24826 : }
24827 :
24828 0 : JSContext* cx = context->Context();
24829 0 : JSAutoRequest ar(cx);
24830 0 : JSAutoCompartment ac(cx, context->Global());
24831 :
24832 : RefPtr<UpdateIndexDataValuesFunction> updateFunction =
24833 0 : new UpdateIndexDataValuesFunction(this, aConnection, cx);
24834 :
24835 0 : NS_NAMED_LITERAL_CSTRING(updateFunctionName, "update_index_data_values");
24836 :
24837 : nsresult rv =
24838 0 : storageConnection->CreateFunction(updateFunctionName,
24839 : 4,
24840 0 : updateFunction);
24841 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24842 0 : return rv;
24843 : }
24844 :
24845 0 : rv = InsertDataFromObjectStoreInternal(aConnection);
24846 :
24847 0 : MOZ_ALWAYS_SUCCEEDS(storageConnection->RemoveFunction(updateFunctionName));
24848 :
24849 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24850 0 : return rv;
24851 : }
24852 :
24853 0 : return NS_OK;
24854 : }
24855 :
24856 : nsresult
24857 0 : CreateIndexOp::InsertDataFromObjectStoreInternal(
24858 : DatabaseConnection* aConnection)
24859 : {
24860 0 : MOZ_ASSERT(aConnection);
24861 0 : aConnection->AssertIsOnConnectionThread();
24862 0 : MOZ_ASSERT(!IndexedDatabaseManager::InLowDiskSpaceMode());
24863 0 : MOZ_ASSERT(mMaybeUniqueIndexTable);
24864 :
24865 0 : DebugOnly<void*> storageConnection = aConnection->GetStorageConnection();
24866 0 : MOZ_ASSERT(storageConnection);
24867 :
24868 0 : DatabaseConnection::CachedStatement stmt;
24869 0 : nsresult rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24870 : "UPDATE object_data "
24871 : "SET index_data_values = update_index_data_values "
24872 : "(key, index_data_values, file_ids, data) "
24873 : "WHERE object_store_id = :object_store_id;"),
24874 0 : &stmt);
24875 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24876 0 : return rv;
24877 : }
24878 :
24879 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
24880 0 : mObjectStoreId);
24881 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24882 0 : return rv;
24883 : }
24884 :
24885 0 : rv = stmt->Execute();
24886 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24887 0 : return rv;
24888 : }
24889 :
24890 0 : return NS_OK;
24891 : }
24892 :
24893 : bool
24894 0 : CreateIndexOp::Init(TransactionBase* aTransaction)
24895 : {
24896 0 : AssertIsOnBackgroundThread();
24897 0 : MOZ_ASSERT(aTransaction);
24898 :
24899 : struct MOZ_STACK_CLASS Helper final
24900 : {
24901 : static void
24902 0 : Destroy(void* aThreadLocal)
24903 : {
24904 0 : delete static_cast<ThreadLocalJSContext*>(aThreadLocal);
24905 0 : }
24906 : };
24907 :
24908 0 : if (sThreadLocalIndex == kBadThreadLocalIndex) {
24909 0 : if (NS_WARN_IF(PR_SUCCESS !=
24910 : PR_NewThreadPrivateIndex(&sThreadLocalIndex,
24911 : &Helper::Destroy))) {
24912 0 : return false;
24913 : }
24914 : }
24915 :
24916 0 : MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
24917 :
24918 : nsresult rv =
24919 0 : GetUniqueIndexTableForObjectStore(aTransaction,
24920 0 : mObjectStoreId,
24921 0 : mMaybeUniqueIndexTable);
24922 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24923 0 : return false;
24924 : }
24925 :
24926 0 : return true;
24927 : }
24928 :
24929 : nsresult
24930 0 : CreateIndexOp::DoDatabaseWork(DatabaseConnection* aConnection)
24931 : {
24932 0 : MOZ_ASSERT(aConnection);
24933 0 : aConnection->AssertIsOnConnectionThread();
24934 :
24935 0 : AUTO_PROFILER_LABEL("CreateIndexOp::DoDatabaseWork", STORAGE);
24936 :
24937 0 : if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
24938 0 : return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
24939 : }
24940 :
24941 : #ifdef DEBUG
24942 : {
24943 : // Make sure that we're not creating an index with the same name and object
24944 : // store as another that already exists. This should be impossible because
24945 : // we should have thrown an error long before now...
24946 0 : DatabaseConnection::CachedStatement stmt;
24947 0 : MOZ_ALWAYS_SUCCEEDS(
24948 : aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24949 : "SELECT name "
24950 : "FROM object_store_index "
24951 : "WHERE object_store_id = :osid "
24952 : "AND name = :name;"),
24953 : &stmt));
24954 0 : MOZ_ALWAYS_SUCCEEDS(
24955 : stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId));
24956 0 : MOZ_ALWAYS_SUCCEEDS(
24957 : stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mMetadata.name()));
24958 :
24959 : bool hasResult;
24960 0 : MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
24961 :
24962 0 : MOZ_ASSERT(!hasResult);
24963 : }
24964 : #endif
24965 :
24966 0 : DatabaseConnection::AutoSavepoint autoSave;
24967 0 : nsresult rv = autoSave.Start(Transaction());
24968 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24969 0 : return rv;
24970 : }
24971 :
24972 0 : DatabaseConnection::CachedStatement stmt;
24973 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24974 : "INSERT INTO object_store_index (id, name, key_path, unique_index, "
24975 : "multientry, object_store_id, locale, "
24976 : "is_auto_locale) "
24977 : "VALUES (:id, :name, :key_path, :unique, :multientry, :osid, :locale, "
24978 : ":is_auto_locale)"),
24979 0 : &stmt);
24980 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24981 0 : return rv;
24982 : }
24983 :
24984 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mMetadata.id());
24985 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24986 0 : return rv;
24987 : }
24988 :
24989 0 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mMetadata.name());
24990 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24991 0 : return rv;
24992 : }
24993 :
24994 0 : nsAutoString keyPathSerialization;
24995 0 : mMetadata.keyPath().SerializeToString(keyPathSerialization);
24996 0 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
24997 0 : keyPathSerialization);
24998 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
24999 0 : return rv;
25000 : }
25001 :
25002 0 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("unique"),
25003 0 : mMetadata.unique() ? 1 : 0);
25004 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25005 0 : return rv;
25006 : }
25007 :
25008 0 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("multientry"),
25009 0 : mMetadata.multiEntry() ? 1 : 0);
25010 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25011 0 : return rv;
25012 : }
25013 :
25014 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId);
25015 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25016 0 : return rv;
25017 : }
25018 :
25019 0 : if (mMetadata.locale().IsEmpty()) {
25020 0 : rv = stmt->BindNullByName(NS_LITERAL_CSTRING("locale"));
25021 : } else {
25022 0 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("locale"),
25023 0 : mMetadata.locale());
25024 : }
25025 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25026 0 : return rv;
25027 : }
25028 :
25029 0 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("is_auto_locale"),
25030 0 : mMetadata.autoLocale());
25031 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25032 0 : return rv;
25033 : }
25034 :
25035 0 : rv = stmt->Execute();
25036 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25037 0 : return rv;
25038 : }
25039 :
25040 : #ifdef DEBUG
25041 : {
25042 : int64_t id;
25043 0 : MOZ_ALWAYS_SUCCEEDS(
25044 : aConnection->GetStorageConnection()->GetLastInsertRowID(&id));
25045 0 : MOZ_ASSERT(mMetadata.id() == id);
25046 : }
25047 : #endif
25048 :
25049 0 : rv = InsertDataFromObjectStore(aConnection);
25050 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25051 0 : return rv;
25052 : }
25053 :
25054 0 : rv = autoSave.Commit();
25055 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25056 0 : return rv;
25057 : }
25058 :
25059 0 : return NS_OK;
25060 : }
25061 :
25062 : static const JSClassOps sNormalJSContextGlobalClassOps = {
25063 : /* addProperty */ nullptr,
25064 : /* delProperty */ nullptr,
25065 : /* getProperty */ nullptr,
25066 : /* setProperty */ nullptr,
25067 : /* enumerate */ nullptr,
25068 : /* newEnumerate */ nullptr,
25069 : /* resolve */ nullptr,
25070 : /* mayResolve */ nullptr,
25071 : /* finalize */ nullptr,
25072 : /* call */ nullptr,
25073 : /* hasInstance */ nullptr,
25074 : /* construct */ nullptr,
25075 : /* trace */ JS_GlobalObjectTraceHook
25076 : };
25077 :
25078 : const JSClass NormalJSContext::sGlobalClass = {
25079 : "IndexedDBTransactionThreadGlobal",
25080 : JSCLASS_GLOBAL_FLAGS,
25081 : &sNormalJSContextGlobalClassOps
25082 : };
25083 :
25084 : bool
25085 0 : NormalJSContext::Init()
25086 : {
25087 0 : MOZ_ASSERT(!IsOnBackgroundThread());
25088 :
25089 0 : mContext = JS_NewContext(kContextHeapSize);
25090 0 : if (NS_WARN_IF(!mContext)) {
25091 0 : return false;
25092 : }
25093 :
25094 : // Let everyone know that we might be able to call JS. This alerts the
25095 : // profiler about certain possible deadlocks.
25096 0 : NS_GetCurrentThread()->SetCanInvokeJS(true);
25097 :
25098 : // Not setting this will cause JS_CHECK_RECURSION to report false positives.
25099 0 : JS_SetNativeStackQuota(mContext, 128 * sizeof(size_t) * 1024);
25100 :
25101 0 : if (NS_WARN_IF(!JS::InitSelfHostedCode(mContext))) {
25102 0 : return false;
25103 : }
25104 :
25105 0 : JSAutoRequest ar(mContext);
25106 :
25107 0 : JS::CompartmentOptions options;
25108 0 : mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr,
25109 : JS::FireOnNewGlobalHook, options);
25110 0 : if (NS_WARN_IF(!mGlobal)) {
25111 0 : return false;
25112 : }
25113 :
25114 0 : return true;
25115 : }
25116 :
25117 : // static
25118 : NormalJSContext*
25119 0 : NormalJSContext::Create()
25120 : {
25121 0 : MOZ_ASSERT(!IsOnBackgroundThread());
25122 :
25123 0 : nsAutoPtr<NormalJSContext> newContext(new NormalJSContext());
25124 :
25125 0 : if (NS_WARN_IF(!newContext->Init())) {
25126 0 : return nullptr;
25127 : }
25128 :
25129 0 : return newContext.forget();
25130 : }
25131 :
25132 : // static
25133 : auto
25134 0 : CreateIndexOp::
25135 : ThreadLocalJSContext::GetOrCreate() -> ThreadLocalJSContext*
25136 : {
25137 0 : MOZ_ASSERT(!IsOnBackgroundThread());
25138 0 : MOZ_ASSERT(CreateIndexOp::kBadThreadLocalIndex !=
25139 : CreateIndexOp::sThreadLocalIndex);
25140 :
25141 : auto* context = static_cast<ThreadLocalJSContext*>(
25142 0 : PR_GetThreadPrivate(CreateIndexOp::sThreadLocalIndex));
25143 0 : if (context) {
25144 0 : return context;
25145 : }
25146 :
25147 0 : nsAutoPtr<ThreadLocalJSContext> newContext(new ThreadLocalJSContext());
25148 :
25149 0 : if (NS_WARN_IF(!newContext->Init())) {
25150 0 : return nullptr;
25151 : }
25152 :
25153 : DebugOnly<PRStatus> status =
25154 0 : PR_SetThreadPrivate(CreateIndexOp::sThreadLocalIndex, newContext);
25155 0 : MOZ_ASSERT(status == PR_SUCCESS);
25156 :
25157 0 : return newContext.forget();
25158 : }
25159 :
25160 0 : NS_IMPL_ISUPPORTS(CreateIndexOp::UpdateIndexDataValuesFunction,
25161 : mozIStorageFunction);
25162 :
25163 : NS_IMETHODIMP
25164 0 : CreateIndexOp::
25165 : UpdateIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aValues,
25166 : nsIVariant** _retval)
25167 : {
25168 0 : MOZ_ASSERT(aValues);
25169 0 : MOZ_ASSERT(_retval);
25170 0 : MOZ_ASSERT(mConnection);
25171 0 : mConnection->AssertIsOnConnectionThread();
25172 0 : MOZ_ASSERT(mOp);
25173 0 : MOZ_ASSERT(mCx);
25174 :
25175 0 : AUTO_PROFILER_LABEL(
25176 : "CreateIndexOp::UpdateIndexDataValuesFunction::OnFunctionCall", STORAGE);
25177 :
25178 : #ifdef DEBUG
25179 : {
25180 : uint32_t argCount;
25181 0 : MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
25182 0 : MOZ_ASSERT(argCount == 4); // key, index_data_values, file_ids, data
25183 :
25184 : int32_t valueType;
25185 0 : MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType));
25186 0 : MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
25187 :
25188 0 : MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(1, &valueType));
25189 0 : MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
25190 : valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
25191 :
25192 0 : MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(2, &valueType));
25193 0 : MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
25194 : valueType == mozIStorageValueArray::VALUE_TYPE_TEXT);
25195 :
25196 0 : MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(3, &valueType));
25197 0 : MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB ||
25198 : valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
25199 : }
25200 : #endif
25201 :
25202 0 : StructuredCloneReadInfo cloneInfo;
25203 : nsresult rv =
25204 0 : GetStructuredCloneReadInfoFromValueArray(aValues,
25205 : /* aDataIndex */ 3,
25206 : /* aFileIdsIndex */ 2,
25207 0 : mOp->mFileManager,
25208 0 : &cloneInfo);
25209 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25210 0 : return rv;
25211 : }
25212 :
25213 0 : JS::Rooted<JS::Value> clone(mCx);
25214 0 : if (NS_WARN_IF(!IDBObjectStore::DeserializeIndexValue(mCx,
25215 : cloneInfo,
25216 : &clone))) {
25217 0 : return NS_ERROR_DOM_DATA_CLONE_ERR;
25218 : }
25219 :
25220 0 : const IndexMetadata& metadata = mOp->mMetadata;
25221 0 : const int64_t& objectStoreId = mOp->mObjectStoreId;
25222 :
25223 0 : AutoTArray<IndexUpdateInfo, 32> updateInfos;
25224 0 : rv = IDBObjectStore::AppendIndexUpdateInfo(metadata.id(),
25225 0 : metadata.keyPath(),
25226 0 : metadata.unique(),
25227 0 : metadata.multiEntry(),
25228 : metadata.locale(),
25229 : mCx,
25230 : clone,
25231 0 : updateInfos);
25232 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25233 0 : return rv;
25234 : }
25235 :
25236 0 : if (updateInfos.IsEmpty()) {
25237 : // XXX See if we can do this without copying...
25238 :
25239 0 : nsCOMPtr<nsIVariant> unmodifiedValue;
25240 :
25241 : // No changes needed, just return the original value.
25242 : int32_t valueType;
25243 0 : rv = aValues->GetTypeOfIndex(1, &valueType);
25244 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25245 0 : return rv;
25246 : }
25247 :
25248 0 : MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
25249 : valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
25250 :
25251 0 : if (valueType == mozIStorageValueArray::VALUE_TYPE_NULL) {
25252 0 : unmodifiedValue = new storage::NullVariant();
25253 0 : unmodifiedValue.forget(_retval);
25254 0 : return NS_OK;
25255 : }
25256 :
25257 0 : MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
25258 :
25259 : const uint8_t* blobData;
25260 : uint32_t blobDataLength;
25261 0 : rv = aValues->GetSharedBlob(1, &blobDataLength, &blobData);
25262 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25263 0 : return rv;
25264 : }
25265 :
25266 : std::pair<uint8_t *, int> copiedBlobDataPair(
25267 0 : static_cast<uint8_t*>(malloc(blobDataLength)),
25268 0 : blobDataLength);
25269 :
25270 0 : if (!copiedBlobDataPair.first) {
25271 0 : IDB_REPORT_INTERNAL_ERR();
25272 0 : return NS_ERROR_OUT_OF_MEMORY;
25273 : }
25274 :
25275 0 : memcpy(copiedBlobDataPair.first, blobData, blobDataLength);
25276 :
25277 0 : unmodifiedValue = new storage::AdoptedBlobVariant(copiedBlobDataPair);
25278 0 : unmodifiedValue.forget(_retval);
25279 :
25280 0 : return NS_OK;
25281 : }
25282 :
25283 0 : Key key;
25284 0 : rv = key.SetFromValueArray(aValues, 0);
25285 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25286 0 : return rv;
25287 : }
25288 :
25289 0 : AutoTArray<IndexDataValue, 32> indexValues;
25290 0 : rv = ReadCompressedIndexDataValues(aValues, 1, indexValues);
25291 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25292 0 : return rv;
25293 : }
25294 :
25295 0 : const bool hadPreviousIndexValues = !indexValues.IsEmpty();
25296 :
25297 0 : const uint32_t updateInfoCount = updateInfos.Length();
25298 :
25299 0 : if (NS_WARN_IF(!indexValues.SetCapacity(indexValues.Length() +
25300 : updateInfoCount, fallible))) {
25301 0 : IDB_REPORT_INTERNAL_ERR();
25302 0 : return NS_ERROR_OUT_OF_MEMORY;
25303 : }
25304 :
25305 : // First construct the full list to update the index_data_values row.
25306 0 : for (uint32_t index = 0; index < updateInfoCount; index++) {
25307 0 : const IndexUpdateInfo& info = updateInfos[index];
25308 :
25309 0 : MOZ_ALWAYS_TRUE(
25310 : indexValues.InsertElementSorted(IndexDataValue(metadata.id(),
25311 : metadata.unique(),
25312 : info.value(),
25313 : info.localizedValue()),
25314 : fallible));
25315 : }
25316 :
25317 0 : UniqueFreePtr<uint8_t> indexValuesBlob;
25318 : uint32_t indexValuesBlobLength;
25319 0 : rv = MakeCompressedIndexDataValues(indexValues,
25320 : indexValuesBlob,
25321 0 : &indexValuesBlobLength);
25322 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25323 0 : return rv;
25324 : }
25325 :
25326 0 : MOZ_ASSERT(!indexValuesBlobLength == !(indexValuesBlob.get()));
25327 :
25328 0 : nsCOMPtr<nsIVariant> value;
25329 :
25330 0 : if (!indexValuesBlob) {
25331 0 : value = new storage::NullVariant();
25332 :
25333 0 : value.forget(_retval);
25334 0 : return NS_OK;
25335 : }
25336 :
25337 : // Now insert the new table rows. We only need to construct a new list if
25338 : // the full list is different.
25339 0 : if (hadPreviousIndexValues) {
25340 0 : indexValues.ClearAndRetainStorage();
25341 :
25342 0 : MOZ_ASSERT(indexValues.Capacity() >= updateInfoCount);
25343 :
25344 0 : for (uint32_t index = 0; index < updateInfoCount; index++) {
25345 0 : const IndexUpdateInfo& info = updateInfos[index];
25346 :
25347 0 : MOZ_ALWAYS_TRUE(
25348 : indexValues.InsertElementSorted(IndexDataValue(metadata.id(),
25349 : metadata.unique(),
25350 : info.value(),
25351 : info.localizedValue()),
25352 : fallible));
25353 : }
25354 : }
25355 :
25356 0 : rv = InsertIndexTableRows(mConnection, objectStoreId, key, indexValues);
25357 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25358 0 : return rv;
25359 : }
25360 :
25361 0 : std::pair<uint8_t *, int> copiedBlobDataPair(indexValuesBlob.release(),
25362 0 : indexValuesBlobLength);
25363 :
25364 0 : value = new storage::AdoptedBlobVariant(copiedBlobDataPair);
25365 :
25366 0 : value.forget(_retval);
25367 0 : return NS_OK;
25368 : }
25369 :
25370 0 : DeleteIndexOp::DeleteIndexOp(VersionChangeTransaction* aTransaction,
25371 : const int64_t aObjectStoreId,
25372 : const int64_t aIndexId,
25373 : const bool aUnique,
25374 0 : const bool aIsLastIndex)
25375 : : VersionChangeTransactionOp(aTransaction)
25376 : , mObjectStoreId(aObjectStoreId)
25377 : , mIndexId(aIndexId)
25378 : , mUnique(aUnique)
25379 0 : , mIsLastIndex(aIsLastIndex)
25380 : {
25381 0 : MOZ_ASSERT(aObjectStoreId);
25382 0 : MOZ_ASSERT(aIndexId);
25383 0 : }
25384 :
25385 : nsresult
25386 0 : DeleteIndexOp::RemoveReferencesToIndex(DatabaseConnection* aConnection,
25387 : const Key& aObjectStoreKey,
25388 : nsTArray<IndexDataValue>& aIndexValues)
25389 : {
25390 0 : MOZ_ASSERT(!NS_IsMainThread());
25391 0 : MOZ_ASSERT(!IsOnBackgroundThread());
25392 0 : MOZ_ASSERT(aConnection);
25393 0 : MOZ_ASSERT(!aObjectStoreKey.IsUnset());
25394 0 : MOZ_ASSERT_IF(!mIsLastIndex, !aIndexValues.IsEmpty());
25395 :
25396 : struct MOZ_STACK_CLASS IndexIdComparator final
25397 : {
25398 : bool
25399 0 : Equals(const IndexDataValue& aA, const IndexDataValue& aB) const
25400 : {
25401 : // Ignore everything but the index id.
25402 0 : return aA.mIndexId == aB.mIndexId;
25403 : };
25404 :
25405 : bool
25406 0 : LessThan(const IndexDataValue& aA, const IndexDataValue& aB) const
25407 : {
25408 0 : return aA.mIndexId < aB.mIndexId;
25409 : };
25410 : };
25411 :
25412 0 : AUTO_PROFILER_LABEL("DeleteIndexOp::RemoveReferencesToIndex", STORAGE);
25413 :
25414 0 : if (mIsLastIndex) {
25415 : // There is no need to parse the previous entry in the index_data_values
25416 : // column if this is the last index. Simply set it to NULL.
25417 0 : DatabaseConnection::CachedStatement stmt;
25418 0 : nsresult rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25419 : "UPDATE object_data "
25420 : "SET index_data_values = NULL "
25421 : "WHERE object_store_id = :object_store_id "
25422 : "AND key = :key;"),
25423 0 : &stmt);
25424 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25425 0 : return rv;
25426 : }
25427 :
25428 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
25429 0 : mObjectStoreId);
25430 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25431 0 : return rv;
25432 : }
25433 :
25434 0 : rv = aObjectStoreKey.BindToStatement(stmt, NS_LITERAL_CSTRING("key"));
25435 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25436 0 : return rv;
25437 : }
25438 :
25439 0 : rv = stmt->Execute();
25440 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25441 0 : return rv;
25442 : }
25443 :
25444 0 : return NS_OK;
25445 : }
25446 :
25447 0 : IndexDataValue search;
25448 0 : search.mIndexId = mIndexId;
25449 :
25450 : // This returns the first element that matches our index id found during a
25451 : // binary search. However, there could still be other elements before that.
25452 : size_t firstElementIndex =
25453 0 : aIndexValues.BinaryIndexOf(search, IndexIdComparator());
25454 0 : if (NS_WARN_IF(firstElementIndex == aIndexValues.NoIndex) ||
25455 0 : NS_WARN_IF(aIndexValues[firstElementIndex].mIndexId != mIndexId)) {
25456 0 : IDB_REPORT_INTERNAL_ERR();
25457 0 : return NS_ERROR_FILE_CORRUPTED;
25458 : }
25459 :
25460 0 : MOZ_ASSERT(aIndexValues[firstElementIndex].mIndexId == mIndexId);
25461 :
25462 : // Walk backwards to find the real first index.
25463 0 : while (firstElementIndex) {
25464 0 : if (aIndexValues[firstElementIndex - 1].mIndexId == mIndexId) {
25465 0 : firstElementIndex--;
25466 : } else {
25467 0 : break;
25468 : }
25469 : }
25470 :
25471 0 : MOZ_ASSERT(aIndexValues[firstElementIndex].mIndexId == mIndexId);
25472 :
25473 0 : const size_t indexValuesLength = aIndexValues.Length();
25474 :
25475 : // Find the last element with the same index id.
25476 0 : size_t lastElementIndex = firstElementIndex;
25477 :
25478 0 : while (lastElementIndex < indexValuesLength) {
25479 0 : if (aIndexValues[lastElementIndex].mIndexId == mIndexId) {
25480 0 : lastElementIndex++;
25481 : } else {
25482 0 : break;
25483 : }
25484 : }
25485 :
25486 0 : MOZ_ASSERT(lastElementIndex > firstElementIndex);
25487 0 : MOZ_ASSERT_IF(lastElementIndex < indexValuesLength,
25488 : aIndexValues[lastElementIndex].mIndexId != mIndexId);
25489 0 : MOZ_ASSERT(aIndexValues[lastElementIndex - 1].mIndexId == mIndexId);
25490 :
25491 0 : aIndexValues.RemoveElementsAt(firstElementIndex,
25492 0 : lastElementIndex - firstElementIndex);
25493 :
25494 0 : nsresult rv = UpdateIndexValues(aConnection,
25495 0 : mObjectStoreId,
25496 : aObjectStoreKey,
25497 0 : aIndexValues);
25498 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25499 0 : return rv;
25500 : }
25501 :
25502 0 : return NS_OK;
25503 : }
25504 :
25505 : nsresult
25506 0 : DeleteIndexOp::DoDatabaseWork(DatabaseConnection* aConnection)
25507 : {
25508 0 : MOZ_ASSERT(aConnection);
25509 0 : aConnection->AssertIsOnConnectionThread();
25510 :
25511 : #ifdef DEBUG
25512 : {
25513 : // Make sure |mIsLastIndex| is telling the truth.
25514 0 : DatabaseConnection::CachedStatement stmt;
25515 0 : MOZ_ALWAYS_SUCCEEDS(
25516 : aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25517 : "SELECT id "
25518 : "FROM object_store_index "
25519 : "WHERE object_store_id = :object_store_id;"),
25520 : &stmt));
25521 :
25522 0 : MOZ_ALWAYS_SUCCEEDS(
25523 : stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
25524 : mObjectStoreId));
25525 :
25526 0 : bool foundThisIndex = false;
25527 0 : bool foundOtherIndex = false;
25528 :
25529 : while (true) {
25530 : bool hasResult;
25531 0 : MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
25532 :
25533 0 : if (!hasResult) {
25534 0 : break;
25535 : }
25536 :
25537 : int64_t id;
25538 0 : MOZ_ALWAYS_SUCCEEDS(stmt->GetInt64(0, &id));
25539 :
25540 0 : if (id == mIndexId) {
25541 0 : foundThisIndex = true;
25542 : } else {
25543 0 : foundOtherIndex = true;
25544 : }
25545 0 : }
25546 :
25547 0 : MOZ_ASSERT_IF(mIsLastIndex, foundThisIndex && !foundOtherIndex);
25548 0 : MOZ_ASSERT_IF(!mIsLastIndex, foundThisIndex && foundOtherIndex);
25549 : }
25550 : #endif
25551 :
25552 0 : AUTO_PROFILER_LABEL("DeleteIndexOp::DoDatabaseWork", STORAGE);
25553 :
25554 0 : DatabaseConnection::AutoSavepoint autoSave;
25555 0 : nsresult rv = autoSave.Start(Transaction());
25556 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25557 0 : return rv;
25558 : }
25559 :
25560 0 : DatabaseConnection::CachedStatement selectStmt;
25561 :
25562 : // mozStorage warns that these statements trigger a sort operation but we
25563 : // don't care because this is a very rare call and we expect it to be slow.
25564 : // The cost of having an index on this field is too high.
25565 0 : if (mUnique) {
25566 0 : if (mIsLastIndex) {
25567 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25568 : "/* do not warn (bug someone else) */ "
25569 : "SELECT value, object_data_key "
25570 : "FROM unique_index_data "
25571 : "WHERE index_id = :index_id "
25572 : "ORDER BY object_data_key ASC;"),
25573 0 : &selectStmt);
25574 : } else {
25575 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25576 : "/* do not warn (bug out) */ "
25577 : "SELECT unique_index_data.value, "
25578 : "unique_index_data.object_data_key, "
25579 : "object_data.index_data_values "
25580 : "FROM unique_index_data "
25581 : "JOIN object_data "
25582 : "ON unique_index_data.object_data_key = object_data.key "
25583 : "WHERE unique_index_data.index_id = :index_id "
25584 : "AND object_data.object_store_id = :object_store_id "
25585 : "ORDER BY unique_index_data.object_data_key ASC;"),
25586 0 : &selectStmt);
25587 : }
25588 : } else {
25589 0 : if (mIsLastIndex) {
25590 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25591 : "/* do not warn (bug me not) */ "
25592 : "SELECT value, object_data_key "
25593 : "FROM index_data "
25594 : "WHERE index_id = :index_id "
25595 : "AND object_store_id = :object_store_id "
25596 : "ORDER BY object_data_key ASC;"),
25597 0 : &selectStmt);
25598 : } else {
25599 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25600 : "/* do not warn (bug off) */ "
25601 : "SELECT index_data.value, "
25602 : "index_data.object_data_key, "
25603 : "object_data.index_data_values "
25604 : "FROM index_data "
25605 : "JOIN object_data "
25606 : "ON index_data.object_data_key = object_data.key "
25607 : "WHERE index_data.index_id = :index_id "
25608 : "AND object_data.object_store_id = :object_store_id "
25609 : "ORDER BY index_data.object_data_key ASC;"),
25610 0 : &selectStmt);
25611 : }
25612 : }
25613 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25614 0 : return rv;
25615 : }
25616 :
25617 0 : NS_NAMED_LITERAL_CSTRING(indexIdString, "index_id");
25618 :
25619 0 : rv = selectStmt->BindInt64ByName(indexIdString, mIndexId);
25620 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25621 0 : return rv;
25622 : }
25623 :
25624 0 : if (!mUnique || !mIsLastIndex) {
25625 0 : rv = selectStmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
25626 0 : mObjectStoreId);
25627 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25628 0 : return rv;
25629 : }
25630 : }
25631 :
25632 0 : NS_NAMED_LITERAL_CSTRING(valueString, "value");
25633 0 : NS_NAMED_LITERAL_CSTRING(objectDataKeyString, "object_data_key");
25634 :
25635 0 : DatabaseConnection::CachedStatement deleteIndexRowStmt;
25636 0 : DatabaseConnection::CachedStatement nullIndexDataValuesStmt;
25637 :
25638 0 : Key lastObjectStoreKey;
25639 0 : AutoTArray<IndexDataValue, 32> lastIndexValues;
25640 :
25641 : bool hasResult;
25642 0 : while (NS_SUCCEEDED(rv = selectStmt->ExecuteStep(&hasResult)) && hasResult) {
25643 : // We always need the index key to delete the index row.
25644 0 : Key indexKey;
25645 0 : rv = indexKey.SetFromStatement(selectStmt, 0);
25646 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25647 0 : return rv;
25648 : }
25649 :
25650 0 : if (NS_WARN_IF(indexKey.IsUnset())) {
25651 0 : IDB_REPORT_INTERNAL_ERR();
25652 0 : return NS_ERROR_FILE_CORRUPTED;
25653 : }
25654 :
25655 : // Don't call |lastObjectStoreKey.BindToStatement()| directly because we
25656 : // don't want to copy the same key multiple times.
25657 : const uint8_t* objectStoreKeyData;
25658 : uint32_t objectStoreKeyDataLength;
25659 0 : rv = selectStmt->GetSharedBlob(1,
25660 : &objectStoreKeyDataLength,
25661 0 : &objectStoreKeyData);
25662 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25663 0 : return rv;
25664 : }
25665 :
25666 0 : if (NS_WARN_IF(!objectStoreKeyDataLength)) {
25667 0 : IDB_REPORT_INTERNAL_ERR();
25668 0 : return NS_ERROR_FILE_CORRUPTED;
25669 : }
25670 :
25671 : nsDependentCString currentObjectStoreKeyBuffer(
25672 : reinterpret_cast<const char*>(objectStoreKeyData),
25673 0 : objectStoreKeyDataLength);
25674 0 : if (currentObjectStoreKeyBuffer != lastObjectStoreKey.GetBuffer()) {
25675 : // We just walked to the next object store key.
25676 0 : if (!lastObjectStoreKey.IsUnset()) {
25677 : // Before we move on to the next key we need to update the previous
25678 : // key's index_data_values column.
25679 : rv = RemoveReferencesToIndex(aConnection,
25680 : lastObjectStoreKey,
25681 0 : lastIndexValues);
25682 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25683 0 : return rv;
25684 : }
25685 : }
25686 :
25687 : // Save the object store key.
25688 0 : lastObjectStoreKey = Key(currentObjectStoreKeyBuffer);
25689 :
25690 : // And the |index_data_values| row if this isn't the only index.
25691 0 : if (!mIsLastIndex) {
25692 0 : lastIndexValues.ClearAndRetainStorage();
25693 0 : rv = ReadCompressedIndexDataValues(selectStmt, 2, lastIndexValues);
25694 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25695 0 : return rv;
25696 : }
25697 :
25698 0 : if (NS_WARN_IF(lastIndexValues.IsEmpty())) {
25699 0 : IDB_REPORT_INTERNAL_ERR();
25700 0 : return NS_ERROR_FILE_CORRUPTED;
25701 : }
25702 : }
25703 : }
25704 :
25705 : // Now delete the index row.
25706 0 : if (deleteIndexRowStmt) {
25707 0 : MOZ_ALWAYS_SUCCEEDS(deleteIndexRowStmt->Reset());
25708 : } else {
25709 0 : if (mUnique) {
25710 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25711 : "DELETE FROM unique_index_data "
25712 : "WHERE index_id = :index_id "
25713 : "AND value = :value;"),
25714 0 : &deleteIndexRowStmt);
25715 : } else {
25716 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25717 : "DELETE FROM index_data "
25718 : "WHERE index_id = :index_id "
25719 : "AND value = :value "
25720 : "AND object_data_key = :object_data_key;"),
25721 0 : &deleteIndexRowStmt);
25722 : }
25723 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25724 0 : return rv;
25725 : }
25726 : }
25727 :
25728 0 : rv = deleteIndexRowStmt->BindInt64ByName(indexIdString, mIndexId);
25729 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25730 0 : return rv;
25731 : }
25732 :
25733 0 : rv = indexKey.BindToStatement(deleteIndexRowStmt, valueString);
25734 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25735 0 : return rv;
25736 : }
25737 :
25738 0 : if (!mUnique) {
25739 0 : rv = lastObjectStoreKey.BindToStatement(deleteIndexRowStmt,
25740 0 : objectDataKeyString);
25741 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25742 0 : return rv;
25743 : }
25744 : }
25745 :
25746 0 : rv = deleteIndexRowStmt->Execute();
25747 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25748 0 : return rv;
25749 : }
25750 : }
25751 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25752 0 : return rv;
25753 : }
25754 :
25755 : // Take care of the last key.
25756 0 : if (!lastObjectStoreKey.IsUnset()) {
25757 0 : MOZ_ASSERT_IF(!mIsLastIndex, !lastIndexValues.IsEmpty());
25758 :
25759 : rv = RemoveReferencesToIndex(aConnection,
25760 : lastObjectStoreKey,
25761 0 : lastIndexValues);
25762 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25763 0 : return rv;
25764 : }
25765 : }
25766 :
25767 0 : DatabaseConnection::CachedStatement deleteStmt;
25768 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25769 : "DELETE FROM object_store_index "
25770 : "WHERE id = :index_id;"),
25771 0 : &deleteStmt);
25772 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25773 0 : return rv;
25774 : }
25775 :
25776 0 : rv = deleteStmt->BindInt64ByName(indexIdString, mIndexId);
25777 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25778 0 : return rv;
25779 : }
25780 :
25781 0 : rv = deleteStmt->Execute();
25782 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25783 0 : return rv;
25784 : }
25785 :
25786 : #ifdef DEBUG
25787 : {
25788 : int32_t deletedRowCount;
25789 0 : MOZ_ALWAYS_SUCCEEDS(
25790 : aConnection->GetStorageConnection()->GetAffectedRows(&deletedRowCount));
25791 0 : MOZ_ASSERT(deletedRowCount == 1);
25792 : }
25793 : #endif
25794 :
25795 0 : rv = autoSave.Commit();
25796 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25797 0 : return rv;
25798 : }
25799 :
25800 0 : return NS_OK;
25801 : }
25802 :
25803 : nsresult
25804 0 : RenameIndexOp::DoDatabaseWork(DatabaseConnection* aConnection)
25805 : {
25806 0 : MOZ_ASSERT(aConnection);
25807 0 : aConnection->AssertIsOnConnectionThread();
25808 :
25809 0 : AUTO_PROFILER_LABEL("RenameIndexOp::DoDatabaseWork", STORAGE);
25810 :
25811 0 : if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
25812 0 : return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
25813 : }
25814 :
25815 : #ifdef DEBUG
25816 : {
25817 : // Make sure that we're not renaming an index with the same name as another
25818 : // that already exists. This should be impossible because we should have
25819 : // thrown an error long before now...
25820 0 : DatabaseConnection::CachedStatement stmt;
25821 0 : MOZ_ALWAYS_SUCCEEDS(
25822 : aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25823 : "SELECT name "
25824 : "FROM object_store_index "
25825 : "WHERE object_store_id = :object_store_id "
25826 : "AND name = :name "
25827 : "AND id != :id;"),
25828 : &stmt));
25829 :
25830 0 : MOZ_ALWAYS_SUCCEEDS(
25831 : stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
25832 : mObjectStoreId));
25833 :
25834 0 : MOZ_ALWAYS_SUCCEEDS(
25835 : stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName));
25836 :
25837 0 : MOZ_ALWAYS_SUCCEEDS(
25838 : stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndexId));
25839 :
25840 : bool hasResult;
25841 0 : MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
25842 0 : MOZ_ASSERT(!hasResult);
25843 : }
25844 : #else
25845 : Unused << mObjectStoreId;
25846 : #endif
25847 :
25848 0 : DatabaseConnection::AutoSavepoint autoSave;
25849 0 : nsresult rv = autoSave.Start(Transaction());
25850 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25851 0 : return rv;
25852 : }
25853 :
25854 0 : DatabaseConnection::CachedStatement stmt;
25855 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25856 : "UPDATE object_store_index "
25857 : "SET name = :name "
25858 : "WHERE id = :id;"),
25859 0 : &stmt);
25860 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25861 0 : return rv;
25862 : }
25863 :
25864 0 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName);
25865 :
25866 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25867 0 : return rv;
25868 : }
25869 :
25870 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndexId);
25871 :
25872 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25873 0 : return rv;
25874 : }
25875 :
25876 0 : rv = stmt->Execute();
25877 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25878 0 : return rv;
25879 : }
25880 :
25881 0 : rv = autoSave.Commit();
25882 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25883 0 : return rv;
25884 : }
25885 :
25886 :
25887 0 : return NS_OK;
25888 : }
25889 :
25890 : // static
25891 : nsresult
25892 0 : NormalTransactionOp::ObjectStoreHasIndexes(NormalTransactionOp* aOp,
25893 : DatabaseConnection* aConnection,
25894 : const int64_t aObjectStoreId,
25895 : const bool aMayHaveIndexes,
25896 : bool* aHasIndexes)
25897 : {
25898 0 : MOZ_ASSERT(aOp);
25899 0 : MOZ_ASSERT(aConnection);
25900 0 : aConnection->AssertIsOnConnectionThread();
25901 0 : MOZ_ASSERT(aObjectStoreId);
25902 0 : MOZ_ASSERT(aHasIndexes);
25903 :
25904 : bool hasIndexes;
25905 0 : if (aOp->Transaction()->GetMode() == IDBTransaction::VERSION_CHANGE &&
25906 : aMayHaveIndexes) {
25907 : // If this is a version change transaction then mObjectStoreMayHaveIndexes
25908 : // could be wrong (e.g. if a unique index failed to be created due to a
25909 : // constraint error). We have to check on this thread by asking the database
25910 : // directly.
25911 : nsresult rv =
25912 : DatabaseOperationBase::ObjectStoreHasIndexes(aConnection,
25913 : aObjectStoreId,
25914 0 : &hasIndexes);
25915 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25916 0 : return rv;
25917 : }
25918 : } else {
25919 0 : MOZ_ASSERT(NS_SUCCEEDED(
25920 : DatabaseOperationBase::ObjectStoreHasIndexes(aConnection,
25921 : aObjectStoreId,
25922 : &hasIndexes)));
25923 0 : MOZ_ASSERT(aMayHaveIndexes == hasIndexes);
25924 :
25925 0 : hasIndexes = aMayHaveIndexes;
25926 : }
25927 :
25928 0 : *aHasIndexes = hasIndexes;
25929 0 : return NS_OK;
25930 : }
25931 :
25932 : nsresult
25933 0 : NormalTransactionOp::GetPreprocessParams(PreprocessParams& aParams)
25934 : {
25935 0 : return NS_OK;
25936 : }
25937 :
25938 : nsresult
25939 0 : NormalTransactionOp::SendPreprocessInfo()
25940 : {
25941 0 : AssertIsOnOwningThread();
25942 0 : MOZ_ASSERT(!IsActorDestroyed());
25943 :
25944 0 : PreprocessParams params;
25945 0 : nsresult rv = GetPreprocessParams(params);
25946 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
25947 0 : return rv;
25948 : }
25949 :
25950 0 : MOZ_ASSERT(params.type() != PreprocessParams::T__None);
25951 :
25952 0 : if (NS_WARN_IF(!PBackgroundIDBRequestParent::SendPreprocess(params))) {
25953 0 : IDB_REPORT_INTERNAL_ERR();
25954 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
25955 : }
25956 :
25957 0 : return NS_OK;
25958 : }
25959 :
25960 : nsresult
25961 0 : NormalTransactionOp::SendSuccessResult()
25962 : {
25963 0 : AssertIsOnOwningThread();
25964 :
25965 0 : if (!IsActorDestroyed()) {
25966 0 : RequestResponse response;
25967 0 : GetResponse(response);
25968 :
25969 0 : MOZ_ASSERT(response.type() != RequestResponse::T__None);
25970 :
25971 0 : if (response.type() == RequestResponse::Tnsresult) {
25972 0 : MOZ_ASSERT(NS_FAILED(response.get_nsresult()));
25973 :
25974 0 : return response.get_nsresult();
25975 : }
25976 :
25977 0 : if (NS_WARN_IF(!PBackgroundIDBRequestParent::Send__delete__(this,
25978 : response))) {
25979 0 : IDB_REPORT_INTERNAL_ERR();
25980 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
25981 : }
25982 : }
25983 :
25984 : #ifdef DEBUG
25985 0 : mResponseSent = true;
25986 : #endif
25987 :
25988 0 : return NS_OK;
25989 : }
25990 :
25991 : bool
25992 0 : NormalTransactionOp::SendFailureResult(nsresult aResultCode)
25993 : {
25994 0 : AssertIsOnOwningThread();
25995 0 : MOZ_ASSERT(NS_FAILED(aResultCode));
25996 :
25997 0 : bool result = false;
25998 :
25999 0 : if (!IsActorDestroyed()) {
26000 : result =
26001 0 : PBackgroundIDBRequestParent::Send__delete__(this,
26002 0 : ClampResultCode(aResultCode));
26003 : }
26004 :
26005 : #ifdef DEBUG
26006 0 : mResponseSent = true;
26007 : #endif
26008 :
26009 0 : return result;
26010 : }
26011 :
26012 : void
26013 0 : NormalTransactionOp::Cleanup()
26014 : {
26015 0 : AssertIsOnOwningThread();
26016 0 : MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent);
26017 :
26018 0 : TransactionDatabaseOperationBase::Cleanup();
26019 0 : }
26020 :
26021 : void
26022 0 : NormalTransactionOp::ActorDestroy(ActorDestroyReason aWhy)
26023 : {
26024 0 : AssertIsOnOwningThread();
26025 :
26026 0 : NoteActorDestroyed();
26027 0 : }
26028 :
26029 : mozilla::ipc::IPCResult
26030 0 : NormalTransactionOp::RecvContinue(const PreprocessResponse& aResponse)
26031 : {
26032 0 : AssertIsOnOwningThread();
26033 :
26034 0 : switch (aResponse.type()) {
26035 : case PreprocessResponse::Tnsresult:
26036 0 : mResultCode = aResponse.get_nsresult();
26037 0 : break;
26038 :
26039 : case PreprocessResponse::TObjectStoreGetPreprocessResponse:
26040 0 : break;
26041 :
26042 : case PreprocessResponse::TObjectStoreGetAllPreprocessResponse:
26043 0 : break;
26044 :
26045 : default:
26046 0 : MOZ_CRASH("Should never get here!");
26047 : }
26048 :
26049 0 : NoteContinueReceived();
26050 :
26051 0 : return IPC_OK();
26052 : }
26053 :
26054 0 : ObjectStoreAddOrPutRequestOp::ObjectStoreAddOrPutRequestOp(
26055 : TransactionBase* aTransaction,
26056 0 : const RequestParams& aParams)
26057 : : NormalTransactionOp(aTransaction)
26058 0 : , mParams(aParams.type() == RequestParams::TObjectStoreAddParams ?
26059 0 : aParams.get_ObjectStoreAddParams().commonParams() :
26060 0 : aParams.get_ObjectStorePutParams().commonParams())
26061 0 : , mGroup(aTransaction->GetDatabase()->Group())
26062 0 : , mOrigin(aTransaction->GetDatabase()->Origin())
26063 0 : , mPersistenceType(aTransaction->GetDatabase()->Type())
26064 0 : , mOverwrite(aParams.type() == RequestParams::TObjectStorePutParams)
26065 0 : , mObjectStoreMayHaveIndexes(false)
26066 : {
26067 0 : MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreAddParams ||
26068 : aParams.type() == RequestParams::TObjectStorePutParams);
26069 :
26070 : mMetadata =
26071 0 : aTransaction->GetMetadataForObjectStoreId(mParams.objectStoreId());
26072 0 : MOZ_ASSERT(mMetadata);
26073 :
26074 0 : mObjectStoreMayHaveIndexes = mMetadata->HasLiveIndexes();
26075 :
26076 0 : mDataOverThreshold =
26077 0 : snappy::MaxCompressedLength(mParams.cloneInfo().data().data.Size()) >
26078 0 : IndexedDatabaseManager::DataThreshold();
26079 0 : }
26080 :
26081 : nsresult
26082 0 : ObjectStoreAddOrPutRequestOp::RemoveOldIndexDataValues(
26083 : DatabaseConnection* aConnection)
26084 : {
26085 0 : AssertIsOnConnectionThread();
26086 0 : MOZ_ASSERT(aConnection);
26087 0 : MOZ_ASSERT(mOverwrite);
26088 0 : MOZ_ASSERT(!mResponse.IsUnset());
26089 :
26090 : #ifdef DEBUG
26091 : {
26092 0 : bool hasIndexes = false;
26093 0 : MOZ_ASSERT(NS_SUCCEEDED(
26094 : DatabaseOperationBase::ObjectStoreHasIndexes(aConnection,
26095 : mParams.objectStoreId(),
26096 : &hasIndexes)));
26097 0 : MOZ_ASSERT(hasIndexes,
26098 : "Don't use this slow method if there are no indexes!");
26099 : }
26100 : #endif
26101 :
26102 0 : DatabaseConnection::CachedStatement indexValuesStmt;
26103 0 : nsresult rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
26104 : "SELECT index_data_values "
26105 : "FROM object_data "
26106 : "WHERE object_store_id = :object_store_id "
26107 : "AND key = :key;"),
26108 0 : &indexValuesStmt);
26109 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26110 0 : return rv;
26111 : }
26112 :
26113 0 : rv = indexValuesStmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
26114 0 : mParams.objectStoreId());
26115 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26116 0 : return rv;
26117 : }
26118 :
26119 0 : rv = mResponse.BindToStatement(indexValuesStmt, NS_LITERAL_CSTRING("key"));
26120 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26121 0 : return rv;
26122 : }
26123 :
26124 : bool hasResult;
26125 0 : rv = indexValuesStmt->ExecuteStep(&hasResult);
26126 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26127 0 : return rv;
26128 : }
26129 :
26130 0 : if (hasResult) {
26131 0 : AutoTArray<IndexDataValue, 32> existingIndexValues;
26132 0 : rv = ReadCompressedIndexDataValues(indexValuesStmt,
26133 : 0,
26134 0 : existingIndexValues);
26135 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26136 0 : return rv;
26137 : }
26138 :
26139 0 : rv = DeleteIndexDataTableRows(aConnection, mResponse, existingIndexValues);
26140 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26141 0 : return rv;
26142 : }
26143 : }
26144 :
26145 0 : return NS_OK;
26146 : }
26147 :
26148 : bool
26149 0 : ObjectStoreAddOrPutRequestOp::Init(TransactionBase* aTransaction)
26150 : {
26151 0 : AssertIsOnOwningThread();
26152 :
26153 : const nsTArray<IndexUpdateInfo>& indexUpdateInfos =
26154 0 : mParams.indexUpdateInfos();
26155 :
26156 0 : if (!indexUpdateInfos.IsEmpty()) {
26157 0 : const uint32_t count = indexUpdateInfos.Length();
26158 :
26159 0 : mUniqueIndexTable.emplace();
26160 :
26161 0 : for (uint32_t index = 0; index < count; index++) {
26162 0 : const IndexUpdateInfo& updateInfo = indexUpdateInfos[index];
26163 :
26164 0 : RefPtr<FullIndexMetadata> indexMetadata;
26165 0 : MOZ_ALWAYS_TRUE(mMetadata->mIndexes.Get(updateInfo.indexId(),
26166 : getter_AddRefs(indexMetadata)));
26167 :
26168 0 : MOZ_ASSERT(!indexMetadata->mDeleted);
26169 :
26170 0 : const int64_t& indexId = indexMetadata->mCommonMetadata.id();
26171 0 : const bool& unique = indexMetadata->mCommonMetadata.unique();
26172 :
26173 0 : MOZ_ASSERT(indexId == updateInfo.indexId());
26174 0 : MOZ_ASSERT_IF(!indexMetadata->mCommonMetadata.multiEntry(),
26175 : !mUniqueIndexTable.ref().Get(indexId));
26176 :
26177 0 : if (NS_WARN_IF(!mUniqueIndexTable.ref().Put(indexId, unique, fallible))) {
26178 0 : return false;
26179 : }
26180 : }
26181 0 : } else if (mOverwrite) {
26182 0 : mUniqueIndexTable.emplace();
26183 : }
26184 :
26185 : #ifdef DEBUG
26186 0 : if (mUniqueIndexTable.isSome()) {
26187 0 : mUniqueIndexTable.ref().MarkImmutable();
26188 : }
26189 : #endif
26190 :
26191 0 : const nsTArray<FileAddInfo>& fileAddInfos = mParams.fileAddInfos();
26192 :
26193 0 : if (!fileAddInfos.IsEmpty()) {
26194 0 : const uint32_t count = fileAddInfos.Length();
26195 :
26196 0 : if (NS_WARN_IF(!mStoredFileInfos.SetCapacity(count, fallible))) {
26197 0 : return false;
26198 : }
26199 :
26200 0 : for (uint32_t index = 0; index < count; index++) {
26201 0 : const FileAddInfo& fileAddInfo = fileAddInfos[index];
26202 :
26203 0 : MOZ_ASSERT(fileAddInfo.type() == StructuredCloneFile::eBlob ||
26204 : fileAddInfo.type() == StructuredCloneFile::eMutableFile ||
26205 : fileAddInfo.type() == StructuredCloneFile::eWasmBytecode ||
26206 : fileAddInfo.type() == StructuredCloneFile::eWasmCompiled);
26207 :
26208 0 : const DatabaseOrMutableFile& file = fileAddInfo.file();
26209 :
26210 0 : StoredFileInfo* storedFileInfo = mStoredFileInfos.AppendElement(fallible);
26211 0 : MOZ_ASSERT(storedFileInfo);
26212 :
26213 0 : switch (fileAddInfo.type()) {
26214 : case StructuredCloneFile::eBlob: {
26215 0 : MOZ_ASSERT(file.type() ==
26216 : DatabaseOrMutableFile::TPBackgroundIDBDatabaseFileParent);
26217 :
26218 : storedFileInfo->mFileActor =
26219 : static_cast<DatabaseFile*>(
26220 0 : file.get_PBackgroundIDBDatabaseFileParent());
26221 0 : MOZ_ASSERT(storedFileInfo->mFileActor);
26222 :
26223 0 : storedFileInfo->mFileInfo = storedFileInfo->mFileActor->GetFileInfo();
26224 0 : MOZ_ASSERT(storedFileInfo->mFileInfo);
26225 :
26226 0 : storedFileInfo->mType = StructuredCloneFile::eBlob;
26227 0 : break;
26228 : }
26229 :
26230 : case StructuredCloneFile::eMutableFile: {
26231 0 : MOZ_ASSERT(file.type() ==
26232 : DatabaseOrMutableFile::TPBackgroundMutableFileParent);
26233 :
26234 : auto mutableFileActor =
26235 : static_cast<MutableFile*>(
26236 0 : file.get_PBackgroundMutableFileParent());
26237 0 : MOZ_ASSERT(mutableFileActor);
26238 :
26239 0 : storedFileInfo->mFileInfo = mutableFileActor->GetFileInfo();
26240 0 : MOZ_ASSERT(storedFileInfo->mFileInfo);
26241 :
26242 0 : storedFileInfo->mType = StructuredCloneFile::eMutableFile;
26243 0 : break;
26244 : }
26245 :
26246 : case StructuredCloneFile::eWasmBytecode:
26247 : case StructuredCloneFile::eWasmCompiled: {
26248 0 : MOZ_ASSERT(file.type() ==
26249 : DatabaseOrMutableFile::TPBackgroundIDBDatabaseFileParent);
26250 :
26251 : storedFileInfo->mFileActor =
26252 : static_cast<DatabaseFile*>(
26253 0 : file.get_PBackgroundIDBDatabaseFileParent());
26254 0 : MOZ_ASSERT(storedFileInfo->mFileActor);
26255 :
26256 0 : storedFileInfo->mFileInfo = storedFileInfo->mFileActor->GetFileInfo();
26257 0 : MOZ_ASSERT(storedFileInfo->mFileInfo);
26258 :
26259 0 : storedFileInfo->mType = fileAddInfo.type();
26260 0 : break;
26261 : }
26262 :
26263 : default:
26264 0 : MOZ_CRASH("Should never get here!");
26265 : }
26266 : }
26267 : }
26268 :
26269 0 : if (mDataOverThreshold) {
26270 0 : StoredFileInfo* storedFileInfo = mStoredFileInfos.AppendElement(fallible);
26271 0 : MOZ_ASSERT(storedFileInfo);
26272 :
26273 : RefPtr<FileManager> fileManager =
26274 0 : aTransaction->GetDatabase()->GetFileManager();
26275 0 : MOZ_ASSERT(fileManager);
26276 :
26277 0 : storedFileInfo->mFileInfo = fileManager->GetNewFileInfo();
26278 :
26279 : storedFileInfo->mInputStream =
26280 0 : new SCInputStream(mParams.cloneInfo().data().data);
26281 :
26282 0 : storedFileInfo->mType = StructuredCloneFile::eStructuredClone;
26283 : }
26284 :
26285 0 : return true;
26286 : }
26287 :
26288 : nsresult
26289 0 : ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
26290 : {
26291 0 : MOZ_ASSERT(aConnection);
26292 0 : aConnection->AssertIsOnConnectionThread();
26293 0 : MOZ_ASSERT(aConnection->GetStorageConnection());
26294 :
26295 0 : AUTO_PROFILER_LABEL("ObjectStoreAddOrPutRequestOp::DoDatabaseWork", STORAGE);
26296 :
26297 0 : if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
26298 0 : return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
26299 : }
26300 :
26301 0 : DatabaseConnection::AutoSavepoint autoSave;
26302 0 : nsresult rv = autoSave.Start(Transaction());
26303 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26304 0 : return rv;
26305 : }
26306 :
26307 : bool objectStoreHasIndexes;
26308 0 : rv = ObjectStoreHasIndexes(this,
26309 : aConnection,
26310 0 : mParams.objectStoreId(),
26311 0 : mObjectStoreMayHaveIndexes,
26312 0 : &objectStoreHasIndexes);
26313 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26314 0 : return rv;
26315 : }
26316 :
26317 : // This will be the final key we use.
26318 0 : Key& key = mResponse;
26319 0 : key = mParams.key();
26320 :
26321 0 : const bool keyUnset = key.IsUnset();
26322 0 : const int64_t osid = mParams.objectStoreId();
26323 :
26324 : // First delete old index_data_values if we're overwriting something and we
26325 : // have indexes.
26326 0 : if (mOverwrite && !keyUnset && objectStoreHasIndexes) {
26327 0 : rv = RemoveOldIndexDataValues(aConnection);
26328 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26329 0 : return rv;
26330 : }
26331 : }
26332 :
26333 : // The "|| keyUnset" here is mostly a debugging tool. If a key isn't
26334 : // specified we should never have a collision and so it shouldn't matter
26335 : // if we allow overwrite or not. By not allowing overwrite we raise
26336 : // detectable errors rather than corrupting data.
26337 0 : DatabaseConnection::CachedStatement stmt;
26338 0 : if (!mOverwrite || keyUnset) {
26339 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
26340 : "INSERT INTO object_data "
26341 : "(object_store_id, key, file_ids, data) "
26342 : "VALUES (:osid, :key, :file_ids, :data);"),
26343 0 : &stmt);
26344 : } else {
26345 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
26346 : "INSERT OR REPLACE INTO object_data "
26347 : "(object_store_id, key, file_ids, data) "
26348 : "VALUES (:osid, :key, :file_ids, :data);"),
26349 0 : &stmt);
26350 : }
26351 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26352 0 : return rv;
26353 : }
26354 :
26355 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid);
26356 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26357 0 : return rv;
26358 : }
26359 :
26360 0 : const SerializedStructuredCloneWriteInfo& cloneInfo = mParams.cloneInfo();
26361 0 : const JSStructuredCloneData& cloneData = cloneInfo.data().data;
26362 0 : size_t cloneDataSize = cloneData.Size();
26363 :
26364 0 : MOZ_ASSERT(!keyUnset || mMetadata->mCommonMetadata.autoIncrement(),
26365 : "Should have key unless autoIncrement");
26366 :
26367 0 : int64_t autoIncrementNum = 0;
26368 :
26369 0 : if (mMetadata->mCommonMetadata.autoIncrement()) {
26370 0 : if (keyUnset) {
26371 0 : autoIncrementNum = mMetadata->mNextAutoIncrementId;
26372 :
26373 0 : MOZ_ASSERT(autoIncrementNum > 0);
26374 :
26375 0 : if (autoIncrementNum > (1LL << 53)) {
26376 0 : return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
26377 : }
26378 :
26379 0 : key.SetFromInteger(autoIncrementNum);
26380 0 : } else if (key.IsFloat()) {
26381 0 : double numericKey = key.ToFloat();
26382 0 : numericKey = std::min(numericKey, double(1LL << 53));
26383 0 : numericKey = floor(numericKey);
26384 0 : if (numericKey >= mMetadata->mNextAutoIncrementId) {
26385 0 : autoIncrementNum = numericKey;
26386 : }
26387 : }
26388 :
26389 0 : if (keyUnset && mMetadata->mCommonMetadata.keyPath().IsValid()) {
26390 0 : const SerializedStructuredCloneWriteInfo& cloneInfo = mParams.cloneInfo();
26391 0 : MOZ_ASSERT(cloneInfo.offsetToKeyProp());
26392 0 : MOZ_ASSERT(cloneDataSize > sizeof(uint64_t));
26393 0 : MOZ_ASSERT(cloneInfo.offsetToKeyProp() <=
26394 : (cloneDataSize - sizeof(uint64_t)));
26395 :
26396 : // Special case where someone put an object into an autoIncrement'ing
26397 : // objectStore with no key in its keyPath set. We needed to figure out
26398 : // which row id we would get above before we could set that properly.
26399 : uint64_t keyPropValue =
26400 0 : ReinterpretDoubleAsUInt64(static_cast<double>(autoIncrementNum));
26401 :
26402 : static const size_t keyPropSize = sizeof(uint64_t);
26403 :
26404 : char keyPropBuffer[keyPropSize];
26405 0 : LittleEndian::writeUint64(keyPropBuffer, keyPropValue);
26406 :
26407 0 : auto iter = cloneData.Iter();
26408 : DebugOnly<bool> result =
26409 0 : iter.AdvanceAcrossSegments(cloneData, cloneInfo.offsetToKeyProp());
26410 0 : MOZ_ASSERT(result);
26411 :
26412 0 : for (char index : keyPropBuffer) {
26413 0 : char* keyPropPointer = iter.Data();
26414 0 : *keyPropPointer = index;
26415 :
26416 0 : result = iter.AdvanceAcrossSegments(cloneData, 1);
26417 0 : MOZ_ASSERT(result);
26418 : }
26419 : }
26420 : }
26421 :
26422 0 : key.BindToStatement(stmt, NS_LITERAL_CSTRING("key"));
26423 :
26424 0 : if (mDataOverThreshold) {
26425 : // The data we store in the SQLite database is a (signed) 64-bit integer.
26426 : // The flags are left-shifted 32 bits so the max value is 0xFFFFFFFF.
26427 : // The file_ids index occupies the lower 32 bits and its max is 0xFFFFFFFF.
26428 : static const uint32_t kCompressedFlag = (1<<0);
26429 :
26430 0 : uint32_t flags = 0;
26431 0 : flags |= kCompressedFlag;
26432 :
26433 0 : uint32_t index = mStoredFileInfos.Length() - 1;
26434 :
26435 0 : int64_t data = (uint64_t(flags) << 32) | index;
26436 :
26437 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("data"), data);
26438 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26439 0 : return rv;
26440 : }
26441 : } else {
26442 0 : nsCString flatCloneData;
26443 0 : flatCloneData.SetLength(cloneDataSize);
26444 0 : auto iter = cloneData.Iter();
26445 0 : cloneData.ReadBytes(iter, flatCloneData.BeginWriting(), cloneDataSize);
26446 :
26447 : // Compress the bytes before adding into the database.
26448 0 : const char* uncompressed = flatCloneData.BeginReading();
26449 0 : size_t uncompressedLength = cloneDataSize;
26450 :
26451 0 : size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
26452 :
26453 : UniqueFreePtr<char> compressed(
26454 0 : static_cast<char*>(malloc(compressedLength)));
26455 0 : if (NS_WARN_IF(!compressed)) {
26456 0 : return NS_ERROR_OUT_OF_MEMORY;
26457 : }
26458 :
26459 0 : snappy::RawCompress(uncompressed, uncompressedLength, compressed.get(),
26460 0 : &compressedLength);
26461 :
26462 0 : uint8_t* dataBuffer = reinterpret_cast<uint8_t*>(compressed.release());
26463 0 : size_t dataBufferLength = compressedLength;
26464 :
26465 0 : rv = stmt->BindAdoptedBlobByName(NS_LITERAL_CSTRING("data"), dataBuffer,
26466 0 : dataBufferLength);
26467 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26468 0 : return rv;
26469 : }
26470 : }
26471 :
26472 0 : if (!mStoredFileInfos.IsEmpty()) {
26473 : // Moved outside the loop to allow it to be cached when demanded by the
26474 : // first write. (We may have mStoredFileInfos without any required writes.)
26475 0 : Maybe<FileHelper> fileHelper;
26476 0 : nsAutoString fileIds;
26477 :
26478 0 : for (uint32_t count = mStoredFileInfos.Length(), index = 0;
26479 0 : index < count;
26480 : index++) {
26481 0 : StoredFileInfo& storedFileInfo = mStoredFileInfos[index];
26482 0 : MOZ_ASSERT(storedFileInfo.mFileInfo);
26483 :
26484 : // If there is a StoredFileInfo, then one of the following is true:
26485 : // - This was an overflow structured clone and storedFileInfo.mInputStream
26486 : // MUST be non-null.
26487 : // - This is a reference to a Blob that may or may not have already been
26488 : // written to disk. storedFileInfo.mFileActor MUST be non-null, but
26489 : // its GetInputStream may return null (so don't assert on them).
26490 : // - It's a mutable file. No writing will be performed.
26491 0 : MOZ_ASSERT(storedFileInfo.mInputStream || storedFileInfo.mFileActor ||
26492 : storedFileInfo.mType == StructuredCloneFile::eMutableFile);
26493 :
26494 0 : nsCOMPtr<nsIInputStream> inputStream;
26495 : // Check for an explicit stream, like a structured clone stream.
26496 0 : storedFileInfo.mInputStream.swap(inputStream);
26497 : // Check for a blob-backed stream otherwise.
26498 0 : if (!inputStream && storedFileInfo.mFileActor) {
26499 0 : ErrorResult streamRv;
26500 : inputStream =
26501 0 : storedFileInfo.mFileActor->GetInputStream(streamRv);
26502 0 : if (NS_WARN_IF(streamRv.Failed())) {
26503 0 : return streamRv.StealNSResult();
26504 : }
26505 : }
26506 :
26507 0 : if (inputStream) {
26508 0 : if (fileHelper.isNothing()) {
26509 : RefPtr<FileManager> fileManager =
26510 0 : Transaction()->GetDatabase()->GetFileManager();
26511 0 : MOZ_ASSERT(fileManager);
26512 :
26513 0 : fileHelper.emplace(fileManager);
26514 0 : rv = fileHelper->Init();
26515 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26516 0 : IDB_REPORT_INTERNAL_ERR();
26517 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
26518 : }
26519 : }
26520 :
26521 0 : RefPtr<FileInfo>& fileInfo = storedFileInfo.mFileInfo;
26522 :
26523 0 : nsCOMPtr<nsIFile> file = fileHelper->GetFile(fileInfo);
26524 0 : if (NS_WARN_IF(!file)) {
26525 0 : IDB_REPORT_INTERNAL_ERR();
26526 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
26527 : }
26528 :
26529 : nsCOMPtr<nsIFile> journalFile =
26530 0 : fileHelper->GetJournalFile(fileInfo);
26531 0 : if (NS_WARN_IF(!journalFile)) {
26532 0 : IDB_REPORT_INTERNAL_ERR();
26533 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
26534 : }
26535 :
26536 : bool compress =
26537 0 : storedFileInfo.mType == StructuredCloneFile::eStructuredClone;
26538 :
26539 0 : rv = fileHelper->CreateFileFromStream(file,
26540 : journalFile,
26541 : inputStream,
26542 0 : compress);
26543 0 : if (NS_FAILED(rv) &&
26544 0 : NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
26545 0 : IDB_REPORT_INTERNAL_ERR();
26546 0 : rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
26547 : }
26548 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26549 : // Try to remove the file if the copy failed.
26550 0 : nsresult rv2 = fileHelper->RemoveFile(file, journalFile);
26551 0 : if (NS_WARN_IF(NS_FAILED(rv2))) {
26552 0 : return rv;
26553 : }
26554 0 : return rv;
26555 : }
26556 :
26557 0 : if (storedFileInfo.mFileActor) {
26558 0 : storedFileInfo.mFileActor->WriteSucceededClearBlobImpl();
26559 : }
26560 : }
26561 :
26562 0 : if (index) {
26563 0 : fileIds.Append(' ');
26564 : }
26565 0 : storedFileInfo.Serialize(fileIds);
26566 : }
26567 :
26568 0 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds);
26569 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26570 0 : return rv;
26571 : }
26572 : } else {
26573 0 : rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids"));
26574 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26575 0 : return rv;
26576 : }
26577 : }
26578 :
26579 0 : rv = stmt->Execute();
26580 0 : if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
26581 0 : MOZ_ASSERT(!keyUnset, "Generated key had a collision!");
26582 0 : return rv;
26583 : }
26584 :
26585 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26586 0 : return rv;
26587 : }
26588 :
26589 : // Update our indexes if needed.
26590 0 : if (!mParams.indexUpdateInfos().IsEmpty()) {
26591 0 : MOZ_ASSERT(mUniqueIndexTable.isSome());
26592 :
26593 : // Write the index_data_values column.
26594 0 : AutoTArray<IndexDataValue, 32> indexValues;
26595 0 : rv = IndexDataValuesFromUpdateInfos(mParams.indexUpdateInfos(),
26596 0 : mUniqueIndexTable.ref(),
26597 0 : indexValues);
26598 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26599 0 : return rv;
26600 : }
26601 :
26602 0 : rv = UpdateIndexValues(aConnection, osid, key, indexValues);
26603 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26604 0 : return rv;
26605 : }
26606 :
26607 0 : rv = InsertIndexTableRows(aConnection, osid, key, indexValues);
26608 0 : if (NS_FAILED(rv)) {
26609 0 : return rv;
26610 : }
26611 : }
26612 :
26613 0 : rv = autoSave.Commit();
26614 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26615 0 : return rv;
26616 : }
26617 :
26618 0 : if (autoIncrementNum) {
26619 0 : mMetadata->mNextAutoIncrementId = autoIncrementNum + 1;
26620 0 : Transaction()->NoteModifiedAutoIncrementObjectStore(mMetadata);
26621 : }
26622 :
26623 0 : return NS_OK;
26624 : }
26625 :
26626 : void
26627 0 : ObjectStoreAddOrPutRequestOp::GetResponse(RequestResponse& aResponse)
26628 : {
26629 0 : AssertIsOnOwningThread();
26630 :
26631 0 : if (mOverwrite) {
26632 0 : aResponse = ObjectStorePutResponse(mResponse);
26633 : } else {
26634 0 : aResponse = ObjectStoreAddResponse(mResponse);
26635 : }
26636 0 : }
26637 :
26638 : void
26639 0 : ObjectStoreAddOrPutRequestOp::Cleanup()
26640 : {
26641 0 : AssertIsOnOwningThread();
26642 :
26643 0 : mStoredFileInfos.Clear();
26644 :
26645 0 : NormalTransactionOp::Cleanup();
26646 0 : }
26647 :
26648 0 : NS_IMPL_ISUPPORTS(ObjectStoreAddOrPutRequestOp::SCInputStream, nsIInputStream)
26649 :
26650 : NS_IMETHODIMP
26651 0 : ObjectStoreAddOrPutRequestOp::
26652 : SCInputStream::Close()
26653 : {
26654 0 : return NS_OK;
26655 : }
26656 :
26657 : NS_IMETHODIMP
26658 0 : ObjectStoreAddOrPutRequestOp::
26659 : SCInputStream::Available(uint64_t* _retval)
26660 : {
26661 0 : return NS_ERROR_NOT_IMPLEMENTED;
26662 : }
26663 :
26664 : NS_IMETHODIMP
26665 0 : ObjectStoreAddOrPutRequestOp::
26666 : SCInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* _retval)
26667 : {
26668 0 : return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
26669 : }
26670 :
26671 : NS_IMETHODIMP
26672 0 : ObjectStoreAddOrPutRequestOp::
26673 : SCInputStream::ReadSegments(nsWriteSegmentFun aWriter,
26674 : void* aClosure,
26675 : uint32_t aCount,
26676 : uint32_t* _retval)
26677 : {
26678 0 : *_retval = 0;
26679 :
26680 0 : while (aCount) {
26681 0 : uint32_t count = std::min(uint32_t(mIter.RemainingInSegment()), aCount);
26682 0 : if (!count) {
26683 : // We've run out of data in the last segment.
26684 0 : break;
26685 : }
26686 :
26687 : uint32_t written;
26688 : nsresult rv =
26689 0 : aWriter(this, aClosure, mIter.Data(), *_retval, count, &written);
26690 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26691 : // InputStreams do not propagate errors to caller.
26692 0 : return NS_OK;
26693 : }
26694 :
26695 : // Writer should write what we asked it to write.
26696 0 : MOZ_ASSERT(written == count);
26697 :
26698 0 : *_retval += count;
26699 0 : aCount -= count;
26700 :
26701 0 : mIter.Advance(mData, count);
26702 : }
26703 :
26704 0 : return NS_OK;
26705 : }
26706 :
26707 : NS_IMETHODIMP
26708 0 : ObjectStoreAddOrPutRequestOp::
26709 : SCInputStream::IsNonBlocking(bool* _retval)
26710 : {
26711 0 : *_retval = false;
26712 0 : return NS_OK;
26713 : }
26714 :
26715 0 : ObjectStoreGetRequestOp::ObjectStoreGetRequestOp(TransactionBase* aTransaction,
26716 : const RequestParams& aParams,
26717 0 : bool aGetAll)
26718 : : NormalTransactionOp(aTransaction)
26719 : , mObjectStoreId(aGetAll ?
26720 0 : aParams.get_ObjectStoreGetAllParams().objectStoreId() :
26721 0 : aParams.get_ObjectStoreGetParams().objectStoreId())
26722 : , mDatabase(aTransaction->GetDatabase())
26723 : , mOptionalKeyRange(aGetAll ?
26724 0 : aParams.get_ObjectStoreGetAllParams()
26725 0 : .optionalKeyRange() :
26726 0 : OptionalKeyRange(aParams.get_ObjectStoreGetParams()
26727 0 : .keyRange()))
26728 0 : , mBackgroundParent(aTransaction->GetBackgroundParent())
26729 : , mPreprocessInfoCount(0)
26730 0 : , mLimit(aGetAll ? aParams.get_ObjectStoreGetAllParams().limit() : 1)
26731 0 : , mGetAll(aGetAll)
26732 : {
26733 0 : MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreGetParams ||
26734 : aParams.type() == RequestParams::TObjectStoreGetAllParams);
26735 0 : MOZ_ASSERT(mObjectStoreId);
26736 0 : MOZ_ASSERT(mDatabase);
26737 0 : MOZ_ASSERT_IF(!aGetAll,
26738 : mOptionalKeyRange.type() ==
26739 : OptionalKeyRange::TSerializedKeyRange);
26740 0 : MOZ_ASSERT(mBackgroundParent);
26741 0 : }
26742 :
26743 : template <typename T>
26744 : void MoveData(StructuredCloneReadInfo& aInfo, T& aResult);
26745 :
26746 : template <>
26747 : void
26748 0 : MoveData<SerializedStructuredCloneReadInfo>(
26749 : StructuredCloneReadInfo& aInfo,
26750 : SerializedStructuredCloneReadInfo& aResult)
26751 : {
26752 0 : aResult.data().data = Move(aInfo.mData);
26753 0 : aResult.hasPreprocessInfo() = aInfo.mHasPreprocessInfo;
26754 0 : }
26755 :
26756 : template <>
26757 : void
26758 0 : MoveData<WasmModulePreprocessInfo>(StructuredCloneReadInfo& aInfo,
26759 : WasmModulePreprocessInfo& aResult)
26760 : {
26761 0 : }
26762 :
26763 : template <bool aForPreprocess, typename T>
26764 : nsresult
26765 0 : ObjectStoreGetRequestOp::ConvertResponse(StructuredCloneReadInfo& aInfo,
26766 : T& aResult)
26767 : {
26768 0 : MoveData(aInfo, aResult);
26769 :
26770 0 : FallibleTArray<SerializedStructuredCloneFile> serializedFiles;
26771 0 : nsresult rv = SerializeStructuredCloneFiles(mBackgroundParent,
26772 : mDatabase,
26773 : aInfo.mFiles,
26774 : aForPreprocess,
26775 0 : serializedFiles);
26776 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26777 0 : return rv;
26778 : }
26779 :
26780 0 : MOZ_ASSERT(aResult.files().IsEmpty());
26781 :
26782 0 : aResult.files().SwapElements(serializedFiles);
26783 :
26784 0 : return NS_OK;
26785 : }
26786 :
26787 : nsresult
26788 0 : ObjectStoreGetRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
26789 : {
26790 0 : MOZ_ASSERT(aConnection);
26791 0 : aConnection->AssertIsOnConnectionThread();
26792 0 : MOZ_ASSERT_IF(!mGetAll,
26793 : mOptionalKeyRange.type() ==
26794 : OptionalKeyRange::TSerializedKeyRange);
26795 0 : MOZ_ASSERT_IF(!mGetAll, mLimit == 1);
26796 :
26797 0 : AUTO_PROFILER_LABEL("ObjectStoreGetRequestOp::DoDatabaseWork", STORAGE);
26798 :
26799 : const bool hasKeyRange =
26800 0 : mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
26801 :
26802 0 : nsAutoCString keyRangeClause;
26803 0 : if (hasKeyRange) {
26804 0 : GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
26805 0 : NS_LITERAL_CSTRING("key"),
26806 0 : keyRangeClause);
26807 : }
26808 :
26809 0 : nsCString limitClause;
26810 0 : if (mLimit) {
26811 0 : limitClause.AssignLiteral(" LIMIT ");
26812 0 : limitClause.AppendInt(mLimit);
26813 : }
26814 :
26815 : nsCString query =
26816 0 : NS_LITERAL_CSTRING("SELECT file_ids, data "
26817 : "FROM object_data "
26818 0 : "WHERE object_store_id = :osid") +
26819 0 : keyRangeClause +
26820 0 : NS_LITERAL_CSTRING(" ORDER BY key ASC") +
26821 0 : limitClause;
26822 :
26823 0 : DatabaseConnection::CachedStatement stmt;
26824 0 : nsresult rv = aConnection->GetCachedStatement(query, &stmt);
26825 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26826 0 : return rv;
26827 : }
26828 :
26829 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId);
26830 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26831 0 : return rv;
26832 : }
26833 :
26834 0 : if (hasKeyRange) {
26835 0 : rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
26836 0 : stmt);
26837 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26838 0 : return rv;
26839 : }
26840 : }
26841 :
26842 : bool hasResult;
26843 0 : while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
26844 0 : StructuredCloneReadInfo* cloneInfo = mResponse.AppendElement(fallible);
26845 0 : if (NS_WARN_IF(!cloneInfo)) {
26846 0 : return NS_ERROR_OUT_OF_MEMORY;
26847 : }
26848 :
26849 0 : rv = GetStructuredCloneReadInfoFromStatement(stmt, 1, 0,
26850 : mDatabase->GetFileManager(),
26851 0 : cloneInfo);
26852 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26853 0 : return rv;
26854 : }
26855 :
26856 0 : if (cloneInfo->mHasPreprocessInfo) {
26857 0 : mPreprocessInfoCount++;
26858 : }
26859 : }
26860 :
26861 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26862 0 : return rv;
26863 : }
26864 :
26865 0 : MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
26866 :
26867 0 : return NS_OK;
26868 : }
26869 :
26870 : bool
26871 0 : ObjectStoreGetRequestOp::HasPreprocessInfo()
26872 : {
26873 0 : return mPreprocessInfoCount > 0;
26874 : }
26875 :
26876 : nsresult
26877 0 : ObjectStoreGetRequestOp::GetPreprocessParams(PreprocessParams& aParams)
26878 : {
26879 0 : AssertIsOnOwningThread();
26880 0 : MOZ_ASSERT(!mResponse.IsEmpty());
26881 :
26882 0 : if (mGetAll) {
26883 0 : aParams = ObjectStoreGetAllPreprocessParams();
26884 :
26885 0 : FallibleTArray<WasmModulePreprocessInfo> falliblePreprocessInfos;
26886 0 : if (NS_WARN_IF(!falliblePreprocessInfos.SetLength(mPreprocessInfoCount,
26887 : fallible))) {
26888 0 : return NS_ERROR_OUT_OF_MEMORY;
26889 : }
26890 :
26891 0 : uint32_t fallibleIndex = 0;
26892 0 : for (uint32_t count = mResponse.Length(), index = 0;
26893 0 : index < count;
26894 : index++) {
26895 0 : StructuredCloneReadInfo& info = mResponse[index];
26896 :
26897 0 : if (info.mHasPreprocessInfo) {
26898 : nsresult rv =
26899 0 : ConvertResponse<true>(info, falliblePreprocessInfos[fallibleIndex++]);
26900 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26901 0 : return rv;
26902 : }
26903 : }
26904 : }
26905 :
26906 : nsTArray<WasmModulePreprocessInfo>& preprocessInfos =
26907 0 : aParams.get_ObjectStoreGetAllPreprocessParams().preprocessInfos();
26908 :
26909 0 : falliblePreprocessInfos.SwapElements(preprocessInfos);
26910 :
26911 0 : return NS_OK;
26912 : }
26913 :
26914 0 : aParams = ObjectStoreGetPreprocessParams();
26915 :
26916 : WasmModulePreprocessInfo& preprocessInfo =
26917 0 : aParams.get_ObjectStoreGetPreprocessParams().preprocessInfo();
26918 :
26919 0 : nsresult rv = ConvertResponse<true>(mResponse[0], preprocessInfo);
26920 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26921 0 : return rv;
26922 : }
26923 :
26924 0 : return NS_OK;
26925 : }
26926 :
26927 : void
26928 0 : ObjectStoreGetRequestOp::GetResponse(RequestResponse& aResponse)
26929 : {
26930 0 : MOZ_ASSERT_IF(mLimit, mResponse.Length() <= mLimit);
26931 :
26932 0 : if (mGetAll) {
26933 0 : aResponse = ObjectStoreGetAllResponse();
26934 :
26935 0 : if (!mResponse.IsEmpty()) {
26936 0 : FallibleTArray<SerializedStructuredCloneReadInfo> fallibleCloneInfos;
26937 0 : if (NS_WARN_IF(!fallibleCloneInfos.SetLength(mResponse.Length(),
26938 : fallible))) {
26939 0 : aResponse = NS_ERROR_OUT_OF_MEMORY;
26940 0 : return;
26941 : }
26942 :
26943 0 : for (uint32_t count = mResponse.Length(), index = 0;
26944 0 : index < count;
26945 : index++) {
26946 : nsresult rv =
26947 0 : ConvertResponse<false>(mResponse[index], fallibleCloneInfos[index]);
26948 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26949 0 : aResponse = rv;
26950 0 : return;
26951 : }
26952 : }
26953 :
26954 : nsTArray<SerializedStructuredCloneReadInfo>& cloneInfos =
26955 0 : aResponse.get_ObjectStoreGetAllResponse().cloneInfos();
26956 :
26957 0 : fallibleCloneInfos.SwapElements(cloneInfos);
26958 : }
26959 :
26960 0 : return;
26961 : }
26962 :
26963 0 : aResponse = ObjectStoreGetResponse();
26964 :
26965 0 : if (!mResponse.IsEmpty()) {
26966 : SerializedStructuredCloneReadInfo& serializedInfo =
26967 0 : aResponse.get_ObjectStoreGetResponse().cloneInfo();
26968 :
26969 0 : nsresult rv = ConvertResponse<false>(mResponse[0], serializedInfo);
26970 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
26971 0 : aResponse = rv;
26972 : }
26973 : }
26974 : }
26975 :
26976 0 : ObjectStoreGetKeyRequestOp::ObjectStoreGetKeyRequestOp(
26977 : TransactionBase* aTransaction,
26978 : const RequestParams& aParams,
26979 0 : bool aGetAll)
26980 : : NormalTransactionOp(aTransaction)
26981 : , mObjectStoreId(aGetAll ?
26982 0 : aParams.get_ObjectStoreGetAllKeysParams().objectStoreId() :
26983 0 : aParams.get_ObjectStoreGetKeyParams().objectStoreId())
26984 : , mOptionalKeyRange(aGetAll ?
26985 0 : aParams.get_ObjectStoreGetAllKeysParams()
26986 0 : .optionalKeyRange() :
26987 0 : OptionalKeyRange(aParams.get_ObjectStoreGetKeyParams()
26988 0 : .keyRange()))
26989 0 : , mLimit(aGetAll ? aParams.get_ObjectStoreGetAllKeysParams().limit() : 1)
26990 0 : , mGetAll(aGetAll)
26991 : {
26992 0 : MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreGetKeyParams ||
26993 : aParams.type() == RequestParams::TObjectStoreGetAllKeysParams);
26994 0 : MOZ_ASSERT(mObjectStoreId);
26995 0 : MOZ_ASSERT_IF(!aGetAll,
26996 : mOptionalKeyRange.type() ==
26997 : OptionalKeyRange::TSerializedKeyRange);
26998 0 : }
26999 :
27000 : nsresult
27001 0 : ObjectStoreGetKeyRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27002 : {
27003 0 : MOZ_ASSERT(aConnection);
27004 0 : aConnection->AssertIsOnConnectionThread();
27005 :
27006 0 : AUTO_PROFILER_LABEL("ObjectStoreGetKeyRequestOp::DoDatabaseWork", STORAGE);
27007 :
27008 : const bool hasKeyRange =
27009 0 : mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
27010 :
27011 0 : nsAutoCString keyRangeClause;
27012 0 : if (hasKeyRange) {
27013 0 : GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
27014 0 : NS_LITERAL_CSTRING("key"),
27015 0 : keyRangeClause);
27016 : }
27017 :
27018 0 : nsAutoCString limitClause;
27019 0 : if (mLimit) {
27020 0 : limitClause.AssignLiteral(" LIMIT ");
27021 0 : limitClause.AppendInt(mLimit);
27022 : }
27023 :
27024 : nsCString query =
27025 0 : NS_LITERAL_CSTRING("SELECT key "
27026 : "FROM object_data "
27027 0 : "WHERE object_store_id = :osid") +
27028 0 : keyRangeClause +
27029 0 : NS_LITERAL_CSTRING(" ORDER BY key ASC") +
27030 0 : limitClause;
27031 :
27032 0 : DatabaseConnection::CachedStatement stmt;
27033 0 : nsresult rv = aConnection->GetCachedStatement(query, &stmt);
27034 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27035 0 : return rv;
27036 : }
27037 :
27038 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId);
27039 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27040 0 : return rv;
27041 : }
27042 :
27043 0 : if (hasKeyRange) {
27044 0 : rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
27045 0 : stmt);
27046 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27047 0 : return rv;
27048 : }
27049 : }
27050 :
27051 : bool hasResult;
27052 0 : while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
27053 0 : Key* key = mResponse.AppendElement(fallible);
27054 0 : if (NS_WARN_IF(!key)) {
27055 0 : return NS_ERROR_OUT_OF_MEMORY;
27056 : }
27057 :
27058 0 : rv = key->SetFromStatement(stmt, 0);
27059 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27060 0 : return rv;
27061 : }
27062 : }
27063 :
27064 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27065 0 : return rv;
27066 : }
27067 :
27068 0 : MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
27069 :
27070 0 : return NS_OK;
27071 : }
27072 :
27073 : void
27074 0 : ObjectStoreGetKeyRequestOp::GetResponse(RequestResponse& aResponse)
27075 : {
27076 0 : MOZ_ASSERT_IF(mLimit, mResponse.Length() <= mLimit);
27077 :
27078 0 : if (mGetAll) {
27079 0 : aResponse = ObjectStoreGetAllKeysResponse();
27080 :
27081 0 : if (!mResponse.IsEmpty()) {
27082 : nsTArray<Key>& response =
27083 0 : aResponse.get_ObjectStoreGetAllKeysResponse().keys();
27084 0 : mResponse.SwapElements(response);
27085 : }
27086 :
27087 0 : return;
27088 : }
27089 :
27090 0 : aResponse = ObjectStoreGetKeyResponse();
27091 :
27092 0 : if (!mResponse.IsEmpty()) {
27093 0 : aResponse.get_ObjectStoreGetKeyResponse().key() = Move(mResponse[0]);
27094 : }
27095 : }
27096 :
27097 0 : ObjectStoreDeleteRequestOp::ObjectStoreDeleteRequestOp(
27098 : TransactionBase* aTransaction,
27099 0 : const ObjectStoreDeleteParams& aParams)
27100 : : NormalTransactionOp(aTransaction)
27101 : , mParams(aParams)
27102 0 : , mObjectStoreMayHaveIndexes(false)
27103 : {
27104 0 : AssertIsOnBackgroundThread();
27105 0 : MOZ_ASSERT(aTransaction);
27106 :
27107 : RefPtr<FullObjectStoreMetadata> metadata =
27108 0 : aTransaction->GetMetadataForObjectStoreId(mParams.objectStoreId());
27109 0 : MOZ_ASSERT(metadata);
27110 :
27111 0 : mObjectStoreMayHaveIndexes = metadata->HasLiveIndexes();
27112 0 : }
27113 :
27114 : nsresult
27115 0 : ObjectStoreDeleteRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27116 : {
27117 0 : MOZ_ASSERT(aConnection);
27118 0 : aConnection->AssertIsOnConnectionThread();
27119 0 : AUTO_PROFILER_LABEL("ObjectStoreDeleteRequestOp::DoDatabaseWork", STORAGE);
27120 :
27121 0 : DatabaseConnection::AutoSavepoint autoSave;
27122 0 : nsresult rv = autoSave.Start(Transaction());
27123 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27124 0 : return rv;
27125 : }
27126 :
27127 : bool objectStoreHasIndexes;
27128 0 : rv = ObjectStoreHasIndexes(this,
27129 : aConnection,
27130 0 : mParams.objectStoreId(),
27131 0 : mObjectStoreMayHaveIndexes,
27132 0 : &objectStoreHasIndexes);
27133 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27134 0 : return rv;
27135 : }
27136 :
27137 0 : if (objectStoreHasIndexes) {
27138 0 : rv = DeleteObjectStoreDataTableRowsWithIndexes(aConnection,
27139 0 : mParams.objectStoreId(),
27140 0 : mParams.keyRange());
27141 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27142 0 : return rv;
27143 : }
27144 : } else {
27145 0 : NS_NAMED_LITERAL_CSTRING(objectStoreIdString, "object_store_id");
27146 :
27147 0 : nsAutoCString keyRangeClause;
27148 0 : GetBindingClauseForKeyRange(mParams.keyRange(),
27149 0 : NS_LITERAL_CSTRING("key"),
27150 0 : keyRangeClause);
27151 :
27152 0 : DatabaseConnection::CachedStatement stmt;
27153 0 : rv = aConnection->GetCachedStatement(
27154 0 : NS_LITERAL_CSTRING("DELETE FROM object_data "
27155 0 : "WHERE object_store_id = :") + objectStoreIdString +
27156 0 : keyRangeClause +
27157 0 : NS_LITERAL_CSTRING(";"),
27158 0 : &stmt);
27159 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27160 0 : return rv;
27161 : }
27162 :
27163 0 : rv = stmt->BindInt64ByName(objectStoreIdString, mParams.objectStoreId());
27164 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27165 0 : return rv;
27166 : }
27167 :
27168 0 : rv = BindKeyRangeToStatement(mParams.keyRange(), stmt);
27169 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27170 0 : return rv;
27171 : }
27172 :
27173 0 : rv = stmt->Execute();
27174 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27175 0 : return rv;
27176 : }
27177 : }
27178 :
27179 0 : rv = autoSave.Commit();
27180 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27181 0 : return rv;
27182 : }
27183 :
27184 0 : return NS_OK;
27185 : }
27186 :
27187 0 : ObjectStoreClearRequestOp::ObjectStoreClearRequestOp(
27188 : TransactionBase* aTransaction,
27189 0 : const ObjectStoreClearParams& aParams)
27190 : : NormalTransactionOp(aTransaction)
27191 : , mParams(aParams)
27192 0 : , mObjectStoreMayHaveIndexes(false)
27193 : {
27194 0 : AssertIsOnBackgroundThread();
27195 0 : MOZ_ASSERT(aTransaction);
27196 :
27197 : RefPtr<FullObjectStoreMetadata> metadata =
27198 0 : aTransaction->GetMetadataForObjectStoreId(mParams.objectStoreId());
27199 0 : MOZ_ASSERT(metadata);
27200 :
27201 0 : mObjectStoreMayHaveIndexes = metadata->HasLiveIndexes();
27202 0 : }
27203 :
27204 : nsresult
27205 0 : ObjectStoreClearRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27206 : {
27207 0 : MOZ_ASSERT(aConnection);
27208 0 : aConnection->AssertIsOnConnectionThread();
27209 :
27210 0 : AUTO_PROFILER_LABEL("ObjectStoreClearRequestOp::DoDatabaseWork", STORAGE);
27211 :
27212 0 : DatabaseConnection::AutoSavepoint autoSave;
27213 0 : nsresult rv = autoSave.Start(Transaction());
27214 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27215 0 : return rv;
27216 : }
27217 :
27218 : bool objectStoreHasIndexes;
27219 0 : rv = ObjectStoreHasIndexes(this,
27220 : aConnection,
27221 0 : mParams.objectStoreId(),
27222 0 : mObjectStoreMayHaveIndexes,
27223 0 : &objectStoreHasIndexes);
27224 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27225 0 : return rv;
27226 : }
27227 :
27228 0 : if (objectStoreHasIndexes) {
27229 0 : rv = DeleteObjectStoreDataTableRowsWithIndexes(aConnection,
27230 0 : mParams.objectStoreId(),
27231 0 : void_t());
27232 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27233 0 : return rv;
27234 : }
27235 : } else {
27236 0 : DatabaseConnection::CachedStatement stmt;
27237 0 : rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
27238 : "DELETE FROM object_data "
27239 : "WHERE object_store_id = :object_store_id;"),
27240 0 : &stmt);
27241 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27242 0 : return rv;
27243 : }
27244 :
27245 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
27246 0 : mParams.objectStoreId());
27247 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27248 0 : return rv;
27249 : }
27250 :
27251 0 : rv = stmt->Execute();
27252 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27253 0 : return rv;
27254 : }
27255 : }
27256 :
27257 0 : rv = autoSave.Commit();
27258 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27259 0 : return rv;
27260 : }
27261 :
27262 0 : return NS_OK;
27263 : }
27264 :
27265 : nsresult
27266 0 : ObjectStoreCountRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27267 : {
27268 0 : MOZ_ASSERT(aConnection);
27269 0 : aConnection->AssertIsOnConnectionThread();
27270 :
27271 0 : AUTO_PROFILER_LABEL("ObjectStoreCountRequestOp::DoDatabaseWork", STORAGE);
27272 :
27273 : const bool hasKeyRange =
27274 0 : mParams.optionalKeyRange().type() == OptionalKeyRange::TSerializedKeyRange;
27275 :
27276 0 : nsAutoCString keyRangeClause;
27277 0 : if (hasKeyRange) {
27278 0 : GetBindingClauseForKeyRange(
27279 0 : mParams.optionalKeyRange().get_SerializedKeyRange(),
27280 0 : NS_LITERAL_CSTRING("key"),
27281 0 : keyRangeClause);
27282 : }
27283 :
27284 : nsCString query =
27285 0 : NS_LITERAL_CSTRING("SELECT count(*) "
27286 : "FROM object_data "
27287 0 : "WHERE object_store_id = :osid") +
27288 0 : keyRangeClause;
27289 :
27290 0 : DatabaseConnection::CachedStatement stmt;
27291 0 : nsresult rv = aConnection->GetCachedStatement(query, &stmt);
27292 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27293 0 : return rv;
27294 : }
27295 :
27296 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
27297 0 : mParams.objectStoreId());
27298 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27299 0 : return rv;
27300 : }
27301 :
27302 0 : if (hasKeyRange) {
27303 0 : rv = BindKeyRangeToStatement(
27304 0 : mParams.optionalKeyRange().get_SerializedKeyRange(),
27305 0 : stmt);
27306 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27307 0 : return rv;
27308 : }
27309 : }
27310 :
27311 : bool hasResult;
27312 0 : rv = stmt->ExecuteStep(&hasResult);
27313 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27314 0 : return rv;
27315 : }
27316 :
27317 0 : if (NS_WARN_IF(!hasResult)) {
27318 0 : MOZ_ASSERT(false, "This should never be possible!");
27319 : IDB_REPORT_INTERNAL_ERR();
27320 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
27321 : }
27322 :
27323 0 : int64_t count = stmt->AsInt64(0);
27324 0 : if (NS_WARN_IF(count < 0)) {
27325 0 : MOZ_ASSERT(false, "This should never be possible!");
27326 : IDB_REPORT_INTERNAL_ERR();
27327 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
27328 : }
27329 :
27330 0 : mResponse.count() = count;
27331 :
27332 0 : return NS_OK;
27333 : }
27334 :
27335 : // static
27336 : already_AddRefed<FullIndexMetadata>
27337 0 : IndexRequestOpBase::IndexMetadataForParams(TransactionBase* aTransaction,
27338 : const RequestParams& aParams)
27339 : {
27340 0 : AssertIsOnBackgroundThread();
27341 0 : MOZ_ASSERT(aTransaction);
27342 0 : MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetParams ||
27343 : aParams.type() == RequestParams::TIndexGetKeyParams ||
27344 : aParams.type() == RequestParams::TIndexGetAllParams ||
27345 : aParams.type() == RequestParams::TIndexGetAllKeysParams ||
27346 : aParams.type() == RequestParams::TIndexCountParams);
27347 :
27348 : uint64_t objectStoreId;
27349 : uint64_t indexId;
27350 :
27351 0 : switch (aParams.type()) {
27352 : case RequestParams::TIndexGetParams: {
27353 0 : const IndexGetParams& params = aParams.get_IndexGetParams();
27354 0 : objectStoreId = params.objectStoreId();
27355 0 : indexId = params.indexId();
27356 0 : break;
27357 : }
27358 :
27359 : case RequestParams::TIndexGetKeyParams: {
27360 0 : const IndexGetKeyParams& params = aParams.get_IndexGetKeyParams();
27361 0 : objectStoreId = params.objectStoreId();
27362 0 : indexId = params.indexId();
27363 0 : break;
27364 : }
27365 :
27366 : case RequestParams::TIndexGetAllParams: {
27367 0 : const IndexGetAllParams& params = aParams.get_IndexGetAllParams();
27368 0 : objectStoreId = params.objectStoreId();
27369 0 : indexId = params.indexId();
27370 0 : break;
27371 : }
27372 :
27373 : case RequestParams::TIndexGetAllKeysParams: {
27374 0 : const IndexGetAllKeysParams& params = aParams.get_IndexGetAllKeysParams();
27375 0 : objectStoreId = params.objectStoreId();
27376 0 : indexId = params.indexId();
27377 0 : break;
27378 : }
27379 :
27380 : case RequestParams::TIndexCountParams: {
27381 0 : const IndexCountParams& params = aParams.get_IndexCountParams();
27382 0 : objectStoreId = params.objectStoreId();
27383 0 : indexId = params.indexId();
27384 0 : break;
27385 : }
27386 :
27387 : default:
27388 0 : MOZ_CRASH("Should never get here!");
27389 : }
27390 :
27391 : const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
27392 0 : aTransaction->GetMetadataForObjectStoreId(objectStoreId);
27393 0 : MOZ_ASSERT(objectStoreMetadata);
27394 :
27395 : RefPtr<FullIndexMetadata> indexMetadata =
27396 0 : aTransaction->GetMetadataForIndexId(objectStoreMetadata, indexId);
27397 0 : MOZ_ASSERT(indexMetadata);
27398 :
27399 0 : return indexMetadata.forget();
27400 : }
27401 :
27402 0 : IndexGetRequestOp::IndexGetRequestOp(TransactionBase* aTransaction,
27403 : const RequestParams& aParams,
27404 0 : bool aGetAll)
27405 : : IndexRequestOpBase(aTransaction, aParams)
27406 : , mDatabase(aTransaction->GetDatabase())
27407 : , mOptionalKeyRange(aGetAll ?
27408 0 : aParams.get_IndexGetAllParams().optionalKeyRange() :
27409 0 : OptionalKeyRange(aParams.get_IndexGetParams()
27410 0 : .keyRange()))
27411 0 : , mBackgroundParent(aTransaction->GetBackgroundParent())
27412 0 : , mLimit(aGetAll ? aParams.get_IndexGetAllParams().limit() : 1)
27413 0 : , mGetAll(aGetAll)
27414 : {
27415 0 : MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetParams ||
27416 : aParams.type() == RequestParams::TIndexGetAllParams);
27417 0 : MOZ_ASSERT(mDatabase);
27418 0 : MOZ_ASSERT_IF(!aGetAll,
27419 : mOptionalKeyRange.type() ==
27420 : OptionalKeyRange::TSerializedKeyRange);
27421 0 : MOZ_ASSERT(mBackgroundParent);
27422 0 : }
27423 :
27424 : nsresult
27425 0 : IndexGetRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27426 : {
27427 0 : MOZ_ASSERT(aConnection);
27428 0 : aConnection->AssertIsOnConnectionThread();
27429 0 : MOZ_ASSERT_IF(!mGetAll,
27430 : mOptionalKeyRange.type() ==
27431 : OptionalKeyRange::TSerializedKeyRange);
27432 0 : MOZ_ASSERT_IF(!mGetAll, mLimit == 1);
27433 :
27434 0 : AUTO_PROFILER_LABEL("IndexGetRequestOp::DoDatabaseWork", STORAGE);
27435 :
27436 : const bool hasKeyRange =
27437 0 : mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
27438 :
27439 0 : nsCString indexTable;
27440 0 : if (mMetadata->mCommonMetadata.unique()) {
27441 0 : indexTable.AssignLiteral("unique_index_data ");
27442 : }
27443 : else {
27444 0 : indexTable.AssignLiteral("index_data ");
27445 : }
27446 :
27447 0 : nsAutoCString keyRangeClause;
27448 0 : if (hasKeyRange) {
27449 0 : GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
27450 0 : NS_LITERAL_CSTRING("value"),
27451 0 : keyRangeClause);
27452 : }
27453 :
27454 0 : nsCString limitClause;
27455 0 : if (mLimit) {
27456 0 : limitClause.AssignLiteral(" LIMIT ");
27457 0 : limitClause.AppendInt(mLimit);
27458 : }
27459 :
27460 : nsCString query =
27461 0 : NS_LITERAL_CSTRING("SELECT file_ids, data "
27462 : "FROM object_data "
27463 0 : "INNER JOIN ") +
27464 0 : indexTable +
27465 0 : NS_LITERAL_CSTRING("AS index_table "
27466 : "ON object_data.object_store_id = "
27467 : "index_table.object_store_id "
27468 : "AND object_data.key = "
27469 : "index_table.object_data_key "
27470 0 : "WHERE index_id = :index_id") +
27471 0 : keyRangeClause +
27472 0 : limitClause;
27473 :
27474 0 : DatabaseConnection::CachedStatement stmt;
27475 0 : nsresult rv = aConnection->GetCachedStatement(query, &stmt);
27476 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27477 0 : return rv;
27478 : }
27479 :
27480 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
27481 0 : mMetadata->mCommonMetadata.id());
27482 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27483 0 : return rv;
27484 : }
27485 :
27486 0 : if (hasKeyRange) {
27487 0 : rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
27488 0 : stmt);
27489 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27490 0 : return rv;
27491 : }
27492 : }
27493 :
27494 : bool hasResult;
27495 0 : while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
27496 0 : StructuredCloneReadInfo* cloneInfo = mResponse.AppendElement(fallible);
27497 0 : if (NS_WARN_IF(!cloneInfo)) {
27498 0 : return NS_ERROR_OUT_OF_MEMORY;
27499 : }
27500 :
27501 0 : rv = GetStructuredCloneReadInfoFromStatement(stmt, 1, 0,
27502 : mDatabase->GetFileManager(),
27503 0 : cloneInfo);
27504 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27505 0 : return rv;
27506 : }
27507 :
27508 0 : if (cloneInfo->mHasPreprocessInfo) {
27509 0 : IDB_WARNING("Preprocessing for indexes not yet implemented!");
27510 0 : return NS_ERROR_NOT_IMPLEMENTED;
27511 : }
27512 : }
27513 :
27514 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27515 0 : return rv;
27516 : }
27517 :
27518 0 : MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
27519 :
27520 0 : return NS_OK;
27521 : }
27522 :
27523 : void
27524 0 : IndexGetRequestOp::GetResponse(RequestResponse& aResponse)
27525 : {
27526 0 : MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
27527 :
27528 0 : if (mGetAll) {
27529 0 : aResponse = IndexGetAllResponse();
27530 :
27531 0 : if (!mResponse.IsEmpty()) {
27532 0 : FallibleTArray<SerializedStructuredCloneReadInfo> fallibleCloneInfos;
27533 0 : if (NS_WARN_IF(!fallibleCloneInfos.SetLength(mResponse.Length(),
27534 : fallible))) {
27535 0 : aResponse = NS_ERROR_OUT_OF_MEMORY;
27536 0 : return;
27537 : }
27538 :
27539 0 : for (uint32_t count = mResponse.Length(), index = 0;
27540 0 : index < count;
27541 : index++) {
27542 0 : StructuredCloneReadInfo& info = mResponse[index];
27543 :
27544 : SerializedStructuredCloneReadInfo& serializedInfo =
27545 0 : fallibleCloneInfos[index];
27546 :
27547 0 : serializedInfo.data().data = Move(info.mData);
27548 :
27549 0 : FallibleTArray<SerializedStructuredCloneFile> serializedFiles;
27550 0 : nsresult rv = SerializeStructuredCloneFiles(mBackgroundParent,
27551 : mDatabase,
27552 : info.mFiles,
27553 : /* aForPreprocess */ false,
27554 0 : serializedFiles);
27555 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27556 0 : aResponse = rv;
27557 0 : return;
27558 : }
27559 :
27560 0 : MOZ_ASSERT(serializedInfo.files().IsEmpty());
27561 :
27562 0 : serializedInfo.files().SwapElements(serializedFiles);
27563 : }
27564 :
27565 : nsTArray<SerializedStructuredCloneReadInfo>& cloneInfos =
27566 0 : aResponse.get_IndexGetAllResponse().cloneInfos();
27567 :
27568 0 : fallibleCloneInfos.SwapElements(cloneInfos);
27569 : }
27570 :
27571 0 : return;
27572 : }
27573 :
27574 0 : aResponse = IndexGetResponse();
27575 :
27576 0 : if (!mResponse.IsEmpty()) {
27577 0 : StructuredCloneReadInfo& info = mResponse[0];
27578 :
27579 : SerializedStructuredCloneReadInfo& serializedInfo =
27580 0 : aResponse.get_IndexGetResponse().cloneInfo();
27581 :
27582 0 : serializedInfo.data().data = Move(info.mData);
27583 :
27584 0 : FallibleTArray<SerializedStructuredCloneFile> serializedFiles;
27585 : nsresult rv =
27586 0 : SerializeStructuredCloneFiles(mBackgroundParent,
27587 : mDatabase,
27588 : info.mFiles,
27589 : /* aForPreprocess */ false,
27590 0 : serializedFiles);
27591 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27592 0 : aResponse = rv;
27593 0 : return;
27594 : }
27595 :
27596 0 : MOZ_ASSERT(serializedInfo.files().IsEmpty());
27597 :
27598 0 : serializedInfo.files().SwapElements(serializedFiles);
27599 : }
27600 : }
27601 :
27602 0 : IndexGetKeyRequestOp::IndexGetKeyRequestOp(TransactionBase* aTransaction,
27603 : const RequestParams& aParams,
27604 0 : bool aGetAll)
27605 : : IndexRequestOpBase(aTransaction, aParams)
27606 : , mOptionalKeyRange(aGetAll ?
27607 0 : aParams.get_IndexGetAllKeysParams().optionalKeyRange() :
27608 0 : OptionalKeyRange(aParams.get_IndexGetKeyParams()
27609 0 : .keyRange()))
27610 0 : , mLimit(aGetAll ? aParams.get_IndexGetAllKeysParams().limit() : 1)
27611 0 : , mGetAll(aGetAll)
27612 : {
27613 0 : MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetKeyParams ||
27614 : aParams.type() == RequestParams::TIndexGetAllKeysParams);
27615 0 : MOZ_ASSERT_IF(!aGetAll,
27616 : mOptionalKeyRange.type() ==
27617 : OptionalKeyRange::TSerializedKeyRange);
27618 0 : }
27619 :
27620 : nsresult
27621 0 : IndexGetKeyRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27622 : {
27623 0 : MOZ_ASSERT(aConnection);
27624 0 : aConnection->AssertIsOnConnectionThread();
27625 0 : MOZ_ASSERT_IF(!mGetAll,
27626 : mOptionalKeyRange.type() ==
27627 : OptionalKeyRange::TSerializedKeyRange);
27628 0 : MOZ_ASSERT_IF(!mGetAll, mLimit == 1);
27629 :
27630 0 : AUTO_PROFILER_LABEL("IndexGetKeyRequestOp::DoDatabaseWork", STORAGE);
27631 :
27632 : const bool hasKeyRange =
27633 0 : mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
27634 :
27635 0 : nsCString indexTable;
27636 0 : if (mMetadata->mCommonMetadata.unique()) {
27637 0 : indexTable.AssignLiteral("unique_index_data ");
27638 : }
27639 : else {
27640 0 : indexTable.AssignLiteral("index_data ");
27641 : }
27642 :
27643 0 : nsAutoCString keyRangeClause;
27644 0 : if (hasKeyRange) {
27645 0 : GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
27646 0 : NS_LITERAL_CSTRING("value"),
27647 0 : keyRangeClause);
27648 : }
27649 :
27650 0 : nsCString limitClause;
27651 0 : if (mLimit) {
27652 0 : limitClause.AssignLiteral(" LIMIT ");
27653 0 : limitClause.AppendInt(mLimit);
27654 : }
27655 :
27656 : nsCString query =
27657 0 : NS_LITERAL_CSTRING("SELECT object_data_key "
27658 0 : "FROM ") +
27659 0 : indexTable +
27660 0 : NS_LITERAL_CSTRING("WHERE index_id = :index_id") +
27661 0 : keyRangeClause +
27662 0 : limitClause;
27663 :
27664 0 : DatabaseConnection::CachedStatement stmt;
27665 0 : nsresult rv = aConnection->GetCachedStatement(query, &stmt);
27666 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27667 0 : return rv;
27668 : }
27669 :
27670 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
27671 0 : mMetadata->mCommonMetadata.id());
27672 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27673 0 : return rv;
27674 : }
27675 :
27676 0 : if (hasKeyRange) {
27677 0 : rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
27678 0 : stmt);
27679 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27680 0 : return rv;
27681 : }
27682 : }
27683 :
27684 : bool hasResult;
27685 0 : while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
27686 0 : Key* key = mResponse.AppendElement(fallible);
27687 0 : if (NS_WARN_IF(!key)) {
27688 0 : return NS_ERROR_OUT_OF_MEMORY;
27689 : }
27690 :
27691 0 : rv = key->SetFromStatement(stmt, 0);
27692 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27693 0 : return rv;
27694 : }
27695 : }
27696 :
27697 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27698 0 : return rv;
27699 : }
27700 :
27701 0 : MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
27702 :
27703 0 : return NS_OK;
27704 : }
27705 :
27706 : void
27707 0 : IndexGetKeyRequestOp::GetResponse(RequestResponse& aResponse)
27708 : {
27709 0 : MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
27710 :
27711 0 : if (mGetAll) {
27712 0 : aResponse = IndexGetAllKeysResponse();
27713 :
27714 0 : if (!mResponse.IsEmpty()) {
27715 0 : mResponse.SwapElements(aResponse.get_IndexGetAllKeysResponse().keys());
27716 : }
27717 :
27718 0 : return;
27719 : }
27720 :
27721 0 : aResponse = IndexGetKeyResponse();
27722 :
27723 0 : if (!mResponse.IsEmpty()) {
27724 0 : aResponse.get_IndexGetKeyResponse().key() = Move(mResponse[0]);
27725 : }
27726 : }
27727 :
27728 : nsresult
27729 0 : IndexCountRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27730 : {
27731 0 : MOZ_ASSERT(aConnection);
27732 0 : aConnection->AssertIsOnConnectionThread();
27733 :
27734 0 : AUTO_PROFILER_LABEL("IndexCountRequestOp::DoDatabaseWork", STORAGE);
27735 :
27736 : const bool hasKeyRange =
27737 0 : mParams.optionalKeyRange().type() == OptionalKeyRange::TSerializedKeyRange;
27738 :
27739 0 : nsCString indexTable;
27740 0 : if (mMetadata->mCommonMetadata.unique()) {
27741 0 : indexTable.AssignLiteral("unique_index_data ");
27742 : }
27743 : else {
27744 0 : indexTable.AssignLiteral("index_data ");
27745 : }
27746 :
27747 0 : nsAutoCString keyRangeClause;
27748 0 : if (hasKeyRange) {
27749 0 : GetBindingClauseForKeyRange(
27750 0 : mParams.optionalKeyRange().get_SerializedKeyRange(),
27751 0 : NS_LITERAL_CSTRING("value"),
27752 0 : keyRangeClause);
27753 : }
27754 :
27755 : nsCString query =
27756 0 : NS_LITERAL_CSTRING("SELECT count(*) "
27757 0 : "FROM ") +
27758 0 : indexTable +
27759 0 : NS_LITERAL_CSTRING("WHERE index_id = :index_id") +
27760 0 : keyRangeClause;
27761 :
27762 0 : DatabaseConnection::CachedStatement stmt;
27763 0 : nsresult rv = aConnection->GetCachedStatement(query, &stmt);
27764 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27765 0 : return rv;
27766 : }
27767 :
27768 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
27769 0 : mMetadata->mCommonMetadata.id());
27770 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27771 0 : return rv;
27772 : }
27773 :
27774 0 : if (hasKeyRange) {
27775 0 : rv = BindKeyRangeToStatement(
27776 0 : mParams.optionalKeyRange().get_SerializedKeyRange(),
27777 0 : stmt);
27778 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27779 0 : return rv;
27780 : }
27781 : }
27782 :
27783 : bool hasResult;
27784 0 : rv = stmt->ExecuteStep(&hasResult);
27785 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27786 0 : return rv;
27787 : }
27788 :
27789 0 : if (NS_WARN_IF(!hasResult)) {
27790 0 : MOZ_ASSERT(false, "This should never be possible!");
27791 : IDB_REPORT_INTERNAL_ERR();
27792 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
27793 : }
27794 :
27795 0 : int64_t count = stmt->AsInt64(0);
27796 0 : if (NS_WARN_IF(count < 0)) {
27797 0 : MOZ_ASSERT(false, "This should never be possible!");
27798 : IDB_REPORT_INTERNAL_ERR();
27799 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
27800 : }
27801 :
27802 0 : mResponse.count() = count;
27803 :
27804 0 : return NS_OK;
27805 : }
27806 :
27807 : bool
27808 0 : Cursor::
27809 : CursorOpBase::SendFailureResult(nsresult aResultCode)
27810 : {
27811 0 : AssertIsOnOwningThread();
27812 0 : MOZ_ASSERT(NS_FAILED(aResultCode));
27813 0 : MOZ_ASSERT(mCursor);
27814 0 : MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this);
27815 0 : MOZ_ASSERT(!mResponseSent);
27816 :
27817 0 : if (!IsActorDestroyed()) {
27818 0 : mResponse = ClampResultCode(aResultCode);
27819 :
27820 : // This is an expected race when the transaction is invalidated after
27821 : // data is retrieved from database. We clear the retrieved files to prevent
27822 : // the assertion failure in SendResponseInternal when mResponse.type() is
27823 : // CursorResponse::Tnsresult.
27824 0 : if (Transaction()->IsInvalidated() && !mFiles.IsEmpty()) {
27825 0 : mFiles.Clear();
27826 : }
27827 :
27828 0 : mCursor->SendResponseInternal(mResponse, mFiles);
27829 : }
27830 :
27831 : #ifdef DEBUG
27832 0 : mResponseSent = true;
27833 : #endif
27834 0 : return false;
27835 : }
27836 :
27837 : void
27838 0 : Cursor::
27839 : CursorOpBase::Cleanup()
27840 : {
27841 0 : AssertIsOnOwningThread();
27842 0 : MOZ_ASSERT(mCursor);
27843 0 : MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent);
27844 :
27845 0 : mCursor = nullptr;
27846 :
27847 : #ifdef DEBUG
27848 : // A bit hacky but the CursorOp request is not generated in response to a
27849 : // child request like most other database operations. Do this to make our
27850 : // assertions happy.
27851 0 : NoteActorDestroyed();
27852 : #endif
27853 :
27854 0 : TransactionDatabaseOperationBase::Cleanup();
27855 0 : }
27856 :
27857 : nsresult
27858 0 : Cursor::
27859 : CursorOpBase::PopulateResponseFromStatement(
27860 : DatabaseConnection::CachedStatement& aStmt,
27861 : bool aInitializeResponse)
27862 : {
27863 0 : Transaction()->AssertIsOnConnectionThread();
27864 0 : MOZ_ASSERT(mResponse.type() == CursorResponse::T__None);
27865 0 : MOZ_ASSERT_IF(mFiles.IsEmpty(), aInitializeResponse);
27866 :
27867 0 : nsresult rv = mCursor->mKey.SetFromStatement(aStmt, 0);
27868 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27869 0 : return rv;
27870 : }
27871 :
27872 0 : switch (mCursor->mType) {
27873 : case OpenCursorParams::TObjectStoreOpenCursorParams: {
27874 0 : StructuredCloneReadInfo cloneInfo;
27875 0 : rv = GetStructuredCloneReadInfoFromStatement(aStmt,
27876 : 2,
27877 : 1,
27878 0 : mCursor->mFileManager,
27879 0 : &cloneInfo);
27880 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27881 0 : return rv;
27882 : }
27883 :
27884 0 : if (cloneInfo.mHasPreprocessInfo) {
27885 0 : IDB_WARNING("Preprocessing for cursors not yet implemented!");
27886 0 : return NS_ERROR_NOT_IMPLEMENTED;
27887 : }
27888 :
27889 0 : if (aInitializeResponse) {
27890 0 : mResponse = nsTArray<ObjectStoreCursorResponse>();
27891 : } else {
27892 0 : MOZ_ASSERT(mResponse.type() ==
27893 : CursorResponse::TArrayOfObjectStoreCursorResponse);
27894 : }
27895 :
27896 0 : auto& responses = mResponse.get_ArrayOfObjectStoreCursorResponse();
27897 0 : auto& response = *responses.AppendElement();
27898 0 : response.cloneInfo().data().data = Move(cloneInfo.mData);
27899 0 : response.key() = mCursor->mKey;
27900 :
27901 0 : mFiles.AppendElement(Move(cloneInfo.mFiles));
27902 0 : break;
27903 : }
27904 :
27905 : case OpenCursorParams::TObjectStoreOpenKeyCursorParams: {
27906 0 : MOZ_ASSERT(aInitializeResponse);
27907 0 : mResponse = ObjectStoreKeyCursorResponse(mCursor->mKey);
27908 0 : break;
27909 : }
27910 :
27911 : case OpenCursorParams::TIndexOpenCursorParams: {
27912 0 : rv = mCursor->mSortKey.SetFromStatement(aStmt, 1);
27913 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27914 0 : return rv;
27915 : }
27916 :
27917 0 : rv = mCursor->mObjectKey.SetFromStatement(aStmt, 2);
27918 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27919 0 : return rv;
27920 : }
27921 :
27922 0 : StructuredCloneReadInfo cloneInfo;
27923 0 : rv = GetStructuredCloneReadInfoFromStatement(aStmt,
27924 : 4,
27925 : 3,
27926 0 : mCursor->mFileManager,
27927 0 : &cloneInfo);
27928 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27929 0 : return rv;
27930 : }
27931 :
27932 0 : if (cloneInfo.mHasPreprocessInfo) {
27933 0 : IDB_WARNING("Preprocessing for cursors not yet implemented!");
27934 0 : return NS_ERROR_NOT_IMPLEMENTED;
27935 : }
27936 :
27937 0 : MOZ_ASSERT(aInitializeResponse);
27938 0 : mResponse = IndexCursorResponse();
27939 :
27940 0 : auto& response = mResponse.get_IndexCursorResponse();
27941 0 : response.cloneInfo().data().data = Move(cloneInfo.mData);
27942 0 : response.key() = mCursor->mKey;
27943 0 : response.sortKey() = mCursor->mSortKey;
27944 0 : response.objectKey() = mCursor->mObjectKey;
27945 :
27946 0 : mFiles.AppendElement(Move(cloneInfo.mFiles));
27947 0 : break;
27948 : }
27949 :
27950 : case OpenCursorParams::TIndexOpenKeyCursorParams: {
27951 0 : rv = mCursor->mSortKey.SetFromStatement(aStmt, 1);
27952 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27953 0 : return rv;
27954 : }
27955 :
27956 0 : rv = mCursor->mObjectKey.SetFromStatement(aStmt, 2);
27957 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27958 0 : return rv;
27959 : }
27960 :
27961 0 : MOZ_ASSERT(aInitializeResponse);
27962 0 : mResponse = IndexKeyCursorResponse(mCursor->mKey,
27963 0 : mCursor->mSortKey,
27964 0 : mCursor->mObjectKey);
27965 0 : break;
27966 : }
27967 :
27968 : default:
27969 0 : MOZ_CRASH("Should never get here!");
27970 : }
27971 :
27972 0 : return NS_OK;
27973 : }
27974 :
27975 : void
27976 0 : Cursor::
27977 : OpenOp::GetRangeKeyInfo(bool aLowerBound, Key* aKey, bool* aOpen)
27978 : {
27979 0 : AssertIsOnConnectionThread();
27980 0 : MOZ_ASSERT(aKey);
27981 0 : MOZ_ASSERT(aKey->IsUnset());
27982 0 : MOZ_ASSERT(aOpen);
27983 :
27984 0 : if (mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange) {
27985 : const SerializedKeyRange& range =
27986 0 : mOptionalKeyRange.get_SerializedKeyRange();
27987 0 : if (range.isOnly()) {
27988 0 : *aKey = range.lower();
27989 0 : *aOpen = false;
27990 : #ifdef ENABLE_INTL_API
27991 0 : if (mCursor->IsLocaleAware()) {
27992 0 : range.lower().ToLocaleBasedKey(*aKey, mCursor->mLocale);
27993 : }
27994 : #endif
27995 : } else {
27996 0 : *aKey = aLowerBound ? range.lower() : range.upper();
27997 0 : *aOpen = aLowerBound ? range.lowerOpen() : range.upperOpen();
27998 : #ifdef ENABLE_INTL_API
27999 0 : if (mCursor->IsLocaleAware()) {
28000 0 : if (aLowerBound) {
28001 0 : range.lower().ToLocaleBasedKey(*aKey, mCursor->mLocale);
28002 : } else {
28003 0 : range.upper().ToLocaleBasedKey(*aKey, mCursor->mLocale);
28004 : }
28005 : }
28006 : #endif
28007 : }
28008 : } else {
28009 0 : *aOpen = false;
28010 : }
28011 0 : }
28012 :
28013 : nsresult
28014 0 : Cursor::
28015 : OpenOp::DoObjectStoreDatabaseWork(DatabaseConnection* aConnection)
28016 : {
28017 0 : MOZ_ASSERT(aConnection);
28018 0 : aConnection->AssertIsOnConnectionThread();
28019 0 : MOZ_ASSERT(mCursor);
28020 0 : MOZ_ASSERT(mCursor->mType == OpenCursorParams::TObjectStoreOpenCursorParams);
28021 0 : MOZ_ASSERT(mCursor->mObjectStoreId);
28022 0 : MOZ_ASSERT(mCursor->mFileManager);
28023 :
28024 0 : AUTO_PROFILER_LABEL("Cursor::OpenOp::DoObjectStoreDatabaseWork", STORAGE);
28025 :
28026 : const bool usingKeyRange =
28027 0 : mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
28028 :
28029 0 : NS_NAMED_LITERAL_CSTRING(keyString, "key");
28030 0 : NS_NAMED_LITERAL_CSTRING(id, "id");
28031 0 : NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT ");
28032 :
28033 : nsCString queryStart =
28034 0 : NS_LITERAL_CSTRING("SELECT ") +
28035 0 : keyString +
28036 0 : NS_LITERAL_CSTRING(", file_ids, data "
28037 : "FROM object_data "
28038 0 : "WHERE object_store_id = :") +
28039 0 : id;
28040 :
28041 0 : nsAutoCString keyRangeClause;
28042 0 : if (usingKeyRange) {
28043 0 : GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
28044 : keyString,
28045 0 : keyRangeClause);
28046 : }
28047 :
28048 0 : nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyString;
28049 0 : switch (mCursor->mDirection) {
28050 : case IDBCursor::NEXT:
28051 : case IDBCursor::NEXT_UNIQUE:
28052 0 : directionClause.AppendLiteral(" ASC");
28053 0 : break;
28054 :
28055 : case IDBCursor::PREV:
28056 : case IDBCursor::PREV_UNIQUE:
28057 0 : directionClause.AppendLiteral(" DESC");
28058 0 : break;
28059 :
28060 : default:
28061 0 : MOZ_CRASH("Should never get here!");
28062 : }
28063 :
28064 : // Note: Changing the number or order of SELECT columns in the query will
28065 : // require changes to CursorOpBase::PopulateResponseFromStatement.
28066 : nsCString firstQuery =
28067 0 : queryStart +
28068 0 : keyRangeClause +
28069 0 : directionClause +
28070 0 : openLimit +
28071 0 : NS_LITERAL_CSTRING("1");
28072 :
28073 0 : DatabaseConnection::CachedStatement stmt;
28074 0 : nsresult rv = aConnection->GetCachedStatement(firstQuery, &stmt);
28075 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28076 0 : return rv;
28077 : }
28078 :
28079 0 : rv = stmt->BindInt64ByName(id, mCursor->mObjectStoreId);
28080 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28081 0 : return rv;
28082 : }
28083 :
28084 0 : if (usingKeyRange) {
28085 0 : rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
28086 0 : stmt);
28087 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28088 0 : return rv;
28089 : }
28090 : }
28091 :
28092 : bool hasResult;
28093 0 : rv = stmt->ExecuteStep(&hasResult);
28094 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28095 0 : return rv;
28096 : }
28097 :
28098 0 : if (!hasResult) {
28099 0 : mResponse = void_t();
28100 0 : return NS_OK;
28101 : }
28102 :
28103 0 : rv = PopulateResponseFromStatement(stmt, true);
28104 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28105 0 : return rv;
28106 : }
28107 :
28108 : // Now we need to make the query to get the next match.
28109 0 : keyRangeClause.Truncate();
28110 0 : nsAutoCString continueToKeyRangeClause;
28111 :
28112 0 : NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
28113 0 : NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
28114 :
28115 0 : switch (mCursor->mDirection) {
28116 : case IDBCursor::NEXT:
28117 : case IDBCursor::NEXT_UNIQUE: {
28118 0 : Key upper;
28119 : bool open;
28120 0 : GetRangeKeyInfo(false, &upper, &open);
28121 0 : AppendConditionClause(keyString, currentKey, false, false,
28122 0 : keyRangeClause);
28123 0 : AppendConditionClause(keyString, currentKey, false, true,
28124 0 : continueToKeyRangeClause);
28125 0 : if (usingKeyRange && !upper.IsUnset()) {
28126 0 : AppendConditionClause(keyString, rangeKey, true, !open, keyRangeClause);
28127 0 : AppendConditionClause(keyString, rangeKey, true, !open,
28128 0 : continueToKeyRangeClause);
28129 0 : mCursor->mRangeKey = upper;
28130 : }
28131 0 : break;
28132 : }
28133 :
28134 : case IDBCursor::PREV:
28135 : case IDBCursor::PREV_UNIQUE: {
28136 0 : Key lower;
28137 : bool open;
28138 0 : GetRangeKeyInfo(true, &lower, &open);
28139 0 : AppendConditionClause(keyString, currentKey, true, false, keyRangeClause);
28140 0 : AppendConditionClause(keyString, currentKey, true, true,
28141 0 : continueToKeyRangeClause);
28142 0 : if (usingKeyRange && !lower.IsUnset()) {
28143 0 : AppendConditionClause(keyString, rangeKey, false, !open,
28144 0 : keyRangeClause);
28145 0 : AppendConditionClause(keyString, rangeKey, false, !open,
28146 0 : continueToKeyRangeClause);
28147 0 : mCursor->mRangeKey = lower;
28148 : }
28149 0 : break;
28150 : }
28151 :
28152 : default:
28153 0 : MOZ_CRASH("Should never get here!");
28154 : }
28155 :
28156 0 : mCursor->mContinueQuery =
28157 0 : queryStart +
28158 0 : keyRangeClause +
28159 0 : directionClause +
28160 0 : openLimit;
28161 :
28162 0 : mCursor->mContinueToQuery =
28163 0 : queryStart +
28164 0 : continueToKeyRangeClause +
28165 0 : directionClause +
28166 0 : openLimit;
28167 :
28168 0 : return NS_OK;
28169 : }
28170 :
28171 : nsresult
28172 0 : Cursor::
28173 : OpenOp::DoObjectStoreKeyDatabaseWork(DatabaseConnection* aConnection)
28174 : {
28175 0 : MOZ_ASSERT(aConnection);
28176 0 : aConnection->AssertIsOnConnectionThread();
28177 0 : MOZ_ASSERT(mCursor);
28178 0 : MOZ_ASSERT(mCursor->mType ==
28179 : OpenCursorParams::TObjectStoreOpenKeyCursorParams);
28180 0 : MOZ_ASSERT(mCursor->mObjectStoreId);
28181 :
28182 0 : AUTO_PROFILER_LABEL("Cursor::OpenOp::DoObjectStoreKeyDatabaseWork", STORAGE);
28183 :
28184 : const bool usingKeyRange =
28185 0 : mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
28186 :
28187 0 : NS_NAMED_LITERAL_CSTRING(keyString, "key");
28188 0 : NS_NAMED_LITERAL_CSTRING(id, "id");
28189 0 : NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT ");
28190 :
28191 : nsCString queryStart =
28192 0 : NS_LITERAL_CSTRING("SELECT ") +
28193 0 : keyString +
28194 0 : NS_LITERAL_CSTRING(" FROM object_data "
28195 0 : "WHERE object_store_id = :") +
28196 0 : id;
28197 :
28198 0 : nsAutoCString keyRangeClause;
28199 0 : if (usingKeyRange) {
28200 0 : GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
28201 : keyString,
28202 0 : keyRangeClause);
28203 : }
28204 :
28205 0 : nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyString;
28206 0 : switch (mCursor->mDirection) {
28207 : case IDBCursor::NEXT:
28208 : case IDBCursor::NEXT_UNIQUE:
28209 0 : directionClause.AppendLiteral(" ASC");
28210 0 : break;
28211 :
28212 : case IDBCursor::PREV:
28213 : case IDBCursor::PREV_UNIQUE:
28214 0 : directionClause.AppendLiteral(" DESC");
28215 0 : break;
28216 :
28217 : default:
28218 0 : MOZ_CRASH("Should never get here!");
28219 : }
28220 :
28221 : // Note: Changing the number or order of SELECT columns in the query will
28222 : // require changes to CursorOpBase::PopulateResponseFromStatement.
28223 : nsCString firstQuery =
28224 0 : queryStart +
28225 0 : keyRangeClause +
28226 0 : directionClause +
28227 0 : openLimit +
28228 0 : NS_LITERAL_CSTRING("1");
28229 :
28230 0 : DatabaseConnection::CachedStatement stmt;
28231 0 : nsresult rv = aConnection->GetCachedStatement(firstQuery, &stmt);
28232 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28233 0 : return rv;
28234 : }
28235 :
28236 0 : rv = stmt->BindInt64ByName(id, mCursor->mObjectStoreId);
28237 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28238 0 : return rv;
28239 : }
28240 :
28241 0 : if (usingKeyRange) {
28242 0 : rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
28243 0 : stmt);
28244 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28245 0 : return rv;
28246 : }
28247 : }
28248 :
28249 : bool hasResult;
28250 0 : rv = stmt->ExecuteStep(&hasResult);
28251 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28252 0 : return rv;
28253 : }
28254 :
28255 0 : if (!hasResult) {
28256 0 : mResponse = void_t();
28257 0 : return NS_OK;
28258 : }
28259 :
28260 0 : rv = PopulateResponseFromStatement(stmt, true);
28261 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28262 0 : return rv;
28263 : }
28264 :
28265 : // Now we need to make the query to get the next match.
28266 0 : keyRangeClause.Truncate();
28267 0 : nsAutoCString continueToKeyRangeClause;
28268 :
28269 0 : NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
28270 0 : NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
28271 :
28272 0 : switch (mCursor->mDirection) {
28273 : case IDBCursor::NEXT:
28274 : case IDBCursor::NEXT_UNIQUE: {
28275 0 : Key upper;
28276 : bool open;
28277 0 : GetRangeKeyInfo(false, &upper, &open);
28278 0 : AppendConditionClause(keyString, currentKey, false, false,
28279 0 : keyRangeClause);
28280 0 : AppendConditionClause(keyString, currentKey, false, true,
28281 0 : continueToKeyRangeClause);
28282 0 : if (usingKeyRange && !upper.IsUnset()) {
28283 0 : AppendConditionClause(keyString, rangeKey, true, !open, keyRangeClause);
28284 0 : AppendConditionClause(keyString, rangeKey, true, !open,
28285 0 : continueToKeyRangeClause);
28286 0 : mCursor->mRangeKey = upper;
28287 : }
28288 0 : break;
28289 : }
28290 :
28291 : case IDBCursor::PREV:
28292 : case IDBCursor::PREV_UNIQUE: {
28293 0 : Key lower;
28294 : bool open;
28295 0 : GetRangeKeyInfo(true, &lower, &open);
28296 0 : AppendConditionClause(keyString, currentKey, true, false, keyRangeClause);
28297 0 : AppendConditionClause(keyString, currentKey, true, true,
28298 0 : continueToKeyRangeClause);
28299 0 : if (usingKeyRange && !lower.IsUnset()) {
28300 0 : AppendConditionClause(keyString, rangeKey, false, !open,
28301 0 : keyRangeClause);
28302 0 : AppendConditionClause(keyString, rangeKey, false, !open,
28303 0 : continueToKeyRangeClause);
28304 0 : mCursor->mRangeKey = lower;
28305 : }
28306 0 : break;
28307 : }
28308 :
28309 : default:
28310 0 : MOZ_CRASH("Should never get here!");
28311 : }
28312 :
28313 0 : mCursor->mContinueQuery =
28314 0 : queryStart +
28315 0 : keyRangeClause +
28316 0 : directionClause +
28317 0 : openLimit;
28318 0 : mCursor->mContinueToQuery =
28319 0 : queryStart +
28320 0 : continueToKeyRangeClause +
28321 0 : directionClause +
28322 0 : openLimit;
28323 :
28324 0 : return NS_OK;
28325 : }
28326 :
28327 : nsresult
28328 0 : Cursor::
28329 : OpenOp::DoIndexDatabaseWork(DatabaseConnection* aConnection)
28330 : {
28331 0 : MOZ_ASSERT(aConnection);
28332 0 : aConnection->AssertIsOnConnectionThread();
28333 0 : MOZ_ASSERT(mCursor);
28334 0 : MOZ_ASSERT(mCursor->mType == OpenCursorParams::TIndexOpenCursorParams);
28335 0 : MOZ_ASSERT(mCursor->mObjectStoreId);
28336 0 : MOZ_ASSERT(mCursor->mIndexId);
28337 :
28338 0 : AUTO_PROFILER_LABEL("Cursor::OpenOp::DoIndexDatabaseWork", STORAGE);
28339 :
28340 : const bool usingKeyRange =
28341 0 : mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
28342 :
28343 0 : nsCString indexTable = mCursor->mUniqueIndex ?
28344 0 : NS_LITERAL_CSTRING("unique_index_data") :
28345 0 : NS_LITERAL_CSTRING("index_data");
28346 :
28347 0 : NS_NAMED_LITERAL_CSTRING(sortColumn, "sort_column");
28348 0 : NS_NAMED_LITERAL_CSTRING(id, "id");
28349 0 : NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT ");
28350 :
28351 0 : nsAutoCString sortColumnAlias;
28352 0 : if (mCursor->IsLocaleAware()) {
28353 : sortColumnAlias = "SELECT index_table.value, "
28354 0 : "index_table.value_locale as sort_column, ";
28355 : } else {
28356 : sortColumnAlias = "SELECT index_table.value as sort_column, "
28357 0 : "index_table.value_locale, ";
28358 : }
28359 :
28360 : nsAutoCString queryStart =
28361 0 : sortColumnAlias +
28362 0 : NS_LITERAL_CSTRING( "index_table.object_data_key, "
28363 : "object_data.file_ids, "
28364 : "object_data.data "
28365 0 : "FROM ") +
28366 0 : indexTable +
28367 0 : NS_LITERAL_CSTRING(" AS index_table "
28368 : "JOIN object_data "
28369 : "ON index_table.object_store_id = "
28370 : "object_data.object_store_id "
28371 : "AND index_table.object_data_key = "
28372 : "object_data.key "
28373 0 : "WHERE index_table.index_id = :") +
28374 0 : id;
28375 :
28376 0 : nsAutoCString keyRangeClause;
28377 0 : if (usingKeyRange) {
28378 0 : GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
28379 : sortColumn,
28380 0 : keyRangeClause);
28381 : }
28382 :
28383 : nsAutoCString directionClause =
28384 0 : NS_LITERAL_CSTRING(" ORDER BY ") +
28385 0 : sortColumn;
28386 :
28387 0 : switch (mCursor->mDirection) {
28388 : case IDBCursor::NEXT:
28389 : case IDBCursor::NEXT_UNIQUE:
28390 0 : directionClause.AppendLiteral(" ASC, index_table.object_data_key ASC");
28391 0 : break;
28392 :
28393 : case IDBCursor::PREV:
28394 0 : directionClause.AppendLiteral(" DESC, index_table.object_data_key DESC");
28395 0 : break;
28396 :
28397 : case IDBCursor::PREV_UNIQUE:
28398 0 : directionClause.AppendLiteral(" DESC, index_table.object_data_key ASC");
28399 0 : break;
28400 :
28401 : default:
28402 0 : MOZ_CRASH("Should never get here!");
28403 : }
28404 :
28405 : // Note: Changing the number or order of SELECT columns in the query will
28406 : // require changes to CursorOpBase::PopulateResponseFromStatement.
28407 : nsCString firstQuery =
28408 0 : queryStart +
28409 0 : keyRangeClause +
28410 0 : directionClause +
28411 0 : openLimit +
28412 0 : NS_LITERAL_CSTRING("1");
28413 :
28414 0 : DatabaseConnection::CachedStatement stmt;
28415 0 : nsresult rv = aConnection->GetCachedStatement(firstQuery, &stmt);
28416 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28417 0 : return rv;
28418 : }
28419 :
28420 0 : rv = stmt->BindInt64ByName(id, mCursor->mIndexId);
28421 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28422 0 : return rv;
28423 : }
28424 :
28425 0 : if (usingKeyRange) {
28426 0 : if (mCursor->IsLocaleAware()) {
28427 0 : rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
28428 : stmt,
28429 0 : mCursor->mLocale);
28430 : } else {
28431 0 : rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
28432 0 : stmt);
28433 : }
28434 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28435 0 : return rv;
28436 : }
28437 : }
28438 :
28439 : bool hasResult;
28440 0 : rv = stmt->ExecuteStep(&hasResult);
28441 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28442 0 : return rv;
28443 : }
28444 :
28445 0 : if (!hasResult) {
28446 0 : mResponse = void_t();
28447 0 : return NS_OK;
28448 : }
28449 :
28450 0 : rv = PopulateResponseFromStatement(stmt, true);
28451 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28452 0 : return rv;
28453 : }
28454 :
28455 : // Now we need to make the query to get the next match.
28456 0 : NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
28457 :
28458 0 : switch (mCursor->mDirection) {
28459 : case IDBCursor::NEXT: {
28460 0 : Key upper;
28461 : bool open;
28462 0 : GetRangeKeyInfo(false, &upper, &open);
28463 0 : if (usingKeyRange && !upper.IsUnset()) {
28464 0 : AppendConditionClause(sortColumn, rangeKey, true, !open, queryStart);
28465 0 : mCursor->mRangeKey = upper;
28466 : }
28467 0 : mCursor->mContinueQuery =
28468 0 : queryStart +
28469 0 : NS_LITERAL_CSTRING(" AND sort_column >= :current_key "
28470 : "AND ( sort_column > :current_key OR "
28471 : "index_table.object_data_key > :object_key ) "
28472 0 : ) +
28473 0 : directionClause +
28474 0 : openLimit;
28475 0 : mCursor->mContinueToQuery =
28476 0 : queryStart +
28477 0 : NS_LITERAL_CSTRING(" AND sort_column >= :current_key") +
28478 0 : directionClause +
28479 0 : openLimit;
28480 0 : mCursor->mContinuePrimaryKeyQuery =
28481 0 : queryStart +
28482 0 : NS_LITERAL_CSTRING(" AND sort_column >= :current_key "
28483 : "AND index_table.object_data_key >= :object_key "
28484 0 : ) +
28485 0 : directionClause +
28486 0 : openLimit;
28487 0 : break;
28488 : }
28489 :
28490 : case IDBCursor::NEXT_UNIQUE: {
28491 0 : Key upper;
28492 : bool open;
28493 0 : GetRangeKeyInfo(false, &upper, &open);
28494 0 : if (usingKeyRange && !upper.IsUnset()) {
28495 0 : AppendConditionClause(sortColumn, rangeKey, true, !open, queryStart);
28496 0 : mCursor->mRangeKey = upper;
28497 : }
28498 0 : mCursor->mContinueQuery =
28499 0 : queryStart +
28500 0 : NS_LITERAL_CSTRING(" AND sort_column > :current_key") +
28501 0 : directionClause +
28502 0 : openLimit;
28503 0 : mCursor->mContinueToQuery =
28504 0 : queryStart +
28505 0 : NS_LITERAL_CSTRING(" AND sort_column >= :current_key") +
28506 0 : directionClause +
28507 0 : openLimit;
28508 0 : break;
28509 : }
28510 :
28511 : case IDBCursor::PREV: {
28512 0 : Key lower;
28513 : bool open;
28514 0 : GetRangeKeyInfo(true, &lower, &open);
28515 0 : if (usingKeyRange && !lower.IsUnset()) {
28516 0 : AppendConditionClause(sortColumn, rangeKey, false, !open, queryStart);
28517 0 : mCursor->mRangeKey = lower;
28518 : }
28519 0 : mCursor->mContinueQuery =
28520 0 : queryStart +
28521 0 : NS_LITERAL_CSTRING(" AND sort_column <= :current_key "
28522 : "AND ( sort_column < :current_key OR "
28523 : "index_table.object_data_key < :object_key ) "
28524 0 : ) +
28525 0 : directionClause +
28526 0 : openLimit;
28527 0 : mCursor->mContinueToQuery =
28528 0 : queryStart +
28529 0 : NS_LITERAL_CSTRING(" AND sort_column <= :current_key") +
28530 0 : directionClause +
28531 0 : openLimit;
28532 0 : mCursor->mContinuePrimaryKeyQuery =
28533 0 : queryStart +
28534 0 : NS_LITERAL_CSTRING(" AND sort_column <= :current_key "
28535 : "AND index_table.object_data_key <= :object_key "
28536 0 : ) +
28537 0 : directionClause +
28538 0 : openLimit;
28539 0 : break;
28540 : }
28541 :
28542 : case IDBCursor::PREV_UNIQUE: {
28543 0 : Key lower;
28544 : bool open;
28545 0 : GetRangeKeyInfo(true, &lower, &open);
28546 0 : if (usingKeyRange && !lower.IsUnset()) {
28547 0 : AppendConditionClause(sortColumn, rangeKey, false, !open, queryStart);
28548 0 : mCursor->mRangeKey = lower;
28549 : }
28550 0 : mCursor->mContinueQuery =
28551 0 : queryStart +
28552 0 : NS_LITERAL_CSTRING(" AND sort_column < :current_key") +
28553 0 : directionClause +
28554 0 : openLimit;
28555 0 : mCursor->mContinueToQuery =
28556 0 : queryStart +
28557 0 : NS_LITERAL_CSTRING(" AND sort_column <= :current_key") +
28558 0 : directionClause +
28559 0 : openLimit;
28560 0 : break;
28561 : }
28562 :
28563 : default:
28564 0 : MOZ_CRASH("Should never get here!");
28565 : }
28566 :
28567 0 : return NS_OK;
28568 : }
28569 :
28570 : nsresult
28571 0 : Cursor::
28572 : OpenOp::DoIndexKeyDatabaseWork(DatabaseConnection* aConnection)
28573 : {
28574 0 : MOZ_ASSERT(aConnection);
28575 0 : aConnection->AssertIsOnConnectionThread();
28576 0 : MOZ_ASSERT(mCursor);
28577 0 : MOZ_ASSERT(mCursor->mType == OpenCursorParams::TIndexOpenKeyCursorParams);
28578 0 : MOZ_ASSERT(mCursor->mObjectStoreId);
28579 0 : MOZ_ASSERT(mCursor->mIndexId);
28580 :
28581 0 : AUTO_PROFILER_LABEL("Cursor::OpenOp::DoIndexKeyDatabaseWork", STORAGE);
28582 :
28583 : const bool usingKeyRange =
28584 0 : mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
28585 :
28586 0 : nsCString table = mCursor->mUniqueIndex ?
28587 0 : NS_LITERAL_CSTRING("unique_index_data") :
28588 0 : NS_LITERAL_CSTRING("index_data");
28589 :
28590 0 : NS_NAMED_LITERAL_CSTRING(sortColumn, "sort_column");
28591 0 : NS_NAMED_LITERAL_CSTRING(id, "id");
28592 0 : NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT ");
28593 :
28594 0 : nsAutoCString sortColumnAlias;
28595 0 : if (mCursor->IsLocaleAware()) {
28596 : sortColumnAlias = "SELECT value, "
28597 0 : "value_locale as sort_column, ";
28598 : } else {
28599 : sortColumnAlias = "SELECT value as sort_column, "
28600 0 : "value_locale, ";
28601 : }
28602 :
28603 : nsAutoCString queryStart =
28604 0 : sortColumnAlias +
28605 0 : NS_LITERAL_CSTRING( "object_data_key "
28606 0 : " FROM ") +
28607 0 : table +
28608 0 : NS_LITERAL_CSTRING(" WHERE index_id = :") +
28609 0 : id;
28610 :
28611 0 : nsAutoCString keyRangeClause;
28612 0 : if (usingKeyRange) {
28613 0 : GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
28614 : sortColumn,
28615 0 : keyRangeClause);
28616 : }
28617 :
28618 : nsAutoCString directionClause =
28619 0 : NS_LITERAL_CSTRING(" ORDER BY ") +
28620 0 : sortColumn;
28621 :
28622 0 : switch (mCursor->mDirection) {
28623 : case IDBCursor::NEXT:
28624 : case IDBCursor::NEXT_UNIQUE:
28625 0 : directionClause.AppendLiteral(" ASC, object_data_key ASC");
28626 0 : break;
28627 :
28628 : case IDBCursor::PREV:
28629 0 : directionClause.AppendLiteral(" DESC, object_data_key DESC");
28630 0 : break;
28631 :
28632 : case IDBCursor::PREV_UNIQUE:
28633 0 : directionClause.AppendLiteral(" DESC, object_data_key ASC");
28634 0 : break;
28635 :
28636 : default:
28637 0 : MOZ_CRASH("Should never get here!");
28638 : }
28639 :
28640 : // Note: Changing the number or order of SELECT columns in the query will
28641 : // require changes to CursorOpBase::PopulateResponseFromStatement.
28642 : nsCString firstQuery =
28643 0 : queryStart +
28644 0 : keyRangeClause +
28645 0 : directionClause +
28646 0 : openLimit +
28647 0 : NS_LITERAL_CSTRING("1");
28648 :
28649 0 : DatabaseConnection::CachedStatement stmt;
28650 0 : nsresult rv = aConnection->GetCachedStatement(firstQuery, &stmt);
28651 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28652 0 : return rv;
28653 : }
28654 :
28655 0 : rv = stmt->BindInt64ByName(id, mCursor->mIndexId);
28656 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28657 0 : return rv;
28658 : }
28659 :
28660 0 : if (usingKeyRange) {
28661 0 : if (mCursor->IsLocaleAware()) {
28662 0 : rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
28663 : stmt,
28664 0 : mCursor->mLocale);
28665 : } else {
28666 0 : rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
28667 0 : stmt);
28668 : }
28669 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28670 0 : return rv;
28671 : }
28672 : }
28673 :
28674 : bool hasResult;
28675 0 : rv = stmt->ExecuteStep(&hasResult);
28676 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28677 0 : return rv;
28678 : }
28679 :
28680 0 : if (!hasResult) {
28681 0 : mResponse = void_t();
28682 0 : return NS_OK;
28683 : }
28684 :
28685 0 : rv = PopulateResponseFromStatement(stmt, true);
28686 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28687 0 : return rv;
28688 : }
28689 :
28690 : // Now we need to make the query to get the next match.
28691 0 : NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
28692 :
28693 0 : switch (mCursor->mDirection) {
28694 : case IDBCursor::NEXT: {
28695 0 : Key upper;
28696 : bool open;
28697 0 : GetRangeKeyInfo(false, &upper, &open);
28698 0 : if (usingKeyRange && !upper.IsUnset()) {
28699 0 : AppendConditionClause(sortColumn, rangeKey, true, !open, queryStart);
28700 0 : mCursor->mRangeKey = upper;
28701 : }
28702 0 : mCursor->mContinueQuery =
28703 0 : queryStart +
28704 0 : NS_LITERAL_CSTRING(" AND sort_column >= :current_key "
28705 : "AND ( sort_column > :current_key OR "
28706 0 : "object_data_key > :object_key )") +
28707 0 : directionClause +
28708 0 : openLimit;
28709 0 : mCursor->mContinueToQuery =
28710 0 : queryStart +
28711 0 : NS_LITERAL_CSTRING(" AND sort_column >= :current_key ") +
28712 0 : directionClause +
28713 0 : openLimit;
28714 0 : mCursor->mContinuePrimaryKeyQuery =
28715 0 : queryStart +
28716 0 : NS_LITERAL_CSTRING(" AND sort_column >= :current_key "
28717 : "AND object_data_key >= :object_key "
28718 0 : ) +
28719 0 : directionClause +
28720 0 : openLimit;
28721 0 : break;
28722 : }
28723 :
28724 : case IDBCursor::NEXT_UNIQUE: {
28725 0 : Key upper;
28726 : bool open;
28727 0 : GetRangeKeyInfo(false, &upper, &open);
28728 0 : if (usingKeyRange && !upper.IsUnset()) {
28729 0 : AppendConditionClause(sortColumn, rangeKey, true, !open, queryStart);
28730 0 : mCursor->mRangeKey = upper;
28731 : }
28732 0 : mCursor->mContinueQuery =
28733 0 : queryStart +
28734 0 : NS_LITERAL_CSTRING(" AND sort_column > :current_key") +
28735 0 : directionClause +
28736 0 : openLimit;
28737 0 : mCursor->mContinueToQuery =
28738 0 : queryStart +
28739 0 : NS_LITERAL_CSTRING(" AND sort_column >= :current_key") +
28740 0 : directionClause +
28741 0 : openLimit;
28742 0 : break;
28743 : }
28744 :
28745 : case IDBCursor::PREV: {
28746 0 : Key lower;
28747 : bool open;
28748 0 : GetRangeKeyInfo(true, &lower, &open);
28749 0 : if (usingKeyRange && !lower.IsUnset()) {
28750 0 : AppendConditionClause(sortColumn, rangeKey, false, !open, queryStart);
28751 0 : mCursor->mRangeKey = lower;
28752 : }
28753 :
28754 0 : mCursor->mContinueQuery =
28755 0 : queryStart +
28756 0 : NS_LITERAL_CSTRING(" AND sort_column <= :current_key "
28757 : "AND ( sort_column < :current_key OR "
28758 0 : "object_data_key < :object_key )") +
28759 0 : directionClause +
28760 0 : openLimit;
28761 0 : mCursor->mContinueToQuery =
28762 0 : queryStart +
28763 0 : NS_LITERAL_CSTRING(" AND sort_column <= :current_key ") +
28764 0 : directionClause +
28765 0 : openLimit;
28766 0 : mCursor->mContinuePrimaryKeyQuery =
28767 0 : queryStart +
28768 0 : NS_LITERAL_CSTRING(" AND sort_column <= :current_key "
28769 : "AND object_data_key <= :object_key "
28770 0 : ) +
28771 0 : directionClause +
28772 0 : openLimit;
28773 0 : break;
28774 : }
28775 :
28776 : case IDBCursor::PREV_UNIQUE: {
28777 0 : Key lower;
28778 : bool open;
28779 0 : GetRangeKeyInfo(true, &lower, &open);
28780 0 : if (usingKeyRange && !lower.IsUnset()) {
28781 0 : AppendConditionClause(sortColumn, rangeKey, false, !open, queryStart);
28782 0 : mCursor->mRangeKey = lower;
28783 : }
28784 0 : mCursor->mContinueQuery =
28785 0 : queryStart +
28786 0 : NS_LITERAL_CSTRING(" AND sort_column < :current_key") +
28787 0 : directionClause +
28788 0 : openLimit;
28789 0 : mCursor->mContinueToQuery =
28790 0 : queryStart +
28791 0 : NS_LITERAL_CSTRING(" AND sort_column <= :current_key") +
28792 0 : directionClause +
28793 0 : openLimit;
28794 0 : break;
28795 : }
28796 :
28797 : default:
28798 0 : MOZ_CRASH("Should never get here!");
28799 : }
28800 :
28801 0 : return NS_OK;
28802 : }
28803 :
28804 : nsresult
28805 0 : Cursor::
28806 : OpenOp::DoDatabaseWork(DatabaseConnection* aConnection)
28807 : {
28808 0 : MOZ_ASSERT(aConnection);
28809 0 : aConnection->AssertIsOnConnectionThread();
28810 0 : MOZ_ASSERT(mCursor);
28811 0 : MOZ_ASSERT(mCursor->mContinueQuery.IsEmpty());
28812 0 : MOZ_ASSERT(mCursor->mContinueToQuery.IsEmpty());
28813 0 : MOZ_ASSERT(mCursor->mContinuePrimaryKeyQuery.IsEmpty());
28814 0 : MOZ_ASSERT(mCursor->mKey.IsUnset());
28815 0 : MOZ_ASSERT(mCursor->mRangeKey.IsUnset());
28816 :
28817 0 : AUTO_PROFILER_LABEL("Cursor::OpenOp::DoDatabaseWork", STORAGE);
28818 :
28819 : nsresult rv;
28820 :
28821 0 : switch (mCursor->mType) {
28822 : case OpenCursorParams::TObjectStoreOpenCursorParams:
28823 0 : rv = DoObjectStoreDatabaseWork(aConnection);
28824 0 : break;
28825 :
28826 : case OpenCursorParams::TObjectStoreOpenKeyCursorParams:
28827 0 : rv = DoObjectStoreKeyDatabaseWork(aConnection);
28828 0 : break;
28829 :
28830 : case OpenCursorParams::TIndexOpenCursorParams:
28831 0 : rv = DoIndexDatabaseWork(aConnection);
28832 0 : break;
28833 :
28834 : case OpenCursorParams::TIndexOpenKeyCursorParams:
28835 0 : rv = DoIndexKeyDatabaseWork(aConnection);
28836 0 : break;
28837 :
28838 : default:
28839 0 : MOZ_CRASH("Should never get here!");
28840 : }
28841 :
28842 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28843 0 : return rv;
28844 : }
28845 :
28846 0 : return NS_OK;
28847 : }
28848 :
28849 : nsresult
28850 0 : Cursor::
28851 : OpenOp::SendSuccessResult()
28852 : {
28853 0 : AssertIsOnOwningThread();
28854 0 : MOZ_ASSERT(mCursor);
28855 0 : MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this);
28856 0 : MOZ_ASSERT(mResponse.type() != CursorResponse::T__None);
28857 0 : MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
28858 : mCursor->mKey.IsUnset());
28859 0 : MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
28860 : mCursor->mSortKey.IsUnset());
28861 0 : MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
28862 : mCursor->mRangeKey.IsUnset());
28863 0 : MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
28864 : mCursor->mObjectKey.IsUnset());
28865 :
28866 0 : if (IsActorDestroyed()) {
28867 0 : return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
28868 : }
28869 :
28870 0 : mCursor->SendResponseInternal(mResponse, mFiles);
28871 :
28872 : #ifdef DEBUG
28873 0 : mResponseSent = true;
28874 : #endif
28875 0 : return NS_OK;
28876 : }
28877 :
28878 : nsresult
28879 0 : Cursor::
28880 : ContinueOp::DoDatabaseWork(DatabaseConnection* aConnection)
28881 : {
28882 0 : MOZ_ASSERT(aConnection);
28883 0 : aConnection->AssertIsOnConnectionThread();
28884 0 : MOZ_ASSERT(mCursor);
28885 0 : MOZ_ASSERT(mCursor->mObjectStoreId);
28886 0 : MOZ_ASSERT(!mCursor->mContinueQuery.IsEmpty());
28887 0 : MOZ_ASSERT(!mCursor->mContinueToQuery.IsEmpty());
28888 0 : MOZ_ASSERT(!mCursor->mKey.IsUnset());
28889 :
28890 : const bool isIndex =
28891 0 : mCursor->mType == OpenCursorParams::TIndexOpenCursorParams ||
28892 0 : mCursor->mType == OpenCursorParams::TIndexOpenKeyCursorParams;
28893 :
28894 0 : MOZ_ASSERT_IF(isIndex &&
28895 : (mCursor->mDirection == IDBCursor::NEXT ||
28896 : mCursor->mDirection == IDBCursor::PREV),
28897 : !mCursor->mContinuePrimaryKeyQuery.IsEmpty());
28898 0 : MOZ_ASSERT_IF(isIndex, mCursor->mIndexId);
28899 0 : MOZ_ASSERT_IF(isIndex, !mCursor->mObjectKey.IsUnset());
28900 :
28901 0 : AUTO_PROFILER_LABEL("Cursor::ContinueOp::DoDatabaseWork", STORAGE);
28902 :
28903 : // We need to pick a query based on whether or not a key was passed to the
28904 : // continue function. If not we'll grab the the next item in the database that
28905 : // is greater than (or less than, if we're running a PREV cursor) the current
28906 : // key. If a key was passed we'll grab the next item in the database that is
28907 : // greater than (or less than, if we're running a PREV cursor) or equal to the
28908 : // key that was specified.
28909 :
28910 : // Note: Changing the number or order of SELECT columns in the query will
28911 : // require changes to CursorOpBase::PopulateResponseFromStatement.
28912 0 : bool hasContinueKey = false;
28913 0 : bool hasContinuePrimaryKey = false;
28914 0 : uint32_t advanceCount = 1;
28915 0 : Key& currentKey = mCursor->IsLocaleAware() ? mCursor->mSortKey : mCursor->mKey;
28916 :
28917 0 : switch (mParams.type()) {
28918 : case CursorRequestParams::TContinueParams:
28919 0 : if (!mParams.get_ContinueParams().key().IsUnset()) {
28920 0 : hasContinueKey = true;
28921 0 : currentKey = mParams.get_ContinueParams().key();
28922 : }
28923 0 : break;
28924 : case CursorRequestParams::TContinuePrimaryKeyParams:
28925 0 : MOZ_ASSERT(!mParams.get_ContinuePrimaryKeyParams().key().IsUnset());
28926 0 : MOZ_ASSERT(!mParams.get_ContinuePrimaryKeyParams().primaryKey().IsUnset());
28927 0 : MOZ_ASSERT(mCursor->mDirection == IDBCursor::NEXT ||
28928 : mCursor->mDirection == IDBCursor::PREV);
28929 0 : hasContinueKey = true;
28930 0 : hasContinuePrimaryKey = true;
28931 0 : currentKey = mParams.get_ContinuePrimaryKeyParams().key();
28932 0 : break;
28933 : case CursorRequestParams::TAdvanceParams:
28934 0 : advanceCount = mParams.get_AdvanceParams().count();
28935 0 : break;
28936 : default:
28937 0 : MOZ_CRASH("Should never get here!");
28938 : }
28939 :
28940 : const nsCString& continueQuery =
28941 0 : hasContinuePrimaryKey ? mCursor->mContinuePrimaryKeyQuery :
28942 0 : hasContinueKey ? mCursor->mContinueToQuery : mCursor->mContinueQuery;
28943 :
28944 0 : MOZ_ASSERT(advanceCount > 0);
28945 0 : nsAutoCString countString;
28946 0 : countString.AppendInt(advanceCount);
28947 :
28948 0 : nsCString query = continueQuery + countString;
28949 :
28950 0 : NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key");
28951 0 : NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
28952 0 : NS_NAMED_LITERAL_CSTRING(objectKeyName, "object_key");
28953 :
28954 0 : const bool usingRangeKey = !mCursor->mRangeKey.IsUnset();
28955 :
28956 0 : DatabaseConnection::CachedStatement stmt;
28957 0 : nsresult rv = aConnection->GetCachedStatement(query, &stmt);
28958 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28959 0 : return rv;
28960 : }
28961 :
28962 0 : int64_t id = isIndex ? mCursor->mIndexId : mCursor->mObjectStoreId;
28963 :
28964 0 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), id);
28965 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28966 0 : return rv;
28967 : }
28968 :
28969 : // Bind current key.
28970 0 : rv = currentKey.BindToStatement(stmt, currentKeyName);
28971 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28972 0 : return rv;
28973 : }
28974 :
28975 : // Bind range key if it is specified.
28976 0 : if (usingRangeKey) {
28977 0 : rv = mCursor->mRangeKey.BindToStatement(stmt, rangeKeyName);
28978 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28979 0 : return rv;
28980 : }
28981 : }
28982 :
28983 : // Bind object key if duplicates are allowed and we're not continuing to a
28984 : // specific key.
28985 0 : if (isIndex &&
28986 0 : !hasContinueKey &&
28987 0 : (mCursor->mDirection == IDBCursor::NEXT ||
28988 0 : mCursor->mDirection == IDBCursor::PREV)) {
28989 0 : rv = mCursor->mObjectKey.BindToStatement(stmt, objectKeyName);
28990 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
28991 0 : return rv;
28992 : }
28993 : }
28994 :
28995 : // Bind object key if primaryKey is specified.
28996 0 : if (hasContinuePrimaryKey) {
28997 0 : rv = mParams.get_ContinuePrimaryKeyParams().primaryKey()
28998 0 : .BindToStatement(stmt, objectKeyName);
28999 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29000 0 : return rv;
29001 : }
29002 : }
29003 :
29004 :
29005 : bool hasResult;
29006 0 : for (uint32_t index = 0; index < advanceCount; index++) {
29007 0 : rv = stmt->ExecuteStep(&hasResult);
29008 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29009 0 : return rv;
29010 : }
29011 :
29012 0 : if (!hasResult) {
29013 0 : mCursor->mKey.Unset();
29014 0 : mCursor->mSortKey.Unset();
29015 0 : mCursor->mRangeKey.Unset();
29016 0 : mCursor->mObjectKey.Unset();
29017 0 : mResponse = void_t();
29018 0 : return NS_OK;
29019 : }
29020 : }
29021 :
29022 0 : rv = PopulateResponseFromStatement(stmt, true);
29023 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29024 0 : return rv;
29025 : }
29026 :
29027 0 : return NS_OK;
29028 : }
29029 :
29030 : nsresult
29031 0 : Cursor::
29032 : ContinueOp::SendSuccessResult()
29033 : {
29034 0 : AssertIsOnOwningThread();
29035 0 : MOZ_ASSERT(mCursor);
29036 0 : MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this);
29037 0 : MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
29038 : mCursor->mKey.IsUnset());
29039 0 : MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
29040 : mCursor->mRangeKey.IsUnset());
29041 0 : MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
29042 : mCursor->mObjectKey.IsUnset());
29043 :
29044 0 : if (IsActorDestroyed()) {
29045 0 : return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
29046 : }
29047 :
29048 0 : mCursor->SendResponseInternal(mResponse, mFiles);
29049 :
29050 : #ifdef DEBUG
29051 0 : mResponseSent = true;
29052 : #endif
29053 0 : return NS_OK;
29054 : }
29055 :
29056 0 : Utils::Utils()
29057 : #ifdef DEBUG
29058 0 : : mActorDestroyed(false)
29059 : #endif
29060 : {
29061 0 : AssertIsOnBackgroundThread();
29062 0 : }
29063 :
29064 0 : Utils::~Utils()
29065 : {
29066 0 : MOZ_ASSERT(mActorDestroyed);
29067 0 : }
29068 :
29069 : void
29070 0 : Utils::ActorDestroy(ActorDestroyReason aWhy)
29071 : {
29072 0 : AssertIsOnBackgroundThread();
29073 0 : MOZ_ASSERT(!mActorDestroyed);
29074 :
29075 : #ifdef DEBUG
29076 0 : mActorDestroyed = true;
29077 : #endif
29078 0 : }
29079 :
29080 : mozilla::ipc::IPCResult
29081 0 : Utils::RecvDeleteMe()
29082 : {
29083 0 : AssertIsOnBackgroundThread();
29084 0 : MOZ_ASSERT(!mActorDestroyed);
29085 :
29086 0 : IProtocol* mgr = Manager();
29087 0 : if (!PBackgroundIndexedDBUtilsParent::Send__delete__(this)) {
29088 0 : return IPC_FAIL_NO_REASON(mgr);
29089 : }
29090 0 : return IPC_OK();
29091 : }
29092 :
29093 : mozilla::ipc::IPCResult
29094 0 : Utils::RecvGetFileReferences(const PersistenceType& aPersistenceType,
29095 : const nsCString& aOrigin,
29096 : const nsString& aDatabaseName,
29097 : const int64_t& aFileId,
29098 : int32_t* aRefCnt,
29099 : int32_t* aDBRefCnt,
29100 : int32_t* aSliceRefCnt,
29101 : bool* aResult)
29102 : {
29103 0 : AssertIsOnBackgroundThread();
29104 0 : MOZ_ASSERT(aRefCnt);
29105 0 : MOZ_ASSERT(aDBRefCnt);
29106 0 : MOZ_ASSERT(aSliceRefCnt);
29107 0 : MOZ_ASSERT(aResult);
29108 0 : MOZ_ASSERT(!mActorDestroyed);
29109 :
29110 0 : if (NS_WARN_IF(!IndexedDatabaseManager::Get() ||
29111 : !QuotaManager::Get())) {
29112 0 : ASSERT_UNLESS_FUZZING();
29113 : return IPC_FAIL_NO_REASON(this);
29114 : }
29115 :
29116 0 : if (NS_WARN_IF(!IndexedDatabaseManager::InTestingMode())) {
29117 0 : ASSERT_UNLESS_FUZZING();
29118 : return IPC_FAIL_NO_REASON(this);
29119 : }
29120 :
29121 0 : if (NS_WARN_IF(aPersistenceType != quota::PERSISTENCE_TYPE_PERSISTENT &&
29122 : aPersistenceType != quota::PERSISTENCE_TYPE_TEMPORARY &&
29123 : aPersistenceType != quota::PERSISTENCE_TYPE_DEFAULT)) {
29124 0 : ASSERT_UNLESS_FUZZING();
29125 : return IPC_FAIL_NO_REASON(this);
29126 : }
29127 :
29128 0 : if (NS_WARN_IF(aOrigin.IsEmpty())) {
29129 0 : ASSERT_UNLESS_FUZZING();
29130 : return IPC_FAIL_NO_REASON(this);
29131 : }
29132 :
29133 0 : if (NS_WARN_IF(aDatabaseName.IsEmpty())) {
29134 0 : ASSERT_UNLESS_FUZZING();
29135 : return IPC_FAIL_NO_REASON(this);
29136 : }
29137 :
29138 0 : if (NS_WARN_IF(aFileId == 0)) {
29139 0 : ASSERT_UNLESS_FUZZING();
29140 : return IPC_FAIL_NO_REASON(this);
29141 : }
29142 :
29143 : RefPtr<GetFileReferencesHelper> helper =
29144 : new GetFileReferencesHelper(aPersistenceType, aOrigin, aDatabaseName,
29145 0 : aFileId);
29146 :
29147 : nsresult rv =
29148 0 : helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt,
29149 0 : aSliceRefCnt, aResult);
29150 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29151 0 : return IPC_FAIL_NO_REASON(this);
29152 : }
29153 :
29154 0 : return IPC_OK();
29155 : }
29156 :
29157 : nsresult
29158 0 : GetFileReferencesHelper::DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
29159 : int32_t* aDBRefCnt,
29160 : int32_t* aSliceRefCnt,
29161 : bool* aResult)
29162 : {
29163 0 : AssertIsOnBackgroundThread();
29164 0 : MOZ_ASSERT(aMemRefCnt);
29165 0 : MOZ_ASSERT(aDBRefCnt);
29166 0 : MOZ_ASSERT(aSliceRefCnt);
29167 0 : MOZ_ASSERT(aResult);
29168 :
29169 0 : QuotaManager* quotaManager = QuotaManager::Get();
29170 0 : MOZ_ASSERT(quotaManager);
29171 :
29172 : nsresult rv =
29173 0 : quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
29174 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29175 0 : return rv;
29176 : }
29177 :
29178 0 : mozilla::MutexAutoLock autolock(mMutex);
29179 0 : while (mWaiting) {
29180 0 : mCondVar.Wait();
29181 : }
29182 :
29183 0 : *aMemRefCnt = mMemRefCnt;
29184 0 : *aDBRefCnt = mDBRefCnt;
29185 0 : *aSliceRefCnt = mSliceRefCnt;
29186 0 : *aResult = mResult;
29187 :
29188 0 : return NS_OK;
29189 : }
29190 :
29191 : NS_IMETHODIMP
29192 0 : GetFileReferencesHelper::Run()
29193 : {
29194 0 : AssertIsOnIOThread();
29195 :
29196 0 : IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
29197 0 : MOZ_ASSERT(mgr);
29198 :
29199 : RefPtr<FileManager> fileManager =
29200 0 : mgr->GetFileManager(mPersistenceType, mOrigin, mDatabaseName);
29201 :
29202 0 : if (fileManager) {
29203 0 : RefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(mFileId);
29204 :
29205 0 : if (fileInfo) {
29206 0 : fileInfo->GetReferences(&mMemRefCnt, &mDBRefCnt, &mSliceRefCnt);
29207 :
29208 0 : if (mMemRefCnt != -1) {
29209 : // We added an extra temp ref, so account for that accordingly.
29210 0 : mMemRefCnt--;
29211 : }
29212 :
29213 0 : mResult = true;
29214 : }
29215 : }
29216 :
29217 0 : mozilla::MutexAutoLock lock(mMutex);
29218 0 : MOZ_ASSERT(mWaiting);
29219 :
29220 0 : mWaiting = false;
29221 0 : mCondVar.Notify();
29222 :
29223 0 : return NS_OK;
29224 : }
29225 :
29226 : NS_IMETHODIMP
29227 0 : FlushPendingFileDeletionsRunnable::Run()
29228 : {
29229 0 : MOZ_ASSERT(NS_IsMainThread());
29230 :
29231 0 : RefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::Get();
29232 0 : if (NS_WARN_IF(!mgr)) {
29233 0 : return NS_ERROR_FAILURE;
29234 : }
29235 :
29236 0 : nsresult rv = mgr->FlushPendingFileDeletions();
29237 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29238 0 : return rv;
29239 : }
29240 :
29241 0 : return NS_OK;
29242 : }
29243 :
29244 : void
29245 0 : PermissionRequestHelper::OnPromptComplete(PermissionValue aPermissionValue)
29246 : {
29247 0 : MOZ_ASSERT(NS_IsMainThread());
29248 :
29249 0 : if (!mActorDestroyed) {
29250 : Unused <<
29251 0 : PIndexedDBPermissionRequestParent::Send__delete__(this, aPermissionValue);
29252 : }
29253 0 : }
29254 :
29255 : void
29256 0 : PermissionRequestHelper::ActorDestroy(ActorDestroyReason aWhy)
29257 : {
29258 0 : MOZ_ASSERT(NS_IsMainThread());
29259 0 : MOZ_ASSERT(!mActorDestroyed);
29260 :
29261 0 : mActorDestroyed = true;
29262 0 : }
29263 :
29264 : #ifdef DEBUG
29265 :
29266 0 : NS_IMPL_ISUPPORTS(DEBUGThreadSlower, nsIThreadObserver)
29267 :
29268 : NS_IMETHODIMP
29269 0 : DEBUGThreadSlower::OnDispatchedEvent(nsIThreadInternal* /* aThread */)
29270 : {
29271 0 : MOZ_CRASH("Should never be called!");
29272 : }
29273 :
29274 : NS_IMETHODIMP
29275 0 : DEBUGThreadSlower::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
29276 : bool /* aMayWait */)
29277 : {
29278 0 : return NS_OK;
29279 : }
29280 :
29281 : NS_IMETHODIMP
29282 0 : DEBUGThreadSlower::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
29283 : bool /* aEventWasProcessed */)
29284 : {
29285 0 : MOZ_ASSERT(kDEBUGThreadSleepMS);
29286 :
29287 : MOZ_ALWAYS_TRUE(PR_Sleep(PR_MillisecondsToInterval(kDEBUGThreadSleepMS)) ==
29288 : PR_SUCCESS);
29289 : return NS_OK;
29290 : }
29291 :
29292 : #endif // DEBUG
29293 :
29294 : nsresult
29295 0 : FileHelper::Init()
29296 : {
29297 0 : MOZ_ASSERT(!IsOnBackgroundThread());
29298 0 : MOZ_ASSERT(mFileManager);
29299 :
29300 0 : nsCOMPtr<nsIFile> fileDirectory = mFileManager->GetCheckedDirectory();
29301 0 : if (NS_WARN_IF(!fileDirectory)) {
29302 0 : return NS_ERROR_FAILURE;
29303 : }
29304 :
29305 0 : nsCOMPtr<nsIFile> journalDirectory = mFileManager->EnsureJournalDirectory();
29306 0 : if (NS_WARN_IF(!journalDirectory)) {
29307 0 : return NS_ERROR_FAILURE;
29308 : }
29309 :
29310 0 : DebugOnly<bool> exists;
29311 0 : MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->Exists(&exists)));
29312 0 : MOZ_ASSERT(exists);
29313 :
29314 0 : DebugOnly<bool> isDirectory;
29315 0 : MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->IsDirectory(&isDirectory)));
29316 0 : MOZ_ASSERT(isDirectory);
29317 :
29318 0 : mFileDirectory = Move(fileDirectory);
29319 0 : mJournalDirectory= Move(journalDirectory);
29320 :
29321 0 : return NS_OK;
29322 : }
29323 :
29324 : already_AddRefed<nsIFile>
29325 0 : FileHelper::GetFile(FileInfo* aFileInfo)
29326 : {
29327 0 : MOZ_ASSERT(!IsOnBackgroundThread());
29328 0 : MOZ_ASSERT(aFileInfo);
29329 0 : MOZ_ASSERT(mFileManager);
29330 0 : MOZ_ASSERT(mFileDirectory);
29331 :
29332 0 : const int64_t fileId = aFileInfo->Id();
29333 0 : MOZ_ASSERT(fileId > 0);
29334 :
29335 : nsCOMPtr<nsIFile> file =
29336 0 : mFileManager->GetFileForId(mFileDirectory, fileId);
29337 0 : return file.forget();
29338 : }
29339 :
29340 : already_AddRefed<nsIFile>
29341 0 : FileHelper::GetCheckedFile(FileInfo* aFileInfo)
29342 : {
29343 0 : MOZ_ASSERT(!IsOnBackgroundThread());
29344 0 : MOZ_ASSERT(aFileInfo);
29345 0 : MOZ_ASSERT(mFileManager);
29346 0 : MOZ_ASSERT(mFileDirectory);
29347 :
29348 0 : const int64_t fileId = aFileInfo->Id();
29349 0 : MOZ_ASSERT(fileId > 0);
29350 :
29351 : nsCOMPtr<nsIFile> file =
29352 0 : mFileManager->GetCheckedFileForId(mFileDirectory, fileId);
29353 0 : return file.forget();
29354 : }
29355 :
29356 : already_AddRefed<nsIFile>
29357 0 : FileHelper::GetJournalFile(FileInfo* aFileInfo)
29358 : {
29359 0 : MOZ_ASSERT(!IsOnBackgroundThread());
29360 0 : MOZ_ASSERT(aFileInfo);
29361 0 : MOZ_ASSERT(mFileManager);
29362 0 : MOZ_ASSERT(mJournalDirectory);
29363 :
29364 0 : const int64_t fileId = aFileInfo->Id();
29365 0 : MOZ_ASSERT(fileId > 0);
29366 :
29367 : nsCOMPtr<nsIFile> file =
29368 0 : mFileManager->GetFileForId(mJournalDirectory, fileId);
29369 0 : return file.forget();
29370 : }
29371 :
29372 : nsresult
29373 0 : FileHelper::CreateFileFromStream(nsIFile* aFile,
29374 : nsIFile* aJournalFile,
29375 : nsIInputStream* aInputStream,
29376 : bool aCompress)
29377 : {
29378 0 : MOZ_ASSERT(!IsOnBackgroundThread());
29379 0 : MOZ_ASSERT(aFile);
29380 0 : MOZ_ASSERT(aJournalFile);
29381 0 : MOZ_ASSERT(aInputStream);
29382 0 : MOZ_ASSERT(mFileManager);
29383 0 : MOZ_ASSERT(mFileDirectory);
29384 0 : MOZ_ASSERT(mJournalDirectory);
29385 :
29386 : bool exists;
29387 0 : nsresult rv = aFile->Exists(&exists);
29388 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29389 0 : return rv;
29390 : }
29391 :
29392 : // DOM blobs that are being stored in IDB are cached by calling
29393 : // IDBDatabase::GetOrCreateFileActorForBlob. So if the same DOM blob is stored
29394 : // again under a different key or in a different object store, we just add
29395 : // a new reference instead of creating a new copy (all such stored blobs share
29396 : // the same id).
29397 : // However, it can happen that CreateFileFromStream failed due to quota
29398 : // exceeded error and for some reason the orphaned file couldn't be deleted
29399 : // immediately. Now, if the operation is being repeated, the DOM blob is
29400 : // already cached, so it has the same file id which clashes with the orphaned
29401 : // file. We could do some tricks to restore previous copy loop, but it's safer
29402 : // to just delete the orphaned file and start from scratch.
29403 : // This corner case is partially simulated in test_file_copy_failure.js
29404 0 : if (exists) {
29405 : bool isFile;
29406 0 : rv = aFile->IsFile(&isFile);
29407 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29408 0 : return rv;
29409 : }
29410 :
29411 0 : if (NS_WARN_IF(!isFile)) {
29412 0 : return NS_ERROR_FAILURE;
29413 : }
29414 :
29415 0 : rv = aJournalFile->Exists(&exists);
29416 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29417 0 : return rv;
29418 : }
29419 :
29420 0 : if (NS_WARN_IF(!exists)) {
29421 0 : return NS_ERROR_FAILURE;
29422 : }
29423 :
29424 0 : rv = aJournalFile->IsFile(&isFile);
29425 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29426 0 : return rv;
29427 : }
29428 :
29429 0 : if (NS_WARN_IF(!isFile)) {
29430 0 : return NS_ERROR_FAILURE;
29431 : }
29432 :
29433 0 : IDB_WARNING("Deleting orphaned file!");
29434 :
29435 0 : rv = RemoveFile(aFile, aJournalFile);
29436 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29437 0 : return rv;
29438 : }
29439 : }
29440 :
29441 : // Create a journal file first.
29442 0 : rv = aJournalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
29443 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29444 0 : return rv;
29445 : }
29446 :
29447 : // Now try to copy the stream.
29448 : RefPtr<FileOutputStream> fileOutputStream =
29449 0 : FileOutputStream::Create(mFileManager->Type(),
29450 : mFileManager->Group(),
29451 : mFileManager->Origin(),
29452 0 : aFile);
29453 0 : if (NS_WARN_IF(!fileOutputStream)) {
29454 0 : return NS_ERROR_FAILURE;
29455 : }
29456 :
29457 0 : if (aCompress) {
29458 : RefPtr<SnappyCompressOutputStream> snappyOutputStream =
29459 0 : new SnappyCompressOutputStream(fileOutputStream);
29460 :
29461 0 : UniquePtr<char[]> buffer(new char[snappyOutputStream->BlockSize()]);
29462 :
29463 0 : rv = SyncCopy(aInputStream,
29464 : snappyOutputStream,
29465 : buffer.get(),
29466 0 : snappyOutputStream->BlockSize());
29467 : } else {
29468 : char buffer[kFileCopyBufferSize];
29469 :
29470 0 : rv = SyncCopy(aInputStream,
29471 : fileOutputStream,
29472 : buffer,
29473 0 : kFileCopyBufferSize);
29474 : }
29475 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29476 0 : return rv;
29477 : }
29478 :
29479 0 : return NS_OK;
29480 : }
29481 :
29482 : nsresult
29483 0 : FileHelper::ReplaceFile(nsIFile* aFile,
29484 : nsIFile* aNewFile,
29485 : nsIFile* aNewJournalFile)
29486 : {
29487 0 : MOZ_ASSERT(!IsOnBackgroundThread());
29488 0 : MOZ_ASSERT(aFile);
29489 0 : MOZ_ASSERT(aNewFile);
29490 0 : MOZ_ASSERT(aNewJournalFile);
29491 0 : MOZ_ASSERT(mFileManager);
29492 0 : MOZ_ASSERT(mFileDirectory);
29493 0 : MOZ_ASSERT(mJournalDirectory);
29494 :
29495 : nsresult rv;
29496 :
29497 : int64_t fileSize;
29498 :
29499 0 : if (mFileManager->EnforcingQuota()) {
29500 0 : rv = aFile->GetFileSize(&fileSize);
29501 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29502 0 : return rv;
29503 : }
29504 : }
29505 :
29506 0 : nsAutoString fileName;
29507 0 : rv = aFile->GetLeafName(fileName);
29508 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29509 0 : return rv;
29510 : }
29511 :
29512 0 : rv = aNewFile->RenameTo(nullptr, fileName);
29513 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29514 0 : return rv;
29515 : }
29516 :
29517 0 : if (mFileManager->EnforcingQuota()) {
29518 0 : QuotaManager* quotaManager = QuotaManager::Get();
29519 0 : MOZ_ASSERT(quotaManager);
29520 :
29521 0 : quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
29522 : mFileManager->Group(),
29523 : mFileManager->Origin(),
29524 0 : fileSize);
29525 : }
29526 :
29527 0 : rv = aNewJournalFile->Remove(false);
29528 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29529 0 : return rv;
29530 : }
29531 :
29532 0 : return NS_OK;
29533 : }
29534 :
29535 : nsresult
29536 0 : FileHelper::RemoveFile(nsIFile* aFile,
29537 : nsIFile* aJournalFile)
29538 : {
29539 : nsresult rv;
29540 :
29541 : int64_t fileSize;
29542 :
29543 0 : if (mFileManager->EnforcingQuota()) {
29544 0 : rv = aFile->GetFileSize(&fileSize);
29545 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29546 0 : return rv;
29547 : }
29548 : }
29549 :
29550 0 : rv = aFile->Remove(false);
29551 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29552 0 : return rv;
29553 : }
29554 :
29555 0 : if (mFileManager->EnforcingQuota()) {
29556 0 : QuotaManager* quotaManager = QuotaManager::Get();
29557 0 : MOZ_ASSERT(quotaManager);
29558 :
29559 0 : quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
29560 : mFileManager->Group(),
29561 : mFileManager->Origin(),
29562 0 : fileSize);
29563 : }
29564 :
29565 0 : rv = aJournalFile->Remove(false);
29566 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29567 0 : return rv;
29568 : }
29569 :
29570 0 : return NS_OK;
29571 : }
29572 :
29573 : already_AddRefed<FileInfo>
29574 0 : FileHelper::GetNewFileInfo()
29575 : {
29576 0 : MOZ_ASSERT(mFileManager);
29577 :
29578 0 : return mFileManager->GetNewFileInfo();
29579 : }
29580 :
29581 : class FileHelper::ReadCallback final : public nsIInputStreamCallback
29582 : {
29583 : public:
29584 : NS_DECL_THREADSAFE_ISUPPORTS
29585 :
29586 0 : ReadCallback()
29587 0 : : mMutex("ReadCallback::mMutex")
29588 : , mCondVar(mMutex, "ReadCallback::mCondVar")
29589 0 : , mInputAvailable(false)
29590 0 : {}
29591 :
29592 : NS_IMETHOD
29593 0 : OnInputStreamReady(nsIAsyncInputStream* aStream) override
29594 : {
29595 0 : mozilla::MutexAutoLock autolock(mMutex);
29596 :
29597 0 : mInputAvailable = true;
29598 0 : mCondVar.Notify();
29599 :
29600 0 : return NS_OK;
29601 : }
29602 :
29603 : nsresult
29604 0 : AsyncWait(nsIAsyncInputStream* aStream, uint32_t aBufferSize,
29605 : nsIEventTarget* aTarget)
29606 : {
29607 0 : MOZ_ASSERT(aStream);
29608 0 : mozilla::MutexAutoLock autolock(mMutex);
29609 :
29610 0 : nsresult rv = aStream->AsyncWait(this, 0, aBufferSize, aTarget);
29611 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29612 0 : return rv;
29613 : }
29614 :
29615 0 : mInputAvailable = false;
29616 0 : while (!mInputAvailable) {
29617 0 : mCondVar.Wait();
29618 : }
29619 :
29620 0 : return NS_OK;
29621 : }
29622 :
29623 : private:
29624 0 : ~ReadCallback() = default;
29625 :
29626 : mozilla::Mutex mMutex;
29627 : mozilla::CondVar mCondVar;
29628 : bool mInputAvailable;
29629 : };
29630 :
29631 0 : NS_IMPL_ADDREF(FileHelper::ReadCallback);
29632 0 : NS_IMPL_RELEASE(FileHelper::ReadCallback);
29633 :
29634 0 : NS_INTERFACE_MAP_BEGIN(FileHelper::ReadCallback)
29635 0 : NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
29636 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStreamCallback)
29637 0 : NS_INTERFACE_MAP_END
29638 :
29639 : nsresult
29640 0 : FileHelper::SyncRead(nsIInputStream* aInputStream,
29641 : char* aBuffer,
29642 : uint32_t aBufferSize,
29643 : uint32_t* aRead)
29644 : {
29645 0 : MOZ_ASSERT(!IsOnBackgroundThread());
29646 0 : MOZ_ASSERT(aInputStream);
29647 :
29648 : // Let's try to read, directly.
29649 0 : nsresult rv = aInputStream->Read(aBuffer, aBufferSize, aRead);
29650 0 : if (NS_SUCCEEDED(rv) || rv != NS_BASE_STREAM_WOULD_BLOCK) {
29651 0 : return rv;
29652 : }
29653 :
29654 : // We need to proceed async.
29655 0 : nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aInputStream);
29656 0 : if (!asyncStream) {
29657 0 : return rv;
29658 : }
29659 :
29660 0 : if (!mReadCallback) {
29661 0 : mReadCallback = new ReadCallback();
29662 : }
29663 :
29664 : // We just need any thread with an event loop for receiving the
29665 : // OnInputStreamReady callback. Let's use the I/O thread.
29666 : nsCOMPtr<nsIEventTarget> target =
29667 0 : do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
29668 0 : MOZ_ASSERT(target);
29669 :
29670 0 : rv = mReadCallback->AsyncWait(asyncStream, aBufferSize, target);
29671 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29672 0 : return rv;
29673 : }
29674 :
29675 0 : return SyncRead(aInputStream, aBuffer, aBufferSize, aRead);
29676 : }
29677 :
29678 : nsresult
29679 0 : FileHelper::SyncCopy(nsIInputStream* aInputStream,
29680 : nsIOutputStream* aOutputStream,
29681 : char* aBuffer,
29682 : uint32_t aBufferSize)
29683 : {
29684 0 : MOZ_ASSERT(!IsOnBackgroundThread());
29685 0 : MOZ_ASSERT(aInputStream);
29686 0 : MOZ_ASSERT(aOutputStream);
29687 :
29688 0 : AUTO_PROFILER_LABEL("FileHelper::SyncCopy", STORAGE);
29689 :
29690 : nsresult rv;
29691 :
29692 : do {
29693 : uint32_t numRead;
29694 0 : rv = SyncRead(aInputStream, aBuffer, aBufferSize, &numRead);
29695 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29696 0 : break;
29697 : }
29698 :
29699 0 : if (!numRead) {
29700 0 : break;
29701 : }
29702 :
29703 : uint32_t numWrite;
29704 0 : rv = aOutputStream->Write(aBuffer, numRead, &numWrite);
29705 0 : if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
29706 0 : rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
29707 : }
29708 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29709 0 : break;
29710 : }
29711 :
29712 0 : if (NS_WARN_IF(numWrite != numRead)) {
29713 0 : rv = NS_ERROR_FAILURE;
29714 0 : break;
29715 0 : }
29716 : } while (true);
29717 :
29718 0 : if (NS_SUCCEEDED(rv)) {
29719 0 : rv = aOutputStream->Flush();
29720 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
29721 0 : return rv;
29722 : }
29723 : }
29724 :
29725 0 : nsresult rv2 = aOutputStream->Close();
29726 0 : if (NS_WARN_IF(NS_FAILED(rv2))) {
29727 0 : return NS_SUCCEEDED(rv) ? rv2 : rv;
29728 : }
29729 :
29730 0 : return rv;
29731 : }
29732 :
29733 : } // namespace indexedDB
29734 : } // namespace dom
29735 9 : } // namespace mozilla
29736 :
29737 : #undef IDB_MOBILE
29738 : #undef IDB_DEBUG_LOG
29739 : #undef ASSERT_UNLESS_FUZZING
29740 : #undef DISABLE_ASSERTS_FOR_FUZZING
|