Bug 1209263 - Allow embedders to tell SpiderMonkey how to structured clone principals; r=bz
authorNick Fitzgerald <fitzgen@gmail.com>
Fri, 02 Oct 2015 16:44:00 +0200
changeset 265960 f8547896d5823f66f61430cdaf5d7104cb241604
parent 265959 9a0c443ed6f3488bfdd67669e5b3cdb767a45e8e
child 265961 b958e25b1ecf218ca7ddb94357e83878ac9c753b
push id66078
push usercbook@mozilla.com
push dateMon, 05 Oct 2015 07:30:10 +0000
treeherdermozilla-inbound@b958e25b1ecf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1209263
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1209263 - Allow embedders to tell SpiderMonkey how to structured clone principals; r=bz
caps/nsJSPrincipals.cpp
caps/nsJSPrincipals.h
dom/base/StructuredCloneHolder.cpp
dom/workers/Principal.cpp
js/public/Principals.h
js/src/jsapi-tests/testCloneScript.cpp
js/src/jsapi-tests/tests.h
js/src/jsapi.cpp
js/src/shell/js.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/SavedFrame.h
--- a/caps/nsJSPrincipals.cpp
+++ b/caps/nsJSPrincipals.cpp
@@ -10,18 +10,23 @@
 #include "nsJSPrincipals.h"
 #include "plstr.h"
 #include "nsXPIDLString.h"
 #include "nsCOMPtr.h"
 #include "nsIServiceManager.h"
 #include "nsMemory.h"
 #include "nsStringBuffer.h"
 
+#include "mozilla/dom/StructuredCloneTags.h"
 // for mozilla::dom::workers::kJSPrincipalsDebugToken
 #include "mozilla/dom/workers/Workers.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::ipc;
 
 NS_IMETHODIMP_(MozExternalRefCountType)
 nsJSPrincipals::AddRef()
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_PRECONDITION(int32_t(refcount) >= 0, "illegal refcnt");
   nsrefcnt count = ++refcount;
   NS_LOG_ADDREF(this, count, "nsJSPrincipals", sizeof(*this));
@@ -80,19 +85,119 @@ nsJSPrincipals::Destroy(JSPrincipals *js
 // Defined here so one can do principals->dump() in the debugger
 JS_EXPORT_API(void)
 JSPrincipals::dump()
 {
     if (debugToken == nsJSPrincipals::DEBUG_TOKEN) {
       nsAutoCString str;
       static_cast<nsJSPrincipals *>(this)->GetScriptLocation(str);
       fprintf(stderr, "nsIPrincipal (%p) = %s\n", static_cast<void*>(this), str.get());
-    } else if (debugToken == mozilla::dom::workers::kJSPrincipalsDebugToken) {
+    } else if (debugToken == dom::workers::kJSPrincipalsDebugToken) {
         fprintf(stderr, "Web Worker principal singleton (%p)\n", this);
     } else {
         fprintf(stderr,
                 "!!! JSPrincipals (%p) is not nsJSPrincipals instance - bad token: "
                 "actual=0x%x expected=0x%x\n",
                 this, unsigned(debugToken), unsigned(nsJSPrincipals::DEBUG_TOKEN));
     }
 }
 
-#endif 
+#endif
+
+/* static */ bool
+nsJSPrincipals::ReadPrincipals(JSContext* aCx, JSStructuredCloneReader* aReader,
+                               JSPrincipals** aOutPrincipals)
+{
+    uint32_t tag;
+    uint32_t unused;
+    if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
+        return false;
+    }
+
+    if (!(tag == SCTAG_DOM_NULL_PRINCIPAL ||
+          tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
+          tag == SCTAG_DOM_CONTENT_PRINCIPAL)) {
+        xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+        return false;
+    }
+
+    return ReadKnownPrincipalType(aCx, aReader, tag, aOutPrincipals);
+}
+
+/* static */ bool
+nsJSPrincipals::ReadKnownPrincipalType(JSContext* aCx,
+                                       JSStructuredCloneReader* aReader,
+                                       uint32_t aTag,
+                                       JSPrincipals** aOutPrincipals)
+{
+    MOZ_ASSERT(aTag == SCTAG_DOM_NULL_PRINCIPAL ||
+               aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
+               aTag == SCTAG_DOM_CONTENT_PRINCIPAL);
+
+    if (NS_WARN_IF(!NS_IsMainThread())) {
+        xpc::Throw(aCx, NS_ERROR_UNCATCHABLE_EXCEPTION);
+        return false;
+    }
+
+    PrincipalInfo info;
+    if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
+        info = SystemPrincipalInfo();
+    } else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
+        info = NullPrincipalInfo();
+    } else {
+        uint32_t suffixLength, specLength;
+        if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
+            return false;
+        }
+
+        nsAutoCString suffix;
+        suffix.SetLength(suffixLength);
+        if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
+            return false;
+        }
+
+        nsAutoCString spec;
+        spec.SetLength(specLength);
+        if (!JS_ReadBytes(aReader, spec.BeginWriting(), specLength)) {
+            return false;
+        }
+
+        OriginAttributes attrs;
+        attrs.PopulateFromSuffix(suffix);
+        info = ContentPrincipalInfo(attrs, spec);
+    }
+
+    nsresult rv;
+    nsCOMPtr<nsIPrincipal> prin = PrincipalInfoToPrincipal(info, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+        xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+        return false;
+    }
+
+    *aOutPrincipals = get(prin.forget().take());
+    return true;
+}
+
+bool
+nsJSPrincipals::write(JSContext* aCx, JSStructuredCloneWriter* aWriter)
+{
+    PrincipalInfo info;
+    if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(this, &info)))) {
+        xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+        return false;
+    }
+
+    if (info.type() == PrincipalInfo::TNullPrincipalInfo) {
+        return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0);
+    }
+    if (info.type() == PrincipalInfo::TSystemPrincipalInfo) {
+        return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
+    }
+
+    MOZ_ASSERT(info.type() == PrincipalInfo::TContentPrincipalInfo);
+    const ContentPrincipalInfo& cInfo = info;
+    nsAutoCString suffix;
+    cInfo.attrs().CreateSuffix(suffix);
+    return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
+           JS_WriteUint32Pair(aWriter, suffix.Length(), cInfo.spec().Length()) &&
+           JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
+           JS_WriteBytes(aWriter, cInfo.spec().get(), cInfo.spec().Length());
+}
--- a/caps/nsJSPrincipals.h
+++ b/caps/nsJSPrincipals.h
@@ -11,16 +11,27 @@
 
 class nsJSPrincipals : public nsIPrincipal, public JSPrincipals
 {
 public:
   /* SpiderMonkey security callbacks. */
   static bool Subsume(JSPrincipals *jsprin, JSPrincipals *other);
   static void Destroy(JSPrincipals *jsprin);
 
+  /* JSReadPrincipalsOp for nsJSPrincipals */
+  static bool ReadPrincipals(JSContext* aCx, JSStructuredCloneReader* aReader,
+                             JSPrincipals** aOutPrincipals);
+
+  static bool ReadKnownPrincipalType(JSContext* aCx,
+                                     JSStructuredCloneReader* aReader,
+                                     uint32_t aTag,
+                                     JSPrincipals** aOutPrincipals);
+
+  bool write(JSContext* aCx, JSStructuredCloneWriter* aWriter) final;
+
   /*
    * Get a weak reference to nsIPrincipal associated with the given JS
    * principal, and vice-versa.
    */
   static nsJSPrincipals* get(JSPrincipals *principals) {
     nsJSPrincipals *self = static_cast<nsJSPrincipals *>(principals);
     MOZ_ASSERT_IF(self, self->debugToken == DEBUG_TOKEN);
     return self;
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -413,59 +413,29 @@ StructuredCloneHolder::ReadFullySerializ
       }
     }
     return result;
   }
 
   if (aTag == SCTAG_DOM_NULL_PRINCIPAL ||
       aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
       aTag == SCTAG_DOM_CONTENT_PRINCIPAL) {
-    if (!NS_IsMainThread()) {
+    JSPrincipals* prin;
+    if (!nsJSPrincipals::ReadKnownPrincipalType(aCx, aReader, aTag, &prin)) {
       return nullptr;
     }
-
-    mozilla::ipc::PrincipalInfo info;
-    if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
-      info = mozilla::ipc::SystemPrincipalInfo();
-    } else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
-      info = mozilla::ipc::NullPrincipalInfo();
-    } else {
-      
-      uint32_t suffixLength, specLength;
-      if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
-        return nullptr;
-      }
-
-      nsAutoCString suffix;
-      suffix.SetLength(suffixLength);
-      if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
-       return nullptr;
-      }
-
-      nsAutoCString spec;
-      spec.SetLength(specLength);
-      if (!JS_ReadBytes(aReader, spec.BeginWriting(), specLength)) {
-        return nullptr;
-      }
-
-      OriginAttributes attrs;
-      attrs.PopulateFromSuffix(suffix);
-      info = mozilla::ipc::ContentPrincipalInfo(attrs, spec);
-    }
-
-    nsresult rv;
-    nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(info, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
-      return nullptr;
-    }
+    // nsJSPrincipals::ReadKnownPrincipalType addrefs for us, but because of the
+    // casting between JSPrincipals* and nsIPrincipal* we can't use
+    // getter_AddRefs above and have to already_AddRefed here.
+    nsCOMPtr<nsIPrincipal> principal = already_AddRefed<nsIPrincipal>(nsJSPrincipals::get(prin));
 
     JS::RootedValue result(aCx);
-    rv = nsContentUtils::WrapNative(aCx, principal, &NS_GET_IID(nsIPrincipal),
-                                    &result);
+    nsresult rv = nsContentUtils::WrapNative(aCx, principal,
+                                             &NS_GET_IID(nsIPrincipal),
+                                             &result);
     if (NS_FAILED(rv)) {
       xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
       return nullptr;
     }
 
     return result.toObjectOrNull();
   }
 
@@ -555,37 +525,18 @@ StructuredCloneHolder::WriteFullySeriali
     }
   }
 #endif
 
   if (NS_IsMainThread() && xpc::IsReflector(aObj)) {
     nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(aObj);
     nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base);
     if (principal) {
-      mozilla::ipc::PrincipalInfo info;
-      if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(principal, &info)))) {
-        xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
-        return false;
-      }
-
-      if (info.type() == mozilla::ipc::PrincipalInfo::TNullPrincipalInfo) {
-        return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0);
-      }
-      if (info.type() == mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
-        return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
-      }
-
-      MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
-      const mozilla::ipc::ContentPrincipalInfo& cInfo = info;
-      nsAutoCString suffix;
-      cInfo.attrs().CreateSuffix(suffix);
-      return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
-             JS_WriteUint32Pair(aWriter, suffix.Length(), cInfo.spec().Length()) &&
-             JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
-             JS_WriteBytes(aWriter, cInfo.spec().get(), cInfo.spec().Length());
+      auto nsjsprincipals = nsJSPrincipals::get(principal);
+      return nsjsprincipals->write(aCx, aWriter);
     }
   }
 
 #ifdef MOZ_NFC
   {
     MozNDEFRecord* ndefRecord;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(MozNDEFRecord, aObj, ndefRecord))) {
       MOZ_ASSERT(NS_IsMainThread());
--- a/dom/workers/Principal.cpp
+++ b/dom/workers/Principal.cpp
@@ -6,20 +6,28 @@
 
 #include "Principal.h"
 
 #include "jsapi.h"
 #include "mozilla/Assertions.h"
 
 BEGIN_WORKERS_NAMESPACE
 
+struct WorkerPrincipal final : public JSPrincipals
+{
+  bool write(JSContext* aCx, JSStructuredCloneWriter* aWriter) override {
+    MOZ_CRASH("WorkerPrincipal::write not implemented");
+    return false;
+  }
+};
+
 JSPrincipals*
 GetWorkerPrincipal()
 {
-  static JSPrincipals sPrincipal;
+  static WorkerPrincipal sPrincipal;
 
   /*
    * To make sure the the principals refcount is initialized to one, atomically
    * increment it on every pass though this function. If we discover this wasn't
    * the first time, decrement it again. This avoids the need for
    * synchronization.
    */
   int32_t prevRefcount = sPrincipal.refcount++;
--- a/js/public/Principals.h
+++ b/js/public/Principals.h
@@ -10,16 +10,18 @@
 #define js_Principals_h
 
 #include "mozilla/Atomics.h"
 
 #include <stdint.h>
 
 #include "jspubtd.h"
 
+#include "js/StructuredClone.h"
+
 namespace js {
     struct PerformanceGroup;
 } // namespace js
 
 struct JSPrincipals {
     /* Don't call "destroy"; use reference counting macros below. */
     mozilla::Atomic<int32_t> refcount;
 
@@ -32,16 +34,22 @@ struct JSPrincipals {
 
     void setDebugToken(uint32_t token) {
 # ifdef JS_DEBUG
         debugToken = token;
 # endif
     }
 
     /*
+     * Write the principals with the given |writer|. Return false on failure,
+     * true on success.
+     */
+    virtual bool write(JSContext* cx, JSStructuredCloneWriter* writer) = 0;
+
+    /*
      * This is not defined by the JS engine but should be provided by the
      * embedding.
      */
     JS_PUBLIC_API(void) dump();
 };
 
 extern JS_PUBLIC_API(void)
 JS_HoldPrincipals(JSPrincipals* principals);
@@ -94,9 +102,31 @@ typedef void
 /*
  * Initialize the callback that is called to destroy JSPrincipals instance
  * when its reference counter drops to zero. The initialization can be done
  * only once per JS runtime.
  */
 extern JS_PUBLIC_API(void)
 JS_InitDestroyPrincipalsCallback(JSRuntime* rt, JSDestroyPrincipalsOp destroyPrincipals);
 
+/*
+ * Read a JSPrincipals instance from the given |reader| and initialize the out
+ * paratemer |outPrincipals| to the JSPrincipals instance read.
+ *
+ * Return false on failure, true on success. The |outPrincipals| parameter
+ * should not be modified if false is returned.
+ *
+ * The caller is not responsible for calling JS_HoldPrincipals on the resulting
+ * JSPrincipals instance, the JSReadPrincipalsOp must increment the refcount of
+ * the resulting JSPrincipals on behalf of the caller.
+ */
+using JSReadPrincipalsOp = bool (*)(JSContext* cx, JSStructuredCloneReader* reader,
+                                    JSPrincipals** outPrincipals);
+
+/*
+ * Initialize the callback that is called to read JSPrincipals instances from a
+ * buffer. The initialization can be done only once per JS runtime.
+ */
+extern JS_PUBLIC_API(void)
+JS_InitReadPrincipalsCallback(JSRuntime* rt, JSReadPrincipalsOp read);
+
+
 #endif  /* js_Principals_h */
--- a/js/src/jsapi-tests/testCloneScript.cpp
+++ b/js/src/jsapi-tests/testCloneScript.cpp
@@ -46,29 +46,28 @@ BEGIN_TEST(test_cloneScript)
         JSAutoCompartment b(cx, B);
         CHECK(JS::CloneFunctionObject(cx, obj));
     }
 
     return true;
 }
 END_TEST(test_cloneScript)
 
-static void
-DestroyPrincipals(JSPrincipals* principals)
-{
-    delete principals;
-}
-
-struct Principals : public JSPrincipals
+struct Principals final : public JSPrincipals
 {
   public:
     Principals()
     {
         refcount = 0;
     }
+
+    bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
+        MOZ_ASSERT(false, "not imlemented");
+        return false;
+    }
 };
 
 class AutoDropPrincipals
 {
     JSRuntime* rt;
     JSPrincipals* principals;
 
   public:
@@ -79,16 +78,23 @@ class AutoDropPrincipals
     }
 
     ~AutoDropPrincipals()
     {
         JS_DropPrincipals(rt, principals);
     }
 };
 
+static void
+DestroyPrincipals(JSPrincipals* principals)
+{
+    auto p = static_cast<Principals*>(principals);
+    delete p;
+}
+
 BEGIN_TEST(test_cloneScriptWithPrincipals)
 {
     JS_InitDestroyPrincipalsCallback(rt, DestroyPrincipals);
 
     JSPrincipals* principalsA = new Principals();
     AutoDropPrincipals dropA(rt, principalsA);
     JSPrincipals* principalsB = new Principals();
     AutoDropPrincipals dropB(rt, principalsB);
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -404,16 +404,21 @@ class TempFile {
 class TestJSPrincipals : public JSPrincipals
 {
   public:
     explicit TestJSPrincipals(int rc = 0)
       : JSPrincipals()
     {
         refcount = rc;
     }
+
+    bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
+        MOZ_ASSERT(false, "not implemented");
+        return false;
+    }
 };
 
 #ifdef JS_GC_ZEAL
 /*
  * Temporarily disable the GC zeal setting. This is only useful in tests that
  * need very explicit GC behavior and should not be used elsewhere.
  */
 class AutoLeaveZeal
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3333,16 +3333,24 @@ JS_SetTrustedPrincipals(JSRuntime* rt, J
 extern JS_PUBLIC_API(void)
 JS_InitDestroyPrincipalsCallback(JSRuntime* rt, JSDestroyPrincipalsOp destroyPrincipals)
 {
     MOZ_ASSERT(destroyPrincipals);
     MOZ_ASSERT(!rt->destroyPrincipals);
     rt->destroyPrincipals = destroyPrincipals;
 }
 
+extern JS_PUBLIC_API(void)
+JS_InitReadPrincipalsCallback(JSRuntime* rt, JSReadPrincipalsOp read)
+{
+    MOZ_ASSERT(read);
+    MOZ_ASSERT(!rt->readPrincipals);
+    rt->readPrincipals = read;
+}
+
 JS_PUBLIC_API(JSFunction*)
 JS_NewFunction(JSContext* cx, JSNative native, unsigned nargs, unsigned flags,
                const char* name)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -209,30 +209,35 @@ NewGlobalObject(JSContext* cx, JS::Compa
  * set bits in P are a superset of those in Q. Thus, the principal 0 is
  * subsumed by everything, and the principal ~0 subsumes everything.
  *
  * As a special case, a null pointer as a principal is treated like 0xffff.
  *
  * The 'newGlobal' function takes an option indicating which principal the
  * new global should have; 'evaluate' does for the new code.
  */
-class ShellPrincipals: public JSPrincipals {
+class ShellPrincipals final : public JSPrincipals {
     uint32_t bits;
 
     static uint32_t getBits(JSPrincipals* p) {
         if (!p)
             return 0xffff;
         return static_cast<ShellPrincipals*>(p)->bits;
     }
 
   public:
     explicit ShellPrincipals(uint32_t bits, int32_t refcount = 0) : bits(bits) {
         this->refcount = refcount;
     }
 
+    bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
+        MOZ_ASSERT(false, "not implemented");
+        return false;
+    }
+
     static void destroy(JSPrincipals* principals) {
         MOZ_ASSERT(principals != &fullyTrusted);
         MOZ_ASSERT(principals->refcount == 0);
         js_delete(static_cast<const ShellPrincipals*>(principals));
     }
 
     static bool subsumes(JSPrincipals* first, JSPrincipals* second) {
         uint32_t firstBits  = getBits(first);
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -191,16 +191,17 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     data(nullptr),
     signalHandlersInstalled_(false),
     canUseSignalHandlers_(false),
     defaultFreeOp_(thisFromCtor()),
     debuggerMutations(0),
     securityCallbacks(const_cast<JSSecurityCallbacks*>(&NullSecurityCallbacks)),
     DOMcallbacks(nullptr),
     destroyPrincipals(nullptr),
+    readPrincipals(nullptr),
     errorReporter(nullptr),
     linkedAsmJSModules(nullptr),
     propertyRemovals(0),
 #if !EXPOSE_INTL_API
     thousandsSeparator(0),
     decimalSeparator(0),
     numGrouping(0),
 #endif
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1153,16 +1153,17 @@ struct JSRuntime : public JS::shadow::Ru
         return &defaultFreeOp_;
     }
 
     uint32_t            debuggerMutations;
 
     const JSSecurityCallbacks* securityCallbacks;
     const js::DOMCallbacks* DOMcallbacks;
     JSDestroyPrincipalsOp destroyPrincipals;
+    JSReadPrincipalsOp readPrincipals;
 
     /* Optional error reporter. */
     JSErrorReporter     errorReporter;
 
     /* AsmJSCache callbacks are runtime-wide. */
     JS::AsmJSCacheOps   asmJSCacheOps;
 
     /* Head of the linked list of linked asm.js modules. */
--- a/js/src/vm/SavedFrame.h
+++ b/js/src/vm/SavedFrame.h
@@ -190,16 +190,21 @@ struct ReconstructedSavedFramePrincipals
 {
     explicit ReconstructedSavedFramePrincipals()
         : JSPrincipals()
     {
         MOZ_ASSERT(is(this));
         this->refcount = 1;
     }
 
+    bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
+        MOZ_ASSERT(false, "ReconstructedSavedFramePrincipals should never be exposed to embedders");
+        return false;
+    }
+
     static ReconstructedSavedFramePrincipals IsSystem;
     static ReconstructedSavedFramePrincipals IsNotSystem;
 
     // Return true if the given JSPrincipals* points to one of the
     // ReconstructedSavedFramePrincipals singletons, false otherwise.
     static bool is(JSPrincipals* p) { return p == &IsSystem || p == &IsNotSystem;}
 
     // Get the appropriate ReconstructedSavedFramePrincipals singleton for the