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 "nsCollationCID.h"
14 #include "nsComponentManagerUtils.h"
15 #include "nsEmbedCID.h"
16 #include "nsExceptionHandler.h"
17 #include "nsThreadUtils.h"
18 #include "mozStoragePrivateHelpers.h"
19 #include "nsIObserverService.h"
20 #include "nsIPropertyBag2.h"
21 #include "mozilla/Services.h"
22 #include "mozilla/LateWriteChecks.h"
23 #include "mozIStorageCompletionCallback.h"
24 #include "mozIStoragePendingStatement.h"
25 #include "mozilla/StaticPrefs_storage.h"
28 #include "mozilla/AutoSQLiteLifetime.h"
31 // "windows.h" was included and it can #define lots of things we care about...
38 ////////////////////////////////////////////////////////////////////////////////
42 mozilla::Atomic
<size_t> gSqliteMemoryUsed
;
45 static int64_t StorageSQLiteDistinguishedAmount() {
46 return ::sqlite3_memory_used();
50 * Passes a single SQLite memory statistic to a memory reporter callback.
52 * @param aHandleReport
55 * The data for the callback.
57 * The SQLite connection.
59 * Head of the path for the memory report.
61 * The memory report statistic kind, one of "stmt", "cache" or
64 * The memory report description.
66 * The SQLite constant for getting the measurement.
68 * The accumulator for the measurement.
70 static void ReportConn(nsIHandleReportCallback
* aHandleReport
,
71 nsISupports
* aData
, Connection
* aConn
,
72 const nsACString
& aPathHead
, const nsACString
& aKind
,
73 const nsACString
& aDesc
, int32_t aOption
,
75 nsCString
path(aPathHead
);
77 path
.AppendLiteral("-used");
79 int32_t val
= aConn
->getSqliteRuntimeStatus(aOption
);
80 aHandleReport
->Callback(""_ns
, path
, nsIMemoryReporter::KIND_HEAP
,
81 nsIMemoryReporter::UNITS_BYTES
, int64_t(val
), aDesc
,
86 // Warning: To get a Connection's measurements requires holding its lock.
87 // There may be a delay getting the lock if another thread is accessing the
88 // Connection. This isn't very nice if CollectReports is called from the main
89 // thread! But at the time of writing this function is only called when
90 // about:memory is loaded (not, for example, when telemetry pings occur) and
91 // any delays in that case aren't so bad.
93 Service::CollectReports(nsIHandleReportCallback
* aHandleReport
,
94 nsISupports
* aData
, bool aAnonymize
) {
95 size_t totalConnSize
= 0;
97 nsTArray
<RefPtr
<Connection
>> connections
;
98 getConnections(connections
);
100 for (uint32_t i
= 0; i
< connections
.Length(); i
++) {
101 RefPtr
<Connection
>& conn
= connections
[i
];
103 // Someone may have closed the Connection, in which case we skip it.
104 // Note that we have consumers of the synchronous API that are off the
105 // main-thread, like the DOM Cache and IndexedDB, and as such we must be
106 // sure that we have a connection.
107 MutexAutoLock
lockedAsyncScope(conn
->sharedAsyncExecutionMutex
);
108 if (!conn
->connectionReady()) {
112 nsCString
pathHead("explicit/storage/sqlite/");
113 // This filename isn't privacy-sensitive, and so is never anonymized.
114 pathHead
.Append(conn
->getFilename());
115 pathHead
.Append('/');
117 SQLiteMutexAutoLock
lockedScope(conn
->sharedDBMutex
);
119 constexpr auto stmtDesc
=
120 "Memory (approximate) used by all prepared statements used by "
121 "connections to this database."_ns
;
122 ReportConn(aHandleReport
, aData
, conn
, pathHead
, "stmt"_ns
, stmtDesc
,
123 SQLITE_DBSTATUS_STMT_USED
, &totalConnSize
);
125 constexpr auto cacheDesc
=
126 "Memory (approximate) used by all pager caches used by connections "
127 "to this database."_ns
;
128 ReportConn(aHandleReport
, aData
, conn
, pathHead
, "cache"_ns
, cacheDesc
,
129 SQLITE_DBSTATUS_CACHE_USED_SHARED
, &totalConnSize
);
131 constexpr auto schemaDesc
=
132 "Memory (approximate) used to store the schema for all databases "
133 "associated with connections to this database."_ns
;
134 ReportConn(aHandleReport
, aData
, conn
, pathHead
, "schema"_ns
, schemaDesc
,
135 SQLITE_DBSTATUS_SCHEMA_USED
, &totalConnSize
);
139 if (::sqlite3_memory_used() != int64_t(gSqliteMemoryUsed
)) {
141 "memory consumption reported by SQLite doesn't match "
147 int64_t other
= ::sqlite3_memory_used() - totalConnSize
;
149 MOZ_COLLECT_REPORT("explicit/storage/sqlite/other", KIND_HEAP
, UNITS_BYTES
,
150 other
, "All unclassified sqlite memory.");
155 ////////////////////////////////////////////////////////////////////////////////
158 NS_IMPL_ISUPPORTS(Service
, mozIStorageService
, nsIObserver
, nsIMemoryReporter
)
160 Service
* Service::gService
= nullptr;
162 already_AddRefed
<Service
> Service::getSingleton() {
164 return do_AddRef(gService
);
167 // The first reference to the storage service must be obtained on the
169 NS_ENSURE_TRUE(NS_IsMainThread(), nullptr);
170 RefPtr
<Service
> service
= new Service();
171 if (NS_SUCCEEDED(service
->initialize())) {
172 // Note: This is cleared in the Service destructor.
173 gService
= service
.get();
174 return service
.forget();
180 int Service::AutoVFSRegistration::Init(UniquePtr
<sqlite3_vfs
> aVFS
) {
183 mVFS
= std::move(aVFS
);
184 return sqlite3_vfs_register(mVFS
.get(), 0);
186 NS_WARNING("Failed to register VFS");
190 Service::AutoVFSRegistration::~AutoVFSRegistration() {
192 int rc
= sqlite3_vfs_unregister(mVFS
.get());
193 if (rc
!= SQLITE_OK
) {
194 NS_WARNING("Failed to unregister sqlite vfs wrapper.");
200 : mMutex("Service::mMutex"),
201 mRegistrationMutex("Service::mRegistrationMutex"),
204 Service::~Service() {
205 mozilla::UnregisterWeakMemoryReporter(this);
206 mozilla::UnregisterStorageSQLiteDistinguishedAmount();
211 void Service::registerConnection(Connection
* aConnection
) {
212 mRegistrationMutex
.AssertNotCurrentThreadOwns();
213 MutexAutoLock
mutex(mRegistrationMutex
);
214 (void)mConnections
.AppendElement(aConnection
);
217 void Service::unregisterConnection(Connection
* aConnection
) {
218 // If this is the last Connection it might be the only thing keeping Service
219 // alive. So ensure that Service is destroyed only after the Connection is
220 // cleanly unregistered and destroyed.
221 RefPtr
<Service
> kungFuDeathGrip(this);
222 RefPtr
<Connection
> forgettingRef
;
224 mRegistrationMutex
.AssertNotCurrentThreadOwns();
225 MutexAutoLock
mutex(mRegistrationMutex
);
227 for (uint32_t i
= 0; i
< mConnections
.Length(); ++i
) {
228 if (mConnections
[i
] == aConnection
) {
229 // Because dropping the final reference can potentially result in
230 // spinning a nested event loop if the connection was not properly
231 // shutdown, we want to do that outside this loop so that we can finish
232 // mutating the array and drop our mutex.
233 forgettingRef
= std::move(mConnections
[i
]);
234 mConnections
.RemoveElementAt(i
);
240 MOZ_ASSERT(forgettingRef
,
241 "Attempt to unregister unknown storage connection!");
243 // Do not proxy the release anywhere, just let this reference drop here. (We
244 // previously did proxy the release, but that was because we invoked Close()
245 // in the destructor and Close() likes to complain if it's not invoked on the
246 // opener thread, so it was essential that the last reference be dropped on
247 // the opener thread. We now enqueue Close() inside our caller, Release(), so
248 // it doesn't actually matter what thread our reference drops on.)
251 void Service::getConnections(
252 /* inout */ nsTArray
<RefPtr
<Connection
>>& aConnections
) {
253 mRegistrationMutex
.AssertNotCurrentThreadOwns();
254 MutexAutoLock
mutex(mRegistrationMutex
);
255 aConnections
.Clear();
256 aConnections
.AppendElements(mConnections
);
259 void Service::minimizeMemory() {
260 nsTArray
<RefPtr
<Connection
>> connections
;
261 getConnections(connections
);
263 for (uint32_t i
= 0; i
< connections
.Length(); i
++) {
264 RefPtr
<Connection
> conn
= connections
[i
];
265 // For non-main-thread owning/opening threads, we may be racing against them
266 // closing their connection or their thread. That's okay, see below.
267 if (!conn
->connectionReady()) {
271 constexpr auto shrinkPragma
= "PRAGMA shrink_memory"_ns
;
272 bool onOpenedThread
= false;
274 if (!conn
->operationSupported(Connection::SYNCHRONOUS
)) {
275 // This is a mozIStorageAsyncConnection, it can only be used on the main
276 // thread, so we can do a straight API call.
277 nsCOMPtr
<mozIStoragePendingStatement
> ps
;
278 DebugOnly
<nsresult
> rv
= conn
->ExecuteSimpleSQLAsync(
279 shrinkPragma
, nullptr, getter_AddRefs(ps
));
280 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Should have purged sqlite caches");
281 } else if (NS_SUCCEEDED(
282 conn
->threadOpenedOn
->IsOnCurrentThread(&onOpenedThread
)) &&
284 if (conn
->isAsyncExecutionThreadAvailable()) {
285 nsCOMPtr
<mozIStoragePendingStatement
> ps
;
286 DebugOnly
<nsresult
> rv
= conn
->ExecuteSimpleSQLAsync(
287 shrinkPragma
, nullptr, getter_AddRefs(ps
));
288 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Should have purged sqlite caches");
290 conn
->ExecuteSimpleSQL(shrinkPragma
);
293 // We are on the wrong thread, the query should be executed on the
294 // opener thread, so we must dispatch to it.
295 // It's possible the connection is already closed or will be closed by the
296 // time our runnable runs. ExecuteSimpleSQL will safely return with a
297 // failure in that case. If the thread is shutting down or shut down, the
298 // dispatch will fail and that's okay.
299 nsCOMPtr
<nsIRunnable
> event
= NewRunnableMethod
<const nsCString
>(
300 "Connection::ExecuteSimpleSQL", conn
, &Connection::ExecuteSimpleSQL
,
302 Unused
<< conn
->threadOpenedOn
->Dispatch(event
, NS_DISPATCH_NORMAL
);
307 UniquePtr
<sqlite3_vfs
> ConstructTelemetryVFS(bool);
308 const char* GetTelemetryVFSName(bool);
310 UniquePtr
<sqlite3_vfs
> ConstructObfuscatingVFS(const char* aBaseVFSName
);
312 static const char* sObserverTopics
[] = {"memory-pressure",
313 "xpcom-shutdown-threads"};
315 nsresult
Service::initialize() {
316 MOZ_ASSERT(NS_IsMainThread(), "Must be initialized on the main thread");
318 int rc
= AutoSQLiteLifetime::getInitResult();
319 if (rc
!= SQLITE_OK
) {
320 return convertResultCode(rc
);
323 rc
= mTelemetrySqliteVFS
.Init(ConstructTelemetryVFS(false));
324 if (rc
!= SQLITE_OK
) {
325 return convertResultCode(rc
);
328 rc
= mTelemetryExclSqliteVFS
.Init(ConstructTelemetryVFS(true));
329 if (rc
!= SQLITE_OK
) {
330 return convertResultCode(rc
);
333 rc
= mObfuscatingSqliteVFS
.Init(ConstructObfuscatingVFS(GetTelemetryVFSName(
334 StaticPrefs::storage_sqlite_exclusiveLock_enabled())));
335 if (rc
!= SQLITE_OK
) {
336 return convertResultCode(rc
);
339 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
340 NS_ENSURE_TRUE(os
, NS_ERROR_FAILURE
);
342 for (size_t i
= 0; i
< ArrayLength(sObserverTopics
); ++i
) {
343 nsresult rv
= os
->AddObserver(this, sObserverTopics
[i
], false);
344 if (NS_WARN_IF(NS_FAILED(rv
))) {
349 mozilla::RegisterWeakMemoryReporter(this);
350 mozilla::RegisterStorageSQLiteDistinguishedAmount(
351 StorageSQLiteDistinguishedAmount
);
356 int Service::localeCompareStrings(const nsAString
& aStr1
,
357 const nsAString
& aStr2
,
358 int32_t aComparisonStrength
) {
359 // The implementation of nsICollation.CompareString() is platform-dependent.
360 // On Linux it's not thread-safe. It may not be on Windows and OS X either,
361 // but it's more difficult to tell. We therefore synchronize this method.
362 MutexAutoLock
mutex(mMutex
);
364 nsICollation
* coll
= getLocaleCollation();
366 NS_ERROR("Storage service has no collation");
371 nsresult rv
= coll
->CompareString(aComparisonStrength
, aStr1
, aStr2
, &res
);
373 NS_ERROR("Collation compare string failed");
380 nsICollation
* Service::getLocaleCollation() {
381 mMutex
.AssertCurrentThreadOwns();
383 if (mLocaleCollation
) return mLocaleCollation
;
385 nsCOMPtr
<nsICollationFactory
> collFact
=
386 do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID
);
388 NS_WARNING("Could not create collation factory");
392 nsresult rv
= collFact
->CreateCollation(getter_AddRefs(mLocaleCollation
));
394 NS_WARNING("Could not create collation");
398 return mLocaleCollation
;
401 ////////////////////////////////////////////////////////////////////////////////
402 //// mozIStorageService
405 Service::OpenSpecialDatabase(const nsACString
& aStorageKey
,
406 const nsACString
& aName
,
407 mozIStorageConnection
** _connection
) {
408 if (!aStorageKey
.Equals(kMozStorageMemoryStorageKey
)) {
409 return NS_ERROR_INVALID_ARG
;
412 int flags
= SQLITE_OPEN_READWRITE
;
414 if (!aName
.IsEmpty()) {
415 flags
|= SQLITE_OPEN_URI
;
418 RefPtr
<Connection
> msc
= new Connection(this, flags
, Connection::SYNCHRONOUS
);
420 nsresult rv
= msc
->initialize(aStorageKey
, aName
);
421 NS_ENSURE_SUCCESS(rv
, rv
);
423 msc
.forget(_connection
);
429 class AsyncInitDatabase final
: public Runnable
{
431 AsyncInitDatabase(Connection
* aConnection
, nsIFile
* aStorageFile
,
432 int32_t aGrowthIncrement
,
433 mozIStorageCompletionCallback
* aCallback
)
434 : Runnable("storage::AsyncInitDatabase"),
435 mConnection(aConnection
),
436 mStorageFile(aStorageFile
),
437 mGrowthIncrement(aGrowthIncrement
),
438 mCallback(aCallback
) {
439 MOZ_ASSERT(NS_IsMainThread());
442 NS_IMETHOD
Run() override
{
443 MOZ_ASSERT(!NS_IsMainThread());
444 nsresult rv
= mConnection
->initializeOnAsyncThread(mStorageFile
);
446 return DispatchResult(rv
, nullptr);
449 if (mGrowthIncrement
>= 0) {
450 // Ignore errors. In the future, we might wish to log them.
451 (void)mConnection
->SetGrowthIncrement(mGrowthIncrement
, ""_ns
);
454 return DispatchResult(
455 NS_OK
, NS_ISUPPORTS_CAST(mozIStorageAsyncConnection
*, mConnection
));
459 nsresult
DispatchResult(nsresult aStatus
, nsISupports
* aValue
) {
460 RefPtr
<CallbackComplete
> event
=
461 new CallbackComplete(aStatus
, aValue
, mCallback
.forget());
462 return NS_DispatchToMainThread(event
);
465 ~AsyncInitDatabase() {
466 NS_ReleaseOnMainThread("AsyncInitDatabase::mStorageFile",
467 mStorageFile
.forget());
468 NS_ReleaseOnMainThread("AsyncInitDatabase::mConnection",
469 mConnection
.forget());
471 // Generally, the callback will be released by CallbackComplete.
472 // However, if for some reason Run() is not executed, we still
473 // need to ensure that it is released here.
474 NS_ReleaseOnMainThread("AsyncInitDatabase::mCallback", mCallback
.forget());
477 RefPtr
<Connection
> mConnection
;
478 nsCOMPtr
<nsIFile
> mStorageFile
;
479 int32_t mGrowthIncrement
;
480 RefPtr
<mozIStorageCompletionCallback
> mCallback
;
486 Service::OpenAsyncDatabase(nsIVariant
* aDatabaseStore
,
487 nsIPropertyBag2
* aOptions
,
488 mozIStorageCompletionCallback
* aCallback
) {
489 if (!NS_IsMainThread()) {
490 return NS_ERROR_NOT_SAME_THREAD
;
492 NS_ENSURE_ARG(aDatabaseStore
);
493 NS_ENSURE_ARG(aCallback
);
497 bool readOnly
= false;
498 bool ignoreLockingMode
= false;
499 int32_t growthIncrement
= -1;
501 #define FAIL_IF_SET_BUT_INVALID(rv) \
502 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) { \
503 return NS_ERROR_INVALID_ARG; \
506 // Deal with options first:
508 rv
= aOptions
->GetPropertyAsBool(u
"readOnly"_ns
, &readOnly
);
509 FAIL_IF_SET_BUT_INVALID(rv
);
511 rv
= aOptions
->GetPropertyAsBool(u
"ignoreLockingMode"_ns
,
513 FAIL_IF_SET_BUT_INVALID(rv
);
514 // Specifying ignoreLockingMode will force use of the readOnly flag:
515 if (ignoreLockingMode
) {
519 rv
= aOptions
->GetPropertyAsBool(u
"shared"_ns
, &shared
);
520 FAIL_IF_SET_BUT_INVALID(rv
);
522 // NB: we re-set to -1 if we don't have a storage file later on.
523 rv
= aOptions
->GetPropertyAsInt32(u
"growthIncrement"_ns
, &growthIncrement
);
524 FAIL_IF_SET_BUT_INVALID(rv
);
526 int flags
= readOnly
? SQLITE_OPEN_READONLY
: SQLITE_OPEN_READWRITE
;
528 nsCOMPtr
<nsIFile
> storageFile
;
529 nsCOMPtr
<nsISupports
> dbStore
;
530 rv
= aDatabaseStore
->GetAsISupports(getter_AddRefs(dbStore
));
531 if (NS_SUCCEEDED(rv
)) {
532 // Generally, aDatabaseStore holds the database nsIFile.
533 storageFile
= do_QueryInterface(dbStore
, &rv
);
535 return NS_ERROR_INVALID_ARG
;
538 nsCOMPtr
<nsIFile
> cloned
;
539 rv
= storageFile
->Clone(getter_AddRefs(cloned
));
540 MOZ_ASSERT(NS_SUCCEEDED(rv
));
541 storageFile
= std::move(cloned
);
544 // Ensure that SQLITE_OPEN_CREATE is passed in for compatibility reasons.
545 flags
|= SQLITE_OPEN_CREATE
;
548 // Apply the shared-cache option.
549 flags
|= shared
? SQLITE_OPEN_SHAREDCACHE
: SQLITE_OPEN_PRIVATECACHE
;
551 // Sometimes, however, it's a special database name.
552 nsAutoCString keyString
;
553 rv
= aDatabaseStore
->GetAsACString(keyString
);
554 if (NS_FAILED(rv
) || !keyString
.Equals(kMozStorageMemoryStorageKey
)) {
555 return NS_ERROR_INVALID_ARG
;
558 // Just fall through with nullptr storageFile, this will cause the storage
559 // connection to use a memory DB.
562 if (!storageFile
&& growthIncrement
>= 0) {
563 return NS_ERROR_INVALID_ARG
;
566 // Create connection on this thread, but initialize it on its helper thread.
567 RefPtr
<Connection
> msc
=
568 new Connection(this, flags
, Connection::ASYNCHRONOUS
, ignoreLockingMode
);
569 nsCOMPtr
<nsIEventTarget
> target
= msc
->getAsyncExecutionTarget();
571 "Cannot initialize a connection that has been closed already");
573 RefPtr
<AsyncInitDatabase
> asyncInit
=
574 new AsyncInitDatabase(msc
, storageFile
, growthIncrement
, aCallback
);
575 return target
->Dispatch(asyncInit
, nsIEventTarget::DISPATCH_NORMAL
);
579 Service::OpenDatabase(nsIFile
* aDatabaseFile
,
580 mozIStorageConnection
** _connection
) {
581 NS_ENSURE_ARG(aDatabaseFile
);
583 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
586 SQLITE_OPEN_READWRITE
| SQLITE_OPEN_SHAREDCACHE
| SQLITE_OPEN_CREATE
;
587 RefPtr
<Connection
> msc
= new Connection(this, flags
, Connection::SYNCHRONOUS
);
589 nsresult rv
= msc
->initialize(aDatabaseFile
);
590 NS_ENSURE_SUCCESS(rv
, rv
);
592 msc
.forget(_connection
);
597 Service::OpenUnsharedDatabase(nsIFile
* aDatabaseFile
,
598 mozIStorageConnection
** _connection
) {
599 NS_ENSURE_ARG(aDatabaseFile
);
601 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
604 SQLITE_OPEN_READWRITE
| SQLITE_OPEN_PRIVATECACHE
| SQLITE_OPEN_CREATE
;
605 RefPtr
<Connection
> msc
= new Connection(this, flags
, Connection::SYNCHRONOUS
);
607 nsresult rv
= msc
->initialize(aDatabaseFile
);
608 NS_ENSURE_SUCCESS(rv
, rv
);
610 msc
.forget(_connection
);
615 Service::OpenDatabaseWithFileURL(nsIFileURL
* aFileURL
,
616 const nsACString
& aTelemetryFilename
,
617 mozIStorageConnection
** _connection
) {
618 NS_ENSURE_ARG(aFileURL
);
620 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
622 int flags
= SQLITE_OPEN_READWRITE
| SQLITE_OPEN_SHAREDCACHE
|
623 SQLITE_OPEN_CREATE
| SQLITE_OPEN_URI
;
624 RefPtr
<Connection
> msc
= new Connection(this, flags
, Connection::SYNCHRONOUS
);
626 nsresult rv
= msc
->initialize(aFileURL
, aTelemetryFilename
);
627 NS_ENSURE_SUCCESS(rv
, rv
);
629 msc
.forget(_connection
);
634 Service::BackupDatabaseFile(nsIFile
* aDBFile
, const nsAString
& aBackupFileName
,
635 nsIFile
* aBackupParentDirectory
, nsIFile
** backup
) {
637 nsCOMPtr
<nsIFile
> parentDir
= aBackupParentDirectory
;
639 // This argument is optional, and defaults to the same parent directory
640 // as the current file.
641 rv
= aDBFile
->GetParent(getter_AddRefs(parentDir
));
642 NS_ENSURE_SUCCESS(rv
, rv
);
645 nsCOMPtr
<nsIFile
> backupDB
;
646 rv
= parentDir
->Clone(getter_AddRefs(backupDB
));
647 NS_ENSURE_SUCCESS(rv
, rv
);
649 rv
= backupDB
->Append(aBackupFileName
);
650 NS_ENSURE_SUCCESS(rv
, rv
);
652 rv
= backupDB
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0600);
653 NS_ENSURE_SUCCESS(rv
, rv
);
655 nsAutoString fileName
;
656 rv
= backupDB
->GetLeafName(fileName
);
657 NS_ENSURE_SUCCESS(rv
, rv
);
659 rv
= backupDB
->Remove(false);
660 NS_ENSURE_SUCCESS(rv
, rv
);
662 backupDB
.forget(backup
);
664 return aDBFile
->CopyTo(parentDir
, fileName
);
667 ////////////////////////////////////////////////////////////////////////////////
671 Service::Observe(nsISupports
*, const char* aTopic
, const char16_t
*) {
672 if (strcmp(aTopic
, "memory-pressure") == 0) {
674 } else if (strcmp(aTopic
, "xpcom-shutdown-threads") == 0) {
675 // The Service is kept alive by our strong observer references and
676 // references held by Connection instances. Since we're about to remove the
677 // former and then wait for the latter ones to go away, it behooves us to
678 // hold a strong reference to ourselves so our calls to getConnections() do
679 // not happen on a deleted object.
680 RefPtr
<Service
> kungFuDeathGrip
= this;
682 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
684 for (size_t i
= 0; i
< ArrayLength(sObserverTopics
); ++i
) {
685 (void)os
->RemoveObserver(this, sObserverTopics
[i
]);
688 SpinEventLoopUntil([&]() -> bool {
689 // We must wait until all the closing connections are closed.
690 nsTArray
<RefPtr
<Connection
>> connections
;
691 getConnections(connections
);
692 for (auto& conn
: connections
) {
693 if (conn
->isClosing()) {
701 nsTArray
<RefPtr
<Connection
>> connections
;
702 getConnections(connections
);
703 for (uint32_t i
= 0, n
= connections
.Length(); i
< n
; i
++) {
704 if (!connections
[i
]->isClosed()) {
705 // getFilename is only the leaf name for the database file,
706 // so it shouldn't contain privacy-sensitive information.
707 CrashReporter::AnnotateCrashReport(
708 CrashReporter::Annotation::StorageConnectionNotClosed
,
709 connections
[i
]->getFilename());
710 printf_stderr("Storage connection not closed: %s",
711 connections
[i
]->getFilename().get());
721 } // namespace storage
722 } // namespace mozilla