diff --git a/js/xpconnect/src/XPCJSID.cpp b/js/xpconnect/src/XPCJSID.cpp index 6970676adf9..2ccbbaaa50e 100644 --- a/js/xpconnect/src/XPCJSID.cpp +++ b/js/xpconnect/src/XPCJSID.cpp @@ -447,6 +447,41 @@ nsJSIID::Enumerate(nsIXPConnectWrappedNative *wrapper, 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, @@ -462,10 +497,15 @@ nsJSIID::HasInstance(nsIXPConnectWrappedNative *wrapper, 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)) { @@ -480,22 +520,15 @@ nsJSIID::HasInstance(nsIXPConnectWrappedNative *wrapper, #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 supp; - identity->QueryInterface(*iid, getter_AddRefs(supp)); - *bp = supp; - return NS_OK; - } + } else if (mozilla::dom::UnwrapDOMObjectToISupports(obj, identity)) { + nsCOMPtr 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; @@ -816,16 +849,14 @@ nsJSCID::HasInstance(nsIXPConnectWrappedNative *wrapper, 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. @@ -872,8 +903,10 @@ xpc_JSObjectToID(JSContext *cx, JSObject* 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)) || @@ -888,8 +921,10 @@ 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)) ||