Bug 929800 - Handlify the structured clone interface; r=sfink,smaug
authorTerrence Cole <terrence@mozilla.com>
Tue, 22 Oct 2013 17:18:32 -0700
changeset 165835 0f6219c6fb61f3f5e6775d269a6cf52305021024
parent 165834 2b98c52e5e08adc009a55fcefd72a15e88723a27
child 165836 f94365dffb6fe73e6b4f5bee500f4537688baba6
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink, smaug
bugs929800
milestone27.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
Bug 929800 - Handlify the structured clone interface; r=sfink,smaug
content/base/src/nsFrameMessageManager.cpp
dom/base/MessagePort.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsStructuredCloneContainer.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/ipc/StructuredCloneUtils.cpp
dom/ipc/StructuredCloneUtils.h
dom/workers/Events.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/XMLHttpRequest.cpp
js/public/StructuredClone.h
js/src/builtin/TestingFunctions.cpp
js/src/jsapi-tests/testStructuredClone.cpp
js/src/vm/StructuredClone.cpp
js/xpconnect/src/Sandbox.cpp
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -412,27 +412,27 @@ JSONCreator(const jschar* aBuf, uint32_t
 }
 
 static bool
 GetParamsForMessage(JSContext* aCx,
                     const JS::Value& aJSON,
                     JSAutoStructuredCloneBuffer& aBuffer,
                     StructuredCloneClosure& aClosure)
 {
-  if (WriteStructuredClone(aCx, aJSON, aBuffer, aClosure)) {
+  JS::Rooted<JS::Value> v(aCx, aJSON);
+  if (WriteStructuredClone(aCx, v, aBuffer, aClosure)) {
     return true;
   }
   JS_ClearPendingException(aCx);
 
   // Not clonable, try JSON
   //XXX This is ugly but currently structured cloning doesn't handle
   //    properly cases when interface is implemented in JS and used
   //    as a dictionary.
   nsAutoString json;
-  JS::Rooted<JS::Value> v(aCx, aJSON);
   NS_ENSURE_TRUE(JS_Stringify(aCx, &v, JS::NullPtr(), JS::NullHandleValue,
                               JSONCreator, &json), false);
   NS_ENSURE_TRUE(!json.IsEmpty(), false);
 
   JS::Rooted<JS::Value> val(aCx, JS::NullValue());
   NS_ENSURE_TRUE(JS_ParseJSON(aCx, static_cast<const jschar*>(json.get()),
                               json.Length(), &val), false);
 
@@ -848,17 +848,17 @@ nsFrameMessageManager::ReceiveMessage(ns
             return NS_ERROR_UNEXPECTED;
           }
         }
 
         JS::RootedValue cpowsv(ctx, JS::ObjectValue(*cpows));
 
         JS::Rooted<JS::Value> json(ctx, JS::NullValue());
         if (aCloneData && aCloneData->mDataLength &&
-            !ReadStructuredClone(ctx, *aCloneData, json.address())) {
+            !ReadStructuredClone(ctx, *aCloneData, &json)) {
           JS_ClearPendingException(ctx);
           return NS_OK;
         }
         JS::Rooted<JSString*> jsMessage(ctx,
           JS_NewUCStringCopyN(ctx,
                               static_cast<const jschar*>(aMessage.BeginReading()),
                               aMessage.Length()));
         NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
--- a/dom/base/MessagePort.cpp
+++ b/dom/base/MessagePort.cpp
@@ -240,18 +240,17 @@ PostMessageRunnable::Run()
 
   // Deserialize the structured clone data
   JS::Rooted<JS::Value> messageData(cx);
   {
     StructuredCloneInfo scInfo;
     scInfo.mEvent = this;
     scInfo.mPort = mPort;
 
-    if (!buffer.read(cx, messageData.address(), &kPostMessageCallbacks,
-                     &scInfo)) {
+    if (!buffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
       return NS_ERROR_DOM_DATA_CLONE_ERR;
     }
   }
 
   // Create the event
   nsIDocument* doc = mPort->GetOwner()->GetExtantDoc();
   if (!doc) {
     return NS_OK;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6972,18 +6972,17 @@ PostMessageEvent::Run()
 
   // Deserialize the structured clone data
   JS::Rooted<JS::Value> messageData(cx);
   {
     StructuredCloneInfo scInfo;
     scInfo.event = this;
     scInfo.window = targetWindow;
 
-    if (!buffer.read(cx, messageData.address(), &kPostMessageCallbacks,
-                     &scInfo)) {
+    if (!buffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
       return NS_ERROR_DOM_DATA_CLONE_ERR;
     }
   }
 
   // Create the event
   nsIDocument* doc = targetWindow->mDoc;
   if (!doc)
     return NS_OK;
@@ -7123,17 +7122,19 @@ nsGlobalWindow::PostMessageMoz(const JS:
   StructuredCloneInfo scInfo;
   scInfo.event = event;
   scInfo.window = this;
 
   nsIPrincipal* principal = GetPrincipal();
   if (NS_FAILED(callerPrin->Subsumes(principal, &scInfo.subsumes)))
     return NS_ERROR_DOM_DATA_CLONE_ERR;
 
-  if (!buffer.write(aCx, aMessage, aTransfer, &kPostMessageCallbacks, &scInfo))
+  JS::Rooted<JS::Value> message(aCx, aMessage);
+  JS::Rooted<JS::Value> transfer(aCx, aTransfer);
+  if (!buffer.write(aCx, message, transfer, &kPostMessageCallbacks, &scInfo))
     return NS_ERROR_DOM_DATA_CLONE_ERR;
 
   event->SetJSData(buffer);
 
   return NS_DispatchToCurrentThread(event);
 }
 
 class nsCloseEvent : public nsRunnable {
--- a/dom/base/nsStructuredCloneContainer.cpp
+++ b/dom/base/nsStructuredCloneContainer.cpp
@@ -47,17 +47,18 @@ nsStructuredCloneContainer::InitFromJSVa
 
   // Make sure that we serialize in the right context.
   MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
   JS::Rooted<JS::Value> jsData(aCx, aData);
   JS_WrapValue(aCx, jsData.address());
 
   uint64_t* jsBytes = nullptr;
   bool success = JS_WriteStructuredClone(aCx, jsData, &jsBytes, &mSize,
-                                         nullptr, nullptr, JSVAL_VOID);
+                                         nullptr, nullptr,
+                                         JS::UndefinedHandleValue);
   NS_ENSURE_STATE(success);
   NS_ENSURE_STATE(jsBytes);
 
   // Copy jsBytes into our own buffer.
   mData = (uint64_t*) malloc(mSize);
   if (!mData) {
     mSize = 0;
     mVersion = 0;
@@ -106,18 +107,17 @@ nsStructuredCloneContainer::DeserializeT
   NS_ENSURE_STATE(mData);
   NS_ENSURE_ARG_POINTER(aData);
   *aData = nullptr;
 
   // Deserialize to a JS::Value.
   JS::Rooted<JS::Value> jsStateObj(aCx);
   bool hasTransferable = false;
   bool success = JS_ReadStructuredClone(aCx, mData, mSize, mVersion,
-                                        jsStateObj.address(), nullptr,
-                                        nullptr) &&
+                                        &jsStateObj, nullptr, nullptr) &&
                  JS_StructuredCloneHasTransferables(mData, mSize,
                                                     &hasTransferable);
   // We want to be sure that mData doesn't contain transferable objects
   MOZ_ASSERT(!hasTransferable);
   NS_ENSURE_STATE(success && !hasTransferable);
 
   // Now wrap the JS::Value as an nsIVariant.
   nsCOMPtr<nsIVariant> varStateObj;
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1335,17 +1335,17 @@ IDBObjectStore::DeserializeValue(JSConte
   JSAutoRequest ar(aCx);
 
   JSStructuredCloneCallbacks callbacks = {
     IDBObjectStore::StructuredCloneReadCallback<MainThreadDeserializationTraits>,
     nullptr,
     nullptr
   };
 
-  return buffer.read(aCx, aValue.address(), &callbacks, &aCloneReadInfo);
+  return buffer.read(aCx, aValue, &callbacks, &aCloneReadInfo);
 }
 
 // static
 bool
 IDBObjectStore::SerializeValue(JSContext* aCx,
                                StructuredCloneWriteInfo& aCloneWriteInfo,
                                JS::Handle<JS::Value> aValue)
 {
@@ -4537,17 +4537,17 @@ CreateIndexHelper::InsertDataFromObjectS
 
     JSStructuredCloneCallbacks callbacks = {
       IDBObjectStore::StructuredCloneReadCallback<CreateIndexDeserializationTraits>,
       nullptr,
       nullptr
     };
 
     JS::Rooted<JS::Value> clone(cx);
-    if (!buffer.read(cx, clone.address(), &callbacks, &cloneReadInfo)) {
+    if (!buffer.read(cx, &clone, &callbacks, &cloneReadInfo)) {
       NS_WARNING("Failed to deserialize structured clone data!");
       return NS_ERROR_DOM_DATA_CLONE_ERR;
     }
 
     nsTArray<IndexUpdateInfo> updateInfo;
     rv = IDBObjectStore::AppendIndexUpdateInfo(mIndex->Id(),
                                                mIndex->GetKeyPath(),
                                                mIndex->IsUnique(),
--- a/dom/ipc/StructuredCloneUtils.cpp
+++ b/dom/ipc/StructuredCloneUtils.cpp
@@ -157,26 +157,27 @@ JSStructuredCloneCallbacks gCallbacks = 
 
 } // anonymous namespace
 
 namespace mozilla {
 namespace dom {
 
 bool
 ReadStructuredClone(JSContext* aCx, uint64_t* aData, size_t aDataLength,
-                    const StructuredCloneClosure& aClosure, JS::Value* aClone)
+                    const StructuredCloneClosure& aClosure,
+                    JS::MutableHandle<JS::Value> aClone)
 {
   void* closure = &const_cast<StructuredCloneClosure&>(aClosure);
   return !!JS_ReadStructuredClone(aCx, aData, aDataLength,
                                   JS_STRUCTURED_CLONE_VERSION, aClone,
                                   &gCallbacks, closure);
 }
 
 bool
-WriteStructuredClone(JSContext* aCx, const JS::Value& aSource,
+WriteStructuredClone(JSContext* aCx, JS::Handle<JS::Value> aSource,
                      JSAutoStructuredCloneBuffer& aBuffer,
                      StructuredCloneClosure& aClosure)
 {
   return aBuffer.write(aCx, aSource, &gCallbacks, &aClosure);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/StructuredCloneUtils.h
+++ b/dom/ipc/StructuredCloneUtils.h
@@ -31,27 +31,28 @@ StructuredCloneData
   StructuredCloneData() : mData(nullptr), mDataLength(0) {}
   uint64_t* mData;
   size_t mDataLength;
   StructuredCloneClosure mClosure;
 };
 
 bool
 ReadStructuredClone(JSContext* aCx, uint64_t* aData, size_t aDataLength,
-                    const StructuredCloneClosure& aClosure, JS::Value* aClone);
+                    const StructuredCloneClosure& aClosure,
+                    JS::MutableHandle<JS::Value> aClone);
 
 inline bool
 ReadStructuredClone(JSContext* aCx, const StructuredCloneData& aData,
-                    JS::Value* aClone)
+                    JS::MutableHandle<JS::Value> aClone)
 {
   return ReadStructuredClone(aCx, aData.mData, aData.mDataLength,
                              aData.mClosure, aClone);
 }
 
 bool
-WriteStructuredClone(JSContext* aCx, const JS::Value& aSource,
+WriteStructuredClone(JSContext* aCx, JS::Handle<JS::Value> aSource,
                      JSAutoStructuredCloneBuffer& aBuffer,
                      StructuredCloneClosure& aClosure);
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_StructuredCloneUtils_h
--- a/dom/workers/Events.cpp
+++ b/dom/workers/Events.cpp
@@ -579,17 +579,17 @@ private:
       buffer.swap(event->mBuffer);
 
       // Release reference to objects that were AddRef'd for
       // cloning into worker when array goes out of scope.
       nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
       clonedObjects.SwapElements(event->mClonedObjects);
 
       JS::Rooted<JS::Value> data(aCx);
-      if (!buffer.read(aCx, data.address(),
+      if (!buffer.read(aCx, &data,
                        WorkerStructuredCloneCallbacks(event->mMainRuntime))) {
         return false;
       }
       JS_SetReservedSlot(obj, Slot, data);
 
       aArgs.rval().set(data);
       return true;
     }
@@ -1161,9 +1161,9 @@ DispatchEventToTarget(JSContext* aCx, JS
   }
 
   *aPreventDefaultCalled = !!preventDefaultCalled;
   return true;
 }
 
 } // namespace events
 
-END_WORKERS_NAMESPACE
\ No newline at end of file
+END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2631,17 +2631,17 @@ WorkerPrivateParent<Derived>::DispatchMe
 
   nsCOMPtr<nsIScriptContext> scx = sgo->GetContext();
   MOZ_ASSERT_IF(scx, scx->GetNativeContext());
 
   AutoPushJSContext cx(scx ? scx->GetNativeContext() : aCx);
   JSAutoCompartment(cx, sgo->GetGlobalJSObject());
 
   JS::Rooted<JS::Value> data(cx);
-  if (!buffer.read(cx, data.address(), WorkerStructuredCloneCallbacks(true))) {
+  if (!buffer.read(cx, &data, WorkerStructuredCloneCallbacks(true))) {
     return false;
   }
 
   buffer.clear();
 
   nsRefPtr<nsDOMMessageEvent> event =
     new nsDOMMessageEvent(port, nullptr, nullptr);
 
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -710,17 +710,17 @@ public:
             aWorkerPrivate->IsChromeWorker() ?
             ChromeWorkerStructuredCloneCallbacks(false) :
             WorkerStructuredCloneCallbacks(false);
 
           nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
           clonedObjects.SwapElements(mClonedObjects);
 
           JS::Rooted<JS::Value> response(aCx);
-          if (!responseBuffer.read(aCx, response.address(), callbacks, &clonedObjects)) {
+          if (!responseBuffer.read(aCx, &response, callbacks, &clonedObjects)) {
             return false;
           }
 
           state.mResponse = response;
         }
         else {
           state.mResponse = mResponse;
         }
@@ -1149,17 +1149,17 @@ public:
       nsresult rv = NS_OK;
 
       JSStructuredCloneCallbacks* callbacks =
         mWorkerPrivate->IsChromeWorker() ?
         ChromeWorkerStructuredCloneCallbacks(true) :
         WorkerStructuredCloneCallbacks(true);
 
       JS::Rooted<JS::Value> body(cx);
-      if (mBody.read(cx, body.address(), callbacks, &mClonedObjects)) {
+      if (mBody.read(cx, &body, callbacks, &mClonedObjects)) {
         if (NS_FAILED(xpc->JSValToVariant(cx, body.address(),
                                           getter_AddRefs(variant)))) {
           rv = NS_ERROR_DOM_INVALID_STATE_ERR;
         }
       }
       else {
         rv = NS_ERROR_DOM_DATA_CLONE_ERR;
       }
@@ -1965,28 +1965,28 @@ XMLHttpRequest::Send(JSObject* aBody, Er
 
   if (!mProxy) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   JSContext* cx = GetJSContext();
 
-  jsval valToClone;
+  JS::Rooted<JS::Value> valToClone(cx);
   if (JS_IsArrayBufferObject(aBody) || JS_IsArrayBufferViewObject(aBody) ||
       file::GetDOMBlobFromJSObject(aBody)) {
-    valToClone = OBJECT_TO_JSVAL(aBody);
+    valToClone.setObject(*aBody);
   }
   else {
     JSString* bodyStr = JS_ValueToString(cx, OBJECT_TO_JSVAL(aBody));
     if (!bodyStr) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
-    valToClone = STRING_TO_JSVAL(bodyStr);
+    valToClone.setString(bodyStr);
   }
 
   JSStructuredCloneCallbacks* callbacks =
     mWorkerPrivate->IsChromeWorker() ?
     ChromeWorkerStructuredCloneCallbacks(false) :
     WorkerStructuredCloneCallbacks(false);
 
   nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
--- a/js/public/StructuredClone.h
+++ b/js/public/StructuredClone.h
@@ -8,17 +8,19 @@
 #define js_StructuredClone_h
 
 #include "mozilla/NullPtr.h"
 
 #include <stdint.h>
 
 #include "jstypes.h"
 
+#include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
+#include "js/Value.h"
 
 struct JSRuntime;
 struct JSStructuredCloneReader;
 struct JSStructuredCloneWriter;
 
 // API for the HTML5 internal structured cloning algorithm.
 
 // Read structured data from the reader r. This hook is used to read a value
@@ -57,34 +59,34 @@ struct JSStructuredCloneCallbacks {
     ReadStructuredCloneOp read;
     WriteStructuredCloneOp write;
     StructuredCloneErrorOp reportError;
 };
 
 // Note: if the *data contains transferable objects, it can be read only once.
 JS_PUBLIC_API(bool)
 JS_ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, uint32_t version,
-                       JS::Value *vp, const JSStructuredCloneCallbacks *optionalCallbacks,
-                       void *closure);
+                       JS::MutableHandleValue vp,
+                       const JSStructuredCloneCallbacks *optionalCallbacks, void *closure);
 
 // Note: On success, the caller is responsible for calling
 // JS_ClearStructuredClone(*datap, nbytesp).
 JS_PUBLIC_API(bool)
-JS_WriteStructuredClone(JSContext *cx, JS::Value v, uint64_t **datap, size_t *nbytesp,
+JS_WriteStructuredClone(JSContext *cx, JS::HandleValue v, uint64_t **datap, size_t *nbytesp,
                         const JSStructuredCloneCallbacks *optionalCallbacks,
-                        void *closure, JS::Value transferable);
+                        void *closure, JS::HandleValue transferable);
 
 JS_PUBLIC_API(bool)
 JS_ClearStructuredClone(const uint64_t *data, size_t nbytes);
 
 JS_PUBLIC_API(bool)
 JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, bool *hasTransferable);
 
 JS_PUBLIC_API(bool)
-JS_StructuredClone(JSContext *cx, JS::Value v, JS::Value *vp,
+JS_StructuredClone(JSContext *cx, JS::HandleValue v, JS::MutableHandleValue vp,
                    const JSStructuredCloneCallbacks *optionalCallbacks, void *closure);
 
 // RAII sugar for JS_WriteStructuredClone.
 class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
     uint64_t *data_;
     size_t nbytes_;
     uint32_t version_;
 
@@ -107,23 +109,23 @@ class JS_PUBLIC_API(JSAutoStructuredClon
     // JSAutoStructuredCloneBuffer::steal).
     void adopt(uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION);
 
     // Remove the buffer so that it will not be automatically freed.
     // After this, the caller is responsible for feeding the memory back to
     // JSAutoStructuredCloneBuffer::adopt.
     void steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp=nullptr);
 
-    bool read(JSContext *cx, JS::Value *vp,
+    bool read(JSContext *cx, JS::MutableHandleValue vp,
               const JSStructuredCloneCallbacks *optionalCallbacks=nullptr, void *closure=nullptr);
 
-    bool write(JSContext *cx, JS::Value v,
+    bool write(JSContext *cx, JS::HandleValue v,
                const JSStructuredCloneCallbacks *optionalCallbacks=nullptr, void *closure=nullptr);
 
-    bool write(JSContext *cx, JS::Value v, JS::Value transferable,
+    bool write(JSContext *cx, JS::HandleValue v, JS::HandleValue transferable,
                const JSStructuredCloneCallbacks *optionalCallbacks=nullptr, void *closure=nullptr);
 
     // Swap ownership with another JSAutoStructuredCloneBuffer.
     void swap(JSAutoStructuredCloneBuffer &other);
 
   private:
     // Copy and assignment are not supported.
     JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other);
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1259,21 +1259,18 @@ const JSPropertySpec CloneBufferObject::
     JS_PS_END
 };
 
 static bool
 Serialize(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    Value v = args.length() > 0 ? args[0] : UndefinedValue();
-    Value transferables = args.length() > 1 ? args[1] : UndefinedValue();
-
     JSAutoStructuredCloneBuffer clonebuf;
-    if (!clonebuf.write(cx, v, transferables))
+    if (!clonebuf.write(cx, args.get(0), args.get(1)))
         return false;
 
     RootedObject obj(cx, CloneBufferObject::Create(cx, &clonebuf));
     if (!obj)
         return false;
 
     args.rval().setObject(*obj);
     return true;
@@ -1304,17 +1301,17 @@ Deserialize(JSContext *cx, unsigned argc
     }
 
     bool hasTransferable;
     if (!JS_StructuredCloneHasTransferables(obj->data(), obj->nbytes(), &hasTransferable))
         return false;
 
     RootedValue deserialized(cx);
     if (!JS_ReadStructuredClone(cx, obj->data(), obj->nbytes(),
-                                JS_STRUCTURED_CLONE_VERSION, deserialized.address(), NULL, NULL)) {
+                                JS_STRUCTURED_CLONE_VERSION, &deserialized, NULL, NULL)) {
         return false;
     }
     args.rval().set(deserialized);
 
     if (hasTransferable)
         obj->discard();
 
     return true;
--- a/js/src/jsapi-tests/testStructuredClone.cpp
+++ b/js/src/jsapi-tests/testStructuredClone.cpp
@@ -23,17 +23,17 @@ BEGIN_TEST(testStructuredClone_object)
         CHECK(v1.isObject());
         CHECK(JS_SetProperty(cx, &v1.toObject(), "prop", prop));
     }
 
     {
         JSAutoCompartment ac(cx, g2);
         JS::RootedValue v2(cx);
 
-        CHECK(JS_StructuredClone(cx, v1, v2.address(), nullptr, nullptr));
+        CHECK(JS_StructuredClone(cx, v1, &v2, nullptr, nullptr));
         CHECK(v2.isObject());
 
         JS::RootedValue prop(cx);
         CHECK(JS_GetProperty(cx, &v2.toObject(), "prop", &prop));
         CHECK(prop.isInt32());
         CHECK(&v1.toObject() != &v2.toObject());
         CHECK_EQUAL(prop.toInt32(), 1337);
     }
@@ -59,20 +59,20 @@ BEGIN_TEST(testStructuredClone_string)
         CHECK(v1.isString());
         CHECK(v1.toString());
     }
 
     {
         JSAutoCompartment ac(cx, g2);
         JS::RootedValue v2(cx);
 
-        CHECK(JS_StructuredClone(cx, v1, v2.address(), nullptr, nullptr));
+        CHECK(JS_StructuredClone(cx, v1, &v2, nullptr, nullptr));
         CHECK(v2.isString());
         CHECK(v2.toString());
-        
+
         JS::RootedValue expected(cx, JS::StringValue(
             JS_NewStringCopyZ(cx, "Hello World!")));
         CHECK_SAME(v2, expected);
     }
 
     return true;
 }
 END_TEST(testStructuredClone_string)
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -318,22 +318,22 @@ WriteStructuredClone(JSContext *cx, Hand
                      jsval transferable)
 {
     SCOutput out(cx);
     JSStructuredCloneWriter w(out, cb, cbClosure, transferable);
     return w.init() && w.write(v) && out.extractBuffer(bufp, nbytesp);
 }
 
 bool
-ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp,
+ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, MutableHandleValue vp,
                     const JSStructuredCloneCallbacks *cb, void *cbClosure)
 {
     SCInput in(cx, data, nbytes);
     JSStructuredCloneReader r(in, cb, cbClosure);
-    return r.read(vp);
+    return r.read(vp.address());
 }
 
 // This may acquire new ways of discarding transfer map entries as new
 // Transferables are implemented.
 static void
 DiscardEntry(uint32_t mapEntryDescriptor, const uint64_t *ptr)
 {
     JS_ASSERT(mapEntryDescriptor == SCTAG_TM_ALLOC_DATA);
@@ -1546,17 +1546,17 @@ JSStructuredCloneReader::read(Value *vp)
     allObjs.clear();
 
     return true;
 }
 using namespace js;
 
 JS_PUBLIC_API(bool)
 JS_ReadStructuredClone(JSContext *cx, uint64_t *buf, size_t nbytes,
-                       uint32_t version, JS::Value *vp,
+                       uint32_t version, JS::MutableHandleValue vp,
                        const JSStructuredCloneCallbacks *optionalCallbacks,
                        void *closure)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
     if (version > JS_STRUCTURED_CLONE_VERSION) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_CLONE_VERSION);
@@ -1565,21 +1565,20 @@ JS_ReadStructuredClone(JSContext *cx, ui
     const JSStructuredCloneCallbacks *callbacks =
         optionalCallbacks ?
         optionalCallbacks :
         cx->runtime()->structuredCloneCallbacks;
     return ReadStructuredClone(cx, buf, nbytes, vp, callbacks, closure);
 }
 
 JS_PUBLIC_API(bool)
-JS_WriteStructuredClone(JSContext *cx, JS::Value valueArg, uint64_t **bufp, size_t *nbytesp,
+JS_WriteStructuredClone(JSContext *cx, JS::HandleValue value, uint64_t **bufp, size_t *nbytesp,
                         const JSStructuredCloneCallbacks *optionalCallbacks,
-                        void *closure, JS::Value transferable)
+                        void *closure, JS::HandleValue transferable)
 {
-    RootedValue value(cx, valueArg);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, value);
 
     const JSStructuredCloneCallbacks *callbacks =
         optionalCallbacks ?
         optionalCallbacks :
         cx->runtime()->structuredCloneCallbacks;
@@ -1601,32 +1600,31 @@ JS_StructuredCloneHasTransferables(const
     if (!StructuredCloneHasTransferObjects(data, nbytes, &transferable))
         return false;
 
     *hasTransferable = transferable;
     return true;
 }
 
 JS_PUBLIC_API(bool)
-JS_StructuredClone(JSContext *cx, JS::Value valueArg, JS::Value *vp,
+JS_StructuredClone(JSContext *cx, JS::HandleValue value, JS::MutableHandleValue vp,
                    const JSStructuredCloneCallbacks *optionalCallbacks,
                    void *closure)
 {
-    RootedValue value(cx, valueArg);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
     // Strings are associated with zones, not compartments,
     // so we copy the string by wrapping it.
     if (value.isString()) {
       RootedString strValue(cx, value.toString());
       if (!cx->compartment()->wrap(cx, strValue.address())) {
         return false;
       }
-      *vp = JS::StringValue(strValue);
+      vp.setString(strValue);
       return true;
     }
 
     const JSStructuredCloneCallbacks *callbacks =
         optionalCallbacks ?
         optionalCallbacks :
         cx->runtime()->structuredCloneCallbacks;
 
@@ -1695,42 +1693,41 @@ JSAutoStructuredCloneBuffer::steal(uint6
         *versionp = version_;
 
     data_ = nullptr;
     nbytes_ = 0;
     version_ = 0;
 }
 
 bool
-JSAutoStructuredCloneBuffer::read(JSContext *cx, JS::Value *vp,
+JSAutoStructuredCloneBuffer::read(JSContext *cx, JS::MutableHandleValue vp,
                                   const JSStructuredCloneCallbacks *optionalCallbacks,
                                   void *closure)
 {
     JS_ASSERT(cx);
     JS_ASSERT(data_);
     return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp,
                                     optionalCallbacks, closure);
 }
 
 bool
-JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::Value valueArg,
+JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::HandleValue value,
                                    const JSStructuredCloneCallbacks *optionalCallbacks,
                                    void *closure)
 {
-    JS::Value transferable = JSVAL_VOID;
-    return write(cx, valueArg, transferable, optionalCallbacks, closure);
+    JS::HandleValue transferable = JS::UndefinedHandleValue;
+    return write(cx, value, transferable, optionalCallbacks, closure);
 }
 
 bool
-JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::Value valueArg,
-                                   JS::Value transferable,
+JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::HandleValue value,
+                                   JS::HandleValue transferable,
                                    const JSStructuredCloneCallbacks *optionalCallbacks,
                                    void *closure)
 {
-    RootedValue value(cx, valueArg);
     clear();
     bool ok = !!JS_WriteStructuredClone(cx, value, &data_, &nbytes_,
                                         optionalCallbacks, closure,
                                         transferable);
     if (!ok) {
         data_ = nullptr;
         nbytes_ = 0;
         version_ = JS_STRUCTURED_CLONE_VERSION;
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -416,18 +416,17 @@ CloneNonReflectors(JSContext *cx, Mutabl
             &gForwarderStructuredCloneCallbacks,
             &rootedReflectors))
         {
             return false;
         }
     }
 
     // Now recreate the clones in the target compartment.
-    RootedValue rval(cx);
-    if (!buffer.read(cx, val.address(),
+    if (!buffer.read(cx, val,
         &gForwarderStructuredCloneCallbacks,
         &rootedReflectors))
     {
         return false;
     }
 
     return true;
 }
@@ -1760,15 +1759,15 @@ nsresult
 xpc::SetSandboxMetadata(JSContext *cx, HandleObject sandbox, HandleValue metadataArg)
 {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(IsSandbox(sandbox));
 
     RootedValue metadata(cx);
 
     JSAutoCompartment ac(cx, sandbox);
-    if (!JS_StructuredClone(cx, metadataArg, metadata.address(), nullptr, nullptr))
+    if (!JS_StructuredClone(cx, metadataArg, &metadata, nullptr, nullptr))
         return NS_ERROR_UNEXPECTED;
 
     JS_SetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT, metadata);
 
     return NS_OK;
 }