dom/bindings/CallbackObject.h

changeset 0
6474c204b198
     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

mercurial