Bug 819741. Add support for passing an ArrayBufferView to XHR.send(). r=sicking,bent
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 19 Dec 2012 17:47:39 -0800
changeset 122153 42213ca59024f89cdd34a7081c184ac60e2d3600
parent 122152 3d2011652b37eecb3e0ea6210a4be108d3735e1b
child 122154 c2b6362784c219a350863e460c5950e0e288c8c2
push idunknown
push userunknown
push dateunknown
reviewerssicking, bent
bugs819741
milestone20.0a1
Bug 819741. Add support for passing an ArrayBufferView to XHR.send(). r=sicking,bent
content/base/src/nsXMLHttpRequest.cpp
content/base/src/nsXMLHttpRequest.h
content/base/test/test_XHRSendData.html
dom/webidl/XMLHttpRequest.webidl
dom/workers/XMLHttpRequest.cpp
dom/workers/XMLHttpRequest.h
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2515,29 +2515,30 @@ GetRequestBody(nsIInputStream* aStream, 
 
 static nsresult
 GetRequestBody(nsIXHRSendable* aSendable, nsIInputStream** aResult, uint64_t* aContentLength,
                nsACString& aContentType, nsACString& aCharset)
 {
   return aSendable->GetSendInfo(aResult, aContentLength, aContentType, aCharset);
 }
 
+// Used for array buffers and array buffer views
 static nsresult
-GetRequestBody(ArrayBuffer* aArrayBuffer, nsIInputStream** aResult, uint64_t* aContentLength,
+GetRequestBody(const uint8_t* aData, uint32_t aDataLength,
+               nsIInputStream** aResult, uint64_t* aContentLength,
                nsACString& aContentType, nsACString& aCharset)
 {
   aContentType.SetIsVoid(true);
   aCharset.Truncate();
 
-  int32_t length = aArrayBuffer->Length();
-  *aContentLength = length;
-  char* data = reinterpret_cast<char*>(aArrayBuffer->Data());
+  *aContentLength = aDataLength;
+  const char* data = reinterpret_cast<const char*>(aData);
 
   nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data, length,
+  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data, aDataLength,
                                       NS_ASSIGNMENT_COPY);
   NS_ENSURE_SUCCESS(rv, rv);
 
   stream.forget(aResult);
 
   return NS_OK;
 }
 
@@ -2590,17 +2591,18 @@ GetRequestBody(nsIVariant* aBody, nsIInp
     // ArrayBuffer?
     jsval realVal;
 
     nsresult rv = aBody->GetAsJSVal(&realVal);
     if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(realVal)) {
       JSObject *obj = JSVAL_TO_OBJECT(realVal);
       if (JS_IsArrayBufferObject(obj)) {
           ArrayBuffer buf(obj);
-          return GetRequestBody(&buf, aResult, aContentLength, aContentType, aCharset);
+          return GetRequestBody(buf.Data(), buf.Length(), aResult,
+                                aContentLength, aContentType, aCharset);
       }
     }
   }
   else if (dataType == nsIDataType::VTYPE_VOID ||
            dataType == nsIDataType::VTYPE_EMPTY) {
     // Makes us act as if !aBody, don't upload anything
     aContentType.AssignLiteral("text/plain");
     aCharset.AssignLiteral("UTF-8");
@@ -2632,18 +2634,25 @@ nsXMLHttpRequest::GetRequestBody(nsIVari
     return ::GetRequestBody(aVariant, aResult, aContentLength, aContentType, aCharset);
   }
 
   const RequestBody& body = aBody.Value();
   RequestBody::Value value = body.GetValue();
   switch (body.GetType()) {
     case nsXMLHttpRequest::RequestBody::ArrayBuffer:
     {
-      return ::GetRequestBody(value.mArrayBuffer, aResult, aContentLength,
-                              aContentType, aCharset);
+      return ::GetRequestBody(value.mArrayBuffer->Data(),
+                              value.mArrayBuffer->Length(), aResult,
+                              aContentLength, aContentType, aCharset);
+    }
+    case nsXMLHttpRequest::RequestBody::ArrayBufferView:
+    {
+      return ::GetRequestBody(value.mArrayBufferView->Data(),
+                              value.mArrayBufferView->Length(), aResult,
+                              aContentLength, aContentType, aCharset);
     }
     case nsXMLHttpRequest::RequestBody::Blob:
     {
       nsresult rv;
       nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(value.mBlob, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
 
       return ::GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -271,16 +271,20 @@ private:
   public:
     RequestBody() : mType(Uninitialized)
     {
     }
     RequestBody(mozilla::dom::ArrayBuffer* aArrayBuffer) : mType(ArrayBuffer)
     {
       mValue.mArrayBuffer = aArrayBuffer;
     }
+    RequestBody(mozilla::dom::ArrayBufferView* aArrayBufferView) : mType(ArrayBufferView)
+    {
+      mValue.mArrayBufferView = aArrayBufferView;
+    }
     RequestBody(nsIDOMBlob* aBlob) : mType(Blob)
     {
       mValue.mBlob = aBlob;
     }
     RequestBody(nsIDocument* aDocument) : mType(Document)
     {
       mValue.mDocument = aDocument;
     }
@@ -295,24 +299,26 @@ private:
     RequestBody(nsIInputStream* aStream) : mType(InputStream)
     {
       mValue.mStream = aStream;
     }
 
     enum Type {
       Uninitialized,
       ArrayBuffer,
+      ArrayBufferView,
       Blob,
       Document,
       DOMString,
       FormData,
       InputStream
     };
     union Value {
       mozilla::dom::ArrayBuffer* mArrayBuffer;
+      mozilla::dom::ArrayBufferView* mArrayBufferView;
       nsIDOMBlob* mBlob;
       nsIDocument* mDocument;
       const nsAString* mString;
       nsFormData* mFormData;
       nsIInputStream* mStream;
     };
 
     Type GetType() const
@@ -352,16 +358,20 @@ public:
   void Send(ErrorResult& aRv)
   {
     aRv = Send(Nullable<RequestBody>());
   }
   void Send(mozilla::dom::ArrayBuffer& aArrayBuffer, ErrorResult& aRv)
   {
     aRv = Send(RequestBody(&aArrayBuffer));
   }
+  void Send(mozilla::dom::ArrayBufferView& aArrayBufferView, ErrorResult& aRv)
+  {
+    aRv = Send(RequestBody(&aArrayBufferView));
+  }
   void Send(nsIDOMBlob* aBlob, ErrorResult& aRv)
   {
     NS_ASSERTION(aBlob, "Null should go to string version");
     aRv = Send(RequestBody(aBlob));
   }
   void Send(nsIDocument* aDoc, ErrorResult& aRv)
   {
     NS_ASSERTION(aDoc, "Null should go to string version");
--- a/content/base/test/test_XHRSendData.html
+++ b/content/base/test/test_XHRSendData.html
@@ -52,16 +52,25 @@ var shortInt8View = new Uint8Array(short
 shortInt8View[0] = 3;
 
 var longArray = new ArrayBuffer(512);
 var longInt8View = new Uint8Array(longArray);
 for (var i = 0; i < longInt8View.length; i++) {
   longInt8View[i] = i % 255;
 }
 
+// arraybufferview test objects
+var longArraySlice = longArray.slice(256, 384);
+var longInt32View1 = new Int32Array(longArraySlice)
+var longInt32View2 = new Int32Array(longArray, 256, 32)
+var longInt16View1 = new Uint16Array(longArraySlice)
+var longInt16View2 = new Uint16Array(longArray, 256, 64)
+var longInt8View1 = new Int8Array(longArraySlice)
+var longInt8View2 = new Int8Array(longArray, 256, 128)
+
 extensions.forEach(
     function (extension) {
       var testFile = createFileWithDataExt(testData, extension);
       testFiles.push(testFile);
 
       var fileList = document.getElementById('fileList');
       fileList.value = testFile.path;
       testDOMFiles.push(fileList.files[0]);
@@ -164,16 +173,40 @@ tests = [{ body: null,
          { body: shortArray,
            resBody: shortArray,
            resType: "arraybuffer"
          },
          { body: longArray,
            resBody: longArray,
            resType: "arraybuffer"
          },
+         { body: longInt32View1,
+           resBody: longArraySlice,
+           resType: "arraybuffer"
+         },
+         { body: longInt32View2,
+           resBody: longArraySlice,
+           resType: "arraybuffer"
+         },
+         { body: longInt16View1,
+           resBody: longArraySlice,
+           resType: "arraybuffer"
+         },
+         { body: longInt16View2,
+           resBody: longArraySlice,
+           resType: "arraybuffer"
+         },
+         { body: longInt8View1,
+           resBody: longArraySlice,
+           resType: "arraybuffer"
+         },
+         { body: longInt8View2,
+           resBody: longArraySlice,
+           resType: "arraybuffer"
+         },
          ];
 
 for (var i = 0; i < testDOMFiles.length; i++) {
   tests.push({ body: testDOMFiles[i],
                resBody: testData,
                resContentType: fileTypes[i],
                resContentLength: testData.length,
               });
--- a/dom/webidl/XMLHttpRequest.webidl
+++ b/dom/webidl/XMLHttpRequest.webidl
@@ -86,16 +86,18 @@ interface XMLHttpRequest : XMLHttpReques
   [Throws=Workers]
   readonly attribute XMLHttpRequestUpload upload;
 
   [Throws]
   void send();
   [Throws]
   void send(ArrayBuffer data);
   [Throws]
+  void send(ArrayBufferView data);
+  [Throws]
   void send(Blob data);
   [Throws]
   void send(Document data);
   [Throws]
   void send(DOMString? data);
   [Throws]
   void send(FormData data);
   [Throws]
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -2004,17 +2004,18 @@ XMLHttpRequest::Send(JSObject* aBody, Er
   if (!mProxy) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   JSContext* cx = GetJSContext();
 
   jsval valToClone;
-  if (JS_IsArrayBufferObject(aBody) || file::GetDOMBlobFromJSObject(aBody)) {
+  if (JS_IsArrayBufferObject(aBody) || JS_IsArrayBufferViewObject(aBody) ||
+      file::GetDOMBlobFromJSObject(aBody)) {
     valToClone = OBJECT_TO_JSVAL(aBody);
   }
   else {
     JSString* bodyStr = JS_ValueToString(cx, OBJECT_TO_JSVAL(aBody));
     if (!bodyStr) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
--- a/dom/workers/XMLHttpRequest.h
+++ b/dom/workers/XMLHttpRequest.h
@@ -185,16 +185,21 @@ public:
   }
 
   void
   Send(ArrayBuffer& aBody, ErrorResult& aRv) {
     return Send(aBody.Obj(), aRv);
   }
 
   void
+  Send(ArrayBufferView& aBody, ErrorResult& aRv) {
+    return Send(aBody.Obj(), aRv);
+  }
+
+  void
   SendAsBinary(const nsAString& aBody, ErrorResult& aRv);
 
   void
   Abort(ErrorResult& aRv);
 
   uint16_t
   GetStatus(ErrorResult& aRv) const
   {