Bug 533882 - Don't call into CAPs when we don't have to. r=jst sr=bzbarsky
authorBlake Kaplan <mrbkap@gmail.com>
Wed, 16 Dec 2009 17:40:14 -0800
changeset 40635 eea9f123edfedafa5034662c5a9cbcda787272a2
parent 40634 6cba83fe343bddfad09761f8b343912e5d555b29
child 40636 e7a05d0e9fc5a36f6594187fdbf4d84aeb073b3f
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst, bzbarsky
bugs533882
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 533882 - Don't call into CAPs when we don't have to. r=jst sr=bzbarsky
js/src/xpconnect/src/XPCCrossOriginWrapper.cpp
js/src/xpconnect/src/XPCWrapper.cpp
js/src/xpconnect/src/XPCWrapper.h
--- a/js/src/xpconnect/src/XPCCrossOriginWrapper.cpp
+++ b/js/src/xpconnect/src/XPCCrossOriginWrapper.cpp
@@ -216,18 +216,45 @@ WrapperMoved(JSContext *cx, XPCWrappedNa
 
 // Returns whether the currently executing code is allowed to access
 // the wrapper.  Uses nsIPrincipal::Subsumes.
 // |cx| must be the top context on the context stack.
 // If the subject is allowed to access the object returns NS_OK. If not,
 // returns NS_ERROR_DOM_PROP_ACCESS_DENIED, returns another error code on
 // failure.
 nsresult
-CanAccessWrapper(JSContext *cx, JSObject *wrappedObj, JSBool *privilegeEnabled)
+CanAccessWrapper(JSContext *cx, JSObject *outerObj, JSObject *wrappedObj,
+                 JSBool *privilegeEnabled)
 {
+  // Fast path: If the wrapper and the wrapped object have the same global
+  // object (or if the wrapped object is a outer for the same inner that
+  // the wrapper is parented to), then we don't need to do any more work.
+
+  if (privilegeEnabled) {
+    *privilegeEnabled = JS_FALSE;
+  }
+
+  if (outerObj) {
+    JSObject *outerParent = outerObj->getParent();
+    JSObject *innerParent = wrappedObj->getParent();
+    if (!innerParent) {
+      innerParent = wrappedObj;
+      OBJ_TO_INNER_OBJECT(cx, innerParent);
+      if (!innerParent) {
+        return NS_ERROR_FAILURE;
+      }
+    } else {
+      innerParent = JS_GetGlobalForObject(cx, innerParent);
+    }
+
+    if (outerParent == innerParent) {
+      return NS_OK;
+    }
+  }
+
   // TODO bug 508928: Refactor this with the XOW security checking code.
   // Get the subject principal from the execution stack.
   nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
   if (!ssm) {
     ThrowException(NS_ERROR_NOT_INITIALIZED, cx);
     return NS_ERROR_NOT_INITIALIZED;
   }
 
@@ -468,17 +495,17 @@ XPC_XOW_FunctionWrapper(JSContext *cx, J
                         jsval *rval)
 {
   JSObject *wrappedObj, *outerObj = obj;
 
   // Allow 'this' to be either an XOW, in which case we unwrap it.
   // We disallow invalid XOWs that have no wrapped object. Otherwise,
   // if it isn't an XOW, then pass it through as-is.
 
-  wrappedObj = GetWrapper(obj);
+  outerObj = wrappedObj = GetWrapper(obj);
   if (wrappedObj) {
     wrappedObj = GetWrappedObject(cx, wrappedObj);
     if (!wrappedObj) {
       return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx);
     }
   } else {
     wrappedObj = obj;
   }
@@ -494,17 +521,17 @@ XPC_XOW_FunctionWrapper(JSContext *cx, J
     return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx);
   }
 
   XPCCallContext ccx(JS_CALLER, cx);
   if (!ccx.IsValid()) {
     return ThrowException(NS_ERROR_FAILURE, cx);
   }
 
-  nsresult rv = CanAccessWrapper(cx, JSVAL_TO_OBJECT(funToCall), nsnull);
+  nsresult rv = CanAccessWrapper(cx, outerObj, JSVAL_TO_OBJECT(funToCall), nsnull);
   if (NS_FAILED(rv) && rv != NS_ERROR_DOM_PROP_ACCESS_DENIED) {
     return ThrowException(rv, cx);
   }
 
 #ifdef DEBUG
   JSNative native = JS_GetFunctionNative(cx, fun);
   NS_ASSERTION(native, "How'd we get here with a scripted function?");
 #endif
@@ -580,17 +607,17 @@ XPC_XOW_AddProperty(JSContext *cx, JSObj
   }
 
   XPCCallContext ccx(JS_CALLER, cx);
   if (!ccx.IsValid()) {
     return ThrowException(NS_ERROR_FAILURE, cx);
   }
 
   JSBool privilegeEnabled = JS_FALSE;
-  nsresult rv = CanAccessWrapper(cx, wrappedObj, &privilegeEnabled);
+  nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, &privilegeEnabled);
   if (NS_FAILED(rv)) {
     if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) {
       // Can't override properties on foreign objects.
       return ThrowException(rv, cx);
     }
     return JS_FALSE;
   }
 
@@ -606,17 +633,17 @@ XPC_XOW_DelProperty(JSContext *cx, JSObj
     return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx);
   }
 
   XPCCallContext ccx(JS_CALLER, cx);
   if (!ccx.IsValid()) {
     return ThrowException(NS_ERROR_FAILURE, cx);
   }
 
-  nsresult rv = CanAccessWrapper(cx, wrappedObj, nsnull);
+  nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, nsnull);
   if (NS_FAILED(rv)) {
     if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) {
       // Can't delete properties on foreign objects.
       return ThrowException(rv, cx);
     }
     return JS_FALSE;
   }
 
@@ -657,17 +684,17 @@ XPC_XOW_GetOrSetProperty(JSContext *cx, 
   AUTO_MARK_JSVAL(ccx, vp);
 
   JSObject *wrappedObj = GetWrappedObject(cx, obj);
   if (!wrappedObj) {
     return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx);
   }
 
   JSBool privilegeEnabled;
-  nsresult rv = CanAccessWrapper(cx, wrappedObj, &privilegeEnabled);
+  nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, &privilegeEnabled);
   if (NS_FAILED(rv)) {
     if (rv != NS_ERROR_DOM_PROP_ACCESS_DENIED) {
       return JS_FALSE;
     }
 
     // This is a request to get a property across origins. We need to
     // determine if this property is allAccess. If it is, then we need to
     // actually get the property. If not, we simply need to throw an
@@ -769,17 +796,17 @@ XPC_XOW_Enumerate(JSContext *cx, JSObjec
     return JS_TRUE;
   }
 
   XPCCallContext ccx(JS_CALLER, cx);
   if (!ccx.IsValid()) {
     return ThrowException(NS_ERROR_FAILURE, cx);
   }
 
-  nsresult rv = CanAccessWrapper(cx, wrappedObj, nsnull);
+  nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, nsnull);
   if (NS_FAILED(rv)) {
     if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) {
       // Can't enumerate on foreign objects.
       return ThrowException(rv, cx);
     }
 
     return JS_FALSE;
   }
@@ -866,17 +893,17 @@ XPC_XOW_NewResolve(JSContext *cx, JSObje
   }
 
   XPCCallContext ccx(JS_CALLER, cx);
   if (!ccx.IsValid()) {
     return ThrowException(NS_ERROR_FAILURE, cx);
   }
 
   JSBool privilegeEnabled;
-  nsresult rv = CanAccessWrapper(cx, wrappedObj, &privilegeEnabled);
+  nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, &privilegeEnabled);
   if (NS_FAILED(rv)) {
     if (rv != NS_ERROR_DOM_PROP_ACCESS_DENIED) {
       return JS_FALSE;
     }
 
     // We're dealing with a cross-origin lookup. Ensure that we're allowed to
     // resolve this property and resolve it if so. Otherwise, we deny access
     // and throw a security error. Note that this code does not actually check
@@ -959,17 +986,17 @@ XPC_XOW_Convert(JSContext *cx, JSObject 
   }
 
   XPCCallContext ccx(JS_CALLER, cx);
   if (!ccx.IsValid()) {
     return ThrowException(NS_ERROR_FAILURE, cx);
   }
 
   // Note: JSTYPE_VOID and JSTYPE_STRING are equivalent.
-  nsresult rv = CanAccessWrapper(cx, wrappedObj, nsnull);
+  nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, nsnull);
   if (NS_FAILED(rv) &&
       (rv != NS_ERROR_DOM_PROP_ACCESS_DENIED ||
        (type != JSTYPE_STRING && type != JSTYPE_VOID))) {
     // Ensure that we report some kind of error.
     if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) {
       ThrowException(rv, cx);
     }
     return JS_FALSE;
@@ -1033,17 +1060,17 @@ XPC_XOW_Call(JSContext *cx, JSObject *ob
     return JS_TRUE;
   }
 
   XPCCallContext ccx(JS_CALLER, cx);
   if (!ccx.IsValid()) {
     return ThrowException(NS_ERROR_FAILURE, cx);
   }
 
-  nsresult rv = CanAccessWrapper(cx, wrappedObj, nsnull);
+  nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, nsnull);
   if (NS_FAILED(rv)) {
     if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) {
       // Can't call.
       return ThrowException(rv, cx);
     }
 
     return JS_FALSE;
   }
@@ -1070,17 +1097,17 @@ XPC_XOW_Construct(JSContext *cx, JSObjec
     return JS_TRUE;
   }
 
   XPCCallContext ccx(JS_CALLER, cx);
   if (!ccx.IsValid()) {
     return ThrowException(NS_ERROR_FAILURE, cx);
   }
 
-  nsresult rv = CanAccessWrapper(cx, wrappedObj, nsnull);
+  nsresult rv = CanAccessWrapper(cx, realObj, wrappedObj, nsnull);
   if (NS_FAILED(rv)) {
     if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) {
       // Can't construct.
       return ThrowException(rv, cx);
     }
     return JS_FALSE;
   }
 
@@ -1097,17 +1124,17 @@ XPC_XOW_HasInstance(JSContext *cx, JSObj
 {
   JSObject *iface = GetWrappedObject(cx, obj);
 
   XPCCallContext ccx(JS_CALLER, cx);
   if (!ccx.IsValid()) {
     return ThrowException(NS_ERROR_FAILURE, cx);
   }
 
-  nsresult rv = CanAccessWrapper(cx, iface, nsnull);
+  nsresult rv = CanAccessWrapper(cx, obj, iface, nsnull);
   if (NS_FAILED(rv)) {
     if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) {
       // Don't do this test across origins.
       return ThrowException(rv, cx);
     }
     return JS_FALSE;
   }
 
@@ -1183,17 +1210,17 @@ XPC_XOW_Iterator(JSContext *cx, JSObject
   }
 
   XPCCallContext ccx(JS_CALLER, cx);
   if (!ccx.IsValid()) {
     ThrowException(NS_ERROR_FAILURE, cx);
     return nsnull;
   }
 
-  nsresult rv = CanAccessWrapper(cx, wrappedObj, nsnull);
+  nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, nsnull);
   if (NS_FAILED(rv)) {
     if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) {
       // Can't create iterators for foreign objects.
       ThrowException(rv, cx);
       return nsnull;
     }
 
     ThrowException(NS_ERROR_FAILURE, cx);
@@ -1248,17 +1275,17 @@ XPC_XOW_toString(JSContext *cx, JSObject
     return JS_TRUE;
   }
 
   XPCCallContext ccx(JS_CALLER, cx);
   if (!ccx.IsValid()) {
     return ThrowException(NS_ERROR_FAILURE, cx);
   }
 
-  nsresult rv = CanAccessWrapper(cx, wrappedObj, nsnull);
+  nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, nsnull);
   if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) {
     nsIScriptSecurityManager *ssm = GetSecurityManager();
     if (!ssm) {
       return ThrowException(NS_ERROR_NOT_INITIALIZED, cx);
     }
     rv = ssm->CheckPropertyAccess(cx, wrappedObj,
                                   wrappedObj->getClass()->name,
                                   GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING),
--- a/js/src/xpconnect/src/XPCWrapper.cpp
+++ b/js/src/xpconnect/src/XPCWrapper.cpp
@@ -74,17 +74,17 @@ Unwrap(JSContext *cx, JSObject *wrapper)
 
     return wrappedObj->GetFlatJSObject();
   }
 
   if (clasp == &XPCSafeJSObjectWrapper::SJOWClass.base) {
     JSObject *wrappedObj =
       XPCSafeJSObjectWrapper::GetUnsafeObject(cx, wrapper);
 
-    if (NS_FAILED(XPCCrossOriginWrapper::CanAccessWrapper(cx, wrappedObj, nsnull))) {
+    if (NS_FAILED(XPCCrossOriginWrapper::CanAccessWrapper(cx, nsnull, wrappedObj, nsnull))) {
       JS_ClearPendingException(cx);
 
       return nsnull;
     }
 
     return wrappedObj;
   }
 
--- a/js/src/xpconnect/src/XPCWrapper.h
+++ b/js/src/xpconnect/src/XPCWrapper.h
@@ -87,17 +87,18 @@ RewrapIfNeeded(JSContext *cx, JSObject *
 JSBool
 WrapperMoved(JSContext *cx, XPCWrappedNative *innerObj,
              XPCWrappedNativeScope *newScope);
 
 // Returns 'true' if the current context is same-origin to the wrappedObject.
 // If we are "same origin" because UniversalXPConnect is enabled and
 // privilegeEnabled is non-null, then privilegeEnabled is set to true.
 nsresult
-CanAccessWrapper(JSContext *cx, JSObject *wrappedObj, JSBool *privilegeEnabled);
+CanAccessWrapper(JSContext *cx, JSObject *outerObj, JSObject *wrappedObj,
+                 JSBool *privilegeEnabled);
 
 // Some elements can change their principal or otherwise need XOWs, even
 // if they're same origin. This function returns 'true' if the element's
 // JSClass's name matches one such element.
 inline JSBool
 ClassNeedsXOW(const char *name)
 {
   switch (*name) {
@@ -349,39 +350,41 @@ UnwrapSOW(JSContext *cx, JSObject *wrapp
 }
 
 /**
  * Unwraps a XOW into its wrapped native.
  */
 inline JSObject *
 UnwrapXOW(JSContext *cx, JSObject *wrapper)
 {
-  wrapper = UnwrapGeneric(cx, &XPCCrossOriginWrapper::XOWClass, wrapper);
-  if (!wrapper) {
+  JSObject *innerObj =
+    UnwrapGeneric(cx, &XPCCrossOriginWrapper::XOWClass, wrapper);
+  if (!innerObj) {
     return nsnull;
   }
 
-  nsresult rv = XPCCrossOriginWrapper::CanAccessWrapper(cx, wrapper, nsnull);
+  nsresult rv =
+    XPCCrossOriginWrapper::CanAccessWrapper(cx, wrapper, innerObj, nsnull);
   if (NS_FAILED(rv)) {
     JS_ClearPendingException(cx);
-    wrapper = nsnull;
+    return nsnull;
   }
 
-  return wrapper;
+  return innerObj;
 }
 
 inline JSObject *
 UnwrapCOW(JSContext *cx, JSObject *wrapper)
 {
   wrapper = UnwrapGeneric(cx, &ChromeObjectWrapper::COWClass, wrapper);
   if (!wrapper) {
     return nsnull;
   }
 
-  nsresult rv = XPCCrossOriginWrapper::CanAccessWrapper(cx, wrapper, nsnull);
+  nsresult rv = XPCCrossOriginWrapper::CanAccessWrapper(cx, nsnull, wrapper, nsnull);
   if (NS_FAILED(rv)) {
     JS_ClearPendingException(cx);
     wrapper = nsnull;
   }
 
   return wrapper;
 }