Skip to content

Commit

Permalink
Bug 945152 - Part 3-2: XHR changes for mapped array buffer. r=smaug
Browse files Browse the repository at this point in the history
  • Loading branch information
moztpe committed May 16, 2014
1 parent 29e21bb commit a5e37bf
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 10 deletions.
3 changes: 3 additions & 0 deletions b2g/app/b2g.js
Original file line number Diff line number Diff line change
Expand Up @@ -974,3 +974,6 @@ pref("dom.wakelock.enabled", true);
pref("services.sync.fxaccounts.enabled", true);
pref("identity.fxaccounts.enabled", true);
#endif

// Enable mapped array buffer
pref("dom.mapped_arraybuffer.enabled", true);
145 changes: 135 additions & 10 deletions content/base/src/nsXMLHttpRequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

#include "nsXMLHttpRequest.h"

#ifndef XP_WIN
#include <unistd.h>
#endif
#include "mozilla/ArrayUtils.h"
#include "mozilla/dom/XMLHttpRequestUploadBinding.h"
#include "mozilla/EventDispatcher.h"
Expand All @@ -15,6 +18,7 @@
#include "nsIDOMDocument.h"
#include "nsIDOMProgressEvent.h"
#include "nsIJARChannel.h"
#include "nsIJARURI.h"
#include "nsLayoutCID.h"
#include "nsReadableUtils.h"

Expand Down Expand Up @@ -69,8 +73,10 @@
#include "nsStreamListenerWrapper.h"
#include "xpcjsid.h"
#include "nsITimedChannel.h"

#include "nsWrapperCacheInlines.h"
#include "nsZipArchive.h"
#include "mozilla/Preferences.h"
#include "private/pprio.h"

using namespace mozilla;
using namespace mozilla::dom;
Expand Down Expand Up @@ -296,6 +302,7 @@ nsXMLHttpRequest::nsXMLHttpRequest()
mInLoadProgressEvent(false),
mResultJSON(JSVAL_VOID),
mResultArrayBuffer(nullptr),
mIsMappedArrayBuffer(false),
mXPCOMifier(nullptr)
{
SetIsDOMBinding();
Expand Down Expand Up @@ -1748,7 +1755,8 @@ nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
xmlHttpRequest->mResponseBlob = nullptr;
}
} else if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER ||
} else if ((xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
!xmlHttpRequest->mIsMappedArrayBuffer) ||
xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
// get the initial capacity to something reasonable to avoid a bunch of reallocs right
// at the start
Expand Down Expand Up @@ -1955,12 +1963,46 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)

// Set up arraybuffer
if (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER && NS_SUCCEEDED(status)) {
int64_t contentLength;
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));
if (mIsMappedArrayBuffer) {
nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
if (jarChannel) {
nsCOMPtr<nsIURI> uri;
rv = channel->GetURI(getter_AddRefs(uri));
if (NS_SUCCEEDED(rv)) {
nsAutoCString file;
nsAutoCString scheme;
uri->GetScheme(scheme);
if (scheme.LowerCaseEqualsLiteral("app")) {
uri->GetPath(file);
// The actual file inside zip package has no leading slash.
file.Trim("/", true, false, false);
} else if (scheme.LowerCaseEqualsLiteral("jar")) {
nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri);
if (jarURI) {
jarURI->GetJAREntry(file);
}
}
nsCOMPtr<nsIFile> jarFile;
jarChannel->GetJarFile(getter_AddRefs(jarFile));
rv = mArrayBufferBuilder.mapToFileInPackage(file, jarFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
mIsMappedArrayBuffer = false;
} else {
channel->SetContentType(NS_LITERAL_CSTRING("application/mem-mapped"));
}
}
}
}
// If memory mapping failed, mIsMappedArrayBuffer would be set to false,
// and we want it fallback to the malloc way.
if (!mIsMappedArrayBuffer) {
int64_t contentLength;
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));
}
}
}

Expand Down Expand Up @@ -2145,7 +2187,8 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
} else if (NS_SUCCEEDED(status) &&
(mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER ||
((mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
!mIsMappedArrayBuffer) ||
mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER)) {
// set the capacity down to the actual length, to realloc back
// down to the actual size
Expand Down Expand Up @@ -2880,6 +2923,21 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
NS_ENSURE_SUCCESS(rv, rv);
}
else {
mIsMappedArrayBuffer = false;
if (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
Preferences::GetBool("dom.mapped_arraybuffer.enabled", false)) {
nsCOMPtr<nsIURI> uri;
nsAutoCString scheme;

rv = mChannel->GetURI(getter_AddRefs(uri));
if (NS_SUCCEEDED(rv)) {
uri->GetScheme(scheme);
if (scheme.LowerCaseEqualsLiteral("app") ||
scheme.LowerCaseEqualsLiteral("jar")) {
mIsMappedArrayBuffer = true;
}
}
}
// Start reading from the channel
rv = mChannel->AsyncOpen(listener, nullptr);
}
Expand Down Expand Up @@ -3844,7 +3902,8 @@ namespace mozilla {
ArrayBufferBuilder::ArrayBufferBuilder()
: mDataPtr(nullptr),
mCapacity(0),
mLength(0)
mLength(0),
mMapPtr(nullptr)
{
}

Expand All @@ -3859,13 +3918,21 @@ ArrayBufferBuilder::reset()
if (mDataPtr) {
JS_free(nullptr, mDataPtr);
}

if (mMapPtr) {
JS_ReleaseMappedArrayBufferContents(mMapPtr, mLength);
mMapPtr = nullptr;
}

mDataPtr = nullptr;
mCapacity = mLength = 0;
}

bool
ArrayBufferBuilder::setCapacity(uint32_t aNewCap)
{
MOZ_ASSERT(!mMapPtr);

uint8_t *newdata = (uint8_t *) JS_ReallocateArrayBufferContents(nullptr, aNewCap, mDataPtr, mCapacity);
if (!newdata) {
return false;
Expand All @@ -3884,6 +3951,8 @@ bool
ArrayBufferBuilder::append(const uint8_t *aNewData, uint32_t aDataLen,
uint32_t aMaxGrowth)
{
MOZ_ASSERT(!mMapPtr);

if (mLength + aDataLen > mCapacity) {
uint32_t newcap;
// Double while under aMaxGrowth or if not specified.
Expand Down Expand Up @@ -3921,6 +3990,18 @@ ArrayBufferBuilder::append(const uint8_t *aNewData, uint32_t aDataLen,
JSObject*
ArrayBufferBuilder::getArrayBuffer(JSContext* aCx)
{
if (mMapPtr) {
JSObject* obj = JS_NewMappedArrayBufferWithContents(aCx, mLength, mMapPtr);
if (!obj) {
JS_ReleaseMappedArrayBufferContents(mMapPtr, mLength);
}
mMapPtr = nullptr;

// The memory-mapped contents will be released when obj been finalized(GCed
// or neutered).
return obj;
}

// we need to check for mLength == 0, because nothing may have been
// added
if (mCapacity > mLength || mLength == 0) {
Expand All @@ -3939,6 +4020,50 @@ ArrayBufferBuilder::getArrayBuffer(JSContext* aCx)
return obj;
}

nsresult
ArrayBufferBuilder::mapToFileInPackage(const nsCString& aFile,
nsIFile* aJarFile)
{
#ifdef XP_WIN
// TODO: Bug 988813 - Support memory mapped array buffer for Windows platform.
MOZ_CRASH("Not implemented");
return NS_ERROR_NOT_IMPLEMENTED;
#else
nsresult rv;

// Open Jar file to get related attributes of target file.
nsRefPtr<nsZipArchive> zip = new nsZipArchive();
rv = zip->OpenArchive(aJarFile);
if (NS_FAILED(rv)) {
return rv;
}
nsZipItem* zipItem = zip->GetItem(aFile.get());
if (NS_FAILED(rv)) {
return rv;
}

// If file was added to the package as stored(uncompressed), map to the
// offset of file in zip package.
if (!zipItem->Compression()) {
uint32_t offset = zip->GetDataOffset(zipItem);
uint32_t size = zipItem->RealSize();
mozilla::AutoFDClose pr_fd;
mozilla::ScopedClose fd;
rv = aJarFile->OpenNSPRFileDesc(PR_RDONLY, 0, &pr_fd.rwget());
if (NS_FAILED(rv)) {
return rv;
}
fd.rwget() = PR_FileDesc2NativeHandle(pr_fd);
mMapPtr = JS_CreateMappedArrayBufferContents(fd, offset, size);
if (mMapPtr) {
mLength = size;
return NS_OK;
}
}
return NS_ERROR_FAILURE;
#endif
}

/* static */ bool
ArrayBufferBuilder::areOverlappingRegions(const uint8_t* aStart1,
uint32_t aLength1,
Expand Down
10 changes: 10 additions & 0 deletions content/base/src/nsXMLHttpRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class ArrayBufferBuilder
uint8_t* mDataPtr;
uint32_t mCapacity;
uint32_t mLength;
void* mMapPtr;
public:
ArrayBufferBuilder();
~ArrayBufferBuilder();
Expand All @@ -89,6 +90,14 @@ class ArrayBufferBuilder

JSObject* getArrayBuffer(JSContext* aCx);

// Memory mapping to starting position of file(aFile) in the zip
// package(aJarFile).
//
// 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);

protected:
static bool areOverlappingRegions(const uint8_t* aStart1, uint32_t aLength1,
const uint8_t* aStart2, uint32_t aLength2);
Expand Down Expand Up @@ -727,6 +736,7 @@ class nsXMLHttpRequest : public nsXHREventTarget,

mozilla::ArrayBufferBuilder mArrayBufferBuilder;
JS::Heap<JSObject*> mResultArrayBuffer;
bool mIsMappedArrayBuffer;

void ResetResponse();

Expand Down

0 comments on commit a5e37bf

Please sign in to comment.