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=1.2.x+ default
authorJeff Walden <jwalden@mit.edu>
Thu, 29 May 2014 19:59:41 -0700
changeset 157146 4c359ff76654e6b4972a1fcf581069e24b1f5b3b
parent 157145 851f136057577387ed70deb219bc507c9e5d2d7a
push id516
push userjwalden@mit.edu
push dateFri, 30 May 2014 03:00:08 +0000
reviewerssfink, shu, jandem, jdm, luke, bbouvier, nmatsakis, bz, ehsan, jgilbert, smaug, sicking, terrence, bholley, bent, efaust, jorendorff, 1.2.x
bugs999651, 995679, 1009952, 1011007, 991981
milestone26.0
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=1.2.x+
CLOBBER
content/base/src/DOMParser.cpp
content/base/src/WebSocket.cpp
content/base/src/nsDOMDataChannel.cpp
content/base/src/nsXMLHttpRequest.cpp
content/canvas/src/CanvasRenderingContext2D.cpp
content/canvas/src/CanvasRenderingContext2D.h
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLContextBuffers.cpp
content/canvas/src/WebGLContextGL.cpp
content/html/content/public/HTMLAudioElement.h
content/media/mediasource/SourceBuffer.cpp
content/media/webaudio/AnalyserNode.cpp
content/media/webaudio/AudioContext.cpp
content/media/webaudio/AudioParam.h
content/media/webaudio/BiquadFilterNode.cpp
content/media/webaudio/WaveShaperNode.cpp
dom/base/Crypto.cpp
dom/bindings/TypedArray.h
dom/encoding/TextDecoder.h
js/src/jit/AsmJSLink.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/MIR.h
js/src/js.msg
js/src/jsfriendapi.h
js/src/vm/TypedArrayObject.cpp
js/src/vm/TypedArrayObject.h
netwerk/base/src/ArrayBufferInputStream.cpp
xpcom/io/nsBinaryStream.cpp
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,9 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Bug 915002 - Clobber needed for webidl updates for AppNotificationServiceOptions. One more time.
+Bug 999651 et al. require a clobber for some unknown reason
--- a/content/base/src/DOMParser.cpp
+++ b/content/base/src/DOMParser.cpp
@@ -134,16 +134,18 @@ DOMParser::ParseFromBuffer(const Sequenc
   nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
   return document.forget();
 }
 
 already_AddRefed<nsIDocument>
 DOMParser::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;
   }
   nsCOMPtr<nsIDOMDocument> domDocument;
   rv = DOMParser::ParseFromBuffer(aBuf.Data(), aBufLen,
                                     StringFromSupportedType(aType),
                                     getter_AddRefs(domDocument));
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -1214,31 +1214,37 @@ WebSocket::Send(nsIDOMBlob* aData,
 }
 
 void
 WebSocket::Send(const 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());
 
   nsDependentCSubstring msgString(data, len);
   Send(nullptr, msgString, len, true, aRv);
 }
 
 void
 WebSocket::Send(const 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());
 
   nsDependentCSubstring msgString(data, len);
   Send(nullptr, msgString, len, true, aRv);
 }
 
 void
--- a/content/base/src/nsDOMDataChannel.cpp
+++ b/content/base/src/nsDOMDataChannel.cpp
@@ -289,30 +289,36 @@ nsDOMDataChannel::Send(nsIDOMBlob* aData
   Send(msgStream, EmptyCString(), msgLength, true, aRv);
 }
 
 void
 nsDOMDataChannel::Send(const 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());
 
   nsDependentCSubstring msgString(data, len);
   Send(nullptr, msgString, len, true, aRv);
 }
 
 void
 nsDOMDataChannel::Send(const 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());
 
   nsDependentCSubstring msgString(data, len);
   Send(nullptr, msgString, len, true, aRv);
 }
 
 void
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2428,16 +2428,17 @@ GetRequestBody(nsIVariant* aBody, nsIInp
     AutoSafeJSContext cx;
     JS::Rooted<JS::Value> realVal(cx);
 
     nsresult rv = aBody->GetAsJSVal(realVal.address());
     if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(realVal)) {
       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);
       }
     }
   }
   else if (dataType == nsIDataType::VTYPE_VOID ||
            dataType == nsIDataType::VTYPE_EMPTY) {
     // Makes us act as if !aBody, don't upload anything
@@ -2471,24 +2472,26 @@ 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->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:
     {
       nsresult rv;
       nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(value.mBlob, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
 
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -3615,43 +3615,43 @@ CanvasRenderingContext2D::FillRuleChange
 void
 CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
                                        double dy, ErrorResult& error)
 {
   dom::Uint8ClampedArray arr(imageData.GetDataObject());
 
   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
 CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
                                        double dy, double dirtyX,
                                        double dirtyY, double dirtyWidth,
                                        double dirtyHeight,
                                        ErrorResult& error)
 {
   dom::Uint8ClampedArray arr(imageData.GetDataObject());
 
   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),
                                 JS_DoubleToInt32(dirtyHeight));
 }
 
 // void putImageData (in ImageData d, in float x, in float y);
 // void putImageData (in ImageData d, in double x, in double y, in double dirtyX, in double dirtyY, in double dirtyWidth, in double dirtyHeight);
 
 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)
 {
   if (w == 0 || h == 0) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   IntRect dirtyRect;
@@ -3694,29 +3694,33 @@ CanvasRenderingContext2D::PutImageData_e
 
   dirtyRect.MoveBy(IntPoint(x, y));
   dirtyRect = IntRect(0, 0, mWidth, mHeight).Intersect(dirtyRect);
 
   if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0) {
     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;
   }
 
   nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(gfxIntSize(w, h),
                                                           gfxASurface::ImageFormatARGB32,
                                                           false);
   if (!imgsurf || imgsurf->CairoStatus()) {
     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++) {
     for (uint32_t i = 0; i < w; i++) {
       uint8_t r = *src++;
       uint8_t g = *src++;
       uint8_t b = *src++;
       uint8_t a = *src++;
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -14,16 +14,17 @@
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "CanvasUtils.h"
 #include "gfxFont.h"
 #include "mozilla/ErrorResult.h"
 #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;
 
 namespace mozilla {
 namespace gfx {
 class SourceSurface;
 }
@@ -450,17 +451,17 @@ public:
   friend class CanvasRenderingContext2DUserData;
 
 protected:
   nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
                              uint32_t aWidth, uint32_t aHeight,
                              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);
 
   /**
    * Internal method to complete initialisation, expects mTarget to have been set
    */
   nsresult Initialize(int32_t width, int32_t height);
 
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -479,135 +479,146 @@ public:
     void Uniform2f(WebGLUniformLocation* location, GLfloat x, GLfloat y);
     void Uniform3f(WebGLUniformLocation* location, GLfloat x, GLfloat y,
                    GLfloat z);
     void Uniform4f(WebGLUniformLocation* location, GLfloat x, GLfloat y,
                    GLfloat z, GLfloat w);
     
     void Uniform1iv(WebGLUniformLocation* location,
                     const dom::Int32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform1iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform1iv(WebGLUniformLocation* location,
                     const dom::Sequence<GLint>& arr) {
         Uniform1iv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform1iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const GLint* data);
 
     void Uniform2iv(WebGLUniformLocation* location,
                     const dom::Int32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform2iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform2iv(WebGLUniformLocation* location,
                     const dom::Sequence<GLint>& arr) {
         Uniform2iv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform2iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const GLint* data);
 
     void Uniform3iv(WebGLUniformLocation* location,
                     const dom::Int32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform3iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform3iv(WebGLUniformLocation* location,
                     const dom::Sequence<GLint>& arr) {
         Uniform3iv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform3iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const GLint* data);
     
     void Uniform4iv(WebGLUniformLocation* location,
                     const dom::Int32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform4iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform4iv(WebGLUniformLocation* location,
                     const dom::Sequence<GLint>& arr) {
         Uniform4iv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform4iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const GLint* data);
 
     void Uniform1fv(WebGLUniformLocation* location,
                     const dom::Float32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform1fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform1fv(WebGLUniformLocation* location,
                     const dom::Sequence<GLfloat>& arr) {
         Uniform1fv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform1fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const GLfloat* data);
 
     void Uniform2fv(WebGLUniformLocation* location,
                     const dom::Float32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform2fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform2fv(WebGLUniformLocation* location,
                     const dom::Sequence<GLfloat>& arr) {
         Uniform2fv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform2fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const GLfloat* data);
 
     void Uniform3fv(WebGLUniformLocation* location,
                     const dom::Float32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform3fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform3fv(WebGLUniformLocation* location,
                     const dom::Sequence<GLfloat>& arr) {
         Uniform3fv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform3fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const GLfloat* data);
     
     void Uniform4fv(WebGLUniformLocation* location,
                     const dom::Float32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform4fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform4fv(WebGLUniformLocation* location,
                     const dom::Sequence<GLfloat>& arr) {
         Uniform4fv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform4fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const GLfloat* data);
 
     void UniformMatrix2fv(WebGLUniformLocation* location,
                           WebGLboolean transpose,
                           const dom::Float32Array &value) {
+        value.ComputeLengthAndData();
         UniformMatrix2fv_base(location, transpose, value.Length(), value.Data());
     }
     void UniformMatrix2fv(WebGLUniformLocation* location,
                           WebGLboolean transpose,
                           const dom::Sequence<float> &value) {
         UniformMatrix2fv_base(location, transpose, value.Length(),
                               value.Elements());
     }
     void UniformMatrix2fv_base(WebGLUniformLocation* location,
                                WebGLboolean transpose, uint32_t arrayLength,
                                const float* data);
 
     void UniformMatrix3fv(WebGLUniformLocation* location,
                           WebGLboolean transpose,
                           const dom::Float32Array &value) {
+        value.ComputeLengthAndData();
         UniformMatrix3fv_base(location, transpose, value.Length(), value.Data());
     }
     void UniformMatrix3fv(WebGLUniformLocation* location,
                           WebGLboolean transpose,
                           const dom::Sequence<float> &value) {
         UniformMatrix3fv_base(location, transpose, value.Length(),
                               value.Elements());
     }
     void UniformMatrix3fv_base(WebGLUniformLocation* location,
                                WebGLboolean transpose, uint32_t arrayLength,
                                const float* data);
 
     void UniformMatrix4fv(WebGLUniformLocation* location,
                           WebGLboolean transpose,
                           const dom::Float32Array &value) {
+        value.ComputeLengthAndData();
         UniformMatrix4fv_base(location, transpose, value.Length(), value.Data());
     }
     void UniformMatrix4fv(WebGLUniformLocation* location,
                           WebGLboolean transpose,
                           const dom::Sequence<float> &value) {
         UniformMatrix4fv_base(location, transpose, value.Length(),
                               value.Elements());
     }
@@ -721,37 +732,41 @@ public:
     void VertexAttrib1f(GLuint index, GLfloat x0);
     void VertexAttrib2f(GLuint index, GLfloat x0, GLfloat x1);
     void VertexAttrib3f(GLuint index, GLfloat x0, GLfloat x1,
                         GLfloat x2);
     void VertexAttrib4f(GLuint index, GLfloat x0, GLfloat x1,
                         GLfloat x2, GLfloat x3);
 
     void VertexAttrib1fv(GLuint idx, const dom::Float32Array &arr) {
+        arr.ComputeLengthAndData();
         VertexAttrib1fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib1fv(GLuint idx, const dom::Sequence<GLfloat>& arr) {
         VertexAttrib1fv_base(idx, arr.Length(), arr.Elements());
     }
 
     void VertexAttrib2fv(GLuint idx, const dom::Float32Array &arr) {
+        arr.ComputeLengthAndData();
         VertexAttrib2fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib2fv(GLuint idx, const dom::Sequence<GLfloat>& arr) {
         VertexAttrib2fv_base(idx, arr.Length(), arr.Elements());
     }
 
     void VertexAttrib3fv(GLuint idx, const dom::Float32Array &arr) {
+        arr.ComputeLengthAndData();
         VertexAttrib3fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib3fv(GLuint idx, const dom::Sequence<GLfloat>& arr) {
         VertexAttrib3fv_base(idx, arr.Length(), arr.Elements());
     }
 
     void VertexAttrib4fv(GLuint idx, const dom::Float32Array &arr) {
+        arr.ComputeLengthAndData();
         VertexAttrib4fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib4fv(GLuint idx, const dom::Sequence<GLfloat>& arr) {
         VertexAttrib4fv_base(idx, arr.Length(), arr.Elements());
     }
 
     void VertexAttribPointer(GLuint index, GLint size, GLenum type,
                              WebGLboolean normalized, GLsizei stride,
--- a/content/canvas/src/WebGLContextBuffers.cpp
+++ b/content/canvas/src/WebGLContextBuffers.cpp
@@ -198,18 +198,20 @@ WebGLContext::BufferData(GLenum target,
 
     WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferData");
 
     if (!bufferSlot) {
         return;
     }
 
     const ArrayBuffer& data = maybeData.Value();
+    data.ComputeLengthAndData();
 
-    // careful: data.Length() could conceivably be any size_t, but GLsizeiptr is like intptr_t.
+    // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
+    // is like intptr_t.
     if (!CheckedInt<GLsizeiptr>(data.Length()).isValid())
         return ErrorOutOfMemory("bufferData: bad size");
 
     if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
         return;
 
     WebGLBuffer* boundBuffer = bufferSlot->get();
 
@@ -248,17 +250,20 @@ WebGLContext::BufferData(GLenum target, 
     if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
         return;
 
     WebGLBuffer* boundBuffer = bufferSlot->get();
 
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
-    // careful: data.Length() could conceivably be any size_t, but GLsizeiptr is like intptr_t.
+    data.ComputeLengthAndData();
+
+    // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
+    // is like intptr_t.
     if (!CheckedInt<GLsizeiptr>(data.Length()).isValid())
         return ErrorOutOfMemory("bufferData: bad size");
 
     InvalidateBufferFetching();
     MakeContextCurrent();
 
     GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage);
     if (error) {
@@ -285,26 +290,27 @@ WebGLContext::BufferSubData(GLenum targe
     }
 
     WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferSubData");
 
     if (!bufferSlot) {
         return;
     }
 
-    const ArrayBuffer& data = maybeData.Value();
-
     if (byteOffset < 0)
         return ErrorInvalidValue("bufferSubData: negative offset");
 
     WebGLBuffer* boundBuffer = bufferSlot->get();
 
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
+    const ArrayBuffer& data = maybeData.Value();
+    data.ComputeLengthAndData();
+
     CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length();
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length");
 
     if (checked_neededByteLength.value() > boundBuffer->ByteLength())
         return ErrorInvalidValue("bufferSubData: not enough data - operation requires %d bytes, but buffer only has %d bytes",
                                  checked_neededByteLength.value(), boundBuffer->ByteLength());
 
@@ -331,16 +337,18 @@ WebGLContext::BufferSubData(GLenum targe
     if (byteOffset < 0)
         return ErrorInvalidValue("bufferSubData: negative offset");
 
     WebGLBuffer* boundBuffer = bufferSlot->get();
 
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferSubData: no buffer bound!");
 
+    data.ComputeLengthAndData();
+
     CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length();
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length");
 
     if (checked_neededByteLength.value() > boundBuffer->ByteLength())
         return ErrorInvalidValue("bufferSubData: not enough data -- operation requires %d bytes, but buffer only has %d bytes",
                                  checked_neededByteLength.value(), boundBuffer->ByteLength());
 
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -2203,17 +2203,18 @@ WebGLContext::ReadPixels(GLint x, GLint 
         case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
             bytesPerPixel = 2;
             requiredDataType = js::ArrayBufferView::TYPE_UINT16;
             break;
         default:
             return ErrorInvalidEnum("readPixels: Bad type");
     }
 
-    int dataType = JS_GetArrayBufferViewType(pixels.Value().Obj());
+    const ArrayBufferView& pixbuf = pixels.Value();
+    int dataType = JS_GetArrayBufferViewType(pixbuf.Obj());
 
     // Check the pixels param type
     if (dataType != requiredDataType)
         return ErrorInvalidOperation("readPixels: Mismatched type/pixels types");
 
     // Check the pixels param size
     CheckedUint32 checked_neededByteLength =
         GetImageSize(height, width, bytesPerPixel, mPixelStorePackAlignment);
@@ -2221,21 +2222,25 @@ WebGLContext::ReadPixels(GLint x, GLint 
     CheckedUint32 checked_plainRowSize = CheckedUint32(width) * bytesPerPixel;
 
     CheckedUint32 checked_alignedRowSize =
         RoundedToNextMultipleOf(checked_plainRowSize, mPixelStorePackAlignment);
 
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("readPixels: integer overflow computing the needed buffer size");
 
-    uint32_t dataByteLen = JS_GetTypedArrayByteLength(pixels.Value().Obj());
+    // Compute length and data.  Don't reenter after this point, lest the
+    // precomputed go out of sync with the instant length/data.
+    pixbuf.ComputeLengthAndData();
+
+    uint32_t dataByteLen = pixbuf.Length();
     if (checked_neededByteLength.value() > dataByteLen)
         return ErrorInvalidOperation("readPixels: buffer too small");
 
-    void* data = pixels.Value().Data();
+    void* data = pixbuf.Data();
     if (!data) {
         ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?");
         return rv.Throw(NS_ERROR_OUT_OF_MEMORY);
     }
 
     // Check the format and type params to assure they are an acceptable pair (as per spec)
     switch (format) {
         case LOCAL_GL_RGBA: {
@@ -3302,16 +3307,18 @@ WebGLContext::CompressedTexImage2D(GLenu
         return;
     }
 
     if (border) {
         ErrorInvalidValue("compressedTexImage2D: border is not 0");
         return;
     }
 
+    view.ComputeLengthAndData();
+
     uint32_t byteLength = view.Length();
     if (!ValidateCompressedTextureSize(target, level, internalformat, width, height, byteLength, "compressedTexImage2D")) {
         return;
     }
 
     MakeContextCurrent();
     gl->fCompressedTexImage2D(target, level, internalformat, width, height, border, byteLength, view.Data());
     tex->SetImageInfo(target, level, width, height, internalformat, LOCAL_GL_UNSIGNED_BYTE);
@@ -3351,16 +3358,18 @@ WebGLContext::CompressedTexSubImage2D(GL
         ErrorInvalidEnum("compressedTexSubImage2D: compressed texture format 0x%x is not supported", format);
         return;
     }
 
     if (!ValidateLevelWidthHeightForTarget(target, level, width, height, "compressedTexSubImage2D")) {
         return;
     }
 
+    view.ComputeLengthAndData();
+
     uint32_t byteLength = view.Length();
     if (!ValidateCompressedTextureSize(target, level, format, width, height, byteLength, "compressedTexSubImage2D")) {
         return;
     }
 
     size_t face = WebGLTexture::FaceForTarget(target);
 
     if (!tex->HasImageInfoAt(level, face)) {
@@ -3776,20 +3785,34 @@ void
 WebGLContext::TexImage2D(GLenum target, GLint level,
                          GLenum internalformat, GLsizei width,
                          GLsizei height, GLint border, GLenum format,
                          GLenum type, const Nullable<ArrayBufferView> &pixels, ErrorResult& rv)
 {
     if (IsContextLost())
         return;
 
+    void* data;
+    uint32_t length;
+    int jsArrayType;
+    if (pixels.IsNull()) {
+        data = nullptr;
+        length = 0;
+        jsArrayType = -1;
+    } else {
+        const ArrayBufferView& view = pixels.Value();
+        view.ComputeLengthAndData();
+
+        data = view.Data();
+        length = view.Length();
+        jsArrayType = int(JS_GetArrayBufferViewType(view.Obj()));
+    }
+
     return TexImage2D_base(target, level, internalformat, width, height, 0, border, format, type,
-                           pixels.IsNull() ? 0 : pixels.Value().Data(),
-                           pixels.IsNull() ? 0 : pixels.Value().Length(),
-                           pixels.IsNull() ? -1 : (int)JS_GetArrayBufferViewType(pixels.Value().Obj()),
+                           data, length, jsArrayType,
                            WebGLTexelConversions::Auto, false);
 }
 
 void
 WebGLContext::TexImage2D(GLenum target, GLint level,
                          GLenum internalformat, GLenum format,
                          GLenum type, ImageData* pixels, ErrorResult& rv)
 {
@@ -3797,16 +3820,18 @@ WebGLContext::TexImage2D(GLenum target, 
         return;
 
     if (!pixels) {
         // Spec says to generate an INVALID_VALUE error
         return ErrorInvalidValue("texImage2D: null ImageData");
     }
     
     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,
                            WebGLTexelConversions::RGBA8, false);
 }
 
 
 void
@@ -3930,36 +3955,41 @@ WebGLContext::TexSubImage2D(GLenum targe
                             ErrorResult& rv)
 {
     if (IsContextLost())
         return;
 
     if (pixels.IsNull())
         return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
 
+    const ArrayBufferView& view = pixels.Value();
+    view.ComputeLengthAndData();
+
     return TexSubImage2D_base(target, level, xoffset, yoffset,
                               width, height, 0, format, type,
-                              pixels.Value().Data(), pixels.Value().Length(),
-                              JS_GetArrayBufferViewType(pixels.Value().Obj()),
+                              view.Data(), view.Length(),
+                              JS_GetArrayBufferViewType(view.Obj()),
                               WebGLTexelConversions::Auto, false);
 }
 
 void
 WebGLContext::TexSubImage2D(GLenum target, GLint level,
                             GLint xoffset, GLint yoffset,
                             GLenum format, GLenum type, ImageData* pixels,
                             ErrorResult& rv)
 {
     if (IsContextLost())
         return;
 
     if (!pixels)
         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,
                               arr.Data(), arr.Length(),
                               -1,
                               WebGLTexelConversions::RGBA8, false);
 }
 
--- a/content/html/content/public/HTMLAudioElement.h
+++ b/content/html/content/public/HTMLAudioElement.h
@@ -49,16 +49,17 @@ public:
   static already_AddRefed<HTMLAudioElement>
   Audio(const GlobalObject& aGlobal,
         const Optional<nsAString>& aSrc, ErrorResult& aRv);
 
   void MozSetup(uint32_t aChannels, uint32_t aRate, ErrorResult& aRv);
 
   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)
   {
     return MozWriteAudio(aData.Elements(), aData.Length(), aRv);
   }
   uint32_t MozWriteAudio(const float* aData, uint32_t aLength,
                          ErrorResult& aRv);
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/SourceBuffer.cpp
@@ -84,22 +84,26 @@ SourceBuffer::SetAppendWindowEnd(double 
     return;
   }
   mAppendWindowEnd = aAppendWindowEnd;
 }
 
 void
 SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv)
 {
+  aData.ComputeLengthAndData();
+
   AppendData(aData.Data(), aData.Length(), aRv);
 }
 
 void
 SourceBuffer::AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv)
 {
+  aData.ComputeLengthAndData();
+
   AppendData(aData.Data(), aData.Length(), aRv);
 }
 
 void
 SourceBuffer::Abort(ErrorResult& aRv)
 {
   if (!mAttached) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
--- a/content/media/webaudio/AnalyserNode.cpp
+++ b/content/media/webaudio/AnalyserNode.cpp
@@ -145,16 +145,18 @@ AnalyserNode::SetSmoothingTimeConstant(d
 void
 AnalyserNode::GetFloatFrequencyData(const Float32Array& aArray)
 {
   if (!FFTAnalysis()) {
     // Might fail to allocate memory
     return;
   }
 
+  aArray.ComputeLengthAndData();
+
   float* buffer = aArray.Data();
   uint32_t length = std::min(aArray.Length(), mOutputBuffer.Length());
 
   for (uint32_t i = 0; i < length; ++i) {
     buffer[i] = WebAudioUtils::ConvertLinearToDecibels(mOutputBuffer[i], mMinDecibels);
   }
 }
 
@@ -163,31 +165,35 @@ AnalyserNode::GetByteFrequencyData(const
 {
   if (!FFTAnalysis()) {
     // Might fail to allocate memory
     return;
   }
 
   const double rangeScaleFactor = 1.0 / (mMaxDecibels - mMinDecibels);
 
+  aArray.ComputeLengthAndData();
+
   unsigned char* buffer = aArray.Data();
   uint32_t length = std::min(aArray.Length(), mOutputBuffer.Length());
 
   for (uint32_t i = 0; i < length; ++i) {
     const double decibels = WebAudioUtils::ConvertLinearToDecibels(mOutputBuffer[i], mMinDecibels);
     // scale down the value to the range of [0, UCHAR_MAX]
     const double scaled = std::max(0.0, std::min(double(UCHAR_MAX),
                                                  UCHAR_MAX * (decibels - mMinDecibels) * rangeScaleFactor));
     buffer[i] = static_cast<unsigned char>(scaled);
   }
 }
 
 void
 AnalyserNode::GetByteTimeDomainData(const Uint8Array& aArray)
 {
+  aArray.ComputeLengthAndData();
+
   unsigned char* buffer = aArray.Data();
   uint32_t length = std::min(aArray.Length(), mBuffer.Length());
 
   for (uint32_t i = 0; i < length; ++i) {
     const float value = mBuffer[(i + mWriteIndex) % mBuffer.Length()];
     // scale the value to the range of [0, UCHAR_MAX]
     const float scaled = std::max(0.0f, std::min(float(UCHAR_MAX),
                                                  128.0f * (value + 1.0f)));
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -155,28 +155,30 @@ AudioContext::CreateBuffer(JSContext* aJ
                           bool aMixToMono, ErrorResult& aRv)
 {
   // Do not accept this method unless the legacy pref has been set.
   if (!Preferences::GetBool("media.webaudio.legacy.AudioContext")) {
     aRv.ThrowNotEnoughArgsError();
     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) {
       buffer->MixToMono(aJSContext);
     }
     return buffer.forget();
   }
 
@@ -367,16 +369,19 @@ AudioContext::CreateOscillator()
   return oscillatorNode.forget();
 }
 
 already_AddRefed<PeriodicWave>
 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) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
 
   nsRefPtr<PeriodicWave> periodicWave =
@@ -397,16 +402,18 @@ AudioContext::Listener()
   return mListener;
 }
 
 void
 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;
   NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, nullptr,
                   aBuffer.Data(), aBuffer.Length(),
                   contentType);
 
   nsCOMPtr<DecodeErrorCallback> failureCallback;
--- a/content/media/webaudio/AudioParam.h
+++ b/content/media/webaudio/AudioParam.h
@@ -48,16 +48,17 @@ public:
   // We override SetValueCurveAtTime to convert the Float32Array to the wrapper
   // object.
   void SetValueCurveAtTime(const Float32Array& aValues, double aStartTime, double aDuration, ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aStartTime)) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
+    aValues.ComputeLengthAndData();
     AudioParamTimeline::SetValueCurveAtTime(aValues.Data(), aValues.Length(),
                                             aStartTime, aDuration, aRv);
     mCallback(mNode);
   }
 
   // We override the rest of the mutating AudioParamTimeline methods in order to make
   // sure that the callback is called every time that this object gets mutated.
   void SetValue(float aValue)
--- a/content/media/webaudio/BiquadFilterNode.cpp
+++ b/content/media/webaudio/BiquadFilterNode.cpp
@@ -254,16 +254,20 @@ BiquadFilterNode::SetType(BiquadFilterTy
                              static_cast<int32_t>(aType));
 }
 
 void
 BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
                                        const Float32Array& aMagResponse,
                                        const Float32Array& aPhaseResponse)
 {
+  aFrequencyHz.ComputeLengthAndData();
+  aMagResponse.ComputeLengthAndData();
+  aPhaseResponse.ComputeLengthAndData();
+
   uint32_t length = std::min(std::min(aFrequencyHz.Length(), aMagResponse.Length()),
                              aPhaseResponse.Length());
   if (!length) {
     return;
   }
 
   nsAutoArrayPtr<float> frequencies(new float[length]);
   float* frequencyHz = aFrequencyHz.Data();
--- a/content/media/webaudio/WaveShaperNode.cpp
+++ b/content/media/webaudio/WaveShaperNode.cpp
@@ -282,20 +282,24 @@ WaveShaperNode::WrapObject(JSContext *aC
   return WaveShaperNodeBinding::Wrap(aCx, aScope, this);
 }
 
 void
 WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve)
 {
   nsTArray<float> curve;
   if (!aCurve.IsNull()) {
-    mCurve = aCurve.Value().Obj();
+    const Float32Array& floats = aCurve.Value();
+
+    mCurve = floats.Obj();
 
-    curve.SetLength(aCurve.Value().Length());
-    PodCopy(curve.Elements(), aCurve.Value().Data(), aCurve.Value().Length());
+    floats.ComputeLengthAndData();
+
+    curve.SetLength(floats.Length());
+    PodCopy(curve.Elements(), floats.Data(), floats.Length());
   } else {
     mCurve = nullptr;
   }
 
   AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
   MOZ_ASSERT(ns, "Why don't we have a stream here?");
   ns->SetRawArrayData(curve);
 }
--- a/dom/base/Crypto.cpp
+++ b/dom/base/Crypto.cpp
@@ -71,16 +71,17 @@ Crypto::GetRandomValues(JSContext* aCx, 
     case TYPE_INT32:
     case TYPE_UINT32:
       break;
     default:
       aRv.Throw(NS_ERROR_DOM_TYPE_MISMATCH_ERR);
       return nullptr;
   }
 
+  aArray.ComputeLengthAndData();
   uint32_t dataLen = aArray.Length();
   if (dataLen == 0) {
     NS_WARNING("ArrayBufferView length is 0, cannot continue");
     return view;
   } else if (dataLen > 65536) {
     aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
     return nullptr;
   }
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -21,17 +21,17 @@ namespace dom {
 
 /*
  * Class that just handles the JSObject storage and tracing for typed arrays
  */
 struct TypedArrayObjectStorage : AllTypedArraysBase {
 protected:
   JSObject* mObj;
 
-  TypedArrayObjectStorage()
+  TypedArrayObjectStorage(JSObject *obj) : mObj(obj)
   {
   }
 
 public:
   inline void TraceSelf(JSTracer* trc)
   {
     JS_CallObjectTracer(trc, &mObj, "TypedArray.mObj");
   }
@@ -42,86 +42,112 @@ private:
 
 /*
  * Various typed array classes for argument conversion.  We have a base class
  * that has a way of initializing a TypedArray from an existing typed array, and
  * a subclass of the base class that supports creation of a relevant typed array
  * 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 : public TypedArrayObjectStorage {
   TypedArray_base(JSObject* obj)
+    : TypedArrayObjectStorage(obj),
+      mData(nullptr),
+      mLength(0),
+      mComputed(false)
   {
-    DoInit(obj);
+    MOZ_ASSERT(obj != nullptr);
   }
 
   TypedArray_base()
+    : TypedArrayObjectStorage(nullptr),
+      mData(nullptr),
+      mLength(0),
+      mComputed(false)
   {
-    mObj = nullptr;
   }
 
 private:
-  T* mData;
-  uint32_t mLength;
+  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;
   }
 
   inline JSObject *Obj() const {
     MOZ_ASSERT(inited());
     return mObj;
   }
 
   inline bool WrapIntoNewCompartment(JSContext* cx)
   {
     return JS_WrapObject(cx, &mObj);
   }
 
+  inline void ComputeLengthAndData() const
+  {
+    MOZ_ASSERT(inited());
+    MOZ_ASSERT(!mComputed);
+    GetLengthAndData(mObj, &mLength, &mData);
+    mComputed = true;
+  }
+
 protected:
   inline void DoInit(JSObject* obj)
   {
-    mObj = UnboxArray(obj, &mLength, &mData);
+    mObj = UnwrapArray(obj);
+  }
+
+  inline void ComputeData() const {
+    MOZ_ASSERT(inited());
+    if (!mComputed) {
+      GetLengthAndData(mObj, &mLength, &mData);
+      mComputed = true;
+    }
   }
 
 private:
   TypedArray_base(const TypedArray_base&) MOZ_DELETE;
 };
 
 
 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)
   {}
 
   TypedArray() :
-    TypedArray_base<T,UnboxArray>()
+    TypedArray_base<T, UnwrapArray, GetLengthAndData>()
   {}
 
   static inline JSObject*
   Create(JSContext* cx, nsWrapperCache* creator, uint32_t length,
          const T* data = nullptr) {
     JS::Rooted<JSObject*> creatorWrapper(cx);
     Maybe<JSAutoCompartment> ac;
     if (creator && (creatorWrapper = creator->GetWrapperPreserveColor())) {
@@ -155,47 +181,47 @@ private:
       memcpy(buf, data, length*sizeof(T));
     }
     return obj;
   }
 
   TypedArray(const TypedArray&) MOZ_DELETE;
 };
 
-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;
 
 // A class for rooting an existing TypedArray struct
 template<typename ArrayType>
 class MOZ_STACK_CLASS TypedArrayRooter : private JS::CustomAutoRooter
 {
 public:
   TypedArrayRooter(JSContext* cx,
--- a/dom/encoding/TextDecoder.h
+++ b/dom/encoding/TextDecoder.h
@@ -101,16 +101,17 @@ public:
               ErrorResult& aRv) {
     Decode(nullptr, 0, false, aOutDecodedString, aRv);
   }
 
   void Decode(const ArrayBufferView& aView,
               const TextDecodeOptions& aOptions,
               nsAString& aOutDecodedString,
               ErrorResult& aRv) {
+    aView.ComputeLengthAndData();
     Decode(reinterpret_cast<char*>(aView.Data()), aView.Length(),
            aOptions.mStream, aOutDecodedString, aRv);
   }
 
 private:
   nsCString mEncoding;
   nsCOMPtr<nsIUnicodeDecoder> mDecoder;
   bool mFatal;
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -210,28 +210,29 @@ DynamicallyLinkModule(JSContext *cx, Cal
 
     Rooted<ArrayBufferObject*> heap(cx);
     if (module.hasArrayView()) {
         if (!IsTypedArrayBuffer(bufferVal))
             return LinkFail(cx, "bad ArrayBuffer argument");
 
         heap = &bufferVal.toObject().as<ArrayBufferObject>();
 
-        if (!IsValidAsmJSHeapLength(heap->byteLength())) {
+        uint32_t heapLength = heap->byteLength();
+        if (!IsValidAsmJSHeapLength(heapLength)) {
             return LinkFail(cx, JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next valid length is 0x%x",
-                                            heap->byteLength(),
-                                            RoundUpToNextValidAsmJSHeapLength(heap->byteLength())));
+                                            heapLength,
+                                            RoundUpToNextValidAsmJSHeapLength(heapLength)));
         }
 
         // This check is sufficient without considering the size of the loaded datum because heap
         // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
         JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
-        if (heap->byteLength() < module.minHeapLength()) {
+        if (heapLength < module.minHeapLength()) {
             return LinkFail(cx, JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the largest constant heap access offset rounded up to the next valid heap size).",
-                                            heap->byteLength(), module.minHeapLength()));
+                                            heapLength, module.minHeapLength()));
         }
 
         if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
             return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
 
         module.initHeap(heap, cx);
     }
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6589,65 +6589,16 @@ bool
 IonBuilder::getElemTryTypedStatic(bool *emitted, MDefinition *obj, MDefinition *index)
 {
     JS_ASSERT(*emitted == false);
 
     ScalarTypeRepresentation::Type arrayType;
     if (!ElementAccessIsTypedArray(obj, index, &arrayType))
         return true;
 
-    if (!LIRGenerator::allowStaticTypedArrayAccesses())
-        return true;
-
-    if (ElementAccessHasExtraIndexedProperty(cx, obj))
-        return true;
-
-    if (!obj->resultTypeSet())
-        return true;
-
-    JSObject *tarrObj = obj->resultTypeSet()->getSingleton();
-    if (!tarrObj)
-        return true;
-
-    TypedArrayObject *tarr = &tarrObj->as<TypedArrayObject>();
-    ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(tarr);
-
-    // LoadTypedArrayElementStatic currently treats uint32 arrays as int32.
-    if (viewType == ArrayBufferView::TYPE_UINT32)
-        return true;
-
-    MDefinition *ptr = convertShiftToMaskForStaticTypedArray(index, viewType);
-    if (!ptr)
-        return true;
-
-    // Emit LoadTypedArrayElementStatic.
-
-    obj->setFoldedUnchecked();
-    index->setFoldedUnchecked();
-
-    MLoadTypedArrayElementStatic *load = MLoadTypedArrayElementStatic::New(tarr, ptr);
-    current->add(load);
-    current->push(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();
-    }
-
-    *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::getElemTryTyped(bool *emitted, MDefinition *obj, MDefinition *index)
 {
     JS_ASSERT(*emitted == false);
 
@@ -6930,39 +6881,22 @@ IonBuilder::jsop_getelem_dense(MDefiniti
 
     current->push(load);
     return pushTypeBarrier(load, types, barrier);
 }
 
 MInstruction *
 IonBuilder::getTypedArrayLength(MDefinition *obj)
 {
-    if (obj->isConstant() && obj->toConstant()->value().isObject()) {
-        TypedArrayObject *tarr = &obj->toConstant()->value().toObject().as<TypedArrayObject>();
-        int32_t length = (int32_t) tarr->length();
-        obj->setFoldedUnchecked();
-        return MConstant::New(Int32Value(length));
-    }
     return MTypedArrayLength::New(obj);
 }
 
 MInstruction *
 IonBuilder::getTypedArrayElements(MDefinition *obj)
 {
-    if (obj->isConstant() && obj->toConstant()->value().isObject()) {
-        TypedArrayObject *tarr = &obj->toConstant()->value().toObject().as<TypedArrayObject>();
-        void *data = tarr->viewData();
-
-        // The 'data' pointer can change in rare circumstances
-        // (ArrayBufferObject::changeContents).
-        types::HeapTypeSet::WatchObjectStateChange(cx, tarr->getType(cx));
-
-        obj->setFoldedUnchecked();
-        return MConstantElements::New(data);
-    }
     return MTypedArrayElements::New(obj);
 }
 
 MDefinition *
 IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition *id,
                                                   ArrayBufferView::ViewType viewType)
 {
     // No shifting is necessary if the typed array has single byte elements.
@@ -7147,54 +7081,16 @@ IonBuilder::setElemTryTypedStatic(bool *
                                   MDefinition *index, MDefinition *value)
 {
     JS_ASSERT(*emitted == false);
 
     ScalarTypeRepresentation::Type arrayType;
     if (!ElementAccessIsTypedArray(object, index, &arrayType))
         return true;
 
-    if (!LIRGenerator::allowStaticTypedArrayAccesses())
-        return true;
-
-    if (ElementAccessHasExtraIndexedProperty(cx, object))
-        return true;
-
-    if (!object->resultTypeSet())
-        return true;
-    JSObject *tarrObj = object->resultTypeSet()->getSingleton();
-    if (!tarrObj)
-        return true;
-
-    TypedArrayObject *tarr = &tarrObj->as<TypedArrayObject>();
-    ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(tarr);
-
-    MDefinition *ptr = convertShiftToMaskForStaticTypedArray(index, viewType);
-    if (!ptr)
-        return true;
-
-    // Emit StoreTypedArrayElementStatic.
-    object->setFoldedUnchecked();
-    index->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(tarr, ptr, toWrite);
-    current->add(store);
-    current->push(value);
-
-    if (!resumeAfter(store))
-        return false;
-
-    *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::setElemTryTyped(bool *emitted, MDefinition *object,
                             MDefinition *index, MDefinition *value)
 {
     JS_ASSERT(*emitted == false);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -210,24 +210,26 @@ class AliasSet {
     enum Flag {
         None_             = 0,
         ObjectFields      = 1 << 0, // shape, class, slots, length etc.
         Element           = 1 << 1, // A member of obj->elements.
         DynamicSlot       = 1 << 2, // A member of obj->slots.
         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)
     {
         JS_STATIC_ASSERT((1 << NumCategories) - 1 == Any);
     }
 
   public:
     inline bool isNone() const {
@@ -4822,19 +4824,17 @@ class MTypedArrayLength
     }
     MDefinition *object() const {
         return getOperand(0);
     }
     bool congruentTo(MDefinition *ins) const {
         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);
     }
 
     void computeRange();
 };
 
 // Load a typed array's elements vector.
 class MTypedArrayElements
   : public MUnaryInstruction,
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -236,21 +236,21 @@ MSG_DEF(JSMSG_BAD_GENERATOR_SEND,     18
 MSG_DEF(JSMSG_UNUSED183,              183, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED184,              184, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE, 185, 0, JSEXN_TYPEERR, "proxy can't report an extensible object as non-extensible")
 MSG_DEF(JSMSG_UNUSED186,              186, 0, JSEXN_NONE, "")
 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")
 MSG_DEF(JSMSG_TOO_MANY_LOCALS,        199, 0, JSEXN_SYNTAXERR, "too many local variables")
 MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG,     200, 0, JSEXN_INTERNALERR, "array initialiser too large")
 MSG_DEF(JSMSG_REGEXP_TOO_COMPLEX,     201, 0, JSEXN_INTERNALERR, "regular expression too complex")
 MSG_DEF(JSMSG_BUFFER_TOO_SMALL,       202, 0, JSEXN_INTERNALERR, "buffer too small")
 MSG_DEF(JSMSG_BAD_SURROGATE_CHAR,     203, 1, JSEXN_TYPEERR, "bad surrogate character {0}")
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsfriendapi_h
 #define jsfriendapi_h
 
+#include "mozilla/Casting.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jsbytecode.h"
 #include "jspubtd.h"
 
 #include "js/CallArgs.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/Class.h"
@@ -1068,16 +1069,103 @@ JS_IsInt32Array(JSObject *obj);
 extern JS_FRIEND_API(bool)
 JS_IsUint32Array(JSObject *obj);
 extern JS_FRIEND_API(bool)
 JS_IsFloat32Array(JSObject *obj);
 extern JS_FRIEND_API(bool)
 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.
  */
 extern JS_FRIEND_API(JSObject *)
 JS_GetObjectAsInt8Array(JSObject *obj, uint32_t *length, int8_t **data);
 extern JS_FRIEND_API(JSObject *)
 JS_GetObjectAsUint8Array(JSObject *obj, uint32_t *length, uint8_t **data);
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -333,17 +333,17 @@ ArrayBufferObject::changeContents(JSCont
    // Grab out data before invalidating it.
    uint32_t byteLengthCopy = byteLength();
    uintptr_t oldDataPointer = uintptr_t(dataPointer());
    ArrayBufferViewObject *viewListHead = GetViewList(this);
 
    // Update all views.
    uintptr_t newDataPointer = uintptr_t(newHeader->elements());
    for (ArrayBufferViewObject *view = viewListHead; view; view = view->nextView()) {
-       uintptr_t newDataPtr = uintptr_t(view->getPrivate()) - oldDataPointer + newDataPointer;
+       uintptr_t newDataPtr = uintptr_t(view->dataPointer()) - oldDataPointer + newDataPointer;
        view->setPrivate(reinterpret_cast<uint8_t*>(newDataPtr));
 
        // Notify compiled jit code that the base pointer has moved.
        if (maybecx)
            MarkObjectStateChange(maybecx, view);
    }
 
    // Change to the new header (now, so we can use SetViewList).
@@ -546,19 +546,22 @@ ArrayBufferObject::create(JSContext *cx,
 
     return obj;
 }
 
 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, nullptr, JSMSG_TYPE_ERR_BAD_ARGS);
+        return nullptr;
+    }
+
     uint32_t length = end - begin;
 
     if (arrayBuffer.hasData())
         return create(cx, length, arrayBuffer.dataPointer() + begin);
 
     return create(cx, 0);
 }
 
@@ -1955,30 +1958,36 @@ class TypedArrayObjectTemplate : public 
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return false;
         }
 
         uint32_t srcBegin;
         uint32_t srcEnd;
         uint32_t dest;
 
-        uint32_t length = tarray->length();
-        if (!ToClampedIndex(cx, args[0], length, &srcBegin) ||
-            !ToClampedIndex(cx, args[1], length, &srcEnd) ||
-            !ToClampedIndex(cx, args[2], length, &dest) ||
+        uint32_t originalLength = tarray->length();
+        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 = tarray->length(); // 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;
         }
 
         uint32_t byteDest = dest * sizeof(NativeType);
         uint32_t byteSrc = srcBegin * sizeof(NativeType);
         uint32_t byteSize = nelts * sizeof(NativeType);
 
@@ -2047,18 +2056,17 @@ class TypedArrayObjectTemplate : public 
 
             if (!copyFromTypedArray(cx, tarray, arg0, offset))
                 return false;
         } else {
             uint32_t len;
             if (!GetLengthProperty(cx, arg0, &len))
                 return false;
 
-            // avoid overflow; we know that offset <= length
-            if (len > tarray->length() - offset) {
+            if (uint32_t(offset) > tarray->length() || len > tarray->length() - offset) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
                 return false;
             }
 
             if (!copyFromArray(cx, tarray, arg0, len, offset))
                 return false;
         }
 
@@ -2202,39 +2210,42 @@ class TypedArrayObjectTemplate : public 
         if (!obj || !copyFromArray(cx, obj, other, len))
             return NULL;
         return obj;
     }
 
     static const NativeType
     getIndex(JSObject *obj, uint32_t index)
     {
-        return *(static_cast<const NativeType*>(obj->as<TypedArrayObject>().viewData()) + index);
+        TypedArrayObject &tarray = obj->as<TypedArrayObject>();
+        MOZ_ASSERT(index < tarray.length());
+        return static_cast<const NativeType*>(tarray.viewData())[index];
     }
 
     static void
     setIndex(JSObject *obj, uint32_t index, NativeType val)
     {
         *(static_cast<NativeType*>(obj->as<TypedArrayObject>().viewData()) + index) = val;
     }
 
     static void copyIndexToValue(JSObject *tarray, uint32_t index, MutableHandleValue vp);
 
     static JSObject *
     createSubarray(JSContext *cx, HandleObject tarrayArg, uint32_t begin, uint32_t end)
     {
         Rooted<TypedArrayObject*> tarray(cx, &tarrayArg->as<TypedArrayObject>());
 
-        JS_ASSERT(begin <= tarray->length());
-        JS_ASSERT(end <= tarray->length());
+        if (begin > tarray->length() || end > tarray->length() || begin > end) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
+            return nullptr;
+        }
 
         RootedObject bufobj(cx, tarray->buffer());
         JS_ASSERT(bufobj);
 
-        JS_ASSERT(begin <= end);
         uint32_t length = end - begin;
 
         JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType));
         uint32_t arrayByteOffset = tarray->byteOffset();
         JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= arrayByteOffset);
         uint32_t byteOffset = arrayByteOffset + begin * sizeof(NativeType);
 
         return makeInstance(cx, bufobj, byteOffset, length);
@@ -2443,63 +2454,64 @@ class TypedArrayObjectTemplate : public 
 
         // We have to make a copy of the source array here, since
         // there's overlap, and we have to convert types.
         void *srcbuf = cx->malloc_(byteLength);
         if (!srcbuf)
             return false;
         js_memcpy(srcbuf, tarray->viewData(), byteLength);
 
+        uint32_t len = tarray->length();
         switch (tarray->type()) {
           case ScalarTypeRepresentation::TYPE_INT8: {
             int8_t *src = (int8_t*) srcbuf;
-            for (unsigned i = 0; i < tarray->length(); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case ScalarTypeRepresentation::TYPE_UINT8:
           case ScalarTypeRepresentation::TYPE_UINT8_CLAMPED: {
             uint8_t *src = (uint8_t*) srcbuf;
-            for (unsigned i = 0; i < tarray->length(); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case ScalarTypeRepresentation::TYPE_INT16: {
             int16_t *src = (int16_t*) srcbuf;
-            for (unsigned i = 0; i < tarray->length(); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case ScalarTypeRepresentation::TYPE_UINT16: {
             uint16_t *src = (uint16_t*) srcbuf;
-            for (unsigned i = 0; i < tarray->length(); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case ScalarTypeRepresentation::TYPE_INT32: {
             int32_t *src = (int32_t*) srcbuf;
-            for (unsigned i = 0; i < tarray->length(); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case ScalarTypeRepresentation::TYPE_UINT32: {
             uint32_t *src = (uint32_t*) srcbuf;
-            for (unsigned i = 0; i < tarray->length(); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case ScalarTypeRepresentation::TYPE_FLOAT32: {
             float *src = (float*) srcbuf;
-            for (unsigned i = 0; i < tarray->length(); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case ScalarTypeRepresentation::TYPE_FLOAT64: {
             double *src = (double*) srcbuf;
-            for (unsigned i = 0; i < tarray->length(); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           default:
             MOZ_ASSUME_UNREACHABLE("copyFromWithOverlap with a TypedArrayObject of unknown type");
         }
 
         js_free(srcbuf);
@@ -2719,16 +2731,23 @@ DataViewObject::create(JSContext *cx, ui
                        Handle<ArrayBufferObject*> arrayBuffer, JSObject *protoArg)
 {
     JS_ASSERT(byteOffset <= INT32_MAX);
     JS_ASSERT(byteLength <= INT32_MAX);
 
     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)
         return NULL;
 
     if (proto) {
         types::TypeObject *type = cx->getNewType(&class_, TaggedProto(proto));
         if (!type)
@@ -2847,31 +2866,27 @@ DataViewObject::class_constructor(JSCont
             return false;
         args.rval().set(args2.rval());
         return true;
     }
 
     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
 needToSwapBytes(bool littleEndian)
 {
 #if IS_LITTLE_ENDIAN
     return !littleEndian;
 #else
@@ -2949,21 +2964,26 @@ DataViewObject::read(JSContext *cx, Hand
                      CallArgs &args, NativeType *val, const char *method)
 {
     if (args.length() < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_MORE_ARGS_NEEDED, method, "0", "s");
         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);
+    if (!data)
+        return false;
+
     DataViewIO<NativeType>::fromBuffer(val, data, needToSwapBytes(fromLittleEndian));
     return true;
 }
 
 template <typename NativeType>
 static inline bool
 WebIDLCast(JSContext *cx, HandleValue value, NativeType *out)
 {
@@ -3001,26 +3021,31 @@ DataViewObject::write(JSContext *cx, Han
                       CallArgs &args, const char *method)
 {
     if (args.length() < 2) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_MORE_ARGS_NEEDED, method, "1", "");
         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;
     if (!WebIDLCast(cx, args[1], &value))
         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;
 }
 
 bool
 DataViewObject::getInt8Impl(JSContext *cx, CallArgs args)
 {
     JS_ASSERT(is(args.thisv()));
@@ -3348,18 +3373,16 @@ DataViewObject::fun_setFloat64(JSContext
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<is, setFloat64Impl>(cx, args);
 }
 
 void
 TypedArrayObject::copyTypedArrayElement(uint32_t index, MutableHandleValue vp)
 {
-    JS_ASSERT(index < length());
-
     switch (type()) {
       case ScalarTypeRepresentation::TYPE_INT8:
         TypedArrayObjectTemplate<int8_t>::copyIndexToValue(this, index, vp);
         break;
       case ScalarTypeRepresentation::TYPE_UINT8:
         TypedArrayObjectTemplate<uint8_t>::copyIndexToValue(this, index, vp);
         break;
       case ScalarTypeRepresentation::TYPE_UINT8_CLAMPED:
@@ -3507,17 +3530,29 @@ const JSFunctionSpec _typedArray##Object
                                                               length, proto);                \
   }                                                                                          \
   JS_FRIEND_API(bool) JS_Is ## Name ## Array(JSObject *obj)                                  \
   {                                                                                          \
       if (!(obj = CheckedUnwrap(obj)))                                                       \
           return false;                                                                      \
       const Class *clasp = obj->getClass();                                                  \
       return (clasp == &TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()]); \
-  }
+  } \
+  JS_FRIEND_API(JSObject *) js::Unwrap ## Name ## Array(JSObject *obj)                          \
+  {                                                                                             \
+      obj = CheckedUnwrap(obj);                                                                 \
+      if (!obj)                                                                                 \
+          return nullptr;                                                                       \
+      const Class *clasp = obj->getClass();                                                     \
+      if (clasp == &TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()]) \
+          return obj;                                                                           \
+      return nullptr;                                                                           \
+  } \
+  JS_FRIEND_DATA(const js::Class* const) js::detail::Name ## ArrayClassPtr =                    \
+      &js::TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()];
 
 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t)
 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8, uint8_t)
 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8Clamped, uint8_clamped)
 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int16, int16_t)
 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint16, uint16_t)
 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int32, int32_t)
 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint32, uint32_t)
@@ -3977,30 +4012,46 @@ js::IsTypedArrayBuffer(HandleValue v)
 
 JS_FRIEND_API(bool)
 JS_IsArrayBufferObject(JSObject *obj)
 {
     obj = CheckedUnwrap(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 : nullptr;
+    return nullptr;
+}
+
 JS_FRIEND_API(bool)
 JS_IsTypedArrayObject(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     return obj ? obj->is<TypedArrayObject>() : false;
 }
 
 JS_FRIEND_API(bool)
 JS_IsArrayBufferViewObject(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     return obj ? obj->is<ArrayBufferViewObject>() : false;
 }
 
+JS_FRIEND_API(JSObject *)
+js::UnwrapArrayBufferView(JSObject *obj)
+{
+    if (JSObject *unwrapped = CheckedUnwrap(obj))
+        return unwrapped->is<ArrayBufferViewObject>() ? unwrapped : nullptr;
+    return nullptr;
+}
+
 JS_FRIEND_API(uint32_t)
 JS_GetArrayBufferByteLength(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     return obj ? obj->as<ArrayBufferObject>().byteLength() : 0;
 }
 
 JS_FRIEND_API(uint8_t *)
@@ -4297,16 +4348,30 @@ JS_GetArrayBufferViewByteLength(JSObject
     obj = CheckedUnwrap(obj);
     if (!obj)
         return 0;
     return obj->is<DataViewObject>()
            ? obj->as<DataViewObject>().byteLength()
            : obj->as<TypedArrayObject>().byteLength();
 }
 
+JS_FRIEND_API(void)
+js::GetArrayBufferViewLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data)
+{
+    MOZ_ASSERT(obj->is<ArrayBufferViewObject>());
+
+    *length = obj->is<DataViewObject>()
+              ? obj->as<DataViewObject>().byteLength()
+              : obj->as<TypedArrayObject>().byteLength();
+
+    *data = static_cast<uint8_t*>(obj->is<DataViewObject>()
+                                  ? obj->as<DataViewObject>().dataPointer()
+                                  : obj->as<TypedArrayObject>().viewData());
+}
+
 JS_FRIEND_API(JSObject *)
 JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data)
 {
     if (!(obj = CheckedUnwrap(obj)))
         return NULL;
     if (!(obj->is<ArrayBufferViewObject>()))
         return NULL;
 
@@ -4328,8 +4393,16 @@ JS_GetObjectAsArrayBuffer(JSObject *obj,
     if (!obj->is<ArrayBufferObject>())
         return NULL;
 
     *length = obj->as<ArrayBufferObject>().byteLength();
     *data = obj->as<ArrayBufferObject>().dataPointer();
 
     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();
+}
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -264,16 +264,20 @@ class ArrayBufferViewObject : public JSO
 
     inline void setNextView(ArrayBufferViewObject *view);
 
     void prependToViews(ArrayBufferViewObject *viewsHead);
 
     void neuter();
 
     static void trace(JSTracer *trc, JSObject *obj);
+
+    uint8_t * dataPointer() {
+        return static_cast<uint8_t*>(getPrivate());
+    }
 };
 
 /*
  * TypedArrayObject
  *
  * The non-templated base class for the specific typed implementations.
  * This class holds all the member variables that are used by
  * the subclasses.
@@ -284,16 +288,19 @@ class TypedArrayObject : public ArrayBuf
   protected:
     // Typed array properties stored in slots, beyond those shared by all
     // ArrayBufferViews.
     static const size_t LENGTH_SLOT    = ArrayBufferViewObject::NUM_SLOTS;
     static const size_t TYPE_SLOT      = ArrayBufferViewObject::NUM_SLOTS + 1;
     static const size_t RESERVED_SLOTS = ArrayBufferViewObject::NUM_SLOTS + 2;
     static const size_t DATA_SLOT      = 7; // private slot, based on alloc kind
 
+    static_assert(js::detail::TypedArrayLengthSlot == LENGTH_SLOT,
+                  "bad inlined constant in jsfriendapi.h");
+
   public:
     static const Class classes[ScalarTypeRepresentation::TYPE_MAX];
     static const Class protoClasses[ScalarTypeRepresentation::TYPE_MAX];
 
     static bool obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
                                   MutableHandleObject objp, MutableHandleShape propp);
     static bool obj_lookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
                                    MutableHandleObject objp, MutableHandleShape propp);
@@ -332,16 +339,17 @@ class TypedArrayObject : public ArrayBuf
     uint32_t length() const {
         return lengthValue(const_cast<TypedArrayObject*>(this)).toInt32();
     }
 
     uint32_t type() const {
         return getFixedSlot(TYPE_SLOT).toInt32();
     }
     void *viewData() const {
+        // Keep synced with js::Get<Type>ArrayLengthAndData in jsfriendapi.h!
         return static_cast<void*>(getPrivate(DATA_SLOT));
     }
 
     inline bool isArrayIndex(jsid id, uint32_t *ip = NULL);
     void copyTypedArrayElement(uint32_t index, MutableHandleValue vp);
 
     void neuter();
 
@@ -428,16 +436,20 @@ class DataViewObject : public ArrayBuffe
 
   private:
     static const Class protoClass;
 
     static bool is(HandleValue v) {
         return v.isObject() && v.toObject().hasClass(&class_);
     }
 
+    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);
 
     template<Value ValueGetter(DataViewObject *view)>
     static bool
     getter(JSContext *cx, unsigned argc, Value *vp);
 
@@ -475,20 +487,16 @@ class DataViewObject : public ArrayBuffe
     ArrayBufferObject &arrayBuffer() const {
         return getReservedSlot(BUFFER_SLOT).toObject().as<ArrayBufferObject>();
     }
 
     static Value bufferValue(DataViewObject *view) {
         return view->hasBuffer() ? ObjectValue(view->arrayBuffer()) : UndefinedValue();
     }
 
-    void *dataPointer() const {
-        return getPrivate();
-    }
-
     static bool class_constructor(JSContext *cx, unsigned argc, Value *vp);
     static bool constructWithProto(JSContext *cx, unsigned argc, Value *vp);
     static bool construct(JSContext *cx, JSObject *bufobj, const CallArgs &args,
                           HandleObject proto);
 
     static inline DataViewObject *
     create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength,
            Handle<ArrayBufferObject*> arrayBuffer, JSObject *proto);
@@ -537,18 +545,16 @@ class DataViewObject : public ArrayBuffe
 
     static bool setFloat32Impl(JSContext *cx, CallArgs args);
     static bool fun_setFloat32(JSContext *cx, unsigned argc, Value *vp);
 
     static bool setFloat64Impl(JSContext *cx, CallArgs args);
     static bool fun_setFloat64(JSContext *cx, unsigned argc, Value *vp);
 
     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);
     template<typename NativeType>
     static bool write(JSContext *cx, Handle<DataViewObject*> obj,
                       CallArgs &args, const char *method);
 
     void neuter();
--- a/netwerk/base/src/ArrayBufferInputStream.cpp
+++ b/netwerk/base/src/ArrayBufferInputStream.cpp
@@ -84,16 +84,27 @@ ArrayBufferInputStream::ReadSegments(nsW
   NS_ASSERTION(result, "null ptr");
   NS_ASSERTION(mBufferLength >= mPos, "bad stream state");
 
   if (mClosed) {
     return NS_BASE_STREAM_CLOSED;
   }
 
   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;
   }
 
   if (aCount > remaining) {
     aCount = remaining;
   }
--- a/xpcom/io/nsBinaryStream.cpp
+++ b/xpcom/io/nsBinaryStream.cpp
@@ -12,29 +12,38 @@
  * produce architecture-neutral binary disk files, i.e. ones that can be read
  * and written by both big-endian and little-endian platforms.  Output is
  * written in big-endian order (high-order byte first), as this is traditional
  * network order.
  *
  * @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 "nsString.h"
 #include "nsISerializable.h"
 #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
 nsBinaryOutputStream::Flush() 
 { 
     NS_ENSURE_STATE(mOutputStream);
     return mOutputStream->Flush(); 
 }
@@ -714,32 +723,59 @@ nsBinaryInputStream::ReadByteArray(uint3
 
 NS_IMETHODIMP
 nsBinaryInputStream::ReadArrayBuffer(uint32_t aLength, const JS::Value& aBuffer, JSContext* cx)
 {
     if (!aBuffer.isObject()) {
         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());
+
+    uint32_t bufferLength = JS_GetArrayBufferByteLength(buffer);
+    if (bufferLength < aLength) {
+        return NS_ERROR_FAILURE;
+    }
+
+    char* data = reinterpret_cast<char*>(JS_GetArrayBufferData(buffer));
     if (!data) {
         return NS_ERROR_FAILURE;
     }
 
-    uint32_t bytesRead;
-    nsresult rv = Read(reinterpret_cast<char*>(data), aLength, &bytesRead);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (bytesRead != aLength) {
-        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
 nsBinaryInputStream::ReadObject(bool aIsStrongRef, nsISupports* *aObject)
 {
     nsCID cid;
     nsIID iid;
     nsresult rv = ReadID(&cid);