Bug 658909 - Remove GWNOJO from XPCJSID. r=mrbkap
authorBobby Holley <bobbyholley@gmail.com>
Thu, 21 Mar 2013 08:20:44 -0700
changeset 125815 ddd198e0e288a9ed7f0c12cb009f4a3a0e2af09c
parent 125814 7149b64f78c974429d95822528634933eadaf7e0
child 125816 d8ac57d4452f8e0e77dbd6deba4b9607200a3c7e
push id24464
push useremorley@mozilla.com
push dateFri, 22 Mar 2013 14:00:12 +0000
treeherdermozilla-central@3825fdbcec62 [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)));
 }