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/AppProcessChecker.h"
23 #include "mozilla/AutoRestore.h"
24 #include "mozilla/Casting.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/SnappyCompressOutputStream.h"
32 #include "mozilla/SnappyUncompressInputStream.h"
33 #include "mozilla/StaticPtr.h"
34 #include "mozilla/storage.h"
35 #include "mozilla/Unused.h"
36 #include "mozilla/UniquePtrExtensions.h"
37 #include "mozilla/dom/ContentParent.h"
38 #include "mozilla/dom/File.h"
39 #include "mozilla/dom/StructuredCloneTags.h"
40 #include "mozilla/dom/TabParent.h"
41 #include "mozilla/dom/filehandle/ActorsParent.h"
42 #include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h"
43 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
44 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h"
45 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestParent.h"
46 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h"
47 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h"
48 #include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h"
49 #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h"
50 #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h"
51 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsParent.h"
52 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestParent.h"
53 #include "mozilla/dom/ipc/BlobParent.h"
54 #include "mozilla/dom/quota/Client.h"
55 #include "mozilla/dom/quota/FileStreams.h"
56 #include "mozilla/dom/quota/OriginScope.h"
57 #include "mozilla/dom/quota/QuotaManager.h"
58 #include "mozilla/dom/quota/UsageInfo.h"
59 #include "mozilla/ipc/BackgroundParent.h"
60 #include "mozilla/ipc/BackgroundUtils.h"
61 #include "mozilla/ipc/InputStreamParams.h"
62 #include "mozilla/ipc/InputStreamUtils.h"
63 #include "mozilla/ipc/PBackground.h"
64 #include "mozilla/Scoped.h"
65 #include "mozilla/storage/Variant.h"
66 #include "nsAutoPtr.h"
67 #include "nsCharSeparatedTokenizer.h"
68 #include "nsClassHashtable.h"
69 #include "nsCOMPtr.h"
70 #include "nsDataHashtable.h"
71 #include "nsEscape.h"
72 #include "nsHashKeys.h"
73 #include "nsNetUtil.h"
74 #include "nsISimpleEnumerator.h"
75 #include "nsIAppsService.h"
76 #include "nsIEventTarget.h"
77 #include "nsIFile.h"
78 #include "nsIFileURL.h"
79 #include "nsIFileProtocolHandler.h"
80 #include "nsIInputStream.h"
81 #include "nsIInterfaceRequestor.h"
82 #include "nsInterfaceHashtable.h"
83 #include "nsIOutputStream.h"
84 #include "nsIPipe.h"
85 #include "nsIPrincipal.h"
86 #include "nsIScriptSecurityManager.h"
87 #include "nsISupports.h"
88 #include "nsISupportsImpl.h"
89 #include "nsISupportsPriority.h"
90 #include "nsIThread.h"
91 #include "nsITimer.h"
92 #include "nsIURI.h"
93 #include "nsNetUtil.h"
94 #include "nsPrintfCString.h"
95 #include "nsQueryObject.h"
96 #include "nsRefPtrHashtable.h"
97 #include "nsStreamUtils.h"
98 #include "nsString.h"
99 #include "nsStringStream.h"
100 #include "nsThreadPool.h"
101 #include "nsThreadUtils.h"
102 #include "nsXPCOMCID.h"
103 #include "PermissionRequestBase.h"
104 #include "ProfilerHelpers.h"
105 #include "prsystem.h"
106 #include "prtime.h"
107 #include "ReportInternalError.h"
108 #include "snappy/snappy.h"
109 
110 #define DISABLE_ASSERTS_FOR_FUZZING 0
111 
112 #if DISABLE_ASSERTS_FOR_FUZZING
113 #define ASSERT_UNLESS_FUZZING(...) do { } while (0)
114 #else
115 #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
116 #endif
117 
118 #define IDB_DEBUG_LOG(_args)                                                   \
119   MOZ_LOG(IndexedDatabaseManager::GetLoggingModule(),                           \
120          LogLevel::Debug,                                                         \
121          _args )
122 
123 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
124 #define IDB_MOBILE
125 #endif
126 
127 #define BLOB_IMPL_STORED_FILE_IID \
128   {0x6b505c84, 0x2c60, 0x4ffb, {0x8b, 0x91, 0xfe, 0x22, 0xb1, 0xec, 0x75, 0xe2}}
129 
130 namespace mozilla {
131 
132 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc,
133                                           PRFileDesc,
134                                           PR_Close);
135 
136 namespace dom {
137 namespace indexedDB {
138 
139 using namespace mozilla::dom::quota;
140 using namespace mozilla::ipc;
141 
142 namespace {
143 
144 class ConnectionPool;
145 class Cursor;
146 class Database;
147 struct DatabaseActorInfo;
148 class DatabaseFile;
149 class DatabaseLoggingInfo;
150 class DatabaseMaintenance;
151 class Factory;
152 class Maintenance;
153 class MutableFile;
154 class OpenDatabaseOp;
155 class TransactionBase;
156 class TransactionDatabaseOperationBase;
157 class VersionChangeTransaction;
158 
159 /*******************************************************************************
160  * Constants
161  ******************************************************************************/
162 
163 // If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
164 // schema version.
165 static_assert(JS_STRUCTURED_CLONE_VERSION == 8,
166               "Need to update the major schema version.");
167 
168 // Major schema version. Bump for almost everything.
169 const uint32_t kMajorSchemaVersion = 25;
170 
171 // Minor schema version. Should almost always be 0 (maybe bump on release
172 // branches if we have to).
173 const uint32_t kMinorSchemaVersion = 0;
174 
175 // The schema version we store in the SQLite database is a (signed) 32-bit
176 // integer. The major version is left-shifted 4 bits so the max value is
177 // 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
178 static_assert(kMajorSchemaVersion <= 0xFFFFFFF,
179               "Major version needs to fit in 28 bits.");
180 static_assert(kMinorSchemaVersion <= 0xF,
181               "Minor version needs to fit in 4 bits.");
182 
183 const int32_t kSQLiteSchemaVersion =
184   int32_t((kMajorSchemaVersion << 4) + kMinorSchemaVersion);
185 
186 const int32_t kStorageProgressGranularity = 1000;
187 
188 // Changing the value here will override the page size of new databases only.
189 // A journal mode change and VACUUM are needed to change existing databases, so
190 // the best way to do that is to use the schema version upgrade mechanism.
191 const uint32_t kSQLitePageSizeOverride =
192 #ifdef IDB_MOBILE
193   2048;
194 #else
195   4096;
196 #endif
197 
198 static_assert(kSQLitePageSizeOverride == /* mozStorage default */ 0 ||
199               (kSQLitePageSizeOverride % 2 == 0 &&
200                kSQLitePageSizeOverride >= 512  &&
201                kSQLitePageSizeOverride <= 65536),
202               "Must be 0 (disabled) or a power of 2 between 512 and 65536!");
203 
204 // Set to -1 to use SQLite's default, 0 to disable, or some positive number to
205 // enforce a custom limit.
206 const int32_t kMaxWALPages = 5000; // 20MB on desktop, 10MB on mobile.
207 
208 // Set to some multiple of the page size to grow the database in larger chunks.
209 const uint32_t kSQLiteGrowthIncrement = kSQLitePageSizeOverride * 2;
210 
211 static_assert(kSQLiteGrowthIncrement >= 0 &&
212               kSQLiteGrowthIncrement % kSQLitePageSizeOverride == 0 &&
213               kSQLiteGrowthIncrement < uint32_t(INT32_MAX),
214               "Must be 0 (disabled) or a positive multiple of the page size!");
215 
216 // The maximum number of threads that can be used for database activity at a
217 // single time.
218 const uint32_t kMaxConnectionThreadCount = 20;
219 
220 static_assert(kMaxConnectionThreadCount, "Must have at least one thread!");
221 
222 // The maximum number of threads to keep when idle. Threads that become idle in
223 // excess of this number will be shut down immediately.
224 const uint32_t kMaxIdleConnectionThreadCount = 2;
225 
226 static_assert(kMaxConnectionThreadCount >= kMaxIdleConnectionThreadCount,
227               "Idle thread limit must be less than total thread limit!");
228 
229 // The length of time that database connections will be held open after all
230 // transactions have completed before doing idle maintenance.
231 const uint32_t kConnectionIdleMaintenanceMS = 2 * 1000; // 2 seconds
232 
233 // The length of time that database connections will be held open after all
234 // transactions and maintenance have completed.
235 const uint32_t kConnectionIdleCloseMS = 10 * 1000; // 10 seconds
236 
237 // The length of time that idle threads will stay alive before being shut down.
238 const uint32_t kConnectionThreadIdleMS = 30 * 1000; // 30 seconds
239 
240 #define SAVEPOINT_CLAUSE "SAVEPOINT sp;"
241 
242 const uint32_t kFileCopyBufferSize = 32768;
243 
244 #define JOURNAL_DIRECTORY_NAME "journals"
245 
246 const char kFileManagerDirectoryNameSuffix[] = ".files";
247 const char kSQLiteJournalSuffix[] = ".sqlite-journal";
248 const char kSQLiteSHMSuffix[] = ".sqlite-shm";
249 const char kSQLiteWALSuffix[] = ".sqlite-wal";
250 
251 const char kPrefIndexedDBEnabled[] = "dom.indexedDB.enabled";
252 
253 const char kPrefFileHandleEnabled[] = "dom.fileHandle.enabled";
254 
255 #define IDB_PREFIX "indexedDB"
256 
257 #define PERMISSION_STRING_CHROME_BASE IDB_PREFIX "-chrome-"
258 #define PERMISSION_STRING_CHROME_READ_SUFFIX "-read"
259 #define PERMISSION_STRING_CHROME_WRITE_SUFFIX "-write"
260 
261 #ifdef DEBUG
262 
263 const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL;
264 const uint32_t kDEBUGThreadSleepMS = 0;
265 
266 const int32_t kDEBUGTransactionThreadPriority =
267   nsISupportsPriority::PRIORITY_NORMAL;
268 const uint32_t kDEBUGTransactionThreadSleepMS = 0;
269 
270 #endif
271 
272 template <size_t N>
273 constexpr size_t
LiteralStringLength(const char (& aArr)[N])274 LiteralStringLength(const char (&aArr)[N])
275 {
276   static_assert(N, "Zero-length string literal?!");
277 
278   // Don't include the null terminator.
279   return N - 1;
280 }
281 
282 /*******************************************************************************
283  * Metadata classes
284  ******************************************************************************/
285 
286 struct FullIndexMetadata
287 {
288   IndexMetadata mCommonMetadata;
289 
290   bool mDeleted;
291 
292 public:
FullIndexMetadatamozilla::dom::indexedDB::__anone276c2b20111::FullIndexMetadata293   FullIndexMetadata()
294     : mCommonMetadata(0, nsString(), KeyPath(0), nsCString(), false, false, false)
295     , mDeleted(false)
296   {
297     // This can happen either on the QuotaManager IO thread or on a
298     // versionchange transaction thread. These threads can never race so this is
299     // totally safe.
300   }
301 
302   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullIndexMetadata)
303 
304 private:
~FullIndexMetadatamozilla::dom::indexedDB::__anone276c2b20111::FullIndexMetadata305   ~FullIndexMetadata()
306   { }
307 };
308 
309 typedef nsRefPtrHashtable<nsUint64HashKey, FullIndexMetadata> IndexTable;
310 
311 struct FullObjectStoreMetadata
312 {
313   ObjectStoreMetadata mCommonMetadata;
314   IndexTable mIndexes;
315 
316   // These two members are only ever touched on a transaction thread!
317   int64_t mNextAutoIncrementId;
318   int64_t mCommittedAutoIncrementId;
319 
320   bool mDeleted;
321 
322 public:
FullObjectStoreMetadatamozilla::dom::indexedDB::__anone276c2b20111::FullObjectStoreMetadata323   FullObjectStoreMetadata()
324     : mCommonMetadata(0, nsString(), KeyPath(0), false)
325     , mNextAutoIncrementId(0)
326     , mCommittedAutoIncrementId(0)
327     , mDeleted(false)
328   {
329     // This can happen either on the QuotaManager IO thread or on a
330     // versionchange transaction thread. These threads can never race so this is
331     // totally safe.
332   }
333 
334   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullObjectStoreMetadata);
335 
336   bool
337   HasLiveIndexes() const;
338 
339 private:
~FullObjectStoreMetadatamozilla::dom::indexedDB::__anone276c2b20111::FullObjectStoreMetadata340   ~FullObjectStoreMetadata()
341   { }
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:
FullDatabaseMetadatamozilla::dom::indexedDB::__anone276c2b20111::FullDatabaseMetadata358   explicit FullDatabaseMetadata(const DatabaseMetadata& aCommonMetadata)
359     : mCommonMetadata(aCommonMetadata)
360     , mNextObjectStoreId(0)
361     , mNextIndexId(0)
362   {
363     AssertIsOnBackgroundThread();
364   }
365 
366   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullDatabaseMetadata)
367 
368   already_AddRefed<FullDatabaseMetadata>
369   Duplicate() const;
370 
371 private:
~FullDatabaseMetadatamozilla::dom::indexedDB::__anone276c2b20111::FullDatabaseMetadata372   ~FullDatabaseMetadata()
373   { }
374 };
375 
376 template <class MetadataType>
377 class MOZ_STACK_CLASS MetadataNameOrIdMatcher final
378 {
379   typedef MetadataNameOrIdMatcher<MetadataType> SelfType;
380 
381   const int64_t mId;
382   const nsString mName;
383   RefPtr<MetadataType> mMetadata;
384   bool mCheckName;
385 
386 public:
387   template <class Enumerable>
388   static MetadataType*
Match(const Enumerable & aEnumerable,uint64_t aId,const nsAString & aName)389   Match(const Enumerable& aEnumerable, uint64_t aId, const nsAString& aName)
390   {
391     AssertIsOnBackgroundThread();
392     MOZ_ASSERT(aId);
393 
394     SelfType closure(aId, aName);
395     MatchHelper(aEnumerable, &closure);
396 
397     return closure.mMetadata;
398   }
399 
400   template <class Enumerable>
401   static MetadataType*
Match(const Enumerable & aEnumerable,uint64_t aId)402   Match(const Enumerable& aEnumerable, uint64_t aId)
403   {
404     AssertIsOnBackgroundThread();
405     MOZ_ASSERT(aId);
406 
407     SelfType closure(aId);
408     MatchHelper(aEnumerable, &closure);
409 
410     return closure.mMetadata;
411   }
412 
413 private:
MetadataNameOrIdMatcher(const int64_t & aId,const nsAString & aName)414   MetadataNameOrIdMatcher(const int64_t& aId, const nsAString& aName)
415     : mId(aId)
416     , mName(PromiseFlatString(aName))
417     , mMetadata(nullptr)
418     , mCheckName(true)
419   {
420     AssertIsOnBackgroundThread();
421     MOZ_ASSERT(aId);
422   }
423 
MetadataNameOrIdMatcher(const int64_t & aId)424   explicit MetadataNameOrIdMatcher(const int64_t& aId)
425     : mId(aId)
426     , mMetadata(nullptr)
427     , mCheckName(false)
428   {
429     AssertIsOnBackgroundThread();
430     MOZ_ASSERT(aId);
431   }
432 
433   template <class Enumerable>
434   static void
MatchHelper(const Enumerable & aEnumerable,SelfType * aClosure)435   MatchHelper(const Enumerable& aEnumerable, SelfType* aClosure)
436   {
437     AssertIsOnBackgroundThread();
438     MOZ_ASSERT(aClosure);
439 
440     for (auto iter = aEnumerable.ConstIter(); !iter.Done(); iter.Next()) {
441 #ifdef DEBUG
442       const uint64_t key = iter.Key();
443 #endif
444       MetadataType* value = iter.UserData();
445       MOZ_ASSERT(key != 0);
446       MOZ_ASSERT(value);
447 
448       if (!value->mDeleted &&
449           (aClosure->mId == value->mCommonMetadata.id() ||
450            (aClosure->mCheckName &&
451             aClosure->mName == value->mCommonMetadata.name()))) {
452         aClosure->mMetadata = value;
453         break;
454       }
455     }
456   }
457 };
458 
459 struct IndexDataValue final
460 {
461   int64_t mIndexId;
462   Key mKey;
463   Key mSortKey;
464   bool mUnique;
465 
IndexDataValuemozilla::dom::indexedDB::__anone276c2b20111::IndexDataValue466   IndexDataValue()
467     : mIndexId(0)
468     , mUnique(false)
469   {
470     MOZ_COUNT_CTOR(IndexDataValue);
471   }
472 
473   explicit
IndexDataValuemozilla::dom::indexedDB::__anone276c2b20111::IndexDataValue474   IndexDataValue(const IndexDataValue& aOther)
475     : mIndexId(aOther.mIndexId)
476     , mKey(aOther.mKey)
477     , mSortKey(aOther.mSortKey)
478     , mUnique(aOther.mUnique)
479   {
480     MOZ_ASSERT(!aOther.mKey.IsUnset());
481 
482     MOZ_COUNT_CTOR(IndexDataValue);
483   }
484 
IndexDataValuemozilla::dom::indexedDB::__anone276c2b20111::IndexDataValue485   IndexDataValue(int64_t aIndexId, bool aUnique, const Key& aKey)
486     : mIndexId(aIndexId)
487     , mKey(aKey)
488     , mUnique(aUnique)
489   {
490     MOZ_ASSERT(!aKey.IsUnset());
491 
492     MOZ_COUNT_CTOR(IndexDataValue);
493   }
494 
IndexDataValuemozilla::dom::indexedDB::__anone276c2b20111::IndexDataValue495   IndexDataValue(int64_t aIndexId, bool aUnique, const Key& aKey,
496                  const Key& aSortKey)
497     : mIndexId(aIndexId)
498     , mKey(aKey)
499     , mSortKey(aSortKey)
500     , mUnique(aUnique)
501   {
502     MOZ_ASSERT(!aKey.IsUnset());
503 
504     MOZ_COUNT_CTOR(IndexDataValue);
505   }
506 
~IndexDataValuemozilla::dom::indexedDB::__anone276c2b20111::IndexDataValue507   ~IndexDataValue()
508   {
509     MOZ_COUNT_DTOR(IndexDataValue);
510   }
511 
512   bool
operator ==mozilla::dom::indexedDB::__anone276c2b20111::IndexDataValue513   operator==(const IndexDataValue& aOther) const
514   {
515     if (mIndexId != aOther.mIndexId) {
516       return false;
517     }
518     if (mSortKey.IsUnset()) {
519       return mKey == aOther.mKey;
520     }
521     return mSortKey == aOther.mSortKey;
522   }
523 
524   bool
operator <mozilla::dom::indexedDB::__anone276c2b20111::IndexDataValue525   operator<(const IndexDataValue& aOther) const
526   {
527     if (mIndexId == aOther.mIndexId) {
528       if (mSortKey.IsUnset()) {
529         return mKey < aOther.mKey;
530       }
531       return mSortKey < aOther.mSortKey;
532     }
533 
534     return mIndexId < aOther.mIndexId;
535   }
536 };
537 
538 /*******************************************************************************
539  * SQLite functions
540  ******************************************************************************/
541 
542 int32_t
MakeSchemaVersion(uint32_t aMajorSchemaVersion,uint32_t aMinorSchemaVersion)543 MakeSchemaVersion(uint32_t aMajorSchemaVersion,
544                   uint32_t aMinorSchemaVersion)
545 {
546   return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion);
547 }
548 
549 uint32_t
HashName(const nsAString & aName)550 HashName(const nsAString& aName)
551 {
552   struct Helper
553   {
554     static uint32_t
555     RotateBitsLeft32(uint32_t aValue, uint8_t aBits)
556     {
557       MOZ_ASSERT(aBits < 32);
558       return (aValue << aBits) | (aValue >> (32 - aBits));
559     }
560   };
561 
562   static const uint32_t kGoldenRatioU32 = 0x9e3779b9u;
563 
564   const char16_t* str = aName.BeginReading();
565   size_t length = aName.Length();
566 
567   uint32_t hash = 0;
568   for (size_t i = 0; i < length; i++) {
569     hash = kGoldenRatioU32 * (Helper::RotateBitsLeft32(hash, 5) ^ str[i]);
570   }
571 
572   return hash;
573 }
574 
575 nsresult
ClampResultCode(nsresult aResultCode)576 ClampResultCode(nsresult aResultCode)
577 {
578   if (NS_SUCCEEDED(aResultCode) ||
579       NS_ERROR_GET_MODULE(aResultCode) == NS_ERROR_MODULE_DOM_INDEXEDDB) {
580     return aResultCode;
581   }
582 
583   switch (aResultCode) {
584     case NS_ERROR_FILE_NO_DEVICE_SPACE:
585       return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
586     case NS_ERROR_STORAGE_CONSTRAINT:
587       return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
588     default:
589 #ifdef DEBUG
590       nsPrintfCString message("Converting non-IndexedDB error code (0x%X) to "
591                               "NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR",
592                               aResultCode);
593       NS_WARNING(message.get());
594 #else
595       ;
596 #endif
597   }
598 
599   IDB_REPORT_INTERNAL_ERR();
600   return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
601 }
602 
603 void
GetDatabaseFilename(const nsAString & aName,nsAutoString & aDatabaseFilename)604 GetDatabaseFilename(const nsAString& aName,
605                     nsAutoString& aDatabaseFilename)
606 {
607   MOZ_ASSERT(aDatabaseFilename.IsEmpty());
608 
609   aDatabaseFilename.AppendInt(HashName(aName));
610 
611   nsCString escapedName;
612   if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) {
613     MOZ_CRASH("Can't escape database name!");
614   }
615 
616   const char* forwardIter = escapedName.BeginReading();
617   const char* backwardIter = escapedName.EndReading() - 1;
618 
619   nsAutoCString substring;
620   while (forwardIter <= backwardIter && substring.Length() < 21) {
621     if (substring.Length() % 2) {
622       substring.Append(*backwardIter--);
623     } else {
624       substring.Append(*forwardIter++);
625     }
626   }
627 
628   aDatabaseFilename.AppendASCII(substring.get(), substring.Length());
629 }
630 
631 uint32_t
CompressedByteCountForNumber(uint64_t aNumber)632 CompressedByteCountForNumber(uint64_t aNumber)
633 {
634   // All bytes have 7 bits available.
635   uint32_t count = 1;
636   while ((aNumber >>= 7)) {
637     count++;
638   }
639 
640   return count;
641 }
642 
643 uint32_t
CompressedByteCountForIndexId(int64_t aIndexId)644 CompressedByteCountForIndexId(int64_t aIndexId)
645 {
646   MOZ_ASSERT(aIndexId);
647   MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId),
648               "Overflow!");
649 
650   return CompressedByteCountForNumber(uint64_t(aIndexId * 2));
651 }
652 
653 void
WriteCompressedNumber(uint64_t aNumber,uint8_t ** aIterator)654 WriteCompressedNumber(uint64_t aNumber, uint8_t** aIterator)
655 {
656   MOZ_ASSERT(aIterator);
657   MOZ_ASSERT(*aIterator);
658 
659   uint8_t*& buffer = *aIterator;
660 
661 #ifdef DEBUG
662   const uint8_t* bufferStart = buffer;
663   const uint64_t originalNumber = aNumber;
664 #endif
665 
666   while (true) {
667     uint64_t shiftedNumber = aNumber >> 7;
668     if (shiftedNumber) {
669       *buffer++ = uint8_t(0x80 | (aNumber & 0x7f));
670       aNumber = shiftedNumber;
671     } else {
672       *buffer++ = uint8_t(aNumber);
673       break;
674     }
675   }
676 
677   MOZ_ASSERT(buffer > bufferStart);
678   MOZ_ASSERT(uint32_t(buffer - bufferStart) ==
679                CompressedByteCountForNumber(originalNumber));
680 }
681 
682 uint64_t
ReadCompressedNumber(const uint8_t ** aIterator,const uint8_t * aEnd)683 ReadCompressedNumber(const uint8_t** aIterator, const uint8_t* aEnd)
684 {
685   MOZ_ASSERT(aIterator);
686   MOZ_ASSERT(*aIterator);
687   MOZ_ASSERT(aEnd);
688   MOZ_ASSERT(*aIterator < aEnd);
689 
690   const uint8_t*& buffer = *aIterator;
691 
692   uint8_t shiftCounter = 0;
693   uint64_t result = 0;
694 
695   while (true) {
696     MOZ_ASSERT(shiftCounter <= 56, "Shifted too many bits!");
697 
698     result += (uint64_t(*buffer & 0x7f) << shiftCounter);
699     shiftCounter += 7;
700 
701     if (!(*buffer++ & 0x80)) {
702       break;
703     }
704 
705     if (NS_WARN_IF(buffer == aEnd)) {
706       MOZ_ASSERT(false);
707       break;
708     }
709   }
710 
711   return result;
712 }
713 
714 void
WriteCompressedIndexId(int64_t aIndexId,bool aUnique,uint8_t ** aIterator)715 WriteCompressedIndexId(int64_t aIndexId, bool aUnique, uint8_t** aIterator)
716 {
717   MOZ_ASSERT(aIndexId);
718   MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId),
719              "Overflow!");
720   MOZ_ASSERT(aIterator);
721   MOZ_ASSERT(*aIterator);
722 
723   const uint64_t indexId = (uint64_t(aIndexId * 2) | (aUnique ? 1 : 0));
724   WriteCompressedNumber(indexId, aIterator);
725 }
726 
727 void
ReadCompressedIndexId(const uint8_t ** aIterator,const uint8_t * aEnd,int64_t * aIndexId,bool * aUnique)728 ReadCompressedIndexId(const uint8_t** aIterator,
729                       const uint8_t* aEnd,
730                       int64_t* aIndexId,
731                       bool* aUnique)
732 {
733   MOZ_ASSERT(aIterator);
734   MOZ_ASSERT(*aIterator);
735   MOZ_ASSERT(aIndexId);
736   MOZ_ASSERT(aUnique);
737 
738   uint64_t indexId = ReadCompressedNumber(aIterator, aEnd);
739 
740   if (indexId % 2) {
741     *aUnique = true;
742     indexId--;
743   } else {
744     *aUnique = false;
745   }
746 
747   MOZ_ASSERT(UINT64_MAX / 2 >= uint64_t(indexId), "Bad index id!");
748 
749   *aIndexId = int64_t(indexId / 2);
750 }
751 
752 // static
753 nsresult
MakeCompressedIndexDataValues(const FallibleTArray<IndexDataValue> & aIndexValues,UniqueFreePtr<uint8_t> & aCompressedIndexDataValues,uint32_t * aCompressedIndexDataValuesLength)754 MakeCompressedIndexDataValues(
755                              const FallibleTArray<IndexDataValue>& aIndexValues,
756                              UniqueFreePtr<uint8_t>& aCompressedIndexDataValues,
757                              uint32_t* aCompressedIndexDataValuesLength)
758 {
759   MOZ_ASSERT(!NS_IsMainThread());
760   MOZ_ASSERT(!IsOnBackgroundThread());
761   MOZ_ASSERT(!aCompressedIndexDataValues);
762   MOZ_ASSERT(aCompressedIndexDataValuesLength);
763 
764   PROFILER_LABEL("IndexedDB",
765                  "MakeCompressedIndexDataValues",
766                  js::ProfileEntry::Category::STORAGE);
767 
768   const uint32_t arrayLength = aIndexValues.Length();
769   if (!arrayLength) {
770     *aCompressedIndexDataValuesLength = 0;
771     return NS_OK;
772   }
773 
774   // First calculate the size of the final buffer.
775   uint32_t blobDataLength = 0;
776 
777   for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
778     const IndexDataValue& info = aIndexValues[arrayIndex];
779     const nsCString& keyBuffer = info.mKey.GetBuffer();
780     const nsCString& sortKeyBuffer = info.mSortKey.GetBuffer();
781     const uint32_t keyBufferLength = keyBuffer.Length();
782     const uint32_t sortKeyBufferLength = sortKeyBuffer.Length();
783 
784     MOZ_ASSERT(!keyBuffer.IsEmpty());
785 
786     // Don't let |infoLength| overflow.
787     if (NS_WARN_IF(UINT32_MAX - keyBuffer.Length() <
788                    CompressedByteCountForIndexId(info.mIndexId) +
789                    CompressedByteCountForNumber(keyBufferLength) +
790                    CompressedByteCountForNumber(sortKeyBufferLength))) {
791       IDB_REPORT_INTERNAL_ERR();
792       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
793     }
794 
795     const uint32_t infoLength =
796       CompressedByteCountForIndexId(info.mIndexId) +
797       CompressedByteCountForNumber(keyBufferLength) +
798       CompressedByteCountForNumber(sortKeyBufferLength) +
799       keyBufferLength +
800       sortKeyBufferLength;
801 
802     // Don't let |blobDataLength| overflow.
803     if (NS_WARN_IF(UINT32_MAX - infoLength < blobDataLength)) {
804       IDB_REPORT_INTERNAL_ERR();
805       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
806     }
807 
808     blobDataLength += infoLength;
809   }
810 
811   UniqueFreePtr<uint8_t> blobData(
812     static_cast<uint8_t*>(malloc(blobDataLength)));
813   if (NS_WARN_IF(!blobData)) {
814     IDB_REPORT_INTERNAL_ERR();
815     return NS_ERROR_OUT_OF_MEMORY;
816   }
817 
818   uint8_t* blobDataIter = blobData.get();
819 
820   for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
821     const IndexDataValue& info = aIndexValues[arrayIndex];
822     const nsCString& keyBuffer = info.mKey.GetBuffer();
823     const nsCString& sortKeyBuffer = info.mSortKey.GetBuffer();
824     const uint32_t keyBufferLength = keyBuffer.Length();
825     const uint32_t sortKeyBufferLength = sortKeyBuffer.Length();
826 
827     WriteCompressedIndexId(info.mIndexId, info.mUnique, &blobDataIter);
828     WriteCompressedNumber(keyBufferLength, &blobDataIter);
829 
830     memcpy(blobDataIter, keyBuffer.get(), keyBufferLength);
831     blobDataIter += keyBufferLength;
832 
833     WriteCompressedNumber(sortKeyBufferLength, &blobDataIter);
834 
835     memcpy(blobDataIter, sortKeyBuffer.get(), sortKeyBufferLength);
836     blobDataIter += sortKeyBufferLength;
837   }
838 
839   MOZ_ASSERT(blobDataIter == blobData.get() + blobDataLength);
840 
841   aCompressedIndexDataValues.swap(blobData);
842   *aCompressedIndexDataValuesLength = uint32_t(blobDataLength);
843 
844   return NS_OK;
845 }
846 
847 nsresult
ReadCompressedIndexDataValuesFromBlob(const uint8_t * aBlobData,uint32_t aBlobDataLength,nsTArray<IndexDataValue> & aIndexValues)848 ReadCompressedIndexDataValuesFromBlob(const uint8_t* aBlobData,
849                                       uint32_t aBlobDataLength,
850                                       nsTArray<IndexDataValue>& aIndexValues)
851 {
852   MOZ_ASSERT(!NS_IsMainThread());
853   MOZ_ASSERT(!IsOnBackgroundThread());
854   MOZ_ASSERT(aBlobData);
855   MOZ_ASSERT(aBlobDataLength);
856   MOZ_ASSERT(aIndexValues.IsEmpty());
857 
858   PROFILER_LABEL("IndexedDB",
859                  "ReadCompressedIndexDataValuesFromBlob",
860                  js::ProfileEntry::Category::STORAGE);
861 
862   const uint8_t* blobDataIter = aBlobData;
863   const uint8_t* blobDataEnd = aBlobData + aBlobDataLength;
864 
865   while (blobDataIter < blobDataEnd) {
866     int64_t indexId;
867     bool unique;
868     ReadCompressedIndexId(&blobDataIter, blobDataEnd, &indexId, &unique);
869 
870     if (NS_WARN_IF(blobDataIter == blobDataEnd)) {
871       IDB_REPORT_INTERNAL_ERR();
872       return NS_ERROR_FILE_CORRUPTED;
873     }
874 
875     // Read key buffer length.
876     const uint64_t keyBufferLength =
877       ReadCompressedNumber(&blobDataIter, blobDataEnd);
878 
879     if (NS_WARN_IF(blobDataIter == blobDataEnd) ||
880         NS_WARN_IF(keyBufferLength > uint64_t(UINT32_MAX)) ||
881         NS_WARN_IF(blobDataIter + keyBufferLength > blobDataEnd)) {
882       IDB_REPORT_INTERNAL_ERR();
883       return NS_ERROR_FILE_CORRUPTED;
884     }
885 
886     nsCString keyBuffer(reinterpret_cast<const char*>(blobDataIter),
887                         uint32_t(keyBufferLength));
888     blobDataIter += keyBufferLength;
889 
890     IndexDataValue idv(indexId, unique, Key(keyBuffer));
891 
892     // Read sort key buffer length.
893     const uint64_t sortKeyBufferLength =
894       ReadCompressedNumber(&blobDataIter, blobDataEnd);
895 
896     if (sortKeyBufferLength > 0) {
897       if (NS_WARN_IF(blobDataIter == blobDataEnd) ||
898           NS_WARN_IF(sortKeyBufferLength > uint64_t(UINT32_MAX)) ||
899           NS_WARN_IF(blobDataIter + sortKeyBufferLength > blobDataEnd)) {
900         IDB_REPORT_INTERNAL_ERR();
901         return NS_ERROR_FILE_CORRUPTED;
902       }
903 
904       nsCString sortKeyBuffer(reinterpret_cast<const char*>(blobDataIter),
905                               uint32_t(sortKeyBufferLength));
906       blobDataIter += sortKeyBufferLength;
907 
908       idv.mSortKey = Key(sortKeyBuffer);
909     }
910 
911     if (NS_WARN_IF(!aIndexValues.InsertElementSorted(idv, fallible))) {
912       IDB_REPORT_INTERNAL_ERR();
913       return NS_ERROR_OUT_OF_MEMORY;
914     }
915   }
916 
917   MOZ_ASSERT(blobDataIter == blobDataEnd);
918 
919   return NS_OK;
920 }
921 
922 // static
923 template <typename T>
924 nsresult
ReadCompressedIndexDataValuesFromSource(T * aSource,uint32_t aColumnIndex,nsTArray<IndexDataValue> & aIndexValues)925 ReadCompressedIndexDataValuesFromSource(T* aSource,
926                                         uint32_t aColumnIndex,
927                                         nsTArray<IndexDataValue>& aIndexValues)
928 {
929   MOZ_ASSERT(!NS_IsMainThread());
930   MOZ_ASSERT(!IsOnBackgroundThread());
931   MOZ_ASSERT(aSource);
932   MOZ_ASSERT(aIndexValues.IsEmpty());
933 
934   int32_t columnType;
935   nsresult rv = aSource->GetTypeOfIndex(aColumnIndex, &columnType);
936   if (NS_WARN_IF(NS_FAILED(rv))) {
937     return rv;
938   }
939 
940   if (columnType == mozIStorageStatement::VALUE_TYPE_NULL) {
941     return NS_OK;
942   }
943 
944   MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_BLOB);
945 
946   const uint8_t* blobData;
947   uint32_t blobDataLength;
948   rv = aSource->GetSharedBlob(aColumnIndex, &blobDataLength, &blobData);
949   if (NS_WARN_IF(NS_FAILED(rv))) {
950     return rv;
951   }
952 
953   if (NS_WARN_IF(!blobDataLength)) {
954     IDB_REPORT_INTERNAL_ERR();
955     return NS_ERROR_FILE_CORRUPTED;
956   }
957 
958   rv = ReadCompressedIndexDataValuesFromBlob(blobData,
959                                              blobDataLength,
960                                              aIndexValues);
961   if (NS_WARN_IF(NS_FAILED(rv))) {
962     return rv;
963   }
964 
965   return NS_OK;
966 }
967 
968 nsresult
ReadCompressedIndexDataValues(mozIStorageStatement * aStatement,uint32_t aColumnIndex,nsTArray<IndexDataValue> & aIndexValues)969 ReadCompressedIndexDataValues(mozIStorageStatement* aStatement,
970                               uint32_t aColumnIndex,
971                               nsTArray<IndexDataValue>& aIndexValues)
972 {
973   return ReadCompressedIndexDataValuesFromSource(aStatement,
974                                                  aColumnIndex,
975                                                  aIndexValues);
976 }
977 
978 nsresult
ReadCompressedIndexDataValues(mozIStorageValueArray * aValues,uint32_t aColumnIndex,nsTArray<IndexDataValue> & aIndexValues)979 ReadCompressedIndexDataValues(mozIStorageValueArray* aValues,
980                               uint32_t aColumnIndex,
981                               nsTArray<IndexDataValue>& aIndexValues)
982 {
983   return ReadCompressedIndexDataValuesFromSource(aValues,
984                                                  aColumnIndex,
985                                                  aIndexValues);
986 }
987 
988 nsresult
CreateFileTables(mozIStorageConnection * aConnection)989 CreateFileTables(mozIStorageConnection* aConnection)
990 {
991   AssertIsOnIOThread();
992   MOZ_ASSERT(aConnection);
993 
994   PROFILER_LABEL("IndexedDB",
995                  "CreateFileTables",
996                  js::ProfileEntry::Category::STORAGE);
997 
998   // Table `file`
999   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1000     "CREATE TABLE file ("
1001       "id INTEGER PRIMARY KEY, "
1002       "refcount INTEGER NOT NULL"
1003     ");"
1004   ));
1005   if (NS_WARN_IF(NS_FAILED(rv))) {
1006     return rv;
1007   }
1008 
1009   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1010     "CREATE TRIGGER object_data_insert_trigger "
1011     "AFTER INSERT ON object_data "
1012     "FOR EACH ROW "
1013     "WHEN NEW.file_ids IS NOT NULL "
1014     "BEGIN "
1015       "SELECT update_refcount(NULL, NEW.file_ids); "
1016     "END;"
1017   ));
1018   if (NS_WARN_IF(NS_FAILED(rv))) {
1019     return rv;
1020   }
1021 
1022   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1023     "CREATE TRIGGER object_data_update_trigger "
1024     "AFTER UPDATE OF file_ids ON object_data "
1025     "FOR EACH ROW "
1026     "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
1027     "BEGIN "
1028       "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
1029     "END;"
1030   ));
1031   if (NS_WARN_IF(NS_FAILED(rv))) {
1032     return rv;
1033   }
1034 
1035   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1036     "CREATE TRIGGER object_data_delete_trigger "
1037     "AFTER DELETE ON object_data "
1038     "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
1039     "BEGIN "
1040       "SELECT update_refcount(OLD.file_ids, NULL); "
1041     "END;"
1042   ));
1043   if (NS_WARN_IF(NS_FAILED(rv))) {
1044     return rv;
1045   }
1046 
1047   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1048     "CREATE TRIGGER file_update_trigger "
1049     "AFTER UPDATE ON file "
1050     "FOR EACH ROW WHEN NEW.refcount = 0 "
1051     "BEGIN "
1052       "DELETE FROM file WHERE id = OLD.id; "
1053     "END;"
1054   ));
1055   if (NS_WARN_IF(NS_FAILED(rv))) {
1056     return rv;
1057   }
1058 
1059   return NS_OK;
1060 }
1061 
1062 nsresult
CreateTables(mozIStorageConnection * aConnection)1063 CreateTables(mozIStorageConnection* aConnection)
1064 {
1065   AssertIsOnIOThread();
1066   MOZ_ASSERT(aConnection);
1067 
1068   PROFILER_LABEL("IndexedDB",
1069                  "CreateTables",
1070                  js::ProfileEntry::Category::STORAGE);
1071 
1072   // Table `database`
1073 
1074   // There are two reasons for having the origin column.
1075   // First, we can ensure that we don't have collisions in the origin hash we
1076   // use for the path because when we open the db we can make sure that the
1077   // origins exactly match. Second, chrome code crawling through the idb
1078   // directory can figure out the origin of every db without having to
1079   // reverse-engineer our hash scheme.
1080   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1081     "CREATE TABLE database"
1082       "( name TEXT PRIMARY KEY"
1083       ", origin TEXT NOT NULL"
1084       ", version INTEGER NOT NULL DEFAULT 0"
1085       ", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
1086       ", last_analyze_time INTEGER NOT NULL DEFAULT 0"
1087       ", last_vacuum_size INTEGER NOT NULL DEFAULT 0"
1088       ") WITHOUT ROWID;"
1089   ));
1090   if (NS_WARN_IF(NS_FAILED(rv))) {
1091     return rv;
1092   }
1093 
1094   // Table `object_store`
1095   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1096     "CREATE TABLE object_store"
1097       "( id INTEGER PRIMARY KEY"
1098       ", auto_increment INTEGER NOT NULL DEFAULT 0"
1099       ", name TEXT NOT NULL"
1100       ", key_path TEXT"
1101       ");"
1102   ));
1103   if (NS_WARN_IF(NS_FAILED(rv))) {
1104     return rv;
1105   }
1106 
1107   // Table `object_store_index`
1108   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1109     "CREATE TABLE object_store_index"
1110       "( id INTEGER PRIMARY KEY"
1111       ", object_store_id INTEGER NOT NULL"
1112       ", name TEXT NOT NULL"
1113       ", key_path TEXT NOT NULL"
1114       ", unique_index INTEGER NOT NULL"
1115       ", multientry INTEGER NOT NULL"
1116       ", locale TEXT"
1117       ", is_auto_locale BOOLEAN NOT NULL"
1118       ", FOREIGN KEY (object_store_id) "
1119           "REFERENCES object_store(id) "
1120       ");"
1121   ));
1122   if (NS_WARN_IF(NS_FAILED(rv))) {
1123     return rv;
1124   }
1125 
1126   // Table `object_data`
1127   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1128     "CREATE TABLE object_data"
1129       "( object_store_id INTEGER NOT NULL"
1130       ", key BLOB NOT NULL"
1131       ", index_data_values BLOB DEFAULT NULL"
1132       ", file_ids TEXT"
1133       ", data BLOB NOT NULL"
1134       ", PRIMARY KEY (object_store_id, key)"
1135       ", FOREIGN KEY (object_store_id) "
1136           "REFERENCES object_store(id) "
1137       ") WITHOUT ROWID;"
1138   ));
1139   if (NS_WARN_IF(NS_FAILED(rv))) {
1140     return rv;
1141   }
1142 
1143   // Table `index_data`
1144   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1145     "CREATE TABLE index_data"
1146       "( index_id INTEGER NOT NULL"
1147       ", value BLOB NOT NULL"
1148       ", object_data_key BLOB NOT NULL"
1149       ", object_store_id INTEGER NOT NULL"
1150       ", value_locale BLOB"
1151       ", PRIMARY KEY (index_id, value, object_data_key)"
1152       ", FOREIGN KEY (index_id) "
1153           "REFERENCES object_store_index(id) "
1154       ", FOREIGN KEY (object_store_id, object_data_key) "
1155           "REFERENCES object_data(object_store_id, key) "
1156       ") WITHOUT ROWID;"
1157   ));
1158   if (NS_WARN_IF(NS_FAILED(rv))) {
1159     return rv;
1160   }
1161 
1162   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1163     "CREATE INDEX index_data_value_locale_index "
1164     "ON index_data (index_id, value_locale, object_data_key, value) "
1165     "WHERE value_locale IS NOT NULL;"
1166   ));
1167   if (NS_WARN_IF(NS_FAILED(rv))) {
1168     return rv;
1169   }
1170 
1171   // Table `unique_index_data`
1172   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1173     "CREATE TABLE unique_index_data"
1174       "( index_id INTEGER NOT NULL"
1175       ", value BLOB NOT NULL"
1176       ", object_store_id INTEGER NOT NULL"
1177       ", object_data_key BLOB NOT NULL"
1178       ", value_locale BLOB"
1179       ", PRIMARY KEY (index_id, value)"
1180       ", FOREIGN KEY (index_id) "
1181           "REFERENCES object_store_index(id) "
1182       ", FOREIGN KEY (object_store_id, object_data_key) "
1183           "REFERENCES object_data(object_store_id, key) "
1184       ") WITHOUT ROWID;"
1185   ));
1186   if (NS_WARN_IF(NS_FAILED(rv))) {
1187     return rv;
1188   }
1189 
1190   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1191     "CREATE INDEX unique_index_data_value_locale_index "
1192     "ON unique_index_data (index_id, value_locale, object_data_key, value) "
1193     "WHERE value_locale IS NOT NULL;"
1194   ));
1195   if (NS_WARN_IF(NS_FAILED(rv))) {
1196     return rv;
1197   }
1198 
1199   rv = CreateFileTables(aConnection);
1200   if (NS_WARN_IF(NS_FAILED(rv))) {
1201     return rv;
1202   }
1203 
1204   rv = aConnection->SetSchemaVersion(kSQLiteSchemaVersion);
1205   if (NS_WARN_IF(NS_FAILED(rv))) {
1206     return rv;
1207   }
1208 
1209   return NS_OK;
1210 }
1211 
1212 nsresult
UpgradeSchemaFrom4To5(mozIStorageConnection * aConnection)1213 UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection)
1214 {
1215   AssertIsOnIOThread();
1216   MOZ_ASSERT(aConnection);
1217 
1218   PROFILER_LABEL("IndexedDB",
1219                  "UpgradeSchemaFrom4To5",
1220                  js::ProfileEntry::Category::STORAGE);
1221 
1222   nsresult rv;
1223 
1224   // All we changed is the type of the version column, so lets try to
1225   // convert that to an integer, and if we fail, set it to 0.
1226   nsCOMPtr<mozIStorageStatement> stmt;
1227   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
1228     "SELECT name, version, dataVersion "
1229     "FROM database"
1230   ), getter_AddRefs(stmt));
1231   if (NS_WARN_IF(NS_FAILED(rv))) {
1232     return rv;
1233   }
1234 
1235   nsString name;
1236   int32_t intVersion;
1237   int64_t dataVersion;
1238 
1239   {
1240     mozStorageStatementScoper scoper(stmt);
1241 
1242     bool hasResults;
1243     rv = stmt->ExecuteStep(&hasResults);
1244     if (NS_WARN_IF(NS_FAILED(rv))) {
1245       return rv;
1246     }
1247     if (NS_WARN_IF(!hasResults)) {
1248       return NS_ERROR_FAILURE;
1249     }
1250 
1251     nsString version;
1252     rv = stmt->GetString(1, version);
1253     if (NS_WARN_IF(NS_FAILED(rv))) {
1254       return rv;
1255     }
1256 
1257     intVersion = version.ToInteger(&rv);
1258     if (NS_FAILED(rv)) {
1259       intVersion = 0;
1260     }
1261 
1262     rv = stmt->GetString(0, name);
1263     if (NS_WARN_IF(NS_FAILED(rv))) {
1264       return rv;
1265     }
1266 
1267     rv = stmt->GetInt64(2, &dataVersion);
1268     if (NS_WARN_IF(NS_FAILED(rv))) {
1269       return rv;
1270     }
1271   }
1272 
1273   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1274     "DROP TABLE database"
1275   ));
1276   if (NS_WARN_IF(NS_FAILED(rv))) {
1277     return rv;
1278   }
1279 
1280   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1281     "CREATE TABLE database ("
1282       "name TEXT NOT NULL, "
1283       "version INTEGER NOT NULL DEFAULT 0, "
1284       "dataVersion INTEGER NOT NULL"
1285     ");"
1286   ));
1287   if (NS_WARN_IF(NS_FAILED(rv))) {
1288     return rv;
1289   }
1290 
1291   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
1292     "INSERT INTO database (name, version, dataVersion) "
1293     "VALUES (:name, :version, :dataVersion)"
1294   ), getter_AddRefs(stmt));
1295   if (NS_WARN_IF(NS_FAILED(rv))) {
1296     return rv;
1297   }
1298 
1299   {
1300     mozStorageStatementScoper scoper(stmt);
1301 
1302     rv = stmt->BindStringParameter(0, name);
1303     if (NS_WARN_IF(NS_FAILED(rv))) {
1304       return rv;
1305     }
1306 
1307     rv = stmt->BindInt32Parameter(1, intVersion);
1308     if (NS_WARN_IF(NS_FAILED(rv))) {
1309       return rv;
1310     }
1311 
1312     rv = stmt->BindInt64Parameter(2, dataVersion);
1313     if (NS_WARN_IF(NS_FAILED(rv))) {
1314       return rv;
1315     }
1316 
1317     rv = stmt->Execute();
1318     if (NS_WARN_IF(NS_FAILED(rv))) {
1319       return rv;
1320     }
1321   }
1322 
1323   rv = aConnection->SetSchemaVersion(5);
1324   if (NS_WARN_IF(NS_FAILED(rv))) {
1325     return rv;
1326   }
1327 
1328   return NS_OK;
1329 }
1330 
1331 nsresult
UpgradeSchemaFrom5To6(mozIStorageConnection * aConnection)1332 UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection)
1333 {
1334   AssertIsOnIOThread();
1335   MOZ_ASSERT(aConnection);
1336 
1337   PROFILER_LABEL("IndexedDB",
1338                  "UpgradeSchemaFrom5To6",
1339                  js::ProfileEntry::Category::STORAGE);
1340 
1341   // First, drop all the indexes we're no longer going to use.
1342   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1343     "DROP INDEX key_index;"
1344   ));
1345   if (NS_WARN_IF(NS_FAILED(rv))) {
1346     return rv;
1347   }
1348 
1349   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1350     "DROP INDEX ai_key_index;"
1351   ));
1352   if (NS_WARN_IF(NS_FAILED(rv))) {
1353     return rv;
1354   }
1355 
1356   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1357     "DROP INDEX value_index;"
1358   ));
1359   if (NS_WARN_IF(NS_FAILED(rv))) {
1360     return rv;
1361   }
1362 
1363   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1364     "DROP INDEX ai_value_index;"
1365   ));
1366   if (NS_WARN_IF(NS_FAILED(rv))) {
1367     return rv;
1368   }
1369 
1370   // Now, reorder the columns of object_data to put the blob data last. We do
1371   // this by copying into a temporary table, dropping the original, then copying
1372   // back into a newly created table.
1373   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1374     "CREATE TEMPORARY TABLE temp_upgrade ("
1375       "id INTEGER PRIMARY KEY, "
1376       "object_store_id, "
1377       "key_value, "
1378       "data "
1379     ");"
1380   ));
1381   if (NS_WARN_IF(NS_FAILED(rv))) {
1382     return rv;
1383   }
1384 
1385   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1386     "INSERT INTO temp_upgrade "
1387       "SELECT id, object_store_id, key_value, data "
1388       "FROM object_data;"
1389   ));
1390   if (NS_WARN_IF(NS_FAILED(rv))) {
1391     return rv;
1392   }
1393 
1394   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1395     "DROP TABLE object_data;"
1396   ));
1397   if (NS_WARN_IF(NS_FAILED(rv))) {
1398     return rv;
1399   }
1400 
1401   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1402     "CREATE TABLE object_data ("
1403       "id INTEGER PRIMARY KEY, "
1404       "object_store_id INTEGER NOT NULL, "
1405       "key_value DEFAULT NULL, "
1406       "data BLOB NOT NULL, "
1407       "UNIQUE (object_store_id, key_value), "
1408       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1409         "CASCADE"
1410     ");"
1411   ));
1412   if (NS_WARN_IF(NS_FAILED(rv))) {
1413     return rv;
1414   }
1415 
1416   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1417     "INSERT INTO object_data "
1418       "SELECT id, object_store_id, key_value, data "
1419       "FROM temp_upgrade;"
1420   ));
1421   if (NS_WARN_IF(NS_FAILED(rv))) {
1422     return rv;
1423   }
1424 
1425   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1426     "DROP TABLE temp_upgrade;"
1427   ));
1428   if (NS_WARN_IF(NS_FAILED(rv))) {
1429     return rv;
1430   }
1431 
1432   // We need to add a unique constraint to our ai_object_data table. Copy all
1433   // the data out of it using a temporary table as before.
1434   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1435     "CREATE TEMPORARY TABLE temp_upgrade ("
1436       "id INTEGER PRIMARY KEY, "
1437       "object_store_id, "
1438       "data "
1439     ");"
1440   ));
1441   if (NS_WARN_IF(NS_FAILED(rv))) {
1442     return rv;
1443   }
1444 
1445   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1446     "INSERT INTO temp_upgrade "
1447       "SELECT id, object_store_id, data "
1448       "FROM ai_object_data;"
1449   ));
1450   if (NS_WARN_IF(NS_FAILED(rv))) {
1451     return rv;
1452   }
1453 
1454   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1455     "DROP TABLE ai_object_data;"
1456   ));
1457   if (NS_WARN_IF(NS_FAILED(rv))) {
1458     return rv;
1459   }
1460 
1461   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1462     "CREATE TABLE ai_object_data ("
1463       "id INTEGER PRIMARY KEY AUTOINCREMENT, "
1464       "object_store_id INTEGER NOT NULL, "
1465       "data BLOB NOT NULL, "
1466       "UNIQUE (object_store_id, id), "
1467       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1468         "CASCADE"
1469     ");"
1470   ));
1471   if (NS_WARN_IF(NS_FAILED(rv))) {
1472     return rv;
1473   }
1474 
1475   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1476     "INSERT INTO ai_object_data "
1477       "SELECT id, object_store_id, data "
1478       "FROM temp_upgrade;"
1479   ));
1480   if (NS_WARN_IF(NS_FAILED(rv))) {
1481     return rv;
1482   }
1483 
1484   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1485     "DROP TABLE temp_upgrade;"
1486   ));
1487   if (NS_WARN_IF(NS_FAILED(rv))) {
1488     return rv;
1489   }
1490 
1491   // Fix up the index_data table. We're reordering the columns as well as
1492   // changing the primary key from being a simple id to being a composite.
1493   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1494     "CREATE TEMPORARY TABLE temp_upgrade ("
1495       "index_id, "
1496       "value, "
1497       "object_data_key, "
1498       "object_data_id "
1499     ");"
1500   ));
1501   if (NS_WARN_IF(NS_FAILED(rv))) {
1502     return rv;
1503   }
1504 
1505   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1506     "INSERT INTO temp_upgrade "
1507       "SELECT index_id, value, object_data_key, object_data_id "
1508       "FROM index_data;"
1509   ));
1510   if (NS_WARN_IF(NS_FAILED(rv))) {
1511     return rv;
1512   }
1513 
1514   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1515     "DROP TABLE index_data;"
1516   ));
1517   if (NS_WARN_IF(NS_FAILED(rv))) {
1518     return rv;
1519   }
1520 
1521   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1522     "CREATE TABLE index_data ("
1523       "index_id INTEGER NOT NULL, "
1524       "value NOT NULL, "
1525       "object_data_key NOT NULL, "
1526       "object_data_id INTEGER NOT NULL, "
1527       "PRIMARY KEY (index_id, value, object_data_key), "
1528       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1529         "CASCADE, "
1530       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1531         "CASCADE"
1532     ");"
1533   ));
1534   if (NS_WARN_IF(NS_FAILED(rv))) {
1535     return rv;
1536   }
1537 
1538   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1539     "INSERT OR IGNORE INTO index_data "
1540       "SELECT index_id, value, object_data_key, object_data_id "
1541       "FROM temp_upgrade;"
1542   ));
1543   if (NS_WARN_IF(NS_FAILED(rv))) {
1544     return rv;
1545   }
1546 
1547   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1548     "DROP TABLE temp_upgrade;"
1549   ));
1550   if (NS_WARN_IF(NS_FAILED(rv))) {
1551     return rv;
1552   }
1553 
1554   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1555     "CREATE INDEX index_data_object_data_id_index "
1556     "ON index_data (object_data_id);"
1557   ));
1558   if (NS_WARN_IF(NS_FAILED(rv))) {
1559     return rv;
1560   }
1561 
1562   // Fix up the unique_index_data table. We're reordering the columns as well as
1563   // changing the primary key from being a simple id to being a composite.
1564   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1565     "CREATE TEMPORARY TABLE temp_upgrade ("
1566       "index_id, "
1567       "value, "
1568       "object_data_key, "
1569       "object_data_id "
1570     ");"
1571   ));
1572   if (NS_WARN_IF(NS_FAILED(rv))) {
1573     return rv;
1574   }
1575 
1576   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1577     "INSERT INTO temp_upgrade "
1578       "SELECT index_id, value, object_data_key, object_data_id "
1579       "FROM unique_index_data;"
1580   ));
1581   if (NS_WARN_IF(NS_FAILED(rv))) {
1582     return rv;
1583   }
1584 
1585   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1586     "DROP TABLE unique_index_data;"
1587   ));
1588   if (NS_WARN_IF(NS_FAILED(rv))) {
1589     return rv;
1590   }
1591 
1592   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1593     "CREATE TABLE unique_index_data ("
1594       "index_id INTEGER NOT NULL, "
1595       "value NOT NULL, "
1596       "object_data_key NOT NULL, "
1597       "object_data_id INTEGER NOT NULL, "
1598       "PRIMARY KEY (index_id, value, object_data_key), "
1599       "UNIQUE (index_id, value), "
1600       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1601         "CASCADE "
1602       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1603         "CASCADE"
1604     ");"
1605   ));
1606   if (NS_WARN_IF(NS_FAILED(rv))) {
1607     return rv;
1608   }
1609 
1610   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1611     "INSERT INTO unique_index_data "
1612       "SELECT index_id, value, object_data_key, object_data_id "
1613       "FROM temp_upgrade;"
1614   ));
1615   if (NS_WARN_IF(NS_FAILED(rv))) {
1616     return rv;
1617   }
1618 
1619   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1620     "DROP TABLE temp_upgrade;"
1621   ));
1622   if (NS_WARN_IF(NS_FAILED(rv))) {
1623     return rv;
1624   }
1625 
1626   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1627     "CREATE INDEX unique_index_data_object_data_id_index "
1628     "ON unique_index_data (object_data_id);"
1629   ));
1630   if (NS_WARN_IF(NS_FAILED(rv))) {
1631     return rv;
1632   }
1633 
1634   // Fix up the ai_index_data table. We're reordering the columns as well as
1635   // changing the primary key from being a simple id to being a composite.
1636   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1637     "CREATE TEMPORARY TABLE temp_upgrade ("
1638       "index_id, "
1639       "value, "
1640       "ai_object_data_id "
1641     ");"
1642   ));
1643   if (NS_WARN_IF(NS_FAILED(rv))) {
1644     return rv;
1645   }
1646 
1647   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1648     "INSERT INTO temp_upgrade "
1649       "SELECT index_id, value, ai_object_data_id "
1650       "FROM ai_index_data;"
1651   ));
1652   if (NS_WARN_IF(NS_FAILED(rv))) {
1653     return rv;
1654   }
1655 
1656   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1657     "DROP TABLE ai_index_data;"
1658   ));
1659   if (NS_WARN_IF(NS_FAILED(rv))) {
1660     return rv;
1661   }
1662 
1663   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1664     "CREATE TABLE ai_index_data ("
1665       "index_id INTEGER NOT NULL, "
1666       "value NOT NULL, "
1667       "ai_object_data_id INTEGER NOT NULL, "
1668       "PRIMARY KEY (index_id, value, ai_object_data_id), "
1669       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1670         "CASCADE, "
1671       "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
1672         "CASCADE"
1673     ");"
1674   ));
1675   if (NS_WARN_IF(NS_FAILED(rv))) {
1676     return rv;
1677   }
1678 
1679   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1680     "INSERT OR IGNORE INTO ai_index_data "
1681       "SELECT index_id, value, ai_object_data_id "
1682       "FROM temp_upgrade;"
1683   ));
1684   if (NS_WARN_IF(NS_FAILED(rv))) {
1685     return rv;
1686   }
1687 
1688   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1689     "DROP TABLE temp_upgrade;"
1690   ));
1691   if (NS_WARN_IF(NS_FAILED(rv))) {
1692     return rv;
1693   }
1694 
1695   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1696     "CREATE INDEX ai_index_data_ai_object_data_id_index "
1697     "ON ai_index_data (ai_object_data_id);"
1698   ));
1699   if (NS_WARN_IF(NS_FAILED(rv))) {
1700     return rv;
1701   }
1702 
1703   // Fix up the ai_unique_index_data table. We're reordering the columns as well
1704   // as changing the primary key from being a simple id to being a composite.
1705   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1706     "CREATE TEMPORARY TABLE temp_upgrade ("
1707       "index_id, "
1708       "value, "
1709       "ai_object_data_id "
1710     ");"
1711   ));
1712   if (NS_WARN_IF(NS_FAILED(rv))) {
1713     return rv;
1714   }
1715 
1716   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1717     "INSERT INTO temp_upgrade "
1718       "SELECT index_id, value, ai_object_data_id "
1719       "FROM ai_unique_index_data;"
1720   ));
1721   if (NS_WARN_IF(NS_FAILED(rv))) {
1722     return rv;
1723   }
1724 
1725   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1726     "DROP TABLE ai_unique_index_data;"
1727   ));
1728   if (NS_WARN_IF(NS_FAILED(rv))) {
1729     return rv;
1730   }
1731 
1732   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1733     "CREATE TABLE ai_unique_index_data ("
1734       "index_id INTEGER NOT NULL, "
1735       "value NOT NULL, "
1736       "ai_object_data_id INTEGER NOT NULL, "
1737       "UNIQUE (index_id, value), "
1738       "PRIMARY KEY (index_id, value, ai_object_data_id), "
1739       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1740         "CASCADE, "
1741       "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
1742         "CASCADE"
1743     ");"
1744   ));
1745   if (NS_WARN_IF(NS_FAILED(rv))) {
1746     return rv;
1747   }
1748 
1749   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1750     "INSERT INTO ai_unique_index_data "
1751       "SELECT index_id, value, ai_object_data_id "
1752       "FROM temp_upgrade;"
1753   ));
1754   if (NS_WARN_IF(NS_FAILED(rv))) {
1755     return rv;
1756   }
1757 
1758   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1759     "DROP TABLE temp_upgrade;"
1760   ));
1761   if (NS_WARN_IF(NS_FAILED(rv))) {
1762     return rv;
1763   }
1764 
1765   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1766     "CREATE INDEX ai_unique_index_data_ai_object_data_id_index "
1767     "ON ai_unique_index_data (ai_object_data_id);"
1768   ));
1769   if (NS_WARN_IF(NS_FAILED(rv))) {
1770     return rv;
1771   }
1772 
1773   rv = aConnection->SetSchemaVersion(6);
1774   if (NS_WARN_IF(NS_FAILED(rv))) {
1775     return rv;
1776   }
1777 
1778   return NS_OK;
1779 }
1780 
1781 nsresult
UpgradeSchemaFrom6To7(mozIStorageConnection * aConnection)1782 UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection)
1783 {
1784   AssertIsOnIOThread();
1785   MOZ_ASSERT(aConnection);
1786 
1787   PROFILER_LABEL("IndexedDB",
1788                  "UpgradeSchemaFrom6To7",
1789                  js::ProfileEntry::Category::STORAGE);
1790 
1791   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1792     "CREATE TEMPORARY TABLE temp_upgrade ("
1793       "id, "
1794       "name, "
1795       "key_path, "
1796       "auto_increment"
1797     ");"
1798   ));
1799   if (NS_WARN_IF(NS_FAILED(rv))) {
1800     return rv;
1801   }
1802 
1803   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1804     "INSERT INTO temp_upgrade "
1805       "SELECT id, name, key_path, auto_increment "
1806       "FROM object_store;"
1807   ));
1808   if (NS_WARN_IF(NS_FAILED(rv))) {
1809     return rv;
1810   }
1811 
1812   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1813     "DROP TABLE object_store;"
1814   ));
1815   if (NS_WARN_IF(NS_FAILED(rv))) {
1816     return rv;
1817   }
1818 
1819   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1820     "CREATE TABLE object_store ("
1821       "id INTEGER PRIMARY KEY, "
1822       "auto_increment INTEGER NOT NULL DEFAULT 0, "
1823       "name TEXT NOT NULL, "
1824       "key_path TEXT, "
1825       "UNIQUE (name)"
1826     ");"
1827   ));
1828   if (NS_WARN_IF(NS_FAILED(rv))) {
1829     return rv;
1830   }
1831 
1832   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1833     "INSERT INTO object_store "
1834       "SELECT id, auto_increment, name, nullif(key_path, '') "
1835       "FROM temp_upgrade;"
1836   ));
1837   if (NS_WARN_IF(NS_FAILED(rv))) {
1838     return rv;
1839   }
1840 
1841   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1842     "DROP TABLE temp_upgrade;"
1843   ));
1844   if (NS_WARN_IF(NS_FAILED(rv))) {
1845     return rv;
1846   }
1847 
1848   rv = aConnection->SetSchemaVersion(7);
1849   if (NS_WARN_IF(NS_FAILED(rv))) {
1850     return rv;
1851   }
1852 
1853   return NS_OK;
1854 }
1855 
1856 nsresult
UpgradeSchemaFrom7To8(mozIStorageConnection * aConnection)1857 UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection)
1858 {
1859   AssertIsOnIOThread();
1860   MOZ_ASSERT(aConnection);
1861 
1862   PROFILER_LABEL("IndexedDB",
1863                  "UpgradeSchemaFrom7To8",
1864                  js::ProfileEntry::Category::STORAGE);
1865 
1866   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1867     "CREATE TEMPORARY TABLE temp_upgrade ("
1868       "id, "
1869       "object_store_id, "
1870       "name, "
1871       "key_path, "
1872       "unique_index, "
1873       "object_store_autoincrement"
1874     ");"
1875   ));
1876   if (NS_WARN_IF(NS_FAILED(rv))) {
1877     return rv;
1878   }
1879 
1880   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1881     "INSERT INTO temp_upgrade "
1882       "SELECT id, object_store_id, name, key_path, "
1883       "unique_index, object_store_autoincrement "
1884       "FROM object_store_index;"
1885   ));
1886   if (NS_WARN_IF(NS_FAILED(rv))) {
1887     return rv;
1888   }
1889 
1890   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1891     "DROP TABLE object_store_index;"
1892   ));
1893   if (NS_WARN_IF(NS_FAILED(rv))) {
1894     return rv;
1895   }
1896 
1897   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1898     "CREATE TABLE object_store_index ("
1899       "id INTEGER, "
1900       "object_store_id INTEGER NOT NULL, "
1901       "name TEXT NOT NULL, "
1902       "key_path TEXT NOT NULL, "
1903       "unique_index INTEGER NOT NULL, "
1904       "multientry INTEGER NOT NULL, "
1905       "object_store_autoincrement INTERGER NOT NULL, "
1906       "PRIMARY KEY (id), "
1907       "UNIQUE (object_store_id, name), "
1908       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1909         "CASCADE"
1910     ");"
1911   ));
1912   if (NS_WARN_IF(NS_FAILED(rv))) {
1913     return rv;
1914   }
1915 
1916   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1917     "INSERT INTO object_store_index "
1918       "SELECT id, object_store_id, name, key_path, "
1919       "unique_index, 0, object_store_autoincrement "
1920       "FROM temp_upgrade;"
1921   ));
1922   if (NS_WARN_IF(NS_FAILED(rv))) {
1923     return rv;
1924   }
1925 
1926   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1927     "DROP TABLE temp_upgrade;"
1928   ));
1929   if (NS_WARN_IF(NS_FAILED(rv))) {
1930     return rv;
1931   }
1932 
1933   rv = aConnection->SetSchemaVersion(8);
1934   if (NS_WARN_IF(NS_FAILED(rv))) {
1935     return rv;
1936   }
1937 
1938   return NS_OK;
1939 }
1940 
1941 class CompressDataBlobsFunction final
1942   : public mozIStorageFunction
1943 {
1944 public:
1945   NS_DECL_ISUPPORTS
1946 
1947 private:
~CompressDataBlobsFunction()1948   ~CompressDataBlobsFunction()
1949   { }
1950 
1951   NS_IMETHOD
OnFunctionCall(mozIStorageValueArray * aArguments,nsIVariant ** aResult)1952   OnFunctionCall(mozIStorageValueArray* aArguments,
1953                  nsIVariant** aResult) override
1954   {
1955     MOZ_ASSERT(aArguments);
1956     MOZ_ASSERT(aResult);
1957 
1958     PROFILER_LABEL("IndexedDB",
1959                    "CompressDataBlobsFunction::OnFunctionCall",
1960                    js::ProfileEntry::Category::STORAGE);
1961 
1962     uint32_t argc;
1963     nsresult rv = aArguments->GetNumEntries(&argc);
1964     if (NS_WARN_IF(NS_FAILED(rv))) {
1965       return rv;
1966     }
1967 
1968     if (argc != 1) {
1969       NS_WARNING("Don't call me with the wrong number of arguments!");
1970       return NS_ERROR_UNEXPECTED;
1971     }
1972 
1973     int32_t type;
1974     rv = aArguments->GetTypeOfIndex(0, &type);
1975     if (NS_WARN_IF(NS_FAILED(rv))) {
1976       return rv;
1977     }
1978 
1979     if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
1980       NS_WARNING("Don't call me with the wrong type of arguments!");
1981       return NS_ERROR_UNEXPECTED;
1982     }
1983 
1984     const uint8_t* uncompressed;
1985     uint32_t uncompressedLength;
1986     rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed);
1987     if (NS_WARN_IF(NS_FAILED(rv))) {
1988       return rv;
1989     }
1990 
1991     size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
1992     UniqueFreePtr<uint8_t> compressed(
1993       static_cast<uint8_t*>(malloc(compressedLength)));
1994     if (NS_WARN_IF(!compressed)) {
1995       return NS_ERROR_OUT_OF_MEMORY;
1996     }
1997 
1998     snappy::RawCompress(reinterpret_cast<const char*>(uncompressed),
1999                         uncompressedLength,
2000                         reinterpret_cast<char*>(compressed.get()),
2001                         &compressedLength);
2002 
2003     std::pair<uint8_t *, int> data(compressed.release(),
2004                                    int(compressedLength));
2005 
2006     nsCOMPtr<nsIVariant> result = new mozilla::storage::AdoptedBlobVariant(data);
2007 
2008     result.forget(aResult);
2009     return NS_OK;
2010   }
2011 };
2012 
2013 nsresult
UpgradeSchemaFrom8To9_0(mozIStorageConnection * aConnection)2014 UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection)
2015 {
2016   AssertIsOnIOThread();
2017   MOZ_ASSERT(aConnection);
2018 
2019   PROFILER_LABEL("IndexedDB",
2020                  "UpgradeSchemaFrom8To9_0",
2021                  js::ProfileEntry::Category::STORAGE);
2022 
2023   // We no longer use the dataVersion column.
2024   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2025     "UPDATE database SET dataVersion = 0;"
2026   ));
2027   if (NS_WARN_IF(NS_FAILED(rv))) {
2028     return rv;
2029   }
2030 
2031   nsCOMPtr<mozIStorageFunction> compressor = new CompressDataBlobsFunction();
2032 
2033   NS_NAMED_LITERAL_CSTRING(compressorName, "compress");
2034 
2035   rv = aConnection->CreateFunction(compressorName, 1, compressor);
2036   if (NS_WARN_IF(NS_FAILED(rv))) {
2037     return rv;
2038   }
2039 
2040   // Turn off foreign key constraints before we do anything here.
2041   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2042     "UPDATE object_data SET data = compress(data);"
2043   ));
2044   if (NS_WARN_IF(NS_FAILED(rv))) {
2045     return rv;
2046   }
2047 
2048   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2049     "UPDATE ai_object_data SET data = compress(data);"
2050   ));
2051   if (NS_WARN_IF(NS_FAILED(rv))) {
2052     return rv;
2053   }
2054 
2055   rv = aConnection->RemoveFunction(compressorName);
2056   if (NS_WARN_IF(NS_FAILED(rv))) {
2057     return rv;
2058   }
2059 
2060   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0));
2061   if (NS_WARN_IF(NS_FAILED(rv))) {
2062     return rv;
2063   }
2064 
2065   return NS_OK;
2066 }
2067 
2068 nsresult
UpgradeSchemaFrom9_0To10_0(mozIStorageConnection * aConnection)2069 UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection)
2070 {
2071   AssertIsOnIOThread();
2072   MOZ_ASSERT(aConnection);
2073 
2074   PROFILER_LABEL("IndexedDB",
2075                  "UpgradeSchemaFrom9_0To10_0",
2076                  js::ProfileEntry::Category::STORAGE);
2077 
2078   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2079     "ALTER TABLE object_data ADD COLUMN file_ids TEXT;"
2080   ));
2081   if (NS_WARN_IF(NS_FAILED(rv))) {
2082     return rv;
2083   }
2084 
2085   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2086     "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;"
2087   ));
2088   if (NS_WARN_IF(NS_FAILED(rv))) {
2089     return rv;
2090   }
2091 
2092   rv = CreateFileTables(aConnection);
2093   if (NS_WARN_IF(NS_FAILED(rv))) {
2094     return rv;
2095   }
2096 
2097   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0));
2098   if (NS_WARN_IF(NS_FAILED(rv))) {
2099     return rv;
2100   }
2101 
2102   return NS_OK;
2103 }
2104 
2105 nsresult
UpgradeSchemaFrom10_0To11_0(mozIStorageConnection * aConnection)2106 UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection)
2107 {
2108   AssertIsOnIOThread();
2109   MOZ_ASSERT(aConnection);
2110 
2111   PROFILER_LABEL("IndexedDB",
2112                  "UpgradeSchemaFrom10_0To11_0",
2113                  js::ProfileEntry::Category::STORAGE);
2114 
2115   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2116     "CREATE TEMPORARY TABLE temp_upgrade ("
2117       "id, "
2118       "object_store_id, "
2119       "name, "
2120       "key_path, "
2121       "unique_index, "
2122       "multientry"
2123     ");"
2124   ));
2125   if (NS_WARN_IF(NS_FAILED(rv))) {
2126     return rv;
2127   }
2128 
2129   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2130     "INSERT INTO temp_upgrade "
2131       "SELECT id, object_store_id, name, key_path, "
2132       "unique_index, multientry "
2133       "FROM object_store_index;"
2134   ));
2135   if (NS_WARN_IF(NS_FAILED(rv))) {
2136     return rv;
2137   }
2138 
2139   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2140     "DROP TABLE object_store_index;"
2141   ));
2142   if (NS_WARN_IF(NS_FAILED(rv))) {
2143     return rv;
2144   }
2145 
2146   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2147     "CREATE TABLE object_store_index ("
2148       "id INTEGER PRIMARY KEY, "
2149       "object_store_id INTEGER NOT NULL, "
2150       "name TEXT NOT NULL, "
2151       "key_path TEXT NOT NULL, "
2152       "unique_index INTEGER NOT NULL, "
2153       "multientry INTEGER NOT NULL, "
2154       "UNIQUE (object_store_id, name), "
2155       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
2156         "CASCADE"
2157     ");"
2158   ));
2159   if (NS_WARN_IF(NS_FAILED(rv))) {
2160     return rv;
2161   }
2162 
2163   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2164     "INSERT INTO object_store_index "
2165       "SELECT id, object_store_id, name, key_path, "
2166       "unique_index, multientry "
2167       "FROM temp_upgrade;"
2168   ));
2169   if (NS_WARN_IF(NS_FAILED(rv))) {
2170     return rv;
2171   }
2172 
2173   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2174     "DROP TABLE temp_upgrade;"
2175   ));
2176   if (NS_WARN_IF(NS_FAILED(rv))) {
2177     return rv;
2178   }
2179 
2180   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2181     "DROP TRIGGER object_data_insert_trigger;"
2182   ));
2183   if (NS_WARN_IF(NS_FAILED(rv))) {
2184     return rv;
2185   }
2186 
2187   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2188     "INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
2189       "SELECT object_store_id, id, data, file_ids "
2190       "FROM ai_object_data;"
2191   ));
2192   if (NS_WARN_IF(NS_FAILED(rv))) {
2193     return rv;
2194   }
2195 
2196   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2197     "CREATE TRIGGER object_data_insert_trigger "
2198     "AFTER INSERT ON object_data "
2199     "FOR EACH ROW "
2200     "WHEN NEW.file_ids IS NOT NULL "
2201     "BEGIN "
2202       "SELECT update_refcount(NULL, NEW.file_ids); "
2203     "END;"
2204   ));
2205   if (NS_WARN_IF(NS_FAILED(rv))) {
2206     return rv;
2207   }
2208 
2209   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2210     "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) "
2211       "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id "
2212       "FROM ai_index_data "
2213       "INNER JOIN object_store_index ON "
2214         "object_store_index.id = ai_index_data.index_id "
2215       "INNER JOIN object_data ON "
2216         "object_data.object_store_id = object_store_index.object_store_id AND "
2217         "object_data.key_value = ai_index_data.ai_object_data_id;"
2218   ));
2219   if (NS_WARN_IF(NS_FAILED(rv))) {
2220     return rv;
2221   }
2222 
2223   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2224     "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) "
2225       "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id "
2226       "FROM ai_unique_index_data "
2227       "INNER JOIN object_store_index ON "
2228         "object_store_index.id = ai_unique_index_data.index_id "
2229       "INNER JOIN object_data ON "
2230         "object_data.object_store_id = object_store_index.object_store_id AND "
2231         "object_data.key_value = ai_unique_index_data.ai_object_data_id;"
2232   ));
2233   if (NS_WARN_IF(NS_FAILED(rv))) {
2234     return rv;
2235   }
2236 
2237   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2238     "UPDATE object_store "
2239       "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 "
2240       "WHERE auto_increment;"
2241   ));
2242   if (NS_WARN_IF(NS_FAILED(rv))) {
2243     return rv;
2244   }
2245 
2246   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2247     "DROP TABLE ai_unique_index_data;"
2248   ));
2249   if (NS_WARN_IF(NS_FAILED(rv))) {
2250     return rv;
2251   }
2252 
2253   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2254     "DROP TABLE ai_index_data;"
2255   ));
2256   if (NS_WARN_IF(NS_FAILED(rv))) {
2257     return rv;
2258   }
2259 
2260   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2261     "DROP TABLE ai_object_data;"
2262   ));
2263   if (NS_WARN_IF(NS_FAILED(rv))) {
2264     return rv;
2265   }
2266 
2267   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0));
2268   if (NS_WARN_IF(NS_FAILED(rv))) {
2269     return rv;
2270   }
2271 
2272   return NS_OK;
2273 }
2274 
2275 class EncodeKeysFunction final
2276   : public mozIStorageFunction
2277 {
2278 public:
2279   NS_DECL_ISUPPORTS
2280 
2281 private:
~EncodeKeysFunction()2282   ~EncodeKeysFunction()
2283   { }
2284 
2285   NS_IMETHOD
OnFunctionCall(mozIStorageValueArray * aArguments,nsIVariant ** aResult)2286   OnFunctionCall(mozIStorageValueArray* aArguments,
2287                  nsIVariant** aResult) override
2288   {
2289     MOZ_ASSERT(aArguments);
2290     MOZ_ASSERT(aResult);
2291 
2292     PROFILER_LABEL("IndexedDB",
2293                    "EncodeKeysFunction::OnFunctionCall",
2294                    js::ProfileEntry::Category::STORAGE);
2295 
2296     uint32_t argc;
2297     nsresult rv = aArguments->GetNumEntries(&argc);
2298     if (NS_WARN_IF(NS_FAILED(rv))) {
2299       return rv;
2300     }
2301 
2302     if (argc != 1) {
2303       NS_WARNING("Don't call me with the wrong number of arguments!");
2304       return NS_ERROR_UNEXPECTED;
2305     }
2306 
2307     int32_t type;
2308     rv = aArguments->GetTypeOfIndex(0, &type);
2309     if (NS_WARN_IF(NS_FAILED(rv))) {
2310       return rv;
2311     }
2312 
2313     Key key;
2314     if (type == mozIStorageStatement::VALUE_TYPE_INTEGER) {
2315       int64_t intKey;
2316       aArguments->GetInt64(0, &intKey);
2317       key.SetFromInteger(intKey);
2318     } else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) {
2319       nsString stringKey;
2320       aArguments->GetString(0, stringKey);
2321       key.SetFromString(stringKey);
2322     } else {
2323       NS_WARNING("Don't call me with the wrong type of arguments!");
2324       return NS_ERROR_UNEXPECTED;
2325     }
2326 
2327     const nsCString& buffer = key.GetBuffer();
2328 
2329     std::pair<const void *, int> data(static_cast<const void*>(buffer.get()),
2330                                       int(buffer.Length()));
2331 
2332     nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
2333 
2334     result.forget(aResult);
2335     return NS_OK;
2336   }
2337 };
2338 
2339 nsresult
UpgradeSchemaFrom11_0To12_0(mozIStorageConnection * aConnection)2340 UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection)
2341 {
2342   AssertIsOnIOThread();
2343   MOZ_ASSERT(aConnection);
2344 
2345   PROFILER_LABEL("IndexedDB",
2346                  "UpgradeSchemaFrom11_0To12_0",
2347                  js::ProfileEntry::Category::STORAGE);
2348 
2349   NS_NAMED_LITERAL_CSTRING(encoderName, "encode");
2350 
2351   nsCOMPtr<mozIStorageFunction> encoder = new EncodeKeysFunction();
2352 
2353   nsresult rv = aConnection->CreateFunction(encoderName, 1, encoder);
2354   if (NS_WARN_IF(NS_FAILED(rv))) {
2355     return rv;
2356   }
2357 
2358   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2359     "CREATE TEMPORARY TABLE temp_upgrade ("
2360       "id INTEGER PRIMARY KEY, "
2361       "object_store_id, "
2362       "key_value, "
2363       "data, "
2364       "file_ids "
2365     ");"
2366   ));
2367   if (NS_WARN_IF(NS_FAILED(rv))) {
2368     return rv;
2369   }
2370 
2371   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2372     "INSERT INTO temp_upgrade "
2373       "SELECT id, object_store_id, encode(key_value), data, file_ids "
2374       "FROM object_data;"
2375   ));
2376   if (NS_WARN_IF(NS_FAILED(rv))) {
2377     return rv;
2378   }
2379 
2380   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2381     "DROP TABLE object_data;"
2382   ));
2383   if (NS_WARN_IF(NS_FAILED(rv))) {
2384     return rv;
2385   }
2386 
2387   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2388     "CREATE TABLE object_data ("
2389       "id INTEGER PRIMARY KEY, "
2390       "object_store_id INTEGER NOT NULL, "
2391       "key_value BLOB DEFAULT NULL, "
2392       "file_ids TEXT, "
2393       "data BLOB NOT NULL, "
2394       "UNIQUE (object_store_id, key_value), "
2395       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
2396         "CASCADE"
2397     ");"
2398   ));
2399   if (NS_WARN_IF(NS_FAILED(rv))) {
2400     return rv;
2401   }
2402 
2403   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2404     "INSERT INTO object_data "
2405       "SELECT id, object_store_id, key_value, file_ids, data "
2406       "FROM temp_upgrade;"
2407   ));
2408   if (NS_WARN_IF(NS_FAILED(rv))) {
2409     return rv;
2410   }
2411 
2412   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2413     "DROP TABLE temp_upgrade;"
2414   ));
2415   if (NS_WARN_IF(NS_FAILED(rv))) {
2416     return rv;
2417   }
2418 
2419   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2420     "CREATE TRIGGER object_data_insert_trigger "
2421     "AFTER INSERT ON object_data "
2422     "FOR EACH ROW "
2423     "WHEN NEW.file_ids IS NOT NULL "
2424     "BEGIN "
2425       "SELECT update_refcount(NULL, NEW.file_ids); "
2426     "END;"
2427   ));
2428   if (NS_WARN_IF(NS_FAILED(rv))) {
2429     return rv;
2430   }
2431 
2432   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2433     "CREATE TRIGGER object_data_update_trigger "
2434     "AFTER UPDATE OF file_ids ON object_data "
2435     "FOR EACH ROW "
2436     "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
2437     "BEGIN "
2438       "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
2439     "END;"
2440   ));
2441   if (NS_WARN_IF(NS_FAILED(rv))) {
2442     return rv;
2443   }
2444 
2445   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2446     "CREATE TRIGGER object_data_delete_trigger "
2447     "AFTER DELETE ON object_data "
2448     "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
2449     "BEGIN "
2450       "SELECT update_refcount(OLD.file_ids, NULL); "
2451     "END;"
2452   ));
2453   if (NS_WARN_IF(NS_FAILED(rv))) {
2454     return rv;
2455   }
2456 
2457   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2458     "CREATE TEMPORARY TABLE temp_upgrade ("
2459       "index_id, "
2460       "value, "
2461       "object_data_key, "
2462       "object_data_id "
2463     ");"
2464   ));
2465   if (NS_WARN_IF(NS_FAILED(rv))) {
2466     return rv;
2467   }
2468 
2469   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2470     "INSERT INTO temp_upgrade "
2471       "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
2472       "FROM index_data;"
2473   ));
2474   if (NS_WARN_IF(NS_FAILED(rv))) {
2475     return rv;
2476   }
2477 
2478   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2479     "DROP TABLE index_data;"
2480   ));
2481   if (NS_WARN_IF(NS_FAILED(rv))) {
2482     return rv;
2483   }
2484 
2485   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2486     "CREATE TABLE index_data ("
2487       "index_id INTEGER NOT NULL, "
2488       "value BLOB NOT NULL, "
2489       "object_data_key BLOB NOT NULL, "
2490       "object_data_id INTEGER NOT NULL, "
2491       "PRIMARY KEY (index_id, value, object_data_key), "
2492       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
2493         "CASCADE, "
2494       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
2495         "CASCADE"
2496     ");"
2497   ));
2498   if (NS_WARN_IF(NS_FAILED(rv))) {
2499     return rv;
2500   }
2501 
2502   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2503     "INSERT INTO index_data "
2504       "SELECT index_id, value, object_data_key, object_data_id "
2505       "FROM temp_upgrade;"
2506   ));
2507   if (NS_WARN_IF(NS_FAILED(rv))) {
2508     return rv;
2509   }
2510 
2511   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2512     "DROP TABLE temp_upgrade;"
2513   ));
2514   if (NS_WARN_IF(NS_FAILED(rv))) {
2515     return rv;
2516   }
2517 
2518   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2519     "CREATE INDEX index_data_object_data_id_index "
2520     "ON index_data (object_data_id);"
2521   ));
2522   if (NS_WARN_IF(NS_FAILED(rv))) {
2523     return rv;
2524   }
2525 
2526   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2527     "CREATE TEMPORARY TABLE temp_upgrade ("
2528       "index_id, "
2529       "value, "
2530       "object_data_key, "
2531       "object_data_id "
2532     ");"
2533   ));
2534   if (NS_WARN_IF(NS_FAILED(rv))) {
2535     return rv;
2536   }
2537 
2538   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2539     "INSERT INTO temp_upgrade "
2540       "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
2541       "FROM unique_index_data;"
2542   ));
2543   if (NS_WARN_IF(NS_FAILED(rv))) {
2544     return rv;
2545   }
2546 
2547   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2548     "DROP TABLE unique_index_data;"
2549   ));
2550   if (NS_WARN_IF(NS_FAILED(rv))) {
2551     return rv;
2552   }
2553 
2554   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2555     "CREATE TABLE unique_index_data ("
2556       "index_id INTEGER NOT NULL, "
2557       "value BLOB NOT NULL, "
2558       "object_data_key BLOB NOT NULL, "
2559       "object_data_id INTEGER NOT NULL, "
2560       "PRIMARY KEY (index_id, value, object_data_key), "
2561       "UNIQUE (index_id, value), "
2562       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
2563         "CASCADE "
2564       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
2565         "CASCADE"
2566     ");"
2567   ));
2568   if (NS_WARN_IF(NS_FAILED(rv))) {
2569     return rv;
2570   }
2571 
2572   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2573     "INSERT INTO unique_index_data "
2574       "SELECT index_id, value, object_data_key, object_data_id "
2575       "FROM temp_upgrade;"
2576   ));
2577   if (NS_WARN_IF(NS_FAILED(rv))) {
2578     return rv;
2579   }
2580 
2581   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2582     "DROP TABLE temp_upgrade;"
2583   ));
2584   if (NS_WARN_IF(NS_FAILED(rv))) {
2585     return rv;
2586   }
2587 
2588   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2589     "CREATE INDEX unique_index_data_object_data_id_index "
2590     "ON unique_index_data (object_data_id);"
2591   ));
2592   if (NS_WARN_IF(NS_FAILED(rv))) {
2593     return rv;
2594   }
2595 
2596   rv = aConnection->RemoveFunction(encoderName);
2597   if (NS_WARN_IF(NS_FAILED(rv))) {
2598     return rv;
2599   }
2600 
2601   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(12, 0));
2602   if (NS_WARN_IF(NS_FAILED(rv))) {
2603     return rv;
2604   }
2605 
2606   return NS_OK;
2607 }
2608 
2609 nsresult
UpgradeSchemaFrom12_0To13_0(mozIStorageConnection * aConnection,bool * aVacuumNeeded)2610 UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection,
2611                             bool* aVacuumNeeded)
2612 {
2613   AssertIsOnIOThread();
2614   MOZ_ASSERT(aConnection);
2615 
2616   PROFILER_LABEL("IndexedDB",
2617                  "UpgradeSchemaFrom12_0To13_0",
2618                  js::ProfileEntry::Category::STORAGE);
2619 
2620   nsresult rv;
2621 
2622 #ifdef IDB_MOBILE
2623   int32_t defaultPageSize;
2624   rv = aConnection->GetDefaultPageSize(&defaultPageSize);
2625   if (NS_WARN_IF(NS_FAILED(rv))) {
2626     return rv;
2627   }
2628 
2629   // Enable auto_vacuum mode and update the page size to the platform default.
2630   nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = ");
2631   upgradeQuery.AppendInt(defaultPageSize);
2632 
2633   rv = aConnection->ExecuteSimpleSQL(upgradeQuery);
2634   if (NS_WARN_IF(NS_FAILED(rv))) {
2635     return rv;
2636   }
2637 
2638   *aVacuumNeeded = true;
2639 #endif
2640 
2641   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(13, 0));
2642   if (NS_WARN_IF(NS_FAILED(rv))) {
2643     return rv;
2644   }
2645 
2646   return NS_OK;
2647 }
2648 
2649 nsresult
UpgradeSchemaFrom13_0To14_0(mozIStorageConnection * aConnection)2650 UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection)
2651 {
2652   AssertIsOnIOThread();
2653   MOZ_ASSERT(aConnection);
2654 
2655   // The only change between 13 and 14 was a different structured
2656   // clone format, but it's backwards-compatible.
2657   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(14, 0));
2658   if (NS_WARN_IF(NS_FAILED(rv))) {
2659     return rv;
2660   }
2661 
2662   return NS_OK;
2663 }
2664 
2665 nsresult
UpgradeSchemaFrom14_0To15_0(mozIStorageConnection * aConnection)2666 UpgradeSchemaFrom14_0To15_0(mozIStorageConnection* aConnection)
2667 {
2668   // The only change between 14 and 15 was a different structured
2669   // clone format, but it's backwards-compatible.
2670   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(15, 0));
2671   if (NS_WARN_IF(NS_FAILED(rv))) {
2672     return rv;
2673   }
2674 
2675   return NS_OK;
2676 }
2677 
2678 nsresult
UpgradeSchemaFrom15_0To16_0(mozIStorageConnection * aConnection)2679 UpgradeSchemaFrom15_0To16_0(mozIStorageConnection* aConnection)
2680 {
2681   // The only change between 15 and 16 was a different structured
2682   // clone format, but it's backwards-compatible.
2683   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(16, 0));
2684   if (NS_WARN_IF(NS_FAILED(rv))) {
2685     return rv;
2686   }
2687 
2688   return NS_OK;
2689 }
2690 
2691 nsresult
UpgradeSchemaFrom16_0To17_0(mozIStorageConnection * aConnection)2692 UpgradeSchemaFrom16_0To17_0(mozIStorageConnection* aConnection)
2693 {
2694   // The only change between 16 and 17 was a different structured
2695   // clone format, but it's backwards-compatible.
2696   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(17, 0));
2697   if (NS_WARN_IF(NS_FAILED(rv))) {
2698     return rv;
2699   }
2700 
2701   return NS_OK;
2702 }
2703 
2704 class UpgradeSchemaFrom17_0To18_0Helper final
2705 {
2706   class InsertIndexDataValuesFunction;
2707   class UpgradeKeyFunction;
2708 
2709 public:
2710   static nsresult
2711   DoUpgrade(mozIStorageConnection* aConnection, const nsACString& aOrigin);
2712 
2713 private:
2714   static nsresult
2715   DoUpgradeInternal(mozIStorageConnection* aConnection,
2716                     const nsACString& aOrigin);
2717 
UpgradeSchemaFrom17_0To18_0Helper()2718   UpgradeSchemaFrom17_0To18_0Helper()
2719   {
2720     MOZ_ASSERT_UNREACHABLE("Don't create instances of this class!");
2721   }
2722 
~UpgradeSchemaFrom17_0To18_0Helper()2723   ~UpgradeSchemaFrom17_0To18_0Helper()
2724   {
2725     MOZ_ASSERT_UNREACHABLE("Don't create instances of this class!");
2726   }
2727 };
2728 
2729 class UpgradeSchemaFrom17_0To18_0Helper::InsertIndexDataValuesFunction final
2730   : public mozIStorageFunction
2731 {
2732 public:
InsertIndexDataValuesFunction()2733   InsertIndexDataValuesFunction()
2734   { }
2735 
2736   NS_DECL_ISUPPORTS
2737 
2738 private:
~InsertIndexDataValuesFunction()2739   ~InsertIndexDataValuesFunction()
2740   { }
2741 
2742   NS_DECL_MOZISTORAGEFUNCTION
2743 };
2744 
2745 NS_IMPL_ISUPPORTS(UpgradeSchemaFrom17_0To18_0Helper::
2746                     InsertIndexDataValuesFunction,
2747                   mozIStorageFunction);
2748 
2749 NS_IMETHODIMP
2750 UpgradeSchemaFrom17_0To18_0Helper::
OnFunctionCall(mozIStorageValueArray * aValues,nsIVariant ** _retval)2751 InsertIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aValues,
2752                                               nsIVariant** _retval)
2753 {
2754   MOZ_ASSERT(!NS_IsMainThread());
2755   MOZ_ASSERT(!IsOnBackgroundThread());
2756   MOZ_ASSERT(aValues);
2757   MOZ_ASSERT(_retval);
2758 
2759 #ifdef DEBUG
2760   {
2761     uint32_t argCount;
2762     MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
2763     MOZ_ASSERT(argCount == 4);
2764 
2765     int32_t valueType;
2766     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType));
2767     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
2768                valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
2769 
2770     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(1, &valueType));
2771     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
2772 
2773     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(2, &valueType));
2774     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
2775 
2776     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(3, &valueType));
2777     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
2778   }
2779 #endif
2780 
2781   // Read out the previous value. It may be NULL, in which case we'll just end
2782   // up with an empty array.
2783   AutoTArray<IndexDataValue, 32> indexValues;
2784   nsresult rv = ReadCompressedIndexDataValues(aValues, 0, indexValues);
2785   if (NS_WARN_IF(NS_FAILED(rv))) {
2786     return rv;
2787   }
2788 
2789   int64_t indexId;
2790   rv = aValues->GetInt64(1, &indexId);
2791   if (NS_WARN_IF(NS_FAILED(rv))) {
2792     return rv;
2793   }
2794 
2795   int32_t unique;
2796   rv = aValues->GetInt32(2, &unique);
2797   if (NS_WARN_IF(NS_FAILED(rv))) {
2798     return rv;
2799   }
2800 
2801   Key value;
2802   rv = value.SetFromValueArray(aValues, 3);
2803   if (NS_WARN_IF(NS_FAILED(rv))) {
2804     return rv;
2805   }
2806 
2807   // Update the array with the new addition.
2808   if (NS_WARN_IF(!indexValues.SetCapacity(indexValues.Length() + 1,
2809                                           fallible))) {
2810     IDB_REPORT_INTERNAL_ERR();
2811     return NS_ERROR_OUT_OF_MEMORY;
2812   }
2813 
2814   MOZ_ALWAYS_TRUE(
2815     indexValues.InsertElementSorted(IndexDataValue(indexId, !!unique, value),
2816                                     fallible));
2817 
2818   // Compress the array.
2819   UniqueFreePtr<uint8_t> indexValuesBlob;
2820   uint32_t indexValuesBlobLength;
2821   rv = MakeCompressedIndexDataValues(indexValues,
2822                                      indexValuesBlob,
2823                                      &indexValuesBlobLength);
2824   if (NS_WARN_IF(NS_FAILED(rv))) {
2825     return rv;
2826   }
2827 
2828   // The compressed blob is the result of this function.
2829   std::pair<uint8_t *, int> indexValuesBlobPair(indexValuesBlob.release(),
2830                                                 indexValuesBlobLength);
2831 
2832   nsCOMPtr<nsIVariant> result =
2833     new storage::AdoptedBlobVariant(indexValuesBlobPair);
2834 
2835   result.forget(_retval);
2836   return NS_OK;
2837 }
2838 
2839 class UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction final
2840   : public mozIStorageFunction
2841 {
2842 public:
UpgradeKeyFunction()2843   UpgradeKeyFunction()
2844   { }
2845 
2846   static nsresult
CopyAndUpgradeKeyBuffer(const uint8_t * aSource,const uint8_t * aSourceEnd,uint8_t * aDestination)2847   CopyAndUpgradeKeyBuffer(const uint8_t* aSource,
2848                           const uint8_t* aSourceEnd,
2849                           uint8_t* aDestination)
2850   {
2851     return CopyAndUpgradeKeyBufferInternal(aSource,
2852                                            aSourceEnd,
2853                                            aDestination,
2854                                            0 /* aTagOffset */,
2855                                            0 /* aRecursionDepth */);
2856   }
2857 
2858   NS_DECL_ISUPPORTS
2859 
2860 private:
~UpgradeKeyFunction()2861   ~UpgradeKeyFunction()
2862   { }
2863 
2864   static nsresult
2865   CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource,
2866                                   const uint8_t* aSourceEnd,
2867                                   uint8_t*& aDestination,
2868                                   uint8_t aTagOffset,
2869                                   uint8_t aRecursionDepth);
2870 
2871   static uint32_t
AdjustedSize(uint32_t aMaxSize,const uint8_t * aSource,const uint8_t * aSourceEnd)2872   AdjustedSize(uint32_t aMaxSize,
2873                const uint8_t* aSource,
2874                const uint8_t* aSourceEnd)
2875   {
2876     MOZ_ASSERT(aMaxSize);
2877     MOZ_ASSERT(aSource);
2878     MOZ_ASSERT(aSourceEnd);
2879     MOZ_ASSERT(aSource <= aSourceEnd);
2880 
2881     return std::min(aMaxSize, uint32_t(aSourceEnd - aSource));
2882   }
2883 
2884   NS_DECL_MOZISTORAGEFUNCTION
2885 };
2886 
2887 // static
2888 nsresult
2889 UpgradeSchemaFrom17_0To18_0Helper::
CopyAndUpgradeKeyBufferInternal(const uint8_t * & aSource,const uint8_t * aSourceEnd,uint8_t * & aDestination,uint8_t aTagOffset,uint8_t aRecursionDepth)2890 UpgradeKeyFunction::CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource,
2891                                                     const uint8_t* aSourceEnd,
2892                                                     uint8_t*& aDestination,
2893                                                     uint8_t aTagOffset,
2894                                                     uint8_t aRecursionDepth)
2895 {
2896   MOZ_ASSERT(!NS_IsMainThread());
2897   MOZ_ASSERT(!IsOnBackgroundThread());
2898   MOZ_ASSERT(aSource);
2899   MOZ_ASSERT(*aSource);
2900   MOZ_ASSERT(aSourceEnd);
2901   MOZ_ASSERT(aSource < aSourceEnd);
2902   MOZ_ASSERT(aDestination);
2903   MOZ_ASSERT(aTagOffset <=  Key::kMaxArrayCollapse);
2904 
2905   static constexpr uint8_t kOldNumberTag = 0x1;
2906   static constexpr uint8_t kOldDateTag = 0x2;
2907   static constexpr uint8_t kOldStringTag = 0x3;
2908   static constexpr uint8_t kOldArrayTag = 0x4;
2909   static constexpr uint8_t kOldMaxType = kOldArrayTag;
2910 
2911   if (NS_WARN_IF(aRecursionDepth > Key::kMaxRecursionDepth)) {
2912     IDB_REPORT_INTERNAL_ERR();
2913     return NS_ERROR_FILE_CORRUPTED;
2914   }
2915 
2916   const uint8_t sourceTag = *aSource - (aTagOffset * kOldMaxType);
2917   MOZ_ASSERT(sourceTag);
2918 
2919   if (NS_WARN_IF(sourceTag > kOldMaxType * Key::kMaxArrayCollapse)) {
2920     IDB_REPORT_INTERNAL_ERR();
2921     return NS_ERROR_FILE_CORRUPTED;
2922   }
2923 
2924   if (sourceTag == kOldNumberTag || sourceTag == kOldDateTag) {
2925     // Write the new tag.
2926     *aDestination++ =
2927       (sourceTag == kOldNumberTag ? Key::eFloat : Key::eDate) +
2928       (aTagOffset * Key::eMaxType);
2929     aSource++;
2930 
2931     // Numbers and Dates are encoded as 64-bit integers, but trailing 0
2932     // bytes have been removed.
2933     const uint32_t byteCount =
2934       AdjustedSize(sizeof(uint64_t), aSource, aSourceEnd);
2935 
2936     for (uint32_t count = 0; count < byteCount; count++) {
2937       *aDestination++ = *aSource++;
2938     }
2939 
2940     return NS_OK;
2941   }
2942 
2943   if (sourceTag == kOldStringTag) {
2944     // Write the new tag.
2945     *aDestination++ = Key::eString + (aTagOffset * Key::eMaxType);
2946     aSource++;
2947 
2948     while (aSource < aSourceEnd) {
2949       const uint8_t byte = *aSource++;
2950       *aDestination++ = byte;
2951 
2952       if (!byte) {
2953         // Just copied the terminator.
2954         break;
2955       }
2956 
2957       // Maybe copy one or two extra bytes if the byte is tagged and we have
2958       // enough source space.
2959       if (byte & 0x80) {
2960         const uint32_t byteCount =
2961           AdjustedSize((byte & 0x40) ? 2 : 1, aSource, aSourceEnd);
2962 
2963         for (uint32_t count = 0; count < byteCount; count++) {
2964           *aDestination++ = *aSource++;
2965         }
2966       }
2967     }
2968 
2969     return NS_OK;
2970   }
2971 
2972   if (NS_WARN_IF(sourceTag < kOldArrayTag)) {
2973     IDB_REPORT_INTERNAL_ERR();
2974     return NS_ERROR_FILE_CORRUPTED;
2975   }
2976 
2977   aTagOffset++;
2978 
2979   if (aTagOffset == Key::kMaxArrayCollapse) {
2980     MOZ_ASSERT(sourceTag == kOldArrayTag);
2981 
2982     *aDestination++ = (aTagOffset * Key::eMaxType);
2983     aSource++;
2984 
2985     aTagOffset = 0;
2986   }
2987 
2988   while (aSource < aSourceEnd &&
2989          (*aSource - (aTagOffset * kOldMaxType)) != Key::eTerminator) {
2990     nsresult rv = CopyAndUpgradeKeyBufferInternal(aSource,
2991                                                   aSourceEnd,
2992                                                   aDestination,
2993                                                   aTagOffset,
2994                                                   aRecursionDepth + 1);
2995     if (NS_WARN_IF(NS_FAILED(rv))) {
2996       return rv;
2997     }
2998 
2999     aTagOffset = 0;
3000   }
3001 
3002   if (aSource < aSourceEnd) {
3003     MOZ_ASSERT((*aSource - (aTagOffset * kOldMaxType)) == Key::eTerminator);
3004     *aDestination++ = Key::eTerminator + (aTagOffset * Key::eMaxType);
3005     aSource++;
3006   }
3007 
3008   return NS_OK;
3009 }
3010 
3011 NS_IMPL_ISUPPORTS(UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction,
3012                   mozIStorageFunction);
3013 
3014 NS_IMETHODIMP
3015 UpgradeSchemaFrom17_0To18_0Helper::
OnFunctionCall(mozIStorageValueArray * aValues,nsIVariant ** _retval)3016 UpgradeKeyFunction::OnFunctionCall(mozIStorageValueArray* aValues,
3017                                    nsIVariant** _retval)
3018 {
3019   MOZ_ASSERT(!NS_IsMainThread());
3020   MOZ_ASSERT(!IsOnBackgroundThread());
3021   MOZ_ASSERT(aValues);
3022   MOZ_ASSERT(_retval);
3023 
3024 #ifdef DEBUG
3025   {
3026     uint32_t argCount;
3027     MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
3028     MOZ_ASSERT(argCount == 1);
3029 
3030     int32_t valueType;
3031     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType));
3032     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
3033   }
3034 #endif
3035 
3036   // Dig the old key out of the values.
3037   const uint8_t* blobData;
3038   uint32_t blobDataLength;
3039   nsresult rv = aValues->GetSharedBlob(0, &blobDataLength, &blobData);
3040   if (NS_WARN_IF(NS_FAILED(rv))) {
3041     return rv;
3042   }
3043 
3044   // Upgrading the key doesn't change the amount of space needed to hold it.
3045   UniqueFreePtr<uint8_t> upgradedBlobData(
3046     static_cast<uint8_t*>(malloc(blobDataLength)));
3047   if (NS_WARN_IF(!upgradedBlobData)) {
3048     IDB_REPORT_INTERNAL_ERR();
3049     return NS_ERROR_OUT_OF_MEMORY;
3050   }
3051 
3052   rv = CopyAndUpgradeKeyBuffer(blobData,
3053                                blobData + blobDataLength,
3054                                upgradedBlobData.get());
3055   if (NS_WARN_IF(NS_FAILED(rv))) {
3056     return rv;
3057   }
3058 
3059   // The upgraded key is the result of this function.
3060   std::pair<uint8_t*, int> data(upgradedBlobData.release(),
3061                                 int(blobDataLength));
3062 
3063   nsCOMPtr<nsIVariant> result = new mozilla::storage::AdoptedBlobVariant(data);
3064 
3065   result.forget(_retval);
3066   return NS_OK;
3067 }
3068 
3069 // static
3070 nsresult
DoUpgrade(mozIStorageConnection * aConnection,const nsACString & aOrigin)3071 UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(mozIStorageConnection* aConnection,
3072                                              const nsACString& aOrigin)
3073 {
3074   MOZ_ASSERT(aConnection);
3075   MOZ_ASSERT(!aOrigin.IsEmpty());
3076 
3077   // Register the |upgrade_key| function.
3078   RefPtr<UpgradeKeyFunction> updateFunction = new UpgradeKeyFunction();
3079 
3080   NS_NAMED_LITERAL_CSTRING(upgradeKeyFunctionName, "upgrade_key");
3081 
3082   nsresult rv =
3083     aConnection->CreateFunction(upgradeKeyFunctionName, 1, updateFunction);
3084   if (NS_WARN_IF(NS_FAILED(rv))) {
3085     return rv;
3086   }
3087 
3088   // Register the |insert_idv| function.
3089   RefPtr<InsertIndexDataValuesFunction> insertIDVFunction =
3090     new InsertIndexDataValuesFunction();
3091 
3092   NS_NAMED_LITERAL_CSTRING(insertIDVFunctionName, "insert_idv");
3093 
3094   rv = aConnection->CreateFunction(insertIDVFunctionName, 4, insertIDVFunction);
3095   if (NS_WARN_IF(NS_FAILED(rv))) {
3096     MOZ_ALWAYS_SUCCEEDS(aConnection->RemoveFunction(upgradeKeyFunctionName));
3097     return rv;
3098   }
3099 
3100   rv = DoUpgradeInternal(aConnection, aOrigin);
3101 
3102   MOZ_ALWAYS_SUCCEEDS(aConnection->RemoveFunction(upgradeKeyFunctionName));
3103   MOZ_ALWAYS_SUCCEEDS(aConnection->RemoveFunction(insertIDVFunctionName));
3104 
3105   if (NS_WARN_IF(NS_FAILED(rv))) {
3106     return rv;
3107   }
3108 
3109   return NS_OK;
3110 }
3111 
3112 // static
3113 nsresult
DoUpgradeInternal(mozIStorageConnection * aConnection,const nsACString & aOrigin)3114 UpgradeSchemaFrom17_0To18_0Helper::DoUpgradeInternal(
3115                                              mozIStorageConnection* aConnection,
3116                                              const nsACString& aOrigin)
3117 {
3118   MOZ_ASSERT(aConnection);
3119   MOZ_ASSERT(!aOrigin.IsEmpty());
3120 
3121   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3122     // Drop these triggers to avoid unnecessary work during the upgrade process.
3123     "DROP TRIGGER object_data_insert_trigger;"
3124     "DROP TRIGGER object_data_update_trigger;"
3125     "DROP TRIGGER object_data_delete_trigger;"
3126   ));
3127   if (NS_WARN_IF(NS_FAILED(rv))) {
3128     return rv;
3129   }
3130 
3131   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3132     // Drop these indexes before we do anything else to free disk space.
3133     "DROP INDEX index_data_object_data_id_index;"
3134     "DROP INDEX unique_index_data_object_data_id_index;"
3135   ));
3136   if (NS_WARN_IF(NS_FAILED(rv))) {
3137     return rv;
3138   }
3139 
3140   // Create the new tables and triggers first.
3141 
3142   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3143     // This will eventually become the |database| table.
3144     "CREATE TABLE database_upgrade "
3145       "( name TEXT PRIMARY KEY"
3146       ", origin TEXT NOT NULL"
3147       ", version INTEGER NOT NULL DEFAULT 0"
3148       ", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
3149       ", last_analyze_time INTEGER NOT NULL DEFAULT 0"
3150       ", last_vacuum_size INTEGER NOT NULL DEFAULT 0"
3151       ") WITHOUT ROWID;"
3152   ));
3153   if (NS_WARN_IF(NS_FAILED(rv))) {
3154     return rv;
3155   }
3156 
3157   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3158      // This will eventually become the |object_store| table.
3159     "CREATE TABLE object_store_upgrade"
3160       "( id INTEGER PRIMARY KEY"
3161       ", auto_increment INTEGER NOT NULL DEFAULT 0"
3162       ", name TEXT NOT NULL"
3163       ", key_path TEXT"
3164       ");"
3165   ));
3166   if (NS_WARN_IF(NS_FAILED(rv))) {
3167     return rv;
3168   }
3169 
3170   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3171     // This will eventually become the |object_store_index| table.
3172     "CREATE TABLE object_store_index_upgrade"
3173       "( id INTEGER PRIMARY KEY"
3174       ", object_store_id INTEGER NOT NULL"
3175       ", name TEXT NOT NULL"
3176       ", key_path TEXT NOT NULL"
3177       ", unique_index INTEGER NOT NULL"
3178       ", multientry INTEGER NOT NULL"
3179       ", FOREIGN KEY (object_store_id) "
3180           "REFERENCES object_store(id) "
3181       ");"
3182   ));
3183   if (NS_WARN_IF(NS_FAILED(rv))) {
3184     return rv;
3185   }
3186 
3187   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3188     // This will eventually become the |object_data| table.
3189     "CREATE TABLE object_data_upgrade"
3190       "( object_store_id INTEGER NOT NULL"
3191       ", key BLOB NOT NULL"
3192       ", index_data_values BLOB DEFAULT NULL"
3193       ", file_ids TEXT"
3194       ", data BLOB NOT NULL"
3195       ", PRIMARY KEY (object_store_id, key)"
3196       ", FOREIGN KEY (object_store_id) "
3197           "REFERENCES object_store(id) "
3198       ") WITHOUT ROWID;"
3199   ));
3200   if (NS_WARN_IF(NS_FAILED(rv))) {
3201     return rv;
3202   }
3203 
3204   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3205     // This will eventually become the |index_data| table.
3206     "CREATE TABLE index_data_upgrade"
3207       "( index_id INTEGER NOT NULL"
3208       ", value BLOB NOT NULL"
3209       ", object_data_key BLOB NOT NULL"
3210       ", object_store_id INTEGER NOT NULL"
3211       ", PRIMARY KEY (index_id, value, object_data_key)"
3212       ", FOREIGN KEY (index_id) "
3213           "REFERENCES object_store_index(id) "
3214       ", FOREIGN KEY (object_store_id, object_data_key) "
3215           "REFERENCES object_data(object_store_id, key) "
3216       ") WITHOUT ROWID;"
3217   ));
3218   if (NS_WARN_IF(NS_FAILED(rv))) {
3219     return rv;
3220   }
3221 
3222   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3223     // This will eventually become the |unique_index_data| table.
3224     "CREATE TABLE unique_index_data_upgrade"
3225       "( index_id INTEGER NOT NULL"
3226       ", value BLOB NOT NULL"
3227       ", object_store_id INTEGER NOT NULL"
3228       ", object_data_key BLOB NOT NULL"
3229       ", PRIMARY KEY (index_id, value)"
3230       ", FOREIGN KEY (index_id) "
3231           "REFERENCES object_store_index(id) "
3232       ", FOREIGN KEY (object_store_id, object_data_key) "
3233           "REFERENCES object_data(object_store_id, key) "
3234       ") WITHOUT ROWID;"
3235   ));
3236   if (NS_WARN_IF(NS_FAILED(rv))) {
3237     return rv;
3238   }
3239 
3240   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3241     // Temporarily store |index_data_values| that we build during the upgrade of
3242     // the index tables. We will later move this to the |object_data| table.
3243     "CREATE TEMPORARY TABLE temp_index_data_values "
3244       "( object_store_id INTEGER NOT NULL"
3245       ", key BLOB NOT NULL"
3246       ", index_data_values BLOB DEFAULT NULL"
3247       ", PRIMARY KEY (object_store_id, key)"
3248       ") WITHOUT ROWID;"
3249   ));
3250   if (NS_WARN_IF(NS_FAILED(rv))) {
3251     return rv;
3252   }
3253 
3254   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3255     // These two triggers help build the |index_data_values| blobs. The nested
3256     // SELECT statements help us achieve an "INSERT OR UPDATE"-like behavior.
3257     "CREATE TEMPORARY TRIGGER unique_index_data_upgrade_insert_trigger "
3258       "AFTER INSERT ON unique_index_data_upgrade "
3259       "BEGIN "
3260         "INSERT OR REPLACE INTO temp_index_data_values "
3261           "VALUES "
3262           "( NEW.object_store_id"
3263           ", NEW.object_data_key"
3264           ", insert_idv("
3265               "( SELECT index_data_values "
3266                   "FROM temp_index_data_values "
3267                   "WHERE object_store_id = NEW.object_store_id "
3268                   "AND key = NEW.object_data_key "
3269               "), NEW.index_id"
3270                ", 1" /* unique */
3271                ", NEW.value"
3272             ")"
3273           ");"
3274       "END;"
3275   ));
3276   if (NS_WARN_IF(NS_FAILED(rv))) {
3277     return rv;
3278   }
3279 
3280   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3281     "CREATE TEMPORARY TRIGGER index_data_upgrade_insert_trigger "
3282       "AFTER INSERT ON index_data_upgrade "
3283       "BEGIN "
3284         "INSERT OR REPLACE INTO temp_index_data_values "
3285           "VALUES "
3286           "( NEW.object_store_id"
3287           ", NEW.object_data_key"
3288           ", insert_idv("
3289               "("
3290                 "SELECT index_data_values "
3291                   "FROM temp_index_data_values "
3292                   "WHERE object_store_id = NEW.object_store_id "
3293                   "AND key = NEW.object_data_key "
3294               "), NEW.index_id"
3295                ", 0" /* not unique */
3296                ", NEW.value"
3297             ")"
3298           ");"
3299       "END;"
3300   ));
3301   if (NS_WARN_IF(NS_FAILED(rv))) {
3302     return rv;
3303   }
3304 
3305   // Update the |unique_index_data| table to change the column order, remove the
3306   // ON DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization.
3307   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3308     // Insert all the data.
3309     "INSERT INTO unique_index_data_upgrade "
3310       "SELECT "
3311         "unique_index_data.index_id, "
3312         "upgrade_key(unique_index_data.value), "
3313         "object_data.object_store_id, "
3314         "upgrade_key(unique_index_data.object_data_key) "
3315         "FROM unique_index_data "
3316         "JOIN object_data "
3317         "ON unique_index_data.object_data_id = object_data.id;"
3318   ));
3319   if (NS_WARN_IF(NS_FAILED(rv))) {
3320     return rv;
3321   }
3322 
3323   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3324     // The trigger is no longer needed.
3325     "DROP TRIGGER unique_index_data_upgrade_insert_trigger;"
3326   ));
3327   if (NS_WARN_IF(NS_FAILED(rv))) {
3328     return rv;
3329   }
3330 
3331   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3332     // The old table is no longer needed.
3333     "DROP TABLE unique_index_data;"
3334   ));
3335   if (NS_WARN_IF(NS_FAILED(rv))) {
3336     return rv;
3337   }
3338 
3339   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3340     // Rename the table.
3341     "ALTER TABLE unique_index_data_upgrade "
3342       "RENAME TO unique_index_data;"
3343   ));
3344   if (NS_WARN_IF(NS_FAILED(rv))) {
3345     return rv;
3346   }
3347 
3348   // Update the |index_data| table to change the column order, remove the ON
3349   // DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization.
3350   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3351     // Insert all the data.
3352     "INSERT INTO index_data_upgrade "
3353       "SELECT "
3354         "index_data.index_id, "
3355         "upgrade_key(index_data.value), "
3356         "upgrade_key(index_data.object_data_key), "
3357         "object_data.object_store_id "
3358         "FROM index_data "
3359         "JOIN object_data "
3360         "ON index_data.object_data_id = object_data.id;"
3361   ));
3362   if (NS_WARN_IF(NS_FAILED(rv))) {
3363     return rv;
3364   }
3365 
3366   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3367     // The trigger is no longer needed.
3368     "DROP TRIGGER index_data_upgrade_insert_trigger;"
3369   ));
3370   if (NS_WARN_IF(NS_FAILED(rv))) {
3371     return rv;
3372   }
3373 
3374   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3375     // The old table is no longer needed.
3376     "DROP TABLE index_data;"
3377   ));
3378   if (NS_WARN_IF(NS_FAILED(rv))) {
3379     return rv;
3380   }
3381 
3382   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3383     // Rename the table.
3384     "ALTER TABLE index_data_upgrade "
3385       "RENAME TO index_data;"
3386   ));
3387   if (NS_WARN_IF(NS_FAILED(rv))) {
3388     return rv;
3389   }
3390 
3391   // Update the |object_data| table to add the |index_data_values| column,
3392   // remove the ON DELETE CASCADE clause, and apply the WITHOUT ROWID
3393   // optimization.
3394   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3395     // Insert all the data.
3396     "INSERT INTO object_data_upgrade "
3397       "SELECT "
3398         "object_data.object_store_id, "
3399         "upgrade_key(object_data.key_value), "
3400         "temp_index_data_values.index_data_values, "
3401         "object_data.file_ids, "
3402         "object_data.data "
3403         "FROM object_data "
3404         "LEFT JOIN temp_index_data_values "
3405         "ON object_data.object_store_id = "
3406           "temp_index_data_values.object_store_id "
3407         "AND upgrade_key(object_data.key_value) = "
3408           "temp_index_data_values.key;"
3409   ));
3410   if (NS_WARN_IF(NS_FAILED(rv))) {
3411     return rv;
3412   }
3413 
3414   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3415     // The temporary table is no longer needed.
3416     "DROP TABLE temp_index_data_values;"
3417   ));
3418   if (NS_WARN_IF(NS_FAILED(rv))) {
3419     return rv;
3420   }
3421 
3422   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3423     // The old table is no longer needed.
3424     "DROP TABLE object_data;"
3425   ));
3426   if (NS_WARN_IF(NS_FAILED(rv))) {
3427     return rv;
3428   }
3429 
3430   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3431     // Rename the table.
3432     "ALTER TABLE object_data_upgrade "
3433       "RENAME TO object_data;"
3434   ));
3435   if (NS_WARN_IF(NS_FAILED(rv))) {
3436     return rv;
3437   }
3438 
3439   // Update the |object_store_index| table to remove the UNIQUE constraint and
3440   // the ON DELETE CASCADE clause.
3441   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3442     "INSERT INTO object_store_index_upgrade "
3443       "SELECT * "
3444         "FROM object_store_index;"
3445   ));
3446   if (NS_WARN_IF(NS_FAILED(rv))) {
3447     return rv;
3448   }
3449 
3450   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3451     "DROP TABLE object_store_index;"
3452   ));
3453   if (NS_WARN_IF(NS_FAILED(rv))) {
3454     return rv;
3455   }
3456 
3457   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3458     "ALTER TABLE object_store_index_upgrade "
3459       "RENAME TO object_store_index;"
3460   ));
3461   if (NS_WARN_IF(NS_FAILED(rv))) {
3462     return rv;
3463   }
3464 
3465   // Update the |object_store| table to remove the UNIQUE constraint.
3466   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3467     "INSERT INTO object_store_upgrade "
3468       "SELECT * "
3469         "FROM object_store;"
3470   ));
3471   if (NS_WARN_IF(NS_FAILED(rv))) {
3472     return rv;
3473   }
3474 
3475   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3476     "DROP TABLE object_store;"
3477   ));
3478   if (NS_WARN_IF(NS_FAILED(rv))) {
3479     return rv;
3480   }
3481 
3482   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3483     "ALTER TABLE object_store_upgrade "
3484       "RENAME TO object_store;"
3485   ));
3486   if (NS_WARN_IF(NS_FAILED(rv))) {
3487     return rv;
3488   }
3489 
3490   // Update the |database| table to include the origin, vacuum information, and
3491   // apply the WITHOUT ROWID optimization.
3492   nsCOMPtr<mozIStorageStatement> stmt;
3493   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
3494     "INSERT INTO database_upgrade "
3495       "SELECT name, :origin, version, 0, 0, 0 "
3496         "FROM database;"
3497   ), getter_AddRefs(stmt));
3498   if (NS_WARN_IF(NS_FAILED(rv))) {
3499     return rv;
3500   }
3501 
3502   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), aOrigin);
3503   if (NS_WARN_IF(NS_FAILED(rv))) {
3504     return rv;
3505   }
3506 
3507   rv = stmt->Execute();
3508   if (NS_WARN_IF(NS_FAILED(rv))) {
3509     return rv;
3510   }
3511 
3512   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3513     "DROP TABLE database;"
3514   ));
3515   if (NS_WARN_IF(NS_FAILED(rv))) {
3516     return rv;
3517   }
3518 
3519   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3520     "ALTER TABLE database_upgrade "
3521       "RENAME TO database;"
3522   ));
3523   if (NS_WARN_IF(NS_FAILED(rv))) {
3524     return rv;
3525   }
3526 
3527 #ifdef DEBUG
3528   {
3529     // Make sure there's only one entry in the |database| table.
3530     nsCOMPtr<mozIStorageStatement> stmt;
3531     MOZ_ASSERT(NS_SUCCEEDED(
3532       aConnection->CreateStatement(
3533         NS_LITERAL_CSTRING("SELECT COUNT(*) "
3534                              "FROM database;"),
3535         getter_AddRefs(stmt))));
3536 
3537     bool hasResult;
3538     MOZ_ASSERT(NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)));
3539 
3540     int64_t count;
3541     MOZ_ASSERT(NS_SUCCEEDED(stmt->GetInt64(0, &count)));
3542 
3543     MOZ_ASSERT(count == 1);
3544   }
3545 #endif
3546 
3547   // Recreate file table triggers.
3548   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3549     "CREATE TRIGGER object_data_insert_trigger "
3550       "AFTER INSERT ON object_data "
3551       "WHEN NEW.file_ids IS NOT NULL "
3552       "BEGIN "
3553         "SELECT update_refcount(NULL, NEW.file_ids);"
3554       "END;"
3555   ));
3556   if (NS_WARN_IF(NS_FAILED(rv))) {
3557     return rv;
3558   }
3559 
3560   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3561     "CREATE TRIGGER object_data_update_trigger "
3562       "AFTER UPDATE OF file_ids ON object_data "
3563       "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
3564       "BEGIN "
3565         "SELECT update_refcount(OLD.file_ids, NEW.file_ids);"
3566       "END;"
3567   ));
3568   if (NS_WARN_IF(NS_FAILED(rv))) {
3569     return rv;
3570   }
3571 
3572   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3573     "CREATE TRIGGER object_data_delete_trigger "
3574       "AFTER DELETE ON object_data "
3575       "WHEN OLD.file_ids IS NOT NULL "
3576       "BEGIN "
3577         "SELECT update_refcount(OLD.file_ids, NULL);"
3578       "END;"
3579   ));
3580   if (NS_WARN_IF(NS_FAILED(rv))) {
3581     return rv;
3582   }
3583 
3584   // Finally, turn on auto_vacuum mode. We use full auto_vacuum mode to reclaim
3585   // disk space on mobile devices (at the cost of some COMMIT speed), and
3586   // incremental auto_vacuum mode on desktop builds.
3587   rv = aConnection->ExecuteSimpleSQL(
3588 #ifdef IDB_MOBILE
3589     NS_LITERAL_CSTRING("PRAGMA auto_vacuum = FULL;")
3590 #else
3591     NS_LITERAL_CSTRING("PRAGMA auto_vacuum = INCREMENTAL;")
3592 #endif
3593   );
3594   if (NS_WARN_IF(NS_FAILED(rv))) {
3595     return rv;
3596   }
3597 
3598   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(18, 0));
3599   if (NS_WARN_IF(NS_FAILED(rv))) {
3600     return rv;
3601   }
3602 
3603   return NS_OK;
3604 }
3605 
3606 nsresult
UpgradeSchemaFrom17_0To18_0(mozIStorageConnection * aConnection,const nsACString & aOrigin)3607 UpgradeSchemaFrom17_0To18_0(mozIStorageConnection* aConnection,
3608                             const nsACString& aOrigin)
3609 {
3610   MOZ_ASSERT(aConnection);
3611   MOZ_ASSERT(!aOrigin.IsEmpty());
3612 
3613   PROFILER_LABEL("IndexedDB",
3614                  "UpgradeSchemaFrom17_0To18_0",
3615                  js::ProfileEntry::Category::STORAGE);
3616 
3617   return UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(aConnection, aOrigin);
3618 }
3619 
3620 nsresult
UpgradeSchemaFrom18_0To19_0(mozIStorageConnection * aConnection)3621 UpgradeSchemaFrom18_0To19_0(mozIStorageConnection* aConnection)
3622 {
3623   AssertIsOnIOThread();
3624   MOZ_ASSERT(aConnection);
3625 
3626   nsresult rv;
3627   PROFILER_LABEL("IndexedDB",
3628                  "UpgradeSchemaFrom18_0To19_0",
3629                  js::ProfileEntry::Category::STORAGE);
3630 
3631   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3632     "ALTER TABLE object_store_index "
3633     "ADD COLUMN locale TEXT;"
3634   ));
3635   if (NS_WARN_IF(NS_FAILED(rv))) {
3636     return rv;
3637   }
3638 
3639   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3640     "ALTER TABLE object_store_index "
3641     "ADD COLUMN is_auto_locale BOOLEAN;"
3642   ));
3643   if (NS_WARN_IF(NS_FAILED(rv))) {
3644     return rv;
3645   }
3646 
3647   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3648     "ALTER TABLE index_data "
3649     "ADD COLUMN value_locale BLOB;"
3650   ));
3651   if (NS_WARN_IF(NS_FAILED(rv))) {
3652     return rv;
3653   }
3654 
3655   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3656     "ALTER TABLE unique_index_data "
3657     "ADD COLUMN value_locale BLOB;"
3658   ));
3659   if (NS_WARN_IF(NS_FAILED(rv))) {
3660     return rv;
3661   }
3662 
3663   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3664     "CREATE INDEX index_data_value_locale_index "
3665     "ON index_data (index_id, value_locale, object_data_key, value) "
3666     "WHERE value_locale IS NOT NULL;"
3667   ));
3668   if (NS_WARN_IF(NS_FAILED(rv))) {
3669     return rv;
3670   }
3671 
3672   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3673     "CREATE INDEX unique_index_data_value_locale_index "
3674     "ON unique_index_data (index_id, value_locale, object_data_key, value) "
3675     "WHERE value_locale IS NOT NULL;"
3676   ));
3677   if (NS_WARN_IF(NS_FAILED(rv))) {
3678     return rv;
3679   }
3680 
3681   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(19, 0));
3682   if (NS_WARN_IF(NS_FAILED(rv))) {
3683     return rv;
3684   }
3685 
3686   return NS_OK;
3687 }
3688 
3689 #if !defined(MOZ_B2G)
3690 
3691 class NormalJSContext;
3692 
3693 class UpgradeFileIdsFunction final
3694   : public mozIStorageFunction
3695 {
3696   RefPtr<FileManager> mFileManager;
3697   nsAutoPtr<NormalJSContext> mContext;
3698 
3699 public:
UpgradeFileIdsFunction()3700   UpgradeFileIdsFunction()
3701   {
3702     AssertIsOnIOThread();
3703   }
3704 
3705   nsresult
3706   Init(nsIFile* aFMDirectory,
3707        mozIStorageConnection* aConnection);
3708 
3709   NS_DECL_ISUPPORTS
3710 
3711 private:
~UpgradeFileIdsFunction()3712   ~UpgradeFileIdsFunction()
3713   {
3714     AssertIsOnIOThread();
3715 
3716     if (mFileManager) {
3717       mFileManager->Invalidate();
3718     }
3719   }
3720 
3721   NS_IMETHOD
3722   OnFunctionCall(mozIStorageValueArray* aArguments,
3723                  nsIVariant** aResult) override;
3724 };
3725 
3726 #endif // MOZ_B2G
3727 
3728 nsresult
UpgradeSchemaFrom19_0To20_0(nsIFile * aFMDirectory,mozIStorageConnection * aConnection)3729 UpgradeSchemaFrom19_0To20_0(nsIFile* aFMDirectory,
3730                             mozIStorageConnection* aConnection)
3731 {
3732   AssertIsOnIOThread();
3733   MOZ_ASSERT(aConnection);
3734 
3735   PROFILER_LABEL("IndexedDB",
3736                  "UpgradeSchemaFrom19_0To20_0",
3737                  js::ProfileEntry::Category::STORAGE);
3738 
3739 #if defined(MOZ_B2G)
3740 
3741   // We don't have to do the upgrade of file ids on B2G. The old format was
3742   // only used by the previous single process implementation and B2G was
3743   // always multi process. This is a nice optimization since the upgrade needs
3744   // to deserialize all structured clones which reference a stored file or
3745   // a mutable file.
3746   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(20, 0));
3747   if (NS_WARN_IF(NS_FAILED(rv))) {
3748     return rv;
3749   }
3750 
3751 #else // MOZ_B2G
3752 
3753   nsCOMPtr<mozIStorageStatement> stmt;
3754   nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
3755     "SELECT count(*) "
3756     "FROM object_data "
3757     "WHERE file_ids IS NOT NULL"
3758   ), getter_AddRefs(stmt));
3759   if (NS_WARN_IF(NS_FAILED(rv))) {
3760     return rv;
3761   }
3762 
3763   int64_t count;
3764 
3765   {
3766     mozStorageStatementScoper scoper(stmt);
3767 
3768     bool hasResult;
3769     rv = stmt->ExecuteStep(&hasResult);
3770     if (NS_WARN_IF(NS_FAILED(rv))) {
3771       return rv;
3772     }
3773 
3774     if (NS_WARN_IF(!hasResult)) {
3775       MOZ_ASSERT(false, "This should never be possible!");
3776       return NS_ERROR_FAILURE;
3777     }
3778 
3779     count = stmt->AsInt64(0);
3780     if (NS_WARN_IF(count < 0)) {
3781       MOZ_ASSERT(false, "This should never be possible!");
3782       return NS_ERROR_FAILURE;
3783     }
3784   }
3785 
3786   if (count == 0) {
3787     // Nothing to upgrade.
3788     rv = aConnection->SetSchemaVersion(MakeSchemaVersion(20, 0));
3789     if (NS_WARN_IF(NS_FAILED(rv))) {
3790       return rv;
3791     }
3792 
3793     return NS_OK;
3794   }
3795 
3796   RefPtr<UpgradeFileIdsFunction> function = new UpgradeFileIdsFunction();
3797 
3798   rv = function->Init(aFMDirectory, aConnection);
3799   if (NS_WARN_IF(NS_FAILED(rv))) {
3800     return rv;
3801   }
3802 
3803   NS_NAMED_LITERAL_CSTRING(functionName, "upgrade");
3804 
3805   rv = aConnection->CreateFunction(functionName, 2, function);
3806   if (NS_WARN_IF(NS_FAILED(rv))) {
3807     return rv;
3808   }
3809 
3810   // Disable update trigger.
3811   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3812     "DROP TRIGGER object_data_update_trigger;"
3813   ));
3814   if (NS_WARN_IF(NS_FAILED(rv))) {
3815     return rv;
3816   }
3817 
3818   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3819     "UPDATE object_data "
3820       "SET file_ids = upgrade(file_ids, data) "
3821       "WHERE file_ids IS NOT NULL;"
3822   ));
3823   if (NS_WARN_IF(NS_FAILED(rv))) {
3824     return rv;
3825   }
3826 
3827   // Enable update trigger.
3828   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
3829     "CREATE TRIGGER object_data_update_trigger "
3830     "AFTER UPDATE OF file_ids ON object_data "
3831     "FOR EACH ROW "
3832     "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
3833     "BEGIN "
3834       "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
3835     "END;"
3836   ));
3837   if (NS_WARN_IF(NS_FAILED(rv))) {
3838     return rv;
3839   }
3840 
3841   rv = aConnection->RemoveFunction(functionName);
3842   if (NS_WARN_IF(NS_FAILED(rv))) {
3843     return rv;
3844   }
3845 
3846   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(20, 0));
3847   if (NS_WARN_IF(NS_FAILED(rv))) {
3848     return rv;
3849   }
3850 
3851 #endif // MOZ_B2G
3852 
3853   return NS_OK;
3854 }
3855 
3856 class UpgradeIndexDataValuesFunction final
3857   : public mozIStorageFunction
3858 {
3859 public:
UpgradeIndexDataValuesFunction()3860   UpgradeIndexDataValuesFunction()
3861   {
3862     AssertIsOnIOThread();
3863   }
3864 
3865   NS_DECL_ISUPPORTS
3866 
3867 private:
~UpgradeIndexDataValuesFunction()3868   ~UpgradeIndexDataValuesFunction()
3869   {
3870     AssertIsOnIOThread();
3871   }
3872 
3873   nsresult
3874   ReadOldCompressedIDVFromBlob(const uint8_t* aBlobData,
3875                                uint32_t aBlobDataLength,
3876                                nsTArray<IndexDataValue>& aIndexValues);
3877 
3878   NS_IMETHOD
3879   OnFunctionCall(mozIStorageValueArray* aArguments,
3880                  nsIVariant** aResult) override;
3881 };
3882 
NS_IMPL_ISUPPORTS(UpgradeIndexDataValuesFunction,mozIStorageFunction)3883 NS_IMPL_ISUPPORTS(UpgradeIndexDataValuesFunction, mozIStorageFunction)
3884 
3885 nsresult
3886 UpgradeIndexDataValuesFunction::ReadOldCompressedIDVFromBlob(
3887                                    const uint8_t* aBlobData,
3888                                    uint32_t aBlobDataLength,
3889                                    nsTArray<IndexDataValue>& aIndexValues)
3890 {
3891   MOZ_ASSERT(!NS_IsMainThread());
3892   MOZ_ASSERT(!IsOnBackgroundThread());
3893   MOZ_ASSERT(aBlobData);
3894   MOZ_ASSERT(aBlobDataLength);
3895   MOZ_ASSERT(aIndexValues.IsEmpty());
3896 
3897   const uint8_t* blobDataIter = aBlobData;
3898   const uint8_t* blobDataEnd = aBlobData + aBlobDataLength;
3899 
3900   int64_t indexId;
3901   bool unique;
3902   bool nextIndexIdAlreadyRead = false;
3903 
3904   while (blobDataIter < blobDataEnd) {
3905     if (!nextIndexIdAlreadyRead) {
3906       ReadCompressedIndexId(&blobDataIter, blobDataEnd, &indexId, &unique);
3907     }
3908     nextIndexIdAlreadyRead = false;
3909 
3910     if (NS_WARN_IF(blobDataIter == blobDataEnd)) {
3911       IDB_REPORT_INTERNAL_ERR();
3912       return NS_ERROR_FILE_CORRUPTED;
3913     }
3914 
3915     // Read key buffer length.
3916     const uint64_t keyBufferLength =
3917       ReadCompressedNumber(&blobDataIter, blobDataEnd);
3918 
3919     if (NS_WARN_IF(blobDataIter == blobDataEnd) ||
3920         NS_WARN_IF(keyBufferLength > uint64_t(UINT32_MAX)) ||
3921         NS_WARN_IF(blobDataIter + keyBufferLength > blobDataEnd)) {
3922       IDB_REPORT_INTERNAL_ERR();
3923       return NS_ERROR_FILE_CORRUPTED;
3924     }
3925 
3926     nsCString keyBuffer(reinterpret_cast<const char*>(blobDataIter),
3927                         uint32_t(keyBufferLength));
3928     blobDataIter += keyBufferLength;
3929 
3930     IndexDataValue idv(indexId, unique, Key(keyBuffer));
3931 
3932     if (blobDataIter < blobDataEnd) {
3933       // Read either a sort key buffer length or an index id.
3934       uint64_t maybeIndexId = ReadCompressedNumber(&blobDataIter, blobDataEnd);
3935 
3936       // Locale-aware indexes haven't been around long enough to have any users,
3937       // we can safely assume all sort key buffer lengths will be zero.
3938       if (maybeIndexId != 0) {
3939         if (maybeIndexId % 2) {
3940           unique = true;
3941           maybeIndexId--;
3942         } else {
3943           unique = false;
3944         }
3945         indexId = maybeIndexId/2;
3946         nextIndexIdAlreadyRead = true;
3947       }
3948     }
3949 
3950     if (NS_WARN_IF(!aIndexValues.InsertElementSorted(idv, fallible))) {
3951       IDB_REPORT_INTERNAL_ERR();
3952       return NS_ERROR_OUT_OF_MEMORY;
3953     }
3954   }
3955 
3956   MOZ_ASSERT(blobDataIter == blobDataEnd);
3957 
3958   return NS_OK;
3959 }
3960 
3961 NS_IMETHODIMP
OnFunctionCall(mozIStorageValueArray * aArguments,nsIVariant ** aResult)3962 UpgradeIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
3963                                                nsIVariant** aResult)
3964 {
3965   MOZ_ASSERT(aArguments);
3966   MOZ_ASSERT(aResult);
3967 
3968   PROFILER_LABEL("IndexedDB",
3969                  "UpgradeIndexDataValuesFunction::OnFunctionCall",
3970                  js::ProfileEntry::Category::STORAGE);
3971 
3972   uint32_t argc;
3973   nsresult rv = aArguments->GetNumEntries(&argc);
3974   if (NS_WARN_IF(NS_FAILED(rv))) {
3975     return rv;
3976   }
3977 
3978   if (argc != 1) {
3979     NS_WARNING("Don't call me with the wrong number of arguments!");
3980     return NS_ERROR_UNEXPECTED;
3981   }
3982 
3983   int32_t type;
3984   rv = aArguments->GetTypeOfIndex(0, &type);
3985   if (NS_WARN_IF(NS_FAILED(rv))) {
3986     return rv;
3987   }
3988 
3989   if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
3990     NS_WARNING("Don't call me with the wrong type of arguments!");
3991     return NS_ERROR_UNEXPECTED;
3992   }
3993 
3994   const uint8_t* oldBlob;
3995   uint32_t oldBlobLength;
3996   rv = aArguments->GetSharedBlob(0, &oldBlobLength, &oldBlob);
3997   if (NS_WARN_IF(NS_FAILED(rv))) {
3998     return rv;
3999   }
4000 
4001   AutoTArray<IndexDataValue, 32> oldIdv;
4002   rv = ReadOldCompressedIDVFromBlob(oldBlob, oldBlobLength, oldIdv);
4003   if (NS_WARN_IF(NS_FAILED(rv))) {
4004     return rv;
4005   }
4006 
4007   UniqueFreePtr<uint8_t> newIdv;
4008   uint32_t newIdvLength;
4009   rv = MakeCompressedIndexDataValues(oldIdv, newIdv, &newIdvLength);
4010 
4011   std::pair<uint8_t*, int> data(newIdv.release(), newIdvLength);
4012 
4013   nsCOMPtr<nsIVariant> result = new storage::AdoptedBlobVariant(data);
4014 
4015   result.forget(aResult);
4016   return NS_OK;
4017 }
4018 
4019 nsresult
UpgradeSchemaFrom20_0To21_0(mozIStorageConnection * aConnection)4020 UpgradeSchemaFrom20_0To21_0(mozIStorageConnection* aConnection)
4021 {
4022   // This should have been part of the 18 to 19 upgrade, where we changed the
4023   // layout of the index_data_values blobs but didn't upgrade the existing data.
4024   // See bug 1202788.
4025 
4026   AssertIsOnIOThread();
4027   MOZ_ASSERT(aConnection);
4028 
4029   PROFILER_LABEL("IndexedDB",
4030                  "UpgradeSchemaFrom20_0To21_0",
4031                  js::ProfileEntry::Category::STORAGE);
4032 
4033   RefPtr<UpgradeIndexDataValuesFunction> function =
4034     new UpgradeIndexDataValuesFunction();
4035 
4036   NS_NAMED_LITERAL_CSTRING(functionName, "upgrade_idv");
4037 
4038   nsresult rv = aConnection->CreateFunction(functionName, 1, function);
4039   if (NS_WARN_IF(NS_FAILED(rv))) {
4040     return rv;
4041   }
4042 
4043   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
4044     "UPDATE object_data "
4045       "SET index_data_values = upgrade_idv(index_data_values) "
4046       "WHERE index_data_values IS NOT NULL;"
4047   ));
4048   if (NS_WARN_IF(NS_FAILED(rv))) {
4049     return rv;
4050   }
4051 
4052   rv = aConnection->RemoveFunction(functionName);
4053   if (NS_WARN_IF(NS_FAILED(rv))) {
4054     return rv;
4055   }
4056 
4057   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(21, 0));
4058   if (NS_WARN_IF(NS_FAILED(rv))) {
4059     return rv;
4060   }
4061 
4062   return NS_OK;
4063 }
4064 
4065 nsresult
UpgradeSchemaFrom21_0To22_0(mozIStorageConnection * aConnection)4066 UpgradeSchemaFrom21_0To22_0(mozIStorageConnection* aConnection)
4067 {
4068   // The only change between 21 and 22 was a different structured clone format,
4069   // but it's backwards-compatible.
4070   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(22, 0));
4071   if (NS_WARN_IF(NS_FAILED(rv))) {
4072     return rv;
4073   }
4074 
4075   return NS_OK;
4076 }
4077 
4078 nsresult
UpgradeSchemaFrom22_0To23_0(mozIStorageConnection * aConnection,const nsACString & aOrigin)4079 UpgradeSchemaFrom22_0To23_0(mozIStorageConnection* aConnection,
4080                             const nsACString& aOrigin)
4081 {
4082   AssertIsOnIOThread();
4083   MOZ_ASSERT(aConnection);
4084   MOZ_ASSERT(!aOrigin.IsEmpty());
4085 
4086   PROFILER_LABEL("IndexedDB",
4087                  "UpgradeSchemaFrom22_0To23_0",
4088                  js::ProfileEntry::Category::STORAGE);
4089 
4090   nsCOMPtr<mozIStorageStatement> stmt;
4091   nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
4092     "UPDATE database "
4093       "SET origin = :origin;"
4094   ), getter_AddRefs(stmt));
4095   if (NS_WARN_IF(NS_FAILED(rv))) {
4096     return rv;
4097   }
4098 
4099   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), aOrigin);
4100   if (NS_WARN_IF(NS_FAILED(rv))) {
4101     return rv;
4102   }
4103 
4104   rv = stmt->Execute();
4105   if (NS_WARN_IF(NS_FAILED(rv))) {
4106     return rv;
4107   }
4108 
4109   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(23, 0));
4110   if (NS_WARN_IF(NS_FAILED(rv))) {
4111     return rv;
4112   }
4113 
4114   return NS_OK;
4115 }
4116 
4117 nsresult
UpgradeSchemaFrom23_0To24_0(mozIStorageConnection * aConnection)4118 UpgradeSchemaFrom23_0To24_0(mozIStorageConnection* aConnection)
4119 {
4120   // The only change between 23 and 24 was a different structured clone format,
4121   // but it's backwards-compatible.
4122   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(24, 0));
4123   if (NS_WARN_IF(NS_FAILED(rv))) {
4124     return rv;
4125   }
4126 
4127   return NS_OK;
4128 }
4129 
4130 nsresult
UpgradeSchemaFrom24_0To25_0(mozIStorageConnection * aConnection)4131 UpgradeSchemaFrom24_0To25_0(mozIStorageConnection* aConnection)
4132 {
4133   // The changes between 24 and 25 were an upgraded snappy library, a different
4134   // structured clone format and a different file_ds format. But everything is
4135   // backwards-compatible.
4136   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(25, 0));
4137   if (NS_WARN_IF(NS_FAILED(rv))) {
4138     return rv;
4139   }
4140 
4141   return NS_OK;
4142 }
4143 
4144 nsresult
GetDatabaseFileURL(nsIFile * aDatabaseFile,PersistenceType aPersistenceType,const nsACString & aGroup,const nsACString & aOrigin,uint32_t aTelemetryId,nsIFileURL ** aResult)4145 GetDatabaseFileURL(nsIFile* aDatabaseFile,
4146                    PersistenceType aPersistenceType,
4147                    const nsACString& aGroup,
4148                    const nsACString& aOrigin,
4149                    uint32_t aTelemetryId,
4150                    nsIFileURL** aResult)
4151 {
4152   MOZ_ASSERT(aDatabaseFile);
4153   MOZ_ASSERT(aResult);
4154 
4155   nsresult rv;
4156 
4157   nsCOMPtr<nsIProtocolHandler> protocolHandler(
4158     do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "file", &rv));
4159   if (NS_WARN_IF(NS_FAILED(rv))) {
4160     return rv;
4161   }
4162 
4163   nsCOMPtr<nsIFileProtocolHandler> fileHandler(
4164     do_QueryInterface(protocolHandler, &rv));
4165   if (NS_WARN_IF(NS_FAILED(rv))) {
4166     return rv;
4167   }
4168 
4169   nsCOMPtr<nsIURI> uri;
4170   rv = fileHandler->NewFileURI(aDatabaseFile, getter_AddRefs(uri));
4171   if (NS_WARN_IF(NS_FAILED(rv))) {
4172     return rv;
4173   }
4174 
4175   nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
4176   MOZ_ASSERT(fileUrl);
4177 
4178   nsAutoCString type;
4179   PersistenceTypeToText(aPersistenceType, type);
4180 
4181   nsAutoCString telemetryFilenameClause;
4182   if (aTelemetryId) {
4183     telemetryFilenameClause.AssignLiteral("&telemetryFilename=indexedDB-");
4184     telemetryFilenameClause.AppendInt(aTelemetryId);
4185     telemetryFilenameClause.AppendLiteral(".sqlite");
4186   }
4187 
4188   rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type +
4189                          NS_LITERAL_CSTRING("&group=") + aGroup +
4190                          NS_LITERAL_CSTRING("&origin=") + aOrigin +
4191                          NS_LITERAL_CSTRING("&cache=private") +
4192                          telemetryFilenameClause);
4193   if (NS_WARN_IF(NS_FAILED(rv))) {
4194     return rv;
4195   }
4196 
4197   fileUrl.forget(aResult);
4198   return NS_OK;
4199 }
4200 
4201 nsresult
SetDefaultPragmas(mozIStorageConnection * aConnection)4202 SetDefaultPragmas(mozIStorageConnection* aConnection)
4203 {
4204   MOZ_ASSERT(!NS_IsMainThread());
4205   MOZ_ASSERT(aConnection);
4206 
4207   static const char kBuiltInPragmas[] =
4208     // We use foreign keys in DEBUG builds only because there is a performance
4209     // cost to using them.
4210    "PRAGMA foreign_keys = "
4211 #ifdef DEBUG
4212      "ON"
4213 #else
4214      "OFF"
4215 #endif
4216      ";"
4217 
4218     // The "INSERT OR REPLACE" statement doesn't fire the update trigger,
4219     // instead it fires only the insert trigger. This confuses the update
4220     // refcount function. This behavior changes with enabled recursive triggers,
4221     // so the statement fires the delete trigger first and then the insert
4222     // trigger.
4223     "PRAGMA recursive_triggers = ON;"
4224 
4225     // We aggressively truncate the database file when idle so don't bother
4226     // overwriting the WAL with 0 during active periods.
4227     "PRAGMA secure_delete = OFF;"
4228   ;
4229 
4230   nsresult rv =
4231     aConnection->ExecuteSimpleSQL(
4232       nsDependentCString(kBuiltInPragmas,
4233                          LiteralStringLength(kBuiltInPragmas)));
4234   if (NS_WARN_IF(NS_FAILED(rv))) {
4235     return rv;
4236   }
4237 
4238   nsAutoCString pragmaStmt;
4239   pragmaStmt.AssignLiteral("PRAGMA synchronous = ");
4240 
4241   if (IndexedDatabaseManager::FullSynchronous()) {
4242     pragmaStmt.AppendLiteral("FULL");
4243   } else {
4244     pragmaStmt.AppendLiteral("NORMAL");
4245   }
4246   pragmaStmt.Append(';');
4247 
4248   rv = aConnection->ExecuteSimpleSQL(pragmaStmt);
4249   if (NS_WARN_IF(NS_FAILED(rv))) {
4250     return rv;
4251   }
4252 
4253 #ifndef IDB_MOBILE
4254   if (kSQLiteGrowthIncrement) {
4255     // This is just an optimization so ignore the failure if the disk is
4256     // currently too full.
4257     rv = aConnection->SetGrowthIncrement(kSQLiteGrowthIncrement,
4258                                          EmptyCString());
4259     if (rv != NS_ERROR_FILE_TOO_BIG && NS_WARN_IF(NS_FAILED(rv))) {
4260       return rv;
4261     }
4262   }
4263 #endif // IDB_MOBILE
4264 
4265   return NS_OK;
4266 }
4267 
4268 nsresult
SetJournalMode(mozIStorageConnection * aConnection)4269 SetJournalMode(mozIStorageConnection* aConnection)
4270 {
4271   MOZ_ASSERT(!NS_IsMainThread());
4272   MOZ_ASSERT(aConnection);
4273 
4274   // Try enabling WAL mode. This can fail in various circumstances so we have to
4275   // check the results here.
4276   NS_NAMED_LITERAL_CSTRING(journalModeQueryStart, "PRAGMA journal_mode = ");
4277   NS_NAMED_LITERAL_CSTRING(journalModeWAL, "wal");
4278 
4279   nsCOMPtr<mozIStorageStatement> stmt;
4280   nsresult rv =
4281     aConnection->CreateStatement(journalModeQueryStart + journalModeWAL,
4282                                  getter_AddRefs(stmt));
4283   if (NS_WARN_IF(NS_FAILED(rv))) {
4284     return rv;
4285   }
4286 
4287   bool hasResult;
4288   rv = stmt->ExecuteStep(&hasResult);
4289   if (NS_WARN_IF(NS_FAILED(rv))) {
4290     return rv;
4291   }
4292 
4293   MOZ_ASSERT(hasResult);
4294 
4295   nsCString journalMode;
4296   rv = stmt->GetUTF8String(0, journalMode);
4297   if (NS_WARN_IF(NS_FAILED(rv))) {
4298     return rv;
4299   }
4300 
4301   if (journalMode.Equals(journalModeWAL)) {
4302     // WAL mode successfully enabled. Maybe set limits on its size here.
4303     if (kMaxWALPages >= 0) {
4304       nsAutoCString pageCount;
4305       pageCount.AppendInt(kMaxWALPages);
4306 
4307       rv = aConnection->ExecuteSimpleSQL(
4308         NS_LITERAL_CSTRING("PRAGMA wal_autocheckpoint = ") + pageCount);
4309       if (NS_WARN_IF(NS_FAILED(rv))) {
4310         return rv;
4311       }
4312     }
4313   } else {
4314     NS_WARNING("Failed to set WAL mode, falling back to normal journal mode.");
4315 #ifdef IDB_MOBILE
4316     rv = aConnection->ExecuteSimpleSQL(journalModeQueryStart +
4317                                        NS_LITERAL_CSTRING("truncate"));
4318     if (NS_WARN_IF(NS_FAILED(rv))) {
4319       return rv;
4320     }
4321 #endif
4322   }
4323 
4324   return NS_OK;
4325 }
4326 
4327 template <class FileOrURLType>
4328 struct StorageOpenTraits;
4329 
4330 template <>
4331 struct StorageOpenTraits<nsIFileURL*>
4332 {
4333   static nsresult
Openmozilla::dom::indexedDB::__anone276c2b20111::StorageOpenTraits4334   Open(mozIStorageService* aStorageService,
4335        nsIFileURL* aFileURL,
4336        mozIStorageConnection** aConnection)
4337   {
4338     return aStorageService->OpenDatabaseWithFileURL(aFileURL, aConnection);
4339   }
4340 
4341 #ifdef DEBUG
4342   static void
GetPathmozilla::dom::indexedDB::__anone276c2b20111::StorageOpenTraits4343   GetPath(nsIFileURL* aFileURL, nsCString& aPath)
4344   {
4345     MOZ_ALWAYS_SUCCEEDS(aFileURL->GetFileName(aPath));
4346   }
4347 #endif
4348 };
4349 
4350 template <>
4351 struct StorageOpenTraits<nsIFile*>
4352 {
4353   static nsresult
Openmozilla::dom::indexedDB::__anone276c2b20111::StorageOpenTraits4354   Open(mozIStorageService* aStorageService,
4355        nsIFile* aFile,
4356        mozIStorageConnection** aConnection)
4357   {
4358     return aStorageService->OpenUnsharedDatabase(aFile, aConnection);
4359   }
4360 
4361 #ifdef DEBUG
4362   static void
GetPathmozilla::dom::indexedDB::__anone276c2b20111::StorageOpenTraits4363   GetPath(nsIFile* aFile, nsCString& aPath)
4364   {
4365     nsString path;
4366     MOZ_ALWAYS_SUCCEEDS(aFile->GetPath(path));
4367 
4368     aPath.AssignWithConversion(path);
4369   }
4370 #endif
4371 };
4372 
4373 template <template <class> class SmartPtr, class FileOrURLType>
4374 struct StorageOpenTraits<SmartPtr<FileOrURLType>>
4375   : public StorageOpenTraits<FileOrURLType*>
4376 { };
4377 
4378 template <class FileOrURLType>
4379 nsresult
OpenDatabaseAndHandleBusy(mozIStorageService * aStorageService,FileOrURLType aFileOrURL,mozIStorageConnection ** aConnection)4380 OpenDatabaseAndHandleBusy(mozIStorageService* aStorageService,
4381                           FileOrURLType aFileOrURL,
4382                           mozIStorageConnection** aConnection)
4383 {
4384   MOZ_ASSERT(!NS_IsMainThread());
4385   MOZ_ASSERT(!IsOnBackgroundThread());
4386   MOZ_ASSERT(aStorageService);
4387   MOZ_ASSERT(aFileOrURL);
4388   MOZ_ASSERT(aConnection);
4389 
4390   nsCOMPtr<mozIStorageConnection> connection;
4391   nsresult rv =
4392     StorageOpenTraits<FileOrURLType>::Open(aStorageService,
4393                                            aFileOrURL,
4394                                            getter_AddRefs(connection));
4395 
4396   if (rv == NS_ERROR_STORAGE_BUSY) {
4397 #ifdef DEBUG
4398     {
4399       nsCString path;
4400       StorageOpenTraits<FileOrURLType>::GetPath(aFileOrURL, path);
4401 
4402       nsPrintfCString message("Received NS_ERROR_STORAGE_BUSY when attempting "
4403                               "to open database '%s', retrying for up to 10 "
4404                               "seconds",
4405                               path.get());
4406       NS_WARNING(message.get());
4407     }
4408 #endif
4409 
4410     // Another thread must be checkpointing the WAL. Wait up to 10 seconds for
4411     // that to complete.
4412     TimeStamp start = TimeStamp::NowLoRes();
4413 
4414     while (true) {
4415       PR_Sleep(PR_MillisecondsToInterval(100));
4416 
4417       rv = StorageOpenTraits<FileOrURLType>::Open(aStorageService,
4418                                                   aFileOrURL,
4419                                                   getter_AddRefs(connection));
4420       if (rv != NS_ERROR_STORAGE_BUSY ||
4421           TimeStamp::NowLoRes() - start > TimeDuration::FromSeconds(10)) {
4422         break;
4423       }
4424     }
4425   }
4426 
4427   if (NS_WARN_IF(NS_FAILED(rv))) {
4428     return rv;
4429   }
4430 
4431   connection.forget(aConnection);
4432   return NS_OK;
4433 }
4434 
4435 nsresult
CreateStorageConnection(nsIFile * aDBFile,nsIFile * aFMDirectory,const nsAString & aName,PersistenceType aPersistenceType,const nsACString & aGroup,const nsACString & aOrigin,uint32_t aTelemetryId,mozIStorageConnection ** aConnection)4436 CreateStorageConnection(nsIFile* aDBFile,
4437                         nsIFile* aFMDirectory,
4438                         const nsAString& aName,
4439                         PersistenceType aPersistenceType,
4440                         const nsACString& aGroup,
4441                         const nsACString& aOrigin,
4442                         uint32_t aTelemetryId,
4443                         mozIStorageConnection** aConnection)
4444 {
4445   AssertIsOnIOThread();
4446   MOZ_ASSERT(aDBFile);
4447   MOZ_ASSERT(aFMDirectory);
4448   MOZ_ASSERT(aConnection);
4449 
4450   PROFILER_LABEL("IndexedDB",
4451                  "CreateStorageConnection",
4452                  js::ProfileEntry::Category::STORAGE);
4453 
4454   nsresult rv;
4455   bool exists;
4456 
4457   if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
4458     rv = aDBFile->Exists(&exists);
4459     if (NS_WARN_IF(NS_FAILED(rv))) {
4460       return rv;
4461     }
4462 
4463     if (!exists) {
4464       NS_WARNING("Refusing to create database because disk space is low!");
4465       return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
4466     }
4467   }
4468 
4469   nsCOMPtr<nsIFileURL> dbFileUrl;
4470   rv = GetDatabaseFileURL(aDBFile,
4471                           aPersistenceType,
4472                           aGroup,
4473                           aOrigin,
4474                           aTelemetryId,
4475                           getter_AddRefs(dbFileUrl));
4476   if (NS_WARN_IF(NS_FAILED(rv))) {
4477     return rv;
4478   }
4479 
4480   nsCOMPtr<mozIStorageService> ss =
4481     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
4482   if (NS_WARN_IF(NS_FAILED(rv))) {
4483     return rv;
4484   }
4485 
4486   nsCOMPtr<mozIStorageConnection> connection;
4487   rv = OpenDatabaseAndHandleBusy(ss, dbFileUrl, getter_AddRefs(connection));
4488   if (rv == NS_ERROR_FILE_CORRUPTED) {
4489     // If we're just opening the database during origin initialization, then
4490     // we don't want to erase any files. The failure here will fail origin
4491     // initialization too.
4492     if (aName.IsVoid()) {
4493       return rv;
4494     }
4495 
4496     // Nuke the database file.
4497     rv = aDBFile->Remove(false);
4498     if (NS_WARN_IF(NS_FAILED(rv))) {
4499       return rv;
4500     }
4501 
4502     rv = aFMDirectory->Exists(&exists);
4503     if (NS_WARN_IF(NS_FAILED(rv))) {
4504       return rv;
4505     }
4506 
4507     if (exists) {
4508       bool isDirectory;
4509       rv = aFMDirectory->IsDirectory(&isDirectory);
4510       if (NS_WARN_IF(NS_FAILED(rv))) {
4511         return rv;
4512       }
4513       if (NS_WARN_IF(!isDirectory)) {
4514         IDB_REPORT_INTERNAL_ERR();
4515         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4516       }
4517 
4518       rv = aFMDirectory->Remove(true);
4519       if (NS_WARN_IF(NS_FAILED(rv))) {
4520         return rv;
4521       }
4522     }
4523 
4524     rv = OpenDatabaseAndHandleBusy(ss, dbFileUrl, getter_AddRefs(connection));
4525   }
4526 
4527   if (NS_WARN_IF(NS_FAILED(rv))) {
4528     return rv;
4529   }
4530 
4531   rv = SetDefaultPragmas(connection);
4532   if (NS_WARN_IF(NS_FAILED(rv))) {
4533     return rv;
4534   }
4535 
4536   rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem"));
4537   if (NS_WARN_IF(NS_FAILED(rv))) {
4538     return rv;
4539   }
4540 
4541   // Check to make sure that the database schema is correct.
4542   int32_t schemaVersion;
4543   rv = connection->GetSchemaVersion(&schemaVersion);
4544   if (NS_WARN_IF(NS_FAILED(rv))) {
4545     return rv;
4546   }
4547 
4548   // Unknown schema will fail origin initialization too.
4549   if (!schemaVersion && aName.IsVoid()) {
4550     IDB_WARNING("Unable to open IndexedDB database, schema is not set!");
4551     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4552   }
4553 
4554   if (schemaVersion > kSQLiteSchemaVersion) {
4555     IDB_WARNING("Unable to open IndexedDB database, schema is too high!");
4556     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4557   }
4558 
4559   bool journalModeSet = false;
4560 
4561   if (schemaVersion != kSQLiteSchemaVersion) {
4562     const bool newDatabase = !schemaVersion;
4563 
4564     if (newDatabase) {
4565       // Set the page size first.
4566       if (kSQLitePageSizeOverride) {
4567         rv = connection->ExecuteSimpleSQL(
4568           nsPrintfCString("PRAGMA page_size = %lu;", kSQLitePageSizeOverride)
4569         );
4570         if (NS_WARN_IF(NS_FAILED(rv))) {
4571           return rv;
4572         }
4573       }
4574 
4575       // We have to set the auto_vacuum mode before opening a transaction.
4576       rv = connection->ExecuteSimpleSQL(
4577 #ifdef IDB_MOBILE
4578         // Turn on full auto_vacuum mode to reclaim disk space on mobile
4579         // devices (at the cost of some COMMIT speed).
4580         NS_LITERAL_CSTRING("PRAGMA auto_vacuum = FULL;")
4581 #else
4582         // Turn on incremental auto_vacuum mode on desktop builds.
4583         NS_LITERAL_CSTRING("PRAGMA auto_vacuum = INCREMENTAL;")
4584 #endif
4585       );
4586       if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
4587         // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
4588         // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
4589         rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
4590       }
4591       if (NS_WARN_IF(NS_FAILED(rv))) {
4592         return rv;
4593       }
4594 
4595       rv = SetJournalMode(connection);
4596       if (NS_WARN_IF(NS_FAILED(rv))) {
4597         return rv;
4598       }
4599 
4600       journalModeSet = true;
4601     } else {
4602 #ifdef DEBUG
4603     // Disable foreign key support while upgrading. This has to be done before
4604     // starting a transaction.
4605     MOZ_ALWAYS_SUCCEEDS(
4606       connection->ExecuteSimpleSQL(
4607         NS_LITERAL_CSTRING("PRAGMA foreign_keys = OFF;")));
4608 #endif
4609     }
4610 
4611     bool vacuumNeeded = false;
4612 
4613     mozStorageTransaction transaction(connection, false,
4614                                   mozIStorageConnection::TRANSACTION_IMMEDIATE);
4615 
4616     if (newDatabase) {
4617       rv = CreateTables(connection);
4618       if (NS_WARN_IF(NS_FAILED(rv))) {
4619         return rv;
4620       }
4621 
4622       MOZ_ASSERT(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)));
4623       MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
4624 
4625       nsCOMPtr<mozIStorageStatement> stmt;
4626       nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING(
4627         "INSERT INTO database (name, origin) "
4628         "VALUES (:name, :origin)"
4629       ), getter_AddRefs(stmt));
4630       if (NS_WARN_IF(NS_FAILED(rv))) {
4631         return rv;
4632       }
4633 
4634       rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
4635       if (NS_WARN_IF(NS_FAILED(rv))) {
4636         return rv;
4637       }
4638 
4639       rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), aOrigin);
4640       if (NS_WARN_IF(NS_FAILED(rv))) {
4641         return rv;
4642       }
4643 
4644       rv = stmt->Execute();
4645       if (NS_WARN_IF(NS_FAILED(rv))) {
4646         return rv;
4647       }
4648     } else  {
4649       // This logic needs to change next time we change the schema!
4650       static_assert(kSQLiteSchemaVersion == int32_t((25 << 4) + 0),
4651                     "Upgrade function needed due to schema version increase.");
4652 
4653       while (schemaVersion != kSQLiteSchemaVersion) {
4654         if (schemaVersion == 4) {
4655           rv = UpgradeSchemaFrom4To5(connection);
4656         } else if (schemaVersion == 5) {
4657           rv = UpgradeSchemaFrom5To6(connection);
4658         } else if (schemaVersion == 6) {
4659           rv = UpgradeSchemaFrom6To7(connection);
4660         } else if (schemaVersion == 7) {
4661           rv = UpgradeSchemaFrom7To8(connection);
4662         } else if (schemaVersion == 8) {
4663           rv = UpgradeSchemaFrom8To9_0(connection);
4664           vacuumNeeded = true;
4665         } else if (schemaVersion == MakeSchemaVersion(9, 0)) {
4666           rv = UpgradeSchemaFrom9_0To10_0(connection);
4667         } else if (schemaVersion == MakeSchemaVersion(10, 0)) {
4668           rv = UpgradeSchemaFrom10_0To11_0(connection);
4669         } else if (schemaVersion == MakeSchemaVersion(11, 0)) {
4670           rv = UpgradeSchemaFrom11_0To12_0(connection);
4671         } else if (schemaVersion == MakeSchemaVersion(12, 0)) {
4672           rv = UpgradeSchemaFrom12_0To13_0(connection, &vacuumNeeded);
4673         } else if (schemaVersion == MakeSchemaVersion(13, 0)) {
4674           rv = UpgradeSchemaFrom13_0To14_0(connection);
4675         } else if (schemaVersion == MakeSchemaVersion(14, 0)) {
4676           rv = UpgradeSchemaFrom14_0To15_0(connection);
4677         } else if (schemaVersion == MakeSchemaVersion(15, 0)) {
4678           rv = UpgradeSchemaFrom15_0To16_0(connection);
4679         } else if (schemaVersion == MakeSchemaVersion(16, 0)) {
4680           rv = UpgradeSchemaFrom16_0To17_0(connection);
4681         } else if (schemaVersion == MakeSchemaVersion(17, 0)) {
4682           rv = UpgradeSchemaFrom17_0To18_0(connection, aOrigin);
4683           vacuumNeeded = true;
4684         } else if (schemaVersion == MakeSchemaVersion(18, 0)) {
4685           rv = UpgradeSchemaFrom18_0To19_0(connection);
4686         } else if (schemaVersion == MakeSchemaVersion(19, 0)) {
4687           rv = UpgradeSchemaFrom19_0To20_0(aFMDirectory, connection);
4688         } else if (schemaVersion == MakeSchemaVersion(20, 0)) {
4689           rv = UpgradeSchemaFrom20_0To21_0(connection);
4690         } else if (schemaVersion == MakeSchemaVersion(21, 0)) {
4691           rv = UpgradeSchemaFrom21_0To22_0(connection);
4692         } else if (schemaVersion == MakeSchemaVersion(22, 0)) {
4693           rv = UpgradeSchemaFrom22_0To23_0(connection, aOrigin);
4694         } else if (schemaVersion == MakeSchemaVersion(23, 0)) {
4695           rv = UpgradeSchemaFrom23_0To24_0(connection);
4696         } else if (schemaVersion == MakeSchemaVersion(24, 0)) {
4697           rv = UpgradeSchemaFrom24_0To25_0(connection);
4698         } else {
4699           IDB_WARNING("Unable to open IndexedDB database, no upgrade path is "
4700                       "available!");
4701           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4702         }
4703 
4704         if (NS_WARN_IF(NS_FAILED(rv))) {
4705           return rv;
4706         }
4707 
4708         rv = connection->GetSchemaVersion(&schemaVersion);
4709         if (NS_WARN_IF(NS_FAILED(rv))) {
4710           return rv;
4711         }
4712       }
4713 
4714       MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
4715     }
4716 
4717     rv = transaction.Commit();
4718     if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
4719       // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
4720       // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
4721       rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
4722     }
4723     if (NS_WARN_IF(NS_FAILED(rv))) {
4724       return rv;
4725     }
4726 
4727 #ifdef DEBUG
4728     if (!newDatabase) {
4729       // Re-enable foreign key support after doing a foreign key check.
4730       nsCOMPtr<mozIStorageStatement> checkStmt;
4731       MOZ_ALWAYS_SUCCEEDS(
4732         connection->CreateStatement(
4733           NS_LITERAL_CSTRING("PRAGMA foreign_key_check;"),
4734           getter_AddRefs(checkStmt)));
4735 
4736       bool hasResult;
4737       MOZ_ALWAYS_SUCCEEDS(checkStmt->ExecuteStep(&hasResult));
4738       MOZ_ASSERT(!hasResult, "Database has inconsisistent foreign keys!");
4739 
4740       MOZ_ALWAYS_SUCCEEDS(
4741         connection->ExecuteSimpleSQL(
4742           NS_LITERAL_CSTRING("PRAGMA foreign_keys = OFF;")));
4743     }
4744 #endif
4745 
4746     if (kSQLitePageSizeOverride && !newDatabase) {
4747       nsCOMPtr<mozIStorageStatement> stmt;
4748       rv = connection->CreateStatement(NS_LITERAL_CSTRING(
4749         "PRAGMA page_size;"
4750       ), getter_AddRefs(stmt));
4751       if (NS_WARN_IF(NS_FAILED(rv))) {
4752         return rv;
4753       }
4754 
4755       bool hasResult;
4756       rv = stmt->ExecuteStep(&hasResult);
4757       if (NS_WARN_IF(NS_FAILED(rv))) {
4758         return rv;
4759       }
4760 
4761       MOZ_ASSERT(hasResult);
4762 
4763       int32_t pageSize;
4764       rv = stmt->GetInt32(0, &pageSize);
4765       if (NS_WARN_IF(NS_FAILED(rv))) {
4766         return rv;
4767       }
4768 
4769       MOZ_ASSERT(pageSize >= 512 && pageSize <= 65536);
4770 
4771       if (kSQLitePageSizeOverride != uint32_t(pageSize)) {
4772         // We must not be in WAL journal mode to change the page size.
4773         rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
4774           "PRAGMA journal_mode = DELETE;"
4775         ));
4776         if (NS_WARN_IF(NS_FAILED(rv))) {
4777           return rv;
4778         }
4779 
4780         rv = connection->CreateStatement(NS_LITERAL_CSTRING(
4781           "PRAGMA journal_mode;"
4782         ), getter_AddRefs(stmt));
4783         if (NS_WARN_IF(NS_FAILED(rv))) {
4784           return rv;
4785         }
4786 
4787         rv = stmt->ExecuteStep(&hasResult);
4788         if (NS_WARN_IF(NS_FAILED(rv))) {
4789           return rv;
4790         }
4791 
4792         MOZ_ASSERT(hasResult);
4793 
4794         nsCString journalMode;
4795         rv = stmt->GetUTF8String(0, journalMode);
4796         if (NS_WARN_IF(NS_FAILED(rv))) {
4797           return rv;
4798         }
4799 
4800         if (journalMode.EqualsLiteral("delete")) {
4801           // Successfully set to rollback journal mode so changing the page size
4802           // is possible with a VACUUM.
4803           rv = connection->ExecuteSimpleSQL(
4804             nsPrintfCString("PRAGMA page_size = %lu;", kSQLitePageSizeOverride)
4805           );
4806           if (NS_WARN_IF(NS_FAILED(rv))) {
4807             return rv;
4808           }
4809 
4810           // We will need to VACUUM in order to change the page size.
4811           vacuumNeeded = true;
4812         } else {
4813           NS_WARNING("Failed to set journal_mode for database, unable to "
4814                      "change the page size!");
4815         }
4816       }
4817     }
4818 
4819     if (vacuumNeeded) {
4820       rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;"));
4821       if (NS_WARN_IF(NS_FAILED(rv))) {
4822         return rv;
4823       }
4824     }
4825 
4826     if (newDatabase || vacuumNeeded) {
4827       if (journalModeSet) {
4828         // Make sure we checkpoint to get an accurate file size.
4829         rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
4830           "PRAGMA wal_checkpoint(FULL);"
4831         ));
4832         if (NS_WARN_IF(NS_FAILED(rv))) {
4833           return rv;
4834         }
4835       }
4836 
4837       int64_t fileSize;
4838       rv = aDBFile->GetFileSize(&fileSize);
4839       if (NS_WARN_IF(NS_FAILED(rv))) {
4840         return rv;
4841       }
4842 
4843       MOZ_ASSERT(fileSize > 0);
4844 
4845       PRTime vacuumTime = PR_Now();
4846       MOZ_ASSERT(vacuumTime);
4847 
4848       nsCOMPtr<mozIStorageStatement> vacuumTimeStmt;
4849       rv = connection->CreateStatement(NS_LITERAL_CSTRING(
4850         "UPDATE database "
4851           "SET last_vacuum_time = :time"
4852             ", last_vacuum_size = :size;"
4853       ), getter_AddRefs(vacuumTimeStmt));
4854       if (NS_WARN_IF(NS_FAILED(rv))) {
4855         return rv;
4856       }
4857 
4858       rv = vacuumTimeStmt->BindInt64ByName(NS_LITERAL_CSTRING("time"),
4859                                            vacuumTime);
4860       if (NS_WARN_IF(NS_FAILED(rv))) {
4861         return rv;
4862       }
4863 
4864       rv = vacuumTimeStmt->BindInt64ByName(NS_LITERAL_CSTRING("size"),
4865                                            fileSize);
4866       if (NS_WARN_IF(NS_FAILED(rv))) {
4867         return rv;
4868       }
4869 
4870       rv = vacuumTimeStmt->Execute();
4871       if (NS_WARN_IF(NS_FAILED(rv))) {
4872         return rv;
4873       }
4874     }
4875   }
4876 
4877   if (!journalModeSet) {
4878     rv = SetJournalMode(connection);
4879     if (NS_WARN_IF(NS_FAILED(rv))) {
4880       return rv;
4881     }
4882   }
4883 
4884   connection.forget(aConnection);
4885   return NS_OK;
4886 }
4887 
4888 already_AddRefed<nsIFile>
GetFileForPath(const nsAString & aPath)4889 GetFileForPath(const nsAString& aPath)
4890 {
4891   MOZ_ASSERT(!aPath.IsEmpty());
4892 
4893   nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
4894   if (NS_WARN_IF(!file)) {
4895     return nullptr;
4896   }
4897 
4898   if (NS_WARN_IF(NS_FAILED(file->InitWithPath(aPath)))) {
4899     return nullptr;
4900   }
4901 
4902   return file.forget();
4903 }
4904 
4905 nsresult
GetStorageConnection(nsIFile * aDatabaseFile,PersistenceType aPersistenceType,const nsACString & aGroup,const nsACString & aOrigin,uint32_t aTelemetryId,mozIStorageConnection ** aConnection)4906 GetStorageConnection(nsIFile* aDatabaseFile,
4907                      PersistenceType aPersistenceType,
4908                      const nsACString& aGroup,
4909                      const nsACString& aOrigin,
4910                      uint32_t aTelemetryId,
4911                      mozIStorageConnection** aConnection)
4912 {
4913   MOZ_ASSERT(!NS_IsMainThread());
4914   MOZ_ASSERT(!IsOnBackgroundThread());
4915   MOZ_ASSERT(aDatabaseFile);
4916   MOZ_ASSERT(aConnection);
4917 
4918   PROFILER_LABEL("IndexedDB",
4919                  "GetStorageConnection",
4920                  js::ProfileEntry::Category::STORAGE);
4921 
4922   bool exists;
4923   nsresult rv = aDatabaseFile->Exists(&exists);
4924   if (NS_WARN_IF(NS_FAILED(rv))) {
4925     return rv;
4926   }
4927 
4928   if (NS_WARN_IF(!exists)) {
4929     IDB_REPORT_INTERNAL_ERR();
4930     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4931   }
4932 
4933   nsCOMPtr<nsIFileURL> dbFileUrl;
4934   rv = GetDatabaseFileURL(aDatabaseFile,
4935                           aPersistenceType,
4936                           aGroup,
4937                           aOrigin,
4938                           aTelemetryId,
4939                           getter_AddRefs(dbFileUrl));
4940   if (NS_WARN_IF(NS_FAILED(rv))) {
4941     return rv;
4942   }
4943 
4944   nsCOMPtr<mozIStorageService> ss =
4945     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
4946   if (NS_WARN_IF(NS_FAILED(rv))) {
4947     return rv;
4948   }
4949 
4950   nsCOMPtr<mozIStorageConnection> connection;
4951   rv = OpenDatabaseAndHandleBusy(ss, dbFileUrl, getter_AddRefs(connection));
4952   if (NS_WARN_IF(NS_FAILED(rv))) {
4953     return rv;
4954   }
4955 
4956   rv = SetDefaultPragmas(connection);
4957   if (NS_WARN_IF(NS_FAILED(rv))) {
4958     return rv;
4959   }
4960 
4961   rv = SetJournalMode(connection);
4962   if (NS_WARN_IF(NS_FAILED(rv))) {
4963     return rv;
4964   }
4965 
4966   connection.forget(aConnection);
4967   return NS_OK;
4968 }
4969 
4970 nsresult
GetStorageConnection(const nsAString & aDatabaseFilePath,PersistenceType aPersistenceType,const nsACString & aGroup,const nsACString & aOrigin,uint32_t aTelemetryId,mozIStorageConnection ** aConnection)4971 GetStorageConnection(const nsAString& aDatabaseFilePath,
4972                      PersistenceType aPersistenceType,
4973                      const nsACString& aGroup,
4974                      const nsACString& aOrigin,
4975                      uint32_t aTelemetryId,
4976                      mozIStorageConnection** aConnection)
4977 {
4978   MOZ_ASSERT(!NS_IsMainThread());
4979   MOZ_ASSERT(!IsOnBackgroundThread());
4980   MOZ_ASSERT(!aDatabaseFilePath.IsEmpty());
4981   MOZ_ASSERT(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")));
4982   MOZ_ASSERT(aConnection);
4983 
4984   nsCOMPtr<nsIFile> dbFile = GetFileForPath(aDatabaseFilePath);
4985   if (NS_WARN_IF(!dbFile)) {
4986     IDB_REPORT_INTERNAL_ERR();
4987     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4988   }
4989 
4990   return GetStorageConnection(dbFile,
4991                               aPersistenceType,
4992                               aGroup,
4993                               aOrigin,
4994                               aTelemetryId,
4995                               aConnection);
4996 }
4997 
4998 /*******************************************************************************
4999  * ConnectionPool declarations
5000  ******************************************************************************/
5001 
5002 class DatabaseConnection final
5003 {
5004   friend class ConnectionPool;
5005 
5006   enum class CheckpointMode
5007   {
5008     Full,
5009     Restart,
5010     Truncate
5011   };
5012 
5013 public:
5014   class AutoSavepoint;
5015   class CachedStatement;
5016   class UpdateRefcountFunction;
5017 
5018 private:
5019   nsCOMPtr<mozIStorageConnection> mStorageConnection;
5020   RefPtr<FileManager> mFileManager;
5021   nsInterfaceHashtable<nsCStringHashKey, mozIStorageStatement>
5022     mCachedStatements;
5023   RefPtr<UpdateRefcountFunction> mUpdateRefcountFunction;
5024   RefPtr<QuotaObject> mQuotaObject;
5025   RefPtr<QuotaObject> mJournalQuotaObject;
5026   bool mInReadTransaction;
5027   bool mInWriteTransaction;
5028 
5029 #ifdef DEBUG
5030   uint32_t mDEBUGSavepointCount;
5031   PRThread* mDEBUGThread;
5032 #endif
5033 
5034 public:
5035   void
AssertIsOnConnectionThread() const5036   AssertIsOnConnectionThread() const
5037   {
5038     MOZ_ASSERT(mDEBUGThread);
5039     MOZ_ASSERT(PR_GetCurrentThread() == mDEBUGThread);
5040   }
5041 
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DatabaseConnection)5042   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DatabaseConnection)
5043 
5044   mozIStorageConnection*
5045   GetStorageConnection() const
5046   {
5047     if (mStorageConnection) {
5048       AssertIsOnConnectionThread();
5049       return mStorageConnection;
5050     }
5051 
5052     return nullptr;
5053   }
5054 
5055   UpdateRefcountFunction*
GetUpdateRefcountFunction() const5056   GetUpdateRefcountFunction() const
5057   {
5058     AssertIsOnConnectionThread();
5059 
5060     return mUpdateRefcountFunction;
5061   }
5062 
5063   nsresult
5064   GetCachedStatement(const nsACString& aQuery,
5065                      CachedStatement* aCachedStatement);
5066 
5067   nsresult
5068   BeginWriteTransaction();
5069 
5070   nsresult
5071   CommitWriteTransaction();
5072 
5073   void
5074   RollbackWriteTransaction();
5075 
5076   void
5077   FinishWriteTransaction();
5078 
5079   nsresult
5080   StartSavepoint();
5081 
5082   nsresult
5083   ReleaseSavepoint();
5084 
5085   nsresult
5086   RollbackSavepoint();
5087 
5088   nsresult
Checkpoint()5089   Checkpoint()
5090   {
5091     AssertIsOnConnectionThread();
5092 
5093     return CheckpointInternal(CheckpointMode::Full);
5094   }
5095 
5096   void
5097   DoIdleProcessing(bool aNeedsCheckpoint);
5098 
5099   void
5100   Close();
5101 
5102   nsresult
5103   DisableQuotaChecks();
5104 
5105   void
5106   EnableQuotaChecks();
5107 
5108 private:
5109   DatabaseConnection(mozIStorageConnection* aStorageConnection,
5110                      FileManager* aFileManager);
5111 
5112   ~DatabaseConnection();
5113 
5114   nsresult
5115   Init();
5116 
5117   nsresult
5118   CheckpointInternal(CheckpointMode aMode);
5119 
5120   nsresult
5121   GetFreelistCount(CachedStatement& aCachedStatement, uint32_t* aFreelistCount);
5122 
5123   nsresult
5124   ReclaimFreePagesWhileIdle(CachedStatement& aFreelistStatement,
5125                             CachedStatement& aRollbackStatement,
5126                             uint32_t aFreelistCount,
5127                             bool aNeedsCheckpoint,
5128                             bool* aFreedSomePages);
5129 
5130   nsresult
5131   GetFileSize(const nsAString& aPath, int64_t* aResult);
5132 };
5133 
5134 class MOZ_STACK_CLASS DatabaseConnection::AutoSavepoint final
5135 {
5136   DatabaseConnection* mConnection;
5137 #ifdef DEBUG
5138   const TransactionBase* mDEBUGTransaction;
5139 #endif
5140 
5141 public:
5142   AutoSavepoint();
5143   ~AutoSavepoint();
5144 
5145   nsresult
5146   Start(const TransactionBase* aConnection);
5147 
5148   nsresult
5149   Commit();
5150 };
5151 
5152 class DatabaseConnection::CachedStatement final
5153 {
5154   friend class DatabaseConnection;
5155 
5156   nsCOMPtr<mozIStorageStatement> mStatement;
5157   Maybe<mozStorageStatementScoper> mScoper;
5158 
5159 #ifdef DEBUG
5160   DatabaseConnection* mDEBUGConnection;
5161 #endif
5162 
5163 public:
5164   CachedStatement();
5165   ~CachedStatement();
5166 
5167   void
AssertIsOnConnectionThread() const5168   AssertIsOnConnectionThread() const
5169   {
5170 #ifdef DEBUG
5171     if (mDEBUGConnection) {
5172       mDEBUGConnection->AssertIsOnConnectionThread();
5173     }
5174     MOZ_ASSERT(!NS_IsMainThread());
5175     MOZ_ASSERT(!IsOnBackgroundThread());
5176 #endif
5177   }
5178 
5179   operator mozIStorageStatement*() const;
5180 
5181   mozIStorageStatement*
5182   operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN;
5183 
5184   void
5185   Reset();
5186 
5187 private:
5188   // Only called by DatabaseConnection.
5189   void
5190   Assign(DatabaseConnection* aConnection,
5191          already_AddRefed<mozIStorageStatement> aStatement);
5192 
5193   // No funny business allowed.
5194   CachedStatement(const CachedStatement&) = delete;
5195   CachedStatement& operator=(const CachedStatement&) = delete;
5196 };
5197 
5198 class DatabaseConnection::UpdateRefcountFunction final
5199   : public mozIStorageFunction
5200 {
5201   class DatabaseUpdateFunction;
5202   class FileInfoEntry;
5203 
5204   enum class UpdateType
5205   {
5206     Increment,
5207     Decrement
5208   };
5209 
5210   DatabaseConnection* mConnection;
5211   FileManager* mFileManager;
5212   nsClassHashtable<nsUint64HashKey, FileInfoEntry> mFileInfoEntries;
5213   nsDataHashtable<nsUint64HashKey, FileInfoEntry*> mSavepointEntriesIndex;
5214 
5215   nsTArray<int64_t> mJournalsToCreateBeforeCommit;
5216   nsTArray<int64_t> mJournalsToRemoveAfterCommit;
5217   nsTArray<int64_t> mJournalsToRemoveAfterAbort;
5218 
5219   bool mInSavepoint;
5220 
5221 public:
5222   NS_DECL_ISUPPORTS
5223   NS_DECL_MOZISTORAGEFUNCTION
5224 
5225   UpdateRefcountFunction(DatabaseConnection* aConnection,
5226                          FileManager* aFileManager);
5227 
5228   nsresult
5229   WillCommit();
5230 
5231   void
5232   DidCommit();
5233 
5234   void
5235   DidAbort();
5236 
5237   void
5238   StartSavepoint();
5239 
5240   void
5241   ReleaseSavepoint();
5242 
5243   void
5244   RollbackSavepoint();
5245 
5246   void
5247   Reset();
5248 
5249 private:
~UpdateRefcountFunction()5250   ~UpdateRefcountFunction()
5251   { }
5252 
5253   nsresult
5254   ProcessValue(mozIStorageValueArray* aValues,
5255                int32_t aIndex,
5256                UpdateType aUpdateType);
5257 
5258   nsresult
5259   CreateJournals();
5260 
5261   nsresult
5262   RemoveJournals(const nsTArray<int64_t>& aJournals);
5263 };
5264 
5265 class DatabaseConnection::UpdateRefcountFunction::DatabaseUpdateFunction final
5266 {
5267   CachedStatement mUpdateStatement;
5268   CachedStatement mSelectStatement;
5269   CachedStatement mInsertStatement;
5270 
5271   UpdateRefcountFunction* mFunction;
5272 
5273   nsresult mErrorCode;
5274 
5275 public:
5276   explicit
DatabaseUpdateFunction(UpdateRefcountFunction * aFunction)5277   DatabaseUpdateFunction(UpdateRefcountFunction* aFunction)
5278     : mFunction(aFunction)
5279     , mErrorCode(NS_OK)
5280   {
5281     MOZ_COUNT_CTOR(
5282       DatabaseConnection::UpdateRefcountFunction::DatabaseUpdateFunction);
5283   }
5284 
~DatabaseUpdateFunction()5285   ~DatabaseUpdateFunction()
5286   {
5287     MOZ_COUNT_DTOR(
5288       DatabaseConnection::UpdateRefcountFunction::DatabaseUpdateFunction);
5289   }
5290 
5291   bool
5292   Update(int64_t aId, int32_t aDelta);
5293 
5294   nsresult
ErrorCode() const5295   ErrorCode() const
5296   {
5297     return mErrorCode;
5298   }
5299 
5300 private:
5301   nsresult
5302   UpdateInternal(int64_t aId, int32_t aDelta);
5303 };
5304 
5305 class DatabaseConnection::UpdateRefcountFunction::FileInfoEntry final
5306 {
5307   friend class UpdateRefcountFunction;
5308 
5309   RefPtr<FileInfo> mFileInfo;
5310   int32_t mDelta;
5311   int32_t mSavepointDelta;
5312 
5313 public:
5314   explicit
FileInfoEntry(FileInfo * aFileInfo)5315   FileInfoEntry(FileInfo* aFileInfo)
5316     : mFileInfo(aFileInfo)
5317     , mDelta(0)
5318     , mSavepointDelta(0)
5319   {
5320     MOZ_COUNT_CTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry);
5321   }
5322 
~FileInfoEntry()5323   ~FileInfoEntry()
5324   {
5325     MOZ_COUNT_DTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry);
5326   }
5327 };
5328 
5329 class ConnectionPool final
5330 {
5331 public:
5332   class FinishCallback;
5333 
5334 private:
5335   class ConnectionRunnable;
5336   class CloseConnectionRunnable;
5337   struct DatabaseInfo;
5338   struct DatabasesCompleteCallback;
5339   class FinishCallbackWrapper;
5340   class IdleConnectionRunnable;
5341   struct IdleDatabaseInfo;
5342   struct IdleResource;
5343   struct IdleThreadInfo;
5344   struct ThreadInfo;
5345   class ThreadRunnable;
5346   class TransactionInfo;
5347   struct TransactionInfoPair;
5348 
5349   // This mutex guards mDatabases, see below.
5350   Mutex mDatabasesMutex;
5351 
5352   nsTArray<IdleThreadInfo> mIdleThreads;
5353   nsTArray<IdleDatabaseInfo> mIdleDatabases;
5354   nsTArray<DatabaseInfo*> mDatabasesPerformingIdleMaintenance;
5355   nsCOMPtr<nsITimer> mIdleTimer;
5356   TimeStamp mTargetIdleTime;
5357 
5358   // Only modifed on the owning thread, but read on multiple threads. Therefore
5359   // all modifications and all reads off the owning thread must be protected by
5360   // mDatabasesMutex.
5361   nsClassHashtable<nsCStringHashKey, DatabaseInfo> mDatabases;
5362 
5363   nsClassHashtable<nsUint64HashKey, TransactionInfo> mTransactions;
5364   nsTArray<TransactionInfo*> mQueuedTransactions;
5365 
5366   nsTArray<nsAutoPtr<DatabasesCompleteCallback>> mCompleteCallbacks;
5367 
5368   uint64_t mNextTransactionId;
5369   uint32_t mTotalThreadCount;
5370   bool mShutdownRequested;
5371   bool mShutdownComplete;
5372 
5373 #ifdef DEBUG
5374   PRThread* mDEBUGOwningThread;
5375 #endif
5376 
5377 public:
5378   ConnectionPool();
5379 
5380   void
5381   AssertIsOnOwningThread() const
5382 #ifdef DEBUG
5383   ;
5384 #else
5385   { }
5386 #endif
5387 
5388   nsresult
5389   GetOrCreateConnection(const Database* aDatabase,
5390                         DatabaseConnection** aConnection);
5391 
5392   uint64_t
5393   Start(const nsID& aBackgroundChildLoggingId,
5394         const nsACString& aDatabaseId,
5395         int64_t aLoggingSerialNumber,
5396         const nsTArray<nsString>& aObjectStoreNames,
5397         bool aIsWriteTransaction,
5398         TransactionDatabaseOperationBase* aTransactionOp);
5399 
5400   void
5401   Dispatch(uint64_t aTransactionId, nsIRunnable* aRunnable);
5402 
5403   void
5404   Finish(uint64_t aTransactionId, FinishCallback* aCallback);
5405 
5406   void
CloseDatabaseWhenIdle(const nsACString & aDatabaseId)5407   CloseDatabaseWhenIdle(const nsACString& aDatabaseId)
5408   {
5409     Unused << CloseDatabaseWhenIdleInternal(aDatabaseId);
5410   }
5411 
5412   void
5413   WaitForDatabasesToComplete(nsTArray<nsCString>&& aDatabaseIds,
5414                              nsIRunnable* aCallback);
5415 
5416   void
5417   Shutdown();
5418 
5419   NS_INLINE_DECL_REFCOUNTING(ConnectionPool)
5420 
5421 private:
5422   ~ConnectionPool();
5423 
5424   static void
5425   IdleTimerCallback(nsITimer* aTimer, void* aClosure);
5426 
5427   void
5428   Cleanup();
5429 
5430   void
5431   AdjustIdleTimer();
5432 
5433   void
5434   CancelIdleTimer();
5435 
5436   void
5437   ShutdownThread(ThreadInfo& aThreadInfo);
5438 
5439   void
5440   CloseIdleDatabases();
5441 
5442   void
5443   ShutdownIdleThreads();
5444 
5445   bool
5446   ScheduleTransaction(TransactionInfo* aTransactionInfo,
5447                       bool aFromQueuedTransactions);
5448 
5449   void
5450   NoteFinishedTransaction(uint64_t aTransactionId);
5451 
5452   void
5453   ScheduleQueuedTransactions(ThreadInfo& aThreadInfo);
5454 
5455   void
5456   NoteIdleDatabase(DatabaseInfo* aDatabaseInfo);
5457 
5458   void
5459   NoteClosedDatabase(DatabaseInfo* aDatabaseInfo);
5460 
5461   bool
5462   MaybeFireCallback(DatabasesCompleteCallback* aCallback);
5463 
5464   void
5465   PerformIdleDatabaseMaintenance(DatabaseInfo* aDatabaseInfo);
5466 
5467   void
5468   CloseDatabase(DatabaseInfo* aDatabaseInfo);
5469 
5470   bool
5471   CloseDatabaseWhenIdleInternal(const nsACString& aDatabaseId);
5472 };
5473 
5474 class ConnectionPool::ConnectionRunnable
5475   : public Runnable
5476 {
5477 protected:
5478   DatabaseInfo* mDatabaseInfo;
5479   nsCOMPtr<nsIEventTarget> mOwningThread;
5480 
5481   explicit
5482   ConnectionRunnable(DatabaseInfo* aDatabaseInfo);
5483 
5484   virtual
~ConnectionRunnable()5485   ~ConnectionRunnable()
5486   { }
5487 };
5488 
5489 class ConnectionPool::IdleConnectionRunnable final
5490   : public ConnectionRunnable
5491 {
5492   bool mNeedsCheckpoint;
5493 
5494 public:
IdleConnectionRunnable(DatabaseInfo * aDatabaseInfo,bool aNeedsCheckpoint)5495   IdleConnectionRunnable(DatabaseInfo* aDatabaseInfo, bool aNeedsCheckpoint)
5496     : ConnectionRunnable(aDatabaseInfo)
5497     , mNeedsCheckpoint(aNeedsCheckpoint)
5498   { }
5499 
5500   NS_DECL_ISUPPORTS_INHERITED
5501 
5502 private:
~IdleConnectionRunnable()5503   ~IdleConnectionRunnable()
5504   { }
5505 
5506   NS_DECL_NSIRUNNABLE
5507 };
5508 
5509 class ConnectionPool::CloseConnectionRunnable final
5510   : public ConnectionRunnable
5511 {
5512 public:
5513   explicit
CloseConnectionRunnable(DatabaseInfo * aDatabaseInfo)5514   CloseConnectionRunnable(DatabaseInfo* aDatabaseInfo)
5515     : ConnectionRunnable(aDatabaseInfo)
5516   { }
5517 
5518   NS_DECL_ISUPPORTS_INHERITED
5519 
5520 private:
~CloseConnectionRunnable()5521   ~CloseConnectionRunnable()
5522   { }
5523 
5524   NS_DECL_NSIRUNNABLE
5525 };
5526 
5527 struct ConnectionPool::ThreadInfo
5528 {
5529   nsCOMPtr<nsIThread> mThread;
5530   RefPtr<ThreadRunnable> mRunnable;
5531 
5532   ThreadInfo();
5533 
5534   explicit
5535   ThreadInfo(const ThreadInfo& aOther);
5536 
5537   ~ThreadInfo();
5538 };
5539 
5540 struct ConnectionPool::DatabaseInfo final
5541 {
5542   friend class nsAutoPtr<DatabaseInfo>;
5543 
5544   RefPtr<ConnectionPool> mConnectionPool;
5545   const nsCString mDatabaseId;
5546   RefPtr<DatabaseConnection> mConnection;
5547   nsClassHashtable<nsStringHashKey, TransactionInfoPair> mBlockingTransactions;
5548   nsTArray<TransactionInfo*> mTransactionsScheduledDuringClose;
5549   nsTArray<TransactionInfo*> mScheduledWriteTransactions;
5550   TransactionInfo* mRunningWriteTransaction;
5551   ThreadInfo mThreadInfo;
5552   uint32_t mReadTransactionCount;
5553   uint32_t mWriteTransactionCount;
5554   bool mNeedsCheckpoint;
5555   bool mIdle;
5556   bool mCloseOnIdle;
5557   bool mClosing;
5558 
5559 #ifdef DEBUG
5560   PRThread* mDEBUGConnectionThread;
5561 #endif
5562 
5563   DatabaseInfo(ConnectionPool* aConnectionPool,
5564                const nsACString& aDatabaseId);
5565 
5566   void
AssertIsOnConnectionThreadmozilla::dom::indexedDB::__anone276c2b20111::ConnectionPool::DatabaseInfo5567   AssertIsOnConnectionThread() const
5568   {
5569     MOZ_ASSERT(mDEBUGConnectionThread);
5570     MOZ_ASSERT(PR_GetCurrentThread() == mDEBUGConnectionThread);
5571   }
5572 
5573   uint64_t
TotalTransactionCountmozilla::dom::indexedDB::__anone276c2b20111::ConnectionPool::DatabaseInfo5574   TotalTransactionCount() const
5575   {
5576     return mReadTransactionCount + mWriteTransactionCount;
5577   }
5578 
5579 private:
5580   ~DatabaseInfo();
5581 
5582   DatabaseInfo(const DatabaseInfo&) = delete;
5583   DatabaseInfo& operator=(const DatabaseInfo&) = delete;
5584 };
5585 
5586 struct ConnectionPool::DatabasesCompleteCallback final
5587 {
5588   friend class nsAutoPtr<DatabasesCompleteCallback>;
5589 
5590   nsTArray<nsCString> mDatabaseIds;
5591   nsCOMPtr<nsIRunnable> mCallback;
5592 
5593   DatabasesCompleteCallback(nsTArray<nsCString>&& aDatabaseIds,
5594                             nsIRunnable* aCallback);
5595 
5596 private:
5597   ~DatabasesCompleteCallback();
5598 };
5599 
5600 class NS_NO_VTABLE ConnectionPool::FinishCallback
5601   : public nsIRunnable
5602 {
5603 public:
5604   // Called on the owning thread before any additional transactions are
5605   // unblocked.
5606   virtual void
5607   TransactionFinishedBeforeUnblock() = 0;
5608 
5609   // Called on the owning thread after additional transactions may have been
5610   // unblocked.
5611   virtual void
5612   TransactionFinishedAfterUnblock() = 0;
5613 
5614 protected:
FinishCallback()5615   FinishCallback()
5616   { }
5617 
~FinishCallback()5618   virtual ~FinishCallback()
5619   { }
5620 };
5621 
5622 class ConnectionPool::FinishCallbackWrapper final
5623   : public Runnable
5624 {
5625   RefPtr<ConnectionPool> mConnectionPool;
5626   RefPtr<FinishCallback> mCallback;
5627   nsCOMPtr<nsIEventTarget> mOwningThread;
5628   uint64_t mTransactionId;
5629   bool mHasRunOnce;
5630 
5631 public:
5632   FinishCallbackWrapper(ConnectionPool* aConnectionPool,
5633                         uint64_t aTransactionId,
5634                         FinishCallback* aCallback);
5635 
5636   NS_DECL_ISUPPORTS_INHERITED
5637 
5638 private:
5639   ~FinishCallbackWrapper();
5640 
5641   NS_DECL_NSIRUNNABLE
5642 };
5643 
5644 struct ConnectionPool::IdleResource
5645 {
5646   TimeStamp mIdleTime;
5647 
5648 protected:
5649   explicit
5650   IdleResource(const TimeStamp& aIdleTime);
5651 
5652   explicit
5653   IdleResource(const IdleResource& aOther) = delete;
5654 
5655   ~IdleResource();
5656 };
5657 
5658 struct ConnectionPool::IdleDatabaseInfo final
5659   : public IdleResource
5660 {
5661   DatabaseInfo* mDatabaseInfo;
5662 
5663 public:
5664   MOZ_IMPLICIT
5665   IdleDatabaseInfo(DatabaseInfo* aDatabaseInfo);
5666 
5667   explicit
5668   IdleDatabaseInfo(const IdleDatabaseInfo& aOther) = delete;
5669 
5670   ~IdleDatabaseInfo();
5671 
5672   bool
operator ==mozilla::dom::indexedDB::__anone276c2b20111::ConnectionPool::IdleDatabaseInfo5673   operator==(const IdleDatabaseInfo& aOther) const
5674   {
5675     return mDatabaseInfo == aOther.mDatabaseInfo;
5676   }
5677 
5678   bool
operator <mozilla::dom::indexedDB::__anone276c2b20111::ConnectionPool::IdleDatabaseInfo5679   operator<(const IdleDatabaseInfo& aOther) const
5680   {
5681     return mIdleTime < aOther.mIdleTime;
5682   }
5683 };
5684 
5685 struct ConnectionPool::IdleThreadInfo final
5686   : public IdleResource
5687 {
5688   ThreadInfo mThreadInfo;
5689 
5690 public:
5691   // Boo, this is needed because nsTArray::InsertElementSorted() doesn't yet
5692   // work with rvalue references.
5693   MOZ_IMPLICIT
5694   IdleThreadInfo(const ThreadInfo& aThreadInfo);
5695 
5696   explicit
5697   IdleThreadInfo(const IdleThreadInfo& aOther) = delete;
5698 
5699   ~IdleThreadInfo();
5700 
5701   bool
operator ==mozilla::dom::indexedDB::__anone276c2b20111::ConnectionPool::IdleThreadInfo5702   operator==(const IdleThreadInfo& aOther) const
5703   {
5704     return mThreadInfo.mRunnable == aOther.mThreadInfo.mRunnable &&
5705            mThreadInfo.mThread == aOther.mThreadInfo.mThread;
5706   }
5707 
5708   bool
operator <mozilla::dom::indexedDB::__anone276c2b20111::ConnectionPool::IdleThreadInfo5709   operator<(const IdleThreadInfo& aOther) const
5710   {
5711     return mIdleTime < aOther.mIdleTime;
5712   }
5713 };
5714 
5715 class ConnectionPool::ThreadRunnable final
5716   : public Runnable
5717 {
5718   // Only touched on the background thread.
5719   static uint32_t sNextSerialNumber;
5720 
5721   // Set at construction for logging.
5722   const uint32_t mSerialNumber;
5723 
5724   // These two values are only modified on the connection thread.
5725   bool mFirstRun;
5726   bool mContinueRunning;
5727 
5728 public:
5729   ThreadRunnable();
5730 
5731   NS_DECL_ISUPPORTS_INHERITED
5732 
5733   uint32_t
SerialNumber() const5734   SerialNumber() const
5735   {
5736     return mSerialNumber;
5737   }
5738 
5739 private:
5740   ~ThreadRunnable();
5741 
5742   NS_DECL_NSIRUNNABLE
5743 };
5744 
5745 class ConnectionPool::TransactionInfo final
5746 {
5747   friend class nsAutoPtr<TransactionInfo>;
5748 
5749   nsTHashtable<nsPtrHashKey<TransactionInfo>> mBlocking;
5750   nsTArray<TransactionInfo*> mBlockingOrdered;
5751 
5752 public:
5753   DatabaseInfo* mDatabaseInfo;
5754   const nsID mBackgroundChildLoggingId;
5755   const nsCString mDatabaseId;
5756   const uint64_t mTransactionId;
5757   const int64_t mLoggingSerialNumber;
5758   const nsTArray<nsString> mObjectStoreNames;
5759   nsTHashtable<nsPtrHashKey<TransactionInfo>> mBlockedOn;
5760   nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
5761   const bool mIsWriteTransaction;
5762   bool mRunning;
5763 
5764 #ifdef DEBUG
5765   bool mFinished;
5766 #endif
5767 
5768   TransactionInfo(DatabaseInfo* aDatabaseInfo,
5769                   const nsID& aBackgroundChildLoggingId,
5770                   const nsACString& aDatabaseId,
5771                   uint64_t aTransactionId,
5772                   int64_t aLoggingSerialNumber,
5773                   const nsTArray<nsString>& aObjectStoreNames,
5774                   bool aIsWriteTransaction,
5775                   TransactionDatabaseOperationBase* aTransactionOp);
5776 
5777   void
5778   AddBlockingTransaction(TransactionInfo* aTransactionInfo);
5779 
5780   void
5781   RemoveBlockingTransactions();
5782 
5783 private:
5784   ~TransactionInfo();
5785 
5786   void
5787   MaybeUnblock(TransactionInfo* aTransactionInfo);
5788 };
5789 
5790 struct ConnectionPool::TransactionInfoPair final
5791 {
5792   friend class nsAutoPtr<TransactionInfoPair>;
5793 
5794   // Multiple reading transactions can block future writes.
5795   nsTArray<TransactionInfo*> mLastBlockingWrites;
5796   // But only a single writing transaction can block future reads.
5797   TransactionInfo* mLastBlockingReads;
5798 
5799   TransactionInfoPair();
5800 
5801 private:
5802   ~TransactionInfoPair();
5803 };
5804 
5805 /*******************************************************************************
5806  * Actor class declarations
5807  ******************************************************************************/
5808 
5809 class DatabaseOperationBase
5810   : public Runnable
5811   , public mozIStorageProgressHandler
5812 {
5813   friend class UpgradeFileIdsFunction;
5814 
5815 protected:
5816   class AutoSetProgressHandler;
5817 
5818   typedef nsDataHashtable<nsUint64HashKey, bool> UniqueIndexTable;
5819 
5820   nsCOMPtr<nsIEventTarget> mOwningThread;
5821   const nsID mBackgroundChildLoggingId;
5822   const uint64_t mLoggingSerialNumber;
5823   nsresult mResultCode;
5824 
5825 private:
5826   Atomic<bool> mOperationMayProceed;
5827   bool mActorDestroyed;
5828 
5829 public:
5830   NS_DECL_ISUPPORTS_INHERITED
5831 
5832   bool
IsOnOwningThread() const5833   IsOnOwningThread() const
5834   {
5835     MOZ_ASSERT(mOwningThread);
5836 
5837     bool current;
5838     return NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)) && current;
5839   }
5840 
5841   void
AssertIsOnOwningThread() const5842   AssertIsOnOwningThread() const
5843   {
5844     MOZ_ASSERT(IsOnBackgroundThread());
5845     MOZ_ASSERT(IsOnOwningThread());
5846   }
5847 
5848   void
NoteActorDestroyed()5849   NoteActorDestroyed()
5850   {
5851     AssertIsOnOwningThread();
5852 
5853     mActorDestroyed = true;
5854     mOperationMayProceed = false;
5855   }
5856 
5857   bool
IsActorDestroyed() const5858   IsActorDestroyed() const
5859   {
5860     AssertIsOnOwningThread();
5861 
5862     return mActorDestroyed;
5863   }
5864 
5865   // May be called on any thread, but you should call IsActorDestroyed() if
5866   // you know you're on the background thread because it is slightly faster.
5867   bool
OperationMayProceed() const5868   OperationMayProceed() const
5869   {
5870     return mOperationMayProceed;
5871   }
5872 
5873   const nsID&
BackgroundChildLoggingId() const5874   BackgroundChildLoggingId() const
5875   {
5876     return mBackgroundChildLoggingId;
5877   }
5878 
5879   uint64_t
LoggingSerialNumber() const5880   LoggingSerialNumber() const
5881   {
5882     return mLoggingSerialNumber;
5883   }
5884 
5885   nsresult
ResultCode() const5886   ResultCode() const
5887   {
5888     return mResultCode;
5889   }
5890 
5891   void
SetFailureCode(nsresult aErrorCode)5892   SetFailureCode(nsresult aErrorCode)
5893   {
5894     MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
5895     MOZ_ASSERT(NS_FAILED(aErrorCode));
5896 
5897     mResultCode = aErrorCode;
5898   }
5899 
5900 protected:
DatabaseOperationBase(const nsID & aBackgroundChildLoggingId,uint64_t aLoggingSerialNumber)5901   DatabaseOperationBase(const nsID& aBackgroundChildLoggingId,
5902                         uint64_t aLoggingSerialNumber)
5903     : mOwningThread(NS_GetCurrentThread())
5904     , mBackgroundChildLoggingId(aBackgroundChildLoggingId)
5905     , mLoggingSerialNumber(aLoggingSerialNumber)
5906     , mResultCode(NS_OK)
5907     , mOperationMayProceed(true)
5908     , mActorDestroyed(false)
5909   {
5910     AssertIsOnOwningThread();
5911   }
5912 
5913   virtual
~DatabaseOperationBase()5914   ~DatabaseOperationBase()
5915   {
5916     MOZ_ASSERT(mActorDestroyed);
5917   }
5918 
5919   static void
5920   GetBindingClauseForKeyRange(const SerializedKeyRange& aKeyRange,
5921                               const nsACString& aKeyColumnName,
5922                               nsAutoCString& aBindingClause);
5923 
5924   static uint64_t
5925   ReinterpretDoubleAsUInt64(double aDouble);
5926 
5927   static nsresult
GetStructuredCloneReadInfoFromStatement(mozIStorageStatement * aStatement,uint32_t aDataIndex,uint32_t aFileIdsIndex,FileManager * aFileManager,StructuredCloneReadInfo * aInfo)5928   GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement,
5929                                           uint32_t aDataIndex,
5930                                           uint32_t aFileIdsIndex,
5931                                           FileManager* aFileManager,
5932                                           StructuredCloneReadInfo* aInfo)
5933   {
5934     return GetStructuredCloneReadInfoFromSource(aStatement,
5935                                                 aDataIndex,
5936                                                 aFileIdsIndex,
5937                                                 aFileManager,
5938                                                 aInfo);
5939   }
5940 
5941   static nsresult
GetStructuredCloneReadInfoFromValueArray(mozIStorageValueArray * aValues,uint32_t aDataIndex,uint32_t aFileIdsIndex,FileManager * aFileManager,StructuredCloneReadInfo * aInfo)5942   GetStructuredCloneReadInfoFromValueArray(mozIStorageValueArray* aValues,
5943                                            uint32_t aDataIndex,
5944                                            uint32_t aFileIdsIndex,
5945                                            FileManager* aFileManager,
5946                                            StructuredCloneReadInfo* aInfo)
5947   {
5948     return GetStructuredCloneReadInfoFromSource(aValues,
5949                                                 aDataIndex,
5950                                                 aFileIdsIndex,
5951                                                 aFileManager,
5952                                                 aInfo);
5953   }
5954 
5955   static nsresult
5956   BindKeyRangeToStatement(const SerializedKeyRange& aKeyRange,
5957                           mozIStorageStatement* aStatement);
5958 
5959   static nsresult
5960   BindKeyRangeToStatement(const SerializedKeyRange& aKeyRange,
5961                           mozIStorageStatement* aStatement,
5962                           const nsCString& aLocale);
5963 
5964   static void
5965   AppendConditionClause(const nsACString& aColumnName,
5966                         const nsACString& aArgName,
5967                         bool aLessThan,
5968                         bool aEquals,
5969                         nsAutoCString& aResult);
5970 
5971   static nsresult
5972   GetUniqueIndexTableForObjectStore(
5973                                TransactionBase* aTransaction,
5974                                int64_t aObjectStoreId,
5975                                Maybe<UniqueIndexTable>& aMaybeUniqueIndexTable);
5976 
5977   static nsresult
5978   IndexDataValuesFromUpdateInfos(const nsTArray<IndexUpdateInfo>& aUpdateInfos,
5979                                  const UniqueIndexTable& aUniqueIndexTable,
5980                                  nsTArray<IndexDataValue>& aIndexValues);
5981 
5982   static nsresult
5983   InsertIndexTableRows(DatabaseConnection* aConnection,
5984                        const int64_t aObjectStoreId,
5985                        const Key& aObjectStoreKey,
5986                        const FallibleTArray<IndexDataValue>& aIndexValues);
5987 
5988   static nsresult
5989   DeleteIndexDataTableRows(DatabaseConnection* aConnection,
5990                            const Key& aObjectStoreKey,
5991                            const FallibleTArray<IndexDataValue>& aIndexValues);
5992 
5993   static nsresult
5994   DeleteObjectStoreDataTableRowsWithIndexes(DatabaseConnection* aConnection,
5995                                             const int64_t aObjectStoreId,
5996                                             const OptionalKeyRange& aKeyRange);
5997 
5998   static nsresult
5999   UpdateIndexValues(DatabaseConnection* aConnection,
6000                     const int64_t aObjectStoreId,
6001                     const Key& aObjectStoreKey,
6002                     const FallibleTArray<IndexDataValue>& aIndexValues);
6003 
6004   static nsresult
6005   ObjectStoreHasIndexes(DatabaseConnection* aConnection,
6006                         const int64_t aObjectStoreId,
6007                         bool* aHasIndexes);
6008 
6009 private:
6010   template <typename T>
6011   static nsresult
6012   GetStructuredCloneReadInfoFromSource(T* aSource,
6013                                        uint32_t aDataIndex,
6014                                        uint32_t aFileIdsIndex,
6015                                        FileManager* aFileManager,
6016                                        StructuredCloneReadInfo* aInfo);
6017 
6018   static nsresult
6019   GetStructuredCloneReadInfoFromBlob(const uint8_t* aBlobData,
6020                                      uint32_t aBlobDataLength,
6021                                      FileManager* aFileManager,
6022                                      const nsAString& aFileIds,
6023                                      StructuredCloneReadInfo* aInfo);
6024 
6025   static nsresult
6026   GetStructuredCloneReadInfoFromExternalBlob(uint64_t aIntData,
6027                                              FileManager* aFileManager,
6028                                              const nsAString& aFileIds,
6029                                              StructuredCloneReadInfo* aInfo);
6030 
6031   // Not to be overridden by subclasses.
6032   NS_DECL_MOZISTORAGEPROGRESSHANDLER
6033 };
6034 
6035 class MOZ_STACK_CLASS DatabaseOperationBase::AutoSetProgressHandler final
6036 {
6037   mozIStorageConnection* mConnection;
6038 #ifdef DEBUG
6039   DatabaseOperationBase* mDEBUGDatabaseOp;
6040 #endif
6041 
6042 public:
6043   AutoSetProgressHandler();
6044 
6045   ~AutoSetProgressHandler();
6046 
6047   nsresult
6048   Register(mozIStorageConnection* aConnection,
6049            DatabaseOperationBase* aDatabaseOp);
6050 };
6051 
6052 class TransactionDatabaseOperationBase
6053   : public DatabaseOperationBase
6054 {
6055   enum class InternalState
6056   {
6057     Initial,
6058     DatabaseWork,
6059     SendingPreprocess,
6060     WaitingForContinue,
6061     SendingResults,
6062     Completed
6063   };
6064 
6065   RefPtr<TransactionBase> mTransaction;
6066   const int64_t mTransactionLoggingSerialNumber;
6067   InternalState mInternalState;
6068   const bool mTransactionIsAborted;
6069 
6070 public:
6071   void
6072   AssertIsOnConnectionThread() const
6073 #ifdef DEBUG
6074   ;
6075 #else
6076   { }
6077 #endif
6078 
6079   uint64_t
6080   StartOnConnectionPool(const nsID& aBackgroundChildLoggingId,
6081                         const nsACString& aDatabaseId,
6082                         int64_t aLoggingSerialNumber,
6083                         const nsTArray<nsString>& aObjectStoreNames,
6084                         bool aIsWriteTransaction);
6085 
6086   void
6087   DispatchToConnectionPool();
6088 
6089   TransactionBase*
Transaction() const6090   Transaction() const
6091   {
6092     MOZ_ASSERT(mTransaction);
6093 
6094     return mTransaction;
6095   }
6096 
6097   void
6098   NoteContinueReceived();
6099 
6100   // May be overridden by subclasses if they need to perform work on the
6101   // background thread before being dispatched. Returning false will kill the
6102   // child actors and prevent dispatch.
6103   virtual bool
6104   Init(TransactionBase* aTransaction);
6105 
6106   // This callback will be called on the background thread before releasing the
6107   // final reference to this request object. Subclasses may perform any
6108   // additional cleanup here but must always call the base class implementation.
6109   virtual void
6110   Cleanup();
6111 
6112 protected:
6113   explicit
6114   TransactionDatabaseOperationBase(TransactionBase* aTransaction);
6115 
6116   TransactionDatabaseOperationBase(TransactionBase* aTransaction,
6117                                    uint64_t aLoggingSerialNumber);
6118 
6119   virtual
6120   ~TransactionDatabaseOperationBase();
6121 
6122   virtual void
6123   RunOnConnectionThread();
6124 
6125   // Must be overridden in subclasses. Called on the target thread to allow the
6126   // subclass to perform necessary database or file operations. A successful
6127   // return value will trigger a SendSuccessResult callback on the background
6128   // thread while a failure value will trigger a SendFailureResult callback.
6129   virtual nsresult
6130   DoDatabaseWork(DatabaseConnection* aConnection) = 0;
6131 
6132   // May be overriden in subclasses. Called on the background thread to decide
6133   // if the subclass needs to send any preprocess info to the child actor.
6134   virtual bool
6135   HasPreprocessInfo();
6136 
6137   // May be overriden in subclasses. Called on the background thread to allow
6138   // the subclass to serialize its preprocess info and send it to the child
6139   // actor. A successful return value will trigger a wait for a
6140   // NoteContinueReceived callback on the background thread while a failure
6141   // value will trigger a SendFailureResult callback.
6142   virtual nsresult
6143   SendPreprocessInfo();
6144 
6145   // Must be overridden in subclasses. Called on the background thread to allow
6146   // the subclass to serialize its results and send them to the child actor. A
6147   // failed return value will trigger a SendFailureResult callback.
6148   virtual nsresult
6149   SendSuccessResult() = 0;
6150 
6151   // Must be overridden in subclasses. Called on the background thread to allow
6152   // the subclass to send its failure code. Returning false will cause the
6153   // transaction to be aborted with aResultCode. Returning true will not cause
6154   // the transaction to be aborted.
6155   virtual bool
6156   SendFailureResult(nsresult aResultCode) = 0;
6157 
6158 private:
6159   void
6160   SendToConnectionPool();
6161 
6162   void
6163   SendPreprocess();
6164 
6165   void
6166   SendResults();
6167 
6168   void
6169   SendPreprocessInfoOrResults(bool aSendPreprocessInfo);
6170 
6171   // Not to be overridden by subclasses.
6172   NS_DECL_NSIRUNNABLE
6173 };
6174 
6175 class Factory final
6176   : public PBackgroundIDBFactoryParent
6177 {
6178 
6179   RefPtr<DatabaseLoggingInfo> mLoggingInfo;
6180 
6181 #ifdef DEBUG
6182   bool mActorDestroyed;
6183 #endif
6184 
6185 public:
6186   static already_AddRefed<Factory>
6187   Create(const LoggingInfo& aLoggingInfo);
6188 
6189   DatabaseLoggingInfo*
GetLoggingInfo() const6190   GetLoggingInfo() const
6191   {
6192     AssertIsOnBackgroundThread();
6193     MOZ_ASSERT(mLoggingInfo);
6194 
6195     return mLoggingInfo;
6196   }
6197 
6198   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Factory)
6199 
6200 private:
6201   // Only constructed in Create().
6202   explicit
6203   Factory(already_AddRefed<DatabaseLoggingInfo> aLoggingInfo);
6204 
6205   // Reference counted.
6206   ~Factory();
6207 
6208   // IPDL methods are only called by IPDL.
6209   virtual void
6210   ActorDestroy(ActorDestroyReason aWhy) override;
6211 
6212   virtual bool
6213   RecvDeleteMe() override;
6214 
6215   virtual bool
6216   RecvIncrementLoggingRequestSerialNumber() override;
6217 
6218   virtual PBackgroundIDBFactoryRequestParent*
6219   AllocPBackgroundIDBFactoryRequestParent(const FactoryRequestParams& aParams)
6220                                           override;
6221 
6222   virtual bool
6223   RecvPBackgroundIDBFactoryRequestConstructor(
6224                                      PBackgroundIDBFactoryRequestParent* aActor,
6225                                      const FactoryRequestParams& aParams)
6226                                      override;
6227 
6228   virtual bool
6229   DeallocPBackgroundIDBFactoryRequestParent(
6230                                      PBackgroundIDBFactoryRequestParent* aActor)
6231                                      override;
6232 
6233   virtual PBackgroundIDBDatabaseParent*
6234   AllocPBackgroundIDBDatabaseParent(
6235                                    const DatabaseSpec& aSpec,
6236                                    PBackgroundIDBFactoryRequestParent* aRequest)
6237                                    override;
6238 
6239   virtual bool
6240   DeallocPBackgroundIDBDatabaseParent(PBackgroundIDBDatabaseParent* aActor)
6241                                       override;
6242 };
6243 
6244 class WaitForTransactionsHelper final
6245   : public Runnable
6246 {
6247   const nsCString mDatabaseId;
6248   nsCOMPtr<nsIRunnable> mCallback;
6249 
6250   enum class State
6251   {
6252     Initial = 0,
6253     WaitingForTransactions,
6254     WaitingForFileHandles,
6255     Complete
6256   } mState;
6257 
6258 public:
WaitForTransactionsHelper(const nsCString & aDatabaseId,nsIRunnable * aCallback)6259   WaitForTransactionsHelper(const nsCString& aDatabaseId,
6260                             nsIRunnable* aCallback)
6261     : mDatabaseId(aDatabaseId)
6262     , mCallback(aCallback)
6263     , mState(State::Initial)
6264   {
6265     AssertIsOnBackgroundThread();
6266     MOZ_ASSERT(!aDatabaseId.IsEmpty());
6267     MOZ_ASSERT(aCallback);
6268   }
6269 
6270   void
6271   WaitForTransactions();
6272 
6273   NS_DECL_ISUPPORTS_INHERITED
6274 
6275 private:
~WaitForTransactionsHelper()6276   ~WaitForTransactionsHelper()
6277   {
6278     MOZ_ASSERT(!mCallback);
6279     MOZ_ASSERT(mState == State::Complete);
6280   }
6281 
6282   void
6283   MaybeWaitForTransactions();
6284 
6285   void
6286   MaybeWaitForFileHandles();
6287 
6288   void
6289   CallCallback();
6290 
6291   NS_DECL_NSIRUNNABLE
6292 };
6293 
6294 class Database final
6295   : public PBackgroundIDBDatabaseParent
6296 {
6297   friend class VersionChangeTransaction;
6298 
6299   class StartTransactionOp;
6300 
6301 private:
6302   RefPtr<Factory> mFactory;
6303   RefPtr<FullDatabaseMetadata> mMetadata;
6304   RefPtr<FileManager> mFileManager;
6305   RefPtr<DirectoryLock> mDirectoryLock;
6306   nsTHashtable<nsPtrHashKey<TransactionBase>> mTransactions;
6307   nsTHashtable<nsPtrHashKey<MutableFile>> mMutableFiles;
6308   RefPtr<DatabaseConnection> mConnection;
6309   const PrincipalInfo mPrincipalInfo;
6310   const Maybe<ContentParentId> mOptionalContentParentId;
6311   const nsCString mGroup;
6312   const nsCString mOrigin;
6313   const nsCString mId;
6314   const nsString mFilePath;
6315   uint32_t mActiveMutableFileCount;
6316   const uint32_t mTelemetryId;
6317   const PersistenceType mPersistenceType;
6318   const bool mFileHandleDisabled;
6319   const bool mChromeWriteAccessAllowed;
6320   bool mClosed;
6321   bool mInvalidated;
6322   bool mActorWasAlive;
6323   bool mActorDestroyed;
6324   bool mMetadataCleanedUp;
6325 
6326 public:
6327   // Created by OpenDatabaseOp.
6328   Database(Factory* aFactory,
6329            const PrincipalInfo& aPrincipalInfo,
6330            const Maybe<ContentParentId>& aOptionalContentParentId,
6331            const nsACString& aGroup,
6332            const nsACString& aOrigin,
6333            uint32_t aTelemetryId,
6334            FullDatabaseMetadata* aMetadata,
6335            FileManager* aFileManager,
6336            already_AddRefed<DirectoryLock> aDirectoryLock,
6337            bool aFileHandleDisabled,
6338            bool aChromeWriteAccessAllowed);
6339 
6340   void
AssertIsOnConnectionThread() const6341   AssertIsOnConnectionThread() const
6342   {
6343 #ifdef DEBUG
6344     if (mConnection) {
6345       MOZ_ASSERT(mConnection);
6346       mConnection->AssertIsOnConnectionThread();
6347     } else {
6348       MOZ_ASSERT(!NS_IsMainThread());
6349       MOZ_ASSERT(!IsOnBackgroundThread());
6350       MOZ_ASSERT(mInvalidated);
6351     }
6352 #endif
6353   }
6354 
6355   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Database)
6356 
6357   void
6358   Invalidate();
6359 
6360   const PrincipalInfo&
GetPrincipalInfo() const6361   GetPrincipalInfo() const
6362   {
6363     return mPrincipalInfo;
6364   }
6365 
6366   bool
IsOwnedByProcess(ContentParentId aContentParentId) const6367   IsOwnedByProcess(ContentParentId aContentParentId) const
6368   {
6369     return
6370       mOptionalContentParentId &&
6371       mOptionalContentParentId.value() == aContentParentId;
6372   }
6373 
6374   const nsCString&
Group() const6375   Group() const
6376   {
6377     return mGroup;
6378   }
6379 
6380   const nsCString&
Origin() const6381   Origin() const
6382   {
6383     return mOrigin;
6384   }
6385 
6386   const nsCString&
Id() const6387   Id() const
6388   {
6389     return mId;
6390   }
6391 
6392   uint32_t
TelemetryId() const6393   TelemetryId() const
6394   {
6395     return mTelemetryId;
6396   }
6397 
6398   PersistenceType
Type() const6399   Type() const
6400   {
6401     return mPersistenceType;
6402   }
6403 
6404   const nsString&
FilePath() const6405   FilePath() const
6406   {
6407     return mFilePath;
6408   }
6409 
6410   FileManager*
GetFileManager() const6411   GetFileManager() const
6412   {
6413     return mFileManager;
6414   }
6415 
6416   FullDatabaseMetadata*
Metadata() const6417   Metadata() const
6418   {
6419     MOZ_ASSERT(mMetadata);
6420     return mMetadata;
6421   }
6422 
6423   PBackgroundParent*
GetBackgroundParent() const6424   GetBackgroundParent() const
6425   {
6426     AssertIsOnBackgroundThread();
6427     MOZ_ASSERT(!IsActorDestroyed());
6428 
6429     return Manager()->Manager();
6430   }
6431 
6432   DatabaseLoggingInfo*
GetLoggingInfo() const6433   GetLoggingInfo() const
6434   {
6435     AssertIsOnBackgroundThread();
6436     MOZ_ASSERT(mFactory);
6437 
6438     return mFactory->GetLoggingInfo();
6439   }
6440 
6441   void
6442   ReleaseTransactionThreadObjects();
6443 
6444   void
6445   ReleaseBackgroundThreadObjects();
6446 
6447   bool
6448   RegisterTransaction(TransactionBase* aTransaction);
6449 
6450   void
6451   UnregisterTransaction(TransactionBase* aTransaction);
6452 
6453   bool
IsFileHandleDisabled() const6454   IsFileHandleDisabled() const
6455   {
6456     return mFileHandleDisabled;
6457   }
6458 
6459   bool
6460   RegisterMutableFile(MutableFile* aMutableFile);
6461 
6462   void
6463   UnregisterMutableFile(MutableFile* aMutableFile);
6464 
6465   void
6466   NoteActiveMutableFile();
6467 
6468   void
6469   NoteInactiveMutableFile();
6470 
6471   void
6472   SetActorAlive();
6473 
6474   bool
IsActorAlive() const6475   IsActorAlive() const
6476   {
6477     AssertIsOnBackgroundThread();
6478 
6479     return mActorWasAlive && !mActorDestroyed;
6480   }
6481 
6482   bool
IsActorDestroyed() const6483   IsActorDestroyed() const
6484   {
6485     AssertIsOnBackgroundThread();
6486 
6487     return mActorWasAlive && mActorDestroyed;
6488   }
6489 
6490   bool
IsClosed() const6491   IsClosed() const
6492   {
6493     AssertIsOnBackgroundThread();
6494 
6495     return mClosed;
6496   }
6497 
6498   bool
IsInvalidated() const6499   IsInvalidated() const
6500   {
6501     AssertIsOnBackgroundThread();
6502 
6503     return mInvalidated;
6504   }
6505 
6506   nsresult
6507   EnsureConnection();
6508 
6509   DatabaseConnection*
GetConnection() const6510   GetConnection() const
6511   {
6512 #ifdef DEBUG
6513     if (mConnection) {
6514       mConnection->AssertIsOnConnectionThread();
6515     }
6516 #endif
6517 
6518     return mConnection;
6519   }
6520 
6521 private:
6522   // Reference counted.
~Database()6523   ~Database()
6524   {
6525     MOZ_ASSERT(mClosed);
6526     MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed);
6527   }
6528 
6529   bool
6530   CloseInternal();
6531 
6532   void
6533   MaybeCloseConnection();
6534 
6535   void
6536   ConnectionClosedCallback();
6537 
6538   void
6539   CleanupMetadata();
6540 
6541   bool
6542   VerifyRequestParams(const DatabaseRequestParams& aParams) const;
6543 
6544   // IPDL methods are only called by IPDL.
6545   virtual void
6546   ActorDestroy(ActorDestroyReason aWhy) override;
6547 
6548   virtual PBackgroundIDBDatabaseFileParent*
6549   AllocPBackgroundIDBDatabaseFileParent(PBlobParent* aBlobParent)
6550                                         override;
6551 
6552   virtual bool
6553   DeallocPBackgroundIDBDatabaseFileParent(
6554                                        PBackgroundIDBDatabaseFileParent* aActor)
6555                                        override;
6556 
6557   virtual PBackgroundIDBDatabaseRequestParent*
6558   AllocPBackgroundIDBDatabaseRequestParent(const DatabaseRequestParams& aParams)
6559                                            override;
6560 
6561   virtual bool
6562   RecvPBackgroundIDBDatabaseRequestConstructor(
6563                                     PBackgroundIDBDatabaseRequestParent* aActor,
6564                                     const DatabaseRequestParams& aParams)
6565                                     override;
6566 
6567   virtual bool
6568   DeallocPBackgroundIDBDatabaseRequestParent(
6569                                     PBackgroundIDBDatabaseRequestParent* aActor)
6570                                     override;
6571 
6572   virtual PBackgroundIDBTransactionParent*
6573   AllocPBackgroundIDBTransactionParent(
6574                                     const nsTArray<nsString>& aObjectStoreNames,
6575                                     const Mode& aMode)
6576                                     override;
6577 
6578   virtual bool
6579   RecvPBackgroundIDBTransactionConstructor(
6580                                     PBackgroundIDBTransactionParent* aActor,
6581                                     InfallibleTArray<nsString>&& aObjectStoreNames,
6582                                     const Mode& aMode)
6583                                     override;
6584 
6585   virtual bool
6586   DeallocPBackgroundIDBTransactionParent(
6587                                         PBackgroundIDBTransactionParent* aActor)
6588                                         override;
6589 
6590   virtual PBackgroundIDBVersionChangeTransactionParent*
6591   AllocPBackgroundIDBVersionChangeTransactionParent(
6592                                               const uint64_t& aCurrentVersion,
6593                                               const uint64_t& aRequestedVersion,
6594                                               const int64_t& aNextObjectStoreId,
6595                                               const int64_t& aNextIndexId)
6596                                               override;
6597 
6598   virtual bool
6599   DeallocPBackgroundIDBVersionChangeTransactionParent(
6600                            PBackgroundIDBVersionChangeTransactionParent* aActor)
6601                            override;
6602 
6603   virtual PBackgroundMutableFileParent*
6604   AllocPBackgroundMutableFileParent(const nsString& aName,
6605                                     const nsString& aType) override;
6606 
6607   virtual bool
6608   DeallocPBackgroundMutableFileParent(PBackgroundMutableFileParent* aActor)
6609                                       override;
6610 
6611   virtual bool
6612   RecvDeleteMe() override;
6613 
6614   virtual bool
6615   RecvBlocked() override;
6616 
6617   virtual bool
6618   RecvClose() override;
6619 };
6620 
6621 class Database::StartTransactionOp final
6622   : public TransactionDatabaseOperationBase
6623 {
6624   friend class Database;
6625 
6626 private:
6627   explicit
StartTransactionOp(TransactionBase * aTransaction)6628   StartTransactionOp(TransactionBase* aTransaction)
6629     : TransactionDatabaseOperationBase(aTransaction,
6630                                        /* aLoggingSerialNumber */ 0)
6631   { }
6632 
~StartTransactionOp()6633   ~StartTransactionOp()
6634   { }
6635 
6636   virtual void
6637   RunOnConnectionThread() override;
6638 
6639   virtual nsresult
6640   DoDatabaseWork(DatabaseConnection* aConnection) override;
6641 
6642   virtual nsresult
6643   SendSuccessResult() override;
6644 
6645   virtual bool
6646   SendFailureResult(nsresult aResultCode) override;
6647 
6648   virtual void
6649   Cleanup() override;
6650 };
6651 
6652 /**
6653  * In coordination with IDBDatabase's mFileActors weak-map on the child side, a
6654  * long-lived mapping from a child process's live Blobs to their corresponding
6655  * FileInfo in our owning database.  Assists in avoiding redundant IPC traffic
6656  * and disk storage.  This includes both:
6657  * - Blobs retrieved from this database and sent to the child that do not need
6658  *   to be written to disk because they already exist on disk in this database's
6659  *   files directory.
6660  * - Blobs retrieved from other databases (that are therefore !IsShareable())
6661  *   or from anywhere else that will need to be written to this database's files
6662  *   directory.  In this case we will hold a reference to its BlobImpl in
6663  *   mBlobImpl until we have successfully written the Blob to disk.
6664  *
6665  * Relevant Blob context: Blobs sent from the parent process to child processes
6666  * are automatically linked back to their source BlobImpl when the child process
6667  * references the Blob via IPC.  (This is true even when a new "KnownBlob" actor
6668  * must be created because the reference is occurring on a different thread than
6669  * the PBlob actor created when the blob was sent to the child.)  However, when
6670  * getting an actor in the child process for sending an in-child-created Blob to
6671  * the parent process, there is (currently) no Blob machinery to automatically
6672  * establish and reuse a long-lived Actor.  As a result, without IDB's weak-map
6673  * cleverness, a memory-backed Blob repeatedly sent from the child to the parent
6674  * would appear as a different Blob each time, requiring the Blob data to be
6675  * sent over IPC each time as well as potentially needing to be written to disk
6676  * each time.
6677  *
6678  * This object remains alive as long as there is an active child actor or an
6679  * ObjectStoreAddOrPutRequestOp::StoredFileInfo for a queued or active add/put
6680  * op is holding a reference to us.
6681  */
6682 class DatabaseFile final
6683   : public PBackgroundIDBDatabaseFileParent
6684 {
6685   friend class Database;
6686 
6687   // mBlobImpl's ownership lifecycle:
6688   // - Initialized on the background thread at creation time.  Then
6689   //   responsibility is handed off to the connection thread.
6690   // - Checked and used by the connection thread to generate a stream to write
6691   //   the blob to disk by an add/put operation.
6692   // - Cleared on the connection thread once the file has successfully been
6693   //   written to disk.
6694   RefPtr<BlobImpl> mBlobImpl;
6695   RefPtr<FileInfo> mFileInfo;
6696 
6697 public:
6698   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::DatabaseFile);
6699 
6700   FileInfo*
GetFileInfo() const6701   GetFileInfo() const
6702   {
6703     AssertIsOnBackgroundThread();
6704 
6705     return mFileInfo;
6706   }
6707 
6708   /**
6709    * If mBlobImpl is non-null (implying the contents of this file have not yet
6710    * been written to disk), then return an input stream that is guaranteed to
6711    * block on reads and never return NS_BASE_STREAM_WOULD_BLOCK.  If mBlobImpl
6712    * is null (because the contents have been written to disk), returns null.
6713    *
6714    * Because this method does I/O, it should only be called on a database I/O
6715    * thread, not on PBackground.  Note that we actually open the stream on this
6716    * thread, where previously it was opened on PBackground.  This is safe and
6717    * equally efficient because blob implementations are thread-safe and blobs in
6718    * the parent are fully populated.  (This would not be efficient in the child
6719    * where the open request would have to be dispatched back to the PBackground
6720    * thread because of the need to potentially interact with actors.)
6721    *
6722    * We enforce this guarantee by wrapping the stream in a blocking pipe unless
6723    * either is true:
6724    * - The stream is already a blocking stream.  (AKA it's not non-blocking.)
6725    *   This is the case for nsFileStreamBase-derived implementations and
6726    *   appropriately configured nsPipes (but not PSendStream-generated pipes).
6727    * - The stream already contains the entire contents of the Blob.  For
6728    *   example, nsStringInputStreams report as non-blocking, but also have their
6729    *   entire contents synchronously available, so they will never return
6730    *   NS_BASE_STREAM_WOULD_BLOCK.  There is no need to wrap these in a pipe.
6731    *   (It's also very common for SendStream-based Blobs to have their contents
6732    *   entirely streamed into the parent process by the time this call is
6733    *   issued.)
6734    *
6735    * This additional logic is necessary because our database operations all
6736    * are written in such a way that the operation is assumed to have completed
6737    * when they yield control-flow, and:
6738    * - When memory-backed blobs cross a certain threshold (1MiB at the time of
6739    *   writing), they will be sent up from the child via PSendStream in chunks
6740    *   to a non-blocking pipe that will return NS_BASE_STREAM_WOULD_BLOCK.
6741    * - Other Blob types could potentially be non-blocking.  (We're not making
6742    *   any assumptions.)
6743    */
6744   already_AddRefed<nsIInputStream>
6745   GetBlockingInputStream(ErrorResult &rv) const;
6746 
6747   /**
6748    * To be called upon successful copying of the stream GetBlockingInputStream()
6749    * returned so that we won't try and redundantly write the file to disk in the
6750    * future.  This is a separate step from GetBlockingInputStream() because
6751    * the write could fail due to quota errors that happen now but that might
6752    * not happen in a future attempt.
6753    */
6754   void
WriteSucceededClearBlobImpl()6755   WriteSucceededClearBlobImpl()
6756   {
6757     MOZ_ASSERT(!IsOnBackgroundThread());
6758 
6759     mBlobImpl = nullptr;
6760   }
6761 
6762 private:
6763   // Called when sending to the child.
DatabaseFile(FileInfo * aFileInfo)6764   explicit DatabaseFile(FileInfo* aFileInfo)
6765     : mFileInfo(aFileInfo)
6766   {
6767     AssertIsOnBackgroundThread();
6768     MOZ_ASSERT(aFileInfo);
6769   }
6770 
6771   // Called when receiving from the child.
DatabaseFile(BlobImpl * aBlobImpl,FileInfo * aFileInfo)6772   DatabaseFile(BlobImpl* aBlobImpl, FileInfo* aFileInfo)
6773     : mBlobImpl(aBlobImpl)
6774     , mFileInfo(aFileInfo)
6775   {
6776     AssertIsOnBackgroundThread();
6777     MOZ_ASSERT(aBlobImpl);
6778     MOZ_ASSERT(aFileInfo);
6779   }
6780 
~DatabaseFile()6781   ~DatabaseFile()
6782   { }
6783 
6784   virtual void
ActorDestroy(ActorDestroyReason aWhy)6785   ActorDestroy(ActorDestroyReason aWhy) override
6786   {
6787     AssertIsOnBackgroundThread();
6788   }
6789 };
6790 
6791 already_AddRefed<nsIInputStream>
GetBlockingInputStream(ErrorResult & rv) const6792 DatabaseFile::GetBlockingInputStream(ErrorResult &rv) const
6793 {
6794   // We should only be called from our DB connection thread, not the background
6795   // thread.
6796   MOZ_ASSERT(!IsOnBackgroundThread());
6797 
6798   if (!mBlobImpl) {
6799     return nullptr;
6800   }
6801 
6802   nsCOMPtr<nsIInputStream> inputStream;
6803   mBlobImpl->GetInternalStream(getter_AddRefs(inputStream), rv);
6804   if (rv.Failed()) {
6805     return nullptr;
6806   }
6807 
6808   // If it's non-blocking we may need a pipe.
6809   bool pipeNeeded;
6810   rv = inputStream->IsNonBlocking(&pipeNeeded);
6811   if (rv.Failed()) {
6812     return nullptr;
6813   }
6814 
6815   // We don't need a pipe if all the bytes might already be available.
6816   if (pipeNeeded) {
6817     uint64_t available;
6818     rv = inputStream->Available(&available);
6819     if (rv.Failed()) {
6820       return nullptr;
6821     }
6822 
6823     uint64_t blobSize = mBlobImpl->GetSize(rv);
6824     if (rv.Failed()) {
6825       return nullptr;
6826     }
6827 
6828     if (available == blobSize) {
6829       pipeNeeded = false;
6830     }
6831   }
6832 
6833   if (pipeNeeded) {
6834     nsCOMPtr<nsIEventTarget> target =
6835       do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
6836     if (!target) {
6837       rv.Throw(NS_ERROR_UNEXPECTED);
6838       return nullptr;
6839     }
6840 
6841     nsCOMPtr<nsIInputStream> pipeInputStream;
6842     nsCOMPtr<nsIOutputStream> pipeOutputStream;
6843 
6844     rv = NS_NewPipe(
6845       getter_AddRefs(pipeInputStream),
6846       getter_AddRefs(pipeOutputStream),
6847       0, 0, // default buffering is fine;
6848       false, // we absolutely want a blocking input stream
6849       true); // we don't need the writer to block
6850     if (rv.Failed()) {
6851       return nullptr;
6852     }
6853 
6854     rv = NS_AsyncCopy(inputStream, pipeOutputStream, target);
6855     if (rv.Failed()) {
6856       return nullptr;
6857     }
6858 
6859     inputStream = pipeInputStream;
6860   }
6861 
6862   return inputStream.forget();
6863 }
6864 
6865 
6866 class TransactionBase
6867 {
6868   friend class Cursor;
6869 
6870   class CommitOp;
6871 
6872 protected:
6873   typedef IDBTransaction::Mode Mode;
6874 
6875 private:
6876   RefPtr<Database> mDatabase;
6877   nsTArray<RefPtr<FullObjectStoreMetadata>>
6878     mModifiedAutoIncrementObjectStoreMetadataArray;
6879   uint64_t mTransactionId;
6880   const nsCString mDatabaseId;
6881   const int64_t mLoggingSerialNumber;
6882   uint64_t mActiveRequestCount;
6883   Atomic<bool> mInvalidatedOnAnyThread;
6884   const Mode mMode;
6885   bool mHasBeenActive;
6886   bool mHasBeenActiveOnConnectionThread;
6887   bool mActorDestroyed;
6888   bool mInvalidated;
6889 
6890 protected:
6891   nsresult mResultCode;
6892   bool mCommitOrAbortReceived;
6893   bool mCommittedOrAborted;
6894   bool mForceAborted;
6895 
6896 public:
6897   void
AssertIsOnConnectionThread() const6898   AssertIsOnConnectionThread() const
6899   {
6900     MOZ_ASSERT(mDatabase);
6901     mDatabase->AssertIsOnConnectionThread();
6902   }
6903 
6904   bool
IsActorDestroyed() const6905   IsActorDestroyed() const
6906   {
6907     AssertIsOnBackgroundThread();
6908 
6909     return mActorDestroyed;
6910   }
6911 
6912   // Must be called on the background thread.
6913   bool
IsInvalidated() const6914   IsInvalidated() const
6915   {
6916     MOZ_ASSERT(IsOnBackgroundThread(), "Use IsInvalidatedOnAnyThread()");
6917     MOZ_ASSERT_IF(mInvalidated, NS_FAILED(mResultCode));
6918 
6919     return mInvalidated;
6920   }
6921 
6922   // May be called on any thread, but is more expensive than IsInvalidated().
6923   bool
IsInvalidatedOnAnyThread() const6924   IsInvalidatedOnAnyThread() const
6925   {
6926     return mInvalidatedOnAnyThread;
6927   }
6928 
6929   void
SetActive(uint64_t aTransactionId)6930   SetActive(uint64_t aTransactionId)
6931   {
6932     AssertIsOnBackgroundThread();
6933     MOZ_ASSERT(aTransactionId);
6934 
6935     mTransactionId = aTransactionId;
6936     mHasBeenActive = true;
6937   }
6938 
6939   void
SetActiveOnConnectionThread()6940   SetActiveOnConnectionThread()
6941   {
6942     AssertIsOnConnectionThread();
6943     mHasBeenActiveOnConnectionThread = true;
6944   }
6945 
6946   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(
6947     mozilla::dom::indexedDB::TransactionBase)
6948 
6949   void
6950   Abort(nsresult aResultCode, bool aForce);
6951 
6952   uint64_t
TransactionId() const6953   TransactionId() const
6954   {
6955     return mTransactionId;
6956   }
6957 
6958   const nsCString&
DatabaseId() const6959   DatabaseId() const
6960   {
6961     return mDatabaseId;
6962   }
6963 
6964   Mode
GetMode() const6965   GetMode() const
6966   {
6967     return mMode;
6968   }
6969 
6970   Database*
GetDatabase() const6971   GetDatabase() const
6972   {
6973     MOZ_ASSERT(mDatabase);
6974 
6975     return mDatabase;
6976   }
6977 
6978   DatabaseLoggingInfo*
GetLoggingInfo() const6979   GetLoggingInfo() const
6980   {
6981     AssertIsOnBackgroundThread();
6982     MOZ_ASSERT(mDatabase);
6983 
6984     return mDatabase->GetLoggingInfo();
6985   }
6986 
6987   int64_t
LoggingSerialNumber() const6988   LoggingSerialNumber() const
6989   {
6990     return mLoggingSerialNumber;
6991   }
6992 
6993   bool
IsAborted() const6994   IsAborted() const
6995   {
6996     AssertIsOnBackgroundThread();
6997 
6998     return NS_FAILED(mResultCode);
6999   }
7000 
7001   already_AddRefed<FullObjectStoreMetadata>
7002   GetMetadataForObjectStoreId(int64_t aObjectStoreId) const;
7003 
7004   already_AddRefed<FullIndexMetadata>
7005   GetMetadataForIndexId(FullObjectStoreMetadata* const aObjectStoreMetadata,
7006                         int64_t aIndexId) const;
7007 
7008   PBackgroundParent*
GetBackgroundParent() const7009   GetBackgroundParent() const
7010   {
7011     AssertIsOnBackgroundThread();
7012     MOZ_ASSERT(!IsActorDestroyed());
7013 
7014     return GetDatabase()->GetBackgroundParent();
7015   }
7016 
7017   void
7018   NoteModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata);
7019 
7020   void
7021   ForgetModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata);
7022 
7023   void
7024   NoteActiveRequest();
7025 
7026   void
7027   NoteFinishedRequest();
7028 
7029   void
7030   Invalidate();
7031 
7032 protected:
7033   TransactionBase(Database* aDatabase, Mode aMode);
7034 
7035   virtual
7036   ~TransactionBase();
7037 
7038   void
NoteActorDestroyed()7039   NoteActorDestroyed()
7040   {
7041     AssertIsOnBackgroundThread();
7042     MOZ_ASSERT(!mActorDestroyed);
7043 
7044     mActorDestroyed = true;
7045   }
7046 
7047 #ifdef DEBUG
7048   // Only called by VersionChangeTransaction.
7049   void
FakeActorDestroyed()7050   FakeActorDestroyed()
7051   {
7052     mActorDestroyed = true;
7053   }
7054 #endif
7055 
7056   bool
7057   RecvCommit();
7058 
7059   bool
7060   RecvAbort(nsresult aResultCode);
7061 
7062   void
MaybeCommitOrAbort()7063   MaybeCommitOrAbort()
7064   {
7065     AssertIsOnBackgroundThread();
7066 
7067     // If we've already committed or aborted then there's nothing else to do.
7068     if (mCommittedOrAborted) {
7069       return;
7070     }
7071 
7072     // If there are active requests then we have to wait for those requests to
7073     // complete (see NoteFinishedRequest).
7074     if (mActiveRequestCount) {
7075       return;
7076     }
7077 
7078     // If we haven't yet received a commit or abort message then there could be
7079     // additional requests coming so we should wait unless we're being forced to
7080     // abort.
7081     if (!mCommitOrAbortReceived && !mForceAborted) {
7082       return;
7083     }
7084 
7085     CommitOrAbort();
7086   }
7087 
7088   PBackgroundIDBRequestParent*
7089   AllocRequest(const RequestParams& aParams, bool aTrustParams);
7090 
7091   bool
7092   StartRequest(PBackgroundIDBRequestParent* aActor);
7093 
7094   bool
7095   DeallocRequest(PBackgroundIDBRequestParent* aActor);
7096 
7097   PBackgroundIDBCursorParent*
7098   AllocCursor(const OpenCursorParams& aParams, bool aTrustParams);
7099 
7100   bool
7101   StartCursor(PBackgroundIDBCursorParent* aActor,
7102               const OpenCursorParams& aParams);
7103 
7104   bool
7105   DeallocCursor(PBackgroundIDBCursorParent* aActor);
7106 
7107   virtual void
UpdateMetadata(nsresult aResult)7108   UpdateMetadata(nsresult aResult)
7109   { }
7110 
7111   virtual void
7112   SendCompleteNotification(nsresult aResult) = 0;
7113 
7114 private:
7115   bool
7116   VerifyRequestParams(const RequestParams& aParams) const;
7117 
7118   bool
7119   VerifyRequestParams(const SerializedKeyRange& aKeyRange) const;
7120 
7121   bool
7122   VerifyRequestParams(const ObjectStoreAddPutParams& aParams) const;
7123 
7124   bool
7125   VerifyRequestParams(const OptionalKeyRange& aKeyRange) const;
7126 
7127   void
7128   CommitOrAbort();
7129 };
7130 
7131 class TransactionBase::CommitOp final
7132   : public DatabaseOperationBase
7133   , public ConnectionPool::FinishCallback
7134 {
7135   friend class TransactionBase;
7136 
7137   RefPtr<TransactionBase> mTransaction;
7138   nsresult mResultCode;
7139 
7140 private:
7141   CommitOp(TransactionBase* aTransaction, nsresult aResultCode);
7142 
~CommitOp()7143   ~CommitOp()
7144   { }
7145 
7146   // Writes new autoIncrement counts to database.
7147   nsresult
7148   WriteAutoIncrementCounts();
7149 
7150   // Updates counts after a database activity has finished.
7151   void
7152   CommitOrRollbackAutoIncrementCounts();
7153 
7154   void
7155   AssertForeignKeyConsistency(DatabaseConnection* aConnection)
7156 #ifdef DEBUG
7157   ;
7158 #else
7159   { }
7160 #endif
7161 
7162   NS_DECL_NSIRUNNABLE
7163 
7164   virtual void
7165   TransactionFinishedBeforeUnblock() override;
7166 
7167   virtual void
7168   TransactionFinishedAfterUnblock() override;
7169 
7170 public:
7171   NS_DECL_ISUPPORTS_INHERITED
7172 };
7173 
7174 class NormalTransaction final
7175   : public TransactionBase
7176   , public PBackgroundIDBTransactionParent
7177 {
7178   friend class Database;
7179 
7180   nsTArray<RefPtr<FullObjectStoreMetadata>> mObjectStores;
7181 
7182 private:
7183   // This constructor is only called by Database.
7184   NormalTransaction(Database* aDatabase,
7185                     TransactionBase::Mode aMode,
7186                     nsTArray<RefPtr<FullObjectStoreMetadata>>& aObjectStores);
7187 
7188   // Reference counted.
~NormalTransaction()7189   ~NormalTransaction()
7190   { }
7191 
7192   bool
7193   IsSameProcessActor();
7194 
7195   // Only called by TransactionBase.
7196   virtual void
7197   SendCompleteNotification(nsresult aResult) override;
7198 
7199   // IPDL methods are only called by IPDL.
7200   virtual void
7201   ActorDestroy(ActorDestroyReason aWhy) override;
7202 
7203   virtual bool
7204   RecvDeleteMe() override;
7205 
7206   virtual bool
7207   RecvCommit() override;
7208 
7209   virtual bool
7210   RecvAbort(const nsresult& aResultCode) override;
7211 
7212   virtual PBackgroundIDBRequestParent*
7213   AllocPBackgroundIDBRequestParent(const RequestParams& aParams) override;
7214 
7215   virtual bool
7216   RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent* aActor,
7217                                        const RequestParams& aParams)
7218                                        override;
7219 
7220   virtual bool
7221   DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent* aActor)
7222                                      override;
7223 
7224   virtual PBackgroundIDBCursorParent*
7225   AllocPBackgroundIDBCursorParent(const OpenCursorParams& aParams) override;
7226 
7227   virtual bool
7228   RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent* aActor,
7229                                       const OpenCursorParams& aParams)
7230                                       override;
7231 
7232   virtual bool
7233   DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent* aActor)
7234                                     override;
7235 };
7236 
7237 class VersionChangeTransaction final
7238   : public TransactionBase
7239   , public PBackgroundIDBVersionChangeTransactionParent
7240 {
7241   friend class OpenDatabaseOp;
7242 
7243   RefPtr<OpenDatabaseOp> mOpenDatabaseOp;
7244   RefPtr<FullDatabaseMetadata> mOldMetadata;
7245 
7246   bool mActorWasAlive;
7247 
7248 private:
7249   // Only called by OpenDatabaseOp.
7250   explicit VersionChangeTransaction(OpenDatabaseOp* aOpenDatabaseOp);
7251 
7252   // Reference counted.
7253   ~VersionChangeTransaction();
7254 
7255   bool
7256   IsSameProcessActor();
7257 
7258   // Only called by OpenDatabaseOp.
7259   bool
7260   CopyDatabaseMetadata();
7261 
7262   void
7263   SetActorAlive();
7264 
7265   // Only called by TransactionBase.
7266   virtual void
7267   UpdateMetadata(nsresult aResult) override;
7268 
7269   // Only called by TransactionBase.
7270   virtual void
7271   SendCompleteNotification(nsresult aResult) override;
7272 
7273   // IPDL methods are only called by IPDL.
7274   virtual void
7275   ActorDestroy(ActorDestroyReason aWhy) override;
7276 
7277   virtual bool
7278   RecvDeleteMe() override;
7279 
7280   virtual bool
7281   RecvCommit() override;
7282 
7283   virtual bool
7284   RecvAbort(const nsresult& aResultCode) override;
7285 
7286   virtual bool
7287   RecvCreateObjectStore(const ObjectStoreMetadata& aMetadata) override;
7288 
7289   virtual bool
7290   RecvDeleteObjectStore(const int64_t& aObjectStoreId) override;
7291 
7292   virtual bool
7293   RecvRenameObjectStore(const int64_t& aObjectStoreId,
7294                         const nsString& aName) override;
7295 
7296   virtual bool
7297   RecvCreateIndex(const int64_t& aObjectStoreId,
7298                   const IndexMetadata& aMetadata) override;
7299 
7300   virtual bool
7301   RecvDeleteIndex(const int64_t& aObjectStoreId,
7302                   const int64_t& aIndexId) override;
7303 
7304   virtual bool
7305   RecvRenameIndex(const int64_t& aObjectStoreId,
7306                   const int64_t& aIndexId,
7307                   const nsString& aName) override;
7308 
7309   virtual PBackgroundIDBRequestParent*
7310   AllocPBackgroundIDBRequestParent(const RequestParams& aParams) override;
7311 
7312   virtual bool
7313   RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent* aActor,
7314                                        const RequestParams& aParams)
7315                                        override;
7316 
7317   virtual bool
7318   DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent* aActor)
7319                                      override;
7320 
7321   virtual PBackgroundIDBCursorParent*
7322   AllocPBackgroundIDBCursorParent(const OpenCursorParams& aParams) override;
7323 
7324   virtual bool
7325   RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent* aActor,
7326                                       const OpenCursorParams& aParams)
7327                                       override;
7328 
7329   virtual bool
7330   DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent* aActor)
7331                                     override;
7332 };
7333 
7334 class MutableFile
7335   : public BackgroundMutableFileParentBase
7336 {
7337   RefPtr<Database> mDatabase;
7338   RefPtr<FileInfo> mFileInfo;
7339 
7340 public:
7341   static already_AddRefed<MutableFile>
7342   Create(nsIFile* aFile,
7343          Database* aDatabase,
7344          FileInfo* aFileInfo);
7345 
7346   Database*
GetDatabase() const7347   GetDatabase() const
7348   {
7349     AssertIsOnBackgroundThread();
7350     MOZ_ASSERT(mDatabase);
7351 
7352     return mDatabase;
7353   }
7354 
7355   FileInfo*
GetFileInfo() const7356   GetFileInfo() const
7357   {
7358     AssertIsOnBackgroundThread();
7359     MOZ_ASSERT(mFileInfo);
7360 
7361     return mFileInfo;
7362   }
7363 
7364   virtual void
7365   NoteActiveState() override;
7366 
7367   virtual void
7368   NoteInactiveState() override;
7369 
7370   virtual PBackgroundParent*
7371   GetBackgroundParent() const override;
7372 
7373   virtual already_AddRefed<nsISupports>
7374   CreateStream(bool aReadOnly) override;
7375 
7376   virtual already_AddRefed<BlobImpl>
7377   CreateBlobImpl() override;
7378 
7379 private:
7380   MutableFile(nsIFile* aFile,
7381               Database* aDatabase,
7382               FileInfo* aFileInfo);
7383 
7384   ~MutableFile();
7385 
7386   virtual PBackgroundFileHandleParent*
7387   AllocPBackgroundFileHandleParent(const FileMode& aMode) override;
7388 
7389   virtual bool
7390   RecvPBackgroundFileHandleConstructor(PBackgroundFileHandleParent* aActor,
7391                                        const FileMode& aMode) override;
7392 
7393   virtual bool
7394   RecvGetFileId(int64_t* aFileId) override;
7395 };
7396 
7397 class FactoryOp
7398   : public DatabaseOperationBase
7399   , public OpenDirectoryListener
7400   , public PBackgroundIDBFactoryRequestParent
7401 {
7402 public:
7403   struct MaybeBlockedDatabaseInfo;
7404 
7405 protected:
7406   enum class State
7407   {
7408     // Just created on the PBackground thread, dispatched to the main thread.
7409     // Next step is either SendingResults if permission is denied,
7410     // PermissionChallenge if the permission is unknown, or FinishOpen
7411     // if permission is granted.
7412     Initial,
7413 
7414     // Sending a permission challenge message to the child on the PBackground
7415     // thread. Next step is PermissionRetry.
7416     PermissionChallenge,
7417 
7418     // Retrying permission check after a challenge on the main thread. Next step
7419     // is either SendingResults if permission is denied or FinishOpen
7420     // if permission is granted.
7421     PermissionRetry,
7422 
7423     // Opening directory or initializing quota manager on the PBackground
7424     // thread. Next step is either DirectoryOpenPending if quota manager is
7425     // already initialized or QuotaManagerPending if quota manager needs to be
7426     // initialized.
7427     FinishOpen,
7428 
7429     // Waiting for quota manager initialization to complete on the PBackground
7430     // thread. Next step is either SendingResults if initialization failed or
7431     // DirectoryOpenPending if initialization succeeded.
7432     QuotaManagerPending,
7433 
7434     // Waiting for directory open allowed on the PBackground thread. The next
7435     // step is either SendingResults if directory lock failed to acquire, or
7436     // DatabaseOpenPending if directory lock is acquired.
7437     DirectoryOpenPending,
7438 
7439     // Waiting for database open allowed on the PBackground thread. The next
7440     // step is DatabaseWorkOpen.
7441     DatabaseOpenPending,
7442 
7443     // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
7444     // either BeginVersionChange if the requested version doesn't match the
7445     // existing database version or SendingResults if the versions match.
7446     DatabaseWorkOpen,
7447 
7448     // Starting a version change transaction or deleting a database on the
7449     // PBackground thread. We need to notify other databases that a version
7450     // change is about to happen, and maybe tell the request that a version
7451     // change has been blocked. If databases are notified then the next step is
7452     // WaitingForOtherDatabasesToClose. Otherwise the next step is
7453     // WaitingForTransactionsToComplete.
7454     BeginVersionChange,
7455 
7456     // Waiting for other databases to close on the PBackground thread. This
7457     // state may persist until all databases are closed. The next state is
7458     // WaitingForTransactionsToComplete.
7459     WaitingForOtherDatabasesToClose,
7460 
7461     // Waiting for all transactions that could interfere with this operation to
7462     // complete on the PBackground thread. Next state is
7463     // DatabaseWorkVersionChange.
7464     WaitingForTransactionsToComplete,
7465 
7466     // Waiting to do/doing work on the "work thread". This involves waiting for
7467     // the VersionChangeOp (OpenDatabaseOp and DeleteDatabaseOp each have a
7468     // different implementation) to do its work. Eventually the state will
7469     // transition to SendingResults.
7470     DatabaseWorkVersionChange,
7471 
7472     // Waiting to send/sending results on the PBackground thread. Next step is
7473     // Completed.
7474     SendingResults,
7475 
7476     // All done.
7477     Completed
7478   };
7479 
7480   // Must be released on the background thread!
7481   RefPtr<Factory> mFactory;
7482 
7483   // Must be released on the main thread!
7484   RefPtr<ContentParent> mContentParent;
7485 
7486   // Must be released on the main thread!
7487   RefPtr<DirectoryLock> mDirectoryLock;
7488 
7489   RefPtr<FactoryOp> mDelayedOp;
7490   nsTArray<MaybeBlockedDatabaseInfo> mMaybeBlockedDatabases;
7491 
7492   const CommonFactoryRequestParams mCommonParams;
7493   nsCString mSuffix;
7494   nsCString mGroup;
7495   nsCString mOrigin;
7496   nsCString mDatabaseId;
7497   nsString mDatabaseFilePath;
7498   State mState;
7499   bool mIsApp;
7500   bool mEnforcingQuota;
7501   const bool mDeleting;
7502   bool mBlockedDatabaseOpen;
7503   bool mChromeWriteAccessAllowed;
7504   bool mFileHandleDisabled;
7505 
7506 public:
7507   void
7508   NoteDatabaseBlocked(Database* aDatabase);
7509 
7510   virtual void
7511   NoteDatabaseClosed(Database* aDatabase) = 0;
7512 
7513 #ifdef DEBUG
7514   bool
HasBlockedDatabases() const7515   HasBlockedDatabases() const
7516   {
7517     return !mMaybeBlockedDatabases.IsEmpty();
7518   }
7519 #endif
7520 
7521   const nsString&
DatabaseFilePath() const7522   DatabaseFilePath() const
7523   {
7524     return mDatabaseFilePath;
7525   }
7526 
7527 protected:
7528   FactoryOp(Factory* aFactory,
7529             already_AddRefed<ContentParent> aContentParent,
7530             const CommonFactoryRequestParams& aCommonParams,
7531             bool aDeleting);
7532 
7533   virtual
~FactoryOp()7534   ~FactoryOp()
7535   {
7536     // Normally this would be out-of-line since it is a virtual function but
7537     // MSVC 2010 fails to link for some reason if it is not inlined here...
7538     MOZ_ASSERT_IF(OperationMayProceed(),
7539                   mState == State::Initial || mState == State::Completed);
7540   }
7541 
7542   nsresult
7543   Open();
7544 
7545   nsresult
7546   ChallengePermission();
7547 
7548   nsresult
7549   RetryCheckPermission();
7550 
7551   nsresult
7552   DirectoryOpen();
7553 
7554   nsresult
7555   SendToIOThread();
7556 
7557   void
7558   WaitForTransactions();
7559 
7560   void
7561   FinishSendResults();
7562 
7563   nsresult
7564   SendVersionChangeMessages(DatabaseActorInfo* aDatabaseActorInfo,
7565                             Database* aOpeningDatabase,
7566                             uint64_t aOldVersion,
7567                             const NullableVersion& aNewVersion);
7568 
7569   // Methods that subclasses must implement.
7570   virtual nsresult
7571   DatabaseOpen() = 0;
7572 
7573   virtual nsresult
7574   DoDatabaseWork() = 0;
7575 
7576   virtual nsresult
7577   BeginVersionChange() = 0;
7578 
7579   virtual nsresult
7580   DispatchToWorkThread() = 0;
7581 
7582   // Should only be called by Run().
7583   virtual void
7584   SendResults() = 0;
7585 
7586   NS_DECL_ISUPPORTS_INHERITED
7587 
7588   // Common nsIRunnable implementation that subclasses may not override.
7589   NS_IMETHOD
7590   Run() final;
7591 
7592   // OpenDirectoryListener overrides.
7593   virtual void
7594   DirectoryLockAcquired(DirectoryLock* aLock) override;
7595 
7596   virtual void
7597   DirectoryLockFailed() override;
7598 
7599   // IPDL methods.
7600   virtual void
7601   ActorDestroy(ActorDestroyReason aWhy) override;
7602 
7603   virtual bool
7604   RecvPermissionRetry() override;
7605 
7606   virtual void
7607   SendBlockedNotification() = 0;
7608 
7609 private:
7610   nsresult
7611   CheckPermission(ContentParent* aContentParent,
7612                   PermissionRequestBase::PermissionValue* aPermission);
7613 
7614   static bool
7615   CheckAtLeastOneAppHasPermission(ContentParent* aContentParent,
7616                                   const nsACString& aPermissionString);
7617 
7618   nsresult
7619   FinishOpen();
7620 
7621   nsresult
7622   QuotaManagerOpen();
7623 
7624   nsresult
7625   OpenDirectory();
7626 
7627   // Test whether this FactoryOp needs to wait for the given op.
7628   bool
7629   MustWaitFor(const FactoryOp& aExistingOp);
7630 };
7631 
7632 struct FactoryOp::MaybeBlockedDatabaseInfo final
7633 {
7634   RefPtr<Database> mDatabase;
7635   bool mBlocked;
7636 
MaybeBlockedDatabaseInfomozilla::dom::indexedDB::__anone276c2b20111::FactoryOp::MaybeBlockedDatabaseInfo7637   MOZ_IMPLICIT MaybeBlockedDatabaseInfo(Database* aDatabase)
7638     : mDatabase(aDatabase)
7639     , mBlocked(false)
7640   {
7641     MOZ_ASSERT(aDatabase);
7642 
7643     MOZ_COUNT_CTOR(FactoryOp::MaybeBlockedDatabaseInfo);
7644   }
7645 
~MaybeBlockedDatabaseInfomozilla::dom::indexedDB::__anone276c2b20111::FactoryOp::MaybeBlockedDatabaseInfo7646   ~MaybeBlockedDatabaseInfo()
7647   {
7648     MOZ_COUNT_DTOR(FactoryOp::MaybeBlockedDatabaseInfo);
7649   }
7650 
7651   bool
operator ==mozilla::dom::indexedDB::__anone276c2b20111::FactoryOp::MaybeBlockedDatabaseInfo7652   operator==(const MaybeBlockedDatabaseInfo& aOther) const
7653   {
7654     return mDatabase == aOther.mDatabase;
7655   }
7656 
7657   bool
operator <mozilla::dom::indexedDB::__anone276c2b20111::FactoryOp::MaybeBlockedDatabaseInfo7658   operator<(const MaybeBlockedDatabaseInfo& aOther) const
7659   {
7660     return mDatabase < aOther.mDatabase;
7661   }
7662 
7663   Database*
operator ->mozilla::dom::indexedDB::__anone276c2b20111::FactoryOp::MaybeBlockedDatabaseInfo7664   operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN
7665   {
7666     return mDatabase;
7667   }
7668 };
7669 
7670 class OpenDatabaseOp final
7671   : public FactoryOp
7672 {
7673   friend class Database;
7674   friend class VersionChangeTransaction;
7675 
7676   class VersionChangeOp;
7677 
7678   Maybe<ContentParentId> mOptionalContentParentId;
7679 
7680   RefPtr<FullDatabaseMetadata> mMetadata;
7681 
7682   uint64_t mRequestedVersion;
7683   RefPtr<FileManager> mFileManager;
7684 
7685   RefPtr<Database> mDatabase;
7686   RefPtr<VersionChangeTransaction> mVersionChangeTransaction;
7687 
7688   // This is only set while a VersionChangeOp is live. It holds a strong
7689   // reference to its OpenDatabaseOp object so this is a weak pointer to avoid
7690   // cycles.
7691   VersionChangeOp* mVersionChangeOp;
7692 
7693   uint32_t mTelemetryId;
7694 
7695 public:
7696   OpenDatabaseOp(Factory* aFactory,
7697                  already_AddRefed<ContentParent> aContentParent,
7698                  const CommonFactoryRequestParams& aParams);
7699 
7700   bool
IsOtherProcessActor() const7701   IsOtherProcessActor() const
7702   {
7703     return mOptionalContentParentId.isSome();
7704   }
7705 
7706 private:
~OpenDatabaseOp()7707   ~OpenDatabaseOp()
7708   {
7709     MOZ_ASSERT(!mVersionChangeOp);
7710   }
7711 
7712   nsresult
7713   LoadDatabaseInformation(mozIStorageConnection* aConnection);
7714 
7715   nsresult
7716   SendUpgradeNeeded();
7717 
7718   void
7719   EnsureDatabaseActor();
7720 
7721   nsresult
7722   EnsureDatabaseActorIsAlive();
7723 
7724   void
7725   MetadataToSpec(DatabaseSpec& aSpec);
7726 
7727   void
7728   AssertMetadataConsistency(const FullDatabaseMetadata* aMetadata)
7729 #ifdef DEBUG
7730   ;
7731 #else
7732   { }
7733 #endif
7734 
7735   void
7736   ConnectionClosedCallback();
7737 
7738   virtual void
7739   ActorDestroy(ActorDestroyReason aWhy) override;
7740 
7741   virtual nsresult
7742   DatabaseOpen() override;
7743 
7744   virtual nsresult
7745   DoDatabaseWork() override;
7746 
7747   virtual nsresult
7748   BeginVersionChange() override;
7749 
7750   virtual void
7751   NoteDatabaseClosed(Database* aDatabase) override;
7752 
7753   virtual void
7754   SendBlockedNotification() override;
7755 
7756   virtual nsresult
7757   DispatchToWorkThread() override;
7758 
7759   virtual void
7760   SendResults() override;
7761 
7762 #ifdef ENABLE_INTL_API
7763   static nsresult
7764   UpdateLocaleAwareIndex(mozIStorageConnection* aConnection,
7765                          const IndexMetadata& aIndexMetadata,
7766                          const nsCString& aLocale);
7767 #endif
7768 };
7769 
7770 class OpenDatabaseOp::VersionChangeOp final
7771   : public TransactionDatabaseOperationBase
7772 {
7773   friend class OpenDatabaseOp;
7774 
7775   RefPtr<OpenDatabaseOp> mOpenDatabaseOp;
7776   const uint64_t mRequestedVersion;
7777   uint64_t mPreviousVersion;
7778 
7779 private:
7780   explicit
VersionChangeOp(OpenDatabaseOp * aOpenDatabaseOp)7781   VersionChangeOp(OpenDatabaseOp* aOpenDatabaseOp)
7782     : TransactionDatabaseOperationBase(
7783                                      aOpenDatabaseOp->mVersionChangeTransaction,
7784                                      aOpenDatabaseOp->LoggingSerialNumber())
7785     , mOpenDatabaseOp(aOpenDatabaseOp)
7786     , mRequestedVersion(aOpenDatabaseOp->mRequestedVersion)
7787     , mPreviousVersion(aOpenDatabaseOp->mMetadata->mCommonMetadata.version())
7788   {
7789     MOZ_ASSERT(aOpenDatabaseOp);
7790     MOZ_ASSERT(mRequestedVersion);
7791   }
7792 
~VersionChangeOp()7793   ~VersionChangeOp()
7794   {
7795     MOZ_ASSERT(!mOpenDatabaseOp);
7796   }
7797 
7798   virtual nsresult
7799   DoDatabaseWork(DatabaseConnection* aConnection) override;
7800 
7801   virtual nsresult
7802   SendSuccessResult() override;
7803 
7804   virtual bool
7805   SendFailureResult(nsresult aResultCode) override;
7806 
7807   virtual void
7808   Cleanup() override;
7809 };
7810 
7811 class DeleteDatabaseOp final
7812   : public FactoryOp
7813 {
7814   class VersionChangeOp;
7815 
7816   nsString mDatabaseDirectoryPath;
7817   nsString mDatabaseFilenameBase;
7818   uint64_t mPreviousVersion;
7819 
7820 public:
DeleteDatabaseOp(Factory * aFactory,already_AddRefed<ContentParent> aContentParent,const CommonFactoryRequestParams & aParams)7821   DeleteDatabaseOp(Factory* aFactory,
7822                    already_AddRefed<ContentParent> aContentParent,
7823                    const CommonFactoryRequestParams& aParams)
7824     : FactoryOp(aFactory, Move(aContentParent), aParams, /* aDeleting */ true)
7825     , mPreviousVersion(0)
7826   { }
7827 
7828 private:
~DeleteDatabaseOp()7829   ~DeleteDatabaseOp()
7830   { }
7831 
7832   void
7833   LoadPreviousVersion(nsIFile* aDatabaseFile);
7834 
7835   virtual nsresult
7836   DatabaseOpen() override;
7837 
7838   virtual nsresult
7839   DoDatabaseWork() override;
7840 
7841   virtual nsresult
7842   BeginVersionChange() override;
7843 
7844   virtual void
7845   NoteDatabaseClosed(Database* aDatabase) override;
7846 
7847   virtual void
7848   SendBlockedNotification() override;
7849 
7850   virtual nsresult
7851   DispatchToWorkThread() override;
7852 
7853   virtual void
7854   SendResults() override;
7855 };
7856 
7857 class DeleteDatabaseOp::VersionChangeOp final
7858   : public DatabaseOperationBase
7859 {
7860   friend class DeleteDatabaseOp;
7861 
7862   RefPtr<DeleteDatabaseOp> mDeleteDatabaseOp;
7863 
7864 private:
7865   explicit
VersionChangeOp(DeleteDatabaseOp * aDeleteDatabaseOp)7866   VersionChangeOp(DeleteDatabaseOp* aDeleteDatabaseOp)
7867     : DatabaseOperationBase(aDeleteDatabaseOp->BackgroundChildLoggingId(),
7868                             aDeleteDatabaseOp->LoggingSerialNumber())
7869     , mDeleteDatabaseOp(aDeleteDatabaseOp)
7870   {
7871     MOZ_ASSERT(aDeleteDatabaseOp);
7872     MOZ_ASSERT(!aDeleteDatabaseOp->mDatabaseDirectoryPath.IsEmpty());
7873   }
7874 
~VersionChangeOp()7875   ~VersionChangeOp()
7876   { }
7877 
7878   nsresult
7879   RunOnIOThread();
7880 
7881   void
7882   RunOnOwningThread();
7883 
7884   nsresult
7885   DeleteFile(nsIFile* aDirectory,
7886              const nsAString& aFilename,
7887              QuotaManager* aQuotaManager);
7888 
7889   NS_DECL_NSIRUNNABLE
7890 };
7891 
7892 class DatabaseOp
7893   : public DatabaseOperationBase
7894   , public PBackgroundIDBDatabaseRequestParent
7895 {
7896 protected:
7897   RefPtr<Database> mDatabase;
7898 
7899   enum class State
7900   {
7901     // Just created on the PBackground thread, dispatched to the main thread.
7902     // Next step is DatabaseWork.
7903     Initial,
7904 
7905     // Waiting to do/doing work on the QuotaManager IO thread. Next step is
7906     // SendingResults.
7907     DatabaseWork,
7908 
7909     // Waiting to send/sending results on the PBackground thread. Next step is
7910     // Completed.
7911     SendingResults,
7912 
7913     // All done.
7914     Completed
7915   };
7916 
7917   State mState;
7918 
7919 public:
7920   void
RunImmediately()7921   RunImmediately()
7922   {
7923     MOZ_ASSERT(mState == State::Initial);
7924 
7925     Unused << this->Run();
7926   }
7927 
7928 protected:
7929   DatabaseOp(Database* aDatabase);
7930 
7931   virtual
~DatabaseOp()7932   ~DatabaseOp()
7933   {
7934     MOZ_ASSERT_IF(OperationMayProceed(),
7935                   mState == State::Initial || mState == State::Completed);
7936   }
7937 
7938   nsresult
7939   SendToIOThread();
7940 
7941   // Methods that subclasses must implement.
7942   virtual nsresult
7943   DoDatabaseWork() = 0;
7944 
7945   virtual void
7946   SendResults() = 0;
7947 
7948   // Common nsIRunnable implementation that subclasses may not override.
7949   NS_IMETHOD
7950   Run() final;
7951 
7952   // IPDL methods.
7953   virtual void
7954   ActorDestroy(ActorDestroyReason aWhy) override;
7955 };
7956 
7957 class CreateFileOp final
7958   : public DatabaseOp
7959 {
7960   const CreateFileParams mParams;
7961 
7962   RefPtr<FileInfo> mFileInfo;
7963 
7964 public:
7965   CreateFileOp(Database* aDatabase,
7966                const DatabaseRequestParams& aParams);
7967 
7968 private:
~CreateFileOp()7969   ~CreateFileOp()
7970   { }
7971 
7972   nsresult
7973   CreateMutableFile(MutableFile** aMutableFile);
7974 
7975   virtual nsresult
7976   DoDatabaseWork() override;
7977 
7978   virtual void
7979   SendResults() override;
7980 };
7981 
7982 class VersionChangeTransactionOp
7983   : public TransactionDatabaseOperationBase
7984 {
7985 public:
7986   virtual void
7987   Cleanup() override;
7988 
7989 protected:
VersionChangeTransactionOp(VersionChangeTransaction * aTransaction)7990   explicit VersionChangeTransactionOp(VersionChangeTransaction* aTransaction)
7991     : TransactionDatabaseOperationBase(aTransaction)
7992   { }
7993 
7994   virtual
~VersionChangeTransactionOp()7995   ~VersionChangeTransactionOp()
7996   { }
7997 
7998 private:
7999   virtual nsresult
8000   SendSuccessResult() override;
8001 
8002   virtual bool
8003   SendFailureResult(nsresult aResultCode) override;
8004 };
8005 
8006 class CreateObjectStoreOp final
8007   : public VersionChangeTransactionOp
8008 {
8009   friend class VersionChangeTransaction;
8010 
8011   const ObjectStoreMetadata mMetadata;
8012 
8013 private:
8014   // Only created by VersionChangeTransaction.
CreateObjectStoreOp(VersionChangeTransaction * aTransaction,const ObjectStoreMetadata & aMetadata)8015   CreateObjectStoreOp(VersionChangeTransaction* aTransaction,
8016                       const ObjectStoreMetadata& aMetadata)
8017     : VersionChangeTransactionOp(aTransaction)
8018     , mMetadata(aMetadata)
8019   {
8020     MOZ_ASSERT(aMetadata.id());
8021   }
8022 
~CreateObjectStoreOp()8023   ~CreateObjectStoreOp()
8024   { }
8025 
8026   virtual nsresult
8027   DoDatabaseWork(DatabaseConnection* aConnection) override;
8028 };
8029 
8030 class DeleteObjectStoreOp final
8031   : public VersionChangeTransactionOp
8032 {
8033   friend class VersionChangeTransaction;
8034 
8035   const RefPtr<FullObjectStoreMetadata> mMetadata;
8036   const bool mIsLastObjectStore;
8037 
8038 private:
8039   // Only created by VersionChangeTransaction.
DeleteObjectStoreOp(VersionChangeTransaction * aTransaction,FullObjectStoreMetadata * const aMetadata,const bool aIsLastObjectStore)8040   DeleteObjectStoreOp(VersionChangeTransaction* aTransaction,
8041                       FullObjectStoreMetadata* const aMetadata,
8042                       const bool aIsLastObjectStore)
8043     : VersionChangeTransactionOp(aTransaction)
8044     , mMetadata(aMetadata)
8045     , mIsLastObjectStore(aIsLastObjectStore)
8046   {
8047     MOZ_ASSERT(aMetadata->mCommonMetadata.id());
8048   }
8049 
~DeleteObjectStoreOp()8050   ~DeleteObjectStoreOp()
8051   { }
8052 
8053   virtual nsresult
8054   DoDatabaseWork(DatabaseConnection* aConnection) override;
8055 };
8056 
8057 class RenameObjectStoreOp final
8058   : public VersionChangeTransactionOp
8059 {
8060   friend class VersionChangeTransaction;
8061 
8062   const int64_t mId;
8063   const nsString mNewName;
8064 
8065 private:
8066   // Only created by VersionChangeTransaction.
RenameObjectStoreOp(VersionChangeTransaction * aTransaction,FullObjectStoreMetadata * const aMetadata)8067   RenameObjectStoreOp(VersionChangeTransaction* aTransaction,
8068                       FullObjectStoreMetadata* const aMetadata)
8069     : VersionChangeTransactionOp(aTransaction)
8070     , mId(aMetadata->mCommonMetadata.id())
8071     , mNewName(aMetadata->mCommonMetadata.name())
8072   {
8073     MOZ_ASSERT(mId);
8074   }
8075 
~RenameObjectStoreOp()8076   ~RenameObjectStoreOp()
8077   { }
8078 
8079   virtual nsresult
8080   DoDatabaseWork(DatabaseConnection* aConnection) override;
8081 };
8082 
8083 class CreateIndexOp final
8084   : public VersionChangeTransactionOp
8085 {
8086   friend class VersionChangeTransaction;
8087 
8088   class ThreadLocalJSContext;
8089   class UpdateIndexDataValuesFunction;
8090 
8091   static const unsigned int kBadThreadLocalIndex =
8092     static_cast<unsigned int>(-1);
8093 
8094   static unsigned int sThreadLocalIndex;
8095 
8096   const IndexMetadata mMetadata;
8097   Maybe<UniqueIndexTable> mMaybeUniqueIndexTable;
8098   RefPtr<FileManager> mFileManager;
8099   const nsCString mDatabaseId;
8100   const uint64_t mObjectStoreId;
8101 
8102 private:
8103   // Only created by VersionChangeTransaction.
8104   CreateIndexOp(VersionChangeTransaction* aTransaction,
8105                 const int64_t aObjectStoreId,
8106                 const IndexMetadata& aMetadata);
8107 
~CreateIndexOp()8108   ~CreateIndexOp()
8109   { }
8110 
8111   nsresult
8112   InsertDataFromObjectStore(DatabaseConnection* aConnection);
8113 
8114   nsresult
8115   InsertDataFromObjectStoreInternal(DatabaseConnection* aConnection);
8116 
8117   virtual bool
8118   Init(TransactionBase* aTransaction) override;
8119 
8120   virtual nsresult
8121   DoDatabaseWork(DatabaseConnection* aConnection) override;
8122 };
8123 
8124 class NormalJSContext
8125 {
8126   friend class nsAutoPtr<NormalJSContext>;
8127 
8128   static const JSClass sGlobalClass;
8129   static const uint32_t kContextHeapSize = 768 * 1024;
8130 
8131   JSContext* mContext;
8132   JSObject* mGlobal;
8133 
8134 public:
8135   static NormalJSContext*
8136   Create();
8137 
8138   JSContext*
Context() const8139   Context() const
8140   {
8141     return mContext;
8142   }
8143 
8144   JSObject*
Global() const8145   Global() const
8146   {
8147     return mGlobal;
8148   }
8149 
8150 protected:
NormalJSContext()8151   NormalJSContext()
8152     : mContext(nullptr)
8153     , mGlobal(nullptr)
8154   {
8155     MOZ_COUNT_CTOR(NormalJSContext);
8156   }
8157 
~NormalJSContext()8158   ~NormalJSContext()
8159   {
8160     MOZ_COUNT_DTOR(NormalJSContext);
8161 
8162     if (mContext) {
8163       JS_DestroyContext(mContext);
8164     }
8165   }
8166 
8167   bool
8168   Init();
8169 };
8170 
8171 class CreateIndexOp::ThreadLocalJSContext final
8172   : public NormalJSContext
8173 {
8174   friend class CreateIndexOp;
8175   friend class nsAutoPtr<ThreadLocalJSContext>;
8176 
8177 public:
8178   static ThreadLocalJSContext*
8179   GetOrCreate();
8180 
8181 private:
ThreadLocalJSContext()8182   ThreadLocalJSContext()
8183   {
8184     MOZ_COUNT_CTOR(CreateIndexOp::ThreadLocalJSContext);
8185   }
8186 
~ThreadLocalJSContext()8187   ~ThreadLocalJSContext()
8188   {
8189     MOZ_COUNT_DTOR(CreateIndexOp::ThreadLocalJSContext);
8190   }
8191 };
8192 
8193 class CreateIndexOp::UpdateIndexDataValuesFunction final
8194   : public mozIStorageFunction
8195 {
8196   RefPtr<CreateIndexOp> mOp;
8197   RefPtr<DatabaseConnection> mConnection;
8198   JSContext* mCx;
8199 
8200 public:
UpdateIndexDataValuesFunction(CreateIndexOp * aOp,DatabaseConnection * aConnection,JSContext * aCx)8201   UpdateIndexDataValuesFunction(CreateIndexOp* aOp,
8202                                 DatabaseConnection* aConnection,
8203                                 JSContext* aCx)
8204     : mOp(aOp)
8205     , mConnection(aConnection)
8206     , mCx(aCx)
8207   {
8208     MOZ_ASSERT(aOp);
8209     MOZ_ASSERT(aConnection);
8210     aConnection->AssertIsOnConnectionThread();
8211     MOZ_ASSERT(aCx);
8212   }
8213 
8214   NS_DECL_ISUPPORTS
8215 
8216 private:
~UpdateIndexDataValuesFunction()8217   ~UpdateIndexDataValuesFunction()
8218   { }
8219 
8220   NS_DECL_MOZISTORAGEFUNCTION
8221 };
8222 
8223 class DeleteIndexOp final
8224   : public VersionChangeTransactionOp
8225 {
8226   friend class VersionChangeTransaction;
8227 
8228   const int64_t mObjectStoreId;
8229   const int64_t mIndexId;
8230   const bool mUnique;
8231   const bool mIsLastIndex;
8232 
8233 private:
8234   // Only created by VersionChangeTransaction.
8235   DeleteIndexOp(VersionChangeTransaction* aTransaction,
8236                 const int64_t aObjectStoreId,
8237                 const int64_t aIndexId,
8238                 const bool aUnique,
8239                 const bool aIsLastIndex);
8240 
~DeleteIndexOp()8241   ~DeleteIndexOp()
8242   { }
8243 
8244   nsresult
8245   RemoveReferencesToIndex(DatabaseConnection* aConnection,
8246                           const Key& aObjectDataKey,
8247                           nsTArray<IndexDataValue>& aIndexValues);
8248 
8249   virtual nsresult
8250   DoDatabaseWork(DatabaseConnection* aConnection) override;
8251 };
8252 
8253 class RenameIndexOp final
8254   : public VersionChangeTransactionOp
8255 {
8256   friend class VersionChangeTransaction;
8257 
8258   const int64_t mObjectStoreId;
8259   const int64_t mIndexId;
8260   const nsString mNewName;
8261 
8262 private:
8263   // Only created by VersionChangeTransaction.
RenameIndexOp(VersionChangeTransaction * aTransaction,FullIndexMetadata * const aMetadata,int64_t aObjectStoreId)8264   RenameIndexOp(VersionChangeTransaction* aTransaction,
8265                 FullIndexMetadata* const aMetadata,
8266                 int64_t aObjectStoreId)
8267     : VersionChangeTransactionOp(aTransaction)
8268     , mObjectStoreId(aObjectStoreId)
8269     , mIndexId(aMetadata->mCommonMetadata.id())
8270     , mNewName(aMetadata->mCommonMetadata.name())
8271   {
8272     MOZ_ASSERT(mIndexId);
8273   }
8274 
~RenameIndexOp()8275   ~RenameIndexOp()
8276   { }
8277 
8278   virtual nsresult
8279   DoDatabaseWork(DatabaseConnection* aConnection) override;
8280 };
8281 
8282 class NormalTransactionOp
8283   : public TransactionDatabaseOperationBase
8284   , public PBackgroundIDBRequestParent
8285 {
8286 #ifdef DEBUG
8287   bool mResponseSent;
8288 #endif
8289 
8290 public:
8291   virtual void
8292   Cleanup() override;
8293 
8294 protected:
NormalTransactionOp(TransactionBase * aTransaction)8295   explicit NormalTransactionOp(TransactionBase* aTransaction)
8296     : TransactionDatabaseOperationBase(aTransaction)
8297 #ifdef DEBUG
8298     , mResponseSent(false)
8299 #endif
8300   { }
8301 
8302   virtual
~NormalTransactionOp()8303   ~NormalTransactionOp()
8304   { }
8305 
8306   // An overload of DatabaseOperationBase's function that can avoid doing extra
8307   // work on non-versionchange transactions.
8308   static nsresult
8309   ObjectStoreHasIndexes(NormalTransactionOp* aOp,
8310                         DatabaseConnection* aConnection,
8311                         const int64_t aObjectStoreId,
8312                         const bool aMayHaveIndexes,
8313                         bool* aHasIndexes);
8314 
8315   virtual nsresult
8316   GetPreprocessParams(PreprocessParams& aParams);
8317 
8318 
8319   // Subclasses use this override to set the IPDL response value.
8320   virtual void
8321   GetResponse(RequestResponse& aResponse) = 0;
8322 
8323 private:
8324   virtual nsresult
8325   SendPreprocessInfo() override;
8326 
8327   virtual nsresult
8328   SendSuccessResult() override;
8329 
8330   virtual bool
8331   SendFailureResult(nsresult aResultCode) override;
8332 
8333   // IPDL methods.
8334   virtual void
8335   ActorDestroy(ActorDestroyReason aWhy) override;
8336 
8337   virtual bool
8338   RecvContinue(const PreprocessResponse& aResponse) override;
8339 };
8340 
8341 class ObjectStoreAddOrPutRequestOp final
8342   : public NormalTransactionOp
8343 {
8344   friend class TransactionBase;
8345 
8346   typedef mozilla::dom::quota::PersistenceType PersistenceType;
8347 
8348   struct StoredFileInfo;
8349   class SCInputStream;
8350 
8351   const ObjectStoreAddPutParams mParams;
8352   Maybe<UniqueIndexTable> mUniqueIndexTable;
8353 
8354   // This must be non-const so that we can update the mNextAutoIncrementId field
8355   // if we are modifying an autoIncrement objectStore.
8356   RefPtr<FullObjectStoreMetadata> mMetadata;
8357 
8358   FallibleTArray<StoredFileInfo> mStoredFileInfos;
8359 
8360   Key mResponse;
8361   const nsCString mGroup;
8362   const nsCString mOrigin;
8363   const PersistenceType mPersistenceType;
8364   const bool mOverwrite;
8365   bool mObjectStoreMayHaveIndexes;
8366   bool mDataOverThreshold;
8367 
8368 private:
8369   // Only created by TransactionBase.
8370   ObjectStoreAddOrPutRequestOp(TransactionBase* aTransaction,
8371                                const RequestParams& aParams);
8372 
~ObjectStoreAddOrPutRequestOp()8373   ~ObjectStoreAddOrPutRequestOp()
8374   { }
8375 
8376   nsresult
8377   RemoveOldIndexDataValues(DatabaseConnection* aConnection);
8378 
8379   virtual bool
8380   Init(TransactionBase* aTransaction) override;
8381 
8382   virtual nsresult
8383   DoDatabaseWork(DatabaseConnection* aConnection) override;
8384 
8385   virtual void
8386   GetResponse(RequestResponse& aResponse) override;
8387 
8388   virtual void
8389   Cleanup() override;
8390 };
8391 
8392 struct ObjectStoreAddOrPutRequestOp::StoredFileInfo final
8393 {
8394   RefPtr<DatabaseFile> mFileActor;
8395   RefPtr<FileInfo> mFileInfo;
8396   // A non-Blob-backed inputstream to write to disk.  If null, mFileActor may
8397   // still have a stream for us to write.
8398   nsCOMPtr<nsIInputStream> mInputStream;
8399   StructuredCloneFile::FileType mType;
8400 
StoredFileInfomozilla::dom::indexedDB::__anone276c2b20111::ObjectStoreAddOrPutRequestOp::StoredFileInfo8401   StoredFileInfo()
8402     : mType(StructuredCloneFile::eBlob)
8403   {
8404     AssertIsOnBackgroundThread();
8405 
8406     MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
8407   }
8408 
~StoredFileInfomozilla::dom::indexedDB::__anone276c2b20111::ObjectStoreAddOrPutRequestOp::StoredFileInfo8409   ~StoredFileInfo()
8410   {
8411     AssertIsOnBackgroundThread();
8412 
8413     MOZ_COUNT_DTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
8414   }
8415 
8416   void
Serializemozilla::dom::indexedDB::__anone276c2b20111::ObjectStoreAddOrPutRequestOp::StoredFileInfo8417   Serialize(nsString& aText)
8418   {
8419     MOZ_ASSERT(mFileInfo);
8420 
8421     const int64_t id = mFileInfo->Id();
8422 
8423     switch (mType) {
8424       case StructuredCloneFile::eBlob:
8425         aText.AppendInt(id);
8426         break;
8427 
8428       case StructuredCloneFile::eMutableFile:
8429         aText.AppendInt(-id);
8430         break;
8431 
8432       case StructuredCloneFile::eStructuredClone:
8433         aText.Append('.');
8434         aText.AppendInt(id);
8435         break;
8436 
8437       case StructuredCloneFile::eWasmBytecode:
8438         aText.Append('/');
8439         aText.AppendInt(id);
8440         break;
8441 
8442       case StructuredCloneFile::eWasmCompiled:
8443         aText.Append('\\');
8444         aText.AppendInt(id);
8445         break;
8446 
8447       default:
8448         MOZ_CRASH("Should never get here!");
8449     }
8450   }
8451 };
8452 
8453 class ObjectStoreAddOrPutRequestOp::SCInputStream final
8454   : public nsIInputStream
8455 {
8456   const JSStructuredCloneData& mData;
8457   JSStructuredCloneData::IterImpl mIter;
8458 
8459 public:
SCInputStream(const JSStructuredCloneData & aData)8460   explicit SCInputStream(const JSStructuredCloneData& aData)
8461     : mData(aData)
8462     , mIter(aData.Iter())
8463   { }
8464 
8465 private:
~SCInputStream()8466   virtual ~SCInputStream()
8467   { }
8468 
8469   NS_DECL_THREADSAFE_ISUPPORTS
8470   NS_DECL_NSIINPUTSTREAM
8471 };
8472 
8473 class ObjectStoreGetRequestOp final
8474   : public NormalTransactionOp
8475 {
8476   friend class TransactionBase;
8477 
8478   const uint32_t mObjectStoreId;
8479   RefPtr<Database> mDatabase;
8480   const OptionalKeyRange mOptionalKeyRange;
8481   AutoTArray<StructuredCloneReadInfo, 1> mResponse;
8482   PBackgroundParent* mBackgroundParent;
8483   uint32_t mPreprocessInfoCount;
8484   const uint32_t mLimit;
8485   const bool mGetAll;
8486 
8487 private:
8488   // Only created by TransactionBase.
8489   ObjectStoreGetRequestOp(TransactionBase* aTransaction,
8490                           const RequestParams& aParams,
8491                           bool aGetAll);
8492 
~ObjectStoreGetRequestOp()8493   ~ObjectStoreGetRequestOp()
8494   { }
8495 
8496   template <bool aForPreprocess, typename T>
8497   nsresult
8498   ConvertResponse(StructuredCloneReadInfo& aInfo, T& aResult);
8499 
8500   virtual nsresult
8501   DoDatabaseWork(DatabaseConnection* aConnection) override;
8502 
8503   virtual bool
8504   HasPreprocessInfo() override;
8505 
8506   virtual nsresult
8507   GetPreprocessParams(PreprocessParams& aParams) override;
8508 
8509   virtual void
8510   GetResponse(RequestResponse& aResponse) override;
8511 };
8512 
8513 class ObjectStoreGetKeyRequestOp final
8514   : public NormalTransactionOp
8515 {
8516   friend class TransactionBase;
8517 
8518   const uint32_t mObjectStoreId;
8519   const OptionalKeyRange mOptionalKeyRange;
8520   const uint32_t mLimit;
8521   const bool mGetAll;
8522   FallibleTArray<Key> mResponse;
8523 
8524 private:
8525   // Only created by TransactionBase.
8526   ObjectStoreGetKeyRequestOp(TransactionBase* aTransaction,
8527                              const RequestParams& aParams,
8528                              bool aGetAll);
8529 
~ObjectStoreGetKeyRequestOp()8530   ~ObjectStoreGetKeyRequestOp()
8531   { }
8532 
8533   virtual nsresult
8534   DoDatabaseWork(DatabaseConnection* aConnection) override;
8535 
8536   virtual void
8537   GetResponse(RequestResponse& aResponse) override;
8538 };
8539 
8540 class ObjectStoreDeleteRequestOp final
8541   : public NormalTransactionOp
8542 {
8543   friend class TransactionBase;
8544 
8545   const ObjectStoreDeleteParams mParams;
8546   ObjectStoreDeleteResponse mResponse;
8547   bool mObjectStoreMayHaveIndexes;
8548 
8549 private:
8550   ObjectStoreDeleteRequestOp(TransactionBase* aTransaction,
8551                              const ObjectStoreDeleteParams& aParams);
8552 
~ObjectStoreDeleteRequestOp()8553   ~ObjectStoreDeleteRequestOp()
8554   { }
8555 
8556   virtual nsresult
8557   DoDatabaseWork(DatabaseConnection* aConnection) override;
8558 
8559   virtual void
GetResponse(RequestResponse & aResponse)8560   GetResponse(RequestResponse& aResponse) override
8561   {
8562     aResponse = Move(mResponse);
8563   }
8564 };
8565 
8566 class ObjectStoreClearRequestOp final
8567   : public NormalTransactionOp
8568 {
8569   friend class TransactionBase;
8570 
8571   const ObjectStoreClearParams mParams;
8572   ObjectStoreClearResponse mResponse;
8573   bool mObjectStoreMayHaveIndexes;
8574 
8575 private:
8576   ObjectStoreClearRequestOp(TransactionBase* aTransaction,
8577                             const ObjectStoreClearParams& aParams);
8578 
~ObjectStoreClearRequestOp()8579   ~ObjectStoreClearRequestOp()
8580   { }
8581 
8582   virtual nsresult
8583   DoDatabaseWork(DatabaseConnection* aConnection) override;
8584 
8585   virtual void
GetResponse(RequestResponse & aResponse)8586   GetResponse(RequestResponse& aResponse) override
8587   {
8588     aResponse = Move(mResponse);
8589   }
8590 };
8591 
8592 class ObjectStoreCountRequestOp final
8593   : public NormalTransactionOp
8594 {
8595   friend class TransactionBase;
8596 
8597   const ObjectStoreCountParams mParams;
8598   ObjectStoreCountResponse mResponse;
8599 
8600 private:
ObjectStoreCountRequestOp(TransactionBase * aTransaction,const ObjectStoreCountParams & aParams)8601   ObjectStoreCountRequestOp(TransactionBase* aTransaction,
8602                             const ObjectStoreCountParams& aParams)
8603     : NormalTransactionOp(aTransaction)
8604     , mParams(aParams)
8605   { }
8606 
~ObjectStoreCountRequestOp()8607   ~ObjectStoreCountRequestOp()
8608   { }
8609 
8610   virtual nsresult
8611   DoDatabaseWork(DatabaseConnection* aConnection) override;
8612 
8613   virtual void
GetResponse(RequestResponse & aResponse)8614   GetResponse(RequestResponse& aResponse) override
8615   {
8616     aResponse = Move(mResponse);
8617   }
8618 };
8619 
8620 class IndexRequestOpBase
8621   : public NormalTransactionOp
8622 {
8623 protected:
8624   const RefPtr<FullIndexMetadata> mMetadata;
8625 
8626 protected:
IndexRequestOpBase(TransactionBase * aTransaction,const RequestParams & aParams)8627   IndexRequestOpBase(TransactionBase* aTransaction,
8628                      const RequestParams& aParams)
8629     : NormalTransactionOp(aTransaction)
8630     , mMetadata(IndexMetadataForParams(aTransaction, aParams))
8631   { }
8632 
8633   virtual
~IndexRequestOpBase()8634   ~IndexRequestOpBase()
8635   { }
8636 
8637 private:
8638   static already_AddRefed<FullIndexMetadata>
8639   IndexMetadataForParams(TransactionBase* aTransaction,
8640                          const RequestParams& aParams);
8641 };
8642 
8643 class IndexGetRequestOp final
8644   : public IndexRequestOpBase
8645 {
8646   friend class TransactionBase;
8647 
8648   RefPtr<Database> mDatabase;
8649   const OptionalKeyRange mOptionalKeyRange;
8650   AutoTArray<StructuredCloneReadInfo, 1> mResponse;
8651   PBackgroundParent* mBackgroundParent;
8652   const uint32_t mLimit;
8653   const bool mGetAll;
8654 
8655 private:
8656   // Only created by TransactionBase.
8657   IndexGetRequestOp(TransactionBase* aTransaction,
8658                     const RequestParams& aParams,
8659                     bool aGetAll);
8660 
~IndexGetRequestOp()8661   ~IndexGetRequestOp()
8662   { }
8663 
8664   virtual nsresult
8665   DoDatabaseWork(DatabaseConnection* aConnection) override;
8666 
8667   virtual void
8668   GetResponse(RequestResponse& aResponse) override;
8669 };
8670 
8671 class IndexGetKeyRequestOp final
8672   : public IndexRequestOpBase
8673 {
8674   friend class TransactionBase;
8675 
8676   const OptionalKeyRange mOptionalKeyRange;
8677   AutoTArray<Key, 1> mResponse;
8678   const uint32_t mLimit;
8679   const bool mGetAll;
8680 
8681 private:
8682   // Only created by TransactionBase.
8683   IndexGetKeyRequestOp(TransactionBase* aTransaction,
8684                        const RequestParams& aParams,
8685                        bool aGetAll);
8686 
~IndexGetKeyRequestOp()8687   ~IndexGetKeyRequestOp()
8688   { }
8689 
8690   virtual nsresult
8691   DoDatabaseWork(DatabaseConnection* aConnection) override;
8692 
8693   virtual void
8694   GetResponse(RequestResponse& aResponse) override;
8695 };
8696 
8697 class IndexCountRequestOp final
8698   : public IndexRequestOpBase
8699 {
8700   friend class TransactionBase;
8701 
8702   const IndexCountParams mParams;
8703   IndexCountResponse mResponse;
8704 
8705 private:
8706   // Only created by TransactionBase.
IndexCountRequestOp(TransactionBase * aTransaction,const RequestParams & aParams)8707   IndexCountRequestOp(TransactionBase* aTransaction,
8708                       const RequestParams& aParams)
8709     : IndexRequestOpBase(aTransaction, aParams)
8710     , mParams(aParams.get_IndexCountParams())
8711   { }
8712 
~IndexCountRequestOp()8713   ~IndexCountRequestOp()
8714   { }
8715 
8716   virtual nsresult
8717   DoDatabaseWork(DatabaseConnection* aConnection) override;
8718 
8719   virtual void
GetResponse(RequestResponse & aResponse)8720   GetResponse(RequestResponse& aResponse) override
8721   {
8722     aResponse = Move(mResponse);
8723   }
8724 };
8725 
8726 class Cursor final :
8727     public PBackgroundIDBCursorParent
8728 {
8729   friend class TransactionBase;
8730 
8731   class ContinueOp;
8732   class CursorOpBase;
8733   class OpenOp;
8734 
8735 public:
8736   typedef OpenCursorParams::Type Type;
8737 
8738 private:
8739   RefPtr<TransactionBase> mTransaction;
8740   RefPtr<Database> mDatabase;
8741   RefPtr<FileManager> mFileManager;
8742   PBackgroundParent* mBackgroundParent;
8743 
8744   // These should only be touched on the PBackground thread to check whether the
8745   // objectStore or index has been deleted. Holding these saves a hash lookup
8746   // for every call to continue()/advance().
8747   RefPtr<FullObjectStoreMetadata> mObjectStoreMetadata;
8748   RefPtr<FullIndexMetadata> mIndexMetadata;
8749 
8750   const int64_t mObjectStoreId;
8751   const int64_t mIndexId;
8752 
8753   nsCString mContinueQuery;
8754   nsCString mContinueToQuery;
8755   nsCString mContinuePrimaryKeyQuery;
8756   nsCString mLocale;
8757 
8758   Key mKey;
8759   Key mObjectKey;
8760   Key mRangeKey;
8761   Key mSortKey;
8762 
8763   CursorOpBase* mCurrentlyRunningOp;
8764 
8765   const Type mType;
8766   const Direction mDirection;
8767 
8768   const bool mUniqueIndex;
8769   const bool mIsSameProcessActor;
8770   bool mActorDestroyed;
8771 
8772 public:
8773   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Cursor)
8774 
8775 private:
8776   // Only created by TransactionBase.
8777   Cursor(TransactionBase* aTransaction,
8778          Type aType,
8779          FullObjectStoreMetadata* aObjectStoreMetadata,
8780          FullIndexMetadata* aIndexMetadata,
8781          Direction aDirection);
8782 
8783   // Reference counted.
~Cursor()8784   ~Cursor()
8785   {
8786     MOZ_ASSERT(mActorDestroyed);
8787   }
8788 
8789   bool
8790   VerifyRequestParams(const CursorRequestParams& aParams) const;
8791 
8792   // Only called by TransactionBase.
8793   bool
8794   Start(const OpenCursorParams& aParams);
8795 
8796   void
8797   SendResponseInternal(
8798     CursorResponse& aResponse,
8799     const nsTArray<FallibleTArray<StructuredCloneFile>>& aFiles);
8800 
8801   // Must call SendResponseInternal!
8802   bool
8803   SendResponse(const CursorResponse& aResponse) = delete;
8804 
8805   // IPDL methods.
8806   virtual void
8807   ActorDestroy(ActorDestroyReason aWhy) override;
8808 
8809   virtual bool
8810   RecvDeleteMe() override;
8811 
8812   virtual bool
8813   RecvContinue(const CursorRequestParams& aParams) override;
8814 
8815   bool
IsLocaleAware() const8816   IsLocaleAware() const {
8817     return !mLocale.IsEmpty();
8818   }
8819 };
8820 
8821 class Cursor::CursorOpBase
8822   : public TransactionDatabaseOperationBase
8823 {
8824 protected:
8825   RefPtr<Cursor> mCursor;
8826   nsTArray<FallibleTArray<StructuredCloneFile>> mFiles;
8827 
8828   CursorResponse mResponse;
8829 
8830 #ifdef DEBUG
8831   bool mResponseSent;
8832 #endif
8833 
8834 protected:
CursorOpBase(Cursor * aCursor)8835   explicit CursorOpBase(Cursor* aCursor)
8836     : TransactionDatabaseOperationBase(aCursor->mTransaction)
8837     , mCursor(aCursor)
8838 #ifdef DEBUG
8839     , mResponseSent(false)
8840 #endif
8841   {
8842     AssertIsOnBackgroundThread();
8843     MOZ_ASSERT(aCursor);
8844   }
8845 
8846   virtual
~CursorOpBase()8847   ~CursorOpBase()
8848   { }
8849 
8850   virtual bool
8851   SendFailureResult(nsresult aResultCode) override;
8852 
8853   virtual void
8854   Cleanup() override;
8855 
8856   nsresult
8857   PopulateResponseFromStatement(DatabaseConnection::CachedStatement& aStmt,
8858                                 bool aInitializeResponse);
8859 };
8860 
8861 class Cursor::OpenOp final
8862   : public Cursor::CursorOpBase
8863 {
8864   friend class Cursor;
8865 
8866   const OptionalKeyRange mOptionalKeyRange;
8867 
8868 private:
8869   // Only created by Cursor.
OpenOp(Cursor * aCursor,const OptionalKeyRange & aOptionalKeyRange)8870   OpenOp(Cursor* aCursor,
8871          const OptionalKeyRange& aOptionalKeyRange)
8872     : CursorOpBase(aCursor)
8873     , mOptionalKeyRange(aOptionalKeyRange)
8874   { }
8875 
8876   // Reference counted.
~OpenOp()8877   ~OpenOp()
8878   { }
8879 
8880   void
8881   GetRangeKeyInfo(bool aLowerBound, Key* aKey, bool* aOpen);
8882 
8883   nsresult
8884   DoObjectStoreDatabaseWork(DatabaseConnection* aConnection);
8885 
8886   nsresult
8887   DoObjectStoreKeyDatabaseWork(DatabaseConnection* aConnection);
8888 
8889   nsresult
8890   DoIndexDatabaseWork(DatabaseConnection* aConnection);
8891 
8892   nsresult
8893   DoIndexKeyDatabaseWork(DatabaseConnection* aConnection);
8894 
8895   virtual nsresult
8896   DoDatabaseWork(DatabaseConnection* aConnection) override;
8897 
8898   virtual nsresult
8899   SendSuccessResult() override;
8900 };
8901 
8902 class Cursor::ContinueOp final
8903   : public Cursor::CursorOpBase
8904 {
8905   friend class Cursor;
8906 
8907   const CursorRequestParams mParams;
8908 
8909 private:
8910   // Only created by Cursor.
ContinueOp(Cursor * aCursor,const CursorRequestParams & aParams)8911   ContinueOp(Cursor* aCursor,
8912              const CursorRequestParams& aParams)
8913     : CursorOpBase(aCursor)
8914     , mParams(aParams)
8915   {
8916     MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None);
8917   }
8918 
8919   // Reference counted.
~ContinueOp()8920   ~ContinueOp()
8921   { }
8922 
8923   virtual nsresult
8924   DoDatabaseWork(DatabaseConnection* aConnection) override;
8925 
8926   virtual nsresult
8927   SendSuccessResult() override;
8928 };
8929 
8930 class Utils final
8931   : public PBackgroundIndexedDBUtilsParent
8932 {
8933 #ifdef DEBUG
8934   bool mActorDestroyed;
8935 #endif
8936 
8937 public:
8938   Utils();
8939 
8940   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Utils)
8941 
8942 private:
8943   // Reference counted.
8944   ~Utils();
8945 
8946   // IPDL methods are only called by IPDL.
8947   virtual void
8948   ActorDestroy(ActorDestroyReason aWhy) override;
8949 
8950   virtual bool
8951   RecvDeleteMe() override;
8952 
8953   virtual bool
8954   RecvGetFileReferences(const PersistenceType& aPersistenceType,
8955                         const nsCString& aOrigin,
8956                         const nsString& aDatabaseName,
8957                         const int64_t& aFileId,
8958                         int32_t* aRefCnt,
8959                         int32_t* aDBRefCnt,
8960                         int32_t* aSliceRefCnt,
8961                         bool* aResult) override;
8962 };
8963 
8964 class GetFileReferencesHelper final
8965   : public Runnable
8966 {
8967   PersistenceType mPersistenceType;
8968   nsCString mOrigin;
8969   nsString mDatabaseName;
8970   int64_t mFileId;
8971 
8972   mozilla::Mutex mMutex;
8973   mozilla::CondVar mCondVar;
8974   int32_t mMemRefCnt;
8975   int32_t mDBRefCnt;
8976   int32_t mSliceRefCnt;
8977   bool mResult;
8978   bool mWaiting;
8979 
8980 public:
GetFileReferencesHelper(PersistenceType aPersistenceType,const nsACString & aOrigin,const nsAString & aDatabaseName,int64_t aFileId)8981   GetFileReferencesHelper(PersistenceType aPersistenceType,
8982                           const nsACString& aOrigin,
8983                           const nsAString& aDatabaseName,
8984                           int64_t aFileId)
8985     : mPersistenceType(aPersistenceType)
8986     , mOrigin(aOrigin)
8987     , mDatabaseName(aDatabaseName)
8988     , mFileId(aFileId)
8989     , mMutex("GetFileReferencesHelper::mMutex")
8990     , mCondVar(mMutex, "GetFileReferencesHelper::mCondVar")
8991     , mMemRefCnt(-1)
8992     , mDBRefCnt(-1)
8993     , mSliceRefCnt(-1)
8994     , mResult(false)
8995     , mWaiting(true)
8996   { }
8997 
8998   nsresult
8999   DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
9000                                   int32_t* aDBRefCnt,
9001                                   int32_t* aSliceRefCnt,
9002                                   bool* aResult);
9003 
9004 private:
~GetFileReferencesHelper()9005   ~GetFileReferencesHelper() {}
9006 
9007   NS_DECL_NSIRUNNABLE
9008 };
9009 
9010 class FlushPendingFileDeletionsRunnable final
9011   : public Runnable
9012 {
9013 private:
~FlushPendingFileDeletionsRunnable()9014   ~FlushPendingFileDeletionsRunnable()
9015   { }
9016 
9017   NS_DECL_NSIRUNNABLE
9018 };
9019 
9020 class PermissionRequestHelper final
9021   : public PermissionRequestBase
9022   , public PIndexedDBPermissionRequestParent
9023 {
9024   bool mActorDestroyed;
9025 
9026 public:
PermissionRequestHelper(Element * aOwnerElement,nsIPrincipal * aPrincipal)9027   PermissionRequestHelper(Element* aOwnerElement,
9028                           nsIPrincipal* aPrincipal)
9029     : PermissionRequestBase(aOwnerElement, aPrincipal)
9030     , mActorDestroyed(false)
9031   { }
9032 
9033 protected:
~PermissionRequestHelper()9034   ~PermissionRequestHelper()
9035   { }
9036 
9037 private:
9038   virtual void
9039   OnPromptComplete(PermissionValue aPermissionValue) override;
9040 
9041   virtual void
9042   ActorDestroy(ActorDestroyReason aWhy) override;
9043 };
9044 
9045 /*******************************************************************************
9046  * Other class declarations
9047  ******************************************************************************/
9048 
9049 struct DatabaseActorInfo final
9050 {
9051   friend class nsAutoPtr<DatabaseActorInfo>;
9052 
9053   RefPtr<FullDatabaseMetadata> mMetadata;
9054   nsTArray<Database*> mLiveDatabases;
9055   RefPtr<FactoryOp> mWaitingFactoryOp;
9056 
DatabaseActorInfomozilla::dom::indexedDB::__anone276c2b20111::DatabaseActorInfo9057   DatabaseActorInfo(FullDatabaseMetadata* aMetadata,
9058                     Database* aDatabase)
9059     : mMetadata(aMetadata)
9060   {
9061     MOZ_ASSERT(aDatabase);
9062 
9063     MOZ_COUNT_CTOR(DatabaseActorInfo);
9064 
9065     mLiveDatabases.AppendElement(aDatabase);
9066   }
9067 
9068 private:
~DatabaseActorInfomozilla::dom::indexedDB::__anone276c2b20111::DatabaseActorInfo9069   ~DatabaseActorInfo()
9070   {
9071     MOZ_ASSERT(mLiveDatabases.IsEmpty());
9072     MOZ_ASSERT(!mWaitingFactoryOp ||
9073                !mWaitingFactoryOp->HasBlockedDatabases());
9074 
9075     MOZ_COUNT_DTOR(DatabaseActorInfo);
9076   }
9077 };
9078 
9079 class DatabaseLoggingInfo final
9080 {
9081 #ifdef DEBUG
9082   // Just for potential warnings.
9083   friend class Factory;
9084 #endif
9085 
9086   LoggingInfo mLoggingInfo;
9087 
9088 public:
9089   explicit
DatabaseLoggingInfo(const LoggingInfo & aLoggingInfo)9090   DatabaseLoggingInfo(const LoggingInfo& aLoggingInfo)
9091     : mLoggingInfo(aLoggingInfo)
9092   {
9093     AssertIsOnBackgroundThread();
9094     MOZ_ASSERT(aLoggingInfo.nextTransactionSerialNumber());
9095     MOZ_ASSERT(aLoggingInfo.nextVersionChangeTransactionSerialNumber());
9096     MOZ_ASSERT(aLoggingInfo.nextRequestSerialNumber());
9097   }
9098 
9099   const nsID&
Id() const9100   Id() const
9101   {
9102     AssertIsOnBackgroundThread();
9103 
9104     return mLoggingInfo.backgroundChildLoggingId();
9105   }
9106 
9107   int64_t
NextTransactionSN(IDBTransaction::Mode aMode)9108   NextTransactionSN(IDBTransaction::Mode aMode)
9109   {
9110     AssertIsOnBackgroundThread();
9111     MOZ_ASSERT(mLoggingInfo.nextTransactionSerialNumber() < INT64_MAX);
9112     MOZ_ASSERT(mLoggingInfo.nextVersionChangeTransactionSerialNumber() >
9113                  INT64_MIN);
9114 
9115     if (aMode == IDBTransaction::VERSION_CHANGE) {
9116       return mLoggingInfo.nextVersionChangeTransactionSerialNumber()--;
9117     }
9118 
9119     return mLoggingInfo.nextTransactionSerialNumber()++;
9120   }
9121 
9122   uint64_t
NextRequestSN()9123   NextRequestSN()
9124   {
9125     AssertIsOnBackgroundThread();
9126     MOZ_ASSERT(mLoggingInfo.nextRequestSerialNumber() < UINT64_MAX);
9127 
9128     return mLoggingInfo.nextRequestSerialNumber()++;
9129   }
9130 
9131   NS_INLINE_DECL_REFCOUNTING(DatabaseLoggingInfo)
9132 
9133 private:
9134   ~DatabaseLoggingInfo();
9135 };
9136 
9137 class BlobImplStoredFile final
9138   : public BlobImplFile
9139 {
9140   RefPtr<FileInfo> mFileInfo;
9141   const bool mSnapshot;
9142 
9143 public:
BlobImplStoredFile(nsIFile * aFile,FileInfo * aFileInfo,bool aSnapshot)9144   BlobImplStoredFile(nsIFile* aFile, FileInfo* aFileInfo, bool aSnapshot)
9145     : BlobImplFile(aFile)
9146     , mFileInfo(aFileInfo)
9147     , mSnapshot(aSnapshot)
9148   {
9149     AssertIsOnBackgroundThread();
9150 
9151     // Getting the content type is not currently supported off the main thread.
9152     // This isn't a problem here because:
9153     //
9154     //   1. The real content type is stored in the structured clone data and
9155     //      that's all that the DOM will see. This blob's data will be updated
9156     //      during RecvSetMysteryBlobInfo().
9157     //   2. The nsExternalHelperAppService guesses the content type based only
9158     //      on the file extension. Our stored files have no extension so the
9159     //      current code path fails and sets the content type to the empty
9160     //      string.
9161     //
9162     // So, this is a hack to keep the nsExternalHelperAppService out of the
9163     // picture entirely. Eventually we should probably fix this some other way.
9164     mContentType.Truncate();
9165     mIsFile = false;
9166   }
9167 
9168   bool
IsShareable(FileManager * aFileManager) const9169   IsShareable(FileManager* aFileManager) const
9170   {
9171     AssertIsOnBackgroundThread();
9172 
9173     return mFileInfo->Manager() == aFileManager && !mSnapshot;
9174   }
9175 
9176   FileInfo*
GetFileInfo() const9177   GetFileInfo() const
9178   {
9179     AssertIsOnBackgroundThread();
9180 
9181     return mFileInfo;
9182   }
9183 
9184 private:
~BlobImplStoredFile()9185   ~BlobImplStoredFile()
9186   { }
9187 
9188   NS_DECL_ISUPPORTS_INHERITED
NS_DECLARE_STATIC_IID_ACCESSOR(BLOB_IMPL_STORED_FILE_IID)9189   NS_DECLARE_STATIC_IID_ACCESSOR(BLOB_IMPL_STORED_FILE_IID)
9190 
9191   virtual int64_t
9192   GetFileId() override
9193   {
9194     MOZ_ASSERT(mFileInfo);
9195 
9196     return mFileInfo->Id();
9197   }
9198 };
9199 
9200 NS_DEFINE_STATIC_IID_ACCESSOR(BlobImplStoredFile, BLOB_IMPL_STORED_FILE_IID)
9201 
9202 class QuotaClient final
9203   : public mozilla::dom::quota::Client
9204 {
9205   static QuotaClient* sInstance;
9206 
9207   nsCOMPtr<nsIEventTarget> mBackgroundThread;
9208   nsTArray<RefPtr<Maintenance>> mMaintenanceQueue;
9209   RefPtr<Maintenance> mCurrentMaintenance;
9210   RefPtr<nsThreadPool> mMaintenanceThreadPool;
9211   bool mShutdownRequested;
9212 
9213 public:
9214   QuotaClient();
9215 
9216   static QuotaClient*
GetInstance()9217   GetInstance()
9218   {
9219     AssertIsOnBackgroundThread();
9220 
9221     return sInstance;
9222   }
9223 
9224   static bool
IsShuttingDownOnBackgroundThread()9225   IsShuttingDownOnBackgroundThread()
9226   {
9227     AssertIsOnBackgroundThread();
9228 
9229     if (sInstance) {
9230       return sInstance->IsShuttingDown();
9231     }
9232 
9233     return QuotaManager::IsShuttingDown();
9234   }
9235 
9236   static bool
IsShuttingDownOnNonBackgroundThread()9237   IsShuttingDownOnNonBackgroundThread()
9238   {
9239     MOZ_ASSERT(!IsOnBackgroundThread());
9240 
9241     return QuotaManager::IsShuttingDown();
9242   }
9243 
9244   nsIEventTarget*
BackgroundThread() const9245   BackgroundThread() const
9246   {
9247     MOZ_ASSERT(mBackgroundThread);
9248     return mBackgroundThread;
9249   }
9250 
9251   bool
IsShuttingDown() const9252   IsShuttingDown() const
9253   {
9254     AssertIsOnBackgroundThread();
9255 
9256     return mShutdownRequested;
9257   }
9258 
9259   already_AddRefed<Maintenance>
GetCurrentMaintenance() const9260   GetCurrentMaintenance() const
9261   {
9262     RefPtr<Maintenance> result = mCurrentMaintenance;
9263     return result.forget();
9264   }
9265 
9266   void
NoteFinishedMaintenance(Maintenance * aMaintenance)9267   NoteFinishedMaintenance(Maintenance* aMaintenance)
9268   {
9269     AssertIsOnBackgroundThread();
9270     MOZ_ASSERT(aMaintenance);
9271     MOZ_ASSERT(mCurrentMaintenance == aMaintenance);
9272 
9273     mCurrentMaintenance = nullptr;
9274     ProcessMaintenanceQueue();
9275   }
9276 
9277   nsThreadPool*
9278   GetOrCreateThreadPool();
9279 
9280   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
9281 
9282   virtual mozilla::dom::quota::Client::Type
9283   GetType() override;
9284 
9285   virtual nsresult
9286   InitOrigin(PersistenceType aPersistenceType,
9287              const nsACString& aGroup,
9288              const nsACString& aOrigin,
9289              const AtomicBool& aCanceled,
9290              UsageInfo* aUsageInfo) override;
9291 
9292   virtual nsresult
9293   GetUsageForOrigin(PersistenceType aPersistenceType,
9294                     const nsACString& aGroup,
9295                     const nsACString& aOrigin,
9296                     const AtomicBool& aCanceled,
9297                     UsageInfo* aUsageInfo) override;
9298 
9299   virtual void
9300   OnOriginClearCompleted(PersistenceType aPersistenceType,
9301                          const nsACString& aOrigin)
9302                          override;
9303 
9304   virtual void
9305   ReleaseIOThreadObjects() override;
9306 
9307   virtual void
9308   AbortOperations(const nsACString& aOrigin) override;
9309 
9310   virtual void
9311   AbortOperationsForProcess(ContentParentId aContentParentId) override;
9312 
9313   virtual void
9314   StartIdleMaintenance() override;
9315 
9316   virtual void
9317   StopIdleMaintenance() override;
9318 
9319   virtual void
9320   ShutdownWorkThreads() override;
9321 
9322   virtual void
9323   DidInitialize(QuotaManager* aQuotaManager) override;
9324 
9325   virtual void
9326   WillShutdown() override;
9327 
9328 private:
9329   ~QuotaClient();
9330 
9331   nsresult
9332   GetDirectory(PersistenceType aPersistenceType,
9333                const nsACString& aOrigin,
9334                nsIFile** aDirectory);
9335 
9336   nsresult
9337   GetUsageForDirectoryInternal(nsIFile* aDirectory,
9338                                const AtomicBool& aCanceled,
9339                                UsageInfo* aUsageInfo,
9340                                bool aDatabaseFiles);
9341 
9342   // Runs on the PBackground thread. Checks to see if there's a queued
9343   // Maintenance to run.
9344   void
9345   ProcessMaintenanceQueue();
9346 };
9347 
9348 class Maintenance final
9349   : public Runnable
9350   , public OpenDirectoryListener
9351 {
9352   struct DirectoryInfo;
9353 
9354   enum class State
9355   {
9356     // Newly created on the PBackground thread. Will proceed immediately or be
9357     // added to the maintenance queue. The next step is either
9358     // DirectoryOpenPending if IndexedDatabaseManager is running, or
9359     // CreateIndexedDatabaseManager if not.
9360     Initial = 0,
9361 
9362     // Create IndexedDatabaseManager on the main thread. The next step is either
9363     // Finishing if IndexedDatabaseManager initialization fails, or
9364     // IndexedDatabaseManagerOpen if initialization succeeds.
9365     CreateIndexedDatabaseManager,
9366 
9367     // Call OpenDirectory() on the PBackground thread. The next step is
9368     // DirectoryOpenPending.
9369     IndexedDatabaseManagerOpen,
9370 
9371     // Waiting for directory open allowed on the PBackground thread. The next
9372     // step is either Finishing if directory lock failed to acquire, or
9373     // DirectoryWorkOpen if directory lock is acquired.
9374     DirectoryOpenPending,
9375 
9376     // Waiting to do/doing work on the QuotaManager IO thread. The next step is
9377     // BeginDatabaseMaintenance.
9378     DirectoryWorkOpen,
9379 
9380     // Dispatching a runnable for each database on the PBackground thread. The
9381     // next state is either WaitingForDatabaseMaintenancesToComplete if at least
9382     // one runnable has been dispatched, or Finishing otherwise.
9383     BeginDatabaseMaintenance,
9384 
9385     // Waiting for DatabaseMaintenance to finish on maintenance thread pool.
9386     // The next state is Finishing if the last runnable has finished.
9387     WaitingForDatabaseMaintenancesToComplete,
9388 
9389     // Waiting to finish/finishing on the PBackground thread. The next step is
9390     // Completed.
9391     Finishing,
9392 
9393     // All done.
9394     Complete
9395   };
9396 
9397   RefPtr<QuotaClient> mQuotaClient;
9398   PRTime mStartTime;
9399   RefPtr<DirectoryLock> mDirectoryLock;
9400   nsTArray<DirectoryInfo> mDirectoryInfos;
9401   nsDataHashtable<nsStringHashKey, DatabaseMaintenance*> mDatabaseMaintenances;
9402   nsresult mResultCode;
9403   Atomic<bool> mAborted;
9404   State mState;
9405 
9406 public:
Maintenance(QuotaClient * aQuotaClient)9407   explicit Maintenance(QuotaClient* aQuotaClient)
9408     : mQuotaClient(aQuotaClient)
9409     , mStartTime(PR_Now())
9410     , mResultCode(NS_OK)
9411     , mAborted(false)
9412     , mState(State::Initial)
9413   {
9414     AssertIsOnBackgroundThread();
9415     MOZ_ASSERT(aQuotaClient);
9416     MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient);
9417     MOZ_ASSERT(mStartTime);
9418   }
9419 
9420   nsIEventTarget*
BackgroundThread() const9421   BackgroundThread() const
9422   {
9423     MOZ_ASSERT(mQuotaClient);
9424     return mQuotaClient->BackgroundThread();
9425   }
9426 
9427   PRTime
StartTime() const9428   StartTime() const
9429   {
9430     return mStartTime;
9431   }
9432 
9433   bool
IsAborted() const9434   IsAborted() const
9435   {
9436     return mAborted;
9437   }
9438 
9439   void
RunImmediately()9440   RunImmediately()
9441   {
9442     MOZ_ASSERT(mState == State::Initial);
9443 
9444     Unused << this->Run();
9445   }
9446 
9447   void
Abort()9448   Abort()
9449   {
9450     AssertIsOnBackgroundThread();
9451 
9452     mAborted = true;
9453   }
9454 
9455   void
9456   RegisterDatabaseMaintenance(DatabaseMaintenance* aDatabaseMaintenance);
9457 
9458   void
9459   UnregisterDatabaseMaintenance(DatabaseMaintenance* aDatabaseMaintenance);
9460 
9461   already_AddRefed<DatabaseMaintenance>
GetDatabaseMaintenance(const nsAString & aDatabasePath) const9462   GetDatabaseMaintenance(const nsAString& aDatabasePath) const
9463   {
9464     AssertIsOnBackgroundThread();
9465 
9466     RefPtr<DatabaseMaintenance> result =
9467       mDatabaseMaintenances.Get(aDatabasePath);
9468     return result.forget();
9469   }
9470 
9471 private:
~Maintenance()9472   ~Maintenance()
9473   {
9474     MOZ_ASSERT(mState == State::Complete);
9475     MOZ_ASSERT(!mDatabaseMaintenances.Count());
9476   }
9477 
9478   // Runs on the PBackground thread. Checks if IndexedDatabaseManager is
9479   // running. Calls OpenDirectory() or dispatches to the main thread on which
9480   // CreateIndexedDatabaseManager() is called.
9481   nsresult
9482   Start();
9483 
9484   // Runs on the main thread. Once IndexedDatabaseManager is created it will
9485   // dispatch to the PBackground thread on which OpenDirectory() is called.
9486   nsresult
9487   CreateIndexedDatabaseManager();
9488 
9489   // Runs on the PBackground thread. Once QuotaManager has given a lock it will
9490   // call DirectoryOpen().
9491   nsresult
9492   OpenDirectory();
9493 
9494   // Runs on the PBackground thread. Dispatches to the QuotaManager I/O thread.
9495   nsresult
9496   DirectoryOpen();
9497 
9498   // Runs on the QuotaManager I/O thread. Once it finds databases it will
9499   // dispatch to the PBackground thread on which BeginDatabaseMaintenance()
9500   // is called.
9501   nsresult
9502   DirectoryWork();
9503 
9504   // Runs on the PBackground thread. It dispatches a runnable for each database.
9505   nsresult
9506   BeginDatabaseMaintenance();
9507 
9508   // Runs on the PBackground thread. Called when the maintenance is finished or
9509   // if any of above methods fails.
9510   void
9511   Finish();
9512 
9513   NS_DECL_ISUPPORTS_INHERITED
9514 
9515   NS_DECL_NSIRUNNABLE
9516 
9517   // OpenDirectoryListener overrides.
9518   virtual void
9519   DirectoryLockAcquired(DirectoryLock* aLock) override;
9520 
9521   virtual void
9522   DirectoryLockFailed() override;
9523 };
9524 
9525 struct Maintenance::DirectoryInfo final
9526 {
9527   const nsCString mGroup;
9528   const nsCString mOrigin;
9529   nsTArray<nsString> mDatabasePaths;
9530   const PersistenceType mPersistenceType;
9531 
DirectoryInfomozilla::dom::indexedDB::__anone276c2b20111::Maintenance::DirectoryInfo9532   DirectoryInfo(PersistenceType aPersistenceType,
9533                 const nsACString& aGroup,
9534                 const nsACString& aOrigin,
9535                 nsTArray<nsString>&& aDatabasePaths)
9536    : mGroup(aGroup)
9537    , mOrigin(aOrigin)
9538    , mDatabasePaths(Move(aDatabasePaths))
9539    , mPersistenceType(aPersistenceType)
9540   {
9541     MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID);
9542     MOZ_ASSERT(!aGroup.IsEmpty());
9543     MOZ_ASSERT(!aOrigin.IsEmpty());
9544 #ifdef DEBUG
9545     MOZ_ASSERT(!mDatabasePaths.IsEmpty());
9546     for (const nsString& databasePath : mDatabasePaths) {
9547       MOZ_ASSERT(!databasePath.IsEmpty());
9548     }
9549 #endif
9550 
9551     MOZ_COUNT_CTOR(Maintenance::DirectoryInfo);
9552   }
9553 
DirectoryInfomozilla::dom::indexedDB::__anone276c2b20111::Maintenance::DirectoryInfo9554   DirectoryInfo(DirectoryInfo&& aOther)
9555     : mGroup(Move(aOther.mGroup))
9556     , mOrigin(Move(aOther.mOrigin))
9557     , mDatabasePaths(Move(aOther.mDatabasePaths))
9558     , mPersistenceType(Move(aOther.mPersistenceType))
9559   {
9560 #ifdef DEBUG
9561     MOZ_ASSERT(!mDatabasePaths.IsEmpty());
9562     for (const nsString& databasePath : mDatabasePaths) {
9563       MOZ_ASSERT(!databasePath.IsEmpty());
9564     }
9565 #endif
9566 
9567     MOZ_COUNT_CTOR(Maintenance::DirectoryInfo);
9568   }
9569 
~DirectoryInfomozilla::dom::indexedDB::__anone276c2b20111::Maintenance::DirectoryInfo9570   ~DirectoryInfo()
9571   {
9572     MOZ_COUNT_DTOR(Maintenance::DirectoryInfo);
9573   }
9574 
9575   DirectoryInfo(const DirectoryInfo& aOther) = delete;
9576 };
9577 
9578 class DatabaseMaintenance final
9579   : public Runnable
9580 {
9581   // The minimum amount of time that has passed since the last vacuum before we
9582   // will attempt to analyze the database for fragmentation.
9583   static const PRTime kMinVacuumAge =
9584     PRTime(PR_USEC_PER_SEC) * 60 * 60 * 24 * 7;
9585 
9586   // If the percent of database pages that are not in contiguous order is higher
9587   // than this percentage we will attempt a vacuum.
9588   static const int32_t kPercentUnorderedThreshold = 30;
9589 
9590   // If the percent of file size growth since the last vacuum is higher than
9591   // this percentage we will attempt a vacuum.
9592   static const int32_t kPercentFileSizeGrowthThreshold = 10;
9593 
9594   // The number of freelist pages beyond which we will favor an incremental
9595   // vacuum over a full vacuum.
9596   static const int32_t kMaxFreelistThreshold = 5;
9597 
9598   // If the percent of unused file bytes in the database exceeds this percentage
9599   // then we will attempt a full vacuum.
9600   static const int32_t kPercentUnusedThreshold = 20;
9601 
9602   class AutoProgressHandler;
9603 
9604   enum class MaintenanceAction
9605   {
9606     Nothing = 0,
9607     IncrementalVacuum,
9608     FullVacuum
9609   };
9610 
9611   RefPtr<Maintenance> mMaintenance;
9612   const nsCString mGroup;
9613   const nsCString mOrigin;
9614   const nsString mDatabasePath;
9615   nsCOMPtr<nsIRunnable> mCompleteCallback;
9616   const PersistenceType mPersistenceType;
9617 
9618 public:
DatabaseMaintenance(Maintenance * aMaintenance,PersistenceType aPersistenceType,const nsCString & aGroup,const nsCString & aOrigin,const nsString & aDatabasePath)9619   DatabaseMaintenance(Maintenance* aMaintenance,
9620                       PersistenceType aPersistenceType,
9621                       const nsCString& aGroup,
9622                       const nsCString& aOrigin,
9623                       const nsString& aDatabasePath)
9624     : mMaintenance(aMaintenance)
9625     , mGroup(aGroup)
9626     , mOrigin(aOrigin)
9627     , mDatabasePath(aDatabasePath)
9628     , mPersistenceType(aPersistenceType)
9629   { }
9630 
9631   const nsString&
DatabasePath() const9632   DatabasePath() const
9633   {
9634     return mDatabasePath;
9635   }
9636 
9637   void
WaitForCompletion(nsIRunnable * aCallback)9638   WaitForCompletion(nsIRunnable* aCallback)
9639   {
9640     AssertIsOnBackgroundThread();
9641     MOZ_ASSERT(!mCompleteCallback);
9642 
9643     mCompleteCallback = aCallback;
9644   }
9645 
9646 private:
~DatabaseMaintenance()9647   ~DatabaseMaintenance()
9648   { }
9649 
9650   // Runs on maintenance thread pool. Does maintenance on the database.
9651   void
9652   PerformMaintenanceOnDatabase();
9653 
9654   // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
9655   nsresult
9656   CheckIntegrity(mozIStorageConnection* aConnection, bool* aOk);
9657 
9658   // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
9659   nsresult
9660   DetermineMaintenanceAction(mozIStorageConnection* aConnection,
9661                              nsIFile* aDatabaseFile,
9662                              MaintenanceAction* aMaintenanceAction);
9663 
9664   // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
9665   void
9666   IncrementalVacuum(mozIStorageConnection* aConnection);
9667 
9668   // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
9669   void
9670   FullVacuum(mozIStorageConnection* aConnection,
9671              nsIFile* aDatabaseFile);
9672 
9673   // Runs on the PBackground thread. It dispatches a complete callback and
9674   // unregisters from Maintenance.
9675   void
9676   RunOnOwningThread();
9677 
9678   // Runs on maintenance thread pool. Once it performs database maintenance
9679   // it will dispatch to the PBackground thread on which RunOnOwningThread()
9680   // is called.
9681   void
9682   RunOnConnectionThread();
9683 
9684   NS_DECL_NSIRUNNABLE
9685 };
9686 
9687 class MOZ_STACK_CLASS DatabaseMaintenance::AutoProgressHandler final
9688   : public mozIStorageProgressHandler
9689 {
9690   Maintenance* mMaintenance;
9691   mozIStorageConnection* mConnection;
9692 
9693   NS_DECL_OWNINGTHREAD
9694 
9695 #ifdef DEBUG
9696   // This class is stack-based so we never actually allow AddRef/Release to do
9697   // anything. But we need to know if any consumer *thinks* that they have a
9698   // reference to this object so we track the reference countin DEBUG builds.
9699   nsrefcnt mDEBUGRefCnt;
9700 #endif
9701 
9702 public:
AutoProgressHandler(Maintenance * aMaintenance)9703   explicit AutoProgressHandler(Maintenance* aMaintenance)
9704     : mMaintenance(aMaintenance)
9705     , mConnection(nullptr)
9706 #ifdef DEBUG
9707     , mDEBUGRefCnt(0)
9708 #endif
9709   {
9710     MOZ_ASSERT(!NS_IsMainThread());
9711     MOZ_ASSERT(!IsOnBackgroundThread());
9712     NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
9713     MOZ_ASSERT(aMaintenance);
9714   }
9715 
~AutoProgressHandler()9716   ~AutoProgressHandler()
9717   {
9718     NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
9719 
9720     if (mConnection) {
9721       Unregister();
9722     }
9723 
9724     MOZ_ASSERT(!mDEBUGRefCnt);
9725   }
9726 
9727   nsresult
9728   Register(mozIStorageConnection* aConnection);
9729 
9730   // We don't want the mRefCnt member but this class does not "inherit"
9731   // nsISupports.
9732   NS_DECL_ISUPPORTS_INHERITED
9733 
9734 private:
9735   void
9736   Unregister();
9737 
9738   NS_DECL_MOZISTORAGEPROGRESSHANDLER
9739 
9740   // Not available for the heap!
9741   void*
9742   operator new(size_t) = delete;
9743   void*
9744   operator new[](size_t) = delete;
9745   void
9746   operator delete(void*) = delete;
9747   void
9748   operator delete[](void*) = delete;
9749 };
9750 
9751 class IntString : public nsAutoString
9752 {
9753 public:
9754   explicit
IntString(int64_t aInteger)9755   IntString(int64_t aInteger)
9756   {
9757     AppendInt(aInteger);
9758   }
9759 };
9760 
9761 #ifdef DEBUG
9762 
9763 class DEBUGThreadSlower final
9764   : public nsIThreadObserver
9765 {
9766 public:
DEBUGThreadSlower()9767   DEBUGThreadSlower()
9768   {
9769     AssertIsOnBackgroundThread();
9770     MOZ_ASSERT(kDEBUGThreadSleepMS);
9771   }
9772 
9773   NS_DECL_ISUPPORTS
9774 
9775 private:
~DEBUGThreadSlower()9776   ~DEBUGThreadSlower()
9777   {
9778     AssertIsOnBackgroundThread();
9779   }
9780 
9781   NS_DECL_NSITHREADOBSERVER
9782 };
9783 
9784 #endif // DEBUG
9785 
9786 /*******************************************************************************
9787  * Helper classes
9788  ******************************************************************************/
9789 
9790 class MOZ_STACK_CLASS FileHelper final
9791 {
9792   RefPtr<FileManager> mFileManager;
9793 
9794   nsCOMPtr<nsIFile> mFileDirectory;
9795   nsCOMPtr<nsIFile> mJournalDirectory;
9796 
9797 public:
FileHelper(FileManager * aFileManager)9798   explicit FileHelper(FileManager* aFileManager)
9799     : mFileManager(aFileManager)
9800   { }
9801 
9802   nsresult
9803   Init();
9804 
9805   already_AddRefed<nsIFile>
9806   GetFile(FileInfo* aFileInfo);
9807 
9808   already_AddRefed<nsIFile>
9809   GetCheckedFile(FileInfo* aFileInfo);
9810 
9811   already_AddRefed<nsIFile>
9812   GetJournalFile(FileInfo* aFileInfo);
9813 
9814   nsresult
9815   CreateFileFromStream(nsIFile* aFile,
9816                        nsIFile* aJournalFile,
9817                        nsIInputStream* aInputStream,
9818                        bool aCompress);
9819 
9820   nsresult
9821   ReplaceFile(nsIFile* aFile,
9822               nsIFile* aNewFile,
9823               nsIFile* aNewJournalFile);
9824 
9825   nsresult
9826   RemoveFile(nsIFile* aFile,
9827              nsIFile* aJournalFile);
9828 
9829   already_AddRefed<FileInfo>
9830   GetNewFileInfo();
9831 
9832 private:
9833   nsresult
9834   SyncCopy(nsIInputStream* aInputStream,
9835            nsIOutputStream* aOutputStream,
9836            char* aBuffer,
9837            uint32_t aBufferSize);
9838 };
9839 
9840 /*******************************************************************************
9841  * Helper Functions
9842  ******************************************************************************/
9843 
9844 bool
TokenizerIgnoreNothing(char16_t)9845 TokenizerIgnoreNothing(char16_t /* aChar */)
9846 {
9847   return false;
9848 }
9849 
9850 nsresult
DeserializeStructuredCloneFile(FileManager * aFileManager,const nsString & aText,StructuredCloneFile * aFile)9851 DeserializeStructuredCloneFile(FileManager* aFileManager,
9852                                const nsString& aText,
9853                                StructuredCloneFile* aFile)
9854 {
9855   MOZ_ASSERT(!aText.IsEmpty());
9856   MOZ_ASSERT(aFile);
9857 
9858   StructuredCloneFile::FileType type;
9859 
9860   switch (aText.First()) {
9861     case char16_t('-'):
9862       type = StructuredCloneFile::eMutableFile;
9863       break;
9864 
9865     case char16_t('.'):
9866       type = StructuredCloneFile::eStructuredClone;
9867       break;
9868 
9869     case char16_t('/'):
9870       type = StructuredCloneFile::eWasmBytecode;
9871       break;
9872 
9873     case char16_t('\\'):
9874       type = StructuredCloneFile::eWasmCompiled;
9875       break;
9876 
9877     default:
9878       type = StructuredCloneFile::eBlob;
9879   }
9880 
9881   nsresult rv;
9882   int32_t id;
9883 
9884   if (type == StructuredCloneFile::eBlob) {
9885     id = aText.ToInteger(&rv);
9886   } else {
9887     nsString text(Substring(aText, 1));
9888 
9889     id = text.ToInteger(&rv);
9890   }
9891   if (NS_WARN_IF(NS_FAILED(rv))) {
9892     return rv;
9893   }
9894 
9895   RefPtr<FileInfo> fileInfo = aFileManager->GetFileInfo(id);
9896   MOZ_ASSERT(fileInfo);
9897 
9898   aFile->mFileInfo.swap(fileInfo);
9899   aFile->mType = type;
9900 
9901   return NS_OK;
9902 }
9903 
9904 nsresult
CheckWasmModule(FileHelper * aFileHelper,StructuredCloneFile * aBytecodeFile,StructuredCloneFile * aCompiledFile)9905 CheckWasmModule(FileHelper* aFileHelper,
9906                 StructuredCloneFile* aBytecodeFile,
9907                 StructuredCloneFile* aCompiledFile)
9908 {
9909   MOZ_ASSERT(!IsOnBackgroundThread());
9910   MOZ_ASSERT(aFileHelper);
9911   MOZ_ASSERT(aBytecodeFile);
9912   MOZ_ASSERT(aCompiledFile);
9913   MOZ_ASSERT(aBytecodeFile->mType == StructuredCloneFile::eWasmBytecode);
9914   MOZ_ASSERT(aCompiledFile->mType == StructuredCloneFile::eWasmCompiled);
9915 
9916   nsCOMPtr<nsIFile> compiledFile =
9917     aFileHelper->GetCheckedFile(aCompiledFile->mFileInfo);
9918   if (NS_WARN_IF(!compiledFile)) {
9919     return NS_ERROR_FAILURE;
9920   }
9921 
9922   nsresult rv;
9923 
9924   bool match;
9925   {
9926     ScopedPRFileDesc compiledFileDesc;
9927     rv = compiledFile->OpenNSPRFileDesc(PR_RDONLY,
9928                                         0644,
9929                                         &compiledFileDesc.rwget());
9930     if (NS_WARN_IF(NS_FAILED(rv))) {
9931       return rv;
9932     }
9933 
9934     JS::BuildIdCharVector buildId;
9935     bool ok = GetBuildId(&buildId);
9936     if (NS_WARN_IF(!ok)) {
9937       return NS_ERROR_FAILURE;
9938     }
9939 
9940     match = JS::CompiledWasmModuleAssumptionsMatch(compiledFileDesc,
9941                                                    Move(buildId));
9942   }
9943   if (match) {
9944     return NS_OK;
9945   }
9946 
9947   // Re-compile the module.  It would be preferable to do this in the child
9948   // (content process) instead of here in the parent, but that would be way more
9949   // complex and without significant memory allocation or security benefits.
9950   // See the discussion starting from
9951   // https://bugzilla.mozilla.org/show_bug.cgi?id=1312808#c9 for more details.
9952   nsCOMPtr<nsIFile> bytecodeFile =
9953     aFileHelper->GetCheckedFile(aBytecodeFile->mFileInfo);
9954   if (NS_WARN_IF(!bytecodeFile)) {
9955     return NS_ERROR_FAILURE;
9956   }
9957 
9958   ScopedPRFileDesc bytecodeFileDesc;
9959   rv = bytecodeFile->OpenNSPRFileDesc(PR_RDONLY,
9960                                       0644,
9961                                       &bytecodeFileDesc.rwget());
9962   if (NS_WARN_IF(NS_FAILED(rv))) {
9963     return rv;
9964   }
9965 
9966   JS::BuildIdCharVector buildId;
9967   bool ok = GetBuildId(&buildId);
9968   if (NS_WARN_IF(!ok)) {
9969     return NS_ERROR_FAILURE;
9970   }
9971 
9972   RefPtr<JS::WasmModule> module = JS::DeserializeWasmModule(bytecodeFileDesc,
9973                                                             nullptr,
9974                                                             Move(buildId),
9975                                                             nullptr,
9976                                                             0,
9977                                                             0);
9978   if (NS_WARN_IF(!module)) {
9979     return NS_ERROR_FAILURE;
9980   }
9981 
9982   size_t compiledSize;
9983   module->serializedSize(nullptr, &compiledSize);
9984 
9985   UniquePtr<uint8_t[]> compiled(new (fallible) uint8_t[compiledSize]);
9986   if (NS_WARN_IF(!compiled)) {
9987     return NS_ERROR_OUT_OF_MEMORY;
9988   }
9989 
9990   module->serialize(nullptr, 0, compiled.get(), compiledSize);
9991 
9992   nsCOMPtr<nsIInputStream> inputStream;
9993   rv = NS_NewByteInputStream(getter_AddRefs(inputStream),
9994                              reinterpret_cast<const char*>(compiled.get()),
9995                              compiledSize);
9996   if (NS_WARN_IF(NS_FAILED(rv))) {
9997     return rv;
9998   }
9999 
10000   RefPtr<FileInfo> newFileInfo = aFileHelper->GetNewFileInfo();
10001 
10002   nsCOMPtr<nsIFile> newFile = aFileHelper->GetFile(newFileInfo);
10003   if (NS_WARN_IF(!newFile)) {
10004     return NS_ERROR_FAILURE;
10005   }
10006 
10007   nsCOMPtr<nsIFile> newJournalFile =
10008     aFileHelper->GetJournalFile(newFileInfo);
10009   if (NS_WARN_IF(!newJournalFile)) {
10010     return NS_ERROR_FAILURE;
10011   }
10012 
10013   rv = aFileHelper->CreateFileFromStream(newFile,
10014                                          newJournalFile,
10015                                          inputStream,
10016                                          /* aCompress */ false);
10017   if (NS_WARN_IF(NS_FAILED(rv))) {
10018     nsresult rv2 = aFileHelper->RemoveFile(newFile, newJournalFile);
10019     if (NS_WARN_IF(NS_FAILED(rv2))) {
10020       return rv;
10021     }
10022     return rv;
10023   }
10024 
10025   rv = aFileHelper->ReplaceFile(compiledFile, newFile, newJournalFile);
10026   if (NS_WARN_IF(NS_FAILED(rv))) {
10027     nsresult rv2 = aFileHelper->RemoveFile(newFile, newJournalFile);
10028     if (NS_WARN_IF(NS_FAILED(rv2))) {
10029       return rv;
10030     }
10031     return rv;
10032   }
10033 
10034   return NS_OK;
10035 }
10036 
10037 nsresult
DeserializeStructuredCloneFiles(FileManager * aFileManager,const nsAString & aText,nsTArray<StructuredCloneFile> & aResult,bool * aHasPreprocessInfo)10038 DeserializeStructuredCloneFiles(FileManager* aFileManager,
10039                                 const nsAString& aText,
10040                                 nsTArray<StructuredCloneFile>& aResult,
10041                                 bool* aHasPreprocessInfo)
10042 {
10043   MOZ_ASSERT(!IsOnBackgroundThread());
10044 
10045   nsCharSeparatedTokenizerTemplate<TokenizerIgnoreNothing>
10046     tokenizer(aText, ' ');
10047 
10048   nsAutoString token;
10049   nsresult rv;
10050   Maybe<FileHelper> fileHelper;
10051 
10052   while (tokenizer.hasMoreTokens()) {
10053     token = tokenizer.nextToken();
10054     MOZ_ASSERT(!token.IsEmpty());
10055 
10056     StructuredCloneFile* file = aResult.AppendElement();
10057     rv = DeserializeStructuredCloneFile(aFileManager, token, file);
10058     if (NS_WARN_IF(NS_FAILED(rv))) {
10059       return rv;
10060     }
10061 
10062     if (!aHasPreprocessInfo) {
10063       continue;
10064     }
10065 
10066     if (file->mType == StructuredCloneFile::eWasmBytecode) {
10067       *aHasPreprocessInfo = true;
10068     }
10069     else if (file->mType == StructuredCloneFile::eWasmCompiled) {
10070       if (fileHelper.isNothing()) {
10071         fileHelper.emplace(aFileManager);
10072 
10073         rv = fileHelper->Init();
10074         if (NS_WARN_IF(NS_FAILED(rv))) {
10075           return rv;
10076         }
10077       }
10078 
10079       MOZ_ASSERT(aResult.Length() > 1);
10080       MOZ_ASSERT(aResult[aResult.Length() - 2].mType ==
10081                    StructuredCloneFile::eWasmBytecode);
10082 
10083       StructuredCloneFile* previousFile = &aResult[aResult.Length() - 2];
10084 
10085       rv = CheckWasmModule(fileHelper.ptr(), previousFile, file);
10086       if (NS_WARN_IF(NS_FAILED(rv))) {
10087         return rv;
10088       }
10089 
10090       *aHasPreprocessInfo = true;
10091     }
10092   }
10093 
10094   return NS_OK;
10095 }
10096 
10097 bool
GetDatabaseBaseFilename(const nsAString & aFilename,nsDependentSubstring & aDatabaseBaseFilename)10098 GetDatabaseBaseFilename(const nsAString& aFilename,
10099                         nsDependentSubstring& aDatabaseBaseFilename)
10100 {
10101   MOZ_ASSERT(!aFilename.IsEmpty());
10102   MOZ_ASSERT(aDatabaseBaseFilename.IsEmpty());
10103 
10104   NS_NAMED_LITERAL_STRING(sqlite, ".sqlite");
10105 
10106   if (!StringEndsWith(aFilename, sqlite) ||
10107       aFilename.Length() == sqlite.Length()) {
10108     return false;
10109   }
10110 
10111   MOZ_ASSERT(aFilename.Length() > sqlite.Length());
10112 
10113   aDatabaseBaseFilename.Rebind(aFilename,
10114                                0,
10115                                aFilename.Length() - sqlite.Length());
10116   return true;
10117 }
10118 
10119 nsresult
SerializeStructuredCloneFiles(PBackgroundParent * aBackgroundActor,Database * aDatabase,const nsTArray<StructuredCloneFile> & aFiles,bool aForPreprocess,FallibleTArray<SerializedStructuredCloneFile> & aResult)10120 SerializeStructuredCloneFiles(
10121                          PBackgroundParent* aBackgroundActor,
10122                          Database* aDatabase,
10123                          const nsTArray<StructuredCloneFile>& aFiles,
10124                          bool aForPreprocess,
10125                          FallibleTArray<SerializedStructuredCloneFile>& aResult)
10126 {
10127   AssertIsOnBackgroundThread();
10128   MOZ_ASSERT(aBackgroundActor);
10129   MOZ_ASSERT(aDatabase);
10130   MOZ_ASSERT(aResult.IsEmpty());
10131 
10132   if (aFiles.IsEmpty()) {
10133     return NS_OK;
10134   }
10135 
10136   FileManager* fileManager = aDatabase->GetFileManager();
10137 
10138   nsCOMPtr<nsIFile> directory = fileManager->GetCheckedDirectory();
10139   if (NS_WARN_IF(!directory)) {
10140     IDB_REPORT_INTERNAL_ERR();
10141     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
10142   }
10143 
10144   const uint32_t count = aFiles.Length();
10145 
10146   if (NS_WARN_IF(!aResult.SetCapacity(count, fallible))) {
10147     return NS_ERROR_OUT_OF_MEMORY;
10148   }
10149 
10150   for (uint32_t index = 0; index < count; index++) {
10151     const StructuredCloneFile& file = aFiles[index];
10152 
10153     if (aForPreprocess &&
10154         file.mType != StructuredCloneFile::eWasmBytecode &&
10155         file.mType != StructuredCloneFile::eWasmCompiled) {
10156       continue;
10157     }
10158 
10159     const int64_t fileId = file.mFileInfo->Id();
10160     MOZ_ASSERT(fileId > 0);
10161 
10162     nsCOMPtr<nsIFile> nativeFile =
10163       fileManager->GetCheckedFileForId(directory, fileId);
10164     if (NS_WARN_IF(!nativeFile)) {
10165       IDB_REPORT_INTERNAL_ERR();
10166       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
10167     }
10168 
10169     switch (file.mType) {
10170       case StructuredCloneFile::eBlob: {
10171         RefPtr<BlobImpl> impl = new BlobImplStoredFile(nativeFile,
10172                                                        file.mFileInfo,
10173                                                        /* aSnapshot */ false);
10174 
10175         PBlobParent* actor =
10176           BackgroundParent::GetOrCreateActorForBlobImpl(aBackgroundActor, impl);
10177         if (!actor) {
10178           // This can only fail if the child has crashed.
10179           IDB_REPORT_INTERNAL_ERR();
10180           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
10181         }
10182 
10183         SerializedStructuredCloneFile* file = aResult.AppendElement(fallible);
10184         MOZ_ASSERT(file);
10185 
10186         file->file() = actor;
10187         file->type() = StructuredCloneFile::eBlob;
10188 
10189         break;
10190       }
10191 
10192       case StructuredCloneFile::eMutableFile: {
10193         if (aDatabase->IsFileHandleDisabled()) {
10194           SerializedStructuredCloneFile* file = aResult.AppendElement(fallible);
10195           MOZ_ASSERT(file);
10196 
10197           file->file() = null_t();
10198           file->type() = StructuredCloneFile::eMutableFile;
10199         } else {
10200           RefPtr<MutableFile> actor =
10201             MutableFile::Create(nativeFile, aDatabase, file.mFileInfo);
10202           if (!actor) {
10203             IDB_REPORT_INTERNAL_ERR();
10204             return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
10205           }
10206 
10207           // Transfer ownership to IPDL.
10208           actor->SetActorAlive();
10209 
10210           if (!aDatabase->SendPBackgroundMutableFileConstructor(actor,
10211                                                                 EmptyString(),
10212                                                                 EmptyString())) {
10213             // This can only fail if the child has crashed.
10214             IDB_REPORT_INTERNAL_ERR();
10215             return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
10216           }
10217 
10218           SerializedStructuredCloneFile* file = aResult.AppendElement(fallible);
10219           MOZ_ASSERT(file);
10220 
10221           file->file() = actor;
10222           file->type() = StructuredCloneFile::eMutableFile;
10223         }
10224 
10225         break;
10226       }
10227 
10228       case StructuredCloneFile::eStructuredClone: {
10229         SerializedStructuredCloneFile* file = aResult.AppendElement(fallible);
10230         MOZ_ASSERT(file);
10231 
10232         file->file() = null_t();
10233         file->type() = StructuredCloneFile::eStructuredClone;
10234 
10235         break;
10236       }
10237 
10238       case StructuredCloneFile::eWasmBytecode:
10239       case StructuredCloneFile::eWasmCompiled: {
10240         if (!aForPreprocess) {
10241           SerializedStructuredCloneFile* serializedFile =
10242             aResult.AppendElement(fallible);
10243           MOZ_ASSERT(serializedFile);
10244 
10245           serializedFile->file() = null_t();
10246           serializedFile->type() = file.mType;
10247         } else {
10248           RefPtr<BlobImpl> impl = new BlobImplStoredFile(nativeFile,
10249                                                          file.mFileInfo,
10250                                                          /* aSnapshot */ false);
10251 
10252           PBlobParent* actor =
10253             BackgroundParent::GetOrCreateActorForBlobImpl(aBackgroundActor,
10254                                                           impl);
10255           if (!actor) {
10256             // This can only fail if the child has crashed.
10257             IDB_REPORT_INTERNAL_ERR();
10258             return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
10259           }
10260 
10261           SerializedStructuredCloneFile* serializedFile =
10262             aResult.AppendElement(fallible);
10263           MOZ_ASSERT(serializedFile);
10264 
10265           serializedFile->file() = actor;
10266           serializedFile->type() = file.mType;
10267         }
10268 
10269         break;
10270       }
10271 
10272       default:
10273         MOZ_CRASH("Should never get here!");
10274     }
10275   }
10276 
10277   return NS_OK;
10278 }
10279 
10280 already_AddRefed<nsIFile>
GetFileForFileInfo(FileInfo * aFileInfo)10281 GetFileForFileInfo(FileInfo* aFileInfo)
10282 {
10283   FileManager* fileManager = aFileInfo->Manager();
10284   nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
10285   if (NS_WARN_IF(!directory)) {
10286     return nullptr;
10287   }
10288 
10289   nsCOMPtr<nsIFile> file = fileManager->GetFileForId(directory,
10290                                                      aFileInfo->Id());
10291   if (NS_WARN_IF(!file)) {
10292     return nullptr;
10293   }
10294 
10295   return file.forget();
10296 }
10297 
10298 /*******************************************************************************
10299  * Globals
10300  ******************************************************************************/
10301 
10302 // Counts the number of "live" Factory, FactoryOp and Database instances.
10303 uint64_t gBusyCount = 0;
10304 
10305 typedef nsTArray<RefPtr<FactoryOp>> FactoryOpArray;
10306 
10307 StaticAutoPtr<FactoryOpArray> gFactoryOps;
10308 
10309 // Maps a database id to information about live database actors.
10310 typedef nsClassHashtable<nsCStringHashKey, DatabaseActorInfo>
10311         DatabaseActorHashtable;
10312 
10313 StaticAutoPtr<DatabaseActorHashtable> gLiveDatabaseHashtable;
10314 
10315 StaticRefPtr<ConnectionPool> gConnectionPool;
10316 
10317 StaticRefPtr<FileHandleThreadPool> gFileHandleThreadPool;
10318 
10319 typedef nsDataHashtable<nsIDHashKey, DatabaseLoggingInfo*>
10320         DatabaseLoggingInfoHashtable;
10321 
10322 StaticAutoPtr<DatabaseLoggingInfoHashtable> gLoggingInfoHashtable;
10323 
10324 typedef nsDataHashtable<nsUint32HashKey, uint32_t> TelemetryIdHashtable;
10325 
10326 StaticAutoPtr<TelemetryIdHashtable> gTelemetryIdHashtable;
10327 
10328 // Protects all reads and writes to gTelemetryIdHashtable.
10329 StaticAutoPtr<Mutex> gTelemetryIdMutex;
10330 
10331 #ifdef DEBUG
10332 
10333 StaticRefPtr<DEBUGThreadSlower> gDEBUGThreadSlower;
10334 
10335 #endif // DEBUG
10336 
10337 
10338 void
IncreaseBusyCount()10339 IncreaseBusyCount()
10340 {
10341   AssertIsOnBackgroundThread();
10342 
10343   // If this is the first instance then we need to do some initialization.
10344   if (!gBusyCount) {
10345     MOZ_ASSERT(!gFactoryOps);
10346     gFactoryOps = new FactoryOpArray();
10347 
10348     MOZ_ASSERT(!gLiveDatabaseHashtable);
10349     gLiveDatabaseHashtable = new DatabaseActorHashtable();
10350 
10351     MOZ_ASSERT(!gLoggingInfoHashtable);
10352     gLoggingInfoHashtable = new DatabaseLoggingInfoHashtable();
10353 
10354 #ifdef DEBUG
10355     if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) {
10356       NS_WARNING("PBackground thread debugging enabled, priority has been "
10357                  "modified!");
10358       nsCOMPtr<nsISupportsPriority> thread =
10359         do_QueryInterface(NS_GetCurrentThread());
10360       MOZ_ASSERT(thread);
10361 
10362       MOZ_ALWAYS_SUCCEEDS(thread->SetPriority(kDEBUGThreadPriority));
10363     }
10364 
10365     if (kDEBUGThreadSleepMS) {
10366       NS_WARNING("PBackground thread debugging enabled, sleeping after every "
10367                  "event!");
10368       nsCOMPtr<nsIThreadInternal> thread =
10369         do_QueryInterface(NS_GetCurrentThread());
10370       MOZ_ASSERT(thread);
10371 
10372       gDEBUGThreadSlower = new DEBUGThreadSlower();
10373 
10374       MOZ_ALWAYS_SUCCEEDS(thread->AddObserver(gDEBUGThreadSlower));
10375     }
10376 #endif // DEBUG
10377   }
10378 
10379   gBusyCount++;
10380 }
10381 
10382 void
DecreaseBusyCount()10383 DecreaseBusyCount()
10384 {
10385   AssertIsOnBackgroundThread();
10386   MOZ_ASSERT(gBusyCount);
10387 
10388   // Clean up if there are no more instances.
10389   if (--gBusyCount == 0) {
10390     MOZ_ASSERT(gLoggingInfoHashtable);
10391     gLoggingInfoHashtable = nullptr;
10392 
10393     MOZ_ASSERT(gLiveDatabaseHashtable);
10394     MOZ_ASSERT(!gLiveDatabaseHashtable->Count());
10395     gLiveDatabaseHashtable = nullptr;
10396 
10397     MOZ_ASSERT(gFactoryOps);
10398     MOZ_ASSERT(gFactoryOps->IsEmpty());
10399     gFactoryOps = nullptr;
10400 
10401 #ifdef DEBUG
10402     if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) {
10403       nsCOMPtr<nsISupportsPriority> thread =
10404         do_QueryInterface(NS_GetCurrentThread());
10405       MOZ_ASSERT(thread);
10406 
10407       MOZ_ALWAYS_SUCCEEDS(
10408         thread->SetPriority(nsISupportsPriority::PRIORITY_NORMAL));
10409     }
10410 
10411     if (kDEBUGThreadSleepMS) {
10412       MOZ_ASSERT(gDEBUGThreadSlower);
10413 
10414       nsCOMPtr<nsIThreadInternal> thread =
10415         do_QueryInterface(NS_GetCurrentThread());
10416       MOZ_ASSERT(thread);
10417 
10418       MOZ_ALWAYS_SUCCEEDS(thread->RemoveObserver(gDEBUGThreadSlower));
10419 
10420       gDEBUGThreadSlower = nullptr;
10421     }
10422 #endif // DEBUG
10423   }
10424 }
10425 
10426 uint32_t
TelemetryIdForFile(nsIFile * aFile)10427 TelemetryIdForFile(nsIFile* aFile)
10428 {
10429   // May be called on any thread!
10430 
10431   MOZ_ASSERT(aFile);
10432   MOZ_ASSERT(gTelemetryIdMutex);
10433 
10434   // The storage directory is structured like this:
10435   //
10436   //   <profile>/storage/<persistence>/<origin>/idb/<filename>.sqlite
10437   //
10438   // For the purposes of this function we're only concerned with the
10439   // <persistence>, <origin>, and <filename> pieces.
10440 
10441   nsString filename;
10442   MOZ_ALWAYS_SUCCEEDS(aFile->GetLeafName(filename));
10443 
10444   // Make sure we were given a database file.
10445   NS_NAMED_LITERAL_STRING(sqliteExtension, ".sqlite");
10446 
10447   MOZ_ASSERT(StringEndsWith(filename, sqliteExtension));
10448 
10449   filename.Truncate(filename.Length() - sqliteExtension.Length());
10450 
10451   // Get the "idb" directory.
10452   nsCOMPtr<nsIFile> idbDirectory;
10453   MOZ_ALWAYS_SUCCEEDS(aFile->GetParent(getter_AddRefs(idbDirectory)));
10454 
10455   DebugOnly<nsString> idbLeafName;
10456   MOZ_ASSERT(NS_SUCCEEDED(idbDirectory->GetLeafName(idbLeafName)));
10457   MOZ_ASSERT(static_cast<nsString&>(idbLeafName).EqualsLiteral("idb"));
10458 
10459   // Get the <origin> directory.
10460   nsCOMPtr<nsIFile> originDirectory;
10461   MOZ_ALWAYS_SUCCEEDS(
10462     idbDirectory->GetParent(getter_AddRefs(originDirectory)));
10463 
10464   nsString origin;
10465   MOZ_ALWAYS_SUCCEEDS(originDirectory->GetLeafName(origin));
10466 
10467   // Any databases in these directories are owned by the application and should
10468   // not have their filenames masked. Hopefully they also appear in the
10469   // Telemetry.cpp whitelist.
10470   if (origin.EqualsLiteral("chrome") ||
10471       origin.EqualsLiteral("moz-safe-about+home")) {
10472     return 0;
10473   }
10474 
10475   // Get the <persistence> directory.
10476   nsCOMPtr<nsIFile> persistenceDirectory;
10477   MOZ_ALWAYS_SUCCEEDS(
10478     originDirectory->GetParent(getter_AddRefs(persistenceDirectory)));
10479 
10480   nsString persistence;
10481   MOZ_ALWAYS_SUCCEEDS(persistenceDirectory->GetLeafName(persistence));
10482 
10483   NS_NAMED_LITERAL_STRING(separator, "*");
10484 
10485   uint32_t hashValue = HashString(persistence + separator +
10486                                   origin + separator +
10487                                   filename);
10488 
10489   MutexAutoLock lock(*gTelemetryIdMutex);
10490 
10491   if (!gTelemetryIdHashtable) {
10492     gTelemetryIdHashtable = new TelemetryIdHashtable();
10493   }
10494 
10495   uint32_t id;
10496   if (!gTelemetryIdHashtable->Get(hashValue, &id)) {
10497     static uint32_t sNextId = 1;
10498 
10499     // We're locked, no need for atomics.
10500     id = sNextId++;
10501 
10502     gTelemetryIdHashtable->Put(hashValue, id);
10503   }
10504 
10505   return id;
10506 }
10507 
10508 } // namespace
10509 
10510 /*******************************************************************************
10511  * Exported functions
10512  ******************************************************************************/
10513 
10514 PBackgroundIDBFactoryParent*
AllocPBackgroundIDBFactoryParent(const LoggingInfo & aLoggingInfo)10515 AllocPBackgroundIDBFactoryParent(const LoggingInfo& aLoggingInfo)
10516 {
10517   AssertIsOnBackgroundThread();
10518 
10519   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
10520     return nullptr;
10521   }
10522 
10523   if (NS_WARN_IF(!aLoggingInfo.nextTransactionSerialNumber()) ||
10524       NS_WARN_IF(!aLoggingInfo.nextVersionChangeTransactionSerialNumber()) ||
10525       NS_WARN_IF(!aLoggingInfo.nextRequestSerialNumber())) {
10526     ASSERT_UNLESS_FUZZING();
10527     return nullptr;
10528   }
10529 
10530   RefPtr<Factory> actor = Factory::Create(aLoggingInfo);
10531   MOZ_ASSERT(actor);
10532 
10533   return actor.forget().take();
10534 }
10535 
10536 bool
RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent * aActor,const LoggingInfo &)10537 RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent* aActor,
10538                                      const LoggingInfo& /* aLoggingInfo */)
10539 {
10540   AssertIsOnBackgroundThread();
10541   MOZ_ASSERT(aActor);
10542   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
10543 
10544   return true;
10545 }
10546 
10547 bool
DeallocPBackgroundIDBFactoryParent(PBackgroundIDBFactoryParent * aActor)10548 DeallocPBackgroundIDBFactoryParent(PBackgroundIDBFactoryParent* aActor)
10549 {
10550   AssertIsOnBackgroundThread();
10551   MOZ_ASSERT(aActor);
10552 
10553   RefPtr<Factory> actor = dont_AddRef(static_cast<Factory*>(aActor));
10554   return true;
10555 }
10556 
10557 PBackgroundIndexedDBUtilsParent*
AllocPBackgroundIndexedDBUtilsParent()10558 AllocPBackgroundIndexedDBUtilsParent()
10559 {
10560   AssertIsOnBackgroundThread();
10561 
10562   RefPtr<Utils> actor = new Utils();
10563 
10564   return actor.forget().take();
10565 }
10566 
10567 bool
DeallocPBackgroundIndexedDBUtilsParent(PBackgroundIndexedDBUtilsParent * aActor)10568 DeallocPBackgroundIndexedDBUtilsParent(PBackgroundIndexedDBUtilsParent* aActor)
10569 {
10570   AssertIsOnBackgroundThread();
10571   MOZ_ASSERT(aActor);
10572 
10573   RefPtr<Utils> actor = dont_AddRef(static_cast<Utils*>(aActor));
10574   return true;
10575 }
10576 
10577 bool
RecvFlushPendingFileDeletions()10578 RecvFlushPendingFileDeletions()
10579 {
10580   AssertIsOnBackgroundThread();
10581 
10582   RefPtr<FlushPendingFileDeletionsRunnable> runnable =
10583     new FlushPendingFileDeletionsRunnable();
10584 
10585   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
10586 
10587   return true;
10588 }
10589 
10590 PIndexedDBPermissionRequestParent*
AllocPIndexedDBPermissionRequestParent(Element * aOwnerElement,nsIPrincipal * aPrincipal)10591 AllocPIndexedDBPermissionRequestParent(Element* aOwnerElement,
10592                                        nsIPrincipal* aPrincipal)
10593 {
10594   MOZ_ASSERT(NS_IsMainThread());
10595 
10596   RefPtr<PermissionRequestHelper> actor =
10597     new PermissionRequestHelper(aOwnerElement, aPrincipal);
10598   return actor.forget().take();
10599 }
10600 
10601 bool
RecvPIndexedDBPermissionRequestConstructor(PIndexedDBPermissionRequestParent * aActor)10602 RecvPIndexedDBPermissionRequestConstructor(
10603                                       PIndexedDBPermissionRequestParent* aActor)
10604 {
10605   MOZ_ASSERT(NS_IsMainThread());
10606   MOZ_ASSERT(aActor);
10607 
10608   auto* actor = static_cast<PermissionRequestHelper*>(aActor);
10609 
10610   PermissionRequestBase::PermissionValue permission;
10611   nsresult rv = actor->PromptIfNeeded(&permission);
10612   if (NS_FAILED(rv)) {
10613     return false;
10614   }
10615 
10616   if (permission != PermissionRequestBase::kPermissionPrompt) {
10617     Unused <<
10618       PIndexedDBPermissionRequestParent::Send__delete__(actor, permission);
10619   }
10620 
10621   return true;
10622 }
10623 
10624 bool
DeallocPIndexedDBPermissionRequestParent(PIndexedDBPermissionRequestParent * aActor)10625 DeallocPIndexedDBPermissionRequestParent(
10626                                       PIndexedDBPermissionRequestParent* aActor)
10627 {
10628   MOZ_ASSERT(NS_IsMainThread());
10629   MOZ_ASSERT(aActor);
10630 
10631   RefPtr<PermissionRequestHelper> actor =
10632     dont_AddRef(static_cast<PermissionRequestHelper*>(aActor));
10633   return true;
10634 }
10635 
10636 already_AddRefed<mozilla::dom::quota::Client>
CreateQuotaClient()10637 CreateQuotaClient()
10638 {
10639   AssertIsOnBackgroundThread();
10640 
10641   RefPtr<QuotaClient> client = new QuotaClient();
10642   return client.forget();
10643 }
10644 
10645 FileHandleThreadPool*
GetFileHandleThreadPool()10646 GetFileHandleThreadPool()
10647 {
10648   AssertIsOnBackgroundThread();
10649 
10650   if (!gFileHandleThreadPool) {
10651     RefPtr<FileHandleThreadPool> fileHandleThreadPool =
10652       FileHandleThreadPool::Create();
10653     if (NS_WARN_IF(!fileHandleThreadPool)) {
10654       return nullptr;
10655     }
10656 
10657     gFileHandleThreadPool = fileHandleThreadPool;
10658   }
10659 
10660   return gFileHandleThreadPool;
10661 }
10662 
10663 /*******************************************************************************
10664  * DatabaseConnection implementation
10665  ******************************************************************************/
10666 
DatabaseConnection(mozIStorageConnection * aStorageConnection,FileManager * aFileManager)10667 DatabaseConnection::DatabaseConnection(
10668                                       mozIStorageConnection* aStorageConnection,
10669                                       FileManager* aFileManager)
10670   : mStorageConnection(aStorageConnection)
10671   , mFileManager(aFileManager)
10672   , mInReadTransaction(false)
10673   , mInWriteTransaction(false)
10674 #ifdef DEBUG
10675   , mDEBUGSavepointCount(0)
10676   , mDEBUGThread(PR_GetCurrentThread())
10677 #endif
10678 {
10679   AssertIsOnConnectionThread();
10680   MOZ_ASSERT(aStorageConnection);
10681   MOZ_ASSERT(aFileManager);
10682 }
10683 
~DatabaseConnection()10684 DatabaseConnection::~DatabaseConnection()
10685 {
10686   MOZ_ASSERT(!mStorageConnection);
10687   MOZ_ASSERT(!mFileManager);
10688   MOZ_ASSERT(!mCachedStatements.Count());
10689   MOZ_ASSERT(!mUpdateRefcountFunction);
10690   MOZ_ASSERT(!mInWriteTransaction);
10691   MOZ_ASSERT(!mDEBUGSavepointCount);
10692 }
10693 
10694 nsresult
Init()10695 DatabaseConnection::Init()
10696 {
10697   AssertIsOnConnectionThread();
10698   MOZ_ASSERT(!mInReadTransaction);
10699   MOZ_ASSERT(!mInWriteTransaction);
10700 
10701   CachedStatement stmt;
10702   nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN;"), &stmt);
10703   if (NS_WARN_IF(NS_FAILED(rv))) {
10704     return rv;
10705   }
10706 
10707   rv = stmt->Execute();
10708   if (NS_WARN_IF(NS_FAILED(rv))) {
10709     return rv;
10710   }
10711 
10712   mInReadTransaction = true;
10713 
10714   return NS_OK;
10715 }
10716 
10717 nsresult
GetCachedStatement(const nsACString & aQuery,CachedStatement * aCachedStatement)10718 DatabaseConnection::GetCachedStatement(const nsACString& aQuery,
10719                                        CachedStatement* aCachedStatement)
10720 {
10721   AssertIsOnConnectionThread();
10722   MOZ_ASSERT(!aQuery.IsEmpty());
10723   MOZ_ASSERT(aCachedStatement);
10724   MOZ_ASSERT(mStorageConnection);
10725 
10726   PROFILER_LABEL("IndexedDB",
10727                  "DatabaseConnection::GetCachedStatement",
10728                  js::ProfileEntry::Category::STORAGE);
10729 
10730   nsCOMPtr<mozIStorageStatement> stmt;
10731 
10732   if (!mCachedStatements.Get(aQuery, getter_AddRefs(stmt))) {
10733     nsresult rv =
10734       mStorageConnection->CreateStatement(aQuery, getter_AddRefs(stmt));
10735     if (NS_FAILED(rv)) {
10736 #ifdef DEBUG
10737       nsCString msg;
10738       MOZ_ALWAYS_SUCCEEDS(mStorageConnection->GetLastErrorString(msg));
10739 
10740       nsAutoCString error =
10741         NS_LITERAL_CSTRING("The statement '") + aQuery +
10742         NS_LITERAL_CSTRING("' failed to compile with the error message '") +
10743         msg + NS_LITERAL_CSTRING("'.");
10744 
10745       NS_WARNING(error.get());
10746 #endif
10747       return rv;
10748     }
10749 
10750     mCachedStatements.Put(aQuery, stmt);
10751   }
10752 
10753   aCachedStatement->Assign(this, stmt.forget());
10754   return NS_OK;
10755 }
10756 
10757 nsresult
BeginWriteTransaction()10758 DatabaseConnection::BeginWriteTransaction()
10759 {
10760   AssertIsOnConnectionThread();
10761   MOZ_ASSERT(mStorageConnection);
10762   MOZ_ASSERT(mInReadTransaction);
10763   MOZ_ASSERT(!mInWriteTransaction);
10764 
10765   PROFILER_LABEL("IndexedDB",
10766                  "DatabaseConnection::BeginWriteTransaction",
10767                  js::ProfileEntry::Category::STORAGE);
10768 
10769   // Release our read locks.
10770   CachedStatement rollbackStmt;
10771   nsresult rv =
10772     GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK;"), &rollbackStmt);
10773   if (NS_WARN_IF(NS_FAILED(rv))) {
10774     return rv;
10775   }
10776 
10777   rv = rollbackStmt->Execute();
10778   if (NS_WARN_IF(NS_FAILED(rv))) {
10779     return rv;
10780   }
10781 
10782   mInReadTransaction = false;
10783 
10784   if (!mUpdateRefcountFunction) {
10785     MOZ_ASSERT(mFileManager);
10786 
10787     RefPtr<UpdateRefcountFunction> function =
10788       new UpdateRefcountFunction(this, mFileManager);
10789 
10790     rv =
10791       mStorageConnection->CreateFunction(NS_LITERAL_CSTRING("update_refcount"),
10792                                          /* aNumArguments */ 2,
10793                                          function);
10794     if (NS_WARN_IF(NS_FAILED(rv))) {
10795       return rv;
10796     }
10797 
10798     mUpdateRefcountFunction.swap(function);
10799   }
10800 
10801   CachedStatement beginStmt;
10802   rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN IMMEDIATE;"), &beginStmt);
10803   if (NS_WARN_IF(NS_FAILED(rv))) {
10804     return rv;
10805   }
10806 
10807   rv = beginStmt->Execute();
10808   if (rv == NS_ERROR_STORAGE_BUSY) {
10809     NS_WARNING("Received NS_ERROR_STORAGE_BUSY when attempting to start write "
10810                "transaction, retrying for up to 10 seconds");
10811 
10812     // Another thread must be using the database. Wait up to 10 seconds for
10813     // that to complete.
10814     TimeStamp start = TimeStamp::NowLoRes();
10815 
10816     while (true) {
10817       PR_Sleep(PR_MillisecondsToInterval(100));
10818 
10819       rv = beginStmt->Execute();
10820       if (rv != NS_ERROR_STORAGE_BUSY ||
10821           TimeStamp::NowLoRes() - start > TimeDuration::FromSeconds(10)) {
10822         break;
10823       }
10824     }
10825   }
10826 
10827   if (NS_WARN_IF(NS_FAILED(rv))) {
10828     return rv;
10829   }
10830 
10831   mInWriteTransaction = true;
10832 
10833   return NS_OK;
10834 }
10835 
10836 nsresult
CommitWriteTransaction()10837 DatabaseConnection::CommitWriteTransaction()
10838 {
10839   AssertIsOnConnectionThread();
10840   MOZ_ASSERT(mStorageConnection);
10841   MOZ_ASSERT(!mInReadTransaction);
10842   MOZ_ASSERT(mInWriteTransaction);
10843 
10844   PROFILER_LABEL("IndexedDB",
10845                  "DatabaseConnection::CommitWriteTransaction",
10846                  js::ProfileEntry::Category::STORAGE);
10847 
10848   CachedStatement stmt;
10849   nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("COMMIT;"), &stmt);
10850   if (NS_WARN_IF(NS_FAILED(rv))) {
10851     return rv;
10852   }
10853 
10854   rv = stmt->Execute();
10855   if (NS_WARN_IF(NS_FAILED(rv))) {
10856     return rv;
10857   }
10858 
10859   mInWriteTransaction = false;
10860   return NS_OK;
10861 }
10862 
10863 void
RollbackWriteTransaction()10864 DatabaseConnection::RollbackWriteTransaction()
10865 {
10866   AssertIsOnConnectionThread();
10867   MOZ_ASSERT(!mInReadTransaction);
10868   MOZ_ASSERT(mStorageConnection);
10869 
10870   PROFILER_LABEL("IndexedDB",
10871                  "DatabaseConnection::RollbackWriteTransaction",
10872                  js::ProfileEntry::Category::STORAGE);
10873 
10874   if (!mInWriteTransaction) {
10875     return;
10876   }
10877 
10878   DatabaseConnection::CachedStatement stmt;
10879   nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK;"), &stmt);
10880   if (NS_WARN_IF(NS_FAILED(rv))) {
10881     return;
10882   }
10883 
10884   // This may fail if SQLite already rolled back the transaction so ignore any
10885   // errors.
10886   Unused << stmt->Execute();
10887 
10888   mInWriteTransaction = false;
10889 }
10890 
10891 void
FinishWriteTransaction()10892 DatabaseConnection::FinishWriteTransaction()
10893 {
10894   AssertIsOnConnectionThread();
10895   MOZ_ASSERT(mStorageConnection);
10896   MOZ_ASSERT(!mInReadTransaction);
10897   MOZ_ASSERT(!mInWriteTransaction);
10898 
10899   PROFILER_LABEL("IndexedDB",
10900                  "DatabaseConnection::FinishWriteTransaction",
10901                  js::ProfileEntry::Category::STORAGE);
10902 
10903   if (mUpdateRefcountFunction) {
10904     mUpdateRefcountFunction->Reset();
10905   }
10906 
10907   CachedStatement stmt;
10908   nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN;"), &stmt);
10909   if (NS_WARN_IF(NS_FAILED(rv))) {
10910     return;
10911   }
10912 
10913   rv = stmt->Execute();
10914   if (NS_WARN_IF(NS_FAILED(rv))) {
10915     return;
10916   }
10917 
10918   mInReadTransaction = true;
10919 }
10920 
10921 nsresult
StartSavepoint()10922 DatabaseConnection::StartSavepoint()
10923 {
10924   AssertIsOnConnectionThread();
10925   MOZ_ASSERT(mStorageConnection);
10926   MOZ_ASSERT(mUpdateRefcountFunction);
10927   MOZ_ASSERT(mInWriteTransaction);
10928 
10929   PROFILER_LABEL("IndexedDB",
10930                  "DatabaseConnection::StartSavepoint",
10931                  js::ProfileEntry::Category::STORAGE);
10932 
10933   CachedStatement stmt;
10934   nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING(SAVEPOINT_CLAUSE), &stmt);
10935   if (NS_WARN_IF(NS_FAILED(rv))) {
10936     return rv;
10937   }
10938 
10939   rv = stmt->Execute();
10940   if (NS_WARN_IF(NS_FAILED(rv))) {
10941     return rv;
10942   }
10943 
10944   mUpdateRefcountFunction->StartSavepoint();
10945 
10946 #ifdef DEBUG
10947   MOZ_ASSERT(mDEBUGSavepointCount < UINT32_MAX);
10948   mDEBUGSavepointCount++;
10949 #endif
10950 
10951   return NS_OK;
10952 }
10953 
10954 nsresult
ReleaseSavepoint()10955 DatabaseConnection::ReleaseSavepoint()
10956 {
10957   AssertIsOnConnectionThread();
10958   MOZ_ASSERT(mStorageConnection);
10959   MOZ_ASSERT(mUpdateRefcountFunction);
10960   MOZ_ASSERT(mInWriteTransaction);
10961 
10962   PROFILER_LABEL("IndexedDB",
10963                  "DatabaseConnection::ReleaseSavepoint",
10964                  js::ProfileEntry::Category::STORAGE);
10965 
10966   CachedStatement stmt;
10967   nsresult rv = GetCachedStatement(
10968     NS_LITERAL_CSTRING("RELEASE " SAVEPOINT_CLAUSE),
10969     &stmt);
10970   if (NS_SUCCEEDED(rv)) {
10971     rv = stmt->Execute();
10972     if (NS_SUCCEEDED(rv)) {
10973       mUpdateRefcountFunction->ReleaseSavepoint();
10974 
10975 #ifdef DEBUG
10976       MOZ_ASSERT(mDEBUGSavepointCount);
10977       mDEBUGSavepointCount--;
10978 #endif
10979     }
10980   }
10981 
10982   return rv;
10983 }
10984 
10985 nsresult
RollbackSavepoint()10986 DatabaseConnection::RollbackSavepoint()
10987 {
10988   AssertIsOnConnectionThread();
10989   MOZ_ASSERT(mStorageConnection);
10990   MOZ_ASSERT(mUpdateRefcountFunction);
10991   MOZ_ASSERT(mInWriteTransaction);
10992 
10993   PROFILER_LABEL("IndexedDB",
10994                  "DatabaseConnection::RollbackSavepoint",
10995                  js::ProfileEntry::Category::STORAGE);
10996 
10997 #ifdef DEBUG
10998   MOZ_ASSERT(mDEBUGSavepointCount);
10999   mDEBUGSavepointCount--;
11000 #endif
11001 
11002   mUpdateRefcountFunction->RollbackSavepoint();
11003 
11004   CachedStatement stmt;
11005   nsresult rv = GetCachedStatement(
11006     NS_LITERAL_CSTRING("ROLLBACK TO " SAVEPOINT_CLAUSE),
11007     &stmt);
11008   if (NS_WARN_IF(NS_FAILED(rv))) {
11009     return rv;
11010   }
11011 
11012   // This may fail if SQLite already rolled back the savepoint so ignore any
11013   // errors.
11014   Unused << stmt->Execute();
11015 
11016   return NS_OK;
11017 }
11018 
11019 nsresult
CheckpointInternal(CheckpointMode aMode)11020 DatabaseConnection::CheckpointInternal(CheckpointMode aMode)
11021 {
11022   AssertIsOnConnectionThread();
11023   MOZ_ASSERT(!mInReadTransaction);
11024   MOZ_ASSERT(!mInWriteTransaction);
11025 
11026   PROFILER_LABEL("IndexedDB",
11027                  "DatabaseConnection::CheckpointInternal",
11028                  js::ProfileEntry::Category::STORAGE);
11029 
11030   nsAutoCString stmtString;
11031   stmtString.AssignLiteral("PRAGMA wal_checkpoint(");
11032 
11033   switch (aMode) {
11034     case CheckpointMode::Full:
11035       // Ensures that the database is completely checkpointed and flushed to
11036       // disk.
11037       stmtString.AppendLiteral("FULL");
11038       break;
11039 
11040     case CheckpointMode::Restart:
11041       // Like Full, but also ensures that the next write will start overwriting
11042       // the existing WAL file rather than letting the WAL file grow.
11043       stmtString.AppendLiteral("RESTART");
11044       break;
11045 
11046     case CheckpointMode::Truncate:
11047       // Like Restart but also truncates the existing WAL file.
11048       stmtString.AppendLiteral("TRUNCATE");
11049       break;
11050 
11051     default:
11052       MOZ_CRASH("Unknown CheckpointMode!");
11053   }
11054 
11055   stmtString.AppendLiteral(");");
11056 
11057   CachedStatement stmt;
11058   nsresult rv = GetCachedStatement(stmtString, &stmt);
11059   if (NS_WARN_IF(NS_FAILED(rv))) {
11060     return rv;
11061   }
11062 
11063   rv = stmt->Execute();
11064   if (NS_WARN_IF(NS_FAILED(rv))) {
11065     return rv;
11066   }
11067 
11068   return NS_OK;
11069 }
11070 
11071 void
DoIdleProcessing(bool aNeedsCheckpoint)11072 DatabaseConnection::DoIdleProcessing(bool aNeedsCheckpoint)
11073 {
11074   AssertIsOnConnectionThread();
11075   MOZ_ASSERT(mInReadTransaction);
11076   MOZ_ASSERT(!mInWriteTransaction);
11077 
11078   PROFILER_LABEL("IndexedDB",
11079                  "DatabaseConnection::DoIdleProcessing",
11080                  js::ProfileEntry::Category::STORAGE);
11081 
11082   DatabaseConnection::CachedStatement freelistStmt;
11083   uint32_t freelistCount;
11084   nsresult rv = GetFreelistCount(freelistStmt, &freelistCount);
11085   if (NS_WARN_IF(NS_FAILED(rv))) {
11086     freelistCount = 0;
11087   }
11088 
11089   CachedStatement rollbackStmt;
11090   CachedStatement beginStmt;
11091   if (aNeedsCheckpoint || freelistCount) {
11092     rv = GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK;"), &rollbackStmt);
11093     if (NS_WARN_IF(NS_FAILED(rv))) {
11094       return;
11095     }
11096 
11097     rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN;"), &beginStmt);
11098     if (NS_WARN_IF(NS_FAILED(rv))) {
11099       return;
11100     }
11101 
11102     // Release the connection's normal transaction. It's possible that it could
11103     // fail, but that isn't a problem here.
11104     Unused << rollbackStmt->Execute();
11105 
11106     mInReadTransaction = false;
11107   }
11108 
11109   bool freedSomePages = false;
11110 
11111   if (freelistCount) {
11112     rv = ReclaimFreePagesWhileIdle(freelistStmt,
11113                                    rollbackStmt,
11114                                    freelistCount,
11115                                    aNeedsCheckpoint,
11116                                    &freedSomePages);
11117     if (NS_WARN_IF(NS_FAILED(rv))) {
11118       MOZ_ASSERT(!freedSomePages);
11119     }
11120 
11121     // Make sure we didn't leave a transaction running.
11122     MOZ_ASSERT(!mInReadTransaction);
11123     MOZ_ASSERT(!mInWriteTransaction);
11124   }
11125 
11126   // Truncate the WAL if we were asked to or if we managed to free some space.
11127   if (aNeedsCheckpoint || freedSomePages) {
11128     rv = CheckpointInternal(CheckpointMode::Truncate);
11129     Unused << NS_WARN_IF(NS_FAILED(rv));
11130   }
11131 
11132   // Finally try to restart the read transaction if we rolled it back earlier.
11133   if (beginStmt) {
11134     rv = beginStmt->Execute();
11135     if (NS_SUCCEEDED(rv)) {
11136       mInReadTransaction = true;
11137     } else {
11138       NS_WARNING("Falied to restart read transaction!");
11139     }
11140   }
11141 }
11142 
11143 nsresult
ReclaimFreePagesWhileIdle(CachedStatement & aFreelistStatement,CachedStatement & aRollbackStatement,uint32_t aFreelistCount,bool aNeedsCheckpoint,bool * aFreedSomePages)11144 DatabaseConnection::ReclaimFreePagesWhileIdle(
11145                                             CachedStatement& aFreelistStatement,
11146                                             CachedStatement& aRollbackStatement,
11147                                             uint32_t aFreelistCount,
11148                                             bool aNeedsCheckpoint,
11149                                             bool* aFreedSomePages)
11150 {
11151   AssertIsOnConnectionThread();
11152   MOZ_ASSERT(aFreelistStatement);
11153   MOZ_ASSERT(aRollbackStatement);
11154   MOZ_ASSERT(aFreelistCount);
11155   MOZ_ASSERT(aFreedSomePages);
11156   MOZ_ASSERT(!mInReadTransaction);
11157   MOZ_ASSERT(!mInWriteTransaction);
11158 
11159   PROFILER_LABEL("IndexedDB",
11160                  "DatabaseConnection::ReclaimFreePagesWhileIdle",
11161                  js::ProfileEntry::Category::STORAGE);
11162 
11163   // Make sure we don't keep working if anything else needs this thread.
11164   nsIThread* currentThread = NS_GetCurrentThread();
11165   MOZ_ASSERT(currentThread);
11166 
11167   if (NS_HasPendingEvents(currentThread)) {
11168     *aFreedSomePages = false;
11169     return NS_OK;
11170   }
11171 
11172   // Only try to free 10% at a time so that we can bail out if this connection
11173   // suddenly becomes active or if the thread is needed otherwise.
11174   nsAutoCString stmtString;
11175   stmtString.AssignLiteral("PRAGMA incremental_vacuum(");
11176   stmtString.AppendInt(std::max(uint64_t(1), uint64_t(aFreelistCount / 10)));
11177   stmtString.AppendLiteral(");");
11178 
11179   // Make all the statements we'll need up front.
11180   CachedStatement incrementalVacuumStmt;
11181   nsresult rv = GetCachedStatement(stmtString, &incrementalVacuumStmt);
11182   if (NS_WARN_IF(NS_FAILED(rv))) {
11183     return rv;
11184   }
11185 
11186   CachedStatement beginImmediateStmt;
11187   rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN IMMEDIATE;"),
11188                           &beginImmediateStmt);
11189   if (NS_WARN_IF(NS_FAILED(rv))) {
11190     return rv;
11191   }
11192 
11193   CachedStatement commitStmt;
11194   rv = GetCachedStatement(NS_LITERAL_CSTRING("COMMIT;"), &commitStmt);
11195   if (NS_WARN_IF(NS_FAILED(rv))) {
11196     return rv;
11197   }
11198 
11199   if (aNeedsCheckpoint) {
11200     // Freeing pages is a journaled operation, so it will require additional WAL
11201     // space. However, we're idle and are about to checkpoint anyway, so doing a
11202     // RESTART checkpoint here should allow us to reuse any existing space.
11203     rv = CheckpointInternal(CheckpointMode::Restart);
11204     if (NS_WARN_IF(NS_FAILED(rv))) {
11205       return rv;
11206     }
11207   }
11208 
11209   // Start the write transaction.
11210   rv = beginImmediateStmt->Execute();
11211   if (NS_WARN_IF(NS_FAILED(rv))) {
11212     return rv;
11213   }
11214 
11215   mInWriteTransaction = true;
11216 
11217   bool freedSomePages = false;
11218 
11219   while (aFreelistCount) {
11220     if (NS_HasPendingEvents(currentThread)) {
11221       // Something else wants to use the thread so roll back this transaction.
11222       // It's ok if we never make progress here because the idle service should
11223       // eventually reclaim this space.
11224       rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
11225       break;
11226     }
11227 
11228     rv = incrementalVacuumStmt->Execute();
11229     if (NS_WARN_IF(NS_FAILED(rv))) {
11230       break;
11231     }
11232 
11233     freedSomePages = true;
11234 
11235     rv = GetFreelistCount(aFreelistStatement, &aFreelistCount);
11236     if (NS_WARN_IF(NS_FAILED(rv))) {
11237       break;
11238     }
11239   }
11240 
11241   if (NS_SUCCEEDED(rv) && freedSomePages) {
11242     // Commit the write transaction.
11243     rv = commitStmt->Execute();
11244     if (NS_SUCCEEDED(rv)) {
11245       mInWriteTransaction = false;
11246     } else {
11247       NS_WARNING("Failed to commit!");
11248     }
11249   }
11250 
11251   if (NS_FAILED(rv)) {
11252     MOZ_ASSERT(mInWriteTransaction);
11253 
11254     // Something failed, make sure we roll everything back.
11255     Unused << aRollbackStatement->Execute();
11256 
11257     mInWriteTransaction = false;
11258 
11259     return rv;
11260   }
11261 
11262   *aFreedSomePages = freedSomePages;
11263   return NS_OK;
11264 }
11265 
11266 nsresult
GetFreelistCount(CachedStatement & aCachedStatement,uint32_t * aFreelistCount)11267 DatabaseConnection::GetFreelistCount(CachedStatement& aCachedStatement,
11268                                      uint32_t* aFreelistCount)
11269 {
11270   AssertIsOnConnectionThread();
11271   MOZ_ASSERT(aFreelistCount);
11272 
11273   PROFILER_LABEL("IndexedDB",
11274                  "DatabaseConnection::GetFreelistCount",
11275                  js::ProfileEntry::Category::STORAGE);
11276 
11277   nsresult rv;
11278 
11279   if (!aCachedStatement) {
11280     rv = GetCachedStatement(NS_LITERAL_CSTRING("PRAGMA freelist_count;"),
11281                             &aCachedStatement);
11282     if (NS_WARN_IF(NS_FAILED(rv))) {
11283       return rv;
11284     }
11285   }
11286 
11287   bool hasResult;
11288   rv = aCachedStatement->ExecuteStep(&hasResult);
11289   if (NS_WARN_IF(NS_FAILED(rv))) {
11290     return rv;
11291   }
11292 
11293   MOZ_ASSERT(hasResult);
11294 
11295   // Make sure this statement is reset when leaving this function since we're
11296   // not using the normal stack-based protection of CachedStatement.
11297   mozStorageStatementScoper scoper(aCachedStatement);
11298 
11299   int32_t freelistCount;
11300   rv = aCachedStatement->GetInt32(0, &freelistCount);
11301   if (NS_WARN_IF(NS_FAILED(rv))) {
11302     return rv;
11303   }
11304 
11305   MOZ_ASSERT(freelistCount >= 0);
11306 
11307   *aFreelistCount = uint32_t(freelistCount);
11308   return NS_OK;
11309 }
11310 
11311 void
Close()11312 DatabaseConnection::Close()
11313 {
11314   AssertIsOnConnectionThread();
11315   MOZ_ASSERT(mStorageConnection);
11316   MOZ_ASSERT(!mDEBUGSavepointCount);
11317   MOZ_ASSERT(!mInWriteTransaction);
11318 
11319   PROFILER_LABEL("IndexedDB",
11320                  "DatabaseConnection::Close",
11321                  js::ProfileEntry::Category::STORAGE);
11322 
11323   if (mUpdateRefcountFunction) {
11324     MOZ_ALWAYS_SUCCEEDS(
11325       mStorageConnection->RemoveFunction(
11326         NS_LITERAL_CSTRING("update_refcount")));
11327     mUpdateRefcountFunction = nullptr;
11328   }
11329 
11330   mCachedStatements.Clear();
11331 
11332   MOZ_ALWAYS_SUCCEEDS(mStorageConnection->Close());
11333   mStorageConnection = nullptr;
11334 
11335   mFileManager = nullptr;
11336 }
11337 
11338 nsresult
DisableQuotaChecks()11339 DatabaseConnection::DisableQuotaChecks()
11340 {
11341   AssertIsOnConnectionThread();
11342   MOZ_ASSERT(mStorageConnection);
11343 
11344   if (!mQuotaObject) {
11345     MOZ_ASSERT(!mJournalQuotaObject);
11346 
11347     nsresult rv = mStorageConnection->GetQuotaObjects(
11348                                            getter_AddRefs(mQuotaObject),
11349                                            getter_AddRefs(mJournalQuotaObject));
11350     if (NS_WARN_IF(NS_FAILED(rv))) {
11351       return rv;
11352     }
11353 
11354     MOZ_ASSERT(mQuotaObject);
11355     MOZ_ASSERT(mJournalQuotaObject);
11356   }
11357 
11358   mQuotaObject->DisableQuotaCheck();
11359   mJournalQuotaObject->DisableQuotaCheck();
11360 
11361   return NS_OK;
11362 }
11363 
11364 void
EnableQuotaChecks()11365 DatabaseConnection::EnableQuotaChecks()
11366 {
11367   AssertIsOnConnectionThread();
11368   MOZ_ASSERT(mQuotaObject);
11369   MOZ_ASSERT(mJournalQuotaObject);
11370 
11371   RefPtr<QuotaObject> quotaObject;
11372   RefPtr<QuotaObject> journalQuotaObject;
11373 
11374   mQuotaObject.swap(quotaObject);
11375   mJournalQuotaObject.swap(journalQuotaObject);
11376 
11377   quotaObject->EnableQuotaCheck();
11378   journalQuotaObject->EnableQuotaCheck();
11379 
11380   int64_t fileSize;
11381   nsresult rv = GetFileSize(quotaObject->Path(), &fileSize);
11382   if (NS_WARN_IF(NS_FAILED(rv))) {
11383     return;
11384   }
11385 
11386   int64_t journalFileSize;
11387   rv = GetFileSize(journalQuotaObject->Path(), &journalFileSize);
11388   if (NS_WARN_IF(NS_FAILED(rv))) {
11389     return;
11390   }
11391 
11392   DebugOnly<bool> result =
11393     journalQuotaObject->MaybeUpdateSize(journalFileSize, /* aTruncate */ true);
11394   MOZ_ASSERT(result);
11395 
11396   result = quotaObject->MaybeUpdateSize(fileSize, /* aTruncate */ true);
11397   MOZ_ASSERT(result);
11398 }
11399 
11400 nsresult
GetFileSize(const nsAString & aPath,int64_t * aResult)11401 DatabaseConnection::GetFileSize(const nsAString& aPath, int64_t* aResult)
11402 {
11403   MOZ_ASSERT(!aPath.IsEmpty());
11404   MOZ_ASSERT(aResult);
11405 
11406   nsresult rv;
11407   nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
11408   if (NS_WARN_IF(NS_FAILED(rv))) {
11409     return rv;
11410   }
11411 
11412   rv = file->InitWithPath(aPath);
11413   if (NS_WARN_IF(NS_FAILED(rv))) {
11414     return rv;
11415   }
11416 
11417   int64_t fileSize;
11418 
11419   bool exists;
11420   rv = file->Exists(&exists);
11421   if (NS_WARN_IF(NS_FAILED(rv))) {
11422     return rv;
11423   }
11424 
11425   if (exists) {
11426     rv = file->GetFileSize(&fileSize);
11427     if (NS_WARN_IF(NS_FAILED(rv))) {
11428       return rv;
11429     }
11430   } else {
11431     fileSize = 0;
11432   }
11433 
11434   *aResult = fileSize;
11435   return NS_OK;
11436 }
11437 
11438 DatabaseConnection::
CachedStatement()11439 CachedStatement::CachedStatement()
11440 #ifdef DEBUG
11441   : mDEBUGConnection(nullptr)
11442 #endif
11443 {
11444   AssertIsOnConnectionThread();
11445 
11446   MOZ_COUNT_CTOR(DatabaseConnection::CachedStatement);
11447 }
11448 
11449 DatabaseConnection::
~CachedStatement()11450 CachedStatement::~CachedStatement()
11451 {
11452   AssertIsOnConnectionThread();
11453 
11454   MOZ_COUNT_DTOR(DatabaseConnection::CachedStatement);
11455 }
11456 
11457 DatabaseConnection::
operator mozIStorageStatement*() const11458 CachedStatement::operator mozIStorageStatement*() const
11459 {
11460   AssertIsOnConnectionThread();
11461 
11462   return mStatement;
11463 }
11464 
11465 mozIStorageStatement*
11466 DatabaseConnection::
operator ->() const11467 CachedStatement::operator->() const
11468 {
11469   AssertIsOnConnectionThread();
11470   MOZ_ASSERT(mStatement);
11471 
11472   return mStatement;
11473 }
11474 
11475 void
11476 DatabaseConnection::
Reset()11477 CachedStatement::Reset()
11478 {
11479   AssertIsOnConnectionThread();
11480   MOZ_ASSERT_IF(mStatement, mScoper);
11481 
11482   if (mStatement) {
11483     mScoper.reset();
11484     mScoper.emplace(mStatement);
11485   }
11486 }
11487 
11488 void
11489 DatabaseConnection::
Assign(DatabaseConnection * aConnection,already_AddRefed<mozIStorageStatement> aStatement)11490 CachedStatement::Assign(DatabaseConnection* aConnection,
11491                         already_AddRefed<mozIStorageStatement> aStatement)
11492 {
11493 #ifdef DEBUG
11494     MOZ_ASSERT(aConnection);
11495     aConnection->AssertIsOnConnectionThread();
11496     MOZ_ASSERT_IF(mDEBUGConnection, mDEBUGConnection == aConnection);
11497 
11498     mDEBUGConnection = aConnection;
11499 #endif
11500   AssertIsOnConnectionThread();
11501 
11502   mScoper.reset();
11503 
11504   mStatement = aStatement;
11505 
11506   if (mStatement) {
11507     mScoper.emplace(mStatement);
11508   }
11509 }
11510 
11511 DatabaseConnection::
AutoSavepoint()11512 AutoSavepoint::AutoSavepoint()
11513   : mConnection(nullptr)
11514 #ifdef DEBUG
11515   , mDEBUGTransaction(nullptr)
11516 #endif
11517 {
11518   MOZ_COUNT_CTOR(DatabaseConnection::AutoSavepoint);
11519 }
11520 
11521 DatabaseConnection::
~AutoSavepoint()11522 AutoSavepoint::~AutoSavepoint()
11523 {
11524   MOZ_COUNT_DTOR(DatabaseConnection::AutoSavepoint);
11525 
11526   if (mConnection) {
11527     mConnection->AssertIsOnConnectionThread();
11528     MOZ_ASSERT(mDEBUGTransaction);
11529     MOZ_ASSERT(mDEBUGTransaction->GetMode() == IDBTransaction::READ_WRITE ||
11530                mDEBUGTransaction->GetMode() ==
11531                  IDBTransaction::READ_WRITE_FLUSH ||
11532                mDEBUGTransaction->GetMode() == IDBTransaction::CLEANUPff ||
11533                mDEBUGTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
11534 
11535     if (NS_FAILED(mConnection->RollbackSavepoint())) {
11536       NS_WARNING("Failed to rollback savepoint!");
11537     }
11538   }
11539 }
11540 
11541 nsresult
11542 DatabaseConnection::
Start(const TransactionBase * aTransaction)11543 AutoSavepoint::Start(const TransactionBase* aTransaction)
11544 {
11545   MOZ_ASSERT(aTransaction);
11546   MOZ_ASSERT(aTransaction->GetMode() == IDBTransaction::READ_WRITE ||
11547              aTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH ||
11548              aTransaction->GetMode() == IDBTransaction::CLEANUPff ||
11549              aTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
11550 
11551   DatabaseConnection* connection = aTransaction->GetDatabase()->GetConnection();
11552   MOZ_ASSERT(connection);
11553   connection->AssertIsOnConnectionThread();
11554 
11555   MOZ_ASSERT(!mConnection);
11556   MOZ_ASSERT(!mDEBUGTransaction);
11557 
11558   nsresult rv = connection->StartSavepoint();
11559   if (NS_WARN_IF(NS_FAILED(rv))) {
11560     return rv;
11561   }
11562 
11563   mConnection = connection;
11564 #ifdef DEBUG
11565   mDEBUGTransaction = aTransaction;
11566 #endif
11567 
11568   return NS_OK;
11569 }
11570 
11571 nsresult
11572 DatabaseConnection::
Commit()11573 AutoSavepoint::Commit()
11574 {
11575   MOZ_ASSERT(mConnection);
11576   mConnection->AssertIsOnConnectionThread();
11577   MOZ_ASSERT(mDEBUGTransaction);
11578 
11579   nsresult rv = mConnection->ReleaseSavepoint();
11580   if (NS_WARN_IF(NS_FAILED(rv))) {
11581     return rv;
11582   }
11583 
11584   mConnection = nullptr;
11585 #ifdef DEBUG
11586   mDEBUGTransaction = nullptr;
11587 #endif
11588 
11589   return NS_OK;
11590 }
11591 
11592 DatabaseConnection::
UpdateRefcountFunction(DatabaseConnection * aConnection,FileManager * aFileManager)11593 UpdateRefcountFunction::UpdateRefcountFunction(DatabaseConnection* aConnection,
11594                                                FileManager* aFileManager)
11595   : mConnection(aConnection)
11596   , mFileManager(aFileManager)
11597   , mInSavepoint(false)
11598 {
11599   MOZ_ASSERT(aConnection);
11600   aConnection->AssertIsOnConnectionThread();
11601   MOZ_ASSERT(aFileManager);
11602 }
11603 
11604 nsresult
11605 DatabaseConnection::
WillCommit()11606 UpdateRefcountFunction::WillCommit()
11607 {
11608   MOZ_ASSERT(mConnection);
11609   mConnection->AssertIsOnConnectionThread();
11610 
11611   PROFILER_LABEL("IndexedDB",
11612                  "DatabaseConnection::UpdateRefcountFunction::WillCommit",
11613                  js::ProfileEntry::Category::STORAGE);
11614 
11615   DatabaseUpdateFunction function(this);
11616   for (auto iter = mFileInfoEntries.ConstIter(); !iter.Done(); iter.Next()) {
11617     auto key = iter.Key();
11618     FileInfoEntry* value = iter.Data();
11619     MOZ_ASSERT(value);
11620 
11621     if (value->mDelta && !function.Update(key, value->mDelta)) {
11622       break;
11623     }
11624   }
11625 
11626   nsresult rv = function.ErrorCode();
11627   if (NS_WARN_IF(NS_FAILED(rv))) {
11628     return rv;
11629   }
11630 
11631   rv = CreateJournals();
11632   if (NS_WARN_IF(NS_FAILED(rv))) {
11633     return rv;
11634   }
11635 
11636   return NS_OK;
11637 }
11638 
11639 void
11640 DatabaseConnection::
DidCommit()11641 UpdateRefcountFunction::DidCommit()
11642 {
11643   MOZ_ASSERT(mConnection);
11644   mConnection->AssertIsOnConnectionThread();
11645 
11646   PROFILER_LABEL("IndexedDB",
11647                  "DatabaseConnection::UpdateRefcountFunction::DidCommit",
11648                  js::ProfileEntry::Category::STORAGE);
11649 
11650   for (auto iter = mFileInfoEntries.ConstIter(); !iter.Done(); iter.Next()) {
11651     FileInfoEntry* value = iter.Data();
11652 
11653     MOZ_ASSERT(value);
11654 
11655     if (value->mDelta) {
11656       value->mFileInfo->UpdateDBRefs(value->mDelta);
11657     }
11658   }
11659 
11660   if (NS_FAILED(RemoveJournals(mJournalsToRemoveAfterCommit))) {
11661     NS_WARNING("RemoveJournals failed!");
11662   }
11663 }
11664 
11665 void
11666 DatabaseConnection::
DidAbort()11667 UpdateRefcountFunction::DidAbort()
11668 {
11669   MOZ_ASSERT(mConnection);
11670   mConnection->AssertIsOnConnectionThread();
11671 
11672   PROFILER_LABEL("IndexedDB",
11673                  "DatabaseConnection::UpdateRefcountFunction::DidAbort",
11674                  js::ProfileEntry::Category::STORAGE);
11675 
11676   if (NS_FAILED(RemoveJournals(mJournalsToRemoveAfterAbort))) {
11677     NS_WARNING("RemoveJournals failed!");
11678   }
11679 }
11680 
11681 void
11682 DatabaseConnection::
StartSavepoint()11683 UpdateRefcountFunction::StartSavepoint()
11684 {
11685   MOZ_ASSERT(mConnection);
11686   mConnection->AssertIsOnConnectionThread();
11687   MOZ_ASSERT(!mInSavepoint);
11688   MOZ_ASSERT(!mSavepointEntriesIndex.Count());
11689 
11690   mInSavepoint = true;
11691 }
11692 
11693 void
11694 DatabaseConnection::
ReleaseSavepoint()11695 UpdateRefcountFunction::ReleaseSavepoint()
11696 {
11697   MOZ_ASSERT(mConnection);
11698   mConnection->AssertIsOnConnectionThread();
11699   MOZ_ASSERT(mInSavepoint);
11700 
11701   mSavepointEntriesIndex.Clear();
11702   mInSavepoint = false;
11703 }
11704 
11705 void
11706 DatabaseConnection::
RollbackSavepoint()11707 UpdateRefcountFunction::RollbackSavepoint()
11708 {
11709   MOZ_ASSERT(mConnection);
11710   mConnection->AssertIsOnConnectionThread();
11711   MOZ_ASSERT(!IsOnBackgroundThread());
11712   MOZ_ASSERT(mInSavepoint);
11713 
11714   for (auto iter = mSavepointEntriesIndex.ConstIter();
11715        !iter.Done(); iter.Next()) {
11716     auto value = iter.Data();
11717     value->mDelta -= value->mSavepointDelta;
11718   }
11719 
11720   mInSavepoint = false;
11721   mSavepointEntriesIndex.Clear();
11722 }
11723 
11724 void
11725 DatabaseConnection::
Reset()11726 UpdateRefcountFunction::Reset()
11727 {
11728   MOZ_ASSERT(mConnection);
11729   mConnection->AssertIsOnConnectionThread();
11730   MOZ_ASSERT(!mSavepointEntriesIndex.Count());
11731   MOZ_ASSERT(!mInSavepoint);
11732 
11733   class MOZ_STACK_CLASS CustomCleanupCallback final
11734     : public FileInfo::CustomCleanupCallback
11735   {
11736     nsCOMPtr<nsIFile> mDirectory;
11737     nsCOMPtr<nsIFile> mJournalDirectory;
11738 
11739   public:
11740     virtual nsresult
11741     Cleanup(FileManager* aFileManager, int64_t aId)
11742     {
11743       if (!mDirectory) {
11744         MOZ_ASSERT(!mJournalDirectory);
11745 
11746         mDirectory = aFileManager->GetDirectory();
11747         if (NS_WARN_IF(!mDirectory)) {
11748           return NS_ERROR_FAILURE;
11749         }
11750 
11751         mJournalDirectory = aFileManager->GetJournalDirectory();
11752         if (NS_WARN_IF(!mJournalDirectory)) {
11753           return NS_ERROR_FAILURE;
11754         }
11755       }
11756 
11757       nsCOMPtr<nsIFile> file = aFileManager->GetFileForId(mDirectory, aId);
11758       if (NS_WARN_IF(!file)) {
11759         return NS_ERROR_FAILURE;
11760       }
11761 
11762       nsresult rv;
11763       int64_t fileSize;
11764 
11765       if (aFileManager->EnforcingQuota()) {
11766         rv = file->GetFileSize(&fileSize);
11767         if (NS_WARN_IF(NS_FAILED(rv))) {
11768           return rv;
11769         }
11770       }
11771 
11772       rv = file->Remove(false);
11773       if (NS_WARN_IF(NS_FAILED(rv))) {
11774         return rv;
11775       }
11776 
11777       if (aFileManager->EnforcingQuota()) {
11778         QuotaManager* quotaManager = QuotaManager::Get();
11779         MOZ_ASSERT(quotaManager);
11780 
11781         quotaManager->DecreaseUsageForOrigin(aFileManager->Type(),
11782                                              aFileManager->Group(),
11783                                              aFileManager->Origin(),
11784                                              fileSize);
11785       }
11786 
11787       file = aFileManager->GetFileForId(mJournalDirectory, aId);
11788       if (NS_WARN_IF(!file)) {
11789         return NS_ERROR_FAILURE;
11790       }
11791 
11792       rv = file->Remove(false);
11793       if (NS_WARN_IF(NS_FAILED(rv))) {
11794         return rv;
11795       }
11796 
11797       return NS_OK;
11798     }
11799   };
11800 
11801   mJournalsToCreateBeforeCommit.Clear();
11802   mJournalsToRemoveAfterCommit.Clear();
11803   mJournalsToRemoveAfterAbort.Clear();
11804 
11805   // FileInfo implementation automatically removes unreferenced files, but it's
11806   // done asynchronously and with a delay. We want to remove them (and decrease
11807   // quota usage) before we fire the commit event.
11808   for (auto iter = mFileInfoEntries.ConstIter(); !iter.Done(); iter.Next()) {
11809     FileInfoEntry* value = iter.Data();
11810 
11811     MOZ_ASSERT(value);
11812 
11813     FileInfo* fileInfo = value->mFileInfo.forget().take();
11814 
11815     MOZ_ASSERT(fileInfo);
11816 
11817     CustomCleanupCallback customCleanupCallback;
11818     fileInfo->Release(&customCleanupCallback);
11819   }
11820 
11821   mFileInfoEntries.Clear();
11822 }
11823 
11824 nsresult
11825 DatabaseConnection::
ProcessValue(mozIStorageValueArray * aValues,int32_t aIndex,UpdateType aUpdateType)11826 UpdateRefcountFunction::ProcessValue(mozIStorageValueArray* aValues,
11827                                      int32_t aIndex,
11828                                      UpdateType aUpdateType)
11829 {
11830   MOZ_ASSERT(mConnection);
11831   mConnection->AssertIsOnConnectionThread();
11832   MOZ_ASSERT(aValues);
11833 
11834   PROFILER_LABEL("IndexedDB",
11835                  "DatabaseConnection::UpdateRefcountFunction::ProcessValue",
11836                  js::ProfileEntry::Category::STORAGE);
11837 
11838   int32_t type;
11839   nsresult rv = aValues->GetTypeOfIndex(aIndex, &type);
11840   if (NS_WARN_IF(NS_FAILED(rv))) {
11841     return rv;
11842   }
11843 
11844   if (type == mozIStorageValueArray::VALUE_TYPE_NULL) {
11845     return NS_OK;
11846   }
11847 
11848   nsString ids;
11849   rv = aValues->GetString(aIndex, ids);
11850   if (NS_WARN_IF(NS_FAILED(rv))) {
11851     return rv;
11852   }
11853 
11854   nsTArray<StructuredCloneFile> files;
11855   rv = DeserializeStructuredCloneFiles(mFileManager, ids, files, nullptr);
11856   if (NS_WARN_IF(NS_FAILED(rv))) {
11857     return rv;
11858   }
11859 
11860   for (uint32_t i = 0; i < files.Length(); i++) {
11861     const StructuredCloneFile& file = files[i];
11862 
11863     const int64_t id = file.mFileInfo->Id();
11864     MOZ_ASSERT(id > 0);
11865 
11866     FileInfoEntry* entry;
11867     if (!mFileInfoEntries.Get(id, &entry)) {
11868       entry = new FileInfoEntry(file.mFileInfo);
11869       mFileInfoEntries.Put(id, entry);
11870     }
11871 
11872     if (mInSavepoint) {
11873       mSavepointEntriesIndex.Put(id, entry);
11874     }
11875 
11876     switch (aUpdateType) {
11877       case UpdateType::Increment:
11878         entry->mDelta++;
11879         if (mInSavepoint) {
11880           entry->mSavepointDelta++;
11881         }
11882         break;
11883       case UpdateType::Decrement:
11884         entry->mDelta--;
11885         if (mInSavepoint) {
11886           entry->mSavepointDelta--;
11887         }
11888         break;
11889       default:
11890         MOZ_CRASH("Unknown update type!");
11891     }
11892   }
11893 
11894   return NS_OK;
11895 }
11896 
11897 nsresult
11898 DatabaseConnection::
CreateJournals()11899 UpdateRefcountFunction::CreateJournals()
11900 {
11901   MOZ_ASSERT(mConnection);
11902   mConnection->AssertIsOnConnectionThread();
11903 
11904   PROFILER_LABEL("IndexedDB",
11905                  "DatabaseConnection::UpdateRefcountFunction::CreateJournals",
11906                  js::ProfileEntry::Category::STORAGE);
11907 
11908   nsCOMPtr<nsIFile> journalDirectory = mFileManager->GetJournalDirectory();
11909   if (NS_WARN_IF(!journalDirectory)) {
11910     return NS_ERROR_FAILURE;
11911   }
11912 
11913   for (uint32_t i = 0; i < mJournalsToCreateBeforeCommit.Length(); i++) {
11914     int64_t id = mJournalsToCreateBeforeCommit[i];
11915 
11916     nsCOMPtr<nsIFile> file =
11917       mFileManager->GetFileForId(journalDirectory, id);
11918     if (NS_WARN_IF(!file)) {
11919       return NS_ERROR_FAILURE;
11920     }
11921 
11922     nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
11923     if (NS_WARN_IF(NS_FAILED(rv))) {
11924       return rv;
11925     }
11926 
11927     mJournalsToRemoveAfterAbort.AppendElement(id);
11928   }
11929 
11930   return NS_OK;
11931 }
11932 
11933 nsresult
11934 DatabaseConnection::
RemoveJournals(const nsTArray<int64_t> & aJournals)11935 UpdateRefcountFunction::RemoveJournals(const nsTArray<int64_t>& aJournals)
11936 {
11937   MOZ_ASSERT(mConnection);
11938   mConnection->AssertIsOnConnectionThread();
11939 
11940   PROFILER_LABEL("IndexedDB",
11941                  "DatabaseConnection::UpdateRefcountFunction::RemoveJournals",
11942                  js::ProfileEntry::Category::STORAGE);
11943 
11944   nsCOMPtr<nsIFile> journalDirectory = mFileManager->GetJournalDirectory();
11945   if (NS_WARN_IF(!journalDirectory)) {
11946     return NS_ERROR_FAILURE;
11947   }
11948 
11949   for (uint32_t index = 0; index < aJournals.Length(); index++) {
11950     nsCOMPtr<nsIFile> file =
11951       mFileManager->GetFileForId(journalDirectory, aJournals[index]);
11952     if (NS_WARN_IF(!file)) {
11953       return NS_ERROR_FAILURE;
11954     }
11955 
11956     if (NS_FAILED(file->Remove(false))) {
11957       NS_WARNING("Failed to removed journal!");
11958     }
11959   }
11960 
11961   return NS_OK;
11962 }
11963 
NS_IMPL_ISUPPORTS(DatabaseConnection::UpdateRefcountFunction,mozIStorageFunction)11964 NS_IMPL_ISUPPORTS(DatabaseConnection::UpdateRefcountFunction,
11965                   mozIStorageFunction)
11966 
11967 NS_IMETHODIMP
11968 DatabaseConnection::
11969 UpdateRefcountFunction::OnFunctionCall(mozIStorageValueArray* aValues,
11970                                        nsIVariant** _retval)
11971 {
11972   MOZ_ASSERT(aValues);
11973   MOZ_ASSERT(_retval);
11974 
11975   PROFILER_LABEL("IndexedDB",
11976                  "DatabaseConnection::UpdateRefcountFunction::OnFunctionCall",
11977                  js::ProfileEntry::Category::STORAGE);
11978 
11979   uint32_t numEntries;
11980   nsresult rv = aValues->GetNumEntries(&numEntries);
11981   if (NS_WARN_IF(NS_FAILED(rv))) {
11982     return rv;
11983   }
11984 
11985   MOZ_ASSERT(numEntries == 2);
11986 
11987 #ifdef DEBUG
11988   {
11989     int32_t type1 = mozIStorageValueArray::VALUE_TYPE_NULL;
11990     MOZ_ASSERT(NS_SUCCEEDED(aValues->GetTypeOfIndex(0, &type1)));
11991 
11992     int32_t type2 = mozIStorageValueArray::VALUE_TYPE_NULL;
11993     MOZ_ASSERT(NS_SUCCEEDED(aValues->GetTypeOfIndex(1, &type2)));
11994 
11995     MOZ_ASSERT(!(type1 == mozIStorageValueArray::VALUE_TYPE_NULL &&
11996                  type2 == mozIStorageValueArray::VALUE_TYPE_NULL));
11997   }
11998 #endif
11999 
12000   rv = ProcessValue(aValues, 0, UpdateType::Decrement);
12001   if (NS_WARN_IF(NS_FAILED(rv))) {
12002     return rv;
12003   }
12004 
12005   rv = ProcessValue(aValues, 1, UpdateType::Increment);
12006   if (NS_WARN_IF(NS_FAILED(rv))) {
12007     return rv;
12008   }
12009 
12010   return NS_OK;
12011 }
12012 
12013 bool
12014 DatabaseConnection::UpdateRefcountFunction::
Update(int64_t aId,int32_t aDelta)12015 DatabaseUpdateFunction::Update(int64_t aId,
12016                                int32_t aDelta)
12017 {
12018   nsresult rv = UpdateInternal(aId, aDelta);
12019   if (NS_FAILED(rv)) {
12020     mErrorCode = rv;
12021     return false;
12022   }
12023 
12024   return true;
12025 }
12026 
12027 nsresult
12028 DatabaseConnection::UpdateRefcountFunction::
UpdateInternal(int64_t aId,int32_t aDelta)12029 DatabaseUpdateFunction::UpdateInternal(int64_t aId,
12030                                        int32_t aDelta)
12031 {
12032   MOZ_ASSERT(mFunction);
12033 
12034   PROFILER_LABEL("IndexedDB",
12035                  "DatabaseConnection::UpdateRefcountFunction::"
12036                  "DatabaseUpdateFunction::UpdateInternal",
12037                  js::ProfileEntry::Category::STORAGE);
12038 
12039   DatabaseConnection* connection = mFunction->mConnection;
12040   MOZ_ASSERT(connection);
12041   connection->AssertIsOnConnectionThread();
12042 
12043   MOZ_ASSERT(connection->GetStorageConnection());
12044 
12045   nsresult rv;
12046   if (!mUpdateStatement) {
12047     rv = connection->GetCachedStatement(NS_LITERAL_CSTRING(
12048       "UPDATE file "
12049       "SET refcount = refcount + :delta "
12050       "WHERE id = :id"),
12051       &mUpdateStatement);
12052     if (NS_WARN_IF(NS_FAILED(rv))) {
12053       return rv;
12054     }
12055   }
12056 
12057   mozStorageStatementScoper updateScoper(mUpdateStatement);
12058 
12059   rv = mUpdateStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
12060   if (NS_WARN_IF(NS_FAILED(rv))) {
12061     return rv;
12062   }
12063 
12064   rv = mUpdateStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
12065   if (NS_WARN_IF(NS_FAILED(rv))) {
12066     return rv;
12067   }
12068 
12069   rv = mUpdateStatement->Execute();
12070   if (NS_WARN_IF(NS_FAILED(rv))) {
12071     return rv;
12072   }
12073 
12074   int32_t rows;
12075   rv = connection->GetStorageConnection()->GetAffectedRows(&rows);
12076   if (NS_WARN_IF(NS_FAILED(rv))) {
12077     return rv;
12078   }
12079 
12080   if (rows > 0) {
12081     if (!mSelectStatement) {
12082       rv = connection->GetCachedStatement(NS_LITERAL_CSTRING(
12083         "SELECT id "
12084         "FROM file "
12085         "WHERE id = :id"),
12086         &mSelectStatement);
12087       if (NS_WARN_IF(NS_FAILED(rv))) {
12088         return rv;
12089       }
12090     }
12091 
12092     mozStorageStatementScoper selectScoper(mSelectStatement);
12093 
12094     rv = mSelectStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
12095     if (NS_WARN_IF(NS_FAILED(rv))) {
12096       return rv;
12097     }
12098 
12099     bool hasResult;
12100     rv = mSelectStatement->ExecuteStep(&hasResult);
12101     if (NS_WARN_IF(NS_FAILED(rv))) {
12102       return rv;
12103     }
12104 
12105     if (!hasResult) {
12106       // Don't have to create the journal here, we can create all at once,
12107       // just before commit
12108       mFunction->mJournalsToCreateBeforeCommit.AppendElement(aId);
12109     }
12110 
12111     return NS_OK;
12112   }
12113 
12114   if (!mInsertStatement) {
12115     rv = connection->GetCachedStatement(NS_LITERAL_CSTRING(
12116       "INSERT INTO file (id, refcount) "
12117       "VALUES(:id, :delta)"),
12118       &mInsertStatement);
12119     if (NS_WARN_IF(NS_FAILED(rv))) {
12120       return rv;
12121     }
12122   }
12123 
12124   mozStorageStatementScoper insertScoper(mInsertStatement);
12125 
12126   rv = mInsertStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
12127   if (NS_WARN_IF(NS_FAILED(rv))) {
12128     return rv;
12129   }
12130 
12131   rv = mInsertStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
12132   if (NS_WARN_IF(NS_FAILED(rv))) {
12133     return rv;
12134   }
12135 
12136   rv = mInsertStatement->Execute();
12137   if (NS_WARN_IF(NS_FAILED(rv))) {
12138     return rv;
12139   }
12140 
12141   mFunction->mJournalsToRemoveAfterCommit.AppendElement(aId);
12142   return NS_OK;
12143 }
12144 
12145 /*******************************************************************************
12146  * ConnectionPool implementation
12147  ******************************************************************************/
12148 
ConnectionPool()12149 ConnectionPool::ConnectionPool()
12150   : mDatabasesMutex("ConnectionPool::mDatabasesMutex")
12151   , mIdleTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
12152   , mNextTransactionId(0)
12153   , mTotalThreadCount(0)
12154   , mShutdownRequested(false)
12155   , mShutdownComplete(false)
12156 #ifdef DEBUG
12157   , mDEBUGOwningThread(PR_GetCurrentThread())
12158 #endif
12159 {
12160   AssertIsOnOwningThread();
12161   AssertIsOnBackgroundThread();
12162   MOZ_ASSERT(mIdleTimer);
12163 }
12164 
~ConnectionPool()12165 ConnectionPool::~ConnectionPool()
12166 {
12167   AssertIsOnOwningThread();
12168   MOZ_ASSERT(mIdleThreads.IsEmpty());
12169   MOZ_ASSERT(mIdleDatabases.IsEmpty());
12170   MOZ_ASSERT(!mIdleTimer);
12171   MOZ_ASSERT(mTargetIdleTime.IsNull());
12172   MOZ_ASSERT(!mDatabases.Count());
12173   MOZ_ASSERT(!mTransactions.Count());
12174   MOZ_ASSERT(mQueuedTransactions.IsEmpty());
12175   MOZ_ASSERT(mCompleteCallbacks.IsEmpty());
12176   MOZ_ASSERT(!mTotalThreadCount);
12177   MOZ_ASSERT(mShutdownRequested);
12178   MOZ_ASSERT(mShutdownComplete);
12179 }
12180 
12181 #ifdef DEBUG
12182 
12183 void
AssertIsOnOwningThread() const12184 ConnectionPool::AssertIsOnOwningThread() const
12185 {
12186   MOZ_ASSERT(mDEBUGOwningThread);
12187   MOZ_ASSERT(PR_GetCurrentThread() == mDEBUGOwningThread);
12188 }
12189 
12190 #endif // DEBUG
12191 
12192 // static
12193 void
IdleTimerCallback(nsITimer * aTimer,void * aClosure)12194 ConnectionPool::IdleTimerCallback(nsITimer* aTimer, void* aClosure)
12195 {
12196   MOZ_ASSERT(aTimer);
12197 
12198   PROFILER_LABEL("IndexedDB",
12199                  "ConnectionPool::IdleTimerCallback",
12200                  js::ProfileEntry::Category::STORAGE);
12201 
12202   auto* self = static_cast<ConnectionPool*>(aClosure);
12203   MOZ_ASSERT(self);
12204   MOZ_ASSERT(self->mIdleTimer);
12205   MOZ_ASSERT(SameCOMIdentity(self->mIdleTimer, aTimer));
12206   MOZ_ASSERT(!self->mTargetIdleTime.IsNull());
12207   MOZ_ASSERT_IF(self->mIdleDatabases.IsEmpty(), !self->mIdleThreads.IsEmpty());
12208   MOZ_ASSERT_IF(self->mIdleThreads.IsEmpty(), !self->mIdleDatabases.IsEmpty());
12209 
12210   self->mTargetIdleTime = TimeStamp();
12211 
12212   // Cheat a little.
12213   TimeStamp now = TimeStamp::NowLoRes() + TimeDuration::FromMilliseconds(500);
12214 
12215   uint32_t index = 0;
12216 
12217   for (uint32_t count = self->mIdleDatabases.Length(); index < count; index++) {
12218     IdleDatabaseInfo& info = self->mIdleDatabases[index];
12219 
12220     if (now >= info.mIdleTime) {
12221       if (info.mDatabaseInfo->mIdle) {
12222         self->PerformIdleDatabaseMaintenance(info.mDatabaseInfo);
12223       } else {
12224         self->CloseDatabase(info.mDatabaseInfo);
12225       }
12226     } else {
12227       break;
12228     }
12229   }
12230 
12231   if (index) {
12232     self->mIdleDatabases.RemoveElementsAt(0, index);
12233 
12234     index = 0;
12235   }
12236 
12237   for (uint32_t count = self->mIdleThreads.Length(); index < count; index++) {
12238     IdleThreadInfo& info = self->mIdleThreads[index];
12239     MOZ_ASSERT(info.mThreadInfo.mThread);
12240     MOZ_ASSERT(info.mThreadInfo.mRunnable);
12241 
12242     if (now >= info.mIdleTime) {
12243       self->ShutdownThread(info.mThreadInfo);
12244     } else {
12245       break;
12246     }
12247   }
12248 
12249   if (index) {
12250     self->mIdleThreads.RemoveElementsAt(0, index);
12251   }
12252 
12253   self->AdjustIdleTimer();
12254 }
12255 
12256 nsresult
GetOrCreateConnection(const Database * aDatabase,DatabaseConnection ** aConnection)12257 ConnectionPool::GetOrCreateConnection(const Database* aDatabase,
12258                                       DatabaseConnection** aConnection)
12259 {
12260   MOZ_ASSERT(!NS_IsMainThread());
12261   MOZ_ASSERT(!IsOnBackgroundThread());
12262   MOZ_ASSERT(aDatabase);
12263 
12264   PROFILER_LABEL("IndexedDB",
12265                  "ConnectionPool::GetOrCreateConnection",
12266                  js::ProfileEntry::Category::STORAGE);
12267 
12268   DatabaseInfo* dbInfo;
12269   {
12270     MutexAutoLock lock(mDatabasesMutex);
12271 
12272     dbInfo = mDatabases.Get(aDatabase->Id());
12273   }
12274 
12275   MOZ_ASSERT(dbInfo);
12276 
12277   RefPtr<DatabaseConnection> connection = dbInfo->mConnection;
12278   if (!connection) {
12279     MOZ_ASSERT(!dbInfo->mDEBUGConnectionThread);
12280 
12281     nsCOMPtr<mozIStorageConnection> storageConnection;
12282     nsresult rv =
12283       GetStorageConnection(aDatabase->FilePath(),
12284                            aDatabase->Type(),
12285                            aDatabase->Group(),
12286                            aDatabase->Origin(),
12287                            aDatabase->TelemetryId(),
12288                            getter_AddRefs(storageConnection));
12289     if (NS_WARN_IF(NS_FAILED(rv))) {
12290       return rv;
12291     }
12292 
12293     connection =
12294       new DatabaseConnection(storageConnection, aDatabase->GetFileManager());
12295 
12296     rv = connection->Init();
12297     if (NS_WARN_IF(NS_FAILED(rv))) {
12298       return rv;
12299     }
12300 
12301     dbInfo->mConnection = connection;
12302 
12303     IDB_DEBUG_LOG(("ConnectionPool created connection 0x%p for '%s'",
12304                    dbInfo->mConnection.get(),
12305                    NS_ConvertUTF16toUTF8(aDatabase->FilePath()).get()));
12306 
12307 #ifdef DEBUG
12308     dbInfo->mDEBUGConnectionThread = PR_GetCurrentThread();
12309 #endif
12310   }
12311 
12312   dbInfo->AssertIsOnConnectionThread();
12313 
12314   connection.forget(aConnection);
12315   return NS_OK;
12316 }
12317 
12318 uint64_t
Start(const nsID & aBackgroundChildLoggingId,const nsACString & aDatabaseId,int64_t aLoggingSerialNumber,const nsTArray<nsString> & aObjectStoreNames,bool aIsWriteTransaction,TransactionDatabaseOperationBase * aTransactionOp)12319 ConnectionPool::Start(const nsID& aBackgroundChildLoggingId,
12320                       const nsACString& aDatabaseId,
12321                       int64_t aLoggingSerialNumber,
12322                       const nsTArray<nsString>& aObjectStoreNames,
12323                       bool aIsWriteTransaction,
12324                       TransactionDatabaseOperationBase* aTransactionOp)
12325 {
12326   AssertIsOnOwningThread();
12327   MOZ_ASSERT(!aDatabaseId.IsEmpty());
12328   MOZ_ASSERT(mNextTransactionId < UINT64_MAX);
12329   MOZ_ASSERT(!mShutdownRequested);
12330 
12331   PROFILER_LABEL("IndexedDB",
12332                  "ConnectionPool::Start",
12333                  js::ProfileEntry::Category::STORAGE);
12334 
12335   const uint64_t transactionId = ++mNextTransactionId;
12336 
12337   DatabaseInfo* dbInfo = mDatabases.Get(aDatabaseId);
12338 
12339   const bool databaseInfoIsNew = !dbInfo;
12340 
12341   if (databaseInfoIsNew) {
12342     dbInfo = new DatabaseInfo(this, aDatabaseId);
12343 
12344     MutexAutoLock lock(mDatabasesMutex);
12345 
12346     mDatabases.Put(aDatabaseId, dbInfo);
12347   }
12348 
12349   TransactionInfo* transactionInfo =
12350     new TransactionInfo(dbInfo,
12351                         aBackgroundChildLoggingId,
12352                         aDatabaseId,
12353                         transactionId,
12354                         aLoggingSerialNumber,
12355                         aObjectStoreNames,
12356                         aIsWriteTransaction,
12357                         aTransactionOp);
12358 
12359   MOZ_ASSERT(!mTransactions.Get(transactionId));
12360   mTransactions.Put(transactionId, transactionInfo);
12361 
12362   if (aIsWriteTransaction) {
12363     MOZ_ASSERT(dbInfo->mWriteTransactionCount < UINT32_MAX);
12364     dbInfo->mWriteTransactionCount++;
12365   } else {
12366     MOZ_ASSERT(dbInfo->mReadTransactionCount < UINT32_MAX);
12367     dbInfo->mReadTransactionCount++;
12368   }
12369 
12370   auto& blockingTransactions = dbInfo->mBlockingTransactions;
12371 
12372   for (uint32_t nameIndex = 0, nameCount = aObjectStoreNames.Length();
12373        nameIndex < nameCount;
12374        nameIndex++) {
12375     const nsString& objectStoreName = aObjectStoreNames[nameIndex];
12376 
12377     TransactionInfoPair* blockInfo = blockingTransactions.Get(objectStoreName);
12378     if (!blockInfo) {
12379       blockInfo = new TransactionInfoPair();
12380       blockingTransactions.Put(objectStoreName, blockInfo);
12381     }
12382 
12383     // Mark what we are blocking on.
12384     if (TransactionInfo* blockingRead = blockInfo->mLastBlockingReads) {
12385       transactionInfo->mBlockedOn.PutEntry(blockingRead);
12386       blockingRead->AddBlockingTransaction(transactionInfo);
12387     }
12388 
12389     if (aIsWriteTransaction) {
12390       if (const uint32_t writeCount = blockInfo->mLastBlockingWrites.Length()) {
12391         for (uint32_t writeIndex = 0; writeIndex < writeCount; writeIndex++) {
12392           TransactionInfo* blockingWrite =
12393             blockInfo->mLastBlockingWrites[writeIndex];
12394           MOZ_ASSERT(blockingWrite);
12395 
12396           transactionInfo->mBlockedOn.PutEntry(blockingWrite);
12397           blockingWrite->AddBlockingTransaction(transactionInfo);
12398         }
12399       }
12400 
12401       blockInfo->mLastBlockingReads = transactionInfo;
12402       blockInfo->mLastBlockingWrites.Clear();
12403     } else {
12404       blockInfo->mLastBlockingWrites.AppendElement(transactionInfo);
12405     }
12406   }
12407 
12408   if (!transactionInfo->mBlockedOn.Count()) {
12409     Unused << ScheduleTransaction(transactionInfo,
12410                                   /* aFromQueuedTransactions */ false);
12411   }
12412 
12413   if (!databaseInfoIsNew &&
12414       (mIdleDatabases.RemoveElement(dbInfo) ||
12415        mDatabasesPerformingIdleMaintenance.RemoveElement(dbInfo))) {
12416     AdjustIdleTimer();
12417   }
12418 
12419   return transactionId;
12420 }
12421 
12422 void
Dispatch(uint64_t aTransactionId,nsIRunnable * aRunnable)12423 ConnectionPool::Dispatch(uint64_t aTransactionId, nsIRunnable* aRunnable)
12424 {
12425   AssertIsOnOwningThread();
12426   MOZ_ASSERT(aRunnable);
12427 
12428   PROFILER_LABEL("IndexedDB",
12429                  "ConnectionPool::Dispatch",
12430                  js::ProfileEntry::Category::STORAGE);
12431 
12432   TransactionInfo* transactionInfo = mTransactions.Get(aTransactionId);
12433   MOZ_ASSERT(transactionInfo);
12434   MOZ_ASSERT(!transactionInfo->mFinished);
12435 
12436   if (transactionInfo->mRunning) {
12437     DatabaseInfo* dbInfo = transactionInfo->mDatabaseInfo;
12438     MOZ_ASSERT(dbInfo);
12439     MOZ_ASSERT(dbInfo->mThreadInfo.mThread);
12440     MOZ_ASSERT(dbInfo->mThreadInfo.mRunnable);
12441     MOZ_ASSERT(!dbInfo->mClosing);
12442     MOZ_ASSERT_IF(transactionInfo->mIsWriteTransaction,
12443                   dbInfo->mRunningWriteTransaction == transactionInfo);
12444 
12445     MOZ_ALWAYS_SUCCEEDS(
12446       dbInfo->mThreadInfo.mThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL));
12447   } else {
12448     transactionInfo->mQueuedRunnables.AppendElement(aRunnable);
12449   }
12450 }
12451 
12452 void
Finish(uint64_t aTransactionId,FinishCallback * aCallback)12453 ConnectionPool::Finish(uint64_t aTransactionId, FinishCallback* aCallback)
12454 {
12455   AssertIsOnOwningThread();
12456 
12457 #ifdef DEBUG
12458   TransactionInfo* transactionInfo = mTransactions.Get(aTransactionId);
12459   MOZ_ASSERT(transactionInfo);
12460   MOZ_ASSERT(!transactionInfo->mFinished);
12461 #endif
12462 
12463   PROFILER_LABEL("IndexedDB",
12464                  "ConnectionPool::Finish",
12465                  js::ProfileEntry::Category::STORAGE);
12466 
12467   RefPtr<FinishCallbackWrapper> wrapper =
12468     new FinishCallbackWrapper(this, aTransactionId, aCallback);
12469 
12470   Dispatch(aTransactionId, wrapper);
12471 
12472 #ifdef DEBUG
12473   MOZ_ASSERT(!transactionInfo->mFinished);
12474   transactionInfo->mFinished = true;
12475 #endif
12476 }
12477 
12478 void
WaitForDatabasesToComplete(nsTArray<nsCString> && aDatabaseIds,nsIRunnable * aCallback)12479 ConnectionPool::WaitForDatabasesToComplete(nsTArray<nsCString>&& aDatabaseIds,
12480                                            nsIRunnable* aCallback)
12481 {
12482   AssertIsOnOwningThread();
12483   MOZ_ASSERT(!aDatabaseIds.IsEmpty());
12484   MOZ_ASSERT(aCallback);
12485 
12486   PROFILER_LABEL("IndexedDB",
12487                  "ConnectionPool::WaitForDatabasesToComplete",
12488                  js::ProfileEntry::Category::STORAGE);
12489 
12490   bool mayRunCallbackImmediately = true;
12491 
12492   for (uint32_t index = 0, count = aDatabaseIds.Length();
12493        index < count;
12494        index++) {
12495     const nsCString& databaseId = aDatabaseIds[index];
12496     MOZ_ASSERT(!databaseId.IsEmpty());
12497 
12498     if (CloseDatabaseWhenIdleInternal(databaseId)) {
12499       mayRunCallbackImmediately = false;
12500     }
12501   }
12502 
12503   if (mayRunCallbackImmediately) {
12504     Unused << aCallback->Run();
12505     return;
12506   }
12507 
12508   nsAutoPtr<DatabasesCompleteCallback> callback(
12509     new DatabasesCompleteCallback(Move(aDatabaseIds), aCallback));
12510   mCompleteCallbacks.AppendElement(callback.forget());
12511 }
12512 
12513 void
Shutdown()12514 ConnectionPool::Shutdown()
12515 {
12516   AssertIsOnOwningThread();
12517   MOZ_ASSERT(!mShutdownRequested);
12518   MOZ_ASSERT(!mShutdownComplete);
12519 
12520   PROFILER_LABEL("IndexedDB",
12521                  "ConnectionPool::Shutdown",
12522                  js::ProfileEntry::Category::STORAGE);
12523 
12524   mShutdownRequested = true;
12525 
12526   CancelIdleTimer();
12527   MOZ_ASSERT(mTargetIdleTime.IsNull());
12528 
12529   mIdleTimer = nullptr;
12530 
12531   CloseIdleDatabases();
12532 
12533   ShutdownIdleThreads();
12534 
12535   if (!mDatabases.Count()) {
12536     MOZ_ASSERT(!mTransactions.Count());
12537 
12538     Cleanup();
12539 
12540     MOZ_ASSERT(mShutdownComplete);
12541     return;
12542   }
12543 
12544   nsIThread* currentThread = NS_GetCurrentThread();
12545   MOZ_ASSERT(currentThread);
12546 
12547   while (!mShutdownComplete) {
12548     MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread));
12549   }
12550 }
12551 
12552 void
Cleanup()12553 ConnectionPool::Cleanup()
12554 {
12555   AssertIsOnOwningThread();
12556   MOZ_ASSERT(mShutdownRequested);
12557   MOZ_ASSERT(!mShutdownComplete);
12558   MOZ_ASSERT(!mDatabases.Count());
12559   MOZ_ASSERT(!mTransactions.Count());
12560   MOZ_ASSERT(mIdleThreads.IsEmpty());
12561 
12562   PROFILER_LABEL("IndexedDB",
12563                  "ConnectionPool::Cleanup",
12564                  js::ProfileEntry::Category::STORAGE);
12565 
12566   if (!mCompleteCallbacks.IsEmpty()) {
12567     // Run all callbacks manually now.
12568     for (uint32_t count = mCompleteCallbacks.Length(), index = 0;
12569          index < count;
12570          index++) {
12571       nsAutoPtr<DatabasesCompleteCallback> completeCallback(
12572         mCompleteCallbacks[index].forget());
12573       MOZ_ASSERT(completeCallback);
12574       MOZ_ASSERT(completeCallback->mCallback);
12575 
12576       Unused << completeCallback->mCallback->Run();
12577     }
12578 
12579     mCompleteCallbacks.Clear();
12580 
12581     // And make sure they get processed.
12582     nsIThread* currentThread = NS_GetCurrentThread();
12583     MOZ_ASSERT(currentThread);
12584 
12585     MOZ_ALWAYS_SUCCEEDS(NS_ProcessPendingEvents(currentThread));
12586   }
12587 
12588   mShutdownComplete = true;
12589 }
12590 
12591 void
AdjustIdleTimer()12592 ConnectionPool::AdjustIdleTimer()
12593 {
12594   AssertIsOnOwningThread();
12595   MOZ_ASSERT(mIdleTimer);
12596 
12597   PROFILER_LABEL("IndexedDB",
12598                  "ConnectionPool::AdjustIdleTimer",
12599                  js::ProfileEntry::Category::STORAGE);
12600 
12601   // Figure out the next time at which we should release idle resources. This
12602   // includes both databases and threads.
12603   TimeStamp newTargetIdleTime;
12604   MOZ_ASSERT(newTargetIdleTime.IsNull());
12605 
12606   if (!mIdleDatabases.IsEmpty()) {
12607     newTargetIdleTime = mIdleDatabases[0].mIdleTime;
12608   }
12609 
12610   if (!mIdleThreads.IsEmpty()) {
12611     const TimeStamp& idleTime = mIdleThreads[0].mIdleTime;
12612 
12613     if (newTargetIdleTime.IsNull() || idleTime < newTargetIdleTime) {
12614       newTargetIdleTime = idleTime;
12615     }
12616   }
12617 
12618   MOZ_ASSERT_IF(newTargetIdleTime.IsNull(), mIdleDatabases.IsEmpty());
12619   MOZ_ASSERT_IF(newTargetIdleTime.IsNull(), mIdleThreads.IsEmpty());
12620 
12621   // Cancel the timer if it was running and the new target time is different.
12622   if (!mTargetIdleTime.IsNull() &&
12623       (newTargetIdleTime.IsNull() || mTargetIdleTime != newTargetIdleTime)) {
12624     CancelIdleTimer();
12625 
12626     MOZ_ASSERT(mTargetIdleTime.IsNull());
12627   }
12628 
12629   // Schedule the timer if we have a target time different than before.
12630   if (!newTargetIdleTime.IsNull() &&
12631       (mTargetIdleTime.IsNull() || mTargetIdleTime != newTargetIdleTime)) {
12632     double delta = (newTargetIdleTime - TimeStamp::NowLoRes()).ToMilliseconds();
12633 
12634     uint32_t delay;
12635     if (delta > 0) {
12636       delay = uint32_t(std::min(delta, double(UINT32_MAX)));
12637     } else {
12638       delay = 0;
12639     }
12640 
12641     MOZ_ALWAYS_SUCCEEDS(
12642       mIdleTimer->InitWithFuncCallback(IdleTimerCallback,
12643                                        this,
12644                                        delay,
12645                                        nsITimer::TYPE_ONE_SHOT));
12646 
12647     mTargetIdleTime = newTargetIdleTime;
12648   }
12649 }
12650 
12651 void
CancelIdleTimer()12652 ConnectionPool::CancelIdleTimer()
12653 {
12654   AssertIsOnOwningThread();
12655   MOZ_ASSERT(mIdleTimer);
12656 
12657   if (!mTargetIdleTime.IsNull()) {
12658     MOZ_ALWAYS_SUCCEEDS(mIdleTimer->Cancel());
12659 
12660     mTargetIdleTime = TimeStamp();
12661     MOZ_ASSERT(mTargetIdleTime.IsNull());
12662   }
12663 }
12664 
12665 void
ShutdownThread(ThreadInfo & aThreadInfo)12666 ConnectionPool::ShutdownThread(ThreadInfo& aThreadInfo)
12667 {
12668   AssertIsOnOwningThread();
12669   MOZ_ASSERT(aThreadInfo.mThread);
12670   MOZ_ASSERT(aThreadInfo.mRunnable);
12671   MOZ_ASSERT(mTotalThreadCount);
12672 
12673   RefPtr<ThreadRunnable> runnable;
12674   aThreadInfo.mRunnable.swap(runnable);
12675 
12676   nsCOMPtr<nsIThread> thread;
12677   aThreadInfo.mThread.swap(thread);
12678 
12679   IDB_DEBUG_LOG(("ConnectionPool shutting down thread %lu",
12680                  runnable->SerialNumber()));
12681 
12682   // This should clean up the thread with the profiler.
12683   MOZ_ALWAYS_SUCCEEDS(thread->Dispatch(runnable.forget(),
12684                                        NS_DISPATCH_NORMAL));
12685 
12686   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
12687                         NewRunnableMethod(thread, &nsIThread::Shutdown)));
12688 
12689   mTotalThreadCount--;
12690 }
12691 
12692 void
CloseIdleDatabases()12693 ConnectionPool::CloseIdleDatabases()
12694 {
12695   AssertIsOnOwningThread();
12696   MOZ_ASSERT(mShutdownRequested);
12697 
12698   PROFILER_LABEL("IndexedDB",
12699                  "ConnectionPool::CloseIdleDatabases",
12700                  js::ProfileEntry::Category::STORAGE);
12701 
12702   if (!mIdleDatabases.IsEmpty()) {
12703     for (IdleDatabaseInfo& idleInfo : mIdleDatabases) {
12704       CloseDatabase(idleInfo.mDatabaseInfo);
12705     }
12706     mIdleDatabases.Clear();
12707   }
12708 
12709   if (!mDatabasesPerformingIdleMaintenance.IsEmpty()) {
12710     for (DatabaseInfo* dbInfo : mDatabasesPerformingIdleMaintenance) {
12711       MOZ_ASSERT(dbInfo);
12712       CloseDatabase(dbInfo);
12713     }
12714     mDatabasesPerformingIdleMaintenance.Clear();
12715   }
12716 }
12717 
12718 void
ShutdownIdleThreads()12719 ConnectionPool::ShutdownIdleThreads()
12720 {
12721   AssertIsOnOwningThread();
12722   MOZ_ASSERT(mShutdownRequested);
12723 
12724   PROFILER_LABEL("IndexedDB",
12725                  "ConnectionPool::ShutdownIdleThreads",
12726                  js::ProfileEntry::Category::STORAGE);
12727 
12728   if (!mIdleThreads.IsEmpty()) {
12729     for (uint32_t threadCount = mIdleThreads.Length(), threadIndex = 0;
12730          threadIndex < threadCount;
12731          threadIndex++) {
12732       ShutdownThread(mIdleThreads[threadIndex].mThreadInfo);
12733     }
12734     mIdleThreads.Clear();
12735   }
12736 }
12737 
12738 bool
ScheduleTransaction(TransactionInfo * aTransactionInfo,bool aFromQueuedTransactions)12739 ConnectionPool::ScheduleTransaction(TransactionInfo* aTransactionInfo,
12740                                     bool aFromQueuedTransactions)
12741 {
12742   AssertIsOnOwningThread();
12743   MOZ_ASSERT(aTransactionInfo);
12744 
12745   PROFILER_LABEL("IndexedDB",
12746                  "ConnectionPool::ScheduleTransaction",
12747                  js::ProfileEntry::Category::STORAGE);
12748 
12749   DatabaseInfo* dbInfo = aTransactionInfo->mDatabaseInfo;
12750   MOZ_ASSERT(dbInfo);
12751 
12752   dbInfo->mIdle = false;
12753 
12754   if (dbInfo->mClosing) {
12755     MOZ_ASSERT(!mIdleDatabases.Contains(dbInfo));
12756     MOZ_ASSERT(
12757       !dbInfo->mTransactionsScheduledDuringClose.Contains(aTransactionInfo));
12758 
12759     dbInfo->mTransactionsScheduledDuringClose.AppendElement(aTransactionInfo);
12760     return true;
12761   }
12762 
12763   if (!dbInfo->mThreadInfo.mThread) {
12764     MOZ_ASSERT(!dbInfo->mThreadInfo.mRunnable);
12765 
12766     if (mIdleThreads.IsEmpty()) {
12767       bool created = false;
12768 
12769       if (mTotalThreadCount < kMaxConnectionThreadCount) {
12770         // This will set the thread up with the profiler.
12771         RefPtr<ThreadRunnable> runnable = new ThreadRunnable();
12772 
12773         nsCOMPtr<nsIThread> newThread;
12774         if (NS_SUCCEEDED(NS_NewThread(getter_AddRefs(newThread), runnable))) {
12775           MOZ_ASSERT(newThread);
12776 
12777           IDB_DEBUG_LOG(("ConnectionPool created thread %lu",
12778                          runnable->SerialNumber()));
12779 
12780           dbInfo->mThreadInfo.mThread.swap(newThread);
12781           dbInfo->mThreadInfo.mRunnable.swap(runnable);
12782 
12783           mTotalThreadCount++;
12784           created = true;
12785         } else {
12786           NS_WARNING("Failed to make new thread!");
12787         }
12788       } else if (!mDatabasesPerformingIdleMaintenance.IsEmpty()) {
12789         // We need a thread right now so force all idle processing to stop by
12790         // posting a dummy runnable to each thread that might be doing idle
12791         // maintenance.
12792         nsCOMPtr<nsIRunnable> runnable = new Runnable();
12793 
12794         for (uint32_t index = mDatabasesPerformingIdleMaintenance.Length();
12795              index > 0;
12796              index--) {
12797           DatabaseInfo* dbInfo = mDatabasesPerformingIdleMaintenance[index - 1];
12798           MOZ_ASSERT(dbInfo);
12799           MOZ_ASSERT(dbInfo->mThreadInfo.mThread);
12800 
12801           MOZ_ALWAYS_SUCCEEDS(
12802             dbInfo->mThreadInfo.mThread->Dispatch(runnable.forget(),
12803                                                   NS_DISPATCH_NORMAL));
12804         }
12805       }
12806 
12807       if (!created) {
12808         if (!aFromQueuedTransactions) {
12809           MOZ_ASSERT(!mQueuedTransactions.Contains(aTransactionInfo));
12810           mQueuedTransactions.AppendElement(aTransactionInfo);
12811         }
12812         return false;
12813       }
12814     } else {
12815       const uint32_t lastIndex = mIdleThreads.Length() - 1;
12816 
12817       ThreadInfo& threadInfo = mIdleThreads[lastIndex].mThreadInfo;
12818 
12819       dbInfo->mThreadInfo.mRunnable.swap(threadInfo.mRunnable);
12820       dbInfo->mThreadInfo.mThread.swap(threadInfo.mThread);
12821 
12822       mIdleThreads.RemoveElementAt(lastIndex);
12823 
12824       AdjustIdleTimer();
12825     }
12826   }
12827 
12828   MOZ_ASSERT(dbInfo->mThreadInfo.mThread);
12829   MOZ_ASSERT(dbInfo->mThreadInfo.mRunnable);
12830 
12831   if (aTransactionInfo->mIsWriteTransaction) {
12832     if (dbInfo->mRunningWriteTransaction) {
12833       // SQLite only allows one write transaction at a time so queue this
12834       // transaction for later.
12835       MOZ_ASSERT(
12836         !dbInfo->mScheduledWriteTransactions.Contains(aTransactionInfo));
12837 
12838       dbInfo->mScheduledWriteTransactions.AppendElement(aTransactionInfo);
12839       return true;
12840     }
12841 
12842     dbInfo->mRunningWriteTransaction = aTransactionInfo;
12843     dbInfo->mNeedsCheckpoint = true;
12844   }
12845 
12846   MOZ_ASSERT(!aTransactionInfo->mRunning);
12847   aTransactionInfo->mRunning = true;
12848 
12849   nsTArray<nsCOMPtr<nsIRunnable>>& queuedRunnables =
12850     aTransactionInfo->mQueuedRunnables;
12851 
12852   if (!queuedRunnables.IsEmpty()) {
12853     for (uint32_t index = 0, count = queuedRunnables.Length();
12854          index < count;
12855          index++) {
12856       nsCOMPtr<nsIRunnable> runnable;
12857       queuedRunnables[index].swap(runnable);
12858 
12859       MOZ_ALWAYS_SUCCEEDS(
12860         dbInfo->mThreadInfo.mThread->Dispatch(runnable.forget(),
12861                                               NS_DISPATCH_NORMAL));
12862     }
12863 
12864     queuedRunnables.Clear();
12865   }
12866 
12867   return true;
12868 }
12869 
12870 void
NoteFinishedTransaction(uint64_t aTransactionId)12871 ConnectionPool::NoteFinishedTransaction(uint64_t aTransactionId)
12872 {
12873   AssertIsOnOwningThread();
12874 
12875   PROFILER_LABEL("IndexedDB",
12876                  "ConnectionPool::NoteFinishedTransaction",
12877                  js::ProfileEntry::Category::STORAGE);
12878 
12879   TransactionInfo* transactionInfo = mTransactions.Get(aTransactionId);
12880   MOZ_ASSERT(transactionInfo);
12881   MOZ_ASSERT(transactionInfo->mRunning);
12882   MOZ_ASSERT(transactionInfo->mFinished);
12883 
12884   transactionInfo->mRunning = false;
12885 
12886   DatabaseInfo* dbInfo = transactionInfo->mDatabaseInfo;
12887   MOZ_ASSERT(dbInfo);
12888   MOZ_ASSERT(mDatabases.Get(transactionInfo->mDatabaseId) == dbInfo);
12889   MOZ_ASSERT(dbInfo->mThreadInfo.mThread);
12890   MOZ_ASSERT(dbInfo->mThreadInfo.mRunnable);
12891 
12892   // Schedule the next write transaction if there are any queued.
12893   if (dbInfo->mRunningWriteTransaction == transactionInfo) {
12894     MOZ_ASSERT(transactionInfo->mIsWriteTransaction);
12895     MOZ_ASSERT(dbInfo->mNeedsCheckpoint);
12896 
12897     dbInfo->mRunningWriteTransaction = nullptr;
12898 
12899     if (!dbInfo->mScheduledWriteTransactions.IsEmpty()) {
12900       TransactionInfo* nextWriteTransaction =
12901         dbInfo->mScheduledWriteTransactions[0];
12902       MOZ_ASSERT(nextWriteTransaction);
12903 
12904       dbInfo->mScheduledWriteTransactions.RemoveElementAt(0);
12905 
12906       MOZ_ALWAYS_TRUE(ScheduleTransaction(nextWriteTransaction,
12907                                           /* aFromQueuedTransactions */ false));
12908     }
12909   }
12910 
12911   const nsTArray<nsString>& objectStoreNames =
12912     transactionInfo->mObjectStoreNames;
12913 
12914   for (uint32_t index = 0, count = objectStoreNames.Length();
12915        index < count;
12916        index++) {
12917     TransactionInfoPair* blockInfo =
12918       dbInfo->mBlockingTransactions.Get(objectStoreNames[index]);
12919     MOZ_ASSERT(blockInfo);
12920 
12921     if (transactionInfo->mIsWriteTransaction &&
12922         blockInfo->mLastBlockingReads == transactionInfo) {
12923       blockInfo->mLastBlockingReads = nullptr;
12924     }
12925 
12926     blockInfo->mLastBlockingWrites.RemoveElement(transactionInfo);
12927   }
12928 
12929   transactionInfo->RemoveBlockingTransactions();
12930 
12931   if (transactionInfo->mIsWriteTransaction) {
12932     MOZ_ASSERT(dbInfo->mWriteTransactionCount);
12933     dbInfo->mWriteTransactionCount--;
12934   } else {
12935     MOZ_ASSERT(dbInfo->mReadTransactionCount);
12936     dbInfo->mReadTransactionCount--;
12937   }
12938 
12939   mTransactions.Remove(aTransactionId);
12940 
12941 #ifdef DEBUG
12942   // That just deleted |transactionInfo|.
12943   transactionInfo = nullptr;
12944 #endif
12945 
12946   if (!dbInfo->TotalTransactionCount()) {
12947     MOZ_ASSERT(!dbInfo->mIdle);
12948     dbInfo->mIdle = true;
12949 
12950     NoteIdleDatabase(dbInfo);
12951   }
12952 }
12953 
12954 void
ScheduleQueuedTransactions(ThreadInfo & aThreadInfo)12955 ConnectionPool::ScheduleQueuedTransactions(ThreadInfo& aThreadInfo)
12956 {
12957   AssertIsOnOwningThread();
12958   MOZ_ASSERT(aThreadInfo.mThread);
12959   MOZ_ASSERT(aThreadInfo.mRunnable);
12960   MOZ_ASSERT(!mQueuedTransactions.IsEmpty());
12961   MOZ_ASSERT(!mIdleThreads.Contains(aThreadInfo));
12962 
12963   PROFILER_LABEL("IndexedDB",
12964                  "ConnectionPool::ScheduleQueuedTransactions",
12965                  js::ProfileEntry::Category::STORAGE);
12966 
12967   mIdleThreads.InsertElementSorted(aThreadInfo);
12968 
12969   aThreadInfo.mRunnable = nullptr;
12970   aThreadInfo.mThread = nullptr;
12971 
12972   uint32_t index = 0;
12973   for (uint32_t count = mQueuedTransactions.Length(); index < count; index++) {
12974     if (!ScheduleTransaction(mQueuedTransactions[index],
12975                              /* aFromQueuedTransactions */ true)) {
12976       break;
12977     }
12978   }
12979 
12980   if (index) {
12981     mQueuedTransactions.RemoveElementsAt(0, index);
12982   }
12983 
12984   AdjustIdleTimer();
12985 }
12986 
12987 void
NoteIdleDatabase(DatabaseInfo * aDatabaseInfo)12988 ConnectionPool::NoteIdleDatabase(DatabaseInfo* aDatabaseInfo)
12989 {
12990   AssertIsOnOwningThread();
12991   MOZ_ASSERT(aDatabaseInfo);
12992   MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount());
12993   MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread);
12994   MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
12995   MOZ_ASSERT(!mIdleDatabases.Contains(aDatabaseInfo));
12996 
12997   PROFILER_LABEL("IndexedDB",
12998                  "ConnectionPool::NoteIdleDatabase",
12999                  js::ProfileEntry::Category::STORAGE);
13000 
13001   const bool otherDatabasesWaiting = !mQueuedTransactions.IsEmpty();
13002 
13003   if (mShutdownRequested ||
13004       otherDatabasesWaiting ||
13005       aDatabaseInfo->mCloseOnIdle) {
13006     // Make sure we close the connection if we're shutting down or giving the
13007     // thread to another database.
13008     CloseDatabase(aDatabaseInfo);
13009 
13010     if (otherDatabasesWaiting) {
13011       // Let another database use this thread.
13012       ScheduleQueuedTransactions(aDatabaseInfo->mThreadInfo);
13013     } else if (mShutdownRequested) {
13014       // If there are no other databases that need to run then we can shut this
13015       // thread down immediately instead of going through the idle thread
13016       // mechanism.
13017       ShutdownThread(aDatabaseInfo->mThreadInfo);
13018     }
13019 
13020     return;
13021   }
13022 
13023   mIdleDatabases.InsertElementSorted(aDatabaseInfo);
13024 
13025   AdjustIdleTimer();
13026 }
13027 
13028 void
NoteClosedDatabase(DatabaseInfo * aDatabaseInfo)13029 ConnectionPool::NoteClosedDatabase(DatabaseInfo* aDatabaseInfo)
13030 {
13031   AssertIsOnOwningThread();
13032   MOZ_ASSERT(aDatabaseInfo);
13033   MOZ_ASSERT(aDatabaseInfo->mClosing);
13034   MOZ_ASSERT(!mIdleDatabases.Contains(aDatabaseInfo));
13035 
13036   PROFILER_LABEL("IndexedDB",
13037                  "ConnectionPool::NoteClosedDatabase",
13038                  js::ProfileEntry::Category::STORAGE);
13039 
13040   aDatabaseInfo->mClosing = false;
13041 
13042   // Figure out what to do with this database's thread. It may have already been
13043   // given to another database, in which case there's nothing to do here.
13044   // Otherwise we prioritize the thread as follows:
13045   //   1. Databases that haven't had an opportunity to run at all are highest
13046   //      priority. Those live in the |mQueuedTransactions| list.
13047   //   2. If this database has additional transactions that were started after
13048   //      we began closing the connection then the thread can be reused for
13049   //      those transactions.
13050   //   3. If we're shutting down then we can get rid of the thread.
13051   //   4. Finally, if nothing above took the thread then we can add it to our
13052   //      list of idle threads. It may be reused or it may time out. If we have
13053   //      too many idle threads then we will shut down the oldest.
13054   if (aDatabaseInfo->mThreadInfo.mThread) {
13055     MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
13056 
13057     if (!mQueuedTransactions.IsEmpty()) {
13058       // Give the thread to another database.
13059       ScheduleQueuedTransactions(aDatabaseInfo->mThreadInfo);
13060     } else if (!aDatabaseInfo->TotalTransactionCount()) {
13061       if (mShutdownRequested) {
13062         ShutdownThread(aDatabaseInfo->mThreadInfo);
13063       } else {
13064         MOZ_ASSERT(!mIdleThreads.Contains(aDatabaseInfo->mThreadInfo));
13065 
13066         mIdleThreads.InsertElementSorted(aDatabaseInfo->mThreadInfo);
13067 
13068         aDatabaseInfo->mThreadInfo.mRunnable = nullptr;
13069         aDatabaseInfo->mThreadInfo.mThread = nullptr;
13070 
13071         if (mIdleThreads.Length() > kMaxIdleConnectionThreadCount) {
13072           ShutdownThread(mIdleThreads[0].mThreadInfo);
13073           mIdleThreads.RemoveElementAt(0);
13074         }
13075 
13076         AdjustIdleTimer();
13077       }
13078     }
13079   }
13080 
13081   // Schedule any transactions that were started while we were closing the
13082   // connection.
13083   if (aDatabaseInfo->TotalTransactionCount()) {
13084     nsTArray<TransactionInfo*>& scheduledTransactions =
13085       aDatabaseInfo->mTransactionsScheduledDuringClose;
13086 
13087     MOZ_ASSERT(!scheduledTransactions.IsEmpty());
13088 
13089     for (uint32_t index = 0, count = scheduledTransactions.Length();
13090          index < count;
13091          index++) {
13092       Unused << ScheduleTransaction(scheduledTransactions[index],
13093                                     /* aFromQueuedTransactions */ false);
13094     }
13095 
13096     scheduledTransactions.Clear();
13097 
13098     return;
13099   }
13100 
13101   // There are no more transactions and the connection has been closed. We're
13102   // done with this database.
13103   {
13104     MutexAutoLock lock(mDatabasesMutex);
13105 
13106     mDatabases.Remove(aDatabaseInfo->mDatabaseId);
13107   }
13108 
13109 #ifdef DEBUG
13110   // That just deleted |aDatabaseInfo|.
13111   aDatabaseInfo = nullptr;
13112 #endif
13113 
13114   // See if we need to fire any complete callbacks now that the database is
13115   // finished.
13116   for (uint32_t index = 0;
13117        index < mCompleteCallbacks.Length();
13118        /* conditionally incremented */) {
13119     if (MaybeFireCallback(mCompleteCallbacks[index])) {
13120       mCompleteCallbacks.RemoveElementAt(index);
13121     } else {
13122       index++;
13123     }
13124   }
13125 
13126   // If that was the last database and we're supposed to be shutting down then
13127   // we are finished.
13128   if (mShutdownRequested && !mDatabases.Count()) {
13129     MOZ_ASSERT(!mTransactions.Count());
13130     Cleanup();
13131   }
13132 }
13133 
13134 bool
MaybeFireCallback(DatabasesCompleteCallback * aCallback)13135 ConnectionPool::MaybeFireCallback(DatabasesCompleteCallback* aCallback)
13136 {
13137   AssertIsOnOwningThread();
13138   MOZ_ASSERT(aCallback);
13139   MOZ_ASSERT(!aCallback->mDatabaseIds.IsEmpty());
13140   MOZ_ASSERT(aCallback->mCallback);
13141 
13142   PROFILER_LABEL("IndexedDB",
13143                  "ConnectionPool::MaybeFireCallback",
13144                  js::ProfileEntry::Category::STORAGE);
13145 
13146   for (uint32_t count = aCallback->mDatabaseIds.Length(), index = 0;
13147        index < count;
13148        index++) {
13149     const nsCString& databaseId = aCallback->mDatabaseIds[index];
13150     MOZ_ASSERT(!databaseId.IsEmpty());
13151 
13152     if (mDatabases.Get(databaseId)) {
13153       return false;
13154     }
13155   }
13156 
13157   Unused << aCallback->mCallback->Run();
13158   return true;
13159 }
13160 
13161 void
PerformIdleDatabaseMaintenance(DatabaseInfo * aDatabaseInfo)13162 ConnectionPool::PerformIdleDatabaseMaintenance(DatabaseInfo* aDatabaseInfo)
13163 {
13164   AssertIsOnOwningThread();
13165   MOZ_ASSERT(aDatabaseInfo);
13166   MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount());
13167   MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread);
13168   MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
13169   MOZ_ASSERT(aDatabaseInfo->mIdle);
13170   MOZ_ASSERT(!aDatabaseInfo->mCloseOnIdle);
13171   MOZ_ASSERT(!aDatabaseInfo->mClosing);
13172   MOZ_ASSERT(mIdleDatabases.Contains(aDatabaseInfo));
13173   MOZ_ASSERT(!mDatabasesPerformingIdleMaintenance.Contains(aDatabaseInfo));
13174 
13175   nsCOMPtr<nsIRunnable> runnable =
13176     new IdleConnectionRunnable(aDatabaseInfo, aDatabaseInfo->mNeedsCheckpoint);
13177 
13178   aDatabaseInfo->mNeedsCheckpoint = false;
13179   aDatabaseInfo->mIdle = false;
13180 
13181   mDatabasesPerformingIdleMaintenance.AppendElement(aDatabaseInfo);
13182 
13183   MOZ_ALWAYS_SUCCEEDS(
13184     aDatabaseInfo->mThreadInfo.mThread->Dispatch(runnable.forget(),
13185                                                  NS_DISPATCH_NORMAL));
13186 }
13187 
13188 void
CloseDatabase(DatabaseInfo * aDatabaseInfo)13189 ConnectionPool::CloseDatabase(DatabaseInfo* aDatabaseInfo)
13190 {
13191   AssertIsOnOwningThread();
13192   MOZ_ASSERT(aDatabaseInfo);
13193   MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount());
13194   MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread);
13195   MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
13196   MOZ_ASSERT(!aDatabaseInfo->mClosing);
13197 
13198   aDatabaseInfo->mIdle = false;
13199   aDatabaseInfo->mNeedsCheckpoint = false;
13200   aDatabaseInfo->mClosing = true;
13201 
13202   nsCOMPtr<nsIRunnable> runnable = new CloseConnectionRunnable(aDatabaseInfo);
13203 
13204   MOZ_ALWAYS_SUCCEEDS(
13205     aDatabaseInfo->mThreadInfo.mThread->Dispatch(runnable.forget(),
13206                                                  NS_DISPATCH_NORMAL));
13207 }
13208 
13209 bool
CloseDatabaseWhenIdleInternal(const nsACString & aDatabaseId)13210 ConnectionPool::CloseDatabaseWhenIdleInternal(const nsACString& aDatabaseId)
13211 {
13212   AssertIsOnOwningThread();
13213   MOZ_ASSERT(!aDatabaseId.IsEmpty());
13214 
13215   PROFILER_LABEL("IndexedDB",
13216                  "ConnectionPool::CloseDatabaseWhenIdleInternal",
13217                  js::ProfileEntry::Category::STORAGE);
13218 
13219   if (DatabaseInfo* dbInfo = mDatabases.Get(aDatabaseId)) {
13220     if (mIdleDatabases.RemoveElement(dbInfo) ||
13221         mDatabasesPerformingIdleMaintenance.RemoveElement(dbInfo)) {
13222       CloseDatabase(dbInfo);
13223       AdjustIdleTimer();
13224     } else {
13225       dbInfo->mCloseOnIdle = true;
13226     }
13227 
13228     return true;
13229   }
13230 
13231   return false;
13232 }
13233 
13234 ConnectionPool::
ConnectionRunnable(DatabaseInfo * aDatabaseInfo)13235 ConnectionRunnable::ConnectionRunnable(DatabaseInfo* aDatabaseInfo)
13236   : mDatabaseInfo(aDatabaseInfo)
13237   , mOwningThread(do_GetCurrentThread())
13238 {
13239   AssertIsOnBackgroundThread();
13240   MOZ_ASSERT(aDatabaseInfo);
13241   MOZ_ASSERT(aDatabaseInfo->mConnectionPool);
13242   aDatabaseInfo->mConnectionPool->AssertIsOnOwningThread();
13243   MOZ_ASSERT(mOwningThread);
13244 }
13245 
NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::IdleConnectionRunnable,ConnectionPool::ConnectionRunnable)13246 NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::IdleConnectionRunnable,
13247                              ConnectionPool::ConnectionRunnable)
13248 
13249 NS_IMETHODIMP
13250 ConnectionPool::
13251 IdleConnectionRunnable::Run()
13252 {
13253   MOZ_ASSERT(mDatabaseInfo);
13254   MOZ_ASSERT(!mDatabaseInfo->mIdle);
13255 
13256   nsCOMPtr<nsIEventTarget> owningThread;
13257   mOwningThread.swap(owningThread);
13258 
13259   if (owningThread) {
13260     mDatabaseInfo->AssertIsOnConnectionThread();
13261 
13262     // The connection could be null if EnsureConnection() didn't run or was not
13263     // successful in TransactionDatabaseOperationBase::RunOnConnectionThread().
13264     if (mDatabaseInfo->mConnection) {
13265       mDatabaseInfo->mConnection->DoIdleProcessing(mNeedsCheckpoint);
13266 
13267       MOZ_ALWAYS_SUCCEEDS(
13268         owningThread->Dispatch(this, NS_DISPATCH_NORMAL));
13269       return NS_OK;
13270     }
13271   }
13272 
13273   RefPtr<ConnectionPool> connectionPool = mDatabaseInfo->mConnectionPool;
13274   MOZ_ASSERT(connectionPool);
13275 
13276   if (mDatabaseInfo->mClosing || mDatabaseInfo->TotalTransactionCount()) {
13277     MOZ_ASSERT(!connectionPool->
13278                  mDatabasesPerformingIdleMaintenance.Contains(mDatabaseInfo));
13279   } else {
13280     MOZ_ALWAYS_TRUE(
13281       connectionPool->
13282         mDatabasesPerformingIdleMaintenance.RemoveElement(mDatabaseInfo));
13283 
13284     connectionPool->NoteIdleDatabase(mDatabaseInfo);
13285   }
13286 
13287   return NS_OK;
13288 }
13289 
NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::CloseConnectionRunnable,ConnectionPool::ConnectionRunnable)13290 NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::CloseConnectionRunnable,
13291                              ConnectionPool::ConnectionRunnable)
13292 
13293 NS_IMETHODIMP
13294 ConnectionPool::
13295 CloseConnectionRunnable::Run()
13296 {
13297   MOZ_ASSERT(mDatabaseInfo);
13298 
13299   PROFILER_LABEL("IndexedDB",
13300                  "ConnectionPool::CloseConnectionRunnable::Run",
13301                  js::ProfileEntry::Category::STORAGE);
13302 
13303   if (mOwningThread) {
13304     MOZ_ASSERT(mDatabaseInfo->mClosing);
13305 
13306     nsCOMPtr<nsIEventTarget> owningThread;
13307     mOwningThread.swap(owningThread);
13308 
13309     // The connection could be null if EnsureConnection() didn't run or was not
13310     // successful in TransactionDatabaseOperationBase::RunOnConnectionThread().
13311     if (mDatabaseInfo->mConnection) {
13312       mDatabaseInfo->AssertIsOnConnectionThread();
13313 
13314       mDatabaseInfo->mConnection->Close();
13315 
13316       IDB_DEBUG_LOG(("ConnectionPool closed connection 0x%p",
13317                      mDatabaseInfo->mConnection.get()));
13318 
13319       mDatabaseInfo->mConnection = nullptr;
13320 
13321 #ifdef DEBUG
13322       mDatabaseInfo->mDEBUGConnectionThread = nullptr;
13323 #endif
13324     }
13325 
13326     MOZ_ALWAYS_SUCCEEDS(
13327       owningThread->Dispatch(this, NS_DISPATCH_NORMAL));
13328     return NS_OK;
13329   }
13330 
13331   RefPtr<ConnectionPool> connectionPool = mDatabaseInfo->mConnectionPool;
13332   MOZ_ASSERT(connectionPool);
13333 
13334   connectionPool->NoteClosedDatabase(mDatabaseInfo);
13335   return NS_OK;
13336 }
13337 
13338 ConnectionPool::
DatabaseInfo(ConnectionPool * aConnectionPool,const nsACString & aDatabaseId)13339 DatabaseInfo::DatabaseInfo(ConnectionPool* aConnectionPool,
13340                            const nsACString& aDatabaseId)
13341   : mConnectionPool(aConnectionPool)
13342   , mDatabaseId(aDatabaseId)
13343   , mRunningWriteTransaction(nullptr)
13344   , mReadTransactionCount(0)
13345   , mWriteTransactionCount(0)
13346   , mNeedsCheckpoint(false)
13347   , mIdle(false)
13348   , mCloseOnIdle(false)
13349   , mClosing(false)
13350 #ifdef DEBUG
13351   , mDEBUGConnectionThread(nullptr)
13352 #endif
13353 {
13354   AssertIsOnBackgroundThread();
13355   MOZ_ASSERT(aConnectionPool);
13356   aConnectionPool->AssertIsOnOwningThread();
13357   MOZ_ASSERT(!aDatabaseId.IsEmpty());
13358 
13359   MOZ_COUNT_CTOR(ConnectionPool::DatabaseInfo);
13360 }
13361 
13362 ConnectionPool::
~DatabaseInfo()13363 DatabaseInfo::~DatabaseInfo()
13364 {
13365   AssertIsOnBackgroundThread();
13366   MOZ_ASSERT(!mConnection);
13367   MOZ_ASSERT(mScheduledWriteTransactions.IsEmpty());
13368   MOZ_ASSERT(!mRunningWriteTransaction);
13369   MOZ_ASSERT(!mThreadInfo.mThread);
13370   MOZ_ASSERT(!mThreadInfo.mRunnable);
13371   MOZ_ASSERT(!TotalTransactionCount());
13372 
13373   MOZ_COUNT_DTOR(ConnectionPool::DatabaseInfo);
13374 }
13375 
13376 ConnectionPool::
DatabasesCompleteCallback(nsTArray<nsCString> && aDatabaseIds,nsIRunnable * aCallback)13377 DatabasesCompleteCallback::DatabasesCompleteCallback(
13378                                              nsTArray<nsCString>&& aDatabaseIds,
13379                                              nsIRunnable* aCallback)
13380   : mDatabaseIds(Move(aDatabaseIds))
13381   , mCallback(aCallback)
13382 {
13383   AssertIsOnBackgroundThread();
13384   MOZ_ASSERT(!mDatabaseIds.IsEmpty());
13385   MOZ_ASSERT(aCallback);
13386 
13387   MOZ_COUNT_CTOR(ConnectionPool::DatabasesCompleteCallback);
13388 }
13389 
13390 ConnectionPool::
~DatabasesCompleteCallback()13391 DatabasesCompleteCallback::~DatabasesCompleteCallback()
13392 {
13393   AssertIsOnBackgroundThread();
13394 
13395   MOZ_COUNT_DTOR(ConnectionPool::DatabasesCompleteCallback);
13396 }
13397 
13398 ConnectionPool::
FinishCallbackWrapper(ConnectionPool * aConnectionPool,uint64_t aTransactionId,FinishCallback * aCallback)13399 FinishCallbackWrapper::FinishCallbackWrapper(ConnectionPool* aConnectionPool,
13400                                              uint64_t aTransactionId,
13401                                              FinishCallback* aCallback)
13402   : mConnectionPool(aConnectionPool)
13403   , mCallback(aCallback)
13404   , mOwningThread(do_GetCurrentThread())
13405   , mTransactionId(aTransactionId)
13406   , mHasRunOnce(false)
13407 {
13408   AssertIsOnBackgroundThread();
13409   MOZ_ASSERT(aConnectionPool);
13410   MOZ_ASSERT(aCallback);
13411   MOZ_ASSERT(mOwningThread);
13412 }
13413 
13414 ConnectionPool::
~FinishCallbackWrapper()13415 FinishCallbackWrapper::~FinishCallbackWrapper()
13416 {
13417   MOZ_ASSERT(!mConnectionPool);
13418   MOZ_ASSERT(!mCallback);
13419 }
13420 
NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::FinishCallbackWrapper,Runnable)13421 NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::FinishCallbackWrapper, Runnable)
13422 
13423 nsresult
13424 ConnectionPool::
13425 FinishCallbackWrapper::Run()
13426 {
13427   MOZ_ASSERT(mConnectionPool);
13428   MOZ_ASSERT(mCallback);
13429   MOZ_ASSERT(mOwningThread);
13430 
13431   PROFILER_LABEL("IndexedDB",
13432                  "ConnectionPool::FinishCallbackWrapper::Run",
13433                  js::ProfileEntry::Category::STORAGE);
13434 
13435   if (!mHasRunOnce) {
13436     MOZ_ASSERT(!IsOnBackgroundThread());
13437 
13438     mHasRunOnce = true;
13439 
13440     Unused << mCallback->Run();
13441 
13442     MOZ_ALWAYS_SUCCEEDS(
13443       mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
13444 
13445     return NS_OK;
13446   }
13447 
13448   mConnectionPool->AssertIsOnOwningThread();
13449   MOZ_ASSERT(mHasRunOnce);
13450 
13451   RefPtr<ConnectionPool> connectionPool = Move(mConnectionPool);
13452   RefPtr<FinishCallback> callback = Move(mCallback);
13453 
13454   callback->TransactionFinishedBeforeUnblock();
13455 
13456   connectionPool->NoteFinishedTransaction(mTransactionId);
13457 
13458   callback->TransactionFinishedAfterUnblock();
13459 
13460   return NS_OK;
13461 }
13462 
13463 uint32_t ConnectionPool::ThreadRunnable::sNextSerialNumber = 0;
13464 
13465 ConnectionPool::
ThreadRunnable()13466 ThreadRunnable::ThreadRunnable()
13467   : mSerialNumber(++sNextSerialNumber)
13468   , mFirstRun(true)
13469   , mContinueRunning(true)
13470 {
13471   AssertIsOnBackgroundThread();
13472 }
13473 
13474 ConnectionPool::
~ThreadRunnable()13475 ThreadRunnable::~ThreadRunnable()
13476 {
13477   MOZ_ASSERT(!mFirstRun);
13478   MOZ_ASSERT(!mContinueRunning);
13479 }
13480 
NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::ThreadRunnable,Runnable)13481 NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::ThreadRunnable, Runnable)
13482 
13483 nsresult
13484 ConnectionPool::
13485 ThreadRunnable::Run()
13486 {
13487 #ifdef MOZ_ENABLE_PROFILER_SPS
13488   char stackTopGuess;
13489 #endif // MOZ_ENABLE_PROFILER_SPS
13490 
13491   MOZ_ASSERT(!IsOnBackgroundThread());
13492   MOZ_ASSERT(mContinueRunning);
13493 
13494   if (!mFirstRun) {
13495     mContinueRunning = false;
13496     return NS_OK;
13497   }
13498 
13499   mFirstRun = false;
13500 
13501   {
13502     // Scope for the thread name. Both PR_SetCurrentThreadName() and
13503     // profiler_register_thread() copy the string so we don't need to keep it.
13504     const nsPrintfCString threadName("IndexedDB #%lu", mSerialNumber);
13505 
13506     PR_SetCurrentThreadName(threadName.get());
13507 
13508 #ifdef MOZ_ENABLE_PROFILER_SPS
13509     profiler_register_thread(threadName.get(), &stackTopGuess);
13510 #endif // MOZ_ENABLE_PROFILER_SPS
13511   }
13512 
13513   {
13514     // Scope for the profiler label.
13515     PROFILER_LABEL("IndexedDB",
13516                    "ConnectionPool::ThreadRunnable::Run",
13517                    js::ProfileEntry::Category::STORAGE);
13518 
13519     nsIThread* currentThread = NS_GetCurrentThread();
13520     MOZ_ASSERT(currentThread);
13521 
13522 #ifdef DEBUG
13523     if (kDEBUGTransactionThreadPriority !=
13524           nsISupportsPriority::PRIORITY_NORMAL) {
13525       NS_WARNING("ConnectionPool thread debugging enabled, priority has been "
13526                  "modified!");
13527 
13528       nsCOMPtr<nsISupportsPriority> thread = do_QueryInterface(currentThread);
13529       MOZ_ASSERT(thread);
13530 
13531       MOZ_ALWAYS_SUCCEEDS(
13532         thread->SetPriority(kDEBUGTransactionThreadPriority));
13533     }
13534 
13535     if (kDEBUGTransactionThreadSleepMS) {
13536       NS_WARNING("TransactionThreadPool thread debugging enabled, sleeping "
13537                  "after every event!");
13538     }
13539 #endif // DEBUG
13540 
13541     while (mContinueRunning) {
13542       MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread));
13543 
13544 #ifdef DEBUG
13545       if (kDEBUGTransactionThreadSleepMS) {
13546         MOZ_ALWAYS_TRUE(
13547           PR_Sleep(PR_MillisecondsToInterval(kDEBUGTransactionThreadSleepMS)) ==
13548             PR_SUCCESS);
13549       }
13550 #endif // DEBUG
13551     }
13552   }
13553 
13554 #ifdef MOZ_ENABLE_PROFILER_SPS
13555   profiler_unregister_thread();
13556 #endif // MOZ_ENABLE_PROFILER_SPS
13557 
13558   return NS_OK;
13559 }
13560 
13561 ConnectionPool::
ThreadInfo()13562 ThreadInfo::ThreadInfo()
13563 {
13564   AssertIsOnBackgroundThread();
13565 
13566   MOZ_COUNT_CTOR(ConnectionPool::ThreadInfo);
13567 }
13568 
13569 ConnectionPool::
ThreadInfo(const ThreadInfo & aOther)13570 ThreadInfo::ThreadInfo(const ThreadInfo& aOther)
13571   : mThread(aOther.mThread)
13572   , mRunnable(aOther.mRunnable)
13573 {
13574   AssertIsOnBackgroundThread();
13575   MOZ_ASSERT(aOther.mThread);
13576   MOZ_ASSERT(aOther.mRunnable);
13577 
13578   MOZ_COUNT_CTOR(ConnectionPool::ThreadInfo);
13579 }
13580 
13581 ConnectionPool::
~ThreadInfo()13582 ThreadInfo::~ThreadInfo()
13583 {
13584   AssertIsOnBackgroundThread();
13585 
13586   MOZ_COUNT_DTOR(ConnectionPool::ThreadInfo);
13587 }
13588 
13589 ConnectionPool::
IdleResource(const TimeStamp & aIdleTime)13590 IdleResource::IdleResource(const TimeStamp& aIdleTime)
13591   : mIdleTime(aIdleTime)
13592 {
13593   AssertIsOnBackgroundThread();
13594   MOZ_ASSERT(!aIdleTime.IsNull());
13595 
13596   MOZ_COUNT_CTOR(ConnectionPool::IdleResource);
13597 }
13598 
13599 ConnectionPool::
~IdleResource()13600 IdleResource::~IdleResource()
13601 {
13602   AssertIsOnBackgroundThread();
13603 
13604   MOZ_COUNT_DTOR(ConnectionPool::IdleResource);
13605 }
13606 
13607 ConnectionPool::
IdleDatabaseInfo(DatabaseInfo * aDatabaseInfo)13608 IdleDatabaseInfo::IdleDatabaseInfo(DatabaseInfo* aDatabaseInfo)
13609   : IdleResource(TimeStamp::NowLoRes() +
13610                  (aDatabaseInfo->mIdle ?
13611                   TimeDuration::FromMilliseconds(kConnectionIdleMaintenanceMS) :
13612                   TimeDuration::FromMilliseconds(kConnectionIdleCloseMS)))
13613   , mDatabaseInfo(aDatabaseInfo)
13614 {
13615   AssertIsOnBackgroundThread();
13616   MOZ_ASSERT(aDatabaseInfo);
13617 
13618   MOZ_COUNT_CTOR(ConnectionPool::IdleDatabaseInfo);
13619 }
13620 
13621 ConnectionPool::
~IdleDatabaseInfo()13622 IdleDatabaseInfo::~IdleDatabaseInfo()
13623 {
13624   AssertIsOnBackgroundThread();
13625   MOZ_ASSERT(mDatabaseInfo);
13626 
13627   MOZ_COUNT_DTOR(ConnectionPool::IdleDatabaseInfo);
13628 }
13629 
13630 ConnectionPool::
IdleThreadInfo(const ThreadInfo & aThreadInfo)13631 IdleThreadInfo::IdleThreadInfo(const ThreadInfo& aThreadInfo)
13632   : IdleResource(TimeStamp::NowLoRes() +
13633                  TimeDuration::FromMilliseconds(kConnectionThreadIdleMS))
13634   , mThreadInfo(aThreadInfo)
13635 {
13636   AssertIsOnBackgroundThread();
13637   MOZ_ASSERT(aThreadInfo.mRunnable);
13638   MOZ_ASSERT(aThreadInfo.mThread);
13639 
13640   MOZ_COUNT_CTOR(ConnectionPool::IdleThreadInfo);
13641 }
13642 
13643 ConnectionPool::
~IdleThreadInfo()13644 IdleThreadInfo::~IdleThreadInfo()
13645 {
13646   AssertIsOnBackgroundThread();
13647 
13648   MOZ_COUNT_DTOR(ConnectionPool::IdleThreadInfo);
13649 }
13650 
13651 ConnectionPool::
TransactionInfo(DatabaseInfo * aDatabaseInfo,const nsID & aBackgroundChildLoggingId,const nsACString & aDatabaseId,uint64_t aTransactionId,int64_t aLoggingSerialNumber,const nsTArray<nsString> & aObjectStoreNames,bool aIsWriteTransaction,TransactionDatabaseOperationBase * aTransactionOp)13652 TransactionInfo::TransactionInfo(
13653                                DatabaseInfo* aDatabaseInfo,
13654                                const nsID& aBackgroundChildLoggingId,
13655                                const nsACString& aDatabaseId,
13656                                uint64_t aTransactionId,
13657                                int64_t aLoggingSerialNumber,
13658                                const nsTArray<nsString>& aObjectStoreNames,
13659                                bool aIsWriteTransaction,
13660                                TransactionDatabaseOperationBase* aTransactionOp)
13661   : mDatabaseInfo(aDatabaseInfo)
13662   , mBackgroundChildLoggingId(aBackgroundChildLoggingId)
13663   , mDatabaseId(aDatabaseId)
13664   , mTransactionId(aTransactionId)
13665   , mLoggingSerialNumber(aLoggingSerialNumber)
13666   , mObjectStoreNames(aObjectStoreNames)
13667   , mIsWriteTransaction(aIsWriteTransaction)
13668   , mRunning(false)
13669 #ifdef DEBUG
13670   , mFinished(false)
13671 #endif
13672 {
13673   AssertIsOnBackgroundThread();
13674   MOZ_ASSERT(aDatabaseInfo);
13675   aDatabaseInfo->mConnectionPool->AssertIsOnOwningThread();
13676 
13677   MOZ_COUNT_CTOR(ConnectionPool::TransactionInfo);
13678 
13679   if (aTransactionOp) {
13680     mQueuedRunnables.AppendElement(aTransactionOp);
13681   }
13682 }
13683 
13684 ConnectionPool::
~TransactionInfo()13685 TransactionInfo::~TransactionInfo()
13686 {
13687   AssertIsOnBackgroundThread();
13688   MOZ_ASSERT(!mBlockedOn.Count());
13689   MOZ_ASSERT(mQueuedRunnables.IsEmpty());
13690   MOZ_ASSERT(!mRunning);
13691   MOZ_ASSERT(mFinished);
13692 
13693   MOZ_COUNT_DTOR(ConnectionPool::TransactionInfo);
13694 }
13695 
13696 void
13697 ConnectionPool::
AddBlockingTransaction(TransactionInfo * aTransactionInfo)13698 TransactionInfo::AddBlockingTransaction(TransactionInfo* aTransactionInfo)
13699 {
13700   AssertIsOnBackgroundThread();
13701   MOZ_ASSERT(aTransactionInfo);
13702 
13703   if (!mBlocking.Contains(aTransactionInfo)) {
13704     mBlocking.PutEntry(aTransactionInfo);
13705     mBlockingOrdered.AppendElement(aTransactionInfo);
13706   }
13707 }
13708 
13709 void
13710 ConnectionPool::
RemoveBlockingTransactions()13711 TransactionInfo::RemoveBlockingTransactions()
13712 {
13713   AssertIsOnBackgroundThread();
13714 
13715   for (uint32_t index = 0, count = mBlockingOrdered.Length();
13716        index < count;
13717        index++) {
13718     TransactionInfo* blockedInfo = mBlockingOrdered[index];
13719     MOZ_ASSERT(blockedInfo);
13720 
13721     blockedInfo->MaybeUnblock(this);
13722   }
13723 
13724   mBlocking.Clear();
13725   mBlockingOrdered.Clear();
13726 }
13727 
13728 void
13729 ConnectionPool::
MaybeUnblock(TransactionInfo * aTransactionInfo)13730 TransactionInfo::MaybeUnblock(TransactionInfo* aTransactionInfo)
13731 {
13732   AssertIsOnBackgroundThread();
13733   MOZ_ASSERT(mBlockedOn.Contains(aTransactionInfo));
13734 
13735   mBlockedOn.RemoveEntry(aTransactionInfo);
13736   if (!mBlockedOn.Count()) {
13737     MOZ_ASSERT(mDatabaseInfo);
13738 
13739     ConnectionPool* connectionPool = mDatabaseInfo->mConnectionPool;
13740     MOZ_ASSERT(connectionPool);
13741     connectionPool->AssertIsOnOwningThread();
13742 
13743     Unused <<
13744       connectionPool->ScheduleTransaction(this,
13745                                           /* aFromQueuedTransactions */ false);
13746   }
13747 }
13748 
13749 ConnectionPool::
TransactionInfoPair()13750 TransactionInfoPair::TransactionInfoPair()
13751   : mLastBlockingReads(nullptr)
13752 {
13753   AssertIsOnBackgroundThread();
13754 
13755   MOZ_COUNT_CTOR(ConnectionPool::TransactionInfoPair);
13756 }
13757 
13758 ConnectionPool::
~TransactionInfoPair()13759 TransactionInfoPair::~TransactionInfoPair()
13760 {
13761   AssertIsOnBackgroundThread();
13762 
13763   MOZ_COUNT_DTOR(ConnectionPool::TransactionInfoPair);
13764 }
13765 
13766 /*******************************************************************************
13767  * Metadata classes
13768  ******************************************************************************/
13769 
13770 bool
HasLiveIndexes() const13771 FullObjectStoreMetadata::HasLiveIndexes() const
13772 {
13773   AssertIsOnBackgroundThread();
13774 
13775   for (auto iter = mIndexes.ConstIter(); !iter.Done(); iter.Next()) {
13776     if (!iter.Data()->mDeleted) {
13777       return true;
13778     }
13779   }
13780 
13781   return false;
13782 }
13783 
13784 already_AddRefed<FullDatabaseMetadata>
Duplicate() const13785 FullDatabaseMetadata::Duplicate() const
13786 {
13787   AssertIsOnBackgroundThread();
13788 
13789   // FullDatabaseMetadata contains two hash tables of pointers that we need to
13790   // duplicate so we can't just use the copy constructor.
13791   RefPtr<FullDatabaseMetadata> newMetadata =
13792     new FullDatabaseMetadata(mCommonMetadata);
13793 
13794   newMetadata->mDatabaseId = mDatabaseId;
13795   newMetadata->mFilePath = mFilePath;
13796   newMetadata->mNextObjectStoreId = mNextObjectStoreId;
13797   newMetadata->mNextIndexId = mNextIndexId;
13798 
13799   for (auto iter = mObjectStores.ConstIter(); !iter.Done(); iter.Next()) {
13800     auto key = iter.Key();
13801     auto value = iter.Data();
13802 
13803     RefPtr<FullObjectStoreMetadata> newOSMetadata =
13804       new FullObjectStoreMetadata();
13805 
13806     newOSMetadata->mCommonMetadata = value->mCommonMetadata;
13807     newOSMetadata->mNextAutoIncrementId = value->mNextAutoIncrementId;
13808     newOSMetadata->mCommittedAutoIncrementId = value->mCommittedAutoIncrementId;
13809 
13810     for (auto iter = value->mIndexes.ConstIter(); !iter.Done(); iter.Next()) {
13811       auto key = iter.Key();
13812       auto value = iter.Data();
13813 
13814       RefPtr<FullIndexMetadata> newIndexMetadata = new FullIndexMetadata();
13815 
13816       newIndexMetadata->mCommonMetadata = value->mCommonMetadata;
13817 
13818       if (NS_WARN_IF(!newOSMetadata->mIndexes.Put(key, newIndexMetadata,
13819                                                   fallible))) {
13820         return nullptr;
13821       }
13822     }
13823 
13824     MOZ_ASSERT(value->mIndexes.Count() == newOSMetadata->mIndexes.Count());
13825 
13826     if (NS_WARN_IF(!newMetadata->mObjectStores.Put(key, newOSMetadata,
13827                                                    fallible))) {
13828       return nullptr;
13829     }
13830   }
13831 
13832   MOZ_ASSERT(mObjectStores.Count() == newMetadata->mObjectStores.Count());
13833 
13834   return newMetadata.forget();
13835 }
13836 
~DatabaseLoggingInfo()13837 DatabaseLoggingInfo::~DatabaseLoggingInfo()
13838 {
13839   AssertIsOnBackgroundThread();
13840 
13841   if (gLoggingInfoHashtable) {
13842     const nsID& backgroundChildLoggingId =
13843       mLoggingInfo.backgroundChildLoggingId();
13844 
13845     MOZ_ASSERT(gLoggingInfoHashtable->Get(backgroundChildLoggingId) == this);
13846 
13847     gLoggingInfoHashtable->Remove(backgroundChildLoggingId);
13848   }
13849 }
13850 
13851 /*******************************************************************************
13852  * Factory
13853  ******************************************************************************/
13854 
Factory(already_AddRefed<DatabaseLoggingInfo> aLoggingInfo)13855 Factory::Factory(already_AddRefed<DatabaseLoggingInfo> aLoggingInfo)
13856   : mLoggingInfo(Move(aLoggingInfo))
13857 #ifdef DEBUG
13858   , mActorDestroyed(false)
13859 #endif
13860 {
13861   AssertIsOnBackgroundThread();
13862   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
13863 }
13864 
~Factory()13865 Factory::~Factory()
13866 {
13867   MOZ_ASSERT(mActorDestroyed);
13868 }
13869 
13870 // static
13871 already_AddRefed<Factory>
Create(const LoggingInfo & aLoggingInfo)13872 Factory::Create(const LoggingInfo& aLoggingInfo)
13873 {
13874   AssertIsOnBackgroundThread();
13875   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
13876 
13877   // Balanced in ActoryDestroy().
13878   IncreaseBusyCount();
13879 
13880   MOZ_ASSERT(gLoggingInfoHashtable);
13881   RefPtr<DatabaseLoggingInfo> loggingInfo =
13882     gLoggingInfoHashtable->Get(aLoggingInfo.backgroundChildLoggingId());
13883   if (loggingInfo) {
13884     MOZ_ASSERT(aLoggingInfo.backgroundChildLoggingId() == loggingInfo->Id());
13885 #if !DISABLE_ASSERTS_FOR_FUZZING
13886     NS_WARNING_ASSERTION(
13887       aLoggingInfo.nextTransactionSerialNumber() ==
13888         loggingInfo->mLoggingInfo.nextTransactionSerialNumber(),
13889       "NextTransactionSerialNumber doesn't match!");
13890     NS_WARNING_ASSERTION(
13891       aLoggingInfo.nextVersionChangeTransactionSerialNumber() ==
13892         loggingInfo->mLoggingInfo.
13893       nextVersionChangeTransactionSerialNumber(),
13894       "NextVersionChangeTransactionSerialNumber doesn't match!");
13895     NS_WARNING_ASSERTION(
13896       aLoggingInfo.nextRequestSerialNumber() ==
13897         loggingInfo->mLoggingInfo.nextRequestSerialNumber(),
13898       "NextRequestSerialNumber doesn't match!");
13899 #endif // !DISABLE_ASSERTS_FOR_FUZZING
13900   } else {
13901     loggingInfo = new DatabaseLoggingInfo(aLoggingInfo);
13902     gLoggingInfoHashtable->Put(aLoggingInfo.backgroundChildLoggingId(),
13903                                loggingInfo);
13904   }
13905 
13906   RefPtr<Factory> actor = new Factory(loggingInfo.forget());
13907 
13908   return actor.forget();
13909 }
13910 
13911 void
ActorDestroy(ActorDestroyReason aWhy)13912 Factory::ActorDestroy(ActorDestroyReason aWhy)
13913 {
13914   AssertIsOnBackgroundThread();
13915   MOZ_ASSERT(!mActorDestroyed);
13916 
13917 #ifdef DEBUG
13918   mActorDestroyed = true;
13919 #endif
13920 
13921   // Match the IncreaseBusyCount in Create().
13922   DecreaseBusyCount();
13923 }
13924 
13925 bool
RecvDeleteMe()13926 Factory::RecvDeleteMe()
13927 {
13928   AssertIsOnBackgroundThread();
13929   MOZ_ASSERT(!mActorDestroyed);
13930 
13931   return PBackgroundIDBFactoryParent::Send__delete__(this);
13932 }
13933 
13934 bool
RecvIncrementLoggingRequestSerialNumber()13935 Factory::RecvIncrementLoggingRequestSerialNumber()
13936 {
13937   AssertIsOnBackgroundThread();
13938   MOZ_ASSERT(mLoggingInfo);
13939 
13940   mLoggingInfo->NextRequestSN();
13941   return true;
13942 }
13943 
13944 PBackgroundIDBFactoryRequestParent*
AllocPBackgroundIDBFactoryRequestParent(const FactoryRequestParams & aParams)13945 Factory::AllocPBackgroundIDBFactoryRequestParent(
13946                                             const FactoryRequestParams& aParams)
13947 {
13948   AssertIsOnBackgroundThread();
13949   MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
13950 
13951   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
13952     return nullptr;
13953   }
13954 
13955   const CommonFactoryRequestParams* commonParams;
13956 
13957   switch (aParams.type()) {
13958     case FactoryRequestParams::TOpenDatabaseRequestParams: {
13959       const OpenDatabaseRequestParams& params =
13960          aParams.get_OpenDatabaseRequestParams();
13961       commonParams = &params.commonParams();
13962       break;
13963     }
13964 
13965     case FactoryRequestParams::TDeleteDatabaseRequestParams: {
13966       const DeleteDatabaseRequestParams& params =
13967          aParams.get_DeleteDatabaseRequestParams();
13968       commonParams = &params.commonParams();
13969       break;
13970     }
13971 
13972     default:
13973       MOZ_CRASH("Should never get here!");
13974   }
13975 
13976   MOZ_ASSERT(commonParams);
13977 
13978   const DatabaseMetadata& metadata = commonParams->metadata();
13979   if (NS_WARN_IF(metadata.persistenceType() != PERSISTENCE_TYPE_PERSISTENT &&
13980                  metadata.persistenceType() != PERSISTENCE_TYPE_TEMPORARY &&
13981                  metadata.persistenceType() != PERSISTENCE_TYPE_DEFAULT)) {
13982     ASSERT_UNLESS_FUZZING();
13983     return nullptr;
13984   }
13985 
13986   const PrincipalInfo& principalInfo = commonParams->principalInfo();
13987   if (NS_WARN_IF(principalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
13988     ASSERT_UNLESS_FUZZING();
13989     return nullptr;
13990   }
13991 
13992   if (NS_WARN_IF(principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo &&
13993                  metadata.persistenceType() != PERSISTENCE_TYPE_PERSISTENT)) {
13994     ASSERT_UNLESS_FUZZING();
13995     return nullptr;
13996   }
13997 
13998   RefPtr<ContentParent> contentParent =
13999     BackgroundParent::GetContentParent(Manager());
14000 
14001   RefPtr<FactoryOp> actor;
14002   if (aParams.type() == FactoryRequestParams::TOpenDatabaseRequestParams) {
14003     actor = new OpenDatabaseOp(this,
14004                                contentParent.forget(),
14005                                *commonParams);
14006   } else {
14007     actor = new DeleteDatabaseOp(this, contentParent.forget(), *commonParams);
14008   }
14009 
14010   // Transfer ownership to IPDL.
14011   return actor.forget().take();
14012 }
14013 
14014 bool
RecvPBackgroundIDBFactoryRequestConstructor(PBackgroundIDBFactoryRequestParent * aActor,const FactoryRequestParams & aParams)14015 Factory::RecvPBackgroundIDBFactoryRequestConstructor(
14016                                      PBackgroundIDBFactoryRequestParent* aActor,
14017                                      const FactoryRequestParams& aParams)
14018 {
14019   AssertIsOnBackgroundThread();
14020   MOZ_ASSERT(aActor);
14021   MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
14022   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
14023 
14024   auto* op = static_cast<FactoryOp*>(aActor);
14025 
14026   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(op));
14027   return true;
14028 }
14029 
14030 bool
DeallocPBackgroundIDBFactoryRequestParent(PBackgroundIDBFactoryRequestParent * aActor)14031 Factory::DeallocPBackgroundIDBFactoryRequestParent(
14032                                      PBackgroundIDBFactoryRequestParent* aActor)
14033 {
14034   AssertIsOnBackgroundThread();
14035   MOZ_ASSERT(aActor);
14036 
14037   // Transfer ownership back from IPDL.
14038   RefPtr<FactoryOp> op = dont_AddRef(static_cast<FactoryOp*>(aActor));
14039   return true;
14040 }
14041 
14042 PBackgroundIDBDatabaseParent*
AllocPBackgroundIDBDatabaseParent(const DatabaseSpec & aSpec,PBackgroundIDBFactoryRequestParent * aRequest)14043 Factory::AllocPBackgroundIDBDatabaseParent(
14044                                    const DatabaseSpec& aSpec,
14045                                    PBackgroundIDBFactoryRequestParent* aRequest)
14046 {
14047   MOZ_CRASH("PBackgroundIDBDatabaseParent actors should be constructed "
14048             "manually!");
14049 }
14050 
14051 bool
DeallocPBackgroundIDBDatabaseParent(PBackgroundIDBDatabaseParent * aActor)14052 Factory::DeallocPBackgroundIDBDatabaseParent(
14053                                            PBackgroundIDBDatabaseParent* aActor)
14054 {
14055   AssertIsOnBackgroundThread();
14056   MOZ_ASSERT(aActor);
14057 
14058   RefPtr<Database> database = dont_AddRef(static_cast<Database*>(aActor));
14059   return true;
14060 }
14061 
14062 /*******************************************************************************
14063  * WaitForTransactionsHelper
14064  ******************************************************************************/
14065 
14066 void
WaitForTransactions()14067 WaitForTransactionsHelper::WaitForTransactions()
14068 {
14069   MOZ_ASSERT(mState == State::Initial);
14070 
14071   Unused << this->Run();
14072 }
14073 
14074 void
MaybeWaitForTransactions()14075 WaitForTransactionsHelper::MaybeWaitForTransactions()
14076 {
14077   AssertIsOnBackgroundThread();
14078   MOZ_ASSERT(mState == State::Initial);
14079 
14080   RefPtr<ConnectionPool> connectionPool = gConnectionPool.get();
14081   if (connectionPool) {
14082     nsTArray<nsCString> ids(1);
14083     ids.AppendElement(mDatabaseId);
14084 
14085     mState = State::WaitingForTransactions;
14086 
14087     connectionPool->WaitForDatabasesToComplete(Move(ids), this);
14088     return;
14089   }
14090 
14091   MaybeWaitForFileHandles();
14092 }
14093 
14094 void
MaybeWaitForFileHandles()14095 WaitForTransactionsHelper::MaybeWaitForFileHandles()
14096 {
14097   AssertIsOnBackgroundThread();
14098   MOZ_ASSERT(mState == State::Initial || mState == State::WaitingForTransactions);
14099 
14100   RefPtr<FileHandleThreadPool> fileHandleThreadPool =
14101     gFileHandleThreadPool.get();
14102   if (fileHandleThreadPool) {
14103     nsTArray<nsCString> ids(1);
14104     ids.AppendElement(mDatabaseId);
14105 
14106     mState = State::WaitingForFileHandles;
14107 
14108     fileHandleThreadPool->WaitForDirectoriesToComplete(Move(ids), this);
14109     return;
14110   }
14111 
14112   CallCallback();
14113 }
14114 
14115 void
CallCallback()14116 WaitForTransactionsHelper::CallCallback()
14117 {
14118   AssertIsOnBackgroundThread();
14119   MOZ_ASSERT(mState == State::Initial ||
14120              mState == State::WaitingForTransactions ||
14121              mState == State::WaitingForFileHandles);
14122 
14123   nsCOMPtr<nsIRunnable> callback;
14124   mCallback.swap(callback);
14125 
14126   callback->Run();
14127 
14128   mState = State::Complete;
14129 }
14130 
NS_IMPL_ISUPPORTS_INHERITED0(WaitForTransactionsHelper,Runnable)14131 NS_IMPL_ISUPPORTS_INHERITED0(WaitForTransactionsHelper, Runnable)
14132 
14133 NS_IMETHODIMP
14134 WaitForTransactionsHelper::Run()
14135 {
14136   MOZ_ASSERT(mState != State::Complete);
14137   MOZ_ASSERT(mCallback);
14138 
14139   switch (mState) {
14140     case State::Initial:
14141       MaybeWaitForTransactions();
14142       break;
14143 
14144     case State::WaitingForTransactions:
14145       MaybeWaitForFileHandles();
14146       break;
14147 
14148     case State::WaitingForFileHandles:
14149       CallCallback();
14150       break;
14151 
14152     default:
14153       MOZ_CRASH("Should never get here!");
14154   }
14155 
14156   return NS_OK;
14157 }
14158 
14159 /*******************************************************************************
14160  * Database
14161  ******************************************************************************/
14162 
Database(Factory * aFactory,const PrincipalInfo & aPrincipalInfo,const Maybe<ContentParentId> & aOptionalContentParentId,const nsACString & aGroup,const nsACString & aOrigin,uint32_t aTelemetryId,FullDatabaseMetadata * aMetadata,FileManager * aFileManager,already_AddRefed<DirectoryLock> aDirectoryLock,bool aFileHandleDisabled,bool aChromeWriteAccessAllowed)14163 Database::Database(Factory* aFactory,
14164                    const PrincipalInfo& aPrincipalInfo,
14165                    const Maybe<ContentParentId>& aOptionalContentParentId,
14166                    const nsACString& aGroup,
14167                    const nsACString& aOrigin,
14168                    uint32_t aTelemetryId,
14169                    FullDatabaseMetadata* aMetadata,
14170                    FileManager* aFileManager,
14171                    already_AddRefed<DirectoryLock> aDirectoryLock,
14172                    bool aFileHandleDisabled,
14173                    bool aChromeWriteAccessAllowed)
14174   : mFactory(aFactory)
14175   , mMetadata(aMetadata)
14176   , mFileManager(aFileManager)
14177   , mDirectoryLock(Move(aDirectoryLock))
14178   , mPrincipalInfo(aPrincipalInfo)
14179   , mOptionalContentParentId(aOptionalContentParentId)
14180   , mGroup(aGroup)
14181   , mOrigin(aOrigin)
14182   , mId(aMetadata->mDatabaseId)
14183   , mFilePath(aMetadata->mFilePath)
14184   , mActiveMutableFileCount(0)
14185   , mTelemetryId(aTelemetryId)
14186   , mPersistenceType(aMetadata->mCommonMetadata.persistenceType())
14187   , mFileHandleDisabled(aFileHandleDisabled)
14188   , mChromeWriteAccessAllowed(aChromeWriteAccessAllowed)
14189   , mClosed(false)
14190   , mInvalidated(false)
14191   , mActorWasAlive(false)
14192   , mActorDestroyed(false)
14193   , mMetadataCleanedUp(false)
14194 {
14195   AssertIsOnBackgroundThread();
14196   MOZ_ASSERT(aFactory);
14197   MOZ_ASSERT(aMetadata);
14198   MOZ_ASSERT(aFileManager);
14199   MOZ_ASSERT_IF(aChromeWriteAccessAllowed,
14200                 aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo);
14201 }
14202 
14203 void
Invalidate()14204 Database::Invalidate()
14205 {
14206   AssertIsOnBackgroundThread();
14207 
14208   class MOZ_STACK_CLASS Helper final
14209   {
14210   public:
14211     static bool
14212     InvalidateTransactions(nsTHashtable<nsPtrHashKey<TransactionBase>>& aTable)
14213     {
14214       AssertIsOnBackgroundThread();
14215 
14216       const uint32_t count = aTable.Count();
14217       if (!count) {
14218         return true;
14219       }
14220 
14221       FallibleTArray<RefPtr<TransactionBase>> transactions;
14222       if (NS_WARN_IF(!transactions.SetCapacity(count, fallible))) {
14223         return false;
14224       }
14225 
14226       for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
14227         if (NS_WARN_IF(!transactions.AppendElement(iter.Get()->GetKey(),
14228                                                    fallible))) {
14229           return false;
14230         }
14231       }
14232 
14233       if (count) {
14234         IDB_REPORT_INTERNAL_ERR();
14235 
14236         for (uint32_t index = 0; index < count; index++) {
14237           RefPtr<TransactionBase> transaction = transactions[index].forget();
14238           MOZ_ASSERT(transaction);
14239 
14240           transaction->Invalidate();
14241         }
14242       }
14243 
14244       return true;
14245     }
14246 
14247     static bool
14248     InvalidateMutableFiles(nsTHashtable<nsPtrHashKey<MutableFile>>& aTable)
14249     {
14250       AssertIsOnBackgroundThread();
14251 
14252       const uint32_t count = aTable.Count();
14253       if (!count) {
14254         return true;
14255       }
14256 
14257       FallibleTArray<RefPtr<MutableFile>> mutableFiles;
14258       if (NS_WARN_IF(!mutableFiles.SetCapacity(count, fallible))) {
14259         return false;
14260       }
14261 
14262       for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
14263         if (NS_WARN_IF(!mutableFiles.AppendElement(iter.Get()->GetKey(),
14264                                                    fallible))) {
14265           return false;
14266         }
14267       }
14268 
14269       if (count) {
14270         IDB_REPORT_INTERNAL_ERR();
14271 
14272         for (uint32_t index = 0; index < count; index++) {
14273           RefPtr<MutableFile> mutableFile = mutableFiles[index].forget();
14274           MOZ_ASSERT(mutableFile);
14275 
14276           mutableFile->Invalidate();
14277         }
14278      }
14279 
14280       return true;
14281     }
14282   };
14283 
14284   if (mInvalidated) {
14285     return;
14286   }
14287 
14288   mInvalidated = true;
14289 
14290   if (mActorWasAlive && !mActorDestroyed) {
14291     Unused << SendInvalidate();
14292   }
14293 
14294   if (!Helper::InvalidateTransactions(mTransactions)) {
14295     NS_WARNING("Failed to abort all transactions!");
14296   }
14297 
14298   if (!Helper::InvalidateMutableFiles(mMutableFiles)) {
14299     NS_WARNING("Failed to abort all mutable files!");
14300   }
14301 
14302   MOZ_ALWAYS_TRUE(CloseInternal());
14303 
14304   CleanupMetadata();
14305 }
14306 
14307 nsresult
EnsureConnection()14308 Database::EnsureConnection()
14309 {
14310   MOZ_ASSERT(!NS_IsMainThread());
14311   MOZ_ASSERT(!IsOnBackgroundThread());
14312 
14313   PROFILER_LABEL("IndexedDB",
14314                  "Database::EnsureConnection",
14315                  js::ProfileEntry::Category::STORAGE);
14316 
14317   if (!mConnection || !mConnection->GetStorageConnection()) {
14318     nsresult rv =
14319       gConnectionPool->GetOrCreateConnection(this, getter_AddRefs(mConnection));
14320     if (NS_WARN_IF(NS_FAILED(rv))) {
14321       return rv;
14322     }
14323   }
14324 
14325   AssertIsOnConnectionThread();
14326 
14327   return NS_OK;
14328 }
14329 
14330 bool
RegisterTransaction(TransactionBase * aTransaction)14331 Database::RegisterTransaction(TransactionBase* aTransaction)
14332 {
14333   AssertIsOnBackgroundThread();
14334   MOZ_ASSERT(aTransaction);
14335   MOZ_ASSERT(!mTransactions.GetEntry(aTransaction));
14336   MOZ_ASSERT(mDirectoryLock);
14337   MOZ_ASSERT(!mInvalidated);
14338   MOZ_ASSERT(!mClosed);
14339 
14340   if (NS_WARN_IF(!mTransactions.PutEntry(aTransaction, fallible))) {
14341     return false;
14342   }
14343 
14344   return true;
14345 }
14346 
14347 void
UnregisterTransaction(TransactionBase * aTransaction)14348 Database::UnregisterTransaction(TransactionBase* aTransaction)
14349 {
14350   AssertIsOnBackgroundThread();
14351   MOZ_ASSERT(aTransaction);
14352   MOZ_ASSERT(mTransactions.GetEntry(aTransaction));
14353 
14354   mTransactions.RemoveEntry(aTransaction);
14355 
14356   MaybeCloseConnection();
14357 }
14358 
14359 bool
RegisterMutableFile(MutableFile * aMutableFile)14360 Database::RegisterMutableFile(MutableFile* aMutableFile)
14361 {
14362   AssertIsOnBackgroundThread();
14363   MOZ_ASSERT(aMutableFile);
14364   MOZ_ASSERT(!mMutableFiles.GetEntry(aMutableFile));
14365   MOZ_ASSERT(mDirectoryLock);
14366 
14367   if (NS_WARN_IF(!mMutableFiles.PutEntry(aMutableFile, fallible))) {
14368     return false;
14369   }
14370 
14371   return true;
14372 }
14373 
14374 void
UnregisterMutableFile(MutableFile * aMutableFile)14375 Database::UnregisterMutableFile(MutableFile* aMutableFile)
14376 {
14377   AssertIsOnBackgroundThread();
14378   MOZ_ASSERT(aMutableFile);
14379   MOZ_ASSERT(mMutableFiles.GetEntry(aMutableFile));
14380 
14381   mMutableFiles.RemoveEntry(aMutableFile);
14382 }
14383 
14384 void
NoteActiveMutableFile()14385 Database::NoteActiveMutableFile()
14386 {
14387   AssertIsOnBackgroundThread();
14388   MOZ_ASSERT(mDirectoryLock);
14389   MOZ_ASSERT(mActiveMutableFileCount < UINT32_MAX);
14390 
14391   ++mActiveMutableFileCount;
14392 }
14393 
14394 void
NoteInactiveMutableFile()14395 Database::NoteInactiveMutableFile()
14396 {
14397   AssertIsOnBackgroundThread();
14398   MOZ_ASSERT(mActiveMutableFileCount > 0);
14399 
14400   --mActiveMutableFileCount;
14401 
14402   MaybeCloseConnection();
14403 }
14404 
14405 void
SetActorAlive()14406 Database::SetActorAlive()
14407 {
14408   AssertIsOnBackgroundThread();
14409   MOZ_ASSERT(!mActorWasAlive);
14410   MOZ_ASSERT(!mActorDestroyed);
14411 
14412   mActorWasAlive = true;
14413 
14414   // This reference will be absorbed by IPDL and released when the actor is
14415   // destroyed.
14416   AddRef();
14417 }
14418 
14419 bool
CloseInternal()14420 Database::CloseInternal()
14421 {
14422   AssertIsOnBackgroundThread();
14423 
14424   if (mClosed) {
14425     if (NS_WARN_IF(!IsInvalidated())) {
14426       // Kill misbehaving child for sending the close message twice.
14427       return false;
14428     }
14429 
14430     // Ignore harmless race when we just invalidated the database.
14431     return true;
14432   }
14433 
14434   mClosed = true;
14435 
14436   if (gConnectionPool) {
14437     gConnectionPool->CloseDatabaseWhenIdle(Id());
14438   }
14439 
14440   DatabaseActorInfo* info;
14441   MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info));
14442 
14443   MOZ_ASSERT(info->mLiveDatabases.Contains(this));
14444 
14445   if (info->mWaitingFactoryOp) {
14446     info->mWaitingFactoryOp->NoteDatabaseClosed(this);
14447   }
14448 
14449   MaybeCloseConnection();
14450 
14451   return true;
14452 }
14453 
14454 void
MaybeCloseConnection()14455 Database::MaybeCloseConnection()
14456 {
14457   AssertIsOnBackgroundThread();
14458 
14459   if (!mTransactions.Count() &&
14460       !mActiveMutableFileCount &&
14461       IsClosed() &&
14462       mDirectoryLock) {
14463     nsCOMPtr<nsIRunnable> callback =
14464       NewRunnableMethod(this, &Database::ConnectionClosedCallback);
14465 
14466     RefPtr<WaitForTransactionsHelper> helper =
14467       new WaitForTransactionsHelper(Id(), callback);
14468     helper->WaitForTransactions();
14469   }
14470 }
14471 
14472 void
ConnectionClosedCallback()14473 Database::ConnectionClosedCallback()
14474 {
14475   AssertIsOnBackgroundThread();
14476   MOZ_ASSERT(mClosed);
14477   MOZ_ASSERT(!mTransactions.Count());
14478   MOZ_ASSERT(!mActiveMutableFileCount);
14479 
14480   mDirectoryLock = nullptr;
14481 
14482   CleanupMetadata();
14483 
14484   if (IsInvalidated() && IsActorAlive()) {
14485     // Step 3 and 4 of "5.2 Closing a Database":
14486     // 1. Wait for all transactions to complete.
14487     // 2. Fire a close event if forced flag is set, i.e., IsInvalidated() in our
14488     //    implementation.
14489     Unused << SendCloseAfterInvalidationComplete();
14490   }
14491 }
14492 
14493 void
CleanupMetadata()14494 Database::CleanupMetadata()
14495 {
14496   AssertIsOnBackgroundThread();
14497 
14498   if (!mMetadataCleanedUp) {
14499     mMetadataCleanedUp = true;
14500 
14501     DatabaseActorInfo* info;
14502     MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info));
14503     MOZ_ALWAYS_TRUE(info->mLiveDatabases.RemoveElement(this));
14504 
14505     if (info->mLiveDatabases.IsEmpty()) {
14506       MOZ_ASSERT(!info->mWaitingFactoryOp ||
14507                  !info->mWaitingFactoryOp->HasBlockedDatabases());
14508       gLiveDatabaseHashtable->Remove(Id());
14509     }
14510 
14511     // Match the IncreaseBusyCount in OpenDatabaseOp::EnsureDatabaseActor().
14512     DecreaseBusyCount();
14513   }
14514 }
14515 
14516 bool
VerifyRequestParams(const DatabaseRequestParams & aParams) const14517 Database::VerifyRequestParams(const DatabaseRequestParams& aParams) const
14518 {
14519   AssertIsOnBackgroundThread();
14520   MOZ_ASSERT(aParams.type() != DatabaseRequestParams::T__None);
14521 
14522   switch (aParams.type()) {
14523     case DatabaseRequestParams::TCreateFileParams: {
14524       if (NS_WARN_IF(mFileHandleDisabled)) {
14525         ASSERT_UNLESS_FUZZING();
14526         return false;
14527       }
14528 
14529       const CreateFileParams& params = aParams.get_CreateFileParams();
14530 
14531       if (NS_WARN_IF(params.name().IsEmpty())) {
14532         ASSERT_UNLESS_FUZZING();
14533         return false;
14534       }
14535 
14536       break;
14537     }
14538 
14539     default:
14540       MOZ_CRASH("Should never get here!");
14541   }
14542 
14543   return true;
14544 }
14545 
14546 void
ActorDestroy(ActorDestroyReason aWhy)14547 Database::ActorDestroy(ActorDestroyReason aWhy)
14548 {
14549   AssertIsOnBackgroundThread();
14550   MOZ_ASSERT(!mActorDestroyed);
14551 
14552   mActorDestroyed = true;
14553 
14554   if (!IsInvalidated()) {
14555     Invalidate();
14556   }
14557 }
14558 
14559 PBackgroundIDBDatabaseFileParent*
AllocPBackgroundIDBDatabaseFileParent(PBlobParent * aBlobParent)14560 Database::AllocPBackgroundIDBDatabaseFileParent(PBlobParent* aBlobParent)
14561 {
14562   AssertIsOnBackgroundThread();
14563   MOZ_ASSERT(aBlobParent);
14564 
14565   RefPtr<BlobImpl> blobImpl =
14566     static_cast<BlobParent*>(aBlobParent)->GetBlobImpl();
14567   MOZ_ASSERT(blobImpl);
14568 
14569   RefPtr<FileInfo> fileInfo;
14570   RefPtr<DatabaseFile> actor;
14571 
14572   RefPtr<BlobImplStoredFile> storedFileImpl = do_QueryObject(blobImpl);
14573   if (storedFileImpl && storedFileImpl->IsShareable(mFileManager)) {
14574     // This blob was previously shared with the child.
14575     fileInfo = storedFileImpl->GetFileInfo();
14576     MOZ_ASSERT(fileInfo);
14577 
14578     actor = new DatabaseFile(fileInfo);
14579   } else {
14580     // This is a blob we haven't seen before.
14581     fileInfo = mFileManager->GetNewFileInfo();
14582     MOZ_ASSERT(fileInfo);
14583 
14584     actor = new DatabaseFile(blobImpl, fileInfo);
14585   }
14586 
14587   MOZ_ASSERT(actor);
14588 
14589   return actor.forget().take();
14590 }
14591 
14592 bool
DeallocPBackgroundIDBDatabaseFileParent(PBackgroundIDBDatabaseFileParent * aActor)14593 Database::DeallocPBackgroundIDBDatabaseFileParent(
14594                                        PBackgroundIDBDatabaseFileParent* aActor)
14595 {
14596   AssertIsOnBackgroundThread();
14597   MOZ_ASSERT(aActor);
14598 
14599   RefPtr<DatabaseFile> actor =
14600     dont_AddRef(static_cast<DatabaseFile*>(aActor));
14601   return true;
14602 }
14603 
14604 PBackgroundIDBDatabaseRequestParent*
AllocPBackgroundIDBDatabaseRequestParent(const DatabaseRequestParams & aParams)14605 Database::AllocPBackgroundIDBDatabaseRequestParent(
14606                                            const DatabaseRequestParams& aParams)
14607 {
14608   AssertIsOnBackgroundThread();
14609   MOZ_ASSERT(aParams.type() != DatabaseRequestParams::T__None);
14610 
14611 #ifdef DEBUG
14612   // Always verify parameters in DEBUG builds!
14613   bool trustParams = false;
14614 #else
14615   PBackgroundParent* backgroundActor = GetBackgroundParent();
14616   MOZ_ASSERT(backgroundActor);
14617 
14618   bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
14619 #endif
14620 
14621   if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
14622     ASSERT_UNLESS_FUZZING();
14623     return nullptr;
14624   }
14625 
14626   RefPtr<DatabaseOp> actor;
14627 
14628   switch (aParams.type()) {
14629     case DatabaseRequestParams::TCreateFileParams: {
14630       actor = new CreateFileOp(this, aParams);
14631       break;
14632     }
14633 
14634     default:
14635       MOZ_CRASH("Should never get here!");
14636   }
14637 
14638   MOZ_ASSERT(actor);
14639 
14640   // Transfer ownership to IPDL.
14641   return actor.forget().take();
14642 }
14643 
14644 bool
RecvPBackgroundIDBDatabaseRequestConstructor(PBackgroundIDBDatabaseRequestParent * aActor,const DatabaseRequestParams & aParams)14645 Database::RecvPBackgroundIDBDatabaseRequestConstructor(
14646                                     PBackgroundIDBDatabaseRequestParent* aActor,
14647                                     const DatabaseRequestParams& aParams)
14648 {
14649   AssertIsOnBackgroundThread();
14650   MOZ_ASSERT(aActor);
14651   MOZ_ASSERT(aParams.type() != DatabaseRequestParams::T__None);
14652 
14653   auto* op = static_cast<DatabaseOp*>(aActor);
14654 
14655   op->RunImmediately();
14656 
14657   return true;
14658 }
14659 
14660 bool
DeallocPBackgroundIDBDatabaseRequestParent(PBackgroundIDBDatabaseRequestParent * aActor)14661 Database::DeallocPBackgroundIDBDatabaseRequestParent(
14662                                     PBackgroundIDBDatabaseRequestParent* aActor)
14663 {
14664   AssertIsOnBackgroundThread();
14665   MOZ_ASSERT(aActor);
14666 
14667   // Transfer ownership back from IPDL.
14668   RefPtr<DatabaseOp> op = dont_AddRef(static_cast<DatabaseOp*>(aActor));
14669   return true;
14670 }
14671 
14672 PBackgroundIDBTransactionParent*
AllocPBackgroundIDBTransactionParent(const nsTArray<nsString> & aObjectStoreNames,const Mode & aMode)14673 Database::AllocPBackgroundIDBTransactionParent(
14674                                     const nsTArray<nsString>& aObjectStoreNames,
14675                                     const Mode& aMode)
14676 {
14677   AssertIsOnBackgroundThread();
14678 
14679   // Once a database is closed it must not try to open new transactions.
14680   if (NS_WARN_IF(mClosed)) {
14681     if (!mInvalidated) {
14682       ASSERT_UNLESS_FUZZING();
14683     }
14684     return nullptr;
14685   }
14686 
14687   if (NS_WARN_IF(aObjectStoreNames.IsEmpty())) {
14688     ASSERT_UNLESS_FUZZING();
14689     return nullptr;
14690   }
14691 
14692   if (NS_WARN_IF(aMode != IDBTransaction::READ_ONLY &&
14693                  aMode != IDBTransaction::READ_WRITE &&
14694                  aMode != IDBTransaction::READ_WRITE_FLUSH &&
14695                  aMode != IDBTransaction::CLEANUPff)) {
14696     ASSERT_UNLESS_FUZZING();
14697     return nullptr;
14698   }
14699 
14700   // If this is a readwrite transaction to a chrome database make sure the child
14701   // has write access.
14702   if (NS_WARN_IF((aMode == IDBTransaction::READ_WRITE ||
14703                   aMode == IDBTransaction::READ_WRITE_FLUSH ||
14704                   aMode == IDBTransaction::CLEANUPff) &&
14705                  mPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo &&
14706                  !mChromeWriteAccessAllowed)) {
14707     return nullptr;
14708   }
14709 
14710   const ObjectStoreTable& objectStores = mMetadata->mObjectStores;
14711   const uint32_t nameCount = aObjectStoreNames.Length();
14712 
14713   if (NS_WARN_IF(nameCount > objectStores.Count())) {
14714     ASSERT_UNLESS_FUZZING();
14715     return nullptr;
14716   }
14717 
14718   FallibleTArray<RefPtr<FullObjectStoreMetadata>> fallibleObjectStores;
14719   if (NS_WARN_IF(!fallibleObjectStores.SetCapacity(nameCount, fallible))) {
14720     return nullptr;
14721   }
14722 
14723   for (uint32_t nameIndex = 0; nameIndex < nameCount; nameIndex++) {
14724     const nsString& name = aObjectStoreNames[nameIndex];
14725 
14726     if (nameIndex) {
14727       // Make sure that this name is sorted properly and not a duplicate.
14728       if (NS_WARN_IF(name <= aObjectStoreNames[nameIndex - 1])) {
14729         ASSERT_UNLESS_FUZZING();
14730         return nullptr;
14731       }
14732     }
14733 
14734     for (auto iter = objectStores.ConstIter(); !iter.Done(); iter.Next()) {
14735       auto value = iter.Data();
14736       MOZ_ASSERT(iter.Key());
14737 
14738       if (name == value->mCommonMetadata.name() && !value->mDeleted) {
14739         if (NS_WARN_IF(!fallibleObjectStores.AppendElement(value, fallible))) {
14740           return nullptr;
14741         }
14742         break;
14743       }
14744     }
14745   }
14746 
14747   nsTArray<RefPtr<FullObjectStoreMetadata>> infallibleObjectStores;
14748   infallibleObjectStores.SwapElements(fallibleObjectStores);
14749 
14750   RefPtr<NormalTransaction> transaction =
14751     new NormalTransaction(this, aMode, infallibleObjectStores);
14752 
14753   MOZ_ASSERT(infallibleObjectStores.IsEmpty());
14754 
14755   return transaction.forget().take();
14756 }
14757 
14758 bool
RecvPBackgroundIDBTransactionConstructor(PBackgroundIDBTransactionParent * aActor,InfallibleTArray<nsString> && aObjectStoreNames,const Mode & aMode)14759 Database::RecvPBackgroundIDBTransactionConstructor(
14760                                     PBackgroundIDBTransactionParent* aActor,
14761                                     InfallibleTArray<nsString>&& aObjectStoreNames,
14762                                     const Mode& aMode)
14763 {
14764   AssertIsOnBackgroundThread();
14765   MOZ_ASSERT(aActor);
14766   MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
14767   MOZ_ASSERT(aMode == IDBTransaction::READ_ONLY ||
14768              aMode == IDBTransaction::READ_WRITE ||
14769              aMode == IDBTransaction::READ_WRITE_FLUSH ||
14770              aMode == IDBTransaction::CLEANUPff);
14771   MOZ_ASSERT(!mClosed);
14772 
14773   if (IsInvalidated()) {
14774     // This is an expected race. We don't want the child to die here, just don't
14775     // actually do any work.
14776     return true;
14777   }
14778 
14779   if (!gConnectionPool) {
14780     gConnectionPool = new ConnectionPool();
14781   }
14782 
14783   auto* transaction = static_cast<NormalTransaction*>(aActor);
14784 
14785   RefPtr<StartTransactionOp> startOp = new StartTransactionOp(transaction);
14786 
14787   uint64_t transactionId =
14788     startOp->StartOnConnectionPool(GetLoggingInfo()->Id(),
14789                                    mMetadata->mDatabaseId,
14790                                    transaction->LoggingSerialNumber(),
14791                                    aObjectStoreNames,
14792                                    aMode != IDBTransaction::READ_ONLY);
14793 
14794   transaction->SetActive(transactionId);
14795 
14796   if (NS_WARN_IF(!RegisterTransaction(transaction))) {
14797     IDB_REPORT_INTERNAL_ERR();
14798     transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, /* aForce */ false);
14799     return true;
14800   }
14801 
14802   return true;
14803 }
14804 
14805 bool
DeallocPBackgroundIDBTransactionParent(PBackgroundIDBTransactionParent * aActor)14806 Database::DeallocPBackgroundIDBTransactionParent(
14807                                         PBackgroundIDBTransactionParent* aActor)
14808 {
14809   AssertIsOnBackgroundThread();
14810   MOZ_ASSERT(aActor);
14811 
14812   RefPtr<NormalTransaction> transaction =
14813     dont_AddRef(static_cast<NormalTransaction*>(aActor));
14814   return true;
14815 }
14816 
14817 PBackgroundIDBVersionChangeTransactionParent*
AllocPBackgroundIDBVersionChangeTransactionParent(const uint64_t & aCurrentVersion,const uint64_t & aRequestedVersion,const int64_t & aNextObjectStoreId,const int64_t & aNextIndexId)14818 Database::AllocPBackgroundIDBVersionChangeTransactionParent(
14819                                               const uint64_t& aCurrentVersion,
14820                                               const uint64_t& aRequestedVersion,
14821                                               const int64_t& aNextObjectStoreId,
14822                                               const int64_t& aNextIndexId)
14823 {
14824   MOZ_CRASH("PBackgroundIDBVersionChangeTransactionParent actors should be "
14825             "constructed manually!");
14826 }
14827 
14828 bool
DeallocPBackgroundIDBVersionChangeTransactionParent(PBackgroundIDBVersionChangeTransactionParent * aActor)14829 Database::DeallocPBackgroundIDBVersionChangeTransactionParent(
14830                            PBackgroundIDBVersionChangeTransactionParent* aActor)
14831 {
14832   AssertIsOnBackgroundThread();
14833   MOZ_ASSERT(aActor);
14834 
14835   RefPtr<VersionChangeTransaction> transaction =
14836     dont_AddRef(static_cast<VersionChangeTransaction*>(aActor));
14837   return true;
14838 }
14839 
14840 Database::PBackgroundMutableFileParent*
AllocPBackgroundMutableFileParent(const nsString & aName,const nsString & aType)14841 Database::AllocPBackgroundMutableFileParent(const nsString& aName,
14842                                             const nsString& aType)
14843 {
14844   MOZ_CRASH("PBackgroundMutableFileParent actors should be constructed "
14845             "manually!");
14846 }
14847 
14848 bool
DeallocPBackgroundMutableFileParent(PBackgroundMutableFileParent * aActor)14849 Database::DeallocPBackgroundMutableFileParent(
14850                                            PBackgroundMutableFileParent* aActor)
14851 {
14852   AssertIsOnBackgroundThread();
14853   MOZ_ASSERT(aActor);
14854 
14855   // Transfer ownership back from IPDL.
14856   RefPtr<MutableFile> mutableFile =
14857     dont_AddRef(static_cast<MutableFile*>(aActor));
14858   return true;
14859 }
14860 
14861 bool
RecvDeleteMe()14862 Database::RecvDeleteMe()
14863 {
14864   AssertIsOnBackgroundThread();
14865   MOZ_ASSERT(!mActorDestroyed);
14866 
14867   return PBackgroundIDBDatabaseParent::Send__delete__(this);
14868 }
14869 
14870 bool
RecvBlocked()14871 Database::RecvBlocked()
14872 {
14873   AssertIsOnBackgroundThread();
14874 
14875   if (NS_WARN_IF(mClosed)) {
14876     return false;
14877   }
14878 
14879   DatabaseActorInfo* info;
14880   MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info));
14881 
14882   MOZ_ASSERT(info->mLiveDatabases.Contains(this));
14883   MOZ_ASSERT(info->mWaitingFactoryOp);
14884 
14885   info->mWaitingFactoryOp->NoteDatabaseBlocked(this);
14886 
14887   return true;
14888 }
14889 
14890 bool
RecvClose()14891 Database::RecvClose()
14892 {
14893   AssertIsOnBackgroundThread();
14894 
14895   if (NS_WARN_IF(!CloseInternal())) {
14896     ASSERT_UNLESS_FUZZING();
14897     return false;
14898   }
14899 
14900   return true;
14901 }
14902 
14903 void
14904 Database::
RunOnConnectionThread()14905 StartTransactionOp::RunOnConnectionThread()
14906 {
14907   MOZ_ASSERT(!IsOnBackgroundThread());
14908   MOZ_ASSERT(Transaction());
14909   MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
14910 
14911   IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
14912                  "Beginning database work",
14913                "IndexedDB %s: P T[%lld]: DB Start",
14914                IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
14915                mLoggingSerialNumber);
14916 
14917   TransactionDatabaseOperationBase::RunOnConnectionThread();
14918 }
14919 
14920 nsresult
14921 Database::
DoDatabaseWork(DatabaseConnection * aConnection)14922 StartTransactionOp::DoDatabaseWork(DatabaseConnection* aConnection)
14923 {
14924   MOZ_ASSERT(aConnection);
14925   aConnection->AssertIsOnConnectionThread();
14926 
14927   Transaction()->SetActiveOnConnectionThread();
14928 
14929   if (Transaction()->GetMode() == IDBTransaction::CLEANUPff) {
14930     nsresult rv = aConnection->DisableQuotaChecks();
14931     if (NS_WARN_IF(NS_FAILED(rv))) {
14932       return rv;
14933     }
14934   }
14935 
14936   if (Transaction()->GetMode() != IDBTransaction::READ_ONLY) {
14937     nsresult rv = aConnection->BeginWriteTransaction();
14938     if (NS_WARN_IF(NS_FAILED(rv))) {
14939       return rv;
14940     }
14941   }
14942 
14943   return NS_OK;
14944 }
14945 
14946 nsresult
14947 Database::
SendSuccessResult()14948 StartTransactionOp::SendSuccessResult()
14949 {
14950   // We don't need to do anything here.
14951   return NS_OK;
14952 }
14953 
14954 bool
14955 Database::
SendFailureResult(nsresult)14956 StartTransactionOp::SendFailureResult(nsresult /* aResultCode */)
14957 {
14958   IDB_REPORT_INTERNAL_ERR();
14959 
14960   // Abort the transaction.
14961   return false;
14962 }
14963 
14964 void
14965 Database::
Cleanup()14966 StartTransactionOp::Cleanup()
14967 {
14968 #ifdef DEBUG
14969   // StartTransactionOp is not a normal database operation that is tied to an
14970   // actor. Do this to make our assertions happy.
14971   NoteActorDestroyed();
14972 #endif
14973 
14974   TransactionDatabaseOperationBase::Cleanup();
14975 }
14976 
14977 /*******************************************************************************
14978  * TransactionBase
14979  ******************************************************************************/
14980 
TransactionBase(Database * aDatabase,Mode aMode)14981 TransactionBase::TransactionBase(Database* aDatabase, Mode aMode)
14982   : mDatabase(aDatabase)
14983   , mTransactionId(0)
14984   , mDatabaseId(aDatabase->Id())
14985   , mLoggingSerialNumber(aDatabase->GetLoggingInfo()->NextTransactionSN(aMode))
14986   , mActiveRequestCount(0)
14987   , mInvalidatedOnAnyThread(false)
14988   , mMode(aMode)
14989   , mHasBeenActive(false)
14990   , mHasBeenActiveOnConnectionThread(false)
14991   , mActorDestroyed(false)
14992   , mInvalidated(false)
14993   , mResultCode(NS_OK)
14994   , mCommitOrAbortReceived(false)
14995   , mCommittedOrAborted(false)
14996   , mForceAborted(false)
14997 {
14998   AssertIsOnBackgroundThread();
14999   MOZ_ASSERT(aDatabase);
15000   MOZ_ASSERT(mLoggingSerialNumber);
15001 }
15002 
~TransactionBase()15003 TransactionBase::~TransactionBase()
15004 {
15005   MOZ_ASSERT(!mActiveRequestCount);
15006   MOZ_ASSERT(mActorDestroyed);
15007   MOZ_ASSERT_IF(mHasBeenActive, mCommittedOrAborted);
15008 }
15009 
15010 void
Abort(nsresult aResultCode,bool aForce)15011 TransactionBase::Abort(nsresult aResultCode, bool aForce)
15012 {
15013   AssertIsOnBackgroundThread();
15014   MOZ_ASSERT(NS_FAILED(aResultCode));
15015 
15016   if (NS_SUCCEEDED(mResultCode)) {
15017     mResultCode = aResultCode;
15018   }
15019 
15020   if (aForce) {
15021     mForceAborted = true;
15022   }
15023 
15024   MaybeCommitOrAbort();
15025 }
15026 
15027 bool
RecvCommit()15028 TransactionBase::RecvCommit()
15029 {
15030   AssertIsOnBackgroundThread();
15031 
15032   if (NS_WARN_IF(mCommitOrAbortReceived)) {
15033     ASSERT_UNLESS_FUZZING();
15034     return false;
15035   }
15036 
15037   mCommitOrAbortReceived = true;
15038 
15039   MaybeCommitOrAbort();
15040   return true;
15041 }
15042 
15043 bool
RecvAbort(nsresult aResultCode)15044 TransactionBase::RecvAbort(nsresult aResultCode)
15045 {
15046   AssertIsOnBackgroundThread();
15047 
15048   if (NS_WARN_IF(NS_SUCCEEDED(aResultCode))) {
15049     ASSERT_UNLESS_FUZZING();
15050     return false;
15051   }
15052 
15053   if (NS_WARN_IF(NS_ERROR_GET_MODULE(aResultCode) !=
15054                  NS_ERROR_MODULE_DOM_INDEXEDDB)) {
15055     ASSERT_UNLESS_FUZZING();
15056     return false;
15057   }
15058 
15059   if (NS_WARN_IF(mCommitOrAbortReceived)) {
15060     ASSERT_UNLESS_FUZZING();
15061     return false;
15062   }
15063 
15064   mCommitOrAbortReceived = true;
15065 
15066   Abort(aResultCode, /* aForce */ false);
15067   return true;
15068 }
15069 
15070 void
CommitOrAbort()15071 TransactionBase::CommitOrAbort()
15072 {
15073   AssertIsOnBackgroundThread();
15074   MOZ_ASSERT(!mCommittedOrAborted);
15075 
15076   mCommittedOrAborted = true;
15077 
15078   if (!mHasBeenActive) {
15079     return;
15080   }
15081 
15082   RefPtr<CommitOp> commitOp =
15083     new CommitOp(this, ClampResultCode(mResultCode));
15084 
15085   gConnectionPool->Finish(TransactionId(), commitOp);
15086 }
15087 
15088 already_AddRefed<FullObjectStoreMetadata>
GetMetadataForObjectStoreId(int64_t aObjectStoreId) const15089 TransactionBase::GetMetadataForObjectStoreId(int64_t aObjectStoreId) const
15090 {
15091   AssertIsOnBackgroundThread();
15092   MOZ_ASSERT(aObjectStoreId);
15093 
15094   if (!aObjectStoreId) {
15095     return nullptr;
15096   }
15097 
15098   RefPtr<FullObjectStoreMetadata> metadata;
15099   if (!mDatabase->Metadata()->mObjectStores.Get(aObjectStoreId,
15100                                                 getter_AddRefs(metadata)) ||
15101       metadata->mDeleted) {
15102     return nullptr;
15103   }
15104 
15105   MOZ_ASSERT(metadata->mCommonMetadata.id() == aObjectStoreId);
15106 
15107   return metadata.forget();
15108 }
15109 
15110 already_AddRefed<FullIndexMetadata>
GetMetadataForIndexId(FullObjectStoreMetadata * const aObjectStoreMetadata,int64_t aIndexId) const15111 TransactionBase::GetMetadataForIndexId(
15112                             FullObjectStoreMetadata* const aObjectStoreMetadata,
15113                             int64_t aIndexId) const
15114 {
15115   AssertIsOnBackgroundThread();
15116   MOZ_ASSERT(aIndexId);
15117 
15118   if (!aIndexId) {
15119     return nullptr;
15120   }
15121 
15122   RefPtr<FullIndexMetadata> metadata;
15123   if (!aObjectStoreMetadata->mIndexes.Get(aIndexId, getter_AddRefs(metadata)) ||
15124       metadata->mDeleted) {
15125     return nullptr;
15126   }
15127 
15128   MOZ_ASSERT(metadata->mCommonMetadata.id() == aIndexId);
15129 
15130   return metadata.forget();
15131 }
15132 
15133 void
NoteModifiedAutoIncrementObjectStore(FullObjectStoreMetadata * aMetadata)15134 TransactionBase::NoteModifiedAutoIncrementObjectStore(
15135                                              FullObjectStoreMetadata* aMetadata)
15136 {
15137   AssertIsOnConnectionThread();
15138   MOZ_ASSERT(aMetadata);
15139 
15140   if (!mModifiedAutoIncrementObjectStoreMetadataArray.Contains(aMetadata)) {
15141     mModifiedAutoIncrementObjectStoreMetadataArray.AppendElement(aMetadata);
15142   }
15143 }
15144 
15145 void
ForgetModifiedAutoIncrementObjectStore(FullObjectStoreMetadata * aMetadata)15146 TransactionBase::ForgetModifiedAutoIncrementObjectStore(
15147                                              FullObjectStoreMetadata* aMetadata)
15148 {
15149   AssertIsOnConnectionThread();
15150   MOZ_ASSERT(aMetadata);
15151 
15152   mModifiedAutoIncrementObjectStoreMetadataArray.RemoveElement(aMetadata);
15153 }
15154 
15155 bool
VerifyRequestParams(const RequestParams & aParams) const15156 TransactionBase::VerifyRequestParams(const RequestParams& aParams) const
15157 {
15158   AssertIsOnBackgroundThread();
15159   MOZ_ASSERT(aParams.type() != RequestParams::T__None);
15160 
15161   switch (aParams.type()) {
15162     case RequestParams::TObjectStoreAddParams: {
15163       const ObjectStoreAddPutParams& params =
15164         aParams.get_ObjectStoreAddParams().commonParams();
15165       if (NS_WARN_IF(!VerifyRequestParams(params))) {
15166         ASSERT_UNLESS_FUZZING();
15167         return false;
15168       }
15169       break;
15170     }
15171 
15172     case RequestParams::TObjectStorePutParams: {
15173       const ObjectStoreAddPutParams& params =
15174         aParams.get_ObjectStorePutParams().commonParams();
15175       if (NS_WARN_IF(!VerifyRequestParams(params))) {
15176         ASSERT_UNLESS_FUZZING();
15177         return false;
15178       }
15179       break;
15180     }
15181 
15182     case RequestParams::TObjectStoreGetParams: {
15183       const ObjectStoreGetParams& params = aParams.get_ObjectStoreGetParams();
15184       const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15185         GetMetadataForObjectStoreId(params.objectStoreId());
15186       if (NS_WARN_IF(!objectStoreMetadata)) {
15187         ASSERT_UNLESS_FUZZING();
15188         return false;
15189       }
15190       if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
15191         ASSERT_UNLESS_FUZZING();
15192         return false;
15193       }
15194       break;
15195     }
15196 
15197     case RequestParams::TObjectStoreGetKeyParams: {
15198       const ObjectStoreGetKeyParams& params =
15199         aParams.get_ObjectStoreGetKeyParams();
15200       const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15201         GetMetadataForObjectStoreId(params.objectStoreId());
15202       if (NS_WARN_IF(!objectStoreMetadata)) {
15203         ASSERT_UNLESS_FUZZING();
15204         return false;
15205       }
15206       if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
15207         ASSERT_UNLESS_FUZZING();
15208         return false;
15209       }
15210       break;
15211     }
15212 
15213     case RequestParams::TObjectStoreGetAllParams: {
15214       const ObjectStoreGetAllParams& params =
15215         aParams.get_ObjectStoreGetAllParams();
15216       const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15217         GetMetadataForObjectStoreId(params.objectStoreId());
15218       if (NS_WARN_IF(!objectStoreMetadata)) {
15219         ASSERT_UNLESS_FUZZING();
15220         return false;
15221       }
15222       if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15223         ASSERT_UNLESS_FUZZING();
15224         return false;
15225       }
15226       break;
15227     }
15228 
15229     case RequestParams::TObjectStoreGetAllKeysParams: {
15230       const ObjectStoreGetAllKeysParams& params =
15231         aParams.get_ObjectStoreGetAllKeysParams();
15232       const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15233         GetMetadataForObjectStoreId(params.objectStoreId());
15234       if (NS_WARN_IF(!objectStoreMetadata)) {
15235         ASSERT_UNLESS_FUZZING();
15236         return false;
15237       }
15238       if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15239         ASSERT_UNLESS_FUZZING();
15240         return false;
15241       }
15242       break;
15243     }
15244 
15245     case RequestParams::TObjectStoreDeleteParams: {
15246       if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
15247                      mMode != IDBTransaction::READ_WRITE_FLUSH &&
15248                      mMode != IDBTransaction::CLEANUPff &&
15249                      mMode != IDBTransaction::VERSION_CHANGE)) {
15250         ASSERT_UNLESS_FUZZING();
15251         return false;
15252       }
15253 
15254       const ObjectStoreDeleteParams& params =
15255         aParams.get_ObjectStoreDeleteParams();
15256       const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15257         GetMetadataForObjectStoreId(params.objectStoreId());
15258       if (NS_WARN_IF(!objectStoreMetadata)) {
15259         ASSERT_UNLESS_FUZZING();
15260         return false;
15261       }
15262       if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
15263         ASSERT_UNLESS_FUZZING();
15264         return false;
15265       }
15266       break;
15267     }
15268 
15269     case RequestParams::TObjectStoreClearParams: {
15270       if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
15271                      mMode != IDBTransaction::READ_WRITE_FLUSH &&
15272                      mMode != IDBTransaction::CLEANUPff &&
15273                      mMode != IDBTransaction::VERSION_CHANGE)) {
15274         ASSERT_UNLESS_FUZZING();
15275         return false;
15276       }
15277 
15278       const ObjectStoreClearParams& params =
15279         aParams.get_ObjectStoreClearParams();
15280       const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15281         GetMetadataForObjectStoreId(params.objectStoreId());
15282       if (NS_WARN_IF(!objectStoreMetadata)) {
15283         ASSERT_UNLESS_FUZZING();
15284         return false;
15285       }
15286       break;
15287     }
15288 
15289     case RequestParams::TObjectStoreCountParams: {
15290       const ObjectStoreCountParams& params =
15291         aParams.get_ObjectStoreCountParams();
15292       const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15293         GetMetadataForObjectStoreId(params.objectStoreId());
15294       if (NS_WARN_IF(!objectStoreMetadata)) {
15295         ASSERT_UNLESS_FUZZING();
15296         return false;
15297       }
15298       if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15299         ASSERT_UNLESS_FUZZING();
15300         return false;
15301       }
15302       break;
15303     }
15304 
15305 
15306     case RequestParams::TIndexGetParams: {
15307       const IndexGetParams& params = aParams.get_IndexGetParams();
15308       const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15309         GetMetadataForObjectStoreId(params.objectStoreId());
15310       if (NS_WARN_IF(!objectStoreMetadata)) {
15311         ASSERT_UNLESS_FUZZING();
15312         return false;
15313       }
15314       const RefPtr<FullIndexMetadata> indexMetadata =
15315         GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15316       if (NS_WARN_IF(!indexMetadata)) {
15317         ASSERT_UNLESS_FUZZING();
15318         return false;
15319       }
15320       if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
15321         ASSERT_UNLESS_FUZZING();
15322         return false;
15323       }
15324       break;
15325     }
15326 
15327     case RequestParams::TIndexGetKeyParams: {
15328       const IndexGetKeyParams& params = aParams.get_IndexGetKeyParams();
15329       const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15330         GetMetadataForObjectStoreId(params.objectStoreId());
15331       if (NS_WARN_IF(!objectStoreMetadata)) {
15332         ASSERT_UNLESS_FUZZING();
15333         return false;
15334       }
15335       const RefPtr<FullIndexMetadata> indexMetadata =
15336         GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15337       if (NS_WARN_IF(!indexMetadata)) {
15338         ASSERT_UNLESS_FUZZING();
15339         return false;
15340       }
15341       if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
15342         ASSERT_UNLESS_FUZZING();
15343         return false;
15344       }
15345       break;
15346     }
15347 
15348     case RequestParams::TIndexGetAllParams: {
15349       const IndexGetAllParams& params = aParams.get_IndexGetAllParams();
15350       const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15351         GetMetadataForObjectStoreId(params.objectStoreId());
15352       if (NS_WARN_IF(!objectStoreMetadata)) {
15353         ASSERT_UNLESS_FUZZING();
15354         return false;
15355       }
15356       const RefPtr<FullIndexMetadata> indexMetadata =
15357         GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15358       if (NS_WARN_IF(!indexMetadata)) {
15359         ASSERT_UNLESS_FUZZING();
15360         return false;
15361       }
15362       if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15363         ASSERT_UNLESS_FUZZING();
15364         return false;
15365       }
15366       break;
15367     }
15368 
15369     case RequestParams::TIndexGetAllKeysParams: {
15370       const IndexGetAllKeysParams& params = aParams.get_IndexGetAllKeysParams();
15371       const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15372         GetMetadataForObjectStoreId(params.objectStoreId());
15373       if (NS_WARN_IF(!objectStoreMetadata)) {
15374         ASSERT_UNLESS_FUZZING();
15375         return false;
15376       }
15377       const RefPtr<FullIndexMetadata> indexMetadata =
15378         GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15379       if (NS_WARN_IF(!indexMetadata)) {
15380         ASSERT_UNLESS_FUZZING();
15381         return false;
15382       }
15383       if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15384         ASSERT_UNLESS_FUZZING();
15385         return false;
15386       }
15387       break;
15388     }
15389 
15390     case RequestParams::TIndexCountParams: {
15391       const IndexCountParams& params = aParams.get_IndexCountParams();
15392       const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
15393         GetMetadataForObjectStoreId(params.objectStoreId());
15394       if (NS_WARN_IF(!objectStoreMetadata)) {
15395         ASSERT_UNLESS_FUZZING();
15396         return false;
15397       }
15398       const RefPtr<FullIndexMetadata> indexMetadata =
15399         GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15400       if (NS_WARN_IF(!indexMetadata)) {
15401         ASSERT_UNLESS_FUZZING();
15402         return false;
15403       }
15404       if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15405         ASSERT_UNLESS_FUZZING();
15406         return false;
15407       }
15408       break;
15409     }
15410 
15411     default:
15412       MOZ_CRASH("Should never get here!");
15413   }
15414 
15415   return true;
15416 }
15417 
15418 bool
VerifyRequestParams(const SerializedKeyRange & aParams) const15419 TransactionBase::VerifyRequestParams(const SerializedKeyRange& aParams) const
15420 {
15421   AssertIsOnBackgroundThread();
15422 
15423   // XXX Check more here?
15424 
15425   if (aParams.isOnly()) {
15426     if (NS_WARN_IF(aParams.lower().IsUnset())) {
15427       ASSERT_UNLESS_FUZZING();
15428       return false;
15429     }
15430     if (NS_WARN_IF(!aParams.upper().IsUnset())) {
15431       ASSERT_UNLESS_FUZZING();
15432       return false;
15433     }
15434     if (NS_WARN_IF(aParams.lowerOpen())) {
15435       ASSERT_UNLESS_FUZZING();
15436       return false;
15437     }
15438     if (NS_WARN_IF(aParams.upperOpen())) {
15439       ASSERT_UNLESS_FUZZING();
15440       return false;
15441     }
15442   } else if (NS_WARN_IF(aParams.lower().IsUnset() &&
15443                         aParams.upper().IsUnset())) {
15444     ASSERT_UNLESS_FUZZING();
15445     return false;
15446   }
15447 
15448   return true;
15449 }
15450 
15451 bool
VerifyRequestParams(const ObjectStoreAddPutParams & aParams) const15452 TransactionBase::VerifyRequestParams(const ObjectStoreAddPutParams& aParams)
15453                                      const
15454 {
15455   AssertIsOnBackgroundThread();
15456 
15457   if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
15458                  mMode != IDBTransaction::READ_WRITE_FLUSH &&
15459                  mMode != IDBTransaction::VERSION_CHANGE)) {
15460     ASSERT_UNLESS_FUZZING();
15461     return false;
15462   }
15463 
15464   RefPtr<FullObjectStoreMetadata> objMetadata =
15465     GetMetadataForObjectStoreId(aParams.objectStoreId());
15466   if (NS_WARN_IF(!objMetadata)) {
15467     ASSERT_UNLESS_FUZZING();
15468     return false;
15469   }
15470 
15471   if (NS_WARN_IF(!aParams.cloneInfo().data().data.Size())) {
15472     ASSERT_UNLESS_FUZZING();
15473     return false;
15474   }
15475 
15476   if (objMetadata->mCommonMetadata.autoIncrement() &&
15477       objMetadata->mCommonMetadata.keyPath().IsValid() &&
15478       aParams.key().IsUnset()) {
15479     const SerializedStructuredCloneWriteInfo cloneInfo = aParams.cloneInfo();
15480 
15481     if (NS_WARN_IF(!cloneInfo.offsetToKeyProp())) {
15482       ASSERT_UNLESS_FUZZING();
15483       return false;
15484     }
15485 
15486     if (NS_WARN_IF(cloneInfo.data().data.Size() < sizeof(uint64_t))) {
15487       ASSERT_UNLESS_FUZZING();
15488       return false;
15489     }
15490 
15491     if (NS_WARN_IF(cloneInfo.offsetToKeyProp() >
15492                    (cloneInfo.data().data.Size() - sizeof(uint64_t)))) {
15493       ASSERT_UNLESS_FUZZING();
15494       return false;
15495     }
15496   } else if (NS_WARN_IF(aParams.cloneInfo().offsetToKeyProp())) {
15497     ASSERT_UNLESS_FUZZING();
15498     return false;
15499   }
15500 
15501   const nsTArray<IndexUpdateInfo>& updates = aParams.indexUpdateInfos();
15502 
15503   for (uint32_t index = 0; index < updates.Length(); index++) {
15504     RefPtr<FullIndexMetadata> indexMetadata =
15505       GetMetadataForIndexId(objMetadata, updates[index].indexId());
15506     if (NS_WARN_IF(!indexMetadata)) {
15507       ASSERT_UNLESS_FUZZING();
15508       return false;
15509     }
15510 
15511     if (NS_WARN_IF(updates[index].value().IsUnset())) {
15512       ASSERT_UNLESS_FUZZING();
15513       return false;
15514     }
15515 
15516     MOZ_ASSERT(!updates[index].value().GetBuffer().IsEmpty());
15517   }
15518 
15519   const nsTArray<FileAddInfo>& fileAddInfos = aParams.fileAddInfos();
15520 
15521   for (uint32_t index = 0; index < fileAddInfos.Length(); index++) {
15522     const FileAddInfo& fileAddInfo = fileAddInfos[index];
15523 
15524     const DatabaseOrMutableFile& file = fileAddInfo.file();
15525     MOZ_ASSERT(file.type() != DatabaseOrMutableFile::T__None);
15526 
15527     switch (fileAddInfo.type()) {
15528       case StructuredCloneFile::eBlob:
15529         if (NS_WARN_IF(file.type() !=
15530                          DatabaseOrMutableFile::TPBackgroundIDBDatabaseFileParent)) {
15531           ASSERT_UNLESS_FUZZING();
15532           return false;
15533         }
15534         if (NS_WARN_IF(!file.get_PBackgroundIDBDatabaseFileParent())) {
15535           ASSERT_UNLESS_FUZZING();
15536           return false;
15537         }
15538         break;
15539 
15540       case StructuredCloneFile::eMutableFile: {
15541         if (NS_WARN_IF(file.type() !=
15542                          DatabaseOrMutableFile::TPBackgroundMutableFileParent)) {
15543           ASSERT_UNLESS_FUZZING();
15544           return false;
15545         }
15546 
15547         if (NS_WARN_IF(mDatabase->IsFileHandleDisabled())) {
15548           ASSERT_UNLESS_FUZZING();
15549           return false;
15550         }
15551 
15552         auto mutableFile =
15553           static_cast<MutableFile*>(file.get_PBackgroundMutableFileParent());
15554 
15555         if (NS_WARN_IF(!mutableFile)) {
15556           ASSERT_UNLESS_FUZZING();
15557           return false;
15558         }
15559 
15560         Database* database = mutableFile->GetDatabase();
15561         if (NS_WARN_IF(!database)) {
15562           ASSERT_UNLESS_FUZZING();
15563           return false;
15564         }
15565 
15566         if (NS_WARN_IF(database->Id() != mDatabase->Id())) {
15567           ASSERT_UNLESS_FUZZING();
15568           return false;
15569         }
15570 
15571         break;
15572       }
15573 
15574       case StructuredCloneFile::eStructuredClone:
15575         ASSERT_UNLESS_FUZZING();
15576         return false;
15577 
15578       case StructuredCloneFile::eWasmBytecode:
15579       case StructuredCloneFile::eWasmCompiled:
15580         if (NS_WARN_IF(file.type() !=
15581                          DatabaseOrMutableFile::TPBackgroundIDBDatabaseFileParent)) {
15582           ASSERT_UNLESS_FUZZING();
15583           return false;
15584         }
15585         if (NS_WARN_IF(!file.get_PBackgroundIDBDatabaseFileParent())) {
15586           ASSERT_UNLESS_FUZZING();
15587           return false;
15588         }
15589         break;
15590 
15591       case StructuredCloneFile::eEndGuard:
15592         ASSERT_UNLESS_FUZZING();
15593         return false;
15594 
15595       default:
15596         MOZ_CRASH("Should never get here!");
15597     }
15598   }
15599 
15600   return true;
15601 }
15602 
15603 bool
VerifyRequestParams(const OptionalKeyRange & aParams) const15604 TransactionBase::VerifyRequestParams(const OptionalKeyRange& aParams) const
15605 {
15606   AssertIsOnBackgroundThread();
15607   MOZ_ASSERT(aParams.type() != OptionalKeyRange::T__None);
15608 
15609   switch (aParams.type()) {
15610     case OptionalKeyRange::TSerializedKeyRange:
15611       if (NS_WARN_IF(!VerifyRequestParams(aParams.get_SerializedKeyRange()))) {
15612         ASSERT_UNLESS_FUZZING();
15613         return false;
15614       }
15615       break;
15616 
15617     case OptionalKeyRange::Tvoid_t:
15618       break;
15619 
15620     default:
15621       MOZ_CRASH("Should never get here!");
15622   }
15623 
15624   return true;
15625 }
15626 
15627 void
NoteActiveRequest()15628 TransactionBase::NoteActiveRequest()
15629 {
15630   AssertIsOnBackgroundThread();
15631   MOZ_ASSERT(mActiveRequestCount < UINT64_MAX);
15632 
15633   mActiveRequestCount++;
15634 }
15635 
15636 void
NoteFinishedRequest()15637 TransactionBase::NoteFinishedRequest()
15638 {
15639   AssertIsOnBackgroundThread();
15640   MOZ_ASSERT(mActiveRequestCount);
15641 
15642   mActiveRequestCount--;
15643 
15644   MaybeCommitOrAbort();
15645 }
15646 
15647 void
Invalidate()15648 TransactionBase::Invalidate()
15649 {
15650   AssertIsOnBackgroundThread();
15651   MOZ_ASSERT(mInvalidated == mInvalidatedOnAnyThread);
15652 
15653   if (!mInvalidated) {
15654     mInvalidated = true;
15655     mInvalidatedOnAnyThread = true;
15656 
15657     Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, /* aForce */ false);
15658   }
15659 }
15660 
15661 PBackgroundIDBRequestParent*
AllocRequest(const RequestParams & aParams,bool aTrustParams)15662 TransactionBase::AllocRequest(const RequestParams& aParams, bool aTrustParams)
15663 {
15664   AssertIsOnBackgroundThread();
15665   MOZ_ASSERT(aParams.type() != RequestParams::T__None);
15666 
15667 #ifdef DEBUG
15668   // Always verify parameters in DEBUG builds!
15669   aTrustParams = false;
15670 #endif
15671 
15672   if (!aTrustParams && NS_WARN_IF(!VerifyRequestParams(aParams))) {
15673     ASSERT_UNLESS_FUZZING();
15674     return nullptr;
15675   }
15676 
15677   if (NS_WARN_IF(mCommitOrAbortReceived)) {
15678     ASSERT_UNLESS_FUZZING();
15679     return nullptr;
15680   }
15681 
15682   RefPtr<NormalTransactionOp> actor;
15683 
15684   switch (aParams.type()) {
15685     case RequestParams::TObjectStoreAddParams:
15686     case RequestParams::TObjectStorePutParams:
15687       actor = new ObjectStoreAddOrPutRequestOp(this, aParams);
15688       break;
15689 
15690     case RequestParams::TObjectStoreGetParams:
15691       actor =
15692         new ObjectStoreGetRequestOp(this, aParams, /* aGetAll */ false);
15693       break;
15694 
15695     case RequestParams::TObjectStoreGetAllParams:
15696       actor =
15697         new ObjectStoreGetRequestOp(this, aParams, /* aGetAll */ true);
15698       break;
15699 
15700     case RequestParams::TObjectStoreGetKeyParams:
15701       actor =
15702         new ObjectStoreGetKeyRequestOp(this, aParams, /* aGetAll */ false);
15703       break;
15704 
15705     case RequestParams::TObjectStoreGetAllKeysParams:
15706       actor =
15707         new ObjectStoreGetKeyRequestOp(this, aParams, /* aGetAll */ true);
15708       break;
15709 
15710     case RequestParams::TObjectStoreDeleteParams:
15711       actor =
15712         new ObjectStoreDeleteRequestOp(this,
15713                                        aParams.get_ObjectStoreDeleteParams());
15714       break;
15715 
15716     case RequestParams::TObjectStoreClearParams:
15717       actor =
15718         new ObjectStoreClearRequestOp(this,
15719                                       aParams.get_ObjectStoreClearParams());
15720       break;
15721 
15722     case RequestParams::TObjectStoreCountParams:
15723       actor =
15724         new ObjectStoreCountRequestOp(this,
15725                                       aParams.get_ObjectStoreCountParams());
15726       break;
15727 
15728     case RequestParams::TIndexGetParams:
15729       actor = new IndexGetRequestOp(this, aParams, /* aGetAll */ false);
15730       break;
15731 
15732     case RequestParams::TIndexGetKeyParams:
15733       actor = new IndexGetKeyRequestOp(this, aParams, /* aGetAll */ false);
15734       break;
15735 
15736     case RequestParams::TIndexGetAllParams:
15737       actor = new IndexGetRequestOp(this, aParams, /* aGetAll */ true);
15738       break;
15739 
15740     case RequestParams::TIndexGetAllKeysParams:
15741       actor = new IndexGetKeyRequestOp(this, aParams, /* aGetAll */ true);
15742       break;
15743 
15744     case RequestParams::TIndexCountParams:
15745       actor = new IndexCountRequestOp(this, aParams);
15746       break;
15747 
15748     default:
15749       MOZ_CRASH("Should never get here!");
15750   }
15751 
15752   MOZ_ASSERT(actor);
15753 
15754   // Transfer ownership to IPDL.
15755   return actor.forget().take();
15756 }
15757 
15758 bool
StartRequest(PBackgroundIDBRequestParent * aActor)15759 TransactionBase::StartRequest(PBackgroundIDBRequestParent* aActor)
15760 {
15761   AssertIsOnBackgroundThread();
15762   MOZ_ASSERT(aActor);
15763 
15764   auto* op = static_cast<NormalTransactionOp*>(aActor);
15765 
15766   if (NS_WARN_IF(!op->Init(this))) {
15767     op->Cleanup();
15768     return false;
15769   }
15770 
15771   op->DispatchToConnectionPool();
15772   return true;
15773 }
15774 
15775 bool
DeallocRequest(PBackgroundIDBRequestParent * aActor)15776 TransactionBase::DeallocRequest(PBackgroundIDBRequestParent* aActor)
15777 {
15778   AssertIsOnBackgroundThread();
15779   MOZ_ASSERT(aActor);
15780 
15781   // Transfer ownership back from IPDL.
15782   RefPtr<NormalTransactionOp> actor =
15783     dont_AddRef(static_cast<NormalTransactionOp*>(aActor));
15784   return true;
15785 }
15786 
15787 PBackgroundIDBCursorParent*
AllocCursor(const OpenCursorParams & aParams,bool aTrustParams)15788 TransactionBase::AllocCursor(const OpenCursorParams& aParams, bool aTrustParams)
15789 {
15790   AssertIsOnBackgroundThread();
15791   MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
15792 
15793 #ifdef DEBUG
15794   // Always verify parameters in DEBUG builds!
15795   aTrustParams = false;
15796 #endif
15797 
15798   OpenCursorParams::Type type = aParams.type();
15799   RefPtr<FullObjectStoreMetadata> objectStoreMetadata;
15800   RefPtr<FullIndexMetadata> indexMetadata;
15801   Cursor::Direction direction;
15802 
15803   switch (type) {
15804     case OpenCursorParams::TObjectStoreOpenCursorParams: {
15805       const ObjectStoreOpenCursorParams& params =
15806         aParams.get_ObjectStoreOpenCursorParams();
15807       objectStoreMetadata = GetMetadataForObjectStoreId(params.objectStoreId());
15808       if (NS_WARN_IF(!objectStoreMetadata)) {
15809         ASSERT_UNLESS_FUZZING();
15810         return nullptr;
15811       }
15812       if (aTrustParams &&
15813           NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15814         ASSERT_UNLESS_FUZZING();
15815         return nullptr;
15816       }
15817       direction = params.direction();
15818       break;
15819     }
15820 
15821     case OpenCursorParams::TObjectStoreOpenKeyCursorParams: {
15822       const ObjectStoreOpenKeyCursorParams& params =
15823         aParams.get_ObjectStoreOpenKeyCursorParams();
15824       objectStoreMetadata = GetMetadataForObjectStoreId(params.objectStoreId());
15825       if (NS_WARN_IF(!objectStoreMetadata)) {
15826         ASSERT_UNLESS_FUZZING();
15827         return nullptr;
15828       }
15829       if (aTrustParams &&
15830           NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15831         ASSERT_UNLESS_FUZZING();
15832         return nullptr;
15833       }
15834       direction = params.direction();
15835       break;
15836     }
15837 
15838     case OpenCursorParams::TIndexOpenCursorParams: {
15839       const IndexOpenCursorParams& params = aParams.get_IndexOpenCursorParams();
15840       objectStoreMetadata = GetMetadataForObjectStoreId(params.objectStoreId());
15841       if (NS_WARN_IF(!objectStoreMetadata)) {
15842         ASSERT_UNLESS_FUZZING();
15843         return nullptr;
15844       }
15845       indexMetadata =
15846         GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15847       if (NS_WARN_IF(!indexMetadata)) {
15848         ASSERT_UNLESS_FUZZING();
15849         return nullptr;
15850       }
15851       if (aTrustParams &&
15852           NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15853         ASSERT_UNLESS_FUZZING();
15854         return nullptr;
15855       }
15856       direction = params.direction();
15857       break;
15858     }
15859 
15860     case OpenCursorParams::TIndexOpenKeyCursorParams: {
15861       const IndexOpenKeyCursorParams& params =
15862         aParams.get_IndexOpenKeyCursorParams();
15863       objectStoreMetadata = GetMetadataForObjectStoreId(params.objectStoreId());
15864       if (NS_WARN_IF(!objectStoreMetadata)) {
15865         ASSERT_UNLESS_FUZZING();
15866         return nullptr;
15867       }
15868       indexMetadata =
15869         GetMetadataForIndexId(objectStoreMetadata, params.indexId());
15870       if (NS_WARN_IF(!indexMetadata)) {
15871         ASSERT_UNLESS_FUZZING();
15872         return nullptr;
15873       }
15874       if (aTrustParams &&
15875           NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
15876         ASSERT_UNLESS_FUZZING();
15877         return nullptr;
15878       }
15879       direction = params.direction();
15880       break;
15881     }
15882 
15883     default:
15884       MOZ_CRASH("Should never get here!");
15885   }
15886 
15887   if (NS_WARN_IF(mCommitOrAbortReceived)) {
15888     ASSERT_UNLESS_FUZZING();
15889     return nullptr;
15890   }
15891 
15892   RefPtr<Cursor> actor =
15893     new Cursor(this, type, objectStoreMetadata, indexMetadata, direction);
15894 
15895   // Transfer ownership to IPDL.
15896   return actor.forget().take();
15897 }
15898 
15899 bool
StartCursor(PBackgroundIDBCursorParent * aActor,const OpenCursorParams & aParams)15900 TransactionBase::StartCursor(PBackgroundIDBCursorParent* aActor,
15901                              const OpenCursorParams& aParams)
15902 {
15903   AssertIsOnBackgroundThread();
15904   MOZ_ASSERT(aActor);
15905   MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
15906 
15907   auto* op = static_cast<Cursor*>(aActor);
15908 
15909   if (NS_WARN_IF(!op->Start(aParams))) {
15910     return false;
15911   }
15912 
15913   return true;
15914 }
15915 
15916 bool
DeallocCursor(PBackgroundIDBCursorParent * aActor)15917 TransactionBase::DeallocCursor(PBackgroundIDBCursorParent* aActor)
15918 {
15919   AssertIsOnBackgroundThread();
15920   MOZ_ASSERT(aActor);
15921 
15922   // Transfer ownership back from IPDL.
15923   RefPtr<Cursor> actor = dont_AddRef(static_cast<Cursor*>(aActor));
15924   return true;
15925 }
15926 
15927 /*******************************************************************************
15928  * NormalTransaction
15929  ******************************************************************************/
15930 
NormalTransaction(Database * aDatabase,TransactionBase::Mode aMode,nsTArray<RefPtr<FullObjectStoreMetadata>> & aObjectStores)15931 NormalTransaction::NormalTransaction(
15932                      Database* aDatabase,
15933                      TransactionBase::Mode aMode,
15934                      nsTArray<RefPtr<FullObjectStoreMetadata>>& aObjectStores)
15935   : TransactionBase(aDatabase, aMode)
15936 {
15937   AssertIsOnBackgroundThread();
15938   MOZ_ASSERT(!aObjectStores.IsEmpty());
15939 
15940   mObjectStores.SwapElements(aObjectStores);
15941 }
15942 
15943 bool
IsSameProcessActor()15944 NormalTransaction::IsSameProcessActor()
15945 {
15946   AssertIsOnBackgroundThread();
15947 
15948   PBackgroundParent* actor = Manager()->Manager()->Manager();
15949   MOZ_ASSERT(actor);
15950 
15951   return !BackgroundParent::IsOtherProcessActor(actor);
15952 }
15953 
15954 void
SendCompleteNotification(nsresult aResult)15955 NormalTransaction::SendCompleteNotification(nsresult aResult)
15956 {
15957   AssertIsOnBackgroundThread();
15958 
15959   if (!IsActorDestroyed()) {
15960     Unused << SendComplete(aResult);
15961   }
15962 }
15963 
15964 void
ActorDestroy(ActorDestroyReason aWhy)15965 NormalTransaction::ActorDestroy(ActorDestroyReason aWhy)
15966 {
15967   AssertIsOnBackgroundThread();
15968 
15969   NoteActorDestroyed();
15970 
15971   if (!mCommittedOrAborted) {
15972     if (NS_SUCCEEDED(mResultCode)) {
15973       IDB_REPORT_INTERNAL_ERR();
15974       mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
15975     }
15976 
15977     mForceAborted = true;
15978 
15979     MaybeCommitOrAbort();
15980   }
15981 }
15982 
15983 bool
RecvDeleteMe()15984 NormalTransaction::RecvDeleteMe()
15985 {
15986   AssertIsOnBackgroundThread();
15987   MOZ_ASSERT(!IsActorDestroyed());
15988 
15989   return PBackgroundIDBTransactionParent::Send__delete__(this);
15990 }
15991 
15992 bool
RecvCommit()15993 NormalTransaction::RecvCommit()
15994 {
15995   AssertIsOnBackgroundThread();
15996 
15997   return TransactionBase::RecvCommit();
15998 }
15999 
16000 bool
RecvAbort(const nsresult & aResultCode)16001 NormalTransaction::RecvAbort(const nsresult& aResultCode)
16002 {
16003   AssertIsOnBackgroundThread();
16004 
16005   return TransactionBase::RecvAbort(aResultCode);
16006 }
16007 
16008 PBackgroundIDBRequestParent*
AllocPBackgroundIDBRequestParent(const RequestParams & aParams)16009 NormalTransaction::AllocPBackgroundIDBRequestParent(
16010                                                    const RequestParams& aParams)
16011 {
16012   AssertIsOnBackgroundThread();
16013   MOZ_ASSERT(aParams.type() != RequestParams::T__None);
16014 
16015   return AllocRequest(aParams, IsSameProcessActor());
16016 }
16017 
16018 bool
RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent * aActor,const RequestParams & aParams)16019 NormalTransaction::RecvPBackgroundIDBRequestConstructor(
16020                                             PBackgroundIDBRequestParent* aActor,
16021                                             const RequestParams& aParams)
16022 {
16023   AssertIsOnBackgroundThread();
16024   MOZ_ASSERT(aActor);
16025   MOZ_ASSERT(aParams.type() != RequestParams::T__None);
16026 
16027   return StartRequest(aActor);
16028 }
16029 
16030 bool
DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent * aActor)16031 NormalTransaction::DeallocPBackgroundIDBRequestParent(
16032                                             PBackgroundIDBRequestParent* aActor)
16033 {
16034   AssertIsOnBackgroundThread();
16035   MOZ_ASSERT(aActor);
16036 
16037   return DeallocRequest(aActor);
16038 }
16039 
16040 PBackgroundIDBCursorParent*
AllocPBackgroundIDBCursorParent(const OpenCursorParams & aParams)16041 NormalTransaction::AllocPBackgroundIDBCursorParent(
16042                                                 const OpenCursorParams& aParams)
16043 {
16044   AssertIsOnBackgroundThread();
16045 
16046   return AllocCursor(aParams, IsSameProcessActor());
16047 }
16048 
16049 bool
RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent * aActor,const OpenCursorParams & aParams)16050 NormalTransaction::RecvPBackgroundIDBCursorConstructor(
16051                                              PBackgroundIDBCursorParent* aActor,
16052                                              const OpenCursorParams& aParams)
16053 {
16054   AssertIsOnBackgroundThread();
16055   MOZ_ASSERT(aActor);
16056   MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
16057 
16058   return StartCursor(aActor, aParams);
16059 }
16060 
16061 bool
DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent * aActor)16062 NormalTransaction::DeallocPBackgroundIDBCursorParent(
16063                                              PBackgroundIDBCursorParent* aActor)
16064 {
16065   AssertIsOnBackgroundThread();
16066   MOZ_ASSERT(aActor);
16067 
16068   return DeallocCursor(aActor);
16069 }
16070 
16071 /*******************************************************************************
16072  * VersionChangeTransaction
16073  ******************************************************************************/
16074 
VersionChangeTransaction(OpenDatabaseOp * aOpenDatabaseOp)16075 VersionChangeTransaction::VersionChangeTransaction(
16076                                                 OpenDatabaseOp* aOpenDatabaseOp)
16077   : TransactionBase(aOpenDatabaseOp->mDatabase,
16078                     IDBTransaction::VERSION_CHANGE)
16079   , mOpenDatabaseOp(aOpenDatabaseOp)
16080   , mActorWasAlive(false)
16081 {
16082   AssertIsOnBackgroundThread();
16083   MOZ_ASSERT(aOpenDatabaseOp);
16084 }
16085 
~VersionChangeTransaction()16086 VersionChangeTransaction::~VersionChangeTransaction()
16087 {
16088 #ifdef DEBUG
16089   // Silence the base class' destructor assertion if we never made this actor
16090   // live.
16091   FakeActorDestroyed();
16092 #endif
16093 }
16094 
16095 bool
IsSameProcessActor()16096 VersionChangeTransaction::IsSameProcessActor()
16097 {
16098   AssertIsOnBackgroundThread();
16099 
16100   PBackgroundParent* actor = Manager()->Manager()->Manager();
16101   MOZ_ASSERT(actor);
16102 
16103   return !BackgroundParent::IsOtherProcessActor(actor);
16104 }
16105 
16106 void
SetActorAlive()16107 VersionChangeTransaction::SetActorAlive()
16108 {
16109   AssertIsOnBackgroundThread();
16110   MOZ_ASSERT(!mActorWasAlive);
16111   MOZ_ASSERT(!IsActorDestroyed());
16112 
16113   mActorWasAlive = true;
16114 
16115   // This reference will be absorbed by IPDL and released when the actor is
16116   // destroyed.
16117   AddRef();
16118 }
16119 
16120 bool
CopyDatabaseMetadata()16121 VersionChangeTransaction::CopyDatabaseMetadata()
16122 {
16123   AssertIsOnBackgroundThread();
16124   MOZ_ASSERT(!mOldMetadata);
16125 
16126   const RefPtr<FullDatabaseMetadata> origMetadata =
16127     GetDatabase()->Metadata();
16128   MOZ_ASSERT(origMetadata);
16129 
16130   RefPtr<FullDatabaseMetadata> newMetadata = origMetadata->Duplicate();
16131   if (NS_WARN_IF(!newMetadata)) {
16132     return false;
16133   }
16134 
16135   // Replace the live metadata with the new mutable copy.
16136   DatabaseActorInfo* info;
16137   MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(origMetadata->mDatabaseId,
16138                                               &info));
16139   MOZ_ASSERT(!info->mLiveDatabases.IsEmpty());
16140   MOZ_ASSERT(info->mMetadata == origMetadata);
16141 
16142   mOldMetadata = info->mMetadata.forget();
16143   info->mMetadata.swap(newMetadata);
16144 
16145   // Replace metadata pointers for all live databases.
16146   for (uint32_t count = info->mLiveDatabases.Length(), index = 0;
16147        index < count;
16148        index++) {
16149     info->mLiveDatabases[index]->mMetadata = info->mMetadata;
16150   }
16151 
16152   return true;
16153 }
16154 
16155 void
UpdateMetadata(nsresult aResult)16156 VersionChangeTransaction::UpdateMetadata(nsresult aResult)
16157 {
16158   AssertIsOnBackgroundThread();
16159   MOZ_ASSERT(GetDatabase());
16160   MOZ_ASSERT(mOpenDatabaseOp);
16161   MOZ_ASSERT(!!mActorWasAlive == !!mOpenDatabaseOp->mDatabase);
16162   MOZ_ASSERT_IF(mActorWasAlive, !mOpenDatabaseOp->mDatabaseId.IsEmpty());
16163 
16164   if (IsActorDestroyed() || !mActorWasAlive) {
16165     return;
16166   }
16167 
16168   RefPtr<FullDatabaseMetadata> oldMetadata;
16169   mOldMetadata.swap(oldMetadata);
16170 
16171   DatabaseActorInfo* info;
16172   if (!gLiveDatabaseHashtable->Get(oldMetadata->mDatabaseId, &info)) {
16173     return;
16174   }
16175 
16176   MOZ_ASSERT(!info->mLiveDatabases.IsEmpty());
16177 
16178   if (NS_SUCCEEDED(aResult)) {
16179     // Remove all deleted objectStores and indexes, then mark immutable.
16180     for (auto objectStoreIter = info->mMetadata->mObjectStores.Iter();
16181          !objectStoreIter.Done();
16182          objectStoreIter.Next()) {
16183       MOZ_ASSERT(objectStoreIter.Key());
16184       RefPtr<FullObjectStoreMetadata>& metadata = objectStoreIter.Data();
16185       MOZ_ASSERT(metadata);
16186 
16187       if (metadata->mDeleted) {
16188         objectStoreIter.Remove();
16189         continue;
16190       }
16191 
16192       for (auto indexIter = metadata->mIndexes.Iter();
16193            !indexIter.Done();
16194            indexIter.Next()) {
16195         MOZ_ASSERT(indexIter.Key());
16196         RefPtr<FullIndexMetadata>& index = indexIter.Data();
16197         MOZ_ASSERT(index);
16198 
16199         if (index->mDeleted) {
16200           indexIter.Remove();
16201         }
16202       }
16203 #ifdef DEBUG
16204       metadata->mIndexes.MarkImmutable();
16205 #endif
16206     }
16207 #ifdef DEBUG
16208     info->mMetadata->mObjectStores.MarkImmutable();
16209 #endif
16210   } else {
16211     // Replace metadata pointers for all live databases.
16212     info->mMetadata = oldMetadata.forget();
16213 
16214     for (uint32_t count = info->mLiveDatabases.Length(), index = 0;
16215          index < count;
16216          index++) {
16217       info->mLiveDatabases[index]->mMetadata = info->mMetadata;
16218     }
16219   }
16220 }
16221 
16222 void
SendCompleteNotification(nsresult aResult)16223 VersionChangeTransaction::SendCompleteNotification(nsresult aResult)
16224 {
16225   AssertIsOnBackgroundThread();
16226   MOZ_ASSERT(mOpenDatabaseOp);
16227   MOZ_ASSERT_IF(!mActorWasAlive, NS_FAILED(mOpenDatabaseOp->mResultCode));
16228   MOZ_ASSERT_IF(!mActorWasAlive,
16229                 mOpenDatabaseOp->mState > OpenDatabaseOp::State::SendingResults);
16230 
16231   RefPtr<OpenDatabaseOp> openDatabaseOp;
16232   mOpenDatabaseOp.swap(openDatabaseOp);
16233 
16234   if (!mActorWasAlive) {
16235     return;
16236   }
16237 
16238   if (NS_FAILED(aResult) && NS_SUCCEEDED(openDatabaseOp->mResultCode)) {
16239     // 3.3.1 Opening a database:
16240     // "If the upgrade transaction was aborted, run the steps for closing a
16241     //  database connection with connection, create and return a new AbortError
16242     //  exception and abort these steps."
16243     openDatabaseOp->mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
16244   }
16245 
16246   openDatabaseOp->mState = OpenDatabaseOp::State::SendingResults;
16247 
16248   if (!IsActorDestroyed()) {
16249     Unused << SendComplete(aResult);
16250   }
16251 
16252   MOZ_ALWAYS_SUCCEEDS(openDatabaseOp->Run());
16253 }
16254 
16255 void
ActorDestroy(ActorDestroyReason aWhy)16256 VersionChangeTransaction::ActorDestroy(ActorDestroyReason aWhy)
16257 {
16258   AssertIsOnBackgroundThread();
16259 
16260   NoteActorDestroyed();
16261 
16262   if (!mCommittedOrAborted) {
16263     if (NS_SUCCEEDED(mResultCode)) {
16264       IDB_REPORT_INTERNAL_ERR();
16265       mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
16266     }
16267 
16268     mForceAborted = true;
16269 
16270     MaybeCommitOrAbort();
16271   }
16272 }
16273 
16274 bool
RecvDeleteMe()16275 VersionChangeTransaction::RecvDeleteMe()
16276 {
16277   AssertIsOnBackgroundThread();
16278   MOZ_ASSERT(!IsActorDestroyed());
16279 
16280   return PBackgroundIDBVersionChangeTransactionParent::Send__delete__(this);
16281 }
16282 
16283 bool
RecvCommit()16284 VersionChangeTransaction::RecvCommit()
16285 {
16286   AssertIsOnBackgroundThread();
16287 
16288   return TransactionBase::RecvCommit();
16289 }
16290 
16291 bool
RecvAbort(const nsresult & aResultCode)16292 VersionChangeTransaction::RecvAbort(const nsresult& aResultCode)
16293 {
16294   AssertIsOnBackgroundThread();
16295 
16296   return TransactionBase::RecvAbort(aResultCode);
16297 }
16298 
16299 bool
RecvCreateObjectStore(const ObjectStoreMetadata & aMetadata)16300 VersionChangeTransaction::RecvCreateObjectStore(
16301                                            const ObjectStoreMetadata& aMetadata)
16302 {
16303   AssertIsOnBackgroundThread();
16304 
16305   if (NS_WARN_IF(!aMetadata.id())) {
16306     ASSERT_UNLESS_FUZZING();
16307     return false;
16308   }
16309 
16310   const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
16311   MOZ_ASSERT(dbMetadata);
16312 
16313   if (NS_WARN_IF(aMetadata.id() != dbMetadata->mNextObjectStoreId)) {
16314     ASSERT_UNLESS_FUZZING();
16315     return false;
16316   }
16317 
16318   auto* foundMetadata =
16319     MetadataNameOrIdMatcher<FullObjectStoreMetadata>::Match(
16320       dbMetadata->mObjectStores, aMetadata.id(), aMetadata.name());
16321 
16322   if (NS_WARN_IF(foundMetadata)) {
16323     ASSERT_UNLESS_FUZZING();
16324     return false;
16325   }
16326 
16327   if (NS_WARN_IF(mCommitOrAbortReceived)) {
16328     ASSERT_UNLESS_FUZZING();
16329     return false;
16330   }
16331 
16332   RefPtr<FullObjectStoreMetadata> newMetadata = new FullObjectStoreMetadata();
16333   newMetadata->mCommonMetadata = aMetadata;
16334   newMetadata->mNextAutoIncrementId = aMetadata.autoIncrement() ? 1 : 0;
16335   newMetadata->mCommittedAutoIncrementId = newMetadata->mNextAutoIncrementId;
16336 
16337   if (NS_WARN_IF(!dbMetadata->mObjectStores.Put(aMetadata.id(), newMetadata,
16338                                                 fallible))) {
16339     return false;
16340   }
16341 
16342   dbMetadata->mNextObjectStoreId++;
16343 
16344   RefPtr<CreateObjectStoreOp> op = new CreateObjectStoreOp(this, aMetadata);
16345 
16346   if (NS_WARN_IF(!op->Init(this))) {
16347     op->Cleanup();
16348     return false;
16349   }
16350 
16351   op->DispatchToConnectionPool();
16352 
16353   return true;
16354 }
16355 
16356 bool
RecvDeleteObjectStore(const int64_t & aObjectStoreId)16357 VersionChangeTransaction::RecvDeleteObjectStore(const int64_t& aObjectStoreId)
16358 {
16359   AssertIsOnBackgroundThread();
16360 
16361   if (NS_WARN_IF(!aObjectStoreId)) {
16362     ASSERT_UNLESS_FUZZING();
16363     return false;
16364   }
16365 
16366   const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
16367   MOZ_ASSERT(dbMetadata);
16368   MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0);
16369 
16370   if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) {
16371     ASSERT_UNLESS_FUZZING();
16372     return false;
16373   }
16374 
16375   RefPtr<FullObjectStoreMetadata> foundMetadata =
16376     GetMetadataForObjectStoreId(aObjectStoreId);
16377 
16378   if (NS_WARN_IF(!foundMetadata)) {
16379     ASSERT_UNLESS_FUZZING();
16380     return false;
16381   }
16382 
16383   if (NS_WARN_IF(mCommitOrAbortReceived)) {
16384     ASSERT_UNLESS_FUZZING();
16385     return false;
16386   }
16387 
16388   foundMetadata->mDeleted = true;
16389 
16390   bool isLastObjectStore = true;
16391   DebugOnly<bool> foundTargetId = false;
16392   for (auto iter = dbMetadata->mObjectStores.Iter();
16393        !iter.Done();
16394        iter.Next()) {
16395     if (uint64_t(aObjectStoreId) == iter.Key()) {
16396       foundTargetId = true;
16397     } else if (!iter.UserData()->mDeleted) {
16398       isLastObjectStore = false;
16399       break;
16400     }
16401   }
16402   MOZ_ASSERT_IF(isLastObjectStore, foundTargetId);
16403 
16404   RefPtr<DeleteObjectStoreOp> op =
16405     new DeleteObjectStoreOp(this, foundMetadata, isLastObjectStore);
16406 
16407   if (NS_WARN_IF(!op->Init(this))) {
16408     op->Cleanup();
16409     return false;
16410   }
16411 
16412   op->DispatchToConnectionPool();
16413 
16414   return true;
16415 }
16416 
16417 bool
RecvRenameObjectStore(const int64_t & aObjectStoreId,const nsString & aName)16418 VersionChangeTransaction::RecvRenameObjectStore(const int64_t& aObjectStoreId,
16419                                                 const nsString& aName)
16420 {
16421   AssertIsOnBackgroundThread();
16422 
16423   if (NS_WARN_IF(!aObjectStoreId)) {
16424     ASSERT_UNLESS_FUZZING();
16425     return false;
16426   }
16427 
16428   const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
16429   MOZ_ASSERT(dbMetadata);
16430   MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0);
16431 
16432   if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) {
16433     ASSERT_UNLESS_FUZZING();
16434     return false;
16435   }
16436 
16437   RefPtr<FullObjectStoreMetadata> foundMetadata =
16438     GetMetadataForObjectStoreId(aObjectStoreId);
16439 
16440   if (NS_WARN_IF(!foundMetadata)) {
16441     ASSERT_UNLESS_FUZZING();
16442     return false;
16443   }
16444 
16445   if (NS_WARN_IF(mCommitOrAbortReceived)) {
16446     ASSERT_UNLESS_FUZZING();
16447     return false;
16448   }
16449 
16450   foundMetadata->mCommonMetadata.name() = aName;
16451 
16452   RefPtr<RenameObjectStoreOp> renameOp =
16453     new RenameObjectStoreOp(this, foundMetadata);
16454 
16455   if (NS_WARN_IF(!renameOp->Init(this))) {
16456     renameOp->Cleanup();
16457     return false;
16458   }
16459 
16460   renameOp->DispatchToConnectionPool();
16461 
16462   return true;
16463 }
16464 
16465 bool
RecvCreateIndex(const int64_t & aObjectStoreId,const IndexMetadata & aMetadata)16466 VersionChangeTransaction::RecvCreateIndex(const int64_t& aObjectStoreId,
16467                                           const IndexMetadata& aMetadata)
16468 {
16469   AssertIsOnBackgroundThread();
16470 
16471   if (NS_WARN_IF(!aObjectStoreId)) {
16472     ASSERT_UNLESS_FUZZING();
16473     return false;
16474   }
16475 
16476   if (NS_WARN_IF(!aMetadata.id())) {
16477     ASSERT_UNLESS_FUZZING();
16478     return false;
16479   }
16480 
16481   const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
16482   MOZ_ASSERT(dbMetadata);
16483 
16484   if (NS_WARN_IF(aMetadata.id() != dbMetadata->mNextIndexId)) {
16485     ASSERT_UNLESS_FUZZING();
16486     return false;
16487   }
16488 
16489   RefPtr<FullObjectStoreMetadata> foundObjectStoreMetadata =
16490     GetMetadataForObjectStoreId(aObjectStoreId);
16491 
16492   if (NS_WARN_IF(!foundObjectStoreMetadata)) {
16493     ASSERT_UNLESS_FUZZING();
16494     return false;
16495   }
16496 
16497   RefPtr<FullIndexMetadata> foundIndexMetadata =
16498     MetadataNameOrIdMatcher<FullIndexMetadata>::Match(
16499       foundObjectStoreMetadata->mIndexes, aMetadata.id(), aMetadata.name());
16500 
16501   if (NS_WARN_IF(foundIndexMetadata)) {
16502     ASSERT_UNLESS_FUZZING();
16503     return false;
16504   }
16505 
16506   if (NS_WARN_IF(mCommitOrAbortReceived)) {
16507     ASSERT_UNLESS_FUZZING();
16508     return false;
16509   }
16510 
16511   RefPtr<FullIndexMetadata> newMetadata = new FullIndexMetadata();
16512   newMetadata->mCommonMetadata = aMetadata;
16513 
16514   if (NS_WARN_IF(!foundObjectStoreMetadata->mIndexes.Put(aMetadata.id(),
16515                                                          newMetadata,
16516                                                          fallible))) {
16517     return false;
16518   }
16519 
16520   dbMetadata->mNextIndexId++;
16521 
16522   RefPtr<CreateIndexOp> op =
16523     new CreateIndexOp(this, aObjectStoreId, aMetadata);
16524 
16525   if (NS_WARN_IF(!op->Init(this))) {
16526     op->Cleanup();
16527     return false;
16528   }
16529 
16530   op->DispatchToConnectionPool();
16531 
16532   return true;
16533 }
16534 
16535 bool
RecvDeleteIndex(const int64_t & aObjectStoreId,const int64_t & aIndexId)16536 VersionChangeTransaction::RecvDeleteIndex(const int64_t& aObjectStoreId,
16537                                           const int64_t& aIndexId)
16538 {
16539   AssertIsOnBackgroundThread();
16540 
16541   if (NS_WARN_IF(!aObjectStoreId)) {
16542     ASSERT_UNLESS_FUZZING();
16543     return false;
16544   }
16545 
16546   if (NS_WARN_IF(!aIndexId)) {
16547     ASSERT_UNLESS_FUZZING();
16548     return false;
16549   }
16550 
16551   const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
16552   MOZ_ASSERT(dbMetadata);
16553   MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0);
16554   MOZ_ASSERT(dbMetadata->mNextIndexId > 0);
16555 
16556   if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) {
16557     ASSERT_UNLESS_FUZZING();
16558     return false;
16559   }
16560 
16561   if (NS_WARN_IF(aIndexId >= dbMetadata->mNextIndexId)) {
16562     ASSERT_UNLESS_FUZZING();
16563     return false;
16564   }
16565 
16566   RefPtr<FullObjectStoreMetadata> foundObjectStoreMetadata =
16567     GetMetadataForObjectStoreId(aObjectStoreId);
16568 
16569   if (NS_WARN_IF(!foundObjectStoreMetadata)) {
16570     ASSERT_UNLESS_FUZZING();
16571     return false;
16572   }
16573 
16574   RefPtr<FullIndexMetadata> foundIndexMetadata =
16575     GetMetadataForIndexId(foundObjectStoreMetadata, aIndexId);
16576 
16577   if (NS_WARN_IF(!foundIndexMetadata)) {
16578     ASSERT_UNLESS_FUZZING();
16579     return false;
16580   }
16581 
16582   if (NS_WARN_IF(mCommitOrAbortReceived)) {
16583     ASSERT_UNLESS_FUZZING();
16584     return false;
16585   }
16586 
16587   foundIndexMetadata->mDeleted = true;
16588 
16589   bool isLastIndex = true;
16590   DebugOnly<bool> foundTargetId = false;
16591   for (auto iter = foundObjectStoreMetadata->mIndexes.ConstIter();
16592        !iter.Done();
16593        iter.Next()) {
16594     if (uint64_t(aIndexId) == iter.Key()) {
16595       foundTargetId = true;
16596     } else if (!iter.UserData()->mDeleted) {
16597       isLastIndex = false;
16598       break;
16599     }
16600   }
16601   MOZ_ASSERT_IF(isLastIndex, foundTargetId);
16602 
16603   RefPtr<DeleteIndexOp> op =
16604     new DeleteIndexOp(this,
16605                       aObjectStoreId,
16606                       aIndexId,
16607                       foundIndexMetadata->mCommonMetadata.unique(),
16608                       isLastIndex);
16609 
16610   if (NS_WARN_IF(!op->Init(this))) {
16611     op->Cleanup();
16612     return false;
16613   }
16614 
16615   op->DispatchToConnectionPool();
16616 
16617   return true;
16618 }
16619 
16620 bool
RecvRenameIndex(const int64_t & aObjectStoreId,const int64_t & aIndexId,const nsString & aName)16621 VersionChangeTransaction::RecvRenameIndex(const int64_t& aObjectStoreId,
16622                                           const int64_t& aIndexId,
16623                                           const nsString& aName)
16624 {
16625   AssertIsOnBackgroundThread();
16626 
16627   if (NS_WARN_IF(!aObjectStoreId)) {
16628     ASSERT_UNLESS_FUZZING();
16629     return false;
16630   }
16631 
16632   if (NS_WARN_IF(!aIndexId)) {
16633     ASSERT_UNLESS_FUZZING();
16634     return false;
16635   }
16636 
16637   const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
16638   MOZ_ASSERT(dbMetadata);
16639   MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0);
16640   MOZ_ASSERT(dbMetadata->mNextIndexId > 0);
16641 
16642   if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) {
16643     ASSERT_UNLESS_FUZZING();
16644     return false;
16645   }
16646 
16647   if (NS_WARN_IF(aIndexId >= dbMetadata->mNextIndexId)) {
16648     ASSERT_UNLESS_FUZZING();
16649     return false;
16650   }
16651 
16652   RefPtr<FullObjectStoreMetadata> foundObjectStoreMetadata =
16653     GetMetadataForObjectStoreId(aObjectStoreId);
16654 
16655   if (NS_WARN_IF(!foundObjectStoreMetadata)) {
16656     ASSERT_UNLESS_FUZZING();
16657     return false;
16658   }
16659 
16660   RefPtr<FullIndexMetadata> foundIndexMetadata =
16661     GetMetadataForIndexId(foundObjectStoreMetadata, aIndexId);
16662 
16663   if (NS_WARN_IF(!foundIndexMetadata)) {
16664     ASSERT_UNLESS_FUZZING();
16665     return false;
16666   }
16667 
16668   if (NS_WARN_IF(mCommitOrAbortReceived)) {
16669     ASSERT_UNLESS_FUZZING();
16670     return false;
16671   }
16672 
16673   foundIndexMetadata->mCommonMetadata.name() = aName;
16674 
16675   RefPtr<RenameIndexOp> renameOp =
16676     new RenameIndexOp(this, foundIndexMetadata, aObjectStoreId);
16677 
16678   if (NS_WARN_IF(!renameOp->Init(this))) {
16679     renameOp->Cleanup();
16680     return false;
16681   }
16682 
16683   renameOp->DispatchToConnectionPool();
16684 
16685   return true;
16686 }
16687 
16688 PBackgroundIDBRequestParent*
AllocPBackgroundIDBRequestParent(const RequestParams & aParams)16689 VersionChangeTransaction::AllocPBackgroundIDBRequestParent(
16690                                                    const RequestParams& aParams)
16691 {
16692   AssertIsOnBackgroundThread();
16693   MOZ_ASSERT(aParams.type() != RequestParams::T__None);
16694 
16695   return AllocRequest(aParams, IsSameProcessActor());
16696 }
16697 
16698 bool
RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent * aActor,const RequestParams & aParams)16699 VersionChangeTransaction::RecvPBackgroundIDBRequestConstructor(
16700                                             PBackgroundIDBRequestParent* aActor,
16701                                             const RequestParams& aParams)
16702 {
16703   AssertIsOnBackgroundThread();
16704   MOZ_ASSERT(aActor);
16705   MOZ_ASSERT(aParams.type() != RequestParams::T__None);
16706 
16707   return StartRequest(aActor);
16708 }
16709 
16710 bool
DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent * aActor)16711 VersionChangeTransaction::DeallocPBackgroundIDBRequestParent(
16712                                             PBackgroundIDBRequestParent* aActor)
16713 {
16714   AssertIsOnBackgroundThread();
16715   MOZ_ASSERT(aActor);
16716 
16717   return DeallocRequest(aActor);
16718 }
16719 
16720 PBackgroundIDBCursorParent*
AllocPBackgroundIDBCursorParent(const OpenCursorParams & aParams)16721 VersionChangeTransaction::AllocPBackgroundIDBCursorParent(
16722                                                 const OpenCursorParams& aParams)
16723 {
16724   AssertIsOnBackgroundThread();
16725 
16726   return AllocCursor(aParams, IsSameProcessActor());
16727 }
16728 
16729 bool
RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent * aActor,const OpenCursorParams & aParams)16730 VersionChangeTransaction::RecvPBackgroundIDBCursorConstructor(
16731                                              PBackgroundIDBCursorParent* aActor,
16732                                              const OpenCursorParams& aParams)
16733 {
16734   AssertIsOnBackgroundThread();
16735   MOZ_ASSERT(aActor);
16736   MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
16737 
16738   return StartCursor(aActor, aParams);
16739 }
16740 
16741 bool
DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent * aActor)16742 VersionChangeTransaction::DeallocPBackgroundIDBCursorParent(
16743                                              PBackgroundIDBCursorParent* aActor)
16744 {
16745   AssertIsOnBackgroundThread();
16746   MOZ_ASSERT(aActor);
16747 
16748   return DeallocCursor(aActor);
16749 }
16750 
16751 /*******************************************************************************
16752  * Cursor
16753  ******************************************************************************/
16754 
Cursor(TransactionBase * aTransaction,Type aType,FullObjectStoreMetadata * aObjectStoreMetadata,FullIndexMetadata * aIndexMetadata,Direction aDirection)16755 Cursor::Cursor(TransactionBase* aTransaction,
16756                Type aType,
16757                FullObjectStoreMetadata* aObjectStoreMetadata,
16758                FullIndexMetadata* aIndexMetadata,
16759                Direction aDirection)
16760   : mTransaction(aTransaction)
16761   , mBackgroundParent(nullptr)
16762   , mObjectStoreMetadata(aObjectStoreMetadata)
16763   , mIndexMetadata(aIndexMetadata)
16764   , mObjectStoreId(aObjectStoreMetadata->mCommonMetadata.id())
16765   , mIndexId(aIndexMetadata ? aIndexMetadata->mCommonMetadata.id() : 0)
16766   , mCurrentlyRunningOp(nullptr)
16767   , mType(aType)
16768   , mDirection(aDirection)
16769   , mUniqueIndex(aIndexMetadata ?
16770                  aIndexMetadata->mCommonMetadata.unique() :
16771                  false)
16772   , mIsSameProcessActor(!BackgroundParent::IsOtherProcessActor(
16773                            aTransaction->GetBackgroundParent()))
16774   , mActorDestroyed(false)
16775 {
16776   AssertIsOnBackgroundThread();
16777   MOZ_ASSERT(aTransaction);
16778   MOZ_ASSERT(aType != OpenCursorParams::T__None);
16779   MOZ_ASSERT(aObjectStoreMetadata);
16780   MOZ_ASSERT_IF(aType == OpenCursorParams::TIndexOpenCursorParams ||
16781                   aType == OpenCursorParams::TIndexOpenKeyCursorParams,
16782                 aIndexMetadata);
16783 
16784   if (mType == OpenCursorParams::TObjectStoreOpenCursorParams ||
16785       mType == OpenCursorParams::TIndexOpenCursorParams) {
16786     mDatabase = aTransaction->GetDatabase();
16787     MOZ_ASSERT(mDatabase);
16788 
16789     mFileManager = mDatabase->GetFileManager();
16790     MOZ_ASSERT(mFileManager);
16791 
16792     mBackgroundParent = aTransaction->GetBackgroundParent();
16793     MOZ_ASSERT(mBackgroundParent);
16794   }
16795 
16796   if (aIndexMetadata) {
16797     mLocale = aIndexMetadata->mCommonMetadata.locale();
16798   }
16799 
16800   static_assert(OpenCursorParams::T__None == 0 &&
16801                   OpenCursorParams::T__Last == 4,
16802                 "Lots of code here assumes only four types of cursors!");
16803 }
16804 
16805 bool
VerifyRequestParams(const CursorRequestParams & aParams) const16806 Cursor::VerifyRequestParams(const CursorRequestParams& aParams) const
16807 {
16808   AssertIsOnBackgroundThread();
16809   MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None);
16810   MOZ_ASSERT(mObjectStoreMetadata);
16811   MOZ_ASSERT_IF(mType == OpenCursorParams::TIndexOpenCursorParams ||
16812                   mType == OpenCursorParams::TIndexOpenKeyCursorParams,
16813                 mIndexMetadata);
16814 
16815 #ifdef DEBUG
16816   {
16817     RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
16818       mTransaction->GetMetadataForObjectStoreId(mObjectStoreId);
16819     if (objectStoreMetadata) {
16820       MOZ_ASSERT(objectStoreMetadata == mObjectStoreMetadata);
16821     } else {
16822       MOZ_ASSERT(mObjectStoreMetadata->mDeleted);
16823     }
16824 
16825     if (objectStoreMetadata &&
16826         (mType == OpenCursorParams::TIndexOpenCursorParams ||
16827          mType == OpenCursorParams::TIndexOpenKeyCursorParams)) {
16828       RefPtr<FullIndexMetadata> indexMetadata =
16829         mTransaction->GetMetadataForIndexId(objectStoreMetadata, mIndexId);
16830       if (indexMetadata) {
16831         MOZ_ASSERT(indexMetadata == mIndexMetadata);
16832       } else {
16833         MOZ_ASSERT(mIndexMetadata->mDeleted);
16834       }
16835     }
16836   }
16837 #endif
16838 
16839   if (NS_WARN_IF(mObjectStoreMetadata->mDeleted) ||
16840       (mIndexMetadata && NS_WARN_IF(mIndexMetadata->mDeleted))) {
16841     ASSERT_UNLESS_FUZZING();
16842     return false;
16843   }
16844 
16845   const Key& sortKey = IsLocaleAware() ? mSortKey : mKey;
16846 
16847   switch (aParams.type()) {
16848     case CursorRequestParams::TContinueParams: {
16849       const Key& key = aParams.get_ContinueParams().key();
16850       if (!key.IsUnset()) {
16851         switch (mDirection) {
16852           case IDBCursor::NEXT:
16853           case IDBCursor::NEXT_UNIQUE:
16854             if (NS_WARN_IF(key <= sortKey)) {
16855               ASSERT_UNLESS_FUZZING();
16856               return false;
16857             }
16858             break;
16859 
16860           case IDBCursor::PREV:
16861           case IDBCursor::PREV_UNIQUE:
16862             if (NS_WARN_IF(key >= sortKey)) {
16863               ASSERT_UNLESS_FUZZING();
16864               return false;
16865             }
16866             break;
16867 
16868           default:
16869             MOZ_CRASH("Should never get here!");
16870         }
16871       }
16872       break;
16873     }
16874 
16875     case CursorRequestParams::TContinuePrimaryKeyParams: {
16876       const Key& key = aParams.get_ContinuePrimaryKeyParams().key();
16877       const Key& primaryKey = aParams.get_ContinuePrimaryKeyParams().primaryKey();
16878       MOZ_ASSERT(!key.IsUnset());
16879       MOZ_ASSERT(!primaryKey.IsUnset());
16880       switch (mDirection) {
16881         case IDBCursor::NEXT:
16882           if (NS_WARN_IF(key < sortKey ||
16883                          (key == sortKey && primaryKey <= mObjectKey))) {
16884             ASSERT_UNLESS_FUZZING();
16885             return false;
16886           }
16887           break;
16888 
16889         case IDBCursor::PREV:
16890           if (NS_WARN_IF(key > sortKey ||
16891                          (key == sortKey && primaryKey >= mObjectKey))) {
16892             ASSERT_UNLESS_FUZZING();
16893             return false;
16894           }
16895           break;
16896 
16897         default:
16898           MOZ_CRASH("Should never get here!");
16899       }
16900       break;
16901     }
16902 
16903     case CursorRequestParams::TAdvanceParams:
16904       if (NS_WARN_IF(!aParams.get_AdvanceParams().count())) {
16905         ASSERT_UNLESS_FUZZING();
16906         return false;
16907       }
16908       break;
16909 
16910     default:
16911       MOZ_CRASH("Should never get here!");
16912   }
16913 
16914   return true;
16915 }
16916 
16917 bool
Start(const OpenCursorParams & aParams)16918 Cursor::Start(const OpenCursorParams& aParams)
16919 {
16920   AssertIsOnBackgroundThread();
16921   MOZ_ASSERT(aParams.type() == mType);
16922   MOZ_ASSERT(!mActorDestroyed);
16923 
16924   if (NS_WARN_IF(mCurrentlyRunningOp)) {
16925     ASSERT_UNLESS_FUZZING();
16926     return false;
16927   }
16928 
16929   const OptionalKeyRange& optionalKeyRange =
16930     mType == OpenCursorParams::TObjectStoreOpenCursorParams ?
16931       aParams.get_ObjectStoreOpenCursorParams().optionalKeyRange() :
16932     mType == OpenCursorParams::TObjectStoreOpenKeyCursorParams ?
16933       aParams.get_ObjectStoreOpenKeyCursorParams().optionalKeyRange() :
16934     mType == OpenCursorParams::TIndexOpenCursorParams ?
16935       aParams.get_IndexOpenCursorParams().optionalKeyRange() :
16936       aParams.get_IndexOpenKeyCursorParams().optionalKeyRange();
16937 
16938   RefPtr<OpenOp> openOp = new OpenOp(this, optionalKeyRange);
16939 
16940   if (NS_WARN_IF(!openOp->Init(mTransaction))) {
16941     openOp->Cleanup();
16942     return false;
16943   }
16944 
16945   openOp->DispatchToConnectionPool();
16946   mCurrentlyRunningOp = openOp;
16947 
16948   return true;
16949 }
16950 
16951 void
SendResponseInternal(CursorResponse & aResponse,const nsTArray<FallibleTArray<StructuredCloneFile>> & aFiles)16952 Cursor::SendResponseInternal(
16953     CursorResponse& aResponse,
16954     const nsTArray<FallibleTArray<StructuredCloneFile>>& aFiles)
16955 {
16956   AssertIsOnBackgroundThread();
16957   MOZ_ASSERT(aResponse.type() != CursorResponse::T__None);
16958   MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult,
16959                 NS_FAILED(aResponse.get_nsresult()));
16960   MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult,
16961                 NS_ERROR_GET_MODULE(aResponse.get_nsresult()) ==
16962                   NS_ERROR_MODULE_DOM_INDEXEDDB);
16963   MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tvoid_t, mKey.IsUnset());
16964   MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tvoid_t,
16965                 mRangeKey.IsUnset());
16966   MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tvoid_t,
16967                 mObjectKey.IsUnset());
16968   MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult ||
16969                 aResponse.type() == CursorResponse::Tvoid_t ||
16970                 aResponse.type() ==
16971                   CursorResponse::TObjectStoreKeyCursorResponse ||
16972                 aResponse.type() == CursorResponse::TIndexKeyCursorResponse,
16973                 aFiles.IsEmpty());
16974   MOZ_ASSERT(!mActorDestroyed);
16975   MOZ_ASSERT(mCurrentlyRunningOp);
16976 
16977   for (size_t i = 0; i < aFiles.Length(); ++i) {
16978     const auto& files = aFiles[i];
16979     if (!files.IsEmpty()) {
16980       MOZ_ASSERT(aResponse.type() ==
16981                    CursorResponse::TArrayOfObjectStoreCursorResponse ||
16982                  aResponse.type() == CursorResponse::TIndexCursorResponse);
16983       MOZ_ASSERT(mDatabase);
16984       MOZ_ASSERT(mBackgroundParent);
16985 
16986       FallibleTArray<SerializedStructuredCloneFile> serializedFiles;
16987       nsresult rv = SerializeStructuredCloneFiles(mBackgroundParent,
16988                                                   mDatabase,
16989                                                   files,
16990                                                   /* aForPreprocess */ false,
16991                                                   serializedFiles);
16992       if (NS_WARN_IF(NS_FAILED(rv))) {
16993         aResponse = ClampResultCode(rv);
16994         break;
16995       }
16996 
16997       SerializedStructuredCloneReadInfo* serializedInfo = nullptr;
16998       switch (aResponse.type()) {
16999         case CursorResponse::TArrayOfObjectStoreCursorResponse: {
17000           auto& responses = aResponse.get_ArrayOfObjectStoreCursorResponse();
17001           MOZ_ASSERT(i < responses.Length());
17002           serializedInfo = &responses[i].cloneInfo();
17003           break;
17004         }
17005 
17006         case CursorResponse::TIndexCursorResponse:
17007           MOZ_ASSERT(i == 0);
17008           serializedInfo = &aResponse.get_IndexCursorResponse().cloneInfo();
17009           break;
17010 
17011         default:
17012           MOZ_CRASH("Should never get here!");
17013       }
17014 
17015       MOZ_ASSERT(serializedInfo);
17016       MOZ_ASSERT(serializedInfo->files().IsEmpty());
17017 
17018       serializedInfo->files().SwapElements(serializedFiles);
17019     }
17020   }
17021 
17022   // Work around the deleted function by casting to the base class.
17023   auto* base = static_cast<PBackgroundIDBCursorParent*>(this);
17024   if (!base->SendResponse(aResponse)) {
17025     NS_WARNING("Failed to send response!");
17026   }
17027 
17028   mCurrentlyRunningOp = nullptr;
17029 }
17030 
17031 void
ActorDestroy(ActorDestroyReason aWhy)17032 Cursor::ActorDestroy(ActorDestroyReason aWhy)
17033 {
17034   AssertIsOnBackgroundThread();
17035   MOZ_ASSERT(!mActorDestroyed);
17036 
17037   mActorDestroyed = true;
17038 
17039   if (mCurrentlyRunningOp) {
17040     mCurrentlyRunningOp->NoteActorDestroyed();
17041   }
17042 
17043   mBackgroundParent = nullptr;
17044 
17045   mObjectStoreMetadata = nullptr;
17046   mIndexMetadata = nullptr;
17047 }
17048 
17049 bool
RecvDeleteMe()17050 Cursor::RecvDeleteMe()
17051 {
17052   AssertIsOnBackgroundThread();
17053   MOZ_ASSERT(!mActorDestroyed);
17054 
17055   if (NS_WARN_IF(mCurrentlyRunningOp)) {
17056     ASSERT_UNLESS_FUZZING();
17057     return false;
17058   }
17059 
17060   return PBackgroundIDBCursorParent::Send__delete__(this);
17061 }
17062 
17063 bool
RecvContinue(const CursorRequestParams & aParams)17064 Cursor::RecvContinue(const CursorRequestParams& aParams)
17065 {
17066   AssertIsOnBackgroundThread();
17067   MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None);
17068   MOZ_ASSERT(!mActorDestroyed);
17069   MOZ_ASSERT(mObjectStoreMetadata);
17070   MOZ_ASSERT_IF(mType == OpenCursorParams::TIndexOpenCursorParams ||
17071                   mType == OpenCursorParams::TIndexOpenKeyCursorParams,
17072                 mIndexMetadata);
17073 
17074   const bool trustParams =
17075 #ifdef DEBUG
17076   // Always verify parameters in DEBUG builds!
17077     false
17078 #else
17079     mIsSameProcessActor
17080 #endif
17081     ;
17082 
17083   if (!trustParams && !VerifyRequestParams(aParams)) {
17084     ASSERT_UNLESS_FUZZING();
17085     return false;
17086   }
17087 
17088   if (NS_WARN_IF(mCurrentlyRunningOp)) {
17089     ASSERT_UNLESS_FUZZING();
17090     return false;
17091   }
17092 
17093   if (NS_WARN_IF(mTransaction->mCommitOrAbortReceived)) {
17094     ASSERT_UNLESS_FUZZING();
17095     return false;
17096   }
17097 
17098   RefPtr<ContinueOp> continueOp = new ContinueOp(this, aParams);
17099   if (NS_WARN_IF(!continueOp->Init(mTransaction))) {
17100     continueOp->Cleanup();
17101     return false;
17102   }
17103 
17104   continueOp->DispatchToConnectionPool();
17105   mCurrentlyRunningOp = continueOp;
17106 
17107   return true;
17108 }
17109 
17110 /*******************************************************************************
17111  * FileManager
17112  ******************************************************************************/
17113 
FileManager(PersistenceType aPersistenceType,const nsACString & aGroup,const nsACString & aOrigin,bool aIsApp,const nsAString & aDatabaseName,bool aEnforcingQuota)17114 FileManager::FileManager(PersistenceType aPersistenceType,
17115                          const nsACString& aGroup,
17116                          const nsACString& aOrigin,
17117                          bool aIsApp,
17118                          const nsAString& aDatabaseName,
17119                          bool aEnforcingQuota)
17120   : mPersistenceType(aPersistenceType)
17121   , mGroup(aGroup)
17122   , mOrigin(aOrigin)
17123   , mDatabaseName(aDatabaseName)
17124   , mLastFileId(0)
17125   , mIsApp(aIsApp)
17126   , mEnforcingQuota(aEnforcingQuota)
17127   , mInvalidated(false)
17128 { }
17129 
~FileManager()17130 FileManager::~FileManager()
17131 { }
17132 
17133 nsresult
Init(nsIFile * aDirectory,mozIStorageConnection * aConnection)17134 FileManager::Init(nsIFile* aDirectory,
17135                   mozIStorageConnection* aConnection)
17136 {
17137   AssertIsOnIOThread();
17138   MOZ_ASSERT(aDirectory);
17139   MOZ_ASSERT(aConnection);
17140 
17141   bool exists;
17142   nsresult rv = aDirectory->Exists(&exists);
17143   if (NS_WARN_IF(NS_FAILED(rv))) {
17144     return rv;
17145   }
17146 
17147   if (exists) {
17148     bool isDirectory;
17149     rv = aDirectory->IsDirectory(&isDirectory);
17150     if (NS_WARN_IF(NS_FAILED(rv))) {
17151       return rv;
17152     }
17153 
17154     if (NS_WARN_IF(!isDirectory)) {
17155       return NS_ERROR_FAILURE;
17156     }
17157   } else {
17158     rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
17159     if (NS_WARN_IF(NS_FAILED(rv))) {
17160       return rv;
17161     }
17162   }
17163 
17164   rv = aDirectory->GetPath(mDirectoryPath);
17165   if (NS_WARN_IF(NS_FAILED(rv))) {
17166     return rv;
17167   }
17168 
17169   nsCOMPtr<nsIFile> journalDirectory;
17170   rv = aDirectory->Clone(getter_AddRefs(journalDirectory));
17171   if (NS_WARN_IF(NS_FAILED(rv))) {
17172     return rv;
17173   }
17174 
17175   rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME));
17176   if (NS_WARN_IF(NS_FAILED(rv))) {
17177     return rv;
17178   }
17179 
17180   rv = journalDirectory->Exists(&exists);
17181   if (NS_WARN_IF(NS_FAILED(rv))) {
17182     return rv;
17183   }
17184 
17185   if (exists) {
17186     bool isDirectory;
17187     rv = journalDirectory->IsDirectory(&isDirectory);
17188     if (NS_WARN_IF(NS_FAILED(rv))) {
17189       return rv;
17190     }
17191 
17192     if (NS_WARN_IF(!isDirectory)) {
17193       return NS_ERROR_FAILURE;
17194     }
17195   }
17196 
17197   rv = journalDirectory->GetPath(mJournalDirectoryPath);
17198   if (NS_WARN_IF(NS_FAILED(rv))) {
17199     return rv;
17200   }
17201 
17202   nsCOMPtr<mozIStorageStatement> stmt;
17203   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
17204     "SELECT id, refcount "
17205     "FROM file"
17206   ), getter_AddRefs(stmt));
17207   if (NS_WARN_IF(NS_FAILED(rv))) {
17208     return rv;
17209   }
17210 
17211   bool hasResult;
17212   while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
17213     int64_t id;
17214     rv = stmt->GetInt64(0, &id);
17215     if (NS_WARN_IF(NS_FAILED(rv))) {
17216       return rv;
17217     }
17218 
17219     int32_t refcount;
17220     rv = stmt->GetInt32(1, &refcount);
17221     if (NS_WARN_IF(NS_FAILED(rv))) {
17222       return rv;
17223     }
17224 
17225     MOZ_ASSERT(refcount > 0);
17226 
17227     RefPtr<FileInfo> fileInfo = FileInfo::Create(this, id);
17228     fileInfo->mDBRefCnt = static_cast<nsrefcnt>(refcount);
17229 
17230     mFileInfos.Put(id, fileInfo);
17231 
17232     mLastFileId = std::max(id, mLastFileId);
17233   }
17234 
17235   if (NS_WARN_IF(NS_FAILED(rv))) {
17236     return rv;
17237   }
17238 
17239   return NS_OK;
17240 }
17241 
17242 nsresult
Invalidate()17243 FileManager::Invalidate()
17244 {
17245   if (IndexedDatabaseManager::IsClosed()) {
17246     MOZ_ASSERT(false, "Shouldn't be called after shutdown!");
17247     return NS_ERROR_UNEXPECTED;
17248   }
17249 
17250   MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
17251 
17252   MOZ_ASSERT(!mInvalidated);
17253   mInvalidated = true;
17254 
17255   for (auto iter = mFileInfos.Iter(); !iter.Done(); iter.Next()) {
17256     FileInfo* info = iter.Data();
17257     MOZ_ASSERT(info);
17258 
17259     if (!info->LockedClearDBRefs()) {
17260       iter.Remove();
17261     }
17262   }
17263 
17264   return NS_OK;
17265 }
17266 
17267 already_AddRefed<nsIFile>
GetDirectory()17268 FileManager::GetDirectory()
17269 {
17270   return GetFileForPath(mDirectoryPath);
17271 }
17272 
17273 already_AddRefed<nsIFile>
GetCheckedDirectory()17274 FileManager::GetCheckedDirectory()
17275 {
17276   nsCOMPtr<nsIFile> directory = GetDirectory();
17277   if (NS_WARN_IF(!directory)) {
17278     return nullptr;
17279   }
17280 
17281   DebugOnly<bool> exists;
17282   MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)));
17283   MOZ_ASSERT(exists);
17284 
17285   DebugOnly<bool> isDirectory;
17286   MOZ_ASSERT(NS_SUCCEEDED(directory->IsDirectory(&isDirectory)));
17287   MOZ_ASSERT(isDirectory);
17288 
17289   return directory.forget();
17290 }
17291 
17292 already_AddRefed<nsIFile>
GetJournalDirectory()17293 FileManager::GetJournalDirectory()
17294 {
17295   return GetFileForPath(mJournalDirectoryPath);
17296 }
17297 
17298 already_AddRefed<nsIFile>
EnsureJournalDirectory()17299 FileManager::EnsureJournalDirectory()
17300 {
17301   // This can happen on the IO or on a transaction thread.
17302   MOZ_ASSERT(!NS_IsMainThread());
17303 
17304   nsCOMPtr<nsIFile> journalDirectory = GetFileForPath(mJournalDirectoryPath);
17305   if (NS_WARN_IF(!journalDirectory)) {
17306     return nullptr;
17307   }
17308 
17309   bool exists;
17310   nsresult rv = journalDirectory->Exists(&exists);
17311   if (NS_WARN_IF(NS_FAILED(rv))) {
17312     return nullptr;
17313   }
17314 
17315   if (exists) {
17316     bool isDirectory;
17317     rv = journalDirectory->IsDirectory(&isDirectory);
17318     if (NS_WARN_IF(NS_FAILED(rv))) {
17319       return nullptr;
17320     }
17321 
17322     if (NS_WARN_IF(!isDirectory)) {
17323       return nullptr;
17324     }
17325   } else {
17326     rv = journalDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
17327     if (NS_WARN_IF(NS_FAILED(rv))) {
17328       return nullptr;
17329     }
17330   }
17331 
17332   return journalDirectory.forget();
17333 }
17334 
17335 already_AddRefed<FileInfo>
GetFileInfo(int64_t aId)17336 FileManager::GetFileInfo(int64_t aId)
17337 {
17338   if (IndexedDatabaseManager::IsClosed()) {
17339     MOZ_ASSERT(false, "Shouldn't be called after shutdown!");
17340     return nullptr;
17341   }
17342 
17343   FileInfo* fileInfo;
17344   {
17345     MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
17346     fileInfo = mFileInfos.Get(aId);
17347   }
17348 
17349   RefPtr<FileInfo> result = fileInfo;
17350   return result.forget();
17351 }
17352 
17353 already_AddRefed<FileInfo>
GetNewFileInfo()17354 FileManager::GetNewFileInfo()
17355 {
17356   MOZ_ASSERT(!IndexedDatabaseManager::IsClosed());
17357 
17358   FileInfo* fileInfo;
17359   {
17360     MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
17361 
17362     int64_t id = mLastFileId + 1;
17363 
17364     fileInfo = FileInfo::Create(this, id);
17365 
17366     mFileInfos.Put(id, fileInfo);
17367 
17368     mLastFileId = id;
17369   }
17370 
17371   RefPtr<FileInfo> result = fileInfo;
17372   return result.forget();
17373 }
17374 
17375 // static
17376 already_AddRefed<nsIFile>
GetFileForId(nsIFile * aDirectory,int64_t aId)17377 FileManager::GetFileForId(nsIFile* aDirectory, int64_t aId)
17378 {
17379   MOZ_ASSERT(aDirectory);
17380   MOZ_ASSERT(aId > 0);
17381 
17382   nsAutoString id;
17383   id.AppendInt(aId);
17384 
17385   nsCOMPtr<nsIFile> file;
17386   nsresult rv = aDirectory->Clone(getter_AddRefs(file));
17387   if (NS_WARN_IF(NS_FAILED(rv))) {
17388     return nullptr;
17389   }
17390 
17391   rv = file->Append(id);
17392   if (NS_WARN_IF(NS_FAILED(rv))) {
17393     return nullptr;
17394   }
17395 
17396   return file.forget();
17397 }
17398 
17399 // static
17400 already_AddRefed<nsIFile>
GetCheckedFileForId(nsIFile * aDirectory,int64_t aId)17401 FileManager::GetCheckedFileForId(nsIFile* aDirectory, int64_t aId)
17402 {
17403   nsCOMPtr<nsIFile> file = GetFileForId(aDirectory, aId);
17404   if (NS_WARN_IF(!file)) {
17405     return nullptr;
17406   }
17407 
17408   DebugOnly<bool> exists;
17409   MOZ_ASSERT(NS_SUCCEEDED(file->Exists(&exists)));
17410   MOZ_ASSERT(exists);
17411 
17412   DebugOnly<bool> isFile;
17413   MOZ_ASSERT(NS_SUCCEEDED(file->IsFile(&isFile)));
17414   MOZ_ASSERT(isFile);
17415 
17416   return file.forget();
17417 }
17418 
17419 // static
17420 nsresult
InitDirectory(nsIFile * aDirectory,nsIFile * aDatabaseFile,PersistenceType aPersistenceType,const nsACString & aGroup,const nsACString & aOrigin,uint32_t aTelemetryId)17421 FileManager::InitDirectory(nsIFile* aDirectory,
17422                            nsIFile* aDatabaseFile,
17423                            PersistenceType aPersistenceType,
17424                            const nsACString& aGroup,
17425                            const nsACString& aOrigin,
17426                            uint32_t aTelemetryId)
17427 {
17428   AssertIsOnIOThread();
17429   MOZ_ASSERT(aDirectory);
17430   MOZ_ASSERT(aDatabaseFile);
17431 
17432   bool exists;
17433   nsresult rv = aDirectory->Exists(&exists);
17434   if (NS_WARN_IF(NS_FAILED(rv))) {
17435     return rv;
17436   }
17437 
17438   if (!exists) {
17439     return NS_OK;
17440   }
17441 
17442   bool isDirectory;
17443   rv = aDirectory->IsDirectory(&isDirectory);
17444   if (NS_WARN_IF(NS_FAILED(rv))) {
17445     return rv;
17446   }
17447 
17448   if (NS_WARN_IF(!isDirectory)) {
17449     return NS_ERROR_FAILURE;
17450   }
17451 
17452   nsCOMPtr<nsIFile> journalDirectory;
17453   rv = aDirectory->Clone(getter_AddRefs(journalDirectory));
17454   if (NS_WARN_IF(NS_FAILED(rv))) {
17455     return rv;
17456   }
17457 
17458   rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME));
17459   if (NS_WARN_IF(NS_FAILED(rv))) {
17460     return rv;
17461   }
17462 
17463   rv = journalDirectory->Exists(&exists);
17464   if (NS_WARN_IF(NS_FAILED(rv))) {
17465     return rv;
17466   }
17467 
17468   if (exists) {
17469     rv = journalDirectory->IsDirectory(&isDirectory);
17470     if (NS_WARN_IF(NS_FAILED(rv))) {
17471       return rv;
17472     }
17473 
17474     if (NS_WARN_IF(!isDirectory)) {
17475       return NS_ERROR_FAILURE;
17476     }
17477 
17478     nsCOMPtr<nsISimpleEnumerator> entries;
17479     rv = journalDirectory->GetDirectoryEntries(getter_AddRefs(entries));
17480     if (NS_WARN_IF(NS_FAILED(rv))) {
17481       return rv;
17482     }
17483 
17484     bool hasElements;
17485     rv = entries->HasMoreElements(&hasElements);
17486     if (NS_WARN_IF(NS_FAILED(rv))) {
17487       return rv;
17488     }
17489 
17490     if (hasElements) {
17491       nsCOMPtr<mozIStorageConnection> connection;
17492       rv = CreateStorageConnection(aDatabaseFile,
17493                                    aDirectory,
17494                                    NullString(),
17495                                    aPersistenceType,
17496                                    aGroup,
17497                                    aOrigin,
17498                                    aTelemetryId,
17499                                    getter_AddRefs(connection));
17500       if (NS_WARN_IF(NS_FAILED(rv))) {
17501         return rv;
17502       }
17503 
17504       mozStorageTransaction transaction(connection, false);
17505 
17506       rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
17507         "CREATE VIRTUAL TABLE fs USING filesystem;"
17508       ));
17509       if (NS_WARN_IF(NS_FAILED(rv))) {
17510         return rv;
17511       }
17512 
17513       nsCOMPtr<mozIStorageStatement> stmt;
17514       rv = connection->CreateStatement(NS_LITERAL_CSTRING(
17515         "SELECT name, (name IN (SELECT id FROM file)) "
17516         "FROM fs "
17517         "WHERE path = :path"
17518       ), getter_AddRefs(stmt));
17519       if (NS_WARN_IF(NS_FAILED(rv))) {
17520         return rv;
17521       }
17522 
17523       nsString path;
17524       rv = journalDirectory->GetPath(path);
17525       if (NS_WARN_IF(NS_FAILED(rv))) {
17526         return rv;
17527       }
17528 
17529       rv = stmt->BindStringByName(NS_LITERAL_CSTRING("path"), path);
17530       if (NS_WARN_IF(NS_FAILED(rv))) {
17531         return rv;
17532       }
17533 
17534       bool hasResult;
17535       while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
17536         nsString name;
17537         rv = stmt->GetString(0, name);
17538         if (NS_WARN_IF(NS_FAILED(rv))) {
17539           return rv;
17540         }
17541 
17542         int32_t flag = stmt->AsInt32(1);
17543 
17544         if (!flag) {
17545           nsCOMPtr<nsIFile> file;
17546           rv = aDirectory->Clone(getter_AddRefs(file));
17547           if (NS_WARN_IF(NS_FAILED(rv))) {
17548             return rv;
17549           }
17550 
17551           rv = file->Append(name);
17552           if (NS_WARN_IF(NS_FAILED(rv))) {
17553             return rv;
17554           }
17555 
17556           if (NS_FAILED(file->Remove(false))) {
17557             NS_WARNING("Failed to remove orphaned file!");
17558           }
17559         }
17560 
17561         nsCOMPtr<nsIFile> journalFile;
17562         rv = journalDirectory->Clone(getter_AddRefs(journalFile));
17563         if (NS_WARN_IF(NS_FAILED(rv))) {
17564           return rv;
17565         }
17566 
17567         rv = journalFile->Append(name);
17568         if (NS_WARN_IF(NS_FAILED(rv))) {
17569           return rv;
17570         }
17571 
17572         if (NS_FAILED(journalFile->Remove(false))) {
17573           NS_WARNING("Failed to remove journal file!");
17574         }
17575       }
17576 
17577       if (NS_WARN_IF(NS_FAILED(rv))) {
17578         return rv;
17579       }
17580 
17581       rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
17582         "DROP TABLE fs;"
17583       ));
17584       if (NS_WARN_IF(NS_FAILED(rv))) {
17585         return rv;
17586       }
17587 
17588       rv = transaction.Commit();
17589       if (NS_WARN_IF(NS_FAILED(rv))) {
17590         return rv;
17591       }
17592     }
17593   }
17594 
17595   return NS_OK;
17596 }
17597 
17598 // static
17599 nsresult
GetUsage(nsIFile * aDirectory,uint64_t * aUsage)17600 FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage)
17601 {
17602   AssertIsOnIOThread();
17603   MOZ_ASSERT(aDirectory);
17604   MOZ_ASSERT(aUsage);
17605 
17606   bool exists;
17607   nsresult rv = aDirectory->Exists(&exists);
17608   if (NS_WARN_IF(NS_FAILED(rv))) {
17609     return rv;
17610   }
17611 
17612   if (!exists) {
17613     *aUsage = 0;
17614     return NS_OK;
17615   }
17616 
17617   nsCOMPtr<nsISimpleEnumerator> entries;
17618   rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
17619   if (NS_WARN_IF(NS_FAILED(rv))) {
17620     return rv;
17621   }
17622 
17623   uint64_t usage = 0;
17624 
17625   bool hasMore;
17626   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
17627     nsCOMPtr<nsISupports> entry;
17628     rv = entries->GetNext(getter_AddRefs(entry));
17629     if (NS_WARN_IF(NS_FAILED(rv))) {
17630       return rv;
17631     }
17632 
17633     nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
17634     MOZ_ASSERT(file);
17635 
17636     nsString leafName;
17637     rv = file->GetLeafName(leafName);
17638     if (NS_WARN_IF(NS_FAILED(rv))) {
17639       return rv;
17640     }
17641 
17642     if (leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) {
17643       continue;
17644     }
17645 
17646     int64_t fileSize;
17647     rv = file->GetFileSize(&fileSize);
17648     if (NS_WARN_IF(NS_FAILED(rv))) {
17649       return rv;
17650     }
17651 
17652     UsageInfo::IncrementUsage(&usage, uint64_t(fileSize));
17653   }
17654 
17655   if (NS_WARN_IF(NS_FAILED(rv))) {
17656     return rv;
17657   }
17658 
17659   *aUsage = usage;
17660   return NS_OK;
17661 }
17662 
17663 /*******************************************************************************
17664  * FileImplStoredFile
17665  ******************************************************************************/
17666 
17667 NS_IMPL_ISUPPORTS_INHERITED(BlobImplStoredFile,
17668                             BlobImplFile,
17669                             BlobImplStoredFile)
17670 
17671 /*******************************************************************************
17672  * QuotaClient
17673  ******************************************************************************/
17674 
17675 QuotaClient* QuotaClient::sInstance = nullptr;
17676 
QuotaClient()17677 QuotaClient::QuotaClient()
17678   : mShutdownRequested(false)
17679 {
17680   AssertIsOnBackgroundThread();
17681   MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
17682   MOZ_ASSERT(!gTelemetryIdMutex);
17683 
17684   // Always create this so that later access to gTelemetryIdHashtable can be
17685   // properly synchronized.
17686   gTelemetryIdMutex = new Mutex("IndexedDB gTelemetryIdMutex");
17687 
17688   sInstance = this;
17689 }
17690 
~QuotaClient()17691 QuotaClient::~QuotaClient()
17692 {
17693   AssertIsOnBackgroundThread();
17694   MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
17695   MOZ_ASSERT(gTelemetryIdMutex);
17696   MOZ_ASSERT(!mMaintenanceThreadPool);
17697 
17698   // No one else should be able to touch gTelemetryIdHashtable now that the
17699   // QuotaClient has gone away.
17700   gTelemetryIdHashtable = nullptr;
17701   gTelemetryIdMutex = nullptr;
17702 
17703   sInstance = nullptr;
17704 }
17705 
17706 nsThreadPool*
GetOrCreateThreadPool()17707 QuotaClient::GetOrCreateThreadPool()
17708 {
17709   AssertIsOnBackgroundThread();
17710   MOZ_ASSERT(!mShutdownRequested);
17711 
17712   if (!mMaintenanceThreadPool) {
17713     RefPtr<nsThreadPool> threadPool = new nsThreadPool();
17714 
17715     // PR_GetNumberOfProcessors() can return -1 on error, so make sure we
17716     // don't set some huge number here. We add 2 in case some threads block on
17717     // the disk I/O.
17718     const uint32_t threadCount =
17719       std::max(int32_t(PR_GetNumberOfProcessors()), int32_t(1)) +
17720       2;
17721 
17722     MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(threadCount));
17723 
17724     // Don't keep more than one idle thread.
17725     MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit(1));
17726 
17727     // Don't keep idle threads alive very long.
17728     MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadTimeout(5 * PR_MSEC_PER_SEC));
17729 
17730     MOZ_ALWAYS_SUCCEEDS(threadPool->SetName(NS_LITERAL_CSTRING("IndexedDB Mnt")));
17731 
17732     mMaintenanceThreadPool = Move(threadPool);
17733   }
17734 
17735   return mMaintenanceThreadPool;
17736 }
17737 
17738 mozilla::dom::quota::Client::Type
GetType()17739 QuotaClient::GetType()
17740 {
17741   return QuotaClient::IDB;
17742 }
17743 
17744 struct FileManagerInitInfo
17745 {
17746   nsCOMPtr<nsIFile> mDirectory;
17747   nsCOMPtr<nsIFile> mDatabaseFile;
17748   nsCOMPtr<nsIFile> mDatabaseWALFile;
17749 };
17750 
17751 nsresult
InitOrigin(PersistenceType aPersistenceType,const nsACString & aGroup,const nsACString & aOrigin,const AtomicBool & aCanceled,UsageInfo * aUsageInfo)17752 QuotaClient::InitOrigin(PersistenceType aPersistenceType,
17753                         const nsACString& aGroup,
17754                         const nsACString& aOrigin,
17755                         const AtomicBool& aCanceled,
17756                         UsageInfo* aUsageInfo)
17757 {
17758   AssertIsOnIOThread();
17759 
17760   nsCOMPtr<nsIFile> directory;
17761   nsresult rv =
17762     GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory));
17763   if (NS_WARN_IF(NS_FAILED(rv))) {
17764     return rv;
17765   }
17766 
17767   // We need to see if there are any files in the directory already. If they
17768   // are database files then we need to cleanup stored files (if it's needed)
17769   // and also get the usage.
17770 
17771   AutoTArray<nsString, 20> subdirsToProcess;
17772   nsTArray<nsCOMPtr<nsIFile>> unknownFiles;
17773   nsTHashtable<nsStringHashKey> validSubdirs(20);
17774   AutoTArray<FileManagerInitInfo, 20> initInfos;
17775 
17776   nsCOMPtr<nsISimpleEnumerator> entries;
17777   rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
17778   if (NS_WARN_IF(NS_FAILED(rv))) {
17779     return rv;
17780   }
17781 
17782   const NS_ConvertASCIItoUTF16 filesSuffix(
17783     kFileManagerDirectoryNameSuffix,
17784     LiteralStringLength(kFileManagerDirectoryNameSuffix));
17785 
17786   const NS_ConvertASCIItoUTF16 journalSuffix(
17787     kSQLiteJournalSuffix,
17788     LiteralStringLength(kSQLiteJournalSuffix));
17789   const NS_ConvertASCIItoUTF16 shmSuffix(kSQLiteSHMSuffix,
17790                                          LiteralStringLength(kSQLiteSHMSuffix));
17791   const NS_ConvertASCIItoUTF16 walSuffix(kSQLiteWALSuffix,
17792                                          LiteralStringLength(kSQLiteWALSuffix));
17793 
17794   bool hasMore;
17795   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
17796          hasMore &&
17797          !aCanceled) {
17798     nsCOMPtr<nsISupports> entry;
17799     rv = entries->GetNext(getter_AddRefs(entry));
17800     if (NS_WARN_IF(NS_FAILED(rv))) {
17801       return rv;
17802     }
17803 
17804     nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
17805     MOZ_ASSERT(file);
17806 
17807     nsString leafName;
17808     rv = file->GetLeafName(leafName);
17809     if (NS_WARN_IF(NS_FAILED(rv))) {
17810       return rv;
17811     }
17812 
17813     bool isDirectory;
17814     rv = file->IsDirectory(&isDirectory);
17815     if (NS_WARN_IF(NS_FAILED(rv))) {
17816       return rv;
17817     }
17818 
17819     if (isDirectory) {
17820       if (!StringEndsWith(leafName, filesSuffix) ||
17821           !validSubdirs.GetEntry(leafName)) {
17822         subdirsToProcess.AppendElement(leafName);
17823       }
17824       continue;
17825     }
17826 
17827     // Skip Desktop Service Store (.DS_Store) files. These files are only used
17828     // on Mac OS X, but the profile can be shared across different operating
17829     // systems, so we check it on all platforms.
17830     if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
17831       continue;
17832     }
17833 
17834     // Skip SQLite temporary files. These files take up space on disk but will
17835     // be deleted as soon as the database is opened, so we don't count them
17836     // towards quota.
17837     if (StringEndsWith(leafName, journalSuffix) ||
17838         StringEndsWith(leafName, shmSuffix)) {
17839       continue;
17840     }
17841 
17842     // The SQLite WAL file does count towards quota, but it is handled below
17843     // once we find the actual database file.
17844     if (StringEndsWith(leafName, walSuffix)) {
17845       continue;
17846     }
17847 
17848     nsDependentSubstring dbBaseFilename;
17849     if (!GetDatabaseBaseFilename(leafName, dbBaseFilename)) {
17850       unknownFiles.AppendElement(file);
17851       continue;
17852     }
17853 
17854     nsCOMPtr<nsIFile> fmDirectory;
17855     rv = directory->Clone(getter_AddRefs(fmDirectory));
17856     if (NS_WARN_IF(NS_FAILED(rv))) {
17857       return rv;
17858     }
17859 
17860     nsString fmDirectoryBaseName = dbBaseFilename + filesSuffix;
17861 
17862     rv = fmDirectory->Append(fmDirectoryBaseName);
17863     if (NS_WARN_IF(NS_FAILED(rv))) {
17864       return rv;
17865     }
17866 
17867     nsCOMPtr<nsIFile> walFile;
17868     if (aUsageInfo) {
17869       rv = directory->Clone(getter_AddRefs(walFile));
17870       if (NS_WARN_IF(NS_FAILED(rv))) {
17871         return rv;
17872       }
17873 
17874       rv = walFile->Append(dbBaseFilename + walSuffix);
17875       if (NS_WARN_IF(NS_FAILED(rv))) {
17876         return rv;
17877       }
17878     }
17879 
17880     FileManagerInitInfo* initInfo = initInfos.AppendElement();
17881     initInfo->mDirectory.swap(fmDirectory);
17882     initInfo->mDatabaseFile.swap(file);
17883     initInfo->mDatabaseWALFile.swap(walFile);
17884 
17885     validSubdirs.PutEntry(fmDirectoryBaseName);
17886   }
17887 
17888   if (NS_WARN_IF(NS_FAILED(rv))) {
17889     return rv;
17890   }
17891 
17892   for (uint32_t count = subdirsToProcess.Length(), i = 0; i < count; i++) {
17893     const nsString& subdirName = subdirsToProcess[i];
17894 
17895     // If the directory has the correct suffix then it must exist in
17896     // validSubdirs.
17897     if (StringEndsWith(subdirName, filesSuffix)) {
17898       if (NS_WARN_IF(!validSubdirs.GetEntry(subdirName))) {
17899         return NS_ERROR_UNEXPECTED;
17900       }
17901 
17902       continue;
17903     }
17904 
17905     // The directory didn't have the right suffix but we might need to rename
17906     // it. Check to see if we have a database that references this directory.
17907     nsString subdirNameWithSuffix = subdirName + filesSuffix;
17908     if (!validSubdirs.GetEntry(subdirNameWithSuffix)) {
17909       // Windows doesn't allow a directory to end with a dot ('.'), so we have
17910       // to check that possibility here too.
17911       // We do this on all platforms, because the origin directory may have
17912       // been created on Windows and now accessed on different OS.
17913       subdirNameWithSuffix = subdirName + NS_LITERAL_STRING(".") + filesSuffix;
17914       if (NS_WARN_IF(!validSubdirs.GetEntry(subdirNameWithSuffix))) {
17915         return NS_ERROR_UNEXPECTED;
17916       }
17917     }
17918 
17919     // We do have a database that uses this directory so we should rename it
17920     // now. However, first check to make sure that we're not overwriting
17921     // something else.
17922     nsCOMPtr<nsIFile> subdir;
17923     rv = directory->Clone(getter_AddRefs(subdir));
17924     if (NS_WARN_IF(NS_FAILED(rv))) {
17925       return rv;
17926     }
17927 
17928     rv = subdir->Append(subdirNameWithSuffix);
17929     if (NS_WARN_IF(NS_FAILED(rv))) {
17930       return rv;
17931     }
17932 
17933     bool exists;
17934     rv = subdir->Exists(&exists);
17935     if (NS_WARN_IF(NS_FAILED(rv))) {
17936       return rv;
17937     }
17938 
17939     if (exists) {
17940       IDB_REPORT_INTERNAL_ERR();
17941       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
17942     }
17943 
17944     rv = directory->Clone(getter_AddRefs(subdir));
17945     if (NS_WARN_IF(NS_FAILED(rv))) {
17946       return rv;
17947     }
17948 
17949     rv = subdir->Append(subdirName);
17950     if (NS_WARN_IF(NS_FAILED(rv))) {
17951       return rv;
17952     }
17953 
17954     DebugOnly<bool> isDirectory;
17955     MOZ_ASSERT(NS_SUCCEEDED(subdir->IsDirectory(&isDirectory)));
17956     MOZ_ASSERT(isDirectory);
17957 
17958     rv = subdir->RenameTo(nullptr, subdirNameWithSuffix);
17959     if (NS_WARN_IF(NS_FAILED(rv))) {
17960       return rv;
17961     }
17962   }
17963 
17964   for (uint32_t count = initInfos.Length(), i = 0;
17965        i < count && !aCanceled;
17966        i++) {
17967     FileManagerInitInfo& initInfo = initInfos[i];
17968     MOZ_ASSERT(initInfo.mDirectory);
17969     MOZ_ASSERT(initInfo.mDatabaseFile);
17970     MOZ_ASSERT_IF(aUsageInfo, initInfo.mDatabaseWALFile);
17971 
17972     rv = FileManager::InitDirectory(initInfo.mDirectory,
17973                                     initInfo.mDatabaseFile,
17974                                     aPersistenceType,
17975                                     aGroup,
17976                                     aOrigin,
17977                                     TelemetryIdForFile(initInfo.mDatabaseFile));
17978     if (NS_WARN_IF(NS_FAILED(rv))) {
17979       return rv;
17980     }
17981 
17982     if (aUsageInfo) {
17983       int64_t fileSize;
17984       rv = initInfo.mDatabaseFile->GetFileSize(&fileSize);
17985       if (NS_WARN_IF(NS_FAILED(rv))) {
17986         return rv;
17987       }
17988 
17989       MOZ_ASSERT(fileSize >= 0);
17990 
17991       aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
17992 
17993       rv = initInfo.mDatabaseWALFile->GetFileSize(&fileSize);
17994       if (NS_SUCCEEDED(rv)) {
17995         MOZ_ASSERT(fileSize >= 0);
17996         aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
17997       } else if (NS_WARN_IF(rv != NS_ERROR_FILE_NOT_FOUND &&
17998                             rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)) {
17999         return rv;
18000       }
18001 
18002       uint64_t usage;
18003       rv = FileManager::GetUsage(initInfo.mDirectory, &usage);
18004       if (NS_WARN_IF(NS_FAILED(rv))) {
18005         return rv;
18006       }
18007 
18008       aUsageInfo->AppendToFileUsage(usage);
18009     }
18010   }
18011 
18012   // We have to do this after file manager initialization.
18013   if (!unknownFiles.IsEmpty()) {
18014 #ifdef DEBUG
18015     for (uint32_t count = unknownFiles.Length(), i = 0; i < count; i++) {
18016       nsCOMPtr<nsIFile>& unknownFile = unknownFiles[i];
18017 
18018       nsString leafName;
18019       MOZ_ALWAYS_SUCCEEDS(unknownFile->GetLeafName(leafName));
18020 
18021       MOZ_ASSERT(!StringEndsWith(leafName, journalSuffix));
18022       MOZ_ASSERT(!StringEndsWith(leafName, shmSuffix));
18023       MOZ_ASSERT(!StringEndsWith(leafName, walSuffix));
18024 
18025       nsString path;
18026       MOZ_ALWAYS_SUCCEEDS(unknownFile->GetPath(path));
18027       MOZ_ASSERT(!path.IsEmpty());
18028 
18029       nsPrintfCString warning("Refusing to open databases for \"%s\" because "
18030                               "an unexpected file exists in the storage "
18031                               "area: \"%s\"",
18032                               PromiseFlatCString(aOrigin).get(),
18033                               NS_ConvertUTF16toUTF8(path).get());
18034       NS_WARNING(warning.get());
18035     }
18036 #endif
18037     return NS_ERROR_UNEXPECTED;
18038   }
18039 
18040   return NS_OK;
18041 }
18042 
18043 nsresult
GetUsageForOrigin(PersistenceType aPersistenceType,const nsACString & aGroup,const nsACString & aOrigin,const AtomicBool & aCanceled,UsageInfo * aUsageInfo)18044 QuotaClient::GetUsageForOrigin(PersistenceType aPersistenceType,
18045                                const nsACString& aGroup,
18046                                const nsACString& aOrigin,
18047                                const AtomicBool& aCanceled,
18048                                UsageInfo* aUsageInfo)
18049 {
18050   AssertIsOnIOThread();
18051   MOZ_ASSERT(aUsageInfo);
18052 
18053   nsCOMPtr<nsIFile> directory;
18054   nsresult rv =
18055     GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory));
18056   if (NS_WARN_IF(NS_FAILED(rv))) {
18057     return rv;
18058   }
18059 
18060   rv = GetUsageForDirectoryInternal(directory, aCanceled, aUsageInfo, true);
18061   if (NS_WARN_IF(NS_FAILED(rv))) {
18062     return rv;
18063   }
18064 
18065   return NS_OK;
18066 }
18067 
18068 void
OnOriginClearCompleted(PersistenceType aPersistenceType,const nsACString & aOrigin)18069 QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
18070                                     const nsACString& aOrigin)
18071 {
18072   AssertIsOnIOThread();
18073 
18074   if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
18075     mgr->InvalidateFileManagers(aPersistenceType, aOrigin);
18076   }
18077 }
18078 
18079 void
ReleaseIOThreadObjects()18080 QuotaClient::ReleaseIOThreadObjects()
18081 {
18082   AssertIsOnIOThread();
18083 
18084   if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
18085     mgr->InvalidateAllFileManagers();
18086   }
18087 }
18088 
18089 void
AbortOperations(const nsACString & aOrigin)18090 QuotaClient::AbortOperations(const nsACString& aOrigin)
18091 {
18092   AssertIsOnBackgroundThread();
18093 
18094   if (!gLiveDatabaseHashtable) {
18095     return;
18096   }
18097 
18098   nsTArray<RefPtr<Database>> databases;
18099 
18100   for (auto iter = gLiveDatabaseHashtable->ConstIter();
18101        !iter.Done(); iter.Next()) {
18102     for (Database* database : iter.Data()->mLiveDatabases) {
18103       if (aOrigin.IsVoid() || database->Origin() == aOrigin) {
18104         databases.AppendElement(database);
18105       }
18106     }
18107   }
18108 
18109   for (Database* database : databases) {
18110     database->Invalidate();
18111   }
18112 
18113   databases.Clear();
18114 }
18115 
18116 void
AbortOperationsForProcess(ContentParentId aContentParentId)18117 QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId)
18118 {
18119   AssertIsOnBackgroundThread();
18120 
18121   if (!gLiveDatabaseHashtable) {
18122     return;
18123   }
18124 
18125   nsTArray<RefPtr<Database>> databases;
18126 
18127   for (auto iter = gLiveDatabaseHashtable->ConstIter();
18128        !iter.Done(); iter.Next()) {
18129     for (Database* database : iter.Data()->mLiveDatabases) {
18130       if (database->IsOwnedByProcess(aContentParentId)) {
18131         databases.AppendElement(database);
18132       }
18133     }
18134   }
18135 
18136   for (Database* database : databases) {
18137     database->Invalidate();
18138   }
18139 
18140   databases.Clear();
18141 }
18142 
18143 void
StartIdleMaintenance()18144 QuotaClient::StartIdleMaintenance()
18145 {
18146   AssertIsOnBackgroundThread();
18147   MOZ_ASSERT(!mShutdownRequested);
18148 
18149   mBackgroundThread = do_GetCurrentThread();
18150 
18151   RefPtr<Maintenance> maintenance = new Maintenance(this);
18152 
18153   mMaintenanceQueue.AppendElement(maintenance.forget());
18154   ProcessMaintenanceQueue();
18155 }
18156 
18157 void
StopIdleMaintenance()18158 QuotaClient::StopIdleMaintenance()
18159 {
18160   AssertIsOnBackgroundThread();
18161   MOZ_ASSERT(!mShutdownRequested);
18162 
18163   if (mCurrentMaintenance) {
18164     mCurrentMaintenance->Abort();
18165   }
18166 
18167   for (RefPtr<Maintenance>& maintenance : mMaintenanceQueue) {
18168     maintenance->Abort();
18169   }
18170 }
18171 
18172 void
ShutdownWorkThreads()18173 QuotaClient::ShutdownWorkThreads()
18174 {
18175   AssertIsOnBackgroundThread();
18176   MOZ_ASSERT(!mShutdownRequested);
18177 
18178   mShutdownRequested = true;
18179 
18180   if (mMaintenanceThreadPool) {
18181     mMaintenanceThreadPool->Shutdown();
18182     mMaintenanceThreadPool = nullptr;
18183   }
18184 
18185   RefPtr<ConnectionPool> connectionPool = gConnectionPool.get();
18186   if (connectionPool) {
18187     connectionPool->Shutdown();
18188 
18189     gConnectionPool = nullptr;
18190   }
18191 
18192   RefPtr<FileHandleThreadPool> fileHandleThreadPool =
18193     gFileHandleThreadPool.get();
18194   if (fileHandleThreadPool) {
18195     fileHandleThreadPool->Shutdown();
18196 
18197     gFileHandleThreadPool = nullptr;
18198   }
18199 }
18200 
18201 void
DidInitialize(QuotaManager * aQuotaManager)18202 QuotaClient::DidInitialize(QuotaManager* aQuotaManager)
18203 {
18204   MOZ_ASSERT(NS_IsMainThread());
18205 
18206   if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
18207     mgr->NoteLiveQuotaManager(aQuotaManager);
18208   }
18209 }
18210 
18211 void
WillShutdown()18212 QuotaClient::WillShutdown()
18213 {
18214   MOZ_ASSERT(NS_IsMainThread());
18215 
18216   if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
18217     mgr->NoteShuttingDownQuotaManager();
18218   }
18219 }
18220 
18221 nsresult
GetDirectory(PersistenceType aPersistenceType,const nsACString & aOrigin,nsIFile ** aDirectory)18222 QuotaClient::GetDirectory(PersistenceType aPersistenceType,
18223                           const nsACString& aOrigin, nsIFile** aDirectory)
18224 {
18225   QuotaManager* quotaManager = QuotaManager::Get();
18226   NS_ASSERTION(quotaManager, "This should never fail!");
18227 
18228   nsCOMPtr<nsIFile> directory;
18229   nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin,
18230                                                     getter_AddRefs(directory));
18231   if (NS_WARN_IF(NS_FAILED(rv))) {
18232     return rv;
18233   }
18234 
18235   MOZ_ASSERT(directory);
18236 
18237   rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
18238   if (NS_WARN_IF(NS_FAILED(rv))) {
18239     return rv;
18240   }
18241 
18242   directory.forget(aDirectory);
18243   return NS_OK;
18244 }
18245 
18246 nsresult
GetUsageForDirectoryInternal(nsIFile * aDirectory,const AtomicBool & aCanceled,UsageInfo * aUsageInfo,bool aDatabaseFiles)18247 QuotaClient::GetUsageForDirectoryInternal(nsIFile* aDirectory,
18248                                           const AtomicBool& aCanceled,
18249                                           UsageInfo* aUsageInfo,
18250                                           bool aDatabaseFiles)
18251 {
18252   AssertIsOnIOThread();
18253   MOZ_ASSERT(aDirectory);
18254   MOZ_ASSERT(aUsageInfo);
18255 
18256   nsCOMPtr<nsISimpleEnumerator> entries;
18257   nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
18258   if (NS_WARN_IF(NS_FAILED(rv))) {
18259     return rv;
18260   }
18261 
18262   if (!entries) {
18263     return NS_OK;
18264   }
18265 
18266   const NS_ConvertASCIItoUTF16 journalSuffix(
18267     kSQLiteJournalSuffix,
18268     LiteralStringLength(kSQLiteJournalSuffix));
18269   const NS_ConvertASCIItoUTF16 shmSuffix(kSQLiteSHMSuffix,
18270                                          LiteralStringLength(kSQLiteSHMSuffix));
18271 
18272   bool hasMore;
18273   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
18274          hasMore &&
18275          !aCanceled) {
18276     nsCOMPtr<nsISupports> entry;
18277     rv = entries->GetNext(getter_AddRefs(entry));
18278     if (NS_WARN_IF(NS_FAILED(rv))) {
18279       return rv;
18280     }
18281 
18282     nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
18283     MOZ_ASSERT(file);
18284 
18285     nsString leafName;
18286     rv = file->GetLeafName(leafName);
18287     if (NS_WARN_IF(NS_FAILED(rv))) {
18288       return rv;
18289     }
18290 
18291     // Journal files and sqlite-shm files don't count towards usage.
18292     if (StringEndsWith(leafName, journalSuffix) ||
18293         StringEndsWith(leafName, shmSuffix)) {
18294       continue;
18295     }
18296 
18297     bool isDirectory;
18298     rv = file->IsDirectory(&isDirectory);
18299     if (rv == NS_ERROR_FILE_NOT_FOUND ||
18300         rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
18301       continue;
18302     }
18303 
18304     if (NS_WARN_IF(NS_FAILED(rv))) {
18305       return rv;
18306     }
18307 
18308     if (isDirectory) {
18309       if (aDatabaseFiles) {
18310         rv = GetUsageForDirectoryInternal(file, aCanceled, aUsageInfo, false);
18311         if (NS_WARN_IF(NS_FAILED(rv))) {
18312           return rv;
18313         }
18314       } else {
18315         nsString leafName;
18316         rv = file->GetLeafName(leafName);
18317         if (NS_WARN_IF(NS_FAILED(rv))) {
18318           return rv;
18319         }
18320 
18321         if (!leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) {
18322           NS_WARNING("Unknown directory found!");
18323         }
18324       }
18325 
18326       continue;
18327     }
18328 
18329     int64_t fileSize;
18330     rv = file->GetFileSize(&fileSize);
18331     if (NS_WARN_IF(NS_FAILED(rv))) {
18332       return rv;
18333     }
18334 
18335     MOZ_ASSERT(fileSize >= 0);
18336 
18337     if (aDatabaseFiles) {
18338       aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
18339     } else {
18340       aUsageInfo->AppendToFileUsage(uint64_t(fileSize));
18341     }
18342   }
18343 
18344   if (NS_WARN_IF(NS_FAILED(rv))) {
18345     return rv;
18346   }
18347 
18348   return NS_OK;
18349 }
18350 
18351 void
ProcessMaintenanceQueue()18352 QuotaClient::ProcessMaintenanceQueue()
18353 {
18354   AssertIsOnBackgroundThread();
18355 
18356   if (mCurrentMaintenance || mMaintenanceQueue.IsEmpty()) {
18357     return;
18358   }
18359 
18360   mCurrentMaintenance = mMaintenanceQueue[0];
18361   mMaintenanceQueue.RemoveElementAt(0);
18362 
18363   mCurrentMaintenance->RunImmediately();
18364 }
18365 
18366 void
RegisterDatabaseMaintenance(DatabaseMaintenance * aDatabaseMaintenance)18367 Maintenance::RegisterDatabaseMaintenance(
18368                                       DatabaseMaintenance* aDatabaseMaintenance)
18369 {
18370   AssertIsOnBackgroundThread();
18371   MOZ_ASSERT(aDatabaseMaintenance);
18372   MOZ_ASSERT(mState == State::BeginDatabaseMaintenance);
18373   MOZ_ASSERT(!mDatabaseMaintenances.Get(aDatabaseMaintenance->DatabasePath()));
18374 
18375   mDatabaseMaintenances.Put(aDatabaseMaintenance->DatabasePath(),
18376                             aDatabaseMaintenance);
18377 }
18378 
18379 void
UnregisterDatabaseMaintenance(DatabaseMaintenance * aDatabaseMaintenance)18380 Maintenance::UnregisterDatabaseMaintenance(
18381                                       DatabaseMaintenance* aDatabaseMaintenance)
18382 {
18383   AssertIsOnBackgroundThread();
18384   MOZ_ASSERT(aDatabaseMaintenance);
18385   MOZ_ASSERT(mState == State::WaitingForDatabaseMaintenancesToComplete);
18386   MOZ_ASSERT(mDatabaseMaintenances.Get(aDatabaseMaintenance->DatabasePath()));
18387 
18388   mDatabaseMaintenances.Remove(aDatabaseMaintenance->DatabasePath());
18389 
18390   if (mDatabaseMaintenances.Count()) {
18391     return;
18392   }
18393 
18394   mState = State::Finishing;
18395   Finish();
18396 }
18397 
18398 nsresult
Start()18399 Maintenance::Start()
18400 {
18401   AssertIsOnBackgroundThread();
18402   MOZ_ASSERT(mState == State::Initial);
18403 
18404   if (IsAborted()) {
18405     return NS_ERROR_ABORT;
18406   }
18407 
18408   // Make sure that the IndexedDatabaseManager is running so that we can check
18409   // for low disk space mode.
18410 
18411   if (IndexedDatabaseManager::Get()) {
18412     OpenDirectory();
18413     return NS_OK;
18414   }
18415 
18416   mState = State::CreateIndexedDatabaseManager;
18417   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
18418 
18419   return NS_OK;
18420 }
18421 
18422 nsresult
CreateIndexedDatabaseManager()18423 Maintenance::CreateIndexedDatabaseManager()
18424 {
18425   MOZ_ASSERT(NS_IsMainThread());
18426   MOZ_ASSERT(mState == State::CreateIndexedDatabaseManager);
18427 
18428   if (IsAborted()) {
18429     return NS_ERROR_ABORT;
18430   }
18431 
18432   IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
18433   if (NS_WARN_IF(!mgr)) {
18434     return NS_ERROR_FAILURE;
18435   }
18436 
18437   mState = State::IndexedDatabaseManagerOpen;
18438   MOZ_ALWAYS_SUCCEEDS(
18439     mQuotaClient->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL));
18440 
18441   return NS_OK;
18442 }
18443 
18444 nsresult
OpenDirectory()18445 Maintenance::OpenDirectory()
18446 {
18447   AssertIsOnBackgroundThread();
18448   MOZ_ASSERT(mState == State::Initial ||
18449              mState == State::IndexedDatabaseManagerOpen);
18450   MOZ_ASSERT(!mDirectoryLock);
18451   MOZ_ASSERT(QuotaManager::Get());
18452 
18453   if (IsAborted()) {
18454     return NS_ERROR_ABORT;
18455   }
18456 
18457   // Get a shared lock for <profile>/storage/*/*/idb
18458 
18459   mState = State::DirectoryOpenPending;
18460   QuotaManager::Get()->OpenDirectoryInternal(
18461                                            Nullable<PersistenceType>(),
18462                                            OriginScope::FromNull(),
18463                                            Nullable<Client::Type>(Client::IDB),
18464                                            /* aExclusive */ false,
18465                                            this);
18466 
18467   return NS_OK;
18468 }
18469 
18470 nsresult
DirectoryOpen()18471 Maintenance::DirectoryOpen()
18472 {
18473   AssertIsOnBackgroundThread();
18474   MOZ_ASSERT(mState == State::DirectoryOpenPending);
18475   MOZ_ASSERT(mDirectoryLock);
18476 
18477   if (IsAborted()) {
18478     return NS_ERROR_ABORT;
18479   }
18480 
18481   QuotaManager* quotaManager = QuotaManager::Get();
18482   MOZ_ASSERT(quotaManager);
18483 
18484   mState = State::DirectoryWorkOpen;
18485 
18486   nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
18487   if (NS_WARN_IF(NS_FAILED(rv))) {
18488     return NS_ERROR_FAILURE;
18489   }
18490 
18491   return NS_OK;
18492 }
18493 
18494 nsresult
DirectoryWork()18495 Maintenance::DirectoryWork()
18496 {
18497   AssertIsOnIOThread();
18498   MOZ_ASSERT(mState == State::DirectoryWorkOpen);
18499 
18500   // The storage directory is structured like this:
18501   //
18502   //   <profile>/storage/<persistence>/<origin>/idb/*.sqlite
18503   //
18504   // We have to find all database files that match any persistence type and any
18505   // origin. We ignore anything out of the ordinary for now.
18506 
18507   if (IsAborted()) {
18508     return NS_ERROR_ABORT;
18509   }
18510 
18511   QuotaManager* quotaManager = QuotaManager::Get();
18512   MOZ_ASSERT(quotaManager);
18513 
18514   nsresult rv = quotaManager->EnsureStorageIsInitialized();
18515   if (NS_WARN_IF(NS_FAILED(rv))) {
18516     return rv;
18517   }
18518 
18519   nsCOMPtr<nsIFile> storageDir = GetFileForPath(quotaManager->GetStoragePath());
18520   if (NS_WARN_IF(!storageDir)) {
18521     return NS_ERROR_FAILURE;
18522   }
18523 
18524   bool exists;
18525   rv = storageDir->Exists(&exists);
18526   if (NS_WARN_IF(NS_FAILED(rv))) {
18527     return rv;
18528   }
18529 
18530   if (!exists) {
18531     return NS_ERROR_NOT_AVAILABLE;
18532   }
18533 
18534   bool isDirectory;
18535   rv = storageDir->IsDirectory(&isDirectory);
18536   if (NS_WARN_IF(NS_FAILED(rv))) {
18537     return rv;
18538   }
18539 
18540   if (NS_WARN_IF(!isDirectory)) {
18541     return NS_ERROR_FAILURE;
18542   }
18543 
18544   // There are currently only 3 persistence types, and we want to iterate them
18545   // in this order:
18546   static const PersistenceType kPersistenceTypes[] = {
18547     PERSISTENCE_TYPE_PERSISTENT,
18548     PERSISTENCE_TYPE_DEFAULT,
18549     PERSISTENCE_TYPE_TEMPORARY
18550   };
18551 
18552   static_assert((sizeof(kPersistenceTypes) / sizeof(kPersistenceTypes[0])) ==
18553                   size_t(PERSISTENCE_TYPE_INVALID),
18554                 "Something changed with available persistence types!");
18555 
18556   NS_NAMED_LITERAL_STRING(idbDirName, IDB_DIRECTORY_NAME);
18557   NS_NAMED_LITERAL_STRING(sqliteExtension, ".sqlite");
18558 
18559   for (const PersistenceType persistenceType : kPersistenceTypes) {
18560     // Loop over "<persistence>" directories.
18561     if (IsAborted()) {
18562       return NS_ERROR_ABORT;
18563     }
18564 
18565     nsAutoCString persistenceTypeString;
18566     if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
18567       // XXX This shouldn't be a special case...
18568       persistenceTypeString.AssignLiteral("permanent");
18569     } else {
18570       PersistenceTypeToText(persistenceType, persistenceTypeString);
18571     }
18572 
18573     nsCOMPtr<nsIFile> persistenceDir;
18574     rv = storageDir->Clone(getter_AddRefs(persistenceDir));
18575     if (NS_WARN_IF(NS_FAILED(rv))) {
18576       return rv;
18577     }
18578 
18579     rv = persistenceDir->Append(NS_ConvertASCIItoUTF16(persistenceTypeString));
18580     if (NS_WARN_IF(NS_FAILED(rv))) {
18581       return rv;
18582     }
18583 
18584     rv = persistenceDir->Exists(&exists);
18585     if (NS_WARN_IF(NS_FAILED(rv))) {
18586       return rv;
18587     }
18588 
18589     if (!exists) {
18590       continue;
18591     }
18592 
18593     rv = persistenceDir->IsDirectory(&isDirectory);
18594     if (NS_WARN_IF(NS_FAILED(rv))) {
18595       return rv;
18596     }
18597 
18598     if (NS_WARN_IF(!isDirectory)) {
18599       continue;
18600     }
18601 
18602     nsCOMPtr<nsISimpleEnumerator> persistenceDirEntries;
18603     rv = persistenceDir->GetDirectoryEntries(
18604                                          getter_AddRefs(persistenceDirEntries));
18605     if (NS_WARN_IF(NS_FAILED(rv))) {
18606       return rv;
18607     }
18608 
18609     if (!persistenceDirEntries) {
18610       continue;
18611     }
18612 
18613     while (true) {
18614       // Loop over "<origin>/idb" directories.
18615       if (IsAborted()) {
18616         return NS_ERROR_ABORT;
18617       }
18618 
18619       bool persistenceDirHasMoreEntries;
18620       rv = persistenceDirEntries->HasMoreElements(
18621                                                  &persistenceDirHasMoreEntries);
18622       if (NS_WARN_IF(NS_FAILED(rv))) {
18623         return rv;
18624       }
18625 
18626       if (!persistenceDirHasMoreEntries) {
18627         break;
18628       }
18629 
18630       nsCOMPtr<nsISupports> persistenceDirEntry;
18631       rv = persistenceDirEntries->GetNext(getter_AddRefs(persistenceDirEntry));
18632       if (NS_WARN_IF(NS_FAILED(rv))) {
18633         return rv;
18634       }
18635 
18636       nsCOMPtr<nsIFile> originDir = do_QueryInterface(persistenceDirEntry);
18637       MOZ_ASSERT(originDir);
18638 
18639       rv = originDir->Exists(&exists);
18640       if (NS_WARN_IF(NS_FAILED(rv))) {
18641         return rv;
18642       }
18643 
18644       MOZ_ASSERT(exists);
18645 
18646       rv = originDir->IsDirectory(&isDirectory);
18647       if (NS_WARN_IF(NS_FAILED(rv))) {
18648         return rv;
18649       }
18650 
18651       if (!isDirectory) {
18652         continue;
18653       }
18654 
18655       nsCOMPtr<nsIFile> idbDir;
18656       rv = originDir->Clone(getter_AddRefs(idbDir));
18657       if (NS_WARN_IF(NS_FAILED(rv))) {
18658         return rv;
18659       }
18660 
18661       rv = idbDir->Append(idbDirName);
18662       if (NS_WARN_IF(NS_FAILED(rv))) {
18663         return rv;
18664       }
18665 
18666       rv = idbDir->Exists(&exists);
18667       if (NS_WARN_IF(NS_FAILED(rv))) {
18668         return rv;
18669       }
18670 
18671       if (!exists) {
18672         continue;
18673       }
18674 
18675       rv = idbDir->IsDirectory(&isDirectory);
18676       if (NS_WARN_IF(NS_FAILED(rv))) {
18677         return rv;
18678       }
18679 
18680       if (NS_WARN_IF(!isDirectory)) {
18681         continue;
18682       }
18683 
18684       nsCOMPtr<nsISimpleEnumerator> idbDirEntries;
18685       rv = idbDir->GetDirectoryEntries(getter_AddRefs(idbDirEntries));
18686       if (NS_WARN_IF(NS_FAILED(rv))) {
18687         return rv;
18688       }
18689 
18690       if (!idbDirEntries) {
18691         continue;
18692       }
18693 
18694       nsCString group;
18695       nsCString origin;
18696       nsTArray<nsString> databasePaths;
18697 
18698       while (true) {
18699         // Loop over files in the "idb" directory.
18700         if (IsAborted()) {
18701           return NS_ERROR_ABORT;
18702         }
18703 
18704         bool idbDirHasMoreEntries;
18705         rv = idbDirEntries->HasMoreElements(&idbDirHasMoreEntries);
18706         if (NS_WARN_IF(NS_FAILED(rv))) {
18707           return rv;
18708         }
18709 
18710         if (!idbDirHasMoreEntries) {
18711           break;
18712         }
18713 
18714         nsCOMPtr<nsISupports> idbDirEntry;
18715         rv = idbDirEntries->GetNext(getter_AddRefs(idbDirEntry));
18716         if (NS_WARN_IF(NS_FAILED(rv))) {
18717           return rv;
18718         }
18719 
18720         nsCOMPtr<nsIFile> idbDirFile = do_QueryInterface(idbDirEntry);
18721         MOZ_ASSERT(idbDirFile);
18722 
18723         nsString idbFilePath;
18724         rv = idbDirFile->GetPath(idbFilePath);
18725         if (NS_WARN_IF(NS_FAILED(rv))) {
18726           return rv;
18727         }
18728 
18729         if (!StringEndsWith(idbFilePath, sqliteExtension)) {
18730           continue;
18731         }
18732 
18733         rv = idbDirFile->Exists(&exists);
18734         if (NS_WARN_IF(NS_FAILED(rv))) {
18735           return rv;
18736         }
18737 
18738         MOZ_ASSERT(exists);
18739 
18740         rv = idbDirFile->IsDirectory(&isDirectory);
18741         if (NS_WARN_IF(NS_FAILED(rv))) {
18742           return rv;
18743         }
18744 
18745         if (isDirectory) {
18746           continue;
18747         }
18748 
18749         // Found a database.
18750         if (databasePaths.IsEmpty()) {
18751           MOZ_ASSERT(group.IsEmpty());
18752           MOZ_ASSERT(origin.IsEmpty());
18753 
18754           int64_t dummyTimeStamp;
18755           nsCString dummySuffix;
18756           bool dummyIsApp;
18757           if (NS_WARN_IF(NS_FAILED(
18758                 quotaManager->GetDirectoryMetadata2(originDir,
18759                                                     &dummyTimeStamp,
18760                                                     dummySuffix,
18761                                                     group,
18762                                                     origin,
18763                                                     &dummyIsApp)))) {
18764             // Not much we can do here...
18765             continue;
18766           }
18767         }
18768 
18769         MOZ_ASSERT(!databasePaths.Contains(idbFilePath));
18770 
18771         databasePaths.AppendElement(idbFilePath);
18772       }
18773 
18774       if (!databasePaths.IsEmpty()) {
18775         mDirectoryInfos.AppendElement(DirectoryInfo(persistenceType,
18776                                                     group,
18777                                                     origin,
18778                                                     Move(databasePaths)));
18779       }
18780     }
18781   }
18782 
18783   mState = State::BeginDatabaseMaintenance;
18784 
18785   MOZ_ALWAYS_SUCCEEDS(
18786     mQuotaClient->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL));
18787 
18788   return NS_OK;
18789 }
18790 
18791 nsresult
BeginDatabaseMaintenance()18792 Maintenance::BeginDatabaseMaintenance()
18793 {
18794   AssertIsOnBackgroundThread();
18795   MOZ_ASSERT(mState == State::BeginDatabaseMaintenance);
18796 
18797   class MOZ_STACK_CLASS Helper final
18798   {
18799   public:
18800     static bool
18801     IsSafeToRunMaintenance(const nsAString& aDatabasePath)
18802     {
18803       if (gFactoryOps) {
18804         for (uint32_t index = gFactoryOps->Length(); index > 0; index--) {
18805           RefPtr<FactoryOp>& existingOp = (*gFactoryOps)[index - 1];
18806 
18807           MOZ_ASSERT(!existingOp->DatabaseFilePath().IsEmpty());
18808 
18809           if (existingOp->DatabaseFilePath() == aDatabasePath) {
18810             return false;
18811           }
18812         }
18813       }
18814 
18815       if (gLiveDatabaseHashtable) {
18816         for (auto iter = gLiveDatabaseHashtable->ConstIter();
18817              !iter.Done(); iter.Next()) {
18818           for (Database* database : iter.Data()->mLiveDatabases) {
18819             if (database->FilePath() == aDatabasePath) {
18820               return false;
18821             }
18822           }
18823         }
18824       }
18825 
18826       return true;
18827     }
18828   };
18829 
18830   RefPtr<nsThreadPool> threadPool;
18831 
18832   for (DirectoryInfo& directoryInfo : mDirectoryInfos) {
18833     for (const nsString& databasePath : directoryInfo.mDatabasePaths) {
18834       if (Helper::IsSafeToRunMaintenance(databasePath)) {
18835         RefPtr<DatabaseMaintenance> databaseMaintenance =
18836           new DatabaseMaintenance(this,
18837                                   directoryInfo.mPersistenceType,
18838                                   directoryInfo.mGroup,
18839                                   directoryInfo.mOrigin,
18840                                   databasePath);
18841 
18842         if (!threadPool) {
18843           threadPool = mQuotaClient->GetOrCreateThreadPool();
18844           MOZ_ASSERT(threadPool);
18845         }
18846 
18847         MOZ_ALWAYS_SUCCEEDS(
18848           threadPool->Dispatch(databaseMaintenance, NS_DISPATCH_NORMAL));
18849 
18850         RegisterDatabaseMaintenance(databaseMaintenance);
18851       }
18852     }
18853   }
18854 
18855   mDirectoryInfos.Clear();
18856 
18857   if (mDatabaseMaintenances.Count()) {
18858     mState = State::WaitingForDatabaseMaintenancesToComplete;
18859   } else {
18860     mState = State::Finishing;
18861     Finish();
18862   }
18863 
18864   return NS_OK;
18865 }
18866 
18867 void
Finish()18868 Maintenance::Finish()
18869 {
18870   AssertIsOnBackgroundThread();
18871   MOZ_ASSERT(mState == State::Finishing);
18872 
18873   if (NS_FAILED(mResultCode)) {
18874     nsCString errorName;
18875     GetErrorName(mResultCode, errorName);
18876 
18877     IDB_WARNING("Maintenance finished with error: %s", errorName.get());
18878   }
18879 
18880   mDirectoryLock = nullptr;
18881 
18882   // It can happen that we are only referenced by mCurrentMaintenance which is
18883   // cleared in NoteFinishedMaintenance()
18884   RefPtr<Maintenance> kungFuDeathGrip = this;
18885 
18886   mQuotaClient->NoteFinishedMaintenance(this);
18887 
18888   mState = State::Complete;
18889 }
18890 
NS_IMPL_ISUPPORTS_INHERITED0(Maintenance,Runnable)18891 NS_IMPL_ISUPPORTS_INHERITED0(Maintenance, Runnable)
18892 
18893 NS_IMETHODIMP
18894 Maintenance::Run()
18895 {
18896   MOZ_ASSERT(mState != State::Complete);
18897 
18898   nsresult rv;
18899 
18900   switch (mState) {
18901     case State::Initial:
18902       rv = Start();
18903       break;
18904 
18905     case State::CreateIndexedDatabaseManager:
18906       rv = CreateIndexedDatabaseManager();
18907       break;
18908 
18909     case State::IndexedDatabaseManagerOpen:
18910       rv = OpenDirectory();
18911       break;
18912 
18913     case State::DirectoryWorkOpen:
18914       rv = DirectoryWork();
18915       break;
18916 
18917     case State::BeginDatabaseMaintenance:
18918       rv = BeginDatabaseMaintenance();
18919       break;
18920 
18921     case State::Finishing:
18922       Finish();
18923       return NS_OK;
18924 
18925     default:
18926       MOZ_CRASH("Bad state!");
18927   }
18928 
18929   if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) {
18930     if (NS_SUCCEEDED(mResultCode)) {
18931       mResultCode = rv;
18932     }
18933 
18934     // Must set mState before dispatching otherwise we will race with the owning
18935     // thread.
18936     mState = State::Finishing;
18937 
18938     if (IsOnBackgroundThread()) {
18939       Finish();
18940     } else {
18941       MOZ_ALWAYS_SUCCEEDS(
18942         mQuotaClient->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL));
18943     }
18944   }
18945 
18946   return NS_OK;
18947 }
18948 
18949 void
DirectoryLockAcquired(DirectoryLock * aLock)18950 Maintenance::DirectoryLockAcquired(DirectoryLock* aLock)
18951 {
18952   AssertIsOnBackgroundThread();
18953   MOZ_ASSERT(mState == State::DirectoryOpenPending);
18954   MOZ_ASSERT(!mDirectoryLock);
18955 
18956   mDirectoryLock = aLock;
18957 
18958   nsresult rv = DirectoryOpen();
18959   if (NS_WARN_IF(NS_FAILED(rv))) {
18960     if (NS_SUCCEEDED(mResultCode)) {
18961       mResultCode = rv;
18962     }
18963 
18964     mState = State::Finishing;
18965     Finish();
18966 
18967     return;
18968   }
18969 }
18970 
18971 void
DirectoryLockFailed()18972 Maintenance::DirectoryLockFailed()
18973 {
18974   AssertIsOnBackgroundThread();
18975   MOZ_ASSERT(mState == State::DirectoryOpenPending);
18976   MOZ_ASSERT(!mDirectoryLock);
18977 
18978   if (NS_SUCCEEDED(mResultCode)) {
18979     mResultCode = NS_ERROR_FAILURE;
18980   }
18981 
18982   mState = State::Finishing;
18983   Finish();
18984 }
18985 
18986 void
PerformMaintenanceOnDatabase()18987 DatabaseMaintenance::PerformMaintenanceOnDatabase()
18988 {
18989   MOZ_ASSERT(!NS_IsMainThread());
18990   MOZ_ASSERT(!IsOnBackgroundThread());
18991   MOZ_ASSERT(mMaintenance);
18992   MOZ_ASSERT(mMaintenance->StartTime());
18993   MOZ_ASSERT(!mDatabasePath.IsEmpty());
18994   MOZ_ASSERT(!mGroup.IsEmpty());
18995   MOZ_ASSERT(!mOrigin.IsEmpty());
18996 
18997   class MOZ_STACK_CLASS AutoClose final
18998   {
18999     nsCOMPtr<mozIStorageConnection> mConnection;
19000 
19001   public:
19002     explicit AutoClose(mozIStorageConnection* aConnection)
19003       : mConnection(aConnection)
19004     {
19005       MOZ_ASSERT(aConnection);
19006     }
19007 
19008     ~AutoClose()
19009     {
19010       MOZ_ASSERT(mConnection);
19011 
19012       MOZ_ALWAYS_SUCCEEDS(mConnection->Close());
19013     }
19014   };
19015 
19016   nsCOMPtr<nsIFile> databaseFile = GetFileForPath(mDatabasePath);
19017   MOZ_ASSERT(databaseFile);
19018 
19019   nsCOMPtr<mozIStorageConnection> connection;
19020   nsresult rv = GetStorageConnection(databaseFile,
19021                                      mPersistenceType,
19022                                      mGroup,
19023                                      mOrigin,
19024                                      TelemetryIdForFile(databaseFile),
19025                                      getter_AddRefs(connection));
19026   if (NS_WARN_IF(NS_FAILED(rv))) {
19027     return;
19028   }
19029 
19030   AutoClose autoClose(connection);
19031 
19032   if (mMaintenance->IsAborted()) {
19033     return;
19034   }
19035 
19036   AutoProgressHandler progressHandler(mMaintenance);
19037   if (NS_WARN_IF(NS_FAILED(progressHandler.Register(connection)))) {
19038     return;
19039   }
19040 
19041   bool databaseIsOk;
19042   rv = CheckIntegrity(connection, &databaseIsOk);
19043   if (NS_WARN_IF(NS_FAILED(rv))) {
19044     return;
19045   }
19046 
19047   if (NS_WARN_IF(!databaseIsOk)) {
19048     // XXX Handle this somehow! Probably need to clear all storage for the
19049     //     origin. Needs followup.
19050     MOZ_ASSERT(false, "Database corruption detected!");
19051     return;
19052   }
19053 
19054   if (mMaintenance->IsAborted()) {
19055     return;
19056   }
19057 
19058   MaintenanceAction maintenanceAction;
19059   rv = DetermineMaintenanceAction(connection, databaseFile, &maintenanceAction);
19060   if (NS_WARN_IF(NS_FAILED(rv))) {
19061     return;
19062   }
19063 
19064   if (mMaintenance->IsAborted()) {
19065     return;
19066   }
19067 
19068   switch (maintenanceAction) {
19069     case MaintenanceAction::Nothing:
19070       break;
19071 
19072     case MaintenanceAction::IncrementalVacuum:
19073       IncrementalVacuum(connection);
19074       break;
19075 
19076     case MaintenanceAction::FullVacuum:
19077       FullVacuum(connection, databaseFile);
19078       break;
19079 
19080     default:
19081       MOZ_CRASH("Unknown MaintenanceAction!");
19082   }
19083 }
19084 
19085 nsresult
CheckIntegrity(mozIStorageConnection * aConnection,bool * aOk)19086 DatabaseMaintenance::CheckIntegrity(mozIStorageConnection* aConnection,
19087                                     bool* aOk)
19088 {
19089   MOZ_ASSERT(!NS_IsMainThread());
19090   MOZ_ASSERT(!IsOnBackgroundThread());
19091   MOZ_ASSERT(aConnection);
19092   MOZ_ASSERT(aOk);
19093 
19094   nsresult rv;
19095 
19096   // First do a full integrity_check. Scope statements tightly here because
19097   // later operations require zero live statements.
19098   {
19099     nsCOMPtr<mozIStorageStatement> stmt;
19100     rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19101       "PRAGMA integrity_check(1);"
19102     ), getter_AddRefs(stmt));
19103     if (NS_WARN_IF(NS_FAILED(rv))) {
19104       return rv;
19105     }
19106 
19107     bool hasResult;
19108     rv = stmt->ExecuteStep(&hasResult);
19109     if (NS_WARN_IF(NS_FAILED(rv))) {
19110       return rv;
19111     }
19112 
19113     MOZ_ASSERT(hasResult);
19114 
19115     nsString result;
19116     rv = stmt->GetString(0, result);
19117     if (NS_WARN_IF(NS_FAILED(rv))) {
19118       return rv;
19119     }
19120 
19121     if (NS_WARN_IF(!result.EqualsLiteral("ok"))) {
19122       *aOk = false;
19123       return NS_OK;
19124     }
19125   }
19126 
19127   // Now enable and check for foreign key constraints.
19128   {
19129     int32_t foreignKeysWereEnabled;
19130     {
19131       nsCOMPtr<mozIStorageStatement> stmt;
19132       rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19133         "PRAGMA foreign_keys;"
19134       ), getter_AddRefs(stmt));
19135       if (NS_WARN_IF(NS_FAILED(rv))) {
19136         return rv;
19137       }
19138 
19139       bool hasResult;
19140       rv = stmt->ExecuteStep(&hasResult);
19141       if (NS_WARN_IF(NS_FAILED(rv))) {
19142         return rv;
19143       }
19144 
19145       MOZ_ASSERT(hasResult);
19146 
19147       rv = stmt->GetInt32(0, &foreignKeysWereEnabled);
19148       if (NS_WARN_IF(NS_FAILED(rv))) {
19149         return rv;
19150       }
19151     }
19152 
19153     if (!foreignKeysWereEnabled) {
19154       rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
19155         "PRAGMA foreign_keys = ON;"));
19156       if (NS_WARN_IF(NS_FAILED(rv))) {
19157         return rv;
19158       }
19159     }
19160 
19161     bool foreignKeyError;
19162     {
19163       nsCOMPtr<mozIStorageStatement> stmt;
19164       rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19165         "PRAGMA foreign_key_check;"
19166       ), getter_AddRefs(stmt));
19167       if (NS_WARN_IF(NS_FAILED(rv))) {
19168         return rv;
19169       }
19170 
19171       rv = stmt->ExecuteStep(&foreignKeyError);
19172       if (NS_WARN_IF(NS_FAILED(rv))) {
19173         return rv;
19174       }
19175     }
19176 
19177     if (!foreignKeysWereEnabled) {
19178       nsAutoCString stmtSQL;
19179       stmtSQL.AssignLiteral("PRAGMA foreign_keys = ");
19180       stmtSQL.AppendLiteral("OFF");
19181       stmtSQL.Append(';');
19182 
19183       rv = aConnection->ExecuteSimpleSQL(stmtSQL);
19184       if (NS_WARN_IF(NS_FAILED(rv))) {
19185         return rv;
19186       }
19187     }
19188 
19189     if (foreignKeyError) {
19190       *aOk = false;
19191       return NS_OK;
19192     }
19193   }
19194 
19195   *aOk = true;
19196   return NS_OK;
19197 }
19198 
19199 nsresult
DetermineMaintenanceAction(mozIStorageConnection * aConnection,nsIFile * aDatabaseFile,MaintenanceAction * aMaintenanceAction)19200 DatabaseMaintenance::DetermineMaintenanceAction(
19201                                           mozIStorageConnection* aConnection,
19202                                           nsIFile* aDatabaseFile,
19203                                           MaintenanceAction* aMaintenanceAction)
19204 {
19205   MOZ_ASSERT(!NS_IsMainThread());
19206   MOZ_ASSERT(!IsOnBackgroundThread());
19207   MOZ_ASSERT(aConnection);
19208   MOZ_ASSERT(aDatabaseFile);
19209   MOZ_ASSERT(aMaintenanceAction);
19210 
19211   int32_t schemaVersion;
19212   nsresult rv = aConnection->GetSchemaVersion(&schemaVersion);
19213   if (NS_WARN_IF(NS_FAILED(rv))) {
19214     return rv;
19215   }
19216 
19217   // Don't do anything if the schema version is less than 18; before that
19218   // version no databases had |auto_vacuum == INCREMENTAL| set and we didn't
19219   // track the values needed for the heuristics below.
19220   if (schemaVersion < MakeSchemaVersion(18, 0)) {
19221     *aMaintenanceAction = MaintenanceAction::Nothing;
19222     return NS_OK;
19223   }
19224 
19225   bool lowDiskSpace = IndexedDatabaseManager::InLowDiskSpaceMode();
19226 
19227   if (QuotaManager::IsRunningXPCShellTests()) {
19228     // If we're running XPCShell then we want to test both the low disk space
19229     // and normal disk space code paths so pick semi-randomly based on the
19230     // current time.
19231     lowDiskSpace = ((PR_Now() / PR_USEC_PER_MSEC) % 2) == 0;
19232   }
19233 
19234   // If we're low on disk space then the best we can hope for is that an
19235   // incremental vacuum might free some space. That is a journaled operation so
19236   // it may not be possible even then.
19237   if (lowDiskSpace) {
19238     *aMaintenanceAction = MaintenanceAction::IncrementalVacuum;
19239     return NS_OK;
19240   }
19241 
19242   // This method shouldn't make any permanent changes to the database, so make
19243   // sure everything gets rolled back when we leave.
19244   mozStorageTransaction transaction(aConnection,
19245                                     /* aCommitOnComplete */ false);
19246 
19247   // Check to see when we last vacuumed this database.
19248   nsCOMPtr<mozIStorageStatement> stmt;
19249   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19250     "SELECT last_vacuum_time, last_vacuum_size "
19251       "FROM database;"
19252   ), getter_AddRefs(stmt));
19253   if (NS_WARN_IF(NS_FAILED(rv))) {
19254     return rv;
19255   }
19256 
19257   bool hasResult;
19258   rv = stmt->ExecuteStep(&hasResult);
19259   if (NS_WARN_IF(NS_FAILED(rv))) {
19260     return rv;
19261   }
19262 
19263   MOZ_ASSERT(hasResult);
19264 
19265   PRTime lastVacuumTime;
19266   rv = stmt->GetInt64(0, &lastVacuumTime);
19267   if (NS_WARN_IF(NS_FAILED(rv))) {
19268     return rv;
19269   }
19270 
19271   int64_t lastVacuumSize;
19272   rv = stmt->GetInt64(1, &lastVacuumSize);
19273   if (NS_WARN_IF(NS_FAILED(rv))) {
19274     return rv;
19275   }
19276 
19277   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.");
19278 
19279   PRTime startTime = mMaintenance->StartTime();
19280 
19281   // This shouldn't really be possible...
19282   if (NS_WARN_IF(startTime <= lastVacuumTime)) {
19283     *aMaintenanceAction = MaintenanceAction::Nothing;
19284     return NS_OK;
19285   }
19286 
19287   if (startTime - lastVacuumTime < kMinVacuumAge) {
19288     *aMaintenanceAction = MaintenanceAction::IncrementalVacuum;
19289     return NS_OK;
19290   }
19291 
19292   // It has been more than a week since the database was vacuumed, so gather
19293   // statistics on its usage to see if vacuuming is worthwhile.
19294 
19295   // Create a temporary copy of the dbstat table to speed up the queries that
19296   // come later.
19297   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
19298     "CREATE VIRTUAL TABLE __stats__ USING dbstat;"
19299     "CREATE TEMP TABLE __temp_stats__ AS SELECT * FROM __stats__;"
19300   ));
19301   if (NS_WARN_IF(NS_FAILED(rv))) {
19302     return rv;
19303   }
19304 
19305   // Calculate the percentage of the database pages that are not in contiguous
19306   // order.
19307   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19308     "SELECT SUM(__ts1__.pageno != __ts2__.pageno + 1) * 100.0 / COUNT(*) "
19309       "FROM __temp_stats__ AS __ts1__, __temp_stats__ AS __ts2__ "
19310       "WHERE __ts1__.name = __ts2__.name "
19311       "AND __ts1__.rowid = __ts2__.rowid + 1;"
19312   ), getter_AddRefs(stmt));
19313   if (NS_WARN_IF(NS_FAILED(rv))) {
19314     return rv;
19315   }
19316 
19317   rv = stmt->ExecuteStep(&hasResult);
19318   if (NS_WARN_IF(NS_FAILED(rv))) {
19319     return rv;
19320   }
19321 
19322   MOZ_ASSERT(hasResult);
19323 
19324   int32_t percentUnordered;
19325   rv = stmt->GetInt32(0, &percentUnordered);
19326   if (NS_WARN_IF(NS_FAILED(rv))) {
19327     return rv;
19328   }
19329 
19330   MOZ_ASSERT(percentUnordered >= 0);
19331   MOZ_ASSERT(percentUnordered <= 100);
19332 
19333   if (percentUnordered >= kPercentUnorderedThreshold) {
19334     *aMaintenanceAction = MaintenanceAction::FullVacuum;
19335     return NS_OK;
19336   }
19337 
19338   // Don't try a full vacuum if the file hasn't grown by 10%.
19339   int64_t currentFileSize;
19340   rv = aDatabaseFile->GetFileSize(&currentFileSize);
19341   if (NS_WARN_IF(NS_FAILED(rv))) {
19342     return rv;
19343   }
19344 
19345   if (currentFileSize <= lastVacuumSize ||
19346       (((currentFileSize - lastVacuumSize) * 100 / currentFileSize) <
19347          kPercentFileSizeGrowthThreshold)) {
19348     *aMaintenanceAction = MaintenanceAction::IncrementalVacuum;
19349     return NS_OK;
19350   }
19351 
19352   // See if there are any free pages that we can reclaim.
19353   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19354     "PRAGMA freelist_count;"
19355   ), getter_AddRefs(stmt));
19356   if (NS_WARN_IF(NS_FAILED(rv))) {
19357     return rv;
19358   }
19359 
19360   rv = stmt->ExecuteStep(&hasResult);
19361   if (NS_WARN_IF(NS_FAILED(rv))) {
19362     return rv;
19363   }
19364 
19365   MOZ_ASSERT(hasResult);
19366 
19367   int32_t freelistCount;
19368   rv = stmt->GetInt32(0, &freelistCount);
19369   if (NS_WARN_IF(NS_FAILED(rv))) {
19370     return rv;
19371   }
19372 
19373   MOZ_ASSERT(freelistCount >= 0);
19374 
19375   // If we have too many free pages then we should try an incremental vacuum. If
19376   // that causes too much fragmentation then we'll try a full vacuum later.
19377   if (freelistCount > kMaxFreelistThreshold) {
19378     *aMaintenanceAction = MaintenanceAction::IncrementalVacuum;
19379     return NS_OK;
19380   }
19381 
19382   // Calculate the percentage of unused bytes on pages in the database.
19383   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19384     "SELECT SUM(unused) * 100.0 / SUM(pgsize) "
19385       "FROM __temp_stats__;"
19386   ), getter_AddRefs(stmt));
19387   if (NS_WARN_IF(NS_FAILED(rv))) {
19388     return rv;
19389   }
19390 
19391   rv = stmt->ExecuteStep(&hasResult);
19392   if (NS_WARN_IF(NS_FAILED(rv))) {
19393     return rv;
19394   }
19395 
19396   MOZ_ASSERT(hasResult);
19397 
19398   int32_t percentUnused;
19399   rv = stmt->GetInt32(0, &percentUnused);
19400   if (NS_WARN_IF(NS_FAILED(rv))) {
19401     return rv;
19402   }
19403 
19404   MOZ_ASSERT(percentUnused >= 0);
19405   MOZ_ASSERT(percentUnused <= 100);
19406 
19407   *aMaintenanceAction = percentUnused >= kPercentUnusedThreshold ?
19408                         MaintenanceAction::FullVacuum :
19409                         MaintenanceAction::IncrementalVacuum;
19410   return NS_OK;
19411 }
19412 
19413 void
IncrementalVacuum(mozIStorageConnection * aConnection)19414 DatabaseMaintenance::IncrementalVacuum(mozIStorageConnection* aConnection)
19415 {
19416   MOZ_ASSERT(!NS_IsMainThread());
19417   MOZ_ASSERT(!IsOnBackgroundThread());
19418   MOZ_ASSERT(aConnection);
19419 
19420   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
19421     "PRAGMA incremental_vacuum;"
19422   ));
19423   if (NS_WARN_IF(NS_FAILED(rv))) {
19424     return;
19425   }
19426 }
19427 
19428 void
FullVacuum(mozIStorageConnection * aConnection,nsIFile * aDatabaseFile)19429 DatabaseMaintenance::FullVacuum(mozIStorageConnection* aConnection,
19430                                 nsIFile* aDatabaseFile)
19431 {
19432   MOZ_ASSERT(!NS_IsMainThread());
19433   MOZ_ASSERT(!IsOnBackgroundThread());
19434   MOZ_ASSERT(aConnection);
19435   MOZ_ASSERT(aDatabaseFile);
19436 
19437   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
19438     "VACUUM;"
19439   ));
19440   if (NS_WARN_IF(NS_FAILED(rv))) {
19441     return;
19442   }
19443 
19444   PRTime vacuumTime = PR_Now();
19445   MOZ_ASSERT(vacuumTime > 0);
19446 
19447   int64_t fileSize;
19448   rv = aDatabaseFile->GetFileSize(&fileSize);
19449   if (NS_WARN_IF(NS_FAILED(rv))) {
19450     return;
19451   }
19452 
19453   MOZ_ASSERT(fileSize > 0);
19454 
19455   nsCOMPtr<mozIStorageStatement> stmt;
19456   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
19457     "UPDATE database "
19458       "SET last_vacuum_time = :time"
19459         ", last_vacuum_size = :size;"
19460   ), getter_AddRefs(stmt));
19461   if (NS_WARN_IF(NS_FAILED(rv))) {
19462     return;
19463   }
19464 
19465   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("time"), vacuumTime);
19466   if (NS_WARN_IF(NS_FAILED(rv))) {
19467     return;
19468   }
19469 
19470   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("size"), fileSize);
19471   if (NS_WARN_IF(NS_FAILED(rv))) {
19472     return;
19473   }
19474 
19475   rv = stmt->Execute();
19476   if (NS_WARN_IF(NS_FAILED(rv))) {
19477     return;
19478   }
19479 }
19480 
19481 void
RunOnOwningThread()19482 DatabaseMaintenance::RunOnOwningThread()
19483 {
19484   AssertIsOnBackgroundThread();
19485 
19486   if (mCompleteCallback) {
19487     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mCompleteCallback.forget()));
19488   }
19489 
19490   mMaintenance->UnregisterDatabaseMaintenance(this);
19491 }
19492 
19493 void
RunOnConnectionThread()19494 DatabaseMaintenance::RunOnConnectionThread()
19495 {
19496   MOZ_ASSERT(!NS_IsMainThread());
19497   MOZ_ASSERT(!IsOnBackgroundThread());
19498 
19499   PerformMaintenanceOnDatabase();
19500 
19501   MOZ_ALWAYS_SUCCEEDS(
19502     mMaintenance->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL));
19503 }
19504 
19505 NS_IMETHODIMP
Run()19506 DatabaseMaintenance::Run()
19507 {
19508   if (IsOnBackgroundThread()) {
19509     RunOnOwningThread();
19510   } else {
19511     RunOnConnectionThread();
19512   }
19513 
19514   return NS_OK;
19515 }
19516 
19517 nsresult
19518 DatabaseMaintenance::
Register(mozIStorageConnection * aConnection)19519 AutoProgressHandler::Register(mozIStorageConnection* aConnection)
19520 {
19521   MOZ_ASSERT(!NS_IsMainThread());
19522   MOZ_ASSERT(!IsOnBackgroundThread());
19523   MOZ_ASSERT(aConnection);
19524 
19525   // We want to quickly bail out of any operation if the user becomes active, so
19526   // use a small granularity here since database performance isn't critical.
19527   static const int32_t kProgressGranularity = 50;
19528 
19529   nsCOMPtr<mozIStorageProgressHandler> oldHandler;
19530   nsresult rv = aConnection->SetProgressHandler(kProgressGranularity,
19531                                                 this,
19532                                                 getter_AddRefs(oldHandler));
19533   if (NS_WARN_IF(NS_FAILED(rv))) {
19534     return rv;
19535   }
19536 
19537   MOZ_ASSERT(!oldHandler);
19538   mConnection = aConnection;
19539 
19540   return NS_OK;
19541 }
19542 
19543 void
19544 DatabaseMaintenance::
Unregister()19545 AutoProgressHandler::Unregister()
19546 {
19547   MOZ_ASSERT(!NS_IsMainThread());
19548   MOZ_ASSERT(!IsOnBackgroundThread());
19549   MOZ_ASSERT(mConnection);
19550 
19551   nsCOMPtr<mozIStorageProgressHandler> oldHandler;
19552   nsresult rv = mConnection->RemoveProgressHandler(getter_AddRefs(oldHandler));
19553   Unused << NS_WARN_IF(NS_FAILED(rv));
19554 
19555   MOZ_ASSERT_IF(NS_SUCCEEDED(rv), oldHandler == this);
19556 }
19557 
NS_IMETHODIMP_(MozExternalRefCountType)19558 NS_IMETHODIMP_(MozExternalRefCountType)
19559 DatabaseMaintenance::
19560 AutoProgressHandler::AddRef()
19561 {
19562   NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
19563 
19564 #ifdef DEBUG
19565   mDEBUGRefCnt++;
19566 #endif
19567   return 2;
19568 }
19569 
NS_IMETHODIMP_(MozExternalRefCountType)19570 NS_IMETHODIMP_(MozExternalRefCountType)
19571 DatabaseMaintenance::
19572 AutoProgressHandler::Release()
19573 {
19574   NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
19575 
19576 #ifdef DEBUG
19577   mDEBUGRefCnt--;
19578 #endif
19579   return 1;
19580 }
19581 
NS_IMPL_QUERY_INTERFACE(DatabaseMaintenance::AutoProgressHandler,mozIStorageProgressHandler)19582 NS_IMPL_QUERY_INTERFACE(DatabaseMaintenance::AutoProgressHandler,
19583                         mozIStorageProgressHandler)
19584 
19585 NS_IMETHODIMP
19586 DatabaseMaintenance::
19587 AutoProgressHandler::OnProgress(mozIStorageConnection* aConnection,
19588                                 bool* _retval)
19589 {
19590   NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
19591   MOZ_ASSERT(aConnection);
19592   MOZ_ASSERT(mConnection == aConnection);
19593   MOZ_ASSERT(_retval);
19594 
19595   *_retval = mMaintenance->IsAborted();
19596 
19597   return NS_OK;
19598 }
19599 
19600 /*******************************************************************************
19601  * Local class implementations
19602  ******************************************************************************/
19603 
NS_IMPL_ISUPPORTS(CompressDataBlobsFunction,mozIStorageFunction)19604 NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction)
19605 NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction)
19606 
19607 #if !defined(MOZ_B2G)
19608 
19609 nsresult
19610 UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory,
19611                              mozIStorageConnection* aConnection)
19612 {
19613   // This file manager doesn't need real origin info, etc. The only purpose is
19614   // to store file ids without adding more complexity or code duplication.
19615   RefPtr<FileManager> fileManager =
19616     new FileManager(PERSISTENCE_TYPE_INVALID,
19617                     EmptyCString(),
19618                     EmptyCString(),
19619                     false,
19620                     EmptyString(),
19621                     false);
19622 
19623   nsresult rv = fileManager->Init(aFMDirectory, aConnection);
19624   if (NS_WARN_IF(NS_FAILED(rv))) {
19625     return rv;
19626   }
19627 
19628   nsAutoPtr<NormalJSContext> context(NormalJSContext::Create());
19629   if (NS_WARN_IF(!context)) {
19630     return NS_ERROR_FAILURE;
19631   }
19632 
19633   mFileManager.swap(fileManager);
19634   mContext = context;
19635   return NS_OK;
19636 }
19637 
NS_IMPL_ISUPPORTS(UpgradeFileIdsFunction,mozIStorageFunction)19638 NS_IMPL_ISUPPORTS(UpgradeFileIdsFunction, mozIStorageFunction)
19639 
19640 NS_IMETHODIMP
19641 UpgradeFileIdsFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
19642                                        nsIVariant** aResult)
19643 {
19644   MOZ_ASSERT(aArguments);
19645   MOZ_ASSERT(aResult);
19646   MOZ_ASSERT(mFileManager);
19647   MOZ_ASSERT(mContext);
19648 
19649   PROFILER_LABEL("IndexedDB",
19650                  "UpgradeFileIdsFunction::OnFunctionCall",
19651                  js::ProfileEntry::Category::STORAGE);
19652 
19653   uint32_t argc;
19654   nsresult rv = aArguments->GetNumEntries(&argc);
19655   if (NS_WARN_IF(NS_FAILED(rv))) {
19656     return rv;
19657   }
19658 
19659   if (argc != 2) {
19660     NS_WARNING("Don't call me with the wrong number of arguments!");
19661     return NS_ERROR_UNEXPECTED;
19662   }
19663 
19664   StructuredCloneReadInfo cloneInfo;
19665   DatabaseOperationBase::GetStructuredCloneReadInfoFromValueArray(aArguments,
19666                                                                   1,
19667                                                                   0,
19668                                                                   mFileManager,
19669                                                                   &cloneInfo);
19670 
19671   JSContext* cx = mContext->Context();
19672   JSAutoRequest ar(cx);
19673   JSAutoCompartment ac(cx, mContext->Global());
19674 
19675   JS::Rooted<JS::Value> clone(cx);
19676   if (NS_WARN_IF(!IDBObjectStore::DeserializeUpgradeValue(cx, cloneInfo,
19677                                                           &clone))) {
19678     return NS_ERROR_DOM_DATA_CLONE_ERR;
19679   }
19680 
19681   nsAutoString fileIds;
19682 
19683   for (uint32_t count = cloneInfo.mFiles.Length(), index = 0;
19684        index < count;
19685        index++) {
19686     StructuredCloneFile& file = cloneInfo.mFiles[index];
19687     MOZ_ASSERT(file.mFileInfo);
19688 
19689     const int64_t id = file.mFileInfo->Id();
19690 
19691     if (index) {
19692       fileIds.Append(' ');
19693     }
19694     fileIds.AppendInt(file.mType == StructuredCloneFile::eBlob ? id : -id);
19695   }
19696 
19697   nsCOMPtr<nsIVariant> result = new mozilla::storage::TextVariant(fileIds);
19698 
19699   result.forget(aResult);
19700   return NS_OK;
19701 }
19702 
19703 #endif // MOZ_B2G
19704 
19705 // static
19706 void
GetBindingClauseForKeyRange(const SerializedKeyRange & aKeyRange,const nsACString & aKeyColumnName,nsAutoCString & aBindingClause)19707 DatabaseOperationBase::GetBindingClauseForKeyRange(
19708                                             const SerializedKeyRange& aKeyRange,
19709                                             const nsACString& aKeyColumnName,
19710                                             nsAutoCString& aBindingClause)
19711 {
19712   MOZ_ASSERT(!IsOnBackgroundThread());
19713   MOZ_ASSERT(!aKeyColumnName.IsEmpty());
19714 
19715   NS_NAMED_LITERAL_CSTRING(andStr, " AND ");
19716   NS_NAMED_LITERAL_CSTRING(spacecolon, " :");
19717   NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key");
19718 
19719   if (aKeyRange.isOnly()) {
19720     // Both keys equal.
19721     aBindingClause = andStr + aKeyColumnName + NS_LITERAL_CSTRING(" =") +
19722                      spacecolon + lowerKey;
19723     return;
19724   }
19725 
19726   aBindingClause.Truncate();
19727 
19728   if (!aKeyRange.lower().IsUnset()) {
19729     // Lower key is set.
19730     aBindingClause.Append(andStr + aKeyColumnName);
19731     aBindingClause.AppendLiteral(" >");
19732     if (!aKeyRange.lowerOpen()) {
19733       aBindingClause.AppendLiteral("=");
19734     }
19735     aBindingClause.Append(spacecolon + lowerKey);
19736   }
19737 
19738   if (!aKeyRange.upper().IsUnset()) {
19739     // Upper key is set.
19740     aBindingClause.Append(andStr + aKeyColumnName);
19741     aBindingClause.AppendLiteral(" <");
19742     if (!aKeyRange.upperOpen()) {
19743       aBindingClause.AppendLiteral("=");
19744     }
19745     aBindingClause.Append(spacecolon + NS_LITERAL_CSTRING("upper_key"));
19746   }
19747 
19748   MOZ_ASSERT(!aBindingClause.IsEmpty());
19749 }
19750 
19751 // static
19752 uint64_t
ReinterpretDoubleAsUInt64(double aDouble)19753 DatabaseOperationBase::ReinterpretDoubleAsUInt64(double aDouble)
19754 {
19755   // This is a duplicate of the js engine's byte munging in StructuredClone.cpp
19756   return BitwiseCast<uint64_t>(aDouble);
19757 }
19758 
19759 // static
19760 template <typename T>
19761 nsresult
GetStructuredCloneReadInfoFromSource(T * aSource,uint32_t aDataIndex,uint32_t aFileIdsIndex,FileManager * aFileManager,StructuredCloneReadInfo * aInfo)19762 DatabaseOperationBase::GetStructuredCloneReadInfoFromSource(
19763                                                  T* aSource,
19764                                                  uint32_t aDataIndex,
19765                                                  uint32_t aFileIdsIndex,
19766                                                  FileManager* aFileManager,
19767                                                  StructuredCloneReadInfo* aInfo)
19768 {
19769   MOZ_ASSERT(!IsOnBackgroundThread());
19770   MOZ_ASSERT(aSource);
19771   MOZ_ASSERT(aFileManager);
19772   MOZ_ASSERT(aInfo);
19773 
19774   int32_t columnType;
19775   nsresult rv = aSource->GetTypeOfIndex(aDataIndex, &columnType);
19776   if (NS_WARN_IF(NS_FAILED(rv))) {
19777     return rv;
19778   }
19779 
19780   MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_BLOB ||
19781              columnType == mozIStorageStatement::VALUE_TYPE_INTEGER);
19782 
19783   bool isNull;
19784   rv = aSource->GetIsNull(aFileIdsIndex, &isNull);
19785   if (NS_WARN_IF(NS_FAILED(rv))) {
19786     return rv;
19787   }
19788 
19789   nsString fileIds;
19790 
19791   if (isNull) {
19792     fileIds.SetIsVoid(true);
19793   } else {
19794     rv = aSource->GetString(aFileIdsIndex, fileIds);
19795     if (NS_WARN_IF(NS_FAILED(rv))) {
19796       return rv;
19797     }
19798   }
19799 
19800   if (columnType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
19801     uint64_t intData;
19802     rv = aSource->GetInt64(aDataIndex, reinterpret_cast<int64_t*>(&intData));
19803     if (NS_WARN_IF(NS_FAILED(rv))) {
19804       return rv;
19805     }
19806 
19807     rv = GetStructuredCloneReadInfoFromExternalBlob(intData,
19808                                                     aFileManager,
19809                                                     fileIds,
19810                                                     aInfo);
19811   } else {
19812     const uint8_t* blobData;
19813     uint32_t blobDataLength;
19814     nsresult rv =
19815       aSource->GetSharedBlob(aDataIndex, &blobDataLength, &blobData);
19816     if (NS_WARN_IF(NS_FAILED(rv))) {
19817       return rv;
19818     }
19819 
19820     rv = GetStructuredCloneReadInfoFromBlob(blobData,
19821                                             blobDataLength,
19822                                             aFileManager,
19823                                             fileIds,
19824                                             aInfo);
19825   }
19826   if (NS_WARN_IF(NS_FAILED(rv))) {
19827     return rv;
19828   }
19829 
19830   return NS_OK;
19831 }
19832 
19833 // static
19834 nsresult
GetStructuredCloneReadInfoFromBlob(const uint8_t * aBlobData,uint32_t aBlobDataLength,FileManager * aFileManager,const nsAString & aFileIds,StructuredCloneReadInfo * aInfo)19835 DatabaseOperationBase::GetStructuredCloneReadInfoFromBlob(
19836                                                  const uint8_t* aBlobData,
19837                                                  uint32_t aBlobDataLength,
19838                                                  FileManager* aFileManager,
19839                                                  const nsAString& aFileIds,
19840                                                  StructuredCloneReadInfo* aInfo)
19841 {
19842   MOZ_ASSERT(!IsOnBackgroundThread());
19843   MOZ_ASSERT(aFileManager);
19844   MOZ_ASSERT(aInfo);
19845 
19846   PROFILER_LABEL("IndexedDB",
19847                  "DatabaseOperationBase::GetStructuredCloneReadInfoFromBlob",
19848                  js::ProfileEntry::Category::STORAGE);
19849 
19850   const char* compressed = reinterpret_cast<const char*>(aBlobData);
19851   size_t compressedLength = size_t(aBlobDataLength);
19852 
19853   size_t uncompressedLength;
19854   if (NS_WARN_IF(!snappy::GetUncompressedLength(compressed, compressedLength,
19855                                                 &uncompressedLength))) {
19856     return NS_ERROR_FILE_CORRUPTED;
19857   }
19858 
19859   AutoTArray<uint8_t, 512> uncompressed;
19860   if (NS_WARN_IF(!uncompressed.SetLength(uncompressedLength, fallible))) {
19861     return NS_ERROR_OUT_OF_MEMORY;
19862   }
19863 
19864   char* uncompressedBuffer = reinterpret_cast<char*>(uncompressed.Elements());
19865 
19866   if (NS_WARN_IF(!snappy::RawUncompress(compressed, compressedLength,
19867                                         uncompressedBuffer))) {
19868     return NS_ERROR_FILE_CORRUPTED;
19869   }
19870 
19871   if (!aInfo->mData.WriteBytes(uncompressedBuffer, uncompressed.Length())) {
19872     return NS_ERROR_OUT_OF_MEMORY;
19873   }
19874 
19875   if (!aFileIds.IsVoid()) {
19876     nsresult rv = DeserializeStructuredCloneFiles(aFileManager,
19877                                                   aFileIds,
19878                                                   aInfo->mFiles,
19879                                                   &aInfo->mHasPreprocessInfo);
19880     if (NS_WARN_IF(NS_FAILED(rv))) {
19881       return rv;
19882     }
19883   }
19884 
19885   return NS_OK;
19886 }
19887 
19888 // static
19889 nsresult
GetStructuredCloneReadInfoFromExternalBlob(uint64_t aIntData,FileManager * aFileManager,const nsAString & aFileIds,StructuredCloneReadInfo * aInfo)19890 DatabaseOperationBase::GetStructuredCloneReadInfoFromExternalBlob(
19891                                                  uint64_t aIntData,
19892                                                  FileManager* aFileManager,
19893                                                  const nsAString& aFileIds,
19894                                                  StructuredCloneReadInfo* aInfo)
19895 {
19896   MOZ_ASSERT(!IsOnBackgroundThread());
19897   MOZ_ASSERT(aFileManager);
19898   MOZ_ASSERT(aInfo);
19899 
19900   PROFILER_LABEL(
19901             "IndexedDB",
19902             "DatabaseOperationBase::GetStructuredCloneReadInfoFromExternalBlob",
19903             js::ProfileEntry::Category::STORAGE);
19904 
19905   nsresult rv;
19906 
19907   if (!aFileIds.IsVoid()) {
19908     rv = DeserializeStructuredCloneFiles(aFileManager,
19909                                          aFileIds,
19910                                          aInfo->mFiles,
19911                                          &aInfo->mHasPreprocessInfo);
19912     if (NS_WARN_IF(NS_FAILED(rv))) {
19913       return rv;
19914     }
19915   }
19916 
19917   // Higher and lower 32 bits described
19918   // in ObjectStoreAddOrPutRequestOp::DoDatabaseWork.
19919   uint32_t index = uint32_t(aIntData & 0xFFFFFFFF);
19920 
19921   if (index >= aInfo->mFiles.Length()) {
19922     MOZ_ASSERT(false, "Bad index value!");
19923     return NS_ERROR_UNEXPECTED;
19924   }
19925 
19926   StructuredCloneFile& file = aInfo->mFiles[index];
19927   MOZ_ASSERT(file.mFileInfo);
19928   MOZ_ASSERT(file.mType == StructuredCloneFile::eStructuredClone);
19929 
19930   nsCOMPtr<nsIFile> nativeFile = GetFileForFileInfo(file.mFileInfo);
19931   if (NS_WARN_IF(!nativeFile)) {
19932     return NS_ERROR_FAILURE;
19933   }
19934 
19935   nsCOMPtr<nsIInputStream> fileInputStream;
19936   rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), nativeFile);
19937   if (NS_WARN_IF(NS_FAILED(rv))) {
19938     return rv;
19939   }
19940 
19941   RefPtr<SnappyUncompressInputStream> snappyInputStream =
19942     new SnappyUncompressInputStream(fileInputStream);
19943 
19944   do {
19945     char buffer[kFileCopyBufferSize];
19946 
19947     uint32_t numRead;
19948     rv = snappyInputStream->Read(buffer, sizeof(buffer), &numRead);
19949     if (NS_WARN_IF(NS_FAILED(rv))) {
19950       break;
19951     }
19952 
19953     if (!numRead) {
19954       break;
19955     }
19956 
19957     if (NS_WARN_IF(!aInfo->mData.WriteBytes(buffer, numRead))) {
19958       rv = NS_ERROR_OUT_OF_MEMORY;
19959       break;
19960     }
19961   } while (true);
19962 
19963   return rv;
19964 }
19965 
19966 // static
19967 nsresult
BindKeyRangeToStatement(const SerializedKeyRange & aKeyRange,mozIStorageStatement * aStatement)19968 DatabaseOperationBase::BindKeyRangeToStatement(
19969                                             const SerializedKeyRange& aKeyRange,
19970                                             mozIStorageStatement* aStatement)
19971 {
19972   MOZ_ASSERT(!IsOnBackgroundThread());
19973   MOZ_ASSERT(aStatement);
19974 
19975   nsresult rv = NS_OK;
19976 
19977   if (!aKeyRange.lower().IsUnset()) {
19978     rv = aKeyRange.lower().BindToStatement(aStatement, NS_LITERAL_CSTRING("lower_key"));
19979     if (NS_WARN_IF(NS_FAILED(rv))) {
19980       return rv;
19981     }
19982   }
19983 
19984   if (aKeyRange.isOnly()) {
19985     return rv;
19986   }
19987 
19988   if (!aKeyRange.upper().IsUnset()) {
19989     rv = aKeyRange.upper().BindToStatement(aStatement, NS_LITERAL_CSTRING("upper_key"));
19990     if (NS_WARN_IF(NS_FAILED(rv))) {
19991       return rv;
19992     }
19993   }
19994 
19995   return NS_OK;
19996 }
19997 
19998 // static
19999 nsresult
BindKeyRangeToStatement(const SerializedKeyRange & aKeyRange,mozIStorageStatement * aStatement,const nsCString & aLocale)20000 DatabaseOperationBase::BindKeyRangeToStatement(
20001                                             const SerializedKeyRange& aKeyRange,
20002                                             mozIStorageStatement* aStatement,
20003                                             const nsCString& aLocale)
20004 {
20005 #ifndef ENABLE_INTL_API
20006   return BindKeyRangeToStatement(aKeyRange, aStatement);
20007 #else
20008   MOZ_ASSERT(!IsOnBackgroundThread());
20009   MOZ_ASSERT(aStatement);
20010   MOZ_ASSERT(!aLocale.IsEmpty());
20011 
20012   nsresult rv = NS_OK;
20013 
20014   if (!aKeyRange.lower().IsUnset()) {
20015     Key lower;
20016     rv = aKeyRange.lower().ToLocaleBasedKey(lower, aLocale);
20017     if (NS_WARN_IF(NS_FAILED(rv))) {
20018       return rv;
20019     }
20020 
20021     rv = lower.BindToStatement(aStatement, NS_LITERAL_CSTRING("lower_key"));
20022     if (NS_WARN_IF(NS_FAILED(rv))) {
20023       return rv;
20024     }
20025   }
20026 
20027   if (aKeyRange.isOnly()) {
20028     return rv;
20029   }
20030 
20031   if (!aKeyRange.upper().IsUnset()) {
20032     Key upper;
20033     rv = aKeyRange.upper().ToLocaleBasedKey(upper, aLocale);
20034     if (NS_WARN_IF(NS_FAILED(rv))) {
20035       return rv;
20036     }
20037 
20038     rv = upper.BindToStatement(aStatement, NS_LITERAL_CSTRING("upper_key"));
20039     if (NS_WARN_IF(NS_FAILED(rv))) {
20040       return rv;
20041     }
20042   }
20043 
20044   return NS_OK;
20045 #endif
20046 }
20047 
20048 // static
20049 void
AppendConditionClause(const nsACString & aColumnName,const nsACString & aArgName,bool aLessThan,bool aEquals,nsAutoCString & aResult)20050 DatabaseOperationBase::AppendConditionClause(const nsACString& aColumnName,
20051                                              const nsACString& aArgName,
20052                                              bool aLessThan,
20053                                              bool aEquals,
20054                                              nsAutoCString& aResult)
20055 {
20056   aResult += NS_LITERAL_CSTRING(" AND ") + aColumnName +
20057              NS_LITERAL_CSTRING(" ");
20058 
20059   if (aLessThan) {
20060     aResult.Append('<');
20061   }
20062   else {
20063     aResult.Append('>');
20064   }
20065 
20066   if (aEquals) {
20067     aResult.Append('=');
20068   }
20069 
20070   aResult += NS_LITERAL_CSTRING(" :") + aArgName;
20071 }
20072 
20073 // static
20074 nsresult
GetUniqueIndexTableForObjectStore(TransactionBase * aTransaction,int64_t aObjectStoreId,Maybe<UniqueIndexTable> & aMaybeUniqueIndexTable)20075 DatabaseOperationBase::GetUniqueIndexTableForObjectStore(
20076                                 TransactionBase* aTransaction,
20077                                 int64_t aObjectStoreId,
20078                                 Maybe<UniqueIndexTable>& aMaybeUniqueIndexTable)
20079 {
20080   AssertIsOnBackgroundThread();
20081   MOZ_ASSERT(aTransaction);
20082   MOZ_ASSERT(aObjectStoreId);
20083   MOZ_ASSERT(aMaybeUniqueIndexTable.isNothing());
20084 
20085   const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
20086     aTransaction->GetMetadataForObjectStoreId(aObjectStoreId);
20087   MOZ_ASSERT(objectStoreMetadata);
20088 
20089   if (!objectStoreMetadata->mIndexes.Count()) {
20090     return NS_OK;
20091   }
20092 
20093   const uint32_t indexCount = objectStoreMetadata->mIndexes.Count();
20094   MOZ_ASSERT(indexCount > 0);
20095 
20096   aMaybeUniqueIndexTable.emplace();
20097   UniqueIndexTable* uniqueIndexTable = aMaybeUniqueIndexTable.ptr();
20098   MOZ_ASSERT(uniqueIndexTable);
20099 
20100   for (auto iter = objectStoreMetadata->mIndexes.Iter(); !iter.Done(); iter.Next()) {
20101     FullIndexMetadata* value = iter.UserData();
20102     MOZ_ASSERT(!uniqueIndexTable->Get(value->mCommonMetadata.id()));
20103 
20104     if (NS_WARN_IF(!uniqueIndexTable->Put(value->mCommonMetadata.id(),
20105                                           value->mCommonMetadata.unique(),
20106                                           fallible))) {
20107       break;
20108     }
20109   }
20110 
20111   if (NS_WARN_IF(aMaybeUniqueIndexTable.ref().Count() != indexCount)) {
20112     IDB_REPORT_INTERNAL_ERR();
20113     aMaybeUniqueIndexTable.reset();
20114     NS_WARNING("out of memory");
20115     return NS_ERROR_OUT_OF_MEMORY;
20116   }
20117 
20118 #ifdef DEBUG
20119   aMaybeUniqueIndexTable.ref().MarkImmutable();
20120 #endif
20121 
20122   return NS_OK;
20123 }
20124 
20125 // static
20126 nsresult
IndexDataValuesFromUpdateInfos(const nsTArray<IndexUpdateInfo> & aUpdateInfos,const UniqueIndexTable & aUniqueIndexTable,nsTArray<IndexDataValue> & aIndexValues)20127 DatabaseOperationBase::IndexDataValuesFromUpdateInfos(
20128                                   const nsTArray<IndexUpdateInfo>& aUpdateInfos,
20129                                   const UniqueIndexTable& aUniqueIndexTable,
20130                                   nsTArray<IndexDataValue>& aIndexValues)
20131 {
20132   MOZ_ASSERT(aIndexValues.IsEmpty());
20133   MOZ_ASSERT_IF(!aUpdateInfos.IsEmpty(), aUniqueIndexTable.Count());
20134 
20135   PROFILER_LABEL("IndexedDB",
20136                  "DatabaseOperationBase::IndexDataValuesFromUpdateInfos",
20137                  js::ProfileEntry::Category::STORAGE);
20138 
20139   const uint32_t count = aUpdateInfos.Length();
20140 
20141   if (!count) {
20142     return NS_OK;
20143   }
20144 
20145   if (NS_WARN_IF(!aIndexValues.SetCapacity(count, fallible))) {
20146     IDB_REPORT_INTERNAL_ERR();
20147     return NS_ERROR_OUT_OF_MEMORY;
20148   }
20149 
20150   for (uint32_t idxIndex = 0; idxIndex < count; idxIndex++) {
20151     const IndexUpdateInfo& updateInfo = aUpdateInfos[idxIndex];
20152     const int64_t& indexId = updateInfo.indexId();
20153     const Key& key = updateInfo.value();
20154     const Key& sortKey = updateInfo.localizedValue();
20155 
20156     bool unique = false;
20157     MOZ_ALWAYS_TRUE(aUniqueIndexTable.Get(indexId, &unique));
20158 
20159     IndexDataValue idv(indexId, unique, key, sortKey);
20160 
20161     MOZ_ALWAYS_TRUE(
20162       aIndexValues.InsertElementSorted(idv, fallible));
20163   }
20164 
20165   return NS_OK;
20166 }
20167 
20168 // static
20169 nsresult
InsertIndexTableRows(DatabaseConnection * aConnection,const int64_t aObjectStoreId,const Key & aObjectStoreKey,const FallibleTArray<IndexDataValue> & aIndexValues)20170 DatabaseOperationBase::InsertIndexTableRows(
20171                              DatabaseConnection* aConnection,
20172                              const int64_t aObjectStoreId,
20173                              const Key& aObjectStoreKey,
20174                              const FallibleTArray<IndexDataValue>& aIndexValues)
20175 {
20176   MOZ_ASSERT(aConnection);
20177   aConnection->AssertIsOnConnectionThread();
20178   MOZ_ASSERT(!aObjectStoreKey.IsUnset());
20179 
20180   PROFILER_LABEL("IndexedDB",
20181                  "DatabaseOperationBase::InsertIndexTableRows",
20182                  js::ProfileEntry::Category::STORAGE);
20183 
20184   const uint32_t count = aIndexValues.Length();
20185   if (!count) {
20186     return NS_OK;
20187   }
20188 
20189   NS_NAMED_LITERAL_CSTRING(objectStoreIdString, "object_store_id");
20190   NS_NAMED_LITERAL_CSTRING(objectDataKeyString, "object_data_key");
20191   NS_NAMED_LITERAL_CSTRING(indexIdString, "index_id");
20192   NS_NAMED_LITERAL_CSTRING(valueString, "value");
20193   NS_NAMED_LITERAL_CSTRING(valueLocaleString, "value_locale");
20194 
20195   DatabaseConnection::CachedStatement insertUniqueStmt;
20196   DatabaseConnection::CachedStatement insertStmt;
20197 
20198   nsresult rv;
20199 
20200   for (uint32_t index = 0; index < count; index++) {
20201     const IndexDataValue& info = aIndexValues[index];
20202 
20203     DatabaseConnection::CachedStatement& stmt =
20204       info.mUnique ? insertUniqueStmt : insertStmt;
20205 
20206     if (stmt) {
20207       stmt.Reset();
20208     } else if (info.mUnique) {
20209       rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20210         "INSERT INTO unique_index_data "
20211           "(index_id, value, object_store_id, object_data_key, value_locale) "
20212           "VALUES (:index_id, :value, :object_store_id, :object_data_key, :value_locale);"),
20213         &stmt);
20214       if (NS_WARN_IF(NS_FAILED(rv))) {
20215         return rv;
20216       }
20217     } else {
20218       rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20219         "INSERT OR IGNORE INTO index_data "
20220           "(index_id, value, object_data_key, object_store_id, value_locale) "
20221           "VALUES (:index_id, :value, :object_data_key, :object_store_id, :value_locale);"),
20222         &stmt);
20223       if (NS_WARN_IF(NS_FAILED(rv))) {
20224         return rv;
20225       }
20226     }
20227 
20228     rv = stmt->BindInt64ByName(indexIdString, info.mIndexId);
20229     if (NS_WARN_IF(NS_FAILED(rv))) {
20230       return rv;
20231     }
20232 
20233     rv = info.mKey.BindToStatement(stmt, valueString);
20234     if (NS_WARN_IF(NS_FAILED(rv))) {
20235       return rv;
20236     }
20237 
20238     rv = info.mSortKey.BindToStatement(stmt, valueLocaleString);
20239     if (NS_WARN_IF(NS_FAILED(rv))) {
20240       return rv;
20241     }
20242 
20243     rv = stmt->BindInt64ByName(objectStoreIdString, aObjectStoreId);
20244     if (NS_WARN_IF(NS_FAILED(rv))) {
20245       return rv;
20246     }
20247 
20248     rv = aObjectStoreKey.BindToStatement(stmt, objectDataKeyString);
20249     if (NS_WARN_IF(NS_FAILED(rv))) {
20250       return rv;
20251     }
20252 
20253     rv = stmt->Execute();
20254     if (rv == NS_ERROR_STORAGE_CONSTRAINT && info.mUnique) {
20255       // If we're inserting multiple entries for the same unique index, then
20256       // we might have failed to insert due to colliding with another entry for
20257       // the same index in which case we should ignore it.
20258       for (int32_t index2 = int32_t(index) - 1;
20259            index2 >= 0 && aIndexValues[index2].mIndexId == info.mIndexId;
20260            --index2) {
20261         if (info.mKey == aIndexValues[index2].mKey) {
20262           // We found a key with the same value for the same index. So we
20263           // must have had a collision with a value we just inserted.
20264           rv = NS_OK;
20265           break;
20266         }
20267       }
20268     }
20269 
20270     if (NS_FAILED(rv)) {
20271       return rv;
20272     }
20273   }
20274 
20275   return NS_OK;
20276 }
20277 
20278 // static
20279 nsresult
DeleteIndexDataTableRows(DatabaseConnection * aConnection,const Key & aObjectStoreKey,const FallibleTArray<IndexDataValue> & aIndexValues)20280 DatabaseOperationBase::DeleteIndexDataTableRows(
20281                              DatabaseConnection* aConnection,
20282                              const Key& aObjectStoreKey,
20283                              const FallibleTArray<IndexDataValue>& aIndexValues)
20284 {
20285   MOZ_ASSERT(aConnection);
20286   aConnection->AssertIsOnConnectionThread();
20287   MOZ_ASSERT(!aObjectStoreKey.IsUnset());
20288 
20289   PROFILER_LABEL("IndexedDB",
20290                  "DatabaseOperationBase::DeleteIndexDataTableRows",
20291                  js::ProfileEntry::Category::STORAGE);
20292 
20293   const uint32_t count = aIndexValues.Length();
20294   if (!count) {
20295     return NS_OK;
20296   }
20297 
20298   NS_NAMED_LITERAL_CSTRING(indexIdString, "index_id");
20299   NS_NAMED_LITERAL_CSTRING(valueString, "value");
20300   NS_NAMED_LITERAL_CSTRING(objectDataKeyString, "object_data_key");
20301 
20302   DatabaseConnection::CachedStatement deleteUniqueStmt;
20303   DatabaseConnection::CachedStatement deleteStmt;
20304 
20305   nsresult rv;
20306 
20307   for (uint32_t index = 0; index < count; index++) {
20308     const IndexDataValue& indexValue = aIndexValues[index];
20309 
20310     DatabaseConnection::CachedStatement& stmt =
20311       indexValue.mUnique ? deleteUniqueStmt : deleteStmt;
20312 
20313     if (stmt) {
20314       stmt.Reset();
20315     } else if (indexValue.mUnique) {
20316       rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20317         "DELETE FROM unique_index_data "
20318           "WHERE index_id = :index_id "
20319           "AND value = :value;"),
20320         &stmt);
20321       if (NS_WARN_IF(NS_FAILED(rv))) {
20322         return rv;
20323       }
20324     } else {
20325       rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20326         "DELETE FROM index_data "
20327           "WHERE index_id = :index_id "
20328           "AND value = :value "
20329           "AND object_data_key = :object_data_key;"),
20330         &stmt);
20331       if (NS_WARN_IF(NS_FAILED(rv))) {
20332         return rv;
20333       }
20334     }
20335 
20336     rv = stmt->BindInt64ByName(indexIdString, indexValue.mIndexId);
20337     if (NS_WARN_IF(NS_FAILED(rv))) {
20338       return rv;
20339     }
20340 
20341     rv = indexValue.mKey.BindToStatement(stmt, valueString);
20342     if (NS_WARN_IF(NS_FAILED(rv))) {
20343       return rv;
20344     }
20345 
20346     if (!indexValue.mUnique) {
20347       rv = aObjectStoreKey.BindToStatement(stmt, objectDataKeyString);
20348       if (NS_WARN_IF(NS_FAILED(rv))) {
20349         return rv;
20350       }
20351     }
20352 
20353     rv = stmt->Execute();
20354     if (NS_WARN_IF(NS_FAILED(rv))) {
20355       return rv;
20356     }
20357   }
20358 
20359   return NS_OK;
20360 }
20361 
20362 // static
20363 nsresult
DeleteObjectStoreDataTableRowsWithIndexes(DatabaseConnection * aConnection,const int64_t aObjectStoreId,const OptionalKeyRange & aKeyRange)20364 DatabaseOperationBase::DeleteObjectStoreDataTableRowsWithIndexes(
20365                                               DatabaseConnection* aConnection,
20366                                               const int64_t aObjectStoreId,
20367                                               const OptionalKeyRange& aKeyRange)
20368 {
20369   MOZ_ASSERT(aConnection);
20370   aConnection->AssertIsOnConnectionThread();
20371   MOZ_ASSERT(aObjectStoreId);
20372 
20373 #ifdef DEBUG
20374   {
20375     bool hasIndexes = false;
20376     MOZ_ASSERT(NS_SUCCEEDED(
20377       ObjectStoreHasIndexes(aConnection, aObjectStoreId, &hasIndexes)));
20378     MOZ_ASSERT(hasIndexes,
20379                "Don't use this slow method if there are no indexes!");
20380   }
20381 #endif
20382 
20383   PROFILER_LABEL("IndexedDB",
20384                  "DatabaseOperationBase::"
20385                  "DeleteObjectStoreDataTableRowsWithIndexes",
20386                  js::ProfileEntry::Category::STORAGE);
20387 
20388   const bool singleRowOnly =
20389     aKeyRange.type() == OptionalKeyRange::TSerializedKeyRange &&
20390     aKeyRange.get_SerializedKeyRange().isOnly();
20391 
20392   NS_NAMED_LITERAL_CSTRING(objectStoreIdString, "object_store_id");
20393   NS_NAMED_LITERAL_CSTRING(keyString, "key");
20394 
20395   nsresult rv;
20396   Key objectStoreKey;
20397   DatabaseConnection::CachedStatement selectStmt;
20398 
20399   if (singleRowOnly) {
20400     rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20401       "SELECT index_data_values "
20402         "FROM object_data "
20403         "WHERE object_store_id = :object_store_id "
20404         "AND key = :key;"),
20405       &selectStmt);
20406     if (NS_WARN_IF(NS_FAILED(rv))) {
20407       return rv;
20408     }
20409 
20410     objectStoreKey = aKeyRange.get_SerializedKeyRange().lower();
20411 
20412     rv = objectStoreKey.BindToStatement(selectStmt, keyString);
20413     if (NS_WARN_IF(NS_FAILED(rv))) {
20414       return rv;
20415     }
20416   } else {
20417     nsAutoCString keyRangeClause;
20418     if (aKeyRange.type() == OptionalKeyRange::TSerializedKeyRange) {
20419       GetBindingClauseForKeyRange(aKeyRange.get_SerializedKeyRange(),
20420                                   keyString,
20421                                   keyRangeClause);
20422     }
20423 
20424     rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20425       "SELECT index_data_values, key "
20426         "FROM object_data "
20427         "WHERE object_store_id = :") +
20428         objectStoreIdString +
20429         keyRangeClause +
20430         NS_LITERAL_CSTRING(";"),
20431       &selectStmt);
20432     if (NS_WARN_IF(NS_FAILED(rv))) {
20433       return rv;
20434     }
20435 
20436     if (aKeyRange.type() == OptionalKeyRange::TSerializedKeyRange) {
20437       rv = BindKeyRangeToStatement(aKeyRange, selectStmt);
20438       if (NS_WARN_IF(NS_FAILED(rv))) {
20439         return rv;
20440       }
20441     }
20442   }
20443 
20444   rv = selectStmt->BindInt64ByName(objectStoreIdString, aObjectStoreId);
20445   if (NS_WARN_IF(NS_FAILED(rv))) {
20446     return rv;
20447   }
20448 
20449   DatabaseConnection::CachedStatement deleteStmt;
20450   AutoTArray<IndexDataValue, 32> indexValues;
20451 
20452   DebugOnly<uint32_t> resultCountDEBUG = 0;
20453 
20454   bool hasResult;
20455   while (NS_SUCCEEDED(rv = selectStmt->ExecuteStep(&hasResult)) && hasResult) {
20456     if (!singleRowOnly) {
20457       rv = objectStoreKey.SetFromStatement(selectStmt, 1);
20458       if (NS_WARN_IF(NS_FAILED(rv))) {
20459         return rv;
20460       }
20461 
20462       indexValues.ClearAndRetainStorage();
20463     }
20464 
20465     rv = ReadCompressedIndexDataValues(selectStmt, 0, indexValues);
20466     if (NS_WARN_IF(NS_FAILED(rv))) {
20467       return rv;
20468     }
20469 
20470     rv = DeleteIndexDataTableRows(aConnection, objectStoreKey, indexValues);
20471     if (NS_WARN_IF(NS_FAILED(rv))) {
20472       return rv;
20473     }
20474 
20475     if (deleteStmt) {
20476       MOZ_ALWAYS_SUCCEEDS(deleteStmt->Reset());
20477     } else {
20478       rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20479         "DELETE FROM object_data "
20480           "WHERE object_store_id = :object_store_id "
20481           "AND key = :key;"),
20482         &deleteStmt);
20483       if (NS_WARN_IF(NS_FAILED(rv))) {
20484         return rv;
20485       }
20486     }
20487 
20488     rv = deleteStmt->BindInt64ByName(objectStoreIdString, aObjectStoreId);
20489     if (NS_WARN_IF(NS_FAILED(rv))) {
20490       return rv;
20491     }
20492 
20493     rv = objectStoreKey.BindToStatement(deleteStmt, keyString);
20494     if (NS_WARN_IF(NS_FAILED(rv))) {
20495       return rv;
20496     }
20497 
20498     rv = deleteStmt->Execute();
20499     if (NS_WARN_IF(NS_FAILED(rv))) {
20500       return rv;
20501     }
20502 
20503     resultCountDEBUG++;
20504   }
20505   if (NS_WARN_IF(NS_FAILED(rv))) {
20506     return rv;
20507   }
20508 
20509   MOZ_ASSERT_IF(singleRowOnly, resultCountDEBUG <= 1);
20510 
20511   return NS_OK;
20512 }
20513 
20514 // static
20515 nsresult
UpdateIndexValues(DatabaseConnection * aConnection,const int64_t aObjectStoreId,const Key & aObjectStoreKey,const FallibleTArray<IndexDataValue> & aIndexValues)20516 DatabaseOperationBase::UpdateIndexValues(
20517                              DatabaseConnection* aConnection,
20518                              const int64_t aObjectStoreId,
20519                              const Key& aObjectStoreKey,
20520                              const FallibleTArray<IndexDataValue>& aIndexValues)
20521 {
20522   MOZ_ASSERT(aConnection);
20523   aConnection->AssertIsOnConnectionThread();
20524   MOZ_ASSERT(!aObjectStoreKey.IsUnset());
20525 
20526   PROFILER_LABEL("IndexedDB",
20527                  "DatabaunseOperationBase::UpdateIndexValues",
20528                  js::ProfileEntry::Category::STORAGE);
20529 
20530   UniqueFreePtr<uint8_t> indexDataValues;
20531   uint32_t indexDataValuesLength;
20532   nsresult rv = MakeCompressedIndexDataValues(aIndexValues,
20533                                               indexDataValues,
20534                                               &indexDataValuesLength);
20535   if (NS_WARN_IF(NS_FAILED(rv))) {
20536     return rv;
20537   }
20538 
20539   MOZ_ASSERT(!indexDataValuesLength == !(indexDataValues.get()));
20540 
20541   DatabaseConnection::CachedStatement updateStmt;
20542   rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20543     "UPDATE object_data "
20544       "SET index_data_values = :index_data_values "
20545       "WHERE object_store_id = :object_store_id "
20546       "AND key = :key;"),
20547     &updateStmt);
20548   if (NS_WARN_IF(NS_FAILED(rv))) {
20549     return rv;
20550   }
20551 
20552   NS_NAMED_LITERAL_CSTRING(indexDataValuesString, "index_data_values");
20553 
20554   if (indexDataValues) {
20555     rv = updateStmt->BindAdoptedBlobByName(indexDataValuesString,
20556                                            indexDataValues.release(),
20557                                            indexDataValuesLength);
20558     if (NS_WARN_IF(NS_FAILED(rv))) {
20559       return rv;
20560     }
20561   } else {
20562     rv = updateStmt->BindNullByName(indexDataValuesString);
20563     if (NS_WARN_IF(NS_FAILED(rv))) {
20564       return rv;
20565     }
20566   }
20567 
20568   rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
20569                                    aObjectStoreId);
20570   if (NS_WARN_IF(NS_FAILED(rv))) {
20571     return rv;
20572   }
20573 
20574   rv = aObjectStoreKey.BindToStatement(updateStmt, NS_LITERAL_CSTRING("key"));
20575   if (NS_WARN_IF(NS_FAILED(rv))) {
20576     return rv;
20577   }
20578 
20579   rv = updateStmt->Execute();
20580   if (NS_WARN_IF(NS_FAILED(rv))) {
20581     return rv;
20582   }
20583 
20584   return NS_OK;
20585 }
20586 
20587 // static
20588 nsresult
ObjectStoreHasIndexes(DatabaseConnection * aConnection,const int64_t aObjectStoreId,bool * aHasIndexes)20589 DatabaseOperationBase::ObjectStoreHasIndexes(DatabaseConnection* aConnection,
20590                                              const int64_t aObjectStoreId,
20591                                              bool* aHasIndexes)
20592 {
20593   MOZ_ASSERT(aConnection);
20594   aConnection->AssertIsOnConnectionThread();
20595   MOZ_ASSERT(aObjectStoreId);
20596   MOZ_ASSERT(aHasIndexes);
20597 
20598   DatabaseConnection::CachedStatement stmt;
20599 
20600   nsresult rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
20601     "SELECT id "
20602       "FROM object_store_index "
20603       "WHERE object_store_id = :object_store_id "
20604       "LIMIT 1;"),
20605     &stmt);
20606   if (NS_WARN_IF(NS_FAILED(rv))) {
20607     return rv;
20608   }
20609 
20610   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
20611                              aObjectStoreId);
20612   if (NS_WARN_IF(NS_FAILED(rv))) {
20613     return rv;
20614   }
20615 
20616   bool hasResult;
20617   rv = stmt->ExecuteStep(&hasResult);
20618   if (NS_WARN_IF(NS_FAILED(rv))) {
20619     return rv;
20620   }
20621 
20622   *aHasIndexes = hasResult;
20623   return NS_OK;
20624 }
20625 
NS_IMPL_ISUPPORTS_INHERITED(DatabaseOperationBase,Runnable,mozIStorageProgressHandler)20626 NS_IMPL_ISUPPORTS_INHERITED(DatabaseOperationBase,
20627                             Runnable,
20628                             mozIStorageProgressHandler)
20629 
20630 NS_IMETHODIMP
20631 DatabaseOperationBase::OnProgress(mozIStorageConnection* aConnection,
20632                                   bool* _retval)
20633 {
20634   MOZ_ASSERT(!IsOnBackgroundThread());
20635   MOZ_ASSERT(aConnection);
20636   MOZ_ASSERT(_retval);
20637 
20638   // This is intentionally racy.
20639   *_retval = !OperationMayProceed();
20640   return NS_OK;
20641 }
20642 
20643 DatabaseOperationBase::
AutoSetProgressHandler()20644 AutoSetProgressHandler::AutoSetProgressHandler()
20645   : mConnection(nullptr)
20646 #ifdef DEBUG
20647   , mDEBUGDatabaseOp(nullptr)
20648 #endif
20649 {
20650   MOZ_ASSERT(!IsOnBackgroundThread());
20651 }
20652 
20653 DatabaseOperationBase::
~AutoSetProgressHandler()20654 AutoSetProgressHandler::~AutoSetProgressHandler()
20655 {
20656   MOZ_ASSERT(!IsOnBackgroundThread());
20657 
20658   if (mConnection) {
20659     nsCOMPtr<mozIStorageProgressHandler> oldHandler;
20660     MOZ_ALWAYS_SUCCEEDS(
20661       mConnection->RemoveProgressHandler(getter_AddRefs(oldHandler)));
20662     MOZ_ASSERT(oldHandler == mDEBUGDatabaseOp);
20663   }
20664 }
20665 
20666 nsresult
20667 DatabaseOperationBase::
Register(mozIStorageConnection * aConnection,DatabaseOperationBase * aDatabaseOp)20668 AutoSetProgressHandler::Register(mozIStorageConnection* aConnection,
20669                                  DatabaseOperationBase* aDatabaseOp)
20670 {
20671   MOZ_ASSERT(!IsOnBackgroundThread());
20672   MOZ_ASSERT(aConnection);
20673   MOZ_ASSERT(aDatabaseOp);
20674   MOZ_ASSERT(!mConnection);
20675 
20676   nsCOMPtr<mozIStorageProgressHandler> oldProgressHandler;
20677 
20678   nsresult rv =
20679     aConnection->SetProgressHandler(kStorageProgressGranularity,
20680                                     aDatabaseOp,
20681                                     getter_AddRefs(oldProgressHandler));
20682   if (NS_WARN_IF(NS_FAILED(rv))) {
20683     return rv;
20684   }
20685 
20686   MOZ_ASSERT(!oldProgressHandler);
20687 
20688   mConnection = aConnection;
20689 #ifdef DEBUG
20690   mDEBUGDatabaseOp = aDatabaseOp;
20691 #endif
20692 
20693   return NS_OK;
20694 }
20695 
MutableFile(nsIFile * aFile,Database * aDatabase,FileInfo * aFileInfo)20696 MutableFile::MutableFile(nsIFile* aFile,
20697                          Database* aDatabase,
20698                          FileInfo* aFileInfo)
20699   : BackgroundMutableFileParentBase(FILE_HANDLE_STORAGE_IDB,
20700                                     aDatabase->Id(),
20701                                     IntString(aFileInfo->Id()),
20702                                     aFile)
20703   , mDatabase(aDatabase)
20704   , mFileInfo(aFileInfo)
20705 {
20706   AssertIsOnBackgroundThread();
20707   MOZ_ASSERT(aDatabase);
20708   MOZ_ASSERT(aFileInfo);
20709 }
20710 
~MutableFile()20711 MutableFile::~MutableFile()
20712 {
20713   mDatabase->UnregisterMutableFile(this);
20714 }
20715 
20716 already_AddRefed<MutableFile>
Create(nsIFile * aFile,Database * aDatabase,FileInfo * aFileInfo)20717 MutableFile::Create(nsIFile* aFile,
20718                     Database* aDatabase,
20719                     FileInfo* aFileInfo)
20720 {
20721   AssertIsOnBackgroundThread();
20722 
20723   RefPtr<MutableFile> newMutableFile =
20724     new MutableFile(aFile, aDatabase, aFileInfo);
20725 
20726   if (!aDatabase->RegisterMutableFile(newMutableFile)) {
20727     return nullptr;
20728   }
20729 
20730   return newMutableFile.forget();
20731 }
20732 
20733 void
NoteActiveState()20734 MutableFile::NoteActiveState()
20735 {
20736   AssertIsOnBackgroundThread();
20737 
20738   mDatabase->NoteActiveMutableFile();
20739 }
20740 
20741 void
NoteInactiveState()20742 MutableFile::NoteInactiveState()
20743 {
20744   AssertIsOnBackgroundThread();
20745 
20746   mDatabase->NoteInactiveMutableFile();
20747 }
20748 
20749 PBackgroundParent*
GetBackgroundParent() const20750 MutableFile::GetBackgroundParent() const
20751 {
20752   AssertIsOnBackgroundThread();
20753   MOZ_ASSERT(!IsActorDestroyed());
20754 
20755   return GetDatabase()->GetBackgroundParent();
20756 }
20757 
20758 already_AddRefed<nsISupports>
CreateStream(bool aReadOnly)20759 MutableFile::CreateStream(bool aReadOnly)
20760 {
20761   AssertIsOnBackgroundThread();
20762 
20763   PersistenceType persistenceType = mDatabase->Type();
20764   const nsACString& group = mDatabase->Group();
20765   const nsACString& origin = mDatabase->Origin();
20766 
20767   nsCOMPtr<nsISupports> result;
20768 
20769   if (aReadOnly) {
20770     RefPtr<FileInputStream> stream =
20771       FileInputStream::Create(persistenceType, group, origin, mFile, -1, -1,
20772                               nsIFileInputStream::DEFER_OPEN);
20773     result = NS_ISUPPORTS_CAST(nsIFileInputStream*, stream);
20774   }
20775   else {
20776     RefPtr<FileStream> stream =
20777       FileStream::Create(persistenceType, group, origin, mFile, -1, -1,
20778                          nsIFileStream::DEFER_OPEN);
20779     result = NS_ISUPPORTS_CAST(nsIFileStream*, stream);
20780   }
20781   if (NS_WARN_IF(!result)) {
20782     return nullptr;
20783   }
20784 
20785   return result.forget();
20786 }
20787 
20788 already_AddRefed<BlobImpl>
CreateBlobImpl()20789 MutableFile::CreateBlobImpl()
20790 {
20791   AssertIsOnBackgroundThread();
20792 
20793   RefPtr<BlobImpl> blobImpl =
20794     new BlobImplStoredFile(mFile, mFileInfo, /* aSnapshot */ true);
20795   return blobImpl.forget();
20796 }
20797 
20798 PBackgroundFileHandleParent*
AllocPBackgroundFileHandleParent(const FileMode & aMode)20799 MutableFile::AllocPBackgroundFileHandleParent(const FileMode& aMode)
20800 {
20801   AssertIsOnBackgroundThread();
20802 
20803   // Once a database is closed it must not try to open new file handles.
20804   if (NS_WARN_IF(mDatabase->IsClosed())) {
20805     if (!mDatabase->IsInvalidated()) {
20806       ASSERT_UNLESS_FUZZING();
20807     }
20808     return nullptr;
20809   }
20810 
20811   if (!gFileHandleThreadPool) {
20812     RefPtr<FileHandleThreadPool> fileHandleThreadPool =
20813       FileHandleThreadPool::Create();
20814     if (NS_WARN_IF(!fileHandleThreadPool)) {
20815       return nullptr;
20816     }
20817 
20818     gFileHandleThreadPool = fileHandleThreadPool;
20819   }
20820 
20821   return BackgroundMutableFileParentBase::AllocPBackgroundFileHandleParent(
20822                                                                          aMode);
20823 }
20824 
20825 bool
RecvPBackgroundFileHandleConstructor(PBackgroundFileHandleParent * aActor,const FileMode & aMode)20826 MutableFile::RecvPBackgroundFileHandleConstructor(
20827                                             PBackgroundFileHandleParent* aActor,
20828                                             const FileMode& aMode)
20829 {
20830   AssertIsOnBackgroundThread();
20831   MOZ_ASSERT(!mDatabase->IsClosed());
20832 
20833   if (NS_WARN_IF(mDatabase->IsInvalidated())) {
20834     // This is an expected race. We don't want the child to die here, just don't
20835     // actually do any work.
20836     return true;
20837   }
20838 
20839   return BackgroundMutableFileParentBase::RecvPBackgroundFileHandleConstructor(
20840                                                                  aActor, aMode);
20841 }
20842 
20843 bool
RecvGetFileId(int64_t * aFileId)20844 MutableFile::RecvGetFileId(int64_t* aFileId)
20845 {
20846   AssertIsOnBackgroundThread();
20847   MOZ_ASSERT(mFileInfo);
20848 
20849   if (NS_WARN_IF(!IndexedDatabaseManager::InTestingMode())) {
20850     ASSERT_UNLESS_FUZZING();
20851     return false;
20852   }
20853 
20854   *aFileId = mFileInfo->Id();
20855   return true;
20856 }
20857 
FactoryOp(Factory * aFactory,already_AddRefed<ContentParent> aContentParent,const CommonFactoryRequestParams & aCommonParams,bool aDeleting)20858 FactoryOp::FactoryOp(Factory* aFactory,
20859                      already_AddRefed<ContentParent> aContentParent,
20860                      const CommonFactoryRequestParams& aCommonParams,
20861                      bool aDeleting)
20862   : DatabaseOperationBase(aFactory->GetLoggingInfo()->Id(),
20863                           aFactory->GetLoggingInfo()->NextRequestSN())
20864   , mFactory(aFactory)
20865   , mContentParent(Move(aContentParent))
20866   , mCommonParams(aCommonParams)
20867   , mState(State::Initial)
20868   , mIsApp(false)
20869   , mEnforcingQuota(true)
20870   , mDeleting(aDeleting)
20871   , mBlockedDatabaseOpen(false)
20872   , mChromeWriteAccessAllowed(false)
20873   , mFileHandleDisabled(false)
20874 {
20875   AssertIsOnBackgroundThread();
20876   MOZ_ASSERT(aFactory);
20877   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
20878 }
20879 
20880 nsresult
Open()20881 FactoryOp::Open()
20882 {
20883   MOZ_ASSERT(NS_IsMainThread());
20884   MOZ_ASSERT(mState == State::Initial);
20885 
20886   // Swap this to the stack now to ensure that we release it on this thread.
20887   RefPtr<ContentParent> contentParent;
20888   mContentParent.swap(contentParent);
20889 
20890   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
20891       !OperationMayProceed()) {
20892     IDB_REPORT_INTERNAL_ERR();
20893     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
20894   }
20895 
20896   PermissionRequestBase::PermissionValue permission;
20897   nsresult rv = CheckPermission(contentParent, &permission);
20898   if (NS_WARN_IF(NS_FAILED(rv))) {
20899     return rv;
20900   }
20901 
20902   MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
20903              permission == PermissionRequestBase::kPermissionDenied ||
20904              permission == PermissionRequestBase::kPermissionPrompt);
20905 
20906   if (permission == PermissionRequestBase::kPermissionDenied) {
20907     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
20908   }
20909 
20910   {
20911     // These services have to be started on the main thread currently.
20912 
20913     IndexedDatabaseManager* mgr;
20914     if (NS_WARN_IF(!(mgr = IndexedDatabaseManager::GetOrCreate()))) {
20915       IDB_REPORT_INTERNAL_ERR();
20916       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
20917     }
20918 
20919     nsCOMPtr<mozIStorageService> ss;
20920     if (NS_WARN_IF(!(ss = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID)))) {
20921       IDB_REPORT_INTERNAL_ERR();
20922       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
20923     }
20924   }
20925 
20926   const DatabaseMetadata& metadata = mCommonParams.metadata();
20927 
20928   QuotaManager::GetStorageId(metadata.persistenceType(),
20929                              mOrigin,
20930                              Client::IDB,
20931                              mDatabaseId);
20932 
20933   mDatabaseId.Append('*');
20934   mDatabaseId.Append(NS_ConvertUTF16toUTF8(metadata.name()));
20935 
20936   if (permission == PermissionRequestBase::kPermissionPrompt) {
20937     mState = State::PermissionChallenge;
20938     MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
20939     return NS_OK;
20940   }
20941 
20942   MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed);
20943 
20944   mState = State::FinishOpen;
20945   MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
20946 
20947   return NS_OK;
20948 }
20949 
20950 nsresult
ChallengePermission()20951 FactoryOp::ChallengePermission()
20952 {
20953   AssertIsOnOwningThread();
20954   MOZ_ASSERT(mState == State::PermissionChallenge);
20955 
20956   const PrincipalInfo& principalInfo = mCommonParams.principalInfo();
20957   MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
20958 
20959   if (NS_WARN_IF(!SendPermissionChallenge(principalInfo))) {
20960     return NS_ERROR_FAILURE;
20961   }
20962 
20963   return NS_OK;
20964 }
20965 
20966 nsresult
RetryCheckPermission()20967 FactoryOp::RetryCheckPermission()
20968 {
20969   MOZ_ASSERT(NS_IsMainThread());
20970   MOZ_ASSERT(mState == State::PermissionRetry);
20971   MOZ_ASSERT(mCommonParams.principalInfo().type() ==
20972                PrincipalInfo::TContentPrincipalInfo);
20973 
20974   // Swap this to the stack now to ensure that we release it on this thread.
20975   RefPtr<ContentParent> contentParent;
20976   mContentParent.swap(contentParent);
20977 
20978   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
20979       !OperationMayProceed()) {
20980     IDB_REPORT_INTERNAL_ERR();
20981     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
20982   }
20983 
20984   PermissionRequestBase::PermissionValue permission;
20985   nsresult rv = CheckPermission(contentParent, &permission);
20986   if (NS_WARN_IF(NS_FAILED(rv))) {
20987     return rv;
20988   }
20989 
20990   MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
20991              permission == PermissionRequestBase::kPermissionDenied ||
20992              permission == PermissionRequestBase::kPermissionPrompt);
20993 
20994   if (permission == PermissionRequestBase::kPermissionDenied ||
20995       permission == PermissionRequestBase::kPermissionPrompt) {
20996     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
20997   }
20998 
20999   MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed);
21000 
21001   mState = State::FinishOpen;
21002   MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
21003 
21004   return NS_OK;
21005 }
21006 
21007 nsresult
DirectoryOpen()21008 FactoryOp::DirectoryOpen()
21009 {
21010   AssertIsOnOwningThread();
21011   MOZ_ASSERT(mState == State::DirectoryOpenPending);
21012   MOZ_ASSERT(mDirectoryLock);
21013   MOZ_ASSERT(!mDatabaseFilePath.IsEmpty());
21014 
21015   // gFactoryOps could be null here if the child process crashed or something
21016   // and that cleaned up the last Factory actor.
21017   if (!gFactoryOps) {
21018     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
21019   }
21020 
21021   // See if this FactoryOp needs to wait.
21022   bool delayed = false;
21023   for (uint32_t index = gFactoryOps->Length(); index > 0; index--) {
21024     RefPtr<FactoryOp>& existingOp = (*gFactoryOps)[index - 1];
21025     if (MustWaitFor(*existingOp)) {
21026       // Only one op can be delayed.
21027       MOZ_ASSERT(!existingOp->mDelayedOp);
21028       existingOp->mDelayedOp = this;
21029       delayed = true;
21030       break;
21031     }
21032   }
21033 
21034   // Adding this to the factory ops list will block any additional ops from
21035   // proceeding until this one is done.
21036   gFactoryOps->AppendElement(this);
21037 
21038   if (!delayed) {
21039     QuotaClient* quotaClient = QuotaClient::GetInstance();
21040     MOZ_ASSERT(quotaClient);
21041 
21042     if (RefPtr<Maintenance> currentMaintenance =
21043           quotaClient->GetCurrentMaintenance()) {
21044       if (RefPtr<DatabaseMaintenance> databaseMaintenance =
21045             currentMaintenance->GetDatabaseMaintenance(mDatabaseFilePath)) {
21046         databaseMaintenance->WaitForCompletion(this);
21047         delayed = true;
21048       }
21049     }
21050   }
21051 
21052   mBlockedDatabaseOpen = true;
21053 
21054   // Balanced in FinishSendResults().
21055   IncreaseBusyCount();
21056 
21057   mState = State::DatabaseOpenPending;
21058   if (!delayed) {
21059     nsresult rv = DatabaseOpen();
21060     if (NS_WARN_IF(NS_FAILED(rv))) {
21061       return rv;
21062     }
21063   }
21064 
21065   return NS_OK;
21066 }
21067 
21068 nsresult
SendToIOThread()21069 FactoryOp::SendToIOThread()
21070 {
21071   AssertIsOnOwningThread();
21072   MOZ_ASSERT(mState == State::DatabaseOpenPending);
21073 
21074   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
21075       !OperationMayProceed()) {
21076     IDB_REPORT_INTERNAL_ERR();
21077     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
21078   }
21079 
21080   QuotaManager* quotaManager = QuotaManager::Get();
21081   MOZ_ASSERT(quotaManager);
21082 
21083   // Must set this before dispatching otherwise we will race with the IO thread.
21084   mState = State::DatabaseWorkOpen;
21085 
21086   nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
21087   if (NS_WARN_IF(NS_FAILED(rv))) {
21088     IDB_REPORT_INTERNAL_ERR();
21089     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
21090   }
21091 
21092   return NS_OK;
21093 }
21094 
21095 void
WaitForTransactions()21096 FactoryOp::WaitForTransactions()
21097 {
21098   AssertIsOnOwningThread();
21099   MOZ_ASSERT(mState == State::BeginVersionChange ||
21100              mState == State::WaitingForOtherDatabasesToClose);
21101   MOZ_ASSERT(!mDatabaseId.IsEmpty());
21102   MOZ_ASSERT(!IsActorDestroyed());
21103 
21104   mState = State::WaitingForTransactionsToComplete;
21105 
21106   RefPtr<WaitForTransactionsHelper> helper =
21107     new WaitForTransactionsHelper(mDatabaseId, this);
21108   helper->WaitForTransactions();
21109 }
21110 
21111 void
FinishSendResults()21112 FactoryOp::FinishSendResults()
21113 {
21114   AssertIsOnOwningThread();
21115   MOZ_ASSERT(mState == State::SendingResults);
21116   MOZ_ASSERT(mFactory);
21117 
21118   // Make sure to release the factory on this thread.
21119   RefPtr<Factory> factory;
21120   mFactory.swap(factory);
21121 
21122   if (mBlockedDatabaseOpen) {
21123     if (mDelayedOp) {
21124       MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mDelayedOp.forget()));
21125     }
21126 
21127     MOZ_ASSERT(gFactoryOps);
21128     gFactoryOps->RemoveElement(this);
21129 
21130     // Match the IncreaseBusyCount in DirectoryOpen().
21131     DecreaseBusyCount();
21132   }
21133 
21134   mState = State::Completed;
21135 }
21136 
21137 nsresult
CheckPermission(ContentParent * aContentParent,PermissionRequestBase::PermissionValue * aPermission)21138 FactoryOp::CheckPermission(ContentParent* aContentParent,
21139                            PermissionRequestBase::PermissionValue* aPermission)
21140 {
21141   MOZ_ASSERT(NS_IsMainThread());
21142   MOZ_ASSERT(mState == State::Initial || mState == State::PermissionRetry);
21143 
21144   const PrincipalInfo& principalInfo = mCommonParams.principalInfo();
21145   if (principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
21146     if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo) {
21147       if (aContentParent) {
21148         // We just want ContentPrincipalInfo or SystemPrincipalInfo.
21149         aContentParent->KillHard("IndexedDB CheckPermission 0");
21150       }
21151 
21152       return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
21153     }
21154 
21155     if (NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) {
21156       if (aContentParent) {
21157         // The DOM in the other process should have kept us from receiving any
21158         // indexedDB messages so assume that the child is misbehaving.
21159         aContentParent->KillHard("IndexedDB CheckPermission 1");
21160       }
21161 
21162       return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
21163     }
21164 
21165     const ContentPrincipalInfo& contentPrincipalInfo =
21166       principalInfo.get_ContentPrincipalInfo();
21167     if (contentPrincipalInfo.attrs().mPrivateBrowsingId != 0) {
21168       // IndexedDB is currently disabled in privateBrowsing.
21169       return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
21170     }
21171   }
21172 
21173   mFileHandleDisabled = !Preferences::GetBool(kPrefFileHandleEnabled);
21174 
21175   PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
21176 
21177   MOZ_ASSERT(principalInfo.type() != PrincipalInfo::TNullPrincipalInfo);
21178 
21179   if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
21180     MOZ_ASSERT(mState == State::Initial);
21181     MOZ_ASSERT(persistenceType == PERSISTENCE_TYPE_PERSISTENT);
21182 
21183     if (aContentParent) {
21184       // Check to make sure that the child process has access to the database it
21185       // is accessing.
21186       NS_NAMED_LITERAL_CSTRING(permissionStringBase,
21187                                PERMISSION_STRING_CHROME_BASE);
21188       NS_ConvertUTF16toUTF8 databaseName(mCommonParams.metadata().name());
21189       NS_NAMED_LITERAL_CSTRING(readSuffix, PERMISSION_STRING_CHROME_READ_SUFFIX);
21190       NS_NAMED_LITERAL_CSTRING(writeSuffix, PERMISSION_STRING_CHROME_WRITE_SUFFIX);
21191 
21192       const nsAutoCString permissionStringWrite =
21193         permissionStringBase + databaseName + writeSuffix;
21194       const nsAutoCString permissionStringRead =
21195         permissionStringBase + databaseName + readSuffix;
21196 
21197       bool canWrite =
21198         CheckAtLeastOneAppHasPermission(aContentParent, permissionStringWrite);
21199 
21200       bool canRead;
21201       if (canWrite) {
21202         MOZ_ASSERT(CheckAtLeastOneAppHasPermission(aContentParent,
21203                                                    permissionStringRead));
21204         canRead = true;
21205       } else {
21206         canRead =
21207           CheckAtLeastOneAppHasPermission(aContentParent, permissionStringRead);
21208       }
21209 
21210       // Deleting a database requires write permissions.
21211       if (mDeleting && !canWrite) {
21212         aContentParent->KillHard("IndexedDB CheckPermission 2");
21213         IDB_REPORT_INTERNAL_ERR();
21214         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
21215       }
21216 
21217       // Opening or deleting requires read permissions.
21218       if (!canRead) {
21219         aContentParent->KillHard("IndexedDB CheckPermission 3");
21220         IDB_REPORT_INTERNAL_ERR();
21221         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
21222       }
21223 
21224       mChromeWriteAccessAllowed = canWrite;
21225     } else {
21226       mChromeWriteAccessAllowed = true;
21227     }
21228 
21229     if (State::Initial == mState) {
21230       QuotaManager::GetInfoForChrome(&mSuffix, &mGroup, &mOrigin, &mIsApp);
21231 
21232       MOZ_ASSERT(!QuotaManager::IsFirstPromptRequired(persistenceType, mOrigin,
21233                                                       mIsApp));
21234 
21235       mEnforcingQuota =
21236         QuotaManager::IsQuotaEnforced(persistenceType, mOrigin, mIsApp);
21237     }
21238 
21239     *aPermission = PermissionRequestBase::kPermissionAllowed;
21240     return NS_OK;
21241   }
21242 
21243   MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
21244 
21245   nsresult rv;
21246   nsCOMPtr<nsIPrincipal> principal =
21247     PrincipalInfoToPrincipal(principalInfo, &rv);
21248   if (NS_WARN_IF(NS_FAILED(rv))) {
21249     return rv;
21250   }
21251 
21252   nsCString suffix;
21253   nsCString group;
21254   nsCString origin;
21255   bool isApp;
21256   rv = QuotaManager::GetInfoFromPrincipal(principal,
21257                                           &suffix,
21258                                           &group,
21259                                           &origin,
21260                                           &isApp);
21261   if (NS_WARN_IF(NS_FAILED(rv))) {
21262     return rv;
21263   }
21264 
21265 #ifdef IDB_MOBILE
21266   if (persistenceType == PERSISTENCE_TYPE_PERSISTENT &&
21267       !QuotaManager::IsOriginInternal(origin) &&
21268       !isApp) {
21269     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
21270   }
21271 #endif
21272 
21273   PermissionRequestBase::PermissionValue permission;
21274 
21275   if (QuotaManager::IsFirstPromptRequired(persistenceType, origin, isApp)) {
21276     rv = PermissionRequestBase::GetCurrentPermission(principal, &permission);
21277     if (NS_WARN_IF(NS_FAILED(rv))) {
21278       return rv;
21279     }
21280   } else {
21281     permission = PermissionRequestBase::kPermissionAllowed;
21282   }
21283 
21284   if (permission != PermissionRequestBase::kPermissionDenied &&
21285       State::Initial == mState) {
21286     mSuffix = suffix;
21287     mGroup = group;
21288     mOrigin = origin;
21289     mIsApp = isApp;
21290 
21291     mEnforcingQuota =
21292       QuotaManager::IsQuotaEnforced(persistenceType, mOrigin, mIsApp);
21293   }
21294 
21295   *aPermission = permission;
21296   return NS_OK;
21297 }
21298 
21299 nsresult
SendVersionChangeMessages(DatabaseActorInfo * aDatabaseActorInfo,Database * aOpeningDatabase,uint64_t aOldVersion,const NullableVersion & aNewVersion)21300 FactoryOp::SendVersionChangeMessages(DatabaseActorInfo* aDatabaseActorInfo,
21301                                      Database* aOpeningDatabase,
21302                                      uint64_t aOldVersion,
21303                                      const NullableVersion& aNewVersion)
21304 {
21305   AssertIsOnOwningThread();
21306   MOZ_ASSERT(aDatabaseActorInfo);
21307   MOZ_ASSERT(mState == State::BeginVersionChange);
21308   MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
21309   MOZ_ASSERT(!IsActorDestroyed());
21310 
21311   const uint32_t expectedCount = mDeleting ? 0 : 1;
21312   const uint32_t liveCount = aDatabaseActorInfo->mLiveDatabases.Length();
21313   if (liveCount > expectedCount) {
21314     FallibleTArray<MaybeBlockedDatabaseInfo> maybeBlockedDatabases;
21315     for (uint32_t index = 0; index < liveCount; index++) {
21316       Database* database = aDatabaseActorInfo->mLiveDatabases[index];
21317       if ((!aOpeningDatabase || database != aOpeningDatabase) &&
21318           !database->IsClosed() &&
21319           NS_WARN_IF(!maybeBlockedDatabases.AppendElement(database, fallible))) {
21320         return NS_ERROR_OUT_OF_MEMORY;
21321       }
21322     }
21323 
21324     if (!maybeBlockedDatabases.IsEmpty()) {
21325       mMaybeBlockedDatabases.SwapElements(maybeBlockedDatabases);
21326     }
21327   }
21328 
21329   if (!mMaybeBlockedDatabases.IsEmpty()) {
21330     for (uint32_t count = mMaybeBlockedDatabases.Length(), index = 0;
21331          index < count;
21332          /* incremented conditionally */) {
21333       if (mMaybeBlockedDatabases[index]->SendVersionChange(aOldVersion,
21334                                                            aNewVersion)) {
21335         index++;
21336       } else {
21337         // We don't want to wait forever if we were not able to send the
21338         // message.
21339         mMaybeBlockedDatabases.RemoveElementAt(index);
21340         count--;
21341       }
21342     }
21343   }
21344 
21345   return NS_OK;
21346 }
21347 
21348 // static
21349 bool
CheckAtLeastOneAppHasPermission(ContentParent * aContentParent,const nsACString & aPermissionString)21350 FactoryOp::CheckAtLeastOneAppHasPermission(ContentParent* aContentParent,
21351                                            const nsACString& aPermissionString)
21352 {
21353   MOZ_ASSERT(NS_IsMainThread());
21354   MOZ_ASSERT(aContentParent);
21355   MOZ_ASSERT(!aPermissionString.IsEmpty());
21356 
21357   return true;
21358 }
21359 
21360 nsresult
FinishOpen()21361 FactoryOp::FinishOpen()
21362 {
21363   AssertIsOnOwningThread();
21364   MOZ_ASSERT(mState == State::FinishOpen);
21365   MOZ_ASSERT(!mContentParent);
21366 
21367   if (QuotaManager::Get()) {
21368     nsresult rv = OpenDirectory();
21369     if (NS_WARN_IF(NS_FAILED(rv))) {
21370       return rv;
21371     }
21372 
21373     return NS_OK;
21374   }
21375 
21376   mState = State::QuotaManagerPending;
21377   QuotaManager::GetOrCreate(this);
21378 
21379   return NS_OK;
21380 }
21381 
21382 nsresult
QuotaManagerOpen()21383 FactoryOp::QuotaManagerOpen()
21384 {
21385   AssertIsOnOwningThread();
21386   MOZ_ASSERT(mState == State::QuotaManagerPending);
21387 
21388   if (NS_WARN_IF(!QuotaManager::Get())) {
21389     return NS_ERROR_FAILURE;
21390   }
21391 
21392   nsresult rv = OpenDirectory();
21393   if (NS_WARN_IF(NS_FAILED(rv))) {
21394     return rv;
21395   }
21396 
21397   return NS_OK;
21398 }
21399 
21400 nsresult
OpenDirectory()21401 FactoryOp::OpenDirectory()
21402 {
21403   AssertIsOnOwningThread();
21404   MOZ_ASSERT(mState == State::FinishOpen ||
21405              mState == State::QuotaManagerPending);
21406   MOZ_ASSERT(!mOrigin.IsEmpty());
21407   MOZ_ASSERT(!mDirectoryLock);
21408   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
21409   MOZ_ASSERT(QuotaManager::Get());
21410 
21411   // Need to get database file path in advance.
21412   const nsString& databaseName = mCommonParams.metadata().name();
21413   PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
21414 
21415   QuotaManager* quotaManager = QuotaManager::Get();
21416   MOZ_ASSERT(quotaManager);
21417 
21418   nsCOMPtr<nsIFile> dbFile;
21419   nsresult rv = quotaManager->GetDirectoryForOrigin(persistenceType,
21420                                                     mOrigin,
21421                                                     getter_AddRefs(dbFile));
21422   if (NS_WARN_IF(NS_FAILED(rv))) {
21423     return rv;
21424   }
21425 
21426   rv = dbFile->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
21427   if (NS_WARN_IF(NS_FAILED(rv))) {
21428     return rv;
21429   }
21430 
21431   nsAutoString filename;
21432   GetDatabaseFilename(databaseName, filename);
21433 
21434   rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
21435   if (NS_WARN_IF(NS_FAILED(rv))) {
21436     return rv;
21437   }
21438 
21439   rv = dbFile->GetPath(mDatabaseFilePath);
21440   if (NS_WARN_IF(NS_FAILED(rv))) {
21441     return rv;
21442   }
21443 
21444   mState = State::DirectoryOpenPending;
21445 
21446   quotaManager->OpenDirectory(persistenceType,
21447                               mGroup,
21448                               mOrigin,
21449                               mIsApp,
21450                               Client::IDB,
21451                               /* aExclusive */ false,
21452                               this);
21453 
21454   return NS_OK;
21455 }
21456 
21457 bool
MustWaitFor(const FactoryOp & aExistingOp)21458 FactoryOp::MustWaitFor(const FactoryOp& aExistingOp)
21459 {
21460   AssertIsOnOwningThread();
21461 
21462   // Things for the same persistence type, the same origin and the same
21463   // database must wait.
21464   return aExistingOp.mCommonParams.metadata().persistenceType() ==
21465            mCommonParams.metadata().persistenceType() &&
21466          aExistingOp.mOrigin == mOrigin &&
21467          aExistingOp.mDatabaseId == mDatabaseId;
21468 }
21469 
21470 void
NoteDatabaseBlocked(Database * aDatabase)21471 FactoryOp::NoteDatabaseBlocked(Database* aDatabase)
21472 {
21473   AssertIsOnOwningThread();
21474   MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose);
21475   MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty());
21476   MOZ_ASSERT(mMaybeBlockedDatabases.Contains(aDatabase));
21477 
21478   // Only send the blocked event if all databases have reported back. If the
21479   // database was closed then it will have been removed from the array.
21480   // Otherwise if it was blocked its |mBlocked| flag will be true.
21481   bool sendBlockedEvent = true;
21482 
21483   for (uint32_t count = mMaybeBlockedDatabases.Length(), index = 0;
21484        index < count;
21485        index++) {
21486     MaybeBlockedDatabaseInfo& info = mMaybeBlockedDatabases[index];
21487     if (info == aDatabase) {
21488       // This database was blocked, mark accordingly.
21489       info.mBlocked = true;
21490     } else if (!info.mBlocked) {
21491       // A database has not yet reported back yet, don't send the event yet.
21492       sendBlockedEvent = false;
21493     }
21494   }
21495 
21496   if (sendBlockedEvent) {
21497     SendBlockedNotification();
21498   }
21499 }
21500 
NS_IMPL_ISUPPORTS_INHERITED0(FactoryOp,DatabaseOperationBase)21501 NS_IMPL_ISUPPORTS_INHERITED0(FactoryOp, DatabaseOperationBase)
21502 
21503 // Run() assumes that the caller holds a strong reference to the object that
21504 // can't be cleared while Run() is being executed.
21505 // So if you call Run() directly (as opposed to dispatching to an event queue)
21506 // you need to make sure there's such a reference.
21507 // See bug 1356824 for more details.
21508 NS_IMETHODIMP
21509 FactoryOp::Run()
21510 {
21511   nsresult rv;
21512 
21513   switch (mState) {
21514     case State::Initial:
21515       rv = Open();
21516       break;
21517 
21518     case State::PermissionChallenge:
21519       rv = ChallengePermission();
21520       break;
21521 
21522     case State::PermissionRetry:
21523       rv = RetryCheckPermission();
21524       break;
21525 
21526     case State::FinishOpen:
21527       rv = FinishOpen();
21528       break;
21529 
21530     case State::QuotaManagerPending:
21531       rv = QuotaManagerOpen();
21532       break;
21533 
21534     case State::DatabaseOpenPending:
21535       rv = DatabaseOpen();
21536       break;
21537 
21538     case State::DatabaseWorkOpen:
21539       rv = DoDatabaseWork();
21540       break;
21541 
21542     case State::BeginVersionChange:
21543       rv = BeginVersionChange();
21544       break;
21545 
21546     case State::WaitingForTransactionsToComplete:
21547       rv = DispatchToWorkThread();
21548       break;
21549 
21550     case State::SendingResults:
21551       SendResults();
21552       return NS_OK;
21553 
21554     default:
21555       MOZ_CRASH("Bad state!");
21556   }
21557 
21558   if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
21559     if (NS_SUCCEEDED(mResultCode)) {
21560       mResultCode = rv;
21561     }
21562 
21563     // Must set mState before dispatching otherwise we will race with the owning
21564     // thread.
21565     mState = State::SendingResults;
21566 
21567     if (IsOnOwningThread()) {
21568       SendResults();
21569     } else {
21570       MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
21571     }
21572   }
21573 
21574   return NS_OK;
21575 }
21576 
21577 void
DirectoryLockAcquired(DirectoryLock * aLock)21578 FactoryOp::DirectoryLockAcquired(DirectoryLock* aLock)
21579 {
21580   AssertIsOnOwningThread();
21581   MOZ_ASSERT(mState == State::DirectoryOpenPending);
21582   MOZ_ASSERT(!mDirectoryLock);
21583 
21584   mDirectoryLock = aLock;
21585 
21586   nsresult rv = DirectoryOpen();
21587   if (NS_WARN_IF(NS_FAILED(rv))) {
21588     if (NS_SUCCEEDED(mResultCode)) {
21589       mResultCode = rv;
21590     }
21591 
21592     // The caller holds a strong reference to us, no need for a self reference
21593     // before calling Run().
21594 
21595     mState = State::SendingResults;
21596     MOZ_ALWAYS_SUCCEEDS(Run());
21597 
21598     return;
21599   }
21600 }
21601 
21602 void
DirectoryLockFailed()21603 FactoryOp::DirectoryLockFailed()
21604 {
21605   AssertIsOnOwningThread();
21606   MOZ_ASSERT(mState == State::DirectoryOpenPending);
21607   MOZ_ASSERT(!mDirectoryLock);
21608 
21609   if (NS_SUCCEEDED(mResultCode)) {
21610     IDB_REPORT_INTERNAL_ERR();
21611     mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
21612   }
21613 
21614   // The caller holds a strong reference to us, no need for a self reference
21615   // before calling Run().
21616 
21617   mState = State::SendingResults;
21618   MOZ_ALWAYS_SUCCEEDS(Run());
21619 }
21620 
21621 void
ActorDestroy(ActorDestroyReason aWhy)21622 FactoryOp::ActorDestroy(ActorDestroyReason aWhy)
21623 {
21624   AssertIsOnBackgroundThread();
21625 
21626   NoteActorDestroyed();
21627 }
21628 
21629 bool
RecvPermissionRetry()21630 FactoryOp::RecvPermissionRetry()
21631 {
21632   AssertIsOnOwningThread();
21633   MOZ_ASSERT(!IsActorDestroyed());
21634   MOZ_ASSERT(mState == State::PermissionChallenge);
21635 
21636   mContentParent = BackgroundParent::GetContentParent(Manager()->Manager());
21637 
21638   mState = State::PermissionRetry;
21639   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
21640 
21641   return true;
21642 }
21643 
OpenDatabaseOp(Factory * aFactory,already_AddRefed<ContentParent> aContentParent,const CommonFactoryRequestParams & aParams)21644 OpenDatabaseOp::OpenDatabaseOp(Factory* aFactory,
21645                                already_AddRefed<ContentParent> aContentParent,
21646                                const CommonFactoryRequestParams& aParams)
21647   : FactoryOp(aFactory, Move(aContentParent), aParams, /* aDeleting */ false)
21648   , mMetadata(new FullDatabaseMetadata(aParams.metadata()))
21649   , mRequestedVersion(aParams.metadata().version())
21650   , mVersionChangeOp(nullptr)
21651   , mTelemetryId(0)
21652 {
21653   if (mContentParent) {
21654     // This is a little scary but it looks safe to call this off the main thread
21655     // for now.
21656     mOptionalContentParentId = Some(mContentParent->ChildID());
21657   }
21658 }
21659 
21660 void
ActorDestroy(ActorDestroyReason aWhy)21661 OpenDatabaseOp::ActorDestroy(ActorDestroyReason aWhy)
21662 {
21663   AssertIsOnOwningThread();
21664 
21665   FactoryOp::ActorDestroy(aWhy);
21666 
21667   if (mVersionChangeOp) {
21668     mVersionChangeOp->NoteActorDestroyed();
21669   }
21670 }
21671 
21672 nsresult
DatabaseOpen()21673 OpenDatabaseOp::DatabaseOpen()
21674 {
21675   AssertIsOnOwningThread();
21676   MOZ_ASSERT(mState == State::DatabaseOpenPending);
21677 
21678   nsresult rv = SendToIOThread();
21679   if (NS_WARN_IF(NS_FAILED(rv))) {
21680     return rv;
21681   }
21682 
21683   return NS_OK;
21684 }
21685 
21686 nsresult
DoDatabaseWork()21687 OpenDatabaseOp::DoDatabaseWork()
21688 {
21689   AssertIsOnIOThread();
21690   MOZ_ASSERT(mState == State::DatabaseWorkOpen);
21691 
21692   PROFILER_LABEL("IndexedDB",
21693                  "OpenDatabaseOp::DoDatabaseWork",
21694                  js::ProfileEntry::Category::STORAGE);
21695 
21696   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
21697       !OperationMayProceed()) {
21698     IDB_REPORT_INTERNAL_ERR();
21699     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
21700   }
21701 
21702   const nsString& databaseName = mCommonParams.metadata().name();
21703   PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
21704 
21705   QuotaManager* quotaManager = QuotaManager::Get();
21706   MOZ_ASSERT(quotaManager);
21707 
21708   nsCOMPtr<nsIFile> dbDirectory;
21709 
21710   nsresult rv =
21711     quotaManager->EnsureOriginIsInitialized(persistenceType,
21712                                             mSuffix,
21713                                             mGroup,
21714                                             mOrigin,
21715                                             mIsApp,
21716                                             getter_AddRefs(dbDirectory));
21717   if (NS_WARN_IF(NS_FAILED(rv))) {
21718     return rv;
21719   }
21720 
21721   rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
21722   if (NS_WARN_IF(NS_FAILED(rv))) {
21723     return rv;
21724   }
21725 
21726   bool exists;
21727   rv = dbDirectory->Exists(&exists);
21728   if (NS_WARN_IF(NS_FAILED(rv))) {
21729     return rv;
21730   }
21731 
21732   if (!exists) {
21733     rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
21734     if (NS_WARN_IF(NS_FAILED(rv))) {
21735       return rv;
21736     }
21737   }
21738 #ifdef DEBUG
21739   else {
21740     bool isDirectory;
21741     MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)));
21742     MOZ_ASSERT(isDirectory);
21743   }
21744 #endif
21745 
21746   nsAutoString filename;
21747   GetDatabaseFilename(databaseName, filename);
21748 
21749   nsCOMPtr<nsIFile> dbFile;
21750   rv = dbDirectory->Clone(getter_AddRefs(dbFile));
21751   if (NS_WARN_IF(NS_FAILED(rv))) {
21752     return rv;
21753   }
21754 
21755   rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
21756   if (NS_WARN_IF(NS_FAILED(rv))) {
21757     return rv;
21758   }
21759 
21760   mTelemetryId = TelemetryIdForFile(dbFile);
21761 
21762 #ifdef DEBUG
21763   nsString databaseFilePath;
21764   rv = dbFile->GetPath(databaseFilePath);
21765   if (NS_WARN_IF(NS_FAILED(rv))) {
21766     return rv;
21767   }
21768 
21769   MOZ_ASSERT(databaseFilePath == mDatabaseFilePath);
21770 #endif
21771 
21772   nsCOMPtr<nsIFile> fmDirectory;
21773   rv = dbDirectory->Clone(getter_AddRefs(fmDirectory));
21774   if (NS_WARN_IF(NS_FAILED(rv))) {
21775     return rv;
21776   }
21777 
21778   const NS_ConvertASCIItoUTF16 filesSuffix(kFileManagerDirectoryNameSuffix);
21779 
21780   rv = fmDirectory->Append(filename + filesSuffix);
21781   if (NS_WARN_IF(NS_FAILED(rv))) {
21782     return rv;
21783   }
21784 
21785   nsCOMPtr<mozIStorageConnection> connection;
21786   rv = CreateStorageConnection(dbFile,
21787                                fmDirectory,
21788                                databaseName,
21789                                persistenceType,
21790                                mGroup,
21791                                mOrigin,
21792                                mTelemetryId,
21793                                getter_AddRefs(connection));
21794   if (NS_WARN_IF(NS_FAILED(rv))) {
21795     return rv;
21796   }
21797 
21798   AutoSetProgressHandler asph;
21799   rv = asph.Register(connection, this);
21800   if (NS_WARN_IF(NS_FAILED(rv))) {
21801     return rv;
21802   }
21803 
21804   rv = LoadDatabaseInformation(connection);
21805   if (NS_WARN_IF(NS_FAILED(rv))) {
21806     return rv;
21807   }
21808 
21809   MOZ_ASSERT(mMetadata->mNextObjectStoreId > mMetadata->mObjectStores.Count());
21810   MOZ_ASSERT(mMetadata->mNextIndexId > 0);
21811 
21812   // See if we need to do a versionchange transaction
21813 
21814   // Optional version semantics.
21815   if (!mRequestedVersion) {
21816     // If the requested version was not specified and the database was created,
21817     // treat it as if version 1 were requested.
21818     if (mMetadata->mCommonMetadata.version() == 0) {
21819       mRequestedVersion = 1;
21820     } else {
21821       // Otherwise, treat it as if the current version were requested.
21822       mRequestedVersion = mMetadata->mCommonMetadata.version();
21823     }
21824   }
21825 
21826   if (NS_WARN_IF(mMetadata->mCommonMetadata.version() > mRequestedVersion)) {
21827     return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR;
21828   }
21829 
21830   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
21831   MOZ_ASSERT(mgr);
21832 
21833   RefPtr<FileManager> fileManager =
21834     mgr->GetFileManager(persistenceType, mOrigin, databaseName);
21835   if (!fileManager) {
21836     fileManager = new FileManager(persistenceType,
21837                                   mGroup,
21838                                   mOrigin,
21839                                   mIsApp,
21840                                   databaseName,
21841                                   mEnforcingQuota);
21842 
21843     rv = fileManager->Init(fmDirectory, connection);
21844     if (NS_WARN_IF(NS_FAILED(rv))) {
21845       return rv;
21846     }
21847 
21848     mgr->AddFileManager(fileManager);
21849   }
21850 
21851   mFileManager = fileManager.forget();
21852 
21853   // Must set mState before dispatching otherwise we will race with the owning
21854   // thread.
21855   mState = (mMetadata->mCommonMetadata.version() == mRequestedVersion) ?
21856            State::SendingResults :
21857            State::BeginVersionChange;
21858 
21859   rv = mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL);
21860   if (NS_WARN_IF(NS_FAILED(rv))) {
21861     return rv;
21862   }
21863 
21864   return NS_OK;
21865 }
21866 
21867 nsresult
LoadDatabaseInformation(mozIStorageConnection * aConnection)21868 OpenDatabaseOp::LoadDatabaseInformation(mozIStorageConnection* aConnection)
21869 {
21870   AssertIsOnIOThread();
21871   MOZ_ASSERT(aConnection);
21872   MOZ_ASSERT(mMetadata);
21873 
21874   // Load version information.
21875   nsCOMPtr<mozIStorageStatement> stmt;
21876   nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
21877     "SELECT name, origin, version "
21878     "FROM database"
21879   ), getter_AddRefs(stmt));
21880   if (NS_WARN_IF(NS_FAILED(rv))) {
21881     return rv;
21882   }
21883 
21884   bool hasResult;
21885   rv = stmt->ExecuteStep(&hasResult);
21886   if (NS_WARN_IF(NS_FAILED(rv))) {
21887     return rv;
21888   }
21889 
21890   if (NS_WARN_IF(!hasResult)) {
21891     return NS_ERROR_FILE_CORRUPTED;
21892   }
21893 
21894   nsString databaseName;
21895   rv = stmt->GetString(0, databaseName);
21896   if (NS_WARN_IF(NS_FAILED(rv))) {
21897     return rv;
21898   }
21899 
21900   if (NS_WARN_IF(mCommonParams.metadata().name() != databaseName)) {
21901     return NS_ERROR_FILE_CORRUPTED;
21902   }
21903 
21904   nsCString origin;
21905   rv = stmt->GetUTF8String(1, origin);
21906   if (NS_WARN_IF(NS_FAILED(rv))) {
21907     return rv;
21908   }
21909 
21910   if (mOrigin != origin) {
21911     NS_WARNING("Origins don't match!");
21912   }
21913 
21914   int64_t version;
21915   rv = stmt->GetInt64(2, &version);
21916   if (NS_WARN_IF(NS_FAILED(rv))) {
21917     return rv;
21918   }
21919 
21920   mMetadata->mCommonMetadata.version() = uint64_t(version);
21921 
21922   ObjectStoreTable& objectStores = mMetadata->mObjectStores;
21923 
21924   // Load object store names and ids.
21925   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
21926     "SELECT id, auto_increment, name, key_path "
21927     "FROM object_store"
21928   ), getter_AddRefs(stmt));
21929   if (NS_WARN_IF(NS_FAILED(rv))) {
21930     return rv;
21931   }
21932 
21933   Maybe<nsTHashtable<nsUint64HashKey>> usedIds;
21934   Maybe<nsTHashtable<nsStringHashKey>> usedNames;
21935 
21936   int64_t lastObjectStoreId = 0;
21937 
21938   while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
21939     int64_t objectStoreId;
21940     rv = stmt->GetInt64(0, &objectStoreId);
21941     if (NS_WARN_IF(NS_FAILED(rv))) {
21942       return rv;
21943     }
21944 
21945     if (!usedIds) {
21946       usedIds.emplace();
21947     }
21948 
21949     if (NS_WARN_IF(objectStoreId <= 0) ||
21950         NS_WARN_IF(usedIds.ref().Contains(objectStoreId))) {
21951       return NS_ERROR_FILE_CORRUPTED;
21952     }
21953 
21954     if (NS_WARN_IF(!usedIds.ref().PutEntry(objectStoreId, fallible))) {
21955       return NS_ERROR_OUT_OF_MEMORY;
21956     }
21957 
21958     nsString name;
21959     rv = stmt->GetString(2, name);
21960     if (NS_WARN_IF(NS_FAILED(rv))) {
21961       return rv;
21962     }
21963 
21964     if (!usedNames) {
21965       usedNames.emplace();
21966     }
21967 
21968     if (NS_WARN_IF(usedNames.ref().Contains(name))) {
21969       return NS_ERROR_FILE_CORRUPTED;
21970     }
21971 
21972     if (NS_WARN_IF(!usedNames.ref().PutEntry(name, fallible))) {
21973       return NS_ERROR_OUT_OF_MEMORY;
21974     }
21975 
21976     RefPtr<FullObjectStoreMetadata> metadata = new FullObjectStoreMetadata();
21977     metadata->mCommonMetadata.id() = objectStoreId;
21978     metadata->mCommonMetadata.name() = name;
21979 
21980     int32_t columnType;
21981     rv = stmt->GetTypeOfIndex(3, &columnType);
21982     if (NS_WARN_IF(NS_FAILED(rv))) {
21983       return rv;
21984     }
21985 
21986     if (columnType == mozIStorageStatement::VALUE_TYPE_NULL) {
21987       metadata->mCommonMetadata.keyPath() = KeyPath(0);
21988     } else {
21989       MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_TEXT);
21990 
21991       nsString keyPathSerialization;
21992       rv = stmt->GetString(3, keyPathSerialization);
21993       if (NS_WARN_IF(NS_FAILED(rv))) {
21994         return rv;
21995       }
21996 
21997       metadata->mCommonMetadata.keyPath() =
21998         KeyPath::DeserializeFromString(keyPathSerialization);
21999       if (NS_WARN_IF(!metadata->mCommonMetadata.keyPath().IsValid())) {
22000         return NS_ERROR_FILE_CORRUPTED;
22001       }
22002     }
22003 
22004     int64_t nextAutoIncrementId;
22005     rv = stmt->GetInt64(1, &nextAutoIncrementId);
22006     if (NS_WARN_IF(NS_FAILED(rv))) {
22007       return rv;
22008     }
22009 
22010     metadata->mCommonMetadata.autoIncrement() = !!nextAutoIncrementId;
22011     metadata->mNextAutoIncrementId = nextAutoIncrementId;
22012     metadata->mCommittedAutoIncrementId = nextAutoIncrementId;
22013 
22014     if (NS_WARN_IF(!objectStores.Put(objectStoreId, metadata, fallible))) {
22015       return NS_ERROR_OUT_OF_MEMORY;
22016     }
22017 
22018     lastObjectStoreId = std::max(lastObjectStoreId, objectStoreId);
22019   }
22020 
22021   if (NS_WARN_IF(NS_FAILED(rv))) {
22022     return rv;
22023   }
22024 
22025   usedIds.reset();
22026   usedNames.reset();
22027 
22028   // Load index information
22029   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
22030     "SELECT "
22031       "id, object_store_id, name, key_path, unique_index, multientry, "
22032       "locale, is_auto_locale "
22033     "FROM object_store_index"
22034   ), getter_AddRefs(stmt));
22035   if (NS_WARN_IF(NS_FAILED(rv))) {
22036     return rv;
22037   }
22038 
22039   int64_t lastIndexId = 0;
22040 
22041   while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
22042     int64_t objectStoreId;
22043     rv = stmt->GetInt64(1, &objectStoreId);
22044     if (NS_WARN_IF(NS_FAILED(rv))) {
22045       return rv;
22046     }
22047 
22048     RefPtr<FullObjectStoreMetadata> objectStoreMetadata;
22049     if (NS_WARN_IF(!objectStores.Get(objectStoreId,
22050                                      getter_AddRefs(objectStoreMetadata)))) {
22051       return NS_ERROR_FILE_CORRUPTED;
22052     }
22053 
22054     MOZ_ASSERT(objectStoreMetadata->mCommonMetadata.id() == objectStoreId);
22055 
22056     int64_t indexId;
22057     rv = stmt->GetInt64(0, &indexId);
22058     if (NS_WARN_IF(NS_FAILED(rv))) {
22059       return rv;
22060     }
22061 
22062     if (!usedIds) {
22063       usedIds.emplace();
22064     }
22065 
22066     if (NS_WARN_IF(indexId <= 0) ||
22067         NS_WARN_IF(usedIds.ref().Contains(indexId))) {
22068       return NS_ERROR_FILE_CORRUPTED;
22069     }
22070 
22071     if (NS_WARN_IF(!usedIds.ref().PutEntry(indexId, fallible))) {
22072       return NS_ERROR_OUT_OF_MEMORY;
22073     }
22074 
22075     nsString name;
22076     rv = stmt->GetString(2, name);
22077     if (NS_WARN_IF(NS_FAILED(rv))) {
22078       return rv;
22079     }
22080 
22081     nsAutoString hashName;
22082     hashName.AppendInt(indexId);
22083     hashName.Append(':');
22084     hashName.Append(name);
22085 
22086     if (!usedNames) {
22087       usedNames.emplace();
22088     }
22089 
22090     if (NS_WARN_IF(usedNames.ref().Contains(hashName))) {
22091       return NS_ERROR_FILE_CORRUPTED;
22092     }
22093 
22094     if (NS_WARN_IF(!usedNames.ref().PutEntry(hashName, fallible))) {
22095       return NS_ERROR_OUT_OF_MEMORY;
22096     }
22097 
22098     RefPtr<FullIndexMetadata> indexMetadata = new FullIndexMetadata();
22099     indexMetadata->mCommonMetadata.id() = indexId;
22100     indexMetadata->mCommonMetadata.name() = name;
22101 
22102 #ifdef DEBUG
22103     {
22104       int32_t columnType;
22105       rv = stmt->GetTypeOfIndex(3, &columnType);
22106       MOZ_ASSERT(NS_SUCCEEDED(rv));
22107       MOZ_ASSERT(columnType != mozIStorageStatement::VALUE_TYPE_NULL);
22108     }
22109 #endif
22110 
22111     nsString keyPathSerialization;
22112     rv = stmt->GetString(3, keyPathSerialization);
22113     if (NS_WARN_IF(NS_FAILED(rv))) {
22114       return rv;
22115     }
22116 
22117     indexMetadata->mCommonMetadata.keyPath() =
22118       KeyPath::DeserializeFromString(keyPathSerialization);
22119     if (NS_WARN_IF(!indexMetadata->mCommonMetadata.keyPath().IsValid())) {
22120       return NS_ERROR_FILE_CORRUPTED;
22121     }
22122 
22123     int32_t scratch;
22124     rv = stmt->GetInt32(4, &scratch);
22125     if (NS_WARN_IF(NS_FAILED(rv))) {
22126       return rv;
22127     }
22128 
22129     indexMetadata->mCommonMetadata.unique() = !!scratch;
22130 
22131     rv = stmt->GetInt32(5, &scratch);
22132     if (NS_WARN_IF(NS_FAILED(rv))) {
22133       return rv;
22134     }
22135 
22136     indexMetadata->mCommonMetadata.multiEntry() = !!scratch;
22137 
22138 #ifdef ENABLE_INTL_API
22139     const bool localeAware = !stmt->IsNull(6);
22140     if (localeAware) {
22141       rv = stmt->GetUTF8String(6, indexMetadata->mCommonMetadata.locale());
22142       if (NS_WARN_IF(NS_FAILED(rv))) {
22143         return rv;
22144       }
22145 
22146       rv = stmt->GetInt32(7, &scratch);
22147       if (NS_WARN_IF(NS_FAILED(rv))) {
22148         return rv;
22149       }
22150 
22151       indexMetadata->mCommonMetadata.autoLocale() = !!scratch;
22152 
22153       // Update locale-aware indexes if necessary
22154       const nsCString& indexedLocale = indexMetadata->mCommonMetadata.locale();
22155       const bool& isAutoLocale = indexMetadata->mCommonMetadata.autoLocale();
22156       nsCString systemLocale = IndexedDatabaseManager::GetLocale();
22157       if (!systemLocale.IsEmpty() &&
22158           isAutoLocale &&
22159           !indexedLocale.EqualsASCII(systemLocale.get())) {
22160         rv = UpdateLocaleAwareIndex(aConnection,
22161                                     indexMetadata->mCommonMetadata,
22162                                     systemLocale);
22163         if (NS_WARN_IF(NS_FAILED(rv))) {
22164           return rv;
22165         }
22166       }
22167     }
22168 #endif
22169 
22170     if (NS_WARN_IF(!objectStoreMetadata->mIndexes.Put(indexId, indexMetadata,
22171                                                       fallible))) {
22172       return NS_ERROR_OUT_OF_MEMORY;
22173     }
22174 
22175     lastIndexId = std::max(lastIndexId, indexId);
22176   }
22177 
22178   if (NS_WARN_IF(NS_FAILED(rv))) {
22179     return rv;
22180   }
22181 
22182   if (NS_WARN_IF(lastObjectStoreId == INT64_MAX) ||
22183       NS_WARN_IF(lastIndexId == INT64_MAX)) {
22184     IDB_REPORT_INTERNAL_ERR();
22185     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22186   }
22187 
22188   mMetadata->mNextObjectStoreId = lastObjectStoreId + 1;
22189   mMetadata->mNextIndexId = lastIndexId + 1;
22190 
22191   return NS_OK;
22192 }
22193 
22194 #ifdef ENABLE_INTL_API
22195 /* static */
22196 nsresult
UpdateLocaleAwareIndex(mozIStorageConnection * aConnection,const IndexMetadata & aIndexMetadata,const nsCString & aLocale)22197 OpenDatabaseOp::UpdateLocaleAwareIndex(mozIStorageConnection* aConnection,
22198                                        const IndexMetadata& aIndexMetadata,
22199                                        const nsCString& aLocale)
22200 {
22201   nsresult rv;
22202 
22203   nsCString indexTable;
22204   if (aIndexMetadata.unique()) {
22205     indexTable.AssignLiteral("unique_index_data");
22206   }
22207   else {
22208     indexTable.AssignLiteral("index_data");
22209   }
22210 
22211   nsCString readQuery = NS_LITERAL_CSTRING("SELECT value, object_data_key FROM ") +
22212                         indexTable +
22213                         NS_LITERAL_CSTRING(" WHERE index_id = :index_id");
22214   nsCOMPtr<mozIStorageStatement> readStmt;
22215   rv = aConnection->CreateStatement(readQuery, getter_AddRefs(readStmt));
22216   if (NS_WARN_IF(NS_FAILED(rv))) {
22217     return rv;
22218   }
22219 
22220   rv = readStmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
22221                              aIndexMetadata.id());
22222   if (NS_WARN_IF(NS_FAILED(rv))) {
22223     return rv;
22224   }
22225 
22226   nsCOMPtr<mozIStorageStatement> writeStmt;
22227   bool needCreateWriteQuery = true;
22228   bool hasResult;
22229   while (NS_SUCCEEDED((rv = readStmt->ExecuteStep(&hasResult))) && hasResult) {
22230     if (needCreateWriteQuery) {
22231       needCreateWriteQuery = false;
22232       nsCString writeQuery = NS_LITERAL_CSTRING("UPDATE ") + indexTable +
22233                              NS_LITERAL_CSTRING("SET value_locale = :value_locale "
22234                                                 "WHERE index_id = :index_id AND "
22235                                                 "value = :value AND "
22236                                                 "object_data_key = :object_data_key");
22237       rv = aConnection->CreateStatement(writeQuery, getter_AddRefs(writeStmt));
22238       if (NS_WARN_IF(NS_FAILED(rv))) {
22239         return rv;
22240       }
22241     }
22242 
22243     mozStorageStatementScoper scoper(writeStmt);
22244     rv = writeStmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
22245                                     aIndexMetadata.id());
22246     if (NS_WARN_IF(NS_FAILED(rv))) {
22247       return rv;
22248     }
22249 
22250     Key oldKey, newSortKey, objectKey;
22251     rv = oldKey.SetFromStatement(readStmt, 0);
22252     if (NS_WARN_IF(NS_FAILED(rv))) {
22253       return rv;
22254     }
22255 
22256     rv = oldKey.BindToStatement(writeStmt, NS_LITERAL_CSTRING("value"));
22257     if (NS_WARN_IF(NS_FAILED(rv))) {
22258       return rv;
22259     }
22260 
22261     rv = oldKey.ToLocaleBasedKey(newSortKey, aLocale);
22262     if (NS_WARN_IF(NS_FAILED(rv))) {
22263       return rv;
22264     }
22265 
22266     rv = newSortKey.BindToStatement(writeStmt,
22267                                     NS_LITERAL_CSTRING("value_locale"));
22268     if (NS_WARN_IF(NS_FAILED(rv))) {
22269       return rv;
22270     }
22271 
22272     rv = objectKey.SetFromStatement(readStmt, 1);
22273     if (NS_WARN_IF(NS_FAILED(rv))) {
22274       return rv;
22275     }
22276 
22277     rv = objectKey.BindToStatement(writeStmt,
22278                                    NS_LITERAL_CSTRING("object_data_key"));
22279     if (NS_WARN_IF(NS_FAILED(rv))) {
22280       return rv;
22281     }
22282 
22283     rv = writeStmt->Execute();
22284     if (NS_WARN_IF(NS_FAILED(rv))) {
22285       return rv;
22286     }
22287   }
22288 
22289   nsCString metaQuery = NS_LITERAL_CSTRING("UPDATE object_store_index SET "
22290                                            "locale = :locale WHERE id = :id");
22291   nsCOMPtr<mozIStorageStatement> metaStmt;
22292   rv = aConnection->CreateStatement(metaQuery, getter_AddRefs(metaStmt));
22293   if (NS_WARN_IF(NS_FAILED(rv))) {
22294     return rv;
22295   }
22296 
22297   nsString locale;
22298   locale.AssignWithConversion(aLocale);
22299   rv = metaStmt->BindStringByName(NS_LITERAL_CSTRING("locale"), locale);
22300   if (NS_WARN_IF(NS_FAILED(rv))) {
22301     return rv;
22302   }
22303 
22304   rv = metaStmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aIndexMetadata.id());
22305   if (NS_WARN_IF(NS_FAILED(rv))) {
22306     return rv;
22307   }
22308 
22309   rv = metaStmt->Execute();
22310   return rv;
22311 }
22312 #endif
22313 
22314 nsresult
BeginVersionChange()22315 OpenDatabaseOp::BeginVersionChange()
22316 {
22317   AssertIsOnOwningThread();
22318   MOZ_ASSERT(mState == State::BeginVersionChange);
22319   MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
22320   MOZ_ASSERT(mMetadata->mCommonMetadata.version() <= mRequestedVersion);
22321   MOZ_ASSERT(!mDatabase);
22322   MOZ_ASSERT(!mVersionChangeTransaction);
22323 
22324   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
22325       IsActorDestroyed()) {
22326     IDB_REPORT_INTERNAL_ERR();
22327     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22328   }
22329 
22330   EnsureDatabaseActor();
22331 
22332   if (mDatabase->IsInvalidated()) {
22333     IDB_REPORT_INTERNAL_ERR();
22334     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22335   }
22336 
22337   MOZ_ASSERT(!mDatabase->IsClosed());
22338 
22339   DatabaseActorInfo* info;
22340   MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info));
22341 
22342   MOZ_ASSERT(info->mLiveDatabases.Contains(mDatabase));
22343   MOZ_ASSERT(!info->mWaitingFactoryOp);
22344   MOZ_ASSERT(info->mMetadata == mMetadata);
22345 
22346   RefPtr<VersionChangeTransaction> transaction =
22347     new VersionChangeTransaction(this);
22348 
22349   if (NS_WARN_IF(!transaction->CopyDatabaseMetadata())) {
22350     return NS_ERROR_OUT_OF_MEMORY;
22351   }
22352 
22353   MOZ_ASSERT(info->mMetadata != mMetadata);
22354   mMetadata = info->mMetadata;
22355 
22356   NullableVersion newVersion = mRequestedVersion;
22357 
22358   nsresult rv =
22359     SendVersionChangeMessages(info,
22360                               mDatabase,
22361                               mMetadata->mCommonMetadata.version(),
22362                               newVersion);
22363   if (NS_WARN_IF(NS_FAILED(rv))) {
22364     return rv;
22365   }
22366 
22367   mVersionChangeTransaction.swap(transaction);
22368 
22369   if (mMaybeBlockedDatabases.IsEmpty()) {
22370     // We don't need to wait on any databases, just jump to the transaction
22371     // pool.
22372     WaitForTransactions();
22373     return NS_OK;
22374   }
22375 
22376   info->mWaitingFactoryOp = this;
22377 
22378   mState = State::WaitingForOtherDatabasesToClose;
22379   return NS_OK;
22380 }
22381 
22382 void
NoteDatabaseClosed(Database * aDatabase)22383 OpenDatabaseOp::NoteDatabaseClosed(Database* aDatabase)
22384 {
22385   AssertIsOnOwningThread();
22386   MOZ_ASSERT(aDatabase);
22387   MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose ||
22388              mState == State::WaitingForTransactionsToComplete ||
22389              mState == State::DatabaseWorkVersionChange);
22390 
22391   if (mState != State::WaitingForOtherDatabasesToClose) {
22392     MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
22393     MOZ_ASSERT(mRequestedVersion >
22394                  aDatabase->Metadata()->mCommonMetadata.version(),
22395                "Must only be closing databases for a previous version!");
22396     return;
22397   }
22398 
22399   MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty());
22400 
22401   bool actorDestroyed = IsActorDestroyed() || mDatabase->IsActorDestroyed();
22402 
22403   nsresult rv;
22404   if (actorDestroyed) {
22405     IDB_REPORT_INTERNAL_ERR();
22406     rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22407   } else {
22408     rv = NS_OK;
22409   }
22410 
22411   // We are being called with an assuption that mWaitingFactoryOp holds a strong
22412   // reference to us.
22413   RefPtr<OpenDatabaseOp> kungFuDeathGrip;
22414 
22415   if (mMaybeBlockedDatabases.RemoveElement(aDatabase) &&
22416       mMaybeBlockedDatabases.IsEmpty()) {
22417     if (actorDestroyed) {
22418       DatabaseActorInfo* info;
22419       MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info));
22420       MOZ_ASSERT(info->mWaitingFactoryOp == this);
22421       kungFuDeathGrip =
22422         static_cast<OpenDatabaseOp*>(info->mWaitingFactoryOp.get());
22423       info->mWaitingFactoryOp = nullptr;
22424     } else {
22425       WaitForTransactions();
22426     }
22427   }
22428 
22429   if (NS_WARN_IF(NS_FAILED(rv))) {
22430     if (NS_SUCCEEDED(mResultCode)) {
22431       mResultCode = rv;
22432     }
22433 
22434     // A strong reference is held in kungFuDeathGrip, so it's safe to call Run()
22435     // directly.
22436 
22437     mState = State::SendingResults;
22438     MOZ_ALWAYS_SUCCEEDS(Run());
22439   }
22440 }
22441 
22442 void
SendBlockedNotification()22443 OpenDatabaseOp::SendBlockedNotification()
22444 {
22445   AssertIsOnOwningThread();
22446   MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose);
22447 
22448   if (!IsActorDestroyed()) {
22449     Unused << SendBlocked(mMetadata->mCommonMetadata.version());
22450   }
22451 }
22452 
22453 nsresult
DispatchToWorkThread()22454 OpenDatabaseOp::DispatchToWorkThread()
22455 {
22456   AssertIsOnOwningThread();
22457   MOZ_ASSERT(mState == State::WaitingForTransactionsToComplete);
22458   MOZ_ASSERT(mVersionChangeTransaction);
22459   MOZ_ASSERT(mVersionChangeTransaction->GetMode() ==
22460                IDBTransaction::VERSION_CHANGE);
22461   MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
22462 
22463   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
22464       IsActorDestroyed() ||
22465       mDatabase->IsInvalidated()) {
22466     IDB_REPORT_INTERNAL_ERR();
22467     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22468   }
22469 
22470   mState = State::DatabaseWorkVersionChange;
22471 
22472   // Intentionally empty.
22473   nsTArray<nsString> objectStoreNames;
22474 
22475   const int64_t loggingSerialNumber =
22476     mVersionChangeTransaction->LoggingSerialNumber();
22477   const nsID& backgroundChildLoggingId =
22478     mVersionChangeTransaction->GetLoggingInfo()->Id();
22479 
22480   if (NS_WARN_IF(!mDatabase->RegisterTransaction(mVersionChangeTransaction))) {
22481     return NS_ERROR_OUT_OF_MEMORY;
22482   }
22483 
22484   if (!gConnectionPool) {
22485     gConnectionPool = new ConnectionPool();
22486   }
22487 
22488   RefPtr<VersionChangeOp> versionChangeOp = new VersionChangeOp(this);
22489 
22490   uint64_t transactionId =
22491     versionChangeOp->StartOnConnectionPool(
22492                                         backgroundChildLoggingId,
22493                                         mVersionChangeTransaction->DatabaseId(),
22494                                         loggingSerialNumber,
22495                                         objectStoreNames,
22496                                         /* aIsWriteTransaction */ true);
22497 
22498   mVersionChangeOp = versionChangeOp;
22499 
22500   mVersionChangeTransaction->NoteActiveRequest();
22501   mVersionChangeTransaction->SetActive(transactionId);
22502 
22503   return NS_OK;
22504 }
22505 
22506 nsresult
SendUpgradeNeeded()22507 OpenDatabaseOp::SendUpgradeNeeded()
22508 {
22509   AssertIsOnOwningThread();
22510   MOZ_ASSERT(mState == State::DatabaseWorkVersionChange);
22511   MOZ_ASSERT(mVersionChangeTransaction);
22512   MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
22513   MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
22514   MOZ_ASSERT_IF(!IsActorDestroyed(), mDatabase);
22515 
22516   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
22517       IsActorDestroyed()) {
22518     IDB_REPORT_INTERNAL_ERR();
22519     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22520   }
22521 
22522   RefPtr<VersionChangeTransaction> transaction;
22523   mVersionChangeTransaction.swap(transaction);
22524 
22525   nsresult rv = EnsureDatabaseActorIsAlive();
22526   if (NS_WARN_IF(NS_FAILED(rv))) {
22527     return rv;
22528   }
22529 
22530   // Transfer ownership to IPDL.
22531   transaction->SetActorAlive();
22532 
22533   if (!mDatabase->SendPBackgroundIDBVersionChangeTransactionConstructor(
22534                                            transaction,
22535                                            mMetadata->mCommonMetadata.version(),
22536                                            mRequestedVersion,
22537                                            mMetadata->mNextObjectStoreId,
22538                                            mMetadata->mNextIndexId)) {
22539     IDB_REPORT_INTERNAL_ERR();
22540     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22541   }
22542 
22543   return NS_OK;
22544 }
22545 
22546 void
SendResults()22547 OpenDatabaseOp::SendResults()
22548 {
22549   AssertIsOnOwningThread();
22550   MOZ_ASSERT(mState == State::SendingResults);
22551   MOZ_ASSERT_IF(NS_SUCCEEDED(mResultCode), mMaybeBlockedDatabases.IsEmpty());
22552   MOZ_ASSERT_IF(NS_SUCCEEDED(mResultCode), !mVersionChangeTransaction);
22553 
22554   mMaybeBlockedDatabases.Clear();
22555 
22556   DatabaseActorInfo* info;
22557   if (gLiveDatabaseHashtable &&
22558       gLiveDatabaseHashtable->Get(mDatabaseId, &info) &&
22559       info->mWaitingFactoryOp) {
22560     MOZ_ASSERT(info->mWaitingFactoryOp == this);
22561     // SendResults() should only be called by Run() and Run() should only be
22562     // called if there's a strong reference to the object that can't be cleared
22563     // here, so it's safe to clear mWaitingFactoryOp without adding additional
22564     // strong reference.
22565     info->mWaitingFactoryOp = nullptr;
22566   }
22567 
22568   if (mVersionChangeTransaction) {
22569     MOZ_ASSERT(NS_FAILED(mResultCode));
22570 
22571     mVersionChangeTransaction->Abort(mResultCode, /* aForce */ true);
22572     mVersionChangeTransaction = nullptr;
22573   }
22574 
22575   if (IsActorDestroyed()) {
22576     if (NS_SUCCEEDED(mResultCode)) {
22577       mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22578     }
22579   } else {
22580     FactoryRequestResponse response;
22581 
22582     if (NS_SUCCEEDED(mResultCode)) {
22583       // If we just successfully completed a versionchange operation then we
22584       // need to update the version in our metadata.
22585       mMetadata->mCommonMetadata.version() = mRequestedVersion;
22586 
22587       nsresult rv = EnsureDatabaseActorIsAlive();
22588       if (NS_SUCCEEDED(rv)) {
22589         // We successfully opened a database so use its actor as the success
22590         // result for this request.
22591         OpenDatabaseRequestResponse openResponse;
22592         openResponse.databaseParent() = mDatabase;
22593         response = openResponse;
22594       } else {
22595         response = ClampResultCode(rv);
22596 #ifdef DEBUG
22597         mResultCode = response.get_nsresult();
22598 #endif
22599       }
22600     } else {
22601 #ifdef DEBUG
22602       // If something failed then our metadata pointer is now bad. No one should
22603       // ever touch it again though so just null it out in DEBUG builds to make
22604       // sure we find such cases.
22605       mMetadata = nullptr;
22606 #endif
22607       response = ClampResultCode(mResultCode);
22608     }
22609 
22610     Unused <<
22611       PBackgroundIDBFactoryRequestParent::Send__delete__(this, response);
22612   }
22613 
22614   if (mDatabase) {
22615     MOZ_ASSERT(!mDirectoryLock);
22616 
22617     if (NS_FAILED(mResultCode)) {
22618       mDatabase->Invalidate();
22619     }
22620 
22621     // Make sure to release the database on this thread.
22622     mDatabase = nullptr;
22623   } else if (mDirectoryLock) {
22624     nsCOMPtr<nsIRunnable> callback =
22625       NewRunnableMethod(this, &OpenDatabaseOp::ConnectionClosedCallback);
22626 
22627     RefPtr<WaitForTransactionsHelper> helper =
22628       new WaitForTransactionsHelper(mDatabaseId, callback);
22629     helper->WaitForTransactions();
22630   }
22631 
22632   FinishSendResults();
22633 }
22634 
22635 void
ConnectionClosedCallback()22636 OpenDatabaseOp::ConnectionClosedCallback()
22637 {
22638   AssertIsOnOwningThread();
22639   MOZ_ASSERT(NS_FAILED(mResultCode));
22640   MOZ_ASSERT(mDirectoryLock);
22641 
22642   mDirectoryLock = nullptr;
22643 }
22644 
22645 void
EnsureDatabaseActor()22646 OpenDatabaseOp::EnsureDatabaseActor()
22647 {
22648   AssertIsOnOwningThread();
22649   MOZ_ASSERT(mState == State::BeginVersionChange ||
22650              mState == State::DatabaseWorkVersionChange ||
22651              mState == State::SendingResults);
22652   MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
22653   MOZ_ASSERT(!mDatabaseFilePath.IsEmpty());
22654   MOZ_ASSERT(!IsActorDestroyed());
22655 
22656   if (mDatabase) {
22657     return;
22658   }
22659 
22660   MOZ_ASSERT(mMetadata->mDatabaseId.IsEmpty());
22661   mMetadata->mDatabaseId = mDatabaseId;
22662 
22663   MOZ_ASSERT(mMetadata->mFilePath.IsEmpty());
22664   mMetadata->mFilePath = mDatabaseFilePath;
22665 
22666   DatabaseActorInfo* info;
22667   if (gLiveDatabaseHashtable->Get(mDatabaseId, &info)) {
22668     AssertMetadataConsistency(info->mMetadata);
22669     mMetadata = info->mMetadata;
22670   }
22671 
22672   auto factory = static_cast<Factory*>(Manager());
22673 
22674   mDatabase = new Database(factory,
22675                            mCommonParams.principalInfo(),
22676                            mOptionalContentParentId,
22677                            mGroup,
22678                            mOrigin,
22679                            mTelemetryId,
22680                            mMetadata,
22681                            mFileManager,
22682                            mDirectoryLock.forget(),
22683                            mFileHandleDisabled,
22684                            mChromeWriteAccessAllowed);
22685 
22686   if (info) {
22687     info->mLiveDatabases.AppendElement(mDatabase);
22688   } else {
22689     info = new DatabaseActorInfo(mMetadata, mDatabase);
22690     gLiveDatabaseHashtable->Put(mDatabaseId, info);
22691   }
22692 
22693   // Balanced in Database::CleanupMetadata().
22694   IncreaseBusyCount();
22695 }
22696 
22697 nsresult
EnsureDatabaseActorIsAlive()22698 OpenDatabaseOp::EnsureDatabaseActorIsAlive()
22699 {
22700   AssertIsOnOwningThread();
22701   MOZ_ASSERT(mState == State::DatabaseWorkVersionChange ||
22702              mState == State::SendingResults);
22703   MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
22704   MOZ_ASSERT(!IsActorDestroyed());
22705 
22706   EnsureDatabaseActor();
22707 
22708   if (mDatabase->IsActorAlive()) {
22709     return NS_OK;
22710   }
22711 
22712   auto factory = static_cast<Factory*>(Manager());
22713 
22714   DatabaseSpec spec;
22715   MetadataToSpec(spec);
22716 
22717   // Transfer ownership to IPDL.
22718   mDatabase->SetActorAlive();
22719 
22720   if (!factory->SendPBackgroundIDBDatabaseConstructor(mDatabase, spec, this)) {
22721     IDB_REPORT_INTERNAL_ERR();
22722     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22723   }
22724 
22725   return NS_OK;
22726 }
22727 
22728 void
MetadataToSpec(DatabaseSpec & aSpec)22729 OpenDatabaseOp::MetadataToSpec(DatabaseSpec& aSpec)
22730 {
22731   AssertIsOnOwningThread();
22732   MOZ_ASSERT(mMetadata);
22733 
22734   aSpec.metadata() = mMetadata->mCommonMetadata;
22735 
22736   for (auto objectStoreIter = mMetadata->mObjectStores.ConstIter();
22737        !objectStoreIter.Done();
22738        objectStoreIter.Next()) {
22739     FullObjectStoreMetadata* metadata = objectStoreIter.UserData();
22740     MOZ_ASSERT(objectStoreIter.Key());
22741     MOZ_ASSERT(metadata);
22742 
22743     // XXX This should really be fallible...
22744     ObjectStoreSpec* objectStoreSpec = aSpec.objectStores().AppendElement();
22745     objectStoreSpec->metadata() = metadata->mCommonMetadata;
22746 
22747     for (auto indexIter = metadata->mIndexes.Iter();
22748          !indexIter.Done();
22749          indexIter.Next()) {
22750       FullIndexMetadata* indexMetadata = indexIter.UserData();
22751       MOZ_ASSERT(indexIter.Key());
22752       MOZ_ASSERT(indexMetadata);
22753 
22754       // XXX This should really be fallible...
22755       IndexMetadata* metadata = objectStoreSpec->indexes().AppendElement();
22756       *metadata = indexMetadata->mCommonMetadata;
22757     }
22758   }
22759 }
22760 
22761 #ifdef DEBUG
22762 
22763 void
AssertMetadataConsistency(const FullDatabaseMetadata * aMetadata)22764 OpenDatabaseOp::AssertMetadataConsistency(const FullDatabaseMetadata* aMetadata)
22765 {
22766   AssertIsOnBackgroundThread();
22767 
22768   const FullDatabaseMetadata* thisDB = mMetadata;
22769   const FullDatabaseMetadata* otherDB = aMetadata;
22770 
22771   MOZ_ASSERT(thisDB);
22772   MOZ_ASSERT(otherDB);
22773   MOZ_ASSERT(thisDB != otherDB);
22774 
22775   MOZ_ASSERT(thisDB->mCommonMetadata.name() == otherDB->mCommonMetadata.name());
22776   MOZ_ASSERT(thisDB->mCommonMetadata.version() ==
22777                otherDB->mCommonMetadata.version());
22778   MOZ_ASSERT(thisDB->mCommonMetadata.persistenceType() ==
22779                otherDB->mCommonMetadata.persistenceType());
22780   MOZ_ASSERT(thisDB->mDatabaseId == otherDB->mDatabaseId);
22781   MOZ_ASSERT(thisDB->mFilePath == otherDB->mFilePath);
22782 
22783   // |thisDB| reflects the latest objectStore and index ids that have committed
22784   // to disk. The in-memory metadata |otherDB| keeps track of objectStores and
22785   // indexes that were created and then removed as well, so the next ids for
22786   // |otherDB| may be higher than for |thisDB|.
22787   MOZ_ASSERT(thisDB->mNextObjectStoreId <= otherDB->mNextObjectStoreId);
22788   MOZ_ASSERT(thisDB->mNextIndexId <= otherDB->mNextIndexId);
22789 
22790   MOZ_ASSERT(thisDB->mObjectStores.Count() == otherDB->mObjectStores.Count());
22791 
22792   for (auto objectStoreIter = thisDB->mObjectStores.ConstIter();
22793        !objectStoreIter.Done();
22794        objectStoreIter.Next()) {
22795     FullObjectStoreMetadata* thisObjectStore = objectStoreIter.UserData();
22796     MOZ_ASSERT(thisObjectStore);
22797     MOZ_ASSERT(!thisObjectStore->mDeleted);
22798 
22799     auto* otherObjectStore =
22800       MetadataNameOrIdMatcher<FullObjectStoreMetadata>::Match(
22801         otherDB->mObjectStores, thisObjectStore->mCommonMetadata.id());
22802     MOZ_ASSERT(otherObjectStore);
22803 
22804     MOZ_ASSERT(thisObjectStore != otherObjectStore);
22805 
22806     MOZ_ASSERT(thisObjectStore->mCommonMetadata.id() ==
22807                  otherObjectStore->mCommonMetadata.id());
22808     MOZ_ASSERT(thisObjectStore->mCommonMetadata.name() ==
22809                  otherObjectStore->mCommonMetadata.name());
22810     MOZ_ASSERT(thisObjectStore->mCommonMetadata.autoIncrement() ==
22811                  otherObjectStore->mCommonMetadata.autoIncrement());
22812     MOZ_ASSERT(thisObjectStore->mCommonMetadata.keyPath() ==
22813                  otherObjectStore->mCommonMetadata.keyPath());
22814     // mNextAutoIncrementId and mCommittedAutoIncrementId may be modified
22815     // concurrently with this OpenOp, so it is not possible to assert equality
22816     // here. It's also possible that we've written the new ids to disk but not
22817     // yet updated the in-memory count.
22818     MOZ_ASSERT(thisObjectStore->mNextAutoIncrementId <=
22819                  otherObjectStore->mNextAutoIncrementId);
22820     MOZ_ASSERT(thisObjectStore->mCommittedAutoIncrementId <=
22821                  otherObjectStore->mCommittedAutoIncrementId ||
22822                thisObjectStore->mCommittedAutoIncrementId ==
22823                  otherObjectStore->mNextAutoIncrementId);
22824     MOZ_ASSERT(!otherObjectStore->mDeleted);
22825 
22826     MOZ_ASSERT(thisObjectStore->mIndexes.Count() ==
22827                  otherObjectStore->mIndexes.Count());
22828 
22829     for (auto indexIter = thisObjectStore->mIndexes.Iter();
22830          !indexIter.Done();
22831          indexIter.Next()) {
22832       FullIndexMetadata* thisIndex = indexIter.UserData();
22833       MOZ_ASSERT(thisIndex);
22834       MOZ_ASSERT(!thisIndex->mDeleted);
22835 
22836       auto* otherIndex =
22837         MetadataNameOrIdMatcher<FullIndexMetadata>::
22838           Match(otherObjectStore->mIndexes, thisIndex->mCommonMetadata.id());
22839       MOZ_ASSERT(otherIndex);
22840 
22841       MOZ_ASSERT(thisIndex != otherIndex);
22842 
22843       MOZ_ASSERT(thisIndex->mCommonMetadata.id() ==
22844                    otherIndex->mCommonMetadata.id());
22845       MOZ_ASSERT(thisIndex->mCommonMetadata.name() ==
22846                    otherIndex->mCommonMetadata.name());
22847       MOZ_ASSERT(thisIndex->mCommonMetadata.keyPath() ==
22848                    otherIndex->mCommonMetadata.keyPath());
22849       MOZ_ASSERT(thisIndex->mCommonMetadata.unique() ==
22850                    otherIndex->mCommonMetadata.unique());
22851       MOZ_ASSERT(thisIndex->mCommonMetadata.multiEntry() ==
22852                    otherIndex->mCommonMetadata.multiEntry());
22853       MOZ_ASSERT(!otherIndex->mDeleted);
22854     }
22855   }
22856 }
22857 
22858 #endif // DEBUG
22859 
22860 nsresult
22861 OpenDatabaseOp::
DoDatabaseWork(DatabaseConnection * aConnection)22862 VersionChangeOp::DoDatabaseWork(DatabaseConnection* aConnection)
22863 {
22864   MOZ_ASSERT(aConnection);
22865   aConnection->AssertIsOnConnectionThread();
22866   MOZ_ASSERT(mOpenDatabaseOp);
22867   MOZ_ASSERT(mOpenDatabaseOp->mState == State::DatabaseWorkVersionChange);
22868 
22869   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
22870       !OperationMayProceed()) {
22871     IDB_REPORT_INTERNAL_ERR();
22872     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
22873   }
22874 
22875   PROFILER_LABEL("IndexedDB",
22876                  "OpenDatabaseOp::VersionChangeOp::DoDatabaseWork",
22877                  js::ProfileEntry::Category::STORAGE);
22878 
22879   IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
22880                  "Beginning database work",
22881                "IndexedDB %s: P T[%lld]: DB Start",
22882                IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
22883                mLoggingSerialNumber);
22884 
22885   Transaction()->SetActiveOnConnectionThread();
22886 
22887   nsresult rv = aConnection->BeginWriteTransaction();
22888   if (NS_WARN_IF(NS_FAILED(rv))) {
22889     return rv;
22890   }
22891 
22892   DatabaseConnection::CachedStatement updateStmt;
22893   rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
22894     "UPDATE database "
22895       "SET version = :version;"),
22896     &updateStmt);
22897   if (NS_WARN_IF(NS_FAILED(rv))) {
22898     return rv;
22899   }
22900 
22901   rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("version"),
22902                                    int64_t(mRequestedVersion));
22903   if (NS_WARN_IF(NS_FAILED(rv))) {
22904     return rv;
22905   }
22906 
22907   rv = updateStmt->Execute();
22908   if (NS_WARN_IF(NS_FAILED(rv))) {
22909     return rv;
22910   }
22911 
22912   return NS_OK;
22913 }
22914 
22915 nsresult
22916 OpenDatabaseOp::
SendSuccessResult()22917 VersionChangeOp::SendSuccessResult()
22918 {
22919   AssertIsOnOwningThread();
22920   MOZ_ASSERT(mOpenDatabaseOp);
22921   MOZ_ASSERT(mOpenDatabaseOp->mState == State::DatabaseWorkVersionChange);
22922   MOZ_ASSERT(mOpenDatabaseOp->mVersionChangeOp == this);
22923 
22924   nsresult rv = mOpenDatabaseOp->SendUpgradeNeeded();
22925   if (NS_WARN_IF(NS_FAILED(rv))) {
22926     return rv;
22927   }
22928 
22929   return NS_OK;
22930 }
22931 
22932 bool
22933 OpenDatabaseOp::
SendFailureResult(nsresult aResultCode)22934 VersionChangeOp::SendFailureResult(nsresult aResultCode)
22935 {
22936   AssertIsOnOwningThread();
22937   MOZ_ASSERT(mOpenDatabaseOp);
22938   MOZ_ASSERT(mOpenDatabaseOp->mState == State::DatabaseWorkVersionChange);
22939   MOZ_ASSERT(mOpenDatabaseOp->mVersionChangeOp == this);
22940 
22941   mOpenDatabaseOp->SetFailureCode(aResultCode);
22942   mOpenDatabaseOp->mState = State::SendingResults;
22943 
22944   MOZ_ALWAYS_SUCCEEDS(mOpenDatabaseOp->Run());
22945 
22946   return false;
22947 }
22948 
22949 void
22950 OpenDatabaseOp::
Cleanup()22951 VersionChangeOp::Cleanup()
22952 {
22953   AssertIsOnOwningThread();
22954   MOZ_ASSERT(mOpenDatabaseOp);
22955   MOZ_ASSERT(mOpenDatabaseOp->mVersionChangeOp == this);
22956 
22957   mOpenDatabaseOp->mVersionChangeOp = nullptr;
22958   mOpenDatabaseOp = nullptr;
22959 
22960 #ifdef DEBUG
22961   // A bit hacky but the VersionChangeOp is not generated in response to a
22962   // child request like most other database operations. Do this to make our
22963   // assertions happy.
22964   NoteActorDestroyed();
22965 #endif
22966 
22967   TransactionDatabaseOperationBase::Cleanup();
22968 }
22969 
22970 void
LoadPreviousVersion(nsIFile * aDatabaseFile)22971 DeleteDatabaseOp::LoadPreviousVersion(nsIFile* aDatabaseFile)
22972 {
22973   AssertIsOnIOThread();
22974   MOZ_ASSERT(aDatabaseFile);
22975   MOZ_ASSERT(mState == State::DatabaseWorkOpen);
22976   MOZ_ASSERT(!mPreviousVersion);
22977 
22978   PROFILER_LABEL("IndexedDB",
22979                  "DeleteDatabaseOp::LoadPreviousVersion",
22980                  js::ProfileEntry::Category::STORAGE);
22981 
22982   nsresult rv;
22983 
22984   nsCOMPtr<mozIStorageService> ss =
22985     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
22986   if (NS_WARN_IF(NS_FAILED(rv))) {
22987     return;
22988   }
22989 
22990   nsCOMPtr<mozIStorageConnection> connection;
22991   rv = OpenDatabaseAndHandleBusy(ss, aDatabaseFile, getter_AddRefs(connection));
22992   if (NS_WARN_IF(NS_FAILED(rv))) {
22993     return;
22994   }
22995 
22996 #ifdef DEBUG
22997   {
22998     nsCOMPtr<mozIStorageStatement> stmt;
22999     MOZ_ALWAYS_SUCCEEDS(
23000       connection->CreateStatement(NS_LITERAL_CSTRING(
23001         "SELECT name "
23002           "FROM database"
23003         ), getter_AddRefs(stmt)));
23004 
23005     bool hasResult;
23006     MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
23007 
23008     nsString databaseName;
23009     MOZ_ALWAYS_SUCCEEDS(stmt->GetString(0, databaseName));
23010 
23011     MOZ_ASSERT(mCommonParams.metadata().name() == databaseName);
23012   }
23013 #endif
23014 
23015   nsCOMPtr<mozIStorageStatement> stmt;
23016   rv = connection->CreateStatement(NS_LITERAL_CSTRING(
23017     "SELECT version "
23018     "FROM database"
23019   ), getter_AddRefs(stmt));
23020   if (NS_WARN_IF(NS_FAILED(rv))) {
23021     return;
23022   }
23023 
23024   bool hasResult;
23025   rv = stmt->ExecuteStep(&hasResult);
23026   if (NS_WARN_IF(NS_FAILED(rv))) {
23027     return;
23028   }
23029 
23030   if (NS_WARN_IF(!hasResult)) {
23031     return;
23032   }
23033 
23034   int64_t version;
23035   rv = stmt->GetInt64(0, &version);
23036   if (NS_WARN_IF(NS_FAILED(rv))) {
23037     return;
23038   }
23039 
23040   mPreviousVersion = uint64_t(version);
23041 }
23042 
23043 nsresult
DatabaseOpen()23044 DeleteDatabaseOp::DatabaseOpen()
23045 {
23046   AssertIsOnOwningThread();
23047   MOZ_ASSERT(mState == State::DatabaseOpenPending);
23048 
23049   // Swap this to the stack now to ensure that we release it on this thread.
23050   RefPtr<ContentParent> contentParent;
23051   mContentParent.swap(contentParent);
23052 
23053   nsresult rv = SendToIOThread();
23054   if (NS_WARN_IF(NS_FAILED(rv))) {
23055     return rv;
23056   }
23057 
23058   return NS_OK;
23059 }
23060 
23061 nsresult
DoDatabaseWork()23062 DeleteDatabaseOp::DoDatabaseWork()
23063 {
23064   AssertIsOnIOThread();
23065   MOZ_ASSERT(mState == State::DatabaseWorkOpen);
23066 
23067   PROFILER_LABEL("IndexedDB",
23068                  "DeleteDatabaseOp::DoDatabaseWork",
23069                  js::ProfileEntry::Category::STORAGE);
23070 
23071   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
23072       !OperationMayProceed()) {
23073     IDB_REPORT_INTERNAL_ERR();
23074     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23075   }
23076 
23077   const nsString& databaseName = mCommonParams.metadata().name();
23078   PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
23079 
23080   QuotaManager* quotaManager = QuotaManager::Get();
23081   MOZ_ASSERT(quotaManager);
23082 
23083   nsCOMPtr<nsIFile> directory;
23084   nsresult rv = quotaManager->GetDirectoryForOrigin(persistenceType,
23085                                                     mOrigin,
23086                                                     getter_AddRefs(directory));
23087   if (NS_WARN_IF(NS_FAILED(rv))) {
23088     return rv;
23089   }
23090 
23091   rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
23092   if (NS_WARN_IF(NS_FAILED(rv))) {
23093     return rv;
23094   }
23095 
23096   rv = directory->GetPath(mDatabaseDirectoryPath);
23097   if (NS_WARN_IF(NS_FAILED(rv))) {
23098     return rv;
23099   }
23100 
23101   nsAutoString filename;
23102   GetDatabaseFilename(databaseName, filename);
23103 
23104   mDatabaseFilenameBase = filename;
23105 
23106   nsCOMPtr<nsIFile> dbFile;
23107   rv = directory->Clone(getter_AddRefs(dbFile));
23108   if (NS_WARN_IF(NS_FAILED(rv))) {
23109     return rv;
23110   }
23111 
23112   rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
23113   if (NS_WARN_IF(NS_FAILED(rv))) {
23114     return rv;
23115   }
23116 
23117 #ifdef DEBUG
23118   nsString databaseFilePath;
23119   rv = dbFile->GetPath(databaseFilePath);
23120   if (NS_WARN_IF(NS_FAILED(rv))) {
23121     return rv;
23122   }
23123 
23124   MOZ_ASSERT(databaseFilePath == mDatabaseFilePath);
23125 #endif
23126 
23127   bool exists;
23128   rv = dbFile->Exists(&exists);
23129   if (NS_WARN_IF(NS_FAILED(rv))) {
23130     return rv;
23131   }
23132 
23133   if (exists) {
23134     // Parts of this function may fail but that shouldn't prevent us from
23135     // deleting the file eventually.
23136     LoadPreviousVersion(dbFile);
23137 
23138     mState = State::BeginVersionChange;
23139   } else {
23140     mState = State::SendingResults;
23141   }
23142 
23143   rv = mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL);
23144   if (NS_WARN_IF(NS_FAILED(rv))) {
23145     return rv;
23146   }
23147 
23148   return NS_OK;
23149 }
23150 
23151 nsresult
BeginVersionChange()23152 DeleteDatabaseOp::BeginVersionChange()
23153 {
23154   AssertIsOnOwningThread();
23155   MOZ_ASSERT(mState == State::BeginVersionChange);
23156   MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
23157 
23158   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
23159       IsActorDestroyed()) {
23160     IDB_REPORT_INTERNAL_ERR();
23161     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23162   }
23163 
23164   DatabaseActorInfo* info;
23165   if (gLiveDatabaseHashtable->Get(mDatabaseId, &info)) {
23166     MOZ_ASSERT(!info->mWaitingFactoryOp);
23167 
23168     NullableVersion newVersion = null_t();
23169 
23170     nsresult rv =
23171       SendVersionChangeMessages(info, nullptr, mPreviousVersion, newVersion);
23172     if (NS_WARN_IF(NS_FAILED(rv))) {
23173       return rv;
23174     }
23175 
23176     if (!mMaybeBlockedDatabases.IsEmpty()) {
23177       info->mWaitingFactoryOp = this;
23178 
23179       mState = State::WaitingForOtherDatabasesToClose;
23180       return NS_OK;
23181     }
23182   }
23183 
23184   // No other databases need to be notified, just make sure that all
23185   // transactions are complete.
23186   WaitForTransactions();
23187   return NS_OK;
23188 }
23189 
23190 nsresult
DispatchToWorkThread()23191 DeleteDatabaseOp::DispatchToWorkThread()
23192 {
23193   AssertIsOnOwningThread();
23194   MOZ_ASSERT(mState == State::WaitingForTransactionsToComplete);
23195   MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
23196 
23197   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
23198       IsActorDestroyed()) {
23199     IDB_REPORT_INTERNAL_ERR();
23200     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23201   }
23202 
23203   mState = State::DatabaseWorkVersionChange;
23204 
23205   RefPtr<VersionChangeOp> versionChangeOp = new VersionChangeOp(this);
23206 
23207   QuotaManager* quotaManager = QuotaManager::Get();
23208   MOZ_ASSERT(quotaManager);
23209 
23210   nsresult rv =
23211     quotaManager->IOThread()->Dispatch(versionChangeOp.forget(),
23212                                        NS_DISPATCH_NORMAL);
23213   if (NS_WARN_IF(NS_FAILED(rv))) {
23214     IDB_REPORT_INTERNAL_ERR();
23215     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23216   }
23217 
23218   return NS_OK;
23219 }
23220 
23221 void
NoteDatabaseClosed(Database * aDatabase)23222 DeleteDatabaseOp::NoteDatabaseClosed(Database* aDatabase)
23223 {
23224   AssertIsOnOwningThread();
23225   MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose);
23226   MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty());
23227 
23228   bool actorDestroyed = IsActorDestroyed();
23229 
23230   nsresult rv;
23231   if (actorDestroyed) {
23232     IDB_REPORT_INTERNAL_ERR();
23233     rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23234   } else {
23235     rv = NS_OK;
23236   }
23237 
23238   // We are being called with an assuption that mWaitingFactoryOp holds a strong
23239   // reference to us.
23240   RefPtr<OpenDatabaseOp> kungFuDeathGrip;
23241 
23242   if (mMaybeBlockedDatabases.RemoveElement(aDatabase) &&
23243       mMaybeBlockedDatabases.IsEmpty()) {
23244     if (actorDestroyed) {
23245       DatabaseActorInfo* info;
23246       MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info));
23247       MOZ_ASSERT(info->mWaitingFactoryOp == this);
23248       kungFuDeathGrip =
23249         static_cast<OpenDatabaseOp*>(info->mWaitingFactoryOp.get());
23250       info->mWaitingFactoryOp = nullptr;
23251     } else {
23252       WaitForTransactions();
23253     }
23254   }
23255 
23256   if (NS_WARN_IF(NS_FAILED(rv))) {
23257     if (NS_SUCCEEDED(mResultCode)) {
23258       mResultCode = rv;
23259     }
23260 
23261     // A strong reference is held in kungFuDeathGrip, so it's safe to call Run()
23262     // directly.
23263 
23264     mState = State::SendingResults;
23265     MOZ_ALWAYS_SUCCEEDS(Run());
23266   }
23267 }
23268 
23269 void
SendBlockedNotification()23270 DeleteDatabaseOp::SendBlockedNotification()
23271 {
23272   AssertIsOnOwningThread();
23273   MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose);
23274 
23275   if (!IsActorDestroyed()) {
23276     Unused << SendBlocked(mPreviousVersion);
23277   }
23278 }
23279 
23280 void
SendResults()23281 DeleteDatabaseOp::SendResults()
23282 {
23283   AssertIsOnOwningThread();
23284   MOZ_ASSERT(mState == State::SendingResults);
23285 
23286   if (!IsActorDestroyed()) {
23287     FactoryRequestResponse response;
23288 
23289     if (NS_SUCCEEDED(mResultCode)) {
23290       response = DeleteDatabaseRequestResponse(mPreviousVersion);
23291     } else {
23292       response = ClampResultCode(mResultCode);
23293     }
23294 
23295     Unused <<
23296       PBackgroundIDBFactoryRequestParent::Send__delete__(this, response);
23297   }
23298 
23299   mDirectoryLock = nullptr;
23300 
23301   FinishSendResults();
23302 }
23303 
23304 nsresult
23305 DeleteDatabaseOp::
DeleteFile(nsIFile * aDirectory,const nsAString & aFilename,QuotaManager * aQuotaManager)23306 VersionChangeOp::DeleteFile(nsIFile* aDirectory,
23307                             const nsAString& aFilename,
23308                             QuotaManager* aQuotaManager)
23309 {
23310   AssertIsOnIOThread();
23311   MOZ_ASSERT(aDirectory);
23312   MOZ_ASSERT(!aFilename.IsEmpty());
23313   MOZ_ASSERT_IF(aQuotaManager, mDeleteDatabaseOp->mEnforcingQuota);
23314 
23315   MOZ_ASSERT(mDeleteDatabaseOp->mState == State::DatabaseWorkVersionChange);
23316 
23317   PROFILER_LABEL("IndexedDB",
23318                  "DeleteDatabaseOp::VersionChangeOp::DeleteFile",
23319                  js::ProfileEntry::Category::STORAGE);
23320 
23321   nsCOMPtr<nsIFile> file;
23322   nsresult rv = aDirectory->Clone(getter_AddRefs(file));
23323   if (NS_WARN_IF(NS_FAILED(rv))) {
23324     return rv;
23325   }
23326 
23327   rv = file->Append(aFilename);
23328   if (NS_WARN_IF(NS_FAILED(rv))) {
23329     return rv;
23330   }
23331 
23332   int64_t fileSize;
23333 
23334   if (aQuotaManager) {
23335     rv = file->GetFileSize(&fileSize);
23336     if (rv == NS_ERROR_FILE_NOT_FOUND ||
23337         rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
23338       return NS_OK;
23339     }
23340 
23341     if (NS_WARN_IF(NS_FAILED(rv))) {
23342       return rv;
23343     }
23344 
23345     MOZ_ASSERT(fileSize >= 0);
23346   }
23347 
23348   rv = file->Remove(false);
23349   if (rv == NS_ERROR_FILE_NOT_FOUND ||
23350       rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
23351     return NS_OK;
23352   }
23353 
23354   if (NS_WARN_IF(NS_FAILED(rv))) {
23355     return rv;
23356   }
23357 
23358   if (aQuotaManager && fileSize > 0) {
23359     const PersistenceType& persistenceType =
23360       mDeleteDatabaseOp->mCommonParams.metadata().persistenceType();
23361 
23362     aQuotaManager->DecreaseUsageForOrigin(persistenceType,
23363                                           mDeleteDatabaseOp->mGroup,
23364                                           mDeleteDatabaseOp->mOrigin,
23365                                           fileSize);
23366   }
23367 
23368   return NS_OK;
23369 }
23370 
23371 nsresult
23372 DeleteDatabaseOp::
RunOnIOThread()23373 VersionChangeOp::RunOnIOThread()
23374 {
23375   AssertIsOnIOThread();
23376   MOZ_ASSERT(mDeleteDatabaseOp->mState == State::DatabaseWorkVersionChange);
23377 
23378   PROFILER_LABEL("IndexedDB",
23379                  "DeleteDatabaseOp::VersionChangeOp::RunOnIOThread",
23380                  js::ProfileEntry::Category::STORAGE);
23381 
23382   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
23383       !OperationMayProceed()) {
23384     IDB_REPORT_INTERNAL_ERR();
23385     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23386   }
23387 
23388   const PersistenceType& persistenceType =
23389     mDeleteDatabaseOp->mCommonParams.metadata().persistenceType();
23390 
23391   QuotaManager* quotaManager =
23392     mDeleteDatabaseOp->mEnforcingQuota ?
23393     QuotaManager::Get() :
23394     nullptr;
23395 
23396   MOZ_ASSERT_IF(mDeleteDatabaseOp->mEnforcingQuota, quotaManager);
23397 
23398   nsCOMPtr<nsIFile> directory =
23399     GetFileForPath(mDeleteDatabaseOp->mDatabaseDirectoryPath);
23400   if (NS_WARN_IF(!directory)) {
23401     IDB_REPORT_INTERNAL_ERR();
23402     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23403   }
23404 
23405   // The database file counts towards quota.
23406   nsAutoString filename =
23407     mDeleteDatabaseOp->mDatabaseFilenameBase + NS_LITERAL_STRING(".sqlite");
23408 
23409   nsresult rv = DeleteFile(directory, filename, quotaManager);
23410   if (NS_WARN_IF(NS_FAILED(rv))) {
23411     return rv;
23412   }
23413 
23414   // .sqlite-journal files don't count towards quota.
23415   const NS_ConvertASCIItoUTF16 journalSuffix(
23416     kSQLiteJournalSuffix,
23417     LiteralStringLength(kSQLiteJournalSuffix));
23418 
23419   filename = mDeleteDatabaseOp->mDatabaseFilenameBase + journalSuffix;
23420 
23421   rv = DeleteFile(directory, filename, /* doesn't count */ nullptr);
23422   if (NS_WARN_IF(NS_FAILED(rv))) {
23423     return rv;
23424   }
23425 
23426   // .sqlite-shm files don't count towards quota.
23427   const NS_ConvertASCIItoUTF16 shmSuffix(kSQLiteSHMSuffix,
23428                                          LiteralStringLength(kSQLiteSHMSuffix));
23429 
23430   filename = mDeleteDatabaseOp->mDatabaseFilenameBase + shmSuffix;
23431 
23432   rv = DeleteFile(directory, filename, /* doesn't count */ nullptr);
23433   if (NS_WARN_IF(NS_FAILED(rv))) {
23434     return rv;
23435   }
23436 
23437   // .sqlite-wal files do count towards quota.
23438   const NS_ConvertASCIItoUTF16 walSuffix(kSQLiteWALSuffix,
23439                                          LiteralStringLength(kSQLiteWALSuffix));
23440 
23441   filename = mDeleteDatabaseOp->mDatabaseFilenameBase + walSuffix;
23442 
23443   rv = DeleteFile(directory, filename, quotaManager);
23444   if (NS_WARN_IF(NS_FAILED(rv))) {
23445     return rv;
23446   }
23447 
23448   nsCOMPtr<nsIFile> fmDirectory;
23449   rv = directory->Clone(getter_AddRefs(fmDirectory));
23450   if (NS_WARN_IF(NS_FAILED(rv))) {
23451     return rv;
23452   }
23453 
23454   // The files directory counts towards quota.
23455   const NS_ConvertASCIItoUTF16 filesSuffix(
23456     kFileManagerDirectoryNameSuffix,
23457     LiteralStringLength(kFileManagerDirectoryNameSuffix));
23458 
23459   rv = fmDirectory->Append(mDeleteDatabaseOp->mDatabaseFilenameBase +
23460                            filesSuffix);
23461   if (NS_WARN_IF(NS_FAILED(rv))) {
23462     return rv;
23463   }
23464 
23465   bool exists;
23466   rv = fmDirectory->Exists(&exists);
23467   if (NS_WARN_IF(NS_FAILED(rv))) {
23468     return rv;
23469   }
23470 
23471   if (exists) {
23472     bool isDirectory;
23473     rv = fmDirectory->IsDirectory(&isDirectory);
23474     if (NS_WARN_IF(NS_FAILED(rv))) {
23475       return rv;
23476     }
23477 
23478     if (NS_WARN_IF(!isDirectory)) {
23479       IDB_REPORT_INTERNAL_ERR();
23480       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23481     }
23482 
23483     uint64_t usage = 0;
23484 
23485     if (mDeleteDatabaseOp->mEnforcingQuota) {
23486       rv = FileManager::GetUsage(fmDirectory, &usage);
23487       if (NS_WARN_IF(NS_FAILED(rv))) {
23488         return rv;
23489       }
23490     }
23491 
23492     rv = fmDirectory->Remove(true);
23493     if (NS_WARN_IF(NS_FAILED(rv))) {
23494       // We may have deleted some files, check if we can and update quota
23495       // information before returning the error.
23496       if (mDeleteDatabaseOp->mEnforcingQuota) {
23497         uint64_t newUsage;
23498         if (NS_SUCCEEDED(FileManager::GetUsage(fmDirectory, &newUsage))) {
23499           MOZ_ASSERT(newUsage <= usage);
23500           usage = usage - newUsage;
23501         }
23502       }
23503     }
23504 
23505     if (mDeleteDatabaseOp->mEnforcingQuota && usage) {
23506       quotaManager->DecreaseUsageForOrigin(persistenceType,
23507                                            mDeleteDatabaseOp->mGroup,
23508                                            mDeleteDatabaseOp->mOrigin,
23509                                            usage);
23510     }
23511 
23512     if (NS_FAILED(rv)) {
23513       return rv;
23514     }
23515   }
23516 
23517   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
23518   MOZ_ASSERT(mgr);
23519 
23520   const nsString& databaseName =
23521     mDeleteDatabaseOp->mCommonParams.metadata().name();
23522 
23523   mgr->InvalidateFileManager(persistenceType,
23524                              mDeleteDatabaseOp->mOrigin,
23525                              databaseName);
23526 
23527   rv = mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL);
23528   if (NS_WARN_IF(NS_FAILED(rv))) {
23529     return rv;
23530   }
23531 
23532   return NS_OK;
23533 }
23534 
23535 void
23536 DeleteDatabaseOp::
RunOnOwningThread()23537 VersionChangeOp::RunOnOwningThread()
23538 {
23539   AssertIsOnOwningThread();
23540   MOZ_ASSERT(mDeleteDatabaseOp->mState == State::DatabaseWorkVersionChange);
23541 
23542   RefPtr<DeleteDatabaseOp> deleteOp;
23543   mDeleteDatabaseOp.swap(deleteOp);
23544 
23545   if (deleteOp->IsActorDestroyed()) {
23546     IDB_REPORT_INTERNAL_ERR();
23547     deleteOp->SetFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
23548   } else {
23549     DatabaseActorInfo* info;
23550     if (gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId, &info) &&
23551         info->mWaitingFactoryOp) {
23552       MOZ_ASSERT(info->mWaitingFactoryOp == deleteOp);
23553       info->mWaitingFactoryOp = nullptr;
23554     }
23555 
23556     if (NS_FAILED(mResultCode)) {
23557       if (NS_SUCCEEDED(deleteOp->ResultCode())) {
23558         deleteOp->SetFailureCode(mResultCode);
23559       }
23560     } else {
23561       // Inform all the other databases that they are now invalidated. That
23562       // should remove the previous metadata from our table.
23563       if (info) {
23564         MOZ_ASSERT(!info->mLiveDatabases.IsEmpty());
23565 
23566         FallibleTArray<Database*> liveDatabases;
23567         if (NS_WARN_IF(!liveDatabases.AppendElements(info->mLiveDatabases,
23568                                                      fallible))) {
23569           deleteOp->SetFailureCode(NS_ERROR_OUT_OF_MEMORY);
23570         } else {
23571 #ifdef DEBUG
23572           // The code below should result in the deletion of |info|. Set to null
23573           // here to make sure we find invalid uses later.
23574           info = nullptr;
23575 #endif
23576           for (uint32_t count = liveDatabases.Length(), index = 0;
23577                index < count;
23578                index++) {
23579             RefPtr<Database> database = liveDatabases[index];
23580             database->Invalidate();
23581           }
23582 
23583           MOZ_ASSERT(!gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId));
23584         }
23585       }
23586     }
23587   }
23588 
23589   // We hold a strong ref to the deleteOp, so it's safe to call Run() directly.
23590 
23591   deleteOp->mState = State::SendingResults;
23592   MOZ_ALWAYS_SUCCEEDS(deleteOp->Run());
23593 
23594 #ifdef DEBUG
23595   // A bit hacky but the DeleteDatabaseOp::VersionChangeOp is not really a
23596   // normal database operation that is tied to an actor. Do this to make our
23597   // assertions happy.
23598   NoteActorDestroyed();
23599 #endif
23600 }
23601 
23602 nsresult
23603 DeleteDatabaseOp::
Run()23604 VersionChangeOp::Run()
23605 {
23606   nsresult rv;
23607 
23608   if (IsOnIOThread()) {
23609     rv = RunOnIOThread();
23610   } else {
23611     RunOnOwningThread();
23612     rv = NS_OK;
23613   }
23614 
23615   if (NS_WARN_IF(NS_FAILED(rv))) {
23616     if (NS_SUCCEEDED(mResultCode)) {
23617       mResultCode = rv;
23618     }
23619 
23620     MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
23621   }
23622 
23623   return NS_OK;
23624 }
23625 
TransactionDatabaseOperationBase(TransactionBase * aTransaction)23626 TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
23627                                                   TransactionBase* aTransaction)
23628   : DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
23629                           aTransaction->GetLoggingInfo()->NextRequestSN())
23630   , mTransaction(aTransaction)
23631   , mTransactionLoggingSerialNumber(aTransaction->LoggingSerialNumber())
23632   , mInternalState(InternalState::Initial)
23633   , mTransactionIsAborted(aTransaction->IsAborted())
23634 {
23635   MOZ_ASSERT(aTransaction);
23636   MOZ_ASSERT(LoggingSerialNumber());
23637 }
23638 
TransactionDatabaseOperationBase(TransactionBase * aTransaction,uint64_t aLoggingSerialNumber)23639 TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
23640                                                   TransactionBase* aTransaction,
23641                                                   uint64_t aLoggingSerialNumber)
23642   : DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
23643                           aLoggingSerialNumber)
23644   , mTransaction(aTransaction)
23645   , mTransactionLoggingSerialNumber(aTransaction->LoggingSerialNumber())
23646   , mInternalState(InternalState::Initial)
23647   , mTransactionIsAborted(aTransaction->IsAborted())
23648 {
23649   MOZ_ASSERT(aTransaction);
23650 }
23651 
~TransactionDatabaseOperationBase()23652 TransactionDatabaseOperationBase::~TransactionDatabaseOperationBase()
23653 {
23654   MOZ_ASSERT(mInternalState == InternalState::Completed);
23655   MOZ_ASSERT(!mTransaction,
23656              "TransactionDatabaseOperationBase::Cleanup() was not called by a "
23657              "subclass!");
23658 }
23659 
23660 #ifdef DEBUG
23661 
23662 void
AssertIsOnConnectionThread() const23663 TransactionDatabaseOperationBase::AssertIsOnConnectionThread() const
23664 {
23665   MOZ_ASSERT(mTransaction);
23666   mTransaction->AssertIsOnConnectionThread();
23667 }
23668 
23669 #endif // DEBUG
23670 
23671 uint64_t
StartOnConnectionPool(const nsID & aBackgroundChildLoggingId,const nsACString & aDatabaseId,int64_t aLoggingSerialNumber,const nsTArray<nsString> & aObjectStoreNames,bool aIsWriteTransaction)23672 TransactionDatabaseOperationBase::StartOnConnectionPool(
23673                                     const nsID& aBackgroundChildLoggingId,
23674                                     const nsACString& aDatabaseId,
23675                                     int64_t aLoggingSerialNumber,
23676                                     const nsTArray<nsString>& aObjectStoreNames,
23677                                     bool aIsWriteTransaction)
23678 {
23679   AssertIsOnOwningThread();
23680   MOZ_ASSERT(mInternalState == InternalState::Initial);
23681 
23682   // Must set mInternalState before dispatching otherwise we will race with the
23683   // connection thread.
23684   mInternalState = InternalState::DatabaseWork;
23685 
23686   return gConnectionPool->Start(aBackgroundChildLoggingId,
23687                                 aDatabaseId,
23688                                 aLoggingSerialNumber,
23689                                 aObjectStoreNames,
23690                                 aIsWriteTransaction,
23691                                 this);
23692 }
23693 
23694 void
DispatchToConnectionPool()23695 TransactionDatabaseOperationBase::DispatchToConnectionPool()
23696 {
23697   AssertIsOnOwningThread();
23698   MOZ_ASSERT(mInternalState == InternalState::Initial);
23699 
23700   Unused << this->Run();
23701 }
23702 
23703 void
RunOnConnectionThread()23704 TransactionDatabaseOperationBase::RunOnConnectionThread()
23705 {
23706   MOZ_ASSERT(!IsOnBackgroundThread());
23707   MOZ_ASSERT(mInternalState == InternalState::DatabaseWork);
23708   MOZ_ASSERT(mTransaction);
23709   MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
23710 
23711   PROFILER_LABEL("IndexedDB",
23712                  "TransactionDatabaseOperationBase::RunOnConnectionThread",
23713                  js::ProfileEntry::Category::STORAGE);
23714 
23715   // There are several cases where we don't actually have to to any work here.
23716 
23717   if (mTransactionIsAborted || mTransaction->IsInvalidatedOnAnyThread()) {
23718     // This transaction is already set to be aborted or invalidated.
23719     mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
23720   } else if (!OperationMayProceed()) {
23721     // The operation was canceled in some way, likely because the child process
23722     // has crashed.
23723     IDB_REPORT_INTERNAL_ERR();
23724     mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23725   } else {
23726     Database* database = mTransaction->GetDatabase();
23727     MOZ_ASSERT(database);
23728 
23729     // Here we're actually going to perform the database operation.
23730     nsresult rv = database->EnsureConnection();
23731     if (NS_WARN_IF(NS_FAILED(rv))) {
23732       mResultCode = rv;
23733     } else {
23734       DatabaseConnection* connection = database->GetConnection();
23735       MOZ_ASSERT(connection);
23736       MOZ_ASSERT(connection->GetStorageConnection());
23737 
23738       AutoSetProgressHandler autoProgress;
23739       if (mLoggingSerialNumber) {
23740         rv = autoProgress.Register(connection->GetStorageConnection(), this);
23741         if (NS_WARN_IF(NS_FAILED(rv))) {
23742           mResultCode = rv;
23743         }
23744       }
23745 
23746       if (NS_SUCCEEDED(rv)) {
23747         if (mLoggingSerialNumber) {
23748           IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld] Request[%llu]: "
23749                          "Beginning database work",
23750                        "IndexedDB %s: P T[%lld] R[%llu]: DB Start",
23751                        IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
23752                        mTransactionLoggingSerialNumber,
23753                        mLoggingSerialNumber);
23754         }
23755 
23756         rv = DoDatabaseWork(connection);
23757 
23758         if (mLoggingSerialNumber) {
23759           IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld] Request[%llu]: "
23760                          "Finished database work",
23761                        "IndexedDB %s: P T[%lld] R[%llu]: DB End",
23762                        IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
23763                        mTransactionLoggingSerialNumber,
23764                        mLoggingSerialNumber);
23765         }
23766 
23767         if (NS_FAILED(rv)) {
23768           mResultCode = rv;
23769         }
23770       }
23771     }
23772   }
23773 
23774   // Must set mInternalState before dispatching otherwise we will race with the
23775   // owning thread.
23776   if (HasPreprocessInfo()) {
23777     mInternalState = InternalState::SendingPreprocess;
23778   } else {
23779     mInternalState = InternalState::SendingResults;
23780   }
23781 
23782   MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
23783 }
23784 
23785 bool
HasPreprocessInfo()23786 TransactionDatabaseOperationBase::HasPreprocessInfo()
23787 {
23788   return false;
23789 }
23790 
23791 nsresult
SendPreprocessInfo()23792 TransactionDatabaseOperationBase::SendPreprocessInfo()
23793 {
23794   return NS_OK;
23795 }
23796 
23797 void
NoteContinueReceived()23798 TransactionDatabaseOperationBase::NoteContinueReceived()
23799 {
23800   AssertIsOnOwningThread();
23801   MOZ_ASSERT(mInternalState == InternalState::WaitingForContinue);
23802 
23803   mInternalState = InternalState::SendingResults;
23804 
23805   // This TransactionDatabaseOperationBase can only be held alive by the IPDL.
23806   // Run() can end up with clearing that last reference. So we need to add
23807   // a self reference here.
23808   RefPtr<TransactionDatabaseOperationBase> kungFuDeathGrip = this;
23809 
23810   Unused << this->Run();
23811 }
23812 
23813 void
SendToConnectionPool()23814 TransactionDatabaseOperationBase::SendToConnectionPool()
23815 {
23816   AssertIsOnOwningThread();
23817   MOZ_ASSERT(mInternalState == InternalState::Initial);
23818 
23819   // Must set mInternalState before dispatching otherwise we will race with the
23820   // connection thread.
23821   mInternalState = InternalState::DatabaseWork;
23822 
23823   gConnectionPool->Dispatch(mTransaction->TransactionId(), this);
23824 
23825   mTransaction->NoteActiveRequest();
23826 }
23827 
23828 void
SendPreprocess()23829 TransactionDatabaseOperationBase::SendPreprocess()
23830 {
23831   AssertIsOnOwningThread();
23832   MOZ_ASSERT(mInternalState == InternalState::SendingPreprocess);
23833 
23834   SendPreprocessInfoOrResults(/* aSendPreprocessInfo */ true);
23835 }
23836 
23837 void
SendResults()23838 TransactionDatabaseOperationBase::SendResults()
23839 {
23840   AssertIsOnOwningThread();
23841   MOZ_ASSERT(mInternalState == InternalState::SendingResults);
23842 
23843   SendPreprocessInfoOrResults(/* aSendPreprocessInfo */ false);
23844 }
23845 
23846 void
SendPreprocessInfoOrResults(bool aSendPreprocessInfo)23847 TransactionDatabaseOperationBase::SendPreprocessInfoOrResults(
23848                                                        bool aSendPreprocessInfo)
23849 {
23850   AssertIsOnOwningThread();
23851   MOZ_ASSERT(mInternalState == InternalState::SendingPreprocess ||
23852              mInternalState == InternalState::SendingResults);
23853   MOZ_ASSERT(mTransaction);
23854 
23855   if (NS_WARN_IF(IsActorDestroyed())) {
23856     // Don't send any notifications if the actor was destroyed already.
23857     if (NS_SUCCEEDED(mResultCode)) {
23858       IDB_REPORT_INTERNAL_ERR();
23859       mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
23860     }
23861   } else {
23862     if (mTransaction->IsInvalidated() || mTransaction->IsAborted()) {
23863       // Aborted transactions always see their requests fail with ABORT_ERR,
23864       // even if the request succeeded or failed with another error.
23865       mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
23866     } else if (NS_SUCCEEDED(mResultCode)) {
23867       if (aSendPreprocessInfo) {
23868         // This should not release the IPDL reference.
23869         mResultCode = SendPreprocessInfo();
23870       } else {
23871         // This may release the IPDL reference.
23872         mResultCode = SendSuccessResult();
23873       }
23874     }
23875 
23876     if (NS_FAILED(mResultCode)) {
23877       // This should definitely release the IPDL reference.
23878       if (!SendFailureResult(mResultCode)) {
23879         // Abort the transaction.
23880         mTransaction->Abort(mResultCode, /* aForce */ false);
23881       }
23882     }
23883   }
23884 
23885   if (aSendPreprocessInfo && NS_SUCCEEDED(mResultCode)) {
23886     mInternalState = InternalState::WaitingForContinue;
23887   } else {
23888     if (mLoggingSerialNumber) {
23889       mTransaction->NoteFinishedRequest();
23890     }
23891 
23892     Cleanup();
23893 
23894     mInternalState = InternalState::Completed;
23895   }
23896 }
23897 
23898 bool
Init(TransactionBase * aTransaction)23899 TransactionDatabaseOperationBase::Init(TransactionBase* aTransaction)
23900 {
23901   AssertIsOnBackgroundThread();
23902   MOZ_ASSERT(mInternalState == InternalState::Initial);
23903   MOZ_ASSERT(aTransaction);
23904 
23905   return true;
23906 }
23907 
23908 void
Cleanup()23909 TransactionDatabaseOperationBase::Cleanup()
23910 {
23911   AssertIsOnOwningThread();
23912   MOZ_ASSERT(mInternalState == InternalState::SendingResults);
23913   MOZ_ASSERT(mTransaction);
23914 
23915   mTransaction = nullptr;
23916 }
23917 
23918 NS_IMETHODIMP
Run()23919 TransactionDatabaseOperationBase::Run()
23920 {
23921   switch (mInternalState) {
23922     case InternalState::Initial:
23923       SendToConnectionPool();
23924       return NS_OK;
23925 
23926     case InternalState::DatabaseWork:
23927       RunOnConnectionThread();
23928       return NS_OK;
23929 
23930     case InternalState::SendingPreprocess:
23931       SendPreprocess();
23932       return NS_OK;
23933 
23934     case InternalState::SendingResults:
23935       SendResults();
23936       return NS_OK;
23937 
23938     default:
23939       MOZ_CRASH("Bad state!");
23940   }
23941 }
23942 
23943 TransactionBase::
CommitOp(TransactionBase * aTransaction,nsresult aResultCode)23944 CommitOp::CommitOp(TransactionBase* aTransaction, nsresult aResultCode)
23945   : DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
23946                           aTransaction->GetLoggingInfo()->NextRequestSN())
23947   , mTransaction(aTransaction)
23948   , mResultCode(aResultCode)
23949 {
23950   MOZ_ASSERT(aTransaction);
23951   MOZ_ASSERT(LoggingSerialNumber());
23952 }
23953 
23954 nsresult
23955 TransactionBase::
WriteAutoIncrementCounts()23956 CommitOp::WriteAutoIncrementCounts()
23957 {
23958   MOZ_ASSERT(mTransaction);
23959   mTransaction->AssertIsOnConnectionThread();
23960   MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::READ_WRITE ||
23961              mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH ||
23962              mTransaction->GetMode() == IDBTransaction::CLEANUPff ||
23963              mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
23964 
23965   const nsTArray<RefPtr<FullObjectStoreMetadata>>& metadataArray =
23966     mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray;
23967 
23968   if (!metadataArray.IsEmpty()) {
23969     NS_NAMED_LITERAL_CSTRING(osid, "osid");
23970     NS_NAMED_LITERAL_CSTRING(ai, "ai");
23971 
23972     Database* database = mTransaction->GetDatabase();
23973     MOZ_ASSERT(database);
23974 
23975     DatabaseConnection* connection = database->GetConnection();
23976     MOZ_ASSERT(connection);
23977 
23978     DatabaseConnection::CachedStatement stmt;
23979     nsresult rv;
23980 
23981     for (uint32_t count = metadataArray.Length(), index = 0;
23982          index < count;
23983          index++) {
23984       const RefPtr<FullObjectStoreMetadata>& metadata = metadataArray[index];
23985       MOZ_ASSERT(!metadata->mDeleted);
23986       MOZ_ASSERT(metadata->mNextAutoIncrementId > 1);
23987 
23988       if (stmt) {
23989         MOZ_ALWAYS_SUCCEEDS(stmt->Reset());
23990       } else {
23991         rv = connection->GetCachedStatement(
23992           NS_LITERAL_CSTRING("UPDATE object_store "
23993                              "SET auto_increment = :") + ai +
23994           NS_LITERAL_CSTRING(" WHERE id = :") + osid +
23995           NS_LITERAL_CSTRING(";"),
23996           &stmt);
23997         if (NS_WARN_IF(NS_FAILED(rv))) {
23998           return rv;
23999         }
24000       }
24001 
24002       rv = stmt->BindInt64ByName(osid, metadata->mCommonMetadata.id());
24003       if (NS_WARN_IF(NS_FAILED(rv))) {
24004         return rv;
24005       }
24006 
24007       rv = stmt->BindInt64ByName(ai, metadata->mNextAutoIncrementId);
24008       if (NS_WARN_IF(NS_FAILED(rv))) {
24009         return rv;
24010       }
24011 
24012       rv = stmt->Execute();
24013       if (NS_WARN_IF(NS_FAILED(rv))) {
24014         return rv;
24015       }
24016     }
24017   }
24018 
24019   return NS_OK;
24020 }
24021 
24022 void
24023 TransactionBase::
CommitOrRollbackAutoIncrementCounts()24024 CommitOp::CommitOrRollbackAutoIncrementCounts()
24025 {
24026   MOZ_ASSERT(mTransaction);
24027   mTransaction->AssertIsOnConnectionThread();
24028   MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::READ_WRITE ||
24029              mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH ||
24030              mTransaction->GetMode() == IDBTransaction::CLEANUPff ||
24031              mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
24032 
24033   nsTArray<RefPtr<FullObjectStoreMetadata>>& metadataArray =
24034     mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray;
24035 
24036   if (!metadataArray.IsEmpty()) {
24037     bool committed = NS_SUCCEEDED(mResultCode);
24038 
24039     for (uint32_t count = metadataArray.Length(), index = 0;
24040          index < count;
24041          index++) {
24042       RefPtr<FullObjectStoreMetadata>& metadata = metadataArray[index];
24043 
24044       if (committed) {
24045         metadata->mCommittedAutoIncrementId = metadata->mNextAutoIncrementId;
24046       } else {
24047         metadata->mNextAutoIncrementId = metadata->mCommittedAutoIncrementId;
24048       }
24049     }
24050   }
24051 }
24052 
24053 #ifdef DEBUG
24054 
24055 void
24056 TransactionBase::
AssertForeignKeyConsistency(DatabaseConnection * aConnection)24057 CommitOp::AssertForeignKeyConsistency(DatabaseConnection* aConnection)
24058 {
24059   MOZ_ASSERT(aConnection);
24060   MOZ_ASSERT(mTransaction);
24061   mTransaction->AssertIsOnConnectionThread();
24062   MOZ_ASSERT(mTransaction->GetMode() != IDBTransaction::READ_ONLY);
24063 
24064   DatabaseConnection::CachedStatement pragmaStmt;
24065   MOZ_ALWAYS_SUCCEEDS(
24066     aConnection->GetCachedStatement(NS_LITERAL_CSTRING("PRAGMA foreign_keys;"),
24067                                     &pragmaStmt));
24068 
24069   bool hasResult;
24070   MOZ_ALWAYS_SUCCEEDS(pragmaStmt->ExecuteStep(&hasResult));
24071 
24072   MOZ_ASSERT(hasResult);
24073 
24074   int32_t foreignKeysEnabled;
24075   MOZ_ALWAYS_SUCCEEDS(pragmaStmt->GetInt32(0, &foreignKeysEnabled));
24076 
24077   MOZ_ASSERT(foreignKeysEnabled, "Database doesn't have foreign keys enabled!");
24078 
24079   DatabaseConnection::CachedStatement checkStmt;
24080   MOZ_ALWAYS_SUCCEEDS(
24081     aConnection->GetCachedStatement(
24082       NS_LITERAL_CSTRING("PRAGMA foreign_key_check;"),
24083       &checkStmt));
24084 
24085   MOZ_ALWAYS_SUCCEEDS(checkStmt->ExecuteStep(&hasResult));
24086 
24087   MOZ_ASSERT(!hasResult, "Database has inconsisistent foreign keys!");
24088 }
24089 
24090 #endif // DEBUG
24091 
NS_IMPL_ISUPPORTS_INHERITED0(TransactionBase::CommitOp,DatabaseOperationBase)24092 NS_IMPL_ISUPPORTS_INHERITED0(TransactionBase::CommitOp, DatabaseOperationBase)
24093 
24094 NS_IMETHODIMP
24095 TransactionBase::
24096 CommitOp::Run()
24097 {
24098   MOZ_ASSERT(mTransaction);
24099   mTransaction->AssertIsOnConnectionThread();
24100 
24101   PROFILER_LABEL("IndexedDB",
24102                  "TransactionBase::CommitOp::Run",
24103                  js::ProfileEntry::Category::STORAGE);
24104 
24105   IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld] Request[%llu]: "
24106                  "Beginning database work",
24107                "IndexedDB %s: P T[%lld] R[%llu]: DB Start",
24108                IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
24109                mTransaction->LoggingSerialNumber(),
24110                mLoggingSerialNumber);
24111 
24112   if (mTransaction->GetMode() != IDBTransaction::READ_ONLY &&
24113       mTransaction->mHasBeenActiveOnConnectionThread) {
24114     Database* database = mTransaction->GetDatabase();
24115     MOZ_ASSERT(database);
24116 
24117     if (DatabaseConnection* connection = database->GetConnection()) {
24118       // May be null if the VersionChangeOp was canceled.
24119       DatabaseConnection::UpdateRefcountFunction* fileRefcountFunction =
24120         connection->GetUpdateRefcountFunction();
24121 
24122       if (NS_SUCCEEDED(mResultCode)) {
24123         if (fileRefcountFunction) {
24124           mResultCode = fileRefcountFunction->WillCommit();
24125           NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode),
24126                                "WillCommit() failed!");
24127         }
24128 
24129         if (NS_SUCCEEDED(mResultCode)) {
24130           mResultCode = WriteAutoIncrementCounts();
24131           NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode),
24132                                "WriteAutoIncrementCounts() failed!");
24133 
24134           if (NS_SUCCEEDED(mResultCode)) {
24135             AssertForeignKeyConsistency(connection);
24136 
24137             mResultCode = connection->CommitWriteTransaction();
24138             NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode), "Commit failed!");
24139 
24140             if (NS_SUCCEEDED(mResultCode) &&
24141                 mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH) {
24142               mResultCode = connection->Checkpoint();
24143             }
24144 
24145             if (NS_SUCCEEDED(mResultCode) && fileRefcountFunction) {
24146               fileRefcountFunction->DidCommit();
24147             }
24148           }
24149         }
24150       }
24151 
24152       if (NS_FAILED(mResultCode)) {
24153         if (fileRefcountFunction) {
24154           fileRefcountFunction->DidAbort();
24155         }
24156 
24157         connection->RollbackWriteTransaction();
24158       }
24159 
24160       CommitOrRollbackAutoIncrementCounts();
24161 
24162       connection->FinishWriteTransaction();
24163 
24164       if (mTransaction->GetMode() == IDBTransaction::CLEANUPff) {
24165         connection->DoIdleProcessing(/* aNeedsCheckpoint */ true);
24166 
24167         connection->EnableQuotaChecks();
24168       }
24169     }
24170   }
24171 
24172   IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld] Request[%llu]: "
24173                  "Finished database work",
24174                "IndexedDB %s: P T[%lld] R[%llu]: DB End",
24175                IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
24176                mTransaction->LoggingSerialNumber(),
24177                mLoggingSerialNumber);
24178 
24179   IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
24180                  "Finished database work",
24181                "IndexedDB %s: P T[%lld]: DB End",
24182                IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
24183                mLoggingSerialNumber);
24184 
24185   return NS_OK;
24186 }
24187 
24188 void
24189 TransactionBase::
TransactionFinishedBeforeUnblock()24190 CommitOp::TransactionFinishedBeforeUnblock()
24191 {
24192   AssertIsOnBackgroundThread();
24193   MOZ_ASSERT(mTransaction);
24194 
24195   PROFILER_LABEL("IndexedDB",
24196                  "CommitOp::TransactionFinishedBeforeUnblock",
24197                  js::ProfileEntry::Category::STORAGE);
24198 
24199   if (!IsActorDestroyed()) {
24200     mTransaction->UpdateMetadata(mResultCode);
24201   }
24202 }
24203 
24204 void
24205 TransactionBase::
TransactionFinishedAfterUnblock()24206 CommitOp::TransactionFinishedAfterUnblock()
24207 {
24208   AssertIsOnBackgroundThread();
24209   MOZ_ASSERT(mTransaction);
24210 
24211   IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
24212                  "Finished with result 0x%x",
24213                "IndexedDB %s: P T[%lld]: Transaction finished (0x%x)",
24214                IDB_LOG_ID_STRING(mTransaction->GetLoggingInfo()->Id()),
24215                mTransaction->LoggingSerialNumber(),
24216                mResultCode);
24217 
24218   mTransaction->SendCompleteNotification(ClampResultCode(mResultCode));
24219 
24220   Database* database = mTransaction->GetDatabase();
24221   MOZ_ASSERT(database);
24222 
24223   database->UnregisterTransaction(mTransaction);
24224 
24225   mTransaction = nullptr;
24226 
24227 #ifdef DEBUG
24228   // A bit hacky but the CommitOp is not really a normal database operation
24229   // that is tied to an actor. Do this to make our assertions happy.
24230   NoteActorDestroyed();
24231 #endif
24232 }
24233 
DatabaseOp(Database * aDatabase)24234 DatabaseOp::DatabaseOp(Database* aDatabase)
24235   : DatabaseOperationBase(aDatabase->GetLoggingInfo()->Id(),
24236                           aDatabase->GetLoggingInfo()->NextRequestSN())
24237   , mDatabase(aDatabase)
24238   , mState(State::Initial)
24239 {
24240   AssertIsOnBackgroundThread();
24241   MOZ_ASSERT(aDatabase);
24242 }
24243 
24244 nsresult
SendToIOThread()24245 DatabaseOp::SendToIOThread()
24246 {
24247   AssertIsOnOwningThread();
24248   MOZ_ASSERT(mState == State::Initial);
24249 
24250   if (!OperationMayProceed()) {
24251     IDB_REPORT_INTERNAL_ERR();
24252     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24253   }
24254 
24255   QuotaManager* quotaManager = QuotaManager::Get();
24256   if (NS_WARN_IF(!quotaManager)) {
24257     IDB_REPORT_INTERNAL_ERR();
24258     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24259   }
24260 
24261   // Must set this before dispatching otherwise we will race with the IO thread.
24262   mState = State::DatabaseWork;
24263 
24264   nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
24265   if (NS_WARN_IF(NS_FAILED(rv))) {
24266     IDB_REPORT_INTERNAL_ERR();
24267     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24268   }
24269 
24270   return NS_OK;
24271 }
24272 
24273 NS_IMETHODIMP
Run()24274 DatabaseOp::Run()
24275 {
24276   nsresult rv;
24277 
24278   switch (mState) {
24279     case State::Initial:
24280       rv = SendToIOThread();
24281       break;
24282 
24283     case State::DatabaseWork:
24284       rv = DoDatabaseWork();
24285       break;
24286 
24287     case State::SendingResults:
24288       SendResults();
24289       return NS_OK;
24290 
24291     default:
24292       MOZ_CRASH("Bad state!");
24293   }
24294 
24295   if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
24296     if (NS_SUCCEEDED(mResultCode)) {
24297       mResultCode = rv;
24298     }
24299 
24300     // Must set mState before dispatching otherwise we will race with the owning
24301     // thread.
24302     mState = State::SendingResults;
24303 
24304     MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
24305   }
24306 
24307   return NS_OK;
24308 }
24309 
24310 void
ActorDestroy(ActorDestroyReason aWhy)24311 DatabaseOp::ActorDestroy(ActorDestroyReason aWhy)
24312 {
24313   AssertIsOnBackgroundThread();
24314 
24315   NoteActorDestroyed();
24316 }
24317 
CreateFileOp(Database * aDatabase,const DatabaseRequestParams & aParams)24318 CreateFileOp::CreateFileOp(Database* aDatabase,
24319                            const DatabaseRequestParams& aParams)
24320   : DatabaseOp(aDatabase)
24321   , mParams(aParams.get_CreateFileParams())
24322 {
24323   MOZ_ASSERT(aParams.type() == DatabaseRequestParams::TCreateFileParams);
24324 }
24325 
24326 nsresult
CreateMutableFile(MutableFile ** aMutableFile)24327 CreateFileOp::CreateMutableFile(MutableFile** aMutableFile)
24328 {
24329   nsCOMPtr<nsIFile> file = GetFileForFileInfo(mFileInfo);
24330   if (NS_WARN_IF(!file)) {
24331     IDB_REPORT_INTERNAL_ERR();
24332     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24333   }
24334 
24335   RefPtr<MutableFile> mutableFile =
24336     MutableFile::Create(file, mDatabase, mFileInfo);
24337   if (NS_WARN_IF(!mutableFile)) {
24338     IDB_REPORT_INTERNAL_ERR();
24339     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24340   }
24341 
24342   // Transfer ownership to IPDL.
24343   mutableFile->SetActorAlive();
24344 
24345   if (!mDatabase->SendPBackgroundMutableFileConstructor(mutableFile,
24346                                                         mParams.name(),
24347                                                         mParams.type())) {
24348     IDB_REPORT_INTERNAL_ERR();
24349     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24350   }
24351 
24352   mutableFile.forget(aMutableFile);
24353   return NS_OK;
24354 }
24355 
24356 nsresult
DoDatabaseWork()24357 CreateFileOp::DoDatabaseWork()
24358 {
24359   AssertIsOnIOThread();
24360   MOZ_ASSERT(mState == State::DatabaseWork);
24361 
24362   PROFILER_LABEL("IndexedDB",
24363                  "CreateFileOp::DoDatabaseWork",
24364                  js::ProfileEntry::Category::STORAGE);
24365 
24366   if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
24367     NS_WARNING("Refusing to create file because disk space is low!");
24368     return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
24369   }
24370 
24371   if (NS_WARN_IF(QuotaManager::IsShuttingDown()) || !OperationMayProceed()) {
24372     IDB_REPORT_INTERNAL_ERR();
24373     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24374   }
24375 
24376   FileManager* fileManager = mDatabase->GetFileManager();
24377 
24378   mFileInfo = fileManager->GetNewFileInfo();
24379   if (NS_WARN_IF(!mFileInfo)) {
24380     IDB_REPORT_INTERNAL_ERR();
24381     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24382   }
24383 
24384   const int64_t fileId = mFileInfo->Id();
24385 
24386   nsCOMPtr<nsIFile> journalDirectory = fileManager->EnsureJournalDirectory();
24387   if (NS_WARN_IF(!journalDirectory)) {
24388     IDB_REPORT_INTERNAL_ERR();
24389     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24390   }
24391 
24392   nsCOMPtr<nsIFile> journalFile =
24393     fileManager->GetFileForId(journalDirectory, fileId);
24394   if (NS_WARN_IF(!journalFile)) {
24395     IDB_REPORT_INTERNAL_ERR();
24396     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24397   }
24398 
24399   nsresult rv = journalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
24400   if (NS_WARN_IF(NS_FAILED(rv))) {
24401     return rv;
24402   }
24403 
24404   nsCOMPtr<nsIFile> fileDirectory = fileManager->GetDirectory();
24405   if (NS_WARN_IF(!fileDirectory)) {
24406     IDB_REPORT_INTERNAL_ERR();
24407     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24408   }
24409 
24410   nsCOMPtr<nsIFile> file = fileManager->GetFileForId(fileDirectory, fileId);
24411   if (NS_WARN_IF(!file)) {
24412     IDB_REPORT_INTERNAL_ERR();
24413     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24414   }
24415 
24416   rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
24417   if (NS_WARN_IF(NS_FAILED(rv))) {
24418     return rv;
24419   }
24420 
24421   // Must set mState before dispatching otherwise we will race with the owning
24422   // thread.
24423   mState = State::SendingResults;
24424 
24425   rv = mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL);
24426   if (NS_WARN_IF(NS_FAILED(rv))) {
24427     return rv;
24428   }
24429 
24430   return NS_OK;
24431 }
24432 
24433 void
SendResults()24434 CreateFileOp::SendResults()
24435 {
24436   AssertIsOnOwningThread();
24437   MOZ_ASSERT(mState == State::SendingResults);
24438 
24439   if (!IsActorDestroyed() && !mDatabase->IsInvalidated()) {
24440     DatabaseRequestResponse response;
24441 
24442     if (NS_SUCCEEDED(mResultCode)) {
24443       RefPtr<MutableFile> mutableFile;
24444       nsresult rv = CreateMutableFile(getter_AddRefs(mutableFile));
24445       if (NS_SUCCEEDED(rv)) {
24446         // We successfully created a mutable file so use its actor as the
24447         // success result for this request.
24448         CreateFileRequestResponse createResponse;
24449         createResponse.mutableFileParent() = mutableFile;
24450         response = createResponse;
24451       } else {
24452         response = ClampResultCode(rv);
24453 #ifdef DEBUG
24454         mResultCode = response.get_nsresult();
24455 #endif
24456       }
24457     } else {
24458       response = ClampResultCode(mResultCode);
24459     }
24460 
24461     Unused <<
24462       PBackgroundIDBDatabaseRequestParent::Send__delete__(this, response);
24463   }
24464 
24465   mState = State::Completed;
24466 }
24467 
24468 nsresult
SendSuccessResult()24469 VersionChangeTransactionOp::SendSuccessResult()
24470 {
24471   AssertIsOnOwningThread();
24472 
24473   // Nothing to send here, the API assumes that this request always succeeds.
24474   return NS_OK;
24475 }
24476 
24477 bool
SendFailureResult(nsresult aResultCode)24478 VersionChangeTransactionOp::SendFailureResult(nsresult aResultCode)
24479 {
24480   AssertIsOnOwningThread();
24481 
24482   // The only option here is to cause the transaction to abort.
24483   return false;
24484 }
24485 
24486 void
Cleanup()24487 VersionChangeTransactionOp::Cleanup()
24488 {
24489   AssertIsOnOwningThread();
24490 
24491 #ifdef DEBUG
24492   // A bit hacky but the VersionChangeTransactionOp is not generated in response
24493   // to a child request like most other database operations. Do this to make our
24494   // assertions happy.
24495   NoteActorDestroyed();
24496 #endif
24497 
24498   TransactionDatabaseOperationBase::Cleanup();
24499 }
24500 
24501 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)24502 CreateObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection)
24503 {
24504   MOZ_ASSERT(aConnection);
24505   aConnection->AssertIsOnConnectionThread();
24506 
24507   PROFILER_LABEL("IndexedDB",
24508                  "CreateObjectStoreOp::DoDatabaseWork",
24509                  js::ProfileEntry::Category::STORAGE);
24510 
24511   if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
24512     return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
24513   }
24514 
24515 #ifdef DEBUG
24516   {
24517     // Make sure that we're not creating an object store with the same name as
24518     // another that already exists. This should be impossible because we should
24519     // have thrown an error long before now...
24520     DatabaseConnection::CachedStatement stmt;
24521     MOZ_ALWAYS_SUCCEEDS(
24522       aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24523         "SELECT name "
24524           "FROM object_store "
24525           "WHERE name = :name;"),
24526         &stmt));
24527 
24528     MOZ_ALWAYS_SUCCEEDS(
24529       stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mMetadata.name()));
24530 
24531     bool hasResult;
24532     MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
24533     MOZ_ASSERT(!hasResult);
24534   }
24535 #endif
24536 
24537   DatabaseConnection::AutoSavepoint autoSave;
24538   nsresult rv = autoSave.Start(Transaction());
24539   if (NS_WARN_IF(NS_FAILED(rv))) {
24540     return rv;
24541   }
24542 
24543   DatabaseConnection::CachedStatement stmt;
24544   rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24545     "INSERT INTO object_store (id, auto_increment, name, key_path) "
24546       "VALUES (:id, :auto_increment, :name, :key_path);"),
24547     &stmt);
24548   if (NS_WARN_IF(NS_FAILED(rv))) {
24549     return rv;
24550   }
24551 
24552   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mMetadata.id());
24553   if (NS_WARN_IF(NS_FAILED(rv))) {
24554     return rv;
24555   }
24556 
24557   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"),
24558                              mMetadata.autoIncrement() ? 1 : 0);
24559   if (NS_WARN_IF(NS_FAILED(rv))) {
24560     return rv;
24561   }
24562 
24563   rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mMetadata.name());
24564   if (NS_WARN_IF(NS_FAILED(rv))) {
24565     return rv;
24566   }
24567 
24568   NS_NAMED_LITERAL_CSTRING(keyPath, "key_path");
24569 
24570   if (mMetadata.keyPath().IsValid()) {
24571     nsAutoString keyPathSerialization;
24572     mMetadata.keyPath().SerializeToString(keyPathSerialization);
24573 
24574     rv = stmt->BindStringByName(keyPath, keyPathSerialization);
24575   } else {
24576     rv = stmt->BindNullByName(keyPath);
24577   }
24578 
24579   if (NS_WARN_IF(NS_FAILED(rv))) {
24580     return rv;
24581   }
24582 
24583   rv = stmt->Execute();
24584   if (NS_WARN_IF(NS_FAILED(rv))) {
24585     return rv;
24586   }
24587 
24588 #ifdef DEBUG
24589   {
24590     int64_t id;
24591     MOZ_ALWAYS_SUCCEEDS(
24592       aConnection->GetStorageConnection()->GetLastInsertRowID(&id));
24593     MOZ_ASSERT(mMetadata.id() == id);
24594   }
24595 #endif
24596 
24597   rv = autoSave.Commit();
24598   if (NS_WARN_IF(NS_FAILED(rv))) {
24599     return rv;
24600   }
24601 
24602   return NS_OK;
24603 }
24604 
24605 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)24606 DeleteObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection)
24607 {
24608   MOZ_ASSERT(aConnection);
24609   aConnection->AssertIsOnConnectionThread();
24610 
24611   PROFILER_LABEL("IndexedDB",
24612                  "DeleteObjectStoreOp::DoDatabaseWork",
24613                  js::ProfileEntry::Category::STORAGE);
24614 
24615   NS_NAMED_LITERAL_CSTRING(objectStoreIdString, "object_store_id");
24616 
24617 #ifdef DEBUG
24618   {
24619     // Make sure |mIsLastObjectStore| is telling the truth.
24620     DatabaseConnection::CachedStatement stmt;
24621     MOZ_ALWAYS_SUCCEEDS(
24622       aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24623         "SELECT id "
24624           "FROM object_store;"),
24625         &stmt));
24626 
24627     bool foundThisObjectStore = false;
24628     bool foundOtherObjectStore = false;
24629 
24630     while (true) {
24631       bool hasResult;
24632       MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
24633 
24634       if (!hasResult) {
24635         break;
24636       }
24637 
24638       int64_t id;
24639       MOZ_ALWAYS_SUCCEEDS(stmt->GetInt64(0, &id));
24640 
24641       if (id == mMetadata->mCommonMetadata.id()) {
24642         foundThisObjectStore = true;
24643       } else {
24644         foundOtherObjectStore = true;
24645       }
24646     }
24647 
24648     MOZ_ASSERT_IF(mIsLastObjectStore,
24649                   foundThisObjectStore && !foundOtherObjectStore);
24650     MOZ_ASSERT_IF(!mIsLastObjectStore,
24651                   foundThisObjectStore && foundOtherObjectStore);
24652   }
24653 #endif
24654 
24655   DatabaseConnection::AutoSavepoint autoSave;
24656   nsresult rv = autoSave.Start(Transaction());
24657   if (NS_WARN_IF(NS_FAILED(rv))) {
24658     return rv;
24659   }
24660 
24661   if (mIsLastObjectStore) {
24662     // We can just delete everything if this is the last object store.
24663     DatabaseConnection::CachedStatement stmt;
24664     rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24665       "DELETE FROM index_data;"),
24666       &stmt);
24667     if (NS_WARN_IF(NS_FAILED(rv))) {
24668       return rv;
24669     }
24670 
24671     rv = stmt->Execute();
24672     if (NS_WARN_IF(NS_FAILED(rv))) {
24673       return rv;
24674     }
24675 
24676     rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24677       "DELETE FROM unique_index_data;"),
24678       &stmt);
24679     if (NS_WARN_IF(NS_FAILED(rv))) {
24680       return rv;
24681     }
24682 
24683     rv = stmt->Execute();
24684     if (NS_WARN_IF(NS_FAILED(rv))) {
24685       return rv;
24686     }
24687 
24688     rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24689       "DELETE FROM object_data;"),
24690       &stmt);
24691     if (NS_WARN_IF(NS_FAILED(rv))) {
24692       return rv;
24693     }
24694 
24695     rv = stmt->Execute();
24696     if (NS_WARN_IF(NS_FAILED(rv))) {
24697       return rv;
24698     }
24699 
24700     rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24701       "DELETE FROM object_store_index;"),
24702       &stmt);
24703     if (NS_WARN_IF(NS_FAILED(rv))) {
24704       return rv;
24705     }
24706 
24707     rv = stmt->Execute();
24708     if (NS_WARN_IF(NS_FAILED(rv))) {
24709       return rv;
24710     }
24711 
24712     rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24713       "DELETE FROM object_store;"),
24714       &stmt);
24715     if (NS_WARN_IF(NS_FAILED(rv))) {
24716       return rv;
24717     }
24718 
24719     rv = stmt->Execute();
24720     if (NS_WARN_IF(NS_FAILED(rv))) {
24721       return rv;
24722     }
24723   } else {
24724     bool hasIndexes;
24725     rv = ObjectStoreHasIndexes(aConnection,
24726                                mMetadata->mCommonMetadata.id(),
24727                                &hasIndexes);
24728     if (NS_WARN_IF(NS_FAILED(rv))) {
24729       return rv;
24730     }
24731 
24732     if (hasIndexes) {
24733       rv = DeleteObjectStoreDataTableRowsWithIndexes(
24734         aConnection,
24735         mMetadata->mCommonMetadata.id(),
24736         void_t());
24737       if (NS_WARN_IF(NS_FAILED(rv))) {
24738         return rv;
24739       }
24740 
24741       // Now clean up the object store index table.
24742       DatabaseConnection::CachedStatement stmt;
24743       rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24744         "DELETE FROM object_store_index "
24745           "WHERE object_store_id = :object_store_id;"),
24746         &stmt);
24747       if (NS_WARN_IF(NS_FAILED(rv))) {
24748         return rv;
24749       }
24750 
24751       rv = stmt->BindInt64ByName(objectStoreIdString,
24752                                  mMetadata->mCommonMetadata.id());
24753       if (NS_WARN_IF(NS_FAILED(rv))) {
24754         return rv;
24755       }
24756 
24757       rv = stmt->Execute();
24758       if (NS_WARN_IF(NS_FAILED(rv))) {
24759         return rv;
24760       }
24761     } else {
24762       // We only have to worry about object data if this object store has no
24763       // indexes.
24764       DatabaseConnection::CachedStatement stmt;
24765       rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24766         "DELETE FROM object_data "
24767           "WHERE object_store_id = :object_store_id;"),
24768         &stmt);
24769       if (NS_WARN_IF(NS_FAILED(rv))) {
24770         return rv;
24771       }
24772 
24773       rv = stmt->BindInt64ByName(objectStoreIdString,
24774                                  mMetadata->mCommonMetadata.id());
24775       if (NS_WARN_IF(NS_FAILED(rv))) {
24776         return rv;
24777       }
24778 
24779       rv = stmt->Execute();
24780       if (NS_WARN_IF(NS_FAILED(rv))) {
24781         return rv;
24782       }
24783     }
24784 
24785     DatabaseConnection::CachedStatement stmt;
24786     rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24787       "DELETE FROM object_store "
24788         "WHERE id = :object_store_id;"),
24789       &stmt);
24790     if (NS_WARN_IF(NS_FAILED(rv))) {
24791       return rv;
24792     }
24793 
24794     rv = stmt->BindInt64ByName(objectStoreIdString,
24795                                mMetadata->mCommonMetadata.id());
24796     if (NS_WARN_IF(NS_FAILED(rv))) {
24797       return rv;
24798     }
24799 
24800     rv = stmt->Execute();
24801     if (NS_WARN_IF(NS_FAILED(rv))) {
24802       return rv;
24803     }
24804 
24805 #ifdef DEBUG
24806     {
24807       int32_t deletedRowCount;
24808       MOZ_ALWAYS_SUCCEEDS(
24809         aConnection->GetStorageConnection()->
24810           GetAffectedRows(&deletedRowCount));
24811       MOZ_ASSERT(deletedRowCount == 1);
24812     }
24813 #endif
24814   }
24815 
24816   rv = autoSave.Commit();
24817   if (NS_WARN_IF(NS_FAILED(rv))) {
24818     return rv;
24819   }
24820 
24821   if (mMetadata->mCommonMetadata.autoIncrement()) {
24822     Transaction()->ForgetModifiedAutoIncrementObjectStore(mMetadata);
24823   }
24824 
24825   return NS_OK;
24826 }
24827 
24828 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)24829 RenameObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection)
24830 {
24831   MOZ_ASSERT(aConnection);
24832   aConnection->AssertIsOnConnectionThread();
24833 
24834   PROFILER_LABEL("IndexedDB",
24835                  "RenameObjectStoreOp::DoDatabaseWork",
24836                  js::ProfileEntry::Category::STORAGE);
24837 
24838   if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
24839     return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
24840   }
24841 
24842 #ifdef DEBUG
24843   {
24844     // Make sure that we're not renaming an object store with the same name as
24845     // another that already exists. This should be impossible because we should
24846     // have thrown an error long before now...
24847     DatabaseConnection::CachedStatement stmt;
24848     MOZ_ALWAYS_SUCCEEDS(
24849       aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24850         "SELECT name "
24851           "FROM object_store "
24852           "WHERE name = :name "
24853           "AND id != :id;"),
24854         &stmt));
24855 
24856     MOZ_ALWAYS_SUCCEEDS(
24857       stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName));
24858 
24859     MOZ_ALWAYS_SUCCEEDS(
24860       stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mId));
24861 
24862     bool hasResult;
24863     MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
24864     MOZ_ASSERT(!hasResult);
24865   }
24866 #endif
24867 
24868   DatabaseConnection::AutoSavepoint autoSave;
24869   nsresult rv = autoSave.Start(Transaction());
24870   if (NS_WARN_IF(NS_FAILED(rv))) {
24871     return rv;
24872   }
24873 
24874   DatabaseConnection::CachedStatement stmt;
24875   rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24876     "UPDATE object_store "
24877       "SET name = :name "
24878       "WHERE id = :id;"),
24879     &stmt);
24880   if (NS_WARN_IF(NS_FAILED(rv))) {
24881     return rv;
24882   }
24883 
24884   rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName);
24885 
24886   if (NS_WARN_IF(NS_FAILED(rv))) {
24887     return rv;
24888   }
24889 
24890   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mId);
24891 
24892   if (NS_WARN_IF(NS_FAILED(rv))) {
24893     return rv;
24894   }
24895 
24896   rv = stmt->Execute();
24897   if (NS_WARN_IF(NS_FAILED(rv))) {
24898     return rv;
24899   }
24900 
24901   rv = autoSave.Commit();
24902   if (NS_WARN_IF(NS_FAILED(rv))) {
24903     return rv;
24904   }
24905 
24906   return NS_OK;
24907 }
24908 
CreateIndexOp(VersionChangeTransaction * aTransaction,const int64_t aObjectStoreId,const IndexMetadata & aMetadata)24909 CreateIndexOp::CreateIndexOp(VersionChangeTransaction* aTransaction,
24910                              const int64_t aObjectStoreId,
24911                              const IndexMetadata& aMetadata)
24912   : VersionChangeTransactionOp(aTransaction)
24913   , mMetadata(aMetadata)
24914   , mFileManager(aTransaction->GetDatabase()->GetFileManager())
24915   , mDatabaseId(aTransaction->DatabaseId())
24916   , mObjectStoreId(aObjectStoreId)
24917 {
24918   MOZ_ASSERT(aObjectStoreId);
24919   MOZ_ASSERT(aMetadata.id());
24920   MOZ_ASSERT(mFileManager);
24921   MOZ_ASSERT(!mDatabaseId.IsEmpty());
24922 }
24923 
24924 unsigned int CreateIndexOp::sThreadLocalIndex = kBadThreadLocalIndex;
24925 
24926 nsresult
InsertDataFromObjectStore(DatabaseConnection * aConnection)24927 CreateIndexOp::InsertDataFromObjectStore(DatabaseConnection* aConnection)
24928 {
24929   MOZ_ASSERT(aConnection);
24930   aConnection->AssertIsOnConnectionThread();
24931   MOZ_ASSERT(!IndexedDatabaseManager::InLowDiskSpaceMode());
24932   MOZ_ASSERT(mMaybeUniqueIndexTable);
24933 
24934   PROFILER_LABEL("IndexedDB",
24935                  "CreateIndexOp::InsertDataFromObjectStore",
24936                  js::ProfileEntry::Category::STORAGE);
24937 
24938   nsCOMPtr<mozIStorageConnection> storageConnection =
24939     aConnection->GetStorageConnection();
24940   MOZ_ASSERT(storageConnection);
24941 
24942   ThreadLocalJSContext* context = ThreadLocalJSContext::GetOrCreate();
24943   if (NS_WARN_IF(!context)) {
24944     IDB_REPORT_INTERNAL_ERR();
24945     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
24946   }
24947 
24948   JSContext* cx = context->Context();
24949   JSAutoRequest ar(cx);
24950   JSAutoCompartment ac(cx, context->Global());
24951 
24952   RefPtr<UpdateIndexDataValuesFunction> updateFunction =
24953     new UpdateIndexDataValuesFunction(this, aConnection, cx);
24954 
24955   NS_NAMED_LITERAL_CSTRING(updateFunctionName, "update_index_data_values");
24956 
24957   nsresult rv =
24958     storageConnection->CreateFunction(updateFunctionName,
24959                                       4,
24960                                       updateFunction);
24961   if (NS_WARN_IF(NS_FAILED(rv))) {
24962     return rv;
24963   }
24964 
24965   rv = InsertDataFromObjectStoreInternal(aConnection);
24966 
24967   MOZ_ALWAYS_SUCCEEDS(storageConnection->RemoveFunction(updateFunctionName));
24968 
24969   if (NS_WARN_IF(NS_FAILED(rv))) {
24970     return rv;
24971   }
24972 
24973   return NS_OK;
24974 }
24975 
24976 nsresult
InsertDataFromObjectStoreInternal(DatabaseConnection * aConnection)24977 CreateIndexOp::InsertDataFromObjectStoreInternal(
24978                                                 DatabaseConnection* aConnection)
24979 {
24980   MOZ_ASSERT(aConnection);
24981   aConnection->AssertIsOnConnectionThread();
24982   MOZ_ASSERT(!IndexedDatabaseManager::InLowDiskSpaceMode());
24983   MOZ_ASSERT(mMaybeUniqueIndexTable);
24984 
24985   DebugOnly<void*> storageConnection = aConnection->GetStorageConnection();
24986   MOZ_ASSERT(storageConnection);
24987 
24988   DatabaseConnection::CachedStatement stmt;
24989   nsresult rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
24990     "UPDATE object_data "
24991       "SET index_data_values = update_index_data_values "
24992         "(key, index_data_values, file_ids, data) "
24993       "WHERE object_store_id = :object_store_id;"),
24994     &stmt);
24995   if (NS_WARN_IF(NS_FAILED(rv))) {
24996     return rv;
24997   }
24998 
24999   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
25000                              mObjectStoreId);
25001   if (NS_WARN_IF(NS_FAILED(rv))) {
25002     return rv;
25003   }
25004 
25005   rv = stmt->Execute();
25006   if (NS_WARN_IF(NS_FAILED(rv))) {
25007     return rv;
25008   }
25009 
25010   return NS_OK;
25011 }
25012 
25013 bool
Init(TransactionBase * aTransaction)25014 CreateIndexOp::Init(TransactionBase* aTransaction)
25015 {
25016   AssertIsOnBackgroundThread();
25017   MOZ_ASSERT(aTransaction);
25018 
25019   struct MOZ_STACK_CLASS Helper final
25020   {
25021     static void
25022     Destroy(void* aThreadLocal)
25023     {
25024       delete static_cast<ThreadLocalJSContext*>(aThreadLocal);
25025     }
25026   };
25027 
25028   if (sThreadLocalIndex == kBadThreadLocalIndex) {
25029     if (NS_WARN_IF(PR_SUCCESS !=
25030                      PR_NewThreadPrivateIndex(&sThreadLocalIndex,
25031                                               &Helper::Destroy))) {
25032       return false;
25033     }
25034   }
25035 
25036   MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
25037 
25038   nsresult rv =
25039     GetUniqueIndexTableForObjectStore(aTransaction,
25040                                       mObjectStoreId,
25041                                       mMaybeUniqueIndexTable);
25042   if (NS_WARN_IF(NS_FAILED(rv))) {
25043     return false;
25044   }
25045 
25046   return true;
25047 }
25048 
25049 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)25050 CreateIndexOp::DoDatabaseWork(DatabaseConnection* aConnection)
25051 {
25052   MOZ_ASSERT(aConnection);
25053   aConnection->AssertIsOnConnectionThread();
25054 
25055   PROFILER_LABEL("IndexedDB",
25056                  "CreateIndexOp::DoDatabaseWork",
25057                  js::ProfileEntry::Category::STORAGE);
25058 
25059   if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
25060     return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
25061   }
25062 
25063 #ifdef DEBUG
25064   {
25065     // Make sure that we're not creating an index with the same name and object
25066     // store as another that already exists. This should be impossible because
25067     // we should have thrown an error long before now...
25068     DatabaseConnection::CachedStatement stmt;
25069     MOZ_ALWAYS_SUCCEEDS(
25070       aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25071         "SELECT name "
25072           "FROM object_store_index "
25073           "WHERE object_store_id = :osid "
25074           "AND name = :name;"),
25075         &stmt));
25076     MOZ_ALWAYS_SUCCEEDS(
25077       stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId));
25078     MOZ_ALWAYS_SUCCEEDS(
25079       stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mMetadata.name()));
25080 
25081     bool hasResult;
25082     MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
25083 
25084     MOZ_ASSERT(!hasResult);
25085   }
25086 #endif
25087 
25088   DatabaseConnection::AutoSavepoint autoSave;
25089   nsresult rv = autoSave.Start(Transaction());
25090   if (NS_WARN_IF(NS_FAILED(rv))) {
25091     return rv;
25092   }
25093 
25094   DatabaseConnection::CachedStatement stmt;
25095   rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25096     "INSERT INTO object_store_index (id, name, key_path, unique_index, "
25097                                     "multientry, object_store_id, locale, "
25098                                     "is_auto_locale) "
25099     "VALUES (:id, :name, :key_path, :unique, :multientry, :osid, :locale, "
25100             ":is_auto_locale)"),
25101     &stmt);
25102   if (NS_WARN_IF(NS_FAILED(rv))) {
25103     return rv;
25104   }
25105 
25106   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mMetadata.id());
25107   if (NS_WARN_IF(NS_FAILED(rv))) {
25108     return rv;
25109   }
25110 
25111   rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mMetadata.name());
25112   if (NS_WARN_IF(NS_FAILED(rv))) {
25113     return rv;
25114   }
25115 
25116   nsAutoString keyPathSerialization;
25117   mMetadata.keyPath().SerializeToString(keyPathSerialization);
25118   rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
25119                               keyPathSerialization);
25120   if (NS_WARN_IF(NS_FAILED(rv))) {
25121     return rv;
25122   }
25123 
25124   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("unique"),
25125                              mMetadata.unique() ? 1 : 0);
25126   if (NS_WARN_IF(NS_FAILED(rv))) {
25127     return rv;
25128   }
25129 
25130   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("multientry"),
25131                              mMetadata.multiEntry() ? 1 : 0);
25132   if (NS_WARN_IF(NS_FAILED(rv))) {
25133     return rv;
25134   }
25135 
25136   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId);
25137   if (NS_WARN_IF(NS_FAILED(rv))) {
25138     return rv;
25139   }
25140 
25141   if (mMetadata.locale().IsEmpty()) {
25142     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("locale"));
25143   } else {
25144     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("locale"),
25145                                     mMetadata.locale());
25146   }
25147   if (NS_WARN_IF(NS_FAILED(rv))) {
25148     return rv;
25149   }
25150 
25151   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("is_auto_locale"),
25152                              mMetadata.autoLocale());
25153   if (NS_WARN_IF(NS_FAILED(rv))) {
25154     return rv;
25155   }
25156 
25157   rv = stmt->Execute();
25158   if (NS_WARN_IF(NS_FAILED(rv))) {
25159     return rv;
25160   }
25161 
25162 #ifdef DEBUG
25163   {
25164     int64_t id;
25165     MOZ_ALWAYS_SUCCEEDS(
25166       aConnection->GetStorageConnection()->GetLastInsertRowID(&id));
25167     MOZ_ASSERT(mMetadata.id() == id);
25168   }
25169 #endif
25170 
25171   rv = InsertDataFromObjectStore(aConnection);
25172   if (NS_WARN_IF(NS_FAILED(rv))) {
25173     return rv;
25174   }
25175 
25176   rv = autoSave.Commit();
25177   if (NS_WARN_IF(NS_FAILED(rv))) {
25178     return rv;
25179   }
25180 
25181   return NS_OK;
25182 }
25183 
25184 static const JSClassOps sNormalJSContextGlobalClassOps = {
25185   /* addProperty */ nullptr,
25186   /* delProperty */ nullptr,
25187   /* getProperty */ nullptr,
25188   /* setProperty */ nullptr,
25189   /* enumerate */ nullptr,
25190   /* resolve */ nullptr,
25191   /* mayResolve */ nullptr,
25192   /* finalize */ nullptr,
25193   /* call */ nullptr,
25194   /* hasInstance */ nullptr,
25195   /* construct */ nullptr,
25196   /* trace */ JS_GlobalObjectTraceHook
25197 };
25198 
25199 const JSClass NormalJSContext::sGlobalClass = {
25200   "IndexedDBTransactionThreadGlobal",
25201   JSCLASS_GLOBAL_FLAGS,
25202   &sNormalJSContextGlobalClassOps
25203 };
25204 
25205 bool
Init()25206 NormalJSContext::Init()
25207 {
25208   MOZ_ASSERT(!IsOnBackgroundThread());
25209 
25210   mContext = JS_NewContext(kContextHeapSize);
25211   if (NS_WARN_IF(!mContext)) {
25212     return false;
25213   }
25214 
25215   // Let everyone know that we might be able to call JS. This alerts the
25216   // profiler about certain possible deadlocks.
25217   NS_GetCurrentThread()->SetCanInvokeJS(true);
25218 
25219   // Not setting this will cause JS_CHECK_RECURSION to report false positives.
25220   JS_SetNativeStackQuota(mContext, 128 * sizeof(size_t) * 1024);
25221 
25222   if (NS_WARN_IF(!JS::InitSelfHostedCode(mContext))) {
25223     return false;
25224   }
25225 
25226   JSAutoRequest ar(mContext);
25227 
25228   JS::CompartmentOptions options;
25229   mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr,
25230                                JS::FireOnNewGlobalHook, options);
25231   if (NS_WARN_IF(!mGlobal)) {
25232     return false;
25233   }
25234 
25235   return true;
25236 }
25237 
25238 // static
25239 NormalJSContext*
Create()25240 NormalJSContext::Create()
25241 {
25242   MOZ_ASSERT(!IsOnBackgroundThread());
25243 
25244   nsAutoPtr<NormalJSContext> newContext(new NormalJSContext());
25245 
25246   if (NS_WARN_IF(!newContext->Init())) {
25247     return nullptr;
25248   }
25249 
25250   return newContext.forget();
25251 }
25252 
25253 // static
25254 auto
25255 CreateIndexOp::
GetOrCreate()25256 ThreadLocalJSContext::GetOrCreate() -> ThreadLocalJSContext*
25257 {
25258   MOZ_ASSERT(!IsOnBackgroundThread());
25259   MOZ_ASSERT(CreateIndexOp::kBadThreadLocalIndex !=
25260              CreateIndexOp::sThreadLocalIndex);
25261 
25262   auto* context = static_cast<ThreadLocalJSContext*>(
25263     PR_GetThreadPrivate(CreateIndexOp::sThreadLocalIndex));
25264   if (context) {
25265     return context;
25266   }
25267 
25268   nsAutoPtr<ThreadLocalJSContext> newContext(new ThreadLocalJSContext());
25269 
25270   if (NS_WARN_IF(!newContext->Init())) {
25271     return nullptr;
25272   }
25273 
25274   DebugOnly<PRStatus> status =
25275     PR_SetThreadPrivate(CreateIndexOp::sThreadLocalIndex, newContext);
25276   MOZ_ASSERT(status == PR_SUCCESS);
25277 
25278   return newContext.forget();
25279 }
25280 
25281 NS_IMPL_ISUPPORTS(CreateIndexOp::UpdateIndexDataValuesFunction,
25282                   mozIStorageFunction);
25283 
25284 NS_IMETHODIMP
25285 CreateIndexOp::
OnFunctionCall(mozIStorageValueArray * aValues,nsIVariant ** _retval)25286 UpdateIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aValues,
25287                                               nsIVariant** _retval)
25288 {
25289   MOZ_ASSERT(aValues);
25290   MOZ_ASSERT(_retval);
25291   MOZ_ASSERT(mConnection);
25292   mConnection->AssertIsOnConnectionThread();
25293   MOZ_ASSERT(mOp);
25294   MOZ_ASSERT(mCx);
25295 
25296   PROFILER_LABEL("IndexedDB",
25297                  "CreateIndexOp::UpdateIndexDataValuesFunction::OnFunctionCall",
25298                  js::ProfileEntry::Category::STORAGE);
25299 
25300 #ifdef DEBUG
25301   {
25302     uint32_t argCount;
25303     MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
25304     MOZ_ASSERT(argCount == 4); // key, index_data_values, file_ids, data
25305 
25306     int32_t valueType;
25307     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType));
25308     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
25309 
25310     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(1, &valueType));
25311     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
25312                valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
25313 
25314     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(2, &valueType));
25315     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
25316                valueType == mozIStorageValueArray::VALUE_TYPE_TEXT);
25317 
25318     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(3, &valueType));
25319     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB ||
25320                valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
25321   }
25322 #endif
25323 
25324   StructuredCloneReadInfo cloneInfo;
25325   nsresult rv =
25326     GetStructuredCloneReadInfoFromValueArray(aValues,
25327                                              /* aDataIndex */ 3,
25328                                              /* aFileIdsIndex */ 2,
25329                                              mOp->mFileManager,
25330                                              &cloneInfo);
25331   if (NS_WARN_IF(NS_FAILED(rv))) {
25332     return rv;
25333   }
25334 
25335   JS::Rooted<JS::Value> clone(mCx);
25336   if (NS_WARN_IF(!IDBObjectStore::DeserializeIndexValue(mCx,
25337                                                         cloneInfo,
25338                                                         &clone))) {
25339     return NS_ERROR_DOM_DATA_CLONE_ERR;
25340   }
25341 
25342   const IndexMetadata& metadata = mOp->mMetadata;
25343   const int64_t& objectStoreId = mOp->mObjectStoreId;
25344 
25345   AutoTArray<IndexUpdateInfo, 32> updateInfos;
25346   rv = IDBObjectStore::AppendIndexUpdateInfo(metadata.id(),
25347                                              metadata.keyPath(),
25348                                              metadata.unique(),
25349                                              metadata.multiEntry(),
25350                                              metadata.locale(),
25351                                              mCx,
25352                                              clone,
25353                                              updateInfos);
25354   if (NS_WARN_IF(NS_FAILED(rv))) {
25355     return rv;
25356   }
25357 
25358   if (updateInfos.IsEmpty()) {
25359     // XXX See if we can do this without copying...
25360 
25361     nsCOMPtr<nsIVariant> unmodifiedValue;
25362 
25363     // No changes needed, just return the original value.
25364     int32_t valueType;
25365     rv = aValues->GetTypeOfIndex(1, &valueType);
25366     if (NS_WARN_IF(NS_FAILED(rv))) {
25367       return rv;
25368     }
25369 
25370     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
25371                valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
25372 
25373     if (valueType == mozIStorageValueArray::VALUE_TYPE_NULL) {
25374       unmodifiedValue = new storage::NullVariant();
25375       unmodifiedValue.forget(_retval);
25376       return NS_OK;
25377     }
25378 
25379     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
25380 
25381     const uint8_t* blobData;
25382     uint32_t blobDataLength;
25383     rv = aValues->GetSharedBlob(1, &blobDataLength, &blobData);
25384     if (NS_WARN_IF(NS_FAILED(rv))) {
25385       return rv;
25386     }
25387 
25388     std::pair<uint8_t *, int> copiedBlobDataPair(
25389       static_cast<uint8_t*>(malloc(blobDataLength)),
25390       blobDataLength);
25391 
25392     if (!copiedBlobDataPair.first) {
25393       IDB_REPORT_INTERNAL_ERR();
25394       return NS_ERROR_OUT_OF_MEMORY;
25395     }
25396 
25397     memcpy(copiedBlobDataPair.first, blobData, blobDataLength);
25398 
25399     unmodifiedValue = new storage::AdoptedBlobVariant(copiedBlobDataPair);
25400     unmodifiedValue.forget(_retval);
25401 
25402     return NS_OK;
25403   }
25404 
25405   Key key;
25406   rv = key.SetFromValueArray(aValues, 0);
25407   if (NS_WARN_IF(NS_FAILED(rv))) {
25408     return rv;
25409   }
25410 
25411   AutoTArray<IndexDataValue, 32> indexValues;
25412   rv = ReadCompressedIndexDataValues(aValues, 1, indexValues);
25413   if (NS_WARN_IF(NS_FAILED(rv))) {
25414     return rv;
25415   }
25416 
25417   const bool hadPreviousIndexValues = !indexValues.IsEmpty();
25418 
25419   const uint32_t updateInfoCount = updateInfos.Length();
25420 
25421   if (NS_WARN_IF(!indexValues.SetCapacity(indexValues.Length() +
25422                                           updateInfoCount, fallible))) {
25423     IDB_REPORT_INTERNAL_ERR();
25424     return NS_ERROR_OUT_OF_MEMORY;
25425   }
25426 
25427   // First construct the full list to update the index_data_values row.
25428   for (uint32_t index = 0; index < updateInfoCount; index++) {
25429     const IndexUpdateInfo& info = updateInfos[index];
25430 
25431     MOZ_ALWAYS_TRUE(
25432       indexValues.InsertElementSorted(IndexDataValue(metadata.id(),
25433                                                      metadata.unique(),
25434                                                      info.value(),
25435                                                      info.localizedValue()),
25436                                       fallible));
25437   }
25438 
25439   UniqueFreePtr<uint8_t> indexValuesBlob;
25440   uint32_t indexValuesBlobLength;
25441   rv = MakeCompressedIndexDataValues(indexValues,
25442                                      indexValuesBlob,
25443                                      &indexValuesBlobLength);
25444   if (NS_WARN_IF(NS_FAILED(rv))) {
25445     return rv;
25446   }
25447 
25448   MOZ_ASSERT(!indexValuesBlobLength == !(indexValuesBlob.get()));
25449 
25450   nsCOMPtr<nsIVariant> value;
25451 
25452   if (!indexValuesBlob) {
25453     value = new storage::NullVariant();
25454 
25455     value.forget(_retval);
25456     return NS_OK;
25457   }
25458 
25459   // Now insert the new table rows. We only need to construct a new list if
25460   // the full list is different.
25461   if (hadPreviousIndexValues) {
25462     indexValues.ClearAndRetainStorage();
25463 
25464     MOZ_ASSERT(indexValues.Capacity() >= updateInfoCount);
25465 
25466     for (uint32_t index = 0; index < updateInfoCount; index++) {
25467       const IndexUpdateInfo& info = updateInfos[index];
25468 
25469       MOZ_ALWAYS_TRUE(
25470         indexValues.InsertElementSorted(IndexDataValue(metadata.id(),
25471                                                        metadata.unique(),
25472                                                        info.value(),
25473                                                        info.localizedValue()),
25474                                         fallible));
25475     }
25476   }
25477 
25478   rv = InsertIndexTableRows(mConnection, objectStoreId, key, indexValues);
25479   if (NS_WARN_IF(NS_FAILED(rv))) {
25480     return rv;
25481   }
25482 
25483   std::pair<uint8_t *, int> copiedBlobDataPair(indexValuesBlob.release(),
25484                                                indexValuesBlobLength);
25485 
25486   value = new storage::AdoptedBlobVariant(copiedBlobDataPair);
25487 
25488   value.forget(_retval);
25489   return NS_OK;
25490 }
25491 
DeleteIndexOp(VersionChangeTransaction * aTransaction,const int64_t aObjectStoreId,const int64_t aIndexId,const bool aUnique,const bool aIsLastIndex)25492 DeleteIndexOp::DeleteIndexOp(VersionChangeTransaction* aTransaction,
25493                              const int64_t aObjectStoreId,
25494                              const int64_t aIndexId,
25495                              const bool aUnique,
25496                              const bool aIsLastIndex)
25497   : VersionChangeTransactionOp(aTransaction)
25498   , mObjectStoreId(aObjectStoreId)
25499   , mIndexId(aIndexId)
25500   , mUnique(aUnique)
25501   , mIsLastIndex(aIsLastIndex)
25502 {
25503   MOZ_ASSERT(aObjectStoreId);
25504   MOZ_ASSERT(aIndexId);
25505 }
25506 
25507 nsresult
RemoveReferencesToIndex(DatabaseConnection * aConnection,const Key & aObjectStoreKey,nsTArray<IndexDataValue> & aIndexValues)25508 DeleteIndexOp::RemoveReferencesToIndex(DatabaseConnection* aConnection,
25509                                        const Key& aObjectStoreKey,
25510                                        nsTArray<IndexDataValue>& aIndexValues)
25511 {
25512   MOZ_ASSERT(!NS_IsMainThread());
25513   MOZ_ASSERT(!IsOnBackgroundThread());
25514   MOZ_ASSERT(aConnection);
25515   MOZ_ASSERT(!aObjectStoreKey.IsUnset());
25516   MOZ_ASSERT_IF(!mIsLastIndex, !aIndexValues.IsEmpty());
25517 
25518   struct MOZ_STACK_CLASS IndexIdComparator final
25519   {
25520     bool
25521     Equals(const IndexDataValue& aA, const IndexDataValue& aB) const
25522     {
25523       // Ignore everything but the index id.
25524       return aA.mIndexId == aB.mIndexId;
25525     };
25526 
25527     bool
25528     LessThan(const IndexDataValue& aA, const IndexDataValue& aB) const
25529     {
25530       return aA.mIndexId < aB.mIndexId;
25531     };
25532   };
25533 
25534   PROFILER_LABEL("IndexedDB",
25535                  "DeleteIndexOp::RemoveReferencesToIndex",
25536                  js::ProfileEntry::Category::STORAGE);
25537 
25538   if (mIsLastIndex) {
25539     // There is no need to parse the previous entry in the index_data_values
25540     // column if this is the last index. Simply set it to NULL.
25541     DatabaseConnection::CachedStatement stmt;
25542     nsresult rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25543       "UPDATE object_data "
25544         "SET index_data_values = NULL "
25545         "WHERE object_store_id = :object_store_id "
25546         "AND key = :key;"),
25547       &stmt);
25548     if (NS_WARN_IF(NS_FAILED(rv))) {
25549       return rv;
25550     }
25551 
25552     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
25553                                mObjectStoreId);
25554     if (NS_WARN_IF(NS_FAILED(rv))) {
25555       return rv;
25556     }
25557 
25558     rv = aObjectStoreKey.BindToStatement(stmt, NS_LITERAL_CSTRING("key"));
25559     if (NS_WARN_IF(NS_FAILED(rv))) {
25560       return rv;
25561     }
25562 
25563     rv = stmt->Execute();
25564     if (NS_WARN_IF(NS_FAILED(rv))) {
25565       return rv;
25566     }
25567 
25568     return NS_OK;
25569   }
25570 
25571   IndexDataValue search;
25572   search.mIndexId = mIndexId;
25573 
25574   // This returns the first element that matches our index id found during a
25575   // binary search. However, there could still be other elements before that.
25576   size_t firstElementIndex =
25577     aIndexValues.BinaryIndexOf(search, IndexIdComparator());
25578   if (NS_WARN_IF(firstElementIndex == aIndexValues.NoIndex) ||
25579       NS_WARN_IF(aIndexValues[firstElementIndex].mIndexId != mIndexId)) {
25580     IDB_REPORT_INTERNAL_ERR();
25581     return NS_ERROR_FILE_CORRUPTED;
25582   }
25583 
25584   MOZ_ASSERT(aIndexValues[firstElementIndex].mIndexId == mIndexId);
25585 
25586   // Walk backwards to find the real first index.
25587   while (firstElementIndex) {
25588     if (aIndexValues[firstElementIndex - 1].mIndexId == mIndexId) {
25589       firstElementIndex--;
25590     } else {
25591       break;
25592     }
25593   }
25594 
25595   MOZ_ASSERT(aIndexValues[firstElementIndex].mIndexId == mIndexId);
25596 
25597   const size_t indexValuesLength = aIndexValues.Length();
25598 
25599   // Find the last element with the same index id.
25600   size_t lastElementIndex = firstElementIndex;
25601 
25602   while (lastElementIndex < indexValuesLength) {
25603     if (aIndexValues[lastElementIndex].mIndexId == mIndexId) {
25604       lastElementIndex++;
25605     } else {
25606       break;
25607     }
25608   }
25609 
25610   MOZ_ASSERT(lastElementIndex > firstElementIndex);
25611   MOZ_ASSERT_IF(lastElementIndex < indexValuesLength,
25612                 aIndexValues[lastElementIndex].mIndexId != mIndexId);
25613   MOZ_ASSERT(aIndexValues[lastElementIndex - 1].mIndexId == mIndexId);
25614 
25615   aIndexValues.RemoveElementsAt(firstElementIndex,
25616                                 lastElementIndex - firstElementIndex);
25617 
25618   nsresult rv = UpdateIndexValues(aConnection,
25619                                   mObjectStoreId,
25620                                   aObjectStoreKey,
25621                                   aIndexValues);
25622   if (NS_WARN_IF(NS_FAILED(rv))) {
25623     return rv;
25624   }
25625 
25626   return NS_OK;
25627 }
25628 
25629 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)25630 DeleteIndexOp::DoDatabaseWork(DatabaseConnection* aConnection)
25631 {
25632   MOZ_ASSERT(aConnection);
25633   aConnection->AssertIsOnConnectionThread();
25634 
25635 #ifdef DEBUG
25636   {
25637     // Make sure |mIsLastIndex| is telling the truth.
25638     DatabaseConnection::CachedStatement stmt;
25639     MOZ_ALWAYS_SUCCEEDS(
25640       aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25641         "SELECT id "
25642           "FROM object_store_index "
25643           "WHERE object_store_id = :object_store_id;"),
25644         &stmt));
25645 
25646     MOZ_ALWAYS_SUCCEEDS(
25647       stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
25648                             mObjectStoreId));
25649 
25650     bool foundThisIndex = false;
25651     bool foundOtherIndex = false;
25652 
25653     while (true) {
25654       bool hasResult;
25655       MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
25656 
25657       if (!hasResult) {
25658         break;
25659       }
25660 
25661       int64_t id;
25662       MOZ_ALWAYS_SUCCEEDS(stmt->GetInt64(0, &id));
25663 
25664       if (id == mIndexId) {
25665         foundThisIndex = true;
25666       } else {
25667         foundOtherIndex = true;
25668       }
25669     }
25670 
25671     MOZ_ASSERT_IF(mIsLastIndex, foundThisIndex && !foundOtherIndex);
25672     MOZ_ASSERT_IF(!mIsLastIndex, foundThisIndex && foundOtherIndex);
25673   }
25674 #endif
25675 
25676   PROFILER_LABEL("IndexedDB",
25677                  "DeleteIndexOp::DoDatabaseWork",
25678                  js::ProfileEntry::Category::STORAGE);
25679 
25680   DatabaseConnection::AutoSavepoint autoSave;
25681   nsresult rv = autoSave.Start(Transaction());
25682   if (NS_WARN_IF(NS_FAILED(rv))) {
25683     return rv;
25684   }
25685 
25686   DatabaseConnection::CachedStatement selectStmt;
25687 
25688   // mozStorage warns that these statements trigger a sort operation but we
25689   // don't care because this is a very rare call and we expect it to be slow.
25690   // The cost of having an index on this field is too high.
25691   if (mUnique) {
25692     if (mIsLastIndex) {
25693       rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25694         "/* do not warn (bug someone else) */ "
25695         "SELECT value, object_data_key "
25696           "FROM unique_index_data "
25697           "WHERE index_id = :index_id "
25698           "ORDER BY object_data_key ASC;"),
25699         &selectStmt);
25700     } else {
25701       rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25702         "/* do not warn (bug out) */ "
25703         "SELECT unique_index_data.value, "
25704                "unique_index_data.object_data_key, "
25705                "object_data.index_data_values "
25706           "FROM unique_index_data "
25707           "JOIN object_data "
25708           "ON unique_index_data.object_data_key = object_data.key "
25709           "WHERE unique_index_data.index_id = :index_id "
25710           "AND object_data.object_store_id = :object_store_id "
25711           "ORDER BY unique_index_data.object_data_key ASC;"),
25712         &selectStmt);
25713     }
25714   } else {
25715     if (mIsLastIndex) {
25716       rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25717         "/* do not warn (bug me not) */ "
25718         "SELECT value, object_data_key "
25719           "FROM index_data "
25720           "WHERE index_id = :index_id "
25721           "AND object_store_id = :object_store_id "
25722           "ORDER BY object_data_key ASC;"),
25723         &selectStmt);
25724     } else {
25725       rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25726         "/* do not warn (bug off) */ "
25727         "SELECT index_data.value, "
25728                "index_data.object_data_key, "
25729                "object_data.index_data_values "
25730           "FROM index_data "
25731           "JOIN object_data "
25732           "ON index_data.object_data_key = object_data.key "
25733           "WHERE index_data.index_id = :index_id "
25734           "AND object_data.object_store_id = :object_store_id "
25735           "ORDER BY index_data.object_data_key ASC;"),
25736         &selectStmt);
25737     }
25738   }
25739   if (NS_WARN_IF(NS_FAILED(rv))) {
25740     return rv;
25741   }
25742 
25743   NS_NAMED_LITERAL_CSTRING(indexIdString, "index_id");
25744 
25745   rv = selectStmt->BindInt64ByName(indexIdString, mIndexId);
25746   if (NS_WARN_IF(NS_FAILED(rv))) {
25747     return rv;
25748   }
25749 
25750   if (!mUnique || !mIsLastIndex) {
25751     rv = selectStmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
25752                                      mObjectStoreId);
25753     if (NS_WARN_IF(NS_FAILED(rv))) {
25754       return rv;
25755     }
25756   }
25757 
25758   NS_NAMED_LITERAL_CSTRING(valueString, "value");
25759   NS_NAMED_LITERAL_CSTRING(objectDataKeyString, "object_data_key");
25760 
25761   DatabaseConnection::CachedStatement deleteIndexRowStmt;
25762   DatabaseConnection::CachedStatement nullIndexDataValuesStmt;
25763 
25764   Key lastObjectStoreKey;
25765   AutoTArray<IndexDataValue, 32> lastIndexValues;
25766 
25767   bool hasResult;
25768   while (NS_SUCCEEDED(rv = selectStmt->ExecuteStep(&hasResult)) && hasResult) {
25769     // We always need the index key to delete the index row.
25770     Key indexKey;
25771     rv = indexKey.SetFromStatement(selectStmt, 0);
25772     if (NS_WARN_IF(NS_FAILED(rv))) {
25773       return rv;
25774     }
25775 
25776     if (NS_WARN_IF(indexKey.IsUnset())) {
25777       IDB_REPORT_INTERNAL_ERR();
25778       return NS_ERROR_FILE_CORRUPTED;
25779     }
25780 
25781     // Don't call |lastObjectStoreKey.BindToStatement()| directly because we
25782     // don't want to copy the same key multiple times.
25783     const uint8_t* objectStoreKeyData;
25784     uint32_t objectStoreKeyDataLength;
25785     rv = selectStmt->GetSharedBlob(1,
25786                                    &objectStoreKeyDataLength,
25787                                    &objectStoreKeyData);
25788     if (NS_WARN_IF(NS_FAILED(rv))) {
25789       return rv;
25790     }
25791 
25792     if (NS_WARN_IF(!objectStoreKeyDataLength)) {
25793       IDB_REPORT_INTERNAL_ERR();
25794       return NS_ERROR_FILE_CORRUPTED;
25795     }
25796 
25797     nsDependentCString currentObjectStoreKeyBuffer(
25798       reinterpret_cast<const char*>(objectStoreKeyData),
25799       objectStoreKeyDataLength);
25800     if (currentObjectStoreKeyBuffer != lastObjectStoreKey.GetBuffer()) {
25801       // We just walked to the next object store key.
25802       if (!lastObjectStoreKey.IsUnset()) {
25803         // Before we move on to the next key we need to update the previous
25804         // key's index_data_values column.
25805         rv = RemoveReferencesToIndex(aConnection,
25806                                       lastObjectStoreKey,
25807                                       lastIndexValues);
25808         if (NS_WARN_IF(NS_FAILED(rv))) {
25809           return rv;
25810         }
25811       }
25812 
25813       // Save the object store key.
25814       lastObjectStoreKey = Key(currentObjectStoreKeyBuffer);
25815 
25816       // And the |index_data_values| row if this isn't the only index.
25817       if (!mIsLastIndex) {
25818         lastIndexValues.ClearAndRetainStorage();
25819         rv = ReadCompressedIndexDataValues(selectStmt, 2, lastIndexValues);
25820         if (NS_WARN_IF(NS_FAILED(rv))) {
25821           return rv;
25822         }
25823 
25824         if (NS_WARN_IF(lastIndexValues.IsEmpty())) {
25825           IDB_REPORT_INTERNAL_ERR();
25826           return NS_ERROR_FILE_CORRUPTED;
25827         }
25828       }
25829     }
25830 
25831     // Now delete the index row.
25832     if (deleteIndexRowStmt) {
25833         MOZ_ALWAYS_SUCCEEDS(deleteIndexRowStmt->Reset());
25834     } else {
25835       if (mUnique) {
25836         rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25837           "DELETE FROM unique_index_data "
25838             "WHERE index_id = :index_id "
25839             "AND value = :value;"),
25840           &deleteIndexRowStmt);
25841       } else {
25842         rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25843           "DELETE FROM index_data "
25844             "WHERE index_id = :index_id "
25845             "AND value = :value "
25846             "AND object_data_key = :object_data_key;"),
25847           &deleteIndexRowStmt);
25848       }
25849       if (NS_WARN_IF(NS_FAILED(rv))) {
25850         return rv;
25851       }
25852     }
25853 
25854     rv = deleteIndexRowStmt->BindInt64ByName(indexIdString, mIndexId);
25855     if (NS_WARN_IF(NS_FAILED(rv))) {
25856       return rv;
25857     }
25858 
25859     rv = indexKey.BindToStatement(deleteIndexRowStmt, valueString);
25860     if (NS_WARN_IF(NS_FAILED(rv))) {
25861       return rv;
25862     }
25863 
25864     if (!mUnique) {
25865       rv = lastObjectStoreKey.BindToStatement(deleteIndexRowStmt,
25866                                               objectDataKeyString);
25867       if (NS_WARN_IF(NS_FAILED(rv))) {
25868         return rv;
25869       }
25870     }
25871 
25872     rv = deleteIndexRowStmt->Execute();
25873     if (NS_WARN_IF(NS_FAILED(rv))) {
25874       return rv;
25875     }
25876   }
25877   if (NS_WARN_IF(NS_FAILED(rv))) {
25878     return rv;
25879   }
25880 
25881   // Take care of the last key.
25882   if (!lastObjectStoreKey.IsUnset()) {
25883     MOZ_ASSERT_IF(!mIsLastIndex, !lastIndexValues.IsEmpty());
25884 
25885     rv = RemoveReferencesToIndex(aConnection,
25886                                  lastObjectStoreKey,
25887                                  lastIndexValues);
25888     if (NS_WARN_IF(NS_FAILED(rv))) {
25889       return rv;
25890     }
25891   }
25892 
25893   DatabaseConnection::CachedStatement deleteStmt;
25894   rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25895     "DELETE FROM object_store_index "
25896       "WHERE id = :index_id;"),
25897     &deleteStmt);
25898   if (NS_WARN_IF(NS_FAILED(rv))) {
25899     return rv;
25900   }
25901 
25902   rv = deleteStmt->BindInt64ByName(indexIdString, mIndexId);
25903   if (NS_WARN_IF(NS_FAILED(rv))) {
25904     return rv;
25905   }
25906 
25907   rv = deleteStmt->Execute();
25908   if (NS_WARN_IF(NS_FAILED(rv))) {
25909     return rv;
25910   }
25911 
25912 #ifdef DEBUG
25913   {
25914     int32_t deletedRowCount;
25915     MOZ_ALWAYS_SUCCEEDS(
25916       aConnection->GetStorageConnection()->GetAffectedRows(&deletedRowCount));
25917     MOZ_ASSERT(deletedRowCount == 1);
25918   }
25919 #endif
25920 
25921   rv = autoSave.Commit();
25922   if (NS_WARN_IF(NS_FAILED(rv))) {
25923     return rv;
25924   }
25925 
25926   return NS_OK;
25927 }
25928 
25929 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)25930 RenameIndexOp::DoDatabaseWork(DatabaseConnection* aConnection)
25931 {
25932   MOZ_ASSERT(aConnection);
25933   aConnection->AssertIsOnConnectionThread();
25934 
25935   PROFILER_LABEL("IndexedDB",
25936                  "RenameIndexOp::DoDatabaseWork",
25937                  js::ProfileEntry::Category::STORAGE);
25938 
25939   if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
25940     return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
25941   }
25942 
25943 #ifdef DEBUG
25944   {
25945     // Make sure that we're not renaming an index with the same name as another
25946     // that already exists. This should be impossible because we should have
25947     // thrown an error long before now...
25948     DatabaseConnection::CachedStatement stmt;
25949     MOZ_ALWAYS_SUCCEEDS(
25950       aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25951         "SELECT name "
25952           "FROM object_store_index "
25953           "WHERE object_store_id = :object_store_id "
25954            "AND name = :name "
25955            "AND id != :id;"),
25956         &stmt));
25957 
25958     MOZ_ALWAYS_SUCCEEDS(
25959       stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
25960                             mObjectStoreId));
25961 
25962     MOZ_ALWAYS_SUCCEEDS(
25963       stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName));
25964 
25965     MOZ_ALWAYS_SUCCEEDS(
25966       stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndexId));
25967 
25968     bool hasResult;
25969     MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
25970     MOZ_ASSERT(!hasResult);
25971   }
25972 #else
25973   Unused << mObjectStoreId;
25974 #endif
25975 
25976   DatabaseConnection::AutoSavepoint autoSave;
25977   nsresult rv = autoSave.Start(Transaction());
25978   if (NS_WARN_IF(NS_FAILED(rv))) {
25979     return rv;
25980   }
25981 
25982   DatabaseConnection::CachedStatement stmt;
25983   rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
25984     "UPDATE object_store_index "
25985       "SET name = :name "
25986       "WHERE id = :id;"),
25987     &stmt);
25988   if (NS_WARN_IF(NS_FAILED(rv))) {
25989     return rv;
25990   }
25991 
25992   rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName);
25993 
25994   if (NS_WARN_IF(NS_FAILED(rv))) {
25995     return rv;
25996   }
25997 
25998   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndexId);
25999 
26000   if (NS_WARN_IF(NS_FAILED(rv))) {
26001     return rv;
26002   }
26003 
26004   rv = stmt->Execute();
26005   if (NS_WARN_IF(NS_FAILED(rv))) {
26006     return rv;
26007   }
26008 
26009   rv = autoSave.Commit();
26010   if (NS_WARN_IF(NS_FAILED(rv))) {
26011     return rv;
26012   }
26013 
26014 
26015   return NS_OK;
26016 }
26017 
26018 // static
26019 nsresult
ObjectStoreHasIndexes(NormalTransactionOp * aOp,DatabaseConnection * aConnection,const int64_t aObjectStoreId,const bool aMayHaveIndexes,bool * aHasIndexes)26020 NormalTransactionOp::ObjectStoreHasIndexes(NormalTransactionOp* aOp,
26021                                            DatabaseConnection* aConnection,
26022                                            const int64_t aObjectStoreId,
26023                                            const bool aMayHaveIndexes,
26024                                            bool* aHasIndexes)
26025 {
26026   MOZ_ASSERT(aOp);
26027   MOZ_ASSERT(aConnection);
26028   aConnection->AssertIsOnConnectionThread();
26029   MOZ_ASSERT(aObjectStoreId);
26030   MOZ_ASSERT(aHasIndexes);
26031 
26032   bool hasIndexes;
26033   if (aOp->Transaction()->GetMode() == IDBTransaction::VERSION_CHANGE &&
26034       aMayHaveIndexes) {
26035     // If this is a version change transaction then mObjectStoreMayHaveIndexes
26036     // could be wrong (e.g. if a unique index failed to be created due to a
26037     // constraint error). We have to check on this thread by asking the database
26038     // directly.
26039     nsresult rv =
26040       DatabaseOperationBase::ObjectStoreHasIndexes(aConnection,
26041                                                    aObjectStoreId,
26042                                                    &hasIndexes);
26043     if (NS_WARN_IF(NS_FAILED(rv))) {
26044       return rv;
26045     }
26046   } else {
26047     MOZ_ASSERT(NS_SUCCEEDED(
26048       DatabaseOperationBase::ObjectStoreHasIndexes(aConnection,
26049                                                    aObjectStoreId,
26050                                                    &hasIndexes)));
26051     MOZ_ASSERT(aMayHaveIndexes == hasIndexes);
26052 
26053     hasIndexes = aMayHaveIndexes;
26054   }
26055 
26056   *aHasIndexes = hasIndexes;
26057   return NS_OK;
26058 }
26059 
26060 nsresult
GetPreprocessParams(PreprocessParams & aParams)26061 NormalTransactionOp::GetPreprocessParams(PreprocessParams& aParams)
26062 {
26063   return NS_OK;
26064 }
26065 
26066 nsresult
SendPreprocessInfo()26067 NormalTransactionOp::SendPreprocessInfo()
26068 {
26069   AssertIsOnOwningThread();
26070   MOZ_ASSERT(!IsActorDestroyed());
26071 
26072   PreprocessParams params;
26073   nsresult rv = GetPreprocessParams(params);
26074   if (NS_WARN_IF(NS_FAILED(rv))) {
26075     return rv;
26076   }
26077 
26078   MOZ_ASSERT(params.type() != PreprocessParams::T__None);
26079 
26080   if (NS_WARN_IF(!PBackgroundIDBRequestParent::SendPreprocess(params))) {
26081     IDB_REPORT_INTERNAL_ERR();
26082     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
26083   }
26084 
26085   return NS_OK;
26086 }
26087 
26088 nsresult
SendSuccessResult()26089 NormalTransactionOp::SendSuccessResult()
26090 {
26091   AssertIsOnOwningThread();
26092 
26093   if (!IsActorDestroyed()) {
26094     RequestResponse response;
26095     GetResponse(response);
26096 
26097     MOZ_ASSERT(response.type() != RequestResponse::T__None);
26098 
26099     if (response.type() == RequestResponse::Tnsresult) {
26100       MOZ_ASSERT(NS_FAILED(response.get_nsresult()));
26101 
26102       return response.get_nsresult();
26103     }
26104 
26105     if (NS_WARN_IF(!PBackgroundIDBRequestParent::Send__delete__(this,
26106                                                                 response))) {
26107       IDB_REPORT_INTERNAL_ERR();
26108       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
26109     }
26110   }
26111 
26112 #ifdef DEBUG
26113   mResponseSent = true;
26114 #endif
26115 
26116   return NS_OK;
26117 }
26118 
26119 bool
SendFailureResult(nsresult aResultCode)26120 NormalTransactionOp::SendFailureResult(nsresult aResultCode)
26121 {
26122   AssertIsOnOwningThread();
26123   MOZ_ASSERT(NS_FAILED(aResultCode));
26124 
26125   bool result = false;
26126 
26127   if (!IsActorDestroyed()) {
26128     result =
26129       PBackgroundIDBRequestParent::Send__delete__(this,
26130                                                   ClampResultCode(aResultCode));
26131   }
26132 
26133 #ifdef DEBUG
26134   mResponseSent = true;
26135 #endif
26136 
26137   return result;
26138 }
26139 
26140 void
Cleanup()26141 NormalTransactionOp::Cleanup()
26142 {
26143   AssertIsOnOwningThread();
26144   MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent);
26145 
26146   TransactionDatabaseOperationBase::Cleanup();
26147 }
26148 
26149 void
ActorDestroy(ActorDestroyReason aWhy)26150 NormalTransactionOp::ActorDestroy(ActorDestroyReason aWhy)
26151 {
26152   AssertIsOnOwningThread();
26153 
26154   NoteActorDestroyed();
26155 }
26156 
26157 bool
RecvContinue(const PreprocessResponse & aResponse)26158 NormalTransactionOp::RecvContinue(const PreprocessResponse& aResponse)
26159 {
26160   AssertIsOnOwningThread();
26161 
26162   switch (aResponse.type()) {
26163     case PreprocessResponse::Tnsresult:
26164       mResultCode = aResponse.get_nsresult();
26165       break;
26166 
26167     case PreprocessResponse::TObjectStoreGetPreprocessResponse:
26168       break;
26169 
26170     case PreprocessResponse::TObjectStoreGetAllPreprocessResponse:
26171       break;
26172 
26173     default:
26174       MOZ_CRASH("Should never get here!");
26175   }
26176 
26177   NoteContinueReceived();
26178 
26179   return true;
26180 }
26181 
ObjectStoreAddOrPutRequestOp(TransactionBase * aTransaction,const RequestParams & aParams)26182 ObjectStoreAddOrPutRequestOp::ObjectStoreAddOrPutRequestOp(
26183                                                   TransactionBase* aTransaction,
26184                                                   const RequestParams& aParams)
26185   : NormalTransactionOp(aTransaction)
26186   , mParams(aParams.type() == RequestParams::TObjectStoreAddParams ?
26187               aParams.get_ObjectStoreAddParams().commonParams() :
26188               aParams.get_ObjectStorePutParams().commonParams())
26189   , mGroup(aTransaction->GetDatabase()->Group())
26190   , mOrigin(aTransaction->GetDatabase()->Origin())
26191   , mPersistenceType(aTransaction->GetDatabase()->Type())
26192   , mOverwrite(aParams.type() == RequestParams::TObjectStorePutParams)
26193   , mObjectStoreMayHaveIndexes(false)
26194 {
26195   MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreAddParams ||
26196              aParams.type() == RequestParams::TObjectStorePutParams);
26197 
26198   mMetadata =
26199     aTransaction->GetMetadataForObjectStoreId(mParams.objectStoreId());
26200   MOZ_ASSERT(mMetadata);
26201 
26202   mObjectStoreMayHaveIndexes = mMetadata->HasLiveIndexes();
26203 
26204   mDataOverThreshold =
26205     snappy::MaxCompressedLength(mParams.cloneInfo().data().data.Size()) >
26206       IndexedDatabaseManager::DataThreshold();
26207 }
26208 
26209 nsresult
RemoveOldIndexDataValues(DatabaseConnection * aConnection)26210 ObjectStoreAddOrPutRequestOp::RemoveOldIndexDataValues(
26211                                                 DatabaseConnection* aConnection)
26212 {
26213   AssertIsOnConnectionThread();
26214   MOZ_ASSERT(aConnection);
26215   MOZ_ASSERT(mOverwrite);
26216   MOZ_ASSERT(!mResponse.IsUnset());
26217 
26218 #ifdef DEBUG
26219   {
26220     bool hasIndexes = false;
26221     MOZ_ASSERT(NS_SUCCEEDED(
26222       DatabaseOperationBase::ObjectStoreHasIndexes(aConnection,
26223                                                    mParams.objectStoreId(),
26224                                                    &hasIndexes)));
26225     MOZ_ASSERT(hasIndexes,
26226                "Don't use this slow method if there are no indexes!");
26227   }
26228 #endif
26229 
26230   DatabaseConnection::CachedStatement indexValuesStmt;
26231   nsresult rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
26232     "SELECT index_data_values "
26233       "FROM object_data "
26234       "WHERE object_store_id = :object_store_id "
26235       "AND key = :key;"),
26236     &indexValuesStmt);
26237   if (NS_WARN_IF(NS_FAILED(rv))) {
26238     return rv;
26239   }
26240 
26241   rv = indexValuesStmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
26242                                         mParams.objectStoreId());
26243   if (NS_WARN_IF(NS_FAILED(rv))) {
26244     return rv;
26245   }
26246 
26247   rv = mResponse.BindToStatement(indexValuesStmt, NS_LITERAL_CSTRING("key"));
26248   if (NS_WARN_IF(NS_FAILED(rv))) {
26249     return rv;
26250   }
26251 
26252   bool hasResult;
26253   rv = indexValuesStmt->ExecuteStep(&hasResult);
26254   if (NS_WARN_IF(NS_FAILED(rv))) {
26255     return rv;
26256   }
26257 
26258   if (hasResult) {
26259     AutoTArray<IndexDataValue, 32> existingIndexValues;
26260     rv = ReadCompressedIndexDataValues(indexValuesStmt,
26261                                         0,
26262                                         existingIndexValues);
26263     if (NS_WARN_IF(NS_FAILED(rv))) {
26264       return rv;
26265     }
26266 
26267     rv = DeleteIndexDataTableRows(aConnection, mResponse, existingIndexValues);
26268     if (NS_WARN_IF(NS_FAILED(rv))) {
26269       return rv;
26270     }
26271   }
26272 
26273   return NS_OK;
26274 }
26275 
26276 bool
Init(TransactionBase * aTransaction)26277 ObjectStoreAddOrPutRequestOp::Init(TransactionBase* aTransaction)
26278 {
26279   AssertIsOnOwningThread();
26280 
26281   const nsTArray<IndexUpdateInfo>& indexUpdateInfos =
26282     mParams.indexUpdateInfos();
26283 
26284   if (!indexUpdateInfos.IsEmpty()) {
26285     const uint32_t count = indexUpdateInfos.Length();
26286 
26287     mUniqueIndexTable.emplace();
26288 
26289     for (uint32_t index = 0; index < count; index++) {
26290       const IndexUpdateInfo& updateInfo = indexUpdateInfos[index];
26291 
26292       RefPtr<FullIndexMetadata> indexMetadata;
26293       MOZ_ALWAYS_TRUE(mMetadata->mIndexes.Get(updateInfo.indexId(),
26294                                               getter_AddRefs(indexMetadata)));
26295 
26296       MOZ_ASSERT(!indexMetadata->mDeleted);
26297 
26298       const int64_t& indexId = indexMetadata->mCommonMetadata.id();
26299       const bool& unique = indexMetadata->mCommonMetadata.unique();
26300 
26301       MOZ_ASSERT(indexId == updateInfo.indexId());
26302       MOZ_ASSERT_IF(!indexMetadata->mCommonMetadata.multiEntry(),
26303                     !mUniqueIndexTable.ref().Get(indexId));
26304 
26305       if (NS_WARN_IF(!mUniqueIndexTable.ref().Put(indexId, unique, fallible))) {
26306         return false;
26307       }
26308     }
26309   } else if (mOverwrite) {
26310     mUniqueIndexTable.emplace();
26311   }
26312 
26313 #ifdef DEBUG
26314   if (mUniqueIndexTable.isSome()) {
26315     mUniqueIndexTable.ref().MarkImmutable();
26316   }
26317 #endif
26318 
26319   const nsTArray<FileAddInfo>& fileAddInfos = mParams.fileAddInfos();
26320 
26321   if (!fileAddInfos.IsEmpty()) {
26322     const uint32_t count = fileAddInfos.Length();
26323 
26324     if (NS_WARN_IF(!mStoredFileInfos.SetCapacity(count, fallible))) {
26325       return false;
26326     }
26327 
26328     for (uint32_t index = 0; index < count; index++) {
26329       const FileAddInfo& fileAddInfo = fileAddInfos[index];
26330 
26331       MOZ_ASSERT(fileAddInfo.type() == StructuredCloneFile::eBlob ||
26332                  fileAddInfo.type() == StructuredCloneFile::eMutableFile ||
26333                  fileAddInfo.type() == StructuredCloneFile::eWasmBytecode ||
26334                  fileAddInfo.type() == StructuredCloneFile::eWasmCompiled);
26335 
26336       const DatabaseOrMutableFile& file = fileAddInfo.file();
26337 
26338       StoredFileInfo* storedFileInfo = mStoredFileInfos.AppendElement(fallible);
26339       MOZ_ASSERT(storedFileInfo);
26340 
26341       switch (fileAddInfo.type()) {
26342         case StructuredCloneFile::eBlob: {
26343           MOZ_ASSERT(file.type() ==
26344                        DatabaseOrMutableFile::TPBackgroundIDBDatabaseFileParent);
26345 
26346           storedFileInfo->mFileActor =
26347             static_cast<DatabaseFile*>(
26348               file.get_PBackgroundIDBDatabaseFileParent());
26349           MOZ_ASSERT(storedFileInfo->mFileActor);
26350 
26351           storedFileInfo->mFileInfo = storedFileInfo->mFileActor->GetFileInfo();
26352           MOZ_ASSERT(storedFileInfo->mFileInfo);
26353 
26354           storedFileInfo->mType = StructuredCloneFile::eBlob;
26355           break;
26356         }
26357 
26358         case StructuredCloneFile::eMutableFile: {
26359           MOZ_ASSERT(file.type() ==
26360                        DatabaseOrMutableFile::TPBackgroundMutableFileParent);
26361 
26362           auto mutableFileActor =
26363             static_cast<MutableFile*>(
26364               file.get_PBackgroundMutableFileParent());
26365           MOZ_ASSERT(mutableFileActor);
26366 
26367           storedFileInfo->mFileInfo = mutableFileActor->GetFileInfo();
26368           MOZ_ASSERT(storedFileInfo->mFileInfo);
26369 
26370           storedFileInfo->mType = StructuredCloneFile::eMutableFile;
26371           break;
26372         }
26373 
26374         case StructuredCloneFile::eWasmBytecode:
26375         case StructuredCloneFile::eWasmCompiled: {
26376           MOZ_ASSERT(file.type() ==
26377                        DatabaseOrMutableFile::TPBackgroundIDBDatabaseFileParent);
26378 
26379           storedFileInfo->mFileActor =
26380             static_cast<DatabaseFile*>(
26381               file.get_PBackgroundIDBDatabaseFileParent());
26382           MOZ_ASSERT(storedFileInfo->mFileActor);
26383 
26384           storedFileInfo->mFileInfo = storedFileInfo->mFileActor->GetFileInfo();
26385           MOZ_ASSERT(storedFileInfo->mFileInfo);
26386 
26387           storedFileInfo->mType = fileAddInfo.type();
26388           break;
26389         }
26390 
26391         default:
26392           MOZ_CRASH("Should never get here!");
26393       }
26394     }
26395   }
26396 
26397   if (mDataOverThreshold) {
26398     StoredFileInfo* storedFileInfo = mStoredFileInfos.AppendElement(fallible);
26399     MOZ_ASSERT(storedFileInfo);
26400 
26401     RefPtr<FileManager> fileManager =
26402       aTransaction->GetDatabase()->GetFileManager();
26403     MOZ_ASSERT(fileManager);
26404 
26405     storedFileInfo->mFileInfo = fileManager->GetNewFileInfo();
26406 
26407     storedFileInfo->mInputStream =
26408       new SCInputStream(mParams.cloneInfo().data().data);
26409 
26410     storedFileInfo->mType = StructuredCloneFile::eStructuredClone;
26411   }
26412 
26413   return true;
26414 }
26415 
26416 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)26417 ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
26418 {
26419   MOZ_ASSERT(aConnection);
26420   aConnection->AssertIsOnConnectionThread();
26421   MOZ_ASSERT(aConnection->GetStorageConnection());
26422 
26423   PROFILER_LABEL("IndexedDB",
26424                  "ObjectStoreAddOrPutRequestOp::DoDatabaseWork",
26425                  js::ProfileEntry::Category::STORAGE);
26426 
26427   if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
26428     return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
26429   }
26430 
26431   DatabaseConnection::AutoSavepoint autoSave;
26432   nsresult rv = autoSave.Start(Transaction());
26433   if (NS_WARN_IF(NS_FAILED(rv))) {
26434     return rv;
26435   }
26436 
26437   bool objectStoreHasIndexes;
26438   rv = ObjectStoreHasIndexes(this,
26439                              aConnection,
26440                              mParams.objectStoreId(),
26441                              mObjectStoreMayHaveIndexes,
26442                              &objectStoreHasIndexes);
26443   if (NS_WARN_IF(NS_FAILED(rv))) {
26444     return rv;
26445   }
26446 
26447   // This will be the final key we use.
26448   Key& key = mResponse;
26449   key = mParams.key();
26450 
26451   const bool keyUnset = key.IsUnset();
26452   const int64_t osid = mParams.objectStoreId();
26453 
26454   // First delete old index_data_values if we're overwriting something and we
26455   // have indexes.
26456   if (mOverwrite && !keyUnset && objectStoreHasIndexes) {
26457     rv = RemoveOldIndexDataValues(aConnection);
26458     if (NS_WARN_IF(NS_FAILED(rv))) {
26459       return rv;
26460     }
26461   }
26462 
26463   // The "|| keyUnset" here is mostly a debugging tool. If a key isn't
26464   // specified we should never have a collision and so it shouldn't matter
26465   // if we allow overwrite or not. By not allowing overwrite we raise
26466   // detectable errors rather than corrupting data.
26467   DatabaseConnection::CachedStatement stmt;
26468   if (!mOverwrite || keyUnset) {
26469     rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
26470       "INSERT INTO object_data "
26471         "(object_store_id, key, file_ids, data) "
26472         "VALUES (:osid, :key, :file_ids, :data);"),
26473       &stmt);
26474   } else {
26475     rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
26476       "INSERT OR REPLACE INTO object_data "
26477         "(object_store_id, key, file_ids, data) "
26478         "VALUES (:osid, :key, :file_ids, :data);"),
26479     &stmt);
26480   }
26481   if (NS_WARN_IF(NS_FAILED(rv))) {
26482     return rv;
26483   }
26484 
26485   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid);
26486   if (NS_WARN_IF(NS_FAILED(rv))) {
26487     return rv;
26488   }
26489 
26490   const SerializedStructuredCloneWriteInfo& cloneInfo = mParams.cloneInfo();
26491   const JSStructuredCloneData& cloneData = cloneInfo.data().data;
26492   size_t cloneDataSize = cloneData.Size();
26493 
26494   MOZ_ASSERT(!keyUnset || mMetadata->mCommonMetadata.autoIncrement(),
26495              "Should have key unless autoIncrement");
26496 
26497   int64_t autoIncrementNum = 0;
26498 
26499   if (mMetadata->mCommonMetadata.autoIncrement()) {
26500     if (keyUnset) {
26501       autoIncrementNum = mMetadata->mNextAutoIncrementId;
26502 
26503       MOZ_ASSERT(autoIncrementNum > 0);
26504 
26505       if (autoIncrementNum > (1LL << 53)) {
26506         return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
26507       }
26508 
26509       key.SetFromInteger(autoIncrementNum);
26510     } else if (key.IsFloat() &&
26511                key.ToFloat() >= mMetadata->mNextAutoIncrementId) {
26512       autoIncrementNum = floor(key.ToFloat());
26513     }
26514 
26515     if (keyUnset && mMetadata->mCommonMetadata.keyPath().IsValid()) {
26516       const SerializedStructuredCloneWriteInfo& cloneInfo = mParams.cloneInfo();
26517       MOZ_ASSERT(cloneInfo.offsetToKeyProp());
26518       MOZ_ASSERT(cloneDataSize > sizeof(uint64_t));
26519       MOZ_ASSERT(cloneInfo.offsetToKeyProp() <=
26520                  (cloneDataSize - sizeof(uint64_t)));
26521 
26522       // Special case where someone put an object into an autoIncrement'ing
26523       // objectStore with no key in its keyPath set. We needed to figure out
26524       // which row id we would get above before we could set that properly.
26525       uint64_t keyPropValue =
26526         ReinterpretDoubleAsUInt64(static_cast<double>(autoIncrementNum));
26527 
26528       static const size_t keyPropSize = sizeof(uint64_t);
26529 
26530       char keyPropBuffer[keyPropSize];
26531       LittleEndian::writeUint64(keyPropBuffer, keyPropValue);
26532 
26533       auto iter = cloneData.Iter();
26534       DebugOnly<bool> result =
26535        iter.AdvanceAcrossSegments(cloneData, cloneInfo.offsetToKeyProp());
26536       MOZ_ASSERT(result);
26537 
26538       for (uint32_t index = 0; index < keyPropSize; index++) {
26539         char* keyPropPointer = iter.Data();
26540         *keyPropPointer = keyPropBuffer[index];
26541 
26542         result = iter.AdvanceAcrossSegments(cloneData, 1);
26543         MOZ_ASSERT(result);
26544       }
26545     }
26546   }
26547 
26548   key.BindToStatement(stmt, NS_LITERAL_CSTRING("key"));
26549 
26550   if (mDataOverThreshold) {
26551     // The data we store in the SQLite database is a (signed) 64-bit integer.
26552     // The flags are left-shifted 32 bits so the max value is 0xFFFFFFFF.
26553     // The file_ids index occupies the lower 32 bits and its max is 0xFFFFFFFF.
26554     static const uint32_t kCompressedFlag = (1<<0);
26555 
26556     uint32_t flags = 0;
26557     flags |= kCompressedFlag;
26558 
26559     uint32_t index = mStoredFileInfos.Length() - 1;
26560 
26561     int64_t data = (uint64_t(flags) << 32) | index;
26562 
26563     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("data"), data);
26564     if (NS_WARN_IF(NS_FAILED(rv))) {
26565       return rv;
26566     }
26567   } else {
26568     nsCString flatCloneData;
26569     flatCloneData.SetLength(cloneDataSize);
26570     auto iter = cloneData.Iter();
26571     cloneData.ReadBytes(iter, flatCloneData.BeginWriting(), cloneDataSize);
26572 
26573     // Compress the bytes before adding into the database.
26574     const char* uncompressed = flatCloneData.BeginReading();
26575     size_t uncompressedLength = cloneDataSize;
26576 
26577     size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
26578 
26579     UniqueFreePtr<char> compressed(
26580       static_cast<char*>(malloc(compressedLength)));
26581     if (NS_WARN_IF(!compressed)) {
26582       return NS_ERROR_OUT_OF_MEMORY;
26583     }
26584 
26585     snappy::RawCompress(uncompressed, uncompressedLength, compressed.get(),
26586                         &compressedLength);
26587 
26588     uint8_t* dataBuffer = reinterpret_cast<uint8_t*>(compressed.release());
26589     size_t dataBufferLength = compressedLength;
26590 
26591     rv = stmt->BindAdoptedBlobByName(NS_LITERAL_CSTRING("data"), dataBuffer,
26592                                      dataBufferLength);
26593     if (NS_WARN_IF(NS_FAILED(rv))) {
26594       return rv;
26595     }
26596   }
26597 
26598   if (!mStoredFileInfos.IsEmpty()) {
26599     // Moved outside the loop to allow it to be cached when demanded by the
26600     // first write.  (We may have mStoredFileInfos without any required writes.)
26601     Maybe<FileHelper> fileHelper;
26602     nsAutoString fileIds;
26603 
26604     for (uint32_t count = mStoredFileInfos.Length(), index = 0;
26605          index < count;
26606          index++) {
26607       StoredFileInfo& storedFileInfo = mStoredFileInfos[index];
26608       MOZ_ASSERT(storedFileInfo.mFileInfo);
26609 
26610       // If there is a StoredFileInfo, then one of the following is true:
26611       // - This was an overflow structured clone and storedFileInfo.mInputStream
26612       //   MUST be non-null.
26613       // - This is a reference to a Blob that may or may not have already been
26614       //   written to disk.  storedFileInfo.mFileActor MUST be non-null, but
26615       //   its GetBlockingInputStream may return null (so don't assert on them).
26616       // - It's a mutable file.  No writing will be performed.
26617       MOZ_ASSERT(storedFileInfo.mInputStream || storedFileInfo.mFileActor ||
26618                  storedFileInfo.mType == StructuredCloneFile::eMutableFile);
26619 
26620       nsCOMPtr<nsIInputStream> inputStream;
26621       // Check for an explicit stream, like a structured clone stream.
26622       storedFileInfo.mInputStream.swap(inputStream);
26623       // Check for a blob-backed stream otherwise.
26624       if (!inputStream && storedFileInfo.mFileActor) {
26625         ErrorResult streamRv;
26626         inputStream =
26627           storedFileInfo.mFileActor->GetBlockingInputStream(streamRv);
26628         if (NS_WARN_IF(streamRv.Failed())) {
26629           return streamRv.StealNSResult();
26630         }
26631       }
26632 
26633       if (inputStream) {
26634         if (fileHelper.isNothing()) {
26635           RefPtr<FileManager> fileManager =
26636             Transaction()->GetDatabase()->GetFileManager();
26637           MOZ_ASSERT(fileManager);
26638 
26639           fileHelper.emplace(fileManager);
26640           rv = fileHelper->Init();
26641           if (NS_WARN_IF(NS_FAILED(rv))) {
26642             IDB_REPORT_INTERNAL_ERR();
26643             return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
26644           }
26645         }
26646 
26647         RefPtr<FileInfo>& fileInfo = storedFileInfo.mFileInfo;
26648 
26649         nsCOMPtr<nsIFile> file = fileHelper->GetFile(fileInfo);
26650         if (NS_WARN_IF(!file)) {
26651           IDB_REPORT_INTERNAL_ERR();
26652           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
26653         }
26654 
26655         nsCOMPtr<nsIFile> journalFile =
26656           fileHelper->GetJournalFile(fileInfo);
26657         if (NS_WARN_IF(!journalFile)) {
26658           IDB_REPORT_INTERNAL_ERR();
26659           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
26660         }
26661 
26662         bool compress =
26663           storedFileInfo.mType == StructuredCloneFile::eStructuredClone;
26664 
26665         rv = fileHelper->CreateFileFromStream(file,
26666                                               journalFile,
26667                                               inputStream,
26668                                               compress);
26669         if (NS_FAILED(rv) &&
26670             NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
26671           IDB_REPORT_INTERNAL_ERR();
26672           rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
26673         }
26674         if (NS_WARN_IF(NS_FAILED(rv))) {
26675           // Try to remove the file if the copy failed.
26676           nsresult rv2 = fileHelper->RemoveFile(file, journalFile);
26677           if (NS_WARN_IF(NS_FAILED(rv2))) {
26678             return rv;
26679           }
26680           return rv;
26681         }
26682 
26683         if (storedFileInfo.mFileActor) {
26684           storedFileInfo.mFileActor->WriteSucceededClearBlobImpl();
26685         }
26686       }
26687 
26688       if (index) {
26689         fileIds.Append(' ');
26690       }
26691       storedFileInfo.Serialize(fileIds);
26692     }
26693 
26694     rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds);
26695     if (NS_WARN_IF(NS_FAILED(rv))) {
26696       return rv;
26697     }
26698   } else {
26699     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids"));
26700     if (NS_WARN_IF(NS_FAILED(rv))) {
26701       return rv;
26702     }
26703   }
26704 
26705   rv = stmt->Execute();
26706   if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
26707     MOZ_ASSERT(!keyUnset, "Generated key had a collision!");
26708     return rv;
26709   }
26710 
26711   if (NS_WARN_IF(NS_FAILED(rv))) {
26712     return rv;
26713   }
26714 
26715   // Update our indexes if needed.
26716   if (!mParams.indexUpdateInfos().IsEmpty()) {
26717     MOZ_ASSERT(mUniqueIndexTable.isSome());
26718 
26719     // Write the index_data_values column.
26720     AutoTArray<IndexDataValue, 32> indexValues;
26721     rv = IndexDataValuesFromUpdateInfos(mParams.indexUpdateInfos(),
26722                                         mUniqueIndexTable.ref(),
26723                                         indexValues);
26724     if (NS_WARN_IF(NS_FAILED(rv))) {
26725       return rv;
26726     }
26727 
26728     rv = UpdateIndexValues(aConnection, osid, key, indexValues);
26729     if (NS_WARN_IF(NS_FAILED(rv))) {
26730       return rv;
26731     }
26732 
26733     rv = InsertIndexTableRows(aConnection, osid, key, indexValues);
26734     if (NS_FAILED(rv)) {
26735       return rv;
26736     }
26737   }
26738 
26739   rv = autoSave.Commit();
26740   if (NS_WARN_IF(NS_FAILED(rv))) {
26741     return rv;
26742   }
26743 
26744   if (autoIncrementNum) {
26745     mMetadata->mNextAutoIncrementId = autoIncrementNum + 1;
26746     Transaction()->NoteModifiedAutoIncrementObjectStore(mMetadata);
26747   }
26748 
26749   return NS_OK;
26750 }
26751 
26752 void
GetResponse(RequestResponse & aResponse)26753 ObjectStoreAddOrPutRequestOp::GetResponse(RequestResponse& aResponse)
26754 {
26755   AssertIsOnOwningThread();
26756 
26757   if (mOverwrite) {
26758     aResponse = ObjectStorePutResponse(mResponse);
26759   } else {
26760     aResponse = ObjectStoreAddResponse(mResponse);
26761   }
26762 }
26763 
26764 void
Cleanup()26765 ObjectStoreAddOrPutRequestOp::Cleanup()
26766 {
26767   AssertIsOnOwningThread();
26768 
26769   mStoredFileInfos.Clear();
26770 
26771   NormalTransactionOp::Cleanup();
26772 }
26773 
NS_IMPL_ISUPPORTS(ObjectStoreAddOrPutRequestOp::SCInputStream,nsIInputStream)26774 NS_IMPL_ISUPPORTS(ObjectStoreAddOrPutRequestOp::SCInputStream, nsIInputStream)
26775 
26776 NS_IMETHODIMP
26777 ObjectStoreAddOrPutRequestOp::
26778 SCInputStream::Close()
26779 {
26780   return NS_OK;
26781 }
26782 
26783 NS_IMETHODIMP
26784 ObjectStoreAddOrPutRequestOp::
Available(uint64_t * _retval)26785 SCInputStream::Available(uint64_t* _retval)
26786 {
26787   return NS_ERROR_NOT_IMPLEMENTED;
26788 }
26789 
26790 NS_IMETHODIMP
26791 ObjectStoreAddOrPutRequestOp::
Read(char * aBuf,uint32_t aCount,uint32_t * _retval)26792 SCInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* _retval)
26793 {
26794   return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
26795 }
26796 
26797 NS_IMETHODIMP
26798 ObjectStoreAddOrPutRequestOp::
ReadSegments(nsWriteSegmentFun aWriter,void * aClosure,uint32_t aCount,uint32_t * _retval)26799 SCInputStream::ReadSegments(nsWriteSegmentFun aWriter,
26800                             void* aClosure,
26801                             uint32_t aCount,
26802                             uint32_t* _retval)
26803 {
26804   *_retval = 0;
26805 
26806   while (aCount) {
26807     uint32_t count = std::min(uint32_t(mIter.RemainingInSegment()), aCount);
26808     if (!count) {
26809       // We've run out of data in the last segment.
26810       break;
26811     }
26812 
26813     uint32_t written;
26814     nsresult rv =
26815       aWriter(this, aClosure, mIter.Data(), *_retval, count, &written);
26816     if (NS_WARN_IF(NS_FAILED(rv))) {
26817       // InputStreams do not propagate errors to caller.
26818       return NS_OK;
26819     }
26820 
26821     // Writer should write what we asked it to write.
26822     MOZ_ASSERT(written == count);
26823 
26824     *_retval += count;
26825     aCount -= count;
26826 
26827     mIter.Advance(mData, count);
26828   }
26829 
26830   return NS_OK;
26831 }
26832 
26833 NS_IMETHODIMP
26834 ObjectStoreAddOrPutRequestOp::
IsNonBlocking(bool * _retval)26835 SCInputStream::IsNonBlocking(bool* _retval)
26836 {
26837   *_retval = false;
26838   return NS_OK;
26839 }
26840 
ObjectStoreGetRequestOp(TransactionBase * aTransaction,const RequestParams & aParams,bool aGetAll)26841 ObjectStoreGetRequestOp::ObjectStoreGetRequestOp(TransactionBase* aTransaction,
26842                                                  const RequestParams& aParams,
26843                                                  bool aGetAll)
26844   : NormalTransactionOp(aTransaction)
26845   , mObjectStoreId(aGetAll ?
26846                      aParams.get_ObjectStoreGetAllParams().objectStoreId() :
26847                      aParams.get_ObjectStoreGetParams().objectStoreId())
26848   , mDatabase(aTransaction->GetDatabase())
26849   , mOptionalKeyRange(aGetAll ?
26850                         aParams.get_ObjectStoreGetAllParams()
26851                                .optionalKeyRange() :
26852                         OptionalKeyRange(aParams.get_ObjectStoreGetParams()
26853                                                 .keyRange()))
26854   , mBackgroundParent(aTransaction->GetBackgroundParent())
26855   , mPreprocessInfoCount(0)
26856   , mLimit(aGetAll ? aParams.get_ObjectStoreGetAllParams().limit() : 1)
26857   , mGetAll(aGetAll)
26858 {
26859   MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreGetParams ||
26860              aParams.type() == RequestParams::TObjectStoreGetAllParams);
26861   MOZ_ASSERT(mObjectStoreId);
26862   MOZ_ASSERT(mDatabase);
26863   MOZ_ASSERT_IF(!aGetAll,
26864                 mOptionalKeyRange.type() ==
26865                   OptionalKeyRange::TSerializedKeyRange);
26866   MOZ_ASSERT(mBackgroundParent);
26867 }
26868 
26869 template <typename T>
26870 void MoveData(StructuredCloneReadInfo& aInfo, T& aResult);
26871 
26872 template <>
26873 void
MoveData(StructuredCloneReadInfo & aInfo,SerializedStructuredCloneReadInfo & aResult)26874 MoveData<SerializedStructuredCloneReadInfo>(
26875                                      StructuredCloneReadInfo& aInfo,
26876                                      SerializedStructuredCloneReadInfo& aResult)
26877 {
26878   aResult.data().data = Move(aInfo.mData);
26879   aResult.hasPreprocessInfo() = aInfo.mHasPreprocessInfo;
26880 }
26881 
26882 template <>
26883 void
MoveData(StructuredCloneReadInfo & aInfo,WasmModulePreprocessInfo & aResult)26884 MoveData<WasmModulePreprocessInfo>(StructuredCloneReadInfo& aInfo,
26885                                    WasmModulePreprocessInfo& aResult)
26886 {
26887 }
26888 
26889 template <bool aForPreprocess, typename T>
26890 nsresult
ConvertResponse(StructuredCloneReadInfo & aInfo,T & aResult)26891 ObjectStoreGetRequestOp::ConvertResponse(StructuredCloneReadInfo& aInfo,
26892                                          T& aResult)
26893 {
26894   MoveData(aInfo, aResult);
26895 
26896   FallibleTArray<SerializedStructuredCloneFile> serializedFiles;
26897   nsresult rv = SerializeStructuredCloneFiles(mBackgroundParent,
26898                                               mDatabase,
26899                                               aInfo.mFiles,
26900                                               aForPreprocess,
26901                                               serializedFiles);
26902   if (NS_WARN_IF(NS_FAILED(rv))) {
26903     return rv;
26904   }
26905 
26906   MOZ_ASSERT(aResult.files().IsEmpty());
26907 
26908   aResult.files().SwapElements(serializedFiles);
26909 
26910   return NS_OK;
26911 }
26912 
26913 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)26914 ObjectStoreGetRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
26915 {
26916   MOZ_ASSERT(aConnection);
26917   aConnection->AssertIsOnConnectionThread();
26918   MOZ_ASSERT_IF(!mGetAll,
26919                 mOptionalKeyRange.type() ==
26920                   OptionalKeyRange::TSerializedKeyRange);
26921   MOZ_ASSERT_IF(!mGetAll, mLimit == 1);
26922 
26923   PROFILER_LABEL("IndexedDB",
26924                  "ObjectStoreGetRequestOp::DoDatabaseWork",
26925                  js::ProfileEntry::Category::STORAGE);
26926 
26927   const bool hasKeyRange =
26928     mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
26929 
26930   nsAutoCString keyRangeClause;
26931   if (hasKeyRange) {
26932     GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
26933                                 NS_LITERAL_CSTRING("key"),
26934                                 keyRangeClause);
26935   }
26936 
26937   nsCString limitClause;
26938   if (mLimit) {
26939     limitClause.AssignLiteral(" LIMIT ");
26940     limitClause.AppendInt(mLimit);
26941   }
26942 
26943   nsCString query =
26944     NS_LITERAL_CSTRING("SELECT file_ids, data "
26945                        "FROM object_data "
26946                        "WHERE object_store_id = :osid") +
26947     keyRangeClause +
26948     NS_LITERAL_CSTRING(" ORDER BY key ASC") +
26949     limitClause;
26950 
26951   DatabaseConnection::CachedStatement stmt;
26952   nsresult rv = aConnection->GetCachedStatement(query, &stmt);
26953   if (NS_WARN_IF(NS_FAILED(rv))) {
26954     return rv;
26955   }
26956 
26957   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId);
26958   if (NS_WARN_IF(NS_FAILED(rv))) {
26959     return rv;
26960   }
26961 
26962   if (hasKeyRange) {
26963     rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
26964                                  stmt);
26965     if (NS_WARN_IF(NS_FAILED(rv))) {
26966       return rv;
26967     }
26968   }
26969 
26970   bool hasResult;
26971   while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
26972     StructuredCloneReadInfo* cloneInfo = mResponse.AppendElement(fallible);
26973     if (NS_WARN_IF(!cloneInfo)) {
26974       return NS_ERROR_OUT_OF_MEMORY;
26975     }
26976 
26977     rv = GetStructuredCloneReadInfoFromStatement(stmt, 1, 0,
26978                                                  mDatabase->GetFileManager(),
26979                                                  cloneInfo);
26980     if (NS_WARN_IF(NS_FAILED(rv))) {
26981       return rv;
26982     }
26983 
26984     if (cloneInfo->mHasPreprocessInfo) {
26985       mPreprocessInfoCount++;
26986     }
26987   }
26988 
26989   if (NS_WARN_IF(NS_FAILED(rv))) {
26990     return rv;
26991   }
26992 
26993   MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
26994 
26995   return NS_OK;
26996 }
26997 
26998 bool
HasPreprocessInfo()26999 ObjectStoreGetRequestOp::HasPreprocessInfo()
27000 {
27001   return mPreprocessInfoCount > 0;
27002 }
27003 
27004 nsresult
GetPreprocessParams(PreprocessParams & aParams)27005 ObjectStoreGetRequestOp::GetPreprocessParams(PreprocessParams& aParams)
27006 {
27007   AssertIsOnOwningThread();
27008   MOZ_ASSERT(!mResponse.IsEmpty());
27009 
27010   if (mGetAll) {
27011     aParams = ObjectStoreGetAllPreprocessParams();
27012 
27013     FallibleTArray<WasmModulePreprocessInfo> falliblePreprocessInfos;
27014     if (NS_WARN_IF(!falliblePreprocessInfos.SetLength(mPreprocessInfoCount,
27015                                                       fallible))) {
27016       return NS_ERROR_OUT_OF_MEMORY;
27017     }
27018 
27019     uint32_t fallibleIndex = 0;
27020     for (uint32_t count = mResponse.Length(), index = 0;
27021          index < count;
27022          index++) {
27023       StructuredCloneReadInfo& info = mResponse[index];
27024 
27025       if (info.mHasPreprocessInfo) {
27026         nsresult rv =
27027           ConvertResponse<true>(info, falliblePreprocessInfos[fallibleIndex++]);
27028         if (NS_WARN_IF(NS_FAILED(rv))) {
27029           return rv;
27030         }
27031       }
27032     }
27033 
27034     nsTArray<WasmModulePreprocessInfo>& preprocessInfos =
27035       aParams.get_ObjectStoreGetAllPreprocessParams().preprocessInfos();
27036 
27037     falliblePreprocessInfos.SwapElements(preprocessInfos);
27038 
27039     return NS_OK;
27040   }
27041 
27042   aParams = ObjectStoreGetPreprocessParams();
27043 
27044   WasmModulePreprocessInfo& preprocessInfo =
27045     aParams.get_ObjectStoreGetPreprocessParams().preprocessInfo();
27046 
27047   nsresult rv = ConvertResponse<true>(mResponse[0], preprocessInfo);
27048   if (NS_WARN_IF(NS_FAILED(rv))) {
27049     return rv;
27050   }
27051 
27052   return NS_OK;
27053 }
27054 
27055 void
GetResponse(RequestResponse & aResponse)27056 ObjectStoreGetRequestOp::GetResponse(RequestResponse& aResponse)
27057 {
27058   MOZ_ASSERT_IF(mLimit, mResponse.Length() <= mLimit);
27059 
27060   if (mGetAll) {
27061     aResponse = ObjectStoreGetAllResponse();
27062 
27063     if (!mResponse.IsEmpty()) {
27064       FallibleTArray<SerializedStructuredCloneReadInfo> fallibleCloneInfos;
27065       if (NS_WARN_IF(!fallibleCloneInfos.SetLength(mResponse.Length(),
27066                                                    fallible))) {
27067         aResponse = NS_ERROR_OUT_OF_MEMORY;
27068         return;
27069       }
27070 
27071       for (uint32_t count = mResponse.Length(), index = 0;
27072            index < count;
27073            index++) {
27074         nsresult rv =
27075           ConvertResponse<false>(mResponse[index], fallibleCloneInfos[index]);
27076         if (NS_WARN_IF(NS_FAILED(rv))) {
27077           aResponse = rv;
27078           return;
27079         }
27080       }
27081 
27082       nsTArray<SerializedStructuredCloneReadInfo>& cloneInfos =
27083         aResponse.get_ObjectStoreGetAllResponse().cloneInfos();
27084 
27085       fallibleCloneInfos.SwapElements(cloneInfos);
27086     }
27087 
27088     return;
27089   }
27090 
27091   aResponse = ObjectStoreGetResponse();
27092 
27093   if (!mResponse.IsEmpty()) {
27094     SerializedStructuredCloneReadInfo& serializedInfo =
27095       aResponse.get_ObjectStoreGetResponse().cloneInfo();
27096 
27097     nsresult rv = ConvertResponse<false>(mResponse[0], serializedInfo);
27098     if (NS_WARN_IF(NS_FAILED(rv))) {
27099       aResponse = rv;
27100     }
27101   }
27102 }
27103 
ObjectStoreGetKeyRequestOp(TransactionBase * aTransaction,const RequestParams & aParams,bool aGetAll)27104 ObjectStoreGetKeyRequestOp::ObjectStoreGetKeyRequestOp(
27105                                                   TransactionBase* aTransaction,
27106                                                   const RequestParams& aParams,
27107                                                   bool aGetAll)
27108   : NormalTransactionOp(aTransaction)
27109   , mObjectStoreId(aGetAll ?
27110                      aParams.get_ObjectStoreGetAllKeysParams().objectStoreId() :
27111                      aParams.get_ObjectStoreGetKeyParams().objectStoreId())
27112   , mOptionalKeyRange(aGetAll ?
27113                         aParams.get_ObjectStoreGetAllKeysParams()
27114                                .optionalKeyRange() :
27115                         OptionalKeyRange(aParams.get_ObjectStoreGetKeyParams()
27116                                                 .keyRange()))
27117   , mLimit(aGetAll ? aParams.get_ObjectStoreGetAllKeysParams().limit() : 1)
27118   , mGetAll(aGetAll)
27119 {
27120   MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreGetKeyParams ||
27121              aParams.type() == RequestParams::TObjectStoreGetAllKeysParams);
27122   MOZ_ASSERT(mObjectStoreId);
27123   MOZ_ASSERT_IF(!aGetAll,
27124                 mOptionalKeyRange.type() ==
27125                   OptionalKeyRange::TSerializedKeyRange);
27126 }
27127 
27128 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)27129 ObjectStoreGetKeyRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27130 {
27131   MOZ_ASSERT(aConnection);
27132   aConnection->AssertIsOnConnectionThread();
27133 
27134   PROFILER_LABEL("IndexedDB",
27135                  "ObjectStoreGetKeyRequestOp::DoDatabaseWork",
27136                  js::ProfileEntry::Category::STORAGE);
27137 
27138   const bool hasKeyRange =
27139       mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
27140 
27141   nsAutoCString keyRangeClause;
27142   if (hasKeyRange) {
27143     GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
27144                                 NS_LITERAL_CSTRING("key"),
27145                                 keyRangeClause);
27146   }
27147 
27148   nsAutoCString limitClause;
27149   if (mLimit) {
27150     limitClause.AssignLiteral(" LIMIT ");
27151     limitClause.AppendInt(mLimit);
27152   }
27153 
27154   nsCString query =
27155     NS_LITERAL_CSTRING("SELECT key "
27156                        "FROM object_data "
27157                        "WHERE object_store_id = :osid") +
27158     keyRangeClause +
27159     NS_LITERAL_CSTRING(" ORDER BY key ASC") +
27160     limitClause;
27161 
27162   DatabaseConnection::CachedStatement stmt;
27163   nsresult rv = aConnection->GetCachedStatement(query, &stmt);
27164   if (NS_WARN_IF(NS_FAILED(rv))) {
27165     return rv;
27166   }
27167 
27168   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId);
27169   if (NS_WARN_IF(NS_FAILED(rv))) {
27170     return rv;
27171   }
27172 
27173   if (hasKeyRange) {
27174     rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
27175                                  stmt);
27176     if (NS_WARN_IF(NS_FAILED(rv))) {
27177       return rv;
27178     }
27179   }
27180 
27181   bool hasResult;
27182   while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
27183     Key* key = mResponse.AppendElement(fallible);
27184     if (NS_WARN_IF(!key)) {
27185       return NS_ERROR_OUT_OF_MEMORY;
27186     }
27187 
27188     rv = key->SetFromStatement(stmt, 0);
27189     if (NS_WARN_IF(NS_FAILED(rv))) {
27190       return rv;
27191     }
27192   }
27193 
27194   if (NS_WARN_IF(NS_FAILED(rv))) {
27195     return rv;
27196   }
27197 
27198   MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
27199 
27200   return NS_OK;
27201 }
27202 
27203 void
GetResponse(RequestResponse & aResponse)27204 ObjectStoreGetKeyRequestOp::GetResponse(RequestResponse& aResponse)
27205 {
27206   MOZ_ASSERT_IF(mLimit, mResponse.Length() <= mLimit);
27207 
27208   if (mGetAll) {
27209     aResponse = ObjectStoreGetAllKeysResponse();
27210 
27211     if (!mResponse.IsEmpty()) {
27212       nsTArray<Key>& response =
27213         aResponse.get_ObjectStoreGetAllKeysResponse().keys();
27214       mResponse.SwapElements(response);
27215     }
27216 
27217     return;
27218   }
27219 
27220   aResponse = ObjectStoreGetKeyResponse();
27221 
27222   if (!mResponse.IsEmpty()) {
27223     aResponse.get_ObjectStoreGetKeyResponse().key() = Move(mResponse[0]);
27224   }
27225 }
27226 
ObjectStoreDeleteRequestOp(TransactionBase * aTransaction,const ObjectStoreDeleteParams & aParams)27227 ObjectStoreDeleteRequestOp::ObjectStoreDeleteRequestOp(
27228                                          TransactionBase* aTransaction,
27229                                          const ObjectStoreDeleteParams& aParams)
27230   : NormalTransactionOp(aTransaction)
27231   , mParams(aParams)
27232   , mObjectStoreMayHaveIndexes(false)
27233 {
27234   AssertIsOnBackgroundThread();
27235   MOZ_ASSERT(aTransaction);
27236 
27237   RefPtr<FullObjectStoreMetadata> metadata =
27238     aTransaction->GetMetadataForObjectStoreId(mParams.objectStoreId());
27239   MOZ_ASSERT(metadata);
27240 
27241   mObjectStoreMayHaveIndexes = metadata->HasLiveIndexes();
27242 }
27243 
27244 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)27245 ObjectStoreDeleteRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27246 {
27247   MOZ_ASSERT(aConnection);
27248   aConnection->AssertIsOnConnectionThread();
27249   PROFILER_LABEL("IndexedDB",
27250                  "ObjectStoreDeleteRequestOp::DoDatabaseWork",
27251                  js::ProfileEntry::Category::STORAGE);
27252 
27253   DatabaseConnection::AutoSavepoint autoSave;
27254   nsresult rv = autoSave.Start(Transaction());
27255   if (NS_WARN_IF(NS_FAILED(rv))) {
27256     return rv;
27257   }
27258 
27259   bool objectStoreHasIndexes;
27260   rv = ObjectStoreHasIndexes(this,
27261                              aConnection,
27262                              mParams.objectStoreId(),
27263                              mObjectStoreMayHaveIndexes,
27264                              &objectStoreHasIndexes);
27265   if (NS_WARN_IF(NS_FAILED(rv))) {
27266     return rv;
27267   }
27268 
27269   if (objectStoreHasIndexes) {
27270     rv = DeleteObjectStoreDataTableRowsWithIndexes(aConnection,
27271                                                    mParams.objectStoreId(),
27272                                                    mParams.keyRange());
27273     if (NS_WARN_IF(NS_FAILED(rv))) {
27274       return rv;
27275     }
27276   } else {
27277     NS_NAMED_LITERAL_CSTRING(objectStoreIdString, "object_store_id");
27278 
27279     nsAutoCString keyRangeClause;
27280     GetBindingClauseForKeyRange(mParams.keyRange(),
27281                                 NS_LITERAL_CSTRING("key"),
27282                                 keyRangeClause);
27283 
27284     DatabaseConnection::CachedStatement stmt;
27285     rv = aConnection->GetCachedStatement(
27286       NS_LITERAL_CSTRING("DELETE FROM object_data "
27287                            "WHERE object_store_id = :") + objectStoreIdString +
27288       keyRangeClause +
27289       NS_LITERAL_CSTRING(";"),
27290       &stmt);
27291     if (NS_WARN_IF(NS_FAILED(rv))) {
27292       return rv;
27293     }
27294 
27295     rv = stmt->BindInt64ByName(objectStoreIdString, mParams.objectStoreId());
27296     if (NS_WARN_IF(NS_FAILED(rv))) {
27297       return rv;
27298     }
27299 
27300     rv = BindKeyRangeToStatement(mParams.keyRange(), stmt);
27301     if (NS_WARN_IF(NS_FAILED(rv))) {
27302       return rv;
27303     }
27304 
27305     rv = stmt->Execute();
27306     if (NS_WARN_IF(NS_FAILED(rv))) {
27307       return rv;
27308     }
27309   }
27310 
27311   rv = autoSave.Commit();
27312   if (NS_WARN_IF(NS_FAILED(rv))) {
27313     return rv;
27314   }
27315 
27316   return NS_OK;
27317 }
27318 
ObjectStoreClearRequestOp(TransactionBase * aTransaction,const ObjectStoreClearParams & aParams)27319 ObjectStoreClearRequestOp::ObjectStoreClearRequestOp(
27320                                           TransactionBase* aTransaction,
27321                                           const ObjectStoreClearParams& aParams)
27322   : NormalTransactionOp(aTransaction)
27323   , mParams(aParams)
27324   , mObjectStoreMayHaveIndexes(false)
27325 {
27326   AssertIsOnBackgroundThread();
27327   MOZ_ASSERT(aTransaction);
27328 
27329   RefPtr<FullObjectStoreMetadata> metadata =
27330     aTransaction->GetMetadataForObjectStoreId(mParams.objectStoreId());
27331   MOZ_ASSERT(metadata);
27332 
27333   mObjectStoreMayHaveIndexes = metadata->HasLiveIndexes();
27334 }
27335 
27336 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)27337 ObjectStoreClearRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27338 {
27339   MOZ_ASSERT(aConnection);
27340   aConnection->AssertIsOnConnectionThread();
27341 
27342   PROFILER_LABEL("IndexedDB",
27343                  "ObjectStoreClearRequestOp::DoDatabaseWork",
27344                  js::ProfileEntry::Category::STORAGE);
27345 
27346   DatabaseConnection::AutoSavepoint autoSave;
27347   nsresult rv = autoSave.Start(Transaction());
27348   if (NS_WARN_IF(NS_FAILED(rv))) {
27349     return rv;
27350   }
27351 
27352   bool objectStoreHasIndexes;
27353   rv = ObjectStoreHasIndexes(this,
27354                              aConnection,
27355                              mParams.objectStoreId(),
27356                              mObjectStoreMayHaveIndexes,
27357                              &objectStoreHasIndexes);
27358   if (NS_WARN_IF(NS_FAILED(rv))) {
27359     return rv;
27360   }
27361 
27362   if (objectStoreHasIndexes) {
27363     rv = DeleteObjectStoreDataTableRowsWithIndexes(aConnection,
27364                                                    mParams.objectStoreId(),
27365                                                    void_t());
27366     if (NS_WARN_IF(NS_FAILED(rv))) {
27367       return rv;
27368     }
27369   } else {
27370     DatabaseConnection::CachedStatement stmt;
27371     rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
27372       "DELETE FROM object_data "
27373         "WHERE object_store_id = :object_store_id;"),
27374       &stmt);
27375     if (NS_WARN_IF(NS_FAILED(rv))) {
27376       return rv;
27377     }
27378 
27379     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
27380                                mParams.objectStoreId());
27381     if (NS_WARN_IF(NS_FAILED(rv))) {
27382       return rv;
27383     }
27384 
27385     rv = stmt->Execute();
27386     if (NS_WARN_IF(NS_FAILED(rv))) {
27387       return rv;
27388     }
27389   }
27390 
27391   rv = autoSave.Commit();
27392   if (NS_WARN_IF(NS_FAILED(rv))) {
27393     return rv;
27394   }
27395 
27396   return NS_OK;
27397 }
27398 
27399 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)27400 ObjectStoreCountRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27401 {
27402   MOZ_ASSERT(aConnection);
27403   aConnection->AssertIsOnConnectionThread();
27404 
27405   PROFILER_LABEL("IndexedDB",
27406                  "ObjectStoreCountRequestOp::DoDatabaseWork",
27407                  js::ProfileEntry::Category::STORAGE);
27408 
27409   const bool hasKeyRange =
27410     mParams.optionalKeyRange().type() == OptionalKeyRange::TSerializedKeyRange;
27411 
27412   nsAutoCString keyRangeClause;
27413   if (hasKeyRange) {
27414     GetBindingClauseForKeyRange(
27415       mParams.optionalKeyRange().get_SerializedKeyRange(),
27416       NS_LITERAL_CSTRING("key"),
27417       keyRangeClause);
27418   }
27419 
27420   nsCString query =
27421     NS_LITERAL_CSTRING("SELECT count(*) "
27422                        "FROM object_data "
27423                        "WHERE object_store_id = :osid") +
27424     keyRangeClause;
27425 
27426   DatabaseConnection::CachedStatement stmt;
27427   nsresult rv = aConnection->GetCachedStatement(query, &stmt);
27428   if (NS_WARN_IF(NS_FAILED(rv))) {
27429     return rv;
27430   }
27431 
27432   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
27433                              mParams.objectStoreId());
27434   if (NS_WARN_IF(NS_FAILED(rv))) {
27435     return rv;
27436   }
27437 
27438   if (hasKeyRange) {
27439     rv = BindKeyRangeToStatement(
27440       mParams.optionalKeyRange().get_SerializedKeyRange(),
27441       stmt);
27442     if (NS_WARN_IF(NS_FAILED(rv))) {
27443       return rv;
27444     }
27445   }
27446 
27447   bool hasResult;
27448   rv = stmt->ExecuteStep(&hasResult);
27449   if (NS_WARN_IF(NS_FAILED(rv))) {
27450     return rv;
27451   }
27452 
27453   if (NS_WARN_IF(!hasResult)) {
27454     MOZ_ASSERT(false, "This should never be possible!");
27455     IDB_REPORT_INTERNAL_ERR();
27456     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
27457   }
27458 
27459   int64_t count = stmt->AsInt64(0);
27460   if (NS_WARN_IF(count < 0)) {
27461     MOZ_ASSERT(false, "This should never be possible!");
27462     IDB_REPORT_INTERNAL_ERR();
27463     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
27464   }
27465 
27466   mResponse.count() = count;
27467 
27468   return NS_OK;
27469 }
27470 
27471 // static
27472 already_AddRefed<FullIndexMetadata>
IndexMetadataForParams(TransactionBase * aTransaction,const RequestParams & aParams)27473 IndexRequestOpBase::IndexMetadataForParams(TransactionBase* aTransaction,
27474                                            const RequestParams& aParams)
27475 {
27476   AssertIsOnBackgroundThread();
27477   MOZ_ASSERT(aTransaction);
27478   MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetParams ||
27479              aParams.type() == RequestParams::TIndexGetKeyParams ||
27480              aParams.type() == RequestParams::TIndexGetAllParams ||
27481              aParams.type() == RequestParams::TIndexGetAllKeysParams ||
27482              aParams.type() == RequestParams::TIndexCountParams);
27483 
27484   uint64_t objectStoreId;
27485   uint64_t indexId;
27486 
27487   switch (aParams.type()) {
27488     case RequestParams::TIndexGetParams: {
27489       const IndexGetParams& params = aParams.get_IndexGetParams();
27490       objectStoreId = params.objectStoreId();
27491       indexId = params.indexId();
27492       break;
27493     }
27494 
27495     case RequestParams::TIndexGetKeyParams: {
27496       const IndexGetKeyParams& params = aParams.get_IndexGetKeyParams();
27497       objectStoreId = params.objectStoreId();
27498       indexId = params.indexId();
27499       break;
27500     }
27501 
27502     case RequestParams::TIndexGetAllParams: {
27503       const IndexGetAllParams& params = aParams.get_IndexGetAllParams();
27504       objectStoreId = params.objectStoreId();
27505       indexId = params.indexId();
27506       break;
27507     }
27508 
27509     case RequestParams::TIndexGetAllKeysParams: {
27510       const IndexGetAllKeysParams& params = aParams.get_IndexGetAllKeysParams();
27511       objectStoreId = params.objectStoreId();
27512       indexId = params.indexId();
27513       break;
27514     }
27515 
27516     case RequestParams::TIndexCountParams: {
27517       const IndexCountParams& params = aParams.get_IndexCountParams();
27518       objectStoreId = params.objectStoreId();
27519       indexId = params.indexId();
27520       break;
27521     }
27522 
27523     default:
27524       MOZ_CRASH("Should never get here!");
27525   }
27526 
27527   const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
27528     aTransaction->GetMetadataForObjectStoreId(objectStoreId);
27529   MOZ_ASSERT(objectStoreMetadata);
27530 
27531   RefPtr<FullIndexMetadata> indexMetadata =
27532     aTransaction->GetMetadataForIndexId(objectStoreMetadata, indexId);
27533   MOZ_ASSERT(indexMetadata);
27534 
27535   return indexMetadata.forget();
27536 }
27537 
IndexGetRequestOp(TransactionBase * aTransaction,const RequestParams & aParams,bool aGetAll)27538 IndexGetRequestOp::IndexGetRequestOp(TransactionBase* aTransaction,
27539                                      const RequestParams& aParams,
27540                                      bool aGetAll)
27541   : IndexRequestOpBase(aTransaction, aParams)
27542   , mDatabase(aTransaction->GetDatabase())
27543   , mOptionalKeyRange(aGetAll ?
27544                         aParams.get_IndexGetAllParams().optionalKeyRange() :
27545                         OptionalKeyRange(aParams.get_IndexGetParams()
27546                                                 .keyRange()))
27547   , mBackgroundParent(aTransaction->GetBackgroundParent())
27548   , mLimit(aGetAll ? aParams.get_IndexGetAllParams().limit() : 1)
27549   , mGetAll(aGetAll)
27550 {
27551   MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetParams ||
27552              aParams.type() == RequestParams::TIndexGetAllParams);
27553   MOZ_ASSERT(mDatabase);
27554   MOZ_ASSERT_IF(!aGetAll,
27555                 mOptionalKeyRange.type() ==
27556                   OptionalKeyRange::TSerializedKeyRange);
27557   MOZ_ASSERT(mBackgroundParent);
27558 }
27559 
27560 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)27561 IndexGetRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27562 {
27563   MOZ_ASSERT(aConnection);
27564   aConnection->AssertIsOnConnectionThread();
27565   MOZ_ASSERT_IF(!mGetAll,
27566                 mOptionalKeyRange.type() ==
27567                   OptionalKeyRange::TSerializedKeyRange);
27568   MOZ_ASSERT_IF(!mGetAll, mLimit == 1);
27569 
27570   PROFILER_LABEL("IndexedDB",
27571                  "IndexGetRequestOp::DoDatabaseWork",
27572                  js::ProfileEntry::Category::STORAGE);
27573 
27574   const bool hasKeyRange =
27575     mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
27576 
27577   nsCString indexTable;
27578   if (mMetadata->mCommonMetadata.unique()) {
27579     indexTable.AssignLiteral("unique_index_data ");
27580   }
27581   else {
27582     indexTable.AssignLiteral("index_data ");
27583   }
27584 
27585   nsAutoCString keyRangeClause;
27586   if (hasKeyRange) {
27587     GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
27588                                 NS_LITERAL_CSTRING("value"),
27589                                 keyRangeClause);
27590   }
27591 
27592   nsCString limitClause;
27593   if (mLimit) {
27594     limitClause.AssignLiteral(" LIMIT ");
27595     limitClause.AppendInt(mLimit);
27596   }
27597 
27598   nsCString query =
27599     NS_LITERAL_CSTRING("SELECT file_ids, data "
27600                        "FROM object_data "
27601                        "INNER JOIN ") +
27602     indexTable +
27603     NS_LITERAL_CSTRING("AS index_table "
27604                        "ON object_data.object_store_id = "
27605                          "index_table.object_store_id "
27606                        "AND object_data.key = "
27607                          "index_table.object_data_key "
27608                        "WHERE index_id = :index_id") +
27609     keyRangeClause +
27610     limitClause;
27611 
27612   DatabaseConnection::CachedStatement stmt;
27613   nsresult rv = aConnection->GetCachedStatement(query, &stmt);
27614   if (NS_WARN_IF(NS_FAILED(rv))) {
27615     return rv;
27616   }
27617 
27618   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
27619                              mMetadata->mCommonMetadata.id());
27620   if (NS_WARN_IF(NS_FAILED(rv))) {
27621     return rv;
27622   }
27623 
27624   if (hasKeyRange) {
27625     rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
27626                                  stmt);
27627     if (NS_WARN_IF(NS_FAILED(rv))) {
27628       return rv;
27629     }
27630   }
27631 
27632   bool hasResult;
27633   while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
27634     StructuredCloneReadInfo* cloneInfo = mResponse.AppendElement(fallible);
27635     if (NS_WARN_IF(!cloneInfo)) {
27636       return NS_ERROR_OUT_OF_MEMORY;
27637     }
27638 
27639     rv = GetStructuredCloneReadInfoFromStatement(stmt, 1, 0,
27640                                                  mDatabase->GetFileManager(),
27641                                                  cloneInfo);
27642     if (NS_WARN_IF(NS_FAILED(rv))) {
27643       return rv;
27644     }
27645 
27646     if (cloneInfo->mHasPreprocessInfo) {
27647       IDB_WARNING("Preprocessing for indexes not yet implemented!");
27648       return NS_ERROR_NOT_IMPLEMENTED;
27649     }
27650   }
27651 
27652   if (NS_WARN_IF(NS_FAILED(rv))) {
27653     return rv;
27654   }
27655 
27656   MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
27657 
27658   return NS_OK;
27659 }
27660 
27661 void
GetResponse(RequestResponse & aResponse)27662 IndexGetRequestOp::GetResponse(RequestResponse& aResponse)
27663 {
27664   MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
27665 
27666   if (mGetAll) {
27667     aResponse = IndexGetAllResponse();
27668 
27669     if (!mResponse.IsEmpty()) {
27670       FallibleTArray<SerializedStructuredCloneReadInfo> fallibleCloneInfos;
27671       if (NS_WARN_IF(!fallibleCloneInfos.SetLength(mResponse.Length(),
27672                                                    fallible))) {
27673         aResponse = NS_ERROR_OUT_OF_MEMORY;
27674         return;
27675       }
27676 
27677       for (uint32_t count = mResponse.Length(), index = 0;
27678            index < count;
27679            index++) {
27680         StructuredCloneReadInfo& info = mResponse[index];
27681 
27682         SerializedStructuredCloneReadInfo& serializedInfo =
27683           fallibleCloneInfos[index];
27684 
27685         serializedInfo.data().data = Move(info.mData);
27686 
27687         FallibleTArray<SerializedStructuredCloneFile> serializedFiles;
27688         nsresult rv = SerializeStructuredCloneFiles(mBackgroundParent,
27689                                                     mDatabase,
27690                                                     info.mFiles,
27691                                                     /* aForPreprocess */ false,
27692                                                     serializedFiles);
27693         if (NS_WARN_IF(NS_FAILED(rv))) {
27694           aResponse = rv;
27695           return;
27696         }
27697 
27698         MOZ_ASSERT(serializedInfo.files().IsEmpty());
27699 
27700         serializedInfo.files().SwapElements(serializedFiles);
27701       }
27702 
27703       nsTArray<SerializedStructuredCloneReadInfo>& cloneInfos =
27704         aResponse.get_IndexGetAllResponse().cloneInfos();
27705 
27706       fallibleCloneInfos.SwapElements(cloneInfos);
27707     }
27708 
27709     return;
27710   }
27711 
27712   aResponse = IndexGetResponse();
27713 
27714   if (!mResponse.IsEmpty()) {
27715     StructuredCloneReadInfo& info = mResponse[0];
27716 
27717     SerializedStructuredCloneReadInfo& serializedInfo =
27718       aResponse.get_IndexGetResponse().cloneInfo();
27719 
27720     serializedInfo.data().data = Move(info.mData);
27721 
27722     FallibleTArray<SerializedStructuredCloneFile> serializedFiles;
27723     nsresult rv =
27724       SerializeStructuredCloneFiles(mBackgroundParent,
27725                                     mDatabase,
27726                                     info.mFiles,
27727                                     /* aForPreprocess */ false,
27728                                     serializedFiles);
27729     if (NS_WARN_IF(NS_FAILED(rv))) {
27730       aResponse = rv;
27731       return;
27732     }
27733 
27734     MOZ_ASSERT(serializedInfo.files().IsEmpty());
27735 
27736     serializedInfo.files().SwapElements(serializedFiles);
27737   }
27738 }
27739 
IndexGetKeyRequestOp(TransactionBase * aTransaction,const RequestParams & aParams,bool aGetAll)27740 IndexGetKeyRequestOp::IndexGetKeyRequestOp(TransactionBase* aTransaction,
27741                                            const RequestParams& aParams,
27742                                            bool aGetAll)
27743   : IndexRequestOpBase(aTransaction, aParams)
27744   , mOptionalKeyRange(aGetAll ?
27745                         aParams.get_IndexGetAllKeysParams().optionalKeyRange() :
27746                         OptionalKeyRange(aParams.get_IndexGetKeyParams()
27747                                                 .keyRange()))
27748   , mLimit(aGetAll ? aParams.get_IndexGetAllKeysParams().limit() : 1)
27749   , mGetAll(aGetAll)
27750 {
27751   MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetKeyParams ||
27752              aParams.type() == RequestParams::TIndexGetAllKeysParams);
27753   MOZ_ASSERT_IF(!aGetAll,
27754                 mOptionalKeyRange.type() ==
27755                   OptionalKeyRange::TSerializedKeyRange);
27756 }
27757 
27758 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)27759 IndexGetKeyRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27760 {
27761   MOZ_ASSERT(aConnection);
27762   aConnection->AssertIsOnConnectionThread();
27763   MOZ_ASSERT_IF(!mGetAll,
27764                 mOptionalKeyRange.type() ==
27765                   OptionalKeyRange::TSerializedKeyRange);
27766   MOZ_ASSERT_IF(!mGetAll, mLimit == 1);
27767 
27768   PROFILER_LABEL("IndexedDB",
27769                  "IndexGetKeyRequestOp::DoDatabaseWork",
27770                  js::ProfileEntry::Category::STORAGE);
27771 
27772   const bool hasKeyRange =
27773     mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
27774 
27775   nsCString indexTable;
27776   if (mMetadata->mCommonMetadata.unique()) {
27777     indexTable.AssignLiteral("unique_index_data ");
27778   }
27779   else {
27780     indexTable.AssignLiteral("index_data ");
27781   }
27782 
27783   nsAutoCString keyRangeClause;
27784   if (hasKeyRange) {
27785     GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
27786                                 NS_LITERAL_CSTRING("value"),
27787                                 keyRangeClause);
27788   }
27789 
27790   nsCString limitClause;
27791   if (mLimit) {
27792     limitClause.AssignLiteral(" LIMIT ");
27793     limitClause.AppendInt(mLimit);
27794   }
27795 
27796   nsCString query =
27797     NS_LITERAL_CSTRING("SELECT object_data_key "
27798                        "FROM ") +
27799     indexTable +
27800     NS_LITERAL_CSTRING("WHERE index_id = :index_id") +
27801     keyRangeClause +
27802     limitClause;
27803 
27804   DatabaseConnection::CachedStatement stmt;
27805   nsresult rv = aConnection->GetCachedStatement(query, &stmt);
27806   if (NS_WARN_IF(NS_FAILED(rv))) {
27807     return rv;
27808   }
27809 
27810   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
27811                              mMetadata->mCommonMetadata.id());
27812   if (NS_WARN_IF(NS_FAILED(rv))) {
27813     return rv;
27814   }
27815 
27816   if (hasKeyRange) {
27817     rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
27818                                  stmt);
27819     if (NS_WARN_IF(NS_FAILED(rv))) {
27820       return rv;
27821     }
27822   }
27823 
27824   bool hasResult;
27825   while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
27826     Key* key = mResponse.AppendElement(fallible);
27827     if (NS_WARN_IF(!key)) {
27828       return NS_ERROR_OUT_OF_MEMORY;
27829     }
27830 
27831     rv = key->SetFromStatement(stmt, 0);
27832     if (NS_WARN_IF(NS_FAILED(rv))) {
27833       return rv;
27834     }
27835   }
27836 
27837   if (NS_WARN_IF(NS_FAILED(rv))) {
27838     return rv;
27839   }
27840 
27841   MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
27842 
27843   return NS_OK;
27844 }
27845 
27846 void
GetResponse(RequestResponse & aResponse)27847 IndexGetKeyRequestOp::GetResponse(RequestResponse& aResponse)
27848 {
27849   MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
27850 
27851   if (mGetAll) {
27852     aResponse = IndexGetAllKeysResponse();
27853 
27854     if (!mResponse.IsEmpty()) {
27855       mResponse.SwapElements(aResponse.get_IndexGetAllKeysResponse().keys());
27856     }
27857 
27858     return;
27859   }
27860 
27861   aResponse = IndexGetKeyResponse();
27862 
27863   if (!mResponse.IsEmpty()) {
27864     aResponse.get_IndexGetKeyResponse().key() = Move(mResponse[0]);
27865   }
27866 }
27867 
27868 nsresult
DoDatabaseWork(DatabaseConnection * aConnection)27869 IndexCountRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
27870 {
27871   MOZ_ASSERT(aConnection);
27872   aConnection->AssertIsOnConnectionThread();
27873 
27874   PROFILER_LABEL("IndexedDB",
27875                  "IndexCountRequestOp::DoDatabaseWork",
27876                  js::ProfileEntry::Category::STORAGE);
27877 
27878   const bool hasKeyRange =
27879     mParams.optionalKeyRange().type() == OptionalKeyRange::TSerializedKeyRange;
27880 
27881   nsCString indexTable;
27882   if (mMetadata->mCommonMetadata.unique()) {
27883     indexTable.AssignLiteral("unique_index_data ");
27884   }
27885   else {
27886     indexTable.AssignLiteral("index_data ");
27887   }
27888 
27889   nsAutoCString keyRangeClause;
27890   if (hasKeyRange) {
27891     GetBindingClauseForKeyRange(
27892       mParams.optionalKeyRange().get_SerializedKeyRange(),
27893       NS_LITERAL_CSTRING("value"),
27894       keyRangeClause);
27895   }
27896 
27897   nsCString query =
27898     NS_LITERAL_CSTRING("SELECT count(*) "
27899                        "FROM ") +
27900     indexTable +
27901     NS_LITERAL_CSTRING("WHERE index_id = :index_id") +
27902     keyRangeClause;
27903 
27904   DatabaseConnection::CachedStatement stmt;
27905   nsresult rv = aConnection->GetCachedStatement(query, &stmt);
27906   if (NS_WARN_IF(NS_FAILED(rv))) {
27907     return rv;
27908   }
27909 
27910   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
27911                              mMetadata->mCommonMetadata.id());
27912   if (NS_WARN_IF(NS_FAILED(rv))) {
27913     return rv;
27914   }
27915 
27916   if (hasKeyRange) {
27917     rv = BindKeyRangeToStatement(
27918       mParams.optionalKeyRange().get_SerializedKeyRange(),
27919       stmt);
27920     if (NS_WARN_IF(NS_FAILED(rv))) {
27921       return rv;
27922     }
27923   }
27924 
27925   bool hasResult;
27926   rv = stmt->ExecuteStep(&hasResult);
27927   if (NS_WARN_IF(NS_FAILED(rv))) {
27928     return rv;
27929   }
27930 
27931   if (NS_WARN_IF(!hasResult)) {
27932     MOZ_ASSERT(false, "This should never be possible!");
27933     IDB_REPORT_INTERNAL_ERR();
27934     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
27935   }
27936 
27937   int64_t count = stmt->AsInt64(0);
27938   if (NS_WARN_IF(count < 0)) {
27939     MOZ_ASSERT(false, "This should never be possible!");
27940     IDB_REPORT_INTERNAL_ERR();
27941     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
27942   }
27943 
27944   mResponse.count() = count;
27945 
27946   return NS_OK;
27947 }
27948 
27949 bool
27950 Cursor::
SendFailureResult(nsresult aResultCode)27951 CursorOpBase::SendFailureResult(nsresult aResultCode)
27952 {
27953   AssertIsOnOwningThread();
27954   MOZ_ASSERT(NS_FAILED(aResultCode));
27955   MOZ_ASSERT(mCursor);
27956   MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this);
27957   MOZ_ASSERT(!mResponseSent);
27958 
27959   if (!IsActorDestroyed()) {
27960     mResponse = ClampResultCode(aResultCode);
27961 
27962     // This is an expected race when the transaction is invalidated after
27963     // data is retrieved from database. We clear the retrieved files to prevent
27964     // the assertion failure in SendResponseInternal when mResponse.type() is
27965     // CursorResponse::Tnsresult.
27966     if (Transaction()->IsInvalidated() && !mFiles.IsEmpty()) {
27967       mFiles.Clear();
27968     }
27969 
27970     mCursor->SendResponseInternal(mResponse, mFiles);
27971   }
27972 
27973 #ifdef DEBUG
27974   mResponseSent = true;
27975 #endif
27976   return false;
27977 }
27978 
27979 void
27980 Cursor::
Cleanup()27981 CursorOpBase::Cleanup()
27982 {
27983   AssertIsOnOwningThread();
27984   MOZ_ASSERT(mCursor);
27985   MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent);
27986 
27987   mCursor = nullptr;
27988 
27989 #ifdef DEBUG
27990   // A bit hacky but the CursorOp request is not generated in response to a
27991   // child request like most other database operations. Do this to make our
27992   // assertions happy.
27993   NoteActorDestroyed();
27994 #endif
27995 
27996   TransactionDatabaseOperationBase::Cleanup();
27997 }
27998 
27999 nsresult
28000 Cursor::
PopulateResponseFromStatement(DatabaseConnection::CachedStatement & aStmt,bool aInitializeResponse)28001 CursorOpBase::PopulateResponseFromStatement(
28002     DatabaseConnection::CachedStatement& aStmt,
28003     bool aInitializeResponse)
28004 {
28005   Transaction()->AssertIsOnConnectionThread();
28006   MOZ_ASSERT(mResponse.type() == CursorResponse::T__None);
28007   MOZ_ASSERT_IF(mFiles.IsEmpty(), aInitializeResponse);
28008 
28009   nsresult rv = mCursor->mKey.SetFromStatement(aStmt, 0);
28010   if (NS_WARN_IF(NS_FAILED(rv))) {
28011     return rv;
28012   }
28013 
28014   switch (mCursor->mType) {
28015     case OpenCursorParams::TObjectStoreOpenCursorParams: {
28016       StructuredCloneReadInfo cloneInfo;
28017       rv = GetStructuredCloneReadInfoFromStatement(aStmt,
28018                                                    2,
28019                                                    1,
28020                                                    mCursor->mFileManager,
28021                                                    &cloneInfo);
28022       if (NS_WARN_IF(NS_FAILED(rv))) {
28023         return rv;
28024       }
28025 
28026       if (cloneInfo.mHasPreprocessInfo) {
28027         IDB_WARNING("Preprocessing for cursors not yet implemented!");
28028         return NS_ERROR_NOT_IMPLEMENTED;
28029       }
28030 
28031       if (aInitializeResponse) {
28032         mResponse = nsTArray<ObjectStoreCursorResponse>();
28033       } else {
28034         MOZ_ASSERT(mResponse.type() ==
28035                      CursorResponse::TArrayOfObjectStoreCursorResponse);
28036       }
28037 
28038       auto& responses = mResponse.get_ArrayOfObjectStoreCursorResponse();
28039       auto& response = *responses.AppendElement();
28040       response.cloneInfo().data().data = Move(cloneInfo.mData);
28041       response.key() = mCursor->mKey;
28042 
28043       mFiles.AppendElement(Move(cloneInfo.mFiles));
28044       break;
28045     }
28046 
28047     case OpenCursorParams::TObjectStoreOpenKeyCursorParams: {
28048       MOZ_ASSERT(aInitializeResponse);
28049       mResponse = ObjectStoreKeyCursorResponse(mCursor->mKey);
28050       break;
28051     }
28052 
28053     case OpenCursorParams::TIndexOpenCursorParams: {
28054       rv = mCursor->mSortKey.SetFromStatement(aStmt, 1);
28055       if (NS_WARN_IF(NS_FAILED(rv))) {
28056         return rv;
28057       }
28058 
28059       rv = mCursor->mObjectKey.SetFromStatement(aStmt, 2);
28060       if (NS_WARN_IF(NS_FAILED(rv))) {
28061         return rv;
28062       }
28063 
28064       StructuredCloneReadInfo cloneInfo;
28065       rv = GetStructuredCloneReadInfoFromStatement(aStmt,
28066                                                    4,
28067                                                    3,
28068                                                    mCursor->mFileManager,
28069                                                    &cloneInfo);
28070       if (NS_WARN_IF(NS_FAILED(rv))) {
28071         return rv;
28072       }
28073 
28074       if (cloneInfo.mHasPreprocessInfo) {
28075         IDB_WARNING("Preprocessing for cursors not yet implemented!");
28076         return NS_ERROR_NOT_IMPLEMENTED;
28077       }
28078 
28079       MOZ_ASSERT(aInitializeResponse);
28080       mResponse = IndexCursorResponse();
28081 
28082       auto& response = mResponse.get_IndexCursorResponse();
28083       response.cloneInfo().data().data = Move(cloneInfo.mData);
28084       response.key() = mCursor->mKey;
28085       response.sortKey() = mCursor->mSortKey;
28086       response.objectKey() = mCursor->mObjectKey;
28087 
28088       mFiles.AppendElement(Move(cloneInfo.mFiles));
28089       break;
28090     }
28091 
28092     case OpenCursorParams::TIndexOpenKeyCursorParams: {
28093       rv = mCursor->mSortKey.SetFromStatement(aStmt, 1);
28094       if (NS_WARN_IF(NS_FAILED(rv))) {
28095         return rv;
28096       }
28097 
28098       rv = mCursor->mObjectKey.SetFromStatement(aStmt, 2);
28099       if (NS_WARN_IF(NS_FAILED(rv))) {
28100         return rv;
28101       }
28102 
28103       MOZ_ASSERT(aInitializeResponse);
28104       mResponse = IndexKeyCursorResponse(mCursor->mKey,
28105                                          mCursor->mSortKey,
28106                                          mCursor->mObjectKey);
28107       break;
28108     }
28109 
28110     default:
28111       MOZ_CRASH("Should never get here!");
28112   }
28113 
28114   return NS_OK;
28115 }
28116 
28117 void
28118 Cursor::
GetRangeKeyInfo(bool aLowerBound,Key * aKey,bool * aOpen)28119 OpenOp::GetRangeKeyInfo(bool aLowerBound, Key* aKey, bool* aOpen)
28120 {
28121   AssertIsOnConnectionThread();
28122   MOZ_ASSERT(aKey);
28123   MOZ_ASSERT(aKey->IsUnset());
28124   MOZ_ASSERT(aOpen);
28125 
28126   if (mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange) {
28127     const SerializedKeyRange& range =
28128       mOptionalKeyRange.get_SerializedKeyRange();
28129     if (range.isOnly()) {
28130       *aKey = range.lower();
28131       *aOpen = false;
28132 #ifdef ENABLE_INTL_API
28133       if (mCursor->IsLocaleAware()) {
28134         range.lower().ToLocaleBasedKey(*aKey, mCursor->mLocale);
28135       }
28136 #endif
28137     } else {
28138       *aKey = aLowerBound ? range.lower() : range.upper();
28139       *aOpen = aLowerBound ? range.lowerOpen() : range.upperOpen();
28140 #ifdef ENABLE_INTL_API
28141       if (mCursor->IsLocaleAware()) {
28142         if (aLowerBound) {
28143           range.lower().ToLocaleBasedKey(*aKey, mCursor->mLocale);
28144         } else {
28145           range.upper().ToLocaleBasedKey(*aKey, mCursor->mLocale);
28146         }
28147       }
28148 #endif
28149     }
28150   } else {
28151     *aOpen = false;
28152   }
28153 }
28154 
28155 nsresult
28156 Cursor::
DoObjectStoreDatabaseWork(DatabaseConnection * aConnection)28157 OpenOp::DoObjectStoreDatabaseWork(DatabaseConnection* aConnection)
28158 {
28159   MOZ_ASSERT(aConnection);
28160   aConnection->AssertIsOnConnectionThread();
28161   MOZ_ASSERT(mCursor);
28162   MOZ_ASSERT(mCursor->mType == OpenCursorParams::TObjectStoreOpenCursorParams);
28163   MOZ_ASSERT(mCursor->mObjectStoreId);
28164   MOZ_ASSERT(mCursor->mFileManager);
28165 
28166   PROFILER_LABEL("IndexedDB",
28167                  "Cursor::OpenOp::DoObjectStoreDatabaseWork",
28168                  js::ProfileEntry::Category::STORAGE);
28169 
28170   const bool usingKeyRange =
28171     mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
28172 
28173   NS_NAMED_LITERAL_CSTRING(keyString, "key");
28174   NS_NAMED_LITERAL_CSTRING(id, "id");
28175   NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT ");
28176 
28177   nsCString queryStart =
28178     NS_LITERAL_CSTRING("SELECT ") +
28179     keyString +
28180     NS_LITERAL_CSTRING(", file_ids, data "
28181                        "FROM object_data "
28182                        "WHERE object_store_id = :") +
28183     id;
28184 
28185   nsAutoCString keyRangeClause;
28186   if (usingKeyRange) {
28187     GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
28188                                 keyString,
28189                                 keyRangeClause);
28190   }
28191 
28192   nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyString;
28193   switch (mCursor->mDirection) {
28194     case IDBCursor::NEXT:
28195     case IDBCursor::NEXT_UNIQUE:
28196       directionClause.AppendLiteral(" ASC");
28197       break;
28198 
28199     case IDBCursor::PREV:
28200     case IDBCursor::PREV_UNIQUE:
28201       directionClause.AppendLiteral(" DESC");
28202       break;
28203 
28204     default:
28205       MOZ_CRASH("Should never get here!");
28206   }
28207 
28208   // Note: Changing the number or order of SELECT columns in the query will
28209   // require changes to CursorOpBase::PopulateResponseFromStatement.
28210   nsCString firstQuery =
28211     queryStart +
28212     keyRangeClause +
28213     directionClause +
28214     openLimit +
28215     NS_LITERAL_CSTRING("1");
28216 
28217   DatabaseConnection::CachedStatement stmt;
28218   nsresult rv = aConnection->GetCachedStatement(firstQuery, &stmt);
28219   if (NS_WARN_IF(NS_FAILED(rv))) {
28220     return rv;
28221   }
28222 
28223   rv = stmt->BindInt64ByName(id, mCursor->mObjectStoreId);
28224   if (NS_WARN_IF(NS_FAILED(rv))) {
28225     return rv;
28226   }
28227 
28228   if (usingKeyRange) {
28229     rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
28230                                  stmt);
28231     if (NS_WARN_IF(NS_FAILED(rv))) {
28232       return rv;
28233     }
28234   }
28235 
28236   bool hasResult;
28237   rv = stmt->ExecuteStep(&hasResult);
28238   if (NS_WARN_IF(NS_FAILED(rv))) {
28239     return rv;
28240   }
28241 
28242   if (!hasResult) {
28243     mResponse = void_t();
28244     return NS_OK;
28245   }
28246 
28247   rv = PopulateResponseFromStatement(stmt, true);
28248   if (NS_WARN_IF(NS_FAILED(rv))) {
28249     return rv;
28250   }
28251 
28252   // Now we need to make the query to get the next match.
28253   keyRangeClause.Truncate();
28254   nsAutoCString continueToKeyRangeClause;
28255 
28256   NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
28257   NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
28258 
28259   switch (mCursor->mDirection) {
28260     case IDBCursor::NEXT:
28261     case IDBCursor::NEXT_UNIQUE: {
28262       Key upper;
28263       bool open;
28264       GetRangeKeyInfo(false, &upper, &open);
28265       AppendConditionClause(keyString, currentKey, false, false,
28266                             keyRangeClause);
28267       AppendConditionClause(keyString, currentKey, false, true,
28268                             continueToKeyRangeClause);
28269       if (usingKeyRange && !upper.IsUnset()) {
28270         AppendConditionClause(keyString, rangeKey, true, !open, keyRangeClause);
28271         AppendConditionClause(keyString, rangeKey, true, !open,
28272                               continueToKeyRangeClause);
28273         mCursor->mRangeKey = upper;
28274       }
28275       break;
28276     }
28277 
28278     case IDBCursor::PREV:
28279     case IDBCursor::PREV_UNIQUE: {
28280       Key lower;
28281       bool open;
28282       GetRangeKeyInfo(true, &lower, &open);
28283       AppendConditionClause(keyString, currentKey, true, false, keyRangeClause);
28284       AppendConditionClause(keyString, currentKey, true, true,
28285                            continueToKeyRangeClause);
28286       if (usingKeyRange && !lower.IsUnset()) {
28287         AppendConditionClause(keyString, rangeKey, false, !open,
28288                               keyRangeClause);
28289         AppendConditionClause(keyString, rangeKey, false, !open,
28290                               continueToKeyRangeClause);
28291         mCursor->mRangeKey = lower;
28292       }
28293       break;
28294     }
28295 
28296     default:
28297       MOZ_CRASH("Should never get here!");
28298   }
28299 
28300   mCursor->mContinueQuery =
28301     queryStart +
28302     keyRangeClause +
28303     directionClause +
28304     openLimit;
28305 
28306   mCursor->mContinueToQuery =
28307     queryStart +
28308     continueToKeyRangeClause +
28309     directionClause +
28310     openLimit;
28311 
28312   return NS_OK;
28313 }
28314 
28315 nsresult
28316 Cursor::
DoObjectStoreKeyDatabaseWork(DatabaseConnection * aConnection)28317 OpenOp::DoObjectStoreKeyDatabaseWork(DatabaseConnection* aConnection)
28318 {
28319   MOZ_ASSERT(aConnection);
28320   aConnection->AssertIsOnConnectionThread();
28321   MOZ_ASSERT(mCursor);
28322   MOZ_ASSERT(mCursor->mType ==
28323                OpenCursorParams::TObjectStoreOpenKeyCursorParams);
28324   MOZ_ASSERT(mCursor->mObjectStoreId);
28325 
28326   PROFILER_LABEL("IndexedDB",
28327                  "Cursor::OpenOp::DoObjectStoreKeyDatabaseWork",
28328                  js::ProfileEntry::Category::STORAGE);
28329 
28330   const bool usingKeyRange =
28331     mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
28332 
28333   NS_NAMED_LITERAL_CSTRING(keyString, "key");
28334   NS_NAMED_LITERAL_CSTRING(id, "id");
28335   NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT ");
28336 
28337   nsCString queryStart =
28338     NS_LITERAL_CSTRING("SELECT ") +
28339     keyString +
28340     NS_LITERAL_CSTRING(" FROM object_data "
28341                        "WHERE object_store_id = :") +
28342     id;
28343 
28344   nsAutoCString keyRangeClause;
28345   if (usingKeyRange) {
28346     GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
28347                                 keyString,
28348                                 keyRangeClause);
28349   }
28350 
28351   nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyString;
28352   switch (mCursor->mDirection) {
28353     case IDBCursor::NEXT:
28354     case IDBCursor::NEXT_UNIQUE:
28355       directionClause.AppendLiteral(" ASC");
28356       break;
28357 
28358     case IDBCursor::PREV:
28359     case IDBCursor::PREV_UNIQUE:
28360       directionClause.AppendLiteral(" DESC");
28361       break;
28362 
28363     default:
28364       MOZ_CRASH("Should never get here!");
28365   }
28366 
28367   // Note: Changing the number or order of SELECT columns in the query will
28368   // require changes to CursorOpBase::PopulateResponseFromStatement.
28369   nsCString firstQuery =
28370     queryStart +
28371     keyRangeClause +
28372     directionClause +
28373     openLimit +
28374     NS_LITERAL_CSTRING("1");
28375 
28376   DatabaseConnection::CachedStatement stmt;
28377   nsresult rv = aConnection->GetCachedStatement(firstQuery, &stmt);
28378   if (NS_WARN_IF(NS_FAILED(rv))) {
28379     return rv;
28380   }
28381 
28382   rv = stmt->BindInt64ByName(id, mCursor->mObjectStoreId);
28383   if (NS_WARN_IF(NS_FAILED(rv))) {
28384     return rv;
28385   }
28386 
28387   if (usingKeyRange) {
28388     rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
28389                                  stmt);
28390     if (NS_WARN_IF(NS_FAILED(rv))) {
28391       return rv;
28392     }
28393   }
28394 
28395   bool hasResult;
28396   rv = stmt->ExecuteStep(&hasResult);
28397   if (NS_WARN_IF(NS_FAILED(rv))) {
28398     return rv;
28399   }
28400 
28401   if (!hasResult) {
28402     mResponse = void_t();
28403     return NS_OK;
28404   }
28405 
28406   rv = PopulateResponseFromStatement(stmt, true);
28407   if (NS_WARN_IF(NS_FAILED(rv))) {
28408     return rv;
28409   }
28410 
28411   // Now we need to make the query to get the next match.
28412   keyRangeClause.Truncate();
28413   nsAutoCString continueToKeyRangeClause;
28414 
28415   NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
28416   NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
28417 
28418   switch (mCursor->mDirection) {
28419     case IDBCursor::NEXT:
28420     case IDBCursor::NEXT_UNIQUE: {
28421       Key upper;
28422       bool open;
28423       GetRangeKeyInfo(false, &upper, &open);
28424       AppendConditionClause(keyString, currentKey, false, false,
28425                             keyRangeClause);
28426       AppendConditionClause(keyString, currentKey, false, true,
28427                             continueToKeyRangeClause);
28428       if (usingKeyRange && !upper.IsUnset()) {
28429         AppendConditionClause(keyString, rangeKey, true, !open, keyRangeClause);
28430         AppendConditionClause(keyString, rangeKey, true, !open,
28431                               continueToKeyRangeClause);
28432         mCursor->mRangeKey = upper;
28433       }
28434       break;
28435     }
28436 
28437     case IDBCursor::PREV:
28438     case IDBCursor::PREV_UNIQUE: {
28439       Key lower;
28440       bool open;
28441       GetRangeKeyInfo(true, &lower, &open);
28442       AppendConditionClause(keyString, currentKey, true, false, keyRangeClause);
28443       AppendConditionClause(keyString, currentKey, true, true,
28444                             continueToKeyRangeClause);
28445       if (usingKeyRange && !lower.IsUnset()) {
28446         AppendConditionClause(keyString, rangeKey, false, !open,
28447                               keyRangeClause);
28448         AppendConditionClause(keyString, rangeKey, false, !open,
28449                               continueToKeyRangeClause);
28450         mCursor->mRangeKey = lower;
28451       }
28452       break;
28453     }
28454 
28455     default:
28456       MOZ_CRASH("Should never get here!");
28457   }
28458 
28459   mCursor->mContinueQuery =
28460     queryStart +
28461     keyRangeClause +
28462     directionClause +
28463     openLimit;
28464   mCursor->mContinueToQuery =
28465     queryStart +
28466     continueToKeyRangeClause +
28467     directionClause +
28468     openLimit;
28469 
28470   return NS_OK;
28471 }
28472 
28473 nsresult
28474 Cursor::
DoIndexDatabaseWork(DatabaseConnection * aConnection)28475 OpenOp::DoIndexDatabaseWork(DatabaseConnection* aConnection)
28476 {
28477   MOZ_ASSERT(aConnection);
28478   aConnection->AssertIsOnConnectionThread();
28479   MOZ_ASSERT(mCursor);
28480   MOZ_ASSERT(mCursor->mType == OpenCursorParams::TIndexOpenCursorParams);
28481   MOZ_ASSERT(mCursor->mObjectStoreId);
28482   MOZ_ASSERT(mCursor->mIndexId);
28483 
28484   PROFILER_LABEL("IndexedDB",
28485                  "Cursor::OpenOp::DoIndexDatabaseWork",
28486                  js::ProfileEntry::Category::STORAGE);
28487 
28488   const bool usingKeyRange =
28489     mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
28490 
28491   nsCString indexTable = mCursor->mUniqueIndex ?
28492     NS_LITERAL_CSTRING("unique_index_data") :
28493     NS_LITERAL_CSTRING("index_data");
28494 
28495   NS_NAMED_LITERAL_CSTRING(sortColumn, "sort_column");
28496   NS_NAMED_LITERAL_CSTRING(id, "id");
28497   NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT ");
28498 
28499   nsAutoCString sortColumnAlias;
28500   if (mCursor->IsLocaleAware()) {
28501     sortColumnAlias = "SELECT index_table.value, "
28502                              "index_table.value_locale as sort_column, ";
28503   } else {
28504     sortColumnAlias = "SELECT index_table.value as sort_column, "
28505                              "index_table.value_locale, ";
28506   }
28507 
28508   nsAutoCString queryStart =
28509     sortColumnAlias +
28510     NS_LITERAL_CSTRING(       "index_table.object_data_key, "
28511                               "object_data.file_ids, "
28512                               "object_data.data "
28513                        "FROM ") +
28514     indexTable +
28515     NS_LITERAL_CSTRING(" AS index_table "
28516                        "JOIN object_data "
28517                        "ON index_table.object_store_id = "
28518                          "object_data.object_store_id "
28519                        "AND index_table.object_data_key = "
28520                          "object_data.key "
28521                        "WHERE index_table.index_id = :") +
28522     id;
28523 
28524   nsAutoCString keyRangeClause;
28525   if (usingKeyRange) {
28526     GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
28527                                 sortColumn,
28528                                 keyRangeClause);
28529   }
28530 
28531   nsAutoCString directionClause =
28532     NS_LITERAL_CSTRING(" ORDER BY ") +
28533     sortColumn;
28534 
28535   switch (mCursor->mDirection) {
28536     case IDBCursor::NEXT:
28537     case IDBCursor::NEXT_UNIQUE:
28538       directionClause.AppendLiteral(" ASC, index_table.object_data_key ASC");
28539       break;
28540 
28541     case IDBCursor::PREV:
28542       directionClause.AppendLiteral(" DESC, index_table.object_data_key DESC");
28543       break;
28544 
28545     case IDBCursor::PREV_UNIQUE:
28546       directionClause.AppendLiteral(" DESC, index_table.object_data_key ASC");
28547       break;
28548 
28549     default:
28550       MOZ_CRASH("Should never get here!");
28551   }
28552 
28553   // Note: Changing the number or order of SELECT columns in the query will
28554   // require changes to CursorOpBase::PopulateResponseFromStatement.
28555   nsCString firstQuery =
28556     queryStart +
28557     keyRangeClause +
28558     directionClause +
28559     openLimit +
28560     NS_LITERAL_CSTRING("1");
28561 
28562   DatabaseConnection::CachedStatement stmt;
28563   nsresult rv = aConnection->GetCachedStatement(firstQuery, &stmt);
28564   if (NS_WARN_IF(NS_FAILED(rv))) {
28565     return rv;
28566   }
28567 
28568   rv = stmt->BindInt64ByName(id, mCursor->mIndexId);
28569   if (NS_WARN_IF(NS_FAILED(rv))) {
28570     return rv;
28571   }
28572 
28573   if (usingKeyRange) {
28574     if (mCursor->IsLocaleAware()) {
28575       rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
28576                                    stmt,
28577                                    mCursor->mLocale);
28578     } else {
28579       rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
28580                                    stmt);
28581     }
28582     if (NS_WARN_IF(NS_FAILED(rv))) {
28583       return rv;
28584     }
28585   }
28586 
28587   bool hasResult;
28588   rv = stmt->ExecuteStep(&hasResult);
28589   if (NS_WARN_IF(NS_FAILED(rv))) {
28590     return rv;
28591   }
28592 
28593   if (!hasResult) {
28594     mResponse = void_t();
28595     return NS_OK;
28596   }
28597 
28598   rv = PopulateResponseFromStatement(stmt, true);
28599   if (NS_WARN_IF(NS_FAILED(rv))) {
28600     return rv;
28601   }
28602 
28603   // Now we need to make the query to get the next match.
28604   NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
28605 
28606   switch (mCursor->mDirection) {
28607     case IDBCursor::NEXT: {
28608       Key upper;
28609       bool open;
28610       GetRangeKeyInfo(false, &upper, &open);
28611       if (usingKeyRange && !upper.IsUnset()) {
28612         AppendConditionClause(sortColumn, rangeKey, true, !open, queryStart);
28613         mCursor->mRangeKey = upper;
28614       }
28615       mCursor->mContinueQuery =
28616         queryStart +
28617         NS_LITERAL_CSTRING(" AND sort_column >= :current_key "
28618                             "AND ( sort_column > :current_key OR "
28619                                   "index_table.object_data_key > :object_key ) "
28620                           ) +
28621         directionClause +
28622         openLimit;
28623       mCursor->mContinueToQuery =
28624         queryStart +
28625         NS_LITERAL_CSTRING(" AND sort_column >= :current_key") +
28626         directionClause +
28627         openLimit;
28628       mCursor->mContinuePrimaryKeyQuery =
28629         queryStart +
28630         NS_LITERAL_CSTRING(" AND sort_column >= :current_key "
28631                             "AND index_table.object_data_key >= :object_key "
28632                           ) +
28633         directionClause +
28634         openLimit;
28635       break;
28636     }
28637 
28638     case IDBCursor::NEXT_UNIQUE: {
28639       Key upper;
28640       bool open;
28641       GetRangeKeyInfo(false, &upper, &open);
28642       if (usingKeyRange && !upper.IsUnset()) {
28643         AppendConditionClause(sortColumn, rangeKey, true, !open, queryStart);
28644         mCursor->mRangeKey = upper;
28645       }
28646       mCursor->mContinueQuery =
28647         queryStart +
28648         NS_LITERAL_CSTRING(" AND sort_column > :current_key") +
28649         directionClause +
28650         openLimit;
28651       mCursor->mContinueToQuery =
28652         queryStart +
28653         NS_LITERAL_CSTRING(" AND sort_column >= :current_key") +
28654         directionClause +
28655         openLimit;
28656       break;
28657     }
28658 
28659     case IDBCursor::PREV: {
28660       Key lower;
28661       bool open;
28662       GetRangeKeyInfo(true, &lower, &open);
28663       if (usingKeyRange && !lower.IsUnset()) {
28664         AppendConditionClause(sortColumn, rangeKey, false, !open, queryStart);
28665         mCursor->mRangeKey = lower;
28666       }
28667       mCursor->mContinueQuery =
28668         queryStart +
28669         NS_LITERAL_CSTRING(" AND sort_column <= :current_key "
28670                             "AND ( sort_column < :current_key OR "
28671                                   "index_table.object_data_key < :object_key ) "
28672                           ) +
28673         directionClause +
28674         openLimit;
28675       mCursor->mContinueToQuery =
28676         queryStart +
28677         NS_LITERAL_CSTRING(" AND sort_column <= :current_key") +
28678         directionClause +
28679         openLimit;
28680       mCursor->mContinuePrimaryKeyQuery =
28681         queryStart +
28682         NS_LITERAL_CSTRING(" AND sort_column <= :current_key "
28683                             "AND index_table.object_data_key <= :object_key "
28684                           ) +
28685         directionClause +
28686         openLimit;
28687       break;
28688     }
28689 
28690     case IDBCursor::PREV_UNIQUE: {
28691       Key lower;
28692       bool open;
28693       GetRangeKeyInfo(true, &lower, &open);
28694       if (usingKeyRange && !lower.IsUnset()) {
28695         AppendConditionClause(sortColumn, rangeKey, false, !open, queryStart);
28696         mCursor->mRangeKey = lower;
28697       }
28698       mCursor->mContinueQuery =
28699         queryStart +
28700         NS_LITERAL_CSTRING(" AND sort_column < :current_key") +
28701         directionClause +
28702         openLimit;
28703       mCursor->mContinueToQuery =
28704         queryStart +
28705         NS_LITERAL_CSTRING(" AND sort_column <= :current_key") +
28706         directionClause +
28707         openLimit;
28708       break;
28709     }
28710 
28711     default:
28712       MOZ_CRASH("Should never get here!");
28713   }
28714 
28715   return NS_OK;
28716 }
28717 
28718 nsresult
28719 Cursor::
DoIndexKeyDatabaseWork(DatabaseConnection * aConnection)28720 OpenOp::DoIndexKeyDatabaseWork(DatabaseConnection* aConnection)
28721 {
28722   MOZ_ASSERT(aConnection);
28723   aConnection->AssertIsOnConnectionThread();
28724   MOZ_ASSERT(mCursor);
28725   MOZ_ASSERT(mCursor->mType == OpenCursorParams::TIndexOpenKeyCursorParams);
28726   MOZ_ASSERT(mCursor->mObjectStoreId);
28727   MOZ_ASSERT(mCursor->mIndexId);
28728 
28729   PROFILER_LABEL("IndexedDB",
28730                  "Cursor::OpenOp::DoIndexKeyDatabaseWork",
28731                  js::ProfileEntry::Category::STORAGE);
28732 
28733   const bool usingKeyRange =
28734     mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
28735 
28736   nsCString table = mCursor->mUniqueIndex ?
28737     NS_LITERAL_CSTRING("unique_index_data") :
28738     NS_LITERAL_CSTRING("index_data");
28739 
28740   NS_NAMED_LITERAL_CSTRING(sortColumn, "sort_column");
28741   NS_NAMED_LITERAL_CSTRING(id, "id");
28742   NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT ");
28743 
28744   nsAutoCString sortColumnAlias;
28745   if (mCursor->IsLocaleAware()) {
28746     sortColumnAlias = "SELECT value, "
28747                              "value_locale as sort_column, ";
28748   } else {
28749     sortColumnAlias = "SELECT value as sort_column, "
28750                              "value_locale, ";
28751   }
28752 
28753   nsAutoCString queryStart =
28754     sortColumnAlias +
28755     NS_LITERAL_CSTRING(      "object_data_key "
28756                        " FROM ") +
28757     table +
28758     NS_LITERAL_CSTRING(" WHERE index_id = :") +
28759     id;
28760 
28761   nsAutoCString keyRangeClause;
28762   if (usingKeyRange) {
28763     GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
28764                                 sortColumn,
28765                                 keyRangeClause);
28766   }
28767 
28768   nsAutoCString directionClause =
28769     NS_LITERAL_CSTRING(" ORDER BY ") +
28770     sortColumn;
28771 
28772   switch (mCursor->mDirection) {
28773     case IDBCursor::NEXT:
28774     case IDBCursor::NEXT_UNIQUE:
28775       directionClause.AppendLiteral(" ASC, object_data_key ASC");
28776       break;
28777 
28778     case IDBCursor::PREV:
28779       directionClause.AppendLiteral(" DESC, object_data_key DESC");
28780       break;
28781 
28782     case IDBCursor::PREV_UNIQUE:
28783       directionClause.AppendLiteral(" DESC, object_data_key ASC");
28784       break;
28785 
28786     default:
28787       MOZ_CRASH("Should never get here!");
28788   }
28789 
28790   // Note: Changing the number or order of SELECT columns in the query will
28791   // require changes to CursorOpBase::PopulateResponseFromStatement.
28792   nsCString firstQuery =
28793     queryStart +
28794     keyRangeClause +
28795     directionClause +
28796     openLimit +
28797     NS_LITERAL_CSTRING("1");
28798 
28799   DatabaseConnection::CachedStatement stmt;
28800   nsresult rv = aConnection->GetCachedStatement(firstQuery, &stmt);
28801   if (NS_WARN_IF(NS_FAILED(rv))) {
28802     return rv;
28803   }
28804 
28805   rv = stmt->BindInt64ByName(id, mCursor->mIndexId);
28806   if (NS_WARN_IF(NS_FAILED(rv))) {
28807     return rv;
28808   }
28809 
28810   if (usingKeyRange) {
28811     if (mCursor->IsLocaleAware()) {
28812       rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
28813                                    stmt,
28814                                    mCursor->mLocale);
28815     } else {
28816       rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
28817                                    stmt);
28818     }
28819     if (NS_WARN_IF(NS_FAILED(rv))) {
28820       return rv;
28821     }
28822   }
28823 
28824   bool hasResult;
28825   rv = stmt->ExecuteStep(&hasResult);
28826   if (NS_WARN_IF(NS_FAILED(rv))) {
28827     return rv;
28828   }
28829 
28830   if (!hasResult) {
28831     mResponse = void_t();
28832     return NS_OK;
28833   }
28834 
28835   rv = PopulateResponseFromStatement(stmt, true);
28836   if (NS_WARN_IF(NS_FAILED(rv))) {
28837     return rv;
28838   }
28839 
28840   // Now we need to make the query to get the next match.
28841   NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
28842 
28843   switch (mCursor->mDirection) {
28844     case IDBCursor::NEXT: {
28845       Key upper;
28846       bool open;
28847       GetRangeKeyInfo(false, &upper, &open);
28848       if (usingKeyRange && !upper.IsUnset()) {
28849         AppendConditionClause(sortColumn, rangeKey, true, !open, queryStart);
28850         mCursor->mRangeKey = upper;
28851       }
28852       mCursor->mContinueQuery =
28853         queryStart +
28854         NS_LITERAL_CSTRING(" AND sort_column >= :current_key "
28855                             "AND ( sort_column > :current_key OR "
28856                                   "object_data_key > :object_key )") +
28857         directionClause +
28858         openLimit;
28859       mCursor->mContinueToQuery =
28860         queryStart +
28861         NS_LITERAL_CSTRING(" AND sort_column >= :current_key ") +
28862         directionClause +
28863         openLimit;
28864       mCursor->mContinuePrimaryKeyQuery =
28865         queryStart +
28866         NS_LITERAL_CSTRING(" AND sort_column >= :current_key "
28867                             "AND object_data_key >= :object_key "
28868                           ) +
28869         directionClause +
28870         openLimit;
28871       break;
28872     }
28873 
28874     case IDBCursor::NEXT_UNIQUE: {
28875       Key upper;
28876       bool open;
28877       GetRangeKeyInfo(false, &upper, &open);
28878       if (usingKeyRange && !upper.IsUnset()) {
28879         AppendConditionClause(sortColumn, rangeKey, true, !open, queryStart);
28880         mCursor->mRangeKey = upper;
28881       }
28882       mCursor->mContinueQuery =
28883         queryStart +
28884         NS_LITERAL_CSTRING(" AND sort_column > :current_key") +
28885         directionClause +
28886         openLimit;
28887       mCursor->mContinueToQuery =
28888         queryStart +
28889         NS_LITERAL_CSTRING(" AND sort_column >= :current_key") +
28890         directionClause +
28891         openLimit;
28892       break;
28893     }
28894 
28895     case IDBCursor::PREV: {
28896       Key lower;
28897       bool open;
28898       GetRangeKeyInfo(true, &lower, &open);
28899       if (usingKeyRange && !lower.IsUnset()) {
28900         AppendConditionClause(sortColumn, rangeKey, false, !open, queryStart);
28901         mCursor->mRangeKey = lower;
28902       }
28903 
28904       mCursor->mContinueQuery =
28905         queryStart +
28906         NS_LITERAL_CSTRING(" AND sort_column <= :current_key "
28907                             "AND ( sort_column < :current_key OR "
28908                                   "object_data_key < :object_key )") +
28909         directionClause +
28910         openLimit;
28911       mCursor->mContinueToQuery =
28912         queryStart +
28913         NS_LITERAL_CSTRING(" AND sort_column <= :current_key ") +
28914         directionClause +
28915         openLimit;
28916       mCursor->mContinuePrimaryKeyQuery =
28917         queryStart +
28918         NS_LITERAL_CSTRING(" AND sort_column <= :current_key "
28919                             "AND object_data_key <= :object_key "
28920                           ) +
28921         directionClause +
28922         openLimit;
28923       break;
28924     }
28925 
28926     case IDBCursor::PREV_UNIQUE: {
28927       Key lower;
28928       bool open;
28929       GetRangeKeyInfo(true, &lower, &open);
28930       if (usingKeyRange && !lower.IsUnset()) {
28931         AppendConditionClause(sortColumn, rangeKey, false, !open, queryStart);
28932         mCursor->mRangeKey = lower;
28933       }
28934       mCursor->mContinueQuery =
28935         queryStart +
28936         NS_LITERAL_CSTRING(" AND sort_column < :current_key") +
28937         directionClause +
28938         openLimit;
28939       mCursor->mContinueToQuery =
28940         queryStart +
28941         NS_LITERAL_CSTRING(" AND sort_column <= :current_key") +
28942         directionClause +
28943         openLimit;
28944       break;
28945     }
28946 
28947     default:
28948       MOZ_CRASH("Should never get here!");
28949   }
28950 
28951   return NS_OK;
28952 }
28953 
28954 nsresult
28955 Cursor::
DoDatabaseWork(DatabaseConnection * aConnection)28956 OpenOp::DoDatabaseWork(DatabaseConnection* aConnection)
28957 {
28958   MOZ_ASSERT(aConnection);
28959   aConnection->AssertIsOnConnectionThread();
28960   MOZ_ASSERT(mCursor);
28961   MOZ_ASSERT(mCursor->mContinueQuery.IsEmpty());
28962   MOZ_ASSERT(mCursor->mContinueToQuery.IsEmpty());
28963   MOZ_ASSERT(mCursor->mContinuePrimaryKeyQuery.IsEmpty());
28964   MOZ_ASSERT(mCursor->mKey.IsUnset());
28965   MOZ_ASSERT(mCursor->mRangeKey.IsUnset());
28966 
28967   PROFILER_LABEL("IndexedDB",
28968                  "Cursor::OpenOp::DoDatabaseWork",
28969                  js::ProfileEntry::Category::STORAGE);
28970 
28971   nsresult rv;
28972 
28973   switch (mCursor->mType) {
28974     case OpenCursorParams::TObjectStoreOpenCursorParams:
28975       rv = DoObjectStoreDatabaseWork(aConnection);
28976       break;
28977 
28978     case OpenCursorParams::TObjectStoreOpenKeyCursorParams:
28979       rv = DoObjectStoreKeyDatabaseWork(aConnection);
28980       break;
28981 
28982     case OpenCursorParams::TIndexOpenCursorParams:
28983       rv = DoIndexDatabaseWork(aConnection);
28984       break;
28985 
28986     case OpenCursorParams::TIndexOpenKeyCursorParams:
28987       rv = DoIndexKeyDatabaseWork(aConnection);
28988       break;
28989 
28990     default:
28991       MOZ_CRASH("Should never get here!");
28992   }
28993 
28994   if (NS_WARN_IF(NS_FAILED(rv))) {
28995     return rv;
28996   }
28997 
28998   return NS_OK;
28999 }
29000 
29001 nsresult
29002 Cursor::
SendSuccessResult()29003 OpenOp::SendSuccessResult()
29004 {
29005   AssertIsOnOwningThread();
29006   MOZ_ASSERT(mCursor);
29007   MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this);
29008   MOZ_ASSERT(mResponse.type() != CursorResponse::T__None);
29009   MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
29010                 mCursor->mKey.IsUnset());
29011   MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
29012                 mCursor->mSortKey.IsUnset());
29013   MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
29014                 mCursor->mRangeKey.IsUnset());
29015   MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
29016                 mCursor->mObjectKey.IsUnset());
29017 
29018   if (IsActorDestroyed()) {
29019     return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
29020   }
29021 
29022   mCursor->SendResponseInternal(mResponse, mFiles);
29023 
29024 #ifdef DEBUG
29025   mResponseSent = true;
29026 #endif
29027   return NS_OK;
29028 }
29029 
29030 nsresult
29031 Cursor::
DoDatabaseWork(DatabaseConnection * aConnection)29032 ContinueOp::DoDatabaseWork(DatabaseConnection* aConnection)
29033 {
29034   MOZ_ASSERT(aConnection);
29035   aConnection->AssertIsOnConnectionThread();
29036   MOZ_ASSERT(mCursor);
29037   MOZ_ASSERT(mCursor->mObjectStoreId);
29038   MOZ_ASSERT(!mCursor->mContinueQuery.IsEmpty());
29039   MOZ_ASSERT(!mCursor->mContinueToQuery.IsEmpty());
29040   MOZ_ASSERT(!mCursor->mKey.IsUnset());
29041 
29042   const bool isIndex =
29043     mCursor->mType == OpenCursorParams::TIndexOpenCursorParams ||
29044     mCursor->mType == OpenCursorParams::TIndexOpenKeyCursorParams;
29045 
29046   MOZ_ASSERT_IF(isIndex &&
29047                 (mCursor->mDirection == IDBCursor::NEXT ||
29048                  mCursor->mDirection == IDBCursor::PREV),
29049                 !mCursor->mContinuePrimaryKeyQuery.IsEmpty());
29050   MOZ_ASSERT_IF(isIndex, mCursor->mIndexId);
29051   MOZ_ASSERT_IF(isIndex, !mCursor->mObjectKey.IsUnset());
29052 
29053   PROFILER_LABEL("IndexedDB",
29054                  "Cursor::ContinueOp::DoDatabaseWork",
29055                  js::ProfileEntry::Category::STORAGE);
29056 
29057   // We need to pick a query based on whether or not a key was passed to the
29058   // continue function. If not we'll grab the the next item in the database that
29059   // is greater than (or less than, if we're running a PREV cursor) the current
29060   // key. If a key was passed we'll grab the next item in the database that is
29061   // greater than (or less than, if we're running a PREV cursor) or equal to the
29062   // key that was specified.
29063 
29064   // Note: Changing the number or order of SELECT columns in the query will
29065   // require changes to CursorOpBase::PopulateResponseFromStatement.
29066   bool hasContinueKey = false;
29067   bool hasContinuePrimaryKey = false;
29068   uint32_t advanceCount = 1;
29069   Key& currentKey = mCursor->IsLocaleAware() ? mCursor->mSortKey : mCursor->mKey;
29070 
29071   switch (mParams.type()) {
29072     case CursorRequestParams::TContinueParams:
29073       if (!mParams.get_ContinueParams().key().IsUnset()) {
29074         hasContinueKey = true;
29075         currentKey = mParams.get_ContinueParams().key();
29076       }
29077       break;
29078     case CursorRequestParams::TContinuePrimaryKeyParams:
29079       MOZ_ASSERT(!mParams.get_ContinuePrimaryKeyParams().key().IsUnset());
29080       MOZ_ASSERT(!mParams.get_ContinuePrimaryKeyParams().primaryKey().IsUnset());
29081       MOZ_ASSERT(mCursor->mDirection == IDBCursor::NEXT ||
29082                  mCursor->mDirection == IDBCursor::PREV);
29083       hasContinueKey = true;
29084       hasContinuePrimaryKey = true;
29085       currentKey = mParams.get_ContinuePrimaryKeyParams().key();
29086       break;
29087     case CursorRequestParams::TAdvanceParams:
29088       advanceCount = mParams.get_AdvanceParams().count();
29089       break;
29090     default:
29091       MOZ_CRASH("Should never get here!");
29092   }
29093 
29094   const nsCString& continueQuery =
29095     hasContinuePrimaryKey ? mCursor->mContinuePrimaryKeyQuery :
29096     hasContinueKey ? mCursor->mContinueToQuery : mCursor->mContinueQuery;
29097 
29098   MOZ_ASSERT(advanceCount > 0);
29099   nsAutoCString countString;
29100   countString.AppendInt(advanceCount);
29101 
29102   nsCString query = continueQuery + countString;
29103 
29104   NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key");
29105   NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
29106   NS_NAMED_LITERAL_CSTRING(objectKeyName, "object_key");
29107 
29108   const bool usingRangeKey = !mCursor->mRangeKey.IsUnset();
29109 
29110   DatabaseConnection::CachedStatement stmt;
29111   nsresult rv = aConnection->GetCachedStatement(query, &stmt);
29112   if (NS_WARN_IF(NS_FAILED(rv))) {
29113     return rv;
29114   }
29115 
29116   int64_t id = isIndex ? mCursor->mIndexId : mCursor->mObjectStoreId;
29117 
29118   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), id);
29119   if (NS_WARN_IF(NS_FAILED(rv))) {
29120     return rv;
29121   }
29122 
29123   // Bind current key.
29124   rv = currentKey.BindToStatement(stmt, currentKeyName);
29125   if (NS_WARN_IF(NS_FAILED(rv))) {
29126     return rv;
29127   }
29128 
29129   // Bind range key if it is specified.
29130   if (usingRangeKey) {
29131     rv = mCursor->mRangeKey.BindToStatement(stmt, rangeKeyName);
29132     if (NS_WARN_IF(NS_FAILED(rv))) {
29133       return rv;
29134     }
29135   }
29136 
29137   // Bind object key if duplicates are allowed and we're not continuing to a
29138   // specific key.
29139   if (isIndex &&
29140       !hasContinueKey &&
29141       (mCursor->mDirection == IDBCursor::NEXT ||
29142        mCursor->mDirection == IDBCursor::PREV)) {
29143     rv = mCursor->mObjectKey.BindToStatement(stmt, objectKeyName);
29144     if (NS_WARN_IF(NS_FAILED(rv))) {
29145       return rv;
29146     }
29147   }
29148 
29149   // Bind object key if primaryKey is specified.
29150   if (hasContinuePrimaryKey) {
29151     rv = mParams.get_ContinuePrimaryKeyParams().primaryKey()
29152       .BindToStatement(stmt, objectKeyName);
29153     if (NS_WARN_IF(NS_FAILED(rv))) {
29154       return rv;
29155     }
29156   }
29157 
29158 
29159   bool hasResult;
29160   for (uint32_t index = 0; index < advanceCount; index++) {
29161     rv = stmt->ExecuteStep(&hasResult);
29162     if (NS_WARN_IF(NS_FAILED(rv))) {
29163       return rv;
29164     }
29165 
29166     if (!hasResult) {
29167       mCursor->mKey.Unset();
29168       mCursor->mSortKey.Unset();
29169       mCursor->mRangeKey.Unset();
29170       mCursor->mObjectKey.Unset();
29171       mResponse = void_t();
29172       return NS_OK;
29173     }
29174   }
29175 
29176   rv = PopulateResponseFromStatement(stmt, true);
29177   if (NS_WARN_IF(NS_FAILED(rv))) {
29178     return rv;
29179   }
29180 
29181   return NS_OK;
29182 }
29183 
29184 nsresult
29185 Cursor::
SendSuccessResult()29186 ContinueOp::SendSuccessResult()
29187 {
29188   AssertIsOnOwningThread();
29189   MOZ_ASSERT(mCursor);
29190   MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this);
29191   MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
29192                 mCursor->mKey.IsUnset());
29193   MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
29194                 mCursor->mRangeKey.IsUnset());
29195   MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t,
29196                 mCursor->mObjectKey.IsUnset());
29197 
29198   if (IsActorDestroyed()) {
29199     return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
29200   }
29201 
29202   mCursor->SendResponseInternal(mResponse, mFiles);
29203 
29204 #ifdef DEBUG
29205   mResponseSent = true;
29206 #endif
29207   return NS_OK;
29208 }
29209 
Utils()29210 Utils::Utils()
29211 #ifdef DEBUG
29212   : mActorDestroyed(false)
29213 #endif
29214 {
29215   AssertIsOnBackgroundThread();
29216 }
29217 
~Utils()29218 Utils::~Utils()
29219 {
29220   MOZ_ASSERT(mActorDestroyed);
29221 }
29222 
29223 void
ActorDestroy(ActorDestroyReason aWhy)29224 Utils::ActorDestroy(ActorDestroyReason aWhy)
29225 {
29226   AssertIsOnBackgroundThread();
29227   MOZ_ASSERT(!mActorDestroyed);
29228 
29229 #ifdef DEBUG
29230   mActorDestroyed = true;
29231 #endif
29232 }
29233 
29234 bool
RecvDeleteMe()29235 Utils::RecvDeleteMe()
29236 {
29237   AssertIsOnBackgroundThread();
29238   MOZ_ASSERT(!mActorDestroyed);
29239 
29240   return PBackgroundIndexedDBUtilsParent::Send__delete__(this);
29241 }
29242 
29243 bool
RecvGetFileReferences(const PersistenceType & aPersistenceType,const nsCString & aOrigin,const nsString & aDatabaseName,const int64_t & aFileId,int32_t * aRefCnt,int32_t * aDBRefCnt,int32_t * aSliceRefCnt,bool * aResult)29244 Utils::RecvGetFileReferences(const PersistenceType& aPersistenceType,
29245                              const nsCString& aOrigin,
29246                              const nsString& aDatabaseName,
29247                              const int64_t& aFileId,
29248                              int32_t* aRefCnt,
29249                              int32_t* aDBRefCnt,
29250                              int32_t* aSliceRefCnt,
29251                              bool* aResult)
29252 {
29253   AssertIsOnBackgroundThread();
29254   MOZ_ASSERT(aRefCnt);
29255   MOZ_ASSERT(aDBRefCnt);
29256   MOZ_ASSERT(aSliceRefCnt);
29257   MOZ_ASSERT(aResult);
29258   MOZ_ASSERT(!mActorDestroyed);
29259 
29260   if (NS_WARN_IF(!IndexedDatabaseManager::Get() ||
29261                  !QuotaManager::Get())) {
29262     ASSERT_UNLESS_FUZZING();
29263     return false;
29264   }
29265 
29266   if (NS_WARN_IF(!IndexedDatabaseManager::InTestingMode())) {
29267     ASSERT_UNLESS_FUZZING();
29268     return false;
29269   }
29270 
29271   if (NS_WARN_IF(aPersistenceType != quota::PERSISTENCE_TYPE_PERSISTENT &&
29272                  aPersistenceType != quota::PERSISTENCE_TYPE_TEMPORARY &&
29273                  aPersistenceType != quota::PERSISTENCE_TYPE_DEFAULT)) {
29274     ASSERT_UNLESS_FUZZING();
29275     return false;
29276   }
29277 
29278   if (NS_WARN_IF(aOrigin.IsEmpty())) {
29279     ASSERT_UNLESS_FUZZING();
29280     return false;
29281   }
29282 
29283   if (NS_WARN_IF(aDatabaseName.IsEmpty())) {
29284     ASSERT_UNLESS_FUZZING();
29285     return false;
29286   }
29287 
29288   if (NS_WARN_IF(aFileId == 0)) {
29289     ASSERT_UNLESS_FUZZING();
29290     return false;
29291   }
29292 
29293   RefPtr<GetFileReferencesHelper> helper =
29294     new GetFileReferencesHelper(aPersistenceType, aOrigin, aDatabaseName,
29295                                 aFileId);
29296 
29297   nsresult rv =
29298     helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt,
29299                                             aSliceRefCnt, aResult);
29300   if (NS_WARN_IF(NS_FAILED(rv))) {
29301     return false;
29302   }
29303 
29304   return true;
29305 }
29306 
29307 nsresult
DispatchAndReturnFileReferences(int32_t * aMemRefCnt,int32_t * aDBRefCnt,int32_t * aSliceRefCnt,bool * aResult)29308 GetFileReferencesHelper::DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
29309                                                          int32_t* aDBRefCnt,
29310                                                          int32_t* aSliceRefCnt,
29311                                                          bool* aResult)
29312 {
29313   AssertIsOnBackgroundThread();
29314   MOZ_ASSERT(aMemRefCnt);
29315   MOZ_ASSERT(aDBRefCnt);
29316   MOZ_ASSERT(aSliceRefCnt);
29317   MOZ_ASSERT(aResult);
29318 
29319   QuotaManager* quotaManager = QuotaManager::Get();
29320   MOZ_ASSERT(quotaManager);
29321 
29322   nsresult rv =
29323     quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
29324   if (NS_WARN_IF(NS_FAILED(rv))) {
29325     return rv;
29326   }
29327 
29328   mozilla::MutexAutoLock autolock(mMutex);
29329   while (mWaiting) {
29330     mCondVar.Wait();
29331   }
29332 
29333   *aMemRefCnt = mMemRefCnt;
29334   *aDBRefCnt = mDBRefCnt;
29335   *aSliceRefCnt = mSliceRefCnt;
29336   *aResult = mResult;
29337 
29338   return NS_OK;
29339 }
29340 
29341 NS_IMETHODIMP
Run()29342 GetFileReferencesHelper::Run()
29343 {
29344   AssertIsOnIOThread();
29345 
29346   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
29347   MOZ_ASSERT(mgr);
29348 
29349   RefPtr<FileManager> fileManager =
29350     mgr->GetFileManager(mPersistenceType, mOrigin, mDatabaseName);
29351 
29352   if (fileManager) {
29353     RefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(mFileId);
29354 
29355     if (fileInfo) {
29356       fileInfo->GetReferences(&mMemRefCnt, &mDBRefCnt, &mSliceRefCnt);
29357 
29358       if (mMemRefCnt != -1) {
29359         // We added an extra temp ref, so account for that accordingly.
29360         mMemRefCnt--;
29361       }
29362 
29363       mResult = true;
29364     }
29365   }
29366 
29367   mozilla::MutexAutoLock lock(mMutex);
29368   MOZ_ASSERT(mWaiting);
29369 
29370   mWaiting = false;
29371   mCondVar.Notify();
29372 
29373   return NS_OK;
29374 }
29375 
29376 NS_IMETHODIMP
Run()29377 FlushPendingFileDeletionsRunnable::Run()
29378 {
29379   MOZ_ASSERT(NS_IsMainThread());
29380 
29381   RefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::Get();
29382   if (NS_WARN_IF(!mgr)) {
29383     return NS_ERROR_FAILURE;
29384   }
29385 
29386   nsresult rv = mgr->FlushPendingFileDeletions();
29387   if (NS_WARN_IF(NS_FAILED(rv))) {
29388     return rv;
29389   }
29390 
29391   return NS_OK;
29392 }
29393 
29394 void
OnPromptComplete(PermissionValue aPermissionValue)29395 PermissionRequestHelper::OnPromptComplete(PermissionValue aPermissionValue)
29396 {
29397   MOZ_ASSERT(NS_IsMainThread());
29398 
29399   if (!mActorDestroyed) {
29400     Unused <<
29401       PIndexedDBPermissionRequestParent::Send__delete__(this, aPermissionValue);
29402   }
29403 }
29404 
29405 void
ActorDestroy(ActorDestroyReason aWhy)29406 PermissionRequestHelper::ActorDestroy(ActorDestroyReason aWhy)
29407 {
29408   MOZ_ASSERT(NS_IsMainThread());
29409   MOZ_ASSERT(!mActorDestroyed);
29410 
29411   mActorDestroyed = true;
29412 }
29413 
29414 #ifdef DEBUG
29415 
NS_IMPL_ISUPPORTS(DEBUGThreadSlower,nsIThreadObserver)29416 NS_IMPL_ISUPPORTS(DEBUGThreadSlower, nsIThreadObserver)
29417 
29418 NS_IMETHODIMP
29419 DEBUGThreadSlower::OnDispatchedEvent(nsIThreadInternal* /* aThread */)
29420 {
29421   MOZ_CRASH("Should never be called!");
29422 }
29423 
29424 NS_IMETHODIMP
OnProcessNextEvent(nsIThreadInternal *,bool)29425 DEBUGThreadSlower::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
29426                                       bool /* aMayWait */)
29427 {
29428   return NS_OK;
29429 }
29430 
29431 NS_IMETHODIMP
AfterProcessNextEvent(nsIThreadInternal *,bool)29432 DEBUGThreadSlower::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
29433                                          bool /* aEventWasProcessed */)
29434 {
29435   MOZ_ASSERT(kDEBUGThreadSleepMS);
29436 
29437   MOZ_ALWAYS_TRUE(PR_Sleep(PR_MillisecondsToInterval(kDEBUGThreadSleepMS)) ==
29438                     PR_SUCCESS);
29439   return NS_OK;
29440 }
29441 
29442 #endif // DEBUG
29443 
29444 nsresult
Init()29445 FileHelper::Init()
29446 {
29447   MOZ_ASSERT(!IsOnBackgroundThread());
29448   MOZ_ASSERT(mFileManager);
29449 
29450   nsCOMPtr<nsIFile> fileDirectory = mFileManager->GetCheckedDirectory();
29451   if (NS_WARN_IF(!fileDirectory)) {
29452     return NS_ERROR_FAILURE;
29453   }
29454 
29455   nsCOMPtr<nsIFile> journalDirectory = mFileManager->EnsureJournalDirectory();
29456   if (NS_WARN_IF(!journalDirectory)) {
29457     return NS_ERROR_FAILURE;
29458   }
29459 
29460   DebugOnly<bool> exists;
29461   MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->Exists(&exists)));
29462   MOZ_ASSERT(exists);
29463 
29464   DebugOnly<bool> isDirectory;
29465   MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->IsDirectory(&isDirectory)));
29466   MOZ_ASSERT(isDirectory);
29467 
29468   mFileDirectory = Move(fileDirectory);
29469   mJournalDirectory= Move(journalDirectory);
29470 
29471   return NS_OK;
29472 }
29473 
29474 already_AddRefed<nsIFile>
GetFile(FileInfo * aFileInfo)29475 FileHelper::GetFile(FileInfo* aFileInfo)
29476 {
29477   MOZ_ASSERT(!IsOnBackgroundThread());
29478   MOZ_ASSERT(aFileInfo);
29479   MOZ_ASSERT(mFileManager);
29480   MOZ_ASSERT(mFileDirectory);
29481 
29482   const int64_t fileId = aFileInfo->Id();
29483   MOZ_ASSERT(fileId > 0);
29484 
29485   nsCOMPtr<nsIFile> file =
29486     mFileManager->GetFileForId(mFileDirectory, fileId);
29487   return file.forget();
29488 }
29489 
29490 already_AddRefed<nsIFile>
GetCheckedFile(FileInfo * aFileInfo)29491 FileHelper::GetCheckedFile(FileInfo* aFileInfo)
29492 {
29493   MOZ_ASSERT(!IsOnBackgroundThread());
29494   MOZ_ASSERT(aFileInfo);
29495   MOZ_ASSERT(mFileManager);
29496   MOZ_ASSERT(mFileDirectory);
29497 
29498   const int64_t fileId = aFileInfo->Id();
29499   MOZ_ASSERT(fileId > 0);
29500 
29501   nsCOMPtr<nsIFile> file =
29502     mFileManager->GetCheckedFileForId(mFileDirectory, fileId);
29503   return file.forget();
29504 }
29505 
29506 already_AddRefed<nsIFile>
GetJournalFile(FileInfo * aFileInfo)29507 FileHelper::GetJournalFile(FileInfo* aFileInfo)
29508 {
29509   MOZ_ASSERT(!IsOnBackgroundThread());
29510   MOZ_ASSERT(aFileInfo);
29511   MOZ_ASSERT(mFileManager);
29512   MOZ_ASSERT(mJournalDirectory);
29513 
29514   const int64_t fileId = aFileInfo->Id();
29515   MOZ_ASSERT(fileId > 0);
29516 
29517   nsCOMPtr<nsIFile> file =
29518     mFileManager->GetFileForId(mJournalDirectory, fileId);
29519   return file.forget();
29520 }
29521 
29522 nsresult
CreateFileFromStream(nsIFile * aFile,nsIFile * aJournalFile,nsIInputStream * aInputStream,bool aCompress)29523 FileHelper::CreateFileFromStream(nsIFile* aFile,
29524                                  nsIFile* aJournalFile,
29525                                  nsIInputStream* aInputStream,
29526                                  bool aCompress)
29527 {
29528   MOZ_ASSERT(!IsOnBackgroundThread());
29529   MOZ_ASSERT(aFile);
29530   MOZ_ASSERT(aJournalFile);
29531   MOZ_ASSERT(aInputStream);
29532   MOZ_ASSERT(mFileManager);
29533   MOZ_ASSERT(mFileDirectory);
29534   MOZ_ASSERT(mJournalDirectory);
29535 
29536   bool exists;
29537   nsresult rv = aFile->Exists(&exists);
29538   if (NS_WARN_IF(NS_FAILED(rv))) {
29539     return rv;
29540   }
29541 
29542   // DOM blobs that are being stored in IDB are cached by calling
29543   // IDBDatabase::GetOrCreateFileActorForBlob. So if the same DOM blob is stored
29544   // again under a different key or in a different object store, we just add
29545   // a new reference instead of creating a new copy (all such stored blobs share
29546   // the same id).
29547   // However, it can happen that CreateFileFromStream failed due to quota
29548   // exceeded error and for some reason the orphaned file couldn't be deleted
29549   // immediately. Now, if the operation is being repeated, the DOM blob is
29550   // already cached, so it has the same file id which clashes with the orphaned
29551   // file. We could do some tricks to restore previous copy loop, but it's safer
29552   // to just delete the orphaned file and start from scratch.
29553   // This corner case is partially simulated in test_file_copy_failure.js
29554   if (exists) {
29555     bool isFile;
29556     rv = aFile->IsFile(&isFile);
29557     if (NS_WARN_IF(NS_FAILED(rv))) {
29558       return rv;
29559     }
29560 
29561     if (NS_WARN_IF(!isFile)) {
29562       return NS_ERROR_FAILURE;
29563     }
29564 
29565     rv = aJournalFile->Exists(&exists);
29566     if (NS_WARN_IF(NS_FAILED(rv))) {
29567       return rv;
29568     }
29569 
29570     if (NS_WARN_IF(!exists)) {
29571       return NS_ERROR_FAILURE;
29572     }
29573 
29574     rv = aJournalFile->IsFile(&isFile);
29575     if (NS_WARN_IF(NS_FAILED(rv))) {
29576       return rv;
29577     }
29578 
29579     if (NS_WARN_IF(!isFile)) {
29580       return NS_ERROR_FAILURE;
29581     }
29582 
29583     IDB_WARNING("Deleting orphaned file!");
29584 
29585     rv = RemoveFile(aFile, aJournalFile);
29586     if (NS_WARN_IF(NS_FAILED(rv))) {
29587       return rv;
29588     }
29589   }
29590 
29591   // Create a journal file first.
29592   rv = aJournalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
29593   if (NS_WARN_IF(NS_FAILED(rv))) {
29594     return rv;
29595   }
29596 
29597   // Now try to copy the stream.
29598   RefPtr<FileOutputStream> fileOutputStream =
29599     FileOutputStream::Create(mFileManager->Type(),
29600                              mFileManager->Group(),
29601                              mFileManager->Origin(),
29602                              aFile);
29603   if (NS_WARN_IF(!fileOutputStream)) {
29604     return NS_ERROR_FAILURE;
29605   }
29606 
29607   if (aCompress) {
29608     RefPtr<SnappyCompressOutputStream> snappyOutputStream =
29609       new SnappyCompressOutputStream(fileOutputStream);
29610 
29611     UniquePtr<char[]> buffer(new char[snappyOutputStream->BlockSize()]);
29612 
29613     rv = SyncCopy(aInputStream,
29614                   snappyOutputStream,
29615                   buffer.get(),
29616                   snappyOutputStream->BlockSize());
29617   } else {
29618     char buffer[kFileCopyBufferSize];
29619 
29620     rv = SyncCopy(aInputStream,
29621                   fileOutputStream,
29622                   buffer,
29623                   kFileCopyBufferSize);
29624   }
29625   if (NS_WARN_IF(NS_FAILED(rv))) {
29626     return rv;
29627   }
29628 
29629   return NS_OK;
29630 }
29631 
29632 nsresult
ReplaceFile(nsIFile * aFile,nsIFile * aNewFile,nsIFile * aNewJournalFile)29633 FileHelper::ReplaceFile(nsIFile* aFile,
29634                         nsIFile* aNewFile,
29635                         nsIFile* aNewJournalFile)
29636 {
29637   MOZ_ASSERT(!IsOnBackgroundThread());
29638   MOZ_ASSERT(aFile);
29639   MOZ_ASSERT(aNewFile);
29640   MOZ_ASSERT(aNewJournalFile);
29641   MOZ_ASSERT(mFileManager);
29642   MOZ_ASSERT(mFileDirectory);
29643   MOZ_ASSERT(mJournalDirectory);
29644 
29645   nsresult rv;
29646 
29647   int64_t fileSize;
29648 
29649   if (mFileManager->EnforcingQuota()) {
29650     rv = aFile->GetFileSize(&fileSize);
29651     if (NS_WARN_IF(NS_FAILED(rv))) {
29652       return rv;
29653     }
29654   }
29655 
29656   nsAutoString fileName;
29657   rv = aFile->GetLeafName(fileName);
29658   if (NS_WARN_IF(NS_FAILED(rv))) {
29659     return rv;
29660   }
29661 
29662   rv = aNewFile->RenameTo(nullptr, fileName);
29663   if (NS_WARN_IF(NS_FAILED(rv))) {
29664     return rv;
29665   }
29666 
29667   if (mFileManager->EnforcingQuota()) {
29668     QuotaManager* quotaManager = QuotaManager::Get();
29669     MOZ_ASSERT(quotaManager);
29670 
29671     quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
29672                                          mFileManager->Group(),
29673                                          mFileManager->Origin(),
29674                                          fileSize);
29675   }
29676 
29677   rv = aNewJournalFile->Remove(false);
29678   if (NS_WARN_IF(NS_FAILED(rv))) {
29679     return rv;
29680   }
29681 
29682   return NS_OK;
29683 }
29684 
29685 nsresult
RemoveFile(nsIFile * aFile,nsIFile * aJournalFile)29686 FileHelper::RemoveFile(nsIFile* aFile,
29687                        nsIFile* aJournalFile)
29688 {
29689   nsresult rv;
29690 
29691   int64_t fileSize;
29692 
29693   if (mFileManager->EnforcingQuota()) {
29694     rv = aFile->GetFileSize(&fileSize);
29695     if (NS_WARN_IF(NS_FAILED(rv))) {
29696       return rv;
29697     }
29698   }
29699 
29700   rv = aFile->Remove(false);
29701   if (NS_WARN_IF(NS_FAILED(rv))) {
29702     return rv;
29703   }
29704 
29705   if (mFileManager->EnforcingQuota()) {
29706     QuotaManager* quotaManager = QuotaManager::Get();
29707     MOZ_ASSERT(quotaManager);
29708 
29709     quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
29710                                          mFileManager->Group(),
29711                                          mFileManager->Origin(),
29712                                          fileSize);
29713   }
29714 
29715   rv = aJournalFile->Remove(false);
29716   if (NS_WARN_IF(NS_FAILED(rv))) {
29717     return rv;
29718   }
29719 
29720   return NS_OK;
29721 }
29722 
29723 already_AddRefed<FileInfo>
GetNewFileInfo()29724 FileHelper::GetNewFileInfo()
29725 {
29726   MOZ_ASSERT(mFileManager);
29727 
29728   return mFileManager->GetNewFileInfo();
29729 }
29730 
29731 nsresult
SyncCopy(nsIInputStream * aInputStream,nsIOutputStream * aOutputStream,char * aBuffer,uint32_t aBufferSize)29732 FileHelper::SyncCopy(nsIInputStream* aInputStream,
29733                      nsIOutputStream* aOutputStream,
29734                      char* aBuffer,
29735                      uint32_t aBufferSize)
29736 {
29737   MOZ_ASSERT(!IsOnBackgroundThread());
29738   MOZ_ASSERT(aInputStream);
29739   MOZ_ASSERT(aOutputStream);
29740 
29741   PROFILER_LABEL("IndexedDB",
29742                  "FileHelper::SyncCopy",
29743                  js::ProfileEntry::Category::STORAGE);
29744 
29745   nsresult rv;
29746 
29747   do {
29748     uint32_t numRead;
29749     rv = aInputStream->Read(aBuffer, aBufferSize, &numRead);
29750     if (NS_WARN_IF(NS_FAILED(rv))) {
29751       break;
29752     }
29753 
29754     if (!numRead) {
29755       break;
29756     }
29757 
29758     uint32_t numWrite;
29759     rv = aOutputStream->Write(aBuffer, numRead, &numWrite);
29760     if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
29761       rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
29762     }
29763     if (NS_WARN_IF(NS_FAILED(rv))) {
29764       break;
29765     }
29766 
29767     if (NS_WARN_IF(numWrite != numRead)) {
29768       rv = NS_ERROR_FAILURE;
29769       break;
29770     }
29771   } while (true);
29772 
29773   if (NS_SUCCEEDED(rv)) {
29774     rv = aOutputStream->Flush();
29775     if (NS_WARN_IF(NS_FAILED(rv))) {
29776       return rv;
29777     }
29778   }
29779 
29780   nsresult rv2 = aOutputStream->Close();
29781   if (NS_WARN_IF(NS_FAILED(rv2))) {
29782     return NS_SUCCEEDED(rv) ? rv2 : rv;
29783   }
29784 
29785   return rv;
29786 }
29787 
29788 } // namespace indexedDB
29789 } // namespace dom
29790 } // namespace mozilla
29791 
29792 #undef IDB_MOBILE
29793 #undef IDB_DEBUG_LOG
29794 #undef ASSERT_UNLESS_FUZZING
29795 #undef DISABLE_ASSERTS_FOR_FUZZING
29796