Backed out de9fff3a523, c567df2244f5, 8e3d976d5dc5, and c7499faaec23 (bug 800915) for bustage.
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 12 Nov 2012 21:43:39 -0500
changeset 113056 f60b494448f881a35c5a1d7806bdd2aabb8d436d
parent 113055 cd854e9fe7c1261507c8c0de712bdddbdb1898f4
child 113057 359b234648375ba78bc4135ef18a314f1a65c310
push id23848
push useremorley@mozilla.com
push dateTue, 13 Nov 2012 16:29:34 +0000
treeherdermozilla-central@d56d537a1843 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs800915
milestone19.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out de9fff3a523, c567df2244f5, 8e3d976d5dc5, and c7499faaec23 (bug 800915) for bustage.
content/base/src/nsContentUtils.cpp
content/base/src/nsDOMBlobBuilder.cpp
content/base/src/nsDOMDataChannel.cpp
content/base/src/nsDOMFileReader.cpp
content/base/src/nsXMLHttpRequest.cpp
content/canvas/src/CanvasRenderingContext2D.cpp
content/canvas/src/WebGLContextGL.cpp
content/events/src/nsDOMNotifyAudioAvailableEvent.cpp
content/html/content/src/nsHTMLAudioElement.cpp
dom/base/nsGlobalWindow.cpp
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/TypedArray.h
dom/file/LockedFile.cpp
dom/network/src/TCPSocketChild.cpp
dom/network/src/TCPSocketParent.cpp
dom/plugins/base/nsJSNPRuntime.cpp
dom/system/gonk/SystemWorkerManager.cpp
dom/workers/FileReaderSync.cpp
dom/workers/ImageData.cpp
dom/workers/XMLHttpRequest.cpp
js/src/ctypes/CTypes.cpp
js/src/jsapi-tests/testArrayBuffer.cpp
js/src/jsapi-tests/testTypedArrays.cpp
js/src/jsclone.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jstypedarray.cpp
js/src/jswrapper.cpp
js/src/jswrapper.h
js/src/vm/Debugger.cpp
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCConvert.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/wrappers/AccessCheck.cpp
js/xpconnect/wrappers/AccessCheck.h
js/xpconnect/wrappers/FilteringWrapper.cpp
js/xpconnect/wrappers/WrapperFactory.cpp
js/xpconnect/wrappers/XrayWrapper.cpp
js/xpconnect/wrappers/XrayWrapper.h
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -6182,18 +6182,18 @@ nsContentUtils::CreateArrayBuffer(JSCont
 
   int32_t dataLen = aData.Length();
   *aResult = JS_NewArrayBuffer(aCx, dataLen);
   if (!*aResult) {
     return NS_ERROR_FAILURE;
   }
 
   if (dataLen > 0) {
-    NS_ASSERTION(JS_IsArrayBufferObject(*aResult), "What happened?");
-    memcpy(JS_GetArrayBufferData(*aResult), aData.BeginReading(), dataLen);
+    NS_ASSERTION(JS_IsArrayBufferObject(*aResult, aCx), "What happened?");
+    memcpy(JS_GetArrayBufferData(*aResult, aCx), aData.BeginReading(), dataLen);
   }
 
   return NS_OK;
 }
 
 // Initial implementation: only stores to RAM, not file
 // TODO: bug 704447: large file support
 nsresult
--- a/content/base/src/nsDOMBlobBuilder.cpp
+++ b/content/base/src/nsDOMBlobBuilder.cpp
@@ -229,22 +229,22 @@ nsDOMMultipartFile::InitInternal(JSConte
               subBlobs = file->GetSubBlobs();
           if (subBlobs) {
             blobSet.AppendBlobs(*subBlobs);
           } else {
             blobSet.AppendBlob(blob);
           }
           continue;
         }
-        if (JS_IsArrayBufferViewObject(&obj)) {
-          blobSet.AppendVoidPtr(JS_GetArrayBufferViewData(&obj),
-                                JS_GetArrayBufferViewByteLength(&obj));
+        if (JS_IsArrayBufferViewObject(&obj, aCx)) {
+          blobSet.AppendVoidPtr(JS_GetArrayBufferViewData(&obj, aCx),
+                                JS_GetArrayBufferViewByteLength(&obj, aCx));
           continue;
         }
-        if (JS_IsArrayBufferObject(&obj)) {
+        if (JS_IsArrayBufferObject(&obj, aCx)) {
           blobSet.AppendArrayBuffer(&obj, aCx);
           continue;
         }
         // neither Blob nor ArrayBuffer(View)
       } else if (element.isString()) {
         blobSet.AppendString(element.toString(), nativeEOL, aCx);
         continue;
       }
@@ -316,11 +316,11 @@ BlobSet::AppendBlobs(const nsTArray<nsCO
   mBlobs.AppendElements(aBlob);
 
   return NS_OK;
 }
 
 nsresult
 BlobSet::AppendArrayBuffer(JSObject* aBuffer, JSContext *aCx)
 {
-  return AppendVoidPtr(JS_GetArrayBufferData(aBuffer),
-                       JS_GetArrayBufferByteLength(aBuffer));
+  return AppendVoidPtr(JS_GetArrayBufferData(aBuffer, aCx),
+                       JS_GetArrayBufferByteLength(aBuffer, aCx));
 }
--- a/content/base/src/nsDOMDataChannel.cpp
+++ b/content/base/src/nsDOMDataChannel.cpp
@@ -312,19 +312,19 @@ nsDOMDataChannel::GetSendParams(nsIVaria
     nsMemory::Free(iid);
 
     // ArrayBuffer?
     jsval realVal;
     JSObject* obj;
     nsresult rv = aData->GetAsJSVal(&realVal);
     if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(realVal) &&
         (obj = JSVAL_TO_OBJECT(realVal)) &&
-        (JS_IsArrayBufferObject(obj))) {
-      int32_t len = JS_GetArrayBufferByteLength(obj);
-      char* data = reinterpret_cast<char*>(JS_GetArrayBufferData(obj));
+        (JS_IsArrayBufferObject(obj, aCx))) {
+      int32_t len = JS_GetArrayBufferByteLength(obj, aCx);
+      char* data = reinterpret_cast<char*>(JS_GetArrayBufferData(obj, aCx));
 
       aStringOut.Assign(data, len);
       aIsBinary = true;
       aOutgoingLength = len;
       return NS_OK;
     }
 
     // Blob?
--- a/content/base/src/nsDOMFileReader.cpp
+++ b/content/base/src/nsDOMFileReader.cpp
@@ -304,17 +304,17 @@ nsDOMFileReader::DoOnDataAvailable(nsIRe
 
     uint32_t bytesRead = 0;
     aInputStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
                                &bytesRead);
     NS_ASSERTION(bytesRead == aCount, "failed to read data");
   }
   else if (mDataFormat == FILE_AS_ARRAYBUFFER) {
     uint32_t bytesRead = 0;
-    aInputStream->Read((char*)JS_GetArrayBufferData(mResultArrayBuffer) + aOffset,
+    aInputStream->Read((char*)JS_GetArrayBufferData(mResultArrayBuffer, NULL) + aOffset,
                        aCount, &bytesRead);
     NS_ASSERTION(bytesRead == aCount, "failed to read data");
   }
   else {
     //Update memory buffer to reflect the contents of the file
     if (aOffset + aCount > UINT32_MAX) {
       // PR_Realloc doesn't support over 4GB memory size even if 64-bit OS
       return NS_ERROR_OUT_OF_MEMORY;
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2664,18 +2664,18 @@ GetRequestBody(nsIVariant* aBody, nsIInp
         return NS_ERROR_FAILURE;
       }
     }
 
     nsresult rv = aBody->GetAsJSVal(&realVal);
     if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(realVal)) {
       JSObject *obj = JSVAL_TO_OBJECT(realVal);
       ac.construct(cx, obj);
-      if (JS_IsArrayBufferObject(obj)) {
-          ArrayBuffer buf(obj);
+      if (JS_IsArrayBufferObject(obj, cx)) {
+          ArrayBuffer buf(cx, obj);
           return GetRequestBody(&buf, aResult, aContentLength, aContentType, aCharset);
       }
     }
   }
   else if (dataType == nsIDataType::VTYPE_VOID ||
            dataType == nsIDataType::VTYPE_EMPTY) {
     // Makes us act as if !aBody, don't upload anything
     aContentType.AssignLiteral("text/plain");
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -4316,17 +4316,17 @@ CanvasRenderingContext2D::GetImageDataAr
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (mZero) {
     *aRetval = darray;
     return NS_OK;
   }
 
-  uint8_t* data = JS_GetUint8ClampedArrayData(darray);
+  uint8_t* data = JS_GetUint8ClampedArrayData(darray, aCx);
 
   IntRect srcRect(0, 0, mWidth, mHeight);
   IntRect destRect(aX, aY, aWidth, aHeight);
 
   IntRect srcReadRect = srcRect.Intersect(destRect);
   IntRect dstWriteRect = srcReadRect;
   dstWriteRect.MoveBy(-aX, -aY);
 
@@ -4428,17 +4428,17 @@ CanvasRenderingContext2D::PutImageData(J
                                        ImageData& imageData, double dx,
                                        double dy, ErrorResult& error)
 {
   if (!FloatValidate(dx, dy)) {
     error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
 
-  dom::Uint8ClampedArray arr(imageData.GetDataObject());
+  dom::Uint8ClampedArray arr(cx, imageData.GetDataObject());
 
   error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
                                 imageData.Width(), imageData.Height(),
                                 arr.Data(), arr.Length(), false, 0, 0, 0, 0);
 }
 
 void
 CanvasRenderingContext2D::PutImageData(JSContext* cx,
@@ -4448,17 +4448,17 @@ CanvasRenderingContext2D::PutImageData(J
                                        double dirtyHeight,
                                        ErrorResult& error)
 {
   if (!FloatValidate(dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight)) {
     error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
 
-  dom::Uint8ClampedArray arr(imageData.GetDataObject());
+  dom::Uint8ClampedArray arr(cx, imageData.GetDataObject());
 
   error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
                                 imageData.Width(), imageData.Height(),
                                 arr.Data(), arr.Length(), true,
                                 JS_DoubleToInt32(dirtyX),
                                 JS_DoubleToInt32(dirtyY),
                                 JS_DoubleToInt32(dirtyWidth),
                                 JS_DoubleToInt32(dirtyHeight));
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -3152,18 +3152,18 @@ WebGLContext::ReadPixels(WebGLint x, Web
     if (!pixels)
         return ErrorInvalidValue("readPixels: null destination buffer");
 
     const WebGLRectangleObject *framebufferRect = FramebufferRectangleObject();
     WebGLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
     WebGLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
 
     void* data = pixels->Data();
-    uint32_t dataByteLen = JS_GetTypedArrayByteLength(pixels->Obj());
-    int dataType = JS_GetTypedArrayType(pixels->Obj());
+    uint32_t dataByteLen = JS_GetTypedArrayByteLength(pixels->Obj(), NULL);
+    int dataType = JS_GetTypedArrayType(pixels->Obj(), NULL);
 
     uint32_t channels = 0;
 
     // Check the format param
     switch (format) {
         case LOCAL_GL_ALPHA:
             channels = 1;
             break;
@@ -4868,34 +4868,34 @@ WebGLContext::TexImage2D(JSContext* cx, 
                          WebGLenum type, ArrayBufferView *pixels, ErrorResult& rv)
 {
     if (!IsContextStable())
         return;
 
     return TexImage2D_base(target, level, internalformat, width, height, 0, border, format, type,
                            pixels ? pixels->Data() : 0,
                            pixels ? pixels->Length() : 0,
-                           pixels ? (int)JS_GetTypedArrayType(pixels->Obj()) : -1,
+                           pixels ? (int)JS_GetTypedArrayType(pixels->Obj(), cx) : -1,
                            WebGLTexelConversions::Auto, false);
 }
 
 void
 WebGLContext::TexImage2D(JSContext* cx, WebGLenum target, WebGLint level,
                          WebGLenum internalformat, WebGLenum format,
                          WebGLenum type, ImageData* pixels, ErrorResult& rv)
 {
     if (!IsContextStable())
         return;
 
     if (!pixels) {
         // Spec says to generate an INVALID_VALUE error
         return ErrorInvalidValue("texImage2D: null ImageData");
     }
     
-    Uint8ClampedArray arr(pixels->GetDataObject());
+    Uint8ClampedArray arr(cx, pixels->GetDataObject());
     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
@@ -5022,33 +5022,33 @@ WebGLContext::TexSubImage2D(JSContext* c
         return;
 
     if (!pixels)
         return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
 
     return TexSubImage2D_base(target, level, xoffset, yoffset,
                               width, height, 0, format, type,
                               pixels->Data(), pixels->Length(),
-                              JS_GetTypedArrayType(pixels->Obj()),
+                              JS_GetTypedArrayType(pixels->Obj(), cx),
                               WebGLTexelConversions::Auto, false);
 }
 
 void
 WebGLContext::TexSubImage2D(JSContext* cx, WebGLenum target, WebGLint level,
                             WebGLint xoffset, WebGLint yoffset,
                             WebGLenum format, WebGLenum type, ImageData* pixels,
                             ErrorResult& rv)
 {
     if (!IsContextStable())
         return;
 
     if (!pixels)
         return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
 
-    Uint8ClampedArray arr(pixels->GetDataObject());
+    Uint8ClampedArray arr(cx, pixels->GetDataObject());
     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/events/src/nsDOMNotifyAudioAvailableEvent.cpp
+++ b/content/events/src/nsDOMNotifyAudioAvailableEvent.cpp
@@ -81,17 +81,17 @@ nsDOMNotifyAudioAvailableEvent::GetFrame
   // Cache this array so we don't recreate on next call.
   NS_HOLD_JS_OBJECTS(this, nsDOMNotifyAudioAvailableEvent);
 
   mCachedArray = JS_NewFloat32Array(aCx, mFrameBufferLength);
   if (!mCachedArray) {
     NS_DROP_JS_OBJECTS(this, nsDOMNotifyAudioAvailableEvent);
     return NS_ERROR_FAILURE;
   }
-  memcpy(JS_GetFloat32ArrayData(mCachedArray), mFrameBuffer.get(), mFrameBufferLength * sizeof(float));
+  memcpy(JS_GetFloat32ArrayData(mCachedArray, aCx), mFrameBuffer.get(), mFrameBufferLength * sizeof(float));
 
   *aResult = OBJECT_TO_JSVAL(mCachedArray);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMNotifyAudioAvailableEvent::GetTime(float *aRetVal)
 {
--- a/content/html/content/src/nsHTMLAudioElement.cpp
+++ b/content/html/content/src/nsHTMLAudioElement.cpp
@@ -151,41 +151,41 @@ nsHTMLAudioElement::MozWriteAudio(const 
     return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
   }
 
   JSObject* darray = &aData.toObject();
   JS::AutoObjectRooter tvr(aCx);
   JSObject* tsrc = NULL;
 
   // Allow either Float32Array or plain JS Array
-  if (JS_IsFloat32Array(darray)) {
+  if (JS_IsFloat32Array(darray, aCx)) {
     tsrc = darray;
   } else if (JS_IsArrayObject(aCx, darray)) {
     JSObject* nobj = JS_NewFloat32ArrayFromArray(aCx, darray);
     if (!nobj) {
       return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
     }
     tsrc = nobj;
   } else {
     return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
   }
   tvr.setObject(tsrc);
 
-  uint32_t dataLength = JS_GetTypedArrayLength(tsrc);
+  uint32_t dataLength = JS_GetTypedArrayLength(tsrc, aCx);
 
   // Make sure that we are going to write the correct amount of data based
   // on number of channels.
   if (dataLength % mChannels != 0) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // Don't write more than can be written without blocking.
   uint32_t writeLen = NS_MIN(mAudioStream->Available(), dataLength / mChannels);
 
-  float* frames = JS_GetFloat32ArrayData(tsrc);
+  float* frames = JS_GetFloat32ArrayData(tsrc, aCx);
   // Convert the samples back to integers as we are using fixed point audio in
   // the nsAudioStream.
   // This could be optimized to avoid allocation and memcpy when
   // AudioDataValue is 'float', but it's not worth it for this deprecated API.
   nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[writeLen * mChannels]);
   ConvertAudioSamples(frames, audioData.get(), writeLen * mChannels);
   nsresult rv = mAudioStream->Write(audioData.get(), writeLen);
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -514,17 +514,17 @@ nsPIDOMWindow::~nsPIDOMWindow() {}
 
 //*****************************************************************************
 // nsOuterWindowProxy: Outer Window Proxy
 //*****************************************************************************
 
 class nsOuterWindowProxy : public js::Wrapper
 {
 public:
-  nsOuterWindowProxy() : js::Wrapper(0) { setSafeToUnwrap(false); }
+  nsOuterWindowProxy() : js::Wrapper(0) {}
 
   virtual bool isOuterWindow() {
     return true;
   }
   JSString *obj_toString(JSContext *cx, JSObject *wrapper);
   void finalize(JSFreeOp *fop, JSObject *proxy);
 
   static nsOuterWindowProxy singleton;
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -517,21 +517,19 @@ QueryInterface(JSContext* cx, unsigned a
 {
   JS::Value thisv = JS_THIS(cx, vp);
   if (thisv == JSVAL_NULL)
     return false;
 
   // Get the object. It might be a security wrapper, in which case we do a checked
   // unwrap.
   JSObject* origObj = JSVAL_TO_OBJECT(thisv);
-  JSObject* obj = js::UnwrapObjectChecked(origObj);
-  if (!obj) {
-      JS_ReportError(cx, "Permission denied to access object");
+  JSObject* obj = js::UnwrapObjectChecked(cx, origObj);
+  if (!obj)
       return false;
-  }
 
   nsISupports* native;
   if (!UnwrapDOMObjectToISupports(obj, native)) {
     return Throw<true>(cx, NS_ERROR_FAILURE);
   }
 
   if (argc < 1) {
     return Throw<true>(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -240,17 +240,17 @@ IsArrayLike(JSContext* cx, JSObject* obj
       return false;
     }
 
     ac.construct(cx, obj);
   }
 
   // XXXbz need to detect platform objects (including listbinding
   // ones) with indexGetters here!
-  return JS_IsArrayObject(cx, obj) || JS_IsTypedArrayObject(obj);
+  return JS_IsArrayObject(cx, obj) || JS_IsTypedArrayObject(obj, cx);
 }
 
 inline bool
 IsPlatformObject(JSContext* cx, JSObject* obj)
 {
   // XXXbz Should be treating list-binding objects as platform objects
   // too?  The one consumer so far wants non-array-like platform
   // objects, so listbindings that have an indexGetter should test
@@ -267,17 +267,17 @@ IsPlatformObject(JSContext* cx, JSObject
     obj = xpc::Unwrap(cx, obj, false);
     if (!obj) {
       // Let's say it's not
       return false;
     }
     clasp = js::GetObjectJSClass(obj);
   }
   return IS_WRAPPER_CLASS(js::Valueify(clasp)) || IsDOMClass(clasp) ||
-    JS_IsArrayBufferObject(obj);
+    JS_IsArrayBufferObject(obj, cx);
 }
 
 // U must be something that a T* can be assigned to (e.g. T* or an nsRefPtr<T>).
 template <class T, typename U>
 inline nsresult
 UnwrapObject(JSContext* cx, JSObject* obj, U& value)
 {
   return UnwrapObject<static_cast<prototypes::ID>(
@@ -1309,21 +1309,16 @@ public:
   bool WasPassed() const {
     return !mImpl.empty();
   }
 
   void Construct() {
     mImpl.construct();
   }
 
-  template <class T1>
-  void Construct(const T1 &t1) {
-    mImpl.construct(t1);
-  }
-
   template <class T1, class T2>
   void Construct(const T1 &t1, const T2 &t2) {
     mImpl.construct(t1, t2);
   }
 
   const T& Value() const {
     return mImpl.ref();
   }
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2585,17 +2585,17 @@ for (uint32_t i = 0; i < length; ++i) {
                 # We don't need a holder in this case
                 holderType = None
                 constructLoc = "(const_cast<Optional<" + name + ">& >(${declName}))"
                 constructMethod = "Construct"
                 constructInternal = "Value"
             else:
                 declType = "NonNull<" + name + ">"
         template = (
-            "%s.%s(&${val}.toObject());\n"
+            "%s.%s(cx, &${val}.toObject());\n"
             "if (!%s.%s().inited()) {\n"
             "%s" # No newline here because onFailureBadType() handles that
             "}\n" %
             (constructLoc, constructMethod, constructLoc, constructInternal,
              CGIndenter(onFailureBadType(failureCode, type.name)).define()))
         nullableTarget = ""
         if type.nullable():
             if isOptional:
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -14,21 +14,21 @@ 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* UnboxArray(JSContext*, JSObject*, uint32_t*, T**)>
 struct TypedArray_base {
-  TypedArray_base(JSObject* obj)
+  TypedArray_base(JSContext* cx, JSObject* obj)
   {
-    mObj = UnboxArray(obj, &mLength, &mData);
+    mObj = UnboxArray(cx, obj, &mLength, &mData);
   }
 
 private:
   T* mData;
   uint32_t mLength;
   JSObject* mObj;
 
 public:
@@ -49,38 +49,38 @@ public:
   inline JSObject *Obj() const {
     MOZ_ASSERT(inited());
     return mObj;
   }
 };
 
 
 template<typename T,
-         T* GetData(JSObject*),
-         JSObject* UnboxArray(JSObject*, uint32_t*, T**),
+         T* GetData(JSObject*, JSContext*),
+         JSObject* UnboxArray(JSContext*, JSObject*, uint32_t*, T**),
          JSObject* CreateNew(JSContext*, uint32_t)>
 struct TypedArray : public TypedArray_base<T,UnboxArray> {
-  TypedArray(JSObject* obj) :
-    TypedArray_base<T,UnboxArray>(obj)
+  TypedArray(JSContext* cx, JSObject* obj) :
+    TypedArray_base<T,UnboxArray>(cx, obj)
   {}
 
   static inline JSObject*
   Create(JSContext* cx, nsWrapperCache* creator, uint32_t length,
          const T* data = NULL) {
     JSObject* creatorWrapper;
     Maybe<JSAutoCompartment> ac;
     if (creator && (creatorWrapper = creator->GetWrapperPreserveColor())) {
       ac.construct(cx, creatorWrapper);
     }
     JSObject* obj = CreateNew(cx, length);
     if (!obj) {
       return NULL;
     }
     if (data) {
-      T* buf = static_cast<T*>(GetData(obj));
+      T* buf = static_cast<T*>(GetData(obj, cx));
       memcpy(buf, data, length*sizeof(T));
     }
     return obj;
   }
 };
 
 typedef TypedArray<int8_t, JS_GetInt8ArrayData, JS_GetObjectAsInt8Array,
                    JS_NewInt8Array>
--- a/dom/file/LockedFile.cpp
+++ b/dom/file/LockedFile.cpp
@@ -213,19 +213,19 @@ CreateGenericEvent(const nsAString& aTyp
 inline nsresult
 GetInputStreamForJSVal(const jsval& aValue, JSContext* aCx,
                        nsIInputStream** aInputStream, uint64_t* aInputLength)
 {
   nsresult rv;
 
   if (!JSVAL_IS_PRIMITIVE(aValue)) {
     JSObject* obj = JSVAL_TO_OBJECT(aValue);
-    if (JS_IsArrayBufferObject(obj)) {
-      char* data = reinterpret_cast<char*>(JS_GetArrayBufferData(obj));
-      uint32_t length = JS_GetArrayBufferByteLength(obj);
+    if (JS_IsArrayBufferObject(obj, aCx)) {
+      char* data = reinterpret_cast<char*>(JS_GetArrayBufferData(obj, aCx));
+      uint32_t length = JS_GetArrayBufferByteLength(obj, aCx);
 
       rv = NS_NewByteInputStream(aInputStream, data, length,
                                  NS_ASSIGNMENT_COPY);
       NS_ENSURE_SUCCESS(rv, rv);
 
       *aInputLength = length;
 
       return NS_OK;
--- a/dom/network/src/TCPSocketChild.cpp
+++ b/dom/network/src/TCPSocketChild.cpp
@@ -23,17 +23,17 @@ DeserializeUint8Array(JSRawObject aObj,
 {
   JSContext* cx = nsContentUtils::GetSafeJSContext();
   JSAutoRequest ar(cx);
   JSAutoCompartment ac(cx, aObj);
 
   JSObject* obj = JS_NewArrayBuffer(cx, aBuffer.Length());
   if (!obj)
     return false;
-  uint8_t* data = JS_GetArrayBufferData(obj);
+  uint8_t* data = JS_GetArrayBufferData(obj, cx);
   if (!data)
     return false;
   memcpy(data, aBuffer.Elements(), aBuffer.Length());
   JSObject* arr = JS_NewUint8ArrayWithBuffer(cx, obj, 0, aBuffer.Length());
   if (!arr)
     return false;
   *aVal = OBJECT_TO_JSVAL(arr);
   return true;
@@ -182,20 +182,20 @@ TCPSocketChild::Send(const JS::Value& aD
     nsDependentJSString str;
     bool ok = str.init(aCx, jsstr);
     NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
     SendData(str);
 
   } else {
     NS_ENSURE_TRUE(aData.isObject(), NS_ERROR_FAILURE);
     JSObject* obj = &aData.toObject();
-    NS_ENSURE_TRUE(JS_IsTypedArrayObject(obj), NS_ERROR_FAILURE);
-    NS_ENSURE_TRUE(JS_IsUint8Array(obj), NS_ERROR_FAILURE);
-    uint32_t nbytes = JS_GetTypedArrayByteLength(obj);
-    uint8_t* data = JS_GetUint8ArrayData(obj);
+    NS_ENSURE_TRUE(JS_IsTypedArrayObject(obj, aCx), NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(JS_IsUint8Array(obj, aCx), NS_ERROR_FAILURE);
+    uint32_t nbytes = JS_GetTypedArrayByteLength(obj, aCx);
+    uint8_t* data = JS_GetUint8ArrayData(obj, aCx);
     if (!data) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
     FallibleTArray<uint8_t> fallibleArr;
     if (!fallibleArr.InsertElementsAt(0, data, nbytes)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
     InfallibleTArray<uint8_t> arr;
--- a/dom/network/src/TCPSocketParent.cpp
+++ b/dom/network/src/TCPSocketParent.cpp
@@ -151,20 +151,20 @@ TCPSocketParent::SendCallback(const nsAS
     }
     data = str;
 
   } else if (aDataVal.isUndefined() || aDataVal.isNull()) {
     data = mozilla::void_t();
 
   } else if (aDataVal.isObject()) {
     JSObject* obj = &aDataVal.toObject();
-    if (JS_IsTypedArrayObject(obj)) {
-      NS_ENSURE_TRUE(JS_IsUint8Array(obj), NS_ERROR_FAILURE);
-      uint32_t nbytes = JS_GetTypedArrayByteLength(obj);
-      uint8_t* buffer = JS_GetUint8ArrayData(obj);
+    if (JS_IsTypedArrayObject(obj, aCx)) {
+      NS_ENSURE_TRUE(JS_IsUint8Array(obj, aCx), NS_ERROR_FAILURE);
+      uint32_t nbytes = JS_GetTypedArrayByteLength(obj, aCx);
+      uint8_t* buffer = JS_GetUint8ArrayData(obj, aCx);
       if (!buffer) {
         FireInteralError(this, __LINE__);
         return NS_ERROR_OUT_OF_MEMORY;
       }
       FallibleTArray<uint8_t> fallibleArr;
       if (!fallibleArr.InsertElementsAt(0, buffer, nbytes)) {
         FireInteralError(this, __LINE__);
         return NS_ERROR_OUT_OF_MEMORY;
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -455,22 +455,30 @@ JSValToNPVariant(NPP npp, JSContext *cx,
 
     return true;
   }
 
   // The reflected plugin object may be in another compartment if the plugin
   // element has since been adopted into a new document. We don't bother
   // transplanting the plugin objects, and just do a unwrap with security
   // checks if we encounter one of them as an argument. If the unwrap fails,
-  // we run with the original wrapped object, since sometimes there are
-  // legitimate cases where a security wrapper ends up here (for example,
-  // Location objects, which are _always_ behind security wrappers).
+  // we clear the pending exception and just run with the original wrapped object,
+  // since sometimes there are legitimate cases where a security wrapper ends
+  // up here (for example, Location objects, which are _always_ behind security
+  // wrappers).
+  //
+  // NB: In addition to clearing the pending exception, we also have to temporarily
+  // disable the error reporter, because SpiderMonkey calls it directly if there's
+  // no JS code on the stack, which might be the case here.
   JSObject *obj = JSVAL_TO_OBJECT(val);
-  obj = js::UnwrapObjectChecked(obj);
+  JSErrorReporter reporter = JS_SetErrorReporter(cx, NULL);
+  obj = js::UnwrapObjectChecked(cx, obj);
+  JS_SetErrorReporter(cx, reporter);
   if (!obj) {
+    JS_ClearPendingException(cx);
     obj = JSVAL_TO_OBJECT(val);
   }
 
   NPObject *npobj = nsJSObjWrapper::GetNewOrUsed(npp, cx, obj);
   if (!npobj) {
     return false;
   }
 
@@ -1121,17 +1129,17 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JS
 // wrapper.
 //
 // Because this function unwraps, its return value must be wrapped for the cx
 // compartment for callers that plan to hold onto the result or do anything
 // substantial with it.
 static JSObject *
 GetNPObjectWrapper(JSContext *cx, JSObject *obj, bool wrapResult = true)
 {
-  while (obj && (obj = js::UnwrapObjectChecked(obj))) {
+  while (obj && (obj = js::UnwrapObjectChecked(cx, obj))) {
     if (JS_GetClass(obj) == &sNPObjectJSWrapperClass) {
       if (wrapResult && !JS_WrapObject(cx, &obj)) {
         return NULL;
       }
       return obj;
     }
     if (!::JS_GetPrototype(cx, obj, &obj)) {
       return NULL;
--- a/dom/system/gonk/SystemWorkerManager.cpp
+++ b/dom/system/gonk/SystemWorkerManager.cpp
@@ -84,31 +84,31 @@ PostToRIL(JSContext *cx, unsigned argc, 
     if (!abs.encode(cx, str)) {
       return false;
     }
 
     size = JS_GetStringLength(str);
     data = abs.ptr();
   } else if (!JSVAL_IS_PRIMITIVE(v)) {
     JSObject *obj = JSVAL_TO_OBJECT(v);
-    if (!JS_IsTypedArrayObject(obj)) {
+    if (!JS_IsTypedArrayObject(obj, cx)) {
       JS_ReportError(cx, "Object passed in wasn't a typed array");
       return false;
     }
 
-    uint32_t type = JS_GetTypedArrayType(obj);
+    uint32_t type = JS_GetTypedArrayType(obj, cx);
     if (type != js::ArrayBufferView::TYPE_INT8 &&
         type != js::ArrayBufferView::TYPE_UINT8 &&
         type != js::ArrayBufferView::TYPE_UINT8_CLAMPED) {
       JS_ReportError(cx, "Typed array data is not octets");
       return false;
     }
 
-    size = JS_GetTypedArrayByteLength(obj);
-    data = JS_GetArrayBufferViewData(obj);
+    size = JS_GetTypedArrayByteLength(obj, cx);
+    data = JS_GetArrayBufferViewData(obj, cx);
   } else {
     JS_ReportError(cx,
                    "Incorrect argument. Expecting a string or a typed array");
     return false;
   }
 
   if (size > RilRawData::MAX_DATA_SIZE) {
     JS_ReportError(cx, "Passed-in data is too large");
@@ -170,17 +170,17 @@ RILReceiver::DispatchRILEvent::RunTask(J
 {
   JSObject *obj = JS_GetGlobalObject(aCx);
 
   JSObject *array = JS_NewUint8Array(aCx, mMessage->mSize);
   if (!array) {
     return false;
   }
 
-  memcpy(JS_GetArrayBufferViewData(array), mMessage->mData, mMessage->mSize);
+  memcpy(JS_GetArrayBufferViewData(array, aCx), mMessage->mData, mMessage->mSize);
   jsval argv[] = { OBJECT_TO_JSVAL(array) };
   return JS_CallFunctionName(aCx, obj, "onRILMessage", NS_ARRAY_LENGTH(argv),
                              argv, argv);
 }
 
 #ifdef MOZ_WIDGET_GONK
 
 JSBool
@@ -212,36 +212,36 @@ DoNetdCommand(JSContext *cx, unsigned ar
 
     data = abs.ptr();
     if (!data) {
       JS_ReportError(cx, "Command string is empty");
       return false;
     }
   } else if (!JSVAL_IS_PRIMITIVE(v)) {
     JSObject *obj = JSVAL_TO_OBJECT(v);
-    if (!JS_IsTypedArrayObject(obj)) {
+    if (!JS_IsTypedArrayObject(obj, cx)) {
       JS_ReportError(cx, "Object passed in wasn't a typed array");
       return false;
     }
 
-    uint32_t type = JS_GetTypedArrayType(obj);
+    uint32_t type = JS_GetTypedArrayType(obj, cx);
     if (type != js::ArrayBufferView::TYPE_INT8 &&
         type != js::ArrayBufferView::TYPE_UINT8 &&
         type != js::ArrayBufferView::TYPE_UINT8_CLAMPED) {
       JS_ReportError(cx, "Typed array data is not octets");
       return false;
     }
 
-    size = JS_GetTypedArrayByteLength(obj);
+    size = JS_GetTypedArrayByteLength(obj, cx);
     if (!size) {
       JS_ReportError(cx, "Typed array byte length is zero");
       return false;
     }
 
-    data = JS_GetArrayBufferViewData(obj);
+    data = JS_GetArrayBufferViewData(obj, cx);
     if (!data) {
       JS_ReportError(cx, "Array buffer view data is NULL");
       return false;
     }
   } else {
     JS_ReportError(cx,
                    "Incorrect argument. Expecting a string or a typed array");
     return false;
@@ -317,17 +317,17 @@ NetdReceiver::DispatchNetdEvent::RunTask
 {
   JSObject *obj = JS_GetGlobalObject(aCx);
 
   JSObject *array = JS_NewUint8Array(aCx, mMessage->mSize);
   if (!array) {
     return false;
   }
 
-  memcpy(JS_GetUint8ArrayData(array), mMessage->mData, mMessage->mSize);
+  memcpy(JS_GetUint8ArrayData(array, aCx), mMessage->mData, mMessage->mSize);
   jsval argv[] = { OBJECT_TO_JSVAL(array) };
   return JS_CallFunctionName(aCx, obj, "onNetdMessage", NS_ARRAY_LENGTH(argv),
                              argv, argv);
 }
 
 #endif // MOZ_WIDGET_GONK
 
 } // anonymous namespace
--- a/dom/workers/FileReaderSync.cpp
+++ b/dom/workers/FileReaderSync.cpp
@@ -90,18 +90,18 @@ FileReaderSync::ReadAsArrayBuffer(JSCont
   JSObject* jsArrayBuffer = JS_NewArrayBuffer(aCx, blobSize);
   if (!jsArrayBuffer) {
     // XXXkhuey we need a way to indicate to the bindings that the call failed
     // but there's already a pending exception that we should not clobber.
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
 
-  uint32_t bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer);
-  uint8_t* arrayBuffer = JS_GetArrayBufferData(jsArrayBuffer);
+  uint32_t bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer, aCx);
+  uint8_t* arrayBuffer = JS_GetArrayBufferData(jsArrayBuffer, aCx);
 
   nsCOMPtr<nsIInputStream> stream;
   rv = blob->GetInternalStream(getter_AddRefs(stream));
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
--- a/dom/workers/ImageData.cpp
+++ b/dom/workers/ImageData.cpp
@@ -35,18 +35,18 @@ public:
     return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0, sProperties,
                         NULL, NULL, NULL);
   }
 
   static JSObject*
   Create(JSContext* aCx, uint32_t aWidth, uint32_t aHeight, JSObject *aData)
   {
     MOZ_ASSERT(aData);
-    MOZ_ASSERT(JS_IsTypedArrayObject(aData));
-    MOZ_ASSERT(JS_IsUint8ClampedArray(aData));
+    MOZ_ASSERT(JS_IsTypedArrayObject(aData, aCx));
+    MOZ_ASSERT(JS_IsUint8ClampedArray(aData, aCx));
 
     JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
     if (!obj) {
       return NULL;
     }
 
     JS_SetReservedSlot(obj, SLOT_width, UINT_TO_JSVAL(aWidth));
     JS_SetReservedSlot(obj, SLOT_height, UINT_TO_JSVAL(aHeight));
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -2003,17 +2003,17 @@ XMLHttpRequest::Send(JSObject* aBody, Er
   if (!mProxy) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   JSContext* cx = GetJSContext();
 
   jsval valToClone;
-  if (JS_IsArrayBufferObject(aBody) || file::GetDOMBlobFromJSObject(aBody)) {
+  if (JS_IsArrayBufferObject(aBody, cx) || file::GetDOMBlobFromJSObject(aBody)) {
     valToClone = OBJECT_TO_JSVAL(aBody);
   }
   else {
     JSString* bodyStr = JS_ValueToString(cx, OBJECT_TO_JSVAL(aBody));
     if (!bodyStr) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -2111,17 +2111,17 @@ ConvertToJS(JSContext* cx,
 // ambiguity to a C type. Elements of a Int8Array are converted to
 // ctypes.int8_t, UInt8Array to ctypes.uint8_t, etc.
 bool CanConvertTypedArrayItemTo(JSObject *baseType, JSObject *valObj, JSContext *cx) {
   TypeCode baseTypeCode = CType::GetTypeCode(baseType);
   if (baseTypeCode == TYPE_void_t) {
     return true;
   }
   TypeCode elementTypeCode;
-  switch (JS_GetTypedArrayType(valObj)) {
+  switch (JS_GetTypedArrayType(valObj, cx)) {
   case TypedArray::TYPE_INT8:
     elementTypeCode = TYPE_int8_t;
     break;
   case TypedArray::TYPE_UINT8:
   case TypedArray::TYPE_UINT8_CLAMPED:
     elementTypeCode = TYPE_uint8_t;
     break;
   case TypedArray::TYPE_INT16:
@@ -2336,31 +2336,31 @@ ImplicitConvert(JSContext* cx,
         memcpy(*jscharBuffer, sourceChars, sourceLength * sizeof(jschar));
         (*jscharBuffer)[sourceLength] = 0;
         break;
       }
       default:
         return TypeError(cx, "string pointer", val);
       }
       break;
-    } else if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayBufferObject(valObj)) {
+    } else if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayBufferObject(valObj, cx)) {
       // Convert ArrayBuffer to pointer without any copy.
       // Just as with C arrays, we make no effort to
       // keep the ArrayBuffer alive.
-      *static_cast<void**>(buffer) = JS_GetArrayBufferData(valObj);
+      *static_cast<void**>(buffer) = JS_GetArrayBufferData(valObj, cx);
       break;
-    } if (!JSVAL_IS_PRIMITIVE(val) && JS_IsTypedArrayObject(valObj)) {
+    } if (!JSVAL_IS_PRIMITIVE(val) && JS_IsTypedArrayObject(valObj, cx)) {
       if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
         return TypeError(cx, "typed array with the appropriate type", val);
       }
 
       // Convert TypedArray to pointer without any copy.
       // Just as with C arrays, we make no effort to
       // keep the TypedArray alive.
-      *static_cast<void**>(buffer) = JS_GetArrayBufferViewData(valObj);
+      *static_cast<void**>(buffer) = JS_GetArrayBufferViewData(valObj, cx);
       break;
     }
     return TypeError(cx, "pointer", val);
   }
   case TYPE_array: {
     JSObject* baseType = ArrayType::GetBaseType(targetType);
     size_t targetLength = ArrayType::GetLength(targetType);
 
@@ -2439,44 +2439,44 @@ ImplicitConvert(JSContext* cx,
         char* data = intermediate.get() + elementSize * i;
         if (!ImplicitConvert(cx, item.jsval_value(), baseType, data, false, NULL))
           return false;
       }
 
       memcpy(buffer, intermediate.get(), arraySize);
 
     } else if (!JSVAL_IS_PRIMITIVE(val) &&
-               JS_IsArrayBufferObject(valObj)) {
+               JS_IsArrayBufferObject(valObj, cx)) {
       // Check that array is consistent with type, then
       // copy the array.
-      uint32_t sourceLength = JS_GetArrayBufferByteLength(valObj);
+      uint32_t sourceLength = JS_GetArrayBufferByteLength(valObj, cx);
       size_t elementSize = CType::GetSize(baseType);
       size_t arraySize = elementSize * targetLength;
       if (arraySize != size_t(sourceLength)) {
         JS_ReportError(cx, "ArrayType length does not match source ArrayBuffer length");
         return false;
       }
-      memcpy(buffer, JS_GetArrayBufferData(valObj), sourceLength);
+      memcpy(buffer, JS_GetArrayBufferData(valObj, cx), sourceLength);
       break;
     }  else if (!JSVAL_IS_PRIMITIVE(val) &&
-               JS_IsTypedArrayObject(valObj)) {
+               JS_IsTypedArrayObject(valObj, cx)) {
       // Check that array is consistent with type, then
       // copy the array.
       if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
         return TypeError(cx, "typed array with the appropriate type", val);
       }
 
-      uint32_t sourceLength = JS_GetTypedArrayByteLength(valObj);
+      uint32_t sourceLength = JS_GetTypedArrayByteLength(valObj, cx);
       size_t elementSize = CType::GetSize(baseType);
       size_t arraySize = elementSize * targetLength;
       if (arraySize != size_t(sourceLength)) {
         JS_ReportError(cx, "typed array length does not match source TypedArray length");
         return false;
       }
-      memcpy(buffer, JS_GetArrayBufferViewData(valObj), sourceLength);
+      memcpy(buffer, JS_GetArrayBufferViewData(valObj, cx), sourceLength);
       break;
     } else {
       // Don't implicitly convert to string. Users can implicitly convert
       // with `String(x)` or `""+x`.
       return TypeError(cx, "array", val);
     }
     break;
   }
--- a/js/src/jsapi-tests/testArrayBuffer.cpp
+++ b/js/src/jsapi-tests/testArrayBuffer.cpp
@@ -31,61 +31,61 @@ BEGIN_TEST(testArrayBuffer_bug720949_ste
 
     for (unsigned i = 0; i < NUM_TEST_BUFFERS; i++) {
         JS::HandleObject obj = testBuf[i];
         JS::HandleObject view = testArray[i];
         uint32_t size = sizes[i];
         jsval v;
 
         // Byte lengths should all agree
-        CHECK(JS_IsArrayBufferObject(obj));
-        CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), size);
+        CHECK(JS_IsArrayBufferObject(obj, cx));
+        CHECK_EQUAL(JS_GetArrayBufferByteLength(obj, cx), size);
         JS_GetProperty(cx, obj, "byteLength", &v);
         CHECK_SAME(v, INT_TO_JSVAL(size));
         JS_GetProperty(cx, view, "byteLength", &v);
         CHECK_SAME(v, INT_TO_JSVAL(size));
 
         // Modifying the underlying data should update the value returned through the view
-        uint8_t *data = JS_GetArrayBufferData(obj);
+        uint8_t *data = JS_GetArrayBufferData(obj, cx);
         CHECK(data != NULL);
         *reinterpret_cast<uint32_t*>(data) = MAGIC_VALUE_2;
         CHECK(JS_GetElement(cx, view, 0, &v));
         CHECK_SAME(v, INT_TO_JSVAL(MAGIC_VALUE_2));
 
         // Steal the contents
         void *contents;
         CHECK(JS_StealArrayBufferContents(cx, obj, &contents, &data));
         CHECK(contents != NULL);
         CHECK(data != NULL);
 
         // Check that the original ArrayBuffer is neutered
-        CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), 0);
+        CHECK_EQUAL(JS_GetArrayBufferByteLength(obj, cx), 0);
         CHECK(JS_GetProperty(cx, obj, "byteLength", &v));
         CHECK_SAME(v, INT_TO_JSVAL(0));
         CHECK(JS_GetProperty(cx, view, "byteLength", &v));
         CHECK_SAME(v, INT_TO_JSVAL(0));
         CHECK(JS_GetProperty(cx, view, "byteOffset", &v));
         CHECK_SAME(v, INT_TO_JSVAL(0));
         CHECK(JS_GetProperty(cx, view, "length", &v));
         CHECK_SAME(v, INT_TO_JSVAL(0));
-        CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), 0);
+        CHECK_EQUAL(JS_GetArrayBufferByteLength(obj, cx), 0);
         v = JSVAL_VOID;
         JS_GetElement(cx, obj, 0, &v);
         CHECK_SAME(v, JSVAL_VOID);
 
         // Transfer to a new ArrayBuffer
         js::RootedObject dst(cx, JS_NewArrayBufferWithContents(cx, contents));
-        CHECK(JS_IsArrayBufferObject(dst));
-        data = JS_GetArrayBufferData(obj);
+        CHECK(JS_IsArrayBufferObject(dst, cx));
+        data = JS_GetArrayBufferData(obj, cx);
 
         js::RootedObject dstview(cx, JS_NewInt32ArrayWithBuffer(cx, dst, 0, -1));
         CHECK(dstview != NULL);
 
-        CHECK_EQUAL(JS_GetArrayBufferByteLength(dst), size);
-        data = JS_GetArrayBufferData(dst);
+        CHECK_EQUAL(JS_GetArrayBufferByteLength(dst, cx), size);
+        data = JS_GetArrayBufferData(dst, cx);
         CHECK(data != NULL);
         CHECK_EQUAL(*reinterpret_cast<uint32_t*>(data), MAGIC_VALUE_2);
         CHECK(JS_GetElement(cx, dstview, 0, &v));
         CHECK_SAME(v, INT_TO_JSVAL(MAGIC_VALUE_2));
     }
 
     return true;
 }
--- a/js/src/jsapi-tests/testTypedArrays.cpp
+++ b/js/src/jsapi-tests/testTypedArrays.cpp
@@ -23,26 +23,26 @@ BEGIN_TEST(testTypedArrays)
         TestPlainTypedArray<JS_NewUint16Array, uint16_t, JS_GetUint16ArrayData>(cx) &&
         TestPlainTypedArray<JS_NewInt32Array, int32_t, JS_GetInt32ArrayData>(cx) &&
         TestPlainTypedArray<JS_NewUint32Array, uint32_t, JS_GetUint32ArrayData>(cx) &&
         TestPlainTypedArray<JS_NewFloat32Array, float, JS_GetFloat32ArrayData>(cx) &&
         TestPlainTypedArray<JS_NewFloat64Array, double, JS_GetFloat64ArrayData>(cx);
 
     size_t nbytes = sizeof(double) * 8;
     RootedObject buffer(cx, JS_NewArrayBuffer(cx, nbytes));
-    CHECK(JS_IsArrayBufferObject(buffer));
+    CHECK(JS_IsArrayBufferObject(buffer, cx));
 
     RootedObject proto(cx);
     JS_GetPrototype(cx, buffer, proto.address());
-    CHECK(!JS_IsArrayBufferObject(proto));
+    CHECK(!JS_IsArrayBufferObject(proto, cx));
     RootedObject dummy(cx, JS_GetParent(proto));
-    CHECK(!JS_IsArrayBufferObject(dummy));
+    CHECK(!JS_IsArrayBufferObject(dummy, cx));
 
-    CHECK_EQUAL(JS_GetArrayBufferByteLength(buffer), nbytes);
-    memset(JS_GetArrayBufferData(buffer), 1, nbytes);
+    CHECK_EQUAL(JS_GetArrayBufferByteLength(buffer, cx), nbytes);
+    memset(JS_GetArrayBufferData(buffer, cx), 1, nbytes);
 
     ok = ok &&
         TestArrayFromBuffer<JS_NewInt8ArrayWithBuffer, JS_NewInt8ArrayFromArray, int8_t, JS_GetInt8ArrayData>(cx) &&
         TestArrayFromBuffer<JS_NewUint8ArrayWithBuffer, JS_NewUint8ArrayFromArray, uint8_t, JS_GetUint8ArrayData>(cx) &&
         TestArrayFromBuffer<JS_NewUint8ClampedArrayWithBuffer, JS_NewUint8ClampedArrayFromArray, uint8_t, JS_GetUint8ClampedArrayData>(cx) &&
         TestArrayFromBuffer<JS_NewInt16ArrayWithBuffer, JS_NewInt16ArrayFromArray, int16_t, JS_GetInt16ArrayData>(cx) &&
         TestArrayFromBuffer<JS_NewUint16ArrayWithBuffer, JS_NewUint16ArrayFromArray, uint16_t, JS_GetUint16ArrayData>(cx) &&
         TestArrayFromBuffer<JS_NewInt32ArrayWithBuffer, JS_NewInt32ArrayFromArray, int32_t, JS_GetInt32ArrayData>(cx) &&
@@ -50,79 +50,79 @@ BEGIN_TEST(testTypedArrays)
         TestArrayFromBuffer<JS_NewFloat32ArrayWithBuffer, JS_NewFloat32ArrayFromArray, float, JS_GetFloat32ArrayData>(cx) &&
         TestArrayFromBuffer<JS_NewFloat64ArrayWithBuffer, JS_NewFloat64ArrayFromArray, double, JS_GetFloat64ArrayData>(cx);
 
     return ok;
 }
 
 template<JSObject *Create(JSContext *, uint32_t),
          typename Element,
-         Element *GetData(JSObject *)>
+         Element *GetData(JSObject *, JSContext *)>
 bool
 TestPlainTypedArray(JSContext *cx)
 {
     RootedObject array(cx, Create(cx, 7));
-    CHECK(JS_IsTypedArrayObject(array));
+    CHECK(JS_IsTypedArrayObject(array, cx));
     RootedObject proto(cx);
     JS_GetPrototype(cx, array, proto.address());
-    CHECK(!JS_IsTypedArrayObject(proto));
+    CHECK(!JS_IsTypedArrayObject(proto, cx));
     RootedObject dummy(cx, JS_GetParent(proto));
-    CHECK(!JS_IsTypedArrayObject(dummy));
+    CHECK(!JS_IsTypedArrayObject(dummy, cx));
 
-    CHECK_EQUAL(JS_GetTypedArrayLength(array), 7);
-    CHECK_EQUAL(JS_GetTypedArrayByteOffset(array), 0);
-    CHECK_EQUAL(JS_GetTypedArrayByteLength(array), sizeof(Element) * 7);
+    CHECK_EQUAL(JS_GetTypedArrayLength(array, cx), 7);
+    CHECK_EQUAL(JS_GetTypedArrayByteOffset(array, cx), 0);
+    CHECK_EQUAL(JS_GetTypedArrayByteLength(array, cx), sizeof(Element) * 7);
 
     Element *data;
-    CHECK(data = GetData(array));
+    CHECK(data = GetData(array, cx));
     *data = 13;
     jsval v;
     CHECK(JS_GetElement(cx, array, 0, &v));
     CHECK_SAME(v, INT_TO_JSVAL(13));
 
     return true;
 }
 
 template<JSObject *CreateWithBuffer(JSContext *, JSObject *, uint32_t, int32_t),
          JSObject *CreateFromArray(JSContext *, JSObject *),
          typename Element,
-         Element *GetData(JSObject *)>
+         Element *GetData(JSObject *, JSContext *)>
 bool
 TestArrayFromBuffer(JSContext *cx)
 {
     size_t elts = 8;
     size_t nbytes = elts * sizeof(Element);
     RootedObject buffer(cx, JS_NewArrayBuffer(cx, nbytes));
     uint8_t *bufdata;
-    CHECK(bufdata = JS_GetArrayBufferData(buffer));
+    CHECK(bufdata = JS_GetArrayBufferData(buffer, cx));
     memset(bufdata, 1, nbytes);
 
     RootedObject array(cx, CreateWithBuffer(cx, buffer, 0, -1));
-    CHECK_EQUAL(JS_GetTypedArrayLength(array), elts);
-    CHECK_EQUAL(JS_GetTypedArrayByteOffset(array), 0);
-    CHECK_EQUAL(JS_GetTypedArrayByteLength(array), nbytes);
-    CHECK_EQUAL(JS_GetArrayBufferViewBuffer(array), (JSObject*) buffer);
+    CHECK_EQUAL(JS_GetTypedArrayLength(array, cx), elts);
+    CHECK_EQUAL(JS_GetTypedArrayByteOffset(array, cx), 0);
+    CHECK_EQUAL(JS_GetTypedArrayByteLength(array, cx), nbytes);
+    CHECK_EQUAL(JS_GetArrayBufferViewBuffer(array, cx), (JSObject*) buffer);
 
     Element *data;
-    CHECK(data = GetData(array));
-    CHECK(bufdata = JS_GetArrayBufferData(buffer));
+    CHECK(data = GetData(array, cx));
+    CHECK(bufdata = JS_GetArrayBufferData(buffer, cx));
     CHECK_EQUAL((void*) data, (void*) bufdata);
 
     CHECK_EQUAL(*bufdata, 1);
     CHECK_EQUAL(*reinterpret_cast<uint8_t*>(data), 1);
 
     RootedObject shortArray(cx, CreateWithBuffer(cx, buffer, 0, elts / 2));
-    CHECK_EQUAL(JS_GetTypedArrayLength(shortArray), elts / 2);
-    CHECK_EQUAL(JS_GetTypedArrayByteOffset(shortArray), 0);
-    CHECK_EQUAL(JS_GetTypedArrayByteLength(shortArray), nbytes / 2);
+    CHECK_EQUAL(JS_GetTypedArrayLength(shortArray, cx), elts / 2);
+    CHECK_EQUAL(JS_GetTypedArrayByteOffset(shortArray, cx), 0);
+    CHECK_EQUAL(JS_GetTypedArrayByteLength(shortArray, cx), nbytes / 2);
 
     RootedObject ofsArray(cx, CreateWithBuffer(cx, buffer, nbytes / 2, -1));
-    CHECK_EQUAL(JS_GetTypedArrayLength(ofsArray), elts / 2);
-    CHECK_EQUAL(JS_GetTypedArrayByteOffset(ofsArray), nbytes / 2);
-    CHECK_EQUAL(JS_GetTypedArrayByteLength(ofsArray), nbytes / 2);
+    CHECK_EQUAL(JS_GetTypedArrayLength(ofsArray, cx), elts / 2);
+    CHECK_EQUAL(JS_GetTypedArrayByteOffset(ofsArray, cx), nbytes / 2);
+    CHECK_EQUAL(JS_GetTypedArrayByteLength(ofsArray, cx), nbytes / 2);
 
     // Make sure all 3 views reflect the same buffer at the expected locations
     jsval v = INT_TO_JSVAL(39);
     JS_SetElement(cx, array, 0, &v);
     jsval v2;
     CHECK(JS_GetElement(cx, array, 0, &v2));
     CHECK_SAME(v, v2);
     CHECK(JS_GetElement(cx, shortArray, 0, &v2));
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -562,23 +562,23 @@ JSStructuredCloneWriter::checkStack()
 }
 
 JS_PUBLIC_API(JSBool)
 JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v)
 {
     JS_ASSERT(v.isObject());
     RootedObject obj(w->context(), &v.toObject());
 
-    // If the object is a security wrapper, see if we're allowed to unwrap it.
-    // If we aren't, throw.
-    if (obj->isWrapper())
-        obj = UnwrapObjectChecked(obj);
-    if (!obj) {
-        JS_ReportError(w->context(), "Permission denied to access object");
-        return false;
+    // If the object is a security wrapper, try puncturing it. This may throw
+    // if the access is not allowed.
+    if (obj->isWrapper()) {
+        JSObject *unwrapped = UnwrapObjectChecked(w->context(), obj);
+        if (!unwrapped)
+            return false;
+        obj = unwrapped;
     }
     return w->writeTypedArray(obj);
 }
 
 bool
 JSStructuredCloneWriter::writeTypedArray(HandleObject arr)
 {
     if (!out.writePair(ArrayTypeToTag(TypedArray::type(arr)), TypedArray::length(arr)))
@@ -669,21 +669,19 @@ JSStructuredCloneWriter::startWrite(cons
         return out.writePair(SCTAG_NULL, 0);
     } else if (v.isUndefined()) {
         return out.writePair(SCTAG_UNDEFINED, 0);
     } else if (v.isObject()) {
         RootedObject obj(context(), &v.toObject());
 
         // The object might be a security wrapper. See if we can clone what's
         // behind it. If we can, unwrap the object.
-        obj = UnwrapObjectChecked(obj);
-        if (!obj) {
-            JS_ReportError(context(), "Permission denied to access object");
+        obj = UnwrapObjectChecked(context(), obj);
+        if (!obj)
             return false;
-        }
 
         AutoCompartment ac(context(), obj);
 
         bool backref;
         if (!startObject(obj, &backref))
             return false;
         if (backref)
             return true;
@@ -894,33 +892,33 @@ JSStructuredCloneReader::readTypedArray(
 
     if (!obj)
         return false;
     vp->setObject(*obj);
 
     JS_ASSERT(TypedArray::length(obj) == nelems);
     switch (tag) {
       case SCTAG_TYPED_ARRAY_INT8:
-        return in.readArray((uint8_t*) JS_GetInt8ArrayData(obj), nelems);
+        return in.readArray((uint8_t*) JS_GetInt8ArrayData(obj, context()), nelems);
       case SCTAG_TYPED_ARRAY_UINT8:
-        return in.readArray(JS_GetUint8ArrayData(obj), nelems);
+        return in.readArray(JS_GetUint8ArrayData(obj, context()), nelems);
       case SCTAG_TYPED_ARRAY_INT16:
-        return in.readArray((uint16_t*) JS_GetInt16ArrayData(obj), nelems);
+        return in.readArray((uint16_t*) JS_GetInt16ArrayData(obj, context()), nelems);
       case SCTAG_TYPED_ARRAY_UINT16:
-        return in.readArray(JS_GetUint16ArrayData(obj), nelems);
+        return in.readArray(JS_GetUint16ArrayData(obj, context()), nelems);
       case SCTAG_TYPED_ARRAY_INT32:
-        return in.readArray((uint32_t*) JS_GetInt32ArrayData(obj), nelems);
+        return in.readArray((uint32_t*) JS_GetInt32ArrayData(obj, context()), nelems);
       case SCTAG_TYPED_ARRAY_UINT32:
-        return in.readArray(JS_GetUint32ArrayData(obj), nelems);
+        return in.readArray(JS_GetUint32ArrayData(obj, context()), nelems);
       case SCTAG_TYPED_ARRAY_FLOAT32:
-        return in.readArray((uint32_t*) JS_GetFloat32ArrayData(obj), nelems);
+        return in.readArray((uint32_t*) JS_GetFloat32ArrayData(obj, context()), nelems);
       case SCTAG_TYPED_ARRAY_FLOAT64:
-        return in.readArray((uint64_t*) JS_GetFloat64ArrayData(obj), nelems);
+        return in.readArray((uint64_t*) JS_GetFloat64ArrayData(obj, context()), nelems);
       case SCTAG_TYPED_ARRAY_UINT8_CLAMPED:
-        return in.readArray(JS_GetUint8ClampedArrayData(obj), nelems);
+        return in.readArray(JS_GetUint8ClampedArrayData(obj, context()), nelems);
       default:
         JS_NOT_REACHED("unknown TypedArray type");
         return false;
     }
 }
 
 bool
 JSStructuredCloneReader::readArrayBuffer(uint32_t nbytes, Value *vp)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1178,247 +1178,258 @@ JS_NewArrayBuffer(JSContext *cx, uint32_
 
 /*
  * Check whether obj supports JS_GetTypedArray* APIs. Note that this may return
  * false if a security wrapper is encountered that denies the unwrapping. If
  * this test or one of the JS_Is*Array tests succeeds, then it is safe to call
  * the various accessor JSAPI calls defined below.
  */
 extern JS_FRIEND_API(JSBool)
-JS_IsTypedArrayObject(JSObject *obj);
+JS_IsTypedArrayObject(JSObject *obj, JSContext *cx);
 
 /*
  * Check whether obj supports JS_GetArrayBufferView* APIs. Note that this may
  * return false if a security wrapper is encountered that denies the
  * unwrapping. If this test or one of the more specific tests succeeds, then it
  * is safe to call the various ArrayBufferView accessor JSAPI calls defined
- * below.
+ * below. cx MUST be non-NULL and valid.
  */
 extern JS_FRIEND_API(JSBool)
-JS_IsArrayBufferViewObject(JSObject *obj);
+JS_IsArrayBufferViewObject(JSObject *obj, JSContext *cx);
 
 /*
  * Test for specific typed array types (ArrayBufferView subtypes)
  */
 
 extern JS_FRIEND_API(JSBool)
-JS_IsInt8Array(JSObject *obj);
+JS_IsInt8Array(JSObject *obj, JSContext *cx);
 extern JS_FRIEND_API(JSBool)
-JS_IsUint8Array(JSObject *obj);
+JS_IsUint8Array(JSObject *obj, JSContext *cx);
 extern JS_FRIEND_API(JSBool)
-JS_IsUint8ClampedArray(JSObject *obj);
+JS_IsUint8ClampedArray(JSObject *obj, JSContext *cx);
 extern JS_FRIEND_API(JSBool)
-JS_IsInt16Array(JSObject *obj);
+JS_IsInt16Array(JSObject *obj, JSContext *cx);
 extern JS_FRIEND_API(JSBool)
-JS_IsUint16Array(JSObject *obj);
+JS_IsUint16Array(JSObject *obj, JSContext *cx);
 extern JS_FRIEND_API(JSBool)
-JS_IsInt32Array(JSObject *obj);
+JS_IsInt32Array(JSObject *obj, JSContext *cx);
 extern JS_FRIEND_API(JSBool)
-JS_IsUint32Array(JSObject *obj);
+JS_IsUint32Array(JSObject *obj, JSContext *cx);
 extern JS_FRIEND_API(JSBool)
-JS_IsFloat32Array(JSObject *obj);
+JS_IsFloat32Array(JSObject *obj, JSContext *cx);
 extern JS_FRIEND_API(JSBool)
-JS_IsFloat64Array(JSObject *obj);
+JS_IsFloat64Array(JSObject *obj, JSContext *cx);
+
 
 /*
  * 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);
+JS_GetObjectAsInt8Array(JSContext *cx, JSObject *obj, uint32_t *length, int8_t **data);
 extern JS_FRIEND_API(JSObject *)
-JS_GetObjectAsUint8Array(JSObject *obj, uint32_t *length, uint8_t **data);
+JS_GetObjectAsUint8Array(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data);
 extern JS_FRIEND_API(JSObject *)
-JS_GetObjectAsUint8ClampedArray(JSObject *obj, uint32_t *length, uint8_t **data);
+JS_GetObjectAsUint8ClampedArray(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data);
 extern JS_FRIEND_API(JSObject *)
-JS_GetObjectAsInt16Array(JSObject *obj, uint32_t *length, int16_t **data);
+JS_GetObjectAsInt16Array(JSContext *cx, JSObject *obj, uint32_t *length, int16_t **data);
 extern JS_FRIEND_API(JSObject *)
-JS_GetObjectAsUint16Array(JSObject *obj, uint32_t *length, uint16_t **data);
+JS_GetObjectAsUint16Array(JSContext *cx, JSObject *obj, uint32_t *length, uint16_t **data);
 extern JS_FRIEND_API(JSObject *)
-JS_GetObjectAsInt32Array(JSObject *obj, uint32_t *length, int32_t **data);
+JS_GetObjectAsInt32Array(JSContext *cx, JSObject *obj, uint32_t *length, int32_t **data);
 extern JS_FRIEND_API(JSObject *)
-JS_GetObjectAsUint32Array(JSObject *obj, uint32_t *length, uint32_t **data);
+JS_GetObjectAsUint32Array(JSContext *cx, JSObject *obj, uint32_t *length, uint32_t **data);
 extern JS_FRIEND_API(JSObject *)
-JS_GetObjectAsFloat32Array(JSObject *obj, uint32_t *length, float **data);
+JS_GetObjectAsFloat32Array(JSContext *cx, JSObject *obj, uint32_t *length, float **data);
 extern JS_FRIEND_API(JSObject *)
-JS_GetObjectAsFloat64Array(JSObject *obj, uint32_t *length, double **data);
+JS_GetObjectAsFloat64Array(JSContext *cx, JSObject *obj, uint32_t *length, double **data);
 extern JS_FRIEND_API(JSObject *)
-JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data);
+JS_GetObjectAsArrayBufferView(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data);
 extern JS_FRIEND_API(JSObject *)
-JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data);
+JS_GetObjectAsArrayBuffer(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data);
 
 /*
  * Get the type of elements in a typed array.
  *
  * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
  * be known that it would pass such a test: it is a typed array or a wrapper of
- * a typed array, and the unwrapping will succeed.
+ * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG
+ * builds may be unable to assert when unwrapping should be disallowed.
  */
 extern JS_FRIEND_API(JSArrayBufferViewType)
-JS_GetTypedArrayType(JSObject *obj);
+JS_GetTypedArrayType(JSObject *obj, JSContext *maybecx);
 
 /*
  * Check whether obj supports the JS_GetArrayBuffer* APIs. Note that this may
  * return false if a security wrapper is encountered that denies the
  * unwrapping. If this test succeeds, then it is safe to call the various
  * accessor JSAPI calls defined below.
  */
 extern JS_FRIEND_API(JSBool)
-JS_IsArrayBufferObject(JSObject *obj);
+JS_IsArrayBufferObject(JSObject *obj, JSContext *maybecx);
 
 /*
  * Return the available byte length of an array buffer.
  *
  * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known
  * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
- * ArrayBuffer, and the unwrapping will succeed.
+ * ArrayBuffer, and the unwrapping will succeed. If cx is NULL, then DEBUG
+ * builds may be unable to assert when unwrapping should be disallowed.
  */
 extern JS_FRIEND_API(uint32_t)
-JS_GetArrayBufferByteLength(JSObject *obj);
+JS_GetArrayBufferByteLength(JSObject *obj, JSContext *maybecx);
 
 /*
  * Return a pointer to an array buffer's data. The buffer is still owned by the
  * array buffer object, and should not be modified on another thread. The
  * returned pointer is stable across GCs.
  *
  * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known
  * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
- * ArrayBuffer, and the unwrapping will succeed.
+ * ArrayBuffer, and the unwrapping will succeed. If cx is NULL, then DEBUG
+ * builds may be unable to assert when unwrapping should be disallowed.
  */
 extern JS_FRIEND_API(uint8_t *)
-JS_GetArrayBufferData(JSObject *obj);
+JS_GetArrayBufferData(JSObject *obj, JSContext *maybecx);
 
 /*
  * Return the number of elements in a typed array.
  *
  * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
  * be known that it would pass such a test: it is a typed array or a wrapper of
- * a typed array, and the unwrapping will succeed.
+ * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG
+ * builds may be unable to assert when unwrapping should be disallowed.
  */
 extern JS_FRIEND_API(uint32_t)
-JS_GetTypedArrayLength(JSObject *obj);
+JS_GetTypedArrayLength(JSObject *obj, JSContext *cx);
 
 /*
  * Return the byte offset from the start of an array buffer to the start of a
  * typed array view.
  *
  * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
  * be known that it would pass such a test: it is a typed array or a wrapper of
- * a typed array, and the unwrapping will succeed.
+ * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG
+ * builds may be unable to assert when unwrapping should be disallowed.
  */
 extern JS_FRIEND_API(uint32_t)
-JS_GetTypedArrayByteOffset(JSObject *obj);
+JS_GetTypedArrayByteOffset(JSObject *obj, JSContext *cx);
 
 /*
  * Return the byte length of a typed array.
  *
  * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
  * be known that it would pass such a test: it is a typed array or a wrapper of
- * a typed array, and the unwrapping will succeed.
+ * a typed array, and the unwrapping will succeed. If cx is NULL, then DEBUG
+ * builds may be unable to assert when unwrapping should be disallowed.
  */
 extern JS_FRIEND_API(uint32_t)
-JS_GetTypedArrayByteLength(JSObject *obj);
+JS_GetTypedArrayByteLength(JSObject *obj, JSContext *cx);
 
 /*
  * Check whether obj supports JS_ArrayBufferView* APIs. Note that this may
  * return false if a security wrapper is encountered that denies the
  * unwrapping.
  */
 extern JS_FRIEND_API(JSBool)
-JS_IsArrayBufferViewObject(JSObject *obj);
+JS_IsArrayBufferViewObject(JSObject *obj, JSContext *cx);
 
 /*
  * More generic name for JS_GetTypedArrayByteLength to cover DataViews as well
  */
 extern JS_FRIEND_API(uint32_t)
-JS_GetArrayBufferViewByteLength(JSObject *obj);
+JS_GetArrayBufferViewByteLength(JSObject *obj, JSContext *cx);
 
 /*
  * Return a pointer to the start of the data referenced by a typed array. The
  * data is still owned by the typed array, and should not be modified on
  * another thread.
  *
  * |obj| must have passed a JS_Is*Array test, or somehow be known that it would
  * pass such a test: it is a typed array or a wrapper of a typed array, and the
- * unwrapping will succeed.
+ * unwrapping will succeed. If cx is NULL, then DEBUG builds may be unable to
+ * assert when unwrapping should be disallowed.
  */
 
 extern JS_FRIEND_API(int8_t *)
-JS_GetInt8ArrayData(JSObject *obj);
+JS_GetInt8ArrayData(JSObject *obj, JSContext *maybecx);
 extern JS_FRIEND_API(uint8_t *)
-JS_GetUint8ArrayData(JSObject *obj);
+JS_GetUint8ArrayData(JSObject *obj, JSContext *maybecx);
 extern JS_FRIEND_API(uint8_t *)
-JS_GetUint8ClampedArrayData(JSObject *obj);
+JS_GetUint8ClampedArrayData(JSObject *obj, JSContext *maybecx);
 extern JS_FRIEND_API(int16_t *)
-JS_GetInt16ArrayData(JSObject *obj);
+JS_GetInt16ArrayData(JSObject *obj, JSContext *maybecx);
 extern JS_FRIEND_API(uint16_t *)
-JS_GetUint16ArrayData(JSObject *obj);
+JS_GetUint16ArrayData(JSObject *obj, JSContext *maybecx);
 extern JS_FRIEND_API(int32_t *)
-JS_GetInt32ArrayData(JSObject *obj);
+JS_GetInt32ArrayData(JSObject *obj, JSContext *maybecx);
 extern JS_FRIEND_API(uint32_t *)
-JS_GetUint32ArrayData(JSObject *obj);
+JS_GetUint32ArrayData(JSObject *obj, JSContext *maybecx);
 extern JS_FRIEND_API(float *)
-JS_GetFloat32ArrayData(JSObject *obj);
+JS_GetFloat32ArrayData(JSObject *obj, JSContext *maybecx);
 extern JS_FRIEND_API(double *)
-JS_GetFloat64ArrayData(JSObject *obj);
+JS_GetFloat64ArrayData(JSObject *obj, JSContext *maybecx);
 
 /*
  * Same as above, but for any kind of ArrayBufferView. Prefer the type-specific
  * versions when possible.
  */
 extern JS_FRIEND_API(void *)
-JS_GetArrayBufferViewData(JSObject *obj);
+JS_GetArrayBufferViewData(JSObject *obj, JSContext *maybecx);
 
 /*
  * 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);
+JS_GetArrayBufferViewBuffer(JSObject *obj, JSContext *maybecx);
 
 /*
- * Check whether obj supports JS_GetDataView* APIs.
+ * Check whether obj supports JS_GetDataView* APIs. Note that this may fail and
+ * throw an exception if a security wrapper is encountered that denies the
+ * operation.
  */
 JS_FRIEND_API(JSBool)
-JS_IsDataViewObject(JSObject *obj);
+JS_IsDataViewObject(JSContext *cx, JSObject *obj, JSBool *isDataView);
 
 /*
  * Return the byte offset of a data view into its array buffer. |obj| must be a
  * DataView.
  *
  * |obj| must have passed a JS_IsDataViewObject test, or somehow be known that
  * it would pass such a test: it is a data view or a wrapper of a data view,
- * and the unwrapping will succeed.
+ * and the unwrapping will succeed. If cx is NULL, then DEBUG builds may be
+ * unable to assert when unwrapping should be disallowed.
  */
 JS_FRIEND_API(uint32_t)
-JS_GetDataViewByteOffset(JSObject *obj);
+JS_GetDataViewByteOffset(JSObject *obj, JSContext *maybecx);
 
 /*
  * Return the byte length of a data view.
  *
  * |obj| must have passed a JS_IsDataViewObject test, or somehow be known that
  * it would pass such a test: it is a data view or a wrapper of a data view,
  * and the unwrapping will succeed. If cx is NULL, then DEBUG builds may be
  * unable to assert when unwrapping should be disallowed.
  */
 JS_FRIEND_API(uint32_t)
-JS_GetDataViewByteLength(JSObject *obj);
+JS_GetDataViewByteLength(JSObject *obj, JSContext *maybecx);
 
 /*
  * Return a pointer to the beginning of the data referenced by a DataView.
  *
  * |obj| must have passed a JS_IsDataViewObject test, or somehow be known that
  * it would pass such a test: it is a data view or a wrapper of a data view,
  * and the unwrapping will succeed. If cx is NULL, then DEBUG builds may be
  * unable to assert when unwrapping should be disallowed.
  */
 JS_FRIEND_API(void *)
-JS_GetDataViewData(JSObject *obj);
+JS_GetDataViewData(JSObject *obj, JSContext *maybecx);
 
 /*
  * This struct contains metadata passed from the DOM to the JS Engine for JIT
  * optimizations on DOM property accessors. Eventually, this should be made
  * available to general JSAPI users, but we are not currently ready to do so.
  */
 typedef bool
 (* JSJitPropertyOp)(JSContext *cx, JSHandleObject thisObj,
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -169,20 +169,26 @@ fun_getProperty(JSContext *cx, HandleObj
             return true;
         }
 
         vp.set(iter.calleev());
         if (!cx->compartment->wrap(cx, vp.address()))
             return false;
 
         /*
-         * Censor the caller if we don't have full access to it.
+         * Censor the caller if we can't PUNCTURE it.
+         *
+         * NB - This will get much much nicer with bug 800915
          */
         JSObject &caller = vp.toObject();
-        if (caller.isWrapper() && !Wrapper::wrapperHandler(&caller)->isSafeToUnwrap()) {
+        JSErrorReporter reporter = JS_SetErrorReporter(cx, NULL);
+        bool punctureThrew = !UnwrapObjectChecked(cx, &caller);
+        JS_SetErrorReporter(cx, reporter);
+        if (punctureThrew) {
+            JS_ClearPendingException(cx);
             vp.setNull();
         } else if (caller.isFunction()) {
             JSFunction *callerFun = caller.toFunction();
             if (callerFun->isInterpreted() && callerFun->inStrictMode()) {
                 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
                                              JSMSG_CALLER_IS_STRICT);
                 return false;
             }
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -1868,21 +1868,19 @@ class TypedArrayTemplate
              * construct the new typed array in the compartment of the buffer,
              * so that the typed array can point directly at their buffer's
              * data without crossing compartment boundaries. So we use the
              * machinery underlying NonGenericMethodGuard directly to proxy the
              * native call. We will end up with a wrapper in the origin
              * compartment for a view in the target compartment referencing the
              * ArrayBuffer in that same compartment.
              */
-            JSObject *wrapped = UnwrapObjectChecked(bufobj);
-            if (!wrapped) {
-                JS_ReportError(cx, "Permission denied to access object");
+            JSObject *wrapped = UnwrapObjectChecked(cx, bufobj);
+            if (!wrapped)
                 return NULL;
-            }
             if (wrapped->isArrayBuffer()) {
                 /*
                  * And for even more fun, the new view's prototype should be
                  * set to the origin compartment's prototype object, not the
                  * target's (specifically, the actual view in the target
                  * compartment will use as its prototype a wrapper around the
                  * origin compartment's view.prototype object).
                  *
@@ -3138,41 +3136,50 @@ JSFunctionSpec _typedArray::jsfuncs[] = 
                                JSObject *arrayBuffer_, uint32_t byteoffset, int32_t length)  \
   {                                                                                          \
       MOZ_ASSERT(byteoffset <= INT32_MAX);                                                   \
       Rooted<JSObject*> arrayBuffer(cx, arrayBuffer_);                                       \
       Rooted<JSObject*> proto(cx, NULL);                                                     \
       return TypedArrayTemplate<NativeType>::fromBuffer(cx, arrayBuffer, byteoffset, length, \
                                                         proto);                              \
   }                                                                                          \
-  JS_FRIEND_API(JSBool) JS_Is ## Name ## Array(JSObject *obj)                                \
+  JS_FRIEND_API(JSBool) JS_Is ## Name ## Array(JSObject *obj, JSContext *cx)                 \
   {                                                                                          \
-      if (!(obj = UnwrapObjectChecked(obj)))                                                 \
+      MOZ_ASSERT(!cx->isExceptionPending());                                                 \
+      if (!(obj = UnwrapObjectChecked(cx, obj))) {                                           \
+          cx->clearPendingException();                                                       \
           return false;                                                                      \
+      }                                                                                      \
       Class *clasp = obj->getClass();                                                        \
       return (clasp == &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)
 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float32, float)
 IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double)
 
 #define IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Name, ExternalType, InternalType)              \
-  JS_FRIEND_API(JSObject *) JS_GetObjectAs ## Name ## Array(JSObject *obj,                  \
+  JS_FRIEND_API(JSObject *) JS_GetObjectAs ## Name ## Array(JSContext *cx,                  \
+                                                            JSObject *obj,                  \
                                                             uint32_t *length,               \
                                                             ExternalType **data)            \
   {                                                                                         \
-      if (!(obj = UnwrapObjectChecked(obj)))                                                \
-          return NULL;                                                                      \
+      if (obj->isWrapper()) {                                                               \
+          MOZ_ASSERT(!cx->isExceptionPending());                                            \
+          if (!(obj = UnwrapObjectChecked(cx, obj))) {                                      \
+              cx->clearPendingException();                                                  \
+              return NULL;                                                                  \
+          }                                                                                 \
+      }                                                                                     \
                                                                                             \
       Class *clasp = obj->getClass();                                                       \
       if (clasp != &TypedArray::classes[TypedArrayTemplate<InternalType>::ArrayTypeID()])   \
           return NULL;                                                                      \
                                                                                             \
       *length = TypedArray::length(obj);                                                    \
       *data = static_cast<ExternalType *>(TypedArray::viewData(obj));                       \
                                                                                             \
@@ -3559,52 +3566,86 @@ js_InitTypedArrayClasses(JSContext *cx, 
         return NULL;
     }
 
     return InitArrayBufferClass(cx);
 }
 
 /* JS Friend API */
 
-JS_FRIEND_API(JSBool)
-JS_IsArrayBufferObject(JSObject *obj)
+// The typed array friend API defines a number of accessor functions that want
+// to unwrap an argument, but in certain rare cases may not have a cx available
+// and so pass in NULL instead. Use UnwrapObjectChecked when possible.
+static JSObject *
+CheckedUnwrap(JSContext *cx, JSObject *obj)
 {
-    obj = UnwrapObjectChecked(obj);
-    return obj ? obj->isArrayBuffer() : false;
+    if (!cx)
+        return UnwrapObject(obj);
+    MOZ_ASSERT(!cx->isExceptionPending());
+    obj = UnwrapObjectChecked(cx, obj);
+    MOZ_ASSERT(obj);
+    return obj;
 }
 
 JS_FRIEND_API(JSBool)
-JS_IsTypedArrayObject(JSObject *obj)
+JS_IsArrayBufferObject(JSObject *objArg, JSContext *cx)
 {
-    obj = UnwrapObjectChecked(obj);
-    return obj ? obj->isTypedArray() : false;
+    RootedObject obj_(cx, objArg);
+    MOZ_ASSERT(!cx->isExceptionPending());
+    JSObject *obj = UnwrapObjectChecked(cx, obj_);
+    if (!obj) {
+        cx->clearPendingException();
+        return false;
+    }
+    return obj->isArrayBuffer();
 }
 
 JS_FRIEND_API(JSBool)
-JS_IsArrayBufferViewObject(JSObject *obj)
+JS_IsTypedArrayObject(JSObject *objArg, JSContext *cx)
 {
-    obj = UnwrapObjectChecked(obj);
-    return obj ? (obj->isTypedArray() || obj->isDataView()) : false;
+    RootedObject obj_(cx, objArg);
+    MOZ_ASSERT(!cx->isExceptionPending());
+    JSObject *obj = UnwrapObjectChecked(cx, obj_);
+    if (!obj) {
+        cx->clearPendingException();
+        return false;
+    }
+    return obj->isTypedArray();
+}
+
+JS_FRIEND_API(JSBool)
+JS_IsArrayBufferViewObject(JSObject *objArg, JSContext *cx)
+{
+    RootedObject obj_(cx, objArg);
+    MOZ_ASSERT(!cx->isExceptionPending());
+    JSObject *obj = UnwrapObjectChecked(cx, obj_);
+    if (!obj) {
+        cx->clearPendingException();
+        return false;
+    }
+    return obj->isTypedArray() || obj->isDataView();
 }
 
 JS_FRIEND_API(uint32_t)
-JS_GetArrayBufferByteLength(JSObject *obj)
+JS_GetArrayBufferByteLength(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
-    return obj ? obj->asArrayBuffer().byteLength() : 0;
+    obj = CheckedUnwrap(maybecx, obj);
+    if (!obj)
+        return 0;
+    return obj->asArrayBuffer().byteLength();
 }
 
 JS_FRIEND_API(uint8_t *)
-JS_GetArrayBufferData(JSObject *obj)
+JS_GetArrayBufferData(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return NULL;
     ArrayBufferObject &buffer = obj->asArrayBuffer();
-    if (!buffer.uninlineData(NULL))
+    if (!buffer.uninlineData(maybecx))
         return NULL;
     return buffer.dataPointer();
 }
 
 JS_FRIEND_API(JSObject *)
 JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes)
 {
     JS_ASSERT(nbytes <= INT32_MAX);
@@ -3634,258 +3675,271 @@ JS_AllocateArrayBufferContents(JSContext
     *data = reinterpret_cast<uint8_t*>(header->elements());
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_StealArrayBufferContents(JSContext *cx, JSObject *obj, void **contents,
                             uint8_t **data)
 {
-    if (!(obj = UnwrapObjectChecked(obj)))
+    if (!(obj = UnwrapObjectChecked(cx, obj)))
         return false;
 
     if (!obj->isArrayBuffer()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
         return false;
     }
 
     if (!ArrayBufferObject::stealContents(cx, obj, contents, data))
         return false;
 
     return true;
 }
 
 JS_FRIEND_API(uint32_t)
-JS_GetTypedArrayLength(JSObject *obj)
+JS_GetTypedArrayLength(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return 0;
     JS_ASSERT(obj->isTypedArray());
     return TypedArray::length(obj);
 }
 
 JS_FRIEND_API(uint32_t)
-JS_GetTypedArrayByteOffset(JSObject *obj)
+JS_GetTypedArrayByteOffset(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return 0;
     JS_ASSERT(obj->isTypedArray());
     return TypedArray::byteOffset(obj);
 }
 
 JS_FRIEND_API(uint32_t)
-JS_GetTypedArrayByteLength(JSObject *obj)
+JS_GetTypedArrayByteLength(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return 0;
     JS_ASSERT(obj->isTypedArray());
     return TypedArray::byteLength(obj);
 }
 
 JS_FRIEND_API(JSArrayBufferViewType)
-JS_GetTypedArrayType(JSObject *obj)
+JS_GetTypedArrayType(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return ArrayBufferView::TYPE_MAX;
     JS_ASSERT(obj->isTypedArray());
     return static_cast<JSArrayBufferViewType>(TypedArray::type(obj));
 }
 
 JS_FRIEND_API(int8_t *)
-JS_GetInt8ArrayData(JSObject *obj)
+JS_GetInt8ArrayData(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isTypedArray());
     JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_INT8);
     return static_cast<int8_t *>(TypedArray::viewData(obj));
 }
 
 JS_FRIEND_API(uint8_t *)
-JS_GetUint8ArrayData(JSObject *obj)
+JS_GetUint8ArrayData(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isTypedArray());
     JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_UINT8);
     return static_cast<uint8_t *>(TypedArray::viewData(obj));
 }
 
 JS_FRIEND_API(uint8_t *)
-JS_GetUint8ClampedArrayData(JSObject *obj)
+JS_GetUint8ClampedArrayData(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isTypedArray());
     JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_UINT8_CLAMPED);
     return static_cast<uint8_t *>(TypedArray::viewData(obj));
 }
 
 JS_FRIEND_API(int16_t *)
-JS_GetInt16ArrayData(JSObject *obj)
+JS_GetInt16ArrayData(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isTypedArray());
     JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_INT16);
     return static_cast<int16_t *>(TypedArray::viewData(obj));
 }
 
 JS_FRIEND_API(uint16_t *)
-JS_GetUint16ArrayData(JSObject *obj)
+JS_GetUint16ArrayData(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isTypedArray());
     JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_UINT16);
     return static_cast<uint16_t *>(TypedArray::viewData(obj));
 }
 
 JS_FRIEND_API(int32_t *)
-JS_GetInt32ArrayData(JSObject *obj)
+JS_GetInt32ArrayData(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isTypedArray());
     JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_INT32);
     return static_cast<int32_t *>(TypedArray::viewData(obj));
 }
 
 JS_FRIEND_API(uint32_t *)
-JS_GetUint32ArrayData(JSObject *obj)
+JS_GetUint32ArrayData(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isTypedArray());
     JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_UINT32);
     return static_cast<uint32_t *>(TypedArray::viewData(obj));
 }
 
 JS_FRIEND_API(float *)
-JS_GetFloat32ArrayData(JSObject *obj)
+JS_GetFloat32ArrayData(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isTypedArray());
     JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_FLOAT32);
     return static_cast<float *>(TypedArray::viewData(obj));
 }
 
 JS_FRIEND_API(double *)
-JS_GetFloat64ArrayData(JSObject *obj)
+JS_GetFloat64ArrayData(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isTypedArray());
     JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_FLOAT64);
     return static_cast<double *>(TypedArray::viewData(obj));
 }
 
 JS_FRIEND_API(JSBool)
-JS_IsDataViewObject(JSObject *obj)
+JS_IsDataViewObject(JSContext *cx, JSObject *objArg, JSBool *isDataView)
 {
-    obj = UnwrapObjectChecked(obj);
-    return obj ? obj->isDataView() : false;
+    RootedObject obj_(cx, objArg);
+    JSObject *obj = CheckedUnwrap(cx, obj_);
+    if (!obj)
+        return false;
+    *isDataView = obj->isDataView();
+    return true;
 }
 
 JS_FRIEND_API(uint32_t)
-JS_GetDataViewByteOffset(JSObject *obj)
+JS_GetDataViewByteOffset(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return 0;
     return obj->asDataView().byteOffset();
 }
 
 JS_FRIEND_API(void *)
-JS_GetDataViewData(JSObject *obj)
+JS_GetDataViewData(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isDataView());
     return obj->asDataView().dataPointer();
 }
 
 JS_FRIEND_API(uint32_t)
-JS_GetDataViewByteLength(JSObject *obj)
+JS_GetDataViewByteLength(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return 0;
     JS_ASSERT(obj->isDataView());
     return obj->asDataView().byteLength();
 }
 
 JS_FRIEND_API(void *)
-JS_GetArrayBufferViewData(JSObject *obj)
+JS_GetArrayBufferViewData(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isTypedArray() || obj->isDataView());
     return obj->isDataView() ? obj->asDataView().dataPointer() : TypedArray::viewData(obj);
 }
 
 JS_FRIEND_API(JSObject *)
-JS_GetArrayBufferViewBuffer(JSObject *obj)
+JS_GetArrayBufferViewBuffer(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isTypedArray() || obj->isDataView());
     return &obj->getFixedSlot(BufferView::BUFFER_SLOT).toObject();
 }
 
 JS_FRIEND_API(uint32_t)
-JS_GetArrayBufferViewByteLength(JSObject *obj)
+JS_GetArrayBufferViewByteLength(JSObject *obj, JSContext *maybecx)
 {
-    obj = UnwrapObjectChecked(obj);
+    obj = CheckedUnwrap(maybecx, obj);
     if (!obj)
         return 0;
     JS_ASSERT(obj->isTypedArray() || obj->isDataView());
     return obj->isDataView()
            ? obj->asDataView().byteLength()
            : TypedArray::byteLengthValue(obj).toInt32();
 }
 
 JS_FRIEND_API(JSObject *)
-JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data)
+JS_GetObjectAsArrayBufferView(JSContext *cx, JSObject *obj,
+                              uint32_t *length, uint8_t **data)
 {
-    if (!(obj = UnwrapObjectChecked(obj)))
-        return NULL;
+    if (obj->isWrapper()) {
+        if (!(obj = UnwrapObjectChecked(cx, obj))) {
+            cx->clearPendingException();
+            return NULL;
+        }
+    }
     if (!(obj->isTypedArray() || obj->isDataView()))
         return NULL;
 
     *length = obj->isDataView() ? obj->asDataView().byteLength()
                                 : TypedArray::byteLengthValue(obj).toInt32();
 
     *data = static_cast<uint8_t *>(obj->isDataView() ? obj->asDataView().dataPointer()
                                                      : TypedArray::viewData(obj));
     return obj;
 }
 
 JS_FRIEND_API(JSObject *)
-JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data)
+JS_GetObjectAsArrayBuffer(JSContext *cx, JSObject *obj, uint32_t *length, uint8_t **data)
 {
-   if (!(obj = UnwrapObjectChecked(obj)))
-       return NULL;
+    if (obj->isWrapper()) {
+        if (!(obj = UnwrapObjectChecked(cx, obj))) {
+            cx->clearPendingException();
+            return NULL;
+        }
+    }
     if (!obj->isArrayBuffer())
         return NULL;
 
     *length = obj->asArrayBuffer().byteLength();
     *data = obj->asArrayBuffer().dataPointer();
 
     return obj;
 }
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -104,38 +104,45 @@ js::UnwrapObject(JSObject *wrapped, bool
         wrapped = GetProxyPrivate(wrapped).toObjectOrNull();
     }
     if (flagsp)
         *flagsp = flags;
     return wrapped;
 }
 
 JS_FRIEND_API(JSObject *)
-js::UnwrapObjectChecked(RawObject obj)
+js::UnwrapObjectChecked(JSContext *cx, RawObject objArg)
 {
+    RootedObject obj(cx, objArg);
     while (true) {
         JSObject *wrapper = obj;
-        obj = UnwrapOneChecked(obj);
+        obj = UnwrapOneChecked(cx, obj);
         if (!obj || obj == wrapper)
             return obj;
     }
 }
 
 JS_FRIEND_API(JSObject *)
-js::UnwrapOneChecked(RawObject obj)
+js::UnwrapOneChecked(JSContext *cx, HandleObject obj)
 {
     // Checked unwraps should never unwrap outer windows.
     if (!obj->isWrapper() ||
         JS_UNLIKELY(!!obj->getClass()->ext.innerObject))
     {
         return obj;
     }
 
     Wrapper *handler = Wrapper::wrapperHandler(obj);
-    return handler->isSafeToUnwrap() ? Wrapper::wrappedObject(obj) : NULL;
+    bool rvOnFailure;
+    if (!handler->enter(cx, obj, JSID_VOID, Wrapper::PUNCTURE, &rvOnFailure))
+        return rvOnFailure ? (JSObject*) obj : NULL;
+    JSObject *ret = Wrapper::wrappedObject(obj);
+    JS_ASSERT(ret);
+
+    return ret;
 }
 
 bool
 js::IsCrossCompartmentWrapper(RawObject wrapper)
 {
     return wrapper->isWrapper() &&
            !!(Wrapper::wrapperHandler(wrapper)->flags() & Wrapper::CROSS_COMPARTMENT);
 }
@@ -148,17 +155,16 @@ js::IsCrossCompartmentWrapper(RawObject 
         return (op);                                                         \
     JS_END_MACRO
 
 #define SET(action) CHECKED(action, SET)
 #define GET(action) CHECKED(action, GET)
 
 Wrapper::Wrapper(unsigned flags, bool hasPrototype) : DirectProxyHandler(&sWrapperFamily)
                                                     , mFlags(flags)
-                                                    , mSafeToUnwrap(true)
 {
     setHasPrototype(hasPrototype);
 }
 
 Wrapper::~Wrapper()
 {
 }
 
@@ -210,32 +216,35 @@ Wrapper::enumerate(JSContext *cx, JSObje
 {
     JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype.
     // if we refuse to perform this action, props remains empty
     static jsid id = JSID_VOID;
     GET(DirectProxyHandler::enumerate(cx, wrapper, props));
 }
 
 /*
- * Ordinarily, the convert trap would require unwrapping. However, the default
+ * Ordinarily, the convert trap would require a PUNCTURE. However, the default
  * implementation of convert, JS_ConvertStub, obtains a default value by calling
- * the toString/valueOf method on the wrapper, if any. Throwing if we can't unwrap
- * in this case would be overly conservative. To make matters worse, XPConnect sometimes
+ * the toString/valueOf method on the wrapper, if any. Doing a PUNCTURE in this
+ * case would be overly conservative. To make matters worse, XPConnect sometimes
  * installs a custom convert trap that obtains a default value by calling the
  * toString method on the wrapper. Doing a puncture in this case would be overly
- * conservative as well. We deal with these anomalies by falling back to the DefaultValue
- * algorithm whenever unwrapping is forbidden.
+ * conservative as well. We deal with these anomalies by clearing the pending
+ * exception and falling back to the DefaultValue algorithm whenever the
+ * PUNCTURE fails.
  */
 bool
 Wrapper::defaultValue(JSContext *cx, JSObject *wrapper_, JSType hint, Value *vp)
 {
     RootedObject wrapper(cx, wrapper_);
 
-    if (!wrapperHandler(wrapper)->isSafeToUnwrap()) {
+    bool status;
+    if (!enter(cx, wrapper_, JSID_VOID, PUNCTURE, &status)) {
         RootedValue v(cx);
+        JS_ClearPendingException(cx);
         if (!DefaultValue(cx, wrapper, hint, &v))
             return false;
         *vp = v;
         return true;
     }
     /*
      * We enter the compartment of the wrappee here, even if we're not a cross
      * compartment wrapper. Moreover, cross compartment wrappers do not enter
@@ -782,19 +791,17 @@ CrossCompartmentWrapper::getPrototypeOf(
 
 CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
 
 /* Security wrappers. */
 
 template <class Base>
 SecurityWrapper<Base>::SecurityWrapper(unsigned flags)
   : Base(flags)
-{
-    Base::setSafeToUnwrap(false);
-}
+{}
 
 template <class Base>
 bool
 SecurityWrapper<Base>::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                                   CallArgs args)
 {
     /*
      * Let this through until compartment-per-global lets us have stronger
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -24,39 +24,30 @@ class DummyFrameGuard;
  * Handler in the sense that it can be "unwrapped" in C++, exposing the underlying
  * object (Direct Proxy Handlers have an underlying target object, but don't
  * expect to expose this object via any kind of unwrapping operation). Callers
  * should be careful to avoid unwrapping security wrappers in the wrong context.
  */
 class JS_FRIEND_API(Wrapper) : public DirectProxyHandler
 {
     unsigned mFlags;
-    bool mSafeToUnwrap;
 
   public:
     enum Action {
         GET,
         SET,
-        CALL
+        CALL,
+        PUNCTURE
     };
 
     enum Flags {
         CROSS_COMPARTMENT = 1 << 0,
         LAST_USED_FLAG = CROSS_COMPARTMENT
     };
 
-    /*
-     * Wrappers can explicitly specify that they are unsafe to unwrap from a
-     * security perspective (as is the case for SecurityWrappers). If a wrapper
-     * is not safe to unwrap, operations requiring full access to the underlying
-     * object (via UnwrapObjectChecked) will throw. Otherwise, they will succeed.
-     */
-    void setSafeToUnwrap(bool safe) { mSafeToUnwrap = safe; };
-    bool isSafeToUnwrap() { return mSafeToUnwrap; };
-
     static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto,
                          JSObject *parent, Wrapper *handler);
 
     static JSObject *Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler);
 
     static Wrapper *wrapperHandler(RawObject wrapper);
 
     static JSObject *wrappedObject(RawObject wrapper);
@@ -66,17 +57,34 @@ class JS_FRIEND_API(Wrapper) : public Di
     }
 
     /* Policy enforcement traps.
      *
      * enter() allows the policy to specify whether the caller may perform |act|
      * on the underlying object's |id| property. In the case when |act| is CALL,
      * |id| is generally JSID_VOID.
      *
-     * The |act| parameter to enter() specifies the action being performed.
+     * The |act| parameter to enter() specifies the action being performed. GET,
+     * SET, and CALL are self-explanatory, but PUNCTURE requires more
+     * explanation:
+     *
+     * GET and SET allow for a very fine-grained security membrane, through
+     * which access can be granted or denied on a per-property, per-object, and
+     * per-action basis. Sometimes though, we just want to asks if we can access
+     * _everything_ behind the wrapper barrier. For example, when the structured
+     * clone algorithm runs up against a cross-compartment wrapper, it needs to
+     * know whether it can enter the compartment and keep cloning, or whether it
+     * should throw. This is the role of PUNCTURE.
+     *
+     * PUNCTURE allows the policy to specify whether the wrapper barrier may
+     * be lifted - that is to say, whether the caller is allowed to access
+     * anything that the wrapped object could access. This is a very powerful
+     * permission, and thus should generally be denied for security wrappers
+     * except under very special circumstances. When |act| is PUNCTURE, |id|
+     * should be JSID_VOID.
      */
     virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act,
                        bool *bp);
 
     explicit Wrapper(unsigned flags, bool hasPrototype = false);
 
     virtual ~Wrapper();
 
@@ -250,22 +258,22 @@ IsWrapper(RawObject obj)
 JS_FRIEND_API(JSObject *)
 UnwrapObject(JSObject *obj, bool stopAtOuter = true, unsigned *flagsp = NULL);
 
 // Given a JSObject, returns that object stripped of wrappers. At each stage,
 // the security wrapper has the opportunity to veto the unwrap. Since checked
 // code should never be unwrapping outer window wrappers, we always stop at
 // outer windows.
 JS_FRIEND_API(JSObject *)
-UnwrapObjectChecked(RawObject obj);
+UnwrapObjectChecked(JSContext *cx, RawObject obj);
 
 // Unwrap only the outermost security wrapper, with the same semantics as
 // above. This is the checked version of Wrapper::wrappedObject.
 JS_FRIEND_API(JSObject *)
-UnwrapOneChecked(RawObject obj);
+UnwrapOneChecked(JSContext *cx, HandleObject obj);
 
 JS_FRIEND_API(bool)
 IsCrossCompartmentWrapper(RawObject obj);
 
 bool
 IsDeadProxyObject(RawObject obj);
 
 JSObject *
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1814,21 +1814,19 @@ Debugger::unwrapDebuggeeArgument(JSConte
     if (obj->getClass() == &DebuggerObject_class) {
         Value rv = v;
         if (!unwrapDebuggeeValue(cx, &rv))
             return NULL;
         obj = &rv.toObject();
     }
 
     /* If we have a cross-compartment wrapper, dereference as far as is secure. */
-    obj = UnwrapObjectChecked(obj);
-    if (!obj) {
-        JS_ReportError(cx, "Permission denied to access object");
+    obj = UnwrapObjectChecked(cx, obj);
+    if (!obj)
         return NULL;
-    }
 
     /* If that produced an outer window, innerize it. */
     obj = GetInnerObject(cx, obj);
     if (!obj)
         return NULL;
 
     /* If that didn't produce a global object, it's an error. */
     if (!obj->isGlobal()) {
@@ -4462,18 +4460,28 @@ DebuggerObject_evalInGlobalWithBindings(
     return DebuggerGenericEval(cx, "Debugger.Object.prototype.evalInGlobalWithBindings",
                                args[0], &args[1], vp, dbg, referent, NULL);
 }
 
 static JSBool
 DebuggerObject_unwrap(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "unwrap", args, dbg, referent);
-    JSObject *unwrapped = UnwrapOneChecked(referent);
+    JSObject *unwrapped = UnwrapOneChecked(cx, referent);
     if (!unwrapped) {
+        // If we were terminated, then pass that along.
+        if (!cx->isExceptionPending())
+            return false;
+
+        // If the unwrap operation threw an exception, assume it's a
+        // security exception, and return null. It seems like the wrappers
+        // in use in Firefox just call JS_ReportError, so we have no way to
+        // distinguish genuine should-not-unwrap errors from other kinds of
+        // errors.
+        cx->clearPendingException();
         vp->setNull();
         return true;
     }
 
     *vp = ObjectValue(*unwrapped);
     if (!dbg->wrapDebuggeeValue(cx, vp))
         return false;
     return true;
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -2706,17 +2706,17 @@ nsXPCComponents_Utils::LookupMethod(cons
         return NS_ERROR_XPC_BAD_CONVERT_JS;
     JSString *methodName = name.toString();
     jsid methodId = INTERNED_STRING_TO_JSID(cx, JS_InternJSString(cx, methodName));
 
     // If |obj| is a cross-compartment wrapper, try to puncture it. If this fails,
     // we don't have full access to the other compartment, in which case we throw.
     // Otherwise, enter the compartment.
     if (js::IsCrossCompartmentWrapper(obj)) {
-        obj = js::UnwrapOneChecked(obj);
+        obj = js::UnwrapOneChecked(cx, obj);
         if (!obj)
             return NS_ERROR_XPC_BAD_CONVERT_JS;
     }
 
     {
         // Enter the target compartment.
         JSAutoCompartment ac(cx, obj);
 
@@ -3858,17 +3858,17 @@ nsXPCComponents_Utils::EvalInSandbox(con
 
 nsresult
 xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
                   const char *filename, int32_t lineNo,
                   JSVersion jsVersion, bool returnStringOnly, jsval *rval)
 {
     JS_AbortIfWrongThread(JS_GetRuntime(cx));
 
-    sandbox = js::UnwrapObjectChecked(sandbox);
+    sandbox = js::UnwrapObjectChecked(cx, sandbox);
     if (!sandbox || js::GetObjectJSClass(sandbox) != &SandboxClass) {
         return NS_ERROR_INVALID_ARG;
     }
 
     nsIScriptObjectPrincipal *sop =
         (nsIScriptObjectPrincipal*)xpc_GetJSPrivate(sandbox);
     NS_ASSERTION(sop, "Invalid sandbox passed");
     nsCOMPtr<nsIPrincipal> prin = sop->GetPrincipal();
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -1478,17 +1478,18 @@ failure:
 
 
 
 // Check that the tag part of the type matches the type
 // of the array. If the check succeeds, check that the size
 // of the output does not exceed UINT32_MAX bytes. Allocate
 // the memory and copy the elements by memcpy.
 static JSBool
-CheckTargetAndPopulate(const nsXPTType& type,
+CheckTargetAndPopulate(JSContext *cx,
+                       const nsXPTType& type,
                        uint8_t requiredType,
                        size_t typeSize,
                        uint32_t count,
                        JSObject* tArr,
                        void** output,
                        nsresult* pErr)
 {
     // Check that the element type expected by the interface matches
@@ -1509,112 +1510,113 @@ CheckTargetAndPopulate(const nsXPTType& 
     size_t byteSize = count * typeSize;
     if (count > max || !(*output = nsMemory::Alloc(byteSize))) {
         if (pErr)
             *pErr = NS_ERROR_OUT_OF_MEMORY;
 
         return false;
     }
 
-    memcpy(*output, JS_GetArrayBufferViewData(tArr), byteSize);
+    memcpy(*output, JS_GetArrayBufferViewData(tArr, cx), byteSize);
     return true;
 }
 
 // Fast conversion of typed arrays to native using memcpy.
 // No float or double canonicalization is done. Called by
 // JSarray2Native whenever a TypedArray is met. ArrayBuffers
 // are not accepted; create a properly typed array view on them
 // first. The element type of array must match the XPCOM
 // type in size, type and signedness exactly. As an exception,
 // Uint8ClampedArray is allowed for arrays of uint8_t.
 
 // static
 JSBool
-XPCConvert::JSTypedArray2Native(void** d,
+XPCConvert::JSTypedArray2Native(JSContext* cx,
+                                void** d,
                                 JSObject* jsArray,
                                 uint32_t count,
                                 const nsXPTType& type,
                                 nsresult* pErr)
 {
     NS_ABORT_IF_FALSE(jsArray, "bad param");
     NS_ABORT_IF_FALSE(d, "bad param");
-    NS_ABORT_IF_FALSE(JS_IsTypedArrayObject(jsArray), "not a typed array");
+    NS_ABORT_IF_FALSE(JS_IsTypedArrayObject(jsArray, cx), "not a typed array");
 
     // Check the actual length of the input array against the
     // given size_is.
-    uint32_t len = JS_GetTypedArrayLength(jsArray);
+    uint32_t len = JS_GetTypedArrayLength(jsArray, cx);
     if (len < count) {
         if (pErr)
             *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
 
         return false;
     }
 
     void* output = nullptr;
 
-    switch (JS_GetTypedArrayType(jsArray)) {
+    switch (JS_GetTypedArrayType(jsArray, cx)) {
     case js::ArrayBufferView::TYPE_INT8:
-        if (!CheckTargetAndPopulate(nsXPTType::T_I8, type,
+        if (!CheckTargetAndPopulate(cx, nsXPTType::T_I8, type,
                                     sizeof(int8_t), count,
                                     jsArray, &output, pErr)) {
             return false;
         }
         break;
 
     case js::ArrayBufferView::TYPE_UINT8:
     case js::ArrayBufferView::TYPE_UINT8_CLAMPED:
-        if (!CheckTargetAndPopulate(nsXPTType::T_U8, type,
+        if (!CheckTargetAndPopulate(cx, nsXPTType::T_U8, type,
                                     sizeof(uint8_t), count,
                                     jsArray, &output, pErr)) {
             return false;
         }
         break;
 
     case js::ArrayBufferView::TYPE_INT16:
-        if (!CheckTargetAndPopulate(nsXPTType::T_I16, type,
+        if (!CheckTargetAndPopulate(cx, nsXPTType::T_I16, type,
                                     sizeof(int16_t), count,
                                     jsArray, &output, pErr)) {
             return false;
         }
         break;
 
     case js::ArrayBufferView::TYPE_UINT16:
-        if (!CheckTargetAndPopulate(nsXPTType::T_U16, type,
+        if (!CheckTargetAndPopulate(cx, nsXPTType::T_U16, type,
                                     sizeof(uint16_t), count,
                                     jsArray, &output, pErr)) {
             return false;
         }
         break;
 
     case js::ArrayBufferView::TYPE_INT32:
-        if (!CheckTargetAndPopulate(nsXPTType::T_I32, type,
+        if (!CheckTargetAndPopulate(cx, nsXPTType::T_I32, type,
                                     sizeof(int32_t), count,
                                     jsArray, &output, pErr)) {
             return false;
         }
         break;
 
     case js::ArrayBufferView::TYPE_UINT32:
-        if (!CheckTargetAndPopulate(nsXPTType::T_U32, type,
+        if (!CheckTargetAndPopulate(cx, nsXPTType::T_U32, type,
                                     sizeof(uint32_t), count,
                                     jsArray, &output, pErr)) {
             return false;
         }
         break;
 
     case js::ArrayBufferView::TYPE_FLOAT32:
-        if (!CheckTargetAndPopulate(nsXPTType::T_FLOAT, type,
+        if (!CheckTargetAndPopulate(cx, nsXPTType::T_FLOAT, type,
                                     sizeof(float), count,
                                     jsArray, &output, pErr)) {
             return false;
         }
         break;
 
     case js::ArrayBufferView::TYPE_FLOAT64:
-        if (!CheckTargetAndPopulate(nsXPTType::T_DOUBLE, type,
+        if (!CheckTargetAndPopulate(cx, nsXPTType::T_DOUBLE, type,
                                     sizeof(double), count,
                                     jsArray, &output, pErr)) {
             return false;
         }
         break;
 
     // Yet another array type was defined? It is not supported yet...
     default:
@@ -1658,18 +1660,18 @@ XPCConvert::JSArray2Native(JSContext* cx
         if (pErr)
             *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY;
         return false;
     }
 
     JSObject* jsarray = &s.toObject();
 
     // If this is a typed array, then try a fast conversion with memcpy.
-    if (JS_IsTypedArrayObject(jsarray)) {
-        return JSTypedArray2Native(d, jsarray, count, type, pErr);
+    if (JS_IsTypedArrayObject(jsarray, cx)) {
+        return JSTypedArray2Native(cx, d, jsarray, count, type, pErr);
     }
 
     if (!JS_IsArrayObject(cx, jsarray)) {
         if (pErr)
             *pErr = NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY;
         return false;
     }
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3363,17 +3363,18 @@ public:
                                  jsval* d, const void** s,
                                  const nsXPTType& type, const nsID* iid,
                                  uint32_t count, nsresult* pErr);
 
     static JSBool JSArray2Native(JSContext* cx, void** d, jsval s,
                                  uint32_t count, const nsXPTType& type,
                                  const nsID* iid, nsresult* pErr);
 
-    static JSBool JSTypedArray2Native(void** d,
+    static JSBool JSTypedArray2Native(JSContext* cx,
+                                      void** d,
                                       JSObject* jsarray,
                                       uint32_t count,
                                       const nsXPTType& type,
                                       nsresult* pErr);
 
     static JSBool NativeStringWithSize2JS(JSContext* cx,
                                           jsval* d, const void* s,
                                           const nsXPTType& type,
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -226,16 +226,21 @@ AccessCheck::isCrossOriginAccessPermitte
     if (!XPCWrapper::GetSecurityManager())
         return true;
 
     if (act == Wrapper::CALL)
         return true;
 
     JSObject *obj = Wrapper::wrappedObject(wrapper);
 
+    // PUNCTURE Is always denied for cross-origin access.
+    if (act == Wrapper::PUNCTURE) {
+        return false;
+    }
+
     const char *name;
     js::Class *clasp = js::GetObjectClass(obj);
     NS_ASSERTION(Jsvalify(clasp) != &XrayUtils::HolderClass, "shouldn't have a holder here");
     if (clasp->ext.innerObject)
         name = "Window";
     else
         name = clasp->name;
 
@@ -335,31 +340,34 @@ IsInSandbox(JSContext *cx, JSObject *obj
 bool
 ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act)
 {
     JSObject *wrappedObject = Wrapper::wrappedObject(wrapper);
 
     if (act == Wrapper::CALL)
         return true;
 
+    if (act == Wrapper::PUNCTURE)
+        return false;
+
     jsid exposedPropsId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS);
 
     // We need to enter the wrappee's compartment to look at __exposedProps__,
     // but we want to be in the wrapper's compartment if we call Deny().
     //
     // Unfortunately, |cx| can be in either compartment when we call ::check. :-(
     JSAutoCompartment ac(cx, wrappedObject);
 
     JSBool found = false;
     if (!JS_HasPropertyById(cx, wrappedObject, exposedPropsId, &found))
         return false;
 
     // Always permit access to "length" and indexed properties of arrays.
     if ((JS_IsArrayObject(cx, wrappedObject) ||
-         JS_IsTypedArrayObject(wrappedObject)) &&
+         JS_IsTypedArrayObject(wrappedObject, cx)) &&
         ((JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) ||
          (JSID_IS_STRING(id) && JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), "length")))) {
         return true; // Allow
     }
 
     // If no __exposedProps__ existed, deny access.
     if (!found) {
         // Everything below here needs to be done in the wrapper's compartment.
--- a/js/xpconnect/wrappers/AccessCheck.h
+++ b/js/xpconnect/wrappers/AccessCheck.h
@@ -91,17 +91,19 @@ struct CrossOriginAccessiblePropertiesOn
 // same-origin or not, is allowed to make. If it isn't, it _also_ checks the
 // state of the outer window to determine whether we happen to be same-origin
 // at the moment.
 struct LocationPolicy : public Policy {
     static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) {
         // We should only be dealing with Location objects here.
         MOZ_ASSERT(WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper)));
 
-        if ((AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act) ||
+        // Location object security is complicated enough. Don't allow punctures.
+        if (act != js::Wrapper::PUNCTURE &&
+            (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act) ||
              AccessCheck::isLocationObjectSameOrigin(cx, wrapper))) {
             return true;
         }
         return false;
     }
     static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) {
         AccessCheck::deny(cx, id);
         return false;
--- a/js/xpconnect/wrappers/FilteringWrapper.cpp
+++ b/js/xpconnect/wrappers/FilteringWrapper.cpp
@@ -128,23 +128,30 @@ FilteringWrapper<Base, Policy>::enter(JS
         return false;
     }
     *bp = true;
     return Base::enter(cx, wrapper, id, act, bp);
 }
 
 #define SOW FilteringWrapper<CrossCompartmentSecurityWrapper, OnlyIfSubjectIsSystem>
 #define SCSOW FilteringWrapper<SameCompartmentSecurityWrapper, OnlyIfSubjectIsSystem>
-#define XOW FilteringWrapper<SecurityXrayXPCWN, CrossOriginAccessiblePropertiesOnly>
-#define DXOW   FilteringWrapper<SecurityXrayDOM, CrossOriginAccessiblePropertiesOnly>
-#define NNXOW FilteringWrapper<CrossCompartmentSecurityWrapper, CrossOriginAccessiblePropertiesOnly>
-#define LW    FilteringWrapper<SCSecurityXrayXPCWN, LocationPolicy>
-#define XLW   FilteringWrapper<SecurityXrayXPCWN, LocationPolicy>
-#define CW FilteringWrapper<SameCompartmentSecurityWrapper, ComponentsObjectPolicy>
-#define XCW FilteringWrapper<CrossCompartmentSecurityWrapper, ComponentsObjectPolicy>
+#define XOW FilteringWrapper<XrayWrapper<CrossCompartmentSecurityWrapper>, \
+                             CrossOriginAccessiblePropertiesOnly>
+#define DXOW   FilteringWrapper<XrayDOM, \
+                                CrossOriginAccessiblePropertiesOnly>
+#define NNXOW FilteringWrapper<CrossCompartmentSecurityWrapper, \
+                               CrossOriginAccessiblePropertiesOnly>
+#define LW    FilteringWrapper<XrayWrapper<SameCompartmentSecurityWrapper>, \
+                               LocationPolicy>
+#define XLW   FilteringWrapper<XrayWrapper<CrossCompartmentSecurityWrapper>, \
+                               LocationPolicy>
+#define CW FilteringWrapper<SameCompartmentSecurityWrapper, \
+                            ComponentsObjectPolicy>
+#define XCW FilteringWrapper<CrossCompartmentSecurityWrapper, \
+                            ComponentsObjectPolicy>
 template<> SOW SOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                               WrapperFactory::SOW_FLAG);
 template<> SCSOW SCSOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                                   WrapperFactory::SOW_FLAG);
 template<> XOW XOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG);
 template<> DXOW DXOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG);
 template<> NNXOW NNXOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG);
 template<> LW  LW::singleton(WrapperFactory::SHADOWING_FORBIDDEN);
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -286,41 +286,16 @@ static XPCWrappedNative *
 GetWrappedNative(JSContext *cx, JSObject *obj)
 {
     obj = JS_ObjectToInnerObject(cx, obj);
     return IS_WN_WRAPPER(obj)
            ? static_cast<XPCWrappedNative *>(js::GetObjectPrivate(obj))
            : nullptr;
 }
 
-#ifdef DEBUG
-static void
-DEBUG_CheckUnwrapSafety(JSObject *obj, js::Wrapper *handler,
-                        JSCompartment *origin, JSCompartment *target)
-{
-    typedef FilteringWrapper<CrossCompartmentSecurityWrapper, OnlyIfSubjectIsSystem> XSOW;
-
-    if (AccessCheck::isChrome(target) || xpc::IsUniversalXPConnectEnabled(target)) {
-        // If the caller is chrome (or effectively so), unwrap should always be allowed.
-        MOZ_ASSERT(handler->isSafeToUnwrap());
-    } else if (WrapperFactory::IsLocationObject(obj) ||
-               WrapperFactory::IsComponentsObject(obj) ||
-               handler == &XSOW::singleton)
-    {
-        // This is an object that is restricted regardless of origin.
-        MOZ_ASSERT(!handler->isSafeToUnwrap());
-    } else {
-        // Otherwise, it should depend on whether the target subsumes the origin.
-        MOZ_ASSERT(handler->isSafeToUnwrap() == AccessCheck::subsumes(target, origin));
-    }
-}
-#else
-#define DEBUG_CheckUnwrapSafety(obj, handler, origin, target) {}
-#endif
-
 JSObject *
 WrapperFactory::Rewrap(JSContext *cx, JSObject *existing, JSObject *obj,
                        JSObject *wrappedProto, JSObject *parent,
                        unsigned flags)
 {
     NS_ASSERTION(!IsWrapper(obj) ||
                  GetProxyHandler(obj) == &XrayWaiver ||
                  js::GetObjectClass(obj)->ext.innerObject,
@@ -343,19 +318,20 @@ WrapperFactory::Rewrap(JSContext *cx, JS
             if (flags & WAIVE_XRAY_WRAPPER_FLAG) {
                 // If we waived the X-ray wrapper for this object, wrap it into a
                 // special wrapper to transitively maintain the X-ray waiver.
                 wrapper = &WaiveXrayWrapper::singleton;
             } else {
                 // Native objects must be wrapped into an X-ray wrapper.
                 XrayType type = GetXrayType(obj);
                 if (type == XrayForDOMObject) {
-                    wrapper = &PermissiveXrayDOM::singleton;
+                    wrapper = &XrayDOM::singleton;
                 } else if (type == XrayForWrappedNative) {
-                    wrapper = &PermissiveXrayXPCWN::singleton;
+                    typedef XrayWrapper<CrossCompartmentWrapper> Xray;
+                    wrapper = &Xray::singleton;
                 } else {
                     wrapper = &CrossCompartmentWrapper::singleton;
                 }
             }
         }
     } else if (xpc::IsUniversalXPConnectEnabled(target)) {
         wrapper = &CrossCompartmentWrapper::singleton;
     } else if (AccessCheck::isChrome(origin)) {
@@ -366,22 +342,23 @@ WrapperFactory::Rewrap(JSContext *cx, JS
                 return nullptr;
             }
         }
 
         XPCWrappedNative *wn;
         if (targetdata &&
             (wn = GetWrappedNative(cx, obj)) &&
             wn->HasProto() && wn->GetProto()->ClassIsDOMObject()) {
+            typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray;
             if (IsLocationObject(obj))
-                wrapper = &FilteringWrapper<SecurityXrayXPCWN, LocationPolicy>::singleton;
+                wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton;
             else
-                wrapper = &FilteringWrapper<SecurityXrayXPCWN, CrossOriginAccessiblePropertiesOnly>::singleton;
+                wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton;
         } else if (mozilla::dom::UseDOMXray(obj)) {
-            wrapper = &FilteringWrapper<SecurityXrayDOM, CrossOriginAccessiblePropertiesOnly>::singleton;
+            wrapper = &FilteringWrapper<XrayDOM, CrossOriginAccessiblePropertiesOnly>::singleton;
         } else if (IsComponentsObject(obj)) {
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         ComponentsObjectPolicy>::singleton;
         } else {
             wrapper = &ChromeObjectWrapper::singleton;
 
             // If the prototype of the chrome object being wrapped is a prototype
             // for a standard class, use the one from the content compartment so
@@ -426,55 +403,57 @@ WrapperFactory::Rewrap(JSContext *cx, JS
         //
         // The first two cases always require a security wrapper for non-chrome
         // access, regardless of the origin of the object.
         XrayType type;
         if (AccessCheck::needsSystemOnlyWrapper(obj)) {
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         OnlyIfSubjectIsSystem>::singleton;
         } else if (IsLocationObject(obj)) {
-            wrapper = &FilteringWrapper<SecurityXrayXPCWN, LocationPolicy>::singleton;
+            typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray;
+            wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton;
         } else if (IsComponentsObject(obj)) {
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         ComponentsObjectPolicy>::singleton;
         } else if (!targetdata || !targetdata->wantXrays ||
                    (type = GetXrayType(obj)) == NotXray) {
             wrapper = &CrossCompartmentWrapper::singleton;
         } else if (type == XrayForDOMObject) {
-            wrapper = &PermissiveXrayDOM::singleton;
+            wrapper = &XrayDOM::singleton;
         } else {
-            wrapper = &PermissiveXrayXPCWN::singleton;
+            typedef XrayWrapper<CrossCompartmentWrapper> Xray;
+            wrapper = &Xray::singleton;
         }
     } else {
         NS_ASSERTION(!AccessCheck::needsSystemOnlyWrapper(obj),
                      "bad object exposed across origins");
 
         // Cross origin we want to disallow scripting and limit access to
         // a predefined set of properties.
         XrayType type = GetXrayType(obj);
         if (type == NotXray) {
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         CrossOriginAccessiblePropertiesOnly>::singleton;
         } else if (type == XrayForDOMObject) {
-            wrapper = &FilteringWrapper<SecurityXrayDOM,
+            wrapper = &FilteringWrapper<XrayDOM,
                                         CrossOriginAccessiblePropertiesOnly>::singleton;
         } else {
+            typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray;
+
             // Location objects can become same origin after navigation, so we might
             // have to grant transparent access later on.
             if (IsLocationObject(obj)) {
-                wrapper = &FilteringWrapper<SecurityXrayXPCWN, LocationPolicy>::singleton;
+                wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton;
             } else {
-                wrapper = &FilteringWrapper<SecurityXrayXPCWN,
-                                            CrossOriginAccessiblePropertiesOnly>::singleton;
+                wrapper = &FilteringWrapper<Xray,
+                    CrossOriginAccessiblePropertiesOnly>::singleton;
             }
         }
     }
 
-    DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target);
-
     if (existing && proxyProto == wrappedProto)
         return Wrapper::Renew(cx, existing, obj, wrapper);
 
     return Wrapper::New(cx, obj, proxyProto, parent, wrapper);
 }
 
 JSObject *
 WrapperFactory::WrapForSameCompartment(JSContext *cx, JSObject *obj)
@@ -487,36 +466,35 @@ WrapperFactory::WrapForSameCompartment(J
     if (!IS_WN_WRAPPER(obj))
         return obj;
 
     // Extract the WN. It should exist.
     XPCWrappedNative *wn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj));
     MOZ_ASSERT(wn, "Trying to wrap a dead WN!");
 
     // The WN knows what to do.
-    JSObject *wrapper = wn->GetSameCompartmentSecurityWrapper(cx);
-    MOZ_ASSERT_IF(wrapper != obj, !Wrapper::wrapperHandler(wrapper)->isSafeToUnwrap());
-    return wrapper;
+    return wn->GetSameCompartmentSecurityWrapper(cx);
 }
 
+typedef FilteringWrapper<XrayWrapper<SameCompartmentSecurityWrapper>, LocationPolicy> LW;
+
 bool
 WrapperFactory::IsLocationObject(JSObject *obj)
 {
     const char *name = js::GetObjectClass(obj)->name;
     return name[0] == 'L' && !strcmp(name, "Location");
 }
 
 JSObject *
 WrapperFactory::WrapLocationObject(JSContext *cx, JSObject *obj)
 {
     JSObject *proto;
     if (!js::GetObjectProto(cx, obj, &proto))
         return nullptr;
-    return Wrapper::New(cx, obj, proto, js::GetObjectParent(obj),
-                        &FilteringWrapper<SCSecurityXrayXPCWN, LocationPolicy>::singleton);
+    return Wrapper::New(cx, obj, proto, js::GetObjectParent(obj), &LW::singleton);
 }
 
 // Call WaiveXrayAndWrap when you have a JS object that you don't want to be
 // wrapped in an Xray wrapper. cx->compartment is the compartment that will be
 // using the returned object. If the object to be wrapped is already in the
 // correct compartment, then this returns the unwrapped object.
 bool
 WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp)
@@ -581,19 +559,19 @@ WrapperFactory::WrapForSameCompartmentXr
     // Sort out what kind of Xray we can do. If we can't Xray, bail.
     XrayType type = GetXrayType(obj);
     if (type == NotXray)
         return NULL;
 
     // Select the appropriate proxy handler.
     Wrapper *wrapper = NULL;
     if (type == XrayForWrappedNative)
-        wrapper = &SCPermissiveXrayXPCWN::singleton;
+        wrapper = &XrayWrapper<Wrapper>::singleton;
     else if (type == XrayForDOMObject)
-        wrapper = &SCPermissiveXrayDOM::singleton;
+        wrapper = &XrayWrapper<Wrapper, DOMXrayTraits>::singleton;
     else
         MOZ_NOT_REACHED("Bad Xray type");
 
     // Make the Xray.
     JSObject *parent = JS_GetGlobalForObject(cx, obj);
     return Wrapper::New(cx, obj, NULL, parent, wrapper);
 }
 
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1697,31 +1697,42 @@ XrayWrapper<Base, Traits>::call(JSContex
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::construct(JSContext *cx, JSObject *wrapper, unsigned argc,
                                      js::Value *argv, js::Value *rval)
 {
     return Traits::construct(cx, wrapper, argc, argv, rval);
 }
 
-/*
- * The Permissive / Security variants should be used depending on whether the
- * compartment of the wrapper is guranteed to subsume the compartment of the
- * wrapped object (i.e. - whether it is safe from a security perspective to
- * unwrap the wrapper).
- */
+
+#define XRAY XrayWrapper<CrossCompartmentSecurityWrapper, XPCWrappedNativeXrayTraits >
+template <> XRAY XRAY::singleton(0);
+template class XRAY;
+#undef XRAY
+
+#define XRAY XrayWrapper<SameCompartmentSecurityWrapper, XPCWrappedNativeXrayTraits >
+template <> XRAY XRAY::singleton(0);
+template class XRAY;
+#undef XRAY
+
+#define XRAY XrayWrapper<CrossCompartmentWrapper, XPCWrappedNativeXrayTraits >
+template <> XRAY XRAY::singleton(0);
+template class XRAY;
+#undef XRAY
 
-template<>
-PermissiveXrayXPCWN PermissiveXrayXPCWN::singleton(0);
-template<>
-SecurityXrayXPCWN SecurityXrayXPCWN::singleton(0);
-template<>
-PermissiveXrayDOM PermissiveXrayDOM::singleton(0);
-template<>
-SecurityXrayDOM SecurityXrayDOM::singleton(0);
-template<>
-SCPermissiveXrayXPCWN SCPermissiveXrayXPCWN::singleton(0);
-template<>
-SCSecurityXrayXPCWN SCSecurityXrayXPCWN::singleton(0);
-template<>
-SCPermissiveXrayDOM SCPermissiveXrayDOM::singleton(0);
+#define XRAY XrayWrapper<CrossCompartmentWrapper, DOMXrayTraits >
+template <> XRAY XRAY::singleton(0);
+template class XRAY;
+#undef XRAY
+
+/* Same-compartment non-filtering versions. */
+
+#define XRAY XrayWrapper<Wrapper, XPCWrappedNativeXrayTraits >
+template <> XRAY XRAY::singleton(0);
+template class XRAY;
+#undef XRAY
+
+#define XRAY XrayWrapper<Wrapper, DOMXrayTraits >
+template <> XRAY XRAY::singleton(0);
+template class XRAY;
+#undef XRAY
 
 }
--- a/js/xpconnect/wrappers/XrayWrapper.h
+++ b/js/xpconnect/wrappers/XrayWrapper.h
@@ -88,23 +88,17 @@ class XrayWrapper : public Base {
 
     static XrayWrapper singleton;
 
   private:
     bool enumerate(JSContext *cx, JSObject *wrapper, unsigned flags,
                    JS::AutoIdVector &props);
 };
 
-typedef XrayWrapper<js::CrossCompartmentWrapper, XPCWrappedNativeXrayTraits > PermissiveXrayXPCWN;
-typedef XrayWrapper<js::CrossCompartmentSecurityWrapper, XPCWrappedNativeXrayTraits > SecurityXrayXPCWN;
-typedef XrayWrapper<js::CrossCompartmentWrapper, DOMXrayTraits > PermissiveXrayDOM;
-typedef XrayWrapper<js::CrossCompartmentSecurityWrapper, DOMXrayTraits > SecurityXrayDOM;
-typedef XrayWrapper<js::SameCompartmentSecurityWrapper, XPCWrappedNativeXrayTraits > SCSecurityXrayXPCWN;
-typedef XrayWrapper<js::Wrapper, XPCWrappedNativeXrayTraits > SCPermissiveXrayXPCWN;
-typedef XrayWrapper<js::Wrapper, DOMXrayTraits > SCPermissiveXrayDOM;
+typedef XrayWrapper<js::CrossCompartmentWrapper, DOMXrayTraits > XrayDOM;
 
 class SandboxProxyHandler : public js::Wrapper {
 public:
     SandboxProxyHandler() : js::Wrapper(0)
     {
     }
 
     virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,