mozStorageService.cpp (26061B)
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/. */ 6 7 #include "BaseVFS.h" 8 #include "mozilla/DebugOnly.h" 9 #include "mozilla/SpinEventLoopUntil.h" 10 #include "nsIFile.h" 11 #include "nsIFileURL.h" 12 #include "mozStorageService.h" 13 #include "mozStorageConnection.h" 14 #include "nsComponentManagerUtils.h" 15 #include "nsEmbedCID.h" 16 #include "nsExceptionHandler.h" 17 #include "nsThreadUtils.h" 18 #include "mozStoragePrivateHelpers.h" 19 #include "nsIObserverService.h" 20 #include "nsIPropertyBag2.h" 21 #include "ObfuscatingVFS.h" 22 #include "QuotaVFS.h" 23 #include "mozilla/Services.h" 24 #include "mozilla/LateWriteChecks.h" 25 #include "mozIStorageCompletionCallback.h" 26 #include "mozIStoragePendingStatement.h" 27 #include "mozilla/StaticPrefs_storage.h" 28 #include "mozilla/intl/Collator.h" 29 #include "mozilla/intl/LocaleService.h" 30 31 #include "sqlite3.h" 32 #include "mozilla/AutoSQLiteLifetime.h" 33 34 #ifdef XP_WIN 35 // "windows.h" was included and it can #define lots of things we care about... 36 # undef CompareString 37 #endif 38 39 using mozilla::intl::Collator; 40 41 namespace mozilla::storage { 42 43 //////////////////////////////////////////////////////////////////////////////// 44 //// Memory Reporting 45 46 #ifdef MOZ_DMD 47 mozilla::Atomic<size_t> gSqliteMemoryUsed; 48 #endif 49 50 static int64_t StorageSQLiteDistinguishedAmount() { 51 return ::sqlite3_memory_used(); 52 } 53 54 /** 55 * Passes a single SQLite memory statistic to a memory reporter callback. 56 * 57 * @param aHandleReport 58 * The callback. 59 * @param aData 60 * The data for the callback. 61 * @param aConn 62 * The SQLite connection. 63 * @param aPathHead 64 * Head of the path for the memory report. 65 * @param aKind 66 * The memory report statistic kind, one of "stmt", "cache" or 67 * "schema". 68 * @param aDesc 69 * The memory report description. 70 * @param aOption 71 * The SQLite constant for getting the measurement. 72 * @param aTotal 73 * The accumulator for the measurement. 74 */ 75 static void ReportConn(nsIHandleReportCallback* aHandleReport, 76 nsISupports* aData, Connection* aConn, 77 const nsACString& aPathHead, const nsACString& aKind, 78 const nsACString& aDesc, int32_t aOption, 79 size_t* aTotal) { 80 nsCString path(aPathHead); 81 path.Append(aKind); 82 path.AppendLiteral("-used"); 83 84 int32_t val = aConn->getSqliteRuntimeStatus(aOption); 85 aHandleReport->Callback(""_ns, path, nsIMemoryReporter::KIND_HEAP, 86 nsIMemoryReporter::UNITS_BYTES, int64_t(val), aDesc, 87 aData); 88 *aTotal += val; 89 } 90 91 // Warning: To get a Connection's measurements requires holding its lock. 92 // There may be a delay getting the lock if another thread is accessing the 93 // Connection. This isn't very nice if CollectReports is called from the main 94 // thread! But at the time of writing this function is only called when 95 // about:memory is loaded (not, for example, when telemetry pings occur) and 96 // any delays in that case aren't so bad. 97 NS_IMETHODIMP 98 Service::CollectReports(nsIHandleReportCallback* aHandleReport, 99 nsISupports* aData, bool aAnonymize) { 100 size_t totalConnSize = 0; 101 { 102 nsTArray<RefPtr<Connection>> connections; 103 getConnections(connections); 104 105 for (uint32_t i = 0; i < connections.Length(); i++) { 106 RefPtr<Connection>& conn = connections[i]; 107 108 // Someone may have closed the Connection, in which case we skip it. 109 // Note that we have consumers of the synchronous API that are off the 110 // main-thread, like the DOM Cache and IndexedDB, and as such we must be 111 // sure that we have a connection. 112 MutexAutoLock lockedAsyncScope(conn->sharedAsyncExecutionMutex); 113 if (!conn->connectionReady()) { 114 continue; 115 } 116 117 nsCString pathHead("explicit/storage/sqlite/"); 118 // This filename isn't privacy-sensitive, and so is never anonymized. 119 pathHead.Append(conn->getFilename()); 120 pathHead.Append('/'); 121 122 SQLiteMutexAutoLock lockedScope(conn->sharedDBMutex); 123 124 constexpr auto stmtDesc = 125 "Memory (approximate) used by all prepared statements used by " 126 "connections to this database."_ns; 127 ReportConn(aHandleReport, aData, conn, pathHead, "stmt"_ns, stmtDesc, 128 SQLITE_DBSTATUS_STMT_USED, &totalConnSize); 129 130 constexpr auto cacheDesc = 131 "Memory (approximate) used by all pager caches used by connections " 132 "to this database."_ns; 133 ReportConn(aHandleReport, aData, conn, pathHead, "cache"_ns, cacheDesc, 134 SQLITE_DBSTATUS_CACHE_USED_SHARED, &totalConnSize); 135 136 constexpr auto schemaDesc = 137 "Memory (approximate) used to store the schema for all databases " 138 "associated with connections to this database."_ns; 139 ReportConn(aHandleReport, aData, conn, pathHead, "schema"_ns, schemaDesc, 140 SQLITE_DBSTATUS_SCHEMA_USED, &totalConnSize); 141 } 142 143 #ifdef MOZ_DMD 144 if (::sqlite3_memory_used() != int64_t(gSqliteMemoryUsed)) { 145 NS_WARNING( 146 "memory consumption reported by SQLite doesn't match " 147 "our measurements"); 148 } 149 #endif 150 } 151 152 int64_t other = static_cast<int64_t>(::sqlite3_memory_used() - totalConnSize); 153 154 MOZ_COLLECT_REPORT("explicit/storage/sqlite/other", KIND_HEAP, UNITS_BYTES, 155 other, "All unclassified sqlite memory."); 156 157 return NS_OK; 158 } 159 160 //////////////////////////////////////////////////////////////////////////////// 161 //// Service 162 163 NS_IMPL_ISUPPORTS(Service, mozIStorageService, nsIObserver, nsIMemoryReporter) 164 165 Service* Service::gService = nullptr; 166 167 already_AddRefed<Service> Service::getSingleton() { 168 if (gService) { 169 return do_AddRef(gService); 170 } 171 172 // The first reference to the storage service must be obtained on the 173 // main thread. 174 NS_ENSURE_TRUE(NS_IsMainThread(), nullptr); 175 RefPtr<Service> service = new Service(); 176 if (NS_SUCCEEDED(service->initialize())) { 177 // Note: This is cleared in the Service destructor. 178 gService = service.get(); 179 return service.forget(); 180 } 181 182 return nullptr; 183 } 184 185 int Service::AutoVFSRegistration::Init(UniquePtr<sqlite3_vfs> aVFS) { 186 MOZ_ASSERT(!mVFS); 187 if (aVFS) { 188 mVFS = std::move(aVFS); 189 return sqlite3_vfs_register(mVFS.get(), 0); 190 } 191 NS_WARNING("Failed to register VFS"); 192 return SQLITE_OK; 193 } 194 195 Service::AutoVFSRegistration::~AutoVFSRegistration() { 196 if (mVFS) { 197 int rc = sqlite3_vfs_unregister(mVFS.get()); 198 if (rc != SQLITE_OK) { 199 NS_WARNING("Failed to unregister sqlite vfs wrapper."); 200 } 201 } 202 } 203 204 Service::Service() 205 : mMutex("Service::mMutex"), 206 mRegistrationMutex("Service::mRegistrationMutex"), 207 mLastSensitivity(mozilla::intl::Collator::Sensitivity::Base) {} 208 209 Service::~Service() { 210 mozilla::UnregisterWeakMemoryReporter(this); 211 mozilla::UnregisterStorageSQLiteDistinguishedAmount(); 212 213 gService = nullptr; 214 } 215 216 void Service::registerConnection(Connection* aConnection) { 217 mRegistrationMutex.AssertNotCurrentThreadOwns(); 218 MutexAutoLock mutex(mRegistrationMutex); 219 (void)mConnections.AppendElement(aConnection); 220 } 221 222 void Service::unregisterConnection(Connection* aConnection) { 223 // If this is the last Connection it might be the only thing keeping Service 224 // alive. So ensure that Service is destroyed only after the Connection is 225 // cleanly unregistered and destroyed. 226 RefPtr<Service> kungFuDeathGrip(this); 227 RefPtr<Connection> forgettingRef; 228 { 229 mRegistrationMutex.AssertNotCurrentThreadOwns(); 230 MutexAutoLock mutex(mRegistrationMutex); 231 232 for (uint32_t i = 0; i < mConnections.Length(); ++i) { 233 if (mConnections[i] == aConnection) { 234 // Because dropping the final reference can potentially result in 235 // spinning a nested event loop if the connection was not properly 236 // shutdown, we want to do that outside this loop so that we can finish 237 // mutating the array and drop our mutex. 238 forgettingRef = std::move(mConnections[i]); 239 mConnections.RemoveElementAt(i); 240 break; 241 } 242 } 243 } 244 245 MOZ_ASSERT(forgettingRef, 246 "Attempt to unregister unknown storage connection!"); 247 248 // Do not proxy the release anywhere, just let this reference drop here. (We 249 // previously did proxy the release, but that was because we invoked Close() 250 // in the destructor and Close() likes to complain if it's not invoked on the 251 // opener event target, so it was essential that the last reference be dropped 252 // on the opener event target. We now enqueue Close() inside our caller, 253 // Release(), so it doesn't actually matter what thread our reference drops 254 // on.) 255 } 256 257 void Service::getConnections( 258 /* inout */ nsTArray<RefPtr<Connection>>& aConnections) { 259 mRegistrationMutex.AssertNotCurrentThreadOwns(); 260 MutexAutoLock mutex(mRegistrationMutex); 261 aConnections.Clear(); 262 aConnections.AppendElements(mConnections); 263 } 264 265 void Service::minimizeMemory() { 266 nsTArray<RefPtr<Connection>> connections; 267 getConnections(connections); 268 269 for (uint32_t i = 0; i < connections.Length(); i++) { 270 RefPtr<Connection> conn = connections[i]; 271 // For non-main-thread owning/opening threads, we may be racing against them 272 // closing their connection or their thread. That's okay, see below. 273 if (!conn->connectionReady()) { 274 continue; 275 } 276 277 constexpr auto shrinkPragma = "PRAGMA shrink_memory"_ns; 278 279 if (!conn->operationSupported(Connection::SYNCHRONOUS)) { 280 // This is a mozIStorageAsyncConnection, it can only be used on the main 281 // thread, so we can do a straight API call. 282 nsCOMPtr<mozIStoragePendingStatement> ps; 283 DebugOnly<nsresult> rv = conn->ExecuteSimpleSQLAsync( 284 shrinkPragma, nullptr, getter_AddRefs(ps)); 285 MOZ_ASSERT(NS_SUCCEEDED(rv), "Should have purged sqlite caches"); 286 } else if (IsOnCurrentSerialEventTarget(conn->eventTargetOpenedOn)) { 287 if (conn->isAsyncExecutionThreadAvailable()) { 288 nsCOMPtr<mozIStoragePendingStatement> ps; 289 DebugOnly<nsresult> rv = conn->ExecuteSimpleSQLAsync( 290 shrinkPragma, nullptr, getter_AddRefs(ps)); 291 MOZ_ASSERT(NS_SUCCEEDED(rv), "Should have purged sqlite caches"); 292 } else { 293 conn->ExecuteSimpleSQL(shrinkPragma); 294 } 295 } else { 296 // We are on the wrong event target, the query should be executed on the 297 // opener event target, so we must dispatch to it. 298 // It's possible the connection is already closed or will be closed by the 299 // time our runnable runs. ExecuteSimpleSQL will safely return with a 300 // failure in that case. If the event target is shutting down or shut 301 // down, the dispatch will fail and that's okay. 302 nsCOMPtr<nsIRunnable> event = NewRunnableMethod<const nsCString>( 303 "Connection::ExecuteSimpleSQL", conn, &Connection::ExecuteSimpleSQL, 304 shrinkPragma); 305 (void)conn->eventTargetOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL); 306 } 307 } 308 } 309 310 UniquePtr<sqlite3_vfs> ConstructReadOnlyNoLockVFS(); 311 312 static const char* sObserverTopics[] = {"memory-pressure", 313 "xpcom-shutdown-threads"}; 314 315 nsresult Service::initialize() { 316 MOZ_ASSERT(NS_IsMainThread(), "Must be initialized on the main thread"); 317 318 int rc = AutoSQLiteLifetime::getInitResult(); 319 if (rc != SQLITE_OK) { 320 return convertResultCode(rc); 321 } 322 323 /** 324 * The virtual file system hierarchy 325 * 326 * obfsvfs 327 * | 328 * | 329 * | 330 * quotavfs 331 * / \ 332 * / \ 333 * / \ 334 * / \ 335 * / \ 336 * base-vfs-excl base-vfs 337 * / \ / \ 338 * / \ / \ 339 * / \ / \ 340 * unix-excl win32 unix win32 341 */ 342 343 rc = mBaseSqliteVFS.Init(basevfs::ConstructVFS(false)); 344 if (rc != SQLITE_OK) { 345 return convertResultCode(rc); 346 } 347 348 rc = mBaseExclSqliteVFS.Init(basevfs::ConstructVFS(true)); 349 if (rc != SQLITE_OK) { 350 return convertResultCode(rc); 351 } 352 353 rc = mQuotaSqliteVFS.Init(quotavfs::ConstructVFS(basevfs::GetVFSName( 354 StaticPrefs::storage_sqlite_exclusiveLock_enabled()))); 355 if (rc != SQLITE_OK) { 356 return convertResultCode(rc); 357 } 358 359 rc = 360 mObfuscatingSqliteVFS.Init(obfsvfs::ConstructVFS(quotavfs::GetVFSName())); 361 if (rc != SQLITE_OK) { 362 return convertResultCode(rc); 363 } 364 365 rc = mReadOnlyNoLockSqliteVFS.Init(ConstructReadOnlyNoLockVFS()); 366 if (rc != SQLITE_OK) { 367 return convertResultCode(rc); 368 } 369 370 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 371 NS_ENSURE_TRUE(os, NS_ERROR_FAILURE); 372 373 for (auto& sObserverTopic : sObserverTopics) { 374 nsresult rv = os->AddObserver(this, sObserverTopic, false); 375 if (NS_WARN_IF(NS_FAILED(rv))) { 376 return rv; 377 } 378 } 379 380 mozilla::RegisterWeakMemoryReporter(this); 381 mozilla::RegisterStorageSQLiteDistinguishedAmount( 382 StorageSQLiteDistinguishedAmount); 383 384 return NS_OK; 385 } 386 387 int Service::localeCompareStrings(const nsAString& aStr1, 388 const nsAString& aStr2, 389 Collator::Sensitivity aSensitivity) { 390 // The mozilla::intl::Collator is not thread safe, since the Collator::Options 391 // can be changed. 392 MutexAutoLock mutex(mMutex); 393 394 Collator* collator = getCollator(); 395 if (!collator) { 396 NS_ERROR("Storage service has no collation"); 397 return 0; 398 } 399 400 if (aSensitivity != mLastSensitivity) { 401 Collator::Options options{}; 402 options.sensitivity = aSensitivity; 403 auto result = mCollator->SetOptions(options); 404 405 if (result.isErr()) { 406 NS_WARNING("Could not configure the mozilla::intl::Collation."); 407 return 0; 408 } 409 mLastSensitivity = aSensitivity; 410 } 411 412 return collator->CompareStrings(aStr1, aStr2); 413 } 414 415 Collator* Service::getCollator() { 416 mMutex.AssertCurrentThreadOwns(); 417 418 if (mCollator) { 419 return mCollator.get(); 420 } 421 422 auto result = mozilla::intl::LocaleService::TryCreateComponent<Collator>(); 423 if (result.isErr()) { 424 NS_WARNING("Could not create mozilla::intl::Collation."); 425 return nullptr; 426 } 427 428 mCollator = result.unwrap(); 429 430 // Sort in a case-insensitive way, where "base" letters are considered 431 // equal, e.g: a = á, a = A, a ≠b. 432 Collator::Options options{}; 433 options.sensitivity = Collator::Sensitivity::Base; 434 auto optResult = mCollator->SetOptions(options); 435 436 if (optResult.isErr()) { 437 NS_WARNING("Could not configure the mozilla::intl::Collation."); 438 mCollator = nullptr; 439 return nullptr; 440 } 441 442 return mCollator.get(); 443 } 444 445 //////////////////////////////////////////////////////////////////////////////// 446 //// mozIStorageService 447 448 NS_IMETHODIMP 449 Service::OpenSpecialDatabase(const nsACString& aStorageKey, 450 const nsACString& aName, uint32_t aConnectionFlags, 451 mozIStorageConnection** _connection) { 452 if (!aStorageKey.Equals(kMozStorageMemoryStorageKey)) { 453 return NS_ERROR_INVALID_ARG; 454 } 455 456 const bool interruptible = 457 aConnectionFlags & mozIStorageService::CONNECTION_INTERRUPTIBLE; 458 459 int flags = SQLITE_OPEN_READWRITE; 460 461 if (!aName.IsEmpty()) { 462 flags |= SQLITE_OPEN_URI; 463 } 464 465 RefPtr<Connection> msc = 466 new Connection(this, flags, Connection::SYNCHRONOUS, 467 kMozStorageMemoryStorageKey, interruptible); 468 const nsresult rv = msc->initialize(aStorageKey, aName); 469 NS_ENSURE_SUCCESS(rv, rv); 470 471 msc.forget(_connection); 472 return NS_OK; 473 } 474 475 namespace { 476 477 class AsyncInitDatabase final : public Runnable { 478 public: 479 AsyncInitDatabase(Connection* aConnection, nsIFile* aStorageFile, 480 int32_t aGrowthIncrement, 481 mozIStorageCompletionCallback* aCallback) 482 : Runnable("storage::AsyncInitDatabase"), 483 mConnection(aConnection), 484 mStorageFile(aStorageFile), 485 mGrowthIncrement(aGrowthIncrement), 486 mCallback(aCallback) { 487 MOZ_ASSERT(NS_IsMainThread()); 488 } 489 490 NS_IMETHOD Run() override { 491 MOZ_ASSERT(!NS_IsMainThread()); 492 nsresult rv = mConnection->initializeOnAsyncThread(mStorageFile); 493 if (NS_FAILED(rv)) { 494 return DispatchResult(rv, nullptr); 495 } 496 497 if (mGrowthIncrement >= 0) { 498 // Ignore errors. In the future, we might wish to log them. 499 (void)mConnection->SetGrowthIncrement(mGrowthIncrement, ""_ns); 500 } 501 502 return DispatchResult( 503 NS_OK, NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, mConnection)); 504 } 505 506 private: 507 nsresult DispatchResult(nsresult aStatus, nsISupports* aValue) { 508 RefPtr<CallbackComplete> event = 509 new CallbackComplete(aStatus, aValue, mCallback.forget()); 510 return NS_DispatchToMainThread(event); 511 } 512 513 ~AsyncInitDatabase() { 514 NS_ReleaseOnMainThread("AsyncInitDatabase::mStorageFile", 515 mStorageFile.forget()); 516 NS_ReleaseOnMainThread("AsyncInitDatabase::mConnection", 517 mConnection.forget()); 518 519 // Generally, the callback will be released by CallbackComplete. 520 // However, if for some reason Run() is not executed, we still 521 // need to ensure that it is released here. 522 NS_ReleaseOnMainThread("AsyncInitDatabase::mCallback", mCallback.forget()); 523 } 524 525 RefPtr<Connection> mConnection; 526 nsCOMPtr<nsIFile> mStorageFile; 527 int32_t mGrowthIncrement; 528 RefPtr<mozIStorageCompletionCallback> mCallback; 529 }; 530 531 } // namespace 532 533 NS_IMETHODIMP 534 Service::OpenAsyncDatabase(nsIVariant* aDatabaseStore, uint32_t aOpenFlags, 535 uint32_t /* aConnectionFlags */, 536 mozIStorageCompletionCallback* aCallback) { 537 if (!NS_IsMainThread()) { 538 return NS_ERROR_NOT_SAME_THREAD; 539 } 540 NS_ENSURE_ARG(aDatabaseStore); 541 NS_ENSURE_ARG(aCallback); 542 543 const bool shared = aOpenFlags & mozIStorageService::OPEN_SHARED; 544 const bool ignoreLockingMode = 545 aOpenFlags & mozIStorageService::OPEN_IGNORE_LOCKING_MODE; 546 // Specifying ignoreLockingMode will force use of the readOnly flag: 547 const bool readOnly = 548 ignoreLockingMode || (aOpenFlags & mozIStorageService::OPEN_READONLY); 549 const bool openNotExclusive = 550 aOpenFlags & mozIStorageService::OPEN_NOT_EXCLUSIVE; 551 int flags = readOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE; 552 553 nsCOMPtr<nsIFile> storageFile; 554 nsCOMPtr<nsISupports> dbStore; 555 nsresult rv = aDatabaseStore->GetAsISupports(getter_AddRefs(dbStore)); 556 if (NS_SUCCEEDED(rv)) { 557 // Generally, aDatabaseStore holds the database nsIFile. 558 storageFile = do_QueryInterface(dbStore, &rv); 559 if (NS_FAILED(rv)) { 560 return NS_ERROR_INVALID_ARG; 561 } 562 563 nsCOMPtr<nsIFile> cloned; 564 rv = storageFile->Clone(getter_AddRefs(cloned)); 565 MOZ_ASSERT(NS_SUCCEEDED(rv)); 566 storageFile = std::move(cloned); 567 568 if (!readOnly) { 569 // Ensure that SQLITE_OPEN_CREATE is passed in for compatibility reasons. 570 flags |= SQLITE_OPEN_CREATE; 571 } 572 573 // Apply the shared-cache option. 574 flags |= shared ? SQLITE_OPEN_SHAREDCACHE : SQLITE_OPEN_PRIVATECACHE; 575 } else { 576 // Sometimes, however, it's a special database name. 577 nsAutoCString keyString; 578 rv = aDatabaseStore->GetAsACString(keyString); 579 if (NS_FAILED(rv) || !keyString.Equals(kMozStorageMemoryStorageKey)) { 580 return NS_ERROR_INVALID_ARG; 581 } 582 583 // Just fall through with nullptr storageFile, this will cause the storage 584 // connection to use a memory DB. 585 } 586 587 // Create connection on this thread, but initialize it on its helper thread. 588 nsAutoCString telemetryFilename; 589 if (!storageFile) { 590 telemetryFilename.Assign(kMozStorageMemoryStorageKey); 591 } else { 592 rv = storageFile->GetNativeLeafName(telemetryFilename); 593 NS_ENSURE_SUCCESS(rv, rv); 594 } 595 RefPtr<Connection> msc = new Connection( 596 this, flags, Connection::ASYNCHRONOUS, telemetryFilename, 597 /* interruptible */ true, ignoreLockingMode, openNotExclusive); 598 nsCOMPtr<nsIEventTarget> target = msc->getAsyncExecutionTarget(); 599 MOZ_ASSERT(target, 600 "Cannot initialize a connection that has been closed already"); 601 602 RefPtr<AsyncInitDatabase> asyncInit = new AsyncInitDatabase( 603 msc, storageFile, /* growthIncrement */ -1, aCallback); 604 return target->Dispatch(asyncInit, nsIEventTarget::DISPATCH_NORMAL); 605 } 606 607 NS_IMETHODIMP 608 Service::OpenDatabase(nsIFile* aDatabaseFile, uint32_t aConnectionFlags, 609 mozIStorageConnection** _connection) { 610 NS_ENSURE_ARG(aDatabaseFile); 611 612 const bool interruptible = 613 aConnectionFlags & mozIStorageService::CONNECTION_INTERRUPTIBLE; 614 615 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility 616 // reasons. 617 const int flags = 618 SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_CREATE; 619 nsAutoCString telemetryFilename; 620 nsresult rv = aDatabaseFile->GetNativeLeafName(telemetryFilename); 621 NS_ENSURE_SUCCESS(rv, rv); 622 RefPtr<Connection> msc = new Connection(this, flags, Connection::SYNCHRONOUS, 623 telemetryFilename, interruptible); 624 rv = msc->initialize(aDatabaseFile); 625 NS_ENSURE_SUCCESS(rv, rv); 626 627 msc.forget(_connection); 628 return NS_OK; 629 } 630 631 NS_IMETHODIMP 632 Service::OpenUnsharedDatabase(nsIFile* aDatabaseFile, uint32_t aConnectionFlags, 633 mozIStorageConnection** _connection) { 634 NS_ENSURE_ARG(aDatabaseFile); 635 636 const bool interruptible = 637 aConnectionFlags & mozIStorageService::CONNECTION_INTERRUPTIBLE; 638 639 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility 640 // reasons. 641 const int flags = 642 SQLITE_OPEN_READWRITE | SQLITE_OPEN_PRIVATECACHE | SQLITE_OPEN_CREATE; 643 nsAutoCString telemetryFilename; 644 nsresult rv = aDatabaseFile->GetNativeLeafName(telemetryFilename); 645 NS_ENSURE_SUCCESS(rv, rv); 646 RefPtr<Connection> msc = new Connection(this, flags, Connection::SYNCHRONOUS, 647 telemetryFilename, interruptible); 648 rv = msc->initialize(aDatabaseFile); 649 NS_ENSURE_SUCCESS(rv, rv); 650 651 msc.forget(_connection); 652 return NS_OK; 653 } 654 655 NS_IMETHODIMP 656 Service::OpenDatabaseWithFileURL(nsIFileURL* aFileURL, 657 const nsACString& aTelemetryFilename, 658 uint32_t aConnectionFlags, 659 mozIStorageConnection** _connection) { 660 NS_ENSURE_ARG(aFileURL); 661 662 const bool interruptible = 663 aConnectionFlags & mozIStorageService::CONNECTION_INTERRUPTIBLE; 664 665 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility 666 // reasons. 667 const int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE | 668 SQLITE_OPEN_CREATE | SQLITE_OPEN_URI; 669 nsresult rv; 670 nsAutoCString telemetryFilename; 671 if (!aTelemetryFilename.IsEmpty()) { 672 telemetryFilename = aTelemetryFilename; 673 } else { 674 nsCOMPtr<nsIFile> databaseFile; 675 rv = aFileURL->GetFile(getter_AddRefs(databaseFile)); 676 NS_ENSURE_SUCCESS(rv, rv); 677 rv = databaseFile->GetNativeLeafName(telemetryFilename); 678 NS_ENSURE_SUCCESS(rv, rv); 679 } 680 RefPtr<Connection> msc = new Connection(this, flags, Connection::SYNCHRONOUS, 681 telemetryFilename, interruptible); 682 rv = msc->initialize(aFileURL); 683 NS_ENSURE_SUCCESS(rv, rv); 684 685 msc.forget(_connection); 686 return NS_OK; 687 } 688 689 //////////////////////////////////////////////////////////////////////////////// 690 //// nsIObserver 691 692 NS_IMETHODIMP 693 Service::Observe(nsISupports*, const char* aTopic, const char16_t*) { 694 if (strcmp(aTopic, "memory-pressure") == 0) { 695 minimizeMemory(); 696 } else if (strcmp(aTopic, "xpcom-shutdown-threads") == 0) { 697 // The Service is kept alive by our strong observer references and 698 // references held by Connection instances. Since we're about to remove the 699 // former and then wait for the latter ones to go away, it behooves us to 700 // hold a strong reference to ourselves so our calls to getConnections() do 701 // not happen on a deleted object. 702 RefPtr<Service> kungFuDeathGrip = this; 703 704 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 705 706 for (auto& sObserverTopic : sObserverTopics) { 707 (void)os->RemoveObserver(this, sObserverTopic); 708 } 709 710 SpinEventLoopUntil("storage::Service::Observe(xpcom-shutdown-threads)"_ns, 711 [&]() -> bool { 712 // We must wait until all the closing connections are 713 // closed. 714 nsTArray<RefPtr<Connection>> connections; 715 getConnections(connections); 716 for (auto& conn : connections) { 717 if (conn->isClosing()) { 718 return false; 719 } 720 } 721 return true; 722 }); 723 724 #ifdef DEBUG 725 nsTArray<RefPtr<Connection>> connections; 726 getConnections(connections); 727 for (uint32_t i = 0, n = connections.Length(); i < n; i++) { 728 if (!connections[i]->isClosed()) { 729 // getFilename is only the leaf name for the database file, 730 // so it shouldn't contain privacy-sensitive information. 731 CrashReporter::RecordAnnotationNSCString( 732 CrashReporter::Annotation::StorageConnectionNotClosed, 733 connections[i]->getFilename()); 734 printf_stderr("Storage connection not closed: %s", 735 connections[i]->getFilename().get()); 736 MOZ_CRASH(); 737 } 738 } 739 #endif 740 } 741 742 return NS_OK; 743 } 744 745 } // namespace mozilla::storage