From 64201718579899a150245d2737fdc513201a53f2 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Wed, 28 Sep 2016 14:37:54 +0200 Subject: [PATCH] Bug 1202006 - Memory Blob to Temporary File - part 1 - MutableBlobStorage for XHR, r=smaug --HG-- rename : dom/base/BlobSet.cpp => dom/base/MutableBlobStorage.cpp rename : dom/base/BlobSet.h => dom/base/MutableBlobStorage.h --- dom/base/MutableBlobStorage.cpp | 101 +++++++++++++++++++++++++++ dom/base/MutableBlobStorage.h | 53 ++++++++++++++ dom/base/moz.build | 2 + dom/xhr/XMLHttpRequestMainThread.cpp | 28 ++++---- dom/xhr/XMLHttpRequestMainThread.h | 6 +- 5 files changed, 173 insertions(+), 17 deletions(-) create mode 100644 dom/base/MutableBlobStorage.cpp create mode 100644 dom/base/MutableBlobStorage.h diff --git a/dom/base/MutableBlobStorage.cpp b/dom/base/MutableBlobStorage.cpp new file mode 100644 index 000000000000..f750be787ddd --- /dev/null +++ b/dom/base/MutableBlobStorage.cpp @@ -0,0 +1,101 @@ +/* -*- 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/MutableBlobStorage.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/dom/File.h" +#include "MultipartBlobImpl.h" + +namespace mozilla { +namespace dom { + +already_AddRefed +MutableBlobStorage::GetBlob(nsISupports* aParent, + const nsACString& aContentType, + ErrorResult& aRv) +{ + Flush(); + + nsTArray> blobImpls(mBlobImpls); + RefPtr blobImpl = + MultipartBlobImpl::Create(Move(blobImpls), + NS_ConvertASCIItoUTF16(aContentType), + aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RefPtr blob = Blob::Create(aParent, blobImpl); + return blob.forget(); +} + +nsresult +MutableBlobStorage::Append(const void* aData, uint32_t aLength) +{ + NS_ENSURE_ARG_POINTER(aData); + if (!aLength) { + return NS_OK; + } + + uint64_t offset = mDataLen; + + if (!ExpandBufferSize(aLength)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + memcpy((char*)mData + offset, aData, aLength); + return NS_OK; +} + +bool +MutableBlobStorage::ExpandBufferSize(uint64_t aSize) +{ + if (mDataBufferLen >= mDataLen + aSize) { + mDataLen += aSize; + return true; + } + + // Start at 1 or we'll loop forever. + CheckedUint32 bufferLen = + std::max(static_cast(mDataBufferLen), 1); + while (bufferLen.isValid() && bufferLen.value() < mDataLen + aSize) { + bufferLen *= 2; + } + + if (!bufferLen.isValid()) { + return false; + } + + void* data = realloc(mData, bufferLen.value()); + if (!data) { + return false; + } + + mData = data; + mDataBufferLen = bufferLen.value(); + mDataLen += aSize; + return true; +} + +void +MutableBlobStorage::Flush() +{ + if (mData) { + // If we have some data, create a blob for it + // and put it on the stack + + RefPtr blobImpl = + new BlobImplMemory(mData, mDataLen, EmptyString()); + mBlobImpls.AppendElement(blobImpl); + + mData = nullptr; // The nsDOMMemoryFile takes ownership of the buffer + mDataLen = 0; + mDataBufferLen = 0; + } +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/base/MutableBlobStorage.h b/dom/base/MutableBlobStorage.h new file mode 100644 index 000000000000..e7ed36e33f7b --- /dev/null +++ b/dom/base/MutableBlobStorage.h @@ -0,0 +1,53 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_MutableBlobStorage_h +#define mozilla_dom_MutableBlobStorage_h + +#include "mozilla/RefPtr.h" + +namespace mozilla { +namespace dom { + +class Blob; +class BlobImpl; + +class MutableBlobStorage final +{ +public: + MutableBlobStorage() + : mData(nullptr) + , mDataLen(0) + , mDataBufferLen(0) + {} + + ~MutableBlobStorage() + { + free(mData); + } + + nsresult Append(const void* aData, uint32_t aLength); + + already_AddRefed GetBlob(nsISupports* aParent, + const nsACString& aContentType, + ErrorResult& aRv); + +private: + bool ExpandBufferSize(uint64_t aSize); + + void Flush(); + + nsTArray> mBlobImpls; + + void* mData; + uint64_t mDataLen; + uint64_t mDataBufferLen; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_MutableBlobStorage_h diff --git a/dom/base/moz.build b/dom/base/moz.build index 114635f2b5c6..8d856a04e1e6 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -186,6 +186,7 @@ EXPORTS.mozilla.dom += [ 'ImportManager.h', 'Link.h', 'Location.h', + 'MutableBlobStorage.h', 'NameSpaceConstants.h', 'Navigator.h', 'NodeInfo.h', @@ -247,6 +248,7 @@ UNIFIED_SOURCES += [ 'Link.cpp', 'Location.cpp', 'MultipartBlobImpl.cpp', + 'MutableBlobStorage.cpp', 'Navigator.cpp', 'NodeInfo.cpp', 'NodeIterator.cpp', diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp index 496ee55c0937..3a249784708b 100644 --- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -12,10 +12,10 @@ #endif #include "mozilla/ArrayUtils.h" #include "mozilla/CheckedInt.h" -#include "mozilla/dom/BlobSet.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FetchUtil.h" #include "mozilla/dom/FormData.h" +#include "mozilla/dom/MutableBlobStorage.h" #include "mozilla/dom/XMLDocument.h" #include "mozilla/dom/URLSearchParams.h" #include "mozilla/EventDispatcher.h" @@ -291,7 +291,7 @@ XMLHttpRequestMainThread::ResetResponse() TruncateResponseText(); mResponseBlob = nullptr; mDOMBlob = nullptr; - mBlobSet = nullptr; + mBlobStorage = nullptr; mResultArrayBuffer = nullptr; mArrayBufferBuilder.reset(); mResultJSON.setUndefined(); @@ -677,8 +677,8 @@ XMLHttpRequestMainThread::CreatePartialBlob(ErrorResult& aRv) return; } - // mBlobSet can be null if the request has been canceled - if (!mBlobSet) { + // mBlobStorage can be null if the request has been canceled + if (!mBlobStorage) { return; } @@ -687,7 +687,7 @@ XMLHttpRequestMainThread::CreatePartialBlob(ErrorResult& aRv) mChannel->GetContentType(contentType); } - mResponseBlob = mBlobSet->GetBlobInternal(GetOwner(), contentType, aRv); + mResponseBlob = mBlobStorage->GetBlob(GetOwner(), contentType, aRv); } NS_IMETHODIMP XMLHttpRequestMainThread::GetResponseType(nsAString& aResponseType) @@ -1584,10 +1584,10 @@ XMLHttpRequestMainThread::StreamReaderFunc(nsIInputStream* in, if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Blob || xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_blob) { if (!xmlHttpRequest->mDOMBlob) { - if (!xmlHttpRequest->mBlobSet) { - xmlHttpRequest->mBlobSet = new BlobSet(); + if (!xmlHttpRequest->mBlobStorage) { + xmlHttpRequest->mBlobStorage = new MutableBlobStorage(); } - rv = xmlHttpRequest->mBlobSet->AppendVoidPtr(fromRawSegment, count); + rv = xmlHttpRequest->mBlobStorage->Append(fromRawSegment, count); } // Clear the cache so that the blob size is updated. if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_blob) { @@ -1668,7 +1668,7 @@ bool XMLHttpRequestMainThread::CreateDOMBlob(nsIRequest *request) mDOMBlob = File::CreateFromFile(GetOwner(), file, EmptyString(), NS_ConvertASCIItoUTF16(contentType)); - mBlobSet = nullptr; + mBlobStorage = nullptr; NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty"); return true; } @@ -2038,18 +2038,18 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt, mResponseBlob = mDOMBlob; mDOMBlob = nullptr; } else { - // mBlobSet can be null if the channel is non-file non-cacheable + // mBlobStorage can be null if the channel is non-file non-cacheable // and if the response length is zero. - if (!mBlobSet) { - mBlobSet = new BlobSet(); + if (!mBlobStorage) { + mBlobStorage = new MutableBlobStorage(); } // Smaller files may be written in cache map instead of separate files. // Also, no-store response cannot be written in persistent cache. nsAutoCString contentType; mChannel->GetContentType(contentType); - mResponseBlob = mBlobSet->GetBlobInternal(GetOwner(), contentType, rv); - mBlobSet = nullptr; + mResponseBlob = mBlobStorage->GetBlob(GetOwner(), contentType, rv); + mBlobStorage = nullptr; if (NS_WARN_IF(rv.Failed())) { return rv.StealNSResult(); diff --git a/dom/xhr/XMLHttpRequestMainThread.h b/dom/xhr/XMLHttpRequestMainThread.h index 9fa711f66ff6..992b18f0fcee 100644 --- a/dom/xhr/XMLHttpRequestMainThread.h +++ b/dom/xhr/XMLHttpRequestMainThread.h @@ -55,8 +55,8 @@ namespace mozilla { namespace dom { class Blob; -class BlobSet; class FormData; +class MutableBlobStorage; class URLSearchParams; class XMLHttpRequestUpload; struct OriginAttributesDictionary; @@ -644,9 +644,9 @@ protected: // Non-null only when we are able to get a os-file representation of the // response, i.e. when loading from a file. RefPtr mDOMBlob; - // We stream data to mBlobSet when response type is "blob" or "moz-blob" + // We stream data to mBlobStorage when response type is "blob" or "moz-blob" // and mDOMBlob is null. - nsAutoPtr mBlobSet; + nsAutoPtr mBlobStorage; nsString mOverrideMimeType;