1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "mozilla/dom/quota/QuotaManager.h"
10 #include "mozilla/dom/quota/UsageRunnable.h"
11 #include "mozilla/dom/quota/Utilities.h"
13 #include "IDBDatabase.h"
14 #include "IndexedDatabaseManager.h"
15 #include "TransactionThreadPool.h"
17 USING_INDEXEDDB_NAMESPACE
18 using mozilla::dom::quota::AssertIsOnIOThread
;
19 using mozilla::dom::quota::QuotaManager
;
24 GetDatabaseBaseFilename(const nsAString
& aFilename
,
25 nsAString
& aDatabaseBaseFilename
)
27 NS_ASSERTION(!aFilename
.IsEmpty(), "Bad argument!");
29 NS_NAMED_LITERAL_STRING(sqlite
, ".sqlite");
31 if (!StringEndsWith(aFilename
, sqlite
)) {
35 aDatabaseBaseFilename
=
36 Substring(aFilename
, 0, aFilename
.Length() - sqlite
.Length());
41 } // anonymous namespace
43 // This needs to be fully qualified to not confuse trace refcnt assertions.
44 NS_IMPL_ADDREF(mozilla::dom::indexedDB::Client
)
45 NS_IMPL_RELEASE(mozilla::dom::indexedDB::Client
)
48 Client::InitOrigin(const nsACString
& aOrigin
, UsageRunnable
* aUsageRunnable
)
52 nsCOMPtr
<nsIFile
> directory
;
53 nsresult rv
= GetDirectory(aOrigin
, getter_AddRefs(directory
));
54 NS_ENSURE_SUCCESS(rv
, rv
);
56 // We need to see if there are any files in the directory already. If they
57 // are database files then we need to cleanup stored files (if it's needed)
58 // and also get the usage.
60 nsAutoTArray
<nsString
, 20> subdirsToProcess
;
61 nsAutoTArray
<nsCOMPtr
<nsIFile
> , 20> unknownFiles
;
62 nsTHashtable
<nsStringHashKey
> validSubdirs
;
63 validSubdirs
.Init(20);
65 nsCOMPtr
<nsISimpleEnumerator
> entries
;
66 rv
= directory
->GetDirectoryEntries(getter_AddRefs(entries
));
67 NS_ENSURE_SUCCESS(rv
, rv
);
70 while (NS_SUCCEEDED((rv
= entries
->HasMoreElements(&hasMore
))) &&
71 hasMore
&& (!aUsageRunnable
|| !aUsageRunnable
->Canceled())) {
72 nsCOMPtr
<nsISupports
> entry
;
73 rv
= entries
->GetNext(getter_AddRefs(entry
));
74 NS_ENSURE_SUCCESS(rv
, rv
);
76 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(entry
);
77 NS_ENSURE_TRUE(file
, NS_NOINTERFACE
);
80 rv
= file
->GetLeafName(leafName
);
81 NS_ENSURE_SUCCESS(rv
, rv
);
83 if (StringEndsWith(leafName
, NS_LITERAL_STRING(".sqlite-journal"))) {
88 rv
= file
->IsDirectory(&isDirectory
);
89 NS_ENSURE_SUCCESS(rv
, rv
);
92 if (!validSubdirs
.GetEntry(leafName
)) {
93 subdirsToProcess
.AppendElement(leafName
);
98 nsString dbBaseFilename
;
99 if (!GetDatabaseBaseFilename(leafName
, dbBaseFilename
)) {
100 unknownFiles
.AppendElement(file
);
104 nsCOMPtr
<nsIFile
> fmDirectory
;
105 rv
= directory
->Clone(getter_AddRefs(fmDirectory
));
106 NS_ENSURE_SUCCESS(rv
, rv
);
108 rv
= fmDirectory
->Append(dbBaseFilename
);
109 NS_ENSURE_SUCCESS(rv
, rv
);
111 rv
= FileManager::InitDirectory(fmDirectory
, file
, aOrigin
);
112 NS_ENSURE_SUCCESS(rv
, rv
);
114 if (aUsageRunnable
) {
116 rv
= file
->GetFileSize(&fileSize
);
117 NS_ENSURE_SUCCESS(rv
, rv
);
119 aUsageRunnable
->AppendToDatabaseUsage(uint64_t(fileSize
));
122 rv
= FileManager::GetUsage(fmDirectory
, &usage
);
123 NS_ENSURE_SUCCESS(rv
, rv
);
125 aUsageRunnable
->AppendToFileUsage(usage
);
128 validSubdirs
.PutEntry(dbBaseFilename
);
130 NS_ENSURE_SUCCESS(rv
, rv
);
132 for (uint32_t i
= 0; i
< subdirsToProcess
.Length(); i
++) {
133 const nsString
& subdir
= subdirsToProcess
[i
];
134 if (!validSubdirs
.GetEntry(subdir
)) {
135 NS_WARNING("Unknown subdirectory found!");
136 return NS_ERROR_UNEXPECTED
;
140 for (uint32_t i
= 0; i
< unknownFiles
.Length(); i
++) {
141 nsCOMPtr
<nsIFile
>& unknownFile
= unknownFiles
[i
];
143 // Some temporary SQLite files could disappear, so we have to check if the
144 // unknown file still exists.
146 rv
= unknownFile
->Exists(&exists
);
147 NS_ENSURE_SUCCESS(rv
, rv
);
151 unknownFile
->GetLeafName(leafName
);
153 // The journal file may exists even after db has been correctly opened.
154 if (!StringEndsWith(leafName
, NS_LITERAL_STRING(".sqlite-journal"))) {
155 NS_WARNING("Unknown file found!");
156 return NS_ERROR_UNEXPECTED
;
165 Client::GetUsageForOrigin(const nsACString
& aOrigin
,
166 UsageRunnable
* aUsageRunnable
)
168 AssertIsOnIOThread();
169 NS_ASSERTION(aUsageRunnable
, "Null pointer!");
171 nsCOMPtr
<nsIFile
> directory
;
172 nsresult rv
= GetDirectory(aOrigin
, getter_AddRefs(directory
));
173 NS_ENSURE_SUCCESS(rv
, rv
);
175 rv
= GetUsageForDirectoryInternal(directory
, aUsageRunnable
, true);
176 NS_ENSURE_SUCCESS(rv
, rv
);
182 Client::OnOriginClearCompleted(const nsACString
& aPattern
)
184 AssertIsOnIOThread();
186 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
188 mgr
->InvalidateFileManagersForPattern(aPattern
);
193 Client::ReleaseIOThreadObjects()
195 AssertIsOnIOThread();
197 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
199 mgr
->InvalidateAllFileManagers();
204 Client::IsTransactionServiceActivated()
206 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
208 return !!TransactionThreadPool::Get();
212 Client::WaitForStoragesToComplete(nsTArray
<nsIOfflineStorage
*>& aStorages
,
213 nsIRunnable
* aCallback
)
215 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
216 NS_ASSERTION(!aStorages
.IsEmpty(), "No storages to wait on!");
217 NS_ASSERTION(aCallback
, "Passed null callback!");
219 TransactionThreadPool
* pool
= TransactionThreadPool::Get();
220 NS_ASSERTION(pool
, "Should have checked if transaction service is active!");
222 nsTArray
<nsRefPtr
<IDBDatabase
> > databases(aStorages
.Length());
223 for (uint32_t index
= 0; index
< aStorages
.Length(); index
++) {
224 IDBDatabase
* database
= IDBDatabase::FromStorage(aStorages
[index
]);
229 databases
.AppendElement(database
);
232 pool
->WaitForDatabasesToComplete(databases
, aCallback
);
236 Client::AbortTransactionsForStorage(nsIOfflineStorage
* aStorage
)
238 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
239 NS_ASSERTION(aStorage
, "Passed null storage!");
241 TransactionThreadPool
* pool
= TransactionThreadPool::Get();
242 NS_ASSERTION(pool
, "Should have checked if transaction service is active!");
244 IDBDatabase
* database
= IDBDatabase::FromStorage(aStorage
);
245 NS_ASSERTION(database
, "This shouldn't be null!");
247 pool
->AbortTransactionsForDatabase(database
);
251 Client::HasTransactionsForStorage(nsIOfflineStorage
* aStorage
)
253 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
255 TransactionThreadPool
* pool
= TransactionThreadPool::Get();
256 NS_ASSERTION(pool
, "Should have checked if transaction service is active!");
258 IDBDatabase
* database
= IDBDatabase::FromStorage(aStorage
);
259 NS_ASSERTION(database
, "This shouldn't be null!");
261 return pool
->HasTransactionsForDatabase(database
);
265 Client::ShutdownTransactionService()
267 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
269 TransactionThreadPool::Shutdown();
273 Client::GetDirectory(const nsACString
& aOrigin
, nsIFile
** aDirectory
)
275 QuotaManager
* quotaManager
= QuotaManager::Get();
276 NS_ASSERTION(quotaManager
, "This should never fail!");
278 nsCOMPtr
<nsIFile
> directory
;
280 quotaManager
->GetDirectoryForOrigin(aOrigin
, getter_AddRefs(directory
));
281 NS_ENSURE_SUCCESS(rv
, rv
);
283 NS_ASSERTION(directory
, "What?");
285 rv
= directory
->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME
));
286 NS_ENSURE_SUCCESS(rv
, rv
);
288 directory
.forget(aDirectory
);
293 Client::GetUsageForDirectoryInternal(nsIFile
* aDirectory
,
294 UsageRunnable
* aUsageRunnable
,
297 NS_ASSERTION(aDirectory
, "Null pointer!");
298 NS_ASSERTION(aUsageRunnable
, "Null pointer!");
300 nsCOMPtr
<nsISimpleEnumerator
> entries
;
301 nsresult rv
= aDirectory
->GetDirectoryEntries(getter_AddRefs(entries
));
302 NS_ENSURE_SUCCESS(rv
, rv
);
309 while (NS_SUCCEEDED((rv
= entries
->HasMoreElements(&hasMore
))) &&
310 hasMore
&& !aUsageRunnable
->Canceled()) {
311 nsCOMPtr
<nsISupports
> entry
;
312 rv
= entries
->GetNext(getter_AddRefs(entry
));
313 NS_ENSURE_SUCCESS(rv
, rv
);
315 nsCOMPtr
<nsIFile
> file(do_QueryInterface(entry
));
316 NS_ASSERTION(file
, "Don't know what this is!");
319 rv
= file
->IsDirectory(&isDirectory
);
320 NS_ENSURE_SUCCESS(rv
, rv
);
323 if (aDatabaseFiles
) {
324 rv
= GetUsageForDirectoryInternal(file
, aUsageRunnable
, false);
325 NS_ENSURE_SUCCESS(rv
, rv
);
329 rv
= file
->GetLeafName(leafName
);
330 NS_ENSURE_SUCCESS(rv
, rv
);
332 if (!leafName
.EqualsLiteral(JOURNAL_DIRECTORY_NAME
)) {
333 NS_WARNING("Unknown directory found!");
341 rv
= file
->GetFileSize(&fileSize
);
342 NS_ENSURE_SUCCESS(rv
, rv
);
344 NS_ASSERTION(fileSize
>= 0, "Negative size?!");
346 if (aDatabaseFiles
) {
347 aUsageRunnable
->AppendToDatabaseUsage(uint64_t(fileSize
));
350 aUsageRunnable
->AppendToFileUsage(uint64_t(fileSize
));
353 NS_ENSURE_SUCCESS(rv
, rv
);