diff --git a/dom/base/StructuredCloneTags.h b/dom/base/StructuredCloneTags.h index 1500e684213c..a19305311187 100644 --- a/dom/base/StructuredCloneTags.h +++ b/dom/base/StructuredCloneTags.h @@ -27,6 +27,10 @@ enum StructuredCloneTags { SCTAG_DOM_MUTABLEFILE, SCTAG_DOM_FILE, + SCTAG_DOM_WASM, + + // New IDB tags go here! + // These tags are used for both main thread and workers. SCTAG_DOM_IMAGEDATA, SCTAG_DOM_MAP_MESSAGEPORT, @@ -56,6 +60,12 @@ enum StructuredCloneTags { // This tag is used by both main thread and workers. SCTAG_DOM_URLSEARCHPARAMS, + // When adding a new tag for IDB, please don't add it to the end of the list! + // Tags that are supported by IDB must not ever change. See the static assert + // in IDBObjectStore.cpp, method CommonStructuredCloneReadCallback. + // Adding to the end of the list would make removing of other tags harder in + // future. + SCTAG_DOM_MAX }; diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index 7ccb09ef46b1..a3a92a8a3c1d 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -8237,6 +8237,16 @@ struct ObjectStoreAddOrPutRequestOp::StoredFileInfo final aText.AppendInt(id); break; + case StructuredCloneFile::eWasmBytecode: + aText.Append('/'); + aText.AppendInt(id); + break; + + case StructuredCloneFile::eWasmCompiled: + aText.Append('\\'); + aText.AppendInt(id); + break; + default: MOZ_CRASH("Should never get here!"); } @@ -15097,6 +15107,22 @@ TransactionBase::VerifyRequestParams(const ObjectStoreAddPutParams& aParams) } case StructuredCloneFile::eStructuredClone: + ASSERT_UNLESS_FUZZING(); + return false; + + case StructuredCloneFile::eWasmBytecode: + case StructuredCloneFile::eWasmCompiled: + if (NS_WARN_IF(file.type() != + DatabaseOrMutableFile::TPBackgroundIDBDatabaseFileParent)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!file.get_PBackgroundIDBDatabaseFileParent())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + case StructuredCloneFile::eEndGuard: ASSERT_UNLESS_FUZZING(); return false; @@ -25545,7 +25571,9 @@ ObjectStoreAddOrPutRequestOp::Init(TransactionBase* aTransaction) const FileAddInfo& fileAddInfo = fileAddInfos[index]; MOZ_ASSERT(fileAddInfo.type() == StructuredCloneFile::eBlob || - fileAddInfo.type() == StructuredCloneFile::eMutableFile); + fileAddInfo.type() == StructuredCloneFile::eMutableFile || + fileAddInfo.type() == StructuredCloneFile::eWasmBytecode || + fileAddInfo.type() == StructuredCloneFile::eWasmCompiled); const DatabaseOrMutableFile& file = fileAddInfo.file(); @@ -25591,6 +25619,29 @@ ObjectStoreAddOrPutRequestOp::Init(TransactionBase* aTransaction) break; } + case StructuredCloneFile::eWasmBytecode: + case StructuredCloneFile::eWasmCompiled: { + MOZ_ASSERT(file.type() == + DatabaseOrMutableFile::TPBackgroundIDBDatabaseFileParent); + + storedFileInfo->mFileActor = + static_cast( + file.get_PBackgroundIDBDatabaseFileParent()); + MOZ_ASSERT(storedFileInfo->mFileActor); + + storedFileInfo->mFileInfo = storedFileInfo->mFileActor->GetFileInfo(); + MOZ_ASSERT(storedFileInfo->mFileInfo); + + storedFileInfo->mInputStream = + storedFileInfo->mFileActor->GetInputStream(); + if (storedFileInfo->mInputStream && !mFileManager) { + mFileManager = fileManager; + } + + storedFileInfo->mType = fileAddInfo.type(); + break; + } + default: MOZ_CRASH("Should never get here!"); } diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index c6d14be6378d..8e0a0e589d8f 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -323,6 +323,56 @@ StructuredCloneWriteCallback(JSContext* aCx, } } + if (JS::IsWasmModuleObject(aObj)) { + RefPtr module = JS::GetWasmModule(aObj); + MOZ_ASSERT(module); + + size_t bytecodeSize; + size_t compiledSize; + module->serializedSize(&bytecodeSize, &compiledSize); + + UniquePtr bytecode(new uint8_t[bytecodeSize]); + MOZ_ASSERT(bytecode); + + UniquePtr compiled(new uint8_t[compiledSize]); + MOZ_ASSERT(compiled); + + module->serialize(bytecode.get(), + bytecodeSize, + compiled.get(), + compiledSize); + + RefPtr blobImpl = + new BlobImplMemory(bytecode.release(), bytecodeSize, EmptyString()); + RefPtr bytecodeBlob = Blob::Create(nullptr, blobImpl); + + blobImpl = + new BlobImplMemory(compiled.release(), compiledSize, EmptyString()); + RefPtr compiledBlob = Blob::Create(nullptr, blobImpl); + + if (cloneWriteInfo->mFiles.Length() + 1 > size_t(UINT32_MAX)) { + MOZ_ASSERT(false, "Fix the structured clone data to use a bigger type!"); + return false; + } + + const uint32_t index = cloneWriteInfo->mFiles.Length(); + + if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_WASM, /* flags */ 0) || + !JS_WriteUint32Pair(aWriter, index, index + 1)) { + return false; + } + + StructuredCloneFile* newFile = cloneWriteInfo->mFiles.AppendElement(); + newFile->mBlob = bytecodeBlob; + newFile->mType = StructuredCloneFile::eWasmBytecode; + + newFile = cloneWriteInfo->mFiles.AppendElement(); + newFile->mBlob = compiledBlob; + newFile->mType = StructuredCloneFile::eWasmCompiled; + + return true; + } + return StructuredCloneHolder::WriteFullySerializableObjects(aCx, aWriter, aObj); } @@ -797,7 +847,8 @@ CommonStructuredCloneReadCallback(JSContext* aCx, static_assert(SCTAG_DOM_BLOB == 0xffff8001 && SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 && SCTAG_DOM_MUTABLEFILE == 0xffff8004 && - SCTAG_DOM_FILE == 0xffff8005, + SCTAG_DOM_FILE == 0xffff8005 && + SCTAG_DOM_WASM == 0xffff8006, "You changed our structured clone tag values and just ate " "everyone's IndexedDB data. I hope you are happy."); @@ -1320,6 +1371,25 @@ IDBObjectStore::AddOrPut(JSContext* aCx, break; } + case StructuredCloneFile::eWasmBytecode: + case StructuredCloneFile::eWasmCompiled: { + MOZ_ASSERT(file.mBlob); + MOZ_ASSERT(!file.mMutableFile); + + PBackgroundIDBDatabaseFileChild* fileActor = + database->GetOrCreateFileActorForBlob(file.mBlob); + if (NS_WARN_IF(!fileActor)) { + IDB_REPORT_INTERNAL_ERR(); + aRv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + return nullptr; + } + + fileAddInfo->file() = fileActor; + fileAddInfo->type() = file.mType; + + break; + } + default: MOZ_CRASH("Should never get here!"); } diff --git a/dom/indexedDB/IndexedDatabase.h b/dom/indexedDB/IndexedDatabase.h index da353bfa1a89..1238ad14eb97 100644 --- a/dom/indexedDB/IndexedDatabase.h +++ b/dom/indexedDB/IndexedDatabase.h @@ -29,6 +29,8 @@ struct StructuredCloneFile eBlob, eMutableFile, eStructuredClone, + eWasmBytecode, + eWasmCompiled, eEndGuard }; diff --git a/dom/indexedDB/test/file.js b/dom/indexedDB/test/file.js index 5986a9072136..c2974be2b405 100644 --- a/dom/indexedDB/test/file.js +++ b/dom/indexedDB/test/file.js @@ -85,6 +85,21 @@ function getNullFile(name, size) return getFile(name, "binary/null", getView(size)); } +function isWasmSupported() +{ + let testingFunctions = SpecialPowers.Cu.getJSTestingFunctions(); + return testingFunctions.wasmIsSupported(); +} + +function getWasmModule(text) +{ + let testingFunctions = SpecialPowers.Cu.getJSTestingFunctions(); + let wasmTextToBinary = SpecialPowers.unwrap(testingFunctions.wasmTextToBinary); + let binary = wasmTextToBinary(text); + let module = new WebAssembly.Module(binary); + return module; +} + function verifyBuffers(buffer1, buffer2) { ok(compareBuffers(buffer1, buffer2), "Correct buffer data"); diff --git a/dom/indexedDB/test/mochitest.ini b/dom/indexedDB/test/mochitest.ini index 0907bea658bf..a3683e08ed36 100644 --- a/dom/indexedDB/test/mochitest.ini +++ b/dom/indexedDB/test/mochitest.ini @@ -113,6 +113,7 @@ support-files = unit/test_transaction_ordering.js unit/test_unique_index_update.js unit/test_view_put_get_values.js + unit/test_wasm_put_get_values.js unit/test_writer_starvation.js [test_abort_deleted_index.html] @@ -390,5 +391,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_view_put_get_values.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_wasm_put_get_values.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_serviceworker.html] skip-if = buildapp == 'b2g' diff --git a/dom/indexedDB/test/test_wasm_put_get_values.html b/dom/indexedDB/test/test_wasm_put_get_values.html new file mode 100644 index 000000000000..5769ffdc2b9f --- /dev/null +++ b/dom/indexedDB/test/test_wasm_put_get_values.html @@ -0,0 +1,20 @@ + + + + Indexed Database Property Test + + + + + + + + + + + + + diff --git a/dom/indexedDB/test/unit/test_wasm_put_get_values.js b/dom/indexedDB/test/unit/test_wasm_put_get_values.js new file mode 100644 index 000000000000..2c3668b86fab --- /dev/null +++ b/dom/indexedDB/test/unit/test_wasm_put_get_values.js @@ -0,0 +1,68 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var disableWorkerTest = "Need a way to set temporary prefs from a worker"; + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = + this.window ? window.location.pathname : "test_wasm_put_get_values.js"; + + const objectStoreName = "Wasm"; + + const wasmData = { key: 1, wasm: null }; + + if (this.window) { + SpecialPowers.pushPrefEnv({ "set": [["javascript.options.wasm", true]] }, + continueToNextStep); + yield undefined; + } else { + enableWasm(); + } + + if (!isWasmSupported()) { + finishTest(); + yield undefined; + } + + info("Opening database"); + + let request = indexedDB.open(name); + request.onerror = errorHandler; + request.onupgradeneeded = continueToNextStepSync; + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + // upgradeneeded + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = continueToNextStepSync; + + info("Creating objectStore"); + + request.result.createObjectStore(objectStoreName); + + yield undefined; + + // success + let db = request.result; + db.onerror = errorHandler; + + info("Storing wasm"); + + wasmData.wasm = getWasmModule('(module (func (nop)))'); + + let objectStore = db.transaction([objectStoreName], "readwrite") + .objectStore(objectStoreName); + request = objectStore.add(wasmData.wasm, wasmData.key); + request.onsuccess = continueToNextStepSync; + yield undefined; + + is(request.result, wasmData.key, "Got correct key"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js index addaaefa9d77..b372befc271e 100644 --- a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js +++ b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js @@ -209,6 +209,11 @@ function resetTesting() SpecialPowers.clearUserPref("dom.indexedDB.testing"); } +function enableWasm() +{ + SpecialPowers.setBoolPref("javascript.options.wasm", true); +} + function gc() { Cu.forceGC(); @@ -363,6 +368,20 @@ function getFile(name, type, str) return new File([str], name, {type: type}); } +function isWasmSupported() +{ + let testingFunctions = Cu.getJSTestingFunctions(); + return testingFunctions.wasmIsSupported(); +} + +function getWasmModule(text) +{ + let testingFunctions = Cu.getJSTestingFunctions(); + let binary = testingFunctions.wasmTextToBinary(text); + let module = new WebAssembly.Module(binary); + return module; +} + function compareBuffers(buffer1, buffer2) { if (buffer1.byteLength != buffer2.byteLength) { diff --git a/dom/indexedDB/test/unit/xpcshell-parent-process.ini b/dom/indexedDB/test/unit/xpcshell-parent-process.ini index 31fc5af20aa3..3d270dfde227 100644 --- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini +++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini @@ -59,3 +59,4 @@ skip-if = true # bug 951017: intermittent failure on Android x86 emulator skip-if = os == "android" && processor == "x86" [test_view_put_get_values.js] +[test_wasm_put_get_values.js]