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 "nsAutoPtr.h"
13 #include "nsCollationCID.h"
14 #include "nsEmbedCID.h"
15 #include "nsExceptionHandler.h"
16 #include "nsThreadUtils.h"
17 #include "mozStoragePrivateHelpers.h"
18 #include "nsIXPConnect.h"
19 #include "nsIObserverService.h"
20 #include "nsIPropertyBag2.h"
21 #include "mozilla/Services.h"
22 #include "mozilla/Preferences.h"
23 #include "mozilla/LateWriteChecks.h"
24 #include "mozIStorageCompletionCallback.h"
25 #include "mozIStoragePendingStatement.h"
28 #include "mozilla/AutoSQLiteLifetime.h"
31 // "windows.h" was included and it can #define lots of things we care about...
35 #include "nsIPromptService.h"
37 ////////////////////////////////////////////////////////////////////////////////
40 #define PREF_TS_SYNCHRONOUS "toolkit.storage.synchronous"
41 #define PREF_TS_SYNCHRONOUS_DEFAULT 1
43 #define PREF_TS_PAGESIZE "toolkit.storage.pageSize"
45 // This value must be kept in sync with the value of SQLITE_DEFAULT_PAGE_SIZE in
46 // db/sqlite3/src/Makefile.in.
47 #define PREF_TS_PAGESIZE_DEFAULT 32768
52 ////////////////////////////////////////////////////////////////////////////////
56 static mozilla::Atomic
<size_t> gSqliteMemoryUsed
;
60 StorageSQLiteDistinguishedAmount()
62 return ::sqlite3_memory_used();
66 * Passes a single SQLite memory statistic to a memory reporter callback.
68 * @param aHandleReport
71 * The data for the callback.
73 * The SQLite connection.
75 * Head of the path for the memory report.
77 * The memory report statistic kind, one of "stmt", "cache" or
80 * The memory report description.
82 * The SQLite constant for getting the measurement.
84 * The accumulator for the measurement.
87 ReportConn(nsIHandleReportCallback
*aHandleReport
,
90 const nsACString
&aPathHead
,
91 const nsACString
&aKind
,
92 const nsACString
&aDesc
,
96 nsCString
path(aPathHead
);
98 path
.AppendLiteral("-used");
100 int32_t val
= aConn
->getSqliteRuntimeStatus(aOption
);
101 aHandleReport
->Callback(EmptyCString(), path
,
102 nsIMemoryReporter::KIND_HEAP
,
103 nsIMemoryReporter::UNITS_BYTES
,
104 int64_t(val
), aDesc
, aData
);
108 // Warning: To get a Connection's measurements requires holding its lock.
109 // There may be a delay getting the lock if another thread is accessing the
110 // Connection. This isn't very nice if CollectReports is called from the main
111 // thread! But at the time of writing this function is only called when
112 // about:memory is loaded (not, for example, when telemetry pings occur) and
113 // any delays in that case aren't so bad.
115 Service::CollectReports(nsIHandleReportCallback
*aHandleReport
,
116 nsISupports
*aData
, bool aAnonymize
)
118 size_t totalConnSize
= 0;
120 nsTArray
<RefPtr
<Connection
> > connections
;
121 getConnections(connections
);
123 for (uint32_t i
= 0; i
< connections
.Length(); i
++) {
124 RefPtr
<Connection
> &conn
= connections
[i
];
126 // Someone may have closed the Connection, in which case we skip it.
127 // Note that we have consumers of the synchronous API that are off the
128 // main-thread, like the DOM Cache and IndexedDB, and as such we must be
129 // sure that we have a connection.
130 MutexAutoLock
lockedAsyncScope(conn
->sharedAsyncExecutionMutex
);
131 if (!conn
->connectionReady()) {
135 nsCString
pathHead("explicit/storage/sqlite/");
136 // This filename isn't privacy-sensitive, and so is never anonymized.
137 pathHead
.Append(conn
->getFilename());
138 pathHead
.Append('/');
140 SQLiteMutexAutoLock
lockedScope(conn
->sharedDBMutex
);
142 NS_NAMED_LITERAL_CSTRING(stmtDesc
,
143 "Memory (approximate) used by all prepared statements used by "
144 "connections to this database.");
145 ReportConn(aHandleReport
, aData
, conn
, pathHead
,
146 NS_LITERAL_CSTRING("stmt"), stmtDesc
,
147 SQLITE_DBSTATUS_STMT_USED
, &totalConnSize
);
149 NS_NAMED_LITERAL_CSTRING(cacheDesc
,
150 "Memory (approximate) used by all pager caches used by connections "
151 "to this database.");
152 ReportConn(aHandleReport
, aData
, conn
, pathHead
,
153 NS_LITERAL_CSTRING("cache"), cacheDesc
,
154 SQLITE_DBSTATUS_CACHE_USED_SHARED
, &totalConnSize
);
156 NS_NAMED_LITERAL_CSTRING(schemaDesc
,
157 "Memory (approximate) used to store the schema for all databases "
158 "associated with connections to this database.");
159 ReportConn(aHandleReport
, aData
, conn
, pathHead
,
160 NS_LITERAL_CSTRING("schema"), schemaDesc
,
161 SQLITE_DBSTATUS_SCHEMA_USED
, &totalConnSize
);
165 if (::sqlite3_memory_used() != int64_t(gSqliteMemoryUsed
)) {
166 NS_WARNING("memory consumption reported by SQLite doesn't match "
172 int64_t other
= ::sqlite3_memory_used() - totalConnSize
;
175 "explicit/storage/sqlite/other", KIND_HEAP
, UNITS_BYTES
, other
,
176 "All unclassified sqlite memory.");
181 ////////////////////////////////////////////////////////////////////////////////
191 Service
*Service::gService
= nullptr;
193 already_AddRefed
<Service
>
194 Service::getSingleton()
197 return do_AddRef(gService
);
200 // Ensure that we are using the same version of SQLite that we compiled with
201 // or newer. Our configure check ensures we are using a new enough version
203 if (SQLITE_VERSION_NUMBER
> ::sqlite3_libversion_number()) {
204 nsCOMPtr
<nsIPromptService
> ps(do_GetService(NS_PROMPTSERVICE_CONTRACTID
));
206 nsAutoString title
, message
;
207 title
.AppendLiteral("SQLite Version Error");
208 message
.AppendLiteral("The application has been updated, but the SQLite "
209 "library wasn't updated properly and the application "
210 "cannot run. Please try to launch the application again. "
211 "If that should still fail, please try reinstalling "
212 "it, or visit https://support.mozilla.org/.");
213 (void)ps
->Alert(nullptr, title
.get(), message
.get());
215 MOZ_CRASH("SQLite Version Error");
218 // The first reference to the storage service must be obtained on the
220 NS_ENSURE_TRUE(NS_IsMainThread(), nullptr);
221 RefPtr
<Service
> service
= new Service();
222 if (NS_SUCCEEDED(service
->initialize())) {
223 // Note: This is cleared in the Service destructor.
224 gService
= service
.get();
225 return service
.forget();
231 int32_t Service::sSynchronousPref
;
235 Service::getSynchronousPref()
237 return sSynchronousPref
;
240 int32_t Service::sDefaultPageSize
= PREF_TS_PAGESIZE_DEFAULT
;
243 : mMutex("Service::mMutex")
244 , mSqliteVFS(nullptr)
245 , mRegistrationMutex("Service::mRegistrationMutex")
252 mozilla::UnregisterWeakMemoryReporter(this);
253 mozilla::UnregisterStorageSQLiteDistinguishedAmount();
255 int rc
= sqlite3_vfs_unregister(mSqliteVFS
);
257 NS_WARNING("Failed to unregister sqlite vfs wrapper.");
261 mSqliteVFS
= nullptr;
265 Service::registerConnection(Connection
*aConnection
)
267 mRegistrationMutex
.AssertNotCurrentThreadOwns();
268 MutexAutoLock
mutex(mRegistrationMutex
);
269 (void)mConnections
.AppendElement(aConnection
);
273 Service::unregisterConnection(Connection
*aConnection
)
275 // If this is the last Connection it might be the only thing keeping Service
276 // alive. So ensure that Service is destroyed only after the Connection is
277 // cleanly unregistered and destroyed.
278 RefPtr
<Service
> kungFuDeathGrip(this);
279 RefPtr
<Connection
> forgettingRef
;
281 mRegistrationMutex
.AssertNotCurrentThreadOwns();
282 MutexAutoLock
mutex(mRegistrationMutex
);
284 for (uint32_t i
= 0 ; i
< mConnections
.Length(); ++i
) {
285 if (mConnections
[i
] == aConnection
) {
286 // Because dropping the final reference can potentially result in
287 // spinning a nested event loop if the connection was not properly
288 // shutdown, we want to do that outside this loop so that we can finish
289 // mutating the array and drop our mutex.
290 forgettingRef
= mConnections
[i
].forget();
291 mConnections
.RemoveElementAt(i
);
297 MOZ_ASSERT(forgettingRef
,
298 "Attempt to unregister unknown storage connection!");
300 // Do not proxy the release anywhere, just let this reference drop here. (We
301 // previously did proxy the release, but that was because we invoked Close()
302 // in the destructor and Close() likes to complain if it's not invoked on the
303 // opener thread, so it was essential that the last reference be dropped on
304 // the opener thread. We now enqueue Close() inside our caller, Release(), so
305 // it doesn't actually matter what thread our reference drops on.)
309 Service::getConnections(/* inout */ nsTArray
<RefPtr
<Connection
> >& aConnections
)
311 mRegistrationMutex
.AssertNotCurrentThreadOwns();
312 MutexAutoLock
mutex(mRegistrationMutex
);
313 aConnections
.Clear();
314 aConnections
.AppendElements(mConnections
);
318 Service::minimizeMemory()
320 nsTArray
<RefPtr
<Connection
> > connections
;
321 getConnections(connections
);
323 for (uint32_t i
= 0; i
< connections
.Length(); i
++) {
324 RefPtr
<Connection
> conn
= connections
[i
];
325 // For non-main-thread owning/opening threads, we may be racing against them
326 // closing their connection or their thread. That's okay, see below.
327 if (!conn
->connectionReady())
330 NS_NAMED_LITERAL_CSTRING(shrinkPragma
, "PRAGMA shrink_memory");
331 nsCOMPtr
<mozIStorageConnection
> syncConn
= do_QueryInterface(
332 NS_ISUPPORTS_CAST(mozIStorageAsyncConnection
*, conn
));
333 bool onOpenedThread
= false;
336 // This is a mozIStorageAsyncConnection, it can only be used on the main
337 // thread, so we can do a straight API call.
338 nsCOMPtr
<mozIStoragePendingStatement
> ps
;
339 DebugOnly
<nsresult
> rv
=
340 conn
->ExecuteSimpleSQLAsync(shrinkPragma
, nullptr, getter_AddRefs(ps
));
341 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Should have purged sqlite caches");
342 } else if (NS_SUCCEEDED(conn
->threadOpenedOn
->IsOnCurrentThread(&onOpenedThread
)) &&
344 if (conn
->isAsyncExecutionThreadAvailable()) {
345 nsCOMPtr
<mozIStoragePendingStatement
> ps
;
346 DebugOnly
<nsresult
> rv
=
347 conn
->ExecuteSimpleSQLAsync(shrinkPragma
, nullptr, getter_AddRefs(ps
));
348 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Should have purged sqlite caches");
350 conn
->ExecuteSimpleSQL(shrinkPragma
);
353 // We are on the wrong thread, the query should be executed on the
354 // opener thread, so we must dispatch to it.
355 // It's possible the connection is already closed or will be closed by the
356 // time our runnable runs. ExecuteSimpleSQL will safely return with a
357 // failure in that case. If the thread is shutting down or shut down, the
358 // dispatch will fail and that's okay.
359 nsCOMPtr
<nsIRunnable
> event
=
360 NewRunnableMethod
<const nsCString
>(
361 "Connection::ExecuteSimpleSQL",
362 conn
, &Connection::ExecuteSimpleSQL
, shrinkPragma
);
363 Unused
<< conn
->threadOpenedOn
->Dispatch(event
, NS_DISPATCH_NORMAL
);
368 sqlite3_vfs
*ConstructTelemetryVFS();
369 const char *GetVFSName();
371 static const char* sObserverTopics
[] = {
373 "xpcom-shutdown-threads"
377 Service::initialize()
379 MOZ_ASSERT(NS_IsMainThread(), "Must be initialized on the main thread");
381 int rc
= AutoSQLiteLifetime::getInitResult();
383 return convertResultCode(rc
);
385 mSqliteVFS
= ConstructTelemetryVFS();
387 rc
= sqlite3_vfs_register(mSqliteVFS
, 0);
389 return convertResultCode(rc
);
391 NS_WARNING("Failed to register telemetry VFS");
394 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
395 NS_ENSURE_TRUE(os
, NS_ERROR_FAILURE
);
397 for (size_t i
= 0; i
< ArrayLength(sObserverTopics
); ++i
) {
398 nsresult rv
= os
->AddObserver(this, sObserverTopics
[i
], false);
399 if (NS_WARN_IF(NS_FAILED(rv
))) {
404 // We need to obtain the toolkit.storage.synchronous preferences on the main
405 // thread because the preference service can only be accessed there. This
406 // is cached in the service for all future Open[Unshared]Database calls.
408 Preferences::GetInt(PREF_TS_SYNCHRONOUS
, PREF_TS_SYNCHRONOUS_DEFAULT
);
410 // We need to obtain the toolkit.storage.pageSize preferences on the main
411 // thread because the preference service can only be accessed there. This
412 // is cached in the service for all future Open[Unshared]Database calls.
414 Preferences::GetInt(PREF_TS_PAGESIZE
, PREF_TS_PAGESIZE_DEFAULT
);
416 mozilla::RegisterWeakMemoryReporter(this);
417 mozilla::RegisterStorageSQLiteDistinguishedAmount(StorageSQLiteDistinguishedAmount
);
423 Service::localeCompareStrings(const nsAString
&aStr1
,
424 const nsAString
&aStr2
,
425 int32_t aComparisonStrength
)
427 // The implementation of nsICollation.CompareString() is platform-dependent.
428 // On Linux it's not thread-safe. It may not be on Windows and OS X either,
429 // but it's more difficult to tell. We therefore synchronize this method.
430 MutexAutoLock
mutex(mMutex
);
432 nsICollation
*coll
= getLocaleCollation();
434 NS_ERROR("Storage service has no collation");
439 nsresult rv
= coll
->CompareString(aComparisonStrength
, aStr1
, aStr2
, &res
);
441 NS_ERROR("Collation compare string failed");
449 Service::getLocaleCollation()
451 mMutex
.AssertCurrentThreadOwns();
453 if (mLocaleCollation
)
454 return mLocaleCollation
;
456 nsCOMPtr
<nsICollationFactory
> collFact
=
457 do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID
);
459 NS_WARNING("Could not create collation factory");
463 nsresult rv
= collFact
->CreateCollation(getter_AddRefs(mLocaleCollation
));
465 NS_WARNING("Could not create collation");
469 return mLocaleCollation
;
472 ////////////////////////////////////////////////////////////////////////////////
473 //// mozIStorageService
477 Service::OpenSpecialDatabase(const char *aStorageKey
,
478 mozIStorageConnection
**_connection
)
482 nsCOMPtr
<nsIFile
> storageFile
;
483 if (::strcmp(aStorageKey
, "memory") == 0) {
484 // just fall through with nullptr storageFile, this will cause the storage
485 // connection to use a memory DB.
488 return NS_ERROR_INVALID_ARG
;
491 RefPtr
<Connection
> msc
= new Connection(this, SQLITE_OPEN_READWRITE
, false);
493 rv
= storageFile
? msc
->initialize(storageFile
) : msc
->initialize();
494 NS_ENSURE_SUCCESS(rv
, rv
);
496 msc
.forget(_connection
);
503 class AsyncInitDatabase final
: public Runnable
506 AsyncInitDatabase(Connection
* aConnection
,
507 nsIFile
* aStorageFile
,
508 int32_t aGrowthIncrement
,
509 mozIStorageCompletionCallback
* aCallback
)
510 : Runnable("storage::AsyncInitDatabase")
511 , mConnection(aConnection
)
512 , mStorageFile(aStorageFile
)
513 , mGrowthIncrement(aGrowthIncrement
)
514 , mCallback(aCallback
)
516 MOZ_ASSERT(NS_IsMainThread());
519 NS_IMETHOD
Run() override
521 MOZ_ASSERT(!NS_IsMainThread());
522 nsresult rv
= mConnection
->initializeOnAsyncThread(mStorageFile
);
524 return DispatchResult(rv
, nullptr);
527 if (mGrowthIncrement
>= 0) {
528 // Ignore errors. In the future, we might wish to log them.
529 (void)mConnection
->SetGrowthIncrement(mGrowthIncrement
, EmptyCString());
532 return DispatchResult(NS_OK
, NS_ISUPPORTS_CAST(mozIStorageAsyncConnection
*,
537 nsresult
DispatchResult(nsresult aStatus
, nsISupports
* aValue
) {
538 RefPtr
<CallbackComplete
> event
=
539 new CallbackComplete(aStatus
,
542 return NS_DispatchToMainThread(event
);
547 NS_ReleaseOnMainThreadSystemGroup(
548 "AsyncInitDatabase::mStorageFile", mStorageFile
.forget());
549 NS_ReleaseOnMainThreadSystemGroup(
550 "AsyncInitDatabase::mConnection", mConnection
.forget());
552 // Generally, the callback will be released by CallbackComplete.
553 // However, if for some reason Run() is not executed, we still
554 // need to ensure that it is released here.
555 NS_ReleaseOnMainThreadSystemGroup(
556 "AsyncInitDatabase::mCallback", mCallback
.forget());
559 RefPtr
<Connection
> mConnection
;
560 nsCOMPtr
<nsIFile
> mStorageFile
;
561 int32_t mGrowthIncrement
;
562 RefPtr
<mozIStorageCompletionCallback
> mCallback
;
568 Service::OpenAsyncDatabase(nsIVariant
*aDatabaseStore
,
569 nsIPropertyBag2
*aOptions
,
570 mozIStorageCompletionCallback
*aCallback
)
572 if (!NS_IsMainThread()) {
573 return NS_ERROR_NOT_SAME_THREAD
;
575 NS_ENSURE_ARG(aDatabaseStore
);
576 NS_ENSURE_ARG(aCallback
);
580 bool readOnly
= false;
581 bool ignoreLockingMode
= false;
582 int32_t growthIncrement
= -1;
584 #define FAIL_IF_SET_BUT_INVALID(rv)\
585 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) { \
586 return NS_ERROR_INVALID_ARG; \
589 // Deal with options first:
591 rv
= aOptions
->GetPropertyAsBool(NS_LITERAL_STRING("readOnly"), &readOnly
);
592 FAIL_IF_SET_BUT_INVALID(rv
);
594 rv
= aOptions
->GetPropertyAsBool(NS_LITERAL_STRING("ignoreLockingMode"),
596 FAIL_IF_SET_BUT_INVALID(rv
);
597 // Specifying ignoreLockingMode will force use of the readOnly flag:
598 if (ignoreLockingMode
) {
602 rv
= aOptions
->GetPropertyAsBool(NS_LITERAL_STRING("shared"), &shared
);
603 FAIL_IF_SET_BUT_INVALID(rv
);
605 // NB: we re-set to -1 if we don't have a storage file later on.
606 rv
= aOptions
->GetPropertyAsInt32(NS_LITERAL_STRING("growthIncrement"),
608 FAIL_IF_SET_BUT_INVALID(rv
);
610 int flags
= readOnly
? SQLITE_OPEN_READONLY
: SQLITE_OPEN_READWRITE
;
612 nsCOMPtr
<nsIFile
> storageFile
;
613 nsCOMPtr
<nsISupports
> dbStore
;
614 rv
= aDatabaseStore
->GetAsISupports(getter_AddRefs(dbStore
));
615 if (NS_SUCCEEDED(rv
)) {
616 // Generally, aDatabaseStore holds the database nsIFile.
617 storageFile
= do_QueryInterface(dbStore
, &rv
);
619 return NS_ERROR_INVALID_ARG
;
622 rv
= storageFile
->Clone(getter_AddRefs(storageFile
));
623 MOZ_ASSERT(NS_SUCCEEDED(rv
));
626 // Ensure that SQLITE_OPEN_CREATE is passed in for compatibility reasons.
627 flags
|= SQLITE_OPEN_CREATE
;
630 // Apply the shared-cache option.
631 flags
|= shared
? SQLITE_OPEN_SHAREDCACHE
: SQLITE_OPEN_PRIVATECACHE
;
633 // Sometimes, however, it's a special database name.
634 nsAutoCString keyString
;
635 rv
= aDatabaseStore
->GetAsACString(keyString
);
636 if (NS_FAILED(rv
) || !keyString
.EqualsLiteral("memory")) {
637 return NS_ERROR_INVALID_ARG
;
640 // Just fall through with nullptr storageFile, this will cause the storage
641 // connection to use a memory DB.
644 if (!storageFile
&& growthIncrement
>= 0) {
645 return NS_ERROR_INVALID_ARG
;
648 // Create connection on this thread, but initialize it on its helper thread.
649 RefPtr
<Connection
> msc
= new Connection(this, flags
, true,
651 nsCOMPtr
<nsIEventTarget
> target
= msc
->getAsyncExecutionTarget();
652 MOZ_ASSERT(target
, "Cannot initialize a connection that has been closed already");
654 RefPtr
<AsyncInitDatabase
> asyncInit
=
655 new AsyncInitDatabase(msc
,
659 return target
->Dispatch(asyncInit
, nsIEventTarget::DISPATCH_NORMAL
);
663 Service::OpenDatabase(nsIFile
*aDatabaseFile
,
664 mozIStorageConnection
**_connection
)
666 NS_ENSURE_ARG(aDatabaseFile
);
668 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
670 int flags
= SQLITE_OPEN_READWRITE
| SQLITE_OPEN_SHAREDCACHE
|
672 RefPtr
<Connection
> msc
= new Connection(this, flags
, false);
674 nsresult rv
= msc
->initialize(aDatabaseFile
);
675 NS_ENSURE_SUCCESS(rv
, rv
);
677 msc
.forget(_connection
);
682 Service::OpenUnsharedDatabase(nsIFile
*aDatabaseFile
,
683 mozIStorageConnection
**_connection
)
685 NS_ENSURE_ARG(aDatabaseFile
);
687 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
689 int flags
= SQLITE_OPEN_READWRITE
| SQLITE_OPEN_PRIVATECACHE
|
691 RefPtr
<Connection
> msc
= new Connection(this, flags
, false);
693 nsresult rv
= msc
->initialize(aDatabaseFile
);
694 NS_ENSURE_SUCCESS(rv
, rv
);
696 msc
.forget(_connection
);
701 Service::OpenDatabaseWithFileURL(nsIFileURL
*aFileURL
,
702 mozIStorageConnection
**_connection
)
704 NS_ENSURE_ARG(aFileURL
);
706 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
708 int flags
= SQLITE_OPEN_READWRITE
| SQLITE_OPEN_SHAREDCACHE
|
709 SQLITE_OPEN_CREATE
| SQLITE_OPEN_URI
;
710 RefPtr
<Connection
> msc
= new Connection(this, flags
, false);
712 nsresult rv
= msc
->initialize(aFileURL
);
713 NS_ENSURE_SUCCESS(rv
, rv
);
715 msc
.forget(_connection
);
720 Service::BackupDatabaseFile(nsIFile
*aDBFile
,
721 const nsAString
&aBackupFileName
,
722 nsIFile
*aBackupParentDirectory
,
726 nsCOMPtr
<nsIFile
> parentDir
= aBackupParentDirectory
;
728 // This argument is optional, and defaults to the same parent directory
729 // as the current file.
730 rv
= aDBFile
->GetParent(getter_AddRefs(parentDir
));
731 NS_ENSURE_SUCCESS(rv
, rv
);
734 nsCOMPtr
<nsIFile
> backupDB
;
735 rv
= parentDir
->Clone(getter_AddRefs(backupDB
));
736 NS_ENSURE_SUCCESS(rv
, rv
);
738 rv
= backupDB
->Append(aBackupFileName
);
739 NS_ENSURE_SUCCESS(rv
, rv
);
741 rv
= backupDB
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0600);
742 NS_ENSURE_SUCCESS(rv
, rv
);
744 nsAutoString fileName
;
745 rv
= backupDB
->GetLeafName(fileName
);
746 NS_ENSURE_SUCCESS(rv
, rv
);
748 rv
= backupDB
->Remove(false);
749 NS_ENSURE_SUCCESS(rv
, rv
);
751 backupDB
.forget(backup
);
753 return aDBFile
->CopyTo(parentDir
, fileName
);
756 ////////////////////////////////////////////////////////////////////////////////
760 Service::Observe(nsISupports
*, const char *aTopic
, const char16_t
*)
762 if (strcmp(aTopic
, "memory-pressure") == 0) {
764 } else if (strcmp(aTopic
, "xpcom-shutdown-threads") == 0) {
765 // The Service is kept alive by our strong observer references and
766 // references held by Connection instances. Since we're about to remove the
767 // former and then wait for the latter ones to go away, it behooves us to
768 // hold a strong reference to ourselves so our calls to getConnections() do
769 // not happen on a deleted object.
770 RefPtr
<Service
> kungFuDeathGrip
= this;
772 nsCOMPtr
<nsIObserverService
> os
=
773 mozilla::services::GetObserverService();
775 for (size_t i
= 0; i
< ArrayLength(sObserverTopics
); ++i
) {
776 (void)os
->RemoveObserver(this, sObserverTopics
[i
]);
779 SpinEventLoopUntil([&]() -> bool {
780 // We must wait until all the closing connections are closed.
781 nsTArray
<RefPtr
<Connection
>> connections
;
782 getConnections(connections
);
783 for (auto& conn
: connections
) {
784 if (conn
->isClosing()) {
791 if (gShutdownChecks
== SCM_CRASH
) {
792 nsTArray
<RefPtr
<Connection
> > connections
;
793 getConnections(connections
);
794 for (uint32_t i
= 0, n
= connections
.Length(); i
< n
; i
++) {
795 if (!connections
[i
]->isClosed()) {
796 // getFilename is only the leaf name for the database file,
797 // so it shouldn't contain privacy-sensitive information.
798 CrashReporter::AnnotateCrashReport(
799 NS_LITERAL_CSTRING("StorageConnectionNotClosed"),
800 connections
[i
]->getFilename());
802 printf_stderr("Storage connection not closed: %s",
803 connections
[i
]->getFilename().get());
814 } // namespace storage
815 } // namespace mozilla