CallbackObject.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /**
  6. * A common base class for representing WebIDL callback function and
  7. * callback interface types in C++.
  8. *
  9. * This class implements common functionality like lifetime
  10. * management, initialization with the JS object, and setup of the
  11. * call environment. Subclasses are responsible for providing methods
  12. * that do the call into JS as needed.
  13. */
  14. #ifndef mozilla_dom_CallbackObject_h
  15. #define mozilla_dom_CallbackObject_h
  16. #include "nsISupports.h"
  17. #include "nsISupportsImpl.h"
  18. #include "nsCycleCollectionParticipant.h"
  19. #include "jswrapper.h"
  20. #include "mozilla/Assertions.h"
  21. #include "mozilla/ErrorResult.h"
  22. #include "mozilla/HoldDropJSObjects.h"
  23. #include "mozilla/MemoryReporting.h"
  24. #include "mozilla/OwningNonNull.h"
  25. #include "mozilla/dom/ScriptSettings.h"
  26. #include "nsWrapperCache.h"
  27. #include "nsJSEnvironment.h"
  28. #include "xpcpublic.h"
  29. #include "jsapi.h"
  30. #include "js/TracingAPI.h"
  31. namespace mozilla {
  32. namespace dom {
  33. #define DOM_CALLBACKOBJECT_IID \
  34. { 0xbe74c190, 0x6d76, 0x4991, \
  35. { 0x84, 0xb9, 0x65, 0x06, 0x99, 0xe6, 0x93, 0x2b } }
  36. class CallbackObject : public nsISupports
  37. {
  38. public:
  39. NS_DECLARE_STATIC_IID_ACCESSOR(DOM_CALLBACKOBJECT_IID)
  40. NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  41. NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CallbackObject)
  42. // The caller may pass a global object which will act as an override for the
  43. // incumbent script settings object when the callback is invoked (overriding
  44. // the entry point computed from aCallback). If no override is required, the
  45. // caller should pass null. |aCx| is used to capture the current
  46. // stack, which is later used as an async parent when the callback
  47. // is invoked. aCx can be nullptr, in which case no stack is
  48. // captured.
  49. explicit CallbackObject(JSContext* aCx, JS::Handle<JSObject*> aCallback,
  50. nsIGlobalObject* aIncumbentGlobal)
  51. {
  52. if (aCx && JS::ContextOptionsRef(aCx).asyncStack()) {
  53. JS::RootedObject stack(aCx);
  54. if (!JS::CaptureCurrentStack(aCx, &stack)) {
  55. JS_ClearPendingException(aCx);
  56. }
  57. Init(aCallback, stack, aIncumbentGlobal);
  58. } else {
  59. Init(aCallback, nullptr, aIncumbentGlobal);
  60. }
  61. }
  62. // Instead of capturing the current stack to use as an async parent when the
  63. // callback is invoked, the caller can use this overload to pass in a stack
  64. // for that purpose.
  65. explicit CallbackObject(JS::Handle<JSObject*> aCallback,
  66. JS::Handle<JSObject*> aAsyncStack,
  67. nsIGlobalObject* aIncumbentGlobal)
  68. {
  69. Init(aCallback, aAsyncStack, aIncumbentGlobal);
  70. }
  71. JS::Handle<JSObject*> Callback() const
  72. {
  73. mCallback.exposeToActiveJS();
  74. return CallbackPreserveColor();
  75. }
  76. JSObject* GetCreationStack() const
  77. {
  78. return mCreationStack;
  79. }
  80. void MarkForCC()
  81. {
  82. mCallback.exposeToActiveJS();
  83. mCreationStack.exposeToActiveJS();
  84. }
  85. /*
  86. * This getter does not change the color of the JSObject meaning that the
  87. * object returned is not guaranteed to be kept alive past the next CC.
  88. *
  89. * This should only be called if you are certain that the return value won't
  90. * be passed into a JS API function and that it won't be stored without being
  91. * rooted (or otherwise signaling the stored value to the CC).
  92. */
  93. JS::Handle<JSObject*> CallbackPreserveColor() const
  94. {
  95. // Calling fromMarkedLocation() is safe because we trace our mCallback, and
  96. // because the value of mCallback cannot change after if has been set.
  97. return JS::Handle<JSObject*>::fromMarkedLocation(mCallback.address());
  98. }
  99. /*
  100. * If the callback is known to be non-gray, then this method can be
  101. * used instead of Callback() to avoid the overhead of
  102. * ExposeObjectToActiveJS().
  103. */
  104. JS::Handle<JSObject*> CallbackKnownNotGray() const
  105. {
  106. MOZ_ASSERT(!JS::ObjectIsMarkedGray(mCallback));
  107. return CallbackPreserveColor();
  108. }
  109. nsIGlobalObject* IncumbentGlobalOrNull() const
  110. {
  111. return mIncumbentGlobal;
  112. }
  113. enum ExceptionHandling {
  114. // Report any exception and don't throw it to the caller code.
  115. eReportExceptions,
  116. // Throw an exception to the caller code if the thrown exception is a
  117. // binding object for a DOMError or DOMException from the caller's scope,
  118. // otherwise report it.
  119. eRethrowContentExceptions,
  120. // Throw exceptions to the caller code, unless the caller compartment is
  121. // provided, the exception is not a DOMError or DOMException from the
  122. // caller compartment, and the caller compartment does not subsume our
  123. // unwrapped callback.
  124. eRethrowExceptions
  125. };
  126. size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
  127. {
  128. return aMallocSizeOf(this);
  129. }
  130. protected:
  131. virtual ~CallbackObject()
  132. {
  133. DropJSObjects();
  134. }
  135. explicit CallbackObject(CallbackObject* aCallbackObject)
  136. {
  137. Init(aCallbackObject->mCallback, aCallbackObject->mCreationStack,
  138. aCallbackObject->mIncumbentGlobal);
  139. }
  140. bool operator==(const CallbackObject& aOther) const
  141. {
  142. JSObject* thisObj =
  143. js::UncheckedUnwrap(CallbackPreserveColor());
  144. JSObject* otherObj =
  145. js::UncheckedUnwrap(aOther.CallbackPreserveColor());
  146. return thisObj == otherObj;
  147. }
  148. private:
  149. inline void InitNoHold(JSObject* aCallback, JSObject* aCreationStack,
  150. nsIGlobalObject* aIncumbentGlobal)
  151. {
  152. MOZ_ASSERT(aCallback && !mCallback);
  153. // Set script objects before we hold, on the off chance that a GC could
  154. // somehow happen in there... (which would be pretty odd, granted).
  155. mCallback = aCallback;
  156. mCreationStack = aCreationStack;
  157. if (aIncumbentGlobal) {
  158. mIncumbentGlobal = aIncumbentGlobal;
  159. mIncumbentJSGlobal = aIncumbentGlobal->GetGlobalJSObject();
  160. }
  161. }
  162. inline void Init(JSObject* aCallback, JSObject* aCreationStack,
  163. nsIGlobalObject* aIncumbentGlobal)
  164. {
  165. InitNoHold(aCallback, aCreationStack, aIncumbentGlobal);
  166. mozilla::HoldJSObjects(this);
  167. }
  168. inline void ClearJSReferences()
  169. {
  170. mCallback = nullptr;
  171. mCreationStack = nullptr;
  172. mIncumbentJSGlobal = nullptr;
  173. }
  174. CallbackObject(const CallbackObject&) = delete;
  175. CallbackObject& operator =(const CallbackObject&) = delete;
  176. protected:
  177. void DropJSObjects()
  178. {
  179. MOZ_ASSERT_IF(mIncumbentJSGlobal, mCallback);
  180. if (mCallback) {
  181. ClearJSReferences();
  182. mozilla::DropJSObjects(this);
  183. }
  184. }
  185. // For use from subclasses that want to be usable with Rooted.
  186. void Trace(JSTracer* aTracer);
  187. // For use from subclasses that want to be traced for a bit then possibly
  188. // switch to HoldJSObjects. If we have more than one owner, this will
  189. // HoldJSObjects; otherwise it will just forget all our JS references.
  190. void HoldJSObjectsIfMoreThanOneOwner();
  191. // Struct used as a way to force a CallbackObject constructor to not call
  192. // HoldJSObjects. We're putting it here so that CallbackObject subclasses will
  193. // have access to it, but outside code will not.
  194. //
  195. // Places that use this need to ensure that the callback is traced (e.g. via a
  196. // Rooted) until the HoldJSObjects call happens.
  197. struct FastCallbackConstructor {
  198. };
  199. // Just like the public version without the FastCallbackConstructor argument,
  200. // except for not calling HoldJSObjects. If you use this, you MUST ensure
  201. // that the object is traced until the HoldJSObjects happens!
  202. CallbackObject(JSContext* aCx, JS::Handle<JSObject*> aCallback,
  203. nsIGlobalObject* aIncumbentGlobal,
  204. const FastCallbackConstructor&)
  205. {
  206. if (aCx && JS::ContextOptionsRef(aCx).asyncStack()) {
  207. JS::RootedObject stack(aCx);
  208. if (!JS::CaptureCurrentStack(aCx, &stack)) {
  209. JS_ClearPendingException(aCx);
  210. }
  211. InitNoHold(aCallback, stack, aIncumbentGlobal);
  212. } else {
  213. InitNoHold(aCallback, nullptr, aIncumbentGlobal);
  214. }
  215. }
  216. // mCallback is not unwrapped, so it can be a cross-compartment-wrapper.
  217. // This is done to ensure that, if JS code can't call a callback f(), or get
  218. // its members, directly itself, this code won't call f(), or get its members,
  219. // on the code's behalf.
  220. JS::Heap<JSObject*> mCallback;
  221. JS::Heap<JSObject*> mCreationStack;
  222. // Ideally, we'd just hold a reference to the nsIGlobalObject, since that's
  223. // what we need to pass to AutoIncumbentScript. Unfortunately, that doesn't
  224. // hold the actual JS global alive. So we maintain an additional pointer to
  225. // the JS global itself so that we can trace it.
  226. //
  227. // At some point we should consider trying to make native globals hold their
  228. // scripted global alive, at which point we can get rid of the duplication
  229. // here.
  230. nsCOMPtr<nsIGlobalObject> mIncumbentGlobal;
  231. JS::TenuredHeap<JSObject*> mIncumbentJSGlobal;
  232. class MOZ_STACK_CLASS CallSetup
  233. {
  234. /**
  235. * A class that performs whatever setup we need to safely make a
  236. * call while this class is on the stack, After the constructor
  237. * returns, the call is safe to make if GetContext() returns
  238. * non-null.
  239. */
  240. public:
  241. // If aExceptionHandling == eRethrowContentExceptions then aCompartment
  242. // needs to be set to the compartment in which exceptions will be rethrown.
  243. //
  244. // If aExceptionHandling == eRethrowExceptions then aCompartment may be set
  245. // to the compartment in which exceptions will be rethrown. In that case
  246. // they will only be rethrown if that compartment's principal subsumes the
  247. // principal of our (unwrapped) callback.
  248. CallSetup(CallbackObject* aCallback, ErrorResult& aRv,
  249. const char* aExecutionReason,
  250. ExceptionHandling aExceptionHandling,
  251. JSCompartment* aCompartment = nullptr,
  252. bool aIsJSImplementedWebIDL = false);
  253. ~CallSetup();
  254. JSContext* GetContext() const
  255. {
  256. return mCx;
  257. }
  258. private:
  259. // We better not get copy-constructed
  260. CallSetup(const CallSetup&) = delete;
  261. bool ShouldRethrowException(JS::Handle<JS::Value> aException);
  262. // Members which can go away whenever
  263. JSContext* mCx;
  264. // Caller's compartment. This will only have a sensible value if
  265. // mExceptionHandling == eRethrowContentExceptions or eRethrowExceptions.
  266. JSCompartment* mCompartment;
  267. // And now members whose construction/destruction order we need to control.
  268. Maybe<AutoEntryScript> mAutoEntryScript;
  269. Maybe<AutoIncumbentScript> mAutoIncumbentScript;
  270. Maybe<JS::Rooted<JSObject*> > mRootedCallable;
  271. // Members which are used to set the async stack.
  272. Maybe<JS::Rooted<JSObject*>> mAsyncStack;
  273. Maybe<JS::AutoSetAsyncStackForNewCalls> mAsyncStackSetter;
  274. // Can't construct a JSAutoCompartment without a JSContext either. Also,
  275. // Put mAc after mAutoEntryScript so that we exit the compartment before
  276. // we pop the JSContext. Though in practice we'll often manually order
  277. // those two things.
  278. Maybe<JSAutoCompartment> mAc;
  279. // An ErrorResult to possibly re-throw exceptions on and whether
  280. // we should re-throw them.
  281. ErrorResult& mErrorResult;
  282. const ExceptionHandling mExceptionHandling;
  283. const bool mIsMainThread;
  284. };
  285. };
  286. template<class WebIDLCallbackT, class XPCOMCallbackT>
  287. class CallbackObjectHolder;
  288. template<class T, class U>
  289. void ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField);
  290. class CallbackObjectHolderBase
  291. {
  292. protected:
  293. // Returns null on all failures
  294. already_AddRefed<nsISupports> ToXPCOMCallback(CallbackObject* aCallback,
  295. const nsIID& aIID) const;
  296. };
  297. template<class WebIDLCallbackT, class XPCOMCallbackT>
  298. class CallbackObjectHolder : CallbackObjectHolderBase
  299. {
  300. /**
  301. * A class which stores either a WebIDLCallbackT* or an XPCOMCallbackT*. Both
  302. * types must inherit from nsISupports. The pointer that's stored can be
  303. * null.
  304. *
  305. * When storing a WebIDLCallbackT*, mPtrBits is set to the pointer value.
  306. * When storing an XPCOMCallbackT*, mPtrBits is the pointer value with low bit
  307. * set.
  308. */
  309. public:
  310. explicit CallbackObjectHolder(WebIDLCallbackT* aCallback)
  311. : mPtrBits(reinterpret_cast<uintptr_t>(aCallback))
  312. {
  313. NS_IF_ADDREF(aCallback);
  314. }
  315. explicit CallbackObjectHolder(XPCOMCallbackT* aCallback)
  316. : mPtrBits(reinterpret_cast<uintptr_t>(aCallback) | XPCOMCallbackFlag)
  317. {
  318. NS_IF_ADDREF(aCallback);
  319. }
  320. CallbackObjectHolder(CallbackObjectHolder&& aOther)
  321. : mPtrBits(aOther.mPtrBits)
  322. {
  323. aOther.mPtrBits = 0;
  324. static_assert(sizeof(CallbackObjectHolder) == sizeof(void*),
  325. "This object is expected to be as small as a pointer, and it "
  326. "is currently passed by value in various places. If it is "
  327. "bloating, we may want to pass it by reference then.");
  328. }
  329. CallbackObjectHolder(const CallbackObjectHolder& aOther) = delete;
  330. CallbackObjectHolder()
  331. : mPtrBits(0)
  332. {}
  333. ~CallbackObjectHolder()
  334. {
  335. UnlinkSelf();
  336. }
  337. void operator=(WebIDLCallbackT* aCallback)
  338. {
  339. UnlinkSelf();
  340. mPtrBits = reinterpret_cast<uintptr_t>(aCallback);
  341. NS_IF_ADDREF(aCallback);
  342. }
  343. void operator=(XPCOMCallbackT* aCallback)
  344. {
  345. UnlinkSelf();
  346. mPtrBits = reinterpret_cast<uintptr_t>(aCallback) | XPCOMCallbackFlag;
  347. NS_IF_ADDREF(aCallback);
  348. }
  349. void operator=(CallbackObjectHolder&& aOther)
  350. {
  351. UnlinkSelf();
  352. mPtrBits = aOther.mPtrBits;
  353. aOther.mPtrBits = 0;
  354. }
  355. void operator=(const CallbackObjectHolder& aOther) = delete;
  356. nsISupports* GetISupports() const
  357. {
  358. return reinterpret_cast<nsISupports*>(mPtrBits & ~XPCOMCallbackFlag);
  359. }
  360. // Boolean conversion operator so people can use this in boolean tests
  361. explicit operator bool() const
  362. {
  363. return GetISupports();
  364. }
  365. CallbackObjectHolder Clone() const
  366. {
  367. CallbackObjectHolder result;
  368. result.mPtrBits = mPtrBits;
  369. NS_IF_ADDREF(GetISupports());
  370. return result;
  371. }
  372. // Even if HasWebIDLCallback returns true, GetWebIDLCallback() might still
  373. // return null.
  374. bool HasWebIDLCallback() const
  375. {
  376. return !(mPtrBits & XPCOMCallbackFlag);
  377. }
  378. WebIDLCallbackT* GetWebIDLCallback() const
  379. {
  380. MOZ_ASSERT(HasWebIDLCallback());
  381. return reinterpret_cast<WebIDLCallbackT*>(mPtrBits);
  382. }
  383. XPCOMCallbackT* GetXPCOMCallback() const
  384. {
  385. MOZ_ASSERT(!HasWebIDLCallback());
  386. return reinterpret_cast<XPCOMCallbackT*>(mPtrBits & ~XPCOMCallbackFlag);
  387. }
  388. bool operator==(WebIDLCallbackT* aOtherCallback) const
  389. {
  390. if (!aOtherCallback) {
  391. // If other is null, then we must be null to be equal.
  392. return !GetISupports();
  393. }
  394. if (!HasWebIDLCallback() || !GetWebIDLCallback()) {
  395. // If other is non-null, then we can't be equal if we have a
  396. // non-WebIDL callback or a null callback.
  397. return false;
  398. }
  399. return *GetWebIDLCallback() == *aOtherCallback;
  400. }
  401. bool operator==(XPCOMCallbackT* aOtherCallback) const
  402. {
  403. return (!aOtherCallback && !GetISupports()) ||
  404. (!HasWebIDLCallback() && GetXPCOMCallback() == aOtherCallback);
  405. }
  406. bool operator==(const CallbackObjectHolder& aOtherCallback) const
  407. {
  408. if (aOtherCallback.HasWebIDLCallback()) {
  409. return *this == aOtherCallback.GetWebIDLCallback();
  410. }
  411. return *this == aOtherCallback.GetXPCOMCallback();
  412. }
  413. // Try to return an XPCOMCallbackT version of this object.
  414. already_AddRefed<XPCOMCallbackT> ToXPCOMCallback() const
  415. {
  416. if (!HasWebIDLCallback()) {
  417. RefPtr<XPCOMCallbackT> callback = GetXPCOMCallback();
  418. return callback.forget();
  419. }
  420. nsCOMPtr<nsISupports> supp =
  421. CallbackObjectHolderBase::ToXPCOMCallback(GetWebIDLCallback(),
  422. NS_GET_TEMPLATE_IID(XPCOMCallbackT));
  423. // ToXPCOMCallback already did the right QI for us.
  424. return supp.forget().downcast<XPCOMCallbackT>();
  425. }
  426. // Try to return a WebIDLCallbackT version of this object.
  427. already_AddRefed<WebIDLCallbackT> ToWebIDLCallback() const
  428. {
  429. if (HasWebIDLCallback()) {
  430. RefPtr<WebIDLCallbackT> callback = GetWebIDLCallback();
  431. return callback.forget();
  432. }
  433. return nullptr;
  434. }
  435. private:
  436. static const uintptr_t XPCOMCallbackFlag = 1u;
  437. friend void
  438. ImplCycleCollectionUnlink<WebIDLCallbackT,
  439. XPCOMCallbackT>(CallbackObjectHolder& aField);
  440. void UnlinkSelf()
  441. {
  442. // NS_IF_RELEASE because we might have been unlinked before
  443. nsISupports* ptr = GetISupports();
  444. // Clear mPtrBits before the release to prevent reentrance.
  445. mPtrBits = 0;
  446. NS_IF_RELEASE(ptr);
  447. }
  448. uintptr_t mPtrBits;
  449. };
  450. NS_DEFINE_STATIC_IID_ACCESSOR(CallbackObject, DOM_CALLBACKOBJECT_IID)
  451. template<class T, class U>
  452. inline void
  453. ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
  454. CallbackObjectHolder<T, U>& aField,
  455. const char* aName,
  456. uint32_t aFlags = 0)
  457. {
  458. CycleCollectionNoteChild(aCallback, aField.GetISupports(), aName, aFlags);
  459. }
  460. template<class T, class U>
  461. void
  462. ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField)
  463. {
  464. aField.UnlinkSelf();
  465. }
  466. // T is expected to be a RefPtr or OwningNonNull around a CallbackObject
  467. // subclass. This class is used in bindings to safely handle Fast* callbacks;
  468. // it ensures that the callback is traced, and that if something is holding onto
  469. // the callback when we're done with it HoldJSObjects is called.
  470. template<typename T>
  471. class RootedCallback : public JS::Rooted<T>
  472. {
  473. public:
  474. explicit RootedCallback(JSContext* cx)
  475. : JS::Rooted<T>(cx)
  476. {}
  477. // We need a way to make assignment from pointers (how we're normally used)
  478. // work.
  479. template<typename S>
  480. void operator=(S* arg)
  481. {
  482. this->get().operator=(arg);
  483. }
  484. // But nullptr can't use the above template, because it doesn't know which S
  485. // to select. So we need a special overload for nullptr.
  486. void operator=(decltype(nullptr) arg)
  487. {
  488. this->get().operator=(arg);
  489. }
  490. // Codegen relies on being able to do Callback() on us.
  491. JS::Handle<JSObject*> Callback() const
  492. {
  493. return this->get()->Callback();
  494. }
  495. ~RootedCallback()
  496. {
  497. // Ensure that our callback starts holding on to its own JS objects as
  498. // needed. Having to null-check here when T is OwningNonNull is a bit
  499. // silly, but it's simpler than creating two separate RootedCallback
  500. // instantiations for OwningNonNull and RefPtr.
  501. if (IsInitialized(this->get())) {
  502. this->get()->HoldJSObjectsIfMoreThanOneOwner();
  503. }
  504. }
  505. private:
  506. template<typename U>
  507. static bool IsInitialized(U& aArg); // Not implemented
  508. template<typename U>
  509. static bool IsInitialized(RefPtr<U>& aRefPtr)
  510. {
  511. return aRefPtr;
  512. }
  513. template<typename U>
  514. static bool IsInitialized(OwningNonNull<U>& aOwningNonNull)
  515. {
  516. return aOwningNonNull.isInitialized();
  517. }
  518. };
  519. } // namespace dom
  520. } // namespace mozilla
  521. #endif // mozilla_dom_CallbackObject_h