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