dom/bindings/CallbackObject.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
     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/. */
     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  */
    17 #ifndef mozilla_dom_CallbackObject_h
    18 #define mozilla_dom_CallbackObject_h
    20 #include "nsISupports.h"
    21 #include "nsISupportsImpl.h"
    22 #include "nsCycleCollectionParticipant.h"
    23 #include "jswrapper.h"
    24 #include "mozilla/Assertions.h"
    25 #include "mozilla/ErrorResult.h"
    26 #include "mozilla/HoldDropJSObjects.h"
    27 #include "mozilla/MemoryReporting.h"
    28 #include "mozilla/dom/ScriptSettings.h"
    29 #include "nsContentUtils.h"
    30 #include "nsWrapperCache.h"
    31 #include "nsJSEnvironment.h"
    32 #include "xpcpublic.h"
    34 namespace mozilla {
    35 namespace dom {
    37 #define DOM_CALLBACKOBJECT_IID \
    38 { 0xbe74c190, 0x6d76, 0x4991, \
    39  { 0x84, 0xb9, 0x65, 0x06, 0x99, 0xe6, 0x93, 0x2b } }
    41 class CallbackObject : public nsISupports
    42 {
    43 public:
    44   NS_DECLARE_STATIC_IID_ACCESSOR(DOM_CALLBACKOBJECT_IID)
    46   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    47   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CallbackObject)
    49   // The caller may pass a global object which will act as an override for the
    50   // incumbent script settings object when the callback is invoked (overriding
    51   // the entry point computed from aCallback). If no override is required, the
    52   // caller should pass null.
    53   explicit CallbackObject(JS::Handle<JSObject*> aCallback, nsIGlobalObject *aIncumbentGlobal)
    54   {
    55     Init(aCallback, aIncumbentGlobal);
    56   }
    58   virtual ~CallbackObject()
    59   {
    60     DropJSObjects();
    61   }
    63   JS::Handle<JSObject*> Callback() const
    64   {
    65     JS::ExposeObjectToActiveJS(mCallback);
    66     return CallbackPreserveColor();
    67   }
    69   /*
    70    * This getter does not change the color of the JSObject meaning that the
    71    * object returned is not guaranteed to be kept alive past the next CC.
    72    *
    73    * This should only be called if you are certain that the return value won't
    74    * be passed into a JS API function and that it won't be stored without being
    75    * rooted (or otherwise signaling the stored value to the CC).
    76    */
    77   JS::Handle<JSObject*> CallbackPreserveColor() const
    78   {
    79     // Calling fromMarkedLocation() is safe because we trace our mCallback, and
    80     // because the value of mCallback cannot change after if has been set.
    81     return JS::Handle<JSObject*>::fromMarkedLocation(mCallback.address());
    82   }
    84   nsIGlobalObject* IncumbentGlobalOrNull() const
    85   {
    86     return mIncumbentGlobal;
    87   }
    89   enum ExceptionHandling {
    90     // Report any exception and don't throw it to the caller code.
    91     eReportExceptions,
    92     // Throw an exception to the caller code if the thrown exception is a
    93     // binding object for a DOMError from the caller's scope, otherwise report
    94     // it.
    95     eRethrowContentExceptions,
    96     // Throw any exception to the caller code.
    97     eRethrowExceptions
    98   };
   100   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
   101   {
   102     return aMallocSizeOf(this);
   103   }
   105 protected:
   106   explicit CallbackObject(CallbackObject* aCallbackObject)
   107   {
   108     Init(aCallbackObject->mCallback, aCallbackObject->mIncumbentGlobal);
   109   }
   111   bool operator==(const CallbackObject& aOther) const
   112   {
   113     JSObject* thisObj =
   114       js::UncheckedUnwrap(CallbackPreserveColor());
   115     JSObject* otherObj =
   116       js::UncheckedUnwrap(aOther.CallbackPreserveColor());
   117     return thisObj == otherObj;
   118   }
   120 private:
   121   inline void Init(JSObject* aCallback, nsIGlobalObject* aIncumbentGlobal)
   122   {
   123     MOZ_ASSERT(aCallback && !mCallback);
   124     // Set script objects before we hold, on the off chance that a GC could
   125     // somehow happen in there... (which would be pretty odd, granted).
   126     mCallback = aCallback;
   127     if (aIncumbentGlobal) {
   128       mIncumbentGlobal = aIncumbentGlobal;
   129       mIncumbentJSGlobal = aIncumbentGlobal->GetGlobalJSObject();
   130     }
   131     mozilla::HoldJSObjects(this);
   132   }
   134   CallbackObject(const CallbackObject&) MOZ_DELETE;
   135   CallbackObject& operator =(const CallbackObject&) MOZ_DELETE;
   137 protected:
   138   void DropJSObjects()
   139   {
   140     MOZ_ASSERT_IF(mIncumbentJSGlobal, mCallback);
   141     if (mCallback) {
   142       mCallback = nullptr;
   143       mIncumbentJSGlobal = nullptr;
   144       mozilla::DropJSObjects(this);
   145     }
   146   }
   148   JS::Heap<JSObject*> mCallback;
   149   // Ideally, we'd just hold a reference to the nsIGlobalObject, since that's
   150   // what we need to pass to AutoIncumbentScript. Unfortunately, that doesn't
   151   // hold the actual JS global alive. So we maintain an additional pointer to
   152   // the JS global itself so that we can trace it.
   153   //
   154   // At some point we should consider trying to make native globals hold their
   155   // scripted global alive, at which point we can get rid of the duplication
   156   // here.
   157   nsCOMPtr<nsIGlobalObject> mIncumbentGlobal;
   158   JS::TenuredHeap<JSObject*> mIncumbentJSGlobal;
   160   class MOZ_STACK_CLASS CallSetup
   161   {
   162     /**
   163      * A class that performs whatever setup we need to safely make a
   164      * call while this class is on the stack, After the constructor
   165      * returns, the call is safe to make if GetContext() returns
   166      * non-null.
   167      */
   168   public:
   169     // If aExceptionHandling == eRethrowContentExceptions then aCompartment
   170     // needs to be set to the compartment in which exceptions will be rethrown.
   171     CallSetup(CallbackObject* aCallback, ErrorResult& aRv,
   172               ExceptionHandling aExceptionHandling,
   173               JSCompartment* aCompartment = nullptr,
   174               bool aIsJSImplementedWebIDL = false);
   175     ~CallSetup();
   177     JSContext* GetContext() const
   178     {
   179       return mCx;
   180     }
   182   private:
   183     // We better not get copy-constructed
   184     CallSetup(const CallSetup&) MOZ_DELETE;
   186     bool ShouldRethrowException(JS::Handle<JS::Value> aException);
   188     // Members which can go away whenever
   189     JSContext* mCx;
   191     // Caller's compartment. This will only have a sensible value if
   192     // mExceptionHandling == eRethrowContentExceptions.
   193     JSCompartment* mCompartment;
   195     // And now members whose construction/destruction order we need to control.
   196     Maybe<AutoEntryScript> mAutoEntryScript;
   197     Maybe<AutoIncumbentScript> mAutoIncumbentScript;
   199     // Constructed the rooter within the scope of mCxPusher above, so that it's
   200     // always within a request during its lifetime.
   201     Maybe<JS::Rooted<JSObject*> > mRootedCallable;
   203     // Can't construct a JSAutoCompartment without a JSContext either.  Also,
   204     // Put mAc after mAutoEntryScript so that we exit the compartment before
   205     // we pop the JSContext. Though in practice we'll often manually order
   206     // those two things.
   207     Maybe<JSAutoCompartment> mAc;
   209     // An ErrorResult to possibly re-throw exceptions on and whether
   210     // we should re-throw them.
   211     ErrorResult& mErrorResult;
   212     const ExceptionHandling mExceptionHandling;
   213     JS::ContextOptions mSavedJSContextOptions;
   214     const bool mIsMainThread;
   215   };
   216 };
   218 template<class WebIDLCallbackT, class XPCOMCallbackT>
   219 class CallbackObjectHolder;
   221 template<class T, class U>
   222 void ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField);
   224 class CallbackObjectHolderBase
   225 {
   226 protected:
   227   // Returns null on all failures
   228   already_AddRefed<nsISupports> ToXPCOMCallback(CallbackObject* aCallback,
   229                                                 const nsIID& aIID) const;
   230 };
   232 template<class WebIDLCallbackT, class XPCOMCallbackT>
   233 class CallbackObjectHolder : CallbackObjectHolderBase
   234 {
   235   /**
   236    * A class which stores either a WebIDLCallbackT* or an XPCOMCallbackT*.  Both
   237    * types must inherit from nsISupports.  The pointer that's stored can be
   238    * null.
   239    *
   240    * When storing a WebIDLCallbackT*, mPtrBits is set to the pointer value.
   241    * When storing an XPCOMCallbackT*, mPtrBits is the pointer value with low bit
   242    * set.
   243    */
   244 public:
   245   explicit CallbackObjectHolder(WebIDLCallbackT* aCallback)
   246     : mPtrBits(reinterpret_cast<uintptr_t>(aCallback))
   247   {
   248     NS_IF_ADDREF(aCallback);
   249   }
   251   explicit CallbackObjectHolder(XPCOMCallbackT* aCallback)
   252     : mPtrBits(reinterpret_cast<uintptr_t>(aCallback) | XPCOMCallbackFlag)
   253   {
   254     NS_IF_ADDREF(aCallback);
   255   }
   257   explicit CallbackObjectHolder(const CallbackObjectHolder& aOther)
   258     : mPtrBits(aOther.mPtrBits)
   259   {
   260     NS_IF_ADDREF(GetISupports());
   261   }
   263   CallbackObjectHolder()
   264     : mPtrBits(0)
   265   {}
   267   ~CallbackObjectHolder()
   268   {
   269     UnlinkSelf();
   270   }
   272   void operator=(WebIDLCallbackT* aCallback)
   273   {
   274     UnlinkSelf();
   275     mPtrBits = reinterpret_cast<uintptr_t>(aCallback);
   276     NS_IF_ADDREF(aCallback);
   277   }
   279   void operator=(XPCOMCallbackT* aCallback)
   280   {
   281     UnlinkSelf();
   282     mPtrBits = reinterpret_cast<uintptr_t>(aCallback) | XPCOMCallbackFlag;
   283     NS_IF_ADDREF(aCallback);
   284   }
   286   void operator=(const CallbackObjectHolder& aOther)
   287   {
   288     UnlinkSelf();
   289     mPtrBits = aOther.mPtrBits;
   290     NS_IF_ADDREF(GetISupports());
   291   }
   293   nsISupports* GetISupports() const
   294   {
   295     return reinterpret_cast<nsISupports*>(mPtrBits & ~XPCOMCallbackFlag);
   296   }
   298   // Boolean conversion operator so people can use this in boolean tests
   299   operator bool() const
   300   {
   301     return GetISupports();
   302   }
   304   // Even if HasWebIDLCallback returns true, GetWebIDLCallback() might still
   305   // return null.
   306   bool HasWebIDLCallback() const
   307   {
   308     return !(mPtrBits & XPCOMCallbackFlag);
   309   }
   311   WebIDLCallbackT* GetWebIDLCallback() const
   312   {
   313     MOZ_ASSERT(HasWebIDLCallback());
   314     return reinterpret_cast<WebIDLCallbackT*>(mPtrBits);
   315   }
   317   XPCOMCallbackT* GetXPCOMCallback() const
   318   {
   319     MOZ_ASSERT(!HasWebIDLCallback());
   320     return reinterpret_cast<XPCOMCallbackT*>(mPtrBits & ~XPCOMCallbackFlag);
   321   }
   323   bool operator==(WebIDLCallbackT* aOtherCallback) const
   324   {
   325     if (!aOtherCallback) {
   326       // If other is null, then we must be null to be equal.
   327       return !GetISupports();
   328     }
   330     if (!HasWebIDLCallback() || !GetWebIDLCallback()) {
   331       // If other is non-null, then we can't be equal if we have a
   332       // non-WebIDL callback or a null callback.
   333       return false;
   334     }
   336     return *GetWebIDLCallback() == *aOtherCallback;
   337   }
   339   bool operator==(XPCOMCallbackT* aOtherCallback) const
   340   {
   341     return (!aOtherCallback && !GetISupports()) ||
   342       (!HasWebIDLCallback() && GetXPCOMCallback() == aOtherCallback);
   343   }
   345   bool operator==(const CallbackObjectHolder& aOtherCallback) const
   346   {
   347     if (aOtherCallback.HasWebIDLCallback()) {
   348       return *this == aOtherCallback.GetWebIDLCallback();
   349     }
   351     return *this == aOtherCallback.GetXPCOMCallback();
   352   }
   354   // Try to return an XPCOMCallbackT version of this object.
   355   already_AddRefed<XPCOMCallbackT> ToXPCOMCallback() const
   356   {
   357     if (!HasWebIDLCallback()) {
   358       nsRefPtr<XPCOMCallbackT> callback = GetXPCOMCallback();
   359       return callback.forget();
   360     }
   362     nsCOMPtr<nsISupports> supp =
   363       CallbackObjectHolderBase::ToXPCOMCallback(GetWebIDLCallback(),
   364                                                 NS_GET_TEMPLATE_IID(XPCOMCallbackT));
   365     // ToXPCOMCallback already did the right QI for us.
   366     return supp.forget().downcast<XPCOMCallbackT>();
   367   }
   369   // Try to return a WebIDLCallbackT version of this object.
   370   already_AddRefed<WebIDLCallbackT> ToWebIDLCallback() const
   371   {
   372     if (HasWebIDLCallback()) {
   373       nsRefPtr<WebIDLCallbackT> callback = GetWebIDLCallback();
   374       return callback.forget();
   375     }
   376     return nullptr;
   377   }
   379 private:
   380   static const uintptr_t XPCOMCallbackFlag = 1u;
   382   friend void
   383   ImplCycleCollectionUnlink<WebIDLCallbackT,
   384                             XPCOMCallbackT>(CallbackObjectHolder& aField);
   386   void UnlinkSelf()
   387   {
   388     // NS_IF_RELEASE because we might have been unlinked before
   389     nsISupports* ptr = GetISupports();
   390     NS_IF_RELEASE(ptr);
   391     mPtrBits = 0;
   392   }
   394   uintptr_t mPtrBits;
   395 };
   397 NS_DEFINE_STATIC_IID_ACCESSOR(CallbackObject, DOM_CALLBACKOBJECT_IID)
   399 template<class T, class U>
   400 inline void
   401 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
   402                             CallbackObjectHolder<T, U>& aField,
   403                             const char* aName,
   404                             uint32_t aFlags = 0)
   405 {
   406   CycleCollectionNoteChild(aCallback, aField.GetISupports(), aName, aFlags);
   407 }
   409 template<class T, class U>
   410 void
   411 ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField)
   412 {
   413   aField.UnlinkSelf();
   414 }
   416 } // namespace dom
   417 } // namespace mozilla
   419 #endif // mozilla_dom_CallbackObject_h

mercurial