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

View File

@ -208,10 +208,10 @@ public:
void SetRequestObserver(nsIRequestObserver* aObserver);
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXMLHttpRequest,
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsXMLHttpRequest,
nsXHREventTarget)
PRBool AllowUploadProgress();
void RootResultArrayBuffer();
protected:
friend class nsMultipartProxyListener;
@ -224,7 +224,7 @@ protected:
PRUint32 toOffset,
PRUint32 count,
PRUint32 *writeCount);
nsresult GetResponseArrayBuffer(jsval *aResult);
nsresult CreateResponseArrayBuffer(JSContext* aCx);
void CreateResponseBlob(nsIRequest *request);
// Change the state of the object with this. The broadcast argument
// determines if the onreadystatechange listener should be called.
@ -345,6 +345,8 @@ protected:
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
nsCOMPtr<nsIChannel> mNewRedirectChannel;
JSObject* mResultArrayBuffer;
};
// 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");
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')
var onloadCount = 0;
function checkOnloadCount() {

View File

@ -436,6 +436,20 @@ public:
"not the nsISupports pointer we expect"); \
_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) \
void \
NS_CYCLE_COLLECTION_CLASSNAME(_class)::Trace(void *p, \