Bug 1704628 Part 4: Avoid use of ESC to close context menu in browser_toolbox_content...
[gecko.git] / dom / cache / DBAction.cpp
bloba61bd7168c948a203c88960ea46da4159c91e38b
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/dom/cache/DBAction.h"
9 #include "mozilla/dom/cache/Connection.h"
10 #include "mozilla/dom/cache/DBSchema.h"
11 #include "mozilla/dom/cache/FileUtils.h"
12 #include "mozilla/dom/cache/QuotaClient.h"
13 #include "mozilla/dom/quota/PersistenceType.h"
14 #include "mozilla/net/nsFileProtocolHandler.h"
15 #include "mozIStorageConnection.h"
16 #include "mozIStorageService.h"
17 #include "mozStorageCID.h"
18 #include "nsIFile.h"
19 #include "nsIURI.h"
20 #include "nsIURIMutator.h"
21 #include "nsIFileURL.h"
22 #include "nsThreadUtils.h"
24 namespace mozilla::dom::cache {
26 using mozilla::dom::quota::AssertIsOnIOThread;
27 using mozilla::dom::quota::Client;
28 using mozilla::dom::quota::CloneFileAndAppend;
29 using mozilla::dom::quota::IsDatabaseCorruptionError;
30 using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
31 using mozilla::dom::quota::PersistenceType;
33 namespace {
35 nsresult WipeDatabase(const QuotaInfo& aQuotaInfo, nsIFile& aDBFile) {
36 CACHE_TRY_INSPECT(
37 const auto& dbDir,
38 MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<nsIFile>, aDBFile, GetParent));
40 CACHE_TRY(RemoveNsIFile(aQuotaInfo, aDBFile));
42 // Note, the -wal journal file will be automatically deleted by sqlite when
43 // the new database is created. No need to explicitly delete it here.
45 // Delete the morgue as well.
46 CACHE_TRY(BodyDeleteDir(aQuotaInfo, *dbDir));
48 CACHE_TRY(WipePaddingFile(aQuotaInfo, dbDir));
50 return NS_OK;
53 } // namespace
55 DBAction::DBAction(Mode aMode) : mMode(aMode) {}
57 DBAction::~DBAction() = default;
59 void DBAction::RunOnTarget(SafeRefPtr<Resolver> aResolver,
60 const QuotaInfo& aQuotaInfo, Data* aOptionalData) {
61 MOZ_ASSERT(!NS_IsMainThread());
62 MOZ_DIAGNOSTIC_ASSERT(aResolver);
63 MOZ_DIAGNOSTIC_ASSERT(aQuotaInfo.mDir);
65 if (IsCanceled()) {
66 aResolver->Resolve(NS_ERROR_ABORT);
67 return;
70 const auto resolveErr = [&aResolver](const nsresult rv) {
71 aResolver->Resolve(rv);
74 CACHE_TRY_INSPECT(const auto& dbDir,
75 CloneFileAndAppend(*aQuotaInfo.mDir, u"cache"_ns), QM_VOID,
76 resolveErr);
78 nsCOMPtr<mozIStorageConnection> conn;
80 // Attempt to reuse the connection opened by a previous Action.
81 if (aOptionalData) {
82 conn = aOptionalData->GetConnection();
85 // If there is no previous Action, then we must open one.
86 if (!conn) {
87 CACHE_TRY_UNWRAP(conn, OpenConnection(aQuotaInfo, *dbDir), QM_VOID,
88 resolveErr);
89 MOZ_DIAGNOSTIC_ASSERT(conn);
91 // Save this connection in the shared Data object so later Actions can
92 // use it. This avoids opening a new connection for every Action.
93 if (aOptionalData) {
94 // Since we know this connection will be around for as long as the
95 // Cache is open, use our special wrapped connection class. This
96 // will let us perform certain operations once the Cache origin
97 // is closed.
98 nsCOMPtr<mozIStorageConnection> wrapped = new Connection(conn);
99 aOptionalData->SetConnection(wrapped);
103 RunWithDBOnTarget(std::move(aResolver), aQuotaInfo, dbDir, conn);
106 Result<nsCOMPtr<mozIStorageConnection>, nsresult> DBAction::OpenConnection(
107 const QuotaInfo& aQuotaInfo, nsIFile& aDBDir) {
108 MOZ_ASSERT(!NS_IsMainThread());
109 MOZ_DIAGNOSTIC_ASSERT(aQuotaInfo.mDirectoryLockId >= 0);
111 CACHE_TRY_INSPECT(const bool& exists, MOZ_TO_RESULT_INVOKE(aDBDir, Exists));
113 if (!exists) {
114 CACHE_TRY(OkIf(mMode == Create), Err(NS_ERROR_FILE_NOT_FOUND));
115 CACHE_TRY(aDBDir.Create(nsIFile::DIRECTORY_TYPE, 0755));
118 CACHE_TRY_INSPECT(const auto& dbFile,
119 CloneFileAndAppend(aDBDir, kCachesSQLiteFilename));
121 CACHE_TRY_RETURN(OpenDBConnection(aQuotaInfo, *dbFile));
124 SyncDBAction::SyncDBAction(Mode aMode) : DBAction(aMode) {}
126 SyncDBAction::~SyncDBAction() = default;
128 void SyncDBAction::RunWithDBOnTarget(SafeRefPtr<Resolver> aResolver,
129 const QuotaInfo& aQuotaInfo,
130 nsIFile* aDBDir,
131 mozIStorageConnection* aConn) {
132 MOZ_ASSERT(!NS_IsMainThread());
133 MOZ_DIAGNOSTIC_ASSERT(aResolver);
134 MOZ_DIAGNOSTIC_ASSERT(aDBDir);
135 MOZ_DIAGNOSTIC_ASSERT(aConn);
137 nsresult rv = RunSyncWithDBOnTarget(aQuotaInfo, aDBDir, aConn);
138 aResolver->Resolve(rv);
141 Result<nsCOMPtr<mozIStorageConnection>, nsresult> OpenDBConnection(
142 const QuotaInfo& aQuotaInfo, nsIFile& aDBFile) {
143 MOZ_ASSERT(!NS_IsMainThread());
144 MOZ_DIAGNOSTIC_ASSERT(aQuotaInfo.mDirectoryLockId >= -1);
146 // Use our default file:// protocol handler directly to construct the database
147 // URL. This avoids any problems if a plugin registers a custom file://
148 // handler. If such a custom handler used javascript, then we would have a
149 // bad time running off the main thread here.
150 auto handler = MakeRefPtr<nsFileProtocolHandler>();
151 CACHE_TRY(handler->Init());
153 CACHE_TRY_INSPECT(const auto& mutator,
154 MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<nsIURIMutator>, handler,
155 NewFileURIMutator, &aDBFile));
157 const nsCString directoryLockIdClause =
158 aQuotaInfo.mDirectoryLockId >= 0
159 ? "&directoryLockId="_ns + IntToCString(aQuotaInfo.mDirectoryLockId)
160 : EmptyCString();
162 nsCOMPtr<nsIFileURL> dbFileUrl;
163 CACHE_TRY(NS_MutateURI(mutator)
164 .SetQuery("cache=private"_ns + directoryLockIdClause)
165 .Finalize(dbFileUrl));
167 CACHE_TRY_INSPECT(
168 const auto& storageService,
169 ToResultGet<nsCOMPtr<mozIStorageService>>(
170 MOZ_SELECT_OVERLOAD(do_GetService), MOZ_STORAGE_SERVICE_CONTRACTID),
171 Err(NS_ERROR_UNEXPECTED));
173 CACHE_TRY_UNWRAP(
174 auto conn,
175 QM_OR_ELSE_WARN(
176 MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageConnection>,
177 storageService, OpenDatabaseWithFileURL,
178 dbFileUrl, ""_ns),
179 ([&aQuotaInfo, &aDBFile, &storageService,
180 &dbFileUrl](const nsresult rv)
181 -> Result<nsCOMPtr<mozIStorageConnection>, nsresult> {
182 if (IsDatabaseCorruptionError(rv)) {
183 NS_WARNING(
184 "Cache database corrupted. Recreating empty database.");
186 // There is nothing else we can do to recover. Also, this data
187 // can be deleted by QuotaManager at any time anyways.
188 CACHE_TRY(WipeDatabase(aQuotaInfo, aDBFile));
190 CACHE_TRY_RETURN(MOZ_TO_RESULT_INVOKE_TYPED(
191 nsCOMPtr<mozIStorageConnection>, storageService,
192 OpenDatabaseWithFileURL, dbFileUrl, ""_ns));
194 return Err(rv);
195 })));
197 // Check the schema to make sure it is not too old.
198 CACHE_TRY_INSPECT(const int32_t& schemaVersion,
199 MOZ_TO_RESULT_INVOKE(conn, GetSchemaVersion));
200 if (schemaVersion > 0 && schemaVersion < db::kFirstShippedSchemaVersion) {
201 // Close existing connection before wiping database.
202 conn = nullptr;
204 CACHE_TRY(WipeDatabase(aQuotaInfo, aDBFile));
206 CACHE_TRY_UNWRAP(conn, MOZ_TO_RESULT_INVOKE_TYPED(
207 nsCOMPtr<mozIStorageConnection>, storageService,
208 OpenDatabaseWithFileURL, dbFileUrl, ""_ns));
211 CACHE_TRY(db::InitializeConnection(*conn));
213 return conn;
216 } // namespace mozilla::dom::cache