зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1591579 - XHR uses a ref-counted ArrayBufferBuilder, r=smaug
Differential Revision: https://phabricator.services.mozilla.com/D50919 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
1045168094
Коммит
d7261d6930
|
@ -229,6 +229,7 @@ XMLHttpRequestMainThread::XMLHttpRequestMainThread()
|
|||
mFirstStartRequestSeen(false),
|
||||
mInLoadProgressEvent(false),
|
||||
mResultJSON(JS::UndefinedValue()),
|
||||
mArrayBufferBuilder(new ArrayBufferBuilder()),
|
||||
mResultArrayBuffer(nullptr),
|
||||
mIsMappedArrayBuffer(false),
|
||||
mXPCOMifier(nullptr),
|
||||
|
@ -317,7 +318,7 @@ void XMLHttpRequestMainThread::ResetResponse() {
|
|||
mResponseBlob = nullptr;
|
||||
mBlobStorage = nullptr;
|
||||
mResultArrayBuffer = nullptr;
|
||||
mArrayBufferBuilder.reset();
|
||||
mArrayBufferBuilder = new ArrayBufferBuilder();
|
||||
mResultJSON.setUndefined();
|
||||
mLoadTransferred = 0;
|
||||
mResponseBodyDecodedPos = 0;
|
||||
|
@ -351,7 +352,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestMainThread,
|
||||
XMLHttpRequestEventTarget)
|
||||
tmp->mResultArrayBuffer = nullptr;
|
||||
tmp->mArrayBufferBuilder.reset();
|
||||
tmp->mArrayBufferBuilder = nullptr;
|
||||
tmp->mResultJSON.setUndefined();
|
||||
tmp->mResponseBlobImpl = nullptr;
|
||||
|
||||
|
@ -679,7 +680,7 @@ void XMLHttpRequestMainThread::GetResponse(
|
|||
}
|
||||
|
||||
if (!mResultArrayBuffer) {
|
||||
mResultArrayBuffer = mArrayBufferBuilder.getArrayBuffer(aCx);
|
||||
mResultArrayBuffer = mArrayBufferBuilder->GetArrayBuffer(aCx);
|
||||
if (!mResultArrayBuffer) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
|
@ -1512,11 +1513,11 @@ nsresult XMLHttpRequestMainThread::StreamReaderFunc(
|
|||
!xmlHttpRequest->mIsMappedArrayBuffer) {
|
||||
// get the initial capacity to something reasonable to avoid a bunch of
|
||||
// reallocs right at the start
|
||||
if (xmlHttpRequest->mArrayBufferBuilder.capacity() == 0)
|
||||
xmlHttpRequest->mArrayBufferBuilder.setCapacity(
|
||||
if (xmlHttpRequest->mArrayBufferBuilder->Capacity() == 0)
|
||||
xmlHttpRequest->mArrayBufferBuilder->SetCapacity(
|
||||
std::max(count, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE));
|
||||
|
||||
if (NS_WARN_IF(!xmlHttpRequest->mArrayBufferBuilder.append(
|
||||
if (NS_WARN_IF(!xmlHttpRequest->mArrayBufferBuilder->Append(
|
||||
reinterpret_cast<const uint8_t*>(fromRawSegment), count,
|
||||
XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -1861,7 +1862,7 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest* request) {
|
|||
if (!jarFile) {
|
||||
mIsMappedArrayBuffer = false;
|
||||
} else {
|
||||
rv = mArrayBufferBuilder.mapToFileInPackage(file, jarFile);
|
||||
rv = mArrayBufferBuilder->MapToFileInPackage(file, jarFile);
|
||||
// This can happen legitimately if there are compressed files
|
||||
// in the jarFile. See bug #1357219. No need to warn on the error.
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -1881,7 +1882,7 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest* request) {
|
|||
rv = channel->GetContentLength(&contentLength);
|
||||
if (NS_SUCCEEDED(rv) && contentLength > 0 &&
|
||||
contentLength < XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE) {
|
||||
mArrayBufferBuilder.setCapacity(static_cast<int32_t>(contentLength));
|
||||
mArrayBufferBuilder->SetCapacity(static_cast<int32_t>(contentLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2154,7 +2155,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest* request, nsresult status) {
|
|||
mResponseType == XMLHttpRequestResponseType::Arraybuffer) {
|
||||
// set the capacity down to the actual length, to realloc back
|
||||
// down to the actual size
|
||||
if (!mArrayBufferBuilder.setCapacity(mArrayBufferBuilder.length())) {
|
||||
if (!mArrayBufferBuilder->SetCapacity(mArrayBufferBuilder->Length())) {
|
||||
// this should never happen!
|
||||
status = NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
@ -3651,11 +3652,13 @@ nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID& aIID, void** aResult) {
|
|||
}
|
||||
|
||||
ArrayBufferBuilder::ArrayBufferBuilder()
|
||||
: mDataPtr(nullptr), mCapacity(0), mLength(0), mMapPtr(nullptr) {}
|
||||
: mMutex("ArrayBufferBuilder"),
|
||||
mDataPtr(nullptr),
|
||||
mCapacity(0),
|
||||
mLength(0),
|
||||
mMapPtr(nullptr) {}
|
||||
|
||||
ArrayBufferBuilder::~ArrayBufferBuilder() { reset(); }
|
||||
|
||||
void ArrayBufferBuilder::reset() {
|
||||
ArrayBufferBuilder::~ArrayBufferBuilder() {
|
||||
if (mDataPtr) {
|
||||
JS_free(nullptr, mDataPtr);
|
||||
}
|
||||
|
@ -3669,7 +3672,13 @@ void ArrayBufferBuilder::reset() {
|
|||
mCapacity = mLength = 0;
|
||||
}
|
||||
|
||||
bool ArrayBufferBuilder::setCapacity(uint32_t aNewCap) {
|
||||
bool ArrayBufferBuilder::SetCapacity(uint32_t aNewCap) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
return SetCapacityInternal(aNewCap, lock);
|
||||
}
|
||||
|
||||
bool ArrayBufferBuilder::SetCapacityInternal(
|
||||
uint32_t aNewCap, const MutexAutoLock& aProofOfLock) {
|
||||
MOZ_ASSERT(!mMapPtr);
|
||||
|
||||
// To ensure that realloc won't free mDataPtr, use a size of 1
|
||||
|
@ -3693,8 +3702,9 @@ bool ArrayBufferBuilder::setCapacity(uint32_t aNewCap) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ArrayBufferBuilder::append(const uint8_t* aNewData, uint32_t aDataLen,
|
||||
bool ArrayBufferBuilder::Append(const uint8_t* aNewData, uint32_t aDataLen,
|
||||
uint32_t aMaxGrowth) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(!mMapPtr);
|
||||
|
||||
CheckedUint32 neededCapacity = mLength;
|
||||
|
@ -3720,14 +3730,14 @@ bool ArrayBufferBuilder::append(const uint8_t* aNewData, uint32_t aDataLen,
|
|||
newcap = neededCapacity;
|
||||
}
|
||||
|
||||
if (!setCapacity(newcap.value())) {
|
||||
if (!SetCapacityInternal(newcap.value(), lock)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Assert that the region isn't overlapping so we can memcpy.
|
||||
MOZ_ASSERT(
|
||||
!areOverlappingRegions(aNewData, aDataLen, mDataPtr + mLength, aDataLen));
|
||||
!AreOverlappingRegions(aNewData, aDataLen, mDataPtr + mLength, aDataLen));
|
||||
|
||||
memcpy(mDataPtr + mLength, aNewData, aDataLen);
|
||||
mLength += aDataLen;
|
||||
|
@ -3735,7 +3745,19 @@ bool ArrayBufferBuilder::append(const uint8_t* aNewData, uint32_t aDataLen,
|
|||
return true;
|
||||
}
|
||||
|
||||
JSObject* ArrayBufferBuilder::getArrayBuffer(JSContext* aCx) {
|
||||
uint32_t ArrayBufferBuilder::Length() {
|
||||
MutexAutoLock lock(mMutex);
|
||||
return mLength;
|
||||
}
|
||||
|
||||
uint32_t ArrayBufferBuilder::Capacity() {
|
||||
MutexAutoLock lock(mMutex);
|
||||
return mCapacity;
|
||||
}
|
||||
|
||||
JSObject* ArrayBufferBuilder::GetArrayBuffer(JSContext* aCx) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mMapPtr) {
|
||||
JSObject* obj = JS::NewMappedArrayBufferWithContents(aCx, mLength, mMapPtr);
|
||||
if (!obj) {
|
||||
|
@ -3751,7 +3773,7 @@ JSObject* ArrayBufferBuilder::getArrayBuffer(JSContext* aCx) {
|
|||
// we need to check for mLength == 0, because nothing may have been
|
||||
// added
|
||||
if (mCapacity > mLength || mLength == 0) {
|
||||
if (!setCapacity(mLength)) {
|
||||
if (!SetCapacityInternal(mLength, lock)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -3765,8 +3787,10 @@ JSObject* ArrayBufferBuilder::getArrayBuffer(JSContext* aCx) {
|
|||
return obj;
|
||||
}
|
||||
|
||||
nsresult ArrayBufferBuilder::mapToFileInPackage(const nsCString& aFile,
|
||||
nsresult ArrayBufferBuilder::MapToFileInPackage(const nsCString& aFile,
|
||||
nsIFile* aJarFile) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// Open Jar file to get related attributes of target file.
|
||||
|
@ -3801,7 +3825,7 @@ nsresult ArrayBufferBuilder::mapToFileInPackage(const nsCString& aFile,
|
|||
}
|
||||
|
||||
/* static */
|
||||
bool ArrayBufferBuilder::areOverlappingRegions(const uint8_t* aStart1,
|
||||
bool ArrayBufferBuilder::AreOverlappingRegions(const uint8_t* aStart1,
|
||||
uint32_t aLength1,
|
||||
const uint8_t* aStart2,
|
||||
uint32_t aLength2) {
|
||||
|
|
|
@ -73,24 +73,17 @@ struct OriginAttributesDictionary;
|
|||
// before creating the ArrayBuffer itself. Will do doubling
|
||||
// based reallocation, up to an optional maximum growth given.
|
||||
//
|
||||
// When all the data has been appended, call getArrayBuffer,
|
||||
// When all the data has been appended, call GetArrayBuffer,
|
||||
// passing in the JSContext* for which the ArrayBuffer object
|
||||
// is to be created. This also implicitly resets the builder,
|
||||
// or it can be reset explicitly at any point by calling reset().
|
||||
// is to be created. This also implicitly resets the builder.
|
||||
class ArrayBufferBuilder {
|
||||
uint8_t* mDataPtr;
|
||||
uint32_t mCapacity;
|
||||
uint32_t mLength;
|
||||
void* mMapPtr;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ArrayBufferBuilder);
|
||||
|
||||
ArrayBufferBuilder();
|
||||
~ArrayBufferBuilder();
|
||||
|
||||
void reset();
|
||||
|
||||
// Will truncate if aNewCap is < length().
|
||||
bool setCapacity(uint32_t aNewCap);
|
||||
// Will truncate if aNewCap is < Length().
|
||||
bool SetCapacity(uint32_t aNewCap);
|
||||
|
||||
// Append aDataLen bytes from data to the current buffer. If we
|
||||
// need to grow the buffer, grow by doubling the size up to a
|
||||
|
@ -99,13 +92,13 @@ class ArrayBufferBuilder {
|
|||
//
|
||||
// The data parameter must not overlap with anything beyond the
|
||||
// builder's current valid contents [0..length)
|
||||
bool append(const uint8_t* aNewData, uint32_t aDataLen,
|
||||
bool Append(const uint8_t* aNewData, uint32_t aDataLen,
|
||||
uint32_t aMaxGrowth = 0);
|
||||
|
||||
uint32_t length() { return mLength; }
|
||||
uint32_t capacity() { return mCapacity; }
|
||||
uint32_t Length();
|
||||
uint32_t Capacity();
|
||||
|
||||
JSObject* getArrayBuffer(JSContext* aCx);
|
||||
JSObject* GetArrayBuffer(JSContext* aCx);
|
||||
|
||||
// Memory mapping to starting position of file(aFile) in the zip
|
||||
// package(aJarFile).
|
||||
|
@ -113,11 +106,27 @@ class ArrayBufferBuilder {
|
|||
// The file in the zip package has to be uncompressed and the starting
|
||||
// position of the file must be aligned according to array buffer settings
|
||||
// in JS engine.
|
||||
nsresult mapToFileInPackage(const nsCString& aFile, nsIFile* aJarFile);
|
||||
nsresult MapToFileInPackage(const nsCString& aFile, nsIFile* aJarFile);
|
||||
|
||||
protected:
|
||||
static bool areOverlappingRegions(const uint8_t* aStart1, uint32_t aLength1,
|
||||
private:
|
||||
~ArrayBufferBuilder();
|
||||
|
||||
ArrayBufferBuilder(const ArrayBufferBuilder&) = delete;
|
||||
ArrayBufferBuilder& operator=(const ArrayBufferBuilder&) = delete;
|
||||
ArrayBufferBuilder& operator=(const ArrayBufferBuilder&&) = delete;
|
||||
|
||||
bool SetCapacityInternal(uint32_t aNewCap, const MutexAutoLock& aProofOfLock);
|
||||
|
||||
static bool AreOverlappingRegions(const uint8_t* aStart1, uint32_t aLength1,
|
||||
const uint8_t* aStart2, uint32_t aLength2);
|
||||
|
||||
Mutex mMutex;
|
||||
|
||||
// All of these are protected by mMutex.
|
||||
uint8_t* mDataPtr;
|
||||
uint32_t mCapacity;
|
||||
uint32_t mLength;
|
||||
void* mMapPtr;
|
||||
};
|
||||
|
||||
class nsXMLHttpRequestXPCOMifier;
|
||||
|
@ -718,7 +727,7 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest,
|
|||
|
||||
JS::Heap<JS::Value> mResultJSON;
|
||||
|
||||
ArrayBufferBuilder mArrayBufferBuilder;
|
||||
RefPtr<ArrayBufferBuilder> mArrayBufferBuilder;
|
||||
JS::Heap<JSObject*> mResultArrayBuffer;
|
||||
bool mIsMappedArrayBuffer;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче