1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/bindings/CallbackObject.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,419 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/** 1.11 + * A common base class for representing WebIDL callback function and 1.12 + * callback interface types in C++. 1.13 + * 1.14 + * This class implements common functionality like lifetime 1.15 + * management, initialization with the JS object, and setup of the 1.16 + * call environment. Subclasses are responsible for providing methods 1.17 + * that do the call into JS as needed. 1.18 + */ 1.19 + 1.20 +#ifndef mozilla_dom_CallbackObject_h 1.21 +#define mozilla_dom_CallbackObject_h 1.22 + 1.23 +#include "nsISupports.h" 1.24 +#include "nsISupportsImpl.h" 1.25 +#include "nsCycleCollectionParticipant.h" 1.26 +#include "jswrapper.h" 1.27 +#include "mozilla/Assertions.h" 1.28 +#include "mozilla/ErrorResult.h" 1.29 +#include "mozilla/HoldDropJSObjects.h" 1.30 +#include "mozilla/MemoryReporting.h" 1.31 +#include "mozilla/dom/ScriptSettings.h" 1.32 +#include "nsContentUtils.h" 1.33 +#include "nsWrapperCache.h" 1.34 +#include "nsJSEnvironment.h" 1.35 +#include "xpcpublic.h" 1.36 + 1.37 +namespace mozilla { 1.38 +namespace dom { 1.39 + 1.40 +#define DOM_CALLBACKOBJECT_IID \ 1.41 +{ 0xbe74c190, 0x6d76, 0x4991, \ 1.42 + { 0x84, 0xb9, 0x65, 0x06, 0x99, 0xe6, 0x93, 0x2b } } 1.43 + 1.44 +class CallbackObject : public nsISupports 1.45 +{ 1.46 +public: 1.47 + NS_DECLARE_STATIC_IID_ACCESSOR(DOM_CALLBACKOBJECT_IID) 1.48 + 1.49 + NS_DECL_CYCLE_COLLECTING_ISUPPORTS 1.50 + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CallbackObject) 1.51 + 1.52 + // The caller may pass a global object which will act as an override for the 1.53 + // incumbent script settings object when the callback is invoked (overriding 1.54 + // the entry point computed from aCallback). If no override is required, the 1.55 + // caller should pass null. 1.56 + explicit CallbackObject(JS::Handle<JSObject*> aCallback, nsIGlobalObject *aIncumbentGlobal) 1.57 + { 1.58 + Init(aCallback, aIncumbentGlobal); 1.59 + } 1.60 + 1.61 + virtual ~CallbackObject() 1.62 + { 1.63 + DropJSObjects(); 1.64 + } 1.65 + 1.66 + JS::Handle<JSObject*> Callback() const 1.67 + { 1.68 + JS::ExposeObjectToActiveJS(mCallback); 1.69 + return CallbackPreserveColor(); 1.70 + } 1.71 + 1.72 + /* 1.73 + * This getter does not change the color of the JSObject meaning that the 1.74 + * object returned is not guaranteed to be kept alive past the next CC. 1.75 + * 1.76 + * This should only be called if you are certain that the return value won't 1.77 + * be passed into a JS API function and that it won't be stored without being 1.78 + * rooted (or otherwise signaling the stored value to the CC). 1.79 + */ 1.80 + JS::Handle<JSObject*> CallbackPreserveColor() const 1.81 + { 1.82 + // Calling fromMarkedLocation() is safe because we trace our mCallback, and 1.83 + // because the value of mCallback cannot change after if has been set. 1.84 + return JS::Handle<JSObject*>::fromMarkedLocation(mCallback.address()); 1.85 + } 1.86 + 1.87 + nsIGlobalObject* IncumbentGlobalOrNull() const 1.88 + { 1.89 + return mIncumbentGlobal; 1.90 + } 1.91 + 1.92 + enum ExceptionHandling { 1.93 + // Report any exception and don't throw it to the caller code. 1.94 + eReportExceptions, 1.95 + // Throw an exception to the caller code if the thrown exception is a 1.96 + // binding object for a DOMError from the caller's scope, otherwise report 1.97 + // it. 1.98 + eRethrowContentExceptions, 1.99 + // Throw any exception to the caller code. 1.100 + eRethrowExceptions 1.101 + }; 1.102 + 1.103 + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const 1.104 + { 1.105 + return aMallocSizeOf(this); 1.106 + } 1.107 + 1.108 +protected: 1.109 + explicit CallbackObject(CallbackObject* aCallbackObject) 1.110 + { 1.111 + Init(aCallbackObject->mCallback, aCallbackObject->mIncumbentGlobal); 1.112 + } 1.113 + 1.114 + bool operator==(const CallbackObject& aOther) const 1.115 + { 1.116 + JSObject* thisObj = 1.117 + js::UncheckedUnwrap(CallbackPreserveColor()); 1.118 + JSObject* otherObj = 1.119 + js::UncheckedUnwrap(aOther.CallbackPreserveColor()); 1.120 + return thisObj == otherObj; 1.121 + } 1.122 + 1.123 +private: 1.124 + inline void Init(JSObject* aCallback, nsIGlobalObject* aIncumbentGlobal) 1.125 + { 1.126 + MOZ_ASSERT(aCallback && !mCallback); 1.127 + // Set script objects before we hold, on the off chance that a GC could 1.128 + // somehow happen in there... (which would be pretty odd, granted). 1.129 + mCallback = aCallback; 1.130 + if (aIncumbentGlobal) { 1.131 + mIncumbentGlobal = aIncumbentGlobal; 1.132 + mIncumbentJSGlobal = aIncumbentGlobal->GetGlobalJSObject(); 1.133 + } 1.134 + mozilla::HoldJSObjects(this); 1.135 + } 1.136 + 1.137 + CallbackObject(const CallbackObject&) MOZ_DELETE; 1.138 + CallbackObject& operator =(const CallbackObject&) MOZ_DELETE; 1.139 + 1.140 +protected: 1.141 + void DropJSObjects() 1.142 + { 1.143 + MOZ_ASSERT_IF(mIncumbentJSGlobal, mCallback); 1.144 + if (mCallback) { 1.145 + mCallback = nullptr; 1.146 + mIncumbentJSGlobal = nullptr; 1.147 + mozilla::DropJSObjects(this); 1.148 + } 1.149 + } 1.150 + 1.151 + JS::Heap<JSObject*> mCallback; 1.152 + // Ideally, we'd just hold a reference to the nsIGlobalObject, since that's 1.153 + // what we need to pass to AutoIncumbentScript. Unfortunately, that doesn't 1.154 + // hold the actual JS global alive. So we maintain an additional pointer to 1.155 + // the JS global itself so that we can trace it. 1.156 + // 1.157 + // At some point we should consider trying to make native globals hold their 1.158 + // scripted global alive, at which point we can get rid of the duplication 1.159 + // here. 1.160 + nsCOMPtr<nsIGlobalObject> mIncumbentGlobal; 1.161 + JS::TenuredHeap<JSObject*> mIncumbentJSGlobal; 1.162 + 1.163 + class MOZ_STACK_CLASS CallSetup 1.164 + { 1.165 + /** 1.166 + * A class that performs whatever setup we need to safely make a 1.167 + * call while this class is on the stack, After the constructor 1.168 + * returns, the call is safe to make if GetContext() returns 1.169 + * non-null. 1.170 + */ 1.171 + public: 1.172 + // If aExceptionHandling == eRethrowContentExceptions then aCompartment 1.173 + // needs to be set to the compartment in which exceptions will be rethrown. 1.174 + CallSetup(CallbackObject* aCallback, ErrorResult& aRv, 1.175 + ExceptionHandling aExceptionHandling, 1.176 + JSCompartment* aCompartment = nullptr, 1.177 + bool aIsJSImplementedWebIDL = false); 1.178 + ~CallSetup(); 1.179 + 1.180 + JSContext* GetContext() const 1.181 + { 1.182 + return mCx; 1.183 + } 1.184 + 1.185 + private: 1.186 + // We better not get copy-constructed 1.187 + CallSetup(const CallSetup&) MOZ_DELETE; 1.188 + 1.189 + bool ShouldRethrowException(JS::Handle<JS::Value> aException); 1.190 + 1.191 + // Members which can go away whenever 1.192 + JSContext* mCx; 1.193 + 1.194 + // Caller's compartment. This will only have a sensible value if 1.195 + // mExceptionHandling == eRethrowContentExceptions. 1.196 + JSCompartment* mCompartment; 1.197 + 1.198 + // And now members whose construction/destruction order we need to control. 1.199 + Maybe<AutoEntryScript> mAutoEntryScript; 1.200 + Maybe<AutoIncumbentScript> mAutoIncumbentScript; 1.201 + 1.202 + // Constructed the rooter within the scope of mCxPusher above, so that it's 1.203 + // always within a request during its lifetime. 1.204 + Maybe<JS::Rooted<JSObject*> > mRootedCallable; 1.205 + 1.206 + // Can't construct a JSAutoCompartment without a JSContext either. Also, 1.207 + // Put mAc after mAutoEntryScript so that we exit the compartment before 1.208 + // we pop the JSContext. Though in practice we'll often manually order 1.209 + // those two things. 1.210 + Maybe<JSAutoCompartment> mAc; 1.211 + 1.212 + // An ErrorResult to possibly re-throw exceptions on and whether 1.213 + // we should re-throw them. 1.214 + ErrorResult& mErrorResult; 1.215 + const ExceptionHandling mExceptionHandling; 1.216 + JS::ContextOptions mSavedJSContextOptions; 1.217 + const bool mIsMainThread; 1.218 + }; 1.219 +}; 1.220 + 1.221 +template<class WebIDLCallbackT, class XPCOMCallbackT> 1.222 +class CallbackObjectHolder; 1.223 + 1.224 +template<class T, class U> 1.225 +void ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField); 1.226 + 1.227 +class CallbackObjectHolderBase 1.228 +{ 1.229 +protected: 1.230 + // Returns null on all failures 1.231 + already_AddRefed<nsISupports> ToXPCOMCallback(CallbackObject* aCallback, 1.232 + const nsIID& aIID) const; 1.233 +}; 1.234 + 1.235 +template<class WebIDLCallbackT, class XPCOMCallbackT> 1.236 +class CallbackObjectHolder : CallbackObjectHolderBase 1.237 +{ 1.238 + /** 1.239 + * A class which stores either a WebIDLCallbackT* or an XPCOMCallbackT*. Both 1.240 + * types must inherit from nsISupports. The pointer that's stored can be 1.241 + * null. 1.242 + * 1.243 + * When storing a WebIDLCallbackT*, mPtrBits is set to the pointer value. 1.244 + * When storing an XPCOMCallbackT*, mPtrBits is the pointer value with low bit 1.245 + * set. 1.246 + */ 1.247 +public: 1.248 + explicit CallbackObjectHolder(WebIDLCallbackT* aCallback) 1.249 + : mPtrBits(reinterpret_cast<uintptr_t>(aCallback)) 1.250 + { 1.251 + NS_IF_ADDREF(aCallback); 1.252 + } 1.253 + 1.254 + explicit CallbackObjectHolder(XPCOMCallbackT* aCallback) 1.255 + : mPtrBits(reinterpret_cast<uintptr_t>(aCallback) | XPCOMCallbackFlag) 1.256 + { 1.257 + NS_IF_ADDREF(aCallback); 1.258 + } 1.259 + 1.260 + explicit CallbackObjectHolder(const CallbackObjectHolder& aOther) 1.261 + : mPtrBits(aOther.mPtrBits) 1.262 + { 1.263 + NS_IF_ADDREF(GetISupports()); 1.264 + } 1.265 + 1.266 + CallbackObjectHolder() 1.267 + : mPtrBits(0) 1.268 + {} 1.269 + 1.270 + ~CallbackObjectHolder() 1.271 + { 1.272 + UnlinkSelf(); 1.273 + } 1.274 + 1.275 + void operator=(WebIDLCallbackT* aCallback) 1.276 + { 1.277 + UnlinkSelf(); 1.278 + mPtrBits = reinterpret_cast<uintptr_t>(aCallback); 1.279 + NS_IF_ADDREF(aCallback); 1.280 + } 1.281 + 1.282 + void operator=(XPCOMCallbackT* aCallback) 1.283 + { 1.284 + UnlinkSelf(); 1.285 + mPtrBits = reinterpret_cast<uintptr_t>(aCallback) | XPCOMCallbackFlag; 1.286 + NS_IF_ADDREF(aCallback); 1.287 + } 1.288 + 1.289 + void operator=(const CallbackObjectHolder& aOther) 1.290 + { 1.291 + UnlinkSelf(); 1.292 + mPtrBits = aOther.mPtrBits; 1.293 + NS_IF_ADDREF(GetISupports()); 1.294 + } 1.295 + 1.296 + nsISupports* GetISupports() const 1.297 + { 1.298 + return reinterpret_cast<nsISupports*>(mPtrBits & ~XPCOMCallbackFlag); 1.299 + } 1.300 + 1.301 + // Boolean conversion operator so people can use this in boolean tests 1.302 + operator bool() const 1.303 + { 1.304 + return GetISupports(); 1.305 + } 1.306 + 1.307 + // Even if HasWebIDLCallback returns true, GetWebIDLCallback() might still 1.308 + // return null. 1.309 + bool HasWebIDLCallback() const 1.310 + { 1.311 + return !(mPtrBits & XPCOMCallbackFlag); 1.312 + } 1.313 + 1.314 + WebIDLCallbackT* GetWebIDLCallback() const 1.315 + { 1.316 + MOZ_ASSERT(HasWebIDLCallback()); 1.317 + return reinterpret_cast<WebIDLCallbackT*>(mPtrBits); 1.318 + } 1.319 + 1.320 + XPCOMCallbackT* GetXPCOMCallback() const 1.321 + { 1.322 + MOZ_ASSERT(!HasWebIDLCallback()); 1.323 + return reinterpret_cast<XPCOMCallbackT*>(mPtrBits & ~XPCOMCallbackFlag); 1.324 + } 1.325 + 1.326 + bool operator==(WebIDLCallbackT* aOtherCallback) const 1.327 + { 1.328 + if (!aOtherCallback) { 1.329 + // If other is null, then we must be null to be equal. 1.330 + return !GetISupports(); 1.331 + } 1.332 + 1.333 + if (!HasWebIDLCallback() || !GetWebIDLCallback()) { 1.334 + // If other is non-null, then we can't be equal if we have a 1.335 + // non-WebIDL callback or a null callback. 1.336 + return false; 1.337 + } 1.338 + 1.339 + return *GetWebIDLCallback() == *aOtherCallback; 1.340 + } 1.341 + 1.342 + bool operator==(XPCOMCallbackT* aOtherCallback) const 1.343 + { 1.344 + return (!aOtherCallback && !GetISupports()) || 1.345 + (!HasWebIDLCallback() && GetXPCOMCallback() == aOtherCallback); 1.346 + } 1.347 + 1.348 + bool operator==(const CallbackObjectHolder& aOtherCallback) const 1.349 + { 1.350 + if (aOtherCallback.HasWebIDLCallback()) { 1.351 + return *this == aOtherCallback.GetWebIDLCallback(); 1.352 + } 1.353 + 1.354 + return *this == aOtherCallback.GetXPCOMCallback(); 1.355 + } 1.356 + 1.357 + // Try to return an XPCOMCallbackT version of this object. 1.358 + already_AddRefed<XPCOMCallbackT> ToXPCOMCallback() const 1.359 + { 1.360 + if (!HasWebIDLCallback()) { 1.361 + nsRefPtr<XPCOMCallbackT> callback = GetXPCOMCallback(); 1.362 + return callback.forget(); 1.363 + } 1.364 + 1.365 + nsCOMPtr<nsISupports> supp = 1.366 + CallbackObjectHolderBase::ToXPCOMCallback(GetWebIDLCallback(), 1.367 + NS_GET_TEMPLATE_IID(XPCOMCallbackT)); 1.368 + // ToXPCOMCallback already did the right QI for us. 1.369 + return supp.forget().downcast<XPCOMCallbackT>(); 1.370 + } 1.371 + 1.372 + // Try to return a WebIDLCallbackT version of this object. 1.373 + already_AddRefed<WebIDLCallbackT> ToWebIDLCallback() const 1.374 + { 1.375 + if (HasWebIDLCallback()) { 1.376 + nsRefPtr<WebIDLCallbackT> callback = GetWebIDLCallback(); 1.377 + return callback.forget(); 1.378 + } 1.379 + return nullptr; 1.380 + } 1.381 + 1.382 +private: 1.383 + static const uintptr_t XPCOMCallbackFlag = 1u; 1.384 + 1.385 + friend void 1.386 + ImplCycleCollectionUnlink<WebIDLCallbackT, 1.387 + XPCOMCallbackT>(CallbackObjectHolder& aField); 1.388 + 1.389 + void UnlinkSelf() 1.390 + { 1.391 + // NS_IF_RELEASE because we might have been unlinked before 1.392 + nsISupports* ptr = GetISupports(); 1.393 + NS_IF_RELEASE(ptr); 1.394 + mPtrBits = 0; 1.395 + } 1.396 + 1.397 + uintptr_t mPtrBits; 1.398 +}; 1.399 + 1.400 +NS_DEFINE_STATIC_IID_ACCESSOR(CallbackObject, DOM_CALLBACKOBJECT_IID) 1.401 + 1.402 +template<class T, class U> 1.403 +inline void 1.404 +ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, 1.405 + CallbackObjectHolder<T, U>& aField, 1.406 + const char* aName, 1.407 + uint32_t aFlags = 0) 1.408 +{ 1.409 + CycleCollectionNoteChild(aCallback, aField.GetISupports(), aName, aFlags); 1.410 +} 1.411 + 1.412 +template<class T, class U> 1.413 +void 1.414 +ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField) 1.415 +{ 1.416 + aField.UnlinkSelf(); 1.417 +} 1.418 + 1.419 +} // namespace dom 1.420 +} // namespace mozilla 1.421 + 1.422 +#endif // mozilla_dom_CallbackObject_h