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"
10 #include "mozStorageService.h"
11 #include "mozStorageConnection.h"
12 #include "nsCollationCID.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/Preferences.h"
22 #include "mozilla/LateWriteChecks.h"
23 #include "mozIStorageCompletionCallback.h"
24 #include "mozIStoragePendingStatement.h"
27 #include "mozilla/AutoSQLiteLifetime.h"
30 // "windows.h" was included and it can #define lots of things we care about...
34 ////////////////////////////////////////////////////////////////////////////////
37 #define PREF_TS_SYNCHRONOUS "toolkit.storage.synchronous"
38 #define PREF_TS_SYNCHRONOUS_DEFAULT 1
40 #define PREF_TS_PAGESIZE "toolkit.storage.pageSize"
42 // This value must be kept in sync with the value of SQLITE_DEFAULT_PAGE_SIZE in
43 // third_party/sqlite3/src/Makefile.in.
44 #define PREF_TS_PAGESIZE_DEFAULT 32768
49 ////////////////////////////////////////////////////////////////////////////////
53 mozilla::Atomic
<size_t> gSqliteMemoryUsed
;
56 static int64_t StorageSQLiteDistinguishedAmount() {
57 return ::sqlite3_memory_used();
61 * Passes a single SQLite memory statistic to a memory reporter callback.
63 * @param aHandleReport
66 * The data for the callback.
68 * The SQLite connection.
70 * Head of the path for the memory report.
72 * The memory report statistic kind, one of "stmt", "cache" or
75 * The memory report description.
77 * The SQLite constant for getting the measurement.
79 * The accumulator for the measurement.
81 static void ReportConn(nsIHandleReportCallback
* aHandleReport
,
82 nsISupports
* aData
, Connection
* aConn
,
83 const nsACString
& aPathHead
, const nsACString
& aKind
,
84 const nsACString
& aDesc
, int32_t aOption
,
86 nsCString
path(aPathHead
);
88 path
.AppendLiteral("-used");
90 int32_t val
= aConn
->getSqliteRuntimeStatus(aOption
);
91 aHandleReport
->Callback(EmptyCString(), path
, nsIMemoryReporter::KIND_HEAP
,
92 nsIMemoryReporter::UNITS_BYTES
, int64_t(val
), aDesc
,
97 // Warning: To get a Connection's measurements requires holding its lock.
98 // There may be a delay getting the lock if another thread is accessing the
99 // Connection. This isn't very nice if CollectReports is called from the main
100 // thread! But at the time of writing this function is only called when
101 // about:memory is loaded (not, for example, when telemetry pings occur) and
102 // any delays in that case aren't so bad.
104 Service::CollectReports(nsIHandleReportCallback
* aHandleReport
,
105 nsISupports
* aData
, bool aAnonymize
) {
106 size_t totalConnSize
= 0;
108 nsTArray
<RefPtr
<Connection
>> connections
;
109 getConnections(connections
);
111 for (uint32_t i
= 0; i
< connections
.Length(); i
++) {
112 RefPtr
<Connection
>& conn
= connections
[i
];
114 // Someone may have closed the Connection, in which case we skip it.
115 // Note that we have consumers of the synchronous API that are off the
116 // main-thread, like the DOM Cache and IndexedDB, and as such we must be
117 // sure that we have a connection.
118 MutexAutoLock
lockedAsyncScope(conn
->sharedAsyncExecutionMutex
);
119 if (!conn
->connectionReady()) {
123 nsCString
pathHead("explicit/storage/sqlite/");
124 // This filename isn't privacy-sensitive, and so is never anonymized.
125 pathHead
.Append(conn
->getFilename());
126 pathHead
.Append('/');
128 SQLiteMutexAutoLock
lockedScope(conn
->sharedDBMutex
);
130 NS_NAMED_LITERAL_CSTRING(
132 "Memory (approximate) used by all prepared statements used by "
133 "connections to this database.");
134 ReportConn(aHandleReport
, aData
, conn
, pathHead
,
135 NS_LITERAL_CSTRING("stmt"), stmtDesc
,
136 SQLITE_DBSTATUS_STMT_USED
, &totalConnSize
);
138 NS_NAMED_LITERAL_CSTRING(
140 "Memory (approximate) used by all pager caches used by connections "
141 "to this database.");
142 ReportConn(aHandleReport
, aData
, conn
, pathHead
,
143 NS_LITERAL_CSTRING("cache"), cacheDesc
,
144 SQLITE_DBSTATUS_CACHE_USED_SHARED
, &totalConnSize
);
146 NS_NAMED_LITERAL_CSTRING(
148 "Memory (approximate) used to store the schema for all databases "
149 "associated with connections to this database.");
150 ReportConn(aHandleReport
, aData
, conn
, pathHead
,
151 NS_LITERAL_CSTRING("schema"), schemaDesc
,
152 SQLITE_DBSTATUS_SCHEMA_USED
, &totalConnSize
);
156 if (::sqlite3_memory_used() != int64_t(gSqliteMemoryUsed
)) {
158 "memory consumption reported by SQLite doesn't match "
164 int64_t other
= ::sqlite3_memory_used() - totalConnSize
;
166 MOZ_COLLECT_REPORT("explicit/storage/sqlite/other", KIND_HEAP
, UNITS_BYTES
,
167 other
, "All unclassified sqlite memory.");
172 ////////////////////////////////////////////////////////////////////////////////
175 NS_IMPL_ISUPPORTS(Service
, mozIStorageService
, nsIObserver
, nsIMemoryReporter
)
177 Service
* Service::gService
= nullptr;
179 already_AddRefed
<Service
> Service::getSingleton() {
181 return do_AddRef(gService
);
184 // The first reference to the storage service must be obtained on the
186 NS_ENSURE_TRUE(NS_IsMainThread(), nullptr);
187 RefPtr
<Service
> service
= new Service();
188 if (NS_SUCCEEDED(service
->initialize())) {
189 // Note: This is cleared in the Service destructor.
190 gService
= service
.get();
191 return service
.forget();
197 int32_t Service::sSynchronousPref
;
200 int32_t Service::getSynchronousPref() { return sSynchronousPref
; }
202 int32_t Service::sDefaultPageSize
= PREF_TS_PAGESIZE_DEFAULT
;
205 : mMutex("Service::mMutex"),
207 mRegistrationMutex("Service::mRegistrationMutex"),
210 Service::~Service() {
211 mozilla::UnregisterWeakMemoryReporter(this);
212 mozilla::UnregisterStorageSQLiteDistinguishedAmount();
214 int rc
= sqlite3_vfs_unregister(mSqliteVFS
);
215 if (rc
!= SQLITE_OK
) NS_WARNING("Failed to unregister sqlite vfs wrapper.");
219 mSqliteVFS
= nullptr;
222 void Service::registerConnection(Connection
* aConnection
) {
223 mRegistrationMutex
.AssertNotCurrentThreadOwns();
224 MutexAutoLock
mutex(mRegistrationMutex
);
225 (void)mConnections
.AppendElement(aConnection
);
228 void Service::unregisterConnection(Connection
* aConnection
) {
229 // If this is the last Connection it might be the only thing keeping Service
230 // alive. So ensure that Service is destroyed only after the Connection is
231 // cleanly unregistered and destroyed.
232 RefPtr
<Service
> kungFuDeathGrip(this);
233 RefPtr
<Connection
> forgettingRef
;
235 mRegistrationMutex
.AssertNotCurrentThreadOwns();
236 MutexAutoLock
mutex(mRegistrationMutex
);
238 for (uint32_t i
= 0; i
< mConnections
.Length(); ++i
) {
239 if (mConnections
[i
] == aConnection
) {
240 // Because dropping the final reference can potentially result in
241 // spinning a nested event loop if the connection was not properly
242 // shutdown, we want to do that outside this loop so that we can finish
243 // mutating the array and drop our mutex.
244 forgettingRef
= std::move(mConnections
[i
]);
245 mConnections
.RemoveElementAt(i
);
251 MOZ_ASSERT(forgettingRef
,
252 "Attempt to unregister unknown storage connection!");
254 // Do not proxy the release anywhere, just let this reference drop here. (We
255 // previously did proxy the release, but that was because we invoked Close()
256 // in the destructor and Close() likes to complain if it's not invoked on the
257 // opener thread, so it was essential that the last reference be dropped on
258 // the opener thread. We now enqueue Close() inside our caller, Release(), so
259 // it doesn't actually matter what thread our reference drops on.)
262 void Service::getConnections(
263 /* inout */ nsTArray
<RefPtr
<Connection
>>& aConnections
) {
264 mRegistrationMutex
.AssertNotCurrentThreadOwns();
265 MutexAutoLock
mutex(mRegistrationMutex
);
266 aConnections
.Clear();
267 aConnections
.AppendElements(mConnections
);
270 void Service::minimizeMemory() {
271 nsTArray
<RefPtr
<Connection
>> connections
;
272 getConnections(connections
);
274 for (uint32_t i
= 0; i
< connections
.Length(); i
++) {
275 RefPtr
<Connection
> conn
= connections
[i
];
276 // For non-main-thread owning/opening threads, we may be racing against them
277 // closing their connection or their thread. That's okay, see below.
278 if (!conn
->connectionReady()) {
282 NS_NAMED_LITERAL_CSTRING(shrinkPragma
, "PRAGMA shrink_memory");
283 bool onOpenedThread
= false;
285 if (!conn
->operationSupported(Connection::SYNCHRONOUS
)) {
286 // This is a mozIStorageAsyncConnection, it can only be used on the main
287 // thread, so we can do a straight API call.
288 nsCOMPtr
<mozIStoragePendingStatement
> ps
;
289 DebugOnly
<nsresult
> rv
= conn
->ExecuteSimpleSQLAsync(
290 shrinkPragma
, nullptr, getter_AddRefs(ps
));
291 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Should have purged sqlite caches");
292 } else if (NS_SUCCEEDED(
293 conn
->threadOpenedOn
->IsOnCurrentThread(&onOpenedThread
)) &&
295 if (conn
->isAsyncExecutionThreadAvailable()) {
296 nsCOMPtr
<mozIStoragePendingStatement
> ps
;
297 DebugOnly
<nsresult
> rv
= conn
->ExecuteSimpleSQLAsync(
298 shrinkPragma
, nullptr, getter_AddRefs(ps
));
299 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Should have purged sqlite caches");
301 conn
->ExecuteSimpleSQL(shrinkPragma
);
304 // We are on the wrong thread, the query should be executed on the
305 // opener thread, so we must dispatch to it.
306 // It's possible the connection is already closed or will be closed by the
307 // time our runnable runs. ExecuteSimpleSQL will safely return with a
308 // failure in that case. If the thread is shutting down or shut down, the
309 // dispatch will fail and that's okay.
310 nsCOMPtr
<nsIRunnable
> event
= NewRunnableMethod
<const nsCString
>(
311 "Connection::ExecuteSimpleSQL", conn
, &Connection::ExecuteSimpleSQL
,
313 Unused
<< conn
->threadOpenedOn
->Dispatch(event
, NS_DISPATCH_NORMAL
);
318 sqlite3_vfs
* ConstructTelemetryVFS();
319 const char* GetVFSName();
321 static const char* sObserverTopics
[] = {"memory-pressure",
322 "xpcom-shutdown-threads"};
324 nsresult
Service::initialize() {
325 MOZ_ASSERT(NS_IsMainThread(), "Must be initialized on the main thread");
327 int rc
= AutoSQLiteLifetime::getInitResult();
328 if (rc
!= SQLITE_OK
) return convertResultCode(rc
);
330 mSqliteVFS
= ConstructTelemetryVFS();
332 rc
= sqlite3_vfs_register(mSqliteVFS
, 0);
333 if (rc
!= SQLITE_OK
) return convertResultCode(rc
);
335 NS_WARNING("Failed to register telemetry VFS");
338 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
339 NS_ENSURE_TRUE(os
, NS_ERROR_FAILURE
);
341 for (size_t i
= 0; i
< ArrayLength(sObserverTopics
); ++i
) {
342 nsresult rv
= os
->AddObserver(this, sObserverTopics
[i
], false);
343 if (NS_WARN_IF(NS_FAILED(rv
))) {
348 // We need to obtain the toolkit.storage.synchronous preferences on the main
349 // thread because the preference service can only be accessed there. This
350 // is cached in the service for all future Open[Unshared]Database calls.
352 Preferences::GetInt(PREF_TS_SYNCHRONOUS
, PREF_TS_SYNCHRONOUS_DEFAULT
);
354 // We need to obtain the toolkit.storage.pageSize preferences on the main
355 // thread because the preference service can only be accessed there. This
356 // is cached in the service for all future Open[Unshared]Database calls.
358 Preferences::GetInt(PREF_TS_PAGESIZE
, PREF_TS_PAGESIZE_DEFAULT
);
360 mozilla::RegisterWeakMemoryReporter(this);
361 mozilla::RegisterStorageSQLiteDistinguishedAmount(
362 StorageSQLiteDistinguishedAmount
);
367 int Service::localeCompareStrings(const nsAString
& aStr1
,
368 const nsAString
& aStr2
,
369 int32_t aComparisonStrength
) {
370 // The implementation of nsICollation.CompareString() is platform-dependent.
371 // On Linux it's not thread-safe. It may not be on Windows and OS X either,
372 // but it's more difficult to tell. We therefore synchronize this method.
373 MutexAutoLock
mutex(mMutex
);
375 nsICollation
* coll
= getLocaleCollation();
377 NS_ERROR("Storage service has no collation");
382 nsresult rv
= coll
->CompareString(aComparisonStrength
, aStr1
, aStr2
, &res
);
384 NS_ERROR("Collation compare string failed");
391 nsICollation
* Service::getLocaleCollation() {
392 mMutex
.AssertCurrentThreadOwns();
394 if (mLocaleCollation
) return mLocaleCollation
;
396 nsCOMPtr
<nsICollationFactory
> collFact
=
397 do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID
);
399 NS_WARNING("Could not create collation factory");
403 nsresult rv
= collFact
->CreateCollation(getter_AddRefs(mLocaleCollation
));
405 NS_WARNING("Could not create collation");
409 return mLocaleCollation
;
412 ////////////////////////////////////////////////////////////////////////////////
413 //// mozIStorageService
416 Service::OpenSpecialDatabase(const char* aStorageKey
,
417 mozIStorageConnection
** _connection
) {
420 nsCOMPtr
<nsIFile
> storageFile
;
421 if (::strcmp(aStorageKey
, "memory") == 0) {
422 // just fall through with nullptr storageFile, this will cause the storage
423 // connection to use a memory DB.
425 return NS_ERROR_INVALID_ARG
;
428 RefPtr
<Connection
> msc
=
429 new Connection(this, SQLITE_OPEN_READWRITE
, Connection::SYNCHRONOUS
);
431 rv
= storageFile
? msc
->initialize(storageFile
) : msc
->initialize();
432 NS_ENSURE_SUCCESS(rv
, rv
);
434 msc
.forget(_connection
);
440 class AsyncInitDatabase final
: public Runnable
{
442 AsyncInitDatabase(Connection
* aConnection
, nsIFile
* aStorageFile
,
443 int32_t aGrowthIncrement
,
444 mozIStorageCompletionCallback
* aCallback
)
445 : Runnable("storage::AsyncInitDatabase"),
446 mConnection(aConnection
),
447 mStorageFile(aStorageFile
),
448 mGrowthIncrement(aGrowthIncrement
),
449 mCallback(aCallback
) {
450 MOZ_ASSERT(NS_IsMainThread());
453 NS_IMETHOD
Run() override
{
454 MOZ_ASSERT(!NS_IsMainThread());
455 nsresult rv
= mConnection
->initializeOnAsyncThread(mStorageFile
);
457 return DispatchResult(rv
, nullptr);
460 if (mGrowthIncrement
>= 0) {
461 // Ignore errors. In the future, we might wish to log them.
462 (void)mConnection
->SetGrowthIncrement(mGrowthIncrement
, EmptyCString());
465 return DispatchResult(
466 NS_OK
, NS_ISUPPORTS_CAST(mozIStorageAsyncConnection
*, mConnection
));
470 nsresult
DispatchResult(nsresult aStatus
, nsISupports
* aValue
) {
471 RefPtr
<CallbackComplete
> event
=
472 new CallbackComplete(aStatus
, aValue
, mCallback
.forget());
473 return NS_DispatchToMainThread(event
);
476 ~AsyncInitDatabase() {
477 NS_ReleaseOnMainThread("AsyncInitDatabase::mStorageFile",
478 mStorageFile
.forget());
479 NS_ReleaseOnMainThread("AsyncInitDatabase::mConnection",
480 mConnection
.forget());
482 // Generally, the callback will be released by CallbackComplete.
483 // However, if for some reason Run() is not executed, we still
484 // need to ensure that it is released here.
485 NS_ReleaseOnMainThread("AsyncInitDatabase::mCallback", mCallback
.forget());
488 RefPtr
<Connection
> mConnection
;
489 nsCOMPtr
<nsIFile
> mStorageFile
;
490 int32_t mGrowthIncrement
;
491 RefPtr
<mozIStorageCompletionCallback
> mCallback
;
497 Service::OpenAsyncDatabase(nsIVariant
* aDatabaseStore
,
498 nsIPropertyBag2
* aOptions
,
499 mozIStorageCompletionCallback
* aCallback
) {
500 if (!NS_IsMainThread()) {
501 return NS_ERROR_NOT_SAME_THREAD
;
503 NS_ENSURE_ARG(aDatabaseStore
);
504 NS_ENSURE_ARG(aCallback
);
508 bool readOnly
= false;
509 bool ignoreLockingMode
= false;
510 int32_t growthIncrement
= -1;
512 #define FAIL_IF_SET_BUT_INVALID(rv) \
513 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) { \
514 return NS_ERROR_INVALID_ARG; \
517 // Deal with options first:
519 rv
= aOptions
->GetPropertyAsBool(NS_LITERAL_STRING("readOnly"), &readOnly
);
520 FAIL_IF_SET_BUT_INVALID(rv
);
522 rv
= aOptions
->GetPropertyAsBool(NS_LITERAL_STRING("ignoreLockingMode"),
524 FAIL_IF_SET_BUT_INVALID(rv
);
525 // Specifying ignoreLockingMode will force use of the readOnly flag:
526 if (ignoreLockingMode
) {
530 rv
= aOptions
->GetPropertyAsBool(NS_LITERAL_STRING("shared"), &shared
);
531 FAIL_IF_SET_BUT_INVALID(rv
);
533 // NB: we re-set to -1 if we don't have a storage file later on.
534 rv
= aOptions
->GetPropertyAsInt32(NS_LITERAL_STRING("growthIncrement"),
536 FAIL_IF_SET_BUT_INVALID(rv
);
538 int flags
= readOnly
? SQLITE_OPEN_READONLY
: SQLITE_OPEN_READWRITE
;
540 nsCOMPtr
<nsIFile
> storageFile
;
541 nsCOMPtr
<nsISupports
> dbStore
;
542 rv
= aDatabaseStore
->GetAsISupports(getter_AddRefs(dbStore
));
543 if (NS_SUCCEEDED(rv
)) {
544 // Generally, aDatabaseStore holds the database nsIFile.
545 storageFile
= do_QueryInterface(dbStore
, &rv
);
547 return NS_ERROR_INVALID_ARG
;
550 nsCOMPtr
<nsIFile
> cloned
;
551 rv
= storageFile
->Clone(getter_AddRefs(cloned
));
552 MOZ_ASSERT(NS_SUCCEEDED(rv
));
553 storageFile
= std::move(cloned
);
556 // Ensure that SQLITE_OPEN_CREATE is passed in for compatibility reasons.
557 flags
|= SQLITE_OPEN_CREATE
;
560 // Apply the shared-cache option.
561 flags
|= shared
? SQLITE_OPEN_SHAREDCACHE
: SQLITE_OPEN_PRIVATECACHE
;
563 // Sometimes, however, it's a special database name.
564 nsAutoCString keyString
;
565 rv
= aDatabaseStore
->GetAsACString(keyString
);
566 if (NS_FAILED(rv
) || !keyString
.EqualsLiteral("memory")) {
567 return NS_ERROR_INVALID_ARG
;
570 // Just fall through with nullptr storageFile, this will cause the storage
571 // connection to use a memory DB.
574 if (!storageFile
&& growthIncrement
>= 0) {
575 return NS_ERROR_INVALID_ARG
;
578 // Create connection on this thread, but initialize it on its helper thread.
579 RefPtr
<Connection
> msc
=
580 new Connection(this, flags
, Connection::ASYNCHRONOUS
, ignoreLockingMode
);
581 nsCOMPtr
<nsIEventTarget
> target
= msc
->getAsyncExecutionTarget();
583 "Cannot initialize a connection that has been closed already");
585 RefPtr
<AsyncInitDatabase
> asyncInit
=
586 new AsyncInitDatabase(msc
, storageFile
, growthIncrement
, aCallback
);
587 return target
->Dispatch(asyncInit
, nsIEventTarget::DISPATCH_NORMAL
);
591 Service::OpenDatabase(nsIFile
* aDatabaseFile
,
592 mozIStorageConnection
** _connection
) {
593 NS_ENSURE_ARG(aDatabaseFile
);
595 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
598 SQLITE_OPEN_READWRITE
| SQLITE_OPEN_SHAREDCACHE
| SQLITE_OPEN_CREATE
;
599 RefPtr
<Connection
> msc
= new Connection(this, flags
, Connection::SYNCHRONOUS
);
601 nsresult rv
= msc
->initialize(aDatabaseFile
);
602 NS_ENSURE_SUCCESS(rv
, rv
);
604 msc
.forget(_connection
);
609 Service::OpenUnsharedDatabase(nsIFile
* aDatabaseFile
,
610 mozIStorageConnection
** _connection
) {
611 NS_ENSURE_ARG(aDatabaseFile
);
613 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
616 SQLITE_OPEN_READWRITE
| SQLITE_OPEN_PRIVATECACHE
| SQLITE_OPEN_CREATE
;
617 RefPtr
<Connection
> msc
= new Connection(this, flags
, Connection::SYNCHRONOUS
);
619 nsresult rv
= msc
->initialize(aDatabaseFile
);
620 NS_ENSURE_SUCCESS(rv
, rv
);
622 msc
.forget(_connection
);
627 Service::OpenDatabaseWithFileURL(nsIFileURL
* aFileURL
,
628 mozIStorageConnection
** _connection
) {
629 NS_ENSURE_ARG(aFileURL
);
631 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
633 int flags
= SQLITE_OPEN_READWRITE
| SQLITE_OPEN_SHAREDCACHE
|
634 SQLITE_OPEN_CREATE
| SQLITE_OPEN_URI
;
635 RefPtr
<Connection
> msc
= new Connection(this, flags
, Connection::SYNCHRONOUS
);
637 nsresult rv
= msc
->initialize(aFileURL
);
638 NS_ENSURE_SUCCESS(rv
, rv
);
640 msc
.forget(_connection
);
645 Service::BackupDatabaseFile(nsIFile
* aDBFile
, const nsAString
& aBackupFileName
,
646 nsIFile
* aBackupParentDirectory
, nsIFile
** backup
) {
648 nsCOMPtr
<nsIFile
> parentDir
= aBackupParentDirectory
;
650 // This argument is optional, and defaults to the same parent directory
651 // as the current file.
652 rv
= aDBFile
->GetParent(getter_AddRefs(parentDir
));
653 NS_ENSURE_SUCCESS(rv
, rv
);
656 nsCOMPtr
<nsIFile
> backupDB
;
657 rv
= parentDir
->Clone(getter_AddRefs(backupDB
));
658 NS_ENSURE_SUCCESS(rv
, rv
);
660 rv
= backupDB
->Append(aBackupFileName
);
661 NS_ENSURE_SUCCESS(rv
, rv
);
663 rv
= backupDB
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0600);
664 NS_ENSURE_SUCCESS(rv
, rv
);
666 nsAutoString fileName
;
667 rv
= backupDB
->GetLeafName(fileName
);
668 NS_ENSURE_SUCCESS(rv
, rv
);
670 rv
= backupDB
->Remove(false);
671 NS_ENSURE_SUCCESS(rv
, rv
);
673 backupDB
.forget(backup
);
675 return aDBFile
->CopyTo(parentDir
, fileName
);
678 ////////////////////////////////////////////////////////////////////////////////
682 Service::Observe(nsISupports
*, const char* aTopic
, const char16_t
*) {
683 if (strcmp(aTopic
, "memory-pressure") == 0) {
685 } else if (strcmp(aTopic
, "xpcom-shutdown-threads") == 0) {
686 // The Service is kept alive by our strong observer references and
687 // references held by Connection instances. Since we're about to remove the
688 // former and then wait for the latter ones to go away, it behooves us to
689 // hold a strong reference to ourselves so our calls to getConnections() do
690 // not happen on a deleted object.
691 RefPtr
<Service
> kungFuDeathGrip
= this;
693 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
695 for (size_t i
= 0; i
< ArrayLength(sObserverTopics
); ++i
) {
696 (void)os
->RemoveObserver(this, sObserverTopics
[i
]);
699 SpinEventLoopUntil([&]() -> bool {
700 // We must wait until all the closing connections are closed.
701 nsTArray
<RefPtr
<Connection
>> connections
;
702 getConnections(connections
);
703 for (auto& conn
: connections
) {
704 if (conn
->isClosing()) {
712 nsTArray
<RefPtr
<Connection
>> connections
;
713 getConnections(connections
);
714 for (uint32_t i
= 0, n
= connections
.Length(); i
< n
; i
++) {
715 if (!connections
[i
]->isClosed()) {
716 // getFilename is only the leaf name for the database file,
717 // so it shouldn't contain privacy-sensitive information.
718 CrashReporter::AnnotateCrashReport(
719 CrashReporter::Annotation::StorageConnectionNotClosed
,
720 connections
[i
]->getFilename());
721 printf_stderr("Storage connection not closed: %s",
722 connections
[i
]->getFilename().get());
732 } // namespace storage
733 } // namespace mozilla