Bug 658909 - Remove GWNOJO from XPCJSID. r=mrbkap
authorBobby Holley <bobbyholley@gmail.com>
Sat, 16 Mar 2013 22:58:15 -0700
changeset 125063 218ca8a31f9af7d8c00e09fddc2641e690ce1fa4
parent 125062 2b42cac96ee70d611e27a130f617eeaa3cf3ff45
child 125064 519f06cae45467bb81195a608319f9d52db3c2ef
push id24762
push userbobbyholley@gmail.com
push dateSun, 17 Mar 2013 05:58:40 +0000
treeherdermozilla-inbound@3a5f73a4f816 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs658909
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 658909 - Remove GWNOJO from XPCJSID. r=mrbkap
js/xpconnect/src/XPCJSID.cpp
--- a/js/xpconnect/src/XPCJSID.cpp
+++ b/js/xpconnect/src/XPCJSID.cpp
@@ -442,65 +442,98 @@ nsJSIID::Enumerate(nsIXPConnectWrappedNa
         if (member && member->IsConstant() &&
             !xpc_ForcePropertyResolve(cx, obj, member->GetName())) {
             return NS_ERROR_UNEXPECTED;
         }
     }
     return NS_OK;
 }
 
+/*
+ * HasInstance hooks need to find an appropriate reflector in order to function
+ * properly. There are two complexities that we need to handle:
+ *
+ * 1 - Cross-compartment wrappers. Chrome uses over 100 compartments, all with
+ *     system principal. The success of an instanceof check should not depend
+ *     on which compartment an object comes from. At the same time, we want to
+ *     make sure we don't unwrap important security wrappers.
+ *     UnwrapObjectChecked does the right thing here.
+ *
+ * 2 - Prototype chains. Suppose someone creates a vanilla JS object |a| and
+ *     sets its __proto__ to some WN |b|. If |b instanceof nsIFoo| returns true,
+ *     one would expect |a instanceof nsIFoo| to return true as well, since
+ *     instanceof is transitive up the prototype chain in ECMAScript. Moreover,
+ *     there's chrome code that relies on this.
+ *
+ * This static method handles both complexities, returning either an XPCWN, a
+ * slim wrapper, a DOM object, or null. The object may well be cross-compartment
+ * from |cx|.
+ */
+static JSObject *
+FindObjectForHasInstance(JSContext *cx, JSObject *obj)
+{
+    while (obj && !IS_WRAPPER_CLASS(js::GetObjectClass(obj)) &&
+           !mozilla::dom::IsDOMObject(obj))
+    {
+        if (js::IsWrapper(obj))
+            obj = js::UnwrapObjectChecked(obj, /* stopAtOuter = */ false);
+        else if (!js::GetObjectProto(cx, obj, &obj))
+            return nullptr;
+    }
+    return obj;
+}
+
+
 /* bool hasInstance (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval val, out bool bp); */
 NS_IMETHODIMP
 nsJSIID::HasInstance(nsIXPConnectWrappedNative *wrapper,
                      JSContext * cx, JSObject * obj,
                      const jsval &val, bool *bp, bool *_retval)
 {
     *bp = false;
     nsresult rv = NS_OK;
 
     if (!JSVAL_IS_PRIMITIVE(val)) {
         // we have a JSObject
         JSObject* obj = JSVAL_TO_OBJECT(val);
 
         NS_ASSERTION(obj, "when is an object not an object?");
 
+        nsISupports *identity = nullptr;
         // is this really a native xpcom object with a wrapper?
         const nsIID* iid;
         mInfo->GetIIDShared(&iid);
 
+        obj = FindObjectForHasInstance(cx, obj);
+        if (!obj)
+            return NS_OK;
+
         if (IS_SLIM_WRAPPER(obj)) {
             XPCWrappedNativeProto* proto = GetSlimWrapperProto(obj);
             if (proto->GetSet()->HasInterfaceWithAncestor(iid)) {
                 *bp = true;
                 return NS_OK;
             }
 
 #ifdef DEBUG_slimwrappers
             char foo[NSID_LENGTH];
             iid->ToProvidedString(foo);
             SLIM_LOG_WILL_MORPH_FOR_PROP(cx, obj, foo);
 #endif
             if (!MorphSlimWrapper(cx, obj))
                 return NS_ERROR_FAILURE;
-        } else {
-            JSObject* unsafeObj =
-                XPCWrapper::Unwrap(cx, obj, /* stopAtOuter = */ false);
-            JSObject* cur = unsafeObj ? unsafeObj : obj;
-            nsISupports *identity;
-            if (mozilla::dom::UnwrapDOMObjectToISupports(cur, identity)) {
-                nsCOMPtr<nsISupports> supp;
-                identity->QueryInterface(*iid, getter_AddRefs(supp));
-                *bp = supp;
-                return NS_OK;
-            }
+        } else if (mozilla::dom::UnwrapDOMObjectToISupports(obj, identity)) {
+              nsCOMPtr<nsISupports> supp;
+              identity->QueryInterface(*iid, getter_AddRefs(supp));
+              *bp = supp;
+              return NS_OK;
         }
 
-        XPCWrappedNative* other_wrapper =
-           XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj);
-
+        MOZ_ASSERT(IS_WN_WRAPPER(obj));
+        XPCWrappedNative* other_wrapper = XPCWrappedNative::Get(obj);
         if (!other_wrapper)
             return NS_OK;
 
         // We'll trust the interface set of the wrapper if this is known
         // to be an interface that the objects *expects* to be able to
         // handle.
         if (other_wrapper->HasInterfaceNoQI(*iid)) {
             *bp = true;
@@ -811,26 +844,24 @@ nsJSCID::HasInstance(nsIXPConnectWrapped
 
     if (!JSVAL_IS_PRIMITIVE(val)) {
         // we have a JSObject
         JSObject* obj = JSVAL_TO_OBJECT(val);
 
         NS_ASSERTION(obj, "when is an object not an object?");
 
         // is this really a native xpcom object with a wrapper?
-        JSObject* obj2;
-        XPCWrappedNative* other_wrapper =
-           XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj, nullptr, &obj2);
-
-        if (!other_wrapper && !obj2)
-            return NS_OK;
-
-        nsIClassInfo* ci = other_wrapper ?
-                           other_wrapper->GetClassInfo() :
-                           GetSlimWrapperProto(obj2)->GetClassInfo();
+        nsIClassInfo* ci = nullptr;
+        obj = FindObjectForHasInstance(cx, obj);
+        if (!obj || !IS_WRAPPER_CLASS(js::GetObjectClass(obj)))
+            return rv;
+        if (IS_SLIM_WRAPPER_OBJECT(obj))
+            ci = GetSlimWrapperProto(obj)->GetClassInfo();
+        else if (XPCWrappedNative* other_wrapper = XPCWrappedNative::Get(obj))
+            ci = other_wrapper->GetClassInfo();
 
         // We consider CID equality to be the thing that matters here.
         // This is perhaps debatable.
         if (ci) {
             nsID cid;
             if (NS_SUCCEEDED(ci->GetClassIDNoAlloc(&cid)))
                 *bp = cid.Equals(mDetails.ID());
         }
@@ -867,33 +898,37 @@ xpc_NewIDObject(JSContext *cx, JSObject*
 // note: returned pointer is only valid while |obj| remains alive!
 const nsID*
 xpc_JSObjectToID(JSContext *cx, JSObject* obj)
 {
     if (!cx || !obj)
         return nullptr;
 
     // NOTE: this call does NOT addref
-    XPCWrappedNative* wrapper =
-        XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj);
+    XPCWrappedNative* wrapper = nullptr;
+    obj = js::UnwrapObjectChecked(obj);
+    if (obj && IS_WN_WRAPPER(obj))
+        wrapper = XPCWrappedNative::Get(obj);
     if (wrapper &&
         (wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSID))  ||
          wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSIID)) ||
          wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSCID)))) {
         return ((nsIJSID*)wrapper->GetIdentityObject())->GetID();
     }
     return nullptr;
 }
 
 JSBool
 xpc_JSObjectIsID(JSContext *cx, JSObject* obj)
 {
     NS_ASSERTION(cx && obj, "bad param");
     // NOTE: this call does NOT addref
-    XPCWrappedNative* wrapper =
-        XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj);
+    XPCWrappedNative* wrapper = nullptr;
+    obj = js::UnwrapObjectChecked(obj);
+    if (obj && IS_WN_WRAPPER(obj))
+        wrapper = XPCWrappedNative::Get(obj);
     return wrapper &&
            (wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSID))  ||
             wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSIID)) ||
             wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSCID)));
 }