Bug 611485 - Don't enter the foreign compartment when calling through Xray wrappers. r=gal
authorBlake Kaplan <mrbkap@gmail.com>
Tue, 25 Jan 2011 15:06:45 -0800
changeset 61453 a2825fbe23e39d34451769d44ab0cf2e3a883253
parent 61452 68931522981d42fd4ca3b8b5f8447d5e73cc52fe
child 61454 8474f8f13bb1ad491364ec4eca67628590628fc8
push id18358
push usercleary@mozilla.com
push dateFri, 28 Jan 2011 04:07:41 +0000
treeherdermozilla-central@787b1e101a5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgal
bugs611485
milestone2.0b10pre
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 611485 - Don't enter the foreign compartment when calling through Xray wrappers. r=gal
content/base/src/nsContentUtils.cpp
js/src/xpconnect/src/xpccomponents.cpp
js/src/xpconnect/src/xpcwrappednative.cpp
js/src/xpconnect/src/xpcwrappednativeinfo.cpp
js/src/xpconnect/tests/chrome/test_bug448587.xul
js/src/xpconnect/wrappers/CrossOriginWrapper.cpp
js/src/xpconnect/wrappers/CrossOriginWrapper.h
js/src/xpconnect/wrappers/FilteringWrapper.cpp
js/src/xpconnect/wrappers/WrapperFactory.cpp
js/src/xpconnect/wrappers/XrayWrapper.cpp
js/src/xpconnect/wrappers/XrayWrapper.h
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -1518,16 +1518,21 @@ nsContentUtils::GetWindowFromCaller()
 nsIDOMDocument *
 nsContentUtils::GetDocumentFromCaller()
 {
   JSContext *cx = nsnull;
   JSObject *obj = nsnull;
   sXPConnect->GetCaller(&cx, &obj);
   NS_ASSERTION(cx && obj, "Caller ensures something is running");
 
+  JSAutoEnterCompartment ac;
+  if (!ac.enter(cx, obj)) {
+    return nsnull;
+  }
+
   nsCOMPtr<nsPIDOMWindow> win =
     do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(cx, obj));
   if (!win) {
     return nsnull;
   }
 
   return win->GetExtantDocument();
 }
--- a/js/src/xpconnect/src/xpccomponents.cpp
+++ b/js/src/xpconnect/src/xpccomponents.cpp
@@ -2817,43 +2817,28 @@ nsXPCComponents_Utils::LookupMethod()
     // it would a be a big surprise if there is a member without an interface :)
     XPCNativeInterface* iface = inner_cc.GetInterface();
     if(!iface)
         return NS_ERROR_XPC_BAD_CONVERT_JS;
 
     jsval funval;
     JSFunction *oldfunction;
 
-    {
-        JSAutoEnterCompartment ac;
-
-        if (!ac.enter(inner_cc, wrapper->GetFlatJSObjectAndMark())) {
-            return NS_ERROR_UNEXPECTED;
-        }
-
-        // get (and perhaps lazily create) the member's cloned function
-        if(!member->NewFunctionObject(inner_cc, iface,
-                                      wrapper->GetFlatJSObjectAndMark(),
-                                      &funval))
-            return NS_ERROR_XPC_BAD_CONVERT_JS;
-
-        oldfunction = JS_ValueToFunction(inner_cc, funval);
-        NS_ASSERTION(oldfunction, "Function is not a function");
-    }
+    // get (and perhaps lazily create) the member's cloned function
+    if(!member->NewFunctionObject(inner_cc, iface,
+                                  JSVAL_TO_OBJECT(argv[0]),
+                                  &funval))
+        return NS_ERROR_XPC_BAD_CONVERT_JS;
+
+    oldfunction = JS_ValueToFunction(inner_cc, funval);
+    NS_ASSERTION(oldfunction, "Function is not a function");
 
     // Stick the function in the return value. This roots it.
     *retval = funval;
 
-    // Callers of this method are implicitly buying into
-    // XPCNativeWrapper-like protection. The easiest way to enforce
-    // this is to let the JS engine wrap the function.
-    if (!JS_WrapValue(inner_cc, retval)) {
-        return NS_ERROR_UNEXPECTED;
-    }
-
     // Tell XPConnect that we returned the function through the call context.
     cc->SetReturnValueWasSet(PR_TRUE);
     return NS_OK;
 }
 
 /* void reportError (); */
 NS_IMETHODIMP
 nsXPCComponents_Utils::ReportError()
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -1684,17 +1684,18 @@ XPCWrappedNative::GetWrappedNativeOfJSOb
     XPCWrappedNativeProto* proto = nsnull;
     nsIClassInfo* protoClassInfo = nsnull;
 
     // If we were passed a function object then we need to find the correct
     // wrapper out of those that might be in the callee obj's proto chain.
 
     if(funobj)
     {
-        JSObject* funObjParent = funobj->getParent();
+        JSObject* funObjParent = funobj->getParent()->unwrap();
+        OBJ_TO_INNER_OBJECT(cx, funObjParent);
         NS_ASSERTION(funObjParent, "funobj has no parent");
 
         js::Class* funObjParentClass = funObjParent->getClass();
 
         if(IS_PROTO_CLASS(funObjParentClass))
         {
             NS_ASSERTION(funObjParent->getParent(), "funobj's parent (proto) is global");
             proto = (XPCWrappedNativeProto*) xpc_GetJSPrivate(funObjParent);
--- a/js/src/xpconnect/src/xpcwrappednativeinfo.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativeinfo.cpp
@@ -88,26 +88,19 @@ xpc_CloneJSFunction(XPCCallContext &ccx,
 
 // static
 JSBool
 XPCNativeMember::GetCallInfo(XPCCallContext& ccx,
                              JSObject* funobj,
                              XPCNativeInterface** pInterface,
                              XPCNativeMember**    pMember)
 {
-    jsval ifaceVal;
-    jsval memberVal;
-
-    if(!JS_GetReservedSlot(ccx, funobj, 0, &ifaceVal) ||
-       JSVAL_IS_VOID(ifaceVal) ||
-       !JS_GetReservedSlot(ccx, funobj, 1, &memberVal) ||
-       JSVAL_IS_VOID(memberVal))
-    {
-        return JS_FALSE;
-    }
+    funobj = funobj->unwrap();
+    jsval ifaceVal = js::Jsvalify(funobj->getSlot(0));
+    jsval memberVal = js::Jsvalify(funobj->getSlot(1));
 
     *pInterface = (XPCNativeInterface*) JSVAL_TO_PRIVATE(ifaceVal);
     *pMember = (XPCNativeMember*) JSVAL_TO_PRIVATE(memberVal);
 
     return JS_TRUE;
 }
 
 JSBool
--- a/js/src/xpconnect/tests/chrome/test_bug448587.xul
+++ b/js/src/xpconnect/tests/chrome/test_bug448587.xul
@@ -20,12 +20,13 @@ https://bugzilla.mozilla.org/show_bug.cg
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
 
   /** Test for Bug 448587 **/
   const Cu = Components.utils;
   var sandbox = new Cu.Sandbox("about:blank");
   var fwrapper = Cu.evalInSandbox("function f() {} f", sandbox);
-  ok(fwrapper.prototype == Cu.evalInSandbox("f.prototype", sandbox));
+  is(fwrapper.prototype, Cu.evalInSandbox("f.prototype", sandbox),
+     "we don't censor .prototype through .wrappedJSObject");
   ]]>
   </script>
 </window>
--- a/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp
+++ b/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp
@@ -41,17 +41,25 @@
 
 #include "XPCWrapper.h"
 
 #include "CrossOriginWrapper.h"
 #include "WrapperFactory.h"
 
 namespace xpc {
 
-CrossOriginWrapper::CrossOriginWrapper(uintN flags) : JSCrossCompartmentWrapper(flags)
+NoWaiverWrapper::NoWaiverWrapper(uintN flags) : JSCrossCompartmentWrapper(flags)
+{
+}
+
+NoWaiverWrapper::~NoWaiverWrapper()
+{
+}
+
+CrossOriginWrapper::CrossOriginWrapper(uintN flags) : NoWaiverWrapper(flags)
 {
 }
 
 CrossOriginWrapper::~CrossOriginWrapper()
 {
 }
 
 static nsIPrincipal *
@@ -95,17 +103,17 @@ bool
 CrossOriginWrapper::construct(JSContext *cx, JSObject *wrapper,
                               uintN argc, js::Value *argv, js::Value *rval)
 {
     return JSCrossCompartmentWrapper::construct(cx, wrapper, argc, argv, rval) &&
            WrapperFactory::WaiveXrayAndWrap(cx, js::Jsvalify(rval));
 }
 
 bool
-CrossOriginWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act)
+NoWaiverWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act)
 {
     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
     if (!ssm) {
         return true;
     }
     JSStackFrame *fp = NULL;
     nsIPrincipal *principal = GetCompartmentPrincipal(wrappedObject(wrapper)->compartment());
     nsresult rv = ssm->PushContextPrincipal(cx, JS_FrameIterator(cx, &fp), principal);
@@ -113,17 +121,17 @@ CrossOriginWrapper::enter(JSContext *cx,
         NS_WARNING("Not allowing call because we're out of memory");
         JS_ReportOutOfMemory(cx);
         return false;
     }
     return true;
 }
 
 void
-CrossOriginWrapper::leave(JSContext *cx, JSObject *wrapper)
+NoWaiverWrapper::leave(JSContext *cx, JSObject *wrapper)
 {
     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
     if (ssm) {
         ssm->PopContextPrincipal(cx);
     }
 }
 
 }
--- a/js/src/xpconnect/wrappers/CrossOriginWrapper.h
+++ b/js/src/xpconnect/wrappers/CrossOriginWrapper.h
@@ -40,33 +40,41 @@
 #ifndef __CrossOriginWrapper_h__
 #define __CrossOriginWrapper_h__
 
 #include "jsapi.h"
 #include "jswrapper.h"
 
 namespace xpc {
 
-class CrossOriginWrapper : public JSCrossCompartmentWrapper {
+class NoWaiverWrapper : public JSCrossCompartmentWrapper {
+  public:
+    NoWaiverWrapper(uintN flags);
+    virtual ~NoWaiverWrapper();
+
+    virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act);
+    virtual void leave(JSContext *cx, JSObject *wrapper);
+
+    static NoWaiverWrapper singleton;
+};
+
+class CrossOriginWrapper : public NoWaiverWrapper {
   public:
     CrossOriginWrapper(uintN flags);
     virtual ~CrossOriginWrapper();
 
     virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
                                        bool set, js::PropertyDescriptor *desc);
     virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
                                           bool set, js::PropertyDescriptor *desc);
     virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
                      js::Value *vp);
 
     virtual bool call(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *vp);
     virtual bool construct(JSContext *cx, JSObject *wrapper,
                            uintN argc, js::Value *argv, js::Value *rval);
 
-    virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act);
-    virtual void leave(JSContext *cx, JSObject *wrapper);
-
     static CrossOriginWrapper singleton;
 };
 
 }
 
 #endif
--- a/js/src/xpconnect/wrappers/FilteringWrapper.cpp
+++ b/js/src/xpconnect/wrappers/FilteringWrapper.cpp
@@ -147,22 +147,22 @@ FilteringWrapper<Base, Policy>::enter(JS
     Permission perm;
     return CheckAndReport<Policy>(cx, wrapper, id, act, perm) &&
            Base::enter(cx, wrapper, id, act);
 }
 
 #define SOW FilteringWrapper<JSCrossCompartmentWrapper, OnlyIfSubjectIsSystem>
 #define SCSOW FilteringWrapper<JSWrapper, OnlyIfSubjectIsSystem>
 #define COW FilteringWrapper<JSCrossCompartmentWrapper, ExposedPropertiesOnly>
-#define XOW FilteringWrapper<XrayWrapper<JSCrossCompartmentWrapper, CrossCompartmentXray>, \
+#define XOW FilteringWrapper<XrayWrapper<JSCrossCompartmentWrapper>, \
                              CrossOriginAccessiblePropertiesOnly>
 #define NNXOW FilteringWrapper<JSCrossCompartmentWrapper, CrossOriginAccessiblePropertiesOnly>
-#define LW    FilteringWrapper<XrayWrapper<JSWrapper, SameCompartmentXray>, \
+#define LW    FilteringWrapper<XrayWrapper<JSWrapper>, \
                                SameOriginOrCrossOriginAccessiblePropertiesOnly>
-#define XLW   FilteringWrapper<XrayWrapper<JSCrossCompartmentWrapper, CrossCompartmentXray>, \
+#define XLW   FilteringWrapper<XrayWrapper<JSCrossCompartmentWrapper>, \
                                SameOriginOrCrossOriginAccessiblePropertiesOnly>
 
 template<> SOW SOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                               WrapperFactory::SOW_FLAG);
 template<> SCSOW SCSOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                                   WrapperFactory::SOW_FLAG);
 template<> COW COW::singleton(0);
 template<> XOW XOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
--- a/js/src/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp
@@ -56,16 +56,23 @@ namespace xpc {
 // .wrappedJSObject, we want it to cross the membrane into the
 // chrome compartment without automatically being wrapped into an
 // X-ray wrapper. We achieve this by wrapping it into a special
 // transparent wrapper in the origin (non-chrome) compartment. When
 // an object with that special wrapper applied crosses into chrome,
 // we know to not apply an X-ray wrapper.
 JSWrapper WaiveXrayWrapperWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG);
 
+// Objects that haven't been explicitly waived, but have been exposed
+// to chrome don't want a CrossOriginWrapper, since that deeply-waives
+// but need the transparent behavior of a CrossOriginWrapper. The
+// NoWaiverWrapper is like a CrossOriginWrapper that can also hand out
+// XrayWrappers as return values.
+NoWaiverWrapper NoWaiverWrapper::singleton(0);
+
 // When objects for which we waived the X-ray wrapper cross into
 // chrome, we wrap them into a special cross-compartment wrapper
 // that transitively extends the waiver to all properties we get
 // off it.
 CrossOriginWrapper CrossOriginWrapper::singleton(0);
 
 static JSObject *
 GetCurrentOuter(JSContext *cx, JSObject *obj)
@@ -239,33 +246,32 @@ WrapperFactory::Rewrap(JSContext *cx, JS
 
     JSCompartment *origin = obj->compartment();
     JSCompartment *target = cx->compartment;
     JSObject *xrayHolder = nsnull;
 
     JSWrapper *wrapper;
     CompartmentPrivate *targetdata = static_cast<CompartmentPrivate *>(target->data);
     if (AccessCheck::isChrome(target)) {
-        if (AccessCheck::isChrome(origin)) {
+        if (AccessCheck::isChrome(origin) || obj->getGlobal()->isSystem()) {
             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 = &CrossOriginWrapper::singleton;
         } else {
             // Native objects must be wrapped into an X-ray wrapper.
-            if (!obj->getGlobal()->isSystem() &&
-                (IS_WN_WRAPPER(obj) || obj->getClass()->ext.innerObject)) {
-                typedef XrayWrapper<JSCrossCompartmentWrapper, CrossCompartmentXray> Xray;
+            if (IS_WN_WRAPPER(obj) || obj->getClass()->ext.innerObject) {
+                typedef XrayWrapper<JSCrossCompartmentWrapper> Xray;
                 wrapper = &Xray::singleton;
                 xrayHolder = Xray::createHolder(cx, obj, parent);
                 if (!xrayHolder)
                     return nsnull;
             } else {
-                wrapper = &JSCrossCompartmentWrapper::singleton;
+                wrapper = &NoWaiverWrapper::singleton;
             }
         }
     } else if (AccessCheck::isChrome(origin)) {
         if (obj->isFunction()) {
             JSFunction *fun = obj->getFunctionPrivate();
             if (js::IsBuiltinEvalFunction(fun) || js::IsBuiltinFunctionConstructor(fun)) {
                 JS_ReportError(cx, "Not allowed to access chrome eval or Function from content");
                 return nsnull;
@@ -276,17 +282,17 @@ WrapperFactory::Rewrap(JSContext *cx, JS
     } else if (AccessCheck::isSameOrigin(origin, target)) {
         // Same origin we use a transparent wrapper, unless the compartment asks
         // for an Xray or the wrapper needs a SOW.
         if (AccessCheck::needsSystemOnlyWrapper(obj)) {
             wrapper = &FilteringWrapper<JSCrossCompartmentWrapper,
                                         OnlyIfSubjectIsSystem>::singleton;
         } else if (targetdata && targetdata->wantXrays &&
                    (IS_WN_WRAPPER(obj) || obj->getClass()->ext.innerObject)) {
-            typedef XrayWrapper<JSCrossCompartmentWrapper, CrossCompartmentXray> Xray;
+            typedef XrayWrapper<JSCrossCompartmentWrapper> Xray;
             wrapper = &Xray::singleton;
             xrayHolder = Xray::createHolder(cx, obj, parent);
             if (!xrayHolder)
                 return nsnull;
         } else {
             wrapper = &JSCrossCompartmentWrapper::singleton;
         }
     } else {
@@ -296,17 +302,17 @@ WrapperFactory::Rewrap(JSContext *cx, JS
         // 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;
         } else {
-            typedef XrayWrapper<JSCrossCompartmentWrapper, CrossCompartmentXray> Xray;
+            typedef XrayWrapper<JSCrossCompartmentWrapper> Xray;
 
             // Location objects can become same origin after navigation, so we might
             // have to grant transparent access later on.
             if (IsLocationObject(obj)) {
                 wrapper = &FilteringWrapper<Xray,
                     SameOriginOrCrossOriginAccessiblePropertiesOnly>::singleton;
             } else {
                 wrapper= &FilteringWrapper<Xray,
@@ -324,17 +330,17 @@ WrapperFactory::Rewrap(JSContext *cx, JS
         return wrapperObj;
 
     // NB: The fact that the only wrappers to use ProxyExtra are XrayWrappers
     // is relied on by XPCNativeWrapper.unwrap.
     wrapperObj->setProxyExtra(js::ObjectValue(*xrayHolder));
     return wrapperObj;
 }
 
-typedef FilteringWrapper<XrayWrapper<JSWrapper, SameCompartmentXray>,
+typedef FilteringWrapper<XrayWrapper<JSWrapper>,
                          SameOriginOrCrossOriginAccessiblePropertiesOnly> LW;
 
 bool
 WrapperFactory::IsLocationObject(JSObject *obj)
 {
     const char *name = obj->getClass()->name;
     return name[0] == 'L' && !strcmp(name, "Location");
 }
--- a/js/src/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp
@@ -268,55 +268,42 @@ ResolveNativeProperty(JSContext *cx, JSO
     jsval fval = JSVAL_VOID;
     if (member->IsConstant()) {
         if (!member->GetConstantValue(ccx, iface, &desc->value)) {
             JS_ReportError(cx, "Failed to convert constant native property to JS value");
             return false;
         }
     } else if (member->IsAttribute()) {
         // This is a getter/setter. Clone a function for it.
-
-        JSAutoEnterCompartment ac;
-        if (!ac.enter(cx, wnObject))
-            return false;
-
-        if (!member->NewFunctionObject(ccx, iface, wnObject, &fval)) {
+        if (!member->NewFunctionObject(ccx, iface, wrapper, &fval)) {
             JS_ReportError(cx, "Failed to clone function object for native getter/setter");
             return false;
         }
 
         desc->attrs |= JSPROP_GETTER;
         if (member->IsWritableAttribute())
             desc->attrs |= JSPROP_SETTER;
 
         // Make the property shared on the holder so no slot is allocated
         // for it. This avoids keeping garbage alive through that slot.
         desc->attrs |= JSPROP_SHARED;
     } else {
-        JSAutoEnterCompartment ac;
-        if (!ac.enter(cx, wnObject))
-            return false;
-
         // This is a method. Clone a function for it.
-        if (!member->NewFunctionObject(ccx, iface, wnObject, &desc->value)) {
+        if (!member->NewFunctionObject(ccx, iface, wrapper, &desc->value)) {
             JS_ReportError(cx, "Failed to clone function object for native function");
             return false;
         }
 
         // Without a wrapper the function would live on the prototype. Since we
         // don't have one, we have to avoid calling the scriptable helper's
         // GetProperty method for this property, so stub out the getter and
         // setter here explicitly.
         desc->getter = desc->setter = JS_PropertyStub;
     }
 
-    JSAutoEnterCompartment ac;
-    if (!ac.enter(cx, holder))
-        return false;
-
     if (!JS_WrapValue(cx, &desc->value) || !JS_WrapValue(cx, &fval))
         return false;
 
     if (desc->attrs & JSPROP_GETTER)
         desc->getter = CastAsJSPropertyOp(JSVAL_TO_OBJECT(fval));
     if (desc->attrs & JSPROP_SETTER)
         desc->setter = desc->getter;
 
@@ -361,42 +348,42 @@ XrayToString(JSContext *cx, uintN argc, 
                                         result.Length());
     if (!str)
         return false;
 
     *vp = STRING_TO_JSVAL(str);
     return true;
 }
 
-template <typename Base, typename Policy>
-XrayWrapper<Base, Policy>::XrayWrapper(uintN flags)
+template <typename Base>
+XrayWrapper<Base>::XrayWrapper(uintN flags)
   : Base(flags | WrapperFactory::IS_XRAY_WRAPPER_FLAG)
 {
 }
 
-template <typename Base, typename Policy>
-XrayWrapper<Base, Policy>::~XrayWrapper()
+template <typename Base>
+XrayWrapper<Base>::~XrayWrapper()
 {
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 class AutoLeaveHelper
 {
   public:
-    AutoLeaveHelper(XrayWrapper<Base, Policy> &xray, JSContext *cx, JSObject *wrapper)
+    AutoLeaveHelper(XrayWrapper<Base> &xray, JSContext *cx, JSObject *wrapper)
       : xray(xray), cx(cx), wrapper(wrapper)
     {
     }
     ~AutoLeaveHelper()
     {
         xray.leave(cx, wrapper);
     }
 
   private:
-    XrayWrapper<Base, Policy> &xray;
+    XrayWrapper<Base> &xray;
     JSContext *cx;
     JSObject *wrapper;
 };
 
 static bool
 Transparent(JSContext *cx, JSObject *wrapper)
 {
     if (WrapperFactory::HasWaiveXrayFlag(wrapper))
@@ -421,28 +408,28 @@ namespace XrayUtils {
 bool
 IsTransparent(JSContext *cx, JSObject *wrapper)
 {
     return Transparent(cx, wrapper);
 }
 
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::resolveOwnProperty(JSContext *cx, JSObject *wrapper, jsid id, bool set,
-                                              PropertyDescriptor *desc_in)
+XrayWrapper<Base>::resolveOwnProperty(JSContext *cx, JSObject *wrapper, jsid id, bool set,
+                                      PropertyDescriptor *desc_in)
 {
     JSPropertyDescriptor *desc = Jsvalify(desc_in);
 
     if (id == nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) {
         if (!this->enter(cx, wrapper, id, set ? JSWrapper::SET : JSWrapper::GET))
             return false;
 
-        AutoLeaveHelper<Base, Policy> helper(*this, cx, wrapper);
+        AutoLeaveHelper<Base> helper(*this, cx, wrapper);
 
         desc->obj = wrapper;
         desc->attrs = JSPROP_ENUMERATE|JSPROP_SHARED;
         desc->getter = wrappedJSObject_getter;
         desc->setter = NULL;
         desc->shortid = 0;
         desc->value = JSVAL_VOID;
         return true;
@@ -489,32 +476,32 @@ XrayWrapper<Base, Policy>::resolveOwnPro
             return true;
         }
     }
 
     desc->obj = nsnull;
     return true;
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
-                                                 bool set, PropertyDescriptor *desc_in)
+XrayWrapper<Base>::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
+                                         bool set, PropertyDescriptor *desc_in)
 {
     JSPropertyDescriptor *desc = Jsvalify(desc_in);
     JSObject *holder = GetHolder(wrapper);
     if (IsResolving(holder, id)) {
         desc->obj = NULL;
         return true;
     }
 
     if (!this->enter(cx, wrapper, id, set ? JSWrapper::SET : JSWrapper::GET))
         return false;
 
-    AutoLeaveHelper<Base, Policy> helper(*this, cx, wrapper);
+    AutoLeaveHelper<Base> helper(*this, cx, wrapper);
 
     ResolvingId resolving(holder, id);
 
     // Redirect access straight to the wrapper if we should be transparent.
     if (Transparent(cx, wrapper)) {
         JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder);
 
         {
@@ -533,22 +520,17 @@ XrayWrapper<Base, Policy>::getPropertyDe
     }
 
     if (!this->resolveOwnProperty(cx, wrapper, id, set, desc_in))
         return false;
 
     if (desc->obj)
         return true;
 
-    void *priv;
-    if (!Policy::enter(cx, wrapper, &id, set ? JSWrapper::SET : JSWrapper::GET, &priv))
-        return false;
-
     bool ok = ResolveNativeProperty(cx, wrapper, holder, id, set, desc);
-    Policy::leave(cx, wrapper, priv);
     if (!ok || desc->obj)
         return ok;
 
     if (id == nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING)) {
         desc->obj = wrapper;
         desc->attrs = 0;
         desc->getter = NULL;
         desc->setter = NULL;
@@ -559,32 +541,32 @@ XrayWrapper<Base, Policy>::getPropertyDe
             return false;
         desc->value = OBJECT_TO_JSVAL(toString);
         return true;
     }
 
     return true;
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
-                                                    bool set, PropertyDescriptor *desc_in)
+XrayWrapper<Base>::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
+                                            bool set, PropertyDescriptor *desc_in)
 {
     JSPropertyDescriptor *desc = Jsvalify(desc_in);
     JSObject *holder = GetHolder(wrapper);
     if (IsResolving(holder, id)) {
         desc->obj = NULL;
         return true;
     }
 
     if (!this->enter(cx, wrapper, id, set ? JSWrapper::SET : JSWrapper::GET))
         return false;
 
-    AutoLeaveHelper<Base, Policy> helper(*this, cx, wrapper);
+    AutoLeaveHelper<Base> helper(*this, cx, wrapper);
 
     ResolvingId resolving(holder, id);
 
     // NB: Nothing we do here acts on the wrapped native itself, so we don't
     // enter our policy.
     // Redirect access straight to the wrapper if we should be transparent.
     if (Transparent(cx, wrapper)) {
         JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder);
@@ -603,20 +585,20 @@ XrayWrapper<Base, Policy>::getOwnPropert
 
         desc->obj = (desc->obj == wnObject) ? wrapper : nsnull;
         return cx->compartment->wrap(cx, desc_in);
     }
 
     return this->resolveOwnProperty(cx, wrapper, id, set, desc_in);
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
-                                          js::PropertyDescriptor *desc)
+XrayWrapper<Base>::defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
+                                  js::PropertyDescriptor *desc)
 {
     JSObject *holder = GetHolder(wrapper);
     JSPropertyDescriptor *jsdesc = Jsvalify(desc);
 
     // Redirect access straight to the wrapper if we should be transparent.
     if (Transparent(cx, wrapper)) {
         JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder);
 
@@ -701,27 +683,27 @@ EnumerateNames(JSContext *cx, JSObject *
         if (!JS_HasPropertyById(cx, wrapper, id, &hasProp))
             return false;
         if (hasProp)
             props.append(id);
     }
     return true;
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::getOwnPropertyNames(JSContext *cx, JSObject *wrapper,
-                                               js::AutoIdVector &props)
+XrayWrapper<Base>::getOwnPropertyNames(JSContext *cx, JSObject *wrapper,
+                                       js::AutoIdVector &props)
 {
     return EnumerateNames(cx, wrapper, JSITER_OWNONLY | JSITER_HIDDEN, props);
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
+XrayWrapper<Base>::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
 {
     JSObject *holder = GetHolder(wrapper);
     jsval v;
     JSBool b;
 
     // Redirect access straight to the wrapper if we should be transparent.
     if (Transparent(cx, wrapper)) {
         JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder);
@@ -743,88 +725,147 @@ XrayWrapper<Base, Policy>::delete_(JSCon
          !JS_ValueToBoolean(cx, v, &b))) {
         return false;
     }
 
     *bp = !!b;
     return true;
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::enumerate(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props)
+XrayWrapper<Base>::enumerate(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props)
 {
     return EnumerateNames(cx, wrapper, 0, props);
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::fix(JSContext *cx, JSObject *proxy, js::Value *vp)
+XrayWrapper<Base>::fix(JSContext *cx, JSObject *proxy, js::Value *vp)
 {
     vp->setUndefined();
     return true;
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
-                               js::Value *vp)
+XrayWrapper<Base>::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
+                       js::Value *vp)
 {
     // Skip our Base if it isn't already JSProxyHandler.
     // NB: None of the functions we call are prepared for the receiver not
     // being the wrapper, so ignore the receiver here.
     return JSProxyHandler::get(cx, wrapper, wrapper, id, vp);
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
-                               js::Value *vp)
+XrayWrapper<Base>::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
+                       js::Value *vp)
 {
     // Skip our Base if it isn't already JSProxyHandler.
     // NB: None of the functions we call are prepared for the receiver not
     // being the wrapper, so ignore the receiver here.
     return JSProxyHandler::set(cx, wrapper, wrapper, id, vp);
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
+XrayWrapper<Base>::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
 {
     // Skip our Base if it isn't already JSProxyHandler.
     return JSProxyHandler::has(cx, wrapper, id, bp);
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
+XrayWrapper<Base>::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
 {
     // Skip our Base if it isn't already JSProxyHandler.
     return JSProxyHandler::hasOwn(cx, wrapper, id, bp);
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::keys(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props)
+XrayWrapper<Base>::keys(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props)
 {
     // Skip our Base if it isn't already JSProxyHandler.
     return JSProxyHandler::keys(cx, wrapper, props);
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
 bool
-XrayWrapper<Base, Policy>::iterate(JSContext *cx, JSObject *wrapper, uintN flags, js::Value *vp)
+XrayWrapper<Base>::iterate(JSContext *cx, JSObject *wrapper, uintN flags, js::Value *vp)
 {
     // Skip our Base if it isn't already JSProxyHandler.
     return JSProxyHandler::iterate(cx, wrapper, flags, vp);
 }
 
-template <typename Base, typename Policy>
+template <typename Base>
+bool
+XrayWrapper<Base>::call(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *vp)
+{
+    JSObject *holder = GetHolder(wrapper);
+    JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder);
+    XPCWrappedNative *wn = GetWrappedNative(wnObject);
+
+    // Run the resolve hook of the wrapped native.
+    if (NATIVE_HAS_FLAG(wn, WantCall)) {
+        XPCCallContext ccx(JS_CALLER, cx, wrapper, nsnull, JSID_VOID, argc,
+                           Jsvalify(vp + 2), Jsvalify(vp));
+        if (!ccx.IsValid())
+            return false;
+        PRBool ok = PR_TRUE;
+        nsresult rv = wn->GetScriptableInfo()->GetCallback()->Call(wn, cx, wrapper,
+                                                                   argc, Jsvalify(vp + 2),
+                                                                   Jsvalify(vp), &ok);
+        if (NS_FAILED(rv)) {
+            if (ok)
+                XPCThrower::Throw(rv, cx);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+template <typename Base>
+bool
+XrayWrapper<Base>::construct(JSContext *cx, JSObject *wrapper, uintN argc,
+                             js::Value *argv, js::Value *rval)
+{
+    JSObject *holder = GetHolder(wrapper);
+    JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder);
+    XPCWrappedNative *wn = GetWrappedNative(wnObject);
+
+    // Run the resolve hook of the wrapped native.
+    if (NATIVE_HAS_FLAG(wn, WantConstruct)) {
+        XPCCallContext ccx(JS_CALLER, cx, wrapper, nsnull, JSID_VOID, argc,
+                           Jsvalify(argv), Jsvalify(rval));
+        if (!ccx.IsValid())
+            return false;
+        PRBool ok = PR_TRUE;
+        nsresult rv = wn->GetScriptableInfo()->GetCallback()->Construct(wn, cx, wrapper,
+                                                                        argc,
+                                                                        Jsvalify(argv),
+                                                                        Jsvalify(rval),
+                                                                        &ok);
+        if (NS_FAILED(rv)) {
+            if (ok)
+                XPCThrower::Throw(rv, cx);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+template <typename Base>
 JSObject *
-XrayWrapper<Base, Policy>::createHolder(JSContext *cx, JSObject *wrappedNative, JSObject *parent)
+XrayWrapper<Base>::createHolder(JSContext *cx, JSObject *wrappedNative, JSObject *parent)
 {
     JSObject *holder = JS_NewObjectWithGivenProto(cx, &HolderClass, nsnull, parent);
     if (!holder)
         return nsnull;
 
     CompartmentPrivate *priv =
         (CompartmentPrivate *)JS_GetCompartmentPrivate(cx, holder->compartment());
     JSObject *inner = wrappedNative;
@@ -835,37 +876,18 @@ XrayWrapper<Base, Policy>::createHolder(
     JS_ASSERT(IS_WN_WRAPPER(wrappedNative) ||
               wrappedNative->getClass()->ext.innerObject);
     holder->setSlot(JSSLOT_WN_OBJ, ObjectValue(*wrappedNative));
     holder->setSlot(JSSLOT_RESOLVING, PrivateValue(NULL));
     holder->setSlot(JSSLOT_EXPANDO, expando);
     return holder;
 }
 
-bool
-CrossCompartmentXray::enter(JSContext *cx, JSObject *wrapper, jsid *idp,
-                            JSWrapper::Action act, void **priv)
-{
-    JSObject *target = wrapper->unwrap();
-    JSCrossCompartmentCall *call = JS_EnterCrossCompartmentCall(cx, target);
-    if (!call)
-        return false;
-
-    *priv = call;
-    return true;
-}
-
-void
-CrossCompartmentXray::leave(JSContext *cx, JSObject *wrapper, void *priv)
-{
-    JS_LeaveCrossCompartmentCall(static_cast<JSCrossCompartmentCall *>(priv));
-}
-
-#define XPCNW XrayWrapper<JSCrossCompartmentWrapper, CrossCompartmentXray>
-#define SCNW XrayWrapper<JSWrapper, SameCompartmentXray>
+#define XPCNW XrayWrapper<JSCrossCompartmentWrapper>
+#define SCNW XrayWrapper<JSWrapper>
 
 template <> XPCNW XPCNW::singleton(0);
 template <> SCNW SCNW::singleton(0);
 
 template class XPCNW;
 template class SCNW;
 
 }
--- a/js/src/xpconnect/wrappers/XrayWrapper.h
+++ b/js/src/xpconnect/wrappers/XrayWrapper.h
@@ -53,17 +53,17 @@ namespace XrayUtils {
 extern JSClass HolderClass;
 
 bool
 IsTransparent(JSContext *cx, JSObject *wrapper);
 
 }
 
 // NB: Base *must* derive from JSProxyHandler
-template <typename Base, typename Policy>
+template <typename Base>
 class XrayWrapper : public Base {
   public:
     XrayWrapper(uintN flags);
     virtual ~XrayWrapper();
 
     bool resolveWrappedJSObject(JSContext *cx, JSObject *wrapper, jsid id,
                                 bool set, js::PropertyDescriptor *desc);
 
@@ -85,34 +85,22 @@ class XrayWrapper : public Base {
                      js::Value *vp);
     virtual bool set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
                      js::Value *vp);
     virtual bool has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp);
     virtual bool hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp);
     virtual bool keys(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props);
     virtual bool iterate(JSContext *cx, JSObject *wrapper, uintN flags, js::Value *vp);
 
+    virtual bool call(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *vp);
+    virtual bool construct(JSContext *cx, JSObject *wrapper,
+                           uintN argc, js::Value *argv, js::Value *rval);
+
     static JSObject *createHolder(JSContext *cx, JSObject *wrappedNative, JSObject *parent);
 
     static XrayWrapper singleton;
 
   private:
     bool resolveOwnProperty(JSContext *cx, JSObject *wrapper, jsid id, bool set,
                             js::PropertyDescriptor *desc);
 };
 
-class CrossCompartmentXray {
-  public:
-    static bool enter(JSContext *cx, JSObject *wrapper, jsid *idp,
-                      JSWrapper::Action act, void **priv);
-    static void leave(JSContext *cx, JSObject *wrapper, void *priv);
-};
-
-class SameCompartmentXray {
-  public:
-    static bool enter(JSContext *, JSObject *, jsid *, JSWrapper::Action, void **) {
-        return true;
-    }
-    static void leave(JSContext *cx, JSObject *wrapper, void *priv) {
-    }
-};
-
 }