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/. */
8 #include "mozilla/Attributes.h"
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/SpinEventLoopUntil.h"
12 #include "nsIFileURL.h"
13 #include "mozStorageService.h"
14 #include "mozStorageConnection.h"
15 #include "nsComponentManagerUtils.h"
16 #include "nsEmbedCID.h"
17 #include "nsExceptionHandler.h"
18 #include "nsThreadUtils.h"
19 #include "mozStoragePrivateHelpers.h"
20 #include "nsIObserverService.h"
21 #include "nsIPropertyBag2.h"
22 #include "ObfuscatingVFS.h"
24 #include "mozilla/Services.h"
25 #include "mozilla/LateWriteChecks.h"
26 #include "mozIStorageCompletionCallback.h"
27 #include "mozIStoragePendingStatement.h"
28 #include "mozilla/StaticPrefs_storage.h"
29 #include "mozilla/intl/Collator.h"
30 #include "mozilla/intl/LocaleService.h"
33 #include "mozilla/AutoSQLiteLifetime.h"
36 // "windows.h" was included and it can #define lots of things we care about...
40 using mozilla::intl::Collator
;
42 namespace mozilla::storage
{
44 ////////////////////////////////////////////////////////////////////////////////
48 mozilla::Atomic
<size_t> gSqliteMemoryUsed
;
51 static int64_t StorageSQLiteDistinguishedAmount() {
52 return ::sqlite3_memory_used();
56 * Passes a single SQLite memory statistic to a memory reporter callback.
58 * @param aHandleReport
61 * The data for the callback.
63 * The SQLite connection.
65 * Head of the path for the memory report.
67 * The memory report statistic kind, one of "stmt", "cache" or
70 * The memory report description.
72 * The SQLite constant for getting the measurement.
74 * The accumulator for the measurement.
76 static void ReportConn(nsIHandleReportCallback
* aHandleReport
,
77 nsISupports
* aData
, Connection
* aConn
,
78 const nsACString
& aPathHead
, const nsACString
& aKind
,
79 const nsACString
& aDesc
, int32_t aOption
,
81 nsCString
path(aPathHead
);
83 path
.AppendLiteral("-used");
85 int32_t val
= aConn
->getSqliteRuntimeStatus(aOption
);
86 aHandleReport
->Callback(""_ns
, path
, nsIMemoryReporter::KIND_HEAP
,
87 nsIMemoryReporter::UNITS_BYTES
, int64_t(val
), aDesc
,
92 // Warning: To get a Connection's measurements requires holding its lock.
93 // There may be a delay getting the lock if another thread is accessing the
94 // Connection. This isn't very nice if CollectReports is called from the main
95 // thread! But at the time of writing this function is only called when
96 // about:memory is loaded (not, for example, when telemetry pings occur) and
97 // any delays in that case aren't so bad.
99 Service::CollectReports(nsIHandleReportCallback
* aHandleReport
,
100 nsISupports
* aData
, bool aAnonymize
) {
101 size_t totalConnSize
= 0;
103 nsTArray
<RefPtr
<Connection
>> connections
;
104 getConnections(connections
);
106 for (uint32_t i
= 0; i
< connections
.Length(); i
++) {
107 RefPtr
<Connection
>& conn
= connections
[i
];
109 // Someone may have closed the Connection, in which case we skip it.
110 // Note that we have consumers of the synchronous API that are off the
111 // main-thread, like the DOM Cache and IndexedDB, and as such we must be
112 // sure that we have a connection.
113 MutexAutoLock
lockedAsyncScope(conn
->sharedAsyncExecutionMutex
);
114 if (!conn
->connectionReady()) {
118 nsCString
pathHead("explicit/storage/sqlite/");
119 // This filename isn't privacy-sensitive, and so is never anonymized.
120 pathHead
.Append(conn
->getFilename());
121 pathHead
.Append('/');
123 SQLiteMutexAutoLock
lockedScope(conn
->sharedDBMutex
);
125 constexpr auto stmtDesc
=
126 "Memory (approximate) used by all prepared statements used by "
127 "connections to this database."_ns
;
128 ReportConn(aHandleReport
, aData
, conn
, pathHead
, "stmt"_ns
, stmtDesc
,
129 SQLITE_DBSTATUS_STMT_USED
, &totalConnSize
);
131 constexpr auto cacheDesc
=
132 "Memory (approximate) used by all pager caches used by connections "
133 "to this database."_ns
;
134 ReportConn(aHandleReport
, aData
, conn
, pathHead
, "cache"_ns
, cacheDesc
,
135 SQLITE_DBSTATUS_CACHE_USED_SHARED
, &totalConnSize
);
137 constexpr auto schemaDesc
=
138 "Memory (approximate) used to store the schema for all databases "
139 "associated with connections to this database."_ns
;
140 ReportConn(aHandleReport
, aData
, conn
, pathHead
, "schema"_ns
, schemaDesc
,
141 SQLITE_DBSTATUS_SCHEMA_USED
, &totalConnSize
);
145 if (::sqlite3_memory_used() != int64_t(gSqliteMemoryUsed
)) {
147 "memory consumption reported by SQLite doesn't match "
153 int64_t other
= static_cast<int64_t>(::sqlite3_memory_used() - totalConnSize
);
155 MOZ_COLLECT_REPORT("explicit/storage/sqlite/other", KIND_HEAP
, UNITS_BYTES
,
156 other
, "All unclassified sqlite memory.");
161 ////////////////////////////////////////////////////////////////////////////////
164 NS_IMPL_ISUPPORTS(Service
, mozIStorageService
, nsIObserver
, nsIMemoryReporter
)
166 Service
* Service::gService
= nullptr;
168 already_AddRefed
<Service
> Service::getSingleton() {
170 return do_AddRef(gService
);
173 // The first reference to the storage service must be obtained on the
175 NS_ENSURE_TRUE(NS_IsMainThread(), nullptr);
176 RefPtr
<Service
> service
= new Service();
177 if (NS_SUCCEEDED(service
->initialize())) {
178 // Note: This is cleared in the Service destructor.
179 gService
= service
.get();
180 return service
.forget();
186 int Service::AutoVFSRegistration::Init(UniquePtr
<sqlite3_vfs
> aVFS
) {
189 mVFS
= std::move(aVFS
);
190 return sqlite3_vfs_register(mVFS
.get(), 0);
192 NS_WARNING("Failed to register VFS");
196 Service::AutoVFSRegistration::~AutoVFSRegistration() {
198 int rc
= sqlite3_vfs_unregister(mVFS
.get());
199 if (rc
!= SQLITE_OK
) {
200 NS_WARNING("Failed to unregister sqlite vfs wrapper.");
206 : mMutex("Service::mMutex"),
207 mRegistrationMutex("Service::mRegistrationMutex"),
208 mLastSensitivity(mozilla::intl::Collator::Sensitivity::Base
) {}
210 Service::~Service() {
211 mozilla::UnregisterWeakMemoryReporter(this);
212 mozilla::UnregisterStorageSQLiteDistinguishedAmount();
217 void Service::registerConnection(Connection
* aConnection
) {
218 mRegistrationMutex
.AssertNotCurrentThreadOwns();
219 MutexAutoLock
mutex(mRegistrationMutex
);
220 (void)mConnections
.AppendElement(aConnection
);
223 void Service::unregisterConnection(Connection
* aConnection
) {
224 // If this is the last Connection it might be the only thing keeping Service
225 // alive. So ensure that Service is destroyed only after the Connection is
226 // cleanly unregistered and destroyed.
227 RefPtr
<Service
> kungFuDeathGrip(this);
228 RefPtr
<Connection
> forgettingRef
;
230 mRegistrationMutex
.AssertNotCurrentThreadOwns();
231 MutexAutoLock
mutex(mRegistrationMutex
);
233 for (uint32_t i
= 0; i
< mConnections
.Length(); ++i
) {
234 if (mConnections
[i
] == aConnection
) {
235 // Because dropping the final reference can potentially result in
236 // spinning a nested event loop if the connection was not properly
237 // shutdown, we want to do that outside this loop so that we can finish
238 // mutating the array and drop our mutex.
239 forgettingRef
= std::move(mConnections
[i
]);
240 mConnections
.RemoveElementAt(i
);
246 MOZ_ASSERT(forgettingRef
,
247 "Attempt to unregister unknown storage connection!");
249 // Do not proxy the release anywhere, just let this reference drop here. (We
250 // previously did proxy the release, but that was because we invoked Close()
251 // in the destructor and Close() likes to complain if it's not invoked on the
252 // opener event target, so it was essential that the last reference be dropped
253 // on the opener event target. We now enqueue Close() inside our caller,
254 // Release(), so it doesn't actually matter what thread our reference drops
258 void Service::getConnections(
259 /* inout */ nsTArray
<RefPtr
<Connection
>>& aConnections
) {
260 mRegistrationMutex
.AssertNotCurrentThreadOwns();
261 MutexAutoLock
mutex(mRegistrationMutex
);
262 aConnections
.Clear();
263 aConnections
.AppendElements(mConnections
);
266 void Service::minimizeMemory() {
267 nsTArray
<RefPtr
<Connection
>> connections
;
268 getConnections(connections
);
270 for (uint32_t i
= 0; i
< connections
.Length(); i
++) {
271 RefPtr
<Connection
> conn
= connections
[i
];
272 // For non-main-thread owning/opening threads, we may be racing against them
273 // closing their connection or their thread. That's okay, see below.
274 if (!conn
->connectionReady()) {
278 constexpr auto shrinkPragma
= "PRAGMA shrink_memory"_ns
;
280 if (!conn
->operationSupported(Connection::SYNCHRONOUS
)) {
281 // This is a mozIStorageAsyncConnection, it can only be used on the main
282 // thread, so we can do a straight API call.
283 nsCOMPtr
<mozIStoragePendingStatement
> ps
;
284 DebugOnly
<nsresult
> rv
= conn
->ExecuteSimpleSQLAsync(
285 shrinkPragma
, nullptr, getter_AddRefs(ps
));
286 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Should have purged sqlite caches");
287 } else if (IsOnCurrentSerialEventTarget(conn
->eventTargetOpenedOn
)) {
288 if (conn
->isAsyncExecutionThreadAvailable()) {
289 nsCOMPtr
<mozIStoragePendingStatement
> ps
;
290 DebugOnly
<nsresult
> rv
= conn
->ExecuteSimpleSQLAsync(
291 shrinkPragma
, nullptr, getter_AddRefs(ps
));
292 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Should have purged sqlite caches");
294 conn
->ExecuteSimpleSQL(shrinkPragma
);
297 // We are on the wrong event target, the query should be executed on the
298 // opener event target, so we must dispatch to it.
299 // It's possible the connection is already closed or will be closed by the
300 // time our runnable runs. ExecuteSimpleSQL will safely return with a
301 // failure in that case. If the event target is shutting down or shut
302 // down, the dispatch will fail and that's okay.
303 nsCOMPtr
<nsIRunnable
> event
= NewRunnableMethod
<const nsCString
>(
304 "Connection::ExecuteSimpleSQL", conn
, &Connection::ExecuteSimpleSQL
,
306 Unused
<< conn
->eventTargetOpenedOn
->Dispatch(event
, NS_DISPATCH_NORMAL
);
311 UniquePtr
<sqlite3_vfs
> ConstructReadOnlyNoLockVFS();
313 static const char* sObserverTopics
[] = {"memory-pressure",
314 "xpcom-shutdown-threads"};
316 nsresult
Service::initialize() {
317 MOZ_ASSERT(NS_IsMainThread(), "Must be initialized on the main thread");
319 int rc
= AutoSQLiteLifetime::getInitResult();
320 if (rc
!= SQLITE_OK
) {
321 return convertResultCode(rc
);
325 * The virtual file system hierarchy
337 * base-vfs-excl base-vfs
341 * unix-excl win32 unix win32
344 rc
= mBaseSqliteVFS
.Init(basevfs::ConstructVFS(false));
345 if (rc
!= SQLITE_OK
) {
346 return convertResultCode(rc
);
349 rc
= mBaseExclSqliteVFS
.Init(basevfs::ConstructVFS(true));
350 if (rc
!= SQLITE_OK
) {
351 return convertResultCode(rc
);
354 rc
= mQuotaSqliteVFS
.Init(quotavfs::ConstructVFS(basevfs::GetVFSName(
355 StaticPrefs::storage_sqlite_exclusiveLock_enabled())));
356 if (rc
!= SQLITE_OK
) {
357 return convertResultCode(rc
);
361 mObfuscatingSqliteVFS
.Init(obfsvfs::ConstructVFS(quotavfs::GetVFSName()));
362 if (rc
!= SQLITE_OK
) {
363 return convertResultCode(rc
);
366 rc
= mReadOnlyNoLockSqliteVFS
.Init(ConstructReadOnlyNoLockVFS());
367 if (rc
!= SQLITE_OK
) {
368 return convertResultCode(rc
);
371 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
372 NS_ENSURE_TRUE(os
, NS_ERROR_FAILURE
);
374 for (auto& sObserverTopic
: sObserverTopics
) {
375 nsresult rv
= os
->AddObserver(this, sObserverTopic
, false);
376 if (NS_WARN_IF(NS_FAILED(rv
))) {
381 mozilla::RegisterWeakMemoryReporter(this);
382 mozilla::RegisterStorageSQLiteDistinguishedAmount(
383 StorageSQLiteDistinguishedAmount
);
388 int Service::localeCompareStrings(const nsAString
& aStr1
,
389 const nsAString
& aStr2
,
390 Collator::Sensitivity aSensitivity
) {
391 // The mozilla::intl::Collator is not thread safe, since the Collator::Options
393 MutexAutoLock
mutex(mMutex
);
395 Collator
* collator
= getCollator();
397 NS_ERROR("Storage service has no collation");
401 if (aSensitivity
!= mLastSensitivity
) {
402 Collator::Options options
{};
403 options
.sensitivity
= aSensitivity
;
404 auto result
= mCollator
->SetOptions(options
);
406 if (result
.isErr()) {
407 NS_WARNING("Could not configure the mozilla::intl::Collation.");
410 mLastSensitivity
= aSensitivity
;
413 return collator
->CompareStrings(aStr1
, aStr2
);
416 Collator
* Service::getCollator() {
417 mMutex
.AssertCurrentThreadOwns();
420 return mCollator
.get();
423 auto result
= mozilla::intl::LocaleService::TryCreateComponent
<Collator
>();
424 if (result
.isErr()) {
425 NS_WARNING("Could not create mozilla::intl::Collation.");
429 mCollator
= result
.unwrap();
431 // Sort in a case-insensitive way, where "base" letters are considered
432 // equal, e.g: a = á, a = A, a ≠b.
433 Collator::Options options
{};
434 options
.sensitivity
= Collator::Sensitivity::Base
;
435 auto optResult
= mCollator
->SetOptions(options
);
437 if (optResult
.isErr()) {
438 NS_WARNING("Could not configure the mozilla::intl::Collation.");
443 return mCollator
.get();
446 ////////////////////////////////////////////////////////////////////////////////
447 //// mozIStorageService
450 Service::OpenSpecialDatabase(const nsACString
& aStorageKey
,
451 const nsACString
& aName
, uint32_t aConnectionFlags
,
452 mozIStorageConnection
** _connection
) {
453 if (!aStorageKey
.Equals(kMozStorageMemoryStorageKey
)) {
454 return NS_ERROR_INVALID_ARG
;
457 const bool interruptible
=
458 aConnectionFlags
& mozIStorageService::CONNECTION_INTERRUPTIBLE
;
460 int flags
= SQLITE_OPEN_READWRITE
;
462 if (!aName
.IsEmpty()) {
463 flags
|= SQLITE_OPEN_URI
;
466 RefPtr
<Connection
> msc
=
467 new Connection(this, flags
, Connection::SYNCHRONOUS
,
468 kMozStorageMemoryStorageKey
, interruptible
);
469 const nsresult rv
= msc
->initialize(aStorageKey
, aName
);
470 NS_ENSURE_SUCCESS(rv
, rv
);
472 msc
.forget(_connection
);
478 class AsyncInitDatabase final
: public Runnable
{
480 AsyncInitDatabase(Connection
* aConnection
, nsIFile
* aStorageFile
,
481 int32_t aGrowthIncrement
,
482 mozIStorageCompletionCallback
* aCallback
)
483 : Runnable("storage::AsyncInitDatabase"),
484 mConnection(aConnection
),
485 mStorageFile(aStorageFile
),
486 mGrowthIncrement(aGrowthIncrement
),
487 mCallback(aCallback
) {
488 MOZ_ASSERT(NS_IsMainThread());
491 NS_IMETHOD
Run() override
{
492 MOZ_ASSERT(!NS_IsMainThread());
493 nsresult rv
= mConnection
->initializeOnAsyncThread(mStorageFile
);
495 return DispatchResult(rv
, nullptr);
498 if (mGrowthIncrement
>= 0) {
499 // Ignore errors. In the future, we might wish to log them.
500 (void)mConnection
->SetGrowthIncrement(mGrowthIncrement
, ""_ns
);
503 return DispatchResult(
504 NS_OK
, NS_ISUPPORTS_CAST(mozIStorageAsyncConnection
*, mConnection
));
508 nsresult
DispatchResult(nsresult aStatus
, nsISupports
* aValue
) {
509 RefPtr
<CallbackComplete
> event
=
510 new CallbackComplete(aStatus
, aValue
, mCallback
.forget());
511 return NS_DispatchToMainThread(event
);
514 ~AsyncInitDatabase() {
515 NS_ReleaseOnMainThread("AsyncInitDatabase::mStorageFile",
516 mStorageFile
.forget());
517 NS_ReleaseOnMainThread("AsyncInitDatabase::mConnection",
518 mConnection
.forget());
520 // Generally, the callback will be released by CallbackComplete.
521 // However, if for some reason Run() is not executed, we still
522 // need to ensure that it is released here.
523 NS_ReleaseOnMainThread("AsyncInitDatabase::mCallback", mCallback
.forget());
526 RefPtr
<Connection
> mConnection
;
527 nsCOMPtr
<nsIFile
> mStorageFile
;
528 int32_t mGrowthIncrement
;
529 RefPtr
<mozIStorageCompletionCallback
> mCallback
;
535 Service::OpenAsyncDatabase(nsIVariant
* aDatabaseStore
, uint32_t aOpenFlags
,
536 uint32_t /* aConnectionFlags */,
537 mozIStorageCompletionCallback
* aCallback
) {
538 if (!NS_IsMainThread()) {
539 return NS_ERROR_NOT_SAME_THREAD
;
541 NS_ENSURE_ARG(aDatabaseStore
);
542 NS_ENSURE_ARG(aCallback
);
544 const bool shared
= aOpenFlags
& mozIStorageService::OPEN_SHARED
;
545 const bool ignoreLockingMode
=
546 aOpenFlags
& mozIStorageService::OPEN_IGNORE_LOCKING_MODE
;
547 // Specifying ignoreLockingMode will force use of the readOnly flag:
548 const bool readOnly
=
549 ignoreLockingMode
|| (aOpenFlags
& mozIStorageService::OPEN_READONLY
);
550 int flags
= readOnly
? SQLITE_OPEN_READONLY
: SQLITE_OPEN_READWRITE
;
552 nsCOMPtr
<nsIFile
> storageFile
;
553 nsCOMPtr
<nsISupports
> dbStore
;
554 nsresult rv
= aDatabaseStore
->GetAsISupports(getter_AddRefs(dbStore
));
555 if (NS_SUCCEEDED(rv
)) {
556 // Generally, aDatabaseStore holds the database nsIFile.
557 storageFile
= do_QueryInterface(dbStore
, &rv
);
559 return NS_ERROR_INVALID_ARG
;
562 nsCOMPtr
<nsIFile
> cloned
;
563 rv
= storageFile
->Clone(getter_AddRefs(cloned
));
564 MOZ_ASSERT(NS_SUCCEEDED(rv
));
565 storageFile
= std::move(cloned
);
568 // Ensure that SQLITE_OPEN_CREATE is passed in for compatibility reasons.
569 flags
|= SQLITE_OPEN_CREATE
;
572 // Apply the shared-cache option.
573 flags
|= shared
? SQLITE_OPEN_SHAREDCACHE
: SQLITE_OPEN_PRIVATECACHE
;
575 // Sometimes, however, it's a special database name.
576 nsAutoCString keyString
;
577 rv
= aDatabaseStore
->GetAsACString(keyString
);
578 if (NS_FAILED(rv
) || !keyString
.Equals(kMozStorageMemoryStorageKey
)) {
579 return NS_ERROR_INVALID_ARG
;
582 // Just fall through with nullptr storageFile, this will cause the storage
583 // connection to use a memory DB.
586 // Create connection on this thread, but initialize it on its helper thread.
587 nsAutoCString telemetryFilename
;
589 telemetryFilename
.Assign(kMozStorageMemoryStorageKey
);
591 rv
= storageFile
->GetNativeLeafName(telemetryFilename
);
592 NS_ENSURE_SUCCESS(rv
, rv
);
594 RefPtr
<Connection
> msc
=
595 new Connection(this, flags
, Connection::ASYNCHRONOUS
, telemetryFilename
,
596 /* interruptible */ true, ignoreLockingMode
);
597 nsCOMPtr
<nsIEventTarget
> target
= msc
->getAsyncExecutionTarget();
599 "Cannot initialize a connection that has been closed already");
601 RefPtr
<AsyncInitDatabase
> asyncInit
= new AsyncInitDatabase(
602 msc
, storageFile
, /* growthIncrement */ -1, aCallback
);
603 return target
->Dispatch(asyncInit
, nsIEventTarget::DISPATCH_NORMAL
);
607 Service::OpenDatabase(nsIFile
* aDatabaseFile
, uint32_t aConnectionFlags
,
608 mozIStorageConnection
** _connection
) {
609 NS_ENSURE_ARG(aDatabaseFile
);
611 const bool interruptible
=
612 aConnectionFlags
& mozIStorageService::CONNECTION_INTERRUPTIBLE
;
614 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
617 SQLITE_OPEN_READWRITE
| SQLITE_OPEN_SHAREDCACHE
| SQLITE_OPEN_CREATE
;
618 nsAutoCString telemetryFilename
;
619 nsresult rv
= aDatabaseFile
->GetNativeLeafName(telemetryFilename
);
620 NS_ENSURE_SUCCESS(rv
, rv
);
621 RefPtr
<Connection
> msc
= new Connection(this, flags
, Connection::SYNCHRONOUS
,
622 telemetryFilename
, interruptible
);
623 rv
= msc
->initialize(aDatabaseFile
);
624 NS_ENSURE_SUCCESS(rv
, rv
);
626 msc
.forget(_connection
);
631 Service::OpenUnsharedDatabase(nsIFile
* aDatabaseFile
, uint32_t aConnectionFlags
,
632 mozIStorageConnection
** _connection
) {
633 NS_ENSURE_ARG(aDatabaseFile
);
635 const bool interruptible
=
636 aConnectionFlags
& mozIStorageService::CONNECTION_INTERRUPTIBLE
;
638 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
641 SQLITE_OPEN_READWRITE
| SQLITE_OPEN_PRIVATECACHE
| SQLITE_OPEN_CREATE
;
642 nsAutoCString telemetryFilename
;
643 nsresult rv
= aDatabaseFile
->GetNativeLeafName(telemetryFilename
);
644 NS_ENSURE_SUCCESS(rv
, rv
);
645 RefPtr
<Connection
> msc
= new Connection(this, flags
, Connection::SYNCHRONOUS
,
646 telemetryFilename
, interruptible
);
647 rv
= msc
->initialize(aDatabaseFile
);
648 NS_ENSURE_SUCCESS(rv
, rv
);
650 msc
.forget(_connection
);
655 Service::OpenDatabaseWithFileURL(nsIFileURL
* aFileURL
,
656 const nsACString
& aTelemetryFilename
,
657 uint32_t aConnectionFlags
,
658 mozIStorageConnection
** _connection
) {
659 NS_ENSURE_ARG(aFileURL
);
661 const bool interruptible
=
662 aConnectionFlags
& mozIStorageService::CONNECTION_INTERRUPTIBLE
;
664 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
666 const int flags
= SQLITE_OPEN_READWRITE
| SQLITE_OPEN_SHAREDCACHE
|
667 SQLITE_OPEN_CREATE
| SQLITE_OPEN_URI
;
669 nsAutoCString telemetryFilename
;
670 if (!aTelemetryFilename
.IsEmpty()) {
671 telemetryFilename
= aTelemetryFilename
;
673 nsCOMPtr
<nsIFile
> databaseFile
;
674 rv
= aFileURL
->GetFile(getter_AddRefs(databaseFile
));
675 NS_ENSURE_SUCCESS(rv
, rv
);
676 rv
= databaseFile
->GetNativeLeafName(telemetryFilename
);
677 NS_ENSURE_SUCCESS(rv
, rv
);
679 RefPtr
<Connection
> msc
= new Connection(this, flags
, Connection::SYNCHRONOUS
,
680 telemetryFilename
, interruptible
);
681 rv
= msc
->initialize(aFileURL
);
682 NS_ENSURE_SUCCESS(rv
, rv
);
684 msc
.forget(_connection
);
688 ////////////////////////////////////////////////////////////////////////////////
692 Service::Observe(nsISupports
*, const char* aTopic
, const char16_t
*) {
693 if (strcmp(aTopic
, "memory-pressure") == 0) {
695 } else if (strcmp(aTopic
, "xpcom-shutdown-threads") == 0) {
696 // The Service is kept alive by our strong observer references and
697 // references held by Connection instances. Since we're about to remove the
698 // former and then wait for the latter ones to go away, it behooves us to
699 // hold a strong reference to ourselves so our calls to getConnections() do
700 // not happen on a deleted object.
701 RefPtr
<Service
> kungFuDeathGrip
= this;
703 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
705 for (auto& sObserverTopic
: sObserverTopics
) {
706 (void)os
->RemoveObserver(this, sObserverTopic
);
709 SpinEventLoopUntil("storage::Service::Observe(xpcom-shutdown-threads)"_ns
,
711 // We must wait until all the closing connections are
713 nsTArray
<RefPtr
<Connection
>> connections
;
714 getConnections(connections
);
715 for (auto& conn
: connections
) {
716 if (conn
->isClosing()) {
724 nsTArray
<RefPtr
<Connection
>> connections
;
725 getConnections(connections
);
726 for (uint32_t i
= 0, n
= connections
.Length(); i
< n
; i
++) {
727 if (!connections
[i
]->isClosed()) {
728 // getFilename is only the leaf name for the database file,
729 // so it shouldn't contain privacy-sensitive information.
730 CrashReporter::RecordAnnotationNSCString(
731 CrashReporter::Annotation::StorageConnectionNotClosed
,
732 connections
[i
]->getFilename());
733 printf_stderr("Storage connection not closed: %s",
734 connections
[i
]->getFilename().get());
744 } // namespace mozilla::storage