Source code

Revision control

Other Tools

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 "mozIStorageConnection.h"
10
#include "mozIStorageService.h"
11
#include "mozIThirdPartyUtil.h"
12
#include "nsIObjectInputStream.h"
13
#include "nsIObjectOutputStream.h"
14
#include "nsIFile.h"
15
#include "nsIFileStreams.h"
16
#include "nsIObserverService.h"
17
#include "nsIPermissionManager.h"
18
#include "nsIPlatformInfo.h"
19
#include "nsIPrincipal.h"
20
#include "nsIRunnable.h"
21
#include "nsISimpleEnumerator.h"
22
#include "nsIScriptObjectPrincipal.h"
23
#include "nsIScriptSecurityManager.h"
24
#include "nsISupportsPrimitives.h"
25
#include "nsITimer.h"
26
#include "nsIURI.h"
27
#include "nsPIDOMWindow.h"
28
29
#include <algorithm>
30
#include "GeckoProfiler.h"
31
#include "mozilla/Atomics.h"
32
#include "mozilla/AutoRestore.h"
33
#include "mozilla/BasePrincipal.h"
34
#include "mozilla/CondVar.h"
35
#include "mozilla/Telemetry.h"
36
#include "mozilla/dom/PContent.h"
37
#include "mozilla/dom/cache/QuotaClient.h"
38
#include "mozilla/dom/indexedDB/ActorsParent.h"
39
#include "mozilla/dom/localstorage/ActorsParent.h"
40
#include "mozilla/dom/quota/PQuotaParent.h"
41
#include "mozilla/dom/quota/PQuotaRequestParent.h"
42
#include "mozilla/dom/quota/PQuotaUsageRequestParent.h"
43
#include "mozilla/dom/simpledb/ActorsParent.h"
44
#include "mozilla/dom/StorageActivityService.h"
45
#include "mozilla/dom/StorageDBUpdater.h"
46
#include "mozilla/ipc/BackgroundChild.h"
47
#include "mozilla/ipc/BackgroundParent.h"
48
#include "mozilla/ipc/BackgroundUtils.h"
49
#include "mozilla/ipc/PBackgroundChild.h"
50
#include "mozilla/net/MozURL.h"
51
#include "mozilla/Mutex.h"
52
#include "mozilla/Preferences.h"
53
#include "mozilla/Services.h"
54
#include "mozilla/StaticPrefs_dom.h"
55
#include "mozilla/StaticPtr.h"
56
#include "mozilla/TextUtils.h"
57
#include "mozilla/Telemetry.h"
58
#include "mozilla/TypeTraits.h"
59
#include "mozilla/Unused.h"
60
#include "mozStorageCID.h"
61
#include "mozStorageHelper.h"
62
#include "nsAppDirectoryServiceDefs.h"
63
#include "nsCharSeparatedTokenizer.h"
64
#include "nsComponentManagerUtils.h"
65
#include "nsAboutProtocolUtils.h"
66
#include "nsCharSeparatedTokenizer.h"
67
#include "nsContentUtils.h"
68
#include "nsCRTGlue.h"
69
#include "nsDirectoryServiceUtils.h"
70
#include "nsEscape.h"
71
#include "nsNetUtil.h"
72
#include "nsPrintfCString.h"
73
#include "nsScriptSecurityManager.h"
74
#include "nsThreadUtils.h"
75
#include "nsXULAppAPI.h"
76
#include "prio.h"
77
#include "xpcpublic.h"
78
79
#include "OriginScope.h"
80
#include "QuotaManager.h"
81
#include "QuotaManagerService.h"
82
#include "QuotaObject.h"
83
#include "UsageInfo.h"
84
85
#define DISABLE_ASSERTS_FOR_FUZZING 0
86
87
#if DISABLE_ASSERTS_FOR_FUZZING
88
# define ASSERT_UNLESS_FUZZING(...) \
89
do { \
90
} while (0)
91
#else
92
# define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
93
#endif
94
95
#define QM_LOG_TEST() MOZ_LOG_TEST(GetQuotaManagerLogger(), LogLevel::Info)
96
#define QM_LOG(_args) MOZ_LOG(GetQuotaManagerLogger(), LogLevel::Info, _args)
97
98
#define UNKNOWN_FILE_WARNING(_leafName) \
99
QM_WARNING("Something (%s) in the directory that doesn't belong!", \
100
NS_ConvertUTF16toUTF8(_leafName).get())
101
102
// The amount of time, in milliseconds, that our IO thread will stay alive
103
// after the last event it processes.
104
#define DEFAULT_THREAD_TIMEOUT_MS 30000
105
106
// The amount of time, in milliseconds, that we will wait for active storage
107
// transactions on shutdown before aborting them.
108
#define DEFAULT_SHUTDOWN_TIMER_MS 30000
109
110
// profile-before-change, when we need to shut down quota manager
111
#define PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID "profile-before-change-qm"
112
113
#define KB *1024ULL
114
#define MB *1024ULL KB
115
#define GB *1024ULL MB
116
117
namespace mozilla {
118
namespace dom {
119
namespace quota {
120
121
using namespace mozilla::ipc;
122
using mozilla::net::MozURL;
123
124
// We want profiles to be platform-independent so we always need to replace
125
// the same characters on every platform. Windows has the most extensive set
126
// of illegal characters so we use its FILE_ILLEGAL_CHARACTERS and
127
// FILE_PATH_SEPARATOR.
128
const char QuotaManager::kReplaceChars[] = CONTROL_CHARACTERS "/:*?\"<>|\\";
129
130
namespace {
131
132
template <typename T>
133
void AssertNoOverflow(uint64_t aDest, T aArg);
134
135
/*******************************************************************************
136
* Constants
137
******************************************************************************/
138
139
const uint32_t kSQLitePageSizeOverride = 512;
140
141
const uint32_t kHackyDowngradeMajorStorageVersion = 2;
142
const uint32_t kHackyDowngradeMinorStorageVersion = 1;
143
144
// Important version history:
145
// - Bug 1290481 bumped our schema from major.minor 2.0 to 3.0 in Firefox 57
146
// which caused Firefox 57 release concerns because the major schema upgrade
147
// means anyone downgrading to Firefox 56 will experience a non-operational
148
// QuotaManager and all of its clients.
149
// - Bug 1404344 got very concerned about that and so we decided to effectively
150
// rename 3.0 to 2.1, effective in Firefox 57. This works because post
151
// storage.sqlite v1.0, QuotaManager doesn't care about minor storage version
152
// increases. It also works because all the upgrade did was give the DOM
153
// Cache API QuotaClient an opportunity to create its newly added .padding
154
// files during initialization/upgrade, which isn't functionally necessary as
155
// that can be done on demand.
156
157
// Major storage version. Bump for backwards-incompatible changes.
158
// (The next major version should be 4 to distinguish from the Bug 1290481
159
// downgrade snafu.)
160
const uint32_t kMajorStorageVersion = 2;
161
162
// Minor storage version. Bump for backwards-compatible changes.
163
const uint32_t kMinorStorageVersion = 3;
164
165
// The storage version we store in the SQLite database is a (signed) 32-bit
166
// integer. The major version is left-shifted 16 bits so the max value is
167
// 0xFFFF. The minor version occupies the lower 16 bits and its max is 0xFFFF.
168
static_assert(kMajorStorageVersion <= 0xFFFF,
169
"Major version needs to fit in 16 bits.");
170
static_assert(kMinorStorageVersion <= 0xFFFF,
171
"Minor version needs to fit in 16 bits.");
172
173
const int32_t kStorageVersion =
174
int32_t((kMajorStorageVersion << 16) + kMinorStorageVersion);
175
176
// See comments above about why these are a thing.
177
const int32_t kHackyPreDowngradeStorageVersion = int32_t((3 << 16) + 0);
178
const int32_t kHackyPostDowngradeStorageVersion = int32_t((2 << 16) + 1);
179
180
static_assert(static_cast<uint32_t>(StorageType::Persistent) ==
181
static_cast<uint32_t>(PERSISTENCE_TYPE_PERSISTENT),
182
"Enum values should match.");
183
184
static_assert(static_cast<uint32_t>(StorageType::Temporary) ==
185
static_cast<uint32_t>(PERSISTENCE_TYPE_TEMPORARY),
186
"Enum values should match.");
187
188
static_assert(static_cast<uint32_t>(StorageType::Default) ==
189
static_cast<uint32_t>(PERSISTENCE_TYPE_DEFAULT),
190
"Enum values should match.");
191
192
const char kChromeOrigin[] = "chrome";
193
const char kAboutHomeOriginPrefix[] = "moz-safe-about:home";
194
const char kIndexedDBOriginPrefix[] = "indexeddb://";
195
const char kResourceOriginPrefix[] = "resource://";
196
197
#define INDEXEDDB_DIRECTORY_NAME "indexedDB"
198
#define STORAGE_DIRECTORY_NAME "storage"
199
#define PERSISTENT_DIRECTORY_NAME "persistent"
200
#define PERMANENT_DIRECTORY_NAME "permanent"
201
#define TEMPORARY_DIRECTORY_NAME "temporary"
202
#define DEFAULT_DIRECTORY_NAME "default"
203
204
#define STORAGE_FILE_NAME "storage.sqlite"
205
206
// The name of the file that we use to load/save the last access time of an
207
// origin.
208
// XXX We should get rid of old metadata files at some point, bug 1343576.
209
#define METADATA_FILE_NAME ".metadata"
210
#define METADATA_TMP_FILE_NAME ".metadata-tmp"
211
#define METADATA_V2_FILE_NAME ".metadata-v2"
212
#define METADATA_V2_TMP_FILE_NAME ".metadata-v2-tmp"
213
214
#define WEB_APPS_STORE_FILE_NAME "webappsstore.sqlite"
215
#define LS_ARCHIVE_FILE_NAME "ls-archive.sqlite"
216
#define LS_ARCHIVE_TMP_FILE_NAME "ls-archive-tmp.sqlite"
217
218
const uint32_t kLocalStorageArchiveVersion = 4;
219
220
const char kProfileDoChangeTopic[] = "profile-do-change";
221
222
const int32_t kCacheVersion = 1;
223
224
/******************************************************************************
225
* SQLite functions
226
******************************************************************************/
227
228
int32_t MakeStorageVersion(uint32_t aMajorStorageVersion,
229
uint32_t aMinorStorageVersion) {
230
return int32_t((aMajorStorageVersion << 16) + aMinorStorageVersion);
231
}
232
233
uint32_t GetMajorStorageVersion(int32_t aStorageVersion) {
234
return uint32_t(aStorageVersion >> 16);
235
}
236
237
nsresult CreateTables(mozIStorageConnection* aConnection) {
238
AssertIsOnIOThread();
239
MOZ_ASSERT(aConnection);
240
241
// Table `database`
242
nsresult rv = aConnection->ExecuteSimpleSQL(
243
NS_LITERAL_CSTRING("CREATE TABLE database"
244
"( cache_version INTEGER NOT NULL DEFAULT 0"
245
");"));
246
if (NS_WARN_IF(NS_FAILED(rv))) {
247
return rv;
248
}
249
250
#ifdef DEBUG
251
{
252
int32_t storageVersion;
253
rv = aConnection->GetSchemaVersion(&storageVersion);
254
if (NS_WARN_IF(NS_FAILED(rv))) {
255
return rv;
256
}
257
258
MOZ_ASSERT(storageVersion == 0);
259
}
260
#endif
261
262
rv = aConnection->SetSchemaVersion(kStorageVersion);
263
if (NS_WARN_IF(NS_FAILED(rv))) {
264
return rv;
265
}
266
267
return NS_OK;
268
}
269
270
nsresult LoadCacheVersion(mozIStorageConnection* aConnection,
271
int32_t& aVersion) {
272
AssertIsOnIOThread();
273
MOZ_ASSERT(aConnection);
274
275
nsCOMPtr<mozIStorageStatement> stmt;
276
nsresult rv = aConnection->CreateStatement(
277
NS_LITERAL_CSTRING("SELECT cache_version FROM database"),
278
getter_AddRefs(stmt));
279
if (NS_WARN_IF(NS_FAILED(rv))) {
280
return rv;
281
}
282
283
bool hasResult;
284
rv = stmt->ExecuteStep(&hasResult);
285
if (NS_WARN_IF(NS_FAILED(rv))) {
286
return rv;
287
}
288
289
if (NS_WARN_IF(!hasResult)) {
290
return NS_ERROR_FILE_CORRUPTED;
291
}
292
293
int32_t version;
294
rv = stmt->GetInt32(0, &version);
295
if (NS_WARN_IF(NS_FAILED(rv))) {
296
return rv;
297
}
298
299
aVersion = version;
300
return NS_OK;
301
}
302
303
nsresult SaveCacheVersion(mozIStorageConnection* aConnection,
304
int32_t aVersion) {
305
AssertIsOnIOThread();
306
MOZ_ASSERT(aConnection);
307
308
nsCOMPtr<mozIStorageStatement> stmt;
309
nsresult rv = aConnection->CreateStatement(
310
NS_LITERAL_CSTRING("UPDATE database SET cache_version = :version;"),
311
getter_AddRefs(stmt));
312
if (NS_WARN_IF(NS_FAILED(rv))) {
313
return rv;
314
}
315
316
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("version"), aVersion);
317
if (NS_WARN_IF(NS_FAILED(rv))) {
318
return rv;
319
}
320
321
rv = stmt->Execute();
322
if (NS_WARN_IF(NS_FAILED(rv))) {
323
return rv;
324
}
325
326
return NS_OK;
327
}
328
329
nsresult CreateCacheTables(mozIStorageConnection* aConnection) {
330
AssertIsOnIOThread();
331
MOZ_ASSERT(aConnection);
332
333
// Table `cache`
334
nsresult rv = aConnection->ExecuteSimpleSQL(
335
NS_LITERAL_CSTRING("CREATE TABLE cache"
336
"( valid INTEGER NOT NULL DEFAULT 0"
337
", build_id TEXT NOT NULL DEFAULT ''"
338
");"));
339
if (NS_WARN_IF(NS_FAILED(rv))) {
340
return rv;
341
}
342
343
// Table `repository`
344
rv = aConnection->ExecuteSimpleSQL(
345
NS_LITERAL_CSTRING("CREATE TABLE repository"
346
"( id INTEGER PRIMARY KEY"
347
", name TEXT NOT NULL"
348
");"));
349
if (NS_WARN_IF(NS_FAILED(rv))) {
350
return rv;
351
}
352
353
// Table `origin`
354
rv = aConnection->ExecuteSimpleSQL(
355
NS_LITERAL_CSTRING("CREATE TABLE origin"
356
"( repository_id INTEGER NOT NULL"
357
", origin TEXT NOT NULL"
358
", group_ TEXT NOT NULL"
359
", client_usages TEXT NOT NULL"
360
", usage INTEGER NOT NULL"
361
", last_access_time INTEGER NOT NULL"
362
", accessed INTEGER NOT NULL"
363
", persisted INTEGER NOT NULL"
364
", PRIMARY KEY (repository_id, origin)"
365
", FOREIGN KEY (repository_id) "
366
"REFERENCES repository(id) "
367
");"));
368
if (NS_WARN_IF(NS_FAILED(rv))) {
369
return rv;
370
}
371
372
#ifdef DEBUG
373
{
374
int32_t cacheVersion;
375
rv = LoadCacheVersion(aConnection, cacheVersion);
376
if (NS_WARN_IF(NS_FAILED(rv))) {
377
return rv;
378
}
379
380
MOZ_ASSERT(cacheVersion == 0);
381
}
382
#endif
383
384
rv = SaveCacheVersion(aConnection, kCacheVersion);
385
if (NS_WARN_IF(NS_FAILED(rv))) {
386
return rv;
387
}
388
389
return NS_OK;
390
}
391
392
/*
393
nsresult UpgradeCacheFrom1To2(mozIStorageConnection* aConnection) {
394
AssertIsOnIOThread();
395
MOZ_ASSERT(aConnection);
396
397
nsresult rv;
398
399
#ifdef DEBUG
400
{
401
int32_t cacheVersion;
402
rv = LoadCacheVersion(aConnection, cacheVersion);
403
if (NS_WARN_IF(NS_FAILED(rv))) {
404
return rv;
405
}
406
407
MOZ_ASSERT(cacheVersion == 1);
408
}
409
#endif
410
411
rv = SaveCacheVersion(aConnection, 2);
412
if (NS_WARN_IF(NS_FAILED(rv))) {
413
return rv;
414
}
415
416
return NS_OK;
417
}
418
*/
419
420
nsresult InvalidateCache(mozIStorageConnection* aConnection) {
421
AssertIsOnIOThread();
422
MOZ_ASSERT(aConnection);
423
424
mozStorageTransaction transaction(
425
aConnection, false, mozIStorageConnection::TRANSACTION_IMMEDIATE);
426
427
nsresult rv =
428
aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM origin;"));
429
if (NS_WARN_IF(NS_FAILED(rv))) {
430
return rv;
431
}
432
433
rv = aConnection->ExecuteSimpleSQL(
434
NS_LITERAL_CSTRING("UPDATE cache SET valid = 0"));
435
if (NS_WARN_IF(NS_FAILED(rv))) {
436
return rv;
437
}
438
439
rv = transaction.Commit();
440
if (NS_WARN_IF(NS_FAILED(rv))) {
441
return rv;
442
}
443
444
return NS_OK;
445
}
446
447
nsresult CreateWebAppsStoreConnection(nsIFile* aWebAppsStoreFile,
448
mozIStorageService* aStorageService,
449
mozIStorageConnection** aConnection) {
450
AssertIsOnIOThread();
451
MOZ_ASSERT(aWebAppsStoreFile);
452
MOZ_ASSERT(aStorageService);
453
MOZ_ASSERT(aConnection);
454
455
// Check if the old database exists at all.
456
bool exists;
457
nsresult rv = aWebAppsStoreFile->Exists(&exists);
458
if (NS_WARN_IF(NS_FAILED(rv))) {
459
return rv;
460
}
461
462
if (!exists) {
463
// webappsstore.sqlite doesn't exist, return a null connection.
464
*aConnection = nullptr;
465
return NS_OK;
466
}
467
468
bool isDirectory;
469
rv = aWebAppsStoreFile->IsDirectory(&isDirectory);
470
if (NS_WARN_IF(NS_FAILED(rv))) {
471
return rv;
472
}
473
474
if (isDirectory) {
475
QM_WARNING("webappsstore.sqlite is not a file!");
476
*aConnection = nullptr;
477
return NS_OK;
478
}
479
480
nsCOMPtr<mozIStorageConnection> connection;
481
rv = aStorageService->OpenUnsharedDatabase(aWebAppsStoreFile,
482
getter_AddRefs(connection));
483
if (rv == NS_ERROR_FILE_CORRUPTED) {
484
// Don't throw an error, leave a corrupted webappsstore database as it is.
485
*aConnection = nullptr;
486
return NS_OK;
487
}
488
if (NS_WARN_IF(NS_FAILED(rv))) {
489
return rv;
490
}
491
492
rv = StorageDBUpdater::Update(connection);
493
if (NS_FAILED(rv)) {
494
// Don't throw an error, leave a non-updateable webappsstore database as
495
// it is.
496
*aConnection = nullptr;
497
return NS_OK;
498
}
499
500
connection.forget(aConnection);
501
return NS_OK;
502
}
503
504
nsresult GetLocalStorageArchiveFile(const nsAString& aDirectoryPath,
505
nsIFile** aLsArchiveFile) {
506
AssertIsOnIOThread();
507
MOZ_ASSERT(!aDirectoryPath.IsEmpty());
508
MOZ_ASSERT(aLsArchiveFile);
509
510
nsCOMPtr<nsIFile> lsArchiveFile;
511
nsresult rv =
512
NS_NewLocalFile(aDirectoryPath, false, getter_AddRefs(lsArchiveFile));
513
if (NS_WARN_IF(NS_FAILED(rv))) {
514
return rv;
515
}
516
517
rv = lsArchiveFile->Append(NS_LITERAL_STRING(LS_ARCHIVE_FILE_NAME));
518
if (NS_WARN_IF(NS_FAILED(rv))) {
519
return rv;
520
}
521
522
lsArchiveFile.forget(aLsArchiveFile);
523
return NS_OK;
524
}
525
526
nsresult GetLocalStorageArchiveTmpFile(const nsAString& aDirectoryPath,
527
nsIFile** aLsArchiveTmpFile) {
528
AssertIsOnIOThread();
529
MOZ_ASSERT(!aDirectoryPath.IsEmpty());
530
MOZ_ASSERT(aLsArchiveTmpFile);
531
532
nsCOMPtr<nsIFile> lsArchiveTmpFile;
533
nsresult rv =
534
NS_NewLocalFile(aDirectoryPath, false, getter_AddRefs(lsArchiveTmpFile));
535
if (NS_WARN_IF(NS_FAILED(rv))) {
536
return rv;
537
}
538
539
rv = lsArchiveTmpFile->Append(NS_LITERAL_STRING(LS_ARCHIVE_TMP_FILE_NAME));
540
if (NS_WARN_IF(NS_FAILED(rv))) {
541
return rv;
542
}
543
544
lsArchiveTmpFile.forget(aLsArchiveTmpFile);
545
return NS_OK;
546
}
547
548
nsresult InitializeLocalStorageArchive(mozIStorageConnection* aConnection,
549
uint32_t aVersion) {
550
AssertIsOnIOThread();
551
MOZ_ASSERT(aConnection);
552
553
nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
554
"CREATE TABLE database(version INTEGER NOT NULL DEFAULT 0);"));
555
if (NS_WARN_IF(NS_FAILED(rv))) {
556
return rv;
557
}
558
559
nsCOMPtr<mozIStorageStatement> stmt;
560
rv = aConnection->CreateStatement(
561
NS_LITERAL_CSTRING("INSERT INTO database (version) VALUES (:version)"),
562
getter_AddRefs(stmt));
563
if (NS_WARN_IF(NS_FAILED(rv))) {
564
return rv;
565
}
566
567
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("version"), aVersion);
568
if (NS_WARN_IF(NS_FAILED(rv))) {
569
return rv;
570
}
571
572
rv = stmt->Execute();
573
if (NS_WARN_IF(NS_FAILED(rv))) {
574
return rv;
575
}
576
577
return NS_OK;
578
}
579
580
nsresult IsLocalStorageArchiveInitialized(mozIStorageConnection* aConnection,
581
bool& aInitialized) {
582
AssertIsOnIOThread();
583
MOZ_ASSERT(aConnection);
584
585
bool exists;
586
nsresult rv =
587
aConnection->TableExists(NS_LITERAL_CSTRING("database"), &exists);
588
if (NS_WARN_IF(NS_FAILED(rv))) {
589
return rv;
590
}
591
592
aInitialized = exists;
593
return NS_OK;
594
}
595
596
nsresult LoadLocalStorageArchiveVersion(mozIStorageConnection* aConnection,
597
uint32_t& aVersion) {
598
AssertIsOnIOThread();
599
MOZ_ASSERT(aConnection);
600
601
nsCOMPtr<mozIStorageStatement> stmt;
602
nsresult rv = aConnection->CreateStatement(
603
NS_LITERAL_CSTRING("SELECT version FROM database"), getter_AddRefs(stmt));
604
if (NS_WARN_IF(NS_FAILED(rv))) {
605
return rv;
606
}
607
608
bool hasResult;
609
rv = stmt->ExecuteStep(&hasResult);
610
if (NS_WARN_IF(NS_FAILED(rv))) {
611
return rv;
612
}
613
614
if (NS_WARN_IF(!hasResult)) {
615
return NS_ERROR_FILE_CORRUPTED;
616
}
617
618
int32_t version;
619
rv = stmt->GetInt32(0, &version);
620
if (NS_WARN_IF(NS_FAILED(rv))) {
621
return rv;
622
}
623
624
aVersion = version;
625
return NS_OK;
626
}
627
628
/*
629
nsresult SaveLocalStorageArchiveVersion(mozIStorageConnection* aConnection,
630
uint32_t aVersion) {
631
AssertIsOnIOThread();
632
MOZ_ASSERT(aConnection);
633
634
nsCOMPtr<mozIStorageStatement> stmt;
635
nsresult rv = aConnection->CreateStatement(
636
NS_LITERAL_CSTRING("UPDATE database SET version = :version;"),
637
getter_AddRefs(stmt));
638
if (NS_WARN_IF(NS_FAILED(rv))) {
639
return rv;
640
}
641
642
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("version"), aVersion);
643
if (NS_WARN_IF(NS_FAILED(rv))) {
644
return rv;
645
}
646
647
rv = stmt->Execute();
648
if (NS_WARN_IF(NS_FAILED(rv))) {
649
return rv;
650
}
651
652
return NS_OK;
653
}
654
*/
655
656
/******************************************************************************
657
* Quota manager class declarations
658
******************************************************************************/
659
660
} // namespace
661
662
class DirectoryLockImpl final : public DirectoryLock {
663
RefPtr<QuotaManager> mQuotaManager;
664
665
const Nullable<PersistenceType> mPersistenceType;
666
const nsCString mGroup;
667
const OriginScope mOriginScope;
668
const Nullable<Client::Type> mClientType;
669
RefPtr<OpenDirectoryListener> mOpenListener;
670
671
nsTArray<DirectoryLockImpl*> mBlocking;
672
nsTArray<DirectoryLockImpl*> mBlockedOn;
673
674
const bool mExclusive;
675
676
// Internal quota manager operations use this flag to prevent directory lock
677
// registraction/unregistration from updating origin access time, etc.
678
const bool mInternal;
679
680
bool mRegistered;
681
bool mInvalidated;
682
683
public:
684
DirectoryLockImpl(QuotaManager* aQuotaManager,
685
const Nullable<PersistenceType>& aPersistenceType,
686
const nsACString& aGroup, const OriginScope& aOriginScope,
687
const Nullable<Client::Type>& aClientType, bool aExclusive,
688
bool aInternal, OpenDirectoryListener* aOpenListener);
689
690
void AssertIsOnOwningThread() const
691
#ifdef DEBUG
692
;
693
#else
694
{
695
}
696
#endif
697
698
const Nullable<PersistenceType>& GetPersistenceType() const {
699
return mPersistenceType;
700
}
701
702
const nsACString& GetGroup() const { return mGroup; }
703
704
const OriginScope& GetOriginScope() const { return mOriginScope; }
705
706
const Nullable<Client::Type>& GetClientType() const { return mClientType; }
707
708
bool IsInternal() const { return mInternal; }
709
710
void SetRegistered(bool aRegistered) { mRegistered = aRegistered; }
711
712
bool ShouldUpdateLockTable() {
713
return !mInternal &&
714
mPersistenceType.Value() != PERSISTENCE_TYPE_PERSISTENT;
715
}
716
717
bool Overlaps(const DirectoryLockImpl& aLock) const;
718
719
// Test whether this DirectoryLock needs to wait for the given lock.
720
bool MustWaitFor(const DirectoryLockImpl& aLock) const;
721
722
void AddBlockingLock(DirectoryLockImpl* aLock) {
723
AssertIsOnOwningThread();
724
725
mBlocking.AppendElement(aLock);
726
}
727
728
const nsTArray<DirectoryLockImpl*>& GetBlockedOnLocks() { return mBlockedOn; }
729
730
void AddBlockedOnLock(DirectoryLockImpl* aLock) {
731
AssertIsOnOwningThread();
732
733
mBlockedOn.AppendElement(aLock);
734
}
735
736
void MaybeUnblock(DirectoryLockImpl* aLock) {
737
AssertIsOnOwningThread();
738
739
mBlockedOn.RemoveElement(aLock);
740
if (mBlockedOn.IsEmpty()) {
741
NotifyOpenListener();
742
}
743
}
744
745
void NotifyOpenListener();
746
747
void Invalidate() {
748
AssertIsOnOwningThread();
749
750
mInvalidated = true;
751
}
752
753
NS_INLINE_DECL_REFCOUNTING(DirectoryLockImpl, override)
754
755
already_AddRefed<DirectoryLock> Specialize(PersistenceType aPersistenceType,
756
const nsACString& aGroup,
757
const nsACString& aOrigin,
758
Client::Type aClientType) const;
759
760
void Log() const;
761
762
private:
763
~DirectoryLockImpl();
764
};
765
766
const DirectoryLockImpl* GetDirectoryLockImpl(
767
const DirectoryLock* aDirectoryLock) {
768
MOZ_ASSERT(aDirectoryLock);
769
770
return static_cast<const DirectoryLockImpl*>(aDirectoryLock);
771
}
772
773
class QuotaManager::Observer final : public nsIObserver {
774
static Observer* sInstance;
775
776
bool mPendingProfileChange;
777
bool mShutdownComplete;
778
779
public:
780
static nsresult Initialize();
781
782
static void ShutdownCompleted();
783
784
private:
785
Observer() : mPendingProfileChange(false), mShutdownComplete(false) {
786
MOZ_ASSERT(NS_IsMainThread());
787
}
788
789
~Observer() { MOZ_ASSERT(NS_IsMainThread()); }
790
791
nsresult Init();
792
793
nsresult Shutdown();
794
795
NS_DECL_ISUPPORTS
796
NS_DECL_NSIOBSERVER
797
};
798
799
namespace {
800
801
/*******************************************************************************
802
* Local class declarations
803
******************************************************************************/
804
805
} // namespace
806
807
class ClientUsageArray final
808
: public AutoTArray<Maybe<uint64_t>, Client::TYPE_MAX> {
809
public:
810
ClientUsageArray() { SetLength(Client::TypeMax()); }
811
812
void Serialize(nsACString& aText) const;
813
814
nsresult Deserialize(const nsACString& aText);
815
};
816
817
class OriginInfo final {
818
friend class GroupInfo;
819
friend class QuotaManager;
820
friend class QuotaObject;
821
822
public:
823
OriginInfo(GroupInfo* aGroupInfo, const nsACString& aOrigin,
824
const ClientUsageArray& aClientUsages, uint64_t aUsage,
825
int64_t aAccessTime, bool aPersisted, bool aDirectoryExists);
826
827
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo)
828
829
GroupInfo* GetGroupInfo() const { return mGroupInfo; }
830
831
const nsCString& Origin() const { return mOrigin; }
832
833
int64_t LockedUsage() const {
834
AssertCurrentThreadOwnsQuotaMutex();
835
836
#ifdef DEBUG
837
QuotaManager* quotaManager = QuotaManager::Get();
838
MOZ_ASSERT(quotaManager);
839
840
uint64_t usage = 0;
841
for (Client::Type type : quotaManager->AllClientTypes()) {
842
AssertNoOverflow(usage, mClientUsages[type].valueOr(0));
843
usage += mClientUsages[type].valueOr(0);
844
}
845
MOZ_ASSERT(mUsage == usage);
846
#endif
847
848
return mUsage;
849
}
850
851
int64_t LockedAccessTime() const {
852
AssertCurrentThreadOwnsQuotaMutex();
853
854
return mAccessTime;
855
}
856
857
bool LockedPersisted() const {
858
AssertCurrentThreadOwnsQuotaMutex();
859
860
return mPersisted;
861
}
862
863
nsresult LockedBindToStatement(mozIStorageStatement* aStatement) const;
864
865
private:
866
// Private destructor, to discourage deletion outside of Release():
867
~OriginInfo() {
868
MOZ_COUNT_DTOR(OriginInfo);
869
870
MOZ_ASSERT(!mQuotaObjects.Count());
871
}
872
873
void LockedDecreaseUsage(Client::Type aClientType, int64_t aSize);
874
875
void LockedResetUsageForClient(Client::Type aClientType);
876
877
bool LockedGetUsageForClient(Client::Type aClientType, uint64_t& aUsage);
878
879
void LockedUpdateAccessTime(int64_t aAccessTime) {
880
AssertCurrentThreadOwnsQuotaMutex();
881
882
mAccessTime = aAccessTime;
883
if (!mAccessed) {
884
mAccessed = true;
885
}
886
}
887
888
void LockedPersist();
889
890
nsDataHashtable<nsStringHashKey, QuotaObject*> mQuotaObjects;
891
ClientUsageArray mClientUsages;
892
GroupInfo* mGroupInfo;
893
const nsCString mOrigin;
894
uint64_t mUsage;
895
int64_t mAccessTime;
896
bool mAccessed;
897
bool mPersisted;
898
/**
899
* In some special cases like the LocalStorage client where it's possible to
900
* create a Quota-using representation but not actually write any data, we
901
* want to be able to track quota for an origin without creating its origin
902
* directory or the per-client files until they are actually needed to store
903
* data. In those cases, the OriginInfo will be created by
904
* EnsureQuotaForOrigin and the resulting mDirectoryExists will be false until
905
* the origin actually needs to be created. It is possible for mUsage to be
906
* greater than zero while mDirectoryExists is false, representing a state
907
* where a client like LocalStorage has reserved quota for disk writes, but
908
* has not yet flushed the data to disk.
909
*/
910
bool mDirectoryExists;
911
};
912
913
class OriginInfoLRUComparator {
914
public:
915
bool Equals(const OriginInfo* a, const OriginInfo* b) const {
916
return a && b ? a->LockedAccessTime() == b->LockedAccessTime()
917
: !a && !b ? true : false;
918
}
919
920
bool LessThan(const OriginInfo* a, const OriginInfo* b) const {
921
return a && b ? a->LockedAccessTime() < b->LockedAccessTime()
922
: b ? true : false;
923
}
924
};
925
926
class GroupInfo final {
927
friend class GroupInfoPair;
928
friend class OriginInfo;
929
friend class QuotaManager;
930
friend class QuotaObject;
931
932
public:
933
GroupInfo(GroupInfoPair* aGroupInfoPair, PersistenceType aPersistenceType,
934
const nsACString& aGroup)
935
: mGroupInfoPair(aGroupInfoPair),
936
mPersistenceType(aPersistenceType),
937
mGroup(aGroup),
938
mUsage(0) {
939
MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
940
941
MOZ_COUNT_CTOR(GroupInfo);
942
}
943
944
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GroupInfo)
945
946
PersistenceType GetPersistenceType() const { return mPersistenceType; }
947
948
private:
949
// Private destructor, to discourage deletion outside of Release():
950
~GroupInfo() { MOZ_COUNT_DTOR(GroupInfo); }
951
952
already_AddRefed<OriginInfo> LockedGetOriginInfo(const nsACString& aOrigin);
953
954
void LockedAddOriginInfo(OriginInfo* aOriginInfo);
955
956
void LockedRemoveOriginInfo(const nsACString& aOrigin);
957
958
void LockedRemoveOriginInfos();
959
960
bool LockedHasOriginInfos() {
961
AssertCurrentThreadOwnsQuotaMutex();
962
963
return !mOriginInfos.IsEmpty();
964
}
965
966
nsTArray<RefPtr<OriginInfo>> mOriginInfos;
967
968
GroupInfoPair* mGroupInfoPair;
969
PersistenceType mPersistenceType;
970
nsCString mGroup;
971
uint64_t mUsage;
972
};
973
974
class GroupInfoPair {
975
friend class QuotaManager;
976
friend class QuotaObject;
977
978
public:
979
GroupInfoPair() { MOZ_COUNT_CTOR(GroupInfoPair); }
980
981
~GroupInfoPair() { MOZ_COUNT_DTOR(GroupInfoPair); }
982
983
private:
984
already_AddRefed<GroupInfo> LockedGetGroupInfo(
985
PersistenceType aPersistenceType) {
986
AssertCurrentThreadOwnsQuotaMutex();
987
MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
988
989
RefPtr<GroupInfo> groupInfo =
990
GetGroupInfoForPersistenceType(aPersistenceType);
991
return groupInfo.forget();
992
}
993
994
void LockedSetGroupInfo(PersistenceType aPersistenceType,
995
GroupInfo* aGroupInfo) {
996
AssertCurrentThreadOwnsQuotaMutex();
997
MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
998
999
RefPtr<GroupInfo>& groupInfo =
1000
GetGroupInfoForPersistenceType(aPersistenceType);
1001
groupInfo = aGroupInfo;
1002
}
1003
1004
void LockedClearGroupInfo(PersistenceType aPersistenceType) {
1005
AssertCurrentThreadOwnsQuotaMutex();
1006
MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
1007
1008
RefPtr<GroupInfo>& groupInfo =
1009
GetGroupInfoForPersistenceType(aPersistenceType);
1010
groupInfo = nullptr;
1011
}
1012
1013
bool LockedHasGroupInfos() {
1014
AssertCurrentThreadOwnsQuotaMutex();
1015
1016
return mTemporaryStorageGroupInfo || mDefaultStorageGroupInfo;
1017
}
1018
1019
RefPtr<GroupInfo>& GetGroupInfoForPersistenceType(
1020
PersistenceType aPersistenceType);
1021
1022
RefPtr<GroupInfo> mTemporaryStorageGroupInfo;
1023
RefPtr<GroupInfo> mDefaultStorageGroupInfo;
1024
};
1025
1026
namespace {
1027
1028
class CollectOriginsHelper final : public Runnable {
1029
uint64_t mMinSizeToBeFreed;
1030
1031
Mutex& mMutex;
1032
CondVar mCondVar;
1033
1034
// The members below are protected by mMutex.
1035
nsTArray<RefPtr<DirectoryLockImpl>> mLocks;
1036
uint64_t mSizeToBeFreed;
1037
bool mWaiting;
1038
1039
public:
1040
CollectOriginsHelper(mozilla::Mutex& aMutex, uint64_t aMinSizeToBeFreed);
1041
1042
// Blocks the current thread until origins are collected on the main thread.
1043
// The returned value contains an aggregate size of those origins.
1044
int64_t BlockAndReturnOriginsForEviction(
1045
nsTArray<RefPtr<DirectoryLockImpl>>& aLocks);
1046
1047
private:
1048
~CollectOriginsHelper() {}
1049
1050
NS_IMETHOD
1051
Run() override;
1052
};
1053
1054
class OriginOperationBase : public BackgroundThreadObject, public Runnable {
1055
protected:
1056
nsresult mResultCode;
1057
1058
enum State {
1059
// Not yet run.
1060
State_Initial,
1061
1062
// Running quota manager initialization on the owning thread.
1063
State_CreatingQuotaManager,
1064
1065
// Running on the owning thread in the listener for OpenDirectory.
1066
State_DirectoryOpenPending,
1067
1068
// Running on the IO thread.
1069
State_DirectoryWorkOpen,
1070
1071
// Running on the owning thread after all work is done.
1072
State_UnblockingOpen,
1073
1074
// All done.
1075
State_Complete
1076
};
1077
1078
private:
1079
State mState;
1080
bool mActorDestroyed;
1081
1082
protected:
1083
bool mNeedsQuotaManagerInit;
1084
1085
public:
1086
void NoteActorDestroyed() {
1087
AssertIsOnOwningThread();
1088
1089
mActorDestroyed = true;
1090
}
1091
1092
bool IsActorDestroyed() const {
1093
AssertIsOnOwningThread();
1094
1095
return mActorDestroyed;
1096
}
1097
1098
protected:
1099
explicit OriginOperationBase(
1100
nsIEventTarget* aOwningThread = GetCurrentThreadEventTarget())
1101
: BackgroundThreadObject(aOwningThread),
1102
Runnable("dom::quota::OriginOperationBase"),
1103
mResultCode(NS_OK),
1104
mState(State_Initial),
1105
mActorDestroyed(false),
1106
mNeedsQuotaManagerInit(false) {}
1107
1108
// Reference counted.
1109
virtual ~OriginOperationBase() {
1110
MOZ_ASSERT(mState == State_Complete);
1111
MOZ_ASSERT(mActorDestroyed);
1112
}
1113
1114
#ifdef DEBUG
1115
State GetState() const { return mState; }
1116
#endif
1117
1118
void SetState(State aState) {
1119
MOZ_ASSERT(mState == State_Initial);
1120
mState = aState;
1121
}
1122
1123
void AdvanceState() {
1124
switch (mState) {
1125
case State_Initial:
1126
mState = State_CreatingQuotaManager;
1127
return;
1128
case State_CreatingQuotaManager:
1129
mState = State_DirectoryOpenPending;
1130
return;
1131
case State_DirectoryOpenPending:
1132
mState = State_DirectoryWorkOpen;
1133
return;
1134
case State_DirectoryWorkOpen:
1135
mState = State_UnblockingOpen;
1136
return;
1137
case State_UnblockingOpen:
1138
mState = State_Complete;
1139
return;
1140
default:
1141
MOZ_CRASH("Bad state!");
1142
}
1143
}
1144
1145
NS_IMETHOD
1146
Run() override;
1147
1148
virtual void Open() = 0;
1149
1150
nsresult DirectoryOpen();
1151
1152
virtual nsresult DoDirectoryWork(QuotaManager* aQuotaManager) = 0;
1153
1154
void Finish(nsresult aResult);
1155
1156
virtual void UnblockOpen() = 0;
1157
1158
private:
1159
nsresult Init();
1160
1161
nsresult FinishInit();
1162
1163
nsresult QuotaManagerOpen();
1164
1165
nsresult DirectoryWork();
1166
};
1167
1168
class FinalizeOriginEvictionOp : public OriginOperationBase {
1169
nsTArray<RefPtr<DirectoryLockImpl>> mLocks;
1170
1171
public:
1172
FinalizeOriginEvictionOp(nsIEventTarget* aBackgroundThread,
1173
nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
1174
: OriginOperationBase(aBackgroundThread) {
1175
MOZ_ASSERT(!NS_IsMainThread());
1176
1177
mLocks.SwapElements(aLocks);
1178
}
1179
1180
void Dispatch();
1181
1182
void RunOnIOThreadImmediately();
1183
1184
private:
1185
~FinalizeOriginEvictionOp() {}
1186
1187
virtual void Open() override;
1188
1189
virtual nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
1190
1191
virtual void UnblockOpen() override;
1192
};
1193
1194
class NormalOriginOperationBase : public OriginOperationBase,
1195
public OpenDirectoryListener {
1196
RefPtr<DirectoryLock> mDirectoryLock;
1197
1198
protected:
1199
Nullable<PersistenceType> mPersistenceType;
1200
OriginScope mOriginScope;
1201
Nullable<Client::Type> mClientType;
1202
mozilla::Atomic<bool> mCanceled;
1203
const bool mExclusive;
1204
bool mNeedsDirectoryLocking;
1205
1206
public:
1207
void RunImmediately() {
1208
MOZ_ASSERT(GetState() == State_Initial);
1209
1210
MOZ_ALWAYS_SUCCEEDS(this->Run());
1211
}
1212
1213
protected:
1214
NormalOriginOperationBase(const Nullable<PersistenceType>& aPersistenceType,
1215
const OriginScope& aOriginScope, bool aExclusive)
1216
: mPersistenceType(aPersistenceType),
1217
mOriginScope(aOriginScope),
1218
mExclusive(aExclusive),
1219
mNeedsDirectoryLocking(true) {
1220
AssertIsOnOwningThread();
1221
}
1222
1223
~NormalOriginOperationBase() {}
1224
1225
private:
1226
// Need to declare refcounting unconditionally, because
1227
// OpenDirectoryListener has pure-virtual refcounting.
1228
NS_DECL_ISUPPORTS_INHERITED
1229
1230
virtual void Open() override;
1231
1232
virtual void UnblockOpen() override;
1233
1234
// OpenDirectoryListener overrides.
1235
virtual void DirectoryLockAcquired(DirectoryLock* aLock) override;
1236
1237
virtual void DirectoryLockFailed() override;
1238
1239
// Used to send results before unblocking open.
1240
virtual void SendResults() = 0;
1241
};
1242
1243
class SaveOriginAccessTimeOp : public NormalOriginOperationBase {
1244
int64_t mTimestamp;
1245
1246
public:
1247
SaveOriginAccessTimeOp(PersistenceType aPersistenceType,
1248
const nsACString& aOrigin, int64_t aTimestamp)
1249
: NormalOriginOperationBase(Nullable<PersistenceType>(aPersistenceType),
1250
OriginScope::FromOrigin(aOrigin),
1251
/* aExclusive */ false),
1252
mTimestamp(aTimestamp) {
1253
AssertIsOnOwningThread();
1254
}
1255
1256
private:
1257
~SaveOriginAccessTimeOp() {}
1258
1259
virtual nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
1260
1261
virtual void SendResults() override;
1262
};
1263
1264
/*******************************************************************************
1265
* Actor class declarations
1266
******************************************************************************/
1267
1268
class Quota final : public PQuotaParent {
1269
#ifdef DEBUG
1270
bool mActorDestroyed;
1271
#endif
1272
1273
public:
1274
Quota();
1275
1276
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::quota::Quota)
1277
1278
private:
1279
~Quota();
1280
1281
void StartIdleMaintenance();
1282
1283
bool VerifyRequestParams(const UsageRequestParams& aParams) const;
1284
1285
bool VerifyRequestParams(const RequestParams& aParams) const;
1286
1287
// IPDL methods.
1288
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
1289
1290
virtual PQuotaUsageRequestParent* AllocPQuotaUsageRequestParent(
1291
const UsageRequestParams& aParams) override;
1292
1293
virtual mozilla::ipc::IPCResult RecvPQuotaUsageRequestConstructor(
1294
PQuotaUsageRequestParent* aActor,
1295
const UsageRequestParams& aParams) override;
1296
1297
virtual bool DeallocPQuotaUsageRequestParent(
1298
PQuotaUsageRequestParent* aActor) override;
1299
1300
virtual PQuotaRequestParent* AllocPQuotaRequestParent(
1301
const RequestParams& aParams) override;
1302
1303
virtual mozilla::ipc::IPCResult RecvPQuotaRequestConstructor(
1304
PQuotaRequestParent* aActor, const RequestParams& aParams) override;
1305
1306
virtual bool DeallocPQuotaRequestParent(PQuotaRequestParent* aActor) override;
1307
1308
virtual mozilla::ipc::IPCResult RecvStartIdleMaintenance() override;
1309
1310
virtual mozilla::ipc::IPCResult RecvStopIdleMaintenance() override;
1311
1312
virtual mozilla::ipc::IPCResult RecvAbortOperationsForProcess(
1313
const ContentParentId& aContentParentId) override;
1314
};
1315
1316
class QuotaUsageRequestBase : public NormalOriginOperationBase,
1317
public PQuotaUsageRequestParent {
1318
public:
1319
// May be overridden by subclasses if they need to perform work on the
1320
// background thread before being run.
1321
virtual bool Init(Quota* aQuota);
1322
1323
protected:
1324
QuotaUsageRequestBase()
1325
: NormalOriginOperationBase(Nullable<PersistenceType>(),
1326
OriginScope::FromNull(),
1327
/* aExclusive */ false) {}
1328
1329
nsresult GetUsageForOrigin(QuotaManager* aQuotaManager,
1330
PersistenceType aPersistenceType,
1331
const nsACString& aGroup,
1332
const nsACString& aOrigin, UsageInfo* aUsageInfo);
1333
1334
// Subclasses use this override to set the IPDL response value.
1335
virtual void GetResponse(UsageRequestResponse& aResponse) = 0;
1336
1337
private:
1338
void SendResults() override;
1339
1340
// IPDL methods.
1341
void ActorDestroy(ActorDestroyReason aWhy) override;
1342
1343
mozilla::ipc::IPCResult RecvCancel() final;
1344
};
1345
1346
// A mix-in class to simplify operations that need to process every origin in
1347
// one or more repositories. Sub-classes should call TraverseRepository in their
1348
// DoDirectoryWork and implement a ProcessOrigin method for their per-origin
1349
// logic.
1350
class TraverseRepositoryHelper {
1351
public:
1352
TraverseRepositoryHelper() = default;
1353
1354
protected:
1355
virtual ~TraverseRepositoryHelper() = default;
1356
1357
// If ProcessOrigin returns an error, TraverseRepository will immediately
1358
// terminate and return the received error code to its caller.
1359
nsresult TraverseRepository(QuotaManager* aQuotaManager,
1360
PersistenceType aPersistenceType);
1361
1362
private:
1363
virtual bool IsCanceled() = 0;
1364
1365
virtual nsresult ProcessOrigin(QuotaManager* aQuotaManager,
1366
nsIFile* aOriginDir, const bool aPersistent,
1367
const PersistenceType aPersistenceType) = 0;
1368
};
1369
1370
class GetUsageOp final : public QuotaUsageRequestBase,
1371
public TraverseRepositoryHelper {
1372
nsTArray<OriginUsage> mOriginUsages;
1373
nsDataHashtable<nsCStringHashKey, uint32_t> mOriginUsagesIndex;
1374
1375
bool mGetAll;
1376
1377
public:
1378
explicit GetUsageOp(const UsageRequestParams& aParams);
1379
1380
private:
1381
~GetUsageOp() {}
1382
1383
void ProcessOriginInternal(QuotaManager* aQuotaManager,
1384
const PersistenceType aPersistenceType,
1385
const nsACString& aOrigin,
1386
const int64_t aTimestamp, const bool aPersisted,
1387
const uint64_t aUsage);
1388
1389
nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
1390
1391
bool IsCanceled() override;
1392
1393
nsresult ProcessOrigin(QuotaManager* aQuotaManager, nsIFile* aOriginDir,
1394
const bool aPersistent,
1395
const PersistenceType aPersistenceType) override;
1396
1397
void GetResponse(UsageRequestResponse& aResponse) override;
1398
};
1399
1400
class GetOriginUsageOp final : public QuotaUsageRequestBase {
1401
nsCString mSuffix;
1402
nsCString mGroup;
1403
uint64_t mUsage;
1404
uint64_t mFileUsage;
1405
bool mFromMemory;
1406
1407
public:
1408
explicit GetOriginUsageOp(const UsageRequestParams& aParams);
1409
1410
private:
1411
~GetOriginUsageOp() = default;
1412
1413
virtual nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
1414
1415
void GetResponse(UsageRequestResponse& aResponse) override;
1416
};
1417
1418
class QuotaRequestBase : public NormalOriginOperationBase,
1419
public PQuotaRequestParent {
1420
public:
1421
// May be overridden by subclasses if they need to perform work on the
1422
// background thread before being run.
1423
virtual bool Init(Quota* aQuota);
1424
1425
protected:
1426
explicit QuotaRequestBase(bool aExclusive)
1427
: NormalOriginOperationBase(Nullable<PersistenceType>(),
1428
OriginScope::FromNull(), aExclusive) {}
1429
1430
// Subclasses use this override to set the IPDL response value.
1431
virtual void GetResponse(RequestResponse& aResponse) = 0;
1432
1433
private:
1434
virtual void SendResults() override;
1435
1436
// IPDL methods.
1437
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
1438
};
1439
1440
class InitOp final : public QuotaRequestBase {
1441
public:
1442
InitOp() : QuotaRequestBase(/* aExclusive */ false) {
1443
AssertIsOnOwningThread();
1444
}
1445
1446
private:
1447
~InitOp() {}
1448
1449
nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
1450
1451
void GetResponse(RequestResponse& aResponse) override;
1452
};
1453
1454
class InitTemporaryStorageOp final : public QuotaRequestBase {
1455
public:
1456
InitTemporaryStorageOp() : QuotaRequestBase(/* aExclusive */ false) {
1457
AssertIsOnOwningThread();
1458
}
1459
1460
private:
1461
~InitTemporaryStorageOp() {}
1462
1463
nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
1464
1465
void GetResponse(RequestResponse& aResponse) override;
1466
};
1467
1468
class InitOriginOp final : public QuotaRequestBase {
1469
const InitOriginParams mParams;
1470
nsCString mSuffix;
1471
nsCString mGroup;
1472
bool mCreated;
1473
1474
public:
1475
explicit InitOriginOp(const RequestParams& aParams);
1476
1477
bool Init(Quota* aQuota) override;
1478
1479
private:
1480
~InitOriginOp() {}
1481
1482
nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
1483
1484
void GetResponse(RequestResponse& aResponse) override;
1485
};
1486
1487
class ResetOrClearOp final : public QuotaRequestBase {
1488
const bool mClear;
1489
1490
public:
1491
explicit ResetOrClearOp(bool aClear)
1492
: QuotaRequestBase(/* aExclusive */ true), mClear(aClear) {
1493
AssertIsOnOwningThread();
1494
}
1495
1496
private:
1497
~ResetOrClearOp() {}
1498
1499
void DeleteFiles(QuotaManager* aQuotaManager);
1500
1501
void DeleteStorageFile(QuotaManager* aQuotaManager);
1502
1503
virtual nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
1504
1505
virtual void GetResponse(RequestResponse& aResponse) override;
1506
};
1507
1508
class ClearRequestBase : public QuotaRequestBase {
1509
protected:
1510
const bool mClear;
1511
1512
protected:
1513
ClearRequestBase(bool aExclusive, bool aClear)
1514
: QuotaRequestBase(aExclusive), mClear(aClear) {
1515
AssertIsOnOwningThread();
1516
}
1517
1518
void DeleteFiles(QuotaManager* aQuotaManager,
1519
PersistenceType aPersistenceType);
1520
1521
nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
1522
};
1523
1524
class ClearOriginOp final : public ClearRequestBase {
1525
const ClearResetOriginParams mParams;
1526
1527
public:
1528
explicit ClearOriginOp(const RequestParams& aParams);
1529
1530
bool Init(Quota* aQuota) override;
1531
1532
private:
1533
~ClearOriginOp() {}
1534
1535
void GetResponse(RequestResponse& aResponse) override;
1536
};
1537
1538
class ClearDataOp final : public ClearRequestBase {
1539
const ClearDataParams mParams;
1540
1541
public:
1542
explicit ClearDataOp(const RequestParams& aParams);
1543
1544
bool Init(Quota* aQuota) override;
1545
1546
private:
1547
~ClearDataOp() {}
1548
1549
void GetResponse(RequestResponse& aResponse) override;
1550
};
1551
1552
class PersistRequestBase : public QuotaRequestBase {
1553
const PrincipalInfo mPrincipalInfo;
1554
1555
protected:
1556
nsCString mSuffix;
1557
nsCString mGroup;
1558
1559
public:
1560
bool Init(Quota* aQuota) override;
1561
1562
protected:
1563
explicit PersistRequestBase(const PrincipalInfo& aPrincipalInfo);
1564
};
1565
1566
class PersistedOp final : public PersistRequestBase {
1567
bool mPersisted;
1568
1569
public:
1570
explicit PersistedOp(const RequestParams& aParams);
1571
1572
private:
1573
~PersistedOp() {}
1574
1575
nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
1576
1577
void GetResponse(RequestResponse& aResponse) override;
1578
};
1579
1580
class PersistOp final : public PersistRequestBase {
1581
public:
1582
explicit PersistOp(const RequestParams& aParams);
1583
1584
private:
1585
~PersistOp() {}
1586
1587
nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
1588
1589
void GetResponse(RequestResponse& aResponse) override;
1590
};
1591
1592
class EstimateOp final : public QuotaRequestBase {
1593
nsCString mGroup;
1594
uint64_t mUsage;
1595
uint64_t mLimit;
1596
1597
public:
1598
explicit EstimateOp(const RequestParams& aParams);
1599
1600
private:
1601
~EstimateOp() = default;
1602
1603
virtual nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
1604
1605
void GetResponse(RequestResponse& aResponse) override;
1606
};
1607
1608
class ListOriginsOp final : public QuotaRequestBase,
1609
public TraverseRepositoryHelper {
1610
// XXX Bug 1521541 will make each origin has it's own state.
1611
nsTArray<nsCString> mOrigins;
1612
1613
public:
1614
ListOriginsOp();
1615
1616
bool Init(Quota* aQuota) override;
1617
1618
private:
1619
~ListOriginsOp() = default;
1620
1621
nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
1622
1623
bool IsCanceled() override;
1624
1625
nsresult ProcessOrigin(QuotaManager* aQuotaManager, nsIFile* aOriginDir,
1626
const bool aPersistent,
1627
const PersistenceType aPersistenceType) override;
1628
1629
void GetResponse(RequestResponse& aResponse) override;
1630
};
1631
1632
/*******************************************************************************
1633
* Other class declarations
1634
******************************************************************************/
1635
1636
class StoragePressureRunnable final : public Runnable {
1637
const uint64_t mUsage;
1638
1639
public:
1640
explicit StoragePressureRunnable(uint64_t aUsage)
1641
: Runnable("dom::quota::QuotaObject::StoragePressureRunnable"),
1642
mUsage(aUsage) {}
1643
1644
private:
1645
~StoragePressureRunnable() = default;
1646
1647
NS_DECL_NSIRUNNABLE
1648
};
1649
1650
/*******************************************************************************
1651
* Helper classes
1652
******************************************************************************/
1653
1654
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1655
1656
class PrincipalVerifier final : public Runnable {
1657
nsTArray<PrincipalInfo> mPrincipalInfos;
1658
1659
public:
1660
static already_AddRefed<PrincipalVerifier> CreateAndDispatch(
1661
nsTArray<PrincipalInfo>&& aPrincipalInfos);
1662
1663
private:
1664
explicit PrincipalVerifier(nsTArray<PrincipalInfo>&& aPrincipalInfos)
1665
: Runnable("dom::quota::PrincipalVerifier"),
1666
mPrincipalInfos(std::move(aPrincipalInfos)) {
1667
AssertIsOnIOThread();
1668
}
1669
1670
virtual ~PrincipalVerifier() = default;
1671
1672
bool IsPrincipalInfoValid(const PrincipalInfo& aPrincipalInfo);
1673
1674
NS_DECL_NSIRUNNABLE
1675
};
1676
1677
#endif
1678
1679
/*******************************************************************************
1680
* Helper Functions
1681
******************************************************************************/
1682
1683
template <typename T, bool = mozilla::IsUnsigned<T>::value>
1684
struct IntChecker {
1685
static void Assert(T aInt) {
1686
static_assert(mozilla::IsIntegral<T>::value, "Not an integer!");
1687
MOZ_ASSERT(aInt >= 0);
1688
}
1689
};
1690
1691
template <typename T>
1692
struct IntChecker<T, true> {
1693
static void Assert(T aInt) {
1694
static_assert(mozilla::IsIntegral<T>::value, "Not an integer!");
1695
}
1696
};
1697
1698
template <typename T>
1699
void AssertNoOverflow(uint64_t aDest, T aArg) {
1700
IntChecker<T>::Assert(aDest);
1701
IntChecker<T>::Assert(aArg);
1702
MOZ_ASSERT(UINT64_MAX - aDest >= uint64_t(aArg));
1703
}
1704
1705
template <typename T, typename U>
1706
void AssertNoUnderflow(T aDest, U aArg) {
1707
IntChecker<T>::Assert(aDest);
1708
IntChecker<T>::Assert(aArg);
1709
MOZ_ASSERT(uint64_t(aDest) >= uint64_t(aArg));
1710
}
1711
1712
inline bool IsDotFile(const nsAString& aFileName) {
1713
return QuotaManager::IsDotFile(aFileName);
1714
}
1715
1716
inline bool IsOSMetadata(const nsAString& aFileName) {
1717
return QuotaManager::IsOSMetadata(aFileName);
1718
}
1719
1720
bool IsOriginMetadata(const nsAString& aFileName) {
1721
return aFileName.EqualsLiteral(METADATA_FILE_NAME) ||
1722
aFileName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
1723
IsOSMetadata(aFileName);
1724
}
1725
1726
bool IsTempMetadata(const nsAString& aFileName) {
1727
return aFileName.EqualsLiteral(METADATA_TMP_FILE_NAME) ||
1728
aFileName.EqualsLiteral(METADATA_V2_TMP_FILE_NAME);
1729
}
1730
1731
nsresult MaybeUpdateGroupForOrigin(const nsACString& aOrigin,
1732
nsACString& aGroup, bool& aUpdated) {
1733
MOZ_ASSERT(!NS_IsMainThread());
1734
1735
aUpdated = false;
1736
1737
if (aOrigin.EqualsLiteral(kChromeOrigin)) {
1738
if (!aGroup.EqualsLiteral(kChromeOrigin)) {
1739
aGroup.AssignLiteral(kChromeOrigin);
1740
aUpdated = true;
1741
}
1742
} else {
1743
OriginAttributes originAttributes;
1744
nsCString originNoSuffix;
1745
if (NS_WARN_IF(
1746
!originAttributes.PopulateFromOrigin(aOrigin, originNoSuffix))) {
1747
return NS_ERROR_FAILURE;
1748
}
1749
1750
nsCString suffix;
1751
originAttributes.CreateSuffix(suffix);
1752
1753
RefPtr<MozURL> url;
1754
nsresult rv = MozURL::Init(getter_AddRefs(url), originNoSuffix);
1755
if (NS_WARN_IF(NS_FAILED(rv))) {
1756
QM_WARNING("A URL %s is not recognized by MozURL", originNoSuffix.get());
1757
return rv;
1758
}
1759
1760
nsCString baseDomain;
1761
rv = url->BaseDomain(baseDomain);
1762
if (NS_WARN_IF(NS_FAILED(rv))) {
1763
return rv;
1764
}
1765
1766
nsCString upToDateGroup = baseDomain + suffix;
1767
1768
if (aGroup != upToDateGroup) {
1769
aGroup = upToDateGroup;
1770
aUpdated = true;
1771
1772
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1773
ContentPrincipalInfo contentPrincipalInfo;
1774
contentPrincipalInfo.attrs() = originAttributes;
1775
contentPrincipalInfo.originNoSuffix() = originNoSuffix;
1776
contentPrincipalInfo.spec() = originNoSuffix;
1777
contentPrincipalInfo.baseDomain() = baseDomain;
1778
1779
PrincipalInfo principalInfo(contentPrincipalInfo);
1780
1781
nsTArray<PrincipalInfo> principalInfos;
1782
principalInfos.AppendElement(principalInfo);
1783
1784
RefPtr<PrincipalVerifier> principalVerifier =
1785
PrincipalVerifier::CreateAndDispatch(std::move(principalInfos));
1786
#endif
1787
}
1788
}
1789
1790
return NS_OK;
1791
}
1792
1793
} // namespace
1794
1795
BackgroundThreadObject::BackgroundThreadObject()
1796
: mOwningThread(GetCurrentThreadEventTarget()) {
1797
AssertIsOnOwningThread();
1798
}
1799
1800
BackgroundThreadObject::BackgroundThreadObject(nsIEventTarget* aOwningThread)
1801
: mOwningThread(aOwningThread) {}
1802
1803
#ifdef DEBUG
1804
1805
void BackgroundThreadObject::AssertIsOnOwningThread() const {
1806
AssertIsOnBackgroundThread();
1807
MOZ_ASSERT(mOwningThread);
1808
bool current;
1809
MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)));
1810
MOZ_ASSERT(current);
1811
}
1812
1813
#endif // DEBUG
1814
1815
nsIEventTarget* BackgroundThreadObject::OwningThread() const {
1816
MOZ_ASSERT(mOwningThread);
1817
return mOwningThread;
1818
}
1819
1820
bool IsOnIOThread() {
1821
QuotaManager* quotaManager = QuotaManager::Get();
1822
NS_ASSERTION(quotaManager, "Must have a manager here!");
1823
1824
bool currentThread;
1825
return NS_SUCCEEDED(
1826
quotaManager->IOThread()->IsOnCurrentThread(&currentThread)) &&
1827
currentThread;
1828
}
1829
1830
void AssertIsOnIOThread() {
1831
NS_ASSERTION(IsOnIOThread(), "Running on the wrong thread!");
1832
}
1833
1834
void AssertCurrentThreadOwnsQuotaMutex() {
1835
#ifdef DEBUG
1836
QuotaManager* quotaManager = QuotaManager::Get();
1837
NS_ASSERTION(quotaManager, "Must have a manager here!");
1838
1839
quotaManager->AssertCurrentThreadOwnsQuotaMutex();
1840
#endif
1841
}
1842
1843
void ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr) {
1844
// Get leaf of file path
1845
for (const char* p = aFile; *p; ++p) {
1846
if (*p == '/' && *(p + 1)) {
1847
aFile = p + 1;
1848
}
1849
}
1850
1851
nsContentUtils::LogSimpleConsoleError(
1852
NS_ConvertUTF8toUTF16(
1853
nsPrintfCString("Quota %s: %s:%" PRIu32, aStr, aFile, aLine)),
1854
"quota", false /* Quota Manager is not active in private browsing mode */,
1855
true /* Quota Manager runs always in a chrome context */);
1856
}
1857
1858
namespace {
1859
1860
bool gInvalidateQuotaCache = false;
1861
StaticAutoPtr<nsString> gBaseDirPath;
1862
StaticAutoPtr<nsCString> gBuildId;
1863
1864
#ifdef DEBUG
1865
bool gQuotaManagerInitialized = false;
1866
#endif
1867
1868
StaticRefPtr<QuotaManager> gInstance;
1869
bool gCreateFailed = false;
1870
mozilla::Atomic<bool> gShutdown(false);
1871
1872
class StorageOperationBase {
1873
protected:
1874
struct OriginProps;
1875
1876
nsTArray<OriginProps> mOriginProps;
1877
1878
nsCOMPtr<nsIFile> mDirectory;
1879
1880
const bool mPersistent;
1881
1882
public:
1883
StorageOperationBase(nsIFile* aDirectory, bool aPersistent)
1884
: mDirectory(aDirectory), mPersistent(aPersistent) {
1885
AssertIsOnIOThread();
1886
}
1887
1888
NS_INLINE_DECL_REFCOUNTING(StorageOperationBase)
1889