diff --git a/content/base/src/nsNodeUtils.cpp b/content/base/src/nsNodeUtils.cpp index f4a75a6c81a..14e7f429d38 100644 --- a/content/base/src/nsNodeUtils.cpp +++ b/content/base/src/nsNodeUtils.cpp @@ -597,29 +597,10 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep, if (aCx && wrapper) { nsIXPConnect *xpc = nsContentUtils::XPConnect(); if (xpc) { - JSObject *preservedWrapper = nsnull; - - // If reparenting moves us to a new compartment, preserving causes - // problems. In that case, we release ourselves and re-preserve after - // reparenting so we're sure to have the right JS object preserved. - // We use a JSObject stack copy of the wrapper to protect it from GC - // under ReparentWrappedNativeIfFound. - if (aNode->PreservingWrapper()) { - preservedWrapper = wrapper; - nsContentUtils::ReleaseWrapper(aNode, aNode); - NS_ASSERTION(aNode->GetWrapper(), - "ReleaseWrapper cleared our wrapper, this code needs to " - "be changed to deal with that!"); - } - nsCOMPtr oldWrapper; rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode, getter_AddRefs(oldWrapper)); - if (preservedWrapper) { - nsContentUtils::PreserveWrapper(aNode, aNode); - } - if (NS_FAILED(rv)) { aNode->mNodeInfo.swap(nodeInfo); diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index 26cf95789f4..4372f9d2a4c 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -53,6 +53,8 @@ #include "WrapperFactory.h" #include "dombindings.h" +#include "nsContentUtils.h" + #include "mozilla/Util.h" bool @@ -1479,6 +1481,43 @@ XPCWrappedNative::SystemIsBeingShutDown() /***************************************************************************/ +// If we have to transplant an object across compartments, we need to be +// careful if the underlying object implements nsWrapperCache and is preserving +// the wrapper. +// +// The class brackets a pair of Unpreserve/Preserve calls in the given scope. +// +// This class _must_ live on the stack, in part so that mPreservedWrapper is +// visible to the stack scanner. The caller wants the wrapper to be preserved, +// so we don't want it to get accidentally GCed. +class AutoWrapperChanger NS_STACK_CLASS { +public: + AutoWrapperChanger() : mCache(nsnull) + , mCOMObj(nsnull) + , mPreservedWrapper(nsnull) + {} + + void init(nsISupports* aCOMObj, nsWrapperCache* aWrapperCache) { + mCOMObj = aCOMObj; + mCache = aWrapperCache; + if (mCache->PreservingWrapper()) { + mPreservedWrapper = mCache->GetWrapper(); + MOZ_ASSERT(mPreservedWrapper); + nsContentUtils::ReleaseWrapper(mCOMObj, mCache); + } + } + + ~AutoWrapperChanger() { + if (mPreservedWrapper) + nsContentUtils::PreserveWrapper(mCOMObj, mCache); + } + +private: + nsWrapperCache* mCache; + nsISupports* mCOMObj; + JSObject* mPreservedWrapper; +}; + // static nsresult XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx, @@ -1497,10 +1536,16 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx, nsresult rv; nsRefPtr wrapper; + AutoWrapperChanger wrapperChanger; JSObject *flat; nsWrapperCache* cache = nsnull; CallQueryInterface(aCOMObj, &cache); if (cache) { + + // There's a wrapper cache. Make sure we keep it sane no matter what + // happens. + wrapperChanger.init(aCOMObj, cache); + flat = cache->GetWrapper(); if (flat && !IS_SLIM_WRAPPER_OBJECT(flat)) { wrapper = static_cast(xpc_GetJSPrivate(flat));