Bug 658683: Make xhr.response not create a new ArrayBuffer every time it is accessed. r=sicking. Additional fixes by sicking, r=peterv

This commit is contained in:
Shawn Gong 2011-05-23 18:09:28 -07:00
parent 8e10a6f050
commit a19b1cf42c
4 changed files with 61 additions and 22 deletions

View File

@ -426,7 +426,8 @@ nsXMLHttpRequest::nsXMLHttpRequest()
mErrorLoad(PR_FALSE), mTimerIsActive(PR_FALSE), mErrorLoad(PR_FALSE), mTimerIsActive(PR_FALSE),
mProgressEventWasDelayed(PR_FALSE), mProgressEventWasDelayed(PR_FALSE),
mLoadLengthComputable(PR_FALSE), mLoadTotal(0), mLoadLengthComputable(PR_FALSE), mLoadTotal(0),
mFirstStartRequestSeen(PR_FALSE) mFirstStartRequestSeen(PR_FALSE),
mResultArrayBuffer(nsnull)
{ {
mResponseBodyUnicode.SetIsVoid(PR_TRUE); mResponseBodyUnicode.SetIsVoid(PR_TRUE);
nsLayoutStatics::AddRef(); nsLayoutStatics::AddRef();
@ -450,6 +451,12 @@ nsXMLHttpRequest::~nsXMLHttpRequest()
nsLayoutStatics::Release(); nsLayoutStatics::Release();
} }
void
nsXMLHttpRequest::RootResultArrayBuffer()
{
nsContentUtils::PreserveWrapper(static_cast<nsPIDOMEventTarget*>(this), this);
}
/** /**
* This Init method is called from the factory constructor. * This Init method is called from the factory constructor.
*/ */
@ -572,9 +579,9 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLHttpRequest,
nsIXMLHttpRequestUpload) nsIXMLHttpRequestUpload)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXMLHttpRequest, NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXMLHttpRequest,
nsXHREventTarget) nsXHREventTarget)
tmp->mResultArrayBuffer = nsnull;
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannel) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannel)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mReadRequest) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mReadRequest)
@ -592,6 +599,14 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXMLHttpRequest,
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mUpload) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mUpload)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsXMLHttpRequest,
nsXHREventTarget)
if(tmp->mResultArrayBuffer) {
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(tmp->mResultArrayBuffer,
"mResultArrayBuffer")
}
NS_IMPL_CYCLE_COLLECTION_TRACE_END
DOMCI_DATA(XMLHttpRequest, nsXMLHttpRequest) DOMCI_DATA(XMLHttpRequest, nsXMLHttpRequest)
// QueryInterface implementation for nsXMLHttpRequest // QueryInterface implementation for nsXMLHttpRequest
@ -839,27 +854,20 @@ NS_IMETHODIMP nsXMLHttpRequest::GetResponseText(nsAString& aResponseText)
return rv; return rv;
} }
nsresult nsXMLHttpRequest::GetResponseArrayBuffer(jsval *aResult) nsresult nsXMLHttpRequest::CreateResponseArrayBuffer(JSContext *aCx)
{ {
JSContext *cx = nsContentUtils::GetCurrentJSContext(); if (!aCx)
if (!cx)
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
if (!(mState & (XML_HTTP_REQUEST_DONE |
XML_HTTP_REQUEST_LOADING))) {
*aResult = JSVAL_NULL;
return NS_OK;
}
PRInt32 dataLen = mResponseBody.Length(); PRInt32 dataLen = mResponseBody.Length();
JSObject *obj = js_CreateArrayBuffer(cx, dataLen); RootResultArrayBuffer();
if (!obj) mResultArrayBuffer = js_CreateArrayBuffer(aCx, dataLen);
if (!mResultArrayBuffer) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
}
*aResult = OBJECT_TO_JSVAL(obj);
if (dataLen > 0) { if (dataLen > 0) {
js::ArrayBuffer *abuf = js::ArrayBuffer::fromJSObject(obj); js::ArrayBuffer *abuf = js::ArrayBuffer::fromJSObject(mResultArrayBuffer);
NS_ASSERTION(abuf, "What happened?"); NS_ASSERTION(abuf, "What happened?");
memcpy(abuf->data, mResponseBody.BeginReading(), dataLen); memcpy(abuf->data, mResponseBody.BeginReading(), dataLen);
} }
@ -954,7 +962,11 @@ NS_IMETHODIMP nsXMLHttpRequest::GetResponse(JSContext *aCx, jsval *aResult)
case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER: case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER:
if (mState & XML_HTTP_REQUEST_DONE) { if (mState & XML_HTTP_REQUEST_DONE) {
rv = GetResponseArrayBuffer(aResult); if (!mResultArrayBuffer) {
rv = CreateResponseArrayBuffer(aCx);
NS_ENSURE_SUCCESS(rv, rv);
}
*aResult = OBJECT_TO_JSVAL(mResultArrayBuffer);
} else { } else {
*aResult = JSVAL_NULL; *aResult = JSVAL_NULL;
} }
@ -1073,6 +1085,7 @@ nsXMLHttpRequest::Abort()
mResponseBodyUnicode.SetIsVoid(PR_TRUE); mResponseBodyUnicode.SetIsVoid(PR_TRUE);
mResponseBlob = nsnull; mResponseBlob = nsnull;
mState |= XML_HTTP_REQUEST_ABORTED; mState |= XML_HTTP_REQUEST_ABORTED;
mResultArrayBuffer = nsnull;
if (!(mState & (XML_HTTP_REQUEST_UNSENT | if (!(mState & (XML_HTTP_REQUEST_UNSENT |
XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_OPENED |

View File

@ -208,10 +208,10 @@ public:
void SetRequestObserver(nsIRequestObserver* aObserver); void SetRequestObserver(nsIRequestObserver* aObserver);
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXMLHttpRequest, NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsXMLHttpRequest,
nsXHREventTarget) nsXHREventTarget)
PRBool AllowUploadProgress(); PRBool AllowUploadProgress();
void RootResultArrayBuffer();
protected: protected:
friend class nsMultipartProxyListener; friend class nsMultipartProxyListener;
@ -224,7 +224,7 @@ protected:
PRUint32 toOffset, PRUint32 toOffset,
PRUint32 count, PRUint32 count,
PRUint32 *writeCount); PRUint32 *writeCount);
nsresult GetResponseArrayBuffer(jsval *aResult); nsresult CreateResponseArrayBuffer(JSContext* aCx);
void CreateResponseBlob(nsIRequest *request); void CreateResponseBlob(nsIRequest *request);
// Change the state of the object with this. The broadcast argument // Change the state of the object with this. The broadcast argument
// determines if the onreadystatechange listener should be called. // determines if the onreadystatechange listener should be called.
@ -345,6 +345,8 @@ protected:
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback; nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
nsCOMPtr<nsIChannel> mNewRedirectChannel; nsCOMPtr<nsIChannel> mNewRedirectChannel;
JSObject* mResultArrayBuffer;
}; };
// helper class to expose a progress DOM Event // helper class to expose a progress DOM Event

View File

@ -134,6 +134,16 @@ ab = xhr.response;
ok(ab != null, "should have a non-null arraybuffer"); ok(ab != null, "should have a non-null arraybuffer");
arraybuffer_equals_to(ab, "\xaa\xee\0\x03\xff\xff\xff\xff\xbb\xbb\xbb\xbb"); arraybuffer_equals_to(ab, "\xaa\xee\0\x03\xff\xff\xff\xff\xbb\xbb\xbb\xbb");
// test array buffer GetResult returns the same object
xhr = new XMLHttpRequest();
xhr.open("GET", 'file_XHR_binary1.bin', false);
xhr.responseType = 'arraybuffer';
xhr.send(null)
is(xhr.status, 200, "wrong status");
checkResponseTextAccessThrows(xhr);
checkResponseXMLAccessThrows(xhr);
is(xhr.response, xhr.response, "returns the same ArrayBuffer");
// test response (responseType='blob') // test response (responseType='blob')
var onloadCount = 0; var onloadCount = 0;
function checkOnloadCount() { function checkOnloadCount() {

View File

@ -436,6 +436,20 @@ public:
"not the nsISupports pointer we expect"); \ "not the nsISupports pointer we expect"); \
_class *tmp = Downcast(s); _class *tmp = Downcast(s);
#define NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(_class, _base_class) \
void \
NS_CYCLE_COLLECTION_CLASSNAME(_class)::Trace(void *p, \
TraceCallback aCallback, \
void *aClosure) \
{ \
nsISupports *s = static_cast<nsISupports*>(p); \
NS_ASSERTION(CheckForRightISupports(s), \
"not the nsISupports pointer we expect"); \
_class *tmp = static_cast<_class*>(Downcast(s)); \
NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Trace(s, \
aCallback, \
aClosure);
#define NS_IMPL_CYCLE_COLLECTION_TRACE_NATIVE_BEGIN(_class) \ #define NS_IMPL_CYCLE_COLLECTION_TRACE_NATIVE_BEGIN(_class) \
void \ void \
NS_CYCLE_COLLECTION_CLASSNAME(_class)::Trace(void *p, \ NS_CYCLE_COLLECTION_CLASSNAME(_class)::Trace(void *p, \