From 4296a9ecc3242d4bfa5542237e81a1ba9da0017b Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Thu, 26 Jan 2012 14:55:27 +0100 Subject: [PATCH] Bug 723111 - Add an option to js::UnwrapObject so that it doesn't stop at outer windows when unwrapping. This is useful for cases where we are trying to find the wrapped native for a security wrapper. r=bzbarsky --- js/src/jscompartment.cpp | 2 +- js/src/jswrapper.cpp | 4 ++-- js/src/jswrapper.h | 9 ++++++++- js/src/shell/js.cpp | 2 +- js/xpconnect/src/XPCConvert.cpp | 2 +- js/xpconnect/src/XPCQuickStubs.cpp | 2 +- js/xpconnect/src/XPCWrapper.cpp | 4 ++-- js/xpconnect/src/XPCWrapper.h | 2 +- js/xpconnect/wrappers/AccessCheck.cpp | 12 +++++++++++- js/xpconnect/wrappers/WrapperFactory.h | 2 +- 10 files changed, 29 insertions(+), 12 deletions(-) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index d22ef17f6136..8ab058472f7d 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -216,7 +216,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp) /* Don't unwrap an outer window proxy. */ if (!obj->getClass()->ext.innerObject) { - obj = UnwrapObject(&vp->toObject(), &flags); + obj = UnwrapObject(&vp->toObject(), true, &flags); vp->setObject(*obj); if (obj->compartment() == this) return true; diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 567d004900c2..5d2c7156eb30 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -76,13 +76,13 @@ js::IsWrapper(const JSObject *wrapper) } JS_FRIEND_API(JSObject *) -js::UnwrapObject(JSObject *wrapped, uintN *flagsp) +js::UnwrapObject(JSObject *wrapped, bool stopAtOuter, uintN *flagsp) { uintN flags = 0; while (wrapped->isWrapper()) { flags |= static_cast(GetProxyHandler(wrapped))->flags(); wrapped = GetProxyPrivate(wrapped).toObjectOrNull(); - if (wrapped->getClass()->ext.innerObject) + if (stopAtOuter && wrapped->getClass()->ext.innerObject) break; } if (flagsp) diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index a86af286b9ba..e9ddd8fc7011 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -206,7 +206,14 @@ TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, J uintN flags); JS_FRIEND_API(bool) IsWrapper(const JSObject *obj); -JS_FRIEND_API(JSObject *) UnwrapObject(JSObject *obj, uintN *flagsp = NULL); + +// Given a JSObject, returns that object stripped of wrappers. If +// stopAtOuter is true, then this returns the outer window if it was +// previously wrapped. Otherwise, this returns the first object for +// which JSObject::isWrapper returns false. +JS_FRIEND_API(JSObject *) UnwrapObject(JSObject *obj, bool stopAtOuter = true, + uintN *flagsp = NULL); + bool IsCrossCompartmentWrapper(const JSObject *obj); } /* namespace js */ diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 638bca0fb9c2..1d94adb3bf0f 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3046,7 +3046,7 @@ EvalInContext(JSContext *cx, uintN argc, jsval *vp) { JSAutoEnterCompartment ac; uintN flags; - JSObject *unwrapped = UnwrapObject(sobj, &flags); + JSObject *unwrapped = UnwrapObject(sobj, true, &flags); if (flags & Wrapper::CROSS_COMPARTMENT) { sobj = unwrapped; if (!ac.enter(cx, sobj)) diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp index a105034d6a21..59b4d51c684f 100644 --- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -1220,7 +1220,7 @@ XPCConvert::JSObject2NativeInterface(XPCCallContext& ccx, // we aren't, throw an exception eagerly. JSObject* inner = nsnull; if (XPCWrapper::IsSecurityWrapper(src)) { - inner = XPCWrapper::Unwrap(cx, src); + inner = XPCWrapper::Unwrap(cx, src, false); if (!inner) { if (pErr) *pErr = NS_ERROR_XPC_SECURITY_MANAGER_VETO; diff --git a/js/xpconnect/src/XPCQuickStubs.cpp b/js/xpconnect/src/XPCQuickStubs.cpp index 4a651dca8cc7..2b79e145e1cd 100644 --- a/js/xpconnect/src/XPCQuickStubs.cpp +++ b/js/xpconnect/src/XPCQuickStubs.cpp @@ -788,7 +788,7 @@ getWrapper(JSContext *cx, XPCWrappedNativeTearOff **tearoff) { if (XPCWrapper::IsSecurityWrapper(obj) && - !(obj = XPCWrapper::Unwrap(cx, obj))) { + !(obj = XPCWrapper::Unwrap(cx, obj, false))) { return NS_ERROR_XPC_SECURITY_MANAGER_VETO; } diff --git a/js/xpconnect/src/XPCWrapper.cpp b/js/xpconnect/src/XPCWrapper.cpp index 9901c1b354d1..f66ed2e899be 100644 --- a/js/xpconnect/src/XPCWrapper.cpp +++ b/js/xpconnect/src/XPCWrapper.cpp @@ -123,12 +123,12 @@ AttachNewConstructorObject(XPCCallContext &ccx, JSObject *aGlobalObject) namespace XPCWrapper { JSObject * -Unwrap(JSContext *cx, JSObject *wrapper) +Unwrap(JSContext *cx, JSObject *wrapper, bool stopAtOuter) { if (js::IsWrapper(wrapper)) { if (xpc::AccessCheck::isScriptAccessOnly(cx, wrapper)) return nsnull; - return js::UnwrapObject(wrapper); + return js::UnwrapObject(wrapper, stopAtOuter); } return nsnull; diff --git a/js/xpconnect/src/XPCWrapper.h b/js/xpconnect/src/XPCWrapper.h index 8918e4466681..36c7205ae06a 100644 --- a/js/xpconnect/src/XPCWrapper.h +++ b/js/xpconnect/src/XPCWrapper.h @@ -93,7 +93,7 @@ IsSecurityWrapper(JSObject *wrapper) * exception on |cx|. */ JSObject * -Unwrap(JSContext *cx, JSObject *wrapper); +Unwrap(JSContext *cx, JSObject *wrapper, bool stopAtOuter = true); JSObject * UnsafeUnwrapSecurityWrapper(JSObject *obj); diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index c3a831b39503..540c3cf9a0ff 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -90,12 +90,22 @@ AccessCheck::isSameOrigin(JSCompartment *a, JSCompartment *b) bool AccessCheck::isLocationObjectSameOrigin(JSContext *cx, JSObject *wrapper) { + // Location objects are parented to the outer window for which they + // were created. This gives us an easy way to determine whether our + // object is same origin with the current inner window: + + // Grab the outer window... JSObject *obj = js::GetObjectParent(js::UnwrapObject(wrapper)); if (!js::GetObjectClass(obj)->ext.innerObject) { + // ...which might be wrapped in a security wrapper. obj = js::UnwrapObject(obj); JS_ASSERT(js::GetObjectClass(obj)->ext.innerObject); } + + // Now innerize it to find the *current* inner window for our outer. obj = JS_ObjectToInnerObject(cx, obj); + + // Which lets us compare the current compartment against the old one. return obj && (isSameOrigin(js::GetObjectCompartment(wrapper), js::GetObjectCompartment(obj)) || @@ -384,7 +394,7 @@ AccessCheck::isScriptAccessOnly(JSContext *cx, JSObject *wrapper) JS_ASSERT(js::IsWrapper(wrapper)); uintN flags; - JSObject *obj = js::UnwrapObject(wrapper, &flags); + JSObject *obj = js::UnwrapObject(wrapper, true, &flags); // If the wrapper indicates script-only access, we are done. if (flags & WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG) { diff --git a/js/xpconnect/wrappers/WrapperFactory.h b/js/xpconnect/wrappers/WrapperFactory.h index e33abca9f83c..1bda2340053d 100644 --- a/js/xpconnect/wrappers/WrapperFactory.h +++ b/js/xpconnect/wrappers/WrapperFactory.h @@ -53,7 +53,7 @@ class WrapperFactory { // Return true if any of any of the nested wrappers have the flag set. static bool HasWrapperFlag(JSObject *wrapper, uintN flag) { uintN flags = 0; - js::UnwrapObject(wrapper, &flags); + js::UnwrapObject(wrapper, true, &flags); return !!(flags & flag); }