author | Boris Zbarsky <bzbarsky@mit.edu> |
Tue, 26 Feb 2013 15:10:15 -0500 | |
changeset 123129 | ac3b7681470c502d3fe0c0be29385233f8d5e942 |
parent 123128 | 672ce8b62c32bd111aeecc0aa50f0e6012e29820 |
child 123130 | a3aaa9067e143f785b4762ad4ecb2699580a9010 |
push id | 24372 |
push user | emorley@mozilla.com |
push date | Wed, 27 Feb 2013 13:22:59 +0000 |
treeherder | mozilla-central@0a91da5f5eab [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | peterv |
bugs | 838686 |
milestone | 22.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
|
dom/bindings/CallbackObject.cpp | file | annotate | diff | comparison | revisions | |
dom/bindings/CallbackObject.h | file | annotate | diff | comparison | revisions |
--- a/dom/bindings/CallbackObject.cpp +++ b/dom/bindings/CallbackObject.cpp @@ -7,16 +7,17 @@ #include "mozilla/dom/CallbackObject.h" #include "jsfriendapi.h" #include "nsIScriptGlobalObject.h" #include "nsIXPConnect.h" #include "nsIScriptContext.h" #include "nsPIDOMWindow.h" #include "nsJSUtils.h" #include "nsIScriptSecurityManager.h" +#include "xpcprivate.h" namespace mozilla { namespace dom { NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CallbackObject) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END @@ -176,10 +177,44 @@ CallbackObject::CallSetup::~CallSetup() // Popping an nsCxPusher is safe even if it never got pushed. mCxPusher.Pop(); if (mCtx) { mCtx->ScriptEvaluated(true); } } +already_AddRefed<nsISupports> +CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback, + const nsIID& aIID) +{ + if (!aCallback) { + return nullptr; + } + + JSObject* callback = aCallback->Callback(); + + SafeAutoJSContext cx; + JSAutoCompartment ac(cx, callback); + XPCCallContext ccx(NATIVE_CALLER, cx); + if (!ccx.IsValid()) { + return nullptr; + } + + nsRefPtr<nsXPCWrappedJS> wrappedJS; + nsresult rv = + nsXPCWrappedJS::GetNewOrUsed(ccx, callback, aIID, + nullptr, getter_AddRefs(wrappedJS)); + if (NS_FAILED(rv) || !wrappedJS) { + return nullptr; + } + + nsCOMPtr<nsISupports> retval; + rv = wrappedJS->QueryInterface(aIID, getter_AddRefs(retval)); + if (NS_FAILED(rv)) { + return nullptr; + } + + return retval.forget(); +} + } // namespace dom } // namespace mozilla
--- a/dom/bindings/CallbackObject.h +++ b/dom/bindings/CallbackObject.h @@ -76,16 +76,29 @@ public: } JSObject* Callback() const { xpc_UnmarkGrayObject(mCallback); return mCallback; } + /* + * This getter does not change the color of the JSObject meaning that the + * object returned is not guaranteed to be kept alive past the next CC. + * + * This should only be called if you are certain that the return value won't + * be passed into a JS API function and that it won't be stored without being + * rooted (or otherwise signaling the stored value to the CC). + */ + JSObject* CallbackPreserveColor() const + { + return mCallback; + } + enum ExceptionHandling { eReportExceptions, eRethrowExceptions }; protected: explicit CallbackObject(CallbackObject* aCallbackFunction) : mCallback(aCallbackFunction->mCallback) @@ -159,12 +172,210 @@ protected: // An ErrorResult to possibly re-throw exceptions on and whether // we should re-throw them. ErrorResult& mErrorResult; const ExceptionHandling mExceptionHandling; uint32_t mSavedJSContextOptions; }; }; +template<class WebIDLCallbackT, class XPCOMCallbackT> +class CallbackObjectHolder; + +template<class T, class U> +inline void ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField); + +class CallbackObjectHolderBase +{ +protected: + // Returns null on all failures + already_AddRefed<nsISupports> ToXPCOMCallback(CallbackObject* aCallback, + const nsIID& aIID); +}; + +template<class WebIDLCallbackT, class XPCOMCallbackT> +class CallbackObjectHolder : CallbackObjectHolderBase +{ + /** + * A class which stores either a WebIDLCallbackT* or an XPCOMCallbackT*. Both + * types must inherit from nsISupports. The pointer that's stored can be + * null. + * + * When storing a WebIDLCallbackT*, mPtrBits is set to the pointer value. + * When storing an XPCOMCallbackT*, mPtrBits is the pointer value with low bit + * set. + */ +public: + explicit CallbackObjectHolder(WebIDLCallbackT* aCallback) + : mPtrBits(reinterpret_cast<uintptr_t>(aCallback)) + { + NS_IF_ADDREF(aCallback); + } + + explicit CallbackObjectHolder(XPCOMCallbackT* aCallback) + : mPtrBits(reinterpret_cast<uintptr_t>(aCallback) | XPCOMCallbackFlag) + { + NS_IF_ADDREF(aCallback); + } + + explicit CallbackObjectHolder(const CallbackObjectHolder& aOther) + : mPtrBits(aOther.mPtrBits) + { + NS_IF_ADDREF(GetISupports()); + } + + CallbackObjectHolder() + : mPtrBits(0) + {} + + ~CallbackObjectHolder() + { + UnlinkSelf(); + } + + nsISupports* GetISupports() const + { + return reinterpret_cast<nsISupports*>(mPtrBits & ~XPCOMCallbackFlag); + } + + // Even if HasWebIDLCallback returns true, GetWebIDLCallback() might still + // return null. + bool HasWebIDLCallback() const + { + return !(mPtrBits & XPCOMCallbackFlag); + } + + WebIDLCallbackT* GetWebIDLCallback() const + { + MOZ_ASSERT(HasWebIDLCallback()); + return reinterpret_cast<WebIDLCallbackT*>(mPtrBits); + } + + XPCOMCallbackT* GetXPCOMCallback() const + { + MOZ_ASSERT(!HasWebIDLCallback()); + return reinterpret_cast<XPCOMCallbackT*>(mPtrBits & ~XPCOMCallbackFlag); + } + + bool operator==(WebIDLCallbackT* aOtherCallback) const + { + if (!aOtherCallback) { + // If other is null, then we must be null to be equal. + return !GetISupports(); + } + + if (!HasWebIDLCallback() || !GetWebIDLCallback()) { + // If other is non-null, then we can't be equal if we have a + // non-WebIDL callback or a null callback. + return false; + } + + JSObject* thisObj = + js::UnwrapObject(GetWebIDLCallback()->CallbackPreserveColor()); + JSObject* otherObj = + js::UnwrapObject(aOtherCallback->CallbackPreserveColor()); + return thisObj == otherObj; + } + + bool operator==(XPCOMCallbackT* aOtherCallback) const + { + return (!aOtherCallback && !GetISupports()) || + (!HasWebIDLCallback() && GetXPCOMCallback() == aOtherCallback); + } + + bool operator==(const CallbackObjectHolder& aOtherCallback) const + { + if (aOtherCallback.HasWebIDLCallback()) { + return *this == aOtherCallback.GetWebIDLCallback(); + } + + return *this == aOtherCallback.GetXPCOMCallback(); + } + + // Try to return an XPCOMCallbackT version of this object. + already_AddRefed<XPCOMCallbackT> ToXPCOMCallback() + { + if (!HasWebIDLCallback()) { + nsRefPtr<XPCOMCallbackT> callback = GetXPCOMCallback(); + return callback.forget(); + } + + nsCOMPtr<nsISupports> supp = + CallbackObjectHolderBase::ToXPCOMCallback(GetWebIDLCallback(), + NS_GET_TEMPLATE_IID(XPCOMCallbackT)); + // ToXPCOMCallback already did the right QI for us. + return static_cast<XPCOMCallbackT*>(supp.forget().get()); + } + + // Try to return a WebIDLCallbackT version of this object. + already_AddRefed<WebIDLCallbackT> ToWebIDLCallback() + { + if (HasWebIDLCallback()) { + nsRefPtr<WebIDLCallbackT> callback = GetWebIDLCallback(); + return callback.forget(); + } + + XPCOMCallbackT* callback = GetXPCOMCallback(); + if (!callback) { + return nullptr; + } + + nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = do_QueryInterface(callback); + if (!wrappedJS) { + return nullptr; + } + + JSObject* obj; + if (NS_FAILED(wrappedJS->GetJSObject(&obj)) || !obj) { + return nullptr; + } + + SafeAutoJSContext cx; + JSAutoCompartment ac(cx, obj); + + bool inited; + nsRefPtr<WebIDLCallbackT> newCallback = + new WebIDLCallbackT(cx, nullptr, obj, &inited); + if (!inited) { + return nullptr; + } + return newCallback.forget(); + } + +private: + static const uintptr_t XPCOMCallbackFlag = 1u; + + friend inline void + ImplCycleCollectionUnlink<WebIDLCallbackT, + XPCOMCallbackT>(CallbackObjectHolder& aField); + + void UnlinkSelf() + { + // NS_IF_RELEASE because we might have been unlinked before + nsISupports* ptr = GetISupports(); + NS_IF_RELEASE(ptr); + mPtrBits = 0; + } + + uintptr_t mPtrBits; +}; + +template<class T, class U> +inline void +ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, + CallbackObjectHolder<T, U>& aField, + const char* aName, + uint32_t aFlags = 0) +{ + CycleCollectionNoteChild(aCallback, aField.GetISupports(), aName, aFlags); +} + +template<class T, class U> +inline void +ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField) +{ + aField.UnlinkSelf(); +} + } // namespace dom } // namespace mozilla #endif // mozilla_dom_CallbackObject_h