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 idunknown
push userunknown
push dateunknown
reviewersgal
bugs611485
milestone2.0b10pre
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) {
-    }
-};
-
 }