Bug 1454293 [wpt PR 10484] - null is not the correct origin for createDocument()...
[gecko.git] / storage / mozStorageService.cpp
blobd4e52ff71173d080a96f90768d03fa3f96b96806
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"
27 #include "sqlite3.h"
28 #include "mozilla/AutoSQLiteLifetime.h"
30 #ifdef XP_WIN
31 // "windows.h" was included and it can #define lots of things we care about...
32 #undef CompareString
33 #endif
35 #include "nsIPromptService.h"
37 ////////////////////////////////////////////////////////////////////////////////
38 //// Defines
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
49 namespace mozilla {
50 namespace storage {
52 ////////////////////////////////////////////////////////////////////////////////
53 //// Memory Reporting
55 #ifdef MOZ_DMD
56 static mozilla::Atomic<size_t> gSqliteMemoryUsed;
57 #endif
59 static int64_t
60 StorageSQLiteDistinguishedAmount()
62 return ::sqlite3_memory_used();
65 /**
66 * Passes a single SQLite memory statistic to a memory reporter callback.
68 * @param aHandleReport
69 * The callback.
70 * @param aData
71 * The data for the callback.
72 * @param aConn
73 * The SQLite connection.
74 * @param aPathHead
75 * Head of the path for the memory report.
76 * @param aKind
77 * The memory report statistic kind, one of "stmt", "cache" or
78 * "schema".
79 * @param aDesc
80 * The memory report description.
81 * @param aOption
82 * The SQLite constant for getting the measurement.
83 * @param aTotal
84 * The accumulator for the measurement.
86 static void
87 ReportConn(nsIHandleReportCallback *aHandleReport,
88 nsISupports *aData,
89 Connection *aConn,
90 const nsACString &aPathHead,
91 const nsACString &aKind,
92 const nsACString &aDesc,
93 int32_t aOption,
94 size_t *aTotal)
96 nsCString path(aPathHead);
97 path.Append(aKind);
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);
105 *aTotal += val;
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.
114 NS_IMETHODIMP
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()) {
132 continue;
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);
164 #ifdef MOZ_DMD
165 if (::sqlite3_memory_used() != int64_t(gSqliteMemoryUsed)) {
166 NS_WARNING("memory consumption reported by SQLite doesn't match "
167 "our measurements");
169 #endif
172 int64_t other = ::sqlite3_memory_used() - totalConnSize;
174 MOZ_COLLECT_REPORT(
175 "explicit/storage/sqlite/other", KIND_HEAP, UNITS_BYTES, other,
176 "All unclassified sqlite memory.");
178 return NS_OK;
181 ////////////////////////////////////////////////////////////////////////////////
182 //// Service
184 NS_IMPL_ISUPPORTS(
185 Service,
186 mozIStorageService,
187 nsIObserver,
188 nsIMemoryReporter
191 Service *Service::gService = nullptr;
193 already_AddRefed<Service>
194 Service::getSingleton()
196 if (gService) {
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
202 // at compile time.
203 if (SQLITE_VERSION_NUMBER > ::sqlite3_libversion_number()) {
204 nsCOMPtr<nsIPromptService> ps(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
205 if (ps) {
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
219 // main thread.
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();
228 return nullptr;
231 int32_t Service::sSynchronousPref;
233 // static
234 int32_t
235 Service::getSynchronousPref()
237 return sSynchronousPref;
240 int32_t Service::sDefaultPageSize = PREF_TS_PAGESIZE_DEFAULT;
242 Service::Service()
243 : mMutex("Service::mMutex")
244 , mSqliteVFS(nullptr)
245 , mRegistrationMutex("Service::mRegistrationMutex")
246 , mConnections()
250 Service::~Service()
252 mozilla::UnregisterWeakMemoryReporter(this);
253 mozilla::UnregisterStorageSQLiteDistinguishedAmount();
255 int rc = sqlite3_vfs_unregister(mSqliteVFS);
256 if (rc != SQLITE_OK)
257 NS_WARNING("Failed to unregister sqlite vfs wrapper.");
259 gService = nullptr;
260 delete mSqliteVFS;
261 mSqliteVFS = nullptr;
264 void
265 Service::registerConnection(Connection *aConnection)
267 mRegistrationMutex.AssertNotCurrentThreadOwns();
268 MutexAutoLock mutex(mRegistrationMutex);
269 (void)mConnections.AppendElement(aConnection);
272 void
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);
292 break;
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.)
308 void
309 Service::getConnections(/* inout */ nsTArray<RefPtr<Connection> >& aConnections)
311 mRegistrationMutex.AssertNotCurrentThreadOwns();
312 MutexAutoLock mutex(mRegistrationMutex);
313 aConnections.Clear();
314 aConnections.AppendElements(mConnections);
317 void
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())
328 continue;
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;
335 if (!syncConn) {
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)) &&
343 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");
349 } else {
350 conn->ExecuteSimpleSQL(shrinkPragma);
352 } else {
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[] = {
372 "memory-pressure",
373 "xpcom-shutdown-threads"
376 nsresult
377 Service::initialize()
379 MOZ_ASSERT(NS_IsMainThread(), "Must be initialized on the main thread");
381 int rc = AutoSQLiteLifetime::getInitResult();
382 if (rc != SQLITE_OK)
383 return convertResultCode(rc);
385 mSqliteVFS = ConstructTelemetryVFS();
386 if (mSqliteVFS) {
387 rc = sqlite3_vfs_register(mSqliteVFS, 0);
388 if (rc != SQLITE_OK)
389 return convertResultCode(rc);
390 } else {
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))) {
400 return 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.
407 sSynchronousPref =
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.
413 sDefaultPageSize =
414 Preferences::GetInt(PREF_TS_PAGESIZE, PREF_TS_PAGESIZE_DEFAULT);
416 mozilla::RegisterWeakMemoryReporter(this);
417 mozilla::RegisterStorageSQLiteDistinguishedAmount(StorageSQLiteDistinguishedAmount);
419 return NS_OK;
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();
433 if (!coll) {
434 NS_ERROR("Storage service has no collation");
435 return 0;
438 int32_t res;
439 nsresult rv = coll->CompareString(aComparisonStrength, aStr1, aStr2, &res);
440 if (NS_FAILED(rv)) {
441 NS_ERROR("Collation compare string failed");
442 return 0;
445 return res;
448 nsICollation *
449 Service::getLocaleCollation()
451 mMutex.AssertCurrentThreadOwns();
453 if (mLocaleCollation)
454 return mLocaleCollation;
456 nsCOMPtr<nsICollationFactory> collFact =
457 do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID);
458 if (!collFact) {
459 NS_WARNING("Could not create collation factory");
460 return nullptr;
463 nsresult rv = collFact->CreateCollation(getter_AddRefs(mLocaleCollation));
464 if (NS_FAILED(rv)) {
465 NS_WARNING("Could not create collation");
466 return nullptr;
469 return mLocaleCollation;
472 ////////////////////////////////////////////////////////////////////////////////
473 //// mozIStorageService
476 NS_IMETHODIMP
477 Service::OpenSpecialDatabase(const char *aStorageKey,
478 mozIStorageConnection **_connection)
480 nsresult rv;
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.
487 else {
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);
497 return NS_OK;
501 namespace {
503 class AsyncInitDatabase final : public Runnable
505 public:
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);
523 if (NS_FAILED(rv)) {
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*,
533 mConnection));
536 private:
537 nsresult DispatchResult(nsresult aStatus, nsISupports* aValue) {
538 RefPtr<CallbackComplete> event =
539 new CallbackComplete(aStatus,
540 aValue,
541 mCallback.forget());
542 return NS_DispatchToMainThread(event);
545 ~AsyncInitDatabase()
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;
565 } // namespace
567 NS_IMETHODIMP
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);
578 nsresult rv;
579 bool shared = false;
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:
590 if (aOptions) {
591 rv = aOptions->GetPropertyAsBool(NS_LITERAL_STRING("readOnly"), &readOnly);
592 FAIL_IF_SET_BUT_INVALID(rv);
594 rv = aOptions->GetPropertyAsBool(NS_LITERAL_STRING("ignoreLockingMode"),
595 &ignoreLockingMode);
596 FAIL_IF_SET_BUT_INVALID(rv);
597 // Specifying ignoreLockingMode will force use of the readOnly flag:
598 if (ignoreLockingMode) {
599 readOnly = true;
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"),
607 &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);
618 if (NS_FAILED(rv)) {
619 return NS_ERROR_INVALID_ARG;
622 rv = storageFile->Clone(getter_AddRefs(storageFile));
623 MOZ_ASSERT(NS_SUCCEEDED(rv));
625 if (!readOnly) {
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;
632 } else {
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,
650 ignoreLockingMode);
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,
656 storageFile,
657 growthIncrement,
658 aCallback);
659 return target->Dispatch(asyncInit, nsIEventTarget::DISPATCH_NORMAL);
662 NS_IMETHODIMP
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
669 // reasons.
670 int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE |
671 SQLITE_OPEN_CREATE;
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);
678 return NS_OK;
681 NS_IMETHODIMP
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
688 // reasons.
689 int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_PRIVATECACHE |
690 SQLITE_OPEN_CREATE;
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);
697 return NS_OK;
700 NS_IMETHODIMP
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
707 // reasons.
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);
716 return NS_OK;
719 NS_IMETHODIMP
720 Service::BackupDatabaseFile(nsIFile *aDBFile,
721 const nsAString &aBackupFileName,
722 nsIFile *aBackupParentDirectory,
723 nsIFile **backup)
725 nsresult rv;
726 nsCOMPtr<nsIFile> parentDir = aBackupParentDirectory;
727 if (!parentDir) {
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 ////////////////////////////////////////////////////////////////////////////////
757 //// nsIObserver
759 NS_IMETHODIMP
760 Service::Observe(nsISupports *, const char *aTopic, const char16_t *)
762 if (strcmp(aTopic, "memory-pressure") == 0) {
763 minimizeMemory();
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()) {
785 return false;
788 return true;
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());
801 #ifdef DEBUG
802 printf_stderr("Storage connection not closed: %s",
803 connections[i]->getFilename().get());
804 #endif
805 MOZ_CRASH();
811 return NS_OK;
814 } // namespace storage
815 } // namespace mozilla