bug 700693 - OCSP stapling PSM changes r=bsmith
[gecko.git] / dom / indexedDB / IndexedDatabaseManager.cpp
blob6faf1e0d88453ae948af707b87204138acb1298d
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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "IndexedDatabaseManager.h"
9 #include "nsIConsoleService.h"
10 #include "nsIDiskSpaceWatcher.h"
11 #include "nsIDOMScriptObjectFactory.h"
12 #include "nsIFile.h"
13 #include "nsIFileStorage.h"
14 #include "nsIObserverService.h"
15 #include "nsIScriptError.h"
17 #include "mozilla/ClearOnShutdown.h"
18 #include "mozilla/CondVar.h"
19 #include "mozilla/dom/quota/QuotaManager.h"
20 #include "mozilla/dom/quota/Utilities.h"
21 #include "mozilla/dom/TabContext.h"
22 #include "mozilla/Services.h"
23 #include "mozilla/storage.h"
24 #include "nsContentUtils.h"
25 #include "nsEventDispatcher.h"
26 #include "nsThreadUtils.h"
28 #include "IDBEvents.h"
29 #include "IDBFactory.h"
30 #include "IDBKeyRange.h"
31 #include "IDBRequest.h"
33 // The two possible values for the data argument when receiving the disk space
34 // observer notification.
35 #define LOW_DISK_SPACE_DATA_FULL "full"
36 #define LOW_DISK_SPACE_DATA_FREE "free"
38 USING_INDEXEDDB_NAMESPACE
39 using namespace mozilla::dom;
40 USING_QUOTA_NAMESPACE
42 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
44 namespace {
46 mozilla::StaticRefPtr<IndexedDatabaseManager> gInstance;
48 int32_t gInitialized = 0;
49 int32_t gClosed = 0;
51 class AsyncDeleteFileRunnable MOZ_FINAL : public nsIRunnable
53 public:
54 NS_DECL_ISUPPORTS
55 NS_DECL_NSIRUNNABLE
57 AsyncDeleteFileRunnable(FileManager* aFileManager, int64_t aFileId);
59 private:
60 nsRefPtr<FileManager> mFileManager;
61 int64_t mFileId;
64 class GetFileReferencesHelper MOZ_FINAL : public nsIRunnable
66 public:
67 NS_DECL_ISUPPORTS
68 NS_DECL_NSIRUNNABLE
70 GetFileReferencesHelper(const nsACString& aOrigin,
71 const nsAString& aDatabaseName,
72 int64_t aFileId)
73 : mOrigin(aOrigin), mDatabaseName(aDatabaseName), mFileId(aFileId),
74 mMutex(IndexedDatabaseManager::FileMutex()),
75 mCondVar(mMutex, "GetFileReferencesHelper::mCondVar"),
76 mMemRefCnt(-1),
77 mDBRefCnt(-1),
78 mSliceRefCnt(-1),
79 mResult(false),
80 mWaiting(true)
81 { }
83 nsresult
84 DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
85 int32_t* aDBRefCnt,
86 int32_t* aSliceRefCnt,
87 bool* aResult);
89 private:
90 nsCString mOrigin;
91 nsString mDatabaseName;
92 int64_t mFileId;
94 mozilla::Mutex& mMutex;
95 mozilla::CondVar mCondVar;
96 int32_t mMemRefCnt;
97 int32_t mDBRefCnt;
98 int32_t mSliceRefCnt;
99 bool mResult;
100 bool mWaiting;
103 PLDHashOperator
104 InvalidateAndRemoveFileManagers(
105 const nsACString& aKey,
106 nsAutoPtr<nsTArray<nsRefPtr<FileManager> > >& aValue,
107 void* aUserArg)
109 AssertIsOnIOThread();
110 NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
111 NS_ASSERTION(aValue, "Null pointer!");
113 const nsACString* pattern =
114 static_cast<const nsACString*>(aUserArg);
116 if (!pattern || PatternMatchesOrigin(*pattern, aKey)) {
117 for (uint32_t i = 0; i < aValue->Length(); i++) {
118 nsRefPtr<FileManager>& fileManager = aValue->ElementAt(i);
119 fileManager->Invalidate();
121 return PL_DHASH_REMOVE;
124 return PL_DHASH_NEXT;
127 } // anonymous namespace
129 IndexedDatabaseManager::IndexedDatabaseManager()
130 : mFileMutex("IndexedDatabaseManager.mFileMutex")
132 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
134 mFileManagers.Init();
137 IndexedDatabaseManager::~IndexedDatabaseManager()
139 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
142 bool IndexedDatabaseManager::sIsMainProcess = false;
143 int32_t IndexedDatabaseManager::sLowDiskSpaceMode = 0;
145 // static
146 IndexedDatabaseManager*
147 IndexedDatabaseManager::GetOrCreate()
149 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
151 if (IsClosed()) {
152 NS_ERROR("Calling GetOrCreate() after shutdown!");
153 return nullptr;
156 if (!gInstance) {
157 sIsMainProcess = XRE_GetProcessType() == GeckoProcessType_Default;
159 if (sIsMainProcess) {
160 // See if we're starting up in low disk space conditions.
161 nsCOMPtr<nsIDiskSpaceWatcher> watcher =
162 do_GetService(DISKSPACEWATCHER_CONTRACTID);
163 if (watcher) {
164 bool isDiskFull;
165 if (NS_SUCCEEDED(watcher->GetIsDiskFull(&isDiskFull))) {
166 sLowDiskSpaceMode = isDiskFull ? 1 : 0;
168 else {
169 NS_WARNING("GetIsDiskFull failed!");
172 else {
173 NS_WARNING("No disk space watcher component available!");
177 nsRefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager());
179 nsresult rv = instance->Init();
180 NS_ENSURE_SUCCESS(rv, nullptr);
182 if (PR_ATOMIC_SET(&gInitialized, 1)) {
183 NS_ERROR("Initialized more than once?!");
186 gInstance = instance;
188 ClearOnShutdown(&gInstance);
191 return gInstance;
194 // static
195 IndexedDatabaseManager*
196 IndexedDatabaseManager::Get()
198 // Does not return an owning reference.
199 return gInstance;
202 // static
203 IndexedDatabaseManager*
204 IndexedDatabaseManager::FactoryCreate()
206 // Returns a raw pointer that carries an owning reference! Lame, but the
207 // singleton factory macros force this.
208 IndexedDatabaseManager* mgr = GetOrCreate();
209 NS_IF_ADDREF(mgr);
210 return mgr;
213 nsresult
214 IndexedDatabaseManager::Init()
216 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
218 // Make sure that the quota manager is up.
219 QuotaManager* qm = QuotaManager::GetOrCreate();
220 NS_ENSURE_STATE(qm);
222 // During Init() we can't yet call IsMainProcess(), just check sIsMainProcess
223 // directly.
224 if (sIsMainProcess) {
225 // Must initialize the storage service on the main thread.
226 nsCOMPtr<mozIStorageService> ss =
227 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
228 NS_ENSURE_STATE(ss);
230 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
231 NS_ENSURE_STATE(obs);
233 nsresult rv =
234 obs->AddObserver(this, DISKSPACEWATCHER_OBSERVER_TOPIC, false);
235 NS_ENSURE_SUCCESS(rv, rv);
238 return NS_OK;
241 void
242 IndexedDatabaseManager::Destroy()
244 // Setting the closed flag prevents the service from being recreated.
245 // Don't set it though if there's no real instance created.
246 if (!!gInitialized && PR_ATOMIC_SET(&gClosed, 1)) {
247 NS_ERROR("Shutdown more than once?!");
250 delete this;
253 // static
254 nsresult
255 IndexedDatabaseManager::FireWindowOnError(nsPIDOMWindow* aOwner,
256 nsEventChainPostVisitor& aVisitor)
258 NS_ENSURE_TRUE(aVisitor.mDOMEvent, NS_ERROR_UNEXPECTED);
259 if (!aOwner) {
260 return NS_OK;
263 if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
264 return NS_OK;
267 nsString type;
268 nsresult rv = aVisitor.mDOMEvent->GetType(type);
269 NS_ENSURE_SUCCESS(rv, rv);
271 if (!type.EqualsLiteral(ERROR_EVT_STR)) {
272 return NS_OK;
275 nsCOMPtr<EventTarget> eventTarget =
276 aVisitor.mDOMEvent->InternalDOMEvent()->GetTarget();
278 nsCOMPtr<nsIIDBRequest> strongRequest = do_QueryInterface(eventTarget);
279 IDBRequest* request = static_cast<IDBRequest*>(strongRequest.get());
280 NS_ENSURE_TRUE(request, NS_ERROR_UNEXPECTED);
282 ErrorResult ret;
283 nsRefPtr<DOMError> error = request->GetError(ret);
284 if (ret.Failed()) {
285 return ret.ErrorCode();
288 nsString errorName;
289 if (error) {
290 error->GetName(errorName);
293 nsScriptErrorEvent event(true, NS_LOAD_ERROR);
294 request->FillScriptErrorEvent(&event);
295 NS_ABORT_IF_FALSE(event.fileName,
296 "FillScriptErrorEvent should give us a non-null string "
297 "for our error's fileName");
299 event.errorMsg = errorName.get();
301 nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aOwner));
302 NS_ASSERTION(sgo, "How can this happen?!");
304 nsEventStatus status = nsEventStatus_eIgnore;
305 if (NS_FAILED(sgo->HandleScriptError(&event, &status))) {
306 NS_WARNING("Failed to dispatch script error event");
307 status = nsEventStatus_eIgnore;
310 bool preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault;
311 if (preventDefaultCalled) {
312 return NS_OK;
315 // Log an error to the error console.
316 nsCOMPtr<nsIScriptError> scriptError =
317 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
318 NS_ENSURE_SUCCESS(rv, rv);
320 if (NS_FAILED(scriptError->InitWithWindowID(errorName,
321 nsDependentString(event.fileName),
322 EmptyString(), event.lineNr,
323 0, 0,
324 "IndexedDB",
325 aOwner->WindowID()))) {
326 NS_WARNING("Failed to init script error!");
327 return NS_ERROR_FAILURE;
330 nsCOMPtr<nsIConsoleService> consoleService =
331 do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
332 NS_ENSURE_SUCCESS(rv, rv);
334 return consoleService->LogMessage(scriptError);
337 // static
338 bool
339 IndexedDatabaseManager::TabContextMayAccessOrigin(const TabContext& aContext,
340 const nsACString& aOrigin)
342 NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
344 // If aContext is for a browser element, it's allowed only to access other
345 // browser elements. But if aContext is not for a browser element, it may
346 // access both browser and non-browser elements.
347 nsAutoCString pattern;
348 QuotaManager::GetOriginPatternStringMaybeIgnoreBrowser(
349 aContext.OwnOrContainingAppId(),
350 aContext.IsBrowserElement(),
351 pattern);
353 return PatternMatchesOrigin(pattern, aOrigin);
356 // static
357 bool
358 IndexedDatabaseManager::IsClosed()
360 return !!gClosed;
363 #ifdef DEBUG
364 // static
365 bool
366 IndexedDatabaseManager::IsMainProcess()
368 NS_ASSERTION(gInstance,
369 "IsMainProcess() called before indexedDB has been initialized!");
370 NS_ASSERTION((XRE_GetProcessType() == GeckoProcessType_Default) ==
371 sIsMainProcess, "XRE_GetProcessType changed its tune!");
372 return sIsMainProcess;
375 //static
376 bool
377 IndexedDatabaseManager::InLowDiskSpaceMode()
379 NS_ASSERTION(gInstance,
380 "InLowDiskSpaceMode() called before indexedDB has been "
381 "initialized!");
382 return !!sLowDiskSpaceMode;
384 #endif
386 already_AddRefed<FileManager>
387 IndexedDatabaseManager::GetFileManager(const nsACString& aOrigin,
388 const nsAString& aDatabaseName)
390 AssertIsOnIOThread();
392 nsTArray<nsRefPtr<FileManager> >* array;
393 if (!mFileManagers.Get(aOrigin, &array)) {
394 return nullptr;
397 for (uint32_t i = 0; i < array->Length(); i++) {
398 nsRefPtr<FileManager>& fileManager = array->ElementAt(i);
400 if (fileManager->DatabaseName().Equals(aDatabaseName)) {
401 nsRefPtr<FileManager> result = fileManager;
402 return result.forget();
406 return nullptr;
409 void
410 IndexedDatabaseManager::AddFileManager(FileManager* aFileManager)
412 AssertIsOnIOThread();
413 NS_ASSERTION(aFileManager, "Null file manager!");
415 nsTArray<nsRefPtr<FileManager> >* array;
416 if (!mFileManagers.Get(aFileManager->Origin(), &array)) {
417 array = new nsTArray<nsRefPtr<FileManager> >();
418 mFileManagers.Put(aFileManager->Origin(), array);
421 array->AppendElement(aFileManager);
424 void
425 IndexedDatabaseManager::InvalidateAllFileManagers()
427 AssertIsOnIOThread();
429 mFileManagers.Enumerate(InvalidateAndRemoveFileManagers, nullptr);
432 void
433 IndexedDatabaseManager::InvalidateFileManagersForPattern(
434 const nsACString& aPattern)
436 AssertIsOnIOThread();
437 NS_ASSERTION(!aPattern.IsEmpty(), "Empty pattern!");
439 mFileManagers.Enumerate(InvalidateAndRemoveFileManagers,
440 const_cast<nsACString*>(&aPattern));
443 void
444 IndexedDatabaseManager::InvalidateFileManager(const nsACString& aOrigin,
445 const nsAString& aDatabaseName)
447 AssertIsOnIOThread();
449 nsTArray<nsRefPtr<FileManager> >* array;
450 if (!mFileManagers.Get(aOrigin, &array)) {
451 return;
454 for (uint32_t i = 0; i < array->Length(); i++) {
455 nsRefPtr<FileManager> fileManager = array->ElementAt(i);
456 if (fileManager->DatabaseName().Equals(aDatabaseName)) {
457 fileManager->Invalidate();
458 array->RemoveElementAt(i);
460 if (array->IsEmpty()) {
461 mFileManagers.Remove(aOrigin);
464 break;
469 nsresult
470 IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager,
471 int64_t aFileId)
473 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
475 NS_ENSURE_ARG_POINTER(aFileManager);
477 QuotaManager* quotaManager = QuotaManager::Get();
478 NS_ASSERTION(quotaManager, "Shouldn't be null!");
480 // See if we're currently clearing the storages for this origin. If so then
481 // we pretend that we've already deleted everything.
482 if (quotaManager->IsClearOriginPending(aFileManager->Origin())) {
483 return NS_OK;
486 nsRefPtr<AsyncDeleteFileRunnable> runnable =
487 new AsyncDeleteFileRunnable(aFileManager, aFileId);
489 nsresult rv =
490 quotaManager->IOThread()->Dispatch(runnable, NS_DISPATCH_NORMAL);
491 NS_ENSURE_SUCCESS(rv, rv);
493 return NS_OK;
496 nsresult
497 IndexedDatabaseManager::BlockAndGetFileReferences(
498 const nsACString& aOrigin,
499 const nsAString& aDatabaseName,
500 int64_t aFileId,
501 int32_t* aRefCnt,
502 int32_t* aDBRefCnt,
503 int32_t* aSliceRefCnt,
504 bool* aResult)
506 nsRefPtr<GetFileReferencesHelper> helper =
507 new GetFileReferencesHelper(aOrigin, aDatabaseName, aFileId);
509 nsresult rv = helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt,
510 aSliceRefCnt, aResult);
511 NS_ENSURE_SUCCESS(rv, rv);
513 return NS_OK;
516 NS_IMPL_ADDREF(IndexedDatabaseManager)
517 NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy())
518 NS_IMPL_QUERY_INTERFACE2(IndexedDatabaseManager, nsIIndexedDatabaseManager,
519 nsIObserver)
521 NS_IMETHODIMP
522 IndexedDatabaseManager::InitWindowless(const jsval& aObj, JSContext* aCx)
524 NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
525 NS_ENSURE_ARG(!JSVAL_IS_PRIMITIVE(aObj));
527 JS::Rooted<JSObject*> obj(aCx, JSVAL_TO_OBJECT(aObj));
529 JSBool hasIndexedDB;
530 if (!JS_HasProperty(aCx, obj, "indexedDB", &hasIndexedDB)) {
531 return NS_ERROR_FAILURE;
534 if (hasIndexedDB) {
535 NS_WARNING("Passed object already has an 'indexedDB' property!");
536 return NS_ERROR_FAILURE;
539 // Instantiating this class will register exception providers so even
540 // in xpcshell we will get typed (dom) exceptions, instead of general
541 // exceptions.
542 nsCOMPtr<nsIDOMScriptObjectFactory> sof(do_GetService(kDOMSOF_CID));
544 JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, obj));
545 NS_ASSERTION(global, "What?! No global!");
547 nsRefPtr<IDBFactory> factory;
548 nsresult rv =
549 IDBFactory::Create(aCx, global, nullptr, getter_AddRefs(factory));
550 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
552 NS_ASSERTION(factory, "This should never fail for chrome!");
554 JS::Rooted<JS::Value> indexedDBVal(aCx);
555 rv = nsContentUtils::WrapNative(aCx, obj, factory, indexedDBVal.address());
556 NS_ENSURE_SUCCESS(rv, rv);
558 if (!JS_DefineProperty(aCx, obj, "indexedDB", indexedDBVal, nullptr,
559 nullptr, JSPROP_ENUMERATE)) {
560 return NS_ERROR_FAILURE;
563 JS::Rooted<JSObject*> keyrangeObj(aCx,
564 JS_NewObject(aCx, nullptr, nullptr, nullptr));
565 NS_ENSURE_TRUE(keyrangeObj, NS_ERROR_OUT_OF_MEMORY);
567 if (!IDBKeyRange::DefineConstructors(aCx, keyrangeObj)) {
568 return NS_ERROR_FAILURE;
571 if (!JS_DefineProperty(aCx, obj, "IDBKeyRange", OBJECT_TO_JSVAL(keyrangeObj),
572 nullptr, nullptr, JSPROP_ENUMERATE)) {
573 return NS_ERROR_FAILURE;
576 return NS_OK;
579 NS_IMETHODIMP
580 IndexedDatabaseManager::Observe(nsISupports* aSubject, const char* aTopic,
581 const PRUnichar* aData)
583 NS_ASSERTION(IsMainProcess(), "Wrong process!");
584 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
586 if (!strcmp(aTopic, DISKSPACEWATCHER_OBSERVER_TOPIC)) {
587 NS_ASSERTION(aData, "No data?!");
589 const nsDependentString data(aData);
591 if (data.EqualsLiteral(LOW_DISK_SPACE_DATA_FULL)) {
592 PR_ATOMIC_SET(&sLowDiskSpaceMode, 1);
594 else if (data.EqualsLiteral(LOW_DISK_SPACE_DATA_FREE)) {
595 PR_ATOMIC_SET(&sLowDiskSpaceMode, 0);
597 else {
598 NS_NOTREACHED("Unknown data value!");
601 return NS_OK;
604 NS_NOTREACHED("Unknown topic!");
605 return NS_ERROR_UNEXPECTED;
608 AsyncDeleteFileRunnable::AsyncDeleteFileRunnable(FileManager* aFileManager,
609 int64_t aFileId)
610 : mFileManager(aFileManager), mFileId(aFileId)
614 NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncDeleteFileRunnable,
615 nsIRunnable)
617 NS_IMETHODIMP
618 AsyncDeleteFileRunnable::Run()
620 AssertIsOnIOThread();
622 nsCOMPtr<nsIFile> directory = mFileManager->GetDirectory();
623 NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
625 nsCOMPtr<nsIFile> file = mFileManager->GetFileForId(directory, mFileId);
626 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
628 nsresult rv;
629 int64_t fileSize;
631 if (mFileManager->Privilege() != Chrome) {
632 rv = file->GetFileSize(&fileSize);
633 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
636 rv = file->Remove(false);
637 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
639 if (mFileManager->Privilege() != Chrome) {
640 QuotaManager* quotaManager = QuotaManager::Get();
641 NS_ASSERTION(quotaManager, "Shouldn't be null!");
643 quotaManager->DecreaseUsageForOrigin(mFileManager->Origin(), fileSize);
646 directory = mFileManager->GetJournalDirectory();
647 NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
649 file = mFileManager->GetFileForId(directory, mFileId);
650 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
652 rv = file->Remove(false);
653 NS_ENSURE_SUCCESS(rv, rv);
655 return NS_OK;
658 nsresult
659 GetFileReferencesHelper::DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
660 int32_t* aDBRefCnt,
661 int32_t* aSliceRefCnt,
662 bool* aResult)
664 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
666 QuotaManager* quotaManager = QuotaManager::Get();
667 NS_ASSERTION(quotaManager, "Shouldn't be null!");
669 nsresult rv =
670 quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
671 NS_ENSURE_SUCCESS(rv, rv);
673 mozilla::MutexAutoLock autolock(mMutex);
674 while (mWaiting) {
675 mCondVar.Wait();
678 *aMemRefCnt = mMemRefCnt;
679 *aDBRefCnt = mDBRefCnt;
680 *aSliceRefCnt = mSliceRefCnt;
681 *aResult = mResult;
683 return NS_OK;
686 NS_IMPL_THREADSAFE_ISUPPORTS1(GetFileReferencesHelper,
687 nsIRunnable)
689 NS_IMETHODIMP
690 GetFileReferencesHelper::Run()
692 AssertIsOnIOThread();
694 IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
695 NS_ASSERTION(mgr, "This should never fail!");
697 nsRefPtr<FileManager> fileManager =
698 mgr->GetFileManager(mOrigin, mDatabaseName);
700 if (fileManager) {
701 nsRefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(mFileId);
703 if (fileInfo) {
704 fileInfo->GetReferences(&mMemRefCnt, &mDBRefCnt, &mSliceRefCnt);
706 if (mMemRefCnt != -1) {
707 // We added an extra temp ref, so account for that accordingly.
708 mMemRefCnt--;
711 mResult = true;
715 mozilla::MutexAutoLock lock(mMutex);
716 NS_ASSERTION(mWaiting, "Huh?!");
718 mWaiting = false;
719 mCondVar.Notify();
721 return NS_OK;