1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "ActorsParent.h"
8 
9 // Local includes
10 #include "LSInitializationTypes.h"
11 #include "LSObject.h"
12 #include "ReportInternalError.h"
13 
14 // Global includes
15 #include <cinttypes>
16 #include <cstdlib>
17 #include <cstring>
18 #include <new>
19 #include <tuple>
20 #include <type_traits>
21 #include <utility>
22 #include "CrashAnnotations.h"
23 #include "ErrorList.h"
24 #include "MainThreadUtils.h"
25 #include "mozIStorageAsyncConnection.h"
26 #include "mozIStorageConnection.h"
27 #include "mozIStorageFunction.h"
28 #include "mozIStorageService.h"
29 #include "mozIStorageStatement.h"
30 #include "mozIStorageValueArray.h"
31 #include "mozStorageCID.h"
32 #include "mozStorageHelper.h"
33 #include "mozilla/Assertions.h"
34 #include "mozilla/Atomics.h"
35 #include "mozilla/Attributes.h"
36 #include "mozilla/DebugOnly.h"
37 #include "mozilla/Logging.h"
38 #include "mozilla/MacroForEach.h"
39 #include "mozilla/Maybe.h"
40 #include "mozilla/Monitor.h"
41 #include "mozilla/Mutex.h"
42 #include "mozilla/NotNull.h"
43 #include "mozilla/OriginAttributes.h"
44 #include "mozilla/Preferences.h"
45 #include "mozilla/RefPtr.h"
46 #include "mozilla/Result.h"
47 #include "mozilla/ResultExtensions.h"
48 #include "mozilla/ScopeExit.h"
49 #include "mozilla/Services.h"
50 #include "mozilla/StaticPrefs_dom.h"
51 #include "mozilla/StaticPtr.h"
52 #include "mozilla/StoragePrincipalHelper.h"
53 #include "mozilla/UniquePtr.h"
54 #include "mozilla/Unused.h"
55 #include "mozilla/Variant.h"
56 #include "mozilla/dom/ClientManagerService.h"
57 #include "mozilla/dom/FlippedOnce.h"
58 #include "mozilla/dom/LSSnapshot.h"
59 #include "mozilla/dom/LSValue.h"
60 #include "mozilla/dom/LSWriteOptimizer.h"
61 #include "mozilla/dom/LSWriteOptimizerImpl.h"
62 #include "mozilla/dom/LocalStorageCommon.h"
63 #include "mozilla/dom/Nullable.h"
64 #include "mozilla/dom/PBackgroundLSDatabase.h"
65 #include "mozilla/dom/PBackgroundLSDatabaseParent.h"
66 #include "mozilla/dom/PBackgroundLSObserverParent.h"
67 #include "mozilla/dom/PBackgroundLSRequestParent.h"
68 #include "mozilla/dom/PBackgroundLSSharedTypes.h"
69 #include "mozilla/dom/PBackgroundLSSimpleRequestParent.h"
70 #include "mozilla/dom/PBackgroundLSSnapshotParent.h"
71 #include "mozilla/dom/SnappyUtils.h"
72 #include "mozilla/dom/StorageDBUpdater.h"
73 #include "mozilla/dom/StorageUtils.h"
74 #include "mozilla/dom/ipc/IdType.h"
75 #include "mozilla/dom/quota/CachingDatabaseConnection.h"
76 #include "mozilla/dom/quota/CheckedUnsafePtr.h"
77 #include "mozilla/dom/quota/Client.h"
78 #include "mozilla/dom/quota/ClientImpl.h"
79 #include "mozilla/dom/quota/DirectoryLock.h"
80 #include "mozilla/dom/quota/FirstInitializationAttemptsImpl.h"
81 #include "mozilla/dom/quota/OriginScope.h"
82 #include "mozilla/dom/quota/PersistenceType.h"
83 #include "mozilla/dom/quota/QuotaCommon.h"
84 #include "mozilla/dom/quota/QuotaManager.h"
85 #include "mozilla/dom/quota/QuotaObject.h"
86 #include "mozilla/dom/quota/UsageInfo.h"
87 #include "mozilla/ipc/BackgroundChild.h"
88 #include "mozilla/ipc/BackgroundParent.h"
89 #include "mozilla/ipc/PBackgroundChild.h"
90 #include "mozilla/ipc/PBackgroundParent.h"
91 #include "mozilla/ipc/PBackgroundSharedTypes.h"
92 #include "mozilla/ipc/ProtocolUtils.h"
93 #include "mozilla/storage/Variant.h"
94 #include "nsBaseHashtable.h"
95 #include "nsCOMPtr.h"
96 #include "nsClassHashtable.h"
97 #include "nsTHashMap.h"
98 #include "nsDebug.h"
99 #include "nsError.h"
100 #include "nsExceptionHandler.h"
101 #include "nsHashKeys.h"
102 #include "nsIBinaryInputStream.h"
103 #include "nsIBinaryOutputStream.h"
104 #include "nsIDirectoryEnumerator.h"
105 #include "nsIEventTarget.h"
106 #include "nsIFile.h"
107 #include "nsIInputStream.h"
108 #include "nsIObjectInputStream.h"
109 #include "nsIObjectOutputStream.h"
110 #include "nsIObserver.h"
111 #include "nsIObserverService.h"
112 #include "nsIOutputStream.h"
113 #include "nsIRunnable.h"
114 #include "nsISerialEventTarget.h"
115 #include "nsISupports.h"
116 #include "nsIThread.h"
117 #include "nsITimer.h"
118 #include "nsIVariant.h"
119 #include "nsInterfaceHashtable.h"
120 #include "nsLiteralString.h"
121 #include "nsNetUtil.h"
122 #include "nsPointerHashKeys.h"
123 #include "nsPrintfCString.h"
124 #include "nsRefPtrHashtable.h"
125 #include "nsServiceManagerUtils.h"
126 #include "nsString.h"
127 #include "nsStringFlags.h"
128 #include "nsStringFwd.h"
129 #include "nsTArray.h"
130 #include "nsTHashSet.h"
131 #include "nsTLiteralString.h"
132 #include "nsTStringRepr.h"
133 #include "nsThreadUtils.h"
134 #include "nsVariant.h"
135 #include "nsXPCOM.h"
136 #include "nsXULAppAPI.h"
137 #include "nscore.h"
138 #include "prenv.h"
139 #include "prtime.h"
140 
141 #define DISABLE_ASSERTS_FOR_FUZZING 0
142 
143 #if DISABLE_ASSERTS_FOR_FUZZING
144 #  define ASSERT_UNLESS_FUZZING(...) \
145     do {                             \
146     } while (0)
147 #else
148 #  define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
149 #endif
150 
151 #define LS_LOG_TEST() MOZ_LOG_TEST(GetLocalStorageLogger(), LogLevel::Info)
152 #define LS_LOG(_args) MOZ_LOG(GetLocalStorageLogger(), LogLevel::Info, _args)
153 
154 #if defined(MOZ_WIDGET_ANDROID)
155 #  define LS_MOBILE
156 #endif
157 
158 namespace mozilla::dom {
159 
160 using namespace mozilla::dom::quota;
161 using namespace mozilla::dom::StorageUtils;
162 using namespace mozilla::ipc;
163 
164 namespace {
165 
166 struct ArchivedOriginInfo;
167 class ArchivedOriginScope;
168 class Connection;
169 class ConnectionThread;
170 class Database;
171 class Observer;
172 class PrepareDatastoreOp;
173 class PreparedDatastore;
174 class QuotaClient;
175 class Snapshot;
176 
177 typedef nsClassHashtable<nsCStringHashKey, ArchivedOriginInfo>
178     ArchivedOriginHashtable;
179 
180 /*******************************************************************************
181  * Constants
182  ******************************************************************************/
183 
184 // Major schema version. Bump for almost everything.
185 const uint32_t kMajorSchemaVersion = 4;
186 
187 // Minor schema version. Should almost always be 0 (maybe bump on release
188 // branches if we have to).
189 const uint32_t kMinorSchemaVersion = 0;
190 
191 // The schema version we store in the SQLite database is a (signed) 32-bit
192 // integer. The major version is left-shifted 4 bits so the max value is
193 // 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
194 static_assert(kMajorSchemaVersion <= 0xFFFFFFF,
195               "Major version needs to fit in 28 bits.");
196 static_assert(kMinorSchemaVersion <= 0xF,
197               "Minor version needs to fit in 4 bits.");
198 
199 const int32_t kSQLiteSchemaVersion =
200     int32_t((kMajorSchemaVersion << 4) + kMinorSchemaVersion);
201 
202 // Changing the value here will override the page size of new databases only.
203 // A journal mode change and VACUUM are needed to change existing databases, so
204 // the best way to do that is to use the schema version upgrade mechanism.
205 const uint32_t kSQLitePageSizeOverride =
206 #ifdef LS_MOBILE
207     512;
208 #else
209     1024;
210 #endif
211 
212 static_assert(kSQLitePageSizeOverride == /* mozStorage default */ 0 ||
213                   (kSQLitePageSizeOverride % 2 == 0 &&
214                    kSQLitePageSizeOverride >= 512 &&
215                    kSQLitePageSizeOverride <= 65536),
216               "Must be 0 (disabled) or a power of 2 between 512 and 65536!");
217 
218 // Set to some multiple of the page size to grow the database in larger chunks.
219 const uint32_t kSQLiteGrowthIncrement = kSQLitePageSizeOverride * 2;
220 
221 static_assert(kSQLiteGrowthIncrement >= 0 &&
222                   kSQLiteGrowthIncrement % kSQLitePageSizeOverride == 0 &&
223                   kSQLiteGrowthIncrement < uint32_t(INT32_MAX),
224               "Must be 0 (disabled) or a positive multiple of the page size!");
225 
226 /**
227  * The database name for LocalStorage data in a per-origin directory.
228  */
229 constexpr auto kDataFileName = u"data.sqlite"_ns;
230 
231 /**
232  * The journal corresponding to kDataFileName.  (We don't use WAL mode.)
233  * Currently only needed in QuotaClient::InitOrigin and only in DEBUG builds.
234  * See the corresponding comment in QuotaClient::InitOrigin.
235  */
236 #ifdef DEBUG
237 constexpr auto kJournalFileName = u"data.sqlite-journal"_ns;
238 #endif
239 
240 /**
241  * This file contains the current usage of the LocalStorage database as defined
242  * by the mozLength totals of all keys and values for the database, which
243  * differs from the actual size on disk.  We store this value in a separate
244  * file as a cache so that we can initialize the QuotaClient faster.
245  * In the future, this file will be eliminated and the information will be
246  * stored in PROFILE/storage.sqlite or similar QuotaManager-wide storage.
247  *
248  * The file contains a binary verification cookie (32-bits) followed by the
249  * actual usage (64-bits).
250  */
251 constexpr auto kUsageFileName = u"usage"_ns;
252 
253 /**
254  * Following a QuotaManager idiom, this journal file's existence is a marker
255  * that the usage file was in the process of being updated and is currently
256  * invalid.  This file is created prior to updating the usage file and only
257  * deleted after the usage file has been written and closed and any pending
258  * database transactions have been committed.  Note that this idiom is expected
259  * to work if Gecko crashes in the middle of a write, but is not expected to be
260  * foolproof in the face of a system crash, as we do not explicitly attempt to
261  * fsync the directory containing the journal file.
262  *
263  * If the journal file is found to exist at origin initialization time, the
264  * usage will be re-computed from the current state of DATA_FILE_NAME.
265  */
266 constexpr auto kUsageJournalFileName = u"usage-journal"_ns;
267 
268 static const uint32_t kUsageFileSize = 12;
269 static const uint32_t kUsageFileCookie = 0x420a420a;
270 
271 /**
272  * How long between the first moment we know we have data to be written on a
273  * `Connection` and when we should actually perform the write.  This helps
274  * limit disk churn under silly usage patterns and is historically consistent
275  * with the previous, legacy implementation.
276  *
277  * Note that flushing happens downstream of Snapshot checkpointing and its
278  * batch mechanism which helps avoid wasteful IPC in the case of silly content
279  * code.
280  */
281 const uint32_t kFlushTimeoutMs = 5000;
282 
283 const char kPrivateBrowsingObserverTopic[] = "last-pb-context-exited";
284 
285 const uint32_t kDefaultShadowWrites = true;
286 const uint32_t kDefaultSnapshotPrefill = 16384;
287 const uint32_t kDefaultSnapshotGradualPrefill = 4096;
288 const uint32_t kDefaultClientValidation = true;
289 /**
290  * Should all mutations also be reflected in the "shadow" database, which is
291  * the legacy webappsstore.sqlite database.  When this is enabled, users can
292  * downgrade their version of Firefox and/or otherwise fall back to the legacy
293  * implementation without loss of data.  (Older versions of Firefox will
294  * recognize the presence of ls-archive.sqlite and purge it and the other
295  * LocalStorage directories so privacy is maintained.)
296  */
297 const char kShadowWritesPref[] = "dom.storage.shadow_writes";
298 /**
299  * Byte budget for sending data down to the LSSnapshot instance when it is first
300  * created.  If there is less data than this (measured by tallying the string
301  * length of the keys and values), all data is sent, otherwise partial data is
302  * sent.  See `Snapshot`.
303  */
304 const char kSnapshotPrefillPref[] = "dom.storage.snapshot_prefill";
305 /**
306  * When a specific value is requested by an LSSnapshot that is not already fully
307  * populated, gradual prefill is used. This preference specifies the number of
308  * bytes to be used to send values beyond the specific value that is requested.
309  * (The size of the explicitly requested value does not impact this preference.)
310  * Setting the value to 0 disables gradual prefill. Tests may set this value to
311  * -1 which is converted to INT_MAX in order to cause gradual prefill to send
312  * all values not previously sent.
313  */
314 const char kSnapshotGradualPrefillPref[] =
315     "dom.storage.snapshot_gradual_prefill";
316 
317 const char kClientValidationPref[] = "dom.storage.client_validation";
318 
319 /**
320  * The amount of time a PreparedDatastore instance should stick around after a
321  * preload is triggered in order to give time for the page to use LocalStorage
322  * without triggering worst-case synchronous jank.
323  */
324 const uint32_t kPreparedDatastoreTimeoutMs = 20000;
325 
326 /**
327  * Cold storage for LocalStorage data extracted from webappsstore.sqlite at
328  * LSNG first-run that has not yet been migrated to its own per-origin directory
329  * by use.
330  *
331  * In other words, at first run, LSNG copies the contents of webappsstore.sqlite
332  * into this database.  As requests are made for that LocalStorage data, the
333  * contents are removed from this database and placed into per-origin QM
334  * storage.  So the contents of this database are always old, unused
335  * LocalStorage data that we can potentially get rid of at some point in the
336  * future.
337  */
338 #define LS_ARCHIVE_FILE_NAME u"ls-archive.sqlite"
339 /**
340  * The legacy LocalStorage database.  Its contents are maintained as our
341  * "shadow" database so that LSNG can be disabled without loss of user data.
342  */
343 #define WEB_APPS_STORE_FILE_NAME u"webappsstore.sqlite"
344 
345 // Shadow database Write Ahead Log's maximum size is 512KB
346 const uint32_t kShadowMaxWALSize = 512 * 1024;
347 
348 const uint32_t kShadowJournalSizeLimit = kShadowMaxWALSize * 3;
349 
350 bool IsOnGlobalConnectionThread();
351 
352 void AssertIsOnGlobalConnectionThread();
353 
354 /*******************************************************************************
355  * SQLite functions
356  ******************************************************************************/
357 
MakeSchemaVersion(uint32_t aMajorSchemaVersion,uint32_t aMinorSchemaVersion)358 int32_t MakeSchemaVersion(uint32_t aMajorSchemaVersion,
359                           uint32_t aMinorSchemaVersion) {
360   return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion);
361 }
362 
GetArchivedOriginHashKey(const nsACString & aOriginSuffix,const nsACString & aOriginNoSuffix)363 nsCString GetArchivedOriginHashKey(const nsACString& aOriginSuffix,
364                                    const nsACString& aOriginNoSuffix) {
365   return aOriginSuffix + ":"_ns + aOriginNoSuffix;
366 }
367 
CreateTables(mozIStorageConnection * aConnection)368 nsresult CreateTables(mozIStorageConnection* aConnection) {
369   MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
370   MOZ_ASSERT(aConnection);
371 
372   // Table `database`
373   QM_TRY(aConnection->ExecuteSimpleSQL(
374       "CREATE TABLE database"
375       "( origin TEXT NOT NULL"
376       ", usage INTEGER NOT NULL DEFAULT 0"
377       ", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
378       ", last_analyze_time INTEGER NOT NULL DEFAULT 0"
379       ", last_vacuum_size INTEGER NOT NULL DEFAULT 0"
380       ");"_ns));
381 
382   // Table `data`
383   QM_TRY(aConnection->ExecuteSimpleSQL(
384       "CREATE TABLE data"
385       "( key TEXT PRIMARY KEY"
386       ", value TEXT NOT NULL"
387       ", utf16Length INTEGER NOT NULL DEFAULT 0"
388       ", compressed INTEGER NOT NULL DEFAULT 0"
389       ", lastAccessTime INTEGER NOT NULL DEFAULT 0"
390       ");"_ns));
391 
392   QM_TRY(aConnection->SetSchemaVersion(kSQLiteSchemaVersion));
393 
394   return NS_OK;
395 }
396 
UpgradeSchemaFrom1_0To2_0(mozIStorageConnection * aConnection)397 nsresult UpgradeSchemaFrom1_0To2_0(mozIStorageConnection* aConnection) {
398   AssertIsOnIOThread();
399   MOZ_ASSERT(aConnection);
400 
401   QM_TRY(aConnection->ExecuteSimpleSQL(
402       "ALTER TABLE database ADD COLUMN usage INTEGER NOT NULL DEFAULT 0;"_ns));
403 
404   QM_TRY(aConnection->ExecuteSimpleSQL(
405       "UPDATE database "
406       "SET usage = (SELECT total(utf16Length(key) + utf16Length(value)) "
407       "FROM data);"_ns));
408 
409   QM_TRY(aConnection->SetSchemaVersion(MakeSchemaVersion(2, 0)));
410 
411   return NS_OK;
412 }
413 
UpgradeSchemaFrom2_0To3_0(mozIStorageConnection * aConnection)414 nsresult UpgradeSchemaFrom2_0To3_0(mozIStorageConnection* aConnection) {
415   AssertIsOnIOThread();
416   MOZ_ASSERT(aConnection);
417 
418   QM_TRY(aConnection->ExecuteSimpleSQL(
419       "ALTER TABLE data ADD COLUMN utf16Length INTEGER NOT NULL DEFAULT 0;"_ns));
420 
421   QM_TRY(aConnection->ExecuteSimpleSQL(
422       "UPDATE data SET utf16Length = utf16Length(value);"_ns));
423 
424   QM_TRY(aConnection->SetSchemaVersion(MakeSchemaVersion(3, 0)));
425 
426   return NS_OK;
427 }
428 
UpgradeSchemaFrom3_0To4_0(mozIStorageConnection * aConnection)429 nsresult UpgradeSchemaFrom3_0To4_0(mozIStorageConnection* aConnection) {
430   AssertIsOnIOThread();
431   MOZ_ASSERT(aConnection);
432 
433   QM_TRY(aConnection->SetSchemaVersion(MakeSchemaVersion(4, 0)));
434 
435   return NS_OK;
436 }
437 
SetDefaultPragmas(mozIStorageConnection * aConnection)438 nsresult SetDefaultPragmas(mozIStorageConnection* aConnection) {
439   MOZ_ASSERT(!NS_IsMainThread());
440   MOZ_ASSERT(aConnection);
441 
442   QM_TRY(aConnection->ExecuteSimpleSQL("PRAGMA synchronous = FULL;"_ns));
443 
444 #ifndef LS_MOBILE
445   if (kSQLiteGrowthIncrement) {
446     // This is just an optimization so ignore the failure if the disk is
447     // currently too full.
448     QM_TRY(QM_OR_ELSE_WARN_IF(
449         // Expression.
450         ToResult(
451             aConnection->SetGrowthIncrement(kSQLiteGrowthIncrement, ""_ns)),
452         // Predicate.
453         IsSpecificError<NS_ERROR_FILE_TOO_BIG>,
454         // Fallback.
455         ErrToDefaultOk<>));
456   }
457 #endif  // LS_MOBILE
458 
459   return NS_OK;
460 }
461 
462 template <typename CorruptedFileHandler>
CreateStorageConnection(nsIFile & aDBFile,nsIFile & aUsageFile,const nsACString & aOrigin,CorruptedFileHandler && aCorruptedFileHandler)463 Result<nsCOMPtr<mozIStorageConnection>, nsresult> CreateStorageConnection(
464     nsIFile& aDBFile, nsIFile& aUsageFile, const nsACString& aOrigin,
465     CorruptedFileHandler&& aCorruptedFileHandler) {
466   MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
467 
468   // XXX Common logic should be refactored out of this method and
469   // cache::DBAction::OpenDBConnection, and maybe other similar functions.
470 
471   QM_TRY_INSPECT(
472       const auto& storageService,
473       ToResultGet<nsCOMPtr<mozIStorageService>>(
474           MOZ_SELECT_OVERLOAD(do_GetService), MOZ_STORAGE_SERVICE_CONTRACTID));
475 
476   // XXX We can't use QM_OR_ELSE_WARN_IF because base-toolchains builds fail
477   // with: error: use of 'tryResult28' before deduction of 'auto'
478   QM_TRY_UNWRAP(
479       auto connection,
480       OrElseIf(
481           // Expression.
482           MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageConnection>,
483                                      storageService, OpenDatabase, &aDBFile),
484           // Predicate.
485           IsDatabaseCorruptionError,
486           // Fallback.
487           ([&aUsageFile, &aDBFile, &aCorruptedFileHandler,
488             &storageService](const nsresult rv)
489                -> Result<nsCOMPtr<mozIStorageConnection>, nsresult> {
490             // Remove the usage file first (it might not exist at all due
491             // to corrupted state, which is ignored here).
492 
493             // Usually we only use QM_OR_ELSE_LOG_VERBOSE(_IF) with Remove and
494             // NS_ERROR_FILE_NOT_FOUND/NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
495             // check, but we're already in the rare case of corruption here,
496             // so the use of QM_OR_ELSE_WARN_IF is ok here.
497             QM_TRY(QM_OR_ELSE_WARN_IF(
498                 // Expression.
499                 ToResult(aUsageFile.Remove(false)),
500                 // Predicate.
501                 ([](const nsresult rv) {
502                   return rv == NS_ERROR_FILE_NOT_FOUND ||
503                          rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
504                 }),
505                 // Fallback.
506                 ErrToDefaultOk<>));
507 
508             // Call the corrupted file handler before trying to remove the
509             // database file, which might fail.
510             std::forward<CorruptedFileHandler>(aCorruptedFileHandler)();
511 
512             // Nuke the database file.
513             QM_TRY(aDBFile.Remove(false));
514 
515             QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_TYPED(
516                 nsCOMPtr<mozIStorageConnection>, storageService, OpenDatabase,
517                 &aDBFile));
518           })));
519 
520   QM_TRY(SetDefaultPragmas(connection));
521 
522   // Check to make sure that the database schema is correct.
523   // XXX Try to make schemaVersion const.
524   QM_TRY_UNWRAP(int32_t schemaVersion,
525                 MOZ_TO_RESULT_INVOKE(connection, GetSchemaVersion));
526 
527   QM_TRY(OkIf(schemaVersion <= kSQLiteSchemaVersion), Err(NS_ERROR_FAILURE));
528 
529   if (schemaVersion != kSQLiteSchemaVersion) {
530     const bool newDatabase = !schemaVersion;
531 
532     if (newDatabase) {
533       // Set the page size first.
534       if (kSQLitePageSizeOverride) {
535         QM_TRY(connection->ExecuteSimpleSQL(nsPrintfCString(
536             "PRAGMA page_size = %" PRIu32 ";", kSQLitePageSizeOverride)));
537       }
538 
539       // We have to set the auto_vacuum mode before opening a transaction.
540       QM_TRY(connection->ExecuteSimpleSQL(
541 #ifdef LS_MOBILE
542           // Turn on full auto_vacuum mode to reclaim disk space on mobile
543           // devices (at the cost of some COMMIT speed).
544           "PRAGMA auto_vacuum = FULL;"_ns
545 #else
546           // Turn on incremental auto_vacuum mode on desktop builds.
547           "PRAGMA auto_vacuum = INCREMENTAL;"_ns
548 #endif
549           ));
550     }
551 
552     mozStorageTransaction transaction(
553         connection, false, mozIStorageConnection::TRANSACTION_IMMEDIATE);
554 
555     QM_TRY(transaction.Start())
556 
557     if (newDatabase) {
558       QM_TRY(CreateTables(connection));
559 
560 #ifdef DEBUG
561       {
562         QM_TRY_INSPECT(const int32_t& schemaVersion,
563                        MOZ_TO_RESULT_INVOKE(connection, GetSchemaVersion),
564                        QM_ASSERT_UNREACHABLE);
565 
566         MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
567       }
568 #endif
569 
570       QM_TRY_INSPECT(
571           const auto& stmt,
572           MOZ_TO_RESULT_INVOKE_TYPED(
573               nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
574               "INSERT INTO database (origin) VALUES (:origin)"_ns));
575 
576       QM_TRY(stmt->BindUTF8StringByName("origin"_ns, aOrigin));
577 
578       QM_TRY(stmt->Execute());
579     } else {
580       // This logic needs to change next time we change the schema!
581       static_assert(kSQLiteSchemaVersion == int32_t((4 << 4) + 0),
582                     "Upgrade function needed due to schema version increase.");
583 
584       while (schemaVersion != kSQLiteSchemaVersion) {
585         if (schemaVersion == MakeSchemaVersion(1, 0)) {
586           QM_TRY(UpgradeSchemaFrom1_0To2_0(connection));
587         } else if (schemaVersion == MakeSchemaVersion(2, 0)) {
588           QM_TRY(UpgradeSchemaFrom2_0To3_0(connection));
589         } else if (schemaVersion == MakeSchemaVersion(3, 0)) {
590           QM_TRY(UpgradeSchemaFrom3_0To4_0(connection));
591         } else {
592           LS_WARNING(
593               "Unable to open LocalStorage database, no upgrade path is "
594               "available!");
595           return Err(NS_ERROR_FAILURE);
596         }
597 
598         QM_TRY_UNWRAP(schemaVersion,
599                       MOZ_TO_RESULT_INVOKE(connection, GetSchemaVersion));
600       }
601 
602       MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
603     }
604 
605     QM_TRY(transaction.Commit());
606 
607     if (newDatabase) {
608       // Windows caches the file size, let's force it to stat the file again.
609       QM_TRY_INSPECT(const bool& exists, MOZ_TO_RESULT_INVOKE(aDBFile, Exists));
610       Unused << exists;
611 
612       QM_TRY_INSPECT(const int64_t& fileSize,
613                      MOZ_TO_RESULT_INVOKE(aDBFile, GetFileSize));
614 
615       MOZ_ASSERT(fileSize > 0);
616 
617       const PRTime vacuumTime = PR_Now();
618       MOZ_ASSERT(vacuumTime);
619 
620       QM_TRY_INSPECT(
621           const auto& vacuumTimeStmt,
622           MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageStatement>, connection,
623                                      CreateStatement,
624                                      "UPDATE database "
625                                      "SET last_vacuum_time = :time"
626                                      ", last_vacuum_size = :size;"_ns));
627 
628       QM_TRY(vacuumTimeStmt->BindInt64ByName("time"_ns, vacuumTime));
629 
630       QM_TRY(vacuumTimeStmt->BindInt64ByName("size"_ns, fileSize));
631 
632       QM_TRY(vacuumTimeStmt->Execute());
633     }
634   }
635 
636   return connection;
637 }
638 
GetStorageConnection(const nsAString & aDatabaseFilePath)639 Result<nsCOMPtr<mozIStorageConnection>, nsresult> GetStorageConnection(
640     const nsAString& aDatabaseFilePath) {
641   AssertIsOnGlobalConnectionThread();
642   MOZ_ASSERT(!aDatabaseFilePath.IsEmpty());
643   MOZ_ASSERT(StringEndsWith(aDatabaseFilePath, u".sqlite"_ns));
644 
645   QM_TRY_INSPECT(const auto& databaseFile, QM_NewLocalFile(aDatabaseFilePath));
646 
647   QM_TRY_INSPECT(const bool& exists,
648                  MOZ_TO_RESULT_INVOKE(databaseFile, Exists));
649 
650   QM_TRY(OkIf(exists), Err(NS_ERROR_FAILURE));
651 
652   QM_TRY_INSPECT(const auto& ss, ToResultGet<nsCOMPtr<mozIStorageService>>(
653                                      MOZ_SELECT_OVERLOAD(do_GetService),
654                                      MOZ_STORAGE_SERVICE_CONTRACTID));
655 
656   QM_TRY_UNWRAP(auto connection,
657                 MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageConnection>, ss,
658                                            OpenDatabase, databaseFile));
659 
660   QM_TRY(SetDefaultPragmas(connection));
661 
662   return connection;
663 }
664 
GetArchiveFile(const nsAString & aStoragePath)665 Result<nsCOMPtr<nsIFile>, nsresult> GetArchiveFile(
666     const nsAString& aStoragePath) {
667   AssertIsOnIOThread();
668   MOZ_ASSERT(!aStoragePath.IsEmpty());
669 
670   QM_TRY_UNWRAP(auto archiveFile, QM_NewLocalFile(aStoragePath));
671 
672   QM_TRY(archiveFile->Append(nsLiteralString(LS_ARCHIVE_FILE_NAME)));
673 
674   return archiveFile;
675 }
676 
677 Result<nsCOMPtr<mozIStorageConnection>, nsresult>
CreateArchiveStorageConnection(const nsAString & aStoragePath)678 CreateArchiveStorageConnection(const nsAString& aStoragePath) {
679   AssertIsOnIOThread();
680   MOZ_ASSERT(!aStoragePath.IsEmpty());
681 
682   QM_TRY_INSPECT(const auto& archiveFile, GetArchiveFile(aStoragePath));
683 
684   // QuotaManager ensures this file always exists.
685   DebugOnly<bool> exists;
686   MOZ_ASSERT(NS_SUCCEEDED(archiveFile->Exists(&exists)));
687   MOZ_ASSERT(exists);
688 
689   QM_TRY_INSPECT(const bool& isDirectory,
690                  MOZ_TO_RESULT_INVOKE(archiveFile, IsDirectory));
691 
692   if (isDirectory) {
693     LS_WARNING("ls-archive is not a file!");
694     return nsCOMPtr<mozIStorageConnection>{};
695   }
696 
697   QM_TRY_INSPECT(const auto& ss, ToResultGet<nsCOMPtr<mozIStorageService>>(
698                                      MOZ_SELECT_OVERLOAD(do_GetService),
699                                      MOZ_STORAGE_SERVICE_CONTRACTID));
700 
701   QM_TRY_UNWRAP(
702       auto connection,
703       QM_OR_ELSE_WARN_IF(
704           // Expression.
705           MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageConnection>, ss,
706                                      OpenUnsharedDatabase, archiveFile),
707           // Predicate.
708           IsDatabaseCorruptionError,
709           // Fallback. Don't throw an error, leave a corrupted ls-archive
710           // database as it is.
711           ErrToDefaultOk<nsCOMPtr<mozIStorageConnection>>));
712 
713   if (connection) {
714     const nsresult rv = StorageDBUpdater::Update(connection);
715     if (NS_FAILED(rv)) {
716       // Don't throw an error, leave a non-updateable ls-archive database as
717       // it is.
718       return nsCOMPtr<mozIStorageConnection>{};
719     }
720   }
721 
722   return connection;
723 }
724 
AttachArchiveDatabase(const nsAString & aStoragePath,mozIStorageConnection * aConnection)725 nsresult AttachArchiveDatabase(const nsAString& aStoragePath,
726                                mozIStorageConnection* aConnection) {
727   AssertIsOnIOThread();
728   MOZ_ASSERT(!aStoragePath.IsEmpty());
729   MOZ_ASSERT(aConnection);
730 
731   QM_TRY_INSPECT(const auto& archiveFile, GetArchiveFile(aStoragePath));
732 
733 #ifdef DEBUG
734   {
735     QM_TRY_INSPECT(const bool& exists,
736                    MOZ_TO_RESULT_INVOKE(archiveFile, Exists));
737 
738     MOZ_ASSERT(exists);
739   }
740 #endif
741 
742   QM_TRY_INSPECT(const auto& path,
743                  MOZ_TO_RESULT_INVOKE_TYPED(nsString, archiveFile, GetPath));
744 
745   QM_TRY_INSPECT(const auto& stmt,
746                  MOZ_TO_RESULT_INVOKE_TYPED(
747                      nsCOMPtr<mozIStorageStatement>, aConnection,
748                      CreateStatement, "ATTACH DATABASE :path AS archive;"_ns));
749 
750   QM_TRY(stmt->BindStringByName("path"_ns, path));
751   QM_TRY(stmt->Execute());
752 
753   return NS_OK;
754 }
755 
DetachArchiveDatabase(mozIStorageConnection * aConnection)756 nsresult DetachArchiveDatabase(mozIStorageConnection* aConnection) {
757   AssertIsOnIOThread();
758   MOZ_ASSERT(aConnection);
759 
760   QM_TRY(aConnection->ExecuteSimpleSQL("DETACH DATABASE archive"_ns));
761 
762   return NS_OK;
763 }
764 
GetShadowFile(const nsAString & aBasePath)765 Result<nsCOMPtr<nsIFile>, nsresult> GetShadowFile(const nsAString& aBasePath) {
766   MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
767   MOZ_ASSERT(!aBasePath.IsEmpty());
768 
769   QM_TRY_UNWRAP(auto archiveFile, QM_NewLocalFile(aBasePath));
770 
771   QM_TRY(archiveFile->Append(nsLiteralString(WEB_APPS_STORE_FILE_NAME)));
772 
773   return archiveFile;
774 }
775 
SetShadowJournalMode(mozIStorageConnection * aConnection)776 nsresult SetShadowJournalMode(mozIStorageConnection* aConnection) {
777   MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
778   MOZ_ASSERT(aConnection);
779 
780   // Try enabling WAL mode. This can fail in various circumstances so we have to
781   // check the results here.
782   constexpr auto journalModeQueryStart = "PRAGMA journal_mode = "_ns;
783   constexpr auto journalModeWAL = "wal"_ns;
784 
785   QM_TRY_INSPECT(const auto& stmt,
786                  CreateAndExecuteSingleStepStatement(
787                      *aConnection, journalModeQueryStart + journalModeWAL));
788 
789   QM_TRY_INSPECT(
790       const auto& journalMode,
791       MOZ_TO_RESULT_INVOKE_TYPED(nsAutoCString, *stmt, GetUTF8String, 0));
792 
793   if (journalMode.Equals(journalModeWAL)) {
794     // WAL mode successfully enabled. Set limits on its size here.
795 
796     // Set the threshold for auto-checkpointing the WAL. We don't want giant
797     // logs slowing down us.
798     QM_TRY_INSPECT(const auto& stmt, CreateAndExecuteSingleStepStatement(
799                                          *aConnection, "PRAGMA page_size;"_ns));
800 
801     QM_TRY_INSPECT(const int32_t& pageSize,
802                    MOZ_TO_RESULT_INVOKE(*stmt, GetInt32, 0));
803 
804     MOZ_ASSERT(pageSize >= 512 && pageSize <= 65536);
805 
806     QM_TRY(aConnection->ExecuteSimpleSQL(
807         "PRAGMA wal_autocheckpoint = "_ns +
808         IntToCString(static_cast<int32_t>(kShadowMaxWALSize / pageSize))));
809 
810     // Set the maximum WAL log size to reduce footprint on mobile (large empty
811     // WAL files will be truncated)
812     QM_TRY(
813         aConnection->ExecuteSimpleSQL("PRAGMA journal_size_limit = "_ns +
814                                       IntToCString(kShadowJournalSizeLimit)));
815   } else {
816     QM_TRY(
817         aConnection->ExecuteSimpleSQL(journalModeQueryStart + "truncate"_ns));
818   }
819 
820   return NS_OK;
821 }
822 
CreateShadowStorageConnection(const nsAString & aBasePath)823 Result<nsCOMPtr<mozIStorageConnection>, nsresult> CreateShadowStorageConnection(
824     const nsAString& aBasePath) {
825   MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
826   MOZ_ASSERT(!aBasePath.IsEmpty());
827 
828   QM_TRY_INSPECT(const auto& shadowFile, GetShadowFile(aBasePath));
829 
830   QM_TRY_INSPECT(const auto& ss, ToResultGet<nsCOMPtr<mozIStorageService>>(
831                                      MOZ_SELECT_OVERLOAD(do_GetService),
832                                      MOZ_STORAGE_SERVICE_CONTRACTID));
833 
834   QM_TRY_UNWRAP(
835       auto connection,
836       QM_OR_ELSE_WARN_IF(
837           // Expression.
838           MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageConnection>, ss,
839                                      OpenUnsharedDatabase, shadowFile),
840           // Predicate.
841           IsDatabaseCorruptionError,
842           // Fallback.
843           ([&shadowFile, &ss](const nsresult rv)
844                -> Result<nsCOMPtr<mozIStorageConnection>, nsresult> {
845             QM_TRY(shadowFile->Remove(false));
846 
847             QM_TRY_RETURN(
848                 MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageConnection>, ss,
849                                            OpenUnsharedDatabase, shadowFile));
850           })));
851 
852   QM_TRY(SetShadowJournalMode(connection));
853 
854   // XXX Depending on whether the *first* call to OpenUnsharedDatabase above
855   // failed, we (a) might or (b) might not be dealing with a fresh database
856   // here. This is confusing, since in a failure of case (a) we would do the
857   // same thing again. Probably, the control flow should be changed here so that
858   // it's clear we only delete & create a fresh database once. If we still have
859   // a failure then, we better give up. Or, if we really want to handle that,
860   // the number of 2 retries seems arbitrary, and we should better do this in
861   // some loop until a maximum number of retries is reached.
862   //
863   // Compare this with QuotaManager::CreateLocalStorageArchiveConnection, which
864   // actually tracks if the file was removed before, but it's also more
865   // complicated than it should be. Maybe these two methods can be merged (which
866   // would mean that a parameter must be added that indicates whether it's
867   // handling the shadow file or not).
868   QM_TRY(QM_OR_ELSE_WARN(
869       // Expression.
870       ToResult(StorageDBUpdater::Update(connection)),
871       // Fallback.
872       ([&connection, &shadowFile, &ss](const nsresult) -> Result<Ok, nsresult> {
873         QM_TRY(connection->Close());
874         QM_TRY(shadowFile->Remove(false));
875 
876         QM_TRY_UNWRAP(connection, MOZ_TO_RESULT_INVOKE_TYPED(
877                                       nsCOMPtr<mozIStorageConnection>, ss,
878                                       OpenUnsharedDatabase, shadowFile));
879 
880         QM_TRY(SetShadowJournalMode(connection));
881 
882         QM_TRY(StorageDBUpdater::CreateCurrentSchema(connection));
883 
884         return Ok{};
885       })));
886 
887   return connection;
888 }
889 
GetShadowStorageConnection(const nsAString & aBasePath)890 Result<nsCOMPtr<mozIStorageConnection>, nsresult> GetShadowStorageConnection(
891     const nsAString& aBasePath) {
892   AssertIsOnIOThread();
893   MOZ_ASSERT(!aBasePath.IsEmpty());
894 
895   QM_TRY_INSPECT(const auto& shadowFile, GetShadowFile(aBasePath));
896 
897   QM_TRY_INSPECT(const bool& exists, MOZ_TO_RESULT_INVOKE(shadowFile, Exists));
898 
899   QM_TRY(OkIf(exists), Err(NS_ERROR_FAILURE));
900 
901   QM_TRY_INSPECT(const auto& ss, ToResultGet<nsCOMPtr<mozIStorageService>>(
902                                      MOZ_SELECT_OVERLOAD(do_GetService),
903                                      MOZ_STORAGE_SERVICE_CONTRACTID));
904 
905   QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageConnection>, ss,
906                                            OpenUnsharedDatabase, shadowFile));
907 }
908 
AttachShadowDatabase(const nsAString & aBasePath,mozIStorageConnection * aConnection)909 nsresult AttachShadowDatabase(const nsAString& aBasePath,
910                               mozIStorageConnection* aConnection) {
911   AssertIsOnGlobalConnectionThread();
912   MOZ_ASSERT(!aBasePath.IsEmpty());
913   MOZ_ASSERT(aConnection);
914 
915   QM_TRY_INSPECT(const auto& shadowFile, GetShadowFile(aBasePath));
916 
917 #ifdef DEBUG
918   {
919     QM_TRY_INSPECT(const bool& exists,
920                    MOZ_TO_RESULT_INVOKE(shadowFile, Exists));
921 
922     MOZ_ASSERT(exists);
923   }
924 #endif
925 
926   QM_TRY_INSPECT(const auto& path,
927                  MOZ_TO_RESULT_INVOKE_TYPED(nsString, shadowFile, GetPath));
928 
929   QM_TRY_INSPECT(const auto& stmt,
930                  MOZ_TO_RESULT_INVOKE_TYPED(
931                      nsCOMPtr<mozIStorageStatement>, aConnection,
932                      CreateStatement, "ATTACH DATABASE :path AS shadow;"_ns));
933 
934   QM_TRY(stmt->BindStringByName("path"_ns, path));
935 
936   QM_TRY(stmt->Execute());
937 
938   return NS_OK;
939 }
940 
DetachShadowDatabase(mozIStorageConnection * aConnection)941 nsresult DetachShadowDatabase(mozIStorageConnection* aConnection) {
942   AssertIsOnGlobalConnectionThread();
943   MOZ_ASSERT(aConnection);
944 
945   QM_TRY(aConnection->ExecuteSimpleSQL("DETACH DATABASE shadow"_ns));
946 
947   return NS_OK;
948 }
949 
GetUsageFile(const nsAString & aDirectoryPath)950 Result<nsCOMPtr<nsIFile>, nsresult> GetUsageFile(
951     const nsAString& aDirectoryPath) {
952   MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
953   MOZ_ASSERT(!aDirectoryPath.IsEmpty());
954 
955   QM_TRY_UNWRAP(auto usageFile, QM_NewLocalFile(aDirectoryPath));
956 
957   QM_TRY(usageFile->Append(kUsageFileName));
958 
959   return usageFile;
960 }
961 
GetUsageJournalFile(const nsAString & aDirectoryPath)962 Result<nsCOMPtr<nsIFile>, nsresult> GetUsageJournalFile(
963     const nsAString& aDirectoryPath) {
964   MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
965   MOZ_ASSERT(!aDirectoryPath.IsEmpty());
966 
967   QM_TRY_UNWRAP(auto usageJournalFile, QM_NewLocalFile(aDirectoryPath));
968 
969   QM_TRY(usageJournalFile->Append(kUsageJournalFileName));
970 
971   return usageJournalFile;
972 }
973 
974 // Checks if aFile exists and is a file. Returns true if it exists and is a
975 // file, false if it doesn't exist, and an error if it exists but isn't a file.
ExistsAsFile(nsIFile & aFile)976 Result<bool, nsresult> ExistsAsFile(nsIFile& aFile) {
977   enum class ExistsAsFileResult { DoesNotExist, IsDirectory, IsFile };
978 
979   // This is an optimization to check both properties in one OS case, rather
980   // than calling Exists first, and then IsDirectory. IsDirectory also checks
981   // if the path exists. QM_OR_ELSE_WARN_IF is not used here since we just want
982   // to log NS_ERROR_FILE_NOT_FOUND/NS_ERROR_FILE_TARGET_DOES_NOT_EXIST result
983   // and not spam the reports.
984   QM_TRY_INSPECT(const auto& res,
985                  QM_OR_ELSE_LOG_VERBOSE_IF(
986                      // Expression.
987                      MOZ_TO_RESULT_INVOKE(aFile, IsDirectory)
988                          .map([](const bool isDirectory) {
989                            return isDirectory ? ExistsAsFileResult::IsDirectory
990                                               : ExistsAsFileResult::IsFile;
991                          }),
992                      // Predicate.
993                      ([](const nsresult rv) {
994                        return rv == NS_ERROR_FILE_NOT_FOUND ||
995                               rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
996                      }),
997                      // Fallback.
998                      ErrToOk<ExistsAsFileResult::DoesNotExist>));
999 
1000   QM_TRY(OkIf(res != ExistsAsFileResult::IsDirectory), Err(NS_ERROR_FAILURE));
1001 
1002   return res == ExistsAsFileResult::IsFile;
1003 }
1004 
UpdateUsageFile(nsIFile * aUsageFile,nsIFile * aUsageJournalFile,int64_t aUsage)1005 nsresult UpdateUsageFile(nsIFile* aUsageFile, nsIFile* aUsageJournalFile,
1006                          int64_t aUsage) {
1007   MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
1008   MOZ_ASSERT(aUsageFile);
1009   MOZ_ASSERT(aUsageJournalFile);
1010   MOZ_ASSERT(aUsage >= 0);
1011 
1012   QM_TRY_INSPECT(const bool& usageJournalFileExists,
1013                  ExistsAsFile(*aUsageJournalFile));
1014   if (!usageJournalFileExists) {
1015     QM_TRY(aUsageJournalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644));
1016   }
1017 
1018   QM_TRY_INSPECT(const auto& stream, NS_NewLocalFileOutputStream(aUsageFile));
1019 
1020   nsCOMPtr<nsIBinaryOutputStream> binaryStream =
1021       NS_NewObjectOutputStream(stream);
1022 
1023   QM_TRY(binaryStream->Write32(kUsageFileCookie));
1024 
1025   QM_TRY(binaryStream->Write64(aUsage));
1026 
1027 #if defined(EARLY_BETA_OR_EARLIER) || defined(DEBUG)
1028   QM_TRY(stream->Flush());
1029 #endif
1030 
1031   QM_TRY(stream->Close());
1032 
1033   return NS_OK;
1034 }
1035 
LoadUsageFile(nsIFile & aUsageFile)1036 Result<UsageInfo, nsresult> LoadUsageFile(nsIFile& aUsageFile) {
1037   AssertIsOnIOThread();
1038 
1039   QM_TRY_INSPECT(const int64_t& fileSize,
1040                  MOZ_TO_RESULT_INVOKE(aUsageFile, GetFileSize));
1041 
1042   QM_TRY(OkIf(fileSize == kUsageFileSize), Err(NS_ERROR_FILE_CORRUPTED));
1043 
1044   QM_TRY_UNWRAP(auto stream, NS_NewLocalFileInputStream(&aUsageFile));
1045 
1046   QM_TRY_INSPECT(const auto& bufferedStream,
1047                  NS_NewBufferedInputStream(stream.forget(), 16));
1048 
1049   const nsCOMPtr<nsIBinaryInputStream> binaryStream =
1050       NS_NewObjectInputStream(bufferedStream);
1051 
1052   QM_TRY_INSPECT(const uint32_t& cookie,
1053                  MOZ_TO_RESULT_INVOKE(binaryStream, Read32));
1054 
1055   QM_TRY(OkIf(cookie == kUsageFileCookie), Err(NS_ERROR_FILE_CORRUPTED));
1056 
1057   QM_TRY_INSPECT(const uint64_t& usage,
1058                  MOZ_TO_RESULT_INVOKE(binaryStream, Read64));
1059 
1060   return UsageInfo{DatabaseUsageType(Some(usage))};
1061 }
1062 
1063 /*******************************************************************************
1064  * Non-actor class declarations
1065  ******************************************************************************/
1066 
1067 /**
1068  * Coalescing manipulation queue used by `Datastore`.  Used by `Datastore` to
1069  * update `Datastore::mOrderedItems` efficiently/for code simplification.
1070  * (Datastore does not actually depend on the coalescing, as mutations are
1071  * applied atomically when a Snapshot Checkpoints, and with `Datastore::mValues`
1072  * being updated at the same time the mutations are applied to Datastore's
1073  * mWriteOptimizer.)
1074  */
1075 class DatastoreWriteOptimizer final : public LSWriteOptimizer<LSValue> {
1076  public:
1077   void ApplyAndReset(nsTArray<LSItemInfo>& aOrderedItems);
1078 };
1079 
1080 /**
1081  * Coalescing manipulation queue used by `Connection`.  Used by `Connection` to
1082  * buffer and coalesce manipulations applied to the Datastore in batches by
1083  * Snapshot Checkpointing until flushed to disk.
1084  */
1085 class ConnectionWriteOptimizer final : public LSWriteOptimizer<LSValue> {
1086  public:
1087   // Returns the usage as the success value.
1088   Result<int64_t, nsresult> Perform(Connection* aConnection,
1089                                     bool aShadowWrites);
1090 
1091  private:
1092   /**
1093    * Handlers for specific mutations.  Each method knows how to `Perform` the
1094    * manipulation against a `Connection` and the "shadow" database (legacy
1095    * webappsstore.sqlite database that exists so LSNG can be disabled/safely
1096    * downgraded from.)
1097    */
1098   nsresult PerformInsertOrUpdate(Connection* aConnection, bool aShadowWrites,
1099                                  const nsAString& aKey, const LSValue& aValue);
1100 
1101   nsresult PerformDelete(Connection* aConnection, bool aShadowWrites,
1102                          const nsAString& aKey);
1103 
1104   nsresult PerformTruncate(Connection* aConnection, bool aShadowWrites);
1105 };
1106 
1107 class DatastoreOperationBase : public Runnable {
1108   nsCOMPtr<nsIEventTarget> mOwningEventTarget;
1109   nsresult mResultCode;
1110   Atomic<bool> mMayProceedOnNonOwningThread;
1111   bool mMayProceed;
1112 
1113  public:
OwningEventTarget() const1114   nsIEventTarget* OwningEventTarget() const {
1115     MOZ_ASSERT(mOwningEventTarget);
1116 
1117     return mOwningEventTarget;
1118   }
1119 
IsOnOwningThread() const1120   bool IsOnOwningThread() const {
1121     MOZ_ASSERT(mOwningEventTarget);
1122 
1123     bool current;
1124     return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
1125            current;
1126   }
1127 
AssertIsOnOwningThread() const1128   void AssertIsOnOwningThread() const {
1129     MOZ_ASSERT(IsOnBackgroundThread());
1130     MOZ_ASSERT(IsOnOwningThread());
1131   }
1132 
ResultCode() const1133   nsresult ResultCode() const { return mResultCode; }
1134 
SetFailureCode(nsresult aErrorCode)1135   void SetFailureCode(nsresult aErrorCode) {
1136     MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
1137     MOZ_ASSERT(NS_FAILED(aErrorCode));
1138 
1139     mResultCode = aErrorCode;
1140   }
1141 
MaybeSetFailureCode(nsresult aErrorCode)1142   void MaybeSetFailureCode(nsresult aErrorCode) {
1143     MOZ_ASSERT(NS_FAILED(aErrorCode));
1144 
1145     if (NS_SUCCEEDED(mResultCode)) {
1146       mResultCode = aErrorCode;
1147     }
1148   }
1149 
NoteComplete()1150   void NoteComplete() {
1151     AssertIsOnOwningThread();
1152 
1153     mMayProceed = false;
1154     mMayProceedOnNonOwningThread = false;
1155   }
1156 
MayProceed() const1157   bool MayProceed() const {
1158     AssertIsOnOwningThread();
1159 
1160     return mMayProceed;
1161   }
1162 
1163   // May be called on any thread, but you should call MayProceed() if you know
1164   // you're on the background thread because it is slightly faster.
MayProceedOnNonOwningThread() const1165   bool MayProceedOnNonOwningThread() const {
1166     return mMayProceedOnNonOwningThread;
1167   }
1168 
1169  protected:
DatastoreOperationBase()1170   DatastoreOperationBase()
1171       : Runnable("dom::DatastoreOperationBase"),
1172         mOwningEventTarget(GetCurrentEventTarget()),
1173         mResultCode(NS_OK),
1174         mMayProceedOnNonOwningThread(true),
1175         mMayProceed(true) {}
1176 
~DatastoreOperationBase()1177   ~DatastoreOperationBase() override { MOZ_ASSERT(!mMayProceed); }
1178 };
1179 
1180 class ConnectionDatastoreOperationBase : public DatastoreOperationBase {
1181  protected:
1182   RefPtr<Connection> mConnection;
1183   /**
1184    * This boolean flag is used by the CloseOp to avoid creating empty databases.
1185    */
1186   const bool mEnsureStorageConnection;
1187 
1188  public:
1189   // This callback will be called on the background thread before releasing the
1190   // final reference to this request object. Subclasses may perform any
1191   // additional cleanup here but must always call the base class implementation.
1192   virtual void Cleanup();
1193 
1194  protected:
1195   ConnectionDatastoreOperationBase(Connection* aConnection,
1196                                    bool aEnsureStorageConnection = true);
1197 
1198   ~ConnectionDatastoreOperationBase();
1199 
1200   // Must be overridden in subclasses. Called on the target thread to allow the
1201   // subclass to perform necessary datastore operations. A successful return
1202   // value will trigger an OnSuccess callback on the background thread while
1203   // while a failure value will trigger an OnFailure callback.
1204   virtual nsresult DoDatastoreWork() = 0;
1205 
1206   // Methods that subclasses may implement.
1207   virtual void OnSuccess();
1208 
1209   virtual void OnFailure(nsresult aResultCode);
1210 
1211  private:
1212   void RunOnConnectionThread();
1213 
1214   void RunOnOwningThread();
1215 
1216   // Not to be overridden by subclasses.
1217   NS_DECL_NSIRUNNABLE
1218 };
1219 
1220 class Connection final : public CachingDatabaseConnection {
1221   friend class ConnectionThread;
1222 
1223   class InitTemporaryOriginHelper;
1224 
1225   class FlushOp;
1226   class CloseOp;
1227 
1228   RefPtr<ConnectionThread> mConnectionThread;
1229   RefPtr<QuotaClient> mQuotaClient;
1230   nsCOMPtr<nsITimer> mFlushTimer;
1231   UniquePtr<ArchivedOriginScope> mArchivedOriginScope;
1232   ConnectionWriteOptimizer mWriteOptimizer;
1233   // XXX Consider changing this to ClientMetadata.
1234   const OriginMetadata mOriginMetadata;
1235   nsString mDirectoryPath;
1236   /**
1237    * Propagated from PrepareDatastoreOp. PrepareDatastoreOp may defer the
1238    * creation of the localstorage client directory and database on the
1239    * QuotaManager IO thread in its DatabaseWork method to
1240    * Connection::EnsureStorageConnection, in which case the method needs to know
1241    * it is responsible for taking those actions (without redundantly performing
1242    * the existence checks).
1243    */
1244   const bool mDatabaseWasNotAvailable;
1245   bool mHasCreatedDatabase;
1246   bool mFlushScheduled;
1247 #ifdef DEBUG
1248   bool mInUpdateBatch;
1249   bool mFinished;
1250 #endif
1251 
1252  public:
NS_INLINE_DECL_REFCOUNTING(mozilla::dom::Connection)1253   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::Connection)
1254 
1255   void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(Connection); }
1256 
GetQuotaClient() const1257   QuotaClient* GetQuotaClient() const {
1258     MOZ_ASSERT(mQuotaClient);
1259 
1260     return mQuotaClient;
1261   }
1262 
GetArchivedOriginScope() const1263   ArchivedOriginScope* GetArchivedOriginScope() const {
1264     return mArchivedOriginScope.get();
1265   }
1266 
Origin() const1267   const nsCString& Origin() const { return mOriginMetadata.mOrigin; }
1268 
DirectoryPath() const1269   const nsString& DirectoryPath() const { return mDirectoryPath; }
1270 
GetFinishInfo(bool & aDatabaseWasNotAvailable,bool & aHasCreatedDatabase) const1271   void GetFinishInfo(bool& aDatabaseWasNotAvailable,
1272                      bool& aHasCreatedDatabase) const {
1273     AssertIsOnOwningThread();
1274     MOZ_ASSERT(mFinished);
1275 
1276     aDatabaseWasNotAvailable = mDatabaseWasNotAvailable;
1277     aHasCreatedDatabase = mHasCreatedDatabase;
1278   }
1279 
1280   //////////////////////////////////////////////////////////////////////////////
1281   // Methods which can only be called on the owning thread.
1282 
1283   // This method is used to asynchronously execute a connection datastore
1284   // operation on the connection thread.
1285   void Dispatch(ConnectionDatastoreOperationBase* aOp);
1286 
1287   // This method is used to asynchronously close the storage connection on the
1288   // connection thread.
1289   void Close(nsIRunnable* aCallback);
1290 
1291   void SetItem(const nsString& aKey, const LSValue& aValue, int64_t aDelta,
1292                bool aIsNewItem);
1293 
1294   void RemoveItem(const nsString& aKey, int64_t aDelta);
1295 
1296   void Clear(int64_t aDelta);
1297 
1298   void BeginUpdateBatch();
1299 
1300   void EndUpdateBatch();
1301 
1302   //////////////////////////////////////////////////////////////////////////////
1303   // Methods which can only be called on the connection thread.
1304 
1305   nsresult EnsureStorageConnection();
1306 
StorageConnection() const1307   mozIStorageConnection* StorageConnection() const {
1308     AssertIsOnGlobalConnectionThread();
1309 
1310     return &MutableStorageConnection();
1311   }
1312 
1313   void CloseStorageConnection();
1314 
1315   nsresult BeginWriteTransaction();
1316 
1317   nsresult CommitWriteTransaction();
1318 
1319   nsresult RollbackWriteTransaction();
1320 
1321  private:
1322   // Only created by ConnectionThread.
1323   Connection(ConnectionThread* aConnectionThread,
1324              const OriginMetadata& aOriginMetadata,
1325              UniquePtr<ArchivedOriginScope>&& aArchivedOriginScope,
1326              bool aDatabaseWasNotAvailable);
1327 
1328   ~Connection();
1329 
1330   void ScheduleFlush();
1331 
1332   void Flush();
1333 
1334   static void FlushTimerCallback(nsITimer* aTimer, void* aClosure);
1335 };
1336 
1337 /**
1338  * Helper to invoke EnsureTemporaryOriginIsInitialized on the QuotaManager IO
1339  * thread from the LocalStorage connection thread when creating a database
1340  * connection on demand. This is necessary because we attempt to defer the
1341  * creation of the origin directory and the database until absolutely needed,
1342  * but the directory creation and origin initialization must happen on the QM
1343  * IO thread for invariant reasons. (We can't just use a mutex because there
1344  * could be logic on the IO thread that also wants to deal with the same
1345  * origin, so we need to queue a runnable and wait our turn.)
1346  */
1347 class Connection::InitTemporaryOriginHelper final : public Runnable {
1348   mozilla::Monitor mMonitor;
1349   const OriginMetadata mOriginMetadata;
1350   nsString mOriginDirectoryPath;
1351   nsresult mIOThreadResultCode;
1352   bool mWaiting;
1353 
1354  public:
InitTemporaryOriginHelper(const OriginMetadata & aOriginMetadata)1355   explicit InitTemporaryOriginHelper(const OriginMetadata& aOriginMetadata)
1356       : Runnable("dom::localstorage::Connection::InitTemporaryOriginHelper"),
1357         mMonitor("InitTemporaryOriginHelper::mMonitor"),
1358         mOriginMetadata(aOriginMetadata),
1359         mIOThreadResultCode(NS_OK),
1360         mWaiting(true) {
1361     AssertIsOnGlobalConnectionThread();
1362   }
1363 
1364   Result<nsString, nsresult> BlockAndReturnOriginDirectoryPath();
1365 
1366  private:
1367   ~InitTemporaryOriginHelper() = default;
1368 
1369   nsresult RunOnIOThread();
1370 
1371   NS_DECL_NSIRUNNABLE
1372 };
1373 
1374 class Connection::FlushOp final : public ConnectionDatastoreOperationBase {
1375   ConnectionWriteOptimizer mWriteOptimizer;
1376   bool mShadowWrites;
1377 
1378  public:
1379   FlushOp(Connection* aConnection, ConnectionWriteOptimizer&& aWriteOptimizer);
1380 
1381  private:
1382   nsresult DoDatastoreWork() override;
1383 
1384   void Cleanup() override;
1385 };
1386 
1387 class Connection::CloseOp final : public ConnectionDatastoreOperationBase {
1388   nsCOMPtr<nsIRunnable> mCallback;
1389 
1390  public:
CloseOp(Connection * aConnection,nsIRunnable * aCallback)1391   CloseOp(Connection* aConnection, nsIRunnable* aCallback)
1392       : ConnectionDatastoreOperationBase(aConnection,
1393                                          /* aEnsureStorageConnection */ false),
1394         mCallback(aCallback) {}
1395 
1396  private:
1397   nsresult DoDatastoreWork() override;
1398 
1399   void Cleanup() override;
1400 };
1401 
1402 class ConnectionThread final {
1403   friend class Connection;
1404 
1405   nsCOMPtr<nsIThread> mThread;
1406   nsRefPtrHashtable<nsCStringHashKey, Connection> mConnections;
1407 
1408  public:
1409   ConnectionThread();
1410 
AssertIsOnOwningThread() const1411   void AssertIsOnOwningThread() const {
1412     NS_ASSERT_OWNINGTHREAD(ConnectionThread);
1413   }
1414 
1415   bool IsOnConnectionThread();
1416 
1417   void AssertIsOnConnectionThread();
1418 
1419   already_AddRefed<Connection> CreateConnection(
1420       const OriginMetadata& aOriginMetadata,
1421       UniquePtr<ArchivedOriginScope>&& aArchivedOriginScope,
1422       bool aDatabaseWasNotAvailable);
1423 
1424   void Shutdown();
1425 
1426   NS_INLINE_DECL_REFCOUNTING(ConnectionThread)
1427 
1428  private:
1429   ~ConnectionThread();
1430 };
1431 
1432 /**
1433  * Canonical state of Storage for an origin, containing all keys and their
1434  * values in the parent process.  Specifically, this is the state that will
1435  * be handed out to freshly created Snapshots and that will be persisted to disk
1436  * when the Connection's flush completes.  State is mutated in batches as
1437  * Snapshot instances Checkpoint their mutations locally accumulated in the
1438  * child LSSnapshots.
1439  */
1440 class Datastore final
1441     : public SupportsCheckedUnsafePtr<CheckIf<DiagnosticAssertEnabled>> {
1442   RefPtr<DirectoryLock> mDirectoryLock;
1443   RefPtr<Connection> mConnection;
1444   RefPtr<QuotaObject> mQuotaObject;
1445   nsCOMPtr<nsIRunnable> mCompleteCallback;
1446   /**
1447    * PrepareDatastoreOps register themselves with the Datastore at
1448    * and unregister in PrepareDatastoreOp::Cleanup.
1449    */
1450   nsTHashSet<PrepareDatastoreOp*> mPrepareDatastoreOps;
1451   /**
1452    * PreparedDatastore instances register themselves with their associated
1453    * Datastore at construction time and unregister at destruction time.  They
1454    * hang around for kPreparedDatastoreTimeoutMs in order to keep the Datastore
1455    * from closing itself via MaybeClose(), thereby giving the document enough
1456    * time to load and access LocalStorage.
1457    */
1458   nsTHashSet<PreparedDatastore*> mPreparedDatastores;
1459   /**
1460    * A database is live (and in this hashtable) if it has a live LSDatabase
1461    * actor.  There is at most one Database per origin per content process.  Each
1462    * Database corresponds to an LSDatabase in its associated content process.
1463    */
1464   nsTHashSet<Database*> mDatabases;
1465   /**
1466    * A database is active if it has a non-null `mSnapshot`.  As long as there
1467    * are any active databases final deltas can't be calculated and
1468    * `UpdateUsage()` can't be invoked.
1469    */
1470   nsTHashSet<Database*> mActiveDatabases;
1471   /**
1472    * Non-authoritative hashtable representation of mOrderedItems for efficient
1473    * lookup.
1474    */
1475   nsTHashMap<nsStringHashKey, LSValue> mValues;
1476   /**
1477    * The authoritative ordered state of the Datastore; mValue also exists as an
1478    * unordered hashtable for efficient lookup.
1479    */
1480   nsTArray<LSItemInfo> mOrderedItems;
1481   nsTArray<int64_t> mPendingUsageDeltas;
1482   DatastoreWriteOptimizer mWriteOptimizer;
1483   const OriginMetadata mOriginMetadata;
1484   const uint32_t mPrivateBrowsingId;
1485   int64_t mUsage;
1486   int64_t mUpdateBatchUsage;
1487   int64_t mSizeOfKeys;
1488   int64_t mSizeOfItems;
1489   bool mClosed;
1490   bool mInUpdateBatch;
1491   bool mHasLivePrivateDatastore;
1492 
1493  public:
1494   // Created by PrepareDatastoreOp.
1495   Datastore(const OriginMetadata& aOriginMetadata, uint32_t aPrivateBrowsingId,
1496             int64_t aUsage, int64_t aSizeOfKeys, int64_t aSizeOfItems,
1497             RefPtr<DirectoryLock>&& aDirectoryLock,
1498             RefPtr<Connection>&& aConnection,
1499             RefPtr<QuotaObject>&& aQuotaObject,
1500             nsTHashMap<nsStringHashKey, LSValue>& aValues,
1501             nsTArray<LSItemInfo>&& aOrderedItems);
1502 
MaybeDirectoryLockRef() const1503   Maybe<DirectoryLock&> MaybeDirectoryLockRef() const {
1504     AssertIsOnBackgroundThread();
1505 
1506     return ToMaybeRef(mDirectoryLock.get());
1507   }
1508 
Origin() const1509   const nsCString& Origin() const { return mOriginMetadata.mOrigin; }
1510 
PrivateBrowsingId() const1511   uint32_t PrivateBrowsingId() const { return mPrivateBrowsingId; }
1512 
IsPersistent() const1513   bool IsPersistent() const {
1514     // Private-browsing is forbidden from touching disk, but
1515     // StorageAccess::eSessionScoped is allowed to touch disk because
1516     // QuotaManager's storage for such origins is wiped at shutdown.
1517     return mPrivateBrowsingId == 0;
1518   }
1519 
1520   void Close();
1521 
IsClosed() const1522   bool IsClosed() const {
1523     AssertIsOnBackgroundThread();
1524 
1525     return mClosed;
1526   }
1527 
1528   void WaitForConnectionToComplete(nsIRunnable* aCallback);
1529 
1530   void NoteLivePrepareDatastoreOp(PrepareDatastoreOp* aPrepareDatastoreOp);
1531 
1532   void NoteFinishedPrepareDatastoreOp(PrepareDatastoreOp* aPrepareDatastoreOp);
1533 
1534   void NoteLivePrivateDatastore();
1535 
1536   void NoteFinishedPrivateDatastore();
1537 
1538   void NoteLivePreparedDatastore(PreparedDatastore* aPreparedDatastore);
1539 
1540   void NoteFinishedPreparedDatastore(PreparedDatastore* aPreparedDatastore);
1541 
1542   void NoteLiveDatabase(Database* aDatabase);
1543 
1544   void NoteFinishedDatabase(Database* aDatabase);
1545 
1546   void NoteActiveDatabase(Database* aDatabase);
1547 
1548   void NoteInactiveDatabase(Database* aDatabase);
1549 
1550   void GetSnapshotLoadInfo(const nsString& aKey, bool& aAddKeyToUnknownItems,
1551                            nsTHashtable<nsStringHashKey>& aLoadedItems,
1552                            nsTArray<LSItemInfo>& aItemInfos,
1553                            uint32_t& aNextLoadIndex,
1554                            LSSnapshot::LoadState& aLoadState);
1555 
GetLength() const1556   uint32_t GetLength() const { return mValues.Count(); }
1557 
GetOrderedItems() const1558   const nsTArray<LSItemInfo>& GetOrderedItems() const { return mOrderedItems; }
1559 
1560   void GetItem(const nsString& aKey, LSValue& aValue) const;
1561 
1562   void GetKeys(nsTArray<nsString>& aKeys) const;
1563 
1564   //////////////////////////////////////////////////////////////////////////////
1565   // Mutation Methods
1566   //
1567   // These are only called during Snapshot::RecvCheckpoint
1568 
1569   /**
1570    * Used by Snapshot::RecvCheckpoint to set a key/value pair as part of a an
1571    * explicit batch.
1572    */
1573   void SetItem(Database* aDatabase, const nsString& aKey,
1574                const LSValue& aValue);
1575 
1576   void RemoveItem(Database* aDatabase, const nsString& aKey);
1577 
1578   void Clear(Database* aDatabase);
1579 
1580   void BeginUpdateBatch(int64_t aSnapshotInitialUsage);
1581 
1582   int64_t EndUpdateBatch(int64_t aSnapshotPeakUsage);
1583 
GetUsage() const1584   int64_t GetUsage() const { return mUsage; }
1585 
1586   int64_t RequestUpdateUsage(int64_t aRequestedSize, int64_t aMinSize);
1587 
1588   bool HasOtherProcessObservers(Database* aDatabase);
1589 
1590   void NotifyOtherProcessObservers(Database* aDatabase,
1591                                    const nsString& aDocumentURI,
1592                                    const nsString& aKey,
1593                                    const LSValue& aOldValue,
1594                                    const LSValue& aNewValue);
1595 
1596   void NoteChangedObserverArray(const nsTArray<NotNull<Observer*>>& aObservers);
1597 
1598   void Stringify(nsACString& aResult) const;
1599 
1600   NS_INLINE_DECL_REFCOUNTING(Datastore)
1601 
1602  private:
1603   // Reference counted.
1604   ~Datastore();
1605 
1606   bool UpdateUsage(int64_t aDelta);
1607 
1608   void MaybeClose();
1609 
1610   void ConnectionClosedCallback();
1611 
1612   void CleanupMetadata();
1613 
1614   void NotifySnapshots(Database* aDatabase, const nsAString& aKey,
1615                        const LSValue& aOldValue, bool aAffectsOrder);
1616 };
1617 
1618 class PrivateDatastore {
1619   const NotNull<RefPtr<Datastore>> mDatastore;
1620 
1621  public:
PrivateDatastore(MovingNotNull<RefPtr<Datastore>> aDatastore)1622   explicit PrivateDatastore(MovingNotNull<RefPtr<Datastore>> aDatastore)
1623       : mDatastore(std::move(aDatastore)) {
1624     AssertIsOnBackgroundThread();
1625 
1626     mDatastore->NoteLivePrivateDatastore();
1627   }
1628 
~PrivateDatastore()1629   ~PrivateDatastore() { mDatastore->NoteFinishedPrivateDatastore(); }
1630 
DatastoreRef() const1631   const Datastore& DatastoreRef() const {
1632     AssertIsOnBackgroundThread();
1633 
1634     return *mDatastore;
1635   }
1636 };
1637 
1638 class PreparedDatastore {
1639   RefPtr<Datastore> mDatastore;
1640   nsCOMPtr<nsITimer> mTimer;
1641   const Maybe<ContentParentId> mContentParentId;
1642   // Strings share buffers if possible, so it's not a problem to duplicate the
1643   // origin here.
1644   const nsCString mOrigin;
1645   uint64_t mDatastoreId;
1646   bool mForPreload;
1647   bool mInvalidated;
1648 
1649  public:
PreparedDatastore(Datastore * aDatastore,const Maybe<ContentParentId> & aContentParentId,const nsACString & aOrigin,uint64_t aDatastoreId,bool aForPreload)1650   PreparedDatastore(Datastore* aDatastore,
1651                     const Maybe<ContentParentId>& aContentParentId,
1652                     const nsACString& aOrigin, uint64_t aDatastoreId,
1653                     bool aForPreload)
1654       : mDatastore(aDatastore),
1655         mTimer(NS_NewTimer()),
1656         mContentParentId(aContentParentId),
1657         mOrigin(aOrigin),
1658         mDatastoreId(aDatastoreId),
1659         mForPreload(aForPreload),
1660         mInvalidated(false) {
1661     AssertIsOnBackgroundThread();
1662     MOZ_ASSERT(aDatastore);
1663     MOZ_ASSERT(mTimer);
1664 
1665     aDatastore->NoteLivePreparedDatastore(this);
1666 
1667     MOZ_ALWAYS_SUCCEEDS(mTimer->InitWithNamedFuncCallback(
1668         TimerCallback, this, kPreparedDatastoreTimeoutMs,
1669         nsITimer::TYPE_ONE_SHOT, "PreparedDatastore::TimerCallback"));
1670   }
1671 
~PreparedDatastore()1672   ~PreparedDatastore() {
1673     MOZ_ASSERT(mDatastore);
1674     MOZ_ASSERT(mTimer);
1675 
1676     mTimer->Cancel();
1677 
1678     mDatastore->NoteFinishedPreparedDatastore(this);
1679   }
1680 
DatastoreRef() const1681   const Datastore& DatastoreRef() const {
1682     AssertIsOnBackgroundThread();
1683     MOZ_ASSERT(mDatastore);
1684 
1685     return *mDatastore;
1686   }
1687 
MutableDatastoreRef() const1688   Datastore& MutableDatastoreRef() const {
1689     AssertIsOnBackgroundThread();
1690     MOZ_ASSERT(mDatastore);
1691 
1692     return *mDatastore;
1693   }
1694 
GetContentParentId() const1695   const Maybe<ContentParentId>& GetContentParentId() const {
1696     return mContentParentId;
1697   }
1698 
Origin() const1699   const nsCString& Origin() const { return mOrigin; }
1700 
Invalidate()1701   void Invalidate() {
1702     AssertIsOnBackgroundThread();
1703 
1704     mInvalidated = true;
1705 
1706     if (mForPreload) {
1707       mTimer->Cancel();
1708 
1709       MOZ_ALWAYS_SUCCEEDS(mTimer->InitWithNamedFuncCallback(
1710           TimerCallback, this, 0, nsITimer::TYPE_ONE_SHOT,
1711           "PreparedDatastore::TimerCallback"));
1712     }
1713   }
1714 
IsInvalidated() const1715   bool IsInvalidated() const {
1716     AssertIsOnBackgroundThread();
1717 
1718     return mInvalidated;
1719   }
1720 
1721  private:
1722   void Destroy();
1723 
1724   static void TimerCallback(nsITimer* aTimer, void* aClosure);
1725 };
1726 
1727 /*******************************************************************************
1728  * Actor class declarations
1729  ******************************************************************************/
1730 
1731 class Database final
1732     : public PBackgroundLSDatabaseParent,
1733       public SupportsCheckedUnsafePtr<CheckIf<DiagnosticAssertEnabled>> {
1734   RefPtr<Datastore> mDatastore;
1735   Snapshot* mSnapshot;
1736   const PrincipalInfo mPrincipalInfo;
1737   const Maybe<ContentParentId> mContentParentId;
1738   // Strings share buffers if possible, so it's not a problem to duplicate the
1739   // origin here.
1740   nsCString mOrigin;
1741   uint32_t mPrivateBrowsingId;
1742   bool mAllowedToClose;
1743   bool mActorDestroyed;
1744   bool mRequestedAllowToClose;
1745 #ifdef DEBUG
1746   bool mActorWasAlive;
1747 #endif
1748 
1749  public:
1750   // Created in AllocPBackgroundLSDatabaseParent.
1751   Database(const PrincipalInfo& aPrincipalInfo,
1752            const Maybe<ContentParentId>& aContentParentId,
1753            const nsACString& aOrigin, uint32_t aPrivateBrowsingId);
1754 
GetDatastore() const1755   Datastore* GetDatastore() const {
1756     AssertIsOnBackgroundThread();
1757     return mDatastore;
1758   }
1759 
MaybeDatastoreRef() const1760   Maybe<Datastore&> MaybeDatastoreRef() const {
1761     AssertIsOnBackgroundThread();
1762 
1763     return ToMaybeRef(mDatastore.get());
1764   }
1765 
GetPrincipalInfo() const1766   const PrincipalInfo& GetPrincipalInfo() const { return mPrincipalInfo; }
1767 
IsOwnedByProcess(ContentParentId aContentParentId) const1768   bool IsOwnedByProcess(ContentParentId aContentParentId) const {
1769     return mContentParentId && mContentParentId.value() == aContentParentId;
1770   }
1771 
PrivateBrowsingId() const1772   uint32_t PrivateBrowsingId() const { return mPrivateBrowsingId; }
1773 
Origin() const1774   const nsCString& Origin() const { return mOrigin; }
1775 
1776   void SetActorAlive(Datastore* aDatastore);
1777 
1778   void RegisterSnapshot(Snapshot* aSnapshot);
1779 
1780   void UnregisterSnapshot(Snapshot* aSnapshot);
1781 
GetSnapshot() const1782   Snapshot* GetSnapshot() const {
1783     AssertIsOnBackgroundThread();
1784     return mSnapshot;
1785   }
1786 
1787   void RequestAllowToClose();
1788 
1789   void ForceKill();
1790 
1791   void Stringify(nsACString& aResult) const;
1792 
1793   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::Database)
1794 
1795  private:
1796   // Reference counted.
1797   ~Database();
1798 
1799   void AllowToClose();
1800 
1801   // IPDL methods are only called by IPDL.
1802   void ActorDestroy(ActorDestroyReason aWhy) override;
1803 
1804   mozilla::ipc::IPCResult RecvDeleteMe() override;
1805 
1806   mozilla::ipc::IPCResult RecvAllowToClose() override;
1807 
1808   PBackgroundLSSnapshotParent* AllocPBackgroundLSSnapshotParent(
1809       const nsString& aDocumentURI, const nsString& aKey,
1810       const bool& aIncreasePeakUsage, const int64_t& aRequestedSize,
1811       const int64_t& aMinSize, LSSnapshotInitInfo* aInitInfo) override;
1812 
1813   mozilla::ipc::IPCResult RecvPBackgroundLSSnapshotConstructor(
1814       PBackgroundLSSnapshotParent* aActor, const nsString& aDocumentURI,
1815       const nsString& aKey, const bool& aIncreasePeakUsage,
1816       const int64_t& aRequestedSize, const int64_t& aMinSize,
1817       LSSnapshotInitInfo* aInitInfo) override;
1818 
1819   bool DeallocPBackgroundLSSnapshotParent(
1820       PBackgroundLSSnapshotParent* aActor) override;
1821 };
1822 
1823 /**
1824  * Attempts to capture the state of the underlying Datastore at the time of its
1825  * creation so run-to-completion semantics can be honored.
1826  *
1827  * Rather than simply duplicate the contents of `DataStore::mValues` and
1828  * `Datastore::mOrderedItems` at the time of their creation, the Snapshot tracks
1829  * mutations to the Datastore as they happen, saving off the state of values as
1830  * they existed when the Snapshot was created.  In other words, given an initial
1831  * Datastore state of { foo: 'bar', bar: 'baz' }, the Snapshot won't store those
1832  * values until it hears via `SaveItem` that "foo" is being over-written.  At
1833  * that time, it will save off foo='bar' in mValues.
1834  *
1835  * ## Quota Allocation ##
1836  *
1837  * ## States ##
1838  *
1839  */
1840 class Snapshot final : public PBackgroundLSSnapshotParent {
1841   /**
1842    * The Database that owns this snapshot.  There is a 1:1 relationship between
1843    * snapshots and databases.
1844    */
1845   RefPtr<Database> mDatabase;
1846   RefPtr<Datastore> mDatastore;
1847   /**
1848    * The set of keys for which values have been sent to the child LSSnapshot.
1849    * Cleared once all values have been sent as indicated by
1850    * mLoadedItems.Count()==mTotalLength and therefore mLoadedAllItems should be
1851    * true.  No requests should be received for keys already in this set, and
1852    * this is enforced by fatal IPC error (unless fuzzing).
1853    */
1854   nsTHashtable<nsStringHashKey> mLoadedItems;
1855   /**
1856    * The set of keys for which a RecvLoadValueAndMoreItems request was received
1857    * but there was no such key, and so null was returned.  The child LSSnapshot
1858    * will also cache these values, so redundant requests are also handled with
1859    * fatal process termination just like for mLoadedItems.  Also cleared when
1860    * mLoadedAllItems becomes true because then the child can infer that all
1861    * other values must be null.  (Note: this could also be done when
1862    * mLoadKeysReceived is true as a further optimization, but is not.)
1863    */
1864   nsTHashSet<nsString> mUnknownItems;
1865   /**
1866    * Values that have changed in mDatastore as reported by SaveItem
1867    * notifications that are not yet known to the child LSSnapshot.
1868    *
1869    * The naive way to snapshot the state of mDatastore would be to duplicate its
1870    * internal mValues at the time of our creation, but that is wasteful if few
1871    * changes are made to the Datastore's state.  So we only track values that
1872    * are changed/evicted from the Datastore as they happen, as reported to us by
1873    * SaveItem notifications.
1874    */
1875   nsTHashMap<nsStringHashKey, LSValue> mValues;
1876   /**
1877    * Latched state of mDatastore's keys during a SaveItem notification with
1878    * aAffectsOrder=true.  The ordered keys needed to be saved off so that a
1879    * consistent ordering could be presented to the child LSSnapshot when it asks
1880    * for them via RecvLoadKeys.
1881    */
1882   nsTArray<nsString> mKeys;
1883   nsString mDocumentURI;
1884   /**
1885    * The index used for restoring iteration over not yet sent key/value pairs to
1886    * the child LSSnapshot.
1887    */
1888   uint32_t mNextLoadIndex;
1889   /**
1890    * The number of key/value pairs that were present in the Datastore at the
1891    * time the snapshot was created.  Once we have sent this many values to the
1892    * child LSSnapshot, we can infer that it has received all of the keys/values
1893    * and set mLoadedAllItems to true and clear mLoadedItems and mUnknownItems.
1894    * Note that knowing the keys/values is not the same as knowing their ordering
1895    * and so mKeys may be retained.
1896    */
1897   uint32_t mTotalLength;
1898   int64_t mUsage;
1899   int64_t mPeakUsage;
1900   /**
1901    * True if SaveItem has saved mDatastore's keys into mKeys because a SaveItem
1902    * notification with aAffectsOrder=true was received.
1903    */
1904   bool mSavedKeys;
1905   bool mActorDestroyed;
1906   bool mFinishReceived;
1907   bool mLoadedReceived;
1908   /**
1909    * True if LSSnapshot's mLoadState should be LoadState::AllOrderedItems or
1910    * LoadState::AllUnorderedItems.  It will be AllOrderedItems if the initial
1911    * snapshot contained all the data or if the state was AllOrderedKeys and
1912    * successive RecvLoadValueAndMoreItems requests have resulted in the
1913    * LSSnapshot being told all of the key/value pairs.  It will be
1914    * AllUnorderedItems if the state was LoadState::Partial and successive
1915    * RecvLoadValueAndMoreItem requests got all the keys/values but the key
1916    * ordering was not retrieved.
1917    */
1918   bool mLoadedAllItems;
1919   /**
1920    * True if LSSnapshot's mLoadState should be LoadState::AllOrderedItems or
1921    * AllOrderedKeys.  This can occur because of the initial snapshot, or because
1922    * a RecvLoadKeys request was received.
1923    */
1924   bool mLoadKeysReceived;
1925   bool mSentMarkDirty;
1926 
1927   bool mHasOtherProcessObservers;
1928 
1929  public:
1930   // Created in AllocPBackgroundLSSnapshotParent.
1931   Snapshot(Database* aDatabase, const nsAString& aDocumentURI);
1932 
Init(nsTHashtable<nsStringHashKey> & aLoadedItems,nsTHashSet<nsString> && aUnknownItems,uint32_t aNextLoadIndex,uint32_t aTotalLength,int64_t aInitialUsage,int64_t aPeakUsage,LSSnapshot::LoadState aLoadState,bool aHasOtherProcessObservers)1933   void Init(nsTHashtable<nsStringHashKey>& aLoadedItems,
1934             nsTHashSet<nsString>&& aUnknownItems, uint32_t aNextLoadIndex,
1935             uint32_t aTotalLength, int64_t aInitialUsage, int64_t aPeakUsage,
1936             LSSnapshot::LoadState aLoadState, bool aHasOtherProcessObservers) {
1937     AssertIsOnBackgroundThread();
1938     MOZ_ASSERT(aInitialUsage >= 0);
1939     MOZ_ASSERT(aPeakUsage >= aInitialUsage);
1940     MOZ_ASSERT_IF(aLoadState != LSSnapshot::LoadState::AllOrderedItems,
1941                   aNextLoadIndex < aTotalLength);
1942     MOZ_ASSERT(mTotalLength == 0);
1943     MOZ_ASSERT(mUsage == -1);
1944     MOZ_ASSERT(mPeakUsage == -1);
1945 
1946     mLoadedItems.SwapElements(aLoadedItems);
1947     mUnknownItems = std::move(aUnknownItems);
1948     mNextLoadIndex = aNextLoadIndex;
1949     mTotalLength = aTotalLength;
1950     mUsage = aInitialUsage;
1951     mPeakUsage = aPeakUsage;
1952     if (aLoadState == LSSnapshot::LoadState::AllOrderedKeys) {
1953       MOZ_ASSERT(mUnknownItems.Count() == 0);
1954       mLoadKeysReceived = true;
1955     } else if (aLoadState == LSSnapshot::LoadState::AllOrderedItems) {
1956       MOZ_ASSERT(mLoadedItems.Count() == 0);
1957       MOZ_ASSERT(mUnknownItems.Count() == 0);
1958       MOZ_ASSERT(mNextLoadIndex == mTotalLength);
1959       mLoadedReceived = true;
1960       mLoadedAllItems = true;
1961       mLoadKeysReceived = true;
1962     }
1963     mHasOtherProcessObservers = aHasOtherProcessObservers;
1964   }
1965 
1966   /**
1967    * Called via NotifySnapshots by Datastore whenever it is updating its
1968    * internal state so that snapshots can save off the state of a value at the
1969    * time of their creation.
1970    */
1971   void SaveItem(const nsAString& aKey, const LSValue& aOldValue,
1972                 bool aAffectsOrder);
1973 
1974   void MarkDirty();
1975 
IsDirty() const1976   bool IsDirty() const {
1977     AssertIsOnBackgroundThread();
1978 
1979     return mSentMarkDirty;
1980   }
1981 
HasOtherProcessObservers() const1982   bool HasOtherProcessObservers() const {
1983     AssertIsOnBackgroundThread();
1984 
1985     return mHasOtherProcessObservers;
1986   }
1987 
1988   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::Snapshot)
1989 
1990  private:
1991   // Reference counted.
1992   ~Snapshot();
1993 
1994   void Finish();
1995 
1996   // IPDL methods are only called by IPDL.
1997   void ActorDestroy(ActorDestroyReason aWhy) override;
1998 
1999   mozilla::ipc::IPCResult RecvDeleteMe() override;
2000 
2001   mozilla::ipc::IPCResult RecvCheckpoint(
2002       nsTArray<LSWriteInfo>&& aWriteInfos) override;
2003 
2004   mozilla::ipc::IPCResult RecvCheckpointAndNotify(
2005       nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos) override;
2006 
2007   mozilla::ipc::IPCResult RecvFinish() override;
2008 
2009   mozilla::ipc::IPCResult RecvLoaded() override;
2010 
2011   mozilla::ipc::IPCResult RecvLoadValueAndMoreItems(
2012       const nsString& aKey, LSValue* aValue,
2013       nsTArray<LSItemInfo>* aItemInfos) override;
2014 
2015   mozilla::ipc::IPCResult RecvLoadKeys(nsTArray<nsString>* aKeys) override;
2016 
2017   mozilla::ipc::IPCResult RecvIncreasePeakUsage(const int64_t& aRequestedSize,
2018                                                 const int64_t& aMinSize,
2019                                                 int64_t* aSize) override;
2020 
2021   mozilla::ipc::IPCResult RecvPing() override;
2022 };
2023 
2024 class Observer final : public PBackgroundLSObserverParent {
2025   nsCString mOrigin;
2026   bool mActorDestroyed;
2027 
2028  public:
2029   // Created in AllocPBackgroundLSObserverParent.
2030   explicit Observer(const nsACString& aOrigin);
2031 
Origin() const2032   const nsCString& Origin() const { return mOrigin; }
2033 
2034   void Observe(Database* aDatabase, const nsString& aDocumentURI,
2035                const nsString& aKey, const LSValue& aOldValue,
2036                const LSValue& aNewValue);
2037 
2038   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::Observer)
2039 
2040  private:
2041   // Reference counted.
2042   ~Observer();
2043 
2044   // IPDL methods are only called by IPDL.
2045   void ActorDestroy(ActorDestroyReason aWhy) override;
2046 
2047   mozilla::ipc::IPCResult RecvDeleteMe() override;
2048 };
2049 
2050 class LSRequestBase : public DatastoreOperationBase,
2051                       public PBackgroundLSRequestParent {
2052  protected:
2053   enum class State {
2054     // Just created on the PBackground thread. Next step is StartingRequest.
2055     Initial,
2056 
2057     // Waiting to start/starting request on the PBackground thread. Next step is
2058     // either Nesting if a subclass needs to process more nested states or
2059     // SendingReadyMessage if a subclass doesn't need any nested processing.
2060     StartingRequest,
2061 
2062     // Doing nested processing.
2063     Nesting,
2064 
2065     // Waiting to send/sending the ready message on the PBackground thread. Next
2066     // step is WaitingForFinish.
2067     SendingReadyMessage,
2068 
2069     // Waiting for the finish message on the PBackground thread. Next step is
2070     // SendingResults.
2071     WaitingForFinish,
2072 
2073     // Waiting to send/sending results on the PBackground thread. Next step is
2074     // Completed.
2075     SendingResults,
2076 
2077     // All done.
2078     Completed
2079   };
2080 
2081   const LSRequestParams mParams;
2082   Maybe<ContentParentId> mContentParentId;
2083   State mState;
2084   bool mWaitingForFinish;
2085 
2086  public:
2087   LSRequestBase(const LSRequestParams& aParams,
2088                 const Maybe<ContentParentId>& aContentParentId);
2089 
2090   void Dispatch();
2091 
2092   void StringifyState(nsACString& aResult) const;
2093 
2094   virtual void Stringify(nsACString& aResult) const;
2095 
2096   virtual void Log();
2097 
2098  protected:
2099   ~LSRequestBase() override;
2100 
2101   virtual nsresult Start() = 0;
2102 
2103   virtual nsresult NestedRun();
2104 
2105   virtual void GetResponse(LSRequestResponse& aResponse) = 0;
2106 
Cleanup()2107   virtual void Cleanup() {}
2108 
2109  private:
2110   bool VerifyRequestParams();
2111 
2112   nsresult StartRequest();
2113 
2114   void SendReadyMessage();
2115 
2116   nsresult SendReadyMessageInternal();
2117 
2118   void Finish();
2119 
2120   void FinishInternal();
2121 
2122   void SendResults();
2123 
2124  protected:
2125   // Common nsIRunnable implementation that subclasses may not override.
2126   NS_IMETHOD
2127   Run() final;
2128 
2129   // IPDL methods.
2130   void ActorDestroy(ActorDestroyReason aWhy) override;
2131 
2132  private:
2133   mozilla::ipc::IPCResult RecvCancel() final;
2134 
2135   mozilla::ipc::IPCResult RecvFinish() final;
2136 };
2137 
2138 class PrepareDatastoreOp
2139     : public LSRequestBase,
2140       public OpenDirectoryListener,
2141       public SupportsCheckedUnsafePtr<CheckIf<DiagnosticAssertEnabled>> {
2142   class LoadDataOp;
2143 
2144   class CompressFunction;
2145   class CompressibleFunction;
2146 
2147   enum class NestedState {
2148     // The nesting has not yet taken place. Next step is
2149     // CheckExistingOperations.
2150     BeforeNesting,
2151 
2152     // Checking if a prepare datastore operation is already running for given
2153     // origin on the PBackground thread. Next step is CheckClosingDatastore.
2154     CheckExistingOperations,
2155 
2156     // Checking if a datastore is closing the connection for given origin on
2157     // the PBackground thread. Next step is PreparationPending.
2158     CheckClosingDatastore,
2159 
2160     // Opening directory or initializing quota manager on the PBackground
2161     // thread. Next step is either DirectoryOpenPending if quota manager is
2162     // already initialized or QuotaManagerPending if quota manager needs to be
2163     // initialized.
2164     // If a datastore already exists for given origin then the next state is
2165     // SendingReadyMessage.
2166     PreparationPending,
2167 
2168     // Waiting for quota manager initialization to complete on the PBackground
2169     // thread. Next step is either SendingReadyMessage if initialization failed
2170     // or DirectoryOpenPending if initialization succeeded.
2171     QuotaManagerPending,
2172 
2173     // Waiting for directory open allowed on the PBackground thread. The next
2174     // step is either SendingReadyMessage if directory lock failed to acquire,
2175     // or DatabaseWorkOpen if directory lock is acquired.
2176     DirectoryOpenPending,
2177 
2178     // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
2179     // BeginLoadData.
2180     DatabaseWorkOpen,
2181 
2182     // Starting a load data operation on the PBackground thread. Next step is
2183     // DatabaseWorkLoadData.
2184     BeginLoadData,
2185 
2186     // Waiting to do/doing work on the connection thread. This involves waiting
2187     // for the LoadDataOp to do its work. Eventually the state will transition
2188     // to SendingReadyMessage.
2189     DatabaseWorkLoadData,
2190 
2191     // The nesting has completed.
2192     AfterNesting
2193   };
2194 
2195   RefPtr<PrepareDatastoreOp> mDelayedOp;
2196   RefPtr<DirectoryLock> mPendingDirectoryLock;
2197   RefPtr<DirectoryLock> mDirectoryLock;
2198   RefPtr<Connection> mConnection;
2199   RefPtr<Datastore> mDatastore;
2200   UniquePtr<ArchivedOriginScope> mArchivedOriginScope;
2201   LoadDataOp* mLoadDataOp;
2202   nsTHashMap<nsStringHashKey, LSValue> mValues;
2203   nsTArray<LSItemInfo> mOrderedItems;
2204   OriginMetadata mOriginMetadata;
2205   nsCString mMainThreadOrigin;
2206   nsString mDatabaseFilePath;
2207   uint32_t mPrivateBrowsingId;
2208   int64_t mUsage;
2209   int64_t mSizeOfKeys;
2210   int64_t mSizeOfItems;
2211   uint64_t mDatastoreId;
2212   NestedState mNestedState;
2213   const bool mForPreload;
2214   bool mDatabaseNotAvailable;
2215   // Set when the Datastore has been registered with gPrivateDatastores so that
2216   // it can be unregistered if an error is encountered in PrepareDatastoreOp.
2217   FlippedOnce<false> mPrivateDatastoreRegistered;
2218   // Set when the Datastore has been registered with gPreparedDatastores so
2219   // that it can be unregistered if an error is encountered in
2220   // PrepareDatastoreOp.
2221   FlippedOnce<false> mPreparedDatastoreRegistered;
2222   bool mInvalidated;
2223 
2224 #ifdef DEBUG
2225   int64_t mDEBUGUsage;
2226 #endif
2227 
2228  public:
2229   PrepareDatastoreOp(const LSRequestParams& aParams,
2230                      const Maybe<ContentParentId>& aContentParentId);
2231 
MaybeDirectoryLockRef() const2232   Maybe<DirectoryLock&> MaybeDirectoryLockRef() const {
2233     AssertIsOnBackgroundThread();
2234 
2235     return ToMaybeRef(mDirectoryLock.get());
2236   }
2237 
OriginIsKnown() const2238   bool OriginIsKnown() const {
2239     MOZ_ASSERT(IsOnOwningThread() || IsOnIOThread());
2240 
2241     return !mOriginMetadata.mOrigin.IsEmpty();
2242   }
2243 
Origin() const2244   const nsCString& Origin() const {
2245     MOZ_ASSERT(IsOnOwningThread() || IsOnIOThread());
2246     MOZ_ASSERT(OriginIsKnown());
2247 
2248     return mOriginMetadata.mOrigin;
2249   }
2250 
Invalidate()2251   void Invalidate() {
2252     AssertIsOnOwningThread();
2253 
2254     mInvalidated = true;
2255   }
2256 
2257   void StringifyNestedState(nsACString& aResult) const;
2258 
2259   void Stringify(nsACString& aResult) const override;
2260 
2261   void Log() override;
2262 
2263  private:
2264   ~PrepareDatastoreOp() override;
2265 
2266   nsresult Start() override;
2267 
2268   nsresult CheckExistingOperations();
2269 
2270   nsresult CheckClosingDatastoreInternal();
2271 
2272   nsresult CheckClosingDatastore();
2273 
2274   nsresult BeginDatastorePreparationInternal();
2275 
2276   nsresult BeginDatastorePreparation();
2277 
2278   nsresult QuotaManagerOpen();
2279 
2280   nsresult OpenDirectory();
2281 
2282   void SendToIOThread();
2283 
2284   nsresult DatabaseWork();
2285 
2286   nsresult DatabaseNotAvailable();
2287 
2288   nsresult EnsureDirectoryEntry(nsIFile* aEntry, bool aCreateIfNotExists,
2289                                 bool aDirectory,
2290                                 bool* aAlreadyExisted = nullptr);
2291 
2292   nsresult VerifyDatabaseInformation(mozIStorageConnection* aConnection);
2293 
2294   already_AddRefed<QuotaObject> GetQuotaObject();
2295 
2296   nsresult BeginLoadData();
2297 
2298   void FinishNesting();
2299 
2300   nsresult FinishNestingOnNonOwningThread();
2301 
2302   nsresult NestedRun() override;
2303 
2304   void GetResponse(LSRequestResponse& aResponse) override;
2305 
2306   void Cleanup() override;
2307 
2308   void ConnectionClosedCallback();
2309 
2310   void CleanupMetadata();
2311 
2312   NS_DECL_ISUPPORTS_INHERITED
2313 
2314   // IPDL overrides.
2315   void ActorDestroy(ActorDestroyReason aWhy) override;
2316 
2317   // OpenDirectoryListener overrides.
2318   void DirectoryLockAcquired(DirectoryLock* aLock) override;
2319 
2320   void DirectoryLockFailed() override;
2321 };
2322 
2323 class PrepareDatastoreOp::LoadDataOp final
2324     : public ConnectionDatastoreOperationBase {
2325   RefPtr<PrepareDatastoreOp> mPrepareDatastoreOp;
2326 
2327  public:
LoadDataOp(PrepareDatastoreOp * aPrepareDatastoreOp)2328   explicit LoadDataOp(PrepareDatastoreOp* aPrepareDatastoreOp)
2329       : ConnectionDatastoreOperationBase(aPrepareDatastoreOp->mConnection),
2330         mPrepareDatastoreOp(aPrepareDatastoreOp) {}
2331 
2332  private:
2333   ~LoadDataOp() = default;
2334 
2335   nsresult DoDatastoreWork() override;
2336 
2337   void OnSuccess() override;
2338 
2339   void OnFailure(nsresult aResultCode) override;
2340 
2341   void Cleanup() override;
2342 };
2343 
2344 class PrepareDatastoreOp::CompressFunction final : public mozIStorageFunction {
2345  private:
2346   ~CompressFunction() = default;
2347 
2348   NS_DECL_ISUPPORTS
2349   NS_DECL_MOZISTORAGEFUNCTION
2350 };
2351 
2352 class PrepareDatastoreOp::CompressibleFunction final
2353     : public mozIStorageFunction {
2354  private:
2355   ~CompressibleFunction() = default;
2356 
2357   NS_DECL_ISUPPORTS
2358   NS_DECL_MOZISTORAGEFUNCTION
2359 };
2360 
2361 class PrepareObserverOp : public LSRequestBase {
2362   nsCString mOrigin;
2363 
2364  public:
2365   PrepareObserverOp(const LSRequestParams& aParams,
2366                     const Maybe<ContentParentId>& aContentParentId);
2367 
2368  private:
2369   nsresult Start() override;
2370 
2371   void GetResponse(LSRequestResponse& aResponse) override;
2372 };
2373 
2374 class LSSimpleRequestBase : public DatastoreOperationBase,
2375                             public PBackgroundLSSimpleRequestParent {
2376  protected:
2377   enum class State {
2378     // Just created on the PBackground thread. Next step is StartingRequest.
2379     Initial,
2380 
2381     // Waiting to start/starting request on the PBackground thread. Next step is
2382     // SendingResults.
2383     StartingRequest,
2384 
2385     // Waiting to send/sending results on the PBackground thread. Next step is
2386     // Completed.
2387     SendingResults,
2388 
2389     // All done.
2390     Completed
2391   };
2392 
2393   const LSSimpleRequestParams mParams;
2394   Maybe<ContentParentId> mContentParentId;
2395   State mState;
2396 
2397  public:
2398   LSSimpleRequestBase(const LSSimpleRequestParams& aParams,
2399                       const Maybe<ContentParentId>& aContentParentId);
2400 
2401   void Dispatch();
2402 
2403  protected:
2404   ~LSSimpleRequestBase() override;
2405 
2406   virtual nsresult Start() = 0;
2407 
2408   virtual void GetResponse(LSSimpleRequestResponse& aResponse) = 0;
2409 
2410  private:
2411   bool VerifyRequestParams();
2412 
2413   nsresult StartRequest();
2414 
2415   void SendResults();
2416 
2417   // Common nsIRunnable implementation that subclasses may not override.
2418   NS_IMETHOD
2419   Run() final;
2420 
2421   // IPDL methods.
2422   void ActorDestroy(ActorDestroyReason aWhy) override;
2423 };
2424 
2425 class PreloadedOp : public LSSimpleRequestBase {
2426   nsCString mOrigin;
2427 
2428  public:
2429   PreloadedOp(const LSSimpleRequestParams& aParams,
2430               const Maybe<ContentParentId>& aContentParentId);
2431 
2432  private:
2433   nsresult Start() override;
2434 
2435   void GetResponse(LSSimpleRequestResponse& aResponse) override;
2436 };
2437 
2438 /*******************************************************************************
2439  * Other class declarations
2440  ******************************************************************************/
2441 
2442 struct ArchivedOriginInfo {
2443   OriginAttributes mOriginAttributes;
2444   nsCString mOriginNoSuffix;
2445 
ArchivedOriginInfomozilla::dom::__anone8928e300111::ArchivedOriginInfo2446   ArchivedOriginInfo(const OriginAttributes& aOriginAttributes,
2447                      const nsACString& aOriginNoSuffix)
2448       : mOriginAttributes(aOriginAttributes),
2449         mOriginNoSuffix(aOriginNoSuffix) {}
2450 };
2451 
2452 class ArchivedOriginScope {
2453   struct Origin {
2454     nsCString mOriginSuffix;
2455     nsCString mOriginNoSuffix;
2456 
Originmozilla::dom::__anone8928e300111::ArchivedOriginScope::Origin2457     Origin(const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix)
2458         : mOriginSuffix(aOriginSuffix), mOriginNoSuffix(aOriginNoSuffix) {}
2459 
OriginSuffixmozilla::dom::__anone8928e300111::ArchivedOriginScope::Origin2460     const nsACString& OriginSuffix() const { return mOriginSuffix; }
2461 
OriginNoSuffixmozilla::dom::__anone8928e300111::ArchivedOriginScope::Origin2462     const nsACString& OriginNoSuffix() const { return mOriginNoSuffix; }
2463   };
2464 
2465   struct Prefix {
2466     nsCString mOriginNoSuffix;
2467 
Prefixmozilla::dom::__anone8928e300111::ArchivedOriginScope::Prefix2468     explicit Prefix(const nsACString& aOriginNoSuffix)
2469         : mOriginNoSuffix(aOriginNoSuffix) {}
2470 
OriginNoSuffixmozilla::dom::__anone8928e300111::ArchivedOriginScope::Prefix2471     const nsACString& OriginNoSuffix() const { return mOriginNoSuffix; }
2472   };
2473 
2474   struct Pattern {
2475     UniquePtr<OriginAttributesPattern> mPattern;
2476 
Patternmozilla::dom::__anone8928e300111::ArchivedOriginScope::Pattern2477     explicit Pattern(const OriginAttributesPattern& aPattern)
2478         : mPattern(MakeUnique<OriginAttributesPattern>(aPattern)) {}
2479 
Patternmozilla::dom::__anone8928e300111::ArchivedOriginScope::Pattern2480     Pattern(const Pattern& aOther)
2481         : mPattern(MakeUnique<OriginAttributesPattern>(*aOther.mPattern)) {}
2482 
2483     Pattern(Pattern&& aOther) = default;
2484 
GetPatternmozilla::dom::__anone8928e300111::ArchivedOriginScope::Pattern2485     const OriginAttributesPattern& GetPattern() const {
2486       MOZ_ASSERT(mPattern);
2487       return *mPattern;
2488     }
2489   };
2490 
2491   struct Null {};
2492 
2493   using DataType = Variant<Origin, Pattern, Prefix, Null>;
2494 
2495   DataType mData;
2496 
2497  public:
2498   static UniquePtr<ArchivedOriginScope> CreateFromOrigin(
2499       const nsACString& aOriginAttrSuffix, const nsACString& aOriginKey);
2500 
2501   static UniquePtr<ArchivedOriginScope> CreateFromPrefix(
2502       const nsACString& aOriginKey);
2503 
2504   static UniquePtr<ArchivedOriginScope> CreateFromPattern(
2505       const OriginAttributesPattern& aPattern);
2506 
2507   static UniquePtr<ArchivedOriginScope> CreateFromNull();
2508 
IsOrigin() const2509   bool IsOrigin() const { return mData.is<Origin>(); }
2510 
IsPrefix() const2511   bool IsPrefix() const { return mData.is<Prefix>(); }
2512 
IsPattern() const2513   bool IsPattern() const { return mData.is<Pattern>(); }
2514 
IsNull() const2515   bool IsNull() const { return mData.is<Null>(); }
2516 
OriginSuffix() const2517   const nsACString& OriginSuffix() const {
2518     MOZ_ASSERT(IsOrigin());
2519 
2520     return mData.as<Origin>().OriginSuffix();
2521   }
2522 
OriginNoSuffix() const2523   const nsACString& OriginNoSuffix() const {
2524     MOZ_ASSERT(IsOrigin() || IsPrefix());
2525 
2526     if (IsOrigin()) {
2527       return mData.as<Origin>().OriginNoSuffix();
2528     }
2529     return mData.as<Prefix>().OriginNoSuffix();
2530   }
2531 
GetPattern() const2532   const OriginAttributesPattern& GetPattern() const {
2533     MOZ_ASSERT(IsPattern());
2534 
2535     return mData.as<Pattern>().GetPattern();
2536   }
2537 
2538   nsLiteralCString GetBindingClause() const;
2539 
2540   nsresult BindToStatement(mozIStorageStatement* aStatement) const;
2541 
2542   bool HasMatches(ArchivedOriginHashtable* aHashtable) const;
2543 
2544   void RemoveMatches(ArchivedOriginHashtable* aHashtable) const;
2545 
2546  private:
2547   // Move constructors
ArchivedOriginScope(const Origin && aOrigin)2548   explicit ArchivedOriginScope(const Origin&& aOrigin) : mData(aOrigin) {}
2549 
ArchivedOriginScope(const Pattern && aPattern)2550   explicit ArchivedOriginScope(const Pattern&& aPattern) : mData(aPattern) {}
2551 
ArchivedOriginScope(const Prefix && aPrefix)2552   explicit ArchivedOriginScope(const Prefix&& aPrefix) : mData(aPrefix) {}
2553 
ArchivedOriginScope(const Null && aNull)2554   explicit ArchivedOriginScope(const Null&& aNull) : mData(aNull) {}
2555 };
2556 
2557 class QuotaClient final : public mozilla::dom::quota::Client {
2558   class Observer;
2559   class MatchFunction;
2560 
2561   static QuotaClient* sInstance;
2562 
2563   Mutex mShadowDatabaseMutex;
2564   bool mShutdownRequested;
2565 
2566  public:
2567   QuotaClient();
2568 
2569   static nsresult Initialize();
2570 
GetInstance()2571   static QuotaClient* GetInstance() {
2572     AssertIsOnBackgroundThread();
2573 
2574     return sInstance;
2575   }
2576 
IsShuttingDownOnBackgroundThread()2577   static bool IsShuttingDownOnBackgroundThread() {
2578     AssertIsOnBackgroundThread();
2579 
2580     if (sInstance) {
2581       return sInstance->IsShuttingDown();
2582     }
2583 
2584     return QuotaManager::IsShuttingDown();
2585   }
2586 
IsShuttingDownOnNonBackgroundThread()2587   static bool IsShuttingDownOnNonBackgroundThread() {
2588     MOZ_ASSERT(!IsOnBackgroundThread());
2589 
2590     return QuotaManager::IsShuttingDown();
2591   }
2592 
ShadowDatabaseMutex()2593   mozilla::Mutex& ShadowDatabaseMutex() {
2594     MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
2595 
2596     return mShadowDatabaseMutex;
2597   }
2598 
IsShuttingDown() const2599   bool IsShuttingDown() const {
2600     AssertIsOnBackgroundThread();
2601 
2602     return mShutdownRequested;
2603   }
2604 
2605   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::QuotaClient, override)
2606 
2607   Type GetType() override;
2608 
2609   Result<UsageInfo, nsresult> InitOrigin(PersistenceType aPersistenceType,
2610                                          const OriginMetadata& aOriginMetadata,
2611                                          const AtomicBool& aCanceled) override;
2612 
2613   nsresult InitOriginWithoutTracking(PersistenceType aPersistenceType,
2614                                      const OriginMetadata& aOriginMetadata,
2615                                      const AtomicBool& aCanceled) override;
2616 
2617   Result<UsageInfo, nsresult> GetUsageForOrigin(
2618       PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
2619       const AtomicBool& aCanceled) override;
2620 
2621   nsresult AboutToClearOrigins(
2622       const Nullable<PersistenceType>& aPersistenceType,
2623       const OriginScope& aOriginScope) override;
2624 
2625   void OnOriginClearCompleted(PersistenceType aPersistenceType,
2626                               const nsACString& aOrigin) override;
2627 
2628   void ReleaseIOThreadObjects() override;
2629 
2630   void AbortOperationsForLocks(
2631       const DirectoryLockIdTable& aDirectoryLockIds) override;
2632 
2633   void AbortOperationsForProcess(ContentParentId aContentParentId) override;
2634 
2635   void AbortAllOperations() override;
2636 
2637   void StartIdleMaintenance() override;
2638 
2639   void StopIdleMaintenance() override;
2640 
2641  private:
2642   ~QuotaClient() override;
2643 
2644   void InitiateShutdown() override;
2645   bool IsShutdownCompleted() const override;
2646   nsCString GetShutdownStatus() const override;
2647   void ForceKillActors() override;
2648   void FinalizeShutdown() override;
2649 
2650   Result<UniquePtr<ArchivedOriginScope>, nsresult> CreateArchivedOriginScope(
2651       const OriginScope& aOriginScope);
2652 
2653   nsresult PerformDelete(mozIStorageConnection* aConnection,
2654                          const nsACString& aSchemaName,
2655                          ArchivedOriginScope* aArchivedOriginScope) const;
2656 };
2657 
2658 class QuotaClient::Observer final : public nsIObserver {
2659  public:
2660   static nsresult Initialize();
2661 
2662  private:
Observer()2663   Observer() { MOZ_ASSERT(NS_IsMainThread()); }
2664 
~Observer()2665   ~Observer() { MOZ_ASSERT(NS_IsMainThread()); }
2666 
2667   nsresult Init();
2668 
2669   nsresult Shutdown();
2670 
2671   NS_DECL_ISUPPORTS
2672   NS_DECL_NSIOBSERVER
2673 };
2674 
2675 class QuotaClient::MatchFunction final : public mozIStorageFunction {
2676   OriginAttributesPattern mPattern;
2677 
2678  public:
MatchFunction(const OriginAttributesPattern & aPattern)2679   explicit MatchFunction(const OriginAttributesPattern& aPattern)
2680       : mPattern(aPattern) {}
2681 
2682  private:
2683   ~MatchFunction() = default;
2684 
2685   NS_DECL_ISUPPORTS
2686   NS_DECL_MOZISTORAGEFUNCTION
2687 };
2688 
2689 /*******************************************************************************
2690  * Helper classes
2691  ******************************************************************************/
2692 
2693 class MOZ_STACK_CLASS AutoWriteTransaction final {
2694   Connection* mConnection;
2695   Maybe<MutexAutoLock> mShadowDatabaseLock;
2696   bool mShadowWrites;
2697 
2698  public:
2699   explicit AutoWriteTransaction(bool aShadowWrites);
2700 
2701   ~AutoWriteTransaction();
2702 
2703   nsresult Start(Connection* aConnection);
2704 
2705   nsresult Commit();
2706 
2707  private:
2708   nsresult LockAndAttachShadowDatabase(Connection* aConnection);
2709 
2710   nsresult DetachShadowDatabaseAndUnlock();
2711 };
2712 
2713 /*******************************************************************************
2714  * Globals
2715  ******************************************************************************/
2716 
2717 #ifdef DEBUG
2718 bool gLocalStorageInitialized = false;
2719 #endif
2720 
2721 using PrepareDatastoreOpArray =
2722     nsTArray<NotNull<CheckedUnsafePtr<PrepareDatastoreOp>>>;
2723 
2724 StaticAutoPtr<PrepareDatastoreOpArray> gPrepareDatastoreOps;
2725 
2726 // nsCStringHashKey with disabled memmove
2727 class nsCStringHashKeyDM : public nsCStringHashKey {
2728  public:
nsCStringHashKeyDM(const nsCStringHashKey::KeyTypePointer aKey)2729   explicit nsCStringHashKeyDM(const nsCStringHashKey::KeyTypePointer aKey)
2730       : nsCStringHashKey(aKey) {}
2731   enum { ALLOW_MEMMOVE = false };
2732 };
2733 
2734 // When CheckedUnsafePtr's checking is enabled, it's necessary to ensure that
2735 // the hashtable uses the copy constructor instead of memmove for moving entries
2736 // since memmove will break CheckedUnsafePtr in a memory-corrupting way.
2737 typedef std::conditional<DiagnosticAssertEnabled::value, nsCStringHashKeyDM,
2738                          nsCStringHashKey>::type DatastoreHashKey;
2739 
2740 using DatastoreHashtable =
2741     nsBaseHashtable<DatastoreHashKey, NotNull<CheckedUnsafePtr<Datastore>>,
2742                     MovingNotNull<CheckedUnsafePtr<Datastore>>>;
2743 
2744 StaticAutoPtr<DatastoreHashtable> gDatastores;
2745 
2746 uint64_t gLastDatastoreId = 0;
2747 
2748 typedef nsClassHashtable<nsUint64HashKey, PreparedDatastore>
2749     PreparedDatastoreHashtable;
2750 
2751 StaticAutoPtr<PreparedDatastoreHashtable> gPreparedDatastores;
2752 
2753 using PrivateDatastoreHashtable =
2754     nsClassHashtable<nsCStringHashKey, PrivateDatastore>;
2755 
2756 // Keeps Private Browsing Datastores alive until the private browsing session
2757 // is closed. This is necessary because LocalStorage Private Browsing data is
2758 // (currently) not written to disk and therefore needs to explicitly be kept
2759 // alive in memory so that if a user browses away from a site during a session
2760 // and then back to it that they will still have their data.
2761 //
2762 // The entries are wrapped by PrivateDatastore instances which call
2763 // NoteLivePrivateDatastore and NoteFinishedPrivateDatastore which set and
2764 // clear mHasLivePrivateDatastore which inhibits MaybeClose() from closing the
2765 // datastore (which would discard the data) when there are no active windows
2766 // using LocalStorage for the origin.
2767 //
2768 // The table is cleared when the Private Browsing session is closed, which will
2769 // cause NoteFinishedPrivateDatastore to be called on each Datastore which will
2770 // in turn call MaybeClose which should then discard the Datastore. Or in the
2771 // event of an (unlikely) race where the private browsing windows are still
2772 // being torn down, will cause the Datastore to be discarded when the last
2773 // window actually goes away.
2774 UniquePtr<PrivateDatastoreHashtable> gPrivateDatastores;
2775 
2776 using LiveDatabaseArray = nsTArray<NotNull<CheckedUnsafePtr<Database>>>;
2777 
2778 StaticAutoPtr<LiveDatabaseArray> gLiveDatabases;
2779 
2780 StaticRefPtr<ConnectionThread> gConnectionThread;
2781 
2782 uint64_t gLastObserverId = 0;
2783 
2784 typedef nsRefPtrHashtable<nsUint64HashKey, Observer> PreparedObserverHashtable;
2785 
2786 StaticAutoPtr<PreparedObserverHashtable> gPreparedObsevers;
2787 
2788 typedef nsClassHashtable<nsCStringHashKey, nsTArray<NotNull<Observer*>>>
2789     ObserverHashtable;
2790 
2791 StaticAutoPtr<ObserverHashtable> gObservers;
2792 
2793 Atomic<bool> gShadowWrites(kDefaultShadowWrites);
2794 Atomic<int32_t, Relaxed> gSnapshotPrefill(kDefaultSnapshotPrefill);
2795 Atomic<int32_t, Relaxed> gSnapshotGradualPrefill(
2796     kDefaultSnapshotGradualPrefill);
2797 Atomic<bool> gClientValidation(kDefaultClientValidation);
2798 
2799 typedef nsTHashMap<nsCStringHashKey, int64_t> UsageHashtable;
2800 
2801 StaticAutoPtr<ArchivedOriginHashtable> gArchivedOrigins;
2802 
2803 // Can only be touched on the Quota Manager I/O thread.
2804 bool gInitializedShadowStorage = false;
2805 
2806 StaticAutoPtr<LSInitializationInfo> gInitializationInfo;
2807 
IsOnGlobalConnectionThread()2808 bool IsOnGlobalConnectionThread() {
2809   MOZ_ASSERT(gConnectionThread);
2810   return gConnectionThread->IsOnConnectionThread();
2811 }
2812 
AssertIsOnGlobalConnectionThread()2813 void AssertIsOnGlobalConnectionThread() {
2814   MOZ_ASSERT(gConnectionThread);
2815   gConnectionThread->AssertIsOnConnectionThread();
2816 }
2817 
GetDatastore(const nsACString & aOrigin)2818 already_AddRefed<Datastore> GetDatastore(const nsACString& aOrigin) {
2819   AssertIsOnBackgroundThread();
2820 
2821   if (gDatastores) {
2822     auto maybeDatastore = gDatastores->MaybeGet(aOrigin);
2823     if (maybeDatastore) {
2824       RefPtr<Datastore> result(std::move(*maybeDatastore).unwrapBasePtr());
2825       return result.forget();
2826     }
2827   }
2828 
2829   return nullptr;
2830 }
2831 
LoadArchivedOrigins()2832 nsresult LoadArchivedOrigins() {
2833   AssertIsOnIOThread();
2834   MOZ_ASSERT(!gArchivedOrigins);
2835 
2836   QuotaManager* quotaManager = QuotaManager::Get();
2837   MOZ_ASSERT(quotaManager);
2838 
2839   // Ensure that the webappsstore.sqlite is moved to new place.
2840   QM_TRY(quotaManager->EnsureStorageIsInitialized());
2841 
2842   QM_TRY_INSPECT(const auto& connection, CreateArchiveStorageConnection(
2843                                              quotaManager->GetStoragePath()));
2844 
2845   if (!connection) {
2846     gArchivedOrigins = new ArchivedOriginHashtable();
2847     return NS_OK;
2848   }
2849 
2850   QM_TRY_INSPECT(
2851       const auto& stmt,
2852       MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageStatement>, connection,
2853                                  CreateStatement,
2854                                  "SELECT DISTINCT originAttributes, originKey "
2855                                  "FROM webappsstore2;"_ns));
2856 
2857   auto archivedOrigins = MakeUnique<ArchivedOriginHashtable>();
2858 
2859   // XXX Actually, this could use a hashtable variant of
2860   // CollectElementsWhileHasResult
2861   QM_TRY(quota::CollectWhileHasResult(
2862       *stmt, [&archivedOrigins](auto& stmt) -> Result<Ok, nsresult> {
2863         QM_TRY_INSPECT(
2864             const auto& originSuffix,
2865             MOZ_TO_RESULT_INVOKE_TYPED(nsCString, stmt, GetUTF8String, 0));
2866         QM_TRY_INSPECT(
2867             const auto& originNoSuffix,
2868             MOZ_TO_RESULT_INVOKE_TYPED(nsCString, stmt, GetUTF8String, 1));
2869 
2870         const nsCString hashKey =
2871             GetArchivedOriginHashKey(originSuffix, originNoSuffix);
2872 
2873         OriginAttributes originAttributes;
2874         QM_TRY(OkIf(originAttributes.PopulateFromSuffix(originSuffix)),
2875                Err(NS_ERROR_FAILURE));
2876 
2877         archivedOrigins->InsertOrUpdate(
2878             hashKey,
2879             MakeUnique<ArchivedOriginInfo>(originAttributes, originNoSuffix));
2880 
2881         return Ok{};
2882       }));
2883 
2884   gArchivedOrigins = archivedOrigins.release();
2885   return NS_OK;
2886 }
2887 
GetUsage(mozIStorageConnection & aConnection,ArchivedOriginScope * aArchivedOriginScope)2888 Result<int64_t, nsresult> GetUsage(mozIStorageConnection& aConnection,
2889                                    ArchivedOriginScope* aArchivedOriginScope) {
2890   AssertIsOnIOThread();
2891 
2892   QM_TRY_INSPECT(
2893       const auto& stmt,
2894       ([aArchivedOriginScope,
2895         &aConnection]() -> Result<nsCOMPtr<mozIStorageStatement>, nsresult> {
2896         if (aArchivedOriginScope) {
2897           QM_TRY_RETURN(CreateAndExecuteSingleStepStatement<
2898                         SingleStepResult::ReturnNullIfNoResult>(
2899               aConnection,
2900               "SELECT "
2901               "total(utf16Length(key) + utf16Length(value)) "
2902               "FROM webappsstore2 "
2903               "WHERE originKey = :originKey "
2904               "AND originAttributes = :originAttributes;"_ns,
2905               [aArchivedOriginScope](auto& stmt) -> Result<Ok, nsresult> {
2906                 QM_TRY(aArchivedOriginScope->BindToStatement(&stmt));
2907                 return Ok{};
2908               }));
2909         }
2910 
2911         QM_TRY_RETURN(CreateAndExecuteSingleStepStatement<
2912                       SingleStepResult::ReturnNullIfNoResult>(
2913             aConnection, "SELECT usage FROM database"_ns));
2914       }()));
2915 
2916   QM_TRY(OkIf(stmt), Err(NS_ERROR_FAILURE));
2917 
2918   QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE(stmt, GetInt64, 0));
2919 }
2920 
ShadowWritesPrefChangedCallback(const char * aPrefName,void * aClosure)2921 void ShadowWritesPrefChangedCallback(const char* aPrefName, void* aClosure) {
2922   MOZ_ASSERT(NS_IsMainThread());
2923   MOZ_ASSERT(!strcmp(aPrefName, kShadowWritesPref));
2924   MOZ_ASSERT(!aClosure);
2925 
2926   gShadowWrites = Preferences::GetBool(aPrefName, kDefaultShadowWrites);
2927 }
2928 
SnapshotPrefillPrefChangedCallback(const char * aPrefName,void * aClosure)2929 void SnapshotPrefillPrefChangedCallback(const char* aPrefName, void* aClosure) {
2930   MOZ_ASSERT(NS_IsMainThread());
2931   MOZ_ASSERT(!strcmp(aPrefName, kSnapshotPrefillPref));
2932   MOZ_ASSERT(!aClosure);
2933 
2934   int32_t snapshotPrefill =
2935       Preferences::GetInt(aPrefName, kDefaultSnapshotPrefill);
2936 
2937   // The magic -1 is for use only by tests.
2938   if (snapshotPrefill == -1) {
2939     snapshotPrefill = INT32_MAX;
2940   }
2941 
2942   gSnapshotPrefill = snapshotPrefill;
2943 }
2944 
SnapshotGradualPrefillPrefChangedCallback(const char * aPrefName,void * aClosure)2945 void SnapshotGradualPrefillPrefChangedCallback(const char* aPrefName,
2946                                                void* aClosure) {
2947   MOZ_ASSERT(NS_IsMainThread());
2948   MOZ_ASSERT(!strcmp(aPrefName, kSnapshotGradualPrefillPref));
2949   MOZ_ASSERT(!aClosure);
2950 
2951   int32_t snapshotGradualPrefill =
2952       Preferences::GetInt(aPrefName, kDefaultSnapshotGradualPrefill);
2953 
2954   // The magic -1 is for use only by tests.
2955   if (snapshotGradualPrefill == -1) {
2956     snapshotGradualPrefill = INT32_MAX;
2957   }
2958 
2959   gSnapshotGradualPrefill = snapshotGradualPrefill;
2960 }
2961 
ClientValidationPrefChangedCallback(const char * aPrefName,void * aClosure)2962 void ClientValidationPrefChangedCallback(const char* aPrefName,
2963                                          void* aClosure) {
2964   MOZ_ASSERT(NS_IsMainThread());
2965   MOZ_ASSERT(!strcmp(aPrefName, kClientValidationPref));
2966   MOZ_ASSERT(!aClosure);
2967 
2968   gClientValidation = Preferences::GetBool(aPrefName, kDefaultClientValidation);
2969 }
2970 
2971 template <typename Condition>
InvalidatePrepareDatastoreOpsMatching(const Condition & aCondition)2972 void InvalidatePrepareDatastoreOpsMatching(const Condition& aCondition) {
2973   if (!gPrepareDatastoreOps) {
2974     return;
2975   }
2976 
2977   for (const auto& prepareDatastoreOp : *gPrepareDatastoreOps) {
2978     if (aCondition(*prepareDatastoreOp)) {
2979       prepareDatastoreOp->Invalidate();
2980     }
2981   }
2982 }
2983 
2984 template <typename Condition>
InvalidatePreparedDatastoresMatching(const Condition & aCondition)2985 void InvalidatePreparedDatastoresMatching(const Condition& aCondition) {
2986   if (!gPreparedDatastores) {
2987     return;
2988   }
2989 
2990   for (const auto& preparedDatastore : gPreparedDatastores->Values()) {
2991     MOZ_ASSERT(preparedDatastore);
2992 
2993     if (aCondition(*preparedDatastore)) {
2994       preparedDatastore->Invalidate();
2995     }
2996   }
2997 }
2998 
2999 template <typename Condition>
CollectDatabasesMatching(Condition aCondition)3000 nsTArray<RefPtr<Database>> CollectDatabasesMatching(Condition aCondition) {
3001   AssertIsOnBackgroundThread();
3002 
3003   if (!gLiveDatabases) {
3004     return nsTArray<RefPtr<Database>>{};
3005   }
3006 
3007   nsTArray<RefPtr<Database>> databases;
3008 
3009   for (const auto& database : *gLiveDatabases) {
3010     if (aCondition(*database)) {
3011       databases.AppendElement(database.get());
3012     }
3013   }
3014 
3015   return databases;
3016 }
3017 
3018 template <typename Condition>
RequestAllowToCloseDatabasesMatching(Condition aCondition)3019 void RequestAllowToCloseDatabasesMatching(Condition aCondition) {
3020   AssertIsOnBackgroundThread();
3021 
3022   nsTArray<RefPtr<Database>> databases = CollectDatabasesMatching(aCondition);
3023 
3024   for (const auto& database : databases) {
3025     MOZ_ASSERT(database);
3026 
3027     database->RequestAllowToClose();
3028   }
3029 }
3030 
ForceKillAllDatabases()3031 void ForceKillAllDatabases() {
3032   AssertIsOnBackgroundThread();
3033 
3034   nsTArray<RefPtr<Database>> databases =
3035       CollectDatabasesMatching([](const auto&) { return true; });
3036 
3037   for (const auto& database : databases) {
3038     MOZ_ASSERT(database);
3039 
3040     database->ForceKill();
3041   }
3042 }
3043 
VerifyPrincipalInfo(const PrincipalInfo & aPrincipalInfo,const PrincipalInfo & aStoragePrincipalInfo)3044 bool VerifyPrincipalInfo(const PrincipalInfo& aPrincipalInfo,
3045                          const PrincipalInfo& aStoragePrincipalInfo) {
3046   AssertIsOnBackgroundThread();
3047 
3048   if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
3049     return false;
3050   }
3051 
3052   if (NS_WARN_IF(!StoragePrincipalHelper::
3053                      VerifyValidStoragePrincipalInfoForPrincipalInfo(
3054                          aStoragePrincipalInfo, aPrincipalInfo))) {
3055     return false;
3056   }
3057 
3058   return true;
3059 }
3060 
VerifyClientId(const Maybe<ContentParentId> & aContentParentId,const PrincipalInfo & aPrincipalInfo,const Maybe<nsID> & aClientId)3061 bool VerifyClientId(const Maybe<ContentParentId>& aContentParentId,
3062                     const PrincipalInfo& aPrincipalInfo,
3063                     const Maybe<nsID>& aClientId) {
3064   AssertIsOnBackgroundThread();
3065 
3066   if (gClientValidation) {
3067     if (NS_WARN_IF(aClientId.isNothing())) {
3068       return false;
3069     }
3070 
3071     RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance();
3072     if (svc && NS_WARN_IF(!svc->HasWindow(aContentParentId, aPrincipalInfo,
3073                                           aClientId.ref()))) {
3074       return false;
3075     }
3076   }
3077 
3078   return true;
3079 }
3080 
VerifyOriginKey(const nsACString & aOriginKey,const PrincipalInfo & aPrincipalInfo)3081 bool VerifyOriginKey(const nsACString& aOriginKey,
3082                      const PrincipalInfo& aPrincipalInfo) {
3083   AssertIsOnBackgroundThread();
3084 
3085   QM_TRY_INSPECT((const auto& [originAttrSuffix, originKey]),
3086                  GenerateOriginKey2(aPrincipalInfo), false);
3087 
3088   Unused << originAttrSuffix;
3089 
3090   QM_TRY(OkIf(originKey == aOriginKey), false,
3091          ([&originKey = originKey, &aOriginKey](const auto) {
3092            LS_WARNING("originKey (%s) doesn't match passed one (%s)!",
3093                       originKey.get(), nsCString(aOriginKey).get());
3094          }));
3095 
3096   return true;
3097 }
3098 
3099 }  // namespace
3100 
3101 /*******************************************************************************
3102  * Exported functions
3103  ******************************************************************************/
3104 
InitializeLocalStorage()3105 void InitializeLocalStorage() {
3106   MOZ_ASSERT(XRE_IsParentProcess());
3107   MOZ_ASSERT(NS_IsMainThread());
3108   MOZ_ASSERT(!gLocalStorageInitialized);
3109 
3110   // XXX Isn't this redundant? It's already done in InitializeQuotaManager.
3111   if (!QuotaManager::IsRunningGTests()) {
3112     // This service has to be started on the main thread currently.
3113     const nsCOMPtr<mozIStorageService> ss =
3114         do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
3115 
3116     QM_WARNONLY_TRY(OkIf(ss));
3117   }
3118 
3119   QM_WARNONLY_TRY(QuotaClient::Initialize());
3120 
3121   Preferences::RegisterCallbackAndCall(ShadowWritesPrefChangedCallback,
3122                                        kShadowWritesPref);
3123 
3124   Preferences::RegisterCallbackAndCall(SnapshotPrefillPrefChangedCallback,
3125                                        kSnapshotPrefillPref);
3126 
3127   Preferences::RegisterCallbackAndCall(
3128       SnapshotGradualPrefillPrefChangedCallback, kSnapshotGradualPrefillPref);
3129 
3130   Preferences::RegisterCallbackAndCall(ClientValidationPrefChangedCallback,
3131                                        kClientValidationPref);
3132 
3133 #ifdef DEBUG
3134   gLocalStorageInitialized = true;
3135 #endif
3136 }
3137 
AllocPBackgroundLSDatabaseParent(const PrincipalInfo & aPrincipalInfo,const uint32_t & aPrivateBrowsingId,const uint64_t & aDatastoreId)3138 PBackgroundLSDatabaseParent* AllocPBackgroundLSDatabaseParent(
3139     const PrincipalInfo& aPrincipalInfo, const uint32_t& aPrivateBrowsingId,
3140     const uint64_t& aDatastoreId) {
3141   AssertIsOnBackgroundThread();
3142 
3143   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
3144     return nullptr;
3145   }
3146 
3147   if (NS_WARN_IF(!gPreparedDatastores)) {
3148     ASSERT_UNLESS_FUZZING();
3149     return nullptr;
3150   }
3151 
3152   PreparedDatastore* preparedDatastore = gPreparedDatastores->Get(aDatastoreId);
3153   if (NS_WARN_IF(!preparedDatastore)) {
3154     ASSERT_UNLESS_FUZZING();
3155     return nullptr;
3156   }
3157 
3158   // If we ever decide to return null from this point on, we need to make sure
3159   // that the datastore is closed and the prepared datastore is removed from the
3160   // gPreparedDatastores hashtable.
3161   // We also assume that IPDL must call RecvPBackgroundLSDatabaseConstructor
3162   // once we return a valid actor in this method.
3163 
3164   RefPtr<Database> database =
3165       new Database(aPrincipalInfo, preparedDatastore->GetContentParentId(),
3166                    preparedDatastore->Origin(), aPrivateBrowsingId);
3167 
3168   // Transfer ownership to IPDL.
3169   return database.forget().take();
3170 }
3171 
RecvPBackgroundLSDatabaseConstructor(PBackgroundLSDatabaseParent * aActor,const PrincipalInfo & aPrincipalInfo,const uint32_t & aPrivateBrowsingId,const uint64_t & aDatastoreId)3172 bool RecvPBackgroundLSDatabaseConstructor(PBackgroundLSDatabaseParent* aActor,
3173                                           const PrincipalInfo& aPrincipalInfo,
3174                                           const uint32_t& aPrivateBrowsingId,
3175                                           const uint64_t& aDatastoreId) {
3176   AssertIsOnBackgroundThread();
3177   MOZ_ASSERT(aActor);
3178   MOZ_ASSERT(gPreparedDatastores);
3179   MOZ_ASSERT(gPreparedDatastores->Get(aDatastoreId));
3180   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
3181 
3182   // The actor is now completely built (it has a manager, channel and it's
3183   // registered as a subprotocol).
3184   // ActorDestroy will be called if we fail here.
3185 
3186   mozilla::UniquePtr<PreparedDatastore> preparedDatastore;
3187   gPreparedDatastores->Remove(aDatastoreId, &preparedDatastore);
3188   MOZ_ASSERT(preparedDatastore);
3189 
3190   auto* database = static_cast<Database*>(aActor);
3191 
3192   database->SetActorAlive(&preparedDatastore->MutableDatastoreRef());
3193 
3194   // It's possible that AbortOperationsForLocks was called before the database
3195   // actor was created and became live. Let the child know that the database is
3196   // no longer valid.
3197   if (preparedDatastore->IsInvalidated()) {
3198     database->RequestAllowToClose();
3199   }
3200 
3201   return true;
3202 }
3203 
DeallocPBackgroundLSDatabaseParent(PBackgroundLSDatabaseParent * aActor)3204 bool DeallocPBackgroundLSDatabaseParent(PBackgroundLSDatabaseParent* aActor) {
3205   AssertIsOnBackgroundThread();
3206   MOZ_ASSERT(aActor);
3207 
3208   // Transfer ownership back from IPDL.
3209   RefPtr<Database> actor = dont_AddRef(static_cast<Database*>(aActor));
3210 
3211   return true;
3212 }
3213 
AllocPBackgroundLSObserverParent(const uint64_t & aObserverId)3214 PBackgroundLSObserverParent* AllocPBackgroundLSObserverParent(
3215     const uint64_t& aObserverId) {
3216   AssertIsOnBackgroundThread();
3217 
3218   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
3219     return nullptr;
3220   }
3221 
3222   if (NS_WARN_IF(!gPreparedObsevers)) {
3223     ASSERT_UNLESS_FUZZING();
3224     return nullptr;
3225   }
3226 
3227   RefPtr<Observer> observer = gPreparedObsevers->Get(aObserverId);
3228   if (NS_WARN_IF(!observer)) {
3229     ASSERT_UNLESS_FUZZING();
3230     return nullptr;
3231   }
3232 
3233   // observer->SetObject(this);
3234 
3235   // Transfer ownership to IPDL.
3236   return observer.forget().take();
3237 }
3238 
RecvPBackgroundLSObserverConstructor(PBackgroundLSObserverParent * aActor,const uint64_t & aObserverId)3239 bool RecvPBackgroundLSObserverConstructor(PBackgroundLSObserverParent* aActor,
3240                                           const uint64_t& aObserverId) {
3241   AssertIsOnBackgroundThread();
3242   MOZ_ASSERT(aActor);
3243   MOZ_ASSERT(gPreparedObsevers);
3244   MOZ_ASSERT(gPreparedObsevers->GetWeak(aObserverId));
3245 
3246   RefPtr<Observer> observer;
3247   gPreparedObsevers->Remove(aObserverId, observer.StartAssignment());
3248 
3249   if (!gPreparedObsevers->Count()) {
3250     gPreparedObsevers = nullptr;
3251   }
3252 
3253   if (!gObservers) {
3254     gObservers = new ObserverHashtable();
3255   }
3256 
3257   const auto notNullObserver = WrapNotNull(observer.get());
3258 
3259   nsTArray<NotNull<Observer*>>* const array =
3260       gObservers->GetOrInsertNew(notNullObserver->Origin());
3261   array->AppendElement(notNullObserver);
3262 
3263   if (RefPtr<Datastore> datastore = GetDatastore(observer->Origin())) {
3264     datastore->NoteChangedObserverArray(*array);
3265   }
3266 
3267   return true;
3268 }
3269 
DeallocPBackgroundLSObserverParent(PBackgroundLSObserverParent * aActor)3270 bool DeallocPBackgroundLSObserverParent(PBackgroundLSObserverParent* aActor) {
3271   AssertIsOnBackgroundThread();
3272   MOZ_ASSERT(aActor);
3273 
3274   // Transfer ownership back from IPDL.
3275   RefPtr<Observer> actor = dont_AddRef(static_cast<Observer*>(aActor));
3276 
3277   return true;
3278 }
3279 
AllocPBackgroundLSRequestParent(PBackgroundParent * aBackgroundActor,const LSRequestParams & aParams)3280 PBackgroundLSRequestParent* AllocPBackgroundLSRequestParent(
3281     PBackgroundParent* aBackgroundActor, const LSRequestParams& aParams) {
3282   AssertIsOnBackgroundThread();
3283   MOZ_ASSERT(aParams.type() != LSRequestParams::T__None);
3284 
3285   if (NS_WARN_IF(!NextGenLocalStorageEnabled())) {
3286     return nullptr;
3287   }
3288 
3289   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
3290     return nullptr;
3291   }
3292 
3293   Maybe<ContentParentId> contentParentId;
3294 
3295   uint64_t childID = BackgroundParent::GetChildID(aBackgroundActor);
3296   if (childID) {
3297     contentParentId = Some(ContentParentId(childID));
3298   }
3299 
3300   RefPtr<LSRequestBase> actor;
3301 
3302   switch (aParams.type()) {
3303     case LSRequestParams::TLSRequestPreloadDatastoreParams:
3304     case LSRequestParams::TLSRequestPrepareDatastoreParams: {
3305       RefPtr<PrepareDatastoreOp> prepareDatastoreOp =
3306           new PrepareDatastoreOp(aParams, contentParentId);
3307 
3308       if (!gPrepareDatastoreOps) {
3309         gPrepareDatastoreOps = new PrepareDatastoreOpArray();
3310       }
3311       gPrepareDatastoreOps->AppendElement(
3312           WrapNotNullUnchecked(prepareDatastoreOp.get()));
3313 
3314       actor = std::move(prepareDatastoreOp);
3315 
3316       break;
3317     }
3318 
3319     case LSRequestParams::TLSRequestPrepareObserverParams: {
3320       RefPtr<PrepareObserverOp> prepareObserverOp =
3321           new PrepareObserverOp(aParams, contentParentId);
3322 
3323       actor = std::move(prepareObserverOp);
3324 
3325       break;
3326     }
3327 
3328     default:
3329       MOZ_CRASH("Should never get here!");
3330   }
3331 
3332   // Transfer ownership to IPDL.
3333   return actor.forget().take();
3334 }
3335 
RecvPBackgroundLSRequestConstructor(PBackgroundLSRequestParent * aActor,const LSRequestParams & aParams)3336 bool RecvPBackgroundLSRequestConstructor(PBackgroundLSRequestParent* aActor,
3337                                          const LSRequestParams& aParams) {
3338   AssertIsOnBackgroundThread();
3339   MOZ_ASSERT(aActor);
3340   MOZ_ASSERT(aParams.type() != LSRequestParams::T__None);
3341   MOZ_ASSERT(NextGenLocalStorageEnabled());
3342   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
3343 
3344   // The actor is now completely built.
3345 
3346   auto* op = static_cast<LSRequestBase*>(aActor);
3347 
3348   op->Dispatch();
3349 
3350   return true;
3351 }
3352 
DeallocPBackgroundLSRequestParent(PBackgroundLSRequestParent * aActor)3353 bool DeallocPBackgroundLSRequestParent(PBackgroundLSRequestParent* aActor) {
3354   AssertIsOnBackgroundThread();
3355 
3356   // Transfer ownership back from IPDL.
3357   RefPtr<LSRequestBase> actor =
3358       dont_AddRef(static_cast<LSRequestBase*>(aActor));
3359 
3360   return true;
3361 }
3362 
AllocPBackgroundLSSimpleRequestParent(PBackgroundParent * aBackgroundActor,const LSSimpleRequestParams & aParams)3363 PBackgroundLSSimpleRequestParent* AllocPBackgroundLSSimpleRequestParent(
3364     PBackgroundParent* aBackgroundActor, const LSSimpleRequestParams& aParams) {
3365   AssertIsOnBackgroundThread();
3366   MOZ_ASSERT(aParams.type() != LSSimpleRequestParams::T__None);
3367 
3368   if (NS_WARN_IF(!NextGenLocalStorageEnabled())) {
3369     return nullptr;
3370   }
3371 
3372   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
3373     return nullptr;
3374   }
3375 
3376   Maybe<ContentParentId> contentParentId;
3377 
3378   uint64_t childID = BackgroundParent::GetChildID(aBackgroundActor);
3379   if (childID) {
3380     contentParentId = Some(ContentParentId(childID));
3381   }
3382 
3383   RefPtr<LSSimpleRequestBase> actor;
3384 
3385   switch (aParams.type()) {
3386     case LSSimpleRequestParams::TLSSimpleRequestPreloadedParams: {
3387       RefPtr<PreloadedOp> preloadedOp =
3388           new PreloadedOp(aParams, contentParentId);
3389 
3390       actor = std::move(preloadedOp);
3391 
3392       break;
3393     }
3394 
3395     default:
3396       MOZ_CRASH("Should never get here!");
3397   }
3398 
3399   // Transfer ownership to IPDL.
3400   return actor.forget().take();
3401 }
3402 
RecvPBackgroundLSSimpleRequestConstructor(PBackgroundLSSimpleRequestParent * aActor,const LSSimpleRequestParams & aParams)3403 bool RecvPBackgroundLSSimpleRequestConstructor(
3404     PBackgroundLSSimpleRequestParent* aActor,
3405     const LSSimpleRequestParams& aParams) {
3406   AssertIsOnBackgroundThread();
3407   MOZ_ASSERT(aActor);
3408   MOZ_ASSERT(aParams.type() != LSSimpleRequestParams::T__None);
3409   MOZ_ASSERT(NextGenLocalStorageEnabled());
3410   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
3411 
3412   // The actor is now completely built.
3413 
3414   auto* op = static_cast<LSSimpleRequestBase*>(aActor);
3415 
3416   op->Dispatch();
3417 
3418   return true;
3419 }
3420 
DeallocPBackgroundLSSimpleRequestParent(PBackgroundLSSimpleRequestParent * aActor)3421 bool DeallocPBackgroundLSSimpleRequestParent(
3422     PBackgroundLSSimpleRequestParent* aActor) {
3423   AssertIsOnBackgroundThread();
3424 
3425   // Transfer ownership back from IPDL.
3426   RefPtr<LSSimpleRequestBase> actor =
3427       dont_AddRef(static_cast<LSSimpleRequestBase*>(aActor));
3428 
3429   return true;
3430 }
3431 
RecvLSClearPrivateBrowsing()3432 bool RecvLSClearPrivateBrowsing() {
3433   AssertIsOnBackgroundThread();
3434 
3435   gPrivateDatastores = nullptr;
3436 
3437   if (gDatastores) {
3438     for (const auto& datastore : gDatastores->Values()) {
3439       if (datastore->PrivateBrowsingId()) {
3440         datastore->Clear(nullptr);
3441       }
3442     }
3443   }
3444 
3445   return true;
3446 }
3447 
3448 namespace localstorage {
3449 
CreateQuotaClient()3450 already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient() {
3451   AssertIsOnBackgroundThread();
3452   MOZ_ASSERT(CachedNextGenLocalStorageEnabled());
3453 
3454   RefPtr<QuotaClient> client = new QuotaClient();
3455   return client.forget();
3456 }
3457 
3458 }  // namespace localstorage
3459 
3460 /*******************************************************************************
3461  * DatastoreWriteOptimizer
3462  ******************************************************************************/
3463 
ApplyAndReset(nsTArray<LSItemInfo> & aOrderedItems)3464 void DatastoreWriteOptimizer::ApplyAndReset(
3465     nsTArray<LSItemInfo>& aOrderedItems) {
3466   AssertIsOnOwningThread();
3467 
3468   // The mWriteInfos hash table contains all write infos, but it keeps them in
3469   // an arbitrary order, which means write infos need to be sorted before being
3470   // processed. However, the order is not important for deletions and normal
3471   // updates. Usually, filtering out deletions and updates would require extra
3472   // work, but we have to check the hash table for each ordered item anyway, so
3473   // we can remove the write info if it is a deletion or update without adding
3474   // extra overhead. In the end, only insertions need to be sorted before being
3475   // processed.
3476 
3477   if (mTruncateInfo) {
3478     aOrderedItems.Clear();
3479     mTruncateInfo = nullptr;
3480   }
3481 
3482   for (int32_t index = aOrderedItems.Length() - 1; index >= 0; index--) {
3483     LSItemInfo& item = aOrderedItems[index];
3484 
3485     if (auto entry = mWriteInfos.Lookup(item.key())) {
3486       WriteInfo* writeInfo = entry->get();
3487 
3488       switch (writeInfo->GetType()) {
3489         case WriteInfo::DeleteItem:
3490           aOrderedItems.RemoveElementAt(index);
3491           entry.Remove();
3492           break;
3493 
3494         case WriteInfo::UpdateItem: {
3495           auto updateItemInfo = static_cast<UpdateItemInfo*>(writeInfo);
3496           if (updateItemInfo->UpdateWithMove()) {
3497             // See the comment in LSWriteOptimizer::InsertItem for more details
3498             // about the UpdateWithMove flag.
3499 
3500             aOrderedItems.RemoveElementAt(index);
3501             entry.Data() = MakeUnique<InsertItemInfo>(
3502                 updateItemInfo->SerialNumber(), updateItemInfo->GetKey(),
3503                 updateItemInfo->GetValue());
3504           } else {
3505             item.value() = updateItemInfo->GetValue();
3506             entry.Remove();
3507           }
3508           break;
3509         }
3510 
3511         case WriteInfo::InsertItem:
3512           break;
3513 
3514         default:
3515           MOZ_CRASH("Bad type!");
3516       }
3517     }
3518   }
3519 
3520   nsTArray<NotNull<WriteInfo*>> writeInfos;
3521   GetSortedWriteInfos(writeInfos);
3522 
3523   for (WriteInfo* writeInfo : writeInfos) {
3524     MOZ_ASSERT(writeInfo->GetType() == WriteInfo::InsertItem);
3525 
3526     auto insertItemInfo = static_cast<InsertItemInfo*>(writeInfo);
3527 
3528     LSItemInfo* itemInfo = aOrderedItems.AppendElement();
3529     itemInfo->key() = insertItemInfo->GetKey();
3530     itemInfo->value() = insertItemInfo->GetValue();
3531   }
3532 
3533   mWriteInfos.Clear();
3534 }
3535 
3536 /*******************************************************************************
3537  * ConnectionWriteOptimizer
3538  ******************************************************************************/
3539 
Perform(Connection * aConnection,bool aShadowWrites)3540 Result<int64_t, nsresult> ConnectionWriteOptimizer::Perform(
3541     Connection* aConnection, bool aShadowWrites) {
3542   AssertIsOnGlobalConnectionThread();
3543   MOZ_ASSERT(aConnection);
3544 
3545   // The order of elements is not stored in the database, so write infos don't
3546   // need to be sorted before being processed.
3547 
3548   if (mTruncateInfo) {
3549     QM_TRY(PerformTruncate(aConnection, aShadowWrites));
3550   }
3551 
3552   for (const auto& entry : mWriteInfos) {
3553     const WriteInfo* const writeInfo = entry.GetWeak();
3554 
3555     switch (writeInfo->GetType()) {
3556       case WriteInfo::InsertItem:
3557       case WriteInfo::UpdateItem: {
3558         const auto* const insertItemInfo =
3559             static_cast<const InsertItemInfo*>(writeInfo);
3560 
3561         QM_TRY(PerformInsertOrUpdate(aConnection, aShadowWrites,
3562                                      insertItemInfo->GetKey(),
3563                                      insertItemInfo->GetValue()));
3564 
3565         break;
3566       }
3567 
3568       case WriteInfo::DeleteItem: {
3569         const auto* const deleteItemInfo =
3570             static_cast<const DeleteItemInfo*>(writeInfo);
3571 
3572         QM_TRY(PerformDelete(aConnection, aShadowWrites,
3573                              deleteItemInfo->GetKey()));
3574 
3575         break;
3576       }
3577 
3578       default:
3579         MOZ_CRASH("Bad type!");
3580     }
3581   }
3582 
3583   QM_TRY(aConnection->ExecuteCachedStatement(
3584       "UPDATE database "
3585       "SET usage = usage + :delta"_ns,
3586       [this](auto& stmt) -> Result<Ok, nsresult> {
3587         QM_TRY(stmt.BindInt64ByName("delta"_ns, mTotalDelta));
3588 
3589         return Ok{};
3590       }));
3591 
3592   QM_TRY_INSPECT(const auto& stmt, CreateAndExecuteSingleStepStatement<
3593                                        SingleStepResult::ReturnNullIfNoResult>(
3594                                        aConnection->MutableStorageConnection(),
3595                                        "SELECT usage FROM database"_ns));
3596 
3597   QM_TRY(OkIf(stmt), Err(NS_ERROR_FAILURE));
3598 
3599   QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE(*stmt, GetInt64, 0));
3600 }
3601 
PerformInsertOrUpdate(Connection * aConnection,bool aShadowWrites,const nsAString & aKey,const LSValue & aValue)3602 nsresult ConnectionWriteOptimizer::PerformInsertOrUpdate(
3603     Connection* aConnection, bool aShadowWrites, const nsAString& aKey,
3604     const LSValue& aValue) {
3605   AssertIsOnGlobalConnectionThread();
3606   MOZ_ASSERT(aConnection);
3607 
3608   QM_TRY(aConnection->ExecuteCachedStatement(
3609       "INSERT OR REPLACE INTO data (key, value, utf16Length, compressed) "
3610       "VALUES(:key, :value, :utf16Length, :compressed)"_ns,
3611       [&aKey, &aValue](auto& stmt) -> Result<Ok, nsresult> {
3612         QM_TRY(stmt.BindStringByName("key"_ns, aKey));
3613         QM_TRY(stmt.BindUTF8StringByName("value"_ns, aValue));
3614         QM_TRY(stmt.BindInt32ByName("utf16Length"_ns, aValue.UTF16Length()));
3615         QM_TRY(stmt.BindInt32ByName("compressed"_ns, aValue.IsCompressed()));
3616 
3617         return Ok{};
3618       }));
3619 
3620   if (!aShadowWrites) {
3621     return NS_OK;
3622   }
3623 
3624   QM_TRY(aConnection->ExecuteCachedStatement(
3625       "INSERT OR REPLACE INTO shadow.webappsstore2 "
3626       "(originAttributes, originKey, scope, key, value) "
3627       "VALUES (:originAttributes, :originKey, :scope, :key, :value) "_ns,
3628       [&aConnection, &aKey, &aValue](auto& stmt) -> Result<Ok, nsresult> {
3629         const ArchivedOriginScope* const archivedOriginScope =
3630             aConnection->GetArchivedOriginScope();
3631 
3632         QM_TRY(archivedOriginScope->BindToStatement(&stmt));
3633 
3634         QM_TRY(stmt.BindUTF8StringByName(
3635             "scope"_ns, Scheme0Scope(archivedOriginScope->OriginSuffix(),
3636                                      archivedOriginScope->OriginNoSuffix())));
3637 
3638         QM_TRY(stmt.BindStringByName("key"_ns, aKey));
3639 
3640         if (aValue.IsCompressed()) {
3641           nsCString value;
3642           QM_TRY(OkIf(SnappyUncompress(aValue, value)), Err(NS_ERROR_FAILURE));
3643           QM_TRY(stmt.BindUTF8StringByName("value"_ns, value));
3644         } else {
3645           QM_TRY(stmt.BindUTF8StringByName("value"_ns, aValue));
3646         }
3647 
3648         return Ok{};
3649       }));
3650 
3651   return NS_OK;
3652 }
3653 
PerformDelete(Connection * aConnection,bool aShadowWrites,const nsAString & aKey)3654 nsresult ConnectionWriteOptimizer::PerformDelete(Connection* aConnection,
3655                                                  bool aShadowWrites,
3656                                                  const nsAString& aKey) {
3657   AssertIsOnGlobalConnectionThread();
3658   MOZ_ASSERT(aConnection);
3659 
3660   QM_TRY(aConnection->ExecuteCachedStatement(
3661       "DELETE FROM data "
3662       "WHERE key = :key;"_ns,
3663       [&aKey](auto& stmt) -> Result<Ok, nsresult> {
3664         QM_TRY(stmt.BindStringByName("key"_ns, aKey));
3665 
3666         return Ok{};
3667       }));
3668 
3669   if (!aShadowWrites) {
3670     return NS_OK;
3671   }
3672 
3673   QM_TRY(aConnection->ExecuteCachedStatement(
3674       "DELETE FROM shadow.webappsstore2 "
3675       "WHERE originAttributes = :originAttributes "
3676       "AND originKey = :originKey "
3677       "AND key = :key;"_ns,
3678       [&aConnection, &aKey](auto& stmt) -> Result<Ok, nsresult> {
3679         QM_TRY(aConnection->GetArchivedOriginScope()->BindToStatement(&stmt));
3680 
3681         QM_TRY(stmt.BindStringByName("key"_ns, aKey));
3682 
3683         return Ok{};
3684       }));
3685 
3686   return NS_OK;
3687 }
3688 
PerformTruncate(Connection * aConnection,bool aShadowWrites)3689 nsresult ConnectionWriteOptimizer::PerformTruncate(Connection* aConnection,
3690                                                    bool aShadowWrites) {
3691   AssertIsOnGlobalConnectionThread();
3692   MOZ_ASSERT(aConnection);
3693 
3694   QM_TRY(aConnection->ExecuteCachedStatement("DELETE FROM data;"_ns));
3695 
3696   if (!aShadowWrites) {
3697     return NS_OK;
3698   }
3699 
3700   QM_TRY(aConnection->ExecuteCachedStatement(
3701       "DELETE FROM shadow.webappsstore2 "
3702       "WHERE originAttributes = :originAttributes "
3703       "AND originKey = :originKey;"_ns,
3704       [&aConnection](auto& stmt) -> Result<Ok, nsresult> {
3705         QM_TRY(aConnection->GetArchivedOriginScope()->BindToStatement(&stmt));
3706 
3707         return Ok{};
3708       }));
3709 
3710   return NS_OK;
3711 }
3712 
3713 /*******************************************************************************
3714  * DatastoreOperationBase
3715  ******************************************************************************/
3716 
3717 /*******************************************************************************
3718  * ConnectionDatastoreOperationBase
3719  ******************************************************************************/
3720 
ConnectionDatastoreOperationBase(Connection * aConnection,bool aEnsureStorageConnection)3721 ConnectionDatastoreOperationBase::ConnectionDatastoreOperationBase(
3722     Connection* aConnection, bool aEnsureStorageConnection)
3723     : mConnection(aConnection),
3724       mEnsureStorageConnection(aEnsureStorageConnection) {
3725   MOZ_ASSERT(aConnection);
3726 }
3727 
~ConnectionDatastoreOperationBase()3728 ConnectionDatastoreOperationBase::~ConnectionDatastoreOperationBase() {
3729   MOZ_ASSERT(!mConnection,
3730              "ConnectionDatabaseOperationBase::Cleanup() was not called by a "
3731              "subclass!");
3732 }
3733 
Cleanup()3734 void ConnectionDatastoreOperationBase::Cleanup() {
3735   AssertIsOnOwningThread();
3736   MOZ_ASSERT(mConnection);
3737 
3738   mConnection = nullptr;
3739 
3740   NoteComplete();
3741 }
3742 
OnSuccess()3743 void ConnectionDatastoreOperationBase::OnSuccess() { AssertIsOnOwningThread(); }
3744 
OnFailure(nsresult aResultCode)3745 void ConnectionDatastoreOperationBase::OnFailure(nsresult aResultCode) {
3746   AssertIsOnOwningThread();
3747   MOZ_ASSERT(NS_FAILED(aResultCode));
3748 }
3749 
RunOnConnectionThread()3750 void ConnectionDatastoreOperationBase::RunOnConnectionThread() {
3751   AssertIsOnGlobalConnectionThread();
3752   MOZ_ASSERT(mConnection);
3753   MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
3754 
3755   if (!MayProceedOnNonOwningThread()) {
3756     SetFailureCode(NS_ERROR_ABORT);
3757   } else {
3758     nsresult rv = NS_OK;
3759 
3760     // The boolean flag is only used by the CloseOp to avoid creating empty
3761     // databases.
3762     if (mEnsureStorageConnection) {
3763       rv = mConnection->EnsureStorageConnection();
3764       if (NS_WARN_IF(NS_FAILED(rv))) {
3765         SetFailureCode(rv);
3766       } else {
3767         MOZ_ASSERT(mConnection->HasStorageConnection());
3768       }
3769     }
3770 
3771     if (NS_SUCCEEDED(rv)) {
3772       rv = DoDatastoreWork();
3773       if (NS_FAILED(rv)) {
3774         SetFailureCode(rv);
3775       }
3776     }
3777   }
3778 
3779   MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
3780 }
3781 
RunOnOwningThread()3782 void ConnectionDatastoreOperationBase::RunOnOwningThread() {
3783   AssertIsOnOwningThread();
3784   MOZ_ASSERT(mConnection);
3785 
3786   if (!MayProceed()) {
3787     MaybeSetFailureCode(NS_ERROR_ABORT);
3788   }
3789 
3790   if (NS_SUCCEEDED(ResultCode())) {
3791     OnSuccess();
3792   } else {
3793     OnFailure(ResultCode());
3794   }
3795 
3796   Cleanup();
3797 }
3798 
3799 NS_IMETHODIMP
Run()3800 ConnectionDatastoreOperationBase::Run() {
3801   if (IsOnGlobalConnectionThread()) {
3802     RunOnConnectionThread();
3803   } else {
3804     RunOnOwningThread();
3805   }
3806 
3807   return NS_OK;
3808 }
3809 
3810 /*******************************************************************************
3811  * Connection implementation
3812  ******************************************************************************/
3813 
Connection(ConnectionThread * aConnectionThread,const OriginMetadata & aOriginMetadata,UniquePtr<ArchivedOriginScope> && aArchivedOriginScope,bool aDatabaseWasNotAvailable)3814 Connection::Connection(ConnectionThread* aConnectionThread,
3815                        const OriginMetadata& aOriginMetadata,
3816                        UniquePtr<ArchivedOriginScope>&& aArchivedOriginScope,
3817                        bool aDatabaseWasNotAvailable)
3818     : mConnectionThread(aConnectionThread),
3819       mQuotaClient(QuotaClient::GetInstance()),
3820       mArchivedOriginScope(std::move(aArchivedOriginScope)),
3821       mOriginMetadata(aOriginMetadata),
3822       mDatabaseWasNotAvailable(aDatabaseWasNotAvailable),
3823       mHasCreatedDatabase(false),
3824       mFlushScheduled(false)
3825 #ifdef DEBUG
3826       ,
3827       mInUpdateBatch(false),
3828       mFinished(false)
3829 #endif
3830 {
3831   AssertIsOnOwningThread();
3832   MOZ_ASSERT(!aOriginMetadata.mGroup.IsEmpty());
3833   MOZ_ASSERT(!aOriginMetadata.mOrigin.IsEmpty());
3834 }
3835 
~Connection()3836 Connection::~Connection() {
3837   AssertIsOnOwningThread();
3838   MOZ_ASSERT(!mFlushScheduled);
3839   MOZ_ASSERT(!mInUpdateBatch);
3840   MOZ_ASSERT(mFinished);
3841 }
3842 
Dispatch(ConnectionDatastoreOperationBase * aOp)3843 void Connection::Dispatch(ConnectionDatastoreOperationBase* aOp) {
3844   AssertIsOnOwningThread();
3845   MOZ_ASSERT(mConnectionThread);
3846 
3847   MOZ_ALWAYS_SUCCEEDS(
3848       mConnectionThread->mThread->Dispatch(aOp, NS_DISPATCH_NORMAL));
3849 }
3850 
Close(nsIRunnable * aCallback)3851 void Connection::Close(nsIRunnable* aCallback) {
3852   AssertIsOnOwningThread();
3853   MOZ_ASSERT(aCallback);
3854 
3855   if (mFlushScheduled) {
3856     MOZ_ASSERT(mFlushTimer);
3857     MOZ_ALWAYS_SUCCEEDS(mFlushTimer->Cancel());
3858 
3859     Flush();
3860 
3861     mFlushTimer = nullptr;
3862   }
3863 
3864   RefPtr<CloseOp> op = new CloseOp(this, aCallback);
3865 
3866   Dispatch(op);
3867 }
3868 
SetItem(const nsString & aKey,const LSValue & aValue,int64_t aDelta,bool aIsNewItem)3869 void Connection::SetItem(const nsString& aKey, const LSValue& aValue,
3870                          int64_t aDelta, bool aIsNewItem) {
3871   AssertIsOnOwningThread();
3872   MOZ_ASSERT(mInUpdateBatch);
3873 
3874   if (aIsNewItem) {
3875     mWriteOptimizer.InsertItem(aKey, aValue, aDelta);
3876   } else {
3877     mWriteOptimizer.UpdateItem(aKey, aValue, aDelta);
3878   }
3879 }
3880 
RemoveItem(const nsString & aKey,int64_t aDelta)3881 void Connection::RemoveItem(const nsString& aKey, int64_t aDelta) {
3882   AssertIsOnOwningThread();
3883   MOZ_ASSERT(mInUpdateBatch);
3884 
3885   mWriteOptimizer.DeleteItem(aKey, aDelta);
3886 }
3887 
Clear(int64_t aDelta)3888 void Connection::Clear(int64_t aDelta) {
3889   AssertIsOnOwningThread();
3890   MOZ_ASSERT(mInUpdateBatch);
3891 
3892   mWriteOptimizer.Truncate(aDelta);
3893 }
3894 
BeginUpdateBatch()3895 void Connection::BeginUpdateBatch() {
3896   AssertIsOnOwningThread();
3897   MOZ_ASSERT(!mInUpdateBatch);
3898 
3899 #ifdef DEBUG
3900   mInUpdateBatch = true;
3901 #endif
3902 }
3903 
EndUpdateBatch()3904 void Connection::EndUpdateBatch() {
3905   AssertIsOnOwningThread();
3906   MOZ_ASSERT(mInUpdateBatch);
3907 
3908   if (mWriteOptimizer.HasWrites() && !mFlushScheduled) {
3909     ScheduleFlush();
3910   }
3911 
3912 #ifdef DEBUG
3913   mInUpdateBatch = false;
3914 #endif
3915 }
3916 
EnsureStorageConnection()3917 nsresult Connection::EnsureStorageConnection() {
3918   AssertIsOnGlobalConnectionThread();
3919 
3920   if (HasStorageConnection()) {
3921     return NS_OK;
3922   }
3923 
3924   QuotaManager* quotaManager = QuotaManager::Get();
3925   MOZ_ASSERT(quotaManager);
3926 
3927   if (!mDatabaseWasNotAvailable || mHasCreatedDatabase) {
3928     QM_TRY_INSPECT(const auto& directoryEntry,
3929                    quotaManager->GetDirectoryForOrigin(PERSISTENCE_TYPE_DEFAULT,
3930                                                        Origin()));
3931 
3932     QM_TRY(directoryEntry->Append(
3933         NS_LITERAL_STRING_FROM_CSTRING(LS_DIRECTORY_NAME)));
3934 
3935     QM_TRY(directoryEntry->GetPath(mDirectoryPath));
3936     QM_TRY(directoryEntry->Append(kDataFileName));
3937 
3938     QM_TRY_INSPECT(
3939         const auto& databaseFilePath,
3940         MOZ_TO_RESULT_INVOKE_TYPED(nsString, directoryEntry, GetPath));
3941 
3942     QM_TRY_UNWRAP(auto storageConnection,
3943                   GetStorageConnection(databaseFilePath));
3944     LazyInit(WrapMovingNotNull(std::move(storageConnection)));
3945 
3946     return NS_OK;
3947   }
3948 
3949   RefPtr<InitTemporaryOriginHelper> helper =
3950       new InitTemporaryOriginHelper(mOriginMetadata);
3951 
3952   QM_TRY_INSPECT(const auto& originDirectoryPath,
3953                  helper->BlockAndReturnOriginDirectoryPath());
3954 
3955   QM_TRY_INSPECT(const auto& directoryEntry,
3956                  QM_NewLocalFile(originDirectoryPath));
3957 
3958   QM_TRY(directoryEntry->Append(
3959       NS_LITERAL_STRING_FROM_CSTRING(LS_DIRECTORY_NAME)));
3960 
3961   QM_TRY(directoryEntry->GetPath(mDirectoryPath));
3962 
3963   QM_TRY_INSPECT(const bool& exists,
3964                  MOZ_TO_RESULT_INVOKE(directoryEntry, Exists));
3965 
3966   if (!exists) {
3967     QM_TRY(directoryEntry->Create(nsIFile::DIRECTORY_TYPE, 0755));
3968   }
3969 
3970   QM_TRY(directoryEntry->Append(kDataFileName));
3971 
3972 #ifdef DEBUG
3973   {
3974     QM_TRY_INSPECT(const bool& exists,
3975                    MOZ_TO_RESULT_INVOKE(directoryEntry, Exists));
3976 
3977     MOZ_ASSERT(!exists);
3978   }
3979 #endif
3980 
3981   QM_TRY_INSPECT(const auto& usageFile, GetUsageFile(mDirectoryPath));
3982 
3983   nsCOMPtr<mozIStorageConnection> storageConnection;
3984 
3985   auto autoRemove = MakeScopeExit([&storageConnection, &directoryEntry] {
3986     if (storageConnection) {
3987       MOZ_ALWAYS_SUCCEEDS(storageConnection->Close());
3988     }
3989 
3990     nsresult rv = directoryEntry->Remove(false);
3991     if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
3992         rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
3993       NS_WARNING("Failed to remove database file!");
3994     }
3995   });
3996 
3997   QM_TRY_UNWRAP(storageConnection,
3998                 CreateStorageConnection(*directoryEntry, *usageFile, Origin(),
3999                                         [] { MOZ_ASSERT_UNREACHABLE(); }));
4000 
4001   MOZ_ASSERT(mQuotaClient);
4002 
4003   MutexAutoLock shadowDatabaseLock(mQuotaClient->ShadowDatabaseMutex());
4004 
4005   nsCOMPtr<mozIStorageConnection> shadowConnection;
4006   if (!gInitializedShadowStorage) {
4007     QM_TRY_UNWRAP(shadowConnection,
4008                   CreateShadowStorageConnection(quotaManager->GetBasePath()));
4009 
4010     gInitializedShadowStorage = true;
4011   }
4012 
4013   autoRemove.release();
4014 
4015   if (!mHasCreatedDatabase) {
4016     mHasCreatedDatabase = true;
4017   }
4018 
4019   LazyInit(WrapMovingNotNull(std::move(storageConnection)));
4020 
4021   return NS_OK;
4022 }
4023 
CloseStorageConnection()4024 void Connection::CloseStorageConnection() {
4025   AssertIsOnGlobalConnectionThread();
4026 
4027   CachingDatabaseConnection::Close();
4028 }
4029 
BeginWriteTransaction()4030 nsresult Connection::BeginWriteTransaction() {
4031   AssertIsOnGlobalConnectionThread();
4032   MOZ_ASSERT(HasStorageConnection());
4033 
4034   QM_TRY(ExecuteCachedStatement("BEGIN IMMEDIATE;"_ns));
4035 
4036   return NS_OK;
4037 }
4038 
CommitWriteTransaction()4039 nsresult Connection::CommitWriteTransaction() {
4040   AssertIsOnGlobalConnectionThread();
4041   MOZ_ASSERT(HasStorageConnection());
4042 
4043   QM_TRY(ExecuteCachedStatement("COMMIT;"_ns));
4044 
4045   return NS_OK;
4046 }
4047 
RollbackWriteTransaction()4048 nsresult Connection::RollbackWriteTransaction() {
4049   AssertIsOnGlobalConnectionThread();
4050   MOZ_ASSERT(HasStorageConnection());
4051 
4052   QM_TRY_INSPECT(const auto& stmt, BorrowCachedStatement("ROLLBACK;"_ns));
4053 
4054   // This may fail if SQLite already rolled back the transaction so ignore any
4055   // errors.
4056   Unused << stmt->Execute();
4057 
4058   return NS_OK;
4059 }
4060 
ScheduleFlush()4061 void Connection::ScheduleFlush() {
4062   AssertIsOnOwningThread();
4063   MOZ_ASSERT(mWriteOptimizer.HasWrites());
4064   MOZ_ASSERT(!mFlushScheduled);
4065 
4066   if (!mFlushTimer) {
4067     mFlushTimer = NS_NewTimer();
4068     MOZ_ASSERT(mFlushTimer);
4069   }
4070 
4071   MOZ_ALWAYS_SUCCEEDS(mFlushTimer->InitWithNamedFuncCallback(
4072       FlushTimerCallback, this, kFlushTimeoutMs, nsITimer::TYPE_ONE_SHOT,
4073       "Connection::FlushTimerCallback"));
4074 
4075   mFlushScheduled = true;
4076 }
4077 
Flush()4078 void Connection::Flush() {
4079   AssertIsOnOwningThread();
4080   MOZ_ASSERT(mFlushScheduled);
4081 
4082   if (mWriteOptimizer.HasWrites()) {
4083     RefPtr<FlushOp> op = new FlushOp(this, std::move(mWriteOptimizer));
4084 
4085     Dispatch(op);
4086   }
4087 
4088   mFlushScheduled = false;
4089 }
4090 
4091 // static
FlushTimerCallback(nsITimer * aTimer,void * aClosure)4092 void Connection::FlushTimerCallback(nsITimer* aTimer, void* aClosure) {
4093   MOZ_ASSERT(aClosure);
4094 
4095   auto* self = static_cast<Connection*>(aClosure);
4096   MOZ_ASSERT(self);
4097   MOZ_ASSERT(self->mFlushScheduled);
4098 
4099   self->Flush();
4100 }
4101 
4102 Result<nsString, nsresult>
BlockAndReturnOriginDirectoryPath()4103 Connection::InitTemporaryOriginHelper::BlockAndReturnOriginDirectoryPath() {
4104   AssertIsOnGlobalConnectionThread();
4105 
4106   QuotaManager* quotaManager = QuotaManager::Get();
4107   MOZ_ASSERT(quotaManager);
4108 
4109   MOZ_ALWAYS_SUCCEEDS(
4110       quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
4111 
4112   mozilla::MonitorAutoLock lock(mMonitor);
4113   while (mWaiting) {
4114     lock.Wait();
4115   }
4116 
4117   QM_TRY(mIOThreadResultCode);
4118 
4119   return mOriginDirectoryPath;
4120 }
4121 
RunOnIOThread()4122 nsresult Connection::InitTemporaryOriginHelper::RunOnIOThread() {
4123   AssertIsOnIOThread();
4124 
4125   QuotaManager* quotaManager = QuotaManager::Get();
4126   MOZ_ASSERT(quotaManager);
4127 
4128   QM_TRY_INSPECT(const auto& directoryEntry,
4129                  quotaManager
4130                      ->EnsureTemporaryOriginIsInitialized(
4131                          PERSISTENCE_TYPE_DEFAULT, mOriginMetadata)
4132                      .map([](const auto& res) { return res.first; }));
4133 
4134   QM_TRY(directoryEntry->GetPath(mOriginDirectoryPath));
4135 
4136   return NS_OK;
4137 }
4138 
4139 NS_IMETHODIMP
Run()4140 Connection::InitTemporaryOriginHelper::Run() {
4141   AssertIsOnIOThread();
4142 
4143   nsresult rv = RunOnIOThread();
4144   if (NS_WARN_IF(NS_FAILED(rv))) {
4145     mIOThreadResultCode = rv;
4146   }
4147 
4148   mozilla::MonitorAutoLock lock(mMonitor);
4149   MOZ_ASSERT(mWaiting);
4150 
4151   mWaiting = false;
4152   lock.Notify();
4153 
4154   return NS_OK;
4155 }
4156 
FlushOp(Connection * aConnection,ConnectionWriteOptimizer && aWriteOptimizer)4157 Connection::FlushOp::FlushOp(Connection* aConnection,
4158                              ConnectionWriteOptimizer&& aWriteOptimizer)
4159     : ConnectionDatastoreOperationBase(aConnection),
4160       mWriteOptimizer(std::move(aWriteOptimizer)),
4161       mShadowWrites(gShadowWrites) {}
4162 
DoDatastoreWork()4163 nsresult Connection::FlushOp::DoDatastoreWork() {
4164   AssertIsOnGlobalConnectionThread();
4165   MOZ_ASSERT(mConnection);
4166 
4167   AutoWriteTransaction autoWriteTransaction(mShadowWrites);
4168 
4169   QM_TRY(autoWriteTransaction.Start(mConnection));
4170 
4171   QM_TRY_INSPECT(const int64_t& usage,
4172                  mWriteOptimizer.Perform(mConnection, mShadowWrites));
4173 
4174   QM_TRY_INSPECT(const auto& usageFile,
4175                  GetUsageFile(mConnection->DirectoryPath()));
4176 
4177   QM_TRY_INSPECT(const auto& usageJournalFile,
4178                  GetUsageJournalFile(mConnection->DirectoryPath()));
4179 
4180   QM_TRY(UpdateUsageFile(usageFile, usageJournalFile, usage));
4181 
4182   QM_TRY(autoWriteTransaction.Commit());
4183 
4184   QM_TRY(usageJournalFile->Remove(false));
4185 
4186   return NS_OK;
4187 }
4188 
Cleanup()4189 void Connection::FlushOp::Cleanup() {
4190   AssertIsOnOwningThread();
4191 
4192   mWriteOptimizer.Reset();
4193 
4194   MOZ_ASSERT(!mWriteOptimizer.HasWrites());
4195 
4196   ConnectionDatastoreOperationBase::Cleanup();
4197 }
4198 
DoDatastoreWork()4199 nsresult Connection::CloseOp::DoDatastoreWork() {
4200   AssertIsOnGlobalConnectionThread();
4201   MOZ_ASSERT(mConnection);
4202 
4203   if (mConnection->HasStorageConnection()) {
4204     mConnection->CloseStorageConnection();
4205   }
4206 
4207   return NS_OK;
4208 }
4209 
Cleanup()4210 void Connection::CloseOp::Cleanup() {
4211   AssertIsOnOwningThread();
4212   MOZ_ASSERT(mConnection);
4213 
4214   mConnection->mConnectionThread->mConnections.Remove(mConnection->Origin());
4215 
4216 #ifdef DEBUG
4217   MOZ_ASSERT(!mConnection->mFinished);
4218   mConnection->mFinished = true;
4219 #endif
4220 
4221   nsCOMPtr<nsIRunnable> callback;
4222   mCallback.swap(callback);
4223 
4224   callback->Run();
4225 
4226   ConnectionDatastoreOperationBase::Cleanup();
4227 }
4228 
4229 /*******************************************************************************
4230  * ConnectionThread implementation
4231  ******************************************************************************/
4232 
ConnectionThread()4233 ConnectionThread::ConnectionThread() {
4234   AssertIsOnOwningThread();
4235   AssertIsOnBackgroundThread();
4236 
4237   MOZ_ALWAYS_SUCCEEDS(NS_NewNamedThread("LS Thread", getter_AddRefs(mThread)));
4238 }
4239 
~ConnectionThread()4240 ConnectionThread::~ConnectionThread() {
4241   AssertIsOnOwningThread();
4242   MOZ_ASSERT(!mConnections.Count());
4243 }
4244 
IsOnConnectionThread()4245 bool ConnectionThread::IsOnConnectionThread() {
4246   MOZ_ASSERT(mThread);
4247 
4248   bool current;
4249   return NS_SUCCEEDED(mThread->IsOnCurrentThread(&current)) && current;
4250 }
4251 
AssertIsOnConnectionThread()4252 void ConnectionThread::AssertIsOnConnectionThread() {
4253   MOZ_ASSERT(IsOnConnectionThread());
4254 }
4255 
CreateConnection(const OriginMetadata & aOriginMetadata,UniquePtr<ArchivedOriginScope> && aArchivedOriginScope,bool aDatabaseWasNotAvailable)4256 already_AddRefed<Connection> ConnectionThread::CreateConnection(
4257     const OriginMetadata& aOriginMetadata,
4258     UniquePtr<ArchivedOriginScope>&& aArchivedOriginScope,
4259     bool aDatabaseWasNotAvailable) {
4260   AssertIsOnOwningThread();
4261   MOZ_ASSERT(!aOriginMetadata.mOrigin.IsEmpty());
4262   MOZ_ASSERT(!mConnections.Contains(aOriginMetadata.mOrigin));
4263 
4264   RefPtr<Connection> connection =
4265       new Connection(this, aOriginMetadata, std::move(aArchivedOriginScope),
4266                      aDatabaseWasNotAvailable);
4267   mConnections.InsertOrUpdate(aOriginMetadata.mOrigin, RefPtr{connection});
4268 
4269   return connection.forget();
4270 }
4271 
Shutdown()4272 void ConnectionThread::Shutdown() {
4273   AssertIsOnOwningThread();
4274   MOZ_ASSERT(mThread);
4275 
4276   mThread->Shutdown();
4277 }
4278 
4279 /*******************************************************************************
4280  * Datastore
4281  ******************************************************************************/
4282 
Datastore(const OriginMetadata & aOriginMetadata,uint32_t aPrivateBrowsingId,int64_t aUsage,int64_t aSizeOfKeys,int64_t aSizeOfItems,RefPtr<DirectoryLock> && aDirectoryLock,RefPtr<Connection> && aConnection,RefPtr<QuotaObject> && aQuotaObject,nsTHashMap<nsStringHashKey,LSValue> & aValues,nsTArray<LSItemInfo> && aOrderedItems)4283 Datastore::Datastore(const OriginMetadata& aOriginMetadata,
4284                      uint32_t aPrivateBrowsingId, int64_t aUsage,
4285                      int64_t aSizeOfKeys, int64_t aSizeOfItems,
4286                      RefPtr<DirectoryLock>&& aDirectoryLock,
4287                      RefPtr<Connection>&& aConnection,
4288                      RefPtr<QuotaObject>&& aQuotaObject,
4289                      nsTHashMap<nsStringHashKey, LSValue>& aValues,
4290                      nsTArray<LSItemInfo>&& aOrderedItems)
4291     : mDirectoryLock(std::move(aDirectoryLock)),
4292       mConnection(std::move(aConnection)),
4293       mQuotaObject(std::move(aQuotaObject)),
4294       mOrderedItems(std::move(aOrderedItems)),
4295       mOriginMetadata(aOriginMetadata),
4296       mPrivateBrowsingId(aPrivateBrowsingId),
4297       mUsage(aUsage),
4298       mUpdateBatchUsage(-1),
4299       mSizeOfKeys(aSizeOfKeys),
4300       mSizeOfItems(aSizeOfItems),
4301       mClosed(false),
4302       mInUpdateBatch(false),
4303       mHasLivePrivateDatastore(false) {
4304   AssertIsOnBackgroundThread();
4305 
4306   mValues.SwapElements(aValues);
4307 }
4308 
~Datastore()4309 Datastore::~Datastore() {
4310   AssertIsOnBackgroundThread();
4311   MOZ_ASSERT(mClosed);
4312 }
4313 
Close()4314 void Datastore::Close() {
4315   AssertIsOnBackgroundThread();
4316   MOZ_ASSERT(!mClosed);
4317   MOZ_ASSERT(!mPrepareDatastoreOps.Count());
4318   MOZ_ASSERT(!mPreparedDatastores.Count());
4319   MOZ_ASSERT(!mDatabases.Count());
4320   MOZ_ASSERT(mDirectoryLock);
4321 
4322   mClosed = true;
4323 
4324   if (IsPersistent()) {
4325     MOZ_ASSERT(mConnection);
4326     MOZ_ASSERT(mQuotaObject);
4327 
4328     // We can't release the directory lock and unregister itself from the
4329     // hashtable until the connection is fully closed.
4330     nsCOMPtr<nsIRunnable> callback =
4331         NewRunnableMethod("dom::Datastore::ConnectionClosedCallback", this,
4332                           &Datastore::ConnectionClosedCallback);
4333     mConnection->Close(callback);
4334   } else {
4335     MOZ_ASSERT(!mConnection);
4336     MOZ_ASSERT(!mQuotaObject);
4337 
4338     // There's no connection, so it's safe to release the directory lock and
4339     // unregister itself from the hashtable.
4340 
4341     mDirectoryLock = nullptr;
4342 
4343     CleanupMetadata();
4344   }
4345 }
4346 
WaitForConnectionToComplete(nsIRunnable * aCallback)4347 void Datastore::WaitForConnectionToComplete(nsIRunnable* aCallback) {
4348   AssertIsOnBackgroundThread();
4349   MOZ_ASSERT(aCallback);
4350   MOZ_ASSERT(!mCompleteCallback);
4351   MOZ_ASSERT(mClosed);
4352 
4353   mCompleteCallback = aCallback;
4354 }
4355 
NoteLivePrepareDatastoreOp(PrepareDatastoreOp * aPrepareDatastoreOp)4356 void Datastore::NoteLivePrepareDatastoreOp(
4357     PrepareDatastoreOp* aPrepareDatastoreOp) {
4358   AssertIsOnBackgroundThread();
4359   MOZ_ASSERT(aPrepareDatastoreOp);
4360   MOZ_ASSERT(!mPrepareDatastoreOps.Contains(aPrepareDatastoreOp));
4361   MOZ_ASSERT(mDirectoryLock);
4362   MOZ_ASSERT(!mClosed);
4363 
4364   mPrepareDatastoreOps.Insert(aPrepareDatastoreOp);
4365 }
4366 
NoteFinishedPrepareDatastoreOp(PrepareDatastoreOp * aPrepareDatastoreOp)4367 void Datastore::NoteFinishedPrepareDatastoreOp(
4368     PrepareDatastoreOp* aPrepareDatastoreOp) {
4369   AssertIsOnBackgroundThread();
4370   MOZ_ASSERT(aPrepareDatastoreOp);
4371   MOZ_ASSERT(mPrepareDatastoreOps.Contains(aPrepareDatastoreOp));
4372   MOZ_ASSERT(mDirectoryLock);
4373   MOZ_ASSERT(!mClosed);
4374 
4375   mPrepareDatastoreOps.Remove(aPrepareDatastoreOp);
4376 
4377   QuotaManager::MaybeRecordQuotaClientShutdownStep(
4378       quota::Client::LS, "PrepareDatastoreOp finished"_ns);
4379 
4380   MaybeClose();
4381 }
4382 
NoteLivePrivateDatastore()4383 void Datastore::NoteLivePrivateDatastore() {
4384   AssertIsOnBackgroundThread();
4385   MOZ_ASSERT(!mHasLivePrivateDatastore);
4386   MOZ_ASSERT(mDirectoryLock);
4387   MOZ_ASSERT(!mClosed);
4388 
4389   mHasLivePrivateDatastore = true;
4390 }
4391 
NoteFinishedPrivateDatastore()4392 void Datastore::NoteFinishedPrivateDatastore() {
4393   AssertIsOnBackgroundThread();
4394   MOZ_ASSERT(mHasLivePrivateDatastore);
4395   MOZ_ASSERT(mDirectoryLock);
4396   MOZ_ASSERT(!mClosed);
4397 
4398   mHasLivePrivateDatastore = false;
4399 
4400   QuotaManager::MaybeRecordQuotaClientShutdownStep(
4401       quota::Client::LS, "PrivateDatastore finished"_ns);
4402 
4403   MaybeClose();
4404 }
4405 
NoteLivePreparedDatastore(PreparedDatastore * aPreparedDatastore)4406 void Datastore::NoteLivePreparedDatastore(
4407     PreparedDatastore* aPreparedDatastore) {
4408   AssertIsOnBackgroundThread();
4409   MOZ_ASSERT(aPreparedDatastore);
4410   MOZ_ASSERT(!mPreparedDatastores.Contains(aPreparedDatastore));
4411   MOZ_ASSERT(mDirectoryLock);
4412   MOZ_ASSERT(!mClosed);
4413 
4414   mPreparedDatastores.Insert(aPreparedDatastore);
4415 }
4416 
NoteFinishedPreparedDatastore(PreparedDatastore * aPreparedDatastore)4417 void Datastore::NoteFinishedPreparedDatastore(
4418     PreparedDatastore* aPreparedDatastore) {
4419   AssertIsOnBackgroundThread();
4420   MOZ_ASSERT(aPreparedDatastore);
4421   MOZ_ASSERT(mPreparedDatastores.Contains(aPreparedDatastore));
4422   MOZ_ASSERT(mDirectoryLock);
4423   MOZ_ASSERT(!mClosed);
4424 
4425   mPreparedDatastores.Remove(aPreparedDatastore);
4426 
4427   QuotaManager::MaybeRecordQuotaClientShutdownStep(
4428       quota::Client::LS, "PreparedDatastore finished"_ns);
4429 
4430   MaybeClose();
4431 }
4432 
NoteLiveDatabase(Database * aDatabase)4433 void Datastore::NoteLiveDatabase(Database* aDatabase) {
4434   AssertIsOnBackgroundThread();
4435   MOZ_ASSERT(aDatabase);
4436   MOZ_ASSERT(!mDatabases.Contains(aDatabase));
4437   MOZ_ASSERT(mDirectoryLock);
4438   MOZ_ASSERT(!mClosed);
4439 
4440   mDatabases.Insert(aDatabase);
4441 }
4442 
NoteFinishedDatabase(Database * aDatabase)4443 void Datastore::NoteFinishedDatabase(Database* aDatabase) {
4444   AssertIsOnBackgroundThread();
4445   MOZ_ASSERT(aDatabase);
4446   MOZ_ASSERT(mDatabases.Contains(aDatabase));
4447   MOZ_ASSERT(!mActiveDatabases.Contains(aDatabase));
4448   MOZ_ASSERT(mDirectoryLock);
4449   MOZ_ASSERT(!mClosed);
4450 
4451   mDatabases.Remove(aDatabase);
4452 
4453   QuotaManager::MaybeRecordQuotaClientShutdownStep(quota::Client::LS,
4454                                                    "Database finished"_ns);
4455 
4456   MaybeClose();
4457 }
4458 
NoteActiveDatabase(Database * aDatabase)4459 void Datastore::NoteActiveDatabase(Database* aDatabase) {
4460   AssertIsOnBackgroundThread();
4461   MOZ_ASSERT(aDatabase);
4462   MOZ_ASSERT(mDatabases.Contains(aDatabase));
4463   MOZ_ASSERT(!mActiveDatabases.Contains(aDatabase));
4464   MOZ_ASSERT(!mClosed);
4465 
4466   mActiveDatabases.Insert(aDatabase);
4467 }
4468 
NoteInactiveDatabase(Database * aDatabase)4469 void Datastore::NoteInactiveDatabase(Database* aDatabase) {
4470   AssertIsOnBackgroundThread();
4471   MOZ_ASSERT(aDatabase);
4472   MOZ_ASSERT(mDatabases.Contains(aDatabase));
4473   MOZ_ASSERT(mActiveDatabases.Contains(aDatabase));
4474   MOZ_ASSERT(!mClosed);
4475 
4476   mActiveDatabases.Remove(aDatabase);
4477 
4478   if (!mActiveDatabases.Count() && mPendingUsageDeltas.Length()) {
4479     int64_t finalDelta = 0;
4480 
4481     for (auto delta : mPendingUsageDeltas) {
4482       finalDelta += delta;
4483     }
4484 
4485     MOZ_ASSERT(finalDelta <= 0);
4486 
4487     if (finalDelta != 0) {
4488       DebugOnly<bool> ok = UpdateUsage(finalDelta);
4489       MOZ_ASSERT(ok);
4490     }
4491 
4492     mPendingUsageDeltas.Clear();
4493   }
4494 }
4495 
GetSnapshotLoadInfo(const nsString & aKey,bool & aAddKeyToUnknownItems,nsTHashtable<nsStringHashKey> & aLoadedItems,nsTArray<LSItemInfo> & aItemInfos,uint32_t & aNextLoadIndex,LSSnapshot::LoadState & aLoadState)4496 void Datastore::GetSnapshotLoadInfo(const nsString& aKey,
4497                                     bool& aAddKeyToUnknownItems,
4498                                     nsTHashtable<nsStringHashKey>& aLoadedItems,
4499                                     nsTArray<LSItemInfo>& aItemInfos,
4500                                     uint32_t& aNextLoadIndex,
4501                                     LSSnapshot::LoadState& aLoadState) {
4502   AssertIsOnBackgroundThread();
4503   MOZ_ASSERT(!mClosed);
4504   MOZ_ASSERT(!mInUpdateBatch);
4505 
4506 #ifdef DEBUG
4507   int64_t sizeOfKeys = 0;
4508   int64_t sizeOfItems = 0;
4509   for (auto item : mOrderedItems) {
4510     int64_t sizeOfKey = static_cast<int64_t>(item.key().Length());
4511     sizeOfKeys += sizeOfKey;
4512     sizeOfItems += sizeOfKey + static_cast<int64_t>(item.value().Length());
4513   }
4514   MOZ_ASSERT(mSizeOfKeys == sizeOfKeys);
4515   MOZ_ASSERT(mSizeOfItems == sizeOfItems);
4516 #endif
4517 
4518   // Computes load state optimized for current size of keys and items.
4519   // Zero key length and value can be passed to do a quick initial estimation.
4520   // If computed load state is already AllOrderedItems then excluded key length
4521   // and value length can't make it any better.
4522   auto GetLoadState = [&](int64_t aKeyLength, int64_t aValueLength) {
4523     if (mSizeOfKeys - aKeyLength <= gSnapshotPrefill) {
4524       if (mSizeOfItems - aKeyLength - aValueLength <= gSnapshotPrefill) {
4525         return LSSnapshot::LoadState::AllOrderedItems;
4526       }
4527 
4528       return LSSnapshot::LoadState::AllOrderedKeys;
4529     }
4530 
4531     return LSSnapshot::LoadState::Partial;
4532   };
4533 
4534   // Value for given aKey if aKey is not void (can be void too if value doesn't
4535   // exist for given aKey).
4536   LSValue value;
4537   // If aKey and value are not void, checkKey will be set to true. Once we find
4538   // an item for given aKey in one of the loops below, checkKey is set to false
4539   // to prevent additional comparison of strings (string implementation compares
4540   // string lengths first to avoid char by char comparison if possible).
4541   bool checkKey = false;
4542 
4543   // Avoid additional hash lookup if all ordered items fit into initial prefill
4544   // already.
4545   LSSnapshot::LoadState loadState = GetLoadState(/* aKeyLength */ 0,
4546                                                  /* aValueLength */ 0);
4547   if (loadState != LSSnapshot::LoadState::AllOrderedItems && !aKey.IsVoid()) {
4548     GetItem(aKey, value);
4549     if (!value.IsVoid()) {
4550       // Ok, we have a non void aKey and value.
4551 
4552       // We have to watch for aKey during one of the loops below to exclude it
4553       // from the size computation. The super fast mode (AllOrderedItems)
4554       // doesn't have to do that though.
4555       checkKey = true;
4556 
4557       // We have to compute load state again because aKey length and value
4558       // length is excluded from the size in this case.
4559       loadState = GetLoadState(aKey.Length(), value.Length());
4560     }
4561   }
4562 
4563   switch (loadState) {
4564     case LSSnapshot::LoadState::AllOrderedItems: {
4565       // We're sending all ordered items, we don't need to check keys because
4566       // mOrderedItems must contain a value for aKey if checkKey is true.
4567 
4568       aItemInfos.AppendElements(mOrderedItems);
4569 
4570       MOZ_ASSERT(aItemInfos.Length() == mValues.Count());
4571       aNextLoadIndex = mValues.Count();
4572 
4573       aAddKeyToUnknownItems = false;
4574 
4575       break;
4576     }
4577 
4578     case LSSnapshot::LoadState::AllOrderedKeys: {
4579       // We don't have enough snapshot budget to send all items, but we do have
4580       // enough to send all of the keys and to make a best effort to populate as
4581       // many values as possible. We send void string values once we run out of
4582       // budget. A complicating factor is that we want to make sure that we send
4583       // the value for aKey which is a localStorage read that's triggering this
4584       // request. Since that key can happen anywhere in the list of items, we
4585       // need to handle it specially.
4586       //
4587       // The loop is effectively doing 2 things in parallel:
4588       //
4589       //   1. Looking for the `aKey` to send. This is tracked by `checkKey`
4590       //      which is true if there was an `aKey` specified and until we
4591       //      populate its value, and false thereafter.
4592       //   2. Sending values until we run out of `size` budget and switch to
4593       //      sending void values. `doneSendingValues` tracks when we've run out
4594       //      of size budget, with `setVoidValue` tracking whether a value
4595       //      should be sent for each turn of the event loop but can be
4596       //      overridden when `aKey` is found.
4597 
4598       int64_t size = mSizeOfKeys;
4599       bool setVoidValue = false;
4600       bool doneSendingValues = false;
4601       for (uint32_t index = 0; index < mOrderedItems.Length(); index++) {
4602         const LSItemInfo& item = mOrderedItems[index];
4603 
4604         const nsString& key = item.key();
4605         const LSValue& value = item.value();
4606 
4607         if (checkKey && key == aKey) {
4608           checkKey = false;
4609           setVoidValue = false;
4610         } else if (!setVoidValue) {
4611           if (doneSendingValues) {
4612             setVoidValue = true;
4613           } else {
4614             size += static_cast<int64_t>(value.Length());
4615 
4616             if (size > gSnapshotPrefill) {
4617               setVoidValue = true;
4618               doneSendingValues = true;
4619 
4620               // We set doneSendingValues to true and that will guard against
4621               // entering this branch during next iterations. So aNextLoadIndex
4622               // is set only once.
4623               aNextLoadIndex = index;
4624             }
4625           }
4626         }
4627 
4628         LSItemInfo* itemInfo = aItemInfos.AppendElement();
4629         itemInfo->key() = key;
4630         if (setVoidValue) {
4631           itemInfo->value().SetIsVoid(true);
4632         } else {
4633           aLoadedItems.PutEntry(key);
4634           itemInfo->value() = value;
4635         }
4636       }
4637 
4638       aAddKeyToUnknownItems = false;
4639 
4640       break;
4641     }
4642 
4643     case LSSnapshot::LoadState::Partial: {
4644       int64_t size = 0;
4645       for (uint32_t index = 0; index < mOrderedItems.Length(); index++) {
4646         const LSItemInfo& item = mOrderedItems[index];
4647 
4648         const nsString& key = item.key();
4649         const LSValue& value = item.value();
4650 
4651         if (checkKey && key == aKey) {
4652           checkKey = false;
4653         } else {
4654           size += static_cast<int64_t>(key.Length()) +
4655                   static_cast<int64_t>(value.Length());
4656 
4657           if (size > gSnapshotPrefill) {
4658             aNextLoadIndex = index;
4659             break;
4660           }
4661         }
4662 
4663         aLoadedItems.PutEntry(key);
4664 
4665         LSItemInfo* itemInfo = aItemInfos.AppendElement();
4666         itemInfo->key() = key;
4667         itemInfo->value() = value;
4668       }
4669 
4670       aAddKeyToUnknownItems = false;
4671 
4672       if (!aKey.IsVoid()) {
4673         if (value.IsVoid()) {
4674           aAddKeyToUnknownItems = true;
4675         } else if (checkKey) {
4676           // The item wasn't added in the loop above, add it here.
4677 
4678           LSItemInfo* itemInfo = aItemInfos.AppendElement();
4679           itemInfo->key() = aKey;
4680           itemInfo->value() = value;
4681         }
4682       }
4683 
4684       MOZ_ASSERT(aItemInfos.Length() < mOrderedItems.Length());
4685 
4686       break;
4687     }
4688 
4689     default:
4690       MOZ_CRASH("Bad load state value!");
4691   }
4692 
4693   aLoadState = loadState;
4694 }
4695 
GetItem(const nsString & aKey,LSValue & aValue) const4696 void Datastore::GetItem(const nsString& aKey, LSValue& aValue) const {
4697   AssertIsOnBackgroundThread();
4698   MOZ_ASSERT(!mClosed);
4699 
4700   if (!mValues.Get(aKey, &aValue)) {
4701     aValue.SetIsVoid(true);
4702   }
4703 }
4704 
GetKeys(nsTArray<nsString> & aKeys) const4705 void Datastore::GetKeys(nsTArray<nsString>& aKeys) const {
4706   AssertIsOnBackgroundThread();
4707   MOZ_ASSERT(!mClosed);
4708 
4709   for (auto item : mOrderedItems) {
4710     aKeys.AppendElement(item.key());
4711   }
4712 }
4713 
SetItem(Database * aDatabase,const nsString & aKey,const LSValue & aValue)4714 void Datastore::SetItem(Database* aDatabase, const nsString& aKey,
4715                         const LSValue& aValue) {
4716   AssertIsOnBackgroundThread();
4717   MOZ_ASSERT(aDatabase);
4718   MOZ_ASSERT(!mClosed);
4719   MOZ_ASSERT(mInUpdateBatch);
4720 
4721   LSValue oldValue;
4722   GetItem(aKey, oldValue);
4723 
4724   if (oldValue != aValue) {
4725     bool isNewItem = oldValue.IsVoid();
4726 
4727     NotifySnapshots(aDatabase, aKey, oldValue, /* affectsOrder */ isNewItem);
4728 
4729     mValues.InsertOrUpdate(aKey, aValue);
4730 
4731     int64_t delta;
4732 
4733     if (isNewItem) {
4734       mWriteOptimizer.InsertItem(aKey, aValue);
4735 
4736       int64_t sizeOfKey = static_cast<int64_t>(aKey.Length());
4737 
4738       delta = sizeOfKey + static_cast<int64_t>(aValue.UTF16Length());
4739 
4740       mUpdateBatchUsage += delta;
4741 
4742       mSizeOfKeys += sizeOfKey;
4743       mSizeOfItems += sizeOfKey + static_cast<int64_t>(aValue.Length());
4744     } else {
4745       mWriteOptimizer.UpdateItem(aKey, aValue);
4746 
4747       delta = static_cast<int64_t>(aValue.UTF16Length()) -
4748               static_cast<int64_t>(oldValue.UTF16Length());
4749 
4750       mUpdateBatchUsage += delta;
4751 
4752       mSizeOfItems += static_cast<int64_t>(aValue.Length()) -
4753                       static_cast<int64_t>(oldValue.Length());
4754     }
4755 
4756     if (IsPersistent()) {
4757       mConnection->SetItem(aKey, aValue, delta, isNewItem);
4758     }
4759   }
4760 }
4761 
RemoveItem(Database * aDatabase,const nsString & aKey)4762 void Datastore::RemoveItem(Database* aDatabase, const nsString& aKey) {
4763   AssertIsOnBackgroundThread();
4764   MOZ_ASSERT(aDatabase);
4765   MOZ_ASSERT(!mClosed);
4766   MOZ_ASSERT(mInUpdateBatch);
4767 
4768   LSValue oldValue;
4769   GetItem(aKey, oldValue);
4770 
4771   if (!oldValue.IsVoid()) {
4772     NotifySnapshots(aDatabase, aKey, oldValue, /* aAffectsOrder */ true);
4773 
4774     mValues.Remove(aKey);
4775 
4776     mWriteOptimizer.DeleteItem(aKey);
4777 
4778     int64_t sizeOfKey = static_cast<int64_t>(aKey.Length());
4779 
4780     int64_t delta = -sizeOfKey - static_cast<int64_t>(oldValue.UTF16Length());
4781 
4782     mUpdateBatchUsage += delta;
4783 
4784     mSizeOfKeys -= sizeOfKey;
4785     mSizeOfItems -= sizeOfKey + static_cast<int64_t>(oldValue.Length());
4786 
4787     if (IsPersistent()) {
4788       mConnection->RemoveItem(aKey, delta);
4789     }
4790   }
4791 }
4792 
Clear(Database * aDatabase)4793 void Datastore::Clear(Database* aDatabase) {
4794   AssertIsOnBackgroundThread();
4795   MOZ_ASSERT(!mClosed);
4796 
4797   if (mValues.Count()) {
4798     int64_t delta = 0;
4799     for (const auto& entry : mValues) {
4800       const nsAString& key = entry.GetKey();
4801       const LSValue& value = entry.GetData();
4802 
4803       delta += -static_cast<int64_t>(key.Length()) -
4804                static_cast<int64_t>(value.UTF16Length());
4805 
4806       NotifySnapshots(aDatabase, key, value, /* aAffectsOrder */ true);
4807     }
4808 
4809     mValues.Clear();
4810 
4811     if (mInUpdateBatch) {
4812       mWriteOptimizer.Truncate();
4813 
4814       mUpdateBatchUsage += delta;
4815     } else {
4816       mOrderedItems.Clear();
4817 
4818       DebugOnly<bool> ok = UpdateUsage(delta);
4819       MOZ_ASSERT(ok);
4820     }
4821 
4822     mSizeOfKeys = 0;
4823     mSizeOfItems = 0;
4824 
4825     if (IsPersistent()) {
4826       mConnection->Clear(delta);
4827     }
4828   }
4829 }
4830 
BeginUpdateBatch(int64_t aSnapshotInitialUsage)4831 void Datastore::BeginUpdateBatch(int64_t aSnapshotInitialUsage) {
4832   AssertIsOnBackgroundThread();
4833   MOZ_ASSERT(aSnapshotInitialUsage >= 0);
4834   MOZ_ASSERT(!mClosed);
4835   MOZ_ASSERT(mUpdateBatchUsage == -1);
4836   MOZ_ASSERT(!mInUpdateBatch);
4837 
4838   mUpdateBatchUsage = aSnapshotInitialUsage;
4839 
4840   if (IsPersistent()) {
4841     mConnection->BeginUpdateBatch();
4842   }
4843 
4844   mInUpdateBatch = true;
4845 }
4846 
EndUpdateBatch(int64_t aSnapshotPeakUsage)4847 int64_t Datastore::EndUpdateBatch(int64_t aSnapshotPeakUsage) {
4848   AssertIsOnBackgroundThread();
4849   MOZ_ASSERT(!mClosed);
4850   MOZ_ASSERT(mInUpdateBatch);
4851 
4852   mWriteOptimizer.ApplyAndReset(mOrderedItems);
4853 
4854   MOZ_ASSERT(!mWriteOptimizer.HasWrites());
4855 
4856   if (aSnapshotPeakUsage >= 0) {
4857     int64_t delta = mUpdateBatchUsage - aSnapshotPeakUsage;
4858 
4859     if (mActiveDatabases.Count()) {
4860       // We can't apply deltas while other databases are still active.
4861       // The final delta must be zero or negative, but individual deltas can
4862       // be positive. A positive delta can't be applied asynchronously since
4863       // there's no way to fire the quota exceeded error event.
4864 
4865       mPendingUsageDeltas.AppendElement(delta);
4866     } else {
4867       MOZ_ASSERT(delta <= 0);
4868       if (delta != 0) {
4869         DebugOnly<bool> ok = UpdateUsage(delta);
4870         MOZ_ASSERT(ok);
4871       }
4872     }
4873   }
4874 
4875   int64_t result = mUpdateBatchUsage;
4876   mUpdateBatchUsage = -1;
4877 
4878   if (IsPersistent()) {
4879     mConnection->EndUpdateBatch();
4880   }
4881 
4882   mInUpdateBatch = false;
4883 
4884   return result;
4885 }
4886 
RequestUpdateUsage(int64_t aRequestedSize,int64_t aMinSize)4887 int64_t Datastore::RequestUpdateUsage(int64_t aRequestedSize,
4888                                       int64_t aMinSize) {
4889   AssertIsOnBackgroundThread();
4890   MOZ_ASSERT(aRequestedSize > 0);
4891   MOZ_ASSERT(aMinSize > 0);
4892 
4893   if (UpdateUsage(aRequestedSize)) {
4894     return aRequestedSize;
4895   }
4896 
4897   if (UpdateUsage(aMinSize)) {
4898     return aMinSize;
4899   }
4900 
4901   return 0;
4902 }
4903 
HasOtherProcessObservers(Database * aDatabase)4904 bool Datastore::HasOtherProcessObservers(Database* aDatabase) {
4905   AssertIsOnBackgroundThread();
4906   MOZ_ASSERT(aDatabase);
4907 
4908   if (!gObservers) {
4909     return false;
4910   }
4911 
4912   nsTArray<NotNull<Observer*>>* array;
4913   if (!gObservers->Get(mOriginMetadata.mOrigin, &array)) {
4914     return false;
4915   }
4916 
4917   MOZ_ASSERT(array);
4918 
4919   PBackgroundParent* databaseBackgroundActor = aDatabase->Manager();
4920 
4921   for (Observer* observer : *array) {
4922     if (observer->Manager() != databaseBackgroundActor) {
4923       return true;
4924     }
4925   }
4926 
4927   return false;
4928 }
4929 
NotifyOtherProcessObservers(Database * aDatabase,const nsString & aDocumentURI,const nsString & aKey,const LSValue & aOldValue,const LSValue & aNewValue)4930 void Datastore::NotifyOtherProcessObservers(Database* aDatabase,
4931                                             const nsString& aDocumentURI,
4932                                             const nsString& aKey,
4933                                             const LSValue& aOldValue,
4934                                             const LSValue& aNewValue) {
4935   AssertIsOnBackgroundThread();
4936   MOZ_ASSERT(aDatabase);
4937 
4938   if (!gObservers) {
4939     return;
4940   }
4941 
4942   nsTArray<NotNull<Observer*>>* array;
4943   if (!gObservers->Get(mOriginMetadata.mOrigin, &array)) {
4944     return;
4945   }
4946 
4947   MOZ_ASSERT(array);
4948 
4949   // We do not want to send information about events back to the content process
4950   // that caused the change.
4951   PBackgroundParent* databaseBackgroundActor = aDatabase->Manager();
4952 
4953   for (Observer* observer : *array) {
4954     if (observer->Manager() != databaseBackgroundActor) {
4955       observer->Observe(aDatabase, aDocumentURI, aKey, aOldValue, aNewValue);
4956     }
4957   }
4958 }
4959 
NoteChangedObserverArray(const nsTArray<NotNull<Observer * >> & aObservers)4960 void Datastore::NoteChangedObserverArray(
4961     const nsTArray<NotNull<Observer*>>& aObservers) {
4962   AssertIsOnBackgroundThread();
4963 
4964   for (Database* database : mActiveDatabases) {
4965     Snapshot* snapshot = database->GetSnapshot();
4966     MOZ_ASSERT(snapshot);
4967 
4968     if (snapshot->IsDirty()) {
4969       continue;
4970     }
4971 
4972     bool hasOtherProcessObservers = false;
4973 
4974     PBackgroundParent* databaseBackgroundActor = database->Manager();
4975 
4976     for (Observer* observer : aObservers) {
4977       if (observer->Manager() != databaseBackgroundActor) {
4978         hasOtherProcessObservers = true;
4979         break;
4980       }
4981     }
4982 
4983     if (snapshot->HasOtherProcessObservers() != hasOtherProcessObservers) {
4984       snapshot->MarkDirty();
4985     }
4986   }
4987 }
4988 
Stringify(nsACString & aResult) const4989 void Datastore::Stringify(nsACString& aResult) const {
4990   AssertIsOnBackgroundThread();
4991 
4992   aResult.AppendLiteral("DirectoryLock:");
4993   aResult.AppendInt(!!mDirectoryLock);
4994   aResult.Append(kQuotaGenericDelimiter);
4995 
4996   aResult.AppendLiteral("Connection:");
4997   aResult.AppendInt(!!mConnection);
4998   aResult.Append(kQuotaGenericDelimiter);
4999 
5000   aResult.AppendLiteral("QuotaObject:");
5001   aResult.AppendInt(!!mQuotaObject);
5002   aResult.Append(kQuotaGenericDelimiter);
5003 
5004   aResult.AppendLiteral("PrepareDatastoreOps:");
5005   aResult.AppendInt(mPrepareDatastoreOps.Count());
5006   aResult.Append(kQuotaGenericDelimiter);
5007 
5008   aResult.AppendLiteral("PreparedDatastores:");
5009   aResult.AppendInt(mPreparedDatastores.Count());
5010   aResult.Append(kQuotaGenericDelimiter);
5011 
5012   aResult.AppendLiteral("Databases:");
5013   aResult.AppendInt(mDatabases.Count());
5014   aResult.Append(kQuotaGenericDelimiter);
5015 
5016   aResult.AppendLiteral("ActiveDatabases:");
5017   aResult.AppendInt(mActiveDatabases.Count());
5018   aResult.Append(kQuotaGenericDelimiter);
5019 
5020   aResult.AppendLiteral("Origin:");
5021   aResult.Append(AnonymizedOriginString(mOriginMetadata.mOrigin));
5022   aResult.Append(kQuotaGenericDelimiter);
5023 
5024   aResult.AppendLiteral("PrivateBrowsingId:");
5025   aResult.AppendInt(mPrivateBrowsingId);
5026   aResult.Append(kQuotaGenericDelimiter);
5027 
5028   aResult.AppendLiteral("Closed:");
5029   aResult.AppendInt(mClosed);
5030 }
5031 
UpdateUsage(int64_t aDelta)5032 bool Datastore::UpdateUsage(int64_t aDelta) {
5033   AssertIsOnBackgroundThread();
5034 
5035   // Check internal LocalStorage origin limit.
5036   int64_t newUsage = mUsage + aDelta;
5037 
5038   MOZ_ASSERT(newUsage >= 0);
5039 
5040   if (newUsage > StaticPrefs::dom_storage_default_quota() * 1024) {
5041     return false;
5042   }
5043 
5044   // Check QuotaManager limits (group and global limit).
5045   if (IsPersistent()) {
5046     MOZ_ASSERT(mQuotaObject);
5047 
5048     if (!mQuotaObject->MaybeUpdateSize(newUsage, /* aTruncate */ true)) {
5049       return false;
5050     }
5051   }
5052 
5053   // Quota checks passed, set new usage.
5054   mUsage = newUsage;
5055 
5056   return true;
5057 }
5058 
MaybeClose()5059 void Datastore::MaybeClose() {
5060   AssertIsOnBackgroundThread();
5061 
5062   if (!mPrepareDatastoreOps.Count() && !mHasLivePrivateDatastore &&
5063       !mPreparedDatastores.Count() && !mDatabases.Count()) {
5064     Close();
5065   }
5066 }
5067 
ConnectionClosedCallback()5068 void Datastore::ConnectionClosedCallback() {
5069   AssertIsOnBackgroundThread();
5070   MOZ_ASSERT(mDirectoryLock);
5071   MOZ_ASSERT(mConnection);
5072   MOZ_ASSERT(mQuotaObject);
5073   MOZ_ASSERT(mClosed);
5074 
5075   // Release the quota object first.
5076   mQuotaObject = nullptr;
5077 
5078   bool databaseWasNotAvailable;
5079   bool hasCreatedDatabase;
5080   mConnection->GetFinishInfo(databaseWasNotAvailable, hasCreatedDatabase);
5081 
5082   if (databaseWasNotAvailable && !hasCreatedDatabase) {
5083     MOZ_ASSERT(mUsage == 0);
5084 
5085     QuotaManager* quotaManager = QuotaManager::Get();
5086     MOZ_ASSERT(quotaManager);
5087 
5088     quotaManager->ResetUsageForClient(
5089         ClientMetadata{mOriginMetadata, mozilla::dom::quota::Client::LS});
5090   }
5091 
5092   mConnection = nullptr;
5093 
5094   // Now it's safe to release the directory lock and unregister itself from
5095   // the hashtable.
5096 
5097   mDirectoryLock = nullptr;
5098 
5099   CleanupMetadata();
5100 
5101   if (mCompleteCallback) {
5102     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mCompleteCallback.forget()));
5103   }
5104 }
5105 
CleanupMetadata()5106 void Datastore::CleanupMetadata() {
5107   AssertIsOnBackgroundThread();
5108 
5109   MOZ_ASSERT(gDatastores);
5110   const DebugOnly<bool> removed = gDatastores->Remove(mOriginMetadata.mOrigin);
5111   MOZ_ASSERT(removed);
5112 
5113   QuotaManager::MaybeRecordQuotaClientShutdownStep(quota::Client::LS,
5114                                                    "Datastore removed"_ns);
5115 
5116   if (!gDatastores->Count()) {
5117     gDatastores = nullptr;
5118   }
5119 }
5120 
NotifySnapshots(Database * aDatabase,const nsAString & aKey,const LSValue & aOldValue,bool aAffectsOrder)5121 void Datastore::NotifySnapshots(Database* aDatabase, const nsAString& aKey,
5122                                 const LSValue& aOldValue, bool aAffectsOrder) {
5123   AssertIsOnBackgroundThread();
5124 
5125   for (Database* database : mDatabases) {
5126     MOZ_ASSERT(database);
5127 
5128     if (database == aDatabase) {
5129       continue;
5130     }
5131 
5132     Snapshot* snapshot = database->GetSnapshot();
5133     if (snapshot) {
5134       snapshot->SaveItem(aKey, aOldValue, aAffectsOrder);
5135     }
5136   }
5137 }
5138 
5139 /*******************************************************************************
5140  * PreparedDatastore
5141  ******************************************************************************/
5142 
Destroy()5143 void PreparedDatastore::Destroy() {
5144   AssertIsOnBackgroundThread();
5145   MOZ_ASSERT(gPreparedDatastores);
5146   DebugOnly<bool> removed = gPreparedDatastores->Remove(mDatastoreId);
5147   MOZ_ASSERT(removed);
5148 }
5149 
5150 // static
TimerCallback(nsITimer * aTimer,void * aClosure)5151 void PreparedDatastore::TimerCallback(nsITimer* aTimer, void* aClosure) {
5152   AssertIsOnBackgroundThread();
5153 
5154   auto* self = static_cast<PreparedDatastore*>(aClosure);
5155   MOZ_ASSERT(self);
5156 
5157   self->Destroy();
5158 }
5159 
5160 /*******************************************************************************
5161  * Database
5162  ******************************************************************************/
5163 
Database(const PrincipalInfo & aPrincipalInfo,const Maybe<ContentParentId> & aContentParentId,const nsACString & aOrigin,uint32_t aPrivateBrowsingId)5164 Database::Database(const PrincipalInfo& aPrincipalInfo,
5165                    const Maybe<ContentParentId>& aContentParentId,
5166                    const nsACString& aOrigin, uint32_t aPrivateBrowsingId)
5167     : mSnapshot(nullptr),
5168       mPrincipalInfo(aPrincipalInfo),
5169       mContentParentId(aContentParentId),
5170       mOrigin(aOrigin),
5171       mPrivateBrowsingId(aPrivateBrowsingId),
5172       mAllowedToClose(false),
5173       mActorDestroyed(false),
5174       mRequestedAllowToClose(false)
5175 #ifdef DEBUG
5176       ,
5177       mActorWasAlive(false)
5178 #endif
5179 {
5180   AssertIsOnBackgroundThread();
5181 }
5182 
~Database()5183 Database::~Database() {
5184   MOZ_ASSERT_IF(mActorWasAlive, mAllowedToClose);
5185   MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed);
5186 }
5187 
SetActorAlive(Datastore * aDatastore)5188 void Database::SetActorAlive(Datastore* aDatastore) {
5189   AssertIsOnBackgroundThread();
5190   MOZ_ASSERT(!mActorWasAlive);
5191   MOZ_ASSERT(!mActorDestroyed);
5192 
5193 #ifdef DEBUG
5194   mActorWasAlive = true;
5195 #endif
5196 
5197   mDatastore = aDatastore;
5198 
5199   mDatastore->NoteLiveDatabase(this);
5200 
5201   if (!gLiveDatabases) {
5202     gLiveDatabases = new LiveDatabaseArray();
5203   }
5204 
5205   gLiveDatabases->AppendElement(WrapNotNullUnchecked(this));
5206 }
5207 
RegisterSnapshot(Snapshot * aSnapshot)5208 void Database::RegisterSnapshot(Snapshot* aSnapshot) {
5209   AssertIsOnBackgroundThread();
5210   MOZ_ASSERT(aSnapshot);
5211   MOZ_ASSERT(!mSnapshot);
5212   MOZ_ASSERT(!mAllowedToClose);
5213 
5214   // Only one snapshot at a time is currently supported.
5215   mSnapshot = aSnapshot;
5216 
5217   mDatastore->NoteActiveDatabase(this);
5218 }
5219 
UnregisterSnapshot(Snapshot * aSnapshot)5220 void Database::UnregisterSnapshot(Snapshot* aSnapshot) {
5221   MOZ_ASSERT(aSnapshot);
5222   MOZ_ASSERT(mSnapshot == aSnapshot);
5223 
5224   mSnapshot = nullptr;
5225 
5226   mDatastore->NoteInactiveDatabase(this);
5227 }
5228 
RequestAllowToClose()5229 void Database::RequestAllowToClose() {
5230   AssertIsOnBackgroundThread();
5231 
5232   if (mRequestedAllowToClose) {
5233     return;
5234   }
5235 
5236   mRequestedAllowToClose = true;
5237 
5238   // Send the RequestAllowToClose message to the child to avoid racing with the
5239   // child actor. Except the case when the actor was already destroyed.
5240   if (mActorDestroyed) {
5241     MOZ_ASSERT(mAllowedToClose);
5242     return;
5243   }
5244 
5245   if (NS_WARN_IF(!SendRequestAllowToClose()) && !mSnapshot) {
5246     // This is not necessary, because there should be a runnable scheduled that
5247     // will call ActorDestroy which calls AllowToClose. However we can speedup
5248     // the shutdown a bit if we do it here directly, but only if there's no
5249     // registered snapshot.
5250     AllowToClose();
5251   }
5252 }
5253 
ForceKill()5254 void Database::ForceKill() {
5255   AssertIsOnBackgroundThread();
5256 
5257   if (mActorDestroyed) {
5258     MOZ_ASSERT(mAllowedToClose);
5259     return;
5260   }
5261 
5262   Unused << PBackgroundLSDatabaseParent::Send__delete__(this);
5263 }
5264 
Stringify(nsACString & aResult) const5265 void Database::Stringify(nsACString& aResult) const {
5266   AssertIsOnBackgroundThread();
5267 
5268   aResult.AppendLiteral("SnapshotRegistered:");
5269   aResult.AppendInt(!!mSnapshot);
5270   aResult.Append(kQuotaGenericDelimiter);
5271 
5272   aResult.AppendLiteral("OtherProcessActor:");
5273   aResult.AppendInt(BackgroundParent::IsOtherProcessActor(Manager()));
5274   aResult.Append(kQuotaGenericDelimiter);
5275 
5276   aResult.AppendLiteral("Origin:");
5277   aResult.Append(AnonymizedOriginString(mOrigin));
5278   aResult.Append(kQuotaGenericDelimiter);
5279 
5280   aResult.AppendLiteral("PrivateBrowsingId:");
5281   aResult.AppendInt(mPrivateBrowsingId);
5282   aResult.Append(kQuotaGenericDelimiter);
5283 
5284   aResult.AppendLiteral("AllowedToClose:");
5285   aResult.AppendInt(mAllowedToClose);
5286   aResult.Append(kQuotaGenericDelimiter);
5287 
5288   aResult.AppendLiteral("ActorDestroyed:");
5289   aResult.AppendInt(mActorDestroyed);
5290   aResult.Append(kQuotaGenericDelimiter);
5291 
5292   aResult.AppendLiteral("RequestedAllowToClose:");
5293   aResult.AppendInt(mRequestedAllowToClose);
5294 }
5295 
AllowToClose()5296 void Database::AllowToClose() {
5297   AssertIsOnBackgroundThread();
5298   MOZ_ASSERT(!mAllowedToClose);
5299   MOZ_ASSERT(mDatastore);
5300   MOZ_ASSERT(!mSnapshot);
5301 
5302   mAllowedToClose = true;
5303 
5304   mDatastore->NoteFinishedDatabase(this);
5305 
5306   mDatastore = nullptr;
5307 
5308   MOZ_ASSERT(gLiveDatabases);
5309   gLiveDatabases->RemoveElement(this);
5310 
5311   QuotaManager::MaybeRecordQuotaClientShutdownStep(quota::Client::LS,
5312                                                    "Live database removed"_ns);
5313 
5314   if (gLiveDatabases->IsEmpty()) {
5315     gLiveDatabases = nullptr;
5316   }
5317 }
5318 
ActorDestroy(ActorDestroyReason aWhy)5319 void Database::ActorDestroy(ActorDestroyReason aWhy) {
5320   AssertIsOnBackgroundThread();
5321   MOZ_ASSERT(!mActorDestroyed);
5322 
5323   mActorDestroyed = true;
5324 
5325   if (!mAllowedToClose) {
5326     AllowToClose();
5327   }
5328 }
5329 
RecvDeleteMe()5330 mozilla::ipc::IPCResult Database::RecvDeleteMe() {
5331   AssertIsOnBackgroundThread();
5332   MOZ_ASSERT(!mActorDestroyed);
5333 
5334   IProtocol* mgr = Manager();
5335   if (!PBackgroundLSDatabaseParent::Send__delete__(this)) {
5336     return IPC_FAIL_NO_REASON(mgr);
5337   }
5338   return IPC_OK();
5339 }
5340 
RecvAllowToClose()5341 mozilla::ipc::IPCResult Database::RecvAllowToClose() {
5342   AssertIsOnBackgroundThread();
5343 
5344   if (NS_WARN_IF(mAllowedToClose)) {
5345     ASSERT_UNLESS_FUZZING();
5346     return IPC_FAIL_NO_REASON(this);
5347   }
5348 
5349   AllowToClose();
5350 
5351   return IPC_OK();
5352 }
5353 
AllocPBackgroundLSSnapshotParent(const nsString & aDocumentURI,const nsString & aKey,const bool & aIncreasePeakUsage,const int64_t & aRequestedSize,const int64_t & aMinSize,LSSnapshotInitInfo * aInitInfo)5354 PBackgroundLSSnapshotParent* Database::AllocPBackgroundLSSnapshotParent(
5355     const nsString& aDocumentURI, const nsString& aKey,
5356     const bool& aIncreasePeakUsage, const int64_t& aRequestedSize,
5357     const int64_t& aMinSize, LSSnapshotInitInfo* aInitInfo) {
5358   AssertIsOnBackgroundThread();
5359 
5360   if (NS_WARN_IF(aIncreasePeakUsage && aRequestedSize <= 0)) {
5361     ASSERT_UNLESS_FUZZING();
5362     return nullptr;
5363   }
5364 
5365   if (NS_WARN_IF(aIncreasePeakUsage && aMinSize <= 0)) {
5366     ASSERT_UNLESS_FUZZING();
5367     return nullptr;
5368   }
5369 
5370   if (NS_WARN_IF(mAllowedToClose)) {
5371     ASSERT_UNLESS_FUZZING();
5372     return nullptr;
5373   }
5374 
5375   RefPtr<Snapshot> snapshot = new Snapshot(this, aDocumentURI);
5376 
5377   // Transfer ownership to IPDL.
5378   return snapshot.forget().take();
5379 }
5380 
RecvPBackgroundLSSnapshotConstructor(PBackgroundLSSnapshotParent * aActor,const nsString & aDocumentURI,const nsString & aKey,const bool & aIncreasePeakUsage,const int64_t & aRequestedSize,const int64_t & aMinSize,LSSnapshotInitInfo * aInitInfo)5381 mozilla::ipc::IPCResult Database::RecvPBackgroundLSSnapshotConstructor(
5382     PBackgroundLSSnapshotParent* aActor, const nsString& aDocumentURI,
5383     const nsString& aKey, const bool& aIncreasePeakUsage,
5384     const int64_t& aRequestedSize, const int64_t& aMinSize,
5385     LSSnapshotInitInfo* aInitInfo) {
5386   AssertIsOnBackgroundThread();
5387   MOZ_ASSERT_IF(aIncreasePeakUsage, aRequestedSize > 0);
5388   MOZ_ASSERT_IF(aIncreasePeakUsage, aMinSize > 0);
5389   MOZ_ASSERT(aInitInfo);
5390   MOZ_ASSERT(!mAllowedToClose);
5391 
5392   auto* snapshot = static_cast<Snapshot*>(aActor);
5393 
5394   bool addKeyToUnknownItems;
5395   nsTHashtable<nsStringHashKey> loadedItems;
5396   nsTArray<LSItemInfo> itemInfos;
5397   uint32_t nextLoadIndex;
5398   LSSnapshot::LoadState loadState;
5399   mDatastore->GetSnapshotLoadInfo(aKey, addKeyToUnknownItems, loadedItems,
5400                                   itemInfos, nextLoadIndex, loadState);
5401 
5402   nsTHashSet<nsString> unknownItems;
5403   if (addKeyToUnknownItems) {
5404     unknownItems.Insert(aKey);
5405   }
5406 
5407   uint32_t totalLength = mDatastore->GetLength();
5408 
5409   int64_t initialUsage = mDatastore->GetUsage();
5410 
5411   int64_t peakUsage = initialUsage;
5412 
5413   if (aIncreasePeakUsage) {
5414     int64_t size = mDatastore->RequestUpdateUsage(aRequestedSize, aMinSize);
5415     peakUsage += size;
5416   }
5417 
5418   bool hasOtherProcessObservers = mDatastore->HasOtherProcessObservers(this);
5419 
5420   snapshot->Init(loadedItems, std::move(unknownItems), nextLoadIndex,
5421                  totalLength, initialUsage, peakUsage, loadState,
5422                  hasOtherProcessObservers);
5423 
5424   RegisterSnapshot(snapshot);
5425 
5426   aInitInfo->addKeyToUnknownItems() = addKeyToUnknownItems;
5427   aInitInfo->itemInfos() = std::move(itemInfos);
5428   aInitInfo->totalLength() = totalLength;
5429   aInitInfo->initialUsage() = initialUsage;
5430   aInitInfo->peakUsage() = peakUsage;
5431   aInitInfo->loadState() = loadState;
5432   aInitInfo->hasOtherProcessObservers() = hasOtherProcessObservers;
5433 
5434   return IPC_OK();
5435 }
5436 
DeallocPBackgroundLSSnapshotParent(PBackgroundLSSnapshotParent * aActor)5437 bool Database::DeallocPBackgroundLSSnapshotParent(
5438     PBackgroundLSSnapshotParent* aActor) {
5439   AssertIsOnBackgroundThread();
5440   MOZ_ASSERT(aActor);
5441 
5442   // Transfer ownership back from IPDL.
5443   RefPtr<Snapshot> actor = dont_AddRef(static_cast<Snapshot*>(aActor));
5444 
5445   return true;
5446 }
5447 
5448 /*******************************************************************************
5449  * Snapshot
5450  ******************************************************************************/
5451 
Snapshot(Database * aDatabase,const nsAString & aDocumentURI)5452 Snapshot::Snapshot(Database* aDatabase, const nsAString& aDocumentURI)
5453     : mDatabase(aDatabase),
5454       mDatastore(aDatabase->GetDatastore()),
5455       mDocumentURI(aDocumentURI),
5456       mTotalLength(0),
5457       mUsage(-1),
5458       mPeakUsage(-1),
5459       mSavedKeys(false),
5460       mActorDestroyed(false),
5461       mFinishReceived(false),
5462       mLoadedReceived(false),
5463       mLoadedAllItems(false),
5464       mLoadKeysReceived(false),
5465       mSentMarkDirty(false) {
5466   AssertIsOnBackgroundThread();
5467   MOZ_ASSERT(aDatabase);
5468 }
5469 
~Snapshot()5470 Snapshot::~Snapshot() {
5471   MOZ_ASSERT(mActorDestroyed);
5472   MOZ_ASSERT(mFinishReceived);
5473 }
5474 
SaveItem(const nsAString & aKey,const LSValue & aOldValue,bool aAffectsOrder)5475 void Snapshot::SaveItem(const nsAString& aKey, const LSValue& aOldValue,
5476                         bool aAffectsOrder) {
5477   AssertIsOnBackgroundThread();
5478 
5479   MarkDirty();
5480 
5481   if (mLoadedAllItems) {
5482     return;
5483   }
5484 
5485   if (!mLoadedItems.Contains(aKey) && !mUnknownItems.Contains(aKey)) {
5486     mValues.LookupOrInsert(aKey, aOldValue);
5487   }
5488 
5489   if (aAffectsOrder && !mSavedKeys) {
5490     mDatastore->GetKeys(mKeys);
5491     mSavedKeys = true;
5492   }
5493 }
5494 
MarkDirty()5495 void Snapshot::MarkDirty() {
5496   AssertIsOnBackgroundThread();
5497 
5498   if (!mSentMarkDirty) {
5499     Unused << SendMarkDirty();
5500     mSentMarkDirty = true;
5501   }
5502 }
5503 
Finish()5504 void Snapshot::Finish() {
5505   AssertIsOnBackgroundThread();
5506   MOZ_ASSERT(mDatabase);
5507   MOZ_ASSERT(mDatastore);
5508   MOZ_ASSERT(!mFinishReceived);
5509 
5510   mDatastore->BeginUpdateBatch(mUsage);
5511 
5512   mDatastore->EndUpdateBatch(mPeakUsage);
5513 
5514   mDatabase->UnregisterSnapshot(this);
5515 
5516   mFinishReceived = true;
5517 }
5518 
ActorDestroy(ActorDestroyReason aWhy)5519 void Snapshot::ActorDestroy(ActorDestroyReason aWhy) {
5520   AssertIsOnBackgroundThread();
5521   MOZ_ASSERT(!mActorDestroyed);
5522 
5523   mActorDestroyed = true;
5524 
5525   if (!mFinishReceived) {
5526     Finish();
5527   }
5528 }
5529 
RecvDeleteMe()5530 mozilla::ipc::IPCResult Snapshot::RecvDeleteMe() {
5531   AssertIsOnBackgroundThread();
5532   MOZ_ASSERT(!mActorDestroyed);
5533 
5534   IProtocol* mgr = Manager();
5535   if (!PBackgroundLSSnapshotParent::Send__delete__(this)) {
5536     return IPC_FAIL_NO_REASON(mgr);
5537   }
5538   return IPC_OK();
5539 }
5540 
RecvCheckpoint(nsTArray<LSWriteInfo> && aWriteInfos)5541 mozilla::ipc::IPCResult Snapshot::RecvCheckpoint(
5542     nsTArray<LSWriteInfo>&& aWriteInfos) {
5543   AssertIsOnBackgroundThread();
5544   MOZ_ASSERT(mUsage >= 0);
5545   MOZ_ASSERT(mPeakUsage >= mUsage);
5546 
5547   if (NS_WARN_IF(aWriteInfos.IsEmpty())) {
5548     ASSERT_UNLESS_FUZZING();
5549     return IPC_FAIL_NO_REASON(this);
5550   }
5551 
5552   if (NS_WARN_IF(mHasOtherProcessObservers)) {
5553     ASSERT_UNLESS_FUZZING();
5554     return IPC_FAIL_NO_REASON(this);
5555   }
5556 
5557   mDatastore->BeginUpdateBatch(mUsage);
5558 
5559   for (uint32_t index = 0; index < aWriteInfos.Length(); index++) {
5560     const LSWriteInfo& writeInfo = aWriteInfos[index];
5561 
5562     switch (writeInfo.type()) {
5563       case LSWriteInfo::TLSSetItemInfo: {
5564         const LSSetItemInfo& info = writeInfo.get_LSSetItemInfo();
5565 
5566         mDatastore->SetItem(mDatabase, info.key(), info.value());
5567 
5568         break;
5569       }
5570 
5571       case LSWriteInfo::TLSRemoveItemInfo: {
5572         const LSRemoveItemInfo& info = writeInfo.get_LSRemoveItemInfo();
5573 
5574         mDatastore->RemoveItem(mDatabase, info.key());
5575 
5576         break;
5577       }
5578 
5579       case LSWriteInfo::TLSClearInfo: {
5580         mDatastore->Clear(mDatabase);
5581 
5582         break;
5583       }
5584 
5585       default:
5586         MOZ_CRASH("Should never get here!");
5587     }
5588   }
5589 
5590   mUsage = mDatastore->EndUpdateBatch(-1);
5591 
5592   return IPC_OK();
5593 }
5594 
RecvCheckpointAndNotify(nsTArray<LSWriteAndNotifyInfo> && aWriteAndNotifyInfos)5595 mozilla::ipc::IPCResult Snapshot::RecvCheckpointAndNotify(
5596     nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos) {
5597   AssertIsOnBackgroundThread();
5598   MOZ_ASSERT(mUsage >= 0);
5599   MOZ_ASSERT(mPeakUsage >= mUsage);
5600 
5601   if (NS_WARN_IF(aWriteAndNotifyInfos.IsEmpty())) {
5602     ASSERT_UNLESS_FUZZING();
5603     return IPC_FAIL_NO_REASON(this);
5604   }
5605 
5606   if (NS_WARN_IF(!mHasOtherProcessObservers)) {
5607     ASSERT_UNLESS_FUZZING();
5608     return IPC_FAIL_NO_REASON(this);
5609   }
5610 
5611   mDatastore->BeginUpdateBatch(mUsage);
5612 
5613   for (uint32_t index = 0; index < aWriteAndNotifyInfos.Length(); index++) {
5614     const LSWriteAndNotifyInfo& writeAndNotifyInfo =
5615         aWriteAndNotifyInfos[index];
5616 
5617     switch (writeAndNotifyInfo.type()) {
5618       case LSWriteAndNotifyInfo::TLSSetItemAndNotifyInfo: {
5619         const LSSetItemAndNotifyInfo& info =
5620             writeAndNotifyInfo.get_LSSetItemAndNotifyInfo();
5621 
5622         mDatastore->SetItem(mDatabase, info.key(), info.value());
5623 
5624         mDatastore->NotifyOtherProcessObservers(
5625             mDatabase, mDocumentURI, info.key(), info.oldValue(), info.value());
5626 
5627         break;
5628       }
5629 
5630       case LSWriteAndNotifyInfo::TLSRemoveItemAndNotifyInfo: {
5631         const LSRemoveItemAndNotifyInfo& info =
5632             writeAndNotifyInfo.get_LSRemoveItemAndNotifyInfo();
5633 
5634         mDatastore->RemoveItem(mDatabase, info.key());
5635 
5636         mDatastore->NotifyOtherProcessObservers(mDatabase, mDocumentURI,
5637                                                 info.key(), info.oldValue(),
5638                                                 VoidLSValue());
5639 
5640         break;
5641       }
5642 
5643       case LSWriteAndNotifyInfo::TLSClearInfo: {
5644         mDatastore->Clear(mDatabase);
5645 
5646         mDatastore->NotifyOtherProcessObservers(mDatabase, mDocumentURI,
5647                                                 VoidString(), VoidLSValue(),
5648                                                 VoidLSValue());
5649 
5650         break;
5651       }
5652 
5653       default:
5654         MOZ_CRASH("Should never get here!");
5655     }
5656   }
5657 
5658   mUsage = mDatastore->EndUpdateBatch(-1);
5659 
5660   return IPC_OK();
5661 }
5662 
RecvFinish()5663 mozilla::ipc::IPCResult Snapshot::RecvFinish() {
5664   AssertIsOnBackgroundThread();
5665 
5666   if (NS_WARN_IF(mFinishReceived)) {
5667     ASSERT_UNLESS_FUZZING();
5668     return IPC_FAIL_NO_REASON(this);
5669   }
5670 
5671   Finish();
5672 
5673   return IPC_OK();
5674 }
5675 
RecvLoaded()5676 mozilla::ipc::IPCResult Snapshot::RecvLoaded() {
5677   AssertIsOnBackgroundThread();
5678 
5679   if (NS_WARN_IF(mFinishReceived)) {
5680     ASSERT_UNLESS_FUZZING();
5681     return IPC_FAIL_NO_REASON(this);
5682   }
5683 
5684   if (NS_WARN_IF(mLoadedReceived)) {
5685     ASSERT_UNLESS_FUZZING();
5686     return IPC_FAIL_NO_REASON(this);
5687   }
5688 
5689   if (NS_WARN_IF(mLoadedAllItems)) {
5690     ASSERT_UNLESS_FUZZING();
5691     return IPC_FAIL_NO_REASON(this);
5692   }
5693 
5694   if (NS_WARN_IF(mLoadKeysReceived)) {
5695     ASSERT_UNLESS_FUZZING();
5696     return IPC_FAIL_NO_REASON(this);
5697   }
5698 
5699   mLoadedReceived = true;
5700 
5701   mLoadedItems.Clear();
5702   mUnknownItems.Clear();
5703   mValues.Clear();
5704   mKeys.Clear();
5705   mLoadedAllItems = true;
5706   mLoadKeysReceived = true;
5707 
5708   return IPC_OK();
5709 }
5710 
RecvLoadValueAndMoreItems(const nsString & aKey,LSValue * aValue,nsTArray<LSItemInfo> * aItemInfos)5711 mozilla::ipc::IPCResult Snapshot::RecvLoadValueAndMoreItems(
5712     const nsString& aKey, LSValue* aValue, nsTArray<LSItemInfo>* aItemInfos) {
5713   AssertIsOnBackgroundThread();
5714   MOZ_ASSERT(aValue);
5715   MOZ_ASSERT(aItemInfos);
5716   MOZ_ASSERT(mDatastore);
5717 
5718   if (NS_WARN_IF(mFinishReceived)) {
5719     ASSERT_UNLESS_FUZZING();
5720     return IPC_FAIL_NO_REASON(this);
5721   }
5722 
5723   if (NS_WARN_IF(mLoadedReceived)) {
5724     ASSERT_UNLESS_FUZZING();
5725     return IPC_FAIL_NO_REASON(this);
5726   }
5727 
5728   if (NS_WARN_IF(mLoadedAllItems)) {
5729     ASSERT_UNLESS_FUZZING();
5730     return IPC_FAIL_NO_REASON(this);
5731   }
5732 
5733   if (mLoadedItems.Contains(aKey) || mUnknownItems.Contains(aKey)) {
5734     ASSERT_UNLESS_FUZZING();
5735     return IPC_FAIL_NO_REASON(this);
5736   }
5737 
5738   if (auto entry = mValues.Lookup(aKey)) {
5739     *aValue = entry.Data();
5740     entry.Remove();
5741   } else {
5742     mDatastore->GetItem(aKey, *aValue);
5743   }
5744 
5745   if (aValue->IsVoid()) {
5746     mUnknownItems.Insert(aKey);
5747   } else {
5748     mLoadedItems.PutEntry(aKey);
5749 
5750     // mLoadedItems.Count()==mTotalLength is checked below.
5751   }
5752 
5753   // Load some more key/value pairs (as many as the snapshot gradual prefill
5754   // byte budget allows).
5755 
5756   if (gSnapshotGradualPrefill > 0) {
5757     const nsTArray<LSItemInfo>& orderedItems = mDatastore->GetOrderedItems();
5758 
5759     uint32_t length;
5760     if (mSavedKeys) {
5761       length = mKeys.Length();
5762     } else {
5763       length = orderedItems.Length();
5764     }
5765 
5766     int64_t size = 0;
5767     while (mNextLoadIndex < length) {
5768       // If the datastore's ordering has changed, mSavedKeys will be true and
5769       // mKeys contains an ordered list of the keys. Otherwise we can use the
5770       // datastore's key ordering which is still the same as when the snapshot
5771       // was created.
5772 
5773       nsString key;
5774       if (mSavedKeys) {
5775         key = mKeys[mNextLoadIndex];
5776       } else {
5777         key = orderedItems[mNextLoadIndex].key();
5778       }
5779 
5780       // Normally we would do this:
5781       // if (!mLoadedItems.GetEntry(key)) {
5782       //   ...
5783       //   mLoadedItems.PutEntry(key);
5784       // }
5785       // but that requires two hash lookups. We can reduce that to just one
5786       // hash lookup if we always call PutEntry and check the number of entries
5787       // before and after the put (which is very cheap). However, if we reach
5788       // the prefill limit, we need to call RemoveEntry, but that is also cheap
5789       // because we pass the entry (not the key).
5790 
5791       uint32_t countBeforePut = mLoadedItems.Count();
5792       auto loadedItemEntry = mLoadedItems.PutEntry(key);
5793       if (countBeforePut != mLoadedItems.Count()) {
5794         // Check mValues first since that contains values as they existed when
5795         // our snapshot was created, but have since been changed/removed in the
5796         // datastore. If it's not there, then the datastore has the
5797         // still-current value. However, if the datastore's key ordering has
5798         // changed, we need to do a hash lookup rather than being able to do an
5799         // optimized direct access to the index.
5800 
5801         LSValue value;
5802         auto valueEntry = mValues.Lookup(key);
5803         if (valueEntry) {
5804           value = valueEntry.Data();
5805         } else if (mSavedKeys) {
5806           mDatastore->GetItem(nsString(key), value);
5807         } else {
5808           value = orderedItems[mNextLoadIndex].value();
5809         }
5810 
5811         // All not loaded keys must have a value.
5812         MOZ_ASSERT(!value.IsVoid());
5813 
5814         size += static_cast<int64_t>(key.Length()) +
5815                 static_cast<int64_t>(value.Length());
5816 
5817         if (size > gSnapshotGradualPrefill) {
5818           mLoadedItems.RemoveEntry(loadedItemEntry);
5819 
5820           // mNextLoadIndex is not incremented, so we will resume at the same
5821           // position next time.
5822           break;
5823         }
5824 
5825         if (valueEntry) {
5826           valueEntry.Remove();
5827         }
5828 
5829         LSItemInfo* itemInfo = aItemInfos->AppendElement();
5830         itemInfo->key() = key;
5831         itemInfo->value() = value;
5832       }
5833 
5834       mNextLoadIndex++;
5835     }
5836   }
5837 
5838   if (mLoadedItems.Count() == mTotalLength) {
5839     mLoadedItems.Clear();
5840     mUnknownItems.Clear();
5841 #ifdef DEBUG
5842     const bool allValuesVoid =
5843         std::all_of(mValues.Values().cbegin(), mValues.Values().cend(),
5844                     [](const auto& entry) { return entry.IsVoid(); });
5845     MOZ_ASSERT(allValuesVoid);
5846 #endif
5847     mValues.Clear();
5848     mLoadedAllItems = true;
5849   }
5850 
5851   return IPC_OK();
5852 }
5853 
RecvLoadKeys(nsTArray<nsString> * aKeys)5854 mozilla::ipc::IPCResult Snapshot::RecvLoadKeys(nsTArray<nsString>* aKeys) {
5855   AssertIsOnBackgroundThread();
5856   MOZ_ASSERT(aKeys);
5857   MOZ_ASSERT(mDatastore);
5858 
5859   if (NS_WARN_IF(mFinishReceived)) {
5860     ASSERT_UNLESS_FUZZING();
5861     return IPC_FAIL_NO_REASON(this);
5862   }
5863 
5864   if (NS_WARN_IF(mLoadedReceived)) {
5865     ASSERT_UNLESS_FUZZING();
5866     return IPC_FAIL_NO_REASON(this);
5867   }
5868 
5869   if (NS_WARN_IF(mLoadKeysReceived)) {
5870     ASSERT_UNLESS_FUZZING();
5871     return IPC_FAIL_NO_REASON(this);
5872   }
5873 
5874   mLoadKeysReceived = true;
5875 
5876   if (mSavedKeys) {
5877     aKeys->AppendElements(std::move(mKeys));
5878   } else {
5879     mDatastore->GetKeys(*aKeys);
5880   }
5881 
5882   return IPC_OK();
5883 }
5884 
RecvIncreasePeakUsage(const int64_t & aRequestedSize,const int64_t & aMinSize,int64_t * aSize)5885 mozilla::ipc::IPCResult Snapshot::RecvIncreasePeakUsage(
5886     const int64_t& aRequestedSize, const int64_t& aMinSize, int64_t* aSize) {
5887   AssertIsOnBackgroundThread();
5888   MOZ_ASSERT(aSize);
5889 
5890   if (NS_WARN_IF(aRequestedSize <= 0)) {
5891     ASSERT_UNLESS_FUZZING();
5892     return IPC_FAIL_NO_REASON(this);
5893   }
5894 
5895   if (NS_WARN_IF(aMinSize <= 0)) {
5896     ASSERT_UNLESS_FUZZING();
5897     return IPC_FAIL_NO_REASON(this);
5898   }
5899 
5900   if (NS_WARN_IF(mFinishReceived)) {
5901     ASSERT_UNLESS_FUZZING();
5902     return IPC_FAIL_NO_REASON(this);
5903   }
5904 
5905   int64_t size = mDatastore->RequestUpdateUsage(aRequestedSize, aMinSize);
5906 
5907   mPeakUsage += size;
5908 
5909   *aSize = size;
5910 
5911   return IPC_OK();
5912 }
5913 
RecvPing()5914 mozilla::ipc::IPCResult Snapshot::RecvPing() {
5915   AssertIsOnBackgroundThread();
5916 
5917   // Do nothing here. This is purely a sync message allowing the child to
5918   // confirm that the actor has received previous async message.
5919 
5920   return IPC_OK();
5921 }
5922 
5923 /*******************************************************************************
5924  * Observer
5925  ******************************************************************************/
5926 
Observer(const nsACString & aOrigin)5927 Observer::Observer(const nsACString& aOrigin)
5928     : mOrigin(aOrigin), mActorDestroyed(false) {
5929   AssertIsOnBackgroundThread();
5930 }
5931 
~Observer()5932 Observer::~Observer() { MOZ_ASSERT(mActorDestroyed); }
5933 
Observe(Database * aDatabase,const nsString & aDocumentURI,const nsString & aKey,const LSValue & aOldValue,const LSValue & aNewValue)5934 void Observer::Observe(Database* aDatabase, const nsString& aDocumentURI,
5935                        const nsString& aKey, const LSValue& aOldValue,
5936                        const LSValue& aNewValue) {
5937   AssertIsOnBackgroundThread();
5938   MOZ_ASSERT(aDatabase);
5939 
5940   Unused << SendObserve(aDatabase->GetPrincipalInfo(),
5941                         aDatabase->PrivateBrowsingId(), aDocumentURI, aKey,
5942                         aOldValue, aNewValue);
5943 }
5944 
ActorDestroy(ActorDestroyReason aWhy)5945 void Observer::ActorDestroy(ActorDestroyReason aWhy) {
5946   AssertIsOnBackgroundThread();
5947   MOZ_ASSERT(!mActorDestroyed);
5948 
5949   mActorDestroyed = true;
5950 
5951   MOZ_ASSERT(gObservers);
5952 
5953   nsTArray<NotNull<Observer*>>* array;
5954   gObservers->Get(mOrigin, &array);
5955   MOZ_ASSERT(array);
5956 
5957   array->RemoveElement(this);
5958 
5959   if (RefPtr<Datastore> datastore = GetDatastore(mOrigin)) {
5960     datastore->NoteChangedObserverArray(*array);
5961   }
5962 
5963   if (array->IsEmpty()) {
5964     gObservers->Remove(mOrigin);
5965   }
5966 
5967   if (!gObservers->Count()) {
5968     gObservers = nullptr;
5969   }
5970 }
5971 
RecvDeleteMe()5972 mozilla::ipc::IPCResult Observer::RecvDeleteMe() {
5973   AssertIsOnBackgroundThread();
5974   MOZ_ASSERT(!mActorDestroyed);
5975 
5976   IProtocol* mgr = Manager();
5977   if (!PBackgroundLSObserverParent::Send__delete__(this)) {
5978     return IPC_FAIL_NO_REASON(mgr);
5979   }
5980   return IPC_OK();
5981 }
5982 
5983 /*******************************************************************************
5984  * LSRequestBase
5985  ******************************************************************************/
5986 
LSRequestBase(const LSRequestParams & aParams,const Maybe<ContentParentId> & aContentParentId)5987 LSRequestBase::LSRequestBase(const LSRequestParams& aParams,
5988                              const Maybe<ContentParentId>& aContentParentId)
5989     : mParams(aParams),
5990       mContentParentId(aContentParentId),
5991       mState(State::Initial),
5992       mWaitingForFinish(false) {}
5993 
~LSRequestBase()5994 LSRequestBase::~LSRequestBase() {
5995   MOZ_ASSERT_IF(MayProceedOnNonOwningThread(),
5996                 mState == State::Initial || mState == State::Completed);
5997 }
5998 
Dispatch()5999 void LSRequestBase::Dispatch() {
6000   AssertIsOnOwningThread();
6001 
6002   mState = State::StartingRequest;
6003 
6004   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
6005 }
6006 
StringifyState(nsACString & aResult) const6007 void LSRequestBase::StringifyState(nsACString& aResult) const {
6008   AssertIsOnOwningThread();
6009 
6010   switch (mState) {
6011     case State::Initial:
6012       aResult.AppendLiteral("Initial");
6013       return;
6014 
6015     case State::StartingRequest:
6016       aResult.AppendLiteral("StartingRequest");
6017       return;
6018 
6019     case State::Nesting:
6020       aResult.AppendLiteral("Nesting");
6021       return;
6022 
6023     case State::SendingReadyMessage:
6024       aResult.AppendLiteral("SendingReadyMessage");
6025       return;
6026 
6027     case State::WaitingForFinish:
6028       aResult.AppendLiteral("WaitingForFinish");
6029       return;
6030 
6031     case State::SendingResults:
6032       aResult.AppendLiteral("SendingResults");
6033       return;
6034 
6035     case State::Completed:
6036       aResult.AppendLiteral("Completed");
6037       return;
6038 
6039     default:
6040       MOZ_CRASH("Bad state!");
6041   }
6042 }
6043 
Stringify(nsACString & aResult) const6044 void LSRequestBase::Stringify(nsACString& aResult) const {
6045   AssertIsOnOwningThread();
6046 
6047   aResult.AppendLiteral("State:");
6048   StringifyState(aResult);
6049 }
6050 
Log()6051 void LSRequestBase::Log() {
6052   AssertIsOnOwningThread();
6053 
6054   if (!LS_LOG_TEST()) {
6055     return;
6056   }
6057 
6058   LS_LOG(("LSRequestBase [%p]", this));
6059 
6060   nsCString state;
6061   StringifyState(state);
6062 
6063   LS_LOG(("  mState: %s", state.get()));
6064 }
6065 
NestedRun()6066 nsresult LSRequestBase::NestedRun() { return NS_OK; }
6067 
VerifyRequestParams()6068 bool LSRequestBase::VerifyRequestParams() {
6069   AssertIsOnBackgroundThread();
6070 
6071   MOZ_ASSERT(mParams.type() != LSRequestParams::T__None);
6072 
6073   switch (mParams.type()) {
6074     case LSRequestParams::TLSRequestPreloadDatastoreParams: {
6075       const LSRequestCommonParams& params =
6076           mParams.get_LSRequestPreloadDatastoreParams().commonParams();
6077 
6078       if (NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo(),
6079                                           params.storagePrincipalInfo()))) {
6080         return false;
6081       }
6082 
6083       if (NS_WARN_IF(
6084               !VerifyOriginKey(params.originKey(), params.principalInfo()))) {
6085         return false;
6086       }
6087 
6088       break;
6089     }
6090 
6091     case LSRequestParams::TLSRequestPrepareDatastoreParams: {
6092       const LSRequestPrepareDatastoreParams& params =
6093           mParams.get_LSRequestPrepareDatastoreParams();
6094 
6095       const LSRequestCommonParams& commonParams = params.commonParams();
6096 
6097       if (NS_WARN_IF(
6098               !VerifyPrincipalInfo(commonParams.principalInfo(),
6099                                    commonParams.storagePrincipalInfo()))) {
6100         return false;
6101       }
6102 
6103       if (NS_WARN_IF(!VerifyClientId(mContentParentId,
6104                                      commonParams.principalInfo(),
6105                                      params.clientId()))) {
6106         return false;
6107       }
6108 
6109       if (NS_WARN_IF(!VerifyOriginKey(commonParams.originKey(),
6110                                       commonParams.principalInfo()))) {
6111         return false;
6112       }
6113 
6114       break;
6115     }
6116 
6117     case LSRequestParams::TLSRequestPrepareObserverParams: {
6118       const LSRequestPrepareObserverParams& params =
6119           mParams.get_LSRequestPrepareObserverParams();
6120 
6121       if (NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo(),
6122                                           params.storagePrincipalInfo()))) {
6123         return false;
6124       }
6125 
6126       if (NS_WARN_IF(!VerifyClientId(mContentParentId, params.principalInfo(),
6127                                      params.clientId()))) {
6128         return false;
6129       }
6130 
6131       break;
6132     }
6133 
6134     default:
6135       MOZ_CRASH("Should never get here!");
6136   }
6137 
6138   return true;
6139 }
6140 
StartRequest()6141 nsresult LSRequestBase::StartRequest() {
6142   AssertIsOnOwningThread();
6143   MOZ_ASSERT(mState == State::StartingRequest);
6144 
6145   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
6146       !MayProceed()) {
6147     return NS_ERROR_ABORT;
6148   }
6149 
6150 #ifdef DEBUG
6151   // Always verify parameters in DEBUG builds!
6152   bool trustParams = false;
6153 #else
6154   bool trustParams = !BackgroundParent::IsOtherProcessActor(Manager());
6155 #endif
6156 
6157   if (!trustParams && NS_WARN_IF(!VerifyRequestParams())) {
6158     return NS_ERROR_FAILURE;
6159   }
6160 
6161   QM_TRY(Start());
6162 
6163   return NS_OK;
6164 }
6165 
SendReadyMessage()6166 void LSRequestBase::SendReadyMessage() {
6167   AssertIsOnOwningThread();
6168   MOZ_ASSERT(mState == State::SendingReadyMessage);
6169 
6170   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
6171       !MayProceed()) {
6172     MaybeSetFailureCode(NS_ERROR_ABORT);
6173   }
6174 
6175   nsresult rv = SendReadyMessageInternal();
6176   if (NS_WARN_IF(NS_FAILED(rv))) {
6177     MaybeSetFailureCode(rv);
6178 
6179     FinishInternal();
6180   }
6181 }
6182 
SendReadyMessageInternal()6183 nsresult LSRequestBase::SendReadyMessageInternal() {
6184   AssertIsOnOwningThread();
6185   MOZ_ASSERT(mState == State::SendingReadyMessage);
6186 
6187   if (!MayProceed()) {
6188     return NS_ERROR_ABORT;
6189   }
6190 
6191   if (NS_WARN_IF(!SendReady())) {
6192     return NS_ERROR_FAILURE;
6193   }
6194 
6195   mState = State::WaitingForFinish;
6196 
6197   mWaitingForFinish = true;
6198 
6199   return NS_OK;
6200 }
6201 
Finish()6202 void LSRequestBase::Finish() {
6203   AssertIsOnOwningThread();
6204   MOZ_ASSERT(mState == State::WaitingForFinish);
6205 
6206   mWaitingForFinish = false;
6207 
6208   FinishInternal();
6209 }
6210 
FinishInternal()6211 void LSRequestBase::FinishInternal() {
6212   AssertIsOnOwningThread();
6213   MOZ_ASSERT(mState == State::SendingReadyMessage ||
6214              mState == State::WaitingForFinish);
6215 
6216   mState = State::SendingResults;
6217 
6218   // This LSRequestBase can only be held alive by the IPDL. Run() can end up
6219   // with clearing that last reference. So we need to add a self reference here.
6220   RefPtr<LSRequestBase> kungFuDeathGrip = this;
6221 
6222   MOZ_ALWAYS_SUCCEEDS(this->Run());
6223 }
6224 
SendResults()6225 void LSRequestBase::SendResults() {
6226   AssertIsOnOwningThread();
6227   MOZ_ASSERT(mState == State::SendingResults);
6228 
6229   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
6230       !MayProceed()) {
6231     MaybeSetFailureCode(NS_ERROR_ABORT);
6232   }
6233 
6234   if (MayProceed()) {
6235     LSRequestResponse response;
6236 
6237     if (NS_SUCCEEDED(ResultCode())) {
6238       GetResponse(response);
6239 
6240       MOZ_ASSERT(response.type() != LSRequestResponse::T__None);
6241 
6242       if (response.type() == LSRequestResponse::Tnsresult) {
6243         MOZ_ASSERT(NS_FAILED(response.get_nsresult()));
6244 
6245         SetFailureCode(response.get_nsresult());
6246       }
6247     } else {
6248       response = ResultCode();
6249     }
6250 
6251     Unused << PBackgroundLSRequestParent::Send__delete__(this, response);
6252   }
6253 
6254   Cleanup();
6255 
6256   mState = State::Completed;
6257 }
6258 
6259 NS_IMETHODIMP
Run()6260 LSRequestBase::Run() {
6261   nsresult rv;
6262 
6263   switch (mState) {
6264     case State::StartingRequest:
6265       rv = StartRequest();
6266       break;
6267 
6268     case State::Nesting:
6269       rv = NestedRun();
6270       break;
6271 
6272     case State::SendingReadyMessage:
6273       SendReadyMessage();
6274       return NS_OK;
6275 
6276     case State::SendingResults:
6277       SendResults();
6278       return NS_OK;
6279 
6280     default:
6281       MOZ_CRASH("Bad state!");
6282   }
6283 
6284   if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingReadyMessage) {
6285     MaybeSetFailureCode(rv);
6286 
6287     // Must set mState before dispatching otherwise we will race with the owning
6288     // thread.
6289     mState = State::SendingReadyMessage;
6290 
6291     if (IsOnOwningThread()) {
6292       SendReadyMessage();
6293     } else {
6294       MOZ_ALWAYS_SUCCEEDS(
6295           OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
6296     }
6297   }
6298 
6299   return NS_OK;
6300 }
6301 
ActorDestroy(ActorDestroyReason aWhy)6302 void LSRequestBase::ActorDestroy(ActorDestroyReason aWhy) {
6303   AssertIsOnOwningThread();
6304 
6305   NoteComplete();
6306 
6307   // Assume ActorDestroy can happen at any time, so we can't probe the current
6308   // state since mState can be modified on any thread (only one thread at a time
6309   // based on the state machine).  However we can use mWaitingForFinish which is
6310   // only touched on the owning thread.  If mWaitingForFinisg is true, we can
6311   // also modify mState since we are guaranteed that there are no pending
6312   // runnables which would probe mState to decide what code needs to run (there
6313   // shouldn't be any running runnables on other threads either).
6314 
6315   if (mWaitingForFinish) {
6316     Finish();
6317   }
6318 
6319   // We don't have to handle the case when mWaitingForFinish is not true since
6320   // it means that either nothing has been initialized yet, so nothing to
6321   // cleanup or there are pending runnables that will detect that the actor has
6322   // been destroyed and cleanup accordingly.
6323 }
6324 
RecvCancel()6325 mozilla::ipc::IPCResult LSRequestBase::RecvCancel() {
6326   AssertIsOnOwningThread();
6327 
6328   Log();
6329 
6330   const char* crashOnCancel = PR_GetEnv("LSNG_CRASH_ON_CANCEL");
6331   if (crashOnCancel) {
6332     MOZ_CRASH("LSNG: Crash on cancel.");
6333   }
6334 
6335   IProtocol* mgr = Manager();
6336   if (!PBackgroundLSRequestParent::Send__delete__(this, NS_ERROR_FAILURE)) {
6337     return IPC_FAIL_NO_REASON(mgr);
6338   }
6339 
6340   return IPC_OK();
6341 }
6342 
RecvFinish()6343 mozilla::ipc::IPCResult LSRequestBase::RecvFinish() {
6344   AssertIsOnOwningThread();
6345 
6346   Finish();
6347 
6348   return IPC_OK();
6349 }
6350 
6351 /*******************************************************************************
6352  * PrepareDatastoreOp
6353  ******************************************************************************/
6354 
PrepareDatastoreOp(const LSRequestParams & aParams,const Maybe<ContentParentId> & aContentParentId)6355 PrepareDatastoreOp::PrepareDatastoreOp(
6356     const LSRequestParams& aParams,
6357     const Maybe<ContentParentId>& aContentParentId)
6358     : LSRequestBase(aParams, aContentParentId),
6359       mLoadDataOp(nullptr),
6360       mPrivateBrowsingId(0),
6361       mUsage(0),
6362       mSizeOfKeys(0),
6363       mSizeOfItems(0),
6364       mDatastoreId(0),
6365       mNestedState(NestedState::BeforeNesting),
6366       mForPreload(aParams.type() ==
6367                   LSRequestParams::TLSRequestPreloadDatastoreParams),
6368       mDatabaseNotAvailable(false),
6369       mInvalidated(false)
6370 #ifdef DEBUG
6371       ,
6372       mDEBUGUsage(0)
6373 #endif
6374 {
6375   MOZ_ASSERT(
6376       aParams.type() == LSRequestParams::TLSRequestPreloadDatastoreParams ||
6377       aParams.type() == LSRequestParams::TLSRequestPrepareDatastoreParams);
6378 }
6379 
~PrepareDatastoreOp()6380 PrepareDatastoreOp::~PrepareDatastoreOp() {
6381   MOZ_ASSERT(!mDirectoryLock);
6382   MOZ_ASSERT_IF(MayProceedOnNonOwningThread(),
6383                 mState == State::Initial || mState == State::Completed);
6384   MOZ_ASSERT(!mLoadDataOp);
6385 }
6386 
StringifyNestedState(nsACString & aResult) const6387 void PrepareDatastoreOp::StringifyNestedState(nsACString& aResult) const {
6388   AssertIsOnOwningThread();
6389 
6390   switch (mNestedState) {
6391     case NestedState::BeforeNesting:
6392       aResult.AppendLiteral("BeforeNesting");
6393       return;
6394 
6395     case NestedState::CheckExistingOperations:
6396       aResult.AppendLiteral("CheckExistingOperations");
6397       return;
6398 
6399     case NestedState::CheckClosingDatastore:
6400       aResult.AppendLiteral("CheckClosingDatastore");
6401       return;
6402 
6403     case NestedState::PreparationPending:
6404       aResult.AppendLiteral("PreparationPending");
6405       return;
6406 
6407     case NestedState::QuotaManagerPending:
6408       aResult.AppendLiteral("QuotaManagerPending");
6409       return;
6410 
6411     case NestedState::DirectoryOpenPending:
6412       aResult.AppendLiteral("DirectoryOpenPending");
6413       return;
6414 
6415     case NestedState::DatabaseWorkOpen:
6416       aResult.AppendLiteral("DatabaseWorkOpen");
6417       return;
6418 
6419     case NestedState::BeginLoadData:
6420       aResult.AppendLiteral("BeginLoadData");
6421       return;
6422 
6423     case NestedState::DatabaseWorkLoadData:
6424       aResult.AppendLiteral("DatabaseWorkLoadData");
6425       return;
6426 
6427     case NestedState::AfterNesting:
6428       aResult.AppendLiteral("AfterNesting");
6429       return;
6430 
6431     default:
6432       MOZ_CRASH("Bad state!");
6433   }
6434 }
6435 
Stringify(nsACString & aResult) const6436 void PrepareDatastoreOp::Stringify(nsACString& aResult) const {
6437   AssertIsOnOwningThread();
6438 
6439   LSRequestBase::Stringify(aResult);
6440   aResult.Append(kQuotaGenericDelimiter);
6441 
6442   aResult.AppendLiteral("Origin:");
6443   aResult.Append(AnonymizedOriginString(Origin()));
6444   aResult.Append(kQuotaGenericDelimiter);
6445 
6446   aResult.AppendLiteral("NestedState:");
6447   StringifyNestedState(aResult);
6448 }
6449 
Log()6450 void PrepareDatastoreOp::Log() {
6451   AssertIsOnOwningThread();
6452 
6453   LSRequestBase::Log();
6454 
6455   if (!LS_LOG_TEST()) {
6456     return;
6457   }
6458 
6459   nsCString nestedState;
6460   StringifyNestedState(nestedState);
6461 
6462   LS_LOG(("  mNestedState: %s", nestedState.get()));
6463 
6464   switch (mNestedState) {
6465     case NestedState::CheckClosingDatastore: {
6466       for (uint32_t index = gPrepareDatastoreOps->Length(); index > 0;
6467            index--) {
6468         const auto& existingOp = (*gPrepareDatastoreOps)[index - 1];
6469 
6470         if (existingOp->mDelayedOp == this) {
6471           LS_LOG(("  mDelayedBy: [%p]",
6472                   static_cast<PrepareDatastoreOp*>(existingOp.get())));
6473 
6474           existingOp->Log();
6475 
6476           break;
6477         }
6478       }
6479 
6480       break;
6481     }
6482 
6483     case NestedState::DirectoryOpenPending: {
6484       MOZ_ASSERT(mPendingDirectoryLock);
6485 
6486       LS_LOG(("  mPendingDirectoryLock: [%p]", mPendingDirectoryLock.get()));
6487 
6488       mPendingDirectoryLock->Log();
6489 
6490       break;
6491     }
6492 
6493     default:;
6494   }
6495 }
6496 
Start()6497 nsresult PrepareDatastoreOp::Start() {
6498   AssertIsOnOwningThread();
6499   MOZ_ASSERT(mState == State::StartingRequest);
6500   MOZ_ASSERT(mNestedState == NestedState::BeforeNesting);
6501   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
6502   MOZ_ASSERT(MayProceed());
6503 
6504   const LSRequestCommonParams& commonParams =
6505       mForPreload
6506           ? mParams.get_LSRequestPreloadDatastoreParams().commonParams()
6507           : mParams.get_LSRequestPrepareDatastoreParams().commonParams();
6508 
6509   const PrincipalInfo& storagePrincipalInfo =
6510       commonParams.storagePrincipalInfo();
6511 
6512   if (storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
6513     mOriginMetadata = {QuotaManager::GetInfoForChrome(),
6514                        PERSISTENCE_TYPE_DEFAULT};
6515   } else {
6516     MOZ_ASSERT(storagePrincipalInfo.type() ==
6517                PrincipalInfo::TContentPrincipalInfo);
6518 
6519     PrincipalMetadata principalMetadata =
6520         QuotaManager::GetInfoFromValidatedPrincipalInfo(storagePrincipalInfo);
6521 
6522     mOriginMetadata.mSuffix = std::move(principalMetadata.mSuffix);
6523     mOriginMetadata.mGroup = std::move(principalMetadata.mGroup);
6524     // XXX We can probably get rid of mMainThreadOrigin if we change
6525     // LSRequestBase::Dispatch to synchronously run LSRequestBase::StartRequest
6526     // through LSRequestBase::Run.
6527     mMainThreadOrigin = std::move(principalMetadata.mOrigin);
6528     mOriginMetadata.mPersistenceType = PERSISTENCE_TYPE_DEFAULT;
6529   }
6530 
6531   mState = State::Nesting;
6532   mNestedState = NestedState::CheckExistingOperations;
6533 
6534   MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
6535 
6536   return NS_OK;
6537 }
6538 
CheckExistingOperations()6539 nsresult PrepareDatastoreOp::CheckExistingOperations() {
6540   AssertIsOnOwningThread();
6541   MOZ_ASSERT(mState == State::Nesting);
6542   MOZ_ASSERT(mNestedState == NestedState::CheckExistingOperations);
6543   MOZ_ASSERT(gPrepareDatastoreOps);
6544 
6545   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
6546       !MayProceed()) {
6547     return NS_ERROR_ABORT;
6548   }
6549 
6550   const LSRequestCommonParams& commonParams =
6551       mForPreload
6552           ? mParams.get_LSRequestPreloadDatastoreParams().commonParams()
6553           : mParams.get_LSRequestPrepareDatastoreParams().commonParams();
6554 
6555   const PrincipalInfo& storagePrincipalInfo =
6556       commonParams.storagePrincipalInfo();
6557 
6558   nsCString originAttrSuffix;
6559   uint32_t privateBrowsingId;
6560 
6561   if (storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
6562     privateBrowsingId = 0;
6563   } else {
6564     MOZ_ASSERT(storagePrincipalInfo.type() ==
6565                PrincipalInfo::TContentPrincipalInfo);
6566 
6567     const ContentPrincipalInfo& info =
6568         storagePrincipalInfo.get_ContentPrincipalInfo();
6569     const OriginAttributes& attrs = info.attrs();
6570     attrs.CreateSuffix(originAttrSuffix);
6571 
6572     privateBrowsingId = attrs.mPrivateBrowsingId;
6573   }
6574 
6575   mArchivedOriginScope = ArchivedOriginScope::CreateFromOrigin(
6576       originAttrSuffix, commonParams.originKey());
6577   MOZ_ASSERT(mArchivedOriginScope);
6578 
6579   // Normally it's safe to access member variables without a mutex because even
6580   // though we hop between threads, the variables are never accessed by multiple
6581   // threads at the same time.
6582   // However, the methods OriginIsKnown and Origin can be called at any time.
6583   // So we have to make sure the member variable is set on the same thread as
6584   // those methods are called.
6585   mOriginMetadata.mOrigin = mMainThreadOrigin;
6586 
6587   MOZ_ASSERT(OriginIsKnown());
6588 
6589   mPrivateBrowsingId = privateBrowsingId;
6590 
6591   mNestedState = NestedState::CheckClosingDatastore;
6592 
6593   // See if this PrepareDatastoreOp needs to wait.
6594   bool foundThis = false;
6595   for (uint32_t index = gPrepareDatastoreOps->Length(); index > 0; index--) {
6596     const auto& existingOp = (*gPrepareDatastoreOps)[index - 1];
6597 
6598     if (existingOp == this) {
6599       foundThis = true;
6600       continue;
6601     }
6602 
6603     if (foundThis && existingOp->Origin() == Origin()) {
6604       // Only one op can be delayed.
6605       MOZ_ASSERT(!existingOp->mDelayedOp);
6606       existingOp->mDelayedOp = this;
6607 
6608       return NS_OK;
6609     }
6610   }
6611 
6612   QM_TRY(CheckClosingDatastoreInternal());
6613 
6614   return NS_OK;
6615 }
6616 
CheckClosingDatastore()6617 nsresult PrepareDatastoreOp::CheckClosingDatastore() {
6618   AssertIsOnOwningThread();
6619   MOZ_ASSERT(mState == State::Nesting);
6620   MOZ_ASSERT(mNestedState == NestedState::CheckClosingDatastore);
6621 
6622   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
6623       !MayProceed()) {
6624     return NS_ERROR_ABORT;
6625   }
6626 
6627   QM_TRY(CheckClosingDatastoreInternal());
6628 
6629   return NS_OK;
6630 }
6631 
CheckClosingDatastoreInternal()6632 nsresult PrepareDatastoreOp::CheckClosingDatastoreInternal() {
6633   AssertIsOnOwningThread();
6634   MOZ_ASSERT(mState == State::Nesting);
6635   MOZ_ASSERT(mNestedState == NestedState::CheckClosingDatastore);
6636   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
6637   MOZ_ASSERT(MayProceed());
6638 
6639   mNestedState = NestedState::PreparationPending;
6640 
6641   RefPtr<Datastore> datastore;
6642   if ((datastore = GetDatastore(Origin())) && datastore->IsClosed()) {
6643     datastore->WaitForConnectionToComplete(this);
6644 
6645     return NS_OK;
6646   }
6647 
6648   QM_TRY(BeginDatastorePreparationInternal());
6649 
6650   return NS_OK;
6651 }
6652 
BeginDatastorePreparation()6653 nsresult PrepareDatastoreOp::BeginDatastorePreparation() {
6654   AssertIsOnOwningThread();
6655   MOZ_ASSERT(mState == State::Nesting);
6656   MOZ_ASSERT(mNestedState == NestedState::PreparationPending);
6657 
6658   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
6659       !MayProceed()) {
6660     return NS_ERROR_ABORT;
6661   }
6662 
6663   QM_TRY(BeginDatastorePreparationInternal());
6664 
6665   return NS_OK;
6666 }
6667 
BeginDatastorePreparationInternal()6668 nsresult PrepareDatastoreOp::BeginDatastorePreparationInternal() {
6669   AssertIsOnOwningThread();
6670   MOZ_ASSERT(mState == State::Nesting);
6671   MOZ_ASSERT(mNestedState == NestedState::PreparationPending);
6672   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
6673   MOZ_ASSERT(MayProceed());
6674 
6675   if ((mDatastore = GetDatastore(Origin()))) {
6676     MOZ_ASSERT(!mDatastore->IsClosed());
6677 
6678     mDatastore->NoteLivePrepareDatastoreOp(this);
6679 
6680     FinishNesting();
6681 
6682     return NS_OK;
6683   }
6684 
6685   if (QuotaManager::Get()) {
6686     nsresult rv = OpenDirectory();
6687     if (NS_WARN_IF(NS_FAILED(rv))) {
6688       return rv;
6689     }
6690 
6691     return NS_OK;
6692   }
6693 
6694   mNestedState = NestedState::QuotaManagerPending;
6695   QuotaManager::GetOrCreate(this);
6696 
6697   return NS_OK;
6698 }
6699 
QuotaManagerOpen()6700 nsresult PrepareDatastoreOp::QuotaManagerOpen() {
6701   AssertIsOnOwningThread();
6702   MOZ_ASSERT(mState == State::Nesting);
6703   MOZ_ASSERT(mNestedState == NestedState::QuotaManagerPending);
6704 
6705   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
6706       !MayProceed()) {
6707     return NS_ERROR_ABORT;
6708   }
6709 
6710   QM_TRY(OkIf(QuotaManager::Get()), NS_ERROR_FAILURE);
6711 
6712   QM_TRY(OpenDirectory());
6713 
6714   return NS_OK;
6715 }
6716 
OpenDirectory()6717 nsresult PrepareDatastoreOp::OpenDirectory() {
6718   AssertIsOnOwningThread();
6719   MOZ_ASSERT(mState == State::Nesting);
6720   MOZ_ASSERT(mNestedState == NestedState::PreparationPending ||
6721              mNestedState == NestedState::QuotaManagerPending);
6722   MOZ_ASSERT(OriginIsKnown());
6723   MOZ_ASSERT(!mDirectoryLock);
6724   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
6725   MOZ_ASSERT(MayProceed());
6726   MOZ_ASSERT(QuotaManager::Get());
6727 
6728   mPendingDirectoryLock = QuotaManager::Get()->CreateDirectoryLock(
6729       PERSISTENCE_TYPE_DEFAULT, mOriginMetadata,
6730       mozilla::dom::quota::Client::LS,
6731       /* aExclusive */ false);
6732 
6733   mNestedState = NestedState::DirectoryOpenPending;
6734 
6735   {
6736     // Pin the directory lock, because Acquire might clear mPendingDirectoryLock
6737     // during the Acquire call.
6738     RefPtr pinnedDirectoryLock = mPendingDirectoryLock;
6739     pinnedDirectoryLock->Acquire(this);
6740   }
6741 
6742   return NS_OK;
6743 }
6744 
SendToIOThread()6745 void PrepareDatastoreOp::SendToIOThread() {
6746   AssertIsOnOwningThread();
6747   MOZ_ASSERT(mState == State::Nesting);
6748   MOZ_ASSERT(mNestedState == NestedState::DirectoryOpenPending);
6749   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
6750   MOZ_ASSERT(MayProceed());
6751 
6752   // Skip all disk related stuff and transition to SendingReadyMessage if we
6753   // are preparing a datastore for private browsing.
6754   // Note that we do use a directory lock for private browsing even though we
6755   // don't do any stuff on disk. The thing is that without a directory lock,
6756   // quota manager wouldn't call AbortOperationsForLocks for our private
6757   // browsing origin when a clear origin operation is requested.
6758   // AbortOperationsForLocks requests all databases to close and the datastore
6759   // is destroyed in the end. Any following LocalStorage API call will trigger
6760   // preparation of a new (empty) datastore.
6761   if (mPrivateBrowsingId) {
6762     FinishNesting();
6763 
6764     return;
6765   }
6766 
6767   QuotaManager* quotaManager = QuotaManager::Get();
6768   MOZ_ASSERT(quotaManager);
6769 
6770   // Must set this before dispatching otherwise we will race with the IO thread.
6771   mNestedState = NestedState::DatabaseWorkOpen;
6772 
6773   MOZ_ALWAYS_SUCCEEDS(
6774       quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
6775 }
6776 
DatabaseWork()6777 nsresult PrepareDatastoreOp::DatabaseWork() {
6778   AssertIsOnIOThread();
6779   MOZ_ASSERT(mArchivedOriginScope);
6780   MOZ_ASSERT(mUsage == 0);
6781   MOZ_ASSERT(mState == State::Nesting);
6782   MOZ_ASSERT(mNestedState == NestedState::DatabaseWorkOpen);
6783 
6784   // XXX Maybe add GetOrCreateInitializationInfo for this.
6785   if (!gInitializationInfo) {
6786     gInitializationInfo = new LSInitializationInfo();
6787   }
6788 
6789   auto& originInitializationInfo =
6790       gInitializationInfo->MutableOriginInitializationInfoRef(
6791           mOriginMetadata.mOrigin);
6792 
6793   const auto firstInitializationAttempt =
6794       originInitializationInfo.FirstInitializationAttempt(
6795           LSOriginInitialization::Datastore);
6796 
6797   auto rv = [&firstInitializationAttempt, this]() -> nsresult {
6798     // XXX This function is too long, refactor it into helper functions for
6799     // readability.
6800     const auto maybeExtraInfo =
6801         firstInitializationAttempt.Pending()
6802             ? Some(ScopedLogExtraInfo{
6803                   ScopedLogExtraInfo::kTagContext,
6804                   "dom::localstorage::FirstOriginInitializationAttempt::Datastore"_ns})
6805             : Nothing{};
6806 
6807     if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
6808         !MayProceedOnNonOwningThread()) {
6809       return NS_ERROR_ABORT;
6810     }
6811 
6812     QuotaManager* quotaManager = QuotaManager::Get();
6813     MOZ_ASSERT(quotaManager);
6814 
6815     // This must be called before EnsureTemporaryStorageIsInitialized.
6816     QM_TRY(quotaManager->EnsureStorageIsInitialized());
6817 
6818     // This ensures that usages for existings origin directories are cached in
6819     // memory.
6820     QM_TRY(quotaManager->EnsureTemporaryStorageIsInitialized());
6821 
6822     const UsageInfo usageInfo = quotaManager->GetUsageForClient(
6823         PERSISTENCE_TYPE_DEFAULT, mOriginMetadata,
6824         mozilla::dom::quota::Client::LS);
6825 
6826     const bool hasUsage = usageInfo.DatabaseUsage().isSome();
6827     MOZ_ASSERT(usageInfo.FileUsage().isNothing());
6828 
6829     if (!gArchivedOrigins) {
6830       QM_TRY(LoadArchivedOrigins());
6831       MOZ_ASSERT(gArchivedOrigins);
6832     }
6833 
6834     bool hasDataForMigration =
6835         mArchivedOriginScope->HasMatches(gArchivedOrigins);
6836 
6837     // If there's nothing to preload (except the case when we want to migrate
6838     // data during preloading), then we can finish the operation without
6839     // creating a datastore in GetResponse (GetResponse won't create a datastore
6840     // if mDatatabaseNotAvailable and mForPreload are both true).
6841     if (mForPreload && !hasUsage && !hasDataForMigration) {
6842       return DatabaseNotAvailable();
6843     }
6844 
6845     // The origin directory doesn't need to be created when we don't have data
6846     // for migration. It will be created on the connection thread in
6847     // Connection::EnsureStorageConnection.
6848     // However, origin quota must be initialized, GetQuotaObject in GetResponse
6849     // would fail otherwise.
6850     QM_TRY_INSPECT(
6851         const auto& directoryEntry,
6852         ([hasDataForMigration, &quotaManager,
6853           this]() -> mozilla::Result<nsCOMPtr<nsIFile>, nsresult> {
6854           if (hasDataForMigration) {
6855             QM_TRY_RETURN(quotaManager
6856                               ->EnsureTemporaryOriginIsInitialized(
6857                                   PERSISTENCE_TYPE_DEFAULT, mOriginMetadata)
6858                               .map([](const auto& res) { return res.first; }));
6859           }
6860 
6861           QM_TRY_UNWRAP(auto directoryEntry,
6862                         quotaManager->GetDirectoryForOrigin(
6863                             PERSISTENCE_TYPE_DEFAULT, Origin()));
6864 
6865           quotaManager->EnsureQuotaForOrigin(mOriginMetadata);
6866 
6867           return directoryEntry;
6868         }()));
6869 
6870     QM_TRY(directoryEntry->Append(
6871         NS_LITERAL_STRING_FROM_CSTRING(LS_DIRECTORY_NAME)));
6872 
6873     QM_TRY_INSPECT(
6874         const auto& directoryPath,
6875         MOZ_TO_RESULT_INVOKE_TYPED(nsString, directoryEntry, GetPath));
6876 
6877     // The ls directory doesn't need to be created when we don't have data for
6878     // migration. It will be created on the connection thread in
6879     // Connection::EnsureStorageConnection.
6880     QM_TRY(EnsureDirectoryEntry(directoryEntry,
6881                                 /* aCreateIfNotExists */ hasDataForMigration,
6882                                 /* aIsDirectory */ true));
6883 
6884     QM_TRY(directoryEntry->Append(kDataFileName));
6885 
6886     QM_TRY(directoryEntry->GetPath(mDatabaseFilePath));
6887 
6888     // The database doesn't need to be created when we don't have data for
6889     // migration. It will be created on the connection thread in
6890     // Connection::EnsureStorageConnection.
6891     bool alreadyExisted;
6892     QM_TRY(EnsureDirectoryEntry(directoryEntry,
6893                                 /* aCreateIfNotExists */ hasDataForMigration,
6894                                 /* aIsDirectory */ false, &alreadyExisted));
6895 
6896     if (alreadyExisted) {
6897       // The database does exist.
6898       MOZ_ASSERT(hasUsage);
6899 
6900       // XXX Change type of mUsage to UsageInfo or DatabaseUsageType.
6901       mUsage = usageInfo.DatabaseUsage().valueOr(0);
6902     } else {
6903       // The database doesn't exist.
6904       MOZ_ASSERT(!hasUsage);
6905 
6906       if (!hasDataForMigration) {
6907         // The database doesn't exist and we don't have data for migration.
6908         // Finish the operation, but create an empty datastore in GetResponse
6909         // (GetResponse will create an empty datastore if mDatabaseNotAvailable
6910         // is true and mForPreload is false).
6911         return DatabaseNotAvailable();
6912       }
6913     }
6914 
6915     // We initialized mDatabaseFilePath and mUsage, GetQuotaObject can now be
6916     // called.
6917     const RefPtr<QuotaObject> quotaObject = GetQuotaObject();
6918 
6919     QM_TRY(OkIf(quotaObject), Err(NS_ERROR_FAILURE));
6920 
6921     QM_TRY_INSPECT(const auto& usageFile, GetUsageFile(directoryPath));
6922 
6923     QM_TRY_INSPECT(const auto& usageJournalFile,
6924                    GetUsageJournalFile(directoryPath));
6925 
6926     QM_TRY_INSPECT(
6927         const auto& connection,
6928         (CreateStorageConnection(
6929             *directoryEntry, *usageFile, Origin(), [&quotaObject, this] {
6930               // This is called when the usage file was removed or we notice
6931               // that the usage file doesn't exist anymore. Adjust the usage
6932               // accordingly.
6933 
6934               MOZ_ALWAYS_TRUE(
6935                   quotaObject->MaybeUpdateSize(0, /* aTruncate */ true));
6936 
6937               mUsage = 0;
6938             })));
6939 
6940     QM_TRY(VerifyDatabaseInformation(connection));
6941 
6942     if (hasDataForMigration) {
6943       MOZ_ASSERT(mUsage == 0);
6944 
6945       QM_TRY(AttachArchiveDatabase(quotaManager->GetStoragePath(), connection));
6946 
6947       QM_TRY_INSPECT(const int64_t& newUsage,
6948                      GetUsage(*connection, mArchivedOriginScope.get()));
6949 
6950       if (!quotaObject->MaybeUpdateSize(newUsage, /* aTruncate */ true)) {
6951         return NS_ERROR_FILE_NO_DEVICE_SPACE;
6952       }
6953 
6954       auto autoUpdateSize = MakeScopeExit([&quotaObject] {
6955         MOZ_ALWAYS_TRUE(quotaObject->MaybeUpdateSize(0, /* aTruncate */ true));
6956       });
6957 
6958       mozStorageTransaction transaction(
6959           connection, false, mozIStorageConnection::TRANSACTION_IMMEDIATE);
6960 
6961       QM_TRY(transaction.Start())
6962 
6963       {
6964         nsCOMPtr<mozIStorageFunction> function = new CompressFunction();
6965 
6966         QM_TRY(connection->CreateFunction("compress"_ns, 1, function));
6967 
6968         function = new CompressibleFunction();
6969 
6970         QM_TRY(connection->CreateFunction("compressible"_ns, 1, function));
6971 
6972         QM_TRY_INSPECT(
6973             const auto& stmt,
6974             MOZ_TO_RESULT_INVOKE_TYPED(
6975                 nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
6976                 "INSERT INTO data (key, value, utf16Length, compressed) "
6977                 "SELECT key, compress(value), utf16Length(value), "
6978                 "compressible(value) "
6979                 "FROM webappsstore2 "
6980                 "WHERE originKey = :originKey "
6981                 "AND originAttributes = :originAttributes;"_ns));
6982 
6983         QM_TRY(mArchivedOriginScope->BindToStatement(stmt));
6984 
6985         QM_TRY(stmt->Execute());
6986 
6987         QM_TRY(connection->RemoveFunction("compress"_ns));
6988 
6989         QM_TRY(connection->RemoveFunction("compressible"_ns));
6990       }
6991 
6992       {
6993         QM_TRY_INSPECT(
6994             const auto& stmt,
6995             MOZ_TO_RESULT_INVOKE_TYPED(
6996                 nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
6997                 "UPDATE database SET usage = :usage;"_ns));
6998 
6999         QM_TRY(stmt->BindInt64ByName("usage"_ns, newUsage));
7000 
7001         QM_TRY(stmt->Execute());
7002       }
7003 
7004       {
7005         QM_TRY_INSPECT(
7006             const auto& stmt,
7007             MOZ_TO_RESULT_INVOKE_TYPED(
7008                 nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
7009                 "DELETE FROM webappsstore2 "
7010                 "WHERE originKey = :originKey "
7011                 "AND originAttributes = :originAttributes;"_ns));
7012 
7013         QM_TRY(mArchivedOriginScope->BindToStatement(stmt));
7014         QM_TRY(stmt->Execute());
7015       }
7016 
7017       QM_TRY(UpdateUsageFile(usageFile, usageJournalFile, newUsage));
7018       QM_TRY(transaction.Commit());
7019 
7020       autoUpdateSize.release();
7021 
7022       QM_TRY(usageJournalFile->Remove(false));
7023       QM_TRY(DetachArchiveDatabase(connection));
7024 
7025       MOZ_ASSERT(gArchivedOrigins);
7026       MOZ_ASSERT(mArchivedOriginScope->HasMatches(gArchivedOrigins));
7027       mArchivedOriginScope->RemoveMatches(gArchivedOrigins);
7028 
7029       mUsage = newUsage;
7030     }
7031 
7032     nsCOMPtr<mozIStorageConnection> shadowConnection;
7033     if (!gInitializedShadowStorage) {
7034       QM_TRY_UNWRAP(shadowConnection,
7035                     CreateShadowStorageConnection(quotaManager->GetBasePath()));
7036 
7037       gInitializedShadowStorage = true;
7038     }
7039 
7040     // Must close connections before dispatching otherwise we might race with
7041     // the connection thread which needs to open the same databases.
7042     MOZ_ALWAYS_SUCCEEDS(connection->Close());
7043 
7044     if (shadowConnection) {
7045       MOZ_ALWAYS_SUCCEEDS(shadowConnection->Close());
7046     }
7047 
7048     // Must set this before dispatching otherwise we will race with the owning
7049     // thread.
7050     mNestedState = NestedState::BeginLoadData;
7051 
7052     QM_TRY(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
7053 
7054     return NS_OK;
7055   }();
7056 
7057   firstInitializationAttempt.MaybeRecord(rv);
7058 
7059   return rv;
7060 }
7061 
DatabaseNotAvailable()7062 nsresult PrepareDatastoreOp::DatabaseNotAvailable() {
7063   AssertIsOnIOThread();
7064   MOZ_ASSERT(mState == State::Nesting);
7065   MOZ_ASSERT(mNestedState == NestedState::DatabaseWorkOpen);
7066 
7067   mDatabaseNotAvailable = true;
7068 
7069   nsresult rv = FinishNestingOnNonOwningThread();
7070   if (NS_WARN_IF(NS_FAILED(rv))) {
7071     return rv;
7072   }
7073 
7074   return NS_OK;
7075 }
7076 
EnsureDirectoryEntry(nsIFile * aEntry,bool aCreateIfNotExists,bool aIsDirectory,bool * aAlreadyExisted)7077 nsresult PrepareDatastoreOp::EnsureDirectoryEntry(nsIFile* aEntry,
7078                                                   bool aCreateIfNotExists,
7079                                                   bool aIsDirectory,
7080                                                   bool* aAlreadyExisted) {
7081   AssertIsOnIOThread();
7082   MOZ_ASSERT(aEntry);
7083 
7084   QM_TRY_INSPECT(const bool& exists, MOZ_TO_RESULT_INVOKE(aEntry, Exists));
7085 
7086   if (!exists) {
7087     if (!aCreateIfNotExists) {
7088       if (aAlreadyExisted) {
7089         *aAlreadyExisted = false;
7090       }
7091       return NS_OK;
7092     }
7093 
7094     if (aIsDirectory) {
7095       QM_TRY(aEntry->Create(nsIFile::DIRECTORY_TYPE, 0755));
7096     }
7097   }
7098 #ifdef DEBUG
7099   else {
7100     bool isDirectory;
7101     MOZ_ASSERT(NS_SUCCEEDED(aEntry->IsDirectory(&isDirectory)));
7102     MOZ_ASSERT(isDirectory == aIsDirectory);
7103   }
7104 #endif
7105 
7106   if (aAlreadyExisted) {
7107     *aAlreadyExisted = exists;
7108   }
7109   return NS_OK;
7110 }
7111 
VerifyDatabaseInformation(mozIStorageConnection * aConnection)7112 nsresult PrepareDatastoreOp::VerifyDatabaseInformation(
7113     mozIStorageConnection* aConnection) {
7114   AssertIsOnIOThread();
7115   MOZ_ASSERT(aConnection);
7116 
7117   QM_TRY_INSPECT(const auto& stmt,
7118                  CreateAndExecuteSingleStepStatement<
7119                      SingleStepResult::ReturnNullIfNoResult>(
7120                      *aConnection, "SELECT origin FROM database"_ns));
7121 
7122   QM_TRY(OkIf(stmt), NS_ERROR_FILE_CORRUPTED);
7123 
7124   QM_TRY_INSPECT(const auto& origin,
7125                  MOZ_TO_RESULT_INVOKE_TYPED(nsCString, stmt, GetUTF8String, 0));
7126 
7127   QM_TRY(OkIf(QuotaManager::AreOriginsEqualOnDisk(Origin(), origin)),
7128          NS_ERROR_FILE_CORRUPTED);
7129 
7130   return NS_OK;
7131 }
7132 
GetQuotaObject()7133 already_AddRefed<QuotaObject> PrepareDatastoreOp::GetQuotaObject() {
7134   MOZ_ASSERT(IsOnOwningThread() || IsOnIOThread());
7135   MOZ_ASSERT(!mOriginMetadata.mGroup.IsEmpty());
7136   MOZ_ASSERT(OriginIsKnown());
7137   MOZ_ASSERT(!mDatabaseFilePath.IsEmpty());
7138 
7139   QuotaManager* quotaManager = QuotaManager::Get();
7140   MOZ_ASSERT(quotaManager);
7141 
7142   RefPtr<QuotaObject> quotaObject = quotaManager->GetQuotaObject(
7143       PERSISTENCE_TYPE_DEFAULT, mOriginMetadata,
7144       mozilla::dom::quota::Client::LS, mDatabaseFilePath, mUsage);
7145 
7146   if (!quotaObject) {
7147     LS_WARNING("Failed to get quota object for group (%s) and origin (%s)!",
7148                mOriginMetadata.mGroup.get(), Origin().get());
7149   }
7150 
7151   return quotaObject.forget();
7152 }
7153 
BeginLoadData()7154 nsresult PrepareDatastoreOp::BeginLoadData() {
7155   AssertIsOnOwningThread();
7156   MOZ_ASSERT(mState == State::Nesting);
7157   MOZ_ASSERT(mNestedState == NestedState::BeginLoadData);
7158   MOZ_ASSERT(!mConnection);
7159 
7160   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
7161       !MayProceed()) {
7162     return NS_ERROR_ABORT;
7163   }
7164 
7165   if (!gConnectionThread) {
7166     gConnectionThread = new ConnectionThread();
7167   }
7168 
7169   mConnection = gConnectionThread->CreateConnection(
7170       mOriginMetadata, std::move(mArchivedOriginScope),
7171       /* aDatabaseWasNotAvailable */ false);
7172   MOZ_ASSERT(mConnection);
7173 
7174   // Must set this before dispatching otherwise we will race with the
7175   // connection thread.
7176   mNestedState = NestedState::DatabaseWorkLoadData;
7177 
7178   // Can't assign to mLoadDataOp directly since that's a weak reference and
7179   // LoadDataOp is reference counted.
7180   RefPtr<LoadDataOp> loadDataOp = new LoadDataOp(this);
7181 
7182   // This add refs loadDataOp.
7183   mConnection->Dispatch(loadDataOp);
7184 
7185   // This is cleared in LoadDataOp::Cleanup() before the load data op is
7186   // destroyed.
7187   mLoadDataOp = loadDataOp;
7188 
7189   return NS_OK;
7190 }
7191 
FinishNesting()7192 void PrepareDatastoreOp::FinishNesting() {
7193   AssertIsOnOwningThread();
7194   MOZ_ASSERT(mState == State::Nesting);
7195 
7196   // The caller holds a strong reference to us, no need for a self reference
7197   // before calling Run().
7198 
7199   mState = State::SendingReadyMessage;
7200   mNestedState = NestedState::AfterNesting;
7201 
7202   MOZ_ALWAYS_SUCCEEDS(Run());
7203 }
7204 
FinishNestingOnNonOwningThread()7205 nsresult PrepareDatastoreOp::FinishNestingOnNonOwningThread() {
7206   MOZ_ASSERT(!IsOnOwningThread());
7207   MOZ_ASSERT(mState == State::Nesting);
7208 
7209   // Must set mState before dispatching otherwise we will race with the owning
7210   // thread.
7211   mState = State::SendingReadyMessage;
7212   mNestedState = NestedState::AfterNesting;
7213 
7214   QM_TRY(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
7215 
7216   return NS_OK;
7217 }
7218 
NestedRun()7219 nsresult PrepareDatastoreOp::NestedRun() {
7220   nsresult rv;
7221 
7222   switch (mNestedState) {
7223     case NestedState::CheckExistingOperations:
7224       rv = CheckExistingOperations();
7225       break;
7226 
7227     case NestedState::CheckClosingDatastore:
7228       rv = CheckClosingDatastore();
7229       break;
7230 
7231     case NestedState::PreparationPending:
7232       rv = BeginDatastorePreparation();
7233       break;
7234 
7235     case NestedState::QuotaManagerPending:
7236       rv = QuotaManagerOpen();
7237       break;
7238 
7239     case NestedState::DatabaseWorkOpen:
7240       rv = DatabaseWork();
7241       break;
7242 
7243     case NestedState::BeginLoadData:
7244       rv = BeginLoadData();
7245       break;
7246 
7247     default:
7248       MOZ_CRASH("Bad state!");
7249   }
7250 
7251   if (NS_WARN_IF(NS_FAILED(rv))) {
7252     mNestedState = NestedState::AfterNesting;
7253 
7254     return rv;
7255   }
7256 
7257   return NS_OK;
7258 }
7259 
GetResponse(LSRequestResponse & aResponse)7260 void PrepareDatastoreOp::GetResponse(LSRequestResponse& aResponse) {
7261   AssertIsOnOwningThread();
7262   MOZ_ASSERT(mState == State::SendingResults);
7263   MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
7264   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
7265   MOZ_ASSERT(MayProceed());
7266 
7267   // A datastore is not created when we are just trying to preload data and
7268   // there's no database file.
7269   if (mDatabaseNotAvailable && mForPreload) {
7270     LSRequestPreloadDatastoreResponse preloadDatastoreResponse;
7271 
7272     aResponse = preloadDatastoreResponse;
7273 
7274     return;
7275   }
7276 
7277   if (!mDatastore) {
7278     MOZ_ASSERT(mUsage == mDEBUGUsage);
7279 
7280     RefPtr<QuotaObject> quotaObject;
7281 
7282     if (mPrivateBrowsingId == 0) {
7283       if (!mConnection) {
7284         // This can happen when there's no database file.
7285         MOZ_ASSERT(mDatabaseNotAvailable);
7286 
7287         // Even though there's no database file, we need to create a connection
7288         // and pass it to datastore.
7289         if (!gConnectionThread) {
7290           gConnectionThread = new ConnectionThread();
7291         }
7292 
7293         mConnection = gConnectionThread->CreateConnection(
7294             mOriginMetadata, std::move(mArchivedOriginScope),
7295             /* aDatabaseWasNotAvailable */ true);
7296         MOZ_ASSERT(mConnection);
7297       }
7298 
7299       quotaObject = GetQuotaObject();
7300       if (!quotaObject) {
7301         aResponse = NS_ERROR_FAILURE;
7302         return;
7303       }
7304     }
7305 
7306     mDatastore = new Datastore(
7307         mOriginMetadata, mPrivateBrowsingId, mUsage, mSizeOfKeys, mSizeOfItems,
7308         std::move(mDirectoryLock), std::move(mConnection),
7309         std::move(quotaObject), mValues, std::move(mOrderedItems));
7310 
7311     mDatastore->NoteLivePrepareDatastoreOp(this);
7312 
7313     if (!gDatastores) {
7314       gDatastores = new DatastoreHashtable();
7315     }
7316 
7317     MOZ_ASSERT(!gDatastores->Contains(Origin()));
7318     gDatastores->InsertOrUpdate(Origin(),
7319                                 WrapMovingNotNullUnchecked(mDatastore));
7320   }
7321 
7322   if (mPrivateBrowsingId && !mInvalidated) {
7323     if (!gPrivateDatastores) {
7324       gPrivateDatastores = MakeUnique<PrivateDatastoreHashtable>();
7325     }
7326 
7327     gPrivateDatastores->LookupOrInsertWith(Origin(), [&] {
7328       auto privateDatastore =
7329           MakeUnique<PrivateDatastore>(WrapMovingNotNull(mDatastore));
7330 
7331       mPrivateDatastoreRegistered.Flip();
7332 
7333       return privateDatastore;
7334     });
7335   }
7336 
7337   mDatastoreId = ++gLastDatastoreId;
7338 
7339   if (!gPreparedDatastores) {
7340     gPreparedDatastores = new PreparedDatastoreHashtable();
7341   }
7342   const auto& preparedDatastore = gPreparedDatastores->InsertOrUpdate(
7343       mDatastoreId, MakeUnique<PreparedDatastore>(
7344                         mDatastore, mContentParentId, Origin(), mDatastoreId,
7345                         /* aForPreload */ mForPreload));
7346 
7347   if (mInvalidated) {
7348     preparedDatastore->Invalidate();
7349   }
7350 
7351   mPreparedDatastoreRegistered.Flip();
7352 
7353   if (mForPreload) {
7354     LSRequestPreloadDatastoreResponse preloadDatastoreResponse;
7355 
7356     aResponse = preloadDatastoreResponse;
7357   } else {
7358     LSRequestPrepareDatastoreResponse prepareDatastoreResponse;
7359     prepareDatastoreResponse.datastoreId() = mDatastoreId;
7360 
7361     aResponse = prepareDatastoreResponse;
7362   }
7363 }
7364 
Cleanup()7365 void PrepareDatastoreOp::Cleanup() {
7366   AssertIsOnOwningThread();
7367 
7368   if (mDatastore) {
7369     MOZ_ASSERT(!mDirectoryLock);
7370     MOZ_ASSERT(!mConnection);
7371 
7372     if (NS_FAILED(ResultCode())) {
7373       if (mPrivateDatastoreRegistered) {
7374         MOZ_ASSERT(gPrivateDatastores);
7375         DebugOnly<bool> removed = gPrivateDatastores->Remove(Origin());
7376         MOZ_ASSERT(removed);
7377 
7378         if (!gPrivateDatastores->Count()) {
7379           gPrivateDatastores = nullptr;
7380         }
7381       }
7382 
7383       if (mPreparedDatastoreRegistered) {
7384         // Just in case we failed to send datastoreId to the child, we need to
7385         // destroy prepared datastore, otherwise it won't be destroyed until
7386         // the timer fires (after 20 seconds).
7387         MOZ_ASSERT(gPreparedDatastores);
7388         MOZ_ASSERT(mDatastoreId > 0);
7389         DebugOnly<bool> removed = gPreparedDatastores->Remove(mDatastoreId);
7390         MOZ_ASSERT(removed);
7391 
7392         if (!gPreparedDatastores->Count()) {
7393           gPreparedDatastores = nullptr;
7394         }
7395       }
7396     }
7397 
7398     // Make sure to release the datastore on this thread.
7399 
7400     mDatastore->NoteFinishedPrepareDatastoreOp(this);
7401 
7402     mDatastore = nullptr;
7403 
7404     CleanupMetadata();
7405   } else if (mConnection) {
7406     // If we have a connection then the operation must have failed and there
7407     // must be a directory lock too.
7408     MOZ_ASSERT(NS_FAILED(ResultCode()));
7409     MOZ_ASSERT(mDirectoryLock);
7410 
7411     // We must close the connection on the connection thread before releasing
7412     // it on this thread. The directory lock can't be released either.
7413     nsCOMPtr<nsIRunnable> callback =
7414         NewRunnableMethod("dom::OpenDatabaseOp::ConnectionClosedCallback", this,
7415                           &PrepareDatastoreOp::ConnectionClosedCallback);
7416 
7417     mConnection->Close(callback);
7418   } else {
7419     // If we don't have a connection, but we do have a directory lock then the
7420     // operation must have failed or we were preloading a datastore and there
7421     // was no physical database on disk.
7422     MOZ_ASSERT_IF(mDirectoryLock,
7423                   NS_FAILED(ResultCode()) || mDatabaseNotAvailable);
7424 
7425     // There's no connection, so it's safe to release the directory lock and
7426     // unregister itself from the array.
7427 
7428     mDirectoryLock = nullptr;
7429 
7430     CleanupMetadata();
7431   }
7432 }
7433 
ConnectionClosedCallback()7434 void PrepareDatastoreOp::ConnectionClosedCallback() {
7435   AssertIsOnOwningThread();
7436   MOZ_ASSERT(NS_FAILED(ResultCode()));
7437   MOZ_ASSERT(mDirectoryLock);
7438   MOZ_ASSERT(mConnection);
7439 
7440   mConnection = nullptr;
7441   mDirectoryLock = nullptr;
7442 
7443   CleanupMetadata();
7444 }
7445 
CleanupMetadata()7446 void PrepareDatastoreOp::CleanupMetadata() {
7447   AssertIsOnOwningThread();
7448 
7449   if (mDelayedOp) {
7450     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mDelayedOp.forget()));
7451   }
7452 
7453   MOZ_ASSERT(gPrepareDatastoreOps);
7454   gPrepareDatastoreOps->RemoveElement(this);
7455 
7456   QuotaManager::MaybeRecordQuotaClientShutdownStep(
7457       quota::Client::LS, "PrepareDatastoreOp completed"_ns);
7458 
7459   if (gPrepareDatastoreOps->IsEmpty()) {
7460     gPrepareDatastoreOps = nullptr;
7461   }
7462 }
7463 
NS_IMPL_ISUPPORTS_INHERITED0(PrepareDatastoreOp,LSRequestBase)7464 NS_IMPL_ISUPPORTS_INHERITED0(PrepareDatastoreOp, LSRequestBase)
7465 
7466 void PrepareDatastoreOp::ActorDestroy(ActorDestroyReason aWhy) {
7467   AssertIsOnOwningThread();
7468 
7469   LSRequestBase::ActorDestroy(aWhy);
7470 
7471   if (mLoadDataOp) {
7472     mLoadDataOp->NoteComplete();
7473   }
7474 }
7475 
DirectoryLockAcquired(DirectoryLock * aLock)7476 void PrepareDatastoreOp::DirectoryLockAcquired(DirectoryLock* aLock) {
7477   AssertIsOnOwningThread();
7478   MOZ_ASSERT(mState == State::Nesting);
7479   MOZ_ASSERT(mNestedState == NestedState::DirectoryOpenPending);
7480   MOZ_ASSERT(!mDirectoryLock);
7481 
7482   mPendingDirectoryLock = nullptr;
7483 
7484   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
7485       !MayProceed()) {
7486     MaybeSetFailureCode(NS_ERROR_ABORT);
7487 
7488     FinishNesting();
7489 
7490     return;
7491   }
7492 
7493   mDirectoryLock = aLock;
7494 
7495   SendToIOThread();
7496 }
7497 
DirectoryLockFailed()7498 void PrepareDatastoreOp::DirectoryLockFailed() {
7499   AssertIsOnOwningThread();
7500   MOZ_ASSERT(mState == State::Nesting);
7501   MOZ_ASSERT(mNestedState == NestedState::DirectoryOpenPending);
7502   MOZ_ASSERT(!mDirectoryLock);
7503 
7504   mPendingDirectoryLock = nullptr;
7505 
7506   MaybeSetFailureCode(NS_ERROR_FAILURE);
7507 
7508   FinishNesting();
7509 }
7510 
DoDatastoreWork()7511 nsresult PrepareDatastoreOp::LoadDataOp::DoDatastoreWork() {
7512   AssertIsOnGlobalConnectionThread();
7513   MOZ_ASSERT(mConnection);
7514   MOZ_ASSERT(mPrepareDatastoreOp);
7515   MOZ_ASSERT(mPrepareDatastoreOp->mState == State::Nesting);
7516   MOZ_ASSERT(mPrepareDatastoreOp->mNestedState ==
7517              NestedState::DatabaseWorkLoadData);
7518 
7519   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
7520       !MayProceedOnNonOwningThread()) {
7521     return NS_ERROR_ABORT;
7522   }
7523 
7524   QM_TRY_INSPECT(const auto& stmt,
7525                  mConnection->BorrowCachedStatement(
7526                      "SELECT key, value, utf16Length, compressed "
7527                      "FROM data;"_ns));
7528 
7529   QM_TRY(quota::CollectWhileHasResult(
7530       *stmt, [this](auto& stmt) -> mozilla::Result<Ok, nsresult> {
7531         QM_TRY_UNWRAP(auto key,
7532                       MOZ_TO_RESULT_INVOKE_TYPED(nsString, stmt, GetString, 0));
7533 
7534         LSValue value;
7535         QM_TRY(value.InitFromStatement(&stmt, 1));
7536 
7537         mPrepareDatastoreOp->mValues.InsertOrUpdate(key, value);
7538         mPrepareDatastoreOp->mSizeOfKeys += key.Length();
7539         mPrepareDatastoreOp->mSizeOfItems += key.Length() + value.Length();
7540 #ifdef DEBUG
7541         mPrepareDatastoreOp->mDEBUGUsage += key.Length() + value.UTF16Length();
7542 #endif
7543 
7544         auto item = mPrepareDatastoreOp->mOrderedItems.AppendElement();
7545         item->key() = std::move(key);
7546         item->value() = std::move(value);
7547 
7548         return Ok{};
7549       }));
7550 
7551   return NS_OK;
7552 }
7553 
OnSuccess()7554 void PrepareDatastoreOp::LoadDataOp::OnSuccess() {
7555   AssertIsOnOwningThread();
7556   MOZ_ASSERT(mPrepareDatastoreOp);
7557   MOZ_ASSERT(mPrepareDatastoreOp->mState == State::Nesting);
7558   MOZ_ASSERT(mPrepareDatastoreOp->mNestedState ==
7559              NestedState::DatabaseWorkLoadData);
7560   MOZ_ASSERT(mPrepareDatastoreOp->mLoadDataOp == this);
7561 
7562   mPrepareDatastoreOp->FinishNesting();
7563 }
7564 
OnFailure(nsresult aResultCode)7565 void PrepareDatastoreOp::LoadDataOp::OnFailure(nsresult aResultCode) {
7566   AssertIsOnOwningThread();
7567   MOZ_ASSERT(mPrepareDatastoreOp);
7568   MOZ_ASSERT(mPrepareDatastoreOp->mState == State::Nesting);
7569   MOZ_ASSERT(mPrepareDatastoreOp->mNestedState ==
7570              NestedState::DatabaseWorkLoadData);
7571   MOZ_ASSERT(mPrepareDatastoreOp->mLoadDataOp == this);
7572 
7573   mPrepareDatastoreOp->SetFailureCode(aResultCode);
7574 
7575   mPrepareDatastoreOp->FinishNesting();
7576 }
7577 
Cleanup()7578 void PrepareDatastoreOp::LoadDataOp::Cleanup() {
7579   AssertIsOnOwningThread();
7580   MOZ_ASSERT(mPrepareDatastoreOp);
7581   MOZ_ASSERT(mPrepareDatastoreOp->mLoadDataOp == this);
7582 
7583   mPrepareDatastoreOp->mLoadDataOp = nullptr;
7584   mPrepareDatastoreOp = nullptr;
7585 
7586   ConnectionDatastoreOperationBase::Cleanup();
7587 }
7588 
NS_IMPL_ISUPPORTS(PrepareDatastoreOp::CompressFunction,mozIStorageFunction)7589 NS_IMPL_ISUPPORTS(PrepareDatastoreOp::CompressFunction, mozIStorageFunction)
7590 
7591 NS_IMETHODIMP
7592 PrepareDatastoreOp::CompressFunction::OnFunctionCall(
7593     mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult) {
7594   AssertIsOnIOThread();
7595   MOZ_ASSERT(aFunctionArguments);
7596   MOZ_ASSERT(aResult);
7597 
7598 #ifdef DEBUG
7599   {
7600     uint32_t argCount;
7601     MOZ_ALWAYS_SUCCEEDS(aFunctionArguments->GetNumEntries(&argCount));
7602     MOZ_ASSERT(argCount == 1);
7603 
7604     int32_t type;
7605     MOZ_ALWAYS_SUCCEEDS(aFunctionArguments->GetTypeOfIndex(0, &type));
7606     MOZ_ASSERT(type == mozIStorageValueArray::VALUE_TYPE_TEXT);
7607   }
7608 #endif
7609 
7610   QM_TRY_INSPECT(const auto& value,
7611                  MOZ_TO_RESULT_INVOKE_TYPED(nsCString, aFunctionArguments,
7612                                             GetUTF8String, 0));
7613 
7614   nsCString compressed;
7615   QM_TRY(OkIf(SnappyCompress(value, compressed)), NS_ERROR_FAILURE);
7616 
7617   nsCOMPtr<nsIVariant> result =
7618       new storage::UTF8TextVariant(compressed.IsVoid() ? value : compressed);
7619 
7620   result.forget(aResult);
7621   return NS_OK;
7622 }
7623 
NS_IMPL_ISUPPORTS(PrepareDatastoreOp::CompressibleFunction,mozIStorageFunction)7624 NS_IMPL_ISUPPORTS(PrepareDatastoreOp::CompressibleFunction, mozIStorageFunction)
7625 
7626 NS_IMETHODIMP
7627 PrepareDatastoreOp::CompressibleFunction::OnFunctionCall(
7628     mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult) {
7629   AssertIsOnIOThread();
7630   MOZ_ASSERT(aFunctionArguments);
7631   MOZ_ASSERT(aResult);
7632 
7633 #ifdef DEBUG
7634   {
7635     uint32_t argCount;
7636     MOZ_ALWAYS_SUCCEEDS(aFunctionArguments->GetNumEntries(&argCount));
7637     MOZ_ASSERT(argCount == 1);
7638 
7639     int32_t type;
7640     MOZ_ALWAYS_SUCCEEDS(aFunctionArguments->GetTypeOfIndex(0, &type));
7641     MOZ_ASSERT(type == mozIStorageValueArray::VALUE_TYPE_TEXT);
7642   }
7643 #endif
7644 
7645   QM_TRY_INSPECT(const auto& value,
7646                  MOZ_TO_RESULT_INVOKE_TYPED(nsCString, aFunctionArguments,
7647                                             GetUTF8String, 0));
7648 
7649   nsCString compressed;
7650   QM_TRY(OkIf(SnappyCompress(value, compressed)), NS_ERROR_FAILURE);
7651 
7652   const bool compressible = !compressed.IsVoid();
7653 
7654   nsCOMPtr<nsIVariant> result = new storage::IntegerVariant(compressible);
7655 
7656   result.forget(aResult);
7657   return NS_OK;
7658 }
7659 
7660 /*******************************************************************************
7661  * PrepareObserverOp
7662  ******************************************************************************/
7663 
PrepareObserverOp(const LSRequestParams & aParams,const Maybe<ContentParentId> & aContentParentId)7664 PrepareObserverOp::PrepareObserverOp(
7665     const LSRequestParams& aParams,
7666     const Maybe<ContentParentId>& aContentParentId)
7667     : LSRequestBase(aParams, aContentParentId) {
7668   MOZ_ASSERT(aParams.type() ==
7669              LSRequestParams::TLSRequestPrepareObserverParams);
7670 }
7671 
Start()7672 nsresult PrepareObserverOp::Start() {
7673   AssertIsOnOwningThread();
7674   MOZ_ASSERT(mState == State::StartingRequest);
7675   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
7676   MOZ_ASSERT(MayProceed());
7677 
7678   const LSRequestPrepareObserverParams params =
7679       mParams.get_LSRequestPrepareObserverParams();
7680 
7681   const PrincipalInfo& storagePrincipalInfo = params.storagePrincipalInfo();
7682 
7683   if (storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
7684     mOrigin = QuotaManager::GetOriginForChrome();
7685   } else {
7686     MOZ_ASSERT(storagePrincipalInfo.type() ==
7687                PrincipalInfo::TContentPrincipalInfo);
7688 
7689     mOrigin =
7690         QuotaManager::GetOriginFromValidatedPrincipalInfo(storagePrincipalInfo);
7691   }
7692 
7693   mState = State::SendingReadyMessage;
7694   MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
7695 
7696   return NS_OK;
7697 }
7698 
GetResponse(LSRequestResponse & aResponse)7699 void PrepareObserverOp::GetResponse(LSRequestResponse& aResponse) {
7700   AssertIsOnOwningThread();
7701   MOZ_ASSERT(mState == State::SendingResults);
7702   MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
7703   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
7704   MOZ_ASSERT(MayProceed());
7705 
7706   uint64_t observerId = ++gLastObserverId;
7707 
7708   RefPtr<Observer> observer = new Observer(mOrigin);
7709 
7710   if (!gPreparedObsevers) {
7711     gPreparedObsevers = new PreparedObserverHashtable();
7712   }
7713   gPreparedObsevers->InsertOrUpdate(observerId, std::move(observer));
7714 
7715   LSRequestPrepareObserverResponse prepareObserverResponse;
7716   prepareObserverResponse.observerId() = observerId;
7717 
7718   aResponse = prepareObserverResponse;
7719 }
7720 
7721 /*******************************************************************************
7722 + * LSSimpleRequestBase
7723 +
7724 ******************************************************************************/
7725 
LSSimpleRequestBase(const LSSimpleRequestParams & aParams,const Maybe<ContentParentId> & aContentParentId)7726 LSSimpleRequestBase::LSSimpleRequestBase(
7727     const LSSimpleRequestParams& aParams,
7728     const Maybe<ContentParentId>& aContentParentId)
7729     : mParams(aParams),
7730       mContentParentId(aContentParentId),
7731       mState(State::Initial) {}
7732 
~LSSimpleRequestBase()7733 LSSimpleRequestBase::~LSSimpleRequestBase() {
7734   MOZ_ASSERT_IF(MayProceedOnNonOwningThread(),
7735                 mState == State::Initial || mState == State::Completed);
7736 }
7737 
Dispatch()7738 void LSSimpleRequestBase::Dispatch() {
7739   AssertIsOnOwningThread();
7740 
7741   mState = State::StartingRequest;
7742 
7743   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
7744 }
7745 
VerifyRequestParams()7746 bool LSSimpleRequestBase::VerifyRequestParams() {
7747   AssertIsOnBackgroundThread();
7748 
7749   MOZ_ASSERT(mParams.type() != LSSimpleRequestParams::T__None);
7750 
7751   switch (mParams.type()) {
7752     case LSSimpleRequestParams::TLSSimpleRequestPreloadedParams: {
7753       const LSSimpleRequestPreloadedParams& params =
7754           mParams.get_LSSimpleRequestPreloadedParams();
7755 
7756       if (NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo(),
7757                                           params.storagePrincipalInfo()))) {
7758         return false;
7759       }
7760 
7761       break;
7762     }
7763 
7764     default:
7765       MOZ_CRASH("Should never get here!");
7766   }
7767 
7768   return true;
7769 }
7770 
StartRequest()7771 nsresult LSSimpleRequestBase::StartRequest() {
7772   AssertIsOnOwningThread();
7773   MOZ_ASSERT(mState == State::StartingRequest);
7774 
7775   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
7776       !MayProceed()) {
7777     return NS_ERROR_ABORT;
7778   }
7779 
7780 #ifdef DEBUG
7781   // Always verify parameters in DEBUG builds!
7782   bool trustParams = false;
7783 #else
7784   bool trustParams = !BackgroundParent::IsOtherProcessActor(Manager());
7785 #endif
7786 
7787   if (!trustParams && NS_WARN_IF(!VerifyRequestParams())) {
7788     return NS_ERROR_FAILURE;
7789   }
7790 
7791   QM_TRY(Start());
7792 
7793   return NS_OK;
7794 }
7795 
SendResults()7796 void LSSimpleRequestBase::SendResults() {
7797   AssertIsOnOwningThread();
7798   MOZ_ASSERT(mState == State::SendingResults);
7799 
7800   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
7801       !MayProceed()) {
7802     MaybeSetFailureCode(NS_ERROR_ABORT);
7803   }
7804 
7805   if (MayProceed()) {
7806     LSSimpleRequestResponse response;
7807 
7808     if (NS_SUCCEEDED(ResultCode())) {
7809       GetResponse(response);
7810     } else {
7811       response = ResultCode();
7812     }
7813 
7814     Unused << PBackgroundLSSimpleRequestParent::Send__delete__(this, response);
7815   }
7816 
7817   mState = State::Completed;
7818 }
7819 
7820 NS_IMETHODIMP
Run()7821 LSSimpleRequestBase::Run() {
7822   nsresult rv;
7823 
7824   switch (mState) {
7825     case State::StartingRequest:
7826       rv = StartRequest();
7827       break;
7828 
7829     case State::SendingResults:
7830       SendResults();
7831       return NS_OK;
7832 
7833     default:
7834       MOZ_CRASH("Bad state!");
7835   }
7836 
7837   if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
7838     MaybeSetFailureCode(rv);
7839 
7840     // Must set mState before dispatching otherwise we will race with the owning
7841     // thread.
7842     mState = State::SendingResults;
7843 
7844     if (IsOnOwningThread()) {
7845       SendResults();
7846     } else {
7847       MOZ_ALWAYS_SUCCEEDS(
7848           OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
7849     }
7850   }
7851 
7852   return NS_OK;
7853 }
7854 
ActorDestroy(ActorDestroyReason aWhy)7855 void LSSimpleRequestBase::ActorDestroy(ActorDestroyReason aWhy) {
7856   AssertIsOnOwningThread();
7857 
7858   NoteComplete();
7859 }
7860 
7861 /*******************************************************************************
7862  * PreloadedOp
7863  ******************************************************************************/
7864 
PreloadedOp(const LSSimpleRequestParams & aParams,const Maybe<ContentParentId> & aContentParentId)7865 PreloadedOp::PreloadedOp(const LSSimpleRequestParams& aParams,
7866                          const Maybe<ContentParentId>& aContentParentId)
7867     : LSSimpleRequestBase(aParams, aContentParentId) {
7868   MOZ_ASSERT(aParams.type() ==
7869              LSSimpleRequestParams::TLSSimpleRequestPreloadedParams);
7870 }
7871 
Start()7872 nsresult PreloadedOp::Start() {
7873   AssertIsOnOwningThread();
7874   MOZ_ASSERT(mState == State::StartingRequest);
7875   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
7876   MOZ_ASSERT(MayProceed());
7877 
7878   const LSSimpleRequestPreloadedParams& params =
7879       mParams.get_LSSimpleRequestPreloadedParams();
7880 
7881   const PrincipalInfo& storagePrincipalInfo = params.storagePrincipalInfo();
7882 
7883   MOZ_ASSERT(
7884       storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo ||
7885       storagePrincipalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
7886   mOrigin = storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo
7887                 ? nsCString{QuotaManager::GetOriginForChrome()}
7888                 : QuotaManager::GetOriginFromValidatedPrincipalInfo(
7889                       storagePrincipalInfo);
7890 
7891   mState = State::SendingResults;
7892   MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
7893 
7894   return NS_OK;
7895 }
7896 
GetResponse(LSSimpleRequestResponse & aResponse)7897 void PreloadedOp::GetResponse(LSSimpleRequestResponse& aResponse) {
7898   AssertIsOnOwningThread();
7899   MOZ_ASSERT(mState == State::SendingResults);
7900   MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
7901   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
7902   MOZ_ASSERT(MayProceed());
7903 
7904   bool preloaded;
7905   RefPtr<Datastore> datastore;
7906   if ((datastore = GetDatastore(mOrigin)) && !datastore->IsClosed()) {
7907     preloaded = true;
7908   } else {
7909     preloaded = false;
7910   }
7911 
7912   LSSimpleRequestPreloadedResponse preloadedResponse;
7913   preloadedResponse.preloaded() = preloaded;
7914 
7915   aResponse = preloadedResponse;
7916 }
7917 
7918 /*******************************************************************************
7919  * ArchivedOriginScope
7920  ******************************************************************************/
7921 
7922 // static
CreateFromOrigin(const nsACString & aOriginAttrSuffix,const nsACString & aOriginKey)7923 UniquePtr<ArchivedOriginScope> ArchivedOriginScope::CreateFromOrigin(
7924     const nsACString& aOriginAttrSuffix, const nsACString& aOriginKey) {
7925   return WrapUnique(
7926       new ArchivedOriginScope(Origin(aOriginAttrSuffix, aOriginKey)));
7927 }
7928 
7929 // static
CreateFromPrefix(const nsACString & aOriginKey)7930 UniquePtr<ArchivedOriginScope> ArchivedOriginScope::CreateFromPrefix(
7931     const nsACString& aOriginKey) {
7932   return WrapUnique(new ArchivedOriginScope(Prefix(aOriginKey)));
7933 }
7934 
7935 // static
CreateFromPattern(const OriginAttributesPattern & aPattern)7936 UniquePtr<ArchivedOriginScope> ArchivedOriginScope::CreateFromPattern(
7937     const OriginAttributesPattern& aPattern) {
7938   return WrapUnique(new ArchivedOriginScope(Pattern(aPattern)));
7939 }
7940 
7941 // static
CreateFromNull()7942 UniquePtr<ArchivedOriginScope> ArchivedOriginScope::CreateFromNull() {
7943   return WrapUnique(new ArchivedOriginScope(Null()));
7944 }
7945 
GetBindingClause() const7946 nsLiteralCString ArchivedOriginScope::GetBindingClause() const {
7947   return mData.match(
7948       [](const Origin&) {
7949         return " WHERE originKey = :originKey "
7950                "AND originAttributes = :originAttributes"_ns;
7951       },
7952       [](const Pattern&) {
7953         return " WHERE originAttributes MATCH :originAttributesPattern"_ns;
7954       },
7955       [](const Prefix&) { return " WHERE originKey = :originKey"_ns; },
7956       [](const Null&) { return ""_ns; });
7957 }
7958 
BindToStatement(mozIStorageStatement * aStmt) const7959 nsresult ArchivedOriginScope::BindToStatement(
7960     mozIStorageStatement* aStmt) const {
7961   MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
7962   MOZ_ASSERT(aStmt);
7963 
7964   struct Matcher {
7965     mozIStorageStatement* mStmt;
7966 
7967     explicit Matcher(mozIStorageStatement* aStmt) : mStmt(aStmt) {}
7968 
7969     nsresult operator()(const Origin& aOrigin) {
7970       QM_TRY(mStmt->BindUTF8StringByName("originKey"_ns,
7971                                          aOrigin.OriginNoSuffix()));
7972 
7973       QM_TRY(mStmt->BindUTF8StringByName("originAttributes"_ns,
7974                                          aOrigin.OriginSuffix()));
7975 
7976       return NS_OK;
7977     }
7978 
7979     nsresult operator()(const Prefix& aPrefix) {
7980       QM_TRY(mStmt->BindUTF8StringByName("originKey"_ns,
7981                                          aPrefix.OriginNoSuffix()));
7982 
7983       return NS_OK;
7984     }
7985 
7986     nsresult operator()(const Pattern& aPattern) {
7987       QM_TRY(mStmt->BindUTF8StringByName("originAttributesPattern"_ns,
7988                                          "pattern1"_ns));
7989 
7990       return NS_OK;
7991     }
7992 
7993     nsresult operator()(const Null& aNull) { return NS_OK; }
7994   };
7995 
7996   QM_TRY(mData.match(Matcher(aStmt)));
7997 
7998   return NS_OK;
7999 }
8000 
HasMatches(ArchivedOriginHashtable * aHashtable) const8001 bool ArchivedOriginScope::HasMatches(
8002     ArchivedOriginHashtable* aHashtable) const {
8003   AssertIsOnIOThread();
8004   MOZ_ASSERT(aHashtable);
8005 
8006   return mData.match(
8007       [aHashtable](const Origin& aOrigin) {
8008         const nsCString hashKey = GetArchivedOriginHashKey(
8009             aOrigin.OriginSuffix(), aOrigin.OriginNoSuffix());
8010 
8011         return aHashtable->Contains(hashKey);
8012       },
8013       [aHashtable](const Pattern& aPattern) {
8014         return std::any_of(
8015             aHashtable->Values().cbegin(), aHashtable->Values().cend(),
8016             [&aPattern](const auto& entry) {
8017               return aPattern.GetPattern().Matches(entry->mOriginAttributes);
8018             });
8019       },
8020       [aHashtable](const Prefix& aPrefix) {
8021         return std::any_of(
8022             aHashtable->Values().cbegin(), aHashtable->Values().cend(),
8023             [&aPrefix](const auto& entry) {
8024               return entry->mOriginNoSuffix == aPrefix.OriginNoSuffix();
8025             });
8026       },
8027       [aHashtable](const Null& aNull) { return !aHashtable->IsEmpty(); });
8028 }
8029 
RemoveMatches(ArchivedOriginHashtable * aHashtable) const8030 void ArchivedOriginScope::RemoveMatches(
8031     ArchivedOriginHashtable* aHashtable) const {
8032   AssertIsOnIOThread();
8033   MOZ_ASSERT(aHashtable);
8034 
8035   struct Matcher {
8036     ArchivedOriginHashtable* mHashtable;
8037 
8038     explicit Matcher(ArchivedOriginHashtable* aHashtable)
8039         : mHashtable(aHashtable) {}
8040 
8041     void operator()(const Origin& aOrigin) {
8042       nsCString hashKey = GetArchivedOriginHashKey(aOrigin.OriginSuffix(),
8043                                                    aOrigin.OriginNoSuffix());
8044 
8045       mHashtable->Remove(hashKey);
8046     }
8047 
8048     void operator()(const Prefix& aPrefix) {
8049       for (auto iter = mHashtable->Iter(); !iter.Done(); iter.Next()) {
8050         const auto& archivedOriginInfo = iter.Data();
8051 
8052         if (archivedOriginInfo->mOriginNoSuffix == aPrefix.OriginNoSuffix()) {
8053           iter.Remove();
8054         }
8055       }
8056     }
8057 
8058     void operator()(const Pattern& aPattern) {
8059       for (auto iter = mHashtable->Iter(); !iter.Done(); iter.Next()) {
8060         const auto& archivedOriginInfo = iter.Data();
8061 
8062         if (aPattern.GetPattern().Matches(
8063                 archivedOriginInfo->mOriginAttributes)) {
8064           iter.Remove();
8065         }
8066       }
8067     }
8068 
8069     void operator()(const Null& aNull) { mHashtable->Clear(); }
8070   };
8071 
8072   mData.match(Matcher(aHashtable));
8073 }
8074 
8075 /*******************************************************************************
8076  * QuotaClient
8077  ******************************************************************************/
8078 
8079 QuotaClient* QuotaClient::sInstance = nullptr;
8080 
QuotaClient()8081 QuotaClient::QuotaClient()
8082     : mShadowDatabaseMutex("LocalStorage mShadowDatabaseMutex"),
8083       mShutdownRequested(false) {
8084   AssertIsOnBackgroundThread();
8085   MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
8086 
8087   sInstance = this;
8088 }
8089 
~QuotaClient()8090 QuotaClient::~QuotaClient() {
8091   AssertIsOnBackgroundThread();
8092   MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
8093 
8094   sInstance = nullptr;
8095 }
8096 
8097 // static
Initialize()8098 nsresult QuotaClient::Initialize() {
8099   MOZ_ASSERT(NS_IsMainThread());
8100 
8101   nsresult rv = Observer::Initialize();
8102   if (NS_WARN_IF(NS_FAILED(rv))) {
8103     return rv;
8104   }
8105 
8106   return NS_OK;
8107 }
8108 
GetType()8109 mozilla::dom::quota::Client::Type QuotaClient::GetType() {
8110   return QuotaClient::LS;
8111 }
8112 
InitOrigin(PersistenceType aPersistenceType,const OriginMetadata & aOriginMetadata,const AtomicBool & aCanceled)8113 Result<UsageInfo, nsresult> QuotaClient::InitOrigin(
8114     PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
8115     const AtomicBool& aCanceled) {
8116   AssertIsOnIOThread();
8117   MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_DEFAULT);
8118 
8119   QuotaManager* quotaManager = QuotaManager::Get();
8120   MOZ_ASSERT(quotaManager);
8121 
8122   QM_TRY_INSPECT(const auto& directory,
8123                  quotaManager->GetDirectoryForOrigin(aPersistenceType,
8124                                                      aOriginMetadata.mOrigin));
8125 
8126   MOZ_ASSERT(directory);
8127 
8128   QM_TRY(directory->Append(NS_LITERAL_STRING_FROM_CSTRING(LS_DIRECTORY_NAME)));
8129 
8130 #ifdef DEBUG
8131   {
8132     QM_TRY_INSPECT(const bool& exists, MOZ_TO_RESULT_INVOKE(directory, Exists));
8133     MOZ_ASSERT(exists);
8134   }
8135 #endif
8136 
8137   QM_TRY_INSPECT(const auto& directoryPath,
8138                  MOZ_TO_RESULT_INVOKE_TYPED(nsString, directory, GetPath));
8139 
8140   QM_TRY_INSPECT(const auto& usageFile, GetUsageFile(directoryPath));
8141 
8142   // XXX Try to make usageFileExists const
8143   QM_TRY_UNWRAP(bool usageFileExists, ExistsAsFile(*usageFile));
8144 
8145   QM_TRY_INSPECT(const auto& usageJournalFile,
8146                  GetUsageJournalFile(directoryPath));
8147 
8148   QM_TRY_INSPECT(const bool& usageJournalFileExists,
8149                  ExistsAsFile(*usageJournalFile));
8150 
8151   if (usageJournalFileExists) {
8152     if (usageFileExists) {
8153       QM_TRY(usageFile->Remove(false));
8154 
8155       usageFileExists = false;
8156     }
8157 
8158     QM_TRY(usageJournalFile->Remove(false));
8159   }
8160 
8161   QM_TRY_INSPECT(const auto& file,
8162                  CloneFileAndAppend(*directory, kDataFileName));
8163 
8164   QM_TRY_INSPECT(const bool& fileExists, ExistsAsFile(*file));
8165 
8166   QM_TRY_INSPECT(
8167       const UsageInfo& res,
8168       ([fileExists, usageFileExists, &file, &usageFile, &usageJournalFile,
8169         &aOriginMetadata]() -> Result<UsageInfo, nsresult> {
8170         if (fileExists) {
8171           QM_TRY_RETURN(QM_OR_ELSE_WARN(
8172               // Expression. To simplify control flow, we call LoadUsageFile
8173               // unconditionally here, even though it will necessarily fail if
8174               // usageFileExists is false.
8175               LoadUsageFile(*usageFile),
8176               // Fallback.
8177               ([&file, &usageFile, &usageJournalFile, &aOriginMetadata](
8178                    const nsresult) -> Result<UsageInfo, nsresult> {
8179                 QM_TRY_INSPECT(
8180                     const auto& connection,
8181                     CreateStorageConnection(*file, *usageFile,
8182                                             aOriginMetadata.mOrigin, [] {}));
8183 
8184                 QM_TRY_INSPECT(const int64_t& usage,
8185                                GetUsage(*connection,
8186                                         /* aArchivedOriginScope */ nullptr));
8187 
8188                 QM_TRY(UpdateUsageFile(usageFile, usageJournalFile, usage));
8189 
8190                 QM_TRY(usageJournalFile->Remove(false));
8191 
8192                 MOZ_ASSERT(usage >= 0);
8193                 return UsageInfo{DatabaseUsageType(Some(uint64_t(usage)))};
8194               })));
8195         }
8196 
8197         if (usageFileExists) {
8198           QM_TRY(usageFile->Remove(false));
8199         }
8200 
8201         return UsageInfo{};
8202       }()));
8203 
8204   // Report unknown files in debug builds, but don't fail, just warn (we don't
8205   // report unknown files in release builds because that requires extra
8206   // scanning of the directory which would slow down entire initialization for
8207   // little benefit).
8208 
8209 #ifdef DEBUG
8210   QM_TRY(CollectEachFileAtomicCancelable(
8211       *directory, aCanceled,
8212       [](const nsCOMPtr<nsIFile>& file) -> Result<Ok, nsresult> {
8213         QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(*file));
8214 
8215         switch (dirEntryKind) {
8216           case nsIFileKind::ExistsAsDirectory:
8217             Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
8218             break;
8219 
8220           case nsIFileKind::ExistsAsFile: {
8221             QM_TRY_INSPECT(
8222                 const auto& leafName,
8223                 MOZ_TO_RESULT_INVOKE_TYPED(nsString, file, GetLeafName));
8224 
8225             if (leafName.Equals(kDataFileName) ||
8226                 leafName.Equals(kJournalFileName) ||
8227                 leafName.Equals(kUsageFileName) ||
8228                 leafName.Equals(kUsageJournalFileName)) {
8229               return Ok{};
8230             }
8231 
8232             Unused << WARN_IF_FILE_IS_UNKNOWN(*file);
8233 
8234             break;
8235           }
8236 
8237           case nsIFileKind::DoesNotExist:
8238             // Ignore files that got removed externally while iterating.
8239             break;
8240         }
8241         return Ok{};
8242       }));
8243 #endif
8244 
8245   return res;
8246 }
8247 
InitOriginWithoutTracking(PersistenceType aPersistenceType,const OriginMetadata & aOriginMetadata,const AtomicBool & aCanceled)8248 nsresult QuotaClient::InitOriginWithoutTracking(
8249     PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
8250     const AtomicBool& aCanceled) {
8251   AssertIsOnIOThread();
8252 
8253   // This is called when a storage/permanent/${origin}/ls directory exists. Even
8254   // though this shouldn't happen with a "good" profile, we shouldn't return an
8255   // error here, since that would cause origin initialization to fail. We just
8256   // warn and otherwise ignore that.
8257   UNKNOWN_FILE_WARNING(NS_LITERAL_STRING_FROM_CSTRING(LS_DIRECTORY_NAME));
8258   return NS_OK;
8259 }
8260 
GetUsageForOrigin(PersistenceType aPersistenceType,const OriginMetadata & aOriginMetadata,const AtomicBool & aCanceled)8261 Result<UsageInfo, nsresult> QuotaClient::GetUsageForOrigin(
8262     PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
8263     const AtomicBool& aCanceled) {
8264   AssertIsOnIOThread();
8265   MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_DEFAULT);
8266 
8267   // We can't open the database at this point, since it can be already used
8268   // by the connection thread. Use the cached value instead.
8269 
8270   QuotaManager* quotaManager = QuotaManager::Get();
8271   MOZ_ASSERT(quotaManager);
8272 
8273   return quotaManager->GetUsageForClient(PERSISTENCE_TYPE_DEFAULT,
8274                                          aOriginMetadata, Client::LS);
8275 }
8276 
AboutToClearOrigins(const Nullable<PersistenceType> & aPersistenceType,const OriginScope & aOriginScope)8277 nsresult QuotaClient::AboutToClearOrigins(
8278     const Nullable<PersistenceType>& aPersistenceType,
8279     const OriginScope& aOriginScope) {
8280   AssertIsOnIOThread();
8281 
8282   // This method is not called when the clearing is triggered by the eviction
8283   // process. It's on purpose to avoid a problem with the origin access time
8284   // which can be described as follows:
8285   // When there's a storage pressure condition and quota manager starts
8286   // collecting origins for eviction, there can be an origin that hasn't been
8287   // touched for long time. However, the old implementation of local storage
8288   // could have touched the origin only recently and the new implementation
8289   // hasn't had a chance to create a new per origin database for it yet (the
8290   // data is still in the archive database), so the origin access time hasn't
8291   // been updated either. In the end, the origin would be evicted despite the
8292   // fact that there was recent local storage activity.
8293   // So this method clears the archived data and shadow database entries for
8294   // given origin scope, but only if it's a privacy-related origin clearing.
8295 
8296   if (!aPersistenceType.IsNull() &&
8297       aPersistenceType.Value() != PERSISTENCE_TYPE_DEFAULT) {
8298     return NS_OK;
8299   }
8300 
8301   const bool shadowWrites = gShadowWrites;
8302 
8303   QM_TRY_INSPECT(const auto& archivedOriginScope,
8304                  CreateArchivedOriginScope(aOriginScope));
8305 
8306   if (!gArchivedOrigins) {
8307     QM_TRY(LoadArchivedOrigins());
8308     MOZ_ASSERT(gArchivedOrigins);
8309   }
8310 
8311   const bool hasDataForRemoval =
8312       archivedOriginScope->HasMatches(gArchivedOrigins);
8313 
8314   QuotaManager* quotaManager = QuotaManager::Get();
8315   MOZ_ASSERT(quotaManager);
8316 
8317   const nsString& basePath = quotaManager->GetBasePath();
8318 
8319   {
8320     MutexAutoLock shadowDatabaseLock(mShadowDatabaseMutex);
8321 
8322     QM_TRY_INSPECT(
8323         const auto& connection,
8324         ([&basePath]() -> Result<nsCOMPtr<mozIStorageConnection>, nsresult> {
8325           if (gInitializedShadowStorage) {
8326             QM_TRY_RETURN(GetShadowStorageConnection(basePath));
8327           }
8328 
8329           QM_TRY_UNWRAP(auto connection,
8330                         CreateShadowStorageConnection(basePath));
8331 
8332           gInitializedShadowStorage = true;
8333 
8334           return connection;
8335         }()));
8336 
8337     if (hasDataForRemoval) {
8338       QM_TRY(AttachArchiveDatabase(quotaManager->GetStoragePath(), connection));
8339     }
8340 
8341     if (archivedOriginScope->IsPattern()) {
8342       nsCOMPtr<mozIStorageFunction> function(
8343           new MatchFunction(archivedOriginScope->GetPattern()));
8344 
8345       QM_TRY(connection->CreateFunction("match"_ns, 2, function));
8346     }
8347 
8348     {
8349       QM_TRY_INSPECT(
8350           const auto& stmt,
8351           MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageStatement>, connection,
8352                                      CreateStatement, "BEGIN IMMEDIATE;"_ns));
8353 
8354       QM_TRY(stmt->Execute());
8355     }
8356 
8357     if (shadowWrites) {
8358       QM_TRY(PerformDelete(connection, "main"_ns, archivedOriginScope.get()));
8359     }
8360 
8361     if (hasDataForRemoval) {
8362       QM_TRY(
8363           PerformDelete(connection, "archive"_ns, archivedOriginScope.get()));
8364     }
8365 
8366     {
8367       QM_TRY_INSPECT(
8368           const auto& stmt,
8369           MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageStatement>, connection,
8370                                      CreateStatement, "COMMIT;"_ns));
8371 
8372       QM_TRY(stmt->Execute());
8373     }
8374 
8375     if (archivedOriginScope->IsPattern()) {
8376       QM_TRY(connection->RemoveFunction("match"_ns));
8377     }
8378 
8379     if (hasDataForRemoval) {
8380       QM_TRY(DetachArchiveDatabase(connection));
8381 
8382       MOZ_ASSERT(gArchivedOrigins);
8383       MOZ_ASSERT(archivedOriginScope->HasMatches(gArchivedOrigins));
8384       archivedOriginScope->RemoveMatches(gArchivedOrigins);
8385     }
8386 
8387     QM_TRY(connection->Close());
8388   }
8389 
8390   if (aOriginScope.IsNull()) {
8391     QM_TRY_INSPECT(const auto& shadowFile, GetShadowFile(basePath));
8392 
8393     QM_TRY(shadowFile->Remove(false));
8394 
8395     gInitializedShadowStorage = false;
8396   }
8397 
8398   return NS_OK;
8399 }
8400 
OnOriginClearCompleted(PersistenceType aPersistenceType,const nsACString & aOrigin)8401 void QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
8402                                          const nsACString& aOrigin) {
8403   AssertIsOnIOThread();
8404 }
8405 
ReleaseIOThreadObjects()8406 void QuotaClient::ReleaseIOThreadObjects() {
8407   AssertIsOnIOThread();
8408 
8409   gInitializationInfo = nullptr;
8410 
8411   // Delete archived origins hashtable since QuotaManager clears the whole
8412   // storage directory including ls-archive.sqlite.
8413 
8414   gArchivedOrigins = nullptr;
8415 }
8416 
AbortOperationsForLocks(const DirectoryLockIdTable & aDirectoryLockIds)8417 void QuotaClient::AbortOperationsForLocks(
8418     const DirectoryLockIdTable& aDirectoryLockIds) {
8419   AssertIsOnBackgroundThread();
8420 
8421   // A PrepareDatastoreOp object could already acquire a directory lock for
8422   // the given origin. Its last step is creation of a Datastore object (which
8423   // will take ownership of the directory lock) and a PreparedDatastore object
8424   // which keeps the Datastore alive until a database actor is created.
8425   // We need to invalidate the PreparedDatastore object when it's created,
8426   // otherwise the Datastore object can block the origin clear operation for
8427   // long time. It's not a problem that we don't fail the PrepareDatastoreOp
8428   // immediatelly (avoiding the creation of the Datastore and PreparedDatastore
8429   // object). We will call RequestAllowToClose on the database actor once it's
8430   // created and the child actor will respond by sending AllowToClose which
8431   // will close the Datastore on the parent side (the closing releases the
8432   // directory lock).
8433 
8434   InvalidatePrepareDatastoreOpsMatching(
8435       [&aDirectoryLockIds](const auto& prepareDatastoreOp) {
8436         // Check if the PrepareDatastoreOp holds an acquired DirectoryLock.
8437         // Origin clearing can't be blocked by this PrepareDatastoreOp if there
8438         // is no acquired DirectoryLock. If there is an acquired DirectoryLock,
8439         // check if the table contains the lock for the PrepareDatastoreOp.
8440         return IsLockForObjectAcquiredAndContainedInLockTable(
8441             prepareDatastoreOp, aDirectoryLockIds);
8442       });
8443 
8444   if (gPrivateDatastores) {
8445     gPrivateDatastores->RemoveIf([&aDirectoryLockIds](const auto& iter) {
8446       const auto& privateDatastore = iter.Data();
8447 
8448       // The PrivateDatastore::mDatastore member is not cleared until the
8449       // PrivateDatastore is destroyed.
8450       const auto& datastore = privateDatastore->DatastoreRef();
8451 
8452       // If the PrivateDatastore exists then it must be registered in
8453       // Datastore::mHasLivePrivateDatastore as well. The Datastore must have
8454       // a DirectoryLock if there is a registered PrivateDatastore.
8455       return IsLockForObjectContainedInLockTable(datastore, aDirectoryLockIds);
8456     });
8457 
8458     if (!gPrivateDatastores->Count()) {
8459       gPrivateDatastores = nullptr;
8460     }
8461   }
8462 
8463   InvalidatePreparedDatastoresMatching([&aDirectoryLockIds](
8464                                            const auto& preparedDatastore) {
8465     // The PreparedDatastore::mDatastore member is not cleared until the
8466     // PreparedDatastore is destroyed.
8467     const auto& datastore = preparedDatastore.DatastoreRef();
8468 
8469     // If the PreparedDatastore exists then it must be registered in
8470     // Datastore::mPreparedDatastores as well. The Datastore must have a
8471     // DirectoryLock if there are registered PreparedDatastore objects.
8472     return IsLockForObjectContainedInLockTable(datastore, aDirectoryLockIds);
8473   });
8474 
8475   RequestAllowToCloseDatabasesMatching(
8476       [&aDirectoryLockIds](const auto& database) {
8477         const auto& maybeDatastore = database.MaybeDatastoreRef();
8478 
8479         // If the Database is registered in gLiveDatabases then it must have a
8480         // Datastore.
8481         MOZ_ASSERT(maybeDatastore.isSome());
8482 
8483         // If the Database is registered in gLiveDatabases then it must be
8484         // registered in Datastore::mDatabases as well. The Datastore must have
8485         // a DirectoryLock if there are registered Database objects.
8486         return IsLockForObjectContainedInLockTable(*maybeDatastore,
8487                                                    aDirectoryLockIds);
8488       });
8489 }
8490 
AbortOperationsForProcess(ContentParentId aContentParentId)8491 void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) {
8492   AssertIsOnBackgroundThread();
8493 
8494   RequestAllowToCloseDatabasesMatching(
8495       [&aContentParentId](const auto& database) {
8496         return database.IsOwnedByProcess(aContentParentId);
8497       });
8498 }
8499 
AbortAllOperations()8500 void QuotaClient::AbortAllOperations() {
8501   AssertIsOnBackgroundThread();
8502 
8503   InvalidatePrepareDatastoreOpsMatching([](const auto& prepareDatastoreOp) {
8504     return prepareDatastoreOp.MaybeDirectoryLockRef();
8505   });
8506 
8507   if (gPrivateDatastores) {
8508     gPrivateDatastores = nullptr;
8509   }
8510 
8511   InvalidatePreparedDatastoresMatching([](const auto&) { return true; });
8512 
8513   RequestAllowToCloseDatabasesMatching([](const auto&) { return true; });
8514 }
8515 
StartIdleMaintenance()8516 void QuotaClient::StartIdleMaintenance() { AssertIsOnBackgroundThread(); }
8517 
StopIdleMaintenance()8518 void QuotaClient::StopIdleMaintenance() { AssertIsOnBackgroundThread(); }
8519 
InitiateShutdown()8520 void QuotaClient::InitiateShutdown() {
8521   MOZ_ASSERT(!mShutdownRequested);
8522   mShutdownRequested = true;
8523 
8524   // gPrepareDatastoreOps are short lived objects running a state machine.
8525   // The shutdown flag is checked between states, so we don't have to notify
8526   // all the objects here.
8527   // Allocation of a new PrepareDatastoreOp object is prevented once the
8528   // shutdown flag is set.
8529   // When the last PrepareDatastoreOp finishes, the gPrepareDatastoreOps array
8530   // is destroyed.
8531 
8532   if (gPrivateDatastores) {
8533     gPrivateDatastores->Clear();
8534     gPrivateDatastores = nullptr;
8535   }
8536 
8537   if (gPreparedDatastores) {
8538     gPreparedDatastores->Clear();
8539     gPreparedDatastores = nullptr;
8540   }
8541 
8542   RequestAllowToCloseDatabasesMatching([](const auto&) { return true; });
8543 
8544   if (gPreparedObsevers) {
8545     gPreparedObsevers->Clear();
8546     gPreparedObsevers = nullptr;
8547   }
8548 }
8549 
IsShutdownCompleted() const8550 bool QuotaClient::IsShutdownCompleted() const {
8551   // Don't have to check gPrivateDatastores and gPreparedDatastores since we
8552   // nulled it out in InitiateShutdown.
8553   return !gPrepareDatastoreOps && !gDatastores && !gLiveDatabases;
8554 }
8555 
ForceKillActors()8556 void QuotaClient::ForceKillActors() { ForceKillAllDatabases(); }
8557 
GetShutdownStatus() const8558 nsCString QuotaClient::GetShutdownStatus() const {
8559   AssertIsOnBackgroundThread();
8560 
8561   nsCString data;
8562 
8563   if (gPrepareDatastoreOps) {
8564     data.Append("PrepareDatastoreOperations: ");
8565     data.AppendInt(static_cast<uint32_t>(gPrepareDatastoreOps->Length()));
8566     data.Append(" (");
8567 
8568     // XXX What's the purpose of adding these to a hashtable before joining them
8569     // to the string? (Maybe this used to be an ordered container before???)
8570     nsTHashSet<nsCString> ids;
8571     std::transform(gPrepareDatastoreOps->cbegin(), gPrepareDatastoreOps->cend(),
8572                    MakeInserter(ids), [](const auto& prepareDatastoreOp) {
8573                      nsCString id;
8574                      prepareDatastoreOp->Stringify(id);
8575                      return id;
8576                    });
8577 
8578     StringJoinAppend(data, ", "_ns, ids);
8579 
8580     data.Append(")\n");
8581   }
8582 
8583   if (gDatastores) {
8584     data.Append("Datastores: ");
8585     data.AppendInt(gDatastores->Count());
8586     data.Append(" (");
8587 
8588     // XXX It might be confusing to remove duplicates here, as the actual list
8589     // won't match the count then.
8590     nsTHashSet<nsCString> ids;
8591     std::transform(gDatastores->Values().cbegin(), gDatastores->Values().cend(),
8592                    MakeInserter(ids), [](const auto& entry) {
8593                      nsCString id;
8594                      entry->Stringify(id);
8595                      return id;
8596                    });
8597 
8598     StringJoinAppend(data, ", "_ns, ids);
8599 
8600     data.Append(")\n");
8601   }
8602 
8603   if (gLiveDatabases) {
8604     data.Append("LiveDatabases: ");
8605     data.AppendInt(static_cast<uint32_t>(gLiveDatabases->Length()));
8606     data.Append(" (");
8607 
8608     // XXX It might be confusing to remove duplicates here, as the actual list
8609     // won't match the count then.
8610     nsTHashSet<nsCString> ids;
8611     std::transform(gLiveDatabases->cbegin(), gLiveDatabases->cend(),
8612                    MakeInserter(ids), [](const auto& database) {
8613                      nsCString id;
8614                      database->Stringify(id);
8615                      return id;
8616                    });
8617 
8618     StringJoinAppend(data, ", "_ns, ids);
8619 
8620     data.Append(")\n");
8621   }
8622 
8623   return data;
8624 }
8625 
FinalizeShutdown()8626 void QuotaClient::FinalizeShutdown() {
8627   // And finally, shutdown the connection thread.
8628   if (gConnectionThread) {
8629     gConnectionThread->Shutdown();
8630 
8631     gConnectionThread = nullptr;
8632   }
8633 }
8634 
8635 Result<UniquePtr<ArchivedOriginScope>, nsresult>
CreateArchivedOriginScope(const OriginScope & aOriginScope)8636 QuotaClient::CreateArchivedOriginScope(const OriginScope& aOriginScope) {
8637   AssertIsOnIOThread();
8638 
8639   if (aOriginScope.IsOrigin()) {
8640     QM_TRY_INSPECT(const auto& principalInfo,
8641                    QuotaManager::ParseOrigin(aOriginScope.GetOrigin()));
8642 
8643     QM_TRY_INSPECT((const auto& [originAttrSuffix, originKey]),
8644                    GenerateOriginKey2(principalInfo));
8645 
8646     return ArchivedOriginScope::CreateFromOrigin(originAttrSuffix, originKey);
8647   }
8648 
8649   if (aOriginScope.IsPrefix()) {
8650     QM_TRY_INSPECT(const auto& principalInfo,
8651                    QuotaManager::ParseOrigin(aOriginScope.GetOriginNoSuffix()));
8652 
8653     QM_TRY_INSPECT((const auto& [originAttrSuffix, originKey]),
8654                    GenerateOriginKey2(principalInfo));
8655 
8656     Unused << originAttrSuffix;
8657 
8658     return ArchivedOriginScope::CreateFromPrefix(originKey);
8659   }
8660 
8661   if (aOriginScope.IsPattern()) {
8662     return ArchivedOriginScope::CreateFromPattern(aOriginScope.GetPattern());
8663   }
8664 
8665   MOZ_ASSERT(aOriginScope.IsNull());
8666 
8667   return ArchivedOriginScope::CreateFromNull();
8668 }
8669 
PerformDelete(mozIStorageConnection * aConnection,const nsACString & aSchemaName,ArchivedOriginScope * aArchivedOriginScope) const8670 nsresult QuotaClient::PerformDelete(
8671     mozIStorageConnection* aConnection, const nsACString& aSchemaName,
8672     ArchivedOriginScope* aArchivedOriginScope) const {
8673   AssertIsOnIOThread();
8674   MOZ_ASSERT(aConnection);
8675   MOZ_ASSERT(aArchivedOriginScope);
8676 
8677   QM_TRY_INSPECT(
8678       const auto& stmt,
8679       MOZ_TO_RESULT_INVOKE_TYPED(
8680           nsCOMPtr<mozIStorageStatement>, aConnection, CreateStatement,
8681           "DELETE FROM "_ns + aSchemaName + ".webappsstore2"_ns +
8682               aArchivedOriginScope->GetBindingClause() + ";"_ns));
8683 
8684   QM_TRY(aArchivedOriginScope->BindToStatement(stmt));
8685 
8686   QM_TRY(stmt->Execute());
8687 
8688   return NS_OK;
8689 }
8690 
8691 // static
Initialize()8692 nsresult QuotaClient::Observer::Initialize() {
8693   MOZ_ASSERT(NS_IsMainThread());
8694 
8695   RefPtr<Observer> observer = new Observer();
8696 
8697   QM_TRY(observer->Init());
8698 
8699   return NS_OK;
8700 }
8701 
Init()8702 nsresult QuotaClient::Observer::Init() {
8703   MOZ_ASSERT(NS_IsMainThread());
8704 
8705   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
8706   if (NS_WARN_IF(!obs)) {
8707     return NS_ERROR_FAILURE;
8708   }
8709 
8710   nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
8711   if (NS_WARN_IF(NS_FAILED(rv))) {
8712     return rv;
8713   }
8714 
8715   rv = obs->AddObserver(this, kPrivateBrowsingObserverTopic, false);
8716   if (NS_WARN_IF(NS_FAILED(rv))) {
8717     obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
8718     return rv;
8719   }
8720 
8721   return NS_OK;
8722 }
8723 
Shutdown()8724 nsresult QuotaClient::Observer::Shutdown() {
8725   MOZ_ASSERT(NS_IsMainThread());
8726 
8727   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
8728   if (NS_WARN_IF(!obs)) {
8729     return NS_ERROR_FAILURE;
8730   }
8731 
8732   MOZ_ALWAYS_SUCCEEDS(obs->RemoveObserver(this, kPrivateBrowsingObserverTopic));
8733   MOZ_ALWAYS_SUCCEEDS(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID));
8734 
8735   // In general, the instance will have died after the latter removal call, so
8736   // it's not safe to do anything after that point.
8737   // However, Shutdown is currently called from Observe which is called by the
8738   // Observer Service which holds a strong reference to the observer while the
8739   // Observe method is being called.
8740 
8741   return NS_OK;
8742 }
8743 
NS_IMPL_ISUPPORTS(QuotaClient::Observer,nsIObserver)8744 NS_IMPL_ISUPPORTS(QuotaClient::Observer, nsIObserver)
8745 
8746 NS_IMETHODIMP
8747 QuotaClient::Observer::Observe(nsISupports* aSubject, const char* aTopic,
8748                                const char16_t* aData) {
8749   MOZ_ASSERT(NS_IsMainThread());
8750 
8751   if (!strcmp(aTopic, kPrivateBrowsingObserverTopic)) {
8752     PBackgroundChild* const backgroundActor =
8753         BackgroundChild::GetOrCreateForCurrentThread();
8754     QM_TRY(OkIf(backgroundActor), NS_ERROR_FAILURE);
8755 
8756     QM_TRY(OkIf(backgroundActor->SendLSClearPrivateBrowsing()),
8757            NS_ERROR_FAILURE);
8758 
8759     return NS_OK;
8760   }
8761 
8762   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
8763     QM_TRY(Shutdown());
8764 
8765     return NS_OK;
8766   }
8767 
8768   NS_WARNING("Unknown observer topic!");
8769   return NS_OK;
8770 }
8771 
NS_IMPL_ISUPPORTS(QuotaClient::MatchFunction,mozIStorageFunction)8772 NS_IMPL_ISUPPORTS(QuotaClient::MatchFunction, mozIStorageFunction)
8773 
8774 NS_IMETHODIMP
8775 QuotaClient::MatchFunction::OnFunctionCall(
8776     mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult) {
8777   AssertIsOnIOThread();
8778   MOZ_ASSERT(aFunctionArguments);
8779   MOZ_ASSERT(aResult);
8780 
8781   QM_TRY_INSPECT(const auto& suffix,
8782                  MOZ_TO_RESULT_INVOKE_TYPED(nsAutoCString, aFunctionArguments,
8783                                             GetUTF8String, 1));
8784 
8785   OriginAttributes oa;
8786   QM_TRY(OkIf(oa.PopulateFromSuffix(suffix)), NS_ERROR_FAILURE);
8787 
8788   const bool result = mPattern.Matches(oa);
8789 
8790   RefPtr<nsVariant> outVar(new nsVariant());
8791   QM_TRY(outVar->SetAsBool(result));
8792 
8793   outVar.forget(aResult);
8794   return NS_OK;
8795 }
8796 
8797 /*******************************************************************************
8798  * AutoWriteTransaction
8799  ******************************************************************************/
8800 
AutoWriteTransaction(bool aShadowWrites)8801 AutoWriteTransaction::AutoWriteTransaction(bool aShadowWrites)
8802     : mConnection(nullptr), mShadowWrites(aShadowWrites) {
8803   AssertIsOnGlobalConnectionThread();
8804 
8805   MOZ_COUNT_CTOR(mozilla::dom::AutoWriteTransaction);
8806 }
8807 
~AutoWriteTransaction()8808 AutoWriteTransaction::~AutoWriteTransaction() {
8809   AssertIsOnGlobalConnectionThread();
8810 
8811   MOZ_COUNT_DTOR(mozilla::dom::AutoWriteTransaction);
8812 
8813   if (mConnection) {
8814     QM_WARNONLY_TRY(mConnection->RollbackWriteTransaction());
8815 
8816     if (mShadowWrites) {
8817       QM_WARNONLY_TRY(DetachShadowDatabaseAndUnlock());
8818     }
8819   }
8820 }
8821 
Start(Connection * aConnection)8822 nsresult AutoWriteTransaction::Start(Connection* aConnection) {
8823   AssertIsOnGlobalConnectionThread();
8824   MOZ_ASSERT(aConnection);
8825   MOZ_ASSERT(!mConnection);
8826 
8827   if (mShadowWrites) {
8828     QM_TRY(LockAndAttachShadowDatabase(aConnection));
8829   }
8830 
8831   QM_TRY(aConnection->BeginWriteTransaction());
8832 
8833   mConnection = aConnection;
8834 
8835   return NS_OK;
8836 }
8837 
Commit()8838 nsresult AutoWriteTransaction::Commit() {
8839   AssertIsOnGlobalConnectionThread();
8840   MOZ_ASSERT(mConnection);
8841 
8842   QM_TRY(mConnection->CommitWriteTransaction());
8843 
8844   if (mShadowWrites) {
8845     QM_TRY(DetachShadowDatabaseAndUnlock());
8846   }
8847 
8848   mConnection = nullptr;
8849 
8850   return NS_OK;
8851 }
8852 
LockAndAttachShadowDatabase(Connection * aConnection)8853 nsresult AutoWriteTransaction::LockAndAttachShadowDatabase(
8854     Connection* aConnection) {
8855   AssertIsOnGlobalConnectionThread();
8856   MOZ_ASSERT(aConnection);
8857   MOZ_ASSERT(!mConnection);
8858   MOZ_ASSERT(mShadowDatabaseLock.isNothing());
8859   MOZ_ASSERT(mShadowWrites);
8860 
8861   QuotaManager* quotaManager = QuotaManager::Get();
8862   MOZ_ASSERT(quotaManager);
8863 
8864   mShadowDatabaseLock.emplace(
8865       aConnection->GetQuotaClient()->ShadowDatabaseMutex());
8866 
8867   QM_TRY(AttachShadowDatabase(quotaManager->GetBasePath(),
8868                               &aConnection->MutableStorageConnection()));
8869 
8870   return NS_OK;
8871 }
8872 
DetachShadowDatabaseAndUnlock()8873 nsresult AutoWriteTransaction::DetachShadowDatabaseAndUnlock() {
8874   AssertIsOnGlobalConnectionThread();
8875   MOZ_ASSERT(mConnection);
8876   MOZ_ASSERT(mShadowDatabaseLock.isSome());
8877   MOZ_ASSERT(mShadowWrites);
8878 
8879   nsCOMPtr<mozIStorageConnection> storageConnection =
8880       mConnection->StorageConnection();
8881   MOZ_ASSERT(storageConnection);
8882 
8883   QM_TRY(DetachShadowDatabase(storageConnection));
8884 
8885   mShadowDatabaseLock.reset();
8886 
8887   return NS_OK;
8888 }
8889 
8890 }  // namespace mozilla::dom
8891