summaryrefslogtreecommitdiffstats
path: root/dom/indexedDB/IndexedDatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/indexedDB/IndexedDatabase.cpp')
-rw-r--r--dom/indexedDB/IndexedDatabase.cpp488
1 files changed, 488 insertions, 0 deletions
diff --git a/dom/indexedDB/IndexedDatabase.cpp b/dom/indexedDB/IndexedDatabase.cpp
new file mode 100644
index 0000000000..a73d9fbf5a
--- /dev/null
+++ b/dom/indexedDB/IndexedDatabase.cpp
@@ -0,0 +1,488 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 "mozilla/dom/IndexedDatabase.h"
+#include "IndexedDatabaseInlines.h"
+
+#include "IDBDatabase.h"
+
+#include "mozilla/dom/FileBlobImpl.h"
+#include "mozilla/dom/StructuredCloneTags.h"
+#include "mozilla/dom/URLSearchParams.h"
+#include "mozilla/dom/WorkerScope.h"
+#include "MainThreadUtils.h"
+#include "jsapi.h"
+#include "nsIFile.h"
+#include "nsIGlobalObject.h"
+#include "nsQueryObject.h"
+#include "nsString.h"
+
+namespace mozilla::dom::indexedDB {
+namespace {
+struct MOZ_STACK_CLASS MutableFileData final {
+ nsString type;
+ nsString name;
+
+ MOZ_COUNTED_DEFAULT_CTOR(MutableFileData)
+
+ MOZ_COUNTED_DTOR(MutableFileData)
+};
+
+struct MOZ_STACK_CLASS BlobOrFileData final {
+ uint32_t tag = 0;
+ uint64_t size = 0;
+ nsString type;
+ nsString name;
+ int64_t lastModifiedDate = INT64_MAX;
+
+ MOZ_COUNTED_DEFAULT_CTOR(BlobOrFileData)
+
+ MOZ_COUNTED_DTOR(BlobOrFileData)
+};
+
+struct MOZ_STACK_CLASS WasmModuleData final {
+ uint32_t bytecodeIndex;
+ uint32_t compiledIndex;
+ uint32_t flags;
+
+ explicit WasmModuleData(uint32_t aFlags)
+ : bytecodeIndex(0), compiledIndex(0), flags(aFlags) {
+ MOZ_COUNT_CTOR(WasmModuleData);
+ }
+
+ MOZ_COUNTED_DTOR(WasmModuleData)
+};
+
+bool StructuredCloneReadString(JSStructuredCloneReader* aReader,
+ nsCString& aString) {
+ uint32_t length;
+ if (!JS_ReadBytes(aReader, &length, sizeof(uint32_t))) {
+ NS_WARNING("Failed to read length!");
+ return false;
+ }
+ length = NativeEndian::swapFromLittleEndian(length);
+
+ if (!aString.SetLength(length, fallible)) {
+ NS_WARNING("Out of memory?");
+ return false;
+ }
+ char* const buffer = aString.BeginWriting();
+
+ if (!JS_ReadBytes(aReader, buffer, length)) {
+ NS_WARNING("Failed to read type!");
+ return false;
+ }
+
+ return true;
+}
+
+bool ReadFileHandle(JSStructuredCloneReader* aReader,
+ MutableFileData* aRetval) {
+ static_assert(SCTAG_DOM_MUTABLEFILE == 0xFFFF8004, "Update me!");
+ MOZ_ASSERT(aReader && aRetval);
+
+ nsCString type;
+ if (!StructuredCloneReadString(aReader, type)) {
+ return false;
+ }
+ CopyUTF8toUTF16(type, aRetval->type);
+
+ nsCString name;
+ if (!StructuredCloneReadString(aReader, name)) {
+ return false;
+ }
+ CopyUTF8toUTF16(name, aRetval->name);
+
+ return true;
+}
+
+bool ReadBlobOrFile(JSStructuredCloneReader* aReader, uint32_t aTag,
+ BlobOrFileData* aRetval) {
+ static_assert(SCTAG_DOM_BLOB == 0xffff8001 &&
+ SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 &&
+ SCTAG_DOM_FILE == 0xffff8005,
+ "Update me!");
+
+ MOZ_ASSERT(aReader);
+ MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
+ aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
+ aTag == SCTAG_DOM_BLOB);
+ MOZ_ASSERT(aRetval);
+
+ aRetval->tag = aTag;
+
+ uint64_t size;
+ if (NS_WARN_IF(!JS_ReadBytes(aReader, &size, sizeof(uint64_t)))) {
+ return false;
+ }
+
+ aRetval->size = NativeEndian::swapFromLittleEndian(size);
+
+ nsCString type;
+ if (NS_WARN_IF(!StructuredCloneReadString(aReader, type))) {
+ return false;
+ }
+
+ CopyUTF8toUTF16(type, aRetval->type);
+
+ // Blobs are done.
+ if (aTag == SCTAG_DOM_BLOB) {
+ return true;
+ }
+
+ MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
+ aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE);
+
+ int64_t lastModifiedDate;
+ if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE) {
+ lastModifiedDate = INT64_MAX;
+ } else {
+ if (NS_WARN_IF(!JS_ReadBytes(aReader, &lastModifiedDate,
+ sizeof(lastModifiedDate)))) {
+ return false;
+ }
+ lastModifiedDate = NativeEndian::swapFromLittleEndian(lastModifiedDate);
+ }
+
+ aRetval->lastModifiedDate = lastModifiedDate;
+
+ nsCString name;
+ if (NS_WARN_IF(!StructuredCloneReadString(aReader, name))) {
+ return false;
+ }
+
+ CopyUTF8toUTF16(name, aRetval->name);
+
+ return true;
+}
+
+bool ReadWasmModule(JSStructuredCloneReader* aReader, WasmModuleData* aRetval) {
+ static_assert(SCTAG_DOM_WASM_MODULE == 0xFFFF8006, "Update me!");
+ MOZ_ASSERT(aReader && aRetval);
+
+ uint32_t bytecodeIndex;
+ uint32_t compiledIndex;
+ if (NS_WARN_IF(!JS_ReadUint32Pair(aReader, &bytecodeIndex, &compiledIndex))) {
+ return false;
+ }
+
+ aRetval->bytecodeIndex = bytecodeIndex;
+ aRetval->compiledIndex = compiledIndex;
+
+ return true;
+}
+
+template <typename StructuredCloneFile>
+class ValueDeserializationHelper;
+
+class ValueDeserializationHelperBase {
+ public:
+ static bool CreateAndWrapWasmModule(JSContext* aCx,
+ const StructuredCloneFileBase& aFile,
+ const WasmModuleData& aData,
+ JS::MutableHandle<JSObject*> aResult) {
+ MOZ_ASSERT(aCx);
+ MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eWasmBytecode);
+
+ // Both on the parent and child side, just create a plain object here,
+ // support for de-serialization of WebAssembly.Modules has been removed in
+ // bug 1561876. Full removal is tracked in bug 1487479.
+
+ JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
+ if (NS_WARN_IF(!obj)) {
+ return false;
+ }
+
+ aResult.set(obj);
+ return true;
+ }
+
+ template <typename StructuredCloneFile>
+ static bool CreateAndWrapBlobOrFile(JSContext* aCx, IDBDatabase* aDatabase,
+ const StructuredCloneFile& aFile,
+ const BlobOrFileData& aData,
+ JS::MutableHandle<JSObject*> aResult) {
+ MOZ_ASSERT(aCx);
+ MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
+ aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
+ aData.tag == SCTAG_DOM_BLOB);
+ MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eBlob);
+
+ const auto blob = ValueDeserializationHelper<StructuredCloneFile>::GetBlob(
+ aCx, aDatabase, aFile);
+ if (NS_WARN_IF(!blob)) {
+ return false;
+ }
+
+ if (aData.tag == SCTAG_DOM_BLOB) {
+ blob->Impl()->SetLazyData(VoidString(), aData.type, aData.size,
+ INT64_MAX);
+ MOZ_ASSERT(!blob->IsFile());
+
+ // XXX The comment below is somewhat confusing, since it seems to imply
+ // that this branch is only executed when called from ActorsParent, but
+ // it's executed from both the parent and the child side code.
+
+ // ActorsParent sends here a kind of half blob and half file wrapped into
+ // a DOM File object. DOM File and DOM Blob are a WebIDL wrapper around a
+ // BlobImpl object. SetLazyData() has just changed the BlobImpl to be a
+ // Blob (see the previous assert), but 'blob' still has the WebIDL DOM
+ // File wrapping.
+ // Before exposing it to content, we must recreate a DOM Blob object.
+
+ const RefPtr<Blob> exposedBlob =
+ Blob::Create(blob->GetParentObject(), blob->Impl());
+ if (NS_WARN_IF(!exposedBlob)) {
+ return false;
+ }
+
+ return WrapAsJSObject(aCx, exposedBlob, aResult);
+ }
+
+ blob->Impl()->SetLazyData(aData.name, aData.type, aData.size,
+ aData.lastModifiedDate * PR_USEC_PER_MSEC);
+
+ MOZ_ASSERT(blob->IsFile());
+ const RefPtr<File> file = blob->ToFile();
+ MOZ_ASSERT(file);
+
+ return WrapAsJSObject(aCx, file, aResult);
+ }
+};
+
+template <>
+class ValueDeserializationHelper<StructuredCloneFileParent>
+ : public ValueDeserializationHelperBase {
+ public:
+ static bool CreateAndWrapMutableFile(JSContext* aCx,
+ StructuredCloneFileParent& aFile,
+ const MutableFileData& aData,
+ JS::MutableHandle<JSObject*> aResult) {
+ MOZ_ASSERT(aCx);
+ MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eBlob);
+
+ // We are in an IDB SQLite schema upgrade where we don't care about a real
+ // 'MutableFile', but we just care of having a proper |mType| flag.
+
+ aFile.MutateType(StructuredCloneFileBase::eMutableFile);
+
+ // Just make a dummy object.
+ JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
+
+ if (NS_WARN_IF(!obj)) {
+ return false;
+ }
+
+ aResult.set(obj);
+ return true;
+ }
+
+ static RefPtr<Blob> GetBlob(JSContext* aCx, IDBDatabase* aDatabase,
+ const StructuredCloneFileParent& aFile) {
+ // This is chrome code, so there is no parent, but still we want to set a
+ // correct parent for the new File object.
+ const auto global = [aDatabase, aCx]() -> nsCOMPtr<nsIGlobalObject> {
+ if (NS_IsMainThread()) {
+ if (aDatabase && aDatabase->GetParentObject()) {
+ return aDatabase->GetParentObject();
+ }
+ return xpc::CurrentNativeGlobal(aCx);
+ }
+ const WorkerPrivate* const workerPrivate =
+ GetCurrentThreadWorkerPrivate();
+ MOZ_ASSERT(workerPrivate);
+
+ WorkerGlobalScope* const globalScope = workerPrivate->GlobalScope();
+ MOZ_ASSERT(globalScope);
+
+ return do_QueryObject(globalScope);
+ }();
+
+ MOZ_ASSERT(global);
+
+ // We do not have an mBlob but do have a DatabaseFileInfo.
+ //
+ // If we are creating an index, we do need a real-looking Blob/File instance
+ // because the index's key path can reference their properties. Rather than
+ // create a fake-looking object, create a real Blob.
+ //
+ // If we are in a schema upgrade, we don't strictly need that, but we do not
+ // need to optimize for that, and create it anyway.
+ const nsCOMPtr<nsIFile> file = aFile.FileInfo().GetFileForFileInfo();
+ if (!file) {
+ return nullptr;
+ }
+
+ const auto impl = MakeRefPtr<FileBlobImpl>(file);
+ impl->SetFileId(aFile.FileInfo().Id());
+ return File::Create(global, impl);
+ }
+};
+
+template <>
+class ValueDeserializationHelper<StructuredCloneFileChild>
+ : public ValueDeserializationHelperBase {
+ public:
+ static bool CreateAndWrapMutableFile(JSContext* aCx,
+ StructuredCloneFileChild& aFile,
+ const MutableFileData& aData,
+ JS::MutableHandle<JSObject*> aResult) {
+ MOZ_ASSERT(aCx);
+ MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eMutableFile);
+
+ return false;
+ }
+
+ static RefPtr<Blob> GetBlob(JSContext* aCx, IDBDatabase* aDatabase,
+ const StructuredCloneFileChild& aFile) {
+ if (aFile.HasBlob()) {
+ return aFile.BlobPtr();
+ }
+
+ MOZ_CRASH("Expected a StructuredCloneFile with a Blob");
+ }
+};
+
+} // namespace
+
+template <typename StructuredCloneReadInfo>
+JSObject* CommonStructuredCloneReadCallback(
+ JSContext* aCx, JSStructuredCloneReader* aReader,
+ const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, uint32_t aData,
+ StructuredCloneReadInfo* aCloneReadInfo, IDBDatabase* aDatabase) {
+ // We need to statically assert that our tag values are what we expect
+ // so that if people accidentally change them they notice.
+ static_assert(SCTAG_DOM_BLOB == 0xffff8001 &&
+ SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 &&
+ SCTAG_DOM_MUTABLEFILE == 0xffff8004 &&
+ SCTAG_DOM_FILE == 0xffff8005 &&
+ SCTAG_DOM_WASM_MODULE == 0xffff8006 &&
+ SCTAG_DOM_URLSEARCHPARAMS == 0xffff8014,
+ "You changed our structured clone tag values and just ate "
+ "everyone's IndexedDB data. I hope you are happy.");
+
+ if (aTag == SCTAG_DOM_URLSEARCHPARAMS) {
+ // Protect the result from a moving GC in ~RefPtr.
+ JS::Rooted<JSObject*> result(aCx);
+
+ {
+ // Scope for the RefPtr below.
+
+ nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
+ if (!global) {
+ return nullptr;
+ }
+
+ RefPtr<URLSearchParams> params = new URLSearchParams(global);
+
+ uint32_t paramCount;
+ uint32_t zero;
+ if (!JS_ReadUint32Pair(aReader, &paramCount, &zero)) {
+ return nullptr;
+ }
+
+ nsAutoString key;
+ nsAutoString value;
+ for (uint32_t index = 0; index < paramCount; index++) {
+ if (!StructuredCloneHolder::ReadString(aReader, key) ||
+ !StructuredCloneHolder::ReadString(aReader, value)) {
+ return nullptr;
+ }
+ params->Append(key, value);
+ }
+
+ if (!WrapAsJSObject(aCx, params, &result)) {
+ return nullptr;
+ }
+ }
+
+ return result;
+ }
+
+ using StructuredCloneFile =
+ typename StructuredCloneReadInfo::StructuredCloneFile;
+
+ if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
+ aTag == SCTAG_DOM_BLOB || aTag == SCTAG_DOM_FILE ||
+ aTag == SCTAG_DOM_MUTABLEFILE || aTag == SCTAG_DOM_WASM_MODULE) {
+ JS::Rooted<JSObject*> result(aCx);
+
+ if (aTag == SCTAG_DOM_WASM_MODULE) {
+ WasmModuleData data(aData);
+ if (NS_WARN_IF(!ReadWasmModule(aReader, &data))) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(data.compiledIndex == data.bytecodeIndex + 1);
+ MOZ_ASSERT(!data.flags);
+
+ const auto& files = aCloneReadInfo->Files();
+ if (data.bytecodeIndex >= files.Length() ||
+ data.compiledIndex >= files.Length()) {
+ MOZ_ASSERT(false, "Bad index value!");
+ return nullptr;
+ }
+
+ const auto& file = files[data.bytecodeIndex];
+
+ if (NS_WARN_IF(!ValueDeserializationHelper<StructuredCloneFile>::
+ CreateAndWrapWasmModule(aCx, file, data, &result))) {
+ return nullptr;
+ }
+
+ return result;
+ }
+
+ if (aData >= aCloneReadInfo->Files().Length()) {
+ MOZ_ASSERT(false, "Bad index value!");
+ return nullptr;
+ }
+
+ auto& file = aCloneReadInfo->MutableFile(aData);
+
+ if (aTag == SCTAG_DOM_MUTABLEFILE) {
+ MutableFileData data;
+ if (NS_WARN_IF(!ReadFileHandle(aReader, &data))) {
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(!ValueDeserializationHelper<StructuredCloneFile>::
+ CreateAndWrapMutableFile(aCx, file, data, &result))) {
+ return nullptr;
+ }
+
+ return result;
+ }
+
+ BlobOrFileData data;
+ if (NS_WARN_IF(!ReadBlobOrFile(aReader, aTag, &data))) {
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(!ValueDeserializationHelper<
+ StructuredCloneFile>::CreateAndWrapBlobOrFile(aCx, aDatabase,
+ file, data,
+ &result))) {
+ return nullptr;
+ }
+
+ return result;
+ }
+
+ return StructuredCloneHolder::ReadFullySerializableObjects(aCx, aReader, aTag,
+ true);
+}
+
+template JSObject* CommonStructuredCloneReadCallback(
+ JSContext* aCx, JSStructuredCloneReader* aReader,
+ const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, uint32_t aData,
+ StructuredCloneReadInfoChild* aCloneReadInfo, IDBDatabase* aDatabase);
+
+template JSObject* CommonStructuredCloneReadCallback(
+ JSContext* aCx, JSStructuredCloneReader* aReader,
+ const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, uint32_t aData,
+ StructuredCloneReadInfoParent* aCloneReadInfo, IDBDatabase* aDatabase);
+} // namespace mozilla::dom::indexedDB