bug 580128 - Fix evalInSandbox. r=mrbkap
authorPeter Van der Beken <peterv@propagandism.org>
Sun, 10 Oct 2010 15:46:07 -0700
changeset 55675 6bc49789fb1fffed3631d63fe16c547e00405081
parent 55674 8b36385279e20da7aead78dfe7b6506dbd84c22d
child 55676 74bcde77a13d2b995f180137163fcc1f3645ceeb
push idunknown
push userunknown
push dateunknown
reviewersmrbkap
bugs580128
milestone2.0b8pre
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 580128 - Fix evalInSandbox. r=mrbkap
content/xbl/src/nsXBLDocumentInfo.cpp
content/xul/document/src/nsXULPrototypeDocument.cpp
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpccomponents.cpp
js/src/xpconnect/src/xpcjsruntime.cpp
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcpublic.h
js/src/xpconnect/src/xpcthreadcontext.cpp
js/src/xpconnect/tests/chrome/test_evalInSandbox.xul
js/src/xpconnect/tests/mochitest/file_evalInSandbox.html
js/src/xpconnect/wrappers/WrapperFactory.cpp
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -327,17 +327,17 @@ nsXBLDocGlobalObject::EnsureScriptEnviro
   JS_SetErrorReporter(cx, XBL_ProtoErrorReporter);
 
   nsIPrincipal *principal = GetPrincipal();
   nsCString origin;
   JSCompartment *compartment;
 
   principal->GetOrigin(getter_Copies(origin));
   rv = xpc_CreateGlobalObject(cx, &gSharedGlobalClass, origin, principal,
-                              &mJSObject, &compartment);
+                              false, &mJSObject, &compartment);
   NS_ENSURE_SUCCESS(rv, nsnull);
 
   ::JS_SetGlobalObject(cx, mJSObject);
 
   // Add an owning reference from JS back to us. This'll be
   // released when the JSObject is finalized.
   ::JS_SetPrivate(cx, mJSObject, this);
   NS_ADDREF(this);
--- a/content/xul/document/src/nsXULPrototypeDocument.cpp
+++ b/content/xul/document/src/nsXULPrototypeDocument.cpp
@@ -733,17 +733,17 @@ nsXULPDGlobalObject::EnsureScriptEnviron
 
       nsIPrincipal *principal = GetPrincipal();
       nsCString origin;
       JSObject *newGlob;
       JSCompartment *compartment;
 
       principal->GetOrigin(getter_Copies(origin));
       rv = xpc_CreateGlobalObject(cx, &gSharedGlobalClass, origin, principal,
-                                  &newGlob, &compartment);
+                                  false, &newGlob, &compartment);
       NS_ENSURE_SUCCESS(rv, nsnull);
 
       ::JS_SetGlobalObject(cx, newGlob);
 
       // Add an owning reference from JS back to us. This'll be
       // released when the JSObject is finalized.
       ::JS_SetPrivate(cx, newGlob, this);
       NS_ADDREF(this);
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -937,17 +937,18 @@ static JSClass xpcTempGlobalClass = {
     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
     JS_EnumerateStub, TempGlobalResolve, JS_ConvertStub,   nsnull,
     JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 nsresult
 xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp,
                        const nsACString &origin, nsIPrincipal *principal,
-                       JSObject **global, JSCompartment **compartment)
+                       bool preferXrays, JSObject **global,
+                       JSCompartment **compartment)
 {
     XPCCompartmentMap& map = nsXPConnect::GetRuntimeInstance()->GetCompartmentMap();
     nsCAutoString local_origin(origin);
     if(local_origin.EqualsLiteral("file://") && principal)
     {
         nsCOMPtr<nsIURI> uri;
         principal->GetURI(getter_AddRefs(uri));
         uri->GetSpec(local_origin);
@@ -966,17 +967,19 @@ xpc_CreateGlobalObject(JSContext *cx, JS
         if(!tempGlobal)
             return UnexpectedFailure(NS_ERROR_FAILURE);
 
         *global = tempGlobal;
         *compartment = tempGlobal->getCompartment();
 
         js::SwitchToCompartment sc(cx, *compartment);
 
-        JS_SetCompartmentPrivate(cx, *compartment, ToNewCString(local_origin));
+        xpc::CompartmentPrivate *priv =
+            new xpc::CompartmentPrivate(ToNewCString(local_origin), preferXrays);
+        JS_SetCompartmentPrivate(cx, *compartment, priv);
         map.Put(local_origin, *compartment);
     }
     else
     {
         js::SwitchToCompartment sc(cx, *compartment);
 
 #ifdef DEBUG
         if(principal)
@@ -1034,17 +1037,18 @@ nsXPConnect::InitClassesWithNewWrappedGl
         aPrincipal->GetOrigin(getter_Copies(origin));
     else
         origin = aOrigin;
 
     JSCompartment* compartment;
     JSObject* tempGlobal;
 
     nsresult rv = xpc_CreateGlobalObject(ccx, &xpcTempGlobalClass, origin,
-                                         aPrincipal, &tempGlobal, &compartment);
+                                         aPrincipal, false, &tempGlobal,
+                                         &compartment);
     NS_ENSURE_SUCCESS(rv, rv);
 
     JSAutoEnterCompartment ac;
     if(!ac.enter(ccx, tempGlobal))
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     PRBool system = (aFlags & nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT) != 0;
     if(system && !JS_MakeSystemObject(aJSContext, tempGlobal))
@@ -1897,17 +1901,17 @@ nsXPConnect::CreateSandbox(JSContext *cx
     if(!ccx.IsValid())
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     *_retval = nsnull;
 
     jsval rval = JSVAL_VOID;
     AUTO_MARK_JSVAL(ccx, &rval);
 
-    nsresult rv = xpc_CreateSandboxObject(cx, &rval, principal);
+    nsresult rv = xpc_CreateSandboxObject(cx, &rval, principal, NULL, false);
     NS_ASSERTION(NS_FAILED(rv) || !JSVAL_IS_PRIMITIVE(rval),
                  "Bad return value from xpc_CreateSandboxObject()!");
 
     if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(rval)) {
         *_retval = XPCJSObjectHolder::newHolder(ccx, JSVAL_TO_OBJECT(rval));
         NS_ENSURE_TRUE(*_retval, NS_ERROR_OUT_OF_MEMORY);
 
         NS_ADDREF(*_retval);
--- a/js/src/xpconnect/src/xpccomponents.cpp
+++ b/js/src/xpconnect/src/xpccomponents.cpp
@@ -45,16 +45,19 @@
 #include "xpcprivate.h"
 #include "nsReadableUtils.h"
 #include "xpcIJSModuleLoader.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIDOMWindow.h"
 #include "xpcJSWeakReference.h"
 #include "XPCWrapper.h"
 #include "jsproxy.h"
+#include "WrapperFactory.h"
+#include "XrayWrapper.h"
+#include "nsNullPrincipal.h"
 
 #ifdef MOZ_JSLOADER
 #include "mozJSComponentLoader.h"
 #endif
 
 /***************************************************************************/
 // stuff used by all
 
@@ -3158,17 +3161,18 @@ NS_IMPL_THREADSAFE_RELEASE(nsXPCComponen
 #define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents_utils_Sandbox"
 #define                             XPC_MAP_WANT_CALL
 #define                             XPC_MAP_WANT_CONSTRUCT
 #define XPC_MAP_FLAGS               0
 #include "xpc_map_end.h" /* This #undef's the above. */
 
 #ifndef XPCONNECT_STANDALONE
 nsresult
-xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop)
+xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop, JSObject *proto,
+                        bool bypassXray)
 {
     // Create the sandbox global object
     nsresult rv;
     nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
     if(NS_FAILED(rv))
         return NS_ERROR_XPC_UNEXPECTED;
 
     nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(prinOrSop));
@@ -3197,30 +3201,57 @@ xpc_CreateSandboxObject(JSContext * cx, 
 
     nsIPrincipal *principal = sop->GetPrincipal();
     nsAdoptingCString principalorigin;
     principal->GetOrigin(getter_Copies(principalorigin));
 
     nsCAutoString origin("sandbox:");
     origin.Append(principalorigin);
 
+    nsRefPtr<nsNullPrincipal> nullPrincipal = new nsNullPrincipal();
+    rv = nullPrincipal->Init();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = nullPrincipal->GetOrigin(getter_Copies(principalorigin));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    origin.Append(principalorigin);
+
     JSCompartment *compartment;
     JSObject *sandbox;
 
-    rv = xpc_CreateGlobalObject(cx, &SandboxClass, origin, principal, &sandbox,
-                                &compartment);
+    rv = xpc_CreateGlobalObject(cx, &SandboxClass, origin, principal,
+                                !bypassXray, &sandbox, &compartment);
     NS_ENSURE_SUCCESS(rv, rv);
 
     js::AutoObjectRooter tvr(cx, sandbox);
 
     {
         JSAutoEnterCompartment ac;
         if (!ac.enter(cx, sandbox))
             return NS_ERROR_XPC_UNEXPECTED;
 
+        if (proto) {
+            bool ok = JS_WrapObject(cx, &proto);
+            if (!ok)
+                return NS_ERROR_XPC_UNEXPECTED;
+
+            if (xpc::WrapperFactory::IsXrayWrapper(proto) && bypassXray) {
+                jsval v;
+                if (!JS_GetProperty(cx, proto, "wrappedJSObject", &v))
+                    return NS_ERROR_XPC_UNEXPECTED;
+
+                proto = JSVAL_TO_OBJECT(v);
+            }
+
+            ok = JS_SetPrototype(cx, sandbox, proto);
+            if (!ok)
+                return NS_ERROR_XPC_UNEXPECTED;
+        }
+
         // Pass on ownership of sop to |sandbox|.
         if (!JS_SetPrivate(cx, sandbox, sop.forget().get())) {
             return NS_ERROR_XPC_UNEXPECTED;
         }
 
         rv = xpc->InitClasses(cx, sandbox);
         if (NS_SUCCEEDED(rv) &&
             !JS_DefineFunctions(cx, sandbox, SandboxFunctions)) {
@@ -3340,17 +3371,33 @@ nsXPCComponents_utils_Sandbox::CallOrCon
                 }
             }
         }
 
         if (!prinOrSop)
             return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
     }
 
-    rv = xpc_CreateSandboxObject(cx, vp, prinOrSop);
+    JSObject *proto = nsnull;
+    bool bypassXray = false;
+    if (argc > 1) {
+        if (!JSVAL_IS_OBJECT(argv[1]))
+            return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
+
+        proto = JSVAL_TO_OBJECT(argv[1]);
+
+        if (argc > 2) {
+            if (!JSVAL_IS_BOOLEAN(argv[2]))
+                return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
+
+            bypassXray = JSVAL_TO_BOOLEAN(argv[2]);
+        }
+    }
+
+    rv = xpc_CreateSandboxObject(cx, vp, prinOrSop, proto, bypassXray);
 
     if (NS_FAILED(rv)) {
         return ThrowAndFail(rv, cx, _retval);
     }
 
     *_retval = PR_TRUE;
 
     return rv;
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -242,18 +242,23 @@ CompartmentCallback(JSContext *cx, JSCom
     if(op == JSCOMPARTMENT_NEW)
         return JS_TRUE;
 
     XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
     if(!self)
         return JS_TRUE;
 
     XPCCompartmentMap& map = self->GetCompartmentMap();
+    nsAutoPtr<xpc::CompartmentPrivate> priv(
+        static_cast<xpc::CompartmentPrivate*>(JS_SetCompartmentPrivate(cx, compartment, nsnull)));
+    if (!priv)
+        return JS_TRUE;
+
     nsAdoptingCString origin;
-    origin.Adopt(static_cast<char *>(JS_SetCompartmentPrivate(cx, compartment, nsnull)));
+    origin.Adopt(static_cast<char *>(priv->origin));
 
 #ifdef DEBUG
     {
         JSCompartment *current;
         NS_ASSERTION(map.Get(origin, &current), "no compartment?");
         NS_ASSERTION(current == compartment, "compartment mismatch");
     }
 #endif
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -54,16 +54,17 @@
 #include "jsdhash.h"
 #include "jsprf.h"
 #include "prprf.h"
 #include "jsinterp.h"
 #include "jscntxt.h"
 #include "jsdbgapi.h"
 #include "jsgc.h"
 #include "jscompartment.h"
+#include "xpcpublic.h"
 #include "nscore.h"
 #include "nsXPCOM.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCycleCollector.h"
 #include "nsISupports.h"
 #include "nsIServiceManager.h"
 #include "nsIClassInfoImpl.h"
@@ -3922,21 +3923,16 @@ extern JSBool
 xpc_DumpEvalInJSStackFrame(JSContext* cx, JSUint32 frameno, const char* text);
 
 extern JSBool
 xpc_DumpJSObject(JSObject* obj);
 
 extern JSBool
 xpc_InstallJSDebuggerKeywordHandler(JSRuntime* rt);
 
-nsresult
-xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp,
-                       const nsACString &origin, nsIPrincipal *principal,
-                       JSObject **global, JSCompartment **compartment);
-
 /***************************************************************************/
 
 // Definition of nsScriptError, defined here because we lack a place to put
 // XPCOM objects associated with the JavaScript engine.
 class nsScriptError : public nsIScriptError {
 public:
     nsScriptError();
 
@@ -4378,17 +4374,18 @@ xpc_GetGlobalForObject(JSObject *obj)
 // system using xpc_EvalInSandbox(). Takes the JSContext on which to
 // do setup etc on, puts the sandbox object in *vp (which must be
 // rooted by the caller), and uses the principal that's either
 // directly passed in prinOrSop or indirectly as an
 // nsIScriptObjectPrincipal holding the principal. If no principal is
 // reachable through prinOrSop, a new null principal will be created
 // and used.
 nsresult
-xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop);
+xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop,
+                        JSObject *proto, bool preferXray);
 
 // Helper for evaluating scripts in a sandbox object created with
 // xpc_CreateSandboxObject(). The caller is responsible of ensuring
 // that *rval doesn't get collected during the call or usage after the
 // call. This helper will use filename and lineNo for error reporting,
 // and if no filename is provided it will use the codebase from the
 // principal and line number 1 as a fallback. if returnStringOnly is
 // true, then the result in *rval, or the exception in cx->exception
@@ -4422,16 +4419,31 @@ xpc_NewSystemInheritingJSObject(JSContex
 inline JSBool
 xpc_SameScope(XPCWrappedNativeScope *objectscope,
               XPCWrappedNativeScope *xpcscope,
               JSBool *sameOrigin);
 
 nsISupports *
 XPC_GetIdentityObject(JSContext *cx, JSObject *obj);
 
+namespace xpc {
+
+struct CompartmentPrivate
+{
+  CompartmentPrivate(char *origin, bool preferXrays)
+    : origin(origin),
+      preferXrays(preferXrays)
+  {
+  }
+  char *origin;
+  bool preferXrays;
+};
+
+}
+
 #ifdef XPC_IDISPATCH_SUPPORT
 
 #ifdef WINCE
 /* defined static near the top here */
 FARPROC GetProcAddressA(HMODULE hMod, wchar_t *procName) {
   FARPROC ret = NULL;
   int len = wcslen(procName);
   char *s = new char[len + 1];
--- a/js/src/xpconnect/src/xpcpublic.h
+++ b/js/src/xpconnect/src/xpcpublic.h
@@ -42,11 +42,12 @@
 
 #include "jsapi.h"
 #include "nsAString.h"
 #include "nsIPrincipal.h"
 
 nsresult
 xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp,
                        const nsACString &origin, nsIPrincipal *principal,
-                       JSObject **global, JSCompartment **compartment);
+                       bool preferXrays, JSObject **global,
+                       JSCompartment **compartment);
 
 #endif
--- a/js/src/xpconnect/src/xpcthreadcontext.cpp
+++ b/js/src/xpconnect/src/xpcthreadcontext.cpp
@@ -262,17 +262,17 @@ XPCJSContextStack::GetSafeJSContext(JSCo
                 nsCString origin;
                 principal->GetOrigin(getter_Copies(origin));
 
                 // scoped JS Request
                 JSAutoRequest req(mSafeJSContext);
 
                 JSCompartment *compartment;
                 nsresult rv = xpc_CreateGlobalObject(mSafeJSContext, &global_class,
-                                                     origin, principal, &glob,
+                                                     origin, principal, false, &glob,
                                                      &compartment);
                 if(NS_FAILED(rv))
                     glob = nsnull;
 
 #ifndef XPCONNECT_STANDALONE
                 if(glob)
                 {
                     // Make sure the context is associated with a proper compartment
--- a/js/src/xpconnect/tests/chrome/test_evalInSandbox.xul
+++ b/js/src/xpconnect/tests/chrome/test_evalInSandbox.xul
@@ -12,40 +12,72 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
 
   <iframe type="content"
           src="http://example.org/tests/js/src/xpconnect/tests/mochitest/file_evalInSandbox.html"
-          onload="go()"
-          id="ifr">
+          onload="go(this, true)">
+  </iframe>
+  <iframe type="content"
+          src="data:text/html,&lt;html&gt;&lt;body&gt;&lt;script&gt;document.foo %3D 'bar'%3B&lt;%2Fscript&gt;&lt;%2Fbody&gt;&lt;%2Fhtml&gt;"
+          onload="go(this, false)">
   </iframe>
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
       const Cu = Components.utils;
       const Ci = Components.interfaces;
       const utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
 
-      function go() {
-        var win = $('ifr').contentWindow;
-        var sandbox = new Cu.Sandbox(win);
-        is(utils.getClassName(sandbox),
-           "Proxy",
-           "sandbox was wrapped correctly");
-        sandbox.__proto__ = new XPCNativeWrapper(win);
+      var testsRun = 0;
+      function checkCrossOriginSandbox(sandbox)
+      {
+          is(utils.getClassName(sandbox),
+             "Proxy",
+             "sandbox was wrapped correctly");
+
+          is(utils.getClassName(Cu.evalInSandbox("this.document", sandbox)),
+             "Proxy",
+             "return value was rewrapped correctly");
+      }
+      function go(ifr, crossOrigin) {
+        var win = ifr.contentWindow;
+        if (crossOrigin) {
+          var sandbox = new Cu.Sandbox(win, win, false);
+
+          checkCrossOriginSandbox(sandbox);
 
-        is(utils.getClassName(Cu.evalInSandbox("this.document", sandbox)),
-           "Proxy",
-           "return value was rewrapped correctly");
-        ok(Cu.evalInSandbox("('wrappedJSObject' in this.document);", sandbox),
-           "wrappers inside eIS are XPCNativeWrappers");
+          ok(Cu.evalInSandbox("('wrappedJSObject' in this.document);", sandbox),
+             "wrappers inside eIS are XPCNativeWrappers");
+          ok(Cu.evalInSandbox("!('foo' in this.document);", sandbox),
+             "must not see expandos");
+
+          sandbox = new Cu.Sandbox(win, win, true);
+
+          checkCrossOriginSandbox(sandbox);
 
-        SimpleTest.finish();
+          ok(Cu.evalInSandbox("('foo' in this.document);", sandbox),
+             "can see expandos");
+        }
+        else {
+          var sandbox = new Cu.Sandbox(win, win, false);
+
+          ok(Cu.evalInSandbox("!('foo' in this.document);", sandbox),
+             "must not see expandos");
+
+          sandbox = new Cu.Sandbox(win, win, true);
+
+          ok(Cu.evalInSandbox("('foo' in this.document);", sandbox),
+             "can see expandos");
+        }
+
+        if (++testsRun == 2)
+          SimpleTest.finish();
       }
 
       SimpleTest.waitForExplicitFinish();
   ]]></script>
 </window>
--- a/js/src/xpconnect/tests/mochitest/file_evalInSandbox.html
+++ b/js/src/xpconnect/tests/mochitest/file_evalInSandbox.html
@@ -1,4 +1,7 @@
 <html>
     <body>
+        <script>
+            document.foo = "bar";
+        </script>
     </body>
 </html>
--- a/js/src/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp
@@ -141,17 +141,28 @@ WrapperFactory::Rewrap(JSContext *cx, JS
 
     JSCompartment *origin = obj->getCompartment();
     JSCompartment *target = cx->compartment;
     JSObject *xrayHolder = nsnull;
 
     JSWrapper *wrapper;
     if (AccessCheck::isChrome(target)) {
         if (AccessCheck::isChrome(origin)) {
-            wrapper = &JSCrossCompartmentWrapper::singleton;
+            // Same origin we use a transparent wrapper, unless the compartment asks
+            // for an Xray.
+            if (static_cast<xpc::CompartmentPrivate*>(target->data)->preferXrays &&
+                IS_WN_WRAPPER(obj)) {
+                typedef XrayWrapper<JSCrossCompartmentWrapper, CrossCompartmentXray> Xray;
+                wrapper = &Xray::singleton;
+                xrayHolder = Xray::createHolder(cx, obj, parent);
+                if (!xrayHolder)
+                    return nsnull;
+            } else {
+                wrapper = &JSCrossCompartmentWrapper::singleton;
+            }
         } else if (flags & WAIVE_XRAY_WRAPPER_FLAG) {
             // If we waived the X-ray wrapper for this object, wrap it into a
             // special wrapper to transitively maintain the X-ray waiver.
             wrapper = &XrayWrapperWaivedWrapper;
         } else {
             // Native objects must be wrapped into an X-ray wrapper.
             if (!obj->getGlobal()->isSystem() &&
                 (IS_WN_WRAPPER(obj) || obj->getClass()->ext.innerObject)) {
@@ -172,18 +183,28 @@ WrapperFactory::Rewrap(JSContext *cx, JS
         if (AccessCheck::needsSystemOnlyWrapper(obj)) {
             wrapper = &FilteringWrapper<JSCrossCompartmentWrapper,
                                         OnlyIfSubjectIsSystem>::singleton;
         } else {
             wrapper = &FilteringWrapper<JSCrossCompartmentWrapper,
                                         ExposedPropertiesOnly>::singleton;
         }
     } else if (AccessCheck::isSameOrigin(origin, target)) {
-        // Same origin we use a transparent wrapper;
-        wrapper = &JSCrossCompartmentWrapper::singleton;
+        // Same origin we use a transparent wrapper, unless the compartment asks
+        // for an Xray.
+        if (static_cast<xpc::CompartmentPrivate*>(target->data)->preferXrays &&
+            IS_WN_WRAPPER(obj)) {
+            typedef XrayWrapper<JSCrossCompartmentWrapper, CrossCompartmentXray> Xray;
+            wrapper = &Xray::singleton;
+            xrayHolder = Xray::createHolder(cx, obj, parent);
+            if (!xrayHolder)
+                return nsnull;
+        } else {
+            wrapper = &JSCrossCompartmentWrapper::singleton;
+        }
     } else {
         // Cross origin we want to disallow scripting and limit access to
         // a predefined set of properties. XrayWrapper adds a property
         // (.wrappedJSObject) which allows bypassing the XrayWrapper, but
         // we filter out access to that property.
         if (!IS_WN_WRAPPER(obj) && !obj->getClass()->ext.innerObject) {
             wrapper = &FilteringWrapper<JSCrossCompartmentWrapper,
                                         CrossOriginAccessiblePropertiesOnly>::singleton;