Backout changesets 7e6fb33fdf22:c85332df4320 (bug 905017) for windows bustage.
authorMs2ger <ms2ger@gmail.com>
Thu, 22 Aug 2013 10:16:30 +0200
changeset 156789 d5c39687fffce41563045be1a7fc7d6ab20f0191
parent 156788 c85332df4320446109b4673733f5bd211d3d3463
child 156790 a25a71001d7e9af16f75f256c9d086814435c362
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs905017
milestone26.0a1
backs out7e6fb33fdf224bfb5d54decdcecfd18ab398ef4a
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
Backout changesets 7e6fb33fdf22:c85332df4320 (bug 905017) for windows bustage.
config/check_spidermonkey_style.py
dom/base/StructuredCloneTags.h
dom/base/nsStructuredCloneContainer.cpp
dom/bluetooth/BluetoothReplyRunnable.h
dom/indexedDB/IndexedDatabase.h
dom/indexedDB/Key.h
dom/ipc/StructuredCloneUtils.cpp
dom/ipc/StructuredCloneUtils.h
dom/workers/Events.h
dom/workers/XMLHttpRequest.h
ipc/glue/IPCMessageUtils.h
js/ipc/JavaScriptParent.h
js/public/ProfilingStack.h
js/public/StructuredClone.h
js/public/Value.h
js/src/config/check_spidermonkey_style.py
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsclone.cpp
js/src/jsclone.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/moz.build
js/src/shell/js.cpp
js/src/vm/CharacterEncoding.cpp
js/src/vm/SPSProfiler.cpp
js/src/vm/SPSProfiler.h
js/src/vm/StructuredClone.cpp
js/src/vm/Value.cpp
js/xpconnect/public/nsAutoJSValHolder.h
security/manager/ssl/src/nsNSSIOLayer.cpp
tools/profiler/PseudoStack.h
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -297,50 +297,28 @@ def check_style():
 
 
 def module_name(name):
     '''Strip the trailing .cpp, .h, inlines.h or -inl.h from a filename.'''
 
     return name.replace('inlines.h', '').replace('-inl.h', '').replace('.h', '').replace('.cpp', '')
 
 
-def is_module_header(enclosing_inclname, header_inclname):
-    '''Determine if an included name is the "module header", i.e. should be
-    first in the file.'''
-
-    module = module_name(enclosing_inclname)
-
-    # Normal case, e.g. module == "foo/Bar", header_inclname == "foo/Bar.h".
-    if module == module_name(header_inclname):
-        return True
-
-    # A public header, e.g. module == "foo/Bar", header_inclname == "js/Bar.h".
-    m = re.match(r'js\/(.*)\.h', header_inclname)
-    if m is not None and module.endswith('/' + m.group(1)):
-        return True
-
-    # A weird public header case.
-    if module == 'jsmemorymetrics' and header_inclname == 'js/MemoryMetrics.h':
-        return True
-
-    return False
-
-
 class Include(object):
     '''Important information for a single #include statement.'''
 
     def __init__(self, inclname, linenum, is_system):
         self.inclname = inclname
         self.linenum = linenum
         self.is_system = is_system
 
     def isLeaf(self):
         return True
 
-    def section(self, enclosing_inclname):
+    def section(self, module):
         '''Identify which section inclname belongs to.
 
         The section numbers are as follows.
           0. Module header (e.g. jsfoo.h or jsfooinlines.h within jsfoo.cpp)
           1. mozilla/Foo.h
           2. <foo.h> or <foo>
           3. jsfoo.h, prmjtime.h, etc
           4. foo/Bar.h
@@ -350,19 +328,21 @@ class Include(object):
         '''
 
         if self.is_system:
             return 2
 
         if not self.inclname.endswith('.h'):
             return 7
 
-        # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need
-        # special handling.
-        if is_module_header(enclosing_inclname, self.inclname):
+        # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need special
+        # handling.
+        if module == module_name(self.inclname) or \
+           module == 'jsmemorymetrics' and self.inclname == 'js/MemoryMetrics.h' or \
+           module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h':
             return 0
 
         if '/' in self.inclname:
             if self.inclname.startswith('mozilla/'):
                 return 1
 
             if self.inclname.endswith('-inl.h'):
                 return 6
@@ -466,25 +446,27 @@ def do_file(filename, inclname, file_kin
                     error(filename, include.linenum,
                           'vanilla header includes an inline-header file ' + include.quote())
 
                 # Check a file doesn't #include itself.  (We do this here because the cycle
                 # detection below doesn't detect this case.)
                 if inclname == include.inclname:
                     error(filename, include.linenum, 'the file includes itself')
 
+    module = module_name(inclname)
+
     def check_includes_order(include1, include2):
         '''Check the ordering of two #include statements.'''
 
         if include1.inclname in oddly_ordered_inclnames or \
            include2.inclname in oddly_ordered_inclnames:
             return
 
-        section1 = include1.section(inclname)
-        section2 = include2.section(inclname)
+        section1 = include1.section(module)
+        section2 = include2.section(module)
         if (section1 > section2) or \
            ((section1 == section2) and (include1.inclname.lower() > include2.inclname.lower())):
             error(filename, str(include1.linenum) + ':' + str(include2.linenum),
                   include1.quote() + ' should be included after ' + include2.quote())
 
     # The #include statements in the files in assembler/ and yarr/ have all manner of implicit
     # ordering requirements.  Boo.  Ignore them.
     skip_order_checking = inclname.startswith(('assembler/', 'yarr/'))
--- a/dom/base/StructuredCloneTags.h
+++ b/dom/base/StructuredCloneTags.h
@@ -1,17 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef StructuredCloneTags_h__
 #define StructuredCloneTags_h__
 
-#include "js/StructuredClone.h"
-
 namespace mozilla {
 namespace dom {
 
 // CHANGING THE ORDER/PLACEMENT OF EXISTING ENUM VALUES MAY BREAK INDEXEDDB.
 // PROCEED WITH EXTREME CAUTION.
 enum StructuredCloneTags {
   SCTAG_BASE = JS_SCTAG_USER_MIN,
 
--- a/dom/base/nsStructuredCloneContainer.cpp
+++ b/dom/base/nsStructuredCloneContainer.cpp
@@ -9,17 +9,16 @@
 
 #include "nsCOMPtr.h"
 #include "nsIScriptContext.h"
 #include "nsIVariant.h"
 #include "nsIXPConnect.h"
 #include "nsServiceManagerUtils.h"
 #include "nsContentUtils.h"
 #include "jsapi.h"
-#include "js/StructuredClone.h"
 
 #include "mozilla/Base64.h"
 
 using namespace mozilla;
 
 NS_IMPL_ADDREF(nsStructuredCloneContainer)
 NS_IMPL_RELEASE(nsStructuredCloneContainer)
 
--- a/dom/bluetooth/BluetoothReplyRunnable.h
+++ b/dom/bluetooth/BluetoothReplyRunnable.h
@@ -5,17 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_bluetoothreplyrunnable_h__
 #define mozilla_dom_bluetooth_bluetoothreplyrunnable_h__
 
 #include "mozilla/Attributes.h"
 #include "BluetoothCommon.h"
 #include "nsThreadUtils.h"
-#include "js/Value.h"
+#include "jsapi.h"
 
 class nsIDOMDOMRequest;
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothReply;
 
 class BluetoothReplyRunnable : public nsRunnable
--- a/dom/indexedDB/IndexedDatabase.h
+++ b/dom/indexedDB/IndexedDatabase.h
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_indexeddb_indexeddatabase_h__
 #define mozilla_dom_indexeddb_indexeddatabase_h__
 
 #include "nsIProgrammingLanguage.h"
 
 #include "mozilla/Attributes.h"
-#include "js/StructuredClone.h"
+#include "jsapi.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 
 #define BEGIN_INDEXEDDB_NAMESPACE \
--- a/dom/indexedDB/Key.h
+++ b/dom/indexedDB/Key.h
@@ -6,18 +6,16 @@
 
 #ifndef mozilla_dom_indexeddb_key_h__
 #define mozilla_dom_indexeddb_key_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 
 #include "mozIStorageStatement.h"
 
-#include "js/Value.h"
-
 namespace IPC {
 template <typename T> struct ParamTraits;
 } // namespace IPC
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class Key
 {
@@ -156,17 +154,17 @@ public:
   void SetFromInteger(int64_t aInt)
   {
     mBuffer.Truncate();
     EncodeNumber(double(aInt), eFloat);
     TrimBuffer();
   }
 
   nsresult SetFromJSVal(JSContext* aCx,
-                        const JS::Value aVal)
+                        const jsval aVal)
   {
     mBuffer.Truncate();
 
     if (JSVAL_IS_NULL(aVal) || JSVAL_IS_VOID(aVal)) {
       Unset();
       return NS_OK;
     }
 
@@ -206,17 +204,17 @@ public:
     if (NS_SUCCEEDED(rv)) {
       aVal = value;
     }
     return rv;
   }
 
   nsresult AppendItem(JSContext* aCx,
                       bool aFirstOfArray,
-                      const JS::Value aVal)
+                      const jsval aVal)
   {
     nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0);
     if (NS_FAILED(rv)) {
       Unset();
       return rv;
     }
 
     return NS_OK;
@@ -300,17 +298,17 @@ private:
     while (!*end) {
       --end;
     }
 
     mBuffer.Truncate(end + 1 - mBuffer.BeginReading());
   }
 
   // Encoding functions. These append the encoded value to the end of mBuffer
-  inline nsresult EncodeJSVal(JSContext* aCx, const JS::Value aVal,
+  inline nsresult EncodeJSVal(JSContext* aCx, const jsval aVal,
                               uint8_t aTypeOffset)
   {
     return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0);
   }
   void EncodeString(const nsAString& aString, uint8_t aTypeOffset);
   void EncodeNumber(double aFloat, uint8_t aType);
 
   // Decoding functions. aPos points into mBuffer and is adjusted to point
@@ -326,17 +324,17 @@ private:
                            const unsigned char* aEnd,
                            nsString& aString);
   static double DecodeNumber(const unsigned char*& aPos,
                              const unsigned char* aEnd);
 
   nsCString mBuffer;
 
 private:
-  nsresult EncodeJSValInternal(JSContext* aCx, const JS::Value aVal,
+  nsresult EncodeJSValInternal(JSContext* aCx, const jsval aVal,
                                uint8_t aTypeOffset, uint16_t aRecursionDepth);
 
   static nsresult DecodeJSValInternal(const unsigned char*& aPos,
                                       const unsigned char* aEnd,
                                       JSContext* aCx, uint8_t aTypeOffset,
                                       JS::MutableHandle<JS::Value> aVal, uint16_t aRecursionDepth);
 };
 
--- a/dom/ipc/StructuredCloneUtils.cpp
+++ b/dom/ipc/StructuredCloneUtils.cpp
@@ -10,17 +10,16 @@
 #include "nsIDOMDOMException.h"
 #include "nsIMutable.h"
 #include "nsIXPConnect.h"
 
 #include "nsContentUtils.h"
 #include "nsJSEnvironment.h"
 #include "nsThreadUtils.h"
 #include "StructuredCloneTags.h"
-#include "jsapi.h"
 
 using namespace mozilla::dom;
 
 namespace {
 
 void
 Error(JSContext* aCx, uint32_t aErrorId)
 {
--- a/dom/ipc/StructuredCloneUtils.h
+++ b/dom/ipc/StructuredCloneUtils.h
@@ -6,17 +6,17 @@
 
 #ifndef mozilla_dom_StructuredCloneUtils_h
 #define mozilla_dom_StructuredCloneUtils_h
 
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsIDOMFile.h"
 
-#include "js/StructuredClone.h"
+#include "jsapi.h"
 
 namespace mozilla {
 
 struct SerializedStructuredCloneBuffer;
 
 namespace dom {
 
 struct
--- a/dom/workers/Events.h
+++ b/dom/workers/Events.h
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_events_h__
 #define mozilla_dom_workers_events_h__
 
 #include "Workers.h"
 
-#include "js/StructuredClone.h"
+class JSAutoStructuredCloneBuffer;
 
 BEGIN_WORKERS_NAMESPACE
 
 namespace events {
 
 bool
 InitClasses(JSContext* aCx, JS::Handle<JSObject*> aGlobal, bool aMainRuntime);
 
--- a/dom/workers/XMLHttpRequest.h
+++ b/dom/workers/XMLHttpRequest.h
@@ -10,18 +10,16 @@
 #include "mozilla/dom/workers/bindings/WorkerFeature.h"
 
 // Need this for XMLHttpRequestResponseType.
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/TypedArray.h"
 
-#include "js/StructuredClone.h"
-
 BEGIN_WORKERS_NAMESPACE
 
 class Proxy;
 class XMLHttpRequestUpload;
 class WorkerPrivate;
 
 class XMLHttpRequest : public XMLHttpRequestEventTarget,
                        public WorkerFeature
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -17,17 +17,17 @@
 #include "mozilla/Util.h"
 
 #include <stdint.h>
 
 #include "nsID.h"
 #include "nsMemory.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
-#include "js/StructuredClone.h"
+#include "jsapi.h"
 #include "nsCSSProperty.h"
 
 #ifdef _MSC_VER
 #pragma warning( disable : 4800 )
 #endif
 
 #if !defined(OS_POSIX)
 // This condition must be kept in sync with the one in
--- a/js/ipc/JavaScriptParent.h
+++ b/js/ipc/JavaScriptParent.h
@@ -5,17 +5,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_jsipc_JavaScriptParent__
 #define mozilla_jsipc_JavaScriptParent__
 
 #include "JavaScriptShared.h"
 #include "mozilla/jsipc/PJavaScriptParent.h"
-#include "jsclass.h"
 
 #ifdef XP_WIN
 #undef GetClassName
 #undef GetClassInfo
 #endif
 
 namespace mozilla {
 namespace jsipc {
deleted file mode 100644
--- a/js/public/ProfilingStack.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef js_ProfilingStack_h
-#define js_ProfilingStack_h
-
-#include "jsbytecode.h"
-#include "jstypes.h"
-
-#include "js/Utility.h"
-
-struct JSRuntime;
-class JSScript;
-
-namespace js {
-
-// A call stack can be specified to the JS engine such that all JS entry/exits
-// to functions push/pop an entry to/from the specified stack.
-//
-// For more detailed information, see vm/SPSProfiler.h.
-//
-class ProfileEntry
-{
-    // All fields are marked volatile to prevent the compiler from re-ordering
-    // instructions. Namely this sequence:
-    //
-    //    entry[size] = ...;
-    //    size++;
-    //
-    // If the size modification were somehow reordered before the stores, then
-    // if a sample were taken it would be examining bogus information.
-    //
-    // A ProfileEntry represents both a C++ profile entry and a JS one. Both use
-    // the string as a description, but JS uses the sp as NULL to indicate that
-    // it is a JS entry. The script_ is then only ever examined for a JS entry,
-    // and the idx is used by both, but with different meanings.
-    //
-    const char * volatile string; // Descriptive string of this entry
-    void * volatile sp;           // Relevant stack pointer for the entry
-    JSScript * volatile script_;  // if js(), non-null script which is running
-    int32_t volatile idx;         // if js(), idx of pc, otherwise line number
-
-  public:
-    // All of these methods are marked with the 'volatile' keyword because SPS's
-    // representation of the stack is stored such that all ProfileEntry
-    // instances are volatile. These methods would not be available unless they
-    // were marked as volatile as well.
-
-    bool js() volatile {
-        JS_ASSERT_IF(sp == NULL, script_ != NULL);
-        return sp == NULL;
-    }
-
-    uint32_t line() volatile { JS_ASSERT(!js()); return idx; }
-    JSScript *script() volatile { JS_ASSERT(js()); return script_; }
-    void *stackAddress() volatile { return sp; }
-    const char *label() volatile { return string; }
-
-    void setLine(uint32_t aLine) volatile { JS_ASSERT(!js()); idx = aLine; }
-    void setLabel(const char *aString) volatile { string = aString; }
-    void setStackAddress(void *aSp) volatile { sp = aSp; }
-    void setScript(JSScript *aScript) volatile { script_ = aScript; }
-
-    // We can't know the layout of JSScript, so look in vm/SPSProfiler.cpp.
-    JS_FRIEND_API(jsbytecode *) pc() volatile;
-    JS_FRIEND_API(void) setPC(jsbytecode *pc) volatile;
-
-    static size_t offsetOfString() { return offsetof(ProfileEntry, string); }
-    static size_t offsetOfStackAddress() { return offsetof(ProfileEntry, sp); }
-    static size_t offsetOfPCIdx() { return offsetof(ProfileEntry, idx); }
-    static size_t offsetOfScript() { return offsetof(ProfileEntry, script_); }
-
-    // The index used in the entry can either be a line number or the offset of
-    // a pc into a script's code. To signify a NULL pc, use a -1 index. This is
-    // checked against in pc() and setPC() to set/get the right pc.
-    static const int32_t NullPCIndex = -1;
-};
-
-JS_FRIEND_API(void)
-SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size,
-                         uint32_t max);
-
-JS_FRIEND_API(void)
-EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled);
-
-JS_FRIEND_API(jsbytecode*)
-ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip);
-
-} // namespace js
-
-#endif  /* js_ProfilingStack_h */
deleted file mode 100644
--- a/js/public/StructuredClone.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef js_StructuredClone_h
-#define js_StructuredClone_h
-
-#include <stdint.h>
-
-#include "jstypes.h"
-
-struct JSContext;
-class JSObject;
-struct JSRuntime;
-struct JSStructuredCloneReader;
-struct JSStructuredCloneWriter;
-
-namespace JS {
-template <typename T> class Handle;
-class Value;
-}
-
-// API for the HTML5 internal structured cloning algorithm.
-
-// Read structured data from the reader r. This hook is used to read a value
-// previously serialized by a call to the WriteStructuredCloneOp hook.
-//
-// tag and data are the pair of uint32_t values from the header. The callback
-// may use the JS_Read* APIs to read any other relevant parts of the object
-// from the reader r. closure is any value passed to the JS_ReadStructuredClone
-// function. Return the new object on success, NULL on error/exception.
-typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r,
-                                           uint32_t tag, uint32_t data, void *closure);
-
-// Structured data serialization hook. The engine can write primitive values,
-// Objects, Arrays, Dates, RegExps, TypedArrays, and ArrayBuffers. Any other
-// type of object requires application support. This callback must first use
-// the JS_WriteUint32Pair API to write an object header, passing a value
-// greater than JS_SCTAG_USER to the tag parameter. Then it can use the
-// JS_Write* APIs to write any other relevant parts of the value v to the
-// writer w. closure is any value passed to the JS_WriteStructuredCLone function.
-//
-// Return true on success, false on error/exception.
-typedef bool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w,
-                                         JS::Handle<JSObject*> obj, void *closure);
-
-// This is called when JS_WriteStructuredClone is given an invalid transferable.
-// To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException
-// with error set to one of the JS_SCERR_* values.
-typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid);
-
-// The maximum supported structured-clone serialization format version.
-#define JS_STRUCTURED_CLONE_VERSION 2
-
-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);
-
-// 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,
-                        const JSStructuredCloneCallbacks *optionalCallbacks,
-                        void *closure, JS::Value 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,
-                   const JSStructuredCloneCallbacks *optionalCallbacks, void *closure);
-
-// RAII sugar for JS_WriteStructuredClone.
-class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
-    uint64_t *data_;
-    size_t nbytes_;
-    uint32_t version_;
-
-  public:
-    JSAutoStructuredCloneBuffer()
-        : data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {}
-
-    ~JSAutoStructuredCloneBuffer() { clear(); }
-
-    uint64_t *data() const { return data_; }
-    size_t nbytes() const { return nbytes_; }
-
-    void clear();
-
-    // Copy some memory. It will be automatically freed by the destructor.
-    bool copy(const uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION);
-
-    // Adopt some memory. It will be automatically freed by the destructor.
-    // data must have been allocated by the JS engine (e.g., extracted via
-    // 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=NULL);
-
-    bool read(JSContext *cx, JS::Value *vp,
-              const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL);
-
-    bool write(JSContext *cx, JS::Value v,
-               const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL);
-
-    bool write(JSContext *cx, JS::Value v, JS::Value transferable,
-               const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL);
-
-    // Swap ownership with another JSAutoStructuredCloneBuffer.
-    void swap(JSAutoStructuredCloneBuffer &other);
-
-  private:
-    // Copy and assignment are not supported.
-    JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other);
-    JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other);
-};
-
-// The range of tag values the application may use for its own custom object types.
-#define JS_SCTAG_USER_MIN  ((uint32_t) 0xFFFF8000)
-#define JS_SCTAG_USER_MAX  ((uint32_t) 0xFFFFFFFF)
-
-#define JS_SCERR_RECURSION 0
-#define JS_SCERR_TRANSFERABLE 1
-
-JS_PUBLIC_API(void)
-JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks);
-
-JS_PUBLIC_API(bool)
-JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2);
-
-JS_PUBLIC_API(bool)
-JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len);
-
-JS_PUBLIC_API(bool)
-JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp);
-
-JS_PUBLIC_API(bool)
-JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data);
-
-JS_PUBLIC_API(bool)
-JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len);
-
-JS_PUBLIC_API(bool)
-JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v);
-
-#endif  /* js_StructuredClone_h */
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1891,30 +1891,12 @@ PRIVATE_TO_JSVAL(void *ptr)
 
 static inline void *
 JSVAL_TO_PRIVATE(jsval v)
 {
     MOZ_ASSERT(JSVAL_IS_DOUBLE(v));
     return JSVAL_TO_PRIVATE_PTR_IMPL(JSVAL_TO_IMPL(v));
 }
 
-// JS constants. For efficiency, prefer predicates (e.g. v.isNull()) and
-// constructing values from scratch (e.g. Int32Value(0)).  These constants are
-// stored in memory and initialized at startup, so testing against them and
-// using them requires memory loads and will be correspondingly slow.
-extern JS_PUBLIC_DATA(const jsval) JSVAL_NULL;
-extern JS_PUBLIC_DATA(const jsval) JSVAL_ZERO;
-extern JS_PUBLIC_DATA(const jsval) JSVAL_ONE;
-extern JS_PUBLIC_DATA(const jsval) JSVAL_FALSE;
-extern JS_PUBLIC_DATA(const jsval) JSVAL_TRUE;
-extern JS_PUBLIC_DATA(const jsval) JSVAL_VOID;
-
-namespace JS {
-
-extern JS_PUBLIC_DATA(const Handle<Value>) NullHandleValue;
-extern JS_PUBLIC_DATA(const Handle<Value>) UndefinedHandleValue;
-
-}
-
 #undef JS_VALUE_IS_CONSTEXPR
 #undef JS_RETURN_LAYOUT_FROM_BITS
 
 #endif /* js_Value_h */
--- a/js/src/config/check_spidermonkey_style.py
+++ b/js/src/config/check_spidermonkey_style.py
@@ -297,50 +297,28 @@ def check_style():
 
 
 def module_name(name):
     '''Strip the trailing .cpp, .h, inlines.h or -inl.h from a filename.'''
 
     return name.replace('inlines.h', '').replace('-inl.h', '').replace('.h', '').replace('.cpp', '')
 
 
-def is_module_header(enclosing_inclname, header_inclname):
-    '''Determine if an included name is the "module header", i.e. should be
-    first in the file.'''
-
-    module = module_name(enclosing_inclname)
-
-    # Normal case, e.g. module == "foo/Bar", header_inclname == "foo/Bar.h".
-    if module == module_name(header_inclname):
-        return True
-
-    # A public header, e.g. module == "foo/Bar", header_inclname == "js/Bar.h".
-    m = re.match(r'js\/(.*)\.h', header_inclname)
-    if m is not None and module.endswith('/' + m.group(1)):
-        return True
-
-    # A weird public header case.
-    if module == 'jsmemorymetrics' and header_inclname == 'js/MemoryMetrics.h':
-        return True
-
-    return False
-
-
 class Include(object):
     '''Important information for a single #include statement.'''
 
     def __init__(self, inclname, linenum, is_system):
         self.inclname = inclname
         self.linenum = linenum
         self.is_system = is_system
 
     def isLeaf(self):
         return True
 
-    def section(self, enclosing_inclname):
+    def section(self, module):
         '''Identify which section inclname belongs to.
 
         The section numbers are as follows.
           0. Module header (e.g. jsfoo.h or jsfooinlines.h within jsfoo.cpp)
           1. mozilla/Foo.h
           2. <foo.h> or <foo>
           3. jsfoo.h, prmjtime.h, etc
           4. foo/Bar.h
@@ -350,19 +328,21 @@ class Include(object):
         '''
 
         if self.is_system:
             return 2
 
         if not self.inclname.endswith('.h'):
             return 7
 
-        # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need
-        # special handling.
-        if is_module_header(enclosing_inclname, self.inclname):
+        # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need special
+        # handling.
+        if module == module_name(self.inclname) or \
+           module == 'jsmemorymetrics' and self.inclname == 'js/MemoryMetrics.h' or \
+           module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h':
             return 0
 
         if '/' in self.inclname:
             if self.inclname.startswith('mozilla/'):
                 return 1
 
             if self.inclname.endswith('-inl.h'):
                 return 6
@@ -466,25 +446,27 @@ def do_file(filename, inclname, file_kin
                     error(filename, include.linenum,
                           'vanilla header includes an inline-header file ' + include.quote())
 
                 # Check a file doesn't #include itself.  (We do this here because the cycle
                 # detection below doesn't detect this case.)
                 if inclname == include.inclname:
                     error(filename, include.linenum, 'the file includes itself')
 
+    module = module_name(inclname)
+
     def check_includes_order(include1, include2):
         '''Check the ordering of two #include statements.'''
 
         if include1.inclname in oddly_ordered_inclnames or \
            include2.inclname in oddly_ordered_inclnames:
             return
 
-        section1 = include1.section(inclname)
-        section2 = include2.section(inclname)
+        section1 = include1.section(module)
+        section2 = include2.section(module)
         if (section1 > section2) or \
            ((section1 == section2) and (include1.inclname.lower() > include2.inclname.lower())):
             error(filename, str(include1.linenum) + ':' + str(include2.linenum),
                   include1.quote() + ' should be included after ' + include2.quote())
 
     # The #include statements in the files in assembler/ and yarr/ have all manner of implicit
     # ordering requirements.  Boo.  Ignore them.
     skip_order_checking = inclname.startswith(('assembler/', 'yarr/'))
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -16,16 +16,17 @@
 #include <ctype.h>
 #include <stdarg.h>
 #include <string.h>
 #include <sys/stat.h>
 
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
+#include "jsclone.h"
 #include "jscntxt.h"
 #include "jsdate.h"
 #include "jsexn.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsmath.h"
@@ -54,17 +55,16 @@
 #include "builtin/ParallelArray.h"
 #include "builtin/RegExp.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FullParseHandler.h"  // for JS_BufferIsCompileableUnit
 #include "frontend/Parser.h" // for JS_BufferIsCompileableUnit
 #include "gc/Marking.h"
 #include "jit/AsmJSLink.h"
 #include "js/CharacterEncoding.h"
-#include "js/StructuredClone.h"
 #if ENABLE_INTL_API
 #include "unicode/uclean.h"
 #include "unicode/utypes.h"
 #endif // ENABLE_INTL_API
 #include "vm/DateObject.h"
 #include "vm/Debugger.h"
 #include "vm/ErrorObject.h"
 #include "vm/Interpreter.h"
@@ -123,16 +123,27 @@ JS::detail::CallMethodIfWrapped(JSContex
 #define JS_ADDRESSOF_VA_LIST(ap) (&(ap))
 #endif
 
 #ifdef JS_USE_JSID_STRUCT_TYPES
 const jsid JSID_VOID  = { size_t(JSID_TYPE_VOID) };
 const jsid JSID_EMPTY = { size_t(JSID_TYPE_OBJECT) };
 #endif
 
+const jsval JSVAL_NULL  = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_NULL,      0));
+const jsval JSVAL_ZERO  = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32,     0));
+const jsval JSVAL_ONE   = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32,     1));
+const jsval JSVAL_FALSE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN,   false));
+const jsval JSVAL_TRUE  = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN,   true));
+const jsval JSVAL_VOID  = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0));
+const HandleValue JS::NullHandleValue =
+    HandleValue::fromMarkedLocation(&JSVAL_NULL);
+const HandleValue JS::UndefinedHandleValue =
+    HandleValue::fromMarkedLocation(&JSVAL_VOID);
+
 const jsid voidIdValue = JSID_VOID;
 const jsid emptyIdValue = JSID_EMPTY;
 const HandleId JS::JSID_VOIDHANDLE = HandleId::fromMarkedLocation(&voidIdValue);
 const HandleId JS::JSID_EMPTYHANDLE = HandleId::fromMarkedLocation(&emptyIdValue);
 
 /* Make sure that jschar is two bytes unsigned integer */
 JS_STATIC_ASSERT((jschar)-1 > 0);
 JS_STATIC_ASSERT(sizeof(jschar) == 2);
@@ -169,32 +180,28 @@ JS_GetEmptyStringValue(JSContext *cx)
 
 JS_PUBLIC_API(JSString *)
 JS_GetEmptyString(JSRuntime *rt)
 {
     JS_ASSERT(rt->hasContexts());
     return rt->emptyString;
 }
 
-namespace js {
-
-void
+static void
 AssertHeapIsIdle(JSRuntime *rt)
 {
     JS_ASSERT(rt->heapState == js::Idle);
 }
 
-void
+static void
 AssertHeapIsIdle(JSContext *cx)
 {
     AssertHeapIsIdle(cx->runtime());
 }
 
-}
-
 static void
 AssertHeapIsIdleOrIterating(JSRuntime *rt)
 {
     JS_ASSERT(!rt->isHeapCollecting());
 }
 
 static void
 AssertHeapIsIdleOrIterating(JSContext *cx)
@@ -5913,16 +5920,230 @@ JS_ParseJSONWithReviver(JSContext *cx, c
     RootedValue reviver(cx, reviverArg), value(cx);
     if (!ParseJSONWithReviver(cx, StableCharPtr(chars, len), len, reviver, &value))
         return false;
 
     *vp = value;
     return true;
 }
 
+JS_PUBLIC_API(bool)
+JS_ReadStructuredClone(JSContext *cx, uint64_t *buf, size_t nbytes,
+                       uint32_t version, jsval *vp,
+                       const JSStructuredCloneCallbacks *optionalCallbacks,
+                       void *closure)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+
+    if (version > JS_STRUCTURED_CLONE_VERSION) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION);
+        return false;
+    }
+    const JSStructuredCloneCallbacks *callbacks =
+        optionalCallbacks ?
+        optionalCallbacks :
+        cx->runtime()->structuredCloneCallbacks;
+    return ReadStructuredClone(cx, buf, nbytes, vp, callbacks, closure);
+}
+
+JS_PUBLIC_API(bool)
+JS_WriteStructuredClone(JSContext *cx, jsval valueArg, uint64_t **bufp, size_t *nbytesp,
+                        const JSStructuredCloneCallbacks *optionalCallbacks,
+                        void *closure, jsval transferable)
+{
+    RootedValue value(cx, valueArg);
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, value);
+
+    const JSStructuredCloneCallbacks *callbacks =
+        optionalCallbacks ?
+        optionalCallbacks :
+        cx->runtime()->structuredCloneCallbacks;
+    return WriteStructuredClone(cx, value, (uint64_t **) bufp, nbytesp,
+                                callbacks, closure, transferable);
+}
+
+JS_PUBLIC_API(bool)
+JS_ClearStructuredClone(const uint64_t *data, size_t nbytes)
+{
+    return ClearStructuredClone(data, nbytes);
+}
+
+JS_PUBLIC_API(bool)
+JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes,
+                                   bool *hasTransferable)
+{
+    bool transferable;
+    if (!StructuredCloneHasTransferObjects(data, nbytes, &transferable))
+        return false;
+
+    *hasTransferable = transferable;
+    return true;
+}
+
+JS_PUBLIC_API(bool)
+JS_StructuredClone(JSContext *cx, jsval valueArg, jsval *vp,
+                   const JSStructuredCloneCallbacks *optionalCallbacks,
+                   void *closure)
+{
+    RootedValue value(cx, valueArg);
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, value);
+
+    const JSStructuredCloneCallbacks *callbacks =
+        optionalCallbacks ?
+        optionalCallbacks :
+        cx->runtime()->structuredCloneCallbacks;
+    JSAutoStructuredCloneBuffer buf;
+    return buf.write(cx, value, callbacks, closure) &&
+           buf.read(cx, vp, callbacks, closure);
+}
+
+void
+JSAutoStructuredCloneBuffer::clear()
+{
+    if (data_) {
+        ClearStructuredClone(data_, nbytes_);
+        data_ = NULL;
+        nbytes_ = 0;
+        version_ = 0;
+    }
+}
+
+void
+JSAutoStructuredCloneBuffer::adopt(uint64_t *data, size_t nbytes, uint32_t version)
+{
+    clear();
+    data_ = data;
+    nbytes_ = nbytes;
+    version_ = version;
+}
+
+bool
+JSAutoStructuredCloneBuffer::copy(const uint64_t *srcData, size_t nbytes, uint32_t version)
+{
+    // transferable objects cannot be copied
+    bool hasTransferable;
+    if (!StructuredCloneHasTransferObjects(data_, nbytes_, &hasTransferable) ||
+        hasTransferable)
+        return false;
+
+    uint64_t *newData = static_cast<uint64_t *>(js_malloc(nbytes));
+    if (!newData)
+        return false;
+
+    js_memcpy(newData, srcData, nbytes);
+
+    clear();
+    data_ = newData;
+    nbytes_ = nbytes;
+    version_ = version;
+    return true;
+}
+void
+JSAutoStructuredCloneBuffer::steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp)
+{
+    *datap = data_;
+    *nbytesp = nbytes_;
+    if (versionp)
+        *versionp = version_;
+
+    data_ = NULL;
+    nbytes_ = 0;
+    version_ = 0;
+}
+
+bool
+JSAutoStructuredCloneBuffer::read(JSContext *cx, jsval *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, jsval valueArg,
+                                   const JSStructuredCloneCallbacks *optionalCallbacks,
+                                   void *closure)
+{
+    jsval transferable = JSVAL_VOID;
+    return write(cx, valueArg, transferable, optionalCallbacks, closure);
+}
+
+bool
+JSAutoStructuredCloneBuffer::write(JSContext *cx, jsval valueArg,
+                                   jsval 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_ = NULL;
+        nbytes_ = 0;
+        version_ = JS_STRUCTURED_CLONE_VERSION;
+    }
+    return ok;
+}
+
+void
+JSAutoStructuredCloneBuffer::swap(JSAutoStructuredCloneBuffer &other)
+{
+    uint64_t *data = other.data_;
+    size_t nbytes = other.nbytes_;
+    uint32_t version = other.version_;
+
+    other.data_ = this->data_;
+    other.nbytes_ = this->nbytes_;
+    other.version_ = this->version_;
+
+    this->data_ = data;
+    this->nbytes_ = nbytes;
+    this->version_ = version;
+}
+
+JS_PUBLIC_API(void)
+JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks)
+{
+    rt->structuredCloneCallbacks = callbacks;
+}
+
+JS_PUBLIC_API(bool)
+JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2)
+{
+    return r->input().readPair((uint32_t *) p1, (uint32_t *) p2);
+}
+
+JS_PUBLIC_API(bool)
+JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len)
+{
+    return r->input().readBytes(p, len);
+}
+
+JS_PUBLIC_API(bool)
+JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data)
+{
+    return w->output().writePair(tag, data);
+}
+
+JS_PUBLIC_API(bool)
+JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len)
+{
+    return w->output().writeBytes(p, len);
+}
+
 /************************************************************************/
 
 JS_PUBLIC_API(void)
 JS_ReportError(JSContext *cx, const char *format, ...)
 {
     va_list ap;
 
     AssertHeapIsIdle(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1142,18 +1142,64 @@ typedef JSObject *
 
 typedef void
 (* JSDestroyCompartmentCallback)(JSFreeOp *fop, JSCompartment *compartment);
 
 typedef void
 (* JSCompartmentNameCallback)(JSRuntime *rt, JSCompartment *compartment,
                               char *buf, size_t bufsize);
 
+/*
+ * Read structured data from the reader r. This hook is used to read a value
+ * previously serialized by a call to the WriteStructuredCloneOp hook.
+ *
+ * tag and data are the pair of uint32_t values from the header. The callback
+ * may use the JS_Read* APIs to read any other relevant parts of the object
+ * from the reader r. closure is any value passed to the JS_ReadStructuredClone
+ * function. Return the new object on success, NULL on error/exception.
+ */
+typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r,
+                                           uint32_t tag, uint32_t data, void *closure);
+
+/*
+ * Structured data serialization hook. The engine can write primitive values,
+ * Objects, Arrays, Dates, RegExps, TypedArrays, and ArrayBuffers. Any other
+ * type of object requires application support. This callback must first use
+ * the JS_WriteUint32Pair API to write an object header, passing a value
+ * greater than JS_SCTAG_USER to the tag parameter. Then it can use the
+ * JS_Write* APIs to write any other relevant parts of the value v to the
+ * writer w. closure is any value passed to the JS_WriteStructuredCLone function.
+ *
+ * Return true on success, false on error/exception.
+ */
+typedef bool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w,
+                                         JS::Handle<JSObject*> obj, void *closure);
+
+/*
+ * This is called when JS_WriteStructuredClone is given an invalid transferable.
+ * To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException
+ * with error set to one of the JS_SCERR_* values.
+ */
+typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid);
+
 /************************************************************************/
 
+/*
+ * JS constants. For efficiency, prefer predicates (e.g. v.isNull()) and
+ * constructing values from scratch (e.g. Int32Value(0)).  These constants are
+ * stored in memory and initialized at startup, so testing against them and
+ * using them requires memory loads and will be correspondingly slow.
+ */
+extern JS_PUBLIC_DATA(const jsval) JSVAL_NULL;
+extern JS_PUBLIC_DATA(const jsval) JSVAL_ZERO;
+extern JS_PUBLIC_DATA(const jsval) JSVAL_ONE;
+extern JS_PUBLIC_DATA(const jsval) JSVAL_FALSE;
+extern JS_PUBLIC_DATA(const jsval) JSVAL_TRUE;
+extern JS_PUBLIC_DATA(const jsval) JSVAL_VOID;
+
 static JS_ALWAYS_INLINE jsval
 JS_NumberValue(double d)
 {
     int32_t i;
     d = JS_CANONICALIZE_NAN(d);
     if (mozilla::DoubleIsInt32(d, &i))
         return INT_TO_JSVAL(i);
     return DOUBLE_TO_JSVAL(d);
@@ -1825,22 +1871,16 @@ template <> struct GCMethods<jsid>
     static bool poisoned(jsid id) { return JS::IsPoisonedId(id); }
     static bool needsPostBarrier(jsid id) { return false; }
 #ifdef JSGC_GENERATIONAL
     static void postBarrier(jsid *idp) {}
     static void relocate(jsid *idp) {}
 #endif
 };
 
-void
-AssertHeapIsIdle(JSRuntime *rt);
-
-void
-AssertHeapIsIdle(JSContext *cx);
-
 } /* namespace js */
 
 class JSAutoRequest
 {
   public:
     JSAutoRequest(JSContext *cx
                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : mContext(cx)
@@ -4664,16 +4704,144 @@ JS_PUBLIC_API(bool)
 JS_ParseJSON(JSContext *cx, const jschar *chars, uint32_t len, JS::MutableHandle<JS::Value> vp);
 
 JS_PUBLIC_API(bool)
 JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32_t len, jsval reviver,
                         jsval *vp);
 
 /************************************************************************/
 
+/* API for the HTML5 internal structured cloning algorithm. */
+
+/* The maximum supported structured-clone serialization format version. */
+#define JS_STRUCTURED_CLONE_VERSION 2
+
+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, jsval *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, jsval v, uint64_t **datap, size_t *nbytesp,
+                        const JSStructuredCloneCallbacks *optionalCallbacks,
+                        void *closure, jsval 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, jsval v, jsval *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_;
+
+  public:
+    JSAutoStructuredCloneBuffer()
+        : data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {}
+
+    ~JSAutoStructuredCloneBuffer() { clear(); }
+
+    uint64_t *data() const { return data_; }
+    size_t nbytes() const { return nbytes_; }
+
+    void clear();
+
+    /* Copy some memory. It will be automatically freed by the destructor. */
+    bool copy(const uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION);
+
+    /*
+     * Adopt some memory. It will be automatically freed by the destructor.
+     * data must have been allocated by the JS engine (e.g., extracted via
+     * 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=NULL);
+
+    bool read(JSContext *cx, jsval *vp,
+              const JSStructuredCloneCallbacks *optionalCallbacks=NULL,
+              void *closure=NULL);
+
+    bool write(JSContext *cx, jsval v,
+               const JSStructuredCloneCallbacks *optionalCallbacks=NULL,
+               void *closure=NULL);
+
+    bool write(JSContext *cx, jsval v,
+               jsval transferable,
+               const JSStructuredCloneCallbacks *optionalCallbacks=NULL,
+               void *closure=NULL);
+
+    /**
+     * Swap ownership with another JSAutoStructuredCloneBuffer.
+     */
+    void swap(JSAutoStructuredCloneBuffer &other);
+
+  private:
+    /* Copy and assignment are not supported. */
+    JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other);
+    JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other);
+};
+
+/* API for implementing custom serialization behavior (for ImageData, File, etc.) */
+
+/* The range of tag values the application may use for its own custom object types. */
+#define JS_SCTAG_USER_MIN  ((uint32_t) 0xFFFF8000)
+#define JS_SCTAG_USER_MAX  ((uint32_t) 0xFFFFFFFF)
+
+#define JS_SCERR_RECURSION 0
+#define JS_SCERR_TRANSFERABLE 1
+
+JS_PUBLIC_API(void)
+JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks);
+
+JS_PUBLIC_API(bool)
+JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2);
+
+JS_PUBLIC_API(bool)
+JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len);
+
+JS_PUBLIC_API(bool)
+JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp);
+
+JS_PUBLIC_API(bool)
+JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data);
+
+JS_PUBLIC_API(bool)
+JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len);
+
+JS_PUBLIC_API(bool)
+JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v);
+
+/************************************************************************/
+
 /*
  * The default locale for the ECMAScript Internationalization API
  * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat).
  * Note that the Internationalization API encourages clients to
  * specify their own locales.
  * The locale string remains owned by the caller.
  */
 extern JS_PUBLIC_API(bool)
@@ -5082,16 +5250,19 @@ JS_DecodeScript(JSContext *cx, const voi
                 JSPrincipals *principals, JSPrincipals *originPrincipals);
 
 extern JS_PUBLIC_API(JSObject *)
 JS_DecodeInterpretedFunction(JSContext *cx, const void *data, uint32_t length,
                              JSPrincipals *principals, JSPrincipals *originPrincipals);
 
 namespace JS {
 
+extern JS_PUBLIC_DATA(const Handle<Value>) NullHandleValue;
+extern JS_PUBLIC_DATA(const Handle<Value>) UndefinedHandleValue;
+
 extern JS_PUBLIC_DATA(const Handle<jsid>) JSID_VOIDHANDLE;
 extern JS_PUBLIC_DATA(const Handle<jsid>) JSID_EMPTYHANDLE;
 
 } /* namespace JS */
 
 namespace js {
 
 /*
new file mode 100644
--- /dev/null
+++ b/js/src/jsclone.cpp
@@ -0,0 +1,1282 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file implements the structured clone algorithm of
+ * http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#safe-passing-of-structured-data
+ *
+ * The implementation differs slightly in that it uses an explicit stack, and
+ * the "memory" maps source objects to sequential integer indexes rather than
+ * directly pointing to destination objects. As a result, the order in which
+ * things are added to the memory must exactly match the order in which they
+ * are placed into 'allObjs', an analogous array of back-referenceable
+ * destination objects constructed while reading.
+ *
+ * For the most part, this is easy: simply add objects to the memory when first
+ * encountering them. But reading in a typed array requires an ArrayBuffer for
+ * construction, so objects cannot just be added to 'allObjs' in the order they
+ * are created. If they were, ArrayBuffers would come before typed arrays when
+ * in fact the typed array was added to 'memory' first.
+ *
+ * So during writing, we add objects to the memory when first encountering
+ * them. When reading a typed array, a placeholder is pushed onto allObjs until
+ * the ArrayBuffer has been read, then it is updated with the actual typed
+ * array object.
+ */
+
+#include "jsclone.h"
+
+#include "mozilla/Endian.h"
+#include "mozilla/FloatingPoint.h"
+
+#include "jsdate.h"
+#include "jswrapper.h"
+
+#include "vm/TypedArrayObject.h"
+#include "vm/WrapperObject.h"
+
+#include "jscntxtinlines.h"
+#include "jsobjinlines.h"
+
+using namespace js;
+
+using mozilla::IsNaN;
+using mozilla::LittleEndian;
+using mozilla::NativeEndian;
+
+enum StructuredDataType {
+    /* Structured data types provided by the engine */
+    SCTAG_FLOAT_MAX = 0xFFF00000,
+    SCTAG_NULL = 0xFFFF0000,
+    SCTAG_UNDEFINED,
+    SCTAG_BOOLEAN,
+    SCTAG_INDEX,
+    SCTAG_STRING,
+    SCTAG_DATE_OBJECT,
+    SCTAG_REGEXP_OBJECT,
+    SCTAG_ARRAY_OBJECT,
+    SCTAG_OBJECT_OBJECT,
+    SCTAG_ARRAY_BUFFER_OBJECT,
+    SCTAG_BOOLEAN_OBJECT,
+    SCTAG_STRING_OBJECT,
+    SCTAG_NUMBER_OBJECT,
+    SCTAG_BACK_REFERENCE_OBJECT,
+    SCTAG_TRANSFER_MAP_HEADER,
+    SCTAG_TRANSFER_MAP,
+    SCTAG_TYPED_ARRAY_OBJECT,
+    SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
+    SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_INT8,
+    SCTAG_TYPED_ARRAY_V1_UINT8 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT8,
+    SCTAG_TYPED_ARRAY_V1_INT16 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_INT16,
+    SCTAG_TYPED_ARRAY_V1_UINT16 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT16,
+    SCTAG_TYPED_ARRAY_V1_INT32 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_INT32,
+    SCTAG_TYPED_ARRAY_V1_UINT32 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT32,
+    SCTAG_TYPED_ARRAY_V1_FLOAT32 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_FLOAT32,
+    SCTAG_TYPED_ARRAY_V1_FLOAT64 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_FLOAT64,
+    SCTAG_TYPED_ARRAY_V1_UINT8_CLAMPED = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT8_CLAMPED,
+    SCTAG_TYPED_ARRAY_V1_MAX = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_MAX - 1,
+    SCTAG_END_OF_BUILTIN_TYPES
+};
+
+enum TransferableMapHeader {
+    SCTAG_TM_NOT_MARKED = 0,
+    SCTAG_TM_MARKED
+};
+
+JS_FRIEND_API(uint64_t)
+js_GetSCOffset(JSStructuredCloneWriter* writer)
+{
+    JS_ASSERT(writer);
+    return writer->output().count() * sizeof(uint64_t);
+}
+
+JS_STATIC_ASSERT(SCTAG_END_OF_BUILTIN_TYPES <= JS_SCTAG_USER_MIN);
+JS_STATIC_ASSERT(JS_SCTAG_USER_MIN <= JS_SCTAG_USER_MAX);
+JS_STATIC_ASSERT(TypedArrayObject::TYPE_INT8 == 0);
+
+bool
+js::WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp,
+                         const JSStructuredCloneCallbacks *cb, void *cbClosure,
+                         jsval transferable)
+{
+    SCOutput out(cx);
+    JSStructuredCloneWriter w(out, cb, cbClosure, transferable);
+    return w.init() && w.write(v) && out.extractBuffer(bufp, nbytesp);
+}
+
+bool
+js::ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp,
+                        const JSStructuredCloneCallbacks *cb, void *cbClosure)
+{
+    SCInput in(cx, data, nbytes);
+
+    /* XXX disallow callers from using internal pointers to GC things. */
+    SkipRoot skip(cx, &in);
+
+    JSStructuredCloneReader r(in, cb, cbClosure);
+    return r.read(vp);
+}
+
+bool
+js::ClearStructuredClone(const uint64_t *data, size_t nbytes)
+{
+    const uint64_t *point = data;
+    const uint64_t *end = data + nbytes / 8;
+
+    uint64_t u = LittleEndian::readUint64(point++);
+    uint32_t tag = uint32_t(u >> 32);
+    if (tag == SCTAG_TRANSFER_MAP_HEADER) {
+        if ((TransferableMapHeader)uint32_t(u) == SCTAG_TM_NOT_MARKED) {
+            while (point != end) {
+                uint64_t u = LittleEndian::readUint64(point++);
+                uint32_t tag = uint32_t(u >> 32);
+                if (tag == SCTAG_TRANSFER_MAP) {
+                    u = LittleEndian::readUint64(point++);
+                    js_free(reinterpret_cast<void*>(u));
+                } else {
+                    // The only things in the transfer map should be
+                    // SCTAG_TRANSFER_MAP tags paired with pointers. If we find
+                    // any other tag, we've walked off the end of the transfer
+                    // map.
+                    break;
+                }
+            }
+        }
+    }
+
+    js_free((void *)data);
+    return true;
+}
+
+bool
+js::StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool *hasTransferable)
+{
+    *hasTransferable = false;
+
+    if (data) {
+        uint64_t u = LittleEndian::readUint64(data);
+        uint32_t tag = uint32_t(u >> 32);
+        if (tag == SCTAG_TRANSFER_MAP_HEADER) {
+            *hasTransferable = true;
+        }
+    }
+
+    return true;
+}
+
+static inline uint64_t
+PairToUInt64(uint32_t tag, uint32_t data)
+{
+    return uint64_t(data) | (uint64_t(tag) << 32);
+}
+
+bool
+SCInput::eof()
+{
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "truncated");
+    return false;
+}
+
+SCInput::SCInput(JSContext *cx, uint64_t *data, size_t nbytes)
+    : cx(cx), point(data), end(data + nbytes / 8)
+{
+    JS_ASSERT((uintptr_t(data) & 7) == 0);
+    JS_ASSERT((nbytes & 7) == 0);
+}
+
+bool
+SCInput::read(uint64_t *p)
+{
+    if (point == end) {
+        *p = 0;  /* initialize to shut GCC up */
+        return eof();
+    }
+    *p = LittleEndian::readUint64(point++);
+    return true;
+}
+
+bool
+SCInput::readPair(uint32_t *tagp, uint32_t *datap)
+{
+    uint64_t u;
+    bool ok = read(&u);
+    if (ok) {
+        *tagp = uint32_t(u >> 32);
+        *datap = uint32_t(u);
+    }
+    return ok;
+}
+
+bool
+SCInput::get(uint64_t *p)
+{
+    if (point == end)
+        return eof();
+    *p = LittleEndian::readUint64(point);
+    return true;
+}
+
+bool
+SCInput::getPair(uint32_t *tagp, uint32_t *datap)
+{
+    uint64_t u;
+    if (!get(&u))
+        return false;
+
+    *tagp = uint32_t(u >> 32);
+    *datap = uint32_t(u);
+    return true;
+}
+
+bool
+SCInput::replace(uint64_t u)
+{
+    if (point == end)
+       return eof();
+    LittleEndian::writeUint64(point, u);
+    return true;
+}
+
+bool
+SCInput::replacePair(uint32_t tag, uint32_t data)
+{
+    return replace(PairToUInt64(tag, data));
+}
+
+/*
+ * The purpose of this never-inlined function is to avoid a strange g++ build
+ * error on OS X 10.5 (see bug 624080).  :-(
+ */
+static JS_NEVER_INLINE double
+CanonicalizeNan(double d)
+{
+    return JS_CANONICALIZE_NAN(d);
+}
+
+bool
+SCInput::readDouble(double *p)
+{
+    union {
+        uint64_t u;
+        double d;
+    } pun;
+    if (!read(&pun.u))
+        return false;
+    *p = CanonicalizeNan(pun.d);
+    return true;
+}
+
+template <typename T>
+static void
+copyAndSwapFromLittleEndian(T *dest, const void *src, size_t nelems)
+{
+    NativeEndian::copyAndSwapFromLittleEndian(dest, src, nelems);
+}
+
+template <>
+void
+copyAndSwapFromLittleEndian(uint8_t *dest, const void *src, size_t nelems)
+{
+    memcpy(dest, src, nelems);
+}
+
+template <class T>
+bool
+SCInput::readArray(T *p, size_t nelems)
+{
+    JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
+
+    /*
+     * Fail if nelems is so huge as to make JS_HOWMANY overflow or if nwords is
+     * larger than the remaining data.
+     */
+    size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
+    if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems || nwords > size_t(end - point))
+        return eof();
+
+    copyAndSwapFromLittleEndian(p, point, nelems);
+    point += nwords;
+    return true;
+}
+
+bool
+SCInput::readBytes(void *p, size_t nbytes)
+{
+    return readArray((uint8_t *) p, nbytes);
+}
+
+bool
+SCInput::readChars(jschar *p, size_t nchars)
+{
+    JS_ASSERT(sizeof(jschar) == sizeof(uint16_t));
+    return readArray((uint16_t *) p, nchars);
+}
+
+bool
+SCInput::readPtr(void **p)
+{
+    // On a 32 bit system the void* variable we have to write to is only
+    // 32 bits, so we create a 64 temporary and discard the unused bits.
+    uint64_t tmp;
+    bool ret = read(&tmp);
+    *p = reinterpret_cast<void*>(tmp);
+    return ret;
+}
+
+SCOutput::SCOutput(JSContext *cx) : cx(cx), buf(cx) {}
+
+bool
+SCOutput::write(uint64_t u)
+{
+    return buf.append(NativeEndian::swapToLittleEndian(u));
+}
+
+bool
+SCOutput::writePair(uint32_t tag, uint32_t data)
+{
+    /*
+     * As it happens, the tag word appears after the data word in the output.
+     * This is because exponents occupy the last 2 bytes of doubles on the
+     * little-endian platforms we care most about.
+     *
+     * For example, JSVAL_TRUE is written using writePair(SCTAG_BOOLEAN, 1).
+     * PairToUInt64 produces the number 0xFFFF000200000001.
+     * That is written out as the bytes 01 00 00 00 02 00 FF FF.
+     */
+    return write(PairToUInt64(tag, data));
+}
+
+static inline uint64_t
+ReinterpretDoubleAsUInt64(double d)
+{
+    union {
+        double d;
+        uint64_t u;
+    } pun;
+    pun.d = d;
+    return pun.u;
+}
+
+static inline double
+ReinterpretUInt64AsDouble(uint64_t u)
+{
+    union {
+        uint64_t u;
+        double d;
+    } pun;
+    pun.u = u;
+    return pun.d;
+}
+
+static inline double
+ReinterpretPairAsDouble(uint32_t tag, uint32_t data)
+{
+    return ReinterpretUInt64AsDouble(PairToUInt64(tag, data));
+}
+
+bool
+SCOutput::writeDouble(double d)
+{
+    return write(ReinterpretDoubleAsUInt64(CanonicalizeNan(d)));
+}
+
+template <typename T>
+static void
+copyAndSwapToLittleEndian(void *dest, const T *src, size_t nelems)
+{
+    NativeEndian::copyAndSwapToLittleEndian(dest, src, nelems);
+}
+
+template <>
+void
+copyAndSwapToLittleEndian(void *dest, const uint8_t *src, size_t nelems)
+{
+    memcpy(dest, src, nelems);
+}
+
+template <class T>
+bool
+SCOutput::writeArray(const T *p, size_t nelems)
+{
+    JS_ASSERT(8 % sizeof(T) == 0);
+    JS_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
+
+    if (nelems == 0)
+        return true;
+
+    if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems) {
+        js_ReportAllocationOverflow(context());
+        return false;
+    }
+    size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
+    size_t start = buf.length();
+    if (!buf.growByUninitialized(nwords))
+        return false;
+
+    buf.back() = 0;  /* zero-pad to an 8-byte boundary */
+
+    T *q = (T *) &buf[start];
+    copyAndSwapToLittleEndian(q, p, nelems);
+    return true;
+}
+
+bool
+SCOutput::writeBytes(const void *p, size_t nbytes)
+{
+    return writeArray((const uint8_t *) p, nbytes);
+}
+
+bool
+SCOutput::writeChars(const jschar *p, size_t nchars)
+{
+    JS_ASSERT(sizeof(jschar) == sizeof(uint16_t));
+    return writeArray((const uint16_t *) p, nchars);
+}
+
+bool
+SCOutput::writePtr(const void *p)
+{
+    return write(reinterpret_cast<uint64_t>(p));
+}
+
+bool
+SCOutput::extractBuffer(uint64_t **datap, size_t *sizep)
+{
+    *sizep = buf.length() * sizeof(uint64_t);
+    return (*datap = buf.extractRawBuffer()) != NULL;
+}
+
+JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
+
+bool
+JSStructuredCloneWriter::parseTransferable()
+{
+    transferableObjects.clear();
+
+    if (JSVAL_IS_NULL(transferable) || JSVAL_IS_VOID(transferable))
+        return true;
+
+    if (!transferable.isObject()) {
+        reportErrorTransferable();
+        return false;
+    }
+
+    RootedObject array(context(), &transferable.toObject());
+    if (!JS_IsArrayObject(context(), array)) {
+        reportErrorTransferable();
+        return false;
+    }
+
+    uint32_t length;
+    if (!JS_GetArrayLength(context(), array, &length)) {
+        return false;
+    }
+
+    RootedValue v(context());
+
+    for (uint32_t i = 0; i < length; ++i) {
+        if (!JS_GetElement(context(), array, i, &v)) {
+            return false;
+        }
+
+        if (!v.isObject()) {
+            reportErrorTransferable();
+            return false;
+        }
+
+        JSObject* tObj = CheckedUnwrap(&v.toObject());
+        if (!tObj) {
+            JS_ReportError(context(), "Permission denied to access object");
+            return false;
+        }
+        if (!tObj->is<ArrayBufferObject>()) {
+            reportErrorTransferable();
+            return false;
+        }
+
+        // No duplicate:
+        if (transferableObjects.has(tObj)) {
+            reportErrorTransferable();
+            return false;
+        }
+
+        if (!transferableObjects.putNew(tObj))
+            return false;
+    }
+
+    return true;
+}
+
+void
+JSStructuredCloneWriter::reportErrorTransferable()
+{
+    if (callbacks && callbacks->reportError)
+        return callbacks->reportError(context(), JS_SCERR_TRANSFERABLE);
+}
+
+bool
+JSStructuredCloneWriter::writeString(uint32_t tag, JSString *str)
+{
+    size_t length = str->length();
+    const jschar *chars = str->getChars(context());
+    if (!chars)
+        return false;
+    return out.writePair(tag, uint32_t(length)) && out.writeChars(chars, length);
+}
+
+bool
+JSStructuredCloneWriter::writeId(jsid id)
+{
+    if (JSID_IS_INT(id))
+        return out.writePair(SCTAG_INDEX, uint32_t(JSID_TO_INT(id)));
+    JS_ASSERT(JSID_IS_STRING(id));
+    return writeString(SCTAG_STRING, JSID_TO_STRING(id));
+}
+
+inline void
+JSStructuredCloneWriter::checkStack()
+{
+#ifdef DEBUG
+    /* To avoid making serialization O(n^2), limit stack-checking at 10. */
+    const size_t MAX = 10;
+
+    size_t limit = Min(counts.length(), MAX);
+    JS_ASSERT(objs.length() == counts.length());
+    size_t total = 0;
+    for (size_t i = 0; i < limit; i++) {
+        JS_ASSERT(total + counts[i] >= total);
+        total += counts[i];
+    }
+    if (counts.length() <= MAX)
+        JS_ASSERT(total == ids.length());
+    else
+        JS_ASSERT(total <= ids.length());
+
+    size_t j = objs.length();
+    for (size_t i = 0; i < limit; i++)
+        JS_ASSERT(memory.has(&objs[--j].toObject()));
+#endif
+}
+
+JS_PUBLIC_API(bool)
+JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v)
+{
+    JS_ASSERT(v.isObject());
+    assertSameCompartment(w->context(), v);
+    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->is<WrapperObject>())
+        obj = CheckedUnwrap(obj);
+    if (!obj) {
+        JS_ReportError(w->context(), "Permission denied to access object");
+        return false;
+    }
+    return w->writeTypedArray(obj);
+}
+
+/*
+ * Write out a typed array. Note that post-v1 structured clone buffers do not
+ * perform endianness conversion on stored data, so multibyte typed arrays
+ * cannot be deserialized into a different endianness machine. Endianness
+ * conversion would prevent sharing ArrayBuffers: if you have Int8Array and
+ * Int16Array views of the same ArrayBuffer, should the data bytes be
+ * byte-swapped when writing or not? The Int8Array requires them to not be
+ * swapped; the Int16Array requires that they are.
+ */
+bool
+JSStructuredCloneWriter::writeTypedArray(HandleObject obj)
+{
+    Rooted<TypedArrayObject*> tarr(context(), &obj->as<TypedArrayObject>());
+    if (!out.writePair(SCTAG_TYPED_ARRAY_OBJECT, tarr->length()))
+        return false;
+    uint64_t type = tarr->type();
+    if (!out.write(type))
+        return false;
+
+    // Write out the ArrayBuffer tag and contents
+    if (!startWrite(TypedArrayObject::bufferValue(tarr)))
+        return false;
+
+    return out.write(tarr->byteOffset());
+}
+
+bool
+JSStructuredCloneWriter::writeArrayBuffer(HandleObject obj)
+{
+    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
+    return out.writePair(SCTAG_ARRAY_BUFFER_OBJECT, buffer.byteLength()) &&
+           out.writeBytes(buffer.dataPointer(), buffer.byteLength());
+}
+
+bool
+JSStructuredCloneWriter::startObject(HandleObject obj, bool *backref)
+{
+    /* Handle cycles in the object graph. */
+    CloneMemory::AddPtr p = memory.lookupForAdd(obj);
+    if ((*backref = p))
+        return out.writePair(SCTAG_BACK_REFERENCE_OBJECT, p->value);
+    if (!memory.add(p, obj, memory.count()))
+        return false;
+
+    if (memory.count() == UINT32_MAX) {
+        JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
+                             JSMSG_NEED_DIET, "object graph to serialize");
+        return false;
+    }
+
+    return true;
+}
+
+bool
+JSStructuredCloneWriter::traverseObject(HandleObject obj)
+{
+    /*
+     * Get enumerable property ids and put them in reverse order so that they
+     * will come off the stack in forward order.
+     */
+    size_t initialLength = ids.length();
+    if (!GetPropertyNames(context(), obj, JSITER_OWNONLY, &ids))
+        return false;
+    jsid *begin = ids.begin() + initialLength, *end = ids.end();
+    size_t count = size_t(end - begin);
+    Reverse(begin, end);
+
+    /* Push obj and count to the stack. */
+    if (!objs.append(ObjectValue(*obj)) || !counts.append(count))
+        return false;
+    checkStack();
+
+    /* Write the header for obj. */
+    return out.writePair(obj->is<ArrayObject>() ? SCTAG_ARRAY_OBJECT : SCTAG_OBJECT_OBJECT, 0);
+}
+
+bool
+JSStructuredCloneWriter::startWrite(const Value &v)
+{
+    assertSameCompartment(context(), v);
+
+    if (v.isString()) {
+        return writeString(SCTAG_STRING, v.toString());
+    } else if (v.isNumber()) {
+        return out.writeDouble(v.toNumber());
+    } else if (v.isBoolean()) {
+        return out.writePair(SCTAG_BOOLEAN, v.toBoolean());
+    } else if (v.isNull()) {
+        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 = CheckedUnwrap(obj);
+        if (!obj) {
+            JS_ReportError(context(), "Permission denied to access object");
+            return false;
+        }
+
+        AutoCompartment ac(context(), obj);
+
+        bool backref;
+        if (!startObject(obj, &backref))
+            return false;
+        if (backref)
+            return true;
+
+        if (obj->is<RegExpObject>()) {
+            RegExpObject &reobj = obj->as<RegExpObject>();
+            return out.writePair(SCTAG_REGEXP_OBJECT, reobj.getFlags()) &&
+                   writeString(SCTAG_STRING, reobj.getSource());
+        } else if (obj->is<DateObject>()) {
+            double d = js_DateGetMsecSinceEpoch(obj);
+            return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(d);
+        } else if (obj->is<TypedArrayObject>()) {
+            return writeTypedArray(obj);
+        } else if (obj->is<ArrayBufferObject>() && obj->as<ArrayBufferObject>().hasData()) {
+            return writeArrayBuffer(obj);
+        } else if (obj->is<JSObject>() || obj->is<ArrayObject>()) {
+            return traverseObject(obj);
+        } else if (obj->is<BooleanObject>()) {
+            return out.writePair(SCTAG_BOOLEAN_OBJECT, obj->as<BooleanObject>().unbox());
+        } else if (obj->is<NumberObject>()) {
+            return out.writePair(SCTAG_NUMBER_OBJECT, 0) &&
+                   out.writeDouble(obj->as<NumberObject>().unbox());
+        } else if (obj->is<StringObject>()) {
+            return writeString(SCTAG_STRING_OBJECT, obj->as<StringObject>().unbox());
+        }
+
+        if (callbacks && callbacks->write)
+            return callbacks->write(context(), this, obj, closure);
+        /* else fall through */
+    }
+
+    JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_UNSUPPORTED_TYPE);
+    return false;
+}
+
+bool
+JSStructuredCloneWriter::writeTransferMap()
+{
+    if (!transferableObjects.empty()) {
+        if (!out.writePair(SCTAG_TRANSFER_MAP_HEADER, (uint32_t)SCTAG_TM_NOT_MARKED))
+            return false;
+
+        for (HashSet<JSObject*>::Range r = transferableObjects.all();
+             !r.empty(); r.popFront()) {
+            JSObject *obj = r.front();
+
+            if (!memory.put(obj, memory.count()))
+                return false;
+
+            void *content;
+            uint8_t *data;
+            if (!JS_StealArrayBufferContents(context(), obj, &content, &data))
+               return false;
+
+            if (!out.writePair(SCTAG_TRANSFER_MAP, 0) || !out.writePtr(content))
+                return false;
+        }
+    }
+
+    return true;
+}
+
+bool
+JSStructuredCloneWriter::write(const Value &v)
+{
+    if (!startWrite(v))
+        return false;
+
+    while (!counts.empty()) {
+        RootedObject obj(context(), &objs.back().toObject());
+        AutoCompartment ac(context(), obj);
+        if (counts.back()) {
+            counts.back()--;
+            RootedId id(context(), ids.back());
+            ids.popBack();
+            checkStack();
+            if (JSID_IS_STRING(id) || JSID_IS_INT(id)) {
+                /*
+                 * If obj still has an own property named id, write it out.
+                 * The cost of re-checking could be avoided by using
+                 * NativeIterators.
+                 */
+                RootedObject obj2(context());
+                RootedShape prop(context());
+                if (!HasOwnProperty<CanGC>(context(), obj->getOps()->lookupGeneric, obj, id,
+                                           &obj2, &prop)) {
+                    return false;
+                }
+
+                if (prop) {
+                    RootedValue val(context());
+                    if (!writeId(id) ||
+                        !JSObject::getGeneric(context(), obj, obj, id, &val) ||
+                        !startWrite(val))
+                        return false;
+                }
+            }
+        } else {
+            out.writePair(SCTAG_NULL, 0);
+            objs.popBack();
+            counts.popBack();
+        }
+    }
+
+    memory.clear();
+
+    return true;
+}
+
+bool
+JSStructuredCloneReader::checkDouble(double d)
+{
+    jsval_layout l;
+    l.asDouble = d;
+    if (!JSVAL_IS_DOUBLE_IMPL(l)) {
+        JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
+                             JSMSG_SC_BAD_SERIALIZED_DATA, "unrecognized NaN");
+        return false;
+    }
+    return true;
+}
+
+class Chars {
+    JSContext *cx;
+    jschar *p;
+  public:
+    Chars(JSContext *cx) : cx(cx), p(NULL) {}
+    ~Chars() { if (p) js_free(p); }
+
+    bool allocate(size_t len) {
+        JS_ASSERT(!p);
+        // We're going to null-terminate!
+        p = cx->pod_malloc<jschar>(len + 1);
+        if (p) {
+            p[len] = jschar(0);
+            return true;
+        }
+        return false;
+    }
+    jschar *get() { return p; }
+    void forget() { p = NULL; }
+};
+
+JSString *
+JSStructuredCloneReader::readString(uint32_t nchars)
+{
+    if (nchars > JSString::MAX_LENGTH) {
+        JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
+                             "string length");
+        return NULL;
+    }
+    Chars chars(context());
+    if (!chars.allocate(nchars) || !in.readChars(chars.get(), nchars))
+        return NULL;
+    JSString *str = js_NewString<CanGC>(context(), chars.get(), nchars);
+    if (str)
+        chars.forget();
+    return str;
+}
+
+static uint32_t
+TagToV1ArrayType(uint32_t tag)
+{
+    JS_ASSERT(tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX);
+    return tag - SCTAG_TYPED_ARRAY_V1_MIN;
+}
+
+JS_PUBLIC_API(bool)
+JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp)
+{
+    uint32_t tag, nelems;
+    if (!r->input().readPair(&tag, &nelems))
+        return false;
+    if (tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX) {
+        return r->readTypedArray(TagToV1ArrayType(tag), nelems, vp, true);
+    } else if (tag == SCTAG_TYPED_ARRAY_OBJECT) {
+        uint64_t arrayType;
+        if (!r->input().read(&arrayType))
+            return false;
+        return r->readTypedArray(arrayType, nelems, vp);
+    } else {
+        JS_ReportErrorNumber(r->context(), js_GetErrorMessage, NULL,
+                             JSMSG_SC_BAD_SERIALIZED_DATA, "expected type array");
+        return false;
+    }
+}
+
+bool
+JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, Value *vp,
+                                        bool v1Read)
+{
+    if (arrayType > TypedArrayObject::TYPE_UINT8_CLAMPED) {
+        JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
+                             JSMSG_SC_BAD_SERIALIZED_DATA, "unhandled typed array element type");
+        return false;
+    }
+
+    // Push a placeholder onto the allObjs list to stand in for the typed array
+    uint32_t placeholderIndex = allObjs.length();
+    Value dummy = JSVAL_NULL;
+    if (!allObjs.append(dummy))
+        return false;
+
+    // Read the ArrayBuffer object and its contents (but no properties)
+    RootedValue v(context());
+    uint32_t byteOffset;
+    if (v1Read) {
+        if (!readV1ArrayBuffer(arrayType, nelems, v.address()))
+            return false;
+        byteOffset = 0;
+    } else {
+        if (!startRead(v.address()))
+            return false;
+        uint64_t n;
+        if (!in.read(&n))
+            return false;
+        byteOffset = n;
+    }
+    RootedObject buffer(context(), &v.toObject());
+    RootedObject obj(context(), NULL);
+
+    switch (arrayType) {
+      case TypedArrayObject::TYPE_INT8:
+        obj = JS_NewInt8ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case TypedArrayObject::TYPE_UINT8:
+        obj = JS_NewUint8ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case TypedArrayObject::TYPE_INT16:
+        obj = JS_NewInt16ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case TypedArrayObject::TYPE_UINT16:
+        obj = JS_NewUint16ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case TypedArrayObject::TYPE_INT32:
+        obj = JS_NewInt32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case TypedArrayObject::TYPE_UINT32:
+        obj = JS_NewUint32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case TypedArrayObject::TYPE_FLOAT32:
+        obj = JS_NewFloat32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case TypedArrayObject::TYPE_FLOAT64:
+        obj = JS_NewFloat64ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      case TypedArrayObject::TYPE_UINT8_CLAMPED:
+        obj = JS_NewUint8ClampedArrayWithBuffer(context(), buffer, byteOffset, nelems);
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("unknown TypedArrayObject type");
+    }
+
+    if (!obj)
+        return false;
+    vp->setObject(*obj);
+
+    allObjs[placeholderIndex] = *vp;
+
+    return true;
+}
+
+bool
+JSStructuredCloneReader::readArrayBuffer(uint32_t nbytes, Value *vp)
+{
+    JSObject *obj = ArrayBufferObject::create(context(), nbytes);
+    if (!obj)
+        return false;
+    vp->setObject(*obj);
+    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
+    JS_ASSERT(buffer.byteLength() == nbytes);
+    return in.readArray(buffer.dataPointer(), nbytes);
+}
+
+static size_t
+bytesPerTypedArrayElement(uint32_t arrayType)
+{
+    switch (arrayType) {
+      case TypedArrayObject::TYPE_INT8:
+      case TypedArrayObject::TYPE_UINT8:
+      case TypedArrayObject::TYPE_UINT8_CLAMPED:
+        return sizeof(uint8_t);
+      case TypedArrayObject::TYPE_INT16:
+      case TypedArrayObject::TYPE_UINT16:
+        return sizeof(uint16_t);
+      case TypedArrayObject::TYPE_INT32:
+      case TypedArrayObject::TYPE_UINT32:
+      case TypedArrayObject::TYPE_FLOAT32:
+        return sizeof(uint32_t);
+      case TypedArrayObject::TYPE_FLOAT64:
+        return sizeof(uint64_t);
+      default:
+        MOZ_ASSUME_UNREACHABLE("unknown TypedArrayObject type");
+    }
+}
+
+/*
+ * Read in the data for a structured clone version 1 ArrayBuffer, performing
+ * endianness-conversion while reading.
+ */
+bool
+JSStructuredCloneReader::readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, Value *vp)
+{
+    JS_ASSERT(arrayType <= TypedArrayObject::TYPE_UINT8_CLAMPED);
+
+    uint32_t nbytes = nelems * bytesPerTypedArrayElement(arrayType);
+    JSObject *obj = ArrayBufferObject::create(context(), nbytes);
+    if (!obj)
+        return false;
+    vp->setObject(*obj);
+    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
+    JS_ASSERT(buffer.byteLength() == nbytes);
+
+    switch (arrayType) {
+      case TypedArrayObject::TYPE_INT8:
+      case TypedArrayObject::TYPE_UINT8:
+      case TypedArrayObject::TYPE_UINT8_CLAMPED:
+        return in.readArray((uint8_t*) buffer.dataPointer(), nelems);
+      case TypedArrayObject::TYPE_INT16:
+      case TypedArrayObject::TYPE_UINT16:
+        return in.readArray((uint16_t*) buffer.dataPointer(), nelems);
+      case TypedArrayObject::TYPE_INT32:
+      case TypedArrayObject::TYPE_UINT32:
+      case TypedArrayObject::TYPE_FLOAT32:
+        return in.readArray((uint32_t*) buffer.dataPointer(), nelems);
+      case TypedArrayObject::TYPE_FLOAT64:
+        return in.readArray((uint64_t*) buffer.dataPointer(), nelems);
+      default:
+        MOZ_ASSUME_UNREACHABLE("unknown TypedArrayObject type");
+    }
+}
+
+bool
+JSStructuredCloneReader::startRead(Value *vp)
+{
+    uint32_t tag, data;
+
+    if (!in.readPair(&tag, &data))
+        return false;
+    switch (tag) {
+      case SCTAG_NULL:
+        vp->setNull();
+        break;
+
+      case SCTAG_UNDEFINED:
+        vp->setUndefined();
+        break;
+
+      case SCTAG_BOOLEAN:
+      case SCTAG_BOOLEAN_OBJECT:
+        vp->setBoolean(!!data);
+        if (tag == SCTAG_BOOLEAN_OBJECT && !js_PrimitiveToObject(context(), vp))
+            return false;
+        break;
+
+      case SCTAG_STRING:
+      case SCTAG_STRING_OBJECT: {
+        JSString *str = readString(data);
+        if (!str)
+            return false;
+        vp->setString(str);
+        if (tag == SCTAG_STRING_OBJECT && !js_PrimitiveToObject(context(), vp))
+            return false;
+        break;
+      }
+
+      case SCTAG_NUMBER_OBJECT: {
+        double d;
+        if (!in.readDouble(&d) || !checkDouble(d))
+            return false;
+        vp->setDouble(d);
+        if (!js_PrimitiveToObject(context(), vp))
+            return false;
+        break;
+      }
+
+      case SCTAG_DATE_OBJECT: {
+        double d;
+        if (!in.readDouble(&d) || !checkDouble(d))
+            return false;
+        if (!IsNaN(d) && d != TimeClip(d)) {
+            JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
+                                 "date");
+            return false;
+        }
+        JSObject *obj = js_NewDateObjectMsec(context(), d);
+        if (!obj)
+            return false;
+        vp->setObject(*obj);
+        break;
+      }
+
+      case SCTAG_REGEXP_OBJECT: {
+        RegExpFlag flags = RegExpFlag(data);
+        uint32_t tag2, nchars;
+        if (!in.readPair(&tag2, &nchars))
+            return false;
+        if (tag2 != SCTAG_STRING) {
+            JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
+                                 "regexp");
+            return false;
+        }
+        JSString *str = readString(nchars);
+        if (!str)
+            return false;
+        JSStableString *stable = str->ensureStable(context());
+        if (!stable)
+            return false;
+
+        size_t length = stable->length();
+        const StableCharPtr chars = stable->chars();
+        RegExpObject *reobj = RegExpObject::createNoStatics(context(), chars.get(), length, flags, NULL);
+        if (!reobj)
+            return false;
+        vp->setObject(*reobj);
+        break;
+      }
+
+      case SCTAG_ARRAY_OBJECT:
+      case SCTAG_OBJECT_OBJECT: {
+        JSObject *obj = (tag == SCTAG_ARRAY_OBJECT)
+                        ? NewDenseEmptyArray(context())
+                        : NewBuiltinClassInstance(context(), &JSObject::class_);
+        if (!obj || !objs.append(ObjectValue(*obj)))
+            return false;
+        vp->setObject(*obj);
+        break;
+      }
+
+      case SCTAG_BACK_REFERENCE_OBJECT: {
+        if (data >= allObjs.length()) {
+            JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
+                                 JSMSG_SC_BAD_SERIALIZED_DATA,
+                                 "invalid back reference in input");
+            return false;
+        }
+        *vp = allObjs[data];
+        return true;
+      }
+
+      case SCTAG_TRANSFER_MAP_HEADER:
+        // A map header cannot be here but just at the beginning of the buffer.
+        JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
+                             JSMSG_SC_BAD_SERIALIZED_DATA,
+                             "invalid input");
+        return false;
+
+      case SCTAG_TRANSFER_MAP:
+        // A map cannot be here but just at the beginning of the buffer.
+        JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
+                             JSMSG_SC_BAD_SERIALIZED_DATA,
+                             "invalid input");
+        return false;
+
+      case SCTAG_ARRAY_BUFFER_OBJECT:
+        if (!readArrayBuffer(data, vp))
+            return false;
+        break;
+
+      case SCTAG_TYPED_ARRAY_OBJECT:
+        // readTypedArray adds the array to allObjs
+        uint64_t arrayType;
+        if (!in.read(&arrayType))
+            return false;
+        return readTypedArray(arrayType, data, vp);
+        break;
+
+      default: {
+        if (tag <= SCTAG_FLOAT_MAX) {
+            double d = ReinterpretPairAsDouble(tag, data);
+            if (!checkDouble(d))
+                return false;
+            vp->setNumber(d);
+            break;
+        }
+
+        if (SCTAG_TYPED_ARRAY_V1_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_V1_MAX) {
+            // A v1-format typed array
+            // readTypedArray adds the array to allObjs
+            return readTypedArray(TagToV1ArrayType(tag), data, vp, true);
+        }
+
+        if (!callbacks || !callbacks->read) {
+            JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
+                                 "unsupported type");
+            return false;
+        }
+        JSObject *obj = callbacks->read(context(), this, tag, data, closure);
+        if (!obj)
+            return false;
+        vp->setObject(*obj);
+      }
+    }
+
+    if (vp->isObject() && !allObjs.append(*vp))
+        return false;
+
+    return true;
+}
+
+bool
+JSStructuredCloneReader::readId(jsid *idp)
+{
+    uint32_t tag, data;
+    if (!in.readPair(&tag, &data))
+        return false;
+
+    if (tag == SCTAG_INDEX) {
+        *idp = INT_TO_JSID(int32_t(data));
+        return true;
+    }
+    if (tag == SCTAG_STRING) {
+        JSString *str = readString(data);
+        if (!str)
+            return false;
+        JSAtom *atom = AtomizeString<CanGC>(context(), str);
+        if (!atom)
+            return false;
+        *idp = NON_INTEGER_ATOM_TO_JSID(atom);
+        return true;
+    }
+    if (tag == SCTAG_NULL) {
+        *idp = JSID_VOID;
+        return true;
+    }
+    JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "id");
+    return false;
+}
+
+bool
+JSStructuredCloneReader::readTransferMap()
+{
+    uint32_t tag, data;
+    if (!in.getPair(&tag, &data))
+        return false;
+
+    if (tag != SCTAG_TRANSFER_MAP_HEADER ||
+        (TransferableMapHeader)data == SCTAG_TM_MARKED)
+        return true;
+
+    if (!in.replacePair(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_MARKED))
+        return false;
+
+    if (!in.readPair(&tag, &data))
+        return false;
+
+    while (1) {
+        if (!in.getPair(&tag, &data))
+            return false;
+
+        if (tag != SCTAG_TRANSFER_MAP)
+            break;
+
+        void *content;
+
+        if (!in.readPair(&tag, &data) || !in.readPtr(&content))
+            return false;
+
+        JSObject *obj = JS_NewArrayBufferWithContents(context(), content);
+        if (!obj || !allObjs.append(ObjectValue(*obj)))
+            return false;
+    }
+
+    return true;
+}
+
+bool
+JSStructuredCloneReader::read(Value *vp)
+{
+    if (!readTransferMap())
+        return false;
+
+    if (!startRead(vp))
+        return false;
+
+    while (objs.length() != 0) {
+        RootedObject obj(context(), &objs.back().toObject());
+
+        RootedId id(context());
+        if (!readId(id.address()))
+            return false;
+
+        if (JSID_IS_VOID(id)) {
+            objs.popBack();
+        } else {
+            RootedValue v(context());
+            if (!startRead(v.address()) || !JSObject::defineGeneric(context(), obj, id, v))
+                return false;
+        }
+    }
+
+    allObjs.clear();
+
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jsclone.h
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jsclone_h
+#define jsclone_h
+
+#include "jsapi.h"
+#include "jscntxt.h"
+
+#include "js/Vector.h"
+
+namespace js {
+
+bool
+WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp,
+                     const JSStructuredCloneCallbacks *cb, void *cbClosure,
+                     jsval transferable);
+
+bool
+ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp,
+                    const JSStructuredCloneCallbacks *cb, void *cbClosure);
+
+bool
+ClearStructuredClone(const uint64_t *data, size_t nbytes);
+
+bool
+StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes,
+                                  bool *hasTransferable);
+
+struct SCOutput {
+  public:
+    explicit SCOutput(JSContext *cx);
+
+    JSContext *context() const { return cx; }
+
+    bool write(uint64_t u);
+    bool writePair(uint32_t tag, uint32_t data);
+    bool writeDouble(double d);
+    bool writeBytes(const void *p, size_t nbytes);
+    bool writeChars(const jschar *p, size_t nchars);
+    bool writePtr(const void *);
+
+    template <class T>
+    bool writeArray(const T *p, size_t nbytes);
+
+    bool extractBuffer(uint64_t **datap, size_t *sizep);
+
+    uint64_t count() { return buf.length(); }
+
+  private:
+    JSContext *cx;
+    js::Vector<uint64_t> buf;
+};
+
+struct SCInput {
+  public:
+    SCInput(JSContext *cx, uint64_t *data, size_t nbytes);
+
+    JSContext *context() const { return cx; }
+
+    bool read(uint64_t *p);
+    bool readPair(uint32_t *tagp, uint32_t *datap);
+    bool readDouble(double *p);
+    bool readBytes(void *p, size_t nbytes);
+    bool readChars(jschar *p, size_t nchars);
+    bool readPtr(void **);
+
+    bool get(uint64_t *p);
+    bool getPair(uint32_t *tagp, uint32_t *datap);
+
+    bool replace(uint64_t u);
+    bool replacePair(uint32_t tag, uint32_t data);
+
+    template <class T>
+    bool readArray(T *p, size_t nelems);
+
+  private:
+    bool eof();
+
+    void staticAssertions() {
+        JS_STATIC_ASSERT(sizeof(jschar) == 2);
+        JS_STATIC_ASSERT(sizeof(uint32_t) == 4);
+        JS_STATIC_ASSERT(sizeof(double) == 8);
+    }
+
+    JSContext *cx;
+    uint64_t *point;
+    uint64_t *end;
+};
+
+} /* namespace js */
+
+struct JSStructuredCloneReader {
+  public:
+    explicit JSStructuredCloneReader(js::SCInput &in, const JSStructuredCloneCallbacks *cb,
+                                     void *cbClosure)
+        : in(in), objs(in.context()), allObjs(in.context()),
+          callbacks(cb), closure(cbClosure) { }
+
+    js::SCInput &input() { return in; }
+    bool read(js::Value *vp);
+
+  private:
+    JSContext *context() { return in.context(); }
+
+    bool readTransferMap();
+
+    bool checkDouble(double d);
+    JSString *readString(uint32_t nchars);
+    bool readTypedArray(uint32_t arrayType, uint32_t nelems, js::Value *vp, bool v1Read = false);
+    bool readArrayBuffer(uint32_t nbytes, js::Value *vp);
+    bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, js::Value *vp);
+    bool readId(jsid *idp);
+    bool startRead(js::Value *vp);
+
+    js::SCInput &in;
+
+    // Stack of objects with properties remaining to be read.
+    js::AutoValueVector objs;
+
+    // Stack of all objects read during this deserialization
+    js::AutoValueVector allObjs;
+
+    // The user defined callbacks that will be used for cloning.
+    const JSStructuredCloneCallbacks *callbacks;
+
+    // Any value passed to JS_ReadStructuredClone.
+    void *closure;
+
+    friend bool JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp);
+};
+
+struct JSStructuredCloneWriter {
+  public:
+    explicit JSStructuredCloneWriter(js::SCOutput &out,
+                                     const JSStructuredCloneCallbacks *cb,
+                                     void *cbClosure,
+                                     jsval tVal)
+        : out(out), objs(out.context()),
+          counts(out.context()), ids(out.context()),
+          memory(out.context()), callbacks(cb), closure(cbClosure),
+          transferable(out.context(), tVal), transferableObjects(out.context()) { }
+
+    bool init() { return transferableObjects.init() && parseTransferable() &&
+                         memory.init() && writeTransferMap(); }
+
+    bool write(const js::Value &v);
+
+    js::SCOutput &output() { return out; }
+
+  private:
+    JSContext *context() { return out.context(); }
+
+    bool writeTransferMap();
+
+    bool writeString(uint32_t tag, JSString *str);
+    bool writeId(jsid id);
+    bool writeArrayBuffer(JS::HandleObject obj);
+    bool writeTypedArray(JS::HandleObject obj);
+    bool startObject(JS::HandleObject obj, bool *backref);
+    bool startWrite(const js::Value &v);
+    bool traverseObject(JS::HandleObject obj);
+
+    bool parseTransferable();
+    void reportErrorTransferable();
+
+    inline void checkStack();
+
+    js::SCOutput &out;
+
+    // Vector of objects with properties remaining to be written.
+    //
+    // NB: These can span multiple compartments, so the compartment must be
+    // entered before any manipulation is performed.
+    js::AutoValueVector objs;
+
+    // counts[i] is the number of properties of objs[i] remaining to be written.
+    // counts.length() == objs.length() and sum(counts) == ids.length().
+    js::Vector<size_t> counts;
+
+    // Ids of properties remaining to be written.
+    js::AutoIdVector ids;
+
+    // The "memory" list described in the HTML5 internal structured cloning algorithm.
+    // memory is a superset of objs; items are never removed from Memory
+    // until a serialization operation is finished
+    typedef js::AutoObjectUnsigned32HashMap CloneMemory;
+    CloneMemory memory;
+
+    // The user defined callbacks that will be used for cloning.
+    const JSStructuredCloneCallbacks *callbacks;
+
+    // Any value passed to JS_WriteStructuredClone.
+    void *closure;
+
+    // List of transferable objects
+    JS::RootedValue transferable;
+    js::AutoObjectHashSet transferableObjects;
+
+    friend bool JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v);
+};
+
+#endif /* jsclone_h */
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -987,16 +987,34 @@ js::GetTestingFunctions(JSContext *cx)
 JS_FRIEND_API(unsigned)
 js::GetEnterCompartmentDepth(JSContext *cx)
 {
   return cx->getEnterCompartmentDepth();
 }
 #endif
 
 JS_FRIEND_API(void)
+js::SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, uint32_t max)
+{
+    rt->spsProfiler.setProfilingStack(stack, size, max);
+}
+
+JS_FRIEND_API(void)
+js::EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled)
+{
+    rt->spsProfiler.enable(enabled);
+}
+
+JS_FRIEND_API(jsbytecode*)
+js::ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip)
+{
+    return rt->spsProfiler.ipToPC(script, size_t(ip));
+}
+
+JS_FRIEND_API(void)
 js::SetDOMCallbacks(JSRuntime *rt, const DOMCallbacks *callbacks)
 {
     rt->DOMcallbacks = callbacks;
 }
 
 JS_FRIEND_API(const DOMCallbacks *)
 js::GetDOMCallbacks(JSRuntime *rt)
 {
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -664,16 +664,94 @@ JS_FRIEND_API(size_t)
 GetPCCountScriptCount(JSContext *cx);
 
 JS_FRIEND_API(JSString *)
 GetPCCountScriptSummary(JSContext *cx, size_t script);
 
 JS_FRIEND_API(JSString *)
 GetPCCountScriptContents(JSContext *cx, size_t script);
 
+/*
+ * A call stack can be specified to the JS engine such that all JS entry/exits
+ * to functions push/pop an entry to/from the specified stack.
+ *
+ * For more detailed information, see vm/SPSProfiler.h
+ */
+class ProfileEntry
+{
+    /*
+     * All fields are marked volatile to prevent the compiler from re-ordering
+     * instructions. Namely this sequence:
+     *
+     *    entry[size] = ...;
+     *    size++;
+     *
+     * If the size modification were somehow reordered before the stores, then
+     * if a sample were taken it would be examining bogus information.
+     *
+     * A ProfileEntry represents both a C++ profile entry and a JS one. Both use
+     * the string as a description, but JS uses the sp as NULL to indicate that
+     * it is a JS entry. The script_ is then only ever examined for a JS entry,
+     * and the idx is used by both, but with different meanings.
+     */
+    const char * volatile string; // Descriptive string of this entry
+    void * volatile sp;           // Relevant stack pointer for the entry
+    JSScript * volatile script_;  // if js(), non-null script which is running
+    int32_t volatile idx;         // if js(), idx of pc, otherwise line number
+
+  public:
+    /*
+     * All of these methods are marked with the 'volatile' keyword because SPS's
+     * representation of the stack is stored such that all ProfileEntry
+     * instances are volatile. These methods would not be available unless they
+     * were marked as volatile as well
+     */
+
+    bool js() volatile {
+        JS_ASSERT_IF(sp == NULL, script_ != NULL);
+        return sp == NULL;
+    }
+
+    uint32_t line() volatile { JS_ASSERT(!js()); return idx; }
+    JSScript *script() volatile { JS_ASSERT(js()); return script_; }
+    void *stackAddress() volatile { return sp; }
+    const char *label() volatile { return string; }
+
+    void setLine(uint32_t aLine) volatile { JS_ASSERT(!js()); idx = aLine; }
+    void setLabel(const char *aString) volatile { string = aString; }
+    void setStackAddress(void *aSp) volatile { sp = aSp; }
+    void setScript(JSScript *aScript) volatile { script_ = aScript; }
+
+    /* we can't know the layout of JSScript, so look in vm/SPSProfiler.cpp */
+    JS_FRIEND_API(jsbytecode *) pc() volatile;
+    JS_FRIEND_API(void) setPC(jsbytecode *pc) volatile;
+
+    static size_t offsetOfString() { return offsetof(ProfileEntry, string); }
+    static size_t offsetOfStackAddress() { return offsetof(ProfileEntry, sp); }
+    static size_t offsetOfPCIdx() { return offsetof(ProfileEntry, idx); }
+    static size_t offsetOfScript() { return offsetof(ProfileEntry, script_); }
+
+    /*
+     * The index used in the entry can either be a line number or the offset of
+     * a pc into a script's code. To signify a NULL pc, use a -1 index. This is
+     * checked against in pc() and setPC() to set/get the right pc.
+     */
+    static const int32_t NullPCIndex = -1;
+};
+
+JS_FRIEND_API(void)
+SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size,
+                         uint32_t max);
+
+JS_FRIEND_API(void)
+EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled);
+
+JS_FRIEND_API(jsbytecode*)
+ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip);
+
 #ifdef JS_THREADSAFE
 JS_FRIEND_API(bool)
 ContextHasOutstandingRequests(const JSContext *cx);
 #endif
 
 JS_FRIEND_API(bool)
 HasUnrootedGlobal(const JSContext *cx);
 
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -63,21 +63,19 @@ EXPORTS.js += [
     '../public/CallArgs.h',
     '../public/CharacterEncoding.h',
     '../public/Date.h',
     '../public/GCAPI.h',
     '../public/HashTable.h',
     '../public/HeapAPI.h',
     '../public/LegacyIntTypes.h',
     '../public/MemoryMetrics.h',
-    '../public/ProfilingStack.h',
     '../public/PropertyKey.h',
     '../public/RequiredDefines.h',
     '../public/RootingAPI.h',
-    '../public/StructuredClone.h',
     '../public/Utility.h',
     '../public/Value.h',
     '../public/Vector.h',
 ]
 
 CPP_SOURCES += [
     'ArgumentsObject.cpp',
     'BinaryData.cpp',
@@ -122,36 +120,35 @@ CPP_SOURCES += [
     'ScopeObject.cpp',
     'SelfHosting.cpp',
     'Shape.cpp',
     'Stack.cpp',
     'Statistics.cpp',
     'StoreBuffer.cpp',
     'String.cpp',
     'StringBuffer.cpp',
-    'StructuredClone.cpp',
     'TestingFunctions.cpp',
     'ThreadPool.cpp',
     'TokenStream.cpp',
     'TypedArrayObject.cpp',
     'Unicode.cpp',
-    'Value.cpp',
     'Verifier.cpp',
     'Xdr.cpp',
     'YarrCanonicalizeUCS2.cpp',
     'YarrInterpreter.cpp',
     'YarrPattern.cpp',
     'YarrSyntaxChecker.cpp',
     'Zone.cpp',
     'jsalloc.cpp',
     'jsanalyze.cpp',
     'jsapi.cpp',
     'jsarray.cpp',
     'jsatom.cpp',
     'jsbool.cpp',
+    'jsclone.cpp',
     'jscntxt.cpp',
     'jscompartment.cpp',
     'jscrashreport.cpp',
     'jsdate.cpp',
     'jsdbgapi.cpp',
     'jsdtoa.cpp',
     'jsexn.cpp',
     'jsfriendapi.cpp',
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -57,17 +57,16 @@
 #if JS_TRACE_LOGGING
 #include "TraceLogging.h"
 #endif
 
 #include "builtin/TestingFunctions.h"
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/Parser.h"
 #include "jit/Ion.h"
-#include "js/StructuredClone.h"
 #include "perf/jsperf.h"
 #include "shell/jsheaptools.h"
 #include "shell/jsoptparse.h"
 #include "vm/Shape.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jsfuninlines.h"
--- a/js/src/vm/CharacterEncoding.cpp
+++ b/js/src/vm/CharacterEncoding.cpp
@@ -1,19 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "js/CharacterEncoding.h"
-
 #include "jscntxt.h"
 #include "jsprf.h"
 
+#include "js/CharacterEncoding.h"
+
 using namespace JS;
 
 Latin1CharsZ
 JS::LossyTwoByteCharsToNewLatin1CharsZ(js::ThreadSafeContext *cx, TwoByteChars tbchars)
 {
     JS_ASSERT(cx);
     size_t len = tbchars.length();
     unsigned char *latin1 = cx->pod_malloc<unsigned char>(len + 1);
--- a/js/src/vm/SPSProfiler.cpp
+++ b/js/src/vm/SPSProfiler.cpp
@@ -260,38 +260,19 @@ SPSEntryMarker::~SPSEntryMarker()
 {
     if (profiler != NULL) {
         profiler->pop();
         JS_ASSERT(size_before == *profiler->size_);
     }
 }
 
 JS_FRIEND_API(jsbytecode*)
-ProfileEntry::pc() volatile
-{
+ProfileEntry::pc() volatile {
     JS_ASSERT_IF(idx != NullPCIndex, idx >= 0 && uint32_t(idx) < script()->length);
     return idx == NullPCIndex ? NULL : script()->code + idx;
 }
 
 JS_FRIEND_API(void)
-ProfileEntry::setPC(jsbytecode *pc) volatile
-{
-    JS_ASSERT_IF(pc != NULL, script()->code <= pc && pc < script()->code + script()->length);
+ProfileEntry::setPC(jsbytecode *pc) volatile {
+    JS_ASSERT_IF(pc != NULL, script()->code <= pc &&
+                             pc < script()->code + script()->length);
     idx = pc == NULL ? NullPCIndex : pc - script()->code;
 }
-
-JS_FRIEND_API(void)
-js::SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, uint32_t max)
-{
-    rt->spsProfiler.setProfilingStack(stack, size, max);
-}
-
-JS_FRIEND_API(void)
-js::EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled)
-{
-    rt->spsProfiler.enable(enabled);
-}
-
-JS_FRIEND_API(jsbytecode*)
-js::ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip)
-{
-    return rt->spsProfiler.ipToPC(script, size_t(ip));
-}
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -9,18 +9,16 @@
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/GuardObjects.h"
 
 #include <stddef.h>
 
 #include "jsscript.h"
 
-#include "js/ProfilingStack.h"
-
 /*
  * SPS Profiler integration with the JS Engine
  * https://developer.mozilla.org/en/Performance/Profiling_with_the_Built-in_Profiler
  *
  * The SPS profiler (found in tools/profiler) is an implementation of a profiler
  * which has the ability to walk the C++ stack as well as use instrumentation to
  * gather information. When dealing with JS, however, SPS needs integration
  * with the engine because otherwise it is very difficult to figure out what
deleted file mode 100644
--- a/js/src/vm/StructuredClone.cpp
+++ /dev/null
@@ -1,1679 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/*
- * This file implements the structured clone algorithm of
- * http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#safe-passing-of-structured-data
- *
- * The implementation differs slightly in that it uses an explicit stack, and
- * the "memory" maps source objects to sequential integer indexes rather than
- * directly pointing to destination objects. As a result, the order in which
- * things are added to the memory must exactly match the order in which they
- * are placed into 'allObjs', an analogous array of back-referenceable
- * destination objects constructed while reading.
- *
- * For the most part, this is easy: simply add objects to the memory when first
- * encountering them. But reading in a typed array requires an ArrayBuffer for
- * construction, so objects cannot just be added to 'allObjs' in the order they
- * are created. If they were, ArrayBuffers would come before typed arrays when
- * in fact the typed array was added to 'memory' first.
- *
- * So during writing, we add objects to the memory when first encountering
- * them. When reading a typed array, a placeholder is pushed onto allObjs until
- * the ArrayBuffer has been read, then it is updated with the actual typed
- * array object.
- */
-
-#include "js/StructuredClone.h"
-
-#include "mozilla/Endian.h"
-#include "mozilla/FloatingPoint.h"
-
-#include "jsapi.h"
-#include "jscntxt.h"
-#include "jsdate.h"
-#include "jswrapper.h"
-
-#include "vm/TypedArrayObject.h"
-#include "vm/WrapperObject.h"
-
-#include "jscntxtinlines.h"
-#include "jsobjinlines.h"
-
-using namespace js;
-
-using mozilla::IsNaN;
-using mozilla::LittleEndian;
-using mozilla::NativeEndian;
-
-enum StructuredDataType {
-    /* Structured data types provided by the engine */
-    SCTAG_FLOAT_MAX = 0xFFF00000,
-    SCTAG_NULL = 0xFFFF0000,
-    SCTAG_UNDEFINED,
-    SCTAG_BOOLEAN,
-    SCTAG_INDEX,
-    SCTAG_STRING,
-    SCTAG_DATE_OBJECT,
-    SCTAG_REGEXP_OBJECT,
-    SCTAG_ARRAY_OBJECT,
-    SCTAG_OBJECT_OBJECT,
-    SCTAG_ARRAY_BUFFER_OBJECT,
-    SCTAG_BOOLEAN_OBJECT,
-    SCTAG_STRING_OBJECT,
-    SCTAG_NUMBER_OBJECT,
-    SCTAG_BACK_REFERENCE_OBJECT,
-    SCTAG_TRANSFER_MAP_HEADER,
-    SCTAG_TRANSFER_MAP,
-    SCTAG_TYPED_ARRAY_OBJECT,
-    SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
-    SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_INT8,
-    SCTAG_TYPED_ARRAY_V1_UINT8 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT8,
-    SCTAG_TYPED_ARRAY_V1_INT16 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_INT16,
-    SCTAG_TYPED_ARRAY_V1_UINT16 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT16,
-    SCTAG_TYPED_ARRAY_V1_INT32 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_INT32,
-    SCTAG_TYPED_ARRAY_V1_UINT32 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT32,
-    SCTAG_TYPED_ARRAY_V1_FLOAT32 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_FLOAT32,
-    SCTAG_TYPED_ARRAY_V1_FLOAT64 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_FLOAT64,
-    SCTAG_TYPED_ARRAY_V1_UINT8_CLAMPED = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT8_CLAMPED,
-    SCTAG_TYPED_ARRAY_V1_MAX = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_MAX - 1,
-    SCTAG_END_OF_BUILTIN_TYPES
-};
-
-enum TransferableMapHeader {
-    SCTAG_TM_NOT_MARKED = 0,
-    SCTAG_TM_MARKED
-};
-
-namespace js {
-
-struct SCOutput {
-  public:
-    explicit SCOutput(JSContext *cx);
-
-    JSContext *context() const { return cx; }
-
-    bool write(uint64_t u);
-    bool writePair(uint32_t tag, uint32_t data);
-    bool writeDouble(double d);
-    bool writeBytes(const void *p, size_t nbytes);
-    bool writeChars(const jschar *p, size_t nchars);
-    bool writePtr(const void *);
-
-    template <class T>
-    bool writeArray(const T *p, size_t nbytes);
-
-    bool extractBuffer(uint64_t **datap, size_t *sizep);
-
-    uint64_t count() { return buf.length(); }
-
-  private:
-    JSContext *cx;
-    js::Vector<uint64_t> buf;
-};
-
-struct SCInput {
-  public:
-    SCInput(JSContext *cx, uint64_t *data, size_t nbytes);
-
-    JSContext *context() const { return cx; }
-
-    bool read(uint64_t *p);
-    bool readPair(uint32_t *tagp, uint32_t *datap);
-    bool readDouble(double *p);
-    bool readBytes(void *p, size_t nbytes);
-    bool readChars(jschar *p, size_t nchars);
-    bool readPtr(void **);
-
-    bool get(uint64_t *p);
-    bool getPair(uint32_t *tagp, uint32_t *datap);
-
-    bool replace(uint64_t u);
-    bool replacePair(uint32_t tag, uint32_t data);
-
-    template <class T>
-    bool readArray(T *p, size_t nelems);
-
-  private:
-    bool eof();
-
-    void staticAssertions() {
-        JS_STATIC_ASSERT(sizeof(jschar) == 2);
-        JS_STATIC_ASSERT(sizeof(uint32_t) == 4);
-        JS_STATIC_ASSERT(sizeof(double) == 8);
-    }
-
-    JSContext *cx;
-    uint64_t *point;
-    uint64_t *end;
-};
-
-}
-
-struct JSStructuredCloneReader {
-  public:
-    explicit JSStructuredCloneReader(js::SCInput &in, const JSStructuredCloneCallbacks *cb,
-                                     void *cbClosure)
-        : in(in), objs(in.context()), allObjs(in.context()),
-          callbacks(cb), closure(cbClosure) { }
-
-    js::SCInput &input() { return in; }
-    bool read(js::Value *vp);
-
-  private:
-    JSContext *context() { return in.context(); }
-
-    bool readTransferMap();
-
-    bool checkDouble(double d);
-    JSString *readString(uint32_t nchars);
-    bool readTypedArray(uint32_t arrayType, uint32_t nelems, js::Value *vp, bool v1Read = false);
-    bool readArrayBuffer(uint32_t nbytes, js::Value *vp);
-    bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, js::Value *vp);
-    bool readId(jsid *idp);
-    bool startRead(js::Value *vp);
-
-    js::SCInput &in;
-
-    // Stack of objects with properties remaining to be read.
-    js::AutoValueVector objs;
-
-    // Stack of all objects read during this deserialization
-    js::AutoValueVector allObjs;
-
-    // The user defined callbacks that will be used for cloning.
-    const JSStructuredCloneCallbacks *callbacks;
-
-    // Any value passed to JS_ReadStructuredClone.
-    void *closure;
-
-    friend bool JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp);
-};
-
-struct JSStructuredCloneWriter {
-  public:
-    explicit JSStructuredCloneWriter(js::SCOutput &out,
-                                     const JSStructuredCloneCallbacks *cb,
-                                     void *cbClosure,
-                                     jsval tVal)
-        : out(out), objs(out.context()),
-          counts(out.context()), ids(out.context()),
-          memory(out.context()), callbacks(cb), closure(cbClosure),
-          transferable(out.context(), tVal), transferableObjects(out.context()) { }
-
-    bool init() { return transferableObjects.init() && parseTransferable() &&
-                         memory.init() && writeTransferMap(); }
-
-    bool write(const js::Value &v);
-
-    js::SCOutput &output() { return out; }
-
-  private:
-    JSContext *context() { return out.context(); }
-
-    bool writeTransferMap();
-
-    bool writeString(uint32_t tag, JSString *str);
-    bool writeId(jsid id);
-    bool writeArrayBuffer(JS::HandleObject obj);
-    bool writeTypedArray(JS::HandleObject obj);
-    bool startObject(JS::HandleObject obj, bool *backref);
-    bool startWrite(const js::Value &v);
-    bool traverseObject(JS::HandleObject obj);
-
-    bool parseTransferable();
-    void reportErrorTransferable();
-
-    inline void checkStack();
-
-    js::SCOutput &out;
-
-    // Vector of objects with properties remaining to be written.
-    //
-    // NB: These can span multiple compartments, so the compartment must be
-    // entered before any manipulation is performed.
-    js::AutoValueVector objs;
-
-    // counts[i] is the number of properties of objs[i] remaining to be written.
-    // counts.length() == objs.length() and sum(counts) == ids.length().
-    js::Vector<size_t> counts;
-
-    // Ids of properties remaining to be written.
-    js::AutoIdVector ids;
-
-    // The "memory" list described in the HTML5 internal structured cloning algorithm.
-    // memory is a superset of objs; items are never removed from Memory
-    // until a serialization operation is finished
-    typedef js::AutoObjectUnsigned32HashMap CloneMemory;
-    CloneMemory memory;
-
-    // The user defined callbacks that will be used for cloning.
-    const JSStructuredCloneCallbacks *callbacks;
-
-    // Any value passed to JS_WriteStructuredClone.
-    void *closure;
-
-    // List of transferable objects
-    JS::RootedValue transferable;
-    js::AutoObjectHashSet transferableObjects;
-
-    friend bool JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v);
-};
-
-JS_FRIEND_API(uint64_t)
-js_GetSCOffset(JSStructuredCloneWriter* writer)
-{
-    JS_ASSERT(writer);
-    return writer->output().count() * sizeof(uint64_t);
-}
-
-JS_STATIC_ASSERT(SCTAG_END_OF_BUILTIN_TYPES <= JS_SCTAG_USER_MIN);
-JS_STATIC_ASSERT(JS_SCTAG_USER_MIN <= JS_SCTAG_USER_MAX);
-JS_STATIC_ASSERT(TypedArrayObject::TYPE_INT8 == 0);
-
-namespace js {
-
-bool
-WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp,
-                     const JSStructuredCloneCallbacks *cb, void *cbClosure,
-                     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,
-                    const JSStructuredCloneCallbacks *cb, void *cbClosure)
-{
-    SCInput in(cx, data, nbytes);
-
-    /* XXX disallow callers from using internal pointers to GC things. */
-    SkipRoot skip(cx, &in);
-
-    JSStructuredCloneReader r(in, cb, cbClosure);
-    return r.read(vp);
-}
-
-bool
-ClearStructuredClone(const uint64_t *data, size_t nbytes)
-{
-    const uint64_t *point = data;
-    const uint64_t *end = data + nbytes / 8;
-
-    uint64_t u = LittleEndian::readUint64(point++);
-    uint32_t tag = uint32_t(u >> 32);
-    if (tag == SCTAG_TRANSFER_MAP_HEADER) {
-        if ((TransferableMapHeader)uint32_t(u) == SCTAG_TM_NOT_MARKED) {
-            while (point != end) {
-                uint64_t u = LittleEndian::readUint64(point++);
-                uint32_t tag = uint32_t(u >> 32);
-                if (tag == SCTAG_TRANSFER_MAP) {
-                    u = LittleEndian::readUint64(point++);
-                    js_free(reinterpret_cast<void*>(u));
-                } else {
-                    // The only things in the transfer map should be
-                    // SCTAG_TRANSFER_MAP tags paired with pointers. If we find
-                    // any other tag, we've walked off the end of the transfer
-                    // map.
-                    break;
-                }
-            }
-        }
-    }
-
-    js_free((void *)data);
-    return true;
-}
-
-bool
-StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool *hasTransferable)
-{
-    *hasTransferable = false;
-
-    if (data) {
-        uint64_t u = LittleEndian::readUint64(data);
-        uint32_t tag = uint32_t(u >> 32);
-        if (tag == SCTAG_TRANSFER_MAP_HEADER) {
-            *hasTransferable = true;
-        }
-    }
-
-    return true;
-}
-
-}
-
-static inline uint64_t
-PairToUInt64(uint32_t tag, uint32_t data)
-{
-    return uint64_t(data) | (uint64_t(tag) << 32);
-}
-
-bool
-SCInput::eof()
-{
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "truncated");
-    return false;
-}
-
-SCInput::SCInput(JSContext *cx, uint64_t *data, size_t nbytes)
-    : cx(cx), point(data), end(data + nbytes / 8)
-{
-    JS_ASSERT((uintptr_t(data) & 7) == 0);
-    JS_ASSERT((nbytes & 7) == 0);
-}
-
-bool
-SCInput::read(uint64_t *p)
-{
-    if (point == end) {
-        *p = 0;  /* initialize to shut GCC up */
-        return eof();
-    }
-    *p = LittleEndian::readUint64(point++);
-    return true;
-}
-
-bool
-SCInput::readPair(uint32_t *tagp, uint32_t *datap)
-{
-    uint64_t u;
-    bool ok = read(&u);
-    if (ok) {
-        *tagp = uint32_t(u >> 32);
-        *datap = uint32_t(u);
-    }
-    return ok;
-}
-
-bool
-SCInput::get(uint64_t *p)
-{
-    if (point == end)
-        return eof();
-    *p = LittleEndian::readUint64(point);
-    return true;
-}
-
-bool
-SCInput::getPair(uint32_t *tagp, uint32_t *datap)
-{
-    uint64_t u;
-    if (!get(&u))
-        return false;
-
-    *tagp = uint32_t(u >> 32);
-    *datap = uint32_t(u);
-    return true;
-}
-
-bool
-SCInput::replace(uint64_t u)
-{
-    if (point == end)
-       return eof();
-    LittleEndian::writeUint64(point, u);
-    return true;
-}
-
-bool
-SCInput::replacePair(uint32_t tag, uint32_t data)
-{
-    return replace(PairToUInt64(tag, data));
-}
-
-/*
- * The purpose of this never-inlined function is to avoid a strange g++ build
- * error on OS X 10.5 (see bug 624080).  :-(
- */
-static JS_NEVER_INLINE double
-CanonicalizeNan(double d)
-{
-    return JS_CANONICALIZE_NAN(d);
-}
-
-bool
-SCInput::readDouble(double *p)
-{
-    union {
-        uint64_t u;
-        double d;
-    } pun;
-    if (!read(&pun.u))
-        return false;
-    *p = CanonicalizeNan(pun.d);
-    return true;
-}
-
-template <typename T>
-static void
-copyAndSwapFromLittleEndian(T *dest, const void *src, size_t nelems)
-{
-    NativeEndian::copyAndSwapFromLittleEndian(dest, src, nelems);
-}
-
-template <>
-void
-copyAndSwapFromLittleEndian(uint8_t *dest, const void *src, size_t nelems)
-{
-    memcpy(dest, src, nelems);
-}
-
-template <class T>
-bool
-SCInput::readArray(T *p, size_t nelems)
-{
-    JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
-
-    /*
-     * Fail if nelems is so huge as to make JS_HOWMANY overflow or if nwords is
-     * larger than the remaining data.
-     */
-    size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
-    if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems || nwords > size_t(end - point))
-        return eof();
-
-    copyAndSwapFromLittleEndian(p, point, nelems);
-    point += nwords;
-    return true;
-}
-
-bool
-SCInput::readBytes(void *p, size_t nbytes)
-{
-    return readArray((uint8_t *) p, nbytes);
-}
-
-bool
-SCInput::readChars(jschar *p, size_t nchars)
-{
-    JS_ASSERT(sizeof(jschar) == sizeof(uint16_t));
-    return readArray((uint16_t *) p, nchars);
-}
-
-bool
-SCInput::readPtr(void **p)
-{
-    // On a 32 bit system the void* variable we have to write to is only
-    // 32 bits, so we create a 64 temporary and discard the unused bits.
-    uint64_t tmp;
-    bool ret = read(&tmp);
-    *p = reinterpret_cast<void*>(tmp);
-    return ret;
-}
-
-SCOutput::SCOutput(JSContext *cx) : cx(cx), buf(cx) {}
-
-bool
-SCOutput::write(uint64_t u)
-{
-    return buf.append(NativeEndian::swapToLittleEndian(u));
-}
-
-bool
-SCOutput::writePair(uint32_t tag, uint32_t data)
-{
-    /*
-     * As it happens, the tag word appears after the data word in the output.
-     * This is because exponents occupy the last 2 bytes of doubles on the
-     * little-endian platforms we care most about.
-     *
-     * For example, JSVAL_TRUE is written using writePair(SCTAG_BOOLEAN, 1).
-     * PairToUInt64 produces the number 0xFFFF000200000001.
-     * That is written out as the bytes 01 00 00 00 02 00 FF FF.
-     */
-    return write(PairToUInt64(tag, data));
-}
-
-static inline uint64_t
-ReinterpretDoubleAsUInt64(double d)
-{
-    union {
-        double d;
-        uint64_t u;
-    } pun;
-    pun.d = d;
-    return pun.u;
-}
-
-static inline double
-ReinterpretUInt64AsDouble(uint64_t u)
-{
-    union {
-        uint64_t u;
-        double d;
-    } pun;
-    pun.u = u;
-    return pun.d;
-}
-
-static inline double
-ReinterpretPairAsDouble(uint32_t tag, uint32_t data)
-{
-    return ReinterpretUInt64AsDouble(PairToUInt64(tag, data));
-}
-
-bool
-SCOutput::writeDouble(double d)
-{
-    return write(ReinterpretDoubleAsUInt64(CanonicalizeNan(d)));
-}
-
-template <typename T>
-static void
-copyAndSwapToLittleEndian(void *dest, const T *src, size_t nelems)
-{
-    NativeEndian::copyAndSwapToLittleEndian(dest, src, nelems);
-}
-
-template <>
-void
-copyAndSwapToLittleEndian(void *dest, const uint8_t *src, size_t nelems)
-{
-    memcpy(dest, src, nelems);
-}
-
-template <class T>
-bool
-SCOutput::writeArray(const T *p, size_t nelems)
-{
-    JS_ASSERT(8 % sizeof(T) == 0);
-    JS_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
-
-    if (nelems == 0)
-        return true;
-
-    if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems) {
-        js_ReportAllocationOverflow(context());
-        return false;
-    }
-    size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
-    size_t start = buf.length();
-    if (!buf.growByUninitialized(nwords))
-        return false;
-
-    buf.back() = 0;  /* zero-pad to an 8-byte boundary */
-
-    T *q = (T *) &buf[start];
-    copyAndSwapToLittleEndian(q, p, nelems);
-    return true;
-}
-
-bool
-SCOutput::writeBytes(const void *p, size_t nbytes)
-{
-    return writeArray((const uint8_t *) p, nbytes);
-}
-
-bool
-SCOutput::writeChars(const jschar *p, size_t nchars)
-{
-    JS_ASSERT(sizeof(jschar) == sizeof(uint16_t));
-    return writeArray((const uint16_t *) p, nchars);
-}
-
-bool
-SCOutput::writePtr(const void *p)
-{
-    return write(reinterpret_cast<uint64_t>(p));
-}
-
-bool
-SCOutput::extractBuffer(uint64_t **datap, size_t *sizep)
-{
-    *sizep = buf.length() * sizeof(uint64_t);
-    return (*datap = buf.extractRawBuffer()) != NULL;
-}
-
-JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
-
-bool
-JSStructuredCloneWriter::parseTransferable()
-{
-    transferableObjects.clear();
-
-    if (JSVAL_IS_NULL(transferable) || JSVAL_IS_VOID(transferable))
-        return true;
-
-    if (!transferable.isObject()) {
-        reportErrorTransferable();
-        return false;
-    }
-
-    RootedObject array(context(), &transferable.toObject());
-    if (!JS_IsArrayObject(context(), array)) {
-        reportErrorTransferable();
-        return false;
-    }
-
-    uint32_t length;
-    if (!JS_GetArrayLength(context(), array, &length)) {
-        return false;
-    }
-
-    RootedValue v(context());
-
-    for (uint32_t i = 0; i < length; ++i) {
-        if (!JS_GetElement(context(), array, i, &v)) {
-            return false;
-        }
-
-        if (!v.isObject()) {
-            reportErrorTransferable();
-            return false;
-        }
-
-        JSObject* tObj = CheckedUnwrap(&v.toObject());
-        if (!tObj) {
-            JS_ReportError(context(), "Permission denied to access object");
-            return false;
-        }
-        if (!tObj->is<ArrayBufferObject>()) {
-            reportErrorTransferable();
-            return false;
-        }
-
-        // No duplicate:
-        if (transferableObjects.has(tObj)) {
-            reportErrorTransferable();
-            return false;
-        }
-
-        if (!transferableObjects.putNew(tObj))
-            return false;
-    }
-
-    return true;
-}
-
-void
-JSStructuredCloneWriter::reportErrorTransferable()
-{
-    if (callbacks && callbacks->reportError)
-        return callbacks->reportError(context(), JS_SCERR_TRANSFERABLE);
-}
-
-bool
-JSStructuredCloneWriter::writeString(uint32_t tag, JSString *str)
-{
-    size_t length = str->length();
-    const jschar *chars = str->getChars(context());
-    if (!chars)
-        return false;
-    return out.writePair(tag, uint32_t(length)) && out.writeChars(chars, length);
-}
-
-bool
-JSStructuredCloneWriter::writeId(jsid id)
-{
-    if (JSID_IS_INT(id))
-        return out.writePair(SCTAG_INDEX, uint32_t(JSID_TO_INT(id)));
-    JS_ASSERT(JSID_IS_STRING(id));
-    return writeString(SCTAG_STRING, JSID_TO_STRING(id));
-}
-
-inline void
-JSStructuredCloneWriter::checkStack()
-{
-#ifdef DEBUG
-    /* To avoid making serialization O(n^2), limit stack-checking at 10. */
-    const size_t MAX = 10;
-
-    size_t limit = Min(counts.length(), MAX);
-    JS_ASSERT(objs.length() == counts.length());
-    size_t total = 0;
-    for (size_t i = 0; i < limit; i++) {
-        JS_ASSERT(total + counts[i] >= total);
-        total += counts[i];
-    }
-    if (counts.length() <= MAX)
-        JS_ASSERT(total == ids.length());
-    else
-        JS_ASSERT(total <= ids.length());
-
-    size_t j = objs.length();
-    for (size_t i = 0; i < limit; i++)
-        JS_ASSERT(memory.has(&objs[--j].toObject()));
-#endif
-}
-
-/*
- * Write out a typed array. Note that post-v1 structured clone buffers do not
- * perform endianness conversion on stored data, so multibyte typed arrays
- * cannot be deserialized into a different endianness machine. Endianness
- * conversion would prevent sharing ArrayBuffers: if you have Int8Array and
- * Int16Array views of the same ArrayBuffer, should the data bytes be
- * byte-swapped when writing or not? The Int8Array requires them to not be
- * swapped; the Int16Array requires that they are.
- */
-bool
-JSStructuredCloneWriter::writeTypedArray(HandleObject obj)
-{
-    Rooted<TypedArrayObject*> tarr(context(), &obj->as<TypedArrayObject>());
-    if (!out.writePair(SCTAG_TYPED_ARRAY_OBJECT, tarr->length()))
-        return false;
-    uint64_t type = tarr->type();
-    if (!out.write(type))
-        return false;
-
-    // Write out the ArrayBuffer tag and contents
-    if (!startWrite(TypedArrayObject::bufferValue(tarr)))
-        return false;
-
-    return out.write(tarr->byteOffset());
-}
-
-bool
-JSStructuredCloneWriter::writeArrayBuffer(HandleObject obj)
-{
-    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
-    return out.writePair(SCTAG_ARRAY_BUFFER_OBJECT, buffer.byteLength()) &&
-           out.writeBytes(buffer.dataPointer(), buffer.byteLength());
-}
-
-bool
-JSStructuredCloneWriter::startObject(HandleObject obj, bool *backref)
-{
-    /* Handle cycles in the object graph. */
-    CloneMemory::AddPtr p = memory.lookupForAdd(obj);
-    if ((*backref = p))
-        return out.writePair(SCTAG_BACK_REFERENCE_OBJECT, p->value);
-    if (!memory.add(p, obj, memory.count()))
-        return false;
-
-    if (memory.count() == UINT32_MAX) {
-        JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
-                             JSMSG_NEED_DIET, "object graph to serialize");
-        return false;
-    }
-
-    return true;
-}
-
-bool
-JSStructuredCloneWriter::traverseObject(HandleObject obj)
-{
-    /*
-     * Get enumerable property ids and put them in reverse order so that they
-     * will come off the stack in forward order.
-     */
-    size_t initialLength = ids.length();
-    if (!GetPropertyNames(context(), obj, JSITER_OWNONLY, &ids))
-        return false;
-    jsid *begin = ids.begin() + initialLength, *end = ids.end();
-    size_t count = size_t(end - begin);
-    Reverse(begin, end);
-
-    /* Push obj and count to the stack. */
-    if (!objs.append(ObjectValue(*obj)) || !counts.append(count))
-        return false;
-    checkStack();
-
-    /* Write the header for obj. */
-    return out.writePair(obj->is<ArrayObject>() ? SCTAG_ARRAY_OBJECT : SCTAG_OBJECT_OBJECT, 0);
-}
-
-bool
-JSStructuredCloneWriter::startWrite(const Value &v)
-{
-    assertSameCompartment(context(), v);
-
-    if (v.isString()) {
-        return writeString(SCTAG_STRING, v.toString());
-    } else if (v.isNumber()) {
-        return out.writeDouble(v.toNumber());
-    } else if (v.isBoolean()) {
-        return out.writePair(SCTAG_BOOLEAN, v.toBoolean());
-    } else if (v.isNull()) {
-        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 = CheckedUnwrap(obj);
-        if (!obj) {
-            JS_ReportError(context(), "Permission denied to access object");
-            return false;
-        }
-
-        AutoCompartment ac(context(), obj);
-
-        bool backref;
-        if (!startObject(obj, &backref))
-            return false;
-        if (backref)
-            return true;
-
-        if (obj->is<RegExpObject>()) {
-            RegExpObject &reobj = obj->as<RegExpObject>();
-            return out.writePair(SCTAG_REGEXP_OBJECT, reobj.getFlags()) &&
-                   writeString(SCTAG_STRING, reobj.getSource());
-        } else if (obj->is<DateObject>()) {
-            double d = js_DateGetMsecSinceEpoch(obj);
-            return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(d);
-        } else if (obj->is<TypedArrayObject>()) {
-            return writeTypedArray(obj);
-        } else if (obj->is<ArrayBufferObject>() && obj->as<ArrayBufferObject>().hasData()) {
-            return writeArrayBuffer(obj);
-        } else if (obj->is<JSObject>() || obj->is<ArrayObject>()) {
-            return traverseObject(obj);
-        } else if (obj->is<BooleanObject>()) {
-            return out.writePair(SCTAG_BOOLEAN_OBJECT, obj->as<BooleanObject>().unbox());
-        } else if (obj->is<NumberObject>()) {
-            return out.writePair(SCTAG_NUMBER_OBJECT, 0) &&
-                   out.writeDouble(obj->as<NumberObject>().unbox());
-        } else if (obj->is<StringObject>()) {
-            return writeString(SCTAG_STRING_OBJECT, obj->as<StringObject>().unbox());
-        }
-
-        if (callbacks && callbacks->write)
-            return callbacks->write(context(), this, obj, closure);
-        /* else fall through */
-    }
-
-    JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_UNSUPPORTED_TYPE);
-    return false;
-}
-
-bool
-JSStructuredCloneWriter::writeTransferMap()
-{
-    if (!transferableObjects.empty()) {
-        if (!out.writePair(SCTAG_TRANSFER_MAP_HEADER, (uint32_t)SCTAG_TM_NOT_MARKED))
-            return false;
-
-        for (HashSet<JSObject*>::Range r = transferableObjects.all();
-             !r.empty(); r.popFront()) {
-            JSObject *obj = r.front();
-
-            if (!memory.put(obj, memory.count()))
-                return false;
-
-            void *content;
-            uint8_t *data;
-            if (!JS_StealArrayBufferContents(context(), obj, &content, &data))
-               return false;
-
-            if (!out.writePair(SCTAG_TRANSFER_MAP, 0) || !out.writePtr(content))
-                return false;
-        }
-    }
-
-    return true;
-}
-
-bool
-JSStructuredCloneWriter::write(const Value &v)
-{
-    if (!startWrite(v))
-        return false;
-
-    while (!counts.empty()) {
-        RootedObject obj(context(), &objs.back().toObject());
-        AutoCompartment ac(context(), obj);
-        if (counts.back()) {
-            counts.back()--;
-            RootedId id(context(), ids.back());
-            ids.popBack();
-            checkStack();
-            if (JSID_IS_STRING(id) || JSID_IS_INT(id)) {
-                /*
-                 * If obj still has an own property named id, write it out.
-                 * The cost of re-checking could be avoided by using
-                 * NativeIterators.
-                 */
-                RootedObject obj2(context());
-                RootedShape prop(context());
-                if (!HasOwnProperty<CanGC>(context(), obj->getOps()->lookupGeneric, obj, id,
-                                           &obj2, &prop)) {
-                    return false;
-                }
-
-                if (prop) {
-                    RootedValue val(context());
-                    if (!writeId(id) ||
-                        !JSObject::getGeneric(context(), obj, obj, id, &val) ||
-                        !startWrite(val))
-                        return false;
-                }
-            }
-        } else {
-            out.writePair(SCTAG_NULL, 0);
-            objs.popBack();
-            counts.popBack();
-        }
-    }
-
-    memory.clear();
-
-    return true;
-}
-
-bool
-JSStructuredCloneReader::checkDouble(double d)
-{
-    jsval_layout l;
-    l.asDouble = d;
-    if (!JSVAL_IS_DOUBLE_IMPL(l)) {
-        JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
-                             JSMSG_SC_BAD_SERIALIZED_DATA, "unrecognized NaN");
-        return false;
-    }
-    return true;
-}
-
-class Chars {
-    JSContext *cx;
-    jschar *p;
-  public:
-    Chars(JSContext *cx) : cx(cx), p(NULL) {}
-    ~Chars() { if (p) js_free(p); }
-
-    bool allocate(size_t len) {
-        JS_ASSERT(!p);
-        // We're going to null-terminate!
-        p = cx->pod_malloc<jschar>(len + 1);
-        if (p) {
-            p[len] = jschar(0);
-            return true;
-        }
-        return false;
-    }
-    jschar *get() { return p; }
-    void forget() { p = NULL; }
-};
-
-JSString *
-JSStructuredCloneReader::readString(uint32_t nchars)
-{
-    if (nchars > JSString::MAX_LENGTH) {
-        JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
-                             "string length");
-        return NULL;
-    }
-    Chars chars(context());
-    if (!chars.allocate(nchars) || !in.readChars(chars.get(), nchars))
-        return NULL;
-    JSString *str = js_NewString<CanGC>(context(), chars.get(), nchars);
-    if (str)
-        chars.forget();
-    return str;
-}
-
-static uint32_t
-TagToV1ArrayType(uint32_t tag)
-{
-    JS_ASSERT(tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX);
-    return tag - SCTAG_TYPED_ARRAY_V1_MIN;
-}
-
-bool
-JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, Value *vp,
-                                        bool v1Read)
-{
-    if (arrayType > TypedArrayObject::TYPE_UINT8_CLAMPED) {
-        JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
-                             JSMSG_SC_BAD_SERIALIZED_DATA, "unhandled typed array element type");
-        return false;
-    }
-
-    // Push a placeholder onto the allObjs list to stand in for the typed array
-    uint32_t placeholderIndex = allObjs.length();
-    Value dummy = JSVAL_NULL;
-    if (!allObjs.append(dummy))
-        return false;
-
-    // Read the ArrayBuffer object and its contents (but no properties)
-    RootedValue v(context());
-    uint32_t byteOffset;
-    if (v1Read) {
-        if (!readV1ArrayBuffer(arrayType, nelems, v.address()))
-            return false;
-        byteOffset = 0;
-    } else {
-        if (!startRead(v.address()))
-            return false;
-        uint64_t n;
-        if (!in.read(&n))
-            return false;
-        byteOffset = n;
-    }
-    RootedObject buffer(context(), &v.toObject());
-    RootedObject obj(context(), NULL);
-
-    switch (arrayType) {
-      case TypedArrayObject::TYPE_INT8:
-        obj = JS_NewInt8ArrayWithBuffer(context(), buffer, byteOffset, nelems);
-        break;
-      case TypedArrayObject::TYPE_UINT8:
-        obj = JS_NewUint8ArrayWithBuffer(context(), buffer, byteOffset, nelems);
-        break;
-      case TypedArrayObject::TYPE_INT16:
-        obj = JS_NewInt16ArrayWithBuffer(context(), buffer, byteOffset, nelems);
-        break;
-      case TypedArrayObject::TYPE_UINT16:
-        obj = JS_NewUint16ArrayWithBuffer(context(), buffer, byteOffset, nelems);
-        break;
-      case TypedArrayObject::TYPE_INT32:
-        obj = JS_NewInt32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
-        break;
-      case TypedArrayObject::TYPE_UINT32:
-        obj = JS_NewUint32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
-        break;
-      case TypedArrayObject::TYPE_FLOAT32:
-        obj = JS_NewFloat32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
-        break;
-      case TypedArrayObject::TYPE_FLOAT64:
-        obj = JS_NewFloat64ArrayWithBuffer(context(), buffer, byteOffset, nelems);
-        break;
-      case TypedArrayObject::TYPE_UINT8_CLAMPED:
-        obj = JS_NewUint8ClampedArrayWithBuffer(context(), buffer, byteOffset, nelems);
-        break;
-      default:
-        MOZ_ASSUME_UNREACHABLE("unknown TypedArrayObject type");
-    }
-
-    if (!obj)
-        return false;
-    vp->setObject(*obj);
-
-    allObjs[placeholderIndex] = *vp;
-
-    return true;
-}
-
-bool
-JSStructuredCloneReader::readArrayBuffer(uint32_t nbytes, Value *vp)
-{
-    JSObject *obj = ArrayBufferObject::create(context(), nbytes);
-    if (!obj)
-        return false;
-    vp->setObject(*obj);
-    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
-    JS_ASSERT(buffer.byteLength() == nbytes);
-    return in.readArray(buffer.dataPointer(), nbytes);
-}
-
-static size_t
-bytesPerTypedArrayElement(uint32_t arrayType)
-{
-    switch (arrayType) {
-      case TypedArrayObject::TYPE_INT8:
-      case TypedArrayObject::TYPE_UINT8:
-      case TypedArrayObject::TYPE_UINT8_CLAMPED:
-        return sizeof(uint8_t);
-      case TypedArrayObject::TYPE_INT16:
-      case TypedArrayObject::TYPE_UINT16:
-        return sizeof(uint16_t);
-      case TypedArrayObject::TYPE_INT32:
-      case TypedArrayObject::TYPE_UINT32:
-      case TypedArrayObject::TYPE_FLOAT32:
-        return sizeof(uint32_t);
-      case TypedArrayObject::TYPE_FLOAT64:
-        return sizeof(uint64_t);
-      default:
-        MOZ_ASSUME_UNREACHABLE("unknown TypedArrayObject type");
-    }
-}
-
-/*
- * Read in the data for a structured clone version 1 ArrayBuffer, performing
- * endianness-conversion while reading.
- */
-bool
-JSStructuredCloneReader::readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, Value *vp)
-{
-    JS_ASSERT(arrayType <= TypedArrayObject::TYPE_UINT8_CLAMPED);
-
-    uint32_t nbytes = nelems * bytesPerTypedArrayElement(arrayType);
-    JSObject *obj = ArrayBufferObject::create(context(), nbytes);
-    if (!obj)
-        return false;
-    vp->setObject(*obj);
-    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
-    JS_ASSERT(buffer.byteLength() == nbytes);
-
-    switch (arrayType) {
-      case TypedArrayObject::TYPE_INT8:
-      case TypedArrayObject::TYPE_UINT8:
-      case TypedArrayObject::TYPE_UINT8_CLAMPED:
-        return in.readArray((uint8_t*) buffer.dataPointer(), nelems);
-      case TypedArrayObject::TYPE_INT16:
-      case TypedArrayObject::TYPE_UINT16:
-        return in.readArray((uint16_t*) buffer.dataPointer(), nelems);
-      case TypedArrayObject::TYPE_INT32:
-      case TypedArrayObject::TYPE_UINT32:
-      case TypedArrayObject::TYPE_FLOAT32:
-        return in.readArray((uint32_t*) buffer.dataPointer(), nelems);
-      case TypedArrayObject::TYPE_FLOAT64:
-        return in.readArray((uint64_t*) buffer.dataPointer(), nelems);
-      default:
-        MOZ_ASSUME_UNREACHABLE("unknown TypedArrayObject type");
-    }
-}
-
-bool
-JSStructuredCloneReader::startRead(Value *vp)
-{
-    uint32_t tag, data;
-
-    if (!in.readPair(&tag, &data))
-        return false;
-    switch (tag) {
-      case SCTAG_NULL:
-        vp->setNull();
-        break;
-
-      case SCTAG_UNDEFINED:
-        vp->setUndefined();
-        break;
-
-      case SCTAG_BOOLEAN:
-      case SCTAG_BOOLEAN_OBJECT:
-        vp->setBoolean(!!data);
-        if (tag == SCTAG_BOOLEAN_OBJECT && !js_PrimitiveToObject(context(), vp))
-            return false;
-        break;
-
-      case SCTAG_STRING:
-      case SCTAG_STRING_OBJECT: {
-        JSString *str = readString(data);
-        if (!str)
-            return false;
-        vp->setString(str);
-        if (tag == SCTAG_STRING_OBJECT && !js_PrimitiveToObject(context(), vp))
-            return false;
-        break;
-      }
-
-      case SCTAG_NUMBER_OBJECT: {
-        double d;
-        if (!in.readDouble(&d) || !checkDouble(d))
-            return false;
-        vp->setDouble(d);
-        if (!js_PrimitiveToObject(context(), vp))
-            return false;
-        break;
-      }
-
-      case SCTAG_DATE_OBJECT: {
-        double d;
-        if (!in.readDouble(&d) || !checkDouble(d))
-            return false;
-        if (!IsNaN(d) && d != TimeClip(d)) {
-            JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
-                                 "date");
-            return false;
-        }
-        JSObject *obj = js_NewDateObjectMsec(context(), d);
-        if (!obj)
-            return false;
-        vp->setObject(*obj);
-        break;
-      }
-
-      case SCTAG_REGEXP_OBJECT: {
-        RegExpFlag flags = RegExpFlag(data);
-        uint32_t tag2, nchars;
-        if (!in.readPair(&tag2, &nchars))
-            return false;
-        if (tag2 != SCTAG_STRING) {
-            JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
-                                 "regexp");
-            return false;
-        }
-        JSString *str = readString(nchars);
-        if (!str)
-            return false;
-        JSStableString *stable = str->ensureStable(context());
-        if (!stable)
-            return false;
-
-        size_t length = stable->length();
-        const StableCharPtr chars = stable->chars();
-        RegExpObject *reobj = RegExpObject::createNoStatics(context(), chars.get(), length, flags, NULL);
-        if (!reobj)
-            return false;
-        vp->setObject(*reobj);
-        break;
-      }
-
-      case SCTAG_ARRAY_OBJECT:
-      case SCTAG_OBJECT_OBJECT: {
-        JSObject *obj = (tag == SCTAG_ARRAY_OBJECT)
-                        ? NewDenseEmptyArray(context())
-                        : NewBuiltinClassInstance(context(), &JSObject::class_);
-        if (!obj || !objs.append(ObjectValue(*obj)))
-            return false;
-        vp->setObject(*obj);
-        break;
-      }
-
-      case SCTAG_BACK_REFERENCE_OBJECT: {
-        if (data >= allObjs.length()) {
-            JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
-                                 JSMSG_SC_BAD_SERIALIZED_DATA,
-                                 "invalid back reference in input");
-            return false;
-        }
-        *vp = allObjs[data];
-        return true;
-      }
-
-      case SCTAG_TRANSFER_MAP_HEADER:
-        // A map header cannot be here but just at the beginning of the buffer.
-        JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
-                             JSMSG_SC_BAD_SERIALIZED_DATA,
-                             "invalid input");
-        return false;
-
-      case SCTAG_TRANSFER_MAP:
-        // A map cannot be here but just at the beginning of the buffer.
-        JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
-                             JSMSG_SC_BAD_SERIALIZED_DATA,
-                             "invalid input");
-        return false;
-
-      case SCTAG_ARRAY_BUFFER_OBJECT:
-        if (!readArrayBuffer(data, vp))
-            return false;
-        break;
-
-      case SCTAG_TYPED_ARRAY_OBJECT:
-        // readTypedArray adds the array to allObjs
-        uint64_t arrayType;
-        if (!in.read(&arrayType))
-            return false;
-        return readTypedArray(arrayType, data, vp);
-        break;
-
-      default: {
-        if (tag <= SCTAG_FLOAT_MAX) {
-            double d = ReinterpretPairAsDouble(tag, data);
-            if (!checkDouble(d))
-                return false;
-            vp->setNumber(d);
-            break;
-        }
-
-        if (SCTAG_TYPED_ARRAY_V1_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_V1_MAX) {
-            // A v1-format typed array
-            // readTypedArray adds the array to allObjs
-            return readTypedArray(TagToV1ArrayType(tag), data, vp, true);
-        }
-
-        if (!callbacks || !callbacks->read) {
-            JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
-                                 "unsupported type");
-            return false;
-        }
-        JSObject *obj = callbacks->read(context(), this, tag, data, closure);
-        if (!obj)
-            return false;
-        vp->setObject(*obj);
-      }
-    }
-
-    if (vp->isObject() && !allObjs.append(*vp))
-        return false;
-
-    return true;
-}
-
-bool
-JSStructuredCloneReader::readId(jsid *idp)
-{
-    uint32_t tag, data;
-    if (!in.readPair(&tag, &data))
-        return false;
-
-    if (tag == SCTAG_INDEX) {
-        *idp = INT_TO_JSID(int32_t(data));
-        return true;
-    }
-    if (tag == SCTAG_STRING) {
-        JSString *str = readString(data);
-        if (!str)
-            return false;
-        JSAtom *atom = AtomizeString<CanGC>(context(), str);
-        if (!atom)
-            return false;
-        *idp = NON_INTEGER_ATOM_TO_JSID(atom);
-        return true;
-    }
-    if (tag == SCTAG_NULL) {
-        *idp = JSID_VOID;
-        return true;
-    }
-    JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "id");
-    return false;
-}
-
-bool
-JSStructuredCloneReader::readTransferMap()
-{
-    uint32_t tag, data;
-    if (!in.getPair(&tag, &data))
-        return false;
-
-    if (tag != SCTAG_TRANSFER_MAP_HEADER ||
-        (TransferableMapHeader)data == SCTAG_TM_MARKED)
-        return true;
-
-    if (!in.replacePair(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_MARKED))
-        return false;
-
-    if (!in.readPair(&tag, &data))
-        return false;
-
-    while (1) {
-        if (!in.getPair(&tag, &data))
-            return false;
-
-        if (tag != SCTAG_TRANSFER_MAP)
-            break;
-
-        void *content;
-
-        if (!in.readPair(&tag, &data) || !in.readPtr(&content))
-            return false;
-
-        JSObject *obj = JS_NewArrayBufferWithContents(context(), content);
-        if (!obj || !allObjs.append(ObjectValue(*obj)))
-            return false;
-    }
-
-    return true;
-}
-
-bool
-JSStructuredCloneReader::read(Value *vp)
-{
-    if (!readTransferMap())
-        return false;
-
-    if (!startRead(vp))
-        return false;
-
-    while (objs.length() != 0) {
-        RootedObject obj(context(), &objs.back().toObject());
-
-        RootedId id(context());
-        if (!readId(id.address()))
-            return false;
-
-        if (JSID_IS_VOID(id)) {
-            objs.popBack();
-        } else {
-            RootedValue v(context());
-            if (!startRead(v.address()) || !JSObject::defineGeneric(context(), obj, id, v))
-                return false;
-        }
-    }
-
-    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,
-                       const JSStructuredCloneCallbacks *optionalCallbacks,
-                       void *closure)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-
-    if (version > JS_STRUCTURED_CLONE_VERSION) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION);
-        return false;
-    }
-    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,
-                        const JSStructuredCloneCallbacks *optionalCallbacks,
-                        void *closure, JS::Value transferable)
-{
-    RootedValue value(cx, valueArg);
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, value);
-
-    const JSStructuredCloneCallbacks *callbacks =
-        optionalCallbacks ?
-        optionalCallbacks :
-        cx->runtime()->structuredCloneCallbacks;
-    return WriteStructuredClone(cx, value, bufp, nbytesp, callbacks, closure, transferable);
-}
-
-JS_PUBLIC_API(bool)
-JS_ClearStructuredClone(const uint64_t *data, size_t nbytes)
-{
-    return ClearStructuredClone(data, nbytes);
-}
-
-JS_PUBLIC_API(bool)
-JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes,
-                                   bool *hasTransferable)
-{
-    bool transferable;
-    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,
-                   const JSStructuredCloneCallbacks *optionalCallbacks,
-                   void *closure)
-{
-    RootedValue value(cx, valueArg);
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, value);
-
-    const JSStructuredCloneCallbacks *callbacks =
-        optionalCallbacks ?
-        optionalCallbacks :
-        cx->runtime()->structuredCloneCallbacks;
-    JSAutoStructuredCloneBuffer buf;
-    return buf.write(cx, value, callbacks, closure) &&
-           buf.read(cx, vp, callbacks, closure);
-}
-
-void
-JSAutoStructuredCloneBuffer::clear()
-{
-    if (data_) {
-        ClearStructuredClone(data_, nbytes_);
-        data_ = NULL;
-        nbytes_ = 0;
-        version_ = 0;
-    }
-}
-
-bool
-JSAutoStructuredCloneBuffer::copy(const uint64_t *srcData, size_t nbytes, uint32_t version)
-{
-    // transferable objects cannot be copied
-    bool hasTransferable;
-    if (!StructuredCloneHasTransferObjects(data_, nbytes_, &hasTransferable) ||
-        hasTransferable)
-        return false;
-
-    uint64_t *newData = static_cast<uint64_t *>(js_malloc(nbytes));
-    if (!newData)
-        return false;
-
-    js_memcpy(newData, srcData, nbytes);
-
-    clear();
-    data_ = newData;
-    nbytes_ = nbytes;
-    version_ = version;
-    return true;
-}
-
-void
-JSAutoStructuredCloneBuffer::adopt(uint64_t *data, size_t nbytes, uint32_t version)
-{
-    clear();
-    data_ = data;
-    nbytes_ = nbytes;
-    version_ = version;
-}
-
-void
-JSAutoStructuredCloneBuffer::steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp)
-{
-    *datap = data_;
-    *nbytesp = nbytes_;
-    if (versionp)
-        *versionp = version_;
-
-    data_ = NULL;
-    nbytes_ = 0;
-    version_ = 0;
-}
-
-bool
-JSAutoStructuredCloneBuffer::read(JSContext *cx, JS::Value *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,
-                                   const JSStructuredCloneCallbacks *optionalCallbacks,
-                                   void *closure)
-{
-    JS::Value transferable = JSVAL_VOID;
-    return write(cx, valueArg, transferable, optionalCallbacks, closure);
-}
-
-bool
-JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::Value valueArg,
-                                   JS::Value 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_ = NULL;
-        nbytes_ = 0;
-        version_ = JS_STRUCTURED_CLONE_VERSION;
-    }
-    return ok;
-}
-
-void
-JSAutoStructuredCloneBuffer::swap(JSAutoStructuredCloneBuffer &other)
-{
-    uint64_t *data = other.data_;
-    size_t nbytes = other.nbytes_;
-    uint32_t version = other.version_;
-
-    other.data_ = this->data_;
-    other.nbytes_ = this->nbytes_;
-    other.version_ = this->version_;
-
-    this->data_ = data;
-    this->nbytes_ = nbytes;
-    this->version_ = version;
-}
-
-JS_PUBLIC_API(void)
-JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks)
-{
-    rt->structuredCloneCallbacks = callbacks;
-}
-
-JS_PUBLIC_API(bool)
-JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2)
-{
-    return r->input().readPair((uint32_t *) p1, (uint32_t *) p2);
-}
-
-JS_PUBLIC_API(bool)
-JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len)
-{
-    return r->input().readBytes(p, len);
-}
-
-JS_PUBLIC_API(bool)
-JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp)
-{
-    uint32_t tag, nelems;
-    if (!r->input().readPair(&tag, &nelems))
-        return false;
-    if (tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX) {
-        return r->readTypedArray(TagToV1ArrayType(tag), nelems, vp, true);
-    } else if (tag == SCTAG_TYPED_ARRAY_OBJECT) {
-        uint64_t arrayType;
-        if (!r->input().read(&arrayType))
-            return false;
-        return r->readTypedArray(arrayType, nelems, vp);
-    } else {
-        JS_ReportErrorNumber(r->context(), js_GetErrorMessage, NULL,
-                             JSMSG_SC_BAD_SERIALIZED_DATA, "expected type array");
-        return false;
-    }
-}
-
-JS_PUBLIC_API(bool)
-JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data)
-{
-    return w->output().writePair(tag, data);
-}
-
-JS_PUBLIC_API(bool)
-JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len)
-{
-    return w->output().writeBytes(p, len);
-}
-
-JS_PUBLIC_API(bool)
-JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v)
-{
-    JS_ASSERT(v.isObject());
-    assertSameCompartment(w->context(), v);
-    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->is<WrapperObject>())
-        obj = CheckedUnwrap(obj);
-    if (!obj) {
-        JS_ReportError(w->context(), "Permission denied to access object");
-        return false;
-    }
-    return w->writeTypedArray(obj);
-}
-
deleted file mode 100644
--- a/js/src/vm/Value.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "js/Value.h"
-
-const jsval JSVAL_NULL  = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_NULL,      0));
-const jsval JSVAL_ZERO  = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32,     0));
-const jsval JSVAL_ONE   = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32,     1));
-const jsval JSVAL_FALSE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN,   false));
-const jsval JSVAL_TRUE  = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN,   true));
-const jsval JSVAL_VOID  = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0));
-
-namespace JS {
-
-const HandleValue NullHandleValue = HandleValue::fromMarkedLocation(&JSVAL_NULL);
-const HandleValue UndefinedHandleValue = HandleValue::fromMarkedLocation(&JSVAL_VOID);
-
-} // namespace JS
--- a/js/xpconnect/public/nsAutoJSValHolder.h
+++ b/js/xpconnect/public/nsAutoJSValHolder.h
@@ -2,20 +2,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __NSAUTOJSVALHOLDER_H__
 #define __NSAUTOJSVALHOLDER_H__
 
 #include "nsDebug.h"
-#include "jsapi.h"
 
 /**
- * Simple class that looks and acts like a JS::Value except that it unroots
+ * Simple class that looks and acts like a jsval except that it unroots
  * itself automatically if Root() is ever called. Designed to be rooted on the
  * context or runtime (but not both!).
  */
 class nsAutoJSValHolder
 {
 public:
   nsAutoJSValHolder()
     : mVal(JSVAL_NULL), mRt(nullptr)
@@ -40,17 +39,17 @@ public:
     if (this != &aOther) {
       if (aOther.IsHeld()) {
         // XXX No error handling here...
         this->Hold(aOther.mRt);
       }
       else {
         this->Release();
       }
-      *this = static_cast<JS::Value>(aOther);
+      *this = static_cast<jsval>(aOther);
     }
     return *this;
   }
 
   /**
    * Hold by rooting on the context's runtime.
    */
   bool Hold(JSContext* aCx) {
@@ -72,20 +71,20 @@ public:
       mRt = aRt;
     }
 
     return !!mRt;
   }
 
   /**
    * Manually release, nullifying mVal, and mRt, but returning
-   * the original JS::Value.
+   * the original jsval.
    */
-  JS::Value Release() {
-    JS::Value oldval = mVal;
+  jsval Release() {
+    jsval oldval = mVal;
 
     if (mRt) {
       JS_RemoveValueRootRT(mRt, &mVal); // infallible
       mRt = nullptr;
     }
 
     mVal = JSVAL_NULL;
 
@@ -103,37 +102,37 @@ public:
    * Explicit JSObject* conversion.
    */
   JSObject* ToJSObject() const {
     return mVal.isObject()
          ? &mVal.toObject()
          : nullptr;
   }
 
-  JS::Value* ToJSValPtr() {
+  jsval* ToJSValPtr() {
     return &mVal;
   }
 
   /**
-   * Pretend to be a JS::Value.
+   * Pretend to be a jsval.
    */
-  operator JS::Value() const { return mVal; }
+  operator jsval() const { return mVal; }
 
   nsAutoJSValHolder &operator=(JSObject* aOther) {
     return *this = OBJECT_TO_JSVAL(aOther);
   }
 
-  nsAutoJSValHolder &operator=(JS::Value aOther) {
+  nsAutoJSValHolder &operator=(jsval aOther) {
 #ifdef DEBUG
     if (JSVAL_IS_GCTHING(aOther) && !JSVAL_IS_NULL(aOther)) {
       MOZ_ASSERT(IsHeld(), "Not rooted!");
     }
 #endif
     mVal = aOther;
     return *this;
   }
 
 private:
-  JS::Value mVal;
+  jsval mVal;
   JSRuntime* mRt;
 };
 
 #endif /* __NSAUTOJSVALHOLDER_H__ */
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -2,17 +2,16 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsNSSComponent.h"
 #include "nsNSSIOLayer.h"
 
-#include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Telemetry.h"
 
 #include "prlog.h"
 #include "prnetdb.h"
 #include "nsIPrefService.h"
 #include "nsIClientAuthDialogs.h"
 #include "nsClientAuthRemember.h"
--- a/tools/profiler/PseudoStack.h
+++ b/tools/profiler/PseudoStack.h
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef PROFILER_PSEUDO_STACK_H_
 #define PROFILER_PSEUDO_STACK_H_
 
 #include "mozilla/NullPtr.h"
 #include <stdint.h>
-#include "js/ProfilingStack.h"
+#include "jsfriendapi.h"
 #include <stdlib.h>
 #include <algorithm>
 
 /* we duplicate this code here to avoid header dependencies
  * which make it more difficult to include in other places */
 #if defined(_M_X64) || defined(__x86_64__)
 #define V8_HOST_ARCH_X64 1
 #elif defined(_M_IX86) || defined(__i386__) || defined(__i386)