forked from mirrors/gecko-dev
		
	 e363a41bd4
			
		
	
	
		e363a41bd4
		
	
	
	
	
		
			
			Automatically generated by: $ rg 'IsBaseOf<' | cut -d : -f 1 | xargs sed -i 's/mozilla::IsBaseOf</std::is_base_of</g' $ rg 'IsBaseOf<' | cut -d : -f 1 | xargs sed -i 's/IsBaseOf</std::is_base_of</g Differential Revision: https://phabricator.services.mozilla.com/D59013 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			3216 lines
		
	
	
	
		
			114 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			3216 lines
		
	
	
	
		
			114 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 | |
|  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #ifndef mozilla_dom_BindingUtils_h__
 | |
| #define mozilla_dom_BindingUtils_h__
 | |
| 
 | |
| #include "jsfriendapi.h"
 | |
| #include "js/CharacterEncoding.h"
 | |
| #include "js/Conversions.h"
 | |
| #include "js/MemoryFunctions.h"
 | |
| #include "js/Wrapper.h"
 | |
| #include "mozilla/ArrayUtils.h"
 | |
| #include "mozilla/Alignment.h"
 | |
| #include "mozilla/Array.h"
 | |
| #include "mozilla/Assertions.h"
 | |
| #include "mozilla/DeferredFinalize.h"
 | |
| #include "mozilla/dom/BindingDeclarations.h"
 | |
| #include "mozilla/dom/CallbackObject.h"
 | |
| #include "mozilla/dom/DOMJSClass.h"
 | |
| #include "mozilla/dom/DOMJSProxyHandler.h"
 | |
| #include "mozilla/dom/Exceptions.h"
 | |
| #include "mozilla/dom/NonRefcountedDOMObject.h"
 | |
| #include "mozilla/dom/Nullable.h"
 | |
| #include "mozilla/dom/PrototypeList.h"
 | |
| #include "mozilla/dom/RemoteObjectProxy.h"
 | |
| #include "mozilla/dom/RootedDictionary.h"
 | |
| #include "mozilla/dom/ScriptSettings.h"
 | |
| #include "mozilla/SegmentedVector.h"
 | |
| #include "mozilla/ErrorResult.h"
 | |
| #include "mozilla/Likely.h"
 | |
| #include "mozilla/MemoryReporting.h"
 | |
| #include "nsAutoPtr.h"
 | |
| #include "mozilla/dom/Document.h"
 | |
| #include "nsIGlobalObject.h"
 | |
| #include "nsIVariant.h"
 | |
| #include "nsJSUtils.h"
 | |
| #include "nsISupportsImpl.h"
 | |
| #include "xpcObjectHelper.h"
 | |
| #include "xpcpublic.h"
 | |
| #include "nsIVariant.h"
 | |
| #include "mozilla/dom/FakeString.h"
 | |
| 
 | |
| #include "nsWrapperCacheInlines.h"
 | |
| 
 | |
| class nsGenericHTMLElement;
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| enum UseCounter : int16_t;
 | |
| enum class UseCounterWorker : int16_t;
 | |
| 
 | |
| namespace dom {
 | |
| class CustomElementReactionsStack;
 | |
| class MessageManagerGlobal;
 | |
| class DedicatedWorkerGlobalScope;
 | |
| template <typename KeyType, typename ValueType>
 | |
| class Record;
 | |
| class WindowProxyHolder;
 | |
| 
 | |
| nsresult UnwrapArgImpl(JSContext* cx, JS::Handle<JSObject*> src,
 | |
|                        const nsIID& iid, void** ppArg);
 | |
| 
 | |
| /** Convert a jsval to an XPCOM pointer. Caller must not assume that src will
 | |
|     keep the XPCOM pointer rooted. */
 | |
| template <class Interface>
 | |
| inline nsresult UnwrapArg(JSContext* cx, JS::Handle<JSObject*> src,
 | |
|                           Interface** ppArg) {
 | |
|   return UnwrapArgImpl(cx, src, NS_GET_TEMPLATE_IID(Interface),
 | |
|                        reinterpret_cast<void**>(ppArg));
 | |
| }
 | |
| 
 | |
| nsresult UnwrapWindowProxyArg(JSContext* cx, JS::Handle<JSObject*> src,
 | |
|                               WindowProxyHolder& ppArg);
 | |
| 
 | |
| // Returns true if the JSClass is used for DOM objects.
 | |
| inline bool IsDOMClass(const JSClass* clasp) {
 | |
|   return clasp->flags & JSCLASS_IS_DOMJSCLASS;
 | |
| }
 | |
| 
 | |
| // Return true if the JSClass is used for non-proxy DOM objects.
 | |
| inline bool IsNonProxyDOMClass(const JSClass* clasp) {
 | |
|   return IsDOMClass(clasp) && !clasp->isProxy();
 | |
| }
 | |
| 
 | |
| // Returns true if the JSClass is used for DOM interface and interface
 | |
| // prototype objects.
 | |
| inline bool IsDOMIfaceAndProtoClass(const JSClass* clasp) {
 | |
|   return clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS;
 | |
| }
 | |
| 
 | |
| static_assert(DOM_OBJECT_SLOT == 0,
 | |
|               "DOM_OBJECT_SLOT doesn't match the proxy private slot.  "
 | |
|               "Expect bad things");
 | |
| template <class T>
 | |
| inline T* UnwrapDOMObject(JSObject* obj) {
 | |
|   MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
 | |
|              "Don't pass non-DOM objects to this function");
 | |
| 
 | |
|   JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
 | |
|   return static_cast<T*>(val.toPrivate());
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| inline T* UnwrapPossiblyNotInitializedDOMObject(JSObject* obj) {
 | |
|   // This is used by the OjectMoved JSClass hook which can be called before
 | |
|   // JS_NewObject has returned and so before we have a chance to set
 | |
|   // DOM_OBJECT_SLOT to anything useful.
 | |
| 
 | |
|   MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
 | |
|              "Don't pass non-DOM objects to this function");
 | |
| 
 | |
|   JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
 | |
|   if (val.isUndefined()) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return static_cast<T*>(val.toPrivate());
 | |
| }
 | |
| 
 | |
| inline const DOMJSClass* GetDOMClass(const JSClass* clasp) {
 | |
|   return IsDOMClass(clasp) ? DOMJSClass::FromJSClass(clasp) : nullptr;
 | |
| }
 | |
| 
 | |
| inline const DOMJSClass* GetDOMClass(JSObject* obj) {
 | |
|   return GetDOMClass(js::GetObjectClass(obj));
 | |
| }
 | |
| 
 | |
| inline nsISupports* UnwrapDOMObjectToISupports(JSObject* aObject) {
 | |
|   const DOMJSClass* clasp = GetDOMClass(aObject);
 | |
|   if (!clasp || !clasp->mDOMObjectIsISupports) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   return UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObject);
 | |
| }
 | |
| 
 | |
| inline bool IsDOMObject(JSObject* obj) {
 | |
|   return IsDOMClass(js::GetObjectClass(obj));
 | |
| }
 | |
| 
 | |
| // There are two valid ways to use UNWRAP_OBJECT: Either obj needs to
 | |
| // be a MutableHandle<JSObject*>, or value needs to be a strong-reference
 | |
| // smart pointer type (OwningNonNull or RefPtr or nsCOMPtr), in which case obj
 | |
| // can be anything that converts to JSObject*.
 | |
| //
 | |
| // This can't be used with Window, EventTarget, or Location as the "Interface"
 | |
| // argument (and will fail a static_assert if you try to do that).  Use
 | |
| // UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT to unwrap to those interfaces.
 | |
| #define UNWRAP_OBJECT(Interface, obj, value)                        \
 | |
|   mozilla::dom::binding_detail::UnwrapObjectWithCrossOriginAsserts< \
 | |
|       mozilla::dom::prototypes::id::Interface,                      \
 | |
|       mozilla::dom::Interface##_Binding::NativeType>(obj, value)
 | |
| 
 | |
| // UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT is just like UNWRAP_OBJECT but requires a
 | |
| // JSContext in a Realm that represents "who is doing the unwrapping?" to
 | |
| // properly unwrap the object.
 | |
| #define UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT(Interface, obj, value, cx)          \
 | |
|   mozilla::dom::UnwrapObject<mozilla::dom::prototypes::id::Interface,        \
 | |
|                              mozilla::dom::Interface##_Binding::NativeType>( \
 | |
|       obj, value, cx)
 | |
| 
 | |
| // Test whether the given object is an instance of the given interface.
 | |
| #define IS_INSTANCE_OF(Interface, obj)                                       \
 | |
|   mozilla::dom::IsInstanceOf<mozilla::dom::prototypes::id::Interface,        \
 | |
|                              mozilla::dom::Interface##_Binding::NativeType>( \
 | |
|       obj)
 | |
| 
 | |
| // Unwrap the given non-wrapper object.  This can be used with any obj that
 | |
| // converts to JSObject*; as long as that JSObject* is live the return value
 | |
| // will be valid.
 | |
| #define UNWRAP_NON_WRAPPER_OBJECT(Interface, obj, value) \
 | |
|   mozilla::dom::UnwrapNonWrapperObject<                  \
 | |
|       mozilla::dom::prototypes::id::Interface,           \
 | |
|       mozilla::dom::Interface##_Binding::NativeType>(obj, value)
 | |
| 
 | |
| // Some callers don't want to set an exception when unwrapping fails
 | |
| // (for example, overload resolution uses unwrapping to tell what sort
 | |
| // of thing it's looking at).
 | |
| // U must be something that a T* can be assigned to (e.g. T* or an RefPtr<T>).
 | |
| //
 | |
| // The obj argument will be mutated to point to CheckedUnwrap of itself if the
 | |
| // passed-in value is not a DOM object and CheckedUnwrap succeeds.
 | |
| //
 | |
| // If mayBeWrapper is true, there are three valid ways to invoke
 | |
| // UnwrapObjectInternal: Either obj needs to be a class wrapping a
 | |
| // MutableHandle<JSObject*>, with an assignment operator that sets the handle to
 | |
| // the given object, or U needs to be a strong-reference smart pointer type
 | |
| // (OwningNonNull or RefPtr or nsCOMPtr), or the value being stored in "value"
 | |
| // must not escape past being tested for falsiness immediately after the
 | |
| // UnwrapObjectInternal call.
 | |
| //
 | |
| // If mayBeWrapper is false, obj can just be a JSObject*, and U anything that a
 | |
| // T* can be assigned to.
 | |
| //
 | |
| // CxType is in practice allowed to be either decltype(nullptr) or JSContext*.
 | |
| // If it's decltype(nullptr) we will do a CheckedUnwrapStatic and it's the
 | |
| // caller's responsibility to make sure they're not trying to work with Window
 | |
| // or Location objects.  Otherwise we'll do a CheckedUnwrapDynamic.  This all
 | |
| // only matters if mayBeWrapper is true; if it's false just pass nullptr for
 | |
| // the cx arg.
 | |
| namespace binding_detail {
 | |
| template <class T, bool mayBeWrapper, typename U, typename V, typename CxType>
 | |
| MOZ_ALWAYS_INLINE nsresult UnwrapObjectInternal(V& obj, U& value,
 | |
|                                                 prototypes::ID protoID,
 | |
|                                                 uint32_t protoDepth,
 | |
|                                                 CxType cx) {
 | |
|   static_assert(IsSame<CxType, JSContext*>::value ||
 | |
|                     IsSame<CxType, decltype(nullptr)>::value,
 | |
|                 "Unexpected CxType");
 | |
| 
 | |
|   /* First check to see whether we have a DOM object */
 | |
|   const DOMJSClass* domClass = GetDOMClass(obj);
 | |
|   if (domClass) {
 | |
|     /* This object is a DOM object.  Double-check that it is safely
 | |
|        castable to T by checking whether it claims to inherit from the
 | |
|        class identified by protoID. */
 | |
|     if (domClass->mInterfaceChain[protoDepth] == protoID) {
 | |
|       value = UnwrapDOMObject<T>(obj);
 | |
|       return NS_OK;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* Maybe we have a security wrapper or outer window? */
 | |
|   if (!mayBeWrapper || !js::IsWrapper(obj)) {
 | |
|     // For non-cross-origin-accessible methods and properties, remote object
 | |
|     // proxies should behave the same as opaque wrappers.
 | |
|     if (IsRemoteObjectProxy(obj)) {
 | |
|       return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
 | |
|     }
 | |
| 
 | |
|     /* Not a DOM object, not a wrapper, just bail */
 | |
|     return NS_ERROR_XPC_BAD_CONVERT_JS;
 | |
|   }
 | |
| 
 | |
|   JSObject* unwrappedObj;
 | |
|   if (IsSame<CxType, decltype(nullptr)>::value) {
 | |
|     unwrappedObj = js::CheckedUnwrapStatic(obj);
 | |
|   } else {
 | |
|     unwrappedObj =
 | |
|         js::CheckedUnwrapDynamic(obj, cx, /* stopAtWindowProxy = */ false);
 | |
|   }
 | |
|   if (!unwrappedObj) {
 | |
|     return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
 | |
|   }
 | |
| 
 | |
|   if (IsSame<CxType, decltype(nullptr)>::value) {
 | |
|     // We might still have a windowproxy here.  But it shouldn't matter, because
 | |
|     // that's not what the caller is looking for, so we're going to fail out
 | |
|     // anyway below once we do the recursive call to ourselves with wrapper
 | |
|     // unwrapping disabled.
 | |
|     MOZ_ASSERT(!js::IsWrapper(unwrappedObj) || js::IsWindowProxy(unwrappedObj));
 | |
|   } else {
 | |
|     // We shouldn't have a wrapper by now.
 | |
|     MOZ_ASSERT(!js::IsWrapper(unwrappedObj));
 | |
|   }
 | |
| 
 | |
|   // Recursive call is OK, because now we're using false for mayBeWrapper and
 | |
|   // we never reach this code if that boolean is false, so can't keep calling
 | |
|   // ourselves.
 | |
|   //
 | |
|   // Unwrap into a temporary pointer, because in general unwrapping into
 | |
|   // something of type U might trigger GC (e.g. release the value currently
 | |
|   // stored in there, with arbitrary consequences) and invalidate the
 | |
|   // "unwrappedObj" pointer.
 | |
|   T* tempValue = nullptr;
 | |
|   nsresult rv = UnwrapObjectInternal<T, false>(unwrappedObj, tempValue, protoID,
 | |
|                                                protoDepth, nullptr);
 | |
|   if (NS_SUCCEEDED(rv)) {
 | |
|     // Suppress a hazard related to keeping tempValue alive across
 | |
|     // UnwrapObjectInternal, because the analysis can't tell that this function
 | |
|     // will not GC if maybeWrapped=False and we've already gone through a level
 | |
|     // of unwrapping so unwrappedObj will be !IsWrapper.
 | |
|     JS::AutoSuppressGCAnalysis suppress;
 | |
| 
 | |
|     // It's very important to not update "obj" with the "unwrappedObj" value
 | |
|     // until we know the unwrap has succeeded.  Otherwise, in a situation in
 | |
|     // which we have an overload of object and primitive we could end up
 | |
|     // converting to the primitive from the unwrappedObj, whereas we want to do
 | |
|     // it from the original object.
 | |
|     obj = unwrappedObj;
 | |
|     // And now assign to "value"; at this point we don't care if a GC happens
 | |
|     // and invalidates unwrappedObj.
 | |
|     value = tempValue;
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   /* It's the wrong sort of DOM object */
 | |
|   return NS_ERROR_XPC_BAD_CONVERT_JS;
 | |
| }
 | |
| 
 | |
| struct MutableObjectHandleWrapper {
 | |
|   explicit MutableObjectHandleWrapper(JS::MutableHandle<JSObject*> aHandle)
 | |
|       : mHandle(aHandle) {}
 | |
| 
 | |
|   void operator=(JSObject* aObject) {
 | |
|     MOZ_ASSERT(aObject);
 | |
|     mHandle.set(aObject);
 | |
|   }
 | |
| 
 | |
|   operator JSObject*() const { return mHandle; }
 | |
| 
 | |
|  private:
 | |
|   JS::MutableHandle<JSObject*> mHandle;
 | |
| };
 | |
| 
 | |
| struct MutableValueHandleWrapper {
 | |
|   explicit MutableValueHandleWrapper(JS::MutableHandle<JS::Value> aHandle)
 | |
|       : mHandle(aHandle) {}
 | |
| 
 | |
|   void operator=(JSObject* aObject) {
 | |
|     MOZ_ASSERT(aObject);
 | |
|     mHandle.setObject(*aObject);
 | |
|   }
 | |
| 
 | |
|   operator JSObject*() const { return &mHandle.toObject(); }
 | |
| 
 | |
|  private:
 | |
|   JS::MutableHandle<JS::Value> mHandle;
 | |
| };
 | |
| 
 | |
| }  // namespace binding_detail
 | |
| 
 | |
| // UnwrapObject overloads that ensure we have a MutableHandle to keep it alive.
 | |
| template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
 | |
| MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::MutableHandle<JSObject*> obj,
 | |
|                                         U& value, CxType cx) {
 | |
|   binding_detail::MutableObjectHandleWrapper wrapper(obj);
 | |
|   return binding_detail::UnwrapObjectInternal<T, true>(
 | |
|       wrapper, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
 | |
| }
 | |
| 
 | |
| template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
 | |
| MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::MutableHandle<JS::Value> obj,
 | |
|                                         U& value, CxType cx) {
 | |
|   MOZ_ASSERT(obj.isObject());
 | |
|   binding_detail::MutableValueHandleWrapper wrapper(obj);
 | |
|   return binding_detail::UnwrapObjectInternal<T, true>(
 | |
|       wrapper, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
 | |
| }
 | |
| 
 | |
| // UnwrapObject overloads that ensure we have a strong ref to keep it alive.
 | |
| template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
 | |
| MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, RefPtr<U>& value,
 | |
|                                         CxType cx) {
 | |
|   return binding_detail::UnwrapObjectInternal<T, true>(
 | |
|       obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
 | |
| }
 | |
| 
 | |
| template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
 | |
| MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, nsCOMPtr<U>& value,
 | |
|                                         CxType cx) {
 | |
|   return binding_detail::UnwrapObjectInternal<T, true>(
 | |
|       obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
 | |
| }
 | |
| 
 | |
| template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
 | |
| MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, OwningNonNull<U>& value,
 | |
|                                         CxType cx) {
 | |
|   return binding_detail::UnwrapObjectInternal<T, true>(
 | |
|       obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
 | |
| }
 | |
| 
 | |
| // An UnwrapObject overload that just calls one of the JSObject* ones.
 | |
| template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
 | |
| MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::Handle<JS::Value> obj, U& value,
 | |
|                                         CxType cx) {
 | |
|   MOZ_ASSERT(obj.isObject());
 | |
|   return UnwrapObject<PrototypeID, T>(&obj.toObject(), value, cx);
 | |
| }
 | |
| 
 | |
| template <prototypes::ID PrototypeID>
 | |
| MOZ_ALWAYS_INLINE void AssertStaticUnwrapOK() {
 | |
|   static_assert(PrototypeID != prototypes::id::Window,
 | |
|                 "Can't do static unwrap of WindowProxy; use "
 | |
|                 "UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT or a cross-origin-object "
 | |
|                 "aware version of IS_INSTANCE_OF");
 | |
|   static_assert(PrototypeID != prototypes::id::EventTarget,
 | |
|                 "Can't do static unwrap of WindowProxy (which an EventTarget "
 | |
|                 "might be); use UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT or a "
 | |
|                 "cross-origin-object aware version of IS_INSTANCE_OF");
 | |
|   static_assert(PrototypeID != prototypes::id::Location,
 | |
|                 "Can't do static unwrap of Location; use "
 | |
|                 "UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT or a cross-origin-object "
 | |
|                 "aware version of IS_INSTANCE_OF");
 | |
| }
 | |
| 
 | |
| namespace binding_detail {
 | |
| // This function is just here so we can do some static asserts in a centralized
 | |
| // place instead of putting them in every single UnwrapObject overload.
 | |
| template <prototypes::ID PrototypeID, class T, typename U, typename V>
 | |
| MOZ_ALWAYS_INLINE nsresult UnwrapObjectWithCrossOriginAsserts(V&& obj,
 | |
|                                                               U& value) {
 | |
|   AssertStaticUnwrapOK<PrototypeID>();
 | |
|   return UnwrapObject<PrototypeID, T>(obj, value, nullptr);
 | |
| }
 | |
| }  // namespace binding_detail
 | |
| 
 | |
| template <prototypes::ID PrototypeID, class T>
 | |
| MOZ_ALWAYS_INLINE bool IsInstanceOf(JSObject* obj) {
 | |
|   AssertStaticUnwrapOK<PrototypeID>();
 | |
|   void* ignored;
 | |
|   nsresult unwrapped = binding_detail::UnwrapObjectInternal<T, true>(
 | |
|       obj, ignored, PrototypeID, PrototypeTraits<PrototypeID>::Depth, nullptr);
 | |
|   return NS_SUCCEEDED(unwrapped);
 | |
| }
 | |
| 
 | |
| template <prototypes::ID PrototypeID, class T, typename U>
 | |
| MOZ_ALWAYS_INLINE nsresult UnwrapNonWrapperObject(JSObject* obj, U& value) {
 | |
|   MOZ_ASSERT(!js::IsWrapper(obj));
 | |
|   return binding_detail::UnwrapObjectInternal<T, false>(
 | |
|       obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, nullptr);
 | |
| }
 | |
| 
 | |
| MOZ_ALWAYS_INLINE bool IsConvertibleToDictionary(JS::Handle<JS::Value> val) {
 | |
|   return val.isNullOrUndefined() || val.isObject();
 | |
| }
 | |
| 
 | |
| // The items in the protoAndIfaceCache are indexed by the prototypes::id::ID,
 | |
| // constructors::id::ID and namedpropertiesobjects::id::ID enums, in that order.
 | |
| // The end of the prototype objects should be the start of the interface
 | |
| // objects, and the end of the interface objects should be the start of the
 | |
| // named properties objects.
 | |
| static_assert((size_t)constructors::id::_ID_Start ==
 | |
|                       (size_t)prototypes::id::_ID_Count &&
 | |
|                   (size_t)namedpropertiesobjects::id::_ID_Start ==
 | |
|                       (size_t)constructors::id::_ID_Count,
 | |
|               "Overlapping or discontiguous indexes.");
 | |
| const size_t kProtoAndIfaceCacheCount = namedpropertiesobjects::id::_ID_Count;
 | |
| 
 | |
| class ProtoAndIfaceCache {
 | |
|   // The caching strategy we use depends on what sort of global we're dealing
 | |
|   // with.  For a window-like global, we want everything to be as fast as
 | |
|   // possible, so we use a flat array, indexed by prototype/constructor ID.
 | |
|   // For everything else (e.g. globals for JSMs), space is more important than
 | |
|   // speed, so we use a two-level lookup table.
 | |
| 
 | |
|   class ArrayCache
 | |
|       : public Array<JS::Heap<JSObject*>, kProtoAndIfaceCacheCount> {
 | |
|    public:
 | |
|     bool HasEntryInSlot(size_t i) { return (*this)[i]; }
 | |
| 
 | |
|     JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) { return (*this)[i]; }
 | |
| 
 | |
|     JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) { return (*this)[i]; }
 | |
| 
 | |
|     void Trace(JSTracer* aTracer) {
 | |
|       for (size_t i = 0; i < ArrayLength(*this); ++i) {
 | |
|         JS::TraceEdge(aTracer, &(*this)[i], "protoAndIfaceCache[i]");
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
 | |
|       return aMallocSizeOf(this);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   class PageTableCache {
 | |
|    public:
 | |
|     PageTableCache() { memset(mPages.begin(), 0, sizeof(mPages)); }
 | |
| 
 | |
|     ~PageTableCache() {
 | |
|       for (size_t i = 0; i < ArrayLength(mPages); ++i) {
 | |
|         delete mPages[i];
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     bool HasEntryInSlot(size_t i) {
 | |
|       MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
 | |
|       size_t pageIndex = i / kPageSize;
 | |
|       size_t leafIndex = i % kPageSize;
 | |
|       Page* p = mPages[pageIndex];
 | |
|       if (!p) {
 | |
|         return false;
 | |
|       }
 | |
|       return (*p)[leafIndex];
 | |
|     }
 | |
| 
 | |
|     JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
 | |
|       MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
 | |
|       size_t pageIndex = i / kPageSize;
 | |
|       size_t leafIndex = i % kPageSize;
 | |
|       Page* p = mPages[pageIndex];
 | |
|       if (!p) {
 | |
|         p = new Page;
 | |
|         mPages[pageIndex] = p;
 | |
|       }
 | |
|       return (*p)[leafIndex];
 | |
|     }
 | |
| 
 | |
|     JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
 | |
|       MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
 | |
|       size_t pageIndex = i / kPageSize;
 | |
|       size_t leafIndex = i % kPageSize;
 | |
|       Page* p = mPages[pageIndex];
 | |
|       MOZ_ASSERT(p);
 | |
|       return (*p)[leafIndex];
 | |
|     }
 | |
| 
 | |
|     void Trace(JSTracer* trc) {
 | |
|       for (size_t i = 0; i < ArrayLength(mPages); ++i) {
 | |
|         Page* p = mPages[i];
 | |
|         if (p) {
 | |
|           for (size_t j = 0; j < ArrayLength(*p); ++j) {
 | |
|             JS::TraceEdge(trc, &(*p)[j], "protoAndIfaceCache[i]");
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
 | |
|       size_t n = aMallocSizeOf(this);
 | |
|       for (size_t i = 0; i < ArrayLength(mPages); ++i) {
 | |
|         n += aMallocSizeOf(mPages[i]);
 | |
|       }
 | |
|       return n;
 | |
|     }
 | |
| 
 | |
|    private:
 | |
|     static const size_t kPageSize = 16;
 | |
|     typedef Array<JS::Heap<JSObject*>, kPageSize> Page;
 | |
|     static const size_t kNPages =
 | |
|         kProtoAndIfaceCacheCount / kPageSize +
 | |
|         size_t(bool(kProtoAndIfaceCacheCount % kPageSize));
 | |
|     Array<Page*, kNPages> mPages;
 | |
|   };
 | |
| 
 | |
|  public:
 | |
|   enum Kind { WindowLike, NonWindowLike };
 | |
| 
 | |
|   explicit ProtoAndIfaceCache(Kind aKind) : mKind(aKind) {
 | |
|     MOZ_COUNT_CTOR(ProtoAndIfaceCache);
 | |
|     if (aKind == WindowLike) {
 | |
|       mArrayCache = new ArrayCache();
 | |
|     } else {
 | |
|       mPageTableCache = new PageTableCache();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ~ProtoAndIfaceCache() {
 | |
|     if (mKind == WindowLike) {
 | |
|       delete mArrayCache;
 | |
|     } else {
 | |
|       delete mPageTableCache;
 | |
|     }
 | |
|     MOZ_COUNT_DTOR(ProtoAndIfaceCache);
 | |
|   }
 | |
| 
 | |
| #define FORWARD_OPERATION(opName, args)    \
 | |
|   do {                                     \
 | |
|     if (mKind == WindowLike) {             \
 | |
|       return mArrayCache->opName args;     \
 | |
|     } else {                               \
 | |
|       return mPageTableCache->opName args; \
 | |
|     }                                      \
 | |
|   } while (0)
 | |
| 
 | |
|   // Return whether slot i contains an object.  This doesn't return the object
 | |
|   // itself because in practice consumers just want to know whether it's there
 | |
|   // or not, and that doesn't require barriering, which returning the object
 | |
|   // pointer does.
 | |
|   bool HasEntryInSlot(size_t i) { FORWARD_OPERATION(HasEntryInSlot, (i)); }
 | |
| 
 | |
|   // Return a reference to slot i, creating it if necessary.  There
 | |
|   // may not be an object in the returned slot.
 | |
|   JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
 | |
|     FORWARD_OPERATION(EntrySlotOrCreate, (i));
 | |
|   }
 | |
| 
 | |
|   // Return a reference to slot i, which is guaranteed to already
 | |
|   // exist.  There may not be an object in the slot, if prototype and
 | |
|   // constructor initialization for one of our bindings failed.
 | |
|   JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
 | |
|     FORWARD_OPERATION(EntrySlotMustExist, (i));
 | |
|   }
 | |
| 
 | |
|   void Trace(JSTracer* aTracer) { FORWARD_OPERATION(Trace, (aTracer)); }
 | |
| 
 | |
|   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
 | |
|     size_t n = aMallocSizeOf(this);
 | |
|     n += (mKind == WindowLike
 | |
|               ? mArrayCache->SizeOfIncludingThis(aMallocSizeOf)
 | |
|               : mPageTableCache->SizeOfIncludingThis(aMallocSizeOf));
 | |
|     return n;
 | |
|   }
 | |
| #undef FORWARD_OPERATION
 | |
| 
 | |
|  private:
 | |
|   union {
 | |
|     ArrayCache* mArrayCache;
 | |
|     PageTableCache* mPageTableCache;
 | |
|   };
 | |
|   Kind mKind;
 | |
| };
 | |
| 
 | |
| inline void AllocateProtoAndIfaceCache(JSObject* obj,
 | |
|                                        ProtoAndIfaceCache::Kind aKind) {
 | |
|   MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
 | |
|   MOZ_ASSERT(js::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).isUndefined());
 | |
| 
 | |
|   ProtoAndIfaceCache* protoAndIfaceCache = new ProtoAndIfaceCache(aKind);
 | |
| 
 | |
|   js::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT,
 | |
|                       JS::PrivateValue(protoAndIfaceCache));
 | |
| }
 | |
| 
 | |
| #ifdef DEBUG
 | |
| struct VerifyTraceProtoAndIfaceCacheCalledTracer : public JS::CallbackTracer {
 | |
|   bool ok;
 | |
| 
 | |
|   explicit VerifyTraceProtoAndIfaceCacheCalledTracer(JSContext* cx)
 | |
|       : JS::CallbackTracer(cx), ok(false) {}
 | |
| 
 | |
|   bool onChild(const JS::GCCellPtr&) override {
 | |
|     // We don't do anything here, we only want to verify that
 | |
|     // TraceProtoAndIfaceCache was called.
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   TracerKind getTracerKind() const override {
 | |
|     return TracerKind::VerifyTraceProtoAndIface;
 | |
|   }
 | |
| };
 | |
| #endif
 | |
| 
 | |
| inline void TraceProtoAndIfaceCache(JSTracer* trc, JSObject* obj) {
 | |
|   MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
 | |
| 
 | |
| #ifdef DEBUG
 | |
|   if (trc->isCallbackTracer() &&
 | |
|       (trc->asCallbackTracer()->getTracerKind() ==
 | |
|        JS::CallbackTracer::TracerKind::VerifyTraceProtoAndIface)) {
 | |
|     // We don't do anything here, we only want to verify that
 | |
|     // TraceProtoAndIfaceCache was called.
 | |
|     static_cast<VerifyTraceProtoAndIfaceCacheCalledTracer*>(trc)->ok = true;
 | |
|     return;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   if (!DOMGlobalHasProtoAndIFaceCache(obj)) return;
 | |
|   ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
 | |
|   protoAndIfaceCache->Trace(trc);
 | |
| }
 | |
| 
 | |
| inline void DestroyProtoAndIfaceCache(JSObject* obj) {
 | |
|   MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
 | |
| 
 | |
|   if (!DOMGlobalHasProtoAndIFaceCache(obj)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
 | |
| 
 | |
|   delete protoAndIfaceCache;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Add constants to an object.
 | |
|  */
 | |
| bool DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
 | |
|                      const ConstantSpec* cs);
 | |
| 
 | |
| struct JSNativeHolder {
 | |
|   JSNative mNative;
 | |
|   const NativePropertyHooks* mPropertyHooks;
 | |
| };
 | |
| 
 | |
| struct NamedConstructor {
 | |
|   const char* mName;
 | |
|   const JSNativeHolder mHolder;
 | |
|   unsigned mNargs;
 | |
| };
 | |
| 
 | |
| // clang-format off
 | |
| /*
 | |
|  * Create a DOM interface object (if constructorClass is non-null) and/or a
 | |
|  * DOM interface prototype object (if protoClass is non-null).
 | |
|  *
 | |
|  * global is used as the parent of the interface object and the interface
 | |
|  *        prototype object
 | |
|  * protoProto is the prototype to use for the interface prototype object.
 | |
|  * interfaceProto is the prototype to use for the interface object.  This can be
 | |
|  *                null if both constructorClass and constructor are null (as in,
 | |
|  *                if we're not creating an interface object at all).
 | |
|  * protoClass is the JSClass to use for the interface prototype object.
 | |
|  *            This is null if we should not create an interface prototype
 | |
|  *            object.
 | |
|  * protoCache a pointer to a JSObject pointer where we should cache the
 | |
|  *            interface prototype object. This must be null if protoClass is and
 | |
|  *            vice versa.
 | |
|  * toStringTag if not null, a string to define as @@toStringTag on the prototype.
 | |
|  *             Must be null if protoClass is.
 | |
|  * constructorClass is the JSClass to use for the interface object.
 | |
|  *                  This is null if we should not create an interface object or
 | |
|  *                  if it should be a function object.
 | |
|  * constructor holds the JSNative to back the interface object which should be a
 | |
|  *             Function, unless constructorClass is non-null in which case it is
 | |
|  *             ignored. If this is null and constructorClass is also null then
 | |
|  *             we should not create an interface object at all.
 | |
|  * ctorNargs is the length of the constructor function; 0 if no constructor
 | |
|  * constructorCache a pointer to a JSObject pointer where we should cache the
 | |
|  *                  interface object. This must be null if both constructorClass
 | |
|  *                  and constructor are null, and non-null otherwise.
 | |
|  * properties contains the methods, attributes and constants to be defined on
 | |
|  *            objects in any compartment.
 | |
|  * chromeProperties contains the methods, attributes and constants to be defined
 | |
|  *                  on objects in chrome compartments. This must be null if the
 | |
|  *                  interface doesn't have any ChromeOnly properties or if the
 | |
|  *                  object is being created in non-chrome compartment.
 | |
|  * defineOnGlobal controls whether properties should be defined on the given
 | |
|  *                global for the interface object (if any) and named
 | |
|  *                constructors (if any) for this interface.  This can be
 | |
|  *                false in situations where we want the properties to only
 | |
|  *                appear on privileged Xrays but not on the unprivileged
 | |
|  *                underlying global.
 | |
|  * unscopableNames if not null it points to a null-terminated list of const
 | |
|  *                 char* names of the unscopable properties for this interface.
 | |
|  * isGlobal if true, we're creating interface objects for a [Global] interface,
 | |
|  *          and hence shouldn't define properties on the prototype object.
 | |
|  * legacyWindowAliases if not null it points to a null-terminated list of const
 | |
|  *                     char* names of the legacy window aliases for this
 | |
|  *                     interface.
 | |
|  *
 | |
|  * At least one of protoClass, constructorClass or constructor should be
 | |
|  * non-null. If constructorClass or constructor are non-null, the resulting
 | |
|  * interface object will be defined on the given global with property name
 | |
|  * |name|, which must also be non-null.
 | |
|  */
 | |
| // clang-format on
 | |
| void CreateInterfaceObjects(
 | |
|     JSContext* cx, JS::Handle<JSObject*> global,
 | |
|     JS::Handle<JSObject*> protoProto, const JSClass* protoClass,
 | |
|     JS::Heap<JSObject*>* protoCache, const char* toStringTag,
 | |
|     JS::Handle<JSObject*> interfaceProto, const JSClass* constructorClass,
 | |
|     unsigned ctorNargs, const NamedConstructor* namedConstructors,
 | |
|     JS::Heap<JSObject*>* constructorCache,
 | |
|     const NativeProperties* regularProperties,
 | |
|     const NativeProperties* chromeOnlyProperties, const char* name,
 | |
|     bool defineOnGlobal, const char* const* unscopableNames, bool isGlobal,
 | |
|     const char* const* legacyWindowAliases);
 | |
| 
 | |
| /**
 | |
|  * Define the properties (regular and chrome-only) on obj.
 | |
|  *
 | |
|  * obj the object to install the properties on. This should be the interface
 | |
|  *     prototype object for regular interfaces and the instance object for
 | |
|  *     interfaces marked with Global.
 | |
|  * properties contains the methods, attributes and constants to be defined on
 | |
|  *            objects in any compartment.
 | |
|  * chromeProperties contains the methods, attributes and constants to be defined
 | |
|  *                  on objects in chrome compartments. This must be null if the
 | |
|  *                  interface doesn't have any ChromeOnly properties or if the
 | |
|  *                  object is being created in non-chrome compartment.
 | |
|  */
 | |
| bool DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj,
 | |
|                       const NativeProperties* properties,
 | |
|                       const NativeProperties* chromeOnlyProperties);
 | |
| 
 | |
| /*
 | |
|  * Define the unforgeable methods on an object.
 | |
|  */
 | |
| bool DefineUnforgeableMethods(JSContext* cx, JS::Handle<JSObject*> obj,
 | |
|                               const Prefable<const JSFunctionSpec>* props);
 | |
| 
 | |
| /*
 | |
|  * Define the unforgeable attributes on an object.
 | |
|  */
 | |
| bool DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj,
 | |
|                                  const Prefable<const JSPropertySpec>* props);
 | |
| 
 | |
| #define HAS_MEMBER_TYPEDEFS \
 | |
|  private:                   \
 | |
|   typedef char yes[1];      \
 | |
|   typedef char no[2]
 | |
| 
 | |
| #ifdef _MSC_VER
 | |
| #  define HAS_MEMBER_CHECK(_name) \
 | |
|     template <typename V>         \
 | |
|     static yes& Check##_name(char(*)[(&V::_name == 0) + 1])
 | |
| #else
 | |
| #  define HAS_MEMBER_CHECK(_name) \
 | |
|     template <typename V>         \
 | |
|     static yes& Check##_name(char(*)[sizeof(&V::_name) + 1])
 | |
| #endif
 | |
| 
 | |
| #define HAS_MEMBER(_memberName, _valueName) \
 | |
|  private:                                   \
 | |
|   HAS_MEMBER_CHECK(_memberName);            \
 | |
|   template <typename V>                     \
 | |
|   static no& Check##_memberName(...);       \
 | |
|                                             \
 | |
|  public:                                    \
 | |
|   static bool const _valueName =            \
 | |
|       sizeof(Check##_memberName<T>(nullptr)) == sizeof(yes)
 | |
| 
 | |
| template <class T>
 | |
| struct NativeHasMember {
 | |
|   HAS_MEMBER_TYPEDEFS;
 | |
| 
 | |
|   HAS_MEMBER(GetParentObject, GetParentObject);
 | |
|   HAS_MEMBER(WrapObject, WrapObject);
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| struct IsSmartPtr {
 | |
|   HAS_MEMBER_TYPEDEFS;
 | |
| 
 | |
|   HAS_MEMBER(get, value);
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| struct IsRefcounted {
 | |
|   HAS_MEMBER_TYPEDEFS;
 | |
| 
 | |
|   HAS_MEMBER(AddRef, HasAddref);
 | |
|   HAS_MEMBER(Release, HasRelease);
 | |
| 
 | |
|  public:
 | |
|   static bool const value = HasAddref && HasRelease;
 | |
| 
 | |
|  private:
 | |
|   // This struct only works if T is fully declared (not just forward declared).
 | |
|   // The IsBaseOf check will ensure that, we don't really need it for any other
 | |
|   // reason (the static assert will of course always be true).
 | |
|   static_assert(!std::is_base_of<nsISupports, T>::value || IsRefcounted::value,
 | |
|                 "Classes derived from nsISupports are refcounted!");
 | |
| };
 | |
| 
 | |
| #undef HAS_MEMBER
 | |
| #undef HAS_MEMBER_CHECK
 | |
| #undef HAS_MEMBER_TYPEDEFS
 | |
| 
 | |
| #ifdef DEBUG
 | |
| template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
 | |
| struct CheckWrapperCacheCast {
 | |
|   static bool Check() {
 | |
|     return reinterpret_cast<uintptr_t>(
 | |
|                static_cast<nsWrapperCache*>(reinterpret_cast<T*>(1))) == 1;
 | |
|   }
 | |
| };
 | |
| template <class T>
 | |
| struct CheckWrapperCacheCast<T, true> {
 | |
|   static bool Check() { return true; }
 | |
| };
 | |
| #endif
 | |
| 
 | |
| inline bool TryToOuterize(JS::MutableHandle<JS::Value> rval) {
 | |
|   if (js::IsWindow(&rval.toObject())) {
 | |
|     JSObject* obj = js::ToWindowProxyIfWindow(&rval.toObject());
 | |
|     MOZ_ASSERT(obj);
 | |
|     rval.set(JS::ObjectValue(*obj));
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| inline bool TryToOuterize(JS::MutableHandle<JSObject*> obj) {
 | |
|   if (js::IsWindow(obj)) {
 | |
|     JSObject* proxy = js::ToWindowProxyIfWindow(obj);
 | |
|     MOZ_ASSERT(proxy);
 | |
|     obj.set(proxy);
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| // Make sure to wrap the given string value into the right compartment, as
 | |
| // needed.
 | |
| MOZ_ALWAYS_INLINE
 | |
| bool MaybeWrapStringValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) {
 | |
|   MOZ_ASSERT(rval.isString());
 | |
|   JSString* str = rval.toString();
 | |
|   if (JS::GetStringZone(str) != js::GetContextZone(cx)) {
 | |
|     return JS_WrapValue(cx, rval);
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| // Make sure to wrap the given object value into the right compartment as
 | |
| // needed.  This will work correctly, but possibly slowly, on all objects.
 | |
| MOZ_ALWAYS_INLINE
 | |
| bool MaybeWrapObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) {
 | |
|   MOZ_ASSERT(rval.isObject());
 | |
| 
 | |
|   // Cross-compartment always requires wrapping.
 | |
|   JSObject* obj = &rval.toObject();
 | |
|   if (js::GetObjectCompartment(obj) != js::GetContextCompartment(cx)) {
 | |
|     return JS_WrapValue(cx, rval);
 | |
|   }
 | |
| 
 | |
|   // We're same-compartment, but we might still need to outerize if we
 | |
|   // have a Window.
 | |
|   return TryToOuterize(rval);
 | |
| }
 | |
| 
 | |
| // Like MaybeWrapObjectValue, but working with a
 | |
| // JS::MutableHandle<JSObject*> which must be non-null.
 | |
| MOZ_ALWAYS_INLINE
 | |
| bool MaybeWrapObject(JSContext* cx, JS::MutableHandle<JSObject*> obj) {
 | |
|   if (js::GetObjectCompartment(obj) != js::GetContextCompartment(cx)) {
 | |
|     return JS_WrapObject(cx, obj);
 | |
|   }
 | |
| 
 | |
|   // We're same-compartment, but we might still need to outerize if we
 | |
|   // have a Window.
 | |
|   return TryToOuterize(obj);
 | |
| }
 | |
| 
 | |
| // Like MaybeWrapObjectValue, but also allows null
 | |
| MOZ_ALWAYS_INLINE
 | |
| bool MaybeWrapObjectOrNullValue(JSContext* cx,
 | |
|                                 JS::MutableHandle<JS::Value> rval) {
 | |
|   MOZ_ASSERT(rval.isObjectOrNull());
 | |
|   if (rval.isNull()) {
 | |
|     return true;
 | |
|   }
 | |
|   return MaybeWrapObjectValue(cx, rval);
 | |
| }
 | |
| 
 | |
| // Wrapping for objects that are known to not be DOM objects
 | |
| MOZ_ALWAYS_INLINE
 | |
| bool MaybeWrapNonDOMObjectValue(JSContext* cx,
 | |
|                                 JS::MutableHandle<JS::Value> rval) {
 | |
|   MOZ_ASSERT(rval.isObject());
 | |
|   // Compared to MaybeWrapObjectValue we just skip the TryToOuterize call.  The
 | |
|   // only reason it would be needed is if we have a Window object, which would
 | |
|   // have a DOM class.  Assert that we don't have any DOM-class objects coming
 | |
|   // through here.
 | |
|   MOZ_ASSERT(!GetDOMClass(&rval.toObject()));
 | |
| 
 | |
|   JSObject* obj = &rval.toObject();
 | |
|   if (js::GetObjectCompartment(obj) == js::GetContextCompartment(cx)) {
 | |
|     return true;
 | |
|   }
 | |
|   return JS_WrapValue(cx, rval);
 | |
| }
 | |
| 
 | |
| // Like MaybeWrapNonDOMObjectValue but allows null
 | |
| MOZ_ALWAYS_INLINE
 | |
| bool MaybeWrapNonDOMObjectOrNullValue(JSContext* cx,
 | |
|                                       JS::MutableHandle<JS::Value> rval) {
 | |
|   MOZ_ASSERT(rval.isObjectOrNull());
 | |
|   if (rval.isNull()) {
 | |
|     return true;
 | |
|   }
 | |
|   return MaybeWrapNonDOMObjectValue(cx, rval);
 | |
| }
 | |
| 
 | |
| // If rval is a gcthing and is not in the compartment of cx, wrap rval
 | |
| // into the compartment of cx (typically by replacing it with an Xray or
 | |
| // cross-compartment wrapper around the original object).
 | |
| MOZ_ALWAYS_INLINE bool MaybeWrapValue(JSContext* cx,
 | |
|                                       JS::MutableHandle<JS::Value> rval) {
 | |
|   if (rval.isGCThing()) {
 | |
|     if (rval.isString()) {
 | |
|       return MaybeWrapStringValue(cx, rval);
 | |
|     }
 | |
|     if (rval.isObject()) {
 | |
|       return MaybeWrapObjectValue(cx, rval);
 | |
|     }
 | |
|     // This could be optimized by checking the zone first, similar to
 | |
|     // the way strings are handled. At present, this is used primarily
 | |
|     // for structured cloning, so avoiding the overhead of JS_WrapValue
 | |
|     // calls is less important than for other types.
 | |
|     if (rval.isBigInt()) {
 | |
|       return JS_WrapValue(cx, rval);
 | |
|     }
 | |
|     MOZ_ASSERT(rval.isSymbol());
 | |
|     JS_MarkCrossZoneId(cx, SYMBOL_TO_JSID(rval.toSymbol()));
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| namespace binding_detail {
 | |
| enum GetOrCreateReflectorWrapBehavior {
 | |
|   eWrapIntoContextCompartment,
 | |
|   eDontWrapIntoContextCompartment
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| struct TypeNeedsOuterization {
 | |
|   // We only need to outerize Window objects, so anything inheriting from
 | |
|   // nsGlobalWindow (which inherits from EventTarget itself).
 | |
|   static const bool value = std::is_base_of<nsGlobalWindowInner, T>::value ||
 | |
|                             std::is_base_of<nsGlobalWindowOuter, T>::value ||
 | |
|                             IsSame<EventTarget, T>::value;
 | |
| };
 | |
| 
 | |
| #ifdef DEBUG
 | |
| template <typename T, bool isISupports = std::is_base_of<nsISupports, T>::value>
 | |
| struct CheckWrapperCacheTracing {
 | |
|   static inline void Check(T* aObject) {}
 | |
| };
 | |
| 
 | |
| template <typename T>
 | |
| struct CheckWrapperCacheTracing<T, true> {
 | |
|   static void Check(T* aObject) {
 | |
|     // Rooting analysis thinks QueryInterface may GC, but we're dealing with
 | |
|     // a subset of QueryInterface, C++ only types here.
 | |
|     JS::AutoSuppressGCAnalysis nogc;
 | |
| 
 | |
|     nsWrapperCache* wrapperCacheFromQI = nullptr;
 | |
|     aObject->QueryInterface(NS_GET_IID(nsWrapperCache),
 | |
|                             reinterpret_cast<void**>(&wrapperCacheFromQI));
 | |
| 
 | |
|     MOZ_ASSERT(wrapperCacheFromQI,
 | |
|                "Missing nsWrapperCache from QueryInterface implementation?");
 | |
| 
 | |
|     if (!wrapperCacheFromQI->GetWrapperPreserveColor()) {
 | |
|       // Can't assert that we trace the wrapper, since we don't have any
 | |
|       // wrapper to trace.
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     nsISupports* ccISupports = nullptr;
 | |
|     aObject->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
 | |
|                             reinterpret_cast<void**>(&ccISupports));
 | |
|     MOZ_ASSERT(ccISupports,
 | |
|                "nsWrapperCache object which isn't cycle collectable?");
 | |
| 
 | |
|     nsXPCOMCycleCollectionParticipant* participant = nullptr;
 | |
|     CallQueryInterface(ccISupports, &participant);
 | |
|     MOZ_ASSERT(participant, "Can't QI to CycleCollectionParticipant?");
 | |
| 
 | |
|     bool wasPreservingWrapper = wrapperCacheFromQI->PreservingWrapper();
 | |
|     wrapperCacheFromQI->SetPreservingWrapper(true);
 | |
|     wrapperCacheFromQI->CheckCCWrapperTraversal(ccISupports, participant);
 | |
|     wrapperCacheFromQI->SetPreservingWrapper(wasPreservingWrapper);
 | |
|   }
 | |
| };
 | |
| 
 | |
| void AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
 | |
|                                   JS::Handle<JSObject*> aGivenProto);
 | |
| #endif  // DEBUG
 | |
| 
 | |
| template <class T, GetOrCreateReflectorWrapBehavior wrapBehavior>
 | |
| MOZ_ALWAYS_INLINE bool DoGetOrCreateDOMReflector(
 | |
|     JSContext* cx, T* value, JS::Handle<JSObject*> givenProto,
 | |
|     JS::MutableHandle<JS::Value> rval) {
 | |
|   MOZ_ASSERT(value);
 | |
|   MOZ_ASSERT_IF(givenProto, js::IsObjectInContextCompartment(givenProto, cx));
 | |
|   JSObject* obj = value->GetWrapper();
 | |
|   if (obj) {
 | |
| #ifdef DEBUG
 | |
|     AssertReflectorHasGivenProto(cx, obj, givenProto);
 | |
|     // Have to reget obj because AssertReflectorHasGivenProto can
 | |
|     // trigger gc so the pointer may now be invalid.
 | |
|     obj = value->GetWrapper();
 | |
| #endif
 | |
|   } else {
 | |
|     obj = value->WrapObject(cx, givenProto);
 | |
|     if (!obj) {
 | |
|       // At this point, obj is null, so just return false.
 | |
|       // Callers seem to be testing JS_IsExceptionPending(cx) to
 | |
|       // figure out whether WrapObject() threw.
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
| #ifdef DEBUG
 | |
|     if (std::is_base_of<nsWrapperCache, T>::value) {
 | |
|       CheckWrapperCacheTracing<T>::Check(value);
 | |
|     }
 | |
| #endif
 | |
|   }
 | |
| 
 | |
| #ifdef DEBUG
 | |
|   const DOMJSClass* clasp = GetDOMClass(obj);
 | |
|   // clasp can be null if the cache contained a non-DOM object.
 | |
|   if (clasp) {
 | |
|     // Some sanity asserts about our object.  Specifically:
 | |
|     // 1)  If our class claims we're nsISupports, we better be nsISupports
 | |
|     //     XXXbz ideally, we could assert that reinterpret_cast to nsISupports
 | |
|     //     does the right thing, but I don't see a way to do it.  :(
 | |
|     // 2)  If our class doesn't claim we're nsISupports we better be
 | |
|     //     reinterpret_castable to nsWrapperCache.
 | |
|     MOZ_ASSERT(clasp, "What happened here?");
 | |
|     MOZ_ASSERT_IF(clasp->mDOMObjectIsISupports,
 | |
|                   (std::is_base_of<nsISupports, T>::value));
 | |
|     MOZ_ASSERT(CheckWrapperCacheCast<T>::Check());
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   rval.set(JS::ObjectValue(*obj));
 | |
| 
 | |
|   if (js::GetObjectCompartment(obj) == js::GetContextCompartment(cx)) {
 | |
|     return TypeNeedsOuterization<T>::value ? TryToOuterize(rval) : true;
 | |
|   }
 | |
| 
 | |
|   if (wrapBehavior == eDontWrapIntoContextCompartment) {
 | |
|     if (TypeNeedsOuterization<T>::value) {
 | |
|       JSAutoRealm ar(cx, obj);
 | |
|       return TryToOuterize(rval);
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return JS_WrapValue(cx, rval);
 | |
| }
 | |
| 
 | |
| }  // namespace binding_detail
 | |
| 
 | |
| // Create a JSObject wrapping "value", if there isn't one already, and store it
 | |
| // in rval.  "value" must be a concrete class that implements a
 | |
| // GetWrapperPreserveColor() which can return its existing wrapper, if any, and
 | |
| // a WrapObject() which will try to create a wrapper. Typically, this is done by
 | |
| // having "value" inherit from nsWrapperCache.
 | |
| //
 | |
| // The value stored in rval will be ready to be exposed to whatever JS
 | |
| // is running on cx right now.  In particular, it will be in the
 | |
| // compartment of cx, and outerized as needed.
 | |
| template <class T>
 | |
| MOZ_ALWAYS_INLINE bool GetOrCreateDOMReflector(
 | |
|     JSContext* cx, T* value, JS::MutableHandle<JS::Value> rval,
 | |
|     JS::Handle<JSObject*> givenProto = nullptr) {
 | |
|   using namespace binding_detail;
 | |
|   return DoGetOrCreateDOMReflector<T, eWrapIntoContextCompartment>(
 | |
|       cx, value, givenProto, rval);
 | |
| }
 | |
| 
 | |
| // Like GetOrCreateDOMReflector but doesn't wrap into the context compartment,
 | |
| // and hence does not actually require cx to be in a compartment.
 | |
| template <class T>
 | |
| MOZ_ALWAYS_INLINE bool GetOrCreateDOMReflectorNoWrap(
 | |
|     JSContext* cx, T* value, JS::MutableHandle<JS::Value> rval) {
 | |
|   using namespace binding_detail;
 | |
|   return DoGetOrCreateDOMReflector<T, eDontWrapIntoContextCompartment>(
 | |
|       cx, value, nullptr, rval);
 | |
| }
 | |
| 
 | |
| // Create a JSObject wrapping "value", for cases when "value" is a
 | |
| // non-wrapper-cached object using WebIDL bindings.  "value" must implement a
 | |
| // WrapObject() method taking a JSContext and a prototype (possibly null) and
 | |
| // returning the resulting object via a MutableHandle<JSObject*> outparam.
 | |
| template <class T>
 | |
| inline bool WrapNewBindingNonWrapperCachedObject(
 | |
|     JSContext* cx, JS::Handle<JSObject*> scopeArg, T* value,
 | |
|     JS::MutableHandle<JS::Value> rval,
 | |
|     JS::Handle<JSObject*> givenProto = nullptr) {
 | |
|   static_assert(IsRefcounted<T>::value, "Don't pass owned classes in here.");
 | |
|   MOZ_ASSERT(value);
 | |
|   // We try to wrap in the realm of the underlying object of "scope"
 | |
|   JS::Rooted<JSObject*> obj(cx);
 | |
|   {
 | |
|     // scope for the JSAutoRealm so that we restore the realm
 | |
|     // before we call JS_WrapValue.
 | |
|     Maybe<JSAutoRealm> ar;
 | |
|     // Maybe<Handle> doesn't so much work, and in any case, adding
 | |
|     // more Maybe (one for a Rooted and one for a Handle) adds more
 | |
|     // code (and branches!) than just adding a single rooted.
 | |
|     JS::Rooted<JSObject*> scope(cx, scopeArg);
 | |
|     JS::Rooted<JSObject*> proto(cx, givenProto);
 | |
|     if (js::IsWrapper(scope)) {
 | |
|       // We are working in the Realm of cx and will be producing our reflector
 | |
|       // there, so we need to succeed if that realm has access to the scope.
 | |
|       scope =
 | |
|           js::CheckedUnwrapDynamic(scope, cx, /* stopAtWindowProxy = */ false);
 | |
|       if (!scope) return false;
 | |
|       ar.emplace(cx, scope);
 | |
|       if (!JS_WrapObject(cx, &proto)) {
 | |
|         return false;
 | |
|       }
 | |
|     } else {
 | |
|       // cx and scope are same-compartment, but they might still be
 | |
|       // different-Realm.  Enter the Realm of scope, since that's
 | |
|       // where we want to create our object.
 | |
|       ar.emplace(cx, scope);
 | |
|     }
 | |
| 
 | |
|     MOZ_ASSERT_IF(proto, js::IsObjectInContextCompartment(proto, cx));
 | |
|     MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
 | |
|     if (!value->WrapObject(cx, proto, &obj)) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // We can end up here in all sorts of compartments, per above.  Make
 | |
|   // sure to JS_WrapValue!
 | |
|   rval.set(JS::ObjectValue(*obj));
 | |
|   return MaybeWrapObjectValue(cx, rval);
 | |
| }
 | |
| 
 | |
| // Create a JSObject wrapping "value", for cases when "value" is a
 | |
| // non-wrapper-cached owned object using WebIDL bindings.  "value" must
 | |
| // implement a WrapObject() method taking a taking a JSContext and a prototype
 | |
| // (possibly null) and returning two pieces of information: the resulting object
 | |
| // via a MutableHandle<JSObject*> outparam and a boolean return value that is
 | |
| // true if the JSObject took ownership
 | |
| template <class T>
 | |
| inline bool WrapNewBindingNonWrapperCachedObject(
 | |
|     JSContext* cx, JS::Handle<JSObject*> scopeArg, nsAutoPtr<T>& value,
 | |
|     JS::MutableHandle<JS::Value> rval,
 | |
|     JS::Handle<JSObject*> givenProto = nullptr) {
 | |
|   static_assert(!IsRefcounted<T>::value, "Only pass owned classes in here.");
 | |
|   // We do a runtime check on value, because otherwise we might in
 | |
|   // fact end up wrapping a null and invoking methods on it later.
 | |
|   if (!value) {
 | |
|     MOZ_CRASH("Don't try to wrap null objects");
 | |
|   }
 | |
|   // We try to wrap in the realm of the underlying object of "scope"
 | |
|   JS::Rooted<JSObject*> obj(cx);
 | |
|   {
 | |
|     // scope for the JSAutoRealm so that we restore the realm
 | |
|     // before we call JS_WrapValue.
 | |
|     Maybe<JSAutoRealm> ar;
 | |
|     // Maybe<Handle> doesn't so much work, and in any case, adding
 | |
|     // more Maybe (one for a Rooted and one for a Handle) adds more
 | |
|     // code (and branches!) than just adding a single rooted.
 | |
|     JS::Rooted<JSObject*> scope(cx, scopeArg);
 | |
|     JS::Rooted<JSObject*> proto(cx, givenProto);
 | |
|     if (js::IsWrapper(scope)) {
 | |
|       // We are working in the Realm of cx and will be producing our reflector
 | |
|       // there, so we need to succeed if that realm has access to the scope.
 | |
|       scope =
 | |
|           js::CheckedUnwrapDynamic(scope, cx, /* stopAtWindowProxy = */ false);
 | |
|       if (!scope) return false;
 | |
|       ar.emplace(cx, scope);
 | |
|       if (!JS_WrapObject(cx, &proto)) {
 | |
|         return false;
 | |
|       }
 | |
|     } else {
 | |
|       // cx and scope are same-compartment, but they might still be
 | |
|       // different-Realm.  Enter the Realm of scope, since that's
 | |
|       // where we want to create our object.
 | |
|       ar.emplace(cx, scope);
 | |
|     }
 | |
| 
 | |
|     MOZ_ASSERT_IF(proto, js::IsObjectInContextCompartment(proto, cx));
 | |
|     MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
 | |
|     if (!value->WrapObject(cx, proto, &obj)) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     value.forget();
 | |
|   }
 | |
| 
 | |
|   // We can end up here in all sorts of compartments, per above.  Make
 | |
|   // sure to JS_WrapValue!
 | |
|   rval.set(JS::ObjectValue(*obj));
 | |
|   return MaybeWrapObjectValue(cx, rval);
 | |
| }
 | |
| 
 | |
| // Helper for smart pointers (nsRefPtr/nsCOMPtr).
 | |
| template <
 | |
|     template <typename> class SmartPtr, typename T,
 | |
|     typename U = typename EnableIf<IsRefcounted<T>::value, T>::Type,
 | |
|     typename V = typename EnableIf<IsSmartPtr<SmartPtr<T>>::value, T>::Type>
 | |
| inline bool WrapNewBindingNonWrapperCachedObject(
 | |
|     JSContext* cx, JS::Handle<JSObject*> scope, const SmartPtr<T>& value,
 | |
|     JS::MutableHandle<JS::Value> rval,
 | |
|     JS::Handle<JSObject*> givenProto = nullptr) {
 | |
|   return WrapNewBindingNonWrapperCachedObject(cx, scope, value.get(), rval,
 | |
|                                               givenProto);
 | |
| }
 | |
| 
 | |
| // Helper for object references (as opposed to pointers).
 | |
| template <typename T,
 | |
|           typename U = typename EnableIf<!IsSmartPtr<T>::value, T>::Type>
 | |
| inline bool WrapNewBindingNonWrapperCachedObject(
 | |
|     JSContext* cx, JS::Handle<JSObject*> scope, T& value,
 | |
|     JS::MutableHandle<JS::Value> rval,
 | |
|     JS::Handle<JSObject*> givenProto = nullptr) {
 | |
|   return WrapNewBindingNonWrapperCachedObject(cx, scope, &value, rval,
 | |
|                                               givenProto);
 | |
| }
 | |
| 
 | |
| template <bool Fatal>
 | |
| inline bool EnumValueNotFound(JSContext* cx, JS::HandleString str,
 | |
|                               const char* type, const char* sourceDescription);
 | |
| 
 | |
| template <>
 | |
| inline bool EnumValueNotFound<false>(JSContext* cx, JS::HandleString str,
 | |
|                                      const char* type,
 | |
|                                      const char* sourceDescription) {
 | |
|   // TODO: Log a warning to the console.
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| template <>
 | |
| inline bool EnumValueNotFound<true>(JSContext* cx, JS::HandleString str,
 | |
|                                     const char* type,
 | |
|                                     const char* sourceDescription) {
 | |
|   JS::UniqueChars deflated = JS_EncodeStringToUTF8(cx, str);
 | |
|   if (!deflated) {
 | |
|     return false;
 | |
|   }
 | |
|   return ThrowErrorMessage(cx, MSG_INVALID_ENUM_VALUE, sourceDescription,
 | |
|                            deflated.get(), type);
 | |
| }
 | |
| 
 | |
| template <typename CharT>
 | |
| inline int FindEnumStringIndexImpl(const CharT* chars, size_t length,
 | |
|                                    const EnumEntry* values) {
 | |
|   int i = 0;
 | |
|   for (const EnumEntry* value = values; value->value; ++value, ++i) {
 | |
|     if (length != value->length) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     bool equal = true;
 | |
|     const char* val = value->value;
 | |
|     for (size_t j = 0; j != length; ++j) {
 | |
|       if (unsigned(val[j]) != unsigned(chars[j])) {
 | |
|         equal = false;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (equal) {
 | |
|       return i;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| template <bool InvalidValueFatal>
 | |
| inline bool FindEnumStringIndex(JSContext* cx, JS::Handle<JS::Value> v,
 | |
|                                 const EnumEntry* values, const char* type,
 | |
|                                 const char* sourceDescription, int* index) {
 | |
|   // JS_StringEqualsAscii is slow as molasses, so don't use it here.
 | |
|   JS::RootedString str(cx, JS::ToString(cx, v));
 | |
|   if (!str) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     size_t length;
 | |
|     JS::AutoCheckCannotGC nogc;
 | |
|     if (js::StringHasLatin1Chars(str)) {
 | |
|       const JS::Latin1Char* chars =
 | |
|           JS_GetLatin1StringCharsAndLength(cx, nogc, str, &length);
 | |
|       if (!chars) {
 | |
|         return false;
 | |
|       }
 | |
|       *index = FindEnumStringIndexImpl(chars, length, values);
 | |
|     } else {
 | |
|       const char16_t* chars =
 | |
|           JS_GetTwoByteStringCharsAndLength(cx, nogc, str, &length);
 | |
|       if (!chars) {
 | |
|         return false;
 | |
|       }
 | |
|       *index = FindEnumStringIndexImpl(chars, length, values);
 | |
|     }
 | |
|     if (*index >= 0) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EnumValueNotFound<InvalidValueFatal>(cx, str, type, sourceDescription);
 | |
| }
 | |
| 
 | |
| inline nsWrapperCache* GetWrapperCache(const ParentObject& aParentObject) {
 | |
|   return aParentObject.mWrapperCache;
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| inline T* GetParentPointer(T* aObject) {
 | |
|   return aObject;
 | |
| }
 | |
| 
 | |
| inline nsISupports* GetParentPointer(const ParentObject& aObject) {
 | |
|   return aObject.mObject;
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| inline mozilla::dom::ReflectionScope GetReflectionScope(T* aParentObject) {
 | |
|   return mozilla::dom::ReflectionScope::Content;
 | |
| }
 | |
| 
 | |
| inline mozilla::dom::ReflectionScope GetReflectionScope(
 | |
|     const ParentObject& aParentObject) {
 | |
|   return aParentObject.mReflectionScope;
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| inline void ClearWrapper(T* p, nsWrapperCache* cache, JSObject* obj) {
 | |
|   MOZ_ASSERT(cache->GetWrapperMaybeDead() == obj ||
 | |
|              (js::RuntimeIsBeingDestroyed() && !cache->GetWrapperMaybeDead()));
 | |
|   cache->ClearWrapper(obj);
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| inline void ClearWrapper(T* p, void*, JSObject* obj) {
 | |
|   // QueryInterface to nsWrapperCache can't GC, we hope.
 | |
|   JS::AutoSuppressGCAnalysis nogc;
 | |
| 
 | |
|   nsWrapperCache* cache;
 | |
|   CallQueryInterface(p, &cache);
 | |
|   ClearWrapper(p, cache, obj);
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| inline void UpdateWrapper(T* p, nsWrapperCache* cache, JSObject* obj,
 | |
|                           const JSObject* old) {
 | |
|   JS::AutoAssertGCCallback inCallback;
 | |
|   cache->UpdateWrapper(obj, old);
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| inline void UpdateWrapper(T* p, void*, JSObject* obj, const JSObject* old) {
 | |
|   JS::AutoAssertGCCallback inCallback;
 | |
|   nsWrapperCache* cache;
 | |
|   CallQueryInterface(p, &cache);
 | |
|   UpdateWrapper(p, cache, obj, old);
 | |
| }
 | |
| 
 | |
| // Attempt to preserve the wrapper, if any, for a Paris DOM bindings object.
 | |
| // Return true if we successfully preserved the wrapper, or there is no wrapper
 | |
| // to preserve. In the latter case we don't need to preserve the wrapper,
 | |
| // because the object can only be obtained by JS once, or they cannot be
 | |
| // meaningfully owned from the native side.
 | |
| //
 | |
| // This operation will return false only for non-nsISupports cycle-collected
 | |
| // objects, because we cannot determine if they are wrappercached or not.
 | |
| bool TryPreserveWrapper(JS::Handle<JSObject*> obj);
 | |
| 
 | |
| // Can only be called with a DOM JSClass.
 | |
| bool InstanceClassHasProtoAtDepth(const JSClass* clasp, uint32_t protoID,
 | |
|                                   uint32_t depth);
 | |
| 
 | |
| // Only set allowNativeWrapper to false if you really know you need it; if in
 | |
| // doubt use true. Setting it to false disables security wrappers.
 | |
| bool XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
 | |
|                         xpcObjectHelper& helper, const nsIID* iid,
 | |
|                         bool allowNativeWrapper,
 | |
|                         JS::MutableHandle<JS::Value> rval);
 | |
| 
 | |
| // Special-cased wrapping for variants
 | |
| bool VariantToJsval(JSContext* aCx, nsIVariant* aVariant,
 | |
|                     JS::MutableHandle<JS::Value> aRetval);
 | |
| 
 | |
| // Wrap an object "p" which is not using WebIDL bindings yet.  This _will_
 | |
| // actually work on WebIDL binding objects that are wrappercached, but will be
 | |
| // much slower than GetOrCreateDOMReflector.  "cache" must either be null or be
 | |
| // the nsWrapperCache for "p".
 | |
| template <class T>
 | |
| inline bool WrapObject(JSContext* cx, T* p, nsWrapperCache* cache,
 | |
|                        const nsIID* iid, JS::MutableHandle<JS::Value> rval) {
 | |
|   if (xpc_FastGetCachedWrapper(cx, cache, rval)) return true;
 | |
|   xpcObjectHelper helper(ToSupports(p), cache);
 | |
|   JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
 | |
|   return XPCOMObjectToJsval(cx, scope, helper, iid, true, rval);
 | |
| }
 | |
| 
 | |
| // A specialization of the above for nsIVariant, because that needs to
 | |
| // do something different.
 | |
| template <>
 | |
| inline bool WrapObject<nsIVariant>(JSContext* cx, nsIVariant* p,
 | |
|                                    nsWrapperCache* cache, const nsIID* iid,
 | |
|                                    JS::MutableHandle<JS::Value> rval) {
 | |
|   MOZ_ASSERT(iid);
 | |
|   MOZ_ASSERT(iid->Equals(NS_GET_IID(nsIVariant)));
 | |
|   return VariantToJsval(cx, p, rval);
 | |
| }
 | |
| 
 | |
| // Wrap an object "p" which is not using WebIDL bindings yet.  Just like the
 | |
| // variant that takes an nsWrapperCache above, but will try to auto-derive the
 | |
| // nsWrapperCache* from "p".
 | |
| template <class T>
 | |
| inline bool WrapObject(JSContext* cx, T* p, const nsIID* iid,
 | |
|                        JS::MutableHandle<JS::Value> rval) {
 | |
|   return WrapObject(cx, p, GetWrapperCache(p), iid, rval);
 | |
| }
 | |
| 
 | |
| // Just like the WrapObject above, but without requiring you to pick which
 | |
| // interface you're wrapping as.  This should only be used for objects that have
 | |
| // classinfo, for which it doesn't matter what IID is used to wrap.
 | |
| template <class T>
 | |
| inline bool WrapObject(JSContext* cx, T* p, JS::MutableHandle<JS::Value> rval) {
 | |
|   return WrapObject(cx, p, nullptr, rval);
 | |
| }
 | |
| 
 | |
| // Helper to make it possible to wrap directly out of an nsCOMPtr
 | |
| template <class T>
 | |
| inline bool WrapObject(JSContext* cx, const nsCOMPtr<T>& p, const nsIID* iid,
 | |
|                        JS::MutableHandle<JS::Value> rval) {
 | |
|   return WrapObject(cx, p.get(), iid, rval);
 | |
| }
 | |
| 
 | |
| // Helper to make it possible to wrap directly out of an nsCOMPtr
 | |
| template <class T>
 | |
| inline bool WrapObject(JSContext* cx, const nsCOMPtr<T>& p,
 | |
|                        JS::MutableHandle<JS::Value> rval) {
 | |
|   return WrapObject(cx, p, nullptr, rval);
 | |
| }
 | |
| 
 | |
| // Helper to make it possible to wrap directly out of an nsRefPtr
 | |
| template <class T>
 | |
| inline bool WrapObject(JSContext* cx, const RefPtr<T>& p, const nsIID* iid,
 | |
|                        JS::MutableHandle<JS::Value> rval) {
 | |
|   return WrapObject(cx, p.get(), iid, rval);
 | |
| }
 | |
| 
 | |
| // Helper to make it possible to wrap directly out of an nsRefPtr
 | |
| template <class T>
 | |
| inline bool WrapObject(JSContext* cx, const RefPtr<T>& p,
 | |
|                        JS::MutableHandle<JS::Value> rval) {
 | |
|   return WrapObject(cx, p, nullptr, rval);
 | |
| }
 | |
| 
 | |
| // Specialization to make it easy to use WrapObject in codegen.
 | |
| template <>
 | |
| inline bool WrapObject<JSObject>(JSContext* cx, JSObject* p,
 | |
|                                  JS::MutableHandle<JS::Value> rval) {
 | |
|   rval.set(JS::ObjectOrNullValue(p));
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| inline bool WrapObject(JSContext* cx, JSObject& p,
 | |
|                        JS::MutableHandle<JS::Value> rval) {
 | |
|   rval.set(JS::ObjectValue(p));
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool WrapObject(JSContext* cx, const WindowProxyHolder& p,
 | |
|                 JS::MutableHandle<JS::Value> rval);
 | |
| 
 | |
| // Given an object "p" that inherits from nsISupports, wrap it and return the
 | |
| // result.  Null is returned on wrapping failure.  This is somewhat similar to
 | |
| // WrapObject() above, but does NOT allow Xrays around the result, since we
 | |
| // don't want those for our parent object.
 | |
| template <typename T>
 | |
| static inline JSObject* WrapNativeISupports(JSContext* cx, T* p,
 | |
|                                             nsWrapperCache* cache) {
 | |
|   JS::Rooted<JSObject*> retval(cx);
 | |
|   {
 | |
|     xpcObjectHelper helper(ToSupports(p), cache);
 | |
|     JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
 | |
|     JS::Rooted<JS::Value> v(cx);
 | |
|     retval = XPCOMObjectToJsval(cx, scope, helper, nullptr, false, &v)
 | |
|                  ? v.toObjectOrNull()
 | |
|                  : nullptr;
 | |
|   }
 | |
|   return retval;
 | |
| }
 | |
| 
 | |
| // Wrapping of our native parent, for cases when it's a WebIDL object.
 | |
| template <typename T, bool hasWrapObject = NativeHasMember<T>::WrapObject>
 | |
| struct WrapNativeHelper {
 | |
|   static inline JSObject* Wrap(JSContext* cx, T* parent,
 | |
|                                nsWrapperCache* cache) {
 | |
|     MOZ_ASSERT(cache);
 | |
| 
 | |
|     JSObject* obj;
 | |
|     if ((obj = cache->GetWrapper())) {
 | |
|       // GetWrapper always unmarks gray.
 | |
|       JS::AssertObjectIsNotGray(obj);
 | |
|       return obj;
 | |
|     }
 | |
| 
 | |
|     // WrapObject never returns a gray thing.
 | |
|     obj = parent->WrapObject(cx, nullptr);
 | |
|     JS::AssertObjectIsNotGray(obj);
 | |
| 
 | |
|     return obj;
 | |
|   }
 | |
| };
 | |
| 
 | |
| // Wrapping of our native parent, for cases when it's not a WebIDL object.  In
 | |
| // this case it must be nsISupports.
 | |
| template <typename T>
 | |
| struct WrapNativeHelper<T, false> {
 | |
|   static inline JSObject* Wrap(JSContext* cx, T* parent,
 | |
|                                nsWrapperCache* cache) {
 | |
|     JSObject* obj;
 | |
|     if (cache && (obj = cache->GetWrapper())) {
 | |
| #ifdef DEBUG
 | |
|       JS::Rooted<JSObject*> rootedObj(cx, obj);
 | |
|       NS_ASSERTION(WrapNativeISupports(cx, parent, cache) == rootedObj,
 | |
|                    "Unexpected object in nsWrapperCache");
 | |
|       obj = rootedObj;
 | |
| #endif
 | |
|       JS::AssertObjectIsNotGray(obj);
 | |
|       return obj;
 | |
|     }
 | |
| 
 | |
|     obj = WrapNativeISupports(cx, parent, cache);
 | |
|     JS::AssertObjectIsNotGray(obj);
 | |
|     return obj;
 | |
|   }
 | |
| };
 | |
| 
 | |
| // Finding the associated global for an object.
 | |
| template <typename T>
 | |
| static inline JSObject* FindAssociatedGlobal(
 | |
|     JSContext* cx, T* p, nsWrapperCache* cache,
 | |
|     mozilla::dom::ReflectionScope scope =
 | |
|         mozilla::dom::ReflectionScope::Content) {
 | |
|   if (!p) {
 | |
|     return JS::CurrentGlobalOrNull(cx);
 | |
|   }
 | |
| 
 | |
|   JSObject* obj = WrapNativeHelper<T>::Wrap(cx, p, cache);
 | |
|   if (!obj) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   JS::AssertObjectIsNotGray(obj);
 | |
| 
 | |
|   // The object is never a CCW but it may not be in the current compartment of
 | |
|   // the JSContext.
 | |
|   obj = JS::GetNonCCWObjectGlobal(obj);
 | |
| 
 | |
|   switch (scope) {
 | |
|     case mozilla::dom::ReflectionScope::NAC: {
 | |
|       return xpc::NACScope(obj);
 | |
|     }
 | |
| 
 | |
|     case mozilla::dom::ReflectionScope::UAWidget: {
 | |
|       // If scope is set to UAWidgetScope, it means that the canonical reflector
 | |
|       // for this native object should live in the UA widget scope.
 | |
|       if (xpc::IsInUAWidgetScope(obj)) {
 | |
|         return obj;
 | |
|       }
 | |
|       JS::Rooted<JSObject*> rootedObj(cx, obj);
 | |
|       JSObject* uaWidgetScope = xpc::GetUAWidgetScope(cx, rootedObj);
 | |
|       MOZ_ASSERT_IF(uaWidgetScope, JS_IsGlobalObject(uaWidgetScope));
 | |
|       JS::AssertObjectIsNotGray(uaWidgetScope);
 | |
|       return uaWidgetScope;
 | |
|     }
 | |
| 
 | |
|     case ReflectionScope::Content:
 | |
|       return obj;
 | |
|   }
 | |
| 
 | |
|   MOZ_CRASH("Unknown ReflectionScope variant");
 | |
| 
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| // Finding of the associated global for an object, when we don't want to
 | |
| // explicitly pass in things like the nsWrapperCache for it.
 | |
| template <typename T>
 | |
| static inline JSObject* FindAssociatedGlobal(JSContext* cx, const T& p) {
 | |
|   return FindAssociatedGlobal(cx, GetParentPointer(p), GetWrapperCache(p),
 | |
|                               GetReflectionScope(p));
 | |
| }
 | |
| 
 | |
| // Specialization for the case of nsIGlobalObject, since in that case
 | |
| // we can just get the JSObject* directly.
 | |
| template <>
 | |
| inline JSObject* FindAssociatedGlobal(JSContext* cx,
 | |
|                                       nsIGlobalObject* const& p) {
 | |
|   if (!p) {
 | |
|     return JS::CurrentGlobalOrNull(cx);
 | |
|   }
 | |
| 
 | |
|   JSObject* global = p->GetGlobalJSObject();
 | |
|   if (!global) {
 | |
|     // nsIGlobalObject doesn't have a JS object anymore,
 | |
|     // fallback to the current global.
 | |
|     return JS::CurrentGlobalOrNull(cx);
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(JS_IsGlobalObject(global));
 | |
|   JS::AssertObjectIsNotGray(global);
 | |
|   return global;
 | |
| }
 | |
| 
 | |
| template <typename T,
 | |
|           bool hasAssociatedGlobal = NativeHasMember<T>::GetParentObject>
 | |
| struct FindAssociatedGlobalForNative {
 | |
|   static JSObject* Get(JSContext* cx, JS::Handle<JSObject*> obj) {
 | |
|     MOZ_ASSERT(js::IsObjectInContextCompartment(obj, cx));
 | |
|     T* native = UnwrapDOMObject<T>(obj);
 | |
|     return FindAssociatedGlobal(cx, native->GetParentObject());
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <typename T>
 | |
| struct FindAssociatedGlobalForNative<T, false> {
 | |
|   static JSObject* Get(JSContext* cx, JS::Handle<JSObject*> obj) {
 | |
|     MOZ_CRASH();
 | |
|     return nullptr;
 | |
|   }
 | |
| };
 | |
| 
 | |
| // Helper for calling GetOrCreateDOMReflector with smart pointers
 | |
| // (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
 | |
| template <class T, bool isSmartPtr = IsSmartPtr<T>::value>
 | |
| struct GetOrCreateDOMReflectorHelper {
 | |
|   static inline bool GetOrCreate(JSContext* cx, const T& value,
 | |
|                                  JS::Handle<JSObject*> givenProto,
 | |
|                                  JS::MutableHandle<JS::Value> rval) {
 | |
|     return GetOrCreateDOMReflector(cx, value.get(), rval, givenProto);
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| struct GetOrCreateDOMReflectorHelper<T, false> {
 | |
|   static inline bool GetOrCreate(JSContext* cx, T& value,
 | |
|                                  JS::Handle<JSObject*> givenProto,
 | |
|                                  JS::MutableHandle<JS::Value> rval) {
 | |
|     static_assert(IsRefcounted<T>::value, "Don't pass owned classes in here.");
 | |
|     return GetOrCreateDOMReflector(cx, &value, rval, givenProto);
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| inline bool GetOrCreateDOMReflector(
 | |
|     JSContext* cx, T& value, JS::MutableHandle<JS::Value> rval,
 | |
|     JS::Handle<JSObject*> givenProto = nullptr) {
 | |
|   return GetOrCreateDOMReflectorHelper<T>::GetOrCreate(cx, value, givenProto,
 | |
|                                                        rval);
 | |
| }
 | |
| 
 | |
| // Helper for calling GetOrCreateDOMReflectorNoWrap with smart pointers
 | |
| // (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
 | |
| template <class T, bool isSmartPtr = IsSmartPtr<T>::value>
 | |
| struct GetOrCreateDOMReflectorNoWrapHelper {
 | |
|   static inline bool GetOrCreate(JSContext* cx, const T& value,
 | |
|                                  JS::MutableHandle<JS::Value> rval) {
 | |
|     return GetOrCreateDOMReflectorNoWrap(cx, value.get(), rval);
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| struct GetOrCreateDOMReflectorNoWrapHelper<T, false> {
 | |
|   static inline bool GetOrCreate(JSContext* cx, T& value,
 | |
|                                  JS::MutableHandle<JS::Value> rval) {
 | |
|     return GetOrCreateDOMReflectorNoWrap(cx, &value, rval);
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| inline bool GetOrCreateDOMReflectorNoWrap(JSContext* cx, T& value,
 | |
|                                           JS::MutableHandle<JS::Value> rval) {
 | |
|   return GetOrCreateDOMReflectorNoWrapHelper<T>::GetOrCreate(cx, value, rval);
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| inline JSObject* GetCallbackFromCallbackObject(JSContext* aCx, T* aObj) {
 | |
|   return aObj->Callback(aCx);
 | |
| }
 | |
| 
 | |
| // Helper for getting the callback JSObject* of a smart ptr around a
 | |
| // CallbackObject or a reference to a CallbackObject or something like
 | |
| // that.
 | |
| template <class T, bool isSmartPtr = IsSmartPtr<T>::value>
 | |
| struct GetCallbackFromCallbackObjectHelper {
 | |
|   static inline JSObject* Get(JSContext* aCx, const T& aObj) {
 | |
|     return GetCallbackFromCallbackObject(aCx, aObj.get());
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| struct GetCallbackFromCallbackObjectHelper<T, false> {
 | |
|   static inline JSObject* Get(JSContext* aCx, T& aObj) {
 | |
|     return GetCallbackFromCallbackObject(aCx, &aObj);
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| inline JSObject* GetCallbackFromCallbackObject(JSContext* aCx, T& aObj) {
 | |
|   return GetCallbackFromCallbackObjectHelper<T>::Get(aCx, aObj);
 | |
| }
 | |
| 
 | |
| static inline bool AtomizeAndPinJSString(JSContext* cx, jsid& id,
 | |
|                                          const char* chars) {
 | |
|   if (JSString* str = ::JS_AtomizeAndPinString(cx, chars)) {
 | |
|     id = INTERNED_STRING_TO_JSID(cx, str);
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool InitIds(JSContext* cx, const NativeProperties* properties);
 | |
| 
 | |
| void GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
 | |
|                       nsWrapperCache* aCache, JS::Handle<JS::Value> aIID,
 | |
|                       JS::MutableHandle<JS::Value> aRetval,
 | |
|                       ErrorResult& aError);
 | |
| 
 | |
| template <class T>
 | |
| void GetInterface(JSContext* aCx, T* aThis, JS::Handle<JS::Value> aIID,
 | |
|                   JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError) {
 | |
|   GetInterfaceImpl(aCx, aThis, aThis, aIID, aRetval, aError);
 | |
| }
 | |
| 
 | |
| bool ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
 | |
| 
 | |
| bool ThrowConstructorWithoutNew(JSContext* cx, const char* name);
 | |
| 
 | |
| bool GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
 | |
|                             JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
 | |
|                             bool* found, JS::MutableHandle<JS::Value> vp);
 | |
| 
 | |
| //
 | |
| bool HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
 | |
|                             JS::Handle<jsid> id, bool* has);
 | |
| 
 | |
| // Append the property names in "names" to "props". If
 | |
| // shadowPrototypeProperties is false then skip properties that are also
 | |
| // present on the proto chain of proxy.  If shadowPrototypeProperties is true,
 | |
| // then the "proxy" argument is ignored.
 | |
| bool AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
 | |
|                             nsTArray<nsString>& names,
 | |
|                             bool shadowPrototypeProperties,
 | |
|                             JS::MutableHandleVector<jsid> props);
 | |
| 
 | |
| enum StringificationBehavior { eStringify, eEmpty, eNull };
 | |
| 
 | |
| static inline JSString* ConvertJSValueToJSString(JSContext* cx,
 | |
|                                                  JS::Handle<JS::Value> v) {
 | |
|   if (MOZ_LIKELY(v.isString())) {
 | |
|     return v.toString();
 | |
|   }
 | |
|   return JS::ToString(cx, v);
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| static inline bool ConvertJSValueToString(
 | |
|     JSContext* cx, JS::Handle<JS::Value> v,
 | |
|     StringificationBehavior nullBehavior,
 | |
|     StringificationBehavior undefinedBehavior, T& result) {
 | |
|   JSString* s;
 | |
|   if (v.isString()) {
 | |
|     s = v.toString();
 | |
|   } else {
 | |
|     StringificationBehavior behavior;
 | |
|     if (v.isNull()) {
 | |
|       behavior = nullBehavior;
 | |
|     } else if (v.isUndefined()) {
 | |
|       behavior = undefinedBehavior;
 | |
|     } else {
 | |
|       behavior = eStringify;
 | |
|     }
 | |
| 
 | |
|     if (behavior != eStringify) {
 | |
|       if (behavior == eEmpty) {
 | |
|         result.Truncate();
 | |
|       } else {
 | |
|         result.SetIsVoid(true);
 | |
|       }
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     s = JS::ToString(cx, v);
 | |
|     if (!s) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return AssignJSString(cx, result, s);
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| static inline bool ConvertJSValueToString(JSContext* cx,
 | |
|                                           JS::Handle<JS::Value> v, T& result) {
 | |
|   return ConvertJSValueToString(cx, v, eStringify, eStringify, result);
 | |
| }
 | |
| 
 | |
| MOZ_MUST_USE bool NormalizeUSVString(nsAString& aString);
 | |
| 
 | |
| MOZ_MUST_USE bool NormalizeUSVString(
 | |
|     binding_detail::FakeString<char16_t>& aString);
 | |
| 
 | |
| template <typename T>
 | |
| static inline bool ConvertJSValueToUSVString(JSContext* cx,
 | |
|                                              JS::Handle<JS::Value> v,
 | |
|                                              T& result) {
 | |
|   if (!ConvertJSValueToString(cx, v, eStringify, eStringify, result)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (!NormalizeUSVString(result)) {
 | |
|     JS_ReportOutOfMemory(cx);
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| inline bool ConvertIdToString(JSContext* cx, JS::HandleId id, T& result,
 | |
|                               bool& isSymbol) {
 | |
|   if (MOZ_LIKELY(JSID_IS_STRING(id))) {
 | |
|     if (!AssignJSString(cx, result, JSID_TO_STRING(id))) {
 | |
|       return false;
 | |
|     }
 | |
|   } else if (JSID_IS_SYMBOL(id)) {
 | |
|     isSymbol = true;
 | |
|     return true;
 | |
|   } else {
 | |
|     JS::RootedValue nameVal(cx, js::IdToValue(id));
 | |
|     if (!ConvertJSValueToString(cx, nameVal, eStringify, eStringify, result)) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
|   isSymbol = false;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
 | |
|                                 bool nullable, nsACString& result);
 | |
| 
 | |
| inline bool ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
 | |
|                                        nsACString& result) {
 | |
|   return ConvertJSValueToByteString(cx, v, false, result);
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq);
 | |
| template <typename T>
 | |
| void DoTraceSequence(JSTracer* trc, nsTArray<T>& seq);
 | |
| 
 | |
| // Class used to trace sequences, with specializations for various
 | |
| // sequence types.
 | |
| template <typename T,
 | |
|           bool isDictionary = std::is_base_of<DictionaryBase, T>::value,
 | |
|           bool isTypedArray = std::is_base_of<AllTypedArraysBase, T>::value,
 | |
|           bool isOwningUnion = std::is_base_of<AllOwningUnionBase, T>::value>
 | |
| class SequenceTracer {
 | |
|   explicit SequenceTracer() = delete;  // Should never be instantiated
 | |
| };
 | |
| 
 | |
| // sequence<object> or sequence<object?>
 | |
| template <>
 | |
| class SequenceTracer<JSObject*, false, false, false> {
 | |
|   explicit SequenceTracer() = delete;  // Should never be instantiated
 | |
| 
 | |
|  public:
 | |
|   static void TraceSequence(JSTracer* trc, JSObject** objp, JSObject** end) {
 | |
|     for (; objp != end; ++objp) {
 | |
|       JS::UnsafeTraceRoot(trc, objp, "sequence<object>");
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| // sequence<any>
 | |
| template <>
 | |
| class SequenceTracer<JS::Value, false, false, false> {
 | |
|   explicit SequenceTracer() = delete;  // Should never be instantiated
 | |
| 
 | |
|  public:
 | |
|   static void TraceSequence(JSTracer* trc, JS::Value* valp, JS::Value* end) {
 | |
|     for (; valp != end; ++valp) {
 | |
|       JS::UnsafeTraceRoot(trc, valp, "sequence<any>");
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| // sequence<sequence<T>>
 | |
| template <typename T>
 | |
| class SequenceTracer<Sequence<T>, false, false, false> {
 | |
|   explicit SequenceTracer() = delete;  // Should never be instantiated
 | |
| 
 | |
|  public:
 | |
|   static void TraceSequence(JSTracer* trc, Sequence<T>* seqp,
 | |
|                             Sequence<T>* end) {
 | |
|     for (; seqp != end; ++seqp) {
 | |
|       DoTraceSequence(trc, *seqp);
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| // sequence<sequence<T>> as return value
 | |
| template <typename T>
 | |
| class SequenceTracer<nsTArray<T>, false, false, false> {
 | |
|   explicit SequenceTracer() = delete;  // Should never be instantiated
 | |
| 
 | |
|  public:
 | |
|   static void TraceSequence(JSTracer* trc, nsTArray<T>* seqp,
 | |
|                             nsTArray<T>* end) {
 | |
|     for (; seqp != end; ++seqp) {
 | |
|       DoTraceSequence(trc, *seqp);
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| // sequence<someDictionary>
 | |
| template <typename T>
 | |
| class SequenceTracer<T, true, false, false> {
 | |
|   explicit SequenceTracer() = delete;  // Should never be instantiated
 | |
| 
 | |
|  public:
 | |
|   static void TraceSequence(JSTracer* trc, T* dictp, T* end) {
 | |
|     for (; dictp != end; ++dictp) {
 | |
|       dictp->TraceDictionary(trc);
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| // sequence<SomeTypedArray>
 | |
| template <typename T>
 | |
| class SequenceTracer<T, false, true, false> {
 | |
|   explicit SequenceTracer() = delete;  // Should never be instantiated
 | |
| 
 | |
|  public:
 | |
|   static void TraceSequence(JSTracer* trc, T* arrayp, T* end) {
 | |
|     for (; arrayp != end; ++arrayp) {
 | |
|       arrayp->TraceSelf(trc);
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| // sequence<SomeOwningUnion>
 | |
| template <typename T>
 | |
| class SequenceTracer<T, false, false, true> {
 | |
|   explicit SequenceTracer() = delete;  // Should never be instantiated
 | |
| 
 | |
|  public:
 | |
|   static void TraceSequence(JSTracer* trc, T* arrayp, T* end) {
 | |
|     for (; arrayp != end; ++arrayp) {
 | |
|       arrayp->TraceUnion(trc);
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| // sequence<T?> with T? being a Nullable<T>
 | |
| template <typename T>
 | |
| class SequenceTracer<Nullable<T>, false, false, false> {
 | |
|   explicit SequenceTracer() = delete;  // Should never be instantiated
 | |
| 
 | |
|  public:
 | |
|   static void TraceSequence(JSTracer* trc, Nullable<T>* seqp,
 | |
|                             Nullable<T>* end) {
 | |
|     for (; seqp != end; ++seqp) {
 | |
|       if (!seqp->IsNull()) {
 | |
|         // Pretend like we actually have a length-one sequence here so
 | |
|         // we can do template instantiation correctly for T.
 | |
|         T& val = seqp->Value();
 | |
|         T* ptr = &val;
 | |
|         SequenceTracer<T>::TraceSequence(trc, ptr, ptr + 1);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <typename K, typename V>
 | |
| void TraceRecord(JSTracer* trc, Record<K, V>& record) {
 | |
|   for (auto& entry : record.Entries()) {
 | |
|     // Act like it's a one-element sequence to leverage all that infrastructure.
 | |
|     SequenceTracer<V>::TraceSequence(trc, &entry.mValue, &entry.mValue + 1);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // sequence<record>
 | |
| template <typename K, typename V>
 | |
| class SequenceTracer<Record<K, V>, false, false, false> {
 | |
|   explicit SequenceTracer() = delete;  // Should never be instantiated
 | |
| 
 | |
|  public:
 | |
|   static void TraceSequence(JSTracer* trc, Record<K, V>* seqp,
 | |
|                             Record<K, V>* end) {
 | |
|     for (; seqp != end; ++seqp) {
 | |
|       TraceRecord(trc, *seqp);
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <typename T>
 | |
| void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq) {
 | |
|   SequenceTracer<T>::TraceSequence(trc, seq.Elements(),
 | |
|                                    seq.Elements() + seq.Length());
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| void DoTraceSequence(JSTracer* trc, nsTArray<T>& seq) {
 | |
|   SequenceTracer<T>::TraceSequence(trc, seq.Elements(),
 | |
|                                    seq.Elements() + seq.Length());
 | |
| }
 | |
| 
 | |
| // Rooter class for sequences; this is what we mostly use in the codegen
 | |
| template <typename T>
 | |
| class MOZ_RAII SequenceRooter final : private JS::CustomAutoRooter {
 | |
|  public:
 | |
|   template <typename CX>
 | |
|   SequenceRooter(const CX& cx,
 | |
|                  FallibleTArray<T>* aSequence MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
 | |
|       : JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
 | |
|         mFallibleArray(aSequence),
 | |
|         mSequenceType(eFallibleArray) {}
 | |
| 
 | |
|   template <typename CX>
 | |
|   SequenceRooter(const CX& cx,
 | |
|                  nsTArray<T>* aSequence MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
 | |
|       : JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
 | |
|         mInfallibleArray(aSequence),
 | |
|         mSequenceType(eInfallibleArray) {}
 | |
| 
 | |
|   template <typename CX>
 | |
|   SequenceRooter(const CX& cx, Nullable<nsTArray<T>>* aSequence
 | |
|                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
 | |
|       : JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
 | |
|         mNullableArray(aSequence),
 | |
|         mSequenceType(eNullableArray) {}
 | |
| 
 | |
|  private:
 | |
|   enum SequenceType { eInfallibleArray, eFallibleArray, eNullableArray };
 | |
| 
 | |
|   virtual void trace(JSTracer* trc) override {
 | |
|     if (mSequenceType == eFallibleArray) {
 | |
|       DoTraceSequence(trc, *mFallibleArray);
 | |
|     } else if (mSequenceType == eInfallibleArray) {
 | |
|       DoTraceSequence(trc, *mInfallibleArray);
 | |
|     } else {
 | |
|       MOZ_ASSERT(mSequenceType == eNullableArray);
 | |
|       if (!mNullableArray->IsNull()) {
 | |
|         DoTraceSequence(trc, mNullableArray->Value());
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   union {
 | |
|     nsTArray<T>* mInfallibleArray;
 | |
|     FallibleTArray<T>* mFallibleArray;
 | |
|     Nullable<nsTArray<T>>* mNullableArray;
 | |
|   };
 | |
| 
 | |
|   SequenceType mSequenceType;
 | |
| };
 | |
| 
 | |
| // Rooter class for Record; this is what we mostly use in the codegen.
 | |
| template <typename K, typename V>
 | |
| class MOZ_RAII RecordRooter final : private JS::CustomAutoRooter {
 | |
|  public:
 | |
|   template <typename CX>
 | |
|   RecordRooter(const CX& cx,
 | |
|                Record<K, V>* aRecord MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
 | |
|       : JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
 | |
|         mRecord(aRecord),
 | |
|         mRecordType(eRecord) {}
 | |
| 
 | |
|   template <typename CX>
 | |
|   RecordRooter(const CX& cx,
 | |
|                Nullable<Record<K, V>>* aRecord MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
 | |
|       : JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
 | |
|         mNullableRecord(aRecord),
 | |
|         mRecordType(eNullableRecord) {}
 | |
| 
 | |
|  private:
 | |
|   enum RecordType { eRecord, eNullableRecord };
 | |
| 
 | |
|   virtual void trace(JSTracer* trc) override {
 | |
|     if (mRecordType == eRecord) {
 | |
|       TraceRecord(trc, *mRecord);
 | |
|     } else {
 | |
|       MOZ_ASSERT(mRecordType == eNullableRecord);
 | |
|       if (!mNullableRecord->IsNull()) {
 | |
|         TraceRecord(trc, mNullableRecord->Value());
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   union {
 | |
|     Record<K, V>* mRecord;
 | |
|     Nullable<Record<K, V>>* mNullableRecord;
 | |
|   };
 | |
| 
 | |
|   RecordType mRecordType;
 | |
| };
 | |
| 
 | |
| template <typename T>
 | |
| class MOZ_RAII RootedUnion : public T, private JS::CustomAutoRooter {
 | |
|  public:
 | |
|   template <typename CX>
 | |
|   explicit RootedUnion(const CX& cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
 | |
|       : T(),
 | |
|         JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) {}
 | |
| 
 | |
|   virtual void trace(JSTracer* trc) override { this->TraceUnion(trc); }
 | |
| };
 | |
| 
 | |
| template <typename T>
 | |
| class MOZ_STACK_CLASS NullableRootedUnion : public Nullable<T>,
 | |
|                                             private JS::CustomAutoRooter {
 | |
|  public:
 | |
|   template <typename CX>
 | |
|   explicit NullableRootedUnion(const CX& cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
 | |
|       : Nullable<T>(),
 | |
|         JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) {}
 | |
| 
 | |
|   virtual void trace(JSTracer* trc) override {
 | |
|     if (!this->IsNull()) {
 | |
|       this->Value().TraceUnion(trc);
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| inline bool AddStringToIDVector(JSContext* cx,
 | |
|                                 JS::MutableHandleVector<jsid> vector,
 | |
|                                 const char* name) {
 | |
|   return vector.growBy(1) &&
 | |
|          AtomizeAndPinJSString(cx, *(vector[vector.length() - 1]).address(),
 | |
|                                name);
 | |
| }
 | |
| 
 | |
| // We use one constructor JSNative to represent all DOM interface objects (so
 | |
| // we can easily detect when we need to wrap them in an Xray wrapper). We store
 | |
| // the real JSNative in the mNative member of a JSNativeHolder in the
 | |
| // CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction object for a
 | |
| // specific interface object. We also store the NativeProperties in the
 | |
| // JSNativeHolder.
 | |
| // Note that some interface objects are not yet a JSFunction but a normal
 | |
| // JSObject with a DOMJSClass, those do not use these slots.
 | |
| 
 | |
| enum { CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0 };
 | |
| 
 | |
| bool Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
 | |
| 
 | |
| // Implementation of the bits that XrayWrapper needs
 | |
| 
 | |
| /**
 | |
|  * This resolves operations, attributes and constants of the interfaces for obj.
 | |
|  *
 | |
|  * wrapper is the Xray JS object.
 | |
|  * obj is the target object of the Xray, a binding's instance object or a
 | |
|  *     interface or interface prototype object.
 | |
|  */
 | |
| bool XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
 | |
|                             JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
 | |
|                             JS::MutableHandle<JS::PropertyDescriptor> desc,
 | |
|                             bool& cacheOnHolder);
 | |
| 
 | |
| /**
 | |
|  * Define a property on obj through an Xray wrapper.
 | |
|  *
 | |
|  * wrapper is the Xray JS object.
 | |
|  * obj is the target object of the Xray, a binding's instance object or a
 | |
|  *     interface or interface prototype object.
 | |
|  * id and desc are the parameters for the property to be defined.
 | |
|  * result is the out-parameter indicating success (read it only if
 | |
|  *     this returns true and also sets *defined to true).
 | |
|  * defined will be set to true if a property was set as a result of this call.
 | |
|  */
 | |
| bool XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
 | |
|                         JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
 | |
|                         JS::Handle<JS::PropertyDescriptor> desc,
 | |
|                         JS::ObjectOpResult& result, bool* defined);
 | |
| 
 | |
| /**
 | |
|  * Add to props the property keys of all indexed or named properties of obj and
 | |
|  * operations, attributes and constants of the interfaces for obj.
 | |
|  *
 | |
|  * wrapper is the Xray JS object.
 | |
|  * obj is the target object of the Xray, a binding's instance object or a
 | |
|  *     interface or interface prototype object.
 | |
|  * flags are JSITER_* flags.
 | |
|  */
 | |
| bool XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
 | |
|                          JS::Handle<JSObject*> obj, unsigned flags,
 | |
|                          JS::MutableHandleVector<jsid> props);
 | |
| 
 | |
| /**
 | |
|  * Returns the prototype to use for an Xray for a DOM object, wrapped in cx's
 | |
|  * compartment. This always returns the prototype that would be used for a DOM
 | |
|  * object if we ignore any changes that might have been done to the prototype
 | |
|  * chain by JS, the XBL code or plugins.
 | |
|  *
 | |
|  * cx should be in the Xray's compartment.
 | |
|  * obj is the target object of the Xray, a binding's instance object or an
 | |
|  *     interface or interface prototype object.
 | |
|  */
 | |
| inline bool XrayGetNativeProto(JSContext* cx, JS::Handle<JSObject*> obj,
 | |
|                                JS::MutableHandle<JSObject*> protop) {
 | |
|   JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(obj));
 | |
|   {
 | |
|     JSAutoRealm ar(cx, global);
 | |
|     const DOMJSClass* domClass = GetDOMClass(obj);
 | |
|     if (domClass) {
 | |
|       ProtoHandleGetter protoGetter = domClass->mGetProto;
 | |
|       if (protoGetter) {
 | |
|         protop.set(protoGetter(cx));
 | |
|       } else {
 | |
|         protop.set(JS::GetRealmObjectPrototype(cx));
 | |
|       }
 | |
|     } else if (JS_ObjectIsFunction(obj)) {
 | |
|       MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
 | |
|       protop.set(JS::GetRealmFunctionPrototype(cx));
 | |
|     } else {
 | |
|       const JSClass* clasp = js::GetObjectClass(obj);
 | |
|       MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
 | |
|       ProtoGetter protoGetter =
 | |
|           DOMIfaceAndProtoJSClass::FromJSClass(clasp)->mGetParentProto;
 | |
|       protop.set(protoGetter(cx));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return JS_WrapObject(cx, protop);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Get the Xray expando class to use for the given DOM object.
 | |
|  */
 | |
| const JSClass* XrayGetExpandoClass(JSContext* cx, JS::Handle<JSObject*> obj);
 | |
| 
 | |
| /**
 | |
|  * Delete a named property, if any.  Return value is false if exception thrown,
 | |
|  * true otherwise.  The caller should not do any more work after calling this
 | |
|  * function, because it has no way whether a deletion was performed and hence
 | |
|  * opresult already has state set on it.  If callers ever need to change that,
 | |
|  * add a "bool* found" argument and change the generated DeleteNamedProperty to
 | |
|  * use it instead of a local variable.
 | |
|  */
 | |
| bool XrayDeleteNamedProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
 | |
|                              JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
 | |
|                              JS::ObjectOpResult& opresult);
 | |
| 
 | |
| /**
 | |
|  * Get the object which should be used to cache the return value of a property
 | |
|  * getter in the case of a [Cached] or [StoreInSlot] property.  `obj` is the
 | |
|  * `this` value for our property getter that we're working with.
 | |
|  *
 | |
|  * This function can return null on failure to allocate the object, throwing on
 | |
|  * the JSContext in the process.
 | |
|  *
 | |
|  * The isXray outparam will be set to true if obj is an Xray and false
 | |
|  * otherwise.
 | |
|  *
 | |
|  * Note that the Slow version should only be called from
 | |
|  * GetCachedSlotStorageObject.
 | |
|  */
 | |
| JSObject* GetCachedSlotStorageObjectSlow(JSContext* cx,
 | |
|                                          JS::Handle<JSObject*> obj,
 | |
|                                          bool* isXray);
 | |
| 
 | |
| inline JSObject* GetCachedSlotStorageObject(JSContext* cx,
 | |
|                                             JS::Handle<JSObject*> obj,
 | |
|                                             bool* isXray) {
 | |
|   if (IsDOMObject(obj)) {
 | |
|     *isXray = false;
 | |
|     return obj;
 | |
|   }
 | |
| 
 | |
|   return GetCachedSlotStorageObjectSlow(cx, obj, isXray);
 | |
| }
 | |
| 
 | |
| extern NativePropertyHooks sEmptyNativePropertyHooks;
 | |
| 
 | |
| extern const JSClassOps sBoringInterfaceObjectClassClassOps;
 | |
| 
 | |
| extern const js::ObjectOps sInterfaceObjectClassObjectOps;
 | |
| 
 | |
| inline bool UseDOMXray(JSObject* obj) {
 | |
|   const JSClass* clasp = js::GetObjectClass(obj);
 | |
|   return IsDOMClass(clasp) || JS_IsNativeFunction(obj, Constructor) ||
 | |
|          IsDOMIfaceAndProtoClass(clasp);
 | |
| }
 | |
| 
 | |
| inline bool IsDOMConstructor(JSObject* obj) {
 | |
|   if (JS_IsNativeFunction(obj, dom::Constructor)) {
 | |
|     // NamedConstructor, like Image
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   const JSClass* clasp = js::GetObjectClass(obj);
 | |
|   // Check for a DOM interface object.
 | |
|   return dom::IsDOMIfaceAndProtoClass(clasp) &&
 | |
|          dom::DOMIfaceAndProtoJSClass::FromJSClass(clasp)->mType ==
 | |
|              dom::eInterface;
 | |
| }
 | |
| 
 | |
| #ifdef DEBUG
 | |
| inline bool HasConstructor(JSObject* obj) {
 | |
|   return JS_IsNativeFunction(obj, Constructor) ||
 | |
|          js::GetObjectClass(obj)->getConstruct();
 | |
| }
 | |
| #endif
 | |
| 
 | |
| // Helpers for creating a const version of a type.
 | |
| template <typename T>
 | |
| const T& Constify(T& arg) {
 | |
|   return arg;
 | |
| }
 | |
| 
 | |
| // Helper for turning (Owning)NonNull<T> into T&
 | |
| template <typename T>
 | |
| T& NonNullHelper(T& aArg) {
 | |
|   return aArg;
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| T& NonNullHelper(NonNull<T>& aArg) {
 | |
|   return aArg;
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| const T& NonNullHelper(const NonNull<T>& aArg) {
 | |
|   return aArg;
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| T& NonNullHelper(OwningNonNull<T>& aArg) {
 | |
|   return aArg;
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| const T& NonNullHelper(const OwningNonNull<T>& aArg) {
 | |
|   return aArg;
 | |
| }
 | |
| 
 | |
| template <typename CharT>
 | |
| inline void NonNullHelper(NonNull<binding_detail::FakeString<CharT>>& aArg) {
 | |
|   // This overload is here to make sure that we never end up applying
 | |
|   // NonNullHelper to a NonNull<binding_detail::FakeString>. If we
 | |
|   // try to, it should fail to compile, since presumably the caller will try to
 | |
|   // use our nonexistent return value.
 | |
| }
 | |
| 
 | |
| template <typename CharT>
 | |
| inline void NonNullHelper(
 | |
|     const NonNull<binding_detail::FakeString<CharT>>& aArg) {
 | |
|   // This overload is here to make sure that we never end up applying
 | |
|   // NonNullHelper to a NonNull<binding_detail::FakeString>. If we
 | |
|   // try to, it should fail to compile, since presumably the caller will try to
 | |
|   // use our nonexistent return value.
 | |
| }
 | |
| 
 | |
| template <typename CharT>
 | |
| inline void NonNullHelper(binding_detail::FakeString<CharT>& aArg) {
 | |
|   // This overload is here to make sure that we never end up applying
 | |
|   // NonNullHelper to a FakeString before we've constified it.  If we
 | |
|   // try to, it should fail to compile, since presumably the caller will try to
 | |
|   // use our nonexistent return value.
 | |
| }
 | |
| 
 | |
| template <typename CharT>
 | |
| MOZ_ALWAYS_INLINE const nsTSubstring<CharT>& NonNullHelper(
 | |
|     const binding_detail::FakeString<CharT>& aArg) {
 | |
|   return aArg;
 | |
| }
 | |
| 
 | |
| // Given a DOM reflector aObj, give its underlying DOM object a reflector in
 | |
| // whatever global that underlying DOM object now thinks it should be in.  If
 | |
| // this is in a different compartment from aObj, aObj will become a
 | |
| // cross-compatment wrapper for the new object.  Otherwise, aObj will become the
 | |
| // new object (via a brain transplant).  If the new global is the same as the
 | |
| // old global, we just keep using the same object.
 | |
| //
 | |
| // On entry to this method, aCx and aObj must be same-compartment.
 | |
| void UpdateReflectorGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
 | |
|                            ErrorResult& aError);
 | |
| 
 | |
| /**
 | |
|  * Used to implement the Symbol.hasInstance property of an interface object.
 | |
|  */
 | |
| bool InterfaceHasInstance(JSContext* cx, unsigned argc, JS::Value* vp);
 | |
| 
 | |
| bool InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
 | |
|                           JS::Handle<JSObject*> instance, bool* bp);
 | |
| 
 | |
| // Used to implement the cross-context <Interface>.isInstance static method.
 | |
| bool InterfaceIsInstance(JSContext* cx, unsigned argc, JS::Value* vp);
 | |
| 
 | |
| // Helper for lenient getters/setters to report to console.  If this
 | |
| // returns false, we couldn't even get a global.
 | |
| bool ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj);
 | |
| 
 | |
| // Given a JSObject* that represents the chrome side of a JS-implemented WebIDL
 | |
| // interface, get the nsIGlobalObject corresponding to the content side, if any.
 | |
| // A false return means an exception was thrown.
 | |
| bool GetContentGlobalForJSImplementedObject(JSContext* cx,
 | |
|                                             JS::Handle<JSObject*> obj,
 | |
|                                             nsIGlobalObject** global);
 | |
| 
 | |
| void ConstructJSImplementation(const char* aContractId,
 | |
|                                nsIGlobalObject* aGlobal,
 | |
|                                JS::MutableHandle<JSObject*> aObject,
 | |
|                                ErrorResult& aRv);
 | |
| 
 | |
| template <typename T>
 | |
| already_AddRefed<T> ConstructJSImplementation(const char* aContractId,
 | |
|                                               nsIGlobalObject* aGlobal,
 | |
|                                               ErrorResult& aRv) {
 | |
|   JS::RootingContext* cx = RootingCx();
 | |
|   JS::Rooted<JSObject*> jsImplObj(cx);
 | |
|   ConstructJSImplementation(aContractId, aGlobal, &jsImplObj, aRv);
 | |
|   if (aRv.Failed()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   MOZ_RELEASE_ASSERT(!js::IsWrapper(jsImplObj));
 | |
|   JS::Rooted<JSObject*> jsImplGlobal(cx, JS::GetNonCCWObjectGlobal(jsImplObj));
 | |
|   RefPtr<T> newObj = new T(jsImplObj, jsImplGlobal, aGlobal);
 | |
|   return newObj.forget();
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| already_AddRefed<T> ConstructJSImplementation(const char* aContractId,
 | |
|                                               const GlobalObject& aGlobal,
 | |
|                                               ErrorResult& aRv) {
 | |
|   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
 | |
|   if (!global) {
 | |
|     aRv.Throw(NS_ERROR_FAILURE);
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   return ConstructJSImplementation<T>(aContractId, global, aRv);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Convert an nsCString to jsval, returning true on success.
 | |
|  * These functions are intended for ByteString implementations.
 | |
|  * As such, the string is not UTF-8 encoded.  Any UTF8 strings passed to these
 | |
|  * methods will be mangled.
 | |
|  */
 | |
| bool NonVoidByteStringToJsval(JSContext* cx, const nsACString& str,
 | |
|                               JS::MutableHandle<JS::Value> rval);
 | |
| inline bool ByteStringToJsval(JSContext* cx, const nsACString& str,
 | |
|                               JS::MutableHandle<JS::Value> rval) {
 | |
|   if (str.IsVoid()) {
 | |
|     rval.setNull();
 | |
|     return true;
 | |
|   }
 | |
|   return NonVoidByteStringToJsval(cx, str, rval);
 | |
| }
 | |
| 
 | |
| // Convert an utf-8 encoded nsCString to jsval, returning true on success.
 | |
| //
 | |
| // TODO(bug 1606957): This could probably be better.
 | |
| inline bool NonVoidUTF8StringToJsval(JSContext* cx, const nsACString& str,
 | |
|                                      JS::MutableHandle<JS::Value> rval) {
 | |
|   JSString* jsStr =
 | |
|       JS_NewStringCopyUTF8N(cx, {str.BeginReading(), str.Length()});
 | |
|   if (!jsStr) {
 | |
|     return false;
 | |
|   }
 | |
|   rval.setString(jsStr);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| inline bool UTF8StringToJsval(JSContext* cx, const nsACString& str,
 | |
|                               JS::MutableHandle<JS::Value> rval) {
 | |
|   if (str.IsVoid()) {
 | |
|     rval.setNull();
 | |
|     return true;
 | |
|   }
 | |
|   return NonVoidUTF8StringToJsval(cx, str, rval);
 | |
| }
 | |
| 
 | |
| template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
 | |
| struct PreserveWrapperHelper {
 | |
|   static void PreserveWrapper(T* aObject) {
 | |
|     aObject->PreserveWrapper(aObject, NS_CYCLE_COLLECTION_PARTICIPANT(T));
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| struct PreserveWrapperHelper<T, true> {
 | |
|   static void PreserveWrapper(T* aObject) {
 | |
|     aObject->PreserveWrapper(reinterpret_cast<nsISupports*>(aObject));
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| void PreserveWrapper(T* aObject) {
 | |
|   PreserveWrapperHelper<T>::PreserveWrapper(aObject);
 | |
| }
 | |
| 
 | |
| template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
 | |
| struct CastingAssertions {
 | |
|   static bool ToSupportsIsCorrect(T*) { return true; }
 | |
|   static bool ToSupportsIsOnPrimaryInheritanceChain(T*, nsWrapperCache*) {
 | |
|     return true;
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| struct CastingAssertions<T, true> {
 | |
|   static bool ToSupportsIsCorrect(T* aObject) {
 | |
|     return ToSupports(aObject) == reinterpret_cast<nsISupports*>(aObject);
 | |
|   }
 | |
|   static bool ToSupportsIsOnPrimaryInheritanceChain(T* aObject,
 | |
|                                                     nsWrapperCache* aCache) {
 | |
|     return reinterpret_cast<void*>(aObject) != aCache;
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| bool ToSupportsIsCorrect(T* aObject) {
 | |
|   return CastingAssertions<T>::ToSupportsIsCorrect(aObject);
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| bool ToSupportsIsOnPrimaryInheritanceChain(T* aObject, nsWrapperCache* aCache) {
 | |
|   return CastingAssertions<T>::ToSupportsIsOnPrimaryInheritanceChain(aObject,
 | |
|                                                                      aCache);
 | |
| }
 | |
| 
 | |
| // Get the size of allocated memory to associate with a binding JSObject for a
 | |
| // native object. This is supplied to the JS engine to allow it to schedule GC
 | |
| // when necessary.
 | |
| //
 | |
| // This function supplies a default value and is overloaded for specific native
 | |
| // object types.
 | |
| inline size_t BindingJSObjectMallocBytes(void* aNativePtr) { return 0; }
 | |
| 
 | |
| // The BindingJSObjectCreator class is supposed to be used by a caller that
 | |
| // wants to create and initialise a binding JSObject. After initialisation has
 | |
| // been successfully completed it should call ForgetObject().
 | |
| // The BindingJSObjectCreator object will root the JSObject until ForgetObject()
 | |
| // is called on it. If the native object for the binding is refcounted it will
 | |
| // also hold a strong reference to it, that reference is transferred to the
 | |
| // JSObject (which holds the native in a slot) when ForgetObject() is called. If
 | |
| // the BindingJSObjectCreator object is destroyed and ForgetObject() was never
 | |
| // called on it then the JSObject's slot holding the native will be set to
 | |
| // undefined, and for a refcounted native the strong reference will be released.
 | |
| template <class T>
 | |
| class MOZ_STACK_CLASS BindingJSObjectCreator {
 | |
|  public:
 | |
|   explicit BindingJSObjectCreator(JSContext* aCx) : mReflector(aCx) {}
 | |
| 
 | |
|   ~BindingJSObjectCreator() {
 | |
|     if (mReflector) {
 | |
|       js::SetReservedSlot(mReflector, DOM_OBJECT_SLOT, JS::UndefinedValue());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void CreateProxyObject(JSContext* aCx, const JSClass* aClass,
 | |
|                          const DOMProxyHandler* aHandler,
 | |
|                          JS::Handle<JSObject*> aProto, bool aLazyProto,
 | |
|                          T* aNative, JS::Handle<JS::Value> aExpandoValue,
 | |
|                          JS::MutableHandle<JSObject*> aReflector) {
 | |
|     js::ProxyOptions options;
 | |
|     options.setClass(aClass);
 | |
|     options.setLazyProto(aLazyProto);
 | |
| 
 | |
|     aReflector.set(
 | |
|         js::NewProxyObject(aCx, aHandler, aExpandoValue, aProto, options));
 | |
|     if (aReflector) {
 | |
|       js::SetProxyReservedSlot(aReflector, DOM_OBJECT_SLOT,
 | |
|                                JS::PrivateValue(aNative));
 | |
|       mNative = aNative;
 | |
|       mReflector = aReflector;
 | |
| 
 | |
|       if (size_t mallocBytes = BindingJSObjectMallocBytes(aNative)) {
 | |
|         JS::AddAssociatedMemory(aReflector, mallocBytes,
 | |
|                                 JS::MemoryUse::DOMBinding);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void CreateObject(JSContext* aCx, const JSClass* aClass,
 | |
|                     JS::Handle<JSObject*> aProto, T* aNative,
 | |
|                     JS::MutableHandle<JSObject*> aReflector) {
 | |
|     aReflector.set(JS_NewObjectWithGivenProto(aCx, aClass, aProto));
 | |
|     if (aReflector) {
 | |
|       js::SetReservedSlot(aReflector, DOM_OBJECT_SLOT,
 | |
|                           JS::PrivateValue(aNative));
 | |
|       mNative = aNative;
 | |
|       mReflector = aReflector;
 | |
| 
 | |
|       if (size_t mallocBytes = BindingJSObjectMallocBytes(aNative)) {
 | |
|         JS::AddAssociatedMemory(aReflector, mallocBytes,
 | |
|                                 JS::MemoryUse::DOMBinding);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void InitializationSucceeded() {
 | |
|     T* pointer;
 | |
|     mNative.forget(&pointer);
 | |
| 
 | |
|     // Never collect binding objects while recording or replaying, to avoid
 | |
|     // non-deterministically releasing references during finalization.
 | |
|     recordreplay::HoldJSObject(mReflector);
 | |
| 
 | |
|     mReflector = nullptr;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   struct OwnedNative {
 | |
|     // Make sure the native objects inherit from NonRefcountedDOMObject so
 | |
|     // that we log their ctor and dtor.
 | |
|     static_assert(std::is_base_of<NonRefcountedDOMObject, T>::value,
 | |
|                   "Non-refcounted objects with DOM bindings should inherit "
 | |
|                   "from NonRefcountedDOMObject.");
 | |
| 
 | |
|     OwnedNative& operator=(T* aNative) {
 | |
|       mNative = aNative;
 | |
|       return *this;
 | |
|     }
 | |
| 
 | |
|     // This signature sucks, but it's the only one that will make a nsRefPtr
 | |
|     // just forget about its pointer without warning.
 | |
|     void forget(T** aResult) {
 | |
|       *aResult = mNative;
 | |
|       mNative = nullptr;
 | |
|     }
 | |
| 
 | |
|     // Keep track of the pointer for use in InitializationSucceeded().
 | |
|     // The caller (or, after initialization succeeds, the JS object) retains
 | |
|     // ownership of the object.
 | |
|     T* mNative;
 | |
|   };
 | |
| 
 | |
|   JS::Rooted<JSObject*> mReflector;
 | |
|   typename Conditional<IsRefcounted<T>::value, RefPtr<T>, OwnedNative>::Type
 | |
|       mNative;
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| struct DeferredFinalizerImpl {
 | |
|   typedef typename Conditional<
 | |
|       IsSame<T, nsISupports>::value, nsCOMPtr<T>,
 | |
|       typename Conditional<IsRefcounted<T>::value, RefPtr<T>,
 | |
|                            nsAutoPtr<T>>::Type>::Type SmartPtr;
 | |
|   typedef SegmentedVector<SmartPtr> SmartPtrArray;
 | |
| 
 | |
|   static_assert(
 | |
|       IsSame<T, nsISupports>::value || !std::is_base_of<nsISupports, T>::value,
 | |
|       "nsISupports classes should all use the nsISupports instantiation");
 | |
| 
 | |
|   static inline void AppendAndTake(
 | |
|       SegmentedVector<nsCOMPtr<nsISupports>>& smartPtrArray, nsISupports* ptr) {
 | |
|     smartPtrArray.InfallibleAppend(dont_AddRef(ptr));
 | |
|   }
 | |
|   template <class U>
 | |
|   static inline void AppendAndTake(SegmentedVector<RefPtr<U>>& smartPtrArray,
 | |
|                                    U* ptr) {
 | |
|     smartPtrArray.InfallibleAppend(dont_AddRef(ptr));
 | |
|   }
 | |
|   template <class U>
 | |
|   static inline void AppendAndTake(SegmentedVector<nsAutoPtr<U>>& smartPtrArray,
 | |
|                                    U* ptr) {
 | |
|     smartPtrArray.InfallibleAppend(ptr);
 | |
|   }
 | |
| 
 | |
|   static void* AppendDeferredFinalizePointer(void* aData, void* aObject) {
 | |
|     SmartPtrArray* pointers = static_cast<SmartPtrArray*>(aData);
 | |
|     if (!pointers) {
 | |
|       pointers = new SmartPtrArray();
 | |
|     }
 | |
|     AppendAndTake(*pointers, static_cast<T*>(aObject));
 | |
|     return pointers;
 | |
|   }
 | |
|   static bool DeferredFinalize(uint32_t aSlice, void* aData) {
 | |
|     MOZ_ASSERT(aSlice > 0, "nonsensical/useless call with aSlice == 0");
 | |
|     SmartPtrArray* pointers = static_cast<SmartPtrArray*>(aData);
 | |
|     uint32_t oldLen = pointers->Length();
 | |
|     if (oldLen < aSlice) {
 | |
|       aSlice = oldLen;
 | |
|     }
 | |
|     uint32_t newLen = oldLen - aSlice;
 | |
|     pointers->PopLastN(aSlice);
 | |
|     if (newLen == 0) {
 | |
|       delete pointers;
 | |
|       return true;
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
 | |
| struct DeferredFinalizer {
 | |
|   static void AddForDeferredFinalization(T* aObject) {
 | |
|     typedef DeferredFinalizerImpl<T> Impl;
 | |
|     DeferredFinalize(Impl::AppendDeferredFinalizePointer,
 | |
|                      Impl::DeferredFinalize, aObject);
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| struct DeferredFinalizer<T, true> {
 | |
|   static void AddForDeferredFinalization(T* aObject) {
 | |
|     DeferredFinalize(reinterpret_cast<nsISupports*>(aObject));
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| static void AddForDeferredFinalization(T* aObject) {
 | |
|   DeferredFinalizer<T>::AddForDeferredFinalization(aObject);
 | |
| }
 | |
| 
 | |
| // This returns T's CC participant if it participates in CC and does not inherit
 | |
| // from nsISupports. Otherwise, it returns null. QI should be used to get the
 | |
| // participant if T inherits from nsISupports.
 | |
| template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
 | |
| class GetCCParticipant {
 | |
|   // Helper for GetCCParticipant for classes that participate in CC.
 | |
|   template <class U>
 | |
|   static constexpr nsCycleCollectionParticipant* GetHelper(
 | |
|       int, typename U::NS_CYCLE_COLLECTION_INNERCLASS* dummy = nullptr) {
 | |
|     return T::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant();
 | |
|   }
 | |
|   // Helper for GetCCParticipant for classes that don't participate in CC.
 | |
|   template <class U>
 | |
|   static constexpr nsCycleCollectionParticipant* GetHelper(double) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|  public:
 | |
|   static constexpr nsCycleCollectionParticipant* Get() {
 | |
|     // Passing int() here will try to call the GetHelper that takes an int as
 | |
|     // its first argument. If T doesn't participate in CC then substitution for
 | |
|     // the second argument (with a default value) will fail and because of
 | |
|     // SFINAE the next best match (the variant taking a double) will be called.
 | |
|     return GetHelper<T>(int());
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| class GetCCParticipant<T, true> {
 | |
|  public:
 | |
|   static constexpr nsCycleCollectionParticipant* Get() { return nullptr; }
 | |
| };
 | |
| 
 | |
| void FinalizeGlobal(JSFreeOp* aFop, JSObject* aObj);
 | |
| 
 | |
| bool ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
 | |
|                    JS::Handle<jsid> aId, bool* aResolvedp);
 | |
| 
 | |
| bool MayResolveGlobal(const JSAtomState& aNames, jsid aId, JSObject* aMaybeObj);
 | |
| 
 | |
| bool EnumerateGlobal(JSContext* aCx, JS::HandleObject aObj,
 | |
|                      JS::MutableHandleVector<jsid> aProperties,
 | |
|                      bool aEnumerableOnly);
 | |
| 
 | |
| struct CreateGlobalOptionsGeneric {
 | |
|   static void TraceGlobal(JSTracer* aTrc, JSObject* aObj) {
 | |
|     mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj);
 | |
|   }
 | |
|   static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal) {
 | |
|     MOZ_ALWAYS_TRUE(TryPreserveWrapper(aGlobal));
 | |
| 
 | |
|     return true;
 | |
|   }
 | |
| };
 | |
| 
 | |
| struct CreateGlobalOptionsWithXPConnect {
 | |
|   static void TraceGlobal(JSTracer* aTrc, JSObject* aObj);
 | |
|   static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| using IsGlobalWithXPConnect =
 | |
|     IntegralConstant<bool, std::is_base_of<nsGlobalWindowInner, T>::value ||
 | |
|                                std::is_base_of<MessageManagerGlobal, T>::value>;
 | |
| 
 | |
| template <class T>
 | |
| struct CreateGlobalOptions : Conditional<IsGlobalWithXPConnect<T>::value,
 | |
|                                          CreateGlobalOptionsWithXPConnect,
 | |
|                                          CreateGlobalOptionsGeneric>::Type {
 | |
|   static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
 | |
|       ProtoAndIfaceCache::NonWindowLike;
 | |
| };
 | |
| 
 | |
| template <>
 | |
| struct CreateGlobalOptions<nsGlobalWindowInner>
 | |
|     : public CreateGlobalOptionsWithXPConnect {
 | |
|   static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
 | |
|       ProtoAndIfaceCache::WindowLike;
 | |
| };
 | |
| 
 | |
| uint64_t GetWindowID(void* aGlobal);
 | |
| uint64_t GetWindowID(nsGlobalWindowInner* aGlobal);
 | |
| uint64_t GetWindowID(DedicatedWorkerGlobalScope* aGlobal);
 | |
| 
 | |
| // The return value is true if we created and successfully performed our part of
 | |
| // the setup for the global, false otherwise.
 | |
| //
 | |
| // Typically this method's caller will want to ensure that
 | |
| // xpc::InitGlobalObjectOptions is called before, and xpc::InitGlobalObject is
 | |
| // called after, this method, to ensure that this global object and its
 | |
| // compartment are consistent with other global objects.
 | |
| template <class T, ProtoHandleGetter GetProto>
 | |
| bool CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
 | |
|                   const JSClass* aClass, JS::RealmOptions& aOptions,
 | |
|                   JSPrincipals* aPrincipal, bool aInitStandardClasses,
 | |
|                   JS::MutableHandle<JSObject*> aGlobal) {
 | |
|   aOptions.creationOptions()
 | |
|       .setTrace(CreateGlobalOptions<T>::TraceGlobal)
 | |
|       .setProfilerRealmID(GetWindowID(aNative));
 | |
|   xpc::SetPrefableRealmOptions(aOptions);
 | |
| 
 | |
|   aGlobal.set(JS_NewGlobalObject(aCx, aClass, aPrincipal,
 | |
|                                  JS::DontFireOnNewGlobalHook, aOptions));
 | |
|   if (!aGlobal) {
 | |
|     NS_WARNING("Failed to create global");
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   JSAutoRealm ar(aCx, aGlobal);
 | |
| 
 | |
|   {
 | |
|     js::SetReservedSlot(aGlobal, DOM_OBJECT_SLOT, JS::PrivateValue(aNative));
 | |
|     NS_ADDREF(aNative);
 | |
| 
 | |
|     aCache->SetWrapper(aGlobal);
 | |
| 
 | |
|     dom::AllocateProtoAndIfaceCache(
 | |
|         aGlobal, CreateGlobalOptions<T>::ProtoAndIfaceCacheKind);
 | |
| 
 | |
|     if (!CreateGlobalOptions<T>::PostCreateGlobal(aCx, aGlobal)) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (aInitStandardClasses && !JS::InitRealmStandardClasses(aCx)) {
 | |
|     NS_WARNING("Failed to init standard classes");
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   JS::Handle<JSObject*> proto = GetProto(aCx);
 | |
|   if (!proto || !JS_SplicePrototype(aCx, aGlobal, proto)) {
 | |
|     NS_WARNING("Failed to set proto");
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   bool succeeded;
 | |
|   if (!JS_SetImmutablePrototype(aCx, aGlobal, &succeeded)) {
 | |
|     return false;
 | |
|   }
 | |
|   MOZ_ASSERT(succeeded,
 | |
|              "making a fresh global object's [[Prototype]] immutable can "
 | |
|              "internally fail, but it should never be unsuccessful");
 | |
| 
 | |
|   if (!JS_DefineProfilingFunctions(aCx, aGlobal)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Holds a jsid that is initialized to a pinned string, with automatic
 | |
|  * conversion to Handle<jsid>, as it is held live forever by pinning.
 | |
|  */
 | |
| class PinnedStringId {
 | |
|   jsid id;
 | |
| 
 | |
|  public:
 | |
|   constexpr PinnedStringId() : id(JSID_VOID) {}
 | |
| 
 | |
|   bool init(JSContext* cx, const char* string) {
 | |
|     JSString* str = JS_AtomizeAndPinString(cx, string);
 | |
|     if (!str) return false;
 | |
|     id = INTERNED_STRING_TO_JSID(cx, str);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   operator const jsid&() const { return id; }
 | |
| 
 | |
|   operator JS::Handle<jsid>() const {
 | |
|     /* This is safe because we have pinned the string. */
 | |
|     return JS::Handle<jsid>::fromMarkedLocation(&id);
 | |
|   }
 | |
| } JS_HAZ_ROOTED;
 | |
| 
 | |
| namespace binding_detail {
 | |
| /**
 | |
|  * WebIDL getters have a "generic" JSNative that is responsible for the
 | |
|  * following things:
 | |
|  *
 | |
|  * 1) Determining the "this" pointer for the C++ call.
 | |
|  * 2) Extracting the "specialized" getter from the jitinfo on the JSFunction.
 | |
|  * 3) Calling the specialized getter.
 | |
|  * 4) Handling exceptions as needed.
 | |
|  *
 | |
|  * There are several variants of (1) depending on the interface involved and
 | |
|  * there are two variants of (4) depending on whether the return type is a
 | |
|  * Promise.  We handle this by templating our generic getter on a
 | |
|  * this-determination policy and an exception handling policy, then explicitly
 | |
|  * instantiating the relevant template specializations.
 | |
|  */
 | |
| template <typename ThisPolicy, typename ExceptionPolicy>
 | |
| bool GenericGetter(JSContext* cx, unsigned argc, JS::Value* vp);
 | |
| 
 | |
| /**
 | |
|  * WebIDL setters have a "generic" JSNative that is responsible for the
 | |
|  * following things:
 | |
|  *
 | |
|  * 1) Determining the "this" pointer for the C++ call.
 | |
|  * 2) Extracting the "specialized" setter from the jitinfo on the JSFunction.
 | |
|  * 3) Calling the specialized setter.
 | |
|  *
 | |
|  * There are several variants of (1) depending on the interface
 | |
|  * involved.  We handle this by templating our generic setter on a
 | |
|  * this-determination policy, then explicitly instantiating the
 | |
|  * relevant template specializations.
 | |
|  */
 | |
| template <typename ThisPolicy>
 | |
| bool GenericSetter(JSContext* cx, unsigned argc, JS::Value* vp);
 | |
| 
 | |
| /**
 | |
|  * WebIDL methods have a "generic" JSNative that is responsible for the
 | |
|  * following things:
 | |
|  *
 | |
|  * 1) Determining the "this" pointer for the C++ call.
 | |
|  * 2) Extracting the "specialized" method from the jitinfo on the JSFunction.
 | |
|  * 3) Calling the specialized methodx.
 | |
|  * 4) Handling exceptions as needed.
 | |
|  *
 | |
|  * There are several variants of (1) depending on the interface involved and
 | |
|  * there are two variants of (4) depending on whether the return type is a
 | |
|  * Promise.  We handle this by templating our generic method on a
 | |
|  * this-determination policy and an exception handling policy, then explicitly
 | |
|  * instantiating the relevant template specializations.
 | |
|  */
 | |
| template <typename ThisPolicy, typename ExceptionPolicy>
 | |
| bool GenericMethod(JSContext* cx, unsigned argc, JS::Value* vp);
 | |
| 
 | |
| // A this-extraction policy for normal getters/setters/methods.
 | |
| struct NormalThisPolicy;
 | |
| 
 | |
| // A this-extraction policy for getters/setters/methods on interfaces
 | |
| // that are on some global's proto chain.
 | |
| struct MaybeGlobalThisPolicy;
 | |
| 
 | |
| // A this-extraction policy for lenient getters/setters.
 | |
| struct LenientThisPolicy;
 | |
| 
 | |
| // A this-extraction policy for cross-origin getters/setters/methods.
 | |
| struct CrossOriginThisPolicy;
 | |
| 
 | |
| // A this-extraction policy for getters/setters/methods that should
 | |
| // not be allowed to be called cross-origin but expect objects that
 | |
| // _can_ be cross-origin.
 | |
| struct MaybeCrossOriginObjectThisPolicy;
 | |
| 
 | |
| // A this-extraction policy which is just like
 | |
| // MaybeCrossOriginObjectThisPolicy but has lenient-this behavior.
 | |
| struct MaybeCrossOriginObjectLenientThisPolicy;
 | |
| 
 | |
| // An exception-reporting policy for normal getters/setters/methods.
 | |
| struct ThrowExceptions;
 | |
| 
 | |
| // An exception-handling policy for Promise-returning getters/methods.
 | |
| struct ConvertExceptionsToPromises;
 | |
| }  // namespace binding_detail
 | |
| 
 | |
| bool StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp);
 | |
| 
 | |
| // ConvertExceptionToPromise should only be called when we have an error
 | |
| // condition (e.g. returned false from a JSAPI method).  Note that there may be
 | |
| // no exception on cx, in which case this is an uncatchable failure that will
 | |
| // simply be propagated.  Otherwise this method will attempt to convert the
 | |
| // exception to a Promise rejected with the exception that it will store in
 | |
| // rval.
 | |
| bool ConvertExceptionToPromise(JSContext* cx,
 | |
|                                JS::MutableHandle<JS::Value> rval);
 | |
| 
 | |
| #ifdef DEBUG
 | |
| void AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo,
 | |
|                                     JS::Handle<JS::Value> aValue);
 | |
| #endif
 | |
| 
 | |
| bool CallerSubsumes(JSObject* aObject);
 | |
| 
 | |
| MOZ_ALWAYS_INLINE bool CallerSubsumes(JS::Handle<JS::Value> aValue) {
 | |
|   if (!aValue.isObject()) {
 | |
|     return true;
 | |
|   }
 | |
|   return CallerSubsumes(&aValue.toObject());
 | |
| }
 | |
| 
 | |
| template <class T, class S>
 | |
| inline RefPtr<T> StrongOrRawPtr(already_AddRefed<S>&& aPtr) {
 | |
|   return std::move(aPtr);
 | |
| }
 | |
| 
 | |
| template <class T, class S>
 | |
| inline RefPtr<T> StrongOrRawPtr(RefPtr<S>&& aPtr) {
 | |
|   return std::move(aPtr);
 | |
| }
 | |
| 
 | |
| template <class T, class ReturnType = typename Conditional<
 | |
|                        IsRefcounted<T>::value, T*, nsAutoPtr<T>>::Type>
 | |
| inline ReturnType StrongOrRawPtr(T* aPtr) {
 | |
|   return ReturnType(aPtr);
 | |
| }
 | |
| 
 | |
| template <class T, template <typename> class SmartPtr, class S>
 | |
| inline void StrongOrRawPtr(SmartPtr<S>&& aPtr) = delete;
 | |
| 
 | |
| template <class T>
 | |
| struct StrongPtrForMember {
 | |
|   typedef typename Conditional<IsRefcounted<T>::value, RefPtr<T>,
 | |
|                                nsAutoPtr<T>>::Type Type;
 | |
| };
 | |
| 
 | |
| namespace binding_detail {
 | |
| inline JSObject* GetHackedNamespaceProtoObject(JSContext* aCx) {
 | |
|   return JS_NewPlainObject(aCx);
 | |
| }
 | |
| }  // namespace binding_detail
 | |
| 
 | |
| // Resolve an id on the given global object that wants to be included in
 | |
| // Exposed=System webidl annotations.  False return value means exception
 | |
| // thrown.
 | |
| bool SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
 | |
|                          JS::Handle<jsid> id, bool* resolvedp);
 | |
| 
 | |
| // Enumerate all ids on the given global object that wants to be included in
 | |
| // Exposed=System webidl annotations.  False return value means exception
 | |
| // thrown.
 | |
| bool SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj);
 | |
| 
 | |
| // Slot indexes for maplike/setlike forEach functions
 | |
| #define FOREACH_CALLBACK_SLOT 0
 | |
| #define FOREACH_MAPLIKEORSETLIKEOBJ_SLOT 1
 | |
| 
 | |
| // Backing function for running .forEach() on maplike/setlike interfaces.
 | |
| // Unpacks callback and maplike/setlike object from reserved slots, then runs
 | |
| // callback for each key (and value, for maplikes)
 | |
| bool ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
 | |
| 
 | |
| // Unpacks backing object (ES6 map/set) from the reserved slot of a reflector
 | |
| // for a maplike/setlike interface. If backing object does not exist, creates
 | |
| // backing object in the compartment of the reflector involved, making this safe
 | |
| // to use across compartments/via xrays. Return values of these methods will
 | |
| // always be in the context compartment.
 | |
| bool GetMaplikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
 | |
|                              size_t aSlotIndex,
 | |
|                              JS::MutableHandle<JSObject*> aBackingObj,
 | |
|                              bool* aBackingObjCreated);
 | |
| bool GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
 | |
|                              size_t aSlotIndex,
 | |
|                              JS::MutableHandle<JSObject*> aBackingObj,
 | |
|                              bool* aBackingObjCreated);
 | |
| 
 | |
| // Get the desired prototype object for an object construction from the given
 | |
| // CallArgs.  The CallArgs must be for a constructor call.  The
 | |
| // aProtoId/aCreator arguments are used to get a default if we don't find a
 | |
| // prototype on the newTarget of the callargs.
 | |
| bool GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
 | |
|                      prototypes::id::ID aProtoId,
 | |
|                      CreateInterfaceObjectsMethod aCreator,
 | |
|                      JS::MutableHandle<JSObject*> aDesiredProto);
 | |
| 
 | |
| // This function is expected to be called from the constructor function for an
 | |
| // HTML or XUL element interface; the global/callargs need to be whatever was
 | |
| // passed to that constructor function.
 | |
| already_AddRefed<Element> CreateXULOrHTMLElement(
 | |
|     const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
 | |
|     JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv);
 | |
| 
 | |
| void SetUseCounter(JSObject* aObject, UseCounter aUseCounter);
 | |
| void SetUseCounter(UseCounterWorker aUseCounter);
 | |
| 
 | |
| // Warnings
 | |
| void DeprecationWarning(JSContext* aCx, JSObject* aObject,
 | |
|                         Document::DeprecatedOperations aOperation);
 | |
| 
 | |
| void DeprecationWarning(const GlobalObject& aGlobal,
 | |
|                         Document::DeprecatedOperations aOperation);
 | |
| 
 | |
| // A callback to perform funToString on an interface object
 | |
| JSString* InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
 | |
|                                   unsigned /* indent */);
 | |
| 
 | |
| namespace binding_detail {
 | |
| // Get a JS global object that can be used for some temporary allocations.  The
 | |
| // idea is that this should be used for situations when you need to operate in
 | |
| // _some_ compartment but don't care which one.  A typical example is when you
 | |
| // have non-JS input, non-JS output, but have to go through some sort of JS
 | |
| // representation in the middle, so need a compartment to allocate things in.
 | |
| //
 | |
| // It's VERY important that any consumers of this function only do things that
 | |
| // are guaranteed to be side-effect-free, even in the face of a script
 | |
| // environment controlled by a hostile adversary.  This is because in the worker
 | |
| // case the global is in fact the worker global, so it and its standard objects
 | |
| // are controlled by the worker script.  This is why this function is in the
 | |
| // binding_detail namespace.  Any use of this function MUST be very carefully
 | |
| // reviewed by someone who is sufficiently devious and has a very good
 | |
| // understanding of all the code that will run while we're using the return
 | |
| // value, including the SpiderMonkey parts.
 | |
| JSObject* UnprivilegedJunkScopeOrWorkerGlobal();
 | |
| 
 | |
| // Implementation of the [HTMLConstructor] extended attribute.
 | |
| bool HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
 | |
|                      constructors::id::ID aConstructorId,
 | |
|                      prototypes::id::ID aProtoId,
 | |
|                      CreateInterfaceObjectsMethod aCreator);
 | |
| 
 | |
| // A method to test whether an attribute with the given JSJitGetterOp getter is
 | |
| // enabled in the given set of prefable proeprty specs.  For use for toJSON
 | |
| // conversions.  aObj is the object that would be used as the "this" value.
 | |
| bool IsGetterEnabled(JSContext* aCx, JS::Handle<JSObject*> aObj,
 | |
|                      JSJitGetterOp aGetter,
 | |
|                      const Prefable<const JSPropertySpec>* aAttributes);
 | |
| 
 | |
| // A class that can be used to examine the chars of a linear string.
 | |
| class StringIdChars {
 | |
|  public:
 | |
|   // Require a non-const ref to an AutoRequireNoGC to prevent callers
 | |
|   // from passing temporaries.
 | |
|   StringIdChars(JS::AutoRequireNoGC& nogc, JSLinearString* str) {
 | |
|     mIsLatin1 = js::LinearStringHasLatin1Chars(str);
 | |
|     if (mIsLatin1) {
 | |
|       mLatin1Chars = js::GetLatin1LinearStringChars(nogc, str);
 | |
|     } else {
 | |
|       mTwoByteChars = js::GetTwoByteLinearStringChars(nogc, str);
 | |
|     }
 | |
| #ifdef DEBUG
 | |
|     mLength = js::GetLinearStringLength(str);
 | |
| #endif  // DEBUG
 | |
|   }
 | |
| 
 | |
|   MOZ_ALWAYS_INLINE char16_t operator[](size_t index) {
 | |
|     MOZ_ASSERT(index < mLength);
 | |
|     if (mIsLatin1) {
 | |
|       return mLatin1Chars[index];
 | |
|     }
 | |
|     return mTwoByteChars[index];
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   bool mIsLatin1;
 | |
|   union {
 | |
|     const JS::Latin1Char* mLatin1Chars;
 | |
|     const char16_t* mTwoByteChars;
 | |
|   };
 | |
| #ifdef DEBUG
 | |
|   size_t mLength;
 | |
| #endif  // DEBUG
 | |
| };
 | |
| 
 | |
| }  // namespace binding_detail
 | |
| 
 | |
| }  // namespace dom
 | |
| }  // namespace mozilla
 | |
| 
 | |
| #endif /* mozilla_dom_BindingUtils_h__ */
 |