[tor-commits] [tor-browser/esr24] Bug 999651, bug 995679, bug 1009952, bug 1011007, bug 991981. r=sfink, r=shu, r=jandem, r=jdm, r=luke, r=bbouvier, r=nmatsakis, r=bz, r=ehsan, r=jgilbert, r=smaug, r=sicking, r=terrence, r=bholley, r=bent, r=efaust, r=jorendorff, a=lmandel

mikeperry at torproject.org mikeperry at torproject.org
Fri Aug 29 05:26:41 UTC 2014


commit 9aeffb05a552f1beb1ff37adc88df0640ffa8ca3
Author: Jeff Walden <jwalden at mit.edu>
Date:   Mon Jun 2 11:25:43 2014 -0700

    Bug 999651, bug 995679, bug 1009952, bug 1011007, bug 991981. r=sfink, r=shu, r=jandem, r=jdm, r=luke, r=bbouvier, r=nmatsakis, r=bz, r=ehsan, r=jgilbert, r=smaug, r=sicking, r=terrence, r=bholley, r=bent, r=efaust, r=jorendorff, a=lmandel
    
    --HG--
    extra : rebase_source : 2487b6e09f4caf827c3ba5e0283e3d30c74e5d42
---
 CLOBBER                                         |    2 +-
 content/base/src/WebSocket.cpp                  |   10 +-
 content/base/src/nsDOMDataChannel.cpp           |   10 +-
 content/base/src/nsDOMParser.cpp                |    2 +
 content/base/src/nsXMLHttpRequest.cpp           |   11 +-
 content/canvas/src/CanvasRenderingContext2D.cpp |   14 +-
 content/canvas/src/CanvasRenderingContext2D.h   |    3 +-
 content/canvas/src/WebGLContext.h               |   15 ++
 content/canvas/src/WebGLContextGL.cpp           |   43 ++++-
 content/html/content/public/HTMLAudioElement.h  |    1 +
 content/media/webaudio/AnalyserNode.cpp         |    6 +
 content/media/webaudio/AudioContext.cpp         |   17 +-
 content/media/webaudio/AudioParam.h             |    1 +
 content/media/webaudio/BiquadFilterNode.cpp     |    4 +
 content/media/webaudio/WaveShaperNode.cpp       |    2 +
 dom/bindings/TypedArray.h                       |   88 +++++++----
 dom/encoding/TextDecoder.h                      |    1 +
 dom/workers/TextDecoder.h                       |    1 +
 js/src/builtin/TestingFunctions.cpp             |   50 ++++++
 js/src/jit/IonBuilder.cpp                       |   92 +----------
 js/src/jit/MIR.h                                |   10 +-
 js/src/js.msg                                   |    4 +-
 js/src/jsfriendapi.h                            |  111 +++++++++++++
 js/src/jstypedarray.cpp                         |  190 +++++++++++++++++------
 js/src/jstypedarray.h                           |   16 +-
 js/src/jstypedarrayinlines.h                    |    8 +
 netwerk/base/src/ArrayBufferInputStream.cpp     |   11 ++
 xpcom/io/nsBinaryStream.cpp                     |   56 +++++--
 28 files changed, 561 insertions(+), 218 deletions(-)

diff --git a/CLOBBER b/CLOBBER
index 469d72c..550e8c1 100644
--- a/CLOBBER
+++ b/CLOBBER
@@ -18,4 +18,4 @@
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Bug 902908 renamed js/src/ion to js/src/jit and required a clobber
\ No newline at end of file
+Bug 999651 et al. require a clobber for some unknown reason
diff --git a/content/base/src/WebSocket.cpp b/content/base/src/WebSocket.cpp
index aba7007..2953aa3 100644
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -1221,7 +1221,10 @@ WebSocket::Send(ArrayBuffer& aData,
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
-  MOZ_ASSERT(sizeof(*aData.Data()) == 1);
+  aData.ComputeLengthAndData();
+
+  static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
+
   uint32_t len = aData.Length();
   char* data = reinterpret_cast<char*>(aData.Data());
 
@@ -1235,7 +1238,10 @@ WebSocket::Send(ArrayBufferView& aData,
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
-  MOZ_ASSERT(sizeof(*aData.Data()) == 1);
+  aData.ComputeLengthAndData();
+
+  static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
+
   uint32_t len = aData.Length();
   char* data = reinterpret_cast<char*>(aData.Data());
 
diff --git a/content/base/src/nsDOMDataChannel.cpp b/content/base/src/nsDOMDataChannel.cpp
index 3e038de..a7f4d93 100644
--- a/content/base/src/nsDOMDataChannel.cpp
+++ b/content/base/src/nsDOMDataChannel.cpp
@@ -292,7 +292,10 @@ nsDOMDataChannel::Send(ArrayBuffer& aData, ErrorResult& aRv)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
-  MOZ_ASSERT(sizeof(*aData.Data()) == 1);
+  aData.ComputeLengthAndData();
+
+  static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
+
   uint32_t len = aData.Length();
   char* data = reinterpret_cast<char*>(aData.Data());
 
@@ -305,7 +308,10 @@ nsDOMDataChannel::Send(ArrayBufferView& aData, ErrorResult& aRv)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
-  MOZ_ASSERT(sizeof(*aData.Data()) == 1);
+  aData.ComputeLengthAndData();
+
+  static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
+
   uint32_t len = aData.Length();
   char* data = reinterpret_cast<char*>(aData.Data());
 
diff --git a/content/base/src/nsDOMParser.cpp b/content/base/src/nsDOMParser.cpp
index 0ab06d9..c6bc60d 100644
--- a/content/base/src/nsDOMParser.cpp
+++ b/content/base/src/nsDOMParser.cpp
@@ -137,6 +137,8 @@ already_AddRefed<nsIDocument>
 nsDOMParser::ParseFromBuffer(const Uint8Array& aBuf, uint32_t aBufLen,
                              SupportedType aType, ErrorResult& rv)
 {
+  aBuf.ComputeLengthAndData();
+
   if (aBufLen > aBuf.Length()) {
     rv.Throw(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY);
     return nullptr;
diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp
index 22fe0c8..418edae 100644
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2440,6 +2440,7 @@ GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult, uint64_t* aContentLe
       JS::Rooted<JSObject*> obj(cx, JSVAL_TO_OBJECT(realVal));
       if (JS_IsArrayBufferObject(obj)) {
           ArrayBuffer buf(obj);
+          buf.ComputeLengthAndData();
           return GetRequestBody(buf.Data(), buf.Length(), aResult,
                                 aContentLength, aContentType, aCharset);
       }
@@ -2483,14 +2484,16 @@ nsXMLHttpRequest::GetRequestBody(nsIVariant* aVariant,
   switch (body.GetType()) {
     case nsXMLHttpRequest::RequestBody::ArrayBuffer:
     {
-      return ::GetRequestBody(value.mArrayBuffer->Data(),
-                              value.mArrayBuffer->Length(), aResult,
+      const ArrayBuffer* buffer = value.mArrayBuffer;
+      buffer->ComputeLengthAndData();
+      return ::GetRequestBody(buffer->Data(), buffer->Length(), aResult,
                               aContentLength, aContentType, aCharset);
     }
     case nsXMLHttpRequest::RequestBody::ArrayBufferView:
     {
-      return ::GetRequestBody(value.mArrayBufferView->Data(),
-                              value.mArrayBufferView->Length(), aResult,
+      const ArrayBufferView* view = value.mArrayBufferView;
+      view->ComputeLengthAndData();
+      return ::GetRequestBody(view->Data(), view->Length(), aResult,
                               aContentLength, aContentType, aCharset);
     }
     case nsXMLHttpRequest::RequestBody::Blob:
diff --git a/content/canvas/src/CanvasRenderingContext2D.cpp b/content/canvas/src/CanvasRenderingContext2D.cpp
index 8c67a87..ea685f1 100644
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -3559,7 +3559,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
 
   error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
                                 imageData.Width(), imageData.Height(),
-                                arr.Data(), arr.Length(), false, 0, 0, 0, 0);
+                                &arr, false, 0, 0, 0, 0);
 }
 
 void
@@ -3573,7 +3573,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
 
   error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
                                 imageData.Width(), imageData.Height(),
-                                arr.Data(), arr.Length(), true,
+                                &arr, true,
                                 JS_DoubleToInt32(dirtyX),
                                 JS_DoubleToInt32(dirtyY),
                                 JS_DoubleToInt32(dirtyWidth),
@@ -3585,7 +3585,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
 
 nsresult
 CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
-                                                unsigned char *aData, uint32_t aDataLen,
+                                                dom::Uint8ClampedArray* aArray,
                                                 bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
                                                 int32_t dirtyWidth, int32_t dirtyHeight)
 {
@@ -3638,8 +3638,12 @@ CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w
     return NS_OK;
   }
 
+  aArray->ComputeLengthAndData();
+
+  uint32_t dataLen = aArray->Length();
+
   uint32_t len = w * h * 4;
-  if (aDataLen != len) {
+  if (dataLen != len) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
@@ -3650,7 +3654,7 @@ CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w
     return NS_ERROR_FAILURE;
   }
 
-  uint8_t *src = aData;
+  uint8_t *src = aArray->Data();
   uint8_t *dst = imgsurf->Data();
 
   for (uint32_t j = 0; j < h; j++) {
diff --git a/content/canvas/src/CanvasRenderingContext2D.h b/content/canvas/src/CanvasRenderingContext2D.h
index d0f8fa0..0aff60d 100644
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -21,6 +21,7 @@
 #include "mozilla/dom/CanvasGradient.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
 #include "mozilla/dom/CanvasPattern.h"
+#include "mozilla/dom/TypedArray.h"
 #include "mozilla/gfx/Rect.h"
 
 class nsXULElement;
@@ -443,7 +444,7 @@ protected:
                              JSObject** aRetval);
 
   nsresult PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
-                                 unsigned char *aData, uint32_t aDataLen,
+                                 dom::Uint8ClampedArray* aArray,
                                  bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
                                  int32_t dirtyWidth, int32_t dirtyHeight);
 
diff --git a/content/canvas/src/WebGLContext.h b/content/canvas/src/WebGLContext.h
index e8e55db..db0c20a 100644
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -585,6 +585,7 @@ public:
                    WebGLfloat z, WebGLfloat w);
     
     void Uniform1iv(WebGLUniformLocation* location, dom::Int32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform1iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform1iv(WebGLUniformLocation* location,
@@ -595,6 +596,7 @@ public:
                          const WebGLint* data);
 
     void Uniform2iv(WebGLUniformLocation* location, dom::Int32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform2iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform2iv(WebGLUniformLocation* location,
@@ -605,6 +607,7 @@ public:
                          const WebGLint* data);
 
     void Uniform3iv(WebGLUniformLocation* location, dom::Int32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform3iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform3iv(WebGLUniformLocation* location,
@@ -615,6 +618,7 @@ public:
                          const WebGLint* data);
     
     void Uniform4iv(WebGLUniformLocation* location, dom::Int32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform4iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform4iv(WebGLUniformLocation* location,
@@ -625,6 +629,7 @@ public:
                          const WebGLint* data);
 
     void Uniform1fv(WebGLUniformLocation* location, dom::Float32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform1fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform1fv(WebGLUniformLocation* location,
@@ -635,6 +640,7 @@ public:
                          const WebGLfloat* data);
 
     void Uniform2fv(WebGLUniformLocation* location, dom::Float32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform2fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform2fv(WebGLUniformLocation* location,
@@ -645,6 +651,7 @@ public:
                          const WebGLfloat* data);
 
     void Uniform3fv(WebGLUniformLocation* location, dom::Float32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform3fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform3fv(WebGLUniformLocation* location,
@@ -655,6 +662,7 @@ public:
                          const WebGLfloat* data);
     
     void Uniform4fv(WebGLUniformLocation* location, dom::Float32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform4fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform4fv(WebGLUniformLocation* location,
@@ -667,6 +675,7 @@ public:
     void UniformMatrix2fv(WebGLUniformLocation* location,
                           WebGLboolean transpose,
                           dom::Float32Array &value) {
+        value.ComputeLengthAndData();
         UniformMatrix2fv_base(location, transpose, value.Length(), value.Data());
     }
     void UniformMatrix2fv(WebGLUniformLocation* location,
@@ -682,6 +691,7 @@ public:
     void UniformMatrix3fv(WebGLUniformLocation* location,
                           WebGLboolean transpose,
                           dom::Float32Array &value) {
+        value.ComputeLengthAndData();
         UniformMatrix3fv_base(location, transpose, value.Length(), value.Data());
     }
     void UniformMatrix3fv(WebGLUniformLocation* location,
@@ -697,6 +707,7 @@ public:
     void UniformMatrix4fv(WebGLUniformLocation* location,
                           WebGLboolean transpose,
                           dom::Float32Array &value) {
+        value.ComputeLengthAndData();
         UniformMatrix4fv_base(location, transpose, value.Length(), value.Data());
     }
     void UniformMatrix4fv(WebGLUniformLocation* location,
@@ -731,6 +742,7 @@ public:
                         WebGLfloat x2, WebGLfloat x3);
 
     void VertexAttrib1fv(WebGLuint idx, dom::Float32Array &arr) {
+        arr.ComputeLengthAndData();
         VertexAttrib1fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib1fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) {
@@ -740,6 +752,7 @@ public:
                               const WebGLfloat* ptr);
 
     void VertexAttrib2fv(WebGLuint idx, dom::Float32Array &arr) {
+        arr.ComputeLengthAndData();
         VertexAttrib2fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib2fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) {
@@ -749,6 +762,7 @@ public:
                               const WebGLfloat* ptr);
 
     void VertexAttrib3fv(WebGLuint idx, dom::Float32Array &arr) {
+        arr.ComputeLengthAndData();
         VertexAttrib3fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib3fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) {
@@ -758,6 +772,7 @@ public:
                               const WebGLfloat* ptr);
 
     void VertexAttrib4fv(WebGLuint idx, dom::Float32Array &arr) {
+        arr.ComputeLengthAndData();
         VertexAttrib4fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib4fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) {
diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp
index 6b4c77b..c1f943f 100644
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -431,6 +431,8 @@ WebGLContext::BufferData(WebGLenum target, ArrayBuffer *data, WebGLenum usage)
     MakeContextCurrent();
     InvalidateCachedMinInUseAttribArrayLength();
 
+    data->ComputeLengthAndData();
+
     GLenum error = CheckedBufferData(target, data->Length(), data->Data(), usage);
 
     if (error) {
@@ -469,6 +471,8 @@ WebGLContext::BufferData(WebGLenum target, ArrayBufferView& data, WebGLenum usag
     InvalidateCachedMinInUseAttribArrayLength();
     MakeContextCurrent();
 
+    data.ComputeLengthAndData();
+
     GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage);
     if (error) {
         GenerateWarning("bufferData generated error %s", ErrorName(error));
@@ -509,6 +513,8 @@ WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
+    data->ComputeLengthAndData();
+
     CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + data->Length();
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length");
@@ -547,6 +553,8 @@ WebGLContext::BufferSubData(WebGLenum target, WebGLsizeiptr byteOffset,
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferSubData: no buffer bound!");
 
+    data.ComputeLengthAndData();
+
     CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + data.Length();
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length");
@@ -3382,7 +3390,11 @@ WebGLContext::ReadPixels(WebGLint x, WebGLint y, WebGLsizei width,
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("readPixels: integer overflow computing the needed buffer size");
 
-    uint32_t dataByteLen = JS_GetTypedArrayByteLength(pixels->Obj());
+    // Compute length and data.  Don't reenter after this point, lest the
+    // precomputed go out of sync with the instant length/data.
+    pixels->ComputeLengthAndData();
+
+    uint32_t dataByteLen = pixels->Length();
     if (checked_neededByteLength.value() > dataByteLen)
         return ErrorInvalidOperation("readPixels: buffer too small");
 
@@ -4584,6 +4596,8 @@ WebGLContext::CompressedTexImage2D(WebGLenum target, WebGLint level, WebGLenum i
         return;
     }
 
+    view.ComputeLengthAndData();
+
     uint32_t byteLength = view.Length();
     if (!ValidateCompressedTextureSize(target, level, internalformat, width, height, byteLength, "compressedTexImage2D")) {
         return;
@@ -4633,6 +4647,8 @@ WebGLContext::CompressedTexSubImage2D(WebGLenum target, WebGLint level, WebGLint
         return;
     }
 
+    view.ComputeLengthAndData();
+
     uint32_t byteLength = view.Length();
     if (!ValidateCompressedTextureSize(target, level, format, width, height, byteLength, "compressedTexSubImage2D")) {
         return;
@@ -5135,10 +5151,23 @@ WebGLContext::TexImage2D(WebGLenum target, WebGLint level,
     if (!IsContextStable())
         return;
 
+    void* data;
+    uint32_t length;
+    int jsArrayType;
+    if (!pixels) {
+        data = nullptr;
+        length = 0;
+        jsArrayType = -1;
+    } else {
+        pixels->ComputeLengthAndData();
+
+        data = pixels->Data();
+        length = pixels->Length();
+        jsArrayType = int(JS_GetArrayBufferViewType(pixels->Obj()));
+    }
+
     return TexImage2D_base(target, level, internalformat, width, height, 0, border, format, type,
-                           pixels ? pixels->Data() : 0,
-                           pixels ? pixels->Length() : 0,
-                           pixels ? (int)JS_GetArrayBufferViewType(pixels->Obj()) : -1,
+                           data, length, jsArrayType,
                            WebGLTexelConversions::Auto, false);
 }
 
@@ -5156,6 +5185,8 @@ WebGLContext::TexImage2D(WebGLenum target, WebGLint level,
     }
     
     Uint8ClampedArray arr(pixels->GetDataObject());
+    arr.ComputeLengthAndData();
+
     return TexImage2D_base(target, level, internalformat, pixels->Width(),
                            pixels->Height(), 4*pixels->Width(), 0,
                            format, type, arr.Data(), arr.Length(), -1,
@@ -5289,6 +5320,8 @@ WebGLContext::TexSubImage2D(WebGLenum target, WebGLint level,
     if (!pixels)
         return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
 
+    pixels->ComputeLengthAndData();
+
     return TexSubImage2D_base(target, level, xoffset, yoffset,
                               width, height, 0, format, type,
                               pixels->Data(), pixels->Length(),
@@ -5309,6 +5342,8 @@ WebGLContext::TexSubImage2D(WebGLenum target, WebGLint level,
         return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
 
     Uint8ClampedArray arr(pixels->GetDataObject());
+    arr.ComputeLengthAndData();
+
     return TexSubImage2D_base(target, level, xoffset, yoffset,
                               pixels->Width(), pixels->Height(),
                               4*pixels->Width(), format, type,
diff --git a/content/html/content/public/HTMLAudioElement.h b/content/html/content/public/HTMLAudioElement.h
index 49fcd23..3c19e84 100644
--- a/content/html/content/public/HTMLAudioElement.h
+++ b/content/html/content/public/HTMLAudioElement.h
@@ -63,6 +63,7 @@ public:
 
   uint32_t MozWriteAudio(const Float32Array& aData, ErrorResult& aRv)
   {
+    aData.ComputeLengthAndData();
     return MozWriteAudio(aData.Data(), aData.Length(), aRv);
   }
   uint32_t MozWriteAudio(const Sequence<float>& aData, ErrorResult& aRv)
diff --git a/content/media/webaudio/AnalyserNode.cpp b/content/media/webaudio/AnalyserNode.cpp
index d5fbe9c..613852d 100644
--- a/content/media/webaudio/AnalyserNode.cpp
+++ b/content/media/webaudio/AnalyserNode.cpp
@@ -150,6 +150,8 @@ AnalyserNode::GetFloatFrequencyData(Float32Array& aArray)
     return;
   }
 
+  aArray.ComputeLengthAndData();
+
   float* buffer = aArray.Data();
   uint32_t length = std::min(aArray.Length(), mOutputBuffer.Length());
 
@@ -168,6 +170,8 @@ AnalyserNode::GetByteFrequencyData(Uint8Array& aArray)
 
   const double rangeScaleFactor = 1.0 / (mMaxDecibels - mMinDecibels);
 
+  aArray.ComputeLengthAndData();
+
   unsigned char* buffer = aArray.Data();
   uint32_t length = std::min(aArray.Length(), mOutputBuffer.Length());
 
@@ -183,6 +187,8 @@ AnalyserNode::GetByteFrequencyData(Uint8Array& aArray)
 void
 AnalyserNode::GetByteTimeDomainData(Uint8Array& aArray)
 {
+  aArray.ComputeLengthAndData();
+
   unsigned char* buffer = aArray.Data();
   uint32_t length = std::min(aArray.Length(), mBuffer.Length());
 
diff --git a/content/media/webaudio/AudioContext.cpp b/content/media/webaudio/AudioContext.cpp
index 1bf78ef..3ef2ff4 100644
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -169,18 +169,20 @@ AudioContext::CreateBuffer(JSContext* aJSContext, ArrayBuffer& aBuffer,
     return nullptr;
   }
 
+  aBuffer.ComputeLengthAndData();
+
+  uint32_t len = aBuffer.Length();
+  uint8_t* data = aBuffer.Data();
+
   // Sniff the content of the media.
   // Failed type sniffing will be handled by SyncDecodeMedia.
   nsAutoCString contentType;
-  NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, nullptr,
-                  aBuffer.Data(), aBuffer.Length(),
-                  contentType);
+  NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, nullptr, data, len, contentType);
 
   nsRefPtr<WebAudioDecodeJob> job =
     new WebAudioDecodeJob(contentType, this, aBuffer);
 
-  if (mDecoder.SyncDecodeMedia(contentType.get(),
-                               aBuffer.Data(), aBuffer.Length(), *job) &&
+  if (mDecoder.SyncDecodeMedia(contentType.get(), data, len, *job) &&
       job->mOutput) {
     nsRefPtr<AudioBuffer> buffer = job->mOutput.forget();
     if (aMixToMono) {
@@ -342,6 +344,9 @@ AudioContext::CreatePeriodicWave(const Float32Array& aRealData,
                                  const Float32Array& aImagData,
                                  ErrorResult& aRv)
 {
+  aRealData.ComputeLengthAndData();
+  aImagData.ComputeLengthAndData();
+
   if (aRealData.Length() != aImagData.Length() ||
       aRealData.Length() == 0 ||
       aRealData.Length() > 4096) {
@@ -369,6 +374,8 @@ AudioContext::DecodeAudioData(const ArrayBuffer& aBuffer,
                               DecodeSuccessCallback& aSuccessCallback,
                               const Optional<OwningNonNull<DecodeErrorCallback> >& aFailureCallback)
 {
+  aBuffer.ComputeLengthAndData();
+
   // Sniff the content of the media.
   // Failed type sniffing will be handled by AsyncDecodeMedia.
   nsAutoCString contentType;
diff --git a/content/media/webaudio/AudioParam.h b/content/media/webaudio/AudioParam.h
index e735b59..e6d95b0 100644
--- a/content/media/webaudio/AudioParam.h
+++ b/content/media/webaudio/AudioParam.h
@@ -57,6 +57,7 @@ public:
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
+    aValues.ComputeLengthAndData();
     AudioParamTimeline::SetValueCurveAtTime(aValues.Data(), aValues.Length(),
                                             aStartTime, aDuration, aRv);
     mCallback(mNode);
diff --git a/content/media/webaudio/BiquadFilterNode.cpp b/content/media/webaudio/BiquadFilterNode.cpp
index fe45106..fa4f1d1 100644
--- a/content/media/webaudio/BiquadFilterNode.cpp
+++ b/content/media/webaudio/BiquadFilterNode.cpp
@@ -247,6 +247,10 @@ BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
                                        Float32Array& aMagResponse,
                                        Float32Array& aPhaseResponse)
 {
+  aFrequencyHz.ComputeLengthAndData();
+  aMagResponse.ComputeLengthAndData();
+  aPhaseResponse.ComputeLengthAndData();
+
   uint32_t length = std::min(std::min(aFrequencyHz.Length(), aMagResponse.Length()),
                              aPhaseResponse.Length());
   if (!length) {
diff --git a/content/media/webaudio/WaveShaperNode.cpp b/content/media/webaudio/WaveShaperNode.cpp
index 22df163..fa39637 100644
--- a/content/media/webaudio/WaveShaperNode.cpp
+++ b/content/media/webaudio/WaveShaperNode.cpp
@@ -125,6 +125,8 @@ WaveShaperNode::SetCurve(const Float32Array* aCurve)
   if (aCurve) {
     mCurve = aCurve->Obj();
 
+    aCurve->ComputeLengthAndData();
+
     curve.SetLength(aCurve->Length());
     PodCopy(curve.Elements(), aCurve->Data(), aCurve->Length());
   } else {
diff --git a/dom/bindings/TypedArray.h b/dom/bindings/TypedArray.h
index 6531f2f..c7c2bbd 100644
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -20,30 +20,43 @@ namespace dom {
  * or array buffer object.
  */
 template<typename T,
-         JSObject* UnboxArray(JSObject*, uint32_t*, T**)>
+         JSObject* UnwrapArray(JSObject*),
+         void GetLengthAndData(JSObject*, uint32_t*, T**)>
 struct TypedArray_base {
   TypedArray_base(JSObject* obj)
+    : mObj(obj),
+      mData(NULL),
+      mLength(0),
+      mComputed(false)
   {
-    mObj = UnboxArray(obj, &mLength, &mData);
+    MOZ_ASSERT(obj != NULL);
   }
 
 private:
-  T* mData;
-  uint32_t mLength;
   JSObject* mObj;
+  mutable T* mData;
+  mutable uint32_t mLength;
+  mutable bool mComputed;
 
 public:
+  inline bool Init(JSObject* obj)
+  {
+    MOZ_ASSERT(!inited());
+    DoInit(obj);
+    return inited();
+  }
+
   inline bool inited() const {
     return !!mObj;
   }
 
   inline T *Data() const {
-    MOZ_ASSERT(inited());
+    MOZ_ASSERT(mComputed);
     return mData;
   }
 
   inline uint32_t Length() const {
-    MOZ_ASSERT(inited());
+    MOZ_ASSERT(mComputed);
     return mLength;
   }
 
@@ -51,16 +64,31 @@ public:
     MOZ_ASSERT(inited());
     return mObj;
   }
+
+  inline void ComputeLengthAndData() const
+  {
+    MOZ_ASSERT(inited());
+    MOZ_ASSERT(!mComputed);
+    GetLengthAndData(mObj, &mLength, &mData);
+    mComputed = true;
+  }
+
+protected:
+  inline void DoInit(JSObject* obj)
+  {
+    mObj = UnwrapArray(obj);
+  }
 };
 
 
 template<typename T,
+         JSObject* UnwrapArray(JSObject*),
          T* GetData(JSObject*),
-         JSObject* UnboxArray(JSObject*, uint32_t*, T**),
+         void GetLengthAndData(JSObject*, uint32_t*, T**),
          JSObject* CreateNew(JSContext*, uint32_t)>
-struct TypedArray : public TypedArray_base<T,UnboxArray> {
+struct TypedArray : public TypedArray_base<T, UnwrapArray, GetLengthAndData> {
   TypedArray(JSObject* obj) :
-    TypedArray_base<T,UnboxArray>(obj)
+    TypedArray_base<T, UnwrapArray, GetLengthAndData>(obj)
   {}
 
   static inline JSObject*
@@ -83,37 +111,37 @@ struct TypedArray : public TypedArray_base<T,UnboxArray> {
   }
 };
 
-typedef TypedArray<int8_t, JS_GetInt8ArrayData, JS_GetObjectAsInt8Array,
-                   JS_NewInt8Array>
+typedef TypedArray<int8_t, js::UnwrapInt8Array, JS_GetInt8ArrayData,
+                   js::GetInt8ArrayLengthAndData, JS_NewInt8Array>
         Int8Array;
-typedef TypedArray<uint8_t, JS_GetUint8ArrayData,
-                   JS_GetObjectAsUint8Array, JS_NewUint8Array>
+typedef TypedArray<uint8_t, js::UnwrapUint8Array, JS_GetUint8ArrayData,
+                   js::GetUint8ArrayLengthAndData, JS_NewUint8Array>
         Uint8Array;
-typedef TypedArray<uint8_t, JS_GetUint8ClampedArrayData,
-                   JS_GetObjectAsUint8ClampedArray, JS_NewUint8ClampedArray>
+typedef TypedArray<uint8_t, js::UnwrapUint8ClampedArray, JS_GetUint8ClampedArrayData,
+                   js::GetUint8ClampedArrayLengthAndData, JS_NewUint8ClampedArray>
         Uint8ClampedArray;
-typedef TypedArray<int16_t, JS_GetInt16ArrayData,
-                   JS_GetObjectAsInt16Array, JS_NewInt16Array>
+typedef TypedArray<int16_t, js::UnwrapInt16Array, JS_GetInt16ArrayData,
+                   js::GetInt16ArrayLengthAndData, JS_NewInt16Array>
         Int16Array;
-typedef TypedArray<uint16_t, JS_GetUint16ArrayData,
-                   JS_GetObjectAsUint16Array, JS_NewUint16Array>
+typedef TypedArray<uint16_t, js::UnwrapUint16Array, JS_GetUint16ArrayData,
+                   js::GetUint16ArrayLengthAndData, JS_NewUint16Array>
         Uint16Array;
-typedef TypedArray<int32_t, JS_GetInt32ArrayData,
-                   JS_GetObjectAsInt32Array, JS_NewInt32Array>
+typedef TypedArray<int32_t, js::UnwrapInt32Array, JS_GetInt32ArrayData,
+                   js::GetInt32ArrayLengthAndData, JS_NewInt32Array>
         Int32Array;
-typedef TypedArray<uint32_t, JS_GetUint32ArrayData,
-                   JS_GetObjectAsUint32Array, JS_NewUint32Array>
+typedef TypedArray<uint32_t, js::UnwrapUint32Array, JS_GetUint32ArrayData,
+                   js::GetUint32ArrayLengthAndData, JS_NewUint32Array>
         Uint32Array;
-typedef TypedArray<float, JS_GetFloat32ArrayData,
-                   JS_GetObjectAsFloat32Array, JS_NewFloat32Array>
+typedef TypedArray<float, js::UnwrapFloat32Array, JS_GetFloat32ArrayData,
+                   js::GetFloat32ArrayLengthAndData, JS_NewFloat32Array>
         Float32Array;
-typedef TypedArray<double, JS_GetFloat64ArrayData,
-                   JS_GetObjectAsFloat64Array, JS_NewFloat64Array>
+typedef TypedArray<double, js::UnwrapFloat64Array, JS_GetFloat64ArrayData,
+                   js::GetFloat64ArrayLengthAndData, JS_NewFloat64Array>
         Float64Array;
-typedef TypedArray_base<uint8_t, JS_GetObjectAsArrayBufferView>
+typedef TypedArray_base<uint8_t, js::UnwrapArrayBufferView, js::GetArrayBufferViewLengthAndData>
         ArrayBufferView;
-typedef TypedArray<uint8_t, JS_GetArrayBufferData,
-                   JS_GetObjectAsArrayBuffer, JS_NewArrayBuffer>
+typedef TypedArray<uint8_t, js::UnwrapArrayBuffer, JS_GetArrayBufferData,
+                   js::GetArrayBufferLengthAndData, JS_NewArrayBuffer>
         ArrayBuffer;
 
 } // namespace dom
diff --git a/dom/encoding/TextDecoder.h b/dom/encoding/TextDecoder.h
index d309209..3db00a4 100644
--- a/dom/encoding/TextDecoder.h
+++ b/dom/encoding/TextDecoder.h
@@ -66,6 +66,7 @@ public:
               const TextDecodeOptions& aOptions,
               nsAString& aOutDecodedString,
               ErrorResult& aRv) {
+    aView.ComputeLengthAndData();
     TextDecoderBase::Decode(reinterpret_cast<char*>(aView.Data()),
                             aView.Length(), aOptions.mStream,
                             aOutDecodedString, aRv);
diff --git a/dom/workers/TextDecoder.h b/dom/workers/TextDecoder.h
index 6c88a70..205afc5 100644
--- a/dom/workers/TextDecoder.h
+++ b/dom/workers/TextDecoder.h
@@ -49,6 +49,7 @@ public:
          const TextDecodeOptionsWorkers& aOptions,
          nsAString& aOutDecodedString,
          ErrorResult& aRv) {
+    aView.ComputeLengthAndData();
     TextDecoderBase::Decode(reinterpret_cast<char*>(aView.Data()),
                             aView.Length(), aOptions.mStream,
                             aOutDecodedString, aRv);
diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
index 24cb2d7..ce6d633 100644
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -999,6 +999,49 @@ js::IsAsmJSFunction(JSContext *cx, unsigned argc, Value *vp)
 }
 #endif
 
+static JSBool
+Neuter(JSContext *cx, unsigned argc, jsval *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() != 2) {
+        JS_ReportError(cx, "wrong number of arguments to neuter()");
+        return false;
+    }
+
+    RootedObject obj(cx);
+    if (!JS_ValueToObject(cx, args[0], obj.address()))
+        return false;
+
+    if (!obj) {
+        JS_ReportError(cx, "neuter must be passed an object");
+        return false;
+    }
+
+    NeuterDataDisposition changeData;
+    RootedValue v(cx, args[1]);
+    RootedString str(cx, ToString<CanGC>(cx, v));
+    if (!str)
+        return false;
+    JSAutoByteString dataDisposition(cx, str);
+    if (!dataDisposition)
+        return false;
+    if (strcmp(dataDisposition.ptr(), "same-data") == 0) {
+        changeData = KeepData;
+    } else if (strcmp(dataDisposition.ptr(), "change-data") == 0) {
+        changeData = ChangeData;
+    } else {
+        JS_ReportError(cx, "unknown parameter 2 to neuter()");
+        return false;
+    }
+
+    if (!js::NeuterArrayBuffer(cx, obj, changeData))
+        return false;
+
+    args.rval().setUndefined();
+    return true;
+}
+
 static JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment')",
@@ -1177,6 +1220,13 @@ static JSFunctionSpecWithHelp TestingFunctions[] = {
 "getObjectMetadata(obj)",
 "  Get the metadata for an object."),
 
+    JS_FN_HELP("neuter", Neuter, 1, 0,
+"neuter(buffer, \"change-data\"|\"same-data\")",
+"  Neuter the given ArrayBuffer object as if it had been transferred to a\n"
+"  WebWorker. \"change-data\" will update the internal data pointer.\n"
+"  \"same-data\" will leave it set to its original value, to mimic eg\n"
+"  asm.js ArrayBuffer neutering."),
+
     JS_FS_HELP_END
 };
 
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index a0c70f5..2ceaf71 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6446,29 +6446,12 @@ IonBuilder::jsop_getelem_dense()
 MInstruction *
 IonBuilder::getTypedArrayLength(MDefinition *obj)
 {
-    if (obj->isConstant() && obj->toConstant()->value().isObject()) {
-        JSObject *array = &obj->toConstant()->value().toObject();
-        int32_t length = (int32_t) TypedArray::length(array);
-        obj->setFoldedUnchecked();
-        return MConstant::New(Int32Value(length));
-    }
     return MTypedArrayLength::New(obj);
 }
 
 MInstruction *
 IonBuilder::getTypedArrayElements(MDefinition *obj)
 {
-    if (obj->isConstant() && obj->toConstant()->value().isObject()) {
-        JSObject *array = &obj->toConstant()->value().toObject();
-        void *data = TypedArray::viewData(array);
-
-        // The 'data' pointer can change in rare circumstances
-        // (ArrayBufferObject::changeContents).
-        types::HeapTypeSet::WatchObjectStateChange(cx, array->getType(cx));
-
-        obj->setFoldedUnchecked();
-        return MConstantElements::New(data);
-    }
     return MTypedArrayElements::New(obj);
 }
 
@@ -6504,49 +6487,6 @@ IonBuilder::jsop_getelem_typed_static(bool *psucceeded)
     if (!LIRGenerator::allowStaticTypedArrayAccesses())
         return true;
 
-    MDefinition *id = current->peek(-1);
-    MDefinition *obj = current->peek(-2);
-
-    if (ElementAccessHasExtraIndexedProperty(cx, obj))
-        return true;
-
-    if (!obj->resultTypeSet())
-        return true;
-    JSObject *typedArray = obj->resultTypeSet()->getSingleton();
-    if (!typedArray)
-        return true;
-    JS_ASSERT(typedArray->isTypedArray());
-
-    ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(typedArray);
-
-    MDefinition *ptr = convertShiftToMaskForStaticTypedArray(id, viewType);
-    if (!ptr)
-        return true;
-
-    obj->setFoldedUnchecked();
-
-    MLoadTypedArrayElementStatic *load = MLoadTypedArrayElementStatic::New(typedArray, ptr);
-    current->add(load);
-
-    // The load is infallible if an undefined result will be coerced to the
-    // appropriate numeric type if the read is out of bounds. The truncation
-    // analysis picks up some of these cases, but is incomplete with respect
-    // to others. For now, sniff the bytecode for simple patterns following
-    // the load which guarantee a truncation or numeric conversion.
-    if (viewType == ArrayBufferView::TYPE_FLOAT32 || viewType == ArrayBufferView::TYPE_FLOAT64) {
-        jsbytecode *next = pc + JSOP_GETELEM_LENGTH;
-        if (*next == JSOP_POS)
-            load->setInfallible();
-    } else {
-        jsbytecode *next = pc + JSOP_GETELEM_LENGTH;
-        if (*next == JSOP_ZERO && *(next + JSOP_ZERO_LENGTH) == JSOP_BITOR)
-            load->setInfallible();
-    }
-
-    current->popn(2);
-    current->push(load);
-
-    *psucceeded = true;
     return true;
 }
 
@@ -6854,37 +6794,7 @@ IonBuilder::jsop_setelem_typed_static(MDefinition *obj, MDefinition *id, MDefini
     if (!LIRGenerator::allowStaticTypedArrayAccesses())
         return true;
 
-    if (ElementAccessHasExtraIndexedProperty(cx, obj))
-        return true;
-
-    if (!obj->resultTypeSet())
-        return true;
-    JSObject *typedArray = obj->resultTypeSet()->getSingleton();
-    if (!typedArray)
-        return true;
-    JS_ASSERT(typedArray->isTypedArray());
-
-    ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(typedArray);
-
-    MDefinition *ptr = convertShiftToMaskForStaticTypedArray(id, viewType);
-    if (!ptr)
-        return true;
-
-    obj->setFoldedUnchecked();
-
-    // Clamp value to [0, 255] for Uint8ClampedArray.
-    MDefinition *toWrite = value;
-    if (viewType == ArrayBufferView::TYPE_UINT8_CLAMPED) {
-        toWrite = MClampToUint8::New(value);
-        current->add(toWrite->toInstruction());
-    }
-
-    MInstruction *store = MStoreTypedArrayElementStatic::New(typedArray, ptr, toWrite);
-    current->add(store);
-    current->push(value);
-
-    *psucceeded = true;
-    return resumeAfter(store);
+    return true;
 }
 
 bool
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 96ba2d4..02ba6d7 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -211,14 +211,16 @@ class AliasSet {
         FixedSlot         = 1 << 3, // A member of obj->fixedSlots().
         TypedArrayElement = 1 << 4, // A typed array element.
         DOMProperty       = 1 << 5, // A DOM property
-        Last              = DOMProperty,
+        TypedArrayLength  = 1 << 6, // A typed array's length
+        Last              = TypedArrayLength,
         Any               = Last | (Last - 1),
 
-        NumCategories     = 6,
+        NumCategories     = 7,
 
         // Indicates load or store.
         Store_            = 1 << 31
     };
+
     AliasSet(uint32_t flags)
       : flags_(flags)
     {
@@ -4340,9 +4342,7 @@ class MTypedArrayLength
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
-        // The typed array |length| property is immutable, so there is no
-        // implicit dependency.
-        return AliasSet::None();
+        return AliasSet::Load(AliasSet::TypedArrayLength);
     }
 };
 
diff --git a/js/src/js.msg b/js/src/js.msg
index 4047035..05807e0 100644
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -241,11 +241,11 @@ MSG_DEF(JSMSG_UNUSED187,              187, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_INCOMPATIBLE_METHOD,    188, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}")
 MSG_DEF(JSMSG_UNUSED189,              189, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED190,              190, 0, JSEXN_NONE, "")
-MSG_DEF(JSMSG_UNUSED191,              191, 0, JSEXN_NONE, "")
+MSG_DEF(JSMSG_BAD_INDEX,              191, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_UNUSED192,              192, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP,      193, 0, JSEXN_SYNTAXERR, "invalid for each loop")
 MSG_DEF(JSMSG_UNUSED194,              194, 0, JSEXN_NONE, "")
-MSG_DEF(JSMSG_UNUSED195,              195, 0, JSEXN_NONE, "")
+MSG_DEF(JSMSG_TYPE_ERR_BAD_ARGS,      195, 0, JSEXN_TYPEERR, "invalid arguments")
 MSG_DEF(JSMSG_UNUSED196,              196, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_INTERNAL_INTL_ERROR,    197, 0, JSEXN_ERR, "internal error while computing Intl data")
 MSG_DEF(JSMSG_DEFAULT_LOCALE_ERROR,   198, 0, JSEXN_ERR, "internal error getting the default locale")
diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h
index d9840b1..da447a5 100644
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -7,6 +7,8 @@
 #ifndef jsfriendapi_h
 #define jsfriendapi_h
 
+#include "mozilla/Casting.h"
+
 #include "jsclass.h"
 #include "jspubtd.h"
 #include "jsprvtd.h"
@@ -1289,6 +1291,93 @@ extern JS_FRIEND_API(JSBool)
 JS_IsFloat64Array(JSObject *obj);
 
 /*
+ * Test for specific typed array types (ArrayBufferView subtypes) and return
+ * the unwrapped object if so, else nullptr.  Never throws.
+ */
+
+namespace js {
+
+extern JS_FRIEND_API(JSObject *)
+UnwrapInt8Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapUint8Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapUint8ClampedArray(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapInt16Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapUint16Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapInt32Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapUint32Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapFloat32Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapFloat64Array(JSObject *obj);
+
+extern JS_FRIEND_API(JSObject *)
+UnwrapArrayBuffer(JSObject *obj);
+
+extern JS_FRIEND_API(JSObject *)
+UnwrapArrayBufferView(JSObject *obj);
+
+namespace detail {
+
+extern JS_FRIEND_DATA(const Class* const) Int8ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Uint8ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Uint8ClampedArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Int16ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Uint16ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Int32ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Uint32ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Float32ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Float64ArrayClassPtr;
+
+const size_t TypedArrayLengthSlot = 5;
+
+} // namespace detail
+
+/*
+ * Test for specific typed array types (ArrayBufferView subtypes) and return
+ * the unwrapped object if so, else nullptr.  Never throws.
+ */
+
+#define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Type, type) \
+inline void \
+Get ## Type ## ArrayLengthAndData(JSObject *obj, uint32_t *length, type **data) \
+{ \
+    JS_ASSERT(GetObjectClass(obj) == detail::Type ## ArrayClassPtr); \
+    const JS::Value &slot = GetReservedSlot(obj, detail::TypedArrayLengthSlot); \
+    *length = mozilla::SafeCast<uint32_t>(slot.toInt32()); \
+    *data = static_cast<type*>(GetObjectPrivate(obj)); \
+}
+
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int8, int8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8, uint8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8Clamped, uint8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int16, int16_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint16, uint16_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int32, int32_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint32, uint32_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float32, float)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float64, double)
+
+#undef JS_DEFINE_DATA_AND_LENGTH_ACCESSOR
+
+// This one isn't inlined because it's rather tricky (by dint of having to deal
+// with a dozen-plus classes and varying slot layouts.
+extern JS_FRIEND_API(void)
+GetArrayBufferViewLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data);
+
+// This one isn't inlined because there are a bunch of different ArrayBuffer
+// classes that would have to be individually handled here.
+extern JS_FRIEND_API(void)
+GetArrayBufferLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data);
+
+} // namespace js
+
+/*
  * Unwrap Typed arrays all at once. Return NULL without throwing if the object
  * cannot be viewed as the correct typed array, or the typed array object on
  * success, filling both outparameters.
@@ -1446,6 +1535,28 @@ JS_GetArrayBufferViewData(JSObject *obj);
 extern JS_FRIEND_API(JSObject *)
 JS_GetArrayBufferViewBuffer(JSObject *obj);
 
+typedef enum {
+    ChangeData,
+    KeepData
+} NeuterDataDisposition;
+
+namespace js {
+
+/*
+ * Set an ArrayBuffer's length to 0 and neuter all of its views.
+ *
+ * The |changeData| argument is a hint to inform internal behavior with respect
+ * to the internal pointer to the ArrayBuffer's data after being neutered.
+ * There is no guarantee it will be respected.  But if it is respected, the
+ * ArrayBuffer's internal data pointer will, or will not, have changed
+ * accordingly.
+ */
+extern JS_FRIEND_API(bool)
+NeuterArrayBuffer(JSContext *cx, JS::HandleObject obj,
+                  NeuterDataDisposition changeData);
+
+} /* namespace js */
+
 /*
  * Check whether obj supports JS_GetDataView* APIs.
  */
diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp
index 9d02d06..a78edd4 100644
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -577,9 +577,12 @@ JSObject *
 ArrayBufferObject::createSlice(JSContext *cx, ArrayBufferObject &arrayBuffer,
                                uint32_t begin, uint32_t end)
 {
-    JS_ASSERT(begin <= arrayBuffer.byteLength());
-    JS_ASSERT(end <= arrayBuffer.byteLength());
-    JS_ASSERT(begin <= end);
+    uint32_t bufLength = arrayBuffer.byteLength();
+    if (begin > bufLength || end > bufLength || begin > end) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPE_ERR_BAD_ARGS);
+        return NULL;
+    }
+
     uint32_t length = end - begin;
 
     if (arrayBuffer.hasData())
@@ -620,8 +623,8 @@ ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp
 }
 
 bool
-ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents,
-                                 uint8_t **data)
+ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, NeuterDataDisposition changeData,
+                                 void **contents, uint8_t **data)
 {
     MOZ_ASSERT(cx);
 
@@ -635,7 +638,7 @@ ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents,
 
     // If the ArrayBuffer's elements are transferrable, transfer ownership
     // directly.  Otherwise we have to copy the data into new elements.
-    bool stolen = buffer.hasStealableContents();
+    bool stolen = buffer.hasStealableContents() && changeData == ChangeData;
     if (stolen) {
         newHeader = AllocateArrayBufferContents(cx, byteLen, NULL);
         if (!newHeader)
@@ -1190,13 +1193,15 @@ js::IsDataView(JSObject* obj)
 }
 
 void
-TypedArray::neuter(JSObject *tarray)
+TypedArray::neuter(JSObject *view)
 {
-    JS_ASSERT(tarray->isTypedArray());
-    tarray->setSlot(LENGTH_SLOT, Int32Value(0));
-    tarray->setSlot(BYTELENGTH_SLOT, Int32Value(0));
-    tarray->setSlot(BYTEOFFSET_SLOT, Int32Value(0));
-    tarray->setPrivate(NULL);
+    if (view->isTypedArray())
+        view->setSlot(LENGTH_SLOT, Int32Value(0));
+    else
+        MOZ_ASSERT(view->hasClass(&DataViewObject::class_));
+    view->setSlot(BYTELENGTH_SLOT, Int32Value(0));
+    view->setSlot(BYTEOFFSET_SLOT, Int32Value(0));
+    view->setPrivate(NULL);
 }
 
 JSBool
@@ -2023,20 +2028,26 @@ class TypedArrayTemplate
         uint32_t srcEnd;
         uint32_t dest;
 
-        uint32_t length = TypedArray::length(tarray);
-        if (!ToClampedIndex(cx, args[0], length, &srcBegin) ||
-            !ToClampedIndex(cx, args[1], length, &srcEnd) ||
-            !ToClampedIndex(cx, args[2], length, &dest) ||
+        uint32_t originalLength = TypedArray::length(tarray);
+        if (!ToClampedIndex(cx, args[0], originalLength, &srcBegin) ||
+            !ToClampedIndex(cx, args[1], originalLength, &srcEnd) ||
+            !ToClampedIndex(cx, args[2], originalLength, &dest) ||
             srcBegin > srcEnd)
         {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return false;
         }
 
+        if (srcBegin > srcEnd) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDEX);
+            return false;
+        }
+
+        uint32_t lengthDuringMove = TypedArray::length(tarray); // beware ToClampedIndex
         uint32_t nelts = srcEnd - srcBegin;
 
-        JS_ASSERT(dest + nelts >= dest);
-        if (dest + nelts > length) {
+        MOZ_ASSERT(dest <= INT32_MAX, "size limited to 2**31");
+        MOZ_ASSERT(nelts <= INT32_MAX, "size limited to 2**31");
+        if (dest + nelts > lengthDuringMove || srcEnd > lengthDuringMove) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return false;
         }
@@ -2114,8 +2125,7 @@ class TypedArrayTemplate
             if (!GetLengthProperty(cx, arg0, &len))
                 return false;
 
-            // avoid overflow; we know that offset <= length
-            if (len > length(tarray) - offset) {
+            if (uint32_t(offset) > length(tarray) || len > length(tarray) - offset) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
                 return false;
             }
@@ -2268,7 +2278,8 @@ class TypedArrayTemplate
     static const NativeType
     getIndex(JSObject *obj, uint32_t index)
     {
-        return *(static_cast<const NativeType*>(viewData(obj)) + index);
+        MOZ_ASSERT(index < length(obj));
+        return static_cast<const NativeType*>(viewData(obj))[index];
     }
 
     static void
@@ -2284,13 +2295,14 @@ class TypedArrayTemplate
     {
         JS_ASSERT(tarray);
 
-        JS_ASSERT(begin <= length(tarray));
-        JS_ASSERT(end <= length(tarray));
+        if (begin > length(tarray) || end > length(tarray) || begin > end) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDEX);
+            return NULL;
+        }
 
         RootedObject bufobj(cx, buffer(tarray));
         JS_ASSERT(bufobj);
 
-        JS_ASSERT(begin <= end);
         uint32_t length = end - begin;
 
         JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType));
@@ -2505,53 +2517,54 @@ class TypedArrayTemplate
             return false;
         js_memcpy(srcbuf, viewData(tarray), byteLength);
 
+        uint32_t len = length(tarray);
         switch (type(tarray)) {
           case TypedArray::TYPE_INT8: {
             int8_t *src = (int8_t*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_UINT8:
           case TypedArray::TYPE_UINT8_CLAMPED: {
             uint8_t *src = (uint8_t*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_INT16: {
             int16_t *src = (int16_t*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_UINT16: {
             uint16_t *src = (uint16_t*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_INT32: {
             int32_t *src = (int32_t*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_UINT32: {
             uint32_t *src = (uint32_t*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_FLOAT32: {
             float *src = (float*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_FLOAT64: {
             double *src = (double*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
@@ -2822,21 +2835,17 @@ DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
     return construct(cx, bufobj, args, NullPtr());
 }
 
-/* static */ bool
-DataViewObject::getDataPointer(JSContext *cx, Handle<DataViewObject*> obj,
-                               CallArgs args, size_t typeSize, uint8_t **data)
+template <typename NativeType>
+/* static */ uint8_t *
+DataViewObject::getDataPointer(JSContext *cx, Handle<DataViewObject*> obj, uint32_t offset)
 {
-    uint32_t offset;
-    JS_ASSERT(args.length() > 0);
-    if (!ToUint32(cx, args[0], &offset))
-        return false;
-    if (offset > UINT32_MAX - typeSize || offset + typeSize > obj->byteLength()) {
+    const size_t TypeSize = sizeof(NativeType);
+    if (offset > UINT32_MAX - TypeSize || offset + TypeSize > obj->byteLength()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
-        return false;
+        return NULL;
     }
 
-    *data = static_cast<uint8_t*>(obj->dataPointer()) + offset;
-    return true;
+    return static_cast<uint8_t*>(obj->dataPointer()) + offset;
 }
 
 static inline bool
@@ -2924,11 +2933,17 @@ DataViewObject::read(JSContext *cx, Handle<DataViewObject*> obj,
         return false;
     }
 
-    uint8_t *data;
-    if (!getDataPointer(cx, obj, args, sizeof(NativeType), &data))
+    uint32_t offset;
+    if (!ToUint32(cx, args[0], &offset))
         return false;
 
     bool fromLittleEndian = args.length() >= 2 && ToBoolean(args[1]);
+
+    uint8_t *data = DataViewObject::getDataPointer<NativeType>(cx, obj, offset);
+    SkipRoot skipData(cx, &data);
+    if (!data)
+        return false;
+
     DataViewIO<NativeType>::fromBuffer(val, data, needToSwapBytes(fromLittleEndian));
     return true;
 }
@@ -2976,9 +2991,8 @@ DataViewObject::write(JSContext *cx, Handle<DataViewObject*> obj,
         return false;
     }
 
-    uint8_t *data;
-    SkipRoot skipData(cx, &data);
-    if (!getDataPointer(cx, obj, args, sizeof(NativeType), &data))
+    uint32_t offset;
+    if (!ToUint32(cx, args[0], &offset))
         return false;
 
     NativeType value;
@@ -2986,6 +3000,12 @@ DataViewObject::write(JSContext *cx, Handle<DataViewObject*> obj,
         return false;
 
     bool toLittleEndian = args.length() >= 3 && ToBoolean(args[2]);
+
+    uint8_t *data = DataViewObject::getDataPointer<NativeType>(cx, obj, offset);
+    SkipRoot skipData(cx, &data);
+    if (!data)
+        return false;
+
     DataViewIO<NativeType>::toBuffer(data, &value, needToSwapBytes(toLittleEndian));
     return true;
 }
@@ -3449,7 +3469,19 @@ const JSFunctionSpec _typedArray::jsfuncs[] = {                                \
           return false;                                                                      \
       Class *clasp = obj->getClass();                                                        \
       return (clasp == &TypedArray::classes[TypedArrayTemplate<NativeType>::ArrayTypeID()]); \
-  }
+  } \
+  JS_FRIEND_API(JSObject *) js::Unwrap ## Name ## Array(JSObject *obj)                       \
+  {                                                                                          \
+      obj = CheckedUnwrap(obj);                                                              \
+      if (!obj)                                                                              \
+          return NULL;                                                                       \
+      const Class *clasp = obj->getClass();                                                  \
+      if (clasp == &TypedArray::classes[TypedArrayTemplate<NativeType>::ArrayTypeID()])      \
+          return obj;                                                                        \
+      return NULL;                                                                           \
+  } \
+  JS_FRIEND_DATA(const js::Class* const) js::detail::Name ## ArrayClassPtr =                 \
+      &js::TypedArray::classes[TypedArrayTemplate<NativeType>::ArrayTypeID()];
 
 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t)
 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8, uint8_t)
@@ -3918,6 +3950,14 @@ JS_IsArrayBufferObject(JSObject *obj)
     return obj ? obj->is<ArrayBufferObject>() : false;
 }
 
+JS_FRIEND_API(JSObject *)
+js::UnwrapArrayBuffer(JSObject *obj)
+{
+    if (JSObject *unwrapped = CheckedUnwrap(obj))
+        return unwrapped->is<ArrayBufferObject>() ? unwrapped : NULL;
+    return NULL;
+}
+
 JS_FRIEND_API(JSBool)
 JS_IsTypedArrayObject(JSObject *obj)
 {
@@ -3932,6 +3972,16 @@ JS_IsArrayBufferViewObject(JSObject *obj)
     return obj ? (obj->isTypedArray() || obj->is<DataViewObject>()) : false;
 }
 
+JS_FRIEND_API(JSObject *)
+js::UnwrapArrayBufferView(JSObject *obj)
+{
+    if (JSObject *unwrapped = CheckedUnwrap(obj)) {
+        if (unwrapped->isTypedArray() || unwrapped->is<DataViewObject>())
+            return unwrapped;
+    }
+    return NULL;
+}
+
 JS_FRIEND_API(uint32_t)
 JS_GetArrayBufferByteLength(JSObject *obj)
 {
@@ -3951,6 +4001,24 @@ JS_GetArrayBufferData(JSObject *obj)
     return buffer.dataPointer();
 }
 
+JS_FRIEND_API(bool)
+js::NeuterArrayBuffer(JSContext *cx, HandleObject obj,
+                      NeuterDataDisposition changeData)
+{
+    if (!obj->is<ArrayBufferObject>()) {
+        JS_ReportError(cx, "ArrayBuffer object required");
+        return false;
+    }
+
+    void *contents;
+    uint8_t *data;
+    if (!ArrayBufferObject::stealContents(cx, obj, changeData, &contents, &data))
+        return false;
+
+    JS_free(cx, contents);
+    return true;
+}
+
 JS_FRIEND_API(JSObject *)
 JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes)
 {
@@ -4010,7 +4078,7 @@ JS_StealArrayBufferContents(JSContext *cx, JSObject *obj, void **contents,
         return false;
     }
 
-    if (!ArrayBufferObject::stealContents(cx, obj, contents, data))
+    if (!ArrayBufferObject::stealContents(cx, obj, ChangeData, contents, data))
         return false;
 
     return true;
@@ -4229,6 +4297,18 @@ JS_GetArrayBufferViewByteLength(JSObject *obj)
            : TypedArray::byteLengthValue(obj).toInt32();
 }
 
+JS_FRIEND_API(void)
+js::GetArrayBufferViewLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data)
+{
+    MOZ_ASSERT(obj->is<DataViewObject>() || obj->isTypedArray());
+
+    *length = obj->is<DataViewObject>()
+              ? obj->as<DataViewObject>().byteLength()
+              : TypedArray::byteLength(obj);
+
+    *data = static_cast<uint8_t*>(obj->getPrivate());
+}
+
 JS_FRIEND_API(JSObject *)
 JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data)
 {
@@ -4259,3 +4339,11 @@ JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data)
 
     return obj;
 }
+
+JS_FRIEND_API(void)
+js::GetArrayBufferLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data)
+{
+    MOZ_ASSERT(obj->is<ArrayBufferObject>());
+    *length = obj->as<ArrayBufferObject>().byteLength();
+    *data = obj->as<ArrayBufferObject>().dataPointer();
+}
diff --git a/js/src/jstypedarray.h b/js/src/jstypedarray.h
index 4751d53..07d7f80 100644
--- a/js/src/jstypedarray.h
+++ b/js/src/jstypedarray.h
@@ -9,6 +9,7 @@
 
 #include "jsapi.h"
 #include "jsclass.h"
+#include "jsfriendapi.h"
 #include "jsobj.h"
 
 #include "gc/Barrier.h"
@@ -155,8 +156,8 @@ class ArrayBufferObject : public JSObject
         return !isNeutered();
     }
 
-    static bool stealContents(JSContext *cx, JSObject *obj, void **contents,
-                              uint8_t **data);
+    static bool stealContents(JSContext *cx, JSObject *obj, NeuterDataDisposition changeData,
+                              void **contents, uint8_t **data);
 
     static inline void setElementsHeader(js::ObjectElements *header, uint32_t bytes);
     static inline uint32_t getElementsHeaderInitializedLength(const js::ObjectElements *header);
@@ -308,7 +309,7 @@ struct TypedArray : public BufferView {
   public:
     static bool isArrayIndex(JSObject *obj, jsid id, uint32_t *ip = NULL);
 
-    static void neuter(JSObject *tarray);
+    static void neuter(JSObject *view);
 
     static inline uint32_t slotWidth(int atype);
     static inline int slotWidth(JSObject *obj);
@@ -323,6 +324,9 @@ struct TypedArray : public BufferView {
     static int dataOffset();
 };
 
+MOZ_STATIC_ASSERT(js::detail::TypedArrayLengthSlot == TypedArray::LENGTH_SLOT,
+                  "bad inlined constant in jsfriendapi.h");
+
 inline bool
 IsTypedArrayClass(const Class *clasp)
 {
@@ -376,6 +380,10 @@ private:
 
     static inline bool is(const Value &v);
 
+    template <typename NativeType>
+    static uint8_t *
+    getDataPointer(JSContext *cx, Handle<DataViewObject*> obj, uint32_t offset);
+
     template<Value ValueGetter(DataViewObject &view)>
     static bool
     getterImpl(JSContext *cx, CallArgs args);
@@ -459,8 +467,6 @@ private:
     inline void *dataPointer();
     inline bool hasBuffer() const;
     static JSObject *initClass(JSContext *cx);
-    static bool getDataPointer(JSContext *cx, Handle<DataViewObject*> obj,
-                               CallArgs args, size_t typeSize, uint8_t **data);
     template<typename NativeType>
     static bool read(JSContext *cx, Handle<DataViewObject*> obj,
                      CallArgs &args, NativeType *val, const char *method);
diff --git a/js/src/jstypedarrayinlines.h b/js/src/jstypedarrayinlines.h
index f26e9cc..6c8bf75 100644
--- a/js/src/jstypedarrayinlines.h
+++ b/js/src/jstypedarrayinlines.h
@@ -129,6 +129,7 @@ inline void *
 TypedArray::viewData(JSObject *obj)
 {
     JS_ASSERT(obj->isTypedArray());
+    // Keep synced with js::Get<Type>ArrayLengthAndData in jsfriendapi.h!
     return (void *)obj->getPrivate(DATA_SLOT);
 }
 
@@ -222,6 +223,13 @@ DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength,
     RootedObject proto(cx, protoArg);
     RootedObject obj(cx);
 
+    // This is overflow-safe: 2 * INT32_MAX is still a valid uint32_t.
+    if (byteOffset + byteLength > arrayBuffer->byteLength()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
+        return nullptr;
+
+    }
+
     NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto);
     obj = NewBuiltinClassInstance(cx, &class_, newKind);
     if (!obj)
diff --git a/netwerk/base/src/ArrayBufferInputStream.cpp b/netwerk/base/src/ArrayBufferInputStream.cpp
index 0daa1c5..16322d9 100644
--- a/netwerk/base/src/ArrayBufferInputStream.cpp
+++ b/netwerk/base/src/ArrayBufferInputStream.cpp
@@ -88,6 +88,17 @@ ArrayBufferInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
   }
 
   uint32_t remaining = mBufferLength - mPos;
+  if (!mArrayBuffer.isUndefined()) {
+    JSObject* buf = &mArrayBuffer.toObject();
+    uint32_t byteLength = JS_GetArrayBufferByteLength(buf);
+    if (byteLength == 0 && remaining != 0) {
+      mClosed = true;
+      return NS_BASE_STREAM_CLOSED;
+    }
+  } else {
+    MOZ_ASSERT(remaining == 0, "stream inited incorrectly");
+  }
+
   if (!remaining) {
     *result = 0;
     return NS_OK;
diff --git a/xpcom/io/nsBinaryStream.cpp b/xpcom/io/nsBinaryStream.cpp
index 8c2cc23..24b6ab3 100644
--- a/xpcom/io/nsBinaryStream.cpp
+++ b/xpcom/io/nsBinaryStream.cpp
@@ -17,8 +17,15 @@
  * @See nsIBinaryInputStream
  * @See nsIBinaryOutputStream
  */
+#include <algorithm>
 #include <string.h>
+
 #include "nsBinaryStream.h"
+
+#include "mozilla/Endian.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/Scoped.h"
+
 #include "nsCRT.h"
 #include "prlong.h"
 #include "nsString.h"
@@ -26,11 +33,13 @@
 #include "nsIClassInfo.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIURI.h" // for NS_IURI_IID
-#include "mozilla/Endian.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
+using mozilla::PodCopy;
+using mozilla::ScopedDeleteArray;
+
 NS_IMPL_ISUPPORTS3(nsBinaryOutputStream, nsIObjectOutputStream, nsIBinaryOutputStream, nsIOutputStream)
 
 NS_IMETHODIMP
@@ -720,22 +729,49 @@ nsBinaryInputStream::ReadArrayBuffer(uint32_t aLength, const JS::Value& aBuffer,
         return NS_ERROR_FAILURE;
     }
     JS::RootedObject buffer(cx, &aBuffer.toObject());
-    if (!JS_IsArrayBufferObject(buffer) ||
-        JS_GetArrayBufferByteLength(buffer) < aLength) {
+    if (!JS_IsArrayBufferObject(buffer)) {
         return NS_ERROR_FAILURE;
     }
-    uint8_t* data = JS_GetArrayBufferData(&aBuffer.toObject());
-    if (!data) {
+
+    uint32_t bufferLength = JS_GetArrayBufferByteLength(buffer);
+    if (bufferLength < aLength) {
         return NS_ERROR_FAILURE;
     }
 
-    uint32_t bytesRead;
-    nsresult rv = Read(reinterpret_cast<char*>(data), aLength, &bytesRead);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (bytesRead != aLength) {
+    char* data = reinterpret_cast<char*>(JS_GetArrayBufferData(buffer));
+    if (!data) {
         return NS_ERROR_FAILURE;
     }
-    return NS_OK;
+
+    uint32_t bufSize = std::min<uint32_t>(aLength, 4096);
+    ScopedDeleteArray<char> buf(new char[bufSize]);
+
+    uint32_t remaining = aLength;
+    do {
+        // Read data into temporary buffer.
+        uint32_t bytesRead;
+        uint32_t amount = std::min(remaining, bufSize);
+        nsresult rv = Read(buf, amount, &bytesRead);
+        if (NS_FAILED(rv)) {
+            return rv;
+        }
+        MOZ_ASSERT(bytesRead <= amount);
+
+        if (bytesRead == 0) {
+            break;
+        }
+
+        // Copy data into actual buffer.
+        if (bufferLength != JS_GetArrayBufferByteLength(buffer)) {
+            return NS_ERROR_FAILURE;
+        }
+        PodCopy(data, buf.get(), bytesRead);
+
+        remaining -= bytesRead;
+        data += bytesRead;
+    } while (remaining > 0);
+
+    return remaining > 0 ? NS_ERROR_FAILURE : NS_OK;
 }
 
 NS_IMETHODIMP





More information about the tor-commits mailing list