зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1312808 - rewrite stored WebAssembly.Module when build-id changes; r=asuth,luke
This commit is contained in:
Родитель
b2e95c2ac2
Коммит
d80cf26c97
|
@ -689,7 +689,8 @@ DeserializeStructuredCloneFiles(
|
|||
break;
|
||||
}
|
||||
|
||||
case StructuredCloneFile::eWasmBytecode: {
|
||||
case StructuredCloneFile::eWasmBytecode:
|
||||
case StructuredCloneFile::eWasmCompiled: {
|
||||
if (aModuleSet) {
|
||||
MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t);
|
||||
|
||||
|
@ -701,6 +702,10 @@ DeserializeStructuredCloneFiles(
|
|||
MOZ_ASSERT(moduleIndex < aModuleSet->Length());
|
||||
file->mWasmModule = aModuleSet->ElementAt(moduleIndex);
|
||||
|
||||
if (serializedFile.type() == StructuredCloneFile::eWasmCompiled) {
|
||||
moduleIndex++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -720,68 +725,12 @@ DeserializeStructuredCloneFiles(
|
|||
StructuredCloneFile* file = aFiles.AppendElement();
|
||||
MOZ_ASSERT(file);
|
||||
|
||||
file->mType = StructuredCloneFile::eWasmBytecode;
|
||||
file->mType = serializedFile.type();
|
||||
file->mBlob.swap(blob);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case StructuredCloneFile::eWasmCompiled: {
|
||||
if (aModuleSet) {
|
||||
MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t);
|
||||
|
||||
StructuredCloneFile* file = aFiles.AppendElement();
|
||||
MOZ_ASSERT(file);
|
||||
|
||||
file->mType = serializedFile.type();
|
||||
|
||||
MOZ_ASSERT(moduleIndex < aModuleSet->Length());
|
||||
file->mWasmModule = aModuleSet->ElementAt(moduleIndex++);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t ||
|
||||
blobOrMutableFile.type() ==
|
||||
BlobOrMutableFile::TPBlobChild);
|
||||
|
||||
switch (blobOrMutableFile.type()) {
|
||||
case BlobOrMutableFile::Tnull_t: {
|
||||
StructuredCloneFile* file = aFiles.AppendElement();
|
||||
MOZ_ASSERT(file);
|
||||
|
||||
file->mType = StructuredCloneFile::eWasmCompiled;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BlobOrMutableFile::TPBlobChild: {
|
||||
auto* actor =
|
||||
static_cast<BlobChild*>(blobOrMutableFile.get_PBlobChild());
|
||||
|
||||
RefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
RefPtr<Blob> blob = Blob::Create(aDatabase->GetOwner(), blobImpl);
|
||||
|
||||
aDatabase->NoteReceivedBlob(blob);
|
||||
|
||||
StructuredCloneFile* file = aFiles.AppendElement();
|
||||
MOZ_ASSERT(file);
|
||||
|
||||
file->mType = StructuredCloneFile::eWasmCompiled;
|
||||
file->mBlob.swap(blob);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Should never get here!");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Should never get here!");
|
||||
}
|
||||
|
@ -3015,6 +2964,7 @@ PreprocessHelper::Init(const nsTArray<StructuredCloneFile>& aFiles)
|
|||
MOZ_ASSERT(bytecodeFile.mType == StructuredCloneFile::eWasmBytecode);
|
||||
MOZ_ASSERT(bytecodeFile.mBlob);
|
||||
MOZ_ASSERT(compiledFile.mType == StructuredCloneFile::eWasmCompiled);
|
||||
MOZ_ASSERT(compiledFile.mBlob);
|
||||
|
||||
ErrorResult errorResult;
|
||||
|
||||
|
@ -3026,12 +2976,10 @@ PreprocessHelper::Init(const nsTArray<StructuredCloneFile>& aFiles)
|
|||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> compiledStream;
|
||||
if (compiledFile.mBlob) {
|
||||
compiledFile.mBlob->GetInternalStream(getter_AddRefs(compiledStream),
|
||||
errorResult);
|
||||
if (NS_WARN_IF(errorResult.Failed())) {
|
||||
return errorResult.StealNSResult();
|
||||
}
|
||||
compiledFile.mBlob->GetInternalStream(getter_AddRefs(compiledStream),
|
||||
errorResult);
|
||||
if (NS_WARN_IF(errorResult.Failed())) {
|
||||
return errorResult.StealNSResult();
|
||||
}
|
||||
|
||||
streamPairs.AppendElement(StreamPair(bytecodeStream, compiledStream));
|
||||
|
@ -3104,14 +3052,11 @@ PreprocessHelper::RunOnStreamTransportThread()
|
|||
|
||||
const nsCOMPtr<nsIInputStream>& compiledStream = streamPair.second;
|
||||
|
||||
PRFileDesc* compiledFileDesc;
|
||||
if (compiledStream) {
|
||||
compiledFileDesc = GetFileDescriptorFromStream(compiledStream);
|
||||
if (NS_WARN_IF(!bytecodeFileDesc)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else {
|
||||
compiledFileDesc = nullptr;
|
||||
MOZ_ASSERT(compiledStream);
|
||||
|
||||
PRFileDesc* compiledFileDesc = GetFileDescriptorFromStream(compiledStream);
|
||||
if (NS_WARN_IF(!compiledFileDesc)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JS::BuildIdCharVector buildId;
|
||||
|
|
|
@ -92,6 +92,7 @@
|
|||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "nsThreadPool.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXPCOMCID.h"
|
||||
|
@ -8196,12 +8197,6 @@ private:
|
|||
nsresult
|
||||
RemoveOldIndexDataValues(DatabaseConnection* aConnection);
|
||||
|
||||
nsresult
|
||||
CopyFileData(nsIInputStream* aInputStream,
|
||||
nsIOutputStream* aOutputStream,
|
||||
char* aBuffer,
|
||||
uint32_t aBufferSize);
|
||||
|
||||
bool
|
||||
Init(TransactionBase* aTransaction) override;
|
||||
|
||||
|
@ -9587,6 +9582,60 @@ private:
|
|||
|
||||
#endif // DEBUG
|
||||
|
||||
/*******************************************************************************
|
||||
* Helper classes
|
||||
******************************************************************************/
|
||||
|
||||
class MOZ_STACK_CLASS FileHelper final
|
||||
{
|
||||
RefPtr<FileManager> mFileManager;
|
||||
|
||||
nsCOMPtr<nsIFile> mFileDirectory;
|
||||
nsCOMPtr<nsIFile> mJournalDirectory;
|
||||
|
||||
public:
|
||||
explicit FileHelper(FileManager* aFileManager)
|
||||
: mFileManager(aFileManager)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
Init();
|
||||
|
||||
already_AddRefed<nsIFile>
|
||||
GetFile(FileInfo* aFileInfo);
|
||||
|
||||
already_AddRefed<nsIFile>
|
||||
GetCheckedFile(FileInfo* aFileInfo);
|
||||
|
||||
already_AddRefed<nsIFile>
|
||||
GetJournalFile(FileInfo* aFileInfo);
|
||||
|
||||
nsresult
|
||||
CreateFileFromStream(nsIFile* aFile,
|
||||
nsIFile* aJournalFile,
|
||||
nsIInputStream* aInputStream,
|
||||
bool aCompress);
|
||||
|
||||
nsresult
|
||||
ReplaceFile(nsIFile* aFile,
|
||||
nsIFile* aNewFile,
|
||||
nsIFile* aNewJournalFile);
|
||||
|
||||
nsresult
|
||||
RemoveFile(nsIFile* aFile,
|
||||
nsIFile* aJournalFile);
|
||||
|
||||
already_AddRefed<FileInfo>
|
||||
GetNewFileInfo();
|
||||
|
||||
private:
|
||||
nsresult
|
||||
SyncCopy(nsIInputStream* aInputStream,
|
||||
nsIOutputStream* aOutputStream,
|
||||
char* aBuffer,
|
||||
uint32_t aBufferSize);
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Helper Functions
|
||||
******************************************************************************/
|
||||
|
@ -9651,6 +9700,139 @@ DeserializeStructuredCloneFile(FileManager* aFileManager,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CheckWasmModule(FileHelper* aFileHelper,
|
||||
StructuredCloneFile* aBytecodeFile,
|
||||
StructuredCloneFile* aCompiledFile)
|
||||
{
|
||||
MOZ_ASSERT(!IsOnBackgroundThread());
|
||||
MOZ_ASSERT(aFileHelper);
|
||||
MOZ_ASSERT(aBytecodeFile);
|
||||
MOZ_ASSERT(aCompiledFile);
|
||||
MOZ_ASSERT(aBytecodeFile->mType == StructuredCloneFile::eWasmBytecode);
|
||||
MOZ_ASSERT(aCompiledFile->mType == StructuredCloneFile::eWasmCompiled);
|
||||
|
||||
nsCOMPtr<nsIFile> compiledFile =
|
||||
aFileHelper->GetCheckedFile(aCompiledFile->mFileInfo);
|
||||
if (NS_WARN_IF(!compiledFile)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
||||
bool match;
|
||||
{
|
||||
ScopedPRFileDesc compiledFileDesc;
|
||||
rv = compiledFile->OpenNSPRFileDesc(PR_RDONLY,
|
||||
0644,
|
||||
&compiledFileDesc.rwget());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
JS::BuildIdCharVector buildId;
|
||||
bool ok = GetBuildId(&buildId);
|
||||
if (NS_WARN_IF(!ok)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
match = JS::CompiledWasmModuleAssumptionsMatch(compiledFileDesc,
|
||||
Move(buildId));
|
||||
}
|
||||
if (match) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Re-compile the module. It would be preferable to do this in the child
|
||||
// (content process) instead of here in the parent, but that would be way more
|
||||
// complex and without significant memory allocation or security benefits.
|
||||
// See the discussion starting from
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1312808#c9 for more details.
|
||||
nsCOMPtr<nsIFile> bytecodeFile =
|
||||
aFileHelper->GetCheckedFile(aBytecodeFile->mFileInfo);
|
||||
if (NS_WARN_IF(!bytecodeFile)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
ScopedPRFileDesc bytecodeFileDesc;
|
||||
rv = bytecodeFile->OpenNSPRFileDesc(PR_RDONLY,
|
||||
0644,
|
||||
&bytecodeFileDesc.rwget());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
JS::BuildIdCharVector buildId;
|
||||
bool ok = GetBuildId(&buildId);
|
||||
if (NS_WARN_IF(!ok)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<JS::WasmModule> module = JS::DeserializeWasmModule(bytecodeFileDesc,
|
||||
nullptr,
|
||||
Move(buildId),
|
||||
nullptr,
|
||||
0,
|
||||
0);
|
||||
if (NS_WARN_IF(!module)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
size_t compiledSize;
|
||||
module->serializedSize(nullptr, &compiledSize);
|
||||
|
||||
UniquePtr<uint8_t[]> compiled(new (fallible) uint8_t[compiledSize]);
|
||||
if (NS_WARN_IF(!compiled)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
module->serialize(nullptr, 0, compiled.get(), compiledSize);
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
rv = NS_NewByteInputStream(getter_AddRefs(inputStream),
|
||||
reinterpret_cast<const char*>(compiled.get()),
|
||||
compiledSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
RefPtr<FileInfo> newFileInfo = aFileHelper->GetNewFileInfo();
|
||||
|
||||
nsCOMPtr<nsIFile> newFile = aFileHelper->GetFile(newFileInfo);
|
||||
if (NS_WARN_IF(!newFile)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> newJournalFile =
|
||||
aFileHelper->GetJournalFile(newFileInfo);
|
||||
if (NS_WARN_IF(!newJournalFile)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = aFileHelper->CreateFileFromStream(newFile,
|
||||
newJournalFile,
|
||||
inputStream,
|
||||
/* aCompress */ false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
nsresult rv2 = aFileHelper->RemoveFile(newFile, newJournalFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv2))) {
|
||||
return rv;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = aFileHelper->ReplaceFile(compiledFile, newFile, newJournalFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
nsresult rv2 = aFileHelper->RemoveFile(newFile, newJournalFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv2))) {
|
||||
return rv;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeserializeStructuredCloneFiles(FileManager* aFileManager,
|
||||
const nsAString& aText,
|
||||
|
@ -9658,14 +9840,13 @@ DeserializeStructuredCloneFiles(FileManager* aFileManager,
|
|||
bool* aHasPreprocessInfo)
|
||||
{
|
||||
MOZ_ASSERT(!IsOnBackgroundThread());
|
||||
MOZ_ASSERT(aHasPreprocessInfo);
|
||||
|
||||
nsCharSeparatedTokenizerTemplate<TokenizerIgnoreNothing>
|
||||
tokenizer(aText, ' ');
|
||||
|
||||
nsAutoString token;
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIFile> directory;
|
||||
Maybe<FileHelper> fileHelper;
|
||||
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
token = tokenizer.nextToken();
|
||||
|
@ -9677,44 +9858,34 @@ DeserializeStructuredCloneFiles(FileManager* aFileManager,
|
|||
return rv;
|
||||
}
|
||||
|
||||
if (file->mType == StructuredCloneFile::eWasmBytecode) {
|
||||
MOZ_ASSERT(file->mValid);
|
||||
if (!aHasPreprocessInfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file->mType == StructuredCloneFile::eWasmBytecode) {
|
||||
*aHasPreprocessInfo = true;
|
||||
}
|
||||
else if (file->mType == StructuredCloneFile::eWasmCompiled) {
|
||||
if (!directory) {
|
||||
directory = aFileManager->GetCheckedDirectory();
|
||||
if (NS_WARN_IF(!directory)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
if (fileHelper.isNothing()) {
|
||||
fileHelper.emplace(aFileManager);
|
||||
|
||||
rv = fileHelper->Init();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
const int64_t fileId = file->mFileInfo->Id();
|
||||
MOZ_ASSERT(fileId > 0);
|
||||
MOZ_ASSERT(aResult.Length() > 1);
|
||||
MOZ_ASSERT(aResult[aResult.Length() - 2].mType ==
|
||||
StructuredCloneFile::eWasmBytecode);
|
||||
|
||||
nsCOMPtr<nsIFile> nativeFile =
|
||||
aFileManager->GetCheckedFileForId(directory, fileId);
|
||||
if (NS_WARN_IF(!nativeFile)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
StructuredCloneFile* previousFile = &aResult[aResult.Length() - 2];
|
||||
|
||||
ScopedPRFileDesc fileDesc;
|
||||
rv = nativeFile->OpenNSPRFileDesc(PR_RDONLY, 0644, &fileDesc.rwget());
|
||||
rv = CheckWasmModule(fileHelper.ptr(), previousFile, file);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
JS::BuildIdCharVector buildId;
|
||||
bool ok = GetBuildId(&buildId);
|
||||
if (NS_WARN_IF(!ok)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(file->mValid);
|
||||
file->mValid = JS::CompiledWasmModuleAssumptionsMatch(fileDesc,
|
||||
Move(buildId));
|
||||
|
||||
*aHasPreprocessInfo = true;
|
||||
}
|
||||
}
|
||||
|
@ -9865,7 +10036,7 @@ SerializeStructuredCloneFiles(
|
|||
|
||||
case StructuredCloneFile::eWasmBytecode:
|
||||
case StructuredCloneFile::eWasmCompiled: {
|
||||
if (!aForPreprocess || !file.mValid) {
|
||||
if (!aForPreprocess) {
|
||||
SerializedStructuredCloneFile* serializedFile =
|
||||
aResult.AppendElement(fallible);
|
||||
MOZ_ASSERT(serializedFile);
|
||||
|
@ -11480,8 +11651,7 @@ UpdateRefcountFunction::ProcessValue(mozIStorageValueArray* aValues,
|
|||
}
|
||||
|
||||
nsTArray<StructuredCloneFile> files;
|
||||
bool dummy;
|
||||
rv = DeserializeStructuredCloneFiles(mFileManager, ids, files, &dummy);
|
||||
rv = DeserializeStructuredCloneFiles(mFileManager, ids, files, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -25800,63 +25970,6 @@ ObjectStoreAddOrPutRequestOp::RemoveOldIndexDataValues(
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ObjectStoreAddOrPutRequestOp::CopyFileData(nsIInputStream* aInputStream,
|
||||
nsIOutputStream* aOutputStream,
|
||||
char* aBuffer,
|
||||
uint32_t aBufferSize)
|
||||
{
|
||||
AssertIsOnConnectionThread();
|
||||
MOZ_ASSERT(aInputStream);
|
||||
MOZ_ASSERT(aOutputStream);
|
||||
|
||||
PROFILER_LABEL("IndexedDB",
|
||||
"ObjectStoreAddOrPutRequestOp::CopyFileData",
|
||||
js::ProfileEntry::Category::STORAGE);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
do {
|
||||
uint32_t numRead;
|
||||
rv = aInputStream->Read(aBuffer, aBufferSize, &numRead);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!numRead) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t numWrite;
|
||||
rv = aOutputStream->Write(aBuffer, numRead, &numWrite);
|
||||
if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
|
||||
rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(numWrite != numRead)) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = aOutputStream->Flush();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv2 = aOutputStream->Close();
|
||||
if (NS_WARN_IF(NS_FAILED(rv2))) {
|
||||
return NS_SUCCEEDED(rv) ? rv2 : rv;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
ObjectStoreAddOrPutRequestOp::Init(TransactionBase* aTransaction)
|
||||
{
|
||||
|
@ -26197,35 +26310,16 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> fileDirectory;
|
||||
nsCOMPtr<nsIFile> journalDirectory;
|
||||
Maybe<FileHelper> fileHelper;
|
||||
|
||||
if (mFileManager) {
|
||||
fileDirectory = mFileManager->GetDirectory();
|
||||
if (NS_WARN_IF(!fileDirectory)) {
|
||||
fileHelper.emplace(mFileManager);
|
||||
|
||||
rv = fileHelper->Init();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
journalDirectory = mFileManager->EnsureJournalDirectory();
|
||||
if (NS_WARN_IF(!journalDirectory)) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
DebugOnly<bool> exists;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(fileDirectory->Exists(&exists)));
|
||||
MOZ_ASSERT(exists);
|
||||
|
||||
DebugOnly<bool> isDirectory;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(fileDirectory->IsDirectory(&isDirectory)));
|
||||
MOZ_ASSERT(isDirectory);
|
||||
|
||||
MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->Exists(&exists)));
|
||||
MOZ_ASSERT(exists);
|
||||
|
||||
MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->IsDirectory(&isDirectory)));
|
||||
MOZ_ASSERT(isDirectory);
|
||||
}
|
||||
|
||||
if (!mStoredFileInfos.IsEmpty()) {
|
||||
|
@ -26237,151 +26331,47 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
StoredFileInfo& storedFileInfo = mStoredFileInfos[index];
|
||||
MOZ_ASSERT(storedFileInfo.mFileInfo);
|
||||
|
||||
const int64_t id = storedFileInfo.mFileInfo->Id();
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
storedFileInfo.mInputStream.swap(inputStream);
|
||||
|
||||
if (inputStream) {
|
||||
MOZ_ASSERT(fileDirectory);
|
||||
MOZ_ASSERT(journalDirectory);
|
||||
RefPtr<FileInfo>& fileInfo = storedFileInfo.mFileInfo;
|
||||
|
||||
nsCOMPtr<nsIFile> diskFile =
|
||||
mFileManager->GetFileForId(fileDirectory, id);
|
||||
if (NS_WARN_IF(!diskFile)) {
|
||||
nsCOMPtr<nsIFile> file = fileHelper->GetFile(fileInfo);
|
||||
if (NS_WARN_IF(!file)) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
bool exists;
|
||||
rv = diskFile->Exists(&exists);
|
||||
nsCOMPtr<nsIFile> journalFile =
|
||||
fileHelper->GetJournalFile(fileInfo);
|
||||
if (NS_WARN_IF(!journalFile)) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
bool compress =
|
||||
storedFileInfo.mType == StructuredCloneFile::eStructuredClone;
|
||||
|
||||
rv = fileHelper->CreateFileFromStream(file,
|
||||
journalFile,
|
||||
inputStream,
|
||||
compress);
|
||||
if (NS_FAILED(rv) &&
|
||||
NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
if (exists) {
|
||||
bool isFile;
|
||||
rv = diskFile->IsFile(&isFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!isFile)) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
uint64_t inputStreamSize;
|
||||
rv = inputStream->Available(&inputStreamSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
int64_t fileSize;
|
||||
rv = diskFile->GetFileSize(&fileSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(fileSize < 0)) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(uint64_t(fileSize) != inputStreamSize)) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
} else {
|
||||
// Create a journal file first.
|
||||
nsCOMPtr<nsIFile> journalFile =
|
||||
mFileManager->GetFileForId(journalDirectory, id);
|
||||
if (NS_WARN_IF(!journalFile)) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
rv = journalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
// Now try to copy the stream.
|
||||
RefPtr<FileOutputStream> fileOutputStream =
|
||||
FileOutputStream::Create(mPersistenceType,
|
||||
mGroup,
|
||||
mOrigin,
|
||||
diskFile);
|
||||
if (NS_WARN_IF(!fileOutputStream)) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
if (storedFileInfo.mType == StructuredCloneFile::eStructuredClone) {
|
||||
RefPtr<SnappyCompressOutputStream> snappyOutputStream =
|
||||
new SnappyCompressOutputStream(fileOutputStream);
|
||||
|
||||
UniquePtr<char[]> buffer(new char[snappyOutputStream->BlockSize()]);
|
||||
|
||||
rv = CopyFileData(inputStream,
|
||||
snappyOutputStream,
|
||||
buffer.get(),
|
||||
snappyOutputStream->BlockSize());
|
||||
} else {
|
||||
char buffer[kFileCopyBufferSize];
|
||||
|
||||
rv = CopyFileData(inputStream,
|
||||
fileOutputStream,
|
||||
buffer,
|
||||
kFileCopyBufferSize);
|
||||
}
|
||||
if (NS_FAILED(rv) &&
|
||||
NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// Try to remove the file if the copy failed.
|
||||
nsresult rv2;
|
||||
int64_t fileSize;
|
||||
|
||||
if (mFileManager->EnforcingQuota()) {
|
||||
rv2 = diskFile->GetFileSize(&fileSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv2))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
rv2 = diskFile->Remove(false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv2))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv2 = journalFile->Remove(false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv2))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mFileManager->EnforcingQuota()) {
|
||||
QuotaManager* quotaManager = QuotaManager::Get();
|
||||
MOZ_ASSERT(quotaManager);
|
||||
|
||||
quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
|
||||
mFileManager->Group(),
|
||||
mFileManager->Origin(),
|
||||
fileSize);
|
||||
}
|
||||
|
||||
// Try to remove the file if the copy failed.
|
||||
nsresult rv2 = fileHelper->RemoveFile(file, journalFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv2))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
storedFileInfo.mCopiedSuccessfully = true;
|
||||
return rv;
|
||||
}
|
||||
|
||||
storedFileInfo.mCopiedSuccessfully = true;
|
||||
}
|
||||
|
||||
if (index) {
|
||||
|
@ -29144,6 +29134,350 @@ DEBUGThreadSlower::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
|
|||
|
||||
#endif // DEBUG
|
||||
|
||||
nsresult
|
||||
FileHelper::Init()
|
||||
{
|
||||
MOZ_ASSERT(!IsOnBackgroundThread());
|
||||
MOZ_ASSERT(mFileManager);
|
||||
|
||||
nsCOMPtr<nsIFile> fileDirectory = mFileManager->GetCheckedDirectory();
|
||||
if (NS_WARN_IF(!fileDirectory)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> journalDirectory = mFileManager->EnsureJournalDirectory();
|
||||
if (NS_WARN_IF(!journalDirectory)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
DebugOnly<bool> exists;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->Exists(&exists)));
|
||||
MOZ_ASSERT(exists);
|
||||
|
||||
DebugOnly<bool> isDirectory;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->IsDirectory(&isDirectory)));
|
||||
MOZ_ASSERT(isDirectory);
|
||||
|
||||
mFileDirectory = Move(fileDirectory);
|
||||
mJournalDirectory= Move(journalDirectory);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIFile>
|
||||
FileHelper::GetFile(FileInfo* aFileInfo)
|
||||
{
|
||||
MOZ_ASSERT(!IsOnBackgroundThread());
|
||||
MOZ_ASSERT(aFileInfo);
|
||||
MOZ_ASSERT(mFileManager);
|
||||
MOZ_ASSERT(mFileDirectory);
|
||||
|
||||
const int64_t fileId = aFileInfo->Id();
|
||||
MOZ_ASSERT(fileId > 0);
|
||||
|
||||
nsCOMPtr<nsIFile> file =
|
||||
mFileManager->GetFileForId(mFileDirectory, fileId);
|
||||
return file.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsIFile>
|
||||
FileHelper::GetCheckedFile(FileInfo* aFileInfo)
|
||||
{
|
||||
MOZ_ASSERT(!IsOnBackgroundThread());
|
||||
MOZ_ASSERT(aFileInfo);
|
||||
MOZ_ASSERT(mFileManager);
|
||||
MOZ_ASSERT(mFileDirectory);
|
||||
|
||||
const int64_t fileId = aFileInfo->Id();
|
||||
MOZ_ASSERT(fileId > 0);
|
||||
|
||||
nsCOMPtr<nsIFile> file =
|
||||
mFileManager->GetCheckedFileForId(mFileDirectory, fileId);
|
||||
return file.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsIFile>
|
||||
FileHelper::GetJournalFile(FileInfo* aFileInfo)
|
||||
{
|
||||
MOZ_ASSERT(!IsOnBackgroundThread());
|
||||
MOZ_ASSERT(aFileInfo);
|
||||
MOZ_ASSERT(mFileManager);
|
||||
MOZ_ASSERT(mJournalDirectory);
|
||||
|
||||
const int64_t fileId = aFileInfo->Id();
|
||||
MOZ_ASSERT(fileId > 0);
|
||||
|
||||
nsCOMPtr<nsIFile> file =
|
||||
mFileManager->GetFileForId(mJournalDirectory, fileId);
|
||||
return file.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileHelper::CreateFileFromStream(nsIFile* aFile,
|
||||
nsIFile* aJournalFile,
|
||||
nsIInputStream* aInputStream,
|
||||
bool aCompress)
|
||||
{
|
||||
MOZ_ASSERT(!IsOnBackgroundThread());
|
||||
MOZ_ASSERT(aFile);
|
||||
MOZ_ASSERT(aJournalFile);
|
||||
MOZ_ASSERT(aInputStream);
|
||||
MOZ_ASSERT(mFileManager);
|
||||
MOZ_ASSERT(mFileDirectory);
|
||||
MOZ_ASSERT(mJournalDirectory);
|
||||
|
||||
bool exists;
|
||||
nsresult rv = aFile->Exists(&exists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// DOM blobs that are being stored in IDB are cached by calling
|
||||
// IDBDatabase::GetOrCreateFileActorForBlob. So if the same DOM blob is stored
|
||||
// again under a different key or in a different object store, we just add
|
||||
// a new reference instead of creating a new copy (all such stored blobs share
|
||||
// the same id).
|
||||
// However, it can happen that CreateFileFromStream failed due to quota
|
||||
// exceeded error and for some reason the orphaned file couldn't be deleted
|
||||
// immediately. Now, if the operation is being repeated, the DOM blob is
|
||||
// already cached, so it has the same file id which clashes with the orphaned
|
||||
// file. We could do some tricks to restore previous copy loop, but it's safer
|
||||
// to just delete the orphaned file and start from scratch.
|
||||
// This corner case is partially simulated in test_file_copy_failure.js
|
||||
if (exists) {
|
||||
bool isFile;
|
||||
rv = aFile->IsFile(&isFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!isFile)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = aJournalFile->Exists(&exists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!exists)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = aJournalFile->IsFile(&isFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!isFile)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
IDB_WARNING("Deleting orphaned file!");
|
||||
|
||||
rv = RemoveFile(aFile, aJournalFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a journal file first.
|
||||
rv = aJournalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Now try to copy the stream.
|
||||
RefPtr<FileOutputStream> fileOutputStream =
|
||||
FileOutputStream::Create(mFileManager->Type(),
|
||||
mFileManager->Group(),
|
||||
mFileManager->Origin(),
|
||||
aFile);
|
||||
if (NS_WARN_IF(!fileOutputStream)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (aCompress) {
|
||||
RefPtr<SnappyCompressOutputStream> snappyOutputStream =
|
||||
new SnappyCompressOutputStream(fileOutputStream);
|
||||
|
||||
UniquePtr<char[]> buffer(new char[snappyOutputStream->BlockSize()]);
|
||||
|
||||
rv = SyncCopy(aInputStream,
|
||||
snappyOutputStream,
|
||||
buffer.get(),
|
||||
snappyOutputStream->BlockSize());
|
||||
} else {
|
||||
char buffer[kFileCopyBufferSize];
|
||||
|
||||
rv = SyncCopy(aInputStream,
|
||||
fileOutputStream,
|
||||
buffer,
|
||||
kFileCopyBufferSize);
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileHelper::ReplaceFile(nsIFile* aFile,
|
||||
nsIFile* aNewFile,
|
||||
nsIFile* aNewJournalFile)
|
||||
{
|
||||
MOZ_ASSERT(!IsOnBackgroundThread());
|
||||
MOZ_ASSERT(aFile);
|
||||
MOZ_ASSERT(aNewFile);
|
||||
MOZ_ASSERT(aNewJournalFile);
|
||||
MOZ_ASSERT(mFileManager);
|
||||
MOZ_ASSERT(mFileDirectory);
|
||||
MOZ_ASSERT(mJournalDirectory);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
int64_t fileSize;
|
||||
|
||||
if (mFileManager->EnforcingQuota()) {
|
||||
rv = aFile->GetFileSize(&fileSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString fileName;
|
||||
rv = aFile->GetLeafName(fileName);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = aNewFile->RenameTo(nullptr, fileName);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mFileManager->EnforcingQuota()) {
|
||||
QuotaManager* quotaManager = QuotaManager::Get();
|
||||
MOZ_ASSERT(quotaManager);
|
||||
|
||||
quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
|
||||
mFileManager->Group(),
|
||||
mFileManager->Origin(),
|
||||
fileSize);
|
||||
}
|
||||
|
||||
rv = aNewJournalFile->Remove(false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileHelper::RemoveFile(nsIFile* aFile,
|
||||
nsIFile* aJournalFile)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
int64_t fileSize;
|
||||
|
||||
if (mFileManager->EnforcingQuota()) {
|
||||
rv = aFile->GetFileSize(&fileSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
rv = aFile->Remove(false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mFileManager->EnforcingQuota()) {
|
||||
QuotaManager* quotaManager = QuotaManager::Get();
|
||||
MOZ_ASSERT(quotaManager);
|
||||
|
||||
quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
|
||||
mFileManager->Group(),
|
||||
mFileManager->Origin(),
|
||||
fileSize);
|
||||
}
|
||||
|
||||
rv = aJournalFile->Remove(false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<FileInfo>
|
||||
FileHelper::GetNewFileInfo()
|
||||
{
|
||||
MOZ_ASSERT(mFileManager);
|
||||
|
||||
return mFileManager->GetNewFileInfo();
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileHelper::SyncCopy(nsIInputStream* aInputStream,
|
||||
nsIOutputStream* aOutputStream,
|
||||
char* aBuffer,
|
||||
uint32_t aBufferSize)
|
||||
{
|
||||
MOZ_ASSERT(!IsOnBackgroundThread());
|
||||
MOZ_ASSERT(aInputStream);
|
||||
MOZ_ASSERT(aOutputStream);
|
||||
|
||||
PROFILER_LABEL("IndexedDB",
|
||||
"FileHelper::SyncCopy",
|
||||
js::ProfileEntry::Category::STORAGE);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
do {
|
||||
uint32_t numRead;
|
||||
rv = aInputStream->Read(aBuffer, aBufferSize, &numRead);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!numRead) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t numWrite;
|
||||
rv = aOutputStream->Write(aBuffer, numRead, &numWrite);
|
||||
if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
|
||||
rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(numWrite != numRead)) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = aOutputStream->Flush();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv2 = aOutputStream->Close();
|
||||
if (NS_WARN_IF(NS_FAILED(rv2))) {
|
||||
return NS_SUCCEEDED(rv) ? rv2 : rv;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace indexedDB
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -43,8 +43,6 @@ struct StructuredCloneFile
|
|||
RefPtr<JS::WasmModule> mWasmModule;
|
||||
RefPtr<FileInfo> mFileInfo;
|
||||
FileType mType;
|
||||
// This is currently specific to eWasmCompiled files.
|
||||
bool mValid;
|
||||
|
||||
// In IndexedDatabaseInlines.h
|
||||
inline
|
||||
|
|
|
@ -24,7 +24,6 @@ namespace indexedDB {
|
|||
inline
|
||||
StructuredCloneFile::StructuredCloneFile()
|
||||
: mType(eBlob)
|
||||
, mValid(true)
|
||||
{
|
||||
MOZ_COUNT_CTOR(StructuredCloneFile);
|
||||
}
|
||||
|
@ -42,8 +41,7 @@ StructuredCloneFile::operator==(const StructuredCloneFile& aOther) const
|
|||
return this->mBlob == aOther.mBlob &&
|
||||
this->mMutableFile == aOther.mMutableFile &&
|
||||
this->mFileInfo == aOther.mFileInfo &&
|
||||
this->mType == aOther.mType &&
|
||||
this->mValid == aOther.mValid;
|
||||
this->mType == aOther.mType;
|
||||
}
|
||||
|
||||
inline
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var testGenerator = testSteps();
|
||||
|
||||
function testSteps()
|
||||
{
|
||||
const name = "test_file_copy_failure.js";
|
||||
const objectStoreName = "Blobs";
|
||||
const blob = getBlob(getView(1024));
|
||||
|
||||
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("Creating orphaned file");
|
||||
|
||||
let filesDir = getChromeFilesDir();
|
||||
|
||||
let journalFile = filesDir.clone();
|
||||
journalFile.append("journals");
|
||||
journalFile.append("1");
|
||||
|
||||
let exists = journalFile.exists();
|
||||
ok(!exists, "Journal file doesn't exist");
|
||||
|
||||
journalFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0644", 8));
|
||||
|
||||
let file = filesDir.clone();
|
||||
file.append("1");
|
||||
|
||||
exists = file.exists();
|
||||
ok(!exists, "File doesn't exist");
|
||||
|
||||
file.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0644", 8));
|
||||
|
||||
info("Storing blob");
|
||||
|
||||
let trans = db.transaction(objectStoreName, "readwrite");
|
||||
|
||||
request = trans.objectStore(objectStoreName).add(blob, 1);
|
||||
request.onsuccess = continueToNextStepSync;
|
||||
|
||||
yield undefined;
|
||||
|
||||
trans.oncomplete = continueToNextStepSync;
|
||||
|
||||
yield undefined;
|
||||
|
||||
exists = journalFile.exists();
|
||||
ok(!exists, "Journal file doesn't exist");
|
||||
|
||||
finishTest();
|
||||
yield undefined;
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var testGenerator = testSteps();
|
||||
|
||||
function testSteps()
|
||||
{
|
||||
const name = "test_wasm_recompile.js";
|
||||
|
||||
const objectStoreName = "Wasm";
|
||||
|
||||
const wasmData = { key: 1, wasm: null };
|
||||
|
||||
// The goal of this test is to prove that wasm is recompiled and the on-disk
|
||||
// copy updated.
|
||||
|
||||
if (!isWasmSupported()) {
|
||||
finishTest();
|
||||
yield undefined;
|
||||
}
|
||||
|
||||
getWasmBinary('(module (func (nop)))');
|
||||
let binary = yield undefined;
|
||||
|
||||
wasmData.wasm = getWasmModule(binary);
|
||||
|
||||
info("Installing profile");
|
||||
|
||||
clearAllDatabases(continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
// The profile was created by an older build (buildId: 20161116145318,
|
||||
// cpuId: X64=0x2). It contains one stored wasm module (file id 1 - bytecode
|
||||
// and file id 2 - compiled/machine code). The file create_db.js in the
|
||||
// package was run locally (specifically it was temporarily added to
|
||||
// xpcshell-parent-process.ini and then executed:
|
||||
// mach xpcshell-test dom/indexedDB/test/unit/create_db.js
|
||||
installPackagedProfile("wasm_recompile_profile");
|
||||
|
||||
let filesDir = getChromeFilesDir();
|
||||
|
||||
let file = filesDir.clone();
|
||||
file.append("2");
|
||||
|
||||
info("Reading out contents of compiled blob");
|
||||
|
||||
let fileReader = new FileReader();
|
||||
fileReader.onload = continueToNextStepSync;
|
||||
fileReader.readAsArrayBuffer(File.createFromNsIFile(file));
|
||||
|
||||
yield undefined;
|
||||
|
||||
let compiledBuffer = fileReader.result;
|
||||
|
||||
info("Opening database");
|
||||
|
||||
let request = indexedDB.open(name);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = unexpectedSuccessHandler;
|
||||
request.onsuccess = continueToNextStepSync;
|
||||
yield undefined;
|
||||
|
||||
// success
|
||||
let db = request.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
info("Getting wasm");
|
||||
|
||||
request = db.transaction([objectStoreName])
|
||||
.objectStore(objectStoreName).get(wasmData.key);
|
||||
request.onsuccess = continueToNextStepSync;
|
||||
yield undefined;
|
||||
|
||||
info("Verifying wasm module");
|
||||
|
||||
verifyWasmModule(request.result, wasmData.wasm);
|
||||
yield undefined;
|
||||
|
||||
info("Reading out contents of new compiled blob");
|
||||
|
||||
fileReader = new FileReader();
|
||||
fileReader.onload = continueToNextStepSync;
|
||||
fileReader.readAsArrayBuffer(File.createFromNsIFile(file));
|
||||
|
||||
yield undefined;
|
||||
|
||||
let newCompiledBuffer = fileReader.result;
|
||||
|
||||
info("Verifying blobs differ");
|
||||
|
||||
ok(!compareBuffers(newCompiledBuffer, compiledBuffer), "Blobs differ");
|
||||
|
||||
info("Getting wasm again");
|
||||
|
||||
request = db.transaction([objectStoreName])
|
||||
.objectStore(objectStoreName).get(wasmData.key);
|
||||
request.onsuccess = continueToNextStepSync;
|
||||
yield undefined;
|
||||
|
||||
info("Verifying wasm module");
|
||||
|
||||
verifyWasmModule(request.result, wasmData.wasm);
|
||||
yield undefined;
|
||||
|
||||
info("Reading contents of new compiled blob again");
|
||||
|
||||
fileReader = new FileReader();
|
||||
fileReader.onload = continueToNextStepSync;
|
||||
fileReader.readAsArrayBuffer(File.createFromNsIFile(file));
|
||||
|
||||
yield undefined;
|
||||
|
||||
let newCompiledBuffer2 = fileReader.result;
|
||||
|
||||
info("Verifying blob didn't change");
|
||||
|
||||
ok(compareBuffers(newCompiledBuffer2, newCompiledBuffer),
|
||||
"Blob didn't change");
|
||||
|
||||
finishTest();
|
||||
yield undefined;
|
||||
}
|
Двоичный файл не отображается.
|
@ -347,6 +347,31 @@ function installPackagedProfile(packageName)
|
|||
zipReader.close();
|
||||
}
|
||||
|
||||
function getChromeFilesDir()
|
||||
{
|
||||
let dirService = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties);
|
||||
|
||||
let profileDir = dirService.get("ProfD", Ci.nsIFile);
|
||||
|
||||
let idbDir = profileDir.clone();
|
||||
idbDir.append("storage");
|
||||
idbDir.append("permanent");
|
||||
idbDir.append("chrome");
|
||||
idbDir.append("idb");
|
||||
|
||||
let idbEntries = idbDir.directoryEntries;
|
||||
while (idbEntries.hasMoreElements()) {
|
||||
let entry = idbEntries.getNext();
|
||||
let file = entry.QueryInterface(Ci.nsIFile);
|
||||
if (file.isDirectory()) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("files directory doesn't exist!");
|
||||
}
|
||||
|
||||
function getView(size)
|
||||
{
|
||||
let buffer = new ArrayBuffer(size);
|
||||
|
|
|
@ -26,6 +26,7 @@ support-files =
|
|||
schema23upgrade_profile.zip
|
||||
snappyUpgrade_profile.zip
|
||||
storagePersistentUpgrade_profile.zip
|
||||
wasm_recompile_profile.zip
|
||||
xpcshell-shared.ini
|
||||
|
||||
[include:xpcshell-shared.ini]
|
||||
|
@ -36,6 +37,7 @@ support-files =
|
|||
[test_database_close_without_onclose.js]
|
||||
[test_database_onclose.js]
|
||||
[test_defaultStorageUpgrade.js]
|
||||
[test_file_copy_failure.js]
|
||||
[test_idbSubdirUpgrade.js]
|
||||
[test_globalObjects_ipc.js]
|
||||
skip-if = toolkit == 'android'
|
||||
|
@ -62,3 +64,4 @@ skip-if = os == "android" && processor == "x86"
|
|||
[test_view_put_get_values.js]
|
||||
[test_wasm_getAll.js]
|
||||
[test_wasm_put_get_values.js]
|
||||
[test_wasm_recompile.js]
|
||||
|
|
|
@ -6092,9 +6092,9 @@ struct WasmModule : js::AtomicRefCounted<WasmModule>
|
|||
MOZ_DECLARE_REFCOUNTED_TYPENAME(WasmModule)
|
||||
virtual ~WasmModule() {}
|
||||
|
||||
virtual void serializedSize(size_t* bytecodeSize, size_t* compiledSize) const = 0;
|
||||
virtual void serialize(uint8_t* bytecodeBegin, size_t bytecodeSize,
|
||||
uint8_t* compiledBegin, size_t compiledSize) const = 0;
|
||||
virtual void serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const = 0;
|
||||
virtual void serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize,
|
||||
uint8_t* maybeCompiledBegin, size_t maybeCompiledSize) const = 0;
|
||||
|
||||
virtual JSObject* createObject(JSContext* cx) = 0;
|
||||
};
|
||||
|
|
|
@ -138,44 +138,54 @@ LinkData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
|
|||
}
|
||||
|
||||
/* virtual */ void
|
||||
Module::serializedSize(size_t* bytecodeSize, size_t* compiledSize) const
|
||||
Module::serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const
|
||||
{
|
||||
*bytecodeSize = SerializedPodVectorSize(bytecode_->bytes);
|
||||
if (maybeBytecodeSize)
|
||||
*maybeBytecodeSize = SerializedPodVectorSize(bytecode_->bytes);
|
||||
|
||||
*compiledSize = assumptions_.serializedSize() +
|
||||
SerializedPodVectorSize(code_) +
|
||||
linkData_.serializedSize() +
|
||||
SerializedVectorSize(imports_) +
|
||||
SerializedVectorSize(exports_) +
|
||||
SerializedPodVectorSize(dataSegments_) +
|
||||
SerializedVectorSize(elemSegments_) +
|
||||
metadata_->serializedSize();
|
||||
if (maybeCompiledSize) {
|
||||
*maybeCompiledSize = assumptions_.serializedSize() +
|
||||
SerializedPodVectorSize(code_) +
|
||||
linkData_.serializedSize() +
|
||||
SerializedVectorSize(imports_) +
|
||||
SerializedVectorSize(exports_) +
|
||||
SerializedPodVectorSize(dataSegments_) +
|
||||
SerializedVectorSize(elemSegments_) +
|
||||
metadata_->serializedSize();
|
||||
}
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
Module::serialize(uint8_t* bytecodeBegin, size_t bytecodeSize,
|
||||
uint8_t* compiledBegin, size_t compiledSize) const
|
||||
Module::serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize,
|
||||
uint8_t* maybeCompiledBegin, size_t maybeCompiledSize) const
|
||||
{
|
||||
// Bytecode deserialization is not guarded by Assumptions and thus must not
|
||||
// change incompatibly between builds.
|
||||
MOZ_ASSERT(!!maybeBytecodeBegin == !!maybeBytecodeSize);
|
||||
MOZ_ASSERT(!!maybeCompiledBegin == !!maybeCompiledSize);
|
||||
|
||||
uint8_t* bytecodeEnd = SerializePodVector(bytecodeBegin, bytecode_->bytes);
|
||||
MOZ_RELEASE_ASSERT(bytecodeEnd == bytecodeBegin + bytecodeSize);
|
||||
if (maybeBytecodeBegin) {
|
||||
// Bytecode deserialization is not guarded by Assumptions and thus must not
|
||||
// change incompatibly between builds.
|
||||
|
||||
// Assumption must be serialized at the beginning of the compiled bytes so
|
||||
// that compiledAssumptionsMatch can detect a build-id mismatch before any
|
||||
// other decoding occurs.
|
||||
uint8_t* bytecodeEnd = SerializePodVector(maybeBytecodeBegin, bytecode_->bytes);
|
||||
MOZ_RELEASE_ASSERT(bytecodeEnd == maybeBytecodeBegin + maybeBytecodeSize);
|
||||
}
|
||||
|
||||
uint8_t* cursor = compiledBegin;
|
||||
cursor = assumptions_.serialize(cursor);
|
||||
cursor = SerializePodVector(cursor, code_);
|
||||
cursor = linkData_.serialize(cursor);
|
||||
cursor = SerializeVector(cursor, imports_);
|
||||
cursor = SerializeVector(cursor, exports_);
|
||||
cursor = SerializePodVector(cursor, dataSegments_);
|
||||
cursor = SerializeVector(cursor, elemSegments_);
|
||||
cursor = metadata_->serialize(cursor);
|
||||
MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
|
||||
if (maybeCompiledBegin) {
|
||||
// Assumption must be serialized at the beginning of the compiled bytes so
|
||||
// that compiledAssumptionsMatch can detect a build-id mismatch before any
|
||||
// other decoding occurs.
|
||||
|
||||
uint8_t* cursor = maybeCompiledBegin;
|
||||
cursor = assumptions_.serialize(cursor);
|
||||
cursor = SerializePodVector(cursor, code_);
|
||||
cursor = linkData_.serialize(cursor);
|
||||
cursor = SerializeVector(cursor, imports_);
|
||||
cursor = SerializeVector(cursor, exports_);
|
||||
cursor = SerializePodVector(cursor, dataSegments_);
|
||||
cursor = SerializeVector(cursor, elemSegments_);
|
||||
cursor = metadata_->serialize(cursor);
|
||||
MOZ_RELEASE_ASSERT(cursor == maybeCompiledBegin + maybeCompiledSize);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
|
|
@ -150,9 +150,9 @@ class Module : public JS::WasmModule
|
|||
|
||||
// Structured clone support:
|
||||
|
||||
void serializedSize(size_t* bytecodeSize, size_t* compiledSize) const override;
|
||||
void serialize(uint8_t* bytecodeBegin, size_t bytecodeSize,
|
||||
uint8_t* compiledBegin, size_t compiledSize) const override;
|
||||
void serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const override;
|
||||
void serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize,
|
||||
uint8_t* maybeCompiledBegin, size_t maybeCompiledSize) const override;
|
||||
static bool assumptionsMatch(const Assumptions& current, const uint8_t* compiledBegin,
|
||||
size_t remain);
|
||||
static RefPtr<Module> deserialize(const uint8_t* bytecodeBegin, size_t bytecodeSize,
|
||||
|
|
Загрузка…
Ссылка в новой задаче