--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
#
# Modifying this file will now automatically clobber the buildbot machines \o/
#
# Are you updating CLOBBER because you think it's needed for your WebIDL
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
-Bug 1065897: Updated moz.build requires CLOBBER
+Bug 1069071: IPDL changes require CLOBBER
--- a/content/base/public/nsDOMFile.h
+++ b/content/base/public/nsDOMFile.h
@@ -28,16 +28,17 @@
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/indexedDB/FileInfo.h"
#include "mozilla/dom/indexedDB/FileManager.h"
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
#include "nsWrapperCache.h"
#include "nsCycleCollectionParticipant.h"
+#include "nsWeakReference.h"
class nsDOMMultipartFile;
class nsIFile;
class nsIInputStream;
class nsIClassInfo;
#define PIDOMFILEIMPL_IID \
{ 0x218ee173, 0xf44f, 0x4d30, \
@@ -59,16 +60,17 @@ class FileInfo;
};
class DOMFileImpl;
class DOMFile MOZ_FINAL : public nsIDOMFile
, public nsIXHRSendable
, public nsIMutable
, public nsIJSNativeInitializer
+ , public nsSupportsWeakReference
{
public:
NS_DECL_NSIDOMBLOB
NS_DECL_NSIDOMFILE
NS_DECL_NSIXHRSENDABLE
NS_DECL_NSIMUTABLE
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@@ -185,19 +187,19 @@ public:
virtual nsresult GetSize(uint64_t* aSize) = 0;
virtual nsresult GetType(nsAString& aType) = 0;
virtual nsresult GetMozLastModifiedDate(uint64_t* aDate) = 0;
nsresult Slice(int64_t aStart, int64_t aEnd, const nsAString& aContentType,
- uint8_t aArgc, nsIDOMBlob **aBlob);
+ uint8_t aArgc, DOMFileImpl** aBlobImpl);
- virtual already_AddRefed<nsIDOMBlob>
+ virtual already_AddRefed<DOMFileImpl>
CreateSlice(uint64_t aStart, uint64_t aLength,
const nsAString& aContentType) = 0;
virtual const nsTArray<nsRefPtr<DOMFileImpl>>*
GetSubBlobImpls() const = 0;
virtual nsresult GetInternalStream(nsIInputStream** aStream) = 0;
@@ -316,17 +318,17 @@ public:
virtual nsresult GetMozFullPathInternal(nsAString& aFileName) MOZ_OVERRIDE;
virtual nsresult GetSize(uint64_t* aSize) MOZ_OVERRIDE;
virtual nsresult GetType(nsAString& aType) MOZ_OVERRIDE;
virtual nsresult GetMozLastModifiedDate(uint64_t* aDate) MOZ_OVERRIDE;
- virtual already_AddRefed<nsIDOMBlob>
+ virtual already_AddRefed<DOMFileImpl>
CreateSlice(uint64_t aStart, uint64_t aLength,
const nsAString& aContentType) MOZ_OVERRIDE;
virtual const nsTArray<nsRefPtr<DOMFileImpl>>*
GetSubBlobImpls() const MOZ_OVERRIDE
{
return nullptr;
}
@@ -459,17 +461,17 @@ public:
: DOMFileImplBase(aContentType, aLength)
, mDataOwner(new DataOwner(aMemoryBuffer, aLength))
{
NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
}
virtual nsresult GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
- virtual already_AddRefed<nsIDOMBlob>
+ virtual already_AddRefed<DOMFileImpl>
CreateSlice(uint64_t aStart, uint64_t aLength,
const nsAString& aContentType) MOZ_OVERRIDE;
virtual bool IsMemoryFile() const MOZ_OVERRIDE
{
return true;
}
@@ -546,17 +548,17 @@ public:
, mStartPos(aStartPos)
, mContentType(aContentType)
{
mFileDescOwner = new nsTemporaryFileInputStream::FileDescOwner(aFD);
}
virtual nsresult GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
- virtual already_AddRefed<nsIDOMBlob>
+ virtual already_AddRefed<DOMFileImpl>
CreateSlice(uint64_t aStart, uint64_t aLength,
const nsAString& aContentType) MOZ_OVERRIDE;
private:
DOMFileImplTemporaryFileBlob(const DOMFileImplTemporaryFileBlob* aOther,
uint64_t aStart, uint64_t aLength,
const nsAString& aContentType)
: DOMFileImplBase(aContentType, aLength)
@@ -568,17 +570,17 @@ private:
~DOMFileImplTemporaryFileBlob() {}
uint64_t mLength;
uint64_t mStartPos;
nsRefPtr<nsTemporaryFileInputStream::FileDescOwner> mFileDescOwner;
nsString mContentType;
};
-class DOMFileImplFile MOZ_FINAL : public DOMFileImplBase
+class DOMFileImplFile : public DOMFileImplBase
{
public:
NS_DECL_ISUPPORTS_INHERITED
// Create as a file
explicit DOMFileImplFile(nsIFile* aFile)
: DOMFileImplBase(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
, mFile(aFile)
@@ -685,16 +687,19 @@ public:
virtual nsresult GetLastModifiedDate(JSContext* aCx,
JS::MutableHandle<JS::Value> aLastModifiedDate) MOZ_OVERRIDE;
virtual nsresult GetMozLastModifiedDate(uint64_t* aLastModifiedDate) MOZ_OVERRIDE;
virtual nsresult GetMozFullPathInternal(nsAString& aFullPath) MOZ_OVERRIDE;
virtual nsresult GetInternalStream(nsIInputStream**) MOZ_OVERRIDE;
void SetPath(const nsAString& aFullPath);
+protected:
+ virtual ~DOMFileImplFile() {}
+
private:
// Create slice
DOMFileImplFile(const DOMFileImplFile* aOther, uint64_t aStart,
uint64_t aLength, const nsAString& aContentType)
: DOMFileImplBase(aContentType, aOther->mStart + aStart, aLength)
, mFile(aOther->mFile)
, mWholeFile(false)
, mStoredFile(aOther->mStoredFile)
@@ -714,19 +719,17 @@ private:
mozilla::MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
fileInfo = aOther->GetFileInfo();
}
mFileInfos.AppendElement(fileInfo);
}
}
- ~DOMFileImplFile() {}
-
- virtual already_AddRefed<nsIDOMBlob>
+ virtual already_AddRefed<DOMFileImpl>
CreateSlice(uint64_t aStart, uint64_t aLength,
const nsAString& aContentType) MOZ_OVERRIDE;
virtual bool IsStoredFile() const MOZ_OVERRIDE
{
return mStoredFile;
}
--- a/content/base/src/nsDOMBlobBuilder.cpp
+++ b/content/base/src/nsDOMBlobBuilder.cpp
@@ -50,17 +50,17 @@ DOMMultipartFileImpl::GetInternalStream(
rv = stream->AppendStream(scratchStream);
NS_ENSURE_SUCCESS(rv, rv);
}
return CallQueryInterface(stream, aStream);
}
-already_AddRefed<nsIDOMBlob>
+already_AddRefed<DOMFileImpl>
DOMMultipartFileImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
const nsAString& aContentType)
{
// If we clamped to nothing we create an empty blob
nsTArray<nsRefPtr<DOMFileImpl>> blobImpls;
uint64_t length = aLength;
uint64_t skipStart = aStart;
@@ -72,60 +72,59 @@ DOMMultipartFileImpl::CreateSlice(uint64
uint64_t l;
nsresult rv = blobImpl->GetSize(&l);
NS_ENSURE_SUCCESS(rv, nullptr);
if (skipStart < l) {
uint64_t upperBound = std::min<uint64_t>(l - skipStart, length);
- nsCOMPtr<nsIDOMBlob> firstBlob;
- rv = blobImpl->Slice(skipStart, skipStart + upperBound,
- aContentType, 3,
- getter_AddRefs(firstBlob));
+ nsRefPtr<DOMFileImpl> firstImpl;
+ rv = blobImpl->Slice(skipStart, skipStart + upperBound, aContentType, 3,
+ getter_AddRefs(firstImpl));
NS_ENSURE_SUCCESS(rv, nullptr);
// Avoid wrapping a single blob inside an DOMMultipartFileImpl
if (length == upperBound) {
- return firstBlob.forget();
+ return firstImpl.forget();
}
- blobImpls.AppendElement(static_cast<DOMFile*>(firstBlob.get())->Impl());
+ blobImpls.AppendElement(firstImpl);
length -= upperBound;
i++;
break;
}
skipStart -= l;
}
// Now append enough blobs until we're done
for (; length && i < mBlobImpls.Length(); i++) {
DOMFileImpl* blobImpl = mBlobImpls[i].get();
uint64_t l;
nsresult rv = blobImpl->GetSize(&l);
NS_ENSURE_SUCCESS(rv, nullptr);
if (length < l) {
- nsCOMPtr<nsIDOMBlob> lastBlob;
+ nsRefPtr<DOMFileImpl> lastBlob;
rv = blobImpl->Slice(0, length, aContentType, 3,
getter_AddRefs(lastBlob));
NS_ENSURE_SUCCESS(rv, nullptr);
- blobImpls.AppendElement(static_cast<DOMFile*>(lastBlob.get())->Impl());
+ blobImpls.AppendElement(lastBlob);
} else {
blobImpls.AppendElement(blobImpl);
}
length -= std::min<uint64_t>(l, length);
}
// we can create our blob now
- nsCOMPtr<nsIDOMBlob> blob =
- new DOMFile(new DOMMultipartFileImpl(blobImpls, aContentType));
- return blob.forget();
+ nsRefPtr<DOMFileImpl> impl =
+ new DOMMultipartFileImpl(blobImpls, aContentType);
+ return impl.forget();
}
/* static */ nsresult
DOMMultipartFileImpl::NewFile(const nsAString& aName, nsISupports** aNewObject)
{
nsCOMPtr<nsISupports> file =
do_QueryObject(new DOMFile(new DOMMultipartFileImpl(aName)));
file.forget(aNewObject);
--- a/content/base/src/nsDOMBlobBuilder.h
+++ b/content/base/src/nsDOMBlobBuilder.h
@@ -73,17 +73,17 @@ public:
UnwrapFuncPtr aUnwrapFunc);
nsresult InitFile(JSContext* aCx,
uint32_t aArgc,
JS::Value* aArgv);
nsresult InitChromeFile(JSContext* aCx,
uint32_t aArgc,
JS::Value* aArgv);
- virtual already_AddRefed<nsIDOMBlob>
+ virtual already_AddRefed<DOMFileImpl>
CreateSlice(uint64_t aStart, uint64_t aLength,
const nsAString& aContentType) MOZ_OVERRIDE;
virtual nsresult GetSize(uint64_t* aSize) MOZ_OVERRIDE;
virtual nsresult GetInternalStream(nsIInputStream** aInputStream) MOZ_OVERRIDE;
static nsresult NewFile(const nsAString& aName, nsISupports** aNewObject);
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -142,16 +142,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
MOZ_ASSERT(!aIID.Equals(NS_GET_IID(nsIRemoteBlob)));
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, IsFile())
NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
NS_INTERFACE_MAP_ENTRY(nsIMutable)
NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, IsFile())
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !(IsFile()))
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMFile)
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMFile)
/* static */ already_AddRefed<DOMFile>
@@ -293,17 +294,20 @@ DOMFile::SetLazyData(const nsAString& aN
{
return mImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate);
}
already_AddRefed<nsIDOMBlob>
DOMFile::CreateSlice(uint64_t aStart, uint64_t aLength,
const nsAString& aContentType)
{
- return mImpl->CreateSlice(aStart, aLength, aContentType);
+ nsRefPtr<DOMFileImpl> impl =
+ mImpl->CreateSlice(aStart, aLength, aContentType);
+ nsRefPtr<DOMFile> slice = new DOMFile(impl);
+ return slice.forget();
}
NS_IMETHODIMP
DOMFile::Initialize(nsISupports* aOwner, JSContext* aCx, JSObject* aObj,
const JS::CallArgs& aArgs)
{
return mImpl->Initialize(aOwner, aCx, aObj, aArgs);
}
@@ -395,17 +399,27 @@ ParseSize(int64_t aSize, int64_t& aStart
}
NS_IMETHODIMP
DOMFile::Slice(int64_t aStart, int64_t aEnd,
const nsAString& aContentType, uint8_t aArgc,
nsIDOMBlob **aBlob)
{
MOZ_ASSERT(mImpl);
- return mImpl->Slice(aStart, aEnd, aContentType, aArgc, aBlob);
+ nsRefPtr<DOMFileImpl> impl;
+ nsresult rv = mImpl->Slice(aStart, aEnd, aContentType, aArgc,
+ getter_AddRefs(impl));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsRefPtr<DOMFile> blob = new DOMFile(impl);
+ blob.forget(aBlob);
+
+ return NS_OK;
}
NS_IMETHODIMP
DOMFile::GetInternalStream(nsIInputStream** aStream)
{
return mImpl->GetInternalStream(aStream);
}
@@ -455,37 +469,40 @@ DOMFile::IsMemoryFile()
}
////////////////////////////////////////////////////////////////////////////
// mozilla::dom::DOMFileImpl implementation
nsresult
DOMFileImpl::Slice(int64_t aStart, int64_t aEnd,
const nsAString& aContentType, uint8_t aArgc,
- nsIDOMBlob **aBlob)
+ DOMFileImpl** aBlobImpl)
{
- *aBlob = nullptr;
+ *aBlobImpl = nullptr;
// Truncate aStart and aEnd so that we stay within this file.
uint64_t thisLength;
nsresult rv = GetSize(&thisLength);
NS_ENSURE_SUCCESS(rv, rv);
if (aArgc < 2) {
aEnd = (int64_t)thisLength;
}
ParseSize((int64_t)thisLength, aStart, aEnd);
- // Create the new file
- nsCOMPtr<nsIDOMBlob> blob =
+ nsRefPtr<DOMFileImpl> impl =
CreateSlice((uint64_t)aStart, (uint64_t)(aEnd - aStart), aContentType);
- blob.forget(aBlob);
- return *aBlob ? NS_OK : NS_ERROR_UNEXPECTED;
+ if (!impl) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ impl.forget(aBlobImpl);
+ return NS_OK;
}
////////////////////////////////////////////////////////////////////////////
// DOMFileImpl implementation
NS_IMPL_ISUPPORTS(DOMFileImpl, PIDOMFileImpl)
////////////////////////////////////////////////////////////////////////////
@@ -570,17 +587,17 @@ DOMFileImplBase::GetMozLastModifiedDate(
NS_ASSERTION(mIsFile, "Should only be called on files");
if (IsDateUnknown()) {
mLastModificationDate = PR_Now();
}
*aLastModifiedDate = mLastModificationDate;
return NS_OK;
}
-already_AddRefed<nsIDOMBlob>
+already_AddRefed<DOMFileImpl>
DOMFileImplBase::CreateSlice(uint64_t aStart, uint64_t aLength,
const nsAString& aContentType)
{
return nullptr;
}
nsresult
DOMFileImplBase::GetInternalStream(nsIInputStream** aStream)
@@ -719,23 +736,23 @@ DOMFileImplBase::SetMutable(bool aMutabl
mImmutable = !aMutable;
return rv;
}
////////////////////////////////////////////////////////////////////////////
// DOMFileImplFile implementation
-already_AddRefed<nsIDOMBlob>
+already_AddRefed<DOMFileImpl>
DOMFileImplFile::CreateSlice(uint64_t aStart, uint64_t aLength,
const nsAString& aContentType)
{
- nsCOMPtr<nsIDOMBlob> blob =
- new DOMFile(new DOMFileImplFile(this, aStart, aLength, aContentType));
- return blob.forget();
+ nsRefPtr<DOMFileImpl> impl =
+ new DOMFileImplFile(this, aStart, aLength, aContentType);
+ return impl.forget();
}
nsresult
DOMFileImplFile::GetMozFullPathInternal(nsAString& aFilename)
{
NS_ASSERTION(mIsFile, "Should only be called on files");
return mFile->GetPath(aFilename);
}
@@ -827,17 +844,18 @@ DOMFileImplFile::GetMozLastModifiedDate(
}
*aLastModifiedDate = mLastModificationDate;
return NS_OK;
}
const uint32_t sFileStreamFlags =
nsIFileInputStream::CLOSE_ON_EOF |
nsIFileInputStream::REOPEN_ON_REWIND |
- nsIFileInputStream::DEFER_OPEN;
+ nsIFileInputStream::DEFER_OPEN |
+ nsIFileInputStream::SHARE_DELETE;
nsresult
DOMFileImplFile::GetInternalStream(nsIInputStream** aStream)
{
return mWholeFile ?
NS_NewLocalFileInputStream(aStream, mFile, -1, -1, sFileStreamFlags) :
NS_NewPartialLocalFileInputStream(aStream, mFile, mStart, mLength,
-1, -1, sFileStreamFlags);
@@ -852,23 +870,23 @@ DOMFileImplFile::SetPath(const nsAString
mPath = aPath;
}
////////////////////////////////////////////////////////////////////////////
// DOMFileImplMemory implementation
NS_IMPL_ISUPPORTS_INHERITED0(DOMFileImplMemory, DOMFileImpl)
-already_AddRefed<nsIDOMBlob>
+already_AddRefed<DOMFileImpl>
DOMFileImplMemory::CreateSlice(uint64_t aStart, uint64_t aLength,
const nsAString& aContentType)
{
- nsCOMPtr<nsIDOMBlob> blob =
- new DOMFile(new DOMFileImplMemory(this, aStart, aLength, aContentType));
- return blob.forget();
+ nsRefPtr<DOMFileImpl> impl =
+ new DOMFileImplMemory(this, aStart, aLength, aContentType);
+ return impl.forget();
}
nsresult
DOMFileImplMemory::GetInternalStream(nsIInputStream** aStream)
{
if (mLength > INT32_MAX)
return NS_ERROR_FAILURE;
@@ -977,27 +995,27 @@ DOMFileImplMemory::DataOwner::EnsureMemo
sMemoryReporterRegistered = true;
}
////////////////////////////////////////////////////////////////////////////
// DOMFileImplTemporaryFileBlob implementation
NS_IMPL_ISUPPORTS_INHERITED0(DOMFileImplTemporaryFileBlob, DOMFileImpl)
-already_AddRefed<nsIDOMBlob>
+already_AddRefed<DOMFileImpl>
DOMFileImplTemporaryFileBlob::CreateSlice(uint64_t aStart, uint64_t aLength,
const nsAString& aContentType)
{
if (aStart + aLength > mLength)
return nullptr;
- nsCOMPtr<nsIDOMBlob> blob =
- new DOMFile(new DOMFileImplTemporaryFileBlob(this, aStart + mStartPos,
- aLength, aContentType));
- return blob.forget();
+ nsRefPtr<DOMFileImpl> impl =
+ new DOMFileImplTemporaryFileBlob(this, aStart + mStartPos, aLength,
+ aContentType);
+ return impl.forget();
}
nsresult
DOMFileImplTemporaryFileBlob::GetInternalStream(nsIInputStream** aStream)
{
nsCOMPtr<nsIInputStream> stream =
new nsTemporaryFileInputStream(mFileDescOwner, mStartPos, mStartPos + mLength);
stream.forget(aStream);
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -149,17 +149,16 @@
#include "nsObjectLoadingContent.h"
#include "nsHtml5TreeOpExecutor.h"
#include "mozilla/dom/HTMLLinkElement.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozAutoDocUpdate.h"
#include "nsGlobalWindow.h"
#include "mozilla/dom/EncodingUtils.h"
-#include "mozilla/dom/quota/QuotaManager.h"
#include "nsDOMNavigationTiming.h"
#include "nsSMILAnimationController.h"
#include "imgIContainer.h"
#include "nsSVGUtils.h"
#include "SVGElementFactory.h"
#include "nsRefreshDriver.h"
@@ -8534,23 +8533,16 @@ nsDocument::CanSavePresentation(nsIReque
printf("document %s has request %s\n",
docSpec.get(), requestName.get());
#endif
return false;
}
}
}
- // Check if we have running offline storage transactions
- quota::QuotaManager* quotaManager =
- win ? quota::QuotaManager::Get() : nullptr;
- if (quotaManager && quotaManager->HasOpenTransactions(win)) {
- return false;
- }
-
#ifdef MOZ_MEDIA_NAVIGATOR
// Check if we have active GetUserMedia use
if (MediaManager::Exists() && win &&
MediaManager::Get()->IsWindowStillActive(win->WindowID())) {
return false;
}
#endif // MOZ_MEDIA_NAVIGATOR
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -29,18 +29,18 @@
#include "nsIDOMClassInfo.h"
#include "nsIDOMFile.h"
#include "xpcpublic.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/nsIContentParent.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/StructuredCloneUtils.h"
-#include "mozilla/dom/PBlobChild.h"
-#include "mozilla/dom/PBlobParent.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
#include "JavaScriptChild.h"
#include "JavaScriptParent.h"
#include "mozilla/dom/DOMStringList.h"
#include "nsPrintfCString.h"
#include "nsXULAppAPI.h"
#include <algorithm>
#ifdef ANDROID
--- a/content/base/test/test_ipc_messagemanager_blob.html
+++ b/content/base/test/test_ipc_messagemanager_blob.html
@@ -35,52 +35,91 @@
"error";
sendAsyncMessage(message.name, response);
});
reader.readAsText(message.json);
});
}
function runTests() {
+ function done() {
+ SpecialPowers.removePermission("browser", document);
+ SimpleTest.finish();
+ }
+
ok("Browser prefs set.");
let iframe = document.createElement("iframe");
SpecialPowers.wrap(iframe).mozbrowser = true;
iframe.id = "iframe";
iframe.src = childFrameURL;
iframe.addEventListener("mozbrowserloadend", function() {
ok(true, "Got iframe load event.");
+ const blobString = "this is a great success!";
+
const messages = [
"hi!",
"",
2,
-.04,
3432987324987239872948732982,
true,
false,
null,
0,
+
+ // Make sure this one is always last.
new Blob(["this ", "is ", "a ", "great ", "success!"],
{"type" : "text\/plain"}),
];
let receivedMessageIndex = 0;
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
mm.addMessageListener("test:ipcClonedMessage", function(message) {
- // We need to wrap to access message.json, and unwrap to do the
- // identity check.
- is(SpecialPowers.unwrap(SpecialPowers.wrap(message).json),
+ let data = message.json;
+
+ if (data instanceof Blob) {
+ is(receivedMessageIndex, messages.length - 1, "Blob is last");
+ is (data.size,
+ messages[receivedMessageIndex].size,
+ "Correct blob size");
+ is (data.type,
+ messages[receivedMessageIndex].type,
+ "Correct blob type");
+
+ let result1, result2;
+
+ let reader1 = new FileReader();
+ reader1.onload = function() {
+ result1 = reader1.result == blobString ? reader1.result : "bad1";
+ if (result2) {
+ is(result1, result2, "Same results");
+ done();
+ }
+ };
+
+ let reader2 = new FileReader();
+ reader2.onload = function() {
+ result2 = reader2.result == blobString ? reader2.result : "bad2";
+ if (result1) {
+ is(result1, result2, "Same results");
+ done();
+ }
+ };
+
+ reader1.readAsText(data);
+ reader2.readAsText(messages[receivedMessageIndex]);
+ return;
+ }
+
+ is(message.json,
messages[receivedMessageIndex++],
"Got correct round-tripped response");
- if (receivedMessageIndex == messages.length) {
- SpecialPowers.removePermission("browser", document);
- SimpleTest.finish();
- }
});
mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
false);
for each (let message in messages) {
mm.sendAsyncMessage("test:ipcClonedMessage", message);
}
});
--- a/dom/archivereader/ArchiveZipFile.cpp
+++ b/dom/archivereader/ArchiveZipFile.cpp
@@ -391,21 +391,20 @@ ArchiveZipFileImpl::Unlink()
void
ArchiveZipFileImpl::Traverse(nsCycleCollectionTraversalCallback &cb)
{
ArchiveZipFileImpl* tmp = this;
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArchiveReader);
}
-already_AddRefed<nsIDOMBlob>
+already_AddRefed<mozilla::dom::DOMFileImpl>
ArchiveZipFileImpl::CreateSlice(uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType)
{
- nsCOMPtr<nsIDOMBlob> t =
- new DOMFile(new ArchiveZipFileImpl(mFilename, mContentType,
- aStart, mLength, mCentral,
- mArchiveReader));
- return t.forget();
+ nsRefPtr<DOMFileImpl> impl =
+ new ArchiveZipFileImpl(mFilename, mContentType, aStart, mLength, mCentral,
+ mArchiveReader);
+ return impl.forget();
}
NS_IMPL_ISUPPORTS_INHERITED0(ArchiveZipFileImpl, DOMFileImpl)
--- a/dom/archivereader/ArchiveZipFile.h
+++ b/dom/archivereader/ArchiveZipFile.h
@@ -66,19 +66,19 @@ public:
}
protected:
virtual ~ArchiveZipFileImpl()
{
MOZ_COUNT_DTOR(ArchiveZipFileImpl);
}
- virtual already_AddRefed<nsIDOMBlob> CreateSlice(uint64_t aStart,
- uint64_t aLength,
- const nsAString& aContentType) MOZ_OVERRIDE;
+ virtual already_AddRefed<DOMFileImpl> CreateSlice(uint64_t aStart,
+ uint64_t aLength,
+ const nsAString& aContentType) MOZ_OVERRIDE;
private: // Data
ZipCentral mCentral;
nsRefPtr<ArchiveReader> mArchiveReader;
nsString mFilename;
};
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -95,16 +95,17 @@
#include "nsContentPermissionHelper.h"
#ifdef XP_WIN
#undef GetClassName
#endif
using namespace mozilla;
using namespace mozilla::dom;
+using namespace mozilla::ipc;
using namespace mozilla::layers;
using namespace mozilla::widget;
using namespace mozilla::gfx;
class gfxContext;
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
@@ -2999,40 +3000,75 @@ nsDOMWindowUtils::AreDialogsEnabled(bool
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
*aResult = static_cast<nsGlobalWindow*>(window.get())->AreDialogsEnabled();
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetFileId(JS::Handle<JS::Value> aFile, JSContext* aCx,
- int64_t* aResult)
+ int64_t* _retval)
{
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
- if (!aFile.isPrimitive()) {
- JSObject* obj = aFile.toObjectOrNull();
-
- indexedDB::IDBMutableFile* mutableFile = nullptr;
- if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, obj, mutableFile))) {
- *aResult = mutableFile->GetFileId();
- return NS_OK;
+ if (aFile.isPrimitive()) {
+ *_retval = -1;
+ return NS_OK;
+ }
+
+ JSObject* obj = aFile.toObjectOrNull();
+
+ indexedDB::IDBMutableFile* mutableFile = nullptr;
+ if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, obj, mutableFile))) {
+ *_retval = mutableFile->GetFileId();
+ return NS_OK;
+ }
+
+ nsISupports* nativeObj =
+ nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj);
+
+ nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(nativeObj);
+ if (blob) {
+ *_retval = blob->GetFileId();
+ return NS_OK;
+ }
+
+ *_retval = -1;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::GetFilePath(JS::HandleValue aFile, JSContext* aCx,
+ nsAString& _retval)
+{
+ MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
+
+ if (aFile.isPrimitive()) {
+ _retval.Truncate();
+ return NS_OK;
+ }
+
+ JSObject* obj = aFile.toObjectOrNull();
+
+ nsISupports* nativeObj =
+ nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj);
+
+ nsCOMPtr<nsIDOMFile> file = do_QueryInterface(nativeObj);
+ if (file) {
+ nsString filePath;
+ nsresult rv = file->GetMozFullPathInternal(filePath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
}
- nsISupports* nativeObj =
- nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj);
-
- nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(nativeObj);
- if (blob) {
- *aResult = blob->GetFileId();
- return NS_OK;
- }
+ _retval = filePath;
+ return NS_OK;
}
- *aResult = -1;
+ _retval.Truncate();
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName, int64_t aId,
JS::Handle<JS::Value> aOptions,
int32_t* aRefCnt, int32_t* aDBRefCnt,
int32_t* aSliceRefCnt, JSContext* aCx,
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -182,17 +182,16 @@
#include "prlog.h"
#include "prenv.h"
#include "prprf.h"
#include "mozilla/dom/MessageChannel.h"
#include "mozilla/dom/MessagePort.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/indexedDB/IDBFactory.h"
-#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/dom/StructuredCloneTags.h"
#ifdef MOZ_GAMEPAD
#include "mozilla/dom/GamepadService.h"
#endif
#include "nsRefreshDriver.h"
@@ -1569,22 +1568,16 @@ nsGlobalWindow::FreeInnerObjects()
// re-create.
NotifyDOMWindowDestroyed(this);
mInnerObjectsFreed = true;
// Kill all of the workers for this window.
mozilla::dom::workers::CancelWorkersForWindow(this);
- // Close all offline storages for this window.
- quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
- if (quotaManager) {
- quotaManager->AbortCloseStoragesForWindow(this);
- }
-
ClearAllTimeouts();
if (mIdleTimer) {
mIdleTimer->Cancel();
mIdleTimer = nullptr;
}
mIdleObservers.Clear();
@@ -10588,19 +10581,21 @@ GetIndexedDBEnabledForAboutURI(nsIURI *a
uint32_t flags;
rv = module->GetURIFlags(aURI, &flags);
NS_ENSURE_SUCCESS(rv, false);
return flags & nsIAboutModule::ENABLE_INDEXED_DB;
}
-indexedDB::IDBFactory*
+mozilla::dom::indexedDB::IDBFactory*
nsGlobalWindow::GetIndexedDB(ErrorResult& aError)
{
+ using mozilla::dom::indexedDB::IDBFactory;
+
if (!mIndexedDB) {
// If the document has the sandboxed origin flag set
// don't allow access to indexedDB.
if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
@@ -10637,18 +10632,17 @@ nsGlobalWindow::GetIndexedDB(ErrorResult
NS_WARN_IF_FALSE(aError.Failed(),
"IndexedDB is not permitted in a third-party window.");
return nullptr;
}
}
}
// This may be null if being created from a file.
- aError = indexedDB::IDBFactory::Create(this, nullptr,
- getter_AddRefs(mIndexedDB));
+ aError = IDBFactory::CreateForWindow(this, getter_AddRefs(mIndexedDB));
}
return mIndexedDB;
}
NS_IMETHODIMP
nsGlobalWindow::GetIndexedDB(nsISupports** _retval)
{
--- a/dom/bluetooth/BluetoothService.cpp
+++ b/dom/bluetooth/BluetoothService.cpp
@@ -21,16 +21,18 @@
#include "jsapi.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/unused.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/ipc/UnixSocket.h"
#include "nsContentUtils.h"
#include "nsIObserverService.h"
#include "nsISettingsService.h"
#include "nsISystemMessagesInternal.h"
#include "nsITimer.h"
#include "nsServiceManagerUtils.h"
#include "nsXPCOM.h"
--- a/dom/bluetooth/BluetoothService.h
+++ b/dom/bluetooth/BluetoothService.h
@@ -4,25 +4,30 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_bluetooth_bluetootheventservice_h__
#define mozilla_dom_bluetooth_bluetootheventservice_h__
#include "BluetoothCommon.h"
#include "BluetoothProfileManagerBase.h"
-#include "mozilla/dom/ipc/Blob.h"
#include "nsAutoPtr.h"
#include "nsClassHashtable.h"
#include "nsIDOMFile.h"
#include "nsIObserver.h"
#include "nsTObserverArray.h"
#include "nsThreadUtils.h"
+class nsIDOMBlob;
+
namespace mozilla {
+namespace dom {
+class BlobChild;
+class BlobParent;
+}
namespace ipc {
class UnixSocketConsumer;
}
}
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothManager;
--- a/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
@@ -9,16 +9,17 @@
#include "BluetoothService.h"
#include "BluetoothSocket.h"
#include "BluetoothUtils.h"
#include "BluetoothUuid.h"
#include "ObexBase.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsAutoPtr.h"
#include "nsCExternalHandlerService.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIDOMFile.h"
--- a/dom/bluetooth/bluedroid/BluetoothOppManager.h
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.h
@@ -6,24 +6,30 @@
#ifndef mozilla_dom_bluetooth_bluetoothoppmanager_h__
#define mozilla_dom_bluetooth_bluetoothoppmanager_h__
#include "BluetoothCommon.h"
#include "BluetoothProfileManagerBase.h"
#include "BluetoothSocketObserver.h"
#include "DeviceStorage.h"
-#include "mozilla/dom/ipc/Blob.h"
#include "mozilla/ipc/UnixSocket.h"
#include "nsCOMArray.h"
+class nsIDOMBlob;
class nsIOutputStream;
class nsIInputStream;
class nsIVolumeMountLock;
+namespace mozilla {
+namespace dom {
+class BlobParent;
+}
+}
+
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothSocket;
class ObexHeaderSet;
class SendFileBatch;
class BluetoothOppManager : public BluetoothSocketObserver
, public BluetoothProfileManagerBase
--- a/dom/bluetooth/bluez/BluetoothDBusService.cpp
+++ b/dom/bluetooth/bluez/BluetoothDBusService.cpp
@@ -39,16 +39,17 @@
#include "mozilla/Atomics.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/Hal.h"
#include "mozilla/ipc/UnixSocket.h"
#include "mozilla/ipc/DBusUtils.h"
#include "mozilla/ipc/RawDBusConnection.h"
#include "mozilla/LazyIdleThread.h"
+#include "mozilla/Monitor.h"
#include "mozilla/Mutex.h"
#include "mozilla/NullPtr.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/unused.h"
#if defined(MOZ_WIDGET_GONK)
#include "cutils/properties.h"
#include <dlfcn.h>
--- a/dom/bluetooth/bluez/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothOppManager.cpp
@@ -9,16 +9,17 @@
#include "BluetoothService.h"
#include "BluetoothSocket.h"
#include "BluetoothUtils.h"
#include "BluetoothUuid.h"
#include "ObexBase.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsAutoPtr.h"
#include "nsCExternalHandlerService.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIDOMFile.h"
--- a/dom/bluetooth/bluez/BluetoothOppManager.h
+++ b/dom/bluetooth/bluez/BluetoothOppManager.h
@@ -6,24 +6,30 @@
#ifndef mozilla_dom_bluetooth_bluetoothoppmanager_h__
#define mozilla_dom_bluetooth_bluetoothoppmanager_h__
#include "BluetoothCommon.h"
#include "BluetoothProfileManagerBase.h"
#include "BluetoothSocketObserver.h"
#include "DeviceStorage.h"
-#include "mozilla/dom/ipc/Blob.h"
#include "mozilla/ipc/UnixSocket.h"
#include "nsCOMArray.h"
+class nsIDOMBlob;
class nsIOutputStream;
class nsIInputStream;
class nsIVolumeMountLock;
+namespace mozilla {
+namespace dom {
+class BlobParent;
+}
+}
+
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothSocket;
class ObexHeaderSet;
class SendFileBatch;
class BluetoothOppManager : public BluetoothSocketObserver
, public BluetoothProfileManagerBase
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
@@ -5,16 +5,17 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "base/basictypes.h"
#include "BluetoothServiceChildProcess.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ipc/BlobChild.h"
#include "BluetoothChild.h"
#include "MainThreadUtils.h"
USING_BLUETOOTH_NAMESPACE
namespace {
--- a/dom/bluetooth2/BluetoothService.cpp
+++ b/dom/bluetooth2/BluetoothService.cpp
@@ -21,16 +21,18 @@
#include "jsapi.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/unused.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
#include "nsContentUtils.h"
#include "nsIObserverService.h"
#include "nsISettingsService.h"
#include "nsISystemMessagesInternal.h"
#include "nsITimer.h"
#include "nsServiceManagerUtils.h"
#include "nsXPCOM.h"
#include "mozilla/dom/SettingChangeNotificationBinding.h"
--- a/dom/bluetooth2/BluetoothService.h
+++ b/dom/bluetooth2/BluetoothService.h
@@ -4,25 +4,30 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_bluetooth_bluetootheventservice_h__
#define mozilla_dom_bluetooth_bluetootheventservice_h__
#include "BluetoothCommon.h"
#include "BluetoothProfileManagerBase.h"
-#include "mozilla/dom/ipc/Blob.h"
#include "nsAutoPtr.h"
#include "nsClassHashtable.h"
#include "nsIDOMFile.h"
#include "nsIObserver.h"
#include "nsTObserverArray.h"
#include "nsThreadUtils.h"
+class nsIDOMBlob;
+
namespace mozilla {
+namespace dom {
+class BlobChild;
+class BlobParent;
+}
namespace ipc {
class UnixSocketConsumer;
}
}
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothManager;
--- a/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp
@@ -9,16 +9,17 @@
#include "BluetoothService.h"
#include "BluetoothSocket.h"
#include "BluetoothUtils.h"
#include "BluetoothUuid.h"
#include "ObexBase.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsAutoPtr.h"
#include "nsCExternalHandlerService.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIDOMFile.h"
--- a/dom/bluetooth2/bluedroid/BluetoothOppManager.h
+++ b/dom/bluetooth2/bluedroid/BluetoothOppManager.h
@@ -6,24 +6,30 @@
#ifndef mozilla_dom_bluetooth_bluetoothoppmanager_h__
#define mozilla_dom_bluetooth_bluetoothoppmanager_h__
#include "BluetoothCommon.h"
#include "BluetoothProfileManagerBase.h"
#include "BluetoothSocketObserver.h"
#include "DeviceStorage.h"
-#include "mozilla/dom/ipc/Blob.h"
#include "mozilla/ipc/UnixSocket.h"
#include "nsCOMArray.h"
+class nsIDOMBlob;
class nsIOutputStream;
class nsIInputStream;
class nsIVolumeMountLock;
+namespace mozilla {
+namespace dom {
+class BlobParent;
+}
+}
+
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothSocket;
class ObexHeaderSet;
class SendFileBatch;
class BluetoothOppManager : public BluetoothSocketObserver
, public BluetoothProfileManagerBase
--- a/dom/bluetooth2/bluez/BluetoothOppManager.cpp
+++ b/dom/bluetooth2/bluez/BluetoothOppManager.cpp
@@ -9,16 +9,17 @@
#include "BluetoothService.h"
#include "BluetoothSocket.h"
#include "BluetoothUtils.h"
#include "BluetoothUuid.h"
#include "ObexBase.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsAutoPtr.h"
#include "nsCExternalHandlerService.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIDOMFile.h"
--- a/dom/bluetooth2/bluez/BluetoothOppManager.h
+++ b/dom/bluetooth2/bluez/BluetoothOppManager.h
@@ -6,24 +6,30 @@
#ifndef mozilla_dom_bluetooth_bluetoothoppmanager_h__
#define mozilla_dom_bluetooth_bluetoothoppmanager_h__
#include "BluetoothCommon.h"
#include "BluetoothProfileManagerBase.h"
#include "BluetoothSocketObserver.h"
#include "DeviceStorage.h"
-#include "mozilla/dom/ipc/Blob.h"
#include "mozilla/ipc/UnixSocket.h"
#include "nsCOMArray.h"
+class nsIDOMBlob;
class nsIOutputStream;
class nsIInputStream;
class nsIVolumeMountLock;
+namespace mozilla {
+namespace dom {
+class BlobParent;
+}
+}
+
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothSocket;
class ObexHeaderSet;
class SendFileBatch;
class BluetoothOppManager : public BluetoothSocketObserver
, public BluetoothProfileManagerBase
--- a/dom/datastore/DataStoreDB.cpp
+++ b/dom/datastore/DataStoreDB.cpp
@@ -2,25 +2,32 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DataStoreDB.h"
#include "DataStoreCallbacks.h"
+#include "jsapi.h"
#include "mozilla/dom/IDBDatabaseBinding.h"
#include "mozilla/dom/IDBFactoryBinding.h"
+#include "mozilla/dom/IDBObjectStoreBinding.h"
#include "mozilla/dom/indexedDB/IDBDatabase.h"
#include "mozilla/dom/indexedDB/IDBEvents.h"
#include "mozilla/dom/indexedDB/IDBFactory.h"
#include "mozilla/dom/indexedDB/IDBIndex.h"
#include "mozilla/dom/indexedDB/IDBObjectStore.h"
#include "mozilla/dom/indexedDB/IDBRequest.h"
+#include "mozilla/dom/indexedDB/IDBTransaction.h"
+#include "nsComponentManagerUtils.h"
+#include "nsContentUtils.h"
#include "nsIDOMEvent.h"
+#include "nsIPrincipal.h"
+#include "nsIXPConnect.h"
#define DATASTOREDB_VERSION 1
#define DATASTOREDB_NAME "DataStoreDB"
#define DATASTOREDB_REVISION_INDEX "revisionIndex"
using namespace mozilla::dom::indexedDB;
namespace mozilla {
@@ -58,17 +65,19 @@ public:
#ifdef DEBUG
nsCOMPtr<IDBVersionChangeEvent> event = do_QueryInterface(aEvent);
MOZ_ASSERT(event);
Nullable<uint64_t> version = event->GetNewVersion();
MOZ_ASSERT(version.IsNull());
#endif
- return mDatabase->Close();
+ mDatabase->Close();
+
+ return NS_OK;
}
private:
IDBDatabase* mDatabase;
~VersionChangeListener() {}
};
@@ -88,17 +97,46 @@ DataStoreDB::DataStoreDB(const nsAString
DataStoreDB::~DataStoreDB()
{
}
nsresult
DataStoreDB::CreateFactoryIfNeeded()
{
if (!mFactory) {
- nsresult rv = IDBFactory::Create(nullptr, getter_AddRefs(mFactory));
+ nsresult rv;
+ nsCOMPtr<nsIPrincipal> principal =
+ do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsIXPConnect* xpc = nsContentUtils::XPConnect();
+ MOZ_ASSERT(xpc);
+
+ AutoSafeJSContext cx;
+
+ nsCOMPtr<nsIXPConnectJSObjectHolder> globalHolder;
+ rv = xpc->CreateSandbox(cx, principal, getter_AddRefs(globalHolder));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ JS::Rooted<JSObject*> global(cx, globalHolder->GetJSObject());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // The CreateSandbox call returns a proxy to the actual sandbox object. We
+ // don't need a proxy here.
+ global = js::UncheckedUnwrap(global);
+
+ JSAutoCompartment ac(cx, global);
+
+ rv = IDBFactory::CreateForDatastore(cx, global, getter_AddRefs(mFactory));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;
}
@@ -296,21 +334,17 @@ DataStoreDB::Delete()
nsresult rv = CreateFactoryIfNeeded();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mTransaction = nullptr;
if (mDatabase) {
- rv = mDatabase->Close();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
-
+ mDatabase->Close();
mDatabase = nullptr;
}
ErrorResult error;
nsRefPtr<IDBOpenDBRequest> request =
mFactory->DeleteDatabase(mDatabaseName, IDBOpenDBOptions(), error);
if (NS_WARN_IF(error.Failed())) {
return error.ErrorCode();
--- a/dom/datastore/DataStoreRevision.cpp
+++ b/dom/datastore/DataStoreRevision.cpp
@@ -4,18 +4,19 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DataStoreRevision.h"
#include "DataStoreCallbacks.h"
#include "DataStoreService.h"
#include "mozilla/dom/DataStoreBinding.h"
+#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/indexedDB/IDBObjectStore.h"
-#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/indexedDB/IDBRequest.h"
#include "nsIDOMEvent.h"
namespace mozilla {
namespace dom {
using namespace indexedDB;
NS_IMPL_ISUPPORTS(DataStoreRevision, nsIDOMEventListener)
--- a/dom/datastore/DataStoreService.cpp
+++ b/dom/datastore/DataStoreService.cpp
@@ -17,16 +17,18 @@
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/indexedDB/IDBCursor.h"
#include "mozilla/dom/indexedDB/IDBObjectStore.h"
+#include "mozilla/dom/indexedDB/IDBRequest.h"
+#include "mozilla/dom/indexedDB/IDBTransaction.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/unused.h"
#include "mozIApplication.h"
#include "mozIApplicationClearPrivateDataParams.h"
#include "nsIAppsService.h"
#include "nsIDOMEvent.h"
--- a/dom/datastore/tests/test_oop_events.html
+++ b/dom/datastore/tests/test_oop_events.html
@@ -1,9 +1,9 @@
-<!DOCTYPE HTML>
+<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test for DataStore - basic operation on a readonly db</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
@@ -132,17 +132,17 @@
function() { installApp(gHostedManifestURL1); },
function() { installApp(gHostedManifestURL2); },
// Run tests in apps
testApp,
// Uninstall the apps
function() { uninstallApp(gApps[0]); },
- function() { uninstallApp(gApps[1]); },
+ function() { uninstallApp(gApps[1]); }
];
function runTest() {
if (!tests.length) {
finish();
return;
}
--- a/dom/devicestorage/DeviceStorageRequestChild.cpp
+++ b/dom/devicestorage/DeviceStorageRequestChild.cpp
@@ -3,17 +3,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DeviceStorageRequestChild.h"
#include "DeviceStorageFileDescriptor.h"
#include "nsDeviceStorage.h"
#include "nsDOMFile.h"
-#include "mozilla/dom/ipc/Blob.h"
+#include "mozilla/dom/ipc/BlobChild.h"
namespace mozilla {
namespace dom {
namespace devicestorage {
DeviceStorageRequestChild::DeviceStorageRequestChild()
: mCallback(nullptr)
{
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -3,17 +3,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DeviceStorageRequestParent.h"
#include "nsDOMFile.h"
#include "nsIMIMEService.h"
#include "nsCExternalHandlerService.h"
#include "mozilla/unused.h"
-#include "mozilla/dom/ipc/Blob.h"
+#include "mozilla/dom/ipc/BlobParent.h"
#include "ContentParent.h"
#include "nsProxyRelease.h"
#include "AppProcessChecker.h"
#include "mozilla/Preferences.h"
#include "nsNetCID.h"
namespace mozilla {
namespace dom {
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -11,17 +11,17 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/DeviceStorageBinding.h"
#include "mozilla/dom/DeviceStorageChangeEvent.h"
#include "mozilla/dom/DeviceStorageFileSystem.h"
#include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/FileSystemUtils.h"
-#include "mozilla/dom/ipc/Blob.h"
+#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/dom/PBrowserChild.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/LazyIdleThread.h"
#include "mozilla/Preferences.h"
--- a/dom/filehandle/FileStreamWrappers.cpp
+++ b/dom/filehandle/FileStreamWrappers.cpp
@@ -3,24 +3,30 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileStreamWrappers.h"
#include "FileHelper.h"
#include "MainThreadUtils.h"
+#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
+#include "mozilla/ipc/InputStreamParams.h"
#include "MutableFile.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsIRunnable.h"
#include "nsISeekableStream.h"
#include "nsThreadUtils.h"
+#ifdef DEBUG
+#include "nsXULAppAPI.h"
+#endif
+
namespace mozilla {
namespace dom {
namespace {
class ProgressRunnable MOZ_FINAL : public nsIRunnable
{
public:
@@ -122,17 +128,18 @@ FileInputStreamWrapper::FileInputStreamW
: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
{
mInputStream = do_QueryInterface(mFileStream);
NS_ASSERTION(mInputStream, "This should always succeed!");
}
NS_IMPL_ISUPPORTS_INHERITED(FileInputStreamWrapper,
FileStreamWrapper,
- nsIInputStream)
+ nsIInputStream,
+ nsIIPCSerializableInputStream)
NS_IMETHODIMP
FileInputStreamWrapper::Close()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
if (mFlags & NOTIFY_CLOSE) {
nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
@@ -225,16 +232,36 @@ FileInputStreamWrapper::ReadSegments(nsW
NS_IMETHODIMP
FileInputStreamWrapper::IsNonBlocking(bool* _retval)
{
*_retval = false;
return NS_OK;
}
+void
+FileInputStreamWrapper::Serialize(InputStreamParams& aParams,
+ FileDescriptorArray& /* aFDs */)
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIInputStream> thisStream = do_QueryObject(this);
+
+ aParams = mozilla::ipc::SameProcessInputStreamParams(
+ reinterpret_cast<intptr_t>(thisStream.forget().take()));
+}
+
+bool
+FileInputStreamWrapper::Deserialize(const InputStreamParams& /* aParams */,
+ const FileDescriptorArray& /* aFDs */)
+{
+ MOZ_CRASH("Should never get here!");
+}
+
FileOutputStreamWrapper::FileOutputStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
uint64_t aOffset,
uint64_t aLimit,
uint32_t aFlags)
: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
#ifdef DEBUG
, mWriteThread(nullptr)
--- a/dom/filehandle/FileStreamWrappers.h
+++ b/dom/filehandle/FileStreamWrappers.h
@@ -6,18 +6,23 @@
#ifndef mozilla_dom_FileStreamWrappers_h
#define mozilla_dom_FileStreamWrappers_h
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
+#include "nsIIPCSerializableInputStream.h"
namespace mozilla {
+namespace ipc {
+class InputStreamParams;
+} // namespace ipc
+
namespace dom {
class FileHelper;
class FileStreamWrapper : public nsISupports
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
@@ -41,21 +46,25 @@ protected:
nsRefPtr<FileHelper> mFileHelper;
uint64_t mOffset;
uint64_t mLimit;
uint32_t mFlags;
bool mFirstTime;
};
class FileInputStreamWrapper : public FileStreamWrapper,
- public nsIInputStream
+ public nsIInputStream,
+ public nsIIPCSerializableInputStream
{
+ typedef mozilla::ipc::InputStreamParams InputStreamParams;
+
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
FileInputStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
uint64_t aOffset,
uint64_t aLimit,
uint32_t aFlags);
protected:
--- a/dom/filehandle/moz.build
+++ b/dom/filehandle/moz.build
@@ -23,13 +23,15 @@ UNIFIED_SOURCES += [
'FileStreamWrappers.cpp',
'MemoryStreams.cpp',
'MetadataHelper.cpp',
'MutableFile.cpp',
]
FAIL_ON_WARNINGS = True
+include('/ipc/chromium/chromium-config.mozbuild')
+
LOCAL_INCLUDES += [
'../base',
]
FINAL_LIBRARY = 'xul'
--- a/dom/filesystem/CreateFileTask.cpp
+++ b/dom/filesystem/CreateFileTask.cpp
@@ -8,16 +8,18 @@
#include <algorithm>
#include "DOMError.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
#include "nsDOMFile.h"
#include "nsIFile.h"
#include "nsNetUtil.h"
#include "nsStringGlue.h"
namespace mozilla {
namespace dom {
--- a/dom/filesystem/FileSystemTaskBase.cpp
+++ b/dom/filesystem/FileSystemTaskBase.cpp
@@ -8,16 +8,17 @@
#include "nsNetUtil.h" // Stream transport service.
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemRequestParent.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PContent.h"
+#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/unused.h"
#include "nsDOMFile.h"
namespace mozilla {
namespace dom {
FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem)
: mErrorValue(NS_OK)
--- a/dom/filesystem/FileSystemTaskBase.h
+++ b/dom/filesystem/FileSystemTaskBase.h
@@ -5,23 +5,23 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_FileSystemTaskBase_h
#define mozilla_dom_FileSystemTaskBase_h
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/FileSystemRequestParent.h"
#include "mozilla/dom/PFileSystemRequestChild.h"
-#include "mozilla/dom/ipc/Blob.h"
class nsIDOMFile;
namespace mozilla {
namespace dom {
+class BlobParent;
class FileSystemBase;
class FileSystemParams;
class Promise;
/*
* The base class to implement a Task class.
* The task is used to handle the OOP (out of process) operations.
* The file system operations can only be performed in the parent process. When
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -6,16 +6,18 @@
#include "GetFileOrDirectoryTask.h"
#include "js/Value.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
#include "nsDOMFile.h"
#include "nsIFile.h"
#include "nsStringGlue.h"
namespace mozilla {
namespace dom {
GetFileOrDirectoryTask::GetFileOrDirectoryTask(
--- a/dom/filesystem/RemoveTask.cpp
+++ b/dom/filesystem/RemoveTask.cpp
@@ -5,16 +5,18 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "RemoveTask.h"
#include "DOMError.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
#include "nsDOMFile.h"
#include "nsIFile.h"
#include "nsStringGlue.h"
namespace mozilla {
namespace dom {
RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -0,0 +1,2352 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ActorsChild.h"
+
+#include "BackgroundChildImpl.h"
+#include "FileManager.h"
+#include "IDBDatabase.h"
+#include "IDBEvents.h"
+#include "IDBFactory.h"
+#include "IDBIndex.h"
+#include "IDBObjectStore.h"
+#include "IDBMutableFile.h"
+#include "IDBRequest.h"
+#include "IDBTransaction.h"
+#include "IndexedDatabase.h"
+#include "IndexedDatabaseInlines.h"
+#include "mozilla/BasicEvents.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h"
+#include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "nsCOMPtr.h"
+#include "nsContentUtils.h"
+#include "nsIBFCacheEntry.h"
+#include "nsIDocument.h"
+#include "nsIDOMEvent.h"
+#include "nsIEventTarget.h"
+#include "nsPIDOMWindow.h"
+#include "nsThreadUtils.h"
+#include "nsTraceRefcnt.h"
+#include "PermissionRequestBase.h"
+#include "ProfilerHelpers.h"
+#include "ReportInternalError.h"
+
+#ifdef DEBUG
+#include "IndexedDatabaseManager.h"
+#endif
+
+#define GC_ON_IPC_MESSAGES 0
+
+#if defined(DEBUG) || GC_ON_IPC_MESSAGES
+
+#include "js/GCAPI.h"
+#include "nsJSEnvironment.h"
+
+#define BUILD_GC_ON_IPC_MESSAGES
+
+#endif // DEBUG || GC_ON_IPC_MESSAGES
+
+namespace mozilla {
+namespace dom {
+namespace indexedDB {
+
+/*******************************************************************************
+ * Helpers
+ ******************************************************************************/
+
+namespace {
+
+void
+MaybeCollectGarbageOnIPCMessage()
+{
+#ifdef BUILD_GC_ON_IPC_MESSAGES
+ static const bool kCollectGarbageOnIPCMessages =
+#if GC_ON_IPC_MESSAGES
+ true;
+#else
+ false;
+#endif // GC_ON_IPC_MESSAGES
+
+ if (!kCollectGarbageOnIPCMessages) {
+ return;
+ }
+
+ static bool haveWarnedAboutGC = false;
+ static bool haveWarnedAboutNonMainThread = false;
+
+ if (!haveWarnedAboutGC) {
+ haveWarnedAboutGC = true;
+ NS_WARNING("IndexedDB child actor GC debugging enabled!");
+ }
+
+ if (!NS_IsMainThread()) {
+ if (!haveWarnedAboutNonMainThread) {
+ haveWarnedAboutNonMainThread = true;
+ NS_WARNING("Don't know how to GC on a non-main thread yet.");
+ }
+ return;
+ }
+
+ nsJSContext::GarbageCollectNow(JS::gcreason::DOM_IPC);
+ nsJSContext::CycleCollectNow();
+#endif // BUILD_GC_ON_IPC_MESSAGES
+}
+
+class MOZ_STACK_CLASS AutoSetCurrentTransaction MOZ_FINAL
+{
+ typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl;
+
+ IDBTransaction* const mTransaction;
+ IDBTransaction* mPreviousTransaction;
+ IDBTransaction** mThreadLocalSlot;
+
+public:
+ explicit AutoSetCurrentTransaction(IDBTransaction* aTransaction)
+ : mTransaction(aTransaction)
+ , mPreviousTransaction(nullptr)
+ , mThreadLocalSlot(nullptr)
+ {
+ if (aTransaction) {
+ BackgroundChildImpl::ThreadLocal* threadLocal =
+ BackgroundChildImpl::GetThreadLocalForCurrentThread();
+ MOZ_ASSERT(threadLocal);
+
+ // Hang onto this location for resetting later.
+ mThreadLocalSlot = &threadLocal->mCurrentTransaction;
+
+ // Save the current value.
+ mPreviousTransaction = *mThreadLocalSlot;
+
+ // Set the new value.
+ *mThreadLocalSlot = aTransaction;
+ }
+ }
+
+ ~AutoSetCurrentTransaction()
+ {
+ MOZ_ASSERT_IF(mThreadLocalSlot, mTransaction);
+
+ if (mThreadLocalSlot) {
+ MOZ_ASSERT(*mThreadLocalSlot == mTransaction);
+
+ // Reset old value.
+ *mThreadLocalSlot = mPreviousTransaction;
+ }
+ }
+
+ IDBTransaction*
+ Transaction() const
+ {
+ return mTransaction;
+ }
+};
+
+class MOZ_STACK_CLASS ResultHelper MOZ_FINAL
+ : public IDBRequest::ResultCallback
+{
+ IDBRequest* mRequest;
+ AutoSetCurrentTransaction mAutoTransaction;
+
+ union
+ {
+ nsISupports* mISupports;
+ StructuredCloneReadInfo* mStructuredClone;
+ const nsTArray<StructuredCloneReadInfo>* mStructuredCloneArray;
+ const Key* mKey;
+ const nsTArray<Key>* mKeyArray;
+ const JS::Value* mJSVal;
+ const JS::Handle<JS::Value>* mJSValHandle;
+ } mResult;
+
+ enum
+ {
+ ResultTypeISupports,
+ ResultTypeStructuredClone,
+ ResultTypeStructuredCloneArray,
+ ResultTypeKey,
+ ResultTypeKeyArray,
+ ResultTypeJSVal,
+ ResultTypeJSValHandle,
+ } mResultType;
+
+public:
+ ResultHelper(IDBRequest* aRequest,
+ IDBTransaction* aTransaction,
+ nsISupports* aResult)
+ : mRequest(aRequest)
+ , mAutoTransaction(aTransaction)
+ , mResultType(ResultTypeISupports)
+ {
+ MOZ_ASSERT(NS_IsMainThread(), "This won't work off the main thread!");
+ MOZ_ASSERT(aRequest);
+ MOZ_ASSERT(aResult);
+
+ mResult.mISupports = aResult;
+ }
+
+ ResultHelper(IDBRequest* aRequest,
+ IDBTransaction* aTransaction,
+ StructuredCloneReadInfo* aResult)
+ : mRequest(aRequest)
+ , mAutoTransaction(aTransaction)
+ , mResultType(ResultTypeStructuredClone)
+ {
+ MOZ_ASSERT(aRequest);
+ MOZ_ASSERT(aResult);
+
+ mResult.mStructuredClone = aResult;
+ }
+
+ ResultHelper(IDBRequest* aRequest,
+ IDBTransaction* aTransaction,
+ const nsTArray<StructuredCloneReadInfo>* aResult)
+ : mRequest(aRequest)
+ , mAutoTransaction(aTransaction)
+ , mResultType(ResultTypeStructuredCloneArray)
+ {
+ MOZ_ASSERT(aRequest);
+ MOZ_ASSERT(aResult);
+
+ mResult.mStructuredCloneArray = aResult;
+ }
+
+ ResultHelper(IDBRequest* aRequest,
+ IDBTransaction* aTransaction,
+ const Key* aResult)
+ : mRequest(aRequest)
+ , mAutoTransaction(aTransaction)
+ , mResultType(ResultTypeKey)
+ {
+ MOZ_ASSERT(aRequest);
+ MOZ_ASSERT(aResult);
+
+ mResult.mKey = aResult;
+ }
+
+ ResultHelper(IDBRequest* aRequest,
+ IDBTransaction* aTransaction,
+ const nsTArray<Key>* aResult)
+ : mRequest(aRequest)
+ , mAutoTransaction(aTransaction)
+ , mResultType(ResultTypeKeyArray)
+ {
+ MOZ_ASSERT(aRequest);
+ MOZ_ASSERT(aResult);
+
+ mResult.mKeyArray = aResult;
+ }
+
+ ResultHelper(IDBRequest* aRequest,
+ IDBTransaction* aTransaction,
+ const JS::Value* aResult)
+ : mRequest(aRequest)
+ , mAutoTransaction(aTransaction)
+ , mResultType(ResultTypeJSVal)
+ {
+ MOZ_ASSERT(aRequest);
+ MOZ_ASSERT(!aResult->isGCThing());
+
+ mResult.mJSVal = aResult;
+ }
+
+ ResultHelper(IDBRequest* aRequest,
+ IDBTransaction* aTransaction,
+ const JS::Handle<JS::Value>* aResult)
+ : mRequest(aRequest)
+ , mAutoTransaction(aTransaction)
+ , mResultType(ResultTypeJSValHandle)
+ {
+ MOZ_ASSERT(aRequest);
+
+ mResult.mJSValHandle = aResult;
+ }
+
+ IDBRequest*
+ Request() const
+ {
+ return mRequest;
+ }
+
+ IDBTransaction*
+ Transaction() const
+ {
+ return mAutoTransaction.Transaction();
+ }
+
+ virtual nsresult
+ GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) MOZ_OVERRIDE
+ {
+ MOZ_ASSERT(aCx);
+ MOZ_ASSERT(mRequest);
+
+ switch (mResultType) {
+ case ResultTypeISupports:
+ return GetResult(aCx, mResult.mISupports, aResult);
+
+ case ResultTypeStructuredClone:
+ return GetResult(aCx, mResult.mStructuredClone, aResult);
+
+ case ResultTypeStructuredCloneArray:
+ return GetResult(aCx, mResult.mStructuredCloneArray, aResult);
+
+ case ResultTypeKey:
+ return GetResult(aCx, mResult.mKey, aResult);
+
+ case ResultTypeKeyArray:
+ return GetResult(aCx, mResult.mKeyArray, aResult);
+
+ case ResultTypeJSVal:
+ aResult.set(*mResult.mJSVal);
+ return NS_OK;
+
+ case ResultTypeJSValHandle:
+ aResult.set(*mResult.mJSValHandle);
+ return NS_OK;
+
+ default:
+ MOZ_CRASH("Unknown result type!");
+ }
+
+ MOZ_CRASH("Should never get here!");
+ }
+
+private:
+ nsresult
+ GetResult(JSContext* aCx,
+ nsISupports* aSupports,
+ JS::MutableHandle<JS::Value> aResult)
+ {
+ MOZ_ASSERT(NS_IsMainThread(), "This won't work off the main thread!");
+
+ if (!aSupports) {
+ aResult.setNull();
+ return NS_OK;
+ }
+
+ nsresult rv = nsContentUtils::WrapNative(aCx, aSupports, aResult);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ return NS_OK;
+ }
+
+ nsresult
+ GetResult(JSContext* aCx,
+ StructuredCloneReadInfo* aCloneInfo,
+ JS::MutableHandle<JS::Value> aResult)
+ {
+ bool ok = IDBObjectStore::DeserializeValue(aCx, *aCloneInfo, aResult);
+
+ aCloneInfo->mCloneBuffer.clear();
+
+ if (NS_WARN_IF(!ok)) {
+ return NS_ERROR_DOM_DATA_CLONE_ERR;
+ }
+
+ return NS_OK;
+ }
+
+ nsresult
+ GetResult(JSContext* aCx,
+ const nsTArray<StructuredCloneReadInfo>* aCloneInfos,
+ JS::MutableHandle<JS::Value> aResult)
+ {
+ JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
+ if (NS_WARN_IF(!array)) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ if (!aCloneInfos->IsEmpty()) {
+ const uint32_t count = aCloneInfos->Length();
+
+ if (NS_WARN_IF(!JS_SetArrayLength(aCx, array, count))) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ for (uint32_t index = 0; index < count; index++) {
+ auto& cloneInfo =
+ const_cast<StructuredCloneReadInfo&>(aCloneInfos->ElementAt(index));
+
+ JS::Rooted<JS::Value> value(aCx);
+
+ nsresult rv = GetResult(aCx, &cloneInfo, &value);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (NS_WARN_IF(!JS_SetElement(aCx, array, index, value))) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+ }
+ }
+
+ aResult.setObject(*array);
+ return NS_OK;
+ }
+
+ nsresult
+ GetResult(JSContext* aCx,
+ const Key* aKey,
+ JS::MutableHandle<JS::Value> aResult)
+ {
+ nsresult rv = aKey->ToJSVal(aCx, aResult);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+ }
+
+ nsresult
+ GetResult(JSContext* aCx,
+ const nsTArray<Key>* aKeys,
+ JS::MutableHandle<JS::Value> aResult)
+ {
+ JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
+ if (NS_WARN_IF(!array)) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ if (!aKeys->IsEmpty()) {
+ const uint32_t count = aKeys->Length();
+
+ if (NS_WARN_IF(!JS_SetArrayLength(aCx, array, count))) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ for (uint32_t index = 0; index < count; index++) {
+ const Key& key = aKeys->ElementAt(index);
+ MOZ_ASSERT(!key.IsUnset());
+
+ JS::Rooted<JS::Value> value(aCx);
+
+ nsresult rv = GetResult(aCx, &key, &value);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (NS_WARN_IF(!JS_SetElement(aCx, array, index, value))) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+ }
+ }
+
+ aResult.setObject(*array);
+ return NS_OK;
+ }
+};
+
+class PermissionRequestMainProcessHelper MOZ_FINAL
+ : public PermissionRequestBase
+{
+ BackgroundFactoryRequestChild* mActor;
+ nsRefPtr<IDBFactory> mFactory;
+
+public:
+ PermissionRequestMainProcessHelper(BackgroundFactoryRequestChild* aActor,
+ IDBFactory* aFactory,
+ nsPIDOMWindow* aWindow,
+ nsIPrincipal* aPrincipal)
+ : PermissionRequestBase(aWindow, aPrincipal)
+ , mActor(aActor)
+ , mFactory(aFactory)
+ {
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(aFactory);
+ aActor->AssertIsOnOwningThread();
+ }
+
+protected:
+ ~PermissionRequestMainProcessHelper()
+ { }
+
+private:
+ virtual void
+ OnPromptComplete(PermissionValue aPermissionValue) MOZ_OVERRIDE;
+};
+
+class PermissionRequestChildProcessActor MOZ_FINAL
+ : public PIndexedDBPermissionRequestChild
+{
+ BackgroundFactoryRequestChild* mActor;
+ nsRefPtr<IDBFactory> mFactory;
+
+public:
+ PermissionRequestChildProcessActor(BackgroundFactoryRequestChild* aActor,
+ IDBFactory* aFactory)
+ : mActor(aActor)
+ , mFactory(aFactory)
+ {
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(aFactory);
+ aActor->AssertIsOnOwningThread();
+ }
+
+protected:
+ ~PermissionRequestChildProcessActor()
+ { }
+
+ virtual bool
+ Recv__delete__(const uint32_t& aPermission) MOZ_OVERRIDE;
+};
+
+void
+ConvertActorsToBlobs(IDBDatabase* aDatabase,
+ const SerializedStructuredCloneReadInfo& aCloneReadInfo,
+ nsTArray<StructuredCloneFile>& aFiles)
+{
+ MOZ_ASSERT(aFiles.IsEmpty());
+
+ const nsTArray<PBlobChild*>& blobs = aCloneReadInfo.blobsChild();
+ const nsTArray<intptr_t>& fileInfos = aCloneReadInfo.fileInfos();
+
+ MOZ_ASSERT_IF(IndexedDatabaseManager::IsMainProcess(),
+ blobs.Length() == fileInfos.Length());
+ MOZ_ASSERT_IF(!IndexedDatabaseManager::IsMainProcess(), fileInfos.IsEmpty());
+
+ if (!blobs.IsEmpty()) {
+ const uint32_t count = blobs.Length();
+ aFiles.SetCapacity(count);
+
+ for (uint32_t index = 0; index < count; index++) {
+ BlobChild* actor = static_cast<BlobChild*>(blobs[index]);
+
+ nsCOMPtr<nsIDOMBlob> blob = actor->GetBlob();
+ MOZ_ASSERT(blob);
+
+ nsRefPtr<FileInfo> fileInfo;
+ if (!fileInfos.IsEmpty()) {
+ fileInfo = dont_AddRef(reinterpret_cast<FileInfo*>(fileInfos[index]));
+
+ MOZ_ASSERT(fileInfo);
+ MOZ_ASSERT(fileInfo->Id() > 0);
+
+ blob->AddFileInfo(fileInfo);
+ }
+
+ aDatabase->NoteReceivedBlob(blob);
+
+ StructuredCloneFile* file = aFiles.AppendElement();
+ MOZ_ASSERT(file);
+
+ file->mFile.swap(blob);
+ file->mFileInfo.swap(fileInfo);
+ }
+ }
+}
+
+void
+DispatchSuccessEvent(ResultHelper* aResultHelper,
+ nsIDOMEvent* aEvent = nullptr)
+{
+ MOZ_ASSERT(aResultHelper);
+
+ PROFILER_LABEL("IndexedDB",
+ "DispatchSuccessEvent",
+ js::ProfileEntry::Category::STORAGE);
+
+ nsRefPtr<IDBRequest> request = aResultHelper->Request();
+ MOZ_ASSERT(request);
+ request->AssertIsOnOwningThread();
+
+ nsRefPtr<IDBTransaction> transaction = aResultHelper->Transaction();
+
+ nsCOMPtr<nsIDOMEvent> successEvent;
+ if (!aEvent) {
+ successEvent = CreateGenericEvent(request,
+ nsDependentString(kSuccessEventType),
+ eDoesNotBubble,
+ eNotCancelable);
+ if (NS_WARN_IF(!successEvent)) {
+ return;
+ }
+
+ aEvent = successEvent;
+ }
+
+ request->SetResultCallback(aResultHelper);
+
+ MOZ_ASSERT(aEvent);
+ MOZ_ASSERT_IF(transaction, transaction->IsOpen());
+
+ bool dummy;
+ nsresult rv = request->DispatchEvent(aEvent, &dummy);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ MOZ_ASSERT_IF(transaction,
+ transaction->IsOpen() || transaction->IsAborted());
+
+ WidgetEvent* internalEvent = aEvent->GetInternalNSEvent();
+ MOZ_ASSERT(internalEvent);
+
+ if (transaction &&
+ transaction->IsOpen() &&
+ internalEvent->mFlags.mExceptionHasBeenRisen) {
+ transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+ }
+}
+
+void
+DispatchErrorEvent(IDBRequest* aRequest,
+ nsresult aErrorCode,
+ IDBTransaction* aTransaction = nullptr,
+ nsIDOMEvent* aEvent = nullptr)
+{
+ MOZ_ASSERT(aRequest);
+ aRequest->AssertIsOnOwningThread();
+ MOZ_ASSERT(NS_FAILED(aErrorCode));
+ MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB);
+
+ PROFILER_LABEL("IndexedDB",
+ "DispatchErrorEvent",
+ js::ProfileEntry::Category::STORAGE);
+
+ nsRefPtr<IDBRequest> request = aRequest;
+ nsRefPtr<IDBTransaction> transaction = aTransaction;
+
+ request->SetError(aErrorCode);
+
+ nsCOMPtr<nsIDOMEvent> errorEvent;
+ if (!aEvent) {
+ // Make an error event and fire it at the target.
+ errorEvent = CreateGenericEvent(request,
+ nsDependentString(kErrorEventType),
+ eDoesBubble,
+ eCancelable);
+ if (NS_WARN_IF(!errorEvent)) {
+ return;
+ }
+
+ aEvent = errorEvent;
+ }
+
+ Maybe<AutoSetCurrentTransaction> asct;
+ if (aTransaction) {
+ asct.emplace(aTransaction);
+ }
+
+ bool doDefault;
+ nsresult rv = request->DispatchEvent(aEvent, &doDefault);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ MOZ_ASSERT(!transaction || transaction->IsOpen() || transaction->IsAborted());
+
+ if (transaction && transaction->IsOpen()) {
+ WidgetEvent* internalEvent = aEvent->GetInternalNSEvent();
+ MOZ_ASSERT(internalEvent);
+
+ if (internalEvent->mFlags.mExceptionHasBeenRisen) {
+ transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+ } else if (doDefault) {
+ transaction->Abort(request);
+ }
+ }
+}
+
+} // anonymous namespace
+
+/*******************************************************************************
+ * Local class implementations
+ ******************************************************************************/
+
+void
+PermissionRequestMainProcessHelper::OnPromptComplete(
+ PermissionValue aPermissionValue)
+{
+ MOZ_ASSERT(mActor);
+ mActor->AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ mActor->SendPermissionRetry();
+
+ mActor = nullptr;
+ mFactory = nullptr;
+}
+
+bool
+PermissionRequestChildProcessActor::Recv__delete__(
+ const uint32_t& /* aPermission */)
+{
+ MOZ_ASSERT(mActor);
+ mActor->AssertIsOnOwningThread();
+ MOZ_ASSERT(mFactory);
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ nsRefPtr<IDBFactory> factory;
+ mFactory.swap(factory);
+
+ mActor->SendPermissionRetry();
+ mActor = nullptr;
+
+ return true;
+}
+
+/*******************************************************************************
+ * BackgroundRequestChildBase
+ ******************************************************************************/
+
+BackgroundRequestChildBase::BackgroundRequestChildBase(IDBRequest* aRequest)
+ : mRequest(aRequest)
+ , mActorDestroyed(false)
+{
+ MOZ_ASSERT(aRequest);
+ aRequest->AssertIsOnOwningThread();
+
+ MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChildBase);
+}
+
+BackgroundRequestChildBase::~BackgroundRequestChildBase()
+{
+ AssertIsOnOwningThread();
+
+ MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChildBase);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundRequestChildBase::AssertIsOnOwningThread() const
+{
+ MOZ_ASSERT(mRequest);
+ mRequest->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+BackgroundRequestChildBase::NoteActorDestroyed()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!mActorDestroyed);
+
+ mActorDestroyed = true;
+}
+
+/*******************************************************************************
+ * BackgroundFactoryChild
+ ******************************************************************************/
+
+BackgroundFactoryChild::BackgroundFactoryChild(IDBFactory* aFactory)
+ : mFactory(aFactory)
+#ifdef DEBUG
+ , mOwningThread(NS_GetCurrentThread())
+#endif
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aFactory);
+ aFactory->AssertIsOnOwningThread();
+
+ MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryChild);
+}
+
+BackgroundFactoryChild::~BackgroundFactoryChild()
+{
+ MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryChild);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundFactoryChild::AssertIsOnOwningThread() const
+{
+ MOZ_ASSERT(mOwningThread);
+
+ bool current;
+ MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t)));
+ MOZ_ASSERT(current);
+}
+
+#endif // DEBUG
+
+void
+BackgroundFactoryChild::SendDeleteMeInternal()
+{
+ AssertIsOnOwningThread();
+
+ if (mFactory) {
+ mFactory->ClearBackgroundActor();
+ mFactory = nullptr;
+
+ MOZ_ALWAYS_TRUE(PBackgroundIDBFactoryChild::SendDeleteMe());
+ }
+}
+
+void
+BackgroundFactoryChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ if (mFactory) {
+ mFactory->ClearBackgroundActor();
+#ifdef DEBUG
+ mFactory = nullptr;
+#endif
+ }
+}
+
+PBackgroundIDBFactoryRequestChild*
+BackgroundFactoryChild::AllocPBackgroundIDBFactoryRequestChild(
+ const FactoryRequestParams& aParams)
+{
+ MOZ_CRASH("PBackgroundIDBFactoryRequestChild actors should be manually "
+ "constructed!");
+}
+
+bool
+BackgroundFactoryChild::DeallocPBackgroundIDBFactoryRequestChild(
+ PBackgroundIDBFactoryRequestChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BackgroundFactoryRequestChild*>(aActor);
+ return true;
+}
+
+PBackgroundIDBDatabaseChild*
+BackgroundFactoryChild::AllocPBackgroundIDBDatabaseChild(
+ const DatabaseSpec& aSpec,
+ PBackgroundIDBFactoryRequestChild* aRequest)
+{
+ AssertIsOnOwningThread();
+
+ auto request = static_cast<BackgroundFactoryRequestChild*>(aRequest);
+ MOZ_ASSERT(request);
+
+ return new BackgroundDatabaseChild(aSpec, request);
+}
+
+bool
+BackgroundFactoryChild::DeallocPBackgroundIDBDatabaseChild(
+ PBackgroundIDBDatabaseChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BackgroundDatabaseChild*>(aActor);
+ return true;
+}
+
+/*******************************************************************************
+ * BackgroundFactoryRequestChild
+ ******************************************************************************/
+
+BackgroundFactoryRequestChild::BackgroundFactoryRequestChild(
+ IDBFactory* aFactory,
+ IDBOpenDBRequest* aOpenRequest,
+ bool aIsDeleteOp,
+ uint64_t aRequestedVersion)
+ : BackgroundRequestChildBase(aOpenRequest)
+ , mFactory(aFactory)
+ , mRequestedVersion(aRequestedVersion)
+ , mIsDeleteOp(aIsDeleteOp)
+{
+ // Can't assert owning thread here because IPDL has not yet set our manager!
+ MOZ_ASSERT(aFactory);
+ aFactory->AssertIsOnOwningThread();
+ MOZ_ASSERT(aOpenRequest);
+
+ MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryRequestChild);
+}
+
+BackgroundFactoryRequestChild::~BackgroundFactoryRequestChild()
+{
+ MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryRequestChild);
+}
+
+IDBOpenDBRequest*
+BackgroundFactoryRequestChild::GetOpenDBRequest() const
+{
+ AssertIsOnOwningThread();
+
+ IDBRequest* baseRequest = BackgroundRequestChildBase::GetDOMObject();
+ return static_cast<IDBOpenDBRequest*>(baseRequest);
+}
+
+bool
+BackgroundFactoryRequestChild::HandleResponse(nsresult aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(NS_FAILED(aResponse));
+ MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB);
+
+ mRequest->Reset();
+
+ DispatchErrorEvent(mRequest, aResponse);
+
+ return true;
+}
+
+bool
+BackgroundFactoryRequestChild::HandleResponse(
+ const OpenDatabaseRequestResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ mRequest->Reset();
+
+ auto databaseActor =
+ static_cast<BackgroundDatabaseChild*>(aResponse.databaseChild());
+ MOZ_ASSERT(databaseActor);
+
+ databaseActor->EnsureDOMObject();
+
+ IDBDatabase* database = databaseActor->GetDOMObject();
+ MOZ_ASSERT(database);
+
+ ResultHelper helper(mRequest, nullptr,
+ static_cast<IDBWrapperCache*>(database));
+
+ DispatchSuccessEvent(&helper);
+
+ databaseActor->ReleaseDOMObject();
+
+ return true;
+}
+
+bool
+BackgroundFactoryRequestChild::HandleResponse(
+ const DeleteDatabaseRequestResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ ResultHelper helper(mRequest, nullptr, &JS::UndefinedHandleValue);
+
+ nsCOMPtr<nsIDOMEvent> successEvent =
+ IDBVersionChangeEvent::Create(mRequest,
+ nsDependentString(kSuccessEventType),
+ aResponse.previousVersion());
+ if (NS_WARN_IF(!successEvent)) {
+ return false;
+ }
+
+ DispatchSuccessEvent(&helper, successEvent);
+
+ return true;
+}
+
+void
+BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ NoteActorDestroyed();
+}
+
+bool
+BackgroundFactoryRequestChild::Recv__delete__(
+ const FactoryRequestResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ switch (aResponse.type()) {
+ case FactoryRequestResponse::Tnsresult:
+ return HandleResponse(aResponse.get_nsresult());
+
+ case FactoryRequestResponse::TOpenDatabaseRequestResponse:
+ return HandleResponse(aResponse.get_OpenDatabaseRequestResponse());
+
+ case FactoryRequestResponse::TDeleteDatabaseRequestResponse:
+ return HandleResponse(aResponse.get_DeleteDatabaseRequestResponse());
+
+ default:
+ MOZ_CRASH("Unknown response type!");
+ }
+
+ MOZ_CRASH("Should never get here!");
+}
+
+bool
+BackgroundFactoryRequestChild::RecvPermissionChallenge(
+ const PrincipalInfo& aPrincipalInfo)
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ if (!NS_IsMainThread()) {
+ MOZ_CRASH("Implement me for workers!");
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIPrincipal> principal =
+ mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ if (XRE_GetProcessType() == GeckoProcessType_Default) {
+ nsCOMPtr<nsPIDOMWindow> window = mFactory->GetParentObject();
+ MOZ_ASSERT(window);
+
+ nsRefPtr<PermissionRequestMainProcessHelper> helper =
+ new PermissionRequestMainProcessHelper(this, mFactory, window, principal);
+
+ PermissionRequestBase::PermissionValue permission;
+ if (NS_WARN_IF(NS_FAILED(helper->PromptIfNeeded(&permission)))) {
+ return false;
+ }
+
+ MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
+ permission == PermissionRequestBase::kPermissionDenied ||
+ permission == PermissionRequestBase::kPermissionPrompt);
+
+ if (permission != PermissionRequestBase::kPermissionPrompt) {
+ SendPermissionRetry();
+ }
+ return true;
+ }
+
+ nsRefPtr<TabChild> tabChild = mFactory->GetTabChild();
+ MOZ_ASSERT(tabChild);
+
+ IPC::Principal ipcPrincipal(principal);
+
+ auto* actor = new PermissionRequestChildProcessActor(this, mFactory);
+
+ tabChild->SendPIndexedDBPermissionRequestConstructor(actor, ipcPrincipal);
+
+ return true;
+}
+
+bool
+BackgroundFactoryRequestChild::RecvBlocked(const uint64_t& aCurrentVersion)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ const nsDependentString type(kBlockedEventType);
+
+ nsCOMPtr<nsIDOMEvent> blockedEvent;
+ if (mIsDeleteOp) {
+ blockedEvent =
+ IDBVersionChangeEvent::Create(mRequest, type, aCurrentVersion);
+ } else {
+ blockedEvent =
+ IDBVersionChangeEvent::Create(mRequest,
+ type,
+ aCurrentVersion,
+ mRequestedVersion);
+ }
+
+ if (NS_WARN_IF(!blockedEvent)) {
+ return false;
+ }
+
+ nsRefPtr<IDBRequest> kungFuDeathGrip = mRequest;
+
+ bool dummy;
+ if (NS_FAILED(mRequest->DispatchEvent(blockedEvent, &dummy))) {
+ NS_WARNING("Failed to dispatch event!");
+ }
+
+ return true;
+}
+
+/*******************************************************************************
+ * BackgroundDatabaseChild
+ ******************************************************************************/
+
+BackgroundDatabaseChild::BackgroundDatabaseChild(
+ const DatabaseSpec& aSpec,
+ BackgroundFactoryRequestChild* aOpenRequestActor)
+ : mSpec(new DatabaseSpec(aSpec))
+ , mOpenRequestActor(aOpenRequestActor)
+ , mDatabase(nullptr)
+{
+ // Can't assert owning thread here because IPDL has not yet set our manager!
+ MOZ_ASSERT(aOpenRequestActor);
+
+ MOZ_COUNT_CTOR(indexedDB::BackgroundDatabaseChild);
+}
+
+BackgroundDatabaseChild::~BackgroundDatabaseChild()
+{
+ MOZ_COUNT_DTOR(indexedDB::BackgroundDatabaseChild);
+}
+
+void
+BackgroundDatabaseChild::SendDeleteMeInternal()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!mTemporaryStrongDatabase);
+ MOZ_ASSERT(!mOpenRequestActor);
+
+ if (mDatabase) {
+ mDatabase->ClearBackgroundActor();
+ mDatabase = nullptr;
+
+ MOZ_ALWAYS_TRUE(PBackgroundIDBDatabaseChild::SendDeleteMe());
+ }
+}
+
+void
+BackgroundDatabaseChild::EnsureDOMObject()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mOpenRequestActor);
+
+ if (mTemporaryStrongDatabase) {
+ MOZ_ASSERT(!mSpec);
+ return;
+ }
+
+ MOZ_ASSERT(mSpec);
+
+ auto request = mOpenRequestActor->GetDOMObject();
+ MOZ_ASSERT(request);
+
+ auto factory =
+ static_cast<BackgroundFactoryChild*>(Manager())->GetDOMObject();
+ MOZ_ASSERT(factory);
+
+ mTemporaryStrongDatabase =
+ IDBDatabase::Create(request, factory, this, mSpec);
+
+ MOZ_ASSERT(mTemporaryStrongDatabase);
+ mTemporaryStrongDatabase->AssertIsOnOwningThread();
+
+ mDatabase = mTemporaryStrongDatabase;
+ mSpec.forget();
+}
+
+void
+BackgroundDatabaseChild::ReleaseDOMObject()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mTemporaryStrongDatabase);
+ mTemporaryStrongDatabase->AssertIsOnOwningThread();
+ MOZ_ASSERT(mOpenRequestActor);
+ MOZ_ASSERT(mDatabase == mTemporaryStrongDatabase);
+
+ mOpenRequestActor = nullptr;
+
+ // This may be the final reference to the IDBDatabase object so we may end up
+ // calling SendDeleteMeInternal() here. Make sure everything is cleaned up
+ // properly before proceeding.
+ mTemporaryStrongDatabase = nullptr;
+}
+
+void
+BackgroundDatabaseChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ if (mDatabase) {
+ mDatabase->ClearBackgroundActor();
+#ifdef DEBUG
+ mDatabase = nullptr;
+#endif
+ }
+}
+
+PBackgroundIDBDatabaseFileChild*
+BackgroundDatabaseChild::AllocPBackgroundIDBDatabaseFileChild(
+ PBlobChild* aBlobChild)
+{
+ MOZ_CRASH("PBackgroundIDBFileChild actors should be manually constructed!");
+}
+
+bool
+BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseFileChild(
+ PBackgroundIDBDatabaseFileChild* aActor)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aActor);
+
+ delete aActor;
+ return true;
+}
+
+PBackgroundIDBTransactionChild*
+BackgroundDatabaseChild::AllocPBackgroundIDBTransactionChild(
+ const nsTArray<nsString>& aObjectStoreNames,
+ const Mode& aMode)
+{
+ MOZ_CRASH("PBackgroundIDBTransactionChild actors should be manually "
+ "constructed!");
+}
+
+bool
+BackgroundDatabaseChild::DeallocPBackgroundIDBTransactionChild(
+ PBackgroundIDBTransactionChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BackgroundTransactionChild*>(aActor);
+ return true;
+}
+
+PBackgroundIDBVersionChangeTransactionChild*
+BackgroundDatabaseChild::AllocPBackgroundIDBVersionChangeTransactionChild(
+ const uint64_t& aCurrentVersion,
+ const uint64_t& aRequestedVersion,
+ const int64_t& aNextObjectStoreId,
+ const int64_t& aNextIndexId)
+{
+ AssertIsOnOwningThread();
+
+ IDBOpenDBRequest* request = mOpenRequestActor->GetOpenDBRequest();
+ MOZ_ASSERT(request);
+
+ return new BackgroundVersionChangeTransactionChild(request);
+}
+
+bool
+BackgroundDatabaseChild::RecvPBackgroundIDBVersionChangeTransactionConstructor(
+ PBackgroundIDBVersionChangeTransactionChild* aActor,
+ const uint64_t& aCurrentVersion,
+ const uint64_t& aRequestedVersion,
+ const int64_t& aNextObjectStoreId,
+ const int64_t& aNextIndexId)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(mOpenRequestActor);
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ EnsureDOMObject();
+
+ auto actor = static_cast<BackgroundVersionChangeTransactionChild*>(aActor);
+
+ nsRefPtr<IDBTransaction> transaction =
+ IDBTransaction::CreateVersionChange(mDatabase, actor, aNextObjectStoreId,
+ aNextIndexId);
+ if (NS_WARN_IF(!transaction)) {
+ return false;
+ }
+
+ transaction->AssertIsOnOwningThread();
+
+ actor->SetDOMTransaction(transaction);
+
+ mDatabase->EnterSetVersionTransaction(aRequestedVersion);
+
+ nsRefPtr<IDBOpenDBRequest> request = mOpenRequestActor->GetOpenDBRequest();
+ MOZ_ASSERT(request);
+
+ request->SetTransaction(transaction);
+
+ nsCOMPtr<nsIDOMEvent> upgradeNeededEvent =
+ IDBVersionChangeEvent::Create(request,
+ nsDependentString(kUpgradeNeededEventType),
+ aCurrentVersion,
+ aRequestedVersion);
+ if (NS_WARN_IF(!upgradeNeededEvent)) {
+ return false;
+ }
+
+ ResultHelper helper(request, transaction,
+ static_cast<IDBWrapperCache*>(mDatabase));
+
+ DispatchSuccessEvent(&helper, upgradeNeededEvent);
+
+ return true;
+}
+
+bool
+BackgroundDatabaseChild::DeallocPBackgroundIDBVersionChangeTransactionChild(
+ PBackgroundIDBVersionChangeTransactionChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BackgroundVersionChangeTransactionChild*>(aActor);
+ return true;
+}
+
+bool
+BackgroundDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion,
+ const NullableVersion& aNewVersion)
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ if (!mDatabase || mDatabase->IsClosed()) {
+ return true;
+ }
+
+ nsRefPtr<IDBDatabase> kungFuDeathGrip = mDatabase;
+
+ // Handle bfcache'd windows.
+ if (nsPIDOMWindow* owner = mDatabase->GetOwner()) {
+ // The database must be closed if the window is already frozen.
+ bool shouldAbortAndClose = owner->IsFrozen();
+
+ // Anything in the bfcache has to be evicted and then we have to close the
+ // database also.
+ if (nsCOMPtr<nsIDocument> doc = owner->GetExtantDoc()) {
+ if (nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry()) {
+ bfCacheEntry->RemoveFromBFCacheSync();
+ shouldAbortAndClose = true;
+ }
+ }
+
+ if (shouldAbortAndClose) {
+ // Invalidate() doesn't close the database in the parent, so we have
+ // to call Close() and AbortTransactions() manually.
+ mDatabase->AbortTransactions();
+ mDatabase->Close();
+ return true;
+ }
+ }
+
+ // Otherwise fire a versionchange event.
+ const nsDependentString type(kVersionChangeEventType);
+
+ nsCOMPtr<nsIDOMEvent> versionChangeEvent;
+
+ switch (aNewVersion.type()) {
+ case NullableVersion::Tnull_t:
+ versionChangeEvent =
+ IDBVersionChangeEvent::Create(mDatabase, type, aOldVersion);
+ break;
+
+ case NullableVersion::Tuint64_t:
+ versionChangeEvent =
+ IDBVersionChangeEvent::Create(mDatabase,
+ type,
+ aOldVersion,
+ aNewVersion.get_uint64_t());
+ break;
+
+ default:
+ MOZ_CRASH("Should never get here!");
+ }
+
+ if (NS_WARN_IF(!versionChangeEvent)) {
+ return false;
+ }
+
+ bool dummy;
+ if (NS_FAILED(mDatabase->DispatchEvent(versionChangeEvent, &dummy))) {
+ NS_WARNING("Failed to dispatch event!");
+ }
+
+ if (!mDatabase->IsClosed()) {
+ SendBlocked();
+ }
+
+ return true;
+}
+
+bool
+BackgroundDatabaseChild::RecvInvalidate()
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ if (mDatabase) {
+ mDatabase->Invalidate();
+ }
+
+ return true;
+}
+
+/*******************************************************************************
+ * BackgroundTransactionBase
+ ******************************************************************************/
+
+BackgroundTransactionBase::BackgroundTransactionBase()
+: mTransaction(nullptr)
+{
+ MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionBase);
+}
+
+BackgroundTransactionBase::BackgroundTransactionBase(
+ IDBTransaction* aTransaction)
+ : mTemporaryStrongTransaction(aTransaction)
+ , mTransaction(aTransaction)
+{
+ MOZ_ASSERT(aTransaction);
+ aTransaction->AssertIsOnOwningThread();
+
+ MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionBase);
+}
+
+BackgroundTransactionBase::~BackgroundTransactionBase()
+{
+ MOZ_COUNT_DTOR(indexedDB::BackgroundTransactionBase);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundTransactionBase::AssertIsOnOwningThread() const
+{
+ MOZ_ASSERT(mTransaction);
+ mTransaction->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+BackgroundTransactionBase::NoteActorDestroyed()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT_IF(mTemporaryStrongTransaction, mTransaction);
+
+ if (mTransaction) {
+ mTransaction->ClearBackgroundActor();
+
+ // Normally this would be DEBUG-only but NoteActorDestroyed is also called
+ // from SendDeleteMeInternal. In that case we're going to receive an actual
+ // ActorDestroy call later and we don't want to touch a dead object.
+ mTemporaryStrongTransaction = nullptr;
+ mTransaction = nullptr;
+ }
+}
+
+void
+BackgroundTransactionBase::SetDOMTransaction(IDBTransaction* aTransaction)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aTransaction);
+ aTransaction->AssertIsOnOwningThread();
+ MOZ_ASSERT(!mTemporaryStrongTransaction);
+ MOZ_ASSERT(!mTransaction);
+
+ mTemporaryStrongTransaction = aTransaction;
+ mTransaction = aTransaction;
+}
+
+void
+BackgroundTransactionBase::NoteComplete()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT_IF(mTransaction, mTemporaryStrongTransaction);
+
+ mTemporaryStrongTransaction = nullptr;
+}
+
+/*******************************************************************************
+ * BackgroundTransactionChild
+ ******************************************************************************/
+
+BackgroundTransactionChild::BackgroundTransactionChild(
+ IDBTransaction* aTransaction)
+ : BackgroundTransactionBase(aTransaction)
+{
+ MOZ_ASSERT(aTransaction);
+ aTransaction->AssertIsOnOwningThread();
+
+ MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionChild);
+}
+
+BackgroundTransactionChild::~BackgroundTransactionChild()
+{
+ MOZ_COUNT_DTOR(indexedDB::BackgroundTransactionChild);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundTransactionChild::AssertIsOnOwningThread() const
+{
+ static_cast<BackgroundDatabaseChild*>(Manager())->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+BackgroundTransactionChild::SendDeleteMeInternal()
+{
+ AssertIsOnOwningThread();
+
+ if (mTransaction) {
+ NoteActorDestroyed();
+
+ MOZ_ALWAYS_TRUE(PBackgroundIDBTransactionChild::SendDeleteMe());
+ }
+}
+
+void
+BackgroundTransactionChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ NoteActorDestroyed();
+}
+
+bool
+BackgroundTransactionChild::RecvComplete(const nsresult& aResult)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mTransaction);
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ mTransaction->FireCompleteOrAbortEvents(aResult);
+
+ NoteComplete();
+ return true;
+}
+
+PBackgroundIDBRequestChild*
+BackgroundTransactionChild::AllocPBackgroundIDBRequestChild(
+ const RequestParams& aParams)
+{
+ MOZ_CRASH("PBackgroundIDBRequestChild actors should be manually "
+ "constructed!");
+}
+
+bool
+BackgroundTransactionChild::DeallocPBackgroundIDBRequestChild(
+ PBackgroundIDBRequestChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BackgroundRequestChild*>(aActor);
+ return true;
+}
+
+PBackgroundIDBCursorChild*
+BackgroundTransactionChild::AllocPBackgroundIDBCursorChild(
+ const OpenCursorParams& aParams)
+{
+ AssertIsOnOwningThread();
+
+ MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!");
+}
+
+bool
+BackgroundTransactionChild::DeallocPBackgroundIDBCursorChild(
+ PBackgroundIDBCursorChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BackgroundCursorChild*>(aActor);
+ return true;
+}
+
+/*******************************************************************************
+ * BackgroundVersionChangeTransactionChild
+ ******************************************************************************/
+
+BackgroundVersionChangeTransactionChild::
+BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest)
+ : mOpenDBRequest(aOpenDBRequest)
+{
+ MOZ_ASSERT(aOpenDBRequest);
+ aOpenDBRequest->AssertIsOnOwningThread();
+
+ MOZ_COUNT_CTOR(indexedDB::BackgroundVersionChangeTransactionChild);
+}
+
+BackgroundVersionChangeTransactionChild::
+~BackgroundVersionChangeTransactionChild()
+{
+ AssertIsOnOwningThread();
+
+ MOZ_COUNT_DTOR(indexedDB::BackgroundVersionChangeTransactionChild);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundVersionChangeTransactionChild::AssertIsOnOwningThread() const
+{
+ static_cast<BackgroundDatabaseChild*>(Manager())->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+BackgroundVersionChangeTransactionChild::SendDeleteMeInternal()
+{
+ AssertIsOnOwningThread();
+
+ if (mTransaction) {
+ NoteActorDestroyed();
+
+ MOZ_ALWAYS_TRUE(PBackgroundIDBVersionChangeTransactionChild::
+ SendDeleteMe());
+ }
+}
+
+void
+BackgroundVersionChangeTransactionChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ mOpenDBRequest = nullptr;
+
+ NoteActorDestroyed();
+}
+
+bool
+BackgroundVersionChangeTransactionChild::RecvComplete(const nsresult& aResult)
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ if (!mTransaction) {
+ return true;
+ }
+
+ MOZ_ASSERT(mOpenDBRequest);
+
+ IDBDatabase* database = mTransaction->Database();
+ MOZ_ASSERT(database);
+
+ database->ExitSetVersionTransaction();
+
+ if (NS_FAILED(aResult)) {
+ database->Close();
+ }
+
+ mTransaction->FireCompleteOrAbortEvents(aResult);
+
+ mOpenDBRequest->SetTransaction(nullptr);
+ mOpenDBRequest = nullptr;
+
+ NoteComplete();
+ return true;
+}
+
+PBackgroundIDBRequestChild*
+BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBRequestChild(
+ const RequestParams& aParams)
+{
+ MOZ_CRASH("PBackgroundIDBRequestChild actors should be manually "
+ "constructed!");
+}
+
+bool
+BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBRequestChild(
+ PBackgroundIDBRequestChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BackgroundRequestChild*>(aActor);
+ return true;
+}
+
+PBackgroundIDBCursorChild*
+BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBCursorChild(
+ const OpenCursorParams& aParams)
+{
+ AssertIsOnOwningThread();
+
+ MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!");
+}
+
+bool
+BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBCursorChild(
+ PBackgroundIDBCursorChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BackgroundCursorChild*>(aActor);
+ return true;
+}
+
+/*******************************************************************************
+ * BackgroundRequestChild
+ ******************************************************************************/
+
+BackgroundRequestChild::BackgroundRequestChild(IDBRequest* aRequest)
+ : BackgroundRequestChildBase(aRequest)
+ , mTransaction(aRequest->GetTransaction())
+{
+ MOZ_ASSERT(mTransaction);
+ mTransaction->AssertIsOnOwningThread();
+
+ MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChild);
+
+ mTransaction->OnNewRequest();
+}
+
+BackgroundRequestChild::~BackgroundRequestChild()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT_IF(!IsActorDestroyed(), mTransaction);
+
+ MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChild);
+
+ MaybeFinishTransactionEarly();
+}
+
+void
+BackgroundRequestChild::HoldFileInfosUntilComplete(
+ nsTArray<nsRefPtr<FileInfo>>& aFileInfos)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mFileInfos.IsEmpty());
+
+ mFileInfos.SwapElements(aFileInfos);
+}
+
+void
+BackgroundRequestChild::MaybeFinishTransactionEarly()
+{
+ AssertIsOnOwningThread();
+
+ if (mTransaction) {
+ mTransaction->AssertIsOnOwningThread();
+
+ mTransaction->OnRequestFinished();
+ mTransaction = nullptr;
+ }
+}
+
+bool
+BackgroundRequestChild::HandleResponse(nsresult aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(NS_FAILED(aResponse));
+ MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB);
+ MOZ_ASSERT(mTransaction);
+
+ DispatchErrorEvent(mRequest, aResponse, mTransaction);
+ return true;
+}
+
+bool
+BackgroundRequestChild::HandleResponse(const Key& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ ResultHelper helper(mRequest, mTransaction, &aResponse);
+
+ DispatchSuccessEvent(&helper);
+ return true;
+}
+
+bool
+BackgroundRequestChild::HandleResponse(const nsTArray<Key>& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ ResultHelper helper(mRequest, mTransaction, &aResponse);
+
+ DispatchSuccessEvent(&helper);
+ return true;
+}
+
+bool
+BackgroundRequestChild::HandleResponse(
+ const SerializedStructuredCloneReadInfo& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ // XXX Fix this somehow...
+ auto& serializedCloneInfo =
+ const_cast<SerializedStructuredCloneReadInfo&>(aResponse);
+
+ StructuredCloneReadInfo cloneReadInfo(Move(serializedCloneInfo));
+ cloneReadInfo.mDatabase = mTransaction->Database();
+
+ ConvertActorsToBlobs(mTransaction->Database(),
+ aResponse,
+ cloneReadInfo.mFiles);
+
+ ResultHelper helper(mRequest, mTransaction, &cloneReadInfo);
+
+ DispatchSuccessEvent(&helper);
+ return true;
+}
+
+bool
+BackgroundRequestChild::HandleResponse(
+ const nsTArray<SerializedStructuredCloneReadInfo>& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ nsTArray<StructuredCloneReadInfo> cloneReadInfos;
+
+ if (!aResponse.IsEmpty()) {
+ const uint32_t count = aResponse.Length();
+
+ cloneReadInfos.SetCapacity(count);
+
+ IDBDatabase* database = mTransaction->Database();
+
+ for (uint32_t index = 0; index < count; index++) {
+ // XXX Fix this somehow...
+ auto& serializedCloneInfo =
+ const_cast<SerializedStructuredCloneReadInfo&>(aResponse[index]);
+
+ StructuredCloneReadInfo* cloneReadInfo = cloneReadInfos.AppendElement();
+
+ *cloneReadInfo = Move(serializedCloneInfo);
+
+ cloneReadInfo->mDatabase = mTransaction->Database();
+
+ ConvertActorsToBlobs(database,
+ serializedCloneInfo,
+ cloneReadInfo->mFiles);
+ }
+ }
+
+ ResultHelper helper(mRequest, mTransaction, &cloneReadInfos);
+
+ DispatchSuccessEvent(&helper);
+ return true;
+}
+
+bool
+BackgroundRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse)
+{
+ AssertIsOnOwningThread();
+
+ ResultHelper helper(mRequest, mTransaction, &aResponse);
+
+ DispatchSuccessEvent(&helper);
+ return true;
+}
+
+bool
+BackgroundRequestChild::HandleResponse(uint64_t aResponse)
+{
+ AssertIsOnOwningThread();
+
+ JS::Value response(JS::NumberValue(aResponse));
+
+ ResultHelper helper(mRequest, mTransaction, &response);
+
+ DispatchSuccessEvent(&helper);
+ return true;
+}
+
+void
+BackgroundRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ MaybeFinishTransactionEarly();
+
+ NoteActorDestroyed();
+}
+
+bool
+BackgroundRequestChild::Recv__delete__(const RequestResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mTransaction);
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ // Always fire an "error" event with ABORT_ERR if the transaction was aborted,
+ // even if the request succeeded or failed with another error.
+ if (mTransaction->IsAborted()) {
+ return HandleResponse(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+ }
+
+ switch (aResponse.type()) {
+ case RequestResponse::Tnsresult:
+ return HandleResponse(aResponse.get_nsresult());
+
+ case RequestResponse::TObjectStoreAddResponse:
+ return HandleResponse(aResponse.get_ObjectStoreAddResponse().key());
+
+ case RequestResponse::TObjectStorePutResponse:
+ return HandleResponse(aResponse.get_ObjectStorePutResponse().key());
+
+ case RequestResponse::TObjectStoreGetResponse:
+ return HandleResponse(aResponse.get_ObjectStoreGetResponse().cloneInfo());
+
+ case RequestResponse::TObjectStoreGetAllResponse:
+ return HandleResponse(aResponse.get_ObjectStoreGetAllResponse()
+ .cloneInfos());
+
+ case RequestResponse::TObjectStoreGetAllKeysResponse:
+ return HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse()
+ .keys());
+
+ case RequestResponse::TObjectStoreDeleteResponse:
+ return HandleResponse(JS::UndefinedHandleValue);
+
+ case RequestResponse::TObjectStoreClearResponse:
+ return HandleResponse(JS::UndefinedHandleValue);
+
+ case RequestResponse::TObjectStoreCountResponse:
+ return HandleResponse(aResponse.get_ObjectStoreCountResponse().count());
+
+ case RequestResponse::TIndexGetResponse:
+ return HandleResponse(aResponse.get_IndexGetResponse().cloneInfo());
+
+ case RequestResponse::TIndexGetKeyResponse:
+ return HandleResponse(aResponse.get_IndexGetKeyResponse().key());
+
+ case RequestResponse::TIndexGetAllResponse:
+ return HandleResponse(aResponse.get_IndexGetAllResponse().cloneInfos());
+
+ case RequestResponse::TIndexGetAllKeysResponse:
+ return HandleResponse(aResponse.get_IndexGetAllKeysResponse().keys());
+
+ case RequestResponse::TIndexCountResponse:
+ return HandleResponse(aResponse.get_IndexCountResponse().count());
+
+ default:
+ MOZ_CRASH("Unknown response type!");
+ }
+
+ MOZ_CRASH("Should never get here!");
+}
+
+/*******************************************************************************
+ * BackgroundCursorChild
+ ******************************************************************************/
+
+class BackgroundCursorChild::DelayedDeleteRunnable MOZ_FINAL
+ : public nsIRunnable
+{
+ BackgroundCursorChild* mActor;
+ nsRefPtr<IDBRequest> mRequest;
+
+public:
+ explicit DelayedDeleteRunnable(BackgroundCursorChild* aActor)
+ : mActor(aActor)
+ , mRequest(aActor->mRequest)
+ {
+ MOZ_ASSERT(aActor);
+ aActor->AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+ }
+
+ // Does not need to be threadsafe since this only runs on one thread.
+ NS_DECL_ISUPPORTS
+
+private:
+ ~DelayedDeleteRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+BackgroundCursorChild::BackgroundCursorChild(IDBRequest* aRequest,
+ IDBObjectStore* aObjectStore,
+ Direction aDirection)
+ : mRequest(aRequest)
+ , mTransaction(aRequest->GetTransaction())
+ , mObjectStore(aObjectStore)
+ , mIndex(nullptr)
+ , mCursor(nullptr)
+ , mStrongRequest(aRequest)
+ , mDirection(aDirection)
+{
+ MOZ_ASSERT(aObjectStore);
+ aObjectStore->AssertIsOnOwningThread();
+ MOZ_ASSERT(mTransaction);
+
+ MOZ_COUNT_CTOR(indexedDB::BackgroundCursorChild);
+
+#ifdef DEBUG
+ mOwningThread = PR_GetCurrentThread();
+ MOZ_ASSERT(mOwningThread);
+#endif
+}
+
+BackgroundCursorChild::BackgroundCursorChild(IDBRequest* aRequest,
+ IDBIndex* aIndex,
+ Direction aDirection)
+ : mRequest(aRequest)
+ , mTransaction(aRequest->GetTransaction())
+ , mObjectStore(nullptr)
+ , mIndex(aIndex)
+ , mCursor(nullptr)
+ , mStrongRequest(aRequest)
+ , mDirection(aDirection)
+{
+ MOZ_ASSERT(aIndex);
+ aIndex->AssertIsOnOwningThread();
+ MOZ_ASSERT(mTransaction);
+
+ MOZ_COUNT_CTOR(indexedDB::BackgroundCursorChild);
+
+#ifdef DEBUG
+ mOwningThread = PR_GetCurrentThread();
+ MOZ_ASSERT(mOwningThread);
+#endif
+}
+
+BackgroundCursorChild::~BackgroundCursorChild()
+{
+ MOZ_COUNT_DTOR(indexedDB::BackgroundCursorChild);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundCursorChild::AssertIsOnOwningThread() const
+{
+ MOZ_ASSERT(mOwningThread == PR_GetCurrentThread());
+}
+
+#endif // DEBUG
+
+void
+BackgroundCursorChild::SendContinueInternal(const CursorRequestParams& aParams)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mTransaction);
+ MOZ_ASSERT(mCursor);
+ MOZ_ASSERT(!mStrongRequest);
+ MOZ_ASSERT(!mStrongCursor);
+
+ // Make sure all our DOM objects stay alive.
+ mStrongRequest = mRequest;
+ mStrongCursor = mCursor;
+
+ MOZ_ASSERT(mRequest->ReadyState() == IDBRequestReadyState::Done);
+ mRequest->Reset();
+
+ mTransaction->OnNewRequest();
+
+ MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendContinue(aParams));
+}
+
+void
+BackgroundCursorChild::SendDeleteMeInternal()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!mStrongRequest);
+ MOZ_ASSERT(!mStrongCursor);
+
+ mRequest = nullptr;
+ mTransaction = nullptr;
+ mObjectStore = nullptr;
+ mIndex = nullptr;
+
+ if (mCursor) {
+ mCursor->ClearBackgroundActor();
+ mCursor = nullptr;
+
+ MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendDeleteMe());
+ }
+}
+
+void
+BackgroundCursorChild::HandleResponse(nsresult aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(NS_FAILED(aResponse));
+ MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB);
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mTransaction);
+ MOZ_ASSERT(!mStrongRequest);
+ MOZ_ASSERT(!mStrongCursor);
+
+ DispatchErrorEvent(mRequest, aResponse, mTransaction);
+}
+
+void
+BackgroundCursorChild::HandleResponse(const void_t& aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mTransaction);
+ MOZ_ASSERT(!mStrongRequest);
+ MOZ_ASSERT(!mStrongCursor);
+
+ if (mCursor) {
+ mCursor->Reset();
+ }
+
+ ResultHelper helper(mRequest, mTransaction, &JS::NullHandleValue);
+ DispatchSuccessEvent(&helper);
+
+ if (!mCursor) {
+ nsCOMPtr<nsIRunnable> deleteRunnable = new DelayedDeleteRunnable(this);
+ MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(deleteRunnable)));
+ }
+}
+
+void
+BackgroundCursorChild::HandleResponse(
+ const ObjectStoreCursorResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mTransaction);
+ MOZ_ASSERT(mObjectStore);
+ MOZ_ASSERT(!mStrongRequest);
+ MOZ_ASSERT(!mStrongCursor);
+
+ // XXX Fix this somehow...
+ auto& response = const_cast<ObjectStoreCursorResponse&>(aResponse);
+
+ StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo()));
+
+ ConvertActorsToBlobs(mTransaction->Database(),
+ response.cloneInfo(),
+ cloneReadInfo.mFiles);
+
+ nsRefPtr<IDBCursor> newCursor;
+
+ if (mCursor) {
+ mCursor->Reset(Move(response.key()), Move(cloneReadInfo));
+ } else {
+ newCursor = IDBCursor::Create(mObjectStore,
+ this,
+ mDirection,
+ Move(response.key()),
+ Move(cloneReadInfo));
+ mCursor = newCursor;
+ }
+
+ ResultHelper helper(mRequest, mTransaction, mCursor);
+ DispatchSuccessEvent(&helper);
+}
+
+void
+BackgroundCursorChild::HandleResponse(
+ const ObjectStoreKeyCursorResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mTransaction);
+ MOZ_ASSERT(mObjectStore);
+ MOZ_ASSERT(!mStrongRequest);
+ MOZ_ASSERT(!mStrongCursor);
+
+ // XXX Fix this somehow...
+ auto& response = const_cast<ObjectStoreKeyCursorResponse&>(aResponse);
+
+ nsRefPtr<IDBCursor> newCursor;
+
+ if (mCursor) {
+ mCursor->Reset(Move(response.key()));
+ } else {
+ newCursor = IDBCursor::Create(mObjectStore,
+ this,
+ mDirection,
+ Move(response.key()));
+ mCursor = newCursor;
+ }
+
+ ResultHelper helper(mRequest, mTransaction, mCursor);
+ DispatchSuccessEvent(&helper);
+}
+
+void
+BackgroundCursorChild::HandleResponse(const IndexCursorResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mTransaction);
+ MOZ_ASSERT(mIndex);
+ MOZ_ASSERT(!mStrongRequest);
+ MOZ_ASSERT(!mStrongCursor);
+
+ // XXX Fix this somehow...
+ auto& response = const_cast<IndexCursorResponse&>(aResponse);
+
+ StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo()));
+
+ ConvertActorsToBlobs(mTransaction->Database(),
+ aResponse.cloneInfo(),
+ cloneReadInfo.mFiles);
+
+ nsRefPtr<IDBCursor> newCursor;
+
+ if (mCursor) {
+ mCursor->Reset(Move(response.key()),
+ Move(response.objectKey()),
+ Move(cloneReadInfo));
+ } else {
+ newCursor = IDBCursor::Create(mIndex,
+ this,
+ mDirection,
+ Move(response.key()),
+ Move(response.objectKey()),
+ Move(cloneReadInfo));
+ mCursor = newCursor;
+ }
+
+ ResultHelper helper(mRequest, mTransaction, mCursor);
+ DispatchSuccessEvent(&helper);
+}
+
+void
+BackgroundCursorChild::HandleResponse(const IndexKeyCursorResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mTransaction);
+ MOZ_ASSERT(mIndex);
+ MOZ_ASSERT(!mStrongRequest);
+ MOZ_ASSERT(!mStrongCursor);
+
+ // XXX Fix this somehow...
+ auto& response = const_cast<IndexKeyCursorResponse&>(aResponse);
+
+ nsRefPtr<IDBCursor> newCursor;
+
+ if (mCursor) {
+ mCursor->Reset(Move(response.key()), Move(response.objectKey()));
+ } else {
+ newCursor = IDBCursor::Create(mIndex,
+ this,
+ mDirection,
+ Move(response.key()),
+ Move(response.objectKey()));
+ mCursor = newCursor;
+ }
+
+ ResultHelper helper(mRequest, mTransaction, mCursor);
+ DispatchSuccessEvent(&helper);
+}
+
+void
+BackgroundCursorChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT_IF(aWhy == Deletion, !mStrongRequest);
+ MOZ_ASSERT_IF(aWhy == Deletion, !mStrongCursor);
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ if (mStrongRequest && !mStrongCursor && mTransaction) {
+ mTransaction->OnRequestFinished();
+ }
+
+ if (mCursor) {
+ mCursor->ClearBackgroundActor();
+#ifdef DEBUG
+ mCursor = nullptr;
+#endif
+ }
+
+#ifdef DEBUG
+ mRequest = nullptr;
+ mTransaction = nullptr;
+ mObjectStore = nullptr;
+ mIndex = nullptr;
+#endif
+}
+
+bool
+BackgroundCursorChild::RecvResponse(const CursorResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aResponse.type() != CursorResponse::T__None);
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mTransaction);
+ MOZ_ASSERT(mStrongRequest);
+ MOZ_ASSERT_IF(mCursor, mStrongCursor);
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ nsRefPtr<IDBRequest> request;
+ mStrongRequest.swap(request);
+
+ nsRefPtr<IDBCursor> cursor;
+ mStrongCursor.swap(cursor);
+
+ switch (aResponse.type()) {
+ case CursorResponse::Tnsresult:
+ HandleResponse(aResponse.get_nsresult());
+ break;
+
+ case CursorResponse::Tvoid_t:
+ HandleResponse(aResponse.get_void_t());
+ break;
+
+ case CursorResponse::TObjectStoreCursorResponse:
+ HandleResponse(aResponse.get_ObjectStoreCursorResponse());
+ break;
+
+ case CursorResponse::TObjectStoreKeyCursorResponse:
+ HandleResponse(aResponse.get_ObjectStoreKeyCursorResponse());
+ break;
+
+ case CursorResponse::TIndexCursorResponse:
+ HandleResponse(aResponse.get_IndexCursorResponse());
+ break;
+
+ case CursorResponse::TIndexKeyCursorResponse:
+ HandleResponse(aResponse.get_IndexKeyCursorResponse());
+ break;
+
+ default:
+ MOZ_CRASH("Should never get here!");
+ }
+
+ mTransaction->OnRequestFinished();
+
+ return true;
+}
+
+// XXX This doesn't belong here. However, we're not yet porting MutableFile
+// stuff to PBackground so this is necessary for the time being.
+void
+DispatchMutableFileResult(IDBRequest* aRequest,
+ nsresult aResultCode,
+ IDBMutableFile* aMutableFile)
+{
+ MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aRequest);
+ MOZ_ASSERT_IF(NS_SUCCEEDED(aResultCode), aMutableFile);
+
+ if (NS_SUCCEEDED(aResultCode)) {
+ ResultHelper helper(aRequest, nullptr, aMutableFile);
+ DispatchSuccessEvent(&helper);
+ } else {
+ DispatchErrorEvent(aRequest, aResultCode);
+ }
+}
+
+NS_IMPL_ISUPPORTS(BackgroundCursorChild::DelayedDeleteRunnable,
+ nsIRunnable)
+
+NS_IMETHODIMP
+BackgroundCursorChild::
+DelayedDeleteRunnable::Run()
+{
+ MOZ_ASSERT(mActor);
+ mActor->AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+
+ mActor->SendDeleteMeInternal();
+
+ mActor = nullptr;
+ mRequest = nullptr;
+
+ return NS_OK;
+}
+
+} // namespace indexedDB
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/ActorsChild.h
@@ -0,0 +1,620 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_indexeddb_actorschild_h__
+#define mozilla_dom_indexeddb_actorschild_h__
+
+#include "js/RootingAPI.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBCursorChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBRequestChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBTransactionChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionChild.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+
+class nsIEventTarget;
+struct PRThread;
+
+namespace mozilla {
+namespace ipc {
+
+class BackgroundChildImpl;
+
+} // namespace ipc
+
+namespace dom {
+namespace indexedDB {
+
+class FileInfo;
+class IDBCursor;
+class IDBDatabase;
+class IDBFactory;
+class IDBMutableFile;
+class IDBOpenDBRequest;
+class IDBRequest;
+class IDBTransaction;
+class Key;
+class PBackgroundIDBFileChild;
+class PermissionRequestChild;
+class PermissionRequestParent;
+class SerializedStructuredCloneReadInfo;
+
+class BackgroundFactoryChild MOZ_FINAL
+ : public PBackgroundIDBFactoryChild
+{
+ friend class mozilla::ipc::BackgroundChildImpl;
+ friend class IDBFactory;
+
+ IDBFactory* mFactory;
+
+#ifdef DEBUG
+ nsCOMPtr<nsIEventTarget> mOwningThread;
+#endif
+
+public:
+ void
+ AssertIsOnOwningThread() const
+#ifdef DEBUG
+ ;
+#else
+ { }
+#endif
+
+ IDBFactory*
+ GetDOMObject() const
+ {
+ AssertIsOnOwningThread();
+ return mFactory;
+ }
+
+private:
+ // Only created by IDBFactory.
+ explicit BackgroundFactoryChild(IDBFactory* aFactory);
+
+ // Only destroyed by mozilla::ipc::BackgroundChildImpl.
+ ~BackgroundFactoryChild();
+
+ void
+ SendDeleteMeInternal();
+
+ // IPDL methods are only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBFactoryRequestChild*
+ AllocPBackgroundIDBFactoryRequestChild(const FactoryRequestParams& aParams)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBFactoryRequestChild(
+ PBackgroundIDBFactoryRequestChild* aActor)
+ MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBDatabaseChild*
+ AllocPBackgroundIDBDatabaseChild(const DatabaseSpec& aSpec,
+ PBackgroundIDBFactoryRequestChild* aRequest)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBDatabaseChild(PBackgroundIDBDatabaseChild* aActor)
+ MOZ_OVERRIDE;
+
+ bool
+ SendDeleteMe() MOZ_DELETE;
+};
+
+class BackgroundDatabaseChild;
+
+class BackgroundRequestChildBase
+{
+protected:
+ nsRefPtr<IDBRequest> mRequest;
+
+private:
+ bool mActorDestroyed;
+
+public:
+ void
+ AssertIsOnOwningThread() const
+#ifdef DEBUG
+ ;
+#else
+ { }
+#endif
+
+ IDBRequest*
+ GetDOMObject() const
+ {
+ AssertIsOnOwningThread();
+ return mRequest;
+ }
+
+ bool
+ IsActorDestroyed() const
+ {
+ AssertIsOnOwningThread();
+ return mActorDestroyed;
+ }
+
+protected:
+ explicit BackgroundRequestChildBase(IDBRequest* aRequest);
+
+ virtual
+ ~BackgroundRequestChildBase();
+
+ void
+ NoteActorDestroyed();
+};
+
+class BackgroundFactoryRequestChild MOZ_FINAL
+ : public BackgroundRequestChildBase
+ , public PBackgroundIDBFactoryRequestChild
+{
+ typedef mozilla::dom::quota::PersistenceType PersistenceType;
+
+ friend class IDBFactory;
+ friend class BackgroundFactoryChild;
+ friend class BackgroundDatabaseChild;
+ friend class PermissionRequestChild;
+ friend class PermissionRequestParent;
+
+ nsRefPtr<IDBFactory> mFactory;
+ const uint64_t mRequestedVersion;
+ const bool mIsDeleteOp;
+
+public:
+ IDBOpenDBRequest*
+ GetOpenDBRequest() const;
+
+private:
+ // Only created by IDBFactory.
+ BackgroundFactoryRequestChild(IDBFactory* aFactory,
+ IDBOpenDBRequest* aOpenRequest,
+ bool aIsDeleteOp,
+ uint64_t aRequestedVersion);
+
+ // Only destroyed by BackgroundFactoryChild.
+ ~BackgroundFactoryRequestChild();
+
+ bool
+ HandleResponse(nsresult aResponse);
+
+ bool
+ HandleResponse(const OpenDatabaseRequestResponse& aResponse);
+
+ bool
+ HandleResponse(const DeleteDatabaseRequestResponse& aResponse);
+
+ // IPDL methods are only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+ virtual bool
+ Recv__delete__(const FactoryRequestResponse& aResponse) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvPermissionChallenge(const PrincipalInfo& aPrincipalInfo) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvBlocked(const uint64_t& aCurrentVersion) MOZ_OVERRIDE;
+};
+
+class BackgroundDatabaseChild MOZ_FINAL
+ : public PBackgroundIDBDatabaseChild
+{
+ friend class BackgroundFactoryChild;
+ friend class BackgroundFactoryRequestChild;
+ friend class IDBDatabase;
+
+ nsAutoPtr<DatabaseSpec> mSpec;
+ nsRefPtr<IDBDatabase> mTemporaryStrongDatabase;
+ BackgroundFactoryRequestChild* mOpenRequestActor;
+ IDBDatabase* mDatabase;
+
+public:
+ void
+ AssertIsOnOwningThread() const
+ {
+ static_cast<BackgroundFactoryChild*>(Manager())->AssertIsOnOwningThread();
+ }
+
+ const DatabaseSpec*
+ Spec() const
+ {
+ AssertIsOnOwningThread();
+ return mSpec;
+ }
+
+ IDBDatabase*
+ GetDOMObject() const
+ {
+ AssertIsOnOwningThread();
+ return mDatabase;
+ }
+
+private:
+ // Only constructed by BackgroundFactoryChild.
+ BackgroundDatabaseChild(const DatabaseSpec& aSpec,
+ BackgroundFactoryRequestChild* aOpenRequest);
+
+ // Only destroyed by BackgroundFactoryChild.
+ ~BackgroundDatabaseChild();
+
+ void
+ SendDeleteMeInternal();
+
+ void
+ EnsureDOMObject();
+
+ void
+ ReleaseDOMObject();
+
+ // IPDL methods are only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBDatabaseFileChild*
+ AllocPBackgroundIDBDatabaseFileChild(PBlobChild* aBlobChild)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBDatabaseFileChild(
+ PBackgroundIDBDatabaseFileChild* aActor)
+ MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBTransactionChild*
+ AllocPBackgroundIDBTransactionChild(
+ const nsTArray<nsString>& aObjectStoreNames,
+ const Mode& aMode)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBTransactionChild(PBackgroundIDBTransactionChild* aActor)
+ MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBVersionChangeTransactionChild*
+ AllocPBackgroundIDBVersionChangeTransactionChild(
+ const uint64_t& aCurrentVersion,
+ const uint64_t& aRequestedVersion,
+ const int64_t& aNextObjectStoreId,
+ const int64_t& aNextIndexId)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ RecvPBackgroundIDBVersionChangeTransactionConstructor(
+ PBackgroundIDBVersionChangeTransactionChild* aActor,
+ const uint64_t& aCurrentVersion,
+ const uint64_t& aRequestedVersion,
+ const int64_t& aNextObjectStoreId,
+ const int64_t& aNextIndexId)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBVersionChangeTransactionChild(
+ PBackgroundIDBVersionChangeTransactionChild* aActor)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ RecvVersionChange(const uint64_t& aOldVersion,
+ const NullableVersion& aNewVersion)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ RecvInvalidate() MOZ_OVERRIDE;
+
+ bool
+ SendDeleteMe() MOZ_DELETE;
+};
+
+class BackgroundVersionChangeTransactionChild;
+
+class BackgroundTransactionBase
+{
+ friend class BackgroundVersionChangeTransactionChild;
+
+ // mTemporaryStrongTransaction is strong and is only valid until the end of
+ // NoteComplete() member function or until the NoteActorDestroyed() member
+ // function is called.
+ nsRefPtr<IDBTransaction> mTemporaryStrongTransaction;
+
+protected:
+ // mTransaction is weak and is valid until the NoteActorDestroyed() member
+ // function is called.
+ IDBTransaction* mTransaction;
+
+public:
+#ifdef DEBUG
+ virtual void
+ AssertIsOnOwningThread() const = 0;
+#else
+ void
+ AssertIsOnOwningThread() const
+ { }
+#endif
+
+ IDBTransaction*
+ GetDOMObject() const
+ {
+ AssertIsOnOwningThread();
+ return mTransaction;
+ }
+
+protected:
+ BackgroundTransactionBase();
+ explicit BackgroundTransactionBase(IDBTransaction* aTransaction);
+
+ virtual
+ ~BackgroundTransactionBase();
+
+ void
+ NoteActorDestroyed();
+
+ void
+ NoteComplete();
+
+private:
+ // Only called by BackgroundVersionChangeTransactionChild.
+ void
+ SetDOMTransaction(IDBTransaction* aDOMObject);
+};
+
+class BackgroundTransactionChild MOZ_FINAL
+ : public BackgroundTransactionBase
+ , public PBackgroundIDBTransactionChild
+{
+ friend class BackgroundDatabaseChild;
+ friend class IDBDatabase;
+
+public:
+#ifdef DEBUG
+ virtual void
+ AssertIsOnOwningThread() const MOZ_OVERRIDE;
+#endif
+
+ void
+ SendDeleteMeInternal();
+
+private:
+ // Only created by IDBDatabase.
+ explicit BackgroundTransactionChild(IDBTransaction* aTransaction);
+
+ // Only destroyed by BackgroundDatabaseChild.
+ ~BackgroundTransactionChild();
+
+ // IPDL methods are only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+ bool
+ RecvComplete(const nsresult& aResult) MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBRequestChild*
+ AllocPBackgroundIDBRequestChild(const RequestParams& aParams) MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor)
+ MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBCursorChild*
+ AllocPBackgroundIDBCursorChild(const OpenCursorParams& aParams) MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor)
+ MOZ_OVERRIDE;
+
+ bool
+ SendDeleteMe() MOZ_DELETE;
+};
+
+class BackgroundVersionChangeTransactionChild MOZ_FINAL
+ : public BackgroundTransactionBase
+ , public PBackgroundIDBVersionChangeTransactionChild
+{
+ friend class BackgroundDatabaseChild;
+
+ IDBOpenDBRequest* mOpenDBRequest;
+
+public:
+#ifdef DEBUG
+ virtual void
+ AssertIsOnOwningThread() const MOZ_OVERRIDE;
+#endif
+
+ void
+ SendDeleteMeInternal();
+
+private:
+ // Only created by BackgroundDatabaseChild.
+ explicit BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest);
+
+ // Only destroyed by BackgroundDatabaseChild.
+ ~BackgroundVersionChangeTransactionChild();
+
+ // Only called by BackgroundDatabaseChild.
+ void
+ SetDOMTransaction(IDBTransaction* aDOMObject)
+ {
+ BackgroundTransactionBase::SetDOMTransaction(aDOMObject);
+ }
+
+ // IPDL methods are only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+ bool
+ RecvComplete(const nsresult& aResult) MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBRequestChild*
+ AllocPBackgroundIDBRequestChild(const RequestParams& aParams) MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor)
+ MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBCursorChild*
+ AllocPBackgroundIDBCursorChild(const OpenCursorParams& aParams) MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor)
+ MOZ_OVERRIDE;
+
+ bool
+ SendDeleteMe() MOZ_DELETE;
+};
+
+class BackgroundRequestChild MOZ_FINAL
+ : public BackgroundRequestChildBase
+ , public PBackgroundIDBRequestChild
+{
+ friend class BackgroundTransactionChild;
+ friend class BackgroundVersionChangeTransactionChild;
+
+ nsRefPtr<IDBTransaction> mTransaction;
+ nsTArray<nsRefPtr<FileInfo>> mFileInfos;
+
+public:
+ explicit BackgroundRequestChild(IDBRequest* aRequest);
+
+ void
+ HoldFileInfosUntilComplete(nsTArray<nsRefPtr<FileInfo>>& aFileInfos);
+
+private:
+ // Only destroyed by BackgroundTransactionChild or
+ // BackgroundVersionChangeTransactionChild.
+ ~BackgroundRequestChild();
+
+ void
+ MaybeFinishTransactionEarly();
+
+ bool
+ HandleResponse(nsresult aResponse);
+
+ bool
+ HandleResponse(const Key& aResponse);
+
+ bool
+ HandleResponse(const nsTArray<Key>& aResponse);
+
+ bool
+ HandleResponse(const SerializedStructuredCloneReadInfo& aResponse);
+
+ bool
+ HandleResponse(const nsTArray<SerializedStructuredCloneReadInfo>& aResponse);
+
+ bool
+ HandleResponse(JS::Handle<JS::Value> aResponse);
+
+ bool
+ HandleResponse(uint64_t aResponse);
+
+ // IPDL methods are only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+ virtual bool
+ Recv__delete__(const RequestResponse& aResponse) MOZ_OVERRIDE;
+};
+
+class BackgroundCursorChild MOZ_FINAL
+ : public PBackgroundIDBCursorChild
+{
+ friend class BackgroundTransactionChild;
+ friend class BackgroundVersionChangeTransactionChild;
+
+ class DelayedDeleteRunnable;
+
+ IDBRequest* mRequest;
+ IDBTransaction* mTransaction;
+ IDBObjectStore* mObjectStore;
+ IDBIndex* mIndex;
+ IDBCursor* mCursor;
+
+ // These are only set while a request is in progress.
+ nsRefPtr<IDBRequest> mStrongRequest;
+ nsRefPtr<IDBCursor> mStrongCursor;
+
+ Direction mDirection;
+
+#ifdef DEBUG
+ PRThread* mOwningThread;
+#endif
+
+public:
+ BackgroundCursorChild(IDBRequest* aRequest,
+ IDBObjectStore* aObjectStore,
+ Direction aDirection);
+
+ BackgroundCursorChild(IDBRequest* aRequest,
+ IDBIndex* aIndex,
+ Direction aDirection);
+
+ void
+ AssertIsOnOwningThread() const
+#ifdef DEBUG
+ ;
+#else
+ { }
+#endif
+
+ void
+ SendContinueInternal(const CursorRequestParams& aParams);
+
+ void
+ SendDeleteMeInternal();
+
+private:
+ // Only destroyed by BackgroundTransactionChild or
+ // BackgroundVersionChangeTransactionChild.
+ ~BackgroundCursorChild();
+
+ void
+ HandleResponse(nsresult aResponse);
+
+ void
+ HandleResponse(const void_t& aResponse);
+
+ void
+ HandleResponse(const ObjectStoreCursorResponse& aResponse);
+
+ void
+ HandleResponse(const ObjectStoreKeyCursorResponse& aResponse);
+
+ void
+ HandleResponse(const IndexCursorResponse& aResponse);
+
+ void
+ HandleResponse(const IndexKeyCursorResponse& aResponse);
+
+ // IPDL methods are only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvResponse(const CursorResponse& aResponse) MOZ_OVERRIDE;
+
+ // Force callers to use SendContinueInternal.
+ bool
+ SendContinue(const CursorRequestParams& aParams) MOZ_DELETE;
+
+ bool
+ SendDeleteMe() MOZ_DELETE;
+};
+
+// XXX This doesn't belong here. However, we're not yet porting MutableFile
+// stuff to PBackground so this is necessary for the time being.
+void
+DispatchMutableFileResult(IDBRequest* aRequest,
+ nsresult aResultCode,
+ IDBMutableFile* aMutableFile);
+
+} // namespace indexedDB
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_indexeddb_actorschild_h__
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -0,0 +1,16878 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ActorsParent.h"
+
+#include <algorithm>
+#include "CheckQuotaHelper.h"
+#include "FileInfo.h"
+#include "FileManager.h"
+#include "IDBObjectStore.h"
+#include "IDBTransaction.h"
+#include "IndexedDatabase.h"
+#include "IndexedDatabaseInlines.h"
+#include "IndexedDatabaseManager.h"
+#include "js/StructuredClone.h"
+#include "js/Value.h"
+#include "jsapi.h"
+#include "KeyPath.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/AppProcessChecker.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/Endian.h"
+#include "mozilla/LazyIdleThread.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/storage.h"
+#include "mozilla/unused.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/StructuredCloneTags.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h"
+#include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestParent.h"
+#include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/quota/Client.h"
+#include "mozilla/dom/quota/FileStreams.h"
+#include "mozilla/dom/quota/OriginOrPatternString.h"
+#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/dom/quota/StoragePrivilege.h"
+#include "mozilla/dom/quota/UsageInfo.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/InputStreamParams.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/PBackground.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsClassHashtable.h"
+#include "nsCOMPtr.h"
+#include "nsDataHashtable.h"
+#include "nsDOMFile.h"
+#include "nsEscape.h"
+#include "nsHashKeys.h"
+#include "nsNetUtil.h"
+#include "nsIAppsService.h"
+#include "nsIDOMFile.h"
+#include "nsIEventTarget.h"
+#include "nsIFile.h"
+#include "nsIFileURL.h"
+#include "nsIInputStream.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsInterfaceHashtable.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIOfflineStorage.h"
+#include "nsIOutputStream.h"
+#include "nsIPrincipal.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsISupports.h"
+#include "nsISupportsImpl.h"
+#include "nsISupportsPriority.h"
+#include "nsIURI.h"
+#include "nsNetUtil.h"
+#include "nsPrintfCString.h"
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOMCID.h"
+#include "PermissionRequestBase.h"
+#include "ProfilerHelpers.h"
+#include "ReportInternalError.h"
+#include "snappy/snappy.h"
+#include "TransactionThreadPool.h"
+
+namespace mozilla {
+namespace dom {
+namespace indexedDB {
+
+using namespace mozilla::dom::quota;
+using namespace mozilla::ipc;
+
+#define DISABLE_ASSERTS_FOR_FUZZING 0
+
+#if DISABLE_ASSERTS_FOR_FUZZING
+#define ASSERT_UNLESS_FUZZING(...) do { } while (0)
+#else
+#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
+#endif
+
+namespace {
+
+class Cursor;
+class Database;
+struct DatabaseActorInfo;
+class DatabaseFile;
+class DatabaseOfflineStorage;
+class Factory;
+class OpenDatabaseOp;
+class TransactionBase;
+class VersionChangeTransaction;
+
+/*******************************************************************************
+ * Constants
+ ******************************************************************************/
+
+// If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
+// schema version.
+static_assert(JS_STRUCTURED_CLONE_VERSION == 5,
+ "Need to update the major schema version.");
+
+// Major schema version. Bump for almost everything.
+const uint32_t kMajorSchemaVersion = 17;
+
+// Minor schema version. Should almost always be 0 (maybe bump on release
+// branches if we have to).
+const uint32_t kMinorSchemaVersion = 0;
+
+// The schema version we store in the SQLite database is a (signed) 32-bit
+// integer. The major version is left-shifted 4 bits so the max value is
+// 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
+static_assert(kMajorSchemaVersion <= 0xFFFFFFF,
+ "Major version needs to fit in 28 bits.");
+static_assert(kMinorSchemaVersion <= 0xF,
+ "Minor version needs to fit in 4 bits.");
+
+const int32_t kSQLiteSchemaVersion =
+ int32_t((kMajorSchemaVersion << 4) + kMinorSchemaVersion);
+
+const int32_t kStorageProgressGranularity = 1000;
+
+const char kSavepointClause[] = "SAVEPOINT sp;";
+
+const fallible_t fallible = fallible_t();
+
+const uint32_t kFileCopyBufferSize = 32768;
+
+const char kJournalDirectoryName[] = "journals";
+
+const char kPrefIndexedDBEnabled[] = "dom.indexedDB.enabled";
+
+#define IDB_PREFIX "indexedDB"
+
+#ifdef MOZ_CHILD_PERMISSIONS
+const char kPermissionString[] = IDB_PREFIX;
+#endif // MOZ_CHILD_PERMISSIONS
+
+const char kPermissionStringChromeBase[] = IDB_PREFIX "-chrome-";
+const char kPermissionStringChromeReadSuffix[] = "-read";
+const char kPermissionStringChromeWriteSuffix[] = "-write";
+
+#undef IDB_PREFIX
+
+enum AppId {
+ kNoAppId = nsIScriptSecurityManager::NO_APP_ID,
+ kUnknownAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID
+};
+
+#ifdef DEBUG
+
+const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL;
+const uint32_t kDEBUGThreadSleepMS = 0;
+
+#endif
+
+/*******************************************************************************
+ * Metadata classes
+ ******************************************************************************/
+
+struct FullIndexMetadata
+{
+ IndexMetadata mCommonMetadata;
+
+ bool mDeleted;
+
+public:
+ FullIndexMetadata()
+ : mCommonMetadata(0, nsString(), KeyPath(0), false, false)
+ , mDeleted(false)
+ {
+ // This can happen either on the QuotaManager IO thread or on a
+ // versionchange transaction thread. These threads can never race so this is
+ // totally safe.
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullIndexMetadata)
+
+private:
+ ~FullIndexMetadata()
+ { }
+};
+
+typedef nsRefPtrHashtable<nsUint64HashKey, FullIndexMetadata> IndexTable;
+
+struct FullObjectStoreMetadata
+{
+ ObjectStoreMetadata mCommonMetadata;
+ IndexTable mIndexes;
+
+ // These two members are only ever touched on a transaction thread!
+ int64_t mNextAutoIncrementId;
+ int64_t mComittedAutoIncrementId;
+
+ bool mDeleted;
+
+public:
+ FullObjectStoreMetadata()
+ : mCommonMetadata(0, nsString(), KeyPath(0), false)
+ , mNextAutoIncrementId(0)
+ , mComittedAutoIncrementId(0)
+ , mDeleted(false)
+ {
+ // This can happen either on the QuotaManager IO thread or on a
+ // versionchange transaction thread. These threads can never race so this is
+ // totally safe.
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullObjectStoreMetadata);
+
+private:
+ ~FullObjectStoreMetadata()
+ { }
+};
+
+typedef nsRefPtrHashtable<nsUint64HashKey, FullObjectStoreMetadata>
+ ObjectStoreTable;
+
+struct FullDatabaseMetadata
+{
+ DatabaseMetadata mCommonMetadata;
+ nsCString mDatabaseId;
+ nsString mFilePath;
+ ObjectStoreTable mObjectStores;
+
+ int64_t mNextObjectStoreId;
+ int64_t mNextIndexId;
+
+public:
+ explicit FullDatabaseMetadata(const DatabaseMetadata& aCommonMetadata)
+ : mCommonMetadata(aCommonMetadata)
+ , mNextObjectStoreId(0)
+ , mNextIndexId(0)
+ {
+ AssertIsOnBackgroundThread();
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullDatabaseMetadata)
+
+ already_AddRefed<FullDatabaseMetadata>
+ Duplicate() const;
+
+private:
+ ~FullDatabaseMetadata()
+ { }
+};
+
+template <class MetadataType>
+class MOZ_STACK_CLASS MetadataNameOrIdMatcher MOZ_FINAL
+{
+ typedef MetadataNameOrIdMatcher<MetadataType> SelfType;
+
+ const int64_t mId;
+ const nsString mName;
+ nsRefPtr<MetadataType> mMetadata;
+ bool mCheckName;
+
+public:
+ template <class Enumerable>
+ static MetadataType*
+ Match(const Enumerable& aEnumerable, uint64_t aId, const nsAString& aName)
+ {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aId);
+
+ SelfType closure(aId, aName);
+ aEnumerable.EnumerateRead(Enumerate, &closure);
+
+ return closure.mMetadata;
+ }
+
+ template <class Enumerable>
+ static MetadataType*
+ Match(const Enumerable& aEnumerable, uint64_t aId)
+ {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aId);
+
+ SelfType closure(aId);
+ aEnumerable.EnumerateRead(Enumerate, &closure);
+
+ return closure.mMetadata;
+ }
+
+private:
+ MetadataNameOrIdMatcher(const int64_t& aId, const nsAString& aName)
+ : mId(aId)
+ , mName(PromiseFlatString(aName))
+ , mMetadata(nullptr)
+ , mCheckName(true)
+ {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aId);
+ }
+
+ explicit MetadataNameOrIdMatcher(const int64_t& aId)
+ : mId(aId)
+ , mMetadata(nullptr)
+ , mCheckName(false)
+ {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aId);
+ }
+
+ static PLDHashOperator
+ Enumerate(const uint64_t& aKey, MetadataType* aValue, void* aClosure)
+ {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aKey);
+ MOZ_ASSERT(aValue);
+ MOZ_ASSERT(aClosure);
+
+ auto* closure = static_cast<SelfType*>(aClosure);
+
+ if (!aValue->mDeleted &&
+ (closure->mId == aValue->mCommonMetadata.id() ||
+ (closure->mCheckName &&
+ closure->mName == aValue->mCommonMetadata.name()))) {
+ closure->mMetadata = aValue;
+ return PL_DHASH_STOP;
+ }
+
+ return PL_DHASH_NEXT;
+ }
+};
+
+/*******************************************************************************
+ * SQLite functions
+ ******************************************************************************/
+
+int32_t
+MakeSchemaVersion(uint32_t aMajorSchemaVersion,
+ uint32_t aMinorSchemaVersion)
+{
+ return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion);
+}
+
+uint32_t
+HashName(const nsAString& aName)
+{
+ struct Helper
+ {
+ static uint32_t
+ RotateBitsLeft32(uint32_t aValue, uint8_t aBits)
+ {
+ MOZ_ASSERT(aBits < 32);
+ return (aValue << aBits) | (aValue >> (32 - aBits));
+ }
+ };
+
+ static const uint32_t kGoldenRatioU32 = 0x9e3779b9u;
+
+ const char16_t* str = aName.BeginReading();
+ size_t length = aName.Length();
+
+ uint32_t hash = 0;
+ for (size_t i = 0; i < length; i++) {
+ hash = kGoldenRatioU32 * (Helper::RotateBitsLeft32(hash, 5) ^ str[i]);
+ }
+
+ return hash;
+}
+
+nsresult
+ClampResultCode(nsresult aResultCode)
+{
+ if (NS_SUCCEEDED(aResultCode) ||
+ NS_ERROR_GET_MODULE(aResultCode) == NS_ERROR_MODULE_DOM_INDEXEDDB) {
+ return aResultCode;
+ }
+
+ switch (aResultCode) {
+ case NS_ERROR_FILE_NO_DEVICE_SPACE:
+ return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
+ case NS_ERROR_STORAGE_CONSTRAINT:
+ return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
+ default:
+#ifdef DEBUG
+ nsPrintfCString message("Converting non-IndexedDB error code (0x%X) to "
+ "NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR",
+ aResultCode);
+ NS_WARNING(message.get());
+#else
+ ;
+#endif
+ }
+
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+}
+
+void
+GetDatabaseFilename(const nsAString& aName,
+ nsAutoString& aDatabaseFilename)
+{
+ MOZ_ASSERT(aDatabaseFilename.IsEmpty());
+
+ aDatabaseFilename.AppendInt(HashName(aName));
+
+ nsCString escapedName;
+ if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) {
+ MOZ_CRASH("Can't escape database name!");
+ }
+
+ const char* forwardIter = escapedName.BeginReading();
+ const char* backwardIter = escapedName.EndReading() - 1;
+
+ nsAutoCString substring;
+ while (forwardIter <= backwardIter && substring.Length() < 21) {
+ if (substring.Length() % 2) {
+ substring.Append(*backwardIter--);
+ } else {
+ substring.Append(*forwardIter++);
+ }
+ }
+
+ aDatabaseFilename.AppendASCII(substring.get(), substring.Length());
+}
+
+nsresult
+CreateFileTables(mozIStorageConnection* aConnection)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aConnection);
+
+ PROFILER_LABEL("IndexedDB",
+ "CreateFileTables",
+ js::ProfileEntry::Category::STORAGE);
+
+ // Table `file`
+ nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE file ("
+ "id INTEGER PRIMARY KEY, "
+ "refcount INTEGER NOT NULL"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TRIGGER object_data_insert_trigger "
+ "AFTER INSERT ON object_data "
+ "FOR EACH ROW "
+ "WHEN NEW.file_ids IS NOT NULL "
+ "BEGIN "
+ "SELECT update_refcount(NULL, NEW.file_ids); "
+ "END;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TRIGGER object_data_update_trigger "
+ "AFTER UPDATE OF file_ids ON object_data "
+ "FOR EACH ROW "
+ "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
+ "BEGIN "
+ "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
+ "END;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TRIGGER object_data_delete_trigger "
+ "AFTER DELETE ON object_data "
+ "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
+ "BEGIN "
+ "SELECT update_refcount(OLD.file_ids, NULL); "
+ "END;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TRIGGER file_update_trigger "
+ "AFTER UPDATE ON file "
+ "FOR EACH ROW WHEN NEW.refcount = 0 "
+ "BEGIN "
+ "DELETE FROM file WHERE id = OLD.id; "
+ "END;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+CreateTables(mozIStorageConnection* aConnection)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aConnection);
+
+ PROFILER_LABEL("IndexedDB",
+ "CreateTables",
+ js::ProfileEntry::Category::STORAGE);
+
+ // Table `database`
+ nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE database ("
+ "name TEXT NOT NULL, "
+ "version INTEGER NOT NULL DEFAULT 0"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Table `object_store`
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE object_store ("
+ "id INTEGER PRIMARY KEY, "
+ "auto_increment INTEGER NOT NULL DEFAULT 0, "
+ "name TEXT NOT NULL, "
+ "key_path TEXT, "
+ "UNIQUE (name)"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Table `object_data`
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE object_data ("
+ "id INTEGER PRIMARY KEY, "
+ "object_store_id INTEGER NOT NULL, "
+ "key_value BLOB DEFAULT NULL, "
+ "file_ids TEXT, "
+ "data BLOB NOT NULL, "
+ "UNIQUE (object_store_id, key_value), "
+ "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Table `index`
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE object_store_index ("
+ "id INTEGER PRIMARY KEY, "
+ "object_store_id INTEGER NOT NULL, "
+ "name TEXT NOT NULL, "
+ "key_path TEXT NOT NULL, "
+ "unique_index INTEGER NOT NULL, "
+ "multientry INTEGER NOT NULL, "
+ "UNIQUE (object_store_id, name), "
+ "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Table `index_data`
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE index_data ("
+ "index_id INTEGER NOT NULL, "
+ "value BLOB NOT NULL, "
+ "object_data_key BLOB NOT NULL, "
+ "object_data_id INTEGER NOT NULL, "
+ "PRIMARY KEY (index_id, value, object_data_key), "
+ "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+ "CASCADE, "
+ "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Need this to make cascading deletes from object_data and object_store fast.
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE INDEX index_data_object_data_id_index "
+ "ON index_data (object_data_id);"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Table `unique_index_data`
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE unique_index_data ("
+ "index_id INTEGER NOT NULL, "
+ "value BLOB NOT NULL, "
+ "object_data_key BLOB NOT NULL, "
+ "object_data_id INTEGER NOT NULL, "
+ "PRIMARY KEY (index_id, value, object_data_key), "
+ "UNIQUE (index_id, value), "
+ "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+ "CASCADE "
+ "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Need this to make cascading deletes from object_data and object_store fast.
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE INDEX unique_index_data_object_data_id_index "
+ "ON unique_index_data (object_data_id);"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = CreateFileTables(aConnection);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->SetSchemaVersion(kSQLiteSchemaVersion);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aConnection);
+
+ PROFILER_LABEL("IndexedDB",
+ "UpgradeSchemaFrom4To5",
+ js::ProfileEntry::Category::STORAGE);
+
+ nsresult rv;
+
+ // All we changed is the type of the version column, so lets try to
+ // convert that to an integer, and if we fail, set it to 0.
+ nsCOMPtr<mozIStorageStatement> stmt;
+ rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
+ "SELECT name, version, dataVersion "
+ "FROM database"
+ ), getter_AddRefs(stmt));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsString name;
+ int32_t intVersion;
+ int64_t dataVersion;
+
+ {
+ mozStorageStatementScoper scoper(stmt);
+
+ bool hasResults;
+ rv = stmt->ExecuteStep(&hasResults);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ if (NS_WARN_IF(!hasResults)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsString version;
+ rv = stmt->GetString(1, version);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ intVersion = version.ToInteger(&rv);
+ if (NS_FAILED(rv)) {
+ intVersion = 0;
+ }
+
+ rv = stmt->GetString(0, name);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = stmt->GetInt64(2, &dataVersion);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE database"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE database ("
+ "name TEXT NOT NULL, "
+ "version INTEGER NOT NULL DEFAULT 0, "
+ "dataVersion INTEGER NOT NULL"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
+ "INSERT INTO database (name, version, dataVersion) "
+ "VALUES (:name, :version, :dataVersion)"
+ ), getter_AddRefs(stmt));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ {
+ mozStorageStatementScoper scoper(stmt);
+
+ rv = stmt->BindStringParameter(0, name);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = stmt->BindInt32Parameter(1, intVersion);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = stmt->BindInt64Parameter(2, dataVersion);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = stmt->Execute();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ rv = aConnection->SetSchemaVersion(5);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aConnection);
+
+ PROFILER_LABEL("IndexedDB",
+ "UpgradeSchemaFrom5To6",
+ js::ProfileEntry::Category::STORAGE);
+
+ // First, drop all the indexes we're no longer going to use.
+ nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP INDEX key_index;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP INDEX ai_key_index;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP INDEX value_index;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP INDEX ai_value_index;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Now, reorder the columns of object_data to put the blob data last. We do
+ // this by copying into a temporary table, dropping the original, then copying
+ // back into a newly created table.
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TEMPORARY TABLE temp_upgrade ("
+ "id INTEGER PRIMARY KEY, "
+ "object_store_id, "
+ "key_value, "
+ "data "
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO temp_upgrade "
+ "SELECT id, object_store_id, key_value, data "
+ "FROM object_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE object_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE object_data ("
+ "id INTEGER PRIMARY KEY, "
+ "object_store_id INTEGER NOT NULL, "
+ "key_value DEFAULT NULL, "
+ "data BLOB NOT NULL, "
+ "UNIQUE (object_store_id, key_value), "
+ "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO object_data "
+ "SELECT id, object_store_id, key_value, data "
+ "FROM temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // We need to add a unique constraint to our ai_object_data table. Copy all
+ // the data out of it using a temporary table as before.
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TEMPORARY TABLE temp_upgrade ("
+ "id INTEGER PRIMARY KEY, "
+ "object_store_id, "
+ "data "
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO temp_upgrade "
+ "SELECT id, object_store_id, data "
+ "FROM ai_object_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE ai_object_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE ai_object_data ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "object_store_id INTEGER NOT NULL, "
+ "data BLOB NOT NULL, "
+ "UNIQUE (object_store_id, id), "
+ "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO ai_object_data "
+ "SELECT id, object_store_id, data "
+ "FROM temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Fix up the index_data table. We're reordering the columns as well as
+ // changing the primary key from being a simple id to being a composite.
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TEMPORARY TABLE temp_upgrade ("
+ "index_id, "
+ "value, "
+ "object_data_key, "
+ "object_data_id "
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO temp_upgrade "
+ "SELECT index_id, value, object_data_key, object_data_id "
+ "FROM index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE index_data ("
+ "index_id INTEGER NOT NULL, "
+ "value NOT NULL, "
+ "object_data_key NOT NULL, "
+ "object_data_id INTEGER NOT NULL, "
+ "PRIMARY KEY (index_id, value, object_data_key), "
+ "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+ "CASCADE, "
+ "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT OR IGNORE INTO index_data "
+ "SELECT index_id, value, object_data_key, object_data_id "
+ "FROM temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE INDEX index_data_object_data_id_index "
+ "ON index_data (object_data_id);"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Fix up the unique_index_data table. We're reordering the columns as well as
+ // changing the primary key from being a simple id to being a composite.
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TEMPORARY TABLE temp_upgrade ("
+ "index_id, "
+ "value, "
+ "object_data_key, "
+ "object_data_id "
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO temp_upgrade "
+ "SELECT index_id, value, object_data_key, object_data_id "
+ "FROM unique_index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE unique_index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE unique_index_data ("
+ "index_id INTEGER NOT NULL, "
+ "value NOT NULL, "
+ "object_data_key NOT NULL, "
+ "object_data_id INTEGER NOT NULL, "
+ "PRIMARY KEY (index_id, value, object_data_key), "
+ "UNIQUE (index_id, value), "
+ "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+ "CASCADE "
+ "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO unique_index_data "
+ "SELECT index_id, value, object_data_key, object_data_id "
+ "FROM temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE INDEX unique_index_data_object_data_id_index "
+ "ON unique_index_data (object_data_id);"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Fix up the ai_index_data table. We're reordering the columns as well as
+ // changing the primary key from being a simple id to being a composite.
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TEMPORARY TABLE temp_upgrade ("
+ "index_id, "
+ "value, "
+ "ai_object_data_id "
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO temp_upgrade "
+ "SELECT index_id, value, ai_object_data_id "
+ "FROM ai_index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE ai_index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE ai_index_data ("
+ "index_id INTEGER NOT NULL, "
+ "value NOT NULL, "
+ "ai_object_data_id INTEGER NOT NULL, "
+ "PRIMARY KEY (index_id, value, ai_object_data_id), "
+ "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+ "CASCADE, "
+ "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT OR IGNORE INTO ai_index_data "
+ "SELECT index_id, value, ai_object_data_id "
+ "FROM temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE INDEX ai_index_data_ai_object_data_id_index "
+ "ON ai_index_data (ai_object_data_id);"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Fix up the ai_unique_index_data table. We're reordering the columns as well
+ // as changing the primary key from being a simple id to being a composite.
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TEMPORARY TABLE temp_upgrade ("
+ "index_id, "
+ "value, "
+ "ai_object_data_id "
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO temp_upgrade "
+ "SELECT index_id, value, ai_object_data_id "
+ "FROM ai_unique_index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE ai_unique_index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE ai_unique_index_data ("
+ "index_id INTEGER NOT NULL, "
+ "value NOT NULL, "
+ "ai_object_data_id INTEGER NOT NULL, "
+ "UNIQUE (index_id, value), "
+ "PRIMARY KEY (index_id, value, ai_object_data_id), "
+ "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+ "CASCADE, "
+ "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO ai_unique_index_data "
+ "SELECT index_id, value, ai_object_data_id "
+ "FROM temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE INDEX ai_unique_index_data_ai_object_data_id_index "
+ "ON ai_unique_index_data (ai_object_data_id);"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->SetSchemaVersion(6);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aConnection);
+
+ PROFILER_LABEL("IndexedDB",
+ "UpgradeSchemaFrom6To7",
+ js::ProfileEntry::Category::STORAGE);
+
+ nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TEMPORARY TABLE temp_upgrade ("
+ "id, "
+ "name, "
+ "key_path, "
+ "auto_increment"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO temp_upgrade "
+ "SELECT id, name, key_path, auto_increment "
+ "FROM object_store;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE object_store;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE object_store ("
+ "id INTEGER PRIMARY KEY, "
+ "auto_increment INTEGER NOT NULL DEFAULT 0, "
+ "name TEXT NOT NULL, "
+ "key_path TEXT, "
+ "UNIQUE (name)"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO object_store "
+ "SELECT id, auto_increment, name, nullif(key_path, '') "
+ "FROM temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->SetSchemaVersion(7);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aConnection);
+
+ PROFILER_LABEL("IndexedDB",
+ "UpgradeSchemaFrom7To8",
+ js::ProfileEntry::Category::STORAGE);
+
+ nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TEMPORARY TABLE temp_upgrade ("
+ "id, "
+ "object_store_id, "
+ "name, "
+ "key_path, "
+ "unique_index, "
+ "object_store_autoincrement"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO temp_upgrade "
+ "SELECT id, object_store_id, name, key_path, "
+ "unique_index, object_store_autoincrement "
+ "FROM object_store_index;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE object_store_index;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE object_store_index ("
+ "id INTEGER, "
+ "object_store_id INTEGER NOT NULL, "
+ "name TEXT NOT NULL, "
+ "key_path TEXT NOT NULL, "
+ "unique_index INTEGER NOT NULL, "
+ "multientry INTEGER NOT NULL, "
+ "object_store_autoincrement INTERGER NOT NULL, "
+ "PRIMARY KEY (id), "
+ "UNIQUE (object_store_id, name), "
+ "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO object_store_index "
+ "SELECT id, object_store_id, name, key_path, "
+ "unique_index, 0, object_store_autoincrement "
+ "FROM temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->SetSchemaVersion(8);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+class CompressDataBlobsFunction MOZ_FINAL
+ : public mozIStorageFunction
+{
+public:
+ NS_DECL_ISUPPORTS
+
+private:
+ ~CompressDataBlobsFunction()
+ { }
+
+ NS_IMETHOD
+ OnFunctionCall(mozIStorageValueArray* aArguments,
+ nsIVariant** aResult) MOZ_OVERRIDE
+ {
+ MOZ_ASSERT(aArguments);
+ MOZ_ASSERT(aResult);
+
+ PROFILER_LABEL("IndexedDB",
+ "CompressDataBlobsFunction::OnFunctionCall",
+ js::ProfileEntry::Category::STORAGE);
+
+ uint32_t argc;
+ nsresult rv = aArguments->GetNumEntries(&argc);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (argc != 1) {
+ NS_WARNING("Don't call me with the wrong number of arguments!");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ int32_t type;
+ rv = aArguments->GetTypeOfIndex(0, &type);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
+ NS_WARNING("Don't call me with the wrong type of arguments!");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ const uint8_t* uncompressed;
+ uint32_t uncompressedLength;
+ rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
+ nsAutoArrayPtr<char> compressed(new (fallible) char[compressedLength]);
+ if (NS_WARN_IF(!compressed)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ snappy::RawCompress(reinterpret_cast<const char*>(uncompressed),
+ uncompressedLength, compressed.get(),
+ &compressedLength);
+
+ std::pair<const void *, int> data(static_cast<void*>(compressed.get()),
+ int(compressedLength));
+
+ // XXX This copies the buffer again... There doesn't appear to be any way to
+ // preallocate space and write directly to a BlobVariant at the moment.
+ nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
+
+ result.forget(aResult);
+ return NS_OK;
+ }
+};
+
+nsresult
+UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aConnection);
+
+ PROFILER_LABEL("IndexedDB",
+ "UpgradeSchemaFrom8To9_0",
+ js::ProfileEntry::Category::STORAGE);
+
+ // We no longer use the dataVersion column.
+ nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "UPDATE database SET dataVersion = 0;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<mozIStorageFunction> compressor = new CompressDataBlobsFunction();
+
+ NS_NAMED_LITERAL_CSTRING(compressorName, "compress");
+
+ rv = aConnection->CreateFunction(compressorName, 1, compressor);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Turn off foreign key constraints before we do anything here.
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "UPDATE object_data SET data = compress(data);"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "UPDATE ai_object_data SET data = compress(data);"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->RemoveFunction(compressorName);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aConnection);
+
+ PROFILER_LABEL("IndexedDB",
+ "UpgradeSchemaFrom9_0To10_0",
+ js::ProfileEntry::Category::STORAGE);
+
+ nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "ALTER TABLE object_data ADD COLUMN file_ids TEXT;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = CreateFileTables(aConnection);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aConnection);
+
+ PROFILER_LABEL("IndexedDB",
+ "UpgradeSchemaFrom10_0To11_0",
+ js::ProfileEntry::Category::STORAGE);
+
+ nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TEMPORARY TABLE temp_upgrade ("
+ "id, "
+ "object_store_id, "
+ "name, "
+ "key_path, "
+ "unique_index, "
+ "multientry"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO temp_upgrade "
+ "SELECT id, object_store_id, name, key_path, "
+ "unique_index, multientry "
+ "FROM object_store_index;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE object_store_index;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE object_store_index ("
+ "id INTEGER PRIMARY KEY, "
+ "object_store_id INTEGER NOT NULL, "
+ "name TEXT NOT NULL, "
+ "key_path TEXT NOT NULL, "
+ "unique_index INTEGER NOT NULL, "
+ "multientry INTEGER NOT NULL, "
+ "UNIQUE (object_store_id, name), "
+ "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO object_store_index "
+ "SELECT id, object_store_id, name, key_path, "
+ "unique_index, multientry "
+ "FROM temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TRIGGER object_data_insert_trigger;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
+ "SELECT object_store_id, id, data, file_ids "
+ "FROM ai_object_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TRIGGER object_data_insert_trigger "
+ "AFTER INSERT ON object_data "
+ "FOR EACH ROW "
+ "WHEN NEW.file_ids IS NOT NULL "
+ "BEGIN "
+ "SELECT update_refcount(NULL, NEW.file_ids); "
+ "END;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) "
+ "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id "
+ "FROM ai_index_data "
+ "INNER JOIN object_store_index ON "
+ "object_store_index.id = ai_index_data.index_id "
+ "INNER JOIN object_data ON "
+ "object_data.object_store_id = object_store_index.object_store_id AND "
+ "object_data.key_value = ai_index_data.ai_object_data_id;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) "
+ "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id "
+ "FROM ai_unique_index_data "
+ "INNER JOIN object_store_index ON "
+ "object_store_index.id = ai_unique_index_data.index_id "
+ "INNER JOIN object_data ON "
+ "object_data.object_store_id = object_store_index.object_store_id AND "
+ "object_data.key_value = ai_unique_index_data.ai_object_data_id;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "UPDATE object_store "
+ "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 "
+ "WHERE auto_increment;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE ai_unique_index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE ai_index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE ai_object_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+class EncodeKeysFunction MOZ_FINAL
+ : public mozIStorageFunction
+{
+public:
+ NS_DECL_ISUPPORTS
+
+private:
+ ~EncodeKeysFunction()
+ { }
+
+ NS_IMETHOD
+ OnFunctionCall(mozIStorageValueArray* aArguments,
+ nsIVariant** aResult) MOZ_OVERRIDE
+ {
+ MOZ_ASSERT(aArguments);
+ MOZ_ASSERT(aResult);
+
+ PROFILER_LABEL("IndexedDB",
+ "EncodeKeysFunction::OnFunctionCall",
+ js::ProfileEntry::Category::STORAGE);
+
+ uint32_t argc;
+ nsresult rv = aArguments->GetNumEntries(&argc);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (argc != 1) {
+ NS_WARNING("Don't call me with the wrong number of arguments!");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ int32_t type;
+ rv = aArguments->GetTypeOfIndex(0, &type);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ Key key;
+ if (type == mozIStorageStatement::VALUE_TYPE_INTEGER) {
+ int64_t intKey;
+ aArguments->GetInt64(0, &intKey);
+ key.SetFromInteger(intKey);
+ } else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) {
+ nsString stringKey;
+ aArguments->GetString(0, stringKey);
+ key.SetFromString(stringKey);
+ } else {
+ NS_WARNING("Don't call me with the wrong type of arguments!");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ const nsCString& buffer = key.GetBuffer();
+
+ std::pair<const void *, int> data(static_cast<const void*>(buffer.get()),
+ int(buffer.Length()));
+
+ nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
+
+ result.forget(aResult);
+ return NS_OK;
+ }
+};
+
+nsresult
+UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aConnection);
+
+ PROFILER_LABEL("IndexedDB",
+ "UpgradeSchemaFrom11_0To12_0",
+ js::ProfileEntry::Category::STORAGE);
+
+ NS_NAMED_LITERAL_CSTRING(encoderName, "encode");
+
+ nsCOMPtr<mozIStorageFunction> encoder = new EncodeKeysFunction();
+
+ nsresult rv = aConnection->CreateFunction(encoderName, 1, encoder);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TEMPORARY TABLE temp_upgrade ("
+ "id INTEGER PRIMARY KEY, "
+ "object_store_id, "
+ "key_value, "
+ "data, "
+ "file_ids "
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO temp_upgrade "
+ "SELECT id, object_store_id, encode(key_value), data, file_ids "
+ "FROM object_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE object_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE object_data ("
+ "id INTEGER PRIMARY KEY, "
+ "object_store_id INTEGER NOT NULL, "
+ "key_value BLOB DEFAULT NULL, "
+ "file_ids TEXT, "
+ "data BLOB NOT NULL, "
+ "UNIQUE (object_store_id, key_value), "
+ "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO object_data "
+ "SELECT id, object_store_id, key_value, file_ids, data "
+ "FROM temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TRIGGER object_data_insert_trigger "
+ "AFTER INSERT ON object_data "
+ "FOR EACH ROW "
+ "WHEN NEW.file_ids IS NOT NULL "
+ "BEGIN "
+ "SELECT update_refcount(NULL, NEW.file_ids); "
+ "END;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TRIGGER object_data_update_trigger "
+ "AFTER UPDATE OF file_ids ON object_data "
+ "FOR EACH ROW "
+ "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
+ "BEGIN "
+ "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
+ "END;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TRIGGER object_data_delete_trigger "
+ "AFTER DELETE ON object_data "
+ "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
+ "BEGIN "
+ "SELECT update_refcount(OLD.file_ids, NULL); "
+ "END;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TEMPORARY TABLE temp_upgrade ("
+ "index_id, "
+ "value, "
+ "object_data_key, "
+ "object_data_id "
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO temp_upgrade "
+ "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
+ "FROM index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE index_data ("
+ "index_id INTEGER NOT NULL, "
+ "value BLOB NOT NULL, "
+ "object_data_key BLOB NOT NULL, "
+ "object_data_id INTEGER NOT NULL, "
+ "PRIMARY KEY (index_id, value, object_data_key), "
+ "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+ "CASCADE, "
+ "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO index_data "
+ "SELECT index_id, value, object_data_key, object_data_id "
+ "FROM temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE INDEX index_data_object_data_id_index "
+ "ON index_data (object_data_id);"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TEMPORARY TABLE temp_upgrade ("
+ "index_id, "
+ "value, "
+ "object_data_key, "
+ "object_data_id "
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO temp_upgrade "
+ "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
+ "FROM unique_index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE unique_index_data;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE TABLE unique_index_data ("
+ "index_id INTEGER NOT NULL, "
+ "value BLOB NOT NULL, "
+ "object_data_key BLOB NOT NULL, "
+ "object_data_id INTEGER NOT NULL, "
+ "PRIMARY KEY (index_id, value, object_data_key), "
+ "UNIQUE (index_id, value), "
+ "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+ "CASCADE "
+ "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
+ "CASCADE"
+ ");"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "INSERT INTO unique_index_data "
+ "SELECT index_id, value, object_data_key, object_data_id "
+ "FROM temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "DROP TABLE temp_upgrade;"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ "CREATE INDEX unique_index_data_object_data_id_index "
+ "ON unique_index_data (object_data_id);"
+ ));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->RemoveFunction(encoderName);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aConnection->SetSchemaVersion(MakeSchemaVersion(12, 0));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection,
+ bool* aVacuumNeeded)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aConnection);
+
+ PROFILER_LABEL("IndexedDB",
+ "UpgradeSchemaFrom12_0To13_0",
+ js::ProfileEntry::Category::STORAGE);
+
+ nsresult rv;
+
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+ int32_t defaultPageSize;
+ rv = aConnection->GetDefaultPageSize(&defaultPageSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Enable auto_vacuum mode and update the page size to the platform default.
+ nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = ");
+ upgradeQuery.AppendInt(defaultPageSize);
+
+ rv = aConnection->ExecuteSimpleSQL(upgradeQuery);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ *aVacuumNeeded = true;
+#endif
+
+ rv = aConnection->SetSchemaVersion(MakeSchemaVersion(13, 0));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aConnection);
+
+ // The only change between 13 and 14 was a different structured
+ // clone format, but it's backwards-compatible.
+ nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(14, 0));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom14_0To15_0(mozIStorageConnection* aConnection)
+{
+ // The only change between 14 and 15 was a different structured
+ // clone format, but it's backwards-compatible.
+ nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(15, 0));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom15_0To16_0(mozIStorageConnection* aConnection)
+{
+ // The only change between 15 and 16 was a different structured
+ // clone format, but it's backwards-compatible.
+ nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(16, 0));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom16_0To17_0(mozIStorageConnection* aConnection)
+{
+ // The only change between 16 and 17 was a different structured
+ // clone format, but it's backwards-compatible.
+ nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(17, 0));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+GetDatabaseFileURL(nsIFile* aDatabaseFile,
+ PersistenceType aPersistenceType,
+ const nsACString& aGroup,
+ const nsACString& aOrigin,
+ nsIFileURL** aResult)
+{
+ MOZ_ASSERT(aDatabaseFile);
+ MOZ_ASSERT(aResult);
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
+ MOZ_ASSERT(fileUrl);
+
+ nsAutoCString type;
+ PersistenceTypeToText(aPersistenceType, type);
+
+ rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type +
+ NS_LITERAL_CSTRING("&group=") + aGroup +
+ NS_LITERAL_CSTRING("&origin=") + aOrigin);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ fileUrl.forget(aResult);
+ return NS_OK;
+}
+
+nsresult
+SetDefaultPragmas(mozIStorageConnection* aConnection)
+{
+ MOZ_ASSERT(aConnection);
+
+ static const char query[] =
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+ // Switch the journaling mode to TRUNCATE to avoid changing the directory
+ // structure at the conclusion of every transaction for devices with slower
+ // file systems.
+ "PRAGMA journal_mode = TRUNCATE; "
+#endif
+ // We use foreign keys in lots of places.
+ "PRAGMA foreign_keys = ON; "
+ // The "INSERT OR REPLACE" statement doesn't fire the update trigger,
+ // instead it fires only the insert trigger. This confuses the update
+ // refcount function. This behavior changes with enabled recursive triggers,
+ // so the statement fires the delete trigger first and then the insert
+ // trigger.
+ "PRAGMA recursive_triggers = ON;";
+
+ nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(query));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+CreateDatabaseConnection(nsIFile* aDBFile,
+ nsIFile* aFMDirectory,
+ const nsAString& aName,
+ PersistenceType aPersistenceType,
+ const nsACString& aGroup,
+ const nsACString& aOrigin,
+ mozIStorageConnection** aConnection)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(aDBFile);
+ MOZ_ASSERT(aFMDirectory);
+ MOZ_ASSERT(aConnection);
+
+ PROFILER_LABEL("IndexedDB",
+ "CreateDatabaseConnection",
+ js::ProfileEntry::Category::STORAGE);
+
+ nsresult rv;
+ bool exists;
+
+ if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
+ rv = aDBFile->Exists(&exists);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!exists) {
+ NS_WARNING("Refusing to create database because disk space is low!");
+ return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
+ }
+ }
+
+ nsCOMPtr<nsIFileURL> dbFileUrl;
+ rv = GetDatabaseFileURL(aDBFile, aPersistenceType, aGroup, aOrigin,
+ getter_AddRefs(dbFileUrl));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<mozIStorageService> ss =
+ do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<mozIStorageConnection> connection;
+ rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
+ if (rv == NS_ERROR_FILE_CORRUPTED) {
+ // If we're just opening the database during origin initialization, then
+ // we don't want to erase any files. The failure here will fail origin
+ // initialization too.
+ if (aName.IsVoid()) {
+ return rv;
+ }
+
+ // Nuke the database file.
+ rv = aDBFile->Remove(false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aFMDirectory->Exists(&exists);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (exists) {
+ bool isDirectory;
+ rv = aFMDirectory->IsDirectory(&isDirectory);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ if (NS_WARN_IF(!isDirectory)) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ rv = aFMDirectory->Remove(true);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
+ }
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = SetDefaultPragmas(connection);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Check to make sure that the database schema is correct.
+ int32_t schemaVersion;
+ rv = connection->GetSchemaVersion(&schemaVersion);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Unknown schema will fail origin initialization too.
+ if (!schemaVersion && aName.IsVoid()) {
+ IDB_WARNING("Unable to open IndexedDB database, schema is not set!");
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ if (schemaVersion > kSQLiteSchemaVersion) {
+ IDB_WARNING("Unable to open IndexedDB database, schema is too high!");
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ bool vacuumNeeded = false;
+
+ if (schemaVersion != kSQLiteSchemaVersion) {
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+ if (!schemaVersion) {
+ // Have to do this before opening a transaction.
+ rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+ // Turn on auto_vacuum mode to reclaim disk space on mobile devices.
+ "PRAGMA auto_vacuum = FULL; "
+ ));
+ if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
+ // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
+ // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
+ rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
+ }
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+#endif
+
+ mozStorageTransaction transaction(connection, false,
+ mozIStorageConnection::TRANSACTION_IMMEDIATE);
+
+ if (!schemaVersion) {
+ // Brand new file, initialize our tables.
+ rv = CreateTables(connection);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ MOZ_ASSERT(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)));
+ MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
+
+ nsCOMPtr<mozIStorageStatement> stmt;
+ nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING(
+ "INSERT INTO database (name) "
+ "VALUES (:name)"
+ ), getter_AddRefs(stmt));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = stmt->Execute();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ } else {
+ // This logic needs to change next time we change the schema!
+ static_assert(kSQLiteSchemaVersion == int32_t((17 << 4) + 0),
+ "Upgrade function needed due to schema version increase.");
+
+ while (schemaVersion != kSQLiteSchemaVersion) {
+ if (schemaVersion == 4) {
+ rv = UpgradeSchemaFrom4To5(connection);
+ } else if (schemaVersion == 5) {
+ rv = UpgradeSchemaFrom5To6(connection);
+ } else if (schemaVersion == 6) {
+ rv = UpgradeSchemaFrom6To7(connection);
+ } else if (schemaVersion == 7) {
+ rv = UpgradeSchemaFrom7To8(connection);
+ } else if (schemaVersion == 8) {
+ rv = UpgradeSchemaFrom8To9_0(connection);
+ vacuumNeeded = true;
+ } else if (schemaVersion == MakeSchemaVersion(9, 0)) {
+ rv = UpgradeSchemaFrom9_0To10_0(connection);
+ } else if (schemaVersion == MakeSchemaVersion(10, 0)) {
+ rv = UpgradeSchemaFrom10_0To11_0(connection);
+ } else if (schemaVersion == MakeSchemaVersion(11, 0)) {
+ rv = UpgradeSchemaFrom11_0To12_0(connection);
+ } else if (schemaVersion == MakeSchemaVersion(12, 0)) {
+ rv = UpgradeSchemaFrom12_0To13_0(connection, &vacuumNeeded);
+ } else if (schemaVersion == MakeSchemaVersion(13, 0)) {
+ rv = UpgradeSchemaFrom13_0To14_0(connection);
+ } else if (schemaVersion == MakeSchemaVersion(14, 0)) {
+ rv = UpgradeSchemaFrom14_0To15_0(connection);
+ } else if (schemaVersion == MakeSchemaVersion(15, 0)) {
+ rv = UpgradeSchemaFrom15_0To16_0(connection);
+ } else if (schemaVersion == MakeSchemaVersion(16, 0)) {
+ rv = UpgradeSchemaFrom16_0To17_0(connection);
+ } else {
+ IDB_WARNING("Unable to open IndexedDB database, no upgrade path is "
+ "available!");
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = connection->GetSchemaVersion(&schemaVersion);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
+ }
+
+ rv = transaction.Commit();
+ if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
+ // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
+ // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
+ rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
+ }
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ if (vacuumNeeded) {
+ rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ connection.forget(aConnection);
+ return NS_OK;
+}
+
+already_AddRefed<nsIFile>
+GetFileForPath(const nsAString& aPath)
+{
+ MOZ_ASSERT(!aPath.IsEmpty());
+
+ nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+ if (NS_WARN_IF(!file)) {
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(NS_FAILED(file->InitWithPath(aPath)))) {
+ return nullptr;
+ }
+
+ return file.forget();
+}
+
+nsresult
+GetDatabaseConnection(const nsAString& aDatabaseFilePath,
+ PersistenceType aPersistenceType,
+ const nsACString& aGroup,
+ const nsACString& aOrigin,
+ mozIStorageConnection** aConnection)
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+ MOZ_ASSERT(!IsOnBackgroundThread());
+ MOZ_ASSERT(!aDatabaseFilePath.IsEmpty());
+ MOZ_ASSERT(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")));
+ MOZ_ASSERT(aConnection);
+
+ PROFILER_LABEL("IndexedDB",
+ "GetDatabaseConnection",
+ js::ProfileEntry::Category::STORAGE);
+
+ nsCOMPtr<nsIFile> dbFile = GetFileForPath(aDatabaseFilePath);
+ if (NS_WARN_IF(!dbFile)) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ bool exists;
+ nsresult rv = dbFile->Exists(&exists);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (NS_WARN_IF(!exists)) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ nsCOMPtr<nsIFileURL> dbFileUrl;
+ rv = GetDatabaseFileURL(dbFile, aPersistenceType, aGroup, aOrigin,
+ getter_AddRefs(dbFileUrl));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<mozIStorageService> ss =
+ do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<mozIStorageConnection> connection;
+ rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = SetDefaultPragmas(connection);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ connection.forget(aConnection);
+ return NS_OK;
+}
+
+/*******************************************************************************
+ * Actor class declarations
+ ******************************************************************************/
+
+class DatabaseOperationBase
+ : public nsRunnable
+ , public mozIStorageProgressHandler
+{
+ // Uniquely tracks each operation for logging purposes. Only modified on the
+ // PBackground thread.
+ static uint64_t sNextSerialNumber;
+
+protected:
+ class AutoSetProgressHandler;
+
+ typedef nsDataHashtable<nsUint64HashKey, bool> UniqueIndexTable;
+
+ nsCOMPtr<nsIEventTarget> mOwningThread;
+ const uint64_t mSerialNumber;
+ nsresult mResultCode;
+
+private:
+ Atomic<bool> mOperationMayProceed;
+ bool mActorDestroyed;
+
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ void
+ AssertIsOnOwningThread() const
+ {
+ AssertIsOnBackgroundThread();
+
+#ifdef DEBUG
+ MOZ_ASSERT(mOwningThread);
+ bool current;
+ MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t)));
+ MOZ_ASSERT(current);
+#endif
+ }
+
+ void
+ NoteActorDestroyed()
+ {
+ AssertIsOnOwningThread();
+
+ mActorDestroyed = true;
+ mOperationMayProceed = false;
+ }
+
+ bool
+ IsActorDestroyed() const
+ {
+ AssertIsOnOwningThread();
+
+ return mActorDestroyed;
+ }
+
+ // May be called on any thread.
+ bool
+ OperationMayProceed() const
+ {
+ return mOperationMayProceed;
+ }
+
+ uint64_t
+ SerialNumber() const
+ {
+ return mSerialNumber;
+ }
+
+ nsresult
+ ResultCode() const
+ {
+ return mResultCode;
+ }
+
+ void
+ SetFailureCode(nsresult aErrorCode)
+ {
+ MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
+ MOZ_ASSERT(NS_FAILED(aErrorCode));
+
+ mResultCode = aErrorCode;
+ }
+
+protected:
+ DatabaseOperationBase()
+ : mOwningThread(NS_GetCurrentThread())
+ , mSerialNumber(++sNextSerialNumber)
+ , mResultCode(NS_OK)
+ , mOperationMayProceed(true)
+ , mActorDestroyed(false)
+ {
+ AssertIsOnOwningThread();
+ }
+
+ virtual
+ ~DatabaseOperationBase()
+ {
+ MOZ_ASSERT(mActorDestroyed);
+ }
+
+ static void
+ GetBindingClauseForKeyRange(const SerializedKeyRange& aKeyRange,
+ const nsACString& aKeyColumnName,
+ nsAutoCString& aBindingClause);
+
+ static uint64_t
+ ReinterpretDoubleAsUInt64(double aDouble);
+
+ static nsresult
+ GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement,
+ uint32_t aDataIndex,
+ uint32_t aFileIdsIndex,
+ FileManager* aFileManager,
+ StructuredCloneReadInfo* aInfo);
+
+ static nsresult
+ BindKeyRangeToStatement(const SerializedKeyRange& aKeyRange,
+ mozIStorageStatement* aStatement);
+
+ static void
+ AppendConditionClause(const nsACString& aColumnName,
+ const nsACString& aArgName,
+ bool aLessThan,
+ bool aEquals,
+ nsAutoCString& aResult);
+
+ static nsresult
+ UpdateIndexes(TransactionBase* aTransaction,
+ const UniqueIndexTable& aUniqueIndexTable,
+ const Key& aObjectStoreKey,
+ bool aOverwrite,
+ int64_t aObjectDataId,
+ const nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
+
+private:
+ // Not to be overridden by subclasses.
+ NS_DECL_MOZISTORAGEPROGRESSHANDLER
+};
+
+class MOZ_STACK_CLASS DatabaseOperationBase::AutoSetProgressHandler MOZ_FINAL
+{
+ mozIStorageConnection* mConnection;
+ DebugOnly<DatabaseOperationBase*> mDEBUGDatabaseOp;
+
+public:
+ AutoSetProgressHandler()
+ : mConnection(nullptr)
+ , mDEBUGDatabaseOp(nullptr)
+ { }
+
+ ~AutoSetProgressHandler();
+
+ nsresult
+ Register(DatabaseOperationBase* aDatabaseOp,
+ const nsCOMPtr<mozIStorageConnection>& aConnection);
+};
+
+class TransactionDatabaseOperationBase
+ : public DatabaseOperationBase
+{
+ nsRefPtr<TransactionBase> mTransaction;
+ const bool mTransactionIsAborted;
+
+public:
+ void
+ AssertIsOnTransactionThread() const
+#ifdef DEBUG
+ ;
+#else
+ { }
+#endif
+
+ void
+ DispatchToTransactionThreadPool();
+
+ // May be overridden by subclasses if they need to perform work on the
+ // background thread before being dispatched. Returning false will kill the
+ // child actors and prevent dispatch.
+ virtual bool
+ Init(TransactionBase* aTransaction);
+
+ // This callback will be called on the background thread before releasing the
+ // final reference to this request object. Subclasses may perform any
+ // additional cleanup here but must always call the base class implementation.
+ virtual void
+ Cleanup();
+
+protected:
+ explicit TransactionDatabaseOperationBase(TransactionBase* aTransaction);
+
+ virtual
+ ~TransactionDatabaseOperationBase();
+
+ // Must be overridden in subclasses. Called on the target thread to allow the
+ // subclass to perform necessary database or file operations. A successful
+ // return value will trigger a SendSuccessResult callback on the background
+ // thread while a failure value will trigger a SendFailureResult callback.
+ virtual nsresult
+ DoDatabaseWork(TransactionBase* aTransaction) = 0;
+
+ // Must be overridden in subclasses. Called on the background thread to allow
+ // the subclass to serialize its results and send them to the child actor. A
+ // failed return value will trigger a SendFailureResult callback.
+ virtual nsresult
+ SendSuccessResult() = 0;
+
+ // Must be overridden in subclasses. Called on the background thread to allow
+ // the subclass to send its failure code. Returning false will cause the
+ // transaction to be aborted with aResultCode. Returning true will not cause
+ // the transaction to be aborted.
+ virtual bool
+ SendFailureResult(nsresult aResultCode) = 0;
+
+private:
+ void
+ RunOnTransactionThread();
+
+ void
+ RunOnOwningThread();
+
+ // Not to be overridden by subclasses.
+ NS_DECL_NSIRUNNABLE
+};
+
+class Factory MOZ_FINAL
+ : public PBackgroundIDBFactoryParent
+{
+ // Counts the number of "live" Factory instances that have not yet had
+ // ActorDestroy called.
+ static uint64_t sFactoryInstanceCount;
+
+ const OptionalWindowId mOptionalWindowId;
+
+ DebugOnly<bool> mActorDestroyed;
+
+public:
+ static already_AddRefed<Factory>
+ Create(const OptionalWindowId& aOptionalWindowId);
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Factory)
+
+private:
+ // Only constructed in Create().
+ explicit Factory(const OptionalWindowId& aOptionalWindowId);
+
+ // Reference counted.
+ ~Factory();
+
+ // IPDL methods are only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvDeleteMe() MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBFactoryRequestParent*
+ AllocPBackgroundIDBFactoryRequestParent(const FactoryRequestParams& aParams)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ RecvPBackgroundIDBFactoryRequestConstructor(
+ PBackgroundIDBFactoryRequestParent* aActor,
+ const FactoryRequestParams& aParams)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBFactoryRequestParent(
+ PBackgroundIDBFactoryRequestParent* aActor)
+ MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBDatabaseParent*
+ AllocPBackgroundIDBDatabaseParent(
+ const DatabaseSpec& aSpec,
+ PBackgroundIDBFactoryRequestParent* aRequest)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBDatabaseParent(PBackgroundIDBDatabaseParent* aActor)
+ MOZ_OVERRIDE;
+};
+
+class Database MOZ_FINAL
+ : public PBackgroundIDBDatabaseParent
+{
+ friend class VersionChangeTransaction;
+
+ nsRefPtr<Factory> mFactory;
+ nsRefPtr<FullDatabaseMetadata> mMetadata;
+ nsRefPtr<FileManager> mFileManager;
+ nsRefPtr<DatabaseOfflineStorage> mOfflineStorage;
+ nsTHashtable<nsPtrHashKey<TransactionBase>> mTransactions;
+ const PrincipalInfo mPrincipalInfo;
+ const nsCString mGroup;
+ const nsCString mOrigin;
+ const nsCString mId;
+ const nsString mFilePath;
+ Atomic<bool> mInvalidatedOnAnyThread;
+ const PersistenceType mPersistenceType;
+ const bool mChromeWriteAccessAllowed;
+ bool mClosed;
+ bool mInvalidated;
+ bool mActorWasAlive;
+ bool mActorDestroyed;
+ bool mMetadataCleanedUp;
+
+public:
+ // Created by OpenDatabaseOp.
+ Database(Factory* aFactory,
+ const PrincipalInfo& aPrincipalInfo,
+ const nsACString& aGroup,
+ const nsACString& aOrigin,
+ FullDatabaseMetadata* aMetadata,
+ FileManager* aFileManager,
+ already_AddRefed<DatabaseOfflineStorage> aOfflineStorage,
+ bool aChromeWriteAccessAllowed);
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Database)
+
+ void
+ Invalidate();
+
+ const PrincipalInfo&
+ GetPrincipalInfo() const
+ {
+ return mPrincipalInfo;
+ }
+
+ const nsCString&
+ Group() const
+ {
+ return mGroup;
+ }
+
+ const nsCString&
+ Origin() const
+ {
+ return mOrigin;
+ }
+
+ const nsCString&
+ Id() const
+ {
+ return mId;
+ }
+
+ PersistenceType
+ Type() const
+ {
+ return mPersistenceType;
+ }
+
+ const nsString&
+ FilePath() const
+ {
+ return mFilePath;
+ }
+
+ FileManager*
+ GetFileManager() const
+ {
+ return mFileManager;
+ }
+
+ FullDatabaseMetadata*
+ Metadata() const
+ {
+ MOZ_ASSERT(mMetadata);
+ return mMetadata;
+ }
+
+ PBackgroundParent*
+ GetBackgroundParent() const
+ {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!IsActorDestroyed());
+
+ return Manager()->Manager();
+ }
+
+ bool
+ RegisterTransaction(TransactionBase* aTransaction);
+
+ void
+ UnregisterTransaction(TransactionBase* aTransaction);
+
+ void
+ SetActorAlive();
+
+ bool
+ IsActorAlive() const
+ {
+ AssertIsOnBackgroundThread();
+
+ return mActorWasAlive && !mActorDestroyed;
+ }
+
+ bool
+ IsActorDestroyed() const
+ {
+ AssertIsOnBackgroundThread();
+
+ return mActorWasAlive && mActorDestroyed;
+ }
+
+ bool
+ IsClosed() const
+ {
+ AssertIsOnBackgroundThread();
+
+ return mClosed;
+ }
+
+ bool
+ IsInvalidated() const
+ {
+ AssertIsOnBackgroundThread();
+
+ return mInvalidated;
+ }
+
+private:
+ // Reference counted.
+ ~Database()
+ {
+ MOZ_ASSERT(mClosed);
+ MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed);
+ }
+
+ bool
+ CloseInternal();
+
+ void
+ CleanupMetadata();
+
+ // IPDL methods are only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBDatabaseFileParent*
+ AllocPBackgroundIDBDatabaseFileParent(PBlobParent* aBlobParent)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBDatabaseFileParent(
+ PBackgroundIDBDatabaseFileParent* aActor)
+ MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBTransactionParent*
+ AllocPBackgroundIDBTransactionParent(
+ const nsTArray<nsString>& aObjectStoreNames,
+ const Mode& aMode)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ RecvPBackgroundIDBTransactionConstructor(
+ PBackgroundIDBTransactionParent* aActor,
+ const nsTArray<nsString>& aObjectStoreNames,
+ const Mode& aMode)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBTransactionParent(
+ PBackgroundIDBTransactionParent* aActor)
+ MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBVersionChangeTransactionParent*
+ AllocPBackgroundIDBVersionChangeTransactionParent(
+ const uint64_t& aCurrentVersion,
+ const uint64_t& aRequestedVersion,
+ const int64_t& aNextObjectStoreId,
+ const int64_t& aNextIndexId)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBVersionChangeTransactionParent(
+ PBackgroundIDBVersionChangeTransactionParent* aActor)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ RecvDeleteMe() MOZ_OVERRIDE;
+
+ virtual bool
+ RecvBlocked() MOZ_OVERRIDE;
+
+ virtual bool
+ RecvClose() MOZ_OVERRIDE;
+};
+
+class DatabaseFile MOZ_FINAL
+ : public PBackgroundIDBDatabaseFileParent
+{
+ friend class Database;
+
+ nsRefPtr<DOMFileImpl> mBlobImpl;
+ nsRefPtr<FileInfo> mFileInfo;
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::DatabaseFile);
+
+ FileInfo*
+ GetFileInfo() const
+ {
+ AssertIsOnBackgroundThread();
+
+ return mFileInfo;
+ }
+
+ already_AddRefed<nsIInputStream>
+ GetInputStream() const
+ {
+ AssertIsOnBackgroundThread();
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ if (mBlobImpl) {
+ MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+ mBlobImpl->GetInternalStream(getter_AddRefs(inputStream))));
+ }
+
+ return inputStream.forget();
+ }
+
+ void
+ ClearInputStream()
+ {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mBlobImpl);
+
+ mBlobImpl = nullptr;
+ }
+
+private:
+ // Called when sending to the child.
+ explicit DatabaseFile(FileInfo* aFileInfo)
+ : mFileInfo(aFileInfo)
+ {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aFileInfo);
+ }
+
+ // Called when receiving from the child.
+ DatabaseFile(DOMFileImpl* aBlobImpl, FileInfo* aFileInfo)
+ : mBlobImpl(aBlobImpl)
+ , mFileInfo(aFileInfo)
+ {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aBlobImpl);
+ MOZ_ASSERT(aFileInfo);
+ }
+
+ ~DatabaseFile()
+ { }
+
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE
+ {
+ AssertIsOnBackgroundThread();
+
+ mBlobImpl = nullptr;
+ mFileInfo = nullptr;
+ }
+};
+
+class TransactionBase
+{
+ friend class Cursor;
+
+ class CommitOp;
+ class UpdateRefcountFunction;
+
+public:
+ class AutoSavepoint;
+ class CachedStatement;
+
+protected:
+ typedef IDBTransaction::Mode Mode;
+
+private:
+ nsRefPtr<Database> mDatabase;
+ nsCOMPtr<mozIStorageConnection> mConnection;
+ nsRefPtr<UpdateRefcountFunction> mUpdateFileRefcountFunction;
+ nsInterfaceHashtable<nsCStringHashKey, mozIStorageStatement>
+ mCachedStatements;
+ nsTArray<nsRefPtr<FullObjectStoreMetadata>>
+ mModifiedAutoIncrementObjectStoreMetadataArray;
+ const uint64_t mTransactionId;
+ const nsCString mDatabaseId;
+ uint64_t mActiveRequestCount;
+ Atomic<bool> mInvalidatedOnAnyThread;
+ Mode mMode;
+ bool mHasBeenActive;
+ bool mActorDestroyed;
+ bool mInvalidated;
+
+protected:
+ nsresult mResultCode;
+ bool mCommitOrAbortReceived;
+ bool mCommittedOrAborted;
+ bool mForceAborted;
+
+private:
+ DebugOnly<PRThread*> mTransactionThread;
+ DebugOnly<uint32_t> mSavepointCount;
+
+public:
+ void
+ AssertIsOnTransactionThread() const
+ {
+ MOZ_ASSERT(mTransactionThread);
+ MOZ_ASSERT(PR_GetCurrentThread() == mTransactionThread);
+ }
+
+ bool
+ IsActorDestroyed() const
+ {
+ AssertIsOnBackgroundThread();
+
+ return mActorDestroyed;
+ }
+
+ // Must be called on the background thread.
+ bool
+ IsInvalidated() const
+ {
+ MOZ_ASSERT(IsOnBackgroundThread(), "Use IsInvalidatedOnAnyThread()");
+ MOZ_ASSERT_IF(mInvalidated, NS_FAILED(mResultCode));
+
+ return mInvalidated;
+ }
+
+ // May be called on any thread, but is more expensive than IsInvalidated().
+ bool
+ IsInvalidatedOnAnyThread() const
+ {
+ return mInvalidatedOnAnyThread;
+ }
+
+ void
+ SetActive()
+ {
+ AssertIsOnBackgroundThread();
+
+ mHasBeenActive = true;
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(
+ mozilla::dom::indexedDB::TransactionBase)
+
+ nsresult
+ GetCachedStatement(const nsACString& aQuery,
+ CachedStatement* aCachedStatement);
+
+ template<int N>
+ nsresult
+ GetCachedStatement(const char (&aQuery)[N],
+ CachedStatement* aCachedStatement)
+ {
+ AssertIsOnTransactionThread();
+ MOZ_ASSERT(aCachedStatement);
+
+ return GetCachedStatement(NS_LITERAL_CSTRING(aQuery), aCachedStatement);
+ }
+
+ nsresult
+ EnsureConnection();
+
+ void
+ Abort(nsresult aResultCode, bool aForce);
+
+ mozIStorageConnection*
+ Connection() const
+ {
+ AssertIsOnTransactionThread();
+ MOZ_ASSERT(mConnection);
+
+ return mConnection;
+ }
+
+ uint64_t
+ TransactionId() const
+ {
+ return mTransactionId;
+ }
+
+ const nsCString&
+ DatabaseId() const
+ {
+ return mDatabaseId;
+ }
+
+ Mode
+ GetMode() const
+ {
+ return mMode;
+ }
+
+ Database*
+ GetDatabase() const
+ {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mDatabase);
+
+ return mDatabase;
+ }
+
+ bool
+ IsAborted() const
+ {
+ AssertIsOnBackgroundThread();
+
+ return NS_FAILED(mResultCode);
+ }
+
+ already_AddRefed<FullObjectStoreMetadata>
+ GetMetadataForObjectStoreId(int64_t aObjectStoreId) const;
+
+ already_AddRefed<FullIndexMetadata>
+ GetMetadataForIndexId(FullObjectStoreMetadata* const aObjectStoreMetadata,
+ int64_t aIndexId) const;
+
+ PBackgroundParent*
+ GetBackgroundParent() const
+ {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!IsActorDestroyed());
+
+ return GetDatabase()->GetBackgroundParent();
+ }
+
+ void
+ NoteModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata);
+
+ void
+ ForgetModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata);
+
+ nsresult
+ StartSavepoint();
+
+ nsresult
+ ReleaseSavepoint();
+
+ nsresult
+ RollbackSavepoint();
+
+ void
+ NoteActiveRequest();
+
+ void
+ NoteFinishedRequest();
+
+ void
+ Invalidate();
+
+protected:
+ TransactionBase(Database* aDatabase,
+ Mode aMode);
+
+ virtual
+ ~TransactionBase();
+
+ void
+ NoteActorDestroyed()
+ {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!mActorDestroyed);
+
+ mActorDestroyed = true;
+ }
+
+#ifdef DEBUG
+ // Only called by VersionChangeTransaction.
+ void
+ FakeActorDestroyed()
+ {
+ mActorDestroyed = true;
+ }
+#endif
+
+ bool
+ RecvCommit();
+
+ bool
+ RecvAbort(nsresult aResultCode);
+
+ void
+ MaybeCommitOrAbort()
+ {
+ AssertIsOnBackgroundThread();
+
+ // If we've already committed or aborted then there's nothing else to do.
+ if (mCommittedOrAborted) {
+ return;
+ }
+
+ // If there are active requests then we have to wait for those requests to
+ // complete (see NoteFinishedRequest).
+ if (mActiveRequestCount) {
+ return;
+ }
+
+ // If we haven't yet received a commit or abort message then there could be
+ // additional requests coming so we should wait unless we're being forced to
+ // abort.
+ if (!mCommitOrAbortReceived && !mForceAborted) {
+ return;
+ }
+
+ CommitOrAbort();
+ }
+
+ PBackgroundIDBRequestParent*
+ AllocRequest(const RequestParams& aParams, bool aTrustParams);
+
+ bool
+ StartRequest(PBackgroundIDBRequestParent* aActor);
+
+ bool
+ DeallocRequest(PBackgroundIDBRequestParent* aActor);
+
+ PBackgroundIDBCursorParent*
+ AllocCursor(const OpenCursorParams& aParams, bool aTrustParams);
+
+ bool
+ StartCursor(PBackgroundIDBCursorParent* aActor,
+ const OpenCursorParams& aParams);
+
+ bool
+ DeallocCursor(PBackgroundIDBCursorParent* aActor);
+
+ virtual void
+ UpdateMetadata(nsresult aResult)
+ { }
+
+ virtual bool
+ SendCompleteNotification(nsresult aResult) = 0;
+
+private:
+ // Only called by CommitOp.
+ void
+ ReleaseTransactionThreadObjects();
+
+ // Only called by CommitOp.
+ void
+ ReleaseBackgroundThreadObjects();
+
+ bool
+ VerifyRequestParams(const RequestParams& aParams) const;
+
+ bool
+ VerifyRequestParams(const OpenCursorParams& aParams) const;
+
+ bool
+ VerifyRequestParams(const CursorRequestParams& aParams) const;
+
+ bool
+ VerifyRequestParams(const SerializedKeyRange& aKeyRange) const;
+
+ bool
+ VerifyRequestParams(const ObjectStoreAddPutParams& aParams) const;
+
+ bool
+ VerifyRequestParams(const OptionalKeyRange& aKeyRange) const;
+
+ void
+ CommitOrAbort();
+};
+
+class TransactionBase::CommitOp MOZ_FINAL
+ : public DatabaseOperationBase
+ , public TransactionThreadPool::FinishCallback
+{
+ friend class TransactionBase;
+
+ nsRefPtr<TransactionBase> mTransaction;
+ nsresult mResultCode;
+
+private:
+ CommitOp(TransactionBase* aTransaction,
+ nsresult aResultCode)
+ : mTransaction(aTransaction)
+ , mResultCode(aResultCode)
+ {
+ MOZ_ASSERT(aTransaction);
+ }
+
+ ~CommitOp()
+ { }
+
+ // Writes new autoIncrement counts to database.
+ nsresult
+ WriteAutoIncrementCounts();
+
+ // Updates counts after a database activity has finished.
+ void
+ CommitOrRollbackAutoIncrementCounts();
+
+ NS_DECL_NSIRUNNABLE
+
+ virtual void
+ TransactionFinishedBeforeUnblock() MOZ_OVERRIDE;
+
+ virtual void
+ TransactionFinishedAfterUnblock() MOZ_OVERRIDE;
+
+public:
+ void
+ AssertIsOnTransactionThread() const
+ {
+ MOZ_ASSERT(mTransaction);
+ mTransaction->AssertIsOnTransactionThread();
+ }
+
+ NS_DECL_ISUPPORTS_INHERITED
+};
+
+class TransactionBase::UpdateRefcountFunction MOZ_FINAL
+ : public mozIStorageFunction
+{
+ class FileInfoEntry
+ {
+ friend class UpdateRefcountFunction;
+
+ nsRefPtr<FileInfo> mFileInfo;
+ int32_t mDelta;
+ int32_t mSavepointDelta;
+
+ public:
+ explicit FileInfoEntry(FileInfo* aFileInfo)
+ : mFileInfo(aFileInfo)
+ , mDelta(0)
+ , mSavepointDelta(0)
+ { }
+ };
+
+ enum UpdateType
+ {
+ eIncrement,
+ eDecrement
+ };
+
+ class DatabaseUpdateFunction
+ {
+ nsCOMPtr<mozIStorageConnection> mConnection;
+ nsCOMPtr<mozIStorageStatement> mUpdateStatement;
+ nsCOMPtr<mozIStorageStatement> mSelectStatement;
+ nsCOMPtr<mozIStorageStatement> mInsertStatement;
+
+ UpdateRefcountFunction* mFunction;
+
+ nsresult mErrorCode;
+
+ public:
+ DatabaseUpdateFunction(mozIStorageConnection* aConnection,
+ UpdateRefcountFunction* aFunction)
+ : mConnection(aConnection)
+ , mFunction(aFunction)
+ , mErrorCode(NS_OK)
+ { }
+
+ bool
+ Update(int64_t aId, int32_t aDelta);
+
+ nsresult
+ ErrorCode() const
+ {
+ return mErrorCode;
+ }
+
+ private:
+ nsresult
+ UpdateInternal(int64_t aId, int32_t aDelta);
+ };
+
+ FileManager* mFileManager;
+ nsClassHashtable<nsUint64HashKey, FileInfoEntry> mFileInfoEntries;
+ nsDataHashtable<nsUint64HashKey, FileInfoEntry*> mSavepointEntriesIndex;
+
+ nsTArray<int64_t> mJournalsToCreateBeforeCommit;
+ nsTArray<int64_t> mJournalsToRemoveAfterCommit;
+ nsTArray<int64_t> mJournalsToRemoveAfterAbort;
+
+ bool mInSavepoint;
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_MOZISTORAGEFUNCTION
+
+ explicit UpdateRefcountFunction(FileManager* aFileManager)
+ : mFileManager(aFileManager)
+ , mInSavepoint(false)
+ { }
+
+ void
+ ClearFileInfoEntries()
+ {
+ mFileInfoEntries.Clear();
+ }
+
+ nsresult
+ WillCommit(mozIStorageConnection* aConnection);
+
+ void
+ DidCommit();
+
+ void
+ DidAbort();
+
+ void
+ StartSavepoint();
+
+ void
+ ReleaseSavepoint();
+
+ void
+ RollbackSavepoint();
+
+private:
+ ~UpdateRefcountFunction()
+ { }
+
+ nsresult
+ ProcessValue(mozIStorageValueArray* aValues,
+ int32_t aIndex,
+ UpdateType aUpdateType);
+
+ nsresult
+ CreateJournals();
+
+ nsresult
+ RemoveJournals(const nsTArray<int64_t>& aJournals);
+
+ static PLDHashOperator
+ DatabaseUpdateCallback(const uint64_t& aKey,
+ FileInfoEntry* aValue,
+ void* aUserArg);
+
+ static PLDHashOperator
+ FileInfoUpdateCallback(const uint64_t& aKey,
+ FileInfoEntry* aValue,
+ void* aUserArg);
+};
+
+class MOZ_STACK_CLASS TransactionBase::AutoSavepoint MOZ_FINAL
+{
+ TransactionBase* mTransaction;
+
+public:
+ AutoSavepoint()
+ : mTransaction(nullptr)
+ { }
+
+ ~AutoSavepoint();
+
+ nsresult
+ Start(TransactionBase* aTransaction);
+
+ nsresult
+ Commit();
+};
+
+class TransactionBase::CachedStatement MOZ_FINAL
+{
+ friend class TransactionBase;
+
+ nsCOMPtr<mozIStorageStatement> mStatement;
+ Maybe<mozStorageStatementScoper> mScoper;
+
+public:
+ CachedStatement()
+ { }
+
+ ~CachedStatement()
+ { }
+
+ operator mozIStorageStatement*()
+ {
+ return mStatement;
+ }
+
+ mozIStorageStatement*
+ operator->()
+ {
+ MOZ_ASSERT(mStatement);
+ return mStatement;
+ }
+
+ void
+ Reset()
+ {
+ MOZ_ASSERT_IF(mStatement, mScoper);
+
+ if (mStatement) {
+ mScoper.reset();
+ mScoper.emplace(mStatement);
+ }
+ }
+
+private:
+ // Only called by TransactionBase.
+ void
+ Assign(already_AddRefed<mozIStorageStatement> aStatement)
+ {
+ mScoper.reset();
+
+ mStatement = aStatement;
+
+ if (mStatement) {
+ mScoper.emplace(mStatement);
+ }
+ }
+
+ // No funny business allowed.
+ CachedStatement(const CachedStatement&) MOZ_DELETE;
+ CachedStatement& operator=(const CachedStatement&) MOZ_DELETE;
+};
+
+class NormalTransaction MOZ_FINAL
+ : public TransactionBase
+ , public PBackgroundIDBTransactionParent
+{
+ friend class Database;
+
+ nsTArray<nsRefPtr<FullObjectStoreMetadata>> mObjectStores;
+
+private:
+ // This constructor is only called by Database.
+ NormalTransaction(Database* aDatabase,
+ nsTArray<nsRefPtr<FullObjectStoreMetadata>>& aObjectStores,
+ TransactionBase::Mode aMode);
+
+ // Reference counted.
+ ~NormalTransaction()
+ { }
+
+ bool
+ IsSameProcessActor();
+
+ // Only called by TransactionBase.
+ virtual bool
+ SendCompleteNotification(nsresult aResult) MOZ_OVERRIDE;
+
+ // IPDL methods are only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvDeleteMe() MOZ_OVERRIDE;
+
+ virtual bool
+ RecvCommit() MOZ_OVERRIDE;
+
+ virtual bool
+ RecvAbort(const nsresult& aResultCode) MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBRequestParent*
+ AllocPBackgroundIDBRequestParent(const RequestParams& aParams) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent* aActor,
+ const RequestParams& aParams)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent* aActor)
+ MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBCursorParent*
+ AllocPBackgroundIDBCursorParent(const OpenCursorParams& aParams) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent* aActor,
+ const OpenCursorParams& aParams)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent* aActor)
+ MOZ_OVERRIDE;
+};
+
+class VersionChangeTransaction MOZ_FINAL
+ : public TransactionBase
+ , public PBackgroundIDBVersionChangeTransactionParent
+{
+ friend class OpenDatabaseOp;
+
+ nsRefPtr<OpenDatabaseOp> mOpenDatabaseOp;
+ nsRefPtr<FullDatabaseMetadata> mOldMetadata;
+
+ bool mActorWasAlive;
+
+private:
+ // Only called by OpenDatabaseOp.
+ explicit VersionChangeTransaction(OpenDatabaseOp* aOpenDatabaseOp);
+
+ // Reference counted.
+ ~VersionChangeTransaction();
+
+ bool
+ IsSameProcessActor();
+
+ // Only called by OpenDatabaseOp.
+ bool
+ CopyDatabaseMetadata();
+
+ void
+ SetActorAlive();
+
+ // Only called by TransactionBase.
+ virtual void
+ UpdateMetadata(nsresult aResult) MOZ_OVERRIDE;
+
+ // Only called by TransactionBase.
+ virtual bool
+ SendCompleteNotification(nsresult aResult) MOZ_OVERRIDE;
+
+ // IPDL methods are only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvDeleteMe() MOZ_OVERRIDE;
+
+ virtual bool
+ RecvCommit() MOZ_OVERRIDE;
+
+ virtual bool
+ RecvAbort(const nsresult& aResultCode) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvCreateObjectStore(const ObjectStoreMetadata& aMetadata) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvDeleteObjectStore(const int64_t& aObjectStoreId) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvCreateIndex(const int64_t& aObjectStoreId,
+ const IndexMetadata& aMetadata) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvDeleteIndex(const int64_t& aObjectStoreId,
+ const int64_t& aIndexId) MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBRequestParent*
+ AllocPBackgroundIDBRequestParent(const RequestParams& aParams) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent* aActor,
+ const RequestParams& aParams)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent* aActor)
+ MOZ_OVERRIDE;
+
+ virtual PBackgroundIDBCursorParent*
+ AllocPBackgroundIDBCursorParent(const OpenCursorParams& aParams) MOZ_OVERRIDE;
+
+ virtual bool
+ RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent* aActor,
+ const OpenCursorParams& aParams)
+ MOZ_OVERRIDE;
+
+ virtual bool
+ DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent* aActor)
+ MOZ_OVERRIDE;
+};
+
+class FactoryOp
+ : public DatabaseOperationBase
+ , public PBackgroundIDBFactoryRequestParent
+{
+public:
+ struct MaybeBlockedDatabaseInfo;
+
+protected:
+ enum State
+ {
+ // Just created on the PBackground thread, dispatched to the main thread.
+ // Next step is State_OpenPending.
+ State_Initial,
+
+ // Waiting for open allowed on the main thread. The next step is either
+ // State_SendingResults if permission is denied,
+ // State_PermissionChallenge if the permission is unknown, or
+ // State_DatabaseWorkOpen if permission is granted.
+ State_OpenPending,
+
+ // Sending a permission challenge message to the child on the PBackground
+ // thread. Next step is State_PermissionRetryReady.
+ State_PermissionChallenge,
+
+ // Retrying permission check after a challenge on the main thread. Next step
+ // is either State_SendingResults if permission is denied or
+ // State_DatabaseWorkOpen if permission is granted.
+ State_PermissionRetry,
+
+ // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
+ // either State_BeginVersionChange if the requested version doesn't match
+ // the existing database version or State_SendingResults if the versions
+ // match.
+ State_DatabaseWorkOpen,
+
+ // Starting a version change transaction or deleting a database on the
+ // PBackground thread. We need to notify other databases that a version
+ // change is about to happen, and maybe tell the request that a version
+ // change has been blocked. If databases are notified then the next step is
+ // State_WaitingForOtherDatabasesToClose. Otherwise the next step is
+ // State_DispatchToWorkThread.
+ State_BeginVersionChange,
+
+ // Waiting for other databases to close on the PBackground thread. This
+ // state may persist until all databases are closed. The next state is
+ // State_WaitingForTransactionsToComplete.
+ State_WaitingForOtherDatabasesToClose,
+
+ // Waiting for all transactions that could interfere with this operation to
+ // complete on the PBackground thread. Next state is
+ // State_DatabaseWorkVersionChange.
+ State_WaitingForTransactionsToComplete,
+
+ // Waiting to do/doing work on the "work thread". This involves waiting for
+ // the VersionChangeOp (OpenDatabaseOp and DeleteDatabaseOp each have a
+ // different implementation) to do its work. Eventually the state will
+ // transition to State_SendingResults.
+ State_DatabaseWorkVersionChange,