Merge mozilla-central to autoland. a=merge CLOSED TREE
[gecko.git] / storage / mozStorageService.cpp
blob82a3a7344dff1de7a4120c54124cde71b3139fec
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 "BaseVFS.h"
8 #include "mozilla/Attributes.h"
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/SpinEventLoopUntil.h"
11 #include "nsIFile.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"
23 #include "QuotaVFS.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"
32 #include "sqlite3.h"
33 #include "mozilla/AutoSQLiteLifetime.h"
35 #ifdef XP_WIN
36 // "windows.h" was included and it can #define lots of things we care about...
37 # undef CompareString
38 #endif
40 using mozilla::intl::Collator;
42 namespace mozilla::storage {
44 ////////////////////////////////////////////////////////////////////////////////
45 //// Memory Reporting
47 #ifdef MOZ_DMD
48 mozilla::Atomic<size_t> gSqliteMemoryUsed;
49 #endif
51 static int64_t StorageSQLiteDistinguishedAmount() {
52 return ::sqlite3_memory_used();
55 /**
56 * Passes a single SQLite memory statistic to a memory reporter callback.
58 * @param aHandleReport
59 * The callback.
60 * @param aData
61 * The data for the callback.
62 * @param aConn
63 * The SQLite connection.
64 * @param aPathHead
65 * Head of the path for the memory report.
66 * @param aKind
67 * The memory report statistic kind, one of "stmt", "cache" or
68 * "schema".
69 * @param aDesc
70 * The memory report description.
71 * @param aOption
72 * The SQLite constant for getting the measurement.
73 * @param aTotal
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,
80 size_t* aTotal) {
81 nsCString path(aPathHead);
82 path.Append(aKind);
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,
88 aData);
89 *aTotal += val;
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.
98 NS_IMETHODIMP
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()) {
115 continue;
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);
144 #ifdef MOZ_DMD
145 if (::sqlite3_memory_used() != int64_t(gSqliteMemoryUsed)) {
146 NS_WARNING(
147 "memory consumption reported by SQLite doesn't match "
148 "our measurements");
150 #endif
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.");
158 return NS_OK;
161 ////////////////////////////////////////////////////////////////////////////////
162 //// Service
164 NS_IMPL_ISUPPORTS(Service, mozIStorageService, nsIObserver, nsIMemoryReporter)
166 Service* Service::gService = nullptr;
168 already_AddRefed<Service> Service::getSingleton() {
169 if (gService) {
170 return do_AddRef(gService);
173 // The first reference to the storage service must be obtained on the
174 // main thread.
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();
183 return nullptr;
186 int Service::AutoVFSRegistration::Init(UniquePtr<sqlite3_vfs> aVFS) {
187 MOZ_ASSERT(!mVFS);
188 if (aVFS) {
189 mVFS = std::move(aVFS);
190 return sqlite3_vfs_register(mVFS.get(), 0);
192 NS_WARNING("Failed to register VFS");
193 return SQLITE_OK;
196 Service::AutoVFSRegistration::~AutoVFSRegistration() {
197 if (mVFS) {
198 int rc = sqlite3_vfs_unregister(mVFS.get());
199 if (rc != SQLITE_OK) {
200 NS_WARNING("Failed to unregister sqlite vfs wrapper.");
205 Service::Service()
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();
214 gService = nullptr;
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);
241 break;
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
255 // on.)
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()) {
275 continue;
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");
293 } else {
294 conn->ExecuteSimpleSQL(shrinkPragma);
296 } else {
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,
305 shrinkPragma);
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
327 * obfsvfs
331 * quotavfs
332 * / \
333 * / \
334 * / \
335 * / \
336 * / \
337 * base-vfs-excl base-vfs
338 * / \ / \
339 * / \ / \
340 * / \ / \
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);
360 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))) {
377 return rv;
381 mozilla::RegisterWeakMemoryReporter(this);
382 mozilla::RegisterStorageSQLiteDistinguishedAmount(
383 StorageSQLiteDistinguishedAmount);
385 return NS_OK;
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
392 // can be changed.
393 MutexAutoLock mutex(mMutex);
395 Collator* collator = getCollator();
396 if (!collator) {
397 NS_ERROR("Storage service has no collation");
398 return 0;
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.");
408 return 0;
410 mLastSensitivity = aSensitivity;
413 return collator->CompareStrings(aStr1, aStr2);
416 Collator* Service::getCollator() {
417 mMutex.AssertCurrentThreadOwns();
419 if (mCollator) {
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.");
426 return nullptr;
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.");
439 mCollator = nullptr;
440 return nullptr;
443 return mCollator.get();
446 ////////////////////////////////////////////////////////////////////////////////
447 //// mozIStorageService
449 NS_IMETHODIMP
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);
473 return NS_OK;
476 namespace {
478 class AsyncInitDatabase final : public Runnable {
479 public:
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);
494 if (NS_FAILED(rv)) {
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));
507 private:
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;
532 } // namespace
534 NS_IMETHODIMP
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);
558 if (NS_FAILED(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);
567 if (!readOnly) {
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;
574 } else {
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;
588 if (!storageFile) {
589 telemetryFilename.Assign(kMozStorageMemoryStorageKey);
590 } else {
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();
598 MOZ_ASSERT(target,
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);
606 NS_IMETHODIMP
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
615 // reasons.
616 const int flags =
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);
627 return NS_OK;
630 NS_IMETHODIMP
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
639 // reasons.
640 const int flags =
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);
651 return NS_OK;
654 NS_IMETHODIMP
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
665 // reasons.
666 const int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE |
667 SQLITE_OPEN_CREATE | SQLITE_OPEN_URI;
668 nsresult rv;
669 nsAutoCString telemetryFilename;
670 if (!aTelemetryFilename.IsEmpty()) {
671 telemetryFilename = aTelemetryFilename;
672 } else {
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);
685 return NS_OK;
688 ////////////////////////////////////////////////////////////////////////////////
689 //// nsIObserver
691 NS_IMETHODIMP
692 Service::Observe(nsISupports*, const char* aTopic, const char16_t*) {
693 if (strcmp(aTopic, "memory-pressure") == 0) {
694 minimizeMemory();
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,
710 [&]() -> bool {
711 // We must wait until all the closing connections are
712 // closed.
713 nsTArray<RefPtr<Connection>> connections;
714 getConnections(connections);
715 for (auto& conn : connections) {
716 if (conn->isClosing()) {
717 return false;
720 return true;
723 #ifdef DEBUG
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());
735 MOZ_CRASH();
738 #endif
741 return NS_OK;
744 } // namespace mozilla::storage