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(¤t)) &&
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(¤t)) && 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(["aObject] {
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