1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/Attributes.h"
8 #include "mozilla/DebugOnly.h"
9 #include "mozilla/SpinEventLoopUntil.h"
11 #include "mozStorageService.h"
12 #include "mozStorageConnection.h"
13 #include "nsComponentManagerUtils.h"
14 #include "nsEmbedCID.h"
15 #include "nsExceptionHandler.h"
16 #include "nsThreadUtils.h"
17 #include "mozStoragePrivateHelpers.h"
18 #include "nsIObserverService.h"
19 #include "nsIPropertyBag2.h"
20 #include "mozilla/Services.h"
21 #include "mozilla/LateWriteChecks.h"
22 #include "mozIStorageCompletionCallback.h"
23 #include "mozIStoragePendingStatement.h"
24 #include "mozilla/StaticPrefs_storage.h"
25 #include "mozilla/intl/Collator.h"
26 #include "mozilla/intl/LocaleService.h"
29 #include "mozilla/AutoSQLiteLifetime.h"
32 // "windows.h" was included and it can #define lots of things we care about...
36 using mozilla::intl::Collator
;
41 ////////////////////////////////////////////////////////////////////////////////
45 mozilla::Atomic
<size_t> gSqliteMemoryUsed
;
48 static int64_t StorageSQLiteDistinguishedAmount() {
49 return ::sqlite3_memory_used();
53 * Passes a single SQLite memory statistic to a memory reporter callback.
55 * @param aHandleReport
58 * The data for the callback.
60 * The SQLite connection.
62 * Head of the path for the memory report.
64 * The memory report statistic kind, one of "stmt", "cache" or
67 * The memory report description.
69 * The SQLite constant for getting the measurement.
71 * The accumulator for the measurement.
73 static void ReportConn(nsIHandleReportCallback
* aHandleReport
,
74 nsISupports
* aData
, Connection
* aConn
,
75 const nsACString
& aPathHead
, const nsACString
& aKind
,
76 const nsACString
& aDesc
, int32_t aOption
,
78 nsCString
path(aPathHead
);
80 path
.AppendLiteral("-used");
82 int32_t val
= aConn
->getSqliteRuntimeStatus(aOption
);
83 aHandleReport
->Callback(""_ns
, path
, nsIMemoryReporter::KIND_HEAP
,
84 nsIMemoryReporter::UNITS_BYTES
, int64_t(val
), aDesc
,
89 // Warning: To get a Connection's measurements requires holding its lock.
90 // There may be a delay getting the lock if another thread is accessing the
91 // Connection. This isn't very nice if CollectReports is called from the main
92 // thread! But at the time of writing this function is only called when
93 // about:memory is loaded (not, for example, when telemetry pings occur) and
94 // any delays in that case aren't so bad.
96 Service::CollectReports(nsIHandleReportCallback
* aHandleReport
,
97 nsISupports
* aData
, bool aAnonymize
) {
98 size_t totalConnSize
= 0;
100 nsTArray
<RefPtr
<Connection
>> connections
;
101 getConnections(connections
);
103 for (uint32_t i
= 0; i
< connections
.Length(); i
++) {
104 RefPtr
<Connection
>& conn
= connections
[i
];
106 // Someone may have closed the Connection, in which case we skip it.
107 // Note that we have consumers of the synchronous API that are off the
108 // main-thread, like the DOM Cache and IndexedDB, and as such we must be
109 // sure that we have a connection.
110 MutexAutoLock
lockedAsyncScope(conn
->sharedAsyncExecutionMutex
);
111 if (!conn
->connectionReady()) {
115 nsCString
pathHead("explicit/storage/sqlite/");
116 // This filename isn't privacy-sensitive, and so is never anonymized.
117 pathHead
.Append(conn
->getFilename());
118 pathHead
.Append('/');
120 SQLiteMutexAutoLock
lockedScope(conn
->sharedDBMutex
);
122 constexpr auto stmtDesc
=
123 "Memory (approximate) used by all prepared statements used by "
124 "connections to this database."_ns
;
125 ReportConn(aHandleReport
, aData
, conn
, pathHead
, "stmt"_ns
, stmtDesc
,
126 SQLITE_DBSTATUS_STMT_USED
, &totalConnSize
);
128 constexpr auto cacheDesc
=
129 "Memory (approximate) used by all pager caches used by connections "
130 "to this database."_ns
;
131 ReportConn(aHandleReport
, aData
, conn
, pathHead
, "cache"_ns
, cacheDesc
,
132 SQLITE_DBSTATUS_CACHE_USED_SHARED
, &totalConnSize
);
134 constexpr auto schemaDesc
=
135 "Memory (approximate) used to store the schema for all databases "
136 "associated with connections to this database."_ns
;
137 ReportConn(aHandleReport
, aData
, conn
, pathHead
, "schema"_ns
, schemaDesc
,
138 SQLITE_DBSTATUS_SCHEMA_USED
, &totalConnSize
);
142 if (::sqlite3_memory_used() != int64_t(gSqliteMemoryUsed
)) {
144 "memory consumption reported by SQLite doesn't match "
150 int64_t other
= ::sqlite3_memory_used() - totalConnSize
;
152 MOZ_COLLECT_REPORT("explicit/storage/sqlite/other", KIND_HEAP
, UNITS_BYTES
,
153 other
, "All unclassified sqlite memory.");
158 ////////////////////////////////////////////////////////////////////////////////
161 NS_IMPL_ISUPPORTS(Service
, mozIStorageService
, nsIObserver
, nsIMemoryReporter
)
163 Service
* Service::gService
= nullptr;
165 already_AddRefed
<Service
> Service::getSingleton() {
167 return do_AddRef(gService
);
170 // The first reference to the storage service must be obtained on the
172 NS_ENSURE_TRUE(NS_IsMainThread(), nullptr);
173 RefPtr
<Service
> service
= new Service();
174 if (NS_SUCCEEDED(service
->initialize())) {
175 // Note: This is cleared in the Service destructor.
176 gService
= service
.get();
177 return service
.forget();
183 int Service::AutoVFSRegistration::Init(UniquePtr
<sqlite3_vfs
> aVFS
) {
186 mVFS
= std::move(aVFS
);
187 return sqlite3_vfs_register(mVFS
.get(), 0);
189 NS_WARNING("Failed to register VFS");
193 Service::AutoVFSRegistration::~AutoVFSRegistration() {
195 int rc
= sqlite3_vfs_unregister(mVFS
.get());
196 if (rc
!= SQLITE_OK
) {
197 NS_WARNING("Failed to unregister sqlite vfs wrapper.");
203 : mMutex("Service::mMutex"),
204 mRegistrationMutex("Service::mRegistrationMutex"),
207 Service::~Service() {
208 mozilla::UnregisterWeakMemoryReporter(this);
209 mozilla::UnregisterStorageSQLiteDistinguishedAmount();
214 void Service::registerConnection(Connection
* aConnection
) {
215 mRegistrationMutex
.AssertNotCurrentThreadOwns();
216 MutexAutoLock
mutex(mRegistrationMutex
);
217 (void)mConnections
.AppendElement(aConnection
);
220 void Service::unregisterConnection(Connection
* aConnection
) {
221 // If this is the last Connection it might be the only thing keeping Service
222 // alive. So ensure that Service is destroyed only after the Connection is
223 // cleanly unregistered and destroyed.
224 RefPtr
<Service
> kungFuDeathGrip(this);
225 RefPtr
<Connection
> forgettingRef
;
227 mRegistrationMutex
.AssertNotCurrentThreadOwns();
228 MutexAutoLock
mutex(mRegistrationMutex
);
230 for (uint32_t i
= 0; i
< mConnections
.Length(); ++i
) {
231 if (mConnections
[i
] == aConnection
) {
232 // Because dropping the final reference can potentially result in
233 // spinning a nested event loop if the connection was not properly
234 // shutdown, we want to do that outside this loop so that we can finish
235 // mutating the array and drop our mutex.
236 forgettingRef
= std::move(mConnections
[i
]);
237 mConnections
.RemoveElementAt(i
);
243 MOZ_ASSERT(forgettingRef
,
244 "Attempt to unregister unknown storage connection!");
246 // Do not proxy the release anywhere, just let this reference drop here. (We
247 // previously did proxy the release, but that was because we invoked Close()
248 // in the destructor and Close() likes to complain if it's not invoked on the
249 // opener event target, so it was essential that the last reference be dropped
250 // on the opener event target. We now enqueue Close() inside our caller,
251 // Release(), so it doesn't actually matter what thread our reference drops
255 void Service::getConnections(
256 /* inout */ nsTArray
<RefPtr
<Connection
>>& aConnections
) {
257 mRegistrationMutex
.AssertNotCurrentThreadOwns();
258 MutexAutoLock
mutex(mRegistrationMutex
);
259 aConnections
.Clear();
260 aConnections
.AppendElements(mConnections
);
263 void Service::minimizeMemory() {
264 nsTArray
<RefPtr
<Connection
>> connections
;
265 getConnections(connections
);
267 for (uint32_t i
= 0; i
< connections
.Length(); i
++) {
268 RefPtr
<Connection
> conn
= connections
[i
];
269 // For non-main-thread owning/opening threads, we may be racing against them
270 // closing their connection or their thread. That's okay, see below.
271 if (!conn
->connectionReady()) {
275 constexpr auto shrinkPragma
= "PRAGMA shrink_memory"_ns
;
277 if (!conn
->operationSupported(Connection::SYNCHRONOUS
)) {
278 // This is a mozIStorageAsyncConnection, it can only be used on the main
279 // thread, so we can do a straight API call.
280 nsCOMPtr
<mozIStoragePendingStatement
> ps
;
281 DebugOnly
<nsresult
> rv
= conn
->ExecuteSimpleSQLAsync(
282 shrinkPragma
, nullptr, getter_AddRefs(ps
));
283 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Should have purged sqlite caches");
284 } else if (IsOnCurrentSerialEventTarget(conn
->eventTargetOpenedOn
)) {
285 if (conn
->isAsyncExecutionThreadAvailable()) {
286 nsCOMPtr
<mozIStoragePendingStatement
> ps
;
287 DebugOnly
<nsresult
> rv
= conn
->ExecuteSimpleSQLAsync(
288 shrinkPragma
, nullptr, getter_AddRefs(ps
));
289 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Should have purged sqlite caches");
291 conn
->ExecuteSimpleSQL(shrinkPragma
);
294 // We are on the wrong event target, the query should be executed on the
295 // opener event target, so we must dispatch to it.
296 // It's possible the connection is already closed or will be closed by the
297 // time our runnable runs. ExecuteSimpleSQL will safely return with a
298 // failure in that case. If the event target is shutting down or shut
299 // down, the dispatch will fail and that's okay.
300 nsCOMPtr
<nsIRunnable
> event
= NewRunnableMethod
<const nsCString
>(
301 "Connection::ExecuteSimpleSQL", conn
, &Connection::ExecuteSimpleSQL
,
303 Unused
<< conn
->eventTargetOpenedOn
->Dispatch(event
, NS_DISPATCH_NORMAL
);
308 UniquePtr
<sqlite3_vfs
> ConstructBaseVFS(bool);
309 const char* GetBaseVFSName(bool);
311 UniquePtr
<sqlite3_vfs
> ConstructQuotaVFS(const char* aBaseVFSName
);
312 const char* GetQuotaVFSName();
314 UniquePtr
<sqlite3_vfs
> ConstructObfuscatingVFS(const char* aBaseVFSName
);
316 UniquePtr
<sqlite3_vfs
> ConstructReadOnlyNoLockVFS();
318 static const char* sObserverTopics
[] = {"memory-pressure",
319 "xpcom-shutdown-threads"};
321 nsresult
Service::initialize() {
322 MOZ_ASSERT(NS_IsMainThread(), "Must be initialized on the main thread");
324 int rc
= AutoSQLiteLifetime::getInitResult();
325 if (rc
!= SQLITE_OK
) {
326 return convertResultCode(rc
);
330 * The virtual file system hierarchy
342 * base-vfs-excl base-vfs
346 * unix-excl win32 unix win32
349 rc
= mBaseSqliteVFS
.Init(ConstructBaseVFS(false));
350 if (rc
!= SQLITE_OK
) {
351 return convertResultCode(rc
);
354 rc
= mBaseExclSqliteVFS
.Init(ConstructBaseVFS(true));
355 if (rc
!= SQLITE_OK
) {
356 return convertResultCode(rc
);
359 rc
= mQuotaSqliteVFS
.Init(ConstructQuotaVFS(
360 GetBaseVFSName(StaticPrefs::storage_sqlite_exclusiveLock_enabled())));
361 if (rc
!= SQLITE_OK
) {
362 return convertResultCode(rc
);
365 rc
= mObfuscatingSqliteVFS
.Init(ConstructObfuscatingVFS(GetQuotaVFSName()));
366 if (rc
!= SQLITE_OK
) {
367 return convertResultCode(rc
);
370 rc
= mReadOnlyNoLockSqliteVFS
.Init(ConstructReadOnlyNoLockVFS());
371 if (rc
!= SQLITE_OK
) {
372 return convertResultCode(rc
);
375 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
376 NS_ENSURE_TRUE(os
, NS_ERROR_FAILURE
);
378 for (size_t i
= 0; i
< ArrayLength(sObserverTopics
); ++i
) {
379 nsresult rv
= os
->AddObserver(this, sObserverTopics
[i
], false);
380 if (NS_WARN_IF(NS_FAILED(rv
))) {
385 mozilla::RegisterWeakMemoryReporter(this);
386 mozilla::RegisterStorageSQLiteDistinguishedAmount(
387 StorageSQLiteDistinguishedAmount
);
392 int Service::localeCompareStrings(const nsAString
& aStr1
,
393 const nsAString
& aStr2
,
394 Collator::Sensitivity aSensitivity
) {
395 // The mozilla::intl::Collator is not thread safe, since the Collator::Options
397 MutexAutoLock
mutex(mMutex
);
399 Collator
* collator
= getCollator();
401 NS_ERROR("Storage service has no collation");
405 if (aSensitivity
!= mLastSensitivity
) {
406 Collator::Options options
{};
407 options
.sensitivity
= aSensitivity
;
408 auto result
= mCollator
->SetOptions(options
);
410 if (result
.isErr()) {
411 NS_WARNING("Could not configure the mozilla::intl::Collation.");
414 mLastSensitivity
= aSensitivity
;
417 return collator
->CompareStrings(aStr1
, aStr2
);
420 Collator
* Service::getCollator() {
421 mMutex
.AssertCurrentThreadOwns();
424 return mCollator
.get();
427 auto result
= mozilla::intl::LocaleService::TryCreateComponent
<Collator
>();
428 if (result
.isErr()) {
429 NS_WARNING("Could not create mozilla::intl::Collation.");
433 mCollator
= result
.unwrap();
435 // Sort in a case-insensitive way, where "base" letters are considered
436 // equal, e.g: a = á, a = A, a ≠b.
437 Collator::Options options
{};
438 options
.sensitivity
= Collator::Sensitivity::Base
;
439 auto optResult
= mCollator
->SetOptions(options
);
441 if (optResult
.isErr()) {
442 NS_WARNING("Could not configure the mozilla::intl::Collation.");
447 return mCollator
.get();
450 ////////////////////////////////////////////////////////////////////////////////
451 //// mozIStorageService
454 Service::OpenSpecialDatabase(const nsACString
& aStorageKey
,
455 const nsACString
& aName
, uint32_t aConnectionFlags
,
456 mozIStorageConnection
** _connection
) {
457 if (!aStorageKey
.Equals(kMozStorageMemoryStorageKey
)) {
458 return NS_ERROR_INVALID_ARG
;
461 const bool interruptible
=
462 aConnectionFlags
& mozIStorageService::CONNECTION_INTERRUPTIBLE
;
464 int flags
= SQLITE_OPEN_READWRITE
;
466 if (!aName
.IsEmpty()) {
467 flags
|= SQLITE_OPEN_URI
;
470 RefPtr
<Connection
> msc
=
471 new Connection(this, flags
, Connection::SYNCHRONOUS
, interruptible
);
473 const nsresult rv
= msc
->initialize(aStorageKey
, aName
);
474 NS_ENSURE_SUCCESS(rv
, rv
);
476 msc
.forget(_connection
);
482 class AsyncInitDatabase final
: public Runnable
{
484 AsyncInitDatabase(Connection
* aConnection
, nsIFile
* aStorageFile
,
485 int32_t aGrowthIncrement
,
486 mozIStorageCompletionCallback
* aCallback
)
487 : Runnable("storage::AsyncInitDatabase"),
488 mConnection(aConnection
),
489 mStorageFile(aStorageFile
),
490 mGrowthIncrement(aGrowthIncrement
),
491 mCallback(aCallback
) {
492 MOZ_ASSERT(NS_IsMainThread());
495 NS_IMETHOD
Run() override
{
496 MOZ_ASSERT(!NS_IsMainThread());
497 nsresult rv
= mConnection
->initializeOnAsyncThread(mStorageFile
);
499 return DispatchResult(rv
, nullptr);
502 if (mGrowthIncrement
>= 0) {
503 // Ignore errors. In the future, we might wish to log them.
504 (void)mConnection
->SetGrowthIncrement(mGrowthIncrement
, ""_ns
);
507 return DispatchResult(
508 NS_OK
, NS_ISUPPORTS_CAST(mozIStorageAsyncConnection
*, mConnection
));
512 nsresult
DispatchResult(nsresult aStatus
, nsISupports
* aValue
) {
513 RefPtr
<CallbackComplete
> event
=
514 new CallbackComplete(aStatus
, aValue
, mCallback
.forget());
515 return NS_DispatchToMainThread(event
);
518 ~AsyncInitDatabase() {
519 NS_ReleaseOnMainThread("AsyncInitDatabase::mStorageFile",
520 mStorageFile
.forget());
521 NS_ReleaseOnMainThread("AsyncInitDatabase::mConnection",
522 mConnection
.forget());
524 // Generally, the callback will be released by CallbackComplete.
525 // However, if for some reason Run() is not executed, we still
526 // need to ensure that it is released here.
527 NS_ReleaseOnMainThread("AsyncInitDatabase::mCallback", mCallback
.forget());
530 RefPtr
<Connection
> mConnection
;
531 nsCOMPtr
<nsIFile
> mStorageFile
;
532 int32_t mGrowthIncrement
;
533 RefPtr
<mozIStorageCompletionCallback
> mCallback
;
539 Service::OpenAsyncDatabase(nsIVariant
* aDatabaseStore
, uint32_t aOpenFlags
,
540 uint32_t /* aConnectionFlags */,
541 mozIStorageCompletionCallback
* aCallback
) {
542 if (!NS_IsMainThread()) {
543 return NS_ERROR_NOT_SAME_THREAD
;
545 NS_ENSURE_ARG(aDatabaseStore
);
546 NS_ENSURE_ARG(aCallback
);
548 const bool shared
= aOpenFlags
& mozIStorageService::OPEN_SHARED
;
549 const bool ignoreLockingMode
=
550 aOpenFlags
& mozIStorageService::OPEN_IGNORE_LOCKING_MODE
;
551 // Specifying ignoreLockingMode will force use of the readOnly flag:
552 const bool readOnly
=
553 ignoreLockingMode
|| (aOpenFlags
& mozIStorageService::OPEN_READONLY
);
554 int flags
= readOnly
? SQLITE_OPEN_READONLY
: SQLITE_OPEN_READWRITE
;
556 nsCOMPtr
<nsIFile
> storageFile
;
557 nsCOMPtr
<nsISupports
> dbStore
;
558 nsresult rv
= aDatabaseStore
->GetAsISupports(getter_AddRefs(dbStore
));
559 if (NS_SUCCEEDED(rv
)) {
560 // Generally, aDatabaseStore holds the database nsIFile.
561 storageFile
= do_QueryInterface(dbStore
, &rv
);
563 return NS_ERROR_INVALID_ARG
;
566 nsCOMPtr
<nsIFile
> cloned
;
567 rv
= storageFile
->Clone(getter_AddRefs(cloned
));
568 MOZ_ASSERT(NS_SUCCEEDED(rv
));
569 storageFile
= std::move(cloned
);
572 // Ensure that SQLITE_OPEN_CREATE is passed in for compatibility reasons.
573 flags
|= SQLITE_OPEN_CREATE
;
576 // Apply the shared-cache option.
577 flags
|= shared
? SQLITE_OPEN_SHAREDCACHE
: SQLITE_OPEN_PRIVATECACHE
;
579 // Sometimes, however, it's a special database name.
580 nsAutoCString keyString
;
581 rv
= aDatabaseStore
->GetAsACString(keyString
);
582 if (NS_FAILED(rv
) || !keyString
.Equals(kMozStorageMemoryStorageKey
)) {
583 return NS_ERROR_INVALID_ARG
;
586 // Just fall through with nullptr storageFile, this will cause the storage
587 // connection to use a memory DB.
590 // Create connection on this thread, but initialize it on its helper thread.
591 RefPtr
<Connection
> msc
=
592 new Connection(this, flags
, Connection::ASYNCHRONOUS
,
593 /* interruptible */ true, ignoreLockingMode
);
594 nsCOMPtr
<nsIEventTarget
> target
= msc
->getAsyncExecutionTarget();
596 "Cannot initialize a connection that has been closed already");
598 RefPtr
<AsyncInitDatabase
> asyncInit
= new AsyncInitDatabase(
599 msc
, storageFile
, /* growthIncrement */ -1, aCallback
);
600 return target
->Dispatch(asyncInit
, nsIEventTarget::DISPATCH_NORMAL
);
604 Service::OpenDatabase(nsIFile
* aDatabaseFile
, uint32_t aConnectionFlags
,
605 mozIStorageConnection
** _connection
) {
606 NS_ENSURE_ARG(aDatabaseFile
);
608 const bool interruptible
=
609 aConnectionFlags
& mozIStorageService::CONNECTION_INTERRUPTIBLE
;
611 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
614 SQLITE_OPEN_READWRITE
| SQLITE_OPEN_SHAREDCACHE
| SQLITE_OPEN_CREATE
;
615 RefPtr
<Connection
> msc
=
616 new Connection(this, flags
, Connection::SYNCHRONOUS
, interruptible
);
618 const nsresult rv
= msc
->initialize(aDatabaseFile
);
619 NS_ENSURE_SUCCESS(rv
, rv
);
621 msc
.forget(_connection
);
626 Service::OpenUnsharedDatabase(nsIFile
* aDatabaseFile
, uint32_t aConnectionFlags
,
627 mozIStorageConnection
** _connection
) {
628 NS_ENSURE_ARG(aDatabaseFile
);
630 const bool interruptible
=
631 aConnectionFlags
& mozIStorageService::CONNECTION_INTERRUPTIBLE
;
633 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
636 SQLITE_OPEN_READWRITE
| SQLITE_OPEN_PRIVATECACHE
| SQLITE_OPEN_CREATE
;
637 RefPtr
<Connection
> msc
=
638 new Connection(this, flags
, Connection::SYNCHRONOUS
, interruptible
);
640 const nsresult rv
= msc
->initialize(aDatabaseFile
);
641 NS_ENSURE_SUCCESS(rv
, rv
);
643 msc
.forget(_connection
);
648 Service::OpenDatabaseWithFileURL(nsIFileURL
* aFileURL
,
649 const nsACString
& aTelemetryFilename
,
650 uint32_t aConnectionFlags
,
651 mozIStorageConnection
** _connection
) {
652 NS_ENSURE_ARG(aFileURL
);
654 const bool interruptible
=
655 aConnectionFlags
& mozIStorageService::CONNECTION_INTERRUPTIBLE
;
657 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
659 const int flags
= SQLITE_OPEN_READWRITE
| SQLITE_OPEN_SHAREDCACHE
|
660 SQLITE_OPEN_CREATE
| SQLITE_OPEN_URI
;
661 RefPtr
<Connection
> msc
=
662 new Connection(this, flags
, Connection::SYNCHRONOUS
, interruptible
);
664 const nsresult rv
= msc
->initialize(aFileURL
, aTelemetryFilename
);
665 NS_ENSURE_SUCCESS(rv
, rv
);
667 msc
.forget(_connection
);
672 Service::BackupDatabaseFile(nsIFile
* aDBFile
, const nsAString
& aBackupFileName
,
673 nsIFile
* aBackupParentDirectory
, nsIFile
** backup
) {
675 nsCOMPtr
<nsIFile
> parentDir
= aBackupParentDirectory
;
677 // This argument is optional, and defaults to the same parent directory
678 // as the current file.
679 rv
= aDBFile
->GetParent(getter_AddRefs(parentDir
));
680 NS_ENSURE_SUCCESS(rv
, rv
);
683 nsCOMPtr
<nsIFile
> backupDB
;
684 rv
= parentDir
->Clone(getter_AddRefs(backupDB
));
685 NS_ENSURE_SUCCESS(rv
, rv
);
687 rv
= backupDB
->Append(aBackupFileName
);
688 NS_ENSURE_SUCCESS(rv
, rv
);
690 rv
= backupDB
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0600);
691 NS_ENSURE_SUCCESS(rv
, rv
);
693 nsAutoString fileName
;
694 rv
= backupDB
->GetLeafName(fileName
);
695 NS_ENSURE_SUCCESS(rv
, rv
);
697 rv
= backupDB
->Remove(false);
698 NS_ENSURE_SUCCESS(rv
, rv
);
700 backupDB
.forget(backup
);
702 return aDBFile
->CopyTo(parentDir
, fileName
);
705 ////////////////////////////////////////////////////////////////////////////////
709 Service::Observe(nsISupports
*, const char* aTopic
, const char16_t
*) {
710 if (strcmp(aTopic
, "memory-pressure") == 0) {
712 } else if (strcmp(aTopic
, "xpcom-shutdown-threads") == 0) {
713 // The Service is kept alive by our strong observer references and
714 // references held by Connection instances. Since we're about to remove the
715 // former and then wait for the latter ones to go away, it behooves us to
716 // hold a strong reference to ourselves so our calls to getConnections() do
717 // not happen on a deleted object.
718 RefPtr
<Service
> kungFuDeathGrip
= this;
720 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
722 for (size_t i
= 0; i
< ArrayLength(sObserverTopics
); ++i
) {
723 (void)os
->RemoveObserver(this, sObserverTopics
[i
]);
726 SpinEventLoopUntil("storage::Service::Observe(xpcom-shutdown-threads)"_ns
,
728 // We must wait until all the closing connections are
730 nsTArray
<RefPtr
<Connection
>> connections
;
731 getConnections(connections
);
732 for (auto& conn
: connections
) {
733 if (conn
->isClosing()) {
741 nsTArray
<RefPtr
<Connection
>> connections
;
742 getConnections(connections
);
743 for (uint32_t i
= 0, n
= connections
.Length(); i
< n
; i
++) {
744 if (!connections
[i
]->isClosed()) {
745 // getFilename is only the leaf name for the database file,
746 // so it shouldn't contain privacy-sensitive information.
747 CrashReporter::AnnotateCrashReport(
748 CrashReporter::Annotation::StorageConnectionNotClosed
,
749 connections
[i
]->getFilename());
750 printf_stderr("Storage connection not closed: %s",
751 connections
[i
]->getFilename().get());
761 } // namespace storage
762 } // namespace mozilla