tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CallbackObject.h (26053B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /**
      8 * A common base class for representing WebIDL callback function and
      9 * callback interface types in C++.
     10 *
     11 * This class implements common functionality like lifetime
     12 * management, initialization with the JS object, and setup of the
     13 * call environment.  Subclasses are responsible for providing methods
     14 * that do the call into JS as needed.
     15 */
     16 
     17 #ifndef mozilla_dom_CallbackObject_h
     18 #define mozilla_dom_CallbackObject_h
     19 
     20 #include <cstddef>
     21 #include <cstdint>
     22 
     23 #include "js/Exception.h"
     24 #include "js/RootingAPI.h"
     25 #include "js/Wrapper.h"
     26 #include "jsapi.h"
     27 #include "mozilla/AlreadyAddRefed.h"
     28 #include "mozilla/Assertions.h"
     29 #include "mozilla/Attributes.h"
     30 #include "mozilla/HoldDropJSObjects.h"
     31 #include "mozilla/Maybe.h"
     32 #include "mozilla/MemoryReporting.h"
     33 #include "mozilla/RefPtr.h"
     34 #include "mozilla/dom/AutoEntryScript.h"
     35 #include "mozilla/dom/BindingCallContext.h"
     36 #include "mozilla/dom/ScriptSettings.h"
     37 #include "nsCOMPtr.h"
     38 #include "nsCycleCollectionParticipant.h"
     39 #include "nsID.h"
     40 #include "nsIGlobalObject.h"
     41 #include "nsISupports.h"
     42 #include "nsISupportsUtils.h"
     43 #include "nsStringFwd.h"
     44 
     45 class JSAutoRealm;
     46 class JSObject;
     47 class JSTracer;
     48 class nsCycleCollectionTraversalCallback;
     49 struct JSContext;
     50 
     51 namespace JS {
     52 class AutoSetAsyncStackForNewCalls;
     53 class Realm;
     54 class Value;
     55 }  // namespace JS
     56 
     57 namespace mozilla {
     58 
     59 class ErrorResult;
     60 class PromiseJobRunnable;
     61 template <class T>
     62 class OwningNonNull;
     63 
     64 namespace dom {
     65 
     66 #define DOM_CALLBACKOBJECT_IID \
     67  {0xbe74c190, 0x6d76, 0x4991, {0x84, 0xb9, 0x65, 0x06, 0x99, 0xe6, 0x93, 0x2b}}
     68 
     69 class CallbackObjectBase {
     70 public:
     71  CallbackObjectBase() = default;
     72  CallbackObjectBase(JSObject* aCallback, JSObject* aCallbackGlobal,
     73                     JSObject* aAsyncStack, nsIGlobalObject* aIncumbentGlobal) {
     74    InitNoHold(aCallback, aCallbackGlobal, aAsyncStack, aIncumbentGlobal);
     75  }
     76 
     77  // This is guaranteed to be non-null from the time the CallbackObject is
     78  // created until JavaScript has had a chance to run. It will only return null
     79  // after a JavaScript caller has called nukeSandbox on a Sandbox object and
     80  // the cycle collector has had a chance to run, unless Reset() is explicitly
     81  // called (see below).
     82  //
     83  // This means that any native callee which receives a CallbackObject as an
     84  // argument can safely rely on the callback being non-null so long as it
     85  // doesn't trigger any scripts before it accesses it.
     86  JSObject* CallbackOrNull() const {
     87    mCallback.exposeToActiveJS();
     88    return CallbackPreserveColor();
     89  }
     90 
     91  JSObject* CallbackGlobalOrNull() const {
     92    mCallbackGlobal.exposeToActiveJS();
     93    return mCallbackGlobal;
     94  }
     95 
     96  // Like CallbackOrNull(), but will return a new dead proxy object in the
     97  // caller's realm if the callback is null.
     98  JSObject* Callback(JSContext* aCx);
     99 
    100  JSObject* GetCreationStack() const { return mCreationStack; }
    101 
    102  void MarkForCC() {
    103    mCallback.exposeToActiveJS();
    104    mCallbackGlobal.exposeToActiveJS();
    105    mCreationStack.exposeToActiveJS();
    106  }
    107 
    108  /*
    109   * This getter does not change the color of the JSObject meaning that the
    110   * object returned is not guaranteed to be kept alive past the next CC.
    111   */
    112  JSObject* CallbackPreserveColor() const { return mCallback.unbarrieredGet(); }
    113  JSObject* CallbackGlobalPreserveColor() const {
    114    return mCallbackGlobal.unbarrieredGet();
    115  }
    116 
    117  /*
    118   * If the callback is known to be non-gray, then this method can be
    119   * used instead of CallbackOrNull() to avoid the overhead of
    120   * ExposeObjectToActiveJS().
    121   */
    122  JSObject* CallbackKnownNotGray() const {
    123    JS::AssertObjectIsNotGray(mCallback);
    124    return CallbackPreserveColor();
    125  }
    126 
    127  nsIGlobalObject* IncumbentGlobalOrNull() const { return mIncumbentGlobal; }
    128 
    129  enum ExceptionHandling {
    130    // Report any exception and don't throw it to the caller code.
    131    eReportExceptions,
    132    // Throw any exception to the caller code and don't report it.
    133    eRethrowExceptions,
    134    // Throw an exception to the caller code if the thrown exception is a
    135    // binding object for a DOMException from the caller's scope, otherwise
    136    // report it.
    137    eRethrowContentExceptions
    138  };
    139 
    140  // Append a UTF-8 string to aOutString that describes the callback function,
    141  // for use in logging or profiler markers.
    142  // The string contains the function name and its source location, if
    143  // available, in the following format:
    144  // "<functionName> (<sourceURL>:<lineNumber>)"
    145  void GetDescription(nsACString& aOutString);
    146 
    147  // Used for cycle collection optimization.  Should return true only if all our
    148  // outgoing edges are to known-live objects.  In that case, there's no point
    149  // traversing our edges to them, because we know they can't be collected
    150  // anyway.
    151  bool IsBlackForCC() const {
    152    // Play it safe in case this gets called after unlink.
    153    return (!mCallback || !JS::ObjectIsMarkedGray(mCallback)) &&
    154           (!mCallbackGlobal || !JS::ObjectIsMarkedGray(mCallbackGlobal)) &&
    155           (!mCreationStack || !JS::ObjectIsMarkedGray(mCreationStack)) &&
    156           (!mIncumbentJSGlobal ||
    157            !JS::ObjectIsMarkedGray(mIncumbentJSGlobal)) &&
    158           // mIncumbentGlobal is known-live if we have a known-live
    159           // mIncumbentJSGlobal, since mIncumbentJSGlobal will keep a ref to
    160           // it. At this point if mIncumbentJSGlobal is not null, it's
    161           // known-live.
    162           (!mIncumbentGlobal || mIncumbentJSGlobal);
    163  }
    164 
    165 protected:
    166  virtual ~CallbackObjectBase() = default;
    167 
    168  // Provide a way to clear this object's pointers to GC things after the
    169  // callback has been run. Note that CallbackOrNull() will return null after
    170  // this point. This should only be called if the object is known not to be
    171  // used again, and no handles (e.g. those returned by CallbackPreserveColor)
    172  // are in use.
    173  virtual void Reset() {
    174    ClearJSReferences();
    175    mIncumbentGlobal = nullptr;
    176  }
    177 
    178  friend class mozilla::PromiseJobRunnable;
    179 
    180  inline void ClearJSReferences() {
    181    mCallback = nullptr;
    182    mCallbackGlobal = nullptr;
    183    mCreationStack = nullptr;
    184    mIncumbentJSGlobal = nullptr;
    185  }
    186 
    187  inline void InitNoHold(JSObject* aCallback, JSObject* aCallbackGlobal,
    188                         JSObject* aCreationStack,
    189                         nsIGlobalObject* aIncumbentGlobal) {
    190    MOZ_ASSERT(aCallback && !mCallback);
    191    MOZ_ASSERT(aCallbackGlobal);
    192    MOZ_DIAGNOSTIC_ASSERT(JS::GetCompartment(aCallback) ==
    193                          JS::GetCompartment(aCallbackGlobal));
    194    MOZ_ASSERT(JS_IsGlobalObject(aCallbackGlobal));
    195    mCallback = aCallback;
    196    mCallbackGlobal = aCallbackGlobal;
    197    mCreationStack = aCreationStack;
    198    if (aIncumbentGlobal) {
    199      mIncumbentGlobal = aIncumbentGlobal;
    200      // We don't want to expose to JS here (change the color).  If someone ever
    201      // reads mIncumbentJSGlobal, that will expose.  If not, no need to expose
    202      // here.
    203      mIncumbentJSGlobal = aIncumbentGlobal->GetGlobalJSObjectPreserveColor();
    204    }
    205  }
    206 
    207  void ClearJSObjects() {
    208    MOZ_ASSERT_IF(mIncumbentJSGlobal, mCallback);
    209    if (mCallback) {
    210      ClearJSReferences();
    211    }
    212  }
    213 
    214  // For use from subclasses that want to be usable with Rooted.
    215  void Trace(JSTracer* aTracer);
    216 
    217  // mCallback is not unwrapped, so it can be a cross-compartment-wrapper.
    218  // This is done to ensure that, if JS code can't call a callback f(), or get
    219  // its members, directly itself, this code won't call f(), or get its members,
    220  // on the code's behalf.
    221  JS::Heap<JSObject*> mCallback;
    222  // mCallbackGlobal is the global that we were in when we created the
    223  // callback. In particular, it is guaranteed to be same-compartment with
    224  // aCallback. We store it separately, because we have no way to recover the
    225  // global if mCallback is a cross-compartment wrapper.
    226  JS::Heap<JSObject*> mCallbackGlobal;
    227  JS::Heap<JSObject*> mCreationStack;
    228  // Ideally, we'd just hold a reference to the nsIGlobalObject, since that's
    229  // what we need to pass to AutoIncumbentScript. Unfortunately, that doesn't
    230  // hold the actual JS global alive. So we maintain an additional pointer to
    231  // the JS global itself so that we can trace it.
    232  //
    233  // At some point we should consider trying to make native globals hold their
    234  // scripted global alive, at which point we can get rid of the duplication
    235  // here.
    236  nsCOMPtr<nsIGlobalObject> mIncumbentGlobal;
    237  JS::TenuredHeap<JSObject*> mIncumbentJSGlobal;
    238 };
    239 
    240 /**
    241 * A class that performs whatever setup we need to safely make a
    242 * call while this class is on the stack, After the constructor
    243 * returns, the call is safe to make if GetContext() returns
    244 * non-null.
    245 */
    246 class MOZ_STACK_CLASS CallSetup {
    247 public:
    248  // If aExceptionHandling == eRethrowContentExceptions then aRealm
    249  // needs to be set to the realm in which exceptions will be rethrown.
    250  //
    251  // If aExceptionHandling == eRethrowExceptions then aRealm may be set
    252  // to the realm in which exceptions will be rethrown.  In that case
    253  // they will only be rethrown if that realm's principal subsumes the
    254  // principal of our (unwrapped) callback.
    255  CallSetup(CallbackObjectBase* aCallback, ErrorResult& aRv,
    256            const char* aExecutionReason,
    257            CallbackObjectBase::ExceptionHandling aExceptionHandling,
    258            JS::Realm* aRealm = nullptr, bool aIsJSImplementedWebIDL = false);
    259 
    260  CallSetup(JS::Handle<JSObject*> aCallbackGlobal,
    261            nsIGlobalObject* aIncumbentGlobal,
    262            JS::Handle<JSObject*> aCreationStack, ErrorResult& aRv,
    263            const char* aExecutionReason,
    264            CallbackObjectBase::ExceptionHandling aExceptionHandling,
    265            JS::Realm* aRealm = nullptr);
    266 
    267  MOZ_CAN_RUN_SCRIPT ~CallSetup();
    268 
    269  JSContext* GetContext() const { return mCx; }
    270 
    271  // Safe to call this after the constructor has run without throwing on the
    272  // ErrorResult it was handed.
    273  BindingCallContext& GetCallContext() { return *mCallContext; }
    274 
    275 private:
    276  CallSetup(CallbackObjectBase* aCallback, ErrorResult& aRv,
    277            const char* aExecutionReason,
    278            CallbackObjectBase::ExceptionHandling aExceptionHandling,
    279            JS::Realm* aRealm, bool aIsJSImplementedWebIDL,
    280            CycleCollectedJSContext* aCCJS);
    281  // Private delegating constructor for common initialization
    282  CallSetup(ErrorResult& aRv,
    283            CallbackObjectBase::ExceptionHandling aExceptionHandling,
    284            JS::Realm* aRealm, bool aIsMainThread,
    285            CycleCollectedJSContext* aCCJS);
    286 
    287  // We better not get copy-constructed
    288  CallSetup(const CallSetup&) = delete;
    289 
    290  bool ShouldRethrowException(JS::Handle<JS::Value> aException);
    291 
    292  static nsIGlobalObject* GetActiveGlobalObjectForCall(
    293      JS::Handle<JSObject*> callbackOrGlobal, bool aIsMainThread,
    294      bool aIsJSImplementedWebIDL, ErrorResult& aRv);
    295 
    296  static bool CheckBeforeExecution(nsIGlobalObject* aGlobalObject,
    297                                   JSObject* aCallbackOrGlobal,
    298                                   bool aIsJSImplementedWebIDL,
    299                                   ErrorResult& aRv);
    300 
    301  // Perform the final setup work. If this succeeds, mCx is set and we are able
    302  // to run the callback with the appropriate environment.
    303  void SetupForExecution(nsIGlobalObject* aGlobalObject,
    304                         nsIGlobalObject* aIncumbentGlobal,
    305                         JS::Handle<JSObject*> aCallbackOrGlobal,
    306                         JS::Handle<JSObject*> aCallbackGlobal,
    307                         JS::Handle<JSObject*> aCreationStack,
    308                         nsIPrincipal* aWebIDLCallerPrincipal,
    309                         const char* aExecutionReason, ErrorResult& aRv);
    310 
    311  // Members which can go away whenever
    312  JSContext* mCx;
    313 
    314  // Caller's realm. This will only have a sensible value if
    315  // mExceptionHandling == eRethrowContentExceptions.
    316  JS::Realm* mRealm;
    317 
    318  // And now members whose construction/destruction order we need to control.
    319  Maybe<AutoEntryScript> mAutoEntryScript;
    320  Maybe<AutoIncumbentScript> mAutoIncumbentScript;
    321 
    322  Maybe<JS::Rooted<JSObject*>> mRootedCallable;
    323  Maybe<JS::AutoSetAsyncStackForNewCalls> mAsyncStackSetter;
    324 
    325  // Can't construct a JSAutoRealm without a JSContext either.  Also,
    326  // Put mAr after mAutoEntryScript so that we exit the realm before we
    327  // pop the script settings stack. Though in practice we'll often manually
    328  // order those two things.
    329  Maybe<JSAutoRealm> mAr;
    330 
    331  // Our BindingCallContext.  This is a Maybe so we can avoid constructing it
    332  // until after we have a JSContext to construct it with.
    333  Maybe<BindingCallContext> mCallContext;
    334 
    335  // An ErrorResult to possibly re-throw exceptions on and whether
    336  // we should re-throw them.
    337  ErrorResult& mErrorResult;
    338  const CallbackObjectBase::ExceptionHandling mExceptionHandling;
    339  const bool mIsMainThread;
    340 };
    341 
    342 class CallbackObject : public nsISupports,
    343                       public CallbackObjectBase,
    344                       public JSHolderBase {
    345 public:
    346  NS_INLINE_DECL_STATIC_IID(DOM_CALLBACKOBJECT_IID)
    347 
    348  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    349  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(CallbackObject)
    350 
    351  // The caller may pass a global object which will act as an override for the
    352  // incumbent script settings object when the callback is invoked (overriding
    353  // the entry point computed from aCallback). If no override is required, the
    354  // caller should pass null.  |aCx| is used to capture the current
    355  // stack, which is later used as an async parent when the callback
    356  // is invoked.  aCx can be nullptr, in which case no stack is
    357  // captured.
    358  explicit CallbackObject(JSContext* aCx, JS::Handle<JSObject*> aCallback,
    359                          JS::Handle<JSObject*> aCallbackGlobal,
    360                          nsIGlobalObject* aIncumbentGlobal) {
    361    if (aCx && JS::IsAsyncStackCaptureEnabledForRealm(aCx)) {
    362      JS::Rooted<JSObject*> stack(aCx);
    363      if (!JS::CaptureCurrentStack(aCx, &stack)) {
    364        JS_ClearPendingException(aCx);
    365      }
    366      Init(aCallback, aCallbackGlobal, stack, aIncumbentGlobal);
    367    } else {
    368      Init(aCallback, aCallbackGlobal, nullptr, aIncumbentGlobal);
    369    }
    370  }
    371 
    372  // Instead of capturing the current stack to use as an async parent when the
    373  // callback is invoked, the caller can use this overload to pass in a stack
    374  // for that purpose.
    375  explicit CallbackObject(JSObject* aCallback, JSObject* aCallbackGlobal,
    376                          JSObject* aAsyncStack,
    377                          nsIGlobalObject* aIncumbentGlobal) {
    378    Init(aCallback, aCallbackGlobal, aAsyncStack, aIncumbentGlobal);
    379  }
    380 
    381  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
    382    return aMallocSizeOf(this);
    383  }
    384 
    385  void Reset() final {
    386    CallbackObjectBase::Reset();
    387    mozilla::DropJSObjectsWithKey(this);
    388  }
    389 
    390 protected:
    391  virtual ~CallbackObject() { mozilla::DropJSObjectsWithKey(this); }
    392 
    393  explicit CallbackObject(CallbackObject* aCallbackObject) {
    394    Init(aCallbackObject->mCallback, aCallbackObject->mCallbackGlobal,
    395         aCallbackObject->mCreationStack, aCallbackObject->mIncumbentGlobal);
    396  }
    397 
    398  // For use from subclasses that want to be traced for a bit then possibly
    399  // switch to HoldJSObjects and do other slow JS-related init work we might do.
    400  // If we have more than one owner, this will HoldJSObjects and do said slow
    401  // init work; otherwise it will just forget all our JS references.
    402  void FinishSlowJSInitIfMoreThanOneOwner(JSContext* aCx);
    403 
    404  // Struct used as a way to force a CallbackObject constructor to not call
    405  // HoldJSObjects. We're putting it here so that CallbackObject subclasses will
    406  // have access to it, but outside code will not.
    407  //
    408  // Places that use this need to ensure that the callback is traced (e.g. via a
    409  // Rooted) until the HoldJSObjects call happens.
    410  struct FastCallbackConstructor {};
    411 
    412  // Just like the public version without the FastCallbackConstructor argument,
    413  // except for not calling HoldJSObjects and not capturing async stacks (on the
    414  // assumption that we will do that last whenever we decide to actually
    415  // HoldJSObjects; see FinishSlowJSInitIfMoreThanOneOwner).  If you use this,
    416  // you MUST ensure that the object is traced until the HoldJSObjects happens!
    417  CallbackObject(JSObject* aCallback, JSObject* aCallbackGlobal,
    418                 const FastCallbackConstructor&) {
    419    InitNoHold(aCallback, aCallbackGlobal, nullptr, nullptr);
    420  }
    421 
    422  bool operator==(const CallbackObject& aOther) const {
    423    JSObject* wrappedThis = CallbackPreserveColor();
    424    JSObject* wrappedOther = aOther.CallbackPreserveColor();
    425    if (!wrappedThis || !wrappedOther) {
    426      return this == &aOther;
    427    }
    428 
    429    JSObject* thisObj = js::UncheckedUnwrap(wrappedThis);
    430    JSObject* otherObj = js::UncheckedUnwrap(wrappedOther);
    431    return thisObj == otherObj;
    432  }
    433 
    434  class JSObjectsDropper final {
    435   public:
    436    explicit JSObjectsDropper(CallbackObject* aHolder) : mHolder(aHolder) {}
    437 
    438    ~JSObjectsDropper() { mHolder->ClearJSObjects(); }
    439 
    440   private:
    441    RefPtr<CallbackObject> mHolder;
    442  };
    443 
    444 private:
    445  CallbackObject(const CallbackObject&) = delete;
    446  CallbackObject& operator=(const CallbackObject&) = delete;
    447 
    448  inline void Init(JSObject* aCallback, JSObject* aCallbackGlobal,
    449                   JSObject* aCreationStack,
    450                   nsIGlobalObject* aIncumbentGlobal) {
    451    // Set script objects before we hold, on the off chance that a GC could
    452    // somehow happen in there... (which would be pretty odd, granted).
    453    InitNoHold(aCallback, aCallbackGlobal, aCreationStack, aIncumbentGlobal);
    454    mozilla::HoldJSObjectsWithKey(this);
    455  }
    456 };
    457 
    458 template <class WebIDLCallbackT, class XPCOMCallbackT>
    459 class CallbackObjectHolder;
    460 
    461 template <class T, class U>
    462 void ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField);
    463 
    464 class CallbackObjectHolderBase {
    465 protected:
    466  // Returns null on all failures
    467  already_AddRefed<nsISupports> ToXPCOMCallback(CallbackObject* aCallback,
    468                                                const nsIID& aIID) const;
    469 };
    470 
    471 template <class WebIDLCallbackT, class XPCOMCallbackT>
    472 class CallbackObjectHolder : CallbackObjectHolderBase {
    473  /**
    474   * A class which stores either a WebIDLCallbackT* or an XPCOMCallbackT*.  Both
    475   * types must inherit from nsISupports.  The pointer that's stored can be
    476   * null.
    477   *
    478   * When storing a WebIDLCallbackT*, mPtrBits is set to the pointer value.
    479   * When storing an XPCOMCallbackT*, mPtrBits is the pointer value with low bit
    480   * set.
    481   */
    482 public:
    483  explicit CallbackObjectHolder(WebIDLCallbackT* aCallback)
    484      : mPtrBits(reinterpret_cast<uintptr_t>(aCallback)) {
    485    NS_IF_ADDREF(aCallback);
    486  }
    487 
    488  explicit CallbackObjectHolder(XPCOMCallbackT* aCallback)
    489      : mPtrBits(reinterpret_cast<uintptr_t>(aCallback) | XPCOMCallbackFlag) {
    490    NS_IF_ADDREF(aCallback);
    491  }
    492 
    493  CallbackObjectHolder(CallbackObjectHolder&& aOther)
    494      : mPtrBits(aOther.mPtrBits) {
    495    aOther.mPtrBits = 0;
    496    static_assert(sizeof(CallbackObjectHolder) == sizeof(void*),
    497                  "This object is expected to be as small as a pointer, and it "
    498                  "is currently passed by value in various places. If it is "
    499                  "bloating, we may want to pass it by reference then.");
    500  }
    501 
    502  CallbackObjectHolder(const CallbackObjectHolder& aOther) = delete;
    503 
    504  CallbackObjectHolder() : mPtrBits(0) {}
    505 
    506  ~CallbackObjectHolder() { UnlinkSelf(); }
    507 
    508  void operator=(WebIDLCallbackT* aCallback) {
    509    UnlinkSelf();
    510    mPtrBits = reinterpret_cast<uintptr_t>(aCallback);
    511    NS_IF_ADDREF(aCallback);
    512  }
    513 
    514  void operator=(XPCOMCallbackT* aCallback) {
    515    UnlinkSelf();
    516    mPtrBits = reinterpret_cast<uintptr_t>(aCallback) | XPCOMCallbackFlag;
    517    NS_IF_ADDREF(aCallback);
    518  }
    519 
    520  void operator=(CallbackObjectHolder&& aOther) {
    521    UnlinkSelf();
    522    mPtrBits = aOther.mPtrBits;
    523    aOther.mPtrBits = 0;
    524  }
    525 
    526  void operator=(const CallbackObjectHolder& aOther) = delete;
    527 
    528  void Reset() { UnlinkSelf(); }
    529 
    530  nsISupports* GetISupports() const {
    531    return reinterpret_cast<nsISupports*>(mPtrBits & ~XPCOMCallbackFlag);
    532  }
    533 
    534  already_AddRefed<nsISupports> Forget() {
    535    // This can be called from random threads.  Make sure to not refcount things
    536    // in here!
    537    nsISupports* supp = GetISupports();
    538    mPtrBits = 0;
    539    return dont_AddRef(supp);
    540  }
    541 
    542  // Boolean conversion operator so people can use this in boolean tests
    543  explicit operator bool() const { return GetISupports(); }
    544 
    545  CallbackObjectHolder Clone() const {
    546    CallbackObjectHolder result;
    547    result.mPtrBits = mPtrBits;
    548    NS_IF_ADDREF(GetISupports());
    549    return result;
    550  }
    551 
    552  // Even if HasWebIDLCallback returns true, GetWebIDLCallback() might still
    553  // return null.
    554  bool HasWebIDLCallback() const { return !(mPtrBits & XPCOMCallbackFlag); }
    555 
    556  WebIDLCallbackT* GetWebIDLCallback() const {
    557    MOZ_ASSERT(HasWebIDLCallback());
    558    return reinterpret_cast<WebIDLCallbackT*>(mPtrBits);
    559  }
    560 
    561  XPCOMCallbackT* GetXPCOMCallback() const {
    562    MOZ_ASSERT(!HasWebIDLCallback());
    563    return reinterpret_cast<XPCOMCallbackT*>(mPtrBits & ~XPCOMCallbackFlag);
    564  }
    565 
    566  bool operator==(WebIDLCallbackT* aOtherCallback) const {
    567    if (!aOtherCallback) {
    568      // If other is null, then we must be null to be equal.
    569      return !GetISupports();
    570    }
    571 
    572    if (!HasWebIDLCallback() || !GetWebIDLCallback()) {
    573      // If other is non-null, then we can't be equal if we have a
    574      // non-WebIDL callback or a null callback.
    575      return false;
    576    }
    577 
    578    return *GetWebIDLCallback() == *aOtherCallback;
    579  }
    580 
    581  bool operator==(XPCOMCallbackT* aOtherCallback) const {
    582    return (!aOtherCallback && !GetISupports()) ||
    583           (!HasWebIDLCallback() && GetXPCOMCallback() == aOtherCallback);
    584  }
    585 
    586  bool operator==(const CallbackObjectHolder& aOtherCallback) const {
    587    if (aOtherCallback.HasWebIDLCallback()) {
    588      return *this == aOtherCallback.GetWebIDLCallback();
    589    }
    590 
    591    return *this == aOtherCallback.GetXPCOMCallback();
    592  }
    593 
    594  // Try to return an XPCOMCallbackT version of this object.
    595  already_AddRefed<XPCOMCallbackT> ToXPCOMCallback() const {
    596    if (!HasWebIDLCallback()) {
    597      RefPtr<XPCOMCallbackT> callback = GetXPCOMCallback();
    598      return callback.forget();
    599    }
    600 
    601    nsCOMPtr<nsISupports> supp = CallbackObjectHolderBase::ToXPCOMCallback(
    602        GetWebIDLCallback(), NS_GET_IID(XPCOMCallbackT));
    603    if (supp) {
    604      // ToXPCOMCallback already did the right QI for us.
    605      return supp.forget().downcast<XPCOMCallbackT>();
    606    }
    607    return nullptr;
    608  }
    609 
    610  // Try to return a WebIDLCallbackT version of this object.
    611  already_AddRefed<WebIDLCallbackT> ToWebIDLCallback() const {
    612    if (HasWebIDLCallback()) {
    613      RefPtr<WebIDLCallbackT> callback = GetWebIDLCallback();
    614      return callback.forget();
    615    }
    616    return nullptr;
    617  }
    618 
    619 private:
    620  static const uintptr_t XPCOMCallbackFlag = 1u;
    621 
    622  friend void ImplCycleCollectionUnlink<WebIDLCallbackT, XPCOMCallbackT>(
    623      CallbackObjectHolder& aField);
    624 
    625  void UnlinkSelf() {
    626    // NS_IF_RELEASE because we might have been unlinked before
    627    nsISupports* ptr = GetISupports();
    628    // Clear mPtrBits before the release to prevent reentrance.
    629    mPtrBits = 0;
    630    NS_IF_RELEASE(ptr);
    631  }
    632 
    633  uintptr_t mPtrBits;
    634 };
    635 
    636 template <class T, class U>
    637 inline void ImplCycleCollectionTraverse(
    638    nsCycleCollectionTraversalCallback& aCallback,
    639    CallbackObjectHolder<T, U>& aField, const char* aName,
    640    uint32_t aFlags = 0) {
    641  if (aField) {
    642    CycleCollectionNoteChild(aCallback, aField.GetISupports(), aName, aFlags);
    643  }
    644 }
    645 
    646 template <class T, class U>
    647 void ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField) {
    648  aField.UnlinkSelf();
    649 }
    650 
    651 // T is expected to be a RefPtr or OwningNonNull around a CallbackObject
    652 // subclass.  This class is used in bindings to safely handle Fast* callbacks;
    653 // it ensures that the callback is traced, and that if something is holding onto
    654 // the callback when we're done with it HoldJSObjects is called.
    655 //
    656 // Since we effectively hold a ref to a refcounted thing (like RefPtr or
    657 // OwningNonNull), we are also MOZ_IS_SMARTPTR_TO_REFCOUNTED for static analysis
    658 // purposes.
    659 template <typename T>
    660 class MOZ_RAII MOZ_IS_SMARTPTR_TO_REFCOUNTED RootedCallback
    661    : public JS::Rooted<T> {
    662 public:
    663  explicit RootedCallback(JSContext* cx) : JS::Rooted<T>(cx), mCx(cx) {}
    664 
    665  // We need a way to make assignment from pointers (how we're normally used)
    666  // work.
    667  template <typename S>
    668  void operator=(S* arg) {
    669    this->get().operator=(arg);
    670  }
    671 
    672  // But nullptr can't use the above template, because it doesn't know which S
    673  // to select.  So we need a special overload for nullptr.
    674  void operator=(decltype(nullptr) arg) { this->get().operator=(arg); }
    675 
    676  // Codegen relies on being able to do CallbackOrNull() and Callback() on us.
    677  JSObject* CallbackOrNull() const { return this->get()->CallbackOrNull(); }
    678 
    679  JSObject* Callback(JSContext* aCx) const {
    680    return this->get()->Callback(aCx);
    681  }
    682 
    683  ~RootedCallback() {
    684    // Ensure that our callback starts holding on to its own JS objects as
    685    // needed.  We really do need to check that things are initialized even when
    686    // T is OwningNonNull, because we might be running before the OwningNonNull
    687    // ever got assigned to!
    688    if (IsInitialized(this->get())) {
    689      this->get()->FinishSlowJSInitIfMoreThanOneOwner(mCx);
    690    }
    691  }
    692 
    693 private:
    694  template <typename U>
    695  static bool IsInitialized(U& aArg);  // Not implemented
    696 
    697  template <typename U>
    698  static bool IsInitialized(RefPtr<U>& aRefPtr) {
    699    return aRefPtr;
    700  }
    701 
    702  template <typename U>
    703  static bool IsInitialized(OwningNonNull<U>& aOwningNonNull) {
    704    return aOwningNonNull.isInitialized();
    705  }
    706 
    707  JSContext* mCx;
    708 };
    709 
    710 }  // namespace dom
    711 }  // namespace mozilla
    712 
    713 #endif  // mozilla_dom_CallbackObject_h