bug 730221 - delegating serialization of script principals to the embedding. r=:luke,:bz
authorIgor Bukanov <igor@mir2.org>
Mon, 13 Feb 2012 14:10:04 +0100
changeset 93548 46c5015550aff585c8b24e3e83e5472d97a994ae
parent 93546 22af7bc45a186d51c4d940a49bfff7d32aba7ad6
child 93549 1383ac50bcff5ee23ec13e1948635ece0cba1748
push id160
push userlsblakk@mozilla.com
push dateFri, 13 Jul 2012 18:18:57 +0000
treeherdermozilla-release@228ba1a111fc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs730221
milestone14.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 730221 - delegating serialization of script principals to the embedding. r=:luke,:bz Currently to serialize principals stored in JSScript we have a rather complex schema. First there is the transcode callback that the embedding must provide to transcode principals using XDR API. Second we use rather complex glue code to implement that callback in terms of writing/reading nsIObjectOutputStream/ nsIObjectInputStream. This glue code is duplicated in 3 places. All this can be avoided if we simply delegate transcoding of principals to the caller. In addition, at least in the case of the cached startup scripts we do not even need to transcode the principals as the the cached scripts always have the system principal so we can skip all the transcode complexity there. The patch implemnts this idea. In particular, the code in JS engine responsible for transcoding of principals is replaced by the single API function JS_XDRSetPrincipals that the embedding can use to set principals for decoded scripts and functions. Then the startup cache uses this to set the principals for the decoded script to the system principals. The other two places in nsJSContext::Serialize and XBL_SerializeFunction that need to serialize principals together with a function or script now uses common utilities in nsXPConnect so the serialization complexity resides in the single place.
caps/include/nsJSPrincipals.h
caps/include/nsScriptSecurityManager.h
caps/src/nsJSPrincipals.cpp
caps/src/nsScriptSecurityManager.cpp
content/xbl/src/nsXBLSerialize.cpp
dom/base/nsJSEnvironment.cpp
js/src/jsapi-tests/testCloneScript.cpp
js/src/jsapi-tests/testOriginPrincipals.cpp
js/src/jsapi-tests/testXDR.cpp
js/src/jsapi.h
js/src/jsdbgapi.cpp
js/src/jsdbgapi.h
js/src/jsscript.cpp
js/src/jsxdrapi.cpp
js/src/jsxdrapi.h
js/src/shell/js.cpp
js/xpconnect/idl/nsIXPConnect.idl
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/loader/mozJSLoaderUtils.cpp
js/xpconnect/loader/mozJSLoaderUtils.h
js/xpconnect/loader/mozJSSubScriptLoader.cpp
js/xpconnect/src/nsXPConnect.cpp
--- a/caps/include/nsJSPrincipals.h
+++ b/caps/include/nsJSPrincipals.h
@@ -42,17 +42,16 @@
 #include "nsIPrincipal.h"
 
 class nsCString;
 
 struct nsJSPrincipals : nsIPrincipal, JSPrincipals
 {
   static JSBool Subsume(JSPrincipals *jsprin, JSPrincipals *other);
   static void Destroy(JSPrincipals *jsprin);
-  static JSBool Transcode(JSXDRState *xdr, JSPrincipals **jsprinp);
 
   /*
    * Get a weak reference to nsIPrincipal associated with the given JS
    * principal.
    */
   static nsJSPrincipals* get(JSPrincipals *principals) {
     nsJSPrincipals *self = static_cast<nsJSPrincipals *>(principals);
     MOZ_ASSERT_IF(self, self->debugToken == DEBUG_TOKEN);
--- a/caps/include/nsScriptSecurityManager.h
+++ b/caps/include/nsScriptSecurityManager.h
@@ -496,17 +496,17 @@ private:
     // Callers MUST pass in a non-null rv here.
     nsIPrincipal*
     GetFramePrincipal(JSContext* cx, JSStackFrame* fp, nsresult* rv);
 
     // Returns null if a principal cannot be found.  Note that rv can be NS_OK
     // when this happens -- this means that there was no script.  Callers MUST
     // pass in a non-null rv here.
     static nsIPrincipal*
-    GetScriptPrincipal(JSContext* cx, JSScript* script, nsresult* rv);
+    GetScriptPrincipal(JSScript* script, nsresult* rv);
 
     // Returns null if a principal cannot be found.  Note that rv can be NS_OK
     // when this happens -- this means that there was no script associated
     // with the function object, and no global object associated with the scope
     // of obj (the last object on its parent chain).  If the caller is walking
     // the JS stack, fp must point to the current frame in the stack iteration.
     // Callers MUST pass in a non-null rv here.
     static nsIPrincipal*
--- a/caps/src/nsJSPrincipals.cpp
+++ b/caps/src/nsJSPrincipals.cpp
@@ -81,80 +81,16 @@ nsJSPrincipals::Destroy(JSPrincipals *js
     nsjsprin->AddRef();
     nsjsprin->refcount--;
 #else
     nsjsprin->refcount++;
 #endif
     nsjsprin->Release();
 }
 
-/* static */ JSBool
-nsJSPrincipals::Transcode(JSXDRState *xdr, JSPrincipals **jsprinp)
-{
-    nsresult rv;
-
-    if (xdr->mode == JSXDR_ENCODE) {
-        nsIObjectOutputStream *stream =
-            reinterpret_cast<nsIObjectOutputStream*>(xdr->userdata);
-
-        // Flush xdr'ed data to the underlying object output stream.
-        uint32_t size;
-        char *data = (char*) ::JS_XDRMemGetData(xdr, &size);
-
-        rv = stream->Write32(size);
-        if (NS_SUCCEEDED(rv)) {
-            rv = stream->WriteBytes(data, size);
-            if (NS_SUCCEEDED(rv)) {
-                ::JS_XDRMemResetData(xdr);
-
-                rv = stream->WriteObject(nsJSPrincipals::get(*jsprinp), true);
-            }
-        }
-    } else {
-        NS_ASSERTION(JS_XDRMemDataLeft(xdr) == 0, "XDR out of sync?!");
-        nsIObjectInputStream *stream =
-            reinterpret_cast<nsIObjectInputStream*>(xdr->userdata);
-
-        nsCOMPtr<nsIPrincipal> prin;
-        rv = stream->ReadObject(true, getter_AddRefs(prin));
-        if (NS_SUCCEEDED(rv)) {
-            PRUint32 size;
-            rv = stream->Read32(&size);
-            if (NS_SUCCEEDED(rv)) {
-                char *data = nsnull;
-                if (size != 0)
-                    rv = stream->ReadBytes(size, &data);
-                if (NS_SUCCEEDED(rv)) {
-                    char *olddata;
-                    uint32_t oldsize;
-
-                    // Any decode-mode JSXDRState whose userdata points to an
-                    // nsIObjectInputStream instance must use nsMemory to Alloc
-                    // and Free its data buffer.  Swap the new buffer we just
-                    // read for the old, exhausted data.
-                    olddata = (char*) ::JS_XDRMemGetData(xdr, &oldsize);
-                    nsMemory::Free(olddata);
-                    ::JS_XDRMemSetData(xdr, data, size);
-
-                    *jsprinp = nsJSPrincipals::get(prin);
-                    JS_HoldPrincipals(*jsprinp);
-                }
-            }
-        }
-    }
-
-    if (NS_FAILED(rv)) {
-        ::JS_ReportError(xdr->cx, "can't %scode principals (failure code %x)",
-                         (xdr->mode == JSXDR_ENCODE) ? "en" : "de",
-                         (unsigned int) rv);
-        return JS_FALSE;
-    }
-    return JS_TRUE;
-}
-
 #ifdef DEBUG
 
 // Defined here so one can do principals->dump() in the debugger
 JS_EXPORT_API(void)
 JSPrincipals::dump()
 {
     if (debugToken == nsJSPrincipals::DEBUG_TOKEN) {
         static_cast<nsJSPrincipals *>(this)->dumpImpl();
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -2165,27 +2165,26 @@ nsScriptSecurityManager::GetPrincipalFro
     if (globalData)
         NS_IF_ADDREF(*result = globalData->GetPrincipal());
 
     return NS_OK;
 }
 
 // static
 nsIPrincipal*
-nsScriptSecurityManager::GetScriptPrincipal(JSContext *cx,
-                                            JSScript *script,
+nsScriptSecurityManager::GetScriptPrincipal(JSScript *script,
                                             nsresult* rv)
 {
     NS_PRECONDITION(rv, "Null out param");
     *rv = NS_OK;
     if (!script)
     {
         return nsnull;
     }
-    JSPrincipals *jsp = JS_GetScriptPrincipals(cx, script);
+    JSPrincipals *jsp = JS_GetScriptPrincipals(script);
     if (!jsp) {
         *rv = NS_ERROR_FAILURE;
         NS_ERROR("Script compiled without principals!");
         return nsnull;
     }
     return nsJSPrincipals::get(jsp);
 }
 
@@ -2248,31 +2247,31 @@ nsScriptSecurityManager::GetFunctionObje
         // reliable principals compiled into the function itself.
 
         nsIPrincipal *result = doGetObjectPrincipal(obj);
         if (!result)
             *rv = NS_ERROR_FAILURE;
         return result;
     }
 
-    return GetScriptPrincipal(cx, script, rv);
+    return GetScriptPrincipal(script, rv);
 }
 
 nsIPrincipal*
 nsScriptSecurityManager::GetFramePrincipal(JSContext *cx,
                                            JSStackFrame *fp,
                                            nsresult *rv)
 {
     NS_PRECONDITION(rv, "Null out param");
     JSObject *obj = JS_GetFrameFunctionObject(cx, fp);
     if (!obj)
     {
         // Must be in a top-level script. Get principal from the script.
         JSScript *script = JS_GetFrameScript(cx, fp);
-        return GetScriptPrincipal(cx, script, rv);
+        return GetScriptPrincipal(script, rv);
     }
 
     nsIPrincipal* result = GetFunctionObjectPrincipal(cx, obj, fp, rv);
 
 #ifdef DEBUG
     if (NS_SUCCEEDED(*rv) && !result)
     {
         JSFunction *fun = JS_GetObjectFunction(obj);
@@ -3376,17 +3375,16 @@ nsresult nsScriptSecurityManager::Init()
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = runtimeService->GetRuntime(&sRuntime);
     NS_ENSURE_SUCCESS(rv, rv);
 
     static const JSSecurityCallbacks securityCallbacks = {
         CheckObjectAccess,
         nsJSPrincipals::Subsume,
-        nsJSPrincipals::Transcode,
         ObjectPrincipalFinder,
         ContentSecurityPolicyPermitsJSAction
     };
 
     MOZ_ASSERT(!JS_GetSecurityCallbacks(sRuntime));
     JS_SetSecurityCallbacks(sRuntime, &securityCallbacks);
     JS_InitDestroyPrincipalsCallback(sRuntime, nsJSPrincipals::Destroy);
 
--- a/content/xbl/src/nsXBLSerialize.cpp
+++ b/content/xbl/src/nsXBLSerialize.cpp
@@ -33,82 +33,26 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsXBLSerialize.h"
 #include "nsDOMScriptObjectHolder.h"
 #include "nsContentUtils.h"
-#include "jsxdrapi.h"
 
 nsresult
 XBL_SerializeFunction(nsIScriptContext* aContext,
                       nsIObjectOutputStream* aStream,
                       JSObject* aFunctionObject)
 {
   JSContext* cx = aContext->GetNativeContext();
-  JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE);
-  if (!xdr)
-    return NS_ERROR_OUT_OF_MEMORY;
-  xdr->userdata = static_cast<void*>(aStream);
-
-  JSAutoRequest ar(cx);
-  nsresult rv;
-  if (!JS_XDRFunctionObject(xdr, &aFunctionObject)) {
-    rv = NS_ERROR_FAILURE;
-  } else {
-    uint32_t size;
-    const char* data = reinterpret_cast<const char*>
-                                       (JS_XDRMemGetData(xdr, &size));
-    NS_ASSERTION(data, "no decoded JSXDRState data!");
-
-    rv = aStream->Write32(size);
-    if (NS_SUCCEEDED(rv))
-      rv = aStream->WriteBytes(data, size);
-  }
-
-  JS_XDRDestroy(xdr);
-
-  return rv;
+  return nsContentUtils::XPConnect()->WriteFunction(aStream, cx, aFunctionObject);
 }
 
-// static
 nsresult
 XBL_DeserializeFunction(nsIScriptContext* aContext,
                         nsIObjectInputStream* aStream,
-                        JSObject** aFunctionObject)
+                        JSObject** aFunctionObjectp)
 {
-  *aFunctionObject = nsnull;
-
-  PRUint32 size;
-  nsresult rv = aStream->Read32(&size);
-  if (NS_FAILED(rv))
-    return rv;
-
-  char* data;
-  rv = aStream->ReadBytes(size, &data);
-  if (NS_FAILED(rv))
-    return rv;
-
   JSContext* cx = aContext->GetNativeContext();
-  JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
-  if (!xdr) {
-    rv = NS_ERROR_OUT_OF_MEMORY;
-  } else {
-    xdr->userdata = static_cast<void*>(aStream);
-    JSAutoRequest ar(cx);
-    JS_XDRMemSetData(xdr, data, size);
-
-    if (!JS_XDRFunctionObject(xdr, aFunctionObject)) {
-      rv = NS_ERROR_FAILURE;
-    }
-
-    uint32_t junk;
-    data = static_cast<char*>(JS_XDRMemGetData(xdr, &junk));
-    JS_XDRMemSetData(xdr, NULL, 0);
-    JS_XDRDestroy(xdr);
-  }
-
-  nsMemory::Free(data);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return rv;
+  return nsContentUtils::XPConnect()->ReadFunction(aStream, cx, aFunctionObjectp);
 }
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -73,17 +73,16 @@
 #include "nsXPCOMCIDInternal.h"
 #include "nsIXULRuntime.h"
 
 #include "nsDOMClassInfo.h"
 #include "xpcpublic.h"
 
 #include "jsdbgapi.h"           // for JS_ClearWatchPointsForObject
 #include "jswrapper.h"
-#include "jsxdrapi.h"
 #include "nsIArray.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsDOMScriptObjectHolder.h"
 #include "prmem.h"
 #include "WrapperFactory.h"
 #include "nsGlobalWindow.h"
 #include "nsScriptNameSpaceManager.h"
@@ -1960,129 +1959,33 @@ nsJSContext::BindCompiledEventHandler(ns
 
   return rv;
 }
 
 // serialization
 nsresult
 nsJSContext::Serialize(nsIObjectOutputStream* aStream, JSScript* aScriptObject)
 {
-    if (!aScriptObject)
-        return NS_ERROR_FAILURE;
-
-    nsresult rv;
-
-    JSContext* cx = mContext;
-    JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE);
-    if (! xdr)
-        return NS_ERROR_OUT_OF_MEMORY;
-    xdr->userdata = (void*) aStream;
-
-    JSAutoRequest ar(cx);
-    if (! ::JS_XDRScript(xdr, &aScriptObject)) {
-        rv = NS_ERROR_FAILURE;  // likely to be a principals serialization error
-    } else {
-        // Get the encoded JSXDRState data and write it.  The JSXDRState owns
-        // this buffer memory and will free it beneath ::JS_XDRDestroy.
-        //
-        // If an XPCOM object needs to be written in the midst of the JS XDR
-        // encoding process, the C++ code called back from the JS engine (e.g.,
-        // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
-        // from the JSXDRState to aStream, then write the object, then return
-        // to JS XDR code with xdr reset so new JS data is encoded at the front
-        // of the xdr's data buffer.
-        //
-        // However many XPCOM objects are interleaved with JS XDR data in the
-        // stream, when control returns here from ::JS_XDRScript, we'll have
-        // one last buffer of data to write to aStream.
-
-        uint32_t size;
-        const char* data = reinterpret_cast<const char*>
-                                           (::JS_XDRMemGetData(xdr, &size));
-        NS_ASSERTION(data, "no decoded JSXDRState data!");
-
-        rv = aStream->Write32(size);
-        if (NS_SUCCEEDED(rv))
-            rv = aStream->WriteBytes(data, size);
-    }
-
-    ::JS_XDRDestroy(xdr);
-    if (NS_FAILED(rv)) return rv;
-
-    return rv;
+  if (!aScriptObject)
+    return NS_ERROR_FAILURE;
+
+  return nsContentUtils::XPConnect()->WriteScript(aStream, mContext, aScriptObject);
 }
 
 nsresult
 nsJSContext::Deserialize(nsIObjectInputStream* aStream,
                          nsScriptObjectHolder<JSScript>& aResult)
 {
-    NS_TIME_FUNCTION_MIN(1.0);
-
-    PRUint32 size;
-    nsresult rv = aStream->Read32(&size);
-    if (NS_FAILED(rv)) return rv;
-
-    char* data;
-    rv = aStream->ReadBytes(size, &data);
-    if (NS_FAILED(rv)) return rv;
-
-    JSContext* cx = mContext;
-
-    JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_DECODE);
-    JSScript *result = nsnull;
-    if (! xdr) {
-        rv = NS_ERROR_OUT_OF_MEMORY;
-    } else {
-        xdr->userdata = (void*) aStream;
-        JSAutoRequest ar(cx);
-        ::JS_XDRMemSetData(xdr, data, size);
-
-        if (! ::JS_XDRScript(xdr, &result)) {
-            rv = NS_ERROR_FAILURE;  // principals deserialization error?
-        }
-
-        // Update data in case ::JS_XDRScript called back into C++ code to
-        // read an XPCOM object.
-        //
-        // In that case, the serialization process must have flushed a run
-        // of counted bytes containing JS data at the point where the XPCOM
-        // object starts, after which an encoding C++ callback from the JS
-        // XDR code must have written the XPCOM object directly into the
-        // nsIObjectOutputStream.
-        //
-        // The deserialization process will XDR-decode counted bytes up to
-        // but not including the XPCOM object, then call back into C++ to
-        // read the object, then read more counted bytes and hand them off
-        // to the JSXDRState, so more JS data can be decoded.
-        //
-        // This interleaving of JS XDR data and XPCOM object data may occur
-        // several times beneath the call to ::JS_XDRScript, above.  At the
-        // end of the day, we need to free (via nsMemory) the data owned by
-        // the JSXDRState.  So we steal it back, nulling xdr's buffer so it
-        // doesn't get passed to ::JS_free by ::JS_XDRDestroy.
-
-        uint32_t junk;
-        data = (char*) ::JS_XDRMemGetData(xdr, &junk);
-        if (data)
-            ::JS_XDRMemSetData(xdr, NULL, 0);
-        ::JS_XDRDestroy(xdr);
-    }
-
-    // If data is null now, it must have been freed while deserializing an
-    // XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
-    if (data)
-        nsMemory::Free(data);
-    NS_ASSERTION(aResult.getScriptTypeID()==JAVASCRIPT,
-                 "Expecting JS script object holder");
-
-    // Now that we've cleaned up, handle the case when rv is a failure
-    // code, which could happen for all sorts of reasons above.
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return aResult.set(result);
+  NS_TIME_FUNCTION_MIN(1.0);
+  
+  JSScript *script;
+  nsresult rv = nsContentUtils::XPConnect()->ReadScript(aStream, mContext, &script);
+  if (NS_FAILED(rv)) return rv;
+    
+  return aResult.set(script);
 }
 
 nsIScriptGlobalObject *
 nsJSContext::GetGlobalObject()
 {
   JSObject *global = ::JS_GetGlobalObject(mContext);
 
   if (!global) {
--- a/js/src/jsapi-tests/testCloneScript.cpp
+++ b/js/src/jsapi-tests/testCloneScript.cpp
@@ -112,17 +112,17 @@ BEGIN_TEST(test_cloneScriptWithPrincipal
         JSFunction *fun;
         CHECK(fun = JS_CompileFunctionForPrincipals(cx, A, principalsA, "f",
                                                     mozilla::ArrayLength(argnames), argnames,
                                                     source, strlen(source), __FILE__, 1));
 
         JSScript *script;
         CHECK(script = JS_GetFunctionScript(cx, fun));
 
-        CHECK(JS_GetScriptPrincipals(cx, script) == principalsA);
+        CHECK(JS_GetScriptPrincipals(script) == principalsA);
         CHECK(obj = JS_GetFunctionObject(fun));
     }
 
     // Clone into B
     {
         JSAutoEnterCompartment b;
         if (!b.enter(cx, B))
             return false;
@@ -131,25 +131,25 @@ BEGIN_TEST(test_cloneScriptWithPrincipal
         CHECK(cloned = JS_CloneFunctionObject(cx, obj, B));
 
         JSFunction *fun;
         CHECK(fun = JS_ValueToFunction(cx, JS::ObjectValue(*cloned)));
 
         JSScript *script;
         CHECK(script = JS_GetFunctionScript(cx, fun));
 
-        CHECK(JS_GetScriptPrincipals(cx, script) == principalsB);
+        CHECK(JS_GetScriptPrincipals(script) == principalsB);
 
         JS::Value v;
         JS::Value args[] = { JS::Int32Value(1) };
         CHECK(JS_CallFunctionValue(cx, B, JS::ObjectValue(*cloned), 1, args, &v));
         CHECK(v.isObject());
 
         JSObject *funobj = &v.toObject();
         CHECK(JS_ObjectIsFunction(cx, funobj));
         CHECK(fun = JS_ValueToFunction(cx, v));
         CHECK(script = JS_GetFunctionScript(cx, fun));
-        CHECK(JS_GetScriptPrincipals(cx, script) == principalsB);
+        CHECK(JS_GetScriptPrincipals(script) == principalsB);
     }
 
     return true;
 }
 END_TEST(test_cloneScriptWithPrincipals)
--- a/js/src/jsapi-tests/testOriginPrincipals.cpp
+++ b/js/src/jsapi-tests/testOriginPrincipals.cpp
@@ -8,17 +8,16 @@ JSPrincipals *
 ObjectPrincipalsFinder(JSObject *)
 {
     return sCurrentGlobalPrincipals;
 }
 
 static const JSSecurityCallbacks seccb = {
     NULL,
     NULL,
-    NULL,
     ObjectPrincipalsFinder,
     NULL
 };
 
 JSPrincipals *sOriginPrincipalsInErrorReporter = NULL;
 
 static void
 ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
@@ -96,18 +95,18 @@ bool
 testInner(const char *asciiChars, JSPrincipals *principal, JSPrincipals *originPrincipal)
 {
     sCurrentGlobalPrincipals = principal;
 
     jsval rval;
     CHECK(eval(asciiChars, principal, originPrincipal, &rval));
 
     JSScript *script = JS_GetFunctionScript(cx, JSVAL_TO_OBJECT(rval)->toFunction());
-    CHECK(JS_GetScriptPrincipals(cx, script) == principal);
-    CHECK(JS_GetScriptOriginPrincipals(cx, script) == originPrincipal);
+    CHECK(JS_GetScriptPrincipals(script) == principal);
+    CHECK(JS_GetScriptOriginPrincipals(script) == originPrincipal);
 
     return true;
 }
 
 bool
 testError(const char *asciiChars)
 {
     jsval rval;
--- a/js/src/jsapi-tests/testXDR.cpp
+++ b/js/src/jsapi-tests/testXDR.cpp
@@ -49,109 +49,87 @@ FreezeThawImpl(JSContext *cx, T *thing, 
     }
     JS_XDRDestroy(w);
     if (!memory)
         return NULL;
 
     // thaw
     JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
     JS_XDRMemSetData(r, memory, nbytes);
+
+    JSScript *script = GetScript(cx, thing);
+    JS_XDRSetPrincipals(r, script->principals, script->originPrincipals);
     if (!xdrAction(r, &thing))
         thing = NULL;
     JS_XDRDestroy(r);  // this frees `memory
     return thing;
 }
 
 static JSScript *
+GetScript(JSContext *cx, JSScript *script)
+{
+    return script;
+}
+
+static JSScript *
+GetScript(JSContext *cx, JSObject *funobj)
+{
+    return JS_GetFunctionScript(cx, JS_GetObjectFunction(funobj));
+}
+
+static JSScript *
 FreezeThaw(JSContext *cx, JSScript *script)
 {
     return FreezeThawImpl(cx, script, JS_XDRScript);
 }
 
 static JSObject *
 FreezeThaw(JSContext *cx, JSObject *funobj)
 {
     return FreezeThawImpl(cx, funobj, JS_XDRFunctionObject);
 }
 
 static JSPrincipals testPrincipals[] = {
     { 1 },
     { 1 },
 };
 
-static JSBool
-TranscodePrincipals(JSXDRState *xdr, JSPrincipals **principalsp)
-{
-    uint32_t index;
-    if (xdr->mode == JSXDR_ENCODE) {
-        JSPrincipals *p = *principalsp;
-        for (index = 0; ; ++index) {
-            if (index == mozilla::ArrayLength(testPrincipals))
-                return false;
-            if (p == &testPrincipals[index])
-                break;
-        }
-    }
-
-    if (!JS_XDRUint32(xdr, &index))
-        return false;
-
-    if (xdr->mode == JSXDR_DECODE) {
-        if (index >= mozilla::ArrayLength(testPrincipals))
-            return false;
-        *principalsp = &testPrincipals[index];
-        JS_HoldPrincipals(*principalsp);
-    }
-
-    return true;
-}
-
 BEGIN_TEST(testXDR_principals)
 {
-    static const JSSecurityCallbacks seccb = {
-        NULL,
-        NULL,
-        TranscodePrincipals,
-        NULL,
-        NULL
-    };
-
-    JS_SetSecurityCallbacks(rt, &seccb);
-
     JSScript *script;
     for (int i = TEST_FIRST; i != TEST_END; ++i) {
         script = createScriptViaXDR(NULL, NULL, i);
         CHECK(script);
-        CHECK(!JS_GetScriptPrincipals(cx, script));
-        CHECK(!JS_GetScriptOriginPrincipals(cx, script));
+        CHECK(!JS_GetScriptPrincipals(script));
+        CHECK(!JS_GetScriptOriginPrincipals(script));
     
         script = createScriptViaXDR(NULL, NULL, i);
         CHECK(script);
-        CHECK(!JS_GetScriptPrincipals(cx, script));
-        CHECK(!JS_GetScriptOriginPrincipals(cx, script));
+        CHECK(!JS_GetScriptPrincipals(script));
+        CHECK(!JS_GetScriptOriginPrincipals(script));
         
         script = createScriptViaXDR(&testPrincipals[0], NULL, i);
         CHECK(script);
-        CHECK(JS_GetScriptPrincipals(cx, script) == &testPrincipals[0]);
-        CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[0]);
+        CHECK(JS_GetScriptPrincipals(script) == &testPrincipals[0]);
+        CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipals[0]);
         
         script = createScriptViaXDR(&testPrincipals[0], &testPrincipals[0], i);
         CHECK(script);
-        CHECK(JS_GetScriptPrincipals(cx, script) == &testPrincipals[0]);
-        CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[0]);
+        CHECK(JS_GetScriptPrincipals(script) == &testPrincipals[0]);
+        CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipals[0]);
         
         script = createScriptViaXDR(&testPrincipals[0], &testPrincipals[1], i);
         CHECK(script);
-        CHECK(JS_GetScriptPrincipals(cx, script) == &testPrincipals[0]);
-        CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[1]);
+        CHECK(JS_GetScriptPrincipals(script) == &testPrincipals[0]);
+        CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipals[1]);
         
         script = createScriptViaXDR(NULL, &testPrincipals[1], i);
         CHECK(script);
-        CHECK(!JS_GetScriptPrincipals(cx, script));
-        CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[1]);
+        CHECK(!JS_GetScriptPrincipals(script));
+        CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipals[1]);
     }
 
     return true;
 }
 
 enum TestCase {
     TEST_FIRST,
     TEST_SCRIPT = TEST_FIRST,
@@ -185,17 +163,17 @@ JSScript *createScriptViaXDR(JSPrincipal
     if (!ok || !v.isObject())
         return NULL;
     JSObject *funobj = &v.toObject();
     if (testCase == TEST_FUNCTION) {
         funobj = FreezeThaw(cx, funobj);
         if (!funobj)
             return NULL;
     }
-    return JS_GetFunctionScript(cx, JS_GetObjectFunction(funobj));
+    return GetScript(cx, funobj);
 }
 
 END_TEST(testXDR_principals)
 
 BEGIN_TEST(testXDR_atline)
 {
     JS_ToggleOptions(cx, JSOPTION_ATLINE);
     CHECK(JS_GetOptions(cx) & JSOPTION_ATLINE);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1526,26 +1526,16 @@ typedef JSBool
 
 typedef void
 (* JSDestroyPrincipalsOp)(JSPrincipals *principals);
 
 typedef JSBool
 (* JSSubsumePrincipalsOp)(JSPrincipals *principals1, JSPrincipals *principals2);
 
 /*
- * XDR-encode or -decode a principals instance, based on whether xdr->mode is
- * JSXDR_ENCODE, in which case *principalsp should be encoded; or JSXDR_DECODE,
- * in which case implementations must return a held (via JSPRINCIPALS_HOLD),
- * non-null *principalsp out parameter.  Return true on success, false on any
- * error, which the implementation must have reported.
- */
-typedef JSBool
-(* JSPrincipalsTranscoder)(JSXDRState *xdr, JSPrincipals **principalsp);
-
-/*
  * Return a weak reference to the principals associated with obj, possibly via
  * the immutable parent chain leading from obj to a top-level container (e.g.,
  * a window object in the DOM level 0).  If there are no principals associated
  * with obj, return null.  Therefore null does not mean an error was reported;
  * in no event should an error be reported or an exception be thrown by this
  * callback's implementation.
  */
 typedef JSPrincipals *
@@ -4122,17 +4112,16 @@ extern JS_PUBLIC_API(void)
 JS_HoldPrincipals(JSPrincipals *principals);
 
 extern JS_PUBLIC_API(void)
 JS_DropPrincipals(JSRuntime *rt, JSPrincipals *principals);
 
 struct JSSecurityCallbacks {
     JSCheckAccessOp            checkObjectAccess;
     JSSubsumePrincipalsOp      subsumePrincipals;
-    JSPrincipalsTranscoder     principalsTranscoder;
     JSObjectPrincipalsFinder   findObjectPrincipals;
     JSCSPEvalChecker           contentSecurityPolicyAllows;
 };
 
 extern JS_PUBLIC_API(void)
 JS_SetSecurityCallbacks(JSRuntime *rt, const JSSecurityCallbacks *callbacks);
 
 extern JS_PUBLIC_API(const JSSecurityCallbacks *)
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -477,23 +477,23 @@ JS_GetFunctionScript(JSContext *cx, JSFu
 
 JS_PUBLIC_API(JSNative)
 JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
 {
     return fun->maybeNative();
 }
 
 JS_PUBLIC_API(JSPrincipals *)
-JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
+JS_GetScriptPrincipals(JSScript *script)
 {
     return script->principals;
 }
 
 JS_PUBLIC_API(JSPrincipals *)
-JS_GetScriptOriginPrincipals(JSContext *cx, JSScript *script)
+JS_GetScriptOriginPrincipals(JSScript *script)
 {
     return script->originPrincipals;
 }
 
 /************************************************************************/
 
 /*
  *  Stack Frame Iterator
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -215,20 +215,20 @@ JS_ReleaseFunctionLocalNameArray(JSConte
 
 extern JS_PUBLIC_API(JSScript *)
 JS_GetFunctionScript(JSContext *cx, JSFunction *fun);
 
 extern JS_PUBLIC_API(JSNative)
 JS_GetFunctionNative(JSContext *cx, JSFunction *fun);
 
 extern JS_PUBLIC_API(JSPrincipals *)
-JS_GetScriptPrincipals(JSContext *cx, JSScript *script);
+JS_GetScriptPrincipals(JSScript *script);
 
 extern JS_PUBLIC_API(JSPrincipals *)
-JS_GetScriptOriginPrincipals(JSContext *cx, JSScript *script);
+JS_GetScriptOriginPrincipals(JSScript *script);
 
 /*
  * Stack Frame Iterator
  *
  * Used to iterate through the JS stack frames to extract
  * information from the frames.
  */
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -522,17 +522,17 @@ XDRScript(JSXDRState *xdr, JSScript **sc
         length = script->length;
     if (!JS_XDRUint32(xdr, &length))
         return JS_FALSE;
 
     if (xdr->mode == JSXDR_ENCODE) {
         prologLength = script->mainOffset;
         JS_ASSERT(script->getVersion() != JSVERSION_UNKNOWN);
         version = (uint32_t)script->getVersion() | (script->nfixed << 16);
-        lineno = (uint32_t)script->lineno;
+        lineno = script->lineno;
         nslots = (uint32_t)script->nslots;
         nslots = (uint32_t)((script->staticLevel << 16) | script->nslots);
         natoms = script->natoms;
 
         notes = script->notes();
         nsrcnotes = script->numNotes();
 
         if (JSScript::isValidOffset(script->objectsOffset))
@@ -660,47 +660,44 @@ XDRScript(JSXDRState *xdr, JSScript **sc
             filename = const_cast<char *>(script->filename);
         if (!JS_XDRCString(xdr, &filename))
             return false;
         if (xdr->mode == JSXDR_DECODE) {
             script->filename = SaveScriptFilename(xdr->cx, filename);
             Foreground::free_(filename);
             if (!script->filename)
                 return false;
-            if (!xdr->sharedFilename)
-                xdr->sharedFilename = script->filename;
         }
+        if (!xdr->sharedFilename)
+            xdr->sharedFilename = script->filename;
     } else if (scriptBits & (1 << SharedFilename)) {
         JS_ASSERT(xdr->sharedFilename);
         if (xdr->mode == JSXDR_DECODE)
             script->filename = xdr->sharedFilename;
     }
 
     if (xdr->mode == JSXDR_DECODE) {
+        script->lineno = lineno;
+        script->nslots = uint16_t(nslots);
+        script->staticLevel = uint16_t(nslots >> 16);
+
+        /* The origin principals must be normalized at this point. */ 
+        JS_ASSERT_IF(xdr->principals, xdr->originPrincipals);
         JS_ASSERT(!script->principals);
         JS_ASSERT(!script->originPrincipals);
-
-        /* The origin principals must be normalized at this point. */ 
-        JS_ASSERT_IF(script->principals, script->originPrincipals);
         if (xdr->principals) {
             script->principals = xdr->principals;
             JS_HoldPrincipals(xdr->principals);
         }
         if (xdr->originPrincipals) {
             script->originPrincipals = xdr->originPrincipals;
             JS_HoldPrincipals(xdr->originPrincipals);
         }
     }
 
-    if (xdr->mode == JSXDR_DECODE) {
-        script->lineno = (unsigned)lineno;
-        script->nslots = uint16_t(nslots);
-        script->staticLevel = uint16_t(nslots >> 16);
-    }
-
     for (i = 0; i != natoms; ++i) {
         if (!js_XDRAtom(xdr, &script->atoms[i]))
             return false;
     }
 
     /*
      * Here looping from 0-to-length to xdr objects is essential. It ensures
      * that block objects from the script->objects array will be written and
@@ -1706,19 +1703,17 @@ CloneScript(JSContext *cx, JSScript *scr
 
     /*
      * Hand p off from w to r.  Don't want them to share the data mem, lest
      * they both try to free it in JS_XDRDestroy.
      */
     JS_XDRMemSetData(r, p, nbytes);
     JS_XDRMemSetData(w, NULL, 0);
 
-    r->principals = cx->compartment->principals;
-    r->originPrincipals = JSScript::normalizeOriginPrincipals(cx->compartment->principals,
-                                                              script->originPrincipals);
+    JS_XDRSetPrincipals(r, cx->compartment->principals, script->originPrincipals);
     JSScript *newScript = NULL;
     if (!XDRScript(r, &newScript))
         return NULL;
 
     return newScript;
 }
 
 }  /* namespace js */
--- a/js/src/jsxdrapi.cpp
+++ b/js/src/jsxdrapi.cpp
@@ -231,17 +231,16 @@ static JSXDROps xdrmem_ops = {
     mem_raw,        mem_seek,       mem_tell,       mem_finalize
 };
 
 JS_PUBLIC_API(void)
 JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx)
 {
     xdr->mode = mode;
     xdr->cx = cx;
-    xdr->userdata = NULL;
     xdr->sharedFilename = NULL;
     xdr->principals = NULL;
     xdr->originPrincipals = NULL;
 }
 
 JS_PUBLIC_API(JSXDRState *)
 JS_XDRNewMem(JSContext *cx, JSXDRMode mode)
 {
@@ -302,16 +301,24 @@ JS_XDRMemResetData(JSXDRState *xdr)
 JS_PUBLIC_API(void)
 JS_XDRDestroy(JSXDRState *xdr)
 {
     JSContext *cx = xdr->cx;
     xdr->ops->finalize(xdr);
     cx->free_(xdr);
 }
 
+JS_PUBLIC_API(void)
+JS_XDRSetPrincipals(JSXDRState *xdr, JSPrincipals *principals, JSPrincipals *originPrincipals)
+{
+    JS_ASSERT(xdr->mode == JSXDR_DECODE);
+    xdr->principals = principals;
+    xdr->originPrincipals = JSScript::normalizeOriginPrincipals(principals, originPrincipals);
+}
+
 JS_PUBLIC_API(JSBool)
 JS_XDRUint8(JSXDRState *xdr, uint8_t *b)
 {
     uint32_t l = *b;
     if (!JS_XDRUint32(xdr, &l))
         return JS_FALSE;
     *b = (uint8_t) l;
     return JS_TRUE;
@@ -527,105 +534,20 @@ js_XDRAtom(JSXDRState *xdr, JSAtom **ato
         cx->free_(chars);
 
     if (!atom)
         return JS_FALSE;
     *atomp = atom;
     return JS_TRUE;
 }
 
-static bool
-XDRPrincipals(JSXDRState *xdr)
-{
-    const uint8_t HAS_PRINCIPALS   = 1;
-    const uint8_t HAS_ORIGIN       = 2;
-
-    uint8_t flags = 0;
-    if (xdr->mode == JSXDR_ENCODE) {
-        if (xdr->principals)
-            flags |= HAS_PRINCIPALS;
-
-        /*
-         * For the common case when principals == originPrincipals we want to
-         * avoid serializing the same principal twice. As originPrincipals are
-         * normalized and principals imply originPrincipals we simply set
-         * HAS_ORIGIN only if originPrincipals is set and different from
-         * principals. During decoding we re-normalize originPrincipals.
-         */
-        JS_ASSERT_IF(xdr->principals, xdr->originPrincipals);
-        if (xdr->originPrincipals && xdr->originPrincipals != xdr->principals)
-            flags |= HAS_ORIGIN;
-    }
-
-    if (!JS_XDRUint8(xdr, &flags))
-        return false;
-
-    if (flags & (HAS_PRINCIPALS | HAS_ORIGIN)) {
-        const JSSecurityCallbacks *scb = JS_GetSecurityCallbacks(xdr->cx->runtime);
-        if (xdr->mode == JSXDR_DECODE) {
-            if (!scb || !scb->principalsTranscoder) {
-                JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
-                                     JSMSG_CANT_DECODE_PRINCIPALS);
-                return false;
-            }
-        } else {
-            JS_ASSERT(scb);
-            JS_ASSERT(scb->principalsTranscoder);
-        }
-
-        if (flags & HAS_PRINCIPALS) {
-            if (!scb->principalsTranscoder(xdr, &xdr->principals))
-                return false;
-        }
-
-        if (flags & HAS_ORIGIN) {
-            if (!scb->principalsTranscoder(xdr, &xdr->originPrincipals))
-                return false;
-        } else if (xdr->mode == JSXDR_DECODE && xdr->principals) {
-            xdr->originPrincipals = xdr->principals;
-            JS_HoldPrincipals(xdr->principals);
-        }
-    }
-
-    return true;
-}
-
-namespace {
-
-struct AutoDropXDRPrincipals {
-    JSXDRState *const xdr;
-
-    AutoDropXDRPrincipals(JSXDRState *xdr)
-      : xdr(xdr) { }
-
-    ~AutoDropXDRPrincipals() {
-        if (xdr->mode == JSXDR_DECODE) {
-            if (xdr->principals)
-                JS_DropPrincipals(xdr->cx->runtime, xdr->principals);
-            if (xdr->originPrincipals)
-                JS_DropPrincipals(xdr->cx->runtime, xdr->originPrincipals);
-        }
-        xdr->principals = NULL;
-        xdr->originPrincipals = NULL;
-    }
-};
-
-} /* namespace anonymous */
-
 JS_PUBLIC_API(JSBool)
 JS_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
 {
-    AutoDropXDRPrincipals drop(xdr);
-    if (xdr->mode == JSXDR_ENCODE) {
-        JSScript *script = (*objp)->toFunction()->script();
-        xdr->principals = script->principals;
-        xdr->originPrincipals = script->originPrincipals;
-    }
-
-    return XDRPrincipals(xdr) && XDRFunctionObject(xdr, objp);
+    return XDRFunctionObject(xdr, objp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_XDRScript(JSXDRState *xdr, JSScript **scriptp)
 {
     JSScript *script;
     uint32_t magic;
     uint32_t bytecodeVer;
@@ -645,26 +567,18 @@ JS_XDRScript(JSXDRState *xdr, JSScript *
 
     if (magic != JSXDR_MAGIC_SCRIPT_CURRENT ||
         bytecodeVer != JSXDR_BYTECODE_VERSION) {
         /* We do not provide binary compatibility with older scripts. */
         JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, JSMSG_BAD_SCRIPT_MAGIC);
         return false;
     }
 
-    {
-        AutoDropXDRPrincipals drop(xdr);
-        if (xdr->mode == JSXDR_ENCODE) {
-            xdr->principals = script->principals;
-            xdr->originPrincipals = script->originPrincipals;
-        }
-        
-        if (!XDRPrincipals(xdr) || !XDRScript(xdr, &script))
-            return false;
-    }
+    if (!XDRScript(xdr, &script))
+        return false;
 
     if (xdr->mode == JSXDR_DECODE) {
         JS_ASSERT(!script->compileAndGo);
         script->globalObject = GetCurrentGlobal(xdr->cx);
         js_CallNewScriptHook(xdr->cx, script, NULL);
         Debugger::onNewScript(xdr->cx, script, NULL);
         *scriptp = script;
     }
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -104,17 +104,16 @@ typedef struct JSXDROps {
     uint32_t    (*tell)(JSXDRState *);
     void        (*finalize)(JSXDRState *);
 } JSXDROps;
 
 struct JSXDRState {
     JSXDRMode   mode;
     JSXDROps    *ops;
     JSContext   *cx;
-    void        *userdata;
     const char  *sharedFilename;
     JSPrincipals *principals;
     JSPrincipals *originPrincipals;
 };
 
 extern JS_PUBLIC_API(void)
 JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx);
 
@@ -131,16 +130,26 @@ extern JS_PUBLIC_API(uint32_t)
 JS_XDRMemDataLeft(JSXDRState *xdr);
 
 extern JS_PUBLIC_API(void)
 JS_XDRMemResetData(JSXDRState *xdr);
 
 extern JS_PUBLIC_API(void)
 JS_XDRDestroy(JSXDRState *xdr);
 
+/*
+ * Set principals that should be assigned to decoded scripts and functions.
+ * The principals is not held via JS_HoldPrincipals/JS_DropPrincipals unless
+ * they are stored in a decoded script. Thus the caller must either ensure
+ * that the principals outlive the XDR instance or are explicitly set to NULL
+ * before they release by the caller.
+ */
+extern JS_PUBLIC_API(void)
+JS_XDRSetPrincipals(JSXDRState *xdr, JSPrincipals *principals, JSPrincipals *originPrincipals);
+
 extern JS_PUBLIC_API(JSBool)
 JS_XDRUint8(JSXDRState *xdr, uint8_t *b);
 
 extern JS_PUBLIC_API(JSBool)
 JS_XDRUint16(JSXDRState *xdr, uint16_t *s);
 
 extern JS_PUBLIC_API(JSBool)
 JS_XDRUint32(JSXDRState *xdr, uint32_t *lp);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4854,17 +4854,16 @@ CheckObjectAccess(JSContext *cx, JSObjec
 {
     return true;
 }
 
 JSSecurityCallbacks securityCallbacks = {
     CheckObjectAccess,
     NULL,
     NULL,
-    NULL,
     NULL
 };
 
 int
 main(int argc, char **argv, char **envp)
 {
     int stackDummy;
     JSRuntime *rt;
--- a/js/xpconnect/idl/nsIXPConnect.idl
+++ b/js/xpconnect/idl/nsIXPConnect.idl
@@ -45,16 +45,18 @@
 #include "nsIClassInfo.idl"
 #include "xpccomponents.idl"
 #include "xpcjsid.idl"
 #include "xpcexception.idl"
 #include "nsIInterfaceInfo.idl"
 #include "nsIInterfaceInfoManager.idl"
 #include "nsIExceptionService.idl"
 #include "nsIVariant.idl"
+#include "nsIObjectOutputStream.idl"
+#include "nsIObjectInputStream.idl"
 
 %{ C++
 #include "jspubtd.h"
 #include "xptinfo.h"
 #include "nsAXPCNativeCallContext.h"
 
 class nsWrapperCache;
 %}
@@ -65,16 +67,17 @@ class nsWrapperCache;
 
 [ptr] native JSContextPtr(JSContext);
 [ptr] native JSClassPtr(JSClass);
 [ptr] native JSObjectPtr(JSObject);
 [ptr] native JSValPtr(jsval);
 [ptr] native JSValConstPtr(const jsval);
       native JSPropertyOp(JSPropertyOp);
       native JSEqualityOp(JSEqualityOp);
+[ptr] native JSScriptPtr(JSScript);
 [ptr] native voidPtrPtr(void*);
 [ptr] native nsScriptObjectTracerPtr(nsScriptObjectTracer);
 [ref] native nsCCTraversalCallbackRef(nsCycleCollectionTraversalCallback);
 [ptr] native nsAXPCNativeCallContextPtr(nsAXPCNativeCallContext);
 [ptr] native nsWrapperCachePtr(nsWrapperCache);
 
 /***************************************************************************/
 
@@ -400,17 +403,17 @@ interface nsIXPCFunctionThisTranslator :
 
 enum nsGCType {
     nsGCNormal,
     nsGCShrinking,
     nsGCIncremental
 };
 %}
 
-[uuid(0213cb40-2dd5-4ac8-a9d3-157bd53c3824)]
+[uuid(08ad2253-3ed6-40ed-beec-6472f2d8dc7f)]
 interface nsIXPConnect : nsISupports
 {
 %{ C++
   NS_DEFINE_STATIC_CID_ACCESSOR(NS_XPCONNECT_CID)
 %}
 
     /**
      * Initializes classes on a global object that has already been created.
@@ -796,9 +799,23 @@ interface nsIXPConnect : nsISupports
      * When we place the browser in JS debug mode, there can't be any
      * JS on the stack. This is because we currently activate debugMode 
      * on all scripts in the JSRuntime when the debugger is activated.
      * This method will turn debug mode on or off when the context 
      * stack reaches zero length.
      */
     [noscript] void setDebugModeWhenPossible(in boolean mode,
                                              in boolean allowSyncDisable);
+
+    [noscript] void writeScript(in nsIObjectOutputStream aStream,
+                                in JSContextPtr aJSContext,
+                                in JSScriptPtr aJSScript);
+
+    [noscript] JSScriptPtr readScript(in nsIObjectInputStream aStream,
+                                      in JSContextPtr aJSContext);
+
+    [noscript] void writeFunction(in nsIObjectOutputStream aStream,
+                                  in JSContextPtr aJSContext,
+                                  in JSObjectPtr aJSObject);
+
+    [noscript] JSObjectPtr readFunction(in nsIObjectInputStream aStream,
+                                        in JSContextPtr aJSContext);
 };
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -746,17 +746,17 @@ mozJSComponentLoader::GlobalForLocation(
     bool writeToCache = false;
     StartupCache* cache = StartupCache::GetSingleton();
 
     nsCAutoString cachePath(kJSCachePrefix);
     rv = PathifyURI(aURI, cachePath);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (cache) {
-        rv = ReadCachedScript(cache, cachePath, cx, &script);
+        rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
         if (NS_SUCCEEDED(rv)) {
             LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
         } else {
             // This is ok, it just means the script is not yet in the
             // cache. Could mean that the cache was corrupted and got removed,
             // but either way we're going to write this out.
             writeToCache = true;
         }
@@ -929,17 +929,17 @@ mozJSComponentLoader::GlobalForLocation(
 
 #ifdef DEBUG_shaver_off
     fprintf(stderr, "mJCL: compiled JS component %s\n",
             nativePath.get());
 #endif
 
     if (writeToCache) {
         // We successfully compiled the script, so cache it.
-        rv = WriteCachedScript(cache, cachePath, cx, script);
+        rv = WriteCachedScript(cache, cachePath, cx, mSystemPrincipal, script);
 
         // Don't treat failure to write as fatal, since we might be working
         // with a read-only cache.
         if (NS_SUCCEEDED(rv)) {
             LOG(("Successfully wrote to cache\n"));
         } else {
             LOG(("Failed to write to cache\n"));
         }
--- a/js/xpconnect/loader/mozJSLoaderUtils.cpp
+++ b/js/xpconnect/loader/mozJSLoaderUtils.cpp
@@ -34,158 +34,75 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsAutoPtr.h"
 #include "nsScriptLoader.h"
 
 #include "jsapi.h"
+#include "jsdbgapi.h"
 #include "jsxdrapi.h"
 
+#include "nsJSPrincipals.h"
+
 #include "mozilla/scache/StartupCache.h"
 #include "mozilla/scache/StartupCacheUtils.h"
 
 using namespace mozilla::scache;
 
-static nsresult
-ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream,
-                     JSScript **script)
+// We only serialize scripts with system principals. So we don't serialize the
+// principals when writing a script. Instead, when reading it back, we set the
+// principals to the system principals.
+nsresult
+ReadCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx,
+                 nsIPrincipal *systemPrincipal, JSScript **script)
 {
-    *script = nsnull;
-
-    PRUint32 size;
-    nsresult rv = stream->Read32(&size);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    char *data;
-    rv = stream->ReadBytes(size, &data);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
-    NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
-
-    xdr->userdata = stream;
-    JS_XDRMemSetData(xdr, data, size);
-
-    if (!JS_XDRScript(xdr, script)) {
-        rv = NS_ERROR_FAILURE;
-    }
-
-    // Update data in case ::JS_XDRScript called back into C++ code to
-    // read an XPCOM object.
-    //
-    // In that case, the serialization process must have flushed a run
-    // of counted bytes containing JS data at the point where the XPCOM
-    // object starts, after which an encoding C++ callback from the JS
-    // XDR code must have written the XPCOM object directly into the
-    // nsIObjectOutputStream.
-    //
-    // The deserialization process will XDR-decode counted bytes up to
-    // but not including the XPCOM object, then call back into C++ to
-    // read the object, then read more counted bytes and hand them off
-    // to the JSXDRState, so more JS data can be decoded.
-    //
-    // This interleaving of JS XDR data and XPCOM object data may occur
-    // several times beneath the call to ::JS_XDRScript, above.  At the
-    // end of the day, we need to free (via nsMemory) the data owned by
-    // the JSXDRState.  So we steal it back, nulling xdr's buffer so it
-    // doesn't get passed to ::JS_free by ::JS_XDRDestroy.
-
-    uint32_t length;
-    data = static_cast<char*>(JS_XDRMemGetData(xdr, &length));
-    JS_XDRMemSetData(xdr, nsnull, 0);
-    JS_XDRDestroy(xdr);
-
-    // If data is null now, it must have been freed while deserializing an
-    // XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
-    nsMemory::Free(data);
-
-    return rv;
-}
-
-static nsresult
-WriteScriptToStream(JSContext *cx, JSScript *script,
-                    nsIObjectOutputStream *stream)
-{
-    JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
-    NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
-
-    xdr->userdata = stream;
-    nsresult rv = NS_OK;
-
-    if (JS_XDRScript(xdr, &script)) {
-        // Get the encoded JSXDRState data and write it.  The JSXDRState owns
-        // this buffer memory and will free it beneath ::JS_XDRDestroy.
-        //
-        // If an XPCOM object needs to be written in the midst of the JS XDR
-        // encoding process, the C++ code called back from the JS engine (e.g.,
-        // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
-        // from the JSXDRState to aStream, then write the object, then return
-        // to JS XDR code with xdr reset so new JS data is encoded at the front
-        // of the xdr's data buffer.
-        //
-        // However many XPCOM objects are interleaved with JS XDR data in the
-        // stream, when control returns here from ::JS_XDRScript, we'll have
-        // one last buffer of data to write to aStream.
-
-        uint32_t size;
-        const char* data = reinterpret_cast<const char*>
-                                           (JS_XDRMemGetData(xdr, &size));
-        NS_ASSERTION(data, "no decoded JSXDRState data!");
-
-        rv = stream->Write32(size);
-        if (NS_SUCCEEDED(rv)) {
-            rv = stream->WriteBytes(data, size);
-        }
-    } else {
-        rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
-    }
-
-    JS_XDRDestroy(xdr);
-    return rv;
-}
-
-nsresult
-ReadCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx, JSScript **script)
-{
-    nsresult rv;
-
     nsAutoArrayPtr<char> buf;
     PRUint32 len;
-    rv = cache->GetBuffer(PromiseFlatCString(uri).get(), getter_Transfers(buf),
-                          &len);
+    nsresult rv = cache->GetBuffer(PromiseFlatCString(uri).get(),
+                                   getter_Transfers(buf), &len);
     if (NS_FAILED(rv)) {
         return rv; // don't warn since NOT_AVAILABLE is an ok error
     }
 
-    nsCOMPtr<nsIObjectInputStream> ois;
-    rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois));
-    NS_ENSURE_SUCCESS(rv, rv);
-    buf.forget();
+    JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_DECODE);
+    if (!xdr) {
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    ::JS_XDRMemSetData(xdr, buf, len);
+    ::JS_XDRSetPrincipals(xdr, nsJSPrincipals::get(systemPrincipal), nsnull);
 
-    return ReadScriptFromStream(cx, ois, script);
+    JSBool ok = ::JS_XDRScript(xdr, script);
+    
+    // Prevent XDR from automatically freeing the buffer.
+    ::JS_XDRMemSetData(xdr, NULL, 0);
+    ::JS_XDRDestroy(xdr);
+
+    return ok ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 nsresult
-WriteCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx, JSScript *script)
+WriteCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx,
+                  nsIPrincipal *systemPrincipal, JSScript *script)
 {
-    nsresult rv;
+    MOZ_ASSERT(JS_GetScriptPrincipals(script) == nsJSPrincipals::get(systemPrincipal));
+    MOZ_ASSERT(JS_GetScriptOriginPrincipals(script) == nsJSPrincipals::get(systemPrincipal));
 
-    nsCOMPtr<nsIObjectOutputStream> oos;
-    nsCOMPtr<nsIStorageStream> storageStream;
-    rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(oos),
-                                             getter_AddRefs(storageStream),
-                                             true);
-    NS_ENSURE_SUCCESS(rv, rv);
+    JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE);
+    if (!xdr) {
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
 
-    rv = WriteScriptToStream(cx, script, oos);
-    oos->Close();
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsresult rv;
+    if (!::JS_XDRScript(xdr, &script)) {
+        rv = NS_ERROR_OUT_OF_MEMORY;
+    } else {
+        uint32_t size;
+        char* data = static_cast<char *>(::JS_XDRMemGetData(xdr, &size));
+        MOZ_ASSERT(size);
+        rv = cache->PutBuffer(PromiseFlatCString(uri).get(), data, size);
+    }
 
-    nsAutoArrayPtr<char> buf;
-    PRUint32 len;
-    rv = NewBufferFromStorageStream(storageStream, getter_Transfers(buf), &len);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = cache->PutBuffer(PromiseFlatCString(uri).get(), buf, len);
+    ::JS_XDRDestroy(xdr);
     return rv;
 }
--- a/js/xpconnect/loader/mozJSLoaderUtils.h
+++ b/js/xpconnect/loader/mozJSLoaderUtils.h
@@ -47,14 +47,17 @@ class nsIURI;
 namespace mozilla {
 namespace scache {
 class StartupCache;
 }
 }
 
 nsresult
 ReadCachedScript(mozilla::scache::StartupCache* cache, nsACString &uri,
-                 JSContext *cx, JSScript **script);
+                 JSContext *cx, nsIPrincipal *systemPrincipal,
+                 JSScript **script);
 
 nsresult
 WriteCachedScript(mozilla::scache::StartupCache* cache, nsACString &uri,
-                  JSContext *cx, JSScript *script);
+                  JSContext *cx, nsIPrincipal *systemPrincipal,
+                  JSScript *script);
+
 #endif /* mozJSLoaderUtils_h */
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -324,17 +324,17 @@ mozJSSubScriptLoader::LoadSubScript(cons
     bool writeScript = false;
     JSVersion version = JS_GetVersion(cx);
     nsCAutoString cachePath;
     cachePath.AppendPrintf("jssubloader/%d", version);
     PathifyURI(uri, cachePath);
 
     script = nsnull;
     if (cache)
-        rv = ReadCachedScript(cache, cachePath, cx, &script);
+        rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
     if (!script) {
         rv = ReadScript(uri, cx, targetObj, charset,
                         static_cast<const char*>(uriStr.get()), serv,
                         principal, &script);
         writeScript = true;
     }
 
     if (NS_FAILED(rv) || !script)
@@ -344,13 +344,13 @@ mozJSSubScriptLoader::LoadSubScript(cons
 
     if (ok) {
         JSAutoEnterCompartment rac;
         if (!rac.enter(cx, result_obj) || !JS_WrapValue(cx, retval))
             return NS_ERROR_UNEXPECTED;
     }
 
     if (cache && ok && writeScript) {
-        WriteCachedScript(cache, cachePath, cx, script);
+        WriteCachedScript(cache, cachePath, cx, mSystemPrincipal, script);
     }
 
     return NS_OK;
 }
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -48,16 +48,17 @@
 
 #include "xpcprivate.h"
 #include "XPCWrapper.h"
 #include "nsBaseHashtable.h"
 #include "nsHashKeys.h"
 #include "jsatom.h"
 #include "jsfriendapi.h"
 #include "jsgc.h"
+#include "jsxdrapi.h"
 #include "dom_quickstubs.h"
 #include "nsNullPrincipal.h"
 #include "nsIURI.h"
 #include "nsJSEnvironment.h"
 #include "nsThreadUtils.h"
 
 #include "XrayWrapper.h"
 #include "WrapperFactory.h"
@@ -503,17 +504,17 @@ struct NoteWeakMapsTracer : public js::W
     {
         JS_TracerInit(&mChildTracer, rt, TraceWeakMappingChild);
     }
     nsCycleCollectionTraversalCallback &mCb;
     NoteWeakMapChildrenTracer mChildTracer;
 };
 
 static void
-TraceWeakMapping(js::WeakMapTracer *trc, JSObject *m, 
+TraceWeakMapping(js::WeakMapTracer *trc, JSObject *m,
                  void *k, JSGCTraceKind kkind,
                  void *v, JSGCTraceKind vkind)
 {
     MOZ_ASSERT(trc->callback == TraceWeakMapping);
     NoteWeakMapsTracer *tracer = static_cast<NoteWeakMapsTracer *>(trc);
     if (vkind == JSTRACE_STRING)
         return;
     if (!xpc_IsGrayGCThing(v) && !tracer->mCb.WantAllTraces())
@@ -590,17 +591,17 @@ nsXPConnect::BeginCycleCollection(nsCycl
         JS_TracerInit(&trc, mCycleCollectionContext->GetJSContext(), NoteJSRoot);
         JS_TraceRuntime(&trc);
     }
 #else
     NS_ASSERTION(!explainLiveExpectedGarbage, "Didn't call nsXPConnect::Collect()?");
 #endif
 
     GetRuntime()->AddXPConnectRoots(cb);
- 
+
     NoteWeakMapsTracer trc(GetRuntime()->GetJSRuntime(), TraceWeakMapping, cb);
     js::TraceWeakMaps(&trc);
 
     return NS_OK;
 }
 
 bool
 nsXPConnect::NotifyLeaveMainThread()
@@ -2789,16 +2790,183 @@ nsXPConnect::NotifyDidPaint()
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     JSContext *cx = ccx.GetJSContext();
 
     js::NotifyDidPaint(cx);
     return NS_OK;
 }
 
+const PRUint8 HAS_PRINCIPALS_FLAG               = 1;
+const PRUint8 HAS_ORIGIN_PRINCIPALS_FLAG        = 2;
+
+static nsresult
+WriteScriptOrFunction(nsIObjectOutputStream *stream, JSContext *cx,
+                      JSScript *script, JSObject *functionObj)
+{
+    // Exactly one of script or functionObj must be given
+    MOZ_ASSERT(!script != !functionObj);
+
+    if (!script)
+        script = JS_GetFunctionScript(cx, JS_GetObjectFunction(functionObj));
+
+    nsIPrincipal *principal =
+        nsJSPrincipals::get(JS_GetScriptPrincipals(script));
+    nsIPrincipal *originPrincipal =
+        nsJSPrincipals::get(JS_GetScriptOriginPrincipals(script));
+
+    PRUint8 flags = 0;
+    if (principal)
+        flags |= HAS_PRINCIPALS_FLAG;
+
+    // Optimize for the common case when originPrincipals == principals. As
+    // originPrincipals is set to principals when the former is null we can
+    // simply skip the originPrincipals when they are the same as principals.
+    if (originPrincipal && originPrincipal != principal)
+        flags |= HAS_ORIGIN_PRINCIPALS_FLAG;
+
+    nsresult rv = stream->Write8(flags);
+    if (NS_FAILED(rv))
+        return rv;
+
+    if (flags & HAS_PRINCIPALS_FLAG) {
+        rv = stream->WriteObject(principal, true);
+        if (NS_FAILED(rv))
+            return rv;
+    }
+
+    if (flags & HAS_ORIGIN_PRINCIPALS_FLAG) {
+        rv = stream->WriteObject(originPrincipal, true);
+        if (NS_FAILED(rv))
+            return rv;
+    }
+
+    JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
+    if (!xdr)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    JSBool ok;
+    {
+        JSAutoRequest ar(cx);
+        if (functionObj)
+            ok = JS_XDRFunctionObject(xdr, &functionObj);
+        else
+            ok = JS_XDRScript(xdr, &script);
+    }
+
+    if (!ok) {
+        rv = NS_ERROR_OUT_OF_MEMORY;
+    } else {
+        // Get the encoded JSXDRState data and write it.  The JSXDRState owns
+        // this buffer memory and will free it beneath JS_XDRDestroy.
+        uint32_t size;
+        const char* data = reinterpret_cast<const char*>(::JS_XDRMemGetData(xdr, &size));
+        NS_ASSERTION(data, "no decoded JSXDRState data!");
+
+        rv = stream->Write32(size);
+        if (NS_SUCCEEDED(rv))
+            rv = stream->WriteBytes(data, size);
+    }
+
+    JS_XDRDestroy(xdr);
+
+    return rv;
+}
+
+static nsresult
+ReadScriptOrFunction(nsIObjectInputStream *stream, JSContext *cx,
+                     JSScript **scriptp, JSObject **functionObjp)
+{
+    // Exactly one of script or functionObj must be given
+    MOZ_ASSERT(!scriptp != !functionObjp);
+
+    PRUint8 flags;
+    nsresult rv = stream->Read8(&flags);
+    if (NS_FAILED(rv))
+        return rv;
+
+    nsJSPrincipals* principal = nsnull;
+    nsCOMPtr<nsIPrincipal> readPrincipal;
+    if (flags & HAS_PRINCIPALS_FLAG) {
+        rv = stream->ReadObject(true, getter_AddRefs(readPrincipal));
+        if (NS_FAILED(rv))
+            return rv;
+        principal = nsJSPrincipals::get(readPrincipal);
+    }
+
+    nsJSPrincipals* originPrincipal = nsnull;
+    nsCOMPtr<nsIPrincipal> readOriginPrincipal;
+    if (flags & HAS_ORIGIN_PRINCIPALS_FLAG) {
+        rv = stream->ReadObject(true, getter_AddRefs(readOriginPrincipal));
+        if (NS_FAILED(rv))
+            return rv;
+        originPrincipal = nsJSPrincipals::get(readOriginPrincipal);
+    }
+
+    PRUint32 size;
+    rv = stream->Read32(&size);
+    if (NS_FAILED(rv))
+        return rv;
+
+    char* data;
+    rv = stream->ReadBytes(size, &data);
+    if (NS_FAILED(rv))
+        return rv;
+
+    JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
+    if (!xdr) {
+        nsMemory::Free(data);
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    JS_XDRMemSetData(xdr, data, size);
+    JS_XDRSetPrincipals(xdr, principal, originPrincipal);
+
+    JSBool ok;
+    {
+        JSAutoRequest ar(cx);
+        if (scriptp)
+            ok = JS_XDRScript(xdr, scriptp);
+        else
+            ok = JS_XDRFunctionObject(xdr, functionObjp);
+    }
+
+    // We cannot rely on XDR automatically freeing the data memory as we must
+    // use nsMemory::Free to release it.
+    JS_XDRMemSetData(xdr, NULL, 0);
+    JS_XDRDestroy(xdr);
+    nsMemory::Free(data);
+
+    return ok ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsXPConnect::WriteScript(nsIObjectOutputStream *stream, JSContext *cx, JSScript *script)
+{
+    return WriteScriptOrFunction(stream, cx, script, nsnull);
+}
+
+NS_IMETHODIMP
+nsXPConnect::ReadScript(nsIObjectInputStream *stream, JSContext *cx, JSScript **scriptp)
+{
+    return ReadScriptOrFunction(stream, cx, scriptp, nsnull);
+}
+
+NS_IMETHODIMP
+nsXPConnect::WriteFunction(nsIObjectOutputStream *stream, JSContext *cx, JSObject *functionObj)
+{
+    return WriteScriptOrFunction(stream, cx, nsnull, functionObj);
+}
+
+NS_IMETHODIMP
+nsXPConnect::ReadFunction(nsIObjectInputStream *stream, JSContext *cx, JSObject **functionObjp)
+{
+    return ReadScriptOrFunction(stream, cx, nsnull, functionObjp);
+}
+
 /* These are here to be callable from a debugger */
 JS_BEGIN_EXTERN_C
 JS_EXPORT_API(void) DumpJSStack()
 {
     nsresult rv;
     nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
     if (NS_SUCCEEDED(rv) && xpc)
         xpc->DebugDumpJSStack(true, true, false);