/src/mozilla-central/dom/indexedDB/IndexedDatabaseManager.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #include "IndexedDatabaseManager.h" |
8 | | |
9 | | #include "chrome/common/ipc_channel.h" // for IPC::Channel::kMaximumMessageSize |
10 | | #include "nsIConsoleService.h" |
11 | | #include "nsIDOMWindow.h" |
12 | | #include "nsIEventTarget.h" |
13 | | #include "nsIFile.h" |
14 | | #include "nsIScriptError.h" |
15 | | #include "nsIScriptGlobalObject.h" |
16 | | |
17 | | #include "jsapi.h" |
18 | | #include "mozilla/ClearOnShutdown.h" |
19 | | #include "mozilla/CondVar.h" |
20 | | #include "mozilla/ContentEvents.h" |
21 | | #include "mozilla/EventDispatcher.h" |
22 | | #include "mozilla/Preferences.h" |
23 | | #include "mozilla/Services.h" |
24 | | #include "mozilla/dom/DOMException.h" |
25 | | #include "mozilla/dom/ErrorEvent.h" |
26 | | #include "mozilla/dom/ErrorEventBinding.h" |
27 | | #include "mozilla/dom/quota/QuotaManager.h" |
28 | | #include "mozilla/dom/WorkerScope.h" |
29 | | #include "mozilla/dom/WorkerPrivate.h" |
30 | | #include "mozilla/ipc/BackgroundChild.h" |
31 | | #include "mozilla/ipc/BackgroundParent.h" |
32 | | #include "mozilla/ipc/PBackgroundChild.h" |
33 | | #include "nsContentUtils.h" |
34 | | #include "nsGlobalWindow.h" |
35 | | #include "nsThreadUtils.h" |
36 | | #include "mozilla/Logging.h" |
37 | | |
38 | | #include "FileInfo.h" |
39 | | #include "FileManager.h" |
40 | | #include "IDBEvents.h" |
41 | | #include "IDBFactory.h" |
42 | | #include "IDBKeyRange.h" |
43 | | #include "IDBRequest.h" |
44 | | #include "ProfilerHelpers.h" |
45 | | #include "ScriptErrorHelper.h" |
46 | | #include "nsCharSeparatedTokenizer.h" |
47 | | #include "unicode/locid.h" |
48 | | |
49 | | // Bindings for ResolveConstructors |
50 | | #include "mozilla/dom/IDBCursorBinding.h" |
51 | | #include "mozilla/dom/IDBDatabaseBinding.h" |
52 | | #include "mozilla/dom/IDBFactoryBinding.h" |
53 | | #include "mozilla/dom/IDBIndexBinding.h" |
54 | | #include "mozilla/dom/IDBKeyRangeBinding.h" |
55 | | #include "mozilla/dom/IDBMutableFileBinding.h" |
56 | | #include "mozilla/dom/IDBObjectStoreBinding.h" |
57 | | #include "mozilla/dom/IDBOpenDBRequestBinding.h" |
58 | | #include "mozilla/dom/IDBRequestBinding.h" |
59 | | #include "mozilla/dom/IDBTransactionBinding.h" |
60 | | #include "mozilla/dom/IDBVersionChangeEventBinding.h" |
61 | | |
62 | 0 | #define IDB_STR "indexedDB" |
63 | | |
64 | | namespace mozilla { |
65 | | namespace dom { |
66 | | namespace indexedDB { |
67 | | |
68 | | using namespace mozilla::dom::quota; |
69 | | using namespace mozilla::ipc; |
70 | | |
71 | | class FileManagerInfo |
72 | | { |
73 | | public: |
74 | | already_AddRefed<FileManager> |
75 | | GetFileManager(PersistenceType aPersistenceType, |
76 | | const nsAString& aName) const; |
77 | | |
78 | | void |
79 | | AddFileManager(FileManager* aFileManager); |
80 | | |
81 | | bool |
82 | | HasFileManagers() const |
83 | 0 | { |
84 | 0 | AssertIsOnIOThread(); |
85 | 0 |
|
86 | 0 | return !mPersistentStorageFileManagers.IsEmpty() || |
87 | 0 | !mTemporaryStorageFileManagers.IsEmpty() || |
88 | 0 | !mDefaultStorageFileManagers.IsEmpty(); |
89 | 0 | } |
90 | | |
91 | | void |
92 | | InvalidateAllFileManagers() const; |
93 | | |
94 | | void |
95 | | InvalidateAndRemoveFileManagers(PersistenceType aPersistenceType); |
96 | | |
97 | | void |
98 | | InvalidateAndRemoveFileManager(PersistenceType aPersistenceType, |
99 | | const nsAString& aName); |
100 | | |
101 | | private: |
102 | | nsTArray<RefPtr<FileManager> >& |
103 | | GetArray(PersistenceType aPersistenceType); |
104 | | |
105 | | const nsTArray<RefPtr<FileManager> >& |
106 | | GetImmutableArray(PersistenceType aPersistenceType) const |
107 | 0 | { |
108 | 0 | return const_cast<FileManagerInfo*>(this)->GetArray(aPersistenceType); |
109 | 0 | } |
110 | | |
111 | | nsTArray<RefPtr<FileManager> > mPersistentStorageFileManagers; |
112 | | nsTArray<RefPtr<FileManager> > mTemporaryStorageFileManagers; |
113 | | nsTArray<RefPtr<FileManager> > mDefaultStorageFileManagers; |
114 | | }; |
115 | | |
116 | | } // namespace indexedDB |
117 | | |
118 | | using namespace mozilla::dom::indexedDB; |
119 | | |
120 | | namespace { |
121 | | |
122 | | NS_DEFINE_IID(kIDBRequestIID, PRIVATE_IDBREQUEST_IID); |
123 | | |
124 | | const uint32_t kDeleteTimeoutMs = 1000; |
125 | | |
126 | | // The threshold we use for structured clone data storing. |
127 | | // Anything smaller than the threshold is compressed and stored in the database. |
128 | | // Anything larger is compressed and stored outside the database. |
129 | | const int32_t kDefaultDataThresholdBytes = 1024 * 1024; // 1MB |
130 | | |
131 | | // The maximal size of a serialized object to be transfered through IPC. |
132 | | const int32_t kDefaultMaxSerializedMsgSize = IPC::Channel::kMaximumMessageSize; |
133 | | |
134 | | #define IDB_PREF_BRANCH_ROOT "dom.indexedDB." |
135 | | |
136 | | const char kTestingPref[] = IDB_PREF_BRANCH_ROOT "testing"; |
137 | | const char kPrefExperimental[] = IDB_PREF_BRANCH_ROOT "experimental"; |
138 | | const char kPrefFileHandle[] = "dom.fileHandle.enabled"; |
139 | | const char kDataThresholdPref[] = IDB_PREF_BRANCH_ROOT "dataThreshold"; |
140 | | const char kPrefMaxSerilizedMsgSize[] = IDB_PREF_BRANCH_ROOT "maxSerializedMsgSize"; |
141 | | const char kPrefErrorEventToSelfError[] = IDB_PREF_BRANCH_ROOT "errorEventToSelfError"; |
142 | | |
143 | | #define IDB_PREF_LOGGING_BRANCH_ROOT IDB_PREF_BRANCH_ROOT "logging." |
144 | | |
145 | | const char kPrefLoggingEnabled[] = IDB_PREF_LOGGING_BRANCH_ROOT "enabled"; |
146 | | const char kPrefLoggingDetails[] = IDB_PREF_LOGGING_BRANCH_ROOT "details"; |
147 | | |
148 | | #if defined(DEBUG) || defined(MOZ_GECKO_PROFILER) |
149 | | const char kPrefLoggingProfiler[] = |
150 | | IDB_PREF_LOGGING_BRANCH_ROOT "profiler-marks"; |
151 | | #endif |
152 | | |
153 | | #undef IDB_PREF_LOGGING_BRANCH_ROOT |
154 | | #undef IDB_PREF_BRANCH_ROOT |
155 | | |
156 | | StaticRefPtr<IndexedDatabaseManager> gDBManager; |
157 | | |
158 | | Atomic<bool> gInitialized(false); |
159 | | Atomic<bool> gClosed(false); |
160 | | Atomic<bool> gTestingMode(false); |
161 | | Atomic<bool> gExperimentalFeaturesEnabled(false); |
162 | | Atomic<bool> gFileHandleEnabled(false); |
163 | | Atomic<bool> gPrefErrorEventToSelfError(false); |
164 | | Atomic<int32_t> gDataThresholdBytes(0); |
165 | | Atomic<int32_t> gMaxSerializedMsgSize(0); |
166 | | |
167 | | class DeleteFilesRunnable final |
168 | | : public nsIRunnable |
169 | | , public OpenDirectoryListener |
170 | | { |
171 | | typedef mozilla::dom::quota::DirectoryLock DirectoryLock; |
172 | | |
173 | | enum State |
174 | | { |
175 | | // Just created on the main thread. Next step is State_DirectoryOpenPending. |
176 | | State_Initial, |
177 | | |
178 | | // Waiting for directory open allowed on the main thread. The next step is |
179 | | // State_DatabaseWorkOpen. |
180 | | State_DirectoryOpenPending, |
181 | | |
182 | | // Waiting to do/doing work on the QuotaManager IO thread. The next step is |
183 | | // State_UnblockingOpen. |
184 | | State_DatabaseWorkOpen, |
185 | | |
186 | | // Notifying the QuotaManager that it can proceed to the next operation on |
187 | | // the main thread. Next step is State_Completed. |
188 | | State_UnblockingOpen, |
189 | | |
190 | | // All done. |
191 | | State_Completed |
192 | | }; |
193 | | |
194 | | nsCOMPtr<nsIEventTarget> mBackgroundThread; |
195 | | |
196 | | RefPtr<FileManager> mFileManager; |
197 | | nsTArray<int64_t> mFileIds; |
198 | | |
199 | | RefPtr<DirectoryLock> mDirectoryLock; |
200 | | |
201 | | nsCOMPtr<nsIFile> mDirectory; |
202 | | nsCOMPtr<nsIFile> mJournalDirectory; |
203 | | |
204 | | State mState; |
205 | | |
206 | | public: |
207 | | DeleteFilesRunnable(nsIEventTarget* aBackgroundThread, |
208 | | FileManager* aFileManager, |
209 | | nsTArray<int64_t>& aFileIds); |
210 | | |
211 | | void |
212 | | Dispatch(); |
213 | | |
214 | | NS_DECL_THREADSAFE_ISUPPORTS |
215 | | NS_DECL_NSIRUNNABLE |
216 | | |
217 | | virtual void |
218 | | DirectoryLockAcquired(DirectoryLock* aLock) override; |
219 | | |
220 | | virtual void |
221 | | DirectoryLockFailed() override; |
222 | | |
223 | | private: |
224 | 0 | ~DeleteFilesRunnable() {} |
225 | | |
226 | | nsresult |
227 | | Open(); |
228 | | |
229 | | nsresult |
230 | | DeleteFile(int64_t aFileId); |
231 | | |
232 | | nsresult |
233 | | DoDatabaseWork(); |
234 | | |
235 | | void |
236 | | Finish(); |
237 | | |
238 | | void |
239 | | UnblockOpen(); |
240 | | }; |
241 | | |
242 | | void |
243 | | AtomicBoolPrefChangedCallback(const char* aPrefName, Atomic<bool>* aClosure) |
244 | 0 | { |
245 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
246 | 0 | MOZ_ASSERT(aClosure); |
247 | 0 |
|
248 | 0 | *aClosure = Preferences::GetBool(aPrefName); |
249 | 0 | } |
250 | | |
251 | | void |
252 | | DataThresholdPrefChangedCallback(const char* aPrefName, void* aClosure) |
253 | 0 | { |
254 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
255 | 0 | MOZ_ASSERT(!strcmp(aPrefName, kDataThresholdPref)); |
256 | 0 | MOZ_ASSERT(!aClosure); |
257 | 0 |
|
258 | 0 | int32_t dataThresholdBytes = |
259 | 0 | Preferences::GetInt(aPrefName, kDefaultDataThresholdBytes); |
260 | 0 |
|
261 | 0 | // The magic -1 is for use only by tests that depend on stable blob file id's. |
262 | 0 | if (dataThresholdBytes == -1) { |
263 | 0 | dataThresholdBytes = INT32_MAX; |
264 | 0 | } |
265 | 0 |
|
266 | 0 | gDataThresholdBytes = dataThresholdBytes; |
267 | 0 | } |
268 | | |
269 | | void |
270 | | MaxSerializedMsgSizePrefChangeCallback(const char* aPrefName, void* aClosure) |
271 | 0 | { |
272 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
273 | 0 | MOZ_ASSERT(!strcmp(aPrefName, kPrefMaxSerilizedMsgSize)); |
274 | 0 | MOZ_ASSERT(!aClosure); |
275 | 0 |
|
276 | 0 | gMaxSerializedMsgSize = |
277 | 0 | Preferences::GetInt(aPrefName, kDefaultMaxSerializedMsgSize); |
278 | 0 | MOZ_ASSERT(gMaxSerializedMsgSize > 0); |
279 | 0 | } |
280 | | |
281 | | } // namespace |
282 | | |
283 | | IndexedDatabaseManager::IndexedDatabaseManager() |
284 | | : mFileMutex("IndexedDatabaseManager.mFileMutex") |
285 | | , mBackgroundActor(nullptr) |
286 | 0 | { |
287 | 0 | NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
288 | 0 | } |
289 | | |
290 | | IndexedDatabaseManager::~IndexedDatabaseManager() |
291 | 0 | { |
292 | 0 | NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
293 | 0 |
|
294 | 0 | if (mBackgroundActor) { |
295 | 0 | mBackgroundActor->SendDeleteMeInternal(); |
296 | 0 | MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!"); |
297 | 0 | } |
298 | 0 | } |
299 | | |
300 | | bool IndexedDatabaseManager::sIsMainProcess = false; |
301 | | bool IndexedDatabaseManager::sFullSynchronousMode = false; |
302 | | |
303 | | mozilla::LazyLogModule IndexedDatabaseManager::sLoggingModule("IndexedDB"); |
304 | | |
305 | | Atomic<IndexedDatabaseManager::LoggingMode> |
306 | | IndexedDatabaseManager::sLoggingMode( |
307 | | IndexedDatabaseManager::Logging_Disabled); |
308 | | |
309 | | // static |
310 | | IndexedDatabaseManager* |
311 | | IndexedDatabaseManager::GetOrCreate() |
312 | 0 | { |
313 | 0 | NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
314 | 0 |
|
315 | 0 | if (IsClosed()) { |
316 | 0 | NS_ERROR("Calling GetOrCreate() after shutdown!"); |
317 | 0 | return nullptr; |
318 | 0 | } |
319 | 0 |
|
320 | 0 | if (!gDBManager) { |
321 | 0 | sIsMainProcess = XRE_IsParentProcess(); |
322 | 0 |
|
323 | 0 | RefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager()); |
324 | 0 |
|
325 | 0 | nsresult rv = instance->Init(); |
326 | 0 | NS_ENSURE_SUCCESS(rv, nullptr); |
327 | 0 |
|
328 | 0 | if (gInitialized.exchange(true)) { |
329 | 0 | NS_ERROR("Initialized more than once?!"); |
330 | 0 | } |
331 | 0 |
|
332 | 0 | gDBManager = instance; |
333 | 0 |
|
334 | 0 | ClearOnShutdown(&gDBManager); |
335 | 0 | } |
336 | 0 |
|
337 | 0 | return gDBManager; |
338 | 0 | } |
339 | | |
340 | | // static |
341 | | IndexedDatabaseManager* |
342 | | IndexedDatabaseManager::Get() |
343 | 0 | { |
344 | 0 | // Does not return an owning reference. |
345 | 0 | return gDBManager; |
346 | 0 | } |
347 | | |
348 | | nsresult |
349 | | IndexedDatabaseManager::Init() |
350 | 0 | { |
351 | 0 | NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
352 | 0 |
|
353 | 0 | // During Init() we can't yet call IsMainProcess(), just check sIsMainProcess |
354 | 0 | // directly. |
355 | 0 | if (sIsMainProcess) { |
356 | 0 | mDeleteTimer = NS_NewTimer(); |
357 | 0 | NS_ENSURE_STATE(mDeleteTimer); |
358 | 0 |
|
359 | 0 | if (QuotaManager* quotaManager = QuotaManager::Get()) { |
360 | 0 | NoteLiveQuotaManager(quotaManager); |
361 | 0 | } |
362 | 0 | } |
363 | 0 |
|
364 | 0 | Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback, |
365 | 0 | kTestingPref, |
366 | 0 | &gTestingMode); |
367 | 0 | Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback, |
368 | 0 | kPrefExperimental, |
369 | 0 | &gExperimentalFeaturesEnabled); |
370 | 0 | Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback, |
371 | 0 | kPrefFileHandle, |
372 | 0 | &gFileHandleEnabled); |
373 | 0 | Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback, |
374 | 0 | kPrefErrorEventToSelfError, |
375 | 0 | &gPrefErrorEventToSelfError); |
376 | 0 |
|
377 | 0 | // By default IndexedDB uses SQLite with PRAGMA synchronous = NORMAL. This |
378 | 0 | // guarantees (unlike synchronous = OFF) atomicity and consistency, but not |
379 | 0 | // necessarily durability in situations such as power loss. This preference |
380 | 0 | // allows enabling PRAGMA synchronous = FULL on SQLite, which does guarantee |
381 | 0 | // durability, but with an extra fsync() and the corresponding performance |
382 | 0 | // hit. |
383 | 0 | sFullSynchronousMode = Preferences::GetBool("dom.indexedDB.fullSynchronous"); |
384 | 0 |
|
385 | 0 | Preferences::RegisterCallback(LoggingModePrefChangedCallback, |
386 | 0 | kPrefLoggingDetails); |
387 | 0 | #ifdef MOZ_GECKO_PROFILER |
388 | 0 | Preferences::RegisterCallback(LoggingModePrefChangedCallback, |
389 | 0 | kPrefLoggingProfiler); |
390 | 0 | #endif |
391 | 0 | Preferences::RegisterCallbackAndCall(LoggingModePrefChangedCallback, |
392 | 0 | kPrefLoggingEnabled); |
393 | 0 |
|
394 | 0 | Preferences::RegisterCallbackAndCall(DataThresholdPrefChangedCallback, |
395 | 0 | kDataThresholdPref); |
396 | 0 |
|
397 | 0 | Preferences::RegisterCallbackAndCall(MaxSerializedMsgSizePrefChangeCallback, |
398 | 0 | kPrefMaxSerilizedMsgSize); |
399 | 0 |
|
400 | 0 | nsAutoCString acceptLang; |
401 | 0 | Preferences::GetLocalizedCString("intl.accept_languages", acceptLang); |
402 | 0 |
|
403 | 0 | // Split values on commas. |
404 | 0 | nsCCharSeparatedTokenizer langTokenizer(acceptLang, ','); |
405 | 0 | while (langTokenizer.hasMoreTokens()) { |
406 | 0 | nsAutoCString lang(langTokenizer.nextToken()); |
407 | 0 | icu::Locale locale = icu::Locale::createCanonical(lang.get()); |
408 | 0 | if (!locale.isBogus()) { |
409 | 0 | // icu::Locale::getBaseName is always ASCII as per BCP 47 |
410 | 0 | mLocale.AssignASCII(locale.getBaseName()); |
411 | 0 | break; |
412 | 0 | } |
413 | 0 | } |
414 | 0 |
|
415 | 0 | if (mLocale.IsEmpty()) { |
416 | 0 | mLocale.AssignLiteral("en_US"); |
417 | 0 | } |
418 | 0 |
|
419 | 0 | return NS_OK; |
420 | 0 | } |
421 | | |
422 | | void |
423 | | IndexedDatabaseManager::Destroy() |
424 | 0 | { |
425 | 0 | // Setting the closed flag prevents the service from being recreated. |
426 | 0 | // Don't set it though if there's no real instance created. |
427 | 0 | if (gInitialized && gClosed.exchange(true)) { |
428 | 0 | NS_ERROR("Shutdown more than once?!"); |
429 | 0 | } |
430 | 0 |
|
431 | 0 | if (sIsMainProcess && mDeleteTimer) { |
432 | 0 | if (NS_FAILED(mDeleteTimer->Cancel())) { |
433 | 0 | NS_WARNING("Failed to cancel timer!"); |
434 | 0 | } |
435 | 0 |
|
436 | 0 | mDeleteTimer = nullptr; |
437 | 0 | } |
438 | 0 |
|
439 | 0 | Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback, |
440 | 0 | kTestingPref, |
441 | 0 | &gTestingMode); |
442 | 0 | Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback, |
443 | 0 | kPrefExperimental, |
444 | 0 | &gExperimentalFeaturesEnabled); |
445 | 0 | Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback, |
446 | 0 | kPrefFileHandle, |
447 | 0 | &gFileHandleEnabled); |
448 | 0 | Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback, |
449 | 0 | kPrefErrorEventToSelfError, |
450 | 0 | &gPrefErrorEventToSelfError); |
451 | 0 |
|
452 | 0 | Preferences::UnregisterCallback(LoggingModePrefChangedCallback, |
453 | 0 | kPrefLoggingDetails); |
454 | 0 | #ifdef MOZ_GECKO_PROFILER |
455 | 0 | Preferences::UnregisterCallback(LoggingModePrefChangedCallback, |
456 | 0 | kPrefLoggingProfiler); |
457 | 0 | #endif |
458 | 0 | Preferences::UnregisterCallback(LoggingModePrefChangedCallback, |
459 | 0 | kPrefLoggingEnabled); |
460 | 0 |
|
461 | 0 | Preferences::UnregisterCallback(DataThresholdPrefChangedCallback, |
462 | 0 | kDataThresholdPref); |
463 | 0 |
|
464 | 0 | Preferences::UnregisterCallback(MaxSerializedMsgSizePrefChangeCallback, |
465 | 0 | kPrefMaxSerilizedMsgSize); |
466 | 0 |
|
467 | 0 | delete this; |
468 | 0 | } |
469 | | |
470 | | // static |
471 | | nsresult |
472 | | IndexedDatabaseManager::CommonPostHandleEvent(EventChainPostVisitor& aVisitor, |
473 | | IDBFactory* aFactory) |
474 | 0 | { |
475 | 0 | MOZ_ASSERT(aVisitor.mDOMEvent); |
476 | 0 | MOZ_ASSERT(aFactory); |
477 | 0 |
|
478 | 0 | if (!gPrefErrorEventToSelfError) { |
479 | 0 | return NS_OK; |
480 | 0 | } |
481 | 0 | |
482 | 0 | if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) { |
483 | 0 | return NS_OK; |
484 | 0 | } |
485 | 0 | |
486 | 0 | if (!aVisitor.mDOMEvent->IsTrusted()) { |
487 | 0 | return NS_OK; |
488 | 0 | } |
489 | 0 | |
490 | 0 | nsAutoString type; |
491 | 0 | aVisitor.mDOMEvent->GetType(type); |
492 | 0 |
|
493 | 0 | MOZ_ASSERT(nsDependentString(kErrorEventType).EqualsLiteral("error")); |
494 | 0 | if (!type.EqualsLiteral("error")) { |
495 | 0 | return NS_OK; |
496 | 0 | } |
497 | 0 | |
498 | 0 | nsCOMPtr<EventTarget> eventTarget = aVisitor.mDOMEvent->GetTarget(); |
499 | 0 | MOZ_ASSERT(eventTarget); |
500 | 0 |
|
501 | 0 | // Only mess with events that were originally targeted to an IDBRequest. |
502 | 0 | RefPtr<IDBRequest> request; |
503 | 0 | if (NS_FAILED(eventTarget->QueryInterface(kIDBRequestIID, |
504 | 0 | getter_AddRefs(request))) || |
505 | 0 | !request) { |
506 | 0 | return NS_OK; |
507 | 0 | } |
508 | 0 | |
509 | 0 | RefPtr<DOMException> error = request->GetErrorAfterResult(); |
510 | 0 |
|
511 | 0 | nsString errorName; |
512 | 0 | if (error) { |
513 | 0 | error->GetName(errorName); |
514 | 0 | } |
515 | 0 |
|
516 | 0 | RootedDictionary<ErrorEventInit> init(RootingCx()); |
517 | 0 | request->GetCallerLocation(init.mFilename, &init.mLineno, &init.mColno); |
518 | 0 |
|
519 | 0 | init.mMessage = errorName; |
520 | 0 | init.mCancelable = true; |
521 | 0 | init.mBubbles = true; |
522 | 0 |
|
523 | 0 | nsEventStatus status = nsEventStatus_eIgnore; |
524 | 0 |
|
525 | 0 | if (NS_IsMainThread()) { |
526 | 0 | nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(eventTarget->GetOwnerGlobal()); |
527 | 0 | if (window) { |
528 | 0 | nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window); |
529 | 0 | MOZ_ASSERT(sgo); |
530 | 0 |
|
531 | 0 | if (NS_WARN_IF(!sgo->HandleScriptError(init, &status))) { |
532 | 0 | status = nsEventStatus_eIgnore; |
533 | 0 | } |
534 | 0 | } else { |
535 | 0 | // We don't fire error events at any global for non-window JS on the main |
536 | 0 | // thread. |
537 | 0 | } |
538 | 0 | } else { |
539 | 0 | // Not on the main thread, must be in a worker. |
540 | 0 | WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); |
541 | 0 | MOZ_ASSERT(workerPrivate); |
542 | 0 |
|
543 | 0 | RefPtr<WorkerGlobalScope> globalScope = workerPrivate->GlobalScope(); |
544 | 0 | MOZ_ASSERT(globalScope); |
545 | 0 |
|
546 | 0 | RefPtr<ErrorEvent> errorEvent = |
547 | 0 | ErrorEvent::Constructor(globalScope, |
548 | 0 | nsDependentString(kErrorEventType), |
549 | 0 | init); |
550 | 0 | MOZ_ASSERT(errorEvent); |
551 | 0 |
|
552 | 0 | errorEvent->SetTrusted(true); |
553 | 0 |
|
554 | 0 | auto* target = static_cast<EventTarget*>(globalScope.get()); |
555 | 0 |
|
556 | 0 | if (NS_WARN_IF(NS_FAILED( |
557 | 0 | EventDispatcher::DispatchDOMEvent(target, |
558 | 0 | /* aWidgetEvent */ nullptr, |
559 | 0 | errorEvent, |
560 | 0 | /* aPresContext */ nullptr, |
561 | 0 | &status)))) { |
562 | 0 | status = nsEventStatus_eIgnore; |
563 | 0 | } |
564 | 0 | } |
565 | 0 |
|
566 | 0 | if (status == nsEventStatus_eConsumeNoDefault) { |
567 | 0 | return NS_OK; |
568 | 0 | } |
569 | 0 | |
570 | 0 | // Log the error to the error console. |
571 | 0 | ScriptErrorHelper::Dump(errorName, |
572 | 0 | init.mFilename, |
573 | 0 | init.mLineno, |
574 | 0 | init.mColno, |
575 | 0 | nsIScriptError::errorFlag, |
576 | 0 | aFactory->IsChrome(), |
577 | 0 | aFactory->InnerWindowID()); |
578 | 0 |
|
579 | 0 | return NS_OK; |
580 | 0 | } |
581 | | |
582 | | // static |
583 | | bool |
584 | | IndexedDatabaseManager::ResolveSandboxBinding(JSContext* aCx) |
585 | 0 | { |
586 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
587 | 0 | MOZ_ASSERT(js::GetObjectClass(JS::CurrentGlobalOrNull(aCx))->flags & |
588 | 0 | JSCLASS_DOM_GLOBAL, |
589 | 0 | "Passed object is not a global object!"); |
590 | 0 |
|
591 | 0 | // We need to ensure that the manager has been created already here so that we |
592 | 0 | // load preferences that may control which properties are exposed. |
593 | 0 | if (NS_WARN_IF(!GetOrCreate())) { |
594 | 0 | return false; |
595 | 0 | } |
596 | 0 | |
597 | 0 | if (!IDBCursor_Binding::GetConstructorObject(aCx) || |
598 | 0 | !IDBCursorWithValue_Binding::GetConstructorObject(aCx) || |
599 | 0 | !IDBDatabase_Binding::GetConstructorObject(aCx) || |
600 | 0 | !IDBFactory_Binding::GetConstructorObject(aCx) || |
601 | 0 | !IDBIndex_Binding::GetConstructorObject(aCx) || |
602 | 0 | !IDBKeyRange_Binding::GetConstructorObject(aCx) || |
603 | 0 | !IDBLocaleAwareKeyRange_Binding::GetConstructorObject(aCx) || |
604 | 0 | !IDBMutableFile_Binding::GetConstructorObject(aCx) || |
605 | 0 | !IDBObjectStore_Binding::GetConstructorObject(aCx) || |
606 | 0 | !IDBOpenDBRequest_Binding::GetConstructorObject(aCx) || |
607 | 0 | !IDBRequest_Binding::GetConstructorObject(aCx) || |
608 | 0 | !IDBTransaction_Binding::GetConstructorObject(aCx) || |
609 | 0 | !IDBVersionChangeEvent_Binding::GetConstructorObject(aCx)) |
610 | 0 | { |
611 | 0 | return false; |
612 | 0 | } |
613 | 0 | |
614 | 0 | return true; |
615 | 0 | } |
616 | | |
617 | | // static |
618 | | bool |
619 | | IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx, |
620 | | JS::Handle<JSObject*> aGlobal) |
621 | 0 | { |
622 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
623 | 0 | MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL, |
624 | 0 | "Passed object is not a global object!"); |
625 | 0 |
|
626 | 0 | RefPtr<IDBFactory> factory; |
627 | 0 | if (NS_FAILED(IDBFactory::CreateForMainThreadJS(aCx, |
628 | 0 | aGlobal, |
629 | 0 | getter_AddRefs(factory)))) { |
630 | 0 | return false; |
631 | 0 | } |
632 | 0 | |
633 | 0 | MOZ_ASSERT(factory, "This should never fail for chrome!"); |
634 | 0 |
|
635 | 0 | JS::Rooted<JS::Value> indexedDB(aCx); |
636 | 0 | js::AssertSameCompartment(aCx, aGlobal); |
637 | 0 | if (!GetOrCreateDOMReflector(aCx, factory, &indexedDB)) { |
638 | 0 | return false; |
639 | 0 | } |
640 | 0 | |
641 | 0 | return JS_DefineProperty(aCx, aGlobal, IDB_STR, indexedDB, JSPROP_ENUMERATE); |
642 | 0 | } |
643 | | |
644 | | // static |
645 | | bool |
646 | | IndexedDatabaseManager::IsClosed() |
647 | 0 | { |
648 | 0 | return gClosed; |
649 | 0 | } |
650 | | |
651 | | #ifdef DEBUG |
652 | | // static |
653 | | bool |
654 | | IndexedDatabaseManager::IsMainProcess() |
655 | | { |
656 | | NS_ASSERTION(gDBManager, |
657 | | "IsMainProcess() called before indexedDB has been initialized!"); |
658 | | NS_ASSERTION((XRE_IsParentProcess()) == |
659 | | sIsMainProcess, "XRE_GetProcessType changed its tune!"); |
660 | | return sIsMainProcess; |
661 | | } |
662 | | |
663 | | // static |
664 | | IndexedDatabaseManager::LoggingMode |
665 | | IndexedDatabaseManager::GetLoggingMode() |
666 | | { |
667 | | MOZ_ASSERT(gDBManager, |
668 | | "GetLoggingMode called before IndexedDatabaseManager has been " |
669 | | "initialized!"); |
670 | | |
671 | | return sLoggingMode; |
672 | | } |
673 | | |
674 | | // static |
675 | | mozilla::LogModule* |
676 | | IndexedDatabaseManager::GetLoggingModule() |
677 | | { |
678 | | MOZ_ASSERT(gDBManager, |
679 | | "GetLoggingModule called before IndexedDatabaseManager has been " |
680 | | "initialized!"); |
681 | | |
682 | | return sLoggingModule; |
683 | | } |
684 | | |
685 | | #endif // DEBUG |
686 | | |
687 | | // static |
688 | | bool |
689 | | IndexedDatabaseManager::InTestingMode() |
690 | 0 | { |
691 | 0 | MOZ_ASSERT(gDBManager, |
692 | 0 | "InTestingMode() called before indexedDB has been initialized!"); |
693 | 0 |
|
694 | 0 | return gTestingMode; |
695 | 0 | } |
696 | | |
697 | | // static |
698 | | bool |
699 | | IndexedDatabaseManager::FullSynchronous() |
700 | 0 | { |
701 | 0 | MOZ_ASSERT(gDBManager, |
702 | 0 | "FullSynchronous() called before indexedDB has been initialized!"); |
703 | 0 |
|
704 | 0 | return sFullSynchronousMode; |
705 | 0 | } |
706 | | |
707 | | // static |
708 | | bool |
709 | | IndexedDatabaseManager::ExperimentalFeaturesEnabled() |
710 | 0 | { |
711 | 0 | if (NS_IsMainThread()) { |
712 | 0 | if (NS_WARN_IF(!GetOrCreate())) { |
713 | 0 | return false; |
714 | 0 | } |
715 | 0 | } else { |
716 | 0 | MOZ_ASSERT(Get(), |
717 | 0 | "ExperimentalFeaturesEnabled() called off the main thread " |
718 | 0 | "before indexedDB has been initialized!"); |
719 | 0 | } |
720 | 0 |
|
721 | 0 | return gExperimentalFeaturesEnabled; |
722 | 0 | } |
723 | | |
724 | | // static |
725 | | bool |
726 | | IndexedDatabaseManager::ExperimentalFeaturesEnabled(JSContext* aCx, JSObject* aGlobal) |
727 | 0 | { |
728 | 0 | // If, in the child process, properties of the global object are enumerated |
729 | 0 | // before the chrome registry (and thus the value of |intl.accept_languages|) |
730 | 0 | // is ready, calling IndexedDatabaseManager::Init will permanently break |
731 | 0 | // that preference. We can retrieve gExperimentalFeaturesEnabled without |
732 | 0 | // actually going through IndexedDatabaseManager. |
733 | 0 | // See Bug 1198093 comment 14 for detailed explanation. |
734 | 0 | MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal)); |
735 | 0 | if (IsNonExposedGlobal(aCx, aGlobal, GlobalNames::BackstagePass)) { |
736 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
737 | 0 | static bool featureRetrieved = false; |
738 | 0 | if (!featureRetrieved) { |
739 | 0 | gExperimentalFeaturesEnabled = Preferences::GetBool(kPrefExperimental); |
740 | 0 | featureRetrieved = true; |
741 | 0 | } |
742 | 0 | return gExperimentalFeaturesEnabled; |
743 | 0 | } |
744 | 0 |
|
745 | 0 | return ExperimentalFeaturesEnabled(); |
746 | 0 | } |
747 | | |
748 | | // static |
749 | | bool |
750 | | IndexedDatabaseManager::IsFileHandleEnabled() |
751 | 0 | { |
752 | 0 | MOZ_ASSERT(gDBManager, |
753 | 0 | "IsFileHandleEnabled() called before indexedDB has been " |
754 | 0 | "initialized!"); |
755 | 0 |
|
756 | 0 | return gFileHandleEnabled; |
757 | 0 | } |
758 | | |
759 | | // static |
760 | | uint32_t |
761 | | IndexedDatabaseManager::DataThreshold() |
762 | 0 | { |
763 | 0 | MOZ_ASSERT(gDBManager, |
764 | 0 | "DataThreshold() called before indexedDB has been initialized!"); |
765 | 0 |
|
766 | 0 | return gDataThresholdBytes; |
767 | 0 | } |
768 | | |
769 | | // static |
770 | | uint32_t |
771 | | IndexedDatabaseManager::MaxSerializedMsgSize() |
772 | 0 | { |
773 | 0 | MOZ_ASSERT(gDBManager, |
774 | 0 | "MaxSerializedMsgSize() called before indexedDB has been initialized!"); |
775 | 0 | MOZ_ASSERT(gMaxSerializedMsgSize > 0); |
776 | 0 |
|
777 | 0 | return gMaxSerializedMsgSize; |
778 | 0 | } |
779 | | |
780 | | void |
781 | | IndexedDatabaseManager::ClearBackgroundActor() |
782 | 0 | { |
783 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
784 | 0 |
|
785 | 0 | mBackgroundActor = nullptr; |
786 | 0 | } |
787 | | |
788 | | void |
789 | | IndexedDatabaseManager::NoteLiveQuotaManager(QuotaManager* aQuotaManager) |
790 | 0 | { |
791 | 0 | // This can be called during Init, so we can't use IsMainProcess() yet. |
792 | 0 | MOZ_ASSERT(sIsMainProcess); |
793 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
794 | 0 | MOZ_ASSERT(aQuotaManager); |
795 | 0 |
|
796 | 0 | mBackgroundThread = aQuotaManager->OwningThread(); |
797 | 0 | } |
798 | | |
799 | | void |
800 | | IndexedDatabaseManager::NoteShuttingDownQuotaManager() |
801 | 0 | { |
802 | 0 | MOZ_ASSERT(IsMainProcess()); |
803 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
804 | 0 |
|
805 | 0 | MOZ_ALWAYS_SUCCEEDS(mDeleteTimer->Cancel()); |
806 | 0 |
|
807 | 0 | mBackgroundThread = nullptr; |
808 | 0 | } |
809 | | |
810 | | already_AddRefed<FileManager> |
811 | | IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType, |
812 | | const nsACString& aOrigin, |
813 | | const nsAString& aDatabaseName) |
814 | 0 | { |
815 | 0 | AssertIsOnIOThread(); |
816 | 0 |
|
817 | 0 | FileManagerInfo* info; |
818 | 0 | if (!mFileManagerInfos.Get(aOrigin, &info)) { |
819 | 0 | return nullptr; |
820 | 0 | } |
821 | 0 | |
822 | 0 | RefPtr<FileManager> fileManager = |
823 | 0 | info->GetFileManager(aPersistenceType, aDatabaseName); |
824 | 0 |
|
825 | 0 | return fileManager.forget(); |
826 | 0 | } |
827 | | |
828 | | void |
829 | | IndexedDatabaseManager::AddFileManager(FileManager* aFileManager) |
830 | 0 | { |
831 | 0 | AssertIsOnIOThread(); |
832 | 0 | NS_ASSERTION(aFileManager, "Null file manager!"); |
833 | 0 |
|
834 | 0 | FileManagerInfo* info; |
835 | 0 | if (!mFileManagerInfos.Get(aFileManager->Origin(), &info)) { |
836 | 0 | info = new FileManagerInfo(); |
837 | 0 | mFileManagerInfos.Put(aFileManager->Origin(), info); |
838 | 0 | } |
839 | 0 |
|
840 | 0 | info->AddFileManager(aFileManager); |
841 | 0 | } |
842 | | |
843 | | void |
844 | | IndexedDatabaseManager::InvalidateAllFileManagers() |
845 | 0 | { |
846 | 0 | AssertIsOnIOThread(); |
847 | 0 |
|
848 | 0 | for (auto iter = mFileManagerInfos.ConstIter(); !iter.Done(); iter.Next()) { |
849 | 0 | auto value = iter.Data(); |
850 | 0 | MOZ_ASSERT(value); |
851 | 0 |
|
852 | 0 | value->InvalidateAllFileManagers(); |
853 | 0 | } |
854 | 0 |
|
855 | 0 | mFileManagerInfos.Clear(); |
856 | 0 | } |
857 | | |
858 | | void |
859 | | IndexedDatabaseManager::InvalidateFileManagers(PersistenceType aPersistenceType, |
860 | | const nsACString& aOrigin) |
861 | 0 | { |
862 | 0 | AssertIsOnIOThread(); |
863 | 0 | MOZ_ASSERT(!aOrigin.IsEmpty()); |
864 | 0 |
|
865 | 0 | FileManagerInfo* info; |
866 | 0 | if (!mFileManagerInfos.Get(aOrigin, &info)) { |
867 | 0 | return; |
868 | 0 | } |
869 | 0 | |
870 | 0 | info->InvalidateAndRemoveFileManagers(aPersistenceType); |
871 | 0 |
|
872 | 0 | if (!info->HasFileManagers()) { |
873 | 0 | mFileManagerInfos.Remove(aOrigin); |
874 | 0 | } |
875 | 0 | } |
876 | | |
877 | | void |
878 | | IndexedDatabaseManager::InvalidateFileManager(PersistenceType aPersistenceType, |
879 | | const nsACString& aOrigin, |
880 | | const nsAString& aDatabaseName) |
881 | 0 | { |
882 | 0 | AssertIsOnIOThread(); |
883 | 0 |
|
884 | 0 | FileManagerInfo* info; |
885 | 0 | if (!mFileManagerInfos.Get(aOrigin, &info)) { |
886 | 0 | return; |
887 | 0 | } |
888 | 0 | |
889 | 0 | info->InvalidateAndRemoveFileManager(aPersistenceType, aDatabaseName); |
890 | 0 |
|
891 | 0 | if (!info->HasFileManagers()) { |
892 | 0 | mFileManagerInfos.Remove(aOrigin); |
893 | 0 | } |
894 | 0 | } |
895 | | |
896 | | nsresult |
897 | | IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager, |
898 | | int64_t aFileId) |
899 | 0 | { |
900 | 0 | MOZ_ASSERT(IsMainProcess()); |
901 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
902 | 0 | MOZ_ASSERT(aFileManager); |
903 | 0 | MOZ_ASSERT(aFileId > 0); |
904 | 0 | MOZ_ASSERT(mDeleteTimer); |
905 | 0 |
|
906 | 0 | if (!mBackgroundThread) { |
907 | 0 | return NS_OK; |
908 | 0 | } |
909 | 0 | |
910 | 0 | nsresult rv = mDeleteTimer->Cancel(); |
911 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
912 | 0 | return rv; |
913 | 0 | } |
914 | 0 | |
915 | 0 | rv = mDeleteTimer->InitWithCallback(this, kDeleteTimeoutMs, |
916 | 0 | nsITimer::TYPE_ONE_SHOT); |
917 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
918 | 0 | return rv; |
919 | 0 | } |
920 | 0 | |
921 | 0 | nsTArray<int64_t>* array; |
922 | 0 | if (!mPendingDeleteInfos.Get(aFileManager, &array)) { |
923 | 0 | array = new nsTArray<int64_t>(); |
924 | 0 | mPendingDeleteInfos.Put(aFileManager, array); |
925 | 0 | } |
926 | 0 |
|
927 | 0 | array->AppendElement(aFileId); |
928 | 0 |
|
929 | 0 | return NS_OK; |
930 | 0 | } |
931 | | |
932 | | nsresult |
933 | | IndexedDatabaseManager::BlockAndGetFileReferences( |
934 | | PersistenceType aPersistenceType, |
935 | | const nsACString& aOrigin, |
936 | | const nsAString& aDatabaseName, |
937 | | int64_t aFileId, |
938 | | int32_t* aRefCnt, |
939 | | int32_t* aDBRefCnt, |
940 | | int32_t* aSliceRefCnt, |
941 | | bool* aResult) |
942 | 0 | { |
943 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
944 | 0 |
|
945 | 0 | if (NS_WARN_IF(!InTestingMode())) { |
946 | 0 | return NS_ERROR_UNEXPECTED; |
947 | 0 | } |
948 | 0 | |
949 | 0 | if (!mBackgroundActor) { |
950 | 0 | PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread(); |
951 | 0 | if (NS_WARN_IF(!bgActor)) { |
952 | 0 | return NS_ERROR_FAILURE; |
953 | 0 | } |
954 | 0 | |
955 | 0 | BackgroundUtilsChild* actor = new BackgroundUtilsChild(this); |
956 | 0 |
|
957 | 0 | // We don't set event target for BackgroundUtilsChild because: |
958 | 0 | // 1. BackgroundUtilsChild is a singleton. |
959 | 0 | // 2. SendGetFileReferences is a sync operation to be returned asap if unlabeled. |
960 | 0 | // 3. The rest operations like DeleteMe/__delete__ only happens at shutdown. |
961 | 0 | // Hence, we should keep it unlabeled. |
962 | 0 | mBackgroundActor = |
963 | 0 | static_cast<BackgroundUtilsChild*>( |
964 | 0 | bgActor->SendPBackgroundIndexedDBUtilsConstructor(actor)); |
965 | 0 | } |
966 | 0 |
|
967 | 0 | if (NS_WARN_IF(!mBackgroundActor)) { |
968 | 0 | return NS_ERROR_FAILURE; |
969 | 0 | } |
970 | 0 | |
971 | 0 | if (!mBackgroundActor->SendGetFileReferences(aPersistenceType, |
972 | 0 | nsCString(aOrigin), |
973 | 0 | nsString(aDatabaseName), |
974 | 0 | aFileId, |
975 | 0 | aRefCnt, |
976 | 0 | aDBRefCnt, |
977 | 0 | aSliceRefCnt, |
978 | 0 | aResult)) { |
979 | 0 | return NS_ERROR_FAILURE; |
980 | 0 | } |
981 | 0 | |
982 | 0 | return NS_OK; |
983 | 0 | } |
984 | | |
985 | | nsresult |
986 | | IndexedDatabaseManager::FlushPendingFileDeletions() |
987 | 0 | { |
988 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
989 | 0 |
|
990 | 0 | if (NS_WARN_IF(!InTestingMode())) { |
991 | 0 | return NS_ERROR_UNEXPECTED; |
992 | 0 | } |
993 | 0 | |
994 | 0 | if (IsMainProcess()) { |
995 | 0 | nsresult rv = mDeleteTimer->Cancel(); |
996 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
997 | 0 | return rv; |
998 | 0 | } |
999 | 0 | |
1000 | 0 | rv = Notify(mDeleteTimer); |
1001 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1002 | 0 | return rv; |
1003 | 0 | } |
1004 | 0 | } else { |
1005 | 0 | PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread(); |
1006 | 0 | if (NS_WARN_IF(!bgActor)) { |
1007 | 0 | return NS_ERROR_FAILURE; |
1008 | 0 | } |
1009 | 0 | |
1010 | 0 | if (!bgActor->SendFlushPendingFileDeletions()) { |
1011 | 0 | return NS_ERROR_FAILURE; |
1012 | 0 | } |
1013 | 0 | } |
1014 | 0 | |
1015 | 0 | return NS_OK; |
1016 | 0 | } |
1017 | | |
1018 | | // static |
1019 | | void |
1020 | | IndexedDatabaseManager::LoggingModePrefChangedCallback( |
1021 | | const char* /* aPrefName */, |
1022 | | void* /* aClosure */) |
1023 | 0 | { |
1024 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1025 | 0 |
|
1026 | 0 | if (!Preferences::GetBool(kPrefLoggingEnabled)) { |
1027 | 0 | sLoggingMode = Logging_Disabled; |
1028 | 0 | return; |
1029 | 0 | } |
1030 | 0 | |
1031 | 0 | bool useProfiler = |
1032 | 0 | #if defined(DEBUG) || defined(MOZ_GECKO_PROFILER) |
1033 | 0 | Preferences::GetBool(kPrefLoggingProfiler); |
1034 | | #if !defined(MOZ_GECKO_PROFILER) |
1035 | | if (useProfiler) { |
1036 | | NS_WARNING("IndexedDB cannot create profiler marks because this build does " |
1037 | | "not have profiler extensions enabled!"); |
1038 | | useProfiler = false; |
1039 | | } |
1040 | | #endif |
1041 | | #else |
1042 | | false; |
1043 | | #endif |
1044 | |
|
1045 | 0 | const bool logDetails = Preferences::GetBool(kPrefLoggingDetails); |
1046 | 0 |
|
1047 | 0 | if (useProfiler) { |
1048 | 0 | sLoggingMode = logDetails ? |
1049 | 0 | Logging_DetailedProfilerMarks : |
1050 | 0 | Logging_ConciseProfilerMarks; |
1051 | 0 | } else { |
1052 | 0 | sLoggingMode = logDetails ? Logging_Detailed : Logging_Concise; |
1053 | 0 | } |
1054 | 0 | } |
1055 | | |
1056 | | // static |
1057 | | const nsCString& |
1058 | | IndexedDatabaseManager::GetLocale() |
1059 | 0 | { |
1060 | 0 | IndexedDatabaseManager* idbManager = Get(); |
1061 | 0 | MOZ_ASSERT(idbManager, "IDBManager is not ready!"); |
1062 | 0 |
|
1063 | 0 | return idbManager->mLocale; |
1064 | 0 | } |
1065 | | |
1066 | | NS_IMPL_ADDREF(IndexedDatabaseManager) |
1067 | | NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy()) |
1068 | | NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsITimerCallback, nsINamed) |
1069 | | |
1070 | | NS_IMETHODIMP |
1071 | | IndexedDatabaseManager::Notify(nsITimer* aTimer) |
1072 | 0 | { |
1073 | 0 | MOZ_ASSERT(IsMainProcess()); |
1074 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1075 | 0 | MOZ_ASSERT(mBackgroundThread); |
1076 | 0 |
|
1077 | 0 | for (auto iter = mPendingDeleteInfos.ConstIter(); !iter.Done(); iter.Next()) { |
1078 | 0 | auto key = iter.Key(); |
1079 | 0 | auto value = iter.Data(); |
1080 | 0 | MOZ_ASSERT(!value->IsEmpty()); |
1081 | 0 |
|
1082 | 0 | RefPtr<DeleteFilesRunnable> runnable = |
1083 | 0 | new DeleteFilesRunnable(mBackgroundThread, key, *value); |
1084 | 0 |
|
1085 | 0 | MOZ_ASSERT(value->IsEmpty()); |
1086 | 0 |
|
1087 | 0 | runnable->Dispatch(); |
1088 | 0 | } |
1089 | 0 |
|
1090 | 0 | mPendingDeleteInfos.Clear(); |
1091 | 0 |
|
1092 | 0 | return NS_OK; |
1093 | 0 | } |
1094 | | |
1095 | | NS_IMETHODIMP |
1096 | | IndexedDatabaseManager::GetName(nsACString& aName) |
1097 | 0 | { |
1098 | 0 | aName.AssignLiteral("IndexedDatabaseManager"); |
1099 | 0 | return NS_OK; |
1100 | 0 | } |
1101 | | |
1102 | | already_AddRefed<FileManager> |
1103 | | FileManagerInfo::GetFileManager(PersistenceType aPersistenceType, |
1104 | | const nsAString& aName) const |
1105 | 0 | { |
1106 | 0 | AssertIsOnIOThread(); |
1107 | 0 |
|
1108 | 0 | const nsTArray<RefPtr<FileManager> >& managers = |
1109 | 0 | GetImmutableArray(aPersistenceType); |
1110 | 0 |
|
1111 | 0 | for (uint32_t i = 0; i < managers.Length(); i++) { |
1112 | 0 | const RefPtr<FileManager>& fileManager = managers[i]; |
1113 | 0 |
|
1114 | 0 | if (fileManager->DatabaseName() == aName) { |
1115 | 0 | RefPtr<FileManager> result = fileManager; |
1116 | 0 | return result.forget(); |
1117 | 0 | } |
1118 | 0 | } |
1119 | 0 |
|
1120 | 0 | return nullptr; |
1121 | 0 | } |
1122 | | |
1123 | | void |
1124 | | FileManagerInfo::AddFileManager(FileManager* aFileManager) |
1125 | 0 | { |
1126 | 0 | AssertIsOnIOThread(); |
1127 | 0 |
|
1128 | 0 | nsTArray<RefPtr<FileManager> >& managers = GetArray(aFileManager->Type()); |
1129 | 0 |
|
1130 | 0 | NS_ASSERTION(!managers.Contains(aFileManager), "Adding more than once?!"); |
1131 | 0 |
|
1132 | 0 | managers.AppendElement(aFileManager); |
1133 | 0 | } |
1134 | | |
1135 | | void |
1136 | | FileManagerInfo::InvalidateAllFileManagers() const |
1137 | 0 | { |
1138 | 0 | AssertIsOnIOThread(); |
1139 | 0 |
|
1140 | 0 | uint32_t i; |
1141 | 0 |
|
1142 | 0 | for (i = 0; i < mPersistentStorageFileManagers.Length(); i++) { |
1143 | 0 | mPersistentStorageFileManagers[i]->Invalidate(); |
1144 | 0 | } |
1145 | 0 |
|
1146 | 0 | for (i = 0; i < mTemporaryStorageFileManagers.Length(); i++) { |
1147 | 0 | mTemporaryStorageFileManagers[i]->Invalidate(); |
1148 | 0 | } |
1149 | 0 |
|
1150 | 0 | for (i = 0; i < mDefaultStorageFileManagers.Length(); i++) { |
1151 | 0 | mDefaultStorageFileManagers[i]->Invalidate(); |
1152 | 0 | } |
1153 | 0 | } |
1154 | | |
1155 | | void |
1156 | | FileManagerInfo::InvalidateAndRemoveFileManagers( |
1157 | | PersistenceType aPersistenceType) |
1158 | 0 | { |
1159 | 0 | AssertIsOnIOThread(); |
1160 | 0 |
|
1161 | 0 | nsTArray<RefPtr<FileManager > >& managers = GetArray(aPersistenceType); |
1162 | 0 |
|
1163 | 0 | for (uint32_t i = 0; i < managers.Length(); i++) { |
1164 | 0 | managers[i]->Invalidate(); |
1165 | 0 | } |
1166 | 0 |
|
1167 | 0 | managers.Clear(); |
1168 | 0 | } |
1169 | | |
1170 | | void |
1171 | | FileManagerInfo::InvalidateAndRemoveFileManager( |
1172 | | PersistenceType aPersistenceType, |
1173 | | const nsAString& aName) |
1174 | 0 | { |
1175 | 0 | AssertIsOnIOThread(); |
1176 | 0 |
|
1177 | 0 | nsTArray<RefPtr<FileManager > >& managers = GetArray(aPersistenceType); |
1178 | 0 |
|
1179 | 0 | for (uint32_t i = 0; i < managers.Length(); i++) { |
1180 | 0 | RefPtr<FileManager>& fileManager = managers[i]; |
1181 | 0 | if (fileManager->DatabaseName() == aName) { |
1182 | 0 | fileManager->Invalidate(); |
1183 | 0 | managers.RemoveElementAt(i); |
1184 | 0 | return; |
1185 | 0 | } |
1186 | 0 | } |
1187 | 0 | } |
1188 | | |
1189 | | nsTArray<RefPtr<FileManager> >& |
1190 | | FileManagerInfo::GetArray(PersistenceType aPersistenceType) |
1191 | 0 | { |
1192 | 0 | switch (aPersistenceType) { |
1193 | 0 | case PERSISTENCE_TYPE_PERSISTENT: |
1194 | 0 | return mPersistentStorageFileManagers; |
1195 | 0 | case PERSISTENCE_TYPE_TEMPORARY: |
1196 | 0 | return mTemporaryStorageFileManagers; |
1197 | 0 | case PERSISTENCE_TYPE_DEFAULT: |
1198 | 0 | return mDefaultStorageFileManagers; |
1199 | 0 |
|
1200 | 0 | case PERSISTENCE_TYPE_INVALID: |
1201 | 0 | default: |
1202 | 0 | MOZ_CRASH("Bad storage type value!"); |
1203 | 0 | } |
1204 | 0 | } |
1205 | | |
1206 | | DeleteFilesRunnable::DeleteFilesRunnable(nsIEventTarget* aBackgroundThread, |
1207 | | FileManager* aFileManager, |
1208 | | nsTArray<int64_t>& aFileIds) |
1209 | | : mBackgroundThread(aBackgroundThread) |
1210 | | , mFileManager(aFileManager) |
1211 | | , mState(State_Initial) |
1212 | 0 | { |
1213 | 0 | mFileIds.SwapElements(aFileIds); |
1214 | 0 | } |
1215 | | |
1216 | | void |
1217 | | DeleteFilesRunnable::Dispatch() |
1218 | 0 | { |
1219 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1220 | 0 | MOZ_ASSERT(mState == State_Initial); |
1221 | 0 |
|
1222 | 0 | MOZ_ALWAYS_SUCCEEDS(mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL)); |
1223 | 0 | } |
1224 | | |
1225 | | NS_IMPL_ISUPPORTS(DeleteFilesRunnable, nsIRunnable) |
1226 | | |
1227 | | NS_IMETHODIMP |
1228 | | DeleteFilesRunnable::Run() |
1229 | 0 | { |
1230 | 0 | nsresult rv; |
1231 | 0 |
|
1232 | 0 | switch (mState) { |
1233 | 0 | case State_Initial: |
1234 | 0 | rv = Open(); |
1235 | 0 | break; |
1236 | 0 |
|
1237 | 0 | case State_DatabaseWorkOpen: |
1238 | 0 | rv = DoDatabaseWork(); |
1239 | 0 | break; |
1240 | 0 |
|
1241 | 0 | case State_UnblockingOpen: |
1242 | 0 | UnblockOpen(); |
1243 | 0 | return NS_OK; |
1244 | 0 |
|
1245 | 0 | case State_DirectoryOpenPending: |
1246 | 0 | default: |
1247 | 0 | MOZ_CRASH("Should never get here!"); |
1248 | 0 | } |
1249 | 0 |
|
1250 | 0 | if (NS_WARN_IF(NS_FAILED(rv)) && mState != State_UnblockingOpen) { |
1251 | 0 | Finish(); |
1252 | 0 | } |
1253 | 0 |
|
1254 | 0 | return NS_OK; |
1255 | 0 | } |
1256 | | |
1257 | | void |
1258 | | DeleteFilesRunnable::DirectoryLockAcquired(DirectoryLock* aLock) |
1259 | 0 | { |
1260 | 0 | AssertIsOnBackgroundThread(); |
1261 | 0 | MOZ_ASSERT(mState == State_DirectoryOpenPending); |
1262 | 0 | MOZ_ASSERT(!mDirectoryLock); |
1263 | 0 |
|
1264 | 0 | mDirectoryLock = aLock; |
1265 | 0 |
|
1266 | 0 | QuotaManager* quotaManager = QuotaManager::Get(); |
1267 | 0 | MOZ_ASSERT(quotaManager); |
1268 | 0 |
|
1269 | 0 | // Must set this before dispatching otherwise we will race with the IO thread |
1270 | 0 | mState = State_DatabaseWorkOpen; |
1271 | 0 |
|
1272 | 0 | nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); |
1273 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1274 | 0 | Finish(); |
1275 | 0 | return; |
1276 | 0 | } |
1277 | 0 | } |
1278 | | |
1279 | | void |
1280 | | DeleteFilesRunnable::DirectoryLockFailed() |
1281 | 0 | { |
1282 | 0 | AssertIsOnBackgroundThread(); |
1283 | 0 | MOZ_ASSERT(mState == State_DirectoryOpenPending); |
1284 | 0 | MOZ_ASSERT(!mDirectoryLock); |
1285 | 0 |
|
1286 | 0 | Finish(); |
1287 | 0 | } |
1288 | | |
1289 | | nsresult |
1290 | | DeleteFilesRunnable::Open() |
1291 | 0 | { |
1292 | 0 | AssertIsOnBackgroundThread(); |
1293 | 0 | MOZ_ASSERT(mState == State_Initial); |
1294 | 0 |
|
1295 | 0 | QuotaManager* quotaManager = QuotaManager::Get(); |
1296 | 0 | if (NS_WARN_IF(!quotaManager)) { |
1297 | 0 | return NS_ERROR_FAILURE; |
1298 | 0 | } |
1299 | 0 | |
1300 | 0 | mState = State_DirectoryOpenPending; |
1301 | 0 |
|
1302 | 0 | quotaManager->OpenDirectory(mFileManager->Type(), |
1303 | 0 | mFileManager->Group(), |
1304 | 0 | mFileManager->Origin(), |
1305 | 0 | quota::Client::IDB, |
1306 | 0 | /* aExclusive */ false, |
1307 | 0 | this); |
1308 | 0 |
|
1309 | 0 | return NS_OK; |
1310 | 0 | } |
1311 | | |
1312 | | nsresult |
1313 | | DeleteFilesRunnable::DeleteFile(int64_t aFileId) |
1314 | 0 | { |
1315 | 0 | MOZ_ASSERT(mDirectory); |
1316 | 0 | MOZ_ASSERT(mJournalDirectory); |
1317 | 0 |
|
1318 | 0 | nsCOMPtr<nsIFile> file = mFileManager->GetFileForId(mDirectory, aFileId); |
1319 | 0 | NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); |
1320 | 0 |
|
1321 | 0 | nsresult rv; |
1322 | 0 | int64_t fileSize; |
1323 | 0 |
|
1324 | 0 | if (mFileManager->EnforcingQuota()) { |
1325 | 0 | rv = file->GetFileSize(&fileSize); |
1326 | 0 | NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); |
1327 | 0 | } |
1328 | 0 |
|
1329 | 0 | rv = file->Remove(false); |
1330 | 0 | NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); |
1331 | 0 |
|
1332 | 0 | if (mFileManager->EnforcingQuota()) { |
1333 | 0 | QuotaManager* quotaManager = QuotaManager::Get(); |
1334 | 0 | NS_ASSERTION(quotaManager, "Shouldn't be null!"); |
1335 | 0 |
|
1336 | 0 | quotaManager->DecreaseUsageForOrigin(mFileManager->Type(), |
1337 | 0 | mFileManager->Group(), |
1338 | 0 | mFileManager->Origin(), fileSize); |
1339 | 0 | } |
1340 | 0 |
|
1341 | 0 | file = mFileManager->GetFileForId(mJournalDirectory, aFileId); |
1342 | 0 | NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); |
1343 | 0 |
|
1344 | 0 | rv = file->Remove(false); |
1345 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1346 | 0 |
|
1347 | 0 | return NS_OK; |
1348 | 0 | } |
1349 | | |
1350 | | nsresult |
1351 | | DeleteFilesRunnable::DoDatabaseWork() |
1352 | 0 | { |
1353 | 0 | AssertIsOnIOThread(); |
1354 | 0 | MOZ_ASSERT(mState == State_DatabaseWorkOpen); |
1355 | 0 |
|
1356 | 0 | if (!mFileManager->Invalidated()) { |
1357 | 0 | mDirectory = mFileManager->GetDirectory(); |
1358 | 0 | if (NS_WARN_IF(!mDirectory)) { |
1359 | 0 | return NS_ERROR_FAILURE; |
1360 | 0 | } |
1361 | 0 | |
1362 | 0 | mJournalDirectory = mFileManager->GetJournalDirectory(); |
1363 | 0 | if (NS_WARN_IF(!mJournalDirectory)) { |
1364 | 0 | return NS_ERROR_FAILURE; |
1365 | 0 | } |
1366 | 0 | |
1367 | 0 | for (int64_t fileId : mFileIds) { |
1368 | 0 | if (NS_FAILED(DeleteFile(fileId))) { |
1369 | 0 | NS_WARNING("Failed to delete file!"); |
1370 | 0 | } |
1371 | 0 | } |
1372 | 0 | } |
1373 | 0 |
|
1374 | 0 | Finish(); |
1375 | 0 |
|
1376 | 0 | return NS_OK; |
1377 | 0 | } |
1378 | | |
1379 | | void |
1380 | | DeleteFilesRunnable::Finish() |
1381 | 0 | { |
1382 | 0 | // Must set mState before dispatching otherwise we will race with the main |
1383 | 0 | // thread. |
1384 | 0 | mState = State_UnblockingOpen; |
1385 | 0 |
|
1386 | 0 | MOZ_ALWAYS_SUCCEEDS(mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL)); |
1387 | 0 | } |
1388 | | |
1389 | | void |
1390 | | DeleteFilesRunnable::UnblockOpen() |
1391 | 0 | { |
1392 | 0 | AssertIsOnBackgroundThread(); |
1393 | 0 | MOZ_ASSERT(mState == State_UnblockingOpen); |
1394 | 0 |
|
1395 | 0 | mDirectoryLock = nullptr; |
1396 | 0 |
|
1397 | 0 | mState = State_Completed; |
1398 | 0 | } |
1399 | | |
1400 | | } // namespace dom |
1401 | | } // namespace mozilla |