Bug 999651, bug 995679, bug 1009952, bug 1011007, bug 991981. r=sfink, r=shu, r=jandem, r=jdm, r=luke, r=bbouvier, r=nmatsakis, r=bz, r=ehsan, r=jgilbert, r=smaug, r=sicking, r=terrence, r=bholley, r=bent, r=efaust, r=jorendorff, a=lmandel
authorJeff Walden <jwalden@mit.edu>
Mon, 02 Jun 2014 11:25:43 -0700
changeset 148966 8c406adf76a0
parent 148965 a8859c69b2a5
child 148967 5917a170c6bd
push id257
push userjwalden@mit.edu
push dateMon, 02 Jun 2014 18:27:19 +0000
reviewerssfink, shu, jandem, jdm, luke, bbouvier, nmatsakis, bz, ehsan, jgilbert, smaug, sicking, terrence, bholley, bent, efaust, jorendorff, lmandel
bugs999651, 995679, 1009952, 1011007, 991981
milestone24.6.0esrpre
Bug 999651, bug 995679, bug 1009952, bug 1011007, bug 991981. r=sfink, r=shu, r=jandem, r=jdm, r=luke, r=bbouvier, r=nmatsakis, r=bz, r=ehsan, r=jgilbert, r=smaug, r=sicking, r=terrence, r=bholley, r=bent, r=efaust, r=jorendorff, a=lmandel
CLOBBER
content/base/src/WebSocket.cpp
content/base/src/nsDOMDataChannel.cpp
content/base/src/nsDOMParser.cpp
content/base/src/nsXMLHttpRequest.cpp
content/canvas/src/CanvasRenderingContext2D.cpp
content/canvas/src/CanvasRenderingContext2D.h
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLContextGL.cpp
content/html/content/public/HTMLAudioElement.h
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/bindings/TypedArray.h
dom/encoding/TextDecoder.h
dom/workers/TextDecoder.h
js/src/builtin/TestingFunctions.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/MIR.h
js/src/js.msg
js/src/jsfriendapi.h
js/src/jstypedarray.cpp
js/src/jstypedarray.h
js/src/jstypedarrayinlines.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 902908 renamed js/src/ion to js/src/jit and required a clobber
\ No newline at end of file
+Bug 999651 et al. require a clobber for some unknown reason
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -1216,31 +1216,37 @@ WebSocket::Send(nsIDOMBlob* aData,
 }
 
 void
 WebSocket::Send(ArrayBuffer& aData,
                 ErrorResult& aRv)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
-  MOZ_ASSERT(sizeof(*aData.Data()) == 1);
+  aData.ComputeLengthAndData();
+
+  static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
+
   uint32_t len = aData.Length();
   char* data = reinterpret_cast<char*>(aData.Data());
 
   nsDependentCSubstring msgString(data, len);
   Send(nullptr, msgString, len, true, aRv);
 }
 
 void
 WebSocket::Send(ArrayBufferView& aData,
                 ErrorResult& aRv)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
-  MOZ_ASSERT(sizeof(*aData.Data()) == 1);
+  aData.ComputeLengthAndData();
+
+  static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
+
   uint32_t len = aData.Length();
   char* data = reinterpret_cast<char*>(aData.Data());
 
   nsDependentCSubstring msgString(data, len);
   Send(nullptr, msgString, len, true, aRv);
 }
 
 void
--- a/content/base/src/nsDOMDataChannel.cpp
+++ b/content/base/src/nsDOMDataChannel.cpp
@@ -287,30 +287,36 @@ nsDOMDataChannel::Send(nsIDOMBlob* aData
   Send(msgStream, EmptyCString(), msgLength, true, aRv);
 }
 
 void
 nsDOMDataChannel::Send(ArrayBuffer& aData, ErrorResult& aRv)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
-  MOZ_ASSERT(sizeof(*aData.Data()) == 1);
+  aData.ComputeLengthAndData();
+
+  static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
+
   uint32_t len = aData.Length();
   char* data = reinterpret_cast<char*>(aData.Data());
 
   nsDependentCSubstring msgString(data, len);
   Send(nullptr, msgString, len, true, aRv);
 }
 
 void
 nsDOMDataChannel::Send(ArrayBufferView& aData, ErrorResult& aRv)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
-  MOZ_ASSERT(sizeof(*aData.Data()) == 1);
+  aData.ComputeLengthAndData();
+
+  static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
+
   uint32_t len = aData.Length();
   char* data = reinterpret_cast<char*>(aData.Data());
 
   nsDependentCSubstring msgString(data, len);
   Send(nullptr, msgString, len, true, aRv);
 }
 
 void
--- a/content/base/src/nsDOMParser.cpp
+++ b/content/base/src/nsDOMParser.cpp
@@ -132,16 +132,18 @@ nsDOMParser::ParseFromBuffer(const Seque
   nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
   return document.forget();
 }
 
 already_AddRefed<nsIDocument>
 nsDOMParser::ParseFromBuffer(const Uint8Array& aBuf, uint32_t aBufLen,
                              SupportedType aType, ErrorResult& rv)
 {
+  aBuf.ComputeLengthAndData();
+
   if (aBufLen > aBuf.Length()) {
     rv.Throw(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY);
     return nullptr;
   }
   nsCOMPtr<nsIDOMDocument> domDocument;
   rv = nsDOMParser::ParseFromBuffer(aBuf.Data(), aBufLen,
                                     StringFromSupportedType(aType),
                                     getter_AddRefs(domDocument));
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2435,16 +2435,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
@@ -2478,24 +2479,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
@@ -3554,43 +3554,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;
@@ -3633,29 +3633,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
@@ -16,16 +16,17 @@
 #include "CanvasUtils.h"
 #include "gfxFont.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/UnionTypes.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;
 }
@@ -438,17 +439,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
@@ -580,128 +580,139 @@ public:
     void Uniform1f(WebGLUniformLocation* location, WebGLfloat x);
     void Uniform2f(WebGLUniformLocation* location, WebGLfloat x, WebGLfloat y);
     void Uniform3f(WebGLUniformLocation* location, WebGLfloat x, WebGLfloat y,
                    WebGLfloat z);
     void Uniform4f(WebGLUniformLocation* location, WebGLfloat x, WebGLfloat y,
                    WebGLfloat z, WebGLfloat w);
     
     void Uniform1iv(WebGLUniformLocation* location, dom::Int32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform1iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform1iv(WebGLUniformLocation* location,
                     const dom::Sequence<WebGLint>& arr) {
         Uniform1iv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform1iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const WebGLint* data);
 
     void Uniform2iv(WebGLUniformLocation* location, dom::Int32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform2iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform2iv(WebGLUniformLocation* location,
                     const dom::Sequence<WebGLint>& arr) {
         Uniform2iv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform2iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const WebGLint* data);
 
     void Uniform3iv(WebGLUniformLocation* location, dom::Int32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform3iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform3iv(WebGLUniformLocation* location,
                     const dom::Sequence<WebGLint>& arr) {
         Uniform3iv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform3iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const WebGLint* data);
     
     void Uniform4iv(WebGLUniformLocation* location, dom::Int32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform4iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform4iv(WebGLUniformLocation* location,
                     const dom::Sequence<WebGLint>& arr) {
         Uniform4iv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform4iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const WebGLint* data);
 
     void Uniform1fv(WebGLUniformLocation* location, dom::Float32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform1fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform1fv(WebGLUniformLocation* location,
                     const dom::Sequence<WebGLfloat>& arr) {
         Uniform1fv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform1fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const WebGLfloat* data);
 
     void Uniform2fv(WebGLUniformLocation* location, dom::Float32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform2fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform2fv(WebGLUniformLocation* location,
                     const dom::Sequence<WebGLfloat>& arr) {
         Uniform2fv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform2fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const WebGLfloat* data);
 
     void Uniform3fv(WebGLUniformLocation* location, dom::Float32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform3fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform3fv(WebGLUniformLocation* location,
                     const dom::Sequence<WebGLfloat>& arr) {
         Uniform3fv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform3fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const WebGLfloat* data);
     
     void Uniform4fv(WebGLUniformLocation* location, dom::Float32Array& arr) {
+        arr.ComputeLengthAndData();
         Uniform4fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform4fv(WebGLUniformLocation* location,
                     const dom::Sequence<WebGLfloat>& arr) {
         Uniform4fv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform4fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const WebGLfloat* data);
 
     void UniformMatrix2fv(WebGLUniformLocation* location,
                           WebGLboolean transpose,
                           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,
                           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,
                           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());
     }
@@ -726,43 +737,47 @@ public:
     void VertexAttrib1f(WebGLuint index, WebGLfloat x0);
     void VertexAttrib2f(WebGLuint index, WebGLfloat x0, WebGLfloat x1);
     void VertexAttrib3f(WebGLuint index, WebGLfloat x0, WebGLfloat x1,
                         WebGLfloat x2);
     void VertexAttrib4f(WebGLuint index, WebGLfloat x0, WebGLfloat x1,
                         WebGLfloat x2, WebGLfloat x3);
 
     void VertexAttrib1fv(WebGLuint idx, dom::Float32Array &arr) {
+        arr.ComputeLengthAndData();
         VertexAttrib1fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib1fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) {
         VertexAttrib1fv_base(idx, arr.Length(), arr.Elements());
     }
     void VertexAttrib1fv_base(WebGLuint idx, uint32_t arrayLength,
                               const WebGLfloat* ptr);
 
     void VertexAttrib2fv(WebGLuint idx, dom::Float32Array &arr) {
+        arr.ComputeLengthAndData();
         VertexAttrib2fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib2fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) {
         VertexAttrib2fv_base(idx, arr.Length(), arr.Elements());
     }
     void VertexAttrib2fv_base(WebGLuint idx, uint32_t arrayLength,
                               const WebGLfloat* ptr);
 
     void VertexAttrib3fv(WebGLuint idx, dom::Float32Array &arr) {
+        arr.ComputeLengthAndData();
         VertexAttrib3fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib3fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) {
         VertexAttrib3fv_base(idx, arr.Length(), arr.Elements());
     }
     void VertexAttrib3fv_base(WebGLuint idx, uint32_t arrayLength,
                               const WebGLfloat* ptr);
 
     void VertexAttrib4fv(WebGLuint idx, dom::Float32Array &arr) {
+        arr.ComputeLengthAndData();
         VertexAttrib4fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib4fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) {
         VertexAttrib4fv_base(idx, arr.Length(), arr.Elements());
     }
     void VertexAttrib4fv_base(WebGLuint idx, uint32_t arrayLength,
                               const WebGLfloat* ptr);
     
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -426,16 +426,18 @@ WebGLContext::BufferData(WebGLenum targe
         return;
 
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
     MakeContextCurrent();
     InvalidateCachedMinInUseAttribArrayLength();
 
+    data->ComputeLengthAndData();
+
     GLenum error = CheckedBufferData(target, data->Length(), data->Data(), usage);
 
     if (error) {
         GenerateWarning("bufferData generated error %s", ErrorName(error));
         return;
     }
 
     boundBuffer->SetByteLength(data->Length());
@@ -464,16 +466,18 @@ WebGLContext::BufferData(WebGLenum targe
         return;
 
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
     InvalidateCachedMinInUseAttribArrayLength();
     MakeContextCurrent();
 
+    data.ComputeLengthAndData();
+
     GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage);
     if (error) {
         GenerateWarning("bufferData generated error %s", ErrorName(error));
         return;
     }
 
     boundBuffer->SetByteLength(data.Length());
     if (!boundBuffer->ElementArrayCacheBufferData(data.Data(), data.Length())) {
@@ -504,16 +508,18 @@ WebGLContext::BufferSubData(GLenum targe
     }
 
     if (byteOffset < 0)
         return ErrorInvalidValue("bufferSubData: negative offset");
 
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
+    data->ComputeLengthAndData();
+
     CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + data->Length();
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length");
 
     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());
 
@@ -542,16 +548,18 @@ WebGLContext::BufferSubData(WebGLenum ta
     }
 
     if (byteOffset < 0)
         return ErrorInvalidValue("bufferSubData: negative offset");
 
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferSubData: no buffer bound!");
 
+    data.ComputeLengthAndData();
+
     CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + data.Length();
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length");
 
     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());
 
@@ -3377,17 +3385,21 @@ WebGLContext::ReadPixels(WebGLint x, Web
     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->Obj());
+    // Compute length and data.  Don't reenter after this point, lest the
+    // precomputed go out of sync with the instant length/data.
+    pixels->ComputeLengthAndData();
+
+    uint32_t dataByteLen = pixels->Length();
     if (checked_neededByteLength.value() > dataByteLen)
         return ErrorInvalidOperation("readPixels: buffer too small");
 
     void* data = pixels->Data();
     if (!data) {
         ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?");
         return rv.Throw(NS_ERROR_OUT_OF_MEMORY);
     }
@@ -4579,16 +4591,18 @@ WebGLContext::CompressedTexImage2D(WebGL
         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);
@@ -4628,16 +4642,18 @@ WebGLContext::CompressedTexSubImage2D(We
         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)) {
@@ -5130,20 +5146,33 @@ void
 WebGLContext::TexImage2D(WebGLenum target, WebGLint level,
                          WebGLenum internalformat, WebGLsizei width,
                          WebGLsizei height, WebGLint border, WebGLenum format,
                          WebGLenum type, ArrayBufferView *pixels, ErrorResult& rv)
 {
     if (!IsContextStable())
         return;
 
+    void* data;
+    uint32_t length;
+    int jsArrayType;
+    if (!pixels) {
+        data = nullptr;
+        length = 0;
+        jsArrayType = -1;
+    } else {
+        pixels->ComputeLengthAndData();
+
+        data = pixels->Data();
+        length = pixels->Length();
+        jsArrayType = int(JS_GetArrayBufferViewType(pixels->Obj()));
+    }
+
     return TexImage2D_base(target, level, internalformat, width, height, 0, border, format, type,
-                           pixels ? pixels->Data() : 0,
-                           pixels ? pixels->Length() : 0,
-                           pixels ? (int)JS_GetArrayBufferViewType(pixels->Obj()) : -1,
+                           data, length, jsArrayType,
                            WebGLTexelConversions::Auto, false);
 }
 
 void
 WebGLContext::TexImage2D(WebGLenum target, WebGLint level,
                          WebGLenum internalformat, WebGLenum format,
                          WebGLenum type, ImageData* pixels, ErrorResult& rv)
 {
@@ -5151,16 +5180,18 @@ WebGLContext::TexImage2D(WebGLenum targe
         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
@@ -5284,16 +5315,18 @@ WebGLContext::TexSubImage2D(WebGLenum ta
                             ErrorResult& rv)
 {
     if (!IsContextStable())
         return;
 
     if (!pixels)
         return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
 
+    pixels->ComputeLengthAndData();
+
     return TexSubImage2D_base(target, level, xoffset, yoffset,
                               width, height, 0, format, type,
                               pixels->Data(), pixels->Length(),
                               JS_GetArrayBufferViewType(pixels->Obj()),
                               WebGLTexelConversions::Auto, false);
 }
 
 void
@@ -5304,16 +5337,18 @@ WebGLContext::TexSubImage2D(WebGLenum ta
 {
     if (!IsContextStable())
         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
@@ -58,16 +58,17 @@ public:
   static already_AddRefed<HTMLAudioElement> Audio(const GlobalObject& global,
                                                   const Optional<nsAString>& src,
                                                   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/webaudio/AnalyserNode.cpp
+++ b/content/media/webaudio/AnalyserNode.cpp
@@ -145,16 +145,18 @@ AnalyserNode::SetSmoothingTimeConstant(d
 void
 AnalyserNode::GetFloatFrequencyData(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(Uint8
 {
   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(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
@@ -164,28 +164,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();
   }
 
@@ -337,16 +339,19 @@ AudioContext::CreateBiquadFilter()
   return filterNode.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 =
@@ -364,16 +369,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
@@ -52,16 +52,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
@@ -242,16 +242,20 @@ BiquadFilterNode::SetType(BiquadFilterTy
                              static_cast<int32_t>(aType));
 }
 
 void
 BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
                                        Float32Array& aMagResponse,
                                        Float32Array& aPhaseResponse)
 {
+  aFrequencyHz.ComputeLengthAndData();
+  aMagResponse.ComputeLengthAndData();
+  aPhaseResponse.ComputeLengthAndData();
+
   uint32_t length = std::min(std::min(aFrequencyHz.Length(), aMagResponse.Length()),
                              aPhaseResponse.Length());
   if (!length) {
     return;
   }
 
   nsAutoArrayPtr<float> frequencies(new float[length]);
   float* frequencyHz = aFrequencyHz.Data();
--- a/content/media/webaudio/WaveShaperNode.cpp
+++ b/content/media/webaudio/WaveShaperNode.cpp
@@ -120,16 +120,18 @@ WaveShaperNode::WrapObject(JSContext *aC
 
 void
 WaveShaperNode::SetCurve(const Float32Array* aCurve)
 {
   nsTArray<float> curve;
   if (aCurve) {
     mCurve = aCurve->Obj();
 
+    aCurve->ComputeLengthAndData();
+
     curve.SetLength(aCurve->Length());
     PodCopy(curve.Elements(), aCurve->Data(), aCurve->Length());
   } else {
     mCurve = nullptr;
   }
 
   AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
   MOZ_ASSERT(ns, "Why don't we have a stream here?");
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -15,57 +15,85 @@ namespace dom {
 
 /*
  * 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 {
   TypedArray_base(JSObject* obj)
+    : mObj(obj),
+      mData(NULL),
+      mLength(0),
+      mComputed(false)
   {
-    mObj = UnboxArray(obj, &mLength, &mData);
+    MOZ_ASSERT(obj != NULL);
   }
 
 private:
-  T* mData;
-  uint32_t mLength;
   JSObject* mObj;
+  mutable T* mData;
+  mutable uint32_t mLength;
+  mutable bool mComputed;
 
 public:
+  inline bool Init(JSObject* obj)
+  {
+    MOZ_ASSERT(!inited());
+    DoInit(obj);
+    return inited();
+  }
+
   inline bool inited() const {
     return !!mObj;
   }
 
   inline T *Data() const {
-    MOZ_ASSERT(inited());
+    MOZ_ASSERT(mComputed);
     return mData;
   }
 
   inline uint32_t Length() const {
-    MOZ_ASSERT(inited());
+    MOZ_ASSERT(mComputed);
     return mLength;
   }
 
   inline JSObject *Obj() const {
     MOZ_ASSERT(inited());
     return mObj;
   }
+
+  inline void ComputeLengthAndData() const
+  {
+    MOZ_ASSERT(inited());
+    MOZ_ASSERT(!mComputed);
+    GetLengthAndData(mObj, &mLength, &mData);
+    mComputed = true;
+  }
+
+protected:
+  inline void DoInit(JSObject* obj)
+  {
+    mObj = UnwrapArray(obj);
+  }
 };
 
 
 template<typename T,
+         JSObject* UnwrapArray(JSObject*),
          T* GetData(JSObject*),
-         JSObject* UnboxArray(JSObject*, uint32_t*, T**),
+         void GetLengthAndData(JSObject*, uint32_t*, T**),
          JSObject* CreateNew(JSContext*, uint32_t)>
-struct TypedArray : public TypedArray_base<T,UnboxArray> {
+struct TypedArray : public TypedArray_base<T, UnwrapArray, GetLengthAndData> {
   TypedArray(JSObject* obj) :
-    TypedArray_base<T,UnboxArray>(obj)
+    TypedArray_base<T, UnwrapArray, GetLengthAndData>(obj)
   {}
 
   static inline JSObject*
   Create(JSContext* cx, nsWrapperCache* creator, uint32_t length,
          const T* data = NULL) {
     JS::Rooted<JSObject*> creatorWrapper(cx);
     Maybe<JSAutoCompartment> ac;
     if (creator && (creatorWrapper = creator->GetWrapperPreserveColor())) {
@@ -78,45 +106,45 @@ struct TypedArray : public TypedArray_ba
     if (data) {
       T* buf = static_cast<T*>(GetData(obj));
       memcpy(buf, data, length*sizeof(T));
     }
     return obj;
   }
 };
 
-typedef TypedArray<int8_t, JS_GetInt8ArrayData, JS_GetObjectAsInt8Array,
-                   JS_NewInt8Array>
+typedef TypedArray<int8_t, js::UnwrapInt8Array, JS_GetInt8ArrayData,
+                   js::GetInt8ArrayLengthAndData, JS_NewInt8Array>
         Int8Array;
-typedef TypedArray<uint8_t, JS_GetUint8ArrayData,
-                   JS_GetObjectAsUint8Array, JS_NewUint8Array>
+typedef TypedArray<uint8_t, js::UnwrapUint8Array, JS_GetUint8ArrayData,
+                   js::GetUint8ArrayLengthAndData, JS_NewUint8Array>
         Uint8Array;
-typedef TypedArray<uint8_t, JS_GetUint8ClampedArrayData,
-                   JS_GetObjectAsUint8ClampedArray, JS_NewUint8ClampedArray>
+typedef TypedArray<uint8_t, js::UnwrapUint8ClampedArray, JS_GetUint8ClampedArrayData,
+                   js::GetUint8ClampedArrayLengthAndData, JS_NewUint8ClampedArray>
         Uint8ClampedArray;
-typedef TypedArray<int16_t, JS_GetInt16ArrayData,
-                   JS_GetObjectAsInt16Array, JS_NewInt16Array>
+typedef TypedArray<int16_t, js::UnwrapInt16Array, JS_GetInt16ArrayData,
+                   js::GetInt16ArrayLengthAndData, JS_NewInt16Array>
         Int16Array;
-typedef TypedArray<uint16_t, JS_GetUint16ArrayData,
-                   JS_GetObjectAsUint16Array, JS_NewUint16Array>
+typedef TypedArray<uint16_t, js::UnwrapUint16Array, JS_GetUint16ArrayData,
+                   js::GetUint16ArrayLengthAndData, JS_NewUint16Array>
         Uint16Array;
-typedef TypedArray<int32_t, JS_GetInt32ArrayData,
-                   JS_GetObjectAsInt32Array, JS_NewInt32Array>
+typedef TypedArray<int32_t, js::UnwrapInt32Array, JS_GetInt32ArrayData,
+                   js::GetInt32ArrayLengthAndData, JS_NewInt32Array>
         Int32Array;
-typedef TypedArray<uint32_t, JS_GetUint32ArrayData,
-                   JS_GetObjectAsUint32Array, JS_NewUint32Array>
+typedef TypedArray<uint32_t, js::UnwrapUint32Array, JS_GetUint32ArrayData,
+                   js::GetUint32ArrayLengthAndData, JS_NewUint32Array>
         Uint32Array;
-typedef TypedArray<float, JS_GetFloat32ArrayData,
-                   JS_GetObjectAsFloat32Array, JS_NewFloat32Array>
+typedef TypedArray<float, js::UnwrapFloat32Array, JS_GetFloat32ArrayData,
+                   js::GetFloat32ArrayLengthAndData, JS_NewFloat32Array>
         Float32Array;
-typedef TypedArray<double, JS_GetFloat64ArrayData,
-                   JS_GetObjectAsFloat64Array, JS_NewFloat64Array>
+typedef TypedArray<double, js::UnwrapFloat64Array, JS_GetFloat64ArrayData,
+                   js::GetFloat64ArrayLengthAndData, JS_NewFloat64Array>
         Float64Array;
-typedef TypedArray_base<uint8_t, JS_GetObjectAsArrayBufferView>
+typedef TypedArray_base<uint8_t, js::UnwrapArrayBufferView, js::GetArrayBufferViewLengthAndData>
         ArrayBufferView;
-typedef TypedArray<uint8_t, JS_GetArrayBufferData,
-                   JS_GetObjectAsArrayBuffer, JS_NewArrayBuffer>
+typedef TypedArray<uint8_t, js::UnwrapArrayBuffer, JS_GetArrayBufferData,
+                   js::GetArrayBufferLengthAndData, JS_NewArrayBuffer>
         ArrayBuffer;
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_TypedArray_h */
--- a/dom/encoding/TextDecoder.h
+++ b/dom/encoding/TextDecoder.h
@@ -61,16 +61,17 @@ public:
     TextDecoderBase::Decode(nullptr, 0, false,
                             aOutDecodedString, aRv);
   }
 
   void Decode(const ArrayBufferView& aView,
               const TextDecodeOptions& aOptions,
               nsAString& aOutDecodedString,
               ErrorResult& aRv) {
+    aView.ComputeLengthAndData();
     TextDecoderBase::Decode(reinterpret_cast<char*>(aView.Data()),
                             aView.Length(), aOptions.mStream,
                             aOutDecodedString, aRv);
   }
 
 private:
   nsCOMPtr<nsISupports> mGlobal;
 };
--- a/dom/workers/TextDecoder.h
+++ b/dom/workers/TextDecoder.h
@@ -44,16 +44,17 @@ public:
                             aOutDecodedString, aRv);
   }
 
   void
   Decode(const ArrayBufferView& aView,
          const TextDecodeOptionsWorkers& aOptions,
          nsAString& aOutDecodedString,
          ErrorResult& aRv) {
+    aView.ComputeLengthAndData();
     TextDecoderBase::Decode(reinterpret_cast<char*>(aView.Data()),
                             aView.Length(), aOptions.mStream,
                             aOutDecodedString, aRv);
   }
 };
 
 END_WORKERS_NAMESPACE
 
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -994,16 +994,59 @@ JSBool
 js::IsAsmJSFunction(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().set(BooleanValue(false));
     return true;
 }
 #endif
 
+static JSBool
+Neuter(JSContext *cx, unsigned argc, jsval *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() != 2) {
+        JS_ReportError(cx, "wrong number of arguments to neuter()");
+        return false;
+    }
+
+    RootedObject obj(cx);
+    if (!JS_ValueToObject(cx, args[0], obj.address()))
+        return false;
+
+    if (!obj) {
+        JS_ReportError(cx, "neuter must be passed an object");
+        return false;
+    }
+
+    NeuterDataDisposition changeData;
+    RootedValue v(cx, args[1]);
+    RootedString str(cx, ToString<CanGC>(cx, v));
+    if (!str)
+        return false;
+    JSAutoByteString dataDisposition(cx, str);
+    if (!dataDisposition)
+        return false;
+    if (strcmp(dataDisposition.ptr(), "same-data") == 0) {
+        changeData = KeepData;
+    } else if (strcmp(dataDisposition.ptr(), "change-data") == 0) {
+        changeData = ChangeData;
+    } else {
+        JS_ReportError(cx, "unknown parameter 2 to neuter()");
+        return false;
+    }
+
+    if (!js::NeuterArrayBuffer(cx, obj, changeData))
+        return false;
+
+    args.rval().setUndefined();
+    return true;
+}
+
 static JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment')",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc."),
 
     JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
@@ -1172,16 +1215,23 @@ static JSFunctionSpecWithHelp TestingFun
     JS_FN_HELP("setObjectMetadata", SetObjectMetadata, 2, 0,
 "setObjectMetadata(obj, metadataObj)",
 "  Change the metadata for an object."),
 
     JS_FN_HELP("getObjectMetadata", GetObjectMetadata, 1, 0,
 "getObjectMetadata(obj)",
 "  Get the metadata for an object."),
 
+    JS_FN_HELP("neuter", Neuter, 1, 0,
+"neuter(buffer, \"change-data\"|\"same-data\")",
+"  Neuter the given ArrayBuffer object as if it had been transferred to a\n"
+"  WebWorker. \"change-data\" will update the internal data pointer.\n"
+"  \"same-data\" will leave it set to its original value, to mimic eg\n"
+"  asm.js ArrayBuffer neutering."),
+
     JS_FS_HELP_END
 };
 
 bool
 js::DefineTestingFunctions(JSContext *cx, HandleObject obj)
 {
     return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions);
 }
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6441,39 +6441,22 @@ IonBuilder::jsop_getelem_dense()
 
     current->push(load);
     return pushTypeBarrier(load, types, barrier);
 }
 
 MInstruction *
 IonBuilder::getTypedArrayLength(MDefinition *obj)
 {
-    if (obj->isConstant() && obj->toConstant()->value().isObject()) {
-        JSObject *array = &obj->toConstant()->value().toObject();
-        int32_t length = (int32_t) TypedArray::length(array);
-        obj->setFoldedUnchecked();
-        return MConstant::New(Int32Value(length));
-    }
     return MTypedArrayLength::New(obj);
 }
 
 MInstruction *
 IonBuilder::getTypedArrayElements(MDefinition *obj)
 {
-    if (obj->isConstant() && obj->toConstant()->value().isObject()) {
-        JSObject *array = &obj->toConstant()->value().toObject();
-        void *data = TypedArray::viewData(array);
-
-        // The 'data' pointer can change in rare circumstances
-        // (ArrayBufferObject::changeContents).
-        types::HeapTypeSet::WatchObjectStateChange(cx, array->getType(cx));
-
-        obj->setFoldedUnchecked();
-        return MConstantElements::New(data);
-    }
     return MTypedArrayElements::New(obj);
 }
 
 MDefinition *
 IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition *id,
                                                   ArrayBufferView::ViewType viewType)
 {
     if (!id->isRsh() || id->isEffectful())
@@ -6499,59 +6482,16 @@ IonBuilder::convertShiftToMaskForStaticT
 }
 
 bool
 IonBuilder::jsop_getelem_typed_static(bool *psucceeded)
 {
     if (!LIRGenerator::allowStaticTypedArrayAccesses())
         return true;
 
-    MDefinition *id = current->peek(-1);
-    MDefinition *obj = current->peek(-2);
-
-    if (ElementAccessHasExtraIndexedProperty(cx, obj))
-        return true;
-
-    if (!obj->resultTypeSet())
-        return true;
-    JSObject *typedArray = obj->resultTypeSet()->getSingleton();
-    if (!typedArray)
-        return true;
-    JS_ASSERT(typedArray->isTypedArray());
-
-    ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(typedArray);
-
-    MDefinition *ptr = convertShiftToMaskForStaticTypedArray(id, viewType);
-    if (!ptr)
-        return true;
-
-    obj->setFoldedUnchecked();
-
-    MLoadTypedArrayElementStatic *load = MLoadTypedArrayElementStatic::New(typedArray, ptr);
-    current->add(load);
-
-    // The load is infallible if an undefined result will be coerced to the
-    // appropriate numeric type if the read is out of bounds. The truncation
-    // analysis picks up some of these cases, but is incomplete with respect
-    // to others. For now, sniff the bytecode for simple patterns following
-    // the load which guarantee a truncation or numeric conversion.
-    if (viewType == ArrayBufferView::TYPE_FLOAT32 || viewType == ArrayBufferView::TYPE_FLOAT64) {
-        jsbytecode *next = pc + JSOP_GETELEM_LENGTH;
-        if (*next == JSOP_POS)
-            load->setInfallible();
-    } else {
-        jsbytecode *next = pc + JSOP_GETELEM_LENGTH;
-        if (*next == JSOP_ZERO && *(next + JSOP_ZERO_LENGTH) == JSOP_BITOR)
-            load->setInfallible();
-    }
-
-    current->popn(2);
-    current->push(load);
-
-    *psucceeded = true;
     return true;
 }
 
 bool
 IonBuilder::jsop_getelem_typed(int arrayType)
 {
     bool staticAccess = false;
     if (!jsop_getelem_typed_static(&staticAccess))
@@ -6849,47 +6789,17 @@ IonBuilder::jsop_setelem_dense(types::St
 
 bool
 IonBuilder::jsop_setelem_typed_static(MDefinition *obj, MDefinition *id, MDefinition *value,
                                       bool *psucceeded)
 {
     if (!LIRGenerator::allowStaticTypedArrayAccesses())
         return true;
 
-    if (ElementAccessHasExtraIndexedProperty(cx, obj))
-        return true;
-
-    if (!obj->resultTypeSet())
-        return true;
-    JSObject *typedArray = obj->resultTypeSet()->getSingleton();
-    if (!typedArray)
-        return true;
-    JS_ASSERT(typedArray->isTypedArray());
-
-    ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(typedArray);
-
-    MDefinition *ptr = convertShiftToMaskForStaticTypedArray(id, viewType);
-    if (!ptr)
-        return true;
-
-    obj->setFoldedUnchecked();
-
-    // Clamp value to [0, 255] for Uint8ClampedArray.
-    MDefinition *toWrite = value;
-    if (viewType == ArrayBufferView::TYPE_UINT8_CLAMPED) {
-        toWrite = MClampToUint8::New(value);
-        current->add(toWrite->toInstruction());
-    }
-
-    MInstruction *store = MStoreTypedArrayElementStatic::New(typedArray, ptr, toWrite);
-    current->add(store);
-    current->push(value);
-
-    *psucceeded = true;
-    return resumeAfter(store);
+    return true;
 }
 
 bool
 IonBuilder::jsop_setelem_typed(int arrayType,
                                SetElemSafety safety,
                                MDefinition *obj, MDefinition *id, MDefinition *value)
 {
     bool staticAccess = false;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -206,24 +206,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 {
@@ -4335,19 +4337,17 @@ class MTypedArrayLength
     }
     MDefinition *object() const {
         return getOperand(0);
     }
     bool congruentTo(MDefinition *const &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);
     }
 };
 
 // Load a typed array's elements vector.
 class MTypedArrayElements
   : public MUnaryInstruction,
     public SingleObjectPolicy
 {
--- 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,18 @@
  * 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 "jsclass.h"
 #include "jspubtd.h"
 #include "jsprvtd.h"
 
 #include "js/CallArgs.h"
 
 /*
  * This macro checks if the stack pointer has exceeded a given limit. If
@@ -1284,16 +1286,103 @@ JS_IsInt32Array(JSObject *obj);
 extern JS_FRIEND_API(JSBool)
 JS_IsUint32Array(JSObject *obj);
 extern JS_FRIEND_API(JSBool)
 JS_IsFloat32Array(JSObject *obj);
 extern JS_FRIEND_API(JSBool)
 JS_IsFloat64Array(JSObject *obj);
 
 /*
+ * Test for specific typed array types (ArrayBufferView subtypes) and return
+ * the unwrapped object if so, else nullptr.  Never throws.
+ */
+
+namespace js {
+
+extern JS_FRIEND_API(JSObject *)
+UnwrapInt8Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapUint8Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapUint8ClampedArray(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapInt16Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapUint16Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapInt32Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapUint32Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapFloat32Array(JSObject *obj);
+extern JS_FRIEND_API(JSObject *)
+UnwrapFloat64Array(JSObject *obj);
+
+extern JS_FRIEND_API(JSObject *)
+UnwrapArrayBuffer(JSObject *obj);
+
+extern JS_FRIEND_API(JSObject *)
+UnwrapArrayBufferView(JSObject *obj);
+
+namespace detail {
+
+extern JS_FRIEND_DATA(const Class* const) Int8ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Uint8ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Uint8ClampedArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Int16ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Uint16ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Int32ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Uint32ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Float32ArrayClassPtr;
+extern JS_FRIEND_DATA(const Class* const) Float64ArrayClassPtr;
+
+const size_t TypedArrayLengthSlot = 5;
+
+} // namespace detail
+
+/*
+ * Test for specific typed array types (ArrayBufferView subtypes) and return
+ * the unwrapped object if so, else nullptr.  Never throws.
+ */
+
+#define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Type, type) \
+inline void \
+Get ## Type ## ArrayLengthAndData(JSObject *obj, uint32_t *length, type **data) \
+{ \
+    JS_ASSERT(GetObjectClass(obj) == detail::Type ## ArrayClassPtr); \
+    const JS::Value &slot = GetReservedSlot(obj, detail::TypedArrayLengthSlot); \
+    *length = mozilla::SafeCast<uint32_t>(slot.toInt32()); \
+    *data = static_cast<type*>(GetObjectPrivate(obj)); \
+}
+
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int8, int8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8, uint8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8Clamped, uint8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int16, int16_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint16, uint16_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int32, int32_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint32, uint32_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float32, float)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float64, double)
+
+#undef JS_DEFINE_DATA_AND_LENGTH_ACCESSOR
+
+// This one isn't inlined because it's rather tricky (by dint of having to deal
+// with a dozen-plus classes and varying slot layouts.
+extern JS_FRIEND_API(void)
+GetArrayBufferViewLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data);
+
+// This one isn't inlined because there are a bunch of different ArrayBuffer
+// classes that would have to be individually handled here.
+extern JS_FRIEND_API(void)
+GetArrayBufferLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data);
+
+} // namespace js
+
+/*
  * Unwrap Typed arrays all at once. Return NULL without throwing if the object
  * cannot be viewed as the correct typed array, or the typed array object on
  * success, filling both outparameters.
  */
 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);
@@ -1441,16 +1530,38 @@ JS_GetArrayBufferViewData(JSObject *obj)
 /*
  * Return the ArrayBuffer underlying an ArrayBufferView. If the buffer has been
  * neutered, this will still return the neutered buffer. |obj| must be an
  * object that would return true for JS_IsArrayBufferViewObject().
  */
 extern JS_FRIEND_API(JSObject *)
 JS_GetArrayBufferViewBuffer(JSObject *obj);
 
+typedef enum {
+    ChangeData,
+    KeepData
+} NeuterDataDisposition;
+
+namespace js {
+
+/*
+ * Set an ArrayBuffer's length to 0 and neuter all of its views.
+ *
+ * The |changeData| argument is a hint to inform internal behavior with respect
+ * to the internal pointer to the ArrayBuffer's data after being neutered.
+ * There is no guarantee it will be respected.  But if it is respected, the
+ * ArrayBuffer's internal data pointer will, or will not, have changed
+ * accordingly.
+ */
+extern JS_FRIEND_API(bool)
+NeuterArrayBuffer(JSContext *cx, JS::HandleObject obj,
+                  NeuterDataDisposition changeData);
+
+} /* namespace js */
+
 /*
  * Check whether obj supports JS_GetDataView* APIs.
  */
 JS_FRIEND_API(JSBool)
 JS_IsDataViewObject(JSObject *obj);
 
 /*
  * Return the byte offset of a data view into its array buffer. |obj| must be a
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -572,19 +572,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, NULL, JSMSG_TYPE_ERR_BAD_ARGS);
+        return NULL;
+    }
+
     uint32_t length = end - begin;
 
     if (arrayBuffer.hasData())
         return create(cx, length, arrayBuffer.dataPointer() + begin);
 
     return create(cx, 0);
 }
 
@@ -615,32 +618,32 @@ ArrayBufferObject::createDataViewForThis
 JSBool
 ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args);
 }
 
 bool
-ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents,
-                                 uint8_t **data)
+ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, NeuterDataDisposition changeData,
+                                 void **contents, uint8_t **data)
 {
     MOZ_ASSERT(cx);
 
     ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
     JSObject *views = *GetViewList(&buffer);
 
     uint32_t byteLen = buffer.byteLength();
 
     js::ObjectElements *oldHeader = buffer.getElementsHeader();
     js::ObjectElements *newHeader;
 
     // If the ArrayBuffer's elements are transferrable, transfer ownership
     // directly.  Otherwise we have to copy the data into new elements.
-    bool stolen = buffer.hasStealableContents();
+    bool stolen = buffer.hasStealableContents() && changeData == ChangeData;
     if (stolen) {
         newHeader = AllocateArrayBufferContents(cx, byteLen, NULL);
         if (!newHeader)
             return false;
 
         *GetViewList(&buffer) = NULL;
         *contents = oldHeader;
         *data = buffer.dataPointer();
@@ -1185,23 +1188,25 @@ TypedArray::isArrayIndex(JSObject *obj, 
 bool
 js::IsDataView(JSObject* obj)
 {
     JS_ASSERT(obj);
     return obj->is<DataViewObject>();
 }
 
 void
-TypedArray::neuter(JSObject *tarray)
+TypedArray::neuter(JSObject *view)
 {
-    JS_ASSERT(tarray->isTypedArray());
-    tarray->setSlot(LENGTH_SLOT, Int32Value(0));
-    tarray->setSlot(BYTELENGTH_SLOT, Int32Value(0));
-    tarray->setSlot(BYTEOFFSET_SLOT, Int32Value(0));
-    tarray->setPrivate(NULL);
+    if (view->isTypedArray())
+        view->setSlot(LENGTH_SLOT, Int32Value(0));
+    else
+        MOZ_ASSERT(view->hasClass(&DataViewObject::class_));
+    view->setSlot(BYTELENGTH_SLOT, Int32Value(0));
+    view->setSlot(BYTEOFFSET_SLOT, Int32Value(0));
+    view->setPrivate(NULL);
 }
 
 JSBool
 TypedArray::obj_lookupGeneric(JSContext *cx, HandleObject tarray, HandleId id,
                               MutableHandleObject objp, MutableHandleShape propp)
 {
     JS_ASSERT(tarray->isTypedArray());
 
@@ -2018,30 +2023,36 @@ class TypedArrayTemplate
             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 = TypedArray::length(tarray);
-        if (!ToClampedIndex(cx, args[0], length, &srcBegin) ||
-            !ToClampedIndex(cx, args[1], length, &srcEnd) ||
-            !ToClampedIndex(cx, args[2], length, &dest) ||
+        uint32_t originalLength = TypedArray::length(tarray);
+        if (!ToClampedIndex(cx, args[0], originalLength, &srcBegin) ||
+            !ToClampedIndex(cx, args[1], originalLength, &srcEnd) ||
+            !ToClampedIndex(cx, args[2], originalLength, &dest) ||
             srcBegin > srcEnd)
         {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
+            return false;
+        }
+
+        if (srcBegin > srcEnd) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDEX);
             return false;
         }
 
+        uint32_t lengthDuringMove = TypedArray::length(tarray); // beware ToClampedIndex
         uint32_t nelts = srcEnd - srcBegin;
 
-        JS_ASSERT(dest + nelts >= dest);
-        if (dest + nelts > length) {
+        MOZ_ASSERT(dest <= INT32_MAX, "size limited to 2**31");
+        MOZ_ASSERT(nelts <= INT32_MAX, "size limited to 2**31");
+        if (dest + nelts > lengthDuringMove || srcEnd > lengthDuringMove) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return false;
         }
 
         uint32_t byteDest = dest * sizeof(NativeType);
         uint32_t byteSrc = srcBegin * sizeof(NativeType);
         uint32_t byteSize = nelts * sizeof(NativeType);
 
@@ -2109,18 +2120,17 @@ class TypedArrayTemplate
 
             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 > length(tarray) - offset) {
+            if (uint32_t(offset) > length(tarray) || len > length(tarray) - offset) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
                 return false;
             }
 
             if (!copyFromArray(cx, tarray, arg0, len, offset))
                 return false;
         }
 
@@ -2263,39 +2273,41 @@ class TypedArrayTemplate
         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*>(viewData(obj)) + index);
+        MOZ_ASSERT(index < length(obj));
+        return static_cast<const NativeType*>(viewData(obj))[index];
     }
 
     static void
     setIndex(JSObject *obj, uint32_t index, NativeType val)
     {
         *(static_cast<NativeType*>(viewData(obj)) + index) = val;
     }
 
     static void copyIndexToValue(JSObject *tarray, uint32_t index, MutableHandleValue vp);
 
     static JSObject *
     createSubarray(JSContext *cx, HandleObject tarray, uint32_t begin, uint32_t end)
     {
         JS_ASSERT(tarray);
 
-        JS_ASSERT(begin <= length(tarray));
-        JS_ASSERT(end <= length(tarray));
+        if (begin > length(tarray) || end > length(tarray) || begin > end) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDEX);
+            return NULL;
+        }
 
         RootedObject bufobj(cx, buffer(tarray));
         JS_ASSERT(bufobj);
 
-        JS_ASSERT(begin <= end);
         uint32_t length = end - begin;
 
         JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType));
         uint32_t arrayByteOffset = byteOffsetValue(tarray).toInt32();
         JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= arrayByteOffset);
         uint32_t byteOffset = arrayByteOffset + begin * sizeof(NativeType);
 
         return makeInstance(cx, bufobj, byteOffset, length);
@@ -2500,63 +2512,64 @@ class TypedArrayTemplate
 
         // 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, viewData(tarray), byteLength);
 
+        uint32_t len = length(tarray);
         switch (type(tarray)) {
           case TypedArray::TYPE_INT8: {
             int8_t *src = (int8_t*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_UINT8:
           case TypedArray::TYPE_UINT8_CLAMPED: {
             uint8_t *src = (uint8_t*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_INT16: {
             int16_t *src = (int16_t*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_UINT16: {
             uint16_t *src = (uint16_t*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_INT32: {
             int32_t *src = (int32_t*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_UINT32: {
             uint32_t *src = (uint32_t*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_FLOAT32: {
             float *src = (float*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           case TypedArray::TYPE_FLOAT64: {
             double *src = (double*) srcbuf;
-            for (unsigned i = 0; i < length(tarray); ++i)
+            for (unsigned i = 0; i < len; ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
           default:
             JS_NOT_REACHED("copyFromWithOverlap with a TypedArray of unknown type");
             break;
         }
 
@@ -2817,31 +2830,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
@@ -2919,21 +2928,27 @@ 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);
+    SkipRoot skipData(cx, &data);
+    if (!data)
+        return false;
+
     DataViewIO<NativeType>::fromBuffer(val, data, needToSwapBytes(fromLittleEndian));
     return true;
 }
 
 template <typename NativeType>
 static inline bool
 WebIDLCast(JSContext *cx, const Value &value, NativeType *out)
 {
@@ -2971,26 +2986,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()));
@@ -3444,17 +3464,29 @@ const JSFunctionSpec _typedArray::jsfunc
                                                         proto);                              \
   }                                                                                          \
   JS_FRIEND_API(JSBool) JS_Is ## Name ## Array(JSObject *obj)                                \
   {                                                                                          \
       if (!(obj = CheckedUnwrap(obj)))                                                 \
           return false;                                                                      \
       Class *clasp = obj->getClass();                                                        \
       return (clasp == &TypedArray::classes[TypedArrayTemplate<NativeType>::ArrayTypeID()]); \
-  }
+  } \
+  JS_FRIEND_API(JSObject *) js::Unwrap ## Name ## Array(JSObject *obj)                       \
+  {                                                                                          \
+      obj = CheckedUnwrap(obj);                                                              \
+      if (!obj)                                                                              \
+          return NULL;                                                                       \
+      const Class *clasp = obj->getClass();                                                  \
+      if (clasp == &TypedArray::classes[TypedArrayTemplate<NativeType>::ArrayTypeID()])      \
+          return obj;                                                                        \
+      return NULL;                                                                           \
+  } \
+  JS_FRIEND_DATA(const js::Class* const) js::detail::Name ## ArrayClassPtr =                 \
+      &js::TypedArray::classes[TypedArrayTemplate<NativeType>::ArrayTypeID()];
 
 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t)
 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8, uint8_t)
 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)
@@ -3913,30 +3945,48 @@ js::IsTypedArrayBuffer(const Value &v)
 
 JS_FRIEND_API(JSBool)
 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 : NULL;
+    return NULL;
+}
+
 JS_FRIEND_API(JSBool)
 JS_IsTypedArrayObject(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     return obj ? obj->isTypedArray() : false;
 }
 
 JS_FRIEND_API(JSBool)
 JS_IsArrayBufferViewObject(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     return obj ? (obj->isTypedArray() || obj->is<DataViewObject>()) : false;
 }
 
+JS_FRIEND_API(JSObject *)
+js::UnwrapArrayBufferView(JSObject *obj)
+{
+    if (JSObject *unwrapped = CheckedUnwrap(obj)) {
+        if (unwrapped->isTypedArray() || unwrapped->is<DataViewObject>())
+            return unwrapped;
+    }
+    return NULL;
+}
+
 JS_FRIEND_API(uint32_t)
 JS_GetArrayBufferByteLength(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     return obj ? obj->as<ArrayBufferObject>().byteLength() : 0;
 }
 
 JS_FRIEND_API(uint8_t *)
@@ -3946,16 +3996,34 @@ JS_GetArrayBufferData(JSObject *obj)
     if (!obj)
         return NULL;
     ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
     if (!buffer.uninlineData(NULL))
         return NULL;
     return buffer.dataPointer();
 }
 
+JS_FRIEND_API(bool)
+js::NeuterArrayBuffer(JSContext *cx, HandleObject obj,
+                      NeuterDataDisposition changeData)
+{
+    if (!obj->is<ArrayBufferObject>()) {
+        JS_ReportError(cx, "ArrayBuffer object required");
+        return false;
+    }
+
+    void *contents;
+    uint8_t *data;
+    if (!ArrayBufferObject::stealContents(cx, obj, changeData, &contents, &data))
+        return false;
+
+    JS_free(cx, contents);
+    return true;
+}
+
 JS_FRIEND_API(JSObject *)
 JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes)
 {
     JS_ASSERT(nbytes <= INT32_MAX);
     return ArrayBufferObject::create(cx, nbytes);
 }
 
 JS_PUBLIC_API(JSObject *)
@@ -4005,17 +4073,17 @@ JS_StealArrayBufferContents(JSContext *c
     if (!(obj = CheckedUnwrap(obj)))
         return false;
 
     if (!obj->is<ArrayBufferObject>()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
         return false;
     }
 
-    if (!ArrayBufferObject::stealContents(cx, obj, contents, data))
+    if (!ArrayBufferObject::stealContents(cx, obj, ChangeData, contents, data))
         return false;
 
     return true;
 }
 
 JS_FRIEND_API(uint32_t)
 JS_GetTypedArrayLength(JSObject *obj)
 {
@@ -4224,16 +4292,28 @@ JS_GetArrayBufferViewByteLength(JSObject
     if (!obj)
         return 0;
     JS_ASSERT(obj->isTypedArray() || obj->is<DataViewObject>());
     return obj->is<DataViewObject>()
            ? obj->as<DataViewObject>().byteLength()
            : TypedArray::byteLengthValue(obj).toInt32();
 }
 
+JS_FRIEND_API(void)
+js::GetArrayBufferViewLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data)
+{
+    MOZ_ASSERT(obj->is<DataViewObject>() || obj->isTypedArray());
+
+    *length = obj->is<DataViewObject>()
+              ? obj->as<DataViewObject>().byteLength()
+              : TypedArray::byteLength(obj);
+
+    *data = static_cast<uint8_t*>(obj->getPrivate());
+}
+
 JS_FRIEND_API(JSObject *)
 JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data)
 {
     if (!(obj = CheckedUnwrap(obj)))
         return NULL;
     if (!(obj->isTypedArray() || obj->is<DataViewObject>()))
         return NULL;
 
@@ -4254,8 +4334,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/jstypedarray.h
+++ b/js/src/jstypedarray.h
@@ -4,16 +4,17 @@
  * 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 jstypedarray_h
 #define jstypedarray_h
 
 #include "jsapi.h"
 #include "jsclass.h"
+#include "jsfriendapi.h"
 #include "jsobj.h"
 
 #include "gc/Barrier.h"
 
 typedef struct JSProperty JSProperty;
 
 namespace js {
 
@@ -150,18 +151,18 @@ class ArrayBufferObject : public JSObjec
         // Neutered contents aren't transferrable because we want a neutered
         // array's contents to be backed by zeroed memory equal in length to
         // the original buffer contents.  Transferring these contents would
         // allocate new ones based on the current byteLength, which is 0 for a
         // neutered array -- not the original byteLength.
         return !isNeutered();
     }
 
-    static bool stealContents(JSContext *cx, JSObject *obj, void **contents,
-                              uint8_t **data);
+    static bool stealContents(JSContext *cx, JSObject *obj, NeuterDataDisposition changeData,
+                              void **contents, uint8_t **data);
 
     static inline void setElementsHeader(js::ObjectElements *header, uint32_t bytes);
     static inline uint32_t getElementsHeaderInitializedLength(const js::ObjectElements *header);
 
     void addView(JSObject *view);
 
     bool allocateSlots(JSContext *cx, uint32_t size, uint8_t *contents = NULL);
     void changeContents(JSContext *cx, ObjectElements *newHeader);
@@ -303,31 +304,34 @@ struct TypedArray : public BufferView {
     static inline uint32_t length(JSObject *obj);
 
     static inline uint32_t type(JSObject *obj);
     static inline void * viewData(JSObject *obj);
 
   public:
     static bool isArrayIndex(JSObject *obj, jsid id, uint32_t *ip = NULL);
 
-    static void neuter(JSObject *tarray);
+    static void neuter(JSObject *view);
 
     static inline uint32_t slotWidth(int atype);
     static inline int slotWidth(JSObject *obj);
 
     /*
      * Byte length above which created typed arrays and data views will have
      * singleton types regardless of the context in which they are created.
      */
     static const uint32_t SINGLETON_TYPE_BYTE_LENGTH = 1024 * 1024 * 10;
 
     static int lengthOffset();
     static int dataOffset();
 };
 
+MOZ_STATIC_ASSERT(js::detail::TypedArrayLengthSlot == TypedArray::LENGTH_SLOT,
+                  "bad inlined constant in jsfriendapi.h");
+
 inline bool
 IsTypedArrayClass(const Class *clasp)
 {
     return &TypedArray::classes[0] <= clasp &&
            clasp < &TypedArray::classes[TypedArray::TYPE_MAX];
 }
 
 inline bool
@@ -371,16 +375,20 @@ class DataViewObject : public JSObject, 
 public:
     static Class class_;
 
 private:
     static Class protoClass;
 
     static inline bool is(const Value &v);
 
+    template <typename NativeType>
+    static uint8_t *
+    getDataPointer(JSContext *cx, Handle<DataViewObject*> obj, uint32_t offset);
+
     template<Value ValueGetter(DataViewObject &view)>
     static bool
     getterImpl(JSContext *cx, CallArgs args);
 
     template<Value ValueGetter(DataViewObject &view)>
     static JSBool
     getter(JSContext *cx, unsigned argc, Value *vp);
 
@@ -454,18 +462,16 @@ private:
     static JSBool fun_setFloat64(JSContext *cx, unsigned argc, Value *vp);
 
     inline uint32_t byteLength();
     inline uint32_t byteOffset();
     inline ArrayBufferObject & arrayBuffer();
     inline void *dataPointer();
     inline bool hasBuffer() const;
     static JSObject *initClass(JSContext *cx);
-    static bool getDataPointer(JSContext *cx, Handle<DataViewObject*> obj,
-                               CallArgs args, size_t typeSize, uint8_t **data);
     template<typename NativeType>
     static bool read(JSContext *cx, Handle<DataViewObject*> obj,
                      CallArgs &args, NativeType *val, const char *method);
     template<typename NativeType>
     static bool write(JSContext *cx, Handle<DataViewObject*> obj,
                       CallArgs &args, const char *method);
   private:
     static const JSFunctionSpec jsfuncs[];
--- a/js/src/jstypedarrayinlines.h
+++ b/js/src/jstypedarrayinlines.h
@@ -124,16 +124,17 @@ TypedArray::buffer(JSObject *obj)
 {
     return &bufferValue(obj).toObject().as<ArrayBufferObject>();
 }
 
 inline void *
 TypedArray::viewData(JSObject *obj)
 {
     JS_ASSERT(obj->isTypedArray());
+    // Keep synced with js::Get<Type>ArrayLengthAndData in jsfriendapi.h!
     return (void *)obj->getPrivate(DATA_SLOT);
 }
 
 inline uint32_t
 TypedArray::slotWidth(int atype) {
     switch (atype) {
     case js::TypedArray::TYPE_INT8:
     case js::TypedArray::TYPE_UINT8:
@@ -217,16 +218,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 = proto->getNewType(cx, &class_);
         if (!type)
--- a/netwerk/base/src/ArrayBufferInputStream.cpp
+++ b/netwerk/base/src/ArrayBufferInputStream.cpp
@@ -83,16 +83,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,30 +12,39 @@
  * 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 "prlong.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(); 
 }
@@ -715,32 +724,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);